diff --git a/.circleci/config.yml b/.circleci/config.yml index 2b9fba2ed..248747b3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,132 @@ defaults: &defaults working_directory: ~/repo -version: 2 +macos: &macos + macos: + xcode: "11.2.1" + +bash-env: &bash-env + BASH_ENV: "~/.nvm/nvm.sh" + +install-npm-modules: &install-npm-modules + name: Install NPM modules + command: yarn + +restore-npm-cache-linux: &restore-npm-cache-linux + name: Restore NPM cache + key: node-modules-{{ checksum "yarn.lock" }} + +save-npm-cache-linux: &save-npm-cache-linux + key: node-modules-{{ checksum "yarn.lock" }} + name: Save NPM cache + paths: + - ./node_modules + +restore-npm-cache-mac: &restore-npm-cache-mac + name: Restore NPM cache + key: node-v1-mac-{{ checksum "yarn.lock" }} + +save-npm-cache-mac: &save-npm-cache-mac + key: node-v1-mac-{{ checksum "yarn.lock" }} + name: Save NPM cache + paths: + - ./node_modules + +install-node: &install-node + name: Install Node 10 + command: | + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash + source ~/.nvm/nvm.sh + # https://github.com/creationix/nvm/issues/1394 + set +e + nvm install 10 + echo 'export PATH="/home/circleci/.nvm/versions/node/v10.20.1/bin:$PATH"' >> ~/.bash_profile + source ~/.bash_profile + +restore-gems-cache: &restore-gems-cache + name: Restore gems cache + key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} + +save-gems-cache: &save-gems-cache + name: Save gems cache + key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} + paths: + - vendor/bundle + +update-fastlane: &update-fastlane + name: Update Fastlane + command: | + echo "ruby-2.6.4" > ~/.ruby-version + bundle install + working_directory: ios + +restore-brew-cache: &restore-brew-cache + name: Restore Brew cache + key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} + +save-brew-cache: &save-brew-cache + name: Save brew cache + key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} + paths: + - /usr/local/Homebrew + +install-apple-sim-utils: &install-apple-sim-utils + name: Install appleSimUtils + command: | + brew update + brew tap wix/brew + brew install wix/brew/applesimutils + +rebuild-detox: &rebuild-detox + name: Rebuild Detox framework cache + command: | + npx detox clean-framework-cache + npx detox build-framework-cache + +version: 2.1 + +# EXECUTORS +executors: + mac-env: + <<: *macos + environment: + <<: *bash-env + +# COMMANDS +commands: + detox-test: + parameters: + folder: + type: string + steps: + - checkout + + - attach_workspace: + at: . + + - restore_cache: *restore-npm-cache-mac + + - restore_cache: *restore-brew-cache + + - run: *install-node + + - run: *install-apple-sim-utils + + - run: *install-npm-modules + + - run: *rebuild-detox + + - run: + name: Test + command: | + npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup + +# JOBS jobs: lint-testunit: <<: *defaults docker: - - image: circleci/node:8 + - image: circleci/node:10 environment: CODECOV_TOKEN: caa771ab-3d45-4756-8e2a-e1f25996fef6 @@ -14,14 +134,9 @@ jobs: steps: - checkout - - restore_cache: - name: Restore NPM cache - key: node-modules-{{ checksum "yarn.lock" }} + - restore_cache: *restore-npm-cache-linux - - run: - name: Install NPM modules - command: | - yarn + - run: *install-npm-modules - run: name: Lint @@ -38,162 +153,79 @@ jobs: command: | yarn codecov - - save_cache: - key: node-modules-{{ checksum "yarn.lock" }} - name: Save NPM cache - paths: - - ./node_modules + - save_cache: *save-npm-cache-linux + # E2E e2e-build: - macos: - xcode: "11.2.1" - - environment: - BASH_ENV: "~/.nvm/nvm.sh" + executor: mac-env steps: - checkout - - restore_cache: - name: Restore NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} + - restore_cache: *restore-npm-cache-mac - - run: - name: Install Node 8 - command: | - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash - source ~/.nvm/nvm.sh - # https://github.com/creationix/nvm/issues/1394 - set +e - nvm install 8 + - restore_cache: *restore-brew-cache - - run: - name: Install appleSimUtils - command: | - brew update - brew tap wix/brew - brew install wix/brew/applesimutils + - run: *install-node - - run: - name: Install NPM modules - command: | - yarn global add detox-cli - yarn + - run: *install-apple-sim-utils - - run: - name: Rebuild Detox framework cache - command: | - detox clean-framework-cache - detox build-framework-cache + - run: *install-npm-modules + + - run: *rebuild-detox - run: name: Build command: | - detox build --configuration ios.sim.release + npx detox build --configuration ios.sim.release - persist_to_workspace: root: . paths: - ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app - - save_cache: - name: Save NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} - paths: - - node_modules + - save_cache: *save-npm-cache-mac - e2e-test: - macos: - xcode: "11.2.1" - - environment: - BASH_ENV: "~/.nvm/nvm.sh" + - save_cache: *save-brew-cache + e2e-test-onboarding: + executor: mac-env steps: - - checkout + - detox-test: + folder: "./e2e/tests/onboarding" - - attach_workspace: - at: . + e2e-test-room: + executor: mac-env + steps: + - detox-test: + folder: "./e2e/tests/room" - - restore_cache: - name: Restore NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} - - - run: - name: Install Node 8 - command: | - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash - source ~/.nvm/nvm.sh - # https://github.com/creationix/nvm/issues/1394 - set +e - nvm install 8 - - - run: - name: Install appleSimUtils - command: | - brew update - brew tap wix/brew - brew install wix/brew/applesimutils - - - run: - name: Install NPM modules - command: | - yarn global add detox-cli - yarn - - - run: - name: Rebuild Detox framework cache - command: | - detox clean-framework-cache - detox build-framework-cache - - - run: - name: Test - command: | - detox test --configuration ios.sim.release --cleanup - - - save_cache: - name: Save NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} - paths: - - node_modules + e2e-test-assorted: + executor: mac-env + steps: + - detox-test: + folder: "./e2e/tests/assorted" + # Android builds android-build: <<: *defaults docker: - image: circleci/android:api-28-node environment: - # GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError" - # GRADLE_OPTS: -Xmx2048m -Dorg.gradle.daemon=false - # JVM_OPTS: -Xmx4096m JAVA_OPTS: '-Xms512m -Xmx2g' GRADLE_OPTS: '-Xmx3g -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"' TERM: dumb - BASH_ENV: "~/.nvm/nvm.sh" + <<: *bash-env steps: - checkout - - run: - name: Install Node 8 - command: | - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash - source ~/.nvm/nvm.sh - # https://github.com/creationix/nvm/issues/1394 - set +e - nvm install 8 - echo 'export PATH="/home/circleci/.nvm/versions/node/v8.16.0/bin:$PATH"' >> ~/.bash_profile - source ~/.bash_profile + - run: *install-node - - restore_cache: - name: Restore NPM cache - key: node-modules-{{ checksum "yarn.lock" }} + - restore_cache: *restore-npm-cache-linux - - run: - name: Install NPM modules - command: | - yarn + - run: *install-npm-modules - restore_cache: name: Restore gradle cache @@ -206,6 +238,7 @@ jobs: # echo -e "android.enableAapt2=false" >> ./gradle.properties echo -e "android.useAndroidX=true" >> ./gradle.properties echo -e "android.enableJetifier=true" >> ./gradle.properties + echo -e "FLIPPER_VERSION=0.33.1" >> ./gradle.properties if [[ $KEYSTORE ]]; then echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE @@ -234,8 +267,7 @@ jobs: name: Build Android App command: | if [[ $KEYSTORE ]]; then - # TODO: enable app bundle again - ./gradlew assembleRelease + ./gradlew bundleRelease else ./gradlew assembleDebug fi @@ -261,11 +293,7 @@ jobs: - store_artifacts: path: /tmp/build/outputs - - save_cache: - name: Save NPM cache - key: node-modules-{{ checksum "yarn.lock" }} - paths: - - ./node_modules + - save_cache: *save-npm-cache-linux - save_cache: name: Save gradle cache @@ -273,44 +301,22 @@ jobs: paths: - ~/.gradle + # iOS builds ios-build: - macos: - xcode: "11.2.1" - - environment: - BASH_ENV: "~/.nvm/nvm.sh" + executor: mac-env steps: - checkout - - restore_cache: - name: Restore gems cache - key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} + - restore_cache: *restore-gems-cache - - restore_cache: - name: Restore NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} + - restore_cache: *restore-npm-cache-mac - - run: - name: Install Node 8 - command: | - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash - source ~/.nvm/nvm.sh - # https://github.com/creationix/nvm/issues/1394 - set +e - nvm install 8 + - run: *install-node - - run: - name: Install NPM modules - command: | - yarn + - run: *install-npm-modules - - run: - name: Update Fastlane - command: | - echo "ruby-2.6.4" > ~/.ruby-version - bundle install - working_directory: ios + - run: *update-fastlane - run: name: Set Google Services @@ -348,17 +354,9 @@ jobs: fi working_directory: ios - - save_cache: - name: Save NPM cache - key: node-v1-mac-{{ checksum "yarn.lock" }} - paths: - - node_modules + - save_cache: *save-npm-cache-mac - - save_cache: - name: Save gems cache - key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} - paths: - - vendor/bundle + - save_cache: *save-gems-cache - store_artifacts: path: ios/RocketChatRN.ipa @@ -370,8 +368,7 @@ jobs: - ios/fastlane/report.xml ios-testflight: - macos: - xcode: "11.2.1" + executor: mac-env steps: - checkout @@ -379,16 +376,9 @@ jobs: - attach_workspace: at: ios - - restore_cache: - name: Restore gems cache - key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} + - restore_cache: *restore-gems-cache - - run: - name: Update Fastlane - command: | - echo "ruby-2.4" > ~/.ruby-version - bundle install - working_directory: ios + - run: *update-fastlane - run: name: Fastlane Tesflight Upload @@ -396,14 +386,9 @@ jobs: bundle exec fastlane ios beta working_directory: ios - - save_cache: - name: Save gems cache - key: bundle-v1-{{ checksum "ios/Gemfile.lock" }} - paths: - - vendor/bundle + - save_cache: *save-gems-cache workflows: - version: 2 build-and-test: jobs: - lint-testunit @@ -415,7 +400,13 @@ workflows: - e2e-build: requires: - e2e-hold - - e2e-test: + - e2e-test-onboarding: + requires: + - e2e-build + - e2e-test-room: + requires: + - e2e-build + - e2e-test-assorted: requires: - e2e-build diff --git a/.eslintrc.js b/.eslintrc.js index b7c949197..44e4eaf4c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -87,6 +87,7 @@ module.exports = { "no-regex-spaces": 2, "no-undef": 2, "no-unreachable": 2, + "no-unused-expressions": 0, "no-unused-vars": [2, { "vars": "all", "args": "after-used" @@ -131,7 +132,23 @@ module.exports = { "react-native/no-unused-styles": 2, "react/jsx-one-expression-per-line": 0, "require-await": 2, - "func-names": 0 + "func-names": 0, + "react/sort-comp": ["error", { + "order": [ + "static-variables", + "static-methods", + "lifecycle", + "everything-else", + "render" + ] + }], + "react/static-property-placement": [0], + "arrow-parens": ["error", "as-needed", { requireForBlockBody: true }], + "react/jsx-props-no-spreading": [1], + "react/jsx-curly-newline": [0], + "react/state-in-constructor": [0], + "no-async-promise-executor": [0], + "max-classes-per-file": [0] }, "globals": { "__DEV__": true diff --git a/.gitignore b/.gitignore index 3762b2466..cc34f4320 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ coverage/ buck-out/ \.buckd/ *.keystore +*.jks # fastlane # diff --git a/README.md b/README.md index bcae9f99d..1f0c5deea 100644 --- a/README.md +++ b/README.md @@ -208,13 +208,15 @@ Readme will guide you on how to config. - Build your app ```bash -$ detox build --configuration ios.sim.release +$ npx detox build --configuration ios.sim.release ``` - Run tests ```bash -$ detox test --configuration ios.sim.release +$ npx detox test ./e2e/tests/onboarding --configuration ios.sim.release +$ npx detox test ./e2e/tests/room --configuration ios.sim.release +$ npx detox test ./e2e/tests/assorted --configuration ios.sim.release ``` ## Storybook diff --git a/__mocks__/expo-av.js b/__mocks__/expo-av.js new file mode 100644 index 000000000..e20feac67 --- /dev/null +++ b/__mocks__/expo-av.js @@ -0,0 +1,14 @@ +export class Sound { + loadAsync = () => {}; + + playAsync = () => {}; + + pauseAsync = () => {}; + + stopAsync = () => {}; + + setOnPlaybackStatusUpdate = () => {}; + + setPositionAsync = () => {}; +} +export const Audio = { Sound }; diff --git a/__mocks__/expo-keep-awake.js b/__mocks__/expo-keep-awake.js new file mode 100644 index 000000000..947b94723 --- /dev/null +++ b/__mocks__/expo-keep-awake.js @@ -0,0 +1,4 @@ +export default { + activateKeepAwake: () => '', + deactivateKeepAwake: () => '' +}; diff --git a/__mocks__/react-native-device-info.js b/__mocks__/react-native-device-info.js index d3fa5fa66..c1a00e2de 100644 --- a/__mocks__/react-native-device-info.js +++ b/__mocks__/react-native-device-info.js @@ -2,5 +2,6 @@ export default { getModel: () => '', getReadableVersion: () => '', getBundleId: () => '', - isTablet: () => false + isTablet: () => false, + hasNotch: () => false }; diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 2fa6d1061..e881064aa 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -666,17 +666,19 @@ exports[`Storyshots Markdown list Markdown 1`] = ` @@ -709,17 +711,19 @@ exports[`Storyshots Markdown list Markdown 1`] = ` @@ -896,17 +900,19 @@ exports[`Storyshots Markdown list Markdown 1`] = ` @@ -939,17 +945,19 @@ exports[`Storyshots Markdown list Markdown 1`] = ` @@ -3180,455 +3188,7 @@ exports[`Storyshots Markdown list Markdown 1`] = ` "marginHorizontal": 15, } } - > - - - - - - - - - First Header - - - - - - - Second Header - - - - - - - - - Content from cell 1 - - - - - - - Content from cell 2 - - - - - - - - - Content in the first column - - - - - - - Content in the second column - - - - - - - - - Click to see full table - - - + /> `; @@ -3664,246 +3224,6 @@ exports[`Storyshots Message list message 1`] = ` > Simple - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - Long message - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - Grouped messages - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - ... - - - - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - 10:00 AM - - - - - - Different user - - - - - - - - - - - - - - - - - This is the third message - - - - - - - - - - - - - - - - - This is the second message - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - This is the first message - - - - - - - - Without header - - - - - - - - - Message - - - - - - - - With alias - - - - - - - - - - - - - - - - - Diego Mello - - @ - diego.mello - - - - - - 10:00 AM - - - - - - Message - - - - - - - - - - - - - - - - - - - - - - - - - Diego Mello - - @ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - 10:00 AM - - - - - - Message - - - - - - - - Edited - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - Block Quote - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - Testing block quote - - - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - Testing block quote - - - - - - - Testing block quote - - - - - - - - Lists - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - • - - - - - - Dogs - - - - - - ◦ - - - - - - cats - - - - - - - - ◦ - - - - - - cats - - - - - - - - - - - - Numerated lists - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - 1. - - - - - - Dogs - - - - - - - - 2. - - - - - - Cats - - - - - - - - - - Numerated lists in separated messages - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - 1. - - - - - - Dogs - - - - - - - - - - - - - - - - - - - - 2. - - - - - - Cats - - - - - - - - - - Static avatar - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - Full name - - - - - - - - - - - - - - - - - Diego Mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - Mentions - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - rocket.cat - - - - - - diego.mello - - - - - - all - - - - - - here - - - - - - general - - - - - - - - Emojis - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - 👊🤙👏 - - - - - - - - Single Emoji - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - 👏 - - - - - - - - Custom Emojis - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - - - - - - - - - - - - - - - Single Custom Emojis - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - - - Normal Emoji + Custom Emojis - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - 🤙 - - - - - - - - - - - Four emoji - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - 🤙 - - - - - - 🤙🤙 - - - - - - - - Time format - - - - - - - - - - - - - - - - - diego.mello - - - - - 10 November 2017 - - - - - - Testing - - - - - - - - Reactions - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Reactions - - - - - - - - 😂 - - - 3 - - - - - - - - - - 13 - - - - - - - 🤔 - - - 1 - - - - - - -  - - - - - - - - - Multiple reactions - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Multiple Reactions - - - - - - - - - - - 1 - - - - - - - - - - 1 - - - - - - - - - - 1 - - - - - - - ❤️ - - - 1 - - - - - - - 🐶 - - - 1 - - - - - - - 😀 - - - 1 - - - - - - - 😬 - - - 1 - - - - - - - 😁 - - - 1 - - - - - - -  - - - - - - - - - Intercalated users - - - - - - - - - - - - - - - - - rocket.cat - - - - - 10:00 AM - - - - - - Fourth message - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Third message - - - - - - - - - - - - - - - - - - - - - - - - - rocket.cat - - - - - 10:00 AM - - - - - - Second message - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - First message - - - - - - - - Date and Unread separators - - - - - - - - - - - - - - - - - rocket.cat - - - - - 10:00 AM - - - - - - Fourth message - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Third message - - - - - - - - - - - - - - - - - Second message - - - - - - - - - - - - - - - - - - - - - - - - - rocket.cat - - - - - 10:00 AM - - - - - - Second message - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - First message - - - - - - - - With image - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - - This is a description - - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - - This is a description - - - - - - - - - - - - With video - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - -  - - - - - This is a description - - - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - -  - - - - - - - With audio - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - View - - -  - - - - - 00:00 - - - - - This is a description - - - - - - - - - - - - - - - - - - - First message - - - - - - - - - - - - - - - - View - - -  - - - - - 00:00 - - - - - This is a description - - - - - - - - - - - - - - - View - - -  - - - - - 00:00 - - - - - - - - - - - - - - - View - - -  - - - - - 00:00 - - - - - - - Message with reply - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - I'm fine! - - - - - - - - I'm a very long long title and I'll break - - - 10:00 AM - - - - - How are you? - - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - I'm fine! - - - - - - - - rocket.cat - - - 10:00 AM - - - - - How are you? - - - - - - - - - - - - Message with read receipt - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - I'm fine! - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - I'm fine! - - - - - -  - - - - - - - - - - - - - - I'm fine! - - - - - -  - - - - - Message with thread - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - How are you? - - - - - - -  - - - 1 reply - - - - Nov 10 - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - How are you? - - - - - - -  - - - +999 replies - - - - Nov 10 - - - - - - - - - - - - -  - - - How are you? - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - -  - - - Thread with emoji🙂 😂 - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - -  - - - Markdown: link block code - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - -  - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - -  - - - How are you? - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - -  - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - -  - - - Thread with attachment - - - - - - - - - - - - - - - - - Sent an attachment - - - - - - - Sequential thread messages following thread button - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - How are you? - - - - - - -  - - - 1 reply - - - - Nov 10 - - - - - - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - - - - - - - - - - - Sent an attachment - - - - - - - Sequential thread messages following thread reply - - - - - -  - - - How are you? - - - - - - - - - - - - - - - - - I'm fine! - - - - - - - - - - - - - - - - - - - - - - Cool! - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - - - - - - - - - - - - - - - - - - - Sent an attachment - - - - - - - Discussion - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - Started a discussion: - - - This is a discussion - - - - -  - - - No messages yet - - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - Started a discussion: - - - This is a discussion - - - - -  - - - 1 message - - - - Nov 10 - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - Started a discussion: - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - - - - -  - - - 10 messages - - - - Nov 10 - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - Started a discussion: - - - This is a discussion - - - - -  - - - +999 messages - - - - Nov 10 - - - - - - - URL - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - Rocket.Chat - Free, Open Source, Enterprise Team Chat - - - Rocket.Chat is the leading open source team chat software solution. Free, unlimited and completely customizable with on-premises and SaaS cloud hosting. - - - - - - - Google - - - Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for. - - - - - - - - - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - - - Google - - - Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for. - - - - - - - - - - - - - - - - - - Google - - - Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for. - - - - - - - - Custom fields - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - rocket.cat - - - 10:00 AM - - - - - Custom fields - - - - - - Field 1 - - - Value 1 - - - - - Field 2 - - - Value 2 - - - - - Field 3 - - - Value 3 - - - - - Field 4 - - - Value 4 - - - - - Field 5 - - - Value 5 - - - - - - - - - - Two short custom fields - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - rocket.cat - - - 10:00 AM - - - - - Custom fields - - - - - - Field 1 - - - Value 1 - - - - - Field 2 - - - Value 2 - - - - - - - - - - rocket.cat - - - 10:00 AM - - - - - Custom fields 2 - - - - - - Field 1 - - - Value 1 - - - - - Field 2 - - - Value 2 - - - - - - - - - - Broadcast - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Broadcasted message - - - - - - -  - - - Reply - - - - - - - - Archived - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - This message is inside an archived room - - - - - - - - - - - - - - + /> - - - - diego.mello - - - 10:00 AM - - -  - - - - - - diego.mello - - - 10:00 AM - - -  - - Temp - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Temp message - - - - - - - - Editing - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message being edited - - - - - - - - Removed - - - - - - - - - - - - - - Message removed - - - - - - Joined - - - - - - - - - - - - - - Has joined the channel - - - - - - Room name changed - - - - - - - - - - - - - - Room name changed to: New name by diego.mello - - - - - - Message pinned - - - - - - - - - - - - - - Message pinned - - - - - - Has left the channel - - - - - - - - - - - - - - Has left the channel - - - - - - User removed - - - - - - - - - - - - - - User rocket.cat removed by diego.mello - - - - - - User added - - - - - - - - - - - - - - User rocket.cat added by diego.mello - - - - - - User muted - - - - - - - - - - - - - - User rocket.cat muted by diego.mello - - - - - - User unmuted - - - - - - - - - - - - - - User rocket.cat unmuted by diego.mello - - - - - - Role added - - - - - - - - - - - - - - rocket.cat was set admin by diego.mello - - - - - - Role removed - - - - - - - - - - - - - - rocket.cat is no longer admin by diego.mello - - - - - - Changed description - - - - - - - - - - - - - - Room description changed to: new description by diego.mello - - - - - - Changed announcement - - - - - - - - - - - - - - Room announcement changed to: new announcement by diego.mello - - - - - - Changed topic - - - - - - - - - - - - - - Room topic changed to: new topic by diego.mello - - - - - - Changed type - - - - - - - - - - - - - - Room type changed to: public by diego.mello - - - - - - Custom style - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Message - - - - - - - - Markdown emphasis - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Italic with single - - - underscore - - - or double - - - underscores - - - . Bold with single - - - asterisk - - - or double - - - asterisks - - - . Strikethrough with single - - - Strikethrough - - - or double - - - Strikethrough - - - - - - - - Markdown headers - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - H1 - - - - - H2 - - - - - H3 - - - - - H4 - - - - - H5 - - - - - H6 - - - - - - - - Markdown links - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Support - - - - Google - - - - - - - - I\`m an inline-style link - - - - - - - - https://google.com - - - - - - - - - Markdown image - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - Markdown code - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - Inline - - - code - - - has - - - back-ticks around - - - it. - - - - Code block - - - - - - - - Markdown quote - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - Quote - - - - - - - - - - Markdown table - - - - - - - - - - - - - - - - - diego.mello - - - - - 10:00 AM - - - - - - - - - - - - First Header - - - - - - - Second Header - - - - - - - - - Content from cell 1 - - - - - - - Content from cell 2 - - - - - - - - - Content in the first column - - - - - - - Content in the second column - - - - - - - - - Click to see full table - - - - - - - - `; @@ -33785,6 +5169,25 @@ exports[`Storyshots UiKitMessage list uikitmessage 1`] = ` > Section + + Section + Markdown List + variant.outputs.each { output -> @@ -202,6 +212,7 @@ dependencies { implementation project(":reactnativekeyboardinput") implementation project(':@react-native-community_viewpager') implementation fileTree(dir: "libs", include: ["*.jar"]) + //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules implementation "com.google.firebase:firebase-messaging:18.0.0" implementation "com.google.firebase:firebase-core:16.0.9" @@ -209,6 +220,16 @@ dependencies { implementation('com.crashlytics.sdk.android:crashlytics:2.9.9@aar') { transitive = true } + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { + exclude group:'com.facebook.fbjni' + } + debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + } + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + } if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; diff --git a/android/app/src/debug/java/chat/rocket/reactnative/ReactNativeFlipper.java b/android/app/src/debug/java/chat/rocket/reactnative/ReactNativeFlipper.java new file mode 100644 index 000000000..07e08afd4 --- /dev/null +++ b/android/app/src/debug/java/chat/rocket/reactnative/ReactNativeFlipper.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.rndiffapp; +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.react.ReactFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + final FlipperClient client = AndroidFlipperClient.getInstance(context); + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new ReactFlipperPlugin()); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceManager.ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 25f62f1b0..8130afc86 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,13 +3,13 @@ package="chat.rocket.reactnative"> - + + diff --git a/android/app/src/main/assets/fonts/custom.ttf b/android/app/src/main/assets/fonts/custom.ttf index 78378284b..20adfc203 100755 Binary files a/android/app/src/main/assets/fonts/custom.ttf and b/android/app/src/main/assets/fonts/custom.ttf differ diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java index 2d1d5d22f..530a2a857 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java @@ -9,9 +9,11 @@ import com.facebook.react.PackageList; import com.facebook.hermes.reactexecutor.HermesExecutorFactory; import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.ReactApplication; +import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; +import java.lang.reflect.InvocationTargetException; import chat.rocket.reactnative.generated.BasePackageList; @@ -39,7 +41,7 @@ import java.util.List; public class MainApplication extends Application implements ReactApplication, INotificationsApplication { - private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(new BasePackageList().getPackageList(), Arrays.asList()); + private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(new BasePackageList().getPackageList(), null); private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -58,7 +60,11 @@ public class MainApplication extends Application implements ReactApplication, IN packages.add(new RNNotificationsPackage(MainApplication.this)); packages.add(new WatermelonDBPackage()); packages.add(new RNCViewPagerPackage()); - packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider)); + // packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider)); + List unimodules = Arrays.asList( + new ModuleRegistryAdapter(mModuleRegistryProvider) + ); + packages.addAll(unimodules); return packages; } @@ -82,6 +88,38 @@ public class MainApplication extends Application implements ReactApplication, IN public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); + initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + } + + /** + * Loads Flipper in React Native templates. Call this in the onCreate method with something like + * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + * + * @param context + * @param reactInstanceManager + */ + private static void initializeFlipper( + Context context, ReactInstanceManager reactInstanceManager) { + if (BuildConfig.DEBUG) { + try { + /* + We use reflection here to pick up the class that initializes Flipper, + since Flipper library is not available in release mode + */ + Class aClass = Class.forName("chat.rocket.reactnative"); + aClass + .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) + .invoke(null, context, reactInstanceManager); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } } @Override diff --git a/android/app/src/main/java/chat/rocket/reactnative/generated/BasePackageList.java b/android/app/src/main/java/chat/rocket/reactnative/generated/BasePackageList.java index 013db47e9..80778aba0 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/generated/BasePackageList.java +++ b/android/app/src/main/java/chat/rocket/reactnative/generated/BasePackageList.java @@ -11,6 +11,9 @@ public class BasePackageList { new expo.modules.constants.ConstantsPackage(), new expo.modules.filesystem.FileSystemPackage(), new expo.modules.haptics.HapticsPackage(), + new expo.modules.imageloader.ImageLoaderPackage(), + new expo.modules.keepawake.KeepAwakePackage(), + new expo.modules.localauthentication.LocalAuthenticationPackage(), new expo.modules.permissions.PermissionsPackage(), new expo.modules.webbrowser.WebBrowserPackage() ); diff --git a/android/build.gradle b/android/build.gradle index aaef31e10..f1e95820c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -18,7 +18,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.28.1' classpath 'com.google.firebase:perf-plugin:1.2.1' @@ -42,16 +42,14 @@ allprojects { url("$rootDir/../node_modules/jsc-android/dist") } maven { - // We should change it when Jitsi-SDK release v2.4 - url("$rootDir/../node_modules/react-native-jitsi-meet/jitsi-sdk") - // url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" + url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" } google() jcenter() maven { url 'https://maven.google.com' } - maven { url "https://jitpack.io" } + maven { url 'https://www.jitpack.io' } } } @@ -64,6 +62,12 @@ subprojects { subproject -> defaultConfig { targetSdkVersion 28 } + variantFilter { variant -> + def names = variant.flavors*.name + if (names.contains("reactNative59")) { + setIgnore(true) + } + } } } } diff --git a/android/gradle.properties b/android/gradle.properties index 801f89fdb..9f02f0b3b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -19,7 +19,11 @@ # android.enableAapt2=false # commenting this makes notifications to stop working # android.useDeprecatedNdk=true org.gradle.jvmargs=-Xmx2048M -XX\:MaxHeapSize\=32g +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX android.enableJetifier=true APPLICATIONID=chat.rocket.reactnative VERSIONNAME=4.5.1 @@ -29,3 +33,6 @@ KEYSTORE=my-upload-key.keystore KEY_ALIAS=my-key-alias KEYSTORE_PASSWORD= KEY_PASSWORD= + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.33.1 \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 29e566ccd..e8758b6d5 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/android/gradlew b/android/gradlew index 5e37288d6..77564ddb2 100755 --- a/android/gradlew +++ b/android/gradlew @@ -7,7 +7,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -44,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 33e202a56..69b3f916e 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -31,7 +31,7 @@ export const ROOMS = createRequestTypes('ROOMS', [ 'OPEN_SEARCH_HEADER', 'CLOSE_SEARCH_HEADER' ]); -export const ROOM = createRequestTypes('ROOM', ['LEAVE', 'DELETE', 'REMOVED', 'USER_TYPING']); +export const ROOM = createRequestTypes('ROOM', ['SUBSCRIBE', 'UNSUBSCRIBE', 'LEAVE', 'DELETE', 'REMOVED', 'CLOSE', 'FORWARD', 'USER_TYPING']); export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT', 'INIT_LOCAL_SETTINGS']); export const MESSAGES = createRequestTypes('MESSAGES', ['REPLY_BROADCAST']); export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes]); @@ -64,3 +64,4 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [ ...defaultTypes ]); export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']); +export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']); diff --git a/app/actions/room.js b/app/actions/room.js index 76a37b656..59916bec0 100644 --- a/app/actions/room.js +++ b/app/actions/room.js @@ -1,5 +1,19 @@ import * as types from './actionsTypes'; +export function subscribeRoom(rid) { + return { + type: types.ROOM.SUBSCRIBE, + rid + }; +} + +export function unsubscribeRoom(rid) { + return { + type: types.ROOM.UNSUBSCRIBE, + rid + }; +} + export function leaveRoom(rid, t) { return { type: types.ROOM.LEAVE, @@ -16,6 +30,21 @@ export function deleteRoom(rid, t) { }; } +export function closeRoom(rid) { + return { + type: types.ROOM.CLOSE, + rid + }; +} + +export function forwardRoom(rid, transferData) { + return { + type: types.ROOM.FORWARD, + transferData, + rid + }; +} + export function removedRoom() { return { type: types.ROOM.REMOVED diff --git a/app/constants/colors.js b/app/constants/colors.js index d38ac2cbf..220254816 100644 --- a/app/constants/colors.js +++ b/app/constants/colors.js @@ -46,7 +46,14 @@ export const themes = { messageboxBackground: '#ffffff', searchboxBackground: '#E6E6E7', buttonBackground: '#414852', - buttonText: '#ffffff' + buttonText: '#ffffff', + passcodeBackground: '#EEEFF1', + passcodeButtonActive: '#E4E7EA', + passcodeLockIcon: '#6C727A', + passcodePrimary: '#2F343D', + passcodeSecondary: '#6C727A', + passcodeDotEmpty: '#CBCED1', + passcodeDotFull: '#6C727A' }, dark: { backgroundColor: '#030b1b', @@ -81,7 +88,14 @@ export const themes = { messageboxBackground: '#0b182c', searchboxBackground: '#192d4d', buttonBackground: '#414852', - buttonText: '#ffffff' + buttonText: '#ffffff', + passcodeBackground: '#030C1B', + passcodeButtonActive: '#0B182C', + passcodeLockIcon: '#6C727A', + passcodePrimary: '#FFFFFF', + passcodeSecondary: '#CBCED1', + passcodeDotEmpty: '#CBCED1', + passcodeDotFull: '#6C727A' }, black: { backgroundColor: '#000000', @@ -100,7 +114,7 @@ export const themes = { infoText: '#6d6d72', tintColor: '#1e9bfe', auxiliaryTintColor: '#cdcdcd', - actionTintColor: '#1ea1fe', + actionTintColor: '#1e9bfe', separatorColor: '#272728', navbarBackground: '#0d0d0d', headerBorder: '#323232', @@ -116,6 +130,13 @@ export const themes = { messageboxBackground: '#0d0d0d', searchboxBackground: '#1f1f1f', buttonBackground: '#414852', - buttonText: '#ffffff' + buttonText: '#ffffff', + passcodeBackground: '#000000', + passcodeButtonActive: '#0E0D0D', + passcodeLockIcon: '#6C727A', + passcodePrimary: '#FFFFFF', + passcodeSecondary: '#CBCED1', + passcodeDotEmpty: '#CBCED1', + passcodeDotFull: '#6C727A' } }; diff --git a/app/constants/localAuthentication.js b/app/constants/localAuthentication.js new file mode 100644 index 000000000..1312e57aa --- /dev/null +++ b/app/constants/localAuthentication.js @@ -0,0 +1,12 @@ +export const PASSCODE_KEY = 'kPasscode'; +export const LOCKED_OUT_TIMER_KEY = 'kLockedOutTimer'; +export const ATTEMPTS_KEY = 'kAttempts'; + +export const LOCAL_AUTHENTICATE_EMITTER = 'LOCAL_AUTHENTICATE'; +export const CHANGE_PASSCODE_EMITTER = 'CHANGE_PASSCODE'; + +export const PASSCODE_LENGTH = 6; +export const MAX_ATTEMPTS = 6; +export const TIME_TO_LOCK = 30000; + +export const DEFAULT_AUTO_LOCK = 1800; diff --git a/app/constants/settings.js b/app/constants/settings.js index 5287367fb..ba63d4cab 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -68,6 +68,9 @@ export default { LDAP_Enable: { type: 'valueAsBoolean' }, + Livechat_request_comment_when_closing_conversation: { + type: 'valueAsBoolean' + }, Jitsi_Enabled: { type: 'valueAsBoolean' }, @@ -125,6 +128,9 @@ export default { uniqueID: { type: 'valueAsString' }, + UI_Allow_room_names_with_special_chars: { + type: 'valueAsBoolean' + }, UI_Use_Real_Name: { type: 'valueAsBoolean' }, @@ -157,5 +163,11 @@ export default { }, CAS_login_url: { type: 'valueAsString' + }, + Force_Screen_Lock: { + type: 'valueAsBoolean' + }, + Force_Screen_Lock_After: { + type: 'valueAsNumber' } }; diff --git a/app/containers/Avatar.js b/app/containers/Avatar.js index 2e4aa5353..66bef8411 100644 --- a/app/containers/Avatar.js +++ b/app/containers/Avatar.js @@ -2,12 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { View } from 'react-native'; import FastImage from 'react-native-fast-image'; +import Touchable from 'react-native-platform-touchable'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; -import Touch from '../utils/touch'; + import { avatarURL } from '../utils/avatar'; +import Emoji from './markdown/Emoji'; const Avatar = React.memo(({ - text, size, baseUrl, borderRadius, style, avatar, type, children, userId, token, onPress, theme + text, size, baseUrl, borderRadius, style, avatar, type, children, userId, token, onPress, theme, emoji, getCustomEmoji }) => { const avatarStyle = { width: size, @@ -23,7 +25,15 @@ const Avatar = React.memo(({ type, text, size, userId, token, avatar, baseUrl }); - let image = ( + let image = emoji ? ( + + ) : ( + {image} - + ); } @@ -55,6 +65,7 @@ Avatar.propTypes = { style: PropTypes.any, text: PropTypes.string, avatar: PropTypes.string, + emoji: PropTypes.string, size: PropTypes.number, borderRadius: PropTypes.number, type: PropTypes.string, @@ -62,7 +73,8 @@ Avatar.propTypes = { userId: PropTypes.string, token: PropTypes.string, theme: PropTypes.string, - onPress: PropTypes.func + onPress: PropTypes.func, + getCustomEmoji: PropTypes.func }; Avatar.defaultProps = { diff --git a/app/containers/FormContainer.js b/app/containers/FormContainer.js index fda039765..763e98df6 100644 --- a/app/containers/FormContainer.js +++ b/app/containers/FormContainer.js @@ -23,7 +23,7 @@ export const FormContainerInner = ({ children }) => ( ); -const FormContainer = ({ children, theme }) => ( +const FormContainer = ({ children, theme, testID }) => ( ( > - + {children} @@ -41,6 +41,7 @@ const FormContainer = ({ children, theme }) => ( FormContainer.propTypes = { theme: PropTypes.string, + testID: PropTypes.string, children: PropTypes.element }; diff --git a/app/containers/ItemInfo.js b/app/containers/ItemInfo.js new file mode 100644 index 000000000..c7d541e64 --- /dev/null +++ b/app/containers/ItemInfo.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { View, Text, StyleSheet } from 'react-native'; +import PropTypes from 'prop-types'; + +import sharedStyles from '../views/Styles'; +import { themes } from '../constants/colors'; + +const styles = StyleSheet.create({ + infoContainer: { + padding: 15 + }, + infoText: { + fontSize: 14, + ...sharedStyles.textRegular + } +}); + +const ItemInfo = React.memo(({ info, theme }) => ( + + {info} + +)); + +ItemInfo.propTypes = { + info: PropTypes.string, + theme: PropTypes.string +}; + +export default ItemInfo; diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js index e6e685b34..2cf06d327 100644 --- a/app/containers/LoginServices.js +++ b/app/containers/LoginServices.js @@ -12,7 +12,7 @@ import sharedStyles from '../views/Styles'; import { themes } from '../constants/colors'; import { loginRequest as loginRequestAction } from '../actions/login'; import Button from './Button'; -import OnboardingSeparator from './OnboardingSeparator'; +import OrSeparator from './OrSeparator'; import Touch from '../utils/touch'; import I18n from '../i18n'; import random from '../utils/random'; @@ -252,12 +252,12 @@ class LoginServices extends React.PureComponent { style={styles.options} color={themes[theme].actionTintColor} /> - + ); } if (length > 0 && separator) { - return ; + return ; } return null; } diff --git a/app/containers/MessageBox/Mentions/MentionItem.js b/app/containers/MessageBox/Mentions/MentionItem.js index de4489b26..6a3505da2 100644 --- a/app/containers/MessageBox/Mentions/MentionItem.js +++ b/app/containers/MessageBox/Mentions/MentionItem.js @@ -64,7 +64,7 @@ const MentionItem = ({ content = ( <> / - { item.command} + {item.id} ); } diff --git a/app/containers/MessageBox/Recording.js b/app/containers/MessageBox/Recording.js index 85db3eb8d..c471f2d94 100644 --- a/app/containers/MessageBox/Recording.js +++ b/app/containers/MessageBox/Recording.js @@ -5,6 +5,7 @@ import { } from 'react-native'; import { AudioRecorder, AudioUtils } from 'react-native-audio'; import { BorderlessButton } from 'react-native-gesture-handler'; +import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; import RNFetchBlob from 'rn-fetch-blob'; import styles from './styles'; @@ -59,7 +60,8 @@ export default class extends React.PureComponent { SampleRate: 22050, Channels: 1, AudioQuality: 'Low', - AudioEncoding: 'aac' + AudioEncoding: 'aac', + OutputFormat: 'aac_adts' }); AudioRecorder.onProgress = (data) => { @@ -74,12 +76,16 @@ export default class extends React.PureComponent { } }; AudioRecorder.startRecording(); + + activateKeepAwake(); } componentWillUnmount() { if (this.recording) { this.cancelAudioMessage(); } + + deactivateKeepAwake(); } finishRecording = (didSucceed, filePath, size) => { diff --git a/app/containers/MessageBox/buttons/SendButton.js b/app/containers/MessageBox/buttons/SendButton.js index 4e76aaea7..659a484ae 100644 --- a/app/containers/MessageBox/buttons/SendButton.js +++ b/app/containers/MessageBox/buttons/SendButton.js @@ -8,7 +8,7 @@ const SendButton = React.memo(({ theme, onPress }) => ( onPress={onPress} testID='messagebox-send-message' accessibilityLabel='Send_message' - icon='send1' + icon='Send-active' theme={theme} /> )); diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js index 19141af37..39e29dbc7 100644 --- a/app/containers/MessageBox/index.js +++ b/app/containers/MessageBox/index.js @@ -190,7 +190,7 @@ class MessageBox extends Component { }); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { isFocused, editing, replying } = this.props; if (!isFocused()) { return; @@ -306,9 +306,9 @@ class MessageBox extends Component { if (!isTextEmpty) { try { - const { start, end } = this.component._lastNativeSelection; + const { start, end } = this.component?.lastNativeSelection; const cursor = Math.max(start, end); - const lastNativeText = this.component._lastNativeText || ''; + const lastNativeText = this.component?.lastNativeText || ''; // matches if text either starts with '/' or have (@,#,:) then it groups whatever comes next of mention type const regexp = /(#|@|:|^\/)([a-z0-9._-]+)$/im; const result = lastNativeText.substr(0, cursor).match(regexp); @@ -339,7 +339,7 @@ class MessageBox extends Component { } const { trackingType } = this.state; const msg = this.text; - const { start, end } = this.component._lastNativeSelection; + const { start, end } = this.component?.lastNativeSelection; const cursor = Math.max(start, end); const regexp = /([a-z0-9._-]+)$/im; const result = msg.substr(0, cursor).replace(regexp, ''); @@ -383,8 +383,8 @@ class MessageBox extends Component { let newText = ''; // if messagebox has an active cursor - if (this.component && this.component._lastNativeSelection) { - const { start, end } = this.component._lastNativeSelection; + if (this.component?.lastNativeSelection) { + const { start, end } = this.component.lastNativeSelection; const cursor = Math.max(start, end); newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`; } else { diff --git a/app/containers/OnboardingSeparator.js b/app/containers/OrSeparator.js similarity index 89% rename from app/containers/OnboardingSeparator.js rename to app/containers/OrSeparator.js index ff18bf6a9..665c14538 100644 --- a/app/containers/OnboardingSeparator.js +++ b/app/containers/OrSeparator.js @@ -24,7 +24,7 @@ const styles = StyleSheet.create({ } }); -const DateSeparator = React.memo(({ theme }) => { +const OrSeparator = React.memo(({ theme }) => { const line = { backgroundColor: themes[theme].borderColor }; const text = { color: themes[theme].auxiliaryText }; return ( @@ -36,8 +36,8 @@ const DateSeparator = React.memo(({ theme }) => { ); }); -DateSeparator.propTypes = { +OrSeparator.propTypes = { theme: PropTypes.string }; -export default DateSeparator; +export default OrSeparator; diff --git a/app/containers/Passcode/Base/Button.js b/app/containers/Passcode/Base/Button.js new file mode 100644 index 000000000..7826ed5fe --- /dev/null +++ b/app/containers/Passcode/Base/Button.js @@ -0,0 +1,47 @@ +import React from 'react'; +import { Text } from 'react-native'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import { themes } from '../../../constants/colors'; +import Touch from '../../../utils/touch'; +import { CustomIcon } from '../../../lib/Icons'; + +const Button = React.memo(({ + text, disabled, theme, onPress, icon +}) => { + const press = () => onPress && onPress(text); + + return ( + + { + icon + ? ( + + ) + : ( + + {text} + + ) + } + + ); +}); + +Button.propTypes = { + text: PropTypes.string, + icon: PropTypes.string, + theme: PropTypes.string, + disabled: PropTypes.bool, + onPress: PropTypes.func +}; + +export default Button; diff --git a/app/containers/Passcode/Base/Dots.js b/app/containers/Passcode/Base/Dots.js new file mode 100644 index 000000000..d53aaa783 --- /dev/null +++ b/app/containers/Passcode/Base/Dots.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { View } from 'react-native'; +import _ from 'lodash'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import { themes } from '../../../constants/colors'; + +const SIZE_EMPTY = 12; +const SIZE_FULL = 16; + +const Dots = React.memo(({ passcode, theme, length }) => ( + + {_.range(length).map((val) => { + const lengthSup = (passcode.length >= val + 1); + const height = lengthSup ? SIZE_FULL : SIZE_EMPTY; + const width = lengthSup ? SIZE_FULL : SIZE_EMPTY; + let backgroundColor = ''; + if (lengthSup && passcode.length > 0) { + backgroundColor = themes[theme].passcodeDotFull; + } else { + backgroundColor = themes[theme].passcodeDotEmpty; + } + const borderRadius = lengthSup ? SIZE_FULL / 2 : SIZE_EMPTY / 2; + const marginRight = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10; + const marginLeft = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10; + return ( + + + + ); + })} + +)); + +Dots.propTypes = { + passcode: PropTypes.string, + theme: PropTypes.string, + length: PropTypes.string +}; + +export default Dots; diff --git a/app/containers/Passcode/Base/LockIcon.js b/app/containers/Passcode/Base/LockIcon.js new file mode 100644 index 000000000..023a355c7 --- /dev/null +++ b/app/containers/Passcode/Base/LockIcon.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { View } from 'react-native'; +import { Row } from 'react-native-easy-grid'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import { themes } from '../../../constants/colors'; +import { CustomIcon } from '../../../lib/Icons'; + +const LockIcon = React.memo(({ theme }) => ( + + + + + +)); + +LockIcon.propTypes = { + theme: PropTypes.string +}; + +export default LockIcon; diff --git a/app/containers/Passcode/Base/Locked.js b/app/containers/Passcode/Base/Locked.js new file mode 100644 index 000000000..8371c7287 --- /dev/null +++ b/app/containers/Passcode/Base/Locked.js @@ -0,0 +1,74 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { Grid } from 'react-native-easy-grid'; + +import { themes } from '../../../constants/colors'; +import { resetAttempts } from '../../../utils/localAuthentication'; +import { TYPE } from '../constants'; +import { getLockedUntil, getDiff } from '../utils'; +import I18n from '../../../i18n'; +import styles from './styles'; +import Title from './Title'; +import Subtitle from './Subtitle'; +import LockIcon from './LockIcon'; + +const Timer = React.memo(({ time, theme, setStatus }) => { + const calcTimeLeft = () => { + const diff = getDiff(time); + if (diff > 0) { + return Math.floor((diff / 1000) % 60); + } + }; + + const [timeLeft, setTimeLeft] = useState(calcTimeLeft()); + + useEffect(() => { + setTimeout(() => { + setTimeLeft(calcTimeLeft()); + if (timeLeft <= 1) { + resetAttempts(); + setStatus(TYPE.ENTER); + } + }, 1000); + }); + + if (!timeLeft) { + return null; + } + + return ; +}); + +const Locked = React.memo(({ theme, setStatus }) => { + const [lockedUntil, setLockedUntil] = useState(null); + + const readItemFromStorage = async() => { + const l = await getLockedUntil(); + setLockedUntil(l); + }; + + useEffect(() => { + readItemFromStorage(); + }, []); + + return ( + + + + <Timer theme={theme} time={lockedUntil} setStatus={setStatus} /> + </Grid> + ); +}); + +Locked.propTypes = { + theme: PropTypes.string, + setStatus: PropTypes.func +}; + +Timer.propTypes = { + time: PropTypes.string, + theme: PropTypes.string, + setStatus: PropTypes.func +}; + +export default Locked; diff --git a/app/containers/Passcode/Base/Subtitle.js b/app/containers/Passcode/Base/Subtitle.js new file mode 100644 index 000000000..2ac69fee7 --- /dev/null +++ b/app/containers/Passcode/Base/Subtitle.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +import { Row } from 'react-native-easy-grid'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import { themes } from '../../../constants/colors'; + +const Subtitle = React.memo(({ text, theme }) => ( + <Row style={styles.row}> + <View style={styles.subtitleView}> + <Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text> + </View> + </Row> +)); + +Subtitle.propTypes = { + text: PropTypes.string, + theme: PropTypes.string +}; + +export default Subtitle; diff --git a/app/containers/Passcode/Base/Title.js b/app/containers/Passcode/Base/Title.js new file mode 100644 index 000000000..0aa96d757 --- /dev/null +++ b/app/containers/Passcode/Base/Title.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +import { Row } from 'react-native-easy-grid'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import { themes } from '../../../constants/colors'; + +const Title = React.memo(({ text, theme }) => ( + <Row style={styles.row}> + <View style={styles.titleView}> + <Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text> + </View> + </Row> +)); + +Title.propTypes = { + text: PropTypes.string, + theme: PropTypes.string +}; + +export default Title; diff --git a/app/containers/Passcode/Base/index.js b/app/containers/Passcode/Base/index.js new file mode 100644 index 000000000..d6c584a68 --- /dev/null +++ b/app/containers/Passcode/Base/index.js @@ -0,0 +1,139 @@ +import React, { + useState, forwardRef, useImperativeHandle, useRef +} from 'react'; +import { Col, Row, Grid } from 'react-native-easy-grid'; +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import * as Animatable from 'react-native-animatable'; +import * as Haptics from 'expo-haptics'; + +import styles from './styles'; +import Button from './Button'; +import Dots from './Dots'; +import { TYPE } from '../constants'; +import { themes } from '../../../constants/colors'; +import { PASSCODE_LENGTH } from '../../../constants/localAuthentication'; +import LockIcon from './LockIcon'; +import Title from './Title'; +import Subtitle from './Subtitle'; + +const Base = forwardRef(({ + theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress +}, ref) => { + const rootRef = useRef(); + const dotsRef = useRef(); + const [passcode, setPasscode] = useState(''); + + const clearPasscode = () => setPasscode(''); + + const wrongPasscode = () => { + clearPasscode(); + dotsRef?.current?.shake(500); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); + }; + + const animate = (animation, duration = 500) => { + rootRef?.current?.[animation](duration); + }; + + const onPressNumber = text => setPasscode((p) => { + const currentPasscode = p + text; + if (currentPasscode?.length === PASSCODE_LENGTH) { + switch (type) { + case TYPE.CHOOSE: + onEndProcess(currentPasscode); + break; + case TYPE.CONFIRM: + if (currentPasscode !== previousPasscode) { + onError(); + } else { + onEndProcess(currentPasscode); + } + break; + case TYPE.ENTER: + onEndProcess(currentPasscode); + break; + default: + break; + } + } + return currentPasscode; + }); + + const onPressDelete = () => setPasscode((p) => { + if (p?.length > 0) { + const newPasscode = p.slice(0, -1); + return newPasscode; + } + return ''; + }); + + useImperativeHandle(ref, () => ({ + wrongPasscode, animate, clearPasscode + })); + + return ( + <Animatable.View ref={rootRef} style={styles.container}> + <Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}> + <LockIcon theme={theme} /> + <Title text={title} theme={theme} /> + <Subtitle text={subtitle} theme={theme} /> + <Row style={styles.row}> + <Animatable.View ref={dotsRef}> + <Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} /> + </Animatable.View> + </Row> + <Row style={[styles.row, styles.buttonRow]}> + {_.range(1, 4).map(i => ( + <Col key={i} style={styles.colButton}> + <Button text={i} theme={theme} onPress={onPressNumber} /> + </Col> + ))} + </Row> + <Row style={[styles.row, styles.buttonRow]}> + {_.range(4, 7).map(i => ( + <Col key={i} style={styles.colButton}> + <Button text={i} theme={theme} onPress={onPressNumber} /> + </Col> + ))} + </Row> + <Row style={[styles.row, styles.buttonRow]}> + {_.range(7, 10).map(i => ( + <Col key={i} style={styles.colButton}> + <Button text={i} theme={theme} onPress={onPressNumber} /> + </Col> + ))} + </Row> + <Row style={[styles.row, styles.buttonRow]}> + {showBiometry + ? ( + <Col style={styles.colButton}> + <Button icon='fingerprint' theme={theme} onPress={onBiometryPress} /> + </Col> + ) + : <Col style={styles.colButton} />} + <Col style={styles.colButton}> + <Button text='0' theme={theme} onPress={onPressNumber} /> + </Col> + <Col style={styles.colButton}> + <Button icon='backspace' theme={theme} onPress={onPressDelete} /> + </Col> + </Row> + </Grid> + </Animatable.View> + ); +}); + +Base.propTypes = { + theme: PropTypes.string, + type: PropTypes.string, + previousPasscode: PropTypes.string, + title: PropTypes.string, + subtitle: PropTypes.string, + showBiometry: PropTypes.string, + onEndProcess: PropTypes.func, + onError: PropTypes.func, + onBiometryPress: PropTypes.func +}; + +export default Base; diff --git a/app/containers/Passcode/Base/styles.js b/app/containers/Passcode/Base/styles.js new file mode 100644 index 000000000..252e5a854 --- /dev/null +++ b/app/containers/Passcode/Base/styles.js @@ -0,0 +1,70 @@ +import { StyleSheet } from 'react-native'; + +import sharedStyles from '../../../views/Styles'; + +export default StyleSheet.create({ + container: { + flex: 1 + }, + titleView: { + justifyContent: 'center' + }, + subtitleView: { + justifyContent: 'center', + height: 32 + }, + row: { + flex: 0, + alignItems: 'center', + justifyContent: 'center' + }, + buttonRow: { + height: 102 + }, + colButton: { + flex: 0, + marginLeft: 12, + marginRight: 12, + alignItems: 'center', + width: 78, + height: 78 + }, + buttonText: { + fontSize: 28, + ...sharedStyles.textRegular + }, + buttonView: { + alignItems: 'center', + justifyContent: 'center', + width: 78, + height: 78, + borderRadius: 4 + }, + textTitle: { + fontSize: 22, + ...sharedStyles.textRegular + }, + textSubtitle: { + fontSize: 16, + ...sharedStyles.textMedium + }, + dotsContainer: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + marginTop: 24, + marginBottom: 40 + }, + dotsView: { + justifyContent: 'center', + alignItems: 'center', + height: 16 + }, + grid: { + justifyContent: 'center', + flexDirection: 'column' + }, + iconView: { + marginVertical: 16 + } +}); diff --git a/app/containers/Passcode/PasscodeChoose.js b/app/containers/Passcode/PasscodeChoose.js new file mode 100644 index 000000000..cabc71978 --- /dev/null +++ b/app/containers/Passcode/PasscodeChoose.js @@ -0,0 +1,69 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import * as Haptics from 'expo-haptics'; +import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; + +import Base from './Base'; +import { TYPE } from './constants'; +import I18n from '../../i18n'; + +const PasscodeChoose = ({ theme, finishProcess, force = false }) => { + const chooseRef = useRef(null); + const confirmRef = useRef(null); + const [subtitle, setSubtitle] = useState(null); + const [status, setStatus] = useState(TYPE.CHOOSE); + const [previousPasscode, setPreviouPasscode] = useState(null); + + const firstStep = (p) => { + setTimeout(() => { + setStatus(TYPE.CONFIRM); + setPreviouPasscode(p); + confirmRef?.current?.clearPasscode(); + }, 200); + }; + + const changePasscode = p => finishProcess && finishProcess(p); + + const onError = () => { + setTimeout(() => { + setStatus(TYPE.CHOOSE); + setSubtitle(I18n.t('Passcode_choose_error')); + chooseRef?.current?.animate('shake'); + chooseRef?.current?.clearPasscode(); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); + }, 200); + }; + + if (status === TYPE.CONFIRM) { + return ( + <Base + ref={confirmRef} + theme={theme} + type={TYPE.CONFIRM} + onEndProcess={changePasscode} + previousPasscode={previousPasscode} + title={I18n.t('Passcode_choose_confirm_title')} + onError={onError} + /> + ); + } + + return ( + <Base + ref={chooseRef} + theme={theme} + type={TYPE.CHOOSE} + onEndProcess={firstStep} + title={I18n.t('Passcode_choose_title')} + subtitle={subtitle || (force ? I18n.t('Passcode_choose_force_set') : null)} + /> + ); +}; + +PasscodeChoose.propTypes = { + theme: PropTypes.string, + force: PropTypes.bool, + finishProcess: PropTypes.func +}; + +export default gestureHandlerRootHOC(PasscodeChoose); diff --git a/app/containers/Passcode/PasscodeEnter.js b/app/containers/Passcode/PasscodeEnter.js new file mode 100644 index 000000000..3721b9fee --- /dev/null +++ b/app/containers/Passcode/PasscodeEnter.js @@ -0,0 +1,106 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useAsyncStorage } from '@react-native-community/async-storage'; +import RNUserDefaults from 'rn-user-defaults'; +import PropTypes from 'prop-types'; +import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; +import * as Haptics from 'expo-haptics'; +import { sha256 } from 'js-sha256'; + +import Base from './Base'; +import Locked from './Base/Locked'; +import { TYPE } from './constants'; +import { + ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, PASSCODE_KEY, MAX_ATTEMPTS +} from '../../constants/localAuthentication'; +import { resetAttempts, biometryAuth } from '../../utils/localAuthentication'; +import { getLockedUntil, getDiff } from './utils'; +import I18n from '../../i18n'; + +const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => { + const ref = useRef(null); + let attempts = 0; + let lockedUntil = false; + const [passcode, setPasscode] = useState(null); + const [status, setStatus] = useState(null); + const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY); + const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY); + + const fetchPasscode = async() => { + const p = await RNUserDefaults.get(PASSCODE_KEY); + setPasscode(p); + }; + + const biometry = async() => { + if (hasBiometry && status === TYPE.ENTER) { + const result = await biometryAuth(); + if (result?.success) { + finishProcess(); + } + } + }; + + const readStorage = async() => { + lockedUntil = await getLockedUntil(); + if (lockedUntil) { + const diff = getDiff(lockedUntil); + if (diff <= 1) { + await resetAttempts(); + setStatus(TYPE.ENTER); + } else { + attempts = await getAttempts(); + setStatus(TYPE.LOCKED); + } + } else { + setStatus(TYPE.ENTER); + } + await fetchPasscode(); + biometry(); + }; + + useEffect(() => { + readStorage(); + }, [status]); + + const onEndProcess = (p) => { + setTimeout(() => { + if (sha256(p) === passcode) { + finishProcess(); + } else { + attempts += 1; + if (attempts >= MAX_ATTEMPTS) { + setStatus(TYPE.LOCKED); + setLockedUntil(new Date().toISOString()); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); + } else { + ref.current.wrongPasscode(); + setAttempts(attempts?.toString()); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning); + } + } + }, 200); + }; + + if (status === TYPE.LOCKED) { + return <Locked theme={theme} setStatus={setStatus} />; + } + + return ( + <Base + ref={ref} + theme={theme} + type={TYPE.ENTER} + title={I18n.t('Passcode_enter_title')} + showBiometry={hasBiometry} + onEndProcess={onEndProcess} + onBiometryPress={biometry} + /> + ); +}; + +PasscodeEnter.propTypes = { + theme: PropTypes.string, + hasBiometry: PropTypes.string, + finishProcess: PropTypes.func +}; + +export default gestureHandlerRootHOC(PasscodeEnter); diff --git a/app/containers/Passcode/constants.js b/app/containers/Passcode/constants.js new file mode 100644 index 000000000..d284ee241 --- /dev/null +++ b/app/containers/Passcode/constants.js @@ -0,0 +1,6 @@ +export const TYPE = { + CHOOSE: 'choose', + CONFIRM: 'confirm', + ENTER: 'enter', + LOCKED: 'locked' +}; diff --git a/app/containers/Passcode/index.js b/app/containers/Passcode/index.js new file mode 100644 index 000000000..489003058 --- /dev/null +++ b/app/containers/Passcode/index.js @@ -0,0 +1,4 @@ +import PasscodeEnter from './PasscodeEnter'; +import PasscodeChoose from './PasscodeChoose'; + +export { PasscodeEnter, PasscodeChoose }; diff --git a/app/containers/Passcode/utils.js b/app/containers/Passcode/utils.js new file mode 100644 index 000000000..bbb14eea7 --- /dev/null +++ b/app/containers/Passcode/utils.js @@ -0,0 +1,14 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import moment from 'moment'; + +import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication'; + +export const getLockedUntil = async() => { + const t = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY); + if (t) { + return moment(t).add(TIME_TO_LOCK); + } + return null; +}; + +export const getDiff = t => new Date(t) - new Date(); diff --git a/app/containers/RoomTypeIcon.js b/app/containers/RoomTypeIcon.js index 4e4d0b1d4..b35d83a60 100644 --- a/app/containers/RoomTypeIcon.js +++ b/app/containers/RoomTypeIcon.js @@ -2,7 +2,7 @@ import React from 'react'; import { Image, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import { CustomIcon } from '../lib/Icons'; -import { themes } from '../constants/colors'; +import { STATUS_COLORS, themes } from '../constants/colors'; const styles = StyleSheet.create({ style: { @@ -15,7 +15,7 @@ const styles = StyleSheet.create({ }); const RoomTypeIcon = React.memo(({ - type, size, isGroupChat, style, theme + type, size, isGroupChat, status, style, theme }) => { if (!type) { return null; @@ -36,7 +36,7 @@ const RoomTypeIcon = React.memo(({ } return <CustomIcon name='at' size={13} style={[styles.style, styles.discussion, { color }]} />; } if (type === 'l') { - return <CustomIcon name='livechat' size={13} style={[styles.style, styles.discussion, { color }]} />; + return <CustomIcon name='omnichannel' size={13} style={[styles.style, styles.discussion, { color: STATUS_COLORS[status] }]} />; } return <Image source={{ uri: 'lock' }} style={[styles.style, style, { width: size, height: size, tintColor: color }]} />; }); @@ -45,6 +45,7 @@ RoomTypeIcon.propTypes = { theme: PropTypes.string, type: PropTypes.string, isGroupChat: PropTypes.bool, + status: PropTypes.string, size: PropTypes.number, style: PropTypes.object }; diff --git a/app/containers/TextInput.js b/app/containers/TextInput.js index dc071971f..a7c7d473c 100644 --- a/app/containers/TextInput.js +++ b/app/containers/TextInput.js @@ -64,8 +64,10 @@ export default class RCTextInput extends React.PureComponent { inputRef: PropTypes.func, testID: PropTypes.string, iconLeft: PropTypes.string, + iconRight: PropTypes.string, placeholder: PropTypes.string, left: PropTypes.element, + onIconRightPress: PropTypes.func, theme: PropTypes.string } @@ -90,6 +92,19 @@ export default class RCTextInput extends React.PureComponent { ); } + get iconRight() { + const { iconRight, onIconRightPress, theme } = this.props; + return ( + <BorderlessButton onPress={onIconRightPress} style={[styles.iconContainer, styles.iconRight]}> + <CustomIcon + name={iconRight} + style={{ color: themes[theme].bodyText }} + size={20} + /> + </BorderlessButton> + ); + } + get iconPassword() { const { showPassword } = this.state; const { testID, theme } = this.props; @@ -117,7 +132,7 @@ export default class RCTextInput extends React.PureComponent { render() { const { showPassword } = this.state; const { - label, left, error, loading, secureTextEntry, containerStyle, inputRef, iconLeft, inputStyle, testID, placeholder, theme, ...inputProps + label, left, error, loading, secureTextEntry, containerStyle, inputRef, iconLeft, iconRight, inputStyle, testID, placeholder, theme, ...inputProps } = this.props; const { dangerColor } = themes[theme]; return ( @@ -140,7 +155,7 @@ export default class RCTextInput extends React.PureComponent { style={[ styles.input, iconLeft && styles.inputIconLeft, - secureTextEntry && styles.inputIconRight, + (secureTextEntry || iconRight) && styles.inputIconRight, { backgroundColor: themes[theme].backgroundColor, borderColor: themes[theme].separatorColor, @@ -165,6 +180,7 @@ export default class RCTextInput extends React.PureComponent { {...inputProps} /> {iconLeft ? this.iconLeft : null} + {iconRight ? this.iconRight : null} {secureTextEntry ? this.iconPassword : null} {loading ? this.loading : null} {left} diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js index 3d1c9fe6d..0f32d2e2d 100644 --- a/app/containers/TwoFactor/index.js +++ b/app/containers/TwoFactor/index.js @@ -92,7 +92,7 @@ const TwoFactor = React.memo(({ theme, split }) => { isVisible={visible} hideModalContentWhileAnimating > - <View style={styles.container}> + <View style={styles.container} testID='two-factor'> <View style={[styles.content, split && [sharedStyles.modal, sharedStyles.modalFormSheet], { backgroundColor: themes[theme].backgroundColor }]}> <Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text> {method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null} @@ -106,6 +106,7 @@ const TwoFactor = React.memo(({ theme, split }) => { keyboardType={method?.keyboardType} secureTextEntry={method?.secureTextEntry} error={data.invalid && { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') }} + testID='two-factor-input' /> {isEmail && <Text style={[styles.sendEmail, { color }]} onPress={sendEmail}>{I18n.t('Send_me_the_code_again')}</Text>} <View style={styles.buttonContainer}> @@ -123,6 +124,7 @@ const TwoFactor = React.memo(({ theme, split }) => { style={styles.button} onPress={onSubmit} theme={theme} + testID='two-factor-send' /> </View> </View> diff --git a/app/containers/UIKit/MultiSelect/Chips.js b/app/containers/UIKit/MultiSelect/Chips.js index 59264db0a..496ba2261 100644 --- a/app/containers/UIKit/MultiSelect/Chips.js +++ b/app/containers/UIKit/MultiSelect/Chips.js @@ -12,11 +12,13 @@ import styles from './styles'; const keyExtractor = item => item.value.toString(); -const Chip = ({ item, onSelect, theme }) => ( +const Chip = ({ + item, onSelect, style, theme +}) => ( <Touchable key={item.value} onPress={() => onSelect(item)} - style={[styles.chip, { backgroundColor: themes[theme].auxiliaryBackground }]} + style={[styles.chip, { backgroundColor: themes[theme].auxiliaryBackground }, style]} background={Touchable.Ripple(themes[theme].bannerBackground)} > <> @@ -29,17 +31,21 @@ const Chip = ({ item, onSelect, theme }) => ( Chip.propTypes = { item: PropTypes.object, onSelect: PropTypes.func, + style: PropTypes.object, theme: PropTypes.string }; -const Chips = ({ items, onSelect, theme }) => ( +const Chips = ({ + items, onSelect, style, theme +}) => ( <View style={styles.chips}> - {items.map(item => <Chip key={keyExtractor(item)} item={item} onSelect={onSelect} theme={theme} />)} + {items.map(item => <Chip key={keyExtractor(item)} item={item} onSelect={onSelect} style={style} theme={theme} />)} </View> ); Chips.propTypes = { items: PropTypes.array, onSelect: PropTypes.func, + style: PropTypes.object, theme: PropTypes.string }; diff --git a/app/containers/UIKit/MultiSelect/Input.js b/app/containers/UIKit/MultiSelect/Input.js index d4de6917c..6a7085fae 100644 --- a/app/containers/UIKit/MultiSelect/Input.js +++ b/app/containers/UIKit/MultiSelect/Input.js @@ -1,5 +1,5 @@ import React from 'react'; -import { View } from 'react-native'; +import { View, Text } from 'react-native'; import PropTypes from 'prop-types'; import Touchable from 'react-native-platform-touchable'; @@ -9,16 +9,16 @@ import ActivityIndicator from '../../ActivityIndicator'; import styles from './styles'; const Input = ({ - children, open, theme, loading, inputStyle, disabled + children, onPress, theme, loading, inputStyle, placeholder, disabled }) => ( <Touchable - onPress={() => open(true)} + onPress={onPress} style={[{ backgroundColor: themes[theme].backgroundColor }, inputStyle]} background={Touchable.Ripple(themes[theme].bannerBackground)} disabled={disabled} > <View style={[styles.input, { borderColor: themes[theme].separatorColor }]}> - {children} + {placeholder ? <Text style={[styles.pickerText, { color: themes[theme].auxiliaryText }]}>{placeholder}</Text> : children} { loading ? <ActivityIndicator style={[styles.loading, styles.icon]} /> @@ -29,10 +29,11 @@ const Input = ({ ); Input.propTypes = { children: PropTypes.node, - open: PropTypes.func, + onPress: PropTypes.func, theme: PropTypes.string, inputStyle: PropTypes.object, disabled: PropTypes.bool, + placeholder: PropTypes.string, loading: PropTypes.bool }; diff --git a/app/containers/UIKit/MultiSelect/index.js b/app/containers/UIKit/MultiSelect/index.js index 6aa671a36..b88330e4e 100644 --- a/app/containers/UIKit/MultiSelect/index.js +++ b/app/containers/UIKit/MultiSelect/index.js @@ -43,14 +43,14 @@ export const MultiSelect = React.memo(({ inputStyle, theme }) => { - const [selected, select] = useState(values || []); + const [selected, select] = useState(Array.isArray(values) ? values : []); const [open, setOpen] = useState(false); const [search, onSearchChange] = useState(''); const [currentValue, setCurrentValue] = useState(''); const [showContent, setShowContent] = useState(false); useEffect(() => { - if (values) { + if (Array.isArray(values)) { select(values); } }, [values]); @@ -136,7 +136,7 @@ export const MultiSelect = React.memo(({ /> ) : ( <Input - open={onShow} + onPress={onShow} theme={theme} loading={loading} disabled={disabled} @@ -150,7 +150,7 @@ export const MultiSelect = React.memo(({ const items = options.filter(option => selected.includes(option.value)); button = ( <Input - open={onShow} + onPress={onShow} theme={theme} loading={loading} disabled={disabled} diff --git a/app/containers/UIKit/Section.js b/app/containers/UIKit/Section.js index a97712b81..7e509d776 100644 --- a/app/containers/UIKit/Section.js +++ b/app/containers/UIKit/Section.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { themes } from '../../constants/colors'; -import sharedStyles from '../../views/Styles'; const styles = StyleSheet.create({ content: { @@ -18,11 +17,7 @@ const styles = StyleSheet.create({ }, text: { flex: 1, - padding: 4, - fontSize: 16, - lineHeight: 22, - textAlignVertical: 'center', - ...sharedStyles.textRegular + padding: 4 }, field: { marginVertical: 6 @@ -54,7 +49,7 @@ export const Section = ({ accessory && accessoriesRight.includes(accessory.type) ? styles.row : styles.column ]} > - {text ? <Text style={[styles.text, { color: themes[theme].bodyText }]}>{parser.text(text)}</Text> : null} + {text ? <View style={styles.text}>{parser.text(text)}</View> : null} {fields ? <Fields fields={fields} theme={theme} parser={parser} /> : null} {accessory ? <Accessory element={{ blockId, appId, ...accessory }} parser={parser} /> : null} </View> diff --git a/app/containers/UIKit/index.js b/app/containers/UIKit/index.js index 5d8e36bf8..84cac8861 100644 --- a/app/containers/UIKit/index.js +++ b/app/containers/UIKit/index.js @@ -1,6 +1,6 @@ /* eslint-disable class-methods-use-this */ import React, { useContext } from 'react'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, Text } from 'react-native'; import { uiKitMessage, UiKitParserMessage, @@ -13,8 +13,9 @@ import Markdown from '../markdown'; import Button from '../Button'; import TextInput from '../TextInput'; -import { useBlockContext } from './utils'; +import { useBlockContext, textParser } from './utils'; import { themes } from '../../constants/colors'; +import sharedStyles from '../../views/Styles'; import { Divider } from './Divider'; import { Section } from './Section'; @@ -37,6 +38,12 @@ const styles = StyleSheet.create({ }, button: { marginBottom: 16 + }, + text: { + fontSize: 16, + lineHeight: 22, + textAlignVertical: 'center', + ...sharedStyles.textRegular } }); @@ -46,7 +53,7 @@ class MessageParser extends UiKitParserMessage { text({ text, type } = { text: '' }, context) { const { theme } = useContext(ThemeContext); if (type !== 'mrkdwn') { - return text; + return <Text style={[styles.text, { color: themes[theme].bodyText }]}>{text}</Text>; } const isContext = context === BLOCK_CONTEXT.CONTEXT; @@ -70,7 +77,7 @@ class MessageParser extends UiKitParserMessage { <Button key={actionId} type={style} - title={this.text(text)} + title={textParser([text])} loading={loading} onPress={() => action({ value })} style={styles.button} diff --git a/app/containers/markdown/AtMention.js b/app/containers/markdown/AtMention.js index 513dd8805..fcd7a4953 100644 --- a/app/containers/markdown/AtMention.js +++ b/app/containers/markdown/AtMention.js @@ -11,11 +11,10 @@ const AtMention = React.memo(({ }) => { let mentionStyle = { ...styles.mention, color: themes[theme].buttonText }; if (mention === 'all' || mention === 'here') { - mentionStyle = { - ...mentionStyle, - ...styles.mentionAll - }; - } else if (mention === username) { + return <Text style={[mentionStyle, styles.mentionAll, ...style]}>{mention}</Text>; + } + + if (mention === username) { mentionStyle = { ...mentionStyle, backgroundColor: themes[theme].actionTintColor diff --git a/app/containers/markdown/Emoji.js b/app/containers/markdown/Emoji.js index 6a16c86c4..f0dcc2359 100644 --- a/app/containers/markdown/Emoji.js +++ b/app/containers/markdown/Emoji.js @@ -9,10 +9,10 @@ import { themes } from '../../constants/colors'; import styles from './styles'; const Emoji = React.memo(({ - emojiName, literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, customEmojis, style = [], theme + literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, customEmojis = true, style = [], theme }) => { const emojiUnicode = shortnameToUnicode(literal); - const emoji = getCustomEmoji && getCustomEmoji(emojiName); + const emoji = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, '')); if (emoji && customEmojis) { return ( <CustomEmoji @@ -36,7 +36,6 @@ const Emoji = React.memo(({ }); Emoji.propTypes = { - emojiName: PropTypes.string, literal: PropTypes.string, isMessageContainsOnlyEmoji: PropTypes.bool, getCustomEmoji: PropTypes.func, diff --git a/app/containers/markdown/index.js b/app/containers/markdown/index.js index 5f6b29a22..4da997194 100644 --- a/app/containers/markdown/index.js +++ b/app/containers/markdown/index.js @@ -261,13 +261,12 @@ class Markdown extends PureComponent { ); } - renderEmoji = ({ emojiName, literal }) => { + renderEmoji = ({ literal }) => { const { - getCustomEmoji, baseUrl, customEmojis = true, style, theme + getCustomEmoji, baseUrl, customEmojis, style, theme } = this.props; return ( <MarkdownEmoji - emojiName={emojiName} literal={literal} isMessageContainsOnlyEmoji={this.isMessageContainsOnlyEmoji} getCustomEmoji={getCustomEmoji} diff --git a/app/containers/message/Attachments.js b/app/containers/message/Attachments.js index cf3f6106c..3d4ff48b4 100644 --- a/app/containers/message/Attachments.js +++ b/app/containers/message/Attachments.js @@ -8,7 +8,7 @@ import Video from './Video'; import Reply from './Reply'; const Attachments = React.memo(({ - attachments, timeFormat, user, baseUrl, showAttachment, getCustomEmoji, theme + attachments, timeFormat, showAttachment, getCustomEmoji, theme }) => { if (!attachments || attachments.length === 0) { return null; @@ -16,25 +16,23 @@ const Attachments = React.memo(({ return attachments.map((file, index) => { if (file.image_url) { - return <Image key={file.image_url} file={file} user={user} baseUrl={baseUrl} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />; + return <Image key={file.image_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />; } if (file.audio_url) { - return <Audio key={file.audio_url} file={file} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} theme={theme} />; + return <Audio key={file.audio_url} file={file} getCustomEmoji={getCustomEmoji} theme={theme} />; } if (file.video_url) { - return <Video key={file.video_url} file={file} user={user} baseUrl={baseUrl} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />; + return <Video key={file.video_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />; } // eslint-disable-next-line react/no-array-index-key - return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} theme={theme} />; + return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} theme={theme} />; }); }, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme); Attachments.propTypes = { attachments: PropTypes.array, timeFormat: PropTypes.string, - user: PropTypes.object, - baseUrl: PropTypes.string, showAttachment: PropTypes.func, getCustomEmoji: PropTypes.func, theme: PropTypes.string diff --git a/app/containers/message/Audio.js b/app/containers/message/Audio.js index 6ca5ce14c..5621a8d07 100644 --- a/app/containers/message/Audio.js +++ b/app/containers/message/Audio.js @@ -3,18 +3,31 @@ import PropTypes from 'prop-types'; import { View, StyleSheet, Text, Easing, Dimensions } from 'react-native'; -import Video from 'react-native-video'; +import { Audio } from 'expo-av'; import Slider from '@react-native-community/slider'; import moment from 'moment'; import equal from 'deep-equal'; -import Touchable from 'react-native-platform-touchable'; +import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; +import Touchable from './Touchable'; import Markdown from '../markdown'; import { CustomIcon } from '../../lib/Icons'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; import { isAndroid, isIOS } from '../../utils/deviceInfo'; import { withSplit } from '../../split'; +import MessageContext from './Context'; +import ActivityIndicator from '../ActivityIndicator'; + +const mode = { + allowsRecordingIOS: false, + playsInSilentModeIOS: true, + staysActiveInBackground: false, + shouldDuckAndroid: true, + playThroughEarpieceAndroid: false, + interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX, + interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX +}; const styles = StyleSheet.create({ audioContainer: { @@ -31,6 +44,9 @@ const styles = StyleSheet.create({ alignItems: 'center', backgroundColor: 'transparent' }, + audioLoading: { + marginHorizontal: 8 + }, slider: { flex: 1 }, @@ -51,29 +67,36 @@ const sliderAnimationConfig = { delay: 0 }; -const Button = React.memo(({ paused, onPress, theme }) => ( +const Button = React.memo(({ + loading, paused, onPress, theme +}) => ( <Touchable style={styles.playPauseButton} onPress={onPress} hitSlop={BUTTON_HIT_SLOP} background={Touchable.SelectableBackgroundBorderless()} > - <CustomIcon name={paused ? 'play' : 'pause'} size={36} color={themes[theme].tintColor} /> + { + loading + ? <ActivityIndicator style={[styles.playPauseButton, styles.audioLoading]} theme={theme} /> + : <CustomIcon name={paused ? 'play' : 'pause'} size={36} color={themes[theme].tintColor} /> + } </Touchable> )); Button.propTypes = { + loading: PropTypes.bool, paused: PropTypes.bool, theme: PropTypes.string, onPress: PropTypes.func }; Button.displayName = 'MessageAudioButton'; -class Audio extends React.Component { +class MessageAudio extends React.Component { + static contextType = MessageContext; + static propTypes = { file: PropTypes.object.isRequired, - baseUrl: PropTypes.string.isRequired, - user: PropTypes.object.isRequired, theme: PropTypes.string, split: PropTypes.bool, getCustomEmoji: PropTypes.func @@ -81,18 +104,39 @@ class Audio extends React.Component { constructor(props) { super(props); - const { baseUrl, file, user } = props; this.state = { + loading: false, currentTime: 0, duration: 0, - paused: true, - uri: `${ baseUrl }${ file.audio_url }?rc_uid=${ user.id }&rc_token=${ user.token }` + paused: true }; + + this.sound = new Audio.Sound(); + this.sound.setOnPlaybackStatusUpdate(this.onPlaybackStatusUpdate); + } + + async componentDidMount() { + const { file } = this.props; + const { baseUrl, user } = this.context; + + let url = file.audio_url; + if (!url.startsWith('http')) { + url = `${ baseUrl }${ file.audio_url }`; + } + + this.setState({ loading: true }); + try { + await Audio.setAudioModeAsync(mode); + await this.sound.loadAsync({ uri: `${ url }?rc_uid=${ user.id }&rc_token=${ user.token }` }); + } catch { + // Do nothing + } + this.setState({ loading: false }); } shouldComponentUpdate(nextProps, nextState) { const { - currentTime, duration, paused, uri + currentTime, duration, paused, loading } = this.state; const { file, split, theme } = this.props; if (nextProps.theme !== theme) { @@ -107,58 +151,108 @@ class Audio extends React.Component { if (nextState.paused !== paused) { return true; } - if (nextState.uri !== uri) { - return true; - } if (!equal(nextProps.file, file)) { return true; } if (nextProps.split !== split) { return true; } + if (nextState.loading !== loading) { + return true; + } return false; } + componentDidUpdate() { + const { paused } = this.state; + if (paused) { + deactivateKeepAwake(); + } else { + activateKeepAwake(); + } + } + + async componentWillUnmount() { + try { + await this.sound.stopAsync(); + } catch { + // Do nothing + } + } + + onPlaybackStatusUpdate = (status) => { + if (status) { + this.onLoad(status); + this.onProgress(status); + this.onEnd(status); + } + } + onLoad = (data) => { - this.setState({ duration: data.duration > 0 ? data.duration : 0 }); + const duration = data.durationMillis / 1000; + this.setState({ duration: duration > 0 ? duration : 0 }); } onProgress = (data) => { const { duration } = this.state; - if (data.currentTime <= duration) { - this.setState({ currentTime: data.currentTime }); + const currentTime = data.positionMillis / 1000; + if (currentTime <= duration) { + this.setState({ currentTime }); } } - onEnd = () => { - this.setState({ paused: true, currentTime: 0 }); - requestAnimationFrame(() => { - this.player.seek(0); - }); + onEnd = async(data) => { + if (data.didJustFinish) { + try { + await this.sound.stopAsync(); + this.setState({ paused: true, currentTime: 0 }); + } catch { + // do nothing + } + } } get duration() { - const { duration } = this.state; - return formatTime(duration); + const { currentTime, duration } = this.state; + return formatTime(currentTime || duration); } - setRef = ref => this.player = ref; - togglePlayPause = () => { const { paused } = this.state; - this.setState({ paused: !paused }); + this.setState({ paused: !paused }, this.playPause); } - onValueChange = value => this.setState({ currentTime: value }); + playPause = async() => { + const { paused } = this.state; + try { + if (paused) { + await this.sound.pauseAsync(); + } else { + await this.sound.playAsync(); + } + } catch { + // Do nothing + } + } + + onValueChange = async(value) => { + try { + this.setState({ currentTime: value }); + await this.sound.setPositionAsync(value * 1000); + } catch { + // Do nothing + } + } render() { const { - uri, paused, currentTime, duration + loading, paused, currentTime, duration } = this.state; const { - user, baseUrl, file, getCustomEmoji, split, theme + file, getCustomEmoji, split, theme } = this.props; const { description } = file; + const { baseUrl, user } = this.context; if (!baseUrl) { return null; @@ -173,17 +267,7 @@ class Audio extends React.Component { split && sharedStyles.tabletContent ]} > - <Video - ref={this.setRef} - source={{ uri }} - onLoad={this.onLoad} - onProgress={this.onProgress} - onEnd={this.onEnd} - paused={paused} - repeat={false} - ignoreSilentSwitch='ignore' - /> - <Button paused={paused} onPress={this.togglePlayPause} theme={theme} /> + <Button loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} /> <Slider style={styles.slider} value={currentTime} @@ -205,4 +289,4 @@ class Audio extends React.Component { } } -export default withSplit(Audio); +export default withSplit(MessageAudio); diff --git a/app/containers/message/Broadcast.js b/app/containers/message/Broadcast.js index 8d6192f74..d4ebe1264 100644 --- a/app/containers/message/Broadcast.js +++ b/app/containers/message/Broadcast.js @@ -1,17 +1,19 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View, Text } from 'react-native'; -import Touchable from 'react-native-platform-touchable'; import PropTypes from 'prop-types'; +import Touchable from './Touchable'; import { CustomIcon } from '../../lib/Icons'; import styles from './styles'; import { BUTTON_HIT_SLOP } from './utils'; import I18n from '../../i18n'; import { themes } from '../../constants/colors'; +import MessageContext from './Context'; const Broadcast = React.memo(({ - author, user, broadcast, replyBroadcast, theme + author, broadcast, theme }) => { + const { user, replyBroadcast } = useContext(MessageContext); const isOwn = author._id === user.id; if (broadcast && !isOwn) { return ( @@ -36,10 +38,8 @@ const Broadcast = React.memo(({ Broadcast.propTypes = { author: PropTypes.object, - user: PropTypes.object, broadcast: PropTypes.bool, - theme: PropTypes.string, - replyBroadcast: PropTypes.func + theme: PropTypes.string }; Broadcast.displayName = 'MessageBroadcast'; diff --git a/app/containers/message/CallButton.js b/app/containers/message/CallButton.js index 0b1524b04..6b18cabd7 100644 --- a/app/containers/message/CallButton.js +++ b/app/containers/message/CallButton.js @@ -1,8 +1,8 @@ import React from 'react'; import { View, Text } from 'react-native'; -import Touchable from 'react-native-platform-touchable'; import PropTypes from 'prop-types'; +import Touchable from './Touchable'; import { formatLastMessage, BUTTON_HIT_SLOP } from './utils'; import styles from './styles'; import I18n from '../../i18n'; diff --git a/app/containers/message/Content.js b/app/containers/message/Content.js index b1565ec26..1d2f3012b 100644 --- a/app/containers/message/Content.js +++ b/app/containers/message/Content.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { Text, View } from 'react-native'; import PropTypes from 'prop-types'; import equal from 'deep-equal'; @@ -8,6 +8,7 @@ import styles from './styles'; import Markdown from '../markdown'; import { getInfoMessage } from './utils'; import { themes } from '../../constants/colors'; +import MessageContext from './Context'; const Content = React.memo((props) => { if (props.isInfo) { @@ -26,12 +27,13 @@ const Content = React.memo((props) => { if (props.tmid && !props.msg) { content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>; } else { + const { baseUrl, user } = useContext(MessageContext); content = ( <Markdown msg={props.msg} - baseUrl={props.baseUrl} + baseUrl={baseUrl} getCustomEmoji={props.getCustomEmoji} - username={props.user.username} + username={user.username} isEdited={props.isEdited} numberOfLines={(props.tmid && !props.isThreadRoom) ? 1 : 0} preview={props.tmid && !props.isThreadRoom} @@ -77,8 +79,6 @@ Content.propTypes = { msg: PropTypes.string, theme: PropTypes.string, isEdited: PropTypes.bool, - baseUrl: PropTypes.string, - user: PropTypes.object, getCustomEmoji: PropTypes.func, channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), diff --git a/app/containers/message/Context.js b/app/containers/message/Context.js new file mode 100644 index 000000000..407904b4e --- /dev/null +++ b/app/containers/message/Context.js @@ -0,0 +1,4 @@ +import React from 'react'; + +const MessageContext = React.createContext(); +export default MessageContext; diff --git a/app/containers/message/Discussion.js b/app/containers/message/Discussion.js index 2fc81a7e4..4cdc81abf 100644 --- a/app/containers/message/Discussion.js +++ b/app/containers/message/Discussion.js @@ -1,20 +1,22 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View, Text } from 'react-native'; -import Touchable from 'react-native-platform-touchable'; import PropTypes from 'prop-types'; +import Touchable from './Touchable'; import { formatLastMessage, formatMessageCount, BUTTON_HIT_SLOP } from './utils'; import styles from './styles'; import I18n from '../../i18n'; import { CustomIcon } from '../../lib/Icons'; import { DISCUSSION } from './constants'; import { themes } from '../../constants/colors'; +import MessageContext from './Context'; const Discussion = React.memo(({ - msg, dcount, dlm, onDiscussionPress, theme + msg, dcount, dlm, theme }) => { const time = formatLastMessage(dlm); const buttonText = formatMessageCount(dcount, DISCUSSION); + const { onDiscussionPress } = useContext(MessageContext); return ( <> <Text style={[styles.startedDiscussion, { color: themes[theme].auxiliaryText }]}>{I18n.t('Started_discussion')}</Text> @@ -55,8 +57,7 @@ Discussion.propTypes = { msg: PropTypes.string, dcount: PropTypes.number, dlm: PropTypes.string, - theme: PropTypes.string, - onDiscussionPress: PropTypes.func + theme: PropTypes.string }; Discussion.displayName = 'MessageDiscussion'; diff --git a/app/containers/message/Emoji.js b/app/containers/message/Emoji.js index 139d7707e..e8b817da0 100644 --- a/app/containers/message/Emoji.js +++ b/app/containers/message/Emoji.js @@ -6,7 +6,7 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode'; import CustomEmoji from '../EmojiPicker/CustomEmoji'; const Emoji = React.memo(({ - content, standardEmojiStyle, customEmojiStyle, baseUrl, getCustomEmoji + content, baseUrl, standardEmojiStyle, customEmojiStyle, getCustomEmoji }) => { const parsedContent = content.replace(/^:|:$/g, ''); const emoji = getCustomEmoji(parsedContent); @@ -18,9 +18,9 @@ const Emoji = React.memo(({ Emoji.propTypes = { content: PropTypes.string, + baseUrl: PropTypes.string, standardEmojiStyle: PropTypes.object, customEmojiStyle: PropTypes.object, - baseUrl: PropTypes.string, getCustomEmoji: PropTypes.func }; Emoji.displayName = 'MessageEmoji'; diff --git a/app/containers/message/Image.js b/app/containers/message/Image.js index 72b3a6c6f..0e2d94a85 100644 --- a/app/containers/message/Image.js +++ b/app/containers/message/Image.js @@ -1,18 +1,19 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View } from 'react-native'; import PropTypes from 'prop-types'; import FastImage from 'react-native-fast-image'; import equal from 'deep-equal'; -import Touchable from 'react-native-platform-touchable'; import { createImageProgress } from 'react-native-image-progress'; import * as Progress from 'react-native-progress'; +import Touchable from './Touchable'; import Markdown from '../markdown'; import styles from './styles'; import { formatAttachmentUrl } from '../../lib/utils'; import { withSplit } from '../../split'; import { themes } from '../../constants/colors'; import sharedStyles from '../../views/Styles'; +import MessageContext from './Context'; const ImageProgress = createImageProgress(FastImage); @@ -41,8 +42,9 @@ export const MessageImage = React.memo(({ img, theme }) => ( )); const ImageContainer = React.memo(({ - file, imageUrl, baseUrl, user, showAttachment, getCustomEmoji, split, theme + file, imageUrl, showAttachment, getCustomEmoji, split, theme }) => { + const { baseUrl, user } = useContext(MessageContext); const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl); if (!img) { return null; @@ -71,8 +73,6 @@ const ImageContainer = React.memo(({ ImageContainer.propTypes = { file: PropTypes.object, imageUrl: PropTypes.string, - baseUrl: PropTypes.string, - user: PropTypes.object, showAttachment: PropTypes.func, theme: PropTypes.string, getCustomEmoji: PropTypes.func, diff --git a/app/containers/message/Message.js b/app/containers/message/Message.js index 2deec7fb5..ef356c231 100644 --- a/app/containers/message/Message.js +++ b/app/containers/message/Message.js @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { View } from 'react-native'; import Touchable from 'react-native-platform-touchable'; +import MessageContext from './Context'; + import User from './User'; import styles from './styles'; import RepliedThread from './RepliedThread'; @@ -111,10 +113,11 @@ const MessageTouchable = React.memo((props) => { </View> ); } + const { onPress, onLongPress } = useContext(MessageContext); return ( <Touchable - onLongPress={props.onLongPress} - onPress={props.onPress} + onLongPress={onLongPress} + onPress={onPress} disabled={props.isInfo || props.archived || props.isTemp} > <View> @@ -129,9 +132,7 @@ MessageTouchable.propTypes = { hasError: PropTypes.bool, isInfo: PropTypes.bool, isTemp: PropTypes.bool, - archived: PropTypes.bool, - onLongPress: PropTypes.func, - onPress: PropTypes.func + archived: PropTypes.bool }; Message.propTypes = { @@ -143,7 +144,6 @@ Message.propTypes = { hasError: PropTypes.bool, style: PropTypes.any, onLongPress: PropTypes.func, - onPress: PropTypes.func, isReadReceiptEnabled: PropTypes.bool, unread: PropTypes.bool, theme: PropTypes.string diff --git a/app/containers/message/MessageAvatar.js b/app/containers/message/MessageAvatar.js index 190bb43a8..9f1eb6a23 100644 --- a/app/containers/message/MessageAvatar.js +++ b/app/containers/message/MessageAvatar.js @@ -1,34 +1,34 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; -import { TouchableOpacity } from 'react-native'; import Avatar from '../Avatar'; import styles from './styles'; +import MessageContext from './Context'; const MessageAvatar = React.memo(({ - isHeader, avatar, author, baseUrl, user, small, navToRoomInfo + isHeader, avatar, author, small, navToRoomInfo, emoji, getCustomEmoji, theme }) => { + const { baseUrl, user } = useContext(MessageContext); if (isHeader && author) { const navParam = { t: 'd', rid: author._id }; return ( - <TouchableOpacity - onPress={() => navToRoomInfo(navParam)} - disabled={author._id === user.id} - > - <Avatar - style={small ? styles.avatarSmall : styles.avatar} - text={avatar ? '' : author.username} - size={small ? 20 : 36} - borderRadius={small ? 2 : 4} - avatar={avatar} - baseUrl={baseUrl} - userId={user.id} - token={user.token} - /> - </TouchableOpacity> + <Avatar + style={small ? styles.avatarSmall : styles.avatar} + text={avatar ? '' : author.username} + size={small ? 20 : 36} + borderRadius={small ? 2 : 4} + onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)} + getCustomEmoji={getCustomEmoji} + avatar={avatar} + emoji={emoji} + baseUrl={baseUrl} + userId={user.id} + token={user.token} + theme={theme} + /> ); } return null; @@ -37,11 +37,12 @@ const MessageAvatar = React.memo(({ MessageAvatar.propTypes = { isHeader: PropTypes.bool, avatar: PropTypes.string, + emoji: PropTypes.string, author: PropTypes.obj, - baseUrl: PropTypes.string, - user: PropTypes.obj, small: PropTypes.bool, - navToRoomInfo: PropTypes.func + navToRoomInfo: PropTypes.func, + getCustomEmoji: PropTypes.func, + theme: PropTypes.string }; MessageAvatar.displayName = 'MessageAvatar'; diff --git a/app/containers/message/MessageError.js b/app/containers/message/MessageError.js index d30e40996..b3e3969a1 100644 --- a/app/containers/message/MessageError.js +++ b/app/containers/message/MessageError.js @@ -1,16 +1,18 @@ -import React from 'react'; -import Touchable from 'react-native-platform-touchable'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; +import Touchable from './Touchable'; import { CustomIcon } from '../../lib/Icons'; import styles from './styles'; import { BUTTON_HIT_SLOP } from './utils'; import { themes } from '../../constants/colors'; +import MessageContext from './Context'; -const MessageError = React.memo(({ hasError, onErrorPress, theme }) => { +const MessageError = React.memo(({ hasError, theme }) => { if (!hasError) { return null; } + const { onErrorPress } = useContext(MessageContext); return ( <Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}> <CustomIcon name='warning' color={themes[theme].dangerColor} size={18} /> @@ -20,7 +22,6 @@ const MessageError = React.memo(({ hasError, onErrorPress, theme }) => { MessageError.propTypes = { hasError: PropTypes.bool, - onErrorPress: PropTypes.func, theme: PropTypes.string }; MessageError.displayName = 'MessageError'; diff --git a/app/containers/message/Reactions.js b/app/containers/message/Reactions.js index 1614b1b7a..6ff654221 100644 --- a/app/containers/message/Reactions.js +++ b/app/containers/message/Reactions.js @@ -1,33 +1,40 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View, Text } from 'react-native'; -import Touchable from 'react-native-platform-touchable'; import PropTypes from 'prop-types'; +import Touchable from './Touchable'; import { CustomIcon } from '../../lib/Icons'; import styles from './styles'; import Emoji from './Emoji'; import { BUTTON_HIT_SLOP } from './utils'; import { themes } from '../../constants/colors'; import { withTheme } from '../../theme'; +import MessageContext from './Context'; -const AddReaction = React.memo(({ reactionInit, theme }) => ( - <Touchable - onPress={reactionInit} - key='message-add-reaction' - testID='message-add-reaction' - style={[styles.reactionButton, { backgroundColor: themes[theme].backgroundColor }]} - background={Touchable.Ripple(themes[theme].bannerBackground)} - hitSlop={BUTTON_HIT_SLOP} - > - <View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}> - <CustomIcon name='add-reaction' size={21} color={themes[theme].tintColor} /> - </View> - </Touchable> -)); +const AddReaction = React.memo(({ theme }) => { + const { reactionInit } = useContext(MessageContext); + return ( + <Touchable + onPress={reactionInit} + key='message-add-reaction' + testID='message-add-reaction' + style={[styles.reactionButton, { backgroundColor: themes[theme].backgroundColor }]} + background={Touchable.Ripple(themes[theme].bannerBackground)} + hitSlop={BUTTON_HIT_SLOP} + > + <View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}> + <CustomIcon name='add-reaction' size={21} color={themes[theme].tintColor} /> + </View> + </Touchable> + ); +}); const Reaction = React.memo(({ - reaction, user, onReactionLongPress, onReactionPress, baseUrl, getCustomEmoji, theme + reaction, getCustomEmoji, theme }) => { + const { + onReactionPress, onReactionLongPress, baseUrl, user + } = useContext(MessageContext); const reacted = reaction.usernames.findIndex(item => item === user.username) !== -1; return ( <Touchable @@ -54,7 +61,7 @@ const Reaction = React.memo(({ }); const Reactions = React.memo(({ - reactions, user, baseUrl, onReactionPress, reactionInit, onReactionLongPress, getCustomEmoji, theme + reactions, getCustomEmoji, theme }) => { if (!Array.isArray(reactions) || reactions.length === 0) { return null; @@ -65,25 +72,17 @@ const Reactions = React.memo(({ <Reaction key={reaction.emoji} reaction={reaction} - user={user} - baseUrl={baseUrl} - onReactionLongPress={onReactionLongPress} - onReactionPress={onReactionPress} getCustomEmoji={getCustomEmoji} theme={theme} /> ))} - <AddReaction reactionInit={reactionInit} theme={theme} /> + <AddReaction theme={theme} /> </View> ); }); Reaction.propTypes = { reaction: PropTypes.object, - user: PropTypes.object, - baseUrl: PropTypes.string, - onReactionPress: PropTypes.func, - onReactionLongPress: PropTypes.func, getCustomEmoji: PropTypes.func, theme: PropTypes.string }; @@ -91,18 +90,12 @@ Reaction.displayName = 'MessageReaction'; Reactions.propTypes = { reactions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - user: PropTypes.object, - baseUrl: PropTypes.string, - onReactionPress: PropTypes.func, - reactionInit: PropTypes.func, - onReactionLongPress: PropTypes.func, getCustomEmoji: PropTypes.func, theme: PropTypes.string }; Reactions.displayName = 'MessageReactions'; AddReaction.propTypes = { - reactionInit: PropTypes.func, theme: PropTypes.string }; AddReaction.displayName = 'MessageAddReaction'; diff --git a/app/containers/message/Reply.js b/app/containers/message/Reply.js index 254beba5f..df99258f9 100644 --- a/app/containers/message/Reply.js +++ b/app/containers/message/Reply.js @@ -1,15 +1,16 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import moment from 'moment'; -import Touchable from 'react-native-platform-touchable'; import isEqual from 'deep-equal'; +import Touchable from './Touchable'; import Markdown from '../markdown'; import openLink from '../../utils/openLink'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; import { withSplit } from '../../split'; +import MessageContext from './Context'; const styles = StyleSheet.create({ button: { @@ -79,12 +80,13 @@ const Title = React.memo(({ attachment, timeFormat, theme }) => { }); const Description = React.memo(({ - attachment, baseUrl, user, getCustomEmoji, theme + attachment, getCustomEmoji, theme }) => { const text = attachment.text || attachment.title; if (!text) { return null; } + const { baseUrl, user } = useContext(MessageContext); return ( <Markdown msg={text} @@ -124,11 +126,12 @@ const Fields = React.memo(({ attachment, theme }) => { }, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme); const Reply = React.memo(({ - attachment, timeFormat, baseUrl, user, index, getCustomEmoji, split, theme + attachment, timeFormat, index, getCustomEmoji, split, theme }) => { if (!attachment) { return null; } + const { baseUrl, user } = useContext(MessageContext); const onPress = () => { let url = attachment.title_link || attachment.author_link; @@ -136,7 +139,10 @@ const Reply = React.memo(({ return; } if (attachment.type === 'file') { - url = `${ baseUrl }${ url }?rc_uid=${ user.id }&rc_token=${ user.token }`; + if (!url.startsWith('http')) { + url = `${ baseUrl }${ url }`; + } + url = `${ url }?rc_uid=${ user.id }&rc_token=${ user.token }`; } openLink(url, theme); }; @@ -160,8 +166,6 @@ const Reply = React.memo(({ <Description attachment={attachment} timeFormat={timeFormat} - baseUrl={baseUrl} - user={user} getCustomEmoji={getCustomEmoji} theme={theme} /> @@ -174,8 +178,6 @@ const Reply = React.memo(({ Reply.propTypes = { attachment: PropTypes.object, timeFormat: PropTypes.string, - baseUrl: PropTypes.string, - user: PropTypes.object, index: PropTypes.number, theme: PropTypes.string, getCustomEmoji: PropTypes.func, @@ -192,8 +194,6 @@ Title.displayName = 'MessageReplyTitle'; Description.propTypes = { attachment: PropTypes.object, - baseUrl: PropTypes.string, - user: PropTypes.object, getCustomEmoji: PropTypes.func, theme: PropTypes.string }; diff --git a/app/containers/message/Touchable.js b/app/containers/message/Touchable.js new file mode 100644 index 000000000..edd2d63e5 --- /dev/null +++ b/app/containers/message/Touchable.js @@ -0,0 +1,25 @@ +import React, { useContext } from 'react'; +import Touchable from 'react-native-platform-touchable'; +import PropTypes from 'prop-types'; + +import MessageContext from './Context'; + +const RCTouchable = React.memo(({ children, ...props }) => { + const { onLongPress } = useContext(MessageContext); + + return ( + <Touchable + onLongPress={onLongPress} + {...props} + > + {children} + </Touchable> + ); +}); +RCTouchable.propTypes = { + children: PropTypes.node +}; +RCTouchable.Ripple = (...args) => Touchable.Ripple(...args); +RCTouchable.SelectableBackgroundBorderless = () => Touchable.SelectableBackgroundBorderless(); + +export default RCTouchable; diff --git a/app/containers/message/Urls.js b/app/containers/message/Urls.js index a10459e9d..e4a2ac4e5 100644 --- a/app/containers/message/Urls.js +++ b/app/containers/message/Urls.js @@ -1,12 +1,12 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { View, Text, StyleSheet, Clipboard } from 'react-native'; import PropTypes from 'prop-types'; import FastImage from 'react-native-fast-image'; -import Touchable from 'react-native-platform-touchable'; import isEqual from 'lodash/isEqual'; +import Touchable from './Touchable'; import openLink from '../../utils/openLink'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; @@ -15,6 +15,7 @@ import { withSplit } from '../../split'; import { LISTENER } from '../Toast'; import EventEmitter from '../../utils/events'; import I18n from '../../i18n'; +import MessageContext from './Context'; const styles = StyleSheet.create({ button: { @@ -52,10 +53,11 @@ const styles = StyleSheet.create({ } }); -const UrlImage = React.memo(({ image, user, baseUrl }) => { +const UrlImage = React.memo(({ image }) => { if (!image) { return null; } + const { baseUrl, user } = useContext(MessageContext); image = image.includes('http') ? image : `${ baseUrl }/${ image }?rc_uid=${ user.id }&rc_token=${ user.token }`; return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />; }, (prevProps, nextProps) => prevProps.image === nextProps.image); @@ -79,7 +81,7 @@ const UrlContent = React.memo(({ title, description, theme }) => ( }); const Url = React.memo(({ - url, index, user, baseUrl, split, theme + url, index, split, theme }) => { if (!url) { return null; @@ -109,7 +111,7 @@ const Url = React.memo(({ background={Touchable.Ripple(themes[theme].bannerBackground)} > <> - <UrlImage image={url.image} user={user} baseUrl={baseUrl} /> + <UrlImage image={url.image} /> <UrlContent title={url.title} description={url.description} theme={theme} /> </> </Touchable> @@ -117,21 +119,19 @@ const Url = React.memo(({ }, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.split === newProps.split && oldProps.theme === newProps.theme); const Urls = React.memo(({ - urls, user, baseUrl, split, theme + urls, split, theme }) => { if (!urls || urls.length === 0) { return null; } return urls.map((url, index) => ( - <Url url={url} key={url.url} index={index} user={user} baseUrl={baseUrl} split={split} theme={theme} /> + <Url url={url} key={url.url} index={index} split={split} theme={theme} /> )); }, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.split === newProps.split && oldProps.theme === newProps.theme); UrlImage.propTypes = { - image: PropTypes.string, - user: PropTypes.object, - baseUrl: PropTypes.string + image: PropTypes.string }; UrlImage.displayName = 'MessageUrlImage'; @@ -145,8 +145,6 @@ UrlContent.displayName = 'MessageUrlContent'; Url.propTypes = { url: PropTypes.object.isRequired, index: PropTypes.number, - user: PropTypes.object, - baseUrl: PropTypes.string, theme: PropTypes.string, split: PropTypes.bool }; @@ -154,8 +152,6 @@ Url.displayName = 'MessageUrl'; Urls.propTypes = { urls: PropTypes.array, - user: PropTypes.object, - baseUrl: PropTypes.string, theme: PropTypes.string, split: PropTypes.bool }; diff --git a/app/containers/message/User.js b/app/containers/message/User.js index 022d2a58b..827a9b731 100644 --- a/app/containers/message/User.js +++ b/app/containers/message/User.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { View, Text, StyleSheet, TouchableOpacity @@ -11,6 +11,7 @@ import { withTheme } from '../../theme'; import MessageError from './MessageError'; import sharedStyles from '../../views/Styles'; import messageStyles from './styles'; +import MessageContext from './Context'; const styles = StyleSheet.create({ container: { @@ -35,13 +36,14 @@ const styles = StyleSheet.create({ }); const User = React.memo(({ - isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, user, ...props + isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, ...props }) => { if (isHeader || hasError) { const navParam = { t: 'd', rid: author._id }; + const { user } = useContext(MessageContext); const username = (useRealName && author.name) || author.username; const aliasUsername = alias ? (<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>) : null; const time = moment(ts).format(timeFormat); @@ -49,15 +51,14 @@ const User = React.memo(({ return ( <View style={styles.container}> <TouchableOpacity + style={styles.titleContainer} onPress={() => navToRoomInfo(navParam)} disabled={author._id === user.id} > - <View style={styles.titleContainer}> - <Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}> - {alias || username} - {aliasUsername} - </Text> - </View> + <Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}> + {alias || username} + {aliasUsername} + </Text> </TouchableOpacity> <Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text> { hasError && <MessageError hasError={hasError} theme={theme} {...props} /> } @@ -76,7 +77,6 @@ User.propTypes = { ts: PropTypes.instanceOf(Date), timeFormat: PropTypes.string, theme: PropTypes.string, - user: PropTypes.obj, navToRoomInfo: PropTypes.func }; User.displayName = 'MessageUser'; diff --git a/app/containers/message/Video.js b/app/containers/message/Video.js index a30c3abe8..c765702e0 100644 --- a/app/containers/message/Video.js +++ b/app/containers/message/Video.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { StyleSheet } from 'react-native'; -import Touchable from 'react-native-platform-touchable'; import isEqual from 'deep-equal'; +import Touchable from './Touchable'; import Markdown from '../markdown'; import openLink from '../../utils/openLink'; import { isIOS, isTablet } from '../../utils/deviceInfo'; @@ -11,6 +11,7 @@ import { CustomIcon } from '../../lib/Icons'; import { formatAttachmentUrl } from '../../lib/utils'; import { themes } from '../../constants/colors'; import sharedStyles from '../../views/Styles'; +import MessageContext from './Context'; const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; const isTypeSupported = type => SUPPORTED_TYPES.indexOf(type) !== -1; @@ -27,12 +28,12 @@ const styles = StyleSheet.create({ }); const Video = React.memo(({ - file, baseUrl, user, showAttachment, getCustomEmoji, theme + file, showAttachment, getCustomEmoji, theme }) => { + const { baseUrl, user } = useContext(MessageContext); if (!baseUrl) { return null; } - const onPress = () => { if (isTypeSupported(file.video_type)) { return showAttachment(file); @@ -61,8 +62,6 @@ const Video = React.memo(({ Video.propTypes = { file: PropTypes.object, - baseUrl: PropTypes.string, - user: PropTypes.object, showAttachment: PropTypes.func, getCustomEmoji: PropTypes.func, theme: PropTypes.string diff --git a/app/containers/message/index.js b/app/containers/message/index.js index 1a49b80f6..207e5e70c 100644 --- a/app/containers/message/index.js +++ b/app/containers/message/index.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { KeyboardUtils } from 'react-native-keyboard-input'; import Message from './Message'; +import MessageContext from './Context'; import debounce from '../../utils/debounce'; import { SYSTEM_MESSAGES, getMessageTranslation } from './utils'; import messagesStatus from '../../constants/messagesStatus'; @@ -229,7 +230,7 @@ class MessageContainer extends React.Component { item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, showAttachment, timeFormat, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo, getCustomEmoji, isThreadRoom, callJitsi, blockAction, rid, theme } = this.props; const { - id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, blocks, autoTranslate: autoTranslateMessage + id, msg, ts, attachments, urls, reactions, t, avatar, emoji, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, blocks, autoTranslate: autoTranslateMessage } = item; let message = msg; @@ -240,63 +241,69 @@ class MessageContainer extends React.Component { } return ( - <Message - id={id} - msg={message} - rid={rid} - author={u} - ts={ts} - type={t} - attachments={attachments} - blocks={blocks} - urls={urls} - reactions={reactions} - alias={alias} - avatar={avatar} - user={user} - timeFormat={timeFormat} - customThreadTimeFormat={customThreadTimeFormat} - style={style} - archived={archived} - broadcast={broadcast} - baseUrl={baseUrl} - useRealName={useRealName} - isReadReceiptEnabled={isReadReceiptEnabled} - unread={unread} - role={role} - drid={drid} - dcount={dcount} - dlm={dlm} - tmid={tmid} - tcount={tcount} - tlm={tlm} - tmsg={tmsg} - fetchThreadName={fetchThreadName} - mentions={mentions} - channels={channels} - isEdited={editedBy && !!editedBy.username} - isHeader={this.isHeader} - isThreadReply={this.isThreadReply} - isThreadSequential={this.isThreadSequential} - isThreadRoom={isThreadRoom} - isInfo={this.isInfo} - isTemp={this.isTemp} - hasError={this.hasError} - onErrorPress={this.onErrorPress} - onPress={this.onPress} - onLongPress={this.onLongPress} - onReactionLongPress={this.onReactionLongPress} - onReactionPress={this.onReactionPress} - replyBroadcast={this.replyBroadcast} - reactionInit={this.reactionInit} - onDiscussionPress={this.onDiscussionPress} - showAttachment={showAttachment} - getCustomEmoji={getCustomEmoji} - navToRoomInfo={navToRoomInfo} - callJitsi={callJitsi} - blockAction={blockAction} - theme={theme} - /> + <MessageContext.Provider + value={{ + user, + baseUrl, + onPress: this.onPress, + onLongPress: this.onLongPress, + reactionInit: this.reactionInit, + onErrorPress: this.onErrorPress, + replyBroadcast: this.replyBroadcast, + onReactionPress: this.onReactionPress, + onDiscussionPress: this.onDiscussionPress, + onReactionLongPress: this.onReactionLongPress + }} + > + <Message + id={id} + msg={message} + rid={rid} + author={u} + ts={ts} + type={t} + attachments={attachments} + blocks={blocks} + urls={urls} + reactions={reactions} + alias={alias} + avatar={avatar} + emoji={emoji} + timeFormat={timeFormat} + customThreadTimeFormat={customThreadTimeFormat} + style={style} + archived={archived} + broadcast={broadcast} + useRealName={useRealName} + isReadReceiptEnabled={isReadReceiptEnabled} + unread={unread} + role={role} + drid={drid} + dcount={dcount} + dlm={dlm} + tmid={tmid} + tcount={tcount} + tlm={tlm} + tmsg={tmsg} + fetchThreadName={fetchThreadName} + mentions={mentions} + channels={channels} + isEdited={editedBy && !!editedBy.username} + isHeader={this.isHeader} + isThreadReply={this.isThreadReply} + isThreadSequential={this.isThreadSequential} + isThreadRoom={isThreadRoom} + isInfo={this.isInfo} + isTemp={this.isTemp} + hasError={this.hasError} + showAttachment={showAttachment} + getCustomEmoji={getCustomEmoji} + navToRoomInfo={navToRoomInfo} + callJitsi={callJitsi} + blockAction={blockAction} + theme={theme} + /> + </MessageContext.Provider> ); } } diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js index 06d87464b..bc74bbdd2 100644 --- a/app/i18n/locales/de.js +++ b/app/i18n/locales/de.js @@ -10,6 +10,7 @@ export default { 'error-could-not-change-email': 'E-Mail konnte nicht geändert werden', 'error-could-not-change-name': 'Name konnte nicht geändert werden', 'error-could-not-change-username': 'Benutzername konnte nicht geändert werden', + 'error-could-not-change-status': 'Status konnte nicht geändert werden', 'error-delete-protected-role': 'Eine geschützte Rolle kann nicht gelöscht werden', 'error-department-not-found': 'Abteilung nicht gefunden', 'error-direct-message-file-upload-not-allowed': 'Dateifreigabe in direkten Nachrichten nicht zulässig', @@ -17,6 +18,7 @@ export default { 'error-email-domain-blacklisted': 'Die E-Mail-Domain wird auf die schwarze Liste gesetzt', 'error-email-send-failed': 'Fehler beim Versuch, eine E-Mail zu senden: {{message}}', 'error-save-image': 'Fehler beim Speichern des Bildes', + 'error-save-video': 'Fehler beim Speichern des Videos', 'error-field-unavailable': '{{field}} wird bereits verwendet :(', 'error-file-too-large': 'Datei ist zu groß', 'error-importer-not-defined': 'Der Import wurde nicht korrekt definiert, es fehlt die Importklasse.', @@ -81,12 +83,14 @@ export default { Activity: 'Aktivität', Add_Reaction: 'Reaktion hinzufügen', Add_Server: 'Server hinzufügen', - Add_users: 'Nutzer hinzufügen', + Add_users: 'Benutzer hinzufügen', Admin_Panel: 'Admin-Panel', + Agent: 'Agent', Alert: 'Benachrichtigung', alert: 'Benachrichtigung', alerts: 'Benachrichtigungen', All_users_in_the_channel_can_write_new_messages: 'Alle Benutzer im Kanal können neue Nachrichten schreiben', + A_meaningful_name_for_the_discussion_room: 'Ein aussagekräftiger Name für den Diskussionsraum', All: 'Alles', All_Messages: 'Alle Nachrichten', Allow_Reactions: 'Reaktionen zulassen', @@ -130,12 +134,15 @@ export default { Click_to_join: 'Klicken um teilzunehmen!', Close: 'Schließen', Close_emoji_selector: 'Schließen Sie die Emoji-Auswahl', + Closing_chat: 'Chat schließen', Change_language_loading: 'Ändere Sprache.', + Chat_closed_by_agent: 'Chat durch den Agenten geschlossen', Choose: 'Wählen', Choose_from_library: 'Aus der Bibliothek auswählen', Choose_file: 'Datei auswählen', Choose_where_you_want_links_be_opened: 'Entscheide, wie Links geöffnet werden sollen', Code: 'Code', + Code_or_password_invalid: 'Code oder Passwort sind falsch', Collaborative: 'Kollaborativ', Confirm: 'Bestätigen', Connect: 'Verbinden', @@ -147,6 +154,7 @@ export default { Continue_with: 'Weitermachen mit', Copied_to_clipboard: 'In die Zwischenablage kopiert!', Copy: 'Kopieren', + Conversation: 'Konversationen', Permalink: 'Permalink', Certificate_password: 'Zertifikats-Passwort', Clear_cache: 'Lokalen Server-Cache leeren', @@ -154,14 +162,18 @@ export default { Whats_the_password_for_your_certificate: 'Wie lautet das Passwort für Ihr Zertifikat?', Create_account: 'Ein Konto erstellen', Create_Channel: 'Kanal erstellen', + Create_Direct_Messages: 'Direkt-Nachricht erstellen', + Create_Discussion: 'Diskussion erstellen', Created_snippet: 'Erstellt ein Snippet', Create_a_new_workspace: 'Erstellen Sie einen neuen Arbeitsbereich', Create: 'Erstellen', + Custom_Status: 'eigener Status', Dark: 'Dunkel', Dark_level: 'Dunkelstufe', Default: 'Standard', Default_browser: 'Standard-Browser', Delete_Room_Warning: 'Durch das Löschen eines Raums werden alle Nachrichten gelöscht, die im Raum gepostet wurden. Das kann nicht rückgängig gemacht werden.', + Department: 'Abteilung', delete: 'löschen', Delete: 'Löschen', DELETE: 'LÖSCHEN', @@ -173,17 +185,23 @@ export default { Direct_Messages: 'Direkte Nachrichten', Disable_notifications: 'Benachrichtigungen deaktiveren', Discussions: 'Diskussionen', - Dont_Have_An_Account: 'Sie haben noch kein Konto?', + Discussion_Desc: 'Hilft dir die Übersicht zu behalten! Durch das Erstellen einer Diskussion wird ein Unter-Kanal im ausgewählten Raum erzeugt und beide verknüpft.', + Discussion_name: 'Diskussions-Name', + Done: 'Erledigt', + Dont_Have_An_Account: 'Du hast noch kein Konto?', + Do_you_have_an_account: 'Du hast schon ein Konto?', Do_you_have_a_certificate: 'Haben Sie ein Zertifikat?', Do_you_really_want_to_key_this_room_question_mark: 'Möchten Sie diesen Raum wirklich {{key}}?', edit: 'bearbeiten', edited: 'bearbeitet', Edit: 'Bearbeiten', + Edit_Status: 'Status ändern', Edit_Invite: 'Einladung bearbeiten', Email_or_password_field_is_empty: 'Das E-Mail- oder Passwortfeld ist leer', Email: 'Email', EMAIL: 'EMAIL', email: 'Email', + Empty_title: 'leerer Titel', Enable_Auto_Translate: 'Automatische Übersetzung aktivieren', Enable_notifications: 'Benachrichtigungen aktivieren', Everyone_can_access_this_channel: 'Jeder kann auf diesen Kanal zugreifen', @@ -200,6 +218,10 @@ export default { Forgot_password_If_this_email_is_registered: 'Wenn diese E-Mail registriert ist, senden wir Anweisungen zum Zurücksetzen Ihres Passworts. Wenn Sie in Kürze keine E-Mail erhalten, kommen Sie bitte zurück und versuchen Sie es erneut.', Forgot_password: 'Passwort vergessen', Forgot_Password: 'Passwort vergessen', + Forward: 'Weiterleiten', + Forward_Chat: 'Chat weiterleiten', + Forward_to_department: 'Weiterleiten an Abteilung', + Forward_to_user: 'Weiterleiten an Benutzer', Full_table: 'Klicken um die ganze Tabelle anzuzeigen', Generate_New_Link: 'Neuen Link erstellen', Group_by_favorites: 'Nach Favoriten gruppieren', @@ -210,19 +232,20 @@ export default { Has_left_the_channel: 'Hat den Kanal verlassen', Hide_System_Messages: 'Systemnachrichten verstecken', Hide_type_messages: 'Verstecke "{{type}}"-Nachrichten', - Message_HideType_uj: 'Nutzer beigetreten', - Message_HideType_ul: 'Nutzer verlassen', - Message_HideType_ru: 'Nutzer entfernt', - Message_HideType_au: 'Nutzer hinzugefügt', - Message_HideType_mute_unmute: 'Nutzer stummgeschaltet / freigegeben', + Message_HideType_uj: 'Benutzer beigetreten', + Message_HideType_ul: 'Benutzer verlassen', + Message_HideType_ru: 'Benutzer entfernt', + Message_HideType_au: 'Benutzer hinzugefügt', + Message_HideType_mute_unmute: 'Benutzer stummgeschaltet / freigegeben', Message_HideType_r: 'Raumname geändert', - Message_HideType_ut: 'Nutzer ist der Unterhaltung beigetreten', + Message_HideType_ut: 'Benutzer ist der Unterhaltung beigetreten', Message_HideType_wm: 'Willkommen', Message_HideType_rm: 'Nachricht entfernt', Message_HideType_subscription_role_added: 'Rolle wurde gesetzt', Message_HideType_subscription_role_removed: 'Rolle nicht länger definiert', Message_HideType_room_archived: 'Raum archiviert', Message_HideType_room_unarchived: 'Raum nicht mehr archiviert', + IP: 'IP', In_app: 'In-App-Browser', IN_APP_AND_DESKTOP: 'IN-APP UND DESKTOP', In_App_and_Desktop_Alert_info: 'Zeigt ein Banner oben am Bildschirm, wenn die App geöffnet ist und eine Benachrichtigung auf dem Desktop.', @@ -230,12 +253,14 @@ export default { Invite: 'Einladen', is_a_valid_RocketChat_instance: 'ist eine gültige Rocket.Chat-Instanz', is_not_a_valid_RocketChat_instance: 'ist keine gültige Rocket.Chat-Instanz', - is_typing: 'tippt', + is_typing: 'schreibt', Invalid_or_expired_invite_token: 'Ungültiger oder abgelaufener Einladungscode', Invalid_server_version: 'Der Server, zu dem Sie eine Verbindung herstellen möchten, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{MinVersion}}.', Invite_Link: 'Einladungs-Link', Invite_users: 'Benutzer einladen', Join: 'Beitreten', + Join_our_open_workspace: 'Tritt unserem offenen Arbeitsbereich bei', + Join_your_workspace: 'Tritt deinem Arbeitsbereich bei', Just_invited_people_can_access_this_channel: 'Nur eingeladene Personen können auf diesen Kanal zugreifen', Language: 'Sprache', last_message: 'letzte Nachricht', @@ -246,12 +271,14 @@ export default { Light: 'Hell', License: 'Lizenz', Livechat: 'Live-Chat', + Livechat_edit: 'Livechat bearbeiten', Login: 'Anmeldung', Login_error: 'Ihre Zugangsdaten wurden abgelehnt! Bitte versuchen Sie es erneut.', Login_with: 'Einloggen mit', Logging_out: 'Abmelden.', Logout: 'Abmelden', Max_number_of_uses: 'Maximale Anzahl der Benutzungen', + Max_number_of_users_allowed_is_number: 'Maximale Anzahl von erlaubten Benutzern ist {{maxUsers}}', members: 'Mitglieder', Members: 'Mitglieder', Mentioned_Messages: 'Erwähnte Nachrichten', @@ -277,6 +304,7 @@ export default { N_users: '{{n}} Benutzer', name: 'Name', Name: 'Name', + Navigation_history: 'Navigations-Verlauf', Never: 'Niemals', New_Message: 'Neue Nachricht', New_Password: 'Neues Kennwort', @@ -303,20 +331,34 @@ export default { Notifications: 'Benachrichtigungen', Notification_Duration: 'Benachrichtigungsdauer', Notification_Preferences: 'Benachrichtigungseinstellungen', + No_available_agents_to_transfer: 'Keine Agenten für den Transfer verfügbar', Offline: 'Offline', Oops: 'Hoppla!', + Onboarding_description: 'Ein Arbeitsbereich ist der Ort für die Zusammenarbeit deines Teams oder Organisation. Bitte den Admin des Arbeitsbereichs um eine Adresse, um ihm beizutreten, oder erstelle einen Arbeitsbereich für dein Team.', + Onboarding_join_workspace: 'Tritt einem Arbeitsbereich bei', + Onboarding_subtitle: 'Mehr als Team-Zusammenarbeit', Onboarding_title: 'Willkommen bei Rocket.Chat', + Onboarding_join_open_description: 'Tritt unserem Arbeitsbereich bei um mit dem Rocket.Chat-Team oder der Gemeinschaft zu chatten.', + Onboarding_agree_terms: 'Durch fortfahren stimmst du Rocket.Chats Bedingungen zu', + Onboarding_less_options: 'Weniger Optionen', + Onboarding_more_options: 'Mehr Optionen', Online: 'Online', Only_authorized_users_can_write_new_messages: 'Nur autorisierte Benutzer können neue Nachrichten schreiben', Open_emoji_selector: 'Öffne die Emoji-Auswahl', Open_Source_Communication: 'Open-Source-Kommunikation', + Open_your_authentication_app_and_enter_the_code: 'Öffne deine Authentifizierungsanwendung und gib den Code ein.', + OR: 'ODER', + OS: 'OS', Overwrites_the_server_configuration_and_use_room_config: 'Übergeht die Servereinstellungen und nutzt Einstellung für den Raum', Password: 'Passwort', + Parent_channel_or_group: 'Übergeordneter Kanal oder Gruppe', Permalink_copied_to_clipboard: 'Permalink in die Zwischenablage kopiert!', + Phone: 'Telefon', Pin: 'Anheften', Pinned_Messages: 'Angeheftete Nachrichten', pinned: 'angeheftet', Pinned: 'Angeheftet', + Please_add_a_comment: 'Bitte Kommentar hinzufügen', Please_enter_your_password: 'Bitte geben Sie Ihr Passwort ein', Please_wait: 'Bitte warten.', Preferences: 'Einstellungen', @@ -355,6 +397,7 @@ export default { Reset_password: 'Passwort zurücksetzen', resetting_password: 'Passwort zurücksetzen', RESET: 'ZURÜCKSETZEN', + Return: 'Zurück', Review_app_title: 'Gefällt dir diese App?', Review_app_desc: 'Gib uns 5 Sterne im {{store}}', Review_app_yes: 'Sicher!', @@ -375,7 +418,8 @@ export default { Room_name_changed: 'Raumname geändert in {{name}} von {{userBy}}', SAVE: 'SPEICHERN', Save_Changes: 'Änderungen speichern', - Save: 'sparen', + Save: 'speichern', + Saved: 'gespeichert', saving_preferences: 'Präferenzen speichern', saving_profile: 'Profil speichern', saving_settings: 'Einstellungen speichern', @@ -388,17 +432,25 @@ export default { Seconds: '{{second}} Sekunden', Select_Avatar: 'Wählen Sie einen Avatar aus', Select_Server: 'Server auswählen', - Select_Users: 'Wählen Sie einen Benutzer aus', + Select_Users: 'Benutzer auswählen', + Select_a_Channel: 'Kanal auswählen', + Select_a_Department: 'Abteilung auswählen', + Select_an_option: 'Option auswählen', + Select_a_User: 'Benutzer auswählen', Send: 'Senden', Send_audio_message: 'Audio-Nachricht senden', Send_crash_report: 'Absturzbericht senden', Send_message: 'Nachricht senden', + Send_me_the_code_again: 'Den Code neu versenden', Send_to: 'Senden an …', Sent_an_attachment: 'Sende einen Anhang', Server: 'Server', Servers: 'Server', Server_version: 'Server version: {{version}}', Set_username_subtitle: 'Der Benutzername wird verwendet, damit andere Personen Sie in Nachrichten erwähnen können', + Set_custom_status: 'Individuellen Status setzen', + Set_status: 'Status setzen', + Status_saved_successfully: 'Status erfolgreich gesetzt!', Settings: 'Einstellungen', Settings_succesfully_changed: 'Einstellungen erfolgreich geändert!', Share: 'Teilen', @@ -407,7 +459,7 @@ export default { Show_more: 'Mehr anzeigen …', Show_Unread_Counter: 'Zähler anzeigen', Show_Unread_Counter_Info: 'Anzahl der ungelesenen Nachrichten anzeigen', - Sign_in_your_server: 'Melden Sie sich bei Ihrem Server an', + Sign_in_your_server: 'Melde dich bei deinem Server an', Sign_Up: 'Anmelden', Some_field_is_invalid_or_empty: 'Ein Feld ist ungültig oder leer', Sorting_by: 'Sortierung nach {{key}}', @@ -422,6 +474,7 @@ export default { Started_call: 'Anruf gestartet von {{userBy}}', Submit: 'einreichen', Table: 'Tabelle', + Tags: 'Tags', Take_a_photo: 'Foto aufnehmen', Take_a_video: 'Video aufnehmen', tap_to_change_status: 'Tippen um den Status zu ändern', @@ -441,10 +494,10 @@ export default { Translate: 'Übersetzen', Try_again: 'Versuchen Sie es nochmal', Two_Factor_Authentication: 'Zwei-Faktor-Authentifizierung', - Type_the_channel_name_here: 'Geben Sie hier den Kanalnamen ein', + Type_the_channel_name_here: 'Gib hier den Kanalnamen ein', unarchive: 'wiederherstellen', UNARCHIVE: 'WIEDERHERSTELLEN', - Unblock_user: 'Nutzer entsperren', + Unblock_user: 'Benutzer entsperren', Unfavorite: 'Nicht mehr favorisieren', Unfollowed_thread: 'Thread nicht mehr folgen', Unmute: 'Stummschaltung aufheben', @@ -457,9 +510,10 @@ export default { Updating: 'Aktualisierung …', Uploading: 'Hochladen', Upload_file_question_mark: 'Datei hochladen?', + User: 'Benutzer', Users: 'Benutzer', User_added_by: 'Benutzer {{userAdded}} hinzugefügt von {{userBy}}', - User_Info: 'Nutzerinfo', + User_Info: 'Benutzerinfo', User_has_been_key: 'Benutzer wurde {{key}}!', User_is_no_longer_role_by_: '{{user}} ist nicht länger {{role}} von {{userBy}}', User_muted_by: 'Benutzer {{userMuted}} von {{userBy}} stummgeschaltet', @@ -471,18 +525,26 @@ export default { Username: 'Benutzername', Username_or_email: 'Benutzername oder E-Mail-Adresse', Uses_server_configuration: 'Nutzt Servereinstellungen', + Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Üblicherweise beginnt eine Diskussion mit einer Frage, beispielsweise: "Wie lade ich ein Bild hoch?"', Validating: 'Validierung', + Registration_Succeeded: 'Registrierung erfolgreich!', + Verify: 'Überprüfen', Verify_email_title: 'Registrierung erfolgreich!', Verify_email_desc: 'Wir haben dir eine Email geschickt um deine Anmeldung zu bestätigen. Wenn du keine Email erhältst, komme bitte wieder und versuche es noch einmal.', + Verify_your_email_for_the_code_we_sent: 'Prüfe deine Mails für den Code, den wir dir eben geschickt haben.', Video_call: 'Videoanruf', View_Original: 'Original anzeigen', Voice_call: 'Sprachanruf', Websocket_disabled: 'Websockets sind auf diesem Server nicht aktiviert.\n{{contact}}', Welcome: 'Herzlich willkommen', + What_are_you_doing_right_now: 'Was machst du gerade?', Whats_your_2fa: 'Wie lautet Ihr 2FA-Code?', Without_Servers: 'Ohne Server', + Workspaces: 'Arbeitsbereiche', + Would_you_like_to_return_the_inquiry: 'Willst du zur Anfrage zurück?', Write_External_Permission_Message: 'Rocket.Chat benötigt Zugriff auf Ihre Galerie um Bilder speichern zu können.', Write_External_Permission: 'Galerie-Zugriff', + Yes: 'Ja', Yes_action_it: 'Ja, {{action}}!', Yesterday: 'Gestern', You_are_in_preview_mode: 'Sie befinden sich im Vorschaumodus', @@ -495,11 +557,13 @@ export default { You: 'Sie', Logged_out_by_server: 'Du bist vom Server abgemeldet worden. Bitte melde dich wieder an.', You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Sie benötigen Zugang zu mindestens einem Rocket.Chat-Server um etwas zu teilen.', - Your_certificate: 'Ihr Zertifikat', + Your_certificate: 'Dein Zertifikat', + Your_message: 'Deine Nachricht', Your_invite_link_will_expire_after__usesLeft__uses: 'Dein Einladungs-Link wird nach {{usesLeft}} Benutzungen ablaufen.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Dein Einladungs-Link wird am {{date}} oder nach {{usesLeft}} Benutzungen ablaufen.', Your_invite_link_will_expire_on__date__: 'Dein Einladungs-Link wird am {{date}} ablaufen.', Your_invite_link_will_never_expire: 'Dein Einladungs-Link wird niemals ablaufen.', + Your_workspace: 'Dein Arbeitsbereich', Version_no: 'Version: {{version}}', You_will_not_be_able_to_recover_this_message: 'Sie können diese Nachricht nicht wiederherstellen!', Change_Language: 'Sprache ändern', @@ -521,5 +585,30 @@ export default { You_will_be_logged_out_of_this_application: 'Du wirst in dieser Anwendung vom Server abgemeldet.', Clear: 'Löschen', This_will_clear_all_your_offline_data: 'Dies wird deine Offline-Daten löschen.', - Mark_unread: 'Als ungelesen markieren' + This_will_remove_all_data_from_this_server: 'Dies wird alle Daten von diesem Server löschen.', + Mark_unread: 'Als ungelesen markieren', + Wait_activation_warning: 'Bevor du dich anmelden kannst, muss dein Konto durch einen Administrator freigeschaltet werden.', + Screen_lock: 'Zugriffs-Sperre', + Local_authentication_biometry_title: 'Authentifizieren', + Local_authentication_biometry_fallback: 'Sicherheitscode benutzen', + Local_authentication_unlock_option: 'Entsperren mit Sicherheitscode', + Local_authentication_change_passcode: 'Ändere Sicherheitscode', + Local_authentication_info: 'Anmerkung: Wenn du den Sicherheitscode vergisst, musst du diese App löschen und neu installieren.', + Local_authentication_facial_recognition: 'Gesichtserkennung', + Local_authentication_fingerprint: 'Fingerabdruck', + Local_authentication_unlock_with_label: 'Entsperren mit {{label}}', + Local_authentication_auto_lock_60: 'Nach 1 Minute', + Local_authentication_auto_lock_300: 'Nach 5 Minuten', + Local_authentication_auto_lock_900: 'Nach 15 Minuten', + Local_authentication_auto_lock_1800: 'Nach 30 Minuten', + Local_authentication_auto_lock_3600: 'Nach 1 Stunde', + Passcode_enter_title: 'Gib deinen Sicherheitscode ein', + Passcode_choose_title: 'Setze deinen neuen Sicherheitscode', + Passcode_choose_confirm_title: 'Bestätige deinen neuen Sicherheitscode', + Passcode_choose_error: 'Sicherheitscodes stimmen nicht überein. Probiere es noch einmal.', + Passcode_choose_force_set: 'Sicherheitscode wird vom Admin verlangt', + Passcode_app_locked_title: 'App gesperrt', + Passcode_app_locked_subtitle: 'Versuche es in {{timeLeft}} Sekunden noch einmal.', + After_seconds_set_by_admin: 'Nach {{seconds}} Sekunden (durch den Admin gesetzt)', + Dont_activate: 'Jetzt nicht aktivieren' }; diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index 6b485ea72..4d1a88a2c 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -18,6 +18,7 @@ export default { 'error-email-domain-blacklisted': 'The email domain is blacklisted', 'error-email-send-failed': 'Error trying to send email: {{message}}', 'error-save-image': 'Error while saving image', + 'error-save-video': 'Error while saving video', 'error-field-unavailable': '{{field}} is already in use :(', 'error-file-too-large': 'File is too large', 'error-importer-not-defined': 'The importer was not defined correctly, it is missing the Import class.', @@ -84,6 +85,7 @@ export default { Add_Server: 'Add Server', Add_users: 'Add users', Admin_Panel: 'Admin Panel', + Agent: 'Agent', Alert: 'Alert', alert: 'alert', alerts: 'alerts', @@ -132,7 +134,9 @@ export default { Click_to_join: 'Click to Join!', Close: 'Close', Close_emoji_selector: 'Close emoji selector', + Closing_chat: 'Closing chat', Change_language_loading: 'Changing language.', + Chat_closed_by_agent: 'Chat closed by agent', Choose: 'Choose', Choose_from_library: 'Choose from library', Choose_file: 'Choose file', @@ -150,6 +154,7 @@ export default { Continue_with: 'Continue with', Copied_to_clipboard: 'Copied to clipboard!', Copy: 'Copy', + Conversation: 'Conversation', Permalink: 'Permalink', Certificate_password: 'Certificate Password', Clear_cache: 'Clear local server cache', @@ -168,6 +173,7 @@ export default { Default: 'Default', Default_browser: 'Default browser', Delete_Room_Warning: 'Deleting a room will delete all messages posted within the room. This cannot be undone.', + Department: 'Department', delete: 'delete', Delete: 'Delete', DELETE: 'DELETE', @@ -195,6 +201,7 @@ export default { Email: 'Email', EMAIL: 'EMAIL', email: 'e-mail', + Empty_title: 'Empty title', Enable_Auto_Translate: 'Enable Auto-Translate', Enable_notifications: 'Enable notifications', Everyone_can_access_this_channel: 'Everyone can access this channel', @@ -211,6 +218,10 @@ export default { Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.', Forgot_password: 'Forgot your password?', Forgot_Password: 'Forgot Password', + Forward: 'Forward', + Forward_Chat: 'Forward Chat', + Forward_to_department: 'Forward to department', + Forward_to_user: 'Forward to user', Full_table: 'Click to see full table', Generate_New_Link: 'Generate New Link', Group_by_favorites: 'Group favorites', @@ -234,6 +245,7 @@ export default { Message_HideType_subscription_role_removed: 'Role No Longer Defined', Message_HideType_room_archived: 'Room Archived', Message_HideType_room_unarchived: 'Room Unarchived', + IP: 'IP', In_app: 'In-app', IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP', In_App_and_Desktop_Alert_info: 'Displays a banner at the top of the screen when app is open, and displays a notification on desktop', @@ -259,6 +271,7 @@ export default { Light: 'Light', License: 'License', Livechat: 'Livechat', + Livechat_edit: 'Livechat edit', Login: 'Login', Login_error: 'Your credentials were rejected! Please try again.', Login_with: 'Login with', @@ -291,6 +304,7 @@ export default { N_users: '{{n}} users', name: 'name', Name: 'Name', + Navigation_history: 'Navigation history', Never: 'Never', New_Message: 'New Message', New_Password: 'New Password', @@ -317,6 +331,7 @@ export default { Notifications: 'Notifications', Notification_Duration: 'Notification Duration', Notification_Preferences: 'Notification Preferences', + No_available_agents_to_transfer: 'No available agents to transfer', Offline: 'Offline', Oops: 'Oops!', Onboarding_description: 'A workspace is your team or organization’s space to collaborate. Ask the workspace admin for address to join or create one for your team.', @@ -333,14 +348,17 @@ export default { Open_Source_Communication: 'Open Source Communication', Open_your_authentication_app_and_enter_the_code: 'Open your authentication app and enter the code.', OR: 'OR', + OS: 'OS', Overwrites_the_server_configuration_and_use_room_config: 'Overwrites the server configuration and use room config', Password: 'Password', Parent_channel_or_group: 'Parent channel or group', Permalink_copied_to_clipboard: 'Permalink copied to clipboard!', + Phone: 'Phone', Pin: 'Pin', Pinned_Messages: 'Pinned Messages', pinned: 'pinned', Pinned: 'Pinned', + Please_add_a_comment: 'Please add a comment', Please_enter_your_password: 'Please enter your password', Please_wait: 'Please wait.', Preferences: 'Preferences', @@ -379,6 +397,7 @@ export default { Reset_password: 'Reset password', resetting_password: 'resetting password', RESET: 'RESET', + Return: 'Return', Review_app_title: 'Are you enjoying this app?', Review_app_desc: 'Give us 5 stars on {{store}}', Review_app_yes: 'Sure!', @@ -400,6 +419,7 @@ export default { SAVE: 'SAVE', Save_Changes: 'Save Changes', Save: 'Save', + Saved: 'Saved', saving_preferences: 'saving preferences', saving_profile: 'saving profile', saving_settings: 'saving settings', @@ -414,7 +434,9 @@ export default { Select_Server: 'Select Server', Select_Users: 'Select Users', Select_a_Channel: 'Select a Channel', + Select_a_Department: 'Select a Department', Select_an_option: 'Select an option', + Select_a_User: 'Select a User', Send: 'Send', Send_audio_message: 'Send audio message', Send_crash_report: 'Send crash report', @@ -452,6 +474,7 @@ export default { Started_call: 'Call started by {{userBy}}', Submit: 'Submit', Table: 'Table', + Tags: 'Tags', Take_a_photo: 'Take a photo', Take_a_video: 'Take a video', tap_to_change_status: 'tap to change status', @@ -487,6 +510,7 @@ export default { Updating: 'Updating...', Uploading: 'Uploading', Upload_file_question_mark: 'Upload file?', + User: 'User', Users: 'Users', User_added_by: 'User {{userAdded}} added by {{userBy}}', User_Info: 'User Info', @@ -517,8 +541,10 @@ export default { Whats_your_2fa: 'What\'s your 2FA code?', Without_Servers: 'Without Servers', Workspaces: 'Workspaces', + Would_you_like_to_return_the_inquiry: 'Would you like to return the inquiry?', Write_External_Permission_Message: 'Rocket Chat needs access to your gallery so you can save images.', Write_External_Permission: 'Gallery Permission', + Yes: 'Yes', Yes_action_it: 'Yes, {{action}} it!', Yesterday: 'Yesterday', You_are_in_preview_mode: 'You are in preview mode', @@ -559,6 +585,30 @@ export default { You_will_be_logged_out_of_this_application: 'You will be logged out of this application.', Clear: 'Clear', This_will_clear_all_your_offline_data: 'This will clear all your offline data.', + This_will_remove_all_data_from_this_server: 'This will remove all data from this server.', Mark_unread: 'Mark Unread', - Wait_activation_warning: 'Before you can login, your account must be manually activated by an administrator.' + Wait_activation_warning: 'Before you can login, your account must be manually activated by an administrator.', + Screen_lock: 'Screen lock', + Local_authentication_biometry_title: 'Authenticate', + Local_authentication_biometry_fallback: 'Use passcode', + Local_authentication_unlock_option: 'Unlock with Passcode', + Local_authentication_change_passcode: 'Change Passcode', + Local_authentication_info: 'Note: if you forget the Passcode, you\'ll need to delete and reinstall the app.', + Local_authentication_facial_recognition: 'facial recognition', + Local_authentication_fingerprint: 'fingerprint', + Local_authentication_unlock_with_label: 'Unlock with {{label}}', + Local_authentication_auto_lock_60: 'After 1 minute', + Local_authentication_auto_lock_300: 'After 5 minutes', + Local_authentication_auto_lock_900: 'After 15 minutes', + Local_authentication_auto_lock_1800: 'After 30 minutes', + Local_authentication_auto_lock_3600: 'After 1 hour', + Passcode_enter_title: 'Enter your passcode', + Passcode_choose_title: 'Choose your new passcode', + Passcode_choose_confirm_title: 'Confirm your new passcode', + Passcode_choose_error: 'Passcodes don\'t match. Try again.', + Passcode_choose_force_set: 'Passcode required by admin', + Passcode_app_locked_title: 'App locked', + Passcode_app_locked_subtitle: 'Try again in {{timeLeft}} seconds', + After_seconds_set_by_admin: 'After {{seconds}} seconds (set by admin)', + Dont_activate: 'Don\'t activate now' }; diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index 5cd3fb3e1..6c7e06b50 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -89,6 +89,7 @@ export default { Add_Reaction: 'Reagir', Add_Server: 'Adicionar servidor', Add_users: 'Adicionar usuário', + Agent: 'Agente', Alert: 'Alerta', alert: 'alerta', alerts: 'alertas', @@ -135,7 +136,9 @@ export default { Click_to_join: 'Clique para participar!', Close: 'Fechar', Close_emoji_selector: 'Fechar seletor de emojis', + Closing_chat: 'Fechando conversa', Choose: 'Escolher', + Chat_closed_by_agent: 'Conversa fechada por agente', Choose_from_library: 'Escolha da biblioteca', Choose_file: 'Enviar arquivo', Choose_where_you_want_links_be_opened: 'Escolha onde deseja que os links sejam abertos', @@ -145,6 +148,7 @@ export default { Confirm: 'Confirmar', Connect: 'Conectar', Connected: 'Conectado', + Conversation: 'Conversação', connecting_server: 'conectando no servidor', Connecting: 'Conectando...', Continue_with: 'Entrar com', @@ -187,6 +191,7 @@ export default { Email_or_password_field_is_empty: 'Email ou senha estão vazios', Email: 'Email', email: 'e-mail', + Empty_title: 'Título vazio', Enable_notifications: 'Habilitar notificações', Everyone_can_access_this_channel: 'Todos podem acessar este canal', Error_uploading: 'Erro subindo', @@ -201,6 +206,10 @@ export default { Forgot_password_If_this_email_is_registered: 'Se este e-mail estiver cadastrado, enviaremos instruções sobre como redefinir sua senha. Se você não receber um e-mail em breve, volte e tente novamente.', Forgot_password: 'Esqueceu sua senha?', Forgot_Password: 'Esqueci minha senha', + Forward: 'Encaminhar', + Forward_Chat: 'Encaminhar Conversa', + Forward_to_department: 'Encaminhar para departamento', + Forward_to_user: 'Encaminhar para usuário', Full_table: 'Clique para ver a tabela completa', Generate_New_Link: 'Gerar novo convite', Group_by_favorites: 'Agrupar favoritos', @@ -223,6 +232,7 @@ export default { Message_HideType_subscription_role_removed: 'Papel removido', Message_HideType_room_archived: 'Sala arquivada', Message_HideType_room_unarchived: 'Sala desarquivada', + IP: 'IP', In_app: 'No app', Invisible: 'Invisível', Invite: 'Convidar', @@ -269,6 +279,7 @@ export default { N_users: '{{n}} usuários', name: 'nome', Name: 'Nome', + Navigation_history: 'Histórico de navegação', Never: 'Nunca', New_in_RocketChat_question_mark: 'Novo no Rocket.Chat?', New_Message: 'Nova Mensagem', @@ -289,6 +300,7 @@ export default { Notify_active_in_this_room: 'Notificar usuários ativos nesta sala', Notify_all_in_this_room: 'Notificar todos nesta sala', Not_RC_Server: 'Este não é um servidor Rocket.Chat.\n{{contact}}', + No_available_agents_to_transfer: 'Nenhum agente disponível para transferência', Offline: 'Offline', Oops: 'Ops!', Onboarding_description: 'Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.', @@ -305,6 +317,7 @@ export default { Open_Source_Communication: 'Comunicação Open Source', Open_your_authentication_app_and_enter_the_code: 'Abra seu aplicativo de autenticação e digite o código.', OR: 'OU', + OS: 'SO', Overwrites_the_server_configuration_and_use_room_config: 'Substituir a configuração do servidor e usar a configuração da sala', Password: 'Senha', Parent_channel_or_group: 'Canal ou grupo pai', @@ -315,6 +328,7 @@ export default { Pinned: 'Mensagens Fixadas', Please_wait: 'Por favor, aguarde.', Please_enter_your_password: 'Por favor, digite sua senha', + Please_add_a_comment: 'Por favor, adicione um comentário', Preferences: 'Preferências', Preferences_saved: 'Preferências salvas!', Privacy_Policy: ' Política de Privacidade', @@ -343,6 +357,7 @@ export default { Reset_password: 'Resetar senha', resetting_password: 'redefinindo senha', RESET: 'RESETAR', + Return: 'Retornar', Review_app_title: 'Você está gostando do app?', Review_app_desc: 'Nos dê 5 estrelas na {{store}}', Review_app_yes: 'Claro!', @@ -377,7 +392,9 @@ export default { Select_Server: 'Selecionar Servidor', Select_Users: 'Selecionar Usuários', Select_a_Channel: 'Selecione um canal', + Select_a_Department: 'Selecione um Departamento', Select_an_option: 'Selecione uma opção', + Select_a_User: 'Selecione um Usuário', Send: 'Enviar', Send_audio_message: 'Enviar mensagem de áudio', Send_message: 'Enviar mensagem', @@ -436,6 +453,7 @@ export default { Updating: 'Atualizando...', Uploading: 'Subindo arquivo', Upload_file_question_mark: 'Enviar arquivo?', + User: 'Usuário', Users: 'Usuários', User_added_by: 'Usuário {{userAdded}} adicionado por {{userBy}}', User_has_been_key: 'Usuário foi {{key}}!', @@ -479,8 +497,10 @@ export default { Your_invite_link_will_never_expire: 'Seu link de convite nunca irá vencer.', Your_workspace: 'Sua workspace', You_will_not_be_able_to_recover_this_message: 'Você não será capaz de recuperar essa mensagem!', + Would_you_like_to_return_the_inquiry: 'Deseja retornar a consulta?', Write_External_Permission_Message: 'Rocket Chat precisa de acesso à sua galeria para salvar imagens', Write_External_Permission: 'Acesso à Galeria', + Yes: 'Sim', Crash_report_disclaimer: 'Nós não rastreamos o conteúdo das suas conversas. O relatório de erros apenas contém informações relevantes para identificarmos problemas e corrigí-los.', Type_message: 'Digitar mensagem', Room_search: 'Busca de sala', @@ -499,6 +519,30 @@ export default { You_will_be_logged_out_of_this_application: 'Você sairá deste aplicativo.', Clear: 'Limpar', This_will_clear_all_your_offline_data: 'Isto limpará todos os seus dados offline.', + This_will_remove_all_data_from_this_server: 'Isto removerá todos os dados desse servidor.', Mark_unread: 'Marcar como não Lida', - Wait_activation_warning: 'Antes que você possa fazer o login, sua conta deve ser manualmente ativada por um administrador.' + Wait_activation_warning: 'Antes que você possa fazer o login, sua conta deve ser manualmente ativada por um administrador.', + Screen_lock: 'Bloqueio de Tela', + Local_authentication_biometry_title: 'Autenticar', + Local_authentication_biometry_fallback: 'Usar senha', + Local_authentication_unlock_option: 'Desbloquear com senha', + Local_authentication_change_passcode: 'Alterar senha', + Local_authentication_info: 'Nota: se você esquecer sua senha, terá de apagar e reinstalar o app.', + Local_authentication_facial_recognition: 'reconhecimento facial', + Local_authentication_fingerprint: 'impressão digital', + Local_authentication_unlock_with_label: 'Desbloquear com {{label}}', + Local_authentication_auto_lock_60: 'Após 1 minuto', + Local_authentication_auto_lock_300: 'Após 5 minutos', + Local_authentication_auto_lock_900: 'Após 15 minutos', + Local_authentication_auto_lock_1800: 'Após 30 minutos', + Local_authentication_auto_lock_3600: 'Após 1 hora', + Passcode_enter_title: 'Digite sua senha', + Passcode_choose_title: 'Insira sua nova senha', + Passcode_choose_confirm_title: 'Confirme sua nova senha', + Passcode_choose_error: 'As senhas não coincidem. Tente novamente.', + Passcode_choose_force_set: 'Senha foi exigida pelo admin', + Passcode_app_locked_title: 'Aplicativo bloqueado', + Passcode_app_locked_subtitle: 'Tente novamente em {{timeLeft}} segundos', + After_seconds_set_by_admin: 'Após {{seconds}} segundos (Configurado pelo adm)', + Dont_activate: 'Não ativar agora' }; diff --git a/app/index.js b/app/index.js index 3b245f1d8..14a19e7ef 100644 --- a/app/index.js +++ b/app/index.js @@ -46,6 +46,8 @@ import TwoFactor from './containers/TwoFactor'; import RoomsListView from './views/RoomsListView'; import RoomView from './views/RoomView'; +import ScreenLockedView from './views/ScreenLockedView'; +import ChangePasscodeView from './views/ChangePasscodeView'; if (isIOS) { const RNScreens = require('react-native-screens'); @@ -166,6 +168,15 @@ const ChatsStack = createStackNavigator({ NotificationPrefView: { getScreen: () => require('./views/NotificationPreferencesView').default }, + VisitorNavigationView: { + getScreen: () => require('./views/VisitorNavigationView').default + }, + ForwardLivechatView: { + getScreen: () => require('./views/ForwardLivechatView').default + }, + LivechatEditView: { + getScreen: () => require('./views/LivechatEditView').default + }, PickerView: { getScreen: () => require('./views/PickerView').default }, @@ -224,6 +235,9 @@ const SettingsStack = createStackNavigator({ }, DefaultBrowserView: { getScreen: () => require('./views/DefaultBrowserView').default + }, + ScreenLockConfigView: { + getScreen: () => require('./views/ScreenLockConfigView').default } }, { defaultNavigationOptions: defaultHeader, @@ -514,7 +528,7 @@ class CustomModalStack extends React.Component { const pageSheetViews = ['AttachmentView']; const pageSheet = pageSheetViews.includes(getActiveRouteName(navigation.state)); - const androidProps = isAndroid && { + const androidProps = isAndroid && !pageSheet && { style: { marginBottom: 0 } }; @@ -524,7 +538,7 @@ class CustomModalStack extends React.Component { </View> ); - if (isAndroid) { + if (isAndroid && !pageSheet) { content = ( <ScrollView overScrollMode='never'> {content} @@ -729,6 +743,8 @@ export default class Root extends React.Component { > {content} <TwoFactor /> + <ScreenLockedView /> + <ChangePasscodeView /> </ThemeContext.Provider> </Provider> </AppearanceProvider> diff --git a/app/lib/appStateMiddleware.js b/app/lib/appStateMiddleware.js new file mode 100644 index 000000000..7bc5375f0 --- /dev/null +++ b/app/lib/appStateMiddleware.js @@ -0,0 +1,35 @@ +// https://github.com/bamlab/redux-enhancer-react-native-appstate +import { AppState } from 'react-native'; + +import { APP_STATE } from '../actions/actionsTypes'; + +export default () => createStore => (...args) => { + const store = createStore(...args); + + let currentState = ''; + + const handleAppStateChange = (nextAppState) => { + if (nextAppState !== 'inactive') { + if (currentState !== nextAppState) { + let type; + if (nextAppState === 'active') { + type = APP_STATE.FOREGROUND; + } else if (nextAppState === 'background') { + type = APP_STATE.BACKGROUND; + } + if (type) { + store.dispatch({ + type + }); + } + } + currentState = nextAppState; + } + }; + + AppState.addEventListener('change', handleAppStateChange); + + // setTimeout to allow redux-saga to catch the initial state fired by redux-enhancer-react-native-appstate library + setTimeout(() => handleAppStateChange(AppState.currentState)); + return store; +}; diff --git a/app/lib/createStore.js b/app/lib/createStore.js index b4e1113a0..da52de600 100644 --- a/app/lib/createStore.js +++ b/app/lib/createStore.js @@ -1,9 +1,9 @@ import { createStore, applyMiddleware, compose } from 'redux'; import createSagaMiddleware from 'redux-saga'; -import applyAppStateListener from 'redux-enhancer-react-native-appstate'; import reducers from '../reducers'; import sagas from '../sagas'; +import applyAppStateMiddleware from './appStateMiddleware'; let sagaMiddleware; let enhancers; @@ -16,7 +16,7 @@ if (__DEV__) { }); enhancers = compose( - applyAppStateListener(), + applyAppStateMiddleware(), applyMiddleware(reduxImmutableStateInvariant), applyMiddleware(sagaMiddleware), Reactotron.createEnhancer() @@ -24,7 +24,7 @@ if (__DEV__) { } else { sagaMiddleware = createSagaMiddleware(); enhancers = compose( - applyAppStateListener(), + applyAppStateMiddleware(), applyMiddleware(sagaMiddleware) ); } diff --git a/app/lib/database/index.js b/app/lib/database/index.js index f12cadbca..9632a93e0 100644 --- a/app/lib/database/index.js +++ b/app/lib/database/index.js @@ -34,6 +34,36 @@ if (__DEV__ && isIOS) { console.log(appGroupPath); } +export const getDatabase = (database = '') => { + const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.'); + const dbName = `${ appGroupPath }${ path }.db`; + + const adapter = new SQLiteAdapter({ + dbName, + schema: appSchema, + migrations + }); + + return new Database({ + adapter, + modelClasses: [ + Subscription, + Room, + Message, + Thread, + ThreadMessage, + CustomEmoji, + FrequentlyUsedEmoji, + Upload, + Setting, + Role, + Permission, + SlashCommand + ], + actionsEnabled: true + }); +}; + class DB { databases = { serversDB: new Database({ @@ -87,34 +117,8 @@ class DB { }); } - setActiveDB(database = '') { - const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.'); - const dbName = `${ appGroupPath }${ path }.db`; - - const adapter = new SQLiteAdapter({ - dbName, - schema: appSchema, - migrations - }); - - this.databases.activeDB = new Database({ - adapter, - modelClasses: [ - Subscription, - Room, - Message, - Thread, - ThreadMessage, - CustomEmoji, - FrequentlyUsedEmoji, - Upload, - Setting, - Role, - Permission, - SlashCommand - ], - actionsEnabled: true - }); + setActiveDB(database) { + this.databases.activeDB = getDatabase(database); } } diff --git a/app/lib/database/model/Message.js b/app/lib/database/model/Message.js index 24dec99a6..22dfec731 100644 --- a/app/lib/database/model/Message.js +++ b/app/lib/database/model/Message.js @@ -30,6 +30,8 @@ export default class Message extends Model { @field('avatar') avatar; + @field('emoji') emoji; + @json('attachments', sanitizer) attachments; @json('urls', sanitizer) urls; diff --git a/app/lib/database/model/Room.js b/app/lib/database/model/Room.js index 4a1097a0e..0a8beab12 100644 --- a/app/lib/database/model/Room.js +++ b/app/lib/database/model/Room.js @@ -13,4 +13,14 @@ export default class Room extends Model { @field('encrypted') encrypted; @field('ro') ro; + + @json('v', sanitizer) v; + + @json('served_by', sanitizer) servedBy; + + @field('department_id') departmentId; + + @json('livechat_data', sanitizer) livechatData; + + @json('tags', sanitizer) tags; } diff --git a/app/lib/database/model/Server.js b/app/lib/database/model/Server.js index e4dad5ed2..5ff103fac 100644 --- a/app/lib/database/model/Server.js +++ b/app/lib/database/model/Server.js @@ -17,4 +17,12 @@ export default class Server extends Model { @date('rooms_updated_at') roomsUpdatedAt; @field('version') version; + + @date('last_local_authenticated_session') lastLocalAuthenticatedSession; + + @field('auto_lock') autoLock; + + @field('auto_lock_time') autoLockTime; + + @field('biometry') biometry; } diff --git a/app/lib/database/model/Subscription.js b/app/lib/database/model/Subscription.js index 37bab8c08..77efd3783 100644 --- a/app/lib/database/model/Subscription.js +++ b/app/lib/database/model/Subscription.js @@ -50,6 +50,8 @@ export default class Subscription extends Model { @field('announcement') announcement; + @field('banner_closed') bannerClosed; + @field('topic') topic; @field('blocked') blocked; @@ -95,4 +97,14 @@ export default class Subscription extends Model { @json('uids', sanitizer) uids; @json('usernames', sanitizer) usernames; + + @json('visitor', sanitizer) visitor; + + @field('department_id') departmentId; + + @json('served_by', sanitizer) servedBy; + + @json('livechat_data', sanitizer) livechatData; + + @json('tags', sanitizer) tags; } diff --git a/app/lib/database/model/Thread.js b/app/lib/database/model/Thread.js index e84dd5b7c..fca0122ff 100644 --- a/app/lib/database/model/Thread.js +++ b/app/lib/database/model/Thread.js @@ -30,6 +30,8 @@ export default class Thread extends Model { @field('avatar') avatar; + @field('emoji') emoji; + @json('attachments', sanitizer) attachments; @json('urls', sanitizer) urls; diff --git a/app/lib/database/model/ThreadMessage.js b/app/lib/database/model/ThreadMessage.js index f071a9c62..842f22285 100644 --- a/app/lib/database/model/ThreadMessage.js +++ b/app/lib/database/model/ThreadMessage.js @@ -32,6 +32,8 @@ export default class ThreadMessage extends Model { @field('avatar') avatar; + @field('emoji') emoji; + @json('attachments', sanitizer) attachments; @json('urls', sanitizer) urls; diff --git a/app/lib/database/model/migrations.js b/app/lib/database/model/migrations.js index c8d815245..260209427 100644 --- a/app/lib/database/model/migrations.js +++ b/app/lib/database/model/migrations.js @@ -74,6 +74,50 @@ export default schemaMigrations({ ] }) ] + }, + { + toVersion: 8, + steps: [ + addColumns({ + table: 'messages', + columns: [ + { name: 'emoji', type: 'string', isOptional: true } + ] + }), + addColumns({ + table: 'thread_messages', + columns: [ + { name: 'emoji', type: 'string', isOptional: true } + ] + }), + addColumns({ + table: 'threads', + columns: [ + { name: 'emoji', type: 'string', isOptional: true } + ] + }), + addColumns({ + table: 'subscriptions', + columns: [ + { name: 'banner_closed', type: 'boolean', isOptional: true }, + { name: 'visitor', type: 'string', isOptional: true }, + { name: 'department_id', type: 'string', isOptional: true }, + { name: 'served_by', type: 'string', isOptional: true }, + { name: 'livechat_data', type: 'string', isOptional: true }, + { name: 'tags', type: 'string', isOptional: true } + ] + }), + addColumns({ + table: 'rooms', + columns: [ + { name: 'v', type: 'string', isOptional: true }, + { name: 'department_id', type: 'string', isOptional: true }, + { name: 'served_by', type: 'string', isOptional: true }, + { name: 'livechat_data', type: 'string', isOptional: true }, + { name: 'tags', type: 'string', isOptional: true } + ] + }) + ] } ] }); diff --git a/app/lib/database/model/serversMigrations.js b/app/lib/database/model/serversMigrations.js index d11b76432..0163d3bde 100644 --- a/app/lib/database/model/serversMigrations.js +++ b/app/lib/database/model/serversMigrations.js @@ -12,6 +12,20 @@ export default schemaMigrations({ ] }) ] + }, + { + toVersion: 4, + steps: [ + addColumns({ + table: 'servers', + columns: [ + { name: 'last_local_authenticated_session', type: 'number', isOptional: true }, + { name: 'auto_lock', type: 'boolean', isOptional: true }, + { name: 'auto_lock_time', type: 'number', isOptional: true }, + { name: 'biometry', type: 'boolean', isOptional: true } + ] + }) + ] } ] }); diff --git a/app/lib/database/schema/app.js b/app/lib/database/schema/app.js index a16cb547d..97784b3f9 100644 --- a/app/lib/database/schema/app.js +++ b/app/lib/database/schema/app.js @@ -1,7 +1,7 @@ import { appSchema, tableSchema } from '@nozbe/watermelondb'; export default appSchema({ - version: 7, + version: 8, tables: [ tableSchema({ name: 'subscriptions', @@ -25,6 +25,7 @@ export default appSchema({ { name: 'last_message', type: 'string', isOptional: true }, { name: 'description', type: 'string', isOptional: true }, { name: 'announcement', type: 'string', isOptional: true }, + { name: 'banner_closed', type: 'boolean', isOptional: true }, { name: 'topic', type: 'string', isOptional: true }, { name: 'blocked', type: 'boolean', isOptional: true }, { name: 'blocker', type: 'boolean', isOptional: true }, @@ -42,7 +43,12 @@ export default appSchema({ { name: 'hide_unread_status', type: 'boolean', isOptional: true }, { name: 'sys_mes', type: 'string', isOptional: true }, { name: 'uids', type: 'string', isOptional: true }, - { name: 'usernames', type: 'string', isOptional: true } + { name: 'usernames', type: 'string', isOptional: true }, + { name: 'visitor', type: 'string', isOptional: true }, + { name: 'department_id', type: 'string', isOptional: true }, + { name: 'served_by', type: 'string', isOptional: true }, + { name: 'livechat_data', type: 'string', isOptional: true }, + { name: 'tags', type: 'string', isOptional: true } ] }), tableSchema({ @@ -51,7 +57,12 @@ export default appSchema({ { name: 'custom_fields', type: 'string' }, { name: 'broadcast', type: 'boolean' }, { name: 'encrypted', type: 'boolean' }, - { name: 'ro', type: 'boolean' } + { name: 'ro', type: 'boolean' }, + { name: 'v', type: 'string', isOptional: true }, + { name: 'department_id', type: 'string', isOptional: true }, + { name: 'served_by', type: 'string', isOptional: true }, + { name: 'livechat_data', type: 'string', isOptional: true }, + { name: 'tags', type: 'string', isOptional: true } ] }), tableSchema({ @@ -66,6 +77,7 @@ export default appSchema({ { name: 'parse_urls', type: 'string' }, { name: 'groupable', type: 'boolean', isOptional: true }, { name: 'avatar', type: 'string', isOptional: true }, + { name: 'emoji', type: 'string', isOptional: true }, { name: 'attachments', type: 'string', isOptional: true }, { name: 'urls', type: 'string', isOptional: true }, { name: '_updated_at', type: 'number' }, @@ -104,6 +116,7 @@ export default appSchema({ { name: 'parse_urls', type: 'string', isOptional: true }, { name: 'groupable', type: 'boolean', isOptional: true }, { name: 'avatar', type: 'string', isOptional: true }, + { name: 'emoji', type: 'string', isOptional: true }, { name: 'attachments', type: 'string', isOptional: true }, { name: 'urls', type: 'string', isOptional: true }, { name: 'status', type: 'number', isOptional: true }, @@ -140,6 +153,7 @@ export default appSchema({ { name: 'parse_urls', type: 'string', isOptional: true }, { name: 'groupable', type: 'boolean', isOptional: true }, { name: 'avatar', type: 'string', isOptional: true }, + { name: 'emoji', type: 'string', isOptional: true }, { name: 'attachments', type: 'string', isOptional: true }, { name: 'urls', type: 'string', isOptional: true }, { name: 'status', type: 'number', isOptional: true }, diff --git a/app/lib/database/schema/servers.js b/app/lib/database/schema/servers.js index ec4980d1c..7557f8b1c 100644 --- a/app/lib/database/schema/servers.js +++ b/app/lib/database/schema/servers.js @@ -1,7 +1,7 @@ import { appSchema, tableSchema } from '@nozbe/watermelondb'; export default appSchema({ - version: 3, + version: 4, tables: [ tableSchema({ name: 'users', @@ -24,7 +24,11 @@ export default appSchema({ { name: 'file_upload_media_type_white_list', type: 'string', isOptional: true }, { name: 'file_upload_max_file_size', type: 'number', isOptional: true }, { name: 'rooms_updated_at', type: 'number', isOptional: true }, - { name: 'version', type: 'string', isOptional: true } + { name: 'version', type: 'string', isOptional: true }, + { name: 'last_local_authenticated_session', type: 'number', isOptional: true }, + { name: 'auto_lock', type: 'boolean', isOptional: true }, + { name: 'auto_lock_time', type: 'number', isOptional: true }, + { name: 'biometry', type: 'boolean', isOptional: true } ] }) ] diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js index 0e13d378b..79e3db074 100644 --- a/app/lib/methods/getSettings.js +++ b/app/lib/methods/getSettings.js @@ -10,8 +10,9 @@ import log from '../../utils/log'; import database from '../database'; import protectedFunction from './helpers/protectedFunction'; import fetch from '../../utils/fetch'; +import { DEFAULT_AUTO_LOCK } from '../../constants/localAuthentication'; -const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize']; +const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After']; // these settings are used only on onboarding process const loginSettings = [ @@ -32,6 +33,8 @@ const loginSettings = [ const serverInfoUpdate = async(serverInfo, iconSetting) => { const serversDB = database.servers; const serverId = reduxStore.getState().server.server; + const serversCollection = serversDB.collections.get('servers'); + const server = await serversCollection.find(serverId); let info = serverInfo.reduce((allSettings, setting) => { if (setting._id === 'Site_Name') { @@ -46,6 +49,23 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => { if (setting._id === 'FileUpload_MaxFileSize') { return { ...allSettings, FileUpload_MaxFileSize: setting.valueAsNumber }; } + if (setting._id === 'Force_Screen_Lock') { + // if this was disabled on server side we must keep this enabled on app + const autoLock = server.autoLock || setting.valueAsBoolean; + return { ...allSettings, autoLock }; + } + if (setting._id === 'Force_Screen_Lock_After') { + const forceScreenLock = serverInfo.find(s => s._id === 'Force_Screen_Lock')?.valueAsBoolean; + + // if Force_Screen_Lock_After === 0 and autoLockTime is null, set app's default value + if (setting.valueAsNumber === 0 && !server.autoLockTime) { + return { ...allSettings, autoLockTime: DEFAULT_AUTO_LOCK }; + } + // if Force_Screen_Lock_After > 0 and forceScreenLock is enabled, use it + if (setting.valueAsNumber > 0 && forceScreenLock) { + return { ...allSettings, autoLockTime: setting.valueAsNumber }; + } + } return allSettings; }, {}); @@ -56,9 +76,6 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => { await serversDB.action(async() => { try { - const serversCollection = serversDB.collections.get('servers'); - const server = await serversCollection.find(serverId); - await server.update((record) => { Object.assign(record, info); }); diff --git a/app/lib/methods/helpers/findSubscriptionsRooms.js b/app/lib/methods/helpers/findSubscriptionsRooms.js index 9773c5f7d..457fc3b5f 100644 --- a/app/lib/methods/helpers/findSubscriptionsRooms.js +++ b/app/lib/methods/helpers/findSubscriptionsRooms.js @@ -27,6 +27,7 @@ export default async(subscriptions = [], rooms = []) => { lastOpen: s.lastOpen, description: s.description, announcement: s.announcement, + bannerClosed: s.bannerClosed, topic: s.topic, blocked: s.blocked, blocker: s.blocker, @@ -43,7 +44,12 @@ export default async(subscriptions = [], rooms = []) => { autoTranslateLanguage: s.autoTranslateLanguage, lastMessage: s.lastMessage, usernames: s.usernames, - uids: s.uids + uids: s.uids, + visitor: s.visitor, + departmentId: s.departmentId, + servedBy: s.servedBy, + livechatData: s.livechatData, + tags: s.tags })); subscriptions = subscriptions.concat(existingSubs); @@ -64,7 +70,12 @@ export default async(subscriptions = [], rooms = []) => { ro: r.ro, broadcast: r.broadcast, muted: r.muted, - sysMes: r.sysMes + sysMes: r.sysMes, + v: r.v, + departmentId: r.departmentId, + servedBy: r.servedBy, + livechatData: r.livechatData, + tags: r.tags })); rooms = rooms.concat(existingRooms); } catch { diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.js index 2ab12f652..6d9f6624d 100644 --- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js +++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.js @@ -35,6 +35,21 @@ export const merge = (subscription, room) => { } else { subscription.muted = []; } + if (room.v) { + subscription.visitor = room.v; + } + if (room.departmentId) { + subscription.departmentId = room.departmentId; + } + if (room.servedBy) { + subscription.servedBy = room.servedBy; + } + if (room.livechatData) { + subscription.livechatData = room.livechatData; + } + if (room.tags) { + subscription.tags = room.tags; + } subscription.sysMes = room.sysMes; } diff --git a/app/lib/methods/logout.js b/app/lib/methods/logout.js new file mode 100644 index 000000000..4064362de --- /dev/null +++ b/app/lib/methods/logout.js @@ -0,0 +1,127 @@ +import RNUserDefaults from 'rn-user-defaults'; +import * as FileSystem from 'expo-file-system'; +import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk'; + +import { SERVERS, SERVER_URL } from '../../constants/userDefaults'; +import { getDeviceToken } from '../../notifications/push'; +import { extractHostname } from '../../utils/server'; +import { BASIC_AUTH_KEY } from '../../utils/fetch'; +import database, { getDatabase } from '../database'; +import RocketChat from '../rocketchat'; +import { useSsl } from '../../utils/url'; + +async function removeServerKeys({ server, userId }) { + await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ server }`); + await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ userId }`); + await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`); +} + +async function removeSharedCredentials({ server }) { + try { + const servers = await RNUserDefaults.objectForKey(SERVERS); + await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server)); + + // clear certificate for server - SSL Pinning + const certificate = await RNUserDefaults.objectForKey(extractHostname(server)); + if (certificate && certificate.path) { + await RNUserDefaults.clear(extractHostname(server)); + await FileSystem.deleteAsync(certificate.path); + } + } catch (e) { + console.log('removeSharedCredentials', e); + } +} + +async function removeServerData({ server }) { + try { + const batch = []; + const serversDB = database.servers; + const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`); + + const usersCollection = serversDB.collections.get('users'); + if (userId) { + const userRecord = await usersCollection.find(userId); + batch.push(userRecord.prepareDestroyPermanently()); + } + const serverCollection = serversDB.collections.get('servers'); + const serverRecord = await serverCollection.find(server); + batch.push(serverRecord.prepareDestroyPermanently()); + + await serversDB.action(() => serversDB.batch(...batch)); + await removeSharedCredentials({ server }); + await removeServerKeys({ server }); + } catch (e) { + console.log('removeServerData', e); + } +} + +async function removeCurrentServer() { + await RNUserDefaults.clear('currentServer'); + await RNUserDefaults.clear(RocketChat.TOKEN_KEY); +} + +async function removeServerDatabase({ server }) { + try { + const db = getDatabase(server); + await db.action(() => db.unsafeResetDatabase()); + } catch (e) { + console.log(e); + } +} + +export async function removeServer({ server }) { + try { + const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`); + if (userId) { + const resume = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ userId }`); + + const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); + await sdk.login({ resume }); + + const token = getDeviceToken(); + if (token) { + await sdk.del('push.token', { token }); + } + + await sdk.logout(); + } + + await removeServerData({ server }); + await removeServerDatabase({ server }); + } catch (e) { + console.log('removePush', e); + } +} + +export default async function logout({ server }) { + if (this.roomsSub) { + this.roomsSub.stop(); + this.roomsSub = null; + } + + if (this.activeUsersSubTimeout) { + clearTimeout(this.activeUsersSubTimeout); + this.activeUsersSubTimeout = false; + } + + try { + await this.removePushToken(); + } catch (e) { + console.log('removePushToken', e); + } + + try { + // RC 0.60.0 + await this.sdk.logout(); + } catch (e) { + console.log('logout', e); + } + + if (this.sdk) { + this.sdk = null; + } + + await removeServerData({ server }); + await removeCurrentServer(); + await removeServerDatabase({ server }); +} diff --git a/app/lib/methods/sendFileMessage.js b/app/lib/methods/sendFileMessage.js index b5a85c4cc..041b1f40c 100644 --- a/app/lib/methods/sendFileMessage.js +++ b/app/lib/methods/sendFileMessage.js @@ -62,7 +62,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { formData.append('file', { uri: fileInfo.path, type: fileInfo.type, - name: fileInfo.name || 'fileMessage' + name: encodeURI(fileInfo.name) || 'fileMessage' }); if (fileInfo.description) { diff --git a/app/lib/methods/subscriptions/room.js b/app/lib/methods/subscriptions/room.js index 507c36b09..8dfb45d01 100644 --- a/app/lib/methods/subscriptions/room.js +++ b/app/lib/methods/subscriptions/room.js @@ -10,6 +10,7 @@ import reduxStore from '../../createStore'; import { addUserTyping, removeUserTyping, clearUserTyping } from '../../../actions/usersTyping'; import debounce from '../../../utils/debounce'; import RocketChat from '../../rocketchat'; +import { subscribeRoom, unsubscribeRoom } from '../../../actions/room'; const WINDOW_TIME = 1000; @@ -38,6 +39,8 @@ export default class RoomSubscription { if (!this.isAlive) { this.unsubscribe(); } + + reduxStore.dispatch(subscribeRoom(this.rid)); } unsubscribe = async() => { @@ -59,6 +62,8 @@ export default class RoomSubscription { if (this.timer) { clearTimeout(this.timer); } + + reduxStore.dispatch(unsubscribeRoom(this.rid)); } removeListener = async(promise) => { @@ -155,22 +160,17 @@ export default class RoomSubscription { const msgCollection = db.collections.get('messages'); const threadsCollection = db.collections.get('threads'); const threadMessagesCollection = db.collections.get('thread_messages'); - let messageRecord; - let threadRecord; - let threadMessageRecord; // Create or update message try { - messageRecord = await msgCollection.find(message._id); - } catch (error) { - // Do nothing - } - if (messageRecord) { - const update = messageRecord.prepareUpdate((m) => { - Object.assign(m, message); - }); - this._messagesBatch[message._id] = update; - } else { + const messageRecord = await msgCollection.find(message._id); + if (!messageRecord._hasPendingUpdate) { + const update = messageRecord.prepareUpdate(protectedFunction((m) => { + Object.assign(m, message); + })); + this._messagesBatch[message._id] = update; + } + } catch { const create = msgCollection.prepareCreate(protectedFunction((m) => { m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema); m.subscription.id = this.rid; @@ -182,17 +182,14 @@ export default class RoomSubscription { // Create or update thread if (message.tlm) { try { - threadRecord = await threadsCollection.find(message._id); - } catch (error) { - // Do nothing - } - - if (threadRecord) { - const updateThread = threadRecord.prepareUpdate(protectedFunction((t) => { - Object.assign(t, message); - })); - this._threadsBatch[message._id] = updateThread; - } else { + const threadRecord = await threadsCollection.find(message._id); + if (!threadRecord._hasPendingUpdate) { + const updateThread = threadRecord.prepareUpdate(protectedFunction((t) => { + Object.assign(t, message); + })); + this._threadsBatch[message._id] = updateThread; + } + } catch { const createThread = threadsCollection.prepareCreate(protectedFunction((t) => { t._raw = sanitizedRaw({ id: message._id }, threadsCollection.schema); t.subscription.id = this.rid; @@ -205,19 +202,16 @@ export default class RoomSubscription { // Create or update thread message if (message.tmid) { try { - threadMessageRecord = await threadMessagesCollection.find(message._id); - } catch (error) { - // Do nothing - } - - if (threadMessageRecord) { - const updateThreadMessage = threadMessageRecord.prepareUpdate(protectedFunction((tm) => { - Object.assign(tm, message); - tm.rid = message.tmid; - delete tm.tmid; - })); - this._threadMessagesBatch[message._id] = updateThreadMessage; - } else { + const threadMessageRecord = await threadMessagesCollection.find(message._id); + if (!threadMessageRecord._hasPendingUpdate) { + const updateThreadMessage = threadMessageRecord.prepareUpdate(protectedFunction((tm) => { + Object.assign(tm, message); + tm.rid = message.tmid; + delete tm.tmid; + })); + this._threadMessagesBatch[message._id] = updateThreadMessage; + } + } catch { const createThreadMessage = threadMessagesCollection.prepareCreate(protectedFunction((tm) => { tm._raw = sanitizedRaw({ id: message._id }, threadMessagesCollection.schema); Object.assign(tm, message); diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index d80f3dd84..d6ea0f7b6 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -57,6 +57,7 @@ const createOrUpdateSubscription = async(subscription, room) => { lastOpen: s.lastOpen, description: s.description, announcement: s.announcement, + bannerClosed: s.bannerClosed, topic: s.topic, blocked: s.blocked, blocker: s.blocker, @@ -74,7 +75,12 @@ const createOrUpdateSubscription = async(subscription, room) => { lastMessage: s.lastMessage, roles: s.roles, usernames: s.usernames, - uids: s.uids + uids: s.uids, + visitor: s.visitor, + departmentId: s.departmentId, + servedBy: s.servedBy, + livechatData: s.livechatData, + tags: s.tags }; } catch (error) { try { @@ -97,10 +103,15 @@ const createOrUpdateSubscription = async(subscription, room) => { // We have to create a plain obj so we can manipulate it on `merge` // Can we do it in a better way? room = { - customFields: r.customFields, - broadcast: r.broadcast, + v: r.v, + ro: r.ro, + tags: r.tags, + servedBy: r.servedBy, encrypted: r.encrypted, - ro: r.ro + broadcast: r.broadcast, + customFields: r.customFields, + departmentId: r.departmentId, + livechatData: r.livechatData }; } catch (error) { // Do nothing @@ -121,6 +132,11 @@ const createOrUpdateSubscription = async(subscription, room) => { try { const update = sub.prepareUpdate((s) => { Object.assign(s, tmp); + if (subscription.announcement) { + if (subscription.announcement !== sub.announcement) { + s.bannerClosed = false; + } + } }); batch.push(update); } catch (e) { @@ -141,7 +157,8 @@ const createOrUpdateSubscription = async(subscription, room) => { } } - if (tmp.lastMessage) { + const { rooms } = store.getState().room; + if (tmp.lastMessage && !rooms.includes(tmp.rid)) { const lastMessage = buildMessage(tmp.lastMessage); const messagesCollection = db.collections.get('messages'); let messageRecord; diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 9147eb97b..ecd7ae939 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1,9 +1,9 @@ -import { AsyncStorage, InteractionManager } from 'react-native'; +import { InteractionManager } from 'react-native'; import semver from 'semver'; import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk'; import RNUserDefaults from 'rn-user-defaults'; import { Q } from '@nozbe/watermelondb'; -import * as FileSystem from 'expo-file-system'; +import AsyncStorage from '@react-native-community/async-storage'; import reduxStore from './createStore'; import defaultSettings from '../constants/settings'; @@ -11,8 +11,7 @@ import messagesStatus from '../constants/messagesStatus'; import database from './database'; import log from '../utils/log'; import { isIOS, getBundleId } from '../utils/deviceInfo'; -import { extractHostname } from '../utils/server'; -import fetch, { BASIC_AUTH_KEY } from '../utils/fetch'; +import fetch from '../utils/fetch'; import { setUser, setLoginServices, loginRequest } from '../actions/login'; import { disconnect, connectSuccess, connectRequest } from '../actions/connect'; @@ -43,12 +42,14 @@ import sendMessage, { sendMessageCall } from './methods/sendMessage'; import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage'; import callJitsi from './methods/callJitsi'; +import logout, { removeServer } from './methods/logout'; import { getDeviceToken } from '../notifications/push'; -import { SERVERS, SERVER_URL } from '../constants/userDefaults'; import { setActiveUsers } from '../actions/activeUsers'; import I18n from '../i18n'; import { twoFactor } from '../utils/twoFactor'; +import { selectServerFailure } from '../actions/server'; +import { useSsl } from '../utils/url'; const TOKEN_KEY = 'reactnativemeteor_usertoken'; const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY'; @@ -86,10 +87,7 @@ const RocketChat = { } }, async getWebsocketInfo({ server }) { - // Use useSsl: false only if server url starts with http:// - const useSsl = !/http:\/\//.test(server); - - const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl }); + const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); try { await sdk.connect(); @@ -146,6 +144,10 @@ const RocketChat = { } return result; } catch (e) { + if (e.message === 'Aborted') { + reduxStore.dispatch(selectServerFailure()); + throw e; + } log(e); } return { @@ -159,6 +161,16 @@ const RocketChat = { stopListener(listener) { return listener && listener.stop(); }, + // Abort all requests and create a new AbortController + abort() { + if (this.controller) { + this.controller.abort(); + if (this.sdk) { + this.sdk.abort(); + } + } + this.controller = new AbortController(); + }, connect({ server, user, logoutOnError = false }) { return new Promise((resolve) => { if (!this.sdk || this.sdk.client.host !== server) { @@ -200,15 +212,13 @@ const RocketChat = { this.code = null; } - // Use useSsl: false only if server url starts with http:// - const useSsl = !/http:\/\//.test(server); - - this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl }); + this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); this.getSettings(); const sdkConnect = () => this.sdk.connect() .then(() => { - if (user && user.token) { + const { server: currentServer } = reduxStore.getState().server; + if (user && user.token && server === currentServer) { reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError)); } }) @@ -217,7 +227,9 @@ const RocketChat = { // when `connect` raises an error, we try again in 10 seconds this.connectTimeout = setTimeout(() => { - sdkConnect(); + if (this.sdk?.client?.host === server) { + sdkConnect(); + } }, 10000); }); @@ -270,10 +282,7 @@ const RocketChat = { this.shareSDK = null; } - // Use useSsl: false only if server url starts with http:// - const useSsl = !/http:\/\//.test(server); - - this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl }); + this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); // set Server const serversDB = database.servers; @@ -306,7 +315,7 @@ const RocketChat = { } database.share = null; - reduxStore.dispatch(shareSetUser(null)); + reduxStore.dispatch(shareSetUser({})); }, updateJitsiTimeout(rid) { @@ -369,112 +378,35 @@ const RocketChat = { }; } - try { - return this.loginTOTP(params); - } catch (error) { - throw error; - } + return this.loginTOTP(params); }, async loginOAuthOrSso(params) { - try { - const result = await this.login(params); - reduxStore.dispatch(loginRequest({ resume: result.token })); - } catch (error) { - throw error; - } + const result = await this.login(params); + reduxStore.dispatch(loginRequest({ resume: result.token })); }, async login(params) { - try { - const sdk = this.shareSDK || this.sdk; - // RC 0.64.0 - await sdk.login(params); - const { result } = sdk.currentLogin; - const user = { - id: result.userId, - token: result.authToken, - username: result.me.username, - name: result.me.name, - language: result.me.language, - status: result.me.status, - statusText: result.me.statusText, - customFields: result.me.customFields, - emails: result.me.emails, - roles: result.me.roles - }; - return user; - } catch (e) { - throw e; - } - }, - async logout({ server }) { - if (this.roomsSub) { - this.roomsSub.stop(); - this.roomsSub = null; - } - - if (this.activeUsersSubTimeout) { - clearTimeout(this.activeUsersSubTimeout); - this.activeUsersSubTimeout = false; - } - - try { - await this.removePushToken(); - } catch (error) { - console.log('logout -> removePushToken -> catch -> error', error); - } - try { - // RC 0.60.0 - await this.sdk.logout(); - } catch (error) { - console.log('​logout -> api logout -> catch -> error', error); - } - this.sdk = null; - - try { - const servers = await RNUserDefaults.objectForKey(SERVERS); - await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server)); - // clear certificate for server - SSL Pinning - const certificate = await RNUserDefaults.objectForKey(extractHostname(server)); - if (certificate && certificate.path) { - await RNUserDefaults.clear(extractHostname(server)); - await FileSystem.deleteAsync(certificate.path); - } - } catch (error) { - console.log('logout_rn_user_defaults', error); - } - - const userId = await RNUserDefaults.get(`${ TOKEN_KEY }-${ server }`); - - try { - const serversDB = database.servers; - await serversDB.action(async() => { - const usersCollection = serversDB.collections.get('users'); - const userRecord = await usersCollection.find(userId); - const serverCollection = serversDB.collections.get('servers'); - const serverRecord = await serverCollection.find(server); - await serversDB.batch( - userRecord.prepareDestroyPermanently(), - serverRecord.prepareDestroyPermanently() - ); - }); - } catch (error) { - // Do nothing - } - - await RNUserDefaults.clear('currentServer'); - await RNUserDefaults.clear(TOKEN_KEY); - await RNUserDefaults.clear(`${ TOKEN_KEY }-${ server }`); - await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`); - - try { - const db = database.active; - await db.action(() => db.unsafeResetDatabase()); - } catch (error) { - console.log(error); - } + const sdk = this.shareSDK || this.sdk; + // RC 0.64.0 + await sdk.login(params); + const { result } = sdk.currentLogin; + const user = { + id: result.userId, + token: result.authToken, + username: result.me.username, + name: result.me.name, + language: result.me.language, + status: result.me.status, + statusText: result.me.statusText, + customFields: result.me.customFields, + emails: result.me.emails, + roles: result.me.roles + }; + return user; }, + logout, + removeServer, async clearCache({ server }) { try { const serversDB = database.servers; @@ -573,9 +505,9 @@ const RocketChat = { ).fetch(); if (filterUsers && !filterRooms) { - data = data.filter(item => item.t === 'd'); + data = data.filter(item => item.t === 'd' && !RocketChat.isGroupChat(item)); } else if (!filterUsers && filterRooms) { - data = data.filter(item => item.t !== 'd'); + data = data.filter(item => item.t !== 'd' || RocketChat.isGroupChat(item)); } data = data.slice(0, 7); @@ -824,6 +756,59 @@ const RocketChat = { return this.sdk.get('rooms.info', { roomId }); }, + getVisitorInfo(visitorId) { + // RC 2.3.0 + return this.sdk.get('livechat/visitors.info', { visitorId }); + }, + closeLivechat(rid, comment) { + // RC 0.29.0 + return this.methodCall('livechat:closeRoom', rid, comment, { clientAction: true }); + }, + editLivechat(userData, roomData) { + // RC 0.55.0 + return this.methodCall('livechat:saveInfo', userData, roomData); + }, + returnLivechat(rid) { + // RC 0.72.0 + return this.methodCall('livechat:returnAsInquiry', rid); + }, + forwardLivechat(transferData) { + // RC 0.36.0 + return this.methodCall('livechat:transfer', transferData); + }, + getPagesLivechat(rid, offset) { + // RC 2.3.0 + return this.sdk.get(`livechat/visitors.pagesVisited/${ rid }?count=50&offset=${ offset }`); + }, + getDepartmentInfo(departmentId) { + // RC 2.2.0 + return this.sdk.get(`livechat/department/${ departmentId }?includeAgents=false`); + }, + getDepartments() { + // RC 2.2.0 + return this.sdk.get('livechat/department'); + }, + usersAutoComplete(selector) { + // RC 2.4.0 + return this.sdk.get('users.autocomplete', { selector }); + }, + getRoutingConfig() { + // RC 2.0.0 + return this.methodCall('livechat:getRoutingConfig'); + }, + getTagsList() { + // RC 2.0.0 + return this.methodCall('livechat:getTagsList'); + }, + getAgentDepartments(uid) { + // RC 2.4.0 + return this.sdk.get(`livechat/agents/${ uid }/departments`); + }, + getCustomFields() { + // RC 2.2.0 + return this.sdk.get('livechat/custom-fields'); + }, + getUidDirectMessage(room) { const { id: userId } = reduxStore.getState().login.user; @@ -909,7 +894,7 @@ const RocketChat = { methodCall(...args) { return new Promise(async(resolve, reject) => { try { - const result = await this.sdk.methodCall(...args, this.code); + const result = await this.sdk.methodCall(...args, this.code || ''); return resolve(result); } catch (e) { if (e.error && (e.error === 'totp-required' || e.error === 'totp-invalid')) { @@ -977,7 +962,7 @@ const RocketChat = { const shareUser = reduxStore.getState().share.user; const loginUser = reduxStore.getState().login.user; // get user roles on the server from redux - const userRoles = (shareUser.roles || loginUser.roles) || []; + const userRoles = (shareUser?.roles || loginUser?.roles) || []; // merge both roles const mergedRoles = [...new Set([...roomRoles, ...userRoles])]; @@ -1226,16 +1211,19 @@ const RocketChat = { return this.methodCall('autoTranslate.translateMessage', message, targetLanguage); }, getRoomTitle(room) { - const { UI_Use_Real_Name: useRealName } = reduxStore.getState().settings; + const { UI_Use_Real_Name: useRealName, UI_Allow_room_names_with_special_chars: allowSpecialChars } = reduxStore.getState().settings; const { username } = reduxStore.getState().login.user; if (RocketChat.isGroupChat(room) && !(room.name && room.name.length)) { return room.usernames.filter(u => u !== username).sort((u1, u2) => u1.localeCompare(u2)).join(', '); } + if (allowSpecialChars && room.t !== 'd') { + return room.fname || room.name; + } return ((room.prid || useRealName) && room.fname) || room.name; }, getRoomAvatar(room) { if (RocketChat.isGroupChat(room)) { - return room.uids.length + room.usernames.join(); + return room.uids?.length + room.usernames?.join(); } return room.prid ? room.fname : room.name; }, diff --git a/app/lib/selection.json b/app/lib/selection.json index 0c25def09..d9c9d6607 100755 --- a/app/lib/selection.json +++ b/app/lib/selection.json @@ -1 +1 @@ -{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M517.642 847.16c230.298 0.768 301.875-37.171 323.174-61.645-5.222-3.123-11.674-6.502-16.691-9.114-32.41-17.101-108.544-57.19-69.837-140.442 3.942-9.472 9.062-20.48 14.234-31.642 14.899-31.898 28.979-62.106 28.979-92.314 0-184.832-150.682-335.206-335.872-335.206s-335.872 150.374-335.872 335.206c0 194.202 160.87 335.155 382.515 335.155h9.37zM858.941 710.61c26.47 13.926 81.459 42.906 56.218 96.461-47.77 101.427-241.050 114.534-387.328 114.534h-10.547l-9.011-0.051c-264.806 0-457.062-172.237-457.062-409.549 0-225.894 184.115-409.6 410.419-409.6s410.47 183.706 410.47 409.6c0 46.694-19.098 87.654-35.994 123.75-4.71 10.138-9.37 20.173-13.517 30.054-7.27 15.77-9.472 20.582 36.352 44.8zM288.922 439.731v0c0-20.543 16.654-37.197 37.197-37.197h252.211c20.543 0 37.197 16.654 37.197 37.197v0c0 20.543-16.654 37.197-37.197 37.197h-252.211c-20.543 0-37.197-16.654-37.197-37.197zM288.922 563.712v0c0-20.557 16.665-37.222 37.222-37.222h293.53c20.557 0 37.222 16.665 37.222 37.222v0c0 20.557-16.665 37.222-37.222 37.222h-293.53c-20.557 0-37.222-16.665-37.222-37.222z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["thread"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":125,"id":116,"name":"thread","prevSize":32,"code":59764},"setIdx":3,"setId":5,"iconIdx":0},{"icon":{"paths":["M511.152 360.583c22.304 0 40.392 18.051 40.392 40.327v233.944c0 22.275-18.088 40.327-40.392 40.327s-40.392-18.051-40.392-40.327v-233.944c0-22.275 18.088-40.327 40.392-40.327zM49.119 863.747l413.955-740.751c14.588-26.104 48.090-35.845 75.032-21.957 9.656 4.977 17.597 12.61 22.82 21.957l413.955 740.751c16.58 29.668-5.594 65.533-40.020 65.533h-845.723c-34.426 0-56.599-35.865-40.020-65.533zM510.258 703.736c24.735 0 43.707 19.504 43.707 44.561 0 25.225-18.915 44.785-43.707 44.785s-43.707-19.56-43.707-44.785c0-25.056 18.972-44.561 43.707-44.561zM140.057 855.673h743.887l-371.943-665.573-371.943 665.573z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["warning"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":9,"id":115,"name":"warning","prevSize":32,"code":59651},"setIdx":3,"setId":5,"iconIdx":1},{"icon":{"paths":["M192 363.273v297.454h86.142l10.572 21.377c1.038 2.1 3.898 7.013 8.633 13.789 8.247 11.802 18.498 23.692 30.766 34.773 26.202 23.666 57.348 40.098 94.287 46.786v-531.14c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073zM115.2 686.327v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v614.4c0 21.208-17.192 38.4-38.4 38.4-74.44 0-135.902-26.351-184.163-69.939-19.215-17.354-33.789-34.802-44.168-50.134h-66.069c-28.277 0-51.2-22.923-51.2-51.2zM671.024 687.393c-18.153-10.207-25.682-32.464-17.452-51.595 0.406-0.944 0.785-1.846 1.136-2.706 15.543-37.983 23.692-78.92 23.692-121.092 0-38.196-6.685-75.379-19.494-110.272-1.369-3.729-3.133-8.048-5.292-12.956-8.479-19.278-0.949-41.843 17.409-52.165 17.486-9.832 39.632-3.626 49.463 13.86 0.497 0.884 0.957 1.788 1.378 2.71 2.978 6.521 5.383 12.174 7.216 16.96 17.148 44.781 26.119 92.693 26.119 141.862 0 53.649-10.68 105.8-31.008 154.020-0.588 1.395-1.239 2.889-1.953 4.481l0.001 0.001c-8.25 18.402-29.857 26.632-48.259 18.382-1.008-0.452-1.995-0.949-2.958-1.491zM798.466 787.618c-18.347-10.316-25.51-33.12-16.356-52.074 1.158-2.395 2.188-4.585 3.090-6.569 30.65-67.393 46.8-140.98 46.8-216.975 0-77.121-16.632-151.762-48.171-219.966-0.535-1.158-1.118-2.392-1.747-3.702l0.001-0c-9.088-18.928-1.919-41.659 16.383-51.949 17.881-10.054 40.527-3.708 50.58 14.173 0.35 0.622 0.681 1.253 0.994 1.894 1.056 2.156 2.006 4.143 2.852 5.961 36.594 78.608 55.908 164.728 55.908 253.59 0 87.033-18.527 171.436-53.671 248.728-1.446 3.18-3.177 6.82-5.192 10.92l0.001 0c-9.039 18.386-31.271 25.964-49.657 16.926-0.614-0.302-1.219-0.62-1.815-0.955z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["volume"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":10,"id":114,"name":"volume","prevSize":32,"code":59652},"setIdx":3,"setId":5,"iconIdx":2},{"icon":{"paths":["M226.167 660.727l-81.802 71.829c-17.249-8.236-29.166-25.841-29.166-46.229v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v216.18l-76.8 67.437v-242.104c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073v297.454h34.167zM355.626 751.596c19.822 12.498 41.994 21.37 66.774 25.856 0-20.591 0-36.035 0-46.33 0-8.481 0-21.202 0-38.163l76.8-67.442c0 26.928 0 47.125 0 60.589 0 29.577 0 73.941 0 133.095 0 21.208-17.192 38.4-38.4 38.4-64.739 0-119.664-19.931-164.625-53.797l59.451-52.207zM675.736 470.492l66.722-58.592c8.406 32.339 12.742 65.916 12.742 100.1 0 53.649-10.68 105.8-31.008 154.020-0.588 1.395-1.239 2.889-1.953 4.481l0.001 0.001c-8.25 18.402-29.857 26.632-48.259 18.382-1.008-0.452-1.995-0.949-2.958-1.491-18.153-10.207-25.682-32.464-17.452-51.595 0.406-0.944 0.785-1.846 1.136-2.706 15.543-37.983 23.692-78.92 23.692-121.092 0-13.993-0.897-27.85-2.664-41.508zM807.938 354.398l62.315-54.722c25.313 67.093 38.546 138.769 38.546 212.324 0 87.033-18.527 171.436-53.671 248.728-1.446 3.18-3.177 6.82-5.192 10.92l0.001 0c-9.039 18.386-31.271 25.964-49.657 16.926-0.614-0.302-1.219-0.62-1.815-0.955-18.347-10.316-25.51-33.12-16.356-52.074 1.158-2.395 2.188-4.585 3.090-6.569 30.65-67.393 46.8-140.98 46.8-216.975 0-54.218-8.22-107.21-24.062-157.602zM946.503 130.461c14.061 14.993 13.306 38.546-1.687 52.607-0.296 0.278-0.597 0.551-0.902 0.819l-811.178 712.336c-16.116 14.152-40.553 12.962-55.218-2.688-14.067-15.013-13.301-38.587 1.712-52.654 0.293-0.274 0.59-0.544 0.892-0.809l811.176-712.287c16.11-14.146 40.538-12.962 55.205 2.676z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["volume-mute"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":11,"id":113,"name":"volume-mute","prevSize":32,"code":59653},"setIdx":3,"setId":5,"iconIdx":3},{"icon":{"paths":["M846.779 659.382l0.202-147.149c0.019-14.138-11.426-25.616-25.565-25.635-2.659-0.004-5.303 0.407-7.836 1.218l-140.298 44.887v105.968l140.059 45.047c13.459 4.329 27.88-3.073 32.209-16.532 0.811-2.522 1.226-5.154 1.229-7.803zM598.819 719.874v-268.278c0-14.138-11.462-25.6-25.6-25.6h-370.756c-14.138 0-25.6 11.462-25.6 25.6v268.278c0 14.138 11.462 25.6 25.6 25.6h370.756c14.138 0 25.6-11.462 25.6-25.6zM892.302 415.085c18.566 13.369 29.189 35.29 29.189 60.209v220.784c0 24.919-10.673 46.791-29.189 60.16-11.964 8.601-26.261 12.976-41.352 12.976-8.389 0-16.978-1.327-25.516-4.079l-152.153-48.954v51.819c0 28.277-22.923 51.2-51.2 51.2h-468.482c-28.277 0-51.2-22.923-51.2-51.2v-364.529c0-28.277 22.923-51.2 51.2-51.2h468.482c28.277 0 51.2 22.923 51.2 51.2v51.672l152.202-48.905c23.977-7.717 48.302-4.473 66.818 8.847zM226.5 241.663v0c0-20.359 16.504-36.863 36.863-36.863h248.947c20.359 0 36.863 16.504 36.863 36.863v0c0 20.359-16.504 36.863-36.863 36.863h-248.947c-20.359 0-36.863-16.504-36.863-36.863z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["video"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":12,"id":112,"name":"video","prevSize":32,"code":59654},"setIdx":3,"setId":5,"iconIdx":4},{"icon":{"paths":["M218.87 797.637c9.606 3.608 21.53 7.28 35.985 10.849 59.123 14.598 143.466 23.514 257.146 23.514s198.023-8.916 257.146-23.514c14.454-3.569 26.378-7.241 35.985-10.849-6.496-56.39-38.135-87.347-99.936-113.172-8.559-3.577-17.471-6.998-29.283-11.314 2.748 1.004-22.903-8.286-29.518-10.774-60.736-22.842-89.594-44.548-89.484-88.671-0.075-1.028-0.075-1.028-0.204-3.673-0.731-17.527 0.562-37.647 5.737-57.807 5.146-20.046 13.648-37.544 27.874-52.383 33.963-33.045 49.684-67.214 49.684-118.501 0-83.443-58.409-149.344-128-149.344s-128 65.901-128 149.344c0 51.244 15.357 84.513 49.491 119.13 13.437 13.696 22.254 30.903 27.646 50.494 5.659 20.562 7.040 41.089 6.179 59.081-0.163 2.919-0.163 2.919-0.116 0.759 0 47.022-28.858 68.728-89.594 91.57-6.615 2.488-32.266 11.777-29.518 10.774-11.812 4.315-20.724 7.737-29.283 11.314-61.801 25.825-93.44 56.782-99.936 113.172zM140.8 821.133c0-107.358 52.685-167.534 148.394-207.529 9.82-4.104 19.726-7.907 32.54-12.589-2.308 0.843 22.741-8.229 28.838-10.521 31.709-11.925 39.829-18.033 39.971-22.993-0.015 0.223-0.015 0.223 0.061-1.121 0.507-10.589-0.369-23.607-3.513-35.032-2.19-7.958-5.172-13.777-8.354-17.020-47.496-48.169-71.537-100.249-71.537-172.984 0-123.933 90.591-226.144 204.8-226.144s204.8 102.211 204.8 226.144c0 72.96-24.682 126.604-71.953 172.563-2.969 3.104-5.88 9.097-8.016 17.416-2.989 11.645-3.838 24.838-3.392 35.51 0.063 1.261 0.063 1.261 0.161 3.976 0 1.653 8.12 7.76 39.829 19.686 6.097 2.293 31.146 11.365 28.838 10.521 12.813 4.681 22.719 8.485 32.54 12.589 95.71 39.995 148.394 100.171 148.394 207.529v19.301l-15.489 11.516c-12.629 9.39-38.034 20.697-80.156 31.098-65.585 16.194-156.017 25.753-275.556 25.753s-209.97-9.559-275.556-25.753c-42.121-10.4-67.526-21.708-80.156-31.098l-15.489-11.516v-19.301z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":13,"id":111,"name":"user","prevSize":32,"code":59655},"setIdx":3,"setId":5,"iconIdx":5},{"icon":{"paths":["M839.68 812.373c0 11.469-7.1 21.19-17.094 25.231-11.414-68.704-54.668-111.138-124.191-140.138-11.196-4.588-47.732-18.022-51.555-19.333-13.053-4.97-21.245-8.738-25.832-11.851-0.164-7.591 0.492-16.876 2.567-24.904 1.365-5.352 3.058-8.793 4.205-9.994 40.305-39.103 61.658-85.415 61.658-147.838 0-106.441-78.316-194.533-177.439-194.533s-177.439 88.091-177.439 194.533c0 62.205 20.808 107.151 61.44 148.275 1.311 1.365 3.058 4.697 4.424 9.776 2.185 7.864 2.895 17.094 2.621 24.685-4.588 3.113-12.78 6.881-25.887 11.851-3.768 1.311-40.359 14.746-51.5 19.333-69.523 29-112.777 71.434-124.245 140.138-9.994-3.987-17.094-13.763-17.094-25.231v-600.747c0-15.073 12.288-27.307 27.307-27.307h600.747c15.073 0 27.307 12.233 27.307 27.307v600.747zM405.504 754.975c53.084-19.988 79.244-39.649 79.299-83.831 0.765-15.674-0.437-33.369-5.407-51.337-4.806-17.531-12.78-32.986-25.068-45.493-26.324-26.651-37.847-51.5-37.847-90.767 0-63.188 43.964-112.613 95.519-112.613 51.61 0 95.519 49.425 95.519 112.613 0 39.267-11.742 64.771-37.792 90.112-13.271 13.708-20.862 29.437-25.504 47.35-4.478 17.531-5.625 34.843-4.97 50.080 0.109 2.348 0.109 2.348 0.164 3.331-0.109 40.905 26.051 60.566 79.080 80.555 4.314 1.529 38.939 14.199 48.387 18.132 40.25 16.766 63.242 35.717 72.363 66.574h-454.492c9.175-30.857 32.113-49.807 72.417-66.574 9.448-3.932 43.964-16.548 48.333-18.132zM812.373 102.4h-600.747c-60.293 0-109.227 48.934-109.227 109.227v600.747c0 60.348 48.934 109.227 109.227 109.227h600.747c60.293 0 109.227-48.879 109.227-109.227v-600.747c0-60.293-48.934-109.227-109.227-109.227z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-rounded"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":14,"id":110,"name":"user-rounded","prevSize":32,"code":59656},"setIdx":3,"setId":5,"iconIdx":6},{"icon":{"paths":["M331.532 803.498c38.087 9.46 92.556 15.252 166.070 15.252s127.983-5.792 166.070-15.252c1.811-0.45 3.786-0.977 5.925-1.581l-0.001-0.003c8.162-2.306 12.908-10.792 10.602-18.954-0.067-0.237-0.14-0.472-0.218-0.706-3.314-9.883-6.801-17.137-10.462-21.764-10.418-13.167-26.15-23.175-47.822-32.286-8.815-3.705-44.918-16.974-38.047-14.375-40.557-15.344-60.132-30.155-60.053-60.463-0.052-0.716-0.052-0.716-0.138-2.494-0.485-11.695 0.371-25.086 3.81-38.562 3.453-13.533 9.183-25.394 18.894-35.583 21.483-21.027 31.345-42.589 31.345-75.256 0-52.915-36.621-94.48-79.904-94.48s-79.904 41.565-79.904 94.48c0 32.647 9.636 53.647 31.27 75.718 9.119 9.35 15.063 21.018 18.683 34.251 3.765 13.763 4.68 27.441 4.108 39.46-0.072 32.774-19.646 47.586-60.202 62.929 6.863-2.597-29.235 10.67-38.049 14.375-22.603 9.502-38.745 19.98-49.136 34-3.225 4.352-6.321 11.060-9.289 20.124l0 0c-2.639 8.062 1.757 16.737 9.819 19.376 0.197 0.064 0.395 0.125 0.595 0.181 2.181 0.617 4.192 1.155 6.034 1.612zM279.485 265.898h79.904c17.891 0 32.394 14.503 32.394 32.394v0c0 17.891-14.503 32.394-32.394 32.394h-79.904v79.904c0 17.891-14.503 32.394-32.394 32.394v0c-17.891 0-32.394-14.503-32.394-32.394v-79.904h-79.904c-17.891 0-32.394-14.503-32.394-32.394v0c0-17.891 14.503-32.394 32.394-32.394h79.904v-79.904c0-17.891 14.503-32.394 32.394-32.394v0c17.891 0 32.394 14.503 32.394 32.394v79.904zM413.493 626.257c-1.3-4.751-3.012-8.113-4.672-9.815-31.342-31.976-47.271-66.689-47.271-114.972 0-82.454 60.066-150.629 136.053-150.629s136.053 68.174 136.053 150.629c0 48.437-16.356 84.197-47.509 114.663-1.524 1.603-3.201 5.075-4.473 10.059-1.859 7.285-2.393 15.64-2.115 22.352 0.038 0.772 0.038 0.772 0.107 2.714 0-0.642 4.42 2.703 23.851 10.054-8.126-3.074 29.733 10.84 39.938 15.129 63.15 26.546 98.179 66.795 98.179 138.222v14.061l-11.26 8.421c-8.556 6.399-25.434 13.956-53.167 20.845-42.837 10.64-101.782 16.908-179.606 16.908s-136.769-6.268-179.606-16.908c-27.733-6.888-44.611-14.445-53.167-20.845l-11.26-8.421v-14.061c0-71.428 35.029-111.676 98.179-138.222 10.204-4.29 48.064-18.204 39.938-15.129 19.162-7.249 23.726-10.602 23.948-12.384 0.355-7.334-0.195-15.555-2.142-22.672zM843.638 623.533c1.973-0.49 4.14-1.072 6.5-1.745l0.001 0.003c8.159-2.327 12.888-10.827 10.561-18.986-0.014-0.050-0.029-0.101-0.044-0.151-1.569-5.295-3.144-9.409-4.725-12.341-9.83-18.223-27.528-30.833-54.27-42.074-8.814-3.705-44.912-16.972-38.049-14.375-40.556-15.344-60.13-30.155-60.202-62.929-0.572-12.019 0.343-25.697 4.108-39.46 3.62-13.233 9.564-24.901 18.683-34.251 21.634-22.071 31.27-43.071 31.27-75.718 0-52.915-36.621-94.48-79.904-94.48-26.417 0-50.352 15.483-64.995 39.579-6.233 10.257-11.202 23.644-14.91 40.161h-56.149c2.611-17.627 6.283-32.54 11.016-44.737 20.7-53.351 68.455-91.152 125.038-91.152 75.987 0 136.053 68.174 136.053 150.629 0 48.284-15.929 82.996-47.271 114.972-1.66 1.702-3.372 5.064-4.672 9.815-1.947 7.117-2.497 15.338-2.142 22.672 0.222 1.781 4.786 5.134 23.948 12.384-8.126-3.074 29.733 10.84 39.938 15.129 63.15 26.546 98.179 66.795 98.179 138.222v0c0 8.85-4.173 17.182-11.26 22.482-8.556 6.399-25.434 13.956-53.167 20.845-18.942 4.705-41.378 8.602-67.626 11.477-11.504 1.26-25.438 2.685-41.802 4.273v-56.134c13.095-1.465 24.991-2.783 35.688-3.954 23.822-2.609 43.786-6.077 60.204-10.155z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-plus"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":15,"id":109,"name":"user-plus","prevSize":32,"code":59657},"setIdx":3,"setId":5,"iconIdx":7},{"icon":{"paths":["M806.86 327.115c99.002 16.485 174.473 102.538 174.473 206.218 0 115.464-93.602 209.067-209.067 209.067h-62.587c-21.208 0-38.4-17.192-38.4-38.4s17.192-38.4 38.4-38.4h62.587c73.049 0 132.267-59.218 132.267-132.267s-59.218-132.267-132.267-132.267h-38.4v-38.4c0-49.485-40.115-89.6-89.6-89.6-21.784 0-42.268 7.752-58.425 21.666l-30.468 26.237-24.808-31.643c-28.976-36.958-73.12-58.927-120.965-58.927-84.831 0-153.6 68.769-153.6 153.6 0 17.692 2.976 34.924 8.73 51.203l18.095 51.197h-65.226c-49.485 0-89.6 40.115-89.6 89.6s40.115 89.6 89.6 89.6h65.228c21.208 0 38.4 17.192 38.4 38.4s-17.192 38.4-38.4 38.4h-65.228c-91.9 0-166.4-74.5-166.4-166.4 0-79.358 55.552-145.741 129.89-162.382-1.255-9.763-1.89-19.651-1.89-29.618 0-127.246 103.154-230.4 230.4-230.4 59.366 0 115.051 22.632 157.077 61.83 23.581-12.446 50.090-19.163 77.59-19.163 79.698 0 146.31 56.030 162.594 130.849zM322.543 590.437c-14.996-14.996-14.996-39.31 0-54.306l144.815-144.815c19.995-19.995 52.413-19.995 72.408 0l144.815 144.815c14.996 14.996 14.996 39.31 0 54.306s-39.31 14.996-54.306 0l-85.965-85.965v312.679c0 21.208-17.192 38.4-38.4 38.4s-38.4-17.192-38.4-38.4v-317.376l-90.662 90.662c-14.996 14.996-39.31 14.996-54.306 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["upload"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":16,"id":108,"name":"upload","prevSize":32,"code":59658},"setIdx":3,"setId":5,"iconIdx":8},{"icon":{"paths":["M837.448 345.039c0.456 7.302 0.456 14.558 0.456 21.814 0 221.97-168.896 477.711-477.665 477.711-95.15 0-183.5-27.564-257.84-75.39 13.508 1.597 26.468 2.099 40.524 2.099 78.493 0 150.779-26.514 208.462-71.739-71.817-1.308-134.88-48.073-156.986-116.416 10.405 1.552 20.81 2.556 31.717 2.556 15.060 0 30.119-2.054 44.175-5.704-78.372-15.862-134.692-84.782-134.624-164.744v-2.099c22.361 12.458 48.373 20.262 75.892 21.312-46.789-31.135-74.885-83.625-74.842-139.827 0-31.215 8.306-59.782 22.818-84.745 85.298 104.986 211.14 168.844 346.235 175.696-2.693-12.648-4.085-25.539-4.153-38.471-0.036-44.539 17.641-87.264 49.134-118.758s74.219-49.171 118.758-49.134c46.476-0.11 90.907 19.098 122.668 53.028 37.601-7.249 73.657-20.976 106.559-40.57-12.513 38.803-38.75 71.709-73.792 92.548 33.335-3.805 65.914-12.573 96.656-26.012-22.981 33.478-51.447 62.838-84.197 86.844h0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["twitter"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":17,"id":107,"name":"twitter","prevSize":32,"code":59659},"setIdx":3,"setId":5,"iconIdx":9},{"icon":{"paths":["M622.040 176.873h147.318c41.655 0 75.442 33.116 75.442 74.428v65.873c0 25.466-18.591 46.589-42.942 50.537v479.479c0 41.057-33.95 74.409-75.255 74.409h-429.207c-41.529 0-75.255-33.3-75.255-74.409v-468.242c0-3.775 0.287-7.484 0.84-11.109-24.762-3.594-43.782-24.909-43.782-50.667v-65.873c0-41.17 33.806-74.428 75.442-74.428h147.318c17.178-43.592 59.97-74.473 110.040-74.473s92.862 30.881 110.040 74.473zM544.212 176.873c-8.972-6.68-20.125-10.639-32.212-10.639s-23.24 3.959-32.212 10.639h64.423zM737.445 368.374h-440.049c-5.855 0-10.842 4.899-10.842 10.575v468.242c0 5.802 4.834 10.575 10.842 10.575h429.207c5.855 0 10.842-4.899 10.842-10.575v-478.817zM297.397 304.54h482.991v-53.239c0-5.9-4.79-10.595-11.029-10.595h-514.715c-6.167 0-11.029 4.784-11.029 10.595v53.239h53.784zM426.116 474.764c17.787 0 32.206 14.419 32.206 32.206v212.2c0 17.787-14.419 32.206-32.206 32.206s-32.206-14.419-32.206-32.206v-212.2c0-17.787 14.419-32.206 32.206-32.206zM597.884 474.764c17.787 0 32.206 14.419 32.206 32.206v212.2c0 17.787-14.419 32.206-32.206 32.206s-32.206-14.419-32.206-32.206v-212.2c0-17.787 14.419-32.206 32.206-32.206z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["trash"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":18,"id":106,"name":"trash","prevSize":32,"code":59660},"setIdx":3,"setId":5,"iconIdx":10},{"icon":{"paths":["M792.869 153.6c71.013 0 128.731 57.020 128.731 127.174v462.452c0 70.154-57.718 127.174-128.731 127.174h-561.737c-71.013 0-128.731-57.020-128.731-127.174v-462.452c0-70.154 57.718-127.174 128.731-127.174h561.737zM792.869 801.032c32.253 0 58.514-25.944 58.514-57.806v-104.052h-444.709v161.858h386.194zM172.617 743.226c0 31.863 26.261 57.806 58.514 57.806h105.326v-161.858h-163.84v104.052zM231.131 222.968c-32.253 0-58.514 25.944-58.514 57.806v57.806h163.84v-115.613h-105.326zM851.383 280.774c0-31.863-26.261-57.806-58.514-57.806h-386.194v115.613h444.709v-57.806zM172.617 569.806h163.84v-161.858h-163.84v161.858zM406.674 569.806h444.709v-161.858h-444.709v161.858z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["th-list"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":19,"id":105,"name":"th-list","prevSize":32,"code":59661},"setIdx":3,"setId":5,"iconIdx":11},{"icon":{"paths":["M351.569 797.703c36.794 9.632 89.413 15.529 160.431 15.529s123.637-5.897 160.431-15.529c0 0 0 0 0 0v0c10.921-2.859 17.456-14.029 14.598-24.95-0.149-0.571-0.323-1.135-0.522-1.69-2.676-7.499-5.463-13.186-8.363-17.061-10.064-13.449-25.282-23.663-46.264-32.959-8.515-3.773-43.393-17.282-36.755-14.635-39.18-15.622-58.090-30.703-58.014-61.561-0.050-0.729-0.050-0.729-0.133-2.539-0.469-11.908 0.358-25.541 3.68-39.262 3.336-13.778 8.871-25.855 18.252-36.229 20.753-21.408 30.28-43.362 30.28-76.622 0-53.876-35.378-96.194-77.191-96.194s-77.191 42.319-77.191 96.194c0 33.24 9.309 54.621 30.208 77.092 8.809 9.52 14.551 21.4 18.049 34.873 3.638 14.012 4.521 27.939 3.969 40.176-0.070 33.369-18.979 48.449-58.158 64.071 6.63-2.644-28.242 10.864-36.757 14.636-24.379 10.801-40.978 22.842-50.75 39.757-1.444 2.499-2.87 5.803-4.278 9.913l-0.001-0c-3.741 10.919 2.078 22.803 12.997 26.544 0.488 0.167 0.982 0.316 1.481 0.447 0 0 0 0 0 0zM430.746 617.245c-1.256-4.837-2.91-8.26-4.514-9.993-30.278-32.556-45.666-67.899-45.666-117.059 0-83.951 58.027-153.363 131.434-153.363s131.434 69.412 131.434 153.363c0 49.316-15.8 85.725-45.896 116.745-1.472 1.632-3.093 5.167-4.321 10.241-1.796 7.417-2.312 15.924-2.043 22.758 0.037 0.786 0.037 0.786 0.103 2.763 0-0.653 4.27 2.752 23.042 10.237-7.85-3.13 28.724 11.036 38.581 15.404 61.006 27.028 94.846 68.007 94.846 140.731v0.465c0 8.746-4.009 17.010-10.878 22.425-8.266 6.515-24.57 14.21-51.362 21.223-41.382 10.833-98.326 17.215-173.507 17.215s-132.124-6.382-173.507-17.215c-26.791-7.013-43.096-14.708-51.362-21.223v0c-6.869-5.414-10.878-13.678-10.878-22.425v-0.465c0-72.724 33.84-113.703 94.846-140.731 9.858-4.367 46.432-18.534 38.581-15.404 18.511-7.381 22.92-10.795 23.135-12.609 0.343-7.468-0.188-15.837-2.069-23.084zM177.715 614.472c15.86 4.152 35.147 7.683 58.16 10.34 10.333 1.193 27.284 2.535 50.853 4.026l-2.452 57.11c-25.091-1.589-43.193-3.025-54.307-4.308-25.357-2.927-47.031-6.895-65.329-11.685-26.791-7.013-43.096-14.708-51.362-21.223v0c-6.869-5.414-10.878-13.678-10.878-22.425v-0.465c0-72.724 33.84-113.703 94.846-140.731 9.832-4.356 46.236-18.458 38.644-15.429 18.458-7.364 22.858-10.772 23.072-12.584 0.343-7.468-0.188-15.837-2.069-23.084-1.256-4.837-2.91-8.26-4.514-9.993-30.278-32.556-45.666-67.899-45.666-117.059 0-83.951 58.027-153.363 131.434-153.363 54.993 0 101.354 38.956 121.153 93.797 4.42 12.242 8.54 25.746 12.362 40.512h-54.242c-4.898-13.865-10.304-25.99-16.217-36.376-14.12-24.801-37.374-40.765-63.056-40.765-41.813 0-77.191 42.319-77.191 96.194 0 33.24 9.309 54.621 30.208 77.092 8.809 9.52 14.551 21.4 18.049 34.873 3.638 14.012 4.521 27.939 3.969 40.176-0.070 33.369-18.979 48.449-58.158 64.071 6.63-2.644-28.242 10.864-36.757 14.636-27.095 12.004-44.579 25.54-53.763 45.595-1.181 2.579-2.371 6.104-3.57 10.575l0.001 0c-2.15 8.018 2.459 16.294 10.409 18.686 2.317 0.697 4.441 1.299 6.373 1.804zM846.285 614.472c1.918-0.502 4.026-1.099 6.324-1.79l0.001 0.002c7.966-2.396 12.578-10.697 10.404-18.727-1.339-4.944-2.674-8.809-4.004-11.593-9.309-19.484-26.653-32.758-53.275-44.553-8.515-3.772-43.387-17.28-36.757-14.636-39.179-15.622-58.089-30.703-58.158-64.071-0.552-12.237 0.331-26.164 3.969-40.176 3.497-13.473 9.24-25.353 18.049-34.873 20.899-22.472 30.208-43.852 30.208-77.092 0-53.876-35.378-96.194-77.191-96.194-25.224 0-48.105 15.4-62.292 39.447-6.22 10.544-11.207 23.109-14.96 37.694h-54.242c2.344-14.034 5.613-27.037 9.806-39.008 19.489-55.631 66.193-95.301 121.688-95.301 73.407 0 131.434 69.412 131.434 153.363 0 49.16-15.388 84.503-45.666 117.059-1.603 1.733-3.258 5.156-4.514 9.993-1.881 7.246-2.412 15.616-2.069 23.084 0.214 1.814 4.624 5.228 23.135 12.609-7.85-3.13 28.724 11.036 38.581 15.404 61.006 27.028 94.846 68.007 94.846 140.731v0.465c0 8.746-4.009 17.010-10.878 22.425-8.266 6.515-24.57 14.21-51.362 21.223-18.298 4.79-39.973 8.758-65.329 11.685-11.114 1.283-19.465 2.719-25.054 4.308v-57.11c40.965-6.809 66.734-11.598 77.308-14.366z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["team"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":20,"id":104,"name":"team","prevSize":32,"code":59662},"setIdx":3,"setId":5,"iconIdx":12},{"icon":{"paths":["M446.005 550.4h-190.005c-21.208 0-38.4-17.192-38.4-38.4v0c0-21.208 17.192-38.4 38.4-38.4h96.267c-5.026-13.916-7.479-29.497-7.479-46.85 0-83.525 68.25-139.1 170.625-139.1 74.737 0 133.658 34.233 157.596 87.145 1.273 2.813 2.497 6.146 3.673 9.997l-0.001 0c4.669 15.294-3.945 31.478-19.239 36.147-2.74 0.836-5.589 1.262-8.454 1.262v0c-19.004 0-36.432-10.569-45.215-27.422-0.851-1.631-1.703-3.076-2.558-4.334-16.494-24.267-47.195-38.444-86.777-38.444-57.525 0-95.875 27.625-95.875 69.875 0 22.951 11.611 39.18 38.604 51.725h310.834c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-106.987c21.256 21.227 30.874 48.315 30.874 83.050 0 89.7-69.55 145.925-180.7 145.925-74.662 0-131.736-27.637-159.919-74.254-3.838-6.348-7.406-15.091-10.705-26.229l0-0c-4.519-15.256 4.186-31.287 19.443-35.806 2.656-0.787 5.412-1.186 8.182-1.186v0c19.865 0 38.134 10.884 47.59 28.355 3.288 6.073 6.708 10.829 10.262 14.268 19.781 19.141 51.485 30.178 90.673 30.178 58.5 0 101.4-30.225 101.4-71.825 0-35.75-27.3-57.2-89.375-71.825l-60.45-14.625c-7.818-1.827-15.243-3.834-22.283-6.025z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["strike"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":21,"id":103,"name":"strike","prevSize":32,"code":59663},"setIdx":3,"setId":5,"iconIdx":13},{"icon":{"paths":["M512 702.505l136.962 75.711c7.424 4.104 16.77 1.412 20.874-6.012 1.667-3.015 2.271-6.503 1.717-9.904l-26.352-161.551 112.325-115.124c5.924-6.072 5.804-15.796-0.267-21.721-2.296-2.24-5.236-3.705-8.407-4.19l-154.529-23.61-68.418-145.764c-3.604-7.679-12.752-10.983-20.431-7.378-3.245 1.523-5.855 4.133-7.378 7.378l-68.418 145.764-154.529 23.61c-8.386 1.281-14.145 9.118-12.864 17.504 0.484 3.171 1.95 6.111 4.19 8.407l112.325 115.124-26.352 161.551c-1.366 8.372 4.314 16.267 12.687 17.632 3.4 0.555 6.889-0.050 9.904-1.717l136.962-75.711zM272.433 838.351l34.767-213.14-149.132-152.849c-15.798-16.192-15.479-42.124 0.713-57.922 6.123-5.974 13.962-9.881 22.418-11.173l204.228-31.203 89.495-190.668c9.612-20.478 34.004-29.287 54.482-19.675 8.653 4.062 15.613 11.022 19.675 19.675l89.495 190.668 204.228 31.203c22.362 3.417 37.72 24.314 34.304 46.676-1.292 8.456-5.199 16.295-11.173 22.418l-149.132 152.849 34.767 213.14c3.642 22.327-11.505 43.378-33.832 47.020-9.068 1.479-18.369-0.133-26.41-4.578l-179.325-99.129-179.325 99.129c-19.798 10.944-44.719 3.767-55.664-16.032-4.445-8.041-6.057-17.343-4.578-26.41z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":22,"id":102,"name":"star","prevSize":32,"code":59664},"setIdx":3,"setId":5,"iconIdx":14},{"icon":{"paths":["M626.397 453.766c-4.1 10.953-12.803 16.765-24.728 16.765h-16.695c-8.318 0-15.234-3.647-20.087-10.592-4.873-6.974-5.963-14.981-3.163-23.392l91.103-255.538c4.1-10.953 12.803-16.765 24.728-16.765h24.284c11.925 0 20.628 5.812 24.767 16.873l91.114 255.577c2.75 8.264 1.66 16.271-3.213 23.245-4.853 6.946-11.77 10.592-20.087 10.592h-18.213c-11.023 0-19.151-5.921-23.285-16.973l-21.129-62.089h-84.194l-21.203 62.296zM710.343 329.968l-20.701-63.451-21.925 63.451h42.626zM407.956 688.223c14.571-14.255 38.056-14.255 52.627 0 14.77 14.45 14.77 38.036 0 52.486l-121.639 119c-14.571 14.255-38.056 14.255-52.627 0l-121.639-119c-14.77-14.45-14.77-38.036 0-52.486 14.571-14.255 38.056-14.255 52.627 0l57.934 56.677v-554.366c0-20.481 16.817-36.934 37.392-36.934s37.392 16.453 37.392 36.934v554.366l57.934-56.677zM780.76 796.736c6.867 0 12.918 2.496 17.771 7.358 4.89 4.899 7.423 11.062 7.423 18.071v12.163c0 7.009-2.533 13.172-7.423 18.071-4.854 4.862-10.904 7.358-17.771 7.358h-182.127c-6.867 0-12.918-2.496-17.771-7.358-4.89-4.899-7.423-11.062-7.423-18.071v-12.163c0-5.468 1.416-10.434 4.347-14.834l133.209-190.841h-104.772c-6.867 0-12.918-2.496-17.771-7.358-4.89-4.899-7.423-11.062-7.423-18.071v-12.163c0-7.009 2.533-13.172 7.423-18.071 4.854-4.862 10.904-7.358 17.771-7.358h173.021c6.867 0 12.918 2.496 17.771 7.358 4.89 4.899 7.423 11.062 7.423 18.071v12.163c0 4.57-1.474 9-4.345 13.311l-77.398 110.982c-29.564 42.526-48.847 70.177-57.423 81.382h115.489z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":23,"id":101,"name":"sort","prevSize":32,"code":59665},"setIdx":3,"setId":5,"iconIdx":15},{"icon":{"paths":["M682.048 640l-170.048 170.048-170.048-170.048","M682.048 384l-170.048-170.048-170.048 170.048"],"attrs":[{"fill":"none","stroke":"rgb(203, 206, 209)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96},{"fill":"none","stroke":"rgb(158, 162, 168)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96}],"isMulticolor":false,"isMulticolor2":true,"grid":0,"tags":["sort-up"],"colorPermutations":{"2552552551291162451":[{"s":0},{"s":0}],"15816216812032062091":[{"s":1},{"s":0}]}},"attrs":[{"fill":"none","stroke":"rgb(203, 206, 209)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96},{"fill":"none","stroke":"rgb(158, 162, 168)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96}],"properties":{"order":24,"id":100,"name":"sort-up","prevSize":32,"code":59666},"setIdx":3,"setId":5,"iconIdx":16},{"icon":{"paths":["M682.048 384l-170.048-170.048-170.048 170.048","M682.048 640l-170.048 170.048-170.048-170.048"],"attrs":[{"fill":"none","stroke":"rgb(203, 206, 209)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96},{"fill":"none","stroke":"rgb(158, 162, 168)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96}],"isMulticolor":false,"isMulticolor2":true,"grid":0,"tags":["sort-down"],"colorPermutations":{"2552552551291162451":[{"s":0},{"s":0}],"15816216812032062091":[{"s":1},{"s":0}]}},"attrs":[{"fill":"none","stroke":"rgb(203, 206, 209)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96},{"fill":"none","stroke":"rgb(158, 162, 168)","strokeLinejoin":"miter","strokeLinecap":"butt","strokeMiterlimit":"4","strokeWidth":96}],"properties":{"order":25,"id":99,"name":"sort-down","prevSize":32,"code":59667},"setIdx":3,"setId":5,"iconIdx":17},{"icon":{"paths":["M133.908 323.584c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h462.113c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-462.113zM133.908 528.384c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h378.092c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-378.092zM133.908 733.184c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h294.072c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-294.072zM860.070 688.223c14.073-14.255 36.757-14.255 50.83 0 14.266 14.45 14.266 38.036 0 52.486l-117.485 119c-14.073 14.255-36.757 14.255-50.83 0l-117.485-119c-14.266-14.45-14.266-38.036 0-52.486 14.073-14.255 36.757-14.255 50.83 0l55.955 56.677v-554.366c0-20.481 16.243-36.934 36.115-36.934s36.115 16.453 36.115 36.934v554.366l55.955-56.677z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-amount-down"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":26,"id":98,"name":"sort1","prevSize":32,"code":59648},"setIdx":3,"setId":5,"iconIdx":18},{"icon":{"paths":["M645.693 921.605h-492.029c-28.312 0-51.264-22.923-51.264-51.2v-716.8c0-28.277 22.952-51.2 51.264-51.2l508.89-0.007c28.277-0.002 51.202 22.919 51.204 51.196 0 0.002 0 0.003 0 0.005l-0.007 102.4h-76.896v-68.85h-448.617v649.71h448.617v-68.861h76.896l0.008 102.405c0.001 28.277-22.922 51.201-51.199 51.201-0.002 0-0.004 0-0.005 0l-16.173-0.007c-0.229 0.004-0.458 0.005-0.688 0.005zM698.094 393.607c-0.115-0.109-0.229-0.219-0.343-0.329-16.377-15.9-16.233-41.541 0.324-57.27 16.802-15.962 43.85-15.967 60.658-0.010l147.034 139.589c0.146 0.139 0.291 0.278 0.435 0.418 20.699 20.11 20.504 52.527-0.435 72.407l-147.034 139.589c-16.808 15.957-43.856 15.952-60.658-0.010-0.115-0.109-0.229-0.219-0.342-0.329-16.367-15.91-16.205-41.551 0.361-57.27l81.61-77.433h-379.359c-23.554 0-42.649-18.338-42.649-40.96s19.095-40.96 42.649-40.96h379.359l-81.61-77.433z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sign-out"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":27,"id":97,"name":"sign-out","prevSize":32,"code":59668},"setIdx":3,"setId":5,"iconIdx":19},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":28,"id":96,"name":"shield","prevSize":32,"code":59669},"setIdx":3,"setId":5,"iconIdx":20},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050zM665.48 347.607c14.236-14.414 37.461-14.558 51.887-0.311 14.628 14.807 14.628 38.122 0.311 52.618l-239.682 242.678c-15.896 16.095-41.83 16.256-57.936 0.349l-0.348-0.349-102.257-103.535c-14.812-14.997-14.812-39.117 0-54.114 14.723-14.907 38.742-15.056 53.716-0.266l79.467 80.457 214.843-217.528z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-check"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":29,"id":95,"name":"shield-check","prevSize":32,"code":59670},"setIdx":3,"setId":5,"iconIdx":21},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050zM364.244 386.457c9.881 79.955 46.236 161.347 109.356 244.214v-285.735l-109.356 41.521zM484.179 760.879c-126.028-132.474-192.633-265.254-199.003-398.202-0.796-16.605 9.184-31.836 24.725-37.737l188.469-71.559c25.128-9.541 52.031 9.021 52.031 35.899v445.131c0 34.674-42.322 51.59-66.221 26.468z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-alt"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":30,"id":94,"name":"shield-alt","prevSize":32,"code":59671},"setIdx":3,"setId":5,"iconIdx":22},{"icon":{"paths":["M475.27 238.914l-74.979 72.125c-14.76 14.198-38.1 14.198-52.859 0v0c-14.041-13.507-14.474-35.839-0.968-49.88 0.316-0.329 0.639-0.651 0.968-0.968l128.539-123.647c19.822-19.068 51.168-19.068 70.99 0l128.539 123.647c14.041 13.507 14.474 35.839 0.968 49.88-0.316 0.329-0.639 0.651-0.968 0.968v0c-14.76 14.198-38.1 14.198-52.859 0l-73.91-71.097v409.378c0 20.286-16.445 36.73-36.73 36.73v0c-20.286 0-36.73-16.445-36.73-36.73v-410.406zM634.435 489.758v-70.665h107.965c28.277 0 51.2 22.923 51.2 51.2v400.107c0 28.277-22.923 51.2-51.2 51.2h-460.8c-28.277 0-51.2-22.923-51.2-51.2v-400.107c0-28.277 22.923-51.2 51.2-51.2h107.965v70.665h-85.704v361.177h416.278v-361.177h-85.704z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["share"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":31,"id":93,"name":"share","prevSize":32,"code":59672},"setIdx":3,"setId":5,"iconIdx":23},{"icon":{"paths":["M512.917 779.883l-64-232.448 317.542-261.018-253.542 493.466zM190.818 376.069l528.179-149.094-317.594 261.018-210.586-111.923zM913.199 116.536c-9.421-11.827-25.088-16.794-39.373-12.749l-795.034 224.512c-14.899 4.198-25.754 17.254-27.392 32.819-1.587 15.616 6.349 30.669 20.019 37.939l302.592 160.768 91.955 333.824c4.147 15.104 16.998 26.112 32.41 27.75 1.28 0.154 2.662 0.205 3.891 0.205 13.926 0 26.88-7.834 33.434-20.582l381.645-742.656c6.912-13.517 5.325-29.952-4.147-41.83z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":32,"id":92,"name":"send","prevSize":32,"code":59673},"setIdx":3,"setId":5,"iconIdx":24},{"icon":{"paths":["M19.721 504.267l312.885 327.111c43.945 45.945 122.505 15.269 122.505-49.156v-156.16c274.533 3.221 389.445 29.739 322.116 268.606-14.828 52.476 45.239 92.857 88.32 61.351 59.182-43.271 158.453-141.495 158.453-310.348 0-304.155-274.752-357.396-568.889-360.924v-156.718c0-64.48-78.606-95.051-122.507-49.156l-312.884 327.083c-26.295 27.502-26.295 70.809 0 98.311zM60.836 445.28l312.889-327.111c8.763-9.173 24.498-3.079 24.498 9.831v213.333c279.314 0 568.889 19.876 568.889 304.338 0 132.267-71.111 217.191-135.147 264.018 91.259-323.543-129.038-340.8-433.742-340.8v213.333c0 12.907-15.728 19.004-24.498 9.831l-312.889-327.111c-2.451-2.545-3.96-6.012-3.96-9.831s1.509-7.286 3.964-9.836l-0.004 0.004z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reply"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":33,"id":91,"name":"reply","prevSize":32,"code":59674},"setIdx":3,"setId":5,"iconIdx":25},{"icon":{"paths":["M670.939 599.542h199.461c28.277 0 51.2 22.923 51.2 51.2v197.729c0 19.389-15.718 35.107-35.107 35.107v0c-19.389 0-35.107-15.718-35.107-35.107v-101.060c-14.089 19.847-29.302 38.945-46.809 56.030-77.562 76.158-180.448 118.098-289.699 118.098-214.525 0-391.087-159.758-410.7-371.615-0.048-0.533-0.094-1.083-0.141-1.65l-0.001 0c-1.508-18.485 12.254-34.693 30.739-36.201 0.909-0.074 1.82-0.111 2.731-0.111l2.070-0.001c17.94 0 32.918 13.684 34.534 31.55 0 0 0 0 0 0 16.243 175.439 162.754 307.814 340.768 307.814 90.715 0 176.188-34.779 240.597-98.018 20.924-20.502 39.132-43.626 54.111-68.762 2.809-4.728 4.774-9.923 7.396-14.792h-146.043c-19.389 0-35.107-15.718-35.107-35.107v0c0-19.389 15.718-35.107 35.107-35.107zM388.214 389.291v0c0 19.389-15.718 35.107-35.107 35.107h-199.461c-28.277 0-51.2-22.923-51.2-51.2v-197.729c0-19.389 15.718-35.107 35.107-35.107v0c19.389 0 35.107 15.718 35.107 35.107v101.060c14.136-19.847 29.349-38.945 46.762-55.983 77.609-76.158 180.541-118.145 289.699-118.145 214.571 0 391.134 159.758 410.747 371.662 0.043 0.456 0.085 0.925 0.126 1.406l-0 0c1.593 18.502-12.114 34.792-30.616 36.385-0.959 0.083-1.922 0.124-2.884 0.124l-1.972-0c-17.914 0-32.891-13.621-34.586-31.455 0 0 0 0 0 0-16.196-175.533-162.754-307.908-340.815-307.908-90.669 0-176.095 34.826-240.55 98.064-20.877 20.455-39.085 43.579-54.111 68.762-2.809 4.728-4.821 9.877-7.396 14.745h146.043c19.389 0 35.107 15.718 35.107 35.107z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reload"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":34,"id":90,"name":"reload","prevSize":32,"code":59675},"setIdx":3,"setId":5,"iconIdx":26},{"icon":{"paths":["M414.476 394.968c0-67.325-54.578-121.902-121.905-121.902-67.326 0-121.905 54.579-121.905 121.905v48.762c0 40.396 32.747 73.143 73.143 73.143 13.021 0 24.908 7.408 30.643 19.098 19.758 40.269 18.374 88.36-2.635 144.554-2.325 6.22-5.803 14.311-10.433 24.274l-0.001-0c-2.384 5.129-0.158 11.219 4.971 13.603 3.207 1.49 6.955 1.221 9.916-0.713 14.199-9.273 25.268-17.8 33.208-25.581 63.043-61.782 104.997-167.862 104.997-297.143zM102.4 443.733v-48.762c0-105.029 85.143-190.171 190.171-190.171s190.171 85.141 190.171 190.168c0 241.96-133.814 424.232-307.2 424.232-27.553 0-43.751-30.962-28.041-53.596 56.52-81.429 78.704-142.61 71.172-182.688-66.107-11.859-116.274-69.662-116.274-139.182zM657.531 582.916c-66.107-11.859-116.274-69.662-116.274-139.182v-48.762c0-105.029 85.143-190.171 190.171-190.171s190.171 85.141 190.171 190.168c0 241.96-133.814 424.232-307.2 424.232-27.553 0-43.751-30.962-28.040-53.596 56.52-81.429 78.704-142.61 71.172-182.688zM853.333 394.968c0-67.325-54.578-121.902-121.905-121.902-67.326 0-121.905 54.579-121.905 121.905v48.762c0 40.396 32.747 73.143 73.143 73.143 13.021 0 24.908 7.408 30.643 19.098 19.37 39.477 18.42 86.472-1.418 141.249-2.531 6.989-6.461 16.233-11.79 27.732l-0.002-0.001c-2.379 5.132-0.146 11.221 4.986 13.6 3.2 1.483 6.938 1.215 9.894-0.709 13.847-9.013 24.662-17.295 32.446-24.846 63.549-61.65 105.908-168.128 105.908-298.031z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["quote"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":35,"id":89,"name":"quote","prevSize":32,"code":59676},"setIdx":3,"setId":5,"iconIdx":27},{"icon":{"paths":["M493.033 613.695l-371.535-215.269c-11.371-6.537-18.427-18.716-18.554-31.943-0.127-13.225 6.69-25.541 17.898-32.284l373.418-226.518c11.552-7.017 25.967-7.041 37.53-0.071l371.47 223.236c11.226 6.739 18.059 19.012 17.983 32.215-0.076 13.204-7.052 25.396-18.35 31.996l-373.37 218.564c-5.573 3.269-11.901 4.988-18.344 4.98-6.395-0.001-12.662-1.674-18.146-4.906zM212.267 365.373l298.85 173.185 300.875-176.139-298.831-179.553-300.894 182.507zM493.032 765.31l-371.339-215.205c-11.721-6.392-19.087-18.715-19.289-32.181-0.202-13.463 6.788-26.005 18.306-32.757 11.586-6.792 25.899-6.637 37.111 0.268l353.294 204.693 355.030-207.827c11.407-6.922 25.616-7.035 37.129-0.293 11.446 6.703 18.431 19.126 18.324 32.502-0.107 13.377-7.292 25.685-18.705 32.116l-373.362 218.559c-5.575 3.277-11.909 4.998-18.356 4.984-6.35-0.002-12.592-1.652-18.142-4.861zM493.032 916.74l-371.339-215.205c-11.721-6.392-19.087-18.715-19.289-32.181-0.202-13.463 6.788-26.005 18.306-32.757 11.586-6.792 25.899-6.637 37.111 0.268l353.294 204.693 355.308-207.989c17.57-9.903 39.689-3.594 49.625 14.077 9.872 17.558 4.114 39.933-13.155 50.41l-373.362 218.559c-5.575 3.277-11.909 4.998-18.356 4.984-6.35-0.002-12.592-1.652-18.142-4.861z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["queue"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":36,"id":88,"name":"queue","prevSize":32,"code":59677},"setIdx":3,"setId":5,"iconIdx":28},{"icon":{"paths":["M284.939 207.277v609.445c0 14.138 11.462 25.6 25.6 25.6h402.922c14.138 0 25.6-11.462 25.6-25.6v-425.488c0-13.675-5.471-26.782-15.192-36.399l-160.078-158.357c-9.585-9.482-22.524-14.801-36.008-14.801h-217.244c-14.138 0-25.6 11.462-25.6 25.6zM596.985 117.201l207.022 204.796c9.722 9.617 15.192 22.724 15.192 36.399v512.004c0 28.277-22.923 51.2-51.2 51.2h-512c-28.277 0-51.2-22.923-51.2-51.2v-716.8c0-28.277 22.923-51.2 51.2-51.2h304.978c13.483 0 26.422 5.319 36.008 14.801zM394.24 704v0c0-21.208 17.192-38.4 38.4-38.4h153.6c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-153.6c-21.208 0-38.4-17.192-38.4-38.4zM394.24 550.4v0c0-21.208 17.192-38.4 38.4-38.4h153.6c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-153.6c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["post"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":37,"id":87,"name":"post","prevSize":32,"code":59678},"setIdx":3,"setId":5,"iconIdx":29},{"icon":{"paths":["M512 99.84c199.506 0 360.96 161.463 360.96 360.96 0 110.932-49.907 212.466-133.648 280.482-3.995 3.245-9.055 7.041-15.179 11.39-2.828 2.009-6.132 3.241-9.585 3.574-10.909 1.054-20.607-6.936-21.661-17.845l-0.192-1.986c-1.682-17.418 5.219-34.565 18.497-45.964 9.782-8.395 17.379-15.474 22.781-21.224 52.758-56.15 82.665-130.011 82.665-208.427 0-170.366-140.407-308.090-311.038-304.573-164.381 3.387-297.226 138.223-298.235 302.672-0.493 80.258 30.341 155.851 84.922 212.711 5.234 5.452 12.594 12.206 22.071 20.249 12.387 10.512 19.356 26.062 18.959 42.304l-0.136 5.517c-0.093 3.817-1.342 7.515-3.581 10.607-6.113 8.444-17.914 10.332-26.354 4.217-17.094-12.377-29.857-22.522-38.307-30.459-72.13-67.748-113.9-161.843-113.9-263.245 0-199.506 161.464-360.96 360.96-360.96zM611.84 671.6c0 46.621-18.108 157.105-32.826 211.836-6.815 25.189-30.31 35.604-67.014 35.604s-60.199-10.416-67.014-35.609c-14.708-54.691-32.826-165.054-32.826-211.831 0-47.592 36.076-67.44 99.84-67.44s99.84 19.848 99.84 67.44zM565.76 671.6c0-14.129-23.565-22.134-53.76-22.13-30.191 0.004-53.76 8.013-53.76 22.13 0 34.235 11.511 111.604 24.478 171.428 0.583 2.664 0.583 2.664 1.252 5.622 2.92 12.814 14.315 21.905 27.457 21.905l1.166-0.003c13.14 0 24.537-9.098 27.45-21.919 0.486-2.15 0.486-2.15 0.92-4.125 13.049-59.747 24.797-138.352 24.797-172.908zM616.96 460.8c0 57.967-46.993 104.96-104.96 104.96s-104.96-46.993-104.96-104.96c0-57.967 46.993-104.96 104.96-104.96s104.96 46.993 104.96 104.96zM560.64 460.8c0-26.818-21.822-48.64-48.64-48.64s-48.64 21.822-48.64 48.64c0 26.818 21.822 48.64 48.64 48.64s48.64-21.822 48.64-48.64zM669.982 545.059c1.487-2.642 2.729-5.004 3.728-7.087 11.522-24.039 17.49-50.271 17.49-77.171 0-101.539-84.63-182.985-186.256-179.064-92.382 3.565-167.505 77.969-171.933 170.293-1.487 30.994 4.921 61.326 18.63 88.7 0.64 1.249 0.64 1.249 1.038 2.010 0.5 0.931 0.5 0.931 1.36 2.49 7.294 13.022 5.65 29.218-4.111 40.51-7.331 8.481-20.149 9.412-28.629 2.082-1.423-1.23-2.667-2.651-3.699-4.224-4.637-7.070-8.234-13.097-10.787-18.076-16.586-32.356-25.214-68.047-25.214-104.721 0-127.771 104.48-231.42 232.285-230.392 124.147 0.999 225.904 101.455 228.464 225.55 0.787 38.143-7.742 75.348-24.875 109-2.591 5.088-6.259 11.257-10.999 18.499-6.149 9.395-18.749 12.026-28.144 5.878-1.563-1.023-2.977-2.256-4.203-3.665-9.832-11.298-11.493-27.561-4.146-40.613z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["podcast"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":38,"id":86,"name":"podcast","prevSize":32,"code":59679},"setIdx":3,"setId":5,"iconIdx":30},{"icon":{"paths":["M471.93 193.643v278.288h-278.261c-22.13 0-40.070 17.94-40.070 40.070v0c0 22.13 17.94 40.070 40.070 40.070h278.261v278.288c0 22.115 17.928 40.043 40.043 40.043v0c22.115 0 40.043-17.928 40.043-40.043v-278.288h278.314c22.13 0 40.070-17.94 40.070-40.070v0c0-22.13-17.94-40.070-40.070-40.070h-278.314v-278.288c0-22.115-17.928-40.043-40.043-40.043v0c-22.115 0-40.043 17.928-40.043 40.043z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["plus"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":39,"id":85,"name":"plus","prevSize":32,"code":59680},"setIdx":3,"setId":5,"iconIdx":31},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM456.65 362.026c-4.893-3.071-10.553-4.7-16.33-4.7-16.966 0-30.72 13.754-30.72 30.72v247.902c0 5.777 1.629 11.437 4.7 16.33 9.019 14.371 27.98 18.709 42.35 9.69l197.503-123.951c3.918-2.459 7.231-5.772 9.69-9.69 9.019-14.371 4.68-33.331-9.69-42.35l-197.503-123.951z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":40,"id":84,"name":"play","prevSize":32,"code":59681},"setIdx":3,"setId":5,"iconIdx":32},{"icon":{"paths":["M912.8 429.4l-704-416.2c-57.2-33.8-144.8-1-144.8 82.6v832.2c0 75 81.4 120.2 144.8 82.6l704-416c62.8-37 63-128.2 0-165.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play-solid"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":41,"id":83,"name":"play-solid","prevSize":32,"code":59682},"setIdx":3,"setId":5,"iconIdx":33},{"icon":{"paths":["M482.814 637.831l-224.859-224.911-32.719 32.719c-7.201 7.201-16.751 11.167-26.927 11.167 0 0 0 0 0 0v0c-10.643 0-19.44-8.301-20.056-18.926-0.022-0.403-0.034-0.791-0.034-1.164 0-10.124 3.966-19.517 10.906-26.561l221.049-220.997c0 0 0 0 0 0v0c9.814-9.814 25.725-9.814 35.539 0 6.797 6.797 9.121 16.875 5.988 25.962-1.415 4.107-3.429 7.473-6.042 10.097l-32.719 32.719 205.551 205.603 19.256 18.786 24.213-11.533c2.88-1.355 53.827-24.715 119.015 0.835 7.274 2.851 17.497 8.254 30.671 16.21l-0.003 0.005c7.259 4.384 9.59 13.822 5.206 21.081-0.641 1.062-1.409 2.042-2.286 2.92l-302.654 302.661c-6.001 6.001-15.73 6.001-21.73 0-0.887-0.887-1.662-1.879-2.308-2.955-8.368-13.935-14.002-24.715-16.901-32.338-24.462-64.324-1.531-114.303-0.314-116.906l12.159-24.474zM910.51 469.54c-103.010-103.115-207.534-90.486-255.438-76.657l-137.66-137.66c20.978-36.424 20.195-81.928-2.244-117.569l-17.742-17.742c-44.043-27.762-102.801-22.491-140.948 15.655l-220.945 220.997c-38.146 38.146-43.364 97.009-15.603 140.843l17.69 17.742c35.746 22.491 81.302 23.222 117.569 2.296l137.712 137.712c-13.829 47.8-26.457 152.271 76.657 255.386v0c14.77 14.799 38.741 14.822 53.54 0.052 0.009-0.009 0.017-0.017 0.026-0.026l166.909-166.909 151.971 151.971c14.808 14.808 38.816 14.814 53.632 0.013v0c14.806-14.792 14.818-38.786 0.026-53.592-0.004-0.004-0.009-0.009-0.013-0.013l-152.023-152.023 166.886-166.931c14.783-14.787 14.782-38.759-0.004-53.544z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":42,"id":82,"name":"pin","prevSize":32,"code":59683},"setIdx":3,"setId":5,"iconIdx":34},{"icon":{"paths":["M540.754 693.093c15.352 15.384 15.496 40.229 0.264 55.894l-117.668 117.668c-73.491 73.319-192.458 73.316-265.962-0.017-73.318-73.49-73.317-192.457 0.008-265.954l168.375-168.375c73.52-73.311 192.464-73.27 266.199 0.266 14.589 15.658 14.164 40.055-0.961 55.196-15.147 15.163-39.545 15.57-55.449 0.774-42.469-42.36-111.21-42.361-153.673-0.007l-168.399 168.31c-42.353 42.453-42.356 111.179-0.017 153.617 42.453 42.354 111.179 42.355 153.624 0.010l117.869-117.859c15.603-15.114 40.446-14.899 55.789 0.476zM866.606 423.354l-168.423 168.379c-73.5 73.291-192.444 73.29-265.952-0.010-10.034-10.034-13.953-24.659-10.28-38.367s14.379-24.413 28.086-28.086c13.706-3.672 28.331 0.245 38.355 10.269 42.479 42.339 111.205 42.341 153.675 0.011l168.4-168.355c42.353-42.453 42.356-111.179 0.017-153.617-42.453-42.354-111.18-42.355-153.626-0.008l-117.701 117.701c-10.033 10.044-24.662 13.972-38.376 10.305s-24.431-14.372-28.112-28.082c-3.682-13.711 0.232-28.344 10.269-38.392l117.756-117.711c73.489-73.317 192.457-73.317 265.956 0.010 73.283 73.526 73.268 192.475-0.042 265.957z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["permalink"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":43,"id":81,"name":"permalink","prevSize":32,"code":59684},"setIdx":3,"setId":5,"iconIdx":35},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM563.948 396.373l-0.641 230.827c-0.059 21.149 17.038 38.341 38.187 38.4 0.035 0 0.071 0 0.106 0v0c21.208 0 38.4-17.192 38.4-38.4v-230.827c0-20.972-17.001-37.973-37.973-37.973v0c-20.989 0-38.021 16.984-38.079 37.973zM384 396.8v230.4c0 21.208 17.192 38.4 38.4 38.4v0c21.208 0 38.4-17.192 38.4-38.4v-230.4c0-21.208-17.192-38.4-38.4-38.4v0c-21.208 0-38.4 17.192-38.4 38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":44,"id":80,"name":"pause","prevSize":32,"code":59685},"setIdx":3,"setId":5,"iconIdx":36},{"icon":{"paths":["M413.267 462.457l-61.621 52.827c-12.060-24.101-18.846-51.3-18.846-80.084v-153.6c0-98.969 80.231-179.2 179.2-179.2 81.222 0 149.823 54.036 171.815 128.12l-69.415 59.508v-8.428c0-56.554-45.846-102.4-102.4-102.4s-102.4 45.846-102.4 102.4v153.6c0 9.438 1.277 18.578 3.667 27.257zM377.419 729.227l67.683-58.024c20.198 4.825 42.486 7.197 66.899 7.197 130.426 0 200.2-67.697 214.765-215.514 0.429-4.351 0.833-9.7 1.213-16.048l-0.001-0c1.264-21.105 18.881-37.497 40.023-37.238 20.598 0.252 37.091 17.154 36.839 37.752-0.007 0.58-0.028 1.159-0.062 1.738-0.287 4.905-0.591 9.154-0.908 12.749-15.602 176.721-101.637 277.457-253.469 291.626v116.934h89.6c14.138 0 25.6 11.462 25.6 25.6s-11.462 25.6-25.6 25.6h-256c-14.138 0-25.6-11.462-25.6-25.6s11.462-25.6 25.6-25.6h89.6v-116.934c-35.723-3.334-67.803-11.459-96.181-24.239zM252.971 599.877c-17.135-38.906-28.111-84.873-32.804-137.618-0.329-3.702-0.644-8.093-0.943-13.173l0.001-0c-1.211-20.562 14.476-38.212 35.038-39.423 0.578-0.034 1.157-0.055 1.736-0.062 21.134-0.259 38.746 16.128 40.009 37.226 0.358 5.958 0.736 11.004 1.136 15.136 3.113 32.169 8.828 60.56 17.202 85.298l-61.375 52.616zM689.221 461.922c-12.902 86.295-87.333 152.478-177.221 152.478-0.213 0-0.425-0-0.638-0.001l177.859-152.476zM911.217 153.59c13.803 16.101 11.94 40.343-4.161 54.146l-749.629 642.648c-16.101 13.803-40.343 11.94-54.146-4.161s-11.94-40.343 4.161-54.146l749.629-642.648c16.101-13.803 40.343-11.94 54.146 4.161z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mute"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":45,"id":79,"name":"mute","prevSize":32,"code":59686},"setIdx":3,"setId":5,"iconIdx":37},{"icon":{"paths":["M511.152 360.583c22.304 0 40.392 18.051 40.392 40.327v233.944c0 22.275-18.088 40.327-40.392 40.327s-40.392-18.051-40.392-40.327v-233.944c0-22.275 18.088-40.327 40.392-40.327zM49.119 863.747l413.955-740.751c14.588-26.104 48.090-35.845 75.032-21.957 9.656 4.977 17.597 12.61 22.82 21.957l413.955 740.751c16.58 29.668-5.594 65.533-40.020 65.533h-845.723c-34.426 0-56.599-35.865-40.020-65.533zM510.258 703.736c24.735 0 43.707 19.504 43.707 44.561 0 25.225-18.915 44.785-43.707 44.785s-43.707-19.56-43.707-44.785c0-25.056 18.972-44.561 43.707-44.561zM140.057 855.673h743.887l-371.943-665.573-371.943 665.573z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["modal-warning"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":46,"id":78,"name":"modal-warning","prevSize":32,"code":59687},"setIdx":3,"setId":5,"iconIdx":38},{"icon":{"paths":["M569.165 763.288c0 31.962-25.552 57.796-57.165 57.796s-57.165-25.834-57.165-57.796c0-31.962 25.552-57.796 57.165-57.796s57.165 25.834 57.165 57.796zM768 185.325v653.35c0 45.783-36.736 82.925-82.019 82.925h-347.961c-45.283 0-82.019-37.142-82.019-82.925v-653.35c0-45.783 36.736-82.925 82.019-82.925h347.961c45.283 0 82.019 37.142 82.019 82.925zM703.379 185.325c0-9.657-7.846-17.59-17.398-17.59h-347.961c-9.552 0-17.398 7.933-17.398 17.59v653.35c0 9.657 7.846 17.59 17.398 17.59h347.961c9.552 0 17.398-7.933 17.398-17.59v-653.35z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mobile"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":47,"id":77,"name":"mobile","prevSize":32,"code":59688},"setIdx":3,"setId":5,"iconIdx":39},{"icon":{"paths":["M512 179.2c-56.554 0-102.4 45.846-102.4 102.4v153.6c0 56.554 45.846 102.4 102.4 102.4s102.4-45.846 102.4-102.4v-153.6c0-56.554-45.846-102.4-102.4-102.4zM665.6 896v0c0 14.138-11.462 25.6-25.6 25.6h-256c-14.138 0-25.6-11.462-25.6-25.6v0c0-14.138 11.462-25.6 25.6-25.6h89.6v-116.934c-151.712-14.158-237.732-114.746-253.432-291.208-0.329-3.702-0.644-8.093-0.943-13.173l0.001-0c-1.211-20.562 14.476-38.212 35.038-39.423 0.578-0.034 1.157-0.055 1.736-0.062v0c21.134-0.259 38.746 16.128 40.009 37.226 0.358 5.958 0.736 11.004 1.136 15.136 14.365 148.459 84.158 216.438 214.855 216.438 130.426 0 200.2-67.697 214.765-215.514 0.429-4.351 0.833-9.7 1.213-16.048l-0.001-0c1.264-21.105 18.881-37.497 40.023-37.238v0c20.598 0.252 37.091 17.154 36.839 37.752-0.007 0.58-0.028 1.159-0.062 1.738-0.287 4.905-0.591 9.154-0.908 12.749-15.602 176.721-101.637 277.457-253.469 291.626v116.934h89.6c14.138 0 25.6 11.462 25.6 25.6zM512 102.4c98.969 0 179.2 80.231 179.2 179.2v153.6c0 98.969-80.231 179.2-179.2 179.2s-179.2-80.231-179.2-179.2v-153.6c0-98.969 80.231-179.2 179.2-179.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mic"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":48,"id":76,"name":"mic","prevSize":32,"code":59689},"setIdx":3,"setId":5,"iconIdx":40},{"icon":{"paths":["M541.395 826.208h-8.818c-208.607 0-360.015-132.144-360.015-314.208 0-173.28 141.818-314.256 316.115-314.256s316.115 140.976 316.115 314.256c0 28.32-13.252 56.64-27.275 86.544-4.867 10.464-9.686 20.784-13.396 29.664-36.43 78.048 35.226 115.632 65.729 131.664 4.722 2.448 10.794 5.616 15.709 8.544-20.046 22.944-87.414 58.512-304.164 57.792zM862.618 698.192c-43.129-22.704-41.056-27.216-34.214-42 3.903-9.264 8.288-18.672 12.722-28.176 15.902-33.84 33.876-72.24 33.876-116.016 0-211.776-173.333-384-386.326-384s-386.277 172.224-386.277 384c0 222.48 180.947 383.952 430.177 383.952l8.481 0.048h9.927c137.674 0 319.585-12.288 364.544-107.376 23.757-50.208-27.997-77.376-52.911-90.432z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":49,"id":75,"name":"message","prevSize":32,"code":59690},"setIdx":3,"setId":5,"iconIdx":41},{"icon":{"paths":["M445.44 780.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM445.44 524.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM445.44 268.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["menu"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":50,"id":74,"name":"menu","prevSize":32,"code":59691},"setIdx":3,"setId":5,"iconIdx":42},{"icon":{"paths":["M510.025 277.943c63.563 0 115.2 51.671 115.2 115.2s-51.637 115.2-115.2 115.2c-63.563 0-115.2-51.671-115.2-115.2s51.637-115.2 115.2-115.2zM510.025 445.514c28.91 0 52.4-23.468 52.4-52.371 0-28.862-23.49-52.371-52.4-52.371s-52.4 23.509-52.4 52.371c0 28.903 23.49 52.371 52.4 52.371zM534.763 913.507c-13.199 10.791-32.328 10.791-45.526 0-2.477-2.026-6.813-5.709-12.744-10.965-9.704-8.6-20.469-18.595-32.033-29.9-32.942-32.207-65.856-68.605-96.632-108.552-89.137-115.7-143.029-237.437-143.029-360.552 0-166.357 137.582-301.139 307.2-301.139s307.2 134.781 307.2 301.139c0 123.115-53.892 244.851-143.029 360.552-30.776 39.947-63.69 76.345-96.632 108.552-11.563 11.306-22.329 21.301-32.033 29.9-5.93 5.255-10.266 8.939-12.744 10.965zM529.322 822.806c30.705-30.020 61.437-64.005 90.064-101.164 80.587-104.602 128.529-212.899 128.529-318.104 0-127.491-105.58-230.921-235.916-230.921s-235.916 103.43-235.916 230.921c0 105.205 47.942 213.502 128.529 318.104 28.628 37.159 59.36 71.143 90.064 101.164 9.588 9.374 25.057 9.374 34.645 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["map-pin"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":51,"id":73,"name":"map-pin","prevSize":32,"code":59692},"setIdx":3,"setId":5,"iconIdx":43},{"icon":{"paths":["M217.907 243.597c-28.959 0-52.511 24.086-52.511 53.706v429.395c0 29.619 23.552 53.664 52.511 53.664h588.145c28.959 0 52.552-24.045 52.552-53.664v-429.395c0-29.619-23.593-53.706-52.552-53.706h-588.145zM806.052 844.8h-588.145c-63.734 0-115.507-52.957-115.507-118.102v-429.395c0-65.146 51.773-118.102 115.507-118.102h588.145c63.734 0 115.548 52.957 115.548 118.102v429.395c0 65.146-51.814 118.102-115.548 118.102zM485.814 577.169l-214.684-146.451c-14.683-10.016-18.656-29.937-8.94-44.82v0c9.466-14.499 28.894-18.58 43.393-9.114 0.177 0.116 0.353 0.233 0.528 0.352l208.557 142.256 208.605-142.265c14.294-9.748 33.785-6.063 43.533 8.231 0.122 0.179 0.242 0.358 0.36 0.539v0c9.706 14.885 5.729 34.8-8.951 44.814l-214.694 146.457c-17.403 11.872-40.302 11.872-57.706 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mail"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":52,"id":72,"name":"mail","prevSize":32,"code":59693},"setIdx":3,"setId":5,"iconIdx":44},{"icon":{"paths":["M909.622 909.622v0c-15.971 15.971-41.864 15.971-57.835 0l-155.625-155.625c-62.53 49.946-141.808 79.806-228.060 79.806-201.971 0-365.702-163.73-365.702-365.702s163.73-365.702 365.702-365.702c201.971 0 365.702 163.73 365.702 365.702 0 86.252-29.86 165.53-79.806 228.060l155.625 155.625c15.971 15.971 15.971 41.864 0 57.835zM468.102 764.064c163.456 0 295.963-132.507 295.963-295.963s-132.507-295.963-295.963-295.963c-163.456 0-295.963 132.507-295.963 295.963s132.507 295.963 295.963 295.963z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["magnifier"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":53,"id":71,"name":"magnifier","prevSize":32,"code":59694},"setIdx":3,"setId":5,"iconIdx":45},{"icon":{"paths":["M694.539 847.127h-365.078c-14.138 0-25.6-11.462-25.6-25.6v-288.048c0-14.138 11.462-25.6 25.6-25.6h365.078c14.138 0 25.6 11.462 25.6 25.6v288.048c0 14.138-11.462 25.6-25.6 25.6zM385.5 305.115c0-70.699 56.761-128.242 126.5-128.242s126.5 57.543 126.5 128.242v128.292h-252.999v-128.292zM711.96 433.406v-128.292c0-111.759-89.72-202.715-199.96-202.715s-199.96 90.956-199.96 202.715v128.292h-30.44c-28.277 0-51.2 22.923-51.2 51.2v385.794c0 28.277 22.923 51.2 51.2 51.2h460.8c28.277 0 51.2-22.923 51.2-51.2v-385.794c0-28.277-22.923-51.2-51.2-51.2h-30.44z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["lock"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":54,"id":70,"name":"lock","prevSize":32,"code":59695},"setIdx":3,"setId":5,"iconIdx":46},{"icon":{"paths":["M512 137.576v0c0 19.36-14.836 35.49-34.129 37.105-6.46 0.542-11.94 1.15-16.44 1.823-163.449 24.468-288.814 165.724-288.814 336.314 0 170.932 125.868 312.413 289.798 336.46 4.262 0.625 9.42 1.191 15.472 1.697l-0 0.002c19.283 1.612 34.112 17.734 34.112 37.084v0c0 18.528-15.020 33.548-33.548 33.548-0.816 0-1.631-0.030-2.444-0.089-5.493-0.4-10.229-0.844-14.206-1.331-202.519-24.798-359.402-197.708-359.402-407.37 0-209.201 156.195-381.812 358.068-407.204 4.317-0.543 9.506-1.036 15.567-1.478l0 0.002c18.467-1.347 34.53 12.532 35.877 30.999 0.059 0.812 0.089 1.625 0.089 2.439z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["loading"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":55,"id":69,"name":"loading","prevSize":32,"code":59696},"setIdx":3,"setId":5,"iconIdx":47},{"icon":{"paths":["M851.354 560.845c0 19.251-15.77 34.97-35.123 34.97-19.302 0-35.072-15.718-35.072-34.97v-93.030c0-19.251 15.77-34.97 35.072-34.97 19.354 0 35.123 15.718 35.123 34.97v93.030zM523.674 851.814h-23.398c-19.354 0-35.072-15.718-35.072-34.97s15.718-34.918 35.072-34.918h23.398c19.405 0 35.123 15.667 35.123 34.918s-15.718 34.97-35.123 34.97zM207.718 595.814c-19.354 0-35.123-15.718-35.123-34.97v-93.030c0-19.251 15.77-34.97 35.123-34.97s35.072 15.718 35.072 34.97v93.030c0 19.251-15.718 34.97-35.072 34.97zM816.23 363.059c-14.336 0-27.955 2.867-40.448 8.090-24.832-123.955-133.632-217.549-263.782-217.549s-238.95 93.594-263.834 217.549c-12.442-5.222-26.112-8.090-40.448-8.090-58.112 0-105.318 47.002-105.318 104.755v93.030c0 57.754 47.206 104.755 105.318 104.755 56.013 0 101.53-43.827 104.704-98.765h0.614v-141.824c0-111.155 89.242-201.626 198.963-201.626 109.67 0 198.912 90.47 198.912 201.626v189.133c0 69.12-34.15 130.816-88.525 167.424-14.643-40.397-53.146-69.427-98.714-69.427h-23.398c-58.061 0-105.318 46.95-105.318 104.704s47.258 104.755 105.318 104.755h23.398c41.011 0 76.237-23.654 93.645-57.754 85.606-36.506 145.664-114.022 160.358-205.773 11.981 4.71 24.934 7.526 38.554 7.526 58.112 0 105.37-47.002 105.37-104.755v-93.030c0-57.754-47.258-104.755-105.37-104.755z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["livechat"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":56,"id":68,"name":"livechat","prevSize":32,"code":59697},"setIdx":3,"setId":5,"iconIdx":48},{"icon":{"paths":["M347.27 294.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM347.27 550.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM347.27 806.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM102.177 768v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4zM102.177 512v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4zM102.177 256v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["list"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":57,"id":67,"name":"list","prevSize":32,"code":59698},"setIdx":3,"setId":5,"iconIdx":49},{"icon":{"paths":["M231.131 222.968c-32.317 0-58.514 25.881-58.514 57.806v462.452c0 31.926 26.198 57.806 58.514 57.806h561.737c32.317 0 58.514-25.881 58.514-57.806v-462.452c0-31.926-26.198-57.806-58.514-57.806h-561.737zM231.131 153.6h561.737c71.096 0 128.731 56.938 128.731 127.174v462.452c0 70.236-57.635 127.174-128.731 127.174h-561.737c-71.096 0-128.731-56.938-128.731-127.174v-462.452c0-70.236 57.635-127.174 128.731-127.174zM465.189 407.948c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869zM277.943 407.948c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM465.189 546.684c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869zM277.943 546.684c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM277.943 685.419c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM465.189 685.419c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["list-alt"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":58,"id":66,"name":"list-alt","prevSize":32,"code":59699},"setIdx":3,"setId":5,"iconIdx":50},{"icon":{"paths":["M863.073 102.4h-702.327c-32.176 0-58.345 26.533-58.345 59.073v701.053c0 32.54 26.169 59.073 58.345 59.073h702.327c32.176 0 58.527-26.533 58.527-59.073v-701.053c0-32.54-26.351-59.073-58.527-59.073zM349.98 804.591h-121.424v-390.94h121.606v390.94h-0.182zM289.269 360.22c-37.899-1.363-67.919-32.482-67.919-70.406s30.020-69.043 67.919-70.406c38.853 0.075 70.331 31.553 70.406 70.406 0 38.958-31.403 70.406-70.406 70.406zM805.137 804.591h-121.424v-190.191c0-45.329-0.91-103.674-63.078-103.674-63.26 0-73 49.38-73 100.398v193.422h-121.378v-390.94h116.463v53.43h1.638c16.293-30.72 55.979-63.124 115.007-63.124 122.88 0 145.772 81.010 145.772 186.368v214.312z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["linkedin"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":59,"id":65,"name":"linkedin","prevSize":32,"code":59700},"setIdx":3,"setId":5,"iconIdx":51},{"icon":{"paths":["M911.351 545.469c-25.914-69.895-98.214-111.622-193.431-111.622-3.735 0-7.331 0.046-10.882 0.228l-0.646-84.502 119.068-20.226c14.311-2.431 23.941-16.003 21.51-30.314-0.071-0.417-0.152-0.832-0.242-1.244v0c-3.344-15.203-17.825-25.272-33.241-23.114-35.852 5.020-71.704 10.040-107.555 15.060l-0.367-56.436c-0.103-15.831-13.012-28.586-28.844-28.498v0c-15.684 0.087-28.327 12.872-28.24 28.556 0 0.086 0.001 0.172 0.003 0.258l0.964 65.812c-33.79 5.762-67.579 11.524-101.369 17.286v0c-16.013 2.731-26.78 17.925-24.049 33.937 0.007 0.043 0.015 0.086 0.022 0.129v0c2.818 16.095 18.111 26.888 34.22 24.151l92.19-15.665 1.476 82.636c-35.674 8.733-68.121 27.236-93.603 53.377-31.715 32.688-49.635 76.062-50.121 121.315 0 64.98 40.3 103.568 96.6 110.394 131.229 15.836 212.428-125.319 239.402-193.12 41.847 56.68 15.763 159.705-64.315 227.705-0.625 0.53-1.298 1.091-2.019 1.681l-0-0c-10.687 8.739-12.266 24.486-3.527 35.173 0.145 0.178 0.293 0.354 0.443 0.527l1.609 1.858c9.197 10.635 25.138 12.141 36.164 3.417 0 0 0 0 0 0 89.963-71.182 129.396-175.97 98.782-258.757zM565.62 616.546c0-31.671 13.602-64.707 36.427-88.278 14.197-14.652 31.635-25.865 50.951-32.763l3.412 175.192c-16 5.324-33.199 7.554-51.597 5.324-40.116-4.96-39.193-37.405-39.193-59.474zM707.454 485.084c3.504-0.137 6.916-0.41 10.467-0.41 32.046 0 61.972 5.961 78.202 14.789 16.231 8.919-42.421 111.122-90.56 153.941l1.891-168.321zM223.4 378.718l-119.637 377.943c-4.846 15.31 3.636 31.651 18.947 36.497 2.817 0.892 5.754 1.349 8.708 1.356v0c23.507 0.054 44.265-15.322 51.064-37.825l30.489-100.918h145.154l34.39 102.622c7.249 21.632 27.512 36.212 50.326 36.212v0c15.375 0 27.839-12.464 27.839-27.839 0-2.892-0.451-5.767-1.336-8.52l-122.033-379.593c-8.666-26.956-33.742-45.235-62.057-45.235v0c-28.289 0-53.317 18.329-61.854 45.299zM225.467 598.117l50.215-179.701c1.522-5.447 7.171-8.628 12.618-7.106 3.449 0.964 6.144 3.659 7.107 7.108l50.176 179.699h-120.116z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["language"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":60,"id":64,"name":"language","prevSize":32,"code":59701},"setIdx":3,"setId":5,"iconIdx":52},{"icon":{"paths":["M853.333 250.88c40.46 0 73.387 30.869 73.387 69.12v384c0 38.251-32.927 69.12-73.387 69.12h-682.667c-40.46 0-73.387-30.869-73.387-69.12v-384c0-38.251 32.927-69.12 73.387-69.12h682.667zM870.969 704v-384c0-8.86-7.844-16.213-17.636-16.213h-682.667c-9.792 0-17.636 7.354-17.636 16.213v384c0 8.86 7.844 16.213 17.636 16.213h682.667c9.792 0 17.636-7.354 17.636-16.213zM346.453 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM482.987 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM619.52 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM756.053 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM278.187 634.667c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM824.32 634.667c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM278.187 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM414.72 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM551.253 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM687.787 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM824.32 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM687.787 624c0 11.74-10.005 21.12-22.187 21.12h-307.2c-12.182 0-22.187-9.38-22.187-21.12v-10.667c0-11.74 10.005-21.12 22.187-21.12h307.2c12.182 0 22.187 9.38 22.187 21.12v10.667z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["keyboard"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":61,"id":63,"name":"keyboard","prevSize":32,"code":59702},"setIdx":3,"setId":5,"iconIdx":53},{"icon":{"paths":["M827.303 827.306v0c9.349-9.335 12.764-23.079 8.87-35.704l-4.273-13.856c-6.957-22.557-19.493-42.993-36.449-59.415l-224.459-217.401c-11.86-11.487-14.062-29.707-5.28-43.688 7.009-11.241 15.473-34.315 15.473-80.553 0-112.728-91.748-204.476-204.476-204.476l-6.053 0.091c-106.311 2.958-195.329 92.021-198.333 198.56-1.593 55.795 18.978 108.496 57.798 148.363 38.775 39.958 90.929 61.939 146.588 61.939 26.669 0 50.243-6.007 72.361-18.341v0c13.642-7.642 30.703-5.266 41.738 5.812l62.708 62.953h37.818c28.277 0 51.2 22.923 51.2 51.2v37.909l8.92 8.874h37.909c28.277 0 51.2 22.923 51.2 51.2v37.909l2.685 2.685 48.32 14.819c12.638 3.876 26.383 0.46 35.737-8.88zM902.167 768.735l0.711 2.306c11.517 37.341 1.435 77.99-26.197 105.621v0c-27.636 27.636-68.281 37.74-105.641 26.262l-40.953-12.583c-15.716-4.829-30.010-13.431-41.636-25.056v0c-11.335-11.335-17.703-26.709-17.703-42.74v-23.204h-13.051c-22.532 0-44.143-8.946-60.083-24.871v0c-15.939-15.924-24.894-37.53-24.894-60.061v-13.051c-30.854 0-60.44-12.279-82.227-34.126l-31.73-31.818c-25.577 10.376-53.11 15.564-82.055 15.564-74.727 0-144.54-29.49-196.649-83.056-52.063-53.565-79.643-124.197-77.549-198.97 3.959-142.947 123.514-262.456 266.461-266.416l7.737-0.137c151.23 0 274.243 123.059 274.243 274.289 0 36.772-4.414 66.991-13.425 91.475l197.851 191.682c31.069 30.1 54.041 67.552 66.79 108.889zM276.969 346.78c0-38.547 31.265-69.812 69.812-69.812 38.592 0 69.812 31.265 69.812 69.812s-31.22 69.812-69.812 69.812c-38.547 0-69.812-31.265-69.812-69.812z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["key"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":62,"id":62,"name":"key","prevSize":32,"code":59703},"setIdx":3,"setId":5,"iconIdx":54},{"icon":{"paths":["M204.826 449.459v-193.485c0-28.277 22.923-51.2 51.2-51.2h294.554c28.277 0 51.2 22.923 51.2 51.2v526.49l135.975-130.597c15.17-14.57 39.133-14.576 54.31-0.014v0c14.406 13.822 14.879 36.706 1.057 51.112-0.337 0.352-0.682 0.697-1.033 1.034l-190.927 183.524c-19.817 19.049-51.14 19.050-70.959 0.004l-190.972-183.531c-14.393-13.833-14.848-36.714-1.016-51.108 0.339-0.352 0.684-0.698 1.037-1.036v0c15.176-14.561 39.138-14.552 54.304 0.020l131.373 126.239v-499.456h-243.302v170.803c0 21.208-17.192 38.4-38.4 38.4v0c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":63,"id":61,"name":"jump","prevSize":32,"code":59704},"setIdx":3,"setId":5,"iconIdx":55},{"icon":{"paths":["M512 768v0c19.206 0 34.775-15.569 34.775-34.775v-284.375c0-19.206-15.569-34.775-34.775-34.775v0c-19.206 0-34.775 15.569-34.775 34.775v284.375c0 19.206 15.569 34.775 34.775 34.775zM512 352c26 0 45.175-18.525 45.175-42.575 0-24.375-19.175-42.9-45.175-42.9s-45.175 18.525-45.175 42.9c0 24.050 19.175 42.575 45.175 42.575z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["italic"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":64,"id":60,"name":"italic","prevSize":32,"code":59705},"setIdx":3,"setId":5,"iconIdx":56},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383zM512.042 378.058c-24.155 0-42.364-18.678-42.364-43.535 0-24.904 18.21-43.722 42.364-43.722s42.318 18.818 42.318 43.722c0 24.857-18.163 43.535-42.318 43.535zM513.142 733.17c-21.065 0-38.245-17.133-38.245-38.198v-240.704c0-21.065 17.18-38.198 38.245-38.198s38.198 17.133 38.198 38.198v240.704c0 21.065-17.133 38.198-38.198 38.198z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["info-circled"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":65,"id":59,"name":"info-circled","prevSize":32,"code":59706},"setIdx":3,"setId":5,"iconIdx":57},{"icon":{"paths":["M512 102.4v0c20.524 0 37.161 16.638 37.161 37.161v449.409l74.777-76.869c14.366-14.768 37.984-15.094 52.752-0.728 0.246 0.239 0.489 0.482 0.728 0.728v0c14.886 15.302 14.886 39.673 0 54.976l-129.258 132.875c-19.717 20.269-52.132 20.716-72.401 0.999-0.338-0.328-0.671-0.661-0.999-0.999l-129.258-132.875c-14.886-15.302-14.886-39.673 0-54.976v0c14.366-14.768 37.984-15.094 52.752-0.728 0.246 0.239 0.489 0.482 0.728 0.728l75.858 77.981v-450.521c0-20.524 16.638-37.161 37.161-37.161zM691.2 301.894v-76.402h153.6c28.277 0 51.2 22.923 51.2 51.2v593.708c0 28.277-22.923 51.2-51.2 51.2h-665.6c-28.277 0-51.2-22.923-51.2-51.2v-593.708c0-28.277 22.923-51.2 51.2-51.2h153.6v76.402h-130.477v543.304h619.355v-543.304h-130.477z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["import"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":66,"id":58,"name":"import","prevSize":32,"code":59707},"setIdx":3,"setId":5,"iconIdx":58},{"icon":{"paths":["M277.962 392.518c-32.296 0.102-58.446 26.818-58.521 59.788v119.482c0.1 32.952 26.242 59.639 58.521 59.741h468.075c32.314-0.102 58.471-26.846 58.521-59.834v-119.436c-0.1-32.952-26.242-59.639-58.521-59.741h-468.075zM746.038 497.042l-73.128 74.7h-87.781l-73.128-74.653-73.128 74.607h-87.827l-73.037-74.607v-44.829h43.868l73.128 74.653 73.083-74.607h87.827l73.128 74.653 73.128-74.653h43.868v44.736zM394.958 691.223h234.083v59.695h-234.083v-59.695zM512 153.6c-225.847 0-409.6 173.834-409.6 388.271v268.788c0.1 32.952 26.242 59.639 58.521 59.741h702.158c32.279-0.102 58.421-26.789 58.521-59.741v-268.788c0-214.436-183.753-388.271-409.646-388.271h0.046zM863.079 810.659h-702.158v-268.788c0-184.566 154.448-333.918 351.034-333.918s351.079 149.353 351.079 333.918v268.788h0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hubot"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":67,"id":57,"name":"hubot","prevSize":32,"code":59708},"setIdx":3,"setId":5,"iconIdx":59},{"icon":{"paths":["M543.721 856.474v-127.067c54.295-7.873 101.717-35.408 135.714-75.366l107.588 67.22c-57.253 75.048-144.134 126.157-243.302 135.214zM166.889 531.706l126.839 4.415c6.645 60.075 37.046 112.503 82.148 148.184l-63.488 109.909c-83.422-59.164-139.401-154.192-145.499-262.508zM480.233 167.481v127.067c-54.295 7.919-101.717 35.408-135.714 75.366l-107.543-67.174c57.253-75.048 144.134-126.157 243.257-135.259zM856.974 490.018l-126.93-4.46c-7.191-59.119-37.456-110.729-81.92-145.909l63.442-109.909c82.876 58.755 138.581 152.872 145.408 260.278zM728.496 553.552l126.157 4.415c-5.188 38.958-16.839 75.776-33.906 109.545l-106.997-66.856c6.599-14.928 11.56-30.674 14.746-47.104zM543.721 294.548v-127.067c40.050 3.641 78.006 14.336 112.913 30.492l-63.124 109.363c-15.747-6.281-32.495-10.286-49.789-12.789zM295.276 472.633l-126.293-4.415c5.052-39.731 16.839-77.323 34.27-111.73l106.951 66.81c-6.872 15.61-11.833 32.131-14.928 49.334zM512 669.15c-86.744 0-157.195-70.497-157.195-157.15 0-86.699 70.451-157.195 157.195-157.195 86.699 0 157.195 70.497 157.195 157.195 0 86.653-70.497 157.15-157.195 157.15zM480.233 729.407v127.067c-40.004-3.641-77.961-14.29-112.913-30.492l63.169-109.363c15.701 6.281 32.495 10.286 49.744 12.789zM512 102.4c-225.826 0-409.6 183.728-409.6 409.6 0 225.826 183.774 409.6 409.6 409.6s409.6-183.774 409.6-409.6c0-225.872-183.774-409.6-409.6-409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["help"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":68,"id":56,"name":"help","prevSize":32,"code":59709},"setIdx":3,"setId":5,"iconIdx":60},{"icon":{"paths":["M292.571 643.657v-263.314h-146.286c-24.237 0-43.886-19.648-43.886-43.886v0c0-24.237 19.648-43.886 43.886-43.886h146.286v-146.286c0-24.237 19.648-43.886 43.886-43.886v0c24.237 0 43.886 19.648 43.886 43.886v146.286h263.314v-146.286c0-24.237 19.648-43.886 43.886-43.886v0c24.237 0 43.886 19.648 43.886 43.886v146.286h146.286c24.237 0 43.886 19.648 43.886 43.886v0c0 24.237-19.648 43.886-43.886 43.886h-146.286v263.314h146.286c24.237 0 43.886 19.648 43.886 43.886v0c0 24.237-19.648 43.886-43.886 43.886h-146.286v146.286c0 24.237-19.648 43.886-43.886 43.886v0c-24.237 0-43.886-19.648-43.886-43.886v-146.286h-263.314v146.286c0 24.237-19.648 43.886-43.886 43.886v0c-24.237 0-43.886-19.648-43.886-43.886v-146.286h-146.286c-24.237 0-43.886-19.648-43.886-43.886v0c0-24.237 19.648-43.886 43.886-43.886h146.286zM380.343 643.657h263.314v-263.314h-263.314v263.314z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hashtag"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":69,"id":55,"name":"hashtag","prevSize":32,"code":59710},"setIdx":3,"setId":5,"iconIdx":61},{"icon":{"paths":["M729.177 398.231c64.9-30.875 141.223 17.116 141.223 90.853v137.482c-0 7.684-0.875 15.342-2.607 22.828l-45.070 194.765c-10.486 45.318-50.562 77.441-96.639 77.441h-292.957c-31.771 0-61.833-15.527-80.442-41.539l-180.258-252.049c-31.972-44.7-22.262-107.036 21.821-139.638 31.412-23.228 72.528-25.465 105.831-7.241v-278.467c0-55.281 44.508-100.267 99.246-100.267s99.244 44.985 99.244 100.267v132.010c36.5-9.503 75.619 2.763 100.447 32.405 44.615-25.166 101.182-11.721 130.159 31.149zM230.246 537.691c-18.055 12.931-22.917 34.956-8.598 54.978l180.277 252.049c7.258 10.145 18.933 16.201 31.202 16.201h292.958c18.003 0 33.472-12.498 37.628-30.46l45.073-194.766c0.69-2.986 1.040-6.056 1.040-9.125v-137.481c0-23.624-16.54-38.907-38.761-40.254-21.568-1.307-38.352 11.161-38.581 31.558-0.011 1.235-0.011 1.235-0.017 3.133-0.035 12.857-10.45 23.261-23.284 23.261-12.869 0-23.301-10.451-23.301-23.343v-28.727c0-24.172-16.75-39.256-38.668-39.245-21.915 0.011-38.673 15.109-38.673 39.245v28.728c0 12.891-10.432 23.341-23.3 23.341s-23.3-10.45-23.3-23.341v-51.642c0-24.173-16.75-39.257-38.668-39.245-21.914 0.012-38.673 15.111-38.673 39.245v51.639c0 12.892-10.433 23.343-23.302 23.343s-23.302-10.451-23.302-23.343v-280.775c0-24.173-16.749-39.257-38.667-39.245-21.914 0.012-38.673 15.111-38.673 39.245v383.212c0 9.369-6.039 17.666-14.943 20.532s-18.637-0.356-24.084-7.972l-37.578-52.54c-13.477-18.842-35.918-21.017-53.804-8.208zM437.994 722.849v-100.908c0-12.892 10.433-23.343 23.302-23.343s23.302 10.451 23.302 23.343v100.908c0 12.892-10.433 23.343-23.302 23.343s-23.302-10.451-23.302-23.343zM585.24 598.598c12.869 0 23.301 10.451 23.301 23.343v100.91c0 12.892-10.432 23.343-23.301 23.343s-23.301-10.451-23.301-23.343v-100.91c0-12.892 10.432-23.343 23.301-23.343zM685.883 621.94c0-12.892 10.432-23.343 23.301-23.343s23.301 10.451 23.301 23.343v100.91c0 12.892-10.432 23.343-23.301 23.343s-23.301-10.451-23.301-23.343v-100.91z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hand-pointer"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":70,"id":54,"name":"hand-pointer","prevSize":32,"code":59711},"setIdx":3,"setId":5,"iconIdx":62},{"icon":{"paths":["M921.6 521.557c0 233.745-162.684 400.043-402.893 400.043-110.449 0.121-216.41-42.994-294.509-119.835s-121.921-181.095-121.798-289.764c0-226.6 185.997-409.6 416.307-409.6 112.125 0 206.488 40.505 279.157 107.179l-113.328 107.179c-148.205-140.629-423.847-34.998-423.847 195.243 0 142.905 116.011 258.64 258.018 258.64 164.858 0 226.656-116.281 236.37-176.583h-236.37v-140.811h396.324c3.886 20.981 6.568 41.142 6.568 68.358v-0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["google"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":71,"id":53,"name":"google","prevSize":32,"code":59712},"setIdx":3,"setId":5,"iconIdx":63},{"icon":{"paths":["M150.050 416.542l361.813 466.808-396.538-292.201c-10.911-8.038-15.495-22.054-11.423-34.921l46.148-139.686zM270.655 138.978c-2.277-6.329-8.316-10.555-15.087-10.555s-12.81 4.225-15.087 10.555l-90.431 277.564h211.081l-90.476-277.564zM361.131 416.542l150.778 466.808 150.733-466.808h-301.511zM919.916 556.228l-46.148-139.686-361.859 466.808 396.584-292.201c10.894-8.050 15.46-22.064 11.378-34.921h0.046zM783.246 138.978c-2.277-6.329-8.316-10.555-15.087-10.555s-12.81 4.225-15.087 10.555l-90.431 277.564h211.081l-90.476-277.564z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["gitlab"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":72,"id":52,"name":"gitlab","prevSize":32,"code":59713},"setIdx":3,"setId":5,"iconIdx":64},{"icon":{"paths":["M372.17 762.031c0 3.362-3.897 6.070-8.873 6.070-5.634 0.514-9.53-2.194-9.53-6.070 0-3.408 3.943-6.070 8.873-6.070 5.117-0.56 9.53 2.148 9.53 6.070zM319.215 754.42c-1.221 3.362 2.206 7.284 7.324 8.264 4.413 1.728 9.53 0 10.563-3.362 0.986-3.408-2.253-7.284-7.324-8.824-4.46-1.167-9.389 0.514-10.563 3.922zM394.47 751.525c-4.929 1.167-8.31 4.389-7.84 8.311 0.516 3.362 4.976 5.603 10.047 4.389 4.976-1.167 8.356-4.389 7.84-7.797-0.469-3.222-5.070-5.416-10.047-4.902zM506.578 102.4c-236.281 0-416.978 178.354-416.978 413.343 0 187.832 118.868 348.585 288.72 405.172 21.783 3.875 29.435-9.478 29.435-20.497 0-10.505-0.469-68.447-0.469-104.024 0 0-119.244 25.446-144.313-50.425 0 0-19.389-49.351-47.322-62.050 0 0-39.012-26.613 2.723-26.053 0 0 42.393 3.362 65.725 43.701 37.322 65.365 99.808 46.55 124.173 35.391 3.943-27.080 15.023-45.896 27.229-57.101-95.16-10.505-191.26-24.232-191.26-187.179 0-46.596 12.957-69.941 40.233-99.776-4.46-11.019-18.919-56.401 4.413-114.996 35.585-11.019 117.507 45.756 117.507 45.756 34.842-9.61 70.833-14.478 106.991-14.474 36.143-0.020 72.121 4.849 106.944 14.474 0 0 81.921-56.961 117.554-45.756 23.332 58.782 8.826 103.978 4.413 114.996 27.229 29.975 43.942 53.366 43.942 99.776 0 163.46-100.324 176.487-195.532 187.225 15.68 13.353 28.966 38.752 28.966 78.579 0 57.055-0.516 127.696-0.516 141.61 0 11.019 7.84 24.372 29.435 20.497 170.368-56.261 285.81-217.013 285.81-404.846 0-234.989-191.588-413.343-427.822-413.343zM255.18 686.627c-2.253 1.728-1.737 5.603 1.174 8.824 2.723 2.708 6.619 3.875 8.873 1.681 2.206-1.681 1.69-5.603-1.221-8.778-2.723-2.708-6.619-3.922-8.826-1.728zM236.777 672.9c-1.221 2.241 0.469 4.949 3.897 6.63 2.723 1.681 6.103 1.167 7.324-1.167 1.221-2.241-0.469-4.949-3.897-6.63-3.427-1.027-6.103-0.467-7.324 1.167zM291.939 733.223c-2.723 2.194-1.69 7.284 2.206 10.505 3.943 3.875 8.873 4.389 11.079 1.681 2.206-2.194 1.221-7.284-2.206-10.505-3.756-3.875-8.873-4.389-11.079-1.681zM272.55 708.338c-2.723 1.681-2.723 6.070 0 9.992 2.723 3.875 7.324 5.603 9.53 3.875 2.723-2.194 2.723-6.583 0-10.505-2.394-3.875-6.807-5.603-9.53-3.362z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["github"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":73,"id":51,"name":"github","prevSize":32,"code":59714},"setIdx":3,"setId":5,"iconIdx":65},{"icon":{"paths":["M375.059 230.4h-170.259c-14.138 0-25.6 11.462-25.6 25.6v512c0 14.138 11.462 25.6 25.6 25.6h614.4c14.138 0 25.6-11.462 25.6-25.6v-409.6c0-14.138-11.462-25.6-25.6-25.6h-342.21l-82.738-93.74c-4.86-5.506-11.85-8.66-19.193-8.66zM511.641 256h358.759c28.277 0 51.2 22.923 51.2 51.2v512c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v-614.4c0-28.277 22.923-51.2 51.2-51.2h244.559c14.687 0 28.667 6.307 38.387 17.319l75.095 85.081z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["folder"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":74,"id":50,"name":"folder","prevSize":32,"code":59715},"setIdx":3,"setId":5,"iconIdx":66},{"icon":{"paths":["M649.479 321.022l142.305 128.268c21.614 19.482 22.48 51.889 1.935 72.384-10.193 10.169-24.34 15.925-39.135 15.925h-468.794v345.6c0 21.208-18.13 38.4-40.495 38.4s-40.495-17.192-40.495-38.4v-729.6c0-28.277 24.174-51.2 53.994-51.2h512.942c26.214 0 47.464 20.151 47.464 45.008 0 12.259-5.274 23.988-14.602 32.476l-155.119 141.137zM661.1 179.2h-375.309v281.6h373.075c5.964 0 10.799-4.585 10.799-10.24 0-2.779-1.191-5.439-3.301-7.369l-113.393-103.746c-10.73-9.817-11.036-26.023-0.683-36.198 0.224-0.22 0.451-0.436 0.683-0.647l115.626-105.79c4.292-3.927 4.414-10.409 0.273-14.479-2.035-2-4.84-3.13-7.771-3.13z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["flag"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":75,"id":49,"name":"flag","prevSize":32,"code":59716},"setIdx":3,"setId":5,"iconIdx":67},{"icon":{"paths":["M204.8 358.4h153.6v-102.4h-153.6v102.4zM204.8 460.8v102.4h153.6v-102.4h-153.6zM204.8 665.6v102.4h153.6v-102.4h-153.6zM460.8 768h358.4v-102.4h-358.4v102.4zM819.2 563.2v-102.4h-358.4v102.4h358.4zM819.2 358.4v-102.4h-358.4v102.4h358.4zM204.8 153.6h614.4c56.554 0 102.4 45.846 102.4 102.4v512c0 56.554-45.846 102.4-102.4 102.4h-614.4c-56.554 0-102.4-45.846-102.4-102.4v-512c0-56.554 45.846-102.4 102.4-102.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-sheets"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":76,"id":48,"name":"file-sheets","prevSize":32,"code":59717},"setIdx":3,"setId":5,"iconIdx":68},{"icon":{"paths":["M849.98 555.116c-38.964-38.325-150.107-27.786-205.678-20.759-54.933-33.535-91.661-79.844-117.53-147.871 12.456-51.42 32.257-129.667 17.246-178.851-13.414-83.677-120.724-75.373-136.054-18.843-14.053 51.42-1.278 122.96 22.356 214.301-31.938 76.331-79.525 178.851-113.059 237.616-63.875 32.896-150.107 83.677-162.882 147.552-10.539 50.461 83.038 176.296 243.045-99.645 71.54-23.634 149.468-52.697 218.453-64.195 60.362 32.576 130.944 54.294 178.212 54.294 81.441 0 89.425-90.064 55.891-123.599zM217.296 803.59c16.288-43.755 78.247-94.216 97.090-111.782-60.681 96.771-97.090 114.017-97.090 111.782zM477.907 194.859c23.634 0 21.398 102.52 5.749 130.305-14.053-44.393-13.733-130.305-5.749-130.305zM399.979 631.127c30.979-53.975 57.488-118.169 78.886-174.699 26.508 48.226 60.362 86.87 96.132 113.379-66.43 13.733-124.237 41.838-175.018 61.32zM820.278 615.159c0 0-15.969 19.163-119.127-24.911 112.101-8.304 130.625 17.246 119.127 24.911z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-pdf"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":77,"id":47,"name":"file-pdf","prevSize":32,"code":59718},"setIdx":3,"setId":5,"iconIdx":69},{"icon":{"paths":["M251.909 698.63v0c-13.538-11.36-15.304-31.544-3.944-45.083l294.504-350.976c49.32-58.777 136.496-66.538 195.386-17.125 58.618 49.186 66.186 136.85 17.036 195.424l-315.359 375.83c-71.816 85.587-199.714 96.629-285.354 24.784-85.712-71.905-96.714-200.039-24.728-285.829l342.715-408.431c94.541-112.67 262.87-127.247 375.541-32.689 112.773 94.643 127.503 262.904 32.947 375.591l-322.185 383.965c-11.36 13.538-31.544 15.304-45.083 3.944v0c-13.538-11.36-15.304-31.544-3.944-45.083l322.185-383.965c71.835-85.61 60.639-213.506-25.062-285.429-85.614-71.85-213.568-60.769-285.372 24.804l-342.715 408.431c-49.295 58.747-41.759 146.504 16.834 195.659 58.59 49.152 146.118 41.595 195.194-16.891l315.359-375.83c26.451-31.523 22.368-78.814-9.148-105.259-31.779-26.665-78.593-22.497-105.22 9.236l-294.504 350.976c-11.36 13.538-31.544 15.304-45.083 3.944z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-generic"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":78,"id":46,"name":"file-generic","prevSize":32,"code":59719},"setIdx":3,"setId":5,"iconIdx":70},{"icon":{"paths":["M153.6 153.6h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 358.4h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 563.2h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 768h307.2c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-307.2c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-document"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":79,"id":45,"name":"file-document","prevSize":32,"code":59720},"setIdx":3,"setId":5,"iconIdx":71},{"icon":{"paths":["M921.6 147.547v728.724c0 25.031-20.298 45.147-45.147 45.147h-208.85v-317.076h106.45l15.929-123.608h-122.561v-79.007c0-35.817 9.876-60.166 61.258-60.166h65.49v-110.592c-31.712-3.406-63.588-5.062-95.482-4.961-94.345 0-159.061 57.617-159.061 163.476v91.25h-106.815v123.608h106.815v317.258h-392.078c-24.903-0.075-45.072-20.244-45.147-45.147v-728.906c0-24.849 20.298-45.147 45.147-45.147h728.724c25.031 0 45.329 20.298 45.329 45.147z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["facebook"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":80,"id":44,"name":"facebook","prevSize":32,"code":59721},"setIdx":3,"setId":5,"iconIdx":72},{"icon":{"paths":["M325.242 783.695l70.491-60.414c37.053 10.289 76.034 15.779 116.267 15.779 142.353 0 269.027-68.734 350.137-175.566 4.567-6.016 10.062-13.956 16.484-23.821 10.979-16.866 11.061-38.599 0.207-55.546-6.187-9.661-11.508-17.456-15.964-23.384-21.008-27.953-45.389-53.358-72.597-75.592l60.282-51.664c44.277 39.025 82.118 85.594 111.616 137.828 14.179 25.106 14.179 56.265 0 81.371-90.173 159.672-258.39 266.514-450.166 266.514-65.744 0-128.731-12.558-186.758-35.505zM168.775 686.343c-42.268-38.156-78.486-83.271-106.941-133.655-14.179-25.107-14.179-56.266 0-81.373 90.173-159.672 258.39-266.514 450.166-266.514 63.279 0 124.004 11.634 180.211 32.968l-72.317 61.974c-34.403-8.576-70.532-13.145-107.894-13.145-146.146 0-270.625 71.048-351.315 176.679-4.071 5.329-8.932 12.217-14.583 20.663-11.398 17.036-11.538 39.228-0.356 56.406l0.001-0c5.388 8.276 10.035 15.031 13.942 20.262 20.364 27.269 43.676 52.104 69.448 74.007l-60.362 51.729zM469.864 659.749l194.572-166.755c0.769 6.228 1.164 12.571 1.164 19.007 0 84.831-68.769 153.6-153.6 153.6-14.612 0-28.747-2.040-42.136-5.851zM358.822 523.476c-0.28-3.789-0.422-7.616-0.422-11.476 0-84.831 68.769-153.6 153.6-153.6 11.977 0 23.633 1.371 34.821 3.964l-187.999 161.112zM121.51 842.573c-13.181-15.357-11.417-38.491 3.939-51.672l715.824-613.448c15.373-13.174 38.514-11.395 51.692 3.975 13.174 15.365 11.398 38.501-3.967 51.675l-715.759 613.433c-15.382 13.183-38.536 11.409-51.73-3.963z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eye-off"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":81,"id":43,"name":"eye-off","prevSize":32,"code":59722},"setIdx":3,"setId":5,"iconIdx":73},{"icon":{"paths":["M537.998 831.538h341.366c23.326 0 42.236 19.514 42.236 43.585s-18.91 43.585-42.236 43.585l-464.6 2.891c-28.396 0-53.434-11.69-75.114-35.070l-215.032-215.032c-39.586-39.586-39.788-103.567-0.45-142.905l391.75-391.75c39.338-39.338 103.318-39.136 142.905 0.45l215.032 215.032c39.586 39.586 39.788 103.567 0.45 142.905l-336.308 336.308zM391.608 368.669l250.871 250.871 178.068-178.068c9.834-9.834 9.784-25.83-0.113-35.726l-215.032-215.032c-9.897-9.897-25.892-9.947-35.726-0.113l-178.068 178.068zM338.187 422.090l-160.262 160.262c-9.834 9.834-9.784 25.83 0.113 35.726l215.032 215.032c9.897 9.897 25.892 9.947 35.726 0.113l160.262-160.262-250.871-250.871z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eraser"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":82,"id":42,"name":"eraser","prevSize":32,"code":59723},"setIdx":3,"setId":5,"iconIdx":74},{"icon":{"paths":["M512 921.6c-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6s-183.384 409.6-409.6 409.6zM512 842.323c182.432 0 330.323-147.89 330.323-330.323s-147.89-330.323-330.323-330.323c-182.432 0-330.323 147.89-330.323 330.323s147.89 330.323 330.323 330.323zM617.703 459.148c-29.189 0-52.852-23.662-52.852-52.852s23.662-52.852 52.852-52.852c29.189 0 52.852 23.662 52.852 52.852s-23.662 52.852-52.852 52.852zM406.297 459.148c-29.189 0-52.852-23.662-52.852-52.852s23.662-52.852 52.852-52.852c29.189 0 52.852 23.662 52.852 52.852s-23.662 52.852-52.852 52.852zM365.618 626.852v0c15.048-15.048 38.673-17.26 56.252-5.268 1.49 1.018 2.891 1.924 4.202 2.72 50.324 30.547 112.95 32.907 165.176 7.082 4.591-2.27 9.926-5.508 16.005-9.714l0.001 0.001c17.854-12.352 41.986-10.172 57.338 5.179v0c13.89 13.89 13.89 36.411 0 50.302-1.206 1.206-2.497 2.324-3.864 3.345-12.813 9.569-23.658 16.594-32.536 21.074-79.213 39.971-175.185 35.294-250.475-14.032-2.476-1.622-5.219-3.565-8.23-5.828l0-0.001c-16.077-12.084-19.314-34.914-7.229-50.99 1.028-1.367 2.15-2.66 3.36-3.87z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["emoji"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":83,"id":41,"name":"emoji","prevSize":32,"code":59724},"setIdx":3,"setId":5,"iconIdx":75},{"icon":{"paths":["M774.213 358.611l-108.786-108.736 54.393-54.443c23.582-23.482 64.564-23.582 88.046-0.050l20.79 20.79c24.23 24.28 24.23 63.766 0 88.046l-54.443 54.393zM370.876 761.948l-110.338 33.528c-13.528 4.111-27.826-3.524-31.937-17.051-1.474-4.85-1.474-10.028-0.002-14.879l33.491-110.334 350.439-350.489 108.836 108.786-350.489 350.439zM860.763 142.484c-25.875-25.826-60.226-40.084-96.871-40.084-36.594 0-71.045 14.259-96.92 40.134l-470.992 471.042-65.715 216.365c-8.218 27.057 7.054 55.652 34.111 63.87 9.703 2.947 20.063 2.946 29.766-0.002l216.369-65.751 471.042-470.942c53.396-53.496 53.396-140.395 0-193.841l-20.79-20.79z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":84,"id":40,"name":"edit","prevSize":32,"code":59725},"setIdx":3,"setId":5,"iconIdx":76},{"icon":{"paths":["M801.451 563.304h65.536v247.856c0 60.994-49.446 110.44-110.44 110.44h-543.706c-60.994 0-110.44-49.446-110.44-110.44v-543.706c0-60.994 49.446-110.44 110.44-110.44h257.92l0.634 65.536h-258.554c-24.8 0-44.904 20.104-44.904 44.904v543.706c0 24.8 20.104 44.904 44.904 44.904h543.706c24.8 0 44.904-20.104 44.904-44.904v-247.856zM795.829 321.033l46.458-46.415c20.676-20.719 20.676-54.414 0-75.133l-17.741-17.741c-20.038-20.081-55.009-19.996-75.133 0.043l-46.415 46.458 92.831 92.788zM451.649 665.214l299.084-299.042-92.873-92.831-299.042 299.084-28.579 94.152c-1.256 4.139-1.256 8.558 0.002 12.696 3.508 11.544 15.709 18.058 27.253 14.55l94.155-28.61zM869.686 136.605l17.741 17.741c45.565 45.607 45.565 119.761 0 165.411l-401.955 401.87-184.635 56.108c-8.28 2.516-17.12 2.517-25.4 0.002-23.088-7.012-36.12-31.414-29.108-54.502l56.077-184.632 401.913-401.955c22.080-22.080 51.478-34.248 82.705-34.248 31.27 0 60.583 12.168 82.663 34.205z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit-rounded"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":85,"id":39,"name":"edit-rounded","prevSize":32,"code":59726},"setIdx":3,"setId":5,"iconIdx":77},{"icon":{"paths":["M327.788 691.2h-104.056c36.791 59.058 91.226 105.986 155.931 133.412-21.594-36.602-39.346-81.994-51.874-133.412zM313.652 614.4c-4.212-32.729-6.452-67.041-6.452-102.4 0-26.25 1.235-51.923 3.594-76.8h-129.45c-5.709 24.678-8.727 50.387-8.727 76.8 0 35.688 5.509 70.089 15.722 102.4h125.313zM322.087 358.4c12.547-61.994 32.463-116.447 57.576-159.012-73.799 31.281-134.238 87.932-170.378 159.012h112.802zM701.913 358.4h112.802c-36.139-71.081-96.578-127.731-170.378-159.012 25.112 42.566 45.028 97.018 57.576 159.012zM713.206 435.2c2.359 24.877 3.594 50.55 3.594 76.8 0 35.359-2.24 69.671-6.452 102.4h125.313c10.213-32.311 15.722-66.712 15.722-102.4 0-26.413-3.018-52.122-8.727-76.8h-129.45zM696.212 691.2c-12.528 51.419-30.28 96.81-51.874 133.412 64.705-27.426 119.139-74.355 155.931-133.412h-104.056zM403.174 691.2c22.577 95.277 62.874 158.72 108.826 158.72s86.249-63.443 108.826-158.72h-217.652zM389.768 614.4h244.464c3.749-31.537 5.768-65.070 5.768-99.84 0-27.341-1.249-53.918-3.605-79.36h-248.79c-2.356 25.442-3.605 52.019-3.605 79.36 0 34.77 2.020 68.303 5.768 99.84zM398.695 358.4h226.611c-21.439-106.558-64.134-179.2-113.305-179.2s-91.867 72.642-113.305 179.2zM512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["discover"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":86,"id":38,"name":"discover","prevSize":32,"code":59727},"setIdx":3,"setId":5,"iconIdx":78},{"icon":{"paths":["M530.684 472.361c17.254-61.004 73.343-105.703 139.871-105.703s122.617 44.699 139.871 105.703h71.536c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-71.536c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-388.645c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h388.645zM670.555 578.065c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065zM406.297 630.916c66.528 0 122.617 44.699 139.871 105.703h335.794c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-335.794c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-124.387c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h124.387c17.254-61.004 73.343-105.703 139.871-105.703zM406.297 842.323c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065zM353.445 102.4c66.528 0 122.617 44.699 139.871 105.703h388.645c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-388.645c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-71.536c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h71.536c17.254-61.004 73.343-105.703 139.871-105.703zM353.445 313.806c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["customize"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":87,"id":37,"name":"customize","prevSize":32,"code":59728},"setIdx":3,"setId":5,"iconIdx":79},{"icon":{"paths":["M102.436 291.481c-0.631-14.945 6.986-30.23 22.636-36.505l374.511-150.166c8.022-3.216 16.878-3.212 24.897 0.011l369.772 148.636c15.151 3.68 27.348 17.918 27.348 36.276v396.377c0 14.076-7.511 26.944-19.403 33.24l-374.143 198.101c-9.405 5.13-21.009 5.858-31.74 0.176-0.112-0.056-0.222-0.116-0.332-0.176l-374.143-198.101c-11.892-6.296-19.403-19.164-19.403-33.24v-394.628zM231.134 291.933l280.884 128.051 280.542-127.895-280.557-112.774-280.868 112.619zM547.125 486.752v337.49l304.261-161.1v-316.94l-15.771 7.19-288.49 133.36zM172.65 346.475v316.667l304.261 161.1v-337.466l-304.261-140.301z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cube"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":88,"id":36,"name":"cube","prevSize":32,"code":59729},"setIdx":3,"setId":5,"iconIdx":80},{"icon":{"paths":["M512 447.614l210.621-210.621c17.78-17.78 46.606-17.78 64.386 0v0c17.78 17.78 17.78 46.606 0 64.386l-210.621 210.621 210.621 210.621c17.78 17.78 17.78 46.606 0 64.386v0c-17.78 17.78-46.606 17.78-64.386 0l-210.621-210.621-210.621 210.621c-17.78 17.78-46.606 17.78-64.386 0v0c-17.78-17.78-17.78-46.606 0-64.386l210.621-210.621-210.621-210.621c-17.78-17.78-17.78-46.606 0-64.386v0c17.78-17.78 46.606-17.78 64.386 0l210.621 210.621z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cross"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":89,"id":35,"name":"cross","prevSize":32,"code":59730},"setIdx":3,"setId":5,"iconIdx":81},{"icon":{"paths":["M153.6 153.6v576.366c0 28.277 22.923 51.2 51.2 51.2h133.781v89.234c0 28.277 22.923 51.2 51.2 51.2h429.419c28.277 0 51.2-22.923 51.2-51.2v-351.93c0-13.473-5.31-26.403-14.78-35.986l-221.761-224.436c-9.619-9.735-22.735-15.214-36.42-15.214h-24.857l-123.655-125.211c-9.62-9.741-22.739-15.223-36.429-15.223h-207.696c-28.277 0-51.2 22.923-51.2 51.2zM248.568 172.57h145.906c6.846 0 13.408 2.742 18.218 7.615l61.85 62.649h-135.961v468.114h-90.013c-14.138 0-25.6-11.462-25.6-25.6v-487.178c0-14.138 11.462-25.6 25.6-25.6zM433.548 313.051h110.658c14.138 0 25.6 11.462 25.6 25.6v182.81c0 14.138 11.462 25.6 25.6 25.6h180.026c14.138 0 25.6 11.462 25.6 25.6v253.074c0 14.138-11.462 25.6-25.6 25.6h-341.884c-14.138 0-25.6-11.462-25.6-25.6v-487.085c0-14.138 11.462-25.6 25.6-25.6zM647.937 371.541l95.417 96.585c1.987 2.012 1.968 5.253-0.044 7.241-0.958 0.947-2.251 1.478-3.598 1.478h-95.417c-2.828 0-5.12-2.292-5.12-5.12v-96.585c0-2.828 2.292-5.12 5.12-5.12 1.369 0 2.68 0.548 3.642 1.522z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["copy"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":90,"id":34,"name":"copy","prevSize":32,"code":59731},"setIdx":3,"setId":5,"iconIdx":82},{"icon":{"paths":["M181.677 299.476v265.758c0 36.491 29.713 66.232 66.374 66.232h527.897c36.726 0 66.374-29.67 66.374-66.232v-265.758c0-36.491-29.713-66.232-66.374-66.232h-527.897c-36.726 0-66.374 29.67-66.374 66.232zM102.4 299.476c0-80.618 65.21-145.876 145.652-145.876h527.897c80.379 0 145.652 65.334 145.652 145.876v265.758c0 80.618-65.21 145.876-145.652 145.876h-527.897c-80.379 0-145.652-65.334-145.652-145.876v-265.758zM340.232 830.578v0c0-21.993 17.829-39.822 39.822-39.822h263.891c21.993 0 39.822 17.829 39.822 39.822v0c0 21.993-17.829 39.822-39.822 39.822h-263.891c-21.993 0-39.822-17.829-39.822-39.822z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["computer"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":91,"id":33,"name":"computer","prevSize":32,"code":59732},"setIdx":3,"setId":5,"iconIdx":83},{"icon":{"paths":["M448.953 104.267c3.112-1.171 6.459-1.823 9.925-1.866h105.986c13.206-0.126 24.859 8.613 28.435 21.327l22.541 79.214c5.495 1.863 10.893 3.889 16.211 6.056 8.48 3.267 16.857 6.885 25.115 10.967l71.744-39.959c11.519-6.461 25.938-4.401 35.187 5.027l74.944 74.944c2.209 2.266 3.991 4.82 5.328 7.553 4.372 8.465 4.433 18.757-0.323 27.457l-40.062 71.725c6.617 13.221 12.412 27.104 17.235 41.325l49.353 14.166c1.121 0.265 2.216 0.594 3.283 0.982l26.416 7.517c12.713 3.577 21.453 15.229 21.327 28.435v105.986c-0.012 0.918-0.066 1.828-0.161 2.727-0.132 1.48-0.376 2.96-0.737 4.428-2.516 10.235-10.302 18.351-20.424 21.29l-78.983 22.371c-4.661 13.983-10.365 27.853-16.99 41.276l24.881 44.915c1.28 2.073 2.28 4.268 2.997 6.531l11.431 20.629c6.344 11.31 4.484 25.446-4.57 34.73l-72.429 72.429c-2.195 2.853-4.926 5.323-8.104 7.247-9.017 5.458-20.26 5.692-29.496 0.612l-71.788-40.041c-13.602 6.728-27.455 12.391-41.63 17.099l-6.044 21.069-16.361 57.764c-3.51 12.736-15.122 21.538-28.332 21.478h-106.018c-13.211 0.061-24.822-8.742-28.332-21.478l-19.784-69.849-2.577-8.984c-5.736-1.907-11.378-4.027-16.964-6.321-8.402-3.243-16.62-6.786-24.651-10.81l-39.595 21.941-31.885 17.81c-9.012 5.118-19.837 4.998-28.573 0.179-2.491-1.357-4.814-3.095-6.879-5.2l-74.944-74.944c-1.102-1.13-2.098-2.333-2.985-3.593-6.556-9.146-7.355-21.437-1.653-31.476l32.471-58.134 7.354-13.271c-6.703-13.371-12.5-27.189-17.365-41.585l-79.056-22.392c-12.555-3.68-21.139-15.25-21.021-28.332v-106.475c0.085-12.913 8.629-24.243 21.021-27.875l79.056-22.849c4.827-14.236 10.63-28.133 17.256-41.366l-40.039-71.683c-4.438-8.119-4.682-17.623-1.142-25.735 1.393-3.382 3.436-6.539 6.104-9.275l74.944-74.944c8.713-8.881 22.014-11.224 33.157-6.064 0.665 0.301 1.323 0.629 1.972 0.984l72.058 39.745c13.457-6.642 27.363-12.357 41.382-17.025l22.34-78.873c2.656-9.246 9.626-16.443 18.445-19.531zM777.457 572.567c3.652-4.403 8.577-7.714 14.244-9.384l71.411-20.357v-62.041l-5.361-1.511c-1.065-0.227-2.124-0.515-3.172-0.865l-10.15-2.889-52.405-14.769c-3.317-0.921-6.391-2.403-9.115-4.338-5.474-3.771-9.59-9.357-11.499-15.932-5.939-21.558-14.215-42.045-25.134-61.234-5.192-8.852-5.366-19.777-0.457-28.789l35.999-64.707-43.757-43.757-64.303 35.867c-2.389 1.314-4.915 2.269-7.503 2.868-7.16 1.773-14.836 0.796-21.427-2.93-12.746-7.252-26.064-13.54-39.887-18.685-7.025-2.548-14.144-4.804-21.335-6.78-3.065-0.851-5.923-2.181-8.488-3.905-5.768-3.77-10.11-9.519-12.087-16.327l-20.107-70.831h-61.858l-20.248 71.029c-2.902 9.843-10.75 17.446-20.68 20.034-21.289 5.849-41.628 14.483-61.071 25.527-2.615 1.438-5.394 2.447-8.239 3.028-6.963 1.548-14.363 0.518-20.746-3.090l-37.47-20.994-26.929-14.811-43.622 43.622 21.999 39.999 13.821 24.843c1.956 3.59 3.105 7.485 3.451 11.43 0.592 5.962-0.65 12.065-3.727 17.436-8.467 14.906-15.328 30.149-20.704 46.044-1.652 4.923-3.173 9.908-4.566 14.951-2.649 9.813-10.207 17.555-19.968 20.433l-71.411 20.383v61.726l34.007 9.873 37.081 10.45c9.887 2.745 17.612 10.47 20.357 20.357 5.849 21.289 14.16 41.952 25.204 61.394 5.139 8.867 5.261 19.778 0.323 28.758l-20.483 36.577-15.641 28.115 43.88 43.88 64.4-36.069c8.932-5.050 19.857-5.050 28.789 0 6.009 3.419 12.146 6.624 18.403 9.596 13.962 6.514 28.424 11.754 43.187 15.81 9.887 2.745 17.612 10.47 20.357 20.357l20.034 71.088h62.041l6.578-23.074 13.561-48.080c1.747-6.016 5.341-11.205 10.134-14.932 3.129-2.475 6.78-4.321 10.765-5.36 7.389-2.030 14.625-4.357 21.736-6.991 13.565-5.095 26.64-11.29 39.163-18.414 8.932-5.050 19.857-5.050 28.789 0l15.143 8.485 49.549 27.357 43.61-43.61-36.101-64.89c-4.677-8.587-4.74-18.911-0.244-27.523 0.18-0.363 0.368-0.723 0.565-1.079 3.224-5.676 6.243-11.428 9.047-17.258 6.65-13.966 12.022-28.454 16.224-43.707 1.161-4.276 3.252-8.152 6.047-11.403zM466.144 357.634c14.499-4.319 29.841-6.64 45.704-6.64 14.901 0 29.342 2.048 43.056 5.876 25.934 7.136 50.404 20.855 70.704 41.155 17.869 17.869 30.638 38.967 38.307 61.451 5.693 16.432 8.788 34.054 8.788 52.372 0 22.987-4.874 44.881-13.64 64.699-7.785 17.793-18.937 34.443-33.455 48.961-11.806 11.806-25.021 21.385-39.115 28.739-22.334 11.78-47.74 18.456-74.645 18.456-28.401 0-55.133-7.44-78.343-20.469-12.696-7.061-24.623-15.969-35.38-26.726-12.399-12.399-22.342-26.352-29.83-41.245-11.061-21.789-17.302-46.401-17.302-72.414 0-20.811 3.995-40.726 11.256-59.015 7.82-19.992 19.779-38.711 35.876-54.808 19.599-19.599 43.082-33.062 68.019-40.391zM569.978 416.696c-16.89-10.293-36.78-16.209-58.13-16.209-19.935 0-38.597 5.158-54.733 14.221-8.575 4.873-16.656 10.978-23.993 18.315-9.811 9.811-17.419 20.952-22.824 32.827-6.307 14-9.812 29.567-9.812 45.999 0 11.865 1.827 23.279 5.217 33.978 5.262 16.307 14.401 31.667 27.418 44.684 15.358 15.358 33.978 25.318 53.566 29.881 8.078 1.844 16.499 2.818 25.16 2.818 9.391 0 18.499-1.145 27.194-3.303 18.855-4.762 36.733-14.56 51.569-29.396 11.347-11.347 19.748-24.475 25.202-38.458 4.78-12.454 7.397-26.006 7.397-40.205 0-19.452-4.911-37.692-13.569-53.556-4.986-9.051-11.329-17.57-19.029-25.27-6.371-6.371-13.303-11.813-20.633-16.326z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cog"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":92,"id":32,"name":"cog","prevSize":32,"code":59733},"setIdx":3,"setId":5,"iconIdx":84},{"icon":{"paths":["M118.051 475.773l144.245-144.063c14.452-14.433 37.864-14.432 52.314 0.003v0c14.43 14.416 14.442 37.8 0.027 52.23-0.009 0.009-0.018 0.018-0.027 0.027l-128.16 128.029 128.16 128.029c14.43 14.416 14.442 37.8 0.027 52.23-0.009 0.009-0.018 0.018-0.027 0.027v0c-14.45 14.435-37.862 14.437-52.314 0.003l-144.245-144.063c-20.007-19.982-20.028-52.4-0.046-72.408 0.015-0.015 0.030-0.030 0.046-0.046zM607.804 154.853v0c19.741 5.277 31.466 25.558 26.189 45.299-0.003 0.011-0.006 0.023-0.009 0.034l-172.483 642.778c-5.299 19.747-25.595 31.465-45.346 26.182v0c-19.732-5.278-31.449-25.553-26.171-45.285 0.002-0.009 0.005-0.017 0.007-0.026l172.438-642.777c5.301-19.76 25.609-31.489 45.374-26.206zM709.395 331.733v0c14.45-14.435 37.862-14.437 52.314-0.003l144.24 144.059c20.007 19.982 20.028 52.4 0.046 72.408-0.017 0.017-0.033 0.033-0.050 0.050l-144.233 144.017c-14.454 14.432-37.866 14.429-52.317-0.006v0c-14.428-14.413-14.44-37.794-0.027-52.222 0.010-0.010 0.021-0.021 0.031-0.031l128.156-127.984-128.16-128.029c-14.43-14.416-14.442-37.8-0.027-52.23 0.009-0.009 0.018-0.018 0.027-0.027z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["code"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":93,"id":31,"name":"code","prevSize":32,"code":59734},"setIdx":3,"setId":5,"iconIdx":85},{"icon":{"paths":["M799.928 381.24c98.094 16.763 172.872 119.941 172.872 225.369 0 117.411-92.744 212.591-207.149 212.591v-78.095c72.379 0 131.053-60.216 131.053-134.496s-58.675-150.171-131.053-150.171h-38.048v-39.047c0-50.319-39.747-91.11-88.778-91.11-21.585 0-41.88 7.883-57.889 22.031l-30.189 26.68-24.581-32.176c-28.71-37.581-72.449-59.92-119.855-59.92-84.053 0-152.191 69.928-152.191 156.189 0 17.991 2.949 35.513 8.65 52.067l17.929 52.060h-64.627c-62.473 0-89.968 42.976-88.778 106.785s39.747 91.11 88.778 91.11h549.578v78.095h-549.578c-91.057 0-164.873-75.756-164.873-169.205 0-80.696 55.043-163.872 128.698-180.794-1.243-9.927-1.873-19.982-1.873-30.117 0-129.391 102.207-234.284 228.286-234.284 58.821 0 113.995 23.013 155.636 62.872 23.365-12.656 49.63-19.487 76.878-19.487 78.967 0 144.968 56.975 161.102 133.055zM476.137 464.902c0-17.468 14.16-31.628 31.628-31.628s31.628 14.16 31.628 31.628v58.489h58.497c17.472 0 31.636 14.164 31.636 31.636s-14.164 31.636-31.636 31.636h-58.497v58.489c0 17.468-14.16 31.628-31.628 31.628s-31.628-14.16-31.628-31.628v-58.489h-58.482c-17.472 0-31.636-14.164-31.636-31.636s14.164-31.636 31.636-31.636h58.482v-58.489z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cloud-plus"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":94,"id":30,"name":"cloud-plus","prevSize":32,"code":59735},"setIdx":3,"setId":5,"iconIdx":86},{"icon":{"paths":["M512 102.4c226.27 0 409.6 183.33 409.6 409.6s-183.33 409.6-409.6 409.6c-226.27 0-409.6-183.33-409.6-409.6s183.33-409.6 409.6-409.6zM850.057 512c0-187.006-151.632-338.057-338.057-338.057-187.006 0-338.057 151.632-338.057 338.057 0 187.006 151.632 338.057 338.057 338.057 187.006 0 338.057-151.632 338.057-338.057zM552.606 517.066l98.098 72.79c17.476 12.968 21.132 37.648 8.164 55.124-0.089 0.12-0.178 0.239-0.269 0.358v0c-13.398 17.635-38.434 21.325-56.351 8.307l-118.92-86.409c-7.516-5.577-11.933-14.344-11.933-23.653v-245.889c0-22.426 18.18-40.606 40.606-40.606v0c22.426 0 40.606 18.18 40.606 40.606v219.373z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clock"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":95,"id":29,"name":"clock","prevSize":32,"code":59736},"setIdx":3,"setId":5,"iconIdx":87},{"icon":{"paths":["M736.78 202.915c45.52 0 82.42 37.126 82.42 82.925v552.834c0 45.799-36.9 82.925-82.42 82.925h-449.561c-45.52 0-82.42-37.126-82.42-82.925v-552.834c0-45.799 36.9-82.925 82.42-82.925h130.008c-0.090-1.676-0.135-3.351-0.135-5.026 0-52.66 42.569-95.49 94.907-95.49s94.907 42.83 94.907 95.49c0 1.674-0.045 3.349-0.135 5.026h130.009zM512 152.658c-24.829 0-44.956 20.251-44.956 45.232s20.127 45.232 44.956 45.232c24.829 0 44.956-20.251 44.956-45.232s-20.127-45.232-44.956-45.232zM354.654 268.25h-67.434c-9.633 0-17.483 7.898-17.483 17.59v552.834c0 9.692 7.85 17.59 17.483 17.59h449.561c9.633 0 17.483-7.898 17.483-17.59v-552.834c0-9.692-7.85-17.59-17.483-17.59h-67.434v40.251c0 14.572-11.742 26.385-26.224 26.385h-262.244c-14.483 0-26.224-11.814-26.224-26.385v-40.251z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clipboard"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":96,"id":28,"name":"clipboard","prevSize":32,"code":59737},"setIdx":3,"setId":5,"iconIdx":88},{"icon":{"paths":["M264.13 689.35v0c-12.944-11.048-14.639-30.434-3.811-43.561l280.254-339.75c46.923-56.885 129.865-64.397 185.893-16.573 55.77 47.603 62.97 132.445 16.209 189.134l-300.037 363.734c-68.327 82.833-190.011 93.519-271.49 23.986-81.547-69.59-92.015-193.601-23.527-276.629l326.064-395.286c89.948-109.044 250.098-123.151 357.296-31.637 107.294 91.596 121.308 254.442 31.346 363.502l-306.589 371.677c-10.596 12.846-29.6 14.669-42.446 4.073-0.13-0.108-0.26-0.216-0.389-0.326v0c-12.944-11.048-14.639-30.434-3.811-43.561l306.589-371.677c68.345-82.855 57.693-206.634-23.845-276.242-81.454-69.537-203.192-58.813-271.507 24.006l-326.064 395.286c-46.9 56.857-39.73 141.789 16.016 189.362 55.743 47.57 139.019 40.256 185.711-16.347l300.037-363.734c25.165-30.508 21.281-76.277-8.703-101.871-30.235-25.807-74.774-21.773-100.108 8.939l-280.254 339.75c-10.596 12.846-29.6 14.669-42.446 4.073-0.13-0.108-0.26-0.216-0.389-0.326z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clip"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":97,"id":27,"name":"clip","prevSize":32,"code":59738},"setIdx":3,"setId":5,"iconIdx":89},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383zM547.792 563.291l80.305-80.305c13.715-13.715 35.952-13.715 49.667 0v0c13.715 13.715 13.715 35.952 0 49.667l-129.293 129.302c-19.995 19.995-52.413 19.995-72.408 0-0.001-0.001-0.003-0.003-0.004-0.004l-129.266-129.307c-13.709-13.713-13.713-35.941-0.009-49.658v0c13.693-13.706 35.904-13.716 49.609-0.023 0.006 0.006 0.011 0.011 0.017 0.017l81.165 81.201v-242.951c0-19.39 15.719-35.109 35.109-35.109v0c19.39 0 35.109 15.719 35.109 35.109v242.062z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circled-arrow-down"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":98,"id":26,"name":"circled-arrow-down","prevSize":32,"code":59739},"setIdx":3,"setId":5,"iconIdx":90},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":99,"id":25,"name":"circle","prevSize":32,"code":59740},"setIdx":3,"setId":5,"iconIdx":91},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM475.623 513.887l-107.775-107.775c-11.659-11.659-11.659-30.563 0-42.222s30.563-11.659 42.222 0l107.775 107.775 107.775-107.775c11.659-11.659 30.563-11.659 42.222 0s11.659 30.563 0 42.222l-107.775 107.775 107.775 107.775c11.659 11.659 11.659 30.563 0 42.222s-30.563 11.659-42.222 0l-107.775-107.775-107.775 107.775c-11.659 11.659-30.563 11.659-42.222 0s-11.659-30.563 0-42.222l107.775-107.775zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-cross"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":100,"id":24,"name":"circle-cross","prevSize":32,"code":59741},"setIdx":3,"setId":5,"iconIdx":92},{"icon":{"paths":["M430.733 607.019l248.174-242.694c15.137-14.803 39.379-14.632 54.306 0.382 14.93 15.211 14.704 39.364-0.333 54.121l-270.787 265.759c-17.457 17.133-45.472 16.961-62.717-0.386l-114.626-115.298c-14.926-15.014-14.926-39.263 0-54.277 15.105-15.018 39.269-14.947 54.13 0l91.854 92.393zM512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["checkmark-circled"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":101,"id":23,"name":"checkmark-circled","prevSize":32,"code":59742},"setIdx":3,"setId":5,"iconIdx":93},{"icon":{"paths":["M402.241 688.422l389.132-389.132c14.441-14.441 37.854-14.441 52.294 0v0c14.441 14.441 14.441 37.854 0 52.294l-408.299 408.299c-19.995 19.995-52.413 19.995-72.408 0l-171.436-171.436c-15.29-15.29-15.29-40.080 0-55.371v0c15.29-15.29 40.080-15.29 55.371 0l155.345 155.345z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["check"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":102,"id":22,"name":"check","prevSize":32,"code":59743},"setIdx":3,"setId":5,"iconIdx":94},{"icon":{"paths":["M172.192 806.879c-1.753 0.912-3.253 1.7-4.578 2.41 23.378 19.728 84.492 33.203 196.047 32.831l5.943-0c68.451 0 127.027-21.801 175.729-65.402l78.706 5.168c-32.607 31.619-49.869 48.22-51.786 49.803-52.616 43.442-123.449 67.577-202.649 67.577l-5.671 0.032-6.699 0c-136.95 0-222.963-18.909-249.539-75.336-14.428-30.613 1.439-48.51 37.772-67.626 24.169-12.773 24.606-13.408 20.758-21.754-2.23-5.305-4.163-9.569-8.543-18.995-16.964-36.242-23.235-54.989-23.235-80.395 0-43.195 10.472-84.864 30.015-122.108 10.746-18.442 29.443-48.938 54.204-61.082 0-1.302 13.676 61.082 13.676 59.997-4.346 6.059-8.904 12.78-13.676 20.163-24.761 36.112-26.943 64.99-26.943 103.030 0 14.972 4.334 27.305 17.854 56.249 5.229 11.287 6.997 15.182 9.089 20.209 16.231 34.899 4.636 61.658-24.761 82.359-6.556 4.617-11.96 7.723-21.713 12.869zM848.367 630.922c-1.774-0.959-3.814-2.034-6.289-3.322-11.28-5.952-17.509-9.532-25.058-14.848-33.726-23.75-46.973-54.324-28.286-94.515 2.332-5.613 4.375-10.115 10.412-23.145 15.698-33.609 20.736-47.942 20.736-65.422 0-132.445-107.986-240.215-240.702-240.215s-240.702 107.77-240.702 240.215c0 139.146 115.308 240.178 274.83 240.178l6.872 0c130.587 0.435 201.591-15.525 228.189-38.926zM847.938 544.158c-4.749 10.308-3.991 11.411 24.298 26.361 41.68 21.929 59.773 42.339 43.344 77.195-30.548 64.862-129.872 86.698-287.964 86.698h-7.717l-6.622-0.038c-196.405 0-339.44-128.159-339.44-304.704 0-168.061 136.964-304.742 305.341-304.742 168.384 0 305.379 136.688 305.379 304.742 0 29.238-7.227 50.846-26.812 92.685-5.065 10.9-7.304 15.839-9.807 21.804z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chat"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":103,"id":21,"name":"chat","prevSize":32,"code":59744},"setIdx":3,"setId":5,"iconIdx":95},{"icon":{"paths":["M793.6 204.8c42.4 0 76.8 34.4 76.8 76.8v563.2c0 42.4-34.4 76.8-76.8 76.8h-563.2c-42.4 0-76.8-34.4-76.8-76.8v-563.2c0-42.4 34.4-76.8 76.8-76.8h76.8v-51.2c0-28.277 22.923-51.2 51.2-51.2s51.2 22.923 51.2 51.2v51.2h204.8v-51.2c0-28.277 22.923-51.2 51.2-51.2s51.2 22.923 51.2 51.2v51.2h76.8zM742.4 844.8c28.277 0 51.2-22.923 51.2-51.2v-435.2h-563.2v435.2c0 28.277 22.923 51.2 51.2 51.2h460.8zM322.56 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM322.56 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM476.16 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM476.16 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM629.76 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM629.76 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["calendar"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":104,"id":20,"name":"calendar","prevSize":32,"code":59745},"setIdx":3,"setId":5,"iconIdx":96},{"icon":{"paths":["M538.229 742.4h-144.501c-28.277 0-51.2-22.923-51.2-51.2v-358.4c0-28.277 22.923-51.2 51.2-51.2h140.211c89.435 0 143.228 44.068 143.228 114.96 0 48.22-36.632 91.33-83.495 98.994v5.429c64.354 6.387 107.256 50.774 107.256 110.809 0 81.111-61.053 130.608-162.699 130.608zM416.452 342.274v134.44h87.785c65.344 0 99.336-23.311 99.336-67.060 0-42.472-32.012-67.38-86.795-67.38h-100.326zM416.452 681.726h106.926c66.664 0 101.976-26.185 101.976-75.044 0-48.22-36.632-73.447-106.266-73.447h-102.636v148.491z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bold"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":105,"id":19,"name":"bold","prevSize":32,"code":59746},"setIdx":3,"setId":5,"iconIdx":97},{"icon":{"paths":["M728.623 590.439v-212.883c0-113.231-96.906-205.203-216.623-205.203s-216.623 91.972-216.623 205.203v212.883l-0.885 3.58-78.182 148.403h591.449l-79.136-151.982zM512 854.645c23.566 0 44.551-11.762 55.954-29.929h-111.909c11.403 18.167 32.388 29.929 55.954 29.929zM125.951 743.361l85.798-149.441v-218.453c0-155.152 134.566-280.747 300.251-280.747s300.251 125.595 300.251 280.747l-0.91 214.827 81.715 154.639c13.211 25.001 3.653 55.978-21.348 69.189-7.372 3.895-15.583 5.932-23.921 5.932h-186.412c-16.891 63.603-78.184 109.227-149.376 109.227s-132.485-45.624-149.376-109.227h-192.271c-28.277 0-51.2-22.923-51.2-51.2 0-8.945 2.344-17.735 6.798-25.492z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bell"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":106,"id":18,"name":"bell","prevSize":32,"code":59747},"setIdx":3,"setId":5,"iconIdx":98},{"icon":{"paths":["M512 844.8c-78.029 0-149.658-27.085-206.438-72.038l467.2-467.2c44.954 56.781 72.038 128.41 72.038 206.438 0 183.808-148.992 332.8-332.8 332.8zM179.2 512c0-183.808 148.992-332.8 332.8-332.8 78.029 0 149.658 27.085 206.438 72.038l-467.2 467.2c-44.954-56.781-72.038-128.41-72.038-206.438zM512 102.4c-226.202 0-409.6 183.398-409.6 409.6s183.398 409.6 409.6 409.6c226.202 0 409.6-183.398 409.6-409.6s-183.398-409.6-409.6-409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["ban"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":107,"id":17,"name":"ban","prevSize":32,"code":59748},"setIdx":3,"setId":5,"iconIdx":99},{"icon":{"paths":["M254.865 554.983l171.258 175.077c16.155 16.515 16.155 42.912 0 59.427v0c-15.702 16.052-41.444 16.336-57.497 0.634-0.214-0.209-0.425-0.42-0.634-0.634l-230.571-235.712c-19.466-19.9-19.466-51.705 0-71.605l243.364-248.79c15.103-15.44 39.864-15.713 55.304-0.61 0.205 0.201 0.409 0.404 0.61 0.61v0c15.539 15.885 15.539 41.275 0 57.16l-179.602 183.606h532.172c73.176 0 132.331 58.113 132.331 130.236v116.079c0 22.701-18.403 41.105-41.105 41.105v0c-22.701 0-41.105-18.403-41.105-41.105v-116.079c0-27.461-22.331-49.399-50.121-49.399h-534.404z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["back"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":108,"id":16,"name":"back","prevSize":32,"code":59749},"setIdx":3,"setId":5,"iconIdx":100},{"icon":{"paths":["M670.555 300.594v0c21.892 0 39.639 17.747 39.639 39.639v197.702c0 56.387 28.106 92.982 66.065 92.982 37.77 0 66.065-36.953 66.065-92.982v-25.934c0-182.432-147.89-330.323-330.323-330.323s-330.323 147.89-330.323 330.323c0 182.432 147.89 330.323 330.323 330.323 24.665 0 48.932-2.698 72.491-7.97 4.969-1.112 10.862-2.703 17.679-4.772l-0-0c20.847-6.329 43.096 4.186 51.441 24.311v0c7.896 19.041-1.14 40.877-20.18 48.773-0.979 0.406-1.976 0.77-2.986 1.092-10.574 3.362-19.5 5.876-26.778 7.54-29.799 6.815-60.501 10.304-91.666 10.304-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6v28.635c-1.103 94.621-59.289 169.558-145.342 169.558-50.481 0-91.316-25.583-116.702-65.873-36.285 40.436-88.95 65.873-147.556 65.873-109.459 0-198.194-88.734-198.194-198.194s88.734-198.194 198.194-198.194c44.618 0 85.792 14.743 118.916 39.624v-13.198c0-21.892 17.747-39.639 39.639-39.639zM512 630.916c65.676 0 118.916-53.241 118.916-118.916s-53.241-118.916-118.916-118.916c-65.676 0-118.916 53.241-118.916 118.916s53.241 118.916 118.916 118.916z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["at"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":109,"id":15,"name":"at","prevSize":32,"code":59750},"setIdx":3,"setId":5,"iconIdx":101},{"icon":{"paths":["M512 611.902l228.206-227.278c14.56-14.501 38.103-14.501 52.663 0v0c14.483 14.424 14.531 37.859 0.107 52.342-0.035 0.036-0.071 0.071-0.107 0.107l-244.739 243.744c-19.978 19.897-52.282 19.897-72.26 0l-244.739-243.744c-14.483-14.424-14.531-37.859-0.107-52.342 0.035-0.036 0.071-0.071 0.107-0.107v0c14.56-14.501 38.103-14.501 52.663 0l228.206 227.278z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":110,"id":14,"name":"arrow-down","prevSize":32,"code":59751},"setIdx":3,"setId":5,"iconIdx":102},{"icon":{"paths":["M849.683 174.317h87.602c19.614 0 35.514 15.9 35.514 35.514v0c0 19.614-15.9 35.514-35.514 35.514h-87.602v87.602c0 19.614-15.9 35.514-35.514 35.514v0c-19.614 0-35.514-15.9-35.514-35.514v-87.602h-87.602c-19.614 0-35.514-15.9-35.514-35.514v0c0-19.614 15.9-35.514 35.514-35.514h87.602v-87.602c0-19.614 15.9-35.514 35.514-35.514v0c19.614 0 35.514 15.9 35.514 35.514v87.602zM615.288 456.065c-26.152 0-47.353-21.2-47.353-47.353s21.2-47.353 47.353-47.353c26.152 0 47.353 21.2 47.353 47.353s-21.2 47.353-47.353 47.353zM425.877 456.065c-26.152 0-47.353-21.2-47.353-47.353s21.2-47.353 47.353-47.353c26.152 0 47.353 21.2 47.353 47.353s-21.2 47.353-47.353 47.353zM520.583 136.435v71.029c-28.136 1.51-48.899 3.698-62.287 6.566-133.542 28.605-233.667 147.304-233.667 289.388 0 163.451 132.503 295.954 295.954 295.954 142.116 0 260.836-100.17 289.407-233.758 2.859-13.369 5.042-34.101 6.546-62.196h71.029c-1.4 30.22-3.438 52.58-6.116 67.078-31.519 170.646-181.098 299.904-360.866 299.904-202.679 0-366.983-164.304-366.983-366.983 0-178.815 127.891-327.76 297.193-360.355 15.063-2.9 38.326-5.109 69.79-6.628zM389.432 606.319v0c13.749-13.749 35.363-15.7 51.352-4.635 5.331 3.688 10.014 6.536 14.049 8.542 42.649 21.211 93.092 21.405 135.884 0.581 4.331-2.107 9.382-5.156 15.155-9.146l0 0c16.018-11.071 37.659-9.111 51.427 4.657v0c12.383 12.383 12.383 32.459 0 44.842-1.101 1.101-2.281 2.119-3.532 3.047-13.135 9.744-24.196 16.769-33.182 21.075-64.229 30.776-140.044 29.304-203.163-4.418-6.784-3.624-14.983-9.053-24.597-16.286l0.001-0.001c-14.14-10.638-16.979-30.725-6.341-44.865 0.902-1.199 1.887-2.333 2.947-3.393z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["add-reaction"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":111,"id":13,"name":"add-reaction","prevSize":32,"code":59649},"setIdx":3,"setId":5,"iconIdx":103},{"icon":{"paths":["M786.466 512l54.306 54.306c14.996 14.996 14.996 39.31 0 54.306s-39.31 14.996-54.306 0l-54.306-54.306-54.306 54.306c-14.996 14.996-39.31 14.996-54.306 0s-14.996-39.31 0-54.306l54.306-54.306-54.306-54.306c-14.996-14.996-14.996-39.31 0-54.306s39.31-14.996 54.306 0l54.306 54.306 54.306-54.306c14.996-14.996 39.31-14.996 54.306 0s14.996 39.31 0 54.306l-54.306 54.306zM192 363.273v297.454h86.142l10.572 21.377c1.038 2.1 3.898 7.013 8.633 13.789 8.247 11.802 18.498 23.692 30.766 34.773 26.202 23.666 57.348 40.098 94.287 46.786v-531.14c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073zM115.2 686.327v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v614.4c0 21.208-17.192 38.4-38.4 38.4-74.44 0-135.902-26.351-184.163-69.939-19.215-17.354-33.789-34.802-44.168-50.134h-66.069c-28.277 0-51.2-22.923-51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Volume-disable"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":112,"id":12,"name":"Volume-disable","prevSize":32,"code":59752},"setIdx":3,"setId":5,"iconIdx":104},{"icon":{"paths":["M598.819 531.695l109.94-87.952 116.726-37.506c23.977-7.717 48.302-4.473 66.818 8.847 18.566 13.369 29.189 35.29 29.189 60.209v220.784c0 24.919-10.673 46.791-29.189 60.16-11.964 8.601-26.261 12.976-41.352 12.976-8.389 0-16.978-1.327-25.516-4.079l-152.153-48.954v51.819c0 28.277-22.923 51.2-51.2 51.2h-382.644l92.157-73.726h241.624c14.138 0 25.6-11.462 25.6-25.6v-188.179zM444.104 425.997h-241.641c-14.138 0-25.6 11.462-25.6 25.6v188.193l-74.463 59.57v-295.889c0-28.277 22.923-51.2 51.2-51.2h382.661l-92.157 73.726zM846.779 659.382l0.202-147.149c0.019-14.138-11.426-25.616-25.565-25.635-2.659-0.004-5.303 0.407-7.836 1.218l-140.298 44.887v105.968l140.059 45.047c13.459 4.329 27.88-3.073 32.209-16.532 0.811-2.522 1.226-5.154 1.229-7.803zM226.5 241.663c0-20.359 16.504-36.863 36.863-36.863h248.947c20.359 0 36.863 16.504 36.863 36.863s-16.504 36.863-36.863 36.863h-248.947c-20.359 0-36.863-16.504-36.863-36.863zM64.947 844.058c-13.246-16.554-10.565-40.713 5.99-53.959l0.003-0.002 708.040-566.432c16.558-13.246 40.719-10.563 53.967 5.994 13.246 16.554 10.565 40.713-5.99 53.959l-708.043 566.434c-16.558 13.246-40.719 10.563-53.967-5.994z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Video-off"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":113,"id":11,"name":"Video-off","prevSize":32,"code":59753},"setIdx":3,"setId":5,"iconIdx":105},{"icon":{"paths":["M272.433 838.351l34.767-213.14-149.132-152.849c-15.798-16.192-15.479-42.124 0.713-57.922 6.123-5.974 13.962-9.881 22.418-11.173l204.228-31.203 89.495-190.668c9.612-20.478 34.004-29.287 54.482-19.675 8.653 4.062 15.613 11.022 19.675 19.675l89.495 190.668 204.228 31.203c22.362 3.417 37.72 24.314 34.304 46.676-1.292 8.456-5.199 16.295-11.173 22.418l-149.132 152.849 34.767 213.14c3.642 22.327-11.505 43.378-33.832 47.020-9.068 1.479-18.369-0.133-26.41-4.578l-179.325-99.129-179.325 99.129c-19.798 10.944-44.719 3.767-55.664-16.032-4.445-8.041-6.057-17.343-4.578-26.41z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Star-filled"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":114,"id":10,"name":"Star-filled","prevSize":32,"code":59754},"setIdx":3,"setId":5,"iconIdx":106},{"icon":{"paths":["M315.97 527.941l-244.169-127.797c-30.836-16.139-26.016-61.689 7.515-71.017l809.701-225.253c32.369-9.005 59.876 24.965 44.339 54.754l-388.677 745.174c-16.096 30.859-61.652 26.103-71.027-7.414l-71.019-253.895 192.101-250.863-278.763 136.311z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Send-active"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":115,"id":9,"name":"send1","prevSize":32,"code":59650},"setIdx":3,"setId":5,"iconIdx":107},{"icon":{"paths":["M587.366 294.4v0c0 21.208 17.192 38.4 38.4 38.4h170.803v243.302h-499.456l126.239-131.373c14.572-15.165 14.581-39.128 0.020-54.304v0c-13.821-14.405-36.702-14.878-51.107-1.057-0.353 0.338-0.698 0.684-1.037 1.036l-183.531 190.972c-19.047 19.819-19.045 51.142 0.004 70.959l183.524 190.927c13.835 14.394 36.72 14.846 51.113 1.010 0.351-0.338 0.696-0.683 1.033-1.034v0c14.562-15.177 14.556-39.14-0.014-54.31l-130.597-135.975h526.49c28.277 0 51.2-22.923 51.2-51.2v-294.554c0-28.277-22.923-51.2-51.2-51.2h-193.485c-21.208 0-38.4 17.192-38.4 38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Multiline"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":116,"id":8,"name":"Multiline","prevSize":32,"code":59755},"setIdx":3,"setId":5,"iconIdx":108},{"icon":{"paths":["M511.269 687.543h65.829c28.277 0 51.2 22.923 51.2 51.2v131.657c0 28.277-22.923 51.2-51.2 51.2h-131.657c-28.277 0-51.2-22.923-51.2-51.2v-274.286c0-14.138 11.462-25.6 25.6-25.6h91.429v117.029zM419.84 219.429c-14.138 0-25.6-11.462-25.6-25.6v-65.829c0-14.138 11.462-25.6 25.6-25.6h65.829c14.138 0 25.6 11.462 25.6 25.6v91.429h-91.429zM511.269 336.457v-117.029h91.429c14.138 0 25.6 11.462 25.6 25.6v65.829c0 14.138-11.462 25.6-25.6 25.6h-91.429zM419.84 453.486c-14.138 0-25.6-11.462-25.6-25.6v-65.829c0-14.138 11.462-25.6 25.6-25.6h91.429v117.029h-91.429zM511.269 570.514v-117.029h91.429c14.138 0 25.6 11.462 25.6 25.6v65.829c0 14.138-11.462 25.6-25.6 25.6h-91.429zM478.354 863.086h65.829c14.138 0 25.6-11.462 25.6-25.6v-65.829c0-14.138-11.462-25.6-25.6-25.6h-65.829c-14.138 0-25.6 11.462-25.6 25.6v65.829c0 14.138 11.462 25.6 25.6 25.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-zip"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":117,"id":7,"name":"Files-zip","prevSize":32,"code":59756},"setIdx":3,"setId":5,"iconIdx":109},{"icon":{"paths":["M762.371 415.703c26.243-8.909 53.615-5.079 74.762 10.967 21.24 16.133 33.267 42.304 33.267 71.286v191.066c0 28.942-12.040 55.077-33.272 71.247-13.603 10.316-29.891 15.673-47.002 15.673-9.315 0-18.676-1.565-27.794-4.666l-95.568-70.287v67.011c0 28.277-22.923 51.2-51.2 51.2h-410.764c-28.277 0-51.2-22.923-51.2-51.2v-348.937c0-28.277 22.923-51.2 51.2-51.2h410.764c28.277 0 51.2 22.923 51.2 51.2v69.273l95.607-72.634zM306.614 204.8h207.127c28.277 0 51.2 22.923 51.2 51.2v8.658c0 28.277-22.923 51.2-51.2 51.2h-207.127c-28.277 0-51.2-22.923-51.2-51.2v-8.658c0-28.277 22.923-51.2 51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-video"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":118,"id":6,"name":"Files-video","prevSize":32,"code":59757},"setIdx":3,"setId":5,"iconIdx":110},{"icon":{"paths":["M870.138 150.751c-0.262-3.295-0.872-6.589-1.831-9.706-0.087-0.712-0.349-1.514-0.697-2.226 0.087-0.089 0.087-0.089 0-0.178-0.262-0.89-0.61-1.87-0.959-2.76-0.087-0.178-0.174-0.356-0.262-0.534-0.523-1.425-1.221-2.849-1.918-4.274-0.872-1.603-1.744-3.206-2.79-4.808-0.785-1.247-1.656-2.404-2.528-3.562-0.436-0.534-0.872-1.069-1.308-1.603-0.436-0.623-0.959-1.247-1.569-1.781-0.697-0.712-1.395-1.425-2.092-2.137-0.785-0.801-1.569-1.514-2.441-2.226-0.785-0.712-1.656-1.336-2.528-1.959-0.436-0.445-0.872-0.801-1.308-0.979-0.436-0.356-0.959-0.712-1.395-0.979 0 0-0.087-0.178-0.174-0.089-0.697-0.534-1.395-1.069-2.18-1.425-0.087-0.089-0.262-0.178-0.349-0.178-0.959-0.534-1.918-1.069-2.877-1.514-1.831-0.979-3.836-1.781-5.841-2.493-0.697-0.267-1.395-0.445-2.092-0.712-2.703-0.89-5.58-1.425-8.457-1.781-0.61-0.089-1.308-0.178-2.005-0.178-1.395-0.178-2.964-0.267-4.446-0.267-1.831 0-3.574 0.089-5.318 0.267-1.482 0.178-2.877 0.356-4.272 0.623-1.046 0.178-2.092 0.445-3.139 0.712l-400.421 102.489c-0.959 0.267-1.918 0.445-2.877 0.801-1.133 0.267-2.267 0.623-3.313 0.979-0.349 0.089-0.61 0.178-0.785 0.267-1.221 0.445-2.441 0.979-3.662 1.514-5.492 2.493-10.462 5.877-14.734 10.062-0.959 0.89-1.918 1.87-2.703 2.849-0.785 0.89-1.569 1.781-2.267 2.671-0.697 0.801-1.395 1.692-1.918 2.671-2.005 2.849-3.749 5.877-5.056 9.171-0.61 1.336-1.133 2.671-1.569 4.096-0.523 1.336-0.872 2.76-1.221 4.185-0.349 1.158-0.61 2.315-0.785 3.473-0.174 0.979-0.349 2.048-0.436 3.027-0.262 2.137-0.436 4.363-0.436 6.589v351.009c-16.39-6.055-34.088-9.261-52.483-9.261-86.658 0-157.1 72.036-157.1 160.545s70.442 160.456 157.1 160.456c84.827 0 153.961-69.009 156.838-155.025 0.174-1.781 0.262-3.562 0.262-5.432v-460.622l297.984-76.221v282.535c-16.39-6.055-34.088-9.261-52.483-9.261-86.658 0-157.1 72.036-157.1 160.456 0 88.509 70.442 160.545 157.1 160.545 84.844 0 153.961-69.098 156.829-155.051 0.183-1.808 0.27-3.633 0.27-5.494v-502.205c0-1.692-0.087-3.473-0.262-5.075z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-audio"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":119,"id":5,"name":"Files-audio","prevSize":32,"code":59758},"setIdx":3,"setId":5,"iconIdx":111},{"icon":{"paths":["M516.333 543.331c98.167 0 196.334 0 294.501 0 0.692 0 1.416 0 2.174 0v-0.001c16.019 0 29.005 12.986 29.005 29.005 0 0.81-0.034 1.619-0.102 2.426-16.192 192.934-179.943 346.664-376.779 346.664-207.415 0-378.093-170.699-378.093-378.093 0-193.91 149.176-355.712 338.083-375.96 19.856-2.128 37.677 12.243 39.805 32.098 0.137 1.28 0.206 2.566 0.206 3.854-0.001 0.858-0.001 1.681-0.001 2.47 0 95.446 0 190.891 0 286.337 0 28.277 22.923 51.2 51.2 51.2zM528.147 429.294c0-138.591 0-237.283 0-296.078 0-0.865 0-1.784 0-2.757h0.001c-0-14.921 12.096-27.017 27.017-27.017 0.694-0 1.388 0.027 2.080 0.080 89.608 6.919 174.258 45.618 238.256 109.597 62.121 62.139 100.425 143.748 108.934 230.474 1.813 18.478-11.697 34.927-30.175 36.74-1.091 0.107-2.186 0.161-3.283 0.161l-291.631-0.001c-28.277 0-51.2-22.923-51.2-51.199z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["File-keynote"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":120,"id":4,"name":"File-keynote","prevSize":32,"code":59759},"setIdx":3,"setId":5,"iconIdx":112},{"icon":{"paths":["M870.4 593.185h-251.079l-233.898-388.385h251.127l233.851 388.385zM369.040 242.949l125.516 218.472-215.393 374.724-125.563-218.329 215.44-374.867zM443.813 633.017h425.325l-125.563 237.383h-434.6l134.838-237.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["File-google-drive"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":121,"id":3,"name":"File-google-drive","prevSize":32,"code":59760},"setIdx":3,"setId":5,"iconIdx":113},{"icon":{"paths":["M962.166 471.314c14.179 25.106 14.179 56.265 0 81.371-90.173 159.672-258.39 266.514-450.166 266.514-191.696 0-359.95-106.767-450.166-266.513-14.179-25.107-14.179-56.266 0-81.373 90.173-159.672 258.39-266.514 450.166-266.514 191.696 0 359.951 106.767 450.166 266.514zM512 739.061c142.353 0 269.027-68.734 350.137-175.566 4.567-6.016 10.062-13.956 16.484-23.821 10.979-16.866 11.061-38.599 0.207-55.546-6.187-9.661-11.508-17.456-15.964-23.384-78.965-105.072-205.598-174.146-350.864-174.146-146.146 0-270.625 71.048-351.315 176.679-4.071 5.329-8.932 12.217-14.583 20.663-11.398 17.036-11.538 39.228-0.356 56.406l0.001-0c5.388 8.276 10.035 15.031 13.942 20.262 81.011 108.48 208.68 178.454 352.311 178.454zM512 665.6c-84.831 0-153.6-68.769-153.6-153.6s68.769-153.6 153.6-153.6c84.831 0 153.6 68.769 153.6 153.6s-68.769 153.6-153.6 153.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Eye"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":122,"id":2,"name":"Eye","prevSize":32,"code":59761},"setIdx":3,"setId":5,"iconIdx":114},{"icon":{"paths":["M668.989 710.098v0c13.020 12.835 13.17 33.795 0.335 46.815-0.111 0.112-0.222 0.224-0.335 0.335l-123.486 121.732c-19.934 19.651-51.954 19.651-71.888 0l-123.486-121.732c-13.020-12.835-13.17-33.795-0.335-46.815 0.111-0.112 0.222-0.224 0.335-0.335v0c13.263-13.074 34.566-13.074 47.829 0l75.713 74.637v-270.995c0-18.678 15.142-33.82 33.82-33.82v0c18.678 0 33.82 15.142 33.82 33.82v275.073l79.849-78.715c13.263-13.074 34.566-13.074 47.829 0zM767.936 278.65c87.194 14.313 153.664 89.026 153.664 179.043 0 100.249-82.439 181.517-184.132 181.517h-79.628c-18.413 0-33.34-14.927-33.34-33.34v0c0-18.413 14.927-33.34 33.34-33.34h79.628c64.337 0 116.492-51.414 116.492-114.837s-52.155-114.837-116.492-114.837h-33.82v-33.34c0-42.964-35.331-77.793-78.914-77.793-19.186 0-37.227 6.731-51.457 18.811l-26.834 22.78-21.85-27.473c-25.52-32.088-64.399-51.162-106.538-51.162-74.713 0-135.281 59.707-135.281 133.359 0 15.361 2.621 30.322 7.689 44.456l15.937 44.45h-57.446c-43.583 0-78.914 34.829-78.914 77.793s35.331 77.793 78.914 77.793h97.949c18.413 0 33.34 14.927 33.34 33.34v0c0 18.413-14.927 33.34-33.34 33.34h-97.949c-80.94 0-146.554-64.683-146.554-144.472 0-68.9 48.927-126.535 114.399-140.984-1.105-8.476-1.665-17.061-1.665-25.715 0-110.478 90.851-200.039 202.921-200.039 52.286 0 101.329 19.65 138.343 53.682 20.769-10.806 44.116-16.638 68.336-16.638 70.193 0 128.861 48.647 143.202 113.606z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Download"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":123,"id":1,"name":"Download","prevSize":32,"code":59762},"setIdx":3,"setId":5,"iconIdx":115},{"icon":{"paths":["M260.157 613.052c-12.627 8.1-29.429 4.431-37.53-8.196-2.808-4.377-4.3-9.467-4.3-14.667v-214.704c0-153.693 132.622-278.221 295.987-278.221 62.748 0 122.548 18.415 172.447 52.124 3.216 2.173 6.867 4.84 10.961 8.005 14.4 11.136 17.048 31.836 5.914 46.237-1.352 1.749-2.877 3.358-4.551 4.801-15.788 13.614-38.896 14.489-55.668 2.11-3.572-2.636-6.742-4.823-9.503-6.557-35.289-22.169-76.503-34.17-119.6-34.17-120.367 0-217.856 93.052-217.856 207.718v169.102c0 26.876-13.68 51.905-36.301 66.417zM812.546 744.975l-80.299-154.522v-128.551c0-16.386 7.137-31.96 19.548-42.659l16.174-13.942c10.733-9.252 26.933-8.051 36.185 2.682 4.015 4.658 6.223 10.602 6.223 16.752l-0.601 166.793 76.24 143.151c14.621 27.454 4.219 61.563-23.235 76.184-8.15 4.34-17.241 6.61-26.474 6.61h-175.36c-15.767 63.489-76.199 109.261-146.608 109.261-70.367 0-130.792-45.779-146.557-109.261h-30.326c-12.611 0-22.835-10.223-22.835-22.835 0-6.642 2.892-12.955 7.922-17.292l21.686-18.702c10.226-8.819 23.279-13.67 36.782-13.67h431.534zM122.16 849.45c-15.019-18.454-12.949-45.44 4.71-61.387l711.173-642.24c16.736-15.114 42.556-13.799 57.67 2.937 0.696 0.784 0.696 0.784 1.365 1.592 15.019 18.454 12.949 45.44-4.71 61.387l-711.173 642.24c-16.736 15.114-42.556 13.799-57.67-2.937-0.696-0.784-0.696-0.784-1.365-1.592zM512 854.645c23.566 0 44.551-11.762 55.954-29.929h-111.909c11.403 18.167 32.388 29.929 55.954 29.929z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Bell-off"],"colorPermutations":{"2552552551291162451":[{}],"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":124,"id":0,"name":"Bell-off","prevSize":32,"code":59763},"setIdx":3,"setId":5,"iconIdx":116}],"height":1024,"metadata":{"name":"custom"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"custom","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"noie8":true,"ie7":false,"showSelector":false,"showMetrics":false,"showMetadata":false,"showVersion":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon","name":"icomoon","height":32,"columns":16,"margin":16},"historySize":50,"showCodes":true,"gridSize":16}} \ No newline at end of file +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M336 304c0-97.202 78.797-176 176-176s176 78.798 176 176v142.477h16c53.020 0 96 42.98 96 96v257.523c0 53.020-42.98 96-96 96h-384c-53.020 0-96-42.98-96-96v-257.523c0-53.020 42.98-96 96-96h16v-142.477zM400 446.477h224v-142.477c0-61.856-50.145-112-112-112s-112 50.144-112 112v142.477zM320 510.477c-17.674 0-32 14.326-32 32v257.523c0 17.674 14.326 32 32 32h384c17.674 0 32-14.326 32-32v-257.523c0-17.674-14.326-32-32-32h-384z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["lock"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":146,"id":0,"name":"lock","prevSize":32,"code":59674},"setIdx":0,"setId":4,"iconIdx":0},{"icon":{"paths":["M623.090 896c-2.805 0-5.296-0.626-7.165-0.936-62.003-16.859-102.818-39.649-145.815-80.859-55.148-54.010-85.367-125.815-85.367-202.303 0-65.874 56.704-119.572 126.495-119.572s126.495 53.697 126.495 119.572c0 34.967 31.471 63.374 69.791 63.374 38.323 0 69.791-28.407 69.791-63.374 0-135.805-119.64-246.633-266.701-246.633-105 0-200.34 57.131-243.335 145.795-14.332 29.346-21.498 63.061-21.498 100.838 0 28.723 2.493 73.367 24.926 131.746 2.804 7.179 2.493 14.674-0.623 21.854-3.116 6.869-9.035 11.864-16.202 14.362-2.804 1.249-6.231 1.562-9.659 1.562-11.839 0-22.433-7.182-26.483-18.108-19.006-50.264-28.353-99.903-28.353-151.728 0-46.205 9.035-88.351 26.795-125.503 52.343-108.018 167.934-177.638 294.432-177.638 178.529 0 323.718 135.805 323.718 302.828 0 65.874-56.704 119.572-126.808 119.572-70.101 0-126.808-53.697-126.808-119.572 0-34.964-31.468-63.374-69.791-63.374-38.633 0-69.791 28.41-69.791 63.374 0 61.503 24.303 119.259 68.545 162.344 35.209 34.028 68.858 53.072 120.579 67.12 7.165 1.874 13.397 6.556 17.135 13.113s4.673 14.362 2.805 21.228c-2.805 11.864-14.020 20.918-27.108 20.918zM426.803 888.195c-7.791 0-15.266-3.12-20.252-8.741-33.337-32.779-51.721-54.010-77.892-100.528-26.795-47.141-41.128-105.207-41.128-167.336 0-116.449 100.949-211.356 224.953-211.356s224.953 94.908 224.953 211.356c0 15.61-12.464 28.097-28.353 28.097s-28.663-12.174-28.663-28.097c0-85.541-75.401-155.162-168.246-155.162-92.848 0-168.249 69.621-168.249 155.162 0 52.449 11.841 100.528 34.273 139.551 23.367 41.523 38.946 59.005 68.858 88.664 10.903 11.238 10.903 28.72 0 39.649-5.922 5.931-13.087 8.741-20.252 8.741zM699.736 818.887c-47.357 0-88.798-11.861-123.381-34.964-59.199-39.649-94.717-103.962-94.717-172.334 0-15.61 12.462-28.097 28.353-28.097 15.889 0 28.353 12.487 28.353 28.097 0 49.638 26.172 96.782 69.791 125.503 25.236 16.859 55.145 24.977 91.6 24.977 7.788 0 22.434-0.939 38.010-3.746 1.559-0.313 3.428-0.313 4.986-0.313 13.707 0 25.236 9.99 27.73 23.415 1.246 7.179-0.313 14.672-4.363 20.602-4.361 6.246-10.906 10.615-18.694 11.864-23.367 4.682-43.932 4.995-47.67 4.995zM188.765 435.826c-5.608 0-11.216-1.562-16.201-4.998-6.543-4.056-10.594-10.613-12.151-18.105-1.246-7.495 0.311-14.987 4.985-21.231 38.635-53.697 87.862-95.844 146.127-125.502 60.132-30.595 129.61-46.829 200.96-46.829 71.037 0 140.206 15.922 200.027 46.205 58.576 29.66 107.802 71.493 146.125 124.565 4.363 5.934 6.232 13.426 4.986 20.918s-5.609 14.049-11.841 18.421c-4.983 3.433-10.593 4.995-16.512 4.995-9.037 0-17.761-4.372-23.057-11.864-33.337-45.892-75.711-82.108-125.559-107.082-52.343-26.226-112.788-40.274-174.478-40.274-62.316 0-122.758 14.048-175.101 40.584-49.852 25.913-92.537 62.128-126.186 108.646-3.739 6.866-12.463 11.551-22.121 11.551zM733.696 239.142c-4.673 0-9.347-1.249-13.397-3.434-71.347-35.903-133.35-51.512-207.502-51.512-74.465 0-144.256 17.483-207.818 51.824-4.050 2.185-8.724 3.122-13.397 3.122-10.281 0-19.628-5.619-24.924-14.361-3.739-6.556-4.673-14.361-2.493-21.542s7.166-13.424 13.709-16.858c72.596-38.712 151.735-58.381 234.923-58.381 82.566 0 154.849 17.795 233.987 58.068 6.855 3.434 11.839 9.366 14.333 16.859 2.179 7.181 1.246 14.673-2.182 21.229-4.986 9.054-14.643 14.985-25.239 14.985z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["fingerprint"]},"attrs":[{}],"properties":{"order":143,"id":3,"name":"fingerprint","prevSize":32,"code":59656},"setIdx":1,"setId":3,"iconIdx":0},{"icon":{"paths":["M363.799 256l-188.344 256 188.344 256h468.201v-512h-468.201zM312.246 218.073c12.060-16.393 31.201-26.073 51.553-26.073h468.201c35.345 0 64 28.654 64 64v512c0 35.345-28.655 64-64 64h-468.201c-20.352 0-39.492-9.68-51.553-26.072l-188.343-256c-16.598-22.562-16.598-53.294 0-75.856l188.343-255.999zM737.781 361.375c-12.498-12.496-32.759-12.496-45.255 0l-110.95 110.95-110.948-110.95c-12.496-12.496-32.759-12.496-45.255 0-12.496 12.498-12.496 32.759 0 45.255l110.948 110.95-110.948 110.948c-12.496 12.498-12.496 32.759 0 45.255s32.759 12.496 45.255 0l110.948-110.948 110.95 110.948c12.496 12.496 32.757 12.496 45.255 0 12.496-12.496 12.496-32.757 0-45.255l-110.95-110.948 110.95-110.95c12.496-12.496 12.496-32.757 0-45.255z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["backspace"]},"attrs":[{}],"properties":{"order":142,"id":2,"name":"backspace","prevSize":32,"code":59673},"setIdx":1,"setId":3,"iconIdx":1},{"icon":{"paths":["M839.586 590.144l-115.443-56.653c-34.338-16.849-71.462-8.201-96.563 10.304-6.438 4.745-12.587 8.555-17.719 10.948-1.135 0.529-2.108 0.939-2.927 1.25-1.101-0.563-2.534-1.344-4.326-2.415-6.938-4.13-15.642-10.359-25.374-18.099-9.57-7.607-26.859-23.573-43.392-39.266-15.689-16.529-31.65-33.813-39.258-43.379-7.74-9.737-13.969-18.441-18.099-25.374-1.071-1.797-1.852-3.231-2.415-4.329 0.311-0.82 0.721-1.795 1.25-2.927 2.394-5.133 6.204-11.284 10.953-17.723 18.5-25.097 27.149-62.223 10.3-96.561l-56.653-115.444c-14.556-29.661-48.345-53.737-88.333-45.773-40.992 8.164-93.903 28.79-132.814 63.623-19.815 17.738-37.56 40.588-46.744 68.954-9.419 29.091-8.817 60.955 3.558 93.83 22.971 61.021 65.105 125.644 104.493 178.201 31.076 41.463 61.938 77.372 83.512 100.646l0.176 0.866c2.302 2.304 4.917 4.86 7.823 7.637 2.784 2.91 5.343 5.53 7.648 7.834l0.866 0.175c23.278 21.577 59.188 52.437 100.651 83.516 52.553 39.386 117.175 81.519 178.197 104.491 32.875 12.378 64.738 12.979 93.833 3.558 28.365-9.182 51.213-26.927 68.954-46.746 34.833-38.908 55.458-91.819 63.62-132.813 7.966-39.987-16.111-73.775-45.773-88.333zM471.885 558.165c2.726 2.825 5.299 5.44 7.671 7.812l1.579 0.986c12.407 11.605 27.251 24.759 42.142 36.599 11.268 8.96 23.42 17.86 35.11 24.828 9.762 5.82 26.103 14.647 43.827 15.522 17.698 0.87 33.702-4.83 45.009-10.108 12.006-5.602 23.194-12.873 32.444-19.695 2.014-1.485 4.002-2.172 5.363-2.359 0.708-0.098 1.079-0.051 1.216-0.021l112.299 55.108c-6.797 29.218-21.606 63.279-41.967 86.025-10.569 11.806-21.163 18.944-31.219 22.204-9.335 3.021-20.877 3.618-36.233-2.159-49.297-18.56-106.001-54.758-156.42-92.553-30.647-22.967-69.444-57.271-98.748-84.254-26.982-29.303-61.282-68.096-84.251-98.743-37.79-50.419-73.991-107.124-92.548-156.422-5.78-15.354-5.181-26.895-2.159-36.23 3.257-10.059 10.396-20.652 22.2-31.219 22.747-20.363 56.806-35.172 86.028-41.967l55.107 112.297c0.031 0.137 0.078 0.51-0.020 1.217-0.189 1.359-0.876 3.349-2.36 5.361-6.822 9.254-14.091 20.437-19.696 32.446-5.277 11.307-10.978 27.31-10.106 45.007 0.874 17.724 9.702 34.065 15.522 43.831 6.967 11.686 15.866 23.838 24.826 35.106 11.841 14.891 25 29.739 36.605 42.146l0.981 1.574c2.368 2.368 4.983 4.937 7.799 7.659z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":14,"id":124,"name":"phone","prevSize":32,"code":59653},"setIdx":2,"setId":2,"iconIdx":0},{"icon":{"paths":["M851.354 560.845c0 19.251-15.77 34.97-35.123 34.97-19.302 0-35.072-15.718-35.072-34.97v-93.030c0-19.251 15.77-34.97 35.072-34.97 19.354 0 35.123 15.718 35.123 34.97v93.030zM523.674 851.814h-23.398c-19.354 0-35.072-15.718-35.072-34.97s15.718-34.918 35.072-34.918h23.398c19.405 0 35.123 15.667 35.123 34.918s-15.718 34.97-35.123 34.97zM207.718 595.814c-19.354 0-35.123-15.718-35.123-34.97v-93.030c0-19.251 15.77-34.97 35.123-34.97s35.072 15.718 35.072 34.97v93.030c0 19.251-15.718 34.97-35.072 34.97zM816.23 363.059c-14.336 0-27.955 2.867-40.448 8.090-24.832-123.955-133.632-217.549-263.782-217.549s-238.95 93.594-263.834 217.549c-12.442-5.222-26.112-8.090-40.448-8.090-58.112 0-105.318 47.002-105.318 104.755v93.030c0 57.754 47.206 104.755 105.318 104.755 56.013 0 101.53-43.827 104.704-98.765h0.614v-141.824c0-111.155 89.242-201.626 198.963-201.626 109.67 0 198.912 90.47 198.912 201.626v189.133c0 69.12-34.15 130.816-88.525 167.424-14.643-40.397-53.146-69.427-98.714-69.427h-23.398c-58.061 0-105.318 46.95-105.318 104.704s47.258 104.755 105.318 104.755h23.398c41.011 0 76.237-23.654 93.645-57.754 85.606-36.506 145.664-114.022 160.358-205.773 11.981 4.71 24.934 7.526 38.554 7.526 58.112 0 105.37-47.002 105.37-104.755v-93.030c0-57.754-47.258-104.755-105.37-104.755z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["omnichannel"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":15,"id":123,"name":"omnichannel","prevSize":32,"code":59654},"setIdx":2,"setId":2,"iconIdx":1},{"icon":{"paths":["M520.051 195.702l223.927 259.764v365.909c0 5.892-4.774 10.667-10.667 10.667h-135.979v-149.376c0-23.565-19.102-42.667-42.667-42.667h-85.333c-23.565 0-42.667 19.102-42.667 42.667v149.376h-135.977c-5.891 0-10.667-4.774-10.667-10.667v-365.978l223.87-259.696c4.254-4.936 11.904-4.936 16.158 0zM160 577.617h56.023v243.759c0 41.237 33.429 74.667 74.667 74.667h442.622c41.237 0 74.667-33.429 74.667-74.667v-243.759h55.966c12.514 0 23.876-7.292 29.090-18.667s3.315-24.747-4.855-34.227l-319.654-370.809c-29.786-34.553-83.319-34.553-113.105 0l-319.657 370.809c-8.17 9.481-10.067 22.852-4.854 34.227s16.578 18.667 29.091 18.667z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["home"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":16,"id":122,"name":"home","prevSize":32,"code":59655},"setIdx":2,"setId":2,"iconIdx":2},{"icon":{"paths":["M511.152 360.583c22.304 0 40.392 18.051 40.392 40.327v233.944c0 22.275-18.088 40.327-40.392 40.327s-40.392-18.051-40.392-40.327v-233.944c0-22.275 18.088-40.327 40.392-40.327zM49.119 863.747l413.955-740.751c14.588-26.104 48.090-35.845 75.032-21.957 9.656 4.977 17.597 12.61 22.82 21.957l413.955 740.751c16.58 29.668-5.594 65.533-40.020 65.533h-845.723c-34.426 0-56.599-35.865-40.020-65.533zM510.258 703.736c24.735 0 43.707 19.504 43.707 44.561 0 25.225-18.915 44.785-43.707 44.785s-43.707-19.56-43.707-44.785c0-25.056 18.972-44.561 43.707-44.561zM140.057 855.673h743.887l-371.943-665.573-371.943 665.573z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["warning"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":18,"id":120,"name":"warning","prevSize":32,"code":59657},"setIdx":2,"setId":2,"iconIdx":4},{"icon":{"paths":["M192 363.273v297.454h86.142l10.572 21.377c1.038 2.1 3.898 7.013 8.633 13.789 8.247 11.802 18.498 23.692 30.766 34.773 26.202 23.666 57.348 40.098 94.287 46.786v-531.14c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073zM115.2 686.327v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v614.4c0 21.208-17.192 38.4-38.4 38.4-74.44 0-135.902-26.351-184.163-69.939-19.215-17.354-33.789-34.802-44.168-50.134h-66.069c-28.277 0-51.2-22.923-51.2-51.2zM671.024 687.393c-18.153-10.207-25.682-32.464-17.452-51.595 0.406-0.944 0.785-1.846 1.136-2.706 15.543-37.983 23.692-78.92 23.692-121.092 0-38.196-6.685-75.379-19.494-110.272-1.369-3.729-3.133-8.048-5.292-12.956-8.479-19.278-0.949-41.843 17.409-52.165 17.486-9.832 39.632-3.626 49.463 13.86 0.497 0.884 0.957 1.788 1.378 2.71 2.978 6.521 5.383 12.174 7.216 16.96 17.148 44.781 26.119 92.693 26.119 141.862 0 53.649-10.68 105.8-31.008 154.020-0.588 1.395-1.239 2.889-1.953 4.481l0.001 0.001c-8.25 18.402-29.857 26.632-48.259 18.382-1.008-0.452-1.995-0.949-2.958-1.491zM798.466 787.618c-18.347-10.316-25.51-33.12-16.356-52.074 1.158-2.395 2.188-4.585 3.090-6.569 30.65-67.393 46.8-140.98 46.8-216.975 0-77.121-16.632-151.762-48.171-219.966-0.535-1.158-1.118-2.392-1.747-3.702l0.001-0c-9.088-18.928-1.919-41.659 16.383-51.949 17.881-10.054 40.527-3.708 50.58 14.173 0.35 0.622 0.681 1.253 0.994 1.894 1.056 2.156 2.006 4.143 2.852 5.961 36.594 78.608 55.908 164.728 55.908 253.59 0 87.033-18.527 171.436-53.671 248.728-1.446 3.18-3.177 6.82-5.192 10.92l0.001 0c-9.039 18.386-31.271 25.964-49.657 16.926-0.614-0.302-1.219-0.62-1.815-0.955z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["volume"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":19,"id":119,"name":"volume","prevSize":32,"code":59658},"setIdx":2,"setId":2,"iconIdx":5},{"icon":{"paths":["M226.167 660.727l-81.802 71.829c-17.249-8.236-29.166-25.841-29.166-46.229v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v216.18l-76.8 67.437v-242.104c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073v297.454h34.167zM355.626 751.596c19.822 12.498 41.994 21.37 66.774 25.856 0-20.591 0-36.035 0-46.33 0-8.481 0-21.202 0-38.163l76.8-67.442c0 26.928 0 47.125 0 60.589 0 29.577 0 73.941 0 133.095 0 21.208-17.192 38.4-38.4 38.4-64.739 0-119.664-19.931-164.625-53.797l59.451-52.207zM675.736 470.492l66.722-58.592c8.406 32.339 12.742 65.916 12.742 100.1 0 53.649-10.68 105.8-31.008 154.020-0.588 1.395-1.239 2.889-1.953 4.481l0.001 0.001c-8.25 18.402-29.857 26.632-48.259 18.382-1.008-0.452-1.995-0.949-2.958-1.491-18.153-10.207-25.682-32.464-17.452-51.595 0.406-0.944 0.785-1.846 1.136-2.706 15.543-37.983 23.692-78.92 23.692-121.092 0-13.993-0.897-27.85-2.664-41.508zM807.938 354.398l62.315-54.722c25.313 67.093 38.546 138.769 38.546 212.324 0 87.033-18.527 171.436-53.671 248.728-1.446 3.18-3.177 6.82-5.192 10.92l0.001 0c-9.039 18.386-31.271 25.964-49.657 16.926-0.614-0.302-1.219-0.62-1.815-0.955-18.347-10.316-25.51-33.12-16.356-52.074 1.158-2.395 2.188-4.585 3.090-6.569 30.65-67.393 46.8-140.98 46.8-216.975 0-54.218-8.22-107.21-24.062-157.602zM946.503 130.461c14.061 14.993 13.306 38.546-1.687 52.607-0.296 0.278-0.597 0.551-0.902 0.819l-811.178 712.336c-16.116 14.152-40.553 12.962-55.218-2.688-14.067-15.013-13.301-38.587 1.712-52.654 0.293-0.274 0.59-0.544 0.892-0.809l811.176-712.287c16.11-14.146 40.538-12.962 55.205 2.676z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["volume-mute"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":20,"id":118,"name":"volume-mute","prevSize":32,"code":59659},"setIdx":2,"setId":2,"iconIdx":6},{"icon":{"paths":["M846.779 659.382l0.202-147.149c0.019-14.138-11.426-25.616-25.565-25.635-2.659-0.004-5.303 0.407-7.836 1.218l-140.298 44.887v105.968l140.059 45.047c13.459 4.329 27.88-3.073 32.209-16.532 0.811-2.522 1.226-5.154 1.229-7.803zM598.819 719.874v-268.278c0-14.138-11.462-25.6-25.6-25.6h-370.756c-14.138 0-25.6 11.462-25.6 25.6v268.278c0 14.138 11.462 25.6 25.6 25.6h370.756c14.138 0 25.6-11.462 25.6-25.6zM892.302 415.085c18.566 13.369 29.189 35.29 29.189 60.209v220.784c0 24.919-10.673 46.791-29.189 60.16-11.964 8.601-26.261 12.976-41.352 12.976-8.389 0-16.978-1.327-25.516-4.079l-152.153-48.954v51.819c0 28.277-22.923 51.2-51.2 51.2h-468.482c-28.277 0-51.2-22.923-51.2-51.2v-364.529c0-28.277 22.923-51.2 51.2-51.2h468.482c28.277 0 51.2 22.923 51.2 51.2v51.672l152.202-48.905c23.977-7.717 48.302-4.473 66.818 8.847zM226.5 241.663v0c0-20.359 16.504-36.863 36.863-36.863h248.947c20.359 0 36.863 16.504 36.863 36.863v0c0 20.359-16.504 36.863-36.863 36.863h-248.947c-20.359 0-36.863-16.504-36.863-36.863z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["video"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":21,"id":117,"name":"video","prevSize":32,"code":59660},"setIdx":2,"setId":2,"iconIdx":7},{"icon":{"paths":["M218.87 797.637c9.606 3.608 21.53 7.28 35.985 10.849 59.123 14.598 143.466 23.514 257.146 23.514s198.023-8.916 257.146-23.514c14.454-3.569 26.378-7.241 35.985-10.849-6.496-56.39-38.135-87.347-99.936-113.172-8.559-3.577-17.471-6.998-29.283-11.314 2.748 1.004-22.903-8.286-29.518-10.774-60.736-22.842-89.594-44.548-89.484-88.671-0.075-1.028-0.075-1.028-0.204-3.673-0.731-17.527 0.562-37.647 5.737-57.807 5.146-20.046 13.648-37.544 27.874-52.383 33.963-33.045 49.684-67.214 49.684-118.501 0-83.443-58.409-149.344-128-149.344s-128 65.901-128 149.344c0 51.244 15.357 84.513 49.491 119.13 13.437 13.696 22.254 30.903 27.646 50.494 5.659 20.562 7.040 41.089 6.179 59.081-0.163 2.919-0.163 2.919-0.116 0.759 0 47.022-28.858 68.728-89.594 91.57-6.615 2.488-32.266 11.777-29.518 10.774-11.812 4.315-20.724 7.737-29.283 11.314-61.801 25.825-93.44 56.782-99.936 113.172zM140.8 821.133c0-107.358 52.685-167.534 148.394-207.529 9.82-4.104 19.726-7.907 32.54-12.589-2.308 0.843 22.741-8.229 28.838-10.521 31.709-11.925 39.829-18.033 39.971-22.993-0.015 0.223-0.015 0.223 0.061-1.121 0.507-10.589-0.369-23.607-3.513-35.032-2.19-7.958-5.172-13.777-8.354-17.020-47.496-48.169-71.537-100.249-71.537-172.984 0-123.933 90.591-226.144 204.8-226.144s204.8 102.211 204.8 226.144c0 72.96-24.682 126.604-71.953 172.563-2.969 3.104-5.88 9.097-8.016 17.416-2.989 11.645-3.838 24.838-3.392 35.51 0.063 1.261 0.063 1.261 0.161 3.976 0 1.653 8.12 7.76 39.829 19.686 6.097 2.293 31.146 11.365 28.838 10.521 12.813 4.681 22.719 8.485 32.54 12.589 95.71 39.995 148.394 100.171 148.394 207.529v19.301l-15.489 11.516c-12.629 9.39-38.034 20.697-80.156 31.098-65.585 16.194-156.017 25.753-275.556 25.753s-209.97-9.559-275.556-25.753c-42.121-10.4-67.526-21.708-80.156-31.098l-15.489-11.516v-19.301z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":22,"id":116,"name":"user","prevSize":32,"code":59661},"setIdx":2,"setId":2,"iconIdx":8},{"icon":{"paths":["M839.68 812.373c0 11.469-7.1 21.19-17.094 25.231-11.414-68.704-54.668-111.138-124.191-140.138-11.196-4.588-47.732-18.022-51.555-19.333-13.053-4.97-21.245-8.738-25.832-11.851-0.164-7.591 0.492-16.876 2.567-24.904 1.365-5.352 3.058-8.793 4.205-9.994 40.305-39.103 61.658-85.415 61.658-147.838 0-106.441-78.316-194.533-177.439-194.533s-177.439 88.091-177.439 194.533c0 62.205 20.808 107.151 61.44 148.275 1.311 1.365 3.058 4.697 4.424 9.776 2.185 7.864 2.895 17.094 2.621 24.685-4.588 3.113-12.78 6.881-25.887 11.851-3.768 1.311-40.359 14.746-51.5 19.333-69.523 29-112.777 71.434-124.245 140.138-9.994-3.987-17.094-13.763-17.094-25.231v-600.747c0-15.073 12.288-27.307 27.307-27.307h600.747c15.073 0 27.307 12.233 27.307 27.307v600.747zM405.504 754.975c53.084-19.988 79.244-39.649 79.299-83.831 0.765-15.674-0.437-33.369-5.407-51.337-4.806-17.531-12.78-32.986-25.068-45.493-26.324-26.651-37.847-51.5-37.847-90.767 0-63.188 43.964-112.613 95.519-112.613 51.61 0 95.519 49.425 95.519 112.613 0 39.267-11.742 64.771-37.792 90.112-13.271 13.708-20.862 29.437-25.504 47.35-4.478 17.531-5.625 34.843-4.97 50.080 0.109 2.348 0.109 2.348 0.164 3.331-0.109 40.905 26.051 60.566 79.080 80.555 4.314 1.529 38.939 14.199 48.387 18.132 40.25 16.766 63.242 35.717 72.363 66.574h-454.492c9.175-30.857 32.113-49.807 72.417-66.574 9.448-3.932 43.964-16.548 48.333-18.132zM812.373 102.4h-600.747c-60.293 0-109.227 48.934-109.227 109.227v600.747c0 60.348 48.934 109.227 109.227 109.227h600.747c60.293 0 109.227-48.879 109.227-109.227v-600.747c0-60.293-48.934-109.227-109.227-109.227z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-rounded"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":23,"id":115,"name":"user-rounded","prevSize":32,"code":59662},"setIdx":2,"setId":2,"iconIdx":9},{"icon":{"paths":["M331.532 803.498c38.087 9.46 92.556 15.252 166.070 15.252s127.983-5.792 166.070-15.252c1.811-0.45 3.786-0.977 5.925-1.581l-0.001-0.003c8.162-2.306 12.908-10.792 10.602-18.954-0.067-0.237-0.14-0.472-0.218-0.706-3.314-9.883-6.801-17.137-10.462-21.764-10.418-13.167-26.15-23.175-47.822-32.286-8.815-3.705-44.918-16.974-38.047-14.375-40.557-15.344-60.132-30.155-60.053-60.463-0.052-0.716-0.052-0.716-0.138-2.494-0.485-11.695 0.371-25.086 3.81-38.562 3.453-13.533 9.183-25.394 18.894-35.583 21.483-21.027 31.345-42.589 31.345-75.256 0-52.915-36.621-94.48-79.904-94.48s-79.904 41.565-79.904 94.48c0 32.647 9.636 53.647 31.27 75.718 9.119 9.35 15.063 21.018 18.683 34.251 3.765 13.763 4.68 27.441 4.108 39.46-0.072 32.774-19.646 47.586-60.202 62.929 6.863-2.597-29.235 10.67-38.049 14.375-22.603 9.502-38.745 19.98-49.136 34-3.225 4.352-6.321 11.060-9.289 20.124l0 0c-2.639 8.062 1.757 16.737 9.819 19.376 0.197 0.064 0.395 0.125 0.595 0.181 2.181 0.617 4.192 1.155 6.034 1.612zM279.485 265.898h79.904c17.891 0 32.394 14.503 32.394 32.394v0c0 17.891-14.503 32.394-32.394 32.394h-79.904v79.904c0 17.891-14.503 32.394-32.394 32.394v0c-17.891 0-32.394-14.503-32.394-32.394v-79.904h-79.904c-17.891 0-32.394-14.503-32.394-32.394v0c0-17.891 14.503-32.394 32.394-32.394h79.904v-79.904c0-17.891 14.503-32.394 32.394-32.394v0c17.891 0 32.394 14.503 32.394 32.394v79.904zM413.493 626.257c-1.3-4.751-3.012-8.113-4.672-9.815-31.342-31.976-47.271-66.689-47.271-114.972 0-82.454 60.066-150.629 136.053-150.629s136.053 68.174 136.053 150.629c0 48.437-16.356 84.197-47.509 114.663-1.524 1.603-3.201 5.075-4.473 10.059-1.859 7.285-2.393 15.64-2.115 22.352 0.038 0.772 0.038 0.772 0.107 2.714 0-0.642 4.42 2.703 23.851 10.054-8.126-3.074 29.733 10.84 39.938 15.129 63.15 26.546 98.179 66.795 98.179 138.222v14.061l-11.26 8.421c-8.556 6.399-25.434 13.956-53.167 20.845-42.837 10.64-101.782 16.908-179.606 16.908s-136.769-6.268-179.606-16.908c-27.733-6.888-44.611-14.445-53.167-20.845l-11.26-8.421v-14.061c0-71.428 35.029-111.676 98.179-138.222 10.204-4.29 48.064-18.204 39.938-15.129 19.162-7.249 23.726-10.602 23.948-12.384 0.355-7.334-0.195-15.555-2.142-22.672zM843.638 623.533c1.973-0.49 4.14-1.072 6.5-1.745l0.001 0.003c8.159-2.327 12.888-10.827 10.561-18.986-0.014-0.050-0.029-0.101-0.044-0.151-1.569-5.295-3.144-9.409-4.725-12.341-9.83-18.223-27.528-30.833-54.27-42.074-8.814-3.705-44.912-16.972-38.049-14.375-40.556-15.344-60.13-30.155-60.202-62.929-0.572-12.019 0.343-25.697 4.108-39.46 3.62-13.233 9.564-24.901 18.683-34.251 21.634-22.071 31.27-43.071 31.27-75.718 0-52.915-36.621-94.48-79.904-94.48-26.417 0-50.352 15.483-64.995 39.579-6.233 10.257-11.202 23.644-14.91 40.161h-56.149c2.611-17.627 6.283-32.54 11.016-44.737 20.7-53.351 68.455-91.152 125.038-91.152 75.987 0 136.053 68.174 136.053 150.629 0 48.284-15.929 82.996-47.271 114.972-1.66 1.702-3.372 5.064-4.672 9.815-1.947 7.117-2.497 15.338-2.142 22.672 0.222 1.781 4.786 5.134 23.948 12.384-8.126-3.074 29.733 10.84 39.938 15.129 63.15 26.546 98.179 66.795 98.179 138.222v0c0 8.85-4.173 17.182-11.26 22.482-8.556 6.399-25.434 13.956-53.167 20.845-18.942 4.705-41.378 8.602-67.626 11.477-11.504 1.26-25.438 2.685-41.802 4.273v-56.134c13.095-1.465 24.991-2.783 35.688-3.954 23.822-2.609 43.786-6.077 60.204-10.155z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-plus"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":24,"id":114,"name":"user-plus","prevSize":32,"code":59663},"setIdx":2,"setId":2,"iconIdx":10},{"icon":{"paths":["M806.86 327.115c99.002 16.485 174.473 102.538 174.473 206.218 0 115.464-93.602 209.067-209.067 209.067h-62.587c-21.208 0-38.4-17.192-38.4-38.4s17.192-38.4 38.4-38.4h62.587c73.049 0 132.267-59.218 132.267-132.267s-59.218-132.267-132.267-132.267h-38.4v-38.4c0-49.485-40.115-89.6-89.6-89.6-21.784 0-42.268 7.752-58.425 21.666l-30.468 26.237-24.808-31.643c-28.976-36.958-73.12-58.927-120.965-58.927-84.831 0-153.6 68.769-153.6 153.6 0 17.692 2.976 34.924 8.73 51.203l18.095 51.197h-65.226c-49.485 0-89.6 40.115-89.6 89.6s40.115 89.6 89.6 89.6h65.228c21.208 0 38.4 17.192 38.4 38.4s-17.192 38.4-38.4 38.4h-65.228c-91.9 0-166.4-74.5-166.4-166.4 0-79.358 55.552-145.741 129.89-162.382-1.255-9.763-1.89-19.651-1.89-29.618 0-127.246 103.154-230.4 230.4-230.4 59.366 0 115.051 22.632 157.077 61.83 23.581-12.446 50.090-19.163 77.59-19.163 79.698 0 146.31 56.030 162.594 130.849zM322.543 590.437c-14.996-14.996-14.996-39.31 0-54.306l144.815-144.815c19.995-19.995 52.413-19.995 72.408 0l144.815 144.815c14.996 14.996 14.996 39.31 0 54.306s-39.31 14.996-54.306 0l-85.965-85.965v312.679c0 21.208-17.192 38.4-38.4 38.4s-38.4-17.192-38.4-38.4v-317.376l-90.662 90.662c-14.996 14.996-39.31 14.996-54.306 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["upload"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":25,"id":113,"name":"upload","prevSize":32,"code":59664},"setIdx":2,"setId":2,"iconIdx":11},{"icon":{"paths":["M837.448 345.039c0.456 7.302 0.456 14.558 0.456 21.814 0 221.97-168.896 477.711-477.665 477.711-95.15 0-183.5-27.564-257.84-75.39 13.508 1.597 26.468 2.099 40.524 2.099 78.493 0 150.779-26.514 208.462-71.739-71.817-1.308-134.88-48.073-156.986-116.416 10.405 1.552 20.81 2.556 31.717 2.556 15.060 0 30.119-2.054 44.175-5.704-78.372-15.862-134.692-84.782-134.624-164.744v-2.099c22.361 12.458 48.373 20.262 75.892 21.312-46.789-31.135-74.885-83.625-74.842-139.827 0-31.215 8.306-59.782 22.818-84.745 85.298 104.986 211.14 168.844 346.235 175.696-2.693-12.648-4.085-25.539-4.153-38.471-0.036-44.539 17.641-87.264 49.134-118.758s74.219-49.171 118.758-49.134c46.476-0.11 90.907 19.098 122.668 53.028 37.601-7.249 73.657-20.976 106.559-40.57-12.513 38.803-38.75 71.709-73.792 92.548 33.335-3.805 65.914-12.573 96.656-26.012-22.981 33.478-51.447 62.838-84.197 86.844h0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["twitter"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":26,"id":112,"name":"twitter","prevSize":32,"code":59665},"setIdx":2,"setId":2,"iconIdx":12},{"icon":{"paths":["M622.040 176.873h147.318c41.655 0 75.442 33.116 75.442 74.428v65.873c0 25.466-18.591 46.589-42.942 50.537v479.479c0 41.057-33.95 74.409-75.255 74.409h-429.207c-41.529 0-75.255-33.3-75.255-74.409v-468.242c0-3.775 0.287-7.484 0.84-11.109-24.762-3.594-43.782-24.909-43.782-50.667v-65.873c0-41.17 33.806-74.428 75.442-74.428h147.318c17.178-43.592 59.97-74.473 110.040-74.473s92.862 30.881 110.040 74.473zM544.212 176.873c-8.972-6.68-20.125-10.639-32.212-10.639s-23.24 3.959-32.212 10.639h64.423zM737.445 368.374h-440.049c-5.855 0-10.842 4.899-10.842 10.575v468.242c0 5.802 4.834 10.575 10.842 10.575h429.207c5.855 0 10.842-4.899 10.842-10.575v-478.817zM297.397 304.54h482.991v-53.239c0-5.9-4.79-10.595-11.029-10.595h-514.715c-6.167 0-11.029 4.784-11.029 10.595v53.239h53.784zM426.116 474.764c17.787 0 32.206 14.419 32.206 32.206v212.2c0 17.787-14.419 32.206-32.206 32.206s-32.206-14.419-32.206-32.206v-212.2c0-17.787 14.419-32.206 32.206-32.206zM597.884 474.764c17.787 0 32.206 14.419 32.206 32.206v212.2c0 17.787-14.419 32.206-32.206 32.206s-32.206-14.419-32.206-32.206v-212.2c0-17.787 14.419-32.206 32.206-32.206z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["trash"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":27,"id":111,"name":"trash","prevSize":32,"code":59666},"setIdx":2,"setId":2,"iconIdx":13},{"icon":{"paths":["M517.642 847.16c230.298 0.768 301.875-37.171 323.174-61.645-5.222-3.123-11.674-6.502-16.691-9.114-32.41-17.101-108.544-57.19-69.837-140.442 3.942-9.472 9.062-20.48 14.234-31.642 14.899-31.898 28.979-62.106 28.979-92.314 0-184.832-150.682-335.206-335.872-335.206s-335.872 150.374-335.872 335.206c0 194.202 160.87 335.155 382.515 335.155h9.37zM858.941 710.61c26.47 13.926 81.459 42.906 56.218 96.461-47.77 101.427-241.050 114.534-387.328 114.534h-10.547l-9.011-0.051c-264.806 0-457.062-172.237-457.062-409.549 0-225.894 184.115-409.6 410.419-409.6s410.47 183.706 410.47 409.6c0 46.694-19.098 87.654-35.994 123.75-4.71 10.138-9.37 20.173-13.517 30.054-7.27 15.77-9.472 20.582 36.352 44.8zM288.922 439.731v0c0-20.543 16.654-37.197 37.197-37.197h252.211c20.543 0 37.197 16.654 37.197 37.197v0c0 20.543-16.654 37.197-37.197 37.197h-252.211c-20.543 0-37.197-16.654-37.197-37.197zM288.922 563.712v0c0-20.557 16.665-37.222 37.222-37.222h293.53c20.557 0 37.222 16.665 37.222 37.222v0c0 20.557-16.665 37.222-37.222 37.222h-293.53c-20.557 0-37.222-16.665-37.222-37.222z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["thread"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":28,"id":110,"name":"thread","prevSize":32,"code":59667},"setIdx":2,"setId":2,"iconIdx":14},{"icon":{"paths":["M792.869 153.6c71.013 0 128.731 57.020 128.731 127.174v462.452c0 70.154-57.718 127.174-128.731 127.174h-561.737c-71.013 0-128.731-57.020-128.731-127.174v-462.452c0-70.154 57.718-127.174 128.731-127.174h561.737zM792.869 801.032c32.253 0 58.514-25.944 58.514-57.806v-104.052h-444.709v161.858h386.194zM172.617 743.226c0 31.863 26.261 57.806 58.514 57.806h105.326v-161.858h-163.84v104.052zM231.131 222.968c-32.253 0-58.514 25.944-58.514 57.806v57.806h163.84v-115.613h-105.326zM851.383 280.774c0-31.863-26.261-57.806-58.514-57.806h-386.194v115.613h444.709v-57.806zM172.617 569.806h163.84v-161.858h-163.84v161.858zM406.674 569.806h444.709v-161.858h-444.709v161.858z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["th-list"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":29,"id":109,"name":"th-list","prevSize":32,"code":59668},"setIdx":2,"setId":2,"iconIdx":15},{"icon":{"paths":["M351.569 797.703c36.794 9.632 89.413 15.529 160.431 15.529s123.637-5.897 160.431-15.529c0 0 0 0 0 0v0c10.921-2.859 17.456-14.029 14.598-24.95-0.149-0.571-0.323-1.135-0.522-1.69-2.676-7.499-5.463-13.186-8.363-17.061-10.064-13.449-25.282-23.663-46.264-32.959-8.515-3.773-43.393-17.282-36.755-14.635-39.18-15.622-58.090-30.703-58.014-61.561-0.050-0.729-0.050-0.729-0.133-2.539-0.469-11.908 0.358-25.541 3.68-39.262 3.336-13.778 8.871-25.855 18.252-36.229 20.753-21.408 30.28-43.362 30.28-76.622 0-53.876-35.378-96.194-77.191-96.194s-77.191 42.319-77.191 96.194c0 33.24 9.309 54.621 30.208 77.092 8.809 9.52 14.551 21.4 18.049 34.873 3.638 14.012 4.521 27.939 3.969 40.176-0.070 33.369-18.979 48.449-58.158 64.071 6.63-2.644-28.242 10.864-36.757 14.636-24.379 10.801-40.978 22.842-50.75 39.757-1.444 2.499-2.87 5.803-4.278 9.913l-0.001-0c-3.741 10.919 2.078 22.803 12.997 26.544 0.488 0.167 0.982 0.316 1.481 0.447 0 0 0 0 0 0zM430.746 617.245c-1.256-4.837-2.91-8.26-4.514-9.993-30.278-32.556-45.666-67.899-45.666-117.059 0-83.951 58.027-153.363 131.434-153.363s131.434 69.412 131.434 153.363c0 49.316-15.8 85.725-45.896 116.745-1.472 1.632-3.093 5.167-4.321 10.241-1.796 7.417-2.312 15.924-2.043 22.758 0.037 0.786 0.037 0.786 0.103 2.763 0-0.653 4.27 2.752 23.042 10.237-7.85-3.13 28.724 11.036 38.581 15.404 61.006 27.028 94.846 68.007 94.846 140.731v0.465c0 8.746-4.009 17.010-10.878 22.425-8.266 6.515-24.57 14.21-51.362 21.223-41.382 10.833-98.326 17.215-173.507 17.215s-132.124-6.382-173.507-17.215c-26.791-7.013-43.096-14.708-51.362-21.223v0c-6.869-5.414-10.878-13.678-10.878-22.425v-0.465c0-72.724 33.84-113.703 94.846-140.731 9.858-4.367 46.432-18.534 38.581-15.404 18.511-7.381 22.92-10.795 23.135-12.609 0.343-7.468-0.188-15.837-2.069-23.084zM177.715 614.472c15.86 4.152 35.147 7.683 58.16 10.34 10.333 1.193 27.284 2.535 50.853 4.026l-2.452 57.11c-25.091-1.589-43.193-3.025-54.307-4.308-25.357-2.927-47.031-6.895-65.329-11.685-26.791-7.013-43.096-14.708-51.362-21.223v0c-6.869-5.414-10.878-13.678-10.878-22.425v-0.465c0-72.724 33.84-113.703 94.846-140.731 9.832-4.356 46.236-18.458 38.644-15.429 18.458-7.364 22.858-10.772 23.072-12.584 0.343-7.468-0.188-15.837-2.069-23.084-1.256-4.837-2.91-8.26-4.514-9.993-30.278-32.556-45.666-67.899-45.666-117.059 0-83.951 58.027-153.363 131.434-153.363 54.993 0 101.354 38.956 121.153 93.797 4.42 12.242 8.54 25.746 12.362 40.512h-54.242c-4.898-13.865-10.304-25.99-16.217-36.376-14.12-24.801-37.374-40.765-63.056-40.765-41.813 0-77.191 42.319-77.191 96.194 0 33.24 9.309 54.621 30.208 77.092 8.809 9.52 14.551 21.4 18.049 34.873 3.638 14.012 4.521 27.939 3.969 40.176-0.070 33.369-18.979 48.449-58.158 64.071 6.63-2.644-28.242 10.864-36.757 14.636-27.095 12.004-44.579 25.54-53.763 45.595-1.181 2.579-2.371 6.104-3.57 10.575l0.001 0c-2.15 8.018 2.459 16.294 10.409 18.686 2.317 0.697 4.441 1.299 6.373 1.804zM846.285 614.472c1.918-0.502 4.026-1.099 6.324-1.79l0.001 0.002c7.966-2.396 12.578-10.697 10.404-18.727-1.339-4.944-2.674-8.809-4.004-11.593-9.309-19.484-26.653-32.758-53.275-44.553-8.515-3.772-43.387-17.28-36.757-14.636-39.179-15.622-58.089-30.703-58.158-64.071-0.552-12.237 0.331-26.164 3.969-40.176 3.497-13.473 9.24-25.353 18.049-34.873 20.899-22.472 30.208-43.852 30.208-77.092 0-53.876-35.378-96.194-77.191-96.194-25.224 0-48.105 15.4-62.292 39.447-6.22 10.544-11.207 23.109-14.96 37.694h-54.242c2.344-14.034 5.613-27.037 9.806-39.008 19.489-55.631 66.193-95.301 121.688-95.301 73.407 0 131.434 69.412 131.434 153.363 0 49.16-15.388 84.503-45.666 117.059-1.603 1.733-3.258 5.156-4.514 9.993-1.881 7.246-2.412 15.616-2.069 23.084 0.214 1.814 4.624 5.228 23.135 12.609-7.85-3.13 28.724 11.036 38.581 15.404 61.006 27.028 94.846 68.007 94.846 140.731v0.465c0 8.746-4.009 17.010-10.878 22.425-8.266 6.515-24.57 14.21-51.362 21.223-18.298 4.79-39.973 8.758-65.329 11.685-11.114 1.283-19.465 2.719-25.054 4.308v-57.11c40.965-6.809 66.734-11.598 77.308-14.366z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["team"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":30,"id":108,"name":"team","prevSize":32,"code":59669},"setIdx":2,"setId":2,"iconIdx":16},{"icon":{"paths":["M446.005 550.4h-190.005c-21.208 0-38.4-17.192-38.4-38.4v0c0-21.208 17.192-38.4 38.4-38.4h96.267c-5.026-13.916-7.479-29.497-7.479-46.85 0-83.525 68.25-139.1 170.625-139.1 74.737 0 133.658 34.233 157.596 87.145 1.273 2.813 2.497 6.146 3.673 9.997l-0.001 0c4.669 15.294-3.945 31.478-19.239 36.147-2.74 0.836-5.589 1.262-8.454 1.262v0c-19.004 0-36.432-10.569-45.215-27.422-0.851-1.631-1.703-3.076-2.558-4.334-16.494-24.267-47.195-38.444-86.777-38.444-57.525 0-95.875 27.625-95.875 69.875 0 22.951 11.611 39.18 38.604 51.725h310.834c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-106.987c21.256 21.227 30.874 48.315 30.874 83.050 0 89.7-69.55 145.925-180.7 145.925-74.662 0-131.736-27.637-159.919-74.254-3.838-6.348-7.406-15.091-10.705-26.229l0-0c-4.519-15.256 4.186-31.287 19.443-35.806 2.656-0.787 5.412-1.186 8.182-1.186v0c19.865 0 38.134 10.884 47.59 28.355 3.288 6.073 6.708 10.829 10.262 14.268 19.781 19.141 51.485 30.178 90.673 30.178 58.5 0 101.4-30.225 101.4-71.825 0-35.75-27.3-57.2-89.375-71.825l-60.45-14.625c-7.818-1.827-15.243-3.834-22.283-6.025z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["strike"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":31,"id":107,"name":"strike","prevSize":32,"code":59670},"setIdx":2,"setId":2,"iconIdx":17},{"icon":{"paths":["M512 702.505l136.962 75.711c7.424 4.104 16.77 1.412 20.874-6.012 1.667-3.015 2.271-6.503 1.717-9.904l-26.352-161.551 112.325-115.124c5.924-6.072 5.804-15.796-0.267-21.721-2.296-2.24-5.236-3.705-8.407-4.19l-154.529-23.61-68.418-145.764c-3.604-7.679-12.752-10.983-20.431-7.378-3.245 1.523-5.855 4.133-7.378 7.378l-68.418 145.764-154.529 23.61c-8.386 1.281-14.145 9.118-12.864 17.504 0.484 3.171 1.95 6.111 4.19 8.407l112.325 115.124-26.352 161.551c-1.366 8.372 4.314 16.267 12.687 17.632 3.4 0.555 6.889-0.050 9.904-1.717l136.962-75.711zM272.433 838.351l34.767-213.14-149.132-152.849c-15.798-16.192-15.479-42.124 0.713-57.922 6.123-5.974 13.962-9.881 22.418-11.173l204.228-31.203 89.495-190.668c9.612-20.478 34.004-29.287 54.482-19.675 8.653 4.062 15.613 11.022 19.675 19.675l89.495 190.668 204.228 31.203c22.362 3.417 37.72 24.314 34.304 46.676-1.292 8.456-5.199 16.295-11.173 22.418l-149.132 152.849 34.767 213.14c3.642 22.327-11.505 43.378-33.832 47.020-9.068 1.479-18.369-0.133-26.41-4.578l-179.325-99.129-179.325 99.129c-19.798 10.944-44.719 3.767-55.664-16.032-4.445-8.041-6.057-17.343-4.578-26.41z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":32,"id":106,"name":"star","prevSize":32,"code":59671},"setIdx":2,"setId":2,"iconIdx":18},{"icon":{"paths":["M626.397 453.766c-4.1 10.953-12.803 16.765-24.728 16.765h-16.695c-8.318 0-15.234-3.647-20.087-10.592-4.873-6.974-5.963-14.981-3.163-23.392l91.103-255.538c4.1-10.953 12.803-16.765 24.728-16.765h24.284c11.925 0 20.628 5.812 24.767 16.873l91.114 255.577c2.75 8.264 1.66 16.271-3.213 23.245-4.853 6.946-11.77 10.592-20.087 10.592h-18.213c-11.023 0-19.151-5.921-23.285-16.973l-21.129-62.089h-84.194l-21.203 62.296zM710.343 329.968l-20.701-63.451-21.925 63.451h42.626zM407.956 688.223c14.571-14.255 38.056-14.255 52.627 0 14.77 14.45 14.77 38.036 0 52.486l-121.639 119c-14.571 14.255-38.056 14.255-52.627 0l-121.639-119c-14.77-14.45-14.77-38.036 0-52.486 14.571-14.255 38.056-14.255 52.627 0l57.934 56.677v-554.366c0-20.481 16.817-36.934 37.392-36.934s37.392 16.453 37.392 36.934v554.366l57.934-56.677zM780.76 796.736c6.867 0 12.918 2.496 17.771 7.358 4.89 4.899 7.423 11.062 7.423 18.071v12.163c0 7.009-2.533 13.172-7.423 18.071-4.854 4.862-10.904 7.358-17.771 7.358h-182.127c-6.867 0-12.918-2.496-17.771-7.358-4.89-4.899-7.423-11.062-7.423-18.071v-12.163c0-5.468 1.416-10.434 4.347-14.834l133.209-190.841h-104.772c-6.867 0-12.918-2.496-17.771-7.358-4.89-4.899-7.423-11.062-7.423-18.071v-12.163c0-7.009 2.533-13.172 7.423-18.071 4.854-4.862 10.904-7.358 17.771-7.358h173.021c6.867 0 12.918 2.496 17.771 7.358 4.89 4.899 7.423 11.062 7.423 18.071v12.163c0 4.57-1.474 9-4.345 13.311l-77.398 110.982c-29.564 42.526-48.847 70.177-57.423 81.382h115.489z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":33,"id":105,"name":"sort","prevSize":32,"code":59672},"setIdx":2,"setId":2,"iconIdx":19},{"icon":{"paths":["M133.908 323.584c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h462.113c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-462.113zM133.908 528.384c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h378.092c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-378.092zM133.908 733.184c-17.401 0-31.508-15.129-31.508-33.792s14.106-33.792 31.508-33.792h294.072c17.401 0 31.508 15.129 31.508 33.792s-14.106 33.792-31.508 33.792h-294.072zM860.070 688.223c14.073-14.255 36.757-14.255 50.83 0 14.266 14.45 14.266 38.036 0 52.486l-117.485 119c-14.073 14.255-36.757 14.255-50.83 0l-117.485-119c-14.266-14.45-14.266-38.036 0-52.486 14.073-14.255 36.757-14.255 50.83 0l55.955 56.677v-554.366c0-20.481 16.243-36.934 36.115-36.934s36.115 16.453 36.115 36.934v554.366l55.955-56.677z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-amount-down"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":36,"id":102,"name":"sort-amount-down","prevSize":32,"code":59675},"setIdx":2,"setId":2,"iconIdx":22},{"icon":{"paths":["M645.693 921.605h-492.029c-28.312 0-51.264-22.923-51.264-51.2v-716.8c0-28.277 22.952-51.2 51.264-51.2l508.89-0.007c28.277-0.002 51.202 22.919 51.204 51.196 0 0.002 0 0.003 0 0.005l-0.007 102.4h-76.896v-68.85h-448.617v649.71h448.617v-68.861h76.896l0.008 102.405c0.001 28.277-22.922 51.201-51.199 51.201-0.002 0-0.004 0-0.005 0l-16.173-0.007c-0.229 0.004-0.458 0.005-0.688 0.005zM698.094 393.607c-0.115-0.109-0.229-0.219-0.343-0.329-16.377-15.9-16.233-41.541 0.324-57.27 16.802-15.962 43.85-15.967 60.658-0.010l147.034 139.589c0.146 0.139 0.291 0.278 0.435 0.418 20.699 20.11 20.504 52.527-0.435 72.407l-147.034 139.589c-16.808 15.957-43.856 15.952-60.658-0.010-0.115-0.109-0.229-0.219-0.342-0.329-16.367-15.91-16.205-41.551 0.361-57.27l81.61-77.433h-379.359c-23.554 0-42.649-18.338-42.649-40.96s19.095-40.96 42.649-40.96h379.359l-81.61-77.433z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sign-out"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":37,"id":101,"name":"sign-out","prevSize":32,"code":59676},"setIdx":2,"setId":2,"iconIdx":23},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":38,"id":100,"name":"shield","prevSize":32,"code":59677},"setIdx":2,"setId":2,"iconIdx":24},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050zM665.48 347.607c14.236-14.414 37.461-14.558 51.887-0.311 14.628 14.807 14.628 38.122 0.311 52.618l-239.682 242.678c-15.896 16.095-41.83 16.256-57.936 0.349l-0.348-0.349-102.257-103.535c-14.812-14.997-14.812-39.117 0-54.114 14.723-14.907 38.742-15.056 53.716-0.266l79.467 80.457 214.843-217.528z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-check"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":39,"id":99,"name":"shield-check","prevSize":32,"code":59678},"setIdx":2,"setId":2,"iconIdx":25},{"icon":{"paths":["M848.049 227.32c33.633 14.001 55.631 47.066 55.631 83.629 0 283.352-148.95 524.619-356.981 611.393-22.247 9.25-47.31 9.249-69.574-0.008-208.503-87.024-356.805-333.701-356.805-611.385 0-36.617 22.049-69.663 55.788-83.629l301.198-125.665c22.263-9.248 47.285-9.247 69.567 0.009l301.176 125.656zM515.949 847.874c172.552-68.548 307.095-297.014 306.959-536.924 0-4.129-2.328-7.66-6.016-9.199l-301.276-125.698c-2.305-0.981-5.081-0.973-7.489 0.042l-301.092 125.621c-3.695 1.568-6.1 5.201-6.1 9.235 0 239.977 134.63 468.41 307.148 536.874 2.621 1.050 5.424 1.053 7.866 0.050zM364.244 386.457c9.881 79.955 46.236 161.347 109.356 244.214v-285.735l-109.356 41.521zM484.179 760.879c-126.028-132.474-192.633-265.254-199.003-398.202-0.796-16.605 9.184-31.836 24.725-37.737l188.469-71.559c25.128-9.541 52.031 9.021 52.031 35.899v445.131c0 34.674-42.322 51.59-66.221 26.468z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-alt"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":40,"id":98,"name":"shield-alt","prevSize":32,"code":59679},"setIdx":2,"setId":2,"iconIdx":26},{"icon":{"paths":["M475.27 238.914l-74.979 72.125c-14.76 14.198-38.1 14.198-52.859 0v0c-14.041-13.507-14.474-35.839-0.968-49.88 0.316-0.329 0.639-0.651 0.968-0.968l128.539-123.647c19.822-19.068 51.168-19.068 70.99 0l128.539 123.647c14.041 13.507 14.474 35.839 0.968 49.88-0.316 0.329-0.639 0.651-0.968 0.968v0c-14.76 14.198-38.1 14.198-52.859 0l-73.91-71.097v409.378c0 20.286-16.445 36.73-36.73 36.73v0c-20.286 0-36.73-16.445-36.73-36.73v-410.406zM634.435 489.758v-70.665h107.965c28.277 0 51.2 22.923 51.2 51.2v400.107c0 28.277-22.923 51.2-51.2 51.2h-460.8c-28.277 0-51.2-22.923-51.2-51.2v-400.107c0-28.277 22.923-51.2 51.2-51.2h107.965v70.665h-85.704v361.177h416.278v-361.177h-85.704z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["share"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":41,"id":97,"name":"share","prevSize":32,"code":59680},"setIdx":2,"setId":2,"iconIdx":27},{"icon":{"paths":["M512.917 779.883l-64-232.448 317.542-261.018-253.542 493.466zM190.818 376.069l528.179-149.094-317.594 261.018-210.586-111.923zM913.199 116.536c-9.421-11.827-25.088-16.794-39.373-12.749l-795.034 224.512c-14.899 4.198-25.754 17.254-27.392 32.819-1.587 15.616 6.349 30.669 20.019 37.939l302.592 160.768 91.955 333.824c4.147 15.104 16.998 26.112 32.41 27.75 1.28 0.154 2.662 0.205 3.891 0.205 13.926 0 26.88-7.834 33.434-20.582l381.645-742.656c6.912-13.517 5.325-29.952-4.147-41.83z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":42,"id":96,"name":"send","prevSize":32,"code":59681},"setIdx":2,"setId":2,"iconIdx":28},{"icon":{"paths":["M511.152 360.583c22.304 0 40.392 18.051 40.392 40.327v233.944c0 22.275-18.088 40.327-40.392 40.327s-40.392-18.051-40.392-40.327v-233.944c0-22.275 18.088-40.327 40.392-40.327zM49.119 863.747l413.955-740.751c14.588-26.104 48.090-35.845 75.032-21.957 9.656 4.977 17.597 12.61 22.82 21.957l413.955 740.751c16.58 29.668-5.594 65.533-40.020 65.533h-845.723c-34.426 0-56.599-35.865-40.020-65.533zM510.258 703.736c24.735 0 43.707 19.504 43.707 44.561 0 25.225-18.915 44.785-43.707 44.785s-43.707-19.56-43.707-44.785c0-25.056 18.972-44.561 43.707-44.561zM140.057 855.673h743.887l-371.943-665.573-371.943 665.573z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["report"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":43,"id":95,"name":"report","prevSize":32,"code":59682},"setIdx":2,"setId":2,"iconIdx":29},{"icon":{"paths":["M19.721 504.267l312.885 327.111c43.945 45.945 122.505 15.269 122.505-49.156v-156.16c274.533 3.221 389.445 29.739 322.116 268.606-14.828 52.476 45.239 92.857 88.32 61.351 59.182-43.271 158.453-141.495 158.453-310.348 0-304.155-274.752-357.396-568.889-360.924v-156.718c0-64.48-78.606-95.051-122.507-49.156l-312.884 327.083c-26.295 27.502-26.295 70.809 0 98.311zM60.836 445.28l312.889-327.111c8.763-9.173 24.498-3.079 24.498 9.831v213.333c279.314 0 568.889 19.876 568.889 304.338 0 132.267-71.111 217.191-135.147 264.018 91.259-323.543-129.038-340.8-433.742-340.8v213.333c0 12.907-15.728 19.004-24.498 9.831l-312.889-327.111c-2.451-2.545-3.96-6.012-3.96-9.831s1.509-7.286 3.964-9.836l-0.004 0.004z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reply"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":44,"id":94,"name":"reply","prevSize":32,"code":59683},"setIdx":2,"setId":2,"iconIdx":30},{"icon":{"paths":["M254.865 554.983l171.258 175.077c16.155 16.515 16.155 42.912 0 59.427v0c-15.702 16.052-41.444 16.336-57.497 0.634-0.214-0.209-0.425-0.42-0.634-0.634l-230.571-235.712c-19.466-19.9-19.466-51.705 0-71.605l243.364-248.79c15.103-15.44 39.864-15.713 55.304-0.61 0.205 0.201 0.409 0.404 0.61 0.61v0c15.539 15.885 15.539 41.275 0 57.16l-179.602 183.606h532.172c73.176 0 132.331 58.113 132.331 130.236v116.079c0 22.701-18.403 41.105-41.105 41.105v0c-22.701 0-41.105-18.403-41.105-41.105v-116.079c0-27.461-22.331-49.399-50.121-49.399h-534.404z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reply-directly"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":45,"id":93,"name":"reply-directly","prevSize":32,"code":59684},"setIdx":2,"setId":2,"iconIdx":31},{"icon":{"paths":["M670.939 599.542h199.461c28.277 0 51.2 22.923 51.2 51.2v197.729c0 19.389-15.718 35.107-35.107 35.107v0c-19.389 0-35.107-15.718-35.107-35.107v-101.060c-14.089 19.847-29.302 38.945-46.809 56.030-77.562 76.158-180.448 118.098-289.699 118.098-214.525 0-391.087-159.758-410.7-371.615-0.048-0.533-0.094-1.083-0.141-1.65l-0.001 0c-1.508-18.485 12.254-34.693 30.739-36.201 0.909-0.074 1.82-0.111 2.731-0.111l2.070-0.001c17.94 0 32.918 13.684 34.534 31.55 0 0 0 0 0 0 16.243 175.439 162.754 307.814 340.768 307.814 90.715 0 176.188-34.779 240.597-98.018 20.924-20.502 39.132-43.626 54.111-68.762 2.809-4.728 4.774-9.923 7.396-14.792h-146.043c-19.389 0-35.107-15.718-35.107-35.107v0c0-19.389 15.718-35.107 35.107-35.107zM388.214 389.291v0c0 19.389-15.718 35.107-35.107 35.107h-199.461c-28.277 0-51.2-22.923-51.2-51.2v-197.729c0-19.389 15.718-35.107 35.107-35.107v0c19.389 0 35.107 15.718 35.107 35.107v101.060c14.136-19.847 29.349-38.945 46.762-55.983 77.609-76.158 180.541-118.145 289.699-118.145 214.571 0 391.134 159.758 410.747 371.662 0.043 0.456 0.085 0.925 0.126 1.406l-0 0c1.593 18.502-12.114 34.792-30.616 36.385-0.959 0.083-1.922 0.124-2.884 0.124l-1.972-0c-17.914 0-32.891-13.621-34.586-31.455 0 0 0 0 0 0-16.196-175.533-162.754-307.908-340.815-307.908-90.669 0-176.095 34.826-240.55 98.064-20.877 20.455-39.085 43.579-54.111 68.762-2.809 4.728-4.821 9.877-7.396 14.745h146.043c19.389 0 35.107 15.718 35.107 35.107z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reload"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":46,"id":92,"name":"reload","prevSize":32,"code":59685},"setIdx":2,"setId":2,"iconIdx":32},{"icon":{"paths":["M414.476 394.968c0-67.325-54.578-121.902-121.905-121.902-67.326 0-121.905 54.579-121.905 121.905v48.762c0 40.396 32.747 73.143 73.143 73.143 13.021 0 24.908 7.408 30.643 19.098 19.758 40.269 18.374 88.36-2.635 144.554-2.325 6.22-5.803 14.311-10.433 24.274l-0.001-0c-2.384 5.129-0.158 11.219 4.971 13.603 3.207 1.49 6.955 1.221 9.916-0.713 14.199-9.273 25.268-17.8 33.208-25.581 63.043-61.782 104.997-167.862 104.997-297.143zM102.4 443.733v-48.762c0-105.029 85.143-190.171 190.171-190.171s190.171 85.141 190.171 190.168c0 241.96-133.814 424.232-307.2 424.232-27.553 0-43.751-30.962-28.041-53.596 56.52-81.429 78.704-142.61 71.172-182.688-66.107-11.859-116.274-69.662-116.274-139.182zM657.531 582.916c-66.107-11.859-116.274-69.662-116.274-139.182v-48.762c0-105.029 85.143-190.171 190.171-190.171s190.171 85.141 190.171 190.168c0 241.96-133.814 424.232-307.2 424.232-27.553 0-43.751-30.962-28.040-53.596 56.52-81.429 78.704-142.61 71.172-182.688zM853.333 394.968c0-67.325-54.578-121.902-121.905-121.902-67.326 0-121.905 54.579-121.905 121.905v48.762c0 40.396 32.747 73.143 73.143 73.143 13.021 0 24.908 7.408 30.643 19.098 19.37 39.477 18.42 86.472-1.418 141.249-2.531 6.989-6.461 16.233-11.79 27.732l-0.002-0.001c-2.379 5.132-0.146 11.221 4.986 13.6 3.2 1.483 6.938 1.215 9.894-0.709 13.847-9.013 24.662-17.295 32.446-24.846 63.549-61.65 105.908-168.128 105.908-298.031z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["quote"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":47,"id":91,"name":"quote","prevSize":32,"code":59686},"setIdx":2,"setId":2,"iconIdx":33},{"icon":{"paths":["M493.033 613.695l-371.535-215.269c-11.371-6.537-18.427-18.716-18.554-31.943-0.127-13.225 6.69-25.541 17.898-32.284l373.418-226.518c11.552-7.017 25.967-7.041 37.53-0.071l371.47 223.236c11.226 6.739 18.059 19.012 17.983 32.215-0.076 13.204-7.052 25.396-18.35 31.996l-373.37 218.564c-5.573 3.269-11.901 4.988-18.344 4.98-6.395-0.001-12.662-1.674-18.146-4.906zM212.267 365.373l298.85 173.185 300.875-176.139-298.831-179.553-300.894 182.507zM493.032 765.31l-371.339-215.205c-11.721-6.392-19.087-18.715-19.289-32.181-0.202-13.463 6.788-26.005 18.306-32.757 11.586-6.792 25.899-6.637 37.111 0.268l353.294 204.693 355.030-207.827c11.407-6.922 25.616-7.035 37.129-0.293 11.446 6.703 18.431 19.126 18.324 32.502-0.107 13.377-7.292 25.685-18.705 32.116l-373.362 218.559c-5.575 3.277-11.909 4.998-18.356 4.984-6.35-0.002-12.592-1.652-18.142-4.861zM493.032 916.74l-371.339-215.205c-11.721-6.392-19.087-18.715-19.289-32.181-0.202-13.463 6.788-26.005 18.306-32.757 11.586-6.792 25.899-6.637 37.111 0.268l353.294 204.693 355.308-207.989c17.57-9.903 39.689-3.594 49.625 14.077 9.872 17.558 4.114 39.933-13.155 50.41l-373.362 218.559c-5.575 3.277-11.909 4.998-18.356 4.984-6.35-0.002-12.592-1.652-18.142-4.861z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["queue"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":48,"id":90,"name":"queue","prevSize":32,"code":59687},"setIdx":2,"setId":2,"iconIdx":34},{"icon":{"paths":["M284.939 207.277v609.445c0 14.138 11.462 25.6 25.6 25.6h402.922c14.138 0 25.6-11.462 25.6-25.6v-425.488c0-13.675-5.471-26.782-15.192-36.399l-160.078-158.357c-9.585-9.482-22.524-14.801-36.008-14.801h-217.244c-14.138 0-25.6 11.462-25.6 25.6zM596.985 117.201l207.022 204.796c9.722 9.617 15.192 22.724 15.192 36.399v512.004c0 28.277-22.923 51.2-51.2 51.2h-512c-28.277 0-51.2-22.923-51.2-51.2v-716.8c0-28.277 22.923-51.2 51.2-51.2h304.978c13.483 0 26.422 5.319 36.008 14.801zM394.24 704v0c0-21.208 17.192-38.4 38.4-38.4h153.6c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-153.6c-21.208 0-38.4-17.192-38.4-38.4zM394.24 550.4v0c0-21.208 17.192-38.4 38.4-38.4h153.6c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-153.6c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["post"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":49,"id":89,"name":"post","prevSize":32,"code":59688},"setIdx":2,"setId":2,"iconIdx":35},{"icon":{"paths":["M512 99.84c199.506 0 360.96 161.463 360.96 360.96 0 110.932-49.907 212.466-133.648 280.482-3.995 3.245-9.055 7.041-15.179 11.39-2.828 2.009-6.132 3.241-9.585 3.574-10.909 1.054-20.607-6.936-21.661-17.845l-0.192-1.986c-1.682-17.418 5.219-34.565 18.497-45.964 9.782-8.395 17.379-15.474 22.781-21.224 52.758-56.15 82.665-130.011 82.665-208.427 0-170.366-140.407-308.090-311.038-304.573-164.381 3.387-297.226 138.223-298.235 302.672-0.493 80.258 30.341 155.851 84.922 212.711 5.234 5.452 12.594 12.206 22.071 20.249 12.387 10.512 19.356 26.062 18.959 42.304l-0.136 5.517c-0.093 3.817-1.342 7.515-3.581 10.607-6.113 8.444-17.914 10.332-26.354 4.217-17.094-12.377-29.857-22.522-38.307-30.459-72.13-67.748-113.9-161.843-113.9-263.245 0-199.506 161.464-360.96 360.96-360.96zM611.84 671.6c0 46.621-18.108 157.105-32.826 211.836-6.815 25.189-30.31 35.604-67.014 35.604s-60.199-10.416-67.014-35.609c-14.708-54.691-32.826-165.054-32.826-211.831 0-47.592 36.076-67.44 99.84-67.44s99.84 19.848 99.84 67.44zM565.76 671.6c0-14.129-23.565-22.134-53.76-22.13-30.191 0.004-53.76 8.013-53.76 22.13 0 34.235 11.511 111.604 24.478 171.428 0.583 2.664 0.583 2.664 1.252 5.622 2.92 12.814 14.315 21.905 27.457 21.905l1.166-0.003c13.14 0 24.537-9.098 27.45-21.919 0.486-2.15 0.486-2.15 0.92-4.125 13.049-59.747 24.797-138.352 24.797-172.908zM616.96 460.8c0 57.967-46.993 104.96-104.96 104.96s-104.96-46.993-104.96-104.96c0-57.967 46.993-104.96 104.96-104.96s104.96 46.993 104.96 104.96zM560.64 460.8c0-26.818-21.822-48.64-48.64-48.64s-48.64 21.822-48.64 48.64c0 26.818 21.822 48.64 48.64 48.64s48.64-21.822 48.64-48.64zM669.982 545.059c1.487-2.642 2.729-5.004 3.728-7.087 11.522-24.039 17.49-50.271 17.49-77.171 0-101.539-84.63-182.985-186.256-179.064-92.382 3.565-167.505 77.969-171.933 170.293-1.487 30.994 4.921 61.326 18.63 88.7 0.64 1.249 0.64 1.249 1.038 2.010 0.5 0.931 0.5 0.931 1.36 2.49 7.294 13.022 5.65 29.218-4.111 40.51-7.331 8.481-20.149 9.412-28.629 2.082-1.423-1.23-2.667-2.651-3.699-4.224-4.637-7.070-8.234-13.097-10.787-18.076-16.586-32.356-25.214-68.047-25.214-104.721 0-127.771 104.48-231.42 232.285-230.392 124.147 0.999 225.904 101.455 228.464 225.55 0.787 38.143-7.742 75.348-24.875 109-2.591 5.088-6.259 11.257-10.999 18.499-6.149 9.395-18.749 12.026-28.144 5.878-1.563-1.023-2.977-2.256-4.203-3.665-9.832-11.298-11.493-27.561-4.146-40.613z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["podcast"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":50,"id":88,"name":"podcast","prevSize":32,"code":59689},"setIdx":2,"setId":2,"iconIdx":36},{"icon":{"paths":["M471.93 193.643v278.288h-278.261c-22.13 0-40.070 17.94-40.070 40.070v0c0 22.13 17.94 40.070 40.070 40.070h278.261v278.288c0 22.115 17.928 40.043 40.043 40.043v0c22.115 0 40.043-17.928 40.043-40.043v-278.288h278.314c22.13 0 40.070-17.94 40.070-40.070v0c0-22.13-17.94-40.070-40.070-40.070h-278.314v-278.288c0-22.115-17.928-40.043-40.043-40.043v0c-22.115 0-40.043 17.928-40.043 40.043z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["plus"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":51,"id":87,"name":"plus","prevSize":32,"code":59690},"setIdx":2,"setId":2,"iconIdx":37},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM456.65 362.026c-4.893-3.071-10.553-4.7-16.33-4.7-16.966 0-30.72 13.754-30.72 30.72v247.902c0 5.777 1.629 11.437 4.7 16.33 9.019 14.371 27.98 18.709 42.35 9.69l197.503-123.951c3.918-2.459 7.231-5.772 9.69-9.69 9.019-14.371 4.68-33.331-9.69-42.35l-197.503-123.951z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":52,"id":86,"name":"play","prevSize":32,"code":59691},"setIdx":2,"setId":2,"iconIdx":38},{"icon":{"paths":["M912.8 429.4l-704-416.2c-57.2-33.8-144.8-1-144.8 82.6v832.2c0 75 81.4 120.2 144.8 82.6l704-416c62.8-37 63-128.2 0-165.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play-solid"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":53,"id":85,"name":"play-solid","prevSize":32,"code":59692},"setIdx":2,"setId":2,"iconIdx":39},{"icon":{"paths":["M482.814 637.831l-224.859-224.911-32.719 32.719c-7.201 7.201-16.751 11.167-26.927 11.167 0 0 0 0 0 0v0c-10.643 0-19.44-8.301-20.056-18.926-0.022-0.403-0.034-0.791-0.034-1.164 0-10.124 3.966-19.517 10.906-26.561l221.049-220.997c0 0 0 0 0 0v0c9.814-9.814 25.725-9.814 35.539 0 6.797 6.797 9.121 16.875 5.988 25.962-1.415 4.107-3.429 7.473-6.042 10.097l-32.719 32.719 205.551 205.603 19.256 18.786 24.213-11.533c2.88-1.355 53.827-24.715 119.015 0.835 7.274 2.851 17.497 8.254 30.671 16.21l-0.003 0.005c7.259 4.384 9.59 13.822 5.206 21.081-0.641 1.062-1.409 2.042-2.286 2.92l-302.654 302.661c-6.001 6.001-15.73 6.001-21.73 0-0.887-0.887-1.662-1.879-2.308-2.955-8.368-13.935-14.002-24.715-16.901-32.338-24.462-64.324-1.531-114.303-0.314-116.906l12.159-24.474zM910.51 469.54c-103.010-103.115-207.534-90.486-255.438-76.657l-137.66-137.66c20.978-36.424 20.195-81.928-2.244-117.569l-17.742-17.742c-44.043-27.762-102.801-22.491-140.948 15.655l-220.945 220.997c-38.146 38.146-43.364 97.009-15.603 140.843l17.69 17.742c35.746 22.491 81.302 23.222 117.569 2.296l137.712 137.712c-13.829 47.8-26.457 152.271 76.657 255.386v0c14.77 14.799 38.741 14.822 53.54 0.052 0.009-0.009 0.017-0.017 0.026-0.026l166.909-166.909 151.971 151.971c14.808 14.808 38.816 14.814 53.632 0.013v0c14.806-14.792 14.818-38.786 0.026-53.592-0.004-0.004-0.009-0.009-0.013-0.013l-152.023-152.023 166.886-166.931c14.783-14.787 14.782-38.759-0.004-53.544z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":54,"id":84,"name":"pin","prevSize":32,"code":59693},"setIdx":2,"setId":2,"iconIdx":40},{"icon":{"paths":["M540.754 693.093c15.352 15.384 15.496 40.229 0.264 55.894l-117.668 117.668c-73.491 73.319-192.458 73.316-265.962-0.017-73.318-73.49-73.317-192.457 0.008-265.954l168.375-168.375c73.52-73.311 192.464-73.27 266.199 0.266 14.589 15.658 14.164 40.055-0.961 55.196-15.147 15.163-39.545 15.57-55.449 0.774-42.469-42.36-111.21-42.361-153.673-0.007l-168.399 168.31c-42.353 42.453-42.356 111.179-0.017 153.617 42.453 42.354 111.179 42.355 153.624 0.010l117.869-117.859c15.603-15.114 40.446-14.899 55.789 0.476zM866.606 423.354l-168.423 168.379c-73.5 73.291-192.444 73.29-265.952-0.010-10.034-10.034-13.953-24.659-10.28-38.367s14.379-24.413 28.086-28.086c13.706-3.672 28.331 0.245 38.355 10.269 42.479 42.339 111.205 42.341 153.675 0.011l168.4-168.355c42.353-42.453 42.356-111.179 0.017-153.617-42.453-42.354-111.18-42.355-153.626-0.008l-117.701 117.701c-10.033 10.044-24.662 13.972-38.376 10.305s-24.431-14.372-28.112-28.082c-3.682-13.711 0.232-28.344 10.269-38.392l117.756-117.711c73.489-73.317 192.457-73.317 265.956 0.010 73.283 73.526 73.268 192.475-0.042 265.957z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["permalink"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":55,"id":83,"name":"permalink","prevSize":32,"code":59694},"setIdx":2,"setId":2,"iconIdx":41},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM563.948 396.373l-0.641 230.827c-0.059 21.149 17.038 38.341 38.187 38.4 0.035 0 0.071 0 0.106 0v0c21.208 0 38.4-17.192 38.4-38.4v-230.827c0-20.972-17.001-37.973-37.973-37.973v0c-20.989 0-38.021 16.984-38.079 37.973zM384 396.8v230.4c0 21.208 17.192 38.4 38.4 38.4v0c21.208 0 38.4-17.192 38.4-38.4v-230.4c0-21.208-17.192-38.4-38.4-38.4v0c-21.208 0-38.4 17.192-38.4 38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":56,"id":82,"name":"pause","prevSize":32,"code":59695},"setIdx":2,"setId":2,"iconIdx":42},{"icon":{"paths":["M413.267 462.457l-61.621 52.827c-12.060-24.101-18.846-51.3-18.846-80.084v-153.6c0-98.969 80.231-179.2 179.2-179.2 81.222 0 149.823 54.036 171.815 128.12l-69.415 59.508v-8.428c0-56.554-45.846-102.4-102.4-102.4s-102.4 45.846-102.4 102.4v153.6c0 9.438 1.277 18.578 3.667 27.257zM377.419 729.227l67.683-58.024c20.198 4.825 42.486 7.197 66.899 7.197 130.426 0 200.2-67.697 214.765-215.514 0.429-4.351 0.833-9.7 1.213-16.048l-0.001-0c1.264-21.105 18.881-37.497 40.023-37.238 20.598 0.252 37.091 17.154 36.839 37.752-0.007 0.58-0.028 1.159-0.062 1.738-0.287 4.905-0.591 9.154-0.908 12.749-15.602 176.721-101.637 277.457-253.469 291.626v116.934h89.6c14.138 0 25.6 11.462 25.6 25.6s-11.462 25.6-25.6 25.6h-256c-14.138 0-25.6-11.462-25.6-25.6s11.462-25.6 25.6-25.6h89.6v-116.934c-35.723-3.334-67.803-11.459-96.181-24.239zM252.971 599.877c-17.135-38.906-28.111-84.873-32.804-137.618-0.329-3.702-0.644-8.093-0.943-13.173l0.001-0c-1.211-20.562 14.476-38.212 35.038-39.423 0.578-0.034 1.157-0.055 1.736-0.062 21.134-0.259 38.746 16.128 40.009 37.226 0.358 5.958 0.736 11.004 1.136 15.136 3.113 32.169 8.828 60.56 17.202 85.298l-61.375 52.616zM689.221 461.922c-12.902 86.295-87.333 152.478-177.221 152.478-0.213 0-0.425-0-0.638-0.001l177.859-152.476zM911.217 153.59c13.803 16.101 11.94 40.343-4.161 54.146l-749.629 642.648c-16.101 13.803-40.343 11.94-54.146-4.161s-11.94-40.343 4.161-54.146l749.629-642.648c16.101-13.803 40.343-11.94 54.146 4.161z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mute"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":57,"id":81,"name":"mute","prevSize":32,"code":59696},"setIdx":2,"setId":2,"iconIdx":43},{"icon":{"paths":["M511.152 360.583c22.304 0 40.392 18.051 40.392 40.327v233.944c0 22.275-18.088 40.327-40.392 40.327s-40.392-18.051-40.392-40.327v-233.944c0-22.275 18.088-40.327 40.392-40.327zM49.119 863.747l413.955-740.751c14.588-26.104 48.090-35.845 75.032-21.957 9.656 4.977 17.597 12.61 22.82 21.957l413.955 740.751c16.58 29.668-5.594 65.533-40.020 65.533h-845.723c-34.426 0-56.599-35.865-40.020-65.533zM510.258 703.736c24.735 0 43.707 19.504 43.707 44.561 0 25.225-18.915 44.785-43.707 44.785s-43.707-19.56-43.707-44.785c0-25.056 18.972-44.561 43.707-44.561zM140.057 855.673h743.887l-371.943-665.573-371.943 665.573z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["modal-warning"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":58,"id":80,"name":"modal-warning","prevSize":32,"code":59697},"setIdx":2,"setId":2,"iconIdx":44},{"icon":{"paths":["M569.165 763.288c0 31.962-25.552 57.796-57.165 57.796s-57.165-25.834-57.165-57.796c0-31.962 25.552-57.796 57.165-57.796s57.165 25.834 57.165 57.796zM768 185.325v653.35c0 45.783-36.736 82.925-82.019 82.925h-347.961c-45.283 0-82.019-37.142-82.019-82.925v-653.35c0-45.783 36.736-82.925 82.019-82.925h347.961c45.283 0 82.019 37.142 82.019 82.925zM703.379 185.325c0-9.657-7.846-17.59-17.398-17.59h-347.961c-9.552 0-17.398 7.933-17.398 17.59v653.35c0 9.657 7.846 17.59 17.398 17.59h347.961c9.552 0 17.398-7.933 17.398-17.59v-653.35z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mobile"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":59,"id":79,"name":"mobile","prevSize":32,"code":59698},"setIdx":2,"setId":2,"iconIdx":45},{"icon":{"paths":["M512 179.2c-56.554 0-102.4 45.846-102.4 102.4v153.6c0 56.554 45.846 102.4 102.4 102.4s102.4-45.846 102.4-102.4v-153.6c0-56.554-45.846-102.4-102.4-102.4zM665.6 896v0c0 14.138-11.462 25.6-25.6 25.6h-256c-14.138 0-25.6-11.462-25.6-25.6v0c0-14.138 11.462-25.6 25.6-25.6h89.6v-116.934c-151.712-14.158-237.732-114.746-253.432-291.208-0.329-3.702-0.644-8.093-0.943-13.173l0.001-0c-1.211-20.562 14.476-38.212 35.038-39.423 0.578-0.034 1.157-0.055 1.736-0.062v0c21.134-0.259 38.746 16.128 40.009 37.226 0.358 5.958 0.736 11.004 1.136 15.136 14.365 148.459 84.158 216.438 214.855 216.438 130.426 0 200.2-67.697 214.765-215.514 0.429-4.351 0.833-9.7 1.213-16.048l-0.001-0c1.264-21.105 18.881-37.497 40.023-37.238v0c20.598 0.252 37.091 17.154 36.839 37.752-0.007 0.58-0.028 1.159-0.062 1.738-0.287 4.905-0.591 9.154-0.908 12.749-15.602 176.721-101.637 277.457-253.469 291.626v116.934h89.6c14.138 0 25.6 11.462 25.6 25.6zM512 102.4c98.969 0 179.2 80.231 179.2 179.2v153.6c0 98.969-80.231 179.2-179.2 179.2s-179.2-80.231-179.2-179.2v-153.6c0-98.969 80.231-179.2 179.2-179.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mic"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":60,"id":78,"name":"mic","prevSize":32,"code":59699},"setIdx":2,"setId":2,"iconIdx":46},{"icon":{"paths":["M541.395 826.208h-8.818c-208.607 0-360.015-132.144-360.015-314.208 0-173.28 141.818-314.256 316.115-314.256s316.115 140.976 316.115 314.256c0 28.32-13.252 56.64-27.275 86.544-4.867 10.464-9.686 20.784-13.396 29.664-36.43 78.048 35.226 115.632 65.729 131.664 4.722 2.448 10.794 5.616 15.709 8.544-20.046 22.944-87.414 58.512-304.164 57.792zM862.618 698.192c-43.129-22.704-41.056-27.216-34.214-42 3.903-9.264 8.288-18.672 12.722-28.176 15.902-33.84 33.876-72.24 33.876-116.016 0-211.776-173.333-384-386.326-384s-386.277 172.224-386.277 384c0 222.48 180.947 383.952 430.177 383.952l8.481 0.048h9.927c137.674 0 319.585-12.288 364.544-107.376 23.757-50.208-27.997-77.376-52.911-90.432z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":61,"id":77,"name":"message","prevSize":32,"code":59700},"setIdx":2,"setId":2,"iconIdx":47},{"icon":{"paths":["M445.44 780.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM445.44 524.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM445.44 268.8c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["menu"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":62,"id":76,"name":"menu","prevSize":32,"code":59651},"setIdx":2,"setId":2,"iconIdx":48},{"icon":{"paths":["M510.025 277.943c63.563 0 115.2 51.671 115.2 115.2s-51.637 115.2-115.2 115.2c-63.563 0-115.2-51.671-115.2-115.2s51.637-115.2 115.2-115.2zM510.025 445.514c28.91 0 52.4-23.468 52.4-52.371 0-28.862-23.49-52.371-52.4-52.371s-52.4 23.509-52.4 52.371c0 28.903 23.49 52.371 52.4 52.371zM534.763 913.507c-13.199 10.791-32.328 10.791-45.526 0-2.477-2.026-6.813-5.709-12.744-10.965-9.704-8.6-20.469-18.595-32.033-29.9-32.942-32.207-65.856-68.605-96.632-108.552-89.137-115.7-143.029-237.437-143.029-360.552 0-166.357 137.582-301.139 307.2-301.139s307.2 134.781 307.2 301.139c0 123.115-53.892 244.851-143.029 360.552-30.776 39.947-63.69 76.345-96.632 108.552-11.563 11.306-22.329 21.301-32.033 29.9-5.93 5.255-10.266 8.939-12.744 10.965zM529.322 822.806c30.705-30.020 61.437-64.005 90.064-101.164 80.587-104.602 128.529-212.899 128.529-318.104 0-127.491-105.58-230.921-235.916-230.921s-235.916 103.43-235.916 230.921c0 105.205 47.942 213.502 128.529 318.104 28.628 37.159 59.36 71.143 90.064 101.164 9.588 9.374 25.057 9.374 34.645 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["map-pin"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":63,"id":75,"name":"map-pin","prevSize":32,"code":59701},"setIdx":2,"setId":2,"iconIdx":49},{"icon":{"paths":["M217.907 243.597c-28.959 0-52.511 24.086-52.511 53.706v429.395c0 29.619 23.552 53.664 52.511 53.664h588.145c28.959 0 52.552-24.045 52.552-53.664v-429.395c0-29.619-23.593-53.706-52.552-53.706h-588.145zM806.052 844.8h-588.145c-63.734 0-115.507-52.957-115.507-118.102v-429.395c0-65.146 51.773-118.102 115.507-118.102h588.145c63.734 0 115.548 52.957 115.548 118.102v429.395c0 65.146-51.814 118.102-115.548 118.102zM485.814 577.169l-214.684-146.451c-14.683-10.016-18.656-29.937-8.94-44.82v0c9.466-14.499 28.894-18.58 43.393-9.114 0.177 0.116 0.353 0.233 0.528 0.352l208.557 142.256 208.605-142.265c14.294-9.748 33.785-6.063 43.533 8.231 0.122 0.179 0.242 0.358 0.36 0.539v0c9.706 14.885 5.729 34.8-8.951 44.814l-214.694 146.457c-17.403 11.872-40.302 11.872-57.706 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mail"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":64,"id":74,"name":"mail","prevSize":32,"code":59702},"setIdx":2,"setId":2,"iconIdx":50},{"icon":{"paths":["M909.622 909.622v0c-15.971 15.971-41.864 15.971-57.835 0l-155.625-155.625c-62.53 49.946-141.808 79.806-228.060 79.806-201.971 0-365.702-163.73-365.702-365.702s163.73-365.702 365.702-365.702c201.971 0 365.702 163.73 365.702 365.702 0 86.252-29.86 165.53-79.806 228.060l155.625 155.625c15.971 15.971 15.971 41.864 0 57.835zM468.102 764.064c163.456 0 295.963-132.507 295.963-295.963s-132.507-295.963-295.963-295.963c-163.456 0-295.963 132.507-295.963 295.963s132.507 295.963 295.963 295.963z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["magnifier"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":65,"id":73,"name":"magnifier","prevSize":32,"code":59703},"setIdx":2,"setId":2,"iconIdx":51},{"icon":{"paths":["M512 137.576v0c0 19.36-14.836 35.49-34.129 37.105-6.46 0.542-11.94 1.15-16.44 1.823-163.449 24.468-288.814 165.724-288.814 336.314 0 170.932 125.868 312.413 289.798 336.46 4.262 0.625 9.42 1.191 15.472 1.697l-0 0.002c19.283 1.612 34.112 17.734 34.112 37.084v0c0 18.528-15.020 33.548-33.548 33.548-0.816 0-1.631-0.030-2.444-0.089-5.493-0.4-10.229-0.844-14.206-1.331-202.519-24.798-359.402-197.708-359.402-407.37 0-209.201 156.195-381.812 358.068-407.204 4.317-0.543 9.506-1.036 15.567-1.478l0 0.002c18.467-1.347 34.53 12.532 35.877 30.999 0.059 0.812 0.089 1.625 0.089 2.439z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["loading"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":67,"id":71,"name":"loading","prevSize":32,"code":59705},"setIdx":2,"setId":2,"iconIdx":53},{"icon":{"paths":["M347.27 294.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM347.27 550.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM347.27 806.4c-22.13 0-40.070-17.192-40.070-38.4s17.94-38.4 40.070-38.4h534.261c22.13 0 40.070 17.192 40.070 38.4s-17.94 38.4-40.070 38.4h-534.261zM102.177 768v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4zM102.177 512v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4zM102.177 256v0c0-21.208 17.192-38.4 38.4-38.4h56.765c21.208 0 38.4 17.192 38.4 38.4v0c0 21.208-17.192 38.4-38.4 38.4h-56.765c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["list"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":68,"id":70,"name":"list","prevSize":32,"code":59706},"setIdx":2,"setId":2,"iconIdx":54},{"icon":{"paths":["M231.131 222.968c-32.317 0-58.514 25.881-58.514 57.806v462.452c0 31.926 26.198 57.806 58.514 57.806h561.737c32.317 0 58.514-25.881 58.514-57.806v-462.452c0-31.926-26.198-57.806-58.514-57.806h-561.737zM231.131 153.6h561.737c71.096 0 128.731 56.938 128.731 127.174v462.452c0 70.236-57.635 127.174-128.731 127.174h-561.737c-71.096 0-128.731-56.938-128.731-127.174v-462.452c0-70.236 57.635-127.174 128.731-127.174zM465.189 407.948c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869zM277.943 407.948c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM465.189 546.684c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869zM277.943 546.684c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM277.943 685.419c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h46.811c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-46.811zM465.189 685.419c-19.39 0-35.109-15.528-35.109-34.684s15.719-34.684 35.109-34.684h280.869c19.39 0 35.109 15.528 35.109 34.684s-15.719 34.684-35.109 34.684h-280.869z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["list-alt"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":69,"id":69,"name":"list-alt","prevSize":32,"code":59707},"setIdx":2,"setId":2,"iconIdx":55},{"icon":{"paths":["M863.073 102.4h-702.327c-32.176 0-58.345 26.533-58.345 59.073v701.053c0 32.54 26.169 59.073 58.345 59.073h702.327c32.176 0 58.527-26.533 58.527-59.073v-701.053c0-32.54-26.351-59.073-58.527-59.073zM349.98 804.591h-121.424v-390.94h121.606v390.94h-0.182zM289.269 360.22c-37.899-1.363-67.919-32.482-67.919-70.406s30.020-69.043 67.919-70.406c38.853 0.075 70.331 31.553 70.406 70.406 0 38.958-31.403 70.406-70.406 70.406zM805.137 804.591h-121.424v-190.191c0-45.329-0.91-103.674-63.078-103.674-63.26 0-73 49.38-73 100.398v193.422h-121.378v-390.94h116.463v53.43h1.638c16.293-30.72 55.979-63.124 115.007-63.124 122.88 0 145.772 81.010 145.772 186.368v214.312z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["linkedin"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":70,"id":68,"name":"linkedin","prevSize":32,"code":59708},"setIdx":2,"setId":2,"iconIdx":56},{"icon":{"paths":["M911.351 545.469c-25.914-69.895-98.214-111.622-193.431-111.622-3.735 0-7.331 0.046-10.882 0.228l-0.646-84.502 119.068-20.226c14.311-2.431 23.941-16.003 21.51-30.314-0.071-0.417-0.152-0.832-0.242-1.244v0c-3.344-15.203-17.825-25.272-33.241-23.114-35.852 5.020-71.704 10.040-107.555 15.060l-0.367-56.436c-0.103-15.831-13.012-28.586-28.844-28.498v0c-15.684 0.087-28.327 12.872-28.24 28.556 0 0.086 0.001 0.172 0.003 0.258l0.964 65.812c-33.79 5.762-67.579 11.524-101.369 17.286v0c-16.013 2.731-26.78 17.925-24.049 33.937 0.007 0.043 0.015 0.086 0.022 0.129v0c2.818 16.095 18.111 26.888 34.22 24.151l92.19-15.665 1.476 82.636c-35.674 8.733-68.121 27.236-93.603 53.377-31.715 32.688-49.635 76.062-50.121 121.315 0 64.98 40.3 103.568 96.6 110.394 131.229 15.836 212.428-125.319 239.402-193.12 41.847 56.68 15.763 159.705-64.315 227.705-0.625 0.53-1.298 1.091-2.019 1.681l-0-0c-10.687 8.739-12.266 24.486-3.527 35.173 0.145 0.178 0.293 0.354 0.443 0.527l1.609 1.858c9.197 10.635 25.138 12.141 36.164 3.417 0 0 0 0 0 0 89.963-71.182 129.396-175.97 98.782-258.757zM565.62 616.546c0-31.671 13.602-64.707 36.427-88.278 14.197-14.652 31.635-25.865 50.951-32.763l3.412 175.192c-16 5.324-33.199 7.554-51.597 5.324-40.116-4.96-39.193-37.405-39.193-59.474zM707.454 485.084c3.504-0.137 6.916-0.41 10.467-0.41 32.046 0 61.972 5.961 78.202 14.789 16.231 8.919-42.421 111.122-90.56 153.941l1.891-168.321zM223.4 378.718l-119.637 377.943c-4.846 15.31 3.636 31.651 18.947 36.497 2.817 0.892 5.754 1.349 8.708 1.356v0c23.507 0.054 44.265-15.322 51.064-37.825l30.489-100.918h145.154l34.39 102.622c7.249 21.632 27.512 36.212 50.326 36.212v0c15.375 0 27.839-12.464 27.839-27.839 0-2.892-0.451-5.767-1.336-8.52l-122.033-379.593c-8.666-26.956-33.742-45.235-62.057-45.235v0c-28.289 0-53.317 18.329-61.854 45.299zM225.467 598.117l50.215-179.701c1.522-5.447 7.171-8.628 12.618-7.106 3.449 0.964 6.144 3.659 7.107 7.108l50.176 179.699h-120.116z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["language"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":71,"id":67,"name":"language","prevSize":32,"code":59709},"setIdx":2,"setId":2,"iconIdx":57},{"icon":{"paths":["M853.333 250.88c40.46 0 73.387 30.869 73.387 69.12v384c0 38.251-32.927 69.12-73.387 69.12h-682.667c-40.46 0-73.387-30.869-73.387-69.12v-384c0-38.251 32.927-69.12 73.387-69.12h682.667zM870.969 704v-384c0-8.86-7.844-16.213-17.636-16.213h-682.667c-9.792 0-17.636 7.354-17.636 16.213v384c0 8.86 7.844 16.213 17.636 16.213h682.667c9.792 0 17.636-7.354 17.636-16.213zM346.453 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM482.987 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM619.52 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM756.053 528c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM278.187 634.667c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM824.32 634.667c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM278.187 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM414.72 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM551.253 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM687.787 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM824.32 421.333c0 11.74-10.005 21.12-22.187 21.12h-34.133c-12.182 0-22.187-9.38-22.187-21.12v-32c0-11.74 10.005-21.12 22.187-21.12h34.133c12.182 0 22.187 9.38 22.187 21.12v32zM687.787 624c0 11.74-10.005 21.12-22.187 21.12h-307.2c-12.182 0-22.187-9.38-22.187-21.12v-10.667c0-11.74 10.005-21.12 22.187-21.12h307.2c12.182 0 22.187 9.38 22.187 21.12v10.667z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["keyboard"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":72,"id":66,"name":"keyboard","prevSize":32,"code":59710},"setIdx":2,"setId":2,"iconIdx":58},{"icon":{"paths":["M827.303 827.306v0c9.349-9.335 12.764-23.079 8.87-35.704l-4.273-13.856c-6.957-22.557-19.493-42.993-36.449-59.415l-224.459-217.401c-11.86-11.487-14.062-29.707-5.28-43.688 7.009-11.241 15.473-34.315 15.473-80.553 0-112.728-91.748-204.476-204.476-204.476l-6.053 0.091c-106.311 2.958-195.329 92.021-198.333 198.56-1.593 55.795 18.978 108.496 57.798 148.363 38.775 39.958 90.929 61.939 146.588 61.939 26.669 0 50.243-6.007 72.361-18.341v0c13.642-7.642 30.703-5.266 41.738 5.812l62.708 62.953h37.818c28.277 0 51.2 22.923 51.2 51.2v37.909l8.92 8.874h37.909c28.277 0 51.2 22.923 51.2 51.2v37.909l2.685 2.685 48.32 14.819c12.638 3.876 26.383 0.46 35.737-8.88zM902.167 768.735l0.711 2.306c11.517 37.341 1.435 77.99-26.197 105.621v0c-27.636 27.636-68.281 37.74-105.641 26.262l-40.953-12.583c-15.716-4.829-30.010-13.431-41.636-25.056v0c-11.335-11.335-17.703-26.709-17.703-42.74v-23.204h-13.051c-22.532 0-44.143-8.946-60.083-24.871v0c-15.939-15.924-24.894-37.53-24.894-60.061v-13.051c-30.854 0-60.44-12.279-82.227-34.126l-31.73-31.818c-25.577 10.376-53.11 15.564-82.055 15.564-74.727 0-144.54-29.49-196.649-83.056-52.063-53.565-79.643-124.197-77.549-198.97 3.959-142.947 123.514-262.456 266.461-266.416l7.737-0.137c151.23 0 274.243 123.059 274.243 274.289 0 36.772-4.414 66.991-13.425 91.475l197.851 191.682c31.069 30.1 54.041 67.552 66.79 108.889zM276.969 346.78c0-38.547 31.265-69.812 69.812-69.812 38.592 0 69.812 31.265 69.812 69.812s-31.22 69.812-69.812 69.812c-38.547 0-69.812-31.265-69.812-69.812z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["key"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":73,"id":65,"name":"key","prevSize":32,"code":59711},"setIdx":2,"setId":2,"iconIdx":59},{"icon":{"paths":["M204.826 449.459v-193.485c0-28.277 22.923-51.2 51.2-51.2h294.554c28.277 0 51.2 22.923 51.2 51.2v526.49l135.975-130.597c15.17-14.57 39.133-14.576 54.31-0.014v0c14.406 13.822 14.879 36.706 1.057 51.112-0.337 0.352-0.682 0.697-1.033 1.034l-190.927 183.524c-19.817 19.049-51.14 19.050-70.959 0.004l-190.972-183.531c-14.393-13.833-14.848-36.714-1.016-51.108 0.339-0.352 0.684-0.698 1.037-1.036v0c15.176-14.561 39.138-14.552 54.304 0.020l131.373 126.239v-499.456h-243.302v170.803c0 21.208-17.192 38.4-38.4 38.4v0c-21.208 0-38.4-17.192-38.4-38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":74,"id":64,"name":"jump","prevSize":32,"code":59712},"setIdx":2,"setId":2,"iconIdx":60},{"icon":{"paths":["M512 768v0c19.206 0 34.775-15.569 34.775-34.775v-284.375c0-19.206-15.569-34.775-34.775-34.775v0c-19.206 0-34.775 15.569-34.775 34.775v284.375c0 19.206 15.569 34.775 34.775 34.775zM512 352c26 0 45.175-18.525 45.175-42.575 0-24.375-19.175-42.9-45.175-42.9s-45.175 18.525-45.175 42.9c0 24.050 19.175 42.575 45.175 42.575z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["italic"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":75,"id":63,"name":"italic","prevSize":32,"code":59713},"setIdx":2,"setId":2,"iconIdx":61},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383zM512.042 378.058c-24.155 0-42.364-18.678-42.364-43.535 0-24.904 18.21-43.722 42.364-43.722s42.318 18.818 42.318 43.722c0 24.857-18.163 43.535-42.318 43.535zM513.142 733.17c-21.065 0-38.245-17.133-38.245-38.198v-240.704c0-21.065 17.18-38.198 38.245-38.198s38.198 17.133 38.198 38.198v240.704c0 21.065-17.133 38.198-38.198 38.198z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["info-circled"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":76,"id":62,"name":"info-circled","prevSize":32,"code":59714},"setIdx":2,"setId":2,"iconIdx":62},{"icon":{"paths":["M512 102.4v0c20.524 0 37.161 16.638 37.161 37.161v449.409l74.777-76.869c14.366-14.768 37.984-15.094 52.752-0.728 0.246 0.239 0.489 0.482 0.728 0.728v0c14.886 15.302 14.886 39.673 0 54.976l-129.258 132.875c-19.717 20.269-52.132 20.716-72.401 0.999-0.338-0.328-0.671-0.661-0.999-0.999l-129.258-132.875c-14.886-15.302-14.886-39.673 0-54.976v0c14.366-14.768 37.984-15.094 52.752-0.728 0.246 0.239 0.489 0.482 0.728 0.728l75.858 77.981v-450.521c0-20.524 16.638-37.161 37.161-37.161zM691.2 301.894v-76.402h153.6c28.277 0 51.2 22.923 51.2 51.2v593.708c0 28.277-22.923 51.2-51.2 51.2h-665.6c-28.277 0-51.2-22.923-51.2-51.2v-593.708c0-28.277 22.923-51.2 51.2-51.2h153.6v76.402h-130.477v543.304h619.355v-543.304h-130.477z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["import"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":77,"id":61,"name":"import","prevSize":32,"code":59715},"setIdx":2,"setId":2,"iconIdx":63},{"icon":{"paths":["M277.962 392.518c-32.296 0.102-58.446 26.818-58.521 59.788v119.482c0.1 32.952 26.242 59.639 58.521 59.741h468.075c32.314-0.102 58.471-26.846 58.521-59.834v-119.436c-0.1-32.952-26.242-59.639-58.521-59.741h-468.075zM746.038 497.042l-73.128 74.7h-87.781l-73.128-74.653-73.128 74.607h-87.827l-73.037-74.607v-44.829h43.868l73.128 74.653 73.083-74.607h87.827l73.128 74.653 73.128-74.653h43.868v44.736zM394.958 691.223h234.083v59.695h-234.083v-59.695zM512 153.6c-225.847 0-409.6 173.834-409.6 388.271v268.788c0.1 32.952 26.242 59.639 58.521 59.741h702.158c32.279-0.102 58.421-26.789 58.521-59.741v-268.788c0-214.436-183.753-388.271-409.646-388.271h0.046zM863.079 810.659h-702.158v-268.788c0-184.566 154.448-333.918 351.034-333.918s351.079 149.353 351.079 333.918v268.788h0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hubot"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":78,"id":60,"name":"hubot","prevSize":32,"code":59716},"setIdx":2,"setId":2,"iconIdx":64},{"icon":{"paths":["M543.721 856.474v-127.067c54.295-7.873 101.717-35.408 135.714-75.366l107.588 67.22c-57.253 75.048-144.134 126.157-243.302 135.214zM166.889 531.706l126.839 4.415c6.645 60.075 37.046 112.503 82.148 148.184l-63.488 109.909c-83.422-59.164-139.401-154.192-145.499-262.508zM480.233 167.481v127.067c-54.295 7.919-101.717 35.408-135.714 75.366l-107.543-67.174c57.253-75.048 144.134-126.157 243.257-135.259zM856.974 490.018l-126.93-4.46c-7.191-59.119-37.456-110.729-81.92-145.909l63.442-109.909c82.876 58.755 138.581 152.872 145.408 260.278zM728.496 553.552l126.157 4.415c-5.188 38.958-16.839 75.776-33.906 109.545l-106.997-66.856c6.599-14.928 11.56-30.674 14.746-47.104zM543.721 294.548v-127.067c40.050 3.641 78.006 14.336 112.913 30.492l-63.124 109.363c-15.747-6.281-32.495-10.286-49.789-12.789zM295.276 472.633l-126.293-4.415c5.052-39.731 16.839-77.323 34.27-111.73l106.951 66.81c-6.872 15.61-11.833 32.131-14.928 49.334zM512 669.15c-86.744 0-157.195-70.497-157.195-157.15 0-86.699 70.451-157.195 157.195-157.195 86.699 0 157.195 70.497 157.195 157.195 0 86.653-70.497 157.15-157.195 157.15zM480.233 729.407v127.067c-40.004-3.641-77.961-14.29-112.913-30.492l63.169-109.363c15.701 6.281 32.495 10.286 49.744 12.789zM512 102.4c-225.826 0-409.6 183.728-409.6 409.6 0 225.826 183.774 409.6 409.6 409.6s409.6-183.774 409.6-409.6c0-225.872-183.774-409.6-409.6-409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["help"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":79,"id":59,"name":"help","prevSize":32,"code":59717},"setIdx":2,"setId":2,"iconIdx":65},{"icon":{"paths":["M292.571 643.657v-263.314h-146.286c-24.237 0-43.886-19.648-43.886-43.886v0c0-24.237 19.648-43.886 43.886-43.886h146.286v-146.286c0-24.237 19.648-43.886 43.886-43.886v0c24.237 0 43.886 19.648 43.886 43.886v146.286h263.314v-146.286c0-24.237 19.648-43.886 43.886-43.886v0c24.237 0 43.886 19.648 43.886 43.886v146.286h146.286c24.237 0 43.886 19.648 43.886 43.886v0c0 24.237-19.648 43.886-43.886 43.886h-146.286v263.314h146.286c24.237 0 43.886 19.648 43.886 43.886v0c0 24.237-19.648 43.886-43.886 43.886h-146.286v146.286c0 24.237-19.648 43.886-43.886 43.886v0c-24.237 0-43.886-19.648-43.886-43.886v-146.286h-263.314v146.286c0 24.237-19.648 43.886-43.886 43.886v0c-24.237 0-43.886-19.648-43.886-43.886v-146.286h-146.286c-24.237 0-43.886-19.648-43.886-43.886v0c0-24.237 19.648-43.886 43.886-43.886h146.286zM380.343 643.657h263.314v-263.314h-263.314v263.314z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hashtag"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":80,"id":58,"name":"hashtag","prevSize":32,"code":59718},"setIdx":2,"setId":2,"iconIdx":66},{"icon":{"paths":["M729.177 398.231c64.9-30.875 141.223 17.116 141.223 90.853v137.482c-0 7.684-0.875 15.342-2.607 22.828l-45.070 194.765c-10.486 45.318-50.562 77.441-96.639 77.441h-292.957c-31.771 0-61.833-15.527-80.442-41.539l-180.258-252.049c-31.972-44.7-22.262-107.036 21.821-139.638 31.412-23.228 72.528-25.465 105.831-7.241v-278.467c0-55.281 44.508-100.267 99.246-100.267s99.244 44.985 99.244 100.267v132.010c36.5-9.503 75.619 2.763 100.447 32.405 44.615-25.166 101.182-11.721 130.159 31.149zM230.246 537.691c-18.055 12.931-22.917 34.956-8.598 54.978l180.277 252.049c7.258 10.145 18.933 16.201 31.202 16.201h292.958c18.003 0 33.472-12.498 37.628-30.46l45.073-194.766c0.69-2.986 1.040-6.056 1.040-9.125v-137.481c0-23.624-16.54-38.907-38.761-40.254-21.568-1.307-38.352 11.161-38.581 31.558-0.011 1.235-0.011 1.235-0.017 3.133-0.035 12.857-10.45 23.261-23.284 23.261-12.869 0-23.301-10.451-23.301-23.343v-28.727c0-24.172-16.75-39.256-38.668-39.245-21.915 0.011-38.673 15.109-38.673 39.245v28.728c0 12.891-10.432 23.341-23.3 23.341s-23.3-10.45-23.3-23.341v-51.642c0-24.173-16.75-39.257-38.668-39.245-21.914 0.012-38.673 15.111-38.673 39.245v51.639c0 12.892-10.433 23.343-23.302 23.343s-23.302-10.451-23.302-23.343v-280.775c0-24.173-16.749-39.257-38.667-39.245-21.914 0.012-38.673 15.111-38.673 39.245v383.212c0 9.369-6.039 17.666-14.943 20.532s-18.637-0.356-24.084-7.972l-37.578-52.54c-13.477-18.842-35.918-21.017-53.804-8.208zM437.994 722.849v-100.908c0-12.892 10.433-23.343 23.302-23.343s23.302 10.451 23.302 23.343v100.908c0 12.892-10.433 23.343-23.302 23.343s-23.302-10.451-23.302-23.343zM585.24 598.598c12.869 0 23.301 10.451 23.301 23.343v100.91c0 12.892-10.432 23.343-23.301 23.343s-23.301-10.451-23.301-23.343v-100.91c0-12.892 10.432-23.343 23.301-23.343zM685.883 621.94c0-12.892 10.432-23.343 23.301-23.343s23.301 10.451 23.301 23.343v100.91c0 12.892-10.432 23.343-23.301 23.343s-23.301-10.451-23.301-23.343v-100.91z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hand-pointer"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":81,"id":57,"name":"hand-pointer","prevSize":32,"code":59719},"setIdx":2,"setId":2,"iconIdx":67},{"icon":{"paths":["M921.6 521.557c0 233.745-162.684 400.043-402.893 400.043-110.449 0.121-216.41-42.994-294.509-119.835s-121.921-181.095-121.798-289.764c0-226.6 185.997-409.6 416.307-409.6 112.125 0 206.488 40.505 279.157 107.179l-113.328 107.179c-148.205-140.629-423.847-34.998-423.847 195.243 0 142.905 116.011 258.64 258.018 258.64 164.858 0 226.656-116.281 236.37-176.583h-236.37v-140.811h396.324c3.886 20.981 6.568 41.142 6.568 68.358v-0.046z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["google"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":82,"id":56,"name":"google","prevSize":32,"code":59720},"setIdx":2,"setId":2,"iconIdx":68},{"icon":{"paths":["M150.050 416.542l361.813 466.808-396.538-292.201c-10.911-8.038-15.495-22.054-11.423-34.921l46.148-139.686zM270.655 138.978c-2.277-6.329-8.316-10.555-15.087-10.555s-12.81 4.225-15.087 10.555l-90.431 277.564h211.081l-90.476-277.564zM361.131 416.542l150.778 466.808 150.733-466.808h-301.511zM919.916 556.228l-46.148-139.686-361.859 466.808 396.584-292.201c10.894-8.050 15.46-22.064 11.378-34.921h0.046zM783.246 138.978c-2.277-6.329-8.316-10.555-15.087-10.555s-12.81 4.225-15.087 10.555l-90.431 277.564h211.081l-90.476-277.564z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["gitlab"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":83,"id":55,"name":"gitlab","prevSize":32,"code":59721},"setIdx":2,"setId":2,"iconIdx":69},{"icon":{"paths":["M372.17 762.031c0 3.362-3.897 6.070-8.873 6.070-5.634 0.514-9.53-2.194-9.53-6.070 0-3.408 3.943-6.070 8.873-6.070 5.117-0.56 9.53 2.148 9.53 6.070zM319.215 754.42c-1.221 3.362 2.206 7.284 7.324 8.264 4.413 1.728 9.53 0 10.563-3.362 0.986-3.408-2.253-7.284-7.324-8.824-4.46-1.167-9.389 0.514-10.563 3.922zM394.47 751.525c-4.929 1.167-8.31 4.389-7.84 8.311 0.516 3.362 4.976 5.603 10.047 4.389 4.976-1.167 8.356-4.389 7.84-7.797-0.469-3.222-5.070-5.416-10.047-4.902zM506.578 102.4c-236.281 0-416.978 178.354-416.978 413.343 0 187.832 118.868 348.585 288.72 405.172 21.783 3.875 29.435-9.478 29.435-20.497 0-10.505-0.469-68.447-0.469-104.024 0 0-119.244 25.446-144.313-50.425 0 0-19.389-49.351-47.322-62.050 0 0-39.012-26.613 2.723-26.053 0 0 42.393 3.362 65.725 43.701 37.322 65.365 99.808 46.55 124.173 35.391 3.943-27.080 15.023-45.896 27.229-57.101-95.16-10.505-191.26-24.232-191.26-187.179 0-46.596 12.957-69.941 40.233-99.776-4.46-11.019-18.919-56.401 4.413-114.996 35.585-11.019 117.507 45.756 117.507 45.756 34.842-9.61 70.833-14.478 106.991-14.474 36.143-0.020 72.121 4.849 106.944 14.474 0 0 81.921-56.961 117.554-45.756 23.332 58.782 8.826 103.978 4.413 114.996 27.229 29.975 43.942 53.366 43.942 99.776 0 163.46-100.324 176.487-195.532 187.225 15.68 13.353 28.966 38.752 28.966 78.579 0 57.055-0.516 127.696-0.516 141.61 0 11.019 7.84 24.372 29.435 20.497 170.368-56.261 285.81-217.013 285.81-404.846 0-234.989-191.588-413.343-427.822-413.343zM255.18 686.627c-2.253 1.728-1.737 5.603 1.174 8.824 2.723 2.708 6.619 3.875 8.873 1.681 2.206-1.681 1.69-5.603-1.221-8.778-2.723-2.708-6.619-3.922-8.826-1.728zM236.777 672.9c-1.221 2.241 0.469 4.949 3.897 6.63 2.723 1.681 6.103 1.167 7.324-1.167 1.221-2.241-0.469-4.949-3.897-6.63-3.427-1.027-6.103-0.467-7.324 1.167zM291.939 733.223c-2.723 2.194-1.69 7.284 2.206 10.505 3.943 3.875 8.873 4.389 11.079 1.681 2.206-2.194 1.221-7.284-2.206-10.505-3.756-3.875-8.873-4.389-11.079-1.681zM272.55 708.338c-2.723 1.681-2.723 6.070 0 9.992 2.723 3.875 7.324 5.603 9.53 3.875 2.723-2.194 2.723-6.583 0-10.505-2.394-3.875-6.807-5.603-9.53-3.362z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["github"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":84,"id":54,"name":"github","prevSize":32,"code":59722},"setIdx":2,"setId":2,"iconIdx":70},{"icon":{"paths":["M375.059 230.4h-170.259c-14.138 0-25.6 11.462-25.6 25.6v512c0 14.138 11.462 25.6 25.6 25.6h614.4c14.138 0 25.6-11.462 25.6-25.6v-409.6c0-14.138-11.462-25.6-25.6-25.6h-342.21l-82.738-93.74c-4.86-5.506-11.85-8.66-19.193-8.66zM511.641 256h358.759c28.277 0 51.2 22.923 51.2 51.2v512c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v-614.4c0-28.277 22.923-51.2 51.2-51.2h244.559c14.687 0 28.667 6.307 38.387 17.319l75.095 85.081z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["folder"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":85,"id":53,"name":"folder","prevSize":32,"code":59723},"setIdx":2,"setId":2,"iconIdx":71},{"icon":{"paths":["M649.479 321.022l142.305 128.268c21.614 19.482 22.48 51.889 1.935 72.384-10.193 10.169-24.34 15.925-39.135 15.925h-468.794v345.6c0 21.208-18.13 38.4-40.495 38.4s-40.495-17.192-40.495-38.4v-729.6c0-28.277 24.174-51.2 53.994-51.2h512.942c26.214 0 47.464 20.151 47.464 45.008 0 12.259-5.274 23.988-14.602 32.476l-155.119 141.137zM661.1 179.2h-375.309v281.6h373.075c5.964 0 10.799-4.585 10.799-10.24 0-2.779-1.191-5.439-3.301-7.369l-113.393-103.746c-10.73-9.817-11.036-26.023-0.683-36.198 0.224-0.22 0.451-0.436 0.683-0.647l115.626-105.79c4.292-3.927 4.414-10.409 0.273-14.479-2.035-2-4.84-3.13-7.771-3.13z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["flag"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":86,"id":52,"name":"flag","prevSize":32,"code":59724},"setIdx":2,"setId":2,"iconIdx":72},{"icon":{"paths":["M204.8 358.4h153.6v-102.4h-153.6v102.4zM204.8 460.8v102.4h153.6v-102.4h-153.6zM204.8 665.6v102.4h153.6v-102.4h-153.6zM460.8 768h358.4v-102.4h-358.4v102.4zM819.2 563.2v-102.4h-358.4v102.4h358.4zM819.2 358.4v-102.4h-358.4v102.4h358.4zM204.8 153.6h614.4c56.554 0 102.4 45.846 102.4 102.4v512c0 56.554-45.846 102.4-102.4 102.4h-614.4c-56.554 0-102.4-45.846-102.4-102.4v-512c0-56.554 45.846-102.4 102.4-102.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-sheets"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":87,"id":51,"name":"file-sheets","prevSize":32,"code":59725},"setIdx":2,"setId":2,"iconIdx":73},{"icon":{"paths":["M849.98 555.116c-38.964-38.325-150.107-27.786-205.678-20.759-54.933-33.535-91.661-79.844-117.53-147.871 12.456-51.42 32.257-129.667 17.246-178.851-13.414-83.677-120.724-75.373-136.054-18.843-14.053 51.42-1.278 122.96 22.356 214.301-31.938 76.331-79.525 178.851-113.059 237.616-63.875 32.896-150.107 83.677-162.882 147.552-10.539 50.461 83.038 176.296 243.045-99.645 71.54-23.634 149.468-52.697 218.453-64.195 60.362 32.576 130.944 54.294 178.212 54.294 81.441 0 89.425-90.064 55.891-123.599zM217.296 803.59c16.288-43.755 78.247-94.216 97.090-111.782-60.681 96.771-97.090 114.017-97.090 111.782zM477.907 194.859c23.634 0 21.398 102.52 5.749 130.305-14.053-44.393-13.733-130.305-5.749-130.305zM399.979 631.127c30.979-53.975 57.488-118.169 78.886-174.699 26.508 48.226 60.362 86.87 96.132 113.379-66.43 13.733-124.237 41.838-175.018 61.32zM820.278 615.159c0 0-15.969 19.163-119.127-24.911 112.101-8.304 130.625 17.246 119.127 24.911z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-pdf"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":88,"id":50,"name":"file-pdf","prevSize":32,"code":59726},"setIdx":2,"setId":2,"iconIdx":74},{"icon":{"paths":["M251.909 698.63v0c-13.538-11.36-15.304-31.544-3.944-45.083l294.504-350.976c49.32-58.777 136.496-66.538 195.386-17.125 58.618 49.186 66.186 136.85 17.036 195.424l-315.359 375.83c-71.816 85.587-199.714 96.629-285.354 24.784-85.712-71.905-96.714-200.039-24.728-285.829l342.715-408.431c94.541-112.67 262.87-127.247 375.541-32.689 112.773 94.643 127.503 262.904 32.947 375.591l-322.185 383.965c-11.36 13.538-31.544 15.304-45.083 3.944v0c-13.538-11.36-15.304-31.544-3.944-45.083l322.185-383.965c71.835-85.61 60.639-213.506-25.062-285.429-85.614-71.85-213.568-60.769-285.372 24.804l-342.715 408.431c-49.295 58.747-41.759 146.504 16.834 195.659 58.59 49.152 146.118 41.595 195.194-16.891l315.359-375.83c26.451-31.523 22.368-78.814-9.148-105.259-31.779-26.665-78.593-22.497-105.22 9.236l-294.504 350.976c-11.36 13.538-31.544 15.304-45.083 3.944z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-generic"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":89,"id":49,"name":"file-generic","prevSize":32,"code":59727},"setIdx":2,"setId":2,"iconIdx":75},{"icon":{"paths":["M153.6 153.6h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 358.4h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 563.2h716.8c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-716.8c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2zM153.6 768h307.2c28.277 0 51.2 22.923 51.2 51.2v0c0 28.277-22.923 51.2-51.2 51.2h-307.2c-28.277 0-51.2-22.923-51.2-51.2v0c0-28.277 22.923-51.2 51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-document"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":90,"id":48,"name":"file-document","prevSize":32,"code":59728},"setIdx":2,"setId":2,"iconIdx":76},{"icon":{"paths":["M921.6 147.547v728.724c0 25.031-20.298 45.147-45.147 45.147h-208.85v-317.076h106.45l15.929-123.608h-122.561v-79.007c0-35.817 9.876-60.166 61.258-60.166h65.49v-110.592c-31.712-3.406-63.588-5.062-95.482-4.961-94.345 0-159.061 57.617-159.061 163.476v91.25h-106.815v123.608h106.815v317.258h-392.078c-24.903-0.075-45.072-20.244-45.147-45.147v-728.906c0-24.849 20.298-45.147 45.147-45.147h728.724c25.031 0 45.329 20.298 45.329 45.147z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["facebook"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":91,"id":47,"name":"facebook","prevSize":32,"code":59729},"setIdx":2,"setId":2,"iconIdx":77},{"icon":{"paths":["M325.242 783.695l70.491-60.414c37.053 10.289 76.034 15.779 116.267 15.779 142.353 0 269.027-68.734 350.137-175.566 4.567-6.016 10.062-13.956 16.484-23.821 10.979-16.866 11.061-38.599 0.207-55.546-6.187-9.661-11.508-17.456-15.964-23.384-21.008-27.953-45.389-53.358-72.597-75.592l60.282-51.664c44.277 39.025 82.118 85.594 111.616 137.828 14.179 25.106 14.179 56.265 0 81.371-90.173 159.672-258.39 266.514-450.166 266.514-65.744 0-128.731-12.558-186.758-35.505zM168.775 686.343c-42.268-38.156-78.486-83.271-106.941-133.655-14.179-25.107-14.179-56.266 0-81.373 90.173-159.672 258.39-266.514 450.166-266.514 63.279 0 124.004 11.634 180.211 32.968l-72.317 61.974c-34.403-8.576-70.532-13.145-107.894-13.145-146.146 0-270.625 71.048-351.315 176.679-4.071 5.329-8.932 12.217-14.583 20.663-11.398 17.036-11.538 39.228-0.356 56.406l0.001-0c5.388 8.276 10.035 15.031 13.942 20.262 20.364 27.269 43.676 52.104 69.448 74.007l-60.362 51.729zM469.864 659.749l194.572-166.755c0.769 6.228 1.164 12.571 1.164 19.007 0 84.831-68.769 153.6-153.6 153.6-14.612 0-28.747-2.040-42.136-5.851zM358.822 523.476c-0.28-3.789-0.422-7.616-0.422-11.476 0-84.831 68.769-153.6 153.6-153.6 11.977 0 23.633 1.371 34.821 3.964l-187.999 161.112zM121.51 842.573c-13.181-15.357-11.417-38.491 3.939-51.672l715.824-613.448c15.373-13.174 38.514-11.395 51.692 3.975 13.174 15.365 11.398 38.501-3.967 51.675l-715.759 613.433c-15.382 13.183-38.536 11.409-51.73-3.963z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eye-off"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":92,"id":46,"name":"eye-off","prevSize":32,"code":59730},"setIdx":2,"setId":2,"iconIdx":78},{"icon":{"paths":["M537.998 831.538h341.366c23.326 0 42.236 19.514 42.236 43.585s-18.91 43.585-42.236 43.585l-464.6 2.891c-28.396 0-53.434-11.69-75.114-35.070l-215.032-215.032c-39.586-39.586-39.788-103.567-0.45-142.905l391.75-391.75c39.338-39.338 103.318-39.136 142.905 0.45l215.032 215.032c39.586 39.586 39.788 103.567 0.45 142.905l-336.308 336.308zM391.608 368.669l250.871 250.871 178.068-178.068c9.834-9.834 9.784-25.83-0.113-35.726l-215.032-215.032c-9.897-9.897-25.892-9.947-35.726-0.113l-178.068 178.068zM338.187 422.090l-160.262 160.262c-9.834 9.834-9.784 25.83 0.113 35.726l215.032 215.032c9.897 9.897 25.892 9.947 35.726 0.113l160.262-160.262-250.871-250.871z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eraser"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":93,"id":45,"name":"eraser","prevSize":32,"code":59731},"setIdx":2,"setId":2,"iconIdx":79},{"icon":{"paths":["M512 921.6c-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6s-183.384 409.6-409.6 409.6zM512 842.323c182.432 0 330.323-147.89 330.323-330.323s-147.89-330.323-330.323-330.323c-182.432 0-330.323 147.89-330.323 330.323s147.89 330.323 330.323 330.323zM617.703 459.148c-29.189 0-52.852-23.662-52.852-52.852s23.662-52.852 52.852-52.852c29.189 0 52.852 23.662 52.852 52.852s-23.662 52.852-52.852 52.852zM406.297 459.148c-29.189 0-52.852-23.662-52.852-52.852s23.662-52.852 52.852-52.852c29.189 0 52.852 23.662 52.852 52.852s-23.662 52.852-52.852 52.852zM365.618 626.852v0c15.048-15.048 38.673-17.26 56.252-5.268 1.49 1.018 2.891 1.924 4.202 2.72 50.324 30.547 112.95 32.907 165.176 7.082 4.591-2.27 9.926-5.508 16.005-9.714l0.001 0.001c17.854-12.352 41.986-10.172 57.338 5.179v0c13.89 13.89 13.89 36.411 0 50.302-1.206 1.206-2.497 2.324-3.864 3.345-12.813 9.569-23.658 16.594-32.536 21.074-79.213 39.971-175.185 35.294-250.475-14.032-2.476-1.622-5.219-3.565-8.23-5.828l0-0.001c-16.077-12.084-19.314-34.914-7.229-50.99 1.028-1.367 2.15-2.66 3.36-3.87z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["emoji"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":94,"id":44,"name":"emoji","prevSize":32,"code":59732},"setIdx":2,"setId":2,"iconIdx":80},{"icon":{"paths":["M774.213 358.611l-108.786-108.736 54.393-54.443c23.582-23.482 64.564-23.582 88.046-0.050l20.79 20.79c24.23 24.28 24.23 63.766 0 88.046l-54.443 54.393zM370.876 761.948l-110.338 33.528c-13.528 4.111-27.826-3.524-31.937-17.051-1.474-4.85-1.474-10.028-0.002-14.879l33.491-110.334 350.439-350.489 108.836 108.786-350.489 350.439zM860.763 142.484c-25.875-25.826-60.226-40.084-96.871-40.084-36.594 0-71.045 14.259-96.92 40.134l-470.992 471.042-65.715 216.365c-8.218 27.057 7.054 55.652 34.111 63.87 9.703 2.947 20.063 2.946 29.766-0.002l216.369-65.751 471.042-470.942c53.396-53.496 53.396-140.395 0-193.841l-20.79-20.79z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":95,"id":43,"name":"edit","prevSize":32,"code":59733},"setIdx":2,"setId":2,"iconIdx":81},{"icon":{"paths":["M801.451 563.304h65.536v247.856c0 60.994-49.446 110.44-110.44 110.44h-543.706c-60.994 0-110.44-49.446-110.44-110.44v-543.706c0-60.994 49.446-110.44 110.44-110.44h257.92l0.634 65.536h-258.554c-24.8 0-44.904 20.104-44.904 44.904v543.706c0 24.8 20.104 44.904 44.904 44.904h543.706c24.8 0 44.904-20.104 44.904-44.904v-247.856zM795.829 321.033l46.458-46.415c20.676-20.719 20.676-54.414 0-75.133l-17.741-17.741c-20.038-20.081-55.009-19.996-75.133 0.043l-46.415 46.458 92.831 92.788zM451.649 665.214l299.084-299.042-92.873-92.831-299.042 299.084-28.579 94.152c-1.256 4.139-1.256 8.558 0.002 12.696 3.508 11.544 15.709 18.058 27.253 14.55l94.155-28.61zM869.686 136.605l17.741 17.741c45.565 45.607 45.565 119.761 0 165.411l-401.955 401.87-184.635 56.108c-8.28 2.516-17.12 2.517-25.4 0.002-23.088-7.012-36.12-31.414-29.108-54.502l56.077-184.632 401.913-401.955c22.080-22.080 51.478-34.248 82.705-34.248 31.27 0 60.583 12.168 82.663 34.205z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit-rounded"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":96,"id":42,"name":"edit-rounded","prevSize":32,"code":59734},"setIdx":2,"setId":2,"iconIdx":82},{"icon":{"paths":["M172.192 806.879c-1.753 0.912-3.253 1.7-4.578 2.41 23.378 19.728 84.492 33.203 196.047 32.831l5.943-0c68.451 0 127.027-21.801 175.729-65.402l78.706 5.168c-32.607 31.619-49.869 48.22-51.786 49.803-52.616 43.442-123.449 67.577-202.649 67.577l-5.671 0.032-6.699 0c-136.95 0-222.963-18.909-249.539-75.336-14.428-30.613 1.439-48.51 37.772-67.626 24.169-12.773 24.606-13.408 20.758-21.754-2.23-5.305-4.163-9.569-8.543-18.995-16.964-36.242-23.235-54.989-23.235-80.395 0-43.195 10.472-84.864 30.015-122.108 10.746-18.442 29.443-48.938 54.204-61.082 0-1.302 13.676 61.082 13.676 59.997-4.346 6.059-8.904 12.78-13.676 20.163-24.761 36.112-26.943 64.99-26.943 103.030 0 14.972 4.334 27.305 17.854 56.249 5.229 11.287 6.997 15.182 9.089 20.209 16.231 34.899 4.636 61.658-24.761 82.359-6.556 4.617-11.96 7.723-21.713 12.869zM848.367 630.922c-1.774-0.959-3.814-2.034-6.289-3.322-11.28-5.952-17.509-9.532-25.058-14.848-33.726-23.75-46.973-54.324-28.286-94.515 2.332-5.613 4.375-10.115 10.412-23.145 15.698-33.609 20.736-47.942 20.736-65.422 0-132.445-107.986-240.215-240.702-240.215s-240.702 107.77-240.702 240.215c0 139.146 115.308 240.178 274.83 240.178l6.872 0c130.587 0.435 201.591-15.525 228.189-38.926zM847.938 544.158c-4.749 10.308-3.991 11.411 24.298 26.361 41.68 21.929 59.773 42.339 43.344 77.195-30.548 64.862-129.872 86.698-287.964 86.698h-7.717l-6.622-0.038c-196.405 0-339.44-128.159-339.44-304.704 0-168.061 136.964-304.742 305.341-304.742 168.384 0 305.379 136.688 305.379 304.742 0 29.238-7.227 50.846-26.812 92.685-5.065 10.9-7.304 15.839-9.807 21.804z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["discussion"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":97,"id":41,"name":"discussion","prevSize":32,"code":59735},"setIdx":2,"setId":2,"iconIdx":83},{"icon":{"paths":["M327.788 691.2h-104.056c36.791 59.058 91.226 105.986 155.931 133.412-21.594-36.602-39.346-81.994-51.874-133.412zM313.652 614.4c-4.212-32.729-6.452-67.041-6.452-102.4 0-26.25 1.235-51.923 3.594-76.8h-129.45c-5.709 24.678-8.727 50.387-8.727 76.8 0 35.688 5.509 70.089 15.722 102.4h125.313zM322.087 358.4c12.547-61.994 32.463-116.447 57.576-159.012-73.799 31.281-134.238 87.932-170.378 159.012h112.802zM701.913 358.4h112.802c-36.139-71.081-96.578-127.731-170.378-159.012 25.112 42.566 45.028 97.018 57.576 159.012zM713.206 435.2c2.359 24.877 3.594 50.55 3.594 76.8 0 35.359-2.24 69.671-6.452 102.4h125.313c10.213-32.311 15.722-66.712 15.722-102.4 0-26.413-3.018-52.122-8.727-76.8h-129.45zM696.212 691.2c-12.528 51.419-30.28 96.81-51.874 133.412 64.705-27.426 119.139-74.355 155.931-133.412h-104.056zM403.174 691.2c22.577 95.277 62.874 158.72 108.826 158.72s86.249-63.443 108.826-158.72h-217.652zM389.768 614.4h244.464c3.749-31.537 5.768-65.070 5.768-99.84 0-27.341-1.249-53.918-3.605-79.36h-248.79c-2.356 25.442-3.605 52.019-3.605 79.36 0 34.77 2.020 68.303 5.768 99.84zM398.695 358.4h226.611c-21.439-106.558-64.134-179.2-113.305-179.2s-91.867 72.642-113.305 179.2zM512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["discover"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":98,"id":40,"name":"discover","prevSize":32,"code":59736},"setIdx":2,"setId":2,"iconIdx":84},{"icon":{"paths":["M530.684 472.361c17.254-61.004 73.343-105.703 139.871-105.703s122.617 44.699 139.871 105.703h71.536c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-71.536c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-388.645c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h388.645zM670.555 578.065c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065zM406.297 630.916c66.528 0 122.617 44.699 139.871 105.703h335.794c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-335.794c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-124.387c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h124.387c17.254-61.004 73.343-105.703 139.871-105.703zM406.297 842.323c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065zM353.445 102.4c66.528 0 122.617 44.699 139.871 105.703h388.645c21.892 0 39.639 17.747 39.639 39.639s-17.747 39.639-39.639 39.639h-388.645c-17.254 61.004-73.343 105.703-139.871 105.703s-122.617-44.699-139.871-105.703h-71.536c-21.892 0-39.639-17.747-39.639-39.639s17.747-39.639 39.639-39.639h71.536c17.254-61.004 73.343-105.703 139.871-105.703zM353.445 313.806c36.486 0 66.065-29.578 66.065-66.065s-29.578-66.065-66.065-66.065c-36.486 0-66.065 29.578-66.065 66.065s29.578 66.065 66.065 66.065z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["customize"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":99,"id":39,"name":"customize","prevSize":32,"code":59649},"setIdx":2,"setId":2,"iconIdx":85},{"icon":{"paths":["M102.436 291.481c-0.631-14.945 6.986-30.23 22.636-36.505l374.511-150.166c8.022-3.216 16.878-3.212 24.897 0.011l369.772 148.636c15.151 3.68 27.348 17.918 27.348 36.276v396.377c0 14.076-7.511 26.944-19.403 33.24l-374.143 198.101c-9.405 5.13-21.009 5.858-31.74 0.176-0.112-0.056-0.222-0.116-0.332-0.176l-374.143-198.101c-11.892-6.296-19.403-19.164-19.403-33.24v-394.628zM231.134 291.933l280.884 128.051 280.542-127.895-280.557-112.774-280.868 112.619zM547.125 486.752v337.49l304.261-161.1v-316.94l-15.771 7.19-288.49 133.36zM172.65 346.475v316.667l304.261 161.1v-337.466l-304.261-140.301z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cube"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":100,"id":38,"name":"cube","prevSize":32,"code":59737},"setIdx":2,"setId":2,"iconIdx":86},{"icon":{"paths":["M512 447.614l210.621-210.621c17.78-17.78 46.606-17.78 64.386 0v0c17.78 17.78 17.78 46.606 0 64.386l-210.621 210.621 210.621 210.621c17.78 17.78 17.78 46.606 0 64.386v0c-17.78 17.78-46.606 17.78-64.386 0l-210.621-210.621-210.621 210.621c-17.78 17.78-46.606 17.78-64.386 0v0c-17.78-17.78-17.78-46.606 0-64.386l210.621-210.621-210.621-210.621c-17.78-17.78-17.78-46.606 0-64.386v0c17.78-17.78 46.606-17.78 64.386 0l210.621 210.621z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cross"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":101,"id":37,"name":"cross","prevSize":32,"code":59650},"setIdx":2,"setId":2,"iconIdx":87},{"icon":{"paths":["M153.6 153.6v576.366c0 28.277 22.923 51.2 51.2 51.2h133.781v89.234c0 28.277 22.923 51.2 51.2 51.2h429.419c28.277 0 51.2-22.923 51.2-51.2v-351.93c0-13.473-5.31-26.403-14.78-35.986l-221.761-224.436c-9.619-9.735-22.735-15.214-36.42-15.214h-24.857l-123.655-125.211c-9.62-9.741-22.739-15.223-36.429-15.223h-207.696c-28.277 0-51.2 22.923-51.2 51.2zM248.568 172.57h145.906c6.846 0 13.408 2.742 18.218 7.615l61.85 62.649h-135.961v468.114h-90.013c-14.138 0-25.6-11.462-25.6-25.6v-487.178c0-14.138 11.462-25.6 25.6-25.6zM433.548 313.051h110.658c14.138 0 25.6 11.462 25.6 25.6v182.81c0 14.138 11.462 25.6 25.6 25.6h180.026c14.138 0 25.6 11.462 25.6 25.6v253.074c0 14.138-11.462 25.6-25.6 25.6h-341.884c-14.138 0-25.6-11.462-25.6-25.6v-487.085c0-14.138 11.462-25.6 25.6-25.6zM647.937 371.541l95.417 96.585c1.987 2.012 1.968 5.253-0.044 7.241-0.958 0.947-2.251 1.478-3.598 1.478h-95.417c-2.828 0-5.12-2.292-5.12-5.12v-96.585c0-2.828 2.292-5.12 5.12-5.12 1.369 0 2.68 0.548 3.642 1.522z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["copy"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":102,"id":36,"name":"copy","prevSize":32,"code":59738},"setIdx":2,"setId":2,"iconIdx":88},{"icon":{"paths":["M181.677 299.476v265.758c0 36.491 29.713 66.232 66.374 66.232h527.897c36.726 0 66.374-29.67 66.374-66.232v-265.758c0-36.491-29.713-66.232-66.374-66.232h-527.897c-36.726 0-66.374 29.67-66.374 66.232zM102.4 299.476c0-80.618 65.21-145.876 145.652-145.876h527.897c80.379 0 145.652 65.334 145.652 145.876v265.758c0 80.618-65.21 145.876-145.652 145.876h-527.897c-80.379 0-145.652-65.334-145.652-145.876v-265.758zM340.232 830.578v0c0-21.993 17.829-39.822 39.822-39.822h263.891c21.993 0 39.822 17.829 39.822 39.822v0c0 21.993-17.829 39.822-39.822 39.822h-263.891c-21.993 0-39.822-17.829-39.822-39.822z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["computer"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":103,"id":35,"name":"computer","prevSize":32,"code":59739},"setIdx":2,"setId":2,"iconIdx":89},{"icon":{"paths":["M448.953 104.267c3.112-1.171 6.459-1.823 9.925-1.866h105.986c13.206-0.126 24.859 8.613 28.435 21.327l22.541 79.214c5.495 1.863 10.893 3.889 16.211 6.056 8.48 3.267 16.857 6.885 25.115 10.967l71.744-39.959c11.519-6.461 25.938-4.401 35.187 5.027l74.944 74.944c2.209 2.266 3.991 4.82 5.328 7.553 4.372 8.465 4.433 18.757-0.323 27.457l-40.062 71.725c6.617 13.221 12.412 27.104 17.235 41.325l49.353 14.166c1.121 0.265 2.216 0.594 3.283 0.982l26.416 7.517c12.713 3.577 21.453 15.229 21.327 28.435v105.986c-0.012 0.918-0.066 1.828-0.161 2.727-0.132 1.48-0.376 2.96-0.737 4.428-2.516 10.235-10.302 18.351-20.424 21.29l-78.983 22.371c-4.661 13.983-10.365 27.853-16.99 41.276l24.881 44.915c1.28 2.073 2.28 4.268 2.997 6.531l11.431 20.629c6.344 11.31 4.484 25.446-4.57 34.73l-72.429 72.429c-2.195 2.853-4.926 5.323-8.104 7.247-9.017 5.458-20.26 5.692-29.496 0.612l-71.788-40.041c-13.602 6.728-27.455 12.391-41.63 17.099l-6.044 21.069-16.361 57.764c-3.51 12.736-15.122 21.538-28.332 21.478h-106.018c-13.211 0.061-24.822-8.742-28.332-21.478l-19.784-69.849-2.577-8.984c-5.736-1.907-11.378-4.027-16.964-6.321-8.402-3.243-16.62-6.786-24.651-10.81l-39.595 21.941-31.885 17.81c-9.012 5.118-19.837 4.998-28.573 0.179-2.491-1.357-4.814-3.095-6.879-5.2l-74.944-74.944c-1.102-1.13-2.098-2.333-2.985-3.593-6.556-9.146-7.355-21.437-1.653-31.476l32.471-58.134 7.354-13.271c-6.703-13.371-12.5-27.189-17.365-41.585l-79.056-22.392c-12.555-3.68-21.139-15.25-21.021-28.332v-106.475c0.085-12.913 8.629-24.243 21.021-27.875l79.056-22.849c4.827-14.236 10.63-28.133 17.256-41.366l-40.039-71.683c-4.438-8.119-4.682-17.623-1.142-25.735 1.393-3.382 3.436-6.539 6.104-9.275l74.944-74.944c8.713-8.881 22.014-11.224 33.157-6.064 0.665 0.301 1.323 0.629 1.972 0.984l72.058 39.745c13.457-6.642 27.363-12.357 41.382-17.025l22.34-78.873c2.656-9.246 9.626-16.443 18.445-19.531zM777.457 572.567c3.652-4.403 8.577-7.714 14.244-9.384l71.411-20.357v-62.041l-5.361-1.511c-1.065-0.227-2.124-0.515-3.172-0.865l-10.15-2.889-52.405-14.769c-3.317-0.921-6.391-2.403-9.115-4.338-5.474-3.771-9.59-9.357-11.499-15.932-5.939-21.558-14.215-42.045-25.134-61.234-5.192-8.852-5.366-19.777-0.457-28.789l35.999-64.707-43.757-43.757-64.303 35.867c-2.389 1.314-4.915 2.269-7.503 2.868-7.16 1.773-14.836 0.796-21.427-2.93-12.746-7.252-26.064-13.54-39.887-18.685-7.025-2.548-14.144-4.804-21.335-6.78-3.065-0.851-5.923-2.181-8.488-3.905-5.768-3.77-10.11-9.519-12.087-16.327l-20.107-70.831h-61.858l-20.248 71.029c-2.902 9.843-10.75 17.446-20.68 20.034-21.289 5.849-41.628 14.483-61.071 25.527-2.615 1.438-5.394 2.447-8.239 3.028-6.963 1.548-14.363 0.518-20.746-3.090l-37.47-20.994-26.929-14.811-43.622 43.622 21.999 39.999 13.821 24.843c1.956 3.59 3.105 7.485 3.451 11.43 0.592 5.962-0.65 12.065-3.727 17.436-8.467 14.906-15.328 30.149-20.704 46.044-1.652 4.923-3.173 9.908-4.566 14.951-2.649 9.813-10.207 17.555-19.968 20.433l-71.411 20.383v61.726l34.007 9.873 37.081 10.45c9.887 2.745 17.612 10.47 20.357 20.357 5.849 21.289 14.16 41.952 25.204 61.394 5.139 8.867 5.261 19.778 0.323 28.758l-20.483 36.577-15.641 28.115 43.88 43.88 64.4-36.069c8.932-5.050 19.857-5.050 28.789 0 6.009 3.419 12.146 6.624 18.403 9.596 13.962 6.514 28.424 11.754 43.187 15.81 9.887 2.745 17.612 10.47 20.357 20.357l20.034 71.088h62.041l6.578-23.074 13.561-48.080c1.747-6.016 5.341-11.205 10.134-14.932 3.129-2.475 6.78-4.321 10.765-5.36 7.389-2.030 14.625-4.357 21.736-6.991 13.565-5.095 26.64-11.29 39.163-18.414 8.932-5.050 19.857-5.050 28.789 0l15.143 8.485 49.549 27.357 43.61-43.61-36.101-64.89c-4.677-8.587-4.74-18.911-0.244-27.523 0.18-0.363 0.368-0.723 0.565-1.079 3.224-5.676 6.243-11.428 9.047-17.258 6.65-13.966 12.022-28.454 16.224-43.707 1.161-4.276 3.252-8.152 6.047-11.403zM466.144 357.634c14.499-4.319 29.841-6.64 45.704-6.64 14.901 0 29.342 2.048 43.056 5.876 25.934 7.136 50.404 20.855 70.704 41.155 17.869 17.869 30.638 38.967 38.307 61.451 5.693 16.432 8.788 34.054 8.788 52.372 0 22.987-4.874 44.881-13.64 64.699-7.785 17.793-18.937 34.443-33.455 48.961-11.806 11.806-25.021 21.385-39.115 28.739-22.334 11.78-47.74 18.456-74.645 18.456-28.401 0-55.133-7.44-78.343-20.469-12.696-7.061-24.623-15.969-35.38-26.726-12.399-12.399-22.342-26.352-29.83-41.245-11.061-21.789-17.302-46.401-17.302-72.414 0-20.811 3.995-40.726 11.256-59.015 7.82-19.992 19.779-38.711 35.876-54.808 19.599-19.599 43.082-33.062 68.019-40.391zM569.978 416.696c-16.89-10.293-36.78-16.209-58.13-16.209-19.935 0-38.597 5.158-54.733 14.221-8.575 4.873-16.656 10.978-23.993 18.315-9.811 9.811-17.419 20.952-22.824 32.827-6.307 14-9.812 29.567-9.812 45.999 0 11.865 1.827 23.279 5.217 33.978 5.262 16.307 14.401 31.667 27.418 44.684 15.358 15.358 33.978 25.318 53.566 29.881 8.078 1.844 16.499 2.818 25.16 2.818 9.391 0 18.499-1.145 27.194-3.303 18.855-4.762 36.733-14.56 51.569-29.396 11.347-11.347 19.748-24.475 25.202-38.458 4.78-12.454 7.397-26.006 7.397-40.205 0-19.452-4.911-37.692-13.569-53.556-4.986-9.051-11.329-17.57-19.029-25.27-6.371-6.371-13.303-11.813-20.633-16.326z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cog"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":104,"id":34,"name":"cog","prevSize":32,"code":59740},"setIdx":2,"setId":2,"iconIdx":90},{"icon":{"paths":["M118.051 475.773l144.245-144.063c14.452-14.433 37.864-14.432 52.314 0.003v0c14.43 14.416 14.442 37.8 0.027 52.23-0.009 0.009-0.018 0.018-0.027 0.027l-128.16 128.029 128.16 128.029c14.43 14.416 14.442 37.8 0.027 52.23-0.009 0.009-0.018 0.018-0.027 0.027v0c-14.45 14.435-37.862 14.437-52.314 0.003l-144.245-144.063c-20.007-19.982-20.028-52.4-0.046-72.408 0.015-0.015 0.030-0.030 0.046-0.046zM607.804 154.853v0c19.741 5.277 31.466 25.558 26.189 45.299-0.003 0.011-0.006 0.023-0.009 0.034l-172.483 642.778c-5.299 19.747-25.595 31.465-45.346 26.182v0c-19.732-5.278-31.449-25.553-26.171-45.285 0.002-0.009 0.005-0.017 0.007-0.026l172.438-642.777c5.301-19.76 25.609-31.489 45.374-26.206zM709.395 331.733v0c14.45-14.435 37.862-14.437 52.314-0.003l144.24 144.059c20.007 19.982 20.028 52.4 0.046 72.408-0.017 0.017-0.033 0.033-0.050 0.050l-144.233 144.017c-14.454 14.432-37.866 14.429-52.317-0.006v0c-14.428-14.413-14.44-37.794-0.027-52.222 0.010-0.010 0.021-0.021 0.031-0.031l128.156-127.984-128.16-128.029c-14.43-14.416-14.442-37.8-0.027-52.23 0.009-0.009 0.018-0.018 0.027-0.027z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["code"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":105,"id":33,"name":"code","prevSize":32,"code":59741},"setIdx":2,"setId":2,"iconIdx":91},{"icon":{"paths":["M799.928 381.24c98.094 16.763 172.872 119.941 172.872 225.369 0 117.411-92.744 212.591-207.149 212.591v-78.095c72.379 0 131.053-60.216 131.053-134.496s-58.675-150.171-131.053-150.171h-38.048v-39.047c0-50.319-39.747-91.11-88.778-91.11-21.585 0-41.88 7.883-57.889 22.031l-30.189 26.68-24.581-32.176c-28.71-37.581-72.449-59.92-119.855-59.92-84.053 0-152.191 69.928-152.191 156.189 0 17.991 2.949 35.513 8.65 52.067l17.929 52.060h-64.627c-62.473 0-89.968 42.976-88.778 106.785s39.747 91.11 88.778 91.11h549.578v78.095h-549.578c-91.057 0-164.873-75.756-164.873-169.205 0-80.696 55.043-163.872 128.698-180.794-1.243-9.927-1.873-19.982-1.873-30.117 0-129.391 102.207-234.284 228.286-234.284 58.821 0 113.995 23.013 155.636 62.872 23.365-12.656 49.63-19.487 76.878-19.487 78.967 0 144.968 56.975 161.102 133.055zM476.137 464.902c0-17.468 14.16-31.628 31.628-31.628s31.628 14.16 31.628 31.628v58.489h58.497c17.472 0 31.636 14.164 31.636 31.636s-14.164 31.636-31.636 31.636h-58.497v58.489c0 17.468-14.16 31.628-31.628 31.628s-31.628-14.16-31.628-31.628v-58.489h-58.482c-17.472 0-31.636-14.164-31.636-31.636s14.164-31.636 31.636-31.636h58.482v-58.489z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cloud-plus"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":106,"id":32,"name":"cloud-plus","prevSize":32,"code":59742},"setIdx":2,"setId":2,"iconIdx":92},{"icon":{"paths":["M512 102.4c226.27 0 409.6 183.33 409.6 409.6s-183.33 409.6-409.6 409.6c-226.27 0-409.6-183.33-409.6-409.6s183.33-409.6 409.6-409.6zM850.057 512c0-187.006-151.632-338.057-338.057-338.057-187.006 0-338.057 151.632-338.057 338.057 0 187.006 151.632 338.057 338.057 338.057 187.006 0 338.057-151.632 338.057-338.057zM552.606 517.066l98.098 72.79c17.476 12.968 21.132 37.648 8.164 55.124-0.089 0.12-0.178 0.239-0.269 0.358v0c-13.398 17.635-38.434 21.325-56.351 8.307l-118.92-86.409c-7.516-5.577-11.933-14.344-11.933-23.653v-245.889c0-22.426 18.18-40.606 40.606-40.606v0c22.426 0 40.606 18.18 40.606 40.606v219.373z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clock"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":107,"id":31,"name":"clock","prevSize":32,"code":59743},"setIdx":2,"setId":2,"iconIdx":93},{"icon":{"paths":["M736.78 202.915c45.52 0 82.42 37.126 82.42 82.925v552.834c0 45.799-36.9 82.925-82.42 82.925h-449.561c-45.52 0-82.42-37.126-82.42-82.925v-552.834c0-45.799 36.9-82.925 82.42-82.925h130.008c-0.090-1.676-0.135-3.351-0.135-5.026 0-52.66 42.569-95.49 94.907-95.49s94.907 42.83 94.907 95.49c0 1.674-0.045 3.349-0.135 5.026h130.009zM512 152.658c-24.829 0-44.956 20.251-44.956 45.232s20.127 45.232 44.956 45.232c24.829 0 44.956-20.251 44.956-45.232s-20.127-45.232-44.956-45.232zM354.654 268.25h-67.434c-9.633 0-17.483 7.898-17.483 17.59v552.834c0 9.692 7.85 17.59 17.483 17.59h449.561c9.633 0 17.483-7.898 17.483-17.59v-552.834c0-9.692-7.85-17.59-17.483-17.59h-67.434v40.251c0 14.572-11.742 26.385-26.224 26.385h-262.244c-14.483 0-26.224-11.814-26.224-26.385v-40.251z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clipboard"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":108,"id":30,"name":"clipboard","prevSize":32,"code":59744},"setIdx":2,"setId":2,"iconIdx":94},{"icon":{"paths":["M264.13 689.35v0c-12.944-11.048-14.639-30.434-3.811-43.561l280.254-339.75c46.923-56.885 129.865-64.397 185.893-16.573 55.77 47.603 62.97 132.445 16.209 189.134l-300.037 363.734c-68.327 82.833-190.011 93.519-271.49 23.986-81.547-69.59-92.015-193.601-23.527-276.629l326.064-395.286c89.948-109.044 250.098-123.151 357.296-31.637 107.294 91.596 121.308 254.442 31.346 363.502l-306.589 371.677c-10.596 12.846-29.6 14.669-42.446 4.073-0.13-0.108-0.26-0.216-0.389-0.326v0c-12.944-11.048-14.639-30.434-3.811-43.561l306.589-371.677c68.345-82.855 57.693-206.634-23.845-276.242-81.454-69.537-203.192-58.813-271.507 24.006l-326.064 395.286c-46.9 56.857-39.73 141.789 16.016 189.362 55.743 47.57 139.019 40.256 185.711-16.347l300.037-363.734c25.165-30.508 21.281-76.277-8.703-101.871-30.235-25.807-74.774-21.773-100.108 8.939l-280.254 339.75c-10.596 12.846-29.6 14.669-42.446 4.073-0.13-0.108-0.26-0.216-0.389-0.326z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clip"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":109,"id":29,"name":"clip","prevSize":32,"code":59745},"setIdx":2,"setId":2,"iconIdx":95},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.36-409.6-409.6s183.407-409.6 409.6-409.6c226.193 0 409.6 183.36 409.6 409.6s-183.407 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383zM547.792 563.291l80.305-80.305c13.715-13.715 35.952-13.715 49.667 0v0c13.715 13.715 13.715 35.952 0 49.667l-129.293 129.302c-19.995 19.995-52.413 19.995-72.408 0-0.001-0.001-0.003-0.003-0.004-0.004l-129.266-129.307c-13.709-13.713-13.713-35.941-0.009-49.658v0c13.693-13.706 35.904-13.716 49.609-0.023 0.006 0.006 0.011 0.011 0.017 0.017l81.165 81.201v-242.951c0-19.39 15.719-35.109 35.109-35.109v0c19.39 0 35.109 15.719 35.109 35.109v242.062z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circled-arrow-down"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":110,"id":28,"name":"circled-arrow-down","prevSize":32,"code":59746},"setIdx":2,"setId":2,"iconIdx":96},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":111,"id":27,"name":"circle","prevSize":32,"code":59747},"setIdx":2,"setId":2,"iconIdx":97},{"icon":{"paths":["M512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM475.623 513.887l-107.775-107.775c-11.659-11.659-11.659-30.563 0-42.222s30.563-11.659 42.222 0l107.775 107.775 107.775-107.775c11.659-11.659 30.563-11.659 42.222 0s11.659 30.563 0 42.222l-107.775 107.775 107.775 107.775c11.659 11.659 11.659 30.563 0 42.222s-30.563 11.659-42.222 0l-107.775-107.775-107.775 107.775c-11.659 11.659-30.563 11.659-42.222 0s-11.659-30.563 0-42.222l107.775-107.775zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-cross"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":112,"id":26,"name":"circle-cross","prevSize":32,"code":59748},"setIdx":2,"setId":2,"iconIdx":98},{"icon":{"paths":["M430.733 607.019l248.174-242.694c15.137-14.803 39.379-14.632 54.306 0.382 14.93 15.211 14.704 39.364-0.333 54.121l-270.787 265.759c-17.457 17.133-45.472 16.961-62.717-0.386l-114.626-115.298c-14.926-15.014-14.926-39.263 0-54.277 15.105-15.018 39.269-14.947 54.13 0l91.854 92.393zM512 921.6c-226.193 0-409.6-183.407-409.6-409.6 0-226.24 183.407-409.6 409.6-409.6 226.24 0 409.6 183.36 409.6 409.6 0 226.193-183.36 409.6-409.6 409.6zM512 851.383c187.433 0 339.383-151.95 339.383-339.383s-151.95-339.383-339.383-339.383c-187.433 0-339.383 151.95-339.383 339.383s151.95 339.383 339.383 339.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["checkmark-circled"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":113,"id":25,"name":"checkmark-circled","prevSize":32,"code":59749},"setIdx":2,"setId":2,"iconIdx":99},{"icon":{"paths":["M402.241 688.422l389.132-389.132c14.441-14.441 37.854-14.441 52.294 0v0c14.441 14.441 14.441 37.854 0 52.294l-408.299 408.299c-19.995 19.995-52.413 19.995-72.408 0l-171.436-171.436c-15.29-15.29-15.29-40.080 0-55.371v0c15.29-15.29 40.080-15.29 55.371 0l155.345 155.345z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["check"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":114,"id":24,"name":"check","prevSize":32,"code":59648},"setIdx":2,"setId":2,"iconIdx":100},{"icon":{"paths":["M172.192 806.879c-1.753 0.912-3.253 1.7-4.578 2.41 23.378 19.728 84.492 33.203 196.047 32.831l5.943-0c68.451 0 127.027-21.801 175.729-65.402l78.706 5.168c-32.607 31.619-49.869 48.22-51.786 49.803-52.616 43.442-123.449 67.577-202.649 67.577l-5.671 0.032-6.699 0c-136.95 0-222.963-18.909-249.539-75.336-14.428-30.613 1.439-48.51 37.772-67.626 24.169-12.773 24.606-13.408 20.758-21.754-2.23-5.305-4.163-9.569-8.543-18.995-16.964-36.242-23.235-54.989-23.235-80.395 0-43.195 10.472-84.864 30.015-122.108 10.746-18.442 29.443-48.938 54.204-61.082 0-1.302 13.676 61.082 13.676 59.997-4.346 6.059-8.904 12.78-13.676 20.163-24.761 36.112-26.943 64.99-26.943 103.030 0 14.972 4.334 27.305 17.854 56.249 5.229 11.287 6.997 15.182 9.089 20.209 16.231 34.899 4.636 61.658-24.761 82.359-6.556 4.617-11.96 7.723-21.713 12.869zM848.367 630.922c-1.774-0.959-3.814-2.034-6.289-3.322-11.28-5.952-17.509-9.532-25.058-14.848-33.726-23.75-46.973-54.324-28.286-94.515 2.332-5.613 4.375-10.115 10.412-23.145 15.698-33.609 20.736-47.942 20.736-65.422 0-132.445-107.986-240.215-240.702-240.215s-240.702 107.77-240.702 240.215c0 139.146 115.308 240.178 274.83 240.178l6.872 0c130.587 0.435 201.591-15.525 228.189-38.926zM847.938 544.158c-4.749 10.308-3.991 11.411 24.298 26.361 41.68 21.929 59.773 42.339 43.344 77.195-30.548 64.862-129.872 86.698-287.964 86.698h-7.717l-6.622-0.038c-196.405 0-339.44-128.159-339.44-304.704 0-168.061 136.964-304.742 305.341-304.742 168.384 0 305.379 136.688 305.379 304.742 0 29.238-7.227 50.846-26.812 92.685-5.065 10.9-7.304 15.839-9.807 21.804z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chat"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":115,"id":23,"name":"chat","prevSize":32,"code":59750},"setIdx":2,"setId":2,"iconIdx":101},{"icon":{"paths":["M165.396 486.4h693.207v-189.098c0-29.619-23.593-53.706-52.552-53.706h-588.145c-28.959 0-52.511 24.086-52.511 53.706v189.098zM165.396 563.2v163.498c0 29.619 23.552 53.664 52.511 53.664h588.145c28.959 0 52.552-24.045 52.552-53.664v-163.498h-693.207zM806.052 844.8h-588.145c-63.734 0-115.507-52.957-115.507-118.102v-429.395c0-65.146 51.773-118.102 115.507-118.102h588.145c63.734 0 115.548 52.957 115.548 118.102v429.395c0 65.146-51.814 118.102-115.548 118.102zM739.84 732.16c-35.346 0-64-28.654-64-64s28.654-64 64-64c35.346 0 64 28.654 64 64s-28.654 64-64 64z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["card"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":116,"id":22,"name":"card","prevSize":32,"code":59751},"setIdx":2,"setId":2,"iconIdx":102},{"icon":{"paths":["M793.6 204.8c42.4 0 76.8 34.4 76.8 76.8v563.2c0 42.4-34.4 76.8-76.8 76.8h-563.2c-42.4 0-76.8-34.4-76.8-76.8v-563.2c0-42.4 34.4-76.8 76.8-76.8h76.8v-51.2c0-28.277 22.923-51.2 51.2-51.2s51.2 22.923 51.2 51.2v51.2h204.8v-51.2c0-28.277 22.923-51.2 51.2-51.2s51.2 22.923 51.2 51.2v51.2h76.8zM742.4 844.8c28.277 0 51.2-22.923 51.2-51.2v-435.2h-563.2v435.2c0 28.277 22.923 51.2 51.2 51.2h460.8zM322.56 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM322.56 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM476.16 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM476.16 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM629.76 460.8h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36zM629.76 614.4h71.68c8.483 0 15.36 6.877 15.36 15.36v71.68c0 8.483-6.877 15.36-15.36 15.36h-71.68c-8.483 0-15.36-6.877-15.36-15.36v-71.68c0-8.483 6.877-15.36 15.36-15.36z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["calendar"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":117,"id":21,"name":"calendar","prevSize":32,"code":59752},"setIdx":2,"setId":2,"iconIdx":103},{"icon":{"paths":["M336.457 324.799v-57.6c0-3.6 1.524-6.9 4.571-9.9s6.4-4.5 10.057-4.5h380.343c3.657 0 7.010 1.5 10.057 4.5s4.571 6.3 4.571 9.9v57.6c0 3.6-1.524 6.9-4.571 9.9s-6.4 4.5-10.057 4.5h-380.343c-3.657 0-7.010-1.5-10.057-4.5s-4.571-6.3-4.571-9.9zM731.429 483.199h-380.343c-3.657 0-7.010-1.5-10.057-4.5s-4.571-6.3-4.571-9.9v-57.6c0-3.6 1.524-6.9 4.571-9.9s6.4-4.5 10.057-4.5h380.343c3.657 0 7.010 1.5 10.057 4.5s4.571 6.3 4.571 9.9v57.6c0 3.6-1.524 6.9-4.571 9.9s-6.4 4.5-10.057 4.5zM897.829 771.198c-3.657 10.8-5.486 30-5.486 57.6s1.829 46.8 5.486 57.6c7.314 1.2 13.105 4.5 17.371 9.9s6.4 11.7 6.4 18.9v28.8c0 8.4-2.743 15.3-8.229 20.7s-12.495 8.1-21.029 8.1h-643.657c-40.229 0-74.667-14.1-103.314-42.3s-42.971-62.1-42.971-101.7v-633.598c0-39.6 14.324-73.5 42.971-101.7s63.086-42.3 103.314-42.3h643.657c8.533 0 15.543 2.7 21.029 8.1s8.229 12.3 8.229 20.7v662.398c0 7.2-2.133 13.5-6.4 18.9s-10.057 8.7-17.371 9.9zM822.857 771.198h-574.171c-15.848 0-29.562 5.7-41.143 17.1s-17.371 24.9-17.371 40.5c0 15.6 5.79 29.1 17.371 40.5s25.295 17.1 41.143 17.1h574.171c-2.438-15.6-3.657-34.8-3.657-57.6s1.219-42 3.657-57.6zM833.829 137.6h-585.143c-15.848 0-29.562 5.7-41.143 17.1s-17.371 24.9-17.371 40.5v502.199c18.286-8.4 37.79-12.6 58.514-12.6h585.143v-547.199z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["book"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":118,"id":20,"name":"book","prevSize":32,"code":59753},"setIdx":2,"setId":2,"iconIdx":104},{"icon":{"paths":["M538.229 742.4h-144.501c-28.277 0-51.2-22.923-51.2-51.2v-358.4c0-28.277 22.923-51.2 51.2-51.2h140.211c89.435 0 143.228 44.068 143.228 114.96 0 48.22-36.632 91.33-83.495 98.994v5.429c64.354 6.387 107.256 50.774 107.256 110.809 0 81.111-61.053 130.608-162.699 130.608zM416.452 342.274v134.44h87.785c65.344 0 99.336-23.311 99.336-67.060 0-42.472-32.012-67.38-86.795-67.38h-100.326zM416.452 681.726h106.926c66.664 0 101.976-26.185 101.976-75.044 0-48.22-36.632-73.447-106.266-73.447h-102.636v148.491z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bold"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":119,"id":19,"name":"bold","prevSize":32,"code":59754},"setIdx":2,"setId":2,"iconIdx":105},{"icon":{"paths":["M728.623 590.439v-212.883c0-113.231-96.906-205.203-216.623-205.203s-216.623 91.972-216.623 205.203v212.883l-0.885 3.58-78.182 148.403h591.449l-79.136-151.982zM512 854.645c23.566 0 44.551-11.762 55.954-29.929h-111.909c11.403 18.167 32.388 29.929 55.954 29.929zM125.951 743.361l85.798-149.441v-218.453c0-155.152 134.566-280.747 300.251-280.747s300.251 125.595 300.251 280.747l-0.91 214.827 81.715 154.639c13.211 25.001 3.653 55.978-21.348 69.189-7.372 3.895-15.583 5.932-23.921 5.932h-186.412c-16.891 63.603-78.184 109.227-149.376 109.227s-132.485-45.624-149.376-109.227h-192.271c-28.277 0-51.2-22.923-51.2-51.2 0-8.945 2.344-17.735 6.798-25.492z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bell"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":120,"id":18,"name":"bell","prevSize":32,"code":59755},"setIdx":2,"setId":2,"iconIdx":106},{"icon":{"paths":["M512 844.8c-78.029 0-149.658-27.085-206.438-72.038l467.2-467.2c44.954 56.781 72.038 128.41 72.038 206.438 0 183.808-148.992 332.8-332.8 332.8zM179.2 512c0-183.808 148.992-332.8 332.8-332.8 78.029 0 149.658 27.085 206.438 72.038l-467.2 467.2c-44.954-56.781-72.038-128.41-72.038-206.438zM512 102.4c-226.202 0-409.6 183.398-409.6 409.6s183.398 409.6 409.6 409.6c226.202 0 409.6-183.398 409.6-409.6s-183.398-409.6-409.6-409.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["ban"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":121,"id":17,"name":"ban","prevSize":32,"code":59756},"setIdx":2,"setId":2,"iconIdx":107},{"icon":{"paths":["M254.865 554.983l171.258 175.077c16.155 16.515 16.155 42.912 0 59.427v0c-15.702 16.052-41.444 16.336-57.497 0.634-0.214-0.209-0.425-0.42-0.634-0.634l-230.571-235.712c-19.466-19.9-19.466-51.705 0-71.605l243.364-248.79c15.103-15.44 39.864-15.713 55.304-0.61 0.205 0.201 0.409 0.404 0.61 0.61v0c15.539 15.885 15.539 41.275 0 57.16l-179.602 183.606h532.172c73.176 0 132.331 58.113 132.331 130.236v116.079c0 22.701-18.403 41.105-41.105 41.105v0c-22.701 0-41.105-18.403-41.105-41.105v-116.079c0-27.461-22.331-49.399-50.121-49.399h-534.404z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["back"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":122,"id":16,"name":"back","prevSize":32,"code":59757},"setIdx":2,"setId":2,"iconIdx":108},{"icon":{"paths":["M670.555 300.594v0c21.892 0 39.639 17.747 39.639 39.639v197.702c0 56.387 28.106 92.982 66.065 92.982 37.77 0 66.065-36.953 66.065-92.982v-25.934c0-182.432-147.89-330.323-330.323-330.323s-330.323 147.89-330.323 330.323c0 182.432 147.89 330.323 330.323 330.323 24.665 0 48.932-2.698 72.491-7.97 4.969-1.112 10.862-2.703 17.679-4.772l-0-0c20.847-6.329 43.096 4.186 51.441 24.311v0c7.896 19.041-1.14 40.877-20.18 48.773-0.979 0.406-1.976 0.77-2.986 1.092-10.574 3.362-19.5 5.876-26.778 7.54-29.799 6.815-60.501 10.304-91.666 10.304-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6v28.635c-1.103 94.621-59.289 169.558-145.342 169.558-50.481 0-91.316-25.583-116.702-65.873-36.285 40.436-88.95 65.873-147.556 65.873-109.459 0-198.194-88.734-198.194-198.194s88.734-198.194 198.194-198.194c44.618 0 85.792 14.743 118.916 39.624v-13.198c0-21.892 17.747-39.639 39.639-39.639zM512 630.916c65.676 0 118.916-53.241 118.916-118.916s-53.241-118.916-118.916-118.916c-65.676 0-118.916 53.241-118.916 118.916s53.241 118.916 118.916 118.916z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["at"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":123,"id":15,"name":"at","prevSize":32,"code":59758},"setIdx":2,"setId":2,"iconIdx":109},{"icon":{"paths":["M512 611.902l228.206-227.278c14.56-14.501 38.103-14.501 52.663 0v0c14.483 14.424 14.531 37.859 0.107 52.342-0.035 0.036-0.071 0.071-0.107 0.107l-244.739 243.744c-19.978 19.897-52.282 19.897-72.26 0l-244.739-243.744c-14.483-14.424-14.531-37.859-0.107-52.342 0.035-0.036 0.071-0.071 0.107-0.107v0c14.56-14.501 38.103-14.501 52.663 0l228.206 227.278z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":124,"id":14,"name":"arrow-down","prevSize":32,"code":59759},"setIdx":2,"setId":2,"iconIdx":110},{"icon":{"paths":["M849.683 174.317h87.602c19.614 0 35.514 15.9 35.514 35.514v0c0 19.614-15.9 35.514-35.514 35.514h-87.602v87.602c0 19.614-15.9 35.514-35.514 35.514v0c-19.614 0-35.514-15.9-35.514-35.514v-87.602h-87.602c-19.614 0-35.514-15.9-35.514-35.514v0c0-19.614 15.9-35.514 35.514-35.514h87.602v-87.602c0-19.614 15.9-35.514 35.514-35.514v0c19.614 0 35.514 15.9 35.514 35.514v87.602zM615.288 456.065c-26.152 0-47.353-21.2-47.353-47.353s21.2-47.353 47.353-47.353c26.152 0 47.353 21.2 47.353 47.353s-21.2 47.353-47.353 47.353zM425.877 456.065c-26.152 0-47.353-21.2-47.353-47.353s21.2-47.353 47.353-47.353c26.152 0 47.353 21.2 47.353 47.353s-21.2 47.353-47.353 47.353zM520.583 136.435v71.029c-28.136 1.51-48.899 3.698-62.287 6.566-133.542 28.605-233.667 147.304-233.667 289.388 0 163.451 132.503 295.954 295.954 295.954 142.116 0 260.836-100.17 289.407-233.758 2.859-13.369 5.042-34.101 6.546-62.196h71.029c-1.4 30.22-3.438 52.58-6.116 67.078-31.519 170.646-181.098 299.904-360.866 299.904-202.679 0-366.983-164.304-366.983-366.983 0-178.815 127.891-327.76 297.193-360.355 15.063-2.9 38.326-5.109 69.79-6.628zM389.432 606.319v0c13.749-13.749 35.363-15.7 51.352-4.635 5.331 3.688 10.014 6.536 14.049 8.542 42.649 21.211 93.092 21.405 135.884 0.581 4.331-2.107 9.382-5.156 15.155-9.146l0 0c16.018-11.071 37.659-9.111 51.427 4.657v0c12.383 12.383 12.383 32.459 0 44.842-1.101 1.101-2.281 2.119-3.532 3.047-13.135 9.744-24.196 16.769-33.182 21.075-64.229 30.776-140.044 29.304-203.163-4.418-6.784-3.624-14.983-9.053-24.597-16.286l0.001-0.001c-14.14-10.638-16.979-30.725-6.341-44.865 0.902-1.199 1.887-2.333 2.947-3.393z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["add-reaction"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":125,"id":13,"name":"add-reaction","prevSize":32,"code":59760},"setIdx":2,"setId":2,"iconIdx":111},{"icon":{"paths":["M786.466 512l54.306 54.306c14.996 14.996 14.996 39.31 0 54.306s-39.31 14.996-54.306 0l-54.306-54.306-54.306 54.306c-14.996 14.996-39.31 14.996-54.306 0s-14.996-39.31 0-54.306l54.306-54.306-54.306-54.306c-14.996-14.996-14.996-39.31 0-54.306s39.31-14.996 54.306 0l54.306 54.306 54.306-54.306c14.996-14.996 39.31-14.996 54.306 0s14.996 39.31 0 54.306l-54.306 54.306zM192 363.273v297.454h86.142l10.572 21.377c1.038 2.1 3.898 7.013 8.633 13.789 8.247 11.802 18.498 23.692 30.766 34.773 26.202 23.666 57.348 40.098 94.287 46.786v-531.14c-38.525 6.518-70.099 22.994-95.922 46.772-20.207 18.606-32.373 36.945-37.196 47.609l-10.21 22.579h-87.073zM115.2 686.327v-348.654c0-28.277 22.923-51.2 51.2-51.2h65.398c9.929-15.34 23.961-32.669 42.657-49.885 47.565-43.798 109.679-70.188 186.344-70.188 21.208 0 38.4 17.192 38.4 38.4v614.4c0 21.208-17.192 38.4-38.4 38.4-74.44 0-135.902-26.351-184.163-69.939-19.215-17.354-33.789-34.802-44.168-50.134h-66.069c-28.277 0-51.2-22.923-51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Volume-disable"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":126,"id":12,"name":"Volume-disable","prevSize":32,"code":59761},"setIdx":2,"setId":2,"iconIdx":112},{"icon":{"paths":["M598.819 531.695l109.94-87.952 116.726-37.506c23.977-7.717 48.302-4.473 66.818 8.847 18.566 13.369 29.189 35.29 29.189 60.209v220.784c0 24.919-10.673 46.791-29.189 60.16-11.964 8.601-26.261 12.976-41.352 12.976-8.389 0-16.978-1.327-25.516-4.079l-152.153-48.954v51.819c0 28.277-22.923 51.2-51.2 51.2h-382.644l92.157-73.726h241.624c14.138 0 25.6-11.462 25.6-25.6v-188.179zM444.104 425.997h-241.641c-14.138 0-25.6 11.462-25.6 25.6v188.193l-74.463 59.57v-295.889c0-28.277 22.923-51.2 51.2-51.2h382.661l-92.157 73.726zM846.779 659.382l0.202-147.149c0.019-14.138-11.426-25.616-25.565-25.635-2.659-0.004-5.303 0.407-7.836 1.218l-140.298 44.887v105.968l140.059 45.047c13.459 4.329 27.88-3.073 32.209-16.532 0.811-2.522 1.226-5.154 1.229-7.803zM226.5 241.663c0-20.359 16.504-36.863 36.863-36.863h248.947c20.359 0 36.863 16.504 36.863 36.863s-16.504 36.863-36.863 36.863h-248.947c-20.359 0-36.863-16.504-36.863-36.863zM64.947 844.058c-13.246-16.554-10.565-40.713 5.99-53.959l0.003-0.002 708.040-566.432c16.558-13.246 40.719-10.563 53.967 5.994 13.246 16.554 10.565 40.713-5.99 53.959l-708.043 566.434c-16.558 13.246-40.719 10.563-53.967-5.994z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Video-off"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":127,"id":11,"name":"Video-off","prevSize":32,"code":59762},"setIdx":2,"setId":2,"iconIdx":113},{"icon":{"paths":["M272.433 838.351l34.767-213.14-149.132-152.849c-15.798-16.192-15.479-42.124 0.713-57.922 6.123-5.974 13.962-9.881 22.418-11.173l204.228-31.203 89.495-190.668c9.612-20.478 34.004-29.287 54.482-19.675 8.653 4.062 15.613 11.022 19.675 19.675l89.495 190.668 204.228 31.203c22.362 3.417 37.72 24.314 34.304 46.676-1.292 8.456-5.199 16.295-11.173 22.418l-149.132 152.849 34.767 213.14c3.642 22.327-11.505 43.378-33.832 47.020-9.068 1.479-18.369-0.133-26.41-4.578l-179.325-99.129-179.325 99.129c-19.798 10.944-44.719 3.767-55.664-16.032-4.445-8.041-6.057-17.343-4.578-26.41z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Star-filled"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":128,"id":10,"name":"Star-filled","prevSize":32,"code":59763},"setIdx":2,"setId":2,"iconIdx":114},{"icon":{"paths":["M315.97 527.941l-244.169-127.797c-30.836-16.139-26.016-61.689 7.515-71.017l809.701-225.253c32.369-9.005 59.876 24.965 44.339 54.754l-388.677 745.174c-16.096 30.859-61.652 26.103-71.027-7.414l-71.019-253.895 192.101-250.863-278.763 136.311z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Send-active"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":129,"id":9,"name":"Send-active","prevSize":32,"code":59764},"setIdx":2,"setId":2,"iconIdx":115},{"icon":{"paths":["M587.366 294.4v0c0 21.208 17.192 38.4 38.4 38.4h170.803v243.302h-499.456l126.239-131.373c14.572-15.165 14.581-39.128 0.020-54.304v0c-13.821-14.405-36.702-14.878-51.107-1.057-0.353 0.338-0.698 0.684-1.037 1.036l-183.531 190.972c-19.047 19.819-19.045 51.142 0.004 70.959l183.524 190.927c13.835 14.394 36.72 14.846 51.113 1.010 0.351-0.338 0.696-0.683 1.033-1.034v0c14.562-15.177 14.556-39.14-0.014-54.31l-130.597-135.975h526.49c28.277 0 51.2-22.923 51.2-51.2v-294.554c0-28.277-22.923-51.2-51.2-51.2h-193.485c-21.208 0-38.4 17.192-38.4 38.4z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Multiline"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":130,"id":8,"name":"Multiline","prevSize":32,"code":59765},"setIdx":2,"setId":2,"iconIdx":116},{"icon":{"paths":["M511.269 687.543h65.829c28.277 0 51.2 22.923 51.2 51.2v131.657c0 28.277-22.923 51.2-51.2 51.2h-131.657c-28.277 0-51.2-22.923-51.2-51.2v-274.286c0-14.138 11.462-25.6 25.6-25.6h91.429v117.029zM419.84 219.429c-14.138 0-25.6-11.462-25.6-25.6v-65.829c0-14.138 11.462-25.6 25.6-25.6h65.829c14.138 0 25.6 11.462 25.6 25.6v91.429h-91.429zM511.269 336.457v-117.029h91.429c14.138 0 25.6 11.462 25.6 25.6v65.829c0 14.138-11.462 25.6-25.6 25.6h-91.429zM419.84 453.486c-14.138 0-25.6-11.462-25.6-25.6v-65.829c0-14.138 11.462-25.6 25.6-25.6h91.429v117.029h-91.429zM511.269 570.514v-117.029h91.429c14.138 0 25.6 11.462 25.6 25.6v65.829c0 14.138-11.462 25.6-25.6 25.6h-91.429zM478.354 863.086h65.829c14.138 0 25.6-11.462 25.6-25.6v-65.829c0-14.138-11.462-25.6-25.6-25.6h-65.829c-14.138 0-25.6 11.462-25.6 25.6v65.829c0 14.138 11.462 25.6 25.6 25.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-zip"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":131,"id":7,"name":"Files-zip","prevSize":32,"code":59766},"setIdx":2,"setId":2,"iconIdx":117},{"icon":{"paths":["M762.371 415.703c26.243-8.909 53.615-5.079 74.762 10.967 21.24 16.133 33.267 42.304 33.267 71.286v191.066c0 28.942-12.040 55.077-33.272 71.247-13.603 10.316-29.891 15.673-47.002 15.673-9.315 0-18.676-1.565-27.794-4.666l-95.568-70.287v67.011c0 28.277-22.923 51.2-51.2 51.2h-410.764c-28.277 0-51.2-22.923-51.2-51.2v-348.937c0-28.277 22.923-51.2 51.2-51.2h410.764c28.277 0 51.2 22.923 51.2 51.2v69.273l95.607-72.634zM306.614 204.8h207.127c28.277 0 51.2 22.923 51.2 51.2v8.658c0 28.277-22.923 51.2-51.2 51.2h-207.127c-28.277 0-51.2-22.923-51.2-51.2v-8.658c0-28.277 22.923-51.2 51.2-51.2z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-video"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":132,"id":6,"name":"Files-video","prevSize":32,"code":59767},"setIdx":2,"setId":2,"iconIdx":118},{"icon":{"paths":["M870.138 150.751c-0.262-3.295-0.872-6.589-1.831-9.706-0.087-0.712-0.349-1.514-0.697-2.226 0.087-0.089 0.087-0.089 0-0.178-0.262-0.89-0.61-1.87-0.959-2.76-0.087-0.178-0.174-0.356-0.262-0.534-0.523-1.425-1.221-2.849-1.918-4.274-0.872-1.603-1.744-3.206-2.79-4.808-0.785-1.247-1.656-2.404-2.528-3.562-0.436-0.534-0.872-1.069-1.308-1.603-0.436-0.623-0.959-1.247-1.569-1.781-0.697-0.712-1.395-1.425-2.092-2.137-0.785-0.801-1.569-1.514-2.441-2.226-0.785-0.712-1.656-1.336-2.528-1.959-0.436-0.445-0.872-0.801-1.308-0.979-0.436-0.356-0.959-0.712-1.395-0.979 0 0-0.087-0.178-0.174-0.089-0.697-0.534-1.395-1.069-2.18-1.425-0.087-0.089-0.262-0.178-0.349-0.178-0.959-0.534-1.918-1.069-2.877-1.514-1.831-0.979-3.836-1.781-5.841-2.493-0.697-0.267-1.395-0.445-2.092-0.712-2.703-0.89-5.58-1.425-8.457-1.781-0.61-0.089-1.308-0.178-2.005-0.178-1.395-0.178-2.964-0.267-4.446-0.267-1.831 0-3.574 0.089-5.318 0.267-1.482 0.178-2.877 0.356-4.272 0.623-1.046 0.178-2.092 0.445-3.139 0.712l-400.421 102.489c-0.959 0.267-1.918 0.445-2.877 0.801-1.133 0.267-2.267 0.623-3.313 0.979-0.349 0.089-0.61 0.178-0.785 0.267-1.221 0.445-2.441 0.979-3.662 1.514-5.492 2.493-10.462 5.877-14.734 10.062-0.959 0.89-1.918 1.87-2.703 2.849-0.785 0.89-1.569 1.781-2.267 2.671-0.697 0.801-1.395 1.692-1.918 2.671-2.005 2.849-3.749 5.877-5.056 9.171-0.61 1.336-1.133 2.671-1.569 4.096-0.523 1.336-0.872 2.76-1.221 4.185-0.349 1.158-0.61 2.315-0.785 3.473-0.174 0.979-0.349 2.048-0.436 3.027-0.262 2.137-0.436 4.363-0.436 6.589v351.009c-16.39-6.055-34.088-9.261-52.483-9.261-86.658 0-157.1 72.036-157.1 160.545s70.442 160.456 157.1 160.456c84.827 0 153.961-69.009 156.838-155.025 0.174-1.781 0.262-3.562 0.262-5.432v-460.622l297.984-76.221v282.535c-16.39-6.055-34.088-9.261-52.483-9.261-86.658 0-157.1 72.036-157.1 160.456 0 88.509 70.442 160.545 157.1 160.545 84.844 0 153.961-69.098 156.829-155.051 0.183-1.808 0.27-3.633 0.27-5.494v-502.205c0-1.692-0.087-3.473-0.262-5.075z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Files-audio"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":133,"id":5,"name":"Files-audio","prevSize":32,"code":59768},"setIdx":2,"setId":2,"iconIdx":119},{"icon":{"paths":["M516.333 543.331c98.167 0 196.334 0 294.501 0 0.692 0 1.416 0 2.174 0v-0.001c16.019 0 29.005 12.986 29.005 29.005 0 0.81-0.034 1.619-0.102 2.426-16.192 192.934-179.943 346.664-376.779 346.664-207.415 0-378.093-170.699-378.093-378.093 0-193.91 149.176-355.712 338.083-375.96 19.856-2.128 37.677 12.243 39.805 32.098 0.137 1.28 0.206 2.566 0.206 3.854-0.001 0.858-0.001 1.681-0.001 2.47 0 95.446 0 190.891 0 286.337 0 28.277 22.923 51.2 51.2 51.2zM528.147 429.294c0-138.591 0-237.283 0-296.078 0-0.865 0-1.784 0-2.757h0.001c-0-14.921 12.096-27.017 27.017-27.017 0.694-0 1.388 0.027 2.080 0.080 89.608 6.919 174.258 45.618 238.256 109.597 62.121 62.139 100.425 143.748 108.934 230.474 1.813 18.478-11.697 34.927-30.175 36.74-1.091 0.107-2.186 0.161-3.283 0.161l-291.631-0.001c-28.277 0-51.2-22.923-51.2-51.199z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["File-keynote"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":134,"id":4,"name":"File-keynote","prevSize":32,"code":59769},"setIdx":2,"setId":2,"iconIdx":120},{"icon":{"paths":["M870.4 593.185h-251.079l-233.898-388.385h251.127l233.851 388.385zM369.040 242.949l125.516 218.472-215.393 374.724-125.563-218.329 215.44-374.867zM443.813 633.017h425.325l-125.563 237.383h-434.6l134.838-237.383z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["File-google-drive"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":135,"id":3,"name":"File-google-drive","prevSize":32,"code":59770},"setIdx":2,"setId":2,"iconIdx":121},{"icon":{"paths":["M962.166 471.314c14.179 25.106 14.179 56.265 0 81.371-90.173 159.672-258.39 266.514-450.166 266.514-191.696 0-359.95-106.767-450.166-266.513-14.179-25.107-14.179-56.266 0-81.373 90.173-159.672 258.39-266.514 450.166-266.514 191.696 0 359.951 106.767 450.166 266.514zM512 739.061c142.353 0 269.027-68.734 350.137-175.566 4.567-6.016 10.062-13.956 16.484-23.821 10.979-16.866 11.061-38.599 0.207-55.546-6.187-9.661-11.508-17.456-15.964-23.384-78.965-105.072-205.598-174.146-350.864-174.146-146.146 0-270.625 71.048-351.315 176.679-4.071 5.329-8.932 12.217-14.583 20.663-11.398 17.036-11.538 39.228-0.356 56.406l0.001-0c5.388 8.276 10.035 15.031 13.942 20.262 81.011 108.48 208.68 178.454 352.311 178.454zM512 665.6c-84.831 0-153.6-68.769-153.6-153.6s68.769-153.6 153.6-153.6c84.831 0 153.6 68.769 153.6 153.6s-68.769 153.6-153.6 153.6z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Eye"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":136,"id":2,"name":"Eye","prevSize":32,"code":59771},"setIdx":2,"setId":2,"iconIdx":122},{"icon":{"paths":["M668.989 710.098v0c13.020 12.835 13.17 33.795 0.335 46.815-0.111 0.112-0.222 0.224-0.335 0.335l-123.486 121.732c-19.934 19.651-51.954 19.651-71.888 0l-123.486-121.732c-13.020-12.835-13.17-33.795-0.335-46.815 0.111-0.112 0.222-0.224 0.335-0.335v0c13.263-13.074 34.566-13.074 47.829 0l75.713 74.637v-270.995c0-18.678 15.142-33.82 33.82-33.82v0c18.678 0 33.82 15.142 33.82 33.82v275.073l79.849-78.715c13.263-13.074 34.566-13.074 47.829 0zM767.936 278.65c87.194 14.313 153.664 89.026 153.664 179.043 0 100.249-82.439 181.517-184.132 181.517h-79.628c-18.413 0-33.34-14.927-33.34-33.34v0c0-18.413 14.927-33.34 33.34-33.34h79.628c64.337 0 116.492-51.414 116.492-114.837s-52.155-114.837-116.492-114.837h-33.82v-33.34c0-42.964-35.331-77.793-78.914-77.793-19.186 0-37.227 6.731-51.457 18.811l-26.834 22.78-21.85-27.473c-25.52-32.088-64.399-51.162-106.538-51.162-74.713 0-135.281 59.707-135.281 133.359 0 15.361 2.621 30.322 7.689 44.456l15.937 44.45h-57.446c-43.583 0-78.914 34.829-78.914 77.793s35.331 77.793 78.914 77.793h97.949c18.413 0 33.34 14.927 33.34 33.34v0c0 18.413-14.927 33.34-33.34 33.34h-97.949c-80.94 0-146.554-64.683-146.554-144.472 0-68.9 48.927-126.535 114.399-140.984-1.105-8.476-1.665-17.061-1.665-25.715 0-110.478 90.851-200.039 202.921-200.039 52.286 0 101.329 19.65 138.343 53.682 20.769-10.806 44.116-16.638 68.336-16.638 70.193 0 128.861 48.647 143.202 113.606z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Download"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":137,"id":1,"name":"Download","prevSize":32,"code":59652},"setIdx":2,"setId":2,"iconIdx":123},{"icon":{"paths":["M260.157 613.052c-12.627 8.1-29.429 4.431-37.53-8.196-2.808-4.377-4.3-9.467-4.3-14.667v-214.704c0-153.693 132.622-278.221 295.987-278.221 62.748 0 122.548 18.415 172.447 52.124 3.216 2.173 6.867 4.84 10.961 8.005 14.4 11.136 17.048 31.836 5.914 46.237-1.352 1.749-2.877 3.358-4.551 4.801-15.788 13.614-38.896 14.489-55.668 2.11-3.572-2.636-6.742-4.823-9.503-6.557-35.289-22.169-76.503-34.17-119.6-34.17-120.367 0-217.856 93.052-217.856 207.718v169.102c0 26.876-13.68 51.905-36.301 66.417zM812.546 744.975l-80.299-154.522v-128.551c0-16.386 7.137-31.96 19.548-42.659l16.174-13.942c10.733-9.252 26.933-8.051 36.185 2.682 4.015 4.658 6.223 10.602 6.223 16.752l-0.601 166.793 76.24 143.151c14.621 27.454 4.219 61.563-23.235 76.184-8.15 4.34-17.241 6.61-26.474 6.61h-175.36c-15.767 63.489-76.199 109.261-146.608 109.261-70.367 0-130.792-45.779-146.557-109.261h-30.326c-12.611 0-22.835-10.223-22.835-22.835 0-6.642 2.892-12.955 7.922-17.292l21.686-18.702c10.226-8.819 23.279-13.67 36.782-13.67h431.534zM122.16 849.45c-15.019-18.454-12.949-45.44 4.71-61.387l711.173-642.24c16.736-15.114 42.556-13.799 57.67 2.937 0.696 0.784 0.696 0.784 1.365 1.592 15.019 18.454 12.949 45.44-4.71 61.387l-711.173 642.24c-16.736 15.114-42.556 13.799-57.67-2.937-0.696-0.784-0.696-0.784-1.365-1.592zM512 854.645c23.566 0 44.551-11.762 55.954-29.929h-111.909c11.403 18.167 32.388 29.929 55.954 29.929z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Bell-off"],"colorPermutations":{"15816216812032062091":[{}]}},"attrs":[{}],"properties":{"order":138,"id":0,"name":"Bell-off","prevSize":32,"code":59772},"setIdx":2,"setId":2,"iconIdx":124}],"height":1024,"metadata":{"name":"custom"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"custom","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"noie8":true,"ie7":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16}} \ No newline at end of file diff --git a/app/notifications/inApp/index.js b/app/notifications/inApp/index.js index 4062c1fc4..3862846a0 100644 --- a/app/notifications/inApp/index.js +++ b/app/notifications/inApp/index.js @@ -8,7 +8,7 @@ import equal from 'deep-equal'; import { responsive } from 'react-native-responsive-ui'; import Touchable from 'react-native-platform-touchable'; -import { isNotch, isIOS, isTablet } from '../../utils/deviceInfo'; +import { hasNotch, isIOS, isTablet } from '../../utils/deviceInfo'; import { CustomIcon } from '../../lib/Icons'; import { themes } from '../../constants/colors'; import Avatar from '../../containers/Avatar'; @@ -188,7 +188,7 @@ class NotificationBadge extends React.Component { if (isIOS) { const portrait = window.height > window.width; if (portrait) { - top = isNotch ? 45 : 20; + top = hasNotch ? 45 : 20; } else { top = isTablet ? 20 : 0; } diff --git a/app/presentation/RoomItem/Touchable.js b/app/presentation/RoomItem/Touchable.js index 5107a21da..d4a88c34f 100644 --- a/app/presentation/RoomItem/Touchable.js +++ b/app/presentation/RoomItem/Touchable.js @@ -55,7 +55,7 @@ class Touchable extends React.Component { _handleRelease = (nativeEvent) => { const { translationX } = nativeEvent; const { rowState } = this.state; - this._value = this._value + translationX; + this._value += translationX; let toValue = 0; if (rowState === 0) { // if no option is opened diff --git a/app/presentation/RoomItem/TypeIcon.js b/app/presentation/RoomItem/TypeIcon.js index 19537c07e..d42e31a26 100644 --- a/app/presentation/RoomItem/TypeIcon.js +++ b/app/presentation/RoomItem/TypeIcon.js @@ -11,7 +11,7 @@ const TypeIcon = React.memo(({ if (type === 'd' && !isGroupChat) { return <Status style={styles.status} size={10} status={status} />; } - return <RoomTypeIcon theme={theme} type={prid ? 'discussion' : type} isGroupChat={isGroupChat} />; + return <RoomTypeIcon theme={theme} type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} />; }); TypeIcon.propTypes = { diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js index dc39fe303..9251717cb 100644 --- a/app/presentation/RoomItem/index.js +++ b/app/presentation/RoomItem/index.js @@ -209,12 +209,20 @@ RoomItem.defaultProps = { getUserPresence: () => {} }; -const mapStateToProps = (state, ownProps) => ({ - connected: state.meteor.connected, - status: - state.meteor.connected && ownProps.type === 'd' - ? state.activeUsers[ownProps.id] && state.activeUsers[ownProps.id].status - : 'offline' -}); +const mapStateToProps = (state, ownProps) => { + let status = 'offline'; + const { id, type, visitor = {} } = ownProps; + if (state.meteor.connected) { + if (type === 'd') { + status = state.activeUsers[id]?.status || 'offline'; + } else if (type === 'l' && visitor?.status) { + ({ status } = visitor); + } + } + return { + connected: state.meteor.connected, + status + }; +}; export default connect(mapStateToProps)(RoomItem); diff --git a/app/presentation/UserItem.js b/app/presentation/UserItem.js index de8e0ef73..1f536af06 100644 --- a/app/presentation/UserItem.js +++ b/app/presentation/UserItem.js @@ -1,13 +1,13 @@ import React from 'react'; import { Text, View, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; -import { LongPressGestureHandler, State } from 'react-native-gesture-handler'; import Avatar from '../containers/Avatar'; import { CustomIcon } from '../lib/Icons'; import sharedStyles from '../views/Styles'; import { themes } from '../constants/colors'; import Touch from '../utils/touch'; +import LongPress from '../utils/longPress'; const styles = StyleSheet.create({ button: { @@ -23,7 +23,8 @@ const styles = StyleSheet.create({ textContainer: { flex: 1, flexDirection: 'column', - justifyContent: 'center' + justifyContent: 'center', + marginRight: 15 }, name: { fontSize: 17, @@ -41,38 +42,25 @@ const styles = StyleSheet.create({ const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, baseUrl, user, theme -}) => { - const longPress = ({ nativeEvent }) => { - if (nativeEvent.state === State.ACTIVE) { - if (onLongPress) { - onLongPress(); - } - } - }; - - return ( - <LongPressGestureHandler - onHandlerStateChange={longPress} - minDurationMs={800} +}) => ( + <LongPress onLongPress={onLongPress}> + <Touch + onPress={onPress} + style={{ backgroundColor: themes[theme].backgroundColor }} + testID={testID} + theme={theme} > - <Touch - onPress={onPress} - style={{ backgroundColor: themes[theme].backgroundColor }} - testID={testID} - theme={theme} - > - <View style={[styles.container, styles.button, style]}> - <Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} /> - <View style={styles.textContainer}> - <Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text> - <Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text> - </View> - {icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null} + <View style={[styles.container, styles.button, style]}> + <Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} /> + <View style={styles.textContainer}> + <Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text> + <Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text> </View> - </Touch> - </LongPressGestureHandler> - ); -}; + {icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null} + </View> + </Touch> + </LongPress> +); UserItem.propTypes = { name: PropTypes.string.isRequired, diff --git a/app/reducers/app.js b/app/reducers/app.js index 7955bc7c5..1052d5a46 100644 --- a/app/reducers/app.js +++ b/app/reducers/app.js @@ -1,36 +1,26 @@ -import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate'; -import { APP } from '../actions/actionsTypes'; +import { APP, APP_STATE } from '../actions/actionsTypes'; const initialState = { root: null, ready: false, - inactive: false, + foreground: true, background: false }; export default function app(state = initialState, action) { switch (action.type) { - case FOREGROUND: + case APP_STATE.FOREGROUND: return { ...state, - inactive: false, foreground: true, background: false }; - case BACKGROUND: + case APP_STATE.BACKGROUND: return { ...state, - inactive: false, foreground: false, background: true }; - case INACTIVE: - return { - ...state, - inactive: true, - foreground: false, - background: false - }; case APP.START: return { ...state, diff --git a/app/reducers/room.js b/app/reducers/room.js index 6a34aa8d5..2b5cfcb8a 100644 --- a/app/reducers/room.js +++ b/app/reducers/room.js @@ -2,11 +2,23 @@ import { ROOM } from '../actions/actionsTypes'; const initialState = { rid: null, - isDeleting: false + isDeleting: false, + rooms: [] }; export default function(state = initialState, action) { switch (action.type) { + case ROOM.SUBSCRIBE: + return { + ...state, + rooms: [action.rid, ...state.rooms] + }; + case ROOM.UNSUBSCRIBE: + return { + ...state, + rooms: state.rooms + .filter(room => room.rid === action.rid) + }; case ROOM.LEAVE: return { ...state, @@ -19,6 +31,18 @@ export default function(state = initialState, action) { rid: action.rid, isDeleting: true }; + case ROOM.CLOSE: + return { + ...state, + rid: action.rid, + isDeleting: true + }; + case ROOM.FORWARD: + return { + ...state, + rid: action.rid, + isDeleting: true + }; case ROOM.REMOVED: return { ...state, diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 86aa2df69..d5a2b87cc 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -11,6 +11,7 @@ import database from '../lib/database'; import RocketChat from '../lib/rocketchat'; import EventEmitter from '../utils/events'; import { appStart } from '../actions'; +import { localAuthenticate } from '../utils/localAuthentication'; const roomTypes = { channel: 'c', direct: 'd', group: 'p', channels: 'l' @@ -72,8 +73,9 @@ const handleOpen = function* handleOpen({ params }) { if (server === host && user) { const connected = yield select(state => state.server.connected); if (!connected) { + yield localAuthenticate(host); yield put(selectServerRequest(host)); - yield take(types.SERVER.SELECT_SUCCESS); + yield take(types.LOGIN.SUCCESS); } yield navigate({ params }); } else { @@ -83,6 +85,7 @@ const handleOpen = function* handleOpen({ params }) { try { const servers = yield serversCollection.find(host); if (servers && user) { + yield localAuthenticate(host); yield put(selectServerRequest(host)); yield take(types.LOGIN.SUCCESS); yield navigate({ params }); @@ -103,6 +106,8 @@ const handleOpen = function* handleOpen({ params }) { if (params.token) { yield take(types.SERVER.SELECT_SUCCESS); yield RocketChat.connect({ server: host, user: { token: params.token } }); + yield take(types.LOGIN.SUCCESS); + yield navigate({ params }); } else { yield handleInviteLink({ params, requireLogin: true }); } diff --git a/app/sagas/init.js b/app/sagas/init.js index d7993e42a..c956d9bb5 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -1,8 +1,8 @@ -import { AsyncStorage } from 'react-native'; import { put, takeLatest, all } from 'redux-saga/effects'; import RNUserDefaults from 'rn-user-defaults'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import RNBootSplash from 'react-native-bootsplash'; +import AsyncStorage from '@react-native-community/async-storage'; import * as actions from '../actions'; import { selectServerRequest, serverRequest } from '../actions/server'; @@ -19,6 +19,7 @@ import { isIOS } from '../utils/deviceInfo'; import database from '../lib/database'; import protectedFunction from '../lib/methods/helpers/protectedFunction'; import appConfig from '../../app.json'; +import { localAuthenticate } from '../utils/localAuthentication'; export const initLocalSettings = function* initLocalSettings() { const sortPreferences = yield RocketChat.getSortPreferences(); @@ -100,6 +101,8 @@ const restore = function* restore() { } else { const serversDB = database.servers; const serverCollections = serversDB.collections.get('servers'); + + yield localAuthenticate(server); const serverObj = yield serverCollections.find(server); yield put(selectServerRequest(server, serverObj && serverObj.version)); } diff --git a/app/sagas/login.js b/app/sagas/login.js index 2670a5c07..772e50052 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -22,6 +22,7 @@ import EventEmitter from '../utils/events'; import { inviteLinksRequest } from '../actions/inviteLinks'; import { showErrorAlert } from '../utils/info'; import appConfig from '../../app.json'; +import { localAuthenticate } from '../utils/localAuthentication'; import { setActiveUsers } from '../actions/activeUsers'; const getServer = state => state.server.server; @@ -42,6 +43,8 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE yield put(setUser(result)); yield put(appStart('setUsername')); } else { + const server = yield select(getServer); + yield localAuthenticate(server); yield put(loginSuccess(result)); } } catch (e) { diff --git a/app/sagas/messages.js b/app/sagas/messages.js index 75ba17069..1b815fd5f 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -27,10 +27,12 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) { rid: subscriptions[0].rid, name: username, fname: name, message }); } else { - const room = yield RocketChat.createDirectMessage(username); - yield goRoom({ - rid: room.rid, name: username, fname: name, message - }); + const result = yield RocketChat.createDirectMessage(username); + if (result?.success) { + yield goRoom({ + rid: result?.room.rid, t: 'd', name: username, fname: name, message + }); + } } } catch (e) { log(e); diff --git a/app/sagas/room.js b/app/sagas/room.js index a5cd48b46..9fab31386 100644 --- a/app/sagas/room.js +++ b/app/sagas/room.js @@ -1,4 +1,5 @@ import { Alert } from 'react-native'; +import prompt from 'react-native-prompt-android'; import { takeLatest, take, select, delay, race, put } from 'redux-saga/effects'; @@ -9,6 +10,7 @@ import { removedRoom } from '../actions/room'; import RocketChat from '../lib/rocketchat'; import log from '../utils/log'; import I18n from '../i18n'; +import { showErrorAlert } from '../utils/info'; const watchUserTyping = function* watchUserTyping({ rid, status }) { const auth = yield select(state => state.login.isAuthenticated); @@ -28,10 +30,8 @@ const watchUserTyping = function* watchUserTyping({ rid, status }) { } }; -const handleRemovedRoom = function* handleLeaveRoom({ result }) { - if (result.success) { - yield Navigation.navigate('RoomsListView'); - } +const handleRemovedRoom = function* handleRemovedRoom() { + yield Navigation.navigate('RoomsListView'); // types.ROOM.REMOVE is triggered by `subscriptions-changed` with `removed` arg const { timeout } = yield race({ deleteFinished: take(types.ROOM.REMOVED), @@ -45,7 +45,9 @@ const handleRemovedRoom = function* handleLeaveRoom({ result }) { const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) { try { const result = yield RocketChat.leaveRoom(rid, t); - yield handleRemovedRoom({ result }); + if (result.success) { + yield handleRemovedRoom(); + } } catch (e) { if (e.data && e.data.errorType === 'error-you-are-last-owner') { Alert.alert(I18n.t('Oops'), I18n.t(e.data.errorType)); @@ -58,15 +60,65 @@ const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) { const handleDeleteRoom = function* handleDeleteRoom({ rid, t }) { try { const result = yield RocketChat.deleteRoom(rid, t); - yield handleRemovedRoom({ result }); + if (result.success) { + yield handleRemovedRoom(); + } } catch (e) { Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('deleting_room') })); } }; +const handleCloseRoom = function* handleCloseRoom({ rid }) { + const requestComment = yield select(state => state.settings.Livechat_request_comment_when_closing_conversation); + + const closeRoom = async(comment = '') => { + try { + await RocketChat.closeLivechat(rid, comment); + Navigation.navigate('RoomsListView'); + } catch { + // do nothing + } + }; + + if (!requestComment) { + const comment = I18n.t('Chat_closed_by_agent'); + return closeRoom(comment); + } + + prompt( + I18n.t('Closing_chat'), + I18n.t('Please_add_a_comment'), + [ + { text: I18n.t('Cancel'), onPress: () => { }, style: 'cancel' }, + { + text: I18n.t('Submit'), + onPress: comment => closeRoom(comment) + } + ], + { + cancelable: true + } + ); +}; + +const handleForwardRoom = function* handleForwardRoom({ transferData }) { + try { + const result = yield RocketChat.forwardLivechat(transferData); + if (result === true) { + Navigation.navigate('RoomsListView'); + } else { + showErrorAlert(I18n.t('No_available_agents_to_transfer'), I18n.t('Oops')); + } + } catch (e) { + showErrorAlert(e.reason, I18n.t('Oops')); + } +}; + const root = function* root() { yield takeLatest(types.ROOM.USER_TYPING, watchUserTyping); yield takeLatest(types.ROOM.LEAVE, handleLeaveRoom); yield takeLatest(types.ROOM.DELETE, handleDeleteRoom); + yield takeLatest(types.ROOM.CLOSE, handleCloseRoom); + yield takeLatest(types.ROOM.FORWARD, handleForwardRoom); }; export default root; diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 1b5940d1a..7204a33a9 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -1,10 +1,9 @@ import { put, select, race, take, fork, cancel, delay } from 'redux-saga/effects'; -import { BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate'; import { Q } from '@nozbe/watermelondb'; - import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; + import * as types from '../actions/actionsTypes'; import { roomsSuccess, roomsFailure, roomsRefresh } from '../actions/rooms'; import database from '../lib/database'; @@ -47,12 +46,12 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { const subCollection = db.collections.get('subscriptions'); const messagesCollection = db.collections.get('messages'); - if (subscriptions.length) { - const subsIds = subscriptions.map(sub => sub.rid); + const subsIds = subscriptions.map(sub => sub.rid).concat(roomsResult.remove.map(room => room._id)); + if (subsIds.length) { const existingSubs = yield subCollection.query(Q.where('id', Q.oneOf(subsIds))).fetch(); const subsToUpdate = existingSubs.filter(i1 => subscriptions.find(i2 => i1._id === i2._id)); const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id)); - // TODO: subsToDelete? + const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id)); const lastMessages = subscriptions .map(sub => sub.lastMessage && buildMessage(sub.lastMessage)) @@ -70,9 +69,15 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { ...subsToUpdate.map((subscription) => { const newSub = subscriptions.find(s => s._id === subscription._id); return subscription.prepareUpdate(() => { + if (newSub.announcement) { + if (newSub.announcement !== subscription.announcement) { + subscription.bannerClosed = false; + } + } Object.assign(subscription, newSub); }); }), + ...subsToDelete.map(subscription => subscription.prepareDestroyPermanently()), ...messagesToCreate.map(message => messagesCollection.prepareCreate(protectedFunction((m) => { m._raw = sanitizedRaw({ id: message._id }, messagesCollection.schema); m.subscription.id = message.rid; @@ -109,8 +114,7 @@ const root = function* root() { roomsSuccess: take(types.ROOMS.SUCCESS), roomsFailure: take(types.ROOMS.FAILURE), serverReq: take(types.SERVER.SELECT_REQUEST), - background: take(BACKGROUND), - inactive: take(INACTIVE), + background: take(types.APP_STATE.BACKGROUND), logout: take(types.LOGOUT), timeout: delay(30000) }); diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index ed26c4541..7596e05d1 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -1,6 +1,4 @@ -import { - put, take, takeLatest, fork, cancel, race -} from 'redux-saga/effects'; +import { put, takeLatest } from 'redux-saga/effects'; import { Alert } from 'react-native'; import RNUserDefaults from 'rn-user-defaults'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; @@ -98,6 +96,9 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`); setBasicAuth(basicAuth); + // Check for running requests and abort them before connecting to the server + RocketChat.abort(); + if (user) { yield put(clearSettings()); yield RocketChat.connect({ server, user, logoutOnError: true }); @@ -152,16 +153,6 @@ const handleServerRequest = function* handleServerRequest({ server, certificate const root = function* root() { yield takeLatest(SERVER.REQUEST, handleServerRequest); - - while (true) { - const params = yield take(SERVER.SELECT_REQUEST); - const selectServerTask = yield fork(handleSelectServer, params); - yield race({ - request: take(SERVER.SELECT_REQUEST), - success: take(SERVER.SELECT_SUCCESS), - failure: take(SERVER.SELECT_FAILURE) - }); - yield cancel(selectServerTask); - } + yield takeLatest(SERVER.SELECT_REQUEST, handleSelectServer); }; export default root; diff --git a/app/sagas/state.js b/app/sagas/state.js index 6e3d33728..3c4d63e32 100644 --- a/app/sagas/state.js +++ b/app/sagas/state.js @@ -1,9 +1,10 @@ import { takeLatest, select } from 'redux-saga/effects'; -import { FOREGROUND, BACKGROUND } from 'redux-enhancer-react-native-appstate'; import RocketChat from '../lib/rocketchat'; import { setBadgeCount } from '../notifications/push'; import log from '../utils/log'; +import { localAuthenticate, saveLastLocalAuthenticationSession } from '../utils/localAuthentication'; +import { APP_STATE } from '../actions/actionsTypes'; const appHasComeBackToForeground = function* appHasComeBackToForeground() { const appRoot = yield select(state => state.app.root); @@ -15,6 +16,8 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() { return; } try { + const server = yield select(state => state.server.server); + yield localAuthenticate(server); setBadgeCount(); return yield RocketChat.setUserPresenceOnline(); } catch (e) { @@ -32,25 +35,18 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() { return; } try { - return yield RocketChat.setUserPresenceAway(); + const server = yield select(state => state.server.server); + yield saveLastLocalAuthenticationSession(server); + + yield RocketChat.setUserPresenceAway(); } catch (e) { log(e); } }; const root = function* root() { - yield takeLatest( - FOREGROUND, - appHasComeBackToForeground - ); - yield takeLatest( - BACKGROUND, - appHasComeBackToBackground - ); - // yield takeLatest( - // INACTIVE, - // appHasComeBackToBackground - // ); + yield takeLatest(APP_STATE.FOREGROUND, appHasComeBackToForeground); + yield takeLatest(APP_STATE.BACKGROUND, appHasComeBackToBackground); }; export default root; diff --git a/app/share.js b/app/share.js index 5ab50599a..35e865296 100644 --- a/app/share.js +++ b/app/share.js @@ -15,10 +15,12 @@ import { import Navigation from './lib/ShareNavigation'; import store from './lib/createStore'; import sharedStyles from './views/Styles'; -import { isNotch, supportSystemTheme } from './utils/deviceInfo'; +import { hasNotch, supportSystemTheme } from './utils/deviceInfo'; import { defaultHeader, onNavigationStateChange, cardStyle } from './utils/navigation'; import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import { ThemeContext } from './theme'; +import { localAuthenticate } from './utils/localAuthentication'; +import ScreenLockedView from './views/ScreenLockedView'; const InsideNavigator = createStackNavigator({ ShareListView: { @@ -82,6 +84,7 @@ class Root extends React.Component { const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY); if (currentServer && token) { + await localAuthenticate(currentServer); await Navigation.navigate('InsideStack'); await RocketChat.shareExtensionInit(currentServer); } else { @@ -108,7 +111,7 @@ class Root extends React.Component { return ( <AppearanceProvider> <View - style={[sharedStyles.container, isLandscape && isNotch ? sharedStyles.notchLandscapeContainer : {}]} + style={[sharedStyles.container, isLandscape && hasNotch ? sharedStyles.notchLandscapeContainer : {}]} onLayout={this.handleLayout} > <Provider store={store}> @@ -120,6 +123,7 @@ class Root extends React.Component { onNavigationStateChange={onNavigationStateChange} screenProps={{ theme }} /> + <ScreenLockedView /> </ThemeContext.Provider> </Provider> </View> diff --git a/app/utils/deviceInfo.js b/app/utils/deviceInfo.js index cc9236ab1..a7ba16794 100644 --- a/app/utils/deviceInfo.js +++ b/app/utils/deviceInfo.js @@ -3,9 +3,7 @@ import DeviceInfo from 'react-native-device-info'; import { MIN_WIDTH_SPLIT_LAYOUT } from '../constants/tablet'; -const NOTCH_DEVICES = ['iPhone X', 'iPhone XS', 'iPhone XS Max', 'iPhone XR']; - -export const isNotch = NOTCH_DEVICES.includes(DeviceInfo.getModel()); +export const hasNotch = DeviceInfo.hasNotch(); export const isIOS = Platform.OS === 'ios'; export const isAndroid = !isIOS; export const getReadableVersion = DeviceInfo.getReadableVersion(); diff --git a/app/utils/fetch.js b/app/utils/fetch.js index 7b0fae135..a804e0a12 100644 --- a/app/utils/fetch.js +++ b/app/utils/fetch.js @@ -1,6 +1,7 @@ import { Platform } from 'react-native'; import DeviceInfo from 'react-native-device-info'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; +import RocketChat from '../lib/rocketchat'; // this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js" export const headers = { @@ -25,5 +26,9 @@ export default (url, options = {}) => { if (options && options.headers) { customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } }; } + if (RocketChat.controller) { + const { signal } = RocketChat.controller; + customOptions = { ...customOptions, signal }; + } return fetch(url, customOptions); }; diff --git a/app/utils/goRoom.js b/app/utils/goRoom.js new file mode 100644 index 000000000..70a4fe013 --- /dev/null +++ b/app/utils/goRoom.js @@ -0,0 +1,39 @@ +import Navigation from '../lib/Navigation'; +import RocketChat from '../lib/rocketchat'; + +const navigate = (item) => { + Navigation.navigate('RoomView', { + rid: item.rid, + name: RocketChat.getRoomTitle(item), + t: item.t, + prid: item.prid, + room: item, + search: item.search, + visitor: item.visitor, + roomUserId: RocketChat.getUidDirectMessage(item) + }); +}; + +export const goRoom = async(item = {}) => { + if (!item.search) { + return navigate(item); + } + if (item.t === 'd') { + // if user is using the search we need first to join/create room + try { + const { username } = item; + const result = await RocketChat.createDirectMessage(username); + if (result.success) { + return navigate({ + rid: result.room._id, + name: username, + t: 'd' + }); + } + } catch { + // Do nothing + } + } else { + return navigate(item); + } +}; diff --git a/app/utils/localAuthentication.js b/app/utils/localAuthentication.js new file mode 100644 index 000000000..52a0fc4a8 --- /dev/null +++ b/app/utils/localAuthentication.js @@ -0,0 +1,154 @@ +import * as LocalAuthentication from 'expo-local-authentication'; +import moment from 'moment'; +import RNBootSplash from 'react-native-bootsplash'; +import AsyncStorage from '@react-native-community/async-storage'; +import RNUserDefaults from 'rn-user-defaults'; +import { sha256 } from 'js-sha256'; + +import database from '../lib/database'; +import { isIOS } from './deviceInfo'; +import EventEmitter from './events'; +import { + LOCAL_AUTHENTICATE_EMITTER, LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY, PASSCODE_KEY, CHANGE_PASSCODE_EMITTER +} from '../constants/localAuthentication'; +import I18n from '../i18n'; + +export const saveLastLocalAuthenticationSession = async(server, serverRecord) => { + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + await serversDB.action(async() => { + try { + if (!serverRecord) { + serverRecord = await serversCollection.find(server); + } + await serverRecord.update((record) => { + record.lastLocalAuthenticatedSession = new Date(); + }); + } catch (e) { + // Do nothing + } + }); +}; + +export const resetAttempts = () => AsyncStorage.multiRemove([LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY]); + +const openModal = hasBiometry => new Promise((resolve) => { + EventEmitter.emit(LOCAL_AUTHENTICATE_EMITTER, { + submit: () => resolve(), + hasBiometry + }); +}); + +const openChangePasscodeModal = ({ force }) => new Promise((resolve, reject) => { + EventEmitter.emit(CHANGE_PASSCODE_EMITTER, { + submit: passcode => resolve(passcode), + cancel: () => reject(), + force + }); +}); + +export const changePasscode = async({ force = false }) => { + const passcode = await openChangePasscodeModal({ force }); + await RNUserDefaults.set(PASSCODE_KEY, sha256(passcode)); +}; + +export const biometryAuth = force => LocalAuthentication.authenticateAsync({ + disableDeviceFallback: true, + cancelLabel: force ? I18n.t('Dont_activate') : I18n.t('Local_authentication_biometry_fallback'), + promptMessage: I18n.t('Local_authentication_biometry_title') +}); + +/* + * It'll help us to get the permission to use FaceID + * and enable/disable the biometry when user put their first passcode +*/ +const checkBiometry = async(serverRecord) => { + const serversDB = database.servers; + + const result = await biometryAuth(true); + await serversDB.action(async() => { + try { + await serverRecord.update((record) => { + record.biometry = !!result?.success; + }); + } catch { + // Do nothing + } + }); +}; + +export const checkHasPasscode = async({ force = true, serverRecord }) => { + const storedPasscode = await RNUserDefaults.get(PASSCODE_KEY); + if (!storedPasscode) { + await changePasscode({ force }); + await checkBiometry(serverRecord); + return Promise.resolve({ newPasscode: true }); + } + return Promise.resolve(); +}; + +export const localAuthenticate = async(server) => { + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + + let serverRecord; + try { + serverRecord = await serversCollection.find(server); + } catch (error) { + return Promise.reject(); + } + + // if screen lock is enabled + if (serverRecord?.autoLock) { + // Make sure splash screen has been hidden + RNBootSplash.hide(); + + // Check if the app has passcode + const result = await checkHasPasscode({ serverRecord }); + + // `checkHasPasscode` results newPasscode = true if a passcode has been set + if (!result?.newPasscode) { + // diff to last authenticated session + const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); + + // if last authenticated session is older than configured auto lock time, authentication is required + if (diffToLastSession >= serverRecord?.autoLockTime) { + let hasBiometry = false; + + // if biometry is enabled on the app + if (serverRecord.biometry) { + const isEnrolled = await LocalAuthentication.isEnrolledAsync(); + hasBiometry = isEnrolled; + } + + // Authenticate + await openModal(hasBiometry); + } + } + + await resetAttempts(); + await saveLastLocalAuthenticationSession(server, serverRecord); + } +}; + +export const supportedBiometryLabel = async() => { + try { + const enrolled = await LocalAuthentication.isEnrolledAsync(); + + if (!enrolled) { + return null; + } + + const supported = await LocalAuthentication.supportedAuthenticationTypesAsync(); + + if (supported.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION)) { + return isIOS ? 'FaceID' : I18n.t('Local_authentication_facial_recognition'); + } + if (supported.includes(LocalAuthentication.AuthenticationType.FINGERPRINT)) { + return isIOS ? 'TouchID' : I18n.t('Local_authentication_fingerprint'); + } + } catch { + // Do nothing + } + return null; +}; diff --git a/app/utils/log.js b/app/utils/log.js index b4605bd64..47b2ddc4f 100644 --- a/app/utils/log.js +++ b/app/utils/log.js @@ -17,7 +17,7 @@ export const logServerVersion = (serverVersion) => { }; export default (e) => { - if (e instanceof Error && !__DEV__) { + if (e instanceof Error && e.message !== 'Aborted' && !__DEV__) { bugsnag.notify(e, (report) => { report.metadata = { details: { diff --git a/app/utils/longPress.js b/app/utils/longPress.js new file mode 100644 index 000000000..e491efcb9 --- /dev/null +++ b/app/utils/longPress.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { State, LongPressGestureHandler } from 'react-native-gesture-handler'; + +class LongPress extends React.Component { + setNativeProps(props) { + this.ref.setNativeProps(props); + } + + getRef = (ref) => { + this.ref = ref; + }; + + longPress = ({ nativeEvent }) => { + const { onLongPress } = this.props; + if (nativeEvent.state === State.ACTIVE) { + if (onLongPress) { + onLongPress(); + } + } + }; + + render() { + const { children, ...props } = this.props; + + return ( + <LongPressGestureHandler + onHandlerStateChange={this.longPress} + minDurationMs={800} + ref={this.getRef} + {...props} + > + {children} + </LongPressGestureHandler> + ); + } +} + +LongPress.propTypes = { + children: PropTypes.node, + onLongPress: PropTypes.func +}; + +export default LongPress; diff --git a/app/utils/review.js b/app/utils/review.js index 7e6da9d13..1c38f67c8 100644 --- a/app/utils/review.js +++ b/app/utils/review.js @@ -1,4 +1,5 @@ -import { Alert, Linking, AsyncStorage } from 'react-native'; +import { Alert, Linking } from 'react-native'; +import AsyncStorage from '@react-native-community/async-storage'; import { isIOS } from './deviceInfo'; import I18n from '../i18n'; diff --git a/app/utils/theme.js b/app/utils/theme.js index 24405d34a..3c265eb5a 100644 --- a/app/utils/theme.js +++ b/app/utils/theme.js @@ -35,11 +35,15 @@ export const newThemeState = (prevState, newTheme) => { return { themePreferences, theme: getTheme(themePreferences) }; }; -export const setNativeTheme = (themePreferences) => { +export const setNativeTheme = async(themePreferences) => { const theme = getTheme(themePreferences); if (isAndroid) { const iconsLight = theme === 'light'; - changeNavigationBarColor(themes[theme].navbarBackground, iconsLight); + try { + await changeNavigationBarColor(themes[theme].navbarBackground, iconsLight); + } catch (error) { + // Do nothing + } } setRootViewColor(themes[theme].backgroundColor); }; diff --git a/app/utils/url.js b/app/utils/url.js index 856eac771..16506ed13 100644 --- a/app/utils/url.js +++ b/app/utils/url.js @@ -7,3 +7,6 @@ export const isValidURL = (url) => { + '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator return !!pattern.test(url); }; + +// Use useSsl: false only if server url starts with http:// +export const useSsl = url => !/http:\/\//.test(url); diff --git a/app/views/AttachmentView.js b/app/views/AttachmentView.js index 51646896e..9f23833e3 100644 --- a/app/views/AttachmentView.js +++ b/app/views/AttachmentView.js @@ -33,11 +33,11 @@ class AttachmentView extends React.Component { const attachment = navigation.getParam('attachment'); const from = navigation.getParam('from'); const handleSave = navigation.getParam('handleSave', () => {}); - const { title, video_url } = attachment; + const { title } = attachment; const options = { - title, + title: decodeURI(title), ...themedHeader(theme), - headerRight: !video_url ? <SaveButton testID='save-image' onPress={handleSave} /> : null + headerRight: <SaveButton testID='save-image' onPress={handleSave} /> }; if (from !== 'MessagesView') { options.gesturesEnabled = false; @@ -84,8 +84,11 @@ class AttachmentView extends React.Component { handleSave = async() => { const { attachment } = this.state; const { user, baseUrl } = this.props; - const { image_url, image_type } = attachment; - const img = formatAttachmentUrl(image_url, user.id, user.token, baseUrl); + const { + image_url, image_type, video_url, video_type + } = attachment; + const url = image_url || video_url; + const mediaAttachment = formatAttachmentUrl(url, user.id, user.token, baseUrl); if (isAndroid) { const rationale = { @@ -100,13 +103,13 @@ class AttachmentView extends React.Component { this.setState({ loading: true }); try { - const extension = `.${ mime.extension(image_type) || 'jpg' }`; - const file = `${ FileSystem.documentDirectory + SHA256(image_url) + extension }`; - const { uri } = await FileSystem.downloadAsync(img, file); + const extension = image_url ? `.${ mime.extension(image_type) || 'jpg' }` : `.${ mime.extension(video_type) || 'mp4' }`; + const file = `${ FileSystem.documentDirectory + SHA256(url) + extension }`; + const { uri } = await FileSystem.downloadAsync(mediaAttachment, file); await CameraRoll.save(uri, { album: 'Rocket.Chat' }); EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') }); } catch (e) { - EventEmitter.emit(LISTENER, { message: I18n.t('error-save-image') }); + EventEmitter.emit(LISTENER, { message: I18n.t(image_url ? 'error-save-image' : 'error-save-video') }); } this.setState({ loading: false }); }; diff --git a/app/views/ChangePasscodeView.js b/app/views/ChangePasscodeView.js new file mode 100644 index 000000000..967273824 --- /dev/null +++ b/app/views/ChangePasscodeView.js @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import PropTypes from 'prop-types'; +import Orientation from 'react-native-orientation-locker'; +import useDeepCompareEffect from 'use-deep-compare-effect'; +import _ from 'lodash'; +import Modal from 'react-native-modal'; +import Touchable from 'react-native-platform-touchable'; + +import { withTheme } from '../theme'; +import { isTablet, hasNotch } from '../utils/deviceInfo'; +import { TYPE } from '../containers/Passcode/constants'; +import { PasscodeChoose } from '../containers/Passcode'; +import EventEmitter from '../utils/events'; +import { CustomIcon } from '../lib/Icons'; +import { CHANGE_PASSCODE_EMITTER } from '../constants/localAuthentication'; +import { themes } from '../constants/colors'; + +const styles = StyleSheet.create({ + modal: { + margin: 0 + }, + close: { + position: 'absolute', + top: hasNotch ? 50 : 30, + left: 15 + } +}); + +const ChangePasscodeView = React.memo(({ theme }) => { + const [visible, setVisible] = useState(false); + const [data, setData] = useState({}); + + useDeepCompareEffect(() => { + if (!_.isEmpty(data)) { + setVisible(true); + } else { + setVisible(false); + } + }, [data]); + + const showChangePasscode = (args) => { + setData(args); + }; + + const onSubmit = (passcode) => { + const { submit } = data; + if (submit) { + submit(passcode); + } + setData({}); + }; + + const onCancel = () => { + const { cancel } = data; + if (cancel) { + cancel(); + } + setData({}); + }; + + useEffect(() => { + if (!isTablet) { + Orientation.lockToPortrait(); + } + EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode); + return (() => { + if (!isTablet) { + Orientation.unlockAllOrientations(); + } + EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER); + }); + }, []); + + return ( + <Modal + useNativeDriver + isVisible={visible} + hideModalContentWhileAnimating + style={styles.modal} + > + <PasscodeChoose theme={theme} type={TYPE.choose} finishProcess={onSubmit} force={data?.force} /> + {!data?.force + ? ( + <Touchable onPress={onCancel} style={styles.close}> + <CustomIcon name='cross' color={themes[theme].passcodePrimary} size={30} /> + </Touchable> + ) + : null} + </Modal> + ); +}); + +ChangePasscodeView.propTypes = { + theme: PropTypes.string +}; + +export default withTheme(ChangePasscodeView); diff --git a/app/views/DefaultBrowserView.js b/app/views/DefaultBrowserView.js index 8825ddce6..528d3d59a 100644 --- a/app/views/DefaultBrowserView.js +++ b/app/views/DefaultBrowserView.js @@ -69,13 +69,12 @@ class DefaultBrowserView extends React.Component { theme: PropTypes.string } - state = { - browser: null, - supported: [] - } - constructor(props) { super(props); + this.state = { + browser: null, + supported: [] + }; if (isIOS) { this.init(); } diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js index 698d1c7f1..d767afab6 100644 --- a/app/views/ForgotPasswordView.js +++ b/app/views/ForgotPasswordView.js @@ -85,7 +85,7 @@ class ForgotPasswordView extends React.Component { const { theme } = this.props; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='forgot-password-view'> <FormContainerInner> <Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Forgot_password')}</Text> <TextInput diff --git a/app/views/ForwardLivechatView.js b/app/views/ForwardLivechatView.js new file mode 100644 index 000000000..d9c97b8bb --- /dev/null +++ b/app/views/ForwardLivechatView.js @@ -0,0 +1,150 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { View, StyleSheet } from 'react-native'; +import { connect } from 'react-redux'; + +import I18n from '../i18n'; +import { withTheme } from '../theme'; +import { themes } from '../constants/colors'; +import RocketChat from '../lib/rocketchat'; +import OrSeparator from '../containers/OrSeparator'; +import Input from '../containers/UIKit/MultiSelect/Input'; +import { forwardRoom as forwardRoomAction } from '../actions/room'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16 + } +}); + +const ForwardLivechatView = ({ forwardRoom, navigation, theme }) => { + const [departments, setDepartments] = useState([]); + const [departmentId, setDepartment] = useState(); + const [users, setUsers] = useState([]); + const [userId, setUser] = useState(); + const [room, setRoom] = useState(); + + const rid = navigation.getParam('rid'); + + const getDepartments = async() => { + try { + const result = await RocketChat.getDepartments(); + if (result.success) { + setDepartments(result.departments.map(department => ({ label: department.name, value: department._id }))); + } + } catch { + // do nothing + } + }; + + const getUsers = async(term = '') => { + try { + const { servedBy: { _id: agentId } = {} } = room; + const _id = agentId && { $ne: agentId }; + const result = await RocketChat.usersAutoComplete({ conditions: { _id, status: { $ne: 'offline' }, statusLivechat: 'available' }, term }); + if (result.success) { + const parsedUsers = result.items.map(user => ({ label: user.username, value: user._id })); + setUsers(parsedUsers); + return parsedUsers; + } + } catch { + // do nothing + } + return []; + }; + + const getRoom = async() => { + try { + const result = await RocketChat.getRoomInfo(rid); + if (result.success) { + setRoom(result.room); + } + } catch { + // do nothing + } + }; + + const submit = () => { + const transferData = { roomId: rid }; + + if (!departmentId && !userId) { + return; + } + + if (userId) { + transferData.userId = userId; + } else { + transferData.departmentId = departmentId; + } + + forwardRoom(rid, transferData); + }; + + useEffect(() => { + getRoom(); + }, []); + + useEffect(() => { + if (room) { + getUsers(); + getDepartments(); + } + }, [room]); + + useEffect(() => { + if (departmentId || userId) { + submit(); + } + }, [departmentId, userId]); + + const onPressDepartment = () => { + navigation.navigate('PickerView', { + title: I18n.t('Forward_to_department'), + value: room?.departmentId, + data: departments, + onChangeValue: setDepartment, + goBack: false + }); + }; + + const onPressUser = () => { + navigation.navigate('PickerView', { + title: I18n.t('Forward_to_user'), + data: users, + onChangeValue: setUser, + onChangeText: getUsers, + goBack: false + }); + }; + + return ( + <View style={[styles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}> + <Input + onPress={onPressDepartment} + placeholder={I18n.t('Select_a_Department')} + theme={theme} + /> + <OrSeparator theme={theme} /> + <Input + onPress={onPressUser} + placeholder={I18n.t('Select_a_User')} + theme={theme} + /> + </View> + ); +}; +ForwardLivechatView.propTypes = { + forwardRoom: PropTypes.func, + navigation: PropTypes.object, + theme: PropTypes.string +}; +ForwardLivechatView.navigationOptions = { + title: I18n.t('Forward_Chat') +}; + +const mapDispatchToProps = dispatch => ({ + forwardRoom: (rid, transferData) => dispatch(forwardRoomAction(rid, transferData)) +}); + +export default connect(null, mapDispatchToProps)(withTheme(ForwardLivechatView)); diff --git a/app/views/LivechatEditView.js b/app/views/LivechatEditView.js new file mode 100644 index 000000000..5ac1692e1 --- /dev/null +++ b/app/views/LivechatEditView.js @@ -0,0 +1,285 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { Text, StyleSheet, ScrollView } from 'react-native'; +import { SafeAreaView } from 'react-navigation'; +import { connect } from 'react-redux'; + +import { withTheme } from '../theme'; +import { themes } from '../constants/colors'; +import TextInput from '../containers/TextInput'; +import KeyboardView from '../presentation/KeyboardView'; +import RocketChat from '../lib/rocketchat'; +import I18n from '../i18n'; + +import sharedStyles from './Styles'; +import { LISTENER } from '../containers/Toast'; +import EventEmitter from '../utils/events'; +import scrollPersistTaps from '../utils/scrollPersistTaps'; +import { getUserSelector } from '../selectors/login'; +import Chips from '../containers/UIKit/MultiSelect/Chips'; +import Button from '../containers/Button'; + +const styles = StyleSheet.create({ + container: { + padding: 16 + }, + title: { + fontSize: 20, + paddingVertical: 10, + ...sharedStyles.textMedium + } +}); + +const Title = ({ title, theme }) => (title ? <Text style={[styles.title, { color: themes[theme].titleText }]}>{title}</Text> : null); +Title.propTypes = { + title: PropTypes.string, + theme: PropTypes.string +}; + +const LivechatEditView = ({ user, navigation, theme }) => { + const [customFields, setCustomFields] = useState({}); + const [availableUserTags, setAvailableUserTags] = useState([]); + + const params = {}; + const inputs = {}; + + const livechat = navigation.getParam('room', {}); + const visitor = navigation.getParam('roomUser', {}); + + const getCustomFields = async() => { + const result = await RocketChat.getCustomFields(); + if (result.success && result.customFields?.length) { + const visitorCustomFields = result.customFields + .filter(field => field.visibility !== 'hidden' && field.scope === 'visitor') + .map(field => ({ [field._id]: (visitor.livechatData && visitor.livechatData[field._id]) || '' })) + .reduce((ret, field) => ({ [field]: field, ...ret })); + + const livechatCustomFields = result.customFields + .filter(field => field.visibility !== 'hidden' && field.scope === 'room') + .map(field => ({ [field._id]: (livechat.livechatData && livechat.livechatData[field._id]) || '' })) + .reduce((ret, field) => ({ [field]: field, ...ret })); + + return setCustomFields({ visitor: visitorCustomFields, livechat: livechatCustomFields }); + } + }; + + const [tagParam, setTags] = useState(livechat?.tags || []); + + useEffect(() => { + setTags([...tagParam, ...availableUserTags]); + }, [availableUserTags]); + + const getTagsList = async(agentDepartments) => { + const tags = await RocketChat.getTagsList(); + const isAdmin = ['admin', 'livechat-manager'].find(role => user.roles.includes(role)); + const availableTags = tags + .filter(({ departments }) => isAdmin || (departments.length === 0 || departments.some(i => agentDepartments.indexOf(i) > -1))) + .map(({ name }) => name); + setAvailableUserTags(availableTags); + }; + + const getAgentDepartments = async() => { + const result = await RocketChat.getAgentDepartments(visitor?._id); + if (result.success) { + const agentDepartments = result.departments.map(dept => dept.departmentId); + getTagsList(agentDepartments); + } + }; + + const submit = async() => { + const userData = { _id: visitor?._id }; + + const { rid, sms } = livechat; + const roomData = { _id: rid }; + + if (params.name) { + userData.name = params.name; + } + if (params.email) { + userData.email = params.email; + } + if (params.phone) { + userData.phone = params.phone; + } + + userData.livechatData = {}; + Object.entries(customFields?.visitor || {}).forEach(([key]) => { + if (params[key] || params[key] === '') { + userData.livechatData[key] = params[key]; + } + }); + + if (params.topic) { + roomData.topic = params.topic; + } + + roomData.tags = tagParam; + + roomData.livechatData = {}; + Object.entries(customFields?.livechat || {}).forEach(([key]) => { + if (params[key] || params[key] === '') { + roomData.livechatData[key] = params[key]; + } + }); + + if (sms) { + delete userData.phone; + } + + const { error } = await RocketChat.editLivechat(userData, roomData); + if (error) { + EventEmitter.emit(LISTENER, { message: error }); + } else { + EventEmitter.emit(LISTENER, { message: I18n.t('Saved') }); + navigation.goBack(); + } + }; + + const onChangeText = (key, text) => { params[key] = text; }; + + useEffect(() => { + getAgentDepartments(); + getCustomFields(); + }, []); + + return ( + <KeyboardView + style={{ backgroundColor: themes[theme].auxiliaryBackground }} + contentContainerStyle={sharedStyles.container} + keyboardVerticalOffset={128} + > + <ScrollView {...scrollPersistTaps}> + <SafeAreaView style={[sharedStyles.container, styles.container]} forceInset={{ vertical: 'never' }}> + <Title + title={visitor?.username} + theme={theme} + /> + <TextInput + label={I18n.t('Name')} + defaultValue={visitor?.name} + onChangeText={text => onChangeText('name', text)} + onSubmitEditing={() => { inputs.name.focus(); }} + theme={theme} + /> + <TextInput + label={I18n.t('Email')} + inputRef={(e) => { inputs.name = e; }} + defaultValue={visitor?.visitorEmails && visitor?.visitorEmails[0]?.address} + onChangeText={text => onChangeText('email', text)} + onSubmitEditing={() => { inputs.phone.focus(); }} + theme={theme} + /> + <TextInput + label={I18n.t('Phone')} + inputRef={(e) => { inputs.phone = e; }} + defaultValue={visitor?.phone && visitor?.phone[0]?.phoneNumber} + onChangeText={text => onChangeText('phone', text)} + onSubmitEditing={() => { + const keys = Object.keys(customFields?.visitor || {}); + if (keys.length > 0) { + const key = keys.pop(); + inputs[key].focus(); + } else { + inputs.topic.focus(); + } + }} + theme={theme} + /> + {Object.entries(customFields?.visitor || {}).map(([key, value], index, array) => ( + <TextInput + label={key} + defaultValue={value} + inputRef={(e) => { inputs[key] = e; }} + onChangeText={text => onChangeText(key, text)} + onSubmitEditing={() => { + if (array.length - 1 > index) { + return inputs[array[index + 1]].focus(); + } + inputs.topic.focus(); + }} + theme={theme} + /> + ))} + <Title + title={I18n.t('Conversation')} + theme={theme} + /> + <TextInput + label={I18n.t('Topic')} + inputRef={(e) => { inputs.topic = e; }} + defaultValue={livechat?.topic} + onChangeText={text => onChangeText('topic', text)} + onSubmitEditing={() => inputs.tags.focus()} + theme={theme} + /> + + <TextInput + inputRef={(e) => { inputs.tags = e; }} + label={I18n.t('Tags')} + iconRight='plus' + onIconRightPress={() => { + const lastText = inputs.tags._lastNativeText || ''; + if (lastText.length) { + setTags([...tagParam.filter(t => t !== lastText), lastText]); + inputs.tags.clear(); + } + }} + onSubmitEditing={() => { + const keys = Object.keys(customFields?.livechat || {}); + if (keys.length > 0) { + const key = keys.pop(); + inputs[key].focus(); + } else { + submit(); + } + }} + theme={theme} + /> + <Chips + items={tagParam.map(tag => ({ text: { text: tag }, value: tag }))} + onSelect={tag => setTags(tagParam.filter(t => t !== tag.value) || [])} + style={{ backgroundColor: themes[theme].backgroundColor }} + theme={theme} + /> + + {Object.entries(customFields?.livechat || {}).map(([key, value], index, array) => ( + <TextInput + label={key} + defaultValue={value} + inputRef={(e) => { inputs[key] = e; }} + onChangeText={text => onChangeText(key, text)} + onSubmitEditing={() => { + if (array.length - 1 > index) { + return inputs[array[index + 1]].focus(); + } + submit(); + }} + theme={theme} + /> + ))} + + <Button + title={I18n.t('Save')} + onPress={submit} + theme={theme} + /> + </SafeAreaView> + </ScrollView> + </KeyboardView> + ); +}; +LivechatEditView.propTypes = { + user: PropTypes.object, + navigation: PropTypes.object, + theme: PropTypes.string +}; +LivechatEditView.navigationOptions = ({ + title: I18n.t('Livechat_edit') +}); + +const mapStateToProps = state => ({ + server: state.server.server, + user: getUserSelector(state) +}); + +export default connect(mapStateToProps)(withTheme(LivechatEditView)); diff --git a/app/views/LoginView.js b/app/views/LoginView.js index 3d81b0f58..67cee69cf 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -84,7 +84,7 @@ class LoginView extends React.Component { }; } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { error } = this.props; if (nextProps.failure && !equal(error, nextProps.error)) { Alert.alert(I18n.t('Oops'), I18n.t('Login_error')); @@ -201,7 +201,7 @@ class LoginView extends React.Component { render() { const { Accounts_ShowFormLogin, theme } = this.props; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='login-view'> <FormContainerInner> <LoginServices separator={Accounts_ShowFormLogin} /> {this.renderUserForm()} diff --git a/app/views/MessagesView/index.js b/app/views/MessagesView/index.js index 6d8633b8d..cd7b25201 100644 --- a/app/views/MessagesView/index.js +++ b/app/views/MessagesView/index.js @@ -7,7 +7,7 @@ import equal from 'deep-equal'; import ActionSheet from 'react-native-action-sheet'; import styles from './styles'; -import Message from '../../containers/message/Message'; +import Message from '../../containers/message'; import ActivityIndicator from '../../containers/ActivityIndicator'; import I18n from '../../i18n'; import RocketChat from '../../lib/rocketchat'; @@ -87,6 +87,7 @@ class MessagesView extends React.Component { const { user, baseUrl, theme } = this.props; const renderItemCommonProps = item => ({ + item, baseUrl, user, author: item.u || item.user, @@ -110,21 +111,21 @@ class MessagesView extends React.Component { }, noDataMsg: I18n.t('No_files'), testID: 'room-files-view', - renderItem: (item) => { - const url = getFileUrlFromMessage(item); - - return ( - <Message - {...renderItemCommonProps(item)} - attachments={[{ + renderItem: item => ( + <Message + {...renderItemCommonProps(item)} + item={{ + ...item, + u: item.user, + attachments: [{ title: item.name, description: item.description, - ...url - }]} - theme={theme} - /> - ); - } + ...getFileUrlFromMessage(item) + }] + }} + theme={theme} + /> + ) }, // Mentions Messages Screen Mentions: { diff --git a/app/views/ModalBlockView.js b/app/views/ModalBlockView.js index 3c6314e2b..f8cd270a1 100644 --- a/app/views/ModalBlockView.js +++ b/app/views/ModalBlockView.js @@ -154,8 +154,8 @@ class ModalBlockView extends React.Component { this.setState({ errors }); } else { this.setState({ data }); + navigation.setParams({ data }); } - navigation.setParams({ data }); }; cancel = async({ closeModal }) => { diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js index cecd09886..42f9c1d5e 100644 --- a/app/views/NewMessageView.js +++ b/app/views/NewMessageView.js @@ -26,6 +26,7 @@ import { themedHeader } from '../utils/navigation'; import { getUserSelector } from '../selectors/login'; import Navigation from '../lib/Navigation'; import { createChannelRequest } from '../actions/createChannel'; +import { goRoom } from '../utils/goRoom'; const styles = StyleSheet.create({ safeAreaView: { @@ -123,12 +124,6 @@ class NewMessageView extends React.Component { this.search(text); } - onPressItem = (item) => { - const { navigation } = this.props; - const onPressItem = navigation.getParam('onPressItem', () => {}); - onPressItem(item); - } - dismiss = () => { const { navigation } = this.props; return navigation.pop(); @@ -231,7 +226,7 @@ class NewMessageView extends React.Component { <UserItem name={item.search ? item.name : item.fname} username={item.search ? item.username : item.name} - onPress={() => this.onPressItem(item)} + onPress={() => goRoom(item)} baseUrl={baseUrl} testID={`new-message-view-item-${ item.name }`} style={style} diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js index 38c3acbf5..cb82c58df 100644 --- a/app/views/NewServerView.js +++ b/app/views/NewServerView.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - Text, Keyboard, StyleSheet, TouchableOpacity, View, Alert + Text, Keyboard, StyleSheet, TouchableOpacity, View, Alert, BackHandler } from 'react-native'; import { connect } from 'react-redux'; import * as FileSystem from 'expo-file-system'; @@ -19,7 +19,7 @@ import { appStart as appStartAction } from '../actions'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import TextInput from '../containers/TextInput'; -import OnboardingSeparator from '../containers/OnboardingSeparator'; +import OrSeparator from '../containers/OrSeparator'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import I18n from '../i18n'; import { isIOS } from '../utils/deviceInfo'; @@ -69,7 +69,7 @@ class NewServerView extends React.Component { const previousServer = navigation.getParam('previousServer', null); const close = navigation.getParam('close', () => {}); return { - headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} /> : undefined, + headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} testID='new-server-view-close' /> : undefined, title: I18n.t('Workspaces'), ...themedHeader(screenProps.theme) }; @@ -105,6 +105,7 @@ class NewServerView extends React.Component { certificate: null }; EventEmitter.addEventListener('NewServer', this.handleNewServerEvent); + BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); } componentDidMount() { @@ -116,6 +117,16 @@ class NewServerView extends React.Component { componentWillUnmount() { EventEmitter.removeListener('NewServer', this.handleNewServerEvent); + BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); + } + + handleBackPress = () => { + const { navigation } = this.props; + if (navigation.isFocused() && this.previousServer) { + this.close(); + return true; + } + return false; } onChangeText = (text) => { @@ -286,7 +297,7 @@ class NewServerView extends React.Component { const { connecting, theme } = this.props; const { text, connectingOpen } = this.state; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='new-server-view'> <FormContainerInner> <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Join_your_workspace')}</Text> <TextInput @@ -310,10 +321,10 @@ class NewServerView extends React.Component { disabled={!text || connecting} loading={!connectingOpen && connecting} style={styles.connectButton} - testID='new-server-view-button' theme={theme} + testID='new-server-view-button' /> - <OnboardingSeparator theme={theme} /> + <OrSeparator theme={theme} /> <Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Onboarding_join_open_description')}</Text> <Button title={I18n.t('Join_our_open_workspace')} @@ -323,6 +334,7 @@ class NewServerView extends React.Component { disabled={connecting} loading={connectingOpen && connecting} theme={theme} + testID='new-server-view-open' /> </FormContainerInner> { isIOS ? this.renderCertificatePicker() : null } diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js index 921efd9db..3db267505 100644 --- a/app/views/OnboardingView/index.js +++ b/app/views/OnboardingView/index.js @@ -68,7 +68,7 @@ class OnboardingView extends React.Component { render() { const { theme } = this.props; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='onboarding-view'> <FormContainerInner> <Image style={styles.onboarding} source={{ uri: 'logo' }} fadeDuration={0} /> <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Onboarding_title')}</Text> @@ -80,6 +80,7 @@ class OnboardingView extends React.Component { type='primary' onPress={this.connectServer} theme={theme} + testID='join-workspace' /> <Button title={I18n.t('Create_a_new_workspace')} @@ -87,6 +88,7 @@ class OnboardingView extends React.Component { backgroundColor={themes[theme].chatComponentBackground} onPress={this.createWorkspace} theme={theme} + testID='create-workspace-button' /> </View> </FormContainerInner> diff --git a/app/views/PickerView.js b/app/views/PickerView.js index 5a8c73f2a..69275d30d 100644 --- a/app/views/PickerView.js +++ b/app/views/PickerView.js @@ -1,20 +1,38 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FlatList, StyleSheet } from 'react-native'; +import { + View, FlatList, StyleSheet, Text +} from 'react-native'; import I18n from '../i18n'; import { themedHeader } from '../utils/navigation'; import { withTheme } from '../theme'; import { themes } from '../constants/colors'; +import debounce from '../utils/debounce'; import sharedStyles from './Styles'; import ListItem from '../containers/ListItem'; import Check from '../containers/Check'; import Separator from '../containers/Separator'; +import SearchBox from '../containers/SearchBox'; const styles = StyleSheet.create({ check: { marginHorizontal: 0 + }, + search: { + width: '100%', + height: 56 + }, + noResult: { + fontSize: 16, + paddingVertical: 56, + ...sharedStyles.textAlignCenter, + ...sharedStyles.textSemibold + }, + withoutBorder: { + borderBottomWidth: 0, + borderTopWidth: 0 } }); @@ -54,13 +72,37 @@ class PickerView extends React.PureComponent { const data = props.navigation.getParam('data', []); const value = props.navigation.getParam('value'); this.state = { data, value }; + + this.onSearch = props.navigation.getParam('onChangeText'); } onChangeValue = (value) => { const { navigation } = this.props; + const goBack = navigation.getParam('goBack', true); const onChange = navigation.getParam('onChangeValue', () => {}); onChange(value); - navigation.goBack(); + if (goBack) { + navigation.goBack(); + } + } + + onChangeText = debounce(async(text) => { + if (this.onSearch) { + const data = await this.onSearch(text); + this.setState({ data }); + } + }, 300, true) + + renderSearch() { + if (!this.onSearch) { + return null; + } + + return ( + <View style={styles.search}> + <SearchBox onChangeText={this.onChangeText} /> + </View> + ); } render() { @@ -68,27 +110,32 @@ class PickerView extends React.PureComponent { const { theme } = this.props; return ( - <FlatList - data={data} - keyExtractor={item => item.value} - renderItem={({ item }) => ( - <Item - item={item} - theme={theme} - selected={(value || data[0]?.value) === item.value} - onItemPress={() => this.onChangeValue(item.value)} - /> - )} - ItemSeparatorComponent={() => <Separator theme={theme} />} - contentContainerStyle={[ - sharedStyles.listContentContainer, - { - backgroundColor: themes[theme].auxiliaryBackground, - borderColor: themes[theme].separatorColor - } - ]} - style={{ backgroundColor: themes[theme].auxiliaryBackground }} - /> + <> + {this.renderSearch()} + <FlatList + data={data} + keyExtractor={item => item.value} + renderItem={({ item }) => ( + <Item + item={item} + theme={theme} + selected={!this.onSearch && (value || data[0]?.value) === item.value} + onItemPress={() => this.onChangeValue(item.value)} + /> + )} + ItemSeparatorComponent={() => <Separator theme={theme} />} + ListEmptyComponent={() => <Text style={[styles.noResult, { color: themes[theme].titleText }]}>{I18n.t('No_results_found')}</Text>} + contentContainerStyle={[ + sharedStyles.listContentContainer, + { + backgroundColor: themes[theme].auxiliaryBackground, + borderColor: themes[theme].separatorColor + }, + !data.length && styles.withoutBorder + ]} + style={{ backgroundColor: themes[theme].auxiliaryBackground }} + /> + </> ); } } diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js index b33f15bb2..4ca1ea4a7 100644 --- a/app/views/ProfileView/index.js +++ b/app/views/ProfileView/index.js @@ -84,7 +84,7 @@ class ProfileView extends React.Component { } } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { user } = this.props; if (!equal(user, nextProps.user)) { this.init(nextProps.user); diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js index 28dd12a52..e27e9bfe4 100644 --- a/app/views/RegisterView.js +++ b/app/views/RegisterView.js @@ -58,7 +58,7 @@ class RegisterView extends React.Component { return { ...themedHeader(screenProps.theme), title, - headerRight: <LegalButton navigation={navigation} /> + headerRight: <LegalButton navigation={navigation} testID='register-view-more' /> }; } @@ -230,7 +230,7 @@ class RegisterView extends React.Component { const { saving } = this.state; const { theme, showLoginButton } = this.props; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='register-view'> <FormContainerInner> <LoginServices /> <Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text> diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 21dc281f0..626feb7a7 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -9,7 +9,7 @@ import _ from 'lodash'; import Touch from '../../utils/touch'; import { setLoading as setLoadingAction } from '../../actions/selectedUsers'; -import { leaveRoom as leaveRoomAction } from '../../actions/room'; +import { leaveRoom as leaveRoomAction, closeRoom as closeRoomAction } from '../../actions/room'; import styles from './styles'; import sharedStyles from '../Styles'; import Avatar from '../../containers/Avatar'; @@ -28,6 +28,7 @@ import { themedHeader } from '../../utils/navigation'; import { CloseModalButton } from '../../containers/HeaderButton'; import { getUserSelector } from '../../selectors/login'; import Markdown from '../../containers/markdown'; +import { showConfirmationAlert, showErrorAlert } from '../../utils/info'; class RoomActionsView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { @@ -51,6 +52,7 @@ class RoomActionsView extends React.Component { leaveRoom: PropTypes.func, jitsiEnabled: PropTypes.bool, setLoadingInvite: PropTypes.func, + closeRoom: PropTypes.func, theme: PropTypes.string } @@ -69,7 +71,9 @@ class RoomActionsView extends React.Component { canViewMembers: false, canAutoTranslate: false, canAddUser: false, - canInviteUser: false + canInviteUser: false, + canForwardGuest: false, + canReturnQueue: false }; if (room && room.observe && room.rid) { this.roomObservable = room.observe(); @@ -87,35 +91,43 @@ class RoomActionsView extends React.Component { async componentDidMount() { this.mounted = true; const { room, member } = this.state; - if (!room.id) { - try { - const result = await RocketChat.getChannelInfo(room.rid); - if (result.success) { - this.setState({ room: { ...result.channel, rid: result.channel._id } }); + if (room.rid) { + if (!room.id) { + try { + const result = await RocketChat.getChannelInfo(room.rid); + if (result.success) { + this.setState({ room: { ...result.channel, rid: result.channel._id } }); + } + } catch (e) { + log(e); } - } catch (e) { - log(e); + } + + if (room && room.t !== 'd' && this.canViewMembers()) { + try { + const counters = await RocketChat.getRoomCounters(room.rid, room.t); + if (counters.success) { + this.setState({ membersCount: counters.members, joined: counters.joined }); + } + } catch (e) { + log(e); + } + } else if (room.t === 'd' && _.isEmpty(member)) { + this.updateRoomMember(); + } + + const canAutoTranslate = await RocketChat.canAutoTranslate(); + this.setState({ canAutoTranslate }); + + this.canAddUser(); + this.canInviteUser(); + + // livechat permissions + if (room.t === 'l') { + this.canForwardGuest(); + this.canReturnQueue(); } } - - if (room && room.t !== 'd' && this.canViewMembers()) { - try { - const counters = await RocketChat.getRoomCounters(room.rid, room.t); - if (counters.success) { - this.setState({ membersCount: counters.members, joined: counters.joined }); - } - } catch (e) { - log(e); - } - } else if (room.t === 'd' && _.isEmpty(member)) { - this.updateRoomMember(); - } - - const canAutoTranslate = await RocketChat.canAutoTranslate(); - this.setState({ canAutoTranslate }); - - this.canAddUser(); - this.canInviteUser(); } componentWillUnmount() { @@ -184,9 +196,32 @@ class RoomActionsView extends React.Component { return result; } + canForwardGuest = async() => { + const { room } = this.state; + const { rid } = room; + let result = true; + + const transferLivechatGuest = 'transfer-livechat-guest'; + const permissions = await RocketChat.hasPermission([transferLivechatGuest], rid); + if (!permissions[transferLivechatGuest]) { + result = false; + } + + this.setState({ canForwardGuest: result }); + } + + canReturnQueue = async() => { + try { + const { returnQueue } = await RocketChat.getRoutingConfig(); + this.setState({ canReturnQueue: returnQueue }); + } catch { + // do nothing + } + } + get sections() { const { - room, member, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate + room, member, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue } = this.state; const { jitsiEnabled } = this.props; const { @@ -204,7 +239,7 @@ class RoomActionsView extends React.Component { const jitsiActions = jitsiEnabled ? [ { - icon: 'livechat', + icon: 'omnichannel', name: I18n.t('Voice_call'), event: () => RocketChat.callJitsi(rid, true), testID: 'room-actions-voice' @@ -371,7 +406,42 @@ class RoomActionsView extends React.Component { }); } } else if (t === 'l') { - sections[2].data = [notificationsAction]; + sections[2].data = []; + + sections[2].data.push({ + icon: 'circle-cross', + name: I18n.t('Close'), + event: this.closeLivechat + }); + + if (canForwardGuest) { + sections[2].data.push({ + icon: 'reply', + name: I18n.t('Forward'), + route: 'ForwardLivechatView', + params: { rid } + }); + } + + if (canReturnQueue) { + sections[2].data.push({ + icon: 'back', + name: I18n.t('Return'), + event: this.returnLivechat + }); + } + + sections[2].data.push({ + icon: 'reload', + name: I18n.t('Navigation_history'), + route: 'VisitorNavigationView', + params: { rid } + }); + + sections.push({ + data: [notificationsAction], + renderItem: this.renderItem + }); } return sections; @@ -382,6 +452,28 @@ class RoomActionsView extends React.Component { return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />; } + closeLivechat = () => { + const { room: { rid } } = this.state; + const { closeRoom } = this.props; + + closeRoom(rid); + } + + returnLivechat = () => { + const { room: { rid } } = this.state; + showConfirmationAlert({ + message: I18n.t('Would_you_like_to_return_the_inquiry'), + callToAction: I18n.t('Yes'), + onPress: async() => { + try { + await RocketChat.returnLivechat(rid); + } catch (e) { + showErrorAlert(e.reason, I18n.t('Oops')); + } + } + }); + } + updateRoomMember = async() => { const { room } = this.state; @@ -442,7 +534,7 @@ class RoomActionsView extends React.Component { Alert.alert( I18n.t('Are_you_sure_question_mark'), - I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: room.t === 'd' ? room.fname : room.name }), + I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }), [ { text: I18n.t('Cancel'), @@ -478,13 +570,13 @@ class RoomActionsView extends React.Component { > {t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null } </Avatar> - <View style={styles.roomTitleContainer}> + <View style={[styles.roomTitleContainer, item.disabled && styles.roomTitlePadding]}> {room.t === 'd' ? <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.fname}</Text> : ( <View style={styles.roomTitleRow}> - <RoomTypeIcon type={room.prid ? 'discussion' : room.t} theme={theme} /> - <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.prid ? room.fname : room.name}</Text> + <RoomTypeIcon type={room.prid ? 'discussion' : room.t} status={room.visitor?.status} theme={theme} /> + <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{RocketChat.getRoomTitle(room)}</Text> </View> ) } @@ -495,7 +587,7 @@ class RoomActionsView extends React.Component { numberOfLines={1} theme={theme} /> - {room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} />} + {room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} numberOfLines={1} />} </View> {!item.disabled && <DisclosureIndicator theme={theme} />} </> @@ -581,6 +673,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t)), + closeRoom: rid => dispatch(closeRoomAction(rid)), setLoadingInvite: loading => dispatch(setLoadingAction(loading)) }); diff --git a/app/views/RoomActionsView/styles.js b/app/views/RoomActionsView/styles.js index 8021dfb2a..93079c1d8 100644 --- a/app/views/RoomActionsView/styles.js +++ b/app/views/RoomActionsView/styles.js @@ -43,9 +43,11 @@ export default StyleSheet.create({ roomTitleContainer: { flex: 1 }, + roomTitlePadding: { + paddingRight: 16 + }, roomTitle: { fontSize: 16, - paddingRight: 16, ...sharedStyles.textMedium }, roomDescription: { @@ -53,6 +55,7 @@ export default StyleSheet.create({ ...sharedStyles.textRegular }, roomTitleRow: { + paddingRight: 16, flexDirection: 'row', alignItems: 'center' } diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js index 63de0c02c..592ff8467 100644 --- a/app/views/RoomInfoEditView/index.js +++ b/app/views/RoomInfoEditView/index.js @@ -125,13 +125,13 @@ class RoomInfoEditView extends React.Component { init = (room) => { const { - name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired, sysMes + description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired, sysMes } = room; // fake password just to user knows about it this.randomValue = random(15); this.setState({ room, - name, + name: RocketChat.getRoomTitle(room), description, topic, announcement, diff --git a/app/views/RoomInfoView/Channel.js b/app/views/RoomInfoView/Channel.js new file mode 100644 index 000000000..bb65128b8 --- /dev/null +++ b/app/views/RoomInfoView/Channel.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import I18n from '../../i18n'; +import Item from './Item'; + +const Channel = ({ room, theme }) => { + const { description, topic, announcement } = room; + return ( + <> + <Item + label={I18n.t('Description')} + content={description || `__${ I18n.t('No_label_provided', { label: 'description' }) }__`} + theme={theme} + testID='room-info-view-description' + /> + <Item + label={I18n.t('Topic')} + content={topic || `__${ I18n.t('No_label_provided', { label: 'topic' }) }__`} + theme={theme} + testID='room-info-view-topic' + /> + <Item + label={I18n.t('Announcement')} + content={announcement || `__${ I18n.t('No_label_provided', { label: 'announcement' }) }__`} + theme={theme} + testID='room-info-view-announcement' + /> + <Item + label={I18n.t('Broadcast_Channel')} + content={room.broadcast && I18n.t('Broadcast_channel_Description')} + theme={theme} + testID='room-info-view-broadcast' + /> + </> + ); +}; +Channel.propTypes = { + room: PropTypes.object, + theme: PropTypes.string +}; + +export default Channel; diff --git a/app/views/RoomInfoView/CustomFields.js b/app/views/RoomInfoView/CustomFields.js new file mode 100644 index 000000000..b58c7af18 --- /dev/null +++ b/app/views/RoomInfoView/CustomFields.js @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Item from './Item'; + +const CustomFields = ({ customFields, theme }) => { + if (customFields) { + return ( + Object.keys(customFields).map((title) => { + if (!customFields[title]) { + return; + } + return ( + <Item + label={title} + content={customFields[title]} + theme={theme} + /> + ); + }) + ); + } + + return null; +}; +CustomFields.propTypes = { + customFields: PropTypes.object, + theme: PropTypes.string +}; + +export default CustomFields; diff --git a/app/views/RoomInfoView/Direct.js b/app/views/RoomInfoView/Direct.js new file mode 100644 index 000000000..7587bda6f --- /dev/null +++ b/app/views/RoomInfoView/Direct.js @@ -0,0 +1,42 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +import PropTypes from 'prop-types'; + +import { themes } from '../../constants/colors'; +import I18n from '../../i18n'; + +import Timezone from './Timezone'; +import CustomFields from './CustomFields'; + +import styles from './styles'; + +const Roles = ({ roles, theme }) => (roles && roles.length ? ( + <View style={styles.item}> + <Text style={[styles.itemLabel, { color: themes[theme].titleText }]}>{I18n.t('Roles')}</Text> + <View style={styles.rolesContainer}> + {roles.map(role => (role ? ( + <View style={[styles.roleBadge, { backgroundColor: themes[theme].auxiliaryBackground }]} key={role}> + <Text style={styles.role}>{role}</Text> + </View> + ) : null))} + </View> + </View> +) : null); +Roles.propTypes = { + roles: PropTypes.array, + theme: PropTypes.string +}; + +const Direct = ({ roomUser, theme }) => ( + <> + <Roles roles={roomUser.parsedRoles} theme={theme} /> + <Timezone utcOffset={roomUser.utcOffset} theme={theme} /> + <CustomFields customFields={roomUser.customFields} theme={theme} /> + </> +); +Direct.propTypes = { + roomUser: PropTypes.object, + theme: PropTypes.string +}; + +export default Direct; diff --git a/app/views/RoomInfoView/Item.js b/app/views/RoomInfoView/Item.js new file mode 100644 index 000000000..b2503c2e3 --- /dev/null +++ b/app/views/RoomInfoView/Item.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +import PropTypes from 'prop-types'; + +import styles from './styles'; +import Markdown from '../../containers/markdown'; +import { themes } from '../../constants/colors'; + +const Item = ({ + label, content, theme, testID +}) => ( + content ? ( + <View style={styles.item} testID={testID}> + <Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>{label}</Text> + <Markdown + style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} + msg={content} + theme={theme} + /> + </View> + ) : null +); +Item.propTypes = { + label: PropTypes.string, + content: PropTypes.string, + theme: PropTypes.string, + testID: PropTypes.string +}; + +export default Item; diff --git a/app/views/RoomInfoView/Livechat.js b/app/views/RoomInfoView/Livechat.js new file mode 100644 index 000000000..a93ea1714 --- /dev/null +++ b/app/views/RoomInfoView/Livechat.js @@ -0,0 +1,140 @@ +import React, { useState, useEffect } from 'react'; +import { Text, StyleSheet } from 'react-native'; +import PropTypes from 'prop-types'; + +import RocketChat from '../../lib/rocketchat'; +import { withTheme } from '../../theme'; +import CustomFields from './CustomFields'; +import Item from './Item'; +import Timezone from './Timezone'; +import sharedStyles from '../Styles'; +import { themes } from '../../constants/colors'; +import I18n from '../../i18n'; + +const styles = StyleSheet.create({ + title: { + fontSize: 16, + paddingHorizontal: 20, + ...sharedStyles.textMedium + } +}); + +const Title = ({ title, theme }) => <Text style={[styles.title, { color: themes[theme].titleText }]}>{title}</Text>; +Title.propTypes = { + title: PropTypes.string, + theme: PropTypes.string +}; + +const Livechat = ({ room, roomUser, theme }) => { + const [department, setDepartment] = useState({}); + + + const getDepartment = async(id) => { + if (id) { + const result = await RocketChat.getDepartmentInfo(id); + if (result.success) { + setDepartment(result.department); + } + } + }; + + const getRoom = () => { + if (room.departmentId) { + getDepartment(room.departmentId); + } + }; + + useEffect(() => { getRoom(); }, []); + + return ( + <> + <Title + title={I18n.t('User')} + theme={theme} + /> + <Timezone + utcOffset={roomUser.utc} + theme={theme} + /> + <Item + label={I18n.t('Username')} + content={roomUser.username} + theme={theme} + /> + <Item + label={I18n.t('Email')} + content={roomUser.visitorEmails?.map(email => email.address).reduce((ret, item) => `${ ret }${ item }\n`)} + theme={theme} + /> + <Item + label={I18n.t('Phone')} + content={roomUser.phone?.map(phone => phone.phoneNumber).reduce((ret, item) => `${ ret }${ item }\n`)} + theme={theme} + /> + <Item + label={I18n.t('IP')} + content={roomUser.ip} + theme={theme} + /> + <Item + label={I18n.t('OS')} + content={roomUser.os} + theme={theme} + /> + <Item + label={I18n.t('Browser')} + content={roomUser.browser} + theme={theme} + /> + <CustomFields + customFields={roomUser.livechatData} + theme={theme} + /> + <Title + title={I18n.t('Conversation')} + theme={theme} + /> + <Item + label={I18n.t('Agent')} + content={room.servedBy?.username} + theme={theme} + /> + <Item + label={I18n.t('Facebook')} + content={room.facebook?.page.name} + theme={theme} + /> + <Item + label={I18n.t('SMS')} + content={room.sms && 'SMS Enabled'} + theme={theme} + /> + <Item + label={I18n.t('Topic')} + content={room.topic} + theme={theme} + /> + <Item + label={I18n.t('Tags')} + content={room.tags?.join(', ')} + theme={theme} + /> + <Item + label={I18n.t('Department')} + content={department.name} + theme={theme} + /> + <CustomFields + customFields={room.livechatData} + theme={theme} + /> + </> + ); +}; +Livechat.propTypes = { + room: PropTypes.object, + roomUser: PropTypes.object, + theme: PropTypes.string +}; + +export default withTheme(Livechat); diff --git a/app/views/RoomInfoView/Timezone.js b/app/views/RoomInfoView/Timezone.js new file mode 100644 index 000000000..f07433967 --- /dev/null +++ b/app/views/RoomInfoView/Timezone.js @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import moment from 'moment'; + +import I18n from '../../i18n'; +import Item from './Item'; + +const Timezone = ({ utcOffset, Message_TimeFormat, theme }) => (utcOffset ? ( + <Item + label={I18n.t('Timezone')} + content={`${ moment().utcOffset(utcOffset).format(Message_TimeFormat) } (UTC ${ utcOffset })`} + theme={theme} + /> +) : null); +Timezone.propTypes = { + utcOffset: PropTypes.number, + Message_TimeFormat: PropTypes.string, + theme: PropTypes.string +}; + +const mapStateToProps = state => ({ + Message_TimeFormat: state.settings.Message_TimeFormat +}); + +export default connect(mapStateToProps)(Timezone); diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js index 184109ea7..7cc2cff56 100644 --- a/app/views/RoomInfoView/index.js +++ b/app/views/RoomInfoView/index.js @@ -3,26 +3,34 @@ import PropTypes from 'prop-types'; import { View, Text, ScrollView } from 'react-native'; import { BorderlessButton } from 'react-native-gesture-handler'; import { connect } from 'react-redux'; -import moment from 'moment'; -import _ from 'lodash'; import { SafeAreaView } from 'react-navigation'; +import UAParser from 'ua-parser-js'; +import _ from 'lodash'; + +import database from '../../lib/database'; import { CustomIcon } from '../../lib/Icons'; import Status from '../../containers/Status'; import Avatar from '../../containers/Avatar'; import styles from './styles'; import sharedStyles from '../Styles'; -import database from '../../lib/database'; import RocketChat from '../../lib/rocketchat'; import RoomTypeIcon from '../../containers/RoomTypeIcon'; import I18n from '../../i18n'; -import { CustomHeaderButtons, Item } from '../../containers/HeaderButton'; +import { CustomHeaderButtons } from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; import log from '../../utils/log'; import { themes } from '../../constants/colors'; import { withTheme } from '../../theme'; +import { withSplit } from '../../split'; import { themedHeader } from '../../utils/navigation'; import { getUserSelector } from '../../selectors/login'; import Markdown from '../../containers/markdown'; +import Navigation from '../../lib/Navigation'; + +import Livechat from './Livechat'; +import Channel from './Channel'; +import Item from './Item'; +import Direct from './Direct'; const PERMISSION_EDIT_ROOM = 'edit-room'; const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd' @@ -35,24 +43,30 @@ const getRoomTitle = (room, type, name, username, statusText, theme) => (type == ) : ( <View style={styles.roomTitleRow}> - <RoomTypeIcon type={room.prid ? 'discussion' : room.t} key='room-info-type' theme={theme} /> - <Text testID='room-info-view-name' style={[styles.roomTitle, { color: themes[theme].titleText }]} key='room-info-name'>{room.prid ? room.fname : room.name}</Text> + <RoomTypeIcon type={room.prid ? 'discussion' : room.t} key='room-info-type' status={room.visitor?.status} theme={theme} /> + <Text testID='room-info-view-name' style={[styles.roomTitle, { color: themes[theme].titleText }]} key='room-info-name'>{RocketChat.getRoomTitle(room)}</Text> </View> ) ); class RoomInfoView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { - const showEdit = navigation.getParam('showEdit'); - const rid = navigation.getParam('rid'); const t = navigation.getParam('t'); + const rid = navigation.getParam('rid'); + const room = navigation.getParam('room'); + const roomUser = navigation.getParam('roomUser'); + const showEdit = navigation.getParam('showEdit', t === 'l'); return { title: t === 'd' ? I18n.t('User_Info') : I18n.t('Room_Info'), ...themedHeader(screenProps.theme), headerRight: showEdit ? ( <CustomHeaderButtons> - <Item iconName='edit' onPress={() => navigation.navigate('RoomInfoEditView', { rid })} testID='room-info-view-edit-button' /> + <Item + iconName='edit' + onPress={() => navigation.navigate(t === 'l' ? 'LivechatEditView' : 'RoomInfoEditView', { rid, room, roomUser })} + testID='room-info-view-edit-button' + /> </CustomHeaderButtons> ) : null @@ -66,7 +80,8 @@ class RoomInfoView extends React.Component { token: PropTypes.string }), baseUrl: PropTypes.string, - Message_TimeFormat: PropTypes.string, + rooms: PropTypes.array, + split: PropTypes.bool, theme: PropTypes.string } @@ -78,68 +93,42 @@ class RoomInfoView extends React.Component { this.t = props.navigation.getParam('t'); this.state = { room: room || { rid: this.rid, t: this.t }, - roomUser: roomUser || {}, - parsedRoles: [] + roomUser: roomUser || {} }; } - async componentDidMount() { - const { roomUser, room: roomState } = this.state; - if (this.t === 'd' && !_.isEmpty(roomUser)) { - return; - } - - if (this.t === 'd') { - try { - const roomUserId = RocketChat.getUidDirectMessage(roomState); - const result = await RocketChat.getUserInfo(roomUserId); - if (result.success) { - const { roles } = result.user; - let parsedRoles = []; - if (roles && roles.length) { - parsedRoles = await Promise.all(roles.map(async(role) => { - const description = await this.getRoleDescription(role); - return description; - })); - } - this.setState({ roomUser: result.user, parsedRoles }); - } - } catch (e) { - log(e); - } - return; + componentDidMount() { + if (this.isDirect) { + this.loadUser(); + } else { + this.loadRoom(); } const { navigation } = this.props; - let room = navigation.getParam('room'); - if (room && room.observe) { - this.roomObservable = room.observe(); - this.subscription = this.roomObservable - .subscribe((changes) => { - this.setState({ room: changes }); - }); - } else { - try { - const result = await RocketChat.getRoomInfo(this.rid); - if (result.success) { - // eslint-disable-next-line prefer-destructuring - room = result.room; - this.setState({ room }); - } - } catch (e) { - log(e); + this.willFocusListener = navigation.addListener('willFocus', () => { + if (this.isLivechat) { + this.loadVisitor(); } - } - const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid); - if (permissions[PERMISSION_EDIT_ROOM] && !room.prid && this.t !== 'l') { - navigation.setParams({ showEdit: true }); - } + }); } componentWillUnmount() { if (this.subscription && this.subscription.unsubscribe) { this.subscription.unsubscribe(); } + if (this.willFocusListener && this.willFocusListener.remove) { + this.willFocusListener.remove(); + } + } + + get isDirect() { + const { room } = this.state; + return room.t === 'd'; + } + + get isLivechat() { + const { room } = this.state; + return room.t === 'l'; } getRoleDescription = async(id) => { @@ -154,86 +143,127 @@ class RoomInfoView extends React.Component { } catch (e) { return null; } + }; + + loadVisitor = async() => { + const { room } = this.state; + const { navigation } = this.props; + + try { + const result = await RocketChat.getVisitorInfo(room?.visitor?._id); + if (result.success) { + const { visitor } = result; + if (visitor.userAgent) { + const ua = new UAParser(); + ua.setUA(visitor.userAgent); + visitor.os = `${ ua.getOS().name } ${ ua.getOS().version }`; + visitor.browser = `${ ua.getBrowser().name } ${ ua.getBrowser().version }`; + } + this.setState({ roomUser: visitor }); + navigation.setParams({ roomUser: visitor }); + } + } catch (error) { + // Do nothing + } } - goRoom = async() => { - const { roomUser } = this.state; - const { username } = roomUser; + loadUser = async() => { + const { room: roomState, roomUser } = this.state; + + if (_.isEmpty(roomUser)) { + try { + const roomUserId = RocketChat.getUidDirectMessage(roomState); + const result = await RocketChat.getUserInfo(roomUserId); + if (result.success) { + const { user } = result; + const { roles } = user; + if (roles && roles.length) { + user.parsedRoles = await Promise.all(roles.map(async(role) => { + const description = await this.getRoleDescription(role); + return description; + })); + } + + const room = await this.getDirect(user.username); + + this.setState({ roomUser: user, room: { ...roomState, rid: room.rid } }); + } + } catch { + // do nothing + } + } + } + + loadRoom = async() => { const { navigation } = this.props; + let room = navigation.getParam('room'); + if (room && room.observe) { + this.roomObservable = room.observe(); + this.subscription = this.roomObservable + .subscribe((changes) => { + this.setState({ room: changes }); + navigation.setParams({ room: changes }); + }); + } else { + try { + const result = await RocketChat.getRoomInfo(this.rid); + if (result.success) { + ({ room } = result); + this.setState({ room }); + } + } catch (e) { + log(e); + } + } + + const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid); + if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) { + navigation.setParams({ showEdit: true }); + } + } + + getDirect = async(username) => { try { const result = await RocketChat.createDirectMessage(username); if (result.success) { - await navigation.navigate('RoomsListView'); - const rid = result.room._id; - navigation.navigate('RoomView', { rid, name: RocketChat.getRoomTitle(roomUser), t: 'd' }); + return result.room; } - } catch (e) { + } catch { // do nothing } } - videoCall = () => RocketChat.callJitsi(this.rid) + goRoom = () => { + const { roomUser, room } = this.state; + const { name, username } = roomUser; + const { rooms, navigation, split } = this.props; - isDirect = () => this.t === 'd' + if (room.rid) { + let navigate = navigation.push; - renderItem = ({ label, content }) => { - const { theme } = this.props; - return ( - <View style={styles.item}> - <Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>{label}</Text> - <Markdown - style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} - msg={content || `__${ I18n.t('No_label_provided', { label: label.toLowerCase() }) }__`} - theme={theme} - /> - </View> - ); - } - - renderRole = (description) => { - const { theme } = this.props; - if (description) { - return ( - <View style={[styles.roleBadge, { backgroundColor: themes[theme].auxiliaryBackground }]} key={description}> - <Text style={styles.role}>{ description }</Text> - </View> - ); - } - return null; - } - - renderRoles = () => { - const { parsedRoles } = this.state; - const { theme } = this.props; - if (parsedRoles && parsedRoles.length) { - return ( - <View style={styles.item}> - <Text style={[styles.itemLabel, { color: themes[theme].titleText }]}>{I18n.t('Roles')}</Text> - <View style={styles.rolesContainer}> - {parsedRoles.map(role => this.renderRole(role))} - </View> - </View> - ); - } - return null; - } - - renderTimezone = () => { - const { roomUser } = this.state; - const { Message_TimeFormat } = this.props; - - if (roomUser) { - const { utcOffset } = roomUser; - - if (!utcOffset) { - return null; + // if this is a room focused + if (rooms.includes(room.rid)) { + ({ navigate } = navigation); + } else if (split) { + ({ navigate } = Navigation); } - return this.renderItem({ - label: I18n.t('Timezone'), - content: `${ moment().utcOffset(utcOffset).format(Message_TimeFormat) } (UTC ${ utcOffset })` + + navigate('RoomView', { + rid: room.rid, + name: RocketChat.getRoomTitle({ + t: room.t, + fname: name, + name: username + }), + t: room.t, + roomUserId: RocketChat.getUidDirectMessage(room) }); } - return null; + } + + videoCall = () => { + const { room } = this.state; + RocketChat.callJitsi(room.rid); } renderAvatar = (room, roomUser) => { @@ -254,37 +284,6 @@ class RoomInfoView extends React.Component { ); } - renderBroadcast = () => this.renderItem({ - label: I18n.t('Broadcast_Channel'), - content: I18n.t('Broadcast_channel_Description') - }); - - renderCustomFields = () => { - const { roomUser } = this.state; - if (roomUser) { - const { customFields } = roomUser; - - if (!roomUser.customFields) { - return null; - } - - return ( - Object.keys(customFields).map((title) => { - if (!customFields[title]) { - return; - } - return ( - <View style={styles.item} key={title}> - <Text style={styles.itemLabel}>{title}</Text> - <Text style={styles.itemContent}>{customFields[title]}</Text> - </View> - ); - }) - ); - } - return null; - } - renderButton = (onPress, iconName, text) => { const { theme } = this.props; return ( @@ -309,37 +308,21 @@ class RoomInfoView extends React.Component { </View> ) - renderChannel = () => { - const { room } = this.state; - const { description, topic, announcement } = room; - return ( - <> - {this.renderItem({ label: I18n.t('Description'), content: description })} - {this.renderItem({ label: I18n.t('Topic'), content: topic })} - {this.renderItem({ label: I18n.t('Announcement'), content: announcement })} - {room.broadcast ? this.renderBroadcast() : null} - </> - ); - } + renderContent = () => { + const { room, roomUser } = this.state; + const { theme } = this.props; - renderDirect = () => { - const { roomUser } = this.state; - return ( - <> - {this.renderRoles()} - {this.renderTimezone()} - {this.renderCustomFields(roomUser._id)} - </> - ); + if (this.isDirect) { + return <Direct roomUser={roomUser} theme={theme} />; + } else if (this.t === 'l') { + return <Livechat room={room} roomUser={roomUser} theme={theme} />; + } + return <Channel room={room} theme={theme} />; } render() { const { room, roomUser } = this.state; const { theme } = this.props; - const isDirect = this.isDirect(); - if (!room) { - return <View />; - } return ( <ScrollView style={[styles.scroll, { backgroundColor: themes[theme].backgroundColor }]}> <StatusBar theme={theme} /> @@ -348,12 +331,12 @@ class RoomInfoView extends React.Component { forceInset={{ vertical: 'never' }} testID='room-info-view' > - <View style={[styles.avatarContainer, isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}> + <View style={[styles.avatarContainer, this.isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}> {this.renderAvatar(room, roomUser)} - <View style={styles.roomTitleContainer}>{ getRoomTitle(room, this.t, roomUser && roomUser.name, roomUser && roomUser.username, roomUser && roomUser.statusText, theme) }</View> - {isDirect ? this.renderButtons() : null} + <View style={styles.roomTitleContainer}>{ getRoomTitle(room, this.t, roomUser?.name, roomUser?.username, roomUser?.statusText, theme) }</View> + {this.isDirect ? this.renderButtons() : null} </View> - {isDirect ? this.renderDirect() : this.renderChannel()} + {this.renderContent()} </SafeAreaView> </ScrollView> ); @@ -363,7 +346,7 @@ class RoomInfoView extends React.Component { const mapStateToProps = state => ({ baseUrl: state.server.server, user: getUserSelector(state), - Message_TimeFormat: state.settings.Message_TimeFormat + rooms: state.room.rooms }); -export default connect(mapStateToProps)(withTheme(RoomInfoView)); +export default connect(mapStateToProps)(withSplit(withTheme(RoomInfoView))); diff --git a/app/views/RoomInfoView/styles.js b/app/views/RoomInfoView/styles.js index fe82ca7c4..ed6b1d978 100644 --- a/app/views/RoomInfoView/styles.js +++ b/app/views/RoomInfoView/styles.js @@ -16,28 +16,32 @@ export default StyleSheet.create({ justifyContent: 'center' }, avatarContainer: { - height: 240, + minHeight: 240, flexDirection: 'column', alignItems: 'center', justifyContent: 'center', marginBottom: 20 }, avatarContainerDirectRoom: { - height: 320 + paddingVertical: 16, + minHeight: 320 }, avatar: { marginHorizontal: 10 }, roomTitleContainer: { paddingTop: 20, + marginHorizontal: 16, alignItems: 'center' }, roomTitle: { fontSize: 20, + ...sharedStyles.textAlignCenter, ...sharedStyles.textMedium }, roomUsername: { fontSize: 18, + ...sharedStyles.textAlignCenter, ...sharedStyles.textRegular }, roomTitleRow: { diff --git a/app/views/RoomView/Banner.js b/app/views/RoomView/Banner.js index a023fa1c6..44ae55d12 100644 --- a/app/views/RoomView/Banner.js +++ b/app/views/RoomView/Banner.js @@ -6,17 +6,18 @@ import Modal from 'react-native-modal'; import Markdown from '../../containers/markdown'; +import { CustomIcon } from '../../lib/Icons'; import { themes } from '../../constants/colors'; import styles from './styles'; const Banner = React.memo(({ - text, title, theme + text, title, theme, bannerClosed, closeBanner }) => { const [showModal, openModal] = useState(false); const toggleModal = () => openModal(prevState => !prevState); - if (text) { + if (text && !bannerClosed) { return ( <> <BorderlessButton @@ -28,8 +29,16 @@ const Banner = React.memo(({ msg={text} theme={theme} numberOfLines={1} + style={[styles.bannerText]} preview /> + <BorderlessButton onPress={closeBanner}> + <CustomIcon + color={themes[theme].auxiliaryText} + name='cross' + size={20} + /> + </BorderlessButton> </BorderlessButton> <Modal onBackdropPress={toggleModal} @@ -54,12 +63,14 @@ const Banner = React.memo(({ } return null; -}, (prevProps, nextProps) => prevProps.text === nextProps.text && prevProps.theme === nextProps.theme); +}, (prevProps, nextProps) => prevProps.text === nextProps.text && prevProps.theme === nextProps.theme && prevProps.bannerClosed === nextProps.bannerClosed); Banner.propTypes = { text: PropTypes.string, title: PropTypes.string, - theme: PropTypes.string + theme: PropTypes.string, + bannerClosed: PropTypes.bool, + closeBanner: PropTypes.func }; export default Banner; diff --git a/app/views/RoomView/Header/Header.js b/app/views/RoomView/Header/Header.js index 2a668f0ed..d9b527550 100644 --- a/app/views/RoomView/Header/Header.js +++ b/app/views/RoomView/Header/Header.js @@ -35,6 +35,7 @@ const styles = StyleSheet.create({ alignItems: 'center' }, subtitle: { + marginRight: -16, ...sharedStyles.textRegular, fontSize: 12 }, diff --git a/app/views/RoomView/Header/Icon.js b/app/views/RoomView/Header/Icon.js index 12ab5572e..d47f75603 100644 --- a/app/views/RoomView/Header/Icon.js +++ b/app/views/RoomView/Header/Icon.js @@ -29,7 +29,7 @@ const Icon = React.memo(({ } let colorStyle = {}; - if (type === 'd' && roomUserId) { + if (type === 'l') { colorStyle = { color: STATUS_COLORS[status] }; } else { colorStyle = { color: isAndroid && theme === 'light' ? themes[theme].buttonText : themes[theme].auxiliaryText }; diff --git a/app/views/RoomView/Header/RoomHeaderLeft.js b/app/views/RoomView/Header/RoomHeaderLeft.js index 85b8db314..670dfd150 100644 --- a/app/views/RoomView/Header/RoomHeaderLeft.js +++ b/app/views/RoomView/Header/RoomHeaderLeft.js @@ -37,7 +37,6 @@ const RoomHeaderLeft = ({ style={styles.avatar} userId={userId} token={token} - theme={theme} onPress={goRoomActionsView} /> ); diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js index 5d3dc2592..8b1bec52f 100644 --- a/app/views/RoomView/Header/index.js +++ b/app/views/RoomView/Header/index.js @@ -8,7 +8,6 @@ import Header from './Header'; import RightButtons from './RightButtons'; import { withTheme } from '../../../theme'; import RoomHeaderLeft from './RoomHeaderLeft'; -import { getUserSelector } from '../../../selectors/login'; class RoomHeaderView extends Component { static propTypes = { @@ -95,17 +94,15 @@ class RoomHeaderView extends Component { } const mapStateToProps = (state, ownProps) => { - let status; let statusText; - const { roomUserId, type } = ownProps; - if (type === 'd') { - const user = getUserSelector(state); - if (user.id) { - if (state.activeUsers[roomUserId] && state.meteor.connected) { - ({ status, statusText } = state.activeUsers[roomUserId]); - } else { - status = 'offline'; - } + let status = 'offline'; + const { roomUserId, type, visitor = {} } = ownProps; + + if (state.meteor.connected) { + if (type === 'd' && state.activeUsers[roomUserId]) { + ({ status, statusText } = state.activeUsers[roomUserId]); + } else if (type === 'l' && visitor?.status) { + ({ status } = visitor); } } diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js index 5ca13644b..51240c7a0 100644 --- a/app/views/RoomView/List.js +++ b/app/views/RoomView/List.js @@ -27,11 +27,22 @@ class List extends React.Component { t: PropTypes.string, tmid: PropTypes.string, theme: PropTypes.string, + loading: PropTypes.bool, listRef: PropTypes.func, hideSystemMessages: PropTypes.array, navigation: PropTypes.object }; + // this.state.loading works for this.onEndReached and RoomView.init + static getDerivedStateFromProps(props, state) { + if (props.loading !== state.loading) { + return { + loading: props.loading + }; + } + return null; + } + constructor(props) { super(props); console.time(`${ this.constructor.name } init`); @@ -96,7 +107,7 @@ class List extends React.Component { this.messagesSubscription = this.messagesObservable .subscribe((data) => { this.interaction = InteractionManager.runAfterInteractions(() => { - if (tmid) { + if (tmid && this.thread) { data = [this.thread, ...data]; } const messages = orderBy(data, ['ts'], ['desc']); @@ -116,16 +127,6 @@ class List extends React.Component { this.init(); } - // this.state.loading works for this.onEndReached and RoomView.init - static getDerivedStateFromProps(props, state) { - if (props.loading !== state.loading) { - return { - loading: props.loading - }; - } - return null; - } - shouldComponentUpdate(nextProps, nextState) { const { loading, end, refreshing } = this.state; const { hideSystemMessages, theme } = this.props; diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 341df6ce6..5dba91af7 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -69,7 +69,7 @@ const stateAttrsUpdate = [ 'readOnly', 'member' ]; -const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles']; +const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles', 'bannerClosed', 'visitor']; class RoomView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { @@ -87,6 +87,7 @@ class RoomView extends React.Component { const goRoomActionsView = navigation.getParam('goRoomActionsView', () => {}); const unreadsCount = navigation.getParam('unreadsCount', null); const roomUserId = navigation.getParam('roomUserId'); + const visitor = navigation.getParam('visitor'); if (!rid) { return { ...themedHeader(screenProps.theme) @@ -104,6 +105,7 @@ class RoomView extends React.Component { type={t} widthOffset={tmid ? 95 : 130} roomUserId={roomUserId} + visitor={visitor} goRoomActionsView={goRoomActionsView} /> ), @@ -218,7 +220,7 @@ class RoomView extends React.Component { } = this.props; if ((room.id || room.rid) && !this.tmid) { navigation.setParams({ - name: this.getRoomTitle(room), + name: RocketChat.getRoomTitle(room), subtitle: room.topic, avatar: room.name, t: room.t, @@ -291,8 +293,14 @@ class RoomView extends React.Component { this.setReadOnly(); } } + // If it's a livechat room + if (this.t === 'l') { + if (!isEqual(prevState.roomUpdate.visitor, roomUpdate.visitor)) { + navigation.setParams({ visitor: roomUpdate.visitor }); + } + } if (((roomUpdate.fname !== prevState.roomUpdate.fname) || (roomUpdate.name !== prevState.roomUpdate.name)) && !this.tmid) { - navigation.setParams({ name: this.getRoomTitle(room) }); + navigation.setParams({ name: RocketChat.getRoomTitle(room) }); } } @@ -456,7 +464,7 @@ class RoomView extends React.Component { this.setState({ room }); if (!this.tmid) { navigation.setParams({ - name: this.getRoomTitle(room), + name: RocketChat.getRoomTitle(room), subtitle: room.topic, avatar: room.name, t: room.t @@ -645,7 +653,7 @@ class RoomView extends React.Component { const { room } = this.state; if (rid === this.rid) { Navigation.navigate('RoomsListView'); - showErrorAlert(I18n.t('You_were_removed_from_channel', { channel: this.getRoomTitle(room) }), I18n.t('Oops')); + showErrorAlert(I18n.t('You_were_removed_from_channel', { channel: RocketChat.getRoomTitle(room) }), I18n.t('Oops')); } } @@ -667,11 +675,6 @@ class RoomView extends React.Component { }); }; - getRoomTitle = (room) => { - const { useRealName } = this.props; - return ((room.prid || useRealName) && room.fname) || room.name; - } - getMessages = () => { const { room } = this.state; if (room.lastOpen) { @@ -763,7 +766,7 @@ class RoomView extends React.Component { navigation.navigate('RoomActionsView', { rid: this.rid, t: this.t, room }); ModalNavigation.navigate('RoomInfoView', navParam); } else { - navigation.navigate('RoomInfoView', navParam); + navigation.push('RoomInfoView', navParam); } } @@ -815,6 +818,20 @@ class RoomView extends React.Component { } }); + closeBanner = async() => { + const { room } = this.state; + try { + const db = database.active; + await db.action(async() => { + await room.update((r) => { + r.bannerClosed = true; + }); + }); + } catch { + // do nothing + } + }; + renderItem = (item, previousItem) => { const { room, lastOpen, canAutoTranslate } = this.state; const { @@ -993,7 +1010,9 @@ class RoomView extends React.Component { const { user, baseUrl, theme, navigation, Hide_System_Messages } = this.props; - const { rid, t, sysMes } = room; + const { + rid, t, sysMes, bannerClosed, announcement + } = room; return ( <SafeAreaView @@ -1008,7 +1027,9 @@ class RoomView extends React.Component { <Banner rid={rid} title={I18n.t('Announcement')} - text={room.announcement} + text={announcement} + bannerClosed={bannerClosed} + closeBanner={this.closeBanner} theme={theme} /> <List diff --git a/app/views/RoomView/styles.js b/app/views/RoomView/styles.js index aedc55ad2..fdbb61a7b 100644 --- a/app/views/RoomView/styles.js +++ b/app/views/RoomView/styles.js @@ -28,8 +28,12 @@ export default StyleSheet.create({ bannerContainer: { paddingVertical: 12, paddingHorizontal: 15, + flexDirection: 'row', alignItems: 'center' }, + bannerText: { + flex: 1 + }, bannerModalTitle: { fontSize: 16, ...sharedStyles.textMedium diff --git a/app/views/RoomsListView/Header/Header.android.js b/app/views/RoomsListView/Header/Header.android.js index b40a9442d..33ba0b4b9 100644 --- a/app/views/RoomsListView/Header/Header.android.js +++ b/app/views/RoomsListView/Header/Header.android.js @@ -16,7 +16,8 @@ const styles = StyleSheet.create({ }, button: { flexDirection: 'row', - alignItems: 'center' + alignItems: 'center', + marginRight: 64 }, server: { fontSize: 20, @@ -68,7 +69,7 @@ const Header = React.memo(({ {connecting ? <Text style={[styles.updating, titleColorStyle]}>{I18n.t('Connecting')}</Text> : null} {isFetching ? <Text style={[styles.updating, titleColorStyle]}>{I18n.t('Updating')}</Text> : null} <View style={styles.button}> - <Text style={[styles.server, isFetching && styles.serverSmall, titleColorStyle]}>{serverName}</Text> + <Text style={[styles.server, isFetching && styles.serverSmall, titleColorStyle]} numberOfLines={1}>{serverName}</Text> <Image style={[ styles.disclosure, diff --git a/app/views/RoomsListView/Header/Header.ios.js b/app/views/RoomsListView/Header/Header.ios.js index 05bc65869..46fe0d9fa 100644 --- a/app/views/RoomsListView/Header/Header.ios.js +++ b/app/views/RoomsListView/Header/Header.ios.js @@ -56,11 +56,11 @@ const Header = React.memo(({ onPress={onPress} testID='rooms-list-header-server-dropdown-button' style={styles.container} - disabled={connecting || isFetching} + // disabled={connecting || isFetching} > <HeaderTitle connecting={connecting} isFetching={isFetching} theme={theme} /> <View style={styles.button}> - <Text style={[styles.server, { color: themes[theme].headerTintColor }]}>{serverName}</Text> + <Text style={[styles.server, { color: themes[theme].headerTintColor }]} numberOfLines={1}>{serverName}</Text> <Image style={[styles.disclosure, showServerDropdown && styles.upsideDown]} source={{ uri: 'disclosure_indicator_server' }} /> </View> </TouchableOpacity> */} diff --git a/app/views/RoomsListView/ListHeader/Sort.js b/app/views/RoomsListView/ListHeader/Sort.js index e0ca2d106..208dbc3b7 100644 --- a/app/views/RoomsListView/ListHeader/Sort.js +++ b/app/views/RoomsListView/ListHeader/Sort.js @@ -29,7 +29,7 @@ const Sort = React.memo(({ ]} > <Text style={[styles.sortToggleText, { color: themes[theme].auxiliaryText }]}>{I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}</Text> - <CustomIcon style={[styles.sortIcon, { color: themes[theme].auxiliaryText }]} size={22} name='sort1' /> + <CustomIcon style={[styles.sortIcon, { color: themes[theme].auxiliaryText }]} size={22} name='sort' /> </View> </Touch> ); diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js index f91c20bd8..f151f42d1 100644 --- a/app/views/RoomsListView/ServerDropdown.js +++ b/app/views/RoomsListView/ServerDropdown.js @@ -22,6 +22,9 @@ import { withTheme } from '../../theme'; import { KEY_COMMAND, handleCommandSelectServer } from '../../commands'; import { isTablet } from '../../utils/deviceInfo'; import { withSplit } from '../../split'; +import { localAuthenticate } from '../../utils/localAuthentication'; +import { showConfirmationAlert } from '../../utils/info'; +import LongPress from '../../utils/longPress'; const ROW_HEIGHT = 68; const ANIMATION_DURATION = 200; @@ -132,7 +135,6 @@ class ServerDropdown extends Component { const { server: currentServer, selectServerRequest, navigation, split } = this.props; - this.close(); if (currentServer !== server) { const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`); @@ -147,11 +149,25 @@ class ServerDropdown extends Component { }, ANIMATION_DURATION); }, ANIMATION_DURATION); } else { + await localAuthenticate(server); selectServerRequest(server); } } } + remove = server => showConfirmationAlert({ + message: I18n.t('This_will_remove_all_data_from_this_server'), + callToAction: I18n.t('Delete'), + onPress: async() => { + this.close(); + try { + await RocketChat.removeServer({ server }); + } catch { + // do nothing + } + } + }); + handleCommands = ({ event }) => { const { servers } = this.state; const { navigation } = this.props; @@ -173,35 +189,37 @@ class ServerDropdown extends Component { const { server, theme } = this.props; return ( - <Touch - onPress={() => this.select(item.id)} - testID={`rooms-list-header-server-${ item.id }`} - theme={theme} - > - <View style={styles.serverItemContainer}> - {item.iconURL - ? ( - <Image - source={{ uri: item.iconURL }} - defaultSource={{ uri: 'logo' }} - style={styles.serverIcon} - onError={() => console.warn('error loading serverIcon')} - /> - ) - : ( - <Image - source={{ uri: 'logo' }} - style={styles.serverIcon} - /> - ) - } - <View style={styles.serverTextContainer}> - <Text style={[styles.serverName, { color: themes[theme].titleText }]}>{item.name || item.id}</Text> - <Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>{item.id}</Text> + <LongPress onLongPress={() => (item.id === server || this.remove(item.id))}> + <Touch + onPress={() => this.select(item.id)} + testID={`rooms-list-header-server-${ item.id }`} + theme={theme} + > + <View style={styles.serverItemContainer}> + {item.iconURL + ? ( + <Image + source={{ uri: item.iconURL }} + defaultSource={{ uri: 'logo' }} + style={styles.serverIcon} + onError={() => console.warn('error loading serverIcon')} + /> + ) + : ( + <Image + source={{ uri: 'logo' }} + style={styles.serverIcon} + /> + ) + } + <View style={styles.serverTextContainer}> + <Text style={[styles.serverName, { color: themes[theme].titleText }]} numberOfLines={1}>{item.name || item.id}</Text> + <Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{item.id}</Text> + </View> + {item.id === server ? <Check theme={theme} /> : null} </View> - {item.id === server ? <Check theme={theme} /> : null} - </View> - </Touch> + </Touch> + </LongPress> ); } diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js index 3f68fef0e..d0f2f3386 100644 --- a/app/views/RoomsListView/SortDropdown/index.js +++ b/app/views/RoomsListView/SortDropdown/index.js @@ -138,7 +138,7 @@ class Sort extends PureComponent { <View style={[styles.dropdownContainerHeader, { borderColor: themes[theme].separatorColor }]}> <View style={styles.sortItemContainer}> <Text style={[styles.sortToggleText, { color: themes[theme].auxiliaryText }]}>{I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}</Text> - <CustomIcon style={[styles.sortIcon, { color: themes[theme].auxiliaryText }]} size={22} name='sort1' /> + <CustomIcon style={[styles.sortIcon, { color: themes[theme].auxiliaryText }]} size={22} name='sort' /> </View> </View> </Touch> @@ -161,7 +161,7 @@ class Sort extends PureComponent { <View style={[styles.sortSeparator, { backgroundColor: themes[theme].separatorColor }]} /> <SortItemButton onPress={this.toggleGroupByType} theme={theme}> <SortItemContent - icon='sort1' + icon='sort-amount-down' label='Group_by_type' checked={groupByType} theme={theme} diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 9cccdfc5b..15d00d1aa 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -61,6 +61,7 @@ import { import { MAX_SIDEBAR_WIDTH } from '../../constants/tablet'; import { withSplit } from '../../split'; import { getUserSelector } from '../../selectors/login'; +import { goRoom } from '../../utils/goRoom'; const SCROLL_OFFSET = 56; const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12; @@ -102,7 +103,6 @@ class RoomsListView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { const searching = navigation.getParam('searching'); const cancelSearch = navigation.getParam('cancelSearch', () => {}); - const onPressItem = navigation.getParam('onPressItem', () => {}); const initSearching = navigation.getParam( 'initSearching', () => {} @@ -137,9 +137,7 @@ class RoomsListView extends React.Component { <Item title='new' iconName='edit-rounded' - onPress={() => navigation.navigate('NewMessageView', { - onPressItem - })} + onPress={() => navigation.navigate('NewMessageView')} testID='rooms-list-view-create-channel' /> </CustomHeaderButtons> @@ -200,7 +198,6 @@ class RoomsListView extends React.Component { this.getSubscriptions(); const { navigation, closeServerDropdown } = this.props; navigation.setParams({ - onPressItem: this._onPressItem, initSearching: this.initSearching, cancelSearch: this.cancelSearch }); @@ -208,17 +205,21 @@ class RoomsListView extends React.Component { EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands); } Dimensions.addEventListener('change', this.onDimensionsChange); - Orientation.unlockAllOrientations(); this.willFocusListener = navigation.addListener('willFocus', () => { // Check if there were changes while not focused (it's set on sCU) if (this.shouldUpdate) { - // animateNextTransition(); this.forceUpdate(); this.shouldUpdate = false; } }); this.didFocusListener = navigation.addListener('didFocus', () => { + Orientation.unlockAllOrientations(); this.animated = true; + // Check if there were changes while not focused (it's set on sCU) + if (this.shouldUpdate) { + this.forceUpdate(); + this.shouldUpdate = false; + } this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); }); this.willBlurListener = navigation.addListener('willBlur', () => { @@ -231,7 +232,7 @@ class RoomsListView extends React.Component { console.timeEnd(`${ this.constructor.name } mount`); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { loadingServer, searchText, server } = this.props; if (nextProps.server && loadingServer !== nextProps.loadingServer) { @@ -417,7 +418,8 @@ class RoomsListView extends React.Component { type: item.t, prid: item.prid, uids: item.uids, - usernames: item.usernames + usernames: item.usernames, + visitor: item.visitor })); // unread @@ -533,43 +535,15 @@ class RoomsListView extends React.Component { getUidDirectMessage = room => RocketChat.getUidDirectMessage(room); - goRoom = (item) => { + onPressItem = (item = {}) => { const { navigation } = this.props; + if (!navigation.isFocused()) { + return; + } + this.cancelSearch(); this.item = item; - navigation.navigate('RoomView', { - rid: item.rid, - name: this.getRoomTitle(item), - t: item.t, - prid: item.prid, - room: item, - search: item.search, - roomUserId: this.getUidDirectMessage(item) - }); - } - - _onPressItem = async(item = {}) => { - if (!item.search) { - return this.goRoom(item); - } - if (item.t === 'd') { - // if user is using the search we need first to join/create room - try { - const { username } = item; - const result = await RocketChat.createDirectMessage(username); - if (result.success) { - return this.goRoom({ - rid: result.room._id, - name: username, - t: 'd' - }); - } - } catch (e) { - log(e); - } - } else { - return this.goRoom(item); - } + goRoom(item); }; toggleSort = () => { @@ -713,7 +687,7 @@ class RoomsListView extends React.Component { } else if (handleCommandNextRoom(event)) { this.goOtherRoom(1); } else if (handleCommandShowNewMessage(event)) { - navigation.navigate('NewMessageView', { onPressItem: this._onPressItem }); + navigation.navigate('NewMessageView'); } else if (handleCommandAddNewServer(event)) { navigation.navigate('NewServerView', { previousServer: server }); } @@ -799,7 +773,7 @@ class RoomsListView extends React.Component { baseUrl={server} prid={item.prid} showLastMessage={StoreLastMessage} - onPress={() => this._onPressItem(item)} + onPress={() => this.onPressItem(item)} testID={`rooms-list-view-item-${ item.name }`} width={split ? MAX_SIDEBAR_WIDTH : width} toggleFav={this.toggleFav} @@ -808,6 +782,7 @@ class RoomsListView extends React.Component { useRealName={useRealName} getUserPresence={this.getUserPresence} isGroupChat={isGroupChat} + visitor={item.visitor} /> ); }; diff --git a/app/views/ScreenLockConfigView.js b/app/views/ScreenLockConfigView.js new file mode 100644 index 000000000..1dfdc6fdc --- /dev/null +++ b/app/views/ScreenLockConfigView.js @@ -0,0 +1,316 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { StyleSheet, Switch, ScrollView } from 'react-native'; +import { SafeAreaView } from 'react-navigation'; +import { connect } from 'react-redux'; + +import I18n from '../i18n'; +import { themedHeader } from '../utils/navigation'; +import { withTheme } from '../theme'; +import { themes, SWITCH_TRACK_COLOR } from '../constants/colors'; +import sharedStyles from './Styles'; +import StatusBar from '../containers/StatusBar'; +import Separator from '../containers/Separator'; +import ListItem from '../containers/ListItem'; +import ItemInfo from '../containers/ItemInfo'; +import { CustomIcon } from '../lib/Icons'; +import database from '../lib/database'; +import { supportedBiometryLabel, changePasscode, checkHasPasscode } from '../utils/localAuthentication'; +import { DisclosureImage } from '../containers/DisclosureIndicator'; +import { DEFAULT_AUTO_LOCK } from '../constants/localAuthentication'; + +const styles = StyleSheet.create({ + listPadding: { + paddingVertical: 36 + }, + emptySpace: { + marginTop: 36 + } +}); + +const DEFAULT_BIOMETRY = false; + +class ScreenLockConfigView extends React.Component { + static navigationOptions = ({ screenProps }) => ({ + title: I18n.t('Screen_lock'), + ...themedHeader(screenProps.theme) + }) + + static propTypes = { + theme: PropTypes.string, + server: PropTypes.string, + Force_Screen_Lock: PropTypes.string, + Force_Screen_Lock_After: PropTypes.string + } + + constructor(props) { + super(props); + this.state = { + autoLock: false, + autoLockTime: null, + biometry: DEFAULT_BIOMETRY, + biometryLabel: null + }; + this.init(); + } + + componentWillUnmount() { + if (this.observable && this.observable.unsubscribe) { + this.observable.unsubscribe(); + } + } + + defaultAutoLockOptions = [ + { + title: I18n.t('Local_authentication_auto_lock_60'), + value: 60 + }, + { + title: I18n.t('Local_authentication_auto_lock_300'), + value: 300 + }, + { + title: I18n.t('Local_authentication_auto_lock_900'), + value: 900 + }, + { + title: I18n.t('Local_authentication_auto_lock_1800'), + value: 1800 + }, + { + title: I18n.t('Local_authentication_auto_lock_3600'), + value: 3600 + } + ]; + + init = async() => { + const { server } = this.props; + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + try { + this.serverRecord = await serversCollection.find(server); + this.setState({ + autoLock: this.serverRecord?.autoLock, + autoLockTime: this.serverRecord?.autoLockTime === null ? DEFAULT_AUTO_LOCK : this.serverRecord?.autoLockTime, + biometry: this.serverRecord.biometry === null ? DEFAULT_BIOMETRY : this.serverRecord.biometry + }); + } catch (error) { + // Do nothing + } + + const biometryLabel = await supportedBiometryLabel(); + this.setState({ biometryLabel }); + + this.observe(); + } + + /* + * We should observe biometry value + * because it can be changed by PasscodeChange + * when the user set his first passcode + */ + observe = () => { + this.observable = this.serverRecord?.observe()?.subscribe(({ biometry }) => { + this.setState({ biometry }); + }); + } + + save = async() => { + const { autoLock, autoLockTime, biometry } = this.state; + const serversDB = database.servers; + await serversDB.action(async() => { + await this.serverRecord?.update((record) => { + record.autoLock = autoLock; + record.autoLockTime = autoLockTime === null ? DEFAULT_AUTO_LOCK : autoLockTime; + record.biometry = biometry === null ? DEFAULT_BIOMETRY : biometry; + }); + }); + } + + changePasscode = async({ force }) => { + await changePasscode({ force }); + } + + toggleAutoLock = () => { + this.setState(({ autoLock }) => ({ autoLock: !autoLock, autoLockTime: DEFAULT_AUTO_LOCK }), async() => { + const { autoLock } = this.state; + if (autoLock) { + try { + await checkHasPasscode({ force: false, serverRecord: this.serverRecord }); + } catch { + this.toggleAutoLock(); + } + } + this.save(); + }); + } + + toggleBiometry = () => { + this.setState(({ biometry }) => ({ biometry: !biometry }), () => this.save()); + } + + isSelected = (value) => { + const { autoLockTime } = this.state; + return autoLockTime === value; + } + + changeAutoLockTime = (autoLockTime) => { + this.setState({ autoLockTime }, () => this.save()); + } + + renderSeparator = () => { + const { theme } = this.props; + return <Separator theme={theme} />; + } + + renderIcon = () => { + const { theme } = this.props; + return <CustomIcon name='check' size={20} color={themes[theme].tintColor} />; + } + + renderItem = ({ item }) => { + const { theme } = this.props; + const { title, value, disabled } = item; + return ( + <> + <ListItem + title={title} + onPress={() => this.changeAutoLockTime(value)} + right={this.isSelected(value) ? this.renderIcon : null} + theme={theme} + disabled={disabled} + /> + <Separator theme={theme} /> + </> + ); + } + + renderAutoLockSwitch = () => { + const { autoLock } = this.state; + const { Force_Screen_Lock } = this.props; + return ( + <Switch + value={autoLock} + trackColor={SWITCH_TRACK_COLOR} + onValueChange={this.toggleAutoLock} + disabled={Force_Screen_Lock} + /> + ); + } + + renderBiometrySwitch = () => { + const { biometry } = this.state; + return ( + <Switch + value={biometry} + trackColor={SWITCH_TRACK_COLOR} + onValueChange={this.toggleBiometry} + /> + ); + } + + renderAutoLockItems = () => { + const { autoLock, autoLockTime } = this.state; + const { theme, Force_Screen_Lock_After, Force_Screen_Lock } = this.props; + if (!autoLock) { + return null; + } + let items = this.defaultAutoLockOptions; + if (Force_Screen_Lock && Force_Screen_Lock_After > 0) { + items = [{ + title: I18n.t('After_seconds_set_by_admin', { seconds: Force_Screen_Lock_After }), + value: Force_Screen_Lock_After, + disabled: true + }]; + // if Force_Screen_Lock is disabled and autoLockTime is a value that isn't on our defaultOptions we'll show it + } else if (Force_Screen_Lock_After === autoLockTime && !items.find(item => item.value === autoLockTime)) { + items.push({ + title: I18n.t('After_seconds_set_by_admin', { seconds: Force_Screen_Lock_After }), + value: Force_Screen_Lock_After + }); + } + return ( + <> + <Separator style={styles.emptySpace} theme={theme} /> + {items.map(item => this.renderItem({ item }))} + </> + ); + } + + renderDisclosure = () => { + const { theme } = this.props; + return <DisclosureImage theme={theme} />; + } + + renderBiometry = () => { + const { autoLock, biometryLabel } = this.state; + const { theme } = this.props; + if (!autoLock || !biometryLabel) { + return null; + } + return ( + <> + <Separator theme={theme} /> + <ListItem + title={I18n.t('Local_authentication_unlock_with_label', { label: biometryLabel })} + right={() => this.renderBiometrySwitch()} + theme={theme} + /> + <Separator theme={theme} /> + </> + ); + } + + render() { + const { autoLock } = this.state; + const { theme } = this.props; + return ( + <SafeAreaView + style={[sharedStyles.container, { backgroundColor: themes[theme].auxiliaryBackground }]} + forceInset={{ vertical: 'never' }} + > + <StatusBar theme={theme} /> + <ScrollView + keyExtractor={item => item.value} + contentContainerStyle={styles.listPadding} + > + <Separator theme={theme} /> + <ListItem + title={I18n.t('Local_authentication_unlock_option')} + right={() => this.renderAutoLockSwitch()} + theme={theme} + /> + {autoLock + ? ( + <> + <Separator theme={theme} /> + <ListItem + title={I18n.t('Local_authentication_change_passcode')} + theme={theme} + right={this.renderDisclosure} + onPress={this.changePasscode} + /> + </> + ) + : null + } + <Separator theme={theme} /> + <ItemInfo + info={I18n.t('Local_authentication_info')} + theme={theme} + /> + {this.renderBiometry()} + {this.renderAutoLockItems()} + </ScrollView> + </SafeAreaView> + ); + } +} + +const mapStateToProps = state => ({ + server: state.server.server, + Force_Screen_Lock: state.settings.Force_Screen_Lock, + Force_Screen_Lock_After: state.settings.Force_Screen_Lock_After +}); + +export default connect(mapStateToProps)(withTheme(ScreenLockConfigView)); diff --git a/app/views/ScreenLockedView.js b/app/views/ScreenLockedView.js new file mode 100644 index 000000000..6ff04b7fd --- /dev/null +++ b/app/views/ScreenLockedView.js @@ -0,0 +1,69 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import Modal from 'react-native-modal'; +import useDeepCompareEffect from 'use-deep-compare-effect'; +import _ from 'lodash'; +import Orientation from 'react-native-orientation-locker'; + +import { withTheme } from '../theme'; +import EventEmitter from '../utils/events'; +import { LOCAL_AUTHENTICATE_EMITTER } from '../constants/localAuthentication'; +import { isTablet } from '../utils/deviceInfo'; +import { PasscodeEnter } from '../containers/Passcode'; + +const ScreenLockedView = ({ theme }) => { + const [visible, setVisible] = useState(false); + const [data, setData] = useState({}); + + useDeepCompareEffect(() => { + if (!_.isEmpty(data)) { + setVisible(true); + } else { + setVisible(false); + } + }, [data]); + + const showScreenLock = (args) => { + setData(args); + }; + + useEffect(() => { + if (!isTablet) { + Orientation.lockToPortrait(); + } + EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock); + return (() => { + if (!isTablet) { + Orientation.unlockAllOrientations(); + } + EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER); + }); + }, []); + + const onSubmit = () => { + const { submit } = data; + if (submit) { + submit(); + } + setData({}); + }; + + return ( + <Modal + useNativeDriver + isVisible={visible} + hideModalContentWhileAnimating + style={{ margin: 0 }} + animationIn='fadeIn' + animationOut='fadeOut' + > + <PasscodeEnter theme={theme} hasBiometry={data?.hasBiometry} finishProcess={onSubmit} /> + </Modal> + ); +}; + +ScreenLockedView.propTypes = { + theme: PropTypes.string +}; + +export default withTheme(ScreenLockedView); diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js index 8c8457816..856d83713 100644 --- a/app/views/SearchMessagesView/index.js +++ b/app/views/SearchMessagesView/index.js @@ -11,7 +11,7 @@ import styles from './styles'; import Markdown from '../../containers/markdown'; import debounce from '../../utils/debounce'; import RocketChat from '../../lib/rocketchat'; -import Message from '../../containers/message/Message'; +import Message from '../../containers/message'; import scrollPersistTaps from '../../utils/scrollPersistTaps'; import I18n from '../../i18n'; import StatusBar from '../../containers/StatusBar'; @@ -115,14 +115,10 @@ class SearchMessagesView extends React.Component { const { user, baseUrl, theme } = this.props; return ( <Message + item={item} baseUrl={baseUrl} user={user} - author={item.u} - ts={item.ts} - msg={item.msg} - attachments={item.attachments || []} timeFormat='MMM Do YYYY, h:mm:ss a' - isEdited={!!item.editedAt} isHeader showAttachment={() => {}} getCustomEmoji={this.getCustomEmoji} diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 5df529332..2fda8de9b 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -1,10 +1,11 @@ import React from 'react'; import { - View, Linking, ScrollView, AsyncStorage, Switch, Text, Share, Clipboard + View, Linking, ScrollView, Switch, Share, Clipboard } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { SafeAreaView } from 'react-navigation'; +import AsyncStorage from '@react-native-community/async-storage'; import { logout as logoutAction } from '../../actions/login'; import { selectServerRequest as selectServerRequestAction } from '../../actions/server'; @@ -13,6 +14,7 @@ import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors'; import { DrawerButton, CloseModalButton } from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; import ListItem from '../../containers/ListItem'; +import ItemInfo from '../../containers/ItemInfo'; import { DisclosureImage } from '../../containers/DisclosureIndicator'; import Separator from '../../containers/Separator'; import I18n from '../../i18n'; @@ -53,16 +55,6 @@ SectionSeparator.propTypes = { theme: PropTypes.string }; -const ItemInfo = React.memo(({ info, theme }) => ( - <View style={[styles.infoContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}> - <Text style={[styles.infoText, { color: themes[theme].infoText }]}>{info}</Text> - </View> -)); -ItemInfo.propTypes = { - info: PropTypes.string, - theme: PropTypes.string -}; - class SettingsView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => ({ ...themedHeader(screenProps.theme), @@ -273,6 +265,14 @@ class SettingsView extends React.Component { right={this.renderDisclosure} theme={theme} /> + <Separator theme={theme} /> + <ListItem + title={I18n.t('Screen_lock')} + showActionIndicator + onPress={() => this.navigateToScreen('ScreenLockConfigView')} + right={this.renderDisclosure} + theme={theme} + /> <SectionSeparator theme={theme} /> diff --git a/app/views/SettingsView/styles.js b/app/views/SettingsView/styles.js index d1eb323c3..2bf7cdaeb 100644 --- a/app/views/SettingsView/styles.js +++ b/app/views/SettingsView/styles.js @@ -9,12 +9,5 @@ export default StyleSheet.create({ }, listPadding: { paddingVertical: 36 - }, - infoContainer: { - padding: 15 - }, - infoText: { - fontSize: 14, - ...sharedStyles.textRegular } }); diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js index aba625e9a..44b6d309b 100644 --- a/app/views/ShareListView/index.js +++ b/app/views/ShareListView/index.js @@ -149,7 +149,7 @@ class ShareListView extends React.Component { }, 500); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { server } = this.props; if (nextProps.server !== server) { this.getSubscriptions(nextProps.server); diff --git a/app/views/SidebarView/SidebarItem.js b/app/views/SidebarView/SidebarItem.js index 0b2b8d2bc..7959438c2 100644 --- a/app/views/SidebarView/SidebarItem.js +++ b/app/views/SidebarView/SidebarItem.js @@ -21,7 +21,7 @@ const Item = React.memo(({ {left} </View> <View style={styles.itemCenter}> - <Text style={[styles.itemText, { color: themes[theme].titleText }]}> + <Text style={[styles.itemText, { color: themes[theme].titleText }]} numberOfLines={1}> {text} </Text> </View> diff --git a/app/views/SidebarView/index.js b/app/views/SidebarView/index.js index cad318486..bee708251 100644 --- a/app/views/SidebarView/index.js +++ b/app/views/SidebarView/index.js @@ -59,7 +59,7 @@ class Sidebar extends Component { this.setIsAdmin(); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { const { loadingServer } = this.props; if (loadingServer && nextProps.loadingServer !== loadingServer) { this.setIsAdmin(); @@ -224,7 +224,12 @@ class Sidebar extends Component { <View style={styles.headerUsername}> <Text numberOfLines={1} style={[styles.username, { color: themes[theme].titleText }]}>{useRealName ? user.name : user.username}</Text> </View> - <Text style={[styles.currentServerText, { color: themes[theme].titleText }]} numberOfLines={1}>{Site_Name}</Text> + <Text + style={[styles.currentServerText, { color: themes[theme].titleText }]} + numberOfLines={1} + accessibilityLabel={`Connected to ${ baseUrl }`} + >{Site_Name} + </Text> </View> </View> diff --git a/app/views/VisitorNavigationView.js b/app/views/VisitorNavigationView.js new file mode 100644 index 000000000..4cf4f66e2 --- /dev/null +++ b/app/views/VisitorNavigationView.js @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from 'react'; +import { FlatList, StyleSheet, Text } from 'react-native'; +import PropTypes from 'prop-types'; + +import { withTheme } from '../theme'; +import RocketChat from '../lib/rocketchat'; +import { themes } from '../constants/colors'; +import Separator from '../containers/Separator'; +import openLink from '../utils/openLink'; +import I18n from '../i18n'; +import debounce from '../utils/debounce'; +import sharedStyles from './Styles'; +import ListItem from '../containers/ListItem'; + +const styles = StyleSheet.create({ + noResult: { + fontSize: 16, + paddingVertical: 56, + ...sharedStyles.textAlignCenter, + ...sharedStyles.textSemibold + }, + withoutBorder: { + borderBottomWidth: 0, + borderTopWidth: 0 + } +}); + +const Item = ({ item, theme }) => ( + <ListItem + title={item.navigation?.page?.title || I18n.t('Empty_title')} + onPress={() => openLink(item.navigation?.page?.location?.href)} + theme={theme} + /> +); +Item.propTypes = { + item: PropTypes.object, + theme: PropTypes.string +}; + +const VisitorNavigationView = ({ navigation, theme }) => { + let offset; + let total = 0; + const [pages, setPages] = useState([]); + + const getPages = async() => { + const rid = navigation.getParam('rid'); + if (rid) { + try { + const result = await RocketChat.getPagesLivechat(rid, offset); + if (result.success) { + setPages(result.pages); + offset = result.pages.length; + ({ total } = result); + } + } catch { + // do nothig + } + } + }; + + useEffect(() => { getPages(); }, []); + + const onEndReached = debounce(() => { + if (pages.length <= total) { + getPages(); + } + }, 300); + + return ( + <FlatList + data={pages} + renderItem={({ item }) => <Item item={item} theme={theme} />} + ItemSeparatorComponent={() => <Separator theme={theme} />} + contentContainerStyle={[ + sharedStyles.listContentContainer, + { + backgroundColor: themes[theme].auxiliaryBackground, + borderColor: themes[theme].separatorColor + }, + !pages.length && styles.withoutBorder + ]} + style={{ backgroundColor: themes[theme].auxiliaryBackground }} + ListEmptyComponent={() => <Text style={[styles.noResult, { color: themes[theme].titleText }]}>{I18n.t('No_results_found')}</Text>} + keyExtractor={item => item} + onEndReached={onEndReached} + onEndReachedThreshold={5} + /> + ); +}; +VisitorNavigationView.propTypes = { + theme: PropTypes.string, + navigation: PropTypes.object +}; +VisitorNavigationView.navigationOptions = { + title: I18n.t('Navigation_history') +}; + +export default withTheme(VisitorNavigationView); diff --git a/app/views/WorkspaceView/index.js b/app/views/WorkspaceView/index.js index 512ff107c..3a8770562 100644 --- a/app/views/WorkspaceView/index.js +++ b/app/views/WorkspaceView/index.js @@ -46,7 +46,7 @@ class WorkspaceView extends React.Component { theme, Site_Name, Site_Url, Assets_favicon_512, server, registrationEnabled, registrationText, showLoginButton } = this.props; return ( - <FormContainer theme={theme}> + <FormContainer theme={theme} testID='workspace-view'> <FormContainerInner> <View style={styles.alignItemsCenter}> <ServerAvatar theme={theme} url={server} image={Assets_favicon_512 && Assets_favicon_512.defaultUrl} /> @@ -60,6 +60,7 @@ class WorkspaceView extends React.Component { type='primary' onPress={this.login} theme={theme} + testID='workspace-view-login' /> ) : null} { @@ -70,6 +71,7 @@ class WorkspaceView extends React.Component { backgroundColor={themes[theme].chatComponentBackground} onPress={this.register} theme={theme} + testID='workspace-view-register' /> ) : ( <Text style={[styles.registrationText, { color: themes[theme].auxiliaryText }]}>{registrationText}</Text> diff --git a/e2e/00-onboarding.spec.js b/e2e/00-onboarding.spec.js deleted file mode 100644 index 8746a49a5..000000000 --- a/e2e/00-onboarding.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -const { - device, expect, element, by, waitFor -} = require('detox'); -const data = require('./data'); - -describe('Onboarding', () => { - before(async() => { - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - }); - - describe('Render', async() => { - it('should have onboarding screen', async() => { - await expect(element(by.id('onboarding-view'))).toBeVisible(); - }); - - it('should have "Connect to a server"', async() => { - await expect(element(by.id('connect-server-button'))).toBeVisible(); - }); - - it('should have "Join the community"', async() => { - await expect(element(by.id('join-community-button'))).toBeVisible(); - }); - - it('should have "Create a new workspace"', async() => { - await expect(element(by.id('create-workspace-button'))).toBeVisible(); - }); - }); - - describe('Usage', async() => { - it('should navigate to create new workspace', async() => { - // webviews are not supported by detox: https://github.com/wix/detox/issues/136#issuecomment-306591554 - }); - - it('should navigate to join community', async() => { - await element(by.id('join-community-button')).tap(); - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('welcome-view'))).toBeVisible(); - // await waitFor(element(by.text('Rocket.Chat'))).toBeVisible().withTimeout(60000); - // await expect(element(by.text('Rocket.Chat'))).toBeVisible(); - }); - - it('should navigate to new server', async() => { - await device.launchApp({ newInstance: true }); - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - await element(by.id('connect-server-button')).tap(); - await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('new-server-view'))).toBeVisible(); - }); - - it('should enter an invalid server and get error', async() => { - await element(by.id('new-server-view-input')).replaceText('invalidtest'); - await element(by.id('new-server-view-button')).tap(); - const errorText = 'Oops!'; - await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000); - await expect(element(by.text(errorText))).toBeVisible(); - }); - - it('should enter a valid server with login services and navigate to welcome', async() => { - await element(by.text('OK')).tap(); - await element(by.id('new-server-view-input')).replaceText('open'); - await element(by.id('new-server-view-button')).tap(); - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('welcome-view'))).toBeVisible(); - }); - - it('should enter a valid server without login services and navigate to login', async() => { - await device.launchApp({ newInstance: true }); - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - await element(by.id('connect-server-button')).tap(); - await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await element(by.id('new-server-view-input')).replaceText(data.server); - await element(by.id('new-server-view-button')).tap(); - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('login-view'))).toBeVisible(); - }); - }); -}); diff --git a/e2e/01-welcome.spec.js b/e2e/01-welcome.spec.js deleted file mode 100644 index c78cd09bc..000000000 --- a/e2e/01-welcome.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -const { - device, expect, element, by, waitFor -} = require('detox'); -const { tapBack } = require('./helpers/app'); - -describe('Welcome screen', () => { - before(async() => { - await device.launchApp({ newInstance: true }); - await element(by.id('join-community-button')).tap(); - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000); - }) - - describe('Render', async() => { - it('should have welcome screen', async() => { - await expect(element(by.id('welcome-view'))).toBeVisible(); - }); - - it('should have register button', async() => { - await expect(element(by.id('welcome-view-register'))).toBeVisible(); - }); - - it('should have login button', async() => { - await expect(element(by.id('welcome-view-login'))).toBeVisible(); - }); - - // TODO: oauth - }); - - describe('Usage', async() => { - it('should navigate to login', async() => { - await element(by.id('welcome-view-login')).tap(); - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('login-view'))).toBeVisible(); - }); - - it('should navigate to register', async() => { - await tapBack(); - await element(by.id('welcome-view-register')).tap(); - await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); - }); - - it('should navigate to legal', async() => { - await tapBack(); - await element(by.id('welcome-view-more')).tap(); - await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('legal-view'))).toBeVisible(); - }); - }); -}); diff --git a/e2e/02-legal.spec.js b/e2e/02-legal.spec.js deleted file mode 100644 index bd5f19183..000000000 --- a/e2e/02-legal.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -const { - device, expect, element, by, waitFor -} = require('detox'); -const { tapBack } = require('./helpers/app'); - -describe('Legal screen', () => { - before(async() => { - await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('legal-view'))).toBeVisible(); - }) - - describe('Render', async() => { - it('should have legal screen', async() => { - await expect(element(by.id('legal-view'))).toBeVisible(); - }); - - it('should have terms of service button', async() => { - await expect(element(by.id('legal-terms-button'))).toBeVisible(); - }); - - it('should have privacy policy button', async() => { - await expect(element(by.id('legal-privacy-button'))).toBeVisible(); - }); - }); - - describe('Usage', async() => { - // We can't simulate how webview behaves, so I had to disable :( - // it('should navigate to terms', async() => { - // await element(by.id('legal-terms-button')).tap(); - // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('terms-view'))).toBeVisible(); - // }); - - // it('should navigate to privacy', async() => { - // await tapBack(); - // await element(by.id('legal-privacy-button')).tap(); - // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('privacy-view'))).toBeVisible(); - // }); - - it('should navigate to welcome', async() => { - await tapBack(); - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('welcome-view'))).toBeVisible(); - }); - }); -}); diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js index 162f22204..5c5f646aa 100644 --- a/e2e/helpers/app.js +++ b/e2e/helpers/app.js @@ -3,27 +3,28 @@ const { } = require('detox'); const data = require('../data'); -async function addServer() { +async function navigateToWorkspace() { await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - await element(by.id('connect-server-button')).tap(); - await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('new-server-view'))).toBeVisible(); - await element(by.id('new-server-view-input')).replaceText(data.server); - await element(by.id('new-server-view-button')).tap(); + await element(by.id('join-workspace')).tap(); + await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); + await element(by.id('new-server-view-input')).replaceText(data.server); + await element(by.id('new-server-view-button')).tap(); + await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('workspace-view'))).toBeVisible(); } async function navigateToLogin() { - await addServer(); - try { - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('login-view'))).toBeVisible(); - } catch (error) { - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('welcome-view'))).toBeVisible(); - await element(by.id('welcome-view-login')).tap(); - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('login-view'))).toBeVisible(); - } + await navigateToWorkspace(); + await element(by.id('workspace-view-login')).tap(); + await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('login-view'))).toBeVisible(); +} + +async function navigateToRegister() { + await navigateToWorkspace(); + await element(by.id('workspace-view-register')).tap(); + await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('register-view'))).toBeVisible(); } async function login() { @@ -51,6 +52,18 @@ async function logout() { await expect(element(by.id('onboarding-view'))).toBeVisible(); } +async function createUser() { + await navigateToRegister(); + await element(by.id('register-view-name')).replaceText(data.user); + await element(by.id('register-view-username')).replaceText(data.user); + await element(by.id('register-view-email')).replaceText(data.email); + await element(by.id('register-view-password')).replaceText(data.password); + await sleep(300); + await element(by.id('register-view-submit')).tap(); + await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('rooms-list-view'))).toBeVisible(); +} + async function tapBack() { await element(by.id('header-back')).atIndex(0).tap(); } @@ -60,10 +73,12 @@ async function sleep(ms) { } module.exports = { - addServer, + navigateToWorkspace, navigateToLogin, + navigateToRegister, login, logout, + createUser, tapBack, sleep }; \ No newline at end of file diff --git a/e2e/11-changeserver.spec.js b/e2e/tests/assorted/01-changeserver.spec.js similarity index 56% rename from e2e/11-changeserver.spec.js rename to e2e/tests/assorted/01-changeserver.spec.js index 8d20318ff..e42d25e40 100644 --- a/e2e/11-changeserver.spec.js +++ b/e2e/tests/assorted/01-changeserver.spec.js @@ -1,15 +1,28 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { sleep, logout } = require('./helpers/app'); +const data = require('../../data'); +const { sleep, createUser } = require('../../helpers/app'); + +const checkServer = async(server) => { + const label = `Connected to ${ server }`; + await element(by.id('rooms-list-view-sidebar')).tap(); + await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); + await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000); + await expect(element(by.label(label))).toBeVisible(); + await element(by.type('UIScrollView')).atIndex(1).swipe('left'); // close sidebar +} describe('Change server', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await createUser(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); }); + it('should be logged in main server', async() => { + await checkServer(data.server); + }) + it('should add server and create new user', async() => { await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); @@ -17,29 +30,14 @@ describe('Change server', () => { await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); await sleep(1000); await element(by.id('rooms-list-header-server-add')).tap(); - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(60000); - await sleep(1000); - await element(by.id('connect-server-button')).tap(); - // Add server + + // TODO: refactor await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); await element(by.id('new-server-view-input')).replaceText(data.alternateServer); - await sleep(1000); await element(by.id('new-server-view-button')).tap(); - // Navigate to register - // await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000); - // await element(by.id('welcome-view-register')).tap(); - // await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - try { - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('login-view'))).toBeVisible(); - await sleep(1000); - await element(by.id('login-view-register')).tap(); - } catch (error) { - await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('welcome-view'))).toBeVisible(); - await sleep(1000); - await element(by.id('welcome-view-register')).tap(); - } + await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('workspace-view'))).toBeVisible(); + await element(by.id('workspace-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); await expect(element(by.id('register-view'))).toBeVisible(); // Register new user @@ -51,13 +49,15 @@ describe('Change server', () => { await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); await expect(element(by.id('rooms-list-view'))).toBeVisible(); + // For a sanity test, to make sure roomslist is showing correct rooms // app CANNOT show public room created on previous tests - await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible(); + // await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible().withTimeout(60000); + // await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible(); + await checkServer(data.alternateServer); }); - it('should change server', async() => { + it('should change back', async() => { await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); @@ -65,9 +65,6 @@ describe('Change server', () => { await sleep(1000); await element(by.id(`rooms-list-header-server-${ data.server }`)).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); - // For a sanity test, to make sure roomslist is showing correct rooms - // app MUST show public room created on previous tests - await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible(); + await checkServer(data.server); }); }); diff --git a/e2e/12-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js similarity index 63% rename from e2e/12-broadcast.spec.js rename to e2e/tests/assorted/02-broadcast.spec.js index 50572116c..cd3c95314 100644 --- a/e2e/12-broadcast.spec.js +++ b/e2e/tests/assorted/02-broadcast.spec.js @@ -3,39 +3,14 @@ const { } = require('detox'); const OTP = require('otp.js'); const GA = OTP.googleAuthenticator; -const { navigateToLogin, login, tapBack, sleep } = require('./helpers/app'); -const data = require('./data'); - -const logout = async() => { - // previous tests added alternate server to the device - // so logout will only remove this server data and select alternate server - await element(by.id('rooms-list-view-sidebar')).tap(); - await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); - await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000); - await element(by.id('sidebar-settings')).tap(); - await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000); - await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom'); - await element(by.id('settings-logout')).tap(); - const logoutAlertMessage = 'You will be logged out of this application.'; - await waitFor(element(by.text(logoutAlertMessage)).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text(logoutAlertMessage)).atIndex(0)).toExist(); - await element(by.text('Logout')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await sleep(5000); -} - -const localNavigateToLogin = async() => { - await element(by.id('rooms-list-header-server-dropdown-button')).tap(); - await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); - await sleep(1000); - await element(by.id('rooms-list-header-server-add')).tap(); - await navigateToLogin(); -} +const { navigateToLogin, login, tapBack, sleep, createUser } = require('../../helpers/app'); +const data = require('../../data'); describe('Broadcast room', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(); }); it('should create broadcast room', async() => { @@ -43,6 +18,9 @@ describe('Broadcast room', () => { await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000); + await element(by.id('select-users-view-search')).replaceText(data.alternateUser); + await waitFor(element(by.id(`select-users-view-item-${ data.alternateUser }`))).toBeVisible().withTimeout(60000); + await expect(element(by.id(`select-users-view-item-${ data.alternateUser }`))).toBeVisible(); await element(by.id(`select-users-view-item-${ data.alternateUser }`)).tap(); await waitFor(element(by.id(`selected-user-${ data.alternateUser }`))).toBeVisible().withTimeout(5000); await sleep(1000); @@ -62,44 +40,36 @@ describe('Broadcast room', () => { await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await element(by.id('room-actions-info')).tap(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.label('Broadcast Channel'))).toBeVisible(); + await expect(element(by.label('Broadcast Channel').withAncestor(by.id('room-info-view-broadcast')))).toBeVisible(); await tapBack(); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000); await tapBack(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000); - // await tapBack(); - // await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - // await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000); - // await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist(); }); it('should send message', async() => { - // await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(`${ data.random }message`); await element(by.id('messagebox-send-message')).tap(); - // await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000); - // await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible(); - await sleep(5000); + await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toExist().withTimeout(60000); + await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible(); await tapBack(); }); it('should login as user without write message authorization and enter room', async() => { - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); - await logout(); - await localNavigateToLogin(); - - // 2FA login in stable:detox + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); await element(by.id('login-view-email')).replaceText(data.alternateUser); await element(by.id('login-view-password')).replaceText(data.alternateUserPassword); - await sleep(2000); + await sleep(1000); await element(by.id('login-view-submit')).tap(); + await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000); + await expect(element(by.id('two-factor'))).toBeVisible(); const code = GA.gen(data.alternateUserTOTPSecret); - await element(by.id('login-view-totp')).replaceText(code); - await sleep(2000); - await element(by.id('login-view-submit')).tap(); + await element(by.id('two-factor-input')).replaceText(code); + await sleep(1000); + await element(by.id('two-factor-send')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await element(by.type('UIScrollView')).atIndex(1).scrollTo('top'); await element(by.id('rooms-list-view-search')).typeText(`broadcast${ data.random }`); @@ -140,19 +110,8 @@ describe('Broadcast room', () => { it('should reply broadcasted message', async() => { await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(`${ data.random }broadcastreply`); - await sleep(1000); await element(by.id('messagebox-send-message')).tap(); - await sleep(1000); await waitFor(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible().withTimeout(60000); await expect(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible(); }); - - after(async() => { - // log back as main test user and left screen on RoomsListView - await tapBack(); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await logout(); - await localNavigateToLogin(); - await login(); - }) }); diff --git a/e2e/13-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js similarity index 85% rename from e2e/13-profile.spec.js rename to e2e/tests/assorted/03-profile.spec.js index 2447aa57f..122422aa2 100644 --- a/e2e/13-profile.spec.js +++ b/e2e/tests/assorted/03-profile.spec.js @@ -1,8 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { logout, navigateToLogin, login, sleep } = require('./helpers/app'); -const data = require('./data'); +const { logout, navigateToLogin, login, sleep } = require('../../helpers/app'); +const data = require('../../data'); const scrollDown = 200; @@ -16,13 +16,15 @@ async function waitForToast() { describe('Profile screen', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(); await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('sidebar-profile'))).toBeVisible(); + await expect(element(by.id('sidebar-profile'))).toBeVisible(); await element(by.id('sidebar-profile')).tap(); await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000); - }); describe('Render', async() => { @@ -34,10 +36,6 @@ describe('Profile screen', () => { await expect(element(by.id('profile-view-avatar')).atIndex(0)).toExist(); }); - it('should have custom status', async() => { - await expect(element(by.id('profile-view-custom-status'))).toExist(); - }); - it('should have name', async() => { await expect(element(by.id('profile-view-name'))).toExist(); }); @@ -80,16 +78,6 @@ describe('Profile screen', () => { }); describe('Usage', async() => { - it('should change custom status', async() => { - await element(by.type('UIScrollView')).atIndex(1).swipe('down'); - await element(by.id('profile-view-custom-status')).replaceText(`${ data.user }new`); - await sleep(1000); - await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); - await element(by.id('profile-view-submit')).tap(); - await waitForToast(); - }); - it('should change name and username', async() => { await element(by.type('UIScrollView')).atIndex(1).swipe('down'); await element(by.id('profile-view-name')).replaceText(`${ data.user }new`); diff --git a/e2e/14-setting.spec.js b/e2e/tests/assorted/04-setting.spec.js similarity index 97% rename from e2e/14-setting.spec.js rename to e2e/tests/assorted/04-setting.spec.js index 266f6d672..65a80977a 100644 --- a/e2e/14-setting.spec.js +++ b/e2e/tests/assorted/04-setting.spec.js @@ -1,7 +1,7 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { logout, navigateToLogin, login } = require('./helpers/app'); +const { logout, navigateToLogin, login } = require('../../helpers/app'); describe('Settings screen', () => { before(async() => { diff --git a/e2e/15-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js similarity index 98% rename from e2e/15-joinpublicroom.spec.js rename to e2e/tests/assorted/05-joinpublicroom.spec.js index 258c61941..1e284db58 100644 --- a/e2e/15-joinpublicroom.spec.js +++ b/e2e/tests/assorted/05-joinpublicroom.spec.js @@ -1,8 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { tapBack, sleep } = require('./helpers/app'); +const data = require('../../data'); +const { tapBack, sleep } = require('../../helpers/app'); const room = 'detox-public'; diff --git a/e2e/16-status.spec.js b/e2e/tests/assorted/06-status.spec.js similarity index 70% rename from e2e/16-status.spec.js rename to e2e/tests/assorted/06-status.spec.js index 5b9e6b2d1..ee592d41f 100644 --- a/e2e/16-status.spec.js +++ b/e2e/tests/assorted/06-status.spec.js @@ -1,7 +1,7 @@ const { expect, element, by, waitFor } = require('detox'); -const { sleep } = require('./helpers/app'); +const { sleep } = require('../../helpers/app'); async function waitForToast() { await sleep(5000); @@ -19,8 +19,8 @@ describe('Status screen', () => { describe('Render', async() => { it('should have status input', async() => { - await expect(element(by.id('status-view-input'))).toBeVisible(); - await expect(element(by.id('status-view-online'))).toExist(); + await expect(element(by.id('status-view-input'))).toBeVisible(); + await expect(element(by.id('status-view-online'))).toExist(); await expect(element(by.id('status-view-busy'))).toExist(); await expect(element(by.id('status-view-away'))).toExist(); await expect(element(by.id('status-view-offline'))).toExist(); @@ -29,9 +29,10 @@ describe('Status screen', () => { describe('Usage', async() => { it('should change status', async() => { - await element(by.id('status-view-busy')).tap(); - sleep(1000); - await expect(element(by.id('status-view-current-busy'))).toExist(); + await sleep(1000); + await element(by.id('status-view-busy')).tap(); + await sleep(1000); + await expect(element(by.id('status-view-current-busy'))).toExist(); }); it('should change status text', async() => { @@ -39,6 +40,7 @@ describe('Status screen', () => { await sleep(1000); await element(by.id('status-view-submit')).tap(); await waitForToast(); + await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000); }); }); }); diff --git a/e2e/init.js b/e2e/tests/assorted/init.js similarity index 80% rename from e2e/init.js rename to e2e/tests/assorted/init.js index 28d5d2c66..c9eec4db8 100644 --- a/e2e/init.js +++ b/e2e/tests/assorted/init.js @@ -1,5 +1,5 @@ const detox = require('detox'); -const config = require('../package.json').detox; +const config = require('../../../package.json').detox; before(async() => { await detox.init(config, { launchApp: false }); diff --git a/e2e/tests/onboarding/01-onboarding.spec.js b/e2e/tests/onboarding/01-onboarding.spec.js new file mode 100644 index 000000000..02ad3a690 --- /dev/null +++ b/e2e/tests/onboarding/01-onboarding.spec.js @@ -0,0 +1,62 @@ +const { + device, expect, element, by, waitFor +} = require('detox'); +const data = require('../../data'); + +describe('Onboarding', () => { + before(async() => { + await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); + }); + + describe('Render', () => { + it('should have onboarding screen', async() => { + await expect(element(by.id('onboarding-view'))).toBeVisible(); + }); + + it('should have "Join a workspace"', async() => { + await expect(element(by.id('join-workspace'))).toBeVisible(); + }); + + it('should have "Create a new workspace"', async() => { + await expect(element(by.id('create-workspace-button'))).toBeVisible(); + }); + }); + + describe('Usage', () => { + // it('should navigate to create new workspace', async() => { + // // webviews are not supported by detox: https://github.com/wix/detox/issues/136#issuecomment-306591554 + // }); + + it('should navigate to join a workspace', async() => { + await element(by.id('join-workspace')).tap(); + await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('new-server-view'))).toBeVisible(); + }); + + it('should enter an invalid server and get error', async() => { + await element(by.id('new-server-view-input')).replaceText('invalidtest'); + await element(by.id('new-server-view-button')).tap(); + const errorText = 'Oops!'; + await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000); + await expect(element(by.text(errorText))).toBeVisible(); + await element(by.text('OK')).tap(); + }); + + it('should tap on "Join our open workspace" and navigate', async() => { + await element(by.id('new-server-view-open')).tap(); + await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('workspace-view'))).toBeVisible(); + }); + + it('should enter a valid server without login services and navigate to login', async() => { + await device.launchApp({ newInstance: true }); + await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); + await element(by.id('join-workspace')).tap(); + await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); + await element(by.id('new-server-view-input')).replaceText(data.server); + await element(by.id('new-server-view-button')).tap(); + await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('workspace-view'))).toBeVisible(); + }); + }); +}); diff --git a/e2e/tests/onboarding/02-legal.spec.js b/e2e/tests/onboarding/02-legal.spec.js new file mode 100644 index 000000000..e8109ece5 --- /dev/null +++ b/e2e/tests/onboarding/02-legal.spec.js @@ -0,0 +1,57 @@ +const { + device, expect, element, by, waitFor +} = require('detox'); +const { navigateToRegister, navigateToLogin } = require('../../helpers/app'); + +describe('Legal screen', () => { + it('should have legal button on login', async() => { + await device.launchApp({ newInstance: true }); + await navigateToLogin(); + await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('login-view-more'))).toBeVisible(); + }); + + it('should navigate to legal from login', async() => { + await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); + await element(by.id('login-view-more')).tap(); + }); + + it('should have legal button on register', async() => { + await device.launchApp({ newInstance: true }); + await navigateToRegister(); + await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); + await expect(element(by.id('register-view-more'))).toBeVisible(); + }); + + it('should navigate to legal from register', async() => { + await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); + await element(by.id('register-view-more')).tap(); + }); + + it('should have legal screen', async() => { + await expect(element(by.id('legal-view'))).toBeVisible(); + }); + + it('should have terms of service button', async() => { + await expect(element(by.id('legal-terms-button'))).toBeVisible(); + }); + + it('should have privacy policy button', async() => { + await expect(element(by.id('legal-privacy-button'))).toBeVisible(); + }); + + + // We can't simulate how webview behaves, so I had to disable :( + // it('should navigate to terms', async() => { + // await element(by.id('legal-terms-button')).tap(); + // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); + // await expect(element(by.id('terms-view'))).toBeVisible(); + // }); + + // it('should navigate to privacy', async() => { + // await tapBack(); + // await element(by.id('legal-privacy-button')).tap(); + // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); + // await expect(element(by.id('privacy-view'))).toBeVisible(); + // }); +}); diff --git a/e2e/03-forgotpassword.spec.js b/e2e/tests/onboarding/03-forgotpassword.spec.js similarity index 75% rename from e2e/03-forgotpassword.spec.js rename to e2e/tests/onboarding/03-forgotpassword.spec.js index 6d20d04ae..9e4ba135c 100644 --- a/e2e/03-forgotpassword.spec.js +++ b/e2e/tests/onboarding/03-forgotpassword.spec.js @@ -1,19 +1,20 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); +const data = require('../../data'); +const { navigateToLogin } = require('../../helpers/app'); describe('Forgot password screen', () => { before(async() => { - await element(by.id('welcome-view-login')).tap(); - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); + await device.launchApp({ newInstance: true }); + await navigateToLogin(); await element(by.id('login-view-forgot-password')).tap(); - await waitFor(element(by.id('forgot-password-view'))).toBeVisible().withTimeout(2000); + await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000); }); describe('Render', async() => { it('should have forgot password screen', async() => { - await expect(element(by.id('forgot-password-view'))).toBeVisible(); + await expect(element(by.id('forgot-password-view'))).toExist(); }); it('should have email input', async() => { diff --git a/e2e/04-createuser.spec.js b/e2e/tests/onboarding/04-createuser.spec.js similarity index 78% rename from e2e/04-createuser.spec.js rename to e2e/tests/onboarding/04-createuser.spec.js index 39781503c..0ffef19c4 100644 --- a/e2e/04-createuser.spec.js +++ b/e2e/tests/onboarding/04-createuser.spec.js @@ -1,20 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { logout, sleep } = require('./helpers/app'); -const data = require('./data'); - -async function navigateToRegister() { - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - await element(by.id('connect-server-button')).tap(); - await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await element(by.id('new-server-view-input')).replaceText(data.server); - await element(by.id('new-server-view-button')).tap(); - // we're assuming the server don't have login services and the navigation will jump to login - await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000); - await element(by.id('login-view-register')).tap(); - await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); -} +const { navigateToRegister, sleep } = require('../../helpers/app'); +const data = require('../../data'); describe('Create user screen', () => { before(async() => { @@ -39,10 +27,6 @@ describe('Create user screen', () => { await expect(element(by.id('register-view-password'))).toBeVisible(); }); - it('should have show password icon', async() => { - await expect(element(by.id('register-view-password-icon-right'))).toBeVisible(); - }); - it('should have submit button', async() => { await expect(element(by.id('register-view-submit'))).toBeVisible(); }); @@ -99,9 +83,5 @@ describe('Create user screen', () => { await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); - - after(async() => { - await logout(); - }); }); }); diff --git a/e2e/05-login.spec.js b/e2e/tests/onboarding/05-login.spec.js similarity index 84% rename from e2e/05-login.spec.js rename to e2e/tests/onboarding/05-login.spec.js index 1f8e6c1ec..8a3a36d55 100644 --- a/e2e/05-login.spec.js +++ b/e2e/tests/onboarding/05-login.spec.js @@ -1,11 +1,12 @@ const { - device, expect, element, by, waitFor + expect, element, by, waitFor } = require('detox'); -const { navigateToLogin, tapBack, sleep } = require('./helpers/app'); -const data = require('./data'); +const { navigateToLogin, tapBack, sleep } = require('../../helpers/app'); +const data = require('../../data'); describe('Login screen', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); await navigateToLogin(); }); @@ -22,10 +23,6 @@ describe('Login screen', () => { await expect(element(by.id('login-view-password'))).toBeVisible(); }); - it('should have show password icon', async() => { - await expect(element(by.id('login-view-password-icon-right'))).toBeVisible(); - }); - it('should have submit button', async() => { await expect(element(by.id('login-view-submit'))).toBeVisible(); }); @@ -53,8 +50,8 @@ describe('Login screen', () => { it('should navigate to forgot password', async() => { await element(by.id('login-view-forgot-password')).tap(); - await waitFor(element(by.id('forgot-password-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('forgot-password-view'))).toBeVisible(); + await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000); + await expect(element(by.id('forgot-password-view'))).toExist(); await tapBack(); }); diff --git a/e2e/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js similarity index 57% rename from e2e/06-roomslist.spec.js rename to e2e/tests/onboarding/06-roomslist.spec.js index 00d3dd59d..990fe94c6 100644 --- a/e2e/06-roomslist.spec.js +++ b/e2e/tests/onboarding/06-roomslist.spec.js @@ -1,37 +1,31 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { login, logout, navigateToLogin, tapBack, sleep } = require('./helpers/app'); -const data = require('./data'); +const { logout, tapBack, sleep } = require('../../helpers/app'); describe('Rooms list screen', () => { - describe('Render', async() => { + describe('Render', () => { it('should have rooms list screen', async() => { await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); - - // it('should have rooms list', async() => { - // await expect(element(by.id('rooms-list-view-list'))).toBeVisible(); - // }); it('should have room item', async() => { await expect(element(by.id('rooms-list-view-item-general')).atIndex(0)).toExist(); }); // Render - Header - describe('Header', async() => { + describe('Header', () => { it('should have create channel button', async() => { await expect(element(by.id('rooms-list-view-create-channel'))).toBeVisible(); }); it('should have sidebar button', async() => { await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible(); - // await expect(element(by.id('rooms-list-view-sidebar'))).toHaveLabel(`Connected to ${ data.server }. Tap to view servers list.`); }); }); }); - describe('Usage', async() => { + describe('Usage', () => { it('should search room and navigate', async() => { await element(by.type('UIScrollView')).atIndex(1).scrollTo('top'); await waitFor(element(by.id('rooms-list-view-search'))).toExist().withTimeout(2000); @@ -53,29 +47,8 @@ describe('Rooms list screen', () => { await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist(); }); - // Usage - Sidebar - describe('SidebarView', async() => { - it('should navigate to add server', async() => { - await element(by.id('rooms-list-header-server-dropdown-button')).tap(); - await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible(); - await expect(element(by.id('rooms-list-header-server-add'))).toBeVisible(); - await element(by.id('rooms-list-header-server-add')).tap(); - await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('onboarding-view'))).toBeVisible(); - await element(by.id('onboarding-close')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); - }); - - it('should logout', async() => { - await logout(); - }); - }); - - after(async() => { - await navigateToLogin(); - await login(); + it('should logout', async() => { + await logout(); }); }); }); diff --git a/e2e/tests/onboarding/init.js b/e2e/tests/onboarding/init.js new file mode 100644 index 000000000..9a2385118 --- /dev/null +++ b/e2e/tests/onboarding/init.js @@ -0,0 +1,11 @@ +const detox = require('detox'); +const config = require('../../../package.json').detox; + +before(async() => { + await detox.init(config, { launchApp: false }); + await device.launchApp({ permissions: { notifications: 'YES' } }); +}); + +after(async() => { + await detox.cleanup(); +}); \ No newline at end of file diff --git a/e2e/07-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js similarity index 97% rename from e2e/07-createroom.spec.js rename to e2e/tests/room/01-createroom.spec.js index 65eabdd28..f149a98dc 100644 --- a/e2e/07-createroom.spec.js +++ b/e2e/tests/room/01-createroom.spec.js @@ -1,14 +1,12 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { tapBack, sleep } = require('./helpers/app'); +const data = require('../../data'); +const { tapBack, sleep, createUser } = require('../../helpers/app'); describe('Create room screen', () => { before(async() => { - await sleep(5000); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await device.launchApp({ newInstance: true }); + await createUser(); await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); }); diff --git a/e2e/08-room.spec.js b/e2e/tests/room/02-room.spec.js similarity index 99% rename from e2e/08-room.spec.js rename to e2e/tests/room/02-room.spec.js index 63e1fc59f..8c279d44f 100644 --- a/e2e/08-room.spec.js +++ b/e2e/tests/room/02-room.spec.js @@ -1,8 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { tapBack, sleep } = require('./helpers/app'); +const data = require('../../data'); +const { tapBack, sleep } = require('../../helpers/app'); async function mockMessage(message) { await element(by.id('messagebox-input')).tap(); diff --git a/e2e/09-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js similarity index 99% rename from e2e/09-roomactions.spec.js rename to e2e/tests/room/03-roomactions.spec.js index 2f5821ba2..bef100fa3 100644 --- a/e2e/09-roomactions.spec.js +++ b/e2e/tests/room/03-roomactions.spec.js @@ -1,8 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { tapBack, sleep } = require('./helpers/app'); +const data = require('../../data'); +const { tapBack, sleep } = require('../../helpers/app'); const scrollDown = 200; diff --git a/e2e/10-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js similarity index 92% rename from e2e/10-roominfo.spec.js rename to e2e/tests/room/04-roominfo.spec.js index 4c420ea56..9055fd484 100644 --- a/e2e/10-roominfo.spec.js +++ b/e2e/tests/room/04-roominfo.spec.js @@ -1,8 +1,8 @@ const { device, expect, element, by, waitFor } = require('detox'); -const data = require('./data'); -const { tapBack, sleep } = require('./helpers/app'); +const data = require('../../data'); +const { tapBack, sleep } = require('../../helpers/app'); async function navigateToRoomInfo(type) { let room; @@ -12,7 +12,7 @@ async function navigateToRoomInfo(type) { room = `private${ data.random }`; } await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); - await element(by.type('UIScrollView')).atIndex(1).scrollTo('top'); + await element(by.type('UIScrollView')).atIndex(1).swipe('down'); await element(by.id('rooms-list-view-search')).typeText(room); await sleep(2000); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000); @@ -84,7 +84,7 @@ describe('Room info screen', () => { await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); - await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); + await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); }); it('should have room info edit view', async() => { @@ -169,7 +169,8 @@ describe('Room info screen', () => { // change name to original await element(by.id('room-info-view-edit-button')).tap(); await sleep(1000); - await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); + await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); + await sleep(1000); await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await sleep(1000); @@ -186,8 +187,11 @@ describe('Room info screen', () => { await element(by.id('room-info-edit-view-password')).replaceText('abc'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-t')).tap(); + await sleep(1000); await element(by.id('room-info-edit-view-ro')).tap(); + await sleep(1000); await element(by.id('room-info-edit-view-react-when-ro')).tap(); + await sleep(1000); await element(by.id('room-info-edit-view-reset')).tap(); // after reset await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room); @@ -210,14 +214,14 @@ describe('Room info screen', () => { await tapBack(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await sleep(1000); - // await expect(element(by.id('room-info-view-description'))).toHaveLabel('new description'); - await expect(element(by.label('new description'))).toBeVisible(); - await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); - await element(by.id('room-info-view-edit-button')).tap(); - await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toBeVisible(); }); it('should change room topic', async() => { + await sleep(1000); + await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); + await element(by.id('room-info-view-edit-button')).tap(); + await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); await sleep(1000); await element(by.id('room-info-edit-view-topic')).replaceText('new topic'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); @@ -226,14 +230,14 @@ describe('Room info screen', () => { await tapBack(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await sleep(1000); - // await expect(element(by.id('room-info-view-topic'))).toHaveLabel('new topic'); - await expect(element(by.label('new topic'))).toBeVisible(); - await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); - await element(by.id('room-info-view-edit-button')).tap(); - await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toBeVisible(); }); it('should change room announcement', async() => { + await sleep(1000); + await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); + await element(by.id('room-info-view-edit-button')).tap(); + await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); await sleep(1000); await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); @@ -242,14 +246,14 @@ describe('Room info screen', () => { await tapBack(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await sleep(1000); - // await expect(element(by.id('room-info-view-announcement'))).toHaveLabel('new announcement'); - await expect(element(by.label('new announcement'))).toBeVisible(); - await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); - await element(by.id('room-info-view-edit-button')).tap(); - await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toBeVisible(); }); it('should change room password', async() => { + await sleep(1000); + await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); + await element(by.id('room-info-view-edit-button')).tap(); + await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-password')).replaceText('password'); diff --git a/e2e/tests/room/init.js b/e2e/tests/room/init.js new file mode 100644 index 000000000..9a2385118 --- /dev/null +++ b/e2e/tests/room/init.js @@ -0,0 +1,11 @@ +const detox = require('detox'); +const config = require('../../../package.json').detox; + +before(async() => { + await detox.init(config, { launchApp: false }); + await device.launchApp({ permissions: { notifications: 'YES' } }); +}); + +after(async() => { + await detox.cleanup(); +}); \ No newline at end of file diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock index bf26b8f45..058d663b4 100644 --- a/ios/Gemfile.lock +++ b/ios/Gemfile.lock @@ -1,10 +1,26 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.1) + CFPropertyList (3.0.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) atomos (0.1.3) + aws-eventstream (1.1.0) + aws-partitions (1.310.0) + aws-sdk-core (3.94.1) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.30.0) + aws-sdk-core (~> 3, >= 3.71.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.63.1) + aws-sdk-core (~> 3, >= 3.83.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.1.3) + aws-eventstream (~> 1.0, >= 1.0.2) babosa (1.0.3) claide (1.0.3) colored (1.2) @@ -13,13 +29,13 @@ GEM highline (~> 1.7.2) declarative (0.0.10) declarative-option (0.1.0) - digest-crc (0.4.1) + digest-crc (0.5.1) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.5) emoji_regex (1.0.1) - excon (0.68.0) - faraday (0.17.0) + excon (0.73.0) + faraday (0.17.3) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) faraday (>= 0.7.4) @@ -27,22 +43,23 @@ GEM faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) fastimage (2.1.7) - fastlane (2.134.0) + fastlane (2.146.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) + aws-sdk-s3 (~> 1.0) babosa (>= 1.0.2, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored commander-fastlane (>= 4.4.6, < 5.0.0) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 2.0) - excon (>= 0.45.0, < 1.0.0) + excon (>= 0.71.0, < 1.0.0) faraday (~> 0.17) faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 0.13.1) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.21.2, < 0.24.0) + google-api-client (>= 0.29.2, < 0.37.0) google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) @@ -61,51 +78,54 @@ GEM tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) - xcodeproj (>= 1.8.1, < 2.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-api-client (0.23.9) + google-api-client (0.36.4) addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.5, < 0.7.0) + googleauth (~> 0.9) httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) + mini_mime (~> 1.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - signet (~> 0.9) - google-cloud-core (1.4.1) + signet (~> 0.12) + google-cloud-core (1.5.0) google-cloud-env (~> 1.0) - google-cloud-env (1.3.0) - faraday (~> 0.11) - google-cloud-storage (1.16.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.3.1) + faraday (>= 0.17.3, < 2.0) + google-cloud-errors (1.0.0) + google-cloud-storage (1.26.1) + addressable (~> 2.5) digest-crc (~> 0.4) - google-api-client (~> 0.23) + google-api-client (~> 0.33) google-cloud-core (~> 1.2) - googleauth (>= 0.6.2, < 0.10.0) - googleauth (0.6.7) - faraday (~> 0.12) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.12.0) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.7) + signet (~> 0.14) highline (1.7.10) http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - json (2.2.0) + jmespath (1.4.0) + json (2.3.0) jwt (2.1.0) - memoist (0.16.0) - mime-types (3.3) - mime-types-data (~> 3.2015) - mime-types-data (3.2019.1009) - mini_magick (4.9.5) + memoist (0.16.2) + mini_magick (4.10.1) + mini_mime (1.0.2) multi_json (1.14.1) multi_xml (0.6.0) multipart-post (2.0.0) nanaimo (0.2.6) naturally (2.2.0) - os (1.0.1) + os (1.1.0) plist (3.5.0) public_suffix (2.0.5) representable (3.0.4) @@ -116,29 +136,29 @@ GEM rouge (2.0.7) rubyzip (1.3.0) security (0.1.3) - signet (0.12.0) + signet (0.14.0) addressable (~> 2.3) - faraday (~> 0.9) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.6) + simctl (1.6.8) CFPropertyList naturally slack-notifier (2.3.2) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.7.0) - tty-screen (0.7.0) - tty-spinner (0.9.1) + tty-cursor (0.7.1) + tty-screen (0.7.1) + tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.6) - unicode-display_width (1.6.0) + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) word_wrap (1.0.0) - xcodeproj (1.13.0) + xcodeproj (1.16.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) diff --git a/ios/Podfile b/ios/Podfile index cb86d4ae6..35f65a777 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,7 +1,64 @@ -platform :ios, '10.0' +platform :ios, '11.0' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +def add_flipper_pods!(versions = {}) + versions['Flipper'] ||= '~> 0.33.1' + versions['DoubleConversion'] ||= '1.1.7' + versions['Flipper-Folly'] ||= '~> 2.1' + versions['Flipper-Glog'] ||= '0.3.6' + versions['Flipper-PeerTalk'] ||= '~> 0.0.4' + versions['Flipper-RSocket'] ||= '~> 1.0' + + pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug' + + # List all transitive dependencies for FlipperKit pods + # to avoid them being linked in Release builds + pod 'Flipper', versions['Flipper'], :configuration => 'Debug' + pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug' + pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug' + pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug' + pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug' + pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug' + pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug' + pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug' +end + +# Post Install processing for Flipper +def flipper_post_install(installer) + installer.pods_project.targets.each do |target| + if target.name == 'YogaKit' + target.build_configurations.each do |config| + config.build_settings['SWIFT_VERSION'] = '4.1' + end + end + end + file_name = Dir.glob("*.xcodeproj")[0] + app_project = Xcodeproj::Project.open(file_name) + app_project.native_targets.each do |target| + target.build_configurations.each do |config| + cflags = config.build_settings['OTHER_CFLAGS'] || '$(inherited) ' + unless cflags.include? '-DFB_SONARKIT_ENABLED=1' + puts 'Adding -DFB_SONARKIT_ENABLED=1 in OTHER_CFLAGS...' + cflags << '-DFB_SONARKIT_ENABLED=1' + end + config.build_settings['OTHER_CFLAGS'] = cflags + end + app_project.save + end + installer.pods_project.save +end + target 'RocketChatRN' do pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" @@ -26,9 +83,9 @@ target 'RocketChatRN' do pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' - pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon" + pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" - pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' + pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' @@ -36,7 +93,6 @@ target 'RocketChatRN' do use_native_modules! use_unimodules! - end target 'ShareRocketChatRN' do @@ -63,9 +119,9 @@ target 'ShareRocketChatRN' do pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' - pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon" + pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" - pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' + pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true pod 'JitsiMeetSDK', :git => 'https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git' @@ -74,12 +130,19 @@ target 'ShareRocketChatRN' do pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' use_native_modules! + use_unimodules! end +# Enables Flipper. +# +# Note that if you have use_frameworks! enabled, Flipper will not work and +# you should disable these next few lines. +add_flipper_pods! post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO' end end + flipper_post_install(installer) end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f2b75a51a..2d8776991 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,74 +1,127 @@ PODS: - boost-for-react-native (1.63.0) - - BugsnagReactNative (2.23.2): - - BugsnagReactNative/Core (= 2.23.2) + - BugsnagReactNative (2.23.7): + - BugsnagReactNative/Core (= 2.23.7) - React - - BugsnagReactNative/Core (2.23.2): + - BugsnagReactNative/Core (2.23.7): - React + - CocoaAsyncSocket (7.6.4) + - CocoaLibEvent (1.0.0) - Crashlytics (3.14.0): - Fabric (~> 1.10.2) - DoubleConversion (1.1.6) - - EXAppLoaderProvider (6.0.0) - - EXAV (6.0.0): - - UMCore - - UMPermissionsInterface - - EXConstants (6.0.0): - - UMConstantsInterface - - UMCore - - EXFileSystem (6.0.2): + - EXAV (8.1.0): - UMCore - UMFileSystemInterface - - EXHaptics (6.0.0): + - UMPermissionsInterface + - EXConstants (9.0.0): + - UMConstantsInterface - UMCore - - EXPermissions (6.0.0): + - EXFileSystem (8.1.0): + - UMCore + - UMFileSystemInterface + - EXHaptics (8.1.0): + - UMCore + - EXImageLoader (1.0.1): + - React-Core + - UMCore + - UMImageLoaderInterface + - EXKeepAwake (8.1.0): + - UMCore + - EXLocalAuthentication (9.0.0): + - UMConstantsInterface + - UMCore + - EXPermissions (8.1.0): - UMCore - UMPermissionsInterface - - EXWebBrowser (6.0.0): + - EXWebBrowser (8.2.1): - UMCore - Fabric (1.10.2) - - FBLazyVector (0.61.5) - - FBReactNativeSpec (0.61.5): + - FBLazyVector (0.62.2) + - FBReactNativeSpec (0.62.2): - Folly (= 2018.10.22.00) - - RCTRequired (= 0.61.5) - - RCTTypeSafety (= 0.61.5) - - React-Core (= 0.61.5) - - React-jsi (= 0.61.5) - - ReactCommon/turbomodule/core (= 0.61.5) - - Firebase/Core (6.16.0): + - RCTRequired (= 0.62.2) + - RCTTypeSafety (= 0.62.2) + - React-Core (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - Firebase/Core (6.24.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.2.2) - - Firebase/CoreOnly (6.16.0): - - FirebaseCore (= 6.6.1) - - FirebaseAnalytics (6.2.2): - - FirebaseCore (~> 6.6) - - FirebaseInstanceID (~> 4.3) - - GoogleAppMeasurement (= 6.2.2) + - FirebaseAnalytics (= 6.5.0) + - Firebase/CoreOnly (6.24.0): + - FirebaseCore (= 6.7.0) + - FirebaseAnalytics (6.5.0): + - FirebaseCore (~> 6.7) + - FirebaseInstallations (~> 1.2) + - GoogleAppMeasurement (= 6.5.0) - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (= 0.3.9011) - - FirebaseCore (6.6.1): - - FirebaseCoreDiagnostics (~> 1.2) + - nanopb (~> 1.30905.0) + - FirebaseCore (6.7.0): + - FirebaseCoreDiagnostics (~> 1.3) - FirebaseCoreDiagnosticsInterop (~> 1.2) - GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Logger (~> 6.5) - - FirebaseCoreDiagnostics (1.2.0): + - FirebaseCoreDiagnostics (1.3.0): - FirebaseCoreDiagnosticsInterop (~> 1.2) - - GoogleDataTransportCCTSupport (~> 1.3) + - GoogleDataTransportCCTSupport (~> 3.1) - GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Logger (~> 6.5) - - nanopb (~> 0.3.901) + - nanopb (~> 1.30905.0) - FirebaseCoreDiagnosticsInterop (1.2.0) - - FirebaseInstallations (1.1.0): + - FirebaseInstallations (1.2.0): - FirebaseCore (~> 6.6) - - GoogleUtilities/UserDefaults (~> 6.5) + - GoogleUtilities/Environment (~> 6.6) + - GoogleUtilities/UserDefaults (~> 6.6) - PromisesObjC (~> 1.2) - - FirebaseInstanceID (4.3.0): - - FirebaseCore (~> 6.6) - - FirebaseInstallations (~> 1.0) - - GoogleUtilities/Environment (~> 6.5) - - GoogleUtilities/UserDefaults (~> 6.5) + - Flipper (0.33.1): + - Flipper-Folly (~> 2.1) + - Flipper-RSocket (~> 1.0) + - Flipper-DoubleConversion (1.1.7) + - Flipper-Folly (2.2.0): + - boost-for-react-native + - CocoaLibEvent (~> 1.0) + - Flipper-DoubleConversion + - Flipper-Glog + - OpenSSL-Universal (= 1.0.2.19) + - Flipper-Glog (0.3.6) + - Flipper-PeerTalk (0.0.4) + - Flipper-RSocket (1.1.0): + - Flipper-Folly (~> 2.2) + - FlipperKit (0.33.1): + - FlipperKit/Core (= 0.33.1) + - FlipperKit/Core (0.33.1): + - Flipper (~> 0.33.1) + - FlipperKit/CppBridge + - FlipperKit/FBCxxFollyDynamicConvert + - FlipperKit/FBDefines + - FlipperKit/FKPortForwarding + - FlipperKit/CppBridge (0.33.1): + - Flipper (~> 0.33.1) + - FlipperKit/FBCxxFollyDynamicConvert (0.33.1): + - Flipper-Folly (~> 2.1) + - FlipperKit/FBDefines (0.33.1) + - FlipperKit/FKPortForwarding (0.33.1): + - CocoaAsyncSocket (~> 7.6) + - Flipper-PeerTalk (~> 0.0.4) + - FlipperKit/FlipperKitHighlightOverlay (0.33.1) + - FlipperKit/FlipperKitLayoutPlugin (0.33.1): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutTextSearchable + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutTextSearchable (0.33.1) + - FlipperKit/FlipperKitNetworkPlugin (0.33.1): + - FlipperKit/Core + - FlipperKit/FlipperKitReactPlugin (0.33.1): + - FlipperKit/Core + - FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.33.1): + - FlipperKit/Core + - FlipperKit/FlipperKitNetworkPlugin - Folly (2018.10.22.00): - boost-for-react-native - DoubleConversion @@ -79,35 +132,36 @@ PODS: - DoubleConversion - glog - glog (0.3.5) - - GoogleAppMeasurement (6.2.2): + - GoogleAppMeasurement (6.5.0): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (= 0.3.9011) - - GoogleDataTransport (3.3.1) - - GoogleDataTransportCCTSupport (1.3.1): - - GoogleDataTransport (~> 3.3) - - nanopb (~> 0.3.901) - - GoogleUtilities/AppDelegateSwizzler (6.5.1): + - nanopb (~> 1.30905.0) + - GoogleDataTransport (6.1.0) + - GoogleDataTransportCCTSupport (3.1.0): + - GoogleDataTransport (~> 6.1) + - nanopb (~> 1.30905.0) + - GoogleUtilities/AppDelegateSwizzler (6.6.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.5.1) - - GoogleUtilities/Logger (6.5.1): + - GoogleUtilities/Environment (6.6.0): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.6.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.5.1): + - GoogleUtilities/MethodSwizzler (6.6.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.5.1): + - GoogleUtilities/Network (6.6.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.5.1)" - - GoogleUtilities/Reachability (6.5.1): + - "GoogleUtilities/NSData+zlib (6.6.0)" + - GoogleUtilities/Reachability (6.6.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.5.1): + - GoogleUtilities/UserDefaults (6.6.0): - GoogleUtilities/Logger - - JitsiMeetSDK (2.4.0) + - JitsiMeetSDK (2.8.1) - KeyCommands (2.0.3): - React - libwebp (1.1.0): @@ -119,266 +173,290 @@ PODS: - libwebp/mux (1.1.0): - libwebp/demux - libwebp/webp (1.1.0) - - nanopb (0.3.9011): - - nanopb/decode (= 0.3.9011) - - nanopb/encode (= 0.3.9011) - - nanopb/decode (0.3.9011) - - nanopb/encode (0.3.9011) + - nanopb (1.30905.0): + - nanopb/decode (= 1.30905.0) + - nanopb/encode (= 1.30905.0) + - nanopb/decode (1.30905.0) + - nanopb/encode (1.30905.0) + - OpenSSL-Universal (1.0.2.19): + - OpenSSL-Universal/Static (= 1.0.2.19) + - OpenSSL-Universal/Static (1.0.2.19) - PromisesObjC (1.2.8) - - RCTRequired (0.61.5) - - RCTTypeSafety (0.61.5): - - FBLazyVector (= 0.61.5) + - RCTRequired (0.62.2) + - RCTTypeSafety (0.62.2): + - FBLazyVector (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTRequired (= 0.61.5) - - React-Core (= 0.61.5) - - React (0.61.5): - - React-Core (= 0.61.5) - - React-Core/DevSupport (= 0.61.5) - - React-Core/RCTWebSocket (= 0.61.5) - - React-RCTActionSheet (= 0.61.5) - - React-RCTAnimation (= 0.61.5) - - React-RCTBlob (= 0.61.5) - - React-RCTImage (= 0.61.5) - - React-RCTLinking (= 0.61.5) - - React-RCTNetwork (= 0.61.5) - - React-RCTSettings (= 0.61.5) - - React-RCTText (= 0.61.5) - - React-RCTVibration (= 0.61.5) - - React-Core (0.61.5): + - RCTRequired (= 0.62.2) + - React-Core (= 0.62.2) + - React (0.62.2): + - React-Core (= 0.62.2) + - React-Core/DevSupport (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-RCTActionSheet (= 0.62.2) + - React-RCTAnimation (= 0.62.2) + - React-RCTBlob (= 0.62.2) + - React-RCTImage (= 0.62.2) + - React-RCTLinking (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - React-RCTSettings (= 0.62.2) + - React-RCTText (= 0.62.2) + - React-RCTVibration (= 0.62.2) + - React-Core (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.61.5) - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-Core/Default (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/CoreModulesHeaders (0.61.5): + - React-Core/CoreModulesHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/Default (0.61.5): + - React-Core/Default (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/DevSupport (0.61.5): + - React-Core/DevSupport (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.61.5) - - React-Core/RCTWebSocket (= 0.61.5) - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) - - React-jsinspector (= 0.61.5) + - React-Core/Default (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) + - React-jsinspector (= 0.62.2) - Yoga - - React-Core/RCTActionSheetHeaders (0.61.5): + - React-Core/RCTActionSheetHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTAnimationHeaders (0.61.5): + - React-Core/RCTAnimationHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTBlobHeaders (0.61.5): + - React-Core/RCTBlobHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTImageHeaders (0.61.5): + - React-Core/RCTImageHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTLinkingHeaders (0.61.5): + - React-Core/RCTLinkingHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTNetworkHeaders (0.61.5): + - React-Core/RCTNetworkHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTSettingsHeaders (0.61.5): + - React-Core/RCTSettingsHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTTextHeaders (0.61.5): + - React-Core/RCTTextHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTVibrationHeaders (0.61.5): + - React-Core/RCTVibrationHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTWebSocket (0.61.5): + - React-Core/RCTWebSocket (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.61.5) - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsiexecutor (= 0.61.5) + - React-Core/Default (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-CoreModules (0.61.5): - - FBReactNativeSpec (= 0.61.5) + - React-CoreModules (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.61.5) - - React-Core/CoreModulesHeaders (= 0.61.5) - - React-RCTImage (= 0.61.5) - - ReactCommon/turbomodule/core (= 0.61.5) - - React-cxxreact (0.61.5): + - RCTTypeSafety (= 0.62.2) + - React-Core/CoreModulesHeaders (= 0.62.2) + - React-RCTImage (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-cxxreact (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsinspector (= 0.61.5) - - React-jsi (0.61.5): + - React-jsinspector (= 0.62.2) + - React-jsi (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsi/Default (= 0.61.5) - - React-jsi/Default (0.61.5): + - React-jsi/Default (= 0.62.2) + - React-jsi/Default (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsiexecutor (0.61.5): + - React-jsiexecutor (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - React-jsinspector (0.61.5) - - react-native-appearance (0.3.1): + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsinspector (0.62.2) + - react-native-appearance (0.3.4): - React - - react-native-background-timer (2.1.1): + - react-native-background-timer (2.2.0): - React - - react-native-cameraroll (1.3.0): + - react-native-cameraroll (1.6.0): - React - - react-native-document-picker (3.2.4): + - react-native-document-picker (3.3.3): - React - - react-native-jitsi-meet (2.1.0): - - JitsiMeetSDK + - react-native-jitsi-meet (2.1.1): + - JitsiMeetSDK (= 2.8.1) - React - - react-native-keyboard-input (5.4.1): + - react-native-notifications (2.1.7): - React - - react-native-keyboard-tracking-view (5.6.1): + - react-native-orientation-locker (1.1.8): - React - - react-native-notifications (2.0.6): + - react-native-slider (2.0.9): - React - - react-native-orientation-locker (1.1.6): + - react-native-webview (9.4.0): - React - - react-native-slider (2.0.5): - - React - - react-native-video (5.0.2): - - React - - react-native-video/Video (= 5.0.2) - - react-native-video/Video (5.0.2): - - React - - react-native-webview (7.5.1): - - React - - React-RCTActionSheet (0.61.5): - - React-Core/RCTActionSheetHeaders (= 0.61.5) - - React-RCTAnimation (0.61.5): - - React-Core/RCTAnimationHeaders (= 0.61.5) - - React-RCTBlob (0.61.5): - - React-Core/RCTBlobHeaders (= 0.61.5) - - React-Core/RCTWebSocket (= 0.61.5) - - React-jsi (= 0.61.5) - - React-RCTNetwork (= 0.61.5) - - React-RCTImage (0.61.5): - - React-Core/RCTImageHeaders (= 0.61.5) - - React-RCTNetwork (= 0.61.5) - - React-RCTLinking (0.61.5): - - React-Core/RCTLinkingHeaders (= 0.61.5) - - React-RCTNetwork (0.61.5): - - React-Core/RCTNetworkHeaders (= 0.61.5) - - React-RCTSettings (0.61.5): - - React-Core/RCTSettingsHeaders (= 0.61.5) - - React-RCTText (0.61.5): - - React-Core/RCTTextHeaders (= 0.61.5) - - React-RCTVibration (0.61.5): - - React-Core/RCTVibrationHeaders (= 0.61.5) - - ReactCommon/jscallinvoker (0.61.5): + - React-RCTActionSheet (0.62.2): + - React-Core/RCTActionSheetHeaders (= 0.62.2) + - React-RCTAnimation (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTAnimationHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTBlob (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - React-Core/RCTBlobHeaders (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-jsi (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTImage (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTImageHeaders (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTLinking (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - React-Core/RCTLinkingHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTNetwork (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTNetworkHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTSettings (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTSettingsHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTText (0.62.2): + - React-Core/RCTTextHeaders (= 0.62.2) + - React-RCTVibration (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - Folly (= 2018.10.22.00) + - React-Core/RCTVibrationHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - ReactCommon/callinvoker (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.61.5) - - ReactCommon/turbomodule/core (0.61.5): + - React-cxxreact (= 0.62.2) + - ReactCommon/turbomodule/core (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-Core (= 0.61.5) - - React-cxxreact (= 0.61.5) - - React-jsi (= 0.61.5) - - ReactCommon/jscallinvoker (= 0.61.5) - - ReactNativeART (1.0.4): + - React-Core (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/callinvoker (= 0.62.2) + - ReactNativeART (1.2.0): + - React + - ReactNativeKeyboardInput (6.0.0): + - React + - ReactNativeKeyboardTrackingView (5.7.0): - React - rn-extensions-share (2.3.10): - React - - rn-fetch-blob (0.11.2): + - rn-fetch-blob (0.12.0): - React-Core - RNAudio (4.3.0): - React - - RNBootSplash (2.1.0): + - RNBootSplash (2.2.4): - React - - RNDateTimePicker (2.1.0): + - RNCAsyncStorage (1.9.0): - React - - RNDeviceInfo (2.3.2): + - RNDateTimePicker (2.3.2): - React - - RNFastImage (7.0.2): + - RNDeviceInfo (5.5.7): + - React + - RNFastImage (8.1.5): - React - SDWebImage (~> 5.0) - - SDWebImageWebPCoder (~> 0.2.3) - - RNFirebase (5.5.6): + - SDWebImageWebPCoder (~> 0.4.1) + - RNFirebase (5.6.0): - Firebase/Core - React - - RNFirebase/Crashlytics (= 5.5.6) - - RNFirebase/Crashlytics (5.5.6): + - RNFirebase/Crashlytics (= 5.6.0) + - RNFirebase/Crashlytics (5.6.0): - Crashlytics - Fabric - Firebase/Core - React - - RNGestureHandler (1.5.0): + - RNGestureHandler (1.6.1): - React - RNImageCropPicker (0.28.0): - React-Core @@ -389,54 +467,79 @@ PODS: - React-Core - React-RCTImage - RSKImageCropper - - RNLocalize (1.3.1): + - RNLocalize (1.4.0): - React - - RNReanimated (1.4.0): + - RNReanimated (1.8.0): - React - RNRootView (1.0.3): - React - - RNScreens (2.0.0-alpha.3): + - RNScreens (2.7.0): - React - RNUserDefaults (1.8.1): - React - RNVectorIcons (6.6.0): - React - RSKImageCropper (2.2.3) - - SDWebImage (5.5.2): - - SDWebImage/Core (= 5.5.2) - - SDWebImage/Core (5.5.2) - - SDWebImageWebPCoder (0.2.5): + - SDWebImage (5.7.4): + - SDWebImage/Core (= 5.7.4) + - SDWebImage/Core (5.7.4) + - SDWebImageWebPCoder (0.4.1): - libwebp (~> 1.0) - - SDWebImage/Core (~> 5.0) - - UMBarCodeScannerInterface (3.0.0) - - UMCameraInterface (3.0.0) - - UMConstantsInterface (3.0.0) - - UMCore (3.0.2) - - UMFaceDetectorInterface (3.0.0) - - UMFileSystemInterface (3.0.0) - - UMFontInterface (3.0.0) - - UMImageLoaderInterface (3.0.0) - - UMPermissionsInterface (3.0.0) - - UMReactNativeAdapter (3.0.0): - - React + - SDWebImage/Core (~> 5.5) + - UMAppLoader (1.0.2) + - UMBarCodeScannerInterface (5.1.0) + - UMCameraInterface (5.1.0) + - UMConstantsInterface (5.1.0) + - UMCore (5.1.2) + - UMFaceDetectorInterface (5.1.0) + - UMFileSystemInterface (5.1.0) + - UMFontInterface (5.1.0) + - UMImageLoaderInterface (5.1.0) + - UMPermissionsInterface (5.1.0): + - UMCore + - UMReactNativeAdapter (5.2.0): + - React-Core - UMCore - UMFontInterface - - UMSensorsInterface (3.0.0) - - UMTaskManagerInterface (3.0.0) + - UMSensorsInterface (5.1.0) + - UMTaskManagerInterface (5.1.0) - Yoga (1.14.0) + - YogaKit (1.18.1): + - Yoga (~> 1.14) DEPENDENCIES: - BugsnagReactNative (from `../node_modules/bugsnag-react-native`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - EXAppLoaderProvider (from `../node_modules/expo-app-loader-provider/ios`) - EXAV (from `../node_modules/expo-av/ios`) - EXConstants (from `../node_modules/expo-constants/ios`) - EXFileSystem (from `../node_modules/expo-file-system/ios`) - EXHaptics (from `../node_modules/expo-haptics/ios`) + - EXImageLoader (from `../node_modules/expo-image-loader/ios`) + - EXKeepAwake (from `../node_modules/expo-keep-awake/ios`) + - EXLocalAuthentication (from `../node_modules/expo-local-authentication/ios`) - EXPermissions (from `../node_modules/expo-permissions/ios`) - EXWebBrowser (from `../node_modules/expo-web-browser/ios`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) + - Flipper (~> 0.33.1) + - Flipper-DoubleConversion (= 1.1.7) + - Flipper-Folly (~> 2.1) + - Flipper-Glog (= 0.3.6) + - Flipper-PeerTalk (~> 0.0.4) + - Flipper-RSocket (~> 1.0) + - FlipperKit (~> 0.33.1) + - FlipperKit/Core (~> 0.33.1) + - FlipperKit/CppBridge (~> 0.33.1) + - FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1) + - FlipperKit/FBDefines (~> 0.33.1) + - FlipperKit/FKPortForwarding (~> 0.33.1) + - FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1) + - FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1) + - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1) + - FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1) + - FlipperKit/FlipperKitReactPlugin (~> 0.33.1) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1) + - FlipperKit/SKIOSNetworkPlugin (~> 0.33.1) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - JitsiMeetSDK (from `https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git`) @@ -457,12 +560,9 @@ DEPENDENCIES: - "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)" - react-native-document-picker (from `../node_modules/react-native-document-picker`) - react-native-jitsi-meet (from `../node_modules/react-native-jitsi-meet`) - - react-native-keyboard-input (from `../node_modules/react-native-keyboard-input`) - - react-native-keyboard-tracking-view (from `../node_modules/react-native-keyboard-tracking-view`) - react-native-notifications (from `../node_modules/react-native-notifications`) - react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`) - "react-native-slider (from `../node_modules/@react-native-community/slider`)" - - react-native-video (from `../node_modules/react-native-video`) - react-native-webview (from `../node_modules/react-native-webview`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) @@ -473,13 +573,16 @@ DEPENDENCIES: - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - - ReactCommon/jscallinvoker (from `../node_modules/react-native/ReactCommon`) + - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "ReactNativeART (from `../node_modules/@react-native-community/art`)" + - ReactNativeKeyboardInput (from `../node_modules/react-native-keyboard-input`) + - ReactNativeKeyboardTrackingView (from `../node_modules/react-native-keyboard-tracking-view`) - rn-extensions-share (from `../node_modules/rn-extensions-share`) - rn-fetch-blob (from `../node_modules/rn-fetch-blob`) - RNAudio (from `../node_modules/react-native-audio`) - RNBootSplash (from `../node_modules/react-native-bootsplash`) + - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) - RNFastImage (from `../node_modules/react-native-fast-image`) @@ -492,6 +595,7 @@ DEPENDENCIES: - RNScreens (from `../node_modules/react-native-screens`) - RNUserDefaults (from `../node_modules/rn-user-defaults`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) + - UMAppLoader (from `../node_modules/unimodules-app-loader/ios`) - UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`) - UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`) - UMConstantsInterface (from `../node_modules/unimodules-constants-interface/ios`) @@ -509,6 +613,8 @@ DEPENDENCIES: SPEC REPOS: trunk: - boost-for-react-native + - CocoaAsyncSocket + - CocoaLibEvent - Crashlytics - Fabric - Firebase @@ -517,44 +623,49 @@ SPEC REPOS: - FirebaseCoreDiagnostics - FirebaseCoreDiagnosticsInterop - FirebaseInstallations - - FirebaseInstanceID + - Flipper + - Flipper-DoubleConversion + - Flipper-Folly + - Flipper-Glog + - Flipper-PeerTalk + - Flipper-RSocket + - FlipperKit - GoogleAppMeasurement - GoogleDataTransport - GoogleDataTransportCCTSupport - GoogleUtilities - libwebp - nanopb + - OpenSSL-Universal - PromisesObjC - RSKImageCropper - SDWebImage - SDWebImageWebPCoder + - YogaKit EXTERNAL SOURCES: BugsnagReactNative: :path: "../node_modules/bugsnag-react-native" DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" - EXAppLoaderProvider: - :path: !ruby/object:Pathname - path: "../node_modules/expo-app-loader-provider/ios" EXAV: - :path: !ruby/object:Pathname - path: "../node_modules/expo-av/ios" + :path: "../node_modules/expo-av/ios" EXConstants: - :path: !ruby/object:Pathname - path: "../node_modules/expo-constants/ios" + :path: "../node_modules/expo-constants/ios" EXFileSystem: - :path: !ruby/object:Pathname - path: "../node_modules/expo-file-system/ios" + :path: "../node_modules/expo-file-system/ios" EXHaptics: - :path: !ruby/object:Pathname - path: "../node_modules/expo-haptics/ios" + :path: "../node_modules/expo-haptics/ios" + EXImageLoader: + :path: "../node_modules/expo-image-loader/ios" + EXKeepAwake: + :path: "../node_modules/expo-keep-awake/ios" + EXLocalAuthentication: + :path: "../node_modules/expo-local-authentication/ios" EXPermissions: - :path: !ruby/object:Pathname - path: "../node_modules/expo-permissions/ios" + :path: "../node_modules/expo-permissions/ios" EXWebBrowser: - :path: !ruby/object:Pathname - path: "../node_modules/expo-web-browser/ios" + :path: "../node_modules/expo-web-browser/ios" FBLazyVector: :path: "../node_modules/react-native/Libraries/FBLazyVector" FBReactNativeSpec: @@ -595,18 +706,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-document-picker" react-native-jitsi-meet: :path: "../node_modules/react-native-jitsi-meet" - react-native-keyboard-input: - :path: "../node_modules/react-native-keyboard-input" - react-native-keyboard-tracking-view: - :path: "../node_modules/react-native-keyboard-tracking-view" react-native-notifications: :path: "../node_modules/react-native-notifications" react-native-orientation-locker: :path: "../node_modules/react-native-orientation-locker" react-native-slider: :path: "../node_modules/@react-native-community/slider" - react-native-video: - :path: "../node_modules/react-native-video" react-native-webview: :path: "../node_modules/react-native-webview" React-RCTActionSheet: @@ -631,6 +736,10 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" ReactNativeART: :path: "../node_modules/@react-native-community/art" + ReactNativeKeyboardInput: + :path: "../node_modules/react-native-keyboard-input" + ReactNativeKeyboardTrackingView: + :path: "../node_modules/react-native-keyboard-tracking-view" rn-extensions-share: :path: "../node_modules/rn-extensions-share" rn-fetch-blob: @@ -639,6 +748,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-audio" RNBootSplash: :path: "../node_modules/react-native-bootsplash" + RNCAsyncStorage: + :path: "../node_modules/@react-native-community/async-storage" RNDateTimePicker: :path: "../node_modules/@react-native-community/datetimepicker" RNDeviceInfo: @@ -663,148 +774,151 @@ EXTERNAL SOURCES: :path: "../node_modules/rn-user-defaults" RNVectorIcons: :path: "../node_modules/react-native-vector-icons" + UMAppLoader: + :path: "../node_modules/unimodules-app-loader/ios" UMBarCodeScannerInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-barcode-scanner-interface/ios" + :path: "../node_modules/unimodules-barcode-scanner-interface/ios" UMCameraInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-camera-interface/ios" + :path: "../node_modules/unimodules-camera-interface/ios" UMConstantsInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-constants-interface/ios" + :path: "../node_modules/unimodules-constants-interface/ios" UMCore: - :path: !ruby/object:Pathname - path: "../node_modules/@unimodules/core/ios" + :path: "../node_modules/@unimodules/core/ios" UMFaceDetectorInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-face-detector-interface/ios" + :path: "../node_modules/unimodules-face-detector-interface/ios" UMFileSystemInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-file-system-interface/ios" + :path: "../node_modules/unimodules-file-system-interface/ios" UMFontInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-font-interface/ios" + :path: "../node_modules/unimodules-font-interface/ios" UMImageLoaderInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-image-loader-interface/ios" + :path: "../node_modules/unimodules-image-loader-interface/ios" UMPermissionsInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-permissions-interface/ios" + :path: "../node_modules/unimodules-permissions-interface/ios" UMReactNativeAdapter: - :path: !ruby/object:Pathname - path: "../node_modules/@unimodules/react-native-adapter/ios" + :path: "../node_modules/@unimodules/react-native-adapter/ios" UMSensorsInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-sensors-interface/ios" + :path: "../node_modules/unimodules-sensors-interface/ios" UMTaskManagerInterface: - :path: !ruby/object:Pathname - path: "../node_modules/unimodules-task-manager-interface/ios" + :path: "../node_modules/unimodules-task-manager-interface/ios" Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" CHECKOUT OPTIONS: JitsiMeetSDK: - :commit: 0d7b379179ef81965e505677cd10d07956c3a2dc + :commit: 9177aaa3afb379e17cc687887485e91e5cd24a49 :git: https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - BugsnagReactNative: 0a24a1dd2cac88862d67b938f809bec8274130a9 + BugsnagReactNative: 14c1b59cfbf34fd5591b734bfec65a277b677ef8 + CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 + CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 - EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0 - EXAV: 7228890721d1d74779bc3154fb678a44249b1c71 - EXConstants: 5d81e84ca71b9a552529889cc798b4a04e9e22b3 - EXFileSystem: 091907902fcec9f9182b656fdead41a82f30986a - EXHaptics: f84c93d605e0905c47654e4a6e5dfbff78ed6906 - EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 - EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 + EXAV: 2edd9cd30fe98d04b55325303c7ff01db02d1d0f + EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17 + EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01 + EXHaptics: 013b5065946d4dd7b46ea547b58072d45a206dbd + EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a + EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0 + EXLocalAuthentication: bbf1026cc289d729da4f29240dd7a8f6a14e4b20 + EXPermissions: 24b97f734ce9172d245a5be38ad9ccfcb6135964 + EXWebBrowser: 5902f99ac5ac551e5c82ff46f13a337b323aa9ea Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 - FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f - FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75 - Firebase: 497158b816d0a86fc31babbd05546fcd7e6083ff - FirebaseAnalytics: cf95d3aab897612783020fbd98401d5366f135ee - FirebaseCore: 85064903ed6c28e47fec9c7bd149d94ba1b6b6e7 - FirebaseCoreDiagnostics: 5e78803ab276bc5b50340e3c539c06c3de35c649 + FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245 + FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e + Firebase: b28e55c60efd98963cd9011fe2fac5a10c2ba124 + FirebaseAnalytics: 7386fc2176e3f93ad8ef34b5b1f2b33a891e4962 + FirebaseCore: e610482f64097b0e9f056cd97bc6b33dfabcbb6a + FirebaseCoreDiagnostics: 4a773a47bd83bbd5a9b1ccf1ce7caa8b2d535e67 FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850 - FirebaseInstallations: 575cd32f2aec0feeb0e44f5d0110a09e5e60b47b - FirebaseInstanceID: 6668efc1655a4052c083f287a7141f1ead12f9c2 + FirebaseInstallations: 2119fb3e46b0a88bfdbf12562f855ee3252462fa + Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69 + Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 + Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 + Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 + Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 + FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - GoogleAppMeasurement: d0560d915abf15e692e8538ba1d58442217b6aff - GoogleDataTransport: 0048df6388dab1c254799f2a30365b1dffe20422 - GoogleDataTransportCCTSupport: f880d70972efa2ed1be4e9173a0f4c5f3dc2d176 - GoogleUtilities: 06eb53bb579efe7099152735900dd04bf09e7275 - JitsiMeetSDK: d4a3aeed1a75fd57e6a78e5d202b6051dfcb9320 + GoogleAppMeasurement: 4c644d86835d827bab30ab6aabb9ecaf1f500735 + GoogleDataTransport: f6f8eba931df03ebd2232ff4645aa85f8f47b5ab + GoogleDataTransportCCTSupport: d70a561f7d236af529fee598835caad5e25f6d3d + GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1 + JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79 KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 - nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd + nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 + OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6 - RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1 - RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320 - React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78 - React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04 - React-CoreModules: d04f8494c1a328b69ec11db9d1137d667f916dcb - React-cxxreact: d0f7bcafa196ae410e5300736b424455e7fb7ba7 - React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7 - React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386 - React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 - react-native-appearance: 368f9d1160e3f1d7ecb5945e704affe018deef46 - react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b - react-native-cameraroll: 463aff54e37cff27ea76eb792e6f1fa43b876320 - react-native-document-picker: c36bf5f067a581657ecaf7124dcd921a8be19061 - react-native-jitsi-meet: 04ccc47303c62ba2b7e7407a113f5f46241ebd75 - react-native-keyboard-input: 775c2f00554869563b1d59c6bebd8f8a2aa54295 - react-native-keyboard-tracking-view: 4bb67b89ccd327c7d9eab87f722880d2103a25a8 - react-native-notifications: 163ddedac6fcc8d850ea15b06abdadcacdff00f1 - react-native-orientation-locker: 23918c400376a7043e752c639c122fcf6bce8f1c - react-native-slider: 39208600e44f885e2d2c0510b5c6435a0f62d087 - react-native-video: d01ed7ff1e38fa7dcc6c15c94cf505e661b7bfd0 - react-native-webview: 2aadbfef6b9eaa9e89b306ae3e31e6e870a6306d - React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76 - React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360 - React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72 - React-RCTImage: 6b8e8df449eb7c814c99a92d6b52de6fe39dea4e - React-RCTLinking: 121bb231c7503cf9094f4d8461b96a130fabf4a5 - React-RCTNetwork: fb353640aafcee84ca8b78957297bd395f065c9a - React-RCTSettings: 8db258ea2a5efee381fcf7a6d5044e2f8b68b640 - React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe - React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad - ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd - ReactNativeART: 95d7eeb535cbdcb79f190042834ab3446e15d876 + RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035 + RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce + React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3 + React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05 + React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0 + React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103 + React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161 + React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da + React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493 + react-native-appearance: 0f0e5fc2fcef70e03d48c8fe6b00b9158c2ba8aa + react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4 + react-native-cameraroll: 02e60e9af9273a3cc3b641632bf651189830aaf8 + react-native-document-picker: dd96ce05bf1453b110d7a3912097bf6d298d2cb6 + react-native-jitsi-meet: f89bcb2cfbd5b15403b9c40738036c4f1af45d05 + react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227 + react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d + react-native-slider: e51492f1264d882a8815b71c5870f8978e52887d + react-native-webview: cf5527893252b3b036eea024a1da6996f7344c74 + React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c + React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0 + React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71 + React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3 + React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2 + React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44 + React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a + React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d + React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256 + ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3 + ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab + ReactNativeKeyboardInput: c37e26821519869993b3b61844350feb9177ff37 + ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306 rn-extensions-share: 4bfee75806ad54aadeff1dfa535697a6345a50b8 - rn-fetch-blob: f525a73a78df9ed5d35e67ea65e79d53c15255bc + rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba RNAudio: cae2991f2dccb75163f260b60da8051717b959fa - RNBootSplash: fbf76a4bf1c03e9acd1dd08ce064847f26d6d8bf - RNDateTimePicker: 9db00606d689f5653f08aa601c81b9d3266a0a19 - RNDeviceInfo: 17e34f6dd902f08d88cbe2c0b7a01be948d43641 - RNFastImage: 9b0c22643872bb7494c8d87bbbb66cc4c0d9e7a2 - RNFirebase: ac0de8b24c6f91ae9459575491ed6a77327619c6 - RNGestureHandler: a4ddde1ffc6e590c8127b8b7eabfdade45475c74 + RNBootSplash: 7cb9b4fe7e94177edc0d11010f7631d79db2f5e9 + RNCAsyncStorage: 453cd7c335ec9ba3b877e27d02238956b76f3268 + RNDateTimePicker: 4bd49e09f91ca73d69119a9e1173b0d43b82f5e5 + RNDeviceInfo: e2102056bde3ad5d137fd029d8d431510a00486a + RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9 + RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c + RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 RNImageCropPicker: cf129d17e042ce3e96fb9ada967c28f21f977c82 - RNLocalize: 07eb7a91d10021cdf59d80061ebf3adb8a5b5688 - RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c + RNLocalize: b6df30cc25ae736d37874f9bce13351db2f56796 + RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494 - RNScreens: 402a99b0a27c0c32f079cec12d3ccbd35e20cd7f + RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706 RNUserDefaults: c421fd97ad06b35c16608c5d0fe675db353f632d RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a - SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca - SDWebImageWebPCoder: 947093edd1349d820c40afbd9f42acb6cdecd987 - UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020 - UMCameraInterface: 26b26005d1756a0d5f4f04f1e168e39ea9154535 - UMConstantsInterface: 038bacb19de12b6fd328c589122c8dc977cccf61 - UMCore: 733094f43f7244c60ce1f0592d00013ed68fa52c - UMFaceDetectorInterface: c9c3ae4cb045421283667a1698c2f31331f55e3f - UMFileSystemInterface: e9adc71027017de38eaf7d05fa58b2848ecb3797 - UMFontInterface: f0c5846977ee8a93d7cfa8ae7e666772c727d195 - UMImageLoaderInterface: 36e54e570acc4d720856f03ceebc441f73ea472c - UMPermissionsInterface: 938d010c74c43fcefc9bb990633a7c5a1631267e - UMReactNativeAdapter: 131ea2b944ade8035f0b54c6570c405f6000548d - UMSensorsInterface: 0ed023ce9b96f2ca6fada7bda05b7760da60b293 - UMTaskManagerInterface: 8664abd37a00715727e60df9ecd65e42ba47b548 - Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b + SDWebImage: 48b88379b798fd1e4298f95bb25d2cdabbf4deb3 + SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8 + UMAppLoader: ee77a072f9e15128f777ccd6d2d00f52ab4387e6 + UMBarCodeScannerInterface: 9dc692b87e5f20fe277fa57aa47f45d418c3cc6c + UMCameraInterface: 625878bbf2ba188a8548675e1d1d2e438a653e6d + UMConstantsInterface: 64060cf86587bcd90b1dbd804cceb6d377a308c1 + UMCore: eb200e882eadafcd31ead290770835fd648c0945 + UMFaceDetectorInterface: d6677d6ddc9ab95a0ca857aa7f8ba76656cc770f + UMFileSystemInterface: c70ea7147198b9807080f3597f26236be49b0165 + UMFontInterface: d9d3b27af698c5389ae9e20b99ef56a083f491fb + UMImageLoaderInterface: 14dd2c46c67167491effc9e91250e9510f12709e + UMPermissionsInterface: 5e83a9167c177e4a0f0a3539345983cc749efb3e + UMReactNativeAdapter: 126da3486c1a1f11945b649d557d6c2ebb9407b2 + UMSensorsInterface: 48941f70175e2975af1a9386c6d6cb16d8126805 + UMTaskManagerInterface: cb890c79c63885504ddc0efd7a7d01481760aca2 + Yoga: 3ebccbdd559724312790e7742142d062476b698e + YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 18d0b080112c72e9cc76a381c1baba1172c6ca4d +PODFILE CHECKSUM: 35d9478dd32cf502959b8efc14411ecf09c66c95 COCOAPODS: 1.8.4 diff --git a/ios/Pods/CocoaAsyncSocket/LICENSE.txt b/ios/Pods/CocoaAsyncSocket/LICENSE.txt new file mode 100644 index 000000000..ed3d60f80 --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/LICENSE.txt @@ -0,0 +1,35 @@ +This library is in the public domain. +However, not all organizations are allowed to use such a license. +For example, Germany doesn't recognize the Public Domain and one is not allowed to use libraries under such license (or similar). + +Thus, the library is now dual licensed, +and one is allowed to choose which license they would like to use. + +################################################## +License Option #1 : +################################################## + +Public Domain + +################################################## +License Option #2 : +################################################## + +Software License Agreement (BSD License) + +Copyright (c) 2017, Deusty, LLC +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Neither the name of Deusty LLC nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of Deusty LLC. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/ios/Pods/CocoaAsyncSocket/README.markdown b/ios/Pods/CocoaAsyncSocket/README.markdown new file mode 100644 index 000000000..155a8dab5 --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/README.markdown @@ -0,0 +1,121 @@ +# CocoaAsyncSocket +[![Build Status](https://travis-ci.org/robbiehanson/CocoaAsyncSocket.svg?branch=master)](https://travis-ci.org/robbiehanson/CocoaAsyncSocket) [![Version Status](https://img.shields.io/cocoapods/v/CocoaAsyncSocket.svg?style=flat)](http://cocoadocs.org/docsets/CocoaAsyncSocket) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Platform](http://img.shields.io/cocoapods/p/CocoaAsyncSocket.svg?style=flat)](http://cocoapods.org/?q=CocoaAsyncSocket) [![license Public Domain](https://img.shields.io/badge/license-Public%20Domain-orange.svg?style=flat)](https://en.wikipedia.org/wiki/Public_domain) + + +CocoaAsyncSocket provides easy-to-use and powerful asynchronous socket libraries for macOS, iOS, and tvOS. The classes are described below. + +## Installation + +#### CocoaPods + +Install using [CocoaPods](https://cocoapods.org) by adding this line to your Podfile: + +````ruby +use_frameworks! # Add this if you are targeting iOS 8+ or using Swift +pod 'CocoaAsyncSocket' +```` + +#### Carthage + +CocoaAsyncSocket is [Carthage](https://github.com/Carthage/Carthage) compatible. To include it add the following line to your `Cartfile` + +```bash +github "robbiehanson/CocoaAsyncSocket" "master" +``` + +The project is currently configured to build for **iOS**, **tvOS** and **Mac**. After building with carthage the resultant frameworks will be stored in: + +* `Carthage/Build/iOS/CocoaAsyncSocket.framework` +* `Carthage/Build/tvOS/CocoaAsyncSocket.framework` +* `Carthage/Build/Mac/CocoaAsyncSocket.framework` + +Select the correct framework(s) and drag it into your project. + +#### Swift Package Manager + +Simply add the package dependency to your Package.swift and depend on "CocoaAsyncSocket" in the necessary targets: +```swift +dependencies: [ + .package(url: "https://github.com/robbiehanson/CocoaAsyncSocket", from: "7.6.4") +] +``` + +#### Manual + +You can also include it into your project by adding the source files directly, but you should probably be using a dependency manager to keep up to date. + +### Importing + +Using Objective-C: + +```obj-c +// When using Clang Modules: +@import CocoaAsyncSocket; + +// or when not: +#import "GCDAsyncSocket.h" // for TCP +#import "GCDAsyncUdpSocket.h" // for UDP +``` + +Using Swift: + +```swift +import CocoaAsyncSocket +``` + +## TCP + +**GCDAsyncSocket** is a TCP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available: + +- Native Objective-C, fully self-contained in one class.<br/> + _No need to muck around with sockets or streams. This class handles everything for you._ + +- Full delegate support<br/> + _Errors, connections, read completions, write completions, progress, and disconnections all result in a call to your delegate method._ + +- Queued non-blocking reads and writes, with optional timeouts.<br/> + _You tell it what to read or write, and it handles everything for you. Queueing, buffering, and searching for termination sequences within the stream - all handled for you automatically._ + +- Automatic socket acceptance.<br/> + _Spin up a server socket, tell it to accept connections, and it will call you with new instances of itself for each connection._ + +- Support for TCP streams over IPv4 and IPv6.<br/> + _Automatically connect to IPv4 or IPv6 hosts. Automatically accept incoming connections over both IPv4 and IPv6 with a single instance of this class. No more worrying about multiple sockets._ + +- Support for TLS / SSL<br/> + _Secure your socket with ease using just a single method call. Available for both client and server sockets._ + +- Fully GCD based and Thread-Safe<br/> + _It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._ + +## UDP + +**GCDAsyncUdpSocket** is a UDP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available: + +- Native Objective-C, fully self-contained in one class.<br/> + _No need to muck around with low-level sockets. This class handles everything for you._ + +- Full delegate support.<br/> + _Errors, send completions, receive completions, and disconnections all result in a call to your delegate method._ + +- Queued non-blocking send and receive operations, with optional timeouts.<br/> + _You tell it what to send or receive, and it handles everything for you. Queueing, buffering, waiting and checking errno - all handled for you automatically._ + +- Support for IPv4 and IPv6.<br/> + _Automatically send/recv using IPv4 and/or IPv6. No more worrying about multiple sockets._ + +- Fully GCD based and Thread-Safe<br/> + _It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._ + +*** + +For those new(ish) to networking, it's recommended you **[read the wiki](https://github.com/robbiehanson/CocoaAsyncSocket/wiki)**.<br/>_Sockets might not work exactly like you think they do..._ + +**Still got questions?** Try the **[CocoaAsyncSocket Mailing List](https://groups.google.com/group/cocoaasyncsocket)**. +*** + +Love the project? Wanna buy me a ☕️  ? (or a 🍺  😀 ): + +[![donation-bitcoin](https://bitpay.com/img/donate-sm.png)](https://onename.com/robbiehanson) +[![donation-paypal](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2M8C699FQ8AW2) + diff --git a/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h new file mode 100644 index 000000000..f32a37b81 --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h @@ -0,0 +1,1225 @@ +// +// GCDAsyncSocket.h +// +// This class is in the public domain. +// Originally created by Robbie Hanson in Q3 2010. +// Updated and maintained by Deusty LLC and the Apple development community. +// +// https://github.com/robbiehanson/CocoaAsyncSocket +// + +#import <Foundation/Foundation.h> +#import <Security/Security.h> +#import <Security/SecureTransport.h> +#import <dispatch/dispatch.h> +#import <Availability.h> + +#include <sys/socket.h> // AF_INET, AF_INET6 + +@class GCDAsyncReadPacket; +@class GCDAsyncWritePacket; +@class GCDAsyncSocketPreBuffer; +@protocol GCDAsyncSocketDelegate; + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const GCDAsyncSocketException; +extern NSString *const GCDAsyncSocketErrorDomain; + +extern NSString *const GCDAsyncSocketQueueName; +extern NSString *const GCDAsyncSocketThreadName; + +extern NSString *const GCDAsyncSocketManuallyEvaluateTrust; +#if TARGET_OS_IPHONE +extern NSString *const GCDAsyncSocketUseCFStreamForTLS; +#endif +#define GCDAsyncSocketSSLPeerName (NSString *)kCFStreamSSLPeerName +#define GCDAsyncSocketSSLCertificates (NSString *)kCFStreamSSLCertificates +#define GCDAsyncSocketSSLIsServer (NSString *)kCFStreamSSLIsServer +extern NSString *const GCDAsyncSocketSSLPeerID; +extern NSString *const GCDAsyncSocketSSLProtocolVersionMin; +extern NSString *const GCDAsyncSocketSSLProtocolVersionMax; +extern NSString *const GCDAsyncSocketSSLSessionOptionFalseStart; +extern NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord; +extern NSString *const GCDAsyncSocketSSLCipherSuites; +#if !TARGET_OS_IPHONE +extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters; +#endif + +#define GCDAsyncSocketLoggingContext 65535 + + +typedef NS_ERROR_ENUM(GCDAsyncSocketErrorDomain, GCDAsyncSocketError) { + GCDAsyncSocketNoError = 0, // Never used + GCDAsyncSocketBadConfigError, // Invalid configuration + GCDAsyncSocketBadParamError, // Invalid parameter was passed + GCDAsyncSocketConnectTimeoutError, // A connect operation timed out + GCDAsyncSocketReadTimeoutError, // A read operation timed out + GCDAsyncSocketWriteTimeoutError, // A write operation timed out + GCDAsyncSocketReadMaxedOutError, // Reached set maxLength without completing + GCDAsyncSocketClosedError, // The remote peer closed the connection + GCDAsyncSocketOtherError, // Description provided in userInfo +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +@interface GCDAsyncSocket : NSObject + +/** + * GCDAsyncSocket uses the standard delegate paradigm, + * but executes all delegate callbacks on a given delegate dispatch queue. + * This allows for maximum concurrency, while at the same time providing easy thread safety. + * + * You MUST set a delegate AND delegate dispatch queue before attempting to + * use the socket, or you will get an error. + * + * The socket queue is optional. + * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue. + * If you choose to provide a socket queue, the socket queue must not be a concurrent queue. + * If you choose to provide a socket queue, and the socket queue has a configured target queue, + * then please see the discussion for the method markSocketQueueTargetQueue. + * + * The delegate queue and socket queue can optionally be the same. +**/ +- (instancetype)init; +- (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq; +- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq; +- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq NS_DESIGNATED_INITIALIZER; + +/** + * Create GCDAsyncSocket from already connect BSD socket file descriptor +**/ ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD socketQueue:(nullable dispatch_queue_t)sq error:(NSError**)error; + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error; + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq error:(NSError **)error; + +#pragma mark Configuration + +@property (atomic, weak, readwrite, nullable) id<GCDAsyncSocketDelegate> delegate; +#if OS_OBJECT_USE_OBJC +@property (atomic, strong, readwrite, nullable) dispatch_queue_t delegateQueue; +#else +@property (atomic, assign, readwrite, nullable) dispatch_queue_t delegateQueue; +#endif + +- (void)getDelegate:(id<GCDAsyncSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr; +- (void)setDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; + +/** + * If you are setting the delegate to nil within the delegate's dealloc method, + * you may need to use the synchronous versions below. +**/ +- (void)synchronouslySetDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate; +- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; + +/** + * By default, both IPv4 and IPv6 are enabled. + * + * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols, + * and can simulataneously accept incoming connections on either protocol. + * + * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol. + * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4. + * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6. + * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen. + * By default, the preferred protocol is IPv4, but may be configured as desired. +**/ + +@property (atomic, assign, readwrite, getter=isIPv4Enabled) BOOL IPv4Enabled; +@property (atomic, assign, readwrite, getter=isIPv6Enabled) BOOL IPv6Enabled; + +@property (atomic, assign, readwrite, getter=isIPv4PreferredOverIPv6) BOOL IPv4PreferredOverIPv6; + +/** + * When connecting to both IPv4 and IPv6 using Happy Eyeballs (RFC 6555) https://tools.ietf.org/html/rfc6555 + * this is the delay between connecting to the preferred protocol and the fallback protocol. + * + * Defaults to 300ms. +**/ +@property (atomic, assign, readwrite) NSTimeInterval alternateAddressDelay; + +/** + * User data allows you to associate arbitrary information with the socket. + * This data is not used internally by socket in any way. +**/ +@property (atomic, strong, readwrite, nullable) id userData; + +#pragma mark Accepting + +/** + * Tells the socket to begin listening and accepting connections on the given port. + * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, + * and the socket:didAcceptNewSocket: delegate method will be invoked. + * + * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) +**/ +- (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr; + +/** + * This method is the same as acceptOnPort:error: with the + * additional option of specifying which interface to listen on. + * + * For example, you could specify that the socket should only accept connections over ethernet, + * and not other interfaces such as wifi. + * + * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34"). + * You may also use the special strings "localhost" or "loopback" to specify that + * the socket only accept connections from the local machine. + * + * You can see the list of interfaces via the command line utility "ifconfig", + * or programmatically via the getifaddrs() function. + * + * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method. +**/ +- (BOOL)acceptOnInterface:(nullable NSString *)interface port:(uint16_t)port error:(NSError **)errPtr; + +/** + * Tells the socket to begin listening and accepting connections on the unix domain at the given url. + * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, + * and the socket:didAcceptNewSocket: delegate method will be invoked. + * + * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) + **/ +- (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr; + +#pragma mark Connecting + +/** + * Connects to the given host and port. + * + * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: + * and uses the default interface, and no timeout. +**/ +- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; + +/** + * Connects to the given host and port with an optional timeout. + * + * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface. +**/ +- (BOOL)connectToHost:(NSString *)host + onPort:(uint16_t)port + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr; + +/** + * Connects to the given host & port, via the optional interface, with an optional timeout. + * + * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). + * The host may also be the special strings "localhost" or "loopback" to specify connecting + * to a service on the local machine. + * + * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). + * The interface may also be used to specify the local port (see below). + * + * To not time out use a negative time interval. + * + * This method will return NO if an error is detected, and set the error pointer (if one was given). + * Possible errors would be a nil host, invalid interface, or socket is already connected. + * + * If no errors are detected, this method will start a background connect operation and immediately return YES. + * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. + * + * Since this class supports queued reads and writes, you can immediately start reading and/or writing. + * All read/write operations will be queued, and upon socket connection, + * the operations will be dequeued and processed in order. + * + * The interface may optionally contain a port number at the end of the string, separated by a colon. + * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) + * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". + * To specify only local port: ":8082". + * Please note this is an advanced feature, and is somewhat hidden on purpose. + * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. + * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. + * Local ports do NOT need to match remote ports. In fact, they almost never do. + * This feature is here for networking professionals using very advanced techniques. +**/ +- (BOOL)connectToHost:(NSString *)host + onPort:(uint16_t)port + viaInterface:(nullable NSString *)interface + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr; + +/** + * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object. + * For example, a NSData object returned from NSNetService's addresses method. + * + * If you have an existing struct sockaddr you can convert it to a NSData object like so: + * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; + * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; + * + * This method invokes connectToAddress:remoteAddr viaInterface:nil withTimeout:-1 error:errPtr. +**/ +- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; + +/** + * This method is the same as connectToAddress:error: with an additional timeout option. + * To not time out use a negative time interval, or simply use the connectToAddress:error: method. +**/ +- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; + +/** + * Connects to the given address, using the specified interface and timeout. + * + * The address is specified as a sockaddr structure wrapped in a NSData object. + * For example, a NSData object returned from NSNetService's addresses method. + * + * If you have an existing struct sockaddr you can convert it to a NSData object like so: + * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; + * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; + * + * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). + * The interface may also be used to specify the local port (see below). + * + * The timeout is optional. To not time out use a negative time interval. + * + * This method will return NO if an error is detected, and set the error pointer (if one was given). + * Possible errors would be a nil host, invalid interface, or socket is already connected. + * + * If no errors are detected, this method will start a background connect operation and immediately return YES. + * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. + * + * Since this class supports queued reads and writes, you can immediately start reading and/or writing. + * All read/write operations will be queued, and upon socket connection, + * the operations will be dequeued and processed in order. + * + * The interface may optionally contain a port number at the end of the string, separated by a colon. + * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) + * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". + * To specify only local port: ":8082". + * Please note this is an advanced feature, and is somewhat hidden on purpose. + * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. + * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. + * Local ports do NOT need to match remote ports. In fact, they almost never do. + * This feature is here for networking professionals using very advanced techniques. +**/ +- (BOOL)connectToAddress:(NSData *)remoteAddr + viaInterface:(nullable NSString *)interface + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr; +/** + * Connects to the unix domain socket at the given url, using the specified timeout. + */ +- (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; + +/** + * Iterates over the given NetService's addresses in order, and invokes connectToAddress:error:. Stops at the + * first invocation that succeeds and returns YES; otherwise returns NO. + */ +- (BOOL)connectToNetService:(NSNetService *)netService error:(NSError **)errPtr; + +#pragma mark Disconnecting + +/** + * Disconnects immediately (synchronously). Any pending reads or writes are dropped. + * + * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method + * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods). + * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns. + * + * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method) + * [asyncSocket setDelegate:nil]; + * [asyncSocket disconnect]; + * [asyncSocket release]; + * + * If you plan on disconnecting the socket, and then immediately asking it to connect again, + * you'll likely want to do so like this: + * [asyncSocket setDelegate:nil]; + * [asyncSocket disconnect]; + * [asyncSocket setDelegate:self]; + * [asyncSocket connect...]; +**/ +- (void)disconnect; + +/** + * Disconnects after all pending reads have completed. + * After calling this, the read and write methods will do nothing. + * The socket will disconnect even if there are still pending writes. +**/ +- (void)disconnectAfterReading; + +/** + * Disconnects after all pending writes have completed. + * After calling this, the read and write methods will do nothing. + * The socket will disconnect even if there are still pending reads. +**/ +- (void)disconnectAfterWriting; + +/** + * Disconnects after all pending reads and writes have completed. + * After calling this, the read and write methods will do nothing. +**/ +- (void)disconnectAfterReadingAndWriting; + +#pragma mark Diagnostics + +/** + * Returns whether the socket is disconnected or connected. + * + * A disconnected socket may be recycled. + * That is, it can be used again for connecting or listening. + * + * If a socket is in the process of connecting, it may be neither disconnected nor connected. +**/ +@property (atomic, readonly) BOOL isDisconnected; +@property (atomic, readonly) BOOL isConnected; + +/** + * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. + * The host will be an IP address. +**/ +@property (atomic, readonly, nullable) NSString *connectedHost; +@property (atomic, readonly) uint16_t connectedPort; +@property (atomic, readonly, nullable) NSURL *connectedUrl; + +@property (atomic, readonly, nullable) NSString *localHost; +@property (atomic, readonly) uint16_t localPort; + +/** + * Returns the local or remote address to which this socket is connected, + * specified as a sockaddr structure wrapped in a NSData object. + * + * @seealso connectedHost + * @seealso connectedPort + * @seealso localHost + * @seealso localPort +**/ +@property (atomic, readonly, nullable) NSData *connectedAddress; +@property (atomic, readonly, nullable) NSData *localAddress; + +/** + * Returns whether the socket is IPv4 or IPv6. + * An accepting socket may be both. +**/ +@property (atomic, readonly) BOOL isIPv4; +@property (atomic, readonly) BOOL isIPv6; + +/** + * Returns whether or not the socket has been secured via SSL/TLS. + * + * See also the startTLS method. +**/ +@property (atomic, readonly) BOOL isSecure; + +#pragma mark Reading + +// The readData and writeData methods won't block (they are asynchronous). +// +// When a read is complete the socket:didReadData:withTag: delegate method is dispatched on the delegateQueue. +// When a write is complete the socket:didWriteDataWithTag: delegate method is dispatched on the delegateQueue. +// +// You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.) +// If a read/write opertion times out, the corresponding "socket:shouldTimeout..." delegate method +// is called to optionally allow you to extend the timeout. +// Upon a timeout, the "socket:didDisconnectWithError:" method is called +// +// The tag is for your convenience. +// You can use it as an array index, step number, state id, pointer, etc. + +/** + * Reads the first available bytes that become available on the socket. + * + * If the timeout value is negative, the read operation will not use a timeout. +**/ +- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * Reads the first available bytes that become available on the socket. + * The bytes will be appended to the given byte buffer starting at the given offset. + * The given buffer will automatically be increased in size if needed. + * + * If the timeout value is negative, the read operation will not use a timeout. + * If the buffer is nil, the socket will create a buffer for you. + * + * If the bufferOffset is greater than the length of the given buffer, + * the method will do nothing, and the delegate will not be called. + * + * If you pass a buffer, you must not alter it in any way while the socket is using it. + * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. + * That is, it will reference the bytes that were appended to the given buffer via + * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. +**/ +- (void)readDataWithTimeout:(NSTimeInterval)timeout + buffer:(nullable NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag; + +/** + * Reads the first available bytes that become available on the socket. + * The bytes will be appended to the given byte buffer starting at the given offset. + * The given buffer will automatically be increased in size if needed. + * A maximum of length bytes will be read. + * + * If the timeout value is negative, the read operation will not use a timeout. + * If the buffer is nil, a buffer will automatically be created for you. + * If maxLength is zero, no length restriction is enforced. + * + * If the bufferOffset is greater than the length of the given buffer, + * the method will do nothing, and the delegate will not be called. + * + * If you pass a buffer, you must not alter it in any way while the socket is using it. + * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. + * That is, it will reference the bytes that were appended to the given buffer via + * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. +**/ +- (void)readDataWithTimeout:(NSTimeInterval)timeout + buffer:(nullable NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + maxLength:(NSUInteger)length + tag:(long)tag; + +/** + * Reads the given number of bytes. + * + * If the timeout value is negative, the read operation will not use a timeout. + * + * If the length is 0, this method does nothing and the delegate is not called. +**/ +- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * Reads the given number of bytes. + * The bytes will be appended to the given byte buffer starting at the given offset. + * The given buffer will automatically be increased in size if needed. + * + * If the timeout value is negative, the read operation will not use a timeout. + * If the buffer is nil, a buffer will automatically be created for you. + * + * If the length is 0, this method does nothing and the delegate is not called. + * If the bufferOffset is greater than the length of the given buffer, + * the method will do nothing, and the delegate will not be called. + * + * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. + * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. + * That is, it will reference the bytes that were appended to the given buffer via + * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. +**/ +- (void)readDataToLength:(NSUInteger)length + withTimeout:(NSTimeInterval)timeout + buffer:(nullable NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag; + +/** + * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. + * + * If the timeout value is negative, the read operation will not use a timeout. + * + * If you pass nil or zero-length data as the "data" parameter, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * + * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. + * If you're developing your own custom protocol, be sure your separator can not occur naturally as + * part of the data between separators. + * For example, imagine you want to send several small documents over a socket. + * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. + * In this particular example, it would be better to use a protocol similar to HTTP with + * a header that includes the length of the document. + * Also be careful that your separator cannot occur naturally as part of the encoding for a character. + * + * The given data (separator) parameter should be immutable. + * For performance reasons, the socket will retain it, not copy it. + * So if it is immutable, don't modify it while the socket is using it. +**/ +- (void)readDataToData:(nullable NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. + * The bytes will be appended to the given byte buffer starting at the given offset. + * The given buffer will automatically be increased in size if needed. + * + * If the timeout value is negative, the read operation will not use a timeout. + * If the buffer is nil, a buffer will automatically be created for you. + * + * If the bufferOffset is greater than the length of the given buffer, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * + * If you pass a buffer, you must not alter it in any way while the socket is using it. + * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. + * That is, it will reference the bytes that were appended to the given buffer via + * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. + * + * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. + * If you're developing your own custom protocol, be sure your separator can not occur naturally as + * part of the data between separators. + * For example, imagine you want to send several small documents over a socket. + * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. + * In this particular example, it would be better to use a protocol similar to HTTP with + * a header that includes the length of the document. + * Also be careful that your separator cannot occur naturally as part of the encoding for a character. + * + * The given data (separator) parameter should be immutable. + * For performance reasons, the socket will retain it, not copy it. + * So if it is immutable, don't modify it while the socket is using it. +**/ +- (void)readDataToData:(NSData *)data + withTimeout:(NSTimeInterval)timeout + buffer:(nullable NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag; + +/** + * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. + * + * If the timeout value is negative, the read operation will not use a timeout. + * + * If maxLength is zero, no length restriction is enforced. + * Otherwise if maxLength bytes are read without completing the read, + * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. + * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. + * + * If you pass nil or zero-length data as the "data" parameter, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * If you pass a maxLength parameter that is less than the length of the data parameter, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * + * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. + * If you're developing your own custom protocol, be sure your separator can not occur naturally as + * part of the data between separators. + * For example, imagine you want to send several small documents over a socket. + * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. + * In this particular example, it would be better to use a protocol similar to HTTP with + * a header that includes the length of the document. + * Also be careful that your separator cannot occur naturally as part of the encoding for a character. + * + * The given data (separator) parameter should be immutable. + * For performance reasons, the socket will retain it, not copy it. + * So if it is immutable, don't modify it while the socket is using it. +**/ +- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag; + +/** + * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. + * The bytes will be appended to the given byte buffer starting at the given offset. + * The given buffer will automatically be increased in size if needed. + * + * If the timeout value is negative, the read operation will not use a timeout. + * If the buffer is nil, a buffer will automatically be created for you. + * + * If maxLength is zero, no length restriction is enforced. + * Otherwise if maxLength bytes are read without completing the read, + * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. + * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. + * + * If you pass a maxLength parameter that is less than the length of the data (separator) parameter, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * If the bufferOffset is greater than the length of the given buffer, + * the method will do nothing (except maybe print a warning), and the delegate will not be called. + * + * If you pass a buffer, you must not alter it in any way while the socket is using it. + * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. + * That is, it will reference the bytes that were appended to the given buffer via + * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. + * + * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. + * If you're developing your own custom protocol, be sure your separator can not occur naturally as + * part of the data between separators. + * For example, imagine you want to send several small documents over a socket. + * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. + * In this particular example, it would be better to use a protocol similar to HTTP with + * a header that includes the length of the document. + * Also be careful that your separator cannot occur naturally as part of the encoding for a character. + * + * The given data (separator) parameter should be immutable. + * For performance reasons, the socket will retain it, not copy it. + * So if it is immutable, don't modify it while the socket is using it. +**/ +- (void)readDataToData:(NSData *)data + withTimeout:(NSTimeInterval)timeout + buffer:(nullable NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + maxLength:(NSUInteger)length + tag:(long)tag; + +/** + * Returns progress of the current read, from 0.0 to 1.0, or NaN if no current read (use isnan() to check). + * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. +**/ +- (float)progressOfReadReturningTag:(nullable long *)tagPtr bytesDone:(nullable NSUInteger *)donePtr total:(nullable NSUInteger *)totalPtr; + +#pragma mark Writing + +/** + * Writes data to the socket, and calls the delegate when finished. + * + * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. + * If the timeout value is negative, the write operation will not use a timeout. + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is writing it. In other words, it's not safe to alter the data until after the delegate method + * socket:didWriteDataWithTag: is invoked signifying that this particular write operation has completed. + * This is due to the fact that GCDAsyncSocket does NOT copy the data. It simply retains it. + * This is for performance reasons. Often times, if NSMutableData is passed, it is because + * a request/response was built up in memory. Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes writing the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. +**/ +- (void)writeData:(nullable NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * Returns progress of the current write, from 0.0 to 1.0, or NaN if no current write (use isnan() to check). + * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. +**/ +- (float)progressOfWriteReturningTag:(nullable long *)tagPtr bytesDone:(nullable NSUInteger *)donePtr total:(nullable NSUInteger *)totalPtr; + +#pragma mark Security + +/** + * Secures the connection using SSL/TLS. + * + * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes + * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing + * the upgrade to TLS at the same time, without having to wait for the write to finish. + * Any reads or writes scheduled after this method is called will occur over the secured connection. + * + * ==== The available TOP-LEVEL KEYS are: + * + * - GCDAsyncSocketManuallyEvaluateTrust + * The value must be of type NSNumber, encapsulating a BOOL value. + * If you set this to YES, then the underlying SecureTransport system will not evaluate the SecTrustRef of the peer. + * Instead it will pause at the moment evaulation would typically occur, + * and allow us to handle the security evaluation however we see fit. + * So GCDAsyncSocket will invoke the delegate method socket:shouldTrustPeer: passing the SecTrustRef. + * + * Note that if you set this option, then all other configuration keys are ignored. + * Evaluation will be completely up to you during the socket:didReceiveTrust:completionHandler: delegate method. + * + * For more information on trust evaluation see: + * Apple's Technical Note TN2232 - HTTPS Server Trust Evaluation + * https://developer.apple.com/library/ios/technotes/tn2232/_index.html + * + * If unspecified, the default value is NO. + * + * - GCDAsyncSocketUseCFStreamForTLS (iOS only) + * The value must be of type NSNumber, encapsulating a BOOL value. + * By default GCDAsyncSocket will use the SecureTransport layer to perform encryption. + * This gives us more control over the security protocol (many more configuration options), + * plus it allows us to optimize things like sys calls and buffer allocation. + * + * However, if you absolutely must, you can instruct GCDAsyncSocket to use the old-fashioned encryption + * technique by going through the CFStream instead. So instead of using SecureTransport, GCDAsyncSocket + * will instead setup a CFRead/CFWriteStream. And then set the kCFStreamPropertySSLSettings property + * (via CFReadStreamSetProperty / CFWriteStreamSetProperty) and will pass the given options to this method. + * + * Thus all the other keys in the given dictionary will be ignored by GCDAsyncSocket, + * and will passed directly CFReadStreamSetProperty / CFWriteStreamSetProperty. + * For more infomation on these keys, please see the documentation for kCFStreamPropertySSLSettings. + * + * If unspecified, the default value is NO. + * + * ==== The available CONFIGURATION KEYS are: + * + * - kCFStreamSSLPeerName + * The value must be of type NSString. + * It should match the name in the X.509 certificate given by the remote party. + * See Apple's documentation for SSLSetPeerDomainName. + * + * - kCFStreamSSLCertificates + * The value must be of type NSArray. + * See Apple's documentation for SSLSetCertificate. + * + * - kCFStreamSSLIsServer + * The value must be of type NSNumber, encapsulationg a BOOL value. + * See Apple's documentation for SSLCreateContext for iOS. + * This is optional for iOS. If not supplied, a NO value is the default. + * This is not needed for Mac OS X, and the value is ignored. + * + * - GCDAsyncSocketSSLPeerID + * The value must be of type NSData. + * You must set this value if you want to use TLS session resumption. + * See Apple's documentation for SSLSetPeerID. + * + * - GCDAsyncSocketSSLProtocolVersionMin + * - GCDAsyncSocketSSLProtocolVersionMax + * The value(s) must be of type NSNumber, encapsulting a SSLProtocol value. + * See Apple's documentation for SSLSetProtocolVersionMin & SSLSetProtocolVersionMax. + * See also the SSLProtocol typedef. + * + * - GCDAsyncSocketSSLSessionOptionFalseStart + * The value must be of type NSNumber, encapsulating a BOOL value. + * See Apple's documentation for kSSLSessionOptionFalseStart. + * + * - GCDAsyncSocketSSLSessionOptionSendOneByteRecord + * The value must be of type NSNumber, encapsulating a BOOL value. + * See Apple's documentation for kSSLSessionOptionSendOneByteRecord. + * + * - GCDAsyncSocketSSLCipherSuites + * The values must be of type NSArray. + * Each item within the array must be a NSNumber, encapsulating an SSLCipherSuite. + * See Apple's documentation for SSLSetEnabledCiphers. + * See also the SSLCipherSuite typedef. + * + * - GCDAsyncSocketSSLDiffieHellmanParameters (Mac OS X only) + * The value must be of type NSData. + * See Apple's documentation for SSLSetDiffieHellmanParams. + * + * ==== The following UNAVAILABLE KEYS are: (with throw an exception) + * + * - kCFStreamSSLAllowsAnyRoot (UNAVAILABLE) + * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). + * Corresponding deprecated method: SSLSetAllowsAnyRoot + * + * - kCFStreamSSLAllowsExpiredRoots (UNAVAILABLE) + * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). + * Corresponding deprecated method: SSLSetAllowsExpiredRoots + * + * - kCFStreamSSLAllowsExpiredCertificates (UNAVAILABLE) + * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). + * Corresponding deprecated method: SSLSetAllowsExpiredCerts + * + * - kCFStreamSSLValidatesCertificateChain (UNAVAILABLE) + * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). + * Corresponding deprecated method: SSLSetEnableCertVerify + * + * - kCFStreamSSLLevel (UNAVAILABLE) + * You MUST use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMin instead. + * Corresponding deprecated method: SSLSetProtocolVersionEnabled + * + * + * Please refer to Apple's documentation for corresponding SSLFunctions. + * + * If you pass in nil or an empty dictionary, the default settings will be used. + * + * IMPORTANT SECURITY NOTE: + * The default settings will check to make sure the remote party's certificate is signed by a + * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired. + * However it will not verify the name on the certificate unless you + * give it a name to verify against via the kCFStreamSSLPeerName key. + * The security implications of this are important to understand. + * Imagine you are attempting to create a secure connection to MySecureServer.com, + * but your socket gets directed to MaliciousServer.com because of a hacked DNS server. + * If you simply use the default settings, and MaliciousServer.com has a valid certificate, + * the default settings will not detect any problems since the certificate is valid. + * To properly secure your connection in this particular scenario you + * should set the kCFStreamSSLPeerName property to "MySecureServer.com". + * + * You can also perform additional validation in socketDidSecure. +**/ +- (void)startTLS:(nullable NSDictionary <NSString*,NSObject*>*)tlsSettings; + +#pragma mark Advanced + +/** + * Traditionally sockets are not closed until the conversation is over. + * However, it is technically possible for the remote enpoint to close its write stream. + * Our socket would then be notified that there is no more data to be read, + * but our socket would still be writeable and the remote endpoint could continue to receive our data. + * + * The argument for this confusing functionality stems from the idea that a client could shut down its + * write stream after sending a request to the server, thus notifying the server there are to be no further requests. + * In practice, however, this technique did little to help server developers. + * + * To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close + * and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell + * is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work. + * Otherwise an error will be occur shortly (when the remote end sends us a RST packet). + * + * In addition to the technical challenges and confusion, many high level socket/stream API's provide + * no support for dealing with the problem. If the read stream is closed, the API immediately declares the + * socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does. + * It might sound like poor design at first, but in fact it simplifies development. + * + * The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket. + * Thus it actually makes sense to close the socket at this point. + * And in fact this is what most networking developers want and expect to happen. + * However, if you are writing a server that interacts with a plethora of clients, + * you might encounter a client that uses the discouraged technique of shutting down its write stream. + * If this is the case, you can set this property to NO, + * and make use of the socketDidCloseReadStream delegate method. + * + * The default value is YES. +**/ +@property (atomic, assign, readwrite) BOOL autoDisconnectOnClosedReadStream; + +/** + * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. + * In most cases, the instance creates this queue itself. + * However, to allow for maximum flexibility, the internal queue may be passed in the init method. + * This allows for some advanced options such as controlling socket priority via target queues. + * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. + * + * For example, imagine there are 2 queues: + * dispatch_queue_t socketQueue; + * dispatch_queue_t socketTargetQueue; + * + * If you do this (pseudo-code): + * socketQueue.targetQueue = socketTargetQueue; + * + * Then all socketQueue operations will actually get run on the given socketTargetQueue. + * This is fine and works great in most situations. + * But if you run code directly from within the socketTargetQueue that accesses the socket, + * you could potentially get deadlock. Imagine the following code: + * + * - (BOOL)socketHasSomething + * { + * __block BOOL result = NO; + * dispatch_block_t block = ^{ + * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; + * } + * if (is_executing_on_queue(socketQueue)) + * block(); + * else + * dispatch_sync(socketQueue, block); + * + * return result; + * } + * + * What happens if you call this method from the socketTargetQueue? The result is deadlock. + * This is because the GCD API offers no mechanism to discover a queue's targetQueue. + * Thus we have no idea if our socketQueue is configured with a targetQueue. + * If we had this information, we could easily avoid deadlock. + * But, since these API's are missing or unfeasible, you'll have to explicitly set it. + * + * IF you pass a socketQueue via the init method, + * AND you've configured the passed socketQueue with a targetQueue, + * THEN you should pass the end queue in the target hierarchy. + * + * For example, consider the following queue hierarchy: + * socketQueue -> ipQueue -> moduleQueue + * + * This example demonstrates priority shaping within some server. + * All incoming client connections from the same IP address are executed on the same target queue. + * And all connections for a particular module are executed on the same target queue. + * Thus, the priority of all networking for the entire module can be changed on the fly. + * Additionally, networking traffic from a single IP cannot monopolize the module. + * + * Here's how you would accomplish something like that: + * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock + * { + * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); + * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; + * + * dispatch_set_target_queue(socketQueue, ipQueue); + * dispatch_set_target_queue(iqQueue, moduleQueue); + * + * return socketQueue; + * } + * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket + * { + * [clientConnections addObject:newSocket]; + * [newSocket markSocketQueueTargetQueue:moduleQueue]; + * } + * + * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. + * This is often NOT the case, as such queues are used solely for execution shaping. +**/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; + +/** + * It's not thread-safe to access certain variables from outside the socket's internal queue. + * + * For example, the socket file descriptor. + * File descriptors are simply integers which reference an index in the per-process file table. + * However, when one requests a new file descriptor (by opening a file or socket), + * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. + * So if we're not careful, the following could be possible: + * + * - Thread A invokes a method which returns the socket's file descriptor. + * - The socket is closed via the socket's internal queue on thread B. + * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. + * - Thread A is now accessing/altering the file instead of the socket. + * + * In addition to this, other variables are not actually objects, + * and thus cannot be retained/released or even autoreleased. + * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. + * + * Although there are internal variables that make it difficult to maintain thread-safety, + * it is important to provide access to these variables + * to ensure this class can be used in a wide array of environments. + * This method helps to accomplish this by invoking the current block on the socket's internal queue. + * The methods below can be invoked from within the block to access + * those generally thread-unsafe internal variables in a thread-safe manner. + * The given block will be invoked synchronously on the socket's internal queue. + * + * If you save references to any protected variables and use them outside the block, you do so at your own peril. +**/ +- (void)performBlock:(dispatch_block_t)block; + +/** + * These methods are only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Provides access to the socket's file descriptor(s). + * If the socket is a server socket (is accepting incoming connections), + * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. +**/ +- (int)socketFD; +- (int)socket4FD; +- (int)socket6FD; + +#if TARGET_OS_IPHONE + +/** + * These methods are only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Provides access to the socket's internal CFReadStream/CFWriteStream. + * + * These streams are only used as workarounds for specific iOS shortcomings: + * + * - Apple has decided to keep the SecureTransport framework private is iOS. + * This means the only supplied way to do SSL/TLS is via CFStream or some other API layered on top of it. + * Thus, in order to provide SSL/TLS support on iOS we are forced to rely on CFStream, + * instead of the preferred and faster and more powerful SecureTransport. + * + * - If a socket doesn't have backgrounding enabled, and that socket is closed while the app is backgrounded, + * Apple only bothers to notify us via the CFStream API. + * The faster and more powerful GCD API isn't notified properly in this case. + * + * See also: (BOOL)enableBackgroundingOnSocket +**/ +- (nullable CFReadStreamRef)readStream; +- (nullable CFWriteStreamRef)writeStream; + +/** + * This method is only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Configures the socket to allow it to operate when the iOS application has been backgrounded. + * In other words, this method creates a read & write stream, and invokes: + * + * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + * + * Returns YES if successful, NO otherwise. + * + * Note: Apple does not officially support backgrounding server sockets. + * That is, if your socket is accepting incoming connections, Apple does not officially support + * allowing iOS applications to accept incoming connections while an app is backgrounded. + * + * Example usage: + * + * - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port + * { + * [asyncSocket performBlock:^{ + * [asyncSocket enableBackgroundingOnSocket]; + * }]; + * } +**/ +- (BOOL)enableBackgroundingOnSocket; + +#endif + +/** + * This method is only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Provides access to the socket's SSLContext, if SSL/TLS has been started on the socket. +**/ +- (nullable SSLContextRef)sslContext; + +#pragma mark Utilities + +/** + * The address lookup utility used by the class. + * This method is synchronous, so it's recommended you use it on a background thread/queue. + * + * The special strings "localhost" and "loopback" return the loopback address for IPv4 and IPv6. + * + * @returns + * A mutable array with all IPv4 and IPv6 addresses returned by getaddrinfo. + * The addresses are specifically for TCP connections. + * You can filter the addresses, if needed, using the other utility methods provided by the class. +**/ ++ (nullable NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr; + +/** + * Extracting host and port information from raw address data. +**/ + ++ (nullable NSString *)hostFromAddress:(NSData *)address; ++ (uint16_t)portFromAddress:(NSData *)address; + ++ (BOOL)isIPv4Address:(NSData *)address; ++ (BOOL)isIPv6Address:(NSData *)address; + ++ (BOOL)getHost:( NSString * __nullable * __nullable)hostPtr port:(nullable uint16_t *)portPtr fromAddress:(NSData *)address; + ++ (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(nullable uint16_t *)portPtr family:(nullable sa_family_t *)afPtr fromAddress:(NSData *)address; + +/** + * A few common line separators, for use with the readDataToData:... methods. +**/ ++ (NSData *)CRLFData; // 0x0D0A ++ (NSData *)CRData; // 0x0D ++ (NSData *)LFData; // 0x0A ++ (NSData *)ZeroData; // 0x00 + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol GCDAsyncSocketDelegate <NSObject> +@optional + +/** + * This method is called immediately prior to socket:didAcceptNewSocket:. + * It optionally allows a listening socket to specify the socketQueue for a new accepted socket. + * If this method is not implemented, or returns NULL, the new accepted socket will create its own default queue. + * + * Since you cannot autorelease a dispatch_queue, + * this method uses the "new" prefix in its name to specify that the returned queue has been retained. + * + * Thus you could do something like this in the implementation: + * return dispatch_queue_create("MyQueue", NULL); + * + * If you are placing multiple sockets on the same queue, + * then care should be taken to increment the retain count each time this method is invoked. + * + * For example, your implementation might look something like this: + * dispatch_retain(myExistingQueue); + * return myExistingQueue; +**/ +- (nullable dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock; + +/** + * Called when a socket accepts a connection. + * Another socket is automatically spawned to handle it. + * + * You must retain the newSocket if you wish to handle the connection. + * Otherwise the newSocket instance will be released and the spawned connection will be closed. + * + * By default the new socket will have the same delegate and delegateQueue. + * You may, of course, change this at any time. +**/ +- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket; + +/** + * Called when a socket connects and is ready for reading and writing. + * The host parameter will be an IP address, not a DNS name. +**/ +- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; + +/** + * Called when a socket connects and is ready for reading and writing. + * The host parameter will be an IP address, not a DNS name. + **/ +- (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url; + +/** + * Called when a socket has completed reading the requested data into memory. + * Not called if there is an error. +**/ +- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; + +/** + * Called when a socket has read in data, but has not yet completed the read. + * This would occur if using readToData: or readToLength: methods. + * It may be used for things such as updating progress bars. +**/ +- (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; + +/** + * Called when a socket has completed writing the requested data. Not called if there is an error. +**/ +- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag; + +/** + * Called when a socket has written some data, but has not yet completed the entire write. + * It may be used for things such as updating progress bars. +**/ +- (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; + +/** + * Called if a read operation has reached its timeout without completing. + * This method allows you to optionally extend the timeout. + * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount. + * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual. + * + * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. + * The length parameter is the number of bytes that have been read so far for the read operation. + * + * Note that this method may be called multiple times for a single read if you return positive numbers. +**/ +- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag + elapsed:(NSTimeInterval)elapsed + bytesDone:(NSUInteger)length; + +/** + * Called if a write operation has reached its timeout without completing. + * This method allows you to optionally extend the timeout. + * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount. + * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual. + * + * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. + * The length parameter is the number of bytes that have been written so far for the write operation. + * + * Note that this method may be called multiple times for a single write if you return positive numbers. +**/ +- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag + elapsed:(NSTimeInterval)elapsed + bytesDone:(NSUInteger)length; + +/** + * Conditionally called if the read stream closes, but the write stream may still be writeable. + * + * This delegate method is only called if autoDisconnectOnClosedReadStream has been set to NO. + * See the discussion on the autoDisconnectOnClosedReadStream method for more information. +**/ +- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock; + +/** + * Called when a socket disconnects with or without error. + * + * If you call the disconnect method, and the socket wasn't already disconnected, + * then an invocation of this delegate method will be enqueued on the delegateQueue + * before the disconnect method returns. + * + * Note: If the GCDAsyncSocket instance is deallocated while it is still connected, + * and the delegate is not also deallocated, then this method will be invoked, + * but the sock parameter will be nil. (It must necessarily be nil since it is no longer available.) + * This is a generally rare, but is possible if one writes code like this: + * + * asyncSocket = nil; // I'm implicitly disconnecting the socket + * + * In this case it may preferrable to nil the delegate beforehand, like this: + * + * asyncSocket.delegate = nil; // Don't invoke my delegate method + * asyncSocket = nil; // I'm implicitly disconnecting the socket + * + * Of course, this depends on how your state machine is configured. +**/ +- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err; + +/** + * Called after the socket has successfully completed SSL/TLS negotiation. + * This method is not called unless you use the provided startTLS method. + * + * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close, + * and the socketDidDisconnect:withError: delegate method will be called with the specific SSL error code. +**/ +- (void)socketDidSecure:(GCDAsyncSocket *)sock; + +/** + * Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to. + * + * This is only called if startTLS is invoked with options that include: + * - GCDAsyncSocketManuallyEvaluateTrust == YES + * + * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer. + * + * Note from Apple's documentation: + * Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain, + * [it] might block while attempting network access. You should never call it from your main thread; + * call it only from within a function running on a dispatch queue or on a separate thread. + * + * Thus this method uses a completionHandler block rather than a normal return value. + * The completionHandler block is thread-safe, and may be invoked from a background queue/thread. + * It is safe to invoke the completionHandler block even if the socket has been closed. +**/ +- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust + completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler; + +@end +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m new file mode 100755 index 000000000..1bbbaf4fa --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m @@ -0,0 +1,8495 @@ +// +// GCDAsyncSocket.m +// +// This class is in the public domain. +// Originally created by Robbie Hanson in Q4 2010. +// Updated and maintained by Deusty LLC and the Apple development community. +// +// https://github.com/robbiehanson/CocoaAsyncSocket +// + +#import "GCDAsyncSocket.h" + +#if TARGET_OS_IPHONE +#import <CFNetwork/CFNetwork.h> +#endif + +#import <TargetConditionals.h> +#import <arpa/inet.h> +#import <fcntl.h> +#import <ifaddrs.h> +#import <netdb.h> +#import <netinet/in.h> +#import <net/if.h> +#import <sys/socket.h> +#import <sys/types.h> +#import <sys/ioctl.h> +#import <sys/poll.h> +#import <sys/uio.h> +#import <sys/un.h> +#import <unistd.h> + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +// For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC +#endif + + +#ifndef GCDAsyncSocketLoggingEnabled +#define GCDAsyncSocketLoggingEnabled 0 +#endif + +#if GCDAsyncSocketLoggingEnabled + +// Logging Enabled - See log level below + +// Logging uses the CocoaLumberjack framework (which is also GCD based). +// https://github.com/robbiehanson/CocoaLumberjack +// +// It allows us to do a lot of logging without significantly slowing down the code. +#import "DDLog.h" + +#define LogAsync YES +#define LogContext GCDAsyncSocketLoggingContext + +#define LogObjc(flg, frmt, ...) LOG_OBJC_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) +#define LogC(flg, frmt, ...) LOG_C_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) + +#define LogError(frmt, ...) LogObjc(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogWarn(frmt, ...) LogObjc(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogInfo(frmt, ...) LogObjc(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogVerbose(frmt, ...) LogObjc(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) + +#define LogCError(frmt, ...) LogC(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCWarn(frmt, ...) LogC(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCInfo(frmt, ...) LogC(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCVerbose(frmt, ...) LogC(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) + +#define LogTrace() LogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD) +#define LogCTrace() LogC(LOG_FLAG_VERBOSE, @"%@: %s", THIS_FILE, __FUNCTION__) + +#ifndef GCDAsyncSocketLogLevel +#define GCDAsyncSocketLogLevel LOG_LEVEL_VERBOSE +#endif + +// Log levels : off, error, warn, info, verbose +static const int logLevel = GCDAsyncSocketLogLevel; + +#else + +// Logging Disabled + +#define LogError(frmt, ...) {} +#define LogWarn(frmt, ...) {} +#define LogInfo(frmt, ...) {} +#define LogVerbose(frmt, ...) {} + +#define LogCError(frmt, ...) {} +#define LogCWarn(frmt, ...) {} +#define LogCInfo(frmt, ...) {} +#define LogCVerbose(frmt, ...) {} + +#define LogTrace() {} +#define LogCTrace(frmt, ...) {} + +#endif + +/** + * Seeing a return statements within an inner block + * can sometimes be mistaken for a return point of the enclosing method. + * This makes inline blocks a bit easier to read. +**/ +#define return_from_block return + +/** + * A socket file descriptor is really just an integer. + * It represents the index of the socket within the kernel. + * This makes invalid file descriptor comparisons easier to read. +**/ +#define SOCKET_NULL -1 + + +NSString *const GCDAsyncSocketException = @"GCDAsyncSocketException"; +NSString *const GCDAsyncSocketErrorDomain = @"GCDAsyncSocketErrorDomain"; + +NSString *const GCDAsyncSocketQueueName = @"GCDAsyncSocket"; +NSString *const GCDAsyncSocketThreadName = @"GCDAsyncSocket-CFStream"; + +NSString *const GCDAsyncSocketManuallyEvaluateTrust = @"GCDAsyncSocketManuallyEvaluateTrust"; +#if TARGET_OS_IPHONE +NSString *const GCDAsyncSocketUseCFStreamForTLS = @"GCDAsyncSocketUseCFStreamForTLS"; +#endif +NSString *const GCDAsyncSocketSSLPeerID = @"GCDAsyncSocketSSLPeerID"; +NSString *const GCDAsyncSocketSSLProtocolVersionMin = @"GCDAsyncSocketSSLProtocolVersionMin"; +NSString *const GCDAsyncSocketSSLProtocolVersionMax = @"GCDAsyncSocketSSLProtocolVersionMax"; +NSString *const GCDAsyncSocketSSLSessionOptionFalseStart = @"GCDAsyncSocketSSLSessionOptionFalseStart"; +NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord = @"GCDAsyncSocketSSLSessionOptionSendOneByteRecord"; +NSString *const GCDAsyncSocketSSLCipherSuites = @"GCDAsyncSocketSSLCipherSuites"; +#if !TARGET_OS_IPHONE +NSString *const GCDAsyncSocketSSLDiffieHellmanParameters = @"GCDAsyncSocketSSLDiffieHellmanParameters"; +#endif + +enum GCDAsyncSocketFlags +{ + kSocketStarted = 1 << 0, // If set, socket has been started (accepting/connecting) + kConnected = 1 << 1, // If set, the socket is connected + kForbidReadsWrites = 1 << 2, // If set, no new reads or writes are allowed + kReadsPaused = 1 << 3, // If set, reads are paused due to possible timeout + kWritesPaused = 1 << 4, // If set, writes are paused due to possible timeout + kDisconnectAfterReads = 1 << 5, // If set, disconnect after no more reads are queued + kDisconnectAfterWrites = 1 << 6, // If set, disconnect after no more writes are queued + kSocketCanAcceptBytes = 1 << 7, // If set, we know socket can accept bytes. If unset, it's unknown. + kReadSourceSuspended = 1 << 8, // If set, the read source is suspended + kWriteSourceSuspended = 1 << 9, // If set, the write source is suspended + kQueuedTLS = 1 << 10, // If set, we've queued an upgrade to TLS + kStartingReadTLS = 1 << 11, // If set, we're waiting for TLS negotiation to complete + kStartingWriteTLS = 1 << 12, // If set, we're waiting for TLS negotiation to complete + kSocketSecure = 1 << 13, // If set, socket is using secure communication via SSL/TLS + kSocketHasReadEOF = 1 << 14, // If set, we have read EOF from socket + kReadStreamClosed = 1 << 15, // If set, we've read EOF plus prebuffer has been drained + kDealloc = 1 << 16, // If set, the socket is being deallocated +#if TARGET_OS_IPHONE + kAddedStreamsToRunLoop = 1 << 17, // If set, CFStreams have been added to listener thread + kUsingCFStreamForTLS = 1 << 18, // If set, we're forced to use CFStream instead of SecureTransport + kSecureSocketHasBytesAvailable = 1 << 19, // If set, CFReadStream has notified us of bytes available +#endif +}; + +enum GCDAsyncSocketConfig +{ + kIPv4Disabled = 1 << 0, // If set, IPv4 is disabled + kIPv6Disabled = 1 << 1, // If set, IPv6 is disabled + kPreferIPv6 = 1 << 2, // If set, IPv6 is preferred over IPv4 + kAllowHalfDuplexConnection = 1 << 3, // If set, the socket will stay open even if the read stream closes +}; + +#if TARGET_OS_IPHONE + static NSThread *cfstreamThread; // Used for CFStreams + + + static uint64_t cfstreamThreadRetainCount; // setup & teardown + static dispatch_queue_t cfstreamThreadSetupQueue; // setup & teardown +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * A PreBuffer is used when there is more data available on the socket + * than is being requested by current read request. + * In this case we slurp up all data from the socket (to minimize sys calls), + * and store additional yet unread data in a "prebuffer". + * + * The prebuffer is entirely drained before we read from the socket again. + * In other words, a large chunk of data is written is written to the prebuffer. + * The prebuffer is then drained via a series of one or more reads (for subsequent read request(s)). + * + * A ring buffer was once used for this purpose. + * But a ring buffer takes up twice as much memory as needed (double the size for mirroring). + * In fact, it generally takes up more than twice the needed size as everything has to be rounded up to vm_page_size. + * And since the prebuffer is always completely drained after being written to, a full ring buffer isn't needed. + * + * The current design is very simple and straight-forward, while also keeping memory requirements lower. +**/ + +@interface GCDAsyncSocketPreBuffer : NSObject +{ + uint8_t *preBuffer; + size_t preBufferSize; + + uint8_t *readPointer; + uint8_t *writePointer; +} + +- (instancetype)initWithCapacity:(size_t)numBytes NS_DESIGNATED_INITIALIZER; + +- (void)ensureCapacityForWrite:(size_t)numBytes; + +- (size_t)availableBytes; +- (uint8_t *)readBuffer; + +- (void)getReadBuffer:(uint8_t **)bufferPtr availableBytes:(size_t *)availableBytesPtr; + +- (size_t)availableSpace; +- (uint8_t *)writeBuffer; + +- (void)getWriteBuffer:(uint8_t **)bufferPtr availableSpace:(size_t *)availableSpacePtr; + +- (void)didRead:(size_t)bytesRead; +- (void)didWrite:(size_t)bytesWritten; + +- (void)reset; + +@end + +@implementation GCDAsyncSocketPreBuffer + +// Cover the superclass' designated initializer +- (instancetype)init NS_UNAVAILABLE +{ + NSAssert(0, @"Use the designated initializer"); + return nil; +} + +- (instancetype)initWithCapacity:(size_t)numBytes +{ + if ((self = [super init])) + { + preBufferSize = numBytes; + preBuffer = malloc(preBufferSize); + + readPointer = preBuffer; + writePointer = preBuffer; + } + return self; +} + +- (void)dealloc +{ + if (preBuffer) + free(preBuffer); +} + +- (void)ensureCapacityForWrite:(size_t)numBytes +{ + size_t availableSpace = [self availableSpace]; + + if (numBytes > availableSpace) + { + size_t additionalBytes = numBytes - availableSpace; + + size_t newPreBufferSize = preBufferSize + additionalBytes; + uint8_t *newPreBuffer = realloc(preBuffer, newPreBufferSize); + + size_t readPointerOffset = readPointer - preBuffer; + size_t writePointerOffset = writePointer - preBuffer; + + preBuffer = newPreBuffer; + preBufferSize = newPreBufferSize; + + readPointer = preBuffer + readPointerOffset; + writePointer = preBuffer + writePointerOffset; + } +} + +- (size_t)availableBytes +{ + return writePointer - readPointer; +} + +- (uint8_t *)readBuffer +{ + return readPointer; +} + +- (void)getReadBuffer:(uint8_t **)bufferPtr availableBytes:(size_t *)availableBytesPtr +{ + if (bufferPtr) *bufferPtr = readPointer; + if (availableBytesPtr) *availableBytesPtr = [self availableBytes]; +} + +- (void)didRead:(size_t)bytesRead +{ + readPointer += bytesRead; + + if (readPointer == writePointer) + { + // The prebuffer has been drained. Reset pointers. + readPointer = preBuffer; + writePointer = preBuffer; + } +} + +- (size_t)availableSpace +{ + return preBufferSize - (writePointer - preBuffer); +} + +- (uint8_t *)writeBuffer +{ + return writePointer; +} + +- (void)getWriteBuffer:(uint8_t **)bufferPtr availableSpace:(size_t *)availableSpacePtr +{ + if (bufferPtr) *bufferPtr = writePointer; + if (availableSpacePtr) *availableSpacePtr = [self availableSpace]; +} + +- (void)didWrite:(size_t)bytesWritten +{ + writePointer += bytesWritten; +} + +- (void)reset +{ + readPointer = preBuffer; + writePointer = preBuffer; +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The GCDAsyncReadPacket encompasses the instructions for any given read. + * The content of a read packet allows the code to determine if we're: + * - reading to a certain length + * - reading to a certain separator + * - or simply reading the first chunk of available data +**/ +@interface GCDAsyncReadPacket : NSObject +{ + @public + NSMutableData *buffer; + NSUInteger startOffset; + NSUInteger bytesDone; + NSUInteger maxLength; + NSTimeInterval timeout; + NSUInteger readLength; + NSData *term; + BOOL bufferOwner; + NSUInteger originalBufferLength; + long tag; +} +- (instancetype)initWithData:(NSMutableData *)d + startOffset:(NSUInteger)s + maxLength:(NSUInteger)m + timeout:(NSTimeInterval)t + readLength:(NSUInteger)l + terminator:(NSData *)e + tag:(long)i NS_DESIGNATED_INITIALIZER; + +- (void)ensureCapacityForAdditionalDataOfLength:(NSUInteger)bytesToRead; + +- (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr; + +- (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable; +- (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr; +- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr; + +- (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes; + +@end + +@implementation GCDAsyncReadPacket + +// Cover the superclass' designated initializer +- (instancetype)init NS_UNAVAILABLE +{ + NSAssert(0, @"Use the designated initializer"); + return nil; +} + +- (instancetype)initWithData:(NSMutableData *)d + startOffset:(NSUInteger)s + maxLength:(NSUInteger)m + timeout:(NSTimeInterval)t + readLength:(NSUInteger)l + terminator:(NSData *)e + tag:(long)i +{ + if((self = [super init])) + { + bytesDone = 0; + maxLength = m; + timeout = t; + readLength = l; + term = [e copy]; + tag = i; + + if (d) + { + buffer = d; + startOffset = s; + bufferOwner = NO; + originalBufferLength = [d length]; + } + else + { + if (readLength > 0) + buffer = [[NSMutableData alloc] initWithLength:readLength]; + else + buffer = [[NSMutableData alloc] initWithLength:0]; + + startOffset = 0; + bufferOwner = YES; + originalBufferLength = 0; + } + } + return self; +} + +/** + * Increases the length of the buffer (if needed) to ensure a read of the given size will fit. +**/ +- (void)ensureCapacityForAdditionalDataOfLength:(NSUInteger)bytesToRead +{ + NSUInteger buffSize = [buffer length]; + NSUInteger buffUsed = startOffset + bytesDone; + + NSUInteger buffSpace = buffSize - buffUsed; + + if (bytesToRead > buffSpace) + { + NSUInteger buffInc = bytesToRead - buffSpace; + + [buffer increaseLengthBy:buffInc]; + } +} + +/** + * This method is used when we do NOT know how much data is available to be read from the socket. + * This method returns the default value unless it exceeds the specified readLength or maxLength. + * + * Furthermore, the shouldPreBuffer decision is based upon the packet type, + * and whether the returned value would fit in the current buffer without requiring a resize of the buffer. +**/ +- (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr +{ + NSUInteger result; + + if (readLength > 0) + { + // Read a specific length of data + result = readLength - bytesDone; + + // There is no need to prebuffer since we know exactly how much data we need to read. + // Even if the buffer isn't currently big enough to fit this amount of data, + // it would have to be resized eventually anyway. + + if (shouldPreBufferPtr) + *shouldPreBufferPtr = NO; + } + else + { + // Either reading until we find a specified terminator, + // or we're simply reading all available data. + // + // In other words, one of: + // + // - readDataToData packet + // - readDataWithTimeout packet + + if (maxLength > 0) + result = MIN(defaultValue, (maxLength - bytesDone)); + else + result = defaultValue; + + // Since we don't know the size of the read in advance, + // the shouldPreBuffer decision is based upon whether the returned value would fit + // in the current buffer without requiring a resize of the buffer. + // + // This is because, in all likelyhood, the amount read from the socket will be less than the default value. + // Thus we should avoid over-allocating the read buffer when we can simply use the pre-buffer instead. + + if (shouldPreBufferPtr) + { + NSUInteger buffSize = [buffer length]; + NSUInteger buffUsed = startOffset + bytesDone; + + NSUInteger buffSpace = buffSize - buffUsed; + + if (buffSpace >= result) + *shouldPreBufferPtr = NO; + else + *shouldPreBufferPtr = YES; + } + } + + return result; +} + +/** + * For read packets without a set terminator, returns the amount of data + * that can be read without exceeding the readLength or maxLength. + * + * The given parameter indicates the number of bytes estimated to be available on the socket, + * which is taken into consideration during the calculation. + * + * The given hint MUST be greater than zero. +**/ +- (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable +{ + NSAssert(term == nil, @"This method does not apply to term reads"); + NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable"); + + if (readLength > 0) + { + // Read a specific length of data + + return MIN(bytesAvailable, (readLength - bytesDone)); + + // No need to avoid resizing the buffer. + // If the user provided their own buffer, + // and told us to read a certain length of data that exceeds the size of the buffer, + // then it is clear that our code will resize the buffer during the read operation. + // + // This method does not actually do any resizing. + // The resizing will happen elsewhere if needed. + } + else + { + // Read all available data + + NSUInteger result = bytesAvailable; + + if (maxLength > 0) + { + result = MIN(result, (maxLength - bytesDone)); + } + + // No need to avoid resizing the buffer. + // If the user provided their own buffer, + // and told us to read all available data without giving us a maxLength, + // then it is clear that our code might resize the buffer during the read operation. + // + // This method does not actually do any resizing. + // The resizing will happen elsewhere if needed. + + return result; + } +} + +/** + * For read packets with a set terminator, returns the amount of data + * that can be read without exceeding the maxLength. + * + * The given parameter indicates the number of bytes estimated to be available on the socket, + * which is taken into consideration during the calculation. + * + * To optimize memory allocations, mem copies, and mem moves + * the shouldPreBuffer boolean value will indicate if the data should be read into a prebuffer first, + * or if the data can be read directly into the read packet's buffer. +**/ +- (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr +{ + NSAssert(term != nil, @"This method does not apply to non-term reads"); + NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable"); + + + NSUInteger result = bytesAvailable; + + if (maxLength > 0) + { + result = MIN(result, (maxLength - bytesDone)); + } + + // Should the data be read into the read packet's buffer, or into a pre-buffer first? + // + // One would imagine the preferred option is the faster one. + // So which one is faster? + // + // Reading directly into the packet's buffer requires: + // 1. Possibly resizing packet buffer (malloc/realloc) + // 2. Filling buffer (read) + // 3. Searching for term (memcmp) + // 4. Possibly copying overflow into prebuffer (malloc/realloc, memcpy) + // + // Reading into prebuffer first: + // 1. Possibly resizing prebuffer (malloc/realloc) + // 2. Filling buffer (read) + // 3. Searching for term (memcmp) + // 4. Copying underflow into packet buffer (malloc/realloc, memcpy) + // 5. Removing underflow from prebuffer (memmove) + // + // Comparing the performance of the two we can see that reading + // data into the prebuffer first is slower due to the extra memove. + // + // However: + // The implementation of NSMutableData is open source via core foundation's CFMutableData. + // Decreasing the length of a mutable data object doesn't cause a realloc. + // In other words, the capacity of a mutable data object can grow, but doesn't shrink. + // + // This means the prebuffer will rarely need a realloc. + // The packet buffer, on the other hand, may often need a realloc. + // This is especially true if we are the buffer owner. + // Furthermore, if we are constantly realloc'ing the packet buffer, + // and then moving the overflow into the prebuffer, + // then we're consistently over-allocating memory for each term read. + // And now we get into a bit of a tradeoff between speed and memory utilization. + // + // The end result is that the two perform very similarly. + // And we can answer the original question very simply by another means. + // + // If we can read all the data directly into the packet's buffer without resizing it first, + // then we do so. Otherwise we use the prebuffer. + + if (shouldPreBufferPtr) + { + NSUInteger buffSize = [buffer length]; + NSUInteger buffUsed = startOffset + bytesDone; + + if ((buffSize - buffUsed) >= result) + *shouldPreBufferPtr = NO; + else + *shouldPreBufferPtr = YES; + } + + return result; +} + +/** + * For read packets with a set terminator, + * returns the amount of data that can be read from the given preBuffer, + * without going over a terminator or the maxLength. + * + * It is assumed the terminator has not already been read. +**/ +- (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr +{ + NSAssert(term != nil, @"This method does not apply to non-term reads"); + NSAssert([preBuffer availableBytes] > 0, @"Invoked with empty pre buffer!"); + + // We know that the terminator, as a whole, doesn't exist in our own buffer. + // But it is possible that a _portion_ of it exists in our buffer. + // So we're going to look for the terminator starting with a portion of our own buffer. + // + // Example: + // + // term length = 3 bytes + // bytesDone = 5 bytes + // preBuffer length = 5 bytes + // + // If we append the preBuffer to our buffer, + // it would look like this: + // + // --------------------- + // |B|B|B|B|B|P|P|P|P|P| + // --------------------- + // + // So we start our search here: + // + // --------------------- + // |B|B|B|B|B|P|P|P|P|P| + // -------^-^-^--------- + // + // And move forwards... + // + // --------------------- + // |B|B|B|B|B|P|P|P|P|P| + // ---------^-^-^------- + // + // Until we find the terminator or reach the end. + // + // --------------------- + // |B|B|B|B|B|P|P|P|P|P| + // ---------------^-^-^- + + BOOL found = NO; + + NSUInteger termLength = [term length]; + NSUInteger preBufferLength = [preBuffer availableBytes]; + + if ((bytesDone + preBufferLength) < termLength) + { + // Not enough data for a full term sequence yet + return preBufferLength; + } + + NSUInteger maxPreBufferLength; + if (maxLength > 0) { + maxPreBufferLength = MIN(preBufferLength, (maxLength - bytesDone)); + + // Note: maxLength >= termLength + } + else { + maxPreBufferLength = preBufferLength; + } + + uint8_t seq[termLength]; + const void *termBuf = [term bytes]; + + NSUInteger bufLen = MIN(bytesDone, (termLength - 1)); + uint8_t *buf = (uint8_t *)[buffer mutableBytes] + startOffset + bytesDone - bufLen; + + NSUInteger preLen = termLength - bufLen; + const uint8_t *pre = [preBuffer readBuffer]; + + NSUInteger loopCount = bufLen + maxPreBufferLength - termLength + 1; // Plus one. See example above. + + NSUInteger result = maxPreBufferLength; + + NSUInteger i; + for (i = 0; i < loopCount; i++) + { + if (bufLen > 0) + { + // Combining bytes from buffer and preBuffer + + memcpy(seq, buf, bufLen); + memcpy(seq + bufLen, pre, preLen); + + if (memcmp(seq, termBuf, termLength) == 0) + { + result = preLen; + found = YES; + break; + } + + buf++; + bufLen--; + preLen++; + } + else + { + // Comparing directly from preBuffer + + if (memcmp(pre, termBuf, termLength) == 0) + { + NSUInteger preOffset = pre - [preBuffer readBuffer]; // pointer arithmetic + + result = preOffset + termLength; + found = YES; + break; + } + + pre++; + } + } + + // There is no need to avoid resizing the buffer in this particular situation. + + if (foundPtr) *foundPtr = found; + return result; +} + +/** + * For read packets with a set terminator, scans the packet buffer for the term. + * It is assumed the terminator had not been fully read prior to the new bytes. + * + * If the term is found, the number of excess bytes after the term are returned. + * If the term is not found, this method will return -1. + * + * Note: A return value of zero means the term was found at the very end. + * + * Prerequisites: + * The given number of bytes have been added to the end of our buffer. + * Our bytesDone variable has NOT been changed due to the prebuffered bytes. +**/ +- (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes +{ + NSAssert(term != nil, @"This method does not apply to non-term reads"); + + // The implementation of this method is very similar to the above method. + // See the above method for a discussion of the algorithm used here. + + uint8_t *buff = [buffer mutableBytes]; + NSUInteger buffLength = bytesDone + numBytes; + + const void *termBuff = [term bytes]; + NSUInteger termLength = [term length]; + + // Note: We are dealing with unsigned integers, + // so make sure the math doesn't go below zero. + + NSUInteger i = ((buffLength - numBytes) >= termLength) ? (buffLength - numBytes - termLength + 1) : 0; + + while (i + termLength <= buffLength) + { + uint8_t *subBuffer = buff + startOffset + i; + + if (memcmp(subBuffer, termBuff, termLength) == 0) + { + return buffLength - (i + termLength); + } + + i++; + } + + return -1; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The GCDAsyncWritePacket encompasses the instructions for any given write. +**/ +@interface GCDAsyncWritePacket : NSObject +{ + @public + NSData *buffer; + NSUInteger bytesDone; + long tag; + NSTimeInterval timeout; +} +- (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i NS_DESIGNATED_INITIALIZER; +@end + +@implementation GCDAsyncWritePacket + +// Cover the superclass' designated initializer +- (instancetype)init NS_UNAVAILABLE +{ + NSAssert(0, @"Use the designated initializer"); + return nil; +} + +- (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i +{ + if((self = [super init])) + { + buffer = d; // Retain not copy. For performance as documented in header file. + bytesDone = 0; + timeout = t; + tag = i; + } + return self; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The GCDAsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues. + * This class my be altered to support more than just TLS in the future. +**/ +@interface GCDAsyncSpecialPacket : NSObject +{ + @public + NSDictionary *tlsSettings; +} +- (instancetype)initWithTLSSettings:(NSDictionary <NSString*,NSObject*>*)settings NS_DESIGNATED_INITIALIZER; +@end + +@implementation GCDAsyncSpecialPacket + +// Cover the superclass' designated initializer +- (instancetype)init NS_UNAVAILABLE +{ + NSAssert(0, @"Use the designated initializer"); + return nil; +} + +- (instancetype)initWithTLSSettings:(NSDictionary <NSString*,NSObject*>*)settings +{ + if((self = [super init])) + { + tlsSettings = [settings copy]; + } + return self; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation GCDAsyncSocket +{ + uint32_t flags; + uint16_t config; + + __weak id<GCDAsyncSocketDelegate> delegate; + dispatch_queue_t delegateQueue; + + int socket4FD; + int socket6FD; + int socketUN; + NSURL *socketUrl; + int stateIndex; + NSData * connectInterface4; + NSData * connectInterface6; + NSData * connectInterfaceUN; + + dispatch_queue_t socketQueue; + + dispatch_source_t accept4Source; + dispatch_source_t accept6Source; + dispatch_source_t acceptUNSource; + dispatch_source_t connectTimer; + dispatch_source_t readSource; + dispatch_source_t writeSource; + dispatch_source_t readTimer; + dispatch_source_t writeTimer; + + NSMutableArray *readQueue; + NSMutableArray *writeQueue; + + GCDAsyncReadPacket *currentRead; + GCDAsyncWritePacket *currentWrite; + + unsigned long socketFDBytesAvailable; + + GCDAsyncSocketPreBuffer *preBuffer; + +#if TARGET_OS_IPHONE + CFStreamClientContext streamContext; + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; +#endif + SSLContextRef sslContext; + GCDAsyncSocketPreBuffer *sslPreBuffer; + size_t sslWriteCachedLength; + OSStatus sslErrCode; + OSStatus lastSSLHandshakeError; + + void *IsOnSocketQueueOrTargetQueueKey; + + id userData; + NSTimeInterval alternateAddressDelay; +} + +- (instancetype)init +{ + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:NULL]; +} + +- (instancetype)initWithSocketQueue:(dispatch_queue_t)sq +{ + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq]; +} + +- (instancetype)initWithDelegate:(id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq +{ + return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; +} + +- (instancetype)initWithDelegate:(id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq +{ + if((self = [super init])) + { + delegate = aDelegate; + delegateQueue = dq; + + #if !OS_OBJECT_USE_OBJC + if (dq) dispatch_retain(dq); + #endif + + socket4FD = SOCKET_NULL; + socket6FD = SOCKET_NULL; + socketUN = SOCKET_NULL; + socketUrl = nil; + stateIndex = 0; + + if (sq) + { + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + + socketQueue = sq; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(sq); + #endif + } + else + { + socketQueue = dispatch_queue_create([GCDAsyncSocketQueueName UTF8String], NULL); + } + + // The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter. + // From the documentation: + // + // > Keys are only compared as pointers and are never dereferenced. + // > Thus, you can use a pointer to a static variable for a specific subsystem or + // > any other value that allows you to identify the value uniquely. + // + // We're just going to use the memory address of an ivar. + // Specifically an ivar that is explicitly named for our purpose to make the code more readable. + // + // However, it feels tedious (and less readable) to include the "&" all the time: + // dispatch_get_specific(&IsOnSocketQueueOrTargetQueueKey) + // + // So we're going to make it so it doesn't matter if we use the '&' or not, + // by assigning the value of the ivar to the address of the ivar. + // Thus: IsOnSocketQueueOrTargetQueueKey == &IsOnSocketQueueOrTargetQueueKey; + + IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey; + + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); + + readQueue = [[NSMutableArray alloc] initWithCapacity:5]; + currentRead = nil; + + writeQueue = [[NSMutableArray alloc] initWithCapacity:5]; + currentWrite = nil; + + preBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)]; + alternateAddressDelay = 0.3; + } + return self; +} + +- (void)dealloc +{ + LogInfo(@"%@ - %@ (start)", THIS_METHOD, self); + + // Set dealloc flag. + // This is used by closeWithError to ensure we don't accidentally retain ourself. + flags |= kDealloc; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + [self closeWithError:nil]; + } + else + { + dispatch_sync(socketQueue, ^{ + [self closeWithError:nil]; + }); + } + + delegate = nil; + + #if !OS_OBJECT_USE_OBJC + if (delegateQueue) dispatch_release(delegateQueue); + #endif + delegateQueue = NULL; + + #if !OS_OBJECT_USE_OBJC + if (socketQueue) dispatch_release(socketQueue); + #endif + socketQueue = NULL; + + LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self); +} + +#pragma mark - + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD socketQueue:(nullable dispatch_queue_t)sq error:(NSError**)error { + return [self socketFromConnectedSocketFD:socketFD delegate:nil delegateQueue:NULL socketQueue:sq error:error]; +} + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error { + return [self socketFromConnectedSocketFD:socketFD delegate:aDelegate delegateQueue:dq socketQueue:NULL error:error]; +} + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq error:(NSError* __autoreleasing *)error +{ + __block BOOL errorOccured = NO; + + GCDAsyncSocket *socket = [[[self class] alloc] initWithDelegate:aDelegate delegateQueue:dq socketQueue:sq]; + + dispatch_sync(socket->socketQueue, ^{ @autoreleasepool { + struct sockaddr addr; + socklen_t addr_size = sizeof(struct sockaddr); + int retVal = getpeername(socketFD, (struct sockaddr *)&addr, &addr_size); + if (retVal) + { + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketOtherError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Attempt to create socket from socket FD failed. getpeername() failed", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + errorOccured = YES; + if (error) + *error = [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; + return; + } + + if (addr.sa_family == AF_INET) + { + socket->socket4FD = socketFD; + } + else if (addr.sa_family == AF_INET6) + { + socket->socket6FD = socketFD; + } + else + { + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketOtherError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Attempt to create socket from socket FD failed. socket FD is neither IPv4 nor IPv6", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + errorOccured = YES; + if (error) + *error = [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; + return; + } + + socket->flags = kSocketStarted; + [socket didConnect:socket->stateIndex]; + }}); + + return errorOccured? nil: socket; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Configuration +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (id)delegate +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return delegate; + } + else + { + __block id result; + + dispatch_sync(socketQueue, ^{ + result = self->delegate; + }); + + return result; + } +} + +- (void)setDelegate:(id)newDelegate synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + self->delegate = newDelegate; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegate:(id<GCDAsyncSocketDelegate>)newDelegate +{ + [self setDelegate:newDelegate synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id<GCDAsyncSocketDelegate>)newDelegate +{ + [self setDelegate:newDelegate synchronously:YES]; +} + +- (dispatch_queue_t)delegateQueue +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return delegateQueue; + } + else + { + __block dispatch_queue_t result; + + dispatch_sync(socketQueue, ^{ + result = self->delegateQueue; + }); + + return result; + } +} + +- (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + + #if !OS_OBJECT_USE_OBJC + if (self->delegateQueue) dispatch_release(self->delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif + + self->delegateQueue = newDelegateQueue; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegateQueue:newDelegateQueue synchronously:YES]; +} + +- (void)getDelegate:(id<GCDAsyncSocketDelegate> *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (delegatePtr) *delegatePtr = delegate; + if (delegateQueuePtr) *delegateQueuePtr = delegateQueue; + } + else + { + __block id dPtr = NULL; + __block dispatch_queue_t dqPtr = NULL; + + dispatch_sync(socketQueue, ^{ + dPtr = self->delegate; + dqPtr = self->delegateQueue; + }); + + if (delegatePtr) *delegatePtr = dPtr; + if (delegateQueuePtr) *delegateQueuePtr = dqPtr; + } +} + +- (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + + self->delegate = newDelegate; + + #if !OS_OBJECT_USE_OBJC + if (self->delegateQueue) dispatch_release(self->delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif + + self->delegateQueue = newDelegateQueue; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegate:(id<GCDAsyncSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id<GCDAsyncSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES]; +} + +- (BOOL)isIPv4Enabled +{ + // Note: YES means kIPv4Disabled is OFF + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return ((config & kIPv4Disabled) == 0); + } + else + { + __block BOOL result; + + dispatch_sync(socketQueue, ^{ + result = ((self->config & kIPv4Disabled) == 0); + }); + + return result; + } +} + +- (void)setIPv4Enabled:(BOOL)flag +{ + // Note: YES means kIPv4Disabled is OFF + + dispatch_block_t block = ^{ + + if (flag) + self->config &= ~kIPv4Disabled; + else + self->config |= kIPv4Disabled; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (BOOL)isIPv6Enabled +{ + // Note: YES means kIPv6Disabled is OFF + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return ((config & kIPv6Disabled) == 0); + } + else + { + __block BOOL result; + + dispatch_sync(socketQueue, ^{ + result = ((self->config & kIPv6Disabled) == 0); + }); + + return result; + } +} + +- (void)setIPv6Enabled:(BOOL)flag +{ + // Note: YES means kIPv6Disabled is OFF + + dispatch_block_t block = ^{ + + if (flag) + self->config &= ~kIPv6Disabled; + else + self->config |= kIPv6Disabled; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (BOOL)isIPv4PreferredOverIPv6 +{ + // Note: YES means kPreferIPv6 is OFF + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return ((config & kPreferIPv6) == 0); + } + else + { + __block BOOL result; + + dispatch_sync(socketQueue, ^{ + result = ((self->config & kPreferIPv6) == 0); + }); + + return result; + } +} + +- (void)setIPv4PreferredOverIPv6:(BOOL)flag +{ + // Note: YES means kPreferIPv6 is OFF + + dispatch_block_t block = ^{ + + if (flag) + self->config &= ~kPreferIPv6; + else + self->config |= kPreferIPv6; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (NSTimeInterval) alternateAddressDelay { + __block NSTimeInterval delay; + dispatch_block_t block = ^{ + delay = self->alternateAddressDelay; + }; + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + return delay; +} + +- (void) setAlternateAddressDelay:(NSTimeInterval)delay { + dispatch_block_t block = ^{ + self->alternateAddressDelay = delay; + }; + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (id)userData +{ + __block id result = nil; + + dispatch_block_t block = ^{ + + result = self->userData; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setUserData:(id)arbitraryUserData +{ + dispatch_block_t block = ^{ + + if (self->userData != arbitraryUserData) + { + self->userData = arbitraryUserData; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Accepting +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr +{ + return [self acceptOnInterface:nil port:port error:errPtr]; +} + +- (BOOL)acceptOnInterface:(NSString *)inInterface port:(uint16_t)port error:(NSError **)errPtr +{ + LogTrace(); + + // Just in-case interface parameter is immutable. + NSString *interface = [inInterface copy]; + + __block BOOL result = NO; + __block NSError *err = nil; + + // CreateSocket Block + // This block will be invoked within the dispatch block below. + + int(^createSocket)(int, NSData*) = ^int (int domain, NSData *interfaceAddr) { + + int socketFD = socket(domain, SOCK_STREAM, 0); + + if (socketFD == SOCKET_NULL) + { + NSString *reason = @"Error in socket() function"; + err = [self errorWithErrno:errno reason:reason]; + + return SOCKET_NULL; + } + + int status; + + // Set socket options + + status = fcntl(socketFD, F_SETFL, O_NONBLOCK); + if (status == -1) + { + NSString *reason = @"Error enabling non-blocking IO on socket (fcntl)"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + int reuseOn = 1; + status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); + if (status == -1) + { + NSString *reason = @"Error enabling address reuse (setsockopt)"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + // Bind socket + + status = bind(socketFD, (const struct sockaddr *)[interfaceAddr bytes], (socklen_t)[interfaceAddr length]); + if (status == -1) + { + NSString *reason = @"Error in bind() function"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + // Listen + + status = listen(socketFD, 1024); + if (status == -1) + { + NSString *reason = @"Error in listen() function"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + return socketFD; + }; + + // Create dispatch block and run on socketQueue + + dispatch_block_t block = ^{ @autoreleasepool { + + if (self->delegate == nil) // Must have delegate set + { + NSString *msg = @"Attempting to accept without a delegate. Set a delegate first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + if (self->delegateQueue == NULL) // Must have delegate queue set + { + NSString *msg = @"Attempting to accept without a delegate queue. Set a delegate queue first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled + { + NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + if (![self isDisconnected]) // Must be disconnected + { + NSString *msg = @"Attempting to accept while connected or accepting connections. Disconnect first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + // Clear queues (spurious read/write requests post disconnect) + [self->readQueue removeAllObjects]; + [self->writeQueue removeAllObjects]; + + // Resolve interface from description + + NSMutableData *interface4 = nil; + NSMutableData *interface6 = nil; + + [self getInterfaceAddress4:&interface4 address6:&interface6 fromDescription:interface port:port]; + + if ((interface4 == nil) && (interface6 == nil)) + { + NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; + err = [self badParamError:msg]; + + return_from_block; + } + + if (isIPv4Disabled && (interface6 == nil)) + { + NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; + err = [self badParamError:msg]; + + return_from_block; + } + + if (isIPv6Disabled && (interface4 == nil)) + { + NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; + err = [self badParamError:msg]; + + return_from_block; + } + + BOOL enableIPv4 = !isIPv4Disabled && (interface4 != nil); + BOOL enableIPv6 = !isIPv6Disabled && (interface6 != nil); + + // Create sockets, configure, bind, and listen + + if (enableIPv4) + { + LogVerbose(@"Creating IPv4 socket"); + self->socket4FD = createSocket(AF_INET, interface4); + + if (self->socket4FD == SOCKET_NULL) + { + return_from_block; + } + } + + if (enableIPv6) + { + LogVerbose(@"Creating IPv6 socket"); + + if (enableIPv4 && (port == 0)) + { + // No specific port was specified, so we allowed the OS to pick an available port for us. + // Now we need to make sure the IPv6 socket listens on the same port as the IPv4 socket. + + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)[interface6 mutableBytes]; + addr6->sin6_port = htons([self localPort4]); + } + + self->socket6FD = createSocket(AF_INET6, interface6); + + if (self->socket6FD == SOCKET_NULL) + { + if (self->socket4FD != SOCKET_NULL) + { + LogVerbose(@"close(socket4FD)"); + close(self->socket4FD); + self->socket4FD = SOCKET_NULL; + } + + return_from_block; + } + } + + // Create accept sources + + if (enableIPv4) + { + self->accept4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socket4FD, 0, self->socketQueue); + + int socketFD = self->socket4FD; + dispatch_source_t acceptSource = self->accept4Source; + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(self->accept4Source, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + LogVerbose(@"event4Block"); + + unsigned long i = 0; + unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); + + LogVerbose(@"numPendingConnections: %lu", numPendingConnections); + + while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); + + #pragma clang diagnostic pop + }}); + + + dispatch_source_set_cancel_handler(self->accept4Source, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(accept4Source)"); + dispatch_release(acceptSource); + #endif + + LogVerbose(@"close(socket4FD)"); + close(socketFD); + + #pragma clang diagnostic pop + }); + + LogVerbose(@"dispatch_resume(accept4Source)"); + dispatch_resume(self->accept4Source); + } + + if (enableIPv6) + { + self->accept6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socket6FD, 0, self->socketQueue); + + int socketFD = self->socket6FD; + dispatch_source_t acceptSource = self->accept6Source; + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(self->accept6Source, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + LogVerbose(@"event6Block"); + + unsigned long i = 0; + unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); + + LogVerbose(@"numPendingConnections: %lu", numPendingConnections); + + while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); + + #pragma clang diagnostic pop + }}); + + dispatch_source_set_cancel_handler(self->accept6Source, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(accept6Source)"); + dispatch_release(acceptSource); + #endif + + LogVerbose(@"close(socket6FD)"); + close(socketFD); + + #pragma clang diagnostic pop + }); + + LogVerbose(@"dispatch_resume(accept6Source)"); + dispatch_resume(self->accept6Source); + } + + self->flags |= kSocketStarted; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (result == NO) + { + LogInfo(@"Error in accept: %@", err); + + if (errPtr) + *errPtr = err; + } + + return result; +} + +- (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr +{ + LogTrace(); + + __block BOOL result = NO; + __block NSError *err = nil; + + // CreateSocket Block + // This block will be invoked within the dispatch block below. + + int(^createSocket)(int, NSData*) = ^int (int domain, NSData *interfaceAddr) { + + int socketFD = socket(domain, SOCK_STREAM, 0); + + if (socketFD == SOCKET_NULL) + { + NSString *reason = @"Error in socket() function"; + err = [self errorWithErrno:errno reason:reason]; + + return SOCKET_NULL; + } + + int status; + + // Set socket options + + status = fcntl(socketFD, F_SETFL, O_NONBLOCK); + if (status == -1) + { + NSString *reason = @"Error enabling non-blocking IO on socket (fcntl)"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + int reuseOn = 1; + status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); + if (status == -1) + { + NSString *reason = @"Error enabling address reuse (setsockopt)"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + // Bind socket + + status = bind(socketFD, (const struct sockaddr *)[interfaceAddr bytes], (socklen_t)[interfaceAddr length]); + if (status == -1) + { + NSString *reason = @"Error in bind() function"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + // Listen + + status = listen(socketFD, 1024); + if (status == -1) + { + NSString *reason = @"Error in listen() function"; + err = [self errorWithErrno:errno reason:reason]; + + LogVerbose(@"close(socketFD)"); + close(socketFD); + return SOCKET_NULL; + } + + return socketFD; + }; + + // Create dispatch block and run on socketQueue + + dispatch_block_t block = ^{ @autoreleasepool { + + if (self->delegate == nil) // Must have delegate set + { + NSString *msg = @"Attempting to accept without a delegate. Set a delegate first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + if (self->delegateQueue == NULL) // Must have delegate queue set + { + NSString *msg = @"Attempting to accept without a delegate queue. Set a delegate queue first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + if (![self isDisconnected]) // Must be disconnected + { + NSString *msg = @"Attempting to accept while connected or accepting connections. Disconnect first."; + err = [self badConfigError:msg]; + + return_from_block; + } + + // Clear queues (spurious read/write requests post disconnect) + [self->readQueue removeAllObjects]; + [self->writeQueue removeAllObjects]; + + // Remove a previous socket + + NSError *error = nil; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *urlPath = url.path; + if (urlPath && [fileManager fileExistsAtPath:urlPath]) { + if (![fileManager removeItemAtURL:url error:&error]) { + NSString *msg = @"Could not remove previous unix domain socket at given url."; + err = [self otherError:msg]; + + return_from_block; + } + } + + // Resolve interface from description + + NSData *interface = [self getInterfaceAddressFromUrl:url]; + + if (interface == nil) + { + NSString *msg = @"Invalid unix domain url. Specify a valid file url that does not exist (e.g. \"file:///tmp/socket\")"; + err = [self badParamError:msg]; + + return_from_block; + } + + // Create sockets, configure, bind, and listen + + LogVerbose(@"Creating unix domain socket"); + self->socketUN = createSocket(AF_UNIX, interface); + + if (self->socketUN == SOCKET_NULL) + { + return_from_block; + } + + self->socketUrl = url; + + // Create accept sources + + self->acceptUNSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socketUN, 0, self->socketQueue); + + int socketFD = self->socketUN; + dispatch_source_t acceptSource = self->acceptUNSource; + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(self->acceptUNSource, ^{ @autoreleasepool { + + __strong GCDAsyncSocket *strongSelf = weakSelf; + + LogVerbose(@"eventUNBlock"); + + unsigned long i = 0; + unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); + + LogVerbose(@"numPendingConnections: %lu", numPendingConnections); + + while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); + }}); + + dispatch_source_set_cancel_handler(self->acceptUNSource, ^{ + +#if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(acceptUNSource)"); + dispatch_release(acceptSource); +#endif + + LogVerbose(@"close(socketUN)"); + close(socketFD); + }); + + LogVerbose(@"dispatch_resume(acceptUNSource)"); + dispatch_resume(self->acceptUNSource); + + self->flags |= kSocketStarted; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (result == NO) + { + LogInfo(@"Error in accept: %@", err); + + if (errPtr) + *errPtr = err; + } + + return result; +} + +- (BOOL)doAccept:(int)parentSocketFD +{ + LogTrace(); + + int socketType; + int childSocketFD; + NSData *childSocketAddress; + + if (parentSocketFD == socket4FD) + { + socketType = 0; + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); + + if (childSocketFD == -1) + { + LogWarn(@"Accept failed with error: %@", [self errnoError]); + return NO; + } + + childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; + } + else if (parentSocketFD == socket6FD) + { + socketType = 1; + + struct sockaddr_in6 addr; + socklen_t addrLen = sizeof(addr); + + childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); + + if (childSocketFD == -1) + { + LogWarn(@"Accept failed with error: %@", [self errnoError]); + return NO; + } + + childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; + } + else // if (parentSocketFD == socketUN) + { + socketType = 2; + + struct sockaddr_un addr; + socklen_t addrLen = sizeof(addr); + + childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); + + if (childSocketFD == -1) + { + LogWarn(@"Accept failed with error: %@", [self errnoError]); + return NO; + } + + childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; + } + + // Enable non-blocking IO on the socket + + int result = fcntl(childSocketFD, F_SETFL, O_NONBLOCK); + if (result == -1) + { + LogWarn(@"Error enabling non-blocking IO on accepted socket (fcntl)"); + LogVerbose(@"close(childSocketFD)"); + close(childSocketFD); + return NO; + } + + // Prevent SIGPIPE signals + + int nosigpipe = 1; + setsockopt(childSocketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); + + // Notify delegate + + if (delegateQueue) + { + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + // Query delegate for custom socket queue + + dispatch_queue_t childSocketQueue = NULL; + + if ([theDelegate respondsToSelector:@selector(newSocketQueueForConnectionFromAddress:onSocket:)]) + { + childSocketQueue = [theDelegate newSocketQueueForConnectionFromAddress:childSocketAddress + onSocket:self]; + } + + // Create GCDAsyncSocket instance for accepted socket + + GCDAsyncSocket *acceptedSocket = [[[self class] alloc] initWithDelegate:theDelegate + delegateQueue:self->delegateQueue + socketQueue:childSocketQueue]; + + if (socketType == 0) + acceptedSocket->socket4FD = childSocketFD; + else if (socketType == 1) + acceptedSocket->socket6FD = childSocketFD; + else + acceptedSocket->socketUN = childSocketFD; + + acceptedSocket->flags = (kSocketStarted | kConnected); + + // Setup read and write sources for accepted socket + + dispatch_async(acceptedSocket->socketQueue, ^{ @autoreleasepool { + + [acceptedSocket setupReadAndWriteSourcesForNewlyConnectedSocket:childSocketFD]; + }}); + + // Notify delegate + + if ([theDelegate respondsToSelector:@selector(socket:didAcceptNewSocket:)]) + { + [theDelegate socket:self didAcceptNewSocket:acceptedSocket]; + } + + // Release the socket queue returned from the delegate (it was retained by acceptedSocket) + #if !OS_OBJECT_USE_OBJC + if (childSocketQueue) dispatch_release(childSocketQueue); + #endif + + // The accepted socket should have been retained by the delegate. + // Otherwise it gets properly released when exiting the block. + }}); + } + + return YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Connecting +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * This method runs through the various checks required prior to a connection attempt. + * It is shared between the connectToHost and connectToAddress methods. + * +**/ +- (BOOL)preConnectWithInterface:(NSString *)interface error:(NSError **)errPtr +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (delegate == nil) // Must have delegate set + { + if (errPtr) + { + NSString *msg = @"Attempting to connect without a delegate. Set a delegate first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (delegateQueue == NULL) // Must have delegate queue set + { + if (errPtr) + { + NSString *msg = @"Attempting to connect without a delegate queue. Set a delegate queue first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (![self isDisconnected]) // Must be disconnected + { + if (errPtr) + { + NSString *msg = @"Attempting to connect while connected or accepting connections. Disconnect first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled + { + if (errPtr) + { + NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (interface) + { + NSMutableData *interface4 = nil; + NSMutableData *interface6 = nil; + + [self getInterfaceAddress4:&interface4 address6:&interface6 fromDescription:interface port:0]; + + if ((interface4 == nil) && (interface6 == nil)) + { + if (errPtr) + { + NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; + *errPtr = [self badParamError:msg]; + } + return NO; + } + + if (isIPv4Disabled && (interface6 == nil)) + { + if (errPtr) + { + NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; + *errPtr = [self badParamError:msg]; + } + return NO; + } + + if (isIPv6Disabled && (interface4 == nil)) + { + if (errPtr) + { + NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; + *errPtr = [self badParamError:msg]; + } + return NO; + } + + connectInterface4 = interface4; + connectInterface6 = interface6; + } + + // Clear queues (spurious read/write requests post disconnect) + [readQueue removeAllObjects]; + [writeQueue removeAllObjects]; + + return YES; +} + +- (BOOL)preConnectWithUrl:(NSURL *)url error:(NSError **)errPtr +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (delegate == nil) // Must have delegate set + { + if (errPtr) + { + NSString *msg = @"Attempting to connect without a delegate. Set a delegate first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (delegateQueue == NULL) // Must have delegate queue set + { + if (errPtr) + { + NSString *msg = @"Attempting to connect without a delegate queue. Set a delegate queue first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (![self isDisconnected]) // Must be disconnected + { + if (errPtr) + { + NSString *msg = @"Attempting to connect while connected or accepting connections. Disconnect first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + NSData *interface = [self getInterfaceAddressFromUrl:url]; + + if (interface == nil) + { + if (errPtr) + { + NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; + *errPtr = [self badParamError:msg]; + } + return NO; + } + + connectInterfaceUN = interface; + + // Clear queues (spurious read/write requests post disconnect) + [readQueue removeAllObjects]; + [writeQueue removeAllObjects]; + + return YES; +} + +- (BOOL)connectToHost:(NSString*)host onPort:(uint16_t)port error:(NSError **)errPtr +{ + return [self connectToHost:host onPort:port withTimeout:-1 error:errPtr]; +} + +- (BOOL)connectToHost:(NSString *)host + onPort:(uint16_t)port + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr +{ + return [self connectToHost:host onPort:port viaInterface:nil withTimeout:timeout error:errPtr]; +} + +- (BOOL)connectToHost:(NSString *)inHost + onPort:(uint16_t)port + viaInterface:(NSString *)inInterface + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr +{ + LogTrace(); + + // Just in case immutable objects were passed + NSString *host = [inHost copy]; + NSString *interface = [inInterface copy]; + + __block BOOL result = NO; + __block NSError *preConnectErr = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Check for problems with host parameter + + if ([host length] == 0) + { + NSString *msg = @"Invalid host parameter (nil or \"\"). Should be a domain name or IP address string."; + preConnectErr = [self badParamError:msg]; + + return_from_block; + } + + // Run through standard pre-connect checks + + if (![self preConnectWithInterface:interface error:&preConnectErr]) + { + return_from_block; + } + + // We've made it past all the checks. + // It's time to start the connection process. + + self->flags |= kSocketStarted; + + LogVerbose(@"Dispatching DNS lookup..."); + + // It's possible that the given host parameter is actually a NSMutableString. + // So we want to copy it now, within this block that will be executed synchronously. + // This way the asynchronous lookup block below doesn't have to worry about it changing. + + NSString *hostCpy = [host copy]; + + int aStateIndex = self->stateIndex; + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + NSError *lookupErr = nil; + NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr]; + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + if (lookupErr) + { + dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { + + [strongSelf lookup:aStateIndex didFail:lookupErr]; + }}); + } + else + { + NSData *address4 = nil; + NSData *address6 = nil; + + for (NSData *address in addresses) + { + if (!address4 && [[self class] isIPv4Address:address]) + { + address4 = address; + } + else if (!address6 && [[self class] isIPv6Address:address]) + { + address6 = address; + } + } + + dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { + + [strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6]; + }}); + } + + #pragma clang diagnostic pop + }}); + + [self startConnectTimeout:timeout]; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + + if (errPtr) *errPtr = preConnectErr; + return result; +} + +- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr +{ + return [self connectToAddress:remoteAddr viaInterface:nil withTimeout:-1 error:errPtr]; +} + +- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr +{ + return [self connectToAddress:remoteAddr viaInterface:nil withTimeout:timeout error:errPtr]; +} + +- (BOOL)connectToAddress:(NSData *)inRemoteAddr + viaInterface:(NSString *)inInterface + withTimeout:(NSTimeInterval)timeout + error:(NSError **)errPtr +{ + LogTrace(); + + // Just in case immutable objects were passed + NSData *remoteAddr = [inRemoteAddr copy]; + NSString *interface = [inInterface copy]; + + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Check for problems with remoteAddr parameter + + NSData *address4 = nil; + NSData *address6 = nil; + + if ([remoteAddr length] >= sizeof(struct sockaddr)) + { + const struct sockaddr *sockaddr = (const struct sockaddr *)[remoteAddr bytes]; + + if (sockaddr->sa_family == AF_INET) + { + if ([remoteAddr length] == sizeof(struct sockaddr_in)) + { + address4 = remoteAddr; + } + } + else if (sockaddr->sa_family == AF_INET6) + { + if ([remoteAddr length] == sizeof(struct sockaddr_in6)) + { + address6 = remoteAddr; + } + } + } + + if ((address4 == nil) && (address6 == nil)) + { + NSString *msg = @"A valid IPv4 or IPv6 address was not given"; + err = [self badParamError:msg]; + + return_from_block; + } + + BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && (address4 != nil)) + { + NSString *msg = @"IPv4 has been disabled and an IPv4 address was passed."; + err = [self badParamError:msg]; + + return_from_block; + } + + if (isIPv6Disabled && (address6 != nil)) + { + NSString *msg = @"IPv6 has been disabled and an IPv6 address was passed."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Run through standard pre-connect checks + + if (![self preConnectWithInterface:interface error:&err]) + { + return_from_block; + } + + // We've made it past all the checks. + // It's time to start the connection process. + + if (![self connectWithAddress4:address4 address6:address6 error:&err]) + { + return_from_block; + } + + self->flags |= kSocketStarted; + + [self startConnectTimeout:timeout]; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (result == NO) + { + if (errPtr) + *errPtr = err; + } + + return result; +} + +- (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr +{ + LogTrace(); + + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Check for problems with host parameter + + if ([url.path length] == 0) + { + NSString *msg = @"Invalid unix domain socket url."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Run through standard pre-connect checks + + if (![self preConnectWithUrl:url error:&err]) + { + return_from_block; + } + + // We've made it past all the checks. + // It's time to start the connection process. + + self->flags |= kSocketStarted; + + // Start the normal connection process + + NSError *connectError = nil; + if (![self connectWithAddressUN:self->connectInterfaceUN error:&connectError]) + { + [self closeWithError:connectError]; + + return_from_block; + } + + [self startConnectTimeout:timeout]; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (result == NO) + { + if (errPtr) + *errPtr = err; + } + + return result; +} + +- (BOOL)connectToNetService:(NSNetService *)netService error:(NSError **)errPtr +{ + NSArray* addresses = [netService addresses]; + for (NSData* address in addresses) + { + BOOL result = [self connectToAddress:address error:errPtr]; + if (result) + { + return YES; + } + } + + return NO; +} + +- (void)lookup:(int)aStateIndex didSucceedWithAddress4:(NSData *)address4 address6:(NSData *)address6 +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(address4 || address6, @"Expected at least one valid address"); + + if (aStateIndex != stateIndex) + { + LogInfo(@"Ignoring lookupDidSucceed, already disconnected"); + + // The connect operation has been cancelled. + // That is, socket was disconnected, or connection has already timed out. + return; + } + + // Check for problems + + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && (address6 == nil)) + { + NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address."; + + [self closeWithError:[self otherError:msg]]; + return; + } + + if (isIPv6Disabled && (address4 == nil)) + { + NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address."; + + [self closeWithError:[self otherError:msg]]; + return; + } + + // Start the normal connection process + + NSError *err = nil; + if (![self connectWithAddress4:address4 address6:address6 error:&err]) + { + [self closeWithError:err]; + } +} + +/** + * This method is called if the DNS lookup fails. + * This method is executed on the socketQueue. + * + * Since the DNS lookup executed synchronously on a global concurrent queue, + * the original connection request may have already been cancelled or timed-out by the time this method is invoked. + * The lookupIndex tells us whether the lookup is still valid or not. +**/ +- (void)lookup:(int)aStateIndex didFail:(NSError *)error +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + if (aStateIndex != stateIndex) + { + LogInfo(@"Ignoring lookup:didFail: - already disconnected"); + + // The connect operation has been cancelled. + // That is, socket was disconnected, or connection has already timed out. + return; + } + + [self endConnectTimeout]; + [self closeWithError:error]; +} + +- (BOOL)bindSocket:(int)socketFD toInterface:(NSData *)connectInterface error:(NSError **)errPtr +{ + // Bind the socket to the desired interface (if needed) + + if (connectInterface) + { + LogVerbose(@"Binding socket..."); + + if ([[self class] portFromAddress:connectInterface] > 0) + { + // Since we're going to be binding to a specific port, + // we should turn on reuseaddr to allow us to override sockets in time_wait. + + int reuseOn = 1; + setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); + } + + const struct sockaddr *interfaceAddr = (const struct sockaddr *)[connectInterface bytes]; + + int result = bind(socketFD, interfaceAddr, (socklen_t)[connectInterface length]); + if (result != 0) + { + if (errPtr) + *errPtr = [self errorWithErrno:errno reason:@"Error in bind() function"]; + + return NO; + } + } + + return YES; +} + +- (int)createSocket:(int)family connectInterface:(NSData *)connectInterface errPtr:(NSError **)errPtr +{ + int socketFD = socket(family, SOCK_STREAM, 0); + + if (socketFD == SOCKET_NULL) + { + if (errPtr) + *errPtr = [self errorWithErrno:errno reason:@"Error in socket() function"]; + + return socketFD; + } + + if (![self bindSocket:socketFD toInterface:connectInterface error:errPtr]) + { + [self closeSocket:socketFD]; + + return SOCKET_NULL; + } + + // Prevent SIGPIPE signals + + int nosigpipe = 1; + setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); + + return socketFD; +} + +- (void)connectSocket:(int)socketFD address:(NSData *)address stateIndex:(int)aStateIndex +{ + // If there already is a socket connected, we close socketFD and return + if (self.isConnected) + { + [self closeSocket:socketFD]; + return; + } + + // Start the connection process in a background queue + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(globalConcurrentQueue, ^{ +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wimplicit-retain-self" + + int result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]); + int err = errno; + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { + + if (strongSelf.isConnected) + { + [strongSelf closeSocket:socketFD]; + return_from_block; + } + + if (result == 0) + { + [self closeUnusedSocket:socketFD]; + + [strongSelf didConnect:aStateIndex]; + } + else + { + [strongSelf closeSocket:socketFD]; + + // If there are no more sockets trying to connect, we inform the error to the delegate + if (strongSelf.socket4FD == SOCKET_NULL && strongSelf.socket6FD == SOCKET_NULL) + { + NSError *error = [strongSelf errorWithErrno:err reason:@"Error in connect() function"]; + [strongSelf didNotConnect:aStateIndex error:error]; + } + } + }}); + +#pragma clang diagnostic pop + }); + + LogVerbose(@"Connecting..."); +} + +- (void)closeSocket:(int)socketFD +{ + if (socketFD != SOCKET_NULL && + (socketFD == socket6FD || socketFD == socket4FD)) + { + close(socketFD); + + if (socketFD == socket4FD) + { + LogVerbose(@"close(socket4FD)"); + socket4FD = SOCKET_NULL; + } + else if (socketFD == socket6FD) + { + LogVerbose(@"close(socket6FD)"); + socket6FD = SOCKET_NULL; + } + } +} + +- (void)closeUnusedSocket:(int)usedSocketFD +{ + if (usedSocketFD != socket4FD) + { + [self closeSocket:socket4FD]; + } + else if (usedSocketFD != socket6FD) + { + [self closeSocket:socket6FD]; + } +} + +- (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + LogVerbose(@"IPv4: %@:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]); + LogVerbose(@"IPv6: %@:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]); + + // Determine socket type + + BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO; + + // Create and bind the sockets + + if (address4) + { + LogVerbose(@"Creating IPv4 socket"); + + socket4FD = [self createSocket:AF_INET connectInterface:connectInterface4 errPtr:errPtr]; + } + + if (address6) + { + LogVerbose(@"Creating IPv6 socket"); + + socket6FD = [self createSocket:AF_INET6 connectInterface:connectInterface6 errPtr:errPtr]; + } + + if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL) + { + return NO; + } + + int socketFD, alternateSocketFD; + NSData *address, *alternateAddress; + + if ((preferIPv6 && socket6FD != SOCKET_NULL) || socket4FD == SOCKET_NULL) + { + socketFD = socket6FD; + alternateSocketFD = socket4FD; + address = address6; + alternateAddress = address4; + } + else + { + socketFD = socket4FD; + alternateSocketFD = socket6FD; + address = address4; + alternateAddress = address6; + } + + int aStateIndex = stateIndex; + + [self connectSocket:socketFD address:address stateIndex:aStateIndex]; + + if (alternateAddress) + { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(alternateAddressDelay * NSEC_PER_SEC)), socketQueue, ^{ + [self connectSocket:alternateSocketFD address:alternateAddress stateIndex:aStateIndex]; + }); + } + + return YES; +} + +- (BOOL)connectWithAddressUN:(NSData *)address error:(NSError **)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + // Create the socket + + int socketFD; + + LogVerbose(@"Creating unix domain socket"); + + socketUN = socket(AF_UNIX, SOCK_STREAM, 0); + + socketFD = socketUN; + + if (socketFD == SOCKET_NULL) + { + if (errPtr) + *errPtr = [self errorWithErrno:errno reason:@"Error in socket() function"]; + + return NO; + } + + // Bind the socket to the desired interface (if needed) + + LogVerbose(@"Binding socket..."); + + int reuseOn = 1; + setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); + +// const struct sockaddr *interfaceAddr = (const struct sockaddr *)[address bytes]; +// +// int result = bind(socketFD, interfaceAddr, (socklen_t)[address length]); +// if (result != 0) +// { +// if (errPtr) +// *errPtr = [self errnoErrorWithReason:@"Error in bind() function"]; +// +// return NO; +// } + + // Prevent SIGPIPE signals + + int nosigpipe = 1; + setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); + + // Start the connection process in a background queue + + int aStateIndex = stateIndex; + + dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(globalConcurrentQueue, ^{ + + const struct sockaddr *addr = (const struct sockaddr *)[address bytes]; + int result = connect(socketFD, addr, addr->sa_len); + if (result == 0) + { + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self didConnect:aStateIndex]; + }}); + } + else + { + // TODO: Bad file descriptor + perror("connect"); + NSError *error = [self errorWithErrno:errno reason:@"Error in connect() function"]; + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self didNotConnect:aStateIndex error:error]; + }}); + } + }); + + LogVerbose(@"Connecting..."); + + return YES; +} + +- (void)didConnect:(int)aStateIndex +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + if (aStateIndex != stateIndex) + { + LogInfo(@"Ignoring didConnect, already disconnected"); + + // The connect operation has been cancelled. + // That is, socket was disconnected, or connection has already timed out. + return; + } + + flags |= kConnected; + + [self endConnectTimeout]; + + #if TARGET_OS_IPHONE + // The endConnectTimeout method executed above incremented the stateIndex. + aStateIndex = stateIndex; + #endif + + // Setup read/write streams (as workaround for specific shortcomings in the iOS platform) + // + // Note: + // There may be configuration options that must be set by the delegate before opening the streams. + // The primary example is the kCFStreamNetworkServiceTypeVoIP flag, which only works on an unopened stream. + // + // Thus we wait until after the socket:didConnectToHost:port: delegate method has completed. + // This gives the delegate time to properly configure the streams if needed. + + dispatch_block_t SetupStreamsPart1 = ^{ + #if TARGET_OS_IPHONE + + if (![self createReadAndWriteStream]) + { + [self closeWithError:[self otherError:@"Error creating CFStreams"]]; + return; + } + + if (![self registerForStreamCallbacksIncludingReadWrite:NO]) + { + [self closeWithError:[self otherError:@"Error in CFStreamSetClient"]]; + return; + } + + #endif + }; + dispatch_block_t SetupStreamsPart2 = ^{ + #if TARGET_OS_IPHONE + + if (aStateIndex != self->stateIndex) + { + // The socket has been disconnected. + return; + } + + if (![self addStreamsToRunLoop]) + { + [self closeWithError:[self otherError:@"Error in CFStreamScheduleWithRunLoop"]]; + return; + } + + if (![self openStreams]) + { + [self closeWithError:[self otherError:@"Error creating CFStreams"]]; + return; + } + + #endif + }; + + // Notify delegate + + NSString *host = [self connectedHost]; + uint16_t port = [self connectedPort]; + NSURL *url = [self connectedUrl]; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && host != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) + { + SetupStreamsPart1(); + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didConnectToHost:host port:port]; + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + SetupStreamsPart2(); + }}); + }}); + } + else if (delegateQueue && url != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToUrl:)]) + { + SetupStreamsPart1(); + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didConnectToUrl:url]; + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + SetupStreamsPart2(); + }}); + }}); + } + else + { + SetupStreamsPart1(); + SetupStreamsPart2(); + } + + // Get the connected socket + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + // Enable non-blocking IO on the socket + + int result = fcntl(socketFD, F_SETFL, O_NONBLOCK); + if (result == -1) + { + NSString *errMsg = @"Error enabling non-blocking IO on socket (fcntl)"; + [self closeWithError:[self otherError:errMsg]]; + + return; + } + + // Setup our read/write sources + + [self setupReadAndWriteSourcesForNewlyConnectedSocket:socketFD]; + + // Dequeue any pending read/write requests + + [self maybeDequeueRead]; + [self maybeDequeueWrite]; +} + +- (void)didNotConnect:(int)aStateIndex error:(NSError *)error +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + if (aStateIndex != stateIndex) + { + LogInfo(@"Ignoring didNotConnect, already disconnected"); + + // The connect operation has been cancelled. + // That is, socket was disconnected, or connection has already timed out. + return; + } + + [self closeWithError:error]; +} + +- (void)startConnectTimeout:(NSTimeInterval)timeout +{ + if (timeout >= 0.0) + { + connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(connectTimer, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + [strongSelf doConnectTimeout]; + + #pragma clang diagnostic pop + }}); + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theConnectTimer = connectTimer; + dispatch_source_set_cancel_handler(connectTimer, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + LogVerbose(@"dispatch_release(connectTimer)"); + dispatch_release(theConnectTimer); + + #pragma clang diagnostic pop + }); + #endif + + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); + dispatch_source_set_timer(connectTimer, tt, DISPATCH_TIME_FOREVER, 0); + + dispatch_resume(connectTimer); + } +} + +- (void)endConnectTimeout +{ + LogTrace(); + + if (connectTimer) + { + dispatch_source_cancel(connectTimer); + connectTimer = NULL; + } + + // Increment stateIndex. + // This will prevent us from processing results from any related background asynchronous operations. + // + // Note: This should be called from close method even if connectTimer is NULL. + // This is because one might disconnect a socket prior to a successful connection which had no timeout. + + stateIndex++; + + if (connectInterface4) + { + connectInterface4 = nil; + } + if (connectInterface6) + { + connectInterface6 = nil; + } +} + +- (void)doConnectTimeout +{ + LogTrace(); + + [self endConnectTimeout]; + [self closeWithError:[self connectTimeoutError]]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Disconnecting +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)closeWithError:(NSError *)error +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + [self endConnectTimeout]; + + if (currentRead != nil) [self endCurrentRead]; + if (currentWrite != nil) [self endCurrentWrite]; + + [readQueue removeAllObjects]; + [writeQueue removeAllObjects]; + + [preBuffer reset]; + + #if TARGET_OS_IPHONE + { + if (readStream || writeStream) + { + [self removeStreamsFromRunLoop]; + + if (readStream) + { + CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); + CFReadStreamClose(readStream); + CFRelease(readStream); + readStream = NULL; + } + if (writeStream) + { + CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL); + CFWriteStreamClose(writeStream); + CFRelease(writeStream); + writeStream = NULL; + } + } + } + #endif + + [sslPreBuffer reset]; + sslErrCode = lastSSLHandshakeError = noErr; + + if (sslContext) + { + // Getting a linker error here about the SSLx() functions? + // You need to add the Security Framework to your application. + + SSLClose(sslContext); + + #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) + CFRelease(sslContext); + #else + SSLDisposeContext(sslContext); + #endif + + sslContext = NULL; + } + + // For some crazy reason (in my opinion), cancelling a dispatch source doesn't + // invoke the cancel handler if the dispatch source is paused. + // So we have to unpause the source if needed. + // This allows the cancel handler to be run, which in turn releases the source and closes the socket. + + if (!accept4Source && !accept6Source && !acceptUNSource && !readSource && !writeSource) + { + LogVerbose(@"manually closing close"); + + if (socket4FD != SOCKET_NULL) + { + LogVerbose(@"close(socket4FD)"); + close(socket4FD); + socket4FD = SOCKET_NULL; + } + + if (socket6FD != SOCKET_NULL) + { + LogVerbose(@"close(socket6FD)"); + close(socket6FD); + socket6FD = SOCKET_NULL; + } + + if (socketUN != SOCKET_NULL) + { + LogVerbose(@"close(socketUN)"); + close(socketUN); + socketUN = SOCKET_NULL; + unlink(socketUrl.path.fileSystemRepresentation); + socketUrl = nil; + } + } + else + { + if (accept4Source) + { + LogVerbose(@"dispatch_source_cancel(accept4Source)"); + dispatch_source_cancel(accept4Source); + + // We never suspend accept4Source + + accept4Source = NULL; + } + + if (accept6Source) + { + LogVerbose(@"dispatch_source_cancel(accept6Source)"); + dispatch_source_cancel(accept6Source); + + // We never suspend accept6Source + + accept6Source = NULL; + } + + if (acceptUNSource) + { + LogVerbose(@"dispatch_source_cancel(acceptUNSource)"); + dispatch_source_cancel(acceptUNSource); + + // We never suspend acceptUNSource + + acceptUNSource = NULL; + } + + if (readSource) + { + LogVerbose(@"dispatch_source_cancel(readSource)"); + dispatch_source_cancel(readSource); + + [self resumeReadSource]; + + readSource = NULL; + } + + if (writeSource) + { + LogVerbose(@"dispatch_source_cancel(writeSource)"); + dispatch_source_cancel(writeSource); + + [self resumeWriteSource]; + + writeSource = NULL; + } + + // The sockets will be closed by the cancel handlers of the corresponding source + + socket4FD = SOCKET_NULL; + socket6FD = SOCKET_NULL; + socketUN = SOCKET_NULL; + } + + // If the client has passed the connect/accept method, then the connection has at least begun. + // Notify delegate that it is now ending. + BOOL shouldCallDelegate = (flags & kSocketStarted) ? YES : NO; + BOOL isDeallocating = (flags & kDealloc) ? YES : NO; + + // Clear stored socket info and all flags (config remains as is) + socketFDBytesAvailable = 0; + flags = 0; + sslWriteCachedLength = 0; + + if (shouldCallDelegate) + { + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + __strong id theSelf = isDeallocating ? nil : self; + + if (delegateQueue && [theDelegate respondsToSelector: @selector(socketDidDisconnect:withError:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socketDidDisconnect:theSelf withError:error]; + }}); + } + } +} + +- (void)disconnect +{ + dispatch_block_t block = ^{ @autoreleasepool { + + if (self->flags & kSocketStarted) + { + [self closeWithError:nil]; + } + }}; + + // Synchronous disconnection, as documented in the header file + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); +} + +- (void)disconnectAfterReading +{ + dispatch_async(socketQueue, ^{ @autoreleasepool { + + if (self->flags & kSocketStarted) + { + self->flags |= (kForbidReadsWrites | kDisconnectAfterReads); + [self maybeClose]; + } + }}); +} + +- (void)disconnectAfterWriting +{ + dispatch_async(socketQueue, ^{ @autoreleasepool { + + if (self->flags & kSocketStarted) + { + self->flags |= (kForbidReadsWrites | kDisconnectAfterWrites); + [self maybeClose]; + } + }}); +} + +- (void)disconnectAfterReadingAndWriting +{ + dispatch_async(socketQueue, ^{ @autoreleasepool { + + if (self->flags & kSocketStarted) + { + self->flags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites); + [self maybeClose]; + } + }}); +} + +/** + * Closes the socket if possible. + * That is, if all writes have completed, and we're set to disconnect after writing, + * or if all reads have completed, and we're set to disconnect after reading. +**/ +- (void)maybeClose +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + BOOL shouldClose = NO; + + if (flags & kDisconnectAfterReads) + { + if (([readQueue count] == 0) && (currentRead == nil)) + { + if (flags & kDisconnectAfterWrites) + { + if (([writeQueue count] == 0) && (currentWrite == nil)) + { + shouldClose = YES; + } + } + else + { + shouldClose = YES; + } + } + } + else if (flags & kDisconnectAfterWrites) + { + if (([writeQueue count] == 0) && (currentWrite == nil)) + { + shouldClose = YES; + } + } + + if (shouldClose) + { + [self closeWithError:nil]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Errors +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSError *)badConfigError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketBadConfigError userInfo:userInfo]; +} + +- (NSError *)badParamError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketBadParamError userInfo:userInfo]; +} + ++ (NSError *)gaiError:(int)gai_error +{ + NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding]; + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:userInfo]; +} + +- (NSError *)errorWithErrno:(int)err reason:(NSString *)reason +{ + NSString *errMsg = [NSString stringWithUTF8String:strerror(err)]; + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg, + NSLocalizedFailureReasonErrorKey : reason}; + + return [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:userInfo]; +} + +- (NSError *)errnoError +{ + NSString *errMsg = [NSString stringWithUTF8String:strerror(errno)]; + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; +} + +- (NSError *)sslError:(OSStatus)ssl_error +{ + NSString *msg = @"Error code definition can be found in Apple's SecureTransport.h"; + NSDictionary *userInfo = @{NSLocalizedRecoverySuggestionErrorKey : msg}; + + return [NSError errorWithDomain:@"kCFStreamErrorDomainSSL" code:ssl_error userInfo:userInfo]; +} + +- (NSError *)connectTimeoutError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketConnectTimeoutError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Attempt to connect to host timed out", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketConnectTimeoutError userInfo:userInfo]; +} + +/** + * Returns a standard AsyncSocket maxed out error. +**/ +- (NSError *)readMaxedOutError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketReadMaxedOutError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Read operation reached set maximum length", nil); + + NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketReadMaxedOutError userInfo:info]; +} + +/** + * Returns a standard AsyncSocket write timeout error. +**/ +- (NSError *)readTimeoutError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketReadTimeoutError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Read operation timed out", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketReadTimeoutError userInfo:userInfo]; +} + +/** + * Returns a standard AsyncSocket write timeout error. +**/ +- (NSError *)writeTimeoutError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketWriteTimeoutError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Write operation timed out", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketWriteTimeoutError userInfo:userInfo]; +} + +- (NSError *)connectionClosedError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketClosedError", + @"GCDAsyncSocket", [NSBundle mainBundle], + @"Socket closed by remote peer", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketClosedError userInfo:userInfo]; +} + +- (NSError *)otherError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Diagnostics +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)isDisconnected +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->flags & kSocketStarted) ? NO : YES; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isConnected +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->flags & kConnected) ? YES : NO; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (NSString *)connectedHost +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (socket4FD != SOCKET_NULL) + return [self connectedHostFromSocket4:socket4FD]; + if (socket6FD != SOCKET_NULL) + return [self connectedHostFromSocket6:socket6FD]; + + return nil; + } + else + { + __block NSString *result = nil; + + dispatch_sync(socketQueue, ^{ @autoreleasepool { + + if (self->socket4FD != SOCKET_NULL) + result = [self connectedHostFromSocket4:self->socket4FD]; + else if (self->socket6FD != SOCKET_NULL) + result = [self connectedHostFromSocket6:self->socket6FD]; + }}); + + return result; + } +} + +- (uint16_t)connectedPort +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (socket4FD != SOCKET_NULL) + return [self connectedPortFromSocket4:socket4FD]; + if (socket6FD != SOCKET_NULL) + return [self connectedPortFromSocket6:socket6FD]; + + return 0; + } + else + { + __block uint16_t result = 0; + + dispatch_sync(socketQueue, ^{ + // No need for autorelease pool + + if (self->socket4FD != SOCKET_NULL) + result = [self connectedPortFromSocket4:self->socket4FD]; + else if (self->socket6FD != SOCKET_NULL) + result = [self connectedPortFromSocket6:self->socket6FD]; + }); + + return result; + } +} + +- (NSURL *)connectedUrl +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (socketUN != SOCKET_NULL) + return [self connectedUrlFromSocketUN:socketUN]; + + return nil; + } + else + { + __block NSURL *result = nil; + + dispatch_sync(socketQueue, ^{ @autoreleasepool { + + if (self->socketUN != SOCKET_NULL) + result = [self connectedUrlFromSocketUN:self->socketUN]; + }}); + + return result; + } +} + +- (NSString *)localHost +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (socket4FD != SOCKET_NULL) + return [self localHostFromSocket4:socket4FD]; + if (socket6FD != SOCKET_NULL) + return [self localHostFromSocket6:socket6FD]; + + return nil; + } + else + { + __block NSString *result = nil; + + dispatch_sync(socketQueue, ^{ @autoreleasepool { + + if (self->socket4FD != SOCKET_NULL) + result = [self localHostFromSocket4:self->socket4FD]; + else if (self->socket6FD != SOCKET_NULL) + result = [self localHostFromSocket6:self->socket6FD]; + }}); + + return result; + } +} + +- (uint16_t)localPort +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (socket4FD != SOCKET_NULL) + return [self localPortFromSocket4:socket4FD]; + if (socket6FD != SOCKET_NULL) + return [self localPortFromSocket6:socket6FD]; + + return 0; + } + else + { + __block uint16_t result = 0; + + dispatch_sync(socketQueue, ^{ + // No need for autorelease pool + + if (self->socket4FD != SOCKET_NULL) + result = [self localPortFromSocket4:self->socket4FD]; + else if (self->socket6FD != SOCKET_NULL) + result = [self localPortFromSocket6:self->socket6FD]; + }); + + return result; + } +} + +- (NSString *)connectedHost4 +{ + if (socket4FD != SOCKET_NULL) + return [self connectedHostFromSocket4:socket4FD]; + + return nil; +} + +- (NSString *)connectedHost6 +{ + if (socket6FD != SOCKET_NULL) + return [self connectedHostFromSocket6:socket6FD]; + + return nil; +} + +- (uint16_t)connectedPort4 +{ + if (socket4FD != SOCKET_NULL) + return [self connectedPortFromSocket4:socket4FD]; + + return 0; +} + +- (uint16_t)connectedPort6 +{ + if (socket6FD != SOCKET_NULL) + return [self connectedPortFromSocket6:socket6FD]; + + return 0; +} + +- (NSString *)localHost4 +{ + if (socket4FD != SOCKET_NULL) + return [self localHostFromSocket4:socket4FD]; + + return nil; +} + +- (NSString *)localHost6 +{ + if (socket6FD != SOCKET_NULL) + return [self localHostFromSocket6:socket6FD]; + + return nil; +} + +- (uint16_t)localPort4 +{ + if (socket4FD != SOCKET_NULL) + return [self localPortFromSocket4:socket4FD]; + + return 0; +} + +- (uint16_t)localPort6 +{ + if (socket6FD != SOCKET_NULL) + return [self localPortFromSocket6:socket6FD]; + + return 0; +} + +- (NSString *)connectedHostFromSocket4:(int)socketFD +{ + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getpeername(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) + { + return nil; + } + return [[self class] hostFromSockaddr4:&sockaddr4]; +} + +- (NSString *)connectedHostFromSocket6:(int)socketFD +{ + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getpeername(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) + { + return nil; + } + return [[self class] hostFromSockaddr6:&sockaddr6]; +} + +- (uint16_t)connectedPortFromSocket4:(int)socketFD +{ + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getpeername(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) + { + return 0; + } + return [[self class] portFromSockaddr4:&sockaddr4]; +} + +- (uint16_t)connectedPortFromSocket6:(int)socketFD +{ + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getpeername(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) + { + return 0; + } + return [[self class] portFromSockaddr6:&sockaddr6]; +} + +- (NSURL *)connectedUrlFromSocketUN:(int)socketFD +{ + struct sockaddr_un sockaddr; + socklen_t sockaddrlen = sizeof(sockaddr); + + if (getpeername(socketFD, (struct sockaddr *)&sockaddr, &sockaddrlen) < 0) + { + return 0; + } + return [[self class] urlFromSockaddrUN:&sockaddr]; +} + +- (NSString *)localHostFromSocket4:(int)socketFD +{ + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) + { + return nil; + } + return [[self class] hostFromSockaddr4:&sockaddr4]; +} + +- (NSString *)localHostFromSocket6:(int)socketFD +{ + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) + { + return nil; + } + return [[self class] hostFromSockaddr6:&sockaddr6]; +} + +- (uint16_t)localPortFromSocket4:(int)socketFD +{ + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) + { + return 0; + } + return [[self class] portFromSockaddr4:&sockaddr4]; +} + +- (uint16_t)localPortFromSocket6:(int)socketFD +{ + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) + { + return 0; + } + return [[self class] portFromSockaddr6:&sockaddr6]; +} + +- (NSData *)connectedAddress +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + if (self->socket4FD != SOCKET_NULL) + { + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getpeername(self->socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) + { + result = [[NSData alloc] initWithBytes:&sockaddr4 length:sockaddr4len]; + } + } + + if (self->socket6FD != SOCKET_NULL) + { + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getpeername(self->socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) + { + result = [[NSData alloc] initWithBytes:&sockaddr6 length:sockaddr6len]; + } + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (NSData *)localAddress +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + if (self->socket4FD != SOCKET_NULL) + { + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getsockname(self->socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) + { + result = [[NSData alloc] initWithBytes:&sockaddr4 length:sockaddr4len]; + } + } + + if (self->socket6FD != SOCKET_NULL) + { + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getsockname(self->socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) + { + result = [[NSData alloc] initWithBytes:&sockaddr6 length:sockaddr6len]; + } + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isIPv4 +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return (socket4FD != SOCKET_NULL); + } + else + { + __block BOOL result = NO; + + dispatch_sync(socketQueue, ^{ + result = (self->socket4FD != SOCKET_NULL); + }); + + return result; + } +} + +- (BOOL)isIPv6 +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return (socket6FD != SOCKET_NULL); + } + else + { + __block BOOL result = NO; + + dispatch_sync(socketQueue, ^{ + result = (self->socket6FD != SOCKET_NULL); + }); + + return result; + } +} + +- (BOOL)isSecure +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return (flags & kSocketSecure) ? YES : NO; + } + else + { + __block BOOL result; + + dispatch_sync(socketQueue, ^{ + result = (self->flags & kSocketSecure) ? YES : NO; + }); + + return result; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Utilities +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Finds the address of an interface description. + * An inteface description may be an interface name (en0, en1, lo0) or corresponding IP (192.168.4.34). + * + * The interface description may optionally contain a port number at the end, separated by a colon. + * If a non-zero port parameter is provided, any port number in the interface description is ignored. + * + * The returned value is a 'struct sockaddr' wrapped in an NSMutableData object. +**/ +- (void)getInterfaceAddress4:(NSMutableData **)interfaceAddr4Ptr + address6:(NSMutableData **)interfaceAddr6Ptr + fromDescription:(NSString *)interfaceDescription + port:(uint16_t)port +{ + NSMutableData *addr4 = nil; + NSMutableData *addr6 = nil; + + NSString *interface = nil; + + NSArray *components = [interfaceDescription componentsSeparatedByString:@":"]; + if ([components count] > 0) + { + NSString *temp = [components objectAtIndex:0]; + if ([temp length] > 0) + { + interface = temp; + } + } + if ([components count] > 1 && port == 0) + { + NSString *temp = [components objectAtIndex:1]; + long portL = strtol([temp UTF8String], NULL, 10); + + if (portL > 0 && portL <= UINT16_MAX) + { + port = (uint16_t)portL; + } + } + + if (interface == nil) + { + // ANY address + + struct sockaddr_in sockaddr4; + memset(&sockaddr4, 0, sizeof(sockaddr4)); + + sockaddr4.sin_len = sizeof(sockaddr4); + sockaddr4.sin_family = AF_INET; + sockaddr4.sin_port = htons(port); + sockaddr4.sin_addr.s_addr = htonl(INADDR_ANY); + + struct sockaddr_in6 sockaddr6; + memset(&sockaddr6, 0, sizeof(sockaddr6)); + + sockaddr6.sin6_len = sizeof(sockaddr6); + sockaddr6.sin6_family = AF_INET6; + sockaddr6.sin6_port = htons(port); + sockaddr6.sin6_addr = in6addr_any; + + addr4 = [NSMutableData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; + addr6 = [NSMutableData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; + } + else if ([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"]) + { + // LOOPBACK address + + struct sockaddr_in sockaddr4; + memset(&sockaddr4, 0, sizeof(sockaddr4)); + + sockaddr4.sin_len = sizeof(sockaddr4); + sockaddr4.sin_family = AF_INET; + sockaddr4.sin_port = htons(port); + sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + struct sockaddr_in6 sockaddr6; + memset(&sockaddr6, 0, sizeof(sockaddr6)); + + sockaddr6.sin6_len = sizeof(sockaddr6); + sockaddr6.sin6_family = AF_INET6; + sockaddr6.sin6_port = htons(port); + sockaddr6.sin6_addr = in6addr_loopback; + + addr4 = [NSMutableData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; + addr6 = [NSMutableData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; + } + else + { + const char *iface = [interface UTF8String]; + + struct ifaddrs *addrs; + const struct ifaddrs *cursor; + + if ((getifaddrs(&addrs) == 0)) + { + cursor = addrs; + while (cursor != NULL) + { + if ((addr4 == nil) && (cursor->ifa_addr->sa_family == AF_INET)) + { + // IPv4 + + struct sockaddr_in nativeAddr4; + memcpy(&nativeAddr4, cursor->ifa_addr, sizeof(nativeAddr4)); + + if (strcmp(cursor->ifa_name, iface) == 0) + { + // Name match + + nativeAddr4.sin_port = htons(port); + + addr4 = [NSMutableData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; + } + else + { + char ip[INET_ADDRSTRLEN]; + + const char *conversion = inet_ntop(AF_INET, &nativeAddr4.sin_addr, ip, sizeof(ip)); + + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) + { + // IP match + + nativeAddr4.sin_port = htons(port); + + addr4 = [NSMutableData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; + } + } + } + else if ((addr6 == nil) && (cursor->ifa_addr->sa_family == AF_INET6)) + { + // IPv6 + + struct sockaddr_in6 nativeAddr6; + memcpy(&nativeAddr6, cursor->ifa_addr, sizeof(nativeAddr6)); + + if (strcmp(cursor->ifa_name, iface) == 0) + { + // Name match + + nativeAddr6.sin6_port = htons(port); + + addr6 = [NSMutableData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; + } + else + { + char ip[INET6_ADDRSTRLEN]; + + const char *conversion = inet_ntop(AF_INET6, &nativeAddr6.sin6_addr, ip, sizeof(ip)); + + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) + { + // IP match + + nativeAddr6.sin6_port = htons(port); + + addr6 = [NSMutableData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; + } + } + } + + cursor = cursor->ifa_next; + } + + freeifaddrs(addrs); + } + } + + if (interfaceAddr4Ptr) *interfaceAddr4Ptr = addr4; + if (interfaceAddr6Ptr) *interfaceAddr6Ptr = addr6; +} + +- (NSData *)getInterfaceAddressFromUrl:(NSURL *)url +{ + NSString *path = url.path; + if (path.length == 0) { + return nil; + } + + struct sockaddr_un nativeAddr; + nativeAddr.sun_family = AF_UNIX; + strlcpy(nativeAddr.sun_path, path.fileSystemRepresentation, sizeof(nativeAddr.sun_path)); + nativeAddr.sun_len = (unsigned char)SUN_LEN(&nativeAddr); + NSData *interface = [NSData dataWithBytes:&nativeAddr length:sizeof(struct sockaddr_un)]; + + return interface; +} + +- (void)setupReadAndWriteSourcesForNewlyConnectedSocket:(int)socketFD +{ + readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socketFD, 0, socketQueue); + writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socketFD, 0, socketQueue); + + // Setup event handlers + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(readSource, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + LogVerbose(@"readEventBlock"); + + strongSelf->socketFDBytesAvailable = dispatch_source_get_data(strongSelf->readSource); + LogVerbose(@"socketFDBytesAvailable: %lu", strongSelf->socketFDBytesAvailable); + + if (strongSelf->socketFDBytesAvailable > 0) + [strongSelf doReadData]; + else + [strongSelf doReadEOF]; + + #pragma clang diagnostic pop + }}); + + dispatch_source_set_event_handler(writeSource, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + LogVerbose(@"writeEventBlock"); + + strongSelf->flags |= kSocketCanAcceptBytes; + [strongSelf doWriteData]; + + #pragma clang diagnostic pop + }}); + + // Setup cancel handlers + + __block int socketFDRefCount = 2; + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theReadSource = readSource; + dispatch_source_t theWriteSource = writeSource; + #endif + + dispatch_source_set_cancel_handler(readSource, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + LogVerbose(@"readCancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(readSource)"); + dispatch_release(theReadSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socketFD)"); + close(socketFD); + } + + #pragma clang diagnostic pop + }); + + dispatch_source_set_cancel_handler(writeSource, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + LogVerbose(@"writeCancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(writeSource)"); + dispatch_release(theWriteSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socketFD)"); + close(socketFD); + } + + #pragma clang diagnostic pop + }); + + // We will not be able to read until data arrives. + // But we should be able to write immediately. + + socketFDBytesAvailable = 0; + flags &= ~kReadSourceSuspended; + + LogVerbose(@"dispatch_resume(readSource)"); + dispatch_resume(readSource); + + flags |= kSocketCanAcceptBytes; + flags |= kWriteSourceSuspended; +} + +- (BOOL)usingCFStreamForTLS +{ + #if TARGET_OS_IPHONE + + if ((flags & kSocketSecure) && (flags & kUsingCFStreamForTLS)) + { + // The startTLS method was given the GCDAsyncSocketUseCFStreamForTLS flag. + + return YES; + } + + #endif + + return NO; +} + +- (BOOL)usingSecureTransportForTLS +{ + // Invoking this method is equivalent to ![self usingCFStreamForTLS] (just more readable) + + #if TARGET_OS_IPHONE + + if ((flags & kSocketSecure) && (flags & kUsingCFStreamForTLS)) + { + // The startTLS method was given the GCDAsyncSocketUseCFStreamForTLS flag. + + return NO; + } + + #endif + + return YES; +} + +- (void)suspendReadSource +{ + if (!(flags & kReadSourceSuspended)) + { + LogVerbose(@"dispatch_suspend(readSource)"); + + dispatch_suspend(readSource); + flags |= kReadSourceSuspended; + } +} + +- (void)resumeReadSource +{ + if (flags & kReadSourceSuspended) + { + LogVerbose(@"dispatch_resume(readSource)"); + + dispatch_resume(readSource); + flags &= ~kReadSourceSuspended; + } +} + +- (void)suspendWriteSource +{ + if (!(flags & kWriteSourceSuspended)) + { + LogVerbose(@"dispatch_suspend(writeSource)"); + + dispatch_suspend(writeSource); + flags |= kWriteSourceSuspended; + } +} + +- (void)resumeWriteSource +{ + if (flags & kWriteSourceSuspended) + { + LogVerbose(@"dispatch_resume(writeSource)"); + + dispatch_resume(writeSource); + flags &= ~kWriteSourceSuspended; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Reading +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self readDataWithTimeout:timeout buffer:nil bufferOffset:0 maxLength:0 tag:tag]; +} + +- (void)readDataWithTimeout:(NSTimeInterval)timeout + buffer:(NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag +{ + [self readDataWithTimeout:timeout buffer:buffer bufferOffset:offset maxLength:0 tag:tag]; +} + +- (void)readDataWithTimeout:(NSTimeInterval)timeout + buffer:(NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + maxLength:(NSUInteger)length + tag:(long)tag +{ + if (offset > [buffer length]) { + LogWarn(@"Cannot read: offset > [buffer length]"); + return; + } + + GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer + startOffset:offset + maxLength:length + timeout:timeout + readLength:0 + terminator:nil + tag:tag]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + LogTrace(); + + if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) + { + [self->readQueue addObject:packet]; + [self maybeDequeueRead]; + } + }}); + + // Do not rely on the block being run in order to release the packet, + // as the queue might get released without the block completing. +} + +- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self readDataToLength:length withTimeout:timeout buffer:nil bufferOffset:0 tag:tag]; +} + +- (void)readDataToLength:(NSUInteger)length + withTimeout:(NSTimeInterval)timeout + buffer:(NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag +{ + if (length == 0) { + LogWarn(@"Cannot read: length == 0"); + return; + } + if (offset > [buffer length]) { + LogWarn(@"Cannot read: offset > [buffer length]"); + return; + } + + GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer + startOffset:offset + maxLength:0 + timeout:timeout + readLength:length + terminator:nil + tag:tag]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + LogTrace(); + + if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) + { + [self->readQueue addObject:packet]; + [self maybeDequeueRead]; + } + }}); + + // Do not rely on the block being run in order to release the packet, + // as the queue might get released without the block completing. +} + +- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self readDataToData:data withTimeout:timeout buffer:nil bufferOffset:0 maxLength:0 tag:tag]; +} + +- (void)readDataToData:(NSData *)data + withTimeout:(NSTimeInterval)timeout + buffer:(NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + tag:(long)tag +{ + [self readDataToData:data withTimeout:timeout buffer:buffer bufferOffset:offset maxLength:0 tag:tag]; +} + +- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag +{ + [self readDataToData:data withTimeout:timeout buffer:nil bufferOffset:0 maxLength:length tag:tag]; +} + +- (void)readDataToData:(NSData *)data + withTimeout:(NSTimeInterval)timeout + buffer:(NSMutableData *)buffer + bufferOffset:(NSUInteger)offset + maxLength:(NSUInteger)maxLength + tag:(long)tag +{ + if ([data length] == 0) { + LogWarn(@"Cannot read: [data length] == 0"); + return; + } + if (offset > [buffer length]) { + LogWarn(@"Cannot read: offset > [buffer length]"); + return; + } + if (maxLength > 0 && maxLength < [data length]) { + LogWarn(@"Cannot read: maxLength > 0 && maxLength < [data length]"); + return; + } + + GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer + startOffset:offset + maxLength:maxLength + timeout:timeout + readLength:0 + terminator:data + tag:tag]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + LogTrace(); + + if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) + { + [self->readQueue addObject:packet]; + [self maybeDequeueRead]; + } + }}); + + // Do not rely on the block being run in order to release the packet, + // as the queue might get released without the block completing. +} + +- (float)progressOfReadReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr +{ + __block float result = 0.0F; + + dispatch_block_t block = ^{ + + if (!self->currentRead || ![self->currentRead isKindOfClass:[GCDAsyncReadPacket class]]) + { + // We're not reading anything right now. + + if (tagPtr != NULL) *tagPtr = 0; + if (donePtr != NULL) *donePtr = 0; + if (totalPtr != NULL) *totalPtr = 0; + + result = NAN; + } + else + { + // It's only possible to know the progress of our read if we're reading to a certain length. + // If we're reading to data, we of course have no idea when the data will arrive. + // If we're reading to timeout, then we have no idea when the next chunk of data will arrive. + + NSUInteger done = self->currentRead->bytesDone; + NSUInteger total = self->currentRead->readLength; + + if (tagPtr != NULL) *tagPtr = self->currentRead->tag; + if (donePtr != NULL) *donePtr = done; + if (totalPtr != NULL) *totalPtr = total; + + if (total > 0) + result = (float)done / (float)total; + else + result = 1.0F; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +/** + * This method starts a new read, if needed. + * + * It is called when: + * - a user requests a read + * - after a read request has finished (to handle the next request) + * - immediately after the socket opens to handle any pending requests + * + * This method also handles auto-disconnect post read/write completion. +**/ +- (void)maybeDequeueRead +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + // If we're not currently processing a read AND we have an available read stream + if ((currentRead == nil) && (flags & kConnected)) + { + if ([readQueue count] > 0) + { + // Dequeue the next object in the write queue + currentRead = [readQueue objectAtIndex:0]; + [readQueue removeObjectAtIndex:0]; + + + if ([currentRead isKindOfClass:[GCDAsyncSpecialPacket class]]) + { + LogVerbose(@"Dequeued GCDAsyncSpecialPacket"); + + // Attempt to start TLS + flags |= kStartingReadTLS; + + // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set + [self maybeStartTLS]; + } + else + { + LogVerbose(@"Dequeued GCDAsyncReadPacket"); + + // Setup read timer (if needed) + [self setupReadTimerWithTimeout:currentRead->timeout]; + + // Immediately read, if possible + [self doReadData]; + } + } + else if (flags & kDisconnectAfterReads) + { + if (flags & kDisconnectAfterWrites) + { + if (([writeQueue count] == 0) && (currentWrite == nil)) + { + [self closeWithError:nil]; + } + } + else + { + [self closeWithError:nil]; + } + } + else if (flags & kSocketSecure) + { + [self flushSSLBuffers]; + + // Edge case: + // + // We just drained all data from the ssl buffers, + // and all known data from the socket (socketFDBytesAvailable). + // + // If we didn't get any data from this process, + // then we may have reached the end of the TCP stream. + // + // Be sure callbacks are enabled so we're notified about a disconnection. + + if ([preBuffer availableBytes] == 0) + { + if ([self usingCFStreamForTLS]) { + // Callbacks never disabled + } + else { + [self resumeReadSource]; + } + } + } + } +} + +- (void)flushSSLBuffers +{ + LogTrace(); + + NSAssert((flags & kSocketSecure), @"Cannot flush ssl buffers on non-secure socket"); + + if ([preBuffer availableBytes] > 0) + { + // Only flush the ssl buffers if the prebuffer is empty. + // This is to avoid growing the prebuffer inifinitely large. + + return; + } + + #if TARGET_OS_IPHONE + + if ([self usingCFStreamForTLS]) + { + if ((flags & kSecureSocketHasBytesAvailable) && CFReadStreamHasBytesAvailable(readStream)) + { + LogVerbose(@"%@ - Flushing ssl buffers into prebuffer...", THIS_METHOD); + + CFIndex defaultBytesToRead = (1024 * 4); + + [preBuffer ensureCapacityForWrite:defaultBytesToRead]; + + uint8_t *buffer = [preBuffer writeBuffer]; + + CFIndex result = CFReadStreamRead(readStream, buffer, defaultBytesToRead); + LogVerbose(@"%@ - CFReadStreamRead(): result = %i", THIS_METHOD, (int)result); + + if (result > 0) + { + [preBuffer didWrite:result]; + } + + flags &= ~kSecureSocketHasBytesAvailable; + } + + return; + } + + #endif + + __block NSUInteger estimatedBytesAvailable = 0; + + dispatch_block_t updateEstimatedBytesAvailable = ^{ + + // Figure out if there is any data available to be read + // + // socketFDBytesAvailable <- Number of encrypted bytes we haven't read from the bsd socket + // [sslPreBuffer availableBytes] <- Number of encrypted bytes we've buffered from bsd socket + // sslInternalBufSize <- Number of decrypted bytes SecureTransport has buffered + // + // We call the variable "estimated" because we don't know how many decrypted bytes we'll get + // from the encrypted bytes in the sslPreBuffer. + // However, we do know this is an upper bound on the estimation. + + estimatedBytesAvailable = self->socketFDBytesAvailable + [self->sslPreBuffer availableBytes]; + + size_t sslInternalBufSize = 0; + SSLGetBufferedReadSize(self->sslContext, &sslInternalBufSize); + + estimatedBytesAvailable += sslInternalBufSize; + }; + + updateEstimatedBytesAvailable(); + + if (estimatedBytesAvailable > 0) + { + LogVerbose(@"%@ - Flushing ssl buffers into prebuffer...", THIS_METHOD); + + BOOL done = NO; + do + { + LogVerbose(@"%@ - estimatedBytesAvailable = %lu", THIS_METHOD, (unsigned long)estimatedBytesAvailable); + + // Make sure there's enough room in the prebuffer + + [preBuffer ensureCapacityForWrite:estimatedBytesAvailable]; + + // Read data into prebuffer + + uint8_t *buffer = [preBuffer writeBuffer]; + size_t bytesRead = 0; + + OSStatus result = SSLRead(sslContext, buffer, (size_t)estimatedBytesAvailable, &bytesRead); + LogVerbose(@"%@ - read from secure socket = %u", THIS_METHOD, (unsigned)bytesRead); + + if (bytesRead > 0) + { + [preBuffer didWrite:bytesRead]; + } + + LogVerbose(@"%@ - prebuffer.length = %zu", THIS_METHOD, [preBuffer availableBytes]); + + if (result != noErr) + { + done = YES; + } + else + { + updateEstimatedBytesAvailable(); + } + + } while (!done && estimatedBytesAvailable > 0); + } +} + +- (void)doReadData +{ + LogTrace(); + + // This method is called on the socketQueue. + // It might be called directly, or via the readSource when data is available to be read. + + if ((currentRead == nil) || (flags & kReadsPaused)) + { + LogVerbose(@"No currentRead or kReadsPaused"); + + // Unable to read at this time + + if (flags & kSocketSecure) + { + // Here's the situation: + // + // We have an established secure connection. + // There may not be a currentRead, but there might be encrypted data sitting around for us. + // When the user does get around to issuing a read, that encrypted data will need to be decrypted. + // + // So why make the user wait? + // We might as well get a head start on decrypting some data now. + // + // The other reason we do this has to do with detecting a socket disconnection. + // The SSL/TLS protocol has it's own disconnection handshake. + // So when a secure socket is closed, a "goodbye" packet comes across the wire. + // We want to make sure we read the "goodbye" packet so we can properly detect the TCP disconnection. + + [self flushSSLBuffers]; + } + + if ([self usingCFStreamForTLS]) + { + // CFReadStream only fires once when there is available data. + // It won't fire again until we've invoked CFReadStreamRead. + } + else + { + // If the readSource is firing, we need to pause it + // or else it will continue to fire over and over again. + // + // If the readSource is not firing, + // we want it to continue monitoring the socket. + + if (socketFDBytesAvailable > 0) + { + [self suspendReadSource]; + } + } + return; + } + + BOOL hasBytesAvailable = NO; + unsigned long estimatedBytesAvailable = 0; + + if ([self usingCFStreamForTLS]) + { + #if TARGET_OS_IPHONE + + // Requested CFStream, rather than SecureTransport, for TLS (via GCDAsyncSocketUseCFStreamForTLS) + + estimatedBytesAvailable = 0; + if ((flags & kSecureSocketHasBytesAvailable) && CFReadStreamHasBytesAvailable(readStream)) + hasBytesAvailable = YES; + else + hasBytesAvailable = NO; + + #endif + } + else + { + estimatedBytesAvailable = socketFDBytesAvailable; + + if (flags & kSocketSecure) + { + // There are 2 buffers to be aware of here. + // + // We are using SecureTransport, a TLS/SSL security layer which sits atop TCP. + // We issue a read to the SecureTranport API, which in turn issues a read to our SSLReadFunction. + // Our SSLReadFunction then reads from the BSD socket and returns the encrypted data to SecureTransport. + // SecureTransport then decrypts the data, and finally returns the decrypted data back to us. + // + // The first buffer is one we create. + // SecureTransport often requests small amounts of data. + // This has to do with the encypted packets that are coming across the TCP stream. + // But it's non-optimal to do a bunch of small reads from the BSD socket. + // So our SSLReadFunction reads all available data from the socket (optimizing the sys call) + // and may store excess in the sslPreBuffer. + + estimatedBytesAvailable += [sslPreBuffer availableBytes]; + + // The second buffer is within SecureTransport. + // As mentioned earlier, there are encrypted packets coming across the TCP stream. + // SecureTransport needs the entire packet to decrypt it. + // But if the entire packet produces X bytes of decrypted data, + // and we only asked SecureTransport for X/2 bytes of data, + // it must store the extra X/2 bytes of decrypted data for the next read. + // + // The SSLGetBufferedReadSize function will tell us the size of this internal buffer. + // From the documentation: + // + // "This function does not block or cause any low-level read operations to occur." + + size_t sslInternalBufSize = 0; + SSLGetBufferedReadSize(sslContext, &sslInternalBufSize); + + estimatedBytesAvailable += sslInternalBufSize; + } + + hasBytesAvailable = (estimatedBytesAvailable > 0); + } + + if ((hasBytesAvailable == NO) && ([preBuffer availableBytes] == 0)) + { + LogVerbose(@"No data available to read..."); + + // No data available to read. + + if (![self usingCFStreamForTLS]) + { + // Need to wait for readSource to fire and notify us of + // available data in the socket's internal read buffer. + + [self resumeReadSource]; + } + return; + } + + if (flags & kStartingReadTLS) + { + LogVerbose(@"Waiting for SSL/TLS handshake to complete"); + + // The readQueue is waiting for SSL/TLS handshake to complete. + + if (flags & kStartingWriteTLS) + { + if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) + { + // We are in the process of a SSL Handshake. + // We were waiting for incoming data which has just arrived. + + [self ssl_continueSSLHandshake]; + } + } + else + { + // We are still waiting for the writeQueue to drain and start the SSL/TLS process. + // We now know data is available to read. + + if (![self usingCFStreamForTLS]) + { + // Suspend the read source or else it will continue to fire nonstop. + + [self suspendReadSource]; + } + } + + return; + } + + BOOL done = NO; // Completed read operation + NSError *error = nil; // Error occurred + + NSUInteger totalBytesReadForCurrentRead = 0; + + // + // STEP 1 - READ FROM PREBUFFER + // + + if ([preBuffer availableBytes] > 0) + { + // There are 3 types of read packets: + // + // 1) Read all available data. + // 2) Read a specific length of data. + // 3) Read up to a particular terminator. + + NSUInteger bytesToCopy; + + if (currentRead->term != nil) + { + // Read type #3 - read up to a terminator + + bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; + } + else + { + // Read type #1 or #2 + + bytesToCopy = [currentRead readLengthForNonTermWithHint:[preBuffer availableBytes]]; + } + + // Make sure we have enough room in the buffer for our read. + + [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; + + // Copy bytes from prebuffer into packet buffer + + uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + + currentRead->bytesDone; + + memcpy(buffer, [preBuffer readBuffer], bytesToCopy); + + // Remove the copied bytes from the preBuffer + [preBuffer didRead:bytesToCopy]; + + LogVerbose(@"copied(%lu) preBufferLength(%zu)", (unsigned long)bytesToCopy, [preBuffer availableBytes]); + + // Update totals + + currentRead->bytesDone += bytesToCopy; + totalBytesReadForCurrentRead += bytesToCopy; + + // Check to see if the read operation is done + + if (currentRead->readLength > 0) + { + // Read type #2 - read a specific length of data + + done = (currentRead->bytesDone == currentRead->readLength); + } + else if (currentRead->term != nil) + { + // Read type #3 - read up to a terminator + + // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method + + if (!done && currentRead->maxLength > 0) + { + // We're not done and there's a set maxLength. + // Have we reached that maxLength yet? + + if (currentRead->bytesDone >= currentRead->maxLength) + { + error = [self readMaxedOutError]; + } + } + } + else + { + // Read type #1 - read all available data + // + // We're done as soon as + // - we've read all available data (in prebuffer and socket) + // - we've read the maxLength of read packet. + + done = ((currentRead->maxLength > 0) && (currentRead->bytesDone == currentRead->maxLength)); + } + + } + + // + // STEP 2 - READ FROM SOCKET + // + + BOOL socketEOF = (flags & kSocketHasReadEOF) ? YES : NO; // Nothing more to read via socket (end of file) + BOOL waiting = !done && !error && !socketEOF && !hasBytesAvailable; // Ran out of data, waiting for more + + if (!done && !error && !socketEOF && hasBytesAvailable) + { + NSAssert(([preBuffer availableBytes] == 0), @"Invalid logic"); + + BOOL readIntoPreBuffer = NO; + uint8_t *buffer = NULL; + size_t bytesRead = 0; + + if (flags & kSocketSecure) + { + if ([self usingCFStreamForTLS]) + { + #if TARGET_OS_IPHONE + + // Using CFStream, rather than SecureTransport, for TLS + + NSUInteger defaultReadLength = (1024 * 32); + + NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength + shouldPreBuffer:&readIntoPreBuffer]; + + // Make sure we have enough room in the buffer for our read. + // + // We are either reading directly into the currentRead->buffer, + // or we're reading into the temporary preBuffer. + + if (readIntoPreBuffer) + { + [preBuffer ensureCapacityForWrite:bytesToRead]; + + buffer = [preBuffer writeBuffer]; + } + else + { + [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; + + buffer = (uint8_t *)[currentRead->buffer mutableBytes] + + currentRead->startOffset + + currentRead->bytesDone; + } + + // Read data into buffer + + CFIndex result = CFReadStreamRead(readStream, buffer, (CFIndex)bytesToRead); + LogVerbose(@"CFReadStreamRead(): result = %i", (int)result); + + if (result < 0) + { + error = (__bridge_transfer NSError *)CFReadStreamCopyError(readStream); + } + else if (result == 0) + { + socketEOF = YES; + } + else + { + waiting = YES; + bytesRead = (size_t)result; + } + + // We only know how many decrypted bytes were read. + // The actual number of bytes read was likely more due to the overhead of the encryption. + // So we reset our flag, and rely on the next callback to alert us of more data. + flags &= ~kSecureSocketHasBytesAvailable; + + #endif + } + else + { + // Using SecureTransport for TLS + // + // We know: + // - how many bytes are available on the socket + // - how many encrypted bytes are sitting in the sslPreBuffer + // - how many decypted bytes are sitting in the sslContext + // + // But we do NOT know: + // - how many encypted bytes are sitting in the sslContext + // + // So we play the regular game of using an upper bound instead. + + NSUInteger defaultReadLength = (1024 * 32); + + if (defaultReadLength < estimatedBytesAvailable) { + defaultReadLength = estimatedBytesAvailable + (1024 * 16); + } + + NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength + shouldPreBuffer:&readIntoPreBuffer]; + + if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t + bytesToRead = SIZE_MAX; + } + + // Make sure we have enough room in the buffer for our read. + // + // We are either reading directly into the currentRead->buffer, + // or we're reading into the temporary preBuffer. + + if (readIntoPreBuffer) + { + [preBuffer ensureCapacityForWrite:bytesToRead]; + + buffer = [preBuffer writeBuffer]; + } + else + { + [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; + + buffer = (uint8_t *)[currentRead->buffer mutableBytes] + + currentRead->startOffset + + currentRead->bytesDone; + } + + // The documentation from Apple states: + // + // "a read operation might return errSSLWouldBlock, + // indicating that less data than requested was actually transferred" + // + // However, starting around 10.7, the function will sometimes return noErr, + // even if it didn't read as much data as requested. So we need to watch out for that. + + OSStatus result; + do + { + void *loop_buffer = buffer + bytesRead; + size_t loop_bytesToRead = (size_t)bytesToRead - bytesRead; + size_t loop_bytesRead = 0; + + result = SSLRead(sslContext, loop_buffer, loop_bytesToRead, &loop_bytesRead); + LogVerbose(@"read from secure socket = %u", (unsigned)loop_bytesRead); + + bytesRead += loop_bytesRead; + + } while ((result == noErr) && (bytesRead < bytesToRead)); + + + if (result != noErr) + { + if (result == errSSLWouldBlock) + waiting = YES; + else + { + if (result == errSSLClosedGraceful || result == errSSLClosedAbort) + { + // We've reached the end of the stream. + // Handle this the same way we would an EOF from the socket. + socketEOF = YES; + sslErrCode = result; + } + else + { + error = [self sslError:result]; + } + } + // It's possible that bytesRead > 0, even if the result was errSSLWouldBlock. + // This happens when the SSLRead function is able to read some data, + // but not the entire amount we requested. + + if (bytesRead <= 0) + { + bytesRead = 0; + } + } + + // Do not modify socketFDBytesAvailable. + // It will be updated via the SSLReadFunction(). + } + } + else + { + // Normal socket operation + + NSUInteger bytesToRead; + + // There are 3 types of read packets: + // + // 1) Read all available data. + // 2) Read a specific length of data. + // 3) Read up to a particular terminator. + + if (currentRead->term != nil) + { + // Read type #3 - read up to a terminator + + bytesToRead = [currentRead readLengthForTermWithHint:estimatedBytesAvailable + shouldPreBuffer:&readIntoPreBuffer]; + } + else + { + // Read type #1 or #2 + + bytesToRead = [currentRead readLengthForNonTermWithHint:estimatedBytesAvailable]; + } + + if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t (read param 3) + bytesToRead = SIZE_MAX; + } + + // Make sure we have enough room in the buffer for our read. + // + // We are either reading directly into the currentRead->buffer, + // or we're reading into the temporary preBuffer. + + if (readIntoPreBuffer) + { + [preBuffer ensureCapacityForWrite:bytesToRead]; + + buffer = [preBuffer writeBuffer]; + } + else + { + [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; + + buffer = (uint8_t *)[currentRead->buffer mutableBytes] + + currentRead->startOffset + + currentRead->bytesDone; + } + + // Read data into buffer + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + ssize_t result = read(socketFD, buffer, (size_t)bytesToRead); + LogVerbose(@"read from socket = %i", (int)result); + + if (result < 0) + { + if (errno == EWOULDBLOCK) + waiting = YES; + else + error = [self errorWithErrno:errno reason:@"Error in read() function"]; + + socketFDBytesAvailable = 0; + } + else if (result == 0) + { + socketEOF = YES; + socketFDBytesAvailable = 0; + } + else + { + bytesRead = result; + + if (bytesRead < bytesToRead) + { + // The read returned less data than requested. + // This means socketFDBytesAvailable was a bit off due to timing, + // because we read from the socket right when the readSource event was firing. + socketFDBytesAvailable = 0; + } + else + { + if (socketFDBytesAvailable <= bytesRead) + socketFDBytesAvailable = 0; + else + socketFDBytesAvailable -= bytesRead; + } + + if (socketFDBytesAvailable == 0) + { + waiting = YES; + } + } + } + + if (bytesRead > 0) + { + // Check to see if the read operation is done + + if (currentRead->readLength > 0) + { + // Read type #2 - read a specific length of data + // + // Note: We should never be using a prebuffer when we're reading a specific length of data. + + NSAssert(readIntoPreBuffer == NO, @"Invalid logic"); + + currentRead->bytesDone += bytesRead; + totalBytesReadForCurrentRead += bytesRead; + + done = (currentRead->bytesDone == currentRead->readLength); + } + else if (currentRead->term != nil) + { + // Read type #3 - read up to a terminator + + if (readIntoPreBuffer) + { + // We just read a big chunk of data into the preBuffer + + [preBuffer didWrite:bytesRead]; + LogVerbose(@"read data into preBuffer - preBuffer.length = %zu", [preBuffer availableBytes]); + + // Search for the terminating sequence + + NSUInteger bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; + LogVerbose(@"copying %lu bytes from preBuffer", (unsigned long)bytesToCopy); + + // Ensure there's room on the read packet's buffer + + [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; + + // Copy bytes from prebuffer into read buffer + + uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + + currentRead->bytesDone; + + memcpy(readBuf, [preBuffer readBuffer], bytesToCopy); + + // Remove the copied bytes from the prebuffer + [preBuffer didRead:bytesToCopy]; + LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); + + // Update totals + currentRead->bytesDone += bytesToCopy; + totalBytesReadForCurrentRead += bytesToCopy; + + // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method above + } + else + { + // We just read a big chunk of data directly into the packet's buffer. + // We need to move any overflow into the prebuffer. + + NSInteger overflow = [currentRead searchForTermAfterPreBuffering:bytesRead]; + + if (overflow == 0) + { + // Perfect match! + // Every byte we read stays in the read buffer, + // and the last byte we read was the last byte of the term. + + currentRead->bytesDone += bytesRead; + totalBytesReadForCurrentRead += bytesRead; + done = YES; + } + else if (overflow > 0) + { + // The term was found within the data that we read, + // and there are extra bytes that extend past the end of the term. + // We need to move these excess bytes out of the read packet and into the prebuffer. + + NSInteger underflow = bytesRead - overflow; + + // Copy excess data into preBuffer + + LogVerbose(@"copying %ld overflow bytes into preBuffer", (long)overflow); + [preBuffer ensureCapacityForWrite:overflow]; + + uint8_t *overflowBuffer = buffer + underflow; + memcpy([preBuffer writeBuffer], overflowBuffer, overflow); + + [preBuffer didWrite:overflow]; + LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); + + // Note: The completeCurrentRead method will trim the buffer for us. + + currentRead->bytesDone += underflow; + totalBytesReadForCurrentRead += underflow; + done = YES; + } + else + { + // The term was not found within the data that we read. + + currentRead->bytesDone += bytesRead; + totalBytesReadForCurrentRead += bytesRead; + done = NO; + } + } + + if (!done && currentRead->maxLength > 0) + { + // We're not done and there's a set maxLength. + // Have we reached that maxLength yet? + + if (currentRead->bytesDone >= currentRead->maxLength) + { + error = [self readMaxedOutError]; + } + } + } + else + { + // Read type #1 - read all available data + + if (readIntoPreBuffer) + { + // We just read a chunk of data into the preBuffer + + [preBuffer didWrite:bytesRead]; + + // Now copy the data into the read packet. + // + // Recall that we didn't read directly into the packet's buffer to avoid + // over-allocating memory since we had no clue how much data was available to be read. + // + // Ensure there's room on the read packet's buffer + + [currentRead ensureCapacityForAdditionalDataOfLength:bytesRead]; + + // Copy bytes from prebuffer into read buffer + + uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + + currentRead->bytesDone; + + memcpy(readBuf, [preBuffer readBuffer], bytesRead); + + // Remove the copied bytes from the prebuffer + [preBuffer didRead:bytesRead]; + + // Update totals + currentRead->bytesDone += bytesRead; + totalBytesReadForCurrentRead += bytesRead; + } + else + { + currentRead->bytesDone += bytesRead; + totalBytesReadForCurrentRead += bytesRead; + } + + done = YES; + } + + } // if (bytesRead > 0) + + } // if (!done && !error && !socketEOF && hasBytesAvailable) + + + if (!done && currentRead->readLength == 0 && currentRead->term == nil) + { + // Read type #1 - read all available data + // + // We might arrive here if we read data from the prebuffer but not from the socket. + + done = (totalBytesReadForCurrentRead > 0); + } + + // Check to see if we're done, or if we've made progress + + if (done) + { + [self completeCurrentRead]; + + if (!error && (!socketEOF || [preBuffer availableBytes] > 0)) + { + [self maybeDequeueRead]; + } + } + else if (totalBytesReadForCurrentRead > 0) + { + // We're not done read type #2 or #3 yet, but we have read in some bytes + // + // We ensure that `waiting` is set in order to resume the readSource (if it is suspended). It is + // possible to reach this point and `waiting` not be set, if the current read's length is + // sufficiently large. In that case, we may have read to some upperbound successfully, but + // that upperbound could be smaller than the desired length. + waiting = YES; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadPartialDataOfLength:tag:)]) + { + long theReadTag = currentRead->tag; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didReadPartialDataOfLength:totalBytesReadForCurrentRead tag:theReadTag]; + }}); + } + } + + // Check for errors + + if (error) + { + [self closeWithError:error]; + } + else if (socketEOF) + { + [self doReadEOF]; + } + else if (waiting) + { + if (![self usingCFStreamForTLS]) + { + // Monitor the socket for readability (if we're not already doing so) + [self resumeReadSource]; + } + } + + // Do not add any code here without first adding return statements in the error cases above. +} + +- (void)doReadEOF +{ + LogTrace(); + + // This method may be called more than once. + // If the EOF is read while there is still data in the preBuffer, + // then this method may be called continually after invocations of doReadData to see if it's time to disconnect. + + flags |= kSocketHasReadEOF; + + if (flags & kSocketSecure) + { + // If the SSL layer has any buffered data, flush it into the preBuffer now. + + [self flushSSLBuffers]; + } + + BOOL shouldDisconnect = NO; + NSError *error = nil; + + if ((flags & kStartingReadTLS) || (flags & kStartingWriteTLS)) + { + // We received an EOF during or prior to startTLS. + // The SSL/TLS handshake is now impossible, so this is an unrecoverable situation. + + shouldDisconnect = YES; + + if ([self usingSecureTransportForTLS]) + { + error = [self sslError:errSSLClosedAbort]; + } + } + else if (flags & kReadStreamClosed) + { + // The preBuffer has already been drained. + // The config allows half-duplex connections. + // We've previously checked the socket, and it appeared writeable. + // So we marked the read stream as closed and notified the delegate. + // + // As per the half-duplex contract, the socket will be closed when a write fails, + // or when the socket is manually closed. + + shouldDisconnect = NO; + } + else if ([preBuffer availableBytes] > 0) + { + LogVerbose(@"Socket reached EOF, but there is still data available in prebuffer"); + + // Although we won't be able to read any more data from the socket, + // there is existing data that has been prebuffered that we can read. + + shouldDisconnect = NO; + } + else if (config & kAllowHalfDuplexConnection) + { + // We just received an EOF (end of file) from the socket's read stream. + // This means the remote end of the socket (the peer we're connected to) + // has explicitly stated that it will not be sending us any more data. + // + // Query the socket to see if it is still writeable. (Perhaps the peer will continue reading data from us) + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + struct pollfd pfd[1]; + pfd[0].fd = socketFD; + pfd[0].events = POLLOUT; + pfd[0].revents = 0; + + poll(pfd, 1, 0); + + if (pfd[0].revents & POLLOUT) + { + // Socket appears to still be writeable + + shouldDisconnect = NO; + flags |= kReadStreamClosed; + + // Notify the delegate that we're going half-duplex + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidCloseReadStream:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socketDidCloseReadStream:self]; + }}); + } + } + else + { + shouldDisconnect = YES; + } + } + else + { + shouldDisconnect = YES; + } + + + if (shouldDisconnect) + { + if (error == nil) + { + if ([self usingSecureTransportForTLS]) + { + if (sslErrCode != noErr && sslErrCode != errSSLClosedGraceful) + { + error = [self sslError:sslErrCode]; + } + else + { + error = [self connectionClosedError]; + } + } + else + { + error = [self connectionClosedError]; + } + } + [self closeWithError:error]; + } + else + { + if (![self usingCFStreamForTLS]) + { + // Suspend the read source (if needed) + + [self suspendReadSource]; + } + } +} + +- (void)completeCurrentRead +{ + LogTrace(); + + NSAssert(currentRead, @"Trying to complete current read when there is no current read."); + + + NSData *result = nil; + + if (currentRead->bufferOwner) + { + // We created the buffer on behalf of the user. + // Trim our buffer to be the proper size. + [currentRead->buffer setLength:currentRead->bytesDone]; + + result = currentRead->buffer; + } + else + { + // We did NOT create the buffer. + // The buffer is owned by the caller. + // Only trim the buffer if we had to increase its size. + + if ([currentRead->buffer length] > currentRead->originalBufferLength) + { + NSUInteger readSize = currentRead->startOffset + currentRead->bytesDone; + NSUInteger origSize = currentRead->originalBufferLength; + + NSUInteger buffSize = MAX(readSize, origSize); + + [currentRead->buffer setLength:buffSize]; + } + + uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset; + + result = [NSData dataWithBytesNoCopy:buffer length:currentRead->bytesDone freeWhenDone:NO]; + } + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadData:withTag:)]) + { + GCDAsyncReadPacket *theRead = currentRead; // Ensure currentRead retained since result may not own buffer + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didReadData:result withTag:theRead->tag]; + }}); + } + + [self endCurrentRead]; +} + +- (void)endCurrentRead +{ + if (readTimer) + { + dispatch_source_cancel(readTimer); + readTimer = NULL; + } + + currentRead = nil; +} + +- (void)setupReadTimerWithTimeout:(NSTimeInterval)timeout +{ + if (timeout >= 0.0) + { + readTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(readTimer, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + [strongSelf doReadTimeout]; + + #pragma clang diagnostic pop + }}); + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theReadTimer = readTimer; + dispatch_source_set_cancel_handler(readTimer, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + LogVerbose(@"dispatch_release(readTimer)"); + dispatch_release(theReadTimer); + + #pragma clang diagnostic pop + }); + #endif + + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); + + dispatch_source_set_timer(readTimer, tt, DISPATCH_TIME_FOREVER, 0); + dispatch_resume(readTimer); + } +} + +- (void)doReadTimeout +{ + // This is a little bit tricky. + // Ideally we'd like to synchronously query the delegate about a timeout extension. + // But if we do so synchronously we risk a possible deadlock. + // So instead we have to do so asynchronously, and callback to ourselves from within the delegate block. + + flags |= kReadsPaused; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:shouldTimeoutReadWithTag:elapsed:bytesDone:)]) + { + GCDAsyncReadPacket *theRead = currentRead; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + NSTimeInterval timeoutExtension = 0.0; + + timeoutExtension = [theDelegate socket:self shouldTimeoutReadWithTag:theRead->tag + elapsed:theRead->timeout + bytesDone:theRead->bytesDone]; + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self doReadTimeoutWithExtension:timeoutExtension]; + }}); + }}); + } + else + { + [self doReadTimeoutWithExtension:0.0]; + } +} + +- (void)doReadTimeoutWithExtension:(NSTimeInterval)timeoutExtension +{ + if (currentRead) + { + if (timeoutExtension > 0.0) + { + currentRead->timeout += timeoutExtension; + + // Reschedule the timer + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutExtension * NSEC_PER_SEC)); + dispatch_source_set_timer(readTimer, tt, DISPATCH_TIME_FOREVER, 0); + + // Unpause reads, and continue + flags &= ~kReadsPaused; + [self doReadData]; + } + else + { + LogVerbose(@"ReadTimeout"); + + [self closeWithError:[self readTimeoutError]]; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Writing +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + if ([data length] == 0) return; + + GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + LogTrace(); + + if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) + { + [self->writeQueue addObject:packet]; + [self maybeDequeueWrite]; + } + }}); + + // Do not rely on the block being run in order to release the packet, + // as the queue might get released without the block completing. +} + +- (float)progressOfWriteReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr +{ + __block float result = 0.0F; + + dispatch_block_t block = ^{ + + if (!self->currentWrite || ![self->currentWrite isKindOfClass:[GCDAsyncWritePacket class]]) + { + // We're not writing anything right now. + + if (tagPtr != NULL) *tagPtr = 0; + if (donePtr != NULL) *donePtr = 0; + if (totalPtr != NULL) *totalPtr = 0; + + result = NAN; + } + else + { + NSUInteger done = self->currentWrite->bytesDone; + NSUInteger total = [self->currentWrite->buffer length]; + + if (tagPtr != NULL) *tagPtr = self->currentWrite->tag; + if (donePtr != NULL) *donePtr = done; + if (totalPtr != NULL) *totalPtr = total; + + result = (float)done / (float)total; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +/** + * Conditionally starts a new write. + * + * It is called when: + * - a user requests a write + * - after a write request has finished (to handle the next request) + * - immediately after the socket opens to handle any pending requests + * + * This method also handles auto-disconnect post read/write completion. +**/ +- (void)maybeDequeueWrite +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + // If we're not currently processing a write AND we have an available write stream + if ((currentWrite == nil) && (flags & kConnected)) + { + if ([writeQueue count] > 0) + { + // Dequeue the next object in the write queue + currentWrite = [writeQueue objectAtIndex:0]; + [writeQueue removeObjectAtIndex:0]; + + + if ([currentWrite isKindOfClass:[GCDAsyncSpecialPacket class]]) + { + LogVerbose(@"Dequeued GCDAsyncSpecialPacket"); + + // Attempt to start TLS + flags |= kStartingWriteTLS; + + // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set + [self maybeStartTLS]; + } + else + { + LogVerbose(@"Dequeued GCDAsyncWritePacket"); + + // Setup write timer (if needed) + [self setupWriteTimerWithTimeout:currentWrite->timeout]; + + // Immediately write, if possible + [self doWriteData]; + } + } + else if (flags & kDisconnectAfterWrites) + { + if (flags & kDisconnectAfterReads) + { + if (([readQueue count] == 0) && (currentRead == nil)) + { + [self closeWithError:nil]; + } + } + else + { + [self closeWithError:nil]; + } + } + } +} + +- (void)doWriteData +{ + LogTrace(); + + // This method is called by the writeSource via the socketQueue + + if ((currentWrite == nil) || (flags & kWritesPaused)) + { + LogVerbose(@"No currentWrite or kWritesPaused"); + + // Unable to write at this time + + if ([self usingCFStreamForTLS]) + { + // CFWriteStream only fires once when there is available data. + // It won't fire again until we've invoked CFWriteStreamWrite. + } + else + { + // If the writeSource is firing, we need to pause it + // or else it will continue to fire over and over again. + + if (flags & kSocketCanAcceptBytes) + { + [self suspendWriteSource]; + } + } + return; + } + + if (!(flags & kSocketCanAcceptBytes)) + { + LogVerbose(@"No space available to write..."); + + // No space available to write. + + if (![self usingCFStreamForTLS]) + { + // Need to wait for writeSource to fire and notify us of + // available space in the socket's internal write buffer. + + [self resumeWriteSource]; + } + return; + } + + if (flags & kStartingWriteTLS) + { + LogVerbose(@"Waiting for SSL/TLS handshake to complete"); + + // The writeQueue is waiting for SSL/TLS handshake to complete. + + if (flags & kStartingReadTLS) + { + if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) + { + // We are in the process of a SSL Handshake. + // We were waiting for available space in the socket's internal OS buffer to continue writing. + + [self ssl_continueSSLHandshake]; + } + } + else + { + // We are still waiting for the readQueue to drain and start the SSL/TLS process. + // We now know we can write to the socket. + + if (![self usingCFStreamForTLS]) + { + // Suspend the write source or else it will continue to fire nonstop. + + [self suspendWriteSource]; + } + } + + return; + } + + // Note: This method is not called if currentWrite is a GCDAsyncSpecialPacket (startTLS packet) + + BOOL waiting = NO; + NSError *error = nil; + size_t bytesWritten = 0; + + if (flags & kSocketSecure) + { + if ([self usingCFStreamForTLS]) + { + #if TARGET_OS_IPHONE + + // + // Writing data using CFStream (over internal TLS) + // + + const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + currentWrite->bytesDone; + + NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone; + + if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) + { + bytesToWrite = SIZE_MAX; + } + + CFIndex result = CFWriteStreamWrite(writeStream, buffer, (CFIndex)bytesToWrite); + LogVerbose(@"CFWriteStreamWrite(%lu) = %li", (unsigned long)bytesToWrite, result); + + if (result < 0) + { + error = (__bridge_transfer NSError *)CFWriteStreamCopyError(writeStream); + } + else + { + bytesWritten = (size_t)result; + + // We always set waiting to true in this scenario. + // CFStream may have altered our underlying socket to non-blocking. + // Thus if we attempt to write without a callback, we may end up blocking our queue. + waiting = YES; + } + + #endif + } + else + { + // We're going to use the SSLWrite function. + // + // OSStatus SSLWrite(SSLContextRef context, const void *data, size_t dataLength, size_t *processed) + // + // Parameters: + // context - An SSL session context reference. + // data - A pointer to the buffer of data to write. + // dataLength - The amount, in bytes, of data to write. + // processed - On return, the length, in bytes, of the data actually written. + // + // It sounds pretty straight-forward, + // but there are a few caveats you should be aware of. + // + // The SSLWrite method operates in a non-obvious (and rather annoying) manner. + // According to the documentation: + // + // Because you may configure the underlying connection to operate in a non-blocking manner, + // a write operation might return errSSLWouldBlock, indicating that less data than requested + // was actually transferred. In this case, you should repeat the call to SSLWrite until some + // other result is returned. + // + // This sounds perfect, but when our SSLWriteFunction returns errSSLWouldBlock, + // then the SSLWrite method returns (with the proper errSSLWouldBlock return value), + // but it sets processed to dataLength !! + // + // In other words, if the SSLWrite function doesn't completely write all the data we tell it to, + // then it doesn't tell us how many bytes were actually written. So, for example, if we tell it to + // write 256 bytes then it might actually write 128 bytes, but then report 0 bytes written. + // + // You might be wondering: + // If the SSLWrite function doesn't tell us how many bytes were written, + // then how in the world are we supposed to update our parameters (buffer & bytesToWrite) + // for the next time we invoke SSLWrite? + // + // The answer is that SSLWrite cached all the data we told it to write, + // and it will push out that data next time we call SSLWrite. + // If we call SSLWrite with new data, it will push out the cached data first, and then the new data. + // If we call SSLWrite with empty data, then it will simply push out the cached data. + // + // For this purpose we're going to break large writes into a series of smaller writes. + // This allows us to report progress back to the delegate. + + OSStatus result; + + BOOL hasCachedDataToWrite = (sslWriteCachedLength > 0); + BOOL hasNewDataToWrite = YES; + + if (hasCachedDataToWrite) + { + size_t processed = 0; + + result = SSLWrite(sslContext, NULL, 0, &processed); + + if (result == noErr) + { + bytesWritten = sslWriteCachedLength; + sslWriteCachedLength = 0; + + if ([currentWrite->buffer length] == (currentWrite->bytesDone + bytesWritten)) + { + // We've written all data for the current write. + hasNewDataToWrite = NO; + } + } + else + { + if (result == errSSLWouldBlock) + { + waiting = YES; + } + else + { + error = [self sslError:result]; + } + + // Can't write any new data since we were unable to write the cached data. + hasNewDataToWrite = NO; + } + } + + if (hasNewDataToWrite) + { + const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + + currentWrite->bytesDone + + bytesWritten; + + NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone - bytesWritten; + + if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) + { + bytesToWrite = SIZE_MAX; + } + + size_t bytesRemaining = bytesToWrite; + + BOOL keepLooping = YES; + while (keepLooping) + { + const size_t sslMaxBytesToWrite = 32768; + size_t sslBytesToWrite = MIN(bytesRemaining, sslMaxBytesToWrite); + size_t sslBytesWritten = 0; + + result = SSLWrite(sslContext, buffer, sslBytesToWrite, &sslBytesWritten); + + if (result == noErr) + { + buffer += sslBytesWritten; + bytesWritten += sslBytesWritten; + bytesRemaining -= sslBytesWritten; + + keepLooping = (bytesRemaining > 0); + } + else + { + if (result == errSSLWouldBlock) + { + waiting = YES; + sslWriteCachedLength = sslBytesToWrite; + } + else + { + error = [self sslError:result]; + } + + keepLooping = NO; + } + + } // while (keepLooping) + + } // if (hasNewDataToWrite) + } + } + else + { + // + // Writing data directly over raw socket + // + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + currentWrite->bytesDone; + + NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone; + + if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) + { + bytesToWrite = SIZE_MAX; + } + + ssize_t result = write(socketFD, buffer, (size_t)bytesToWrite); + LogVerbose(@"wrote to socket = %zd", result); + + // Check results + if (result < 0) + { + if (errno == EWOULDBLOCK) + { + waiting = YES; + } + else + { + error = [self errorWithErrno:errno reason:@"Error in write() function"]; + } + } + else + { + bytesWritten = result; + } + } + + // We're done with our writing. + // If we explictly ran into a situation where the socket told us there was no room in the buffer, + // then we immediately resume listening for notifications. + // + // We must do this before we dequeue another write, + // as that may in turn invoke this method again. + // + // Note that if CFStream is involved, it may have maliciously put our socket in blocking mode. + + if (waiting) + { + flags &= ~kSocketCanAcceptBytes; + + if (![self usingCFStreamForTLS]) + { + [self resumeWriteSource]; + } + } + + // Check our results + + BOOL done = NO; + + if (bytesWritten > 0) + { + // Update total amount read for the current write + currentWrite->bytesDone += bytesWritten; + LogVerbose(@"currentWrite->bytesDone = %lu", (unsigned long)currentWrite->bytesDone); + + // Is packet done? + done = (currentWrite->bytesDone == [currentWrite->buffer length]); + } + + if (done) + { + [self completeCurrentWrite]; + + if (!error) + { + dispatch_async(socketQueue, ^{ @autoreleasepool{ + + [self maybeDequeueWrite]; + }}); + } + } + else + { + // We were unable to finish writing the data, + // so we're waiting for another callback to notify us of available space in the lower-level output buffer. + + if (!waiting && !error) + { + // This would be the case if our write was able to accept some data, but not all of it. + + flags &= ~kSocketCanAcceptBytes; + + if (![self usingCFStreamForTLS]) + { + [self resumeWriteSource]; + } + } + + if (bytesWritten > 0) + { + // We're not done with the entire write, but we have written some bytes + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWritePartialDataOfLength:tag:)]) + { + long theWriteTag = currentWrite->tag; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didWritePartialDataOfLength:bytesWritten tag:theWriteTag]; + }}); + } + } + } + + // Check for errors + + if (error) + { + [self closeWithError:[self errorWithErrno:errno reason:@"Error in write() function"]]; + } + + // Do not add any code here without first adding a return statement in the error case above. +} + +- (void)completeCurrentWrite +{ + LogTrace(); + + NSAssert(currentWrite, @"Trying to complete current write when there is no current write."); + + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWriteDataWithTag:)]) + { + long theWriteTag = currentWrite->tag; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didWriteDataWithTag:theWriteTag]; + }}); + } + + [self endCurrentWrite]; +} + +- (void)endCurrentWrite +{ + if (writeTimer) + { + dispatch_source_cancel(writeTimer); + writeTimer = NULL; + } + + currentWrite = nil; +} + +- (void)setupWriteTimerWithTimeout:(NSTimeInterval)timeout +{ + if (timeout >= 0.0) + { + writeTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); + + __weak GCDAsyncSocket *weakSelf = self; + + dispatch_source_set_event_handler(writeTimer, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf == nil) return_from_block; + + [strongSelf doWriteTimeout]; + + #pragma clang diagnostic pop + }}); + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theWriteTimer = writeTimer; + dispatch_source_set_cancel_handler(writeTimer, ^{ + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + LogVerbose(@"dispatch_release(writeTimer)"); + dispatch_release(theWriteTimer); + + #pragma clang diagnostic pop + }); + #endif + + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); + + dispatch_source_set_timer(writeTimer, tt, DISPATCH_TIME_FOREVER, 0); + dispatch_resume(writeTimer); + } +} + +- (void)doWriteTimeout +{ + // This is a little bit tricky. + // Ideally we'd like to synchronously query the delegate about a timeout extension. + // But if we do so synchronously we risk a possible deadlock. + // So instead we have to do so asynchronously, and callback to ourselves from within the delegate block. + + flags |= kWritesPaused; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)]) + { + GCDAsyncWritePacket *theWrite = currentWrite; + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + NSTimeInterval timeoutExtension = 0.0; + + timeoutExtension = [theDelegate socket:self shouldTimeoutWriteWithTag:theWrite->tag + elapsed:theWrite->timeout + bytesDone:theWrite->bytesDone]; + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self doWriteTimeoutWithExtension:timeoutExtension]; + }}); + }}); + } + else + { + [self doWriteTimeoutWithExtension:0.0]; + } +} + +- (void)doWriteTimeoutWithExtension:(NSTimeInterval)timeoutExtension +{ + if (currentWrite) + { + if (timeoutExtension > 0.0) + { + currentWrite->timeout += timeoutExtension; + + // Reschedule the timer + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutExtension * NSEC_PER_SEC)); + dispatch_source_set_timer(writeTimer, tt, DISPATCH_TIME_FOREVER, 0); + + // Unpause writes, and continue + flags &= ~kWritesPaused; + [self doWriteData]; + } + else + { + LogVerbose(@"WriteTimeout"); + + [self closeWithError:[self writeTimeoutError]]; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Security +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)startTLS:(NSDictionary *)tlsSettings +{ + LogTrace(); + + if (tlsSettings == nil) + { + // Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary, + // but causes problems if we later try to fetch the remote host's certificate. + // + // To be exact, it causes the following to return NULL instead of the normal result: + // CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates) + // + // So we use an empty dictionary instead, which works perfectly. + + tlsSettings = [NSDictionary dictionary]; + } + + GCDAsyncSpecialPacket *packet = [[GCDAsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + if ((self->flags & kSocketStarted) && !(self->flags & kQueuedTLS) && !(self->flags & kForbidReadsWrites)) + { + [self->readQueue addObject:packet]; + [self->writeQueue addObject:packet]; + + self->flags |= kQueuedTLS; + + [self maybeDequeueRead]; + [self maybeDequeueWrite]; + } + }}); + +} + +- (void)maybeStartTLS +{ + // We can't start TLS until: + // - All queued reads prior to the user calling startTLS are complete + // - All queued writes prior to the user calling startTLS are complete + // + // We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set + + if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) + { + BOOL useSecureTransport = YES; + + #if TARGET_OS_IPHONE + { + GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; + NSDictionary *tlsSettings = @{}; + if (tlsPacket) { + tlsSettings = tlsPacket->tlsSettings; + } + NSNumber *value = [tlsSettings objectForKey:GCDAsyncSocketUseCFStreamForTLS]; + if (value && [value boolValue]) + useSecureTransport = NO; + } + #endif + + if (useSecureTransport) + { + [self ssl_startTLS]; + } + else + { + #if TARGET_OS_IPHONE + [self cf_startTLS]; + #endif + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Security via SecureTransport +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (OSStatus)sslReadWithBuffer:(void *)buffer length:(size_t *)bufferLength +{ + LogVerbose(@"sslReadWithBuffer:%p length:%lu", buffer, (unsigned long)*bufferLength); + + if ((socketFDBytesAvailable == 0) && ([sslPreBuffer availableBytes] == 0)) + { + LogVerbose(@"%@ - No data available to read...", THIS_METHOD); + + // No data available to read. + // + // Need to wait for readSource to fire and notify us of + // available data in the socket's internal read buffer. + + [self resumeReadSource]; + + *bufferLength = 0; + return errSSLWouldBlock; + } + + size_t totalBytesRead = 0; + size_t totalBytesLeftToBeRead = *bufferLength; + + BOOL done = NO; + BOOL socketError = NO; + + // + // STEP 1 : READ FROM SSL PRE BUFFER + // + + size_t sslPreBufferLength = [sslPreBuffer availableBytes]; + + if (sslPreBufferLength > 0) + { + LogVerbose(@"%@: Reading from SSL pre buffer...", THIS_METHOD); + + size_t bytesToCopy; + if (sslPreBufferLength > totalBytesLeftToBeRead) + bytesToCopy = totalBytesLeftToBeRead; + else + bytesToCopy = sslPreBufferLength; + + LogVerbose(@"%@: Copying %zu bytes from sslPreBuffer", THIS_METHOD, bytesToCopy); + + memcpy(buffer, [sslPreBuffer readBuffer], bytesToCopy); + [sslPreBuffer didRead:bytesToCopy]; + + LogVerbose(@"%@: sslPreBuffer.length = %zu", THIS_METHOD, [sslPreBuffer availableBytes]); + + totalBytesRead += bytesToCopy; + totalBytesLeftToBeRead -= bytesToCopy; + + done = (totalBytesLeftToBeRead == 0); + + if (done) LogVerbose(@"%@: Complete", THIS_METHOD); + } + + // + // STEP 2 : READ FROM SOCKET + // + + if (!done && (socketFDBytesAvailable > 0)) + { + LogVerbose(@"%@: Reading from socket...", THIS_METHOD); + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + BOOL readIntoPreBuffer; + size_t bytesToRead; + uint8_t *buf; + + if (socketFDBytesAvailable > totalBytesLeftToBeRead) + { + // Read all available data from socket into sslPreBuffer. + // Then copy requested amount into dataBuffer. + + LogVerbose(@"%@: Reading into sslPreBuffer...", THIS_METHOD); + + [sslPreBuffer ensureCapacityForWrite:socketFDBytesAvailable]; + + readIntoPreBuffer = YES; + bytesToRead = (size_t)socketFDBytesAvailable; + buf = [sslPreBuffer writeBuffer]; + } + else + { + // Read available data from socket directly into dataBuffer. + + LogVerbose(@"%@: Reading directly into dataBuffer...", THIS_METHOD); + + readIntoPreBuffer = NO; + bytesToRead = totalBytesLeftToBeRead; + buf = (uint8_t *)buffer + totalBytesRead; + } + + ssize_t result = read(socketFD, buf, bytesToRead); + LogVerbose(@"%@: read from socket = %zd", THIS_METHOD, result); + + if (result < 0) + { + LogVerbose(@"%@: read errno = %i", THIS_METHOD, errno); + + if (errno != EWOULDBLOCK) + { + socketError = YES; + } + + socketFDBytesAvailable = 0; + } + else if (result == 0) + { + LogVerbose(@"%@: read EOF", THIS_METHOD); + + socketError = YES; + socketFDBytesAvailable = 0; + } + else + { + size_t bytesReadFromSocket = result; + + if (socketFDBytesAvailable > bytesReadFromSocket) + socketFDBytesAvailable -= bytesReadFromSocket; + else + socketFDBytesAvailable = 0; + + if (readIntoPreBuffer) + { + [sslPreBuffer didWrite:bytesReadFromSocket]; + + size_t bytesToCopy = MIN(totalBytesLeftToBeRead, bytesReadFromSocket); + + LogVerbose(@"%@: Copying %zu bytes out of sslPreBuffer", THIS_METHOD, bytesToCopy); + + memcpy((uint8_t *)buffer + totalBytesRead, [sslPreBuffer readBuffer], bytesToCopy); + [sslPreBuffer didRead:bytesToCopy]; + + totalBytesRead += bytesToCopy; + totalBytesLeftToBeRead -= bytesToCopy; + + LogVerbose(@"%@: sslPreBuffer.length = %zu", THIS_METHOD, [sslPreBuffer availableBytes]); + } + else + { + totalBytesRead += bytesReadFromSocket; + totalBytesLeftToBeRead -= bytesReadFromSocket; + } + + done = (totalBytesLeftToBeRead == 0); + + if (done) LogVerbose(@"%@: Complete", THIS_METHOD); + } + } + + *bufferLength = totalBytesRead; + + if (done) + return noErr; + + if (socketError) + return errSSLClosedAbort; + + return errSSLWouldBlock; +} + +- (OSStatus)sslWriteWithBuffer:(const void *)buffer length:(size_t *)bufferLength +{ + if (!(flags & kSocketCanAcceptBytes)) + { + // Unable to write. + // + // Need to wait for writeSource to fire and notify us of + // available space in the socket's internal write buffer. + + [self resumeWriteSource]; + + *bufferLength = 0; + return errSSLWouldBlock; + } + + size_t bytesToWrite = *bufferLength; + size_t bytesWritten = 0; + + BOOL done = NO; + BOOL socketError = NO; + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + ssize_t result = write(socketFD, buffer, bytesToWrite); + + if (result < 0) + { + if (errno != EWOULDBLOCK) + { + socketError = YES; + } + + flags &= ~kSocketCanAcceptBytes; + } + else if (result == 0) + { + flags &= ~kSocketCanAcceptBytes; + } + else + { + bytesWritten = result; + + done = (bytesWritten == bytesToWrite); + } + + *bufferLength = bytesWritten; + + if (done) + return noErr; + + if (socketError) + return errSSLClosedAbort; + + return errSSLWouldBlock; +} + +static OSStatus SSLReadFunction(SSLConnectionRef connection, void *data, size_t *dataLength) +{ + GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)connection; + + NSCAssert(dispatch_get_specific(asyncSocket->IsOnSocketQueueOrTargetQueueKey), @"What the deuce?"); + + return [asyncSocket sslReadWithBuffer:data length:dataLength]; +} + +static OSStatus SSLWriteFunction(SSLConnectionRef connection, const void *data, size_t *dataLength) +{ + GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)connection; + + NSCAssert(dispatch_get_specific(asyncSocket->IsOnSocketQueueOrTargetQueueKey), @"What the deuce?"); + + return [asyncSocket sslWriteWithBuffer:data length:dataLength]; +} + +- (void)ssl_startTLS +{ + LogTrace(); + + LogVerbose(@"Starting TLS (via SecureTransport)..."); + + OSStatus status; + + GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; + if (tlsPacket == nil) // Code to quiet the analyzer + { + NSAssert(NO, @"Logic error"); + + [self closeWithError:[self otherError:@"Logic error"]]; + return; + } + NSDictionary *tlsSettings = tlsPacket->tlsSettings; + + // Create SSLContext, and setup IO callbacks and connection ref + + NSNumber *isServerNumber = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLIsServer]; + BOOL isServer = [isServerNumber boolValue]; + + #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) + { + if (isServer) + sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType); + else + sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + + if (sslContext == NULL) + { + [self closeWithError:[self otherError:@"Error in SSLCreateContext"]]; + return; + } + } + #else // (__MAC_OS_X_VERSION_MIN_REQUIRED < 1080) + { + status = SSLNewContext(isServer, &sslContext); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLNewContext"]]; + return; + } + } + #endif + + status = SSLSetIOFuncs(sslContext, &SSLReadFunction, &SSLWriteFunction); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetIOFuncs"]]; + return; + } + + status = SSLSetConnection(sslContext, (__bridge SSLConnectionRef)self); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetConnection"]]; + return; + } + + + NSNumber *shouldManuallyEvaluateTrust = [tlsSettings objectForKey:GCDAsyncSocketManuallyEvaluateTrust]; + if ([shouldManuallyEvaluateTrust boolValue]) + { + if (isServer) + { + [self closeWithError:[self otherError:@"Manual trust validation is not supported for server sockets"]]; + return; + } + + status = SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, true); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetSessionOption"]]; + return; + } + + #if !TARGET_OS_IPHONE && (__MAC_OS_X_VERSION_MIN_REQUIRED < 1080) + + // Note from Apple's documentation: + // + // It is only necessary to call SSLSetEnableCertVerify on the Mac prior to OS X 10.8. + // On OS X 10.8 and later setting kSSLSessionOptionBreakOnServerAuth always disables the + // built-in trust evaluation. All versions of iOS behave like OS X 10.8 and thus + // SSLSetEnableCertVerify is not available on that platform at all. + + status = SSLSetEnableCertVerify(sslContext, NO); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetEnableCertVerify"]]; + return; + } + + #endif + } + + // Configure SSLContext from given settings + // + // Checklist: + // 1. kCFStreamSSLPeerName + // 2. kCFStreamSSLCertificates + // 3. GCDAsyncSocketSSLPeerID + // 4. GCDAsyncSocketSSLProtocolVersionMin + // 5. GCDAsyncSocketSSLProtocolVersionMax + // 6. GCDAsyncSocketSSLSessionOptionFalseStart + // 7. GCDAsyncSocketSSLSessionOptionSendOneByteRecord + // 8. GCDAsyncSocketSSLCipherSuites + // 9. GCDAsyncSocketSSLDiffieHellmanParameters (Mac) + // + // Deprecated (throw error): + // 10. kCFStreamSSLAllowsAnyRoot + // 11. kCFStreamSSLAllowsExpiredRoots + // 12. kCFStreamSSLAllowsExpiredCertificates + // 13. kCFStreamSSLValidatesCertificateChain + // 14. kCFStreamSSLLevel + + NSObject *value; + + // 1. kCFStreamSSLPeerName + + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLPeerName]; + if ([value isKindOfClass:[NSString class]]) + { + NSString *peerName = (NSString *)value; + + const char *peer = [peerName UTF8String]; + size_t peerLen = strlen(peer); + + status = SSLSetPeerDomainName(sslContext, peer, peerLen); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetPeerDomainName"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for kCFStreamSSLPeerName. Value must be of type NSString."); + + [self closeWithError:[self otherError:@"Invalid value for kCFStreamSSLPeerName."]]; + return; + } + + // 2. kCFStreamSSLCertificates + + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLCertificates]; + if ([value isKindOfClass:[NSArray class]]) + { + NSArray *certs = (NSArray *)value; + + status = SSLSetCertificate(sslContext, (__bridge CFArrayRef)certs); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetCertificate"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for kCFStreamSSLCertificates. Value must be of type NSArray."); + + [self closeWithError:[self otherError:@"Invalid value for kCFStreamSSLCertificates."]]; + return; + } + + // 3. GCDAsyncSocketSSLPeerID + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLPeerID]; + if ([value isKindOfClass:[NSData class]]) + { + NSData *peerIdData = (NSData *)value; + + status = SSLSetPeerID(sslContext, [peerIdData bytes], [peerIdData length]); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetPeerID"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLPeerID. Value must be of type NSData." + @" (You can convert strings to data using a method like" + @" [string dataUsingEncoding:NSUTF8StringEncoding])"); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLPeerID."]]; + return; + } + + // 4. GCDAsyncSocketSSLProtocolVersionMin + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLProtocolVersionMin]; + if ([value isKindOfClass:[NSNumber class]]) + { + SSLProtocol minProtocol = (SSLProtocol)[(NSNumber *)value intValue]; + if (minProtocol != kSSLProtocolUnknown) + { + status = SSLSetProtocolVersionMin(sslContext, minProtocol); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetProtocolVersionMin"]]; + return; + } + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLProtocolVersionMin. Value must be of type NSNumber."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLProtocolVersionMin."]]; + return; + } + + // 5. GCDAsyncSocketSSLProtocolVersionMax + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLProtocolVersionMax]; + if ([value isKindOfClass:[NSNumber class]]) + { + SSLProtocol maxProtocol = (SSLProtocol)[(NSNumber *)value intValue]; + if (maxProtocol != kSSLProtocolUnknown) + { + status = SSLSetProtocolVersionMax(sslContext, maxProtocol); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetProtocolVersionMax"]]; + return; + } + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLProtocolVersionMax. Value must be of type NSNumber."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLProtocolVersionMax."]]; + return; + } + + // 6. GCDAsyncSocketSSLSessionOptionFalseStart + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLSessionOptionFalseStart]; + if ([value isKindOfClass:[NSNumber class]]) + { + NSNumber *falseStart = (NSNumber *)value; + status = SSLSetSessionOption(sslContext, kSSLSessionOptionFalseStart, [falseStart boolValue]); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetSessionOption (kSSLSessionOptionFalseStart)"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLSessionOptionFalseStart. Value must be of type NSNumber."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLSessionOptionFalseStart."]]; + return; + } + + // 7. GCDAsyncSocketSSLSessionOptionSendOneByteRecord + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLSessionOptionSendOneByteRecord]; + if ([value isKindOfClass:[NSNumber class]]) + { + NSNumber *oneByteRecord = (NSNumber *)value; + status = SSLSetSessionOption(sslContext, kSSLSessionOptionSendOneByteRecord, [oneByteRecord boolValue]); + if (status != noErr) + { + [self closeWithError: + [self otherError:@"Error in SSLSetSessionOption (kSSLSessionOptionSendOneByteRecord)"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLSessionOptionSendOneByteRecord." + @" Value must be of type NSNumber."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLSessionOptionSendOneByteRecord."]]; + return; + } + + // 8. GCDAsyncSocketSSLCipherSuites + + value = [tlsSettings objectForKey:GCDAsyncSocketSSLCipherSuites]; + if ([value isKindOfClass:[NSArray class]]) + { + NSArray *cipherSuites = (NSArray *)value; + NSUInteger numberCiphers = [cipherSuites count]; + SSLCipherSuite ciphers[numberCiphers]; + + NSUInteger cipherIndex; + for (cipherIndex = 0; cipherIndex < numberCiphers; cipherIndex++) + { + NSNumber *cipherObject = [cipherSuites objectAtIndex:cipherIndex]; + ciphers[cipherIndex] = (SSLCipherSuite)[cipherObject unsignedIntValue]; + } + + status = SSLSetEnabledCiphers(sslContext, ciphers, numberCiphers); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetEnabledCiphers"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLCipherSuites. Value must be of type NSArray."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLCipherSuites."]]; + return; + } + + // 9. GCDAsyncSocketSSLDiffieHellmanParameters + + #if !TARGET_OS_IPHONE + value = [tlsSettings objectForKey:GCDAsyncSocketSSLDiffieHellmanParameters]; + if ([value isKindOfClass:[NSData class]]) + { + NSData *diffieHellmanData = (NSData *)value; + + status = SSLSetDiffieHellmanParams(sslContext, [diffieHellmanData bytes], [diffieHellmanData length]); + if (status != noErr) + { + [self closeWithError:[self otherError:@"Error in SSLSetDiffieHellmanParams"]]; + return; + } + } + else if (value) + { + NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLDiffieHellmanParameters. Value must be of type NSData."); + + [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLDiffieHellmanParameters."]]; + return; + } + #endif + + // DEPRECATED checks + + // 10. kCFStreamSSLAllowsAnyRoot + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsAnyRoot]; + #pragma clang diagnostic pop + if (value) + { + NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsAnyRoot" + @" - You must use manual trust evaluation"); + + [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsAnyRoot"]]; + return; + } + + // 11. kCFStreamSSLAllowsExpiredRoots + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsExpiredRoots]; + #pragma clang diagnostic pop + if (value) + { + NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsExpiredRoots" + @" - You must use manual trust evaluation"); + + [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsExpiredRoots"]]; + return; + } + + // 12. kCFStreamSSLValidatesCertificateChain + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLValidatesCertificateChain]; + #pragma clang diagnostic pop + if (value) + { + NSAssert(NO, @"Security option unavailable - kCFStreamSSLValidatesCertificateChain" + @" - You must use manual trust evaluation"); + + [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLValidatesCertificateChain"]]; + return; + } + + // 13. kCFStreamSSLAllowsExpiredCertificates + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsExpiredCertificates]; + #pragma clang diagnostic pop + if (value) + { + NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsExpiredCertificates" + @" - You must use manual trust evaluation"); + + [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsExpiredCertificates"]]; + return; + } + + // 14. kCFStreamSSLLevel + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLLevel]; + #pragma clang diagnostic pop + if (value) + { + NSAssert(NO, @"Security option unavailable - kCFStreamSSLLevel" + @" - You must use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMax"); + + [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLLevel"]]; + return; + } + + // Setup the sslPreBuffer + // + // Any data in the preBuffer needs to be moved into the sslPreBuffer, + // as this data is now part of the secure read stream. + + sslPreBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)]; + + size_t preBufferLength = [preBuffer availableBytes]; + + if (preBufferLength > 0) + { + [sslPreBuffer ensureCapacityForWrite:preBufferLength]; + + memcpy([sslPreBuffer writeBuffer], [preBuffer readBuffer], preBufferLength); + [preBuffer didRead:preBufferLength]; + [sslPreBuffer didWrite:preBufferLength]; + } + + sslErrCode = lastSSLHandshakeError = noErr; + + // Start the SSL Handshake process + + [self ssl_continueSSLHandshake]; +} + +- (void)ssl_continueSSLHandshake +{ + LogTrace(); + + // If the return value is noErr, the session is ready for normal secure communication. + // If the return value is errSSLWouldBlock, the SSLHandshake function must be called again. + // If the return value is errSSLServerAuthCompleted, we ask delegate if we should trust the + // server and then call SSLHandshake again to resume the handshake or close the connection + // errSSLPeerBadCert SSL error. + // Otherwise, the return value indicates an error code. + + OSStatus status = SSLHandshake(sslContext); + lastSSLHandshakeError = status; + + if (status == noErr) + { + LogVerbose(@"SSLHandshake complete"); + + flags &= ~kStartingReadTLS; + flags &= ~kStartingWriteTLS; + + flags |= kSocketSecure; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidSecure:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socketDidSecure:self]; + }}); + } + + [self endCurrentRead]; + [self endCurrentWrite]; + + [self maybeDequeueRead]; + [self maybeDequeueWrite]; + } + else if (status == errSSLPeerAuthCompleted) + { + LogVerbose(@"SSLHandshake peerAuthCompleted - awaiting delegate approval"); + + __block SecTrustRef trust = NULL; + status = SSLCopyPeerTrust(sslContext, &trust); + if (status != noErr) + { + [self closeWithError:[self sslError:status]]; + return; + } + + int aStateIndex = stateIndex; + dispatch_queue_t theSocketQueue = socketQueue; + + __weak GCDAsyncSocket *weakSelf = self; + + void (^comletionHandler)(BOOL) = ^(BOOL shouldTrust){ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + dispatch_async(theSocketQueue, ^{ @autoreleasepool { + + if (trust) { + CFRelease(trust); + trust = NULL; + } + + __strong GCDAsyncSocket *strongSelf = weakSelf; + if (strongSelf) + { + [strongSelf ssl_shouldTrustPeer:shouldTrust stateIndex:aStateIndex]; + } + }}); + + #pragma clang diagnostic pop + }}; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReceiveTrust:completionHandler:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socket:self didReceiveTrust:trust completionHandler:comletionHandler]; + }}); + } + else + { + if (trust) { + CFRelease(trust); + trust = NULL; + } + + NSString *msg = @"GCDAsyncSocketManuallyEvaluateTrust specified in tlsSettings," + @" but delegate doesn't implement socket:shouldTrustPeer:"; + + [self closeWithError:[self otherError:msg]]; + return; + } + } + else if (status == errSSLWouldBlock) + { + LogVerbose(@"SSLHandshake continues..."); + + // Handshake continues... + // + // This method will be called again from doReadData or doWriteData. + } + else + { + [self closeWithError:[self sslError:status]]; + } +} + +- (void)ssl_shouldTrustPeer:(BOOL)shouldTrust stateIndex:(int)aStateIndex +{ + LogTrace(); + + if (aStateIndex != stateIndex) + { + LogInfo(@"Ignoring ssl_shouldTrustPeer - invalid state (maybe disconnected)"); + + // One of the following is true + // - the socket was disconnected + // - the startTLS operation timed out + // - the completionHandler was already invoked once + + return; + } + + // Increment stateIndex to ensure completionHandler can only be called once. + stateIndex++; + + if (shouldTrust) + { + NSAssert(lastSSLHandshakeError == errSSLPeerAuthCompleted, @"ssl_shouldTrustPeer called when last error is %d and not errSSLPeerAuthCompleted", (int)lastSSLHandshakeError); + [self ssl_continueSSLHandshake]; + } + else + { + [self closeWithError:[self sslError:errSSLPeerBadCert]]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Security via CFStream +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if TARGET_OS_IPHONE + +- (void)cf_finishSSLHandshake +{ + LogTrace(); + + if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) + { + flags &= ~kStartingReadTLS; + flags &= ~kStartingWriteTLS; + + flags |= kSocketSecure; + + __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; + + if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidSecure:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate socketDidSecure:self]; + }}); + } + + [self endCurrentRead]; + [self endCurrentWrite]; + + [self maybeDequeueRead]; + [self maybeDequeueWrite]; + } +} + +- (void)cf_abortSSLHandshake:(NSError *)error +{ + LogTrace(); + + if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) + { + flags &= ~kStartingReadTLS; + flags &= ~kStartingWriteTLS; + + [self closeWithError:error]; + } +} + +- (void)cf_startTLS +{ + LogTrace(); + + LogVerbose(@"Starting TLS (via CFStream)..."); + + if ([preBuffer availableBytes] > 0) + { + NSString *msg = @"Invalid TLS transition. Handshake has already been read from socket."; + + [self closeWithError:[self otherError:msg]]; + return; + } + + [self suspendReadSource]; + [self suspendWriteSource]; + + socketFDBytesAvailable = 0; + flags &= ~kSocketCanAcceptBytes; + flags &= ~kSecureSocketHasBytesAvailable; + + flags |= kUsingCFStreamForTLS; + + if (![self createReadAndWriteStream]) + { + [self closeWithError:[self otherError:@"Error in CFStreamCreatePairWithSocket"]]; + return; + } + + if (![self registerForStreamCallbacksIncludingReadWrite:YES]) + { + [self closeWithError:[self otherError:@"Error in CFStreamSetClient"]]; + return; + } + + if (![self addStreamsToRunLoop]) + { + [self closeWithError:[self otherError:@"Error in CFStreamScheduleWithRunLoop"]]; + return; + } + + NSAssert([currentRead isKindOfClass:[GCDAsyncSpecialPacket class]], @"Invalid read packet for startTLS"); + NSAssert([currentWrite isKindOfClass:[GCDAsyncSpecialPacket class]], @"Invalid write packet for startTLS"); + + GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; + CFDictionaryRef tlsSettings = (__bridge CFDictionaryRef)tlsPacket->tlsSettings; + + // Getting an error concerning kCFStreamPropertySSLSettings ? + // You need to add the CFNetwork framework to your iOS application. + + BOOL r1 = CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, tlsSettings); + BOOL r2 = CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, tlsSettings); + + // For some reason, starting around the time of iOS 4.3, + // the first call to set the kCFStreamPropertySSLSettings will return true, + // but the second will return false. + // + // Order doesn't seem to matter. + // So you could call CFReadStreamSetProperty and then CFWriteStreamSetProperty, or you could reverse the order. + // Either way, the first call will return true, and the second returns false. + // + // Interestingly, this doesn't seem to affect anything. + // Which is not altogether unusual, as the documentation seems to suggest that (for many settings) + // setting it on one side of the stream automatically sets it for the other side of the stream. + // + // Although there isn't anything in the documentation to suggest that the second attempt would fail. + // + // Furthermore, this only seems to affect streams that are negotiating a security upgrade. + // In other words, the socket gets connected, there is some back-and-forth communication over the unsecure + // connection, and then a startTLS is issued. + // So this mostly affects newer protocols (XMPP, IMAP) as opposed to older protocols (HTTPS). + + if (!r1 && !r2) // Yes, the && is correct - workaround for apple bug. + { + [self closeWithError:[self otherError:@"Error in CFStreamSetProperty"]]; + return; + } + + if (![self openStreams]) + { + [self closeWithError:[self otherError:@"Error in CFStreamOpen"]]; + return; + } + + LogVerbose(@"Waiting for SSL Handshake to complete..."); +} + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark CFStream +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if TARGET_OS_IPHONE + ++ (void)ignore:(id)_ +{} + ++ (void)startCFStreamThreadIfNeeded +{ + LogTrace(); + + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + + cfstreamThreadRetainCount = 0; + cfstreamThreadSetupQueue = dispatch_queue_create("GCDAsyncSocket-CFStreamThreadSetup", DISPATCH_QUEUE_SERIAL); + }); + + dispatch_sync(cfstreamThreadSetupQueue, ^{ @autoreleasepool { + + if (++cfstreamThreadRetainCount == 1) + { + cfstreamThread = [[NSThread alloc] initWithTarget:self + selector:@selector(cfstreamThread:) + object:nil]; + [cfstreamThread start]; + } + }}); +} + ++ (void)stopCFStreamThreadIfNeeded +{ + LogTrace(); + + // The creation of the cfstreamThread is relatively expensive. + // So we'd like to keep it available for recycling. + // However, there's a tradeoff here, because it shouldn't remain alive forever. + // So what we're going to do is use a little delay before taking it down. + // This way it can be reused properly in situations where multiple sockets are continually in flux. + + int delayInSeconds = 30; + dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(when, cfstreamThreadSetupQueue, ^{ @autoreleasepool { + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wimplicit-retain-self" + + if (cfstreamThreadRetainCount == 0) + { + LogWarn(@"Logic error concerning cfstreamThread start / stop"); + return_from_block; + } + + if (--cfstreamThreadRetainCount == 0) + { + [cfstreamThread cancel]; // set isCancelled flag + + // wake up the thread + [[self class] performSelector:@selector(ignore:) + onThread:cfstreamThread + withObject:[NSNull null] + waitUntilDone:NO]; + + cfstreamThread = nil; + } + + #pragma clang diagnostic pop + }}); +} + ++ (void)cfstreamThread:(id)unused { @autoreleasepool +{ + [[NSThread currentThread] setName:GCDAsyncSocketThreadName]; + + LogInfo(@"CFStreamThread: Started"); + + // We can't run the run loop unless it has an associated input source or a timer. + // So we'll just create a timer that will never fire - unless the server runs for decades. + [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow] + target:self + selector:@selector(ignore:) + userInfo:nil + repeats:YES]; + + NSThread *currentThread = [NSThread currentThread]; + NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; + + BOOL isCancelled = [currentThread isCancelled]; + + while (!isCancelled && [currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + { + isCancelled = [currentThread isCancelled]; + } + + LogInfo(@"CFStreamThread: Stopped"); +}} + ++ (void)scheduleCFStreams:(GCDAsyncSocket *)asyncSocket +{ + LogTrace(); + NSAssert([NSThread currentThread] == cfstreamThread, @"Invoked on wrong thread"); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (asyncSocket->readStream) + CFReadStreamScheduleWithRunLoop(asyncSocket->readStream, runLoop, kCFRunLoopDefaultMode); + + if (asyncSocket->writeStream) + CFWriteStreamScheduleWithRunLoop(asyncSocket->writeStream, runLoop, kCFRunLoopDefaultMode); +} + ++ (void)unscheduleCFStreams:(GCDAsyncSocket *)asyncSocket +{ + LogTrace(); + NSAssert([NSThread currentThread] == cfstreamThread, @"Invoked on wrong thread"); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (asyncSocket->readStream) + CFReadStreamUnscheduleFromRunLoop(asyncSocket->readStream, runLoop, kCFRunLoopDefaultMode); + + if (asyncSocket->writeStream) + CFWriteStreamUnscheduleFromRunLoop(asyncSocket->writeStream, runLoop, kCFRunLoopDefaultMode); +} + +static void CFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo) +{ + GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo; + + switch(type) + { + case kCFStreamEventHasBytesAvailable: + { + dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFReadStreamCallback - HasBytesAvailable"); + + if (asyncSocket->readStream != stream) + return_from_block; + + if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) + { + // If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie. + // (A callback related to the tcp stream, but not to the SSL layer). + + if (CFReadStreamHasBytesAvailable(asyncSocket->readStream)) + { + asyncSocket->flags |= kSecureSocketHasBytesAvailable; + [asyncSocket cf_finishSSLHandshake]; + } + } + else + { + asyncSocket->flags |= kSecureSocketHasBytesAvailable; + [asyncSocket doReadData]; + } + }}); + + break; + } + default: + { + NSError *error = (__bridge_transfer NSError *)CFReadStreamCopyError(stream); + + if (error == nil && type == kCFStreamEventEndEncountered) + { + error = [asyncSocket connectionClosedError]; + } + + dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFReadStreamCallback - Other"); + + if (asyncSocket->readStream != stream) + return_from_block; + + if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) + { + [asyncSocket cf_abortSSLHandshake:error]; + } + else + { + [asyncSocket closeWithError:error]; + } + }}); + + break; + } + } + +} + +static void CFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) +{ + GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo; + + switch(type) + { + case kCFStreamEventCanAcceptBytes: + { + dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFWriteStreamCallback - CanAcceptBytes"); + + if (asyncSocket->writeStream != stream) + return_from_block; + + if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) + { + // If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie. + // (A callback related to the tcp stream, but not to the SSL layer). + + if (CFWriteStreamCanAcceptBytes(asyncSocket->writeStream)) + { + asyncSocket->flags |= kSocketCanAcceptBytes; + [asyncSocket cf_finishSSLHandshake]; + } + } + else + { + asyncSocket->flags |= kSocketCanAcceptBytes; + [asyncSocket doWriteData]; + } + }}); + + break; + } + default: + { + NSError *error = (__bridge_transfer NSError *)CFWriteStreamCopyError(stream); + + if (error == nil && type == kCFStreamEventEndEncountered) + { + error = [asyncSocket connectionClosedError]; + } + + dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFWriteStreamCallback - Other"); + + if (asyncSocket->writeStream != stream) + return_from_block; + + if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) + { + [asyncSocket cf_abortSSLHandshake:error]; + } + else + { + [asyncSocket closeWithError:error]; + } + }}); + + break; + } + } + +} + +- (BOOL)createReadAndWriteStream +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + if (readStream || writeStream) + { + // Streams already created + return YES; + } + + int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; + + if (socketFD == SOCKET_NULL) + { + // Cannot create streams without a file descriptor + return NO; + } + + if (![self isConnected]) + { + // Cannot create streams until file descriptor is connected + return NO; + } + + LogVerbose(@"Creating read and write stream..."); + + CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socketFD, &readStream, &writeStream); + + // The kCFStreamPropertyShouldCloseNativeSocket property should be false by default (for our case). + // But let's not take any chances. + + if (readStream) + CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + if (writeStream) + CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + + if ((readStream == NULL) || (writeStream == NULL)) + { + LogWarn(@"Unable to create read and write stream..."); + + if (readStream) + { + CFReadStreamClose(readStream); + CFRelease(readStream); + readStream = NULL; + } + if (writeStream) + { + CFWriteStreamClose(writeStream); + CFRelease(writeStream); + writeStream = NULL; + } + + return NO; + } + + return YES; +} + +- (BOOL)registerForStreamCallbacksIncludingReadWrite:(BOOL)includeReadWrite +{ + LogVerbose(@"%@ %@", THIS_METHOD, (includeReadWrite ? @"YES" : @"NO")); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); + + streamContext.version = 0; + streamContext.info = (__bridge void *)(self); + streamContext.retain = nil; + streamContext.release = nil; + streamContext.copyDescription = nil; + + CFOptionFlags readStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; + if (includeReadWrite) + readStreamEvents |= kCFStreamEventHasBytesAvailable; + + if (!CFReadStreamSetClient(readStream, readStreamEvents, &CFReadStreamCallback, &streamContext)) + { + return NO; + } + + CFOptionFlags writeStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; + if (includeReadWrite) + writeStreamEvents |= kCFStreamEventCanAcceptBytes; + + if (!CFWriteStreamSetClient(writeStream, writeStreamEvents, &CFWriteStreamCallback, &streamContext)) + { + return NO; + } + + return YES; +} + +- (BOOL)addStreamsToRunLoop +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); + + if (!(flags & kAddedStreamsToRunLoop)) + { + LogVerbose(@"Adding streams to runloop..."); + + [[self class] startCFStreamThreadIfNeeded]; + dispatch_sync(cfstreamThreadSetupQueue, ^{ + [[self class] performSelector:@selector(scheduleCFStreams:) + onThread:cfstreamThread + withObject:self + waitUntilDone:YES]; + }); + flags |= kAddedStreamsToRunLoop; + } + + return YES; +} + +- (void)removeStreamsFromRunLoop +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); + + if (flags & kAddedStreamsToRunLoop) + { + LogVerbose(@"Removing streams from runloop..."); + + dispatch_sync(cfstreamThreadSetupQueue, ^{ + [[self class] performSelector:@selector(unscheduleCFStreams:) + onThread:cfstreamThread + withObject:self + waitUntilDone:YES]; + }); + [[self class] stopCFStreamThreadIfNeeded]; + + flags &= ~kAddedStreamsToRunLoop; + } +} + +- (BOOL)openStreams +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); + + CFStreamStatus readStatus = CFReadStreamGetStatus(readStream); + CFStreamStatus writeStatus = CFWriteStreamGetStatus(writeStream); + + if ((readStatus == kCFStreamStatusNotOpen) || (writeStatus == kCFStreamStatusNotOpen)) + { + LogVerbose(@"Opening read and write stream..."); + + BOOL r1 = CFReadStreamOpen(readStream); + BOOL r2 = CFWriteStreamOpen(writeStream); + + if (!r1 || !r2) + { + LogError(@"Error in CFStreamOpen"); + return NO; + } + } + + return YES; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Advanced +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * See header file for big discussion of this method. +**/ +- (BOOL)autoDisconnectOnClosedReadStream +{ + // Note: YES means kAllowHalfDuplexConnection is OFF + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return ((config & kAllowHalfDuplexConnection) == 0); + } + else + { + __block BOOL result; + + dispatch_sync(socketQueue, ^{ + result = ((self->config & kAllowHalfDuplexConnection) == 0); + }); + + return result; + } +} + +/** + * See header file for big discussion of this method. +**/ +- (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag +{ + // Note: YES means kAllowHalfDuplexConnection is OFF + + dispatch_block_t block = ^{ + + if (flag) + self->config &= ~kAllowHalfDuplexConnection; + else + self->config |= kAllowHalfDuplexConnection; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + + +/** + * See header file for big discussion of this method. +**/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketNewTargetQueue +{ + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketNewTargetQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); +} + +/** + * See header file for big discussion of this method. +**/ +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketOldTargetQueue +{ + dispatch_queue_set_specific(socketOldTargetQueue, IsOnSocketQueueOrTargetQueueKey, NULL, NULL); +} + +/** + * See header file for big discussion of this method. +**/ +- (void)performBlock:(dispatch_block_t)block +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); +} + +/** + * Questions? Have you read the header file? +**/ +- (int)socketFD +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return SOCKET_NULL; + } + + if (socket4FD != SOCKET_NULL) + return socket4FD; + else + return socket6FD; +} + +/** + * Questions? Have you read the header file? +**/ +- (int)socket4FD +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return SOCKET_NULL; + } + + return socket4FD; +} + +/** + * Questions? Have you read the header file? +**/ +- (int)socket6FD +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return SOCKET_NULL; + } + + return socket6FD; +} + +#if TARGET_OS_IPHONE + +/** + * Questions? Have you read the header file? +**/ +- (CFReadStreamRef)readStream +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return NULL; + } + + if (readStream == NULL) + [self createReadAndWriteStream]; + + return readStream; +} + +/** + * Questions? Have you read the header file? +**/ +- (CFWriteStreamRef)writeStream +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return NULL; + } + + if (writeStream == NULL) + [self createReadAndWriteStream]; + + return writeStream; +} + +- (BOOL)enableBackgroundingOnSocketWithCaveat:(BOOL)caveat +{ + if (![self createReadAndWriteStream]) + { + // Error occurred creating streams (perhaps socket isn't open) + return NO; + } + + BOOL r1, r2; + + LogVerbose(@"Enabling backgrouding on socket"); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + r1 = CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + r2 = CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); +#pragma clang diagnostic pop + + if (!r1 || !r2) + { + return NO; + } + + if (!caveat) + { + if (![self openStreams]) + { + return NO; + } + } + + return YES; +} + +/** + * Questions? Have you read the header file? +**/ +- (BOOL)enableBackgroundingOnSocket +{ + LogTrace(); + + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return NO; + } + + return [self enableBackgroundingOnSocketWithCaveat:NO]; +} + +- (BOOL)enableBackgroundingOnSocketWithCaveat // Deprecated in iOS 4.??? +{ + // This method was created as a workaround for a bug in iOS. + // Apple has since fixed this bug. + // I'm not entirely sure which version of iOS they fixed it in... + + LogTrace(); + + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return NO; + } + + return [self enableBackgroundingOnSocketWithCaveat:YES]; +} + +#endif + +- (SSLContextRef)sslContext +{ + if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); + return NULL; + } + + return sslContext; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Class Utilities +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr +{ + LogTrace(); + + NSMutableArray *addresses = nil; + NSError *error = nil; + + if ([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) + { + // Use LOOPBACK address + struct sockaddr_in nativeAddr4; + nativeAddr4.sin_len = sizeof(struct sockaddr_in); + nativeAddr4.sin_family = AF_INET; + nativeAddr4.sin_port = htons(port); + nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero)); + + struct sockaddr_in6 nativeAddr6; + nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); + nativeAddr6.sin6_family = AF_INET6; + nativeAddr6.sin6_port = htons(port); + nativeAddr6.sin6_flowinfo = 0; + nativeAddr6.sin6_addr = in6addr_loopback; + nativeAddr6.sin6_scope_id = 0; + + // Wrap the native address structures + + NSData *address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; + NSData *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; + + addresses = [NSMutableArray arrayWithCapacity:2]; + [addresses addObject:address4]; + [addresses addObject:address6]; + } + else + { + NSString *portStr = [NSString stringWithFormat:@"%hu", port]; + + struct addrinfo hints, *res, *res0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); + + if (gai_error) + { + error = [self gaiError:gai_error]; + } + else + { + NSUInteger capacity = 0; + for (res = res0; res; res = res->ai_next) + { + if (res->ai_family == AF_INET || res->ai_family == AF_INET6) { + capacity++; + } + } + + addresses = [NSMutableArray arrayWithCapacity:capacity]; + + for (res = res0; res; res = res->ai_next) + { + if (res->ai_family == AF_INET) + { + // Found IPv4 address. + // Wrap the native address structure, and add to results. + + NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; + [addresses addObject:address4]; + } + else if (res->ai_family == AF_INET6) + { + // Fixes connection issues with IPv6 + // https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158 + + // Found IPv6 address. + // Wrap the native address structure, and add to results. + + struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)(void *)res->ai_addr; + in_port_t *portPtr = &sockaddr->sin6_port; + if ((portPtr != NULL) && (*portPtr == 0)) { + *portPtr = htons(port); + } + + NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; + [addresses addObject:address6]; + } + } + freeaddrinfo(res0); + + if ([addresses count] == 0) + { + error = [self gaiError:EAI_FAIL]; + } + } + } + + if (errPtr) *errPtr = error; + return addresses; +} + ++ (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 +{ + char addrBuf[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) + { + addrBuf[0] = '\0'; + } + + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; +} + ++ (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 +{ + char addrBuf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) + { + addrBuf[0] = '\0'; + } + + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; +} + ++ (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 +{ + return ntohs(pSockaddr4->sin_port); +} + ++ (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 +{ + return ntohs(pSockaddr6->sin6_port); +} + ++ (NSURL *)urlFromSockaddrUN:(const struct sockaddr_un *)pSockaddr +{ + NSString *path = [NSString stringWithUTF8String:pSockaddr->sun_path]; + return [NSURL fileURLWithPath:path]; +} + ++ (NSString *)hostFromAddress:(NSData *)address +{ + NSString *host; + + if ([self getHost:&host port:NULL fromAddress:address]) + return host; + else + return nil; +} + ++ (uint16_t)portFromAddress:(NSData *)address +{ + uint16_t port; + + if ([self getHost:NULL port:&port fromAddress:address]) + return port; + else + return 0; +} + ++ (BOOL)isIPv4Address:(NSData *)address +{ + if ([address length] >= sizeof(struct sockaddr)) + { + const struct sockaddr *sockaddrX = [address bytes]; + + if (sockaddrX->sa_family == AF_INET) { + return YES; + } + } + + return NO; +} + ++ (BOOL)isIPv6Address:(NSData *)address +{ + if ([address length] >= sizeof(struct sockaddr)) + { + const struct sockaddr *sockaddrX = [address bytes]; + + if (sockaddrX->sa_family == AF_INET6) { + return YES; + } + } + + return NO; +} + ++ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address +{ + return [self getHost:hostPtr port:portPtr family:NULL fromAddress:address]; +} + ++ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(sa_family_t *)afPtr fromAddress:(NSData *)address +{ + if ([address length] >= sizeof(struct sockaddr)) + { + const struct sockaddr *sockaddrX = [address bytes]; + + if (sockaddrX->sa_family == AF_INET) + { + if ([address length] >= sizeof(struct sockaddr_in)) + { + struct sockaddr_in sockaddr4; + memcpy(&sockaddr4, sockaddrX, sizeof(sockaddr4)); + + if (hostPtr) *hostPtr = [self hostFromSockaddr4:&sockaddr4]; + if (portPtr) *portPtr = [self portFromSockaddr4:&sockaddr4]; + if (afPtr) *afPtr = AF_INET; + + return YES; + } + } + else if (sockaddrX->sa_family == AF_INET6) + { + if ([address length] >= sizeof(struct sockaddr_in6)) + { + struct sockaddr_in6 sockaddr6; + memcpy(&sockaddr6, sockaddrX, sizeof(sockaddr6)); + + if (hostPtr) *hostPtr = [self hostFromSockaddr6:&sockaddr6]; + if (portPtr) *portPtr = [self portFromSockaddr6:&sockaddr6]; + if (afPtr) *afPtr = AF_INET6; + + return YES; + } + } + } + + return NO; +} + ++ (NSData *)CRLFData +{ + return [NSData dataWithBytes:"\x0D\x0A" length:2]; +} + ++ (NSData *)CRData +{ + return [NSData dataWithBytes:"\x0D" length:1]; +} + ++ (NSData *)LFData +{ + return [NSData dataWithBytes:"\x0A" length:1]; +} + ++ (NSData *)ZeroData +{ + return [NSData dataWithBytes:"" length:1]; +} + +@end diff --git a/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h new file mode 100644 index 000000000..c98a44803 --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h @@ -0,0 +1,1024 @@ +// +// GCDAsyncUdpSocket +// +// This class is in the public domain. +// Originally created by Robbie Hanson of Deusty LLC. +// Updated and maintained by Deusty LLC and the Apple development community. +// +// https://github.com/robbiehanson/CocoaAsyncSocket +// + +#import <Foundation/Foundation.h> +#import <dispatch/dispatch.h> +#import <TargetConditionals.h> +#import <Availability.h> + +NS_ASSUME_NONNULL_BEGIN +extern NSString *const GCDAsyncUdpSocketException; +extern NSString *const GCDAsyncUdpSocketErrorDomain; + +extern NSString *const GCDAsyncUdpSocketQueueName; +extern NSString *const GCDAsyncUdpSocketThreadName; + +typedef NS_ERROR_ENUM(GCDAsyncUdpSocketErrorDomain, GCDAsyncUdpSocketError) { + GCDAsyncUdpSocketNoError = 0, // Never used + GCDAsyncUdpSocketBadConfigError, // Invalid configuration + GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed + GCDAsyncUdpSocketSendTimeoutError, // A send operation timed out + GCDAsyncUdpSocketClosedError, // The socket was closed + GCDAsyncUdpSocketOtherError, // Description provided in userInfo +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@class GCDAsyncUdpSocket; + +@protocol GCDAsyncUdpSocketDelegate <NSObject> +@optional + +/** + * By design, UDP is a connectionless protocol, and connecting is not needed. + * However, you may optionally choose to connect to a particular host for reasons + * outlined in the documentation for the various connect methods listed above. + * + * This method is called if one of the connect methods are invoked, and the connection is successful. +**/ +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address; + +/** + * By design, UDP is a connectionless protocol, and connecting is not needed. + * However, you may optionally choose to connect to a particular host for reasons + * outlined in the documentation for the various connect methods listed above. + * + * This method is called if one of the connect methods are invoked, and the connection fails. + * This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved. +**/ +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error; + +/** + * Called when the datagram with the given tag has been sent. +**/ +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag; + +/** + * Called if an error occurs while trying to send a datagram. + * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet. +**/ +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error; + +/** + * Called when the socket has received the requested datagram. +**/ +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data + fromAddress:(NSData *)address + withFilterContext:(nullable id)filterContext; + +/** + * Called when the socket is closed. +**/ +- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError * _Nullable)error; + +@end + +/** + * You may optionally set a receive filter for the socket. + * A filter can provide several useful features: + * + * 1. Many times udp packets need to be parsed. + * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. + * The end result is a parallel socket io, datagram parsing, and packet processing. + * + * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. + * The filter can prevent such packets from arriving at the delegate. + * And because the filter can run in its own independent queue, this doesn't slow down the delegate. + * + * - Since the udp protocol does not guarantee delivery, udp packets may be lost. + * Many protocols built atop udp thus provide various resend/re-request algorithms. + * This sometimes results in duplicate packets arriving. + * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. + * + * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. + * Such packets need to be ignored. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * @param data - The packet that was received. + * @param address - The address the data was received from. + * See utilities section for methods to extract info from address. + * @param context - Out parameter you may optionally set, which will then be passed to the delegate method. + * For example, filter block can parse the data and then, + * pass the parsed data to the delegate. + * + * @returns - YES if the received packet should be passed onto the delegate. + * NO if the received packet should be discarded, and not reported to the delegete. + * + * Example: + * + * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { + * + * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; + * + * *context = response; + * return (response != nil); + * }; + * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; + * +**/ +typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id __nullable * __nonnull context); + +/** + * You may optionally set a send filter for the socket. + * A filter can provide several interesting possibilities: + * + * 1. Optional caching of resolved addresses for domain names. + * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. + * + * 2. Reusable modules of code for bandwidth monitoring. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * @param data - The packet that was received. + * @param address - The address the data was received from. + * See utilities section for methods to extract info from address. + * @param tag - The tag that was passed in the send method. + * + * @returns - YES if the packet should actually be sent over the socket. + * NO if the packet should be silently dropped (not sent over the socket). + * + * Regardless of the return value, the delegate will be informed that the packet was successfully sent. + * +**/ +typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag); + + +@interface GCDAsyncUdpSocket : NSObject + +/** + * GCDAsyncUdpSocket uses the standard delegate paradigm, + * but executes all delegate callbacks on a given delegate dispatch queue. + * This allows for maximum concurrency, while at the same time providing easy thread safety. + * + * You MUST set a delegate AND delegate dispatch queue before attempting to + * use the socket, or you will get an error. + * + * The socket queue is optional. + * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. + * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, + * then please see the discussion for the method markSocketQueueTargetQueue. + * + * The delegate queue and socket queue can optionally be the same. +**/ +- (instancetype)init; +- (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq; +- (instancetype)initWithDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq; +- (instancetype)initWithDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq NS_DESIGNATED_INITIALIZER; + +#pragma mark Configuration + +- (nullable id<GCDAsyncUdpSocketDelegate>)delegate; +- (void)setDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate; +- (void)synchronouslySetDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate; + +- (nullable dispatch_queue_t)delegateQueue; +- (void)setDelegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue; + +- (void)getDelegate:(id<GCDAsyncUdpSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr; +- (void)setDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; + +/** + * By default, both IPv4 and IPv6 are enabled. + * + * This means GCDAsyncUdpSocket automatically supports both protocols, + * and can send to IPv4 or IPv6 addresses, + * as well as receive over IPv4 and IPv6. + * + * For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6. + * If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4. + * If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6. + * If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference. + * If IPv4 is preferred, then IPv4 is used. + * If IPv6 is preferred, then IPv6 is used. + * If neutral, then the first IP version in the resolved array will be used. + * + * Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral. + * On prior systems the default IP preference is IPv4. + **/ +- (BOOL)isIPv4Enabled; +- (void)setIPv4Enabled:(BOOL)flag; + +- (BOOL)isIPv6Enabled; +- (void)setIPv6Enabled:(BOOL)flag; + +- (BOOL)isIPv4Preferred; +- (BOOL)isIPv6Preferred; +- (BOOL)isIPVersionNeutral; + +- (void)setPreferIPv4; +- (void)setPreferIPv6; +- (void)setIPVersionNeutral; + +/** + * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. + * The default maximum size is 65535 bytes. + * + * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. + * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. + * + * Since the OS/GCD notifies us of the size of each received UDP packet, + * the actual allocated buffer size for each packet is exact. + * And in practice the size of UDP packets is generally much smaller than the max. + * Indeed most protocols will send and receive packets of only a few bytes, + * or will set a limit on the size of packets to prevent fragmentation in the IP layer. + * + * If you set the buffer size too small, the sockets API in the OS will silently discard + * any extra data, and you will not be notified of the error. +**/ +- (uint16_t)maxReceiveIPv4BufferSize; +- (void)setMaxReceiveIPv4BufferSize:(uint16_t)max; + +- (uint32_t)maxReceiveIPv6BufferSize; +- (void)setMaxReceiveIPv6BufferSize:(uint32_t)max; + +/** + * Gets/Sets the maximum size of the buffer that will be allocated for send operations. + * The default maximum size is 65535 bytes. + * + * Given that a typical link MTU is 1500 bytes, a large UDP datagram will have to be + * fragmented, and that’s both expensive and risky (if one fragment goes missing, the + * entire datagram is lost). You are much better off sending a large number of smaller + * UDP datagrams, preferably using a path MTU algorithm to avoid fragmentation. + * + * You must set it before the sockt is created otherwise it won't work. + * + **/ +- (uint16_t)maxSendBufferSize; +- (void)setMaxSendBufferSize:(uint16_t)max; + +/** + * User data allows you to associate arbitrary information with the socket. + * This data is not used internally in any way. +**/ +- (nullable id)userData; +- (void)setUserData:(nullable id)arbitraryUserData; + +#pragma mark Diagnostics + +/** + * Returns the local address info for the socket. + * + * The localAddress method returns a sockaddr structure wrapped in a NSData object. + * The localHost method returns the human readable IP address as a string. + * + * Note: Address info may not be available until after the socket has been binded, connected + * or until after data has been sent. +**/ +- (nullable NSData *)localAddress; +- (nullable NSString *)localHost; +- (uint16_t)localPort; + +- (nullable NSData *)localAddress_IPv4; +- (nullable NSString *)localHost_IPv4; +- (uint16_t)localPort_IPv4; + +- (nullable NSData *)localAddress_IPv6; +- (nullable NSString *)localHost_IPv6; +- (uint16_t)localPort_IPv6; + +/** + * Returns the remote address info for the socket. + * + * The connectedAddress method returns a sockaddr structure wrapped in a NSData object. + * The connectedHost method returns the human readable IP address as a string. + * + * Note: Since UDP is connectionless by design, connected address info + * will not be available unless the socket is explicitly connected to a remote host/port. + * If the socket is not connected, these methods will return nil / 0. +**/ +- (nullable NSData *)connectedAddress; +- (nullable NSString *)connectedHost; +- (uint16_t)connectedPort; + +/** + * Returns whether or not this socket has been connected to a single host. + * By design, UDP is a connectionless protocol, and connecting is not needed. + * If connected, the socket will only be able to send/receive data to/from the connected host. +**/ +- (BOOL)isConnected; + +/** + * Returns whether or not this socket has been closed. + * The only way a socket can be closed is if you explicitly call one of the close methods. +**/ +- (BOOL)isClosed; + +/** + * Returns whether or not this socket is IPv4. + * + * By default this will be true, unless: + * - IPv4 is disabled (via setIPv4Enabled:) + * - The socket is explicitly bound to an IPv6 address + * - The socket is connected to an IPv6 address +**/ +- (BOOL)isIPv4; + +/** + * Returns whether or not this socket is IPv6. + * + * By default this will be true, unless: + * - IPv6 is disabled (via setIPv6Enabled:) + * - The socket is explicitly bound to an IPv4 address + * _ The socket is connected to an IPv4 address + * + * This method will also return false on platforms that do not support IPv6. + * Note: The iPhone does not currently support IPv6. +**/ +- (BOOL)isIPv6; + +#pragma mark Binding + +/** + * Binds the UDP socket to the given port. + * Binding should be done for server sockets that receive data prior to sending it. + * Client sockets can skip binding, + * as the OS will automatically assign the socket an available port when it starts sending data. + * + * You may optionally pass a port number of zero to immediately bind the socket, + * yet still allow the OS to automatically assign an available port. + * + * You cannot bind a socket after its been connected. + * You can only bind a socket once. + * You can still connect a socket (if desired) after binding. + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. +**/ +- (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr; + +/** + * Binds the UDP socket to the given port and optional interface. + * Binding should be done for server sockets that receive data prior to sending it. + * Client sockets can skip binding, + * as the OS will automatically assign the socket an available port when it starts sending data. + * + * You may optionally pass a port number of zero to immediately bind the socket, + * yet still allow the OS to automatically assign an available port. + * + * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). + * You may also use the special strings "localhost" or "loopback" to specify that + * the socket only accept packets from the local machine. + * + * You cannot bind a socket after its been connected. + * You can only bind a socket once. + * You can still connect a socket (if desired) after binding. + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. +**/ +- (BOOL)bindToPort:(uint16_t)port interface:(nullable NSString *)interface error:(NSError **)errPtr; + +/** + * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. + * + * If you have an existing struct sockaddr you can convert it to a NSData object like so: + * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; + * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; + * + * Binding should be done for server sockets that receive data prior to sending it. + * Client sockets can skip binding, + * as the OS will automatically assign the socket an available port when it starts sending data. + * + * You cannot bind a socket after its been connected. + * You can only bind a socket once. + * You can still connect a socket (if desired) after binding. + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. +**/ +- (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr; + +#pragma mark Connecting + +/** + * Connects the UDP socket to the given host and port. + * By design, UDP is a connectionless protocol, and connecting is not needed. + * + * Choosing to connect to a specific host/port has the following effect: + * - You will only be able to send data to the connected host/port. + * - You will only be able to receive data from the connected host/port. + * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". + * + * The actual process of connecting a UDP socket does not result in any communication on the socket. + * It simply changes the internal state of the socket. + * + * You cannot bind a socket after it has been connected. + * You can only connect a socket once. + * + * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). + * + * This method is asynchronous as it requires a DNS lookup to resolve the given host name. + * If an obvious error is detected, this method immediately returns NO and sets errPtr. + * If you don't care about the error, you can pass nil for errPtr. + * Otherwise, this method returns YES and begins the asynchronous connection process. + * The result of the asynchronous connection process will be reported via the delegate methods. + **/ +- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; + +/** + * Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. + * + * If you have an existing struct sockaddr you can convert it to a NSData object like so: + * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; + * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; + * + * By design, UDP is a connectionless protocol, and connecting is not needed. + * + * Choosing to connect to a specific address has the following effect: + * - You will only be able to send data to the connected address. + * - You will only be able to receive data from the connected address. + * - You will receive ICMP messages that come from the connected address, such as "connection refused". + * + * Connecting a UDP socket does not result in any communication on the socket. + * It simply changes the internal state of the socket. + * + * You cannot bind a socket after its been connected. + * You can only connect a socket once. + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. + * + * Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup. + * Thus when this method returns, the connection has either failed or fully completed. + * In other words, this method is synchronous, unlike the asynchronous connectToHost::: method. + * However, for compatibility and simplification of delegate code, if this method returns YES + * then the corresponding delegate method (udpSocket:didConnectToHost:port:) is still invoked. +**/ +- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; + +#pragma mark Multicast + +/** + * Join multicast group. + * Group should be an IP address (eg @"225.228.0.1"). + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. +**/ +- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr; + +/** + * Join multicast group. + * Group should be an IP address (eg @"225.228.0.1"). + * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). + * + * On success, returns YES. + * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. +**/ +- (BOOL)joinMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr; + +- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr; +- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr; + +#pragma mark Reuse Port + +/** + * By default, only one socket can be bound to a given IP address + port at a time. + * To enable multiple processes to simultaneously bind to the same address+port, + * you need to enable this functionality in the socket. All processes that wish to + * use the address+port simultaneously must all enable reuse port on the socket + * bound to that port. + **/ +- (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr; + +#pragma mark Broadcast + +/** + * By default, the underlying socket in the OS will not allow you to send broadcast messages. + * In order to send broadcast messages, you need to enable this functionality in the socket. + * + * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is + * delivered to every host on the network. + * The reason this is generally disabled by default (by the OS) is to prevent + * accidental broadcast messages from flooding the network. +**/ +- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr; + +#pragma mark Sending + +/** + * Asynchronously sends the given data, with the given timeout and tag. + * + * This method may only be used with a connected socket. + * Recall that connecting is optional for a UDP socket. + * For connected sockets, data can only be sent to the connected address. + * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. + * + * @param data + * The data to send. + * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. + * + * @param timeout + * The timeout for the send opeartion. + * If the timeout value is negative, the send operation will not use a timeout. + * + * @param tag + * The tag is for your convenience. + * It is not sent or received over the socket in any manner what-so-ever. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. + * You can use it as an array index, state id, type constant, etc. + * + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. +**/ +- (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. + * + * This method cannot be used with a connected socket. + * Recall that connecting is optional for a UDP socket. + * For connected sockets, data can only be sent to the connected address. + * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. + * + * @param data + * The data to send. + * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. + * + * @param host + * The destination to send the udp packet to. + * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). + * You may also use the convenience strings of "loopback" or "localhost". + * + * @param port + * The port of the host to send to. + * + * @param timeout + * The timeout for the send opeartion. + * If the timeout value is negative, the send operation will not use a timeout. + * + * @param tag + * The tag is for your convenience. + * It is not sent or received over the socket in any manner what-so-ever. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. + * You can use it as an array index, state id, type constant, etc. + * + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. +**/ +- (void)sendData:(NSData *)data + toHost:(NSString *)host + port:(uint16_t)port + withTimeout:(NSTimeInterval)timeout + tag:(long)tag; + +/** + * Asynchronously sends the given data, with the given timeout and tag, to the given address. + * + * This method cannot be used with a connected socket. + * Recall that connecting is optional for a UDP socket. + * For connected sockets, data can only be sent to the connected address. + * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. + * + * @param data + * The data to send. + * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. + * + * @param remoteAddr + * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). + * + * @param timeout + * The timeout for the send opeartion. + * If the timeout value is negative, the send operation will not use a timeout. + * + * @param tag + * The tag is for your convenience. + * It is not sent or received over the socket in any manner what-so-ever. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. + * You can use it as an array index, state id, type constant, etc. + * + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. +**/ +- (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; + +/** + * You may optionally set a send filter for the socket. + * A filter can provide several interesting possibilities: + * + * 1. Optional caching of resolved addresses for domain names. + * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. + * + * 2. Reusable modules of code for bandwidth monitoring. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. + * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. + * + * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), + * passing YES for the isAsynchronous parameter. +**/ +- (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue; + +/** + * The receive filter can be run via dispatch_async or dispatch_sync. + * Most typical situations call for asynchronous operation. + * + * However, there are a few situations in which synchronous operation is preferred. + * Such is the case when the filter is extremely minimal and fast. + * This is because dispatch_sync is faster than dispatch_async. + * + * If you choose synchronous operation, be aware of possible deadlock conditions. + * Since the socket queue is executing your block via dispatch_sync, + * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. + * For example, you can't query properties on the socket. +**/ +- (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock + withQueue:(nullable dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous; + +#pragma mark Receiving + +/** + * There are two modes of operation for receiving packets: one-at-a-time & continuous. + * + * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. + * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, + * where your state machine may not always be ready to process incoming packets. + * + * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. + * Receiving packets continuously is better suited to real-time streaming applications. + * + * You may switch back and forth between one-at-a-time mode and continuous mode. + * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. + * + * When a packet is received (and not filtered by the optional receive filter), + * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. + * + * If the socket is able to begin receiving packets, this method returns YES. + * Otherwise it returns NO, and sets the errPtr with appropriate error information. + * + * An example error: + * You created a udp socket to act as a server, and immediately called receive. + * You forgot to first bind the socket to a port number, and received a error with a message like: + * "Must bind socket before you can receive data." +**/ +- (BOOL)receiveOnce:(NSError **)errPtr; + +/** + * There are two modes of operation for receiving packets: one-at-a-time & continuous. + * + * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. + * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, + * where your state machine may not always be ready to process incoming packets. + * + * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. + * Receiving packets continuously is better suited to real-time streaming applications. + * + * You may switch back and forth between one-at-a-time mode and continuous mode. + * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. + * + * For every received packet (not filtered by the optional receive filter), + * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. + * + * If the socket is able to begin receiving packets, this method returns YES. + * Otherwise it returns NO, and sets the errPtr with appropriate error information. + * + * An example error: + * You created a udp socket to act as a server, and immediately called receive. + * You forgot to first bind the socket to a port number, and received a error with a message like: + * "Must bind socket before you can receive data." +**/ +- (BOOL)beginReceiving:(NSError **)errPtr; + +/** + * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving. + * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again. + * + * Important Note: + * GCDAsyncUdpSocket may be running in parallel with your code. + * That is, your delegate is likely running on a separate thread/dispatch_queue. + * When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked. + * Thus, if those delegate methods have already been dispatch_async'd, + * your didReceive delegate method may still be invoked after this method has been called. + * You should be aware of this, and program defensively. +**/ +- (void)pauseReceiving; + +/** + * You may optionally set a receive filter for the socket. + * This receive filter may be set to run in its own queue (independent of delegate queue). + * + * A filter can provide several useful features. + * + * 1. Many times udp packets need to be parsed. + * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. + * The end result is a parallel socket io, datagram parsing, and packet processing. + * + * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. + * The filter can prevent such packets from arriving at the delegate. + * And because the filter can run in its own independent queue, this doesn't slow down the delegate. + * + * - Since the udp protocol does not guarantee delivery, udp packets may be lost. + * Many protocols built atop udp thus provide various resend/re-request algorithms. + * This sometimes results in duplicate packets arriving. + * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. + * + * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. + * Such packets need to be ignored. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * Example: + * + * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { + * + * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; + * + * *context = response; + * return (response != nil); + * }; + * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; + * + * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. + * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. + * + * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), + * passing YES for the isAsynchronous parameter. +**/ +- (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue; + +/** + * The receive filter can be run via dispatch_async or dispatch_sync. + * Most typical situations call for asynchronous operation. + * + * However, there are a few situations in which synchronous operation is preferred. + * Such is the case when the filter is extremely minimal and fast. + * This is because dispatch_sync is faster than dispatch_async. + * + * If you choose synchronous operation, be aware of possible deadlock conditions. + * Since the socket queue is executing your block via dispatch_sync, + * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. + * For example, you can't query properties on the socket. +**/ +- (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock + withQueue:(nullable dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous; + +#pragma mark Closing + +/** + * Immediately closes the underlying socket. + * Any pending send operations are discarded. + * + * The GCDAsyncUdpSocket instance may optionally be used again. + * (it will setup/configure/use another unnderlying BSD socket). +**/ +- (void)close; + +/** + * Closes the underlying socket after all pending send operations have been sent. + * + * The GCDAsyncUdpSocket instance may optionally be used again. + * (it will setup/configure/use another unnderlying BSD socket). +**/ +- (void)closeAfterSending; + +#pragma mark Advanced +/** + * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. + * In most cases, the instance creates this queue itself. + * However, to allow for maximum flexibility, the internal queue may be passed in the init method. + * This allows for some advanced options such as controlling socket priority via target queues. + * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. + * + * For example, imagine there are 2 queues: + * dispatch_queue_t socketQueue; + * dispatch_queue_t socketTargetQueue; + * + * If you do this (pseudo-code): + * socketQueue.targetQueue = socketTargetQueue; + * + * Then all socketQueue operations will actually get run on the given socketTargetQueue. + * This is fine and works great in most situations. + * But if you run code directly from within the socketTargetQueue that accesses the socket, + * you could potentially get deadlock. Imagine the following code: + * + * - (BOOL)socketHasSomething + * { + * __block BOOL result = NO; + * dispatch_block_t block = ^{ + * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; + * } + * if (is_executing_on_queue(socketQueue)) + * block(); + * else + * dispatch_sync(socketQueue, block); + * + * return result; + * } + * + * What happens if you call this method from the socketTargetQueue? The result is deadlock. + * This is because the GCD API offers no mechanism to discover a queue's targetQueue. + * Thus we have no idea if our socketQueue is configured with a targetQueue. + * If we had this information, we could easily avoid deadlock. + * But, since these API's are missing or unfeasible, you'll have to explicitly set it. + * + * IF you pass a socketQueue via the init method, + * AND you've configured the passed socketQueue with a targetQueue, + * THEN you should pass the end queue in the target hierarchy. + * + * For example, consider the following queue hierarchy: + * socketQueue -> ipQueue -> moduleQueue + * + * This example demonstrates priority shaping within some server. + * All incoming client connections from the same IP address are executed on the same target queue. + * And all connections for a particular module are executed on the same target queue. + * Thus, the priority of all networking for the entire module can be changed on the fly. + * Additionally, networking traffic from a single IP cannot monopolize the module. + * + * Here's how you would accomplish something like that: + * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock + * { + * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); + * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; + * + * dispatch_set_target_queue(socketQueue, ipQueue); + * dispatch_set_target_queue(iqQueue, moduleQueue); + * + * return socketQueue; + * } + * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket + * { + * [clientConnections addObject:newSocket]; + * [newSocket markSocketQueueTargetQueue:moduleQueue]; + * } + * + * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. + * This is often NOT the case, as such queues are used solely for execution shaping. + **/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; + +/** + * It's not thread-safe to access certain variables from outside the socket's internal queue. + * + * For example, the socket file descriptor. + * File descriptors are simply integers which reference an index in the per-process file table. + * However, when one requests a new file descriptor (by opening a file or socket), + * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. + * So if we're not careful, the following could be possible: + * + * - Thread A invokes a method which returns the socket's file descriptor. + * - The socket is closed via the socket's internal queue on thread B. + * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. + * - Thread A is now accessing/altering the file instead of the socket. + * + * In addition to this, other variables are not actually objects, + * and thus cannot be retained/released or even autoreleased. + * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. + * + * Although there are internal variables that make it difficult to maintain thread-safety, + * it is important to provide access to these variables + * to ensure this class can be used in a wide array of environments. + * This method helps to accomplish this by invoking the current block on the socket's internal queue. + * The methods below can be invoked from within the block to access + * those generally thread-unsafe internal variables in a thread-safe manner. + * The given block will be invoked synchronously on the socket's internal queue. + * + * If you save references to any protected variables and use them outside the block, you do so at your own peril. +**/ +- (void)performBlock:(dispatch_block_t)block; + +/** + * These methods are only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Provides access to the socket's file descriptor(s). + * If the socket isn't connected, or explicity bound to a particular interface, + * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. +**/ +- (int)socketFD; +- (int)socket4FD; +- (int)socket6FD; + +#if TARGET_OS_IPHONE + +/** + * These methods are only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket. + * + * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.) + * However, if you need one for any reason, + * these methods are a convenient way to get access to a safe instance of one. +**/ +- (nullable CFReadStreamRef)readStream; +- (nullable CFWriteStreamRef)writeStream; + +/** + * This method is only available from within the context of a performBlock: invocation. + * See the documentation for the performBlock: method above. + * + * Configures the socket to allow it to operate when the iOS application has been backgrounded. + * In other words, this method creates a read & write stream, and invokes: + * + * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + * + * Returns YES if successful, NO otherwise. + * + * Example usage: + * + * [asyncUdpSocket performBlock:^{ + * [asyncUdpSocket enableBackgroundingOnSocket]; + * }]; + * + * + * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now). +**/ +//- (BOOL)enableBackgroundingOnSockets; + +#endif + +#pragma mark Utilities + +/** + * Extracting host/port/family information from raw address data. +**/ + ++ (nullable NSString *)hostFromAddress:(NSData *)address; ++ (uint16_t)portFromAddress:(NSData *)address; ++ (int)familyFromAddress:(NSData *)address; + ++ (BOOL)isIPv4Address:(NSData *)address; ++ (BOOL)isIPv6Address:(NSData *)address; + ++ (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr fromAddress:(NSData *)address; ++ (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr family:(int * __nullable)afPtr fromAddress:(NSData *)address; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m new file mode 100755 index 000000000..e29799136 --- /dev/null +++ b/ios/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m @@ -0,0 +1,5517 @@ +// +// GCDAsyncUdpSocket +// +// This class is in the public domain. +// Originally created by Robbie Hanson of Deusty LLC. +// Updated and maintained by Deusty LLC and the Apple development community. +// +// https://github.com/robbiehanson/CocoaAsyncSocket +// + +#import "GCDAsyncUdpSocket.h" + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +// For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC +#endif + +#if TARGET_OS_IPHONE + #import <CFNetwork/CFNetwork.h> + #import <UIKit/UIKit.h> +#endif + +#import <arpa/inet.h> +#import <fcntl.h> +#import <ifaddrs.h> +#import <netdb.h> +#import <net/if.h> +#import <sys/socket.h> +#import <sys/types.h> + + +#if 0 + +// Logging Enabled - See log level below + +// Logging uses the CocoaLumberjack framework (which is also GCD based). +// https://github.com/robbiehanson/CocoaLumberjack +// +// It allows us to do a lot of logging without significantly slowing down the code. +#import "DDLog.h" + +#define LogAsync NO +#define LogContext 65535 + +#define LogObjc(flg, frmt, ...) LOG_OBJC_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) +#define LogC(flg, frmt, ...) LOG_C_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) + +#define LogError(frmt, ...) LogObjc(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogWarn(frmt, ...) LogObjc(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogInfo(frmt, ...) LogObjc(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogVerbose(frmt, ...) LogObjc(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) + +#define LogCError(frmt, ...) LogC(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCWarn(frmt, ...) LogC(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCInfo(frmt, ...) LogC(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) +#define LogCVerbose(frmt, ...) LogC(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) + +#define LogTrace() LogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD) +#define LogCTrace() LogC(LOG_FLAG_VERBOSE, @"%@: %s", THIS_FILE, __FUNCTION__) + +// Log levels : off, error, warn, info, verbose +static const int logLevel = LOG_LEVEL_VERBOSE; + +#else + +// Logging Disabled + +#define LogError(frmt, ...) {} +#define LogWarn(frmt, ...) {} +#define LogInfo(frmt, ...) {} +#define LogVerbose(frmt, ...) {} + +#define LogCError(frmt, ...) {} +#define LogCWarn(frmt, ...) {} +#define LogCInfo(frmt, ...) {} +#define LogCVerbose(frmt, ...) {} + +#define LogTrace() {} +#define LogCTrace(frmt, ...) {} + +#endif + +/** + * Seeing a return statements within an inner block + * can sometimes be mistaken for a return point of the enclosing method. + * This makes inline blocks a bit easier to read. +**/ +#define return_from_block return + +/** + * A socket file descriptor is really just an integer. + * It represents the index of the socket within the kernel. + * This makes invalid file descriptor comparisons easier to read. +**/ +#define SOCKET_NULL -1 + +/** + * Just to type less code. +**/ +#define AutoreleasedBlock(block) ^{ @autoreleasepool { block(); }} + + +@class GCDAsyncUdpSendPacket; + +NSString *const GCDAsyncUdpSocketException = @"GCDAsyncUdpSocketException"; +NSString *const GCDAsyncUdpSocketErrorDomain = @"GCDAsyncUdpSocketErrorDomain"; + +NSString *const GCDAsyncUdpSocketQueueName = @"GCDAsyncUdpSocket"; +NSString *const GCDAsyncUdpSocketThreadName = @"GCDAsyncUdpSocket-CFStream"; + +enum GCDAsyncUdpSocketFlags +{ + kDidCreateSockets = 1 << 0, // If set, the sockets have been created. + kDidBind = 1 << 1, // If set, bind has been called. + kConnecting = 1 << 2, // If set, a connection attempt is in progress. + kDidConnect = 1 << 3, // If set, socket is connected. + kReceiveOnce = 1 << 4, // If set, one-at-a-time receive is enabled + kReceiveContinuous = 1 << 5, // If set, continuous receive is enabled + kIPv4Deactivated = 1 << 6, // If set, socket4 was closed due to bind or connect on IPv6. + kIPv6Deactivated = 1 << 7, // If set, socket6 was closed due to bind or connect on IPv4. + kSend4SourceSuspended = 1 << 8, // If set, send4Source is suspended. + kSend6SourceSuspended = 1 << 9, // If set, send6Source is suspended. + kReceive4SourceSuspended = 1 << 10, // If set, receive4Source is suspended. + kReceive6SourceSuspended = 1 << 11, // If set, receive6Source is suspended. + kSock4CanAcceptBytes = 1 << 12, // If set, we know socket4 can accept bytes. If unset, it's unknown. + kSock6CanAcceptBytes = 1 << 13, // If set, we know socket6 can accept bytes. If unset, it's unknown. + kForbidSendReceive = 1 << 14, // If set, no new send or receive operations are allowed to be queued. + kCloseAfterSends = 1 << 15, // If set, close as soon as no more sends are queued. + kFlipFlop = 1 << 16, // Used to alternate between IPv4 and IPv6 sockets. +#if TARGET_OS_IPHONE + kAddedStreamListener = 1 << 17, // If set, CFStreams have been added to listener thread +#endif +}; + +enum GCDAsyncUdpSocketConfig +{ + kIPv4Disabled = 1 << 0, // If set, IPv4 is disabled + kIPv6Disabled = 1 << 1, // If set, IPv6 is disabled + kPreferIPv4 = 1 << 2, // If set, IPv4 is preferred over IPv6 + kPreferIPv6 = 1 << 3, // If set, IPv6 is preferred over IPv4 +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@interface GCDAsyncUdpSocket () +{ +#if __has_feature(objc_arc_weak) + __weak id delegate; +#else + __unsafe_unretained id delegate; +#endif + dispatch_queue_t delegateQueue; + + GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock; + dispatch_queue_t receiveFilterQueue; + BOOL receiveFilterAsync; + + GCDAsyncUdpSocketSendFilterBlock sendFilterBlock; + dispatch_queue_t sendFilterQueue; + BOOL sendFilterAsync; + + uint32_t flags; + uint16_t config; + + uint16_t max4ReceiveSize; + uint32_t max6ReceiveSize; + + uint16_t maxSendSize; + + int socket4FD; + int socket6FD; + + dispatch_queue_t socketQueue; + + dispatch_source_t send4Source; + dispatch_source_t send6Source; + dispatch_source_t receive4Source; + dispatch_source_t receive6Source; + dispatch_source_t sendTimer; + + GCDAsyncUdpSendPacket *currentSend; + NSMutableArray *sendQueue; + + unsigned long socket4FDBytesAvailable; + unsigned long socket6FDBytesAvailable; + + uint32_t pendingFilterOperations; + + NSData *cachedLocalAddress4; + NSString *cachedLocalHost4; + uint16_t cachedLocalPort4; + + NSData *cachedLocalAddress6; + NSString *cachedLocalHost6; + uint16_t cachedLocalPort6; + + NSData *cachedConnectedAddress; + NSString *cachedConnectedHost; + uint16_t cachedConnectedPort; + int cachedConnectedFamily; + + void *IsOnSocketQueueOrTargetQueueKey; + +#if TARGET_OS_IPHONE + CFStreamClientContext streamContext; + CFReadStreamRef readStream4; + CFReadStreamRef readStream6; + CFWriteStreamRef writeStream4; + CFWriteStreamRef writeStream6; +#endif + + id userData; +} + +- (void)resumeSend4Source; +- (void)resumeSend6Source; +- (void)resumeReceive4Source; +- (void)resumeReceive6Source; +- (void)closeSockets; + +- (void)maybeConnect; +- (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr; +- (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr; + +- (void)maybeDequeueSend; +- (void)doPreSend; +- (void)doSend; +- (void)endCurrentSend; +- (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout; + +- (void)doReceive; +- (void)doReceiveEOF; + +- (void)closeWithError:(NSError *)error; + +- (BOOL)performMulticastRequest:(int)requestType forGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; + +#if TARGET_OS_IPHONE +- (BOOL)createReadAndWriteStreams:(NSError **)errPtr; +- (BOOL)registerForStreamCallbacks:(NSError **)errPtr; +- (BOOL)addStreamsToRunLoop:(NSError **)errPtr; +- (BOOL)openStreams:(NSError **)errPtr; +- (void)removeStreamsFromRunLoop; +- (void)closeReadAndWriteStreams; +#endif + ++ (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4; ++ (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6; ++ (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4; ++ (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6; + +#if TARGET_OS_IPHONE +// Forward declaration ++ (void)listenerThread:(id)unused; +#endif + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The GCDAsyncUdpSendPacket encompasses the instructions for a single send/write. +**/ +@interface GCDAsyncUdpSendPacket : NSObject { +@public + NSData *buffer; + NSTimeInterval timeout; + long tag; + + BOOL resolveInProgress; + BOOL filterInProgress; + + NSArray *resolvedAddresses; + NSError *resolveError; + + NSData *address; + int addressFamily; +} + +- (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i NS_DESIGNATED_INITIALIZER; + +@end + +@implementation GCDAsyncUdpSendPacket + +// Cover the superclass' designated initializer +- (instancetype)init NS_UNAVAILABLE +{ + NSAssert(0, @"Use the designated initializer"); + return nil; +} + +- (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i +{ + if ((self = [super init])) + { + buffer = d; + timeout = t; + tag = i; + + resolveInProgress = NO; + } + return self; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@interface GCDAsyncUdpSpecialPacket : NSObject { +@public +// uint8_t type; + + BOOL resolveInProgress; + + NSArray *addresses; + NSError *error; +} + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +@end + +@implementation GCDAsyncUdpSpecialPacket + +- (instancetype)init +{ + self = [super init]; + return self; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation GCDAsyncUdpSocket + +- (instancetype)init +{ + LogTrace(); + + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:NULL]; +} + +- (instancetype)initWithSocketQueue:(dispatch_queue_t)sq +{ + LogTrace(); + + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq]; +} + +- (instancetype)initWithDelegate:(id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq +{ + LogTrace(); + + return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; +} + +- (instancetype)initWithDelegate:(id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq +{ + LogTrace(); + + if ((self = [super init])) + { + delegate = aDelegate; + + if (dq) + { + delegateQueue = dq; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(delegateQueue); + #endif + } + + max4ReceiveSize = 65535; + max6ReceiveSize = 65535; + + maxSendSize = 65535; + + socket4FD = SOCKET_NULL; + socket6FD = SOCKET_NULL; + + if (sq) + { + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + @"The given socketQueue parameter must not be a concurrent queue."); + + socketQueue = sq; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(socketQueue); + #endif + } + else + { + socketQueue = dispatch_queue_create([GCDAsyncUdpSocketQueueName UTF8String], NULL); + } + + // The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter. + // From the documentation: + // + // > Keys are only compared as pointers and are never dereferenced. + // > Thus, you can use a pointer to a static variable for a specific subsystem or + // > any other value that allows you to identify the value uniquely. + // + // We're just going to use the memory address of an ivar. + // Specifically an ivar that is explicitly named for our purpose to make the code more readable. + // + // However, it feels tedious (and less readable) to include the "&" all the time: + // dispatch_get_specific(&IsOnSocketQueueOrTargetQueueKey) + // + // So we're going to make it so it doesn't matter if we use the '&' or not, + // by assigning the value of the ivar to the address of the ivar. + // Thus: IsOnSocketQueueOrTargetQueueKey == &IsOnSocketQueueOrTargetQueueKey; + + IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey; + + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); + + currentSend = nil; + sendQueue = [[NSMutableArray alloc] initWithCapacity:5]; + + #if TARGET_OS_IPHONE + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + #endif + } + return self; +} + +- (void)dealloc +{ + LogInfo(@"%@ - %@ (start)", THIS_METHOD, self); + +#if TARGET_OS_IPHONE + [[NSNotificationCenter defaultCenter] removeObserver:self]; +#endif + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + [self closeWithError:nil]; + } + else + { + dispatch_sync(socketQueue, ^{ + [self closeWithError:nil]; + }); + } + + delegate = nil; + #if !OS_OBJECT_USE_OBJC + if (delegateQueue) dispatch_release(delegateQueue); + #endif + delegateQueue = NULL; + + #if !OS_OBJECT_USE_OBJC + if (socketQueue) dispatch_release(socketQueue); + #endif + socketQueue = NULL; + + LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Configuration +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (id<GCDAsyncUdpSocketDelegate>)delegate +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return delegate; + } + else + { + __block id result = nil; + + dispatch_sync(socketQueue, ^{ + result = self->delegate; + }); + + return result; + } +} + +- (void)setDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + self->delegate = newDelegate; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate +{ + [self setDelegate:newDelegate synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate +{ + [self setDelegate:newDelegate synchronously:YES]; +} + +- (dispatch_queue_t)delegateQueue +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return delegateQueue; + } + else + { + __block dispatch_queue_t result = NULL; + + dispatch_sync(socketQueue, ^{ + result = self->delegateQueue; + }); + + return result; + } +} + +- (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + + #if !OS_OBJECT_USE_OBJC + if (self->delegateQueue) dispatch_release(self->delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif + + self->delegateQueue = newDelegateQueue; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegateQueue:newDelegateQueue synchronously:YES]; +} + +- (void)getDelegate:(id<GCDAsyncUdpSocketDelegate> *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + if (delegatePtr) *delegatePtr = delegate; + if (delegateQueuePtr) *delegateQueuePtr = delegateQueue; + } + else + { + __block id dPtr = NULL; + __block dispatch_queue_t dqPtr = NULL; + + dispatch_sync(socketQueue, ^{ + dPtr = self->delegate; + dqPtr = self->delegateQueue; + }); + + if (delegatePtr) *delegatePtr = dPtr; + if (delegateQueuePtr) *delegateQueuePtr = dqPtr; + } +} + +- (void)setDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously +{ + dispatch_block_t block = ^{ + + self->delegate = newDelegate; + + #if !OS_OBJECT_USE_OBJC + if (self->delegateQueue) dispatch_release(self->delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif + + self->delegateQueue = newDelegateQueue; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { + block(); + } + else { + if (synchronously) + dispatch_sync(socketQueue, block); + else + dispatch_async(socketQueue, block); + } +} + +- (void)setDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id<GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES]; +} + +- (BOOL)isIPv4Enabled +{ + // Note: YES means kIPv4Disabled is OFF + + __block BOOL result = NO; + + dispatch_block_t block = ^{ + + result = ((self->config & kIPv4Disabled) == 0); + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setIPv4Enabled:(BOOL)flag +{ + // Note: YES means kIPv4Disabled is OFF + + dispatch_block_t block = ^{ + + LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO")); + + if (flag) + self->config &= ~kIPv4Disabled; + else + self->config |= kIPv4Disabled; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (BOOL)isIPv6Enabled +{ + // Note: YES means kIPv6Disabled is OFF + + __block BOOL result = NO; + + dispatch_block_t block = ^{ + + result = ((self->config & kIPv6Disabled) == 0); + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setIPv6Enabled:(BOOL)flag +{ + // Note: YES means kIPv6Disabled is OFF + + dispatch_block_t block = ^{ + + LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO")); + + if (flag) + self->config &= ~kIPv6Disabled; + else + self->config |= kIPv6Disabled; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (BOOL)isIPv4Preferred +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->config & kPreferIPv4) ? YES : NO; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isIPv6Preferred +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->config & kPreferIPv6) ? YES : NO; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isIPVersionNeutral +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->config & (kPreferIPv4 | kPreferIPv6)) == 0; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setPreferIPv4 +{ + dispatch_block_t block = ^{ + + LogTrace(); + + self->config |= kPreferIPv4; + self->config &= ~kPreferIPv6; + + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)setPreferIPv6 +{ + dispatch_block_t block = ^{ + + LogTrace(); + + self->config &= ~kPreferIPv4; + self->config |= kPreferIPv6; + + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)setIPVersionNeutral +{ + dispatch_block_t block = ^{ + + LogTrace(); + + self->config &= ~kPreferIPv4; + self->config &= ~kPreferIPv6; + + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (uint16_t)maxReceiveIPv4BufferSize +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + result = self->max4ReceiveSize; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setMaxReceiveIPv4BufferSize:(uint16_t)max +{ + dispatch_block_t block = ^{ + + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); + + self->max4ReceiveSize = max; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (uint32_t)maxReceiveIPv6BufferSize +{ + __block uint32_t result = 0; + + dispatch_block_t block = ^{ + + result = self->max6ReceiveSize; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setMaxReceiveIPv6BufferSize:(uint32_t)max +{ + dispatch_block_t block = ^{ + + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); + + self->max6ReceiveSize = max; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)setMaxSendBufferSize:(uint16_t)max +{ + dispatch_block_t block = ^{ + + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); + + self->maxSendSize = max; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (uint16_t)maxSendBufferSize +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + result = self->maxSendSize; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (id)userData +{ + __block id result = nil; + + dispatch_block_t block = ^{ + + result = self->userData; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (void)setUserData:(id)arbitraryUserData +{ + dispatch_block_t block = ^{ + + if (self->userData != arbitraryUserData) + { + self->userData = arbitraryUserData; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Delegate Helpers +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)notifyDidConnectToAddress:(NSData *)anAddress +{ + LogTrace(); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)]) + { + NSData *address = [anAddress copy]; // In case param is NSMutableData + + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocket:self didConnectToAddress:address]; + }}); + } +} + +- (void)notifyDidNotConnect:(NSError *)error +{ + LogTrace(); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotConnect:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocket:self didNotConnect:error]; + }}); + } +} + +- (void)notifyDidSendDataWithTag:(long)tag +{ + LogTrace(); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didSendDataWithTag:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocket:self didSendDataWithTag:tag]; + }}); + } +} + +- (void)notifyDidNotSendDataWithTag:(long)tag dueToError:(NSError *)error +{ + LogTrace(); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotSendDataWithTag:dueToError:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error]; + }}); + } +} + +- (void)notifyDidReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)context +{ + LogTrace(); + + SEL selector = @selector(udpSocket:didReceiveData:fromAddress:withFilterContext:); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:selector]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context]; + }}); + } +} + +- (void)notifyDidCloseWithError:(NSError *)error +{ + LogTrace(); + + __strong id<GCDAsyncUdpSocketDelegate> theDelegate = delegate; + if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocketDidClose:withError:)]) + { + dispatch_async(delegateQueue, ^{ @autoreleasepool { + + [theDelegate udpSocketDidClose:self withError:error]; + }}); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Errors +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSError *)badConfigError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain + code:GCDAsyncUdpSocketBadConfigError + userInfo:userInfo]; +} + +- (NSError *)badParamError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain + code:GCDAsyncUdpSocketBadParamError + userInfo:userInfo]; +} + +- (NSError *)gaiError:(int)gai_error +{ + NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding]; + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:userInfo]; +} + +- (NSError *)errnoErrorWithReason:(NSString *)reason +{ + NSString *errMsg = [NSString stringWithUTF8String:strerror(errno)]; + NSDictionary *userInfo; + + if (reason) + userInfo = @{NSLocalizedDescriptionKey : errMsg, + NSLocalizedFailureReasonErrorKey : reason}; + else + userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; +} + +- (NSError *)errnoError +{ + return [self errnoErrorWithReason:nil]; +} + +/** + * Returns a standard send timeout error. +**/ +- (NSError *)sendTimeoutError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketSendTimeoutError", + @"GCDAsyncUdpSocket", [NSBundle mainBundle], + @"Send operation timed out", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain + code:GCDAsyncUdpSocketSendTimeoutError + userInfo:userInfo]; +} + +- (NSError *)socketClosedError +{ + NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketClosedError", + @"GCDAsyncUdpSocket", [NSBundle mainBundle], + @"Socket closed", nil); + + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketClosedError userInfo:userInfo]; +} + +- (NSError *)otherError:(NSString *)errMsg +{ + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; + + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain + code:GCDAsyncUdpSocketOtherError + userInfo:userInfo]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Utilities +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)preOp:(NSError **)errPtr +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (delegate == nil) // Must have delegate set + { + if (errPtr) + { + NSString *msg = @"Attempting to use socket without a delegate. Set a delegate first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if (delegateQueue == NULL) // Must have delegate queue set + { + if (errPtr) + { + NSString *msg = @"Attempting to use socket without a delegate queue. Set a delegate queue first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + return YES; +} + +/** + * This method executes on a global concurrent queue. + * When complete, it executes the given completion block on the socketQueue. +**/ +- (void)asyncResolveHost:(NSString *)aHost + port:(uint16_t)port + withCompletionBlock:(void (^)(NSArray *addresses, NSError *error))completionBlock +{ + LogTrace(); + + // Check parameter(s) + + if (aHost == nil) + { + NSString *msg = @"The host param is nil. Should be domain name or IP address string."; + NSError *error = [self badParamError:msg]; + + // We should still use dispatch_async since this method is expected to be asynchronous + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + completionBlock(nil, error); + }}); + + return; + } + + // It's possible that the given aHost parameter is actually a NSMutableString. + // So we want to copy it now, within this block that will be executed synchronously. + // This way the asynchronous lookup block below doesn't have to worry about it changing. + + NSString *host = [aHost copy]; + + + dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool { + + NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:2]; + NSError *error = nil; + + if ([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) + { + // Use LOOPBACK address + struct sockaddr_in sockaddr4; + memset(&sockaddr4, 0, sizeof(sockaddr4)); + + sockaddr4.sin_len = sizeof(struct sockaddr_in); + sockaddr4.sin_family = AF_INET; + sockaddr4.sin_port = htons(port); + sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + struct sockaddr_in6 sockaddr6; + memset(&sockaddr6, 0, sizeof(sockaddr6)); + + sockaddr6.sin6_len = sizeof(struct sockaddr_in6); + sockaddr6.sin6_family = AF_INET6; + sockaddr6.sin6_port = htons(port); + sockaddr6.sin6_addr = in6addr_loopback; + + // Wrap the native address structures and add to list + [addresses addObject:[NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]]; + [addresses addObject:[NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]]; + } + else + { + NSString *portStr = [NSString stringWithFormat:@"%hu", port]; + + struct addrinfo hints, *res, *res0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); + + if (gai_error) + { + error = [self gaiError:gai_error]; + } + else + { + for(res = res0; res; res = res->ai_next) + { + if (res->ai_family == AF_INET) + { + // Found IPv4 address + // Wrap the native address structure and add to list + + [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]]; + } + else if (res->ai_family == AF_INET6) + { + + // Fixes connection issues with IPv6, it is the same solution for udp socket. + // https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158 + struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)(void *)res->ai_addr; + in_port_t *portPtr = &sockaddr->sin6_port; + if ((portPtr != NULL) && (*portPtr == 0)) { + *portPtr = htons(port); + } + + // Found IPv6 address + // Wrap the native address structure and add to list + [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]]; + } + } + freeaddrinfo(res0); + + if ([addresses count] == 0) + { + error = [self gaiError:EAI_FAIL]; + } + } + } + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + completionBlock(addresses, error); + }}); + + }}); +} + +/** + * This method picks an address from the given list of addresses. + * The address picked depends upon which protocols are disabled, deactived, & preferred. + * + * Returns the address family (AF_INET or AF_INET6) of the picked address, + * or AF_UNSPEC and the corresponding error is there's a problem. +**/ +- (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses:(NSArray *)addresses +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert([addresses count] > 0, @"Expected at least one address"); + + int resultAF = AF_UNSPEC; + NSData *resultAddress = nil; + NSError *resultError = nil; + + // Check for problems + + BOOL resolvedIPv4Address = NO; + BOOL resolvedIPv6Address = NO; + + for (NSData *address in addresses) + { + switch ([[self class] familyFromAddress:address]) + { + case AF_INET : resolvedIPv4Address = YES; break; + case AF_INET6 : resolvedIPv6Address = YES; break; + + default : NSAssert(NO, @"Addresses array contains invalid address"); + } + } + + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && !resolvedIPv6Address) + { + NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address(es)."; + resultError = [self otherError:msg]; + + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; + } + + if (isIPv6Disabled && !resolvedIPv4Address) + { + NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address(es)."; + resultError = [self otherError:msg]; + + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; + } + + BOOL isIPv4Deactivated = (flags & kIPv4Deactivated) ? YES : NO; + BOOL isIPv6Deactivated = (flags & kIPv6Deactivated) ? YES : NO; + + if (isIPv4Deactivated && !resolvedIPv6Address) + { + NSString *msg = @"IPv4 has been deactivated due to bind/connect, and DNS lookup found no IPv6 address(es)."; + resultError = [self otherError:msg]; + + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; + } + + if (isIPv6Deactivated && !resolvedIPv4Address) + { + NSString *msg = @"IPv6 has been deactivated due to bind/connect, and DNS lookup found no IPv4 address(es)."; + resultError = [self otherError:msg]; + + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; + } + + // Extract first IPv4 and IPv6 address in list + + BOOL ipv4WasFirstInList = YES; + NSData *address4 = nil; + NSData *address6 = nil; + + for (NSData *address in addresses) + { + int af = [[self class] familyFromAddress:address]; + + if (af == AF_INET) + { + if (address4 == nil) + { + address4 = address; + + if (address6) + break; + else + ipv4WasFirstInList = YES; + } + } + else // af == AF_INET6 + { + if (address6 == nil) + { + address6 = address; + + if (address4) + break; + else + ipv4WasFirstInList = NO; + } + } + } + + // Determine socket type + + BOOL preferIPv4 = (config & kPreferIPv4) ? YES : NO; + BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO; + + BOOL useIPv4 = ((preferIPv4 && address4) || (address6 == nil)); + BOOL useIPv6 = ((preferIPv6 && address6) || (address4 == nil)); + + NSAssert(!(preferIPv4 && preferIPv6), @"Invalid config state"); + NSAssert(!(useIPv4 && useIPv6), @"Invalid logic"); + + if (useIPv4 || (!useIPv6 && ipv4WasFirstInList)) + { + resultAF = AF_INET; + resultAddress = address4; + } + else + { + resultAF = AF_INET6; + resultAddress = address6; + } + + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; +} + +/** + * Finds the address(es) of an interface description. + * An inteface description may be an interface name (en0, en1, lo0) or corresponding IP (192.168.4.34). +**/ +- (void)convertIntefaceDescription:(NSString *)interfaceDescription + port:(uint16_t)port + intoAddress4:(NSData **)interfaceAddr4Ptr + address6:(NSData **)interfaceAddr6Ptr +{ + NSData *addr4 = nil; + NSData *addr6 = nil; + + if (interfaceDescription == nil) + { + // ANY address + + struct sockaddr_in sockaddr4; + memset(&sockaddr4, 0, sizeof(sockaddr4)); + + sockaddr4.sin_len = sizeof(sockaddr4); + sockaddr4.sin_family = AF_INET; + sockaddr4.sin_port = htons(port); + sockaddr4.sin_addr.s_addr = htonl(INADDR_ANY); + + struct sockaddr_in6 sockaddr6; + memset(&sockaddr6, 0, sizeof(sockaddr6)); + + sockaddr6.sin6_len = sizeof(sockaddr6); + sockaddr6.sin6_family = AF_INET6; + sockaddr6.sin6_port = htons(port); + sockaddr6.sin6_addr = in6addr_any; + + addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; + addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; + } + else if ([interfaceDescription isEqualToString:@"localhost"] || + [interfaceDescription isEqualToString:@"loopback"]) + { + // LOOPBACK address + + struct sockaddr_in sockaddr4; + memset(&sockaddr4, 0, sizeof(sockaddr4)); + + sockaddr4.sin_len = sizeof(struct sockaddr_in); + sockaddr4.sin_family = AF_INET; + sockaddr4.sin_port = htons(port); + sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + struct sockaddr_in6 sockaddr6; + memset(&sockaddr6, 0, sizeof(sockaddr6)); + + sockaddr6.sin6_len = sizeof(struct sockaddr_in6); + sockaddr6.sin6_family = AF_INET6; + sockaddr6.sin6_port = htons(port); + sockaddr6.sin6_addr = in6addr_loopback; + + addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; + addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; + } + else + { + const char *iface = [interfaceDescription UTF8String]; + + struct ifaddrs *addrs; + const struct ifaddrs *cursor; + + if ((getifaddrs(&addrs) == 0)) + { + cursor = addrs; + while (cursor != NULL) + { + if ((addr4 == nil) && (cursor->ifa_addr->sa_family == AF_INET)) + { + // IPv4 + + struct sockaddr_in *addr = (struct sockaddr_in *)(void *)cursor->ifa_addr; + + if (strcmp(cursor->ifa_name, iface) == 0) + { + // Name match + + struct sockaddr_in nativeAddr4 = *addr; + nativeAddr4.sin_port = htons(port); + + addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; + } + else + { + char ip[INET_ADDRSTRLEN]; + + const char *conversion; + conversion = inet_ntop(AF_INET, &addr->sin_addr, ip, sizeof(ip)); + + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) + { + // IP match + + struct sockaddr_in nativeAddr4 = *addr; + nativeAddr4.sin_port = htons(port); + + addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; + } + } + } + else if ((addr6 == nil) && (cursor->ifa_addr->sa_family == AF_INET6)) + { + // IPv6 + + const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(const void *)cursor->ifa_addr; + + if (strcmp(cursor->ifa_name, iface) == 0) + { + // Name match + + struct sockaddr_in6 nativeAddr6 = *addr; + nativeAddr6.sin6_port = htons(port); + + addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; + } + else + { + char ip[INET6_ADDRSTRLEN]; + + const char *conversion; + conversion = inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip)); + + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) + { + // IP match + + struct sockaddr_in6 nativeAddr6 = *addr; + nativeAddr6.sin6_port = htons(port); + + addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; + } + } + } + + cursor = cursor->ifa_next; + } + + freeifaddrs(addrs); + } + } + + if (interfaceAddr4Ptr) *interfaceAddr4Ptr = addr4; + if (interfaceAddr6Ptr) *interfaceAddr6Ptr = addr6; +} + +/** + * Converts a numeric hostname into its corresponding address. + * The hostname is expected to be an IPv4 or IPv6 address represented as a human-readable string. (e.g. 192.168.4.34) +**/ +- (void)convertNumericHost:(NSString *)numericHost + port:(uint16_t)port + intoAddress4:(NSData **)addr4Ptr + address6:(NSData **)addr6Ptr +{ + NSData *addr4 = nil; + NSData *addr6 = nil; + + if (numericHost) + { + NSString *portStr = [NSString stringWithFormat:@"%hu", port]; + + struct addrinfo hints, *res, *res0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICHOST; // No name resolution should be attempted + + if (getaddrinfo([numericHost UTF8String], [portStr UTF8String], &hints, &res0) == 0) + { + for (res = res0; res; res = res->ai_next) + { + if ((addr4 == nil) && (res->ai_family == AF_INET)) + { + // Found IPv4 address + // Wrap the native address structure + addr4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; + } + else if ((addr6 == nil) && (res->ai_family == AF_INET6)) + { + // Found IPv6 address + // Wrap the native address structure + addr6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; + } + } + freeaddrinfo(res0); + } + } + + if (addr4Ptr) *addr4Ptr = addr4; + if (addr6Ptr) *addr6Ptr = addr6; +} + +- (BOOL)isConnectedToAddress4:(NSData *)someAddr4 +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(flags & kDidConnect, @"Not connected"); + NSAssert(cachedConnectedAddress, @"Expected cached connected address"); + + if (cachedConnectedFamily != AF_INET) + { + return NO; + } + + const struct sockaddr_in *sSockaddr4 = (const struct sockaddr_in *)[someAddr4 bytes]; + const struct sockaddr_in *cSockaddr4 = (const struct sockaddr_in *)[cachedConnectedAddress bytes]; + + if (memcmp(&sSockaddr4->sin_addr, &cSockaddr4->sin_addr, sizeof(struct in_addr)) != 0) + { + return NO; + } + if (memcmp(&sSockaddr4->sin_port, &cSockaddr4->sin_port, sizeof(in_port_t)) != 0) + { + return NO; + } + + return YES; +} + +- (BOOL)isConnectedToAddress6:(NSData *)someAddr6 +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(flags & kDidConnect, @"Not connected"); + NSAssert(cachedConnectedAddress, @"Expected cached connected address"); + + if (cachedConnectedFamily != AF_INET6) + { + return NO; + } + + const struct sockaddr_in6 *sSockaddr6 = (const struct sockaddr_in6 *)[someAddr6 bytes]; + const struct sockaddr_in6 *cSockaddr6 = (const struct sockaddr_in6 *)[cachedConnectedAddress bytes]; + + if (memcmp(&sSockaddr6->sin6_addr, &cSockaddr6->sin6_addr, sizeof(struct in6_addr)) != 0) + { + return NO; + } + if (memcmp(&sSockaddr6->sin6_port, &cSockaddr6->sin6_port, sizeof(in_port_t)) != 0) + { + return NO; + } + + return YES; +} + +- (unsigned int)indexOfInterfaceAddr4:(NSData *)interfaceAddr4 +{ + if (interfaceAddr4 == nil) + return 0; + if ([interfaceAddr4 length] != sizeof(struct sockaddr_in)) + return 0; + + int result = 0; + const struct sockaddr_in *ifaceAddr = (const struct sockaddr_in *)[interfaceAddr4 bytes]; + + struct ifaddrs *addrs; + const struct ifaddrs *cursor; + + if ((getifaddrs(&addrs) == 0)) + { + cursor = addrs; + while (cursor != NULL) + { + if (cursor->ifa_addr->sa_family == AF_INET) + { + // IPv4 + + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)cursor->ifa_addr; + + if (memcmp(&addr->sin_addr, &ifaceAddr->sin_addr, sizeof(struct in_addr)) == 0) + { + result = if_nametoindex(cursor->ifa_name); + break; + } + } + + cursor = cursor->ifa_next; + } + + freeifaddrs(addrs); + } + + return result; +} + +- (unsigned int)indexOfInterfaceAddr6:(NSData *)interfaceAddr6 +{ + if (interfaceAddr6 == nil) + return 0; + if ([interfaceAddr6 length] != sizeof(struct sockaddr_in6)) + return 0; + + int result = 0; + const struct sockaddr_in6 *ifaceAddr = (const struct sockaddr_in6 *)[interfaceAddr6 bytes]; + + struct ifaddrs *addrs; + const struct ifaddrs *cursor; + + if ((getifaddrs(&addrs) == 0)) + { + cursor = addrs; + while (cursor != NULL) + { + if (cursor->ifa_addr->sa_family == AF_INET6) + { + // IPv6 + + const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(const void *)cursor->ifa_addr; + + if (memcmp(&addr->sin6_addr, &ifaceAddr->sin6_addr, sizeof(struct in6_addr)) == 0) + { + result = if_nametoindex(cursor->ifa_name); + break; + } + } + + cursor = cursor->ifa_next; + } + + freeifaddrs(addrs); + } + + return result; +} + +- (void)setupSendAndReceiveSourcesForSocket4 +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + send4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket4FD, 0, socketQueue); + receive4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket4FD, 0, socketQueue); + + // Setup event handlers + + dispatch_source_set_event_handler(send4Source, ^{ @autoreleasepool { + + LogVerbose(@"send4EventBlock"); + LogVerbose(@"dispatch_source_get_data(send4Source) = %lu", dispatch_source_get_data(send4Source)); + + self->flags |= kSock4CanAcceptBytes; + + // If we're ready to send data, do so immediately. + // Otherwise pause the send source or it will continue to fire over and over again. + + if (self->currentSend == nil) + { + LogVerbose(@"Nothing to send"); + [self suspendSend4Source]; + } + else if (self->currentSend->resolveInProgress) + { + LogVerbose(@"currentSend - waiting for address resolve"); + [self suspendSend4Source]; + } + else if (self->currentSend->filterInProgress) + { + LogVerbose(@"currentSend - waiting on sendFilter"); + [self suspendSend4Source]; + } + else + { + [self doSend]; + } + + }}); + + dispatch_source_set_event_handler(receive4Source, ^{ @autoreleasepool { + + LogVerbose(@"receive4EventBlock"); + + self->socket4FDBytesAvailable = dispatch_source_get_data(self->receive4Source); + LogVerbose(@"socket4FDBytesAvailable: %lu", socket4FDBytesAvailable); + + if (self->socket4FDBytesAvailable > 0) + [self doReceive]; + else + [self doReceiveEOF]; + + }}); + + // Setup cancel handlers + + __block int socketFDRefCount = 2; + + int theSocketFD = socket4FD; + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theSendSource = send4Source; + dispatch_source_t theReceiveSource = receive4Source; + #endif + + dispatch_source_set_cancel_handler(send4Source, ^{ + + LogVerbose(@"send4CancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(send4Source)"); + dispatch_release(theSendSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socket4FD)"); + close(theSocketFD); + } + }); + + dispatch_source_set_cancel_handler(receive4Source, ^{ + + LogVerbose(@"receive4CancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(receive4Source)"); + dispatch_release(theReceiveSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socket4FD)"); + close(theSocketFD); + } + }); + + // We will not be able to receive until the socket is bound to a port, + // either explicitly via bind, or implicitly by connect or by sending data. + // + // But we should be able to send immediately. + + socket4FDBytesAvailable = 0; + flags |= kSock4CanAcceptBytes; + + flags |= kSend4SourceSuspended; + flags |= kReceive4SourceSuspended; +} + +- (void)setupSendAndReceiveSourcesForSocket6 +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + send6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket6FD, 0, socketQueue); + receive6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket6FD, 0, socketQueue); + + // Setup event handlers + + dispatch_source_set_event_handler(send6Source, ^{ @autoreleasepool { + + LogVerbose(@"send6EventBlock"); + LogVerbose(@"dispatch_source_get_data(send6Source) = %lu", dispatch_source_get_data(send6Source)); + + self->flags |= kSock6CanAcceptBytes; + + // If we're ready to send data, do so immediately. + // Otherwise pause the send source or it will continue to fire over and over again. + + if (self->currentSend == nil) + { + LogVerbose(@"Nothing to send"); + [self suspendSend6Source]; + } + else if (self->currentSend->resolveInProgress) + { + LogVerbose(@"currentSend - waiting for address resolve"); + [self suspendSend6Source]; + } + else if (self->currentSend->filterInProgress) + { + LogVerbose(@"currentSend - waiting on sendFilter"); + [self suspendSend6Source]; + } + else + { + [self doSend]; + } + + }}); + + dispatch_source_set_event_handler(receive6Source, ^{ @autoreleasepool { + + LogVerbose(@"receive6EventBlock"); + + self->socket6FDBytesAvailable = dispatch_source_get_data(self->receive6Source); + LogVerbose(@"socket6FDBytesAvailable: %lu", socket6FDBytesAvailable); + + if (self->socket6FDBytesAvailable > 0) + [self doReceive]; + else + [self doReceiveEOF]; + + }}); + + // Setup cancel handlers + + __block int socketFDRefCount = 2; + + int theSocketFD = socket6FD; + + #if !OS_OBJECT_USE_OBJC + dispatch_source_t theSendSource = send6Source; + dispatch_source_t theReceiveSource = receive6Source; + #endif + + dispatch_source_set_cancel_handler(send6Source, ^{ + + LogVerbose(@"send6CancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(send6Source)"); + dispatch_release(theSendSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socket6FD)"); + close(theSocketFD); + } + }); + + dispatch_source_set_cancel_handler(receive6Source, ^{ + + LogVerbose(@"receive6CancelBlock"); + + #if !OS_OBJECT_USE_OBJC + LogVerbose(@"dispatch_release(receive6Source)"); + dispatch_release(theReceiveSource); + #endif + + if (--socketFDRefCount == 0) + { + LogVerbose(@"close(socket6FD)"); + close(theSocketFD); + } + }); + + // We will not be able to receive until the socket is bound to a port, + // either explicitly via bind, or implicitly by connect or by sending data. + // + // But we should be able to send immediately. + + socket6FDBytesAvailable = 0; + flags |= kSock6CanAcceptBytes; + + flags |= kSend6SourceSuspended; + flags |= kReceive6SourceSuspended; +} + +- (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError * __autoreleasing *)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(((flags & kDidCreateSockets) == 0), @"Sockets have already been created"); + + // CreateSocket Block + // This block will be invoked below. + + int(^createSocket)(int) = ^int (int domain) { + + int socketFD = socket(domain, SOCK_DGRAM, 0); + + if (socketFD == SOCKET_NULL) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error in socket() function"]; + + return SOCKET_NULL; + } + + int status; + + // Set socket options + + status = fcntl(socketFD, F_SETFL, O_NONBLOCK); + if (status == -1) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error enabling non-blocking IO on socket (fcntl)"]; + + close(socketFD); + return SOCKET_NULL; + } + + int reuseaddr = 1; + status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); + if (status == -1) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error enabling address reuse (setsockopt)"]; + + close(socketFD); + return SOCKET_NULL; + } + + int nosigpipe = 1; + status = setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); + if (status == -1) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error disabling sigpipe (setsockopt)"]; + + close(socketFD); + return SOCKET_NULL; + } + + /** + * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. + * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. + * + * The default maximum size of the UDP buffer in iOS is 9216 bytes. + * + * This is the reason of #222(GCD does not necessarily return the size of an entire UDP packet) and + * #535(GCDAsyncUDPSocket can not send data when data is greater than 9K) + * + * + * Enlarge the maximum size of UDP packet. + * I can not ensure the protocol type now so that the max size is set to 65535 :) + **/ + + status = setsockopt(socketFD, SOL_SOCKET, SO_SNDBUF, (const char*)&self->maxSendSize, sizeof(int)); + if (status == -1) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error setting send buffer size (setsockopt)"]; + close(socketFD); + return SOCKET_NULL; + } + + status = setsockopt(socketFD, SOL_SOCKET, SO_RCVBUF, (const char*)&self->maxSendSize, sizeof(int)); + if (status == -1) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error setting receive buffer size (setsockopt)"]; + close(socketFD); + return SOCKET_NULL; + } + + + return socketFD; + }; + + // Create sockets depending upon given configuration. + + if (useIPv4) + { + LogVerbose(@"Creating IPv4 socket"); + + socket4FD = createSocket(AF_INET); + if (socket4FD == SOCKET_NULL) + { + // errPtr set in local createSocket() block + return NO; + } + } + + if (useIPv6) + { + LogVerbose(@"Creating IPv6 socket"); + + socket6FD = createSocket(AF_INET6); + if (socket6FD == SOCKET_NULL) + { + // errPtr set in local createSocket() block + + if (socket4FD != SOCKET_NULL) + { + close(socket4FD); + socket4FD = SOCKET_NULL; + } + + return NO; + } + } + + // Setup send and receive sources + + if (useIPv4) + [self setupSendAndReceiveSourcesForSocket4]; + if (useIPv6) + [self setupSendAndReceiveSourcesForSocket6]; + + flags |= kDidCreateSockets; + return YES; +} + +- (BOOL)createSockets:(NSError **)errPtr +{ + LogTrace(); + + BOOL useIPv4 = [self isIPv4Enabled]; + BOOL useIPv6 = [self isIPv6Enabled]; + + return [self createSocket4:useIPv4 socket6:useIPv6 error:errPtr]; +} + +- (void)suspendSend4Source +{ + if (send4Source && !(flags & kSend4SourceSuspended)) + { + LogVerbose(@"dispatch_suspend(send4Source)"); + + dispatch_suspend(send4Source); + flags |= kSend4SourceSuspended; + } +} + +- (void)suspendSend6Source +{ + if (send6Source && !(flags & kSend6SourceSuspended)) + { + LogVerbose(@"dispatch_suspend(send6Source)"); + + dispatch_suspend(send6Source); + flags |= kSend6SourceSuspended; + } +} + +- (void)resumeSend4Source +{ + if (send4Source && (flags & kSend4SourceSuspended)) + { + LogVerbose(@"dispatch_resume(send4Source)"); + + dispatch_resume(send4Source); + flags &= ~kSend4SourceSuspended; + } +} + +- (void)resumeSend6Source +{ + if (send6Source && (flags & kSend6SourceSuspended)) + { + LogVerbose(@"dispatch_resume(send6Source)"); + + dispatch_resume(send6Source); + flags &= ~kSend6SourceSuspended; + } +} + +- (void)suspendReceive4Source +{ + if (receive4Source && !(flags & kReceive4SourceSuspended)) + { + LogVerbose(@"dispatch_suspend(receive4Source)"); + + dispatch_suspend(receive4Source); + flags |= kReceive4SourceSuspended; + } +} + +- (void)suspendReceive6Source +{ + if (receive6Source && !(flags & kReceive6SourceSuspended)) + { + LogVerbose(@"dispatch_suspend(receive6Source)"); + + dispatch_suspend(receive6Source); + flags |= kReceive6SourceSuspended; + } +} + +- (void)resumeReceive4Source +{ + if (receive4Source && (flags & kReceive4SourceSuspended)) + { + LogVerbose(@"dispatch_resume(receive4Source)"); + + dispatch_resume(receive4Source); + flags &= ~kReceive4SourceSuspended; + } +} + +- (void)resumeReceive6Source +{ + if (receive6Source && (flags & kReceive6SourceSuspended)) + { + LogVerbose(@"dispatch_resume(receive6Source)"); + + dispatch_resume(receive6Source); + flags &= ~kReceive6SourceSuspended; + } +} + +- (void)closeSocket4 +{ + if (socket4FD != SOCKET_NULL) + { + LogVerbose(@"dispatch_source_cancel(send4Source)"); + dispatch_source_cancel(send4Source); + + LogVerbose(@"dispatch_source_cancel(receive4Source)"); + dispatch_source_cancel(receive4Source); + + // For some crazy reason (in my opinion), cancelling a dispatch source doesn't + // invoke the cancel handler if the dispatch source is paused. + // So we have to unpause the source if needed. + // This allows the cancel handler to be run, which in turn releases the source and closes the socket. + + [self resumeSend4Source]; + [self resumeReceive4Source]; + + // The sockets will be closed by the cancel handlers of the corresponding source + + send4Source = NULL; + receive4Source = NULL; + + socket4FD = SOCKET_NULL; + + // Clear socket states + + socket4FDBytesAvailable = 0; + flags &= ~kSock4CanAcceptBytes; + + // Clear cached info + + cachedLocalAddress4 = nil; + cachedLocalHost4 = nil; + cachedLocalPort4 = 0; + } +} + +- (void)closeSocket6 +{ + if (socket6FD != SOCKET_NULL) + { + LogVerbose(@"dispatch_source_cancel(send6Source)"); + dispatch_source_cancel(send6Source); + + LogVerbose(@"dispatch_source_cancel(receive6Source)"); + dispatch_source_cancel(receive6Source); + + // For some crazy reason (in my opinion), cancelling a dispatch source doesn't + // invoke the cancel handler if the dispatch source is paused. + // So we have to unpause the source if needed. + // This allows the cancel handler to be run, which in turn releases the source and closes the socket. + + [self resumeSend6Source]; + [self resumeReceive6Source]; + + send6Source = NULL; + receive6Source = NULL; + + // The sockets will be closed by the cancel handlers of the corresponding source + + socket6FD = SOCKET_NULL; + + // Clear socket states + + socket6FDBytesAvailable = 0; + flags &= ~kSock6CanAcceptBytes; + + // Clear cached info + + cachedLocalAddress6 = nil; + cachedLocalHost6 = nil; + cachedLocalPort6 = 0; + } +} + +- (void)closeSockets +{ + [self closeSocket4]; + [self closeSocket6]; + + flags &= ~kDidCreateSockets; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Diagnostics +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)getLocalAddress:(NSData **)dataPtr + host:(NSString **)hostPtr + port:(uint16_t *)portPtr + forSocket:(int)socketFD + withFamily:(int)socketFamily +{ + + NSData *data = nil; + NSString *host = nil; + uint16_t port = 0; + + if (socketFamily == AF_INET) + { + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) + { + data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; + host = [[self class] hostFromSockaddr4:&sockaddr4]; + port = [[self class] portFromSockaddr4:&sockaddr4]; + } + else + { + LogWarn(@"Error in getsockname: %@", [self errnoError]); + } + } + else if (socketFamily == AF_INET6) + { + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) + { + data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; + host = [[self class] hostFromSockaddr6:&sockaddr6]; + port = [[self class] portFromSockaddr6:&sockaddr6]; + } + else + { + LogWarn(@"Error in getsockname: %@", [self errnoError]); + } + } + + if (dataPtr) *dataPtr = data; + if (hostPtr) *hostPtr = host; + if (portPtr) *portPtr = port; + + return (data != nil); +} + +- (void)maybeUpdateCachedLocalAddress4Info +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if ( cachedLocalAddress4 || ((flags & kDidBind) == 0) || (socket4FD == SOCKET_NULL) ) + { + return; + } + + NSData *address = nil; + NSString *host = nil; + uint16_t port = 0; + + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket4FD withFamily:AF_INET]) + { + + cachedLocalAddress4 = address; + cachedLocalHost4 = host; + cachedLocalPort4 = port; + } +} + +- (void)maybeUpdateCachedLocalAddress6Info +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if ( cachedLocalAddress6 || ((flags & kDidBind) == 0) || (socket6FD == SOCKET_NULL) ) + { + return; + } + + NSData *address = nil; + NSString *host = nil; + uint16_t port = 0; + + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket6FD withFamily:AF_INET6]) + { + + cachedLocalAddress6 = address; + cachedLocalHost6 = host; + cachedLocalPort6 = port; + } +} + +- (NSData *)localAddress +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + + if (self->socket4FD != SOCKET_NULL) + { + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalAddress4; + } + else + { + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalAddress6; + } + + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSString *)localHost +{ + __block NSString *result = nil; + + dispatch_block_t block = ^{ + + if (self->socket4FD != SOCKET_NULL) + { + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalHost4; + } + else + { + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalHost6; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (uint16_t)localPort +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + if (self->socket4FD != SOCKET_NULL) + { + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalPort4; + } + else + { + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalPort6; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSData *)localAddress_IPv4 +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalAddress4; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSString *)localHost_IPv4 +{ + __block NSString *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalHost4; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (uint16_t)localPort_IPv4 +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress4Info]; + result = self->cachedLocalPort4; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSData *)localAddress_IPv6 +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalAddress6; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSString *)localHost_IPv6 +{ + __block NSString *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalHost6; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (uint16_t)localPort_IPv6 +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedLocalAddress6Info]; + result = self->cachedLocalPort6; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (void)maybeUpdateCachedConnectedAddressInfo +{ + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (cachedConnectedAddress || (flags & kDidConnect) == 0) + { + return; + } + + NSData *data = nil; + NSString *host = nil; + uint16_t port = 0; + int family = AF_UNSPEC; + + if (socket4FD != SOCKET_NULL) + { + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + if (getpeername(socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) + { + data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; + host = [[self class] hostFromSockaddr4:&sockaddr4]; + port = [[self class] portFromSockaddr4:&sockaddr4]; + family = AF_INET; + } + else + { + LogWarn(@"Error in getpeername: %@", [self errnoError]); + } + } + else if (socket6FD != SOCKET_NULL) + { + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + if (getpeername(socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) + { + data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; + host = [[self class] hostFromSockaddr6:&sockaddr6]; + port = [[self class] portFromSockaddr6:&sockaddr6]; + family = AF_INET6; + } + else + { + LogWarn(@"Error in getpeername: %@", [self errnoError]); + } + } + + + cachedConnectedAddress = data; + cachedConnectedHost = host; + cachedConnectedPort = port; + cachedConnectedFamily = family; +} + +- (NSData *)connectedAddress +{ + __block NSData *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedConnectedAddressInfo]; + result = self->cachedConnectedAddress; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (NSString *)connectedHost +{ + __block NSString *result = nil; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedConnectedAddressInfo]; + result = self->cachedConnectedHost; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (uint16_t)connectedPort +{ + __block uint16_t result = 0; + + dispatch_block_t block = ^{ + + [self maybeUpdateCachedConnectedAddressInfo]; + result = self->cachedConnectedPort; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, AutoreleasedBlock(block)); + + return result; +} + +- (BOOL)isConnected +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + result = (self->flags & kDidConnect) ? YES : NO; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isClosed +{ + __block BOOL result = YES; + + dispatch_block_t block = ^{ + + result = (self->flags & kDidCreateSockets) ? NO : YES; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isIPv4 +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + + if (self->flags & kDidCreateSockets) + { + result = (self->socket4FD != SOCKET_NULL); + } + else + { + result = [self isIPv4Enabled]; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +- (BOOL)isIPv6 +{ + __block BOOL result = NO; + + dispatch_block_t block = ^{ + + if (self->flags & kDidCreateSockets) + { + result = (self->socket6FD != SOCKET_NULL); + } + else + { + result = [self isIPv6Enabled]; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Binding +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * This method runs through the various checks required prior to a bind attempt. + * It is shared between the various bind methods. +**/ +- (BOOL)preBind:(NSError **)errPtr +{ + if (![self preOp:errPtr]) + { + return NO; + } + + if (flags & kDidBind) + { + if (errPtr) + { + NSString *msg = @"Cannot bind a socket more than once."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if ((flags & kConnecting) || (flags & kDidConnect)) + { + if (errPtr) + { + NSString *msg = @"Cannot bind after connecting. If needed, bind first, then connect."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled + { + if (errPtr) + { + NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + return YES; +} + +- (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr +{ + return [self bindToPort:port interface:nil error:errPtr]; +} + +- (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Run through sanity checks + + if (![self preBind:&err]) + { + return_from_block; + } + + // Check the given interface + + NSData *interface4 = nil; + NSData *interface6 = nil; + + [self convertIntefaceDescription:interface port:port intoAddress4:&interface4 address6:&interface6]; + + if ((interface4 == nil) && (interface6 == nil)) + { + NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; + err = [self badParamError:msg]; + + return_from_block; + } + + BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && (interface6 == nil)) + { + NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; + err = [self badParamError:msg]; + + return_from_block; + } + + if (isIPv6Disabled && (interface4 == nil)) + { + NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Determine protocol(s) + + BOOL useIPv4 = !isIPv4Disabled && (interface4 != nil); + BOOL useIPv6 = !isIPv6Disabled && (interface6 != nil); + + // Create the socket(s) if needed + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) + { + return_from_block; + } + } + + // Bind the socket(s) + + LogVerbose(@"Binding socket to port(%hu) interface(%@)", port, interface); + + if (useIPv4) + { + int status = bind(self->socket4FD, (const struct sockaddr *)[interface4 bytes], (socklen_t)[interface4 length]); + if (status == -1) + { + [self closeSockets]; + + NSString *reason = @"Error in bind() function"; + err = [self errnoErrorWithReason:reason]; + + return_from_block; + } + } + + if (useIPv6) + { + int status = bind(self->socket6FD, (const struct sockaddr *)[interface6 bytes], (socklen_t)[interface6 length]); + if (status == -1) + { + [self closeSockets]; + + NSString *reason = @"Error in bind() function"; + err = [self errnoErrorWithReason:reason]; + + return_from_block; + } + } + + // Update flags + + self->flags |= kDidBind; + + if (!useIPv4) self->flags |= kIPv4Deactivated; + if (!useIPv6) self->flags |= kIPv6Deactivated; + + result = YES; + + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error binding to port/interface: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +- (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Run through sanity checks + + if (![self preBind:&err]) + { + return_from_block; + } + + // Check the given address + + int addressFamily = [[self class] familyFromAddress:localAddr]; + + if (addressFamily == AF_UNSPEC) + { + NSString *msg = @"A valid IPv4 or IPv6 address was not given"; + err = [self badParamError:msg]; + + return_from_block; + } + + NSData *localAddr4 = (addressFamily == AF_INET) ? localAddr : nil; + NSData *localAddr6 = (addressFamily == AF_INET6) ? localAddr : nil; + + BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && localAddr4) + { + NSString *msg = @"IPv4 has been disabled and an IPv4 address was passed."; + err = [self badParamError:msg]; + + return_from_block; + } + + if (isIPv6Disabled && localAddr6) + { + NSString *msg = @"IPv6 has been disabled and an IPv6 address was passed."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Determine protocol(s) + + BOOL useIPv4 = !isIPv4Disabled && (localAddr4 != nil); + BOOL useIPv6 = !isIPv6Disabled && (localAddr6 != nil); + + // Create the socket(s) if needed + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) + { + return_from_block; + } + } + + // Bind the socket(s) + + if (useIPv4) + { + LogVerbose(@"Binding socket to address(%@:%hu)", + [[self class] hostFromAddress:localAddr4], + [[self class] portFromAddress:localAddr4]); + + int status = bind(self->socket4FD, (const struct sockaddr *)[localAddr4 bytes], (socklen_t)[localAddr4 length]); + if (status == -1) + { + [self closeSockets]; + + NSString *reason = @"Error in bind() function"; + err = [self errnoErrorWithReason:reason]; + + return_from_block; + } + } + else + { + LogVerbose(@"Binding socket to address(%@:%hu)", + [[self class] hostFromAddress:localAddr6], + [[self class] portFromAddress:localAddr6]); + + int status = bind(self->socket6FD, (const struct sockaddr *)[localAddr6 bytes], (socklen_t)[localAddr6 length]); + if (status == -1) + { + [self closeSockets]; + + NSString *reason = @"Error in bind() function"; + err = [self errnoErrorWithReason:reason]; + + return_from_block; + } + } + + // Update flags + + self->flags |= kDidBind; + + if (!useIPv4) self->flags |= kIPv4Deactivated; + if (!useIPv6) self->flags |= kIPv6Deactivated; + + result = YES; + + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error binding to address: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Connecting +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * This method runs through the various checks required prior to a connect attempt. + * It is shared between the various connect methods. +**/ +- (BOOL)preConnect:(NSError **)errPtr +{ + if (![self preOp:errPtr]) + { + return NO; + } + + if ((flags & kConnecting) || (flags & kDidConnect)) + { + if (errPtr) + { + NSString *msg = @"Cannot connect a socket more than once."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; + BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; + + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled + { + if (errPtr) + { + NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + return YES; +} + +- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Run through sanity checks. + + if (![self preConnect:&err]) + { + return_from_block; + } + + // Check parameter(s) + + if (host == nil) + { + NSString *msg = @"The host param is nil. Should be domain name or IP address string."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Create the socket(s) if needed + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSockets:&err]) + { + return_from_block; + } + } + + // Create special connect packet + + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; + packet->resolveInProgress = YES; + + // Start asynchronous DNS resolve for host:port on background queue + + LogVerbose(@"Dispatching DNS resolve for connect..."); + + [self asyncResolveHost:host port:port withCompletionBlock:^(NSArray *addresses, NSError *error) { + + // The asyncResolveHost:port:: method asynchronously dispatches a task onto the global concurrent queue, + // and immediately returns. Once the async resolve task completes, + // this block is executed on our socketQueue. + + packet->resolveInProgress = NO; + + packet->addresses = addresses; + packet->error = error; + + [self maybeConnect]; + }]; + + // Updates flags, add connect packet to send queue, and pump send queue + + self->flags |= kConnecting; + + [self->sendQueue addObject:packet]; + [self maybeDequeueSend]; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error connecting to host/port: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Run through sanity checks. + + if (![self preConnect:&err]) + { + return_from_block; + } + + // Check parameter(s) + + if (remoteAddr == nil) + { + NSString *msg = @"The address param is nil. Should be a valid address."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Create the socket(s) if needed + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSockets:&err]) + { + return_from_block; + } + } + + // The remoteAddr parameter could be of type NSMutableData. + // So we copy it to be safe. + + NSData *address = [remoteAddr copy]; + NSArray *addresses = [NSArray arrayWithObject:address]; + + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; + packet->addresses = addresses; + + // Updates flags, add connect packet to send queue, and pump send queue + + self->flags |= kConnecting; + + [self->sendQueue addObject:packet]; + [self maybeDequeueSend]; + + result = YES; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error connecting to address: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +- (void)maybeConnect +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + + BOOL sendQueueReady = [currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]; + + if (sendQueueReady) + { + GCDAsyncUdpSpecialPacket *connectPacket = (GCDAsyncUdpSpecialPacket *)currentSend; + + if (connectPacket->resolveInProgress) + { + LogVerbose(@"Waiting for DNS resolve..."); + } + else + { + if (connectPacket->error) + { + [self notifyDidNotConnect:connectPacket->error]; + } + else + { + NSData *address = nil; + NSError *error = nil; + + int addressFamily = [self getAddress:&address error:&error fromAddresses:connectPacket->addresses]; + + // Perform connect + + BOOL result = NO; + + switch (addressFamily) + { + case AF_INET : result = [self connectWithAddress4:address error:&error]; break; + case AF_INET6 : result = [self connectWithAddress6:address error:&error]; break; + } + + if (result) + { + flags |= kDidBind; + flags |= kDidConnect; + + cachedConnectedAddress = address; + cachedConnectedHost = [[self class] hostFromAddress:address]; + cachedConnectedPort = [[self class] portFromAddress:address]; + cachedConnectedFamily = addressFamily; + + [self notifyDidConnectToAddress:address]; + } + else + { + [self notifyDidNotConnect:error]; + } + } + + flags &= ~kConnecting; + + [self endCurrentSend]; + [self maybeDequeueSend]; + } + } +} + +- (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + int status = connect(socket4FD, (const struct sockaddr *)[address4 bytes], (socklen_t)[address4 length]); + if (status != 0) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error in connect() function"]; + + return NO; + } + + [self closeSocket6]; + flags |= kIPv6Deactivated; + + return YES; +} + +- (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + int status = connect(socket6FD, (const struct sockaddr *)[address6 bytes], (socklen_t)[address6 length]); + if (status != 0) + { + if (errPtr) + *errPtr = [self errnoErrorWithReason:@"Error in connect() function"]; + + return NO; + } + + [self closeSocket4]; + flags |= kIPv4Deactivated; + + return YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Multicast +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)preJoin:(NSError **)errPtr +{ + if (![self preOp:errPtr]) + { + return NO; + } + + if (!(flags & kDidBind)) + { + if (errPtr) + { + NSString *msg = @"Must bind a socket before joining a multicast group."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + if ((flags & kConnecting) || (flags & kDidConnect)) + { + if (errPtr) + { + NSString *msg = @"Cannot join a multicast group if connected."; + *errPtr = [self badConfigError:msg]; + } + return NO; + } + + return YES; +} + +- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr +{ + return [self joinMulticastGroup:group onInterface:nil error:errPtr]; +} + +- (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr +{ + // IP_ADD_MEMBERSHIP == IPV6_JOIN_GROUP + return [self performMulticastRequest:IP_ADD_MEMBERSHIP forGroup:group onInterface:interface error:errPtr]; +} + +- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr +{ + return [self leaveMulticastGroup:group onInterface:nil error:errPtr]; +} + +- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr +{ + // IP_DROP_MEMBERSHIP == IPV6_LEAVE_GROUP + return [self performMulticastRequest:IP_DROP_MEMBERSHIP forGroup:group onInterface:interface error:errPtr]; +} + +- (BOOL)performMulticastRequest:(int)requestType + forGroup:(NSString *)group + onInterface:(NSString *)interface + error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + // Run through sanity checks + + if (![self preJoin:&err]) + { + return_from_block; + } + + // Convert group to address + + NSData *groupAddr4 = nil; + NSData *groupAddr6 = nil; + + [self convertNumericHost:group port:0 intoAddress4:&groupAddr4 address6:&groupAddr6]; + + if ((groupAddr4 == nil) && (groupAddr6 == nil)) + { + NSString *msg = @"Unknown group. Specify valid group IP address."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Convert interface to address + + NSData *interfaceAddr4 = nil; + NSData *interfaceAddr6 = nil; + + [self convertIntefaceDescription:interface port:0 intoAddress4:&interfaceAddr4 address6:&interfaceAddr6]; + + if ((interfaceAddr4 == nil) && (interfaceAddr6 == nil)) + { + NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; + err = [self badParamError:msg]; + + return_from_block; + } + + // Perform join + + if ((self->socket4FD != SOCKET_NULL) && groupAddr4 && interfaceAddr4) + { + const struct sockaddr_in *nativeGroup = (const struct sockaddr_in *)[groupAddr4 bytes]; + const struct sockaddr_in *nativeIface = (const struct sockaddr_in *)[interfaceAddr4 bytes]; + + struct ip_mreq imreq; + imreq.imr_multiaddr = nativeGroup->sin_addr; + imreq.imr_interface = nativeIface->sin_addr; + + int status = setsockopt(self->socket4FD, IPPROTO_IP, requestType, (const void *)&imreq, sizeof(imreq)); + if (status != 0) + { + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; + + return_from_block; + } + + // Using IPv4 only + [self closeSocket6]; + + result = YES; + } + else if ((self->socket6FD != SOCKET_NULL) && groupAddr6 && interfaceAddr6) + { + const struct sockaddr_in6 *nativeGroup = (const struct sockaddr_in6 *)[groupAddr6 bytes]; + + struct ipv6_mreq imreq; + imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr; + imreq.ipv6mr_interface = [self indexOfInterfaceAddr6:interfaceAddr6]; + + int status = setsockopt(self->socket6FD, IPPROTO_IPV6, requestType, (const void *)&imreq, sizeof(imreq)); + if (status != 0) + { + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; + + return_from_block; + } + + // Using IPv6 only + [self closeSocket4]; + + result = YES; + } + else + { + NSString *msg = @"Socket, group, and interface do not have matching IP versions"; + err = [self badParamError:msg]; + + return_from_block; + } + + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (errPtr) + *errPtr = err; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Reuse port +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + if (![self preOp:&err]) + { + return_from_block; + } + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSockets:&err]) + { + return_from_block; + } + } + + int value = flag ? 1 : 0; + if (self->socket4FD != SOCKET_NULL) + { + int error = setsockopt(self->socket4FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value)); + + if (error) + { + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; + + return_from_block; + } + result = YES; + } + + if (self->socket6FD != SOCKET_NULL) + { + int error = setsockopt(self->socket6FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value)); + + if (error) + { + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; + + return_from_block; + } + result = YES; + } + + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (errPtr) + *errPtr = err; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Broadcast +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr +{ + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ @autoreleasepool { + + if (![self preOp:&err]) + { + return_from_block; + } + + if ((self->flags & kDidCreateSockets) == 0) + { + if (![self createSockets:&err]) + { + return_from_block; + } + } + + if (self->socket4FD != SOCKET_NULL) + { + int value = flag ? 1 : 0; + int error = setsockopt(self->socket4FD, SOL_SOCKET, SO_BROADCAST, (const void *)&value, sizeof(value)); + + if (error) + { + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; + + return_from_block; + } + result = YES; + } + + // IPv6 does not implement broadcast, the ability to send a packet to all hosts on the attached link. + // The same effect can be achieved by sending a packet to the link-local all hosts multicast group. + + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (errPtr) + *errPtr = err; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Sending +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)sendData:(NSData *)data withTag:(long)tag +{ + [self sendData:data withTimeout:-1.0 tag:tag]; +} + +- (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + LogTrace(); + + if ([data length] == 0) + { + LogWarn(@"Ignoring attempt to send nil/empty data."); + return; + } + + + + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + [self->sendQueue addObject:packet]; + [self maybeDequeueSend]; + }}); + +} + +- (void)sendData:(NSData *)data + toHost:(NSString *)host + port:(uint16_t)port + withTimeout:(NSTimeInterval)timeout + tag:(long)tag +{ + LogTrace(); + + if ([data length] == 0) + { + LogWarn(@"Ignoring attempt to send nil/empty data."); + return; + } + + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; + packet->resolveInProgress = YES; + + [self asyncResolveHost:host port:port withCompletionBlock:^(NSArray *addresses, NSError *error) { + + // The asyncResolveHost:port:: method asynchronously dispatches a task onto the global concurrent queue, + // and immediately returns. Once the async resolve task completes, + // this block is executed on our socketQueue. + + packet->resolveInProgress = NO; + + packet->resolvedAddresses = addresses; + packet->resolveError = error; + + if (packet == self->currentSend) + { + LogVerbose(@"currentSend - address resolved"); + [self doPreSend]; + } + }]; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + [self->sendQueue addObject:packet]; + [self maybeDequeueSend]; + + }}); + +} + +- (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + LogTrace(); + + if ([data length] == 0) + { + LogWarn(@"Ignoring attempt to send nil/empty data."); + return; + } + + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; + packet->addressFamily = [GCDAsyncUdpSocket familyFromAddress:remoteAddr]; + packet->address = remoteAddr; + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + [self->sendQueue addObject:packet]; + [self maybeDequeueSend]; + }}); +} + +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue +{ + [self setSendFilter:filterBlock withQueue:filterQueue isAsynchronous:YES]; +} + +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous +{ + GCDAsyncUdpSocketSendFilterBlock newFilterBlock = NULL; + dispatch_queue_t newFilterQueue = NULL; + + if (filterBlock) + { + NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); + + newFilterBlock = [filterBlock copy]; + newFilterQueue = filterQueue; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(newFilterQueue); + #endif + } + + dispatch_block_t block = ^{ + + #if !OS_OBJECT_USE_OBJC + if (self->sendFilterQueue) dispatch_release(self->sendFilterQueue); + #endif + + self->sendFilterBlock = newFilterBlock; + self->sendFilterQueue = newFilterQueue; + self->sendFilterAsync = isAsynchronous; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)maybeDequeueSend +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + // If we don't have a send operation already in progress + if (currentSend == nil) + { + // Create the sockets if needed + if ((flags & kDidCreateSockets) == 0) + { + NSError *err = nil; + if (![self createSockets:&err]) + { + [self closeWithError:err]; + return; + } + } + + while ([sendQueue count] > 0) + { + // Dequeue the next object in the queue + currentSend = [sendQueue objectAtIndex:0]; + [sendQueue removeObjectAtIndex:0]; + + if ([currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]) + { + [self maybeConnect]; + + return; // The maybeConnect method, if it connects, will invoke this method again + } + else if (currentSend->resolveError) + { + // Notify delegate + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:currentSend->resolveError]; + + // Clear currentSend + currentSend = nil; + + continue; + } + else + { + // Start preprocessing checks on the send packet + [self doPreSend]; + + break; + } + } + + if ((currentSend == nil) && (flags & kCloseAfterSends)) + { + [self closeWithError:nil]; + } + } +} + +/** + * This method is called after a sendPacket has been dequeued. + * It performs various preprocessing checks on the packet, + * and queries the sendFilter (if set) to determine if the packet can be sent. + * + * If the packet passes all checks, it will be passed on to the doSend method. +**/ +- (void)doPreSend +{ + LogTrace(); + + // + // 1. Check for problems with send packet + // + + BOOL waitingForResolve = NO; + NSError *error = nil; + + if (flags & kDidConnect) + { + // Connected socket + + if (currentSend->resolveInProgress || currentSend->resolvedAddresses || currentSend->resolveError) + { + NSString *msg = @"Cannot specify destination of packet for connected socket"; + error = [self badConfigError:msg]; + } + else + { + currentSend->address = cachedConnectedAddress; + currentSend->addressFamily = cachedConnectedFamily; + } + } + else + { + // Non-Connected socket + + if (currentSend->resolveInProgress) + { + // We're waiting for the packet's destination to be resolved. + waitingForResolve = YES; + } + else if (currentSend->resolveError) + { + error = currentSend->resolveError; + } + else if (currentSend->address == nil) + { + if (currentSend->resolvedAddresses == nil) + { + NSString *msg = @"You must specify destination of packet for a non-connected socket"; + error = [self badConfigError:msg]; + } + else + { + // Pick the proper address to use (out of possibly several resolved addresses) + + NSData *address = nil; + int addressFamily = AF_UNSPEC; + + addressFamily = [self getAddress:&address error:&error fromAddresses:currentSend->resolvedAddresses]; + + currentSend->address = address; + currentSend->addressFamily = addressFamily; + } + } + } + + if (waitingForResolve) + { + // We're waiting for the packet's destination to be resolved. + + LogVerbose(@"currentSend - waiting for address resolve"); + + if (flags & kSock4CanAcceptBytes) { + [self suspendSend4Source]; + } + if (flags & kSock6CanAcceptBytes) { + [self suspendSend6Source]; + } + + return; + } + + if (error) + { + // Unable to send packet due to some error. + // Notify delegate and move on. + + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:error]; + [self endCurrentSend]; + [self maybeDequeueSend]; + + return; + } + + // + // 2. Query sendFilter (if applicable) + // + + if (sendFilterBlock && sendFilterQueue) + { + // Query sendFilter + + if (sendFilterAsync) + { + // Scenario 1 of 3 - Need to asynchronously query sendFilter + + currentSend->filterInProgress = YES; + GCDAsyncUdpSendPacket *sendPacket = currentSend; + + dispatch_async(sendFilterQueue, ^{ @autoreleasepool { + + BOOL allowed = self->sendFilterBlock(sendPacket->buffer, sendPacket->address, sendPacket->tag); + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + sendPacket->filterInProgress = NO; + if (sendPacket == self->currentSend) + { + if (allowed) + { + [self doSend]; + } + else + { + LogVerbose(@"currentSend - silently dropped by sendFilter"); + + [self notifyDidSendDataWithTag:self->currentSend->tag]; + [self endCurrentSend]; + [self maybeDequeueSend]; + } + } + }}); + }}); + } + else + { + // Scenario 2 of 3 - Need to synchronously query sendFilter + + __block BOOL allowed = YES; + + dispatch_sync(sendFilterQueue, ^{ @autoreleasepool { + + allowed = self->sendFilterBlock(self->currentSend->buffer, self->currentSend->address, self->currentSend->tag); + }}); + + if (allowed) + { + [self doSend]; + } + else + { + LogVerbose(@"currentSend - silently dropped by sendFilter"); + + [self notifyDidSendDataWithTag:currentSend->tag]; + [self endCurrentSend]; + [self maybeDequeueSend]; + } + } + } + else // if (!sendFilterBlock || !sendFilterQueue) + { + // Scenario 3 of 3 - No sendFilter. Just go straight into sending. + + [self doSend]; + } +} + +/** + * This method performs the actual sending of data in the currentSend packet. + * It should only be called if the +**/ +- (void)doSend +{ + LogTrace(); + + NSAssert(currentSend != nil, @"Invalid logic"); + + // Perform the actual send + + ssize_t result = 0; + + if (flags & kDidConnect) + { + // Connected socket + + const void *buffer = [currentSend->buffer bytes]; + size_t length = (size_t)[currentSend->buffer length]; + + if (currentSend->addressFamily == AF_INET) + { + result = send(socket4FD, buffer, length, 0); + LogVerbose(@"send(socket4FD) = %d", result); + } + else + { + result = send(socket6FD, buffer, length, 0); + LogVerbose(@"send(socket6FD) = %d", result); + } + } + else + { + // Non-Connected socket + + const void *buffer = [currentSend->buffer bytes]; + size_t length = (size_t)[currentSend->buffer length]; + + const void *dst = [currentSend->address bytes]; + socklen_t dstSize = (socklen_t)[currentSend->address length]; + + if (currentSend->addressFamily == AF_INET) + { + result = sendto(socket4FD, buffer, length, 0, dst, dstSize); + LogVerbose(@"sendto(socket4FD) = %d", result); + } + else + { + result = sendto(socket6FD, buffer, length, 0, dst, dstSize); + LogVerbose(@"sendto(socket6FD) = %d", result); + } + } + + // If the socket wasn't bound before, it is now + + if ((flags & kDidBind) == 0) + { + flags |= kDidBind; + } + + // Check the results. + // + // From the send() & sendto() manpage: + // + // Upon successful completion, the number of bytes which were sent is returned. + // Otherwise, -1 is returned and the global variable errno is set to indicate the error. + + BOOL waitingForSocket = NO; + NSError *socketError = nil; + + if (result == 0) + { + waitingForSocket = YES; + } + else if (result < 0) + { + if (errno == EAGAIN) + waitingForSocket = YES; + else + socketError = [self errnoErrorWithReason:@"Error in send() function."]; + } + + if (waitingForSocket) + { + // Not enough room in the underlying OS socket send buffer. + // Wait for a notification of available space. + + LogVerbose(@"currentSend - waiting for socket"); + + if (!(flags & kSock4CanAcceptBytes)) { + [self resumeSend4Source]; + } + if (!(flags & kSock6CanAcceptBytes)) { + [self resumeSend6Source]; + } + + if ((sendTimer == NULL) && (currentSend->timeout >= 0.0)) + { + // Unable to send packet right away. + // Start timer to timeout the send operation. + + [self setupSendTimerWithTimeout:currentSend->timeout]; + } + } + else if (socketError) + { + [self closeWithError:socketError]; + } + else // done + { + [self notifyDidSendDataWithTag:currentSend->tag]; + [self endCurrentSend]; + [self maybeDequeueSend]; + } +} + +/** + * Releases all resources associated with the currentSend. +**/ +- (void)endCurrentSend +{ + if (sendTimer) + { + dispatch_source_cancel(sendTimer); + #if !OS_OBJECT_USE_OBJC + dispatch_release(sendTimer); + #endif + sendTimer = NULL; + } + + currentSend = nil; +} + +/** + * Performs the operations to timeout the current send operation, and move on. +**/ +- (void)doSendTimeout +{ + LogTrace(); + + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:[self sendTimeoutError]]; + [self endCurrentSend]; + [self maybeDequeueSend]; +} + +/** + * Sets up a timer that fires to timeout the current send operation. + * This method should only be called once per send packet. +**/ +- (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout +{ + NSAssert(sendTimer == NULL, @"Invalid logic"); + NSAssert(timeout >= 0.0, @"Invalid logic"); + + LogTrace(); + + sendTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); + + dispatch_source_set_event_handler(sendTimer, ^{ @autoreleasepool { + + [self doSendTimeout]; + }}); + + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); + + dispatch_source_set_timer(sendTimer, tt, DISPATCH_TIME_FOREVER, 0); + dispatch_resume(sendTimer); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Receiving +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)receiveOnce:(NSError **)errPtr +{ + LogTrace(); + + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ + + if ((self->flags & kReceiveOnce) == 0) + { + if ((self->flags & kDidCreateSockets) == 0) + { + NSString *msg = @"Must bind socket before you can receive data. " + @"You can do this explicitly via bind, or implicitly via connect or by sending data."; + + err = [self badConfigError:msg]; + return_from_block; + } + + self->flags |= kReceiveOnce; // Enable + self->flags &= ~kReceiveContinuous; // Disable + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self doReceive]; + }}); + } + + result = YES; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error in beginReceiving: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +- (BOOL)beginReceiving:(NSError **)errPtr +{ + LogTrace(); + + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ + + if ((self->flags & kReceiveContinuous) == 0) + { + if ((self->flags & kDidCreateSockets) == 0) + { + NSString *msg = @"Must bind socket before you can receive data. " + @"You can do this explicitly via bind, or implicitly via connect or by sending data."; + + err = [self badConfigError:msg]; + return_from_block; + } + + self->flags |= kReceiveContinuous; // Enable + self->flags &= ~kReceiveOnce; // Disable + + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + [self doReceive]; + }}); + } + + result = YES; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error in beginReceiving: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + +- (void)pauseReceiving +{ + LogTrace(); + + dispatch_block_t block = ^{ + + self->flags &= ~kReceiveOnce; // Disable + self->flags &= ~kReceiveContinuous; // Disable + + if (self->socket4FDBytesAvailable > 0) { + [self suspendReceive4Source]; + } + if (self->socket6FDBytesAvailable > 0) { + [self suspendReceive6Source]; + } + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue +{ + [self setReceiveFilter:filterBlock withQueue:filterQueue isAsynchronous:YES]; +} + +- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous +{ + GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock = NULL; + dispatch_queue_t newFilterQueue = NULL; + + if (filterBlock) + { + NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); + + newFilterBlock = [filterBlock copy]; + newFilterQueue = filterQueue; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(newFilterQueue); + #endif + } + + dispatch_block_t block = ^{ + + #if !OS_OBJECT_USE_OBJC + if (self->receiveFilterQueue) dispatch_release(self->receiveFilterQueue); + #endif + + self->receiveFilterBlock = newFilterBlock; + self->receiveFilterQueue = newFilterQueue; + self->receiveFilterAsync = isAsynchronous; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +- (void)doReceive +{ + LogTrace(); + + if ((flags & (kReceiveOnce | kReceiveContinuous)) == 0) + { + LogVerbose(@"Receiving is paused..."); + + if (socket4FDBytesAvailable > 0) { + [self suspendReceive4Source]; + } + if (socket6FDBytesAvailable > 0) { + [self suspendReceive6Source]; + } + + return; + } + + if ((flags & kReceiveOnce) && (pendingFilterOperations > 0)) + { + LogVerbose(@"Receiving is temporarily paused (pending filter operations)..."); + + if (socket4FDBytesAvailable > 0) { + [self suspendReceive4Source]; + } + if (socket6FDBytesAvailable > 0) { + [self suspendReceive6Source]; + } + + return; + } + + if ((socket4FDBytesAvailable == 0) && (socket6FDBytesAvailable == 0)) + { + LogVerbose(@"No data available to receive..."); + + if (socket4FDBytesAvailable == 0) { + [self resumeReceive4Source]; + } + if (socket6FDBytesAvailable == 0) { + [self resumeReceive6Source]; + } + + return; + } + + // Figure out if we should receive on socket4 or socket6 + + BOOL doReceive4; + + if (flags & kDidConnect) + { + // Connected socket + + doReceive4 = (socket4FD != SOCKET_NULL); + } + else + { + // Non-Connected socket + + if (socket4FDBytesAvailable > 0) + { + if (socket6FDBytesAvailable > 0) + { + // Bytes available on socket4 & socket6 + + doReceive4 = (flags & kFlipFlop) ? YES : NO; + + flags ^= kFlipFlop; // flags = flags xor kFlipFlop; (toggle flip flop bit) + } + else { + // Bytes available on socket4, but not socket6 + doReceive4 = YES; + } + } + else { + // Bytes available on socket6, but not socket4 + doReceive4 = NO; + } + } + + // Perform socket IO + + ssize_t result = 0; + + NSData *data = nil; + NSData *addr4 = nil; + NSData *addr6 = nil; + + if (doReceive4) + { + NSAssert(socket4FDBytesAvailable > 0, @"Invalid logic"); + LogVerbose(@"Receiving on IPv4"); + + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); + + // #222: GCD does not necessarily return the size of an entire UDP packet + // from dispatch_source_get_data(), so we must use the maximum packet size. + size_t bufSize = max4ReceiveSize; + void *buf = malloc(bufSize); + + result = recvfrom(socket4FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len); + LogVerbose(@"recvfrom(socket4FD) = %i", (int)result); + + if (result > 0) + { + if ((size_t)result >= socket4FDBytesAvailable) + socket4FDBytesAvailable = 0; + else + socket4FDBytesAvailable -= result; + + if ((size_t)result != bufSize) { + buf = realloc(buf, result); + } + + data = [NSData dataWithBytesNoCopy:buf length:result freeWhenDone:YES]; + addr4 = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; + } + else + { + LogVerbose(@"recvfrom(socket4FD) = %@", [self errnoError]); + socket4FDBytesAvailable = 0; + free(buf); + } + } + else + { + NSAssert(socket6FDBytesAvailable > 0, @"Invalid logic"); + LogVerbose(@"Receiving on IPv6"); + + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); + + // #222: GCD does not necessarily return the size of an entire UDP packet + // from dispatch_source_get_data(), so we must use the maximum packet size. + size_t bufSize = max6ReceiveSize; + void *buf = malloc(bufSize); + + result = recvfrom(socket6FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len); + LogVerbose(@"recvfrom(socket6FD) -> %i", (int)result); + + if (result > 0) + { + if ((size_t)result >= socket6FDBytesAvailable) + socket6FDBytesAvailable = 0; + else + socket6FDBytesAvailable -= result; + + if ((size_t)result != bufSize) { + buf = realloc(buf, result); + } + + data = [NSData dataWithBytesNoCopy:buf length:result freeWhenDone:YES]; + addr6 = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; + } + else + { + LogVerbose(@"recvfrom(socket6FD) = %@", [self errnoError]); + socket6FDBytesAvailable = 0; + free(buf); + } + } + + + BOOL waitingForSocket = NO; + BOOL notifiedDelegate = NO; + BOOL ignored = NO; + + NSError *socketError = nil; + + if (result == 0) + { + waitingForSocket = YES; + } + else if (result < 0) + { + if (errno == EAGAIN) + waitingForSocket = YES; + else + socketError = [self errnoErrorWithReason:@"Error in recvfrom() function"]; + } + else + { + if (flags & kDidConnect) + { + if (addr4 && ![self isConnectedToAddress4:addr4]) + ignored = YES; + if (addr6 && ![self isConnectedToAddress6:addr6]) + ignored = YES; + } + + NSData *addr = (addr4 != nil) ? addr4 : addr6; + + if (!ignored) + { + if (receiveFilterBlock && receiveFilterQueue) + { + // Run data through filter, and if approved, notify delegate + + __block id filterContext = nil; + __block BOOL allowed = NO; + + if (receiveFilterAsync) + { + pendingFilterOperations++; + dispatch_async(receiveFilterQueue, ^{ @autoreleasepool { + + allowed = self->receiveFilterBlock(data, addr, &filterContext); + + // Transition back to socketQueue to get the current delegate / delegateQueue + dispatch_async(self->socketQueue, ^{ @autoreleasepool { + + self->pendingFilterOperations--; + + if (allowed) + { + [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; + } + else + { + LogVerbose(@"received packet silently dropped by receiveFilter"); + } + + if (self->flags & kReceiveOnce) + { + if (allowed) + { + // The delegate has been notified, + // so our receive once operation has completed. + self->flags &= ~kReceiveOnce; + } + else if (self->pendingFilterOperations == 0) + { + // All pending filter operations have completed, + // and none were allowed through. + // Our receive once operation hasn't completed yet. + [self doReceive]; + } + } + }}); + }}); + } + else // if (!receiveFilterAsync) + { + dispatch_sync(receiveFilterQueue, ^{ @autoreleasepool { + + allowed = self->receiveFilterBlock(data, addr, &filterContext); + }}); + + if (allowed) + { + [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; + notifiedDelegate = YES; + } + else + { + LogVerbose(@"received packet silently dropped by receiveFilter"); + ignored = YES; + } + } + } + else // if (!receiveFilterBlock || !receiveFilterQueue) + { + [self notifyDidReceiveData:data fromAddress:addr withFilterContext:nil]; + notifiedDelegate = YES; + } + } + } + + if (waitingForSocket) + { + // Wait for a notification of available data. + + if (socket4FDBytesAvailable == 0) { + [self resumeReceive4Source]; + } + if (socket6FDBytesAvailable == 0) { + [self resumeReceive6Source]; + } + } + else if (socketError) + { + [self closeWithError:socketError]; + } + else + { + if (flags & kReceiveContinuous) + { + // Continuous receive mode + [self doReceive]; + } + else + { + // One-at-a-time receive mode + if (notifiedDelegate) + { + // The delegate has been notified (no set filter). + // So our receive once operation has completed. + flags &= ~kReceiveOnce; + } + else if (ignored) + { + [self doReceive]; + } + else + { + // Waiting on asynchronous receive filter... + } + } + } +} + +- (void)doReceiveEOF +{ + LogTrace(); + + [self closeWithError:[self socketClosedError]]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Closing +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)closeWithError:(NSError *)error +{ + LogVerbose(@"closeWithError: %@", error); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (currentSend) [self endCurrentSend]; + + [sendQueue removeAllObjects]; + + // If a socket has been created, we should notify the delegate. + BOOL shouldCallDelegate = (flags & kDidCreateSockets) ? YES : NO; + + // Close all sockets, send/receive sources, cfstreams, etc +#if TARGET_OS_IPHONE + [self removeStreamsFromRunLoop]; + [self closeReadAndWriteStreams]; +#endif + [self closeSockets]; + + // Clear all flags (config remains as is) + flags = 0; + + if (shouldCallDelegate) + { + [self notifyDidCloseWithError:error]; + } +} + +- (void)close +{ + LogTrace(); + + dispatch_block_t block = ^{ @autoreleasepool { + + [self closeWithError:nil]; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); +} + +- (void)closeAfterSending +{ + LogTrace(); + + dispatch_block_t block = ^{ @autoreleasepool { + + self->flags |= kCloseAfterSends; + + if (self->currentSend == nil && [self->sendQueue count] == 0) + { + [self closeWithError:nil]; + } + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark CFStream +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if TARGET_OS_IPHONE + +static NSThread *listenerThread; + ++ (void)ignore:(id)_ +{} + ++ (void)startListenerThreadIfNeeded +{ + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + + listenerThread = [[NSThread alloc] initWithTarget:self + selector:@selector(listenerThread:) + object:nil]; + [listenerThread start]; + }); +} + ++ (void)listenerThread:(id)unused +{ + @autoreleasepool { + + [[NSThread currentThread] setName:GCDAsyncUdpSocketThreadName]; + + LogInfo(@"ListenerThread: Started"); + + // We can't run the run loop unless it has an associated input source or a timer. + // So we'll just create a timer that will never fire - unless the server runs for a decades. + [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow] + target:self + selector:@selector(ignore:) + userInfo:nil + repeats:YES]; + + [[NSRunLoop currentRunLoop] run]; + + LogInfo(@"ListenerThread: Stopped"); + } +} + ++ (void)addStreamListener:(GCDAsyncUdpSocket *)asyncUdpSocket +{ + LogTrace(); + NSAssert([NSThread currentThread] == listenerThread, @"Invoked on wrong thread"); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (asyncUdpSocket->readStream4) + CFReadStreamScheduleWithRunLoop(asyncUdpSocket->readStream4, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->readStream6) + CFReadStreamScheduleWithRunLoop(asyncUdpSocket->readStream6, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->writeStream4) + CFWriteStreamScheduleWithRunLoop(asyncUdpSocket->writeStream4, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->writeStream6) + CFWriteStreamScheduleWithRunLoop(asyncUdpSocket->writeStream6, runLoop, kCFRunLoopDefaultMode); +} + ++ (void)removeStreamListener:(GCDAsyncUdpSocket *)asyncUdpSocket +{ + LogTrace(); + NSAssert([NSThread currentThread] == listenerThread, @"Invoked on wrong thread"); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (asyncUdpSocket->readStream4) + CFReadStreamUnscheduleFromRunLoop(asyncUdpSocket->readStream4, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->readStream6) + CFReadStreamUnscheduleFromRunLoop(asyncUdpSocket->readStream6, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->writeStream4) + CFWriteStreamUnscheduleFromRunLoop(asyncUdpSocket->writeStream4, runLoop, kCFRunLoopDefaultMode); + + if (asyncUdpSocket->writeStream6) + CFWriteStreamUnscheduleFromRunLoop(asyncUdpSocket->writeStream6, runLoop, kCFRunLoopDefaultMode); +} + +static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo) +{ + @autoreleasepool { + GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; + + switch(type) + { + case kCFStreamEventOpenCompleted: + { + LogCVerbose(@"CFReadStreamCallback - Open"); + break; + } + case kCFStreamEventHasBytesAvailable: + { + LogCVerbose(@"CFReadStreamCallback - HasBytesAvailable"); + break; + } + case kCFStreamEventErrorOccurred: + case kCFStreamEventEndEncountered: + { + NSError *error = (__bridge_transfer NSError *)CFReadStreamCopyError(stream); + if (error == nil && type == kCFStreamEventEndEncountered) + { + error = [asyncUdpSocket socketClosedError]; + } + + dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFReadStreamCallback - %@", + (type == kCFStreamEventErrorOccurred) ? @"Error" : @"EndEncountered"); + + if (stream != asyncUdpSocket->readStream4 && + stream != asyncUdpSocket->readStream6 ) + { + LogCVerbose(@"CFReadStreamCallback - Ignored"); + return_from_block; + } + + [asyncUdpSocket closeWithError:error]; + + }}); + + break; + } + default: + { + LogCError(@"CFReadStreamCallback - UnknownType: %i", (int)type); + } + } + } +} + +static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) +{ + @autoreleasepool { + GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; + + switch(type) + { + case kCFStreamEventOpenCompleted: + { + LogCVerbose(@"CFWriteStreamCallback - Open"); + break; + } + case kCFStreamEventCanAcceptBytes: + { + LogCVerbose(@"CFWriteStreamCallback - CanAcceptBytes"); + break; + } + case kCFStreamEventErrorOccurred: + case kCFStreamEventEndEncountered: + { + NSError *error = (__bridge_transfer NSError *)CFWriteStreamCopyError(stream); + if (error == nil && type == kCFStreamEventEndEncountered) + { + error = [asyncUdpSocket socketClosedError]; + } + + dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { + + LogCVerbose(@"CFWriteStreamCallback - %@", + (type == kCFStreamEventErrorOccurred) ? @"Error" : @"EndEncountered"); + + if (stream != asyncUdpSocket->writeStream4 && + stream != asyncUdpSocket->writeStream6 ) + { + LogCVerbose(@"CFWriteStreamCallback - Ignored"); + return_from_block; + } + + [asyncUdpSocket closeWithError:error]; + + }}); + + break; + } + default: + { + LogCError(@"CFWriteStreamCallback - UnknownType: %i", (int)type); + } + } + } +} + +- (BOOL)createReadAndWriteStreams:(NSError **)errPtr +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + NSError *err = nil; + + if (readStream4 || writeStream4 || readStream6 || writeStream6) + { + // Streams already created + return YES; + } + + if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL) + { + err = [self otherError:@"Cannot create streams without a file descriptor"]; + goto Failed; + } + + // Create streams + + LogVerbose(@"Creating read and write stream(s)..."); + + if (socket4FD != SOCKET_NULL) + { + CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socket4FD, &readStream4, &writeStream4); + if (!readStream4 || !writeStream4) + { + err = [self otherError:@"Error in CFStreamCreatePairWithSocket() [IPv4]"]; + goto Failed; + } + } + + if (socket6FD != SOCKET_NULL) + { + CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socket6FD, &readStream6, &writeStream6); + if (!readStream6 || !writeStream6) + { + err = [self otherError:@"Error in CFStreamCreatePairWithSocket() [IPv6]"]; + goto Failed; + } + } + + // Ensure the CFStream's don't close our underlying socket + + CFReadStreamSetProperty(readStream4, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + CFWriteStreamSetProperty(writeStream4, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + + CFReadStreamSetProperty(readStream6, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + CFWriteStreamSetProperty(writeStream6, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + + return YES; + +Failed: + if (readStream4) + { + CFReadStreamClose(readStream4); + CFRelease(readStream4); + readStream4 = NULL; + } + if (writeStream4) + { + CFWriteStreamClose(writeStream4); + CFRelease(writeStream4); + writeStream4 = NULL; + } + if (readStream6) + { + CFReadStreamClose(readStream6); + CFRelease(readStream6); + readStream6 = NULL; + } + if (writeStream6) + { + CFWriteStreamClose(writeStream6); + CFRelease(writeStream6); + writeStream6 = NULL; + } + + if (errPtr) + *errPtr = err; + + return NO; +} + +- (BOOL)registerForStreamCallbacks:(NSError **)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); + + NSError *err = nil; + + streamContext.version = 0; + streamContext.info = (__bridge void *)self; + streamContext.retain = nil; + streamContext.release = nil; + streamContext.copyDescription = nil; + + CFOptionFlags readStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; + CFOptionFlags writeStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; + +// readStreamEvents |= (kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable); +// writeStreamEvents |= (kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes); + + if (socket4FD != SOCKET_NULL) + { + if (readStream4 == NULL || writeStream4 == NULL) + { + err = [self otherError:@"Read/Write stream4 is null"]; + goto Failed; + } + + BOOL r1 = CFReadStreamSetClient(readStream4, readStreamEvents, &CFReadStreamCallback, &streamContext); + BOOL r2 = CFWriteStreamSetClient(writeStream4, writeStreamEvents, &CFWriteStreamCallback, &streamContext); + + if (!r1 || !r2) + { + err = [self otherError:@"Error in CFStreamSetClient(), [IPv4]"]; + goto Failed; + } + } + + if (socket6FD != SOCKET_NULL) + { + if (readStream6 == NULL || writeStream6 == NULL) + { + err = [self otherError:@"Read/Write stream6 is null"]; + goto Failed; + } + + BOOL r1 = CFReadStreamSetClient(readStream6, readStreamEvents, &CFReadStreamCallback, &streamContext); + BOOL r2 = CFWriteStreamSetClient(writeStream6, writeStreamEvents, &CFWriteStreamCallback, &streamContext); + + if (!r1 || !r2) + { + err = [self otherError:@"Error in CFStreamSetClient() [IPv6]"]; + goto Failed; + } + } + + return YES; + +Failed: + if (readStream4) { + CFReadStreamSetClient(readStream4, kCFStreamEventNone, NULL, NULL); + } + if (writeStream4) { + CFWriteStreamSetClient(writeStream4, kCFStreamEventNone, NULL, NULL); + } + if (readStream6) { + CFReadStreamSetClient(readStream6, kCFStreamEventNone, NULL, NULL); + } + if (writeStream6) { + CFWriteStreamSetClient(writeStream6, kCFStreamEventNone, NULL, NULL); + } + + if (errPtr) *errPtr = err; + return NO; +} + +- (BOOL)addStreamsToRunLoop:(NSError **)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); + + if (!(flags & kAddedStreamListener)) + { + [[self class] startListenerThreadIfNeeded]; + [[self class] performSelector:@selector(addStreamListener:) + onThread:listenerThread + withObject:self + waitUntilDone:YES]; + + flags |= kAddedStreamListener; + } + + return YES; +} + +- (BOOL)openStreams:(NSError **)errPtr +{ + LogTrace(); + + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); + + NSError *err = nil; + + if (socket4FD != SOCKET_NULL) + { + BOOL r1 = CFReadStreamOpen(readStream4); + BOOL r2 = CFWriteStreamOpen(writeStream4); + + if (!r1 || !r2) + { + err = [self otherError:@"Error in CFStreamOpen() [IPv4]"]; + goto Failed; + } + } + + if (socket6FD != SOCKET_NULL) + { + BOOL r1 = CFReadStreamOpen(readStream6); + BOOL r2 = CFWriteStreamOpen(writeStream6); + + if (!r1 || !r2) + { + err = [self otherError:@"Error in CFStreamOpen() [IPv6]"]; + goto Failed; + } + } + + return YES; + +Failed: + if (errPtr) *errPtr = err; + return NO; +} + +- (void)removeStreamsFromRunLoop +{ + LogTrace(); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); + + if (flags & kAddedStreamListener) + { + [[self class] performSelector:@selector(removeStreamListener:) + onThread:listenerThread + withObject:self + waitUntilDone:YES]; + + flags &= ~kAddedStreamListener; + } +} + +- (void)closeReadAndWriteStreams +{ + LogTrace(); + + if (readStream4) + { + CFReadStreamSetClient(readStream4, kCFStreamEventNone, NULL, NULL); + CFReadStreamClose(readStream4); + CFRelease(readStream4); + readStream4 = NULL; + } + if (writeStream4) + { + CFWriteStreamSetClient(writeStream4, kCFStreamEventNone, NULL, NULL); + CFWriteStreamClose(writeStream4); + CFRelease(writeStream4); + writeStream4 = NULL; + } + if (readStream6) + { + CFReadStreamSetClient(readStream6, kCFStreamEventNone, NULL, NULL); + CFReadStreamClose(readStream6); + CFRelease(readStream6); + readStream6 = NULL; + } + if (writeStream6) + { + CFWriteStreamSetClient(writeStream6, kCFStreamEventNone, NULL, NULL); + CFWriteStreamClose(writeStream6); + CFRelease(writeStream6); + writeStream6 = NULL; + } +} + +#endif + +#if TARGET_OS_IPHONE +- (void)applicationWillEnterForeground:(NSNotification *)notification +{ + LogTrace(); + + // If the application was backgrounded, then iOS may have shut down our sockets. + // So we take a quick look to see if any of them received an EOF. + + dispatch_block_t block = ^{ @autoreleasepool { + + [self resumeReceive4Source]; + [self resumeReceive6Source]; + }}; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Advanced +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * See header file for big discussion of this method. + **/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketNewTargetQueue +{ + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketNewTargetQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); +} + +/** + * See header file for big discussion of this method. + **/ +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketOldTargetQueue +{ + dispatch_queue_set_specific(socketOldTargetQueue, IsOnSocketQueueOrTargetQueueKey, NULL, NULL); +} + +- (void)performBlock:(dispatch_block_t)block +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); +} + +- (int)socketFD +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return SOCKET_NULL; + } + + if (socket4FD != SOCKET_NULL) + return socket4FD; + else + return socket6FD; +} + +- (int)socket4FD +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return SOCKET_NULL; + } + + return socket4FD; +} + +- (int)socket6FD +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return SOCKET_NULL; + } + + return socket6FD; +} + +#if TARGET_OS_IPHONE + +- (CFReadStreamRef)readStream +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return NULL; + } + + NSError *err = nil; + if (![self createReadAndWriteStreams:&err]) + { + LogError(@"Error creating CFStream(s): %@", err); + return NULL; + } + + // Todo... + + if (readStream4) + return readStream4; + else + return readStream6; +} + +- (CFWriteStreamRef)writeStream +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return NULL; + } + + NSError *err = nil; + if (![self createReadAndWriteStreams:&err]) + { + LogError(@"Error creating CFStream(s): %@", err); + return NULL; + } + + if (writeStream4) + return writeStream4; + else + return writeStream6; +} + +- (BOOL)enableBackgroundingOnSockets +{ + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", + THIS_FILE, THIS_METHOD); + return NO; + } + + // Why is this commented out? + // See comments below. + +// NSError *err = nil; +// if (![self createReadAndWriteStreams:&err]) +// { +// LogError(@"Error creating CFStream(s): %@", err); +// return NO; +// } +// +// LogVerbose(@"Enabling backgrouding on socket"); +// +// BOOL r1, r2; +// +// if (readStream4 && writeStream4) +// { +// r1 = CFReadStreamSetProperty(readStream4, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); +// r2 = CFWriteStreamSetProperty(writeStream4, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); +// +// if (!r1 || !r2) +// { +// LogError(@"Error setting voip type (IPv4)"); +// return NO; +// } +// } +// +// if (readStream6 && writeStream6) +// { +// r1 = CFReadStreamSetProperty(readStream6, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); +// r2 = CFWriteStreamSetProperty(writeStream6, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); +// +// if (!r1 || !r2) +// { +// LogError(@"Error setting voip type (IPv6)"); +// return NO; +// } +// } +// +// return YES; + + // The above code will actually appear to work. + // The methods will return YES, and everything will appear fine. + // + // One tiny problem: the sockets will still get closed when the app gets backgrounded. + // + // Apple does not officially support backgrounding UDP sockets. + + return NO; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Class Methods +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 +{ + char addrBuf[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) + { + addrBuf[0] = '\0'; + } + + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; +} + ++ (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 +{ + char addrBuf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) + { + addrBuf[0] = '\0'; + } + + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; +} + ++ (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 +{ + return ntohs(pSockaddr4->sin_port); +} + ++ (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 +{ + return ntohs(pSockaddr6->sin6_port); +} + ++ (NSString *)hostFromAddress:(NSData *)address +{ + NSString *host = nil; + [self getHost:&host port:NULL family:NULL fromAddress:address]; + + return host; +} + ++ (uint16_t)portFromAddress:(NSData *)address +{ + uint16_t port = 0; + [self getHost:NULL port:&port family:NULL fromAddress:address]; + + return port; +} + ++ (int)familyFromAddress:(NSData *)address +{ + int af = AF_UNSPEC; + [self getHost:NULL port:NULL family:&af fromAddress:address]; + + return af; +} + ++ (BOOL)isIPv4Address:(NSData *)address +{ + int af = AF_UNSPEC; + [self getHost:NULL port:NULL family:&af fromAddress:address]; + + return (af == AF_INET); +} + ++ (BOOL)isIPv6Address:(NSData *)address +{ + int af = AF_UNSPEC; + [self getHost:NULL port:NULL family:&af fromAddress:address]; + + return (af == AF_INET6); +} + ++ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address +{ + return [self getHost:hostPtr port:portPtr family:NULL fromAddress:address]; +} + ++ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPtr fromAddress:(NSData *)address +{ + if ([address length] >= sizeof(struct sockaddr)) + { + const struct sockaddr *addrX = (const struct sockaddr *)[address bytes]; + + if (addrX->sa_family == AF_INET) + { + if ([address length] >= sizeof(struct sockaddr_in)) + { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)(const void *)addrX; + + if (hostPtr) *hostPtr = [self hostFromSockaddr4:addr4]; + if (portPtr) *portPtr = [self portFromSockaddr4:addr4]; + if (afPtr) *afPtr = AF_INET; + + return YES; + } + } + else if (addrX->sa_family == AF_INET6) + { + if ([address length] >= sizeof(struct sockaddr_in6)) + { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)(const void *)addrX; + + if (hostPtr) *hostPtr = [self hostFromSockaddr6:addr6]; + if (portPtr) *portPtr = [self portFromSockaddr6:addr6]; + if (afPtr) *afPtr = AF_INET6; + + return YES; + } + } + } + + if (hostPtr) *hostPtr = nil; + if (portPtr) *portPtr = 0; + if (afPtr) *afPtr = AF_UNSPEC; + + return NO; +} + +@end diff --git a/ios/Pods/CocoaLibEvent/LICENSE b/ios/Pods/CocoaLibEvent/LICENSE new file mode 100644 index 000000000..05a368e54 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 99789999@qq.com <99789999@qq.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ios/Pods/CocoaLibEvent/README.md b/ios/Pods/CocoaLibEvent/README.md new file mode 100644 index 000000000..9dcef3c7b --- /dev/null +++ b/ios/Pods/CocoaLibEvent/README.md @@ -0,0 +1,29 @@ +# CocoaLibEvent + +[![CI Status](http://img.shields.io/travis/99789999@qq.com/CocoaLibEvent.svg?style=flat)](https://travis-ci.org/99789999@qq.com/CocoaLibEvent) +[![Version](https://img.shields.io/cocoapods/v/CocoaLibEvent.svg?style=flat)](http://cocoapods.org/pods/CocoaLibEvent) +[![License](https://img.shields.io/cocoapods/l/CocoaLibEvent.svg?style=flat)](http://cocoapods.org/pods/CocoaLibEvent) +[![Platform](https://img.shields.io/cocoapods/p/CocoaLibEvent.svg?style=flat)](http://cocoapods.org/pods/CocoaLibEvent) + +## Example + +To run the example project, clone the repo, and run `pod install` from the Example directory first. + +## Requirements + +## Installation + +CocoaLibEvent is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod 'CocoaLibEvent' +``` + +## Author + +99789999@qq.com, 99789999@qq.com + +## License + +CocoaLibEvent is available under the MIT license. See the LICENSE file for more info. diff --git a/ios/Pods/CocoaLibEvent/lib/libevent.a b/ios/Pods/CocoaLibEvent/lib/libevent.a new file mode 100644 index 000000000..ac0b23c4e Binary files /dev/null and b/ios/Pods/CocoaLibEvent/lib/libevent.a differ diff --git a/ios/Pods/CocoaLibEvent/lib/libevent_core.a b/ios/Pods/CocoaLibEvent/lib/libevent_core.a new file mode 100644 index 000000000..37b604976 Binary files /dev/null and b/ios/Pods/CocoaLibEvent/lib/libevent_core.a differ diff --git a/ios/Pods/CocoaLibEvent/lib/libevent_extra.a b/ios/Pods/CocoaLibEvent/lib/libevent_extra.a new file mode 100644 index 000000000..ae18d856e Binary files /dev/null and b/ios/Pods/CocoaLibEvent/lib/libevent_extra.a differ diff --git a/ios/Pods/CocoaLibEvent/lib/libevent_pthreads.a b/ios/Pods/CocoaLibEvent/lib/libevent_pthreads.a new file mode 100644 index 000000000..fcae48d3f Binary files /dev/null and b/ios/Pods/CocoaLibEvent/lib/libevent_pthreads.a differ diff --git a/ios/Pods/CocoaLibEvent/src/evdns.h b/ios/Pods/CocoaLibEvent/src/evdns.h new file mode 100644 index 000000000..8672db036 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/evdns.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT1_EVDNS_H_INCLUDED_ +#define EVENT1_EVDNS_H_INCLUDED_ + +/** @file evdns.h + + A dns subsystem for Libevent. + + The <evdns.h> header is deprecated in Libevent 2.0 and later; please + use <event2/evdns.h> instead. Depending on what functionality you + need, you may also want to include more of the other <event2/...> + headers. + */ + +#include <event.h> +#include <event2/dns.h> +#include <event2/dns_compat.h> +#include <event2/dns_struct.h> + +#endif /* EVENT1_EVDNS_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event.h b/ios/Pods/CocoaLibEvent/src/event.h new file mode 100644 index 000000000..ba5186713 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT1_EVENT_H_INCLUDED_ +#define EVENT1_EVENT_H_INCLUDED_ + +/** @file event.h + + A library for writing event-driven network servers. + + The <event.h> header is deprecated in Libevent 2.0 and later; please + use <event2/event.h> instead. Depending on what functionality you + need, you may also want to include more of the other event2/ + headers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_STDINT_H +#include <stdint.h> +#endif +#include <stdarg.h> + +/* For int types. */ +#include <evutil.h> + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <winsock2.h> +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#endif + +#include <event2/event_struct.h> +#include <event2/event.h> +#include <event2/event_compat.h> +#include <event2/buffer.h> +#include <event2/buffer_compat.h> +#include <event2/bufferevent.h> +#include <event2/bufferevent_struct.h> +#include <event2/bufferevent_compat.h> +#include <event2/tag.h> +#include <event2/tag_compat.h> + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT1_EVENT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/buffer.h b/ios/Pods/CocoaLibEvent/src/event2/buffer.h new file mode 100644 index 000000000..468588b9f --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/buffer.h @@ -0,0 +1,1076 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFER_H_INCLUDED_ +#define EVENT2_BUFFER_H_INCLUDED_ + +/** @file event2/buffer.h + + Functions for buffering data for network sending or receiving. + + An evbuffer can be used for preparing data before sending it to + the network or conversely for reading data from the network. + Evbuffers try to avoid memory copies as much as possible. As a + result, evbuffers can be used to pass data around without actually + incurring the overhead of copying the data. + + A new evbuffer can be allocated with evbuffer_new(), and can be + freed with evbuffer_free(). Most users will be using evbuffers via + the bufferevent interface. To access a bufferevent's evbuffers, use + bufferevent_get_input() and bufferevent_get_output(). + + There are several guidelines for using evbuffers. + + - if you already know how much data you are going to add as a result + of calling evbuffer_add() multiple times, it makes sense to use + evbuffer_expand() first to make sure that enough memory is allocated + before hand. + + - evbuffer_add_buffer() adds the contents of one buffer to the other + without incurring any unnecessary memory copies. + + - evbuffer_add() and evbuffer_add_buffer() do not mix very well: + if you use them, you will wind up with fragmented memory in your + buffer. + + - For high-performance code, you may want to avoid copying data into and out + of buffers. You can skip the copy step by using + evbuffer_reserve_space()/evbuffer_commit_space() when writing into a + buffer, and evbuffer_peek() when reading. + + In Libevent 2.0 and later, evbuffers are represented using a linked + list of memory chunks, with pointers to the first and last chunk in + the chain. + + As the contents of an evbuffer can be stored in multiple different + memory blocks, it cannot be accessed directly. Instead, evbuffer_pullup() + can be used to force a specified number of bytes to be contiguous. This + will cause memory reallocation and memory copies if the data is split + across multiple blocks. It is more efficient, however, to use + evbuffer_peek() if you don't require that the memory to be contiguous. + */ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#include <stdarg.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif +#include <event2/util.h> + +/** + An evbuffer is an opaque data type for efficiently buffering data to be + sent or received on the network. + + @see event2/event.h for more information +*/ +struct evbuffer +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; + +/** + Pointer to a position within an evbuffer. + + Used when repeatedly searching through a buffer. Calling any function + that modifies or re-packs the buffer contents may invalidate all + evbuffer_ptrs for that buffer. Do not modify or contruct these values + except with evbuffer_ptr_set. + + An evbuffer_ptr can represent any position from the start of a buffer up + to a position immediately after the end of a buffer. + + @see evbuffer_ptr_set() + */ +struct evbuffer_ptr { + ev_ssize_t pos; + + /* Do not alter or rely on the values of fields: they are for internal + * use */ + struct { + void *chain; + size_t pos_in_chain; + } internal_; +}; + +/** Describes a single extent of memory inside an evbuffer. Used for + direct-access functions. + + @see evbuffer_reserve_space, evbuffer_commit_space, evbuffer_peek + */ +#ifdef EVENT__HAVE_SYS_UIO_H +#define evbuffer_iovec iovec +/* Internal use -- defined only if we are using the native struct iovec */ +#define EVBUFFER_IOVEC_IS_NATIVE_ +#else +struct evbuffer_iovec { + /** The start of the extent of memory. */ + void *iov_base; + /** The length of the extent of memory. */ + size_t iov_len; +}; +#endif + +/** + Allocate storage for a new evbuffer. + + @return a pointer to a newly allocated evbuffer struct, or NULL if an error + occurred + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evbuffer_new(void); +/** + Deallocate storage for an evbuffer. + + @param buf pointer to the evbuffer to be freed + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_free(struct evbuffer *buf); + +/** + Enable locking on an evbuffer so that it can safely be used by multiple + threads at the same time. + + NOTE: when locking is enabled, the lock will be held when callbacks are + invoked. This could result in deadlock if you aren't careful. Plan + accordingly! + + @param buf An evbuffer to make lockable. + @param lock A lock object, or NULL if we should allocate our own. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_enable_locking(struct evbuffer *buf, void *lock); + +/** + Acquire the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_lock(struct evbuffer *buf); + +/** + Release the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_unlock(struct evbuffer *buf); + + +/** If this flag is set, then we will not use evbuffer_peek(), + * evbuffer_remove(), evbuffer_remove_buffer(), and so on to read bytes + * from this buffer: we'll only take bytes out of this buffer by + * writing them to the network (as with evbuffer_write_atmost), by + * removing them without observing them (as with evbuffer_drain), + * or by copying them all out at once (as with evbuffer_add_buffer). + * + * Using this option allows the implementation to use sendfile-based + * operations for evbuffer_add_file(); see that function for more + * information. + * + * This flag is on by default for bufferevents that can take advantage + * of it; you should never actually need to set it on a bufferevent's + * output buffer. + */ +#define EVBUFFER_FLAG_DRAINS_TO_FD 1 + +/** Change the flags that are set for an evbuffer by adding more. + * + * @param buffer the evbuffer that the callback is watching. + * @param cb the callback whose status we want to change. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags); +/** Change the flags that are set for an evbuffer by removing some. + * + * @param buffer the evbuffer that the callback is watching. + * @param cb the callback whose status we want to change. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags); + +/** + Returns the total number of bytes stored in the evbuffer + + @param buf pointer to the evbuffer + @return the number of bytes stored in the evbuffer +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_length(const struct evbuffer *buf); + +/** + Returns the number of contiguous available bytes in the first buffer chain. + + This is useful when processing data that might be split into multiple + chains, or that might all be in the first chain. Calls to + evbuffer_pullup() that cause reallocation and copying of data can thus be + avoided. + + @param buf pointer to the evbuffer + @return 0 if no data is available, otherwise the number of available bytes + in the first buffer chain. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_contiguous_space(const struct evbuffer *buf); + +/** + Expands the available space in an evbuffer. + + Expands the available space in the evbuffer to at least datlen, so that + appending datlen additional bytes will not require any new allocations. + + @param buf the evbuffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_expand(struct evbuffer *buf, size_t datlen); + +/** + Reserves space in the last chain or chains of an evbuffer. + + Makes space available in the last chain or chains of an evbuffer that can + be arbitrarily written to by a user. The space does not become + available for reading until it has been committed with + evbuffer_commit_space(). + + The space is made available as one or more extents, represented by + an initial pointer and a length. You can force the memory to be + available as only one extent. Allowing more extents, however, makes the + function more efficient. + + Multiple subsequent calls to this function will make the same space + available until evbuffer_commit_space() has been called. + + It is an error to do anything that moves around the buffer's internal + memory structures before committing the space. + + NOTE: The code currently does not ever use more than two extents. + This may change in future versions. + + @param buf the evbuffer in which to reserve space. + @param size how much space to make available, at minimum. The + total length of the extents may be greater than the requested + length. + @param vec an array of one or more evbuffer_iovec structures to + hold pointers to the reserved extents of memory. + @param n_vec The length of the vec array. Must be at least 1; + 2 is more efficient. + @return the number of provided extents, or -1 on error. + @see evbuffer_commit_space() +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, + struct evbuffer_iovec *vec, int n_vec); + +/** + Commits previously reserved space. + + Commits some of the space previously reserved with + evbuffer_reserve_space(). It then becomes available for reading. + + This function may return an error if the pointer in the extents do + not match those returned from evbuffer_reserve_space, or if data + has been added to the buffer since the space was reserved. + + If you want to commit less data than you got reserved space for, + modify the iov_len pointer of the appropriate extent to a smaller + value. Note that you may have received more space than you + requested if it was available! + + @param buf the evbuffer in which to reserve space. + @param vec one or two extents returned by evbuffer_reserve_space. + @param n_vecs the number of extents. + @return 0 on success, -1 on error + @see evbuffer_reserve_space() +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_commit_space(struct evbuffer *buf, + struct evbuffer_iovec *vec, int n_vecs); + +/** + Append data to the end of an evbuffer. + + @param buf the evbuffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); + + +/** + Read data from an evbuffer and drain the bytes read. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); + +/** + Read data from an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen); + +/** + Read data from the middle of an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param pos the position to start reading from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen); + +/** + Read data from an evbuffer into another evbuffer, draining + the bytes from the source buffer. This function avoids copy + operations to the extent possible. + + If more bytes are requested than are available in src, the src + buffer is drained completely. + + @param src the evbuffer to be read from + @param dst the destination evbuffer to store the result into + @param datlen the maximum numbers of bytes to transfer + @return the number of bytes read + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, + size_t datlen); + +/** Used to tell evbuffer_readln what kind of line-ending to look for. + */ +enum evbuffer_eol_style { + /** Any sequence of CR and LF characters is acceptable as an + * EOL. + * + * Note that this style can produce ambiguous results: the + * sequence "CRLF" will be treated as a single EOL if it is + * all in the buffer at once, but if you first read a CR from + * the network and later read an LF from the network, it will + * be treated as two EOLs. + */ + EVBUFFER_EOL_ANY, + /** An EOL is an LF, optionally preceded by a CR. This style is + * most useful for implementing text-based internet protocols. */ + EVBUFFER_EOL_CRLF, + /** An EOL is a CR followed by an LF. */ + EVBUFFER_EOL_CRLF_STRICT, + /** An EOL is a LF. */ + EVBUFFER_EOL_LF, + /** An EOL is a NUL character (that is, a single byte with value 0) */ + EVBUFFER_EOL_NUL +}; + +/** + * Read a single line from an evbuffer. + * + * Reads a line terminated by an EOL as determined by the evbuffer_eol_style + * argument. Returns a newly allocated nul-terminated string; the caller must + * free the returned value. The EOL is not included in the returned string. + * + * @param buffer the evbuffer to read from + * @param n_read_out if non-NULL, points to a size_t that is set to the + * number of characters in the returned string. This is useful for + * strings that can contain NUL characters. + * @param eol_style the style of line-ending to use. + * @return pointer to a single line, or NULL if an error occurred + */ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style); + +/** + Move all data from one evbuffer into another evbuffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. However, no unnecessary memory copies occur. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + + @see evbuffer_remove_buffer() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf); + +/** + Copy data from one evbuffer into another evbuffer. + + This is a non-destructive add. The data from one buffer is copied + into the other buffer. However, no unnecessary memory copies occur. + + Note that buffers already containing buffer references can't be added + to other buffers. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer_reference(struct evbuffer *outbuf, + struct evbuffer *inbuf); + +/** + A cleanup function for a piece of memory added to an evbuffer by + reference. + + @see evbuffer_add_reference() + */ +typedef void (*evbuffer_ref_cleanup_cb)(const void *data, + size_t datalen, void *extra); + +/** + Reference memory into an evbuffer without copying. + + The memory needs to remain valid until all the added data has been + read. This function keeps just a reference to the memory without + actually incurring the overhead of a copy. + + @param outbuf the output buffer + @param data the memory to reference + @param datlen how memory to reference + @param cleanupfn callback to be invoked when the memory is no longer + referenced by this evbuffer. + @param cleanupfn_arg optional argument to the cleanup callback + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_reference(struct evbuffer *outbuf, + const void *data, size_t datlen, + evbuffer_ref_cleanup_cb cleanupfn, void *cleanupfn_arg); + +/** + Copy data from a file into the evbuffer for writing to a socket. + + This function avoids unnecessary data copies between userland and + kernel. If sendfile is available and the EVBUFFER_FLAG_DRAINS_TO_FD + flag is set, it uses those functions. Otherwise, it tries to use + mmap (or CreateFileMapping on Windows). + + The function owns the resulting file descriptor and will close it + when finished transferring data. + + The results of using evbuffer_remove() or evbuffer_pullup() on + evbuffers whose data was added using this function are undefined. + + For more fine-grained control, use evbuffer_add_file_segment. + + @param outbuf the output buffer + @param fd the file descriptor + @param offset the offset from which to read data + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @return 0 if successful, or -1 if an error occurred +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file(struct evbuffer *outbuf, int fd, ev_off_t offset, + ev_off_t length); + +/** + An evbuffer_file_segment holds a reference to a range of a file -- + possibly the whole file! -- for use in writing from an evbuffer to a + socket. It could be implemented with mmap, sendfile, splice, or (if all + else fails) by just pulling all the data into RAM. A single + evbuffer_file_segment can be added more than once, and to more than one + evbuffer. + */ +struct evbuffer_file_segment; + +/** + Flag for creating evbuffer_file_segment: If this flag is set, then when + the evbuffer_file_segment is freed and no longer in use by any + evbuffer, the underlying fd is closed. + */ +#define EVBUF_FS_CLOSE_ON_FREE 0x01 +/** + Flag for creating evbuffer_file_segment: Disable memory-map based + implementations. + */ +#define EVBUF_FS_DISABLE_MMAP 0x02 +/** + Flag for creating evbuffer_file_segment: Disable direct fd-to-fd + implementations (including sendfile and splice). + + You might want to use this option if data needs to be taken from the + evbuffer by any means other than writing it to the network: the sendfile + backend is fast, but it only works for sending files directly to the + network. + */ +#define EVBUF_FS_DISABLE_SENDFILE 0x04 +/** + Flag for creating evbuffer_file_segment: Do not allocate a lock for this + segment. If this option is set, then neither the segment nor any + evbuffer it is added to may ever be accessed from more than one thread + at a time. + */ +#define EVBUF_FS_DISABLE_LOCKING 0x08 + +/** + A cleanup function for a evbuffer_file_segment added to an evbuffer + for reference. + */ +typedef void (*evbuffer_file_segment_cleanup_cb)( + struct evbuffer_file_segment const* seg, int flags, void* arg); + +/** + Create and return a new evbuffer_file_segment for reading data from a + file and sending it out via an evbuffer. + + This function avoids unnecessary data copies between userland and + kernel. Where available, it uses sendfile or splice. + + The file descriptor must not be closed so long as any evbuffer is using + this segment. + + The results of using evbuffer_remove() or evbuffer_pullup() or any other + function that reads bytes from an evbuffer on any evbuffer containing + the newly returned segment are undefined, unless you pass the + EVBUF_FS_DISABLE_SENDFILE flag to this function. + + @param fd an open file to read from. + @param offset an index within the file at which to start reading + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @param flags any number of the EVBUF_FS_* flags + @return a new evbuffer_file_segment, or NULL on failure. + **/ +EVENT2_EXPORT_SYMBOL +struct evbuffer_file_segment *evbuffer_file_segment_new( + int fd, ev_off_t offset, ev_off_t length, unsigned flags); + +/** + Free an evbuffer_file_segment + + It is safe to call this function even if the segment has been added to + one or more evbuffers. The evbuffer_file_segment will not be freed + until no more references to it exist. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_free(struct evbuffer_file_segment *seg); + +/** + Add cleanup callback and argument for the callback to an + evbuffer_file_segment. + + The cleanup callback will be invoked when no more references to the + evbuffer_file_segment exist. + **/ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, + evbuffer_file_segment_cleanup_cb cb, void* arg); + +/** + Insert some or all of an evbuffer_file_segment at the end of an evbuffer + + Note that the offset and length parameters of this function have a + different meaning from those provided to evbuffer_file_segment_new: When + you create the segment, the offset is the offset _within the file_, and + the length is the length _of the segment_, whereas when you add a + segment to an evbuffer, the offset is _within the segment_ and the + length is the length of the _part of the segment you want to use. + + In other words, if you have a 10 KiB file, and you create an + evbuffer_file_segment for it with offset 20 and length 1000, it will + refer to bytes 20..1019 inclusive. If you then pass this segment to + evbuffer_add_file_segment and specify an offset of 20 and a length of + 50, you will be adding bytes 40..99 inclusive. + + @param buf the evbuffer to append to + @param seg the segment to add + @param offset the offset within the segment to start from + @param length the amount of data to add, or -1 to add it all. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file_segment(struct evbuffer *buf, + struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length); + +/** + Append a formatted string to the end of an evbuffer. + + The string is formated as printf. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + + @see evutil_printf(), evbuffer_add_vprintf() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + +/** + Append a va_list formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 0))) +#endif +; + + +/** + Remove a specified number of bytes data from the beginning of an evbuffer. + + @param buf the evbuffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_drain(struct evbuffer *buf, size_t len); + + +/** + Write the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); + +/** + Write some of the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @param howmuch the largest allowable number of bytes to write, or -1 + to write as many bytes as we can. + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, + ev_ssize_t howmuch); + +/** + Read from a file descriptor and store the result in an evbuffer. + + @param buffer the evbuffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read + @return the number of bytes read, or -1 if an error occurred + @see evbuffer_write() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch); + +/** + Search for a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start); + +/** + Search for a string within part of an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should start searching. + @param end NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should stop searching. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end); + +/** + Defines how to adjust an evbuffer_ptr by evbuffer_ptr_set() + + @see evbuffer_ptr_set() */ +enum evbuffer_ptr_how { + /** Sets the pointer to the position; can be called on with an + uninitialized evbuffer_ptr. */ + EVBUFFER_PTR_SET, + /** Advances the pointer by adding to the current position. */ + EVBUFFER_PTR_ADD +}; + +/** + Sets the search pointer in the buffer to position. + + There are two ways to use this function: you can call + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET) + to move 'pos' to a position 'N' bytes after the start of the buffer, or + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_ADD) + to move 'pos' forward by 'N' bytes. + + If evbuffer_ptr is not initialized, this function can only be called + with EVBUFFER_PTR_SET. + + An evbuffer_ptr can represent any position from the start of the buffer to + a position immediately after the end of the buffer. + + @param buffer the evbuffer to be search + @param ptr a pointer to a struct evbuffer_ptr + @param position the position at which to start the next search + @param how determines how the pointer should be manipulated. + @returns 0 on success or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *ptr, + size_t position, enum evbuffer_ptr_how how); + +/** + Search for an end-of-line string within an evbuffer. + + @param buffer the evbuffer to be searched + @param start NULL or a pointer to a valid struct evbuffer_ptr to start + searching at. + @param eol_len_out If non-NULL, the pointed-to value will be set to + the length of the end-of-line string. + @param eol_style The kind of EOL to look for; see evbuffer_readln() for + more information + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence EOL in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, + struct evbuffer_ptr *start, size_t *eol_len_out, + enum evbuffer_eol_style eol_style); + +/** Function to peek at data inside an evbuffer without removing it or + copying it out. + + Pointers to the data are returned by filling the 'vec_out' array + with pointers to one or more extents of data inside the buffer. + + The total data in the extents that you get back may be more than + you requested (if there is more data last extent than you asked + for), or less (if you do not provide enough evbuffer_iovecs, or if + the buffer does not have as much data as you asked to see). + + @param buffer the evbuffer to peek into, + @param len the number of bytes to try to peek. If len is negative, we + will try to fill as much of vec_out as we can. If len is negative + and vec_out is not provided, we return the number of evbuffer_iovecs + that would be needed to get all the data in the buffer. + @param start_at an evbuffer_ptr indicating the point at which we + should start looking for data. NULL means, "At the start of the + buffer." + @param vec_out an array of evbuffer_iovec + @param n_vec the length of vec_out. If 0, we only count how many + extents would be necessary to point to the requested amount of + data. + @return The number of extents needed. This may be less than n_vec + if we didn't need all the evbuffer_iovecs we were given, or more + than n_vec if we would need more to return all the data that was + requested. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, + struct evbuffer_ptr *start_at, + struct evbuffer_iovec *vec_out, int n_vec); + + +/** Structure passed to an evbuffer_cb_func evbuffer callback + + @see evbuffer_cb_func, evbuffer_add_cb() + */ +struct evbuffer_cb_info { + /** The number of bytes in this evbuffer when callbacks were last + * invoked. */ + size_t orig_size; + /** The number of bytes added since callbacks were last invoked. */ + size_t n_added; + /** The number of bytes removed since callbacks were last invoked. */ + size_t n_deleted; +}; + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param info a structure describing how the buffer changed. + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg); + +struct evbuffer_cb_entry; +/** Add a new callback to an evbuffer. + + Subsequent calls to evbuffer_add_cb() add new callbacks. To remove this + callback, call evbuffer_remove_cb or evbuffer_remove_cb_entry. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + @return a handle to the callback on success, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** Remove a callback from an evbuffer, given a handle returned from + evbuffer_add_cb. + + Calling this function invalidates the handle. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb_entry(struct evbuffer *buffer, + struct evbuffer_cb_entry *ent); + +/** Remove a callback from an evbuffer, given the function and argument + used to add it. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** If this flag is not set, then a callback is temporarily disabled, and + * should not be invoked. + * + * @see evbuffer_cb_set_flags(), evbuffer_cb_clear_flags() + */ +#define EVBUFFER_CB_ENABLED 1 + +/** Change the flags that are set for a callback on a buffer by adding more. + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to re-enable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_set_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +/** Change the flags that are set for a callback on a buffer by removing some + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to disable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_clear_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +#if 0 +/** Postpone calling a given callback until unsuspend is called later. + + This is different from disabling the callback, since the callback will get + invoked later if the buffer size changes between now and when we unsuspend + it. + + @param the buffer that the callback is watching. + @param cb the callback we want to suspend. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +/** Stop postponing a callback that we postponed with evbuffer_cb_suspend. + + If data was added to or removed from the buffer while the callback was + suspended, the callback will get called once now. + + @param the buffer that the callback is watching. + @param cb the callback we want to stop suspending. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +#endif + +/** + Makes the data at the beginning of an evbuffer contiguous. + + @param buf the evbuffer to make contiguous + @param size the number of bytes to make contiguous, or -1 to make the + entire buffer contiguous. + @return a pointer to the contiguous memory array, or NULL if param size + requested more data than is present in the buffer. +*/ + +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size); + +/** + Prepends data to the beginning of the evbuffer + + @param buf the evbuffer to which to prepend data + @param data a pointer to the memory to prepend + @param size the number of bytes to prepend + @return 0 if successful, or -1 otherwise +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); + +/** + Prepends all data from the src evbuffer to the beginning of the dst + evbuffer. + + @param dst the evbuffer to which to prepend data + @param src the evbuffer to prepend; it will be emptied as a result + @return 0 if successful, or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src); + +/** + Prevent calls that modify an evbuffer from succeeding. A buffer may + frozen at the front, at the back, or at both the front and the back. + + If the front of a buffer is frozen, operations that drain data from + the front of the buffer, or that prepend data to the buffer, will + fail until it is unfrozen. If the back a buffer is frozen, operations + that append data from the buffer will fail until it is unfrozen. + + @param buf The buffer to freeze + @param at_front If true, we freeze the front of the buffer. If false, + we freeze the back. + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_freeze(struct evbuffer *buf, int at_front); +/** + Re-enable calls that modify an evbuffer. + + @param buf The buffer to un-freeze + @param at_front If true, we unfreeze the front of the buffer. If false, + we unfreeze the back. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_unfreeze(struct evbuffer *buf, int at_front); + +struct event_base; +/** + Force all the callbacks on an evbuffer to be run, not immediately after + the evbuffer is altered, but instead from inside the event loop. + + This can be used to serialize all the callbacks to a single thread + of execution. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base); + +/** + Append data from 1 or more iovec's to an evbuffer + + Calculates the number of bytes needed for an iovec structure and guarantees + all data will fit into a single chain. Can be used in lieu of functionality + which calls evbuffer_add() constantly before being used to increase + performance. + + @param buffer the destination buffer + @param vec the source iovec + @param n_vec the number of iovec structures. + @return the number of bytes successfully written to the output buffer. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_add_iovec(struct evbuffer * buffer, struct evbuffer_iovec * vec, int n_vec); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFER_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/buffer_compat.h b/ios/Pods/CocoaLibEvent/src/event2/buffer_compat.h new file mode 100644 index 000000000..24f828c21 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/buffer_compat.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EVENT2_BUFFER_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFER_COMPAT_H_INCLUDED_ + +#include <event2/visibility.h> + +/** @file event2/buffer_compat.h + + Obsolete and deprecated versions of the functions in buffer.h: provided + only for backward compatibility. + */ + + +/** + Obsolete alias for evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY). + + @deprecated This function is deprecated because its behavior is not correct + for almost any protocol, and also because it's wholly subsumed by + evbuffer_readln(). + + @param buffer the evbuffer to read from + @return pointer to a single line, or NULL if an error occurred + +*/ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readline(struct evbuffer *buffer); + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param old_len the previous length of the buffer + @param new_len the current length of the buffer + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg); + +/** + Replace all callbacks on an evbuffer with a single new callback, or + remove them. + + Subsequent calls to evbuffer_setcb() replace callbacks set by previous + calls. Setting the callback to NULL removes any previously set callback. + + @deprecated This function is deprecated because it clears all previous + callbacks set on the evbuffer, which can cause confusing behavior if + multiple parts of the code all want to add their own callbacks on a + buffer. Instead, use evbuffer_add(), evbuffer_del(), and + evbuffer_setflags() to manage your own evbuffer callbacks without + interfering with callbacks set by others. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg); + + +/** + Find a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @return a pointer to the beginning of the search string, or NULL if the search failed. + */ +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len); + +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_LENGTH(x) evbuffer_get_length(x) +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_DATA(x) evbuffer_pullup((x), -1) + +#endif + diff --git a/ios/Pods/CocoaLibEvent/src/event2/bufferevent.h b/ios/Pods/CocoaLibEvent/src/event2/bufferevent.h new file mode 100644 index 000000000..825918e3a --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/bufferevent.h @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_H_INCLUDED_ + +/** + @file event2/bufferevent.h + + Functions for buffering data for network sending or receiving. Bufferevents + are higher level than evbuffers: each has an underlying evbuffer for reading + and one for writing, and callbacks that are invoked under certain + circumstances. + + A bufferevent provides input and output buffers that get filled and + drained automatically. The user of a bufferevent no longer deals + directly with the I/O, but instead is reading from input and writing + to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly + with bufferevent_enable() and bufferevent_disable(). + + When reading is enabled, the bufferevent will try to read from the + file descriptor onto its input buffer, and call the read callback. + When writing is enabled, the bufferevent will try to write data onto its + file descriptor when the output buffer has enough data, and call the write + callback when the output buffer is sufficiently drained. + + Bufferevents come in several flavors, including: + + <dl> + <dt>Socket-based bufferevents</dt> + <dd>A bufferevent that reads and writes data onto a network + socket. Created with bufferevent_socket_new().</dd> + + <dt>Paired bufferevents</dt> + <dd>A pair of bufferevents that send and receive data to one + another without touching the network. Created with + bufferevent_pair_new().</dd> + + <dt>Filtering bufferevents</dt> + <dd>A bufferevent that transforms data, and sends or receives it + over another underlying bufferevent. Created with + bufferevent_filter_new().</dd> + + <dt>SSL-backed bufferevents</dt> + <dd>A bufferevent that uses the openssl library to send and + receive data over an encrypted connection. Created with + bufferevent_openssl_socket_new() or + bufferevent_openssl_filter_new().</dd> + </dl> + */ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/** @name Bufferevent event codes + + These flags are passed as arguments to a bufferevent's event callback. + + @{ +*/ +#define BEV_EVENT_READING 0x01 /**< error encountered while reading */ +#define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */ +#define BEV_EVENT_EOF 0x10 /**< eof file reached */ +#define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */ +#define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */ +#define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */ +/**@}*/ + +/** + An opaque type for handling buffered IO + + @see event2/bufferevent.h + */ +struct bufferevent +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; +struct event_base; +struct evbuffer; +struct sockaddr; + +/** + A read or write callback for a bufferevent. + + The read callback is triggered when new data arrives in the input + buffer and the amount of readable data exceed the low watermark + which is 0 by default. + + The write callback is triggered if the write buffer has been + exhausted or fell below its low watermark. + + @param bev the bufferevent that triggered the callback + @param ctx the user-specified context for this bufferevent + */ +typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); + +/** + An event/error callback for a bufferevent. + + The event callback is triggered if either an EOF condition or another + unrecoverable error was encountered. + + For bufferevents with deferred callbacks, this is a bitwise OR of all errors + that have happened on the bufferevent since the last callback invocation. + + @param bev the bufferevent for which the error condition was reached + @param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING + to indicate if the error was encountered on the read or write path, + and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR, + BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED. + + @param ctx the user-specified context for this bufferevent +*/ +typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx); + +/** Options that can be specified when creating a bufferevent */ +enum bufferevent_options { + /** If set, we close the underlying file + * descriptor/bufferevent/whatever when this bufferevent is freed. */ + BEV_OPT_CLOSE_ON_FREE = (1<<0), + + /** If set, and threading is enabled, operations on this bufferevent + * are protected by a lock */ + BEV_OPT_THREADSAFE = (1<<1), + + /** If set, callbacks are run deferred in the event loop. */ + BEV_OPT_DEFER_CALLBACKS = (1<<2), + + /** If set, callbacks are executed without locks being held on the + * bufferevent. This option currently requires that + * BEV_OPT_DEFER_CALLBACKS also be set; a future version of Libevent + * might remove the requirement.*/ + BEV_OPT_UNLOCK_CALLBACKS = (1<<3) +}; + +/** + Create a new socket bufferevent over an existing socket. + + @param base the event base to associate with the new bufferevent. + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + It is safe to set the fd to -1, so long as you later + set it with bufferevent_setfd or bufferevent_socket_connect(). + @param options Zero or more BEV_OPT_* flags + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_free() + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options); + +/** + Launch a connect() attempt with a socket-based bufferevent. + + When the connect succeeds, the eventcb will be invoked with + BEV_EVENT_CONNECTED set. + + If the bufferevent does not already have a socket set, we allocate a new + socket here and make it nonblocking before we begin. + + If no address is provided, we assume that the socket is already connecting, + and configure the bufferevent so that a BEV_EVENT_CONNECTED event will be + yielded when it is done connecting. + + @param bufev an existing bufferevent allocated with + bufferevent_socket_new(). + @param addr the address we should connect to + @param socklen The length of the address + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect(struct bufferevent *, const struct sockaddr *, int); + +struct evdns_base; +/** + Resolve the hostname 'hostname' and connect to it as with + bufferevent_socket_connect(). + + @param bufev An existing bufferevent allocated with bufferevent_socket_new() + @param evdns_base Optionally, an evdns_base to use for resolving hostnames + asynchronously. May be set to NULL for a blocking resolve. + @param family A preferred address family to resolve addresses to, or + AF_UNSPEC for no preference. Only AF_INET, AF_INET6, and AF_UNSPEC are + supported. + @param hostname The hostname to resolve; see below for notes on recognized + formats + @param port The port to connect to on the resolved address. + @return 0 if successful, -1 on failure. + + Recognized hostname formats are: + + www.example.com (hostname) + 1.2.3.4 (ipv4address) + ::1 (ipv6address) + [::1] ([ipv6address]) + + Performance note: If you do not provide an evdns_base, this function + may block while it waits for a DNS response. This is probably not + what you want. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect_hostname(struct bufferevent *, + struct evdns_base *, int, const char *, int); + +/** + Return the error code for the last failed DNS lookup attempt made by + bufferevent_socket_connect_hostname(). + + @param bev The bufferevent object. + @return DNS error code. + @see evutil_gai_strerror() +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_get_dns_error(struct bufferevent *bev); + +/** + Assign a bufferevent to a specific event_base. + + NOTE that only socket bufferevents support this function. + + @param base an event_base returned by event_init() + @param bufev a bufferevent struct returned by bufferevent_new() + or bufferevent_socket_new() + @return 0 if successful, or -1 if an error occurred + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); + +/** + Return the event_base used by a bufferevent +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *bufferevent_get_base(struct bufferevent *bev); + +/** + Assign a priority to a bufferevent. + + Only supported for socket bufferevents. + + @param bufev a bufferevent struct + @param pri the priority to be assigned + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_priority_set(struct bufferevent *bufev, int pri); + +/** + Return the priority of a bufferevent. + + Only supported for socket bufferevents + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_get_priority(const struct bufferevent *bufev); + +/** + Deallocate the storage associated with a bufferevent structure. + + If there is pending data to write on the bufferevent, it probably won't be + flushed before the bufferevent is freed. + + @param bufev the bufferevent structure to be freed. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_free(struct bufferevent *bufev); + + +/** + Changes the callbacks for a bufferevent. + + @param bufev the bufferevent object for which to change callbacks + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param eventcb callback to invoke when there is an event on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_setcb(struct bufferevent *bufev, + bufferevent_data_cb readcb, bufferevent_data_cb writecb, + bufferevent_event_cb eventcb, void *cbarg); + +/** + Retrieves the callbacks for a bufferevent. + + @param bufev the bufferevent to examine. + @param readcb_ptr if readcb_ptr is nonnull, *readcb_ptr is set to the current + read callback for the bufferevent. + @param writecb_ptr if writecb_ptr is nonnull, *writecb_ptr is set to the + current write callback for the bufferevent. + @param eventcb_ptr if eventcb_ptr is nonnull, *eventcb_ptr is set to the + current event callback for the bufferevent. + @param cbarg_ptr if cbarg_ptr is nonnull, *cbarg_ptr is set to the current + callback argument for the bufferevent. + @see buffervent_setcb() +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_getcb(struct bufferevent *bufev, + bufferevent_data_cb *readcb_ptr, + bufferevent_data_cb *writecb_ptr, + bufferevent_event_cb *eventcb_ptr, + void **cbarg_ptr); + +/** + Changes the file descriptor on which the bufferevent operates. + Not supported for all bufferevent types. + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd); + +/** + Returns the file descriptor associated with a bufferevent, or -1 if + no file descriptor is associated with the bufferevent. + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t bufferevent_getfd(struct bufferevent *bufev); + +/** + Returns the underlying bufferevent associated with a bufferevent (if + the bufferevent is a wrapper), or NULL if there is no underlying bufferevent. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev); + +/** + Write data to a bufferevent buffer. + + The bufferevent_write() function can be used to write data to the file + descriptor. The data is appended to the output buffer and written to the + descriptor automatically as it becomes available for writing. + + @param bufev the bufferevent to be written to + @param data a pointer to the data to be written + @param size the length of the data, in bytes + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write_buffer() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write(struct bufferevent *bufev, + const void *data, size_t size); + + +/** + Write data from an evbuffer to a bufferevent buffer. The evbuffer is + being drained as a result. + + @param bufev the bufferevent to be written to + @param buf the evbuffer to be written + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); + + +/** + Read data from a bufferevent buffer. + + The bufferevent_read() function is used to read data from the input buffer. + + @param bufev the bufferevent to be read from + @param data pointer to a buffer that will store the data + @param size the size of the data buffer, in bytes + @return the amount of data read, in bytes. + */ +EVENT2_EXPORT_SYMBOL +size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); + +/** + Read data from a bufferevent buffer into an evbuffer. This avoids + memory copies. + + @param bufev the bufferevent to be read from + @param buf the evbuffer to which to add data + @return 0 if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf); + +/** + Returns the input buffer. + + The user MUST NOT set the callback on this buffer. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the input buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); + +/** + Returns the output buffer. + + The user MUST NOT set the callback on this buffer. + + When filters are being used, the filters need to be manually + triggered if the output buffer was manipulated. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the output buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); + +/** + Enable a bufferevent. + + @param bufev the bufferevent to be enabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_disable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_enable(struct bufferevent *bufev, short event); + +/** + Disable a bufferevent. + + @param bufev the bufferevent to be disabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_enable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_disable(struct bufferevent *bufev, short event); + +/** + Return the events that are enabled on a given bufferevent. + + @param bufev the bufferevent to inspect + @return A combination of EV_READ | EV_WRITE + */ +EVENT2_EXPORT_SYMBOL +short bufferevent_get_enabled(struct bufferevent *bufev); + +/** + Set the read and write timeout for a bufferevent. + + A bufferevent's timeout will fire the first time that the indicated + amount of time has elapsed since a successful read or write operation, + during which the bufferevent was trying to read or write. + + (In other words, if reading or writing is disabled, or if the + bufferevent's read or write operation has been suspended because + there's no data to write, or not enough banwidth, or so on, the + timeout isn't active. The timeout only becomes active when we we're + willing to actually read or write.) + + Calling bufferevent_enable or setting a timeout for a bufferevent + whose timeout is already pending resets its timeout. + + If the timeout elapses, the corresponding operation (EV_READ or + EV_WRITE) becomes disabled until you re-enable it again. The + bufferevent's event callback is called with the + BEV_EVENT_TIMEOUT|BEV_EVENT_READING or + BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout, or NULL + @param timeout_write the write timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_timeouts(struct bufferevent *bufev, + const struct timeval *timeout_read, const struct timeval *timeout_write); + +/** + Sets the watermarks for read and write events. + + On input, a bufferevent does not invoke the user read callback unless + there is at least low watermark data in the buffer. If the read buffer + is beyond the high watermark, the bufferevent stops reading from the network. + + On output, the user write callback is invoked whenever the buffered data + falls below the low watermark. Filters that write to this bufev will try + not to write more bytes to this buffer than the high watermark would allow, + except when flushing. + + @param bufev the bufferevent to be modified + @param events EV_READ, EV_WRITE or both + @param lowmark the lower watermark to set + @param highmark the high watermark to set +*/ + +EVENT2_EXPORT_SYMBOL +void bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark); + +/** + Retrieves the watermarks for read or write events. + Returns non-zero if events contains not only EV_READ or EV_WRITE. + Returns zero if events equal EV_READ or EV_WRITE + + @param bufev the bufferevent to be examined + @param events EV_READ or EV_WRITE + @param lowmark receives the lower watermark if not NULL + @param highmark receives the high watermark if not NULL +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_getwatermark(struct bufferevent *bufev, short events, + size_t *lowmark, size_t *highmark); + +/** + Acquire the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_lock(struct bufferevent *bufev); + +/** + Release the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_unlock(struct bufferevent *bufev); + + +/** + * Public interface to manually increase the reference count of a bufferevent + * this is useful in situations where a user may reference the bufferevent + * somewhere eles (unknown to libevent) + * + * @param bufev the bufferevent to increase the refcount on + * + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_incref(struct bufferevent *bufev); + +/** + * Public interface to manually decrement the reference count of a bufferevent + * + * Warning: make sure you know what you're doing. This is mainly used in + * conjunction with bufferevent_incref(). This will free up all data associated + * with a bufferevent if the reference count hits 0. + * + * @param bufev the bufferevent to decrement the refcount on + * + * @return 1 if the bufferevent was freed, otherwise 0 (still referenced) + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decref(struct bufferevent *bufev); + +/** + Flags that can be passed into filters to let them know how to + deal with the incoming data. +*/ +enum bufferevent_flush_mode { + /** usually set when processing data */ + BEV_NORMAL = 0, + + /** want to checkpoint all data sent. */ + BEV_FLUSH = 1, + + /** encountered EOF on read or done sending data */ + BEV_FINISHED = 2 +}; + +/** + Triggers the bufferevent to produce more data if possible. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param mode either BEV_NORMAL or BEV_FLUSH or BEV_FINISHED + @return -1 on failure, 0 if no data was produces, 1 if data was produced + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_flush(struct bufferevent *bufev, + short iotype, + enum bufferevent_flush_mode mode); + +/** + Flags for bufferevent_trigger(_event) that modify when and how to trigger + the callback. +*/ +enum bufferevent_trigger_options { + /** trigger the callback regardless of the watermarks */ + BEV_TRIG_IGNORE_WATERMARKS = (1<<16), + + /** defer even if the callbacks are not */ + BEV_TRIG_DEFER_CALLBACKS = BEV_OPT_DEFER_CALLBACKS + + /* (Note: for internal reasons, these need to be disjoint from + * bufferevent_options, except when they mean the same thing. */ +}; + +/** + Triggers bufferevent data callbacks. + + The function will honor watermarks unless options contain + BEV_TRIG_IGNORE_WATERMARKS. If the options contain BEV_OPT_DEFER_CALLBACKS, + the callbacks are deferred. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger(struct bufferevent *bufev, short iotype, + int options); + +/** + Triggers the bufferevent event callback. + + If the options contain BEV_OPT_DEFER_CALLBACKS, the callbacks are deferred. + + @param bufev the bufferevent object + @param what the flags to pass onto the event callback + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger_event(struct bufferevent *bufev, short what, + int options); + +/** + @name Filtering support + + @{ +*/ +/** + Values that filters can return. + */ +enum bufferevent_filter_result { + /** everything is okay */ + BEV_OK = 0, + + /** the filter needs to read more data before output */ + BEV_NEED_MORE = 1, + + /** the filter encountered a critical error, no further data + can be processed. */ + BEV_ERROR = 2 +}; + +/** A callback function to implement a filter for a bufferevent. + + @param src An evbuffer to drain data from. + @param dst An evbuffer to add data to. + @param limit A suggested upper bound of bytes to write to dst. + The filter may ignore this value, but doing so means that + it will overflow the high-water mark associated with dst. + -1 means "no limit". + @param mode Whether we should write data as may be convenient + (BEV_NORMAL), or flush as much data as we can (BEV_FLUSH), + or flush as much as we can, possibly including an end-of-stream + marker (BEV_FINISH). + @param ctx A user-supplied pointer. + + @return BEV_OK if we wrote some data; BEV_NEED_MORE if we can't + produce any more output until we get some input; and BEV_ERROR + on an error. + */ +typedef enum bufferevent_filter_result (*bufferevent_filter_cb)( + struct evbuffer *src, struct evbuffer *dst, ev_ssize_t dst_limit, + enum bufferevent_flush_mode mode, void *ctx); + +/** + Allocate a new filtering bufferevent on top of an existing bufferevent. + + @param underlying the underlying bufferevent. + @param input_filter The filter to apply to data we read from the underlying + bufferevent + @param output_filter The filer to apply to data we write to the underlying + bufferevent + @param options A bitfield of bufferevent options. + @param free_context A function to use to free the filter context when + this bufferevent is freed. + @param ctx A context pointer to pass to the filter functions. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_filter_new(struct bufferevent *underlying, + bufferevent_filter_cb input_filter, + bufferevent_filter_cb output_filter, + int options, + void (*free_context)(void *), + void *ctx); +/**@}*/ + +/** + Allocate a pair of linked bufferevents. The bufferevents behave as would + two bufferevent_sock instances connected to opposite ends of a + socketpair(), except that no internal socketpair is allocated. + + @param base The event base to associate with the socketpair. + @param options A set of options for this bufferevent + @param pair A pointer to an array to hold the two new bufferevent objects. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_pair_new(struct event_base *base, int options, + struct bufferevent *pair[2]); + +/** + Given one bufferevent returned by bufferevent_pair_new(), returns the + other one if it still exists. Otherwise returns NULL. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev); + +/** + Abstract type used to configure rate-limiting on a bufferevent or a group + of bufferevents. + */ +struct ev_token_bucket_cfg; + +/** + A group of bufferevents which are configured to respect the same rate + limit. +*/ +struct bufferevent_rate_limit_group; + +/** Maximum configurable rate- or burst-limit. */ +#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX + +/** + Initialize and return a new object to configure the rate-limiting behavior + of bufferevents. + + @param read_rate The maximum number of bytes to read per tick on + average. + @param read_burst The maximum number of bytes to read in any single tick. + @param write_rate The maximum number of bytes to write per tick on + average. + @param write_burst The maximum number of bytes to write in any single tick. + @param tick_len The length of a single tick. Defaults to one second. + Any fractions of a millisecond are ignored. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + */ +EVENT2_EXPORT_SYMBOL +struct ev_token_bucket_cfg *ev_token_bucket_cfg_new( + size_t read_rate, size_t read_burst, + size_t write_rate, size_t write_burst, + const struct timeval *tick_len); + +/** Free all storage held in 'cfg'. + + Note: 'cfg' is not currently reference-counted; it is not safe to free it + until no bufferevent is using it. + */ +EVENT2_EXPORT_SYMBOL +void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg); + +/** + Set the rate-limit of a the bufferevent 'bev' to the one specified in + 'cfg'. If 'cfg' is NULL, disable any per-bufferevent rate-limiting on + 'bev'. + + Note that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + + Return 0 on sucess, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_rate_limit(struct bufferevent *bev, + struct ev_token_bucket_cfg *cfg); + +/** + Create a new rate-limit group for bufferevents. A rate-limit group + constrains the maximum number of bytes sent and received, in toto, + by all of its bufferevents. + + @param base An event_base to run any necessary timeouts for the group. + Note that all bufferevents in the group do not necessarily need to share + this event_base. + @param cfg The rate-limit for this group. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + + Note also that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new( + struct event_base *base, + const struct ev_token_bucket_cfg *cfg); +/** + Change the rate-limiting settings for a given rate-limiting group. + + Return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_cfg( + struct bufferevent_rate_limit_group *, + const struct ev_token_bucket_cfg *); + +/** + Change the smallest quantum we're willing to allocate to any single + bufferevent in a group for reading or writing at a time. + + The rationale is that, because of TCP/IP protocol overheads and kernel + behavior, if a rate-limiting group is so tight on bandwidth that you're + only willing to send 1 byte per tick per bufferevent, you might instead + want to batch up the reads and writes so that you send N bytes per + 1/N of the bufferevents (chosen at random) each tick, so you still wind + up send 1 byte per tick per bufferevent on average, but you don't send + so many tiny packets. + + The default min-share is currently 64 bytes. + + Returns 0 on success, -1 on faulre. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_min_share( + struct bufferevent_rate_limit_group *, size_t); + +/** + Free a rate-limiting group. The group must have no members when + this function is called. +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *); + +/** + Add 'bev' to the list of bufferevents whose aggregate reading and writing + is restricted by 'g'. If 'g' is NULL, remove 'bev' from its current group. + + A bufferevent may belong to no more than one rate-limit group at a time. + If 'bev' is already a member of a group, it will be removed from its old + group before being added to 'g'. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_add_to_rate_limit_group(struct bufferevent *bev, + struct bufferevent_rate_limit_group *g); + +/** Remove 'bev' from its current rate-limit group (if any). */ +EVENT2_EXPORT_SYMBOL +int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev); + +/** + Set the size limit for single read operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size); + +/** + Set the size limit for single write operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size); + +/** Get the current size limit for single read operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev); + +/** Get the current size limit for single write operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev); + +/** + @name Rate limit inspection + + Return the current read or write bucket size for a bufferevent. + If it is not configured with a per-bufferevent ratelimit, return + EV_SSIZE_MAX. This function does not inspect the group limit, if any. + Note that it can return a negative value if the bufferevent has been + made to read or write more than its limit. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev); +/*@}*/ + +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_write(struct bufferevent *bev); + +EVENT2_EXPORT_SYMBOL +const struct ev_token_bucket_cfg *bufferevent_get_token_bucket_cfg(const struct bufferevent * bev); + +/** + @name Group Rate limit inspection + + Return the read or write bucket size for a bufferevent rate limit + group. Note that it can return a negative value if bufferevents in + the group have been made to read or write more than their limits. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_read_limit( + struct bufferevent_rate_limit_group *); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_write_limit( + struct bufferevent_rate_limit_group *); +/*@}*/ + +/** + @name Rate limit manipulation + + Subtract a number of bytes from a bufferevent's read or write bucket. + The decrement value can be negative, if you want to manually refill + the bucket. If the change puts the bucket above or below zero, the + bufferevent will resume or suspend reading writing as appropriate. + These functions make no change in the buckets for the bufferevent's + group, if any. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr); +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr); +/*@}*/ + +/** + @name Group rate limit manipulation + + Subtract a number of bytes from a bufferevent rate-limiting group's + read or write bucket. The decrement value can be negative, if you + want to manually refill the bucket. If the change puts the bucket + above or below zero, the bufferevents in the group will resume or + suspend reading writing as appropriate. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_read( + struct bufferevent_rate_limit_group *, ev_ssize_t); +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_write( + struct bufferevent_rate_limit_group *, ev_ssize_t); +/*@}*/ + + +/** + * Inspect the total bytes read/written on a group. + * + * Set the variable pointed to by total_read_out to the total number of bytes + * ever read on grp, and the variable pointed to by total_written_out to the + * total number of bytes ever written on grp. */ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_get_totals( + struct bufferevent_rate_limit_group *grp, + ev_uint64_t *total_read_out, ev_uint64_t *total_written_out); + +/** + * Reset the total bytes read/written on a group. + * + * Reset the number of bytes read or written on grp as given by + * bufferevent_rate_limit_group_reset_totals(). */ +EVENT2_EXPORT_SYMBOL +void +bufferevent_rate_limit_group_reset_totals( + struct bufferevent_rate_limit_group *grp); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/bufferevent_compat.h b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_compat.h new file mode 100644 index 000000000..65482042f --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_compat.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007-2012 Niels Provos, Nick Mathewson + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ + +#define evbuffercb bufferevent_data_cb +#define everrorcb bufferevent_event_cb + +/** + Create a new bufferevent for an fd. + + This function is deprecated. Use bufferevent_socket_new and + bufferevent_set_callbacks instead. + + Libevent provides an abstraction on top of the regular event callbacks. + This abstraction is called a buffered event. A buffered event provides + input and output buffers that get filled and drained automatically. The + user of a buffered event no longer deals directly with the I/O, but + instead is reading from input and writing to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly with + bufferevent_enable() and bufferevent_disable(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + If multiple bases are in use, bufferevent_base_set() must be called before + enabling the bufferevent for the first time. + + @deprecated This function is deprecated because it uses the current + event base, and as such can be error prone for multithreaded programs. + Use bufferevent_socket_new() instead. + + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param errorcb callback to invoke when there is an error on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_base_set(), bufferevent_free() + */ +struct bufferevent *bufferevent_new(evutil_socket_t fd, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + + +/** + Set the read and write timeout for a buffered event. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout + @param timeout_write the write timeout + */ +void bufferevent_settimeout(struct bufferevent *bufev, + int timeout_read, int timeout_write); + +#define EVBUFFER_READ BEV_EVENT_READING +#define EVBUFFER_WRITE BEV_EVENT_WRITING +#define EVBUFFER_EOF BEV_EVENT_EOF +#define EVBUFFER_ERROR BEV_EVENT_ERROR +#define EVBUFFER_TIMEOUT BEV_EVENT_TIMEOUT + +/** macro for getting access to the input buffer of a bufferevent */ +#define EVBUFFER_INPUT(x) bufferevent_get_input(x) +/** macro for getting access to the output buffer of a bufferevent */ +#define EVBUFFER_OUTPUT(x) bufferevent_get_output(x) + +#endif diff --git a/ios/Pods/CocoaLibEvent/src/event2/bufferevent_ssl.h b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_ssl.h new file mode 100644 index 000000000..bf39b844a --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_ssl.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ + +/** @file event2/bufferevent_ssl.h + + OpenSSL support for bufferevents. + */ +#include <event2/visibility.h> +#include <event2/event-config.h> +#include <event2/bufferevent.h> +#include <event2/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is what openssl's SSL objects are underneath. */ +struct ssl_st; + +/** + The state of an SSL object to be used when creating a new + SSL bufferevent. + */ +enum bufferevent_ssl_state { + BUFFEREVENT_SSL_OPEN = 0, + BUFFEREVENT_SSL_CONNECTING = 1, + BUFFEREVENT_SSL_ACCEPTING = 2 +}; + +#if defined(EVENT__HAVE_OPENSSL) || defined(EVENT_IN_DOXYGEN_) +/** + Create a new SSL bufferevent to send its data over another bufferevent. + + @param base An event_base to use to detect reading and writing. It + must also be the base for the underlying bufferevent. + @param underlying A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_filter_new(struct event_base *base, + struct bufferevent *underlying, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + Create a new SSL bufferevent to send its data over an SSL * on a socket. + + @param base An event_base to use to detect reading and writing + @param fd A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure. +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_socket_new(struct event_base *base, + evutil_socket_t fd, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** Control how to report dirty SSL shutdowns. + + If the peer (or the network, or an attacker) closes the TCP + connection before closing the SSL channel, and the protocol is SSL >= v3, + this is a "dirty" shutdown. If allow_dirty_shutdown is 0 (default), + this is reported as BEV_EVENT_ERROR. + + If instead allow_dirty_shutdown=1, a dirty shutdown is reported as + BEV_EVENT_EOF. + + (Note that if the protocol is < SSLv3, you will always receive + BEV_EVENT_EOF, since SSL 2 and earlier cannot distinguish a secure + connection close from a dirty one. This is one reason (among many) + not to use SSL 2.) +*/ + +EVENT2_EXPORT_SYMBOL +int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown); + +/** Return the underlying openssl SSL * object for an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +struct ssl_st * +bufferevent_openssl_get_ssl(struct bufferevent *bufev); + +/** Tells a bufferevent to begin SSL renegotiation. */ +EVENT2_EXPORT_SYMBOL +int bufferevent_ssl_renegotiate(struct bufferevent *bev); + +/** Return the most recent OpenSSL error reported on an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +unsigned long bufferevent_get_openssl_error(struct bufferevent *bev); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/bufferevent_struct.h b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_struct.h new file mode 100644 index 000000000..e84c082c3 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/bufferevent_struct.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ + +/** @file event2/bufferevent_struct.h + + Data structures for bufferevents. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + @deprecated Use of bufferevent_struct.h is completely deprecated; these + structures are only exposed for backward compatibility with programs + written before Libevent 2.0 that used them. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> +/* For struct event */ +#include <event2/event_struct.h> + +struct event_watermark { + size_t low; + size_t high; +}; + +/** + Shared implementation of a bufferevent. + + This type is exposed only because it was exposed in previous versions, + and some people's code may rely on manipulating it. Otherwise, you + should really not rely on the layout, size, or contents of this structure: + it is fairly volatile, and WILL change in future versions of the code. +**/ +struct bufferevent { + /** Event base for which this bufferevent was created. */ + struct event_base *ev_base; + /** Pointer to a table of function pointers to set up how this + bufferevent behaves. */ + const struct bufferevent_ops *be_ops; + + /** A read event that triggers when a timeout has happened or a socket + is ready to read data. Only used by some subtypes of + bufferevent. */ + struct event ev_read; + /** A write event that triggers when a timeout has happened or a socket + is ready to write data. Only used by some subtypes of + bufferevent. */ + struct event ev_write; + + /** An input buffer. Only the bufferevent is allowed to add data to + this buffer, though the user is allowed to drain it. */ + struct evbuffer *input; + + /** An input buffer. Only the bufferevent is allowed to drain data + from this buffer, though the user is allowed to add it. */ + struct evbuffer *output; + + struct event_watermark wm_read; + struct event_watermark wm_write; + + bufferevent_data_cb readcb; + bufferevent_data_cb writecb; + /* This should be called 'eventcb', but renaming it would break + * backward compatibility */ + bufferevent_event_cb errorcb; + void *cbarg; + + struct timeval timeout_read; + struct timeval timeout_write; + + /** Events that are currently enabled: currently EV_READ and EV_WRITE + are supported. */ + short enabled; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/dns.h b/ios/Pods/CocoaLibEvent/src/event2/dns.h new file mode 100644 index 000000000..17cd86a2e --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/dns.h @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The original DNS code is due to Adam Langley with heavy + * modifications by Nick Mathewson. Adam put his DNS software in the + * public domain. You can find his original copyright below. Please, + * aware that the code as part of Libevent is governed by the 3-clause + * BSD license above. + * + * This software is Public Domain. To view a copy of the public domain dedication, + * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + * + * I ask and expect, but do not require, that all derivative works contain an + * attribution similar to: + * Parts developed by Adam Langley <agl@imperialviolet.org> + * + * You may wish to replace the word "Parts" with something else depending on + * the amount of original code. + * + * (Derivative works does not include programs which link against, run or include + * the source verbatim in their source distributions) + */ + +/** @file event2/dns.h + * + * Welcome, gentle reader + * + * Async DNS lookups are really a whole lot harder than they should be, + * mostly stemming from the fact that the libc resolver has never been + * very good at them. Before you use this library you should see if libc + * can do the job for you with the modern async call getaddrinfo_a + * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, + * please continue. + * + * The library keeps track of the state of nameservers and will avoid + * them when they go down. Otherwise it will round robin between them. + * + * Quick start guide: + * #include "evdns.h" + * void callback(int result, char type, int count, int ttl, + * void *addresses, void *arg); + * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + * evdns_resolve("www.hostname.com", 0, callback, NULL); + * + * When the lookup is complete the callback function is called. The + * first argument will be one of the DNS_ERR_* defines in evdns.h. + * Hopefully it will be DNS_ERR_NONE, in which case type will be + * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time + * which the data can be cached for (in seconds), addresses will point + * to an array of uint32_t's and arg will be whatever you passed to + * evdns_resolve. + * + * Searching: + * + * In order for this library to be a good replacement for glibc's resolver it + * supports searching. This involves setting a list of default domains, in + * which names will be queried for. The number of dots in the query name + * determines the order in which this list is used. + * + * Searching appears to be a single lookup from the point of view of the API, + * although many DNS queries may be generated from a single call to + * evdns_resolve. Searching can also drastically slow down the resolution + * of names. + * + * To disable searching: + * 1. Never set it up. If you never call evdns_resolv_conf_parse or + * evdns_search_add then no searching will occur. + * + * 2. If you do call evdns_resolv_conf_parse then don't pass + * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). + * + * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. + * + * The order of searches depends on the number of dots in the name. If the + * number is greater than the ndots setting then the names is first tried + * globally. Otherwise each search domain is appended in turn. + * + * The ndots setting can either be set from a resolv.conf, or by calling + * evdns_search_ndots_set. + * + * For example, with ndots set to 1 (the default) and a search domain list of + * ["myhome.net"]: + * Query: www + * Order: www.myhome.net, www. + * + * Query: www.abc + * Order: www.abc., www.abc.myhome.net + * + * Internals: + * + * Requests are kept in two queues. The first is the inflight queue. In + * this queue requests have an allocated transaction id and nameserver. + * They will soon be transmitted if they haven't already been. + * + * The second is the waiting queue. The size of the inflight ring is + * limited and all other requests wait in waiting queue for space. This + * bounds the number of concurrent requests so that we don't flood the + * nameserver. Several algorithms require a full walk of the inflight + * queue and so bounding its size keeps thing going nicely under huge + * (many thousands of requests) loads. + * + * If a nameserver loses too many requests it is considered down and we + * try not to use it. After a while we send a probe to that nameserver + * (a lookup for google.com) and, if it replies, we consider it working + * again. If the nameserver fails a probe we wait longer to try again + * with the next probe. + */ + +#ifndef EVENT2_DNS_H_INCLUDED_ +#define EVENT2_DNS_H_INCLUDED_ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* For integer types. */ +#include <event2/util.h> + +/** Error codes 0-5 are as described in RFC 1035. */ +#define DNS_ERR_NONE 0 +/** The name server was unable to interpret the query */ +#define DNS_ERR_FORMAT 1 +/** The name server was unable to process this query due to a problem with the + * name server */ +#define DNS_ERR_SERVERFAILED 2 +/** The domain name does not exist */ +#define DNS_ERR_NOTEXIST 3 +/** The name server does not support the requested kind of query */ +#define DNS_ERR_NOTIMPL 4 +/** The name server refuses to reform the specified operation for policy + * reasons */ +#define DNS_ERR_REFUSED 5 +/** The reply was truncated or ill-formatted */ +#define DNS_ERR_TRUNCATED 65 +/** An unknown error occurred */ +#define DNS_ERR_UNKNOWN 66 +/** Communication with the server timed out */ +#define DNS_ERR_TIMEOUT 67 +/** The request was canceled because the DNS subsystem was shut down. */ +#define DNS_ERR_SHUTDOWN 68 +/** The request was canceled via a call to evdns_cancel_request */ +#define DNS_ERR_CANCEL 69 +/** There were no answers and no error condition in the DNS packet. + * This can happen when you ask for an address that exists, but a record + * type that doesn't. */ +#define DNS_ERR_NODATA 70 + +#define DNS_IPv4_A 1 +#define DNS_PTR 2 +#define DNS_IPv6_AAAA 3 + +#define DNS_QUERY_NO_SEARCH 1 + +#define DNS_OPTION_SEARCH 1 +#define DNS_OPTION_NAMESERVERS 2 +#define DNS_OPTION_MISC 4 +#define DNS_OPTION_HOSTSFILE 8 +#define DNS_OPTIONS_ALL 15 + +/* Obsolete name for DNS_QUERY_NO_SEARCH */ +#define DNS_NO_SEARCH DNS_QUERY_NO_SEARCH + +/** + * The callback that contains the results from a lookup. + * - result is one of the DNS_ERR_* values (DNS_ERR_NONE for success) + * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA + * - count contains the number of addresses of form type + * - ttl is the number of seconds the resolution may be cached for. + * - addresses needs to be cast according to type. It will be an array of + * 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6, + * or a nul-terminated string for PTR. + */ +typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg); + +struct evdns_base; +struct event_base; + +/** Flag for evdns_base_new: process resolv.conf. */ +#define EVDNS_BASE_INITIALIZE_NAMESERVERS 1 +/** Flag for evdns_base_new: Do not prevent the libevent event loop from + * exiting when we have no active dns requests. */ +#define EVDNS_BASE_DISABLE_WHEN_INACTIVE 0x8000 + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @param event_base the event base to associate the dns client with + @param flags any of EVDNS_BASE_INITIALIZE_NAMESERVERS| + EVDNS_BASE_DISABLE_WHEN_INACTIVE + @return evdns_base object if successful, or NULL if an error occurred. + @see evdns_base_free() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_base * evdns_base_new(struct event_base *event_base, int initialize_nameservers); + + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @param evdns_base the evdns base to free + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_base_new() + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_free(struct evdns_base *base, int fail_requests); + +/** + Remove all hosts entries that have been loaded into the event_base via + evdns_base_load_hosts or via event_base_resolv_conf_parse. + + @param evdns_base the evdns base to remove outdated host addresses from + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_clear_host_addresses(struct evdns_base *base); + +/** + Convert a DNS error code to a string. + + @param err the DNS error code + @return a string containing an explanation of the error code +*/ +EVENT2_EXPORT_SYMBOL +const char *evdns_err_to_string(int err); + + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @param base the evdns_base to which to add the name server + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_ip_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_add(struct evdns_base *base, + unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @param base the evdns_base to which to apply this operation + @return the number of configured nameservers + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_count_nameservers(struct evdns_base *base); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_base_resume() is called. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_resume() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_clear_nameservers_and_suspend(struct evdns_base *base); + + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_base_clear_nameservers_and_suspend(). + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_clear_nameservers_and_suspend() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resume(struct evdns_base *base); + +/** + Add a nameserver by string address. + + This function parses a n IPv4 or IPv6 address from a string and adds it as a + nameserver. It supports the following formats: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, it defaults to 53. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_ip_add(struct evdns_base *base, + const char *ip_as_string); + +/** + Add a nameserver by sockaddr. + **/ +EVENT2_EXPORT_SYMBOL +int +evdns_base_nameserver_sockaddr_add(struct evdns_base *base, + const struct sockaddr *sa, ev_socklen_t len, unsigned flags); + +struct evdns_request; + +/** + Lookup an A record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv4(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv6(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @param base the evdns_base to which to apply this operation + @param in an IPv4 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse(struct evdns_base *base, const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + + +/** + Lookup a PTR record for a given IPv6 address. + + @param base the evdns_base to which to apply this operation + @param in an IPv6 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse_ipv6(struct evdns_base *base, const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Cancels a pending DNS resolution request. + + @param base the evdns_base that was used to make the request + @param req the evdns_request that was returned by calling a resolve function + @see evdns_base_resolve_ipv4(), evdns_base_resolve_ipv6, evdns_base_resolve_reverse +*/ +EVENT2_EXPORT_SYMBOL +void evdns_cancel_request(struct evdns_base *base, struct evdns_request *req); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case, + bind-to, initial-probe-timeout, getaddrinfo-allow-skew. + + In versions before Libevent 2.0.3-alpha, the option name needed to end with + a colon. + + @param base the evdns_base to which to apply this operation + @param option the name of the configuration option to be modified + @param val the value to be set + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val); + + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @param base the evdns_base to which to apply this operation + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTION_HOSTSFILE|DNS_OPTIONS_ALL + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *const filename); + +/** + Load an /etc/hosts-style file from 'hosts_fname' into 'base'. + + If hosts_fname is NULL, add minimal entries for localhost, and nothing + else. + + Note that only evdns_getaddrinfo uses the /etc/hosts entries. + + This function does not replace previously loaded hosts entries; to do that, + call evdns_base_clear_host_addresses first. + + Return 0 on success, negative on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname); + +/** + Obtain nameserver information using the Windows API. + + Attempt to configure a set of nameservers based on platform settings on + a win32 host. Preferentially tries to use GetNetworkParams; if that fails, + looks in the registry. + + @return 0 if successful, or -1 if an error occurred + @see evdns_resolv_conf_parse() + */ +#ifdef _WIN32 +EVENT2_EXPORT_SYMBOL +int evdns_base_config_windows_nameservers(struct evdns_base *); +#define EVDNS_BASE_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + + +/** + Clear the list of search domains. + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_clear(struct evdns_base *base); + + +/** + Add a domain to the list of search domains + + @param domain the domain to be added to the search list + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_add(struct evdns_base *base, const char *domain); + + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @param ndots the new ndots parameter + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_ndots_set(struct evdns_base *base, const int ndots); + +/** + A callback that is invoked when a log message is generated + + @param is_warning indicates if the log message is a 'warning' + @param msg the content of the log message + */ +typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); + + +/** + Set the callback function to handle DNS log messages. If this + callback is not set, evdns log messages are handled with the regular + Libevent logging system. + + @param fn the callback to be invoked when a log message is generated + */ +EVENT2_EXPORT_SYMBOL +void evdns_set_log_fn(evdns_debug_log_fn_type fn); + +/** + Set a callback that will be invoked to generate transaction IDs. By + default, we pick transaction IDs based on the current clock time, which + is bad for security. + + @param fn the new callback, or NULL to use the default. + + NOTE: This function has no effect in Libevent 2.0.4-alpha and later, + since Libevent now provides its own secure RNG. + */ +EVENT2_EXPORT_SYMBOL +void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); + +/** + Set a callback used to generate random bytes. By default, we use + the same function as passed to evdns_set_transaction_id_fn to generate + bytes two at a time. If a function is provided here, it's also used + to generate transaction IDs. + + NOTE: This function has no effect in Libevent 2.0.4-alpha and later, + since Libevent now provides its own secure RNG. +*/ +EVENT2_EXPORT_SYMBOL +void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); + +/* + * Functions used to implement a DNS server. + */ + +struct evdns_server_request; +struct evdns_server_question; + +/** + A callback to implement a DNS server. The callback function receives a DNS + request. It should then optionally add a number of answers to the reply + using the evdns_server_request_add_*_reply functions, before calling either + evdns_server_request_respond to send the reply back, or + evdns_server_request_drop to decline to answer the request. + + @param req A newly received request + @param user_data A pointer that was passed to + evdns_add_server_port_with_base(). + */ +typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); +#define EVDNS_ANSWER_SECTION 0 +#define EVDNS_AUTHORITY_SECTION 1 +#define EVDNS_ADDITIONAL_SECTION 2 + +#define EVDNS_TYPE_A 1 +#define EVDNS_TYPE_NS 2 +#define EVDNS_TYPE_CNAME 5 +#define EVDNS_TYPE_SOA 6 +#define EVDNS_TYPE_PTR 12 +#define EVDNS_TYPE_MX 15 +#define EVDNS_TYPE_TXT 16 +#define EVDNS_TYPE_AAAA 28 + +#define EVDNS_QTYPE_AXFR 252 +#define EVDNS_QTYPE_ALL 255 + +#define EVDNS_CLASS_INET 1 + +/* flags that can be set in answers; as part of the err parameter */ +#define EVDNS_FLAGS_AA 0x400 +#define EVDNS_FLAGS_RD 0x080 + +/** Create a new DNS server port. + + @param base The event base to handle events for the server port. + @param socket A UDP socket to accept DNS requests. + @param flags Always 0 for now. + @param callback A function to invoke whenever we get a DNS request + on the socket. + @param user_data Data to pass to the callback. + @return an evdns_server_port structure for this server port. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_server_port *evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); +/** Close down a DNS server port, and free associated structures. */ +EVENT2_EXPORT_SYMBOL +void evdns_close_server_port(struct evdns_server_port *port); + +/** Sets some flags in a reply we're building. + Allows setting of the AA or RD flags + */ +EVENT2_EXPORT_SYMBOL +void evdns_server_request_set_flags(struct evdns_server_request *req, int flags); + +/* Functions to add an answer to an in-progress DNS reply. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); + +/** + Send back a response to a DNS request, and free the request structure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_respond(struct evdns_server_request *req, int err); +/** + Free a DNS request without sending back a reply. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_drop(struct evdns_server_request *req); +struct sockaddr; +/** + Get the address that made a DNS request. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len); + +/** Callback for evdns_getaddrinfo. */ +typedef void (*evdns_getaddrinfo_cb)(int result, struct evutil_addrinfo *res, void *arg); + +struct evdns_base; +struct evdns_getaddrinfo_request; +/** Make a non-blocking getaddrinfo request using the dns_base in 'dns_base'. + * + * If we can answer the request immediately (with an error or not!), then we + * invoke cb immediately and return NULL. Otherwise we return + * an evdns_getaddrinfo_request and invoke cb later. + * + * When the callback is invoked, we pass as its first argument the error code + * that getaddrinfo would return (or 0 for no error). As its second argument, + * we pass the evutil_addrinfo structures we found (or NULL on error). We + * pass 'arg' as the third argument. + * + * Limitations: + * + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * - For ai_socktype, we only handle SOCKTYPE_STREAM, SOCKTYPE_UDP, and 0. + * - For ai_protocol, we only handle IPPROTO_TCP, IPPROTO_UDP, and 0. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_getaddrinfo_request *evdns_getaddrinfo( + struct evdns_base *dns_base, + const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, + evdns_getaddrinfo_cb cb, void *arg); + +/* Cancel an in-progress evdns_getaddrinfo. This MUST NOT be called after the + * getaddrinfo's callback has been invoked. The resolves will be canceled, + * and the callback will be invoked with the error EVUTIL_EAI_CANCEL. */ +EVENT2_EXPORT_SYMBOL +void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *req); + +/** + Retrieve the address of the 'idx'th configured nameserver. + + @param base The evdns_base to examine. + @param idx The index of the nameserver to get the address of. + @param sa A location to receive the server's address. + @param len The number of bytes available at sa. + + @return the number of bytes written into sa on success. On failure, returns + -1 if idx is greater than the number of configured nameservers, or a + value greater than 'len' if len was not high enough. + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_get_nameserver_addr(struct evdns_base *base, int idx, + struct sockaddr *sa, ev_socklen_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* !EVENT2_DNS_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/dns_compat.h b/ios/Pods/CocoaLibEvent/src/event2/dns_compat.h new file mode 100644 index 000000000..965fd6544 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/dns_compat.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_DNS_COMPAT_H_INCLUDED_ +#define EVENT2_DNS_COMPAT_H_INCLUDED_ + +/** @file event2/dns_compat.h + + Potentially non-threadsafe versions of the functions in dns.h: provided + only for backwards compatibility. + + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @deprecated This function is deprecated because it always uses the current + event base, and is easily confused by multiple calls to event_init(), and + so is not safe for multithreaded use. Additionally, it allocates a global + structure that only one thread can use. The replacement is + evdns_base_new(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_shutdown() + */ +int evdns_init(void); + +struct evdns_base; +/** + Return the global evdns_base created by event_init() and used by the other + deprecated functions. + + @deprecated This function is deprecated because use of the global + evdns_base is error-prone. + */ +struct evdns_base *evdns_get_global_base(void); + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_shutdown(). + + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_init() + */ +void evdns_shutdown(int fail_requests); + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_add(). + + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_ip_add() + */ +int evdns_nameserver_add(unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_count_nameservers(). + + @return the number of configured nameservers + @see evdns_nameserver_add() + */ +int evdns_count_nameservers(void); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_resume() is called. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_clear_nameservers_and_suspend(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_resume() + */ +int evdns_clear_nameservers_and_suspend(void); + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_clear_nameservers_and_suspend(). + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resume(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_clear_nameservers_and_suspend() + */ +int evdns_resume(void); + +/** + Add a nameserver. + + This wraps the evdns_nameserver_add() function by parsing a string as an IP + address and adds it as a nameserver. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_ip_add(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_add() + */ +int evdns_nameserver_ip_add(const char *ip_as_string); + +/** + Lookup an A record for a given name. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_ipv4(). + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse(). + + @param in an IPv4 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup a PTR record for a given IPv6 address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse_ipv6(). + + @param in an IPv6 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, and attempts + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_set_option(). + + @param option the name of the configuration option to be modified + @param val the value to be set + @param flags Ignored. + @return 0 if successful, or -1 if an error occurred + */ +int evdns_set_option(const char *option, const char *val, int flags); + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolv_conf_parse(). + + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTIONS_ALL + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +int evdns_resolv_conf_parse(int flags, const char *const filename); + +/** + Clear the list of search domains. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_clear(). + */ +void evdns_search_clear(void); + +/** + Add a domain to the list of search domains + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_add(). + + @param domain the domain to be added to the search list + */ +void evdns_search_add(const char *domain); + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_ndots_set(). + + @param ndots the new ndots parameter + */ +void evdns_search_ndots_set(const int ndots); + +/** + As evdns_server_new_with_base. + + @deprecated This function is deprecated because it does not allow the + caller to specify which even_base it uses. The recommended + function is evdns_add_server_port_with_base(). + +*/ +struct evdns_server_port *evdns_add_server_port(evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); + +#ifdef _WIN32 +int evdns_config_windows_nameservers(void); +#define EVDNS_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/dns_struct.h b/ios/Pods/CocoaLibEvent/src/event2/dns_struct.h new file mode 100644 index 000000000..593a8a70b --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/dns_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_DNS_STRUCT_H_INCLUDED_ +#define EVENT2_DNS_STRUCT_H_INCLUDED_ + +/** @file event2/dns_struct.h + + Data structures for dns. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/* + * Structures used to implement a DNS server. + */ + +struct evdns_server_request { + int flags; + int nquestions; + struct evdns_server_question **questions; +}; +struct evdns_server_question { + int type; +#ifdef __cplusplus + int dns_question_class; +#else + /* You should refer to this field as "dns_question_class". The + * name "class" works in C for backward compatibility, and will be + * removed in a future version. (1.5 or later). */ + int class; +#define dns_question_class class +#endif + char name[1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_DNS_STRUCT_H_INCLUDED_ */ + diff --git a/ios/Pods/CocoaLibEvent/src/event2/event-config.h b/ios/Pods/CocoaLibEvent/src/event2/event-config.h new file mode 100644 index 000000000..58dd9caca --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/event-config.h @@ -0,0 +1,544 @@ +/* event2/event-config.h +* +* This file was generated by autoconf when libevent was built, and post- +* processed by Libevent so that its macros would have a uniform prefix. +* +* DO NOT EDIT THIS FILE. +* +* Do not rely on macros in this file existing in later versions. +*/ + +#ifndef EVENT2_EVENT_CONFIG_H_INCLUDED_ +#define EVENT2_EVENT_CONFIG_H_INCLUDED_ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if libevent should build without support for a debug mode */ +#define EVENT__DISABLE_DEBUG_MODE 1 + +/* Define if libevent should not allow replacing the mm functions */ +/* #undef EVENT__DISABLE_MM_REPLACEMENT */ + +/* Define if libevent should not be compiled with thread support */ +/* #undef EVENT__DISABLE_THREAD_SUPPORT */ + +/* Define to 1 if you have the `accept4' function. */ +/* #undef EVENT__HAVE_ACCEPT4 */ + +/* Define to 1 if you have the `arc4random' function. */ +#define EVENT__HAVE_ARC4RANDOM 1 + +/* Define to 1 if you have the `arc4random_buf' function. */ +#define EVENT__HAVE_ARC4RANDOM_BUF 1 + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#define EVENT__HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef EVENT__HAVE_CLOCK_GETTIME */ + +/* Define to 1 if you have the declaration of `CTL_KERN', and to 0 if you + don't. */ +#define EVENT__HAVE_DECL_CTL_KERN 1 + +/* Define to 1 if you have the declaration of `KERN_ARND', and to 0 if you + don't. */ +#define EVENT__HAVE_DECL_KERN_ARND 0 + +/* Define to 1 if you have the declaration of `KERN_RANDOM', and to 0 if you + don't. */ +#define EVENT__HAVE_DECL_KERN_RANDOM 0 + +/* Define to 1 if you have the declaration of `RANDOM_UUID', and to 0 if you + don't. */ +#define EVENT__HAVE_DECL_RANDOM_UUID 0 + +/* Define if /dev/poll is available */ +/* #undef EVENT__HAVE_DEVPOLL */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define EVENT__HAVE_DLFCN_H 1 + +/* Define if your system supports the epoll system calls */ +/* #undef EVENT__HAVE_EPOLL */ + +/* Define to 1 if you have the `epoll_create1' function. */ +/* #undef EVENT__HAVE_EPOLL_CREATE1 */ + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef EVENT__HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the <errno.h> header file. */ +#define EVENT__HAVE_ERRNO_H 1 + +/* Define to 1 if you have ERR_remove_thread_stat(). */ +/* #undef EVENT__HAVE_ERR_REMOVE_THREAD_STATE */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef EVENT__HAVE_EVENTFD */ + +/* Define if your system supports event ports */ +/* #undef EVENT__HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +#define EVENT__HAVE_FCNTL 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define EVENT__HAVE_FCNTL_H 1 + +/* Define to 1 if the system has the type `fd_mask'. */ +#define EVENT__HAVE_FD_MASK 1 + +/* Do we have getaddrinfo()? */ +#define EVENT__HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getegid' function. */ +#define EVENT__HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define EVENT__HAVE_GETEUID 1 + +/* Define this if you have any gethostbyname_r() */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R */ + +/* Define this if gethostbyname_r takes 3 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_3_ARG */ + +/* Define this if gethostbyname_r takes 5 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_5_ARG */ + +/* Define this if gethostbyname_r takes 6 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_6_ARG */ + +/* Define to 1 if you have the `getifaddrs' function. */ +#define EVENT__HAVE_GETIFADDRS 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define EVENT__HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getprotobynumber' function. */ +#define EVENT__HAVE_GETPROTOBYNUMBER 1 + +/* Define to 1 if you have the `getservbyname' function. */ +#define EVENT__HAVE_GETSERVBYNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define EVENT__HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the <ifaddrs.h> header file. */ +#define EVENT__HAVE_IFADDRS_H 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define EVENT__HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define EVENT__HAVE_INET_PTON 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define EVENT__HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `issetugid' function. */ +#define EVENT__HAVE_ISSETUGID 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define EVENT__HAVE_KQUEUE 1 + +/* Define if the system has zlib */ +#define EVENT__HAVE_LIBZ 1 + +/* Define to 1 if you have the `mach_absolute_time' function. */ +#define EVENT__HAVE_MACH_ABSOLUTE_TIME 1 + +/* Define to 1 if you have the <mach/mach_time.h> header file. */ +#define EVENT__HAVE_MACH_MACH_TIME_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define EVENT__HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mmap' function. */ +#define EVENT__HAVE_MMAP 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define EVENT__HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the <netdb.h> header file. */ +#define EVENT__HAVE_NETDB_H 1 + +/* Define to 1 if you have the <netinet/in6.h> header file. */ +/* #undef EVENT__HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#define EVENT__HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the <netinet/tcp.h> header file. */ +#define EVENT__HAVE_NETINET_TCP_H 1 + +/* Define if the system has openssl */ +/* #undef EVENT__HAVE_OPENSSL */ + +/* Define to 1 if you have the `pipe' function. */ +#define EVENT__HAVE_PIPE 1 + +/* Define to 1 if you have the `pipe2' function. */ +/* #undef EVENT__HAVE_PIPE2 */ + +/* Define to 1 if you have the `poll' function. */ +#define EVENT__HAVE_POLL 1 + +/* Define to 1 if you have the <poll.h> header file. */ +#define EVENT__HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef EVENT__HAVE_PORT_CREATE */ + +/* Define to 1 if you have the <port.h> header file. */ +/* #undef EVENT__HAVE_PORT_H */ + +/* Define if you have POSIX threads libraries and header files. */ +/* #undef EVENT__HAVE_PTHREAD */ + +/* Define if we have pthreads on this system */ +#define EVENT__HAVE_PTHREADS 1 + +/* Define to 1 if you have the `putenv' function. */ +#define EVENT__HAVE_PUTENV 1 + +/* Define to 1 if the system has the type `sa_family_t'. */ +#define EVENT__HAVE_SA_FAMILY_T 1 + +/* Define to 1 if you have the `select' function. */ +#define EVENT__HAVE_SELECT 1 + +/* Define to 1 if you have the `sendfile' function. */ +#define EVENT__HAVE_SENDFILE 1 + +/* Define to 1 if you have the `setenv' function. */ +#define EVENT__HAVE_SETENV 1 + +/* Define if F_SETFD is defined in <fcntl.h> */ +#define EVENT__HAVE_SETFD 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define EVENT__HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define EVENT__HAVE_SIGACTION 1 + +/* Define to 1 if you have the `signal' function. */ +#define EVENT__HAVE_SIGNAL 1 + +/* Define to 1 if you have the `splice' function. */ +/* #undef EVENT__HAVE_SPLICE */ + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define EVENT__HAVE_STDARG_H 1 + +/* Define to 1 if you have the <stddef.h> header file. */ +#define EVENT__HAVE_STDDEF_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define EVENT__HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define EVENT__HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define EVENT__HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define EVENT__HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define EVENT__HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strsep' function. */ +#define EVENT__HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtok_r' function. */ +#define EVENT__HAVE_STRTOK_R 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define EVENT__HAVE_STRTOLL 1 + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define EVENT__HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if `s6_addr16' is a member of `struct in6_addr'. */ +/* #undef EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 */ + +/* Define to 1 if `s6_addr32' is a member of `struct in6_addr'. */ +/* #undef EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 */ + +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 + +/* Define to 1 if `sin6_len' is a member of `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN 1 + +/* Define to 1 if `sin_len' is a member of `struct sockaddr_in'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 1 + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if the system has the type `struct so_linger'. */ +/* #undef EVENT__HAVE_STRUCT_SO_LINGER */ + +/* Define to 1 if you have the `sysctl' function. */ +#define EVENT__HAVE_SYSCTL 1 + +/* Define to 1 if you have the <sys/devpoll.h> header file. */ +/* #undef EVENT__HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the <sys/epoll.h> header file. */ +/* #undef EVENT__HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the <sys/eventfd.h> header file. */ +/* #undef EVENT__HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the <sys/event.h> header file. */ +#define EVENT__HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define EVENT__HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#define EVENT__HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define EVENT__HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/queue.h> header file. */ +#define EVENT__HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the <sys/resource.h> header file. */ +#define EVENT__HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the <sys/select.h> header file. */ +#define EVENT__HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the <sys/sendfile.h> header file. */ +/* #undef EVENT__HAVE_SYS_SENDFILE_H */ + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define EVENT__HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/sysctl.h> header file. */ +#define EVENT__HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the <sys/timerfd.h> header file. */ +/* #undef EVENT__HAVE_SYS_TIMERFD_H */ + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define EVENT__HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define EVENT__HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#define EVENT__HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the <sys/wait.h> header file. */ +#define EVENT__HAVE_SYS_WAIT_H 1 + +/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define if timeradd is defined in <sys/time.h> */ +#define EVENT__HAVE_TIMERADD 1 + +/* Define if timerclear is defined in <sys/time.h> */ +#define EVENT__HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in <sys/time.h> */ +#define EVENT__HAVE_TIMERCMP 1 + +/* Define to 1 if you have the `timerfd_create' function. */ +/* #undef EVENT__HAVE_TIMERFD_CREATE */ + +/* Define if timerisset is defined in <sys/time.h> */ +#define EVENT__HAVE_TIMERISSET 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define EVENT__HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `uint32_t'. */ +#define EVENT__HAVE_UINT32_T 1 + +/* Define to 1 if the system has the type `uint64_t'. */ +#define EVENT__HAVE_UINT64_T 1 + +/* Define to 1 if the system has the type `uint8_t'. */ +#define EVENT__HAVE_UINT8_T 1 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define EVENT__HAVE_UINTPTR_T 1 + +/* Define to 1 if you have the `umask' function. */ +#define EVENT__HAVE_UMASK 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define EVENT__HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define EVENT__HAVE_UNSETENV 1 + +/* Define to 1 if you have the `usleep' function. */ +#define EVENT__HAVE_USLEEP 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define EVENT__HAVE_VASPRINTF 1 + +/* Define if waitpid() supports WNOWAIT */ +/* #undef EVENT__HAVE_WAITPID_WITH_WNOWAIT */ + +/* Define if kqueue works correctly with pipes */ +/* #undef EVENT__HAVE_WORKING_KQUEUE */ + +/* Define to 1 if you have the <zlib.h> header file. */ +#define EVENT__HAVE_ZLIB_H 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define EVENT__LT_OBJDIR ".libs/" + +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION 0x02010800 + +/* Name of package */ +#define EVENT__PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "libevent" + +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "libevent 2.1.8-stable" + +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "libevent" + +/* Define to the home page for this package. */ +#define EVENT__PACKAGE_URL "" + +/* Define to the version of this package. */ +#define EVENT__PACKAGE_VERSION "2.1.8-stable" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef EVENT__PTHREAD_CREATE_JOINABLE */ + +/* The size of `int', as computed by sizeof. */ +#define EVENT__SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG 4 + +/* The size of `long long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG_LONG 8 + +/* The size of `off_t', as computed by sizeof. */ +#define EVENT__SIZEOF_OFF_T 8 + +/* The size of `pthread_t', as computed by sizeof. */ +#define EVENT__SIZEOF_PTHREAD_T 4 + +/* The size of `short', as computed by sizeof. */ +#define EVENT__SIZEOF_SHORT 2 + +/* The size of `size_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SIZE_T 4 + +/* The size of `void *', as computed by sizeof. */ +#define EVENT__SIZEOF_VOID_P 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define EVENT__STDC_HEADERS 1 + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#define EVENT__TIME_WITH_SYS_TIME 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef EVENT___ALL_SOURCE +# define EVENT___ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef EVENT___GNU_SOURCE +# define EVENT___GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef EVENT___POSIX_PTHREAD_SEMANTICS +# define EVENT___POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef EVENT___TANDEM_SOURCE +# define EVENT___TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef EVENT____EXTENSIONS__ +# define EVENT____EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define EVENT__VERSION "2.1.8-stable" + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef EVENT___DARWIN_USE_64_BIT_INODE +# define EVENT___DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef EVENT___FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef EVENT___LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef EVENT___MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef EVENT___POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef EVENT___POSIX_SOURCE */ + +/* Define to appropriate substitue if compiler doesnt have __func__ */ +/* #undef EVENT____func__ */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef EVENT__const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef EVENT____cplusplus +/* #undef EVENT__inline */ +#endif + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef EVENT__pid_t */ + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef EVENT__size_t */ + +/* Define to unsigned int if you dont have it */ +/* #undef EVENT__socklen_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef EVENT__ssize_t */ + +#endif /* event2/event-config.h */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/event.h b/ios/Pods/CocoaLibEvent/src/event2/event.h new file mode 100644 index 000000000..6e0a4f04c --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/event.h @@ -0,0 +1,1675 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_H_INCLUDED_ +#define EVENT2_EVENT_H_INCLUDED_ + +/** + @mainpage + + @section intro Introduction + + Libevent is an event notification library for developing scalable network + servers. The Libevent API provides a mechanism to execute a callback + function when a specific event occurs on a file descriptor or after a + timeout has been reached. Furthermore, Libevent also support callbacks due + to signals or regular timeouts. + + Libevent is meant to replace the event loop found in event driven network + servers. An application just needs to call event_base_dispatch() and then add or + remove events dynamically without having to change the event loop. + + + Currently, Libevent supports /dev/poll, kqueue(2), select(2), poll(2), + epoll(4), and evports. The internal event mechanism is completely + independent of the exposed event API, and a simple update of Libevent can + provide new functionality without having to redesign the applications. As a + result, Libevent allows for portable application development and provides + the most scalable event notification mechanism available on an operating + system. Libevent can also be used for multithreaded programs. Libevent + should compile on Linux, *BSD, Mac OS X, Solaris and, Windows. + + @section usage Standard usage + + Every program that uses Libevent must include the <event2/event.h> + header, and pass the -levent flag to the linker. (You can instead link + -levent_core if you only want the main event and buffered IO-based code, + and don't want to link any protocol code.) + + @section setup Library setup + + Before you call any other Libevent functions, you need to set up the + library. If you're going to use Libevent from multiple threads in a + multithreaded application, you need to initialize thread support -- + typically by using evthread_use_pthreads() or + evthread_use_windows_threads(). See <event2/thread.h> for more + information. + + This is also the point where you can replace Libevent's memory + management functions with event_set_mem_functions, and enable debug mode + with event_enable_debug_mode(). + + @section base Creating an event base + + Next, you need to create an event_base structure, using event_base_new() + or event_base_new_with_config(). The event_base is responsible for + keeping track of which events are "pending" (that is to say, being + watched to see if they become active) and which events are "active". + Every event is associated with a single event_base. + + @section event Event notification + + For each file descriptor that you wish to monitor, you must create an + event structure with event_new(). (You may also declare an event + structure and call event_assign() to initialize the members of the + structure.) To enable notification, you add the structure to the list + of monitored events by calling event_add(). The event structure must + remain allocated as long as it is active, so it should generally be + allocated on the heap. + + @section loop Dispatching events. + + Finally, you call event_base_dispatch() to loop and dispatch events. + You can also use event_base_loop() for more fine-grained control. + + Currently, only one thread can be dispatching a given event_base at a + time. If you want to run events in multiple threads at once, you can + either have a single event_base whose events add work to a work queue, + or you can create multiple event_base objects. + + @section bufferevent I/O Buffers + + Libevent provides a buffered I/O abstraction on top of the regular event + callbacks. This abstraction is called a bufferevent. A bufferevent + provides input and output buffers that get filled and drained + automatically. The user of a buffered event no longer deals directly + with the I/O, but instead is reading from input and writing to output + buffers. + + Once initialized via bufferevent_socket_new(), the bufferevent structure + can be used repeatedly with bufferevent_enable() and + bufferevent_disable(). Instead of reading and writing directly to a + socket, you would call bufferevent_read() and bufferevent_write(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + See <event2/bufferevent*.h> for more information. + + @section timers Timers + + Libevent can also be used to create timers that invoke a callback after a + certain amount of time has expired. The evtimer_new() macro returns + an event struct to use as a timer. To activate the timer, call + evtimer_add(). Timers can be deactivated by calling evtimer_del(). + (These macros are thin wrappers around event_new(), event_add(), + and event_del(); you can also use those instead.) + + @section evdns Asynchronous DNS resolution + + Libevent provides an asynchronous DNS resolver that should be used instead + of the standard DNS resolver functions. See the <event2/dns.h> + functions for more detail. + + @section evhttp Event-driven HTTP servers + + Libevent provides a very simple event-driven HTTP server that can be + embedded in your program and used to service HTTP requests. + + To use this capability, you need to include the <event2/http.h> header in your + program. See that header for more information. + + @section evrpc A framework for RPC servers and clients + + Libevent provides a framework for creating RPC servers and clients. It + takes care of marshaling and unmarshaling all data structures. + + @section api API Reference + + To browse the complete documentation of the libevent API, click on any of + the following links. + + event2/event.h + The primary libevent header + + event2/thread.h + Functions for use by multithreaded programs + + event2/buffer.h and event2/bufferevent.h + Buffer management for network reading and writing + + event2/util.h + Utility functions for portable nonblocking network code + + event2/dns.h + Asynchronous DNS resolution + + event2/http.h + An embedded libevent-based HTTP server + + event2/rpc.h + A framework for creating RPC servers and clients + + */ + +/** @file event2/event.h + + Core functions for waiting for and receiving events, and using event bases. +*/ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include <stdio.h> + +/* For int types. */ +#include <event2/util.h> + +/** + * Structure to hold information and state for a Libevent dispatch loop. + * + * The event_base lies at the center of Libevent; every application will + * have one. It keeps track of all pending and active events, and + * notifies your application of the active ones. + * + * This is an opaque structure; you can allocate one using + * event_base_new() or event_base_new_with_config(). + * + * @see event_base_new(), event_base_free(), event_base_loop(), + * event_base_new_with_config() + */ +struct event_base +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * @struct event + * + * Structure to represent a single event. + * + * An event can have some underlying condition it represents: a socket + * becoming readable or writeable (or both), or a signal becoming raised. + * (An event that represents no underlying condition is still useful: you + * can use one to implement a timer, or to communicate between threads.) + * + * Generally, you can create events with event_new(), then make them + * pending with event_add(). As your event_base runs, it will run the + * callbacks of an events whose conditions are triggered. When you + * longer want the event, free it with event_free(). + * + * In more depth: + * + * An event may be "pending" (one whose condition we are watching), + * "active" (one whose condition has triggered and whose callback is about + * to run), neither, or both. Events come into existence via + * event_assign() or event_new(), and are then neither active nor pending. + * + * To make an event pending, pass it to event_add(). When doing so, you + * can also set a timeout for the event. + * + * Events become active during an event_base_loop() call when either their + * condition has triggered, or when their timeout has elapsed. You can + * also activate an event manually using event_active(). The even_base + * loop will run the callbacks of active events; after it has done so, it + * marks them as no longer active. + * + * You can make an event non-pending by passing it to event_del(). This + * also makes the event non-active. + * + * Events can be "persistent" or "non-persistent". A non-persistent event + * becomes non-pending as soon as it is triggered: thus, it only runs at + * most once per call to event_add(). A persistent event remains pending + * even when it becomes active: you'll need to event_del() it manually in + * order to make it non-pending. When a persistent event with a timeout + * becomes active, its timeout is reset: this means you can use persistent + * events to implement periodic timeouts. + * + * This should be treated as an opaque structure; you should never read or + * write any of its fields directly. For backward compatibility with old + * code, it is defined in the event2/event_struct.h header; including this + * header may make your code incompatible with other versions of Libevent. + * + * @see event_new(), event_free(), event_assign(), event_get_assignment(), + * event_add(), event_del(), event_active(), event_pending(), + * event_get_fd(), event_get_base(), event_get_events(), + * event_get_callback(), event_get_callback_arg(), + * event_priority_set() + */ +struct event +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Configuration for an event_base. + * + * There are many options that can be used to alter the behavior and + * implementation of an event_base. To avoid having to pass them all in a + * complex many-argument constructor, we provide an abstract data type + * wrhere you set up configation information before passing it to + * event_base_new_with_config(). + * + * @see event_config_new(), event_config_free(), event_base_new_with_config(), + * event_config_avoid_method(), event_config_require_features(), + * event_config_set_flag(), event_config_set_num_cpus_hint() + */ +struct event_config +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Enable some relatively expensive debugging checks in Libevent that + * would normally be turned off. Generally, these checks cause code that + * would otherwise crash mysteriously to fail earlier with an assertion + * failure. Note that this method MUST be called before any events or + * event_bases have been created. + * + * Debug mode can currently catch the following errors: + * An event is re-assigned while it is added + * Any function is called on a non-assigned event + * + * Note that debugging mode uses memory to track every event that has been + * initialized (via event_assign, event_set, or event_new) but not yet + * released (via event_free or event_debug_unassign). If you want to use + * debug mode, and you find yourself running out of memory, you will need + * to use event_debug_unassign to explicitly stop tracking events that + * are no longer considered set-up. + * + * @see event_debug_unassign() + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_mode(void); + +/** + * When debugging mode is enabled, informs Libevent that an event should no + * longer be considered as assigned. When debugging mode is not enabled, does + * nothing. + * + * This function must only be called on a non-added event. + * + * @see event_enable_debug_mode() + */ +EVENT2_EXPORT_SYMBOL +void event_debug_unassign(struct event *); + +/** + * Create and return a new event_base to use with the rest of Libevent. + * + * @return a new event_base on success, or NULL on failure. + * + * @see event_base_free(), event_base_new_with_config() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new(void); + +/** + Reinitialize the event base after a fork + + Some event mechanisms do not survive across fork. The event base needs + to be reinitialized with the event_reinit() function. + + @param base the event base that needs to be re-initialized + @return 0 if successful, or -1 if some events could not be re-added. + @see event_base_new() +*/ +EVENT2_EXPORT_SYMBOL +int event_reinit(struct event_base *base); + +/** + Event dispatching loop + + This loop will run the event base until either there are no more pending or + active, or until something calls event_base_loopbreak() or + event_base_loopexit(). + + @param base the event_base structure returned by event_base_new() or + event_base_new_with_config() + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loop() + */ +EVENT2_EXPORT_SYMBOL +int event_base_dispatch(struct event_base *); + +/** + Get the kernel event notification mechanism used by Libevent. + + @param eb the event_base structure returned by event_base_new() + @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) + */ +EVENT2_EXPORT_SYMBOL +const char *event_base_get_method(const struct event_base *); + +/** + Gets all event notification mechanisms supported by Libevent. + + This functions returns the event mechanism in order preferred by + Libevent. Note that this list will include all backends that + Libevent has compiled-in support for, and will not necessarily check + your OS to see whether it has the required resources. + + @return an array with pointers to the names of support methods. + The end of the array is indicated by a NULL pointer. If an + error is encountered NULL is returned. +*/ +EVENT2_EXPORT_SYMBOL +const char **event_get_supported_methods(void); + +/** Query the current monotonic time from a the timer for a struct + * event_base. + */ +EVENT2_EXPORT_SYMBOL +int event_gettime_monotonic(struct event_base *base, struct timeval *tp); + +/** + @name event type flag + + Flags to pass to event_base_get_num_events() to specify the kinds of events + we want to aggregate counts for +*/ +/**@{*/ +/** count the number of active events, which have been triggered.*/ +#define EVENT_BASE_COUNT_ACTIVE 1U +/** count the number of virtual events, which is used to represent an internal + * condition, other than a pending event, that keeps the loop from exiting. */ +#define EVENT_BASE_COUNT_VIRTUAL 2U +/** count the number of events which have been added to event base, including + * internal events. */ +#define EVENT_BASE_COUNT_ADDED 4U +/**@}*/ + +/** + Gets the number of events in event_base, as specified in the flags. + + Since event base has some internal events added to make some of its + functionalities work, EVENT_BASE_COUNT_ADDED may return more than the + number of events you added using event_add(). + + If you pass EVENT_BASE_COUNT_ACTIVE and EVENT_BASE_COUNT_ADDED together, an + active event will be counted twice. However, this might not be the case in + future libevent versions. The return value is an indication of the work + load, but the user shouldn't rely on the exact value as this may change in + the future. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @return the number of events specified in the flags +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_num_events(struct event_base *, unsigned int); + +/** + Get the maximum number of events in a given event_base as specified in the + flags. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @param clear option used to reset the maximum count. + @return the number of events specified in the flags + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_max_events(struct event_base *, unsigned int, int); + +/** + Allocates a new event configuration object. + + The event configuration object can be used to change the behavior of + an event base. + + @return an event_config object that can be used to store configuration, or + NULL if an error is encountered. + @see event_base_new_with_config(), event_config_free(), event_config +*/ +EVENT2_EXPORT_SYMBOL +struct event_config *event_config_new(void); + +/** + Deallocates all memory associated with an event configuration object + + @param cfg the event configuration object to be freed. +*/ +EVENT2_EXPORT_SYMBOL +void event_config_free(struct event_config *cfg); + +/** + Enters an event method that should be avoided into the configuration. + + This can be used to avoid event mechanisms that do not support certain + file descriptor types, or for debugging to avoid certain event + mechanisms. An application can make use of multiple event bases to + accommodate incompatible file descriptor types. + + @param cfg the event configuration object + @param method the name of the event method to avoid + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int event_config_avoid_method(struct event_config *cfg, const char *method); + +/** + A flag used to describe which features an event_base (must) provide. + + Because of OS limitations, not every Libevent backend supports every + possible feature. You can use this type with + event_config_require_features() to tell Libevent to only proceed if your + event_base implements a given feature, and you can receive this type from + event_base_get_features() to see which features are available. +*/ +enum event_method_feature { + /** Require an event method that allows edge-triggered events with EV_ET. */ + EV_FEATURE_ET = 0x01, + /** Require an event method where having one event triggered among + * many is [approximately] an O(1) operation. This excludes (for + * example) select and poll, which are approximately O(N) for N + * equal to the total number of possible events. */ + EV_FEATURE_O1 = 0x02, + /** Require an event method that allows file descriptors as well as + * sockets. */ + EV_FEATURE_FDS = 0x04, + /** Require an event method that allows you to use EV_CLOSED to detect + * connection close without the necessity of reading all the pending data. + * + * Methods that do support EV_CLOSED may not be able to provide support on + * all kernel versions. + **/ + EV_FEATURE_EARLY_CLOSE = 0x08 +}; + +/** + A flag passed to event_config_set_flag(). + + These flags change the behavior of an allocated event_base. + + @see event_config_set_flag(), event_base_new_with_config(), + event_method_feature + */ +enum event_base_config_flag { + /** Do not allocate a lock for the event base, even if we have + locking set up. + + Setting this option will make it unsafe and nonfunctional to call + functions on the base concurrently from multiple threads. + */ + EVENT_BASE_FLAG_NOLOCK = 0x01, + /** Do not check the EVENT_* environment variables when configuring + an event_base */ + EVENT_BASE_FLAG_IGNORE_ENV = 0x02, + /** Windows only: enable the IOCP dispatcher at startup + + If this flag is set then bufferevent_socket_new() and + evconn_listener_new() will use IOCP-backed implementations + instead of the usual select-based one on Windows. + */ + EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, + /** Instead of checking the current time every time the event loop is + ready to run timeout callbacks, check after each timeout callback. + */ + EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, + + /** If we are using the epoll backend, this flag says that it is + safe to use Libevent's internal change-list code to batch up + adds and deletes in order to try to do as few syscalls as + possible. Setting this flag can make your code run faster, but + it may trigger a Linux bug: it is not safe to use this flag + if you have any fds cloned by dup() or its variants. Doing so + will produce strange and hard-to-diagnose bugs. + + This flag can also be activated by setting the + EVENT_EPOLL_USE_CHANGELIST environment variable. + + This flag has no effect if you wind up using a backend other than + epoll. + */ + EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, + + /** Ordinarily, Libevent implements its time and timeout code using + the fastest monotonic timer that we have. If this flag is set, + however, we use less efficient more precise timer, assuming one is + present. + */ + EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 +}; + +/** + Return a bitmask of the features implemented by an event base. This + will be a bitwise OR of one or more of the values of + event_method_feature + + @see event_method_feature + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_features(const struct event_base *base); + +/** + Enters a required event method feature that the application demands. + + Note that not every feature or combination of features is supported + on every platform. Code that requests features should be prepared + to handle the case where event_base_new_with_config() returns NULL, as in: + <pre> + event_config_require_features(cfg, EV_FEATURE_ET); + base = event_base_new_with_config(cfg); + if (base == NULL) { + // We can't get edge-triggered behavior here. + event_config_require_features(cfg, 0); + base = event_base_new_with_config(cfg); + } + </pre> + + @param cfg the event configuration object + @param feature a bitfield of one or more event_method_feature values. + Replaces values from previous calls to this function. + @return 0 on success, -1 on failure. + @see event_method_feature, event_base_new_with_config() +*/ +EVENT2_EXPORT_SYMBOL +int event_config_require_features(struct event_config *cfg, int feature); + +/** + * Sets one or more flags to configure what parts of the eventual event_base + * will be initialized, and how they'll work. + * + * @see event_base_config_flags, event_base_new_with_config() + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_flag(struct event_config *cfg, int flag); + +/** + * Records a hint for the number of CPUs in the system. This is used for + * tuning thread pools, etc, for optimal performance. In Libevent 2.0, + * it is only on Windows, and only when IOCP is in use. + * + * @param cfg the event configuration object + * @param cpus the number of cpus + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus); + +/** + * Record an interval and/or a number of callbacks after which the event base + * should check for new events. By default, the event base will run as many + * events are as activated at the higest activated priority before checking + * for new events. If you configure it by setting max_interval, it will check + * the time after each callback, and not allow more than max_interval to + * elapse before checking for new events. If you configure it by setting + * max_callbacks to a value >= 0, it will run no more than max_callbacks + * callbacks before checking for new events. + * + * This option can decrease the latency of high-priority events, and + * avoid priority inversions where multiple low-priority events keep us from + * polling for high-priority events, but at the expense of slightly decreasing + * the throughput. Use it with caution! + * + * @param cfg The event_base configuration object. + * @param max_interval An interval after which Libevent should stop running + * callbacks and check for more events, or NULL if there should be + * no such interval. + * @param max_callbacks A number of callbacks after which Libevent should + * stop running callbacks and check for more events, or -1 if there + * should be no such limit. + * @param min_priority A priority below which max_interval and max_callbacks + * should not be enforced. If this is set to 0, they are enforced + * for events of every priority; if it's set to 1, they're enforced + * for events of priority 1 and above, and so on. + * @return 0 on success, -1 on failure. + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_max_dispatch_interval(struct event_config *cfg, + const struct timeval *max_interval, int max_callbacks, + int min_priority); + +/** + Initialize the event API. + + Use event_base_new_with_config() to initialize a new event base, taking + the specified configuration under consideration. The configuration object + can currently be used to avoid certain event notification mechanisms. + + @param cfg the event configuration object + @return an initialized event_base that can be used to registering events, + or NULL if no event base can be created with the requested event_config. + @see event_base_new(), event_base_free(), event_init(), event_assign() +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new_with_config(const struct event_config *); + +/** + Deallocate all memory associated with an event_base, and free the base. + + Note that this function will not close any fds or free any memory passed + to event_new as the argument to callback. + + If there are any pending finalizer callbacks, this function will invoke + them. + + @param eb an event_base to be freed + */ +EVENT2_EXPORT_SYMBOL +void event_base_free(struct event_base *); + +/** + As event_free, but do not run finalizers. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +void event_base_free_nofinalize(struct event_base *); + +/** @name Log severities + */ +/**@{*/ +#define EVENT_LOG_DEBUG 0 +#define EVENT_LOG_MSG 1 +#define EVENT_LOG_WARN 2 +#define EVENT_LOG_ERR 3 +/**@}*/ + +/* Obsolete names: these are deprecated, but older programs might use them. + * They violate the reserved-identifier namespace. */ +#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG +#define _EVENT_LOG_MSG EVENT_LOG_MSG +#define _EVENT_LOG_WARN EVENT_LOG_WARN +#define _EVENT_LOG_ERR EVENT_LOG_ERR + +/** + A callback function used to intercept Libevent's log messages. + + @see event_set_log_callback + */ +typedef void (*event_log_cb)(int severity, const char *msg); +/** + Redirect Libevent's log messages. + + @param cb a function taking two arguments: an integer severity between + EVENT_LOG_DEBUG and EVENT_LOG_ERR, and a string. If cb is NULL, + then the default log is used. + + NOTE: The function you provide *must not* call any other libevent + functionality. Doing so can produce undefined behavior. + */ +EVENT2_EXPORT_SYMBOL +void event_set_log_callback(event_log_cb cb); + +/** + A function to be called if Libevent encounters a fatal internal error. + + @see event_set_fatal_callback + */ +typedef void (*event_fatal_cb)(int err); + +/** + Override Libevent's behavior in the event of a fatal internal error. + + By default, Libevent will call exit(1) if a programming error makes it + impossible to continue correct operation. This function allows you to supply + another callback instead. Note that if the function is ever invoked, + something is wrong with your program, or with Libevent: any subsequent calls + to Libevent may result in undefined behavior. + + Libevent will (almost) always log an EVENT_LOG_ERR message before calling + this function; look at the last log message to see why Libevent has died. + */ +EVENT2_EXPORT_SYMBOL +void event_set_fatal_callback(event_fatal_cb cb); + +#define EVENT_DBG_ALL 0xffffffffu +#define EVENT_DBG_NONE 0 + +/** + Turn on debugging logs and have them sent to the default log handler. + + This is a global setting; if you are going to call it, you must call this + before any calls that create an event-base. You must call it before any + multithreaded use of Libevent. + + Debug logs are verbose. + + @param which Controls which debug messages are turned on. This option is + unused for now; for forward compatibility, you must pass in the constant + "EVENT_DBG_ALL" to turn debugging logs on, or "EVENT_DBG_NONE" to turn + debugging logs off. + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_logging(ev_uint32_t which); + +/** + Associate a different event base with an event. + + The event to be associated must not be currently active or pending. + + @param eb the event base + @param ev the event + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_set(struct event_base *, struct event *); + +/** @name Loop flags + + These flags control the behavior of event_base_loop(). + */ +/**@{*/ +/** Block until we have an active event, then exit once all active events + * have had their callbacks run. */ +#define EVLOOP_ONCE 0x01 +/** Do not block: see which events are ready now, run the callbacks + * of the highest-priority ones, then exit. */ +#define EVLOOP_NONBLOCK 0x02 +/** Do not exit the loop because we have no pending events. Instead, keep + * running until event_base_loopexit() or event_base_loopbreak() makes us + * stop. + */ +#define EVLOOP_NO_EXIT_ON_EMPTY 0x04 +/**@}*/ + +/** + Wait for events to become active, and run their callbacks. + + This is a more flexible version of event_base_dispatch(). + + By default, this loop will run the event base until either there are no more + pending or active events, or until something calls event_base_loopbreak() or + event_base_loopexit(). You can override this behavior with the 'flags' + argument. + + @param eb the event_base structure returned by event_base_new() or + event_base_new_with_config() + @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loopexit(), event_base_dispatch(), EVLOOP_ONCE, + EVLOOP_NONBLOCK + */ +EVENT2_EXPORT_SYMBOL +int event_base_loop(struct event_base *, int); + +/** + Exit the event loop after the specified time + + The next event_base_loop() iteration after the given timer expires will + complete normally (handling all queued events) then exit without + blocking for events again. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @param tv the amount of time after which the loop should terminate, + or NULL to exit after running all currently active events. + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopexit(struct event_base *, const struct timeval *); + +/** + Abort the active event_base_loop() immediately. + + event_base_loop() will abort the loop after the next event is completed; + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopbreak(struct event_base *); + +/** + Tell the active event_base_loop() to scan for new events immediately. + + Calling this function makes the currently active event_base_loop() + start the loop over again (scanning for new events) after the current + event callback finishes. If the event loop is not running, this + function has no effect. + + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "continue;" statement. + + Subsequent invocations of event loop will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopcontinue(struct event_base *); + +/** + Checks if the event loop was told to exit by event_base_loopexit(). + + This function will return true for an event_base at every point after + event_loopexit() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopexit() was called on this event base, + or 0 otherwise + @see event_base_loopexit() + @see event_base_got_break() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_exit(struct event_base *); + +/** + Checks if the event loop was told to abort immediately by event_base_loopbreak(). + + This function will return true for an event_base at every point after + event_base_loopbreak() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopbreak() was called on this event base, + or 0 otherwise + @see event_base_loopbreak() + @see event_base_got_exit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_break(struct event_base *); + +/** + * @name event flags + * + * Flags to pass to event_new(), event_assign(), event_pending(), and + * anything else with an argument of the form "short events" + */ +/**@{*/ +/** Indicates that a timeout has occurred. It's not necessary to pass + * this flag to event_for new()/event_assign() to get a timeout. */ +#define EV_TIMEOUT 0x01 +/** Wait for a socket or FD to become readable */ +#define EV_READ 0x02 +/** Wait for a socket or FD to become writeable */ +#define EV_WRITE 0x04 +/** Wait for a POSIX signal to be raised*/ +#define EV_SIGNAL 0x08 +/** + * Persistent event: won't get removed automatically when activated. + * + * When a persistent event with a timeout becomes activated, its timeout + * is reset to 0. + */ +#define EV_PERSIST 0x10 +/** Select edge-triggered behavior, if supported by the backend. */ +#define EV_ET 0x20 +/** + * If this option is provided, then event_del() will not block in one thread + * while waiting for the event callback to complete in another thread. + * + * To use this option safely, you may need to use event_finalize() or + * event_free_finalize() in order to safely tear down an event in a + * multithreaded application. See those functions for more information. + * + * THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + * BECOMES STABLE. + **/ +#define EV_FINALIZE 0x40 +/** + * Detects connection close events. You can use this to detect when a + * connection has been closed, without having to read all the pending data + * from a connection. + * + * Not all backends support EV_CLOSED. To detect or require it, use the + * feature flag EV_FEATURE_EARLY_CLOSE. + **/ +#define EV_CLOSED 0x80 +/**@}*/ + +/** + @name evtimer_* macros + + Aliases for working with one-shot timer events */ +/**@{*/ +#define evtimer_assign(ev, b, cb, arg) \ + event_assign((ev), (b), -1, 0, (cb), (arg)) +#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) +#define evtimer_add(ev, tv) event_add((ev), (tv)) +#define evtimer_del(ev) event_del(ev) +#define evtimer_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define evtimer_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name evsignal_* macros + + Aliases for working with signal events + */ +/**@{*/ +#define evsignal_add(ev, tv) event_add((ev), (tv)) +#define evsignal_assign(ev, b, x, cb, arg) \ + event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg)) +#define evsignal_new(b, x, cb, arg) \ + event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define evsignal_del(ev) event_del(ev) +#define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define evsignal_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + A callback function for an event. + + It receives three arguments: + + @param fd An fd or signal + @param events One or more EV_* flags + @param arg A user-supplied argument. + + @see event_new() + */ +typedef void (*event_callback_fn)(evutil_socket_t, short, void *); + +/** + Return a value used to specify that the event itself must be used as the callback argument. + + The function event_new() takes a callback argument which is passed + to the event's callback function. To specify that the argument to be + passed to the callback function is the event that event_new() returns, + pass in the return value of event_self_cbarg() as the callback argument + for event_new(). + + For example: + <pre> + struct event *ev = event_new(base, sock, events, callback, %event_self_cbarg()); + </pre> + + For consistency with event_new(), it is possible to pass the return value + of this function as the callback argument for event_assign() – this + achieves the same result as passing the event in directly. + + @return a value to be passed as the callback argument to event_new() or + event_assign(). + @see event_new(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +void *event_self_cbarg(void); + +/** + Allocate and asssign a new event structure, ready to be added. + + The function event_new() returns a new event that can be used in + future calls to event_add() and event_del(). The fd and events + arguments determine which conditions will trigger the event; the + callback and callback_arg arguments tell Libevent what to do when the + event becomes active. + + If events contains one of EV_READ, EV_WRITE, or EV_READ|EV_WRITE, then + fd is a file descriptor or socket that should get monitored for + readiness to read, readiness to write, or readiness for either operation + (respectively). If events contains EV_SIGNAL, then fd is a signal + number to wait for. If events contains none of those flags, then the + event can be triggered only by a timeout or by manual activation with + event_active(): In this case, fd must be -1. + + The EV_PERSIST flag can also be passed in the events argument: it makes + event_add() persistent until event_del() is called. + + The EV_ET flag is compatible with EV_READ and EV_WRITE, and supported + only by certain backends. It tells Libevent to use edge-triggered + events. + + The EV_TIMEOUT flag has no effect here. + + It is okay to have multiple events all listening on the same fds; but + they must either all be edge-triggered, or all not be edge triggerd. + + When the event becomes active, the event loop will run the provided + callbuck function, with three arguments. The first will be the provided + fd value. The second will be a bitfield of the events that triggered: + EV_READ, EV_WRITE, or EV_SIGNAL. Here the EV_TIMEOUT flag indicates + that a timeout occurred, and EV_ET indicates that an edge-triggered + event occurred. The third event will be the callback_arg pointer that + you provide. + + @param base the event base to which the event should be attached. + @param fd the file descriptor or signal to be monitored, or -1. + @param events desired events to monitor: bitfield of EV_READ, EV_WRITE, + EV_SIGNAL, EV_PERSIST, EV_ET. + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return a newly allocated struct event that must later be freed with + event_free(). + @see event_free(), event_add(), event_del(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *); + + +/** + Prepare a new, already-allocated event structure to be added. + + The function event_assign() prepares the event structure ev to be used + in future calls to event_add() and event_del(). Unlike event_new(), it + doesn't allocate memory itself: it requires that you have already + allocated a struct event, probably on the heap. Doing this will + typically make your code depend on the size of the event structure, and + thereby create incompatibility with future versions of Libevent. + + The easiest way to avoid this problem is just to use event_new() and + event_free() instead. + + A slightly harder way to future-proof your code is to use + event_get_struct_event_size() to determine the required size of an event + at runtime. + + Note that it is NOT safe to call this function on an event that is + active or pending. Doing so WILL corrupt internal data structures in + Libevent, and lead to strange, hard-to-diagnose bugs. You _can_ use + event_assign to change an existing event, but only if it is not active + or pending! + + The arguments for this function, and the behavior of the events that it + makes, are as for event_new(). + + @param ev an event struct to be modified + @param base the event base to which ev should be attached. + @param fd the file descriptor to be monitored + @param events desired events to monitor; can be EV_READ and/or EV_WRITE + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return 0 if success, or -1 on invalid arguments. + + @see event_new(), event_add(), event_del(), event_base_once(), + event_get_struct_event_size() + */ +EVENT2_EXPORT_SYMBOL +int event_assign(struct event *, struct event_base *, evutil_socket_t, short, event_callback_fn, void *); + +/** + Deallocate a struct event * returned by event_new(). + + If the event is pending or active, first make it non-pending and + non-active. + */ +EVENT2_EXPORT_SYMBOL +void event_free(struct event *); + +/** + * Callback type for event_finalize and event_free_finalize(). + * + * THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + * BECOMES STABLE. + * + **/ +typedef void (*event_finalize_callback_fn)(struct event *, void *); +/** + @name Finalization functions + + These functions are used to safely tear down an event in a multithreaded + application. If you construct your events with EV_FINALIZE to avoid + deadlocks, you will need a way to remove an event in the certainty that + it will definitely not be running its callback when you deallocate it + and its callback argument. + + To do this, call one of event_finalize() or event_free_finalize with + 0 for its first argument, the event to tear down as its second argument, + and a callback function as its third argument. The callback will be + invoked as part of the event loop, with the event's priority. + + After you call a finalizer function, event_add() and event_active() will + no longer work on the event, and event_del() will produce a no-op. You + must not try to change the event's fields with event_assign() or + event_set() while the finalize callback is in progress. Once the + callback has been invoked, you should treat the event structure as + containing uninitialized memory. + + The event_free_finalize() function frees the event after it's finalized; + event_finalize() does not. + + A finalizer callback must not make events pending or active. It must not + add events, activate events, or attempt to "resucitate" the event being + finalized in any way. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + + @return 0 on succes, -1 on failure. + */ +/**@{*/ +EVENT2_EXPORT_SYMBOL +int event_finalize(unsigned, struct event *, event_finalize_callback_fn); +EVENT2_EXPORT_SYMBOL +int event_free_finalize(unsigned, struct event *, event_finalize_callback_fn); +/**@}*/ + +/** + Schedule a one-time event + + The function event_base_once() is similar to event_new(). However, it + schedules a callback to be called exactly once, and does not require the + caller to prepare an event structure. + + Note that in Libevent 2.0 and earlier, if the event is never triggered, the + internal memory used to hold it will never be freed. In Libevent 2.1, + the internal memory will get freed by event_base_free() if the event + is never triggered. The 'arg' value, however, will not get freed in either + case--you'll need to free that on your own if you want it to go away. + + @param base an event_base + @param fd a file descriptor to monitor, or -1 for no fd. + @param events event(s) to monitor; can be any of EV_READ | + EV_WRITE, or EV_TIMEOUT + @param callback callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + @param timeout the maximum amount of time to wait for the event. NULL + makes an EV_READ/EV_WRITE event make forever; NULL makes an + EV_TIMEOUT event succees immediately. + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_fn, void *, const struct timeval *); + +/** + Add an event to the set of pending events. + + The function event_add() schedules the execution of the event 'ev' when the + condition specified by event_assign() or event_new() occurs, or when the time + specified in timeout has elapesed. If atimeout is NULL, no timeout + occurs and the function will only be + called if a matching event occurs. The event in the + ev argument must be already initialized by event_assign() or event_new() + and may not be used + in calls to event_assign() until it is no longer pending. + + If the event in the ev argument already has a scheduled timeout, calling + event_add() replaces the old timeout with the new one if tv is non-NULL. + + @param ev an event struct initialized via event_assign() or event_new() + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_del(), event_assign(), event_new() + */ +EVENT2_EXPORT_SYMBOL +int event_add(struct event *ev, const struct timeval *timeout); + +/** + Remove a timer from a pending event without removing the event itself. + + If the event has a scheduled timeout, this function unschedules it but + leaves the event otherwise pending. + + @param ev an event struct initialized via event_assign() or event_new() + @return 0 on success, or -1 if an error occurrect. +*/ +EVENT2_EXPORT_SYMBOL +int event_remove_timer(struct event *ev); + +/** + Remove an event from the set of monitored events. + + The function event_del() will cancel the event in the argument ev. If the + event has already executed or has never been added the call will have no + effect. + + @param ev an event struct to be removed from the working set + @return 0 if successful, or -1 if an error occurred + @see event_add() + */ +EVENT2_EXPORT_SYMBOL +int event_del(struct event *); + +/** + As event_del(), but never blocks while the event's callback is running + in another thread, even if the event was constructed without the + EV_FINALIZE flag. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +int event_del_noblock(struct event *ev); +/** + As event_del(), but always blocks while the event's callback is running + in another thread, even if the event was constructed with the + EV_FINALIZE flag. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +int event_del_block(struct event *ev); + +/** + Make an event active. + + You can use this function on a pending or a non-pending event to make it + active, so that its callback will be run by event_base_dispatch() or + event_base_loop(). + + One common use in multithreaded programs is to wake the thread running + event_base_loop() from another thread. + + @param ev an event to make active. + @param res a set of flags to pass to the event's callback. + @param ncalls an obsolete argument: this is ignored. + **/ +EVENT2_EXPORT_SYMBOL +void event_active(struct event *ev, int res, short ncalls); + +/** + Checks if a specific event is pending or scheduled. + + @param ev an event struct previously passed to event_add() + @param events the requested event type; any of EV_TIMEOUT|EV_READ| + EV_WRITE|EV_SIGNAL + @param tv if this field is not NULL, and the event has a timeout, + this field is set to hold the time at which the timeout will + expire. + + @return true if the event is pending on any of the events in 'what', (that + is to say, it has been added), or 0 if the event is not added. + */ +EVENT2_EXPORT_SYMBOL +int event_pending(const struct event *ev, short events, struct timeval *tv); + +/** + If called from within the callback for an event, returns that event. + + The behavior of this function is not defined when called from outside the + callback function for an event. + */ +EVENT2_EXPORT_SYMBOL +struct event *event_base_get_running_event(struct event_base *base); + +/** + Test if an event structure might be initialized. + + The event_initialized() function can be used to check if an event has been + initialized. + + Warning: This function is only useful for distinguishing a a zeroed-out + piece of memory from an initialized event, it can easily be confused by + uninitialized memory. Thus, it should ONLY be used to distinguish an + initialized event from zero. + + @param ev an event structure to be tested + @return 1 if the structure might be initialized, or 0 if it has not been + initialized + */ +EVENT2_EXPORT_SYMBOL +int event_initialized(const struct event *ev); + +/** + Get the signal number assigned to a signal event +*/ +#define event_get_signal(ev) ((int)event_get_fd(ev)) + +/** + Get the socket or signal assigned to an event, or -1 if the event has + no socket. +*/ +EVENT2_EXPORT_SYMBOL +evutil_socket_t event_get_fd(const struct event *ev); + +/** + Get the event_base associated with an event. +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_get_base(const struct event *ev); + +/** + Return the events (EV_READ, EV_WRITE, etc) assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +short event_get_events(const struct event *ev); + +/** + Return the callback assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +event_callback_fn event_get_callback(const struct event *ev); + +/** + Return the callback argument assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +void *event_get_callback_arg(const struct event *ev); + +/** + Return the priority of an event. + @see event_priority_init(), event_get_priority() +*/ +EVENT2_EXPORT_SYMBOL +int event_get_priority(const struct event *ev); + +/** + Extract _all_ of arguments given to construct a given event. The + event_base is copied into *base_out, the fd is copied into *fd_out, and so + on. + + If any of the "_out" arguments is NULL, it will be ignored. + */ +EVENT2_EXPORT_SYMBOL +void event_get_assignment(const struct event *event, + struct event_base **base_out, evutil_socket_t *fd_out, short *events_out, + event_callback_fn *callback_out, void **arg_out); + +/** + Return the size of struct event that the Libevent library was compiled + with. + + This will be NO GREATER than sizeof(struct event) if you're running with + the same version of Libevent that your application was built with, but + otherwise might not. + + Note that it might be SMALLER than sizeof(struct event) if some future + version of Libevent adds extra padding to the end of struct event. + We might do this to help ensure ABI-compatibility between different + versions of Libevent. + */ +EVENT2_EXPORT_SYMBOL +size_t event_get_struct_event_size(void); + +/** + Get the Libevent version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers that you've + compiled against. + + @return a string containing the version number of Libevent +*/ +EVENT2_EXPORT_SYMBOL +const char *event_get_version(void); + +/** + Return a numeric representation of Libevent's version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers you've used to + compile. + + The format uses one byte each for the major, minor, and patchlevel parts of + the version number. The low-order byte is unused. For example, version + 2.0.1-alpha has a numeric representation of 0x02000100 +*/ +EVENT2_EXPORT_SYMBOL +ev_uint32_t event_get_version_number(void); + +/** As event_get_version, but gives the version of Libevent's headers. */ +#define LIBEVENT_VERSION EVENT__VERSION +/** As event_get_version_number, but gives the version number of Libevent's + * headers. */ +#define LIBEVENT_VERSION_NUMBER EVENT__NUMERIC_VERSION + +/** Largest number of priorities that Libevent can support. */ +#define EVENT_MAX_PRIORITIES 256 +/** + Set the number of different event priorities + + By default Libevent schedules all active events with the same priority. + However, some time it is desirable to process some events with a higher + priority than others. For that reason, Libevent supports strict priority + queues. Active events with a lower priority are always processed before + events with a higher priority. + + The number of different priorities can be set initially with the + event_base_priority_init() function. This function should be called + before the first call to event_base_dispatch(). The + event_priority_set() function can be used to assign a priority to an + event. By default, Libevent assigns the middle priority to all events + unless their priority is explicitly set. + + Note that urgent-priority events can starve less-urgent events: after + running all urgent-priority callbacks, Libevent checks for more urgent + events again, before running less-urgent events. Less-urgent events + will not have their callbacks run until there are no events more urgent + than them that want to be active. + + @param eb the event_base structure returned by event_base_new() + @param npriorities the maximum number of priorities + @return 0 if successful, or -1 if an error occurred + @see event_priority_set() + */ +EVENT2_EXPORT_SYMBOL +int event_base_priority_init(struct event_base *, int); + +/** + Get the number of different event priorities. + + @param eb the event_base structure returned by event_base_new() + @return Number of different event priorities + @see event_base_priority_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_npriorities(struct event_base *eb); + +/** + Assign a priority to an event. + + @param ev an event struct + @param priority the new priority to be assigned + @return 0 if successful, or -1 if an error occurred + @see event_priority_init(), event_get_priority() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_set(struct event *, int); + +/** + Prepare an event_base to use a large number of timeouts with the same + duration. + + Libevent's default scheduling algorithm is optimized for having a large + number of timeouts with their durations more or less randomly + distributed. But if you have a large number of timeouts that all have + the same duration (for example, if you have a large number of + connections that all have a 10-second timeout), then you can improve + Libevent's performance by telling Libevent about it. + + To do this, call this function with the common duration. It will return a + pointer to a different, opaque timeout value. (Don't depend on its actual + contents!) When you use this timeout value in event_add(), Libevent will + schedule the event more efficiently. + + (This optimization probably will not be worthwhile until you have thousands + or tens of thousands of events with the same timeout.) + */ +EVENT2_EXPORT_SYMBOL +const struct timeval *event_base_init_common_timeout(struct event_base *base, + const struct timeval *duration); + +#if !defined(EVENT__DISABLE_MM_REPLACEMENT) || defined(EVENT_IN_DOXYGEN_) +/** + Override the functions that Libevent uses for memory management. + + Usually, Libevent uses the standard libc functions malloc, realloc, and + free to allocate memory. Passing replacements for those functions to + event_set_mem_functions() overrides this behavior. + + Note that all memory returned from Libevent will be allocated by the + replacement functions rather than by malloc() and realloc(). Thus, if you + have replaced those functions, it will not be appropriate to free() memory + that you get from Libevent. Instead, you must use the free_fn replacement + that you provided. + + Note also that if you are going to call this function, you should do so + before any call to any Libevent function that does allocation. + Otherwise, those funtions will allocate their memory using malloc(), but + then later free it using your provided free_fn. + + @param malloc_fn A replacement for malloc. + @param realloc_fn A replacement for realloc + @param free_fn A replacement for free. + **/ +EVENT2_EXPORT_SYMBOL +void event_set_mem_functions( + void *(*malloc_fn)(size_t sz), + void *(*realloc_fn)(void *ptr, size_t sz), + void (*free_fn)(void *ptr)); +/** This definition is present if Libevent was built with support for + event_set_mem_functions() */ +#define EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED +#endif + +/** + Writes a human-readable description of all inserted and/or active + events to a provided stdio stream. + + This is intended for debugging; its format is not guaranteed to be the same + between libevent versions. + + @param base An event_base on which to scan the events. + @param output A stdio file to write on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_dump_events(struct event_base *, FILE *); + + +/** + Activates all pending events for the given fd and event mask. + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param fd An fd to active events on. + @param events One or more of EV_{READ,WRITE}. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events); + +/** + Activates all pending signals with a given signal number + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param fd The signal to active events on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_signal(struct event_base *base, int sig); + +/** + * Callback for iterating events in an event base via event_base_foreach_event + */ +typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *); + +/** + Iterate over all added or active events events in an event loop, and invoke + a given callback on each one. + + The callback must not call any function that modifies the event base, that + modifies any event in the event base, or that adds or removes any event to + the event base. Doing so is unsupported and will lead to undefined + behavior -- likely, to crashes. + + event_base_foreach_event() holds a lock on the event_base() for the whole + time it's running: slow callbacks are not advisable. + + Note that Libevent adds some events of its own to make pieces of its + functionality work. You must not assume that the only events you'll + encounter will be the ones you added yourself. + + The callback function must return 0 to continue iteration, or some other + integer to stop iterating. + + @param base An event_base on which to scan the events. + @param fn A callback function to receive the events. + @param arg An argument passed to the callback function. + @return 0 if we iterated over every event, or the value returned by the + callback function if the loop exited early. +*/ +EVENT2_EXPORT_SYMBOL +int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg); + + +/** Sets 'tv' to the current time (as returned by gettimeofday()), + looking at the cached value in 'base' if possible, and calling + gettimeofday() or clock_gettime() as appropriate if there is no + cached time. + + Generally, this value will only be cached while actually + processing event callbacks, and may be very inaccuate if your + callbacks take a long time to execute. + + Returns 0 on success, negative on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_gettimeofday_cached(struct event_base *base, + struct timeval *tv); + +/** Update cached_tv in the 'base' to the current time + * + * You can use this function is useful for selectively increasing + * the accuracy of the cached time value in 'base' during callbacks + * that take a long time to execute. + * + * This function has no effect if the base is currently not in its + * event loop, or if timeval caching is disabled via + * EVENT_BASE_FLAG_NO_CACHE_TIME. + * + * @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int event_base_update_cache_time(struct event_base *base); + +/** Release up all globally-allocated resources allocated by Libevent. + + This function does not free developer-controlled resources like + event_bases, events, bufferevents, listeners, and so on. It only releases + resources like global locks that there is no other way to free. + + It is not actually necessary to call this function before exit: every + resource that it frees would be released anyway on exit. It mainly exists + so that resource-leak debugging tools don't see Libevent as holding + resources at exit. + + You should only call this function when no other Libevent functions will + be invoked -- e.g., when cleanly exiting a program. + */ +EVENT2_EXPORT_SYMBOL +void libevent_global_shutdown(void); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/event_compat.h b/ios/Pods/CocoaLibEvent/src/event2/event_compat.h new file mode 100644 index 000000000..5110175a1 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/event_compat.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_COMPAT_H_INCLUDED_ +#define EVENT2_EVENT_COMPAT_H_INCLUDED_ + +/** @file event2/event_compat.h + + Potentially non-threadsafe versions of the functions in event.h: provided + only for backwards compatibility. + + In the oldest versions of Libevent, event_base was not a first-class + structure. Instead, there was a single event base that every function + manipulated. Later, when separate event bases were added, the old functions + that didn't take an event_base argument needed to work by manipulating the + "current" event base. This could lead to thread-safety issues, and obscure, + hard-to-diagnose bugs. + + @deprecated All functions in this file are by definition deprecated. + */ +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/** + Initialize the event API. + + The event API needs to be initialized with event_init() before it can be + used. Sets the global current base that gets used for events that have no + base associated with them. + + @deprecated This function is deprecated because it replaces the "current" + event_base, and is totally unsafe for multithreaded use. The replacement + is event_base_new(). + + @see event_base_set(), event_base_new() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_init(void); + +/** + Loop to process events. + + Like event_base_dispatch(), but uses the "current" base. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_dispatch(). + + @see event_base_dispatch(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_dispatch(void); + +/** + Handle events. + + This function behaves like event_base_loop(), but uses the "current" base + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loop(). + + @see event_base_loop(), event_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_loop(int); + + +/** + Exit the event loop after the specified time. + + This function behaves like event_base_loopexit(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopexit(). + + @see event_init, event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_loopexit(const struct timeval *); + + +/** + Abort the active event_loop() immediately. + + This function behaves like event_base_loopbreakt(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopbreak(). + + @see event_base_loopbreak(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_loopbreak(void); + +/** + Schedule a one-time event to occur. + + @deprecated This function is obsolete, and has been replaced by + event_base_once(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_once() + */ +EVENT2_EXPORT_SYMBOL +int event_once(evutil_socket_t , short, + void (*)(evutil_socket_t, short, void *), void *, const struct timeval *); + + +/** + Get the kernel event notification mechanism used by Libevent. + + @deprecated This function is obsolete, and has been replaced by + event_base_get_method(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_get_method() + */ +EVENT2_EXPORT_SYMBOL +const char *event_get_method(void); + + +/** + Set the number of different event priorities. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_priority_init(). + + @see event_base_priority_init() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_init(int); + +/** + Prepare an event structure to be added. + + @deprecated event_set() is not recommended for new code, because it requires + a subsequent call to event_base_set() to be safe under most circumstances. + Use event_assign() or event_new() instead. + */ +EVENT2_EXPORT_SYMBOL +void event_set(struct event *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *); + +#define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define evsignal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) + + +/** + @name timeout_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evtimer_* macros instead. + @{ + */ +#define timeout_add(ev, tv) event_add((ev), (tv)) +#define timeout_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define timeout_del(ev) event_del(ev) +#define timeout_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define timeout_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name signal_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evsignal_* macros instead. + @{ + */ +#define signal_add(ev, tv) event_add((ev), (tv)) +#define signal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define signal_del(ev) event_del(ev) +#define signal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define signal_initialized(ev) event_initialized(ev) +/**@}*/ + +#ifndef EVENT_FD +/* These macros are obsolete; use event_get_fd and event_get_signal instead. */ +#define EVENT_FD(ev) ((int)event_get_fd(ev)) +#define EVENT_SIGNAL(ev) event_get_signal(ev) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/event_struct.h b/ios/Pods/CocoaLibEvent/src/event2/event_struct.h new file mode 100644 index 000000000..1c8b71b6b --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/event_struct.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_STRUCT_H_INCLUDED_ +#define EVENT2_EVENT_STRUCT_H_INCLUDED_ + +/** @file event2/event_struct.h + + Structures used by event.h. Using these structures directly WILL harm + forward compatibility: be careful. + + No field declared in this file should be used directly in user code. Except + for historical reasons, these fields would not be exposed at all. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/* For evkeyvalq */ +#include <event2/keyvalq_struct.h> + +#define EVLIST_TIMEOUT 0x01 +#define EVLIST_INSERTED 0x02 +#define EVLIST_SIGNAL 0x04 +#define EVLIST_ACTIVE 0x08 +#define EVLIST_INTERNAL 0x10 +#define EVLIST_ACTIVE_LATER 0x20 +#define EVLIST_FINALIZING 0x40 +#define EVLIST_INIT 0x80 + +#define EVLIST_ALL 0xff + +/* Fix so that people don't have to run with <sys/queue.h> */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* Fix so that people don't have to run with <sys/queue.h> */ +#ifndef LIST_ENTRY +#define EVENT_DEFINED_LISTENTRY_ +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} +#endif /* !LIST_ENTRY */ + +#ifndef LIST_HEAD +#define EVENT_DEFINED_LISTHEAD_ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ + } +#endif /* !LIST_HEAD */ + +struct event; + +struct event_callback { + TAILQ_ENTRY(event_callback) evcb_active_next; + short evcb_flags; + ev_uint8_t evcb_pri; /* smaller numbers are higher priority */ + ev_uint8_t evcb_closure; + /* allows us to adopt for different types of events */ + union { + void (*evcb_callback)(evutil_socket_t, short, void *); + void (*evcb_selfcb)(struct event_callback *, void *); + void (*evcb_evfinalize)(struct event *, void *); + void (*evcb_cbfinalize)(struct event_callback *, void *); + } evcb_cb_union; + void *evcb_arg; +}; + +struct event_base; +struct event { + struct event_callback ev_evcallback; + + /* for managing timeouts */ + union { + TAILQ_ENTRY(event) ev_next_with_common_timeout; + int min_heap_idx; + } ev_timeout_pos; + evutil_socket_t ev_fd; + + struct event_base *ev_base; + + union { + /* used for io events */ + struct { + LIST_ENTRY (event) ev_io_next; + struct timeval ev_timeout; + } ev_io; + + /* used by signal events */ + struct { + LIST_ENTRY (event) ev_signal_next; + short ev_ncalls; + /* Allows deletes in callback */ + short *ev_pncalls; + } ev_signal; + } ev_; + + short ev_events; + short ev_res; /* result passed to event callback */ + struct timeval ev_timeout; +}; + +TAILQ_HEAD (event_list, event); + +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +LIST_HEAD (event_dlist, event); + +#ifdef EVENT_DEFINED_LISTENTRY_ +#undef LIST_ENTRY +#endif + +#ifdef EVENT_DEFINED_LISTHEAD_ +#undef LIST_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_STRUCT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/http.h b/ios/Pods/CocoaLibEvent/src/event2/http.h new file mode 100644 index 000000000..8fb5642f7 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/http.h @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_H_INCLUDED_ +#define EVENT2_HTTP_H_INCLUDED_ + +/* For int types. */ +#include <event2/util.h> +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* In case we haven't included the right headers yet. */ +struct evbuffer; +struct event_base; +struct bufferevent; +struct evhttp_connection; + +/** @file event2/http.h + * + * Basic support for HTTP serving. + * + * As Libevent is a library for dealing with event notification and most + * interesting applications are networked today, I have often found the + * need to write HTTP code. The following prototypes and definitions provide + * an application with a minimal interface for making HTTP requests and for + * creating a very simple HTTP server. + */ + +/* Response codes */ +#define HTTP_OK 200 /**< request completed ok */ +#define HTTP_NOCONTENT 204 /**< request does not have content */ +#define HTTP_MOVEPERM 301 /**< the uri moved permanently */ +#define HTTP_MOVETEMP 302 /**< the uri moved temporarily */ +#define HTTP_NOTMODIFIED 304 /**< page was not modified from last */ +#define HTTP_BADREQUEST 400 /**< invalid http request was made */ +#define HTTP_NOTFOUND 404 /**< could not find content for uri */ +#define HTTP_BADMETHOD 405 /**< method not allowed for this uri */ +#define HTTP_ENTITYTOOLARGE 413 /**< */ +#define HTTP_EXPECTATIONFAILED 417 /**< we can't handle this expectation */ +#define HTTP_INTERNAL 500 /**< internal error */ +#define HTTP_NOTIMPLEMENTED 501 /**< not implemented */ +#define HTTP_SERVUNAVAIL 503 /**< the server is not available */ + +struct evhttp; +struct evhttp_request; +struct evkeyvalq; +struct evhttp_bound_socket; +struct evconnlistener; +struct evdns_base; + +/** + * Create a new HTTP server. + * + * @param base (optional) the event base to receive the HTTP events + * @return a pointer to a newly initialized evhttp server structure + * @see evhttp_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_new(struct event_base *base); + +/** + * Binds an HTTP server on the specified address and port. + * + * Can be called multiple times to bind the same http server + * to multiple different ports. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return 0 on success, -1 on failure. + * @see evhttp_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Like evhttp_bind_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_bind_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Makes an HTTP server accept connections on the specified socket. + * + * This may be useful to create a socket and then fork multiple instances + * of an http server, or when a socket has been communicated via file + * descriptor passing in situations where an http servers does not have + * permissions to bind to a low-numbered port. + * + * Can be called multiple times to have the http server listen to + * multiple different sockets. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return 0 on success, -1 on failure. + * @see evhttp_bind_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd); + +/** + * Like evhttp_accept_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_accept_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd); + +/** + * The most low-level evhttp_bind/accept method: takes an evconnlistener, and + * returns an evhttp_bound_socket. The listener will be freed when the bound + * socket is freed. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener); + +/** + * Return the listener used to implement a bound socket. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound); + +typedef void evhttp_bound_socket_foreach_fn(struct evhttp_bound_socket *, void *); +/** + * Applies the function specified in the first argument to all + * evhttp_bound_sockets associated with "http". The user must not + * attempt to free or remove any connections, sockets or listeners + * in the callback "function". + * + * @param http pointer to an evhttp object + * @param function function to apply to every bound socket + * @param argument pointer value passed to function for every socket iterated + */ +EVENT2_EXPORT_SYMBOL +void evhttp_foreach_bound_socket(struct evhttp *http, evhttp_bound_socket_foreach_fn *function, void *argument); + +/** + * Makes an HTTP server stop accepting connections on the specified socket + * + * This may be useful when a socket has been sent via file descriptor passing + * and is no longer needed by the current process. + * + * If you created this bound socket with evhttp_bind_socket_with_handle or + * evhttp_accept_socket_with_handle, this function closes the fd you provided. + * If you created this bound socket with evhttp_bind_listener, this function + * frees the listener you provided. + * + * \a bound_socket is an invalid pointer after this call returns. + * + * @param http a pointer to an evhttp object + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound_socket); + +/** + * Get the raw file descriptor referenced by an evhttp_bound_socket. + * + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @return the file descriptor used by the bound socket + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound_socket); + +/** + * Free the previously created HTTP server. + * + * Works only if no requests are currently being served. + * + * @param http the evhttp server object to be freed + * @see evhttp_start() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_free(struct evhttp* http); + +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size); +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size); + +/** + Set the value to use for the Content-Type header when none was provided. If + the content type string is NULL, the Content-Type header will not be + automatically added. + + @param http the http server on which to set the default content type + @param content_type the value for the Content-Type header +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_default_content_type(struct evhttp *http, + const char *content_type); + +/** + Sets the what HTTP methods are supported in requests accepted by this + server, and passed to user callbacks. + + If not supported they will generate a "405 Method not allowed" response. + + By default this includes the following methods: GET, POST, HEAD, PUT, DELETE + + @param http the http server on which to set the methods + @param methods bit mask constructed from evhttp_cmd_type values +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods); + +/** + Set a callback for a specified URI + + @param http the http sever on which to set the callback + @param path the path for which to invoke the callback + @param cb the callback function that gets invoked on requesting path + @param cb_arg an additional context argument for the callback + @return 0 on success, -1 if the callback existed already, -2 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_set_cb(struct evhttp *http, const char *path, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Removes the callback for a specified URI */ +EVENT2_EXPORT_SYMBOL +int evhttp_del_cb(struct evhttp *, const char *); + +/** + Set a callback for all requests that are not caught by specific callbacks + + Invokes the specified callback for all requests that do not match any of + the previously specified request paths. This is catchall for requests not + specifically configured with evhttp_set_cb(). + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for any unmatched requests + @param arg an context argument for the callback +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_gencb(struct evhttp *http, + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + Set a callback used to create new bufferevents for connections + to a given evhttp object. + + You can use this to override the default bufferevent type -- for example, + to make this evhttp object use SSL bufferevents rather than unencrypted + ones. + + New bufferevents must be allocated with no fd set on them. + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for incoming connections + @param arg an context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_bevcb(struct evhttp *http, + struct bufferevent *(*cb)(struct event_base *, void *), void *arg); + +/** + Adds a virtual host to the http server. + + A virtual host is a newly initialized evhttp object that has request + callbacks set on it via evhttp_set_cb() or evhttp_set_gencb(). It + most not have any listing sockets associated with it. + + If the virtual host has not been removed by the time that evhttp_free() + is called on the main http server, it will be automatically freed, too. + + It is possible to have hierarchical vhosts. For example: A vhost + with the pattern *.example.com may have other vhosts with patterns + foo.example.com and bar.example.com associated with it. + + @param http the evhttp object to which to add a virtual host + @param pattern the glob pattern against which the hostname is matched. + The match is case insensitive and follows otherwise regular shell + matching. + @param vhost the virtual host to add the regular http server. + @return 0 on success, -1 on failure + @see evhttp_remove_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_virtual_host(struct evhttp* http, const char *pattern, + struct evhttp* vhost); + +/** + Removes a virtual host from the http server. + + @param http the evhttp object from which to remove the virtual host + @param vhost the virtual host to remove from the regular http server. + @return 0 on success, -1 on failure + @see evhttp_add_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost); + +/** + Add a server alias to an http object. The http object can be a virtual + host or the main server. + + @param http the evhttp object + @param alias the alias to add + @see evhttp_add_remove_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_server_alias(struct evhttp *http, const char *alias); + +/** + Remove a server alias from an http object. + + @param http the evhttp object + @param alias the alias to remove + @see evhttp_add_server_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_server_alias(struct evhttp *http, const char *alias); + +/** + * Set the timeout for an HTTP request. + * + * @param http an evhttp object + * @param timeout_in_secs the timeout, in seconds + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout(struct evhttp *http, int timeout_in_secs); + +/** + * Set the timeout for an HTTP request. + * + * @param http an evhttp object + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout_tv(struct evhttp *http, const struct timeval* tv); + +/* Read all the clients body, and only after this respond with an error if the + * clients body exceed max_body_size */ +#define EVHTTP_SERVER_LINGERING_CLOSE 0x0001 +/** + * Set connection flags for HTTP server. + * + * @see EVHTTP_SERVER_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_set_flags(struct evhttp *http, int flags); + +/* Request/Response functionality */ + +/** + * Send an HTML error message to the client. + * + * @param req a request object + * @param error the HTTP error code + * @param reason a brief explanation of the error. If this is NULL, we'll + * just use the standard meaning of the error code. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_error(struct evhttp_request *req, int error, + const char *reason); + +/** + * Send an HTML reply to the client. + * + * The body of the reply consists of the data in databuf. After calling + * evhttp_send_reply() databuf will be empty, but the buffer is still + * owned by the caller and needs to be deallocated by the caller if + * necessary. + * + * @param req a request object + * @param code the HTTP response code to send + * @param reason a brief message to send with the response code + * @param databuf the body of the response + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply(struct evhttp_request *req, int code, + const char *reason, struct evbuffer *databuf); + +/* Low-level response interface, for streaming/chunked replies */ + +/** + Initiate a reply that uses Transfer-Encoding chunked. + + This allows the caller to stream the reply back to the client and is + useful when either not all of the reply data is immediately available + or when sending very large replies. + + The caller needs to supply data chunks with evhttp_send_reply_chunk() + and complete the reply by calling evhttp_send_reply_end(). + + @param req a request object + @param code the HTTP response code to send + @param reason a brief message to send with the response code +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_start(struct evhttp_request *req, int code, + const char *reason); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk(struct evhttp_request *req, + struct evbuffer *databuf); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. + @param cb callback funcion + @param call back's argument. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk_with_cb(struct evhttp_request *, struct evbuffer *, + void (*cb)(struct evhttp_connection *, void *), void *arg); + +/** + Complete a chunked reply, freeing the request as appropriate. + + @param req a request object +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_end(struct evhttp_request *req); + +/* + * Interfaces for making requests + */ + +/** The different request types supported by evhttp. These are as specified + * in RFC2616, except for PATCH which is specified by RFC5789. + * + * By default, only some of these methods are accepted and passed to user + * callbacks; use evhttp_set_allowed_methods() to change which methods + * are allowed. + */ +enum evhttp_cmd_type { + EVHTTP_REQ_GET = 1 << 0, + EVHTTP_REQ_POST = 1 << 1, + EVHTTP_REQ_HEAD = 1 << 2, + EVHTTP_REQ_PUT = 1 << 3, + EVHTTP_REQ_DELETE = 1 << 4, + EVHTTP_REQ_OPTIONS = 1 << 5, + EVHTTP_REQ_TRACE = 1 << 6, + EVHTTP_REQ_CONNECT = 1 << 7, + EVHTTP_REQ_PATCH = 1 << 8 +}; + +/** a request object can represent either a request or a reply */ +enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE }; + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param bev a bufferevent to use for connecting to the server; if NULL, a + * socket-based bufferevent will be created. This buffrevent will be freed + * when the connection closes. It must have no fd set on it. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_bufferevent_new( + struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev, const char *address, ev_uint16_t port); + +/** + * Return the bufferevent that an evhttp_connection is using. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon); + +/** + * Return the HTTP server associated with this connection, or NULL. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_connection_get_server(struct evhttp_connection *evcon); + +/** + * Creates a new request object that needs to be filled in with the request + * parameters. The callback is executed when the request completed or an + * error occurred. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_request *evhttp_request_new( + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + * Enable delivery of chunks to requestor. + * @param cb will be called after every read of data with the same argument + * as the completion callback. Will never be called on an empty + * response. May drain the input buffer; it will be drained + * automatically on return. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_chunked_cb(struct evhttp_request *, + void (*cb)(struct evhttp_request *, void *)); + +/** + * Register callback for additional parsing of request headers. + * @param cb will be called after receiving and parsing the full header. + * It allows analyzing the header and possibly closing the connection + * by returning a value < 0. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_header_cb(struct evhttp_request *, + int (*cb)(struct evhttp_request *, void *)); + +/** + * The different error types supported by evhttp + * + * @see evhttp_request_set_error_cb() + */ +enum evhttp_request_error { + /** + * Timeout reached, also @see evhttp_connection_set_timeout() + */ + EVREQ_HTTP_TIMEOUT, + /** + * EOF reached + */ + EVREQ_HTTP_EOF, + /** + * Error while reading header, or invalid header + */ + EVREQ_HTTP_INVALID_HEADER, + /** + * Error encountered while reading or writing + */ + EVREQ_HTTP_BUFFER_ERROR, + /** + * The evhttp_cancel_request() called on this request. + */ + EVREQ_HTTP_REQUEST_CANCEL, + /** + * Body is greater then evhttp_connection_set_max_body_size() + */ + EVREQ_HTTP_DATA_TOO_LONG +}; +/** + * Set a callback for errors + * @see evhttp_request_error for error types. + * + * On error, both the error callback and the regular callback will be called, + * error callback is called before the regular callback. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_error_cb(struct evhttp_request *, + void (*)(enum evhttp_request_error, void *)); + +/** + * Set a callback to be called on request completion of evhttp_send_* function. + * + * The callback function will be called on the completion of the request after + * the output data has been written and before the evhttp_request object + * is destroyed. This can be useful for tracking resources associated with a + * request (ex: timing metrics). + * + * @param req a request object + * @param cb callback function that will be called on request completion + * @param cb_arg an additional context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_on_complete_cb(struct evhttp_request *req, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Frees the request object and removes associated events. */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_free(struct evhttp_request *req); + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_new( + struct event_base *base, struct evdns_base *dnsbase, + const char *address, ev_uint16_t port); + +/** + * Set family hint for DNS requests. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_family(struct evhttp_connection *evcon, + int family); + +/* reuse connection address on retry */ +#define EVHTTP_CON_REUSE_CONNECTED_ADDR 0x0008 +/* Try to read error, since server may already send and close + * connection, but if at that time we have some data to send then we + * can send get EPIPE and fail, while we can read that HTTP error. */ +#define EVHTTP_CON_READ_ON_WRITE_ERROR 0x0010 +/* @see EVHTTP_SERVER_LINGERING_CLOSE */ +#define EVHTTP_CON_LINGERING_CLOSE 0x0020 +/* Padding for public flags, @see EVHTTP_CON_* in http-internal.h */ +#define EVHTTP_CON_PUBLIC_FLAGS_END 0x100000 +/** + * Set connection flags. + * + * @see EVHTTP_CON_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_connection_set_flags(struct evhttp_connection *evcon, + int flags); + +/** Takes ownership of the request object + * + * Can be used in a request callback to keep onto the request until + * evhttp_request_free() is explicitly called by the user. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_own(struct evhttp_request *req); + +/** Returns 1 if the request is owned by the user */ +EVENT2_EXPORT_SYMBOL +int evhttp_request_is_owned(struct evhttp_request *req); + +/** + * Returns the connection object associated with the request or NULL + * + * The user needs to either free the request explicitly or call + * evhttp_send_reply_end(). + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_request_get_connection(struct evhttp_request *req); + +/** + * Returns the underlying event_base for this connection + */ +EVENT2_EXPORT_SYMBOL +struct event_base *evhttp_connection_get_base(struct evhttp_connection *req); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon, + ev_ssize_t new_max_headers_size); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_body_size(struct evhttp_connection* evcon, + ev_ssize_t new_max_body_size); + +/** Frees an http connection */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free(struct evhttp_connection *evcon); + +/** Disowns a given connection object + * + * Can be used to tell libevent to free the connection object after + * the last request has completed or failed. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free_on_completion(struct evhttp_connection *evcon); + +/** sets the ip address from which http connections are made */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_address(struct evhttp_connection *evcon, + const char *address); + +/** sets the local port from which http connections are made */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_port(struct evhttp_connection *evcon, + ev_uint16_t port); + +/** Sets the timeout in seconds for events related to this connection */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout(struct evhttp_connection *evcon, + int timeout_in_secs); + +/** Sets the timeout for events related to this connection. Takes a struct + * timeval. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** Sets the delay before retrying requests on this connection. This is only + * used if evhttp_connection_set_retries is used to make the number of retries + * at least one. Each retry after the first is twice as long as the one before + * it. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** Sets the retry limit for this connection - -1 repeats indefinitely */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_retries(struct evhttp_connection *evcon, + int retry_max); + +/** Set a callback for connection close. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_closecb(struct evhttp_connection *evcon, + void (*)(struct evhttp_connection *, void *), void *); + +/** Get the remote address and port associated with this connection. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_get_peer(struct evhttp_connection *evcon, + char **address, ev_uint16_t *port); + +/** Get the remote address associated with this connection. + * extracted from getpeername() OR from nameserver. + * + * @return NULL if getpeername() return non success, + * or connection is not connected, + * otherwise it return pointer to struct sockaddr_storage */ +EVENT2_EXPORT_SYMBOL +const struct sockaddr* +evhttp_connection_get_addr(struct evhttp_connection *evcon); + +/** + Make an HTTP request over the specified connection. + + The connection gets ownership of the request. On failure, the + request object is no longer valid as it has been freed. + + @param evcon the evhttp_connection object over which to send the request + @param req the previously created and configured request object + @param type the request type EVHTTP_REQ_GET, EVHTTP_REQ_POST, etc. + @param uri the URI associated with the request + @return 0 on success, -1 on failure + @see evhttp_cancel_request() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri); + +/** + Cancels a pending HTTP request. + + Cancels an ongoing HTTP request. The callback associated with this request + is not executed and the request object is freed. If the request is + currently being processed, e.g. it is ongoing, the corresponding + evhttp_connection object is going to get reset. + + A request cannot be canceled if its callback has executed already. A request + may be canceled reentrantly from its chunked callback. + + @param req the evhttp_request to cancel; req becomes invalid after this call. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_cancel_request(struct evhttp_request *req); + +/** + * A structure to hold a parsed URI or Relative-Ref conforming to RFC3986. + */ +struct evhttp_uri; + +/** Returns the request URI */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_uri(const struct evhttp_request *req); +/** Returns the request URI (parsed) */ +EVENT2_EXPORT_SYMBOL +const struct evhttp_uri *evhttp_request_get_evhttp_uri(const struct evhttp_request *req); +/** Returns the request command */ +EVENT2_EXPORT_SYMBOL +enum evhttp_cmd_type evhttp_request_get_command(const struct evhttp_request *req); + +EVENT2_EXPORT_SYMBOL +int evhttp_request_get_response_code(const struct evhttp_request *req); +EVENT2_EXPORT_SYMBOL +const char * evhttp_request_get_response_code_line(const struct evhttp_request *req); + +/** Returns the input headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req); +/** Returns the output headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req); +/** Returns the input buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req); +/** Returns the output buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req); +/** Returns the host associated with the request. If a client sends an absolute + URI, the host part of that is preferred. Otherwise, the input headers are + searched for a Host: header. NULL is returned if no absolute URI or Host: + header is provided. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_host(struct evhttp_request *req); + +/* Interfaces for dealing with HTTP headers */ + +/** + Finds the value belonging to a header. + + @param headers the evkeyvalq object in which to find the header + @param key the name of the header to find + @returns a pointer to the value for the header or NULL if the header + could not be found. + @see evhttp_add_header(), evhttp_remove_header() +*/ +EVENT2_EXPORT_SYMBOL +const char *evhttp_find_header(const struct evkeyvalq *headers, + const char *key); + +/** + Removes a header from a list of existing headers. + + @param headers the evkeyvalq object from which to remove a header + @param key the name of the header to remove + @returns 0 if the header was removed, -1 otherwise. + @see evhttp_find_header(), evhttp_add_header() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_header(struct evkeyvalq *headers, const char *key); + +/** + Adds a header to a list of existing headers. + + @param headers the evkeyvalq object to which to add a header + @param key the name of the header + @param value the value belonging to the header + @returns 0 on success, -1 otherwise. + @see evhttp_find_header(), evhttp_clear_headers() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value); + +/** + Removes all headers from the header list. + + @param headers the evkeyvalq object from which to remove all headers +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_clear_headers(struct evkeyvalq *headers); + +/* Miscellaneous utility functions */ + + +/** + Helper function to encode a string for inclusion in a URI. All + characters are replaced by their hex-escaped (%22) equivalents, + except for characters explicitly unreserved by RFC3986 -- that is, + ASCII alphanumeric characters, hyphen, dot, underscore, and tilde. + + The returned string must be freed by the caller. + + @param str an unencoded string + @return a newly allocated URI-encoded string or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_encode_uri(const char *str); + +/** + As evhttp_encode_uri, but if 'size' is nonnegative, treat the string + as being 'size' bytes long. This allows you to encode strings that + may contain 0-valued bytes. + + The returned string must be freed by the caller. + + @param str an unencoded string + @param size the length of the string to encode, or -1 if the string + is NUL-terminated + @param space_to_plus if true, space characters in 'str' are encoded + as +, not %20. + @return a newly allocate URI-encoded string, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uriencode(const char *str, ev_ssize_t size, int space_to_plus); + +/** + Helper function to sort of decode a URI-encoded string. Unlike + evhttp_get_decoded_uri, it decodes all plus characters that appear + _after_ the first question mark character, but no plusses that occur + before. This is not a good way to decode URIs in whole or in part. + + The returned string must be freed by the caller + + @deprecated This function is deprecated; you probably want to use + evhttp_get_decoded_uri instead. + + @param uri an encoded URI + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_decode_uri(const char *uri); + +/** + Helper function to decode a URI-escaped string or HTTP parameter. + + If 'decode_plus' is 1, then we decode the string as an HTTP parameter + value, and convert all plus ('+') characters to spaces. If + 'decode_plus' is 0, we leave all plus characters unchanged. + + The returned string must be freed by the caller. + + @param uri a URI-encode encoded URI + @param decode_plus determines whether we convert '+' to space. + @param size_out if size_out is not NULL, *size_out is set to the size of the + returned string + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uridecode(const char *uri, int decode_plus, + size_t *size_out); + +/** + Helper function to parse out arguments in a query. + + Parsing a URI like + + http://foo.com/?q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @deprecated This function is deprecated as of Libevent 2.0.9. Use + evhttp_uri_parse and evhttp_parse_query_str instead. + + @param uri the request URI + @param headers the head of the evkeyval queue + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query(const char *uri, struct evkeyvalq *headers); + +/** + Helper function to parse out arguments from the query portion of an + HTTP URI. + + Parsing a query string like + + q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @param query_parse the query portion of the URI + @param headers the head of the evkeyval queue + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers); + +/** + * Escape HTML character entities in a string. + * + * Replaces <, >, ", ' and & with <, >, ", + * ' and & correspondingly. + * + * The returned string needs to be freed by the caller. + * + * @param html an unescaped HTML string + * @return an escaped HTML string or NULL on error + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_htmlescape(const char *html); + +/** + * Return a new empty evhttp_uri with no fields set. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_new(void); + +/** + * Changes the flags set on a given URI. See EVHTTP_URI_* for + * a list of flags. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags); + +/** Return the scheme of an evhttp_uri, or NULL if there is no scheme has + * been set and the evhttp_uri contains a Relative-Ref. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_scheme(const struct evhttp_uri *uri); +/** + * Return the userinfo part of an evhttp_uri, or NULL if it has no userinfo + * set. + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_userinfo(const struct evhttp_uri *uri); +/** + * Return the host part of an evhttp_uri, or NULL if it has no host set. + * The host may either be a regular hostname (conforming to the RFC 3986 + * "regname" production), or an IPv4 address, or the empty string, or a + * bracketed IPv6 address, or a bracketed 'IP-Future' address. + * + * Note that having a NULL host means that the URI has no authority + * section, but having an empty-string host means that the URI has an + * authority section with no host part. For example, + * "mailto:user@example.com" has a host of NULL, but "file:///etc/motd" + * has a host of "". + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_host(const struct evhttp_uri *uri); +/** Return the port part of an evhttp_uri, or -1 if there is no port set. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_get_port(const struct evhttp_uri *uri); +/** Return the path part of an evhttp_uri, or NULL if it has no path set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_path(const struct evhttp_uri *uri); +/** Return the query part of an evhttp_uri (excluding the leading "?"), or + * NULL if it has no query set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_query(const struct evhttp_uri *uri); +/** Return the fragment part of an evhttp_uri (excluding the leading "#"), + * or NULL if it has no fragment set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_fragment(const struct evhttp_uri *uri); + +/** Set the scheme of an evhttp_uri, or clear the scheme if scheme==NULL. + * Returns 0 on success, -1 if scheme is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme); +/** Set the userinfo of an evhttp_uri, or clear the userinfo if userinfo==NULL. + * Returns 0 on success, -1 if userinfo is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo); +/** Set the host of an evhttp_uri, or clear the host if host==NULL. + * Returns 0 on success, -1 if host is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_host(struct evhttp_uri *uri, const char *host); +/** Set the port of an evhttp_uri, or clear the port if port==-1. + * Returns 0 on success, -1 if port is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_port(struct evhttp_uri *uri, int port); +/** Set the path of an evhttp_uri, or clear the path if path==NULL. + * Returns 0 on success, -1 if path is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_path(struct evhttp_uri *uri, const char *path); +/** Set the query of an evhttp_uri, or clear the query if query==NULL. + * The query should not include a leading "?". + * Returns 0 on success, -1 if query is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_query(struct evhttp_uri *uri, const char *query); +/** Set the fragment of an evhttp_uri, or clear the fragment if fragment==NULL. + * The fragment should not include a leading "#". + * Returns 0 on success, -1 if fragment is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment); + +/** + * Helper function to parse a URI-Reference as specified by RFC3986. + * + * This function matches the URI-Reference production from RFC3986, + * which includes both URIs like + * + * scheme://[[userinfo]@]foo.com[:port]]/[path][?query][#fragment] + * + * and relative-refs like + * + * [path][?query][#fragment] + * + * Any optional elements portions not present in the original URI are + * left set to NULL in the resulting evhttp_uri. If no port is + * specified, the port is set to -1. + * + * Note that no decoding is performed on percent-escaped characters in + * the string; if you want to parse them, use evhttp_uridecode or + * evhttp_parse_query_str as appropriate. + * + * Note also that most URI schemes will have additional constraints that + * this function does not know about, and cannot check. For example, + * mailto://www.example.com/cgi-bin/fortune.pl is not a reasonable + * mailto url, http://www.example.com:99999/ is not a reasonable HTTP + * URL, and ftp:username@example.com is not a reasonable FTP URL. + * Nevertheless, all of these URLs conform to RFC3986, and this function + * accepts all of them as valid. + * + * @param source_uri the request URI + * @param flags Zero or more EVHTTP_URI_* flags to affect the behavior + * of the parser. + * @return uri container to hold parsed data, or NULL if there is error + * @see evhttp_uri_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse_with_flags(const char *source_uri, + unsigned flags); + +/** Tolerate URIs that do not conform to RFC3986. + * + * Unfortunately, some HTTP clients generate URIs that, according to RFC3986, + * are not conformant URIs. If you need to support these URIs, you can + * do so by passing this flag to evhttp_uri_parse_with_flags. + * + * Currently, these changes are: + * <ul> + * <li> Nonconformant URIs are allowed to contain otherwise unreasonable + * characters in their path, query, and fragment components. + * </ul> + */ +#define EVHTTP_URI_NONCONFORMANT 0x01 + +/** Alias for evhttp_uri_parse_with_flags(source_uri, 0) */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse(const char *source_uri); + +/** + * Free all memory allocated for a parsed uri. Only use this for URIs + * generated by evhttp_uri_parse. + * + * @param uri container with parsed data + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_free(struct evhttp_uri *uri); + +/** + * Join together the uri parts from parsed data to form a URI-Reference. + * + * Note that no escaping of reserved characters is done on the members + * of the evhttp_uri, so the generated string might not be a valid URI + * unless the members of evhttp_uri are themselves valid. + * + * @param uri container with parsed data + * @param buf destination buffer + * @param limit destination buffer size + * @return an joined uri as string or NULL on error + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/http_compat.h b/ios/Pods/CocoaLibEvent/src/event2/http_compat.h new file mode 100644 index 000000000..43c2c43e4 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/http_compat.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_COMPAT_H_INCLUDED_ +#define EVENT2_HTTP_COMPAT_H_INCLUDED_ + +/** @file event2/http_compat.h + + Potentially non-threadsafe versions of the functions in http.h: provided + only for backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/** + * Start an HTTP server on the specified address and port + * + * @deprecated It does not allow an event base to be specified + * + * @param address the address to which the HTTP server should be bound + * @param port the port number on which the HTTP server should listen + * @return an struct evhttp object + */ +struct evhttp *evhttp_start(const char *address, ev_uint16_t port); + +/** + * A connection object that can be used to for making HTTP requests. The + * connection object tries to establish the connection when it is given an + * http request object. + * + * @deprecated It does not allow an event base to be specified + */ +struct evhttp_connection *evhttp_connection_new( + const char *address, ev_uint16_t port); + +/** + * Associates an event base with the connection - can only be called + * on a freshly created connection object that has not been used yet. + * + * @deprecated XXXX Why? + */ +void evhttp_connection_set_base(struct evhttp_connection *evcon, + struct event_base *base); + + +/** Returns the request URI */ +#define evhttp_request_uri evhttp_request_get_uri + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/http_struct.h b/ios/Pods/CocoaLibEvent/src/event2/http_struct.h new file mode 100644 index 000000000..4bf5b1ff6 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/http_struct.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_STRUCT_H_INCLUDED_ +#define EVENT2_HTTP_STRUCT_H_INCLUDED_ + +/** @file event2/http_struct.h + + Data structures for http. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +/** + * the request structure that a server receives. + * WARNING: expect this structure to change. I will try to provide + * reasonable accessors. + */ +struct evhttp_request { +#if defined(TAILQ_ENTRY) + TAILQ_ENTRY(evhttp_request) next; +#else +struct { + struct evhttp_request *tqe_next; + struct evhttp_request **tqe_prev; +} next; +#endif + + /* the connection object that this request belongs to */ + struct evhttp_connection *evcon; + int flags; +/** The request obj owns the evhttp connection and needs to free it */ +#define EVHTTP_REQ_OWN_CONNECTION 0x0001 +/** Request was made via a proxy */ +#define EVHTTP_PROXY_REQUEST 0x0002 +/** The request object is owned by the user; the user must free it */ +#define EVHTTP_USER_OWNED 0x0004 +/** The request will be used again upstack; freeing must be deferred */ +#define EVHTTP_REQ_DEFER_FREE 0x0008 +/** The request should be freed upstack */ +#define EVHTTP_REQ_NEEDS_FREE 0x0010 + + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; + + /* address of the remote host and the port connection came from */ + char *remote_host; + ev_uint16_t remote_port; + + /* cache of the hostname for evhttp_request_get_host */ + char *host_cache; + + enum evhttp_request_kind kind; + enum evhttp_cmd_type type; + + size_t headers_size; + size_t body_size; + + char *uri; /* uri after HTTP request was parsed */ + struct evhttp_uri *uri_elems; /* uri elements */ + + char major; /* HTTP Major number */ + char minor; /* HTTP Minor number */ + + int response_code; /* HTTP Response code */ + char *response_code_line; /* Readable response */ + + struct evbuffer *input_buffer; /* read data */ + ev_int64_t ntoread; + unsigned chunked:1, /* a chunked request */ + userdone:1; /* the user has sent all data */ + + struct evbuffer *output_buffer; /* outgoing post or data */ + + /* Callback */ + void (*cb)(struct evhttp_request *, void *); + void *cb_arg; + + /* + * Chunked data callback - call for each completed chunk if + * specified. If not specified, all the data is delivered via + * the regular callback. + */ + void (*chunk_cb)(struct evhttp_request *, void *); + + /* + * Callback added for forked-daapd so they can collect ICY + * (shoutcast) metadata from the http header. If return + * int is negative the connection will be closed. + */ + int (*header_cb)(struct evhttp_request *, void *); + + /* + * Error callback - called when error is occured. + * @see evhttp_request_error for error types. + * + * @see evhttp_request_set_error_cb() + */ + void (*error_cb)(enum evhttp_request_error, void *); + + /* + * Send complete callback - called when the request is actually + * sent and completed. + */ + void (*on_complete_cb)(struct evhttp_request *, void *); + void *on_complete_cb_arg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_STRUCT_H_INCLUDED_ */ + diff --git a/ios/Pods/CocoaLibEvent/src/event2/keyvalq_struct.h b/ios/Pods/CocoaLibEvent/src/event2/keyvalq_struct.h new file mode 100644 index 000000000..bffa54b3a --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/keyvalq_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ +#define EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Fix so that people don't have to run with <sys/queue.h> */ +/* XXXX This code is duplicated with event_struct.h */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* + * Key-Value pairs. Can be used for HTTP headers but also for + * query argument parsing. + */ +struct evkeyval { + TAILQ_ENTRY(evkeyval) next; + + char *key; + char *value; +}; + +TAILQ_HEAD (evkeyvalq, evkeyval); + +/* XXXX This code is duplicated with event_struct.h */ +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ios/Pods/CocoaLibEvent/src/event2/listener.h b/ios/Pods/CocoaLibEvent/src/event2/listener.h new file mode 100644 index 000000000..84b4da055 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/listener.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_LISTENER_H_INCLUDED_ +#define EVENT2_LISTENER_H_INCLUDED_ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event.h> + +struct sockaddr; +struct evconnlistener; + +/** + A callback that we invoke when a listener has a new connection. + + @param listener The evconnlistener + @param fd The new file descriptor + @param addr The source address of the connection + @param socklen The length of addr + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *); + +/** + A callback that we invoke when a listener encounters a non-retriable error. + + @param listener The evconnlistener + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *); + +/** Flag: Indicates that we should not make incoming sockets nonblocking + * before passing them to the callback. */ +#define LEV_OPT_LEAVE_SOCKETS_BLOCKING (1u<<0) +/** Flag: Indicates that freeing the listener should close the underlying + * socket. */ +#define LEV_OPT_CLOSE_ON_FREE (1u<<1) +/** Flag: Indicates that we should set the close-on-exec flag, if possible */ +#define LEV_OPT_CLOSE_ON_EXEC (1u<<2) +/** Flag: Indicates that we should disable the timeout (if any) between when + * this socket is closed and when we can listen again on the same port. */ +#define LEV_OPT_REUSEABLE (1u<<3) +/** Flag: Indicates that the listener should be locked so it's safe to use + * from multiple threadcs at once. */ +#define LEV_OPT_THREADSAFE (1u<<4) +/** Flag: Indicates that the listener should be created in disabled + * state. Use evconnlistener_enable() to enable it later. */ +#define LEV_OPT_DISABLED (1u<<5) +/** Flag: Indicates that the listener should defer accept() until data is + * available, if possible. Ignored on platforms that do not support this. + * + * This option can help performance for protocols where the client transmits + * immediately after connecting. Do not use this option if your protocol + * _doesn't_ start out with the client transmitting data, since in that case + * this option will sometimes cause the kernel to never tell you about the + * connection. + * + * This option is only supported by evconnlistener_new_bind(): it can't + * work with evconnlistener_new_fd(), since the listener needs to be told + * to use the option before it is actually bound. + */ +#define LEV_OPT_DEFERRED_ACCEPT (1u<<6) +/** Flag: Indicates that we ask to allow multiple servers (processes or + * threads) to bind to the same port if they each set the option. + * + * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however + * SO_REUSEPORT does not imply SO_REUSEADDR. + * + * This is only available on Linux and kernel 3.9+ + */ +#define LEV_OPT_REUSEABLE_PORT (1u<<7) + +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given file descriptor. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + Set to 0 if the socket is already listening. + @param fd The file descriptor to listen on. It must be a nonblocking + file descriptor, and it should already be bound to an appropriate + port and address. +*/ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + evutil_socket_t fd); +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given address. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + @param addr The address to listen for connections on. + @param socklen The length of the address. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new_bind(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + const struct sockaddr *sa, int socklen); +/** + Disable and deallocate an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_free(struct evconnlistener *lev); +/** + Re-enable an evconnlistener that has been disabled. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_enable(struct evconnlistener *lev); +/** + Stop listening for connections on an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_disable(struct evconnlistener *lev); + +/** Return an evconnlistener's associated event_base. */ +EVENT2_EXPORT_SYMBOL +struct event_base *evconnlistener_get_base(struct evconnlistener *lev); + +/** Return the socket that an evconnlistner is listening on. */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev); + +/** Change the callback on the listener to cb and its user_data to arg. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_cb(struct evconnlistener *lev, + evconnlistener_cb cb, void *arg); + +/** Set an evconnlistener's error callback. */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_error_cb(struct evconnlistener *lev, + evconnlistener_errorcb errorcb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ios/Pods/CocoaLibEvent/src/event2/rpc.h b/ios/Pods/CocoaLibEvent/src/event2/rpc.h new file mode 100644 index 000000000..dd43df266 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/rpc.h @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_H_INCLUDED_ +#define EVENT2_RPC_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file rpc.h + * + * This header files provides basic support for an RPC server and client. + * + * To support RPCs in a server, every supported RPC command needs to be + * defined and registered. + * + * EVRPC_HEADER(SendCommand, Request, Reply); + * + * SendCommand is the name of the RPC command. + * Request is the name of a structure generated by event_rpcgen.py. + * It contains all parameters relating to the SendCommand RPC. The + * server needs to fill in the Reply structure. + * Reply is the name of a structure generated by event_rpcgen.py. It + * contains the answer to the RPC. + * + * To register an RPC with an HTTP server, you need to first create an RPC + * base with: + * + * struct evrpc_base *base = evrpc_init(http); + * + * A specific RPC can then be registered with + * + * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg); + * + * when the server receives an appropriately formatted RPC, the user callback + * is invoked. The callback needs to fill in the reply structure. + * + * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg); + * + * To send the reply, call EVRPC_REQUEST_DONE(rpc); + * + * See the regression test for an example. + */ + +/** + Determines if the member has been set in the message + + @param msg the message to inspect + @param member the member variable to test for presences + @return 1 if it's present or 0 otherwise. +*/ +#define EVTAG_HAS(msg, member) \ + ((msg)->member##_set == 1) + +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign +*/ +#define EVTAG_ASSIGN(msg, member, value) \ + (*(msg)->base->member##_assign)((msg), (value)) +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign + @param len the length of the value +*/ +#define EVTAG_ASSIGN_WITH_LEN(msg, member, value, len) \ + (*(msg)->base->member##_assign)((msg), (value), (len)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET(msg, member, pvalue) \ + (*(msg)->base->member##_get)((msg), (pvalue)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @param plen a pointer to the length of the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET_WITH_LEN(msg, member, pvalue, plen) \ + (*(msg)->base->member##_get)((msg), (pvalue), (plen)) + +#endif /* EVENT2_RPC_COMPAT_H_INCLUDED_ */ + +/** + Adds a value to an array. +*/ +#define EVTAG_ARRAY_ADD_VALUE(msg, member, value) \ + (*(msg)->base->member##_add)((msg), (value)) +/** + Allocates a new entry in the array and returns it. +*/ +#define EVTAG_ARRAY_ADD(msg, member) \ + (*(msg)->base->member##_add)(msg) +/** + Gets a variable at the specified offset from the array. +*/ +#define EVTAG_ARRAY_GET(msg, member, offset, pvalue) \ + (*(msg)->base->member##_get)((msg), (offset), (pvalue)) +/** + Returns the number of entries in the array. +*/ +#define EVTAG_ARRAY_LEN(msg, member) ((msg)->member##_length) + + +struct evbuffer; +struct event_base; +struct evrpc_req_generic; +struct evrpc_request_wrapper; +struct evrpc; + +/** The type of a specific RPC Message + * + * @param rpcname the name of the RPC message + */ +#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname + +struct evhttp_request; +struct evrpc_status; +struct evrpc_hook_meta; + +/** Creates the definitions and prototypes for an RPC + * + * You need to use EVRPC_HEADER to create structures and function prototypes + * needed by the server and client implementation. The structures have to be + * defined in an .rpc file and converted to source code via event_rpcgen.py + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @see EVRPC_GENERATE() + */ +#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \ +EVRPC_STRUCT(rpcname) { \ + struct evrpc_hook_meta *hook_meta; \ + struct reqstruct* request; \ + struct rplystruct* reply; \ + struct evrpc* rpc; \ + struct evhttp_request* http_req; \ + struct evbuffer* rpc_data; \ +}; \ +int evrpc_send_request_##rpcname(struct evrpc_pool *, \ + struct reqstruct *, struct rplystruct *, \ + void (*)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *); + +struct evrpc_pool; + +/** use EVRPC_GENERATE instead */ +struct evrpc_request_wrapper *evrpc_make_request_ctx( + struct evrpc_pool *pool, void *request, void *reply, + const char *rpcname, + void (*req_marshal)(struct evbuffer*, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *), + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cbarg); + +/** Creates a context structure that contains rpc specific information. + * + * EVRPC_MAKE_CTX is used to populate a RPC specific context that + * contains information about marshaling the RPC data types. + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @param pool the evrpc_pool over which to make the request + * @param request a pointer to the RPC request structure object + * @param reply a pointer to the RPC reply structure object + * @param cb the callback function to call when the RPC has completed + * @param cbarg the argument to supply to the callback + */ +#define EVRPC_MAKE_CTX(rpcname, reqstruct, rplystruct, \ + pool, request, reply, cb, cbarg) \ + evrpc_make_request_ctx(pool, request, reply, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg) + +/** Generates the code for receiving and sending an RPC message + * + * EVRPC_GENERATE is used to create the code corresponding to sending + * and receiving a particular RPC message + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @see EVRPC_HEADER() + */ +#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \ + int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \ + struct reqstruct *request, struct rplystruct *reply, \ + void (*cb)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *cbarg) { \ + return evrpc_send_request_generic(pool, request, reply, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal); \ +} + +/** Provides access to the HTTP request object underlying an RPC + * + * Access to the underlying http object; can be used to look at headers or + * for getting the remote ip address + * + * @param rpc_req the rpc request structure provided to the server callback + * @return an struct evhttp_request object that can be inspected for + * HTTP headers or sender information. + */ +#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req + +/** completes the server response to an rpc request */ +void evrpc_request_done(struct evrpc_req_generic *req); + +/** accessors for request and reply */ +void *evrpc_get_request(struct evrpc_req_generic *req); +void *evrpc_get_reply(struct evrpc_req_generic *req); + +/** Creates the reply to an RPC request + * + * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected + * to have been filled in. The request and reply pointers become invalid + * after this call has finished. + * + * @param rpc_req the rpc request structure provided to the server callback + */ +#define EVRPC_REQUEST_DONE(rpc_req) do { \ + struct evrpc_req_generic *req_ = (struct evrpc_req_generic *)(rpc_req); \ + evrpc_request_done(req_); \ +} while (0) + + +struct evrpc_base; +struct evhttp; + +/* functions to start up the rpc system */ + +/** Creates a new rpc base from which RPC requests can be received + * + * @param server a pointer to an existing HTTP server + * @return a newly allocated evrpc_base struct + * @see evrpc_free() + */ +struct evrpc_base *evrpc_init(struct evhttp *server); + +/** + * Frees the evrpc base + * + * For now, you are responsible for making sure that no rpcs are ongoing. + * + * @param base the evrpc_base object to be freed + * @see evrpc_init + */ +void evrpc_free(struct evrpc_base *base); + +/** register RPCs with the HTTP Server + * + * registers a new RPC with the HTTP server, each RPC needs to have + * a unique name under which it can be identified. + * + * @param base the evrpc_base structure in which the RPC should be + * registered. + * @param name the name of the RPC + * @param request the name of the RPC request structure + * @param reply the name of the RPC reply structure + * @param callback the callback that should be invoked when the RPC + * is received. The callback has the following prototype + * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg) + * @param cbarg an additional parameter that can be passed to the callback. + * The parameter can be used to carry around state. + */ +#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \ + evrpc_register_generic(base, #name, \ + (void (*)(struct evrpc_req_generic *, void *))callback, cbarg, \ + (void *(*)(void *))request##_new, NULL, \ + (void (*)(void *))request##_free, \ + (int (*)(void *, struct evbuffer *))request##_unmarshal, \ + (void *(*)(void *))reply##_new, NULL, \ + (void (*)(void *))reply##_free, \ + (int (*)(void *))reply##_complete, \ + (void (*)(struct evbuffer *, void *))reply##_marshal) + +/** + Low level function for registering an RPC with a server. + + Use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() +*/ +int evrpc_register_rpc(struct evrpc_base *, struct evrpc *, + void (*)(struct evrpc_req_generic*, void *), void *); + +/** + * Unregisters an already registered RPC + * + * @param base the evrpc_base object from which to unregister an RPC + * @param name the name of the rpc to unregister + * @return -1 on error or 0 when successful. + * @see EVRPC_REGISTER() + */ +#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc((base), #name) + +int evrpc_unregister_rpc(struct evrpc_base *base, const char *name); + +/* + * Client-side RPC support + */ + +struct evhttp_connection; +struct evrpc_status; + +/** launches an RPC and sends it to the server + * + * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server. + * + * @param name the name of the RPC + * @param pool the evrpc_pool that contains the connection objects over which + * the request should be sent. + * @param request a pointer to the RPC request structure - it contains the + * data to be sent to the server. + * @param reply a pointer to the RPC reply structure. It is going to be filled + * if the request was answered successfully + * @param cb the callback to invoke when the RPC request has been answered + * @param cbarg an additional argument to be passed to the client + * @return 0 on success, -1 on failure + */ +#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \ + evrpc_send_request_##name((pool), (request), (reply), (cb), (cbarg)) + +/** + Makes an RPC request based on the provided context. + + This is a low-level function and should not be used directly + unless a custom context object is provided. Use EVRPC_MAKE_REQUEST() + instead. + + @param ctx a context from EVRPC_MAKE_CTX() + @returns 0 on success, -1 otherwise. + @see EVRPC_MAKE_REQUEST(), EVRPC_MAKE_CTX() +*/ +int evrpc_make_request(struct evrpc_request_wrapper *ctx); + +/** creates an rpc connection pool + * + * a pool has a number of connections associated with it. + * rpc requests are always made via a pool. + * + * @param base a pointer to an struct event_based object; can be left NULL + * in singled-threaded applications + * @return a newly allocated struct evrpc_pool object + * @see evrpc_pool_free() + */ +struct evrpc_pool *evrpc_pool_new(struct event_base *base); +/** frees an rpc connection pool + * + * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new() + * @see evrpc_pool_new() + */ +void evrpc_pool_free(struct evrpc_pool *pool); + +/** + * Adds a connection over which rpc can be dispatched to the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool to which to add the connection + * @param evcon the connection to add to the pool. + */ +void evrpc_pool_add_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Removes a connection from the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool from which to remove the connection + * @param evcon the connection to remove from the pool. + */ +void evrpc_pool_remove_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Sets the timeout in secs after which a request has to complete. The + * RPC is completely aborted if it does not complete by then. Setting + * the timeout to 0 means that it never timeouts and can be used to + * implement callback type RPCs. + * + * Any connection already in the pool will be updated with the new + * timeout. Connections added to the pool after set_timeout has be + * called receive the pool timeout only if no timeout has been set + * for the connection itself. + * + * @param pool a pointer to a struct evrpc_pool object + * @param timeout_in_secs the number of seconds after which a request should + * timeout and a failure be returned to the callback. + */ +void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs); + +/** + * Hooks for changing the input and output of RPCs; this can be used to + * implement compression, authentication, encryption, ... + */ + +enum EVRPC_HOOK_TYPE { + EVRPC_INPUT, /**< apply the function to an input hook */ + EVRPC_OUTPUT /**< apply the function to an output hook */ +}; + +#ifndef _WIN32 +/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define INPUT EVRPC_INPUT +/** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define OUTPUT EVRPC_OUTPUT +#endif + +/** + * Return value from hook processing functions + */ + +enum EVRPC_HOOK_RESULT { + EVRPC_TERMINATE = -1, /**< indicates the rpc should be terminated */ + EVRPC_CONTINUE = 0, /**< continue processing the rpc */ + EVRPC_PAUSE = 1 /**< pause processing request until resumed */ +}; + +/** adds a processing hook to either an rpc base or rpc pool + * + * If a hook returns TERMINATE, the processing is aborted. On CONTINUE, + * the request is immediately processed after the hook returns. If the + * hook returns PAUSE, request processing stops until evrpc_resume_request() + * has been called. + * + * The add functions return handles that can be used for removing hooks. + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param cb the callback to call when the hook is activated + * @param cb_arg an additional argument for the callback + * @return a handle to the hook so it can be removed later + * @see evrpc_remove_hook() + */ +void *evrpc_add_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + int (*cb)(void *, struct evhttp_request *, struct evbuffer *, void *), + void *cb_arg); + +/** removes a previously added hook + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param handle a handle returned by evrpc_add_hook() + * @return 1 on success or 0 on failure + * @see evrpc_add_hook() + */ +int evrpc_remove_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + void *handle); + +/** resume a paused request + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param ctx the context pointer provided to the original hook call + */ +int +evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res); + +/** adds meta data to request + * + * evrpc_hook_add_meta() allows hooks to add meta data to a request. for + * a client request, the meta data can be inserted by an outgoing request hook + * and retrieved by the incoming request hook. + * + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data the data to be associated with the key + * @param data_size the size of the data + */ +void evrpc_hook_add_meta(void *ctx, const char *key, + const void *data, size_t data_size); + +/** retrieves meta data previously associated + * + * evrpc_hook_find_meta() can be used to retrieve meta data associated to a + * request by a previous hook. + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data pointer to a data pointer that will contain the retrieved data + * @param data_size pointer to the size of the data + * @return 0 on success or -1 on failure + */ +int evrpc_hook_find_meta(void *ctx, const char *key, + void **data, size_t *data_size); + +/** + * returns the connection object associated with the request + * + * @param ctx the context provided to the hook call + * @return a pointer to the evhttp_connection object + */ +struct evhttp_connection *evrpc_hook_get_connection(void *ctx); + +/** + Function for sending a generic RPC request. + + Do not call this function directly, use EVRPC_MAKE_REQUEST() instead. + + @see EVRPC_MAKE_REQUEST() + */ +int evrpc_send_request_generic(struct evrpc_pool *pool, + void *request, void *reply, + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cb_arg, + const char *rpcname, + void (*req_marshal)(struct evbuffer *, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *)); + +/** + Function for registering a generic RPC with the RPC base. + + Do not call this function directly, use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() + */ +int +evrpc_register_generic(struct evrpc_base *base, const char *name, + void (*callback)(struct evrpc_req_generic *, void *), void *cbarg, + void *(*req_new)(void *), void *req_new_arg, void (*req_free)(void *), + int (*req_unmarshal)(void *, struct evbuffer *), + void *(*rpl_new)(void *), void *rpl_new_arg, void (*rpl_free)(void *), + int (*rpl_complete)(void *), + void (*rpl_marshal)(struct evbuffer *, void *)); + +/** accessors for obscure and undocumented functionality */ +struct evrpc_pool* evrpc_request_get_pool(struct evrpc_request_wrapper *ctx); +void evrpc_request_set_pool(struct evrpc_request_wrapper *ctx, + struct evrpc_pool *pool); +void evrpc_request_set_cb(struct evrpc_request_wrapper *ctx, + void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg), + void *cb_arg); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/rpc_compat.h b/ios/Pods/CocoaLibEvent/src/event2/rpc_compat.h new file mode 100644 index 000000000..8d8334d25 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/rpc_compat.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ +#define EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** @file event2/rpc_compat.h + + Deprecated versions of the functions in rpc.h: provided only for + backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** backwards compatible accessors that work only with gcc */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + +#undef EVTAG_ASSIGN +#undef EVTAG_GET +#undef EVTAG_ADD + +#define EVTAG_ASSIGN(msg, member, args...) \ + (*(msg)->base->member##_assign)(msg, ## args) +#define EVTAG_GET(msg, member, args...) \ + (*(msg)->base->member##_get)(msg, ## args) +#define EVTAG_ADD(msg, member, args...) \ + (*(msg)->base->member##_add)(msg, ## args) +#endif +#define EVTAG_LEN(msg, member) ((msg)->member##_length) + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/rpc_struct.h b/ios/Pods/CocoaLibEvent/src/event2/rpc_struct.h new file mode 100644 index 000000000..8f691f49f --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/rpc_struct.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_STRUCT_H_INCLUDED_ +#define EVENT2_RPC_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file event2/rpc_struct.h + + Structures used by rpc.h. Using these structures directly may harm + forward compatibility: be careful! + + */ + +/** + * provides information about the completed RPC request. + */ +struct evrpc_status { +#define EVRPC_STATUS_ERR_NONE 0 +#define EVRPC_STATUS_ERR_TIMEOUT 1 +#define EVRPC_STATUS_ERR_BADPAYLOAD 2 +#define EVRPC_STATUS_ERR_UNSTARTED 3 +#define EVRPC_STATUS_ERR_HOOKABORTED 4 + int error; + + /* for looking at headers or other information */ + struct evhttp_request *http_req; +}; + +/* the structure below needs to be synchronized with evrpc_req_generic */ + +/* Encapsulates a request */ +struct evrpc { + TAILQ_ENTRY(evrpc) next; + + /* the URI at which the request handler lives */ + const char* uri; + + /* creates a new request structure */ + void *(*request_new)(void *); + void *request_new_arg; + + /* frees the request structure */ + void (*request_free)(void *); + + /* unmarshals the buffer into the proper request structure */ + int (*request_unmarshal)(void *, struct evbuffer *); + + /* creates a new reply structure */ + void *(*reply_new)(void *); + void *reply_new_arg; + + /* frees the reply structure */ + void (*reply_free)(void *); + + /* verifies that the reply is valid */ + int (*reply_complete)(void *); + + /* marshals the reply into a buffer */ + void (*reply_marshal)(struct evbuffer*, void *); + + /* the callback invoked for each received rpc */ + void (*cb)(struct evrpc_req_generic *, void *); + void *cb_arg; + + /* reference for further configuration */ + struct evrpc_base *base; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_STRUCT_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/tag.h b/ios/Pods/CocoaLibEvent/src/event2/tag.h new file mode 100644 index 000000000..2f73bfc00 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/tag.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_TAG_H_INCLUDED_ +#define EVENT2_TAG_H_INCLUDED_ + +/** @file event2/tag.h + + Helper functions for reading and writing tagged data onto buffers. + + */ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* For int types. */ +#include <event2/util.h> + +struct evbuffer; + +/* + * Marshaling tagged data - We assume that all tags are inserted in their + * numeric order - so that unknown tags will always be higher than the + * known ones - and we can just ignore the end of an event buffer. + */ + +EVENT2_EXPORT_SYMBOL +void evtag_init(void); + +/** + Unmarshals the header and returns the length of the payload + + @param evbuf the buffer from which to unmarshal data + @param ptag a pointer in which the tag id is being stored + @returns -1 on failure or the number of bytes in the remaining payload. +*/ +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, + ev_uint32_t len); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, + struct evbuffer *data); + +/** + Encode an integer and store it in an evbuffer. + + We encode integers by nybbles; the first nibble contains the number + of significant nibbles - 1; this allows us to encode up to 64-bit + integers. This function is byte-order independent. + + @param evbuf evbuffer to store the encoded number + @param number a 32-bit integer + */ +EVENT2_EXPORT_SYMBOL +void evtag_encode_int(struct evbuffer *evbuf, ev_uint32_t number); +EVENT2_EXPORT_SYMBOL +void evtag_encode_int64(struct evbuffer *evbuf, ev_uint64_t number); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint32_t integer); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int64(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint64_t integer); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, + const char *string); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, + struct timeval *tv); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, + struct evbuffer *dst); +EVENT2_EXPORT_SYMBOL +int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag); +EVENT2_EXPORT_SYMBOL +int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_consume(struct evbuffer *evbuf); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger); +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int64(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint64_t *pinteger); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, + void *data, size_t len); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, + char **pstring); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct timeval *ptv); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/tag_compat.h b/ios/Pods/CocoaLibEvent/src/event2/tag_compat.h new file mode 100644 index 000000000..a276c0d35 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/tag_compat.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_TAG_COMPAT_H_INCLUDED_ +#define EVENT2_TAG_COMPAT_H_INCLUDED_ + +/** @file event2/tag_compat.h + + Obsolete/deprecated functions from tag.h; provided only for backwards + compatibility. + */ + +/** + @name Misnamed functions + + @deprecated These macros are deprecated because their names don't follow + Libevent's naming conventions. Use evtag_encode_int and + evtag_encode_int64 instead. + + @{ +*/ +#define encode_int(evbuf, number) evtag_encode_int((evbuf), (number)) +#define encode_int64(evbuf, number) evtag_encode_int64((evbuf), (number)) +/**@}*/ + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/thread.h b/ios/Pods/CocoaLibEvent/src/event2/thread.h new file mode 100644 index 000000000..b51998631 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/thread.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2008-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_THREAD_H_INCLUDED_ +#define EVENT2_THREAD_H_INCLUDED_ + +/** @file event2/thread.h + + Functions for multi-threaded applications using Libevent. + + When using a multi-threaded application in which multiple threads + add and delete events from a single event base, Libevent needs to + lock its data structures. + + Like the memory-management function hooks, all of the threading functions + _must_ be set up before an event_base is created if you want the base to + use them. + + Most programs will either be using Windows threads or Posix threads. You + can configure Libevent to use one of these event_use_windows_threads() or + event_use_pthreads() respectively. If you're using another threading + library, you'll need to configure threading functions manually using + evthread_set_lock_callbacks() and evthread_set_condition_callbacks(). + + */ + +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> + +/** + @name Flags passed to lock functions + + @{ +*/ +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for writing. */ +#define EVTHREAD_WRITE 0x04 +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for reading. */ +#define EVTHREAD_READ 0x08 +/** A flag passed to a locking callback when we don't want to block waiting + * for the lock; if we can't get the lock immediately, we will instead + * return nonzero from the locking callback. */ +#define EVTHREAD_TRY 0x10 +/**@}*/ + +#if !defined(EVENT__DISABLE_THREAD_SUPPORT) || defined(EVENT_IN_DOXYGEN_) + +#define EVTHREAD_LOCK_API_VERSION 1 + +/** + @name Types of locks + + @{*/ +/** A recursive lock is one that can be acquired multiple times at once by the + * same thread. No other process can allocate the lock until the thread that + * has been holding it has unlocked it as many times as it locked it. */ +#define EVTHREAD_LOCKTYPE_RECURSIVE 1 +/* A read-write lock is one that allows multiple simultaneous readers, but + * where any one writer excludes all other writers and readers. */ +#define EVTHREAD_LOCKTYPE_READWRITE 2 +/**@}*/ + +/** This structure describes the interface a threading library uses for + * locking. It's used to tell evthread_set_lock_callbacks() how to use + * locking on this platform. + */ +struct evthread_lock_callbacks { + /** The current version of the locking API. Set this to + * EVTHREAD_LOCK_API_VERSION */ + int lock_api_version; + /** Which kinds of locks does this version of the locking API + * support? A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and + * EVTHREAD_LOCKTYPE_READWRITE. + * + * (Note that RECURSIVE locks are currently mandatory, and + * READWRITE locks are not currently used.) + **/ + unsigned supported_locktypes; + /** Function to allocate and initialize new lock of type 'locktype'. + * Returns NULL on failure. */ + void *(*alloc)(unsigned locktype); + /** Funtion to release all storage held in 'lock', which was created + * with type 'locktype'. */ + void (*free)(void *lock, unsigned locktype); + /** Acquire an already-allocated lock at 'lock' with mode 'mode'. + * Returns 0 on success, and nonzero on failure. */ + int (*lock)(unsigned mode, void *lock); + /** Release a lock at 'lock' using mode 'mode'. Returns 0 on success, + * and nonzero on failure. */ + int (*unlock)(unsigned mode, void *lock); +}; + +/** Sets a group of functions that Libevent should use for locking. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_lock_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_posix_threads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); + +#define EVTHREAD_CONDITION_API_VERSION 1 + +struct timeval; + +/** This structure describes the interface a threading library uses for + * condition variables. It's used to tell evthread_set_condition_callbacks + * how to use locking on this platform. + */ +struct evthread_condition_callbacks { + /** The current version of the conditions API. Set this to + * EVTHREAD_CONDITION_API_VERSION */ + int condition_api_version; + /** Function to allocate and initialize a new condition variable. + * Returns the condition variable on success, and NULL on failure. + * The 'condtype' argument will be 0 with this API version. + */ + void *(*alloc_condition)(unsigned condtype); + /** Function to free a condition variable. */ + void (*free_condition)(void *cond); + /** Function to signal a condition variable. If 'broadcast' is 1, all + * threads waiting on 'cond' should be woken; otherwise, only on one + * thread is worken. Should return 0 on success, -1 on failure. + * This function will only be called while holding the associated + * lock for the condition. + */ + int (*signal_condition)(void *cond, int broadcast); + /** Function to wait for a condition variable. The lock 'lock' + * will be held when this function is called; should be released + * while waiting for the condition to be come signalled, and + * should be held again when this function returns. + * If timeout is provided, it is interval of seconds to wait for + * the event to become signalled; if it is NULL, the function + * should wait indefinitely. + * + * The function should return -1 on error; 0 if the condition + * was signalled, or 1 on a timeout. */ + int (*wait_condition)(void *cond, void *lock, + const struct timeval *timeout); +}; + +/** Sets a group of functions that Libevent should use for condition variables. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_condition_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_pthreads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_condition_callbacks( + const struct evthread_condition_callbacks *); + +/** + Sets the function for determining the thread id. + + @param base the event base for which to set the id function + @param id_fn the identify function Libevent should invoke to + determine the identity of a thread. +*/ +EVENT2_EXPORT_SYMBOL +void evthread_set_id_callback( + unsigned long (*id_fn)(void)); + +#if (defined(_WIN32) && !defined(EVENT__DISABLE_THREAD_SUPPORT)) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Windows builtin locking and thread ID + functions. Unavailable if Libevent is not built for Windows. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_windows_threads(void); +/** + Defined if Libevent was built with support for evthread_use_windows_threads() +*/ +#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 1 +#endif + +#if defined(EVENT__HAVE_PTHREADS) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Pthreads locking and thread ID functions. + Unavailable if Libevent is not build for use with pthreads. Requires + libraries to link against Libevent_pthreads as well as Libevent. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_pthreads(void); +/** Defined if Libevent was built with support for evthread_use_pthreads() */ +#define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1 + +#endif + +/** Enable debugging wrappers around the current lock callbacks. If Libevent + * makes one of several common locking errors, exit with an assertion failure. + * + * If you're going to call this function, you must do so before any locks are + * allocated. + **/ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debugging(void); + +/* Old (misspelled) version: This is deprecated; use + * evthread_enable_log_debugging instead. */ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debuging(void); + +#endif /* EVENT__DISABLE_THREAD_SUPPORT */ + +struct event_base; +/** Make sure it's safe to tell an event base to wake up from another thread + or a signal handler. + + You shouldn't need to call this by hand; configuring the base with thread + support should be necessary and sufficient. + + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evthread_make_base_notifiable(struct event_base *base); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_THREAD_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/util.h b/ios/Pods/CocoaLibEvent/src/event2/util.h new file mode 100644 index 000000000..dd4bbb69d --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/util.h @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_UTIL_H_INCLUDED_ +#define EVENT2_UTIL_H_INCLUDED_ + +/** @file event2/util.h + + Common convenience functions for cross-platform portability and + related socket manipulations. + + */ +#include <event2/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event2/event-config.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_STDINT_H +#include <stdint.h> +#elif defined(EVENT__HAVE_INTTYPES_H) +#include <inttypes.h> +#endif +#ifdef EVENT__HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef EVENT__HAVE_STDDEF_H +#include <stddef.h> +#endif +#ifdef _MSC_VER +#include <BaseTsd.h> +#endif +#include <stdarg.h> +#ifdef EVENT__HAVE_NETDB_H +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#include <netdb.h> +#endif + +#ifdef _WIN32 +#include <winsock2.h> +#ifdef EVENT__HAVE_GETADDRINFO +/* for EAI_* definitions. */ +#include <ws2tcpip.h> +#endif +#else +#ifdef EVENT__HAVE_ERRNO_H +#include <errno.h> +#endif +#include <sys/socket.h> +#endif + +#include <time.h> + +/* Some openbsd autoconf versions get the name of this macro wrong. */ +#if defined(EVENT__SIZEOF_VOID__) && !defined(EVENT__SIZEOF_VOID_P) +#define EVENT__SIZEOF_VOID_P EVENT__SIZEOF_VOID__ +#endif + +/** + * @name Standard integer types. + * + * Integer type definitions for types that are supposed to be defined in the + * C99-specified stdint.h. Shamefully, some platforms do not include + * stdint.h, so we need to replace it. (If you are on a platform like this, + * your C headers are now over 10 years out of date. You should bug them to + * do something about this.) + * + * We define: + * + * <dl> + * <dt>ev_uint64_t, ev_uint32_t, ev_uint16_t, ev_uint8_t</dt> + * <dd>unsigned integer types of exactly 64, 32, 16, and 8 bits + * respectively.</dd> + * <dt>ev_int64_t, ev_int32_t, ev_int16_t, ev_int8_t</dt> + * <dd>signed integer types of exactly 64, 32, 16, and 8 bits + * respectively.</dd> + * <dt>ev_uintptr_t, ev_intptr_t</dt> + * <dd>unsigned/signed integers large enough + * to hold a pointer without loss of bits.</dd> + * <dt>ev_ssize_t</dt> + * <dd>A signed type of the same size as size_t</dd> + * <dt>ev_off_t</dt> + * <dd>A signed type typically used to represent offsets within a + * (potentially large) file</dd> + * + * @{ + */ +#ifdef EVENT__HAVE_UINT64_T +#define ev_uint64_t uint64_t +#define ev_int64_t int64_t +#elif defined(_WIN32) +#define ev_uint64_t unsigned __int64 +#define ev_int64_t signed __int64 +#elif EVENT__SIZEOF_LONG_LONG == 8 +#define ev_uint64_t unsigned long long +#define ev_int64_t long long +#elif EVENT__SIZEOF_LONG == 8 +#define ev_uint64_t unsigned long +#define ev_int64_t long +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint64_t ... +#define ev_int64_t ... +#else +#error "No way to define ev_uint64_t" +#endif + +#ifdef EVENT__HAVE_UINT32_T +#define ev_uint32_t uint32_t +#define ev_int32_t int32_t +#elif defined(_WIN32) +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif EVENT__SIZEOF_LONG == 4 +#define ev_uint32_t unsigned long +#define ev_int32_t signed long +#elif EVENT__SIZEOF_INT == 4 +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint32_t ... +#define ev_int32_t ... +#else +#error "No way to define ev_uint32_t" +#endif + +#ifdef EVENT__HAVE_UINT16_T +#define ev_uint16_t uint16_t +#define ev_int16_t int16_t +#elif defined(_WIN32) +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif EVENT__SIZEOF_INT == 2 +#define ev_uint16_t unsigned int +#define ev_int16_t signed int +#elif EVENT__SIZEOF_SHORT == 2 +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint16_t ... +#define ev_int16_t ... +#else +#error "No way to define ev_uint16_t" +#endif + +#ifdef EVENT__HAVE_UINT8_T +#define ev_uint8_t uint8_t +#define ev_int8_t int8_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint8_t ... +#define ev_int8_t ... +#else +#define ev_uint8_t unsigned char +#define ev_int8_t signed char +#endif + +#ifdef EVENT__HAVE_UINTPTR_T +#define ev_uintptr_t uintptr_t +#define ev_intptr_t intptr_t +#elif EVENT__SIZEOF_VOID_P <= 4 +#define ev_uintptr_t ev_uint32_t +#define ev_intptr_t ev_int32_t +#elif EVENT__SIZEOF_VOID_P <= 8 +#define ev_uintptr_t ev_uint64_t +#define ev_intptr_t ev_int64_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uintptr_t ... +#define ev_intptr_t ... +#else +#error "No way to define ev_uintptr_t" +#endif + +#ifdef EVENT__ssize_t +#define ev_ssize_t EVENT__ssize_t +#else +#define ev_ssize_t ssize_t +#endif + +/* Note that we define ev_off_t based on the compile-time size of off_t that + * we used to build Libevent, and not based on the current size of off_t. + * (For example, we don't define ev_off_t to off_t.). We do this because + * some systems let you build your software with different off_t sizes + * at runtime, and so putting in any dependency on off_t would risk API + * mismatch. + */ +#ifdef _WIN32 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 8 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 4 +#define ev_off_t ev_int32_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_off_t ... +#else +#define ev_off_t off_t +#endif +/**@}*/ + +/* Limits for integer types. + + We're making two assumptions here: + - The compiler does constant folding properly. + - The platform does signed arithmetic in two's complement. +*/ + +/** + @name Limits for integer types + + These macros hold the largest or smallest values possible for the + ev_[u]int*_t types. + + @{ +*/ +#ifndef EVENT__HAVE_STDINT_H +#define EV_UINT64_MAX ((((ev_uint64_t)0xffffffffUL) << 32) | 0xffffffffUL) +#define EV_INT64_MAX ((((ev_int64_t) 0x7fffffffL) << 32) | 0xffffffffL) +#define EV_INT64_MIN ((-EV_INT64_MAX) - 1) +#define EV_UINT32_MAX ((ev_uint32_t)0xffffffffUL) +#define EV_INT32_MAX ((ev_int32_t) 0x7fffffffL) +#define EV_INT32_MIN ((-EV_INT32_MAX) - 1) +#define EV_UINT16_MAX ((ev_uint16_t)0xffffUL) +#define EV_INT16_MAX ((ev_int16_t) 0x7fffL) +#define EV_INT16_MIN ((-EV_INT16_MAX) - 1) +#define EV_UINT8_MAX 255 +#define EV_INT8_MAX 127 +#define EV_INT8_MIN ((-EV_INT8_MAX) - 1) +#else +#define EV_UINT64_MAX UINT64_MAX +#define EV_INT64_MAX INT64_MAX +#define EV_INT64_MIN INT64_MIN +#define EV_UINT32_MAX UINT32_MAX +#define EV_INT32_MAX INT32_MAX +#define EV_INT32_MIN INT32_MIN +#define EV_UINT16_MAX UINT16_MAX +#define EV_INT16_MAX INT16_MAX +#define EV_UINT8_MAX UINT8_MAX +#define EV_INT8_MAX INT8_MAX +#define EV_INT8_MIN INT8_MIN +/** @} */ +#endif + + +/** + @name Limits for SIZE_T and SSIZE_T + + @{ +*/ +#if EVENT__SIZEOF_SIZE_T == 8 +#define EV_SIZE_MAX EV_UINT64_MAX +#define EV_SSIZE_MAX EV_INT64_MAX +#elif EVENT__SIZEOF_SIZE_T == 4 +#define EV_SIZE_MAX EV_UINT32_MAX +#define EV_SSIZE_MAX EV_INT32_MAX +#elif defined(EVENT_IN_DOXYGEN_) +#define EV_SIZE_MAX ... +#define EV_SSIZE_MAX ... +#else +#error "No way to define SIZE_MAX" +#endif + +#define EV_SSIZE_MIN ((-EV_SSIZE_MAX) - 1) +/**@}*/ + +#ifdef _WIN32 +#define ev_socklen_t int +#elif defined(EVENT__socklen_t) +#define ev_socklen_t EVENT__socklen_t +#else +#define ev_socklen_t socklen_t +#endif + +#ifdef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#if !defined(EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) \ + && !defined(ss_family) +#define ss_family __ss_family +#endif +#endif + +/** + * A type wide enough to hold the output of "socket()" or "accept()". On + * Windows, this is an intptr_t; elsewhere, it is an int. */ +#ifdef _WIN32 +#define evutil_socket_t intptr_t +#else +#define evutil_socket_t int +#endif + +/** + * Structure to hold information about a monotonic timer + * + * Use this with evutil_configure_monotonic_time() and + * evutil_gettime_monotonic(). + * + * This is an opaque structure; you can allocate one using + * evutil_monotonic_timer_new(). + * + * @see evutil_monotonic_timer_new(), evutil_monotonic_timer_free(), + * evutil_configure_monotonic_time(), evutil_gettime_monotonic() + */ +struct evutil_monotonic_timer +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +#define EV_MONOT_PRECISE 1 +#define EV_MONOT_FALLBACK 2 + +/** Format a date string using RFC 1123 format (used in HTTP). + * If `tm` is NULL, current system's time will be used. + * The number of characters written will be returned. + * One should check if the return value is smaller than `datelen` to check if + * the result is truncated or not. + */ +EVENT2_EXPORT_SYMBOL int +evutil_date_rfc1123(char *date, const size_t datelen, const struct tm *tm); + +/** Allocate a new struct evutil_monotonic_timer for use with the + * evutil_configure_monotonic_time() and evutil_gettime_monotonic() + * functions. You must configure the timer with + * evutil_configure_monotonic_time() before using it. + */ +EVENT2_EXPORT_SYMBOL +struct evutil_monotonic_timer * evutil_monotonic_timer_new(void); + +/** Free a struct evutil_monotonic_timer that was allocated using + * evutil_monotonic_timer_new(). + */ +EVENT2_EXPORT_SYMBOL +void evutil_monotonic_timer_free(struct evutil_monotonic_timer *timer); + +/** Set up a struct evutil_monotonic_timer; flags can include + * EV_MONOT_PRECISE and EV_MONOT_FALLBACK. + */ +EVENT2_EXPORT_SYMBOL +int evutil_configure_monotonic_time(struct evutil_monotonic_timer *timer, + int flags); + +/** Query the current monotonic time from a struct evutil_monotonic_timer + * previously configured with evutil_configure_monotonic_time(). Monotonic + * time is guaranteed never to run in reverse, but is not necessarily epoch- + * based, or relative to any other definite point. Use it to make reliable + * measurements of elapsed time between events even when the system time + * may be changed. + * + * It is not safe to use this funtion on the same timer from multiple + * threads. + */ +EVENT2_EXPORT_SYMBOL +int evutil_gettime_monotonic(struct evutil_monotonic_timer *timer, + struct timeval *tp); + +/** Create two new sockets that are connected to each other. + + On Unix, this simply calls socketpair(). On Windows, it uses the + loopback network interface on 127.0.0.1, and only + AF_INET,SOCK_STREAM are supported. + + (This may fail on some Windows hosts where firewall software has cleverly + decided to keep 127.0.0.1 from talking to itself.) + + Parameters and return values are as for socketpair() +*/ +EVENT2_EXPORT_SYMBOL +int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]); +/** Do platform-specific operations as needed to make a socket nonblocking. + + @param sock The socket to make nonblocking + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_nonblocking(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener socket reusable. + + Specifically, we want to make sure that another program will be able + to bind this address right after we've closed the listener. + + This differs from Windows's interpretation of "reusable", which + allows multiple listeners to bind the same address at the same time. + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener port reusable. + + Specifically, we want to make sure that multiple programs which also + set the same socket option will be able to bind, listen at the same time. + + This is a feature available only to Linux 3.9+ + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock); + +/** Do platform-specific operations as needed to close a socket upon a + successful execution of one of the exec*() functions. + + @param sock The socket to be closed + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_closeonexec(evutil_socket_t sock); + +/** Do the platform-specific call needed to close a socket returned from + socket() or accept(). + + @param sock The socket to be closed + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_closesocket(evutil_socket_t sock); +#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s) + +/** Do platform-specific operations, if possible, to make a tcp listener + * socket defer accept()s until there is data to read. + * + * Not all platforms support this. You don't want to do this for every + * listener socket: only the ones that implement a protocol where the + * client transmits before the server needs to respond. + * + * @param sock The listening socket to to make deferred + * @return 0 on success (whether the operation is supported or not), + * -1 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock); + +#ifdef _WIN32 +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() WSAGetLastError() +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { WSASetLastError(errcode); } while (0) +/** Return the most recent socket error to occur on sock. */ +EVENT2_EXPORT_SYMBOL +int evutil_socket_geterror(evutil_socket_t sock); +/** Convert a socket error to a string. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_socket_error_to_string(int errcode); +#elif defined(EVENT_IN_DOXYGEN_) +/** + @name Socket error functions + + These functions are needed for making programs compatible between + Windows and Unix-like platforms. + + You see, Winsock handles socket errors differently from the rest of + the world. Elsewhere, a socket error is like any other error and is + stored in errno. But winsock functions require you to retrieve the + error with a special function, and don't let you use strerror for + the error codes. And handling EWOULDBLOCK is ... different. + + @{ +*/ +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() ... +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) ... +/** Return the most recent socket error to occur on sock. */ +#define evutil_socket_geterror(sock) ... +/** Convert a socket error to a string. */ +#define evutil_socket_error_to_string(errcode) ... +/**@}*/ +#else +#define EVUTIL_SOCKET_ERROR() (errno) +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { errno = (errcode); } while (0) +#define evutil_socket_geterror(sock) (errno) +#define evutil_socket_error_to_string(errcode) (strerror(errcode)) +#endif + + +/** + * @name Manipulation macros for struct timeval. + * + * We define replacements + * for timeradd, timersub, timerclear, timercmp, and timerisset. + * + * @{ + */ +#ifdef EVENT__HAVE_TIMERADD +#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) +#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) +#else +#define evutil_timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define evutil_timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* !EVENT__HAVE_TIMERADD */ + +#ifdef EVENT__HAVE_TIMERCLEAR +#define evutil_timerclear(tvp) timerclear(tvp) +#else +#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +/**@}*/ + +/** Return true iff the tvp is related to uvp according to the relational + * operator cmp. Recognized values for cmp are ==, <=, <, >=, and >. */ +#define evutil_timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) + +#ifdef EVENT__HAVE_TIMERISSET +#define evutil_timerisset(tvp) timerisset(tvp) +#else +#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif + +/** Replacement for offsetof on platforms that don't define it. */ +#ifdef offsetof +#define evutil_offsetof(type, field) offsetof(type, field) +#else +#define evutil_offsetof(type, field) ((off_t)(&((type *)0)->field)) +#endif + +/* big-int related functions */ +/** Parse a 64-bit value from a string. Arguments are as for strtol. */ +EVENT2_EXPORT_SYMBOL +ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); + +/** Replacement for gettimeofday on platforms that lack it. */ +#ifdef EVENT__HAVE_GETTIMEOFDAY +#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#else +struct timezone; +EVENT2_EXPORT_SYMBOL +int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +/** Replacement for snprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif +; +/** Replacement for vsnprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 0))) +#endif +; + +/** Replacement for inet_ntop for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); +/** Replacement for inet_pton for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +int evutil_inet_pton(int af, const char *src, void *dst); +struct sockaddr; + +/** Parse an IPv4 or IPv6 address, with optional port, from a string. + + Recognized formats are: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, the port in the output is set to 0. + + @param str The string to parse. + @param out A struct sockaddr to hold the result. This should probably be + a struct sockaddr_storage. + @param outlen A pointer to the number of bytes that that 'out' can safely + hold. Set to the number of bytes used in 'out' on success. + @return -1 if the address is not well-formed, if the port is out of range, + or if out is not large enough to hold the result. Otherwise returns + 0 on success. +*/ +EVENT2_EXPORT_SYMBOL +int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen); + +/** Compare two sockaddrs; return 0 if they are equal, or less than 0 if sa1 + * preceeds sa2, or greater than 0 if sa1 follows sa2. If include_port is + * true, consider the port as well as the address. Only implemented for + * AF_INET and AF_INET6 addresses. The ordering is not guaranteed to remain + * the same between Libevent versions. */ +EVENT2_EXPORT_SYMBOL +int evutil_sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2, + int include_port); + +/** As strcasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strcasecmp(const char *str1, const char *str2); +/** As strncasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n); + +/* Here we define evutil_addrinfo to the native addrinfo type, or redefine it + * if this system has no getaddrinfo(). */ +#ifdef EVENT__HAVE_STRUCT_ADDRINFO +#define evutil_addrinfo addrinfo +#else +/** A definition of struct addrinfo for systems that lack it. + + (This is just an alias for struct addrinfo if the system defines + struct addrinfo.) +*/ +struct evutil_addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for nodename */ + struct sockaddr *ai_addr; /* binary address */ + struct evutil_addrinfo *ai_next; /* next structure in linked list */ +}; +#endif +/** @name evutil_getaddrinfo() error codes + + These values are possible error codes for evutil_getaddrinfo() and + related functions. + + @{ +*/ +#if defined(EAI_ADDRFAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_ADDRFAMILY EAI_ADDRFAMILY +#else +#define EVUTIL_EAI_ADDRFAMILY -901 +#endif +#if defined(EAI_AGAIN) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_AGAIN EAI_AGAIN +#else +#define EVUTIL_EAI_AGAIN -902 +#endif +#if defined(EAI_BADFLAGS) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_BADFLAGS EAI_BADFLAGS +#else +#define EVUTIL_EAI_BADFLAGS -903 +#endif +#if defined(EAI_FAIL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAIL EAI_FAIL +#else +#define EVUTIL_EAI_FAIL -904 +#endif +#if defined(EAI_FAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAMILY EAI_FAMILY +#else +#define EVUTIL_EAI_FAMILY -905 +#endif +#if defined(EAI_MEMORY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_MEMORY EAI_MEMORY +#else +#define EVUTIL_EAI_MEMORY -906 +#endif +/* This test is a bit complicated, since some MS SDKs decide to + * remove NODATA or redefine it to be the same as NONAME, in a + * fun interpretation of RFC 2553 and RFC 3493. */ +#if defined(EAI_NODATA) && defined(EVENT__HAVE_GETADDRINFO) && (!defined(EAI_NONAME) || EAI_NODATA != EAI_NONAME) +#define EVUTIL_EAI_NODATA EAI_NODATA +#else +#define EVUTIL_EAI_NODATA -907 +#endif +#if defined(EAI_NONAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_NONAME EAI_NONAME +#else +#define EVUTIL_EAI_NONAME -908 +#endif +#if defined(EAI_SERVICE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SERVICE EAI_SERVICE +#else +#define EVUTIL_EAI_SERVICE -909 +#endif +#if defined(EAI_SOCKTYPE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SOCKTYPE EAI_SOCKTYPE +#else +#define EVUTIL_EAI_SOCKTYPE -910 +#endif +#if defined(EAI_SYSTEM) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SYSTEM EAI_SYSTEM +#else +#define EVUTIL_EAI_SYSTEM -911 +#endif + +#define EVUTIL_EAI_CANCEL -90001 + +#if defined(AI_PASSIVE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_PASSIVE AI_PASSIVE +#else +#define EVUTIL_AI_PASSIVE 0x1000 +#endif +#if defined(AI_CANONNAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_CANONNAME AI_CANONNAME +#else +#define EVUTIL_AI_CANONNAME 0x2000 +#endif +#if defined(AI_NUMERICHOST) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICHOST AI_NUMERICHOST +#else +#define EVUTIL_AI_NUMERICHOST 0x4000 +#endif +#if defined(AI_NUMERICSERV) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICSERV AI_NUMERICSERV +#else +#define EVUTIL_AI_NUMERICSERV 0x8000 +#endif +#if defined(AI_V4MAPPED) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_V4MAPPED AI_V4MAPPED +#else +#define EVUTIL_AI_V4MAPPED 0x10000 +#endif +#if defined(AI_ALL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ALL AI_ALL +#else +#define EVUTIL_AI_ALL 0x20000 +#endif +#if defined(AI_ADDRCONFIG) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ADDRCONFIG AI_ADDRCONFIG +#else +#define EVUTIL_AI_ADDRCONFIG 0x40000 +#endif +/**@}*/ + +struct evutil_addrinfo; +/** + * This function clones getaddrinfo for systems that don't have it. For full + * details, see RFC 3493, section 6.1. + * + * Limitations: + * - When the system has no getaddrinfo, we fall back to gethostbyname_r or + * gethostbyname, with their attendant issues. + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * + * For a nonblocking variant, see evdns_getaddrinfo. + */ +EVENT2_EXPORT_SYMBOL +int evutil_getaddrinfo(const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, struct evutil_addrinfo **res); + +/** Release storage allocated by evutil_getaddrinfo or evdns_getaddrinfo. */ +EVENT2_EXPORT_SYMBOL +void evutil_freeaddrinfo(struct evutil_addrinfo *ai); + +EVENT2_EXPORT_SYMBOL +const char *evutil_gai_strerror(int err); + +/** Generate n bytes of secure pseudorandom data, and store them in buf. + * + * Current versions of Libevent use an ARC4-based random number generator, + * seeded using the platform's entropy source (/dev/urandom on Unix-like + * systems; CryptGenRandom on Windows). This is not actually as secure as it + * should be: ARC4 is a pretty lousy cipher, and the current implementation + * provides only rudimentary prediction- and backtracking-resistance. Don't + * use this for serious cryptographic applications. + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_get_bytes(void *buf, size_t n); + +/** + * Seed the secure random number generator if needed, and return 0 on + * success or -1 on failure. + * + * It is okay to call this function more than once; it will still return + * 0 if the RNG has been successfully seeded and -1 if it can't be + * seeded. + * + * Ordinarily you don't need to call this function from your own code; + * Libevent will seed the RNG itself the first time it needs good random + * numbers. You only need to call it if (a) you want to double-check + * that one of the seeding methods did succeed, or (b) you plan to drop + * the capability to seed (by chrooting, or dropping capabilities, or + * whatever), and you want to make sure that seeding happens before your + * program loses the ability to do it. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_init(void); + +/** + * Set a filename to use in place of /dev/urandom for seeding the secure + * PRNG. Return 0 on success, -1 on failure. + * + * Call this function BEFORE calling any other initialization or RNG + * functions. + * + * (This string will _NOT_ be copied internally. Do not free it while any + * user of the secure RNG might be running. Don't pass anything other than a + * real /dev/...random device file here, or you might lose security.) + * + * This API is unstable, and might change in a future libevent version. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_set_urandom_device_file(char *fname); + +/** Seed the random number generator with extra random bytes. + + You should almost never need to call this function; it should be + sufficient to invoke evutil_secure_rng_init(), or let Libevent take + care of calling evutil_secure_rng_init() on its own. + + If you call this function as a _replacement_ for the regular + entropy sources, then you need to be sure that your input + contains a fairly large amount of strong entropy. Doing so is + notoriously hard: most people who try get it wrong. Watch out! + + @param dat a buffer full of a strong source of random numbers + @param datlen the number of bytes to read from datlen + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT1_EVUTIL_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/event2/visibility.h b/ios/Pods/CocoaLibEvent/src/event2/visibility.h new file mode 100644 index 000000000..fb16dbeed --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/event2/visibility.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_VISIBILITY_H_INCLUDED_ +#define EVENT2_VISIBILITY_H_INCLUDED_ + +#include <event2/event-config.h> + +#if defined(event_EXPORTS) || defined(event_extra_EXPORTS) || defined(event_core_EXPORTS) +# if defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define EVENT2_EXPORT_SYMBOL __global +# elif defined __GNUC__ +# define EVENT2_EXPORT_SYMBOL __attribute__ ((visibility("default"))) +# elif defined(_MSC_VER) +# define EVENT2_EXPORT_SYMBOL extern __declspec(dllexport) +# else +# define EVENT2_EXPORT_SYMBOL /* unknown compiler */ +# endif +#else +# if defined(EVENT__NEED_DLLIMPORT) && defined(_MSC_VER) && !defined(EVENT_BUILDING_REGRESS_TEST) +# define EVENT2_EXPORT_SYMBOL extern __declspec(dllimport) +# else +# define EVENT2_EXPORT_SYMBOL +# endif +#endif + +#endif /* EVENT2_VISIBILITY_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/evhttp.h b/ios/Pods/CocoaLibEvent/src/evhttp.h new file mode 100644 index 000000000..549bc9b14 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/evhttp.h @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT1_EVHTTP_H_INCLUDED_ +#define EVENT1_EVHTTP_H_INCLUDED_ + +/** @file evhttp.h + + An http implementation subsystem for Libevent. + + The <evhttp.h> header is deprecated in Libevent 2.0 and later; please + use <event2/http.h> instead. Depending on what functionality you + need, you may also want to include more of the other <event2/...> + headers. + */ + +#include <event.h> +#include <event2/http.h> +#include <event2/http_struct.h> +#include <event2/http_compat.h> + +#endif /* EVENT1_EVHTTP_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/evrpc.h b/ios/Pods/CocoaLibEvent/src/evrpc.h new file mode 100644 index 000000000..7e986f7da --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/evrpc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT1_EVRPC_H_INCLUDED_ +#define EVENT1_EVRPC_H_INCLUDED_ + +/** @file evrpc.h + + An RPC system for Libevent. + + The <evrpc.h> header is deprecated in Libevent 2.0 and later; please + use <event2/rpc.h> instead. Depending on what functionality you + need, you may also want to include more of the other <event2/...> + headers. + */ + +#include <event.h> +#include <event2/rpc.h> +#include <event2/rpc_struct.h> +#include <event2/rpc_compat.h> + +#endif /* EVENT1_EVRPC_H_INCLUDED_ */ diff --git a/ios/Pods/CocoaLibEvent/src/evutil.h b/ios/Pods/CocoaLibEvent/src/evutil.h new file mode 100644 index 000000000..12c137d74 --- /dev/null +++ b/ios/Pods/CocoaLibEvent/src/evutil.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT1_EVUTIL_H_INCLUDED_ +#define EVENT1_EVUTIL_H_INCLUDED_ + +/** @file evutil.h + + Utility and compatibility functions for Libevent. + + The <evutil.h> header is deprecated in Libevent 2.0 and later; please + use <event2/util.h> instead. +*/ + +#include <event2/util.h> + +#endif /* EVENT1_EVUTIL_H_INCLUDED_ */ diff --git a/ios/Pods/Firebase/README.md b/ios/Pods/Firebase/README.md index 5097a89ae..1747cca7a 100644 --- a/ios/Pods/Firebase/README.md +++ b/ios/Pods/Firebase/README.md @@ -1,10 +1,24 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +83,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +134,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +149,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +180,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +191,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,32 +217,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst +### tvOS, macOS, watchOS and Catalyst Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). During app setup in the console, you may get to a step that mentions something like "Checking if the app -has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/Catalyst. +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. **It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Crashlytics' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -252,3 +272,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector index 1a7eaec7d..bb3a7cae8 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector and b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics index fa7ceb951..46030f0c4 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics and b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h index c70c53e25..674f8b121 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h @@ -12,46 +12,41 @@ #import <Foundation/Foundation.h> -/// Add Payment Info event. This event signifies that a user has submitted their payment information -/// to your app. +/// Add Payment Info event. This event signifies that a user has submitted their payment +/// information. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the +/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params: +/// +/// <ul> +/// <li>@c kFIRParameterCoupon (NSString) (optional)</li> +/// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterPaymentType (NSString) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> +/// </ul> static NSString *const kFIREventAddPaymentInfo NS_SWIFT_NAME(AnalyticsEventAddPaymentInfo) = @"add_payment_info"; -/// E-Commerce Add To Cart event. This event signifies that an item was added to a cart for -/// purchase. Add this event to a funnel with kFIREventEcommercePurchase to gauge the effectiveness -/// of your checkout process. Note: If you supply the @c kFIRParameterValue parameter, you must -/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed +/// E-Commerce Add To Cart event. This event signifies that an item(s) was added to a cart for +/// purchase. Add this event to a funnel with @c kFIREventPurchase to gauge the effectiveness of +/// your checkout process. Note: If you supply the @c kFIRParameterValue parameter, you must also +/// supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed /// accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li> -/// <li>@c kFIRParameterItemID (NSString)</li> -/// <li>@c kFIRParameterItemName (NSString)</li> -/// <li>@c kFIRParameterItemCategory (NSString)</li> -/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li> -/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li> /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> -/// <li>@c kFIRParameterOrigin (NSString) (optional)</li> -/// <li>@c kFIRParameterDestination (NSString) (optional)</li> -/// <li>@c kFIRParameterStartDate (NSString) (optional)</li> -/// <li>@c kFIRParameterEndDate (NSString) (optional)</li> /// </ul> static NSString *const kFIREventAddToCart NS_SWIFT_NAME(AnalyticsEventAddToCart) = @"add_to_cart"; -/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist. -/// Use this event to identify popular gift items in your app. Note: If you supply the -/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency -/// parameter so that revenue metrics can be computed accurately. Params: +/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist. Use +/// this event to identify popular gift items. Note: If you supply the @c kFIRParameterValue +/// parameter, you must also supply the @c kFIRParameterCurrency parameter so that revenue metrics +/// can be computed accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li> -/// <li>@c kFIRParameterItemID (NSString)</li> -/// <li>@c kFIRParameterItemName (NSString)</li> -/// <li>@c kFIRParameterItemCategory (NSString)</li> -/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li> -/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li> /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// </ul> static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToWishlist) = @@ -64,26 +59,16 @@ static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToW static NSString *const kFIREventAppOpen NS_SWIFT_NAME(AnalyticsEventAppOpen) = @"app_open"; /// E-Commerce Begin Checkout event. This event signifies that a user has begun the process of -/// checking out. Add this event to a funnel with your kFIREventEcommercePurchase event to gauge the -/// effectiveness of your checkout process. Note: If you supply the @c kFIRParameterValue -/// parameter, you must also supply the @c kFIRParameterCurrency parameter so that revenue -/// metrics can be computed accurately. Params: +/// checking out. Add this event to a funnel with your @c kFIREventPurchase event to gauge the +/// effectiveness of your checkout process. Note: If you supply the @c kFIRParameterValue parameter, +/// you must also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be +/// computed accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> +/// <li>@c kFIRParameterCoupon (NSString) (optional)</li> /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> -/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li> -/// <li>@c kFIRParameterStartDate (NSString) (optional)</li> -/// <li>@c kFIRParameterEndDate (NSString) (optional)</li> -/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings</li> -/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings</li> -/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings</li> -/// <li>@c kFIRParameterOrigin (NSString) (optional)</li> -/// <li>@c kFIRParameterDestination (NSString) (optional)</li> -/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// </ul> static NSString *const kFIREventBeginCheckout NS_SWIFT_NAME(AnalyticsEventBeginCheckout) = @"begin_checkout"; @@ -110,6 +95,7 @@ static NSString *const kFIREventCampaignDetails NS_SWIFT_NAME(AnalyticsEventCamp /// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li> /// <li>@c kFIRParameterCheckoutOption (NSString) (optional)</li> /// </ul> +/// <b>This constant has been deprecated.</b> static NSString *const kFIREventCheckoutProgress NS_SWIFT_NAME(AnalyticsEventCheckoutProgress) = @"checkout_progress"; @@ -150,6 +136,7 @@ static NSString *const kFIREventEarnVirtualCurrency /// <li>@c kFIRParameterDestination (NSString) (optional)</li> /// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li> /// </ul> +/// <b>This constant has been deprecated. Use @c kFIREventPurchase constant instead.</b> static NSString *const kFIREventEcommercePurchase NS_SWIFT_NAME(AnalyticsEventEcommercePurchase) = @"ecommerce_purchase"; @@ -173,6 +160,21 @@ static NSString *const kFIREventGenerateLead NS_SWIFT_NAME(AnalyticsEventGenerat /// </ul> static NSString *const kFIREventJoinGroup NS_SWIFT_NAME(AnalyticsEventJoinGroup) = @"join_group"; +/// Level End event. Log this event when the user finishes a level. Params: +/// +/// <ul> +/// <li>@c kFIRParameterLevelName (NSString)</li> +/// <li>@c kFIRParameterSuccess (NSString)</li> +/// </ul> +static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end"; + +/// Level Start event. Log this event when the user starts a new level. Params: +/// +/// <ul> +/// <li>@c kFIRParameterLevelName (NSString)</li> +/// </ul> +static NSString *const kFIREventLevelStart NS_SWIFT_NAME(AnalyticsEventLevelStart) = @"level_start"; + /// Level Up event. This event signifies that a player has leveled up in your gaming app. It can /// help you gauge the level distribution of your userbase and help you identify certain levels that /// are difficult to pass. Params: @@ -214,6 +216,7 @@ static NSString *const kFIREventPostScore NS_SWIFT_NAME(AnalyticsEventPostScore) /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// </ul> +/// <b>This constant has been deprecated. Use @c kFIREventViewPromotion constant instead.</b> static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresentOffer) = @"present_offer"; @@ -227,24 +230,18 @@ static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresent /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// <li>@c kFIRParameterTransactionID (NSString) (optional)</li> /// </ul> +/// <b>This constant has been deprecated. Use @c kFIREventRefund constant instead.</b> static NSString *const kFIREventPurchaseRefund NS_SWIFT_NAME(AnalyticsEventPurchaseRefund) = @"purchase_refund"; -/// Remove from cart event. Params: +/// E-Commerce Remove from Cart event. This event signifies that an item(s) was removed from a cart. +/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the @c +/// kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li> -/// <li>@c kFIRParameterItemID (NSString)</li> -/// <li>@c kFIRParameterItemName (NSString)</li> -/// <li>@c kFIRParameterItemCategory (NSString)</li> -/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li> -/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li> /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> -/// <li>@c kFIRParameterOrigin (NSString) (optional)</li> -/// <li>@c kFIRParameterDestination (NSString) (optional)</li> -/// <li>@c kFIRParameterStartDate (NSString) (optional)</li> -/// <li>@c kFIRParameterEndDate (NSString) (optional)</li> /// </ul> static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) = @"remove_from_cart"; @@ -286,6 +283,7 @@ static NSString *const kFIREventSelectContent NS_SWIFT_NAME(AnalyticsEventSelect /// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li> /// <li>@c kFIRParameterCheckoutOption (NSString)</li> /// </ul> +/// <b>This constant has been deprecated.</b> static NSString *const kFIREventSetCheckoutOption NS_SWIFT_NAME(AnalyticsEventSetCheckoutOption) = @"set_checkout_option"; @@ -340,43 +338,24 @@ static NSString *const kFIREventTutorialComplete NS_SWIFT_NAME(AnalyticsEventTut static NSString *const kFIREventUnlockAchievement NS_SWIFT_NAME(AnalyticsEventUnlockAchievement) = @"unlock_achievement"; -/// View Item event. This event signifies that some content was shown to the user. This content may -/// be a product, a webpage or just a simple image or text. Use the appropriate parameters to -/// contextualize the event. Use this event to discover the most popular items viewed in your app. -/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the -/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. -/// Params: +/// View Item event. This event signifies that a user has viewed an item. Use the appropriate +/// parameters to contextualize the event. Use this event to discover the most popular items viewed +/// in your app. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the +/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterItemID (NSString)</li> -/// <li>@c kFIRParameterItemName (NSString)</li> -/// <li>@c kFIRParameterItemCategory (NSString)</li> -/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li> -/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li> -/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber) (optional)</li> /// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> /// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> -/// <li>@c kFIRParameterStartDate (NSString) (optional)</li> -/// <li>@c kFIRParameterEndDate (NSString) (optional)</li> -/// <li>@c kFIRParameterFlightNumber (NSString) (optional) for travel bookings</li> -/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings</li> -/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings</li> -/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings</li> -/// <li>@c kFIRParameterOrigin (NSString) (optional)</li> -/// <li>@c kFIRParameterDestination (NSString) (optional)</li> -/// <li>@c kFIRParameterSearchTerm (NSString) (optional) for travel bookings</li> -/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li> /// </ul> static NSString *const kFIREventViewItem NS_SWIFT_NAME(AnalyticsEventViewItem) = @"view_item"; -/// View Item List event. Log this event when the user has been presented with a list of items of a -/// certain category. Params: +/// View Item List event. Log this event when a user sees a list of items or offerings. Params: /// /// <ul> -/// <li>@c kFIRParameterItemCategory (NSString)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterItemListID (NSString) (optional)</li> +/// <li>@c kFIRParameterItemListName (NSString) (optional)</li> /// </ul> static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewItemList) = @"view_item_list"; @@ -387,21 +366,107 @@ static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewIte /// <ul> /// <li>@c kFIRParameterSearchTerm (NSString)</li> /// </ul> +/// <b>This constant has been deprecated.</b> static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) = @"view_search_results"; -/// Level Start event. Log this event when the user starts a new level. Params: +/// Add Shipping Info event. This event signifies that a user has submitted their shipping +/// information. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the +/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params: /// /// <ul> -/// <li>@c kFIRParameterLevelName (NSString)</li> +/// <li>@c kFIRParameterCoupon (NSString) (optional)</li> +/// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterShippingTier (NSString) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// </ul> -static NSString *const kFIREventLevelStart NS_SWIFT_NAME(AnalyticsEventLevelStart) = - @"level_start"; +static NSString *const kFIREventAddShippingInfo NS_SWIFT_NAME(AnalyticsEventAddShippingInfo) = + @"add_shipping_info"; -/// Level End event. Log this event when the user finishes a level. Params: +/// E-Commerce Purchase event. This event signifies that an item(s) was purchased by a user. Note: +/// This is different from the in-app purchase event, which is reported automatically for App +/// Store-based apps. Note: If you supply the @c kFIRParameterValue parameter, you must also supply +/// the @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. +/// Params: /// /// <ul> -/// <li>@c kFIRParameterLevelName (NSString)</li> -/// <li>@c kFIRParameterSuccess (NSString)</li> +/// <li>@c kFIRParameterAffiliation (NSString) (optional)</li> +/// <li>@c kFIRParameterCoupon (NSString) (optional)</li> +/// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li> +/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li> +/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> /// </ul> -static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end"; +static NSString *const kFIREventPurchase NS_SWIFT_NAME(AnalyticsEventPurchase) = @"purchase"; + +/// E-Commerce Refund event. This event signifies that a refund was issued. Note: If you supply the +/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency parameter so +/// that revenue metrics can be computed accurately. Params: +/// +/// <ul> +/// <li>@c kFIRParameterAffiliation (NSString) (optional)</li> +/// <li>@c kFIRParameterCoupon (NSString) (optional)</li> +/// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li> +/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li> +/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> +/// </ul> +static NSString *const kFIREventRefund NS_SWIFT_NAME(AnalyticsEventRefund) = @"refund"; + +/// Select Item event. This event signifies that an item was selected by a user from a list. Use the +/// appropriate parameters to contextualize the event. Use this event to discover the most popular +/// items selected. Params: +/// +/// <ul> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterItemListID (NSString) (optional)</li> +/// <li>@c kFIRParameterItemListName (NSString) (optional)</li> +/// </ul> +static NSString *const kFIREventSelectItem NS_SWIFT_NAME(AnalyticsEventSelectItem) = @"select_item"; + +/// Select promotion event. This event signifies that a user has selected a promotion offer. Use the +/// appropriate parameters to contextualize the event, such as the item(s) for which the promotion +/// applies. Params: +/// +/// <ul> +/// <li>@c kFIRParameterCreativeName (NSString) (optional)</li> +/// <li>@c kFIRParameterCreativeSlot (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterLocationID (NSString) (optional)</li> +/// <li>@c kFIRParameterPromotionID (NSString) (optional)</li> +/// <li>@c kFIRParameterPromotionName (NSString) (optional)</li> +/// </ul> +static NSString *const kFIREventSelectPromotion NS_SWIFT_NAME(AnalyticsEventSelectPromotion) = + @"select_promotion"; + +/// E-commerce View Cart event. This event signifies that a user has viewed their cart. Use this to +/// analyze your purchase funnel. Note: If you supply the @c kFIRParameterValue parameter, you must +/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed +/// accurately. Params: +/// +/// <ul> +/// <li>@c kFIRParameterCurrency (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li> +/// </ul> +static NSString *const kFIREventViewCart NS_SWIFT_NAME(AnalyticsEventViewCart) = @"view_cart"; + +/// View Promotion event. This event signifies that a promotion was shown to a user. Add this event +/// to a funnel with the @c kFIREventAddToCart and @c kFIREventPurchase to gauge your conversion +/// process. Params: +/// +/// <ul> +/// <li>@c kFIRParameterCreativeName (NSString) (optional)</li> +/// <li>@c kFIRParameterCreativeSlot (NSString) (optional)</li> +/// <li>@c kFIRParameterItems (NSArray) (optional)</li> +/// <li>@c kFIRParameterLocationID (NSString) (optional)</li> +/// <li>@c kFIRParameterPromotionID (NSString) (optional)</li> +/// <li>@c kFIRParameterPromotionName (NSString) (optional)</li> +/// </ul> +static NSString *const kFIREventViewPromotion NS_SWIFT_NAME(AnalyticsEventViewPromotion) = + @"view_promotion"; diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h index ad9fff71f..5775d866c 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h @@ -32,8 +32,8 @@ static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParamet static NSString *const kFIRParameterAdNetworkClickID NS_SWIFT_NAME(AnalyticsParameterAdNetworkClickID) = @"aclid"; -/// The store or affiliation from which this transaction occurred (NSString). -/// <pre> +/// A product affiliation to designate a supplying company or brick and mortar store location +/// (NSString). <pre> /// NSDictionary *params = @{ /// kFIRParameterAffiliation : @"Google Store", /// // ... @@ -71,6 +71,7 @@ static NSString *const kFIRParameterCharacter NS_SWIFT_NAME(AnalyticsParameterCh /// // ... /// }; /// </pre> +/// <b>This constant has been deprecated.</b> static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParameterCheckoutStep) = @"checkout_step"; @@ -81,6 +82,7 @@ static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParamete /// // ... /// }; /// </pre> +/// <b>This constant has been deprecated.</b> static NSString *const kFIRParameterCheckoutOption NS_SWIFT_NAME(AnalyticsParameterCheckoutOption) = @"checkout_option"; @@ -97,10 +99,10 @@ static NSString *const kFIRParameterContent NS_SWIFT_NAME(AnalyticsParameterCont static NSString *const kFIRParameterContentType NS_SWIFT_NAME(AnalyticsParameterContentType) = @"content_type"; -/// Coupon code for a purchasable item (NSString). +/// Coupon code used for a purchase (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterCoupon : @"zz123", +/// kFIRParameterCoupon : @"SUMMER_FUN", /// // ... /// }; /// </pre> @@ -136,8 +138,8 @@ static NSString *const kFIRParameterCreativeName NS_SWIFT_NAME(AnalyticsParamete static NSString *const kFIRParameterCreativeSlot NS_SWIFT_NAME(AnalyticsParameterCreativeSlot) = @"creative_slot"; -/// Purchase currency in 3-letter <a href="http://en.wikipedia.org/wiki/ISO_4217#Active_codes"> -/// ISO_4217</a> format (NSString). +/// Currency of the purchase or items associated with the event, in 3-letter +/// <a href="http://en.wikipedia.org/wiki/ISO_4217#Active_codes"> ISO_4217</a> format (NSString). /// <pre> /// NSDictionary *params = @{ /// kFIRParameterCurrency : @"USD", @@ -186,10 +188,10 @@ static NSString *const kFIRParameterFlightNumber NS_SWIFT_NAME(AnalyticsParamete /// </pre> static NSString *const kFIRParameterGroupID NS_SWIFT_NAME(AnalyticsParameterGroupID) = @"group_id"; -/// Index of an item in a list (signed 64-bit integer as NSNumber). +/// The index of the item in a list (signed 64-bit integer as NSNumber). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterIndex : @(1), +/// kFIRParameterIndex : @(5), /// // ... /// }; /// </pre> @@ -205,20 +207,20 @@ static NSString *const kFIRParameterIndex NS_SWIFT_NAME(AnalyticsParameterIndex) static NSString *const kFIRParameterItemBrand NS_SWIFT_NAME(AnalyticsParameterItemBrand) = @"item_brand"; -/// Item category (NSString). +/// Item category (context-specific) (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterItemCategory : @"t-shirts", +/// kFIRParameterItemCategory : @"pants", /// // ... /// }; /// </pre> static NSString *const kFIRParameterItemCategory NS_SWIFT_NAME(AnalyticsParameterItemCategory) = @"item_category"; -/// Item ID (NSString). +/// Item ID (context-specific) (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterItemID : @"p7654", +/// kFIRParameterItemID : @"SKU_12345", /// // ... /// }; /// </pre> @@ -232,13 +234,14 @@ static NSString *const kFIRParameterItemID NS_SWIFT_NAME(AnalyticsParameterItemI /// // ... /// }; /// </pre> +/// <b>This constant has been deprecated. Use @c kFIRParameterLocationID constant instead.</b> static NSString *const kFIRParameterItemLocationID NS_SWIFT_NAME(AnalyticsParameterItemLocationID) = @"item_location_id"; -/// Item name (NSString). +/// Item Name (context-specific) (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterItemName : @"abc", +/// kFIRParameterItemName : @"jeggings", /// // ... /// }; /// </pre> @@ -252,13 +255,14 @@ static NSString *const kFIRParameterItemName NS_SWIFT_NAME(AnalyticsParameterIte /// // ... /// }; /// </pre> +/// <b>This constant has been deprecated. Use @c kFIRParameterItemListName constant instead.</b> static NSString *const kFIRParameterItemList NS_SWIFT_NAME(AnalyticsParameterItemList) = @"item_list"; /// Item variant (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterItemVariant : @"Red", +/// kFIRParameterItemVariant : @"Black", /// // ... /// }; /// </pre> @@ -374,11 +378,11 @@ static NSString *const kFIRParameterScore NS_SWIFT_NAME(AnalyticsParameterScore) static NSString *const kFIRParameterSearchTerm NS_SWIFT_NAME(AnalyticsParameterSearchTerm) = @"search_term"; -/// Shipping cost (double as NSNumber). +/// Shipping cost associated with a transaction (double as NSNumber). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterShipping : @(9.50), -/// kFIRParameterCurrency : @"USD", // e.g. $9.50 USD +/// kFIRParameterShipping : @(5.99), +/// kFIRParameterCurrency : @"USD", // e.g. $5.99 USD /// // ... /// }; /// </pre> @@ -429,11 +433,11 @@ static NSString *const kFIRParameterSource NS_SWIFT_NAME(AnalyticsParameterSourc static NSString *const kFIRParameterStartDate NS_SWIFT_NAME(AnalyticsParameterStartDate) = @"start_date"; -/// Tax amount (double as NSNumber). +/// Tax cost associated with a transaction (double as NSNumber). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterTax : @(1.0), -/// kFIRParameterCurrency : @"USD", // e.g. $1.00 USD +/// kFIRParameterTax : @(2.43), +/// kFIRParameterCurrency : @"USD", // e.g. $2.43 USD /// // ... /// }; /// </pre> @@ -449,10 +453,10 @@ static NSString *const kFIRParameterTax NS_SWIFT_NAME(AnalyticsParameterTax) = @ /// </pre> static NSString *const kFIRParameterTerm NS_SWIFT_NAME(AnalyticsParameterTerm) = @"term"; -/// A single ID for a ecommerce group transaction (NSString). +/// The unique identifier of a transaction (NSString). /// <pre> /// NSDictionary *params = @{ -/// kFIRParameterTransactionID : @"ab7236dd9823", +/// kFIRParameterTransactionID : @"T12345", /// // ... /// }; /// </pre> @@ -530,3 +534,137 @@ static NSString *const kFIRParameterSuccess NS_SWIFT_NAME(AnalyticsParameterSucc /// </pre> static NSString *const kFIRParameterExtendSession NS_SWIFT_NAME(AnalyticsParameterExtendSession) = @"extend_session"; + +/// Monetary value of discount associated with a purchase (double as NSNumber). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterDiscount : @(2.0), +/// kFIRParameterCurrency : @"USD", // e.g. $2.00 USD +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterDiscount NS_SWIFT_NAME(AnalyticsParameterDiscount) = + @"discount"; + +/// Item Category (context-specific) (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemCategory2 : @"pants", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemCategory2 NS_SWIFT_NAME(AnalyticsParameterItemCategory2) = + @"item_category2"; + +/// Item Category (context-specific) (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemCategory3 : @"pants", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemCategory3 NS_SWIFT_NAME(AnalyticsParameterItemCategory3) = + @"item_category3"; + +/// Item Category (context-specific) (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemCategory4 : @"pants", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemCategory4 NS_SWIFT_NAME(AnalyticsParameterItemCategory4) = + @"item_category4"; + +/// Item Category (context-specific) (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemCategory5 : @"pants", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemCategory5 NS_SWIFT_NAME(AnalyticsParameterItemCategory5) = + @"item_category5"; + +/// The ID of the list in which the item was presented to the user (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemListID : @"ABC123", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemListID NS_SWIFT_NAME(AnalyticsParameterItemListID) = + @"item_list_id"; + +/// The name of the list in which the item was presented to the user (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItemListName : @"Related products", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterItemListName NS_SWIFT_NAME(AnalyticsParameterItemListName) = + @"item_list_name"; + +/// The list of items involved in the transaction. (NSArray). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterItems : @[ +/// @{kFIRParameterItemName : @"jeggings", kFIRParameterItemCategory : @"pants"}, +/// @{kFIRParameterItemName : @"boots", kFIRParameterItemCategory : @"shoes"}, +/// ], +/// }; +/// </pre> +static NSString *const kFIRParameterItems NS_SWIFT_NAME(AnalyticsParameterItems) = @"items"; + +/// The location associated with the event. Preferred to be the Google +/// <a href="https://developers.google.com/places/place-id">Place ID</a> that corresponds to the +/// associated item but could be overridden to a custom location ID string.(NSString). <pre> +/// NSDictionary *params = @{ +/// kFIRParameterLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterLocationID NS_SWIFT_NAME(AnalyticsParameterLocationID) = + @"location_id"; + +/// The chosen method of payment (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterPaymentType : @"Visa", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterPaymentType NS_SWIFT_NAME(AnalyticsParameterPaymentType) = + @"payment_type"; + +/// The ID of a product promotion (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterPromotionID : @"ABC123", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterPromotionID NS_SWIFT_NAME(AnalyticsParameterPromotionID) = + @"promotion_id"; + +/// The name of a product promotion (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterPromotionName : @"Summer Sale", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterPromotionName NS_SWIFT_NAME(AnalyticsParameterPromotionName) = + @"promotion_name"; + +/// The shipping tier (e.g. Ground, Air, Next-day) selected for delivery of the purchased item +/// (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterShippingTier : @"Ground", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRParameterShippingTier NS_SWIFT_NAME(AnalyticsParameterShippingTier) = + @"shipping_tier"; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m index 02f3ac3f8..002f93c51 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m @@ -78,6 +78,7 @@ NSString *const kFIRAppDiagnosticsErrorKey = @"Error"; NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp"; NSString *const kFIRAppDiagnosticsSDKNameKey = @"SDKName"; NSString *const kFIRAppDiagnosticsSDKVersionKey = @"SDKVersion"; +NSString *const kFIRAppDiagnosticsApplePlatformPrefix = @"apple-platform"; // Auth internal notification notification and key. NSString *const FIRAuthStateDidChangeInternalNotification = @@ -287,6 +288,8 @@ static dispatch_once_t sFirebaseUserAgentOnceToken; if (sAllApps && sAllApps[self.name]) { FIRLogDebug(kFIRLoggerCore, @"I-COR000006", @"Deleting app named %@", self.name); + // Remove all registered libraries from the container to avoid creating new instances. + [self.container removeAllComponents]; // Remove all cached instances from the container before deleting the app. [self.container removeAllCachedInstances]; @@ -577,6 +580,9 @@ static dispatch_once_t sFirebaseUserAgentOnceToken; NSString *swiftFlagValue = [self hasSwiftRuntime] ? @"true" : @"false"; [FIRApp registerLibrary:@"swift" withVersion:swiftFlagValue]; + + [FIRApp registerLibrary:kFIRAppDiagnosticsApplePlatformPrefix + withVersion:[self applePlatform]]; }); NSMutableArray<NSString *> *libraries = @@ -604,6 +610,26 @@ static dispatch_once_t sFirebaseUserAgentOnceToken; return hasSwiftRuntime; } ++ (NSString *)applePlatform { + NSString *applePlatform = @"unknown"; + + // When a Catalyst app is run on macOS then both `TARGET_OS_MACCATALYST` and `TARGET_OS_IOS` are + // `true`, which means the condition list is order-sensitive. +#if TARGET_OS_MACCATALYST + applePlatform = @"maccatalyst"; +#elif TARGET_OS_IOS + applePlatform = @"ios"; +#elif TARGET_OS_TV + applePlatform = @"tvos"; +#elif TARGET_OS_OSX + applePlatform = @"macos"; +#elif TARGET_OS_WATCH + applePlatform = @"watchos"; +#endif + + return applePlatform; +} + - (void)checkExpectedBundleID { NSArray *bundles = [FIRBundleUtil relevantBundles]; NSString *expectedBundleID = [self expectedBundleID]; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainer.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainer.m index 9f91786fe..bbe887824 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainer.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainer.m @@ -203,6 +203,12 @@ static NSMutableSet<Class> *sFIRComponentRegistrants; } } +- (void)removeAllComponents { + @synchronized(self) { + [self.components removeAllObjects]; + } +} + @end NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRHeartbeatInfo.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRHeartbeatInfo.m index f1359f5d6..277b0f712 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRHeartbeatInfo.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRHeartbeatInfo.m @@ -16,7 +16,7 @@ #import <GoogleUtilities/GULHeartbeatDateStorage.h> #import <GoogleUtilities/GULLogger.h> -const static long secondsInDay = 864000; +const static long secondsInDay = 86400; @implementation FIRHeartbeatInfo : NSObject /** Updates the storage with the heartbeat information corresponding to this tag. diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h index 2b7798189..bf39bc6ce 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h @@ -37,6 +37,9 @@ NS_ASSUME_NONNULL_BEGIN /// Remove all of the cached instances stored and allow them to clean up after themselves. - (void)removeAllCachedInstances; +/// Removes all the components. After calling this method no new instances will be created. +- (void)removeAllComponents; + /// Register a class to provide components for the interoperability system. The class should conform /// to `FIRComponentRegistrant` and provide an array of `FIRComponent` objects. + (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRApp.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRApp.h index e0dd6d692..f5578c6a0 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRApp.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRApp.h @@ -47,15 +47,15 @@ NS_SWIFT_NAME(FirebaseApp) /** * Configures a default Firebase app. Raises an exception if any configuration step fails. The * default app is named "__FIRAPP_DEFAULT". This method should be called after the app is launched - * and before using Firebase services. This method is thread safe and contains synchronous file I/O - * (reading GoogleService-Info.plist from disk). + * and before using Firebase services. This method should be called from the main thread and + * contains synchronous file I/O (reading GoogleService-Info.plist from disk). */ + (void)configure; /** * Configures the default Firebase app with the provided options. The default app is named - * "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method is thread - * safe. + * "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method should be + * called from the main thread. * * @param options The Firebase application options used to configure the service. */ @@ -63,7 +63,7 @@ NS_SWIFT_NAME(FirebaseApp) /** * Configures a Firebase app with the given name and options. Raises an exception if any - * configuration step fails. This method is thread safe. + * configuration step fails. This method should be called from the main thread. * * @param name The application's name given by the developer. The name should should only contain Letters, Numbers and Underscore. diff --git a/ios/Pods/FirebaseCore/README.md b/ios/Pods/FirebaseCore/README.md index 5097a89ae..1747cca7a 100644 --- a/ios/Pods/FirebaseCore/README.md +++ b/ios/Pods/FirebaseCore/README.md @@ -1,10 +1,24 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +83,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +134,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +149,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +180,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +191,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,32 +217,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst +### tvOS, macOS, watchOS and Catalyst Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). During app setup in the console, you may get to a step that mentions something like "Checking if the app -has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/Catalyst. +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. **It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Crashlytics' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -252,3 +272,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m index bb0326be1..87d57c4e8 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m +++ b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m @@ -125,6 +125,7 @@ NSString *const kFIRCoreDiagnosticsHeartbeatDateFileName = @"FIREBASE_DIAGNOSTIC // Encode a 2nd time to actually get the bytes from it. size_t bufferSize = sizestream.bytes_written; CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize); + CFDataSetLength(dataRef, bufferSize); pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize); if (!pb_encode(&ostream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) { GDTCORLogError(GDTCORMCETransportBytesError, @"Error in nanopb encoding for bytes: %s", @@ -219,7 +220,7 @@ NS_ASSUME_NONNULL_END #pragma mark - nanopb helper functions -/** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array. +/** Callocs a pb_bytes_array and copies the given NSString's bytes into the bytes array. * * @note Memory needs to be free manually, through pb_free or pb_release. * @param string The string to encode as pb_bytes. @@ -229,16 +230,18 @@ pb_bytes_array_t *FIREncodeString(NSString *string) { return FIREncodeData(stringBytes); } -/** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array. +/** Callocs a pb_bytes_array and copies the given NSData bytes into the bytes array. * * @note Memory needs to be free manually, through pb_free or pb_release. * @param data The data to copy into the new bytes array. */ pb_bytes_array_t *FIREncodeData(NSData *data) { - pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); - memcpy(pbBytes->bytes, [data bytes], data.length); - pbBytes->size = (pb_size_t)data.length; - return pbBytes; + pb_bytes_array_t *pbBytesArray = calloc(1, PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); + if (pbBytesArray != NULL) { + [data getBytes:pbBytesArray->bytes length:data.length]; + pbBytesArray->size = (pb_size_t)data.length; + } + return pbBytesArray; } /** Maps a service string to the representative nanopb enum. @@ -507,8 +510,11 @@ void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfigu } logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *servicesInstalled = - malloc(sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType) * - sdkServiceInstalledArray.count); + calloc(sdkServiceInstalledArray.count, + sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType)); + if (servicesInstalled == NULL) { + return; + } for (NSUInteger i = 0; i < sdkServiceInstalledArray.count; i++) { NSNumber *typeEnum = sdkServiceInstalledArray[i]; logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType serviceType = diff --git a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.c b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.c index 4b2ac2f74..0bdb27e28 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.c +++ b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.c @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9.3 */ +/* Generated by nanopb-0.3.9.5 */ #include "firebasecore.nanopb.h" diff --git a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h index 3e4c1950d..59d1b3b0d 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h +++ b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9.3 */ +/* Generated by nanopb-0.3.9.5 */ #ifndef PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED #define PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED diff --git a/ios/Pods/FirebaseCoreDiagnostics/README.md b/ios/Pods/FirebaseCoreDiagnostics/README.md index 3ddc8fbd2..1747cca7a 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/README.md +++ b/ios/Pods/FirebaseCoreDiagnostics/README.md @@ -1,10 +1,24 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +83,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +134,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +149,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +180,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +191,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,29 +217,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst -Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore, -FirebaseDatabase, FirebaseMessaging, FirebaseFirestore, -FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +### tvOS, macOS, watchOS and Catalyst +Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). +During app setup in the console, you may get to a step that mentions something like "Checking if the app +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. +**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. + To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -249,3 +272,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m index 1c9dbabeb..b2b69313e 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m @@ -22,8 +22,9 @@ #import "FBLPromises.h" #endif +#import <GoogleUtilities/GULKeychainUtils.h> + #import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsKeychainUtils.h" static NSString *const kFIRInstallationsIIDTokenKeychainId = @"com.google.iid-tokens"; @@ -118,7 +119,7 @@ static NSString *const kFIRInstallationsIIDTokenKeychainId = @"com.google.iid-to NSMutableDictionary *keychainQuery = [self IIDDefaultTokenDataKeychainQuery]; NSError *error; - NSData *data = [FIRInstallationsKeychainUtils getItemWithQuery:keychainQuery error:&error]; + NSData *data = [GULKeychainUtils getItemWithQuery:keychainQuery error:&error]; if (data) { [resultPromise fulfill:data]; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m index 5bd7e3b98..6e7462203 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m @@ -92,17 +92,21 @@ NS_ASSUME_NONNULL_END #pragma mark - Public - (FBLPromise<FIRInstallationsItem *> *)registerInstallation:(FIRInstallationsItem *)installation { - NSURLRequest *request = [self registerRequestWithInstallation:installation]; - return [self sendURLRequest:request].then( - ^id _Nullable(FIRInstallationsURLSessionResponse *response) { + return [self registerRequestWithInstallation:installation] + .then(^id _Nullable(NSURLRequest *_Nullable request) { + return [self sendURLRequest:request]; + }) + .then(^id _Nullable(FIRInstallationsURLSessionResponse *response) { return [self registeredInstallationWithInstallation:installation serverResponse:response]; }); } - (FBLPromise<FIRInstallationsItem *> *)refreshAuthTokenForInstallation: (FIRInstallationsItem *)installation { - NSURLRequest *request = [self authTokenRequestWithInstallation:installation]; - return [self sendURLRequest:request] + return [self authTokenRequestWithInstallation:installation] + .then(^id _Nullable(NSURLRequest *_Nullable request) { + return [self sendURLRequest:request]; + }) .then(^FBLPromise<FIRInstallationsStoredAuthToken *> *( FIRInstallationsURLSessionResponse *response) { return [self authTokenWithServerResponse:response]; @@ -115,17 +119,20 @@ NS_ASSUME_NONNULL_END } - (FBLPromise<FIRInstallationsItem *> *)deleteInstallation:(FIRInstallationsItem *)installation { - NSURLRequest *request = [self deleteInstallationRequestWithInstallation:installation]; - return [[self sendURLRequest:request] - then:^id _Nullable(FIRInstallationsURLSessionResponse *_Nullable value) { + return [self deleteInstallationRequestWithInstallation:installation] + .then(^id _Nullable(NSURLRequest *_Nullable request) { + return [self sendURLRequest:request]; + }) + .then(^id _Nullable(FIRInstallationsURLSessionResponse *_Nullable value) { // Return the original installation on success. return installation; - }]; + }); } #pragma mark - Register Installation -- (NSURLRequest *)registerRequestWithInstallation:(FIRInstallationsItem *)installation { +- (FBLPromise<NSURLRequest *> *)registerRequestWithInstallation: + (FIRInstallationsItem *)installation { NSString *URLString = [NSString stringWithFormat:@"%@/v1/projects/%@/installations/", kFIRInstallationsAPIBaseURL, self.projectID]; NSURL *URL = [NSURL URLWithString:URLString]; @@ -176,7 +183,8 @@ NS_ASSUME_NONNULL_END #pragma mark - Auth token -- (NSURLRequest *)authTokenRequestWithInstallation:(FIRInstallationsItem *)installation { +- (FBLPromise<NSURLRequest *> *)authTokenRequestWithInstallation: + (FIRInstallationsItem *)installation { NSString *URLString = [NSString stringWithFormat:@"%@/v1/projects/%@/installations/%@/authTokens:generate", kFIRInstallationsAPIBaseURL, self.projectID, @@ -216,7 +224,8 @@ NS_ASSUME_NONNULL_END #pragma mark - Delete Installation -- (NSURLRequest *)deleteInstallationRequestWithInstallation:(FIRInstallationsItem *)installation { +- (FBLPromise<NSURLRequest *> *)deleteInstallationRequestWithInstallation: + (FIRInstallationsItem *)installation { NSString *URLString = [NSString stringWithFormat:@"%@/v1/projects/%@/installations/%@/", kFIRInstallationsAPIBaseURL, self.projectID, installation.firebaseInstallationID]; @@ -229,10 +238,10 @@ NS_ASSUME_NONNULL_END } #pragma mark - URL Request -- (NSURLRequest *)requestWithURL:(NSURL *)requestURL - HTTPMethod:(NSString *)HTTPMethod - bodyDict:(NSDictionary *)bodyDict - refreshToken:(nullable NSString *)refreshToken { +- (FBLPromise<NSURLRequest *> *)requestWithURL:(NSURL *)requestURL + HTTPMethod:(NSString *)HTTPMethod + bodyDict:(NSDictionary *)bodyDict + refreshToken:(nullable NSString *)refreshToken { return [self requestWithURL:requestURL HTTPMethod:HTTPMethod bodyDict:bodyDict @@ -240,34 +249,41 @@ NS_ASSUME_NONNULL_END additionalHeaders:nil]; } -- (NSURLRequest *)requestWithURL:(NSURL *)requestURL - HTTPMethod:(NSString *)HTTPMethod - bodyDict:(NSDictionary *)bodyDict - refreshToken:(nullable NSString *)refreshToken - additionalHeaders: - (nullable NSDictionary<NSString *, NSString *> *)additionalHeaders { - __block NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL]; - request.HTTPMethod = HTTPMethod; - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - [request addValue:self.APIKey forHTTPHeaderField:kFIRInstallationsAPIKey]; - [request addValue:bundleIdentifier forHTTPHeaderField:kFIRInstallationsBundleId]; - [self setJSONHTTPBody:bodyDict forRequest:request]; - if (refreshToken) { - NSString *authHeader = [NSString stringWithFormat:@"FIS_v2 %@", refreshToken]; - [request setValue:authHeader forHTTPHeaderField:@"Authorization"]; - } - // User agent Header. - [request setValue:[FIRApp firebaseUserAgent] forHTTPHeaderField:kFIRInstallationsUserAgentKey]; - // Heartbeat Header. - [request setValue:@([FIRHeartbeatInfo heartbeatCodeForTag:kFIRInstallationsHeartbeatTag]) - .stringValue - forHTTPHeaderField:kFIRInstallationsHeartbeatKey]; - [additionalHeaders enumerateKeysAndObjectsUsingBlock:^( - NSString *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) { - [request setValue:obj forHTTPHeaderField:key]; - }]; +- (FBLPromise<NSURLRequest *> *)requestWithURL:(NSURL *)requestURL + HTTPMethod:(NSString *)HTTPMethod + bodyDict:(NSDictionary *)bodyDict + refreshToken:(nullable NSString *)refreshToken + additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *) + additionalHeaders { + return [FBLPromise + onQueue:dispatch_get_global_queue(QOS_CLASS_UTILITY, 0) + do:^id _Nullable { + __block NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL]; + request.HTTPMethod = HTTPMethod; + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + [request addValue:self.APIKey forHTTPHeaderField:kFIRInstallationsAPIKey]; + [request addValue:bundleIdentifier forHTTPHeaderField:kFIRInstallationsBundleId]; + [self setJSONHTTPBody:bodyDict forRequest:request]; + if (refreshToken) { + NSString *authHeader = [NSString stringWithFormat:@"FIS_v2 %@", refreshToken]; + [request setValue:authHeader forHTTPHeaderField:@"Authorization"]; + } + // User agent Header. + [request setValue:[FIRApp firebaseUserAgent] + forHTTPHeaderField:kFIRInstallationsUserAgentKey]; + // Heartbeat Header. + [request setValue:@([FIRHeartbeatInfo + heartbeatCodeForTag:kFIRInstallationsHeartbeatTag]) + .stringValue + forHTTPHeaderField:kFIRInstallationsHeartbeatKey]; + [additionalHeaders + enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSString *_Nonnull obj, + BOOL *_Nonnull stop) { + [request setValue:obj forHTTPHeaderField:key]; + }]; - return [request copy]; + return [request copy]; + }]; } - (FBLPromise<FIRInstallationsURLSessionResponse *> *)URLRequestPromise:(NSURLRequest *)request { diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m index 1982a5789..1b8ddcbfa 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m @@ -23,6 +23,7 @@ #endif #import <FirebaseCore/FIRAppInternal.h> +#import <GoogleUtilities/GULKeychainStorage.h> #import "FIRInstallationsAPIService.h" #import "FIRInstallationsErrorUtil.h" @@ -32,7 +33,6 @@ #import "FIRInstallationsLogger.h" #import "FIRInstallationsSingleOperationPromiseCache.h" #import "FIRInstallationsStore.h" -#import "FIRSecureStorage.h" #import "FIRInstallationsHTTPError.h" #import "FIRInstallationsStoredAuthToken.h" @@ -72,7 +72,8 @@ NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 projectID:(NSString *)projectID GCMSenderID:(NSString *)GCMSenderID accessGroup:(NSString *)accessGroup { - FIRSecureStorage *secureStorage = [[FIRSecureStorage alloc] init]; + GULKeychainStorage *secureStorage = + [[GULKeychainStorage alloc] initWithService:@"com.firebase.FIRInstallations.installations"]; FIRInstallationsStore *installationsStore = [[FIRInstallationsStore alloc] initWithSecureStorage:secureStorage accessGroup:accessGroup]; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h index 5334cc98b..b86fb39ad 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h @@ -18,7 +18,7 @@ @class FBLPromise<ValueType>; @class FIRInstallationsItem; -@class FIRSecureStorage; +@class GULKeychainStorage; NS_ASSUME_NONNULL_BEGIN @@ -33,7 +33,7 @@ extern NSString *const kFIRInstallationsStoreUserDefaultsID; * @param storage The secure storage to save installations data. * @param accessGroup The Keychain Access Group to store and request the installations data. */ -- (instancetype)initWithSecureStorage:(FIRSecureStorage *)storage +- (instancetype)initWithSecureStorage:(GULKeychainStorage *)storage accessGroup:(nullable NSString *)accessGroup; /** diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m index 9fcfd7488..46f58dba7 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m @@ -24,15 +24,16 @@ #import "FBLPromises.h" #endif +#import <GoogleUtilities/GULKeychainStorage.h> + #import "FIRInstallationsErrorUtil.h" #import "FIRInstallationsItem.h" #import "FIRInstallationsStoredItem.h" -#import "FIRSecureStorage.h" NSString *const kFIRInstallationsStoreUserDefaultsID = @"com.firebase.FIRInstallations"; @interface FIRInstallationsStore () -@property(nonatomic, readonly) FIRSecureStorage *secureStorage; +@property(nonatomic, readonly) GULKeychainStorage *secureStorage; @property(nonatomic, readonly, nullable) NSString *accessGroup; @property(nonatomic, readonly) dispatch_queue_t queue; @property(nonatomic, readonly) GULUserDefaults *userDefaults; @@ -40,7 +41,7 @@ NSString *const kFIRInstallationsStoreUserDefaultsID = @"com.firebase.FIRInstall @implementation FIRInstallationsStore -- (instancetype)initWithSecureStorage:(FIRSecureStorage *)storage +- (instancetype)initWithSecureStorage:(GULKeychainStorage *)storage accessGroup:(NSString *)accessGroup { self = [super init]; if (self) { diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.h deleted file mode 100644 index 4d73ec00f..000000000 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -/// Helper functions to access Keychain. -@interface FIRInstallationsKeychainUtils : NSObject - -+ (nullable NSData *)getItemWithQuery:(NSDictionary *)query - error:(NSError *_Nullable *_Nullable)outError; - -+ (BOOL)setItem:(NSData *)item - withQuery:(NSDictionary *)query - error:(NSError *_Nullable *_Nullable)outError; - -+ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/README.md b/ios/Pods/FirebaseInstallations/README.md index 3ddc8fbd2..b04a27099 100644 --- a/ios/Pods/FirebaseInstallations/README.md +++ b/ios/Pods/FirebaseInstallations/README.md @@ -1,10 +1,19 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +# Firebase iOS Open Source Development + [![Actions Status][gh-auth-badge]][gh-actions] + [![Actions Status][gh-core-badge]][gh-actions] + [![Actions Status][gh-datatransport-badge]][gh-actions] + [![Actions Status][gh-dynamiclinks-badge]][gh-actions] + [![Actions Status][gh-firebasepod-badge]][gh-actions] + [![Actions Status][gh-firestore-badge]][gh-actions] + [![Actions Status][gh-interop-badge]][gh-actions] + [![Actions Status][gh-messaging-badge]][gh-actions] + [![Actions Status][gh-storage-badge]][gh-actions] + [![Actions Status][gh-symbolcollision-badge]][gh-actions] + [![Actions Status][gh-zip-badge]][gh-actions] + [![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +This repository contains all Firebase iOS SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +78,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +129,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +144,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +175,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +186,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,29 +212,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst -Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore, -FirebaseDatabase, FirebaseMessaging, FirebaseFirestore, -FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +### tvOS, macOS, watchOS and Catalyst +Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). +During app setup in the console, you may get to a step that mentions something like "Checking if the app +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. +**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. + To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -249,3 +267,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRIMessageCode.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRIMessageCode.h deleted file mode 100644 index e3e36e9df..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRIMessageCode.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -// The format of the debug code will show in the log as: e.g. -// for code 1000, it will show as I-IID001000. -typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) { - // DO NOT USE 2000, 2002. - kFIRInstanceIDMessageCodeFIRApp000 = 1000, // I-IID001000 - kFIRInstanceIDMessageCodeFIRApp001 = 1001, - kFIRInstanceIDMessageCodeFIRApp002 = 1002, - kFIRInstanceIDMessageCodeInternal001 = 2001, - kFIRInstanceIDMessageCodeInternal002 = 2002, - // FIRInstanceID.m - // DO NOT USE 4000. - kFIRInstanceIDMessageCodeInstanceID000 = 3000, - kFIRInstanceIDMessageCodeInstanceID001 = 3001, - kFIRInstanceIDMessageCodeInstanceID002 = 3002, - kFIRInstanceIDMessageCodeInstanceID003 = 3003, - kFIRInstanceIDMessageCodeInstanceID004 = 3004, - kFIRInstanceIDMessageCodeInstanceID005 = 3005, - kFIRInstanceIDMessageCodeInstanceID006 = 3006, - kFIRInstanceIDMessageCodeInstanceID007 = 3007, - kFIRInstanceIDMessageCodeInstanceID008 = 3008, - kFIRInstanceIDMessageCodeInstanceID009 = 3009, - kFIRInstanceIDMessageCodeInstanceID010 = 3010, - kFIRInstanceIDMessageCodeInstanceID011 = 3011, - kFIRInstanceIDMessageCodeInstanceID012 = 3012, - kFIRInstanceIDMessageCodeInstanceID013 = 3013, - kFIRInstanceIDMessageCodeInstanceID014 = 3014, - kFIRInstanceIDMessageCodeInstanceID015 = 3015, - kFIRInstanceIDMessageCodeRefetchingTokenForAPNS = 3016, - kFIRInstanceIDMessageCodeInstanceID017 = 3017, - kFIRInstanceIDMessageCodeInstanceID018 = 3018, - // FIRInstanceIDAuthService.m - kFIRInstanceIDMessageCodeAuthService000 = 5000, - kFIRInstanceIDMessageCodeAuthService001 = 5001, - kFIRInstanceIDMessageCodeAuthService002 = 5002, - kFIRInstanceIDMessageCodeAuthService003 = 5003, - kFIRInstanceIDMessageCodeAuthService004 = 5004, - kFIRInstanceIDMessageCodeAuthServiceCheckinInProgress = 5004, - - // FIRInstanceIDBackupExcludedPlist.m - // Do NOT USE 6003 - kFIRInstanceIDMessageCodeBackupExcludedPlist000 = 6000, - kFIRInstanceIDMessageCodeBackupExcludedPlist001 = 6001, - kFIRInstanceIDMessageCodeBackupExcludedPlist002 = 6002, - // FIRInstanceIDCheckinService.m - kFIRInstanceIDMessageCodeService000 = 7000, - kFIRInstanceIDMessageCodeService001 = 7001, - kFIRInstanceIDMessageCodeService002 = 7002, - kFIRInstanceIDMessageCodeService003 = 7003, - kFIRInstanceIDMessageCodeService004 = 7004, - kFIRInstanceIDMessageCodeService005 = 7005, - kFIRInstanceIDMessageCodeService006 = 7006, - kFIRInstanceIDInvalidNetworkSession = 7007, - kFIRInstanceIDInvalidSettingResponse = 7008, - // FIRInstanceIDCheckinStore.m - // DO NOT USE 8002, 8004 - 8008 - kFIRInstanceIDMessageCodeCheckinStore000 = 8000, - kFIRInstanceIDMessageCodeCheckinStore001 = 8001, - kFIRInstanceIDMessageCodeCheckinStore003 = 8003, - kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted = 8009, - kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved = 8010, - - // DO NOT USE 9000 - 9006 - - // DO NOT USE 10000 - 10009 - - // DO NOT USE 11000 - 11002 - - // DO NOT USE 12000 - 12014 - - // FIRInstanceIDStore.m - // DO NOT USE 13004, 13005, 13007, 13008, 13010, 13011, 13013, 13014 - kFIRInstanceIDMessageCodeStore000 = 13000, - kFIRInstanceIDMessageCodeStore001 = 13001, - kFIRInstanceIDMessageCodeStore002 = 13002, - kFIRInstanceIDMessageCodeStore003 = 13003, - kFIRInstanceIDMessageCodeStore006 = 13006, - kFIRInstanceIDMessageCodeStore009 = 13009, - kFIRInstanceIDMessageCodeStore012 = 13012, - // FIRInstanceIDTokenManager.m - // DO NOT USE 14002, 14005 - kFIRInstanceIDMessageCodeTokenManager000 = 14000, - kFIRInstanceIDMessageCodeTokenManager001 = 14001, - kFIRInstanceIDMessageCodeTokenManager003 = 14003, - kFIRInstanceIDMessageCodeTokenManager004 = 14004, - kFIRInstanceIDMessageCodeTokenManagerErrorDeletingFCMTokensOnAppReset = 14006, - kFIRInstanceIDMessageCodeTokenManagerDeletedFCMTokensOnAppReset = 14007, - kFIRInstanceIDMessageCodeTokenManagerSavedAppVersion = 14008, - kFIRInstanceIDMessageCodeTokenManagerErrorInvalidatingAllTokens = 14009, - kFIRInstanceIDMessageCodeTokenManagerAPNSChanged = 14010, - kFIRInstanceIDMessageCodeTokenManagerAPNSChangedTokenInvalidated = 14011, - kFIRInstanceIDMessageCodeTokenManagerInvalidateStaleToken = 14012, - // FIRInstanceIDTokenStore.m - // DO NOT USE 15002 - 15013 - kFIRInstanceIDMessageCodeTokenStore000 = 15000, - kFIRInstanceIDMessageCodeTokenStore001 = 15001, - kFIRInstanceIDMessageCodeTokenStoreExceptionUnarchivingTokenInfo = 15015, - - // DO NOT USE 16000, 18004 - - // FIRInstanceIDUtilities.m - kFIRInstanceIDMessageCodeUtilitiesMissingBundleIdentifier = 18000, - kFIRInstanceIDMessageCodeUtilitiesAppEnvironmentUtilNotAvailable = 18001, - kFIRInstanceIDMessageCodeUtilitiesCannotGetHardwareModel = 18002, - kFIRInstanceIDMessageCodeUtilitiesCannotGetSystemVersion = 18003, - // FIRInstanceIDTokenOperation.m - kFIRInstanceIDMessageCodeTokenOperationFailedToSignParams = 19000, - // FIRInstanceIDTokenFetchOperation.m - // DO NOT USE 20004, 20005 - kFIRInstanceIDMessageCodeTokenFetchOperationFetchRequest = 20000, - kFIRInstanceIDMessageCodeTokenFetchOperationRequestError = 20001, - kFIRInstanceIDMessageCodeTokenFetchOperationBadResponse = 20002, - kFIRInstanceIDMessageCodeTokenFetchOperationBadTokenStructure = 20003, - // FIRInstanceIDTokenDeleteOperation.m - kFIRInstanceIDMessageCodeTokenDeleteOperationFetchRequest = 21000, - kFIRInstanceIDMessageCodeTokenDeleteOperationRequestError = 21001, - kFIRInstanceIDMessageCodeTokenDeleteOperationBadResponse = 21002, - // FIRInstanceIDTokenInfo.m - kFIRInstanceIDMessageCodeTokenInfoBadAPNSInfo = 22000, - kFIRInstanceIDMessageCodeTokenInfoFirebaseAppIDChanged = 22001, - kFIRInstanceIDMessageCodeTokenInfoLocaleChanged = 22002, - // FIRInstanceIDKeychain.m - kFIRInstanceIDKeychainReadItemError = 23000, - kFIRInstanceIDKeychainAddItemError = 23001, - kFIRInstanceIDKeychainDeleteItemError = 23002, - kFIRInstanceIDKeychainCreateKeyPairError = 23003, - kFIRInstanceIDKeychainUpdateItemError = 23004, - - // FIRInstanceIDStringEncoding.m - kFIRInstanceIDStringEncodingBufferUnderflow = 24000, - kFIRInstanceIDStringEncodingBufferOverflow = 24001, - -}; diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID+Private.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID+Private.m deleted file mode 100644 index c5ffe7a0a..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID+Private.m +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceID+Private.h" - -#import <FirebaseInstallations/FirebaseInstallations.h> - -#import <FirebaseInstanceID/FIRInstanceID_Private.h> -#import "FIRInstanceIDAuthService.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDTokenManager.h" - -@class FIRInstallations; - -@interface FIRInstanceID () - -@property(nonatomic, readonly, strong) FIRInstanceIDTokenManager *tokenManager; - -@end - -@implementation FIRInstanceID (Private) - -// This method just wraps our pre-configured auth service to make the request. -// This method is only needed by first-party users, like Remote Config. -- (void)fetchCheckinInfoWithHandler:(FIRInstanceIDDeviceCheckinCompletion)handler { - [self.tokenManager.authService fetchCheckinInfoWithHandler:handler]; -} - -// TODO(#4486): Delete the method, `self.firebaseInstallationsID` and related -// code for Firebase 7 release. -- (NSString *)appInstanceID:(NSError **)outError { - return self.firebaseInstallationsID; -} - -#pragma mark - Firebase Installations Compatibility - -/// Presence of this method indicates that this version of IID uses FirebaseInstallations under the -/// hood. It is checked by FirebaseInstallations SDK. -+ (BOOL)usesFIS { - return YES; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m deleted file mode 100644 index f9cba4f39..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m +++ /dev/null @@ -1,1130 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceID.h" - -#import <FirebaseInstallations/FIRInstallations.h> - -#import <FirebaseCore/FIRAppInternal.h> -#import <FirebaseCore/FIRComponent.h> -#import <FirebaseCore/FIRComponentContainer.h> -#import <FirebaseCore/FIRLibrary.h> -#import <FirebaseCore/FIROptions.h> -#import <GoogleUtilities/GULAppEnvironmentUtil.h> -#import <GoogleUtilities/GULUserDefaults.h> -#import "FIRInstanceID+Private.h" -#import "FIRInstanceIDAuthService.h" -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDCombinedHandler.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDStore.h" -#import "FIRInstanceIDTokenInfo.h" -#import "FIRInstanceIDTokenManager.h" -#import "FIRInstanceIDUtilities.h" -#import "FIRInstanceIDVersionUtilities.h" -#import "NSError+FIRInstanceID.h" - -// Public constants -NSString *const kFIRInstanceIDScopeFirebaseMessaging = @"fcm"; - -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 -const NSNotificationName kFIRInstanceIDTokenRefreshNotification = - @"com.firebase.iid.notif.refresh-token"; -#else -NSString *const kFIRInstanceIDTokenRefreshNotification = @"com.firebase.iid.notif.refresh-token"; -#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - -NSString *const kFIRInstanceIDInvalidNilHandlerError = @"Invalid nil handler."; - -// Private constants -int64_t const kMaxRetryIntervalForDefaultTokenInSeconds = 20 * 60; // 20 minutes -int64_t const kMinRetryIntervalForDefaultTokenInSeconds = 10; // 10 seconds -// we retry only a max 5 times. -// TODO(chliangGoogle): If we still fail we should listen for the network change notification -// since GCM would have started Reachability. We only start retrying after we see a configuration -// change. -NSInteger const kMaxRetryCountForDefaultToken = 5; - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH -static NSString *const kEntitlementsAPSEnvironmentKey = @"Entitlements.aps-environment"; -#else -static NSString *const kEntitlementsAPSEnvironmentKey = - @"Entitlements.com.apple.developer.aps-environment"; -#endif -static NSString *const kAPSEnvironmentDevelopmentValue = @"development"; -/// FIRMessaging selector that returns the current FIRMessaging auto init -/// enabled flag. -static NSString *const kFIRInstanceIDFCMSelectorAutoInitEnabled = - @"isAutoInitEnabledWithUserDefaults:"; - -static NSString *const kFIRInstanceIDAPNSTokenType = @"APNSTokenType"; -static NSString *const kFIRIIDAppReadyToConfigureSDKNotification = - @"FIRAppReadyToConfigureSDKNotification"; -static NSString *const kFIRIIDAppNameKey = @"FIRAppNameKey"; -static NSString *const kFIRIIDErrorDomain = @"com.firebase.instanceid"; -static NSString *const kFIRIIDServiceInstanceID = @"InstanceID"; - -/** - * The APNS token type for the app. If the token type is set to `UNKNOWN` - * InstanceID will implicitly try to figure out what the actual token type - * is from the provisioning profile. - * This must match FIRMessagingAPNSTokenType in FIRMessaging.h - */ -typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) { - /// Unknown token type. - FIRInstanceIDAPNSTokenTypeUnknown, - /// Sandbox token type. - FIRInstanceIDAPNSTokenTypeSandbox, - /// Production token type. - FIRInstanceIDAPNSTokenTypeProd, -} NS_SWIFT_NAME(InstanceIDAPNSTokenType); - -@interface FIRInstanceIDResult () -@property(nonatomic, readwrite, copy) NSString *instanceID; -@property(nonatomic, readwrite, copy) NSString *token; -@end - -@interface FIRInstanceID () - -// FIRApp configuration objects. -@property(nonatomic, readwrite, copy) NSString *fcmSenderID; -@property(nonatomic, readwrite, copy) NSString *firebaseAppID; - -// Raw APNS token data -@property(nonatomic, readwrite, strong) NSData *apnsTokenData; - -@property(nonatomic, readwrite) FIRInstanceIDAPNSTokenType apnsTokenType; -// String-based, internal representation of APNS token -@property(nonatomic, readwrite, copy) NSString *APNSTupleString; -// Token fetched from the server automatically for the default app. -@property(nonatomic, readwrite, copy) NSString *defaultFCMToken; - -@property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager; -@property(nonatomic, readwrite, strong) FIRInstallations *installations; - -// backoff and retry for default token -@property(nonatomic, readwrite, assign) NSInteger retryCountForDefaultToken; -@property(atomic, strong, nullable) - FIRInstanceIDCombinedHandler<NSString *> *defaultTokenFetchHandler; - -/// A cached value of FID. Should be used only for `-[FIRInstanceID appInstanceID:]`. -@property(atomic, copy, nullable) NSString *firebaseInstallationsID; - -@end - -// InstanceID doesn't provide any functionality to other components, -// so it provides a private, empty protocol that it conforms to and use it for registration. - -@protocol FIRInstanceIDInstanceProvider -@end - -@interface FIRInstanceID () <FIRInstanceIDInstanceProvider, FIRLibrary> -@end - -@implementation FIRInstanceIDResult -- (id)copyWithZone:(NSZone *)zone { - FIRInstanceIDResult *result = [[[self class] allocWithZone:zone] init]; - result.instanceID = self.instanceID; - result.token = self.token; - return result; -} -@end - -@implementation FIRInstanceID - -// File static to support InstanceID tests that call [FIRInstanceID instanceID] after -// [FIRInstanceID instanceIDForTests]. -static FIRInstanceID *gInstanceID; - -+ (instancetype)instanceID { - // If the static instance was created, return it. This should only be set in tests and we should - // eventually use proper dependency injection for a better test structure. - if (gInstanceID != nil) { - return gInstanceID; - } - FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here. - FIRInstanceID *instanceID = - (FIRInstanceID *)FIR_COMPONENT(FIRInstanceIDInstanceProvider, defaultApp.container); - return instanceID; -} - -- (instancetype)initPrivately { - self = [super init]; - if (self != nil) { - // Use automatic detection of sandbox, unless otherwise set by developer - _apnsTokenType = FIRInstanceIDAPNSTokenTypeUnknown; - } - return self; -} - -+ (FIRInstanceID *)instanceIDForTests { - gInstanceID = [[FIRInstanceID alloc] initPrivately]; - [gInstanceID start]; - return gInstanceID; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - Tokens - -- (NSString *)token { - if (!self.fcmSenderID.length) { - return nil; - } - - NSString *cachedToken = [self cachedTokenIfAvailable]; - - if (cachedToken) { - return cachedToken; - } else { - // If we've never had a cached default token, we should fetch one because unrelatedly, - // this request will help us determine whether the locally-generated Instance ID keypair is not - // unique, and therefore generate a new one. - [self defaultTokenWithHandler:nil]; - return nil; - } -} - -- (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler { - FIRInstanceID_WEAKIFY(self); - [self getIDWithHandler:^(NSString *identity, NSError *error) { - FIRInstanceID_STRONGIFY(self); - // This is in main queue already - if (error) { - if (handler) { - handler(nil, error); - } - return; - } - FIRInstanceIDResult *result = [[FIRInstanceIDResult alloc] init]; - result.instanceID = identity; - NSString *cachedToken = [self cachedTokenIfAvailable]; - if (cachedToken) { - if (handler) { - result.token = cachedToken; - handler(result, nil); - } - // If no handler, simply return since client has generated iid and token. - return; - } - [self defaultTokenWithHandler:^(NSString *_Nullable token, NSError *_Nullable error) { - if (handler) { - if (error) { - handler(nil, error); - return; - } - result.token = token; - handler(result, nil); - } - }]; - }]; -} - -- (NSString *)cachedTokenIfAvailable { - FIRInstanceIDTokenInfo *cachedTokenInfo = - [self.tokenManager cachedTokenInfoWithAuthorizedEntity:self.fcmSenderID - scope:kFIRInstanceIDDefaultTokenScope]; - return cachedTokenInfo.token; -} - -- (void)setDefaultFCMToken:(NSString *)defaultFCMToken { - if (_defaultFCMToken && defaultFCMToken && [defaultFCMToken isEqualToString:_defaultFCMToken]) { - return; - } - - _defaultFCMToken = defaultFCMToken; - - // Sending this notification out will ensure that FIRMessaging has the updated - // default FCM token. - NSNotification *internalDefaultTokenNotification = - [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenNotification - object:_defaultFCMToken]; - [[NSNotificationQueue defaultQueue] enqueueNotification:internalDefaultTokenNotification - postingStyle:NSPostASAP]; -} - -- (void)tokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(NSDictionary *)options - handler:(FIRInstanceIDTokenHandler)handler { - if (!handler) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID000, - kFIRInstanceIDInvalidNilHandlerError); - return; - } - - // Add internal options - NSMutableDictionary *tokenOptions = [NSMutableDictionary dictionary]; - if (options.count) { - [tokenOptions addEntriesFromDictionary:options]; - } - - NSString *APNSKey = kFIRInstanceIDTokenOptionsAPNSKey; - NSString *serverTypeKey = kFIRInstanceIDTokenOptionsAPNSIsSandboxKey; - if (tokenOptions[APNSKey] != nil && tokenOptions[serverTypeKey] == nil) { - // APNS key was given, but server type is missing. Supply the server type with automatic - // checking. This can happen when the token is requested from FCM, which does not include a - // server type during its request. - tokenOptions[serverTypeKey] = @([self isSandboxApp]); - } - if (self.firebaseAppID) { - tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID; - } - - // comparing enums to ints directly throws a warning - FIRInstanceIDErrorCode noError = INT_MAX; - FIRInstanceIDErrorCode errorCode = noError; - if (FIRInstanceIDIsValidGCMScope(scope) && !tokenOptions[APNSKey]) { - errorCode = kFIRInstanceIDErrorCodeMissingAPNSToken; - } else if (FIRInstanceIDIsValidGCMScope(scope) && - ![tokenOptions[APNSKey] isKindOfClass:[NSData class]]) { - errorCode = kFIRInstanceIDErrorCodeInvalidRequest; - } else if (![authorizedEntity length]) { - errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity; - } else if (![scope length]) { - errorCode = kFIRInstanceIDErrorCodeInvalidScope; - } else if (!self.installations) { - errorCode = kFIRInstanceIDErrorCodeInvalidStart; - } - - FIRInstanceIDTokenHandler newHandler = ^(NSString *token, NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler(token, error); - }); - }; - - if (errorCode != noError) { - newHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:errorCode]); - return; - } - - FIRInstanceID_WEAKIFY(self); - FIRInstanceIDAuthService *authService = self.tokenManager.authService; - [authService fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, - NSError *error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - newHandler(nil, error); - return; - } - - FIRInstanceID_WEAKIFY(self); - [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier, - NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - - if (error) { - NSError *newError = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair]; - newHandler(nil, newError); - - } else { - FIRInstanceIDTokenInfo *cachedTokenInfo = - [self.tokenManager cachedTokenInfoWithAuthorizedEntity:authorizedEntity scope:scope]; - if (cachedTokenInfo) { - FIRInstanceIDAPNSInfo *optionsAPNSInfo = - [[FIRInstanceIDAPNSInfo alloc] initWithTokenOptionsDictionary:tokenOptions]; - // Check if APNS Info is changed - if ((!cachedTokenInfo.APNSInfo && !optionsAPNSInfo) || - [cachedTokenInfo.APNSInfo isEqualToAPNSInfo:optionsAPNSInfo]) { - // check if token is fresh - if ([cachedTokenInfo isFreshWithIID:identifier]) { - newHandler(cachedTokenInfo.token, nil); - return; - } - } - } - [self.tokenManager fetchNewTokenWithAuthorizedEntity:[authorizedEntity copy] - scope:[scope copy] - instanceID:identifier - options:tokenOptions - handler:newHandler]; - } - }]; - }]; -} - -- (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - handler:(FIRInstanceIDDeleteTokenHandler)handler { - if (!handler) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID001, - kFIRInstanceIDInvalidNilHandlerError); - } - - // comparing enums to ints directly throws a warning - FIRInstanceIDErrorCode noError = INT_MAX; - FIRInstanceIDErrorCode errorCode = noError; - - if (![authorizedEntity length]) { - errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity; - } else if (![scope length]) { - errorCode = kFIRInstanceIDErrorCodeInvalidScope; - } else if (!self.installations) { - errorCode = kFIRInstanceIDErrorCodeInvalidStart; - } - - FIRInstanceIDDeleteTokenHandler newHandler = ^(NSError *error) { - // If a default token is deleted successfully, reset the defaultFCMToken too. - if (!error && [authorizedEntity isEqualToString:self.fcmSenderID] && - [scope isEqualToString:kFIRInstanceIDDefaultTokenScope]) { - self.defaultFCMToken = nil; - } - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - }; - - if (errorCode != noError) { - newHandler([NSError errorWithFIRInstanceIDErrorCode:errorCode]); - return; - } - - FIRInstanceID_WEAKIFY(self); - FIRInstanceIDAuthService *authService = self.tokenManager.authService; - [authService - fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - newHandler(error); - return; - } - - FIRInstanceID_WEAKIFY(self); - [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier, - NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - NSError *newError = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair]; - newHandler(newError); - - } else { - [self.tokenManager deleteTokenWithAuthorizedEntity:authorizedEntity - scope:scope - instanceID:identifier - handler:newHandler]; - } - }]; - }]; -} - -#pragma mark - Identity - -- (void)getIDWithHandler:(FIRInstanceIDHandler)handler { - if (!handler) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID003, - kFIRInstanceIDInvalidNilHandlerError); - return; - } - - FIRInstanceID_WEAKIFY(self); - [self.installations - installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - // When getID is explicitly called, trigger getToken to make sure token always exists. - // This is to avoid ID conflict (ID is not checked for conflict until we generate a token) - if (identifier) { - [self token]; - } - handler(identifier, error); - }]; -} - -- (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler { - if (!handler) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID004, - kFIRInstanceIDInvalidNilHandlerError); - return; - } - - void (^callHandlerOnMainThread)(NSError *) = ^(NSError *error) { - if ([NSThread isMainThread]) { - handler(error); - return; - } - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - }; - - if (!self.installations) { - FIRInstanceIDErrorCode error = kFIRInstanceIDErrorCodeInvalidStart; - callHandlerOnMainThread([NSError errorWithFIRInstanceIDErrorCode:error]); - return; - } - - FIRInstanceID_WEAKIFY(self); - void (^deleteTokensHandler)(NSError *) = ^void(NSError *error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - callHandlerOnMainThread(error); - return; - } - [self deleteIdentityWithHandler:^(NSError *error) { - callHandlerOnMainThread(error); - }]; - }; - - [self.installations - installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - NSError *newError = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair]; - callHandlerOnMainThread(newError); - } else { - [self.tokenManager deleteAllTokensWithInstanceID:identifier handler:deleteTokensHandler]; - } - }]; -} - -- (void)notifyIdentityReset { - [self deleteIdentityWithHandler:nil]; -} - -// Delete all the local cache checkin, IID and token. -- (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler { - // Delete tokens. - [self.tokenManager deleteAllTokensLocallyWithHandler:^(NSError *deleteTokenError) { - // Reset FCM token. - self.defaultFCMToken = nil; - if (deleteTokenError) { - if (handler) { - handler(deleteTokenError); - } - return; - } - - // Delete Instance ID. - [self.installations deleteWithCompletion:^(NSError *_Nullable error) { - if (error) { - if (handler) { - handler(error); - } - return; - } - - [self.tokenManager.authService resetCheckinWithHandler:^(NSError *error) { - if (error) { - if (handler) { - handler(error); - } - return; - } - // Only request new token if FCM auto initialization is - // enabled. - if ([self isFCMAutoInitEnabled]) { - // Deletion succeeds! Requesting new checkin, IID and token. - // TODO(chliangGoogle) see if dispatch_after is necessary - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self defaultTokenWithHandler:nil]; - }); - } - if (handler) { - handler(nil); - } - }]; - }]; - }]; -} - -#pragma mark - Checkin - -- (BOOL)tryToLoadValidCheckinInfo { - FIRInstanceIDCheckinPreferences *checkinPreferences = - [self.tokenManager.authService checkinPreferences]; - return [checkinPreferences hasValidCheckinInfo]; -} - -- (NSString *)deviceAuthID { - return [self.tokenManager.authService checkinPreferences].deviceID; -} - -- (NSString *)secretToken { - return [self.tokenManager.authService checkinPreferences].secretToken; -} - -- (NSString *)versionInfo { - return [self.tokenManager.authService checkinPreferences].versionInfo; -} - -#pragma mark - Config - -+ (void)load { - [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self - withName:@"fire-iid" - withVersion:FIRInstanceIDCurrentLibraryVersion()]; -} - -+ (nonnull NSArray<FIRComponent *> *)componentsToRegister { - FIRComponentCreationBlock creationBlock = - ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) { - // InstanceID only works with the default app. - if (!container.app.isDefaultApp) { - // Only configure for the default FIRApp. - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeFIRApp002, - @"Firebase Instance ID only works with the default app."); - return nil; - } - - // Ensure it's cached so it returns the same instance every time instanceID is called. - *isCacheable = YES; - FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately]; - [instanceID start]; - [instanceID configureInstanceIDWithOptions:container.app.options]; - return instanceID; - }; - FIRComponent *instanceIDProvider = - [FIRComponent componentWithProtocol:@protocol(FIRInstanceIDInstanceProvider) - instantiationTiming:FIRInstantiationTimingEagerInDefaultApp - dependencies:@[] - creationBlock:creationBlock]; - return @[ instanceIDProvider ]; -} - -- (void)configureInstanceIDWithOptions:(FIROptions *)options { - NSString *GCMSenderID = options.GCMSenderID; - if (!GCMSenderID.length) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeFIRApp000, - @"Firebase not set up correctly, nil or empty senderID."); - [NSException raise:kFIRIIDErrorDomain - format:@"Could not configure Firebase InstanceID. GCMSenderID must not be nil or " - @"empty."]; - } - - self.fcmSenderID = GCMSenderID; - self.firebaseAppID = options.googleAppID; - - [self updateFirebaseInstallationID]; - - // FCM generates a FCM token during app start for sending push notification to device. - // This is not needed for app extension except for watch. -#if TARGET_OS_WATCH - [self didCompleteConfigure]; -#else - if (![GULAppEnvironmentUtil isAppExtension]) { - [self didCompleteConfigure]; - } -#endif -} - -// This is used to start any operations when we receive FirebaseSDK setup notification -// from FIRCore. -- (void)didCompleteConfigure { - NSString *cachedToken = [self cachedTokenIfAvailable]; - // When there is a cached token, do the token refresh. - if (cachedToken) { - // Clean up expired tokens by checking the token refresh policy. - [self.installations - installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) { - if ([self.tokenManager checkTokenRefreshPolicyWithIID:identifier]) { - // Default token is expired, fetch default token from server. - [self defaultTokenWithHandler:nil]; - } - // Notify FCM with the default token. - self.defaultFCMToken = [self token]; - }]; - } else if ([self isFCMAutoInitEnabled]) { - // When there is no cached token, must check auto init is enabled. - // If it's disabled, don't initiate token generation/refresh. - // If no cache token and auto init is enabled, fetch a token from server. - [self defaultTokenWithHandler:nil]; - // Notify FCM with the default token. - self.defaultFCMToken = [self token]; - } - // ONLY checkin when auto data collection is turned on. - if ([self isFCMAutoInitEnabled]) { - [self.tokenManager.authService scheduleCheckin:YES]; - } -} - -- (BOOL)isFCMAutoInitEnabled { - Class messagingClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString); - // Firebase Messaging is not installed, auto init should be disabled since it's for FCM. - if (!messagingClass) { - return NO; - } - - // Messaging doesn't have the class method, auto init should be enabled since FCM exists. - SEL autoInitSelector = NSSelectorFromString(kFIRInstanceIDFCMSelectorAutoInitEnabled); - if (![messagingClass respondsToSelector:autoInitSelector]) { - return YES; - } - - // Get the autoInitEnabled class method. - IMP isAutoInitEnabledIMP = [messagingClass methodForSelector:autoInitSelector]; - BOOL(*isAutoInitEnabled) - (Class, SEL, GULUserDefaults *) = (BOOL(*)(id, SEL, GULUserDefaults *))isAutoInitEnabledIMP; - - // Check FCM's isAutoInitEnabled property. - return isAutoInitEnabled(messagingClass, autoInitSelector, - [GULUserDefaults standardUserDefaults]); -} - -// Actually makes InstanceID instantiate both the IID and Token-related subsystems. -- (void)start { - if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) { - [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName]; - } - - [self setupTokenManager]; - self.installations = [FIRInstallations installations]; - [self setupNotificationListeners]; -} - -// Creates the token manager, which is used for fetching, caching, and retrieving tokens. -- (void)setupTokenManager { - self.tokenManager = [[FIRInstanceIDTokenManager alloc] init]; -} - -- (void)setupNotificationListeners { - // To prevent double notifications remove observer from all events during setup. - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self]; - [center addObserver:self - selector:@selector(notifyIdentityReset) - name:kFIRInstanceIDIdentityInvalidatedNotification - object:nil]; - [center addObserver:self - selector:@selector(notifyAPNSTokenIsSet:) - name:kFIRInstanceIDAPNSTokenNotification - object:nil]; - [self observeFirebaseInstallationIDChanges]; -} - -#pragma mark - Private Helpers -/// Maximum retry count to fetch the default token. -+ (int64_t)maxRetryCountForDefaultToken { - return kMaxRetryCountForDefaultToken; -} - -/// Minimum interval in seconds between retries to fetch the default token. -+ (int64_t)minIntervalForDefaultTokenRetry { - return kMinRetryIntervalForDefaultTokenInSeconds; -} - -/// Maximum retry interval between retries to fetch default token. -+ (int64_t)maxRetryIntervalForDefaultTokenInSeconds { - return kMaxRetryIntervalForDefaultTokenInSeconds; -} - -- (NSInteger)retryIntervalToFetchDefaultToken { - if (self.retryCountForDefaultToken >= [[self class] maxRetryCountForDefaultToken]) { - return (NSInteger)[[self class] maxRetryIntervalForDefaultTokenInSeconds]; - } - // exponential backoff with a fixed initial retry time - // 11s, 22s, 44s, 88s ... - int64_t minInterval = [[self class] minIntervalForDefaultTokenRetry]; - return (NSInteger)MIN( - (1 << self.retryCountForDefaultToken) + minInterval * self.retryCountForDefaultToken, - kMaxRetryIntervalForDefaultTokenInSeconds); -} - -- (void)defaultTokenWithHandler:(nullable FIRInstanceIDTokenHandler)aHandler { - [self defaultTokenWithRetry:NO handler:aHandler]; -} - -/** - * @param retry Indicates if the method is called to perform a retry after a failed attempt. - * If `YES`, then actual token request will be performed even if `self.defaultTokenFetchHandler != - * nil` - */ -- (void)defaultTokenWithRetry:(BOOL)retry handler:(nullable FIRInstanceIDTokenHandler)aHandler { - BOOL shouldPerformRequest = retry || self.defaultTokenFetchHandler == nil; - - if (!self.defaultTokenFetchHandler) { - self.defaultTokenFetchHandler = [[FIRInstanceIDCombinedHandler<NSString *> alloc] init]; - } - - if (aHandler) { - [self.defaultTokenFetchHandler addHandler:aHandler]; - } - - if (!shouldPerformRequest) { - return; - } - - NSDictionary *instanceIDOptions = @{}; - BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil; - if (hasFirebaseMessaging && self.apnsTokenData) { - BOOL isSandboxApp = (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeSandbox); - if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) { - isSandboxApp = [self isSandboxApp]; - } - instanceIDOptions = @{ - kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData, - kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp), - }; - } - - FIRInstanceID_WEAKIFY(self); - FIRInstanceIDTokenHandler newHandler = ^void(NSString *token, NSError *error) { - FIRInstanceID_STRONGIFY(self); - - if (error) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID009, - @"Failed to fetch default token %@", error); - - // This notification can be sent multiple times since we can't guarantee success at any point - // of time. - NSNotification *tokenFetchFailNotification = - [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenFailNotification - object:[error copy]]; - [[NSNotificationQueue defaultQueue] enqueueNotification:tokenFetchFailNotification - postingStyle:NSPostASAP]; - - self.retryCountForDefaultToken = (NSInteger)MIN(self.retryCountForDefaultToken + 1, - [[self class] maxRetryCountForDefaultToken]); - - // Do not retry beyond the maximum limit. - if (self.retryCountForDefaultToken < [[self class] maxRetryCountForDefaultToken]) { - NSInteger retryInterval = [self retryIntervalToFetchDefaultToken]; - [self retryGetDefaultTokenAfter:retryInterval]; - } else { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007, - @"Failed to retrieve the default FCM token after %ld retries", - (long)self.retryCountForDefaultToken); - [self performDefaultTokenHandlerWithToken:nil error:error]; - } - } else { - // If somebody updated IID with APNS token while our initial request did not have it - // set we need to update it on the server. - NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey]; - BOOL isSandboxInRequest = - [instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue]; - // Note that APNSTupleStringInRequest will be nil if deviceTokenInRequest is nil - NSString *APNSTupleStringInRequest = FIRInstanceIDAPNSTupleStringForTokenAndServerType( - deviceTokenInRequest, isSandboxInRequest); - // If the APNs value either remained nil, or was the same non-nil value, the APNs value - // did not change. - BOOL APNSRemainedSameDuringFetch = - (self.APNSTupleString == nil && APNSTupleStringInRequest == nil) || - ([self.APNSTupleString isEqualToString:APNSTupleStringInRequest]); - if (!APNSRemainedSameDuringFetch && hasFirebaseMessaging) { - // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs - // value. - [self retryGetDefaultTokenAfter:0]; - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS, - @"Received APNS token while fetching default token. " - @"Refetching default token."); - // Do not notify and handle completion handler since this is a retry. - // Simply return. - return; - } else { - FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010, - @"Successfully fetched default token."); - } - // Post the required notifications if somebody is waiting. - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@", - token); - NSString *previousFCMToken = self.defaultFCMToken; - self.defaultFCMToken = token; - - // Only notify of token refresh if we have a new valid token that's different than before - if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) { - NSNotification *tokenRefreshNotification = - [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification - object:[self.defaultFCMToken copy]]; - [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification - postingStyle:NSPostASAP]; - } - - [self performDefaultTokenHandlerWithToken:token error:nil]; - } - }; - - [self tokenWithAuthorizedEntity:self.fcmSenderID - scope:kFIRInstanceIDDefaultTokenScope - options:instanceIDOptions - handler:newHandler]; -} - -/** - * - */ -- (void)performDefaultTokenHandlerWithToken:(NSString *)token error:(NSError *)error { - if (!self.defaultTokenFetchHandler) { - return; - } - - [self.defaultTokenFetchHandler combinedHandler](token, error); - self.defaultTokenFetchHandler = nil; -} - -- (void)retryGetDefaultTokenAfter:(NSTimeInterval)retryInterval { - FIRInstanceID_WEAKIFY(self); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - FIRInstanceID_STRONGIFY(self); - // Pass nil: no new handlers to be added, currently existing handlers - // will be called - [self defaultTokenWithRetry:YES handler:nil]; - }); -} - -#pragma mark - APNS Token -// This should only be triggered from FCM. -- (void)notifyAPNSTokenIsSet:(NSNotification *)notification { - NSData *token = notification.object; - if (!token || ![token isKindOfClass:[NSData class]]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInternal002, @"Invalid APNS token type %@", - NSStringFromClass([notification.object class])); - return; - } - NSInteger type = [notification.userInfo[kFIRInstanceIDAPNSTokenType] integerValue]; - - // The APNS token is being added, or has changed (rare) - if ([self.apnsTokenData isEqualToData:token]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID011, - @"Trying to reset APNS token to the same value. Will return"); - return; - } - // Use this token type for when we have to automatically fetch tokens in the future - self.apnsTokenType = type; - BOOL isSandboxApp = (type == FIRInstanceIDAPNSTokenTypeSandbox); - if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) { - isSandboxApp = [self isSandboxApp]; - } - self.apnsTokenData = [token copy]; - self.APNSTupleString = FIRInstanceIDAPNSTupleStringForTokenAndServerType(token, isSandboxApp); - - // Pro-actively invalidate the default token, if the APNs change makes it - // invalid. Previously, we invalidated just before fetching the token. - NSArray<FIRInstanceIDTokenInfo *> *invalidatedTokens = - [self.tokenManager updateTokensToAPNSDeviceToken:self.apnsTokenData isSandbox:isSandboxApp]; - - // Re-fetch any invalidated tokens automatically, this time with the current APNs token, so that - // they are up-to-date. - if (invalidatedTokens.count > 0) { - FIRInstanceID_WEAKIFY(self); - - [self.installations - installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - if (self == nil) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID017, - @"Instance ID shut down during token reset. Aborting"); - return; - } - if (self.apnsTokenData == nil) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID018, - @"apnsTokenData was set to nil during token reset. Aborting"); - return; - } - - NSMutableDictionary *tokenOptions = [@{ - kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData, - kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp) - } mutableCopy]; - if (self.firebaseAppID) { - tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID; - } - - for (FIRInstanceIDTokenInfo *tokenInfo in invalidatedTokens) { - if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) { - // We will perform a special fetch for the default FCM token, so that the delegate - // methods are called. For all others, we will do an internal re-fetch. - [self defaultTokenWithHandler:nil]; - } else { - [self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity - scope:tokenInfo.scope - instanceID:identifier - options:tokenOptions - handler:^(NSString *_Nullable token, - NSError *_Nullable error){ - - }]; - } - } - }]; - } -} - -- (BOOL)isSandboxApp { - static BOOL isSandboxApp = YES; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - isSandboxApp = ![self isProductionApp]; - }); - return isSandboxApp; -} - -- (BOOL)isProductionApp { - const BOOL defaultAppTypeProd = YES; - - NSError *error = nil; - if ([GULAppEnvironmentUtil isSimulator]) { - [self logAPNSConfigurationError:@"Running InstanceID on a simulator doesn't have APNS. " - @"Use prod profile by default."]; - return defaultAppTypeProd; - } - - if ([GULAppEnvironmentUtil isFromAppStore]) { - // Apps distributed via AppStore or TestFlight use the Production APNS certificates. - return defaultAppTypeProd; - } -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - NSString *path = [[[NSBundle mainBundle] bundlePath] - stringByAppendingPathComponent:@"embedded.mobileprovision"]; -#elif TARGET_OS_OSX - NSString *path = [[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent] - stringByAppendingPathComponent:@"embedded.provisionprofile"]; -#endif - - if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox] && !path.length) { - // Distributed via TestFlight - return defaultAppTypeProd; - } - - NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:path options:0 error:&error]; - - if (!profileData.length || error) { - NSString *errorString = - [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error]; - [self logAPNSConfigurationError:errorString]; - return defaultAppTypeProd; - } - - // The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the - // end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit - // ASCII. Replace any 0s or invalid characters in the input. - uint8_t *profileBytes = (uint8_t *)profileData.bytes; - for (int i = 0; i < profileData.length; i++) { - uint8_t currentByte = profileBytes[i]; - if (!currentByte || currentByte > 127) { - profileBytes[i] = '.'; - } - } - - NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes - length:profileData.length - encoding:NSASCIIStringEncoding - freeWhenDone:NO]; - - if (error || !embeddedProfile.length) { - NSString *errorString = - [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error]; - [self logAPNSConfigurationError:errorString]; - return defaultAppTypeProd; - } - - NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile]; - NSString *plistContents; - if ([scanner scanUpToString:@"<plist" intoString:nil]) { - if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) { - plistContents = [plistContents stringByAppendingString:@"</plist>"]; - } - } - - if (!plistContents.length) { - return defaultAppTypeProd; - } - - NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding]; - if (!data.length) { - [self logAPNSConfigurationError:@"Couldn't read plist fetched from embedded mobileprovision"]; - return defaultAppTypeProd; - } - - NSError *plistMapError; - id plistData = [NSPropertyListSerialization propertyListWithData:data - options:NSPropertyListImmutable - format:nil - error:&plistMapError]; - if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) { - NSString *errorString = - [NSString stringWithFormat:@"Error while converting assumed plist to dict %@", - plistMapError.localizedDescription]; - [self logAPNSConfigurationError:errorString]; - return defaultAppTypeProd; - } - NSDictionary *plistMap = (NSDictionary *)plistData; - - if ([plistMap valueForKeyPath:@"ProvisionedDevices"]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID012, - @"Provisioning profile has specifically provisioned devices, " - @"most likely a Dev profile."); - } - - NSString *apsEnvironment = [plistMap valueForKeyPath:kEntitlementsAPSEnvironmentKey]; - NSString *debugString __unused = - [NSString stringWithFormat:@"APNS Environment in profile: %@", apsEnvironment]; - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID013, @"%@", debugString); - - // No aps-environment in the profile. - if (!apsEnvironment.length) { - [self logAPNSConfigurationError:@"No aps-environment set. If testing on a device APNS is not " - @"correctly configured. Please recheck your provisioning " - @"profiles. If testing on a simulator this is fine since APNS " - @"doesn't work on the simulator."]; - return defaultAppTypeProd; - } - - if ([apsEnvironment isEqualToString:kAPSEnvironmentDevelopmentValue]) { - return NO; - } - - return defaultAppTypeProd; -} - -/// Log error messages only when Messaging exists in the pod. -- (void)logAPNSConfigurationError:(NSString *)errorString { - BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil; - if (hasFirebaseMessaging) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID014, @"%@", errorString); - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID015, @"%@", errorString); - } -} - -#pragma mark - Sync InstanceID - -- (void)updateFirebaseInstallationID { - FIRInstanceID_WEAKIFY(self); - [self.installations - installationIDWithCompletion:^(NSString *_Nullable installationID, NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - self.firebaseInstallationsID = installationID; - }]; -} - -- (void)installationIDDidChangeNotificationReceived:(NSNotification *)notification { - NSString *installationAppID = - notification.userInfo[kFIRInstallationIDDidChangeNotificationAppNameKey]; - if ([installationAppID isKindOfClass:[NSString class]] && - [installationAppID isEqual:self.firebaseAppID]) { - [self updateFirebaseInstallationID]; - } -} - -- (void)observeFirebaseInstallationIDChanges { - [[NSNotificationCenter defaultCenter] removeObserver:self - name:FIRInstallationIDDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(installationIDDidChangeNotificationReceived:) - name:FIRInstallationIDDidChangeNotification - object:nil]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.h deleted file mode 100644 index 92b2469b1..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -/** - * Represents an APNS device token and whether its environment is for sandbox. - * It can read from and write to an NSDictionary for simple serialization. - */ -@interface FIRInstanceIDAPNSInfo : NSObject <NSCoding> - -/// The APNs device token, provided by the OS to the application delegate -@property(nonatomic, readonly, strong) NSData *deviceToken; -/// Represents whether or not this is deviceToken is for the sandbox -/// environment, or production. -@property(nonatomic, readonly, getter=isSandbox) BOOL sandbox; - -/** - * Initializes the receiver with an APNs device token, and boolean - * representing whether that token is for the sandbox environment. - * - * @param deviceToken The APNs device token typically provided by the - * operating system. - * @param isSandbox YES if the APNs device token is for the sandbox - * environment, or NO if it is for production. - * @return An instance of FIRInstanceIDAPNSInfo. - */ -- (instancetype)initWithDeviceToken:(NSData *)deviceToken isSandbox:(BOOL)isSandbox; - -/** - * Initializes the receiver from a token options dictionary containing data - * within the `kFIRInstanceIDTokenOptionsAPNSKey` and - * `kFIRInstanceIDTokenOptionsAPNSIsSandboxKey` keys. The token should be an - * NSData blob, and the sandbox value should be an NSNumber - * representing a boolean value. - * - * @param dictionary A dictionary containing values under the keys - * `kFIRInstanceIDTokenOptionsAPNSKey` and - * `kFIRInstanceIDTokenOptionsAPNSIsSandboxKey`. - * @return An instance of FIRInstanceIDAPNSInfo, or nil if the - * dictionary data was invalid or missing. - */ -- (nullable instancetype)initWithTokenOptionsDictionary:(NSDictionary *)dictionary; - -- (BOOL)isEqualToAPNSInfo:(FIRInstanceIDAPNSInfo *)otherInfo; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.m deleted file mode 100644 index d1f9d0800..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.m +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDAPNSInfo.h" - -#import "FIRInstanceIDConstants.h" - -/// The key used to find the APNs device token in an archive. -NSString *const kFIRInstanceIDAPNSInfoTokenKey = @"device_token"; -/// The key used to find the sandbox value in an archive. -NSString *const kFIRInstanceIDAPNSInfoSandboxKey = @"sandbox"; - -@implementation FIRInstanceIDAPNSInfo - -- (instancetype)initWithDeviceToken:(NSData *)deviceToken isSandbox:(BOOL)isSandbox { - self = [super init]; - if (self) { - _deviceToken = [deviceToken copy]; - _sandbox = isSandbox; - } - return self; -} - -- (instancetype)initWithTokenOptionsDictionary:(NSDictionary *)dictionary { - id deviceToken = dictionary[kFIRInstanceIDTokenOptionsAPNSKey]; - if (![deviceToken isKindOfClass:[NSData class]]) { - return nil; - } - - id isSandbox = dictionary[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey]; - if (![isSandbox isKindOfClass:[NSNumber class]]) { - return nil; - } - self = [super init]; - if (self) { - _deviceToken = (NSData *)deviceToken; - _sandbox = ((NSNumber *)isSandbox).boolValue; - } - return self; -} - -#pragma mark - NSCoding - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - id deviceToken = [aDecoder decodeObjectForKey:kFIRInstanceIDAPNSInfoTokenKey]; - if (![deviceToken isKindOfClass:[NSData class]]) { - return nil; - } - BOOL isSandbox = [aDecoder decodeBoolForKey:kFIRInstanceIDAPNSInfoSandboxKey]; - return [self initWithDeviceToken:(NSData *)deviceToken isSandbox:isSandbox]; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:self.deviceToken forKey:kFIRInstanceIDAPNSInfoTokenKey]; - [aCoder encodeBool:self.sandbox forKey:kFIRInstanceIDAPNSInfoSandboxKey]; -} - -- (BOOL)isEqualToAPNSInfo:(FIRInstanceIDAPNSInfo *)otherInfo { - if ([super isEqual:otherInfo]) { - return YES; - } - return ([self.deviceToken isEqualToData:otherInfo.deviceToken] && - self.isSandbox == otherInfo.isSandbox); -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h deleted file mode 100644 index 8d453b8b4..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -extern NSString *__nonnull const kFIRInstanceIDKeychainWildcardIdentifier; - -NS_ASSUME_NONNULL_BEGIN - -/** - * Wrapper around storing FCM auth data in iOS keychain. - */ -@interface FIRInstanceIDAuthKeychain : NSObject - -/** - * Designated Initializer. Init a generic `SecClassGenericPassword` keychain with `identifier` - * as the `kSecAttrGeneric`. - * - * @param identifier The generic attribute to be used by the keychain. - * - * @return A Keychain object with `kSecAttrGeneric` attribute set to identifier. - */ -- (instancetype)initWithIdentifier:(NSString *)identifier; - -/** - * Get keychain items matching the given service and account. The service and/or account - * can be a wildcard (`kFIRInstanceIDKeychainWildcardIdentifier`), which case the query - * will include all items matching any services and/or accounts. - * - * @param service The kSecAttrService used to save the password. Can be wildcard. - * @param account The kSecAttrAccount used to save the password. Can be wildcard. - * - * @return An array of |NSData|s matching the provided inputs. - */ -- (NSArray<NSData *> *)itemsMatchingService:(NSString *)service account:(NSString *)account; - -/** - * Get keychain item for a given service and account. - * - * @param service The kSecAttrService used to save the password. - * @param account The kSecAttrAccount used to save the password. - * - * @return A cached keychain item for a given account and service, or nil if it was not - * found or could not be retrieved. - */ -- (NSData *)dataForService:(NSString *)service account:(NSString *)account; - -/** - * Remove the cached items from the keychain matching the service, account and access group. - * In case the items do not exist, YES is returned but with a valid error object with code - * `errSecItemNotFound`. - * - * @param service The kSecAttrService used to save the password. - * @param account The kSecAttrAccount used to save the password. - * @param handler The callback handler which is invoked when the remove operation is complete, with - * an error if there is any. - */ -- (void)removeItemsMatchingService:(NSString *)service - account:(NSString *)account - handler:(nullable void (^)(NSError *error))handler; - -/** - * Set the data for a given service and account. - * We use `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` which - * prevents backup and restore to iCloud, and works for app extension that can - * execute right after a device is restarted (and not unlocked). - * - * @param data The data to save. - * @param service The `kSecAttrService` used to save the password. - * @param account The `kSecAttrAccount` used to save the password. - * @param handler The callback handler which is invoked when the add operation is complete, - * with an error if there is any. - * - */ -- (void)setData:(NSData *)data - forService:(NSString *)service - account:(NSString *)account - handler:(nullable void (^)(NSError *))handler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m deleted file mode 100644 index dfce2f75b..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDAuthKeyChain.h" -#import "FIRInstanceIDKeychain.h" -#import "FIRInstanceIDLogger.h" - -/** - * The error type representing why we couldn't read data from the keychain. - */ -typedef NS_ENUM(int, FIRInstanceIDKeychainErrorType) { - kFIRInstanceIDKeychainErrorBadArguments = -1301, -}; - -NSString *const kFIRInstanceIDKeychainWildcardIdentifier = @"*"; - -@interface FIRInstanceIDAuthKeychain () - -@property(nonatomic, copy) NSString *generic; -// cachedKeychainData is keyed by service and account, the value is an array of NSData. -// It is used to cache the tokens per service, per account, as well as checkin data per service, -// per account inside the keychain. -@property(nonatomic) - NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, NSArray<NSData *> *> *> - *cachedKeychainData; - -@end - -@implementation FIRInstanceIDAuthKeychain - -- (instancetype)initWithIdentifier:(NSString *)identifier { - self = [super init]; - if (self) { - _generic = [identifier copy]; - _cachedKeychainData = [[NSMutableDictionary alloc] init]; - } - return self; -} - -+ (NSMutableDictionary *)keychainQueryForService:(NSString *)service - account:(NSString *)account - generic:(NSString *)generic { - NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword}; - - NSMutableDictionary *finalQuery = [NSMutableDictionary dictionaryWithDictionary:query]; - if ([generic length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:generic]) { - finalQuery[(__bridge NSString *)kSecAttrGeneric] = generic; - } - if ([account length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:account]) { - finalQuery[(__bridge NSString *)kSecAttrAccount] = account; - } - if ([service length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:service]) { - finalQuery[(__bridge NSString *)kSecAttrService] = service; - } - return finalQuery; -} - -- (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NSString *)account { - return [[self class] keychainQueryForService:service account:account generic:self.generic]; -} - -- (NSArray<NSData *> *)itemsMatchingService:(NSString *)service account:(NSString *)account { - // If query wildcard service, it asks for all the results, which always query from keychain. - if (![service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] && - ![account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] && - _cachedKeychainData[service][account]) { - // As long as service, account array exist, even it's empty, it means we've queried it before, - // returns the cache value. - return _cachedKeychainData[service][account]; - } - - NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account]; - NSMutableArray<NSData *> *results; - keychainQuery[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue; -#if TARGET_OS_IOS || TARGET_OS_TV - keychainQuery[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; - keychainQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; - // FIRInstanceIDKeychain should only take a query and return a result, will handle the query here. - NSArray *passwordInfos = - CFBridgingRelease([[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]); -#elif TARGET_OS_OSX || TARGET_OS_WATCH - keychainQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; - NSData *passwordInfos = - CFBridgingRelease([[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]); -#endif - - if (!passwordInfos) { - // Nothing was found, simply return from this sync block. - // Make sure to label the cache entry empty, signaling that we've queried this entry. - if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] || - [account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) { - // Do not update cache if it's wildcard query. - return @[]; - } else if (_cachedKeychainData[service]) { - [_cachedKeychainData[service] setObject:@[] forKey:account]; - } else { - [_cachedKeychainData setObject:[@{account : @[]} mutableCopy] forKey:service]; - } - return @[]; - } - results = [[NSMutableArray alloc] init]; -#if TARGET_OS_IOS || TARGET_OS_TV - NSInteger numPasswords = passwordInfos.count; - for (NSUInteger i = 0; i < numPasswords; i++) { - NSDictionary *passwordInfo = [passwordInfos objectAtIndex:i]; - if (passwordInfo[(__bridge id)kSecValueData]) { - [results addObject:passwordInfo[(__bridge id)kSecValueData]]; - } - } -#elif TARGET_OS_OSX || TARGET_OS_WATCH - [results addObject:passwordInfos]; -#endif - // We query the keychain because it didn't exist in cache, now query is done, update the result in - // the cache. - if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] || - [account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) { - // Do not update cache if it's wildcard query. - return [results copy]; - } else if (_cachedKeychainData[service]) { - [_cachedKeychainData[service] setObject:[results copy] forKey:account]; - } else { - NSMutableDictionary *entry = [@{account : [results copy]} mutableCopy]; - [_cachedKeychainData setObject:entry forKey:service]; - } - return [results copy]; -} - -- (NSData *)dataForService:(NSString *)service account:(NSString *)account { - NSArray<NSData *> *items = [self itemsMatchingService:service account:account]; - // If items is nil or empty, nil will be returned. - return items.firstObject; -} - -- (void)removeItemsMatchingService:(NSString *)service - account:(NSString *)account - handler:(void (^)(NSError *error))handler { - if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) { - // Delete all keychain items. - _cachedKeychainData = [[NSMutableDictionary alloc] init]; - } else if ([account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) { - // Delete all entries under service, - if (_cachedKeychainData[service]) { - _cachedKeychainData[service] = [[NSMutableDictionary alloc] init]; - } - } else if (_cachedKeychainData[service]) { - // We should keep the service/account entry instead of nil so we know - // it's "empty entry" instead of "not query from keychain yet". - [_cachedKeychainData[service] setObject:@[] forKey:account]; - } else { - [_cachedKeychainData setObject:[@{account : @[]} mutableCopy] forKey:service]; - } - NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account]; - [[FIRInstanceIDKeychain sharedInstance] removeItemWithQuery:keychainQuery handler:handler]; -} - -- (void)setData:(NSData *)data - forService:(NSString *)service - account:(NSString *)account - handler:(void (^)(NSError *))handler { - if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] || - [account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) { - if (handler) { - handler([NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain - code:kFIRInstanceIDKeychainErrorBadArguments - userInfo:nil]); - } - return; - } - [self removeItemsMatchingService:service - account:account - handler:^(NSError *error) { - if (error) { - if (handler) { - handler(error); - } - return; - } - if (data.length > 0) { - NSMutableDictionary *keychainQuery = - [self keychainQueryForService:service account:account]; - keychainQuery[(__bridge id)kSecValueData] = data; - - keychainQuery[(__bridge id)kSecAttrAccessible] = - (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; - [[FIRInstanceIDKeychain sharedInstance] - addItemWithQuery:keychainQuery - handler:handler]; - } - }]; - // Set the cache value. This must happen after removeItemsMatchingService:account:handler was - // called, so the cache value was reset before setting a new value. - if (_cachedKeychainData[service]) { - if (_cachedKeychainData[service][account]) { - _cachedKeychainData[service][account] = @[ data ]; - } else { - [_cachedKeychainData[service] setObject:@[ data ] forKey:account]; - } - } else { - [_cachedKeychainData setObject:[@{account : @[ data ]} mutableCopy] forKey:service]; - } -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.h deleted file mode 100644 index 1fb715e7c..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> -#import "FIRInstanceIDCheckinService.h" - -@class FIRInstanceIDCheckinPreferences; -@class FIRInstanceIDStore; - -/** - * FIRInstanceIDAuthService is responsible for retrieving, caching, and supplying checkin info - * for the rest of Instance ID. A checkin can be scheduled, meaning that it will keep retrying the - * checkin request until it is successful. A checkin can also be requested directly, with a - * completion handler. - */ -@interface FIRInstanceIDAuthService : NSObject - -/** - * Used only for testing. In addition to taking a store (for locally caching the checkin info), it - * also takes a checkinService. - */ -- (instancetype)initWithCheckinService:(FIRInstanceIDCheckinService *)checkinService - store:(FIRInstanceIDStore *)store; - -/** - * Initializes the auth service given a store (which provides the local caching of checkin info). - * This initializer will create its own instance of FIRInstanceIDCheckinService. - */ -- (instancetype)initWithStore:(FIRInstanceIDStore *)store; - -#pragma mark - Checkin Service - -/** - * Checks if the current deviceID and secret are valid or not. - * - * @return YES if the checkin credentials are valid else NO. - */ -- (BOOL)hasValidCheckinInfo; - -/** - * Fetch checkin info from the server. This would usually refresh the existing - * checkin credentials for the current app. - * - * @param handler The completion handler to invoke once the checkin info has been - * refreshed. - */ -- (void)fetchCheckinInfoWithHandler:(FIRInstanceIDDeviceCheckinCompletion)handler; - -/** - * Schedule checkin. Will hit the network only if the currently loaded checkin - * preferences are stale. - * - * @param immediately YES if we want it to be scheduled immediately else NO. - */ -- (void)scheduleCheckin:(BOOL)immediately; - -/** - * Returns the checkin preferences currently loaded in memory. The Checkin preferences - * can be either valid or invalid. - * - * @return The checkin preferences loaded in memory. - */ -- (FIRInstanceIDCheckinPreferences *)checkinPreferences; - -/** - * Cancels any ongoing checkin fetch, if any. - */ -- (void)stopCheckinRequest; - -/** - * Resets the checkin information. - * - * @param handler The callback handler which is invoked when checkin reset is complete, - * with an error if there is any. - */ -- (void)resetCheckinWithHandler:(void (^)(NSError *error))handler; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.m deleted file mode 100644 index 8c33c4408..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.m +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDAuthService.h" - -#import "FIRInstanceIDCheckinPreferences+Internal.h" -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDCheckinPreferences_Private.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDStore.h" -#import "NSError+FIRInstanceID.h" - -// Max time interval between checkin retry in seconds. -static const int64_t kMaxCheckinRetryIntervalInSeconds = 1 << 5; - -@interface FIRInstanceIDAuthService () - -// Used to retrieve and cache the checkin info to disk and Keychain. -@property(nonatomic, readwrite, strong) FIRInstanceIDStore *store; -// Used to perform single checkin fetches. -@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinService *checkinService; -// The current checkin info. It will be compared to what is retrieved to determine whether it is -// different than what is in the cache. -@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *checkinPreferences; - -// This array will track multiple handlers waiting for checkin to be performed. When a checkin -// request completes, all the handlers will be notified. -// Changes to the checkinHandlers array should happen in a thread-safe manner. -@property(nonatomic, readonly, strong) - NSMutableArray<FIRInstanceIDDeviceCheckinCompletion> *checkinHandlers; - -// This is set to true if there is a checkin request in-flight. -@property(atomic, readwrite, assign) BOOL isCheckinInProgress; -// This timer is used a perform checkin retries. It is cancellable. -@property(atomic, readwrite, strong) NSTimer *scheduledCheckinTimer; -// The number of times checkin has been retried during a scheduled checkin. -@property(atomic, readwrite, assign) int checkinRetryCount; - -@end - -@implementation FIRInstanceIDAuthService - -- (instancetype)initWithCheckinService:(FIRInstanceIDCheckinService *)checkinService - store:(FIRInstanceIDStore *)store { - self = [super init]; - if (self) { - _store = store; - _checkinPreferences = [_store cachedCheckinPreferences]; - _checkinService = checkinService; - _checkinHandlers = [NSMutableArray array]; - } - return self; -} - -- (void)dealloc { - [_scheduledCheckinTimer invalidate]; -} - -- (instancetype)initWithStore:(FIRInstanceIDStore *)store { - FIRInstanceIDCheckinService *checkinService = [[FIRInstanceIDCheckinService alloc] init]; - return [self initWithCheckinService:checkinService store:store]; -} - -#pragma mark - Schedule Checkin - -- (void)scheduleCheckin:(BOOL)immediately { - // Checkin is still valid, so a remote checkin is not required. - if ([self.checkinPreferences hasValidCheckinInfo]) { - return; - } - - // Checkin is already scheduled, so this (non-immediate) request can be ignored. - if (!immediately && [self.scheduledCheckinTimer isValid]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService000, - @"Checkin sync already scheduled. Will not schedule."); - return; - } - - if (immediately) { - [self performScheduledCheckin]; - } else { - int64_t checkinRetryDuration = [self calculateNextCheckinRetryIntervalInSeconds]; - [self startCheckinTimerWithDuration:(NSTimeInterval)checkinRetryDuration]; - } -} - -- (void)startCheckinTimerWithDuration:(NSTimeInterval)timerDuration { - self.scheduledCheckinTimer = - [NSTimer scheduledTimerWithTimeInterval:timerDuration - target:self - selector:@selector(onScheduledCheckinTimerFired:) - userInfo:nil - repeats:NO]; - // Add some tolerance to the timer, to allow iOS to be more flexible with this timer - self.scheduledCheckinTimer.tolerance = 0.5; -} - -- (void)clearScheduledCheckinTimer { - [self.scheduledCheckinTimer invalidate]; - self.scheduledCheckinTimer = nil; -} - -- (void)onScheduledCheckinTimerFired:(NSTimer *)timer { - [self performScheduledCheckin]; -} - -- (void)performScheduledCheckin { - // No checkin scheduled as of now. - [self clearScheduledCheckinTimer]; - - // Checkin is still valid, so a remote checkin is not required. - if ([self.checkinPreferences hasValidCheckinInfo]) { - return; - } - - FIRInstanceID_WEAKIFY(self); - [self - fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) { - FIRInstanceID_STRONGIFY(self); - self.checkinRetryCount++; - - if (error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService001, @"Checkin error %@.", - error); - - dispatch_async(dispatch_get_main_queue(), ^{ - // Schedule another checkin - [self scheduleCheckin:NO]; - }); - - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService002, @"Checkin success."); - } - }]; -} - -- (int64_t)calculateNextCheckinRetryIntervalInSeconds { - // persistent failures can lead to overflow prevent that. - if (self.checkinRetryCount >= 10) { - return kMaxCheckinRetryIntervalInSeconds; - } - return MIN(1 << self.checkinRetryCount, kMaxCheckinRetryIntervalInSeconds); -} - -#pragma mark - Checkin Service - -- (BOOL)hasValidCheckinInfo { - return [self.checkinPreferences hasValidCheckinInfo]; -} - -- (void)fetchCheckinInfoWithHandler:(nonnull FIRInstanceIDDeviceCheckinCompletion)handler { - // Perform any changes to self.checkinHandlers and _isCheckinInProgress in a thread-safe way. - @synchronized(self) { - [self.checkinHandlers addObject:handler]; - - if (_isCheckinInProgress) { - // Nothing more to do until our checkin request is done - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthServiceCheckinInProgress, - @"Checkin is in progress\n"); - return; - } - } - - // Checkin is still valid, so a remote checkin is not required. - if ([self.checkinPreferences hasValidCheckinInfo]) { - [self notifyCheckinHandlersWithCheckin:self.checkinPreferences error:nil]; - return; - } - - @synchronized(self) { - _isCheckinInProgress = YES; - } - [self.checkinService - checkinWithExistingCheckin:self.checkinPreferences - completion:^(FIRInstanceIDCheckinPreferences *checkinPreferences, - NSError *error) { - @synchronized(self) { - self->_isCheckinInProgress = NO; - } - if (error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService003, - @"Failed to checkin device %@", error); - [self notifyCheckinHandlersWithCheckin:nil error:error]; - return; - } - - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService004, - @"Successfully got checkin credentials"); - BOOL hasSameCachedPreferences = - [self cachedCheckinMatchesCheckin:checkinPreferences]; - checkinPreferences.hasPreCachedAuthCredentials = hasSameCachedPreferences; - - // Update to the most recent checkin preferences - self.checkinPreferences = checkinPreferences; - - // Save the checkin info to disk - // Keychain might not be accessible, so confirm that checkin preferences can - // be saved - [self.store - saveCheckinPreferences:checkinPreferences - handler:^(NSError *checkinSaveError) { - if (checkinSaveError && !hasSameCachedPreferences) { - // The checkin info was new, but it couldn't be - // written to the Keychain. Delete any stuff that was - // cached in memory. This doesn't delete any - // previously persisted preferences. - FIRInstanceIDLoggerError( - kFIRInstanceIDMessageCodeService004, - @"Unable to save checkin info, resetting " - @"checkin preferences " - "in memory."); - [checkinPreferences reset]; - [self - notifyCheckinHandlersWithCheckin:nil - error: - checkinSaveError]; - } else { - // The checkin is either new, or it was the same (and - // it couldn't be saved). Either way, report that the - // checkin preferences were received successfully. - [self notifyCheckinHandlersWithCheckin: - checkinPreferences - error:nil]; - if (!hasSameCachedPreferences) { - // Checkin is new. - // Notify any listeners that might be waiting for - // checkin to be fetched, such as Firebase - // Messaging (for its MCS connection). - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] - postNotificationName: - kFIRInstanceIDCheckinFetchedNotification - object:nil]; - }); - } - } - }]; - }]; -} - -- (FIRInstanceIDCheckinPreferences *)checkinPreferences { - return _checkinPreferences; -} - -- (void)stopCheckinRequest { - [self.checkinService stopFetching]; -} - -- (void)resetCheckinWithHandler:(void (^)(NSError *error))handler { - [self.store removeCheckinPreferencesWithHandler:^(NSError *error) { - if (!error) { - self.checkinPreferences = nil; - } - if (handler) { - handler(error); - } - }]; -} - -#pragma mark - Private - -/** - * Goes through the current list of checkin handlers and fires them with the same checkin and/or - * error info. The checkin handlers will get cleared after. - */ -- (void)notifyCheckinHandlersWithCheckin:(nullable FIRInstanceIDCheckinPreferences *)checkin - error:(nullable NSError *)error { - @synchronized(self) { - for (FIRInstanceIDDeviceCheckinCompletion handler in self.checkinHandlers) { - handler(checkin, error); - } - [self.checkinHandlers removeAllObjects]; - } -} - -/** - * Given a |checkin|, it will compare it to the current checkinPreferences to see if the - * deviceID and secretToken are the same. - */ -- (BOOL)cachedCheckinMatchesCheckin:(FIRInstanceIDCheckinPreferences *)checkin { - if (self.checkinPreferences && checkin) { - return ([self.checkinPreferences.deviceID isEqualToString:checkin.deviceID] && - [self.checkinPreferences.secretToken isEqualToString:checkin.secretToken]); - } - return NO; -} -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h deleted file mode 100644 index bccaced89..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -@interface FIRInstanceIDBackupExcludedPlist : NSObject - -/** - * Caches the plist contents in memory so we don't hit the disk each time we want - * to query something in the plist. This is loaded lazily i.e. if you write to the - * plist the contents you want to write will be stored here if the write was - * successful. The other case where it is loaded is if you read the plist contents - * by calling `contentAsDictionary`. - * - * In case you write to the plist and then try to read the file using - * `contentAsDictionary` we would just return the cachedPlistContents since it would - * represent the disk contents. - */ -@property(nonatomic, readonly, strong) NSDictionary *cachedPlistContents; - -/** - * Init a backup excluded plist file. - * - * @param fileName The filename for the plist file. - * @param subDirectory The subdirectory in Application Support to save the plist. - * - * @return Helper which allows to read write data to a backup excluded plist. - */ -- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory; - -/** - * Write dictionary data to the backup excluded plist file. If the file does not exist - * it would be created before writing to it. - * - * @param dict The data to be written to the plist. - * @param error The error object if any while writing the data. - * - * @return YES if the write was successful else NO. - */ -- (BOOL)writeDictionary:(NSDictionary *)dict error:(NSError **)error; - -/** - * Delete the backup excluded plist created with the above filename. - * - * @param error The error object if any while deleting the file. - * - * @return YES If the delete was successful else NO. - */ -- (BOOL)deleteFile:(NSError **)error; - -/** - * The contents of the plist file. We also store the contents of the file in-memory. - * If the in-memory contents are valid we return the in-memory contents else we read - * the file from disk. - * - * @return A dictionary object that contains the contents of the plist file if the file - * exists else nil. - */ -- (NSDictionary *)contentAsDictionary; - -/** - * Check if the plist exists on the disk or not. - * - * @return YES if the file exists on the disk else NO. - */ -- (BOOL)doesFileExist; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m deleted file mode 100644 index c1085caef..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDBackupExcludedPlist.h" - -#import "FIRInstanceIDLogger.h" - -@interface FIRInstanceIDBackupExcludedPlist () - -@property(nonatomic, readwrite, copy) NSString *fileName; -@property(nonatomic, readwrite, copy) NSString *subDirectoryName; -@property(nonatomic, readwrite, strong) NSDictionary *cachedPlistContents; - -@end - -@implementation FIRInstanceIDBackupExcludedPlist - -- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory { - self = [super init]; - if (self) { - _fileName = [fileName copy]; - _subDirectoryName = [subDirectory copy]; - } - return self; -} - -- (BOOL)writeDictionary:(NSDictionary *)dict error:(NSError **)error { - NSString *path = [self plistPathInDirectory]; - if (![dict writeToFile:path atomically:YES]) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist000, - @"Failed to write to %@.plist", self.fileName); - return NO; - } - - // Successfully wrote contents -- change the in-memory contents - self.cachedPlistContents = [dict copy]; - - NSURL *URL = [NSURL fileURLWithPath:path]; - if (error) { - *error = nil; - } - - NSDictionary *preferences = [URL resourceValuesForKeys:@[ NSURLIsExcludedFromBackupKey ] - error:error]; - if ([preferences[NSURLIsExcludedFromBackupKey] boolValue]) { - return YES; - } - - BOOL success = [URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:error]; - if (!success) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist001, - @"Error excluding %@ from backup, %@", [URL lastPathComponent], - error ? *error : @""); - } - return success; -} - -- (BOOL)deleteFile:(NSError **)error { - BOOL success = YES; - NSString *path = [self plistPathInDirectory]; - if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { - success = [[NSFileManager defaultManager] removeItemAtPath:path error:error]; - } - // remove the in-memory contents - self.cachedPlistContents = nil; - return success; -} - -- (NSDictionary *)contentAsDictionary { - if (!self.cachedPlistContents) { - NSString *path = [self plistPathInDirectory]; - if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { - self.cachedPlistContents = [[NSDictionary alloc] initWithContentsOfFile:path]; - } - } - return self.cachedPlistContents; -} - -- (BOOL)doesFileExist { - NSString *path = [self plistPathInDirectory]; - return [[NSFileManager defaultManager] fileExistsAtPath:path]; -} - -#pragma mark - Private - -- (NSString *)plistPathInDirectory { - NSArray *directoryPaths; - NSString *plistNameWithExtension = [NSString stringWithFormat:@"%@.plist", self.fileName]; - directoryPaths = - NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); - NSArray *components = @[ directoryPaths.lastObject, _subDirectoryName, plistNameWithExtension ]; - - return [NSString pathWithComponents:components]; -} - -- (NSSearchPathDirectory)supportedDirectory { -#if TARGET_OS_TV - return NSCachesDirectory; -#else - return NSApplicationSupportDirectory; -#endif -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h deleted file mode 100644 index d2f286d79..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h> - -@interface FIRInstanceIDCheckinPreferences (Internal) - -/** - * Parse the checkin auth credentials saved in the Keychain to initialize checkin - * preferences. - * - * @param keychainContent The checkin auth credentials saved in the Keychain. - * - * @return A valid checkin preferences object if the checkin auth credentials in the - * keychain can be parsed successfully else nil. - */ -+ (FIRInstanceIDCheckinPreferences *)preferencesFromKeychainContents:(NSString *)keychainContent; - -/** - * Default initializer for InstanceID checkin preferences. - * - * @param deviceID The deviceID for the app. - * @param secretToken The secret token the app uses to authenticate with the server. - * - * @return A checkin preferences object with given deviceID and secretToken. - */ -- (instancetype)initWithDeviceID:(NSString *)deviceID secretToken:(NSString *)secretToken; - -/** - * Update checkin preferences from the preferences dict persisted as a plist. The dict contains - * all the checkin preferences retrieved from the server except the deviceID and secret which - * are stored in the Keychain. - * - * @param checkinPlistContent The checkin preferences saved in a plist on the disk. - */ -- (void)updateWithCheckinPlistContents:(NSDictionary *)checkinPlistContent; - -/** - * Reset the current checkin preferences object. - */ -- (void)reset; - -/** - * The string that contains the checkin auth credentials i.e. deviceID and secret. This - * needs to be stored in the Keychain. - * - * @return The checkin auth credential string containing the deviceID and secret. - */ -- (NSString *)checkinKeychainContent; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.m deleted file mode 100644 index 88cc40a1b..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.m +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDCheckinPreferences+Internal.h" - -#import "FIRInstanceIDCheckinService.h" -#import "FIRInstanceIDUtilities.h" - -static NSString *const kCheckinKeychainContentSeparatorString = @"|"; - -@interface FIRInstanceIDCheckinPreferences () - -@property(nonatomic, readwrite, copy) NSString *deviceID; -@property(nonatomic, readwrite, copy) NSString *secretToken; -@property(nonatomic, readwrite, copy) NSString *digest; -@property(nonatomic, readwrite, copy) NSString *versionInfo; -@property(nonatomic, readwrite, copy) NSString *deviceDataVersion; - -@property(nonatomic, readwrite, strong) NSMutableDictionary *gServicesData; -@property(nonatomic, readwrite, assign) int64_t lastCheckinTimestampMillis; - -@end - -@implementation FIRInstanceIDCheckinPreferences (Internal) - -+ (FIRInstanceIDCheckinPreferences *)preferencesFromKeychainContents:(NSString *)keychainContent { - NSString *deviceID = [self checkinDeviceIDFromKeychainContent:keychainContent]; - NSString *secret = [self checkinSecretFromKeychainContent:keychainContent]; - if ([deviceID length] && [secret length]) { - return [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:deviceID secretToken:secret]; - } else { - return nil; - } -} - -- (instancetype)initWithDeviceID:(NSString *)deviceID secretToken:(NSString *)secretToken { - self = [super init]; - if (self) { - self.deviceID = [deviceID copy]; - self.secretToken = [secretToken copy]; - } - return self; -} - -- (void)reset { - self.deviceID = nil; - self.secretToken = nil; - self.digest = nil; - self.versionInfo = nil; - self.gServicesData = nil; - self.deviceDataVersion = nil; - self.lastCheckinTimestampMillis = 0; -} - -- (void)updateWithCheckinPlistContents:(NSDictionary *)checkinPlistContent { - for (NSString *key in checkinPlistContent) { - if ([kFIRInstanceIDDigestStringKey isEqualToString:key]) { - self.digest = [checkinPlistContent[key] copy]; - } else if ([kFIRInstanceIDVersionInfoStringKey isEqualToString:key]) { - self.versionInfo = [checkinPlistContent[key] copy]; - } else if ([kFIRInstanceIDLastCheckinTimeKey isEqualToString:key]) { - self.lastCheckinTimestampMillis = [checkinPlistContent[key] longLongValue]; - } else if ([kFIRInstanceIDGServicesDictionaryKey isEqualToString:key]) { - self.gServicesData = [checkinPlistContent[key] mutableCopy]; - } else if ([kFIRInstanceIDDeviceDataVersionKey isEqualToString:key]) { - self.deviceDataVersion = [checkinPlistContent[key] copy]; - } - // Otherwise we have some keys we don't care about - } -} - -- (NSString *)checkinKeychainContent { - if ([self.deviceID length] && [self.secretToken length]) { - return [NSString stringWithFormat:@"%@%@%@", self.deviceID, - kCheckinKeychainContentSeparatorString, self.secretToken]; - } else { - return nil; - } -} - -+ (NSString *)checkinDeviceIDFromKeychainContent:(NSString *)keychainContent { - return [self checkinKeychainContent:keychainContent forIndex:0]; -} - -+ (NSString *)checkinSecretFromKeychainContent:(NSString *)keychainContent { - return [self checkinKeychainContent:keychainContent forIndex:1]; -} - -+ (NSString *)checkinKeychainContent:(NSString *)keychainContent forIndex:(int)index { - NSArray *keychainComponents = - [keychainContent componentsSeparatedByString:kCheckinKeychainContentSeparatorString]; - if (index >= 0 && index < 2 && [keychainComponents count] == 2) { - return keychainComponents[index]; - } else { - return nil; - } -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences.m deleted file mode 100644 index 2f3c55ee7..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences.m +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDCheckinPreferences.h" - -#import <GoogleUtilities/GULUserDefaults.h> -#import "FIRInstanceIDCheckinService.h" -#import "FIRInstanceIDUtilities.h" - -const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval = 7 * 24 * 60 * 60; // 7 days. - -@interface FIRInstanceIDCheckinPreferences () - -@property(nonatomic, readwrite, copy) NSString *deviceID; -@property(nonatomic, readwrite, copy) NSString *secretToken; -@property(nonatomic, readwrite, copy) NSString *digest; -@property(nonatomic, readwrite, copy) NSString *versionInfo; -@property(nonatomic, readwrite, copy) NSString *deviceDataVersion; - -@property(nonatomic, readwrite, strong) NSMutableDictionary *gServicesData; -@property(nonatomic, readwrite, assign) int64_t lastCheckinTimestampMillis; - -// This flag indicates that we have already saved the above deviceID and secret -// to our keychain and hence we don't need to save again. This is helpful since -// on checkin refresh we can avoid writing to the Keychain which can sometimes -// be very buggy. For info check this https://forums.developer.apple.com/thread/4743 -@property(nonatomic, readwrite, assign) BOOL hasPreCachedAuthCredentials; - -@end - -@implementation FIRInstanceIDCheckinPreferences - -- (NSDictionary *)checkinPlistContents { - NSMutableDictionary *checkinPlistContents = [NSMutableDictionary dictionary]; - checkinPlistContents[kFIRInstanceIDDigestStringKey] = self.digest ?: @""; - checkinPlistContents[kFIRInstanceIDVersionInfoStringKey] = self.versionInfo ?: @""; - checkinPlistContents[kFIRInstanceIDDeviceDataVersionKey] = self.deviceDataVersion ?: @""; - checkinPlistContents[kFIRInstanceIDLastCheckinTimeKey] = @(self.lastCheckinTimestampMillis); - checkinPlistContents[kFIRInstanceIDGServicesDictionaryKey] = - [self.gServicesData count] ? self.gServicesData : @{}; - return checkinPlistContents; -} - -- (BOOL)hasCheckinInfo { - return (self.deviceID.length && self.secretToken.length); -} - -- (BOOL)hasValidCheckinInfo { - int64_t currentTimestampInMillis = FIRInstanceIDCurrentTimestampInMilliseconds(); - int64_t timeSinceLastCheckinInMillis = currentTimestampInMillis - self.lastCheckinTimestampMillis; - - BOOL hasCheckinInfo = [self hasCheckinInfo]; - NSString *lastLocale = - [[GULUserDefaults standardUserDefaults] stringForKey:kFIRInstanceIDUserDefaultsKeyLocale]; - // If it's app's first time open and checkin is already fetched and no locale information is - // stored, then checkin info is valid. We should not checkin again because locale is considered - // "changed". - if (hasCheckinInfo && !lastLocale) { - NSString *currentLocale = FIRInstanceIDCurrentLocale(); - [[GULUserDefaults standardUserDefaults] setObject:currentLocale - forKey:kFIRInstanceIDUserDefaultsKeyLocale]; - return YES; - } - - // If locale has changed, checkin info is no longer valid. - // Also update locale information if changed. (Only do it here not in token refresh) - if (FIRInstanceIDHasLocaleChanged()) { - NSString *currentLocale = FIRInstanceIDCurrentLocale(); - [[GULUserDefaults standardUserDefaults] setObject:currentLocale - forKey:kFIRInstanceIDUserDefaultsKeyLocale]; - return NO; - } - - return (hasCheckinInfo && - (timeSinceLastCheckinInMillis / 1000.0 < kFIRInstanceIDDefaultCheckinInterval)); -} - -- (void)setHasPreCachedAuthCredentials:(BOOL)hasPreCachedAuthCredentials { - _hasPreCachedAuthCredentials = hasPreCachedAuthCredentials; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.h deleted file mode 100644 index e14b51cb5..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -#import <FirebaseInstanceID/FIRInstanceID+Private.h> -#import "FIRInstanceIDUtilities.h" - -NS_ASSUME_NONNULL_BEGIN - -// keys in Checkin preferences -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceAuthIdKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDSecretTokenKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDigestStringKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDLastCheckinTimeKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDVersionInfoStringKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDGServicesDictionaryKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceDataVersionKey; - -@class FIRInstanceIDCheckinPreferences; - -/** - * Register the device with Checkin Service and get back the `authID`, `secret - * token` etc. for the client. Checkin results are cached in the - * `FIRInstanceIDCache` and periodically refreshed to prevent them from being stale. - * Each client needs to register with checkin before registering with InstanceID. - */ -@interface FIRInstanceIDCheckinService : NSObject - -/** - * Execute a device checkin request to obtain an deviceID, secret token, - * gService data. - * - * @param existingCheckin An existing checkin preference object, if available. - * @param completion Completion hander called on success or failure of device checkin. - */ -- (void)checkinWithExistingCheckin:(nullable FIRInstanceIDCheckinPreferences *)existingCheckin - completion:(FIRInstanceIDDeviceCheckinCompletion)completion; - -/** - * This would stop any request that the service made to the checkin backend and also - * release any callback handlers that it holds. - */ -- (void)stopFetching; - -/** - * Set test block for mock testing network requests. - * - * @param block The block to invoke as a mock response from the network. - */ -+ (void)setCheckinTestBlock:(nullable FIRInstanceIDURLRequestTestBlock)block; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.m deleted file mode 100644 index 8b3357967..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.m +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDCheckinService.h" - -#import "FIRInstanceIDCheckinPreferences+Internal.h" -#import "FIRInstanceIDCheckinPreferences_Private.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDStore.h" -#import "FIRInstanceIDUtilities.h" -#import "NSError+FIRInstanceID.h" - -static NSString *const kDeviceCheckinURL = @"https://device-provisioning.googleapis.com/checkin"; - -// keys in Checkin preferences -NSString *const kFIRInstanceIDDeviceAuthIdKey = @"GMSInstanceIDDeviceAuthIdKey"; -NSString *const kFIRInstanceIDSecretTokenKey = @"GMSInstanceIDSecretTokenKey"; -NSString *const kFIRInstanceIDDigestStringKey = @"GMSInstanceIDDigestKey"; -NSString *const kFIRInstanceIDLastCheckinTimeKey = @"GMSInstanceIDLastCheckinTimestampKey"; -NSString *const kFIRInstanceIDVersionInfoStringKey = @"GMSInstanceIDVersionInfo"; -NSString *const kFIRInstanceIDGServicesDictionaryKey = @"GMSInstanceIDGServicesData"; -NSString *const kFIRInstanceIDDeviceDataVersionKey = @"GMSInstanceIDDeviceDataVersion"; - -static NSUInteger const kCheckinType = 2; // DeviceType IOS in l/w/a/_checkin.proto -static NSUInteger const kCheckinVersion = 2; -static NSUInteger const kFragment = 0; - -static FIRInstanceIDURLRequestTestBlock testBlock; - -@interface FIRInstanceIDCheckinService () - -@property(nonatomic, readwrite, strong) NSURLSession *session; - -@end - -@implementation FIRInstanceIDCheckinService -; - -- (instancetype)init { - self = [super init]; - if (self) { - // Create an URLSession once, even though checkin should happen about once a day - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - config.timeoutIntervalForResource = 60.0f; // 1 minute - config.allowsCellularAccess = YES; - _session = [NSURLSession sessionWithConfiguration:config]; - _session.sessionDescription = @"com.google.iid-checkin"; - } - return self; -} - -- (void)dealloc { - testBlock = nil; - [self.session invalidateAndCancel]; -} - -- (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCheckin - completion:(FIRInstanceIDDeviceCheckinCompletion)completion { - if (self.session == nil) { - FIRInstanceIDLoggerError(kFIRInstanceIDInvalidNetworkSession, - @"Inconsistent state: NSURLSession has been invalidated"); - NSError *error = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]; - if (completion) { - completion(nil, error); - } - return; - } - - NSURL *url = [NSURL URLWithString:kDeviceCheckinURL]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request setValue:@"application/json" forHTTPHeaderField:@"content-type"]; - NSDictionary *checkinParameters = [self checkinParametersWithExistingCheckin:existingCheckin]; - NSData *checkinData = [NSJSONSerialization dataWithJSONObject:checkinParameters - options:0 - error:nil]; - request.HTTPMethod = @"POST"; - request.HTTPBody = checkinData; - - void (^handler)(NSData *, NSURLResponse *, NSError *) = - ^(NSData *data, NSURLResponse *response, NSError *error) { - if (error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService000, - @"Device checkin HTTP fetch error. Error Code: %ld", - (long)error.code); - if (completion) { - completion(nil, error); - } - return; - } - - NSError *serializationError; - NSDictionary *dataResponse = [NSJSONSerialization JSONObjectWithData:data - options:0 - error:&serializationError]; - if (serializationError) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService001, - @"Error serializing json object. Error Code: %ld", - _FIRInstanceID_L(serializationError.code)); - if (completion) { - completion(nil, serializationError); - } - return; - } - - NSString *deviceAuthID = [dataResponse[@"android_id"] stringValue]; - NSString *secretToken = [dataResponse[@"security_token"] stringValue]; - if ([deviceAuthID length] == 0) { - NSError *error = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidRequest]; - if (completion) { - completion(nil, error); - } - return; - } - - int64_t lastCheckinTimestampMillis = [dataResponse[@"time_msec"] longLongValue]; - int64_t currentTimestampMillis = FIRInstanceIDCurrentTimestampInMilliseconds(); - // Somehow the server clock gets out of sync with the device clock. - // Reset the last checkin timestamp in case this happens. - if (lastCheckinTimestampMillis > currentTimestampMillis) { - FIRInstanceIDLoggerDebug( - kFIRInstanceIDMessageCodeService002, @"Invalid last checkin timestamp %@ in future.", - [NSDate dateWithTimeIntervalSince1970:lastCheckinTimestampMillis / 1000.0]); - lastCheckinTimestampMillis = currentTimestampMillis; - } - - NSString *deviceDataVersionInfo = dataResponse[@"device_data_version_info"] ?: @""; - NSString *digest = dataResponse[@"digest"] ?: @""; - - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService003, - @"Checkin successful with authId: %@, " - @"digest: %@, " - @"lastCheckinTimestamp: %lld", - deviceAuthID, digest, lastCheckinTimestampMillis); - - NSString *versionInfo = dataResponse[@"version_info"] ?: @""; - NSMutableDictionary *gservicesData = [NSMutableDictionary dictionary]; - - // Read gServices data. - NSArray *flatSettings = dataResponse[@"setting"]; - for (NSDictionary *dict in flatSettings) { - if (dict[@"name"] && dict[@"value"]) { - gservicesData[dict[@"name"]] = dict[@"value"]; - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDInvalidSettingResponse, - @"Invalid setting in checkin response: (%@: %@)", - dict[@"name"], dict[@"value"]); - } - } - - FIRInstanceIDCheckinPreferences *checkinPreferences = - [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:deviceAuthID - secretToken:secretToken]; - NSDictionary *preferences = @{ - kFIRInstanceIDDigestStringKey : digest, - kFIRInstanceIDVersionInfoStringKey : versionInfo, - kFIRInstanceIDLastCheckinTimeKey : @(lastCheckinTimestampMillis), - kFIRInstanceIDGServicesDictionaryKey : gservicesData, - kFIRInstanceIDDeviceDataVersionKey : deviceDataVersionInfo, - }; - [checkinPreferences updateWithCheckinPlistContents:preferences]; - if (completion) { - completion(checkinPreferences, nil); - } - }; - // Test block - if (testBlock) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService005, - @"Test block set, will not hit the server"); - testBlock(request, handler); - return; - } - - NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:handler]; - [task resume]; -} - -- (void)stopFetching { - [self.session invalidateAndCancel]; - // The session cannot be reused after invalidation. Dispose it to prevent accident reusing. - self.session = nil; -} - -#pragma mark - Private - -- (NSDictionary *)checkinParametersWithExistingCheckin: - (nullable FIRInstanceIDCheckinPreferences *)checkinPreferences { - NSString *deviceModel = FIRInstanceIDDeviceModel(); - NSString *systemVersion = FIRInstanceIDOperatingSystemVersion(); - NSString *osVersion = [NSString stringWithFormat:@"IOS_%@", systemVersion]; - - // Get locale from GCM if GCM exists else use system API. - NSString *locale = FIRInstanceIDCurrentLocale(); - - NSInteger userNumber = 0; // Multi Profile may change this. - NSInteger userSerialNumber = 0; // Multi Profile may change this - - // This ID is generated for logging purpose and it is only logged for performance - // information for backend, not secure information. - // TODO(chliang): Talk to backend team to see if this ID is still needed. - uint32_t loggingID = arc4random(); - NSString *timeZone = [NSTimeZone localTimeZone].name; - int64_t lastCheckingTimestampMillis = checkinPreferences.lastCheckinTimestampMillis; - - NSDictionary *checkinParameters = @{ - @"checkin" : @{ - @"iosbuild" : @{@"model" : deviceModel, @"os_version" : osVersion}, - @"type" : @(kCheckinType), - @"user_number" : @(userNumber), - @"last_checkin_msec" : @(lastCheckingTimestampMillis), - }, - @"fragment" : @(kFragment), - @"logging_id" : @(loggingID), - @"locale" : locale, - @"version" : @(kCheckinVersion), - @"digest" : checkinPreferences.digest ?: @"", - @"time_zone" : timeZone, - @"user_serial_number" : @(userSerialNumber), - @"id" : @([checkinPreferences.deviceID longLongValue]), - @"security_token" : @([checkinPreferences.secretToken longLongValue]), - }; - - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService006, @"Checkin parameters: %@", - checkinParameters); - return checkinParameters; -} - -+ (void)setCheckinTestBlock:(FIRInstanceIDURLRequestTestBlock)block { - testBlock = [block copy]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.h deleted file mode 100644 index 5e1b11947..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -@class FIRInstanceIDAuthKeychain; -@class FIRInstanceIDBackupExcludedPlist; -@class FIRInstanceIDCheckinPreferences; - -// These values exposed for testing -extern NSString *const kFIRInstanceIDCheckinKeychainService; -extern NSString *const kFIRInstanceIDLegacyCheckinKeychainAccount; -extern NSString *const kFIRInstanceIDLegacyCheckinKeychainService; - -/** - * Checkin preferences backing store. - */ -@interface FIRInstanceIDCheckinStore : NSObject - -/** - * Designated Initializer. Initialize a checkin store with the given backup excluded - * plist filename. - * - * @param checkinFilename The backup excluded plist filename to persist checkin - * preferences. - * - * @param subDirectoryName Sub-directory in standard directory where we write - * InstanceID plist. - * - * @return Store to persist checkin preferences. - */ -- (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename - subDirectoryName:(NSString *)subDirectoryName; - -/** - * Initialize a checkin store with the given backup excluded plist and keychain. - * - * @param plist The backup excluded plist to persist checkin preferences. - * @param keychain The keychain used to persist checkin auth preferences. - * - * @return Store to persist checkin preferences. - */ -- (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist - keychain:(FIRInstanceIDAuthKeychain *)keychain; - -/** - * Checks whether the backup excluded checkin preferences are present on the disk or not. - * - * @return YES if the backup excluded checkin plist exists on the disks else NO. - */ -- (BOOL)hasCheckinPlist; - -#pragma mark - Save - -/** - * Save the checkin preferences to backing store. - * - * @param preferences Checkin preferences to save. - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - */ -- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences - handler:(void (^)(NSError *error))handler; - -#pragma mark - Delete - -/** - * Remove the cached checkin preferences. - * - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - */ -- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler; - -#pragma mark - Get - -/** - * Get the cached device secret. If we cannot access it for some reason we - * return the appropriate error object. - * - * @return The cached checkin preferences if present else nil. - */ -- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences; - -/** - * Migrate the checkin item from old service/account to the new one. - * The new account is dynamic as it uses bundle ID. - * This is to ensure checkin is not shared across apps, but still the same - * if app has used GCM before. - * This call should only happen once. - * - */ -- (void)migrateCheckinItemIfNeeded; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.m deleted file mode 100644 index 114277862..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.m +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDCheckinStore.h" - -#import "FIRInstanceIDAuthKeyChain.h" -#import "FIRInstanceIDBackupExcludedPlist.h" -#import "FIRInstanceIDCheckinPreferences+Internal.h" -#import "FIRInstanceIDCheckinPreferences_Private.h" -#import "FIRInstanceIDCheckinService.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDUtilities.h" -#import "FIRInstanceIDVersionUtilities.h" -#import "NSError+FIRInstanceID.h" - -static NSString *const kFIRInstanceIDCheckinKeychainGeneric = @"com.google.iid"; - -NSString *const kFIRInstanceIDCheckinKeychainService = @"com.google.iid.checkin"; -NSString *const kFIRInstanceIDLegacyCheckinKeychainAccount = @"com.google.iid.checkin-account"; -NSString *const kFIRInstanceIDLegacyCheckinKeychainService = @"com.google.iid.checkin-service"; - -// Checkin plist used to have the deviceID and secret stored in them and that's why they -// had 6 items in it. Since the deviceID and secret have been moved to the keychain -// there would only be 4 items. -static const NSInteger kOldCheckinPlistCount = 6; - -@interface FIRInstanceIDCheckinStore () - -@property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist; -@property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain; -// Checkin will store items under -// Keychain account: <app bundle id>, -// Keychain service: |kFIRInstanceIDCheckinKeychainService| -@property(nonatomic, readonly) NSString *bundleIdentifierForKeychainAccount; - -@end - -@implementation FIRInstanceIDCheckinStore - -- (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename - subDirectoryName:(NSString *)subDirectoryName { - FIRInstanceIDBackupExcludedPlist *plist = - [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinFilename - subDirectory:subDirectoryName]; - - FIRInstanceIDAuthKeychain *keychain = - [[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDCheckinKeychainGeneric]; - return [self initWithCheckinPlist:plist keychain:keychain]; -} - -- (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist - keychain:(FIRInstanceIDAuthKeychain *)keychain { - self = [super init]; - if (self) { - _plist = plist; - _keychain = keychain; - } - return self; -} - -- (BOOL)hasCheckinPlist { - return [self.plist doesFileExist]; -} - -- (NSString *)bundleIdentifierForKeychainAccount { - static NSString *bundleIdentifier; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - bundleIdentifier = FIRInstanceIDAppIdentifier(); - }); - return bundleIdentifier; -} - -- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences - handler:(void (^)(NSError *error))handler { - NSDictionary *checkinPlistContents = [preferences checkinPlistContents]; - NSString *checkinKeychainContent = [preferences checkinKeychainContent]; - - if (![checkinKeychainContent length]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore000, - @"Failed to get checkin keychain content from memory."); - if (handler) { - handler([NSError - errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]); - } - return; - } - if (![checkinPlistContents count]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore001, - @"Failed to get checkin plist contents from memory."); - if (handler) { - handler([NSError - errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]); - } - return; - } - - // Save all other checkin preferences in a plist - NSError *error; - if (![self.plist writeDictionary:checkinPlistContents error:&error]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003, - @"Failed to save checkin plist contents." - @"Will delete auth credentials"); - [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount - handler:nil]; - if (handler) { - handler(error); - } - return; - } - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved, - @"Checkin plist file is saved"); - - // Save the deviceID and secret in the Keychain - if (!preferences.hasPreCachedAuthCredentials) { - NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding]; - [self.keychain setData:data - forService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount - handler:^(NSError *error) { - if (error) { - if (handler) { - handler(error); - } - return; - } - if (handler) { - handler(nil); - } - }]; - } else { - handler(nil); - } -} - -- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler { - // Delete the checkin preferences plist first to avoid delay. - NSError *deletePlistError; - if (![self.plist deleteFile:&deletePlistError]) { - handler(deletePlistError); - return; - } - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted, - @"Deleted checkin plist file."); - // Remove deviceID and secret from Keychain - [self.keychain - removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount - handler:^(NSError *error) { - // Try to remove from old location as well because migration - // is no longer needed. Consider this is either a fresh install - // or an identity wipe. - [self.keychain - removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService - account:kFIRInstanceIDLegacyCheckinKeychainAccount - handler:nil]; - handler(error); - }]; -} - -- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences { - // Query the keychain for deviceID and secret - NSData *item = [self.keychain dataForService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount]; - - // Check info found in keychain - NSString *checkinKeychainContent = [[NSString alloc] initWithData:item - encoding:NSUTF8StringEncoding]; - FIRInstanceIDCheckinPreferences *checkinPreferences = - [FIRInstanceIDCheckinPreferences preferencesFromKeychainContents:checkinKeychainContent]; - - NSDictionary *checkinPlistContents = [self.plist contentAsDictionary]; - - NSString *plistDeviceAuthID = checkinPlistContents[kFIRInstanceIDDeviceAuthIdKey]; - NSString *plistSecretToken = checkinPlistContents[kFIRInstanceIDSecretTokenKey]; - - // If deviceID and secret not found in the keychain verify that we don't have them in the - // checkin preferences plist. - if (![checkinPreferences.deviceID length] && ![checkinPreferences.secretToken length]) { - if ([plistDeviceAuthID length] && [plistSecretToken length]) { - // Couldn't find checkin credentials in keychain but found them in the plist. - checkinPreferences = - [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID - secretToken:plistSecretToken]; - } else { - // Couldn't find checkin credentials in keychain nor plist - return nil; - } - } else if (kOldCheckinPlistCount == checkinPlistContents.count) { - // same check as above but just to be extra sure that we cover all upgrade cases properly. - // TODO(chliangGoogle): Remove this case, after verifying it's not needed - if ([plistDeviceAuthID length] && [plistSecretToken length]) { - checkinPreferences = - [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID - secretToken:plistSecretToken]; - } - } - - [checkinPreferences updateWithCheckinPlistContents:checkinPlistContents]; - return checkinPreferences; -} - -- (void)migrateCheckinItemIfNeeded { - // Check for checkin in the old location, using the legacy keys - // Query the keychain for deviceID and secret - NSData *dataInOldLocation = - [self.keychain dataForService:kFIRInstanceIDLegacyCheckinKeychainService - account:kFIRInstanceIDLegacyCheckinKeychainAccount]; - if (dataInOldLocation) { - // Save to new location - [self.keychain setData:dataInOldLocation - forService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount - handler:nil]; - // Remove from old location - [self.keychain removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService - account:kFIRInstanceIDLegacyCheckinKeychainAccount - handler:nil]; - } -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m deleted file mode 100644 index bc6be6c10..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDCombinedHandler.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^FIRInstanseIDHandler)(id _Nullable result, NSError *_Nullable error); - -@interface FIRInstanceIDCombinedHandler <ResultType>() -@property(atomic, readonly, strong) NSMutableArray<FIRInstanseIDHandler> *handlers; -@end - -NS_ASSUME_NONNULL_END - -@implementation FIRInstanceIDCombinedHandler - -- (instancetype)init { - self = [super init]; - if (self) { - _handlers = [NSMutableArray array]; - } - return self; -} - -- (void)addHandler:(FIRInstanseIDHandler)handler { - if (!handler) { - return; - } - - @synchronized(self) { - [self.handlers addObject:handler]; - } -} - -- (FIRInstanseIDHandler)combinedHandler { - FIRInstanseIDHandler combinedHandler = nil; - - @synchronized(self) { - NSArray<FIRInstanseIDHandler> *handlers = [self.handlers copy]; - combinedHandler = ^(id result, NSError *error) { - for (FIRInstanseIDHandler handler in handlers) { - handler(result, error); - } - }; - } - - return combinedHandler; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.h deleted file mode 100644 index cd2e13155..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -#pragma mark - Commands - -/** - * Value included in a structured response or GCM message from IID, indicating - * an identity reset. - */ -FOUNDATION_EXPORT NSString *const kFIRInstanceID_CMD_RST; - -#pragma mark - Notifications - -/// Notification used to deliver GCM messages for InstanceID. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDCheckinFetchedNotification; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDAPNSTokenNotification; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultGCMTokenNotification; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultGCMTokenFailNotification; - -FOUNDATION_EXPORT NSString *const kFIRInstanceIDIdentityInvalidatedNotification; - -#pragma mark - Miscellaneous - -/// The scope used to save the IID "*" scope token. This is used for saving the -/// IID auth token that we receive from the server. This feature was never -/// implemented on the server side. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDAllScopeIdentifier; -/// The scope used to save the IID "*" scope token. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultTokenScope; - -/// Subdirectory in search path directory to store InstanceID preferences. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDSubDirectoryName; - -/// The key for APNS token in options dictionary. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsAPNSKey; - -/// The key for APNS token environment type in options dictionary. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsAPNSIsSandboxKey; - -/// The key for GMP AppID sent in registration requests. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsFirebaseAppIDKey; - -/// The key to enable auto-register by swizzling AppDelegate's methods. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDAppDelegateProxyEnabledInfoPlistKey; - -/// Error code for missing entitlements in Keychain. iOS Keychain error -/// https://forums.developer.apple.com/thread/4743 -FOUNDATION_EXPORT const int kFIRInstanceIDSecMissingEntitlementErrorCode; diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.m deleted file mode 100644 index 81f4620e0..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.m +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDConstants.h" - -// Commands -NSString *const kFIRInstanceID_CMD_RST = @"RST"; - -// NOTIFICATIONS -NSString *const kFIRInstanceIDCheckinFetchedNotification = @"com.google.gcm.notif-checkin-fetched"; -NSString *const kFIRInstanceIDAPNSTokenNotification = @"com.firebase.iid.notif.apns-token"; -NSString *const kFIRInstanceIDDefaultGCMTokenNotification = @"com.firebase.iid.notif.fcm-token"; -NSString *const kFIRInstanceIDDefaultGCMTokenFailNotification = - @"com.firebase.iid.notif.fcm-token-fail"; - -NSString *const kFIRInstanceIDIdentityInvalidatedNotification = @"com.google.iid.identity-invalid"; - -// Miscellaneous -NSString *const kFIRInstanceIDAllScopeIdentifier = @"iid-all"; -NSString *const kFIRInstanceIDDefaultTokenScope = @"*"; -NSString *const kFIRInstanceIDSubDirectoryName = @"Google/FirebaseInstanceID"; - -// Registration Options -NSString *const kFIRInstanceIDTokenOptionsAPNSKey = @"apns_token"; -NSString *const kFIRInstanceIDTokenOptionsAPNSIsSandboxKey = @"apns_sandbox"; -NSString *const kFIRInstanceIDTokenOptionsFirebaseAppIDKey = @"gmp_app_id"; - -NSString *const kFIRInstanceIDAppDelegateProxyEnabledInfoPlistKey = - @"FirebaseAppDelegateProxyEnabled"; - -// iOS Keychain error https://forums.developer.apple.com/thread/4743 -// An undocumented error code hence need to be redeclared. -const int kFIRInstanceIDSecMissingEntitlementErrorCode = -34018; diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDDefines.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDDefines.h deleted file mode 100644 index ccc25b3d5..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDDefines.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FIRInstanceIDLib_FIRInstanceIDDefines_h -#define FIRInstanceIDLib_FIRInstanceIDDefines_h - -#define _FIRInstanceID_VERBOSE_LOGGING 1 - -// Verbose Logging -#if (_FIRInstanceID_VERBOSE_LOGGING) -#define FIRInstanceID_DEV_VERBOSE_LOG(...) NSLog(__VA_ARGS__) -#else -#define FIRInstanceID_DEV_VERBOSE_LOG(...) \ - do { \ - } while (0) -#endif // VERBOSE_LOGGING - -// WEAKIFY & STRONGIFY -// Helper macro. -#define _FIRInstanceID_WEAKNAME(VAR) VAR##_weak_ - -#define FIRInstanceID_WEAKIFY(VAR) __weak __typeof__(VAR) _FIRInstanceID_WEAKNAME(VAR) = (VAR); - -#define FIRInstanceID_STRONGIFY(VAR) \ - _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") \ - __strong __typeof__(VAR) VAR = _FIRInstanceID_WEAKNAME(VAR); \ - _Pragma("clang diagnostic pop") - -// Type Conversions (used for NSInteger etc) -#ifndef _FIRInstanceID_L -#define _FIRInstanceID_L(v) (long)(v) -#endif - -#endif diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.h deleted file mode 100644 index 1c80d8e34..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -/* The Keychain error domain */ -extern NSString *const kFIRInstanceIDKeychainErrorDomain; - -/* - * Wrapping the keychain operations in a serialize queue. This is to avoid keychain operation - * blocking main queue. - */ -@interface FIRInstanceIDKeychain : NSObject - -/** - * FIRInstanceIDKeychain. - * - * @return A shared instance of FIRInstanceIDKeychain. - */ -+ (instancetype)sharedInstance; - -/** - * Get keychain items matching the given a query. - * - * @param keychainQuery The keychain query. - * - * @return An CFTypeRef result matching the provided inputs. - */ -- (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery; - -/** - * Remove the cached items from the keychain matching the query. - * - * @param keychainQuery The keychain query. - * @param handler The callback handler which is invoked when the remove operation is - * complete, with an error if there is any. - */ -- (void)removeItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *error))handler; - -/** - * Add the item with a given query. - * - * @param keychainQuery The keychain query. - * @param handler The callback handler which is invoked when the add operation is - * complete, with an error if there is any. - */ -- (void)addItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *))handler; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.m deleted file mode 100644 index df1b4f763..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.m +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDKeychain.h" - -#import "FIRInstanceIDLogger.h" - -NSString *const kFIRInstanceIDKeychainErrorDomain = @"com.google.iid"; - -@interface FIRInstanceIDKeychain () { - dispatch_queue_t _keychainOperationQueue; -} - -@end - -@implementation FIRInstanceIDKeychain - -+ (instancetype)sharedInstance { - static FIRInstanceIDKeychain *sharedInstance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[FIRInstanceIDKeychain alloc] init]; - }); - return sharedInstance; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _keychainOperationQueue = - dispatch_queue_create("com.google.FirebaseInstanceID.Keychain", DISPATCH_QUEUE_SERIAL); - } - return self; -} - -- (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery { - __block SecKeyRef keyRef = NULL; - dispatch_sync(_keychainOperationQueue, ^{ - OSStatus status = - SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyRef); - - if (status != noErr) { - if (keyRef) { - CFRelease(keyRef); - } - FIRInstanceIDLoggerDebug(kFIRInstanceIDKeychainReadItemError, - @"Info is not found in Keychain. OSStatus: %d. Keychain query: %@", - (int)status, keychainQuery); - } - }); - return keyRef; -} - -- (void)removeItemWithQuery:(NSDictionary *)keychainQuery - handler:(void (^)(NSError *error))handler { - dispatch_async(_keychainOperationQueue, ^{ - OSStatus status = SecItemDelete((__bridge CFDictionaryRef)keychainQuery); - if (status != noErr) { - FIRInstanceIDLoggerDebug( - kFIRInstanceIDKeychainDeleteItemError, - @"Couldn't delete item from Keychain OSStatus: %d with the keychain query %@", - (int)status, keychainQuery); - } - - if (handler) { - NSError *error; - // When item is not found, it should NOT be considered as an error. The operation should - // continue. - if (status != noErr && status != errSecItemNotFound) { - error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain - code:status - userInfo:nil]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - } - }); -} - -- (void)addItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *))handler { - dispatch_async(_keychainOperationQueue, ^{ - OSStatus status = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL); - - if (handler) { - NSError *error; - if (status != noErr) { - FIRInstanceIDLoggerWarning(kFIRInstanceIDKeychainAddItemError, - @"Couldn't add item to Keychain OSStatus: %d", (int)status); - error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain - code:status - userInfo:nil]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - } - }); -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.h deleted file mode 100644 index ab93976ca..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRIMessageCode.h" - -// The convenience macros are only defined if they haven't already been defined. -#ifndef FIRInstanceIDLoggerInfo - -// Convenience macros that log to the shared GTMLogger instance. These macros -// are how users should typically log to FIRInstanceIDLogger. -#define FIRInstanceIDLoggerDebug(code, ...) \ - [FIRInstanceIDSharedLogger() logFuncDebug:__func__ messageCode:code msg:__VA_ARGS__] -#define FIRInstanceIDLoggerInfo(code, ...) \ - [FIRInstanceIDSharedLogger() logFuncInfo:__func__ messageCode:code msg:__VA_ARGS__] -#define FIRInstanceIDLoggerNotice(code, ...) \ - [FIRInstanceIDSharedLogger() logFuncNotice:__func__ messageCode:code msg:__VA_ARGS__] -#define FIRInstanceIDLoggerWarning(code, ...) \ - [FIRInstanceIDSharedLogger() logFuncWarning:__func__ messageCode:code msg:__VA_ARGS__] -#define FIRInstanceIDLoggerError(code, ...) \ - [FIRInstanceIDSharedLogger() logFuncError:__func__ messageCode:code msg:__VA_ARGS__] - -#endif // !defined(FIRInstanceIDLoggerInfo) - -@interface FIRInstanceIDLogger : NSObject - -- (void)logFuncDebug:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4); - -- (void)logFuncInfo:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4); - -- (void)logFuncNotice:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4); - -- (void)logFuncWarning:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4); - -- (void)logFuncError:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4); - -@end - -/** - * Instantiates and/or returns a shared GTMLogger used exclusively - * for InstanceID log messages. - * @return the shared GTMLogger instance - */ -FIRInstanceIDLogger *FIRInstanceIDSharedLogger(void); diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.m deleted file mode 100644 index 2600d3ba8..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.m +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDLogger.h" - -#import <FirebaseCore/FIRLogger.h> - -// Re-definition of FIRLogger service, as it is not included in :FIRAppHeaders target -NSString *const kFIRInstanceIDLoggerService = @"[Firebase/InstanceID]"; - -@implementation FIRInstanceIDLogger - -#pragma mark - Log Helpers - -+ (NSString *)formatMessageCode:(FIRInstanceIDMessageCode)messageCode { - return [NSString stringWithFormat:@"I-IID%06ld", (long)messageCode]; -} - -- (void)logFuncDebug:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - FIRLogBasic(FIRLoggerLevelDebug, kFIRInstanceIDLoggerService, - [FIRInstanceIDLogger formatMessageCode:messageCode], fmt, args); - va_end(args); -} - -- (void)logFuncInfo:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - FIRLogBasic(FIRLoggerLevelInfo, kFIRInstanceIDLoggerService, - [FIRInstanceIDLogger formatMessageCode:messageCode], fmt, args); - va_end(args); -} - -- (void)logFuncNotice:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - FIRLogBasic(FIRLoggerLevelNotice, kFIRInstanceIDLoggerService, - [FIRInstanceIDLogger formatMessageCode:messageCode], fmt, args); - va_end(args); -} - -- (void)logFuncWarning:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - FIRLogBasic(FIRLoggerLevelWarning, kFIRInstanceIDLoggerService, - [FIRInstanceIDLogger formatMessageCode:messageCode], fmt, args); - va_end(args); -} - -- (void)logFuncError:(const char *)func - messageCode:(FIRInstanceIDMessageCode)messageCode - msg:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - FIRLogBasic(FIRLoggerLevelError, kFIRInstanceIDLoggerService, - [FIRInstanceIDLogger formatMessageCode:messageCode], fmt, args); - va_end(args); -} - -@end - -FIRInstanceIDLogger *FIRInstanceIDSharedLogger() { - static dispatch_once_t onceToken; - static FIRInstanceIDLogger *logger; - dispatch_once(&onceToken, ^{ - logger = [[FIRInstanceIDLogger alloc] init]; - }); - - return logger; -} diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.h deleted file mode 100644 index d1f263487..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -@class FIRInstanceIDBackupExcludedPlist; -@class FIRInstanceIDCheckinPreferences; -@class FIRInstanceIDCheckinStore; -@class FIRInstanceIDTokenInfo; -@class FIRInstanceIDTokenStore; - -@class FIRInstanceIDStore; -@protocol FIRInstanceIDStoreDelegate <NSObject> - -/** - * This is called when the store has decided to invalide its tokens associated with the - * previous checkin credentials. After deleting the tokens locally, it calls this method - * to notify the delegate of the change. If possible, the delegate should use this time - * to request the invalidation of the tokens on the server as well. - */ -- (void)store:(FIRInstanceIDStore *)store - didDeleteFCMScopedTokensForCheckin:(FIRInstanceIDCheckinPreferences *)checkin; - -@end - -/** - * Used to persist the InstanceID tokens. This is also used to cache the Checkin - * credentials. The store also checks for stale entries in the store and - * let's us know if things in the store are stale or not. It does not however - * acts on stale entries in anyway. - */ -@interface FIRInstanceIDStore : NSObject - -/** - * The delegate set in the initializer which is notified of changes in the store. - */ -@property(nonatomic, readonly, weak) NSObject<FIRInstanceIDStoreDelegate> *delegate; - -- (instancetype)init __attribute__((unavailable("Use initWithDelegate: instead."))); - -/** - * Initialize a default store to persist InstanceID tokens and options. - * - * @param delegate The delegate with which to be notified of changes in the store. - * @return Store to persist InstanceID tokens. - */ -- (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate; - -/** - * Initialize a store with the token store used to persist tokens, and a checkin store. - * Used for testing. - * - * @param checkinStore Persistent store that persists checkin preferences. - * @param tokenStore Persistent store that persists tokens. - * - * @return Store to persist InstanceID tokens and options. - */ -- (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore - tokenStore:(FIRInstanceIDTokenStore *)tokenStore - delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate - NS_DESIGNATED_INITIALIZER; - -#pragma mark - Save -/** - * Save the instanceID token info to the store. - * - * @param tokenInfo The token info to store. - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - */ -- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo handler:(void (^)(NSError *))handler; - -#pragma mark - Get - -/** - * Get the cached token info. - * - * @param authorizedEntity The authorized entity for which we want the token. - * @param scope The scope for which we want the token. - * - * @return The cached token info if any for the given authorizedEntity and scope else - * returns nil. - */ -- (nullable FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope; -/** - * Return all cached token infos from the Keychain. - * - * @return The cached token infos, if any, that are stored in the Keychain. - */ -- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos; - -#pragma mark - Delete - -/** - * Remove the cached token for a given authorizedEntity and scope. If the token was never - * cached or deleted from the cache before this is a no-op. - * - * @param authorizedEntity The authorizedEntity for the cached token. - * @param scope The scope for the cached token - */ -- (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope; - -/** - * Removes all cached tokens from the persistent store. In case deleting the cached tokens - * fails we try to delete the backup excluded plist that stores the tokens. - * - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - * - */ -- (void)removeAllCachedTokensWithHandler:(nullable void (^)(NSError *error))handler; - -#pragma mark - Persisting Checkin Preferences - -/** - * Save the checkin preferences - * - * @param preferences Checkin preferences to save. - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - */ -- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences - handler:(nullable void (^)(NSError *error))handler; - -/** - * Return the cached checkin preferences. - * - * @return Checkin preferences. - */ -- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences; - -/** - * Remove the cached checkin preferences from the store. - * - * @param handler The callback handler which is invoked when the operation is complete, - * with an error if there is any. - */ -- (void)removeCheckinPreferencesWithHandler:(nullable void (^)(NSError *error))handler; - -#pragma mark - Standard Directory sub-directory - -/** - * Check if supported directory has InstanceID subdirectory - * - * @return YES if the Application Support directory has InstanceID subdirectory else NO. - */ -+ (BOOL)hasSubDirectory:(NSString *)subDirectoryName; - -/** - * Create InstanceID subdirectory in Application support directory. - * - * @return YES if the subdirectory was created successfully else NO. - */ -+ (BOOL)createSubDirectory:(NSString *)subDirectoryName; - -/** - * Removes Application Support subdirectory for InstanceID. - * - * @param error The error object if any while trying to delete the sub-directory. - * - * @return YES if the deletion was successful else NO. - */ -+ (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.m deleted file mode 100644 index 1c7a0d0ac..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.m +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDStore.h" - -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDCheckinStore.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDTokenStore.h" -#import "FIRInstanceIDVersionUtilities.h" - -// NOTE: These values should be in sync with what InstanceID saves in as. -static NSString *const kCheckinFileName = @"g-checkin"; - -// APNS token (use the old key value i.e. with prefix GMS) -static NSString *const kFIRInstanceIDLibraryVersion = @"GMSInstanceID-version"; - -@interface FIRInstanceIDStore () - -@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinStore *checkinStore; -@property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore; - -@end - -@implementation FIRInstanceIDStore - -- (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate { - FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc] - initWithCheckinPlistFileName:kCheckinFileName - subDirectoryName:kFIRInstanceIDSubDirectoryName]; - - FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore]; - - return [self initWithCheckinStore:checkinStore tokenStore:tokenStore delegate:delegate]; -} - -- (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore - tokenStore:(FIRInstanceIDTokenStore *)tokenStore - delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate { - self = [super init]; - if (self) { - _checkinStore = checkinStore; - _tokenStore = tokenStore; - _delegate = delegate; - [self resetCredentialsIfNeeded]; - } - return self; -} - -#pragma mark - Upgrades - -+ (BOOL)hasSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; - BOOL isDirectory; - if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath - isDirectory:&isDirectory]) { - return NO; - } else if (!isDirectory) { - return NO; - } - return YES; -} - -+ (NSSearchPathDirectory)supportedDirectory { -#if TARGET_OS_TV - return NSCachesDirectory; -#else - return NSApplicationSupportDirectory; -#endif -} - -+ (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName { - NSArray *directoryPaths = - NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); - NSString *dirPath = directoryPaths.lastObject; - NSArray *components = @[ dirPath, subDirectoryName ]; - return [NSString pathWithComponents:components]; -} - -+ (BOOL)createSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; - BOOL hasSubDirectory; - - if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath - isDirectory:&hasSubDirectory]) { - NSError *error; - [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath - withIntermediateDirectories:YES - attributes:nil - error:&error]; - if (error) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000, - @"Cannot create directory %@, error: %@", subDirectoryPath, error); - return NO; - } - } else { - if (!hasSubDirectory) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001, - @"Found file instead of directory at %@", subDirectoryPath); - return NO; - } - } - return YES; -} - -+ (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error { - if ([self hasSubDirectory:subDirectoryName]) { - NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; - BOOL isDirectory; - if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath - isDirectory:&isDirectory]) { - return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error]; - } - } - return YES; -} - -/** - * Reset the keychain preferences if the app had been deleted earlier and then reinstalled. - * Keychain preferences are not cleared in the above scenario so explicitly clear them. - * - * In case of an iCloud backup and restore the Keychain preferences should already be empty - * since the Keychain items are marked with `*BackupThisDeviceOnly`. - */ -- (void)resetCredentialsIfNeeded { - BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist]; - // Checkin info existed in backup excluded plist. Should not be a fresh install. - if (checkinPlistExists) { - // FCM user can still have the old version of checkin, migration should only happen once. - [self.checkinStore migrateCheckinItemIfNeeded]; - return; - } - - // reset checkin in keychain if a fresh install. - // set the old checkin preferences to unregister pre-registered tokens - FIRInstanceIDCheckinPreferences *oldCheckinPreferences = - [self.checkinStore cachedCheckinPreferences]; - - if (oldCheckinPreferences) { - [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) { - if (!error) { - FIRInstanceIDLoggerDebug( - kFIRInstanceIDMessageCodeStore002, - @"Removed cached checkin preferences from Keychain because this is a fresh install."); - } else { - FIRInstanceIDLoggerError( - kFIRInstanceIDMessageCodeStore003, - @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error); - } - if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006, - @"App reset detected. Will delete server registrations."); - // We don't really need to delete old FCM tokens created via IID auth tokens since - // those tokens are already hashed by APNS token as the has so creating a new - // token should automatically delete the old-token. - [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences]; - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009, - @"App reset detected but no valid checkin auth preferences found." - @" Will not delete server registrations."); - } - }]; - } -} - -#pragma mark - Get - -- (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope { - // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from - // the keychain. This is because not having the plist signifies a backup and restore operation. - // In case the keychain has any tokens these would now be stale and therefore should be - // deleted. - if (![authorizedEntity length] || ![scope length]) { - return nil; - } - FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity - scope:scope]; - return info; -} - -- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos { - return [self.tokenStore cachedTokenInfos]; -} - -#pragma mark - Save - -- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo - handler:(void (^)(NSError *error))handler { - [self.tokenStore saveTokenInfo:tokenInfo handler:handler]; -} - -#pragma mark - Delete - -- (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope { - if (![authorizedEntity length] || ![scope length]) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012, - @"Will not delete token with invalid entity: %@, scope: %@", - authorizedEntity, scope); - return; - } - [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope]; -} - -- (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler { - [self.tokenStore removeAllTokensWithHandler:handler]; -} - -#pragma mark - FIRInstanceIDCheckinCache protocol - -- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences - handler:(void (^)(NSError *error))handler { - [self.checkinStore saveCheckinPreferences:preferences handler:handler]; -} - -- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences { - return [self.checkinStore cachedCheckinPreferences]; -} - -- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler { - [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) { - if (handler) { - handler(error); - } - }]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.h deleted file mode 100644 index 8f2f36973..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// GTMStringEncoding.h -// -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// 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 is a copy of GTMStringEncoding. FIRInstanceID wants to avoid -// a CocoaPods GTM dependency. Hence we use our own version of StringEncoding. - -#import <Foundation/Foundation.h> - -// A generic class for arbitrary base-2 to 128 string encoding and decoding. -@interface FIRInstanceIDStringEncoding : NSObject { - @private - NSData *charMapData_; - char *charMap_; - int reverseCharMap_[128]; - int shift_; - unsigned int mask_; - BOOL doPad_; - char paddingChar_; - int padLen_; -} - -+ (id)rfc4648Base64WebsafeStringEncoding; - -// Create a new, autoreleased GTMStringEncoding object with the given string, -// as described below. -+ (id)stringEncodingWithString:(NSString *)string; - -// Initialize a new GTMStringEncoding object with the string. -// -// The length of the string must be a power of 2, at least 2 and at most 128. -// Only 7-bit ASCII characters are permitted in the string. -// -// These characters are the canonical set emitted during encoding. -// If the characters have alternatives (e.g. case, easily transposed) then use -// addDecodeSynonyms: to configure them. -- (id)initWithString:(NSString *)string; - -// Indicates whether padding is performed during encoding. -- (BOOL)doPad; -- (void)setDoPad:(BOOL)doPad; - -// Sets the padding character to use during encoding. -- (void)setPaddingChar:(char)c; - -// Encode a raw binary buffer to a 7-bit ASCII string. -- (NSString *)encode:(NSData *)data; - -// Decode a 7-bit ASCII string to a raw binary buffer. -- (NSData *)decode:(NSString *)string; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.m deleted file mode 100644 index e1ab26966..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.m +++ /dev/null @@ -1,208 +0,0 @@ -// -// FIRInstanceIDStringEncoding.m -// -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// 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 is a copy of GTMStringEncoding. FIRInstanceID wants to avoid -// a CocoaPods GTM dependency. Hence we use our own version of StringEncoding. - -#import "FIRInstanceIDStringEncoding.h" - -#import "FIRInstanceIDLogger.h" - -enum { kUnknownChar = -1, kPaddingChar = -2, kIgnoreChar = -3 }; - -@implementation FIRInstanceIDStringEncoding - -+ (id)rfc4648Base64WebsafeStringEncoding { - FIRInstanceIDStringEncoding *ret = [self - stringEncodingWithString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"]; - - [ret setPaddingChar:'=']; - [ret setDoPad:YES]; - return ret; -} - -static inline int lcm(int a, int b) { - for (int aa = a, bb = b;;) { - if (aa == bb) - return aa; - else if (aa < bb) - aa += a; - else - bb += b; - } -} - -+ (id)stringEncodingWithString:(NSString *)string { - return [[FIRInstanceIDStringEncoding alloc] initWithString:string]; -} - -- (id)initWithString:(NSString *)string { - if ((self = [super init])) { - charMapData_ = [string dataUsingEncoding:NSASCIIStringEncoding]; - if (!charMapData_) { - // Unable to convert string to ASCII - return nil; - } - charMap_ = (char *)[charMapData_ bytes]; - NSUInteger length = [charMapData_ length]; - if (length < 2 || length > 128 || length & (length - 1)) { - // Length not a power of 2 between 2 and 128 - return nil; - } - - memset(reverseCharMap_, kUnknownChar, sizeof(reverseCharMap_)); - for (unsigned int i = 0; i < length; i++) { - if (reverseCharMap_[(int)charMap_[i]] != kUnknownChar) { - // Duplicate character at |i| - return nil; - } - reverseCharMap_[(int)charMap_[i]] = i; - } - - for (NSUInteger i = 1; i < length; i <<= 1) shift_++; - mask_ = (1 << shift_) - 1; - padLen_ = lcm(8, shift_) / shift_; - } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<Base%d StringEncoder: %@>", 1 << shift_, charMapData_]; -} - -- (BOOL)doPad { - return doPad_; -} - -- (void)setDoPad:(BOOL)doPad { - doPad_ = doPad; -} - -- (void)setPaddingChar:(char)c { - paddingChar_ = c; - reverseCharMap_[(int)c] = kPaddingChar; -} - -- (NSString *)encode:(NSData *)inData { - NSUInteger inLen = [inData length]; - if (inLen <= 0) { - // Empty input - return @""; - } - unsigned char *inBuf = (unsigned char *)[inData bytes]; - NSUInteger inPos = 0; - - NSUInteger outLen = (inLen * 8 + shift_ - 1) / shift_; - if (doPad_) { - outLen = ((outLen + padLen_ - 1) / padLen_) * padLen_; - } - NSMutableData *outData = [NSMutableData dataWithLength:outLen]; - unsigned char *outBuf = (unsigned char *)[outData mutableBytes]; - NSUInteger outPos = 0; - - unsigned int buffer = inBuf[inPos++]; - int bitsLeft = 8; - while (bitsLeft > 0 || inPos < inLen) { - if (bitsLeft < shift_) { - if (inPos < inLen) { - buffer <<= 8; - buffer |= (inBuf[inPos++] & 0xff); - bitsLeft += 8; - } else { - int pad = shift_ - bitsLeft; - buffer <<= pad; - bitsLeft += pad; - } - } - unsigned int idx = (buffer >> (bitsLeft - shift_)) & mask_; - bitsLeft -= shift_; - outBuf[outPos++] = charMap_[idx]; - } - - if (doPad_) { - while (outPos < outLen) outBuf[outPos++] = paddingChar_; - } - - if (outPos != outLen) { - FIRInstanceIDLoggerError(kFIRInstanceIDStringEncodingBufferUnderflow, - @"Underflowed output buffer"); - return nil; - } - [outData setLength:outPos]; - - return [[NSString alloc] initWithData:outData encoding:NSASCIIStringEncoding]; -} - -- (NSData *)decode:(NSString *)inString { - char *inBuf = (char *)[inString cStringUsingEncoding:NSASCIIStringEncoding]; - if (!inBuf) { - // Unable to convert buffer to ASCII - return nil; - } - NSUInteger inLen = strlen(inBuf); - - NSUInteger outLen = inLen * shift_ / 8; - NSMutableData *outData = [NSMutableData dataWithLength:outLen]; - unsigned char *outBuf = (unsigned char *)[outData mutableBytes]; - NSUInteger outPos = 0; - - int buffer = 0; - int bitsLeft = 0; - BOOL expectPad = NO; - for (NSUInteger i = 0; i < inLen; i++) { - int val = reverseCharMap_[(int)inBuf[i]]; - switch (val) { - case kIgnoreChar: - break; - case kPaddingChar: - expectPad = YES; - break; - case kUnknownChar: - // Unexpected data at input pos |i| - return nil; - default: - if (expectPad) { - // Expected further padding characters - return nil; - } - buffer <<= shift_; - buffer |= val & mask_; - bitsLeft += shift_; - if (bitsLeft >= 8) { - outBuf[outPos++] = (unsigned char)(buffer >> (bitsLeft - 8)); - bitsLeft -= 8; - } - break; - } - } - - if (bitsLeft && buffer & ((1 << bitsLeft) - 1)) { - // Incomplete trailing data - return nil; - } - - // Shorten buffer if needed due to padding chars - if (outPos > outLen) { - FIRInstanceIDLoggerError(kFIRInstanceIDStringEncodingBufferOverflow, @"Overflowed buffer"); - } - [outData setLength:outPos]; - - return outData; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h deleted file mode 100644 index b6723fda0..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenOperation.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRInstanceIDTokenDeleteOperation : FIRInstanceIDTokenOperation - -- (instancetype)initWithAuthorizedEntity:(nullable NSString *)authorizedEntity - scope:(nullable NSString *)scope - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(nullable NSString *)instanceID - action:(FIRInstanceIDTokenAction)action; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.m deleted file mode 100644 index 34511c434..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.m +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenDeleteOperation.h" - -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDTokenOperation+Private.h" -#import "FIRInstanceIDURLQueryItem.h" -#import "FIRInstanceIDUtilities.h" -#import "NSError+FIRInstanceID.h" - -@implementation FIRInstanceIDTokenDeleteOperation - -- (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID - action:(FIRInstanceIDTokenAction)action { - self = [super initWithAction:action - forAuthorizedEntity:authorizedEntity - scope:scope - options:nil - checkinPreferences:checkinPreferences - instanceID:instanceID]; - if (self) { - } - return self; -} - -- (void)performTokenOperation { - NSMutableURLRequest *request = [self tokenRequest]; - - // Build form-encoded body - NSString *deviceAuthID = self.checkinPreferences.deviceID; - NSMutableArray<FIRInstanceIDURLQueryItem *> *queryItems = - [FIRInstanceIDTokenOperation standardQueryItemsWithDeviceID:deviceAuthID scope:self.scope]; - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"delete" value:@"true"]]; - if (self.action == FIRInstanceIDTokenActionDeleteTokenAndIID) { - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"iid-operation" - value:@"delete"]]; - } - if (self.authorizedEntity) { - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"sender" - value:self.authorizedEntity]]; - } - // Typically we include our public key-signed url items, but in some cases (like deleting all FCM - // tokens), we don't. - if (self.instanceID.length > 0) { - [queryItems addObjectsFromArray:[self queryItemsWithInstanceID:self.instanceID]]; - } - - NSString *content = FIRInstanceIDQueryFromQueryItems(queryItems); - request.HTTPBody = [content dataUsingEncoding:NSUTF8StringEncoding]; - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenDeleteOperationFetchRequest, - @"Unregister request to %@ content: %@", FIRInstanceIDRegisterServer(), - content); - - FIRInstanceID_WEAKIFY(self); - void (^requestHandler)(NSData *, NSURLResponse *, NSError *) = - ^(NSData *data, NSURLResponse *response, NSError *error) { - FIRInstanceID_STRONGIFY(self); - [self handleResponseWithData:data response:response error:error]; - }; - - // Test block - if (self.testBlock) { - self.testBlock(request, requestHandler); - return; - } - - NSURLSession *session = [FIRInstanceIDTokenOperation sharedURLSession]; - self.dataTask = [session dataTaskWithRequest:request completionHandler:requestHandler]; - [self.dataTask resume]; -} - -- (void)handleResponseWithData:(NSData *)data - response:(NSURLResponse *)response - error:(NSError *)error { - if (error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenDeleteOperationRequestError, - @"Device unregister HTTP fetch error. Error code: %ld", - _FIRInstanceID_L(error.code)); - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - return; - } - - NSString *dataResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (dataResponse.length == 0) { - NSError *error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]; - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - return; - } - - if (![dataResponse hasPrefix:@"deleted="] && ![dataResponse hasPrefix:@"token="]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenDeleteOperationBadResponse, - @"Invalid unregister response %@", response); - NSError *error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]; - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - return; - } - [self finishWithResult:FIRInstanceIDTokenOperationSucceeded token:nil error:nil]; -} -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h deleted file mode 100644 index 6fa800eff..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenOperation.h" - -NS_ASSUME_NONNULL_BEGIN - -FOUNDATION_EXPORT NSString *const kFIRInstanceIDFirebaseUserAgentKey; - -FOUNDATION_EXPORT NSString *const kFIRInstanceIDFirebaseHeartbeatKey; - -@interface FIRInstanceIDTokenFetchOperation : FIRInstanceIDTokenOperation - -- (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(nullable NSDictionary<NSString *, NSString *> *)options - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID; - -@end -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m deleted file mode 100644 index bdc87014f..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenFetchOperation.h" - -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDTokenOperation+Private.h" -#import "FIRInstanceIDURLQueryItem.h" -#import "FIRInstanceIDUtilities.h" -#import "NSError+FIRInstanceID.h" - -#import <FirebaseCore/FIRAppInternal.h> -#import <FirebaseCore/FIRHeartbeatInfo.h> - -// We can have a static int since this error should theoretically only -// happen once (for the first time). If it repeats there is something -// else that is wrong. -static int phoneRegistrationErrorRetryCount = 0; -static const int kMaxPhoneRegistrationErrorRetryCount = 10; -NSString *const kFIRInstanceIDFirebaseUserAgentKey = @"X-firebase-client"; -NSString *const kFIRInstanceIDFirebaseHeartbeatKey = @"X-firebase-client-log-type"; -NSString *const kFIRInstanceIDHeartbeatTag = @"fire-iid"; - -@implementation FIRInstanceIDTokenFetchOperation - -- (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(nullable NSDictionary<NSString *, NSString *> *)options - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID { - self = [super initWithAction:FIRInstanceIDTokenActionFetch - forAuthorizedEntity:authorizedEntity - scope:scope - options:options - checkinPreferences:checkinPreferences - instanceID:instanceID]; - if (self) { - } - return self; -} - -- (void)performTokenOperation { - NSMutableURLRequest *request = [self tokenRequest]; - NSString *checkinVersionInfo = self.checkinPreferences.versionInfo; - [request setValue:checkinVersionInfo forHTTPHeaderField:@"info"]; - [request setValue:[FIRApp firebaseUserAgent] - forHTTPHeaderField:kFIRInstanceIDFirebaseUserAgentKey]; - [request setValue:@([FIRHeartbeatInfo heartbeatCodeForTag:kFIRInstanceIDHeartbeatTag]).stringValue - forHTTPHeaderField:kFIRInstanceIDFirebaseHeartbeatKey]; - - // Build form-encoded body - NSString *deviceAuthID = self.checkinPreferences.deviceID; - NSMutableArray<FIRInstanceIDURLQueryItem *> *queryItems = - [[self class] standardQueryItemsWithDeviceID:deviceAuthID scope:self.scope]; - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"sender" - value:self.authorizedEntity]]; - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"X-subtype" - value:self.authorizedEntity]]; - - [queryItems addObjectsFromArray:[self queryItemsWithInstanceID:self.instanceID]]; - - // Create query items from passed-in options - id apnsTokenData = self.options[kFIRInstanceIDTokenOptionsAPNSKey]; - id apnsSandboxValue = self.options[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey]; - if ([apnsTokenData isKindOfClass:[NSData class]] && - [apnsSandboxValue isKindOfClass:[NSNumber class]]) { - NSString *APNSString = FIRInstanceIDAPNSTupleStringForTokenAndServerType( - apnsTokenData, ((NSNumber *)apnsSandboxValue).boolValue); - // The name of the query item happens to be the same as the dictionary key - FIRInstanceIDURLQueryItem *item = - [FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDTokenOptionsAPNSKey - value:APNSString]; - [queryItems addObject:item]; - } - id firebaseAppID = self.options[kFIRInstanceIDTokenOptionsFirebaseAppIDKey]; - if ([firebaseAppID isKindOfClass:[NSString class]]) { - // The name of the query item happens to be the same as the dictionary key - FIRInstanceIDURLQueryItem *item = - [FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDTokenOptionsFirebaseAppIDKey - value:(NSString *)firebaseAppID]; - [queryItems addObject:item]; - } - - NSString *content = FIRInstanceIDQueryFromQueryItems(queryItems); - request.HTTPBody = [content dataUsingEncoding:NSUTF8StringEncoding]; - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenFetchOperationFetchRequest, - @"Register request to %@ content: %@", FIRInstanceIDRegisterServer(), - content); - - FIRInstanceID_WEAKIFY(self); - void (^requestHandler)(NSData *, NSURLResponse *, NSError *) = - ^(NSData *data, NSURLResponse *response, NSError *error) { - FIRInstanceID_STRONGIFY(self); - [self handleResponseWithData:data response:response error:error]; - }; - - // Test block - if (self.testBlock) { - self.testBlock(request, requestHandler); - return; - } - - NSURLSession *session = [FIRInstanceIDTokenOperation sharedURLSession]; - self.dataTask = [session dataTaskWithRequest:request completionHandler:requestHandler]; - [self.dataTask resume]; -} - -#pragma mark - Request Handling - -- (void)handleResponseWithData:(NSData *)data - response:(NSURLResponse *)response - error:(NSError *)error { - if (error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenFetchOperationRequestError, - @"Token fetch HTTP error. Error Code: %ld", (long)error.code); - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - return; - } - NSString *dataResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - - if (dataResponse.length == 0) { - NSError *error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]; - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - return; - } - NSDictionary *parsedResponse = [self parseFetchTokenResponse:dataResponse]; - - if ([parsedResponse[@"token"] length]) { - [self finishWithResult:FIRInstanceIDTokenOperationSucceeded - token:parsedResponse[@"token"] - error:nil]; - return; - } - - NSString *errorValue = parsedResponse[@"Error"]; - NSError *responseError; - if (errorValue.length) { - NSArray *errorComponents = [errorValue componentsSeparatedByString:@":"]; - // HACK (Kansas replication delay), PHONE_REGISTRATION_ERROR on App - // uninstall and reinstall. - if ([errorComponents containsObject:@"PHONE_REGISTRATION_ERROR"]) { - // Encountered issue http://b/27043795 - // Retry register until successful or another error encountered or a - // certain number of tries are over. - - if (phoneRegistrationErrorRetryCount < kMaxPhoneRegistrationErrorRetryCount) { - const int nextRetryInterval = 1 << phoneRegistrationErrorRetryCount; - FIRInstanceID_WEAKIFY(self); - - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(nextRetryInterval * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - FIRInstanceID_STRONGIFY(self); - phoneRegistrationErrorRetryCount++; - [self performTokenOperation]; - }); - return; - } - } else if ([errorComponents containsObject:kFIRInstanceID_CMD_RST]) { - // Server detected the identity we use is no longer valid. - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center postNotificationName:kFIRInstanceIDIdentityInvalidatedNotification object:nil]; - - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInternal001, - @"Identity is invalid. Server request identity reset."); - responseError = - [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidIdentity]; - } - } - if (!responseError) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenFetchOperationBadResponse, - @"Invalid fetch response, expected 'token' or 'Error' key"); - responseError = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]; - } - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:responseError]; -} - -// expect a response e.g. "token=<reg id>\nGOOG.ttl=123" -- (NSDictionary *)parseFetchTokenResponse:(NSString *)response { - NSArray *lines = [response componentsSeparatedByString:@"\n"]; - NSMutableDictionary *parsedResponse = [NSMutableDictionary dictionary]; - for (NSString *line in lines) { - NSArray *keyAndValue = [line componentsSeparatedByString:@"="]; - if ([keyAndValue count] > 1) { - parsedResponse[keyAndValue[0]] = keyAndValue[1]; - } - } - return parsedResponse; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.h deleted file mode 100644 index 3b752a3aa..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -#import "FIRInstanceIDAPNSInfo.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * Represents an Instance ID token, and all of the relevant information - * associated with it. It can read from and write to an NSDictionary object, for - * simple serialization. - */ -@interface FIRInstanceIDTokenInfo : NSObject <NSCoding> - -/// The authorized entity (also known as Sender ID), associated with the token. -@property(nonatomic, readonly, copy) NSString *authorizedEntity; -/// The scope associated with the token. This is an arbitrary string, typically "*". -@property(nonatomic, readonly, copy) NSString *scope; -/// The token value itself, with which all other properties are associated. -@property(nonatomic, readonly, copy) NSString *token; - -// These properties are nullable because they might not exist for tokens fetched from -// legacy storage formats. - -/// The app version that this token represents. -@property(nonatomic, readonly, copy, nullable) NSString *appVersion; -/// The Firebase app ID (also known as GMP App ID), that this token is associated with. -@property(nonatomic, readonly, copy, nullable) NSString *firebaseAppID; - -/// Tokens may not always be associated with an APNs token, and may be associated after -/// being created. -@property(nonatomic, strong, nullable) FIRInstanceIDAPNSInfo *APNSInfo; -/// The time that this token info was updated. The cache time is writeable, since in -/// some cases the token info may be refreshed from the server. In those situations, -/// the cacheTime would be updated. -@property(nonatomic, copy, nullable) NSDate *cacheTime; - -/** - * Initializes a FIRInstanceIDTokenInfo object with the required parameters. These - * parameters represent all the relevant associated data with a token. - * - * @param authorizedEntity The authorized entity (also known as Sender ID). - * @param scope The scope of the token, typically "*" meaning - * it's a "default scope". - * @param token The token value itself. - * @param appVersion The application version that this token is associated with. - * @param firebaseAppID The Firebase app ID which this token is associated with. - * @return An instance of FIRInstanceIDTokenInfo. - */ -- (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - token:(NSString *)token - appVersion:(nullable NSString *)appVersion - firebaseAppID:(nullable NSString *)firebaseAppID; - -/** - * Check whether the token is still fresh based on: - * 1. Last fetch token is within the 7 days. - * 2. Language setting is not changed. - * 3. App version is current. - * 4. GMP App ID is current. - * 5. token is consistent with the current IID. - * 6. APNS info has changed. - * @param IID The app identifiier that is used to check if token is prefixed with. - * @return If token is fresh. - * - */ -- (BOOL)isFreshWithIID:(NSString *)IID; - -/* - * Check whether the token is default token. - */ -- (BOOL)isDefaultToken; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.m deleted file mode 100644 index 59b0e9240..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.m +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenInfo.h" - -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDUtilities.h" - -/** - * @enum Token Info Dictionary Key Constants - * @discussion The keys that are checked when a token info is - * created from a dictionary. The same keys are used - * when decoding/encoding an archive. - */ -/// Specifies a dictonary key whose value represents the authorized entity, or -/// Sender ID for the token. -static NSString *const kFIRInstanceIDAuthorizedEntityKey = @"authorized_entity"; -/// Specifies a dictionary key whose value represents the scope of the token, -/// typically "*". -static NSString *const kFIRInstanceIDScopeKey = @"scope"; -/// Specifies a dictionary key which represents the token value itself. -static NSString *const kFIRInstanceIDTokenKey = @"token"; -/// Specifies a dictionary key which represents the app version associated -/// with the token. -static NSString *const kFIRInstanceIDAppVersionKey = @"app_version"; -/// Specifies a dictionary key which represents the GMP App ID associated with -/// the token. -static NSString *const kFIRInstanceIDFirebaseAppIDKey = @"firebase_app_id"; -/// Specifies a dictionary key representing an archive for a -/// `FIRInstanceIDAPNSInfo` object. -static NSString *const kFIRInstanceIDAPNSInfoKey = @"apns_info"; -/// Specifies a dictionary key representing the "last cached" time for the token. -static NSString *const kFIRInstanceIDCacheTimeKey = @"cache_time"; -/// Default interval that token stays fresh. -const NSTimeInterval kDefaultFetchTokenInterval = 7 * 24 * 60 * 60; // 7 days. - -@implementation FIRInstanceIDTokenInfo - -- (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - token:(NSString *)token - appVersion:(NSString *)appVersion - firebaseAppID:(NSString *)firebaseAppID { - self = [super init]; - if (self) { - _authorizedEntity = [authorizedEntity copy]; - _scope = [scope copy]; - _token = [token copy]; - _appVersion = [appVersion copy]; - _firebaseAppID = [firebaseAppID copy]; - } - return self; -} - -- (BOOL)isFreshWithIID:(NSString *)IID { - // Last fetch token cache time could be null if token is from legacy storage format. Then token is - // considered not fresh and should be refreshed and overwrite with the latest storage format. - if (!IID) { - return NO; - } - if (!_cacheTime) { - return NO; - } - - // Check if it's consistent with IID - if (![self.token hasPrefix:IID]) { - return NO; - } - - // Check if app has just been updated to a new version. - NSString *currentAppVersion = FIRInstanceIDCurrentAppVersion(); - if (!_appVersion || ![_appVersion isEqualToString:currentAppVersion]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenManager004, - @"Invalidating cached token for %@ (%@) due to app version change.", - _authorizedEntity, _scope); - return NO; - } - - // Check if GMP App ID has changed - NSString *currentFirebaseAppID = FIRInstanceIDFirebaseAppID(); - if (!_firebaseAppID || ![_firebaseAppID isEqualToString:currentFirebaseAppID]) { - FIRInstanceIDLoggerDebug( - kFIRInstanceIDMessageCodeTokenInfoFirebaseAppIDChanged, - @"Invalidating cached token due to Firebase App IID change from %@ to %@", _firebaseAppID, - currentFirebaseAppID); - return NO; - } - - // Check whether locale has changed, if yes, token needs to be updated with server for locale - // information. - if (FIRInstanceIDHasLocaleChanged()) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenInfoLocaleChanged, - @"Invalidating cached token due to locale change"); - return NO; - } - - // Locale is not changed, check whether token has been fetched within 7 days. - NSTimeInterval lastFetchTokenTimestamp = [_cacheTime timeIntervalSince1970]; - NSTimeInterval currentTimestamp = FIRInstanceIDCurrentTimestampInSeconds(); - NSTimeInterval timeSinceLastFetchToken = currentTimestamp - lastFetchTokenTimestamp; - return (timeSinceLastFetchToken < kDefaultFetchTokenInterval); -} - -- (BOOL)isDefaultToken { - return [self.scope isEqualToString:kFIRInstanceIDDefaultTokenScope]; -} - -#pragma mark - NSCoding - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - // These value cannot be nil - - id authorizedEntity = [aDecoder decodeObjectForKey:kFIRInstanceIDAuthorizedEntityKey]; - if (![authorizedEntity isKindOfClass:[NSString class]]) { - return nil; - } - - id scope = [aDecoder decodeObjectForKey:kFIRInstanceIDScopeKey]; - if (![scope isKindOfClass:[NSString class]]) { - return nil; - } - - id token = [aDecoder decodeObjectForKey:kFIRInstanceIDTokenKey]; - if (![token isKindOfClass:[NSString class]]) { - return nil; - } - - // These values are nullable, so only fail the decode if the type does not match - - id appVersion = [aDecoder decodeObjectForKey:kFIRInstanceIDAppVersionKey]; - if (appVersion && ![appVersion isKindOfClass:[NSString class]]) { - return nil; - } - - id firebaseAppID = [aDecoder decodeObjectForKey:kFIRInstanceIDFirebaseAppIDKey]; - if (firebaseAppID && ![firebaseAppID isKindOfClass:[NSString class]]) { - return nil; - } - - id rawAPNSInfo = [aDecoder decodeObjectForKey:kFIRInstanceIDAPNSInfoKey]; - if (rawAPNSInfo && ![rawAPNSInfo isKindOfClass:[NSData class]]) { - return nil; - } - - FIRInstanceIDAPNSInfo *APNSInfo = nil; - if (rawAPNSInfo) { - // TODO(chliangGoogle: Use the new API and secureCoding protocol. - @try { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - APNSInfo = [NSKeyedUnarchiver unarchiveObjectWithData:rawAPNSInfo]; -#pragma clang diagnostic pop - } @catch (NSException *exception) { - FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeTokenInfoBadAPNSInfo, - @"Could not parse raw APNS Info while parsing archived token info."); - APNSInfo = nil; - } @finally { - } - } - - id cacheTime = [aDecoder decodeObjectForKey:kFIRInstanceIDCacheTimeKey]; - if (cacheTime && ![cacheTime isKindOfClass:[NSDate class]]) { - return nil; - } - - self = [super init]; - if (self) { - _authorizedEntity = authorizedEntity; - _scope = scope; - _token = token; - _appVersion = appVersion; - _firebaseAppID = firebaseAppID; - _APNSInfo = APNSInfo; - _cacheTime = cacheTime; - } - return self; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:self.authorizedEntity forKey:kFIRInstanceIDAuthorizedEntityKey]; - [aCoder encodeObject:self.scope forKey:kFIRInstanceIDScopeKey]; - [aCoder encodeObject:self.token forKey:kFIRInstanceIDTokenKey]; - [aCoder encodeObject:self.appVersion forKey:kFIRInstanceIDAppVersionKey]; - [aCoder encodeObject:self.firebaseAppID forKey:kFIRInstanceIDFirebaseAppIDKey]; - NSData *rawAPNSInfo; - if (self.APNSInfo) { - // TODO(chliangGoogle: Use the new API and secureCoding protocol. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - rawAPNSInfo = [NSKeyedArchiver archivedDataWithRootObject:self.APNSInfo]; -#pragma clang diagnostic pop - - [aCoder encodeObject:rawAPNSInfo forKey:kFIRInstanceIDAPNSInfoKey]; - } - [aCoder encodeObject:self.cacheTime forKey:kFIRInstanceIDCacheTimeKey]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.h deleted file mode 100644 index 46e1ac887..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceID.h" - -@class FIRInstanceIDAuthService; -@class FIRInstanceIDCheckinPreferences; -@class FIRInstanceIDTokenInfo; -@class FIRInstanceIDStore; - -typedef NS_OPTIONS(NSUInteger, FIRInstanceIDInvalidTokenReason) { - FIRInstanceIDInvalidTokenReasonNone = 0, // 0 - FIRInstanceIDInvalidTokenReasonAppVersion = (1 << 0), // 0...00001 - FIRInstanceIDInvalidTokenReasonAPNSToken = (1 << 1), // 0...00010 -}; - -/** - * Manager for the InstanceID token requests i.e `newToken` and `deleteToken`. This - * manages the overall interaction of the `InstanceIDStore`, the token register - * service and the callbacks associated with `GCMInstanceID`. - */ -@interface FIRInstanceIDTokenManager : NSObject - -/// Expose the auth service, so it can be used by others -@property(nonatomic, readonly, strong) FIRInstanceIDAuthService *authService; - -/** - * Fetch new token for the given authorizedEntity and scope. This makes an - * asynchronous request to the InstanceID backend to create a new token for - * the service and returns it. This will replace any old token for the given - * authorizedEntity and scope that has been cached before. - * - * @param authorizedEntity The authorized entity for the token, should not be nil. - * @param scope The scope for the token, should not be nil. - * @param instanceID The unique string identifying the app instance. - * @param options The options to be added to the fetch request. - * @param handler The handler to be invoked once we have the token or the - * fetch request to InstanceID backend results in an error. Also - * since it's a public handler it should always be called - * asynchronously. This should be non-nil. - */ -- (void)fetchNewTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - instanceID:(NSString *)instanceID - options:(NSDictionary *)options - handler:(FIRInstanceIDTokenHandler)handler; - -/** - * Return the cached token info, if one exists, for the given authorizedEntity and scope. - * - * @param authorizedEntity The authorized entity for the token. - * @param scope The scope for the token. - * - * @return The cached token info, if available, matching the parameters. - */ -- (FIRInstanceIDTokenInfo *)cachedTokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope; - -/** - * Delete the token for the given authorizedEntity and scope. If the token has - * been cached, it will be deleted from the store. It will also make an - * asynchronous request to the InstanceID backend to invalidate the token. - * - * @param authorizedEntity The authorized entity for the token, should not be nil. - * @param scope The scope for the token, should not be nil. - * @param instanceID The unique string identifying the app instance. - * @param handler The handler to be invoked once the delete request to - * InstanceID backend has returned. If the request was - * successful we invoke the handler with a nil error; - * otherwise we call it with an appropriate error. Also since - * it's a public handler it should always be called - * asynchronously. This should be non-nil. - */ -- (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - instanceID:(NSString *)instanceID - handler:(FIRInstanceIDDeleteTokenHandler)handler; - -/** - * Deletes all cached tokens from the persistent store. This method should only be triggered - * when InstanceID is deleted - * - * @param instanceID The unique string identifying the app instance. - * @param handler The handler to be invoked once the delete request to InstanceID backend - * has returned. If the request was successful we invoke the handler with - * a nil error; else we pass in an appropriate error. This should be non-nil - * and be called asynchronously. - */ -- (void)deleteAllTokensWithInstanceID:(NSString *)instanceID - handler:(FIRInstanceIDDeleteHandler)handler; - -/** - * Deletes all cached tokens from the persistent store. - * @param handler The callback handler which is invoked when tokens deletion is complete, - * with an error if there is any. - * - */ -- (void)deleteAllTokensLocallyWithHandler:(void (^)(NSError *error))handler; - -/** - * Stop any ongoing token operations. - */ -- (void)stopAllTokenOperations; - -#pragma mark - Invalidating Cached Tokens - -/** - * Invalidate any cached tokens, if the app version has changed since last launch or if the token - * is cached for more than 7 days. - * @param IID The cached instanceID, check if token is prefixed by such IID. - * - * @return Whether we should fetch default token from server. - * - * @discussion This should safely be called prior to any tokens being retrieved from - * the cache or being fetched from the network. - */ -- (BOOL)checkTokenRefreshPolicyWithIID:(NSString *)IID; - -/** - * Upon being provided with different APNs or sandbox, any locally cached tokens - * should be deleted, and the new APNs token should be cached. - * - * @discussion It is possible for this method to be called while token operations are - * in-progress or queued. In this case, the in-flight token operations will have stale - * APNs information. The default token is checked for being out-of-date by Instance ID, - * and re-fetched. Custom tokens are not currently checked. - * - * @param deviceToken The APNS device token, provided by the operating system. - * @param isSandbox YES if the device token is for the sandbox environment, NO otherwise. - * - * @return The array of FIRInstanceIDTokenInfo objects which were invalidated. - */ -- (NSArray<FIRInstanceIDTokenInfo *> *)updateTokensToAPNSDeviceToken:(NSData *)deviceToken - isSandbox:(BOOL)isSandbox; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.m deleted file mode 100644 index 0ebcfc889..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.m +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenManager.h" - -#import "FIRInstanceIDAuthKeyChain.h" -#import "FIRInstanceIDAuthService.h" -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDDefines.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDStore.h" -#import "FIRInstanceIDTokenDeleteOperation.h" -#import "FIRInstanceIDTokenFetchOperation.h" -#import "FIRInstanceIDTokenInfo.h" -#import "FIRInstanceIDTokenOperation.h" -#import "NSError+FIRInstanceID.h" - -@interface FIRInstanceIDTokenManager () <FIRInstanceIDStoreDelegate> - -@property(nonatomic, readwrite, strong) FIRInstanceIDStore *instanceIDStore; -@property(nonatomic, readwrite, strong) FIRInstanceIDAuthService *authService; -@property(nonatomic, readonly, strong) NSOperationQueue *tokenOperations; - -@property(nonatomic, readwrite, strong) FIRInstanceIDAPNSInfo *currentAPNSInfo; - -@end - -@implementation FIRInstanceIDTokenManager - -- (instancetype)init { - self = [super init]; - if (self) { - _instanceIDStore = [[FIRInstanceIDStore alloc] initWithDelegate:self]; - _authService = [[FIRInstanceIDAuthService alloc] initWithStore:_instanceIDStore]; - [self configureTokenOperations]; - } - return self; -} - -- (void)dealloc { - [self stopAllTokenOperations]; -} - -- (void)configureTokenOperations { - _tokenOperations = [[NSOperationQueue alloc] init]; - _tokenOperations.name = @"com.google.iid-token-operations"; - // For now, restrict the operations to be serial, because in some cases (like if the - // authorized entity and scope are the same), order matters. - // If we have to deal with several different token requests simultaneously, it would be a good - // idea to add some better intelligence around this (performing unrelated token operations - // simultaneously, etc.). - _tokenOperations.maxConcurrentOperationCount = 1; - if ([_tokenOperations respondsToSelector:@selector(qualityOfService)]) { - _tokenOperations.qualityOfService = NSOperationQualityOfServiceUtility; - } -} - -- (void)fetchNewTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - instanceID:(NSString *)instanceID - options:(NSDictionary *)options - handler:(FIRInstanceIDTokenHandler)handler { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenManager000, - @"Fetch new token for authorizedEntity: %@, scope: %@", authorizedEntity, - scope); - FIRInstanceIDTokenFetchOperation *operation = - [self createFetchOperationWithAuthorizedEntity:authorizedEntity - scope:scope - options:options - instanceID:instanceID]; - FIRInstanceID_WEAKIFY(self); - FIRInstanceIDTokenOperationCompletion completion = - ^(FIRInstanceIDTokenOperationResult result, NSString *_Nullable token, - NSError *_Nullable error) { - FIRInstanceID_STRONGIFY(self); - if (error) { - handler(nil, error); - return; - } - NSString *firebaseAppID = options[kFIRInstanceIDTokenOptionsFirebaseAppIDKey]; - FIRInstanceIDTokenInfo *tokenInfo = [[FIRInstanceIDTokenInfo alloc] - initWithAuthorizedEntity:authorizedEntity - scope:scope - token:token - appVersion:FIRInstanceIDCurrentAppVersion() - firebaseAppID:firebaseAppID]; - tokenInfo.APNSInfo = [[FIRInstanceIDAPNSInfo alloc] initWithTokenOptionsDictionary:options]; - - [self.instanceIDStore - saveTokenInfo:tokenInfo - handler:^(NSError *error) { - if (!error) { - // Do not send the token back in case the save was unsuccessful. Since with - // the new asychronous fetch mechanism this can lead to infinite loops, for - // example, we will return a valid token even though we weren't able to store - // it in our cache. The first token will lead to a onTokenRefresh callback - // wherein the user again calls `getToken` but since we weren't able to save - // it we won't hit the cache but hit the server again leading to an infinite - // loop. - FIRInstanceIDLoggerDebug( - kFIRInstanceIDMessageCodeTokenManager001, - @"Token fetch successful, token: %@, authorizedEntity: %@, scope:%@", - token, authorizedEntity, scope); - - if (handler) { - handler(token, nil); - } - } else { - if (handler) { - handler(nil, error); - } - } - }]; - }; - // Add completion handler, and ensure it's called on the main queue - [operation addCompletionHandler:^(FIRInstanceIDTokenOperationResult result, - NSString *_Nullable token, NSError *_Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(result, token, error); - }); - }]; - [self.tokenOperations addOperation:operation]; -} - -- (FIRInstanceIDTokenInfo *)cachedTokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope { - return [self.instanceIDStore tokenInfoWithAuthorizedEntity:authorizedEntity scope:scope]; -} - -- (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - instanceID:(NSString *)instanceID - handler:(FIRInstanceIDDeleteTokenHandler)handler { - if ([self.instanceIDStore tokenInfoWithAuthorizedEntity:authorizedEntity scope:scope]) { - [self.instanceIDStore removeCachedTokenWithAuthorizedEntity:authorizedEntity scope:scope]; - } - // Does not matter if we cannot find it in the cache. Still make an effort to unregister - // from the server. - FIRInstanceIDCheckinPreferences *checkinPreferences = self.authService.checkinPreferences; - FIRInstanceIDTokenDeleteOperation *operation = - [self createDeleteOperationWithAuthorizedEntity:authorizedEntity - scope:scope - checkinPreferences:checkinPreferences - instanceID:instanceID - action:FIRInstanceIDTokenActionDeleteToken]; - - if (handler) { - [operation addCompletionHandler:^(FIRInstanceIDTokenOperationResult result, - NSString *_Nullable token, NSError *_Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - }]; - } - [self.tokenOperations addOperation:operation]; -} - -- (void)deleteAllTokensWithInstanceID:(NSString *)instanceID - handler:(FIRInstanceIDDeleteHandler)handler { - // delete all tokens - FIRInstanceIDCheckinPreferences *checkinPreferences = self.authService.checkinPreferences; - if (!checkinPreferences) { - // The checkin is already deleted. No need to trigger the token delete operation as client no - // longer has the checkin information for server to delete. - dispatch_async(dispatch_get_main_queue(), ^{ - handler(nil); - }); - return; - } - FIRInstanceIDTokenDeleteOperation *operation = - [self createDeleteOperationWithAuthorizedEntity:kFIRInstanceIDKeychainWildcardIdentifier - scope:kFIRInstanceIDKeychainWildcardIdentifier - checkinPreferences:checkinPreferences - instanceID:instanceID - action:FIRInstanceIDTokenActionDeleteTokenAndIID]; - if (handler) { - [operation addCompletionHandler:^(FIRInstanceIDTokenOperationResult result, - NSString *_Nullable token, NSError *_Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - }]; - } - [self.tokenOperations addOperation:operation]; -} - -- (void)deleteAllTokensLocallyWithHandler:(void (^)(NSError *error))handler { - [self.instanceIDStore removeAllCachedTokensWithHandler:handler]; -} - -- (void)stopAllTokenOperations { - [self.authService stopCheckinRequest]; - [self.tokenOperations cancelAllOperations]; -} - -#pragma mark - FIRInstanceIDStoreDelegate - -- (void)store:(FIRInstanceIDStore *)store - didDeleteFCMScopedTokensForCheckin:(FIRInstanceIDCheckinPreferences *)checkin { - // Make a best effort try to delete the old client related state on the FCM server. This is - // required to delete old pubusb registrations which weren't cleared when the app was deleted. - // - // This is only a one time effort. If this call fails the client would still receive duplicate - // pubsub notifications if he is again subscribed to the same topic. - // - // The client state should be cleared on the server for the provided checkin preferences. - FIRInstanceIDTokenDeleteOperation *operation = - [self createDeleteOperationWithAuthorizedEntity:nil - scope:nil - checkinPreferences:checkin - instanceID:nil - action:FIRInstanceIDTokenActionDeleteToken]; - [operation addCompletionHandler:^(FIRInstanceIDTokenOperationResult result, - NSString *_Nullable token, NSError *_Nullable error) { - if (error) { - FIRInstanceIDMessageCode code = - kFIRInstanceIDMessageCodeTokenManagerErrorDeletingFCMTokensOnAppReset; - FIRInstanceIDLoggerDebug(code, @"Failed to delete GCM server registrations on app reset."); - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenManagerDeletedFCMTokensOnAppReset, - @"Successfully deleted GCM server registrations on app reset"); - } - }]; - - [self.tokenOperations addOperation:operation]; -} - -#pragma mark - Unit Testing Stub Helpers -// We really have this method so that we can more easily stub it out for unit testing -- (FIRInstanceIDTokenFetchOperation *) - createFetchOperationWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(NSDictionary<NSString *, NSString *> *)options - instanceID:(NSString *)instanceID { - FIRInstanceIDCheckinPreferences *checkinPreferences = self.authService.checkinPreferences; - FIRInstanceIDTokenFetchOperation *operation = - [[FIRInstanceIDTokenFetchOperation alloc] initWithAuthorizedEntity:authorizedEntity - scope:scope - options:options - checkinPreferences:checkinPreferences - instanceID:instanceID]; - return operation; -} - -// We really have this method so that we can more easily stub it out for unit testing -- (FIRInstanceIDTokenDeleteOperation *) - createDeleteOperationWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID - action:(FIRInstanceIDTokenAction)action { - FIRInstanceIDTokenDeleteOperation *operation = - [[FIRInstanceIDTokenDeleteOperation alloc] initWithAuthorizedEntity:authorizedEntity - scope:scope - checkinPreferences:checkinPreferences - instanceID:instanceID - action:action]; - return operation; -} - -#pragma mark - Invalidating Cached Tokens -- (BOOL)checkTokenRefreshPolicyWithIID:(NSString *)IID { - // We know at least one cached token exists. - BOOL shouldFetchDefaultToken = NO; - NSArray<FIRInstanceIDTokenInfo *> *tokenInfos = [self.instanceIDStore cachedTokenInfos]; - - NSMutableArray<FIRInstanceIDTokenInfo *> *tokenInfosToDelete = - [NSMutableArray arrayWithCapacity:tokenInfos.count]; - for (FIRInstanceIDTokenInfo *tokenInfo in tokenInfos) { - if ([tokenInfo isFreshWithIID:IID]) { - // Token is fresh and in right format, do nothing - continue; - } - if ([tokenInfo isDefaultToken]) { - // Default token is expired, do not mark for deletion. Fetch directly from server to - // replace the current one. - shouldFetchDefaultToken = YES; - } else { - // Non-default token is expired, mark for deletion. - [tokenInfosToDelete addObject:tokenInfo]; - } - FIRInstanceIDLoggerDebug( - kFIRInstanceIDMessageCodeTokenManagerInvalidateStaleToken, - @"Invalidating cached token for %@ (%@) due to token is no longer fresh.", - tokenInfo.authorizedEntity, tokenInfo.scope); - } - for (FIRInstanceIDTokenInfo *tokenInfoToDelete in tokenInfosToDelete) { - [self.instanceIDStore removeCachedTokenWithAuthorizedEntity:tokenInfoToDelete.authorizedEntity - scope:tokenInfoToDelete.scope]; - } - return shouldFetchDefaultToken; -} - -- (NSArray<FIRInstanceIDTokenInfo *> *)updateTokensToAPNSDeviceToken:(NSData *)deviceToken - isSandbox:(BOOL)isSandbox { - // Each cached IID token that is missing an APNSInfo, or has an APNSInfo associated should be - // checked and invalidated if needed. - FIRInstanceIDAPNSInfo *APNSInfo = [[FIRInstanceIDAPNSInfo alloc] initWithDeviceToken:deviceToken - isSandbox:isSandbox]; - if ([self.currentAPNSInfo isEqualToAPNSInfo:APNSInfo]) { - return @[]; - } - self.currentAPNSInfo = APNSInfo; - - NSArray<FIRInstanceIDTokenInfo *> *tokenInfos = [self.instanceIDStore cachedTokenInfos]; - NSMutableArray<FIRInstanceIDTokenInfo *> *tokenInfosToDelete = - [NSMutableArray arrayWithCapacity:tokenInfos.count]; - for (FIRInstanceIDTokenInfo *cachedTokenInfo in tokenInfos) { - // Check if the cached APNSInfo is nil, or if it is an old APNSInfo. - if (!cachedTokenInfo.APNSInfo || - ![cachedTokenInfo.APNSInfo isEqualToAPNSInfo:self.currentAPNSInfo]) { - // Mark for invalidation. - [tokenInfosToDelete addObject:cachedTokenInfo]; - } - } - for (FIRInstanceIDTokenInfo *tokenInfoToDelete in tokenInfosToDelete) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenManagerAPNSChangedTokenInvalidated, - @"Invalidating cached token for %@ (%@) due to APNs token change.", - tokenInfoToDelete.authorizedEntity, tokenInfoToDelete.scope); - [self.instanceIDStore removeCachedTokenWithAuthorizedEntity:tokenInfoToDelete.authorizedEntity - scope:tokenInfoToDelete.scope]; - } - return tokenInfosToDelete; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h deleted file mode 100644 index 338387577..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenOperation.h" - -#import "FIRInstanceIDUtilities.h" - -@class FIRInstanceIDURLQueryItem; - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRInstanceIDTokenOperation (Private) - -@property(atomic, strong) NSURLSessionDataTask *dataTask; -@property(readonly, strong) - NSMutableArray<FIRInstanceIDTokenOperationCompletion> *completionHandlers; - -// For testing only -@property(nonatomic, readwrite, copy) FIRInstanceIDURLRequestTestBlock testBlock; - -+ (NSURLSession *)sharedURLSession; - -#pragma mark - Initialization -- (instancetype)initWithAction:(FIRInstanceIDTokenAction)action - forAuthorizedEntity:(nullable NSString *)authorizedEntity - scope:(NSString *)scope - options:(nullable NSDictionary<NSString *, NSString *> *)options - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID; - -#pragma mark - Request Construction -+ (NSMutableArray<FIRInstanceIDURLQueryItem *> *)standardQueryItemsWithDeviceID:(NSString *)deviceID - scope:(NSString *)scope; -- (NSMutableURLRequest *)tokenRequest; -- (NSArray<FIRInstanceIDURLQueryItem *> *)queryItemsWithInstanceID:(NSString *)instanceID; - -#pragma mark - HTTP Headers -/** - * Given a valid checkin preferences object, it will return a string that can be used - * in the "Authorization" HTTP header to authenticate this request. - * - * @param checkin The valid checkin preferences object, with a deviceID and secretToken. - */ -+ (NSString *)HTTPAuthHeaderFromCheckin:(FIRInstanceIDCheckinPreferences *)checkin; - -#pragma mark - Result -- (void)finishWithResult:(FIRInstanceIDTokenOperationResult)result - token:(nullable NSString *)token - error:(nullable NSError *)error; - -#pragma mark - Methods to override -- (void)performTokenOperation; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.h deleted file mode 100644 index fa8ad085b..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -@class FIRInstanceIDCheckinPreferences; - -NS_ASSUME_NONNULL_BEGIN - -/** - * Represents the action taken on an FCM token. - */ -typedef NS_ENUM(NSInteger, FIRInstanceIDTokenAction) { - FIRInstanceIDTokenActionFetch, - FIRInstanceIDTokenActionDeleteToken, - FIRInstanceIDTokenActionDeleteTokenAndIID, -}; - -/** - * Represents the possible results of a token operation. - */ -typedef NS_ENUM(NSInteger, FIRInstanceIDTokenOperationResult) { - FIRInstanceIDTokenOperationSucceeded, - FIRInstanceIDTokenOperationError, - FIRInstanceIDTokenOperationCancelled, -}; - -/** - * Callback to invoke once the HTTP call to FIRMessaging backend for updating - * subscription finishes. - * - * @param result The result of the operation. - * @param token If the action for fetching a token and the request was successful, this will hold - * the value of the token. Otherwise nil. - * @param error The error which occurred while performing the token operation. This will be nil - * in case the operation was successful, or if the operation was cancelled. - */ -typedef void (^FIRInstanceIDTokenOperationCompletion)(FIRInstanceIDTokenOperationResult result, - NSString *_Nullable token, - NSError *_Nullable error); - -@interface FIRInstanceIDTokenOperation : NSOperation - -@property(nonatomic, readonly) FIRInstanceIDTokenAction action; -@property(nonatomic, readonly, nullable) NSString *authorizedEntity; -@property(nonatomic, readonly, nullable) NSString *scope; -@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *options; -@property(nonatomic, readonly, strong) FIRInstanceIDCheckinPreferences *checkinPreferences; -@property(nonatomic, readonly, strong) NSString *instanceID; - -@property(nonatomic, readonly) FIRInstanceIDTokenOperationResult result; - -- (instancetype)init NS_UNAVAILABLE; - -- (void)addCompletionHandler:(FIRInstanceIDTokenOperationCompletion)handler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.m deleted file mode 100644 index aa0f75e07..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.m +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenOperation.h" - -#import <FirebaseInstallations/FirebaseInstallations.h> - -#import "FIRInstanceIDCheckinPreferences.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDURLQueryItem.h" -#import "FIRInstanceIDUtilities.h" -#import "NSError+FIRInstanceID.h" - -static const NSInteger kFIRInstanceIDPlatformVersionIOS = 2; - -static NSString *const kFIRInstanceIDParamInstanceID = @"appid"; -// Scope parameter that defines the service using the token -static NSString *const kFIRInstanceIDParamScope = @"X-scope"; -// Defines the SDK version -static NSString *const kFIRInstanceIDParamFCMLibVersion = @"X-cliv"; - -@interface FIRInstanceIDTokenOperation () { - BOOL _isFinished; - BOOL _isExecuting; -} - -@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *checkinPreferences; -@property(nonatomic, readwrite, strong) NSString *instanceID; - -@property(atomic, strong) NSURLSessionDataTask *dataTask; -@property(readonly, strong) - NSMutableArray<FIRInstanceIDTokenOperationCompletion> *completionHandlers; - -@property(atomic, strong, nullable) NSString *FISAuthToken; - -// For testing only -@property(nonatomic, readwrite, copy) FIRInstanceIDURLRequestTestBlock testBlock; - -@end - -@implementation FIRInstanceIDTokenOperation - -+ (NSURLSession *)sharedURLSession { - static NSURLSession *tokenOperationSharedSession; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - config.timeoutIntervalForResource = 60.0f; // 1 minute - tokenOperationSharedSession = [NSURLSession sessionWithConfiguration:config]; - tokenOperationSharedSession.sessionDescription = @"com.google.iid.tokens.session"; - }); - return tokenOperationSharedSession; -} - -- (instancetype)initWithAction:(FIRInstanceIDTokenAction)action - forAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(NSDictionary<NSString *, NSString *> *)options - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID { - self = [super init]; - if (self) { - _action = action; - _authorizedEntity = [authorizedEntity copy]; - _scope = [scope copy]; - _options = [options copy]; - _checkinPreferences = checkinPreferences; - _instanceID = instanceID; - _completionHandlers = [NSMutableArray array]; - - _isExecuting = NO; - _isFinished = NO; - } - return self; -} - -- (void)dealloc { - _testBlock = nil; - _authorizedEntity = nil; - _scope = nil; - _options = nil; - _checkinPreferences = nil; - _instanceID = nil; - [_completionHandlers removeAllObjects]; - _completionHandlers = nil; -} - -- (void)addCompletionHandler:(FIRInstanceIDTokenOperationCompletion)handler { - [self.completionHandlers addObject:handler]; -} - -- (BOOL)isAsynchronous { - return YES; -} - -- (BOOL)isExecuting { - return _isExecuting; -} - -- (void)setExecuting:(BOOL)executing { - [self willChangeValueForKey:@"isExecuting"]; - _isExecuting = executing; - [self didChangeValueForKey:@"isExecuting"]; -} - -- (BOOL)isFinished { - return _isFinished; -} - -- (void)setFinished:(BOOL)finished { - [self willChangeValueForKey:@"isFinished"]; - _isFinished = finished; - [self didChangeValueForKey:@"isFinished"]; -} - -- (void)start { - if (self.isCancelled) { - [self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil]; - return; - } - - // Quickly validate whether or not the operation has all it needs to begin - BOOL checkinfoAvailable = [self.checkinPreferences hasCheckinInfo]; - if (!checkinfoAvailable) { - FIRInstanceIDErrorCode errorCode = kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn; - [self finishWithResult:FIRInstanceIDTokenOperationError - token:nil - error:[NSError errorWithFIRInstanceIDErrorCode:errorCode]]; - return; - } - - [self setExecuting:YES]; - - [[FIRInstallations installations] - authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult, - NSError *_Nullable error) { - if (tokenResult.authToken.length > 0) { - self.FISAuthToken = tokenResult.authToken; - [self performTokenOperation]; - } else { - [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error]; - } - }]; -} - -- (void)finishWithResult:(FIRInstanceIDTokenOperationResult)result - token:(nullable NSString *)token - error:(nullable NSError *)error { - // Add a check to prevent this finish from being called more than once. - if (self.isFinished) { - return; - } - self.dataTask = nil; - _result = result; - // TODO(chliangGoogle): Call these in the main thread? - for (FIRInstanceIDTokenOperationCompletion completionHandler in self.completionHandlers) { - completionHandler(result, token, error); - } - - [self setExecuting:NO]; - [self setFinished:YES]; -} - -- (void)cancel { - [super cancel]; - [self.dataTask cancel]; - [self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil]; -} - -- (void)performTokenOperation { -} - -- (NSMutableURLRequest *)tokenRequest { - NSString *authHeader = - [FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:self.checkinPreferences]; - return [[self class] requestWithAuthHeader:authHeader FISAuthToken:self.FISAuthToken]; -} - -#pragma mark - Request Construction -+ (NSMutableURLRequest *)requestWithAuthHeader:(NSString *)authHeaderString - FISAuthToken:(NSString *)FISAuthToken { - NSURL *url = [NSURL URLWithString:FIRInstanceIDRegisterServer()]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - - // Add HTTP headers - [request setValue:authHeaderString forHTTPHeaderField:@"Authorization"]; - [request setValue:FIRInstanceIDAppIdentifier() forHTTPHeaderField:@"app"]; - if (FISAuthToken) { - [request setValue:FISAuthToken forHTTPHeaderField:@"x-goog-firebase-installations-auth"]; - } - request.HTTPMethod = @"POST"; - return request; -} - -+ (NSMutableArray<FIRInstanceIDURLQueryItem *> *)standardQueryItemsWithDeviceID:(NSString *)deviceID - scope:(NSString *)scope { - NSMutableArray<FIRInstanceIDURLQueryItem *> *queryItems = [NSMutableArray arrayWithCapacity:8]; - - // E.g. X-osv=10.2.1 - NSString *systemVersion = FIRInstanceIDOperatingSystemVersion(); - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"X-osv" value:systemVersion]]; - // E.g. device= - if (deviceID) { - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"device" value:deviceID]]; - } - // E.g. X-scope=fcm - if (scope) { - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamScope - value:scope]]; - } - // E.g. plat=2 - NSString *platform = [NSString stringWithFormat:@"%ld", (long)kFIRInstanceIDPlatformVersionIOS]; - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"plat" value:platform]]; - // E.g. app=com.myapp.foo - NSString *appIdentifier = FIRInstanceIDAppIdentifier(); - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"app" value:appIdentifier]]; - // E.g. app_ver=1.5 - NSString *appVersion = FIRInstanceIDCurrentAppVersion(); - [queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"app_ver" value:appVersion]]; - // E.g. X-cliv=fiid-1.2.3 - NSString *fcmLibraryVersion = - [NSString stringWithFormat:@"fiid-%@", FIRInstanceIDCurrentGCMVersion()]; - if (fcmLibraryVersion.length) { - FIRInstanceIDURLQueryItem *gcmLibVersion = - [FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamFCMLibVersion - value:fcmLibraryVersion]; - [queryItems addObject:gcmLibVersion]; - } - - return queryItems; -} - -- (NSArray<FIRInstanceIDURLQueryItem *> *)queryItemsWithInstanceID:(NSString *)instanceID { - return @[ [FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamInstanceID - value:instanceID] ]; -} - -#pragma mark - HTTP Header - -+ (NSString *)HTTPAuthHeaderFromCheckin:(FIRInstanceIDCheckinPreferences *)checkin { - NSString *deviceID = checkin.deviceID; - NSString *secret = checkin.secretToken; - return [NSString stringWithFormat:@"AidLogin %@:%@", deviceID, secret]; -} -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.h deleted file mode 100644 index 861c87b99..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -@class FIRInstanceIDAPNSInfo; -@class FIRInstanceIDAuthKeychain; -@class FIRInstanceIDTokenInfo; - -/** - * This class is responsible for retrieving and saving `FIRInstanceIDTokenInfo` objects from the - * keychain. The keychain keys that are used are: - * Account: <Main App Bundle ID> (e.g. com.mycompany.myapp) - * Service: <Sender ID>:<Scope> (e.g. 1234567890:*) - */ -@interface FIRInstanceIDTokenStore : NSObject - -NS_ASSUME_NONNULL_BEGIN - -/** - * Create a default InstanceID token store. Uses a valid Keychain object as it's - * persistent backing store. - * - * @return A valid token store object. - */ -+ (instancetype)defaultStore; - -- (instancetype)init __attribute__((unavailable("Use -initWithKeychain: instead."))); - -/** - * Initialize a token store object with a Keychain object. Used for testing. - * - * @param keychain The Keychain object to use as the backing store for tokens. - * - * @return A valid token store object with the given Keychain as backing store. - */ -- (instancetype)initWithKeychain:(FIRInstanceIDAuthKeychain *)keychain; - -#pragma mark - Get - -/** - * Get the cached token from the Keychain. - * - * @param authorizedEntity The authorized entity for the token. - * @param scope The scope for the token. - * - * @return The cached token info if any for the given authorizedEntity and scope else - * nil. - */ -- (nullable FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope; - -/** - * Return all cached token infos from the Keychain. - * - * @return The cached token infos, if any, that are stored in the Keychain. - */ -- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos; - -#pragma mark - Save - -/** - * Save the instanceID token info to the persistent store. - * - * @param tokenInfo The token info to store. - * @param handler The callback handler which is invoked when token saving is complete, - * with an error if there is any. - */ -- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo - handler:(nullable void (^)(NSError *))handler; - -#pragma mark - Delete - -/** - * Remove the cached token from Keychain. - * - * @param authorizedEntity The authorized entity for the token. - * @param scope The scope for the token. - * - */ -- (void)removeTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope; - -/** - * Remove all the cached tokens from the Keychain. - * @param handler The callback handler which is invoked when tokens deletion is complete, - * with an error if there is any. - * - */ -- (void)removeAllTokensWithHandler:(nullable void (^)(NSError *))handler; - -NS_ASSUME_NONNULL_END - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.m deleted file mode 100644 index f97f9321e..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.m +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDTokenStore.h" - -#import "FIRInstanceIDAuthKeyChain.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDLogger.h" -#import "FIRInstanceIDTokenInfo.h" -#import "FIRInstanceIDUtilities.h" - -static NSString *const kFIRInstanceIDTokenKeychainId = @"com.google.iid-tokens"; - -@interface FIRInstanceIDTokenStore () - -@property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain; - -@end - -@implementation FIRInstanceIDTokenStore - -+ (instancetype)defaultStore { - FIRInstanceIDAuthKeychain *tokenKeychain = - [[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDTokenKeychainId]; - return [[FIRInstanceIDTokenStore alloc] initWithKeychain:tokenKeychain]; -} - -- (instancetype)initWithKeychain:(FIRInstanceIDAuthKeychain *)keychain { - self = [super init]; - if (self) { - _keychain = keychain; - } - return self; -} - -#pragma mark - Get - -+ (NSString *)serviceKeyForAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope { - return [NSString stringWithFormat:@"%@:%@", authorizedEntity, scope]; -} - -- (nullable FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope { - NSString *account = FIRInstanceIDAppIdentifier(); - NSString *service = [[self class] serviceKeyForAuthorizedEntity:authorizedEntity scope:scope]; - NSData *item = [self.keychain dataForService:service account:account]; - if (!item) { - return nil; - } - // Token infos created from legacy storage don't have appVersion, firebaseAppID, or APNSInfo. - FIRInstanceIDTokenInfo *tokenInfo = [[self class] tokenInfoFromKeychainItem:item]; - return tokenInfo; -} - -- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos { - NSString *account = FIRInstanceIDAppIdentifier(); - NSArray<NSData *> *items = - [self.keychain itemsMatchingService:kFIRInstanceIDKeychainWildcardIdentifier account:account]; - NSMutableArray<FIRInstanceIDTokenInfo *> *tokenInfos = - [NSMutableArray arrayWithCapacity:items.count]; - for (NSData *item in items) { - FIRInstanceIDTokenInfo *tokenInfo = [[self class] tokenInfoFromKeychainItem:item]; - if (tokenInfo) { - [tokenInfos addObject:tokenInfo]; - } - } - return tokenInfos; -} - -+ (nullable FIRInstanceIDTokenInfo *)tokenInfoFromKeychainItem:(NSData *)item { - // Check if it is saved as an archived FIRInstanceIDTokenInfo, otherwise return nil. - FIRInstanceIDTokenInfo *tokenInfo = nil; - // NOTE: Passing in nil to unarchiveObjectWithData will result in an iOS error logged - // in the console on iOS 10 and below. Avoid by checking item.data's existence. - if (item) { - // TODO(chliangGoogle: Use the new API and secureCoding protocol. - @try { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - tokenInfo = [NSKeyedUnarchiver unarchiveObjectWithData:item]; -#pragma clang diagnostic pop - - } @catch (NSException *exception) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenStoreExceptionUnarchivingTokenInfo, - @"Unable to parse token info from Keychain item; item was in an " - @"invalid format"); - tokenInfo = nil; - } @finally { - } - } - return tokenInfo; -} - -#pragma mark - Save -// Token Infos will be saved under these Keychain keys: -// Account: <Main App Bundle ID> (e.g. com.mycompany.myapp) -// Service: <Sender ID>:<Scope> (e.g. 1234567890:*) -- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo - handler:(void (^)(NSError *))handler { // Keep the cachetime up-to-date. - tokenInfo.cacheTime = [NSDate date]; - // Always write to the Keychain, so that the cacheTime is up-to-date. - NSData *tokenInfoData; - // TODO(chliangGoogle: Use the new API and secureCoding protocol. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - tokenInfoData = [NSKeyedArchiver archivedDataWithRootObject:tokenInfo]; -#pragma clang diagnostic pop - NSString *account = FIRInstanceIDAppIdentifier(); - NSString *service = [[self class] serviceKeyForAuthorizedEntity:tokenInfo.authorizedEntity - scope:tokenInfo.scope]; - [self.keychain setData:tokenInfoData forService:service account:account handler:handler]; -} - -#pragma mark - Delete - -- (void)removeTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity - scope:(nonnull NSString *)scope { - NSString *account = FIRInstanceIDAppIdentifier(); - NSString *service = [[self class] serviceKeyForAuthorizedEntity:authorizedEntity scope:scope]; - [self.keychain removeItemsMatchingService:service account:account handler:nil]; -} - -- (void)removeAllTokensWithHandler:(void (^)(NSError *error))handler { - NSString *account = FIRInstanceIDAppIdentifier(); - [self.keychain removeItemsMatchingService:kFIRInstanceIDKeychainWildcardIdentifier - account:account - handler:handler]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.h deleted file mode 100644 index 3a3a1d7cd..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -// Stand-in for NSURLQueryItem, which is only available on iOS 8.0 and up. -@interface FIRInstanceIDURLQueryItem : NSObject - -@property(nonatomic, readonly) NSString *name; -@property(nonatomic, readonly) NSString *value; - -+ (instancetype)queryItemWithName:(NSString *)name value:(NSString *)value; -- (instancetype)initWithName:(NSString *)name value:(NSString *)value; - -@end - -/** - * Given an array of query items, construct a URL query. On iOS 8.0 and above, this will use - * NSURLQueryItems internally to perform the string creation, and will be done manually in iOS - * 7 and below. - */ -NSString *FIRInstanceIDQueryFromQueryItems(NSArray<FIRInstanceIDURLQueryItem *> *queryItems); - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.m deleted file mode 100644 index 59b486555..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.m +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDURLQueryItem.h" - -@implementation FIRInstanceIDURLQueryItem - -+ (instancetype)queryItemWithName:(NSString *)name value:(NSString *)value { - return [[[self class] alloc] initWithName:name value:value]; -} - -- (instancetype)initWithName:(NSString *)name value:(NSString *)value { - self = [super init]; - if (self) { - _name = [name copy]; - _value = [value copy]; - } - return self; -} -@end - -NSString *FIRInstanceIDQueryFromQueryItems(NSArray<FIRInstanceIDURLQueryItem *> *queryItems) { - if ([NSURLQueryItem class]) { - // We are iOS 8.0 and above. Convert to NSURLQueryItems and get query that way - // to take advantage of any automatic encoding - NSMutableArray<NSURLQueryItem *> *urlItems = - [NSMutableArray arrayWithCapacity:queryItems.count]; - for (FIRInstanceIDURLQueryItem *queryItem in queryItems) { - [urlItems addObject:[NSURLQueryItem queryItemWithName:queryItem.name value:queryItem.value]]; - } - NSURLComponents *components = [[NSURLComponents alloc] init]; - components.queryItems = urlItems; - return components.query; - } else { - // We are on iOS 7.0. Manually create the query string - NSMutableArray<NSString *> *pairs = [NSMutableArray arrayWithCapacity:queryItems.count]; - for (FIRInstanceIDURLQueryItem *queryItem in queryItems) { - [pairs addObject:[NSString stringWithFormat:@"%@=%@", queryItem.name, queryItem.value]]; - } - return [pairs componentsJoinedByString:@"&"]; - } -} diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.h deleted file mode 100644 index da6ebad33..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -/// FIRMessaging Class that responds to the FIRMessaging SDK version selector. -/// Verify at runtime if the class exists and implements the required method. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDFCMSDKClassString; - -/// locale key stored in GULUserDefaults -FOUNDATION_EXPORT NSString *const kFIRInstanceIDUserDefaultsKeyLocale; - -#pragma mark - Test Blocks - -/** - * Response block for mock registration requests made during tests. - * - * @param data The data as returned by the mock request. - * @param response The response as returned by the mock request. - * @param error The error if any as returned by the mock request. - */ -typedef void (^FIRInstanceIDURLRequestTestResponseBlock)(NSData *data, - NSURLResponse *response, - NSError *error); - -/** - * Test block to mock registration requests response. - * - * @param request The request to mock response for. - * @param response The response block for the mocked request. - */ -typedef void (^FIRInstanceIDURLRequestTestBlock)(NSURLRequest *request, - FIRInstanceIDURLRequestTestResponseBlock response); - -#pragma mark - URL Helpers - -FOUNDATION_EXPORT NSString *FIRInstanceIDRegisterServer(void); - -#pragma mark - Time - -FOUNDATION_EXPORT int64_t FIRInstanceIDCurrentTimestampInSeconds(void); -FOUNDATION_EXPORT int64_t FIRInstanceIDCurrentTimestampInMilliseconds(void); - -#pragma mark - App Info - -FOUNDATION_EXPORT NSString *FIRInstanceIDCurrentAppVersion(void); -FOUNDATION_EXPORT NSString *FIRInstanceIDAppIdentifier(void); -FOUNDATION_EXPORT NSString *FIRInstanceIDFirebaseAppID(void); - -#pragma mark - Device Info - -FOUNDATION_EXPORT NSString *FIRInstanceIDDeviceModel(void); -FOUNDATION_EXPORT NSString *FIRInstanceIDOperatingSystemVersion(void); -FOUNDATION_EXPORT BOOL FIRInstanceIDHasLocaleChanged(void); - -#pragma mark - Helpers - -FOUNDATION_EXPORT BOOL FIRInstanceIDIsValidGCMScope(NSString *scope); -FOUNDATION_EXPORT NSString *FIRInstanceIDStringForAPNSDeviceToken(NSData *deviceToken); -FOUNDATION_EXPORT NSString *FIRInstanceIDAPNSTupleStringForTokenAndServerType(NSData *deviceToken, - BOOL isSandbox); - -#pragma mark - GCM Helpers -/// Returns the current GCM version if GCM library is found else returns nil. -FOUNDATION_EXPORT NSString *FIRInstanceIDCurrentGCMVersion(void); - -/// Returns the current locale. If GCM is present it queries GCM for a -/// Context Manager specific locale. Otherwise, it returns the system's first -/// preferred language (which may be set independently from locale). If the -/// system returns no preferred languages, this method returns the most common -/// language for the user's given locale. Guaranteed to return a nonnull value. -FOUNDATION_EXPORT NSString *FIRInstanceIDCurrentLocale(void); diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.m deleted file mode 100644 index 9eaafa710..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.m +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDUtilities.h" - -#if TARGET_OS_IOS || TARGET_OS_TV -#import <UIKit/UIKit.h> -#endif -#import <sys/utsname.h> - -#import <FirebaseCore/FIROptions.h> -#import <GoogleUtilities/GULUserDefaults.h> -#import "FIRInstanceID.h" -#import "FIRInstanceIDConstants.h" -#import "FIRInstanceIDLogger.h" - -// Convert the macro to a string -#define STR_EXPAND(x) #x -#define STR(x) STR_EXPAND(x) - -static NSString *const kFIRInstanceIDAPNSSandboxPrefix = @"s_"; -static NSString *const kFIRInstanceIDAPNSProdPrefix = @"p_"; - -/// FIRMessaging Class that responds to the FIRMessaging SDK version selector. -/// Verify at runtime if the class exists and implements the required method. -NSString *const kFIRInstanceIDFCMSDKClassString = @"FIRMessaging"; - -/// FIRMessaging selector that returns the current FIRMessaging library version. -static NSString *const kFIRInstanceIDFCMSDKVersionSelectorString = @"FIRMessagingSDKVersion"; - -/// FIRMessaging selector that returns the current device locale. -static NSString *const kFIRInstanceIDFCMSDKLocaleSelectorString = @"FIRMessagingSDKCurrentLocale"; - -NSString *const kFIRInstanceIDUserDefaultsKeyLocale = - @"com.firebase.instanceid.user_defaults.locale"; // locale key stored in GULUserDefaults - -/// Static values which will be populated once retrieved using -/// |FIRInstanceIDRetrieveEnvironmentInfoFromFirebaseCore|. -static NSString *operatingSystemVersion; -static NSString *hardwareDeviceModel; - -#pragma mark - URL Helpers - -NSString *FIRInstanceIDRegisterServer() { - return @"https://fcmtoken.googleapis.com/register"; -} - -#pragma mark - Time - -int64_t FIRInstanceIDCurrentTimestampInSeconds() { - return (int64_t)[[NSDate date] timeIntervalSince1970]; -} - -int64_t FIRInstanceIDCurrentTimestampInMilliseconds() { - return (int64_t)(FIRInstanceIDCurrentTimestampInSeconds() * 1000.0); -} - -#pragma mark - App Info - -NSString *FIRInstanceIDCurrentAppVersion() { - NSString *version = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; - if (![version length]) { - return @""; - } - return version; -} - -NSString *FIRInstanceIDBundleIDByRemovingLastPartFrom(NSString *bundleID) { - NSString *bundleIDComponentsSeparator = @"."; - - NSMutableArray<NSString *> *bundleIDComponents = - [[bundleID componentsSeparatedByString:bundleIDComponentsSeparator] mutableCopy]; - [bundleIDComponents removeLastObject]; - - return [bundleIDComponents componentsJoinedByString:bundleIDComponentsSeparator]; -} - -NSString *FIRInstanceIDAppIdentifier() { - NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; - if (!bundleID.length) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeUtilitiesMissingBundleIdentifier, - @"The mainBundle's bundleIdentifier returned '%@'. Bundle identifier " - @"expected to be non-empty.", - bundleID); - return @""; - } -#if TARGET_OS_WATCH - return FIRInstanceIDBundleIDByRemovingLastPartFrom(bundleID); -#endif - return bundleID; -} - -NSString *FIRInstanceIDFirebaseAppID() { - return [FIROptions defaultOptions].googleAppID; -} - -#pragma mark - Device Info -// Get the device model from Firebase Core's App Environment Util -NSString *FIRInstanceIDDeviceModel() { - static dispatch_once_t once; - dispatch_once(&once, ^{ - struct utsname systemInfo; - if (uname(&systemInfo) == 0) { - hardwareDeviceModel = [NSString stringWithUTF8String:systemInfo.machine]; - } - }); - return hardwareDeviceModel; -} - -// Get the system version from Firebase Core's App Environment Util -NSString *FIRInstanceIDOperatingSystemVersion() { -#if TARGET_OS_IOS || TARGET_OS_TV - return [UIDevice currentDevice].systemVersion; -#elif TARGET_OS_OSX || TARGET_OS_WATCH - return [NSProcessInfo processInfo].operatingSystemVersionString; -#endif -} - -BOOL FIRInstanceIDHasLocaleChanged() { - NSString *lastLocale = - [[GULUserDefaults standardUserDefaults] stringForKey:kFIRInstanceIDUserDefaultsKeyLocale]; - NSString *currentLocale = FIRInstanceIDCurrentLocale(); - if (lastLocale) { - if ([currentLocale isEqualToString:lastLocale]) { - return NO; - } - } - return YES; -} - -#pragma mark - Helpers - -BOOL FIRInstanceIDIsValidGCMScope(NSString *scope) { - return [scope compare:kFIRInstanceIDScopeFirebaseMessaging - options:NSCaseInsensitiveSearch] == NSOrderedSame; -} - -NSString *FIRInstanceIDStringForAPNSDeviceToken(NSData *deviceToken) { - NSMutableString *APNSToken = [NSMutableString string]; - unsigned char *bytes = (unsigned char *)[deviceToken bytes]; - for (int i = 0; i < (int)deviceToken.length; i++) { - [APNSToken appendFormat:@"%02x", bytes[i]]; - } - return APNSToken; -} - -NSString *FIRInstanceIDAPNSTupleStringForTokenAndServerType(NSData *deviceToken, BOOL isSandbox) { - if (deviceToken == nil) { - // A nil deviceToken leads to an invalid tuple string, so return nil. - return nil; - } - NSString *prefix = isSandbox ? kFIRInstanceIDAPNSSandboxPrefix : kFIRInstanceIDAPNSProdPrefix; - NSString *APNSString = FIRInstanceIDStringForAPNSDeviceToken(deviceToken); - NSString *APNSTupleString = [NSString stringWithFormat:@"%@%@", prefix, APNSString]; - - return APNSTupleString; -} - -#pragma mark - GCM Helpers - -NSString *FIRInstanceIDCurrentGCMVersion() { - Class versionClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString); - SEL versionSelector = NSSelectorFromString(kFIRInstanceIDFCMSDKVersionSelectorString); - if ([versionClass respondsToSelector:versionSelector]) { - IMP getVersionIMP = [versionClass methodForSelector:versionSelector]; - NSString *(*getVersion)(id, SEL) = (void *)getVersionIMP; - return getVersion(versionClass, versionSelector); - } - return nil; -} - -NSString *FIRInstanceIDCurrentLocale() { - Class localeClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString); - SEL localeSelector = NSSelectorFromString(kFIRInstanceIDFCMSDKLocaleSelectorString); - - if ([localeClass respondsToSelector:localeSelector]) { - IMP getLocaleIMP = [localeClass methodForSelector:localeSelector]; - NSString *(*getLocale)(id, SEL) = (void *)getLocaleIMP; - NSString *fcmLocale = getLocale(localeClass, localeSelector); - if (fcmLocale != nil) { - return fcmLocale; - } - } - - NSString *systemLanguage = [[NSLocale preferredLanguages] firstObject]; - if (systemLanguage != nil) { - return systemLanguage; - } - - if (@available(macOS 10.12, iOS 10.0, *)) { - return [NSLocale currentLocale].languageCode; - } else { - return nil; - } -} diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.h deleted file mode 100644 index ec5a76c5d..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -/** - * Parsing utility for InstanceID Library versions. InstanceID lib follows semantic versioning. - * This provides utilities to parse the library versions to enable features and do - * updates based on appropriate library versions. - * - * Some example semantic versions are 1.0.1, 2.1.0, 2.1.1, 2.2.0-alpha1, 2.2.1-beta1 - */ - -FOUNDATION_EXPORT NSString *FIRInstanceIDCurrentLibraryVersion(void); -/// Returns the current Major version of GCM library. -FOUNDATION_EXPORT int FIRInstanceIDCurrentLibraryVersionMajor(void); -/// Returns the current Minor version of GCM library. -FOUNDATION_EXPORT int FIRInstanceIDCurrentLibraryVersionMinor(void); -/// Returns the current Patch version of GCM library. -FOUNDATION_EXPORT int FIRInstanceIDCurrentLibraryVersionPatch(void); -/// Returns YES if current library version is `beta` else NO. -FOUNDATION_EXPORT BOOL FIRInstanceIDCurrentLibraryVersionIsBeta(void); diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.m deleted file mode 100644 index c2e532a7b..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.m +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FIRInstanceIDVersionUtilities.h" - -// Convert the macro to a string -#define STR(x) STR_EXPAND(x) -#define STR_EXPAND(x) #x - -static NSString *const kSemanticVersioningSeparator = @"."; -static NSString *const kBetaVersionPrefix = @"-beta"; - -static NSString *libraryVersion; - -static int majorVersion; -static int minorVersion; -static int patchVersion; -static int betaVersion; - -void FIRInstanceIDParseCurrentLibraryVersion() { - static NSArray *allVersions; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSMutableString *daylightVersion = - [NSMutableString stringWithUTF8String:STR(FIRInstanceID_LIB_VERSION)]; - // Parse versions - // major, minor, patch[-beta#] - allVersions = [daylightVersion componentsSeparatedByString:kSemanticVersioningSeparator]; - if (allVersions.count == 3) { - majorVersion = [allVersions[0] intValue]; - minorVersion = [allVersions[1] intValue]; - - // Parse patch and beta versions - NSArray *patchAndBetaVersion = - [allVersions[2] componentsSeparatedByString:kBetaVersionPrefix]; - if (patchAndBetaVersion.count == 2) { - patchVersion = [patchAndBetaVersion[0] intValue]; - betaVersion = [patchAndBetaVersion[1] intValue]; - } else if (patchAndBetaVersion.count == 1) { - patchVersion = [patchAndBetaVersion[0] intValue]; - } - } - - // Copy library version - libraryVersion = [daylightVersion copy]; - }); -} - -NSString *FIRInstanceIDCurrentLibraryVersion() { - FIRInstanceIDParseCurrentLibraryVersion(); - return libraryVersion; -} - -int FIRInstanceIDCurrentLibraryVersionMajor() { - FIRInstanceIDParseCurrentLibraryVersion(); - return majorVersion; -} - -int FIRInstanceIDCurrentLibraryVersionMinor() { - FIRInstanceIDParseCurrentLibraryVersion(); - return minorVersion; -} - -int FIRInstanceIDCurrentLibraryVersionPatch() { - FIRInstanceIDParseCurrentLibraryVersion(); - return patchVersion; -} - -BOOL FIRInstanceIDCurrentLibraryVersionIsBeta() { - FIRInstanceIDParseCurrentLibraryVersion(); - return betaVersion > 0; -} diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.h deleted file mode 100644 index b533dc4a9..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -FOUNDATION_EXPORT NSString *const kFIRInstanceIDDomain; - -typedef NS_ENUM(NSUInteger, FIRInstanceIDErrorCode) { - // Unknown error. - kFIRInstanceIDErrorCodeUnknown = 0, - - // Http related errors. - kFIRInstanceIDErrorCodeAuthentication = 1, - kFIRInstanceIDErrorCodeNoAccess = 2, - kFIRInstanceIDErrorCodeTimeout = 3, - kFIRInstanceIDErrorCodeNetwork = 4, - - // Another operation is in progress. - kFIRInstanceIDErrorCodeOperationInProgress = 5, - - // Failed to perform device check in. - kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn = 6, - - kFIRInstanceIDErrorCodeInvalidRequest = 7, - - // InstanceID generic errors - kFIRInstanceIDErrorCodeMissingDeviceID = 501, - - // InstanceID Token specific errors - kFIRInstanceIDErrorCodeMissingAPNSToken = 1001, - kFIRInstanceIDErrorCodeMissingAPNSServerType = 1002, - kFIRInstanceIDErrorCodeInvalidAuthorizedEntity = 1003, - kFIRInstanceIDErrorCodeInvalidScope = 1004, - kFIRInstanceIDErrorCodeInvalidStart = 1005, - kFIRInstanceIDErrorCodeInvalidKeyPair = 1006, - - // InstanceID Identity specific errors - // Generic InstanceID keypair error - kFIRInstanceIDErrorCodeMissingKeyPair = 2001, - kFIRInstanceIDErrorCodeInvalidKeyPairTags = 2002, - kFIRInstanceIDErrorCodeInvalidKeyPairCreationTime = 2005, - kFIRInstanceIDErrorCodeInvalidIdentity = 2006, - -}; - -@interface NSError (FIRInstanceID) - -@property(nonatomic, readonly) FIRInstanceIDErrorCode instanceIDErrorCode; - -+ (NSError *)errorWithFIRInstanceIDErrorCode:(FIRInstanceIDErrorCode)errorCode; - -+ (NSError *)errorWithFIRInstanceIDErrorCode:(FIRInstanceIDErrorCode)errorCode - userInfo:(NSDictionary *)userInfo; - -+ (NSError *)FIRInstanceIDErrorMissingCheckin; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.m deleted file mode 100644 index 560a5df0e..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.m +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "NSError+FIRInstanceID.h" - -NSString *const kFIRInstanceIDDomain = @"com.firebase.iid"; - -@implementation NSError (FIRInstanceID) - -- (FIRInstanceIDErrorCode)instanceIDErrorCode { - return (FIRInstanceIDErrorCode)self.code; -} - -+ (NSError *)errorWithFIRInstanceIDErrorCode:(FIRInstanceIDErrorCode)errorCode { - return [NSError errorWithFIRInstanceIDErrorCode:errorCode userInfo:nil]; -} - -+ (NSError *)errorWithFIRInstanceIDErrorCode:(FIRInstanceIDErrorCode)errorCode - userInfo:(NSDictionary *)userInfo { - return [NSError errorWithDomain:kFIRInstanceIDDomain code:errorCode userInfo:userInfo]; -} - -+ (NSError *)FIRInstanceIDErrorMissingCheckin { - NSDictionary *userInfo = @{@"msg" : @"Missing device credentials. Retry later."}; - - return [NSError errorWithDomain:kFIRInstanceIDDomain - code:kFIRInstanceIDErrorCodeMissingDeviceID - userInfo:userInfo]; -} - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID+Private.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID+Private.h deleted file mode 100644 index 632e21bc2..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID+Private.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <FirebaseInstanceID/FIRInstanceID.h> -#import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h> - -/** - * @related FIRInstanceIDCheckinService - * - * The completion handler invoked once the fetch from Checkin server finishes. - * For successful fetches we returned checkin information by the checkin service - * and `nil` error, else we return the appropriate error object as reported by the - * Checkin Service. - * - * @param checkinPreferences The checkin preferences as fetched from the server. - * @param error The error object which fetching GServices data. - */ -typedef void (^FIRInstanceIDDeviceCheckinCompletion)( - FIRInstanceIDCheckinPreferences *_Nullable checkinPreferences, NSError *_Nullable error); - -/** - * Private API used by Firebase SDK teams by calling in reflection or internal teams. - */ -@interface FIRInstanceID (Private) - -/** - * Fetches checkin info for the app. If the app has valid cached checkin preferences - * they are returned instead of making a network request. - * - * @param handler The completion handler to invoke once the request has completed. - */ -- (void)fetchCheckinInfoWithHandler:(nullable FIRInstanceIDDeviceCheckinCompletion)handler; - -/** - * Get the InstanceID for the app. If an ID was created before and cached - * successfully we will return that ID. If no cached ID exists we create - * a new ID, cache it and return that. - * - * This is a blocking call and should not really be called on the main thread. - * - * @param error The error object that represents the error while trying to - * retrieve the instance id. - * - * @return The InstanceID for the app. - */ -- (nullable NSString *)appInstanceID:(NSError *_Nullable *_Nullable)error - DEPRECATED_MSG_ATTRIBUTE("Please use getID(handler:) for Swift or " - "getIDWithHandler: for Objective-C instead."); - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h deleted file mode 100644 index be3ec572d..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -/** - * The preferences InstanceID loads from checkin server. The deviceID and secret that checkin - * provides is used to authenticate all future requests to the server. Besides the deviceID - * and secret the other information that checkin provides is stored in a plist on the device. - * The deviceID and secret are persisted in the device keychain. - */ -@interface FIRInstanceIDCheckinPreferences : NSObject - -/** - * DeviceID and secretToken are the checkin auth credentials and are stored in the Keychain. - */ -@property(nonatomic, readonly, copy) NSString *deviceID; -@property(nonatomic, readonly, copy) NSString *secretToken; - -/** - * All the other checkin preferences other than deviceID and secret are stored in a plist. - */ -@property(nonatomic, readonly, copy) NSString *deviceDataVersion; -@property(nonatomic, readonly, copy) NSString *digest; -@property(nonatomic, readonly, copy) NSString *versionInfo; -@property(nonatomic, readonly, assign) int64_t lastCheckinTimestampMillis; - -/** - * The content retrieved from checkin server that should be persisted in a plist. This - * doesn't contain the deviceID and secret which are stored in the Keychain since they - * should be more private. - * - * @return The checkin preferences that should be persisted in a plist. - */ -- (NSDictionary *)checkinPlistContents; - -/** - * Return whether checkin info exists, valid or not. - */ -- (BOOL)hasCheckinInfo; - -/** - * Verify if checkin preferences are valid or not. - * - * @return YES if valid checkin preferences else NO. - */ -- (BOOL)hasValidCheckinInfo; - -@end diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID_Private.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID_Private.h deleted file mode 100644 index c343f88d5..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID_Private.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <FirebaseInstanceID/FIRInstanceID.h> - -NS_ASSUME_NONNULL_BEGIN - -@class FIRInstanceIDCheckinPreferences; -@class FIRInstallations; - -/** - * Private API used by other Firebase SDKs. - */ -@interface FIRInstanceID () - -@property(nonatomic, readonly, strong) NSString *deviceAuthID; -@property(nonatomic, readonly, strong) NSString *secretToken; -@property(nonatomic, readonly, strong) NSString *versionInfo; - -@property(nonatomic, readonly, strong) FIRInstallations *installations; - -/// A cached value of FID. Should be used only for `-[FIRInstanceID appInstanceID:]`. -@property(atomic, readonly, copy, nullable) NSString *firebaseInstallationsID; - -/** - * Private initializer. - */ -- (instancetype)initPrivately; - -/** - * Returns a Firebase Messaging scoped token for the firebase app. - * - * @return Returns the stored token if the device has registered with Firebase Messaging, otherwise - * returns nil. - */ -- (nullable NSString *)token; - -/** - * Verify if valid checkin preferences have been loaded in memory. - * - * @return YES if valid checkin preferences exist in memory else NO. - */ -- (BOOL)hasValidCheckinInfo; - -/** - * Try to load prefetched checkin preferences from the cache. This supports the use case where - * InstanceID library has already obtained a valid checkin and we should be using that. - * - * This should be used as a last gasp effort to retreive any cached checkin preferences before - * hitting the FIRMessaging backend to retrieve new preferences. - * - * Note this is only required because InstanceID and FIRMessaging both require checkin preferences - * which need to be synced with each other. - * - * @return YES if successfully loaded cached checkin preferences into memory else NO. - */ -- (BOOL)tryToLoadValidCheckinInfo; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FIRInstanceID.h b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FIRInstanceID.h deleted file mode 100644 index 0f96d910e..000000000 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FIRInstanceID.h +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -@class FIRInstanceIDResult; -/** - * @memberof FIRInstanceID - * - * The scope to be used when fetching/deleting a token for Firebase Messaging. - */ -FOUNDATION_EXPORT NSString *const kFIRInstanceIDScopeFirebaseMessaging - NS_SWIFT_NAME(InstanceIDScopeFirebaseMessaging); - -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 -/** - * Called when the system determines that tokens need to be refreshed. - * This method is also called if Instance ID has been reset in which - * case, tokens and FCM topic subscriptions also need to be refreshed. - * - * Instance ID service will throttle the refresh event across all devices - * to control the rate of token updates on application servers. - */ -FOUNDATION_EXPORT const NSNotificationName kFIRInstanceIDTokenRefreshNotification - NS_SWIFT_NAME(InstanceIDTokenRefresh); -#else -/** - * Called when the system determines that tokens need to be refreshed. - * This method is also called if Instance ID has been reset in which - * case, tokens and FCM topic subscriptions also need to be refreshed. - * - * Instance ID service will throttle the refresh event across all devices - * to control the rate of token updates on application servers. - */ -FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenRefreshNotification - NS_SWIFT_NAME(InstanceIDTokenRefreshNotification); -#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the InstanceID token returns. If - * the call fails we return the appropriate `error code` as described below. - * - * @param token The valid token as returned by InstanceID backend. - * - * @param error The error describing why generating a new token - * failed. See the error codes below for a more detailed - * description. - */ -typedef void (^FIRInstanceIDTokenHandler)(NSString *__nullable token, NSError *__nullable error) - NS_SWIFT_NAME(InstanceIDTokenHandler); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the InstanceID `deleteToken` returns. If - * the call fails we return the appropriate `error code` as described below - * - * @param error The error describing why deleting the token failed. - * See the error codes below for a more detailed description. - */ -typedef void (^FIRInstanceIDDeleteTokenHandler)(NSError *error) - NS_SWIFT_NAME(InstanceIDDeleteTokenHandler); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the app identity is created. If the - * identity wasn't created for some reason we return the appropriate error code. - * - * @param identity A valid identity for the app instance, nil if there was an error - * while creating an identity. - * @param error The error if fetching the identity fails else nil. - */ -typedef void (^FIRInstanceIDHandler)(NSString *__nullable identity, NSError *__nullable error) - NS_SWIFT_NAME(InstanceIDHandler); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the app identity and all the tokens associated - * with it are deleted. Returns a valid error object in case of failure else nil. - * - * @param error The error if deleting the identity and all the tokens associated with - * it fails else nil. - */ -typedef void (^FIRInstanceIDDeleteHandler)(NSError *__nullable error) - NS_SWIFT_NAME(InstanceIDDeleteHandler); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the app identity and token are fetched. If the - * identity wasn't created for some reason we return the appropriate error code. - * - * @param result The result containing an identity for the app instance and a valid token, - * nil if there was an error while creating the result. - * @param error The error if fetching the identity or token fails else nil. - */ -typedef void (^FIRInstanceIDResultHandler)(FIRInstanceIDResult *__nullable result, - NSError *__nullable error) - NS_SWIFT_NAME(InstanceIDResultHandler); - -/** - * Public errors produced by InstanceID. - */ -typedef NS_ENUM(NSUInteger, FIRInstanceIDError) { - // Http related errors. - - /// Unknown error. - FIRInstanceIDErrorUnknown = 0, - - /// Auth Error -- GCM couldn't validate request from this client. - FIRInstanceIDErrorAuthentication = 1, - - /// NoAccess -- InstanceID service cannot be accessed. - FIRInstanceIDErrorNoAccess = 2, - - /// Timeout -- Request to InstanceID backend timed out. - FIRInstanceIDErrorTimeout = 3, - - /// Network -- No network available to reach the servers. - FIRInstanceIDErrorNetwork = 4, - - /// OperationInProgress -- Another similar operation in progress, - /// bailing this one. - FIRInstanceIDErrorOperationInProgress = 5, - - /// InvalidRequest -- Some parameters of the request were invalid. - FIRInstanceIDErrorInvalidRequest = 7, -} NS_SWIFT_NAME(InstanceIDError); - -/** - * A class contains the results of InstanceID and token query. - */ -NS_SWIFT_NAME(InstanceIDResult) -@interface FIRInstanceIDResult : NSObject <NSCopying> - -/** - * An instanceID uniquely identifies the app instance. - */ -@property(nonatomic, readonly, copy) NSString *instanceID; - -/* - * Returns a Firebase Messaging scoped token for the firebase app. - */ -@property(nonatomic, readonly, copy) NSString *token; - -@end - -/** - * Instance ID provides a unique identifier for each app instance and a mechanism - * to authenticate and authorize actions (for example, sending an FCM message). - * - * Once an InstanceID is generated, the library periodically sends information about the - * application and the device where it's running to the Firebase backend. To stop this. see - * `[FIRInstanceID deleteIDWithHandler:]`. - * - * Instance ID is long lived but, may be reset if the device is not used for - * a long time or the Instance ID service detects a problem. - * If Instance ID is reset, the app will be notified via - * `kFIRInstanceIDTokenRefreshNotification`. - * - * If the Instance ID has become invalid, the app can request a new one and - * send it to the app server. - * To prove ownership of Instance ID and to allow servers to access data or - * services associated with the app, call - * `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. - */ -NS_SWIFT_NAME(InstanceID) -@interface FIRInstanceID : NSObject - -/** - * FIRInstanceID. - * - * @return A shared instance of FIRInstanceID. - */ -+ (instancetype)instanceID NS_SWIFT_NAME(instanceID()); - -/** - * Unavailable. Use +instanceID instead. - */ -- (instancetype)init __attribute__((unavailable("Use +instanceID instead."))); - -#pragma mark - Tokens - -/** - * Returns a result of app instance identifier InstanceID and a Firebase Messaging scoped token. - * param handler The callback handler invoked when an app instanceID and a default token - * are generated and returned. If instanceID and token fetching fail for some - * reason the callback is invoked with nil `result` and the appropriate error. - */ -- (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler; - -/** - * Returns a token that authorizes an Entity (example: cloud service) to perform - * an action on behalf of the application identified by Instance ID. - * - * This is similar to an OAuth2 token except, it applies to the - * application instance instead of a user. - * - * This is an asynchronous call. If the token fetching fails for some reason - * we invoke the completion callback with nil `token` and the appropriate - * error. - * - * This generates an Instance ID if it does not exist yet, which starts periodically sending - * information to the Firebase backend (see `[FIRInstanceID getIDWithHandler:]`). - * - * Note, you can only have one `token` or `deleteToken` call for a given - * authorizedEntity and scope at any point of time. Making another such call with the - * same authorizedEntity and scope before the last one finishes will result in an - * error with code `OperationInProgress`. - * - * @see FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler: - * - * @param authorizedEntity Entity authorized by the token. - * @param scope Action authorized for authorizedEntity. - * @param options The extra options to be sent with your token request. The - * value for the `apns_token` should be the NSData object - * passed to the UIApplicationDelegate's - * `didRegisterForRemoteNotificationsWithDeviceToken` method. - * The value for `apns_sandbox` should be a boolean (or an - * NSNumber representing a BOOL in Objective-C) set to true if - * your app is a debug build, which means that the APNs - * device token is for the sandbox environment. It should be - * set to false otherwise. If the `apns_sandbox` key is not - * provided, an automatically-detected value shall be used. - * @param handler The callback handler which is invoked when the token is - * successfully fetched. In case of success a valid `token` and - * `nil` error are returned. In case of any error the `token` - * is nil and a valid `error` is returned. The valid error - * codes have been documented above. - */ -- (void)tokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - options:(nullable NSDictionary *)options - handler:(FIRInstanceIDTokenHandler)handler; - -/** - * Revokes access to a scope (action) for an entity previously - * authorized by `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. - * - * This is an asynchronous call. Call this on the main thread since InstanceID lib - * is not thread safe. In case token deletion fails for some reason we invoke the - * `handler` callback passed in with the appropriate error code. - * - * Note, you can only have one `token` or `deleteToken` call for a given - * authorizedEntity and scope at a point of time. Making another such call with the - * same authorizedEntity and scope before the last one finishes will result in an error - * with code `OperationInProgress`. - * - * @param authorizedEntity Entity that must no longer have access. - * @param scope Action that entity is no longer authorized to perform. - * @param handler The handler that is invoked once the unsubscribe call ends. - * In case of error an appropriate error object is returned - * else error is nil. - */ -- (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope - handler:(FIRInstanceIDDeleteTokenHandler)handler; - -#pragma mark - Identity - -/** - * Asynchronously fetch a stable identifier that uniquely identifies the app - * instance. If the identifier has been revoked or has expired, this method will - * return a new identifier. - * - * Once an InstanceID is generated, the library periodically sends information about the - * application and the device where it's running to the Firebase backend. To stop this. see - * `[FIRInstanceID deleteIDWithHandler:]`. - * - * @param handler The handler to invoke once the identifier has been fetched. - * In case of error an appropriate error object is returned else - * a valid identifier is returned and a valid identifier for the - * application instance. - */ -- (void)getIDWithHandler:(FIRInstanceIDHandler)handler NS_SWIFT_NAME(getID(handler:)); - -/** - * Resets Instance ID and revokes all tokens. - * - * This method also triggers a request to fetch a new Instance ID and Firebase Messaging scope - * token. Please listen to kFIRInstanceIDTokenRefreshNotification when the new ID and token are - * ready. - * - * This stops the periodic sending of data to the Firebase backend that began when the Instance ID - * was generated. No more data is sent until another library calls Instance ID internally again - * (like FCM, RemoteConfig or Analytics) or user explicitly calls Instance ID APIs to get an - * Instance ID and token again. - */ -- (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler NS_SWIFT_NAME(deleteID(handler:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstanceID/README.md b/ios/Pods/FirebaseInstanceID/README.md deleted file mode 100644 index 3ddc8fbd2..000000000 --- a/ios/Pods/FirebaseInstanceID/README.md +++ /dev/null @@ -1,251 +0,0 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) - -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. - -The repository also includes GoogleUtilities source. The -[GoogleUtilities](GoogleUtilities/README.md) pod is -a set of utilities used by Firebase and other Google products. - -Firebase is an app development platform with tools to help you build, grow and -monetize your app. More information about Firebase can be found at -[https://firebase.google.com](https://firebase.google.com). - -## Installation - -See the three subsections for details about three different installation methods. -1. [Standard pod install](README.md#standard-pod-install) -1. [Installing from the GitHub repo](README.md#installing-from-github) -1. [Experimental Carthage](README.md#carthage-ios-only) - -### Standard pod install - -Go to -[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup). - -### Installing from GitHub - -For releases starting with 5.0.0, the source for each release is also deployed -to CocoaPods master and available via standard -[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod). - -These instructions can be used to access the Firebase repo at other branches, -tags, or commits. - -#### Background - -See -[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod) -for instructions and options about overriding pod source locations. - -#### Accessing Firebase Source Snapshots - -All of the official releases are tagged in this repo and available via CocoaPods. To access a local -source snapshot or unreleased branch, use Podfile directives like the following: - -To access FirebaseFirestore via a branch: -``` -pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master' -pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master' -``` - -To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do: - -``` -pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk' -pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk' -``` - -### Carthage (iOS only) - -Instructions for the experimental Carthage distribution are at -[Carthage](Carthage.md). - -### Rome - -Instructions for installing binary frameworks via -[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). - -## Development - -To develop Firebase software in this repository, ensure that you have at least -the following software: - - * Xcode 10.1 (or later) - * CocoaPods 1.7.2 (or later) - * [CocoaPods generate](https://github.com/square/cocoapods-generate) - -For the pod that you want to develop: - -`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios` - -Note: If the CocoaPods cache is out of date, you may need to run -`pod repo update` before the `pod gen` command. - -Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for -those platforms. Since 10.2, Xcode does not properly handle multi-platform -CocoaPods workspaces. - -Firestore has a self contained Xcode project. See -[Firestore/README.md](Firestore/README.md). - -### Development for Catalyst -* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios` -* Check the Mac box in the App-iOS Build Settings -* Sign the App in the Settings Signing & Capabilities tab -* Click Pods in the Project Manager -* Add Signing to the iOS host app and unit test targets -* Select the Unit-unit scheme -* Run it to build and test - -### Adding a New Firebase Pod - -See [AddNewPod.md](AddNewPod.md). - -### Code Formatting - -To ensure that the code is formatted consistently, run the script -[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh) -before creating a PR. - -Travis will verify that any code changes are done in a style compliant way. Install -`clang-format` and `swiftformat`. -These commands will get the right versions: - -``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb -``` - -Note: if you already have a newer version of these installed you may need to -`brew switch` to this version. - -To update this section, find the versions of clang-format and swiftformat.rb to -match the versions in the CI failure logs -[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula). - -### Running Unit Tests - -Select a scheme and press Command-u to build a component and run its unit tests. - -#### Viewing Code Coverage - -First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. - -After running the `AllUnitTests_iOS` scheme in Xcode, execute -`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output` -at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results. - -### Running Sample Apps -In order to run the sample apps and integration tests, you'll need valid -`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist -files without real values, but can be replaced with real plist files. To get your own -`GoogleService-Info.plist` files: - -1. Go to the [Firebase Console](https://console.firebase.google.com/) -2. Create a new Firebase project, if you don't already have one -3. For each sample app you want to test, create a new Firebase app with the sample app's bundle -identifier (e.g. `com.google.Database-Example`) -4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file -(e.g. in [Example/Database/App/](Example/Database/App/)); - -Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require -special Apple capabilities, and you will have to change the sample app to use a unique bundle -identifier that you can control in your own Apple Developer account. - -## Specific Component Instructions -See the sections below for any special instructions for those components. - -### Firebase Auth - -If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about -building and running the FirebaseAuth pod along with various samples and tests. - -### Firebase Database - -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). - -### Firebase Storage - -To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). - -#### Push Notifications - -Push notifications can only be delivered to specially provisioned App IDs in the developer portal. -In order to actually test receiving push notifications, you will need to: - -1. Change the bundle identifier of the sample app to something you own in your Apple Developer -account, and enable that App ID for push notifications. -2. You'll also need to -[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs) -at **Project Settings > Cloud Messaging > [Your Firebase App]**. -3. Ensure your iOS device is added to your Apple Developer portal as a test device. - -#### iOS Simulator - -The iOS Simulator cannot register for remote notifications, and will not receive push notifications. -In order to receive push notifications, you'll have to follow the steps above and run the app on a -physical device. - -## Community Supported Efforts - -We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are -very grateful! We'd like to empower as many developers as we can to be able to use Firebase and -participate in the Firebase community. - -### tvOS, macOS, and Catalyst -Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore, -FirebaseDatabase, FirebaseMessaging, FirebaseFirestore, -FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. - -For tvOS, checkout the [Sample](Example/tvOSSample). - -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this -repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you -encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). - -To install, add a subset of the following to the Podfile: - -``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' -pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' -pod 'Firebase/Storage' -``` - -#### Additional Catalyst Notes - -* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability` -to Build Settings. -* FirebaseFirestore requires signing the -[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681). - -## Roadmap - -See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source -plans and directions. - -## Contributing - -See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase -iOS SDK. - -## License - -The contents of this repository is licensed under the -[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0). - -Your use of Firebase is governed by the -[Terms of Service for Firebase Services](https://firebase.google.com/terms/). diff --git a/ios/Pods/Flipper-DoubleConversion/LICENSE b/ios/Pods/Flipper-DoubleConversion/LICENSE new file mode 100644 index 000000000..933718a9e --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/LICENSE @@ -0,0 +1,26 @@ +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ios/Pods/Flipper-DoubleConversion/README b/ios/Pods/Flipper-DoubleConversion/README new file mode 100644 index 000000000..167f9c5e9 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/README @@ -0,0 +1,54 @@ +http://code.google.com/p/double-conversion + +This project (double-conversion) provides binary-decimal and decimal-binary +routines for IEEE doubles. + +The library consists of efficient conversion routines that have been extracted +from the V8 JavaScript engine. The code has been refactored and improved so that +it can be used more easily in other projects. + +There is extensive documentation in src/double-conversion.h. Other examples can +be found in test/cctest/test-conversions.cc. + + +Building +======== + +This library can be built with scons [0] or cmake [1]. +The checked-in Makefile simply forwards to scons, and provides a +shortcut to run all tests: + + make + make test + +Scons +----- + +The easiest way to install this library is to use `scons`. It builds +the static and shared library, and is set up to install those at the +correct locations: + + scons install + +Use the `DESTDIR` option to change the target directory: + + scons DESTDIR=alternative_directory install + +Cmake +----- + +To use cmake run `cmake .` in the root directory. This overwrites the +existing Makefile. + +Use `-DBUILD_SHARED_LIBS=ON` to enable the compilation of shared libraries. +Note that this disables static libraries. There is currently no way to +build both libraries at the same time with cmake. + +Use `-DBUILD_TESTING=ON` to build the test executable. + + cmake . -DBUILD_TESTING=ON + make + test/cctest/cctest --list | tr -d '<' | xargs test/cctest/cctest + +[0]: http://www.scons.org +[1]: http://www.cmake.org diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.cc new file mode 100644 index 000000000..f1ad7a5ae --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.cc @@ -0,0 +1,641 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "ieee.h" + +namespace double_conversion { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { + float f = static_cast<float>(v); + ASSERT(f == v); + significand = Single(f).Significand(); + exponent = Single(f).Exponent(); + lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); + } else { + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + } + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + case BIGNUM_DTOA_SHORTEST_SINGLE: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + for (;;) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = static_cast<char>(digit + '0'); + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char> buffer, int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = static_cast<char>(digit + '0'); + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + ASSERT(digit <= 10); + buffer[count - 1] = static_cast<char>(digit + '0'); + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST +// or BIGNUM_DTOA_SHORTEST_SINGLE. + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h new file mode 100644 index 000000000..34b961992 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h @@ -0,0 +1,84 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. + BIGNUM_DTOA_SHORTEST_SINGLE, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.cc new file mode 100644 index 000000000..2743d67e8 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.cc @@ -0,0 +1,766 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "bignum.h" +#include "utils.h" + +namespace double_conversion { + +Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + + +template<typename S> +static int BitSize(S value) { + (void) value; // Mark variable as used. + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + + +static uint64_t ReadUInt64(Vector<const char> buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector<const char> value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + ASSERT('A' <= c && c <= 'F'); + return 10 + c - 'A'; +} + + +void Bignum::AssignHexString(Vector<const char> value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry; + bigits_[i] = static_cast<Chunk>(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if `this` divided by other + // is big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + ASSERT(bigits_[used_digits_ - 1] < 0x10000); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += static_cast<uint16_t>(bigits_[used_digits_ - 1]); + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + ASSERT(quotient < 0x10000); + result += static_cast<uint16_t>(quotient); + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + ASSERT(division_estimate < 0x10000); + result += static_cast<uint16_t>(division_estimate); + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template<typename S> +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return static_cast<char>(value + '0'); + return static_cast<char>(value - 10 + 'A'); +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + Clamp(); +} + + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.h new file mode 100644 index 000000000..5ec3544f5 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/bignum.h @@ -0,0 +1,145 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_H_ + +#include "utils.h" + +namespace double_conversion { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector<const char> value); + void AssignHexString(Vector<const char> value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector<Chunk> bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.cc new file mode 100644 index 000000000..d1359ffe4 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.cc @@ -0,0 +1,176 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdarg.h> +#include <limits.h> +#include <math.h> + +#include "utils.h" + +#include "cached-powers.h" + +namespace double_conversion { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; + +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + (void) max_exponent; // Mark variable as used. + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.h new file mode 100644 index 000000000..61a50614c --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/cached-powers.h @@ -0,0 +1,64 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +class PowersOfTenCache { + public: + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance; + + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.cc new file mode 100644 index 000000000..ddd1891b1 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.cc @@ -0,0 +1,57 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "diy-fp.h" +#include "utils.h" + +namespace double_conversion { + +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.h new file mode 100644 index 000000000..9dcf8fbdb --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/diy-fp.h @@ -0,0 +1,118 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_H_ + +#include "utils.h" + +namespace double_conversion { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.cc new file mode 100644 index 000000000..db3feecb2 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.cc @@ -0,0 +1,910 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <limits.h> +#include <math.h> + +#include "double-conversion.h" + +#include "bignum-dtoa.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "ieee.h" +#include "strtod.h" +#include "utils.h" + +namespace double_conversion { + +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector<char> vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + fast_worked = false; + UNREACHABLE(); + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + + +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +static bool ConsumeSubString(const char** current, + const char* end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; +} + + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + + +// Returns true if a nonspace found and false if the end has reached. +static inline bool AdvanceToNonspace(const char** current, const char* end) { + while (*current != end) { + if (**current != ' ') return true; + ++*current; + } + return false; +} + + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + + +// Returns true if 'c' is a decimal digit that is valid for the given radix. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the last +// condition was always true. By moving it into a separate function the +// compiler wouldn't warn anymore. +static bool IsDecimalDigitForRadix(int c, int radix) { + return '0' <= c && c <= '9' && (c - '0') < radix; +} + +// Returns true if 'c' is a character digit that is valid for the given radix. +// The 'a_character' should be 'a' or 'A'. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the first +// condition was always false. By moving it into a separate function the +// compiler wouldn't warn anymore. +static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { + return radix > 10 && c >= a_character && c < a_character + radix - 10; +} + + +// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +template <int radix_log_2> +static double RadixStringToIeee(const char* current, + const char* end, + bool sign, + bool allow_trailing_junk, + double junk_string_value, + bool read_as_double, + const char** trailing_pointer) { + ASSERT(current != end); + + const int kDoubleSize = Double::kSignificandSize; + const int kSingleSize = Single::kSignificandSize; + const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; + + // Skip leading 0s. + while (*current == '0') { + ++current; + if (current == end) { + *trailing_pointer = end; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (IsDecimalDigitForRadix(*current, radix)) { + digit = static_cast<char>(*current) - '0'; + } else if (IsCharacterDigitForRadix(*current, radix, 'a')) { + digit = static_cast<char>(*current) - 'a' + 10; + } else if (IsCharacterDigitForRadix(*current, radix, 'A')) { + digit = static_cast<char>(*current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast<int>(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast<int>(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + for (;;) { + ++current; + if (current == end || !isDigit(*current, radix)) break; + zero_tail = zero_tail && *current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++current; + } while (current != end); + + ASSERT(number < ((int64_t)1 << kSignificandSize)); + ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number); + + *trailing_pointer = current; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast<double>(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); +} + + +double StringToDoubleConverter::StringToIeee( + const char* input, + int length, + int* processed_characters_count, + bool read_as_double) const { + const char* current = input; + const char* end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = static_cast<int>(current - input); + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // The longest form of simplified number is: "-<significant digits>.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + const char* next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + const char* tail_pointer = NULL; + double result = RadixStringToIeee<4>(current, + end, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + if (tail_pointer != NULL) { + if (allow_trailing_spaces) AdvanceToNonspace(&tail_pointer, end); + *processed_characters_count = static_cast<int>(tail_pointer - input); + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast<char>(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + const char* tail_pointer = NULL; + result = RadixStringToIeee<3>(buffer, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + ASSERT(tail_pointer != NULL); + *processed_characters_count = static_cast<int>(current - input); + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted; + if (read_as_double) { + converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); + } else { + converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent); + } + *processed_characters_count = static_cast<int>(current - input); + return sign? -converted: converted; +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.h new file mode 100644 index 000000000..1c3387d4f --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/double-conversion.h @@ -0,0 +1,536 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +#include "utils.h" + +namespace double_conversion { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or + // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' + // after it has been casted to a single-precision float. That is, in this + // mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading spaces. + // - ALLOW_TRAILING_SPACES: ignore trailing spaces. + // - ALLOW_SPACES_AFTER_SIGN: ignore spaces after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, processed_characters_count, true); + } + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast<float>(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) const { + return static_cast<float>(StringToIeee(buffer, length, + processed_characters_count, false)); + } + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + double StringToIeee(const char* buffer, + int length, + int* processed_characters_count, + bool read_as_double) const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.cc new file mode 100644 index 000000000..61350383a --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.cc @@ -0,0 +1,665 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "ieee.h" + +namespace double_conversion { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector<char> buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector<char> buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// +// Returns power == 10^(exponent_plus_one-1) such that +// power <= number < power * 10. +// If number_bits == 0 then 0^(0-1) is returned. +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). + +// Inspired by the method for finding an integer log base 10 from here: +// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + ASSERT(number < (1u << (number_bits + 1))); + // 1233/4096 is approximately 1/lg(10). + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + // We increment to skip over the first entry in the kPowersOf10 table. + // Note: kPowersOf10[i] == 10^(i-1). + exponent_plus_one_guess++; + // We don't have any guarantees that 2^number_bits <= number. + if (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast<uint64_t>(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + for (;;) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast<uint64_t>(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + FastDtoaMode mode, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + if (mode == FAST_DTOA_SHORTEST) { + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } else { + ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + float single_v = static_cast<float>(v); + Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + case FAST_DTOA_SHORTEST_SINGLE: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.h new file mode 100644 index 000000000..5f1e8eee5 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/fast-dtoa.h @@ -0,0 +1,88 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Same as FAST_DTOA_SHORTEST but for single-precision floats. + FAST_DTOA_SHORTEST_SINGLE, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; +// Same for single-precision numbers. +static const int kFastDtoaMaximalSingleLength = 9; + +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.cc new file mode 100644 index 000000000..aef65fdc2 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.cc @@ -0,0 +1,404 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "fixed-dtoa.h" +#include "ieee.h" + +namespace double_conversion { + +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast<int>(high_bits_ >> (power - 64)); + high_bits_ -= static_cast<uint64_t>(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast<int>(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast<int>(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast<int>(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + +static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector<char> buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + + +static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = static_cast<char>('0' + digit); + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + + +static void FillDigits64FixedLength(uint64_t number, + Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + + +static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + + +// The given fractionals number represents a fixed-point number with binary +// point at bit (-exponent). +// Preconditions: +// -128 <= exponent <= 0. +// 0 <= fractionals * 2^exponent < 1 +// The buffer holds the result. +// The function will round its result. During the rounding-process digits not +// generated by this function might be updated, and the decimal-point variable +// might be updated. If this function generates the digits 99 and the buffer +// already contained "199" (thus yielding a buffer of "19999") then a +// rounding-up will change the contents of the buffer to "20000". +static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector<char> buffer, + int* length, int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast<int>(fractionals >> point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals -= static_cast<uint64_t>(digit) << point; + } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + + +bool FastFixedDtoa(double v, + int fractional_count, + Vector<char> buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast<uint32_t>(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h new file mode 100644 index 000000000..3bdd08e21 --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h @@ -0,0 +1,56 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ +#define DOUBLE_CONVERSION_FIXED_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +// Produces digits necessary to print a given number with +// 'fractional_count' digits after the decimal point. +// The buffer must be big enough to hold the result plus one terminating null +// character. +// +// The produced digits might be too short in which case the caller has to fill +// the gaps with '0's. +// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and +// decimal_point = -2. +// Halfway cases are rounded towards +/-Infinity (away from 0). The call +// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. +// The returned buffer may contain digits that would be truncated from the +// shortest representation of the input. +// +// This method only works for some parameters. If it can't handle the input it +// returns false. The output is null-terminated when the function succeeds. +bool FastFixedDtoa(double v, int fractional_count, + Vector<char> buffer, int* length, int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/ieee.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/ieee.h new file mode 100644 index 000000000..661141d1a --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/ieee.h @@ -0,0 +1,402 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } +static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } +static uint32_t float_to_uint32(float f) { return BitCast<uint32_t>(f); } +static float uint32_to_float(uint32_t d32) { return BitCast<float>(d32); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast<uint64_t>(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } + + DISALLOW_COPY_AND_ASSIGN(Double); +}; + +class Single { + public: + static const uint32_t kSignMask = 0x80000000; + static const uint32_t kExponentMask = 0x7F800000; + static const uint32_t kSignificandMask = 0x007FFFFF; + static const uint32_t kHiddenBit = 0x00800000; + static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. + static const int kSignificandSize = 24; + + Single() : d32_(0) {} + explicit Single(float f) : d32_(float_to_uint32(f)) {} + explicit Single(uint32_t d32) : d32_(d32) {} + + // The value encoded by this Single must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // Returns the single's bit as uint64. + uint32_t AsUint32() const { + return d32_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint32_t d32 = AsUint32(); + int biased_e = + static_cast<int>((d32 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint32_t Significand() const { + uint32_t d32 = AsUint32(); + uint32_t significand = d32 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the single is a denormal. + bool IsDenormal() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) == 0); + } + + int Sign() const { + uint32_t d32 = AsUint32(); + return (d32 & kSignMask) == 0? 1: -1; + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Single must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + // Precondition: the value encoded by this Single must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + float value() const { return uint32_to_float(d32_); } + + static float Infinity() { + return Single(kInfinity).value(); + } + + static float NaN() { + return Single(kNaN).value(); + } + + private: + static const int kExponentBias = 0x7F + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0xFF - kExponentBias; + static const uint32_t kInfinity = 0x7F800000; + static const uint32_t kNaN = 0x7FC00000; + + const uint32_t d32_; + + DISALLOW_COPY_AND_ASSIGN(Single); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.cc b/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.cc new file mode 100644 index 000000000..17abcbb2a --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.cc @@ -0,0 +1,555 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdarg.h> +#include <limits.h> + +#include "strtod.h" +#include "bignum.h" +#include "cached-powers.h" +#include "ieee.h" + +namespace double_conversion { + +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 +}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector<const char> buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + + +// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. +// If possible the input-buffer is reused, but if the buffer needs to be +// modified (due to cutting), then the input needs to be copied into the +// buffer_copy_space. +static void TrimAndCut(Vector<const char> buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector<const char>* trimmed, int* updated_exponent) { + Vector<const char> left_trimmed = TrimLeadingZeros(buffer); + Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + (void) space_size; // Mark variable as used. + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector<const char>(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector<const char> buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector<const char> buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + + +static bool DoubleStrtod(Vector<const char> trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + } +} + + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector<const char> buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + uint64_t error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + + +// Returns +// - -1 if buffer*10^exponent < diy_fp. +// - 0 if buffer*10^exponent == diy_fp. +// - +1 if buffer*10^exponent > diy_fp. +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static int CompareBufferWithDiyFp(Vector<const char> buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + + +// Returns true if the guess is the correct double. +// Returns false, when guess is either correct or the next-lower double. +static bool ComputeGuess(Vector<const char> trimmed, int exponent, + double* guess) { + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DoubleStrtod(trimmed, exponent, guess) || + DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector<const char> buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector<const char> trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +float Strtof(Vector<const char> buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector<const char> trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = static_cast<float>(double_guess); + if (float_guess == double_guess) { + // This shortcut triggers for integer values. + return float_guess; + } + + // We must catch double-rounding. Say the double has been rounded up, and is + // now a boundary of a float, and rounds up again. This is why we have to + // look at previous too. + // Example (in decimal numbers): + // input: 12349 + // high-precision (4 digits): 1235 + // low-precision (3 digits): + // when read from input: 123 + // when rounded from high precision: 124. + // To do this we simply look at the neigbors of the correct result and see + // if they would round to the same float. If the guess is not correct we have + // to look at four values (since two different doubles could be the correct + // double). + + double double_next = Double(double_guess).NextDouble(); + double double_previous = Double(double_guess).PreviousDouble(); + + float f1 = static_cast<float>(double_previous); + float f2 = float_guess; + float f3 = static_cast<float>(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast<float>(double_next2); + } + (void) f2; // Mark variable as used. + ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + + // If the guess doesn't lie near a single-precision boundary we can simply + // return its float-value. + if (f1 == f4) { + return float_guess; + } + + ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + (f1 == f2 && f2 != f3 && f3 == f4) || + (f1 == f2 && f2 == f3 && f3 != f4)); + + // guess and next are the two possible canditates (in the same way that + // double_guess was the lower candidate for a double-precision guess). + float guess = f1; + float next = f4; + DiyFp upper_boundary; + if (guess == 0.0f) { + float min_float = 1e-45f; + upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp(); + } else { + upper_boundary = Single(guess).UpperBoundary(); + } + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return next; + } else if ((Single(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return next; + } +} + +} // namespace double_conversion diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.h new file mode 100644 index 000000000..ed0293b8f --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/strtod.h @@ -0,0 +1,45 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +#include "utils.h" + +namespace double_conversion { + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector<const char> buffer, int exponent); + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +float Strtof(Vector<const char> buffer, int exponent); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/ios/Pods/Flipper-DoubleConversion/double-conversion/utils.h b/ios/Pods/Flipper-DoubleConversion/double-conversion/utils.h new file mode 100644 index 000000000..a7c9b429a --- /dev/null +++ b/ios/Pods/Flipper-DoubleConversion/double-conversion/utils.h @@ -0,0 +1,324 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +#include <stdlib.h> +#include <string.h> + +#include <assert.h> +#ifndef ASSERT +#define ASSERT(condition) \ + assert(condition); +#endif +#ifndef UNIMPLEMENTED +#define UNIMPLEMENTED() (abort()) +#endif +#ifndef UNREACHABLE +#define UNREACHABLE() (abort()) +#endif + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +#if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || \ + defined(__hppa__) || defined(__ia64__) || \ + defined(__mips__) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ + defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ + defined(__SH4__) || defined(__alpha__) || \ + defined(_MIPS_ARCH_MIPS32R2) || \ + defined(__AARCH64EL__) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(__mc68000__) +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif + +#if defined(__GNUC__) +#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) +#else +#define DOUBLE_CONVERSION_UNUSED +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include <stdint.h> + +#endif + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write UINT64_2PART_C(0x12345678,90123456); +#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u)) + + +// The expression ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use ARRAY_SIZE on statically allocated +// arrays. +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) +#endif + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +namespace double_conversion { + +static const int kCharSize = sizeof(char); + +// Returns the maximum of the two parameters. +template <typename T> +static T Max(T a, T b) { + return a < b ? b : a; +} + + +// Returns the minimum of the two parameters. +template <typename T> +static T Min(T a, T b) { + return a < b ? a : b; +} + + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast<size_t>(static_cast<int>(length))); + return static_cast<int>(length); +} + +// This is a simplified version of V8's Vector class. +template <typename T> +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector<T> SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector<T>(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT(static_cast<size_t>(n) <= strlen(s)); + memmove(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector<char> buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template <class Dest, class Source> +inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + DOUBLE_CONVERSION_UNUSED + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template <class Dest, class Source> +inline Dest BitCast(Source* source) { + return BitCast<Dest>(reinterpret_cast<uintptr_t>(source)); +} + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/ios/Pods/Flipper-Folly/LICENSE b/ios/Pods/Flipper-Folly/LICENSE new file mode 100644 index 000000000..48bdb1282 --- /dev/null +++ b/ios/Pods/Flipper-Folly/LICENSE @@ -0,0 +1,200 @@ + + 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 + + +Files in folly/external/farmhash licensed as follows + + Copyright (c) 2014 Google, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/ios/Pods/Flipper-Folly/README.md b/ios/Pods/Flipper-Folly/README.md new file mode 100644 index 000000000..f50d687b9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/README.md @@ -0,0 +1,267 @@ +Folly: Facebook Open-source Library +----------------------------------- + +[![Build Status](https://travis-ci.org/facebook/folly.svg?branch=master)](https://travis-ci.org/facebook/folly) + +### What is `folly`? + +Folly (acronymed loosely after Facebook Open Source Library) is a +library of C++14 components designed with practicality and efficiency +in mind. **Folly contains a variety of core library components used extensively +at Facebook**. In particular, it's often a dependency of Facebook's other +open source C++ efforts and place where those projects can share code. + +It complements (as opposed to competing against) offerings +such as Boost and of course `std`. In fact, we embark on defining our +own component only when something we need is either not available, or +does not meet the needed performance profile. We endeavor to remove +things from folly if or when `std` or Boost obsoletes them. + +Performance concerns permeate much of Folly, sometimes leading to +designs that are more idiosyncratic than they would otherwise be (see +e.g. `PackedSyncPtr.h`, `SmallLocks.h`). Good performance at large +scale is a unifying theme in all of Folly. + +### Logical Design + +Folly is a collection of relatively independent components, some as +simple as a few symbols. There is no restriction on internal +dependencies, meaning that a given folly module may use any other +folly components. + +All symbols are defined in the top-level namespace `folly`, except of +course macros. Macro names are ALL_UPPERCASE and should be prefixed +with `FOLLY_`. Namespace `folly` defines other internal namespaces +such as `internal` or `detail`. User code should not depend on symbols +in those namespaces. + +Folly has an `experimental` directory as well. This designation connotes +primarily that we feel the API may change heavily over time. This code, +typically, is still in heavy use and is well tested. + +### Physical Design + +At the top level Folly uses the classic "stuttering" scheme +`folly/folly` used by Boost and others. The first directory serves as +an installation root of the library (with possible versioning a la +`folly-1.0/`), and the second is to distinguish the library when +including files, e.g. `#include <folly/FBString.h>`. + +The directory structure is flat (mimicking the namespace structure), +i.e. we don't have an elaborate directory hierarchy (it is possible +this will change in future versions). The subdirectory `experimental` +contains files that are used inside folly and possibly at Facebook but +not considered stable enough for client use. Your code should not use +files in `folly/experimental` lest it may break when you update Folly. + +The `folly/folly/test` subdirectory includes the unittests for all +components, usually named `ComponentXyzTest.cpp` for each +`ComponentXyz.*`. The `folly/folly/docs` directory contains +documentation. + +### What's in it? + +Because of folly's fairly flat structure, the best way to see what's in it +is to look at the headers in [top level `folly/` directory](https://github.com/facebook/folly/tree/master/folly). You can also +check the [`docs` folder](folly/docs) for documentation, starting with the +[overview](folly/docs/Overview.md). + +Folly is published on GitHub at https://github.com/facebook/folly + +### Build Notes + +#### Dependencies + +folly requires gcc 5.1+ and a version of boost compiled with C++14 support. + +googletest is required to build and run folly's tests. You can download +it from https://github.com/google/googletest/archive/release-1.8.0.tar.gz +The following commands can be used to download and install it: + +``` +wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz && \ +tar zxf release-1.8.0.tar.gz && \ +rm -f release-1.8.0.tar.gz && \ +cd googletest-release-1.8.0 && \ +cmake . && \ +make && \ +make install +``` + +#### Finding dependencies in non-default locations + +If you have boost, gtest, or other dependencies installed in a non-default +location, you can use the `CMAKE_INCLUDE_PATH` and `CMAKE_LIBRARY_PATH` +variables to make CMAKE look also look for header files and libraries in +non-standard locations. For example, to also search the directories +`/alt/include/path1` and `/alt/include/path2` for header files and the +directories `/alt/lib/path1` and `/alt/lib/path2` for libraries, you can invoke +`cmake` as follows: + +``` +cmake \ + -DCMAKE_INCLUDE_PATH=/alt/include/path1:/alt/include/path2 \ + -DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ... +``` + +#### Building tests + +By default, building the tests is disabled as part of the CMake `all` target. +To build the tests, specify `-DBUILD_TESTS=ON` to CMake at configure time. + +#### Ubuntu 16.04 LTS + +The following packages are required (feel free to cut and paste the apt-get +command below): + +``` +sudo apt-get install \ + g++ \ + cmake \ + libboost-all-dev \ + libevent-dev \ + libdouble-conversion-dev \ + libgoogle-glog-dev \ + libgflags-dev \ + libiberty-dev \ + liblz4-dev \ + liblzma-dev \ + libsnappy-dev \ + make \ + zlib1g-dev \ + binutils-dev \ + libjemalloc-dev \ + libssl-dev \ + pkg-config \ + libunwind-dev +``` + +Folly relies on [fmt](https://github.com/fmtlib/fmt) which needs to be installed from source. +The following commands will download, compile, and install fmt. + +``` +git clone https://github.com/fmtlib/fmt.git && cd fmt + +mkdir _build && cd _build +cmake .. + +make -j$(nproc) +sudo make install +``` + +If advanced debugging functionality is required, use: + +``` +sudo apt-get install \ + libunwind8-dev \ + libelf-dev \ + libdwarf-dev +``` + +In the folly directory (e.g. the checkout root or the archive unpack root), run: +``` + mkdir _build && cd _build + cmake .. + make -j $(nproc) + make install # with either sudo or DESTDIR as necessary +``` + +#### OS X (Homebrew) + +folly is available as a Formula and releases may be built via `brew install folly`. + +You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `master`: + +``` + ./folly/build/bootstrap-osx-homebrew.sh +``` + +This will create a build directory `_build` in the top-level. + +#### OS X (MacPorts) + +Install the required packages from MacPorts: + +``` + sudo port install \ + boost \ + cmake \ + gflags \ + git \ + google-glog \ + libevent \ + libtool \ + lz4 \ + lzma \ + openssl \ + snappy \ + xz \ + zlib +``` + +Download and install double-conversion: + +``` + git clone https://github.com/google/double-conversion.git + cd double-conversion + cmake -DBUILD_SHARED_LIBS=ON . + make + sudo make install +``` + +Download and install folly with the parameters listed below: + +``` + git clone https://github.com/facebook/folly.git + cd folly + mkdir _build + cd _build + cmake .. + make + sudo make install +``` + +#### Windows (Vcpkg) + +folly is available in [Vcpkg](https://github.com/Microsoft/vcpkg#vcpkg) and releases may be built via `vcpkg install folly:x64-windows`. + +You may also use `vcpkg install folly:x64-windows --head` to build against `master`. + +#### Other Linux distributions + +- double-conversion (https://github.com/google/double-conversion) + + Download and build double-conversion. + You may need to tell cmake where to find it. + + [double-conversion/] `ln -s src double-conversion` + + [folly/] `mkdir build && cd build` + [folly/build/] `cmake "-DCMAKE_INCLUDE_PATH=$DOUBLE_CONVERSION_HOME/include" "-DCMAKE_LIBRARY_PATH=$DOUBLE_CONVERSION_HOME/lib" ..` + + [folly/build/] `make` + +- additional platform specific dependencies: + + Fedora >= 21 64-bit (last tested on Fedora 28 64-bit) + - gcc + - gcc-c++ + - cmake + - automake + - boost-devel + - libtool + - lz4-devel + - lzma-devel + - snappy-devel + - zlib-devel + - glog-devel + - gflags-devel + - scons + - double-conversion-devel + - openssl-devel + - libevent-devel + + Optional + - libdwarf-dev + - libelf-dev + - libunwind8-dev diff --git a/ios/Pods/Flipper-Folly/folly/AtomicHashArray-inl.h b/ios/Pods/Flipper-Folly/folly/AtomicHashArray-inl.h new file mode 100644 index 000000000..d05801160 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicHashArray-inl.h @@ -0,0 +1,550 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_ATOMICHASHARRAY_H_ +#error "This should only be included by AtomicHashArray.h" +#endif + +#include <type_traits> + +#include <folly/detail/AtomicHashUtils.h> +#include <folly/detail/Iterators.h> +#include <folly/lang/Bits.h> +#include <folly/lang/Exception.h> + +namespace folly { + +// AtomicHashArray private constructor -- +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>:: + AtomicHashArray( + size_t capacity, + KeyT emptyKey, + KeyT lockedKey, + KeyT erasedKey, + double _maxLoadFactor, + uint32_t cacheSize) + : capacity_(capacity), + maxEntries_(size_t(_maxLoadFactor * capacity_ + 0.5)), + kEmptyKey_(emptyKey), + kLockedKey_(lockedKey), + kErasedKey_(erasedKey), + kAnchorMask_(nextPowTwo(capacity_) - 1), + numEntries_(0, cacheSize), + numPendingEntries_(0, cacheSize), + isFull_(0), + numErases_(0) { + if (capacity == 0) { + throw_exception<std::invalid_argument>("capacity"); + } +} + +/* + * findInternal -- + * + * Sets ret.second to value found and ret.index to index + * of key and returns true, or if key does not exist returns false and + * ret.index is set to capacity_. + */ +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +template <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn> +typename AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::findInternal(const LookupKeyT key_in) { + checkLegalKeyIfKey<LookupKeyT>(key_in); + + for (size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in), + numProbes = 0; + ; + idx = ProbeFcn()(idx, numProbes, capacity_)) { + const KeyT key = acquireLoadKey(cells_[idx]); + if (LIKELY(LookupEqualFcn()(key, key_in))) { + return SimpleRetT(idx, true); + } + if (UNLIKELY(key == kEmptyKey_)) { + // if we hit an empty element, this key does not exist + return SimpleRetT(capacity_, false); + } + // NOTE: the way we count numProbes must be same in find(), insert(), + // and erase(). Otherwise it may break probing. + ++numProbes; + if (UNLIKELY(numProbes >= capacity_)) { + // probed every cell...fail + return SimpleRetT(capacity_, false); + } + } +} + +/* + * insertInternal -- + * + * Returns false on failure due to key collision or full. + * Also sets ret.index to the index of the key. If the map is full, sets + * ret.index = capacity_. Also sets ret.second to cell value, thus if insert + * successful this will be what we just inserted, if there is a key collision + * this will be the previously inserted value, and if the map is full it is + * default. + */ +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +template < + typename LookupKeyT, + typename LookupHashFcn, + typename LookupEqualFcn, + typename LookupKeyToKeyFcn, + typename... ArgTs> +typename AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::insertInternal(LookupKeyT key_in, ArgTs&&... vCtorArgs) { + const short NO_NEW_INSERTS = 1; + const short NO_PENDING_INSERTS = 2; + checkLegalKeyIfKey<LookupKeyT>(key_in); + + size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in); + size_t numProbes = 0; + for (;;) { + DCHECK_LT(idx, capacity_); + value_type* cell = &cells_[idx]; + if (relaxedLoadKey(*cell) == kEmptyKey_) { + // NOTE: isFull_ is set based on numEntries_.readFast(), so it's + // possible to insert more than maxEntries_ entries. However, it's not + // possible to insert past capacity_. + ++numPendingEntries_; + if (isFull_.load(std::memory_order_acquire)) { + --numPendingEntries_; + + // Before deciding whether this insert succeeded, this thread needs to + // wait until no other thread can add a new entry. + + // Correctness assumes isFull_ is true at this point. If + // another thread now does ++numPendingEntries_, we expect it + // to pass the isFull_.load() test above. (It shouldn't insert + // a new entry.) + detail::atomic_hash_spin_wait([&] { + return (isFull_.load(std::memory_order_acquire) != + NO_PENDING_INSERTS) && + (numPendingEntries_.readFull() != 0); + }); + isFull_.store(NO_PENDING_INSERTS, std::memory_order_release); + + if (relaxedLoadKey(*cell) == kEmptyKey_) { + // Don't insert past max load factor + return SimpleRetT(capacity_, false); + } + } else { + // An unallocated cell. Try once to lock it. If we succeed, insert here. + // If we fail, fall through to comparison below; maybe the insert that + // just beat us was for this very key.... + if (tryLockCell(cell)) { + KeyT key_new; + // Write the value - done before unlocking + try { + key_new = LookupKeyToKeyFcn()(key_in); + typedef + typename std::remove_const<LookupKeyT>::type LookupKeyTNoConst; + constexpr bool kAlreadyChecked = + std::is_same<KeyT, LookupKeyTNoConst>::value; + if (!kAlreadyChecked) { + checkLegalKeyIfKey(key_new); + } + DCHECK(relaxedLoadKey(*cell) == kLockedKey_); + // A const mapped_type is only constant once constructed, so cast + // away any const for the placement new here. + using mapped = typename std::remove_const<mapped_type>::type; + new (const_cast<mapped*>(&cell->second)) + ValueT(std::forward<ArgTs>(vCtorArgs)...); + unlockCell(cell, key_new); // Sets the new key + } catch (...) { + // Transition back to empty key---requires handling + // locked->empty below. + unlockCell(cell, kEmptyKey_); + --numPendingEntries_; + throw; + } + // An erase() can race here and delete right after our insertion + // Direct comparison rather than EqualFcn ok here + // (we just inserted it) + DCHECK( + relaxedLoadKey(*cell) == key_new || + relaxedLoadKey(*cell) == kErasedKey_); + --numPendingEntries_; + ++numEntries_; // This is a thread cached atomic increment :) + if (numEntries_.readFast() >= maxEntries_) { + isFull_.store(NO_NEW_INSERTS, std::memory_order_relaxed); + } + return SimpleRetT(idx, true); + } + --numPendingEntries_; + } + } + DCHECK(relaxedLoadKey(*cell) != kEmptyKey_); + if (kLockedKey_ == acquireLoadKey(*cell)) { + detail::atomic_hash_spin_wait( + [&] { return kLockedKey_ == acquireLoadKey(*cell); }); + } + + const KeyT thisKey = acquireLoadKey(*cell); + if (LookupEqualFcn()(thisKey, key_in)) { + // Found an existing entry for our key, but we don't overwrite the + // previous value. + return SimpleRetT(idx, false); + } else if (thisKey == kEmptyKey_ || thisKey == kLockedKey_) { + // We need to try again (i.e., don't increment numProbes or + // advance idx): this case can happen if the constructor for + // ValueT threw for this very cell (the rethrow block above). + continue; + } + + // NOTE: the way we count numProbes must be same in find(), + // insert(), and erase(). Otherwise it may break probing. + ++numProbes; + if (UNLIKELY(numProbes >= capacity_)) { + // probed every cell...fail + return SimpleRetT(capacity_, false); + } + + idx = ProbeFcn()(idx, numProbes, capacity_); + } +} + +/* + * erase -- + * + * This will attempt to erase the given key key_in if the key is found. It + * returns 1 iff the key was located and marked as erased, and 0 otherwise. + * + * Memory is not freed or reclaimed by erase, i.e. the cell containing the + * erased key will never be reused. If there's an associated value, we won't + * touch it either. + */ +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +size_t AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::erase(KeyT key_in) { + CHECK_NE(key_in, kEmptyKey_); + CHECK_NE(key_in, kLockedKey_); + CHECK_NE(key_in, kErasedKey_); + + for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0;; + idx = ProbeFcn()(idx, numProbes, capacity_)) { + DCHECK_LT(idx, capacity_); + value_type* cell = &cells_[idx]; + KeyT currentKey = acquireLoadKey(*cell); + if (currentKey == kEmptyKey_ || currentKey == kLockedKey_) { + // If we hit an empty (or locked) element, this key does not exist. This + // is similar to how it's handled in find(). + return 0; + } + if (EqualFcn()(currentKey, key_in)) { + // Found an existing entry for our key, attempt to mark it erased. + // Some other thread may have erased our key, but this is ok. + KeyT expect = currentKey; + if (cellKeyPtr(*cell)->compare_exchange_strong(expect, kErasedKey_)) { + numErases_.fetch_add(1, std::memory_order_relaxed); + + // Even if there's a value in the cell, we won't delete (or even + // default construct) it because some other thread may be accessing it. + // Locking it meanwhile won't work either since another thread may be + // holding a pointer to it. + + // We found the key and successfully erased it. + return 1; + } + // If another thread succeeds in erasing our key, we'll stop our search. + return 0; + } + + // NOTE: the way we count numProbes must be same in find(), insert(), + // and erase(). Otherwise it may break probing. + ++numProbes; + if (UNLIKELY(numProbes >= capacity_)) { + // probed every cell...fail + return 0; + } + } +} + +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +typename AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SmartPtr +AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::create(size_t maxSize, const Config& c) { + CHECK_LE(c.maxLoadFactor, 1.0); + CHECK_GT(c.maxLoadFactor, 0.0); + CHECK_NE(c.emptyKey, c.lockedKey); + size_t capacity = size_t(maxSize / c.maxLoadFactor); + size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * capacity; + + auto const mem = Allocator().allocate(sz); + try { + new (mem) AtomicHashArray( + capacity, + c.emptyKey, + c.lockedKey, + c.erasedKey, + c.maxLoadFactor, + c.entryCountThreadCacheSize); + } catch (...) { + Allocator().deallocate(mem, sz); + throw; + } + + SmartPtr map(static_cast<AtomicHashArray*>((void*)mem)); + + /* + * Mark all cells as empty. + * + * Note: we're bending the rules a little here accessing the key + * element in our cells even though the cell object has not been + * constructed, and casting them to atomic objects (see cellKeyPtr). + * (Also, in fact we never actually invoke the value_type + * constructor.) This is in order to avoid needing to default + * construct a bunch of value_type when we first start up: if you + * have an expensive default constructor for the value type this can + * noticeably speed construction time for an AHA. + */ + FOR_EACH_RANGE (i, 0, map->capacity_) { + cellKeyPtr(map->cells_[i]) + ->store(map->kEmptyKey_, std::memory_order_relaxed); + } + return map; +} + +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +void AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::destroy(AtomicHashArray* p) { + assert(p); + + size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * p->capacity_; + + FOR_EACH_RANGE (i, 0, p->capacity_) { + if (p->cells_[i].first != p->kEmptyKey_) { + p->cells_[i].~value_type(); + } + } + p->~AtomicHashArray(); + + Allocator().deallocate((char*)p, sz); +} + +// clear -- clears all keys and values in the map and resets all counters +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +void AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::clear() { + FOR_EACH_RANGE (i, 0, capacity_) { + if (cells_[i].first != kEmptyKey_) { + cells_[i].~value_type(); + *const_cast<KeyT*>(&cells_[i].first) = kEmptyKey_; + } + CHECK(cells_[i].first == kEmptyKey_); + } + numEntries_.set(0); + numPendingEntries_.set(0); + isFull_.store(0, std::memory_order_relaxed); + numErases_.store(0, std::memory_order_relaxed); +} + +// Iterator implementation + +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +template <class ContT, class IterVal> +struct AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::aha_iterator + : detail::IteratorFacade< + aha_iterator<ContT, IterVal>, + IterVal, + std::forward_iterator_tag> { + explicit aha_iterator() : aha_(nullptr) {} + + // Conversion ctor for interoperability between const_iterator and + // iterator. The enable_if<> magic keeps us well-behaved for + // is_convertible<> (v. the iterator_facade documentation). + template <class OtherContT, class OtherVal> + aha_iterator( + const aha_iterator<OtherContT, OtherVal>& o, + typename std::enable_if< + std::is_convertible<OtherVal*, IterVal*>::value>::type* = nullptr) + : aha_(o.aha_), offset_(o.offset_) {} + + explicit aha_iterator(ContT* array, size_t offset) + : aha_(array), offset_(offset) {} + + // Returns unique index that can be used with findAt(). + // WARNING: The following function will fail silently for hashtable + // with capacity > 2^32 + uint32_t getIndex() const { + return offset_; + } + + void advancePastEmpty() { + while (offset_ < aha_->capacity_ && !isValid()) { + ++offset_; + } + } + + private: + friend class AtomicHashArray; + friend class detail:: + IteratorFacade<aha_iterator, IterVal, std::forward_iterator_tag>; + + void increment() { + ++offset_; + advancePastEmpty(); + } + + bool equal(const aha_iterator& o) const { + return aha_ == o.aha_ && offset_ == o.offset_; + } + + IterVal& dereference() const { + return aha_->cells_[offset_]; + } + + bool isValid() const { + KeyT key = acquireLoadKey(aha_->cells_[offset_]); + return key != aha_->kEmptyKey_ && key != aha_->kLockedKey_ && + key != aha_->kErasedKey_; + } + + private: + ContT* aha_; + size_t offset_; +}; // aha_iterator + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/AtomicHashArray.h b/ios/Pods/Flipper-Folly/folly/AtomicHashArray.h new file mode 100644 index 000000000..cd62a2329 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicHashArray.h @@ -0,0 +1,448 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * AtomicHashArray is the building block for AtomicHashMap. It provides the + * core lock-free functionality, but is limited by the fact that it cannot + * grow past its initialization size and is a little more awkward (no public + * constructor, for example). If you're confident that you won't run out of + * space, don't mind the awkardness, and really need bare-metal performance, + * feel free to use AHA directly. + * + * Check out AtomicHashMap.h for more thorough documentation on perf and + * general pros and cons relative to other hash maps. + * + * @author Spencer Ahrens <sahrens@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +#pragma once +#define FOLLY_ATOMICHASHARRAY_H_ + +#include <atomic> + +#include <folly/ThreadCachedInt.h> +#include <folly/Utility.h> +#include <folly/hash/Hash.h> + +namespace folly { + +struct AtomicHashArrayLinearProbeFcn { + inline size_t operator()(size_t idx, size_t /* numProbes */, size_t capacity) + const { + idx += 1; // linear probing + + // Avoid modulus because it's slow + return LIKELY(idx < capacity) ? idx : (idx - capacity); + } +}; + +struct AtomicHashArrayQuadraticProbeFcn { + inline size_t operator()(size_t idx, size_t numProbes, size_t capacity) + const { + idx += numProbes; // quadratic probing + + // Avoid modulus because it's slow + return LIKELY(idx < capacity) ? idx : (idx - capacity); + } +}; + +// Enables specializing checkLegalKey without specializing its class. +namespace detail { +template <typename NotKeyT, typename KeyT> +inline void checkLegalKeyIfKeyTImpl( + NotKeyT /* ignored */, + KeyT /* emptyKey */, + KeyT /* lockedKey */, + KeyT /* erasedKey */) {} + +template <typename KeyT> +inline void checkLegalKeyIfKeyTImpl( + KeyT key_in, + KeyT emptyKey, + KeyT lockedKey, + KeyT erasedKey) { + DCHECK_NE(key_in, emptyKey); + DCHECK_NE(key_in, lockedKey); + DCHECK_NE(key_in, erasedKey); +} +} // namespace detail + +template < + class KeyT, + class ValueT, + class HashFcn = std::hash<KeyT>, + class EqualFcn = std::equal_to<KeyT>, + class Allocator = std::allocator<char>, + class ProbeFcn = AtomicHashArrayLinearProbeFcn, + class KeyConvertFcn = Identity> +class AtomicHashMap; + +template < + class KeyT, + class ValueT, + class HashFcn = std::hash<KeyT>, + class EqualFcn = std::equal_to<KeyT>, + class Allocator = std::allocator<char>, + class ProbeFcn = AtomicHashArrayLinearProbeFcn, + class KeyConvertFcn = Identity> +class AtomicHashArray { + static_assert( + (std::is_convertible<KeyT, int32_t>::value || + std::is_convertible<KeyT, int64_t>::value || + std::is_convertible<KeyT, const void*>::value), + "You are trying to use AtomicHashArray with disallowed key " + "types. You must use atomically compare-and-swappable integer " + "keys, or a different container class."); + + public: + typedef KeyT key_type; + typedef ValueT mapped_type; + typedef HashFcn hasher; + typedef EqualFcn key_equal; + typedef KeyConvertFcn key_convert; + typedef std::pair<const KeyT, ValueT> value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + const size_t capacity_; + const size_t maxEntries_; + const KeyT kEmptyKey_; + const KeyT kLockedKey_; + const KeyT kErasedKey_; + + template <class ContT, class IterVal> + struct aha_iterator; + + typedef aha_iterator<const AtomicHashArray, const value_type> const_iterator; + typedef aha_iterator<AtomicHashArray, value_type> iterator; + + // You really shouldn't need this if you use the SmartPtr provided by create, + // but if you really want to do something crazy like stick the released + // pointer into a DescriminatedPtr or something, you'll need this to clean up + // after yourself. + static void destroy(AtomicHashArray*); + + private: + const size_t kAnchorMask_; + + struct Deleter { + void operator()(AtomicHashArray* ptr) { + AtomicHashArray::destroy(ptr); + } + }; + + public: + typedef std::unique_ptr<AtomicHashArray, Deleter> SmartPtr; + + /* + * create -- + * + * Creates AtomicHashArray objects. Use instead of constructor/destructor. + * + * We do things this way in order to avoid the perf penalty of a second + * pointer indirection when composing these into AtomicHashMap, which needs + * to store an array of pointers so that it can perform atomic operations on + * them when growing. + * + * Instead of a mess of arguments, we take a max size and a Config struct to + * simulate named ctor parameters. The Config struct has sensible defaults + * for everything, but is overloaded - if you specify a positive capacity, + * that will be used directly instead of computing it based on + * maxLoadFactor. + * + * Create returns an AHA::SmartPtr which is a unique_ptr with a custom + * deleter to make sure everything is cleaned up properly. + */ + struct Config { + KeyT emptyKey; + KeyT lockedKey; + KeyT erasedKey; + double maxLoadFactor; + double growthFactor; + uint32_t entryCountThreadCacheSize; + size_t capacity; // if positive, overrides maxLoadFactor + + // Cannot have constexpr ctor because some compilers rightly complain. + Config() + : emptyKey((KeyT)-1), + lockedKey((KeyT)-2), + erasedKey((KeyT)-3), + maxLoadFactor(0.8), + growthFactor(-1), + entryCountThreadCacheSize(1000), + capacity(0) {} + }; + + // Cannot have pre-instantiated const Config instance because of SIOF. + static SmartPtr create(size_t maxSize, const Config& c = Config()); + + /* + * find -- + * + * + * Returns the iterator to the element if found, otherwise end(). + * + * As an optional feature, the type of the key to look up (LookupKeyT) is + * allowed to be different from the type of keys actually stored (KeyT). + * + * This enables use cases where materializing the key is costly and usually + * redudant, e.g., canonicalizing/interning a set of strings and being able + * to look up by StringPiece. To use this feature, LookupHashFcn must take + * a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first + * and second parameter, respectively. + * + * See folly/test/ArrayHashArrayTest.cpp for sample usage. + */ + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + iterator find(LookupKeyT k) { + return iterator( + this, findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k).idx); + } + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + const_iterator find(LookupKeyT k) const { + return const_cast<AtomicHashArray*>(this) + ->find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k); + } + + /* + * insert -- + * + * Returns a pair with iterator to the element at r.first and bool success. + * Retrieve the index with ret.first.getIndex(). + * + * Fails on key collision (does not overwrite) or if map becomes + * full, at which point no element is inserted, iterator is set to end(), + * and success is set false. On collisions, success is set false, but the + * iterator is set to the existing entry. + */ + std::pair<iterator, bool> insert(const value_type& r) { + return emplace(r.first, r.second); + } + std::pair<iterator, bool> insert(value_type&& r) { + return emplace(r.first, std::move(r.second)); + } + + /* + * emplace -- + * + * Same contract as insert(), but performs in-place construction + * of the value type using the specified arguments. + * + * Also, like find(), this method optionally allows 'key_in' to have a type + * different from that stored in the table; see find(). If and only if no + * equal key is already present, this method converts 'key_in' to a key of + * type KeyT using the provided LookupKeyToKeyFcn. + */ + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal, + typename LookupKeyToKeyFcn = key_convert, + typename... ArgTs> + std::pair<iterator, bool> emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) { + SimpleRetT ret = insertInternal< + LookupKeyT, + LookupHashFcn, + LookupEqualFcn, + LookupKeyToKeyFcn>(key_in, std::forward<ArgTs>(vCtorArgs)...); + return std::make_pair(iterator(this, ret.idx), ret.success); + } + + // returns the number of elements erased - should never exceed 1 + size_t erase(KeyT k); + + // clears all keys and values in the map and resets all counters. Not thread + // safe. + void clear(); + + // Exact number of elements in the map - note that readFull() acquires a + // mutex. See folly/ThreadCachedInt.h for more details. + size_t size() const { + return numEntries_.readFull() - numErases_.load(std::memory_order_relaxed); + } + + bool empty() const { + return size() == 0; + } + + iterator begin() { + iterator it(this, 0); + it.advancePastEmpty(); + return it; + } + const_iterator begin() const { + const_iterator it(this, 0); + it.advancePastEmpty(); + return it; + } + + iterator end() { + return iterator(this, capacity_); + } + const_iterator end() const { + return const_iterator(this, capacity_); + } + + // See AtomicHashMap::findAt - access elements directly + // WARNING: The following 2 functions will fail silently for hashtable + // with capacity > 2^32 + iterator findAt(uint32_t idx) { + DCHECK_LT(idx, capacity_); + return iterator(this, idx); + } + const_iterator findAt(uint32_t idx) const { + return const_cast<AtomicHashArray*>(this)->findAt(idx); + } + + iterator makeIter(size_t idx) { + return iterator(this, idx); + } + const_iterator makeIter(size_t idx) const { + return const_iterator(this, idx); + } + + // The max load factor allowed for this map + double maxLoadFactor() const { + return ((double)maxEntries_) / capacity_; + } + + void setEntryCountThreadCacheSize(uint32_t newSize) { + numEntries_.setCacheSize(newSize); + numPendingEntries_.setCacheSize(newSize); + } + + uint32_t getEntryCountThreadCacheSize() const { + return numEntries_.getCacheSize(); + } + + /* Private data and helper functions... */ + + private: + friend class AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn>; + + struct SimpleRetT { + size_t idx; + bool success; + SimpleRetT(size_t i, bool s) : idx(i), success(s) {} + SimpleRetT() = default; + }; + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal, + typename LookupKeyToKeyFcn = Identity, + typename... ArgTs> + SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs); + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + SimpleRetT findInternal(const LookupKeyT key); + + template <typename MaybeKeyT> + void checkLegalKeyIfKey(MaybeKeyT key) { + detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_); + } + + static std::atomic<KeyT>* cellKeyPtr(const value_type& r) { + // We need some illegal casting here in order to actually store + // our value_type as a std::pair<const,>. But a little bit of + // undefined behavior never hurt anyone ... + static_assert( + sizeof(std::atomic<KeyT>) == sizeof(KeyT), + "std::atomic is implemented in an unexpected way for AHM"); + return const_cast<std::atomic<KeyT>*>( + reinterpret_cast<std::atomic<KeyT> const*>(&r.first)); + } + + static KeyT relaxedLoadKey(const value_type& r) { + return cellKeyPtr(r)->load(std::memory_order_relaxed); + } + + static KeyT acquireLoadKey(const value_type& r) { + return cellKeyPtr(r)->load(std::memory_order_acquire); + } + + // Fun with thread local storage - atomic increment is expensive + // (relatively), so we accumulate in the thread cache and periodically + // flush to the actual variable, and walk through the unflushed counts when + // reading the value, so be careful of calling size() too frequently. This + // increases insertion throughput several times over while keeping the count + // accurate. + ThreadCachedInt<uint64_t> numEntries_; // Successful key inserts + ThreadCachedInt<uint64_t> numPendingEntries_; // Used by insertInternal + std::atomic<int64_t> isFull_; // Used by insertInternal + std::atomic<int64_t> numErases_; // Successful key erases + + value_type cells_[0]; // This must be the last field of this class + + // Force constructor/destructor private since create/destroy should be + // used externally instead + AtomicHashArray( + size_t capacity, + KeyT emptyKey, + KeyT lockedKey, + KeyT erasedKey, + double maxLoadFactor, + uint32_t cacheSize); + + AtomicHashArray(const AtomicHashArray&) = delete; + AtomicHashArray& operator=(const AtomicHashArray&) = delete; + + ~AtomicHashArray() = default; + + inline void unlockCell(value_type* const cell, KeyT newKey) { + cellKeyPtr(*cell)->store(newKey, std::memory_order_release); + } + + inline bool tryLockCell(value_type* const cell) { + KeyT expect = kEmptyKey_; + return cellKeyPtr(*cell)->compare_exchange_strong( + expect, kLockedKey_, std::memory_order_acq_rel); + } + + template <class LookupKeyT = key_type, class LookupHashFcn = hasher> + inline size_t keyToAnchorIdx(const LookupKeyT k) const { + const size_t hashVal = LookupHashFcn()(k); + const size_t probe = hashVal & kAnchorMask_; + return LIKELY(probe < capacity_) ? probe : hashVal % capacity_; + } + +}; // AtomicHashArray + +} // namespace folly + +#include <folly/AtomicHashArray-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/AtomicHashMap-inl.h b/ios/Pods/Flipper-Folly/folly/AtomicHashMap-inl.h new file mode 100644 index 000000000..f15f07e2b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicHashMap-inl.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_ATOMICHASHMAP_H_ +#error "This should only be included by AtomicHashMap.h" +#endif + +#include <folly/detail/AtomicHashUtils.h> +#include <folly/detail/Iterators.h> + +#include <type_traits> + +namespace folly { + +// AtomicHashMap constructor -- Atomic wrapper that allows growth +// This class has a lot of overhead (184 Bytes) so only use for big maps +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::AtomicHashMap(size_t finalSizeEst, const Config& config) + : kGrowthFrac_( + config.growthFactor < 0 ? 1.0f - config.maxLoadFactor + : config.growthFactor) { + CHECK(config.maxLoadFactor > 0.0f && config.maxLoadFactor < 1.0f); + subMaps_[0].store( + SubMap::create(finalSizeEst, config).release(), + std::memory_order_relaxed); + auto subMapCount = kNumSubMaps_; + FOR_EACH_RANGE (i, 1, subMapCount) { + subMaps_[i].store(nullptr, std::memory_order_relaxed); + } + numMapsAllocated_.store(1, std::memory_order_relaxed); +} + +// emplace -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template < + typename LookupKeyT, + typename LookupHashFcn, + typename LookupEqualFcn, + typename LookupKeyToKeyFcn, + typename... ArgTs> +std::pair< + typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::iterator, + bool> +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::emplace(LookupKeyT k, ArgTs&&... vCtorArgs) { + SimpleRetT ret = insertInternal< + LookupKeyT, + LookupHashFcn, + LookupEqualFcn, + LookupKeyToKeyFcn>(k, std::forward<ArgTs>(vCtorArgs)...); + SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed); + return std::make_pair( + iterator(this, ret.i, subMap->makeIter(ret.j)), ret.success); +} + +// insertInternal -- Allocates new sub maps as existing ones fill up. +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template < + typename LookupKeyT, + typename LookupHashFcn, + typename LookupEqualFcn, + typename LookupKeyToKeyFcn, + typename... ArgTs> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs) { +beginInsertInternal: + auto nextMapIdx = // this maintains our state + numMapsAllocated_.load(std::memory_order_acquire); + typename SubMap::SimpleRetT ret; + FOR_EACH_RANGE (i, 0, nextMapIdx) { + // insert in each map successively. If one succeeds, we're done! + SubMap* subMap = subMaps_[i].load(std::memory_order_relaxed); + ret = subMap->template insertInternal< + LookupKeyT, + LookupHashFcn, + LookupEqualFcn, + LookupKeyToKeyFcn>(key, std::forward<ArgTs>(vCtorArgs)...); + if (ret.idx == subMap->capacity_) { + continue; // map is full, so try the next one + } + // Either collision or success - insert in either case + return SimpleRetT(i, ret.idx, ret.success); + } + + // If we made it this far, all maps are full and we need to try to allocate + // the next one. + + SubMap* primarySubMap = subMaps_[0].load(std::memory_order_relaxed); + if (nextMapIdx >= kNumSubMaps_ || + primarySubMap->capacity_ * kGrowthFrac_ < 1.0) { + // Can't allocate any more sub maps. + throw AtomicHashMapFullError(); + } + + if (tryLockMap(nextMapIdx)) { + // Alloc a new map and shove it in. We can change whatever + // we want because other threads are waiting on us... + size_t numCellsAllocated = (size_t)( + primarySubMap->capacity_ * + std::pow(1.0 + kGrowthFrac_, nextMapIdx - 1)); + size_t newSize = size_t(numCellsAllocated * kGrowthFrac_); + DCHECK( + subMaps_[nextMapIdx].load(std::memory_order_relaxed) == + (SubMap*)kLockedPtr_); + // create a new map using the settings stored in the first map + + Config config; + config.emptyKey = primarySubMap->kEmptyKey_; + config.lockedKey = primarySubMap->kLockedKey_; + config.erasedKey = primarySubMap->kErasedKey_; + config.maxLoadFactor = primarySubMap->maxLoadFactor(); + config.entryCountThreadCacheSize = + primarySubMap->getEntryCountThreadCacheSize(); + subMaps_[nextMapIdx].store( + SubMap::create(newSize, config).release(), std::memory_order_relaxed); + + // Publish the map to other threads. + numMapsAllocated_.fetch_add(1, std::memory_order_release); + DCHECK_EQ( + nextMapIdx + 1, numMapsAllocated_.load(std::memory_order_relaxed)); + } else { + // If we lost the race, we'll have to wait for the next map to get + // allocated before doing any insertion here. + detail::atomic_hash_spin_wait([&] { + return nextMapIdx >= numMapsAllocated_.load(std::memory_order_acquire); + }); + } + + // Relaxed is ok here because either we just created this map, or we + // just did a spin wait with an acquire load on numMapsAllocated_. + SubMap* loadedMap = subMaps_[nextMapIdx].load(std::memory_order_relaxed); + DCHECK(loadedMap && loadedMap != (SubMap*)kLockedPtr_); + ret = loadedMap->insertInternal(key, std::forward<ArgTs>(vCtorArgs)...); + if (ret.idx != loadedMap->capacity_) { + return SimpleRetT(nextMapIdx, ret.idx, ret.success); + } + // We took way too long and the new map is already full...try again from + // the top (this should pretty much never happen). + goto beginInsertInternal; +} + +// find -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::iterator +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::find(LookupKeyT k) { + SimpleRetT ret = findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k); + if (!ret.success) { + return end(); + } + SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed); + return iterator(this, ret.i, subMap->makeIter(ret.j)); +} + +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::const_iterator +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::find(LookupKeyT k) const { + return const_cast<AtomicHashMap*>(this) + ->find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k); +} + +// findInternal -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::findInternal(const LookupKeyT k) const { + SubMap* const primaryMap = subMaps_[0].load(std::memory_order_relaxed); + typename SubMap::SimpleRetT ret = + primaryMap + ->template findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k); + if (LIKELY(ret.idx != primaryMap->capacity_)) { + return SimpleRetT(0, ret.idx, ret.success); + } + const unsigned int numMaps = + numMapsAllocated_.load(std::memory_order_acquire); + FOR_EACH_RANGE (i, 1, numMaps) { + // Check each map successively. If one succeeds, we're done! + SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); + ret = + thisMap + ->template findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>( + k); + if (LIKELY(ret.idx != thisMap->capacity_)) { + return SimpleRetT(i, ret.idx, ret.success); + } + } + // Didn't find our key... + return SimpleRetT(numMaps, 0, false); +} + +// findAtInternal -- see encodeIndex() for details. +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::findAtInternal(uint32_t idx) const { + uint32_t subMapIdx, subMapOffset; + if (idx & kSecondaryMapBit_) { + // idx falls in a secondary map + idx &= ~kSecondaryMapBit_; // unset secondary bit + subMapIdx = idx >> kSubMapIndexShift_; + DCHECK_LT(subMapIdx, numMapsAllocated_.load(std::memory_order_relaxed)); + subMapOffset = idx & kSubMapIndexMask_; + } else { + // idx falls in primary map + subMapIdx = 0; + subMapOffset = idx; + } + return SimpleRetT(subMapIdx, subMapOffset, true); +} + +// erase -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::size_type +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::erase(const KeyT k) { + int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); + FOR_EACH_RANGE (i, 0, numMaps) { + // Check each map successively. If one succeeds, we're done! + if (subMaps_[i].load(std::memory_order_relaxed)->erase(k)) { + return 1; + } + } + // Didn't find our key... + return 0; +} + +// capacity -- summation of capacities of all submaps +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +size_t AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::capacity() const { + size_t totalCap(0); + int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); + FOR_EACH_RANGE (i, 0, numMaps) { + totalCap += subMaps_[i].load(std::memory_order_relaxed)->capacity_; + } + return totalCap; +} + +// spaceRemaining -- +// number of new insertions until current submaps are all at max load +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +size_t AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::spaceRemaining() const { + size_t spaceRem(0); + int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); + FOR_EACH_RANGE (i, 0, numMaps) { + SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); + spaceRem += + std::max(0, thisMap->maxEntries_ - &thisMap->numEntries_.readFull()); + } + return spaceRem; +} + +// clear -- Wipes all keys and values from primary map and destroys +// all secondary maps. Not thread safe. +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +void AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::clear() { + subMaps_[0].load(std::memory_order_relaxed)->clear(); + int const numMaps = numMapsAllocated_.load(std::memory_order_relaxed); + FOR_EACH_RANGE (i, 1, numMaps) { + SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); + DCHECK(thisMap); + SubMap::destroy(thisMap); + subMaps_[i].store(nullptr, std::memory_order_relaxed); + } + numMapsAllocated_.store(1, std::memory_order_relaxed); +} + +// size -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +size_t AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::size() const { + size_t totalSize(0); + int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); + FOR_EACH_RANGE (i, 0, numMaps) { + totalSize += subMaps_[i].load(std::memory_order_relaxed)->size(); + } + return totalSize; +} + +// encodeIndex -- Encode the submap index and offset into return. +// index_ret must be pre-populated with the submap offset. +// +// We leave index_ret untouched when referring to the primary map +// so it can be as large as possible (31 data bits). Max size of +// secondary maps is limited by what can fit in the low 27 bits. +// +// Returns the following bit-encoded data in index_ret: +// if subMap == 0 (primary map) => +// bit(s) value +// 31 0 +// 0-30 submap offset (index_ret input) +// +// if subMap > 0 (secondary maps) => +// bit(s) value +// 31 1 +// 27-30 which subMap +// 0-26 subMap offset (index_ret input) +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +inline uint32_t AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::encodeIndex(uint32_t subMap, uint32_t offset) { + DCHECK_EQ(offset & kSecondaryMapBit_, 0); // offset can't be too big + if (subMap == 0) { + return offset; + } + // Make sure subMap isn't too big + DCHECK_EQ(subMap >> kNumSubMapBits_, 0); + // Make sure subMap bits of offset are clear + DCHECK_EQ(offset & (~kSubMapIndexMask_ | kSecondaryMapBit_), 0); + + // Set high-order bits to encode which submap this index belongs to + return offset | (subMap << kSubMapIndexShift_) | kSecondaryMapBit_; +} + +// Iterator implementation + +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template <class ContT, class IterVal, class SubIt> +struct AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::ahm_iterator + : detail::IteratorFacade< + ahm_iterator<ContT, IterVal, SubIt>, + IterVal, + std::forward_iterator_tag> { + explicit ahm_iterator() : ahm_(nullptr) {} + + // Conversion ctor for interoperability between const_iterator and + // iterator. The enable_if<> magic keeps us well-behaved for + // is_convertible<> (v. the iterator_facade documentation). + template <class OtherContT, class OtherVal, class OtherSubIt> + ahm_iterator( + const ahm_iterator<OtherContT, OtherVal, OtherSubIt>& o, + typename std::enable_if< + std::is_convertible<OtherSubIt, SubIt>::value>::type* = nullptr) + : ahm_(o.ahm_), subMap_(o.subMap_), subIt_(o.subIt_) {} + + /* + * Returns the unique index that can be used for access directly + * into the data storage. + */ + uint32_t getIndex() const { + CHECK(!isEnd()); + return ahm_->encodeIndex(subMap_, subIt_.getIndex()); + } + + private: + friend class AtomicHashMap; + explicit ahm_iterator(ContT* ahm, uint32_t subMap, const SubIt& subIt) + : ahm_(ahm), subMap_(subMap), subIt_(subIt) {} + + friend class detail:: + IteratorFacade<ahm_iterator, IterVal, std::forward_iterator_tag>; + + void increment() { + CHECK(!isEnd()); + ++subIt_; + checkAdvanceToNextSubmap(); + } + + bool equal(const ahm_iterator& other) const { + if (ahm_ != other.ahm_) { + return false; + } + + if (isEnd() || other.isEnd()) { + return isEnd() == other.isEnd(); + } + + return subMap_ == other.subMap_ && subIt_ == other.subIt_; + } + + IterVal& dereference() const { + return *subIt_; + } + + bool isEnd() const { + return ahm_ == nullptr; + } + + void checkAdvanceToNextSubmap() { + if (isEnd()) { + return; + } + + SubMap* thisMap = ahm_->subMaps_[subMap_].load(std::memory_order_relaxed); + while (subIt_ == thisMap->end()) { + // This sub iterator is done, advance to next one + if (subMap_ + 1 < + ahm_->numMapsAllocated_.load(std::memory_order_acquire)) { + ++subMap_; + thisMap = ahm_->subMaps_[subMap_].load(std::memory_order_relaxed); + subIt_ = thisMap->begin(); + } else { + ahm_ = nullptr; + return; + } + } + } + + private: + ContT* ahm_; + uint32_t subMap_; + SubIt subIt_; +}; // ahm_iterator + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/AtomicHashMap.h b/ios/Pods/Flipper-Folly/folly/AtomicHashMap.h new file mode 100644 index 000000000..e9d0e66f8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicHashMap.h @@ -0,0 +1,500 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * AtomicHashMap -- + * + * A high-performance concurrent hash map with int32_t or int64_t keys. Supports + * insert, find(key), findAt(index), erase(key), size, and more. Memory cannot + * be freed or reclaimed by erase. Can grow to a maximum of about 18 times the + * initial capacity, but performance degrades linearly with growth. Can also be + * used as an object store with unique 32-bit references directly into the + * internal storage (retrieved with iterator::getIndex()). + * + * Advantages: + * - High-performance (~2-4x tbb::concurrent_hash_map in heavily + * multi-threaded environments). + * - Efficient memory usage if initial capacity is not over estimated + * (especially for small keys and values). + * - Good fragmentation properties (only allocates in large slabs which can + * be reused with clear() and never move). + * - Can generate unique, long-lived 32-bit references for efficient lookup + * (see findAt()). + * + * Disadvantages: + * - Keys must be native int32_t or int64_t, or explicitly converted. + * - Must be able to specify unique empty, locked, and erased keys + * - Performance degrades linearly as size grows beyond initialization + * capacity. + * - Max size limit of ~18x initial size (dependent on max load factor). + * - Memory is not freed or reclaimed by erase. + * + * Usage and Operation Details: + * Simple performance/memory tradeoff with maxLoadFactor. Higher load factors + * give better memory utilization but probe lengths increase, reducing + * performance. + * + * Implementation and Performance Details: + * AHArray is a fixed size contiguous block of value_type cells. When + * writing a cell, the key is locked while the rest of the record is + * written. Once done, the cell is unlocked by setting the key. find() + * is completely wait-free and doesn't require any non-relaxed atomic + * operations. AHA cannot grow beyond initialization capacity, but is + * faster because of reduced data indirection. + * + * AHMap is a wrapper around AHArray sub-maps that allows growth and provides + * an interface closer to the STL UnorderedAssociativeContainer concept. These + * sub-maps are allocated on the fly and are processed in series, so the more + * there are (from growing past initial capacity), the worse the performance. + * + * Insert returns false if there is a key collision and throws if the max size + * of the map is exceeded. + * + * Benchmark performance with 8 simultaneous threads processing 1 million + * unique <int64_t, int64_t> entries on a 4-core, 2.5 GHz machine: + * + * Load Factor Mem Efficiency usec/Insert usec/Find + * 50% 50% 0.19 0.05 + * 85% 85% 0.20 0.06 + * 90% 90% 0.23 0.08 + * 95% 95% 0.27 0.10 + * + * See folly/tests/AtomicHashMapTest.cpp for more benchmarks. + * + * @author Spencer Ahrens <sahrens@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + * + */ + +#pragma once +#define FOLLY_ATOMICHASHMAP_H_ + +#include <atomic> +#include <functional> +#include <stdexcept> + +#include <folly/AtomicHashArray.h> +#include <folly/CPortability.h> +#include <folly/Likely.h> +#include <folly/ThreadCachedInt.h> +#include <folly/container/Foreach.h> +#include <folly/hash/Hash.h> + +namespace folly { + +/* + * AtomicHashMap provides an interface somewhat similar to the + * UnorderedAssociativeContainer concept in C++. This does not + * exactly match this concept (or even the basic Container concept), + * because of some restrictions imposed by our datastructure. + * + * Specific differences (there are quite a few): + * + * - Efficiently thread safe for inserts (main point of this stuff), + * wait-free for lookups. + * + * - You can erase from this container, but the cell containing the key will + * not be free or reclaimed. + * + * - You can erase everything by calling clear() (and you must guarantee only + * one thread can be using the container to do that). + * + * - We aren't DefaultConstructible, CopyConstructible, Assignable, or + * EqualityComparable. (Most of these are probably not something + * you actually want to do with this anyway.) + * + * - We don't support the various bucket functions, rehash(), + * reserve(), or equal_range(). Also no constructors taking + * iterators, although this could change. + * + * - Several insertion functions, notably operator[], are not + * implemented. It is a little too easy to misuse these functions + * with this container, where part of the point is that when an + * insertion happens for a new key, it will atomically have the + * desired value. + * + * - The map has no templated insert() taking an iterator range, but + * we do provide an insert(key, value). The latter seems more + * frequently useful for this container (to avoid sprinkling + * make_pair everywhere), and providing both can lead to some gross + * template error messages. + * + * - The Allocator must not be stateful (a new instance will be spun up for + * each allocation), and its allocate() method must take a raw number of + * bytes. + * + * - KeyT must be a 32 bit or 64 bit atomic integer type, and you must + * define special 'locked' and 'empty' key values in the ctor + * + * - We don't take the Hash function object as an instance in the + * constructor. + * + */ + +// Thrown when insertion fails due to running out of space for +// submaps. +struct FOLLY_EXPORT AtomicHashMapFullError : std::runtime_error { + explicit AtomicHashMapFullError() + : std::runtime_error("AtomicHashMap is full") {} +}; + +template < + class KeyT, + class ValueT, + class HashFcn, + class EqualFcn, + class Allocator, + class ProbeFcn, + class KeyConvertFcn> +class AtomicHashMap { + typedef AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn> + SubMap; + + public: + typedef KeyT key_type; + typedef ValueT mapped_type; + typedef std::pair<const KeyT, ValueT> value_type; + typedef HashFcn hasher; + typedef EqualFcn key_equal; + typedef KeyConvertFcn key_convert; + typedef value_type* pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::ptrdiff_t difference_type; + typedef std::size_t size_type; + typedef typename SubMap::Config Config; + + template <class ContT, class IterVal, class SubIt> + struct ahm_iterator; + + typedef ahm_iterator< + const AtomicHashMap, + const value_type, + typename SubMap::const_iterator> + const_iterator; + typedef ahm_iterator<AtomicHashMap, value_type, typename SubMap::iterator> + iterator; + + public: + const float kGrowthFrac_; // How much to grow when we run out of capacity. + + // The constructor takes a finalSizeEst which is the optimal + // number of elements to maximize space utilization and performance, + // and a Config object to specify more advanced options. + explicit AtomicHashMap(size_t finalSizeEst, const Config& c = Config()); + + AtomicHashMap(const AtomicHashMap&) = delete; + AtomicHashMap& operator=(const AtomicHashMap&) = delete; + + ~AtomicHashMap() { + const unsigned int numMaps = + numMapsAllocated_.load(std::memory_order_relaxed); + FOR_EACH_RANGE (i, 0, numMaps) { + SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); + DCHECK(thisMap); + SubMap::destroy(thisMap); + } + } + + key_equal key_eq() const { + return key_equal(); + } + hasher hash_function() const { + return hasher(); + } + + /* + * insert -- + * + * Returns a pair with iterator to the element at r.first and + * success. Retrieve the index with ret.first.getIndex(). + * + * Does not overwrite on key collision, but returns an iterator to + * the existing element (since this could due to a race with + * another thread, it is often important to check this return + * value). + * + * Allocates new sub maps as the existing ones become full. If + * all sub maps are full, no element is inserted, and + * AtomicHashMapFullError is thrown. + */ + std::pair<iterator, bool> insert(const value_type& r) { + return emplace(r.first, r.second); + } + std::pair<iterator, bool> insert(key_type k, const mapped_type& v) { + return emplace(k, v); + } + std::pair<iterator, bool> insert(value_type&& r) { + return emplace(r.first, std::move(r.second)); + } + std::pair<iterator, bool> insert(key_type k, mapped_type&& v) { + return emplace(k, std::move(v)); + } + + /* + * emplace -- + * + * Same contract as insert(), but performs in-place construction + * of the value type using the specified arguments. + * + * Also, like find(), this method optionally allows 'key_in' to have a type + * different from that stored in the table; see find(). If and only if no + * equal key is already present, this method converts 'key_in' to a key of + * type KeyT using the provided LookupKeyToKeyFcn. + */ + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal, + typename LookupKeyToKeyFcn = key_convert, + typename... ArgTs> + std::pair<iterator, bool> emplace(LookupKeyT k, ArgTs&&... vCtorArg); + + /* + * find -- + * + * Returns the iterator to the element if found, otherwise end(). + * + * As an optional feature, the type of the key to look up (LookupKeyT) is + * allowed to be different from the type of keys actually stored (KeyT). + * + * This enables use cases where materializing the key is costly and usually + * redudant, e.g., canonicalizing/interning a set of strings and being able + * to look up by StringPiece. To use this feature, LookupHashFcn must take + * a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first + * and second parameter, respectively. + * + * See folly/test/ArrayHashMapTest.cpp for sample usage. + */ + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + iterator find(LookupKeyT k); + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + const_iterator find(LookupKeyT k) const; + + /* + * erase -- + * + * Erases key k from the map + * + * Returns 1 iff the key is found and erased, and 0 otherwise. + */ + size_type erase(key_type k); + + /* + * clear -- + * + * Wipes all keys and values from primary map and destroys all secondary + * maps. Primary map remains allocated and thus the memory can be reused + * in place. Not thread safe. + * + */ + void clear(); + + /* + * size -- + * + * Returns the exact size of the map. Note this is not as cheap as typical + * size() implementations because, for each AtomicHashArray in this AHM, we + * need to grab a lock and accumulate the values from all the thread local + * counters. See folly/ThreadCachedInt.h for more details. + */ + size_t size() const; + + bool empty() const { + return size() == 0; + } + + size_type count(key_type k) const { + return find(k) == end() ? 0 : 1; + } + + /* + * findAt -- + * + * Returns an iterator into the map. + * + * idx should only be an unmodified value returned by calling getIndex() on + * a valid iterator returned by find() or insert(). If idx is invalid you + * have a bug and the process aborts. + */ + iterator findAt(uint32_t idx) { + SimpleRetT ret = findAtInternal(idx); + DCHECK_LT(ret.i, numSubMaps()); + return iterator( + this, + ret.i, + subMaps_[ret.i].load(std::memory_order_relaxed)->makeIter(ret.j)); + } + const_iterator findAt(uint32_t idx) const { + return const_cast<AtomicHashMap*>(this)->findAt(idx); + } + + // Total capacity - summation of capacities of all submaps. + size_t capacity() const; + + // Number of new insertions until current submaps are all at max load factor. + size_t spaceRemaining() const; + + void setEntryCountThreadCacheSize(int32_t newSize) { + const int numMaps = numMapsAllocated_.load(std::memory_order_acquire); + for (int i = 0; i < numMaps; ++i) { + SubMap* map = subMaps_[i].load(std::memory_order_relaxed); + map->setEntryCountThreadCacheSize(newSize); + } + } + + // Number of sub maps allocated so far to implement this map. The more there + // are, the worse the performance. + int numSubMaps() const { + return numMapsAllocated_.load(std::memory_order_acquire); + } + + iterator begin() { + iterator it(this, 0, subMaps_[0].load(std::memory_order_relaxed)->begin()); + it.checkAdvanceToNextSubmap(); + return it; + } + + const_iterator begin() const { + const_iterator it( + this, 0, subMaps_[0].load(std::memory_order_relaxed)->begin()); + it.checkAdvanceToNextSubmap(); + return it; + } + + iterator end() { + return iterator(); + } + + const_iterator end() const { + return const_iterator(); + } + + /* Advanced functions for direct access: */ + + inline uint32_t recToIdx(const value_type& r, bool mayInsert = true) { + SimpleRetT ret = + mayInsert ? insertInternal(r.first, r.second) : findInternal(r.first); + return encodeIndex(ret.i, ret.j); + } + + inline uint32_t recToIdx(value_type&& r, bool mayInsert = true) { + SimpleRetT ret = mayInsert ? insertInternal(r.first, std::move(r.second)) + : findInternal(r.first); + return encodeIndex(ret.i, ret.j); + } + + inline uint32_t + recToIdx(key_type k, const mapped_type& v, bool mayInsert = true) { + SimpleRetT ret = mayInsert ? insertInternal(k, v) : findInternal(k); + return encodeIndex(ret.i, ret.j); + } + + inline uint32_t recToIdx(key_type k, mapped_type&& v, bool mayInsert = true) { + SimpleRetT ret = + mayInsert ? insertInternal(k, std::move(v)) : findInternal(k); + return encodeIndex(ret.i, ret.j); + } + + inline uint32_t keyToIdx(const KeyT k, bool mayInsert = false) { + return recToIdx(value_type(k), mayInsert); + } + + inline const value_type& idxToRec(uint32_t idx) const { + SimpleRetT ret = findAtInternal(idx); + return subMaps_[ret.i].load(std::memory_order_relaxed)->idxToRec(ret.j); + } + + /* Private data and helper functions... */ + + private: + // This limits primary submap size to 2^31 ~= 2 billion, secondary submap + // size to 2^(32 - kNumSubMapBits_ - 1) = 2^27 ~= 130 million, and num subMaps + // to 2^kNumSubMapBits_ = 16. + static const uint32_t kNumSubMapBits_ = 4; + static const uint32_t kSecondaryMapBit_ = 1u << 31; // Highest bit + static const uint32_t kSubMapIndexShift_ = 32 - kNumSubMapBits_ - 1; + static const uint32_t kSubMapIndexMask_ = (1 << kSubMapIndexShift_) - 1; + static const uint32_t kNumSubMaps_ = 1 << kNumSubMapBits_; + static const uintptr_t kLockedPtr_ = 0x88ULL << 48; // invalid pointer + + struct SimpleRetT { + uint32_t i; + size_t j; + bool success; + SimpleRetT(uint32_t ii, size_t jj, bool s) : i(ii), j(jj), success(s) {} + SimpleRetT() = default; + }; + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal, + typename LookupKeyToKeyFcn = key_convert, + typename... ArgTs> + SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... value); + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + SimpleRetT findInternal(const LookupKeyT k) const; + + SimpleRetT findAtInternal(uint32_t idx) const; + + std::atomic<SubMap*> subMaps_[kNumSubMaps_]; + std::atomic<uint32_t> numMapsAllocated_; + + inline bool tryLockMap(unsigned int idx) { + SubMap* val = nullptr; + return subMaps_[idx].compare_exchange_strong( + val, (SubMap*)kLockedPtr_, std::memory_order_acquire); + } + + static inline uint32_t encodeIndex(uint32_t subMap, uint32_t subMapIdx); + +}; // AtomicHashMap + +template < + class KeyT, + class ValueT, + class HashFcn = std::hash<KeyT>, + class EqualFcn = std::equal_to<KeyT>, + class Allocator = std::allocator<char>> +using QuadraticProbingAtomicHashMap = AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + AtomicHashArrayQuadraticProbeFcn>; +} // namespace folly + +#include <folly/AtomicHashMap-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h b/ios/Pods/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h new file mode 100644 index 000000000..aa2a866e0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cassert> +#include <utility> + +namespace folly { + +/** + * A very simple atomic single-linked list primitive. + * + * Usage: + * + * class MyClass { + * AtomicIntrusiveLinkedListHook<MyClass> hook_; + * } + * + * AtomicIntrusiveLinkedList<MyClass, &MyClass::hook_> list; + * list.insert(&a); + * list.sweep([] (MyClass* c) { doSomething(c); } + */ +template <class T> +struct AtomicIntrusiveLinkedListHook { + T* next{nullptr}; +}; + +template <class T, AtomicIntrusiveLinkedListHook<T> T::*HookMember> +class AtomicIntrusiveLinkedList { + public: + AtomicIntrusiveLinkedList() {} + AtomicIntrusiveLinkedList(const AtomicIntrusiveLinkedList&) = delete; + AtomicIntrusiveLinkedList& operator=(const AtomicIntrusiveLinkedList&) = + delete; + AtomicIntrusiveLinkedList(AtomicIntrusiveLinkedList&& other) noexcept { + auto tmp = other.head_.load(); + other.head_ = head_.load(); + head_ = tmp; + } + AtomicIntrusiveLinkedList& operator=( + AtomicIntrusiveLinkedList&& other) noexcept { + auto tmp = other.head_.load(); + other.head_ = head_.load(); + head_ = tmp; + + return *this; + } + + /** + * Note: list must be empty on destruction. + */ + ~AtomicIntrusiveLinkedList() { + assert(empty()); + } + + bool empty() const { + return head_.load() == nullptr; + } + + /** + * Atomically insert t at the head of the list. + * @return True if the inserted element is the only one in the list + * after the call. + */ + bool insertHead(T* t) { + assert(next(t) == nullptr); + + auto oldHead = head_.load(std::memory_order_relaxed); + do { + next(t) = oldHead; + /* oldHead is updated by the call below. + + NOTE: we don't use next(t) instead of oldHead directly due to + compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899), + MSVC (bug 819819); source: + http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */ + } while (!head_.compare_exchange_weak( + oldHead, t, std::memory_order_release, std::memory_order_relaxed)); + + return oldHead == nullptr; + } + + /** + * Replaces the head with nullptr, + * and calls func() on the removed elements in the order from tail to head. + * Returns false if the list was empty. + */ + template <typename F> + bool sweepOnce(F&& func) { + if (auto head = head_.exchange(nullptr)) { + auto rhead = reverse(head); + unlinkAll(rhead, std::forward<F>(func)); + return true; + } + return false; + } + + /** + * Repeatedly replaces the head with nullptr, + * and calls func() on the removed elements in the order from tail to head. + * Stops when the list is empty. + */ + template <typename F> + void sweep(F&& func) { + while (sweepOnce(func)) { + } + } + + /** + * Similar to sweep() but calls func() on elements in LIFO order. + * + * func() is called for all elements in the list at the moment + * reverseSweep() is called. Unlike sweep() it does not loop to ensure the + * list is empty at some point after the last invocation. This way callers + * can reason about the ordering: elements inserted since the last call to + * reverseSweep() will be provided in LIFO order. + * + * Example: if elements are inserted in the order 1-2-3, the callback is + * invoked 3-2-1. If the callback moves elements onto a stack, popping off + * the stack will produce the original insertion order 1-2-3. + */ + template <typename F> + void reverseSweep(F&& func) { + // We don't loop like sweep() does because the overall order of callbacks + // would be strand-wise LIFO which is meaningless to callers. + auto head = head_.exchange(nullptr); + unlinkAll(head, std::forward<F>(func)); + } + + private: + std::atomic<T*> head_{nullptr}; + + static T*& next(T* t) { + return (t->*HookMember).next; + } + + /* Reverses a linked list, returning the pointer to the new head + (old tail) */ + static T* reverse(T* head) { + T* rhead = nullptr; + while (head != nullptr) { + auto t = head; + head = next(t); + next(t) = rhead; + rhead = t; + } + return rhead; + } + + /* Unlinks all elements in the linked list fragment pointed to by `head', + * calling func() on every element */ + template <typename F> + void unlinkAll(T* head, F&& func) { + while (head != nullptr) { + auto t = head; + head = next(t); + next(t) = nullptr; + func(t); + } + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/AtomicLinkedList.h b/ios/Pods/Flipper-Folly/folly/AtomicLinkedList.h new file mode 100644 index 000000000..ecff27ab3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicLinkedList.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/AtomicIntrusiveLinkedList.h> +#include <folly/Memory.h> + +namespace folly { + +/** + * A very simple atomic single-linked list primitive. + * + * Usage: + * + * AtomicLinkedList<MyClass> list; + * list.insert(a); + * list.sweep([] (MyClass& c) { doSomething(c); } + */ + +template <class T> +class AtomicLinkedList { + public: + AtomicLinkedList() {} + AtomicLinkedList(const AtomicLinkedList&) = delete; + AtomicLinkedList& operator=(const AtomicLinkedList&) = delete; + AtomicLinkedList(AtomicLinkedList&& other) noexcept = default; + AtomicLinkedList& operator=(AtomicLinkedList&& other) = default; + + ~AtomicLinkedList() { + sweep([](T&&) {}); + } + + bool empty() const { + return list_.empty(); + } + + /** + * Atomically insert t at the head of the list. + * @return True if the inserted element is the only one in the list + * after the call. + */ + bool insertHead(T t) { + auto wrapper = std::make_unique<Wrapper>(std::move(t)); + + return list_.insertHead(wrapper.release()); + } + + /** + * Repeatedly pops element from head, + * and calls func() on the removed elements in the order from tail to head. + * Stops when the list is empty. + */ + template <typename F> + void sweep(F&& func) { + list_.sweep([&](Wrapper* wrapperPtr) mutable { + std::unique_ptr<Wrapper> wrapper(wrapperPtr); + + func(std::move(wrapper->data)); + }); + } + + /** + * Similar to sweep() but calls func() on elements in LIFO order. + * + * func() is called for all elements in the list at the moment + * reverseSweep() is called. Unlike sweep() it does not loop to ensure the + * list is empty at some point after the last invocation. This way callers + * can reason about the ordering: elements inserted since the last call to + * reverseSweep() will be provided in LIFO order. + * + * Example: if elements are inserted in the order 1-2-3, the callback is + * invoked 3-2-1. If the callback moves elements onto a stack, popping off + * the stack will produce the original insertion order 1-2-3. + */ + template <typename F> + void reverseSweep(F&& func) { + list_.reverseSweep([&](Wrapper* wrapperPtr) mutable { + std::unique_ptr<Wrapper> wrapper(wrapperPtr); + + func(std::move(wrapper->data)); + }); + } + + private: + struct Wrapper { + explicit Wrapper(T&& t) : data(std::move(t)) {} + + AtomicIntrusiveLinkedListHook<Wrapper> hook; + T data; + }; + AtomicIntrusiveLinkedList<Wrapper, &Wrapper::hook> list_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/AtomicUnorderedMap.h b/ios/Pods/Flipper-Folly/folly/AtomicUnorderedMap.h new file mode 100644 index 000000000..f7e84d7af --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/AtomicUnorderedMap.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstdint> +#include <functional> +#include <limits> +#include <stdexcept> +#include <system_error> +#include <type_traits> + +#include <folly/Conv.h> +#include <folly/Likely.h> +#include <folly/Random.h> +#include <folly/Traits.h> +#include <folly/detail/AtomicUnorderedMapUtils.h> +#include <folly/lang/Bits.h> +#include <folly/portability/SysMman.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +/// You're probably reading this because you are looking for an +/// AtomicUnorderedMap<K,V> that is fully general, highly concurrent (for +/// reads, writes, and iteration), and makes no performance compromises. +/// We haven't figured that one out yet. What you will find here is a +/// hash table implementation that sacrifices generality so that it can +/// give you all of the other things. +/// +/// LIMITATIONS: +/// +/// * Insert only (*) - the only write operation supported directly by +/// AtomicUnorderedInsertMap is findOrConstruct. There is a (*) because +/// values aren't moved, so you can roll your own concurrency control for +/// in-place updates of values (see MutableData and MutableAtom below), +/// but the hash table itself doesn't help you. +/// +/// * No resizing - you must specify the capacity up front, and once +/// the hash map gets full you won't be able to insert. Insert +/// performance will degrade once the load factor is high. Insert is +/// O(1/(1-actual_load_factor)). Note that this is a pretty strong +/// limitation, because you can't remove existing keys. +/// +/// * 2^30 maximum default capacity - by default AtomicUnorderedInsertMap +/// uses uint32_t internal indexes (and steals 2 bits), limiting you +/// to about a billion entries. If you need more you can fill in all +/// of the template params so you change IndexType to uint64_t, or you +/// can use AtomicUnorderedInsertMap64. 64-bit indexes will increase +/// the space over of the map, of course. +/// +/// WHAT YOU GET IN EXCHANGE: +/// +/// * Arbitrary key and value types - any K and V that can be used in a +/// std::unordered_map can be used here. In fact, the key and value +/// types don't even have to be copyable or moveable! +/// +/// * Keys and values in the map won't be moved - it is safe to keep +/// pointers or references to the keys and values in the map, because +/// they are never moved or destroyed (until the map itself is destroyed). +/// +/// * Iterators are never invalidated - writes don't invalidate iterators, +/// so you can scan and insert in parallel. +/// +/// * Fast wait-free reads - reads are usually only a single cache miss, +/// even when the hash table is very large. Wait-freedom means that +/// you won't see latency outliers even in the face of concurrent writes. +/// +/// * Lock-free insert - writes proceed in parallel. If a thread in the +/// middle of a write is unlucky and gets suspended, it doesn't block +/// anybody else. +/// +/// COMMENTS ON INSERT-ONLY +/// +/// This map provides wait-free linearizable reads and lock-free +/// linearizable inserts. Inserted values won't be moved, but no +/// concurrency control is provided for safely updating them. To remind +/// you of that fact they are only provided in const form. This is the +/// only simple safe thing to do while preserving something like the normal +/// std::map iteration form, which requires that iteration be exposed +/// via std::pair (and prevents encapsulation of access to the value). +/// +/// There are a couple of reasonable policies for doing in-place +/// concurrency control on the values. I am hoping that the policy can +/// be injected via the value type or an extra template param, to keep +/// the core AtomicUnorderedInsertMap insert-only: +/// +/// CONST: this is the currently implemented strategy, which is simple, +/// performant, and not that expressive. You can always put in a value +/// with a mutable field (see MutableAtom below), but that doesn't look +/// as pretty as it should. +/// +/// ATOMIC: for integers and integer-size trivially copyable structs +/// (via an adapter like tao/queues/AtomicStruct) the value can be a +/// std::atomic and read and written atomically. +/// +/// SEQ-LOCK: attach a counter incremented before and after write. +/// Writers serialize by using CAS to make an even->odd transition, +/// then odd->even after the write. Readers grab the value with memcpy, +/// checking sequence value before and after. Readers retry until they +/// see an even sequence number that doesn't change. This works for +/// larger structs, but still requires memcpy to be equivalent to copy +/// assignment, and it is no longer lock-free. It scales very well, +/// because the readers are still invisible (no cache line writes). +/// +/// LOCK: folly's SharedMutex would be a good choice here. +/// +/// MEMORY ALLOCATION +/// +/// Underlying memory is allocated as a big anonymous mmap chunk, which +/// might be cheaper than calloc() and is certainly not more expensive +/// for large maps. If the SkipKeyValueDeletion template param is true +/// then deletion of the map consists of unmapping the backing memory, +/// which is much faster than destructing all of the keys and values. +/// Feel free to override if std::is_trivial_destructor isn't recognizing +/// the triviality of your destructors. +template < + typename Key, + typename Value, + typename Hash = std::hash<Key>, + typename KeyEqual = std::equal_to<Key>, + bool SkipKeyValueDeletion = + (std::is_trivially_destructible<Key>::value && + std::is_trivially_destructible<Value>::value), + template <typename> class Atom = std::atomic, + typename IndexType = uint32_t, + typename Allocator = folly::detail::MMapAlloc> + +struct AtomicUnorderedInsertMap { + typedef Key key_type; + typedef Value mapped_type; + typedef std::pair<Key, Value> value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef Hash hasher; + typedef KeyEqual key_equal; + typedef const value_type& const_reference; + + typedef struct ConstIterator { + ConstIterator(const AtomicUnorderedInsertMap& owner, IndexType slot) + : owner_(owner), slot_(slot) {} + + ConstIterator(const ConstIterator&) = default; + ConstIterator& operator=(const ConstIterator&) = default; + + const value_type& operator*() const { + return owner_.slots_[slot_].keyValue(); + } + + const value_type* operator->() const { + return &owner_.slots_[slot_].keyValue(); + } + + // pre-increment + const ConstIterator& operator++() { + while (slot_ > 0) { + --slot_; + if (owner_.slots_[slot_].state() == LINKED) { + break; + } + } + return *this; + } + + // post-increment + ConstIterator operator++(int /* dummy */) { + auto prev = *this; + ++*this; + return prev; + } + + bool operator==(const ConstIterator& rhs) const { + return slot_ == rhs.slot_; + } + bool operator!=(const ConstIterator& rhs) const { + return !(*this == rhs); + } + + private: + const AtomicUnorderedInsertMap& owner_; + IndexType slot_; + } const_iterator; + + friend ConstIterator; + + /// Constructs a map that will support the insertion of maxSize key-value + /// pairs without exceeding the max load factor. Load factors of greater + /// than 1 are not supported, and once the actual load factor of the + /// map approaches 1 the insert performance will suffer. The capacity + /// is limited to 2^30 (about a billion) for the default IndexType, + /// beyond which we will throw invalid_argument. + explicit AtomicUnorderedInsertMap( + size_t maxSize, + float maxLoadFactor = 0.8f, + const Allocator& alloc = Allocator()) + : allocator_(alloc) { + size_t capacity = size_t(maxSize / std::min(1.0f, maxLoadFactor) + 128); + size_t avail = size_t{1} << (8 * sizeof(IndexType) - 2); + if (capacity > avail && maxSize < avail) { + // we'll do our best + capacity = avail; + } + if (capacity < maxSize || capacity > avail) { + throw std::invalid_argument( + "AtomicUnorderedInsertMap capacity must fit in IndexType with 2 bits " + "left over"); + } + + numSlots_ = capacity; + slotMask_ = folly::nextPowTwo(capacity * 4) - 1; + mmapRequested_ = sizeof(Slot) * capacity; + slots_ = reinterpret_cast<Slot*>(allocator_.allocate(mmapRequested_)); + zeroFillSlots(); + // mark the zero-th slot as in-use but not valid, since that happens + // to be our nil value + slots_[0].stateUpdate(EMPTY, CONSTRUCTING); + } + + ~AtomicUnorderedInsertMap() { + if (!SkipKeyValueDeletion) { + for (size_t i = 1; i < numSlots_; ++i) { + slots_[i].~Slot(); + } + } + allocator_.deallocate(reinterpret_cast<char*>(slots_), mmapRequested_); + } + + /// Searches for the key, returning (iter,false) if it is found. + /// If it is not found calls the functor Func with a void* argument + /// that is raw storage suitable for placement construction of a Value + /// (see raw_value_type), then returns (iter,true). May call Func and + /// then return (iter,false) if there are other concurrent writes, in + /// which case the newly constructed value will be immediately destroyed. + /// + /// This function does not block other readers or writers. If there + /// are other concurrent writes, many parallel calls to func may happen + /// and only the first one to complete will win. The values constructed + /// by the other calls to func will be destroyed. + /// + /// Usage: + /// + /// AtomicUnorderedInsertMap<std::string,std::string> memo; + /// + /// auto value = memo.findOrConstruct(key, [=](void* raw) { + /// new (raw) std::string(computation(key)); + /// })->first; + template <typename Func> + std::pair<const_iterator, bool> findOrConstruct(const Key& key, Func&& func) { + auto const slot = keyToSlotIdx(key); + auto prev = slots_[slot].headAndState_.load(std::memory_order_acquire); + + auto existing = find(key, slot); + if (existing != 0) { + return std::make_pair(ConstIterator(*this, existing), false); + } + + auto idx = allocateNear(slot); + new (&slots_[idx].keyValue().first) Key(key); + func(static_cast<void*>(&slots_[idx].keyValue().second)); + + while (true) { + slots_[idx].next_ = prev >> 2; + + // we can merge the head update and the CONSTRUCTING -> LINKED update + // into a single CAS if slot == idx (which should happen often) + auto after = idx << 2; + if (slot == idx) { + after += LINKED; + } else { + after += (prev & 3); + } + + if (slots_[slot].headAndState_.compare_exchange_strong(prev, after)) { + // success + if (idx != slot) { + slots_[idx].stateUpdate(CONSTRUCTING, LINKED); + } + return std::make_pair(ConstIterator(*this, idx), true); + } + // compare_exchange_strong updates its first arg on failure, so + // there is no need to reread prev + + existing = find(key, slot); + if (existing != 0) { + // our allocated key and value are no longer needed + slots_[idx].keyValue().first.~Key(); + slots_[idx].keyValue().second.~Value(); + slots_[idx].stateUpdate(CONSTRUCTING, EMPTY); + + return std::make_pair(ConstIterator(*this, existing), false); + } + } + } + + /// This isn't really emplace, but it is what we need to test. + /// Eventually we can duplicate all of the std::pair constructor + /// forms, including a recursive tuple forwarding template + /// http://functionalcpp.wordpress.com/2013/08/28/tuple-forwarding/). + template <class K, class V> + std::pair<const_iterator, bool> emplace(const K& key, V&& value) { + return findOrConstruct( + key, [&](void* raw) { new (raw) Value(std::forward<V>(value)); }); + } + + const_iterator find(const Key& key) const { + return ConstIterator(*this, find(key, keyToSlotIdx(key))); + } + + const_iterator cbegin() const { + IndexType slot = numSlots_ - 1; + while (slot > 0 && slots_[slot].state() != LINKED) { + --slot; + } + return ConstIterator(*this, slot); + } + + const_iterator cend() const { + return ConstIterator(*this, 0); + } + + private: + enum : IndexType { + kMaxAllocationTries = 1000, // after this we throw + }; + + enum BucketState : IndexType { + EMPTY = 0, + CONSTRUCTING = 1, + LINKED = 2, + }; + + /// Lock-free insertion is easiest by prepending to collision chains. + /// A large chaining hash table takes two cache misses instead of + /// one, however. Our solution is to colocate the bucket storage and + /// the head storage, so that even though we are traversing chains we + /// are likely to stay within the same cache line. Just make sure to + /// traverse head before looking at any keys. This strategy gives us + /// 32 bit pointers and fast iteration. + struct Slot { + /// The bottom two bits are the BucketState, the rest is the index + /// of the first bucket for the chain whose keys map to this slot. + /// When things are going well the head usually links to this slot, + /// but that doesn't always have to happen. + Atom<IndexType> headAndState_; + + /// The next bucket in the chain + IndexType next_; + + /// Key and Value + aligned_storage_for_t<value_type> raw_; + + ~Slot() { + auto s = state(); + assert(s == EMPTY || s == LINKED); + if (s == LINKED) { + keyValue().first.~Key(); + keyValue().second.~Value(); + } + } + + BucketState state() const { + return BucketState(headAndState_.load(std::memory_order_acquire) & 3); + } + + void stateUpdate(BucketState before, BucketState after) { + assert(state() == before); + headAndState_ += (after - before); + } + + value_type& keyValue() { + assert(state() != EMPTY); + return *static_cast<value_type*>(static_cast<void*>(&raw_)); + } + + const value_type& keyValue() const { + assert(state() != EMPTY); + return *static_cast<const value_type*>(static_cast<const void*>(&raw_)); + } + }; + + // We manually manage the slot memory so we can bypass initialization + // (by getting a zero-filled mmap chunk) and optionally destruction of + // the slots + + size_t mmapRequested_; + size_t numSlots_; + + /// tricky, see keyToSlodIdx + size_t slotMask_; + + Allocator allocator_; + Slot* slots_; + + IndexType keyToSlotIdx(const Key& key) const { + size_t h = hasher()(key); + h &= slotMask_; + while (h >= numSlots_) { + h -= numSlots_; + } + return h; + } + + IndexType find(const Key& key, IndexType slot) const { + KeyEqual ke = {}; + auto hs = slots_[slot].headAndState_.load(std::memory_order_acquire); + for (slot = hs >> 2; slot != 0; slot = slots_[slot].next_) { + if (ke(key, slots_[slot].keyValue().first)) { + return slot; + } + } + return 0; + } + + /// Allocates a slot and returns its index. Tries to put it near + /// slots_[start]. + IndexType allocateNear(IndexType start) { + for (IndexType tries = 0; tries < kMaxAllocationTries; ++tries) { + auto slot = allocationAttempt(start, tries); + auto prev = slots_[slot].headAndState_.load(std::memory_order_acquire); + if ((prev & 3) == EMPTY && + slots_[slot].headAndState_.compare_exchange_strong( + prev, prev + CONSTRUCTING - EMPTY)) { + return slot; + } + } + throw std::bad_alloc(); + } + + /// Returns the slot we should attempt to allocate after tries failed + /// tries, starting from the specified slot. This is pulled out so we + /// can specialize it differently during deterministic testing + IndexType allocationAttempt(IndexType start, IndexType tries) const { + if (LIKELY(tries < 8 && start + tries < numSlots_)) { + return IndexType(start + tries); + } else { + IndexType rv; + if (sizeof(IndexType) <= 4) { + rv = IndexType(folly::Random::rand32(numSlots_)); + } else { + rv = IndexType(folly::Random::rand64(numSlots_)); + } + assert(rv < numSlots_); + return rv; + } + } + + void zeroFillSlots() { + using folly::detail::GivesZeroFilledMemory; + if (!GivesZeroFilledMemory<Allocator>::value) { + memset(static_cast<void*>(slots_), 0, mmapRequested_); + } + } +}; + +/// AtomicUnorderedInsertMap64 is just a type alias that makes it easier +/// to select a 64 bit slot index type. Use this if you need a capacity +/// bigger than 2^30 (about a billion). This increases memory overheads, +/// obviously. +template < + typename Key, + typename Value, + typename Hash = std::hash<Key>, + typename KeyEqual = std::equal_to<Key>, + bool SkipKeyValueDeletion = + (std::is_trivially_destructible<Key>::value && + std::is_trivially_destructible<Value>::value), + template <typename> class Atom = std::atomic, + typename Allocator = folly::detail::MMapAlloc> +using AtomicUnorderedInsertMap64 = AtomicUnorderedInsertMap< + Key, + Value, + Hash, + KeyEqual, + SkipKeyValueDeletion, + Atom, + uint64_t, + Allocator>; + +/// MutableAtom is a tiny wrapper than gives you the option of atomically +/// updating values inserted into an AtomicUnorderedInsertMap<K, +/// MutableAtom<V>>. This relies on AtomicUnorderedInsertMap's guarantee +/// that it doesn't move values. +template <typename T, template <typename> class Atom = std::atomic> +struct MutableAtom { + mutable Atom<T> data; + + explicit MutableAtom(const T& init) : data(init) {} +}; + +/// MutableData is a tiny wrapper than gives you the option of using an +/// external concurrency control mechanism to updating values inserted +/// into an AtomicUnorderedInsertMap. +template <typename T> +struct MutableData { + mutable T data; + explicit MutableData(const T& init) : data(init) {} +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Benchmark.cpp b/ios/Pods/Flipper-Folly/folly/Benchmark.cpp new file mode 100644 index 000000000..389ee46a1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Benchmark.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) + +#include <folly/Benchmark.h> + +#include <algorithm> +#include <cmath> +#include <cstring> +#include <iostream> +#include <limits> +#include <map> +#include <memory> +#include <numeric> +#include <utility> +#include <vector> + +#include <boost/regex.hpp> + +#include <folly/MapUtil.h> +#include <folly/String.h> +#include <folly/container/Foreach.h> +#include <folly/json.h> + +using namespace std; + +DEFINE_bool(benchmark, false, "Run benchmarks."); +DEFINE_bool(json, false, "Output in JSON format."); +DEFINE_bool(json_verbose, false, "Output in verbose JSON format."); + +DEFINE_string( + bm_regex, + "", + "Only benchmarks whose names match this regex will be run."); + +DEFINE_int64( + bm_min_usec, + 100, + "Minimum # of microseconds we'll accept for each benchmark."); + +DEFINE_int32( + bm_min_iters, + 1, + "Minimum # of iterations we'll try for each benchmark."); + +DEFINE_int64( + bm_max_iters, + 1 << 30, + "Maximum # of iterations we'll try for each benchmark."); + +DEFINE_int32( + bm_max_secs, + 1, + "Maximum # of seconds we'll spend on each benchmark."); + +namespace folly { + +std::chrono::high_resolution_clock::duration BenchmarkSuspender::timeSpent; + +typedef function<detail::TimeIterData(unsigned int)> BenchmarkFun; + +vector<detail::BenchmarkRegistration>& benchmarks() { + static vector<detail::BenchmarkRegistration> _benchmarks; + return _benchmarks; +} + +#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline +#define FB_STRINGIZE_X2(x) FOLLY_PP_STRINGIZE(x) + +// Add the global baseline +BENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE) { +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else + asm volatile(""); +#endif +} + +size_t getGlobalBenchmarkBaselineIndex() { + const char* global = FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE); + auto it = std::find_if( + benchmarks().begin(), + benchmarks().end(), + [global](const detail::BenchmarkRegistration& v) { + return v.name == global; + }); + CHECK(it != benchmarks().end()); + return size_t(std::distance(benchmarks().begin(), it)); +} + +#undef FB_STRINGIZE_X2 +#undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE + +void detail::addBenchmarkImpl( + const char* file, + StringPiece name, + BenchmarkFun fun, + bool useCounter) { + benchmarks().push_back({file, name.str(), std::move(fun), useCounter}); +} + +static std::pair<double, UserCounters> runBenchmarkGetNSPerIteration( + const BenchmarkFun& fun, + const double globalBaseline) { + using std::chrono::duration_cast; + using std::chrono::high_resolution_clock; + using std::chrono::microseconds; + using std::chrono::nanoseconds; + using std::chrono::seconds; + + // They key here is accuracy; too low numbers means the accuracy was + // coarse. We up the ante until we get to at least minNanoseconds + // timings. + static_assert( + std::is_same<high_resolution_clock::duration, nanoseconds>::value, + "High resolution clock must be nanosecond resolution."); + // We choose a minimum minimum (sic) of 100,000 nanoseconds, but if + // the clock resolution is worse than that, it will be larger. In + // essence we're aiming at making the quantization noise 0.01%. + static const auto minNanoseconds = std::max<nanoseconds>( + nanoseconds(100000), microseconds(FLAGS_bm_min_usec)); + + // We do measurements in several epochs and take the minimum, to + // account for jitter. + static const unsigned int epochs = 1000; + // We establish a total time budget as we don't want a measurement + // to take too long. This will curtail the number of actual epochs. + const auto timeBudget = seconds(FLAGS_bm_max_secs); + auto global = high_resolution_clock::now(); + + std::vector<std::pair<double, UserCounters>> epochResults(epochs); + size_t actualEpochs = 0; + + for (; actualEpochs < epochs; ++actualEpochs) { + const auto maxIters = uint32_t(FLAGS_bm_max_iters); + for (auto n = uint32_t(FLAGS_bm_min_iters); n < maxIters; n *= 2) { + detail::TimeIterData timeIterData = fun(static_cast<unsigned int>(n)); + if (timeIterData.duration < minNanoseconds) { + continue; + } + // We got an accurate enough timing, done. But only save if + // smaller than the current result. + auto nsecs = duration_cast<nanoseconds>(timeIterData.duration); + epochResults[actualEpochs] = std::make_pair( + max(0.0, double(nsecs.count()) / timeIterData.niter - globalBaseline), + std::move(timeIterData.userCounters)); + // Done with the current epoch, we got a meaningful timing. + break; + } + auto now = high_resolution_clock::now(); + if (now - global >= timeBudget) { + // No more time budget available. + ++actualEpochs; + break; + } + } + + // Current state of the art: get the minimum. After some + // experimentation, it seems taking the minimum is the best. + auto iter = min_element( + epochResults.begin(), + epochResults.begin() + actualEpochs, + [](const auto& a, const auto& b) { return a.first < b.first; }); + + // If the benchmark was basically drowned in baseline noise, it's + // possible it became negative. + return std::make_pair(max(0.0, iter->first), iter->second); +} + +struct ScaleInfo { + double boundary; + const char* suffix; +}; + +static const ScaleInfo kTimeSuffixes[]{ + {365.25 * 24 * 3600, "years"}, + {24 * 3600, "days"}, + {3600, "hr"}, + {60, "min"}, + {1, "s"}, + {1E-3, "ms"}, + {1E-6, "us"}, + {1E-9, "ns"}, + {1E-12, "ps"}, + {1E-15, "fs"}, + {0, nullptr}, +}; + +static const ScaleInfo kMetricSuffixes[]{ + {1E24, "Y"}, // yotta + {1E21, "Z"}, // zetta + {1E18, "X"}, // "exa" written with suffix 'X' so as to not create + // confusion with scientific notation + {1E15, "P"}, // peta + {1E12, "T"}, // terra + {1E9, "G"}, // giga + {1E6, "M"}, // mega + {1E3, "K"}, // kilo + {1, ""}, + {1E-3, "m"}, // milli + {1E-6, "u"}, // micro + {1E-9, "n"}, // nano + {1E-12, "p"}, // pico + {1E-15, "f"}, // femto + {1E-18, "a"}, // atto + {1E-21, "z"}, // zepto + {1E-24, "y"}, // yocto + {0, nullptr}, +}; + +static string +humanReadable(double n, unsigned int decimals, const ScaleInfo* scales) { + if (std::isinf(n) || std::isnan(n)) { + return folly::to<string>(n); + } + + const double absValue = fabs(n); + const ScaleInfo* scale = scales; + while (absValue < scale[0].boundary && scale[1].suffix != nullptr) { + ++scale; + } + + const double scaledValue = n / scale->boundary; + return stringPrintf("%.*f%s", decimals, scaledValue, scale->suffix); +} + +static string readableTime(double n, unsigned int decimals) { + return humanReadable(n, decimals, kTimeSuffixes); +} + +static string metricReadable(double n, unsigned int decimals) { + return humanReadable(n, decimals, kMetricSuffixes); +} + +namespace { +class BenchmarkResultsPrinter { + public: + BenchmarkResultsPrinter() = default; + explicit BenchmarkResultsPrinter(std::set<std::string> counterNames) + : counterNames_(std::move(counterNames)), + namesLength_{std::accumulate( + counterNames_.begin(), + counterNames_.end(), + size_t{0}, + [](size_t acc, auto&& name) { return acc + 2 + name.length(); })} {} + + static constexpr unsigned int columns{76}; + void separator(char pad) { + puts(string(columns + namesLength_, pad).c_str()); + } + + void header(const string& file) { + separator('='); + printf("%-*srelative time/iter iters/s", columns - 28, file.c_str()); + for (auto const& name : counterNames_) { + printf(" %s", name.c_str()); + } + printf("\n"); + separator('='); + } + + void print(const vector<detail::BenchmarkResult>& data) { + for (auto& datum : data) { + auto file = datum.file; + if (file != lastFile_) { + // New file starting + header(file); + lastFile_ = file; + } + + string s = datum.name; + if (s == "-") { + separator('-'); + continue; + } + bool useBaseline /* = void */; + if (s[0] == '%') { + s.erase(0, 1); + useBaseline = true; + } else { + baselineNsPerIter_ = datum.timeInNs; + useBaseline = false; + } + s.resize(columns - 29, ' '); + auto nsPerIter = datum.timeInNs; + auto secPerIter = nsPerIter / 1E9; + auto itersPerSec = (secPerIter == 0) + ? std::numeric_limits<double>::infinity() + : (1 / secPerIter); + if (!useBaseline) { + // Print without baseline + printf( + "%*s %9s %7s", + static_cast<int>(s.size()), + s.c_str(), + readableTime(secPerIter, 2).c_str(), + metricReadable(itersPerSec, 2).c_str()); + } else { + // Print with baseline + auto rel = baselineNsPerIter_ / nsPerIter * 100.0; + printf( + "%*s %7.2f%% %9s %7s", + static_cast<int>(s.size()), + s.c_str(), + rel, + readableTime(secPerIter, 2).c_str(), + metricReadable(itersPerSec, 2).c_str()); + } + for (auto const& name : counterNames_) { + if (auto ptr = folly::get_ptr(datum.counters, name)) { + switch (ptr->type) { + case UserMetric::Type::TIME: + printf( + " %-*s", + int(name.length()), + readableTime(ptr->value, 2).c_str()); + break; + case UserMetric::Type::METRIC: + printf( + " %-*s", + int(name.length()), + metricReadable(ptr->value, 2).c_str()); + break; + case UserMetric::Type::CUSTOM: + default: + printf(" %-*" PRId64, int(name.length()), ptr->value); + } + } else { + printf(" %-*s", int(name.length()), "NaN"); + } + } + printf("\n"); + } + } + + private: + std::set<std::string> counterNames_; + size_t namesLength_{0}; + double baselineNsPerIter_{numeric_limits<double>::max()}; + string lastFile_; +}; +} // namespace + +static void printBenchmarkResultsAsJson( + const vector<detail::BenchmarkResult>& data) { + dynamic d = dynamic::object; + for (auto& datum : data) { + d[datum.name] = datum.timeInNs * 1000.; + } + + printf("%s\n", toPrettyJson(d).c_str()); +} + +static void printBenchmarkResultsAsVerboseJson( + const vector<detail::BenchmarkResult>& data) { + dynamic d; + benchmarkResultsToDynamic(data, d); + printf("%s\n", toPrettyJson(d).c_str()); +} + +static void printBenchmarkResults(const vector<detail::BenchmarkResult>& data) { + if (FLAGS_json_verbose) { + printBenchmarkResultsAsVerboseJson(data); + return; + } else if (FLAGS_json) { + printBenchmarkResultsAsJson(data); + return; + } + + CHECK(FLAGS_json_verbose || FLAGS_json) << "Cannot print benchmark results"; +} + +void benchmarkResultsToDynamic( + const vector<detail::BenchmarkResult>& data, + dynamic& out) { + out = dynamic::array; + for (auto& datum : data) { + if (!datum.counters.empty()) { + dynamic obj = dynamic::object; + for (auto& counter : datum.counters) { + dynamic counterInfo = dynamic::object; + counterInfo["value"] = counter.second.value; + counterInfo["type"] = static_cast<int>(counter.second.type); + obj[counter.first] = counterInfo; + } + out.push_back( + dynamic::array(datum.file, datum.name, datum.timeInNs, obj)); + } else { + out.push_back(dynamic::array(datum.file, datum.name, datum.timeInNs)); + } + } +} + +void benchmarkResultsFromDynamic( + const dynamic& d, + vector<detail::BenchmarkResult>& results) { + for (auto& datum : d) { + results.push_back({datum[0].asString(), + datum[1].asString(), + datum[2].asDouble(), + UserCounters{}}); + } +} + +static pair<StringPiece, StringPiece> resultKey( + const detail::BenchmarkResult& result) { + return pair<StringPiece, StringPiece>(result.file, result.name); +} + +void printResultComparison( + const vector<detail::BenchmarkResult>& base, + const vector<detail::BenchmarkResult>& test) { + map<pair<StringPiece, StringPiece>, double> baselines; + + for (auto& baseResult : base) { + baselines[resultKey(baseResult)] = baseResult.timeInNs; + } + // + // Width available + static const unsigned int columns = 76; + + // Compute the longest benchmark name + size_t longestName = 0; + for (auto& datum : test) { + longestName = max(longestName, datum.name.size()); + } + + // Print a horizontal rule + auto separator = [&](char pad) { puts(string(columns, pad).c_str()); }; + + // Print header for a file + auto header = [&](const string& file) { + separator('='); + printf("%-*srelative time/iter iters/s", columns - 28, file.c_str()); + separator('='); + }; + + string lastFile; + + for (auto& datum : test) { + folly::Optional<double> baseline = + folly::get_optional(baselines, resultKey(datum)); + auto file = datum.file; + if (file != lastFile) { + // New file starting + header(file); + lastFile = file; + } + + string s = datum.name; + if (s == "-") { + separator('-'); + continue; + } + if (s[0] == '%') { + s.erase(0, 1); + } + s.resize(columns - 29, ' '); + auto nsPerIter = datum.timeInNs; + auto secPerIter = nsPerIter / 1E9; + auto itersPerSec = (secPerIter == 0) + ? std::numeric_limits<double>::infinity() + : (1 / secPerIter); + if (!baseline) { + // Print without baseline + printf( + "%*s %9s %7s\n", + static_cast<int>(s.size()), + s.c_str(), + readableTime(secPerIter, 2).c_str(), + metricReadable(itersPerSec, 2).c_str()); + } else { + // Print with baseline + auto rel = *baseline / nsPerIter * 100.0; + printf( + "%*s %7.2f%% %9s %7s\n", + static_cast<int>(s.size()), + s.c_str(), + rel, + readableTime(secPerIter, 2).c_str(), + metricReadable(itersPerSec, 2).c_str()); + } + } + separator('='); +} + +void checkRunMode() { + if (folly::kIsDebug || folly::kIsSanitize) { + std::cerr << "WARNING: Benchmark running " + << (folly::kIsDebug ? "in DEBUG mode" : "with SANITIZERS") + << std::endl; + } +} + +void runBenchmarks() { + CHECK(!benchmarks().empty()); + + checkRunMode(); + + vector<detail::BenchmarkResult> results; + results.reserve(benchmarks().size() - 1); + + std::unique_ptr<boost::regex> bmRegex; + if (!FLAGS_bm_regex.empty()) { + bmRegex = std::make_unique<boost::regex>(FLAGS_bm_regex); + } + + // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS. + + size_t baselineIndex = getGlobalBenchmarkBaselineIndex(); + + auto const globalBaseline = + runBenchmarkGetNSPerIteration(benchmarks()[baselineIndex].func, 0); + + bool useCounter = + std::any_of(benchmarks().begin(), benchmarks().end(), [](const auto& bm) { + return bm.useCounter; + }); + BenchmarkResultsPrinter printer; + std::set<std::string> counterNames; + FOR_EACH_RANGE (i, 0, benchmarks().size()) { + if (i == baselineIndex) { + continue; + } + std::pair<double, UserCounters> elapsed; + auto& bm = benchmarks()[i]; + if (bm.name != "-") { // skip separators + if (bmRegex && !boost::regex_search(bm.name, *bmRegex)) { + continue; + } + elapsed = runBenchmarkGetNSPerIteration(bm.func, globalBaseline.first); + } + + // if customized user counters is used, it cannot print the result in real + // time as it needs to run all cases first to know the complete set of + // counters have been used, then the header can be printed out properly + if (!FLAGS_json_verbose && !FLAGS_json && !useCounter) { + printer.print({{bm.file, bm.name, elapsed.first, elapsed.second}}); + } else { + results.push_back({bm.file, bm.name, elapsed.first, elapsed.second}); + } + + // get all counter names + for (auto const& kv : elapsed.second) { + counterNames.insert(kv.first); + } + } + + // PLEASE MAKE NOISE. MEASUREMENTS DONE. + if (FLAGS_json_verbose || FLAGS_json) { + printBenchmarkResults(results); + } else { + printer = BenchmarkResultsPrinter{std::move(counterNames)}; + printer.print(results); + printer.separator('='); + } + + checkRunMode(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Benchmark.h b/ios/Pods/Flipper-Folly/folly/Benchmark.h new file mode 100644 index 000000000..bd1673ade --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Benchmark.h @@ -0,0 +1,684 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE +#include <folly/Range.h> +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> +#include <folly/portability/GFlags.h> + +#include <cassert> +#include <chrono> +#include <functional> +#include <limits> +#include <type_traits> +#include <unordered_map> + +#include <boost/function_types/function_arity.hpp> +#include <glog/logging.h> + +DECLARE_bool(benchmark); + +namespace folly { + +/** + * Runs all benchmarks defined. Usually put in main(). + */ +void runBenchmarks(); + +/** + * Runs all benchmarks defined if and only if the --benchmark flag has + * been passed to the program. Usually put in main(). + */ +inline bool runBenchmarksOnFlag() { + if (FLAGS_benchmark) { + runBenchmarks(); + } + return FLAGS_benchmark; +} + +class UserMetric { + public: + enum class Type { CUSTOM, TIME, METRIC }; + + int64_t value{}; + Type type{Type::CUSTOM}; + + UserMetric() = default; + /* implicit */ UserMetric(int64_t val, Type typ = Type::CUSTOM) + : value(val), type(typ) {} +}; + +using UserCounters = std::unordered_map<std::string, UserMetric>; + +namespace detail { +struct TimeIterData { + std::chrono::high_resolution_clock::duration duration; + unsigned int niter; + UserCounters userCounters; +}; + +using BenchmarkFun = std::function<TimeIterData(unsigned int)>; + +struct BenchmarkRegistration { + std::string file; + std::string name; + BenchmarkFun func; + bool useCounter = false; +}; + +struct BenchmarkResult { + std::string file; + std::string name; + double timeInNs; + UserCounters counters; +}; + +/** + * Adds a benchmark wrapped in a std::function. Only used + * internally. Pass by value is intentional. + */ +void addBenchmarkImpl( + const char* file, + StringPiece name, + BenchmarkFun, + bool useCounter); + +} // namespace detail + +/** + * Supporting type for BENCHMARK_SUSPEND defined below. + */ +struct BenchmarkSuspender { + using Clock = std::chrono::high_resolution_clock; + using TimePoint = Clock::time_point; + using Duration = Clock::duration; + + BenchmarkSuspender() { + start = Clock::now(); + } + + BenchmarkSuspender(const BenchmarkSuspender&) = delete; + BenchmarkSuspender(BenchmarkSuspender&& rhs) noexcept { + start = rhs.start; + rhs.start = {}; + } + + BenchmarkSuspender& operator=(const BenchmarkSuspender&) = delete; + BenchmarkSuspender& operator=(BenchmarkSuspender&& rhs) noexcept { + if (start != TimePoint{}) { + tally(); + } + start = rhs.start; + rhs.start = {}; + return *this; + } + + ~BenchmarkSuspender() { + if (start != TimePoint{}) { + tally(); + } + } + + void dismiss() { + assert(start != TimePoint{}); + tally(); + start = {}; + } + + void rehire() { + assert(start == TimePoint{}); + start = Clock::now(); + } + + template <class F> + auto dismissing(F f) -> invoke_result_t<F> { + SCOPE_EXIT { + rehire(); + }; + dismiss(); + return f(); + } + + /** + * This is for use inside of if-conditions, used in BENCHMARK macros. + * If-conditions bypass the explicit on operator bool. + */ + explicit operator bool() const { + return false; + } + + /** + * Accumulates time spent outside benchmark. + */ + static Duration timeSpent; + + private: + void tally() { + auto end = Clock::now(); + timeSpent += end - start; + start = end; + } + + TimePoint start; +}; + +/** + * Adds a benchmark. Usually not called directly but instead through + * the macro BENCHMARK defined below. The lambda function involved + * must take exactly one parameter of type unsigned, and the benchmark + * uses it with counter semantics (iteration occurs inside the + * function). + */ +template <typename Lambda> +typename std::enable_if<folly::is_invocable_v<Lambda, unsigned>>::type +addBenchmark(const char* file, StringPiece name, Lambda&& lambda) { + auto execute = [=](unsigned int times) { + BenchmarkSuspender::timeSpent = {}; + unsigned int niter; + + // CORE MEASUREMENT STARTS + auto start = std::chrono::high_resolution_clock::now(); + niter = lambda(times); + auto end = std::chrono::high_resolution_clock::now(); + // CORE MEASUREMENT ENDS + return detail::TimeIterData{ + (end - start) - BenchmarkSuspender::timeSpent, niter, UserCounters{}}; + }; + + detail::addBenchmarkImpl(file, name, detail::BenchmarkFun(execute), false); +} + +/** + * Adds a benchmark. Usually not called directly but instead through + * the macro BENCHMARK defined below. The lambda function involved + * must take zero parameters, and the benchmark calls it repeatedly + * (iteration occurs outside the function). + */ +template <typename Lambda> +typename std::enable_if<folly::is_invocable_v<Lambda>>::type +addBenchmark(const char* file, StringPiece name, Lambda&& lambda) { + addBenchmark(file, name, [=](unsigned int times) { + unsigned int niter = 0; + while (times-- > 0) { + niter += lambda(); + } + return niter; + }); +} + +/** + * similar as previous two template specialization, but lambda will also take + * customized counters in the following two cases + */ +template <typename Lambda> +typename std::enable_if< + folly::is_invocable_v<Lambda, UserCounters&, unsigned>>::type +addBenchmark(const char* file, StringPiece name, Lambda&& lambda) { + auto execute = [=](unsigned int times) { + BenchmarkSuspender::timeSpent = {}; + unsigned int niter; + + // CORE MEASUREMENT STARTS + auto start = std::chrono::high_resolution_clock::now(); + UserCounters counters; + niter = lambda(counters, times); + auto end = std::chrono::high_resolution_clock::now(); + // CORE MEASUREMENT ENDS + return detail::TimeIterData{ + (end - start) - BenchmarkSuspender::timeSpent, niter, counters}; + }; + + detail::addBenchmarkImpl( + file, + name, + std::function<detail::TimeIterData(unsigned int)>(execute), + true); +} + +template <typename Lambda> +typename std::enable_if<folly::is_invocable_v<Lambda, UserCounters&>>::type +addBenchmark(const char* file, StringPiece name, Lambda&& lambda) { + addBenchmark(file, name, [=](UserCounters& counters, unsigned int times) { + unsigned int niter = 0; + while (times-- > 0) { + niter += lambda(counters); + } + return niter; + }); +} + +/** + * Call doNotOptimizeAway(var) to ensure that var will be computed even + * post-optimization. Use it for variables that are computed during + * benchmarking but otherwise are useless. The compiler tends to do a + * good job at eliminating unused variables, and this function fools it + * into thinking var is in fact needed. + * + * Call makeUnpredictable(var) when you don't want the optimizer to use + * its knowledge of var to shape the following code. This is useful + * when constant propagation or power reduction is possible during your + * benchmark but not in real use cases. + */ + +#ifdef _MSC_VER + +#pragma optimize("", off) + +inline void doNotOptimizeDependencySink(const void*) {} + +#pragma optimize("", on) + +template <class T> +void doNotOptimizeAway(const T& datum) { + doNotOptimizeDependencySink(&datum); +} + +template <typename T> +void makeUnpredictable(T& datum) { + doNotOptimizeDependencySink(&datum); +} + +#else + +namespace detail { +template <typename T> +struct DoNotOptimizeAwayNeedsIndirect { + using Decayed = typename std::decay<T>::type; + + // First two constraints ensure it can be an "r" operand. + // std::is_pointer check is because callers seem to expect that + // doNotOptimizeAway(&x) is equivalent to doNotOptimizeAway(x). + constexpr static bool value = !folly::is_trivially_copyable<Decayed>::value || + sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value; +}; +} // namespace detail + +template <typename T> +auto doNotOptimizeAway(const T& datum) -> typename std::enable_if< + !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type { + // The "r" constraint forces the compiler to make datum available + // in a register to the asm block, which means that it must have + // computed/loaded it. We use this path for things that are <= + // sizeof(long) (they have to fit), trivial (otherwise the compiler + // doesn't want to put them in a register), and not a pointer (because + // doNotOptimizeAway(&foo) would otherwise be a foot gun that didn't + // necessarily compute foo). + // + // An earlier version of this method had a more permissive input operand + // constraint, but that caused unnecessary variation between clang and + // gcc benchmarks. + asm volatile("" ::"r"(datum)); +} + +template <typename T> +auto doNotOptimizeAway(const T& datum) -> typename std::enable_if< + detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type { + // This version of doNotOptimizeAway tells the compiler that the asm + // block will read datum from memory, and that in addition it might read + // or write from any memory location. If the memory clobber could be + // separated into input and output that would be preferrable. + asm volatile("" ::"m"(datum) : "memory"); +} + +template <typename T> +auto makeUnpredictable(T& datum) -> typename std::enable_if< + !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type { + asm volatile("" : "+r"(datum)); +} + +template <typename T> +auto makeUnpredictable(T& datum) -> typename std::enable_if< + detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type { + asm volatile("" ::"m"(datum) : "memory"); +} + +#endif + +struct dynamic; + +void benchmarkResultsToDynamic( + const std::vector<detail::BenchmarkResult>& data, + dynamic&); + +void benchmarkResultsFromDynamic( + const dynamic&, + std::vector<detail::BenchmarkResult>&); + +void printResultComparison( + const std::vector<detail::BenchmarkResult>& base, + const std::vector<detail::BenchmarkResult>& test); + +} // namespace folly + +/** + * Introduces a benchmark function. Used internally, see BENCHMARK and + * friends below. + */ + +#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \ + static void funName(paramType); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](paramType paramName) -> unsigned { \ + funName(paramName); \ + return rv; \ + }), \ + true); \ + static void funName(paramType paramName) + +#define BENCHMARK_IMPL_COUNTERS( \ + funName, stringName, counters, rv, paramType, paramName) \ + static void funName( \ + ::folly::UserCounters& FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType)); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](::folly::UserCounters& counters FOLLY_PP_DETAIL_APPEND_VA_ARG( \ + paramType paramName)) -> unsigned { \ + funName(counters FOLLY_PP_DETAIL_APPEND_VA_ARG(paramName)); \ + return rv; \ + }), \ + true); \ + static void funName(::folly::UserCounters& counters \ + FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType paramName)) + +/** + * Introduces a benchmark function with support for returning the actual + * number of iterations. Used internally, see BENCHMARK_MULTI and friends + * below. + */ +#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \ + static unsigned funName(paramType); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](paramType paramName) { return funName(paramName); }), \ + true); \ + static unsigned funName(paramType paramName) + +/** + * Introduces a benchmark function. Use with either one or two arguments. + * The first is the name of the benchmark. Use something descriptive, such + * as insertVectorBegin. The second argument may be missing, or could be a + * symbolic counter. The counter dictates how many internal iteration the + * benchmark does. Example: + * + * BENCHMARK(vectorPushBack) { + * vector<int> v; + * v.push_back(42); + * } + * + * BENCHMARK(insertVectorBegin, iters) { + * vector<int> v; + * FOR_EACH_RANGE (i, 0, iters) { + * v.insert(v.begin(), 42); + * } + * } + */ +#define BENCHMARK(name, ...) \ + BENCHMARK_IMPL( \ + name, \ + FOLLY_PP_STRINGIZE(name), \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) + +/** + * Allow users to record customized counter during benchmarking, + * there will be one extra column showing in the output result for each counter + * + * BENCHMARK_COUNTERS(insertVectorBegin, couters, iters) { + * vector<int> v; + * FOR_EACH_RANGE (i, 0, iters) { + * v.insert(v.begin(), 42); + * } + * BENCHMARK_SUSPEND { + * counters["foo"] = 10; + * } + * } + */ +#define BENCHMARK_COUNTERS(name, counters, ...) \ + BENCHMARK_IMPL_COUNTERS( \ + name, \ + FOLLY_PP_STRINGIZE(name), \ + counters, \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) +/** + * Like BENCHMARK above, but allows the user to return the actual + * number of iterations executed in the function body. This can be + * useful if the benchmark function doesn't know upfront how many + * iterations it's going to run or if it runs through a certain + * number of test cases, e.g.: + * + * BENCHMARK_MULTI(benchmarkSomething) { + * std::vector<int> testCases { 0, 1, 1, 2, 3, 5 }; + * for (int c : testCases) { + * doSomething(c); + * } + * return testCases.size(); + * } + */ +#define BENCHMARK_MULTI(name, ...) \ + BENCHMARK_MULTI_IMPL( \ + name, \ + FOLLY_PP_STRINGIZE(name), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) + +/** + * Defines a benchmark that passes a parameter to another one. This is + * common for benchmarks that need a "problem size" in addition to + * "number of iterations". Consider: + * + * void pushBack(uint32_t n, size_t initialSize) { + * vector<int> v; + * BENCHMARK_SUSPEND { + * v.resize(initialSize); + * } + * FOR_EACH_RANGE (i, 0, n) { + * v.push_back(i); + * } + * } + * BENCHMARK_PARAM(pushBack, 0) + * BENCHMARK_PARAM(pushBack, 1000) + * BENCHMARK_PARAM(pushBack, 1000000) + * + * The benchmark above estimates the speed of push_back at different + * initial sizes of the vector. The framework will pass 0, 1000, and + * 1000000 for initialSize, and the iteration count for n. + */ +#define BENCHMARK_PARAM(name, param) BENCHMARK_NAMED_PARAM(name, param, param) + +/** + * Same as BENCHMARK_PARAM, but allows one to return the actual number of + * iterations that have been run. + */ +#define BENCHMARK_PARAM_MULTI(name, param) \ + BENCHMARK_NAMED_PARAM_MULTI(name, param, param) + +/* + * Like BENCHMARK_PARAM(), but allows a custom name to be specified for each + * parameter, rather than using the parameter value. + * + * Useful when the parameter value is not a valid token for string pasting, + * of when you want to specify multiple parameter arguments. + * + * For example: + * + * void addValue(uint32_t n, int64_t bucketSize, int64_t min, int64_t max) { + * Histogram<int64_t> hist(bucketSize, min, max); + * int64_t num = min; + * FOR_EACH_RANGE (i, 0, n) { + * hist.addValue(num); + * ++num; + * if (num > max) { num = min; } + * } + * } + * + * BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100) + * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000) + * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000) + */ +#define BENCHMARK_NAMED_PARAM(name, param_name, ...) \ + BENCHMARK_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + iters, \ + unsigned, \ + iters) { \ + name(iters, ##__VA_ARGS__); \ + } + +/** + * Same as BENCHMARK_NAMED_PARAM, but allows one to return the actual number + * of iterations that have been run. + */ +#define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...) \ + BENCHMARK_MULTI_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + unsigned, \ + iters) { \ + return name(iters, ##__VA_ARGS__); \ + } + +/** + * Just like BENCHMARK, but prints the time relative to a + * baseline. The baseline is the most recent BENCHMARK() seen in + * the current scope. Example: + * + * // This is the baseline + * BENCHMARK(insertVectorBegin, n) { + * vector<int> v; + * FOR_EACH_RANGE (i, 0, n) { + * v.insert(v.begin(), 42); + * } + * } + * + * BENCHMARK_RELATIVE(insertListBegin, n) { + * list<int> s; + * FOR_EACH_RANGE (i, 0, n) { + * s.insert(s.begin(), 42); + * } + * } + * + * Any number of relative benchmark can be associated with a + * baseline. Another BENCHMARK() occurrence effectively establishes a + * new baseline. + */ +#define BENCHMARK_RELATIVE(name, ...) \ + BENCHMARK_IMPL( \ + name, \ + "%" FOLLY_PP_STRINGIZE(name), \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) + +#define BENCHMARK_COUNTERS_RELATIVE(name, counters, ...) \ + BENCHMARK_IMPL_COUNTERS( \ + name, \ + "%" FOLLY_PP_STRINGIZE(name), \ + counters, \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) +/** + * Same as BENCHMARK_RELATIVE, but allows one to return the actual number + * of iterations that have been run. + */ +#define BENCHMARK_RELATIVE_MULTI(name, ...) \ + BENCHMARK_MULTI_IMPL( \ + name, \ + "%" FOLLY_PP_STRINGIZE(name), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) + +/** + * A combination of BENCHMARK_RELATIVE and BENCHMARK_PARAM. + */ +#define BENCHMARK_RELATIVE_PARAM(name, param) \ + BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param) + +/** + * Same as BENCHMARK_RELATIVE_PARAM, but allows one to return the actual + * number of iterations that have been run. + */ +#define BENCHMARK_RELATIVE_PARAM_MULTI(name, param) \ + BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param, param) + +/** + * A combination of BENCHMARK_RELATIVE and BENCHMARK_NAMED_PARAM. + */ +#define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...) \ + BENCHMARK_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + "%" FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + iters, \ + unsigned, \ + iters) { \ + name(iters, ##__VA_ARGS__); \ + } + +/** + * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows one to return the + * actual number of iterations that have been run. + */ +#define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...) \ + BENCHMARK_MULTI_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + "%" FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + unsigned, \ + iters) { \ + return name(iters, ##__VA_ARGS__); \ + } + +/** + * Draws a line of dashes. + */ +#define BENCHMARK_DRAW_LINE() \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \ + true) + +/** + * Allows execution of code that doesn't count torward the benchmark's + * time budget. Example: + * + * BENCHMARK_START_GROUP(insertVectorBegin, n) { + * vector<int> v; + * BENCHMARK_SUSPEND { + * v.reserve(n); + * } + * FOR_EACH_RANGE (i, 0, n) { + * v.insert(v.begin(), 42); + * } + * } + */ +#define BENCHMARK_SUSPEND \ + if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) = \ + ::folly::BenchmarkSuspender()) { \ + } else diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FirebaseInstanceID.h b/ios/Pods/Flipper-Folly/folly/Bits.h similarity index 78% rename from ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FirebaseInstanceID.h rename to ios/Pods/Flipper-Folly/folly/Bits.h index 78c9ef161..1569d598c 100644 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/Public/FirebaseInstanceID.h +++ b/ios/Pods/Flipper-Folly/folly/Bits.h @@ -1,11 +1,11 @@ /* - * Copyright 2019 Google + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,4 +14,4 @@ * limitations under the License. */ -#import "FIRInstanceID.h" +#include <folly/lang/Bits.h> // @shim diff --git a/ios/Pods/Flipper-Folly/folly/CPortability.h b/ios/Pods/Flipper-Folly/folly/CPortability.h new file mode 100644 index 000000000..976daf069 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CPortability.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/* These definitions are in a separate file so that they + * may be included from C- as well as C++-based projects. */ + +#include <folly/portability/Config.h> + +/** + * Portable version check. + */ +#ifndef __GNUC_PREREQ +#if defined __GNUC__ && defined __GNUC_MINOR__ +/* nolint */ +#define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +/* nolint */ +#define __GNUC_PREREQ(maj, min) 0 +#endif +#endif + +// portable version check for clang +#ifndef __CLANG_PREREQ +#if defined __clang__ && defined __clang_major__ && defined __clang_minor__ +/* nolint */ +#define __CLANG_PREREQ(maj, min) \ + ((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min)) +#else +/* nolint */ +#define __CLANG_PREREQ(maj, min) 0 +#endif +#endif + +#if defined(__has_builtin) +#define FOLLY_HAS_BUILTIN(...) __has_builtin(__VA_ARGS__) +#else +#define FOLLY_HAS_BUILTIN(...) 0 +#endif + +#if defined(__has_feature) +#define FOLLY_HAS_FEATURE(...) __has_feature(__VA_ARGS__) +#else +#define FOLLY_HAS_FEATURE(...) 0 +#endif + +/* FOLLY_SANITIZE_ADDRESS is defined to 1 if the current compilation unit + * is being compiled with ASAN enabled. + * + * Beware when using this macro in a header file: this macro may change values + * across compilation units if some libraries are built with ASAN enabled + * and some built with ASAN disabled. For instance, this may occur, if folly + * itself was compiled without ASAN but a downstream project that uses folly is + * compiling with ASAN enabled. + * + * Use FOLLY_LIBRARY_SANITIZE_ADDRESS (defined in folly-config.h) to check if + * folly itself was compiled with ASAN enabled. + */ +#ifndef FOLLY_SANITIZE_ADDRESS +#if FOLLY_HAS_FEATURE(address_sanitizer) || __SANITIZE_ADDRESS__ +#define FOLLY_SANITIZE_ADDRESS 1 +#endif +#endif + +/* Define attribute wrapper for function attribute used to disable + * address sanitizer instrumentation. Unfortunately, this attribute + * has issues when inlining is used, so disable that as well. */ +#ifdef FOLLY_SANITIZE_ADDRESS +#if defined(__clang__) +#if __has_attribute(__no_sanitize__) +#define FOLLY_DISABLE_ADDRESS_SANITIZER \ + __attribute__((__no_sanitize__("address"), __noinline__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define FOLLY_DISABLE_ADDRESS_SANITIZER \ + __attribute__((__no_address_safety_analysis__, __noinline__)) +#elif __has_attribute(__no_sanitize_address__) +#define FOLLY_DISABLE_ADDRESS_SANITIZER \ + __attribute__((__no_sanitize_address__, __noinline__)) +#endif +#elif defined(__GNUC__) +#define FOLLY_DISABLE_ADDRESS_SANITIZER \ + __attribute__((__no_address_safety_analysis__, __noinline__)) +#endif +#endif +#ifndef FOLLY_DISABLE_ADDRESS_SANITIZER +#define FOLLY_DISABLE_ADDRESS_SANITIZER +#endif + +/* Define a convenience macro to test when thread sanitizer is being used + * across the different compilers (e.g. clang, gcc) */ +#ifndef FOLLY_SANITIZE_THREAD +#if FOLLY_HAS_FEATURE(thread_sanitizer) || __SANITIZE_THREAD__ +#define FOLLY_SANITIZE_THREAD 1 +#endif +#endif + +#if FOLLY_SANITIZE_THREAD +#define FOLLY_DISABLE_THREAD_SANITIZER \ + __attribute__((no_sanitize_thread, noinline)) +#else +#define FOLLY_DISABLE_THREAD_SANITIZER +#endif + +/** + * Define a convenience macro to test when memory sanitizer is being used + * across the different compilers (e.g. clang, gcc) + */ +#ifndef FOLLY_SANITIZE_MEMORY +#if FOLLY_HAS_FEATURE(memory_sanitizer) || __SANITIZE_MEMORY__ +#define FOLLY_SANITIZE_MEMORY 1 +#endif +#endif + +#if FOLLY_SANITIZE_MEMORY +#define FOLLY_DISABLE_MEMORY_SANITIZER \ + __attribute__((no_sanitize_memory, noinline)) +#else +#define FOLLY_DISABLE_MEMORY_SANITIZER +#endif + +/** + * Define a convenience macro to test when ASAN, UBSAN, TSAN or MSAN sanitizer + * are being used + */ +#ifndef FOLLY_SANITIZE +#if defined(FOLLY_SANITIZE_ADDRESS) || defined(FOLLY_SANITIZE_THREAD) || \ + defined(FOLLY_SANITIZE_MEMORY) +#define FOLLY_SANITIZE 1 +#endif +#endif + +#if FOLLY_SANITIZE +#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) \ + __attribute__((no_sanitize(__VA_ARGS__))) +#else +#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) +#endif // FOLLY_SANITIZE + +#define FOLLY_DISABLE_SANITIZERS \ + FOLLY_DISABLE_ADDRESS_SANITIZER FOLLY_DISABLE_THREAD_SANITIZER \ + FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER("undefined") + +/** + * Macro for marking functions as having public visibility. + */ +#if defined(__GNUC__) +#define FOLLY_EXPORT __attribute__((__visibility__("default"))) +#else +#define FOLLY_EXPORT +#endif + +// noinline +#ifdef _MSC_VER +#define FOLLY_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define FOLLY_NOINLINE __attribute__((__noinline__)) +#else +#define FOLLY_NOINLINE +#endif + +// always inline +#ifdef _MSC_VER +#define FOLLY_ALWAYS_INLINE __forceinline +#elif defined(__GNUC__) +#define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#else +#define FOLLY_ALWAYS_INLINE inline +#endif + +// attribute hidden +#if defined(_MSC_VER) +#define FOLLY_ATTR_VISIBILITY_HIDDEN +#elif defined(__GNUC__) +#define FOLLY_ATTR_VISIBILITY_HIDDEN __attribute__((__visibility__("hidden"))) +#else +#define FOLLY_ATTR_VISIBILITY_HIDDEN +#endif + +// An attribute for marking symbols as weak, if supported +#if FOLLY_HAVE_WEAK_SYMBOLS +#define FOLLY_ATTR_WEAK __attribute__((__weak__)) +#else +#define FOLLY_ATTR_WEAK +#endif + +// Microsoft ABI version (can be overridden manually if necessary) +#ifndef FOLLY_MICROSOFT_ABI_VER +#ifdef _MSC_VER +#define FOLLY_MICROSOFT_ABI_VER _MSC_VER +#endif +#endif + +// FOLLY_ERASE +// +// A conceptual attribute/syntax combo for erasing a function from the build +// artifacts and forcing all call-sites to inline the callee, at least as far +// as each compiler supports. +// +// Semantically includes the inline specifier. +#define FOLLY_ERASE FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN + +// FOLLY_ERASE_HACK_GCC +// +// Equivalent to FOLLY_ERASE, but without hiding under gcc. Useful when applied +// to a function which may sometimes be hidden separately, for example by being +// declared in an anonymous namespace, since in such cases with -Wattributes +// enabled, gcc would emit: 'visibility' attribute ignored. +// +// Semantically includes the inline specifier. +#if defined(__GNUC__) && !defined(__clang__) +#define FOLLY_ERASE_HACK_GCC FOLLY_ALWAYS_INLINE +#else +#define FOLLY_ERASE_HACK_GCC FOLLY_ERASE +#endif + +// FOLLY_ERASE_TRYCATCH +// +// Equivalent to FOLLY_ERASE, but for code which might contain explicit +// exception handling. Has the effect of FOLLY_ERASE, except under MSVC which +// warns about __forceinline when functions contain exception handling. +// +// Semantically includes the inline specifier. +#ifdef _MSC_VER +#define FOLLY_ERASE_TRYCATCH inline +#else +#define FOLLY_ERASE_TRYCATCH FOLLY_ERASE +#endif diff --git a/ios/Pods/Flipper-Folly/folly/CancellationToken-inl.h b/ios/Pods/Flipper-Folly/folly/CancellationToken-inl.h new file mode 100644 index 000000000..8ce5c5c79 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CancellationToken-inl.h @@ -0,0 +1,351 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <algorithm> +#include <cstdint> +#include <limits> +#include <utility> + +#include <glog/logging.h> + +namespace folly { + +namespace detail { + +// Internal cancellation state object. +class CancellationState { + public: + FOLLY_NODISCARD static CancellationStateSourcePtr create(); + + private: + // Constructed initially with a CancellationSource reference count of 1. + CancellationState() noexcept; + + ~CancellationState(); + + friend struct CancellationStateTokenDeleter; + friend struct CancellationStateSourceDeleter; + + void removeTokenReference() noexcept; + void removeSourceReference() noexcept; + + public: + FOLLY_NODISCARD CancellationStateTokenPtr addTokenReference() noexcept; + + FOLLY_NODISCARD CancellationStateSourcePtr addSourceReference() noexcept; + + bool tryAddCallback( + CancellationCallback* callback, + bool incrementRefCountIfSuccessful) noexcept; + + void removeCallback(CancellationCallback* callback) noexcept; + + bool isCancellationRequested() const noexcept; + bool canBeCancelled() const noexcept; + + // Request cancellation. + // Return 'true' if cancellation had already been requested. + // Return 'false' if this was the first thread to request + // cancellation. + bool requestCancellation() noexcept; + + private: + void lock() noexcept; + void unlock() noexcept; + void unlockAndIncrementTokenCount() noexcept; + void unlockAndDecrementTokenCount() noexcept; + bool tryLockAndCancelUnlessCancelled() noexcept; + + template <typename Predicate> + bool tryLock(Predicate predicate) noexcept; + + static bool canBeCancelled(std::uint64_t state) noexcept; + static bool isCancellationRequested(std::uint64_t state) noexcept; + static bool isLocked(std::uint64_t state) noexcept; + + static constexpr std::uint64_t kCancellationRequestedFlag = 1; + static constexpr std::uint64_t kLockedFlag = 2; + static constexpr std::uint64_t kTokenReferenceCountIncrement = 4; + static constexpr std::uint64_t kSourceReferenceCountIncrement = + std::uint64_t(1) << 33u; + static constexpr std::uint64_t kTokenReferenceCountMask = + (kSourceReferenceCountIncrement - 1u) - + (kTokenReferenceCountIncrement - 1u); + static constexpr std::uint64_t kSourceReferenceCountMask = + std::numeric_limits<std::uint64_t>::max() - + (kSourceReferenceCountIncrement - 1u); + + // Bit 0 - Cancellation Requested + // Bit 1 - Locked Flag + // Bits 2-32 - Token reference count (max ~2 billion) + // Bits 33-63 - Source reference count (max ~2 billion) + std::atomic<std::uint64_t> state_; + CancellationCallback* head_; + std::thread::id signallingThreadId_; +}; + +inline void CancellationStateTokenDeleter::operator()( + CancellationState* state) noexcept { + state->removeTokenReference(); +} + +inline void CancellationStateSourceDeleter::operator()( + CancellationState* state) noexcept { + state->removeSourceReference(); +} + +} // namespace detail + +inline CancellationToken::CancellationToken( + const CancellationToken& other) noexcept + : state_() { + if (other.state_) { + state_ = other.state_->addTokenReference(); + } +} + +inline CancellationToken::CancellationToken(CancellationToken&& other) noexcept + : state_(std::move(other.state_)) {} + +inline CancellationToken& CancellationToken::operator=( + const CancellationToken& other) noexcept { + if (state_ != other.state_) { + CancellationToken temp{other}; + swap(temp); + } + return *this; +} + +inline CancellationToken& CancellationToken::operator=( + CancellationToken&& other) noexcept { + state_ = std::move(other.state_); + return *this; +} + +inline bool CancellationToken::isCancellationRequested() const noexcept { + return state_ != nullptr && state_->isCancellationRequested(); +} + +inline bool CancellationToken::canBeCancelled() const noexcept { + return state_ != nullptr && state_->canBeCancelled(); +} + +inline void CancellationToken::swap(CancellationToken& other) noexcept { + std::swap(state_, other.state_); +} + +inline CancellationToken::CancellationToken( + detail::CancellationStateTokenPtr state) noexcept + : state_(std::move(state)) {} + +inline bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept { + return a.state_ == b.state_; +} + +inline bool operator!=( + const CancellationToken& a, + const CancellationToken& b) noexcept { + return !(a == b); +} + +inline CancellationSource::CancellationSource() + : state_(detail::CancellationState::create()) {} + +inline CancellationSource::CancellationSource( + const CancellationSource& other) noexcept + : state_() { + if (other.state_) { + state_ = other.state_->addSourceReference(); + } +} + +inline CancellationSource::CancellationSource( + CancellationSource&& other) noexcept + : state_(std::move(other.state_)) {} + +inline CancellationSource& CancellationSource::operator=( + const CancellationSource& other) noexcept { + if (state_ != other.state_) { + CancellationSource temp{other}; + swap(temp); + } + return *this; +} + +inline CancellationSource& CancellationSource::operator=( + CancellationSource&& other) noexcept { + state_ = std::move(other.state_); + return *this; +} + +inline CancellationSource CancellationSource::invalid() noexcept { + return CancellationSource{detail::CancellationStateSourcePtr{}}; +} + +inline bool CancellationSource::isCancellationRequested() const noexcept { + return state_ != nullptr && state_->isCancellationRequested(); +} + +inline bool CancellationSource::canBeCancelled() const noexcept { + return state_ != nullptr; +} + +inline CancellationToken CancellationSource::getToken() const noexcept { + if (state_ != nullptr) { + return CancellationToken{state_->addTokenReference()}; + } + return CancellationToken{}; +} + +inline bool CancellationSource::requestCancellation() const noexcept { + if (state_ != nullptr) { + return state_->requestCancellation(); + } + return false; +} + +inline void CancellationSource::swap(CancellationSource& other) noexcept { + std::swap(state_, other.state_); +} + +inline CancellationSource::CancellationSource( + detail::CancellationStateSourcePtr&& state) noexcept + : state_(std::move(state)) {} + +template < + typename Callable, + std::enable_if_t< + std::is_constructible<CancellationCallback::VoidFunction, Callable>:: + value, + int>> +inline CancellationCallback::CancellationCallback( + CancellationToken&& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast<Callable&&>(callable)), + destructorHasRunInsideCallback_(nullptr), + callbackCompleted_(false) { + if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, false)) { + state_ = ct.state_.release(); + } +} + +template < + typename Callable, + std::enable_if_t< + std::is_constructible<CancellationCallback::VoidFunction, Callable>:: + value, + int>> +inline CancellationCallback::CancellationCallback( + const CancellationToken& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast<Callable&&>(callable)), + destructorHasRunInsideCallback_(nullptr), + callbackCompleted_(false) { + if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, true)) { + state_ = ct.state_.get(); + } +} + +inline CancellationCallback::~CancellationCallback() { + if (state_ != nullptr) { + state_->removeCallback(this); + } +} + +inline void CancellationCallback::invokeCallback() noexcept { + // Invoke within a noexcept context so that we std::terminate() if it throws. + callback_(); +} + +namespace detail { + +inline CancellationStateSourcePtr CancellationState::create() { + return CancellationStateSourcePtr{new CancellationState()}; +} + +inline CancellationState::CancellationState() noexcept + : state_(kSourceReferenceCountIncrement), + head_(nullptr), + signallingThreadId_() {} + +inline CancellationStateTokenPtr +CancellationState::addTokenReference() noexcept { + state_.fetch_add(kTokenReferenceCountIncrement, std::memory_order_relaxed); + return CancellationStateTokenPtr{this}; +} + +inline void CancellationState::removeTokenReference() noexcept { + const auto oldState = state_.fetch_sub( + kTokenReferenceCountIncrement, std::memory_order_acq_rel); + DCHECK( + (oldState & kTokenReferenceCountMask) >= kTokenReferenceCountIncrement); + if (oldState < (2 * kTokenReferenceCountIncrement)) { + delete this; + } +} + +inline CancellationStateSourcePtr +CancellationState::addSourceReference() noexcept { + state_.fetch_add(kSourceReferenceCountIncrement, std::memory_order_relaxed); + return CancellationStateSourcePtr{this}; +} + +inline void CancellationState::removeSourceReference() noexcept { + const auto oldState = state_.fetch_sub( + kSourceReferenceCountIncrement, std::memory_order_acq_rel); + DCHECK( + (oldState & kSourceReferenceCountMask) >= kSourceReferenceCountIncrement); + if (oldState < + (kSourceReferenceCountIncrement + kTokenReferenceCountIncrement)) { + delete this; + } +} + +inline bool CancellationState::isCancellationRequested() const noexcept { + return isCancellationRequested(state_.load(std::memory_order_acquire)); +} + +inline bool CancellationState::canBeCancelled() const noexcept { + return canBeCancelled(state_.load(std::memory_order_acquire)); +} + +inline bool CancellationState::canBeCancelled(std::uint64_t state) noexcept { + // Can be cancelled if there is at least one CancellationSource ref-count + // or if cancellation has been requested. + return (state >= kSourceReferenceCountIncrement) || + isCancellationRequested(state); +} + +inline bool CancellationState::isCancellationRequested( + std::uint64_t state) noexcept { + return (state & kCancellationRequestedFlag) != 0; +} + +inline bool CancellationState::isLocked(std::uint64_t state) noexcept { + return (state & kLockedFlag) != 0; +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/CancellationToken.cpp b/ios/Pods/Flipper-Folly/folly/CancellationToken.cpp new file mode 100644 index 000000000..057779987 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CancellationToken.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/CancellationToken.h> +#include <folly/Optional.h> +#include <folly/synchronization/detail/Sleeper.h> + +#include <glog/logging.h> + +#include <algorithm> +#include <new> +#include <thread> +#include <tuple> + +namespace folly { +namespace detail { + +CancellationState::~CancellationState() { + DCHECK(head_ == nullptr); + DCHECK(!isLocked(state_.load(std::memory_order_relaxed))); + DCHECK( + state_.load(std::memory_order_relaxed) < kTokenReferenceCountIncrement); +} + +bool CancellationState::tryAddCallback( + CancellationCallback* callback, + bool incrementRefCountIfSuccessful) noexcept { + // Try to acquire the lock, but abandon trying to acquire the lock if + // cancellation has already been requested (we can just immediately invoke + // the callback) or if cancellation can never be requested (we can just + // skip registration). + if (!tryLock([callback](std::uint64_t oldState) noexcept { + if (isCancellationRequested(oldState)) { + callback->invokeCallback(); + return false; + } + return canBeCancelled(oldState); + })) { + return false; + } + + // We've acquired the lock and cancellation has not yet been requested. + // Push this callback onto the head of the list. + if (head_ != nullptr) { + head_->prevNext_ = &callback->next_; + } + callback->next_ = head_; + callback->prevNext_ = &head_; + head_ = callback; + + if (incrementRefCountIfSuccessful) { + // Combine multiple atomic operations into a single atomic operation. + unlockAndIncrementTokenCount(); + } else { + unlock(); + } + + // Successfully added the callback. + return true; +} + +void CancellationState::removeCallback( + CancellationCallback* callback) noexcept { + DCHECK(callback != nullptr); + + lock(); + + if (callback->prevNext_ != nullptr) { + // Still registered in the list => not yet executed. + // Just remove it from the list. + *callback->prevNext_ = callback->next_; + if (callback->next_ != nullptr) { + callback->next_->prevNext_ = callback->prevNext_; + } + + unlockAndDecrementTokenCount(); + return; + } + + unlock(); + + // Callback has either already executed or is executing concurrently on + // another thread. + + if (signallingThreadId_ == std::this_thread::get_id()) { + // Callback executed on this thread or is still currently executing + // and is deregistering itself from within the callback. + if (callback->destructorHasRunInsideCallback_ != nullptr) { + // Currently inside the callback, let the requestCancellation() method + // know the object is about to be destructed and that it should + // not try to access the object when the callback returns. + *callback->destructorHasRunInsideCallback_ = true; + } + } else { + // Callback is currently executing on another thread, block until it + // finishes executing. + folly::detail::Sleeper sleeper; + while (!callback->callbackCompleted_.load(std::memory_order_acquire)) { + sleeper.wait(); + } + } + + removeTokenReference(); +} + +bool CancellationState::requestCancellation() noexcept { + if (!tryLockAndCancelUnlessCancelled()) { + // Was already marked as cancelled + return true; + } + + // This thread marked as cancelled and acquired the lock + + signallingThreadId_ = std::this_thread::get_id(); + + while (head_ != nullptr) { + // Dequeue the first item on the queue. + CancellationCallback* callback = head_; + head_ = callback->next_; + const bool anyMore = head_ != nullptr; + if (anyMore) { + head_->prevNext_ = &head_; + } + // Mark this item as removed from the list. + callback->prevNext_ = nullptr; + + // Don't hold the lock while executing the callback + // as we don't want to block other threads from + // deregistering callbacks. + unlock(); + + // TRICKY: Need to store a flag on the stack here that the callback + // can use to signal that the destructor was executed inline + // during the call. + // If the destructor was executed inline then it's not safe to + // dereference 'callback' after 'invokeCallback()' returns. + // If the destructor runs on some other thread then the other + // thread will block waiting for this thread to signal that the + // callback has finished executing. + bool destructorHasRunInsideCallback = false; + callback->destructorHasRunInsideCallback_ = &destructorHasRunInsideCallback; + + callback->invokeCallback(); + + if (!destructorHasRunInsideCallback) { + callback->destructorHasRunInsideCallback_ = nullptr; + callback->callbackCompleted_.store(true, std::memory_order_release); + } + + if (!anyMore) { + // This was the last item in the queue when we dequeued it. + // No more items should be added to the queue after we have + // marked the state as cancelled, only removed from the queue. + // Avoid acquring/releasing the lock in this case. + return false; + } + + lock(); + } + + unlock(); + + return false; +} + +void CancellationState::lock() noexcept { + folly::detail::Sleeper sleeper; + std::uint64_t oldState = state_.load(std::memory_order_relaxed); + do { + while (isLocked(oldState)) { + sleeper.wait(); + oldState = state_.load(std::memory_order_relaxed); + } + } while (!state_.compare_exchange_weak( + oldState, + oldState | kLockedFlag, + std::memory_order_acquire, + std::memory_order_relaxed)); +} + +void CancellationState::unlock() noexcept { + state_.fetch_sub(kLockedFlag, std::memory_order_release); +} + +void CancellationState::unlockAndIncrementTokenCount() noexcept { + state_.fetch_sub( + kLockedFlag - kTokenReferenceCountIncrement, std::memory_order_release); +} + +void CancellationState::unlockAndDecrementTokenCount() noexcept { + auto oldState = state_.fetch_sub( + kLockedFlag + kTokenReferenceCountIncrement, std::memory_order_acq_rel); + if (oldState < (kLockedFlag + 2 * kTokenReferenceCountIncrement)) { + delete this; + } +} + +bool CancellationState::tryLockAndCancelUnlessCancelled() noexcept { + folly::detail::Sleeper sleeper; + std::uint64_t oldState = state_.load(std::memory_order_acquire); + while (true) { + if (isCancellationRequested(oldState)) { + return false; + } else if (isLocked(oldState)) { + sleeper.wait(); + oldState = state_.load(std::memory_order_acquire); + } else if (state_.compare_exchange_weak( + oldState, + oldState | kLockedFlag | kCancellationRequestedFlag, + std::memory_order_acq_rel, + std::memory_order_acquire)) { + return true; + } + } +} + +template <typename Predicate> +bool CancellationState::tryLock(Predicate predicate) noexcept { + folly::detail::Sleeper sleeper; + std::uint64_t oldState = state_.load(std::memory_order_acquire); + while (true) { + if (!predicate(oldState)) { + return false; + } else if (isLocked(oldState)) { + sleeper.wait(); + oldState = state_.load(std::memory_order_acquire); + } else if (state_.compare_exchange_weak( + oldState, + oldState | kLockedFlag, + std::memory_order_acquire)) { + return true; + } + } +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/CancellationToken.h b/ios/Pods/Flipper-Folly/folly/CancellationToken.h new file mode 100644 index 000000000..bdda121b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CancellationToken.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/CppAttributes.h> +#include <folly/Function.h> + +#include <atomic> +#include <memory> +#include <thread> +#include <type_traits> + +namespace folly { + +class CancellationCallback; +class CancellationSource; +struct OperationCancelled : public std::exception { + const char* what() const noexcept override { + return "coroutine operation cancelled"; + } +}; + +namespace detail { +class CancellationState; +struct CancellationStateTokenDeleter { + void operator()(CancellationState*) noexcept; +}; +struct CancellationStateSourceDeleter { + void operator()(CancellationState*) noexcept; +}; +using CancellationStateTokenPtr = + std::unique_ptr<CancellationState, CancellationStateTokenDeleter>; +using CancellationStateSourcePtr = + std::unique_ptr<CancellationState, CancellationStateSourceDeleter>; +} // namespace detail + +// A CancellationToken is an object that can be passed into an function or +// operation that allows the caller to later request that the operation be +// cancelled. +// +// A CancellationToken object can be obtained by calling the .getToken() +// method on a CancellationSource or by copying another CancellationToken +// object. All CancellationToken objects obtained from the same original +// CancellationSource object all reference the same underlying cancellation +// state and will all be cancelled together. +// +// If your function needs to be cancellable but does not need to request +// cancellation then you should take a CancellationToken as a parameter. +// If your function needs to be able to request cancellation then you +// should instead take a CancellationSource as a parameter. +class CancellationToken { + public: + // Constructs to a token that can never be cancelled. + // + // Pass a default-constructed CancellationToken into an operation that + // you never intend to cancel. These objects are very cheap to create. + CancellationToken() noexcept = default; + + // Construct a copy of the token that shares the same underlying state. + CancellationToken(const CancellationToken& other) noexcept; + CancellationToken(CancellationToken&& other) noexcept; + + CancellationToken& operator=(const CancellationToken& other) noexcept; + CancellationToken& operator=(CancellationToken&& other) noexcept; + + // Query whether someone has called .requestCancellation() on an instance + // of CancellationSource object associated with this CancellationToken. + bool isCancellationRequested() const noexcept; + + // Query whether this CancellationToken can ever have cancellation requested + // on it. + // + // This will return false if the CancellationToken is not associated with a + // CancellationSource object. eg. because the CancellationToken was + // default-constructed, has been moved-from or because the last + // CancellationSource object associated with the underlying cancellation state + // has been destroyed and the operation has not yet been cancelled and so + // never will be. + // + // Implementations of operations may be able to take more efficient code-paths + // if they know they can never be cancelled. + bool canBeCancelled() const noexcept; + + void swap(CancellationToken& other) noexcept; + + friend bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept; + + private: + friend class CancellationCallback; + friend class CancellationSource; + + explicit CancellationToken(detail::CancellationStateTokenPtr state) noexcept; + + detail::CancellationStateTokenPtr state_; +}; + +bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept; +bool operator!=( + const CancellationToken& a, + const CancellationToken& b) noexcept; + +// A CancellationSource object provides the ability to request cancellation of +// operations that an associated CancellationToken was passed to. +// +// Example usage: +// CancellationSource cs; +// Future<void> f = startSomeOperation(cs.getToken()); +// +// // Later... +// cs.requestCancellation(); +class CancellationSource { + public: + // Construct to a new, independent cancellation source. + CancellationSource(); + + // Construct a new reference to the same underlying cancellation state. + // + // Either the original or the new copy can be used to request cancellation + // of associated work. + CancellationSource(const CancellationSource& other) noexcept; + + // This leaves 'other' in an empty state where 'requestCancellation()' is a + // no-op and 'canBeCancelled()' returns false. + CancellationSource(CancellationSource&& other) noexcept; + + CancellationSource& operator=(const CancellationSource& other) noexcept; + CancellationSource& operator=(CancellationSource&& other) noexcept; + + // Construct a CancellationSource that cannot be cancelled. + // + // This factory function can be used to obtain a CancellationSource that + // is equivalent to a moved-from CancellationSource object without needing + // to allocate any shared-state. + static CancellationSource invalid() noexcept; + + // Query if cancellation has already been requested on this CancellationSource + // or any other CancellationSource object copied from the same original + // CancellationSource object. + bool isCancellationRequested() const noexcept; + + // Query if cancellation can be requested through this CancellationSource + // object. This will only return false if the CancellationSource object has + // been moved-from. + bool canBeCancelled() const noexcept; + + // Obtain a CancellationToken linked to this CancellationSource. + // + // This token can be passed into cancellable operations to allow the caller + // to later request cancellation of that operation. + CancellationToken getToken() const noexcept; + + // Request cancellation of work associated with this CancellationSource. + // + // This will ensure subsequent calls to isCancellationRequested() on any + // CancellationSource or CancellationToken object associated with the same + // underlying cancellation state to return true. + // + // If this is the first call to requestCancellation() on any + // CancellationSource object with the same underlying state then this call + // will also execute the callbacks associated with any CancellationCallback + // objects that were constructed with an associated CancellationToken. + // + // Note that it is possible that another thread may be concurrently + // registering a callback with CancellationCallback. This method guarantees + // that either this thread will see the callback registration and will + // ensure that the callback is called, or the CancellationCallback constructor + // will see the cancellation-requested signal and will execute the callback + // inline inside the constructor. + // + // Returns the previous state of 'isCancellationRequested()'. i.e. + // - 'true' if cancellation had previously been requested. + // - 'false' if this was the first call to request cancellation. + bool requestCancellation() const noexcept; + + void swap(CancellationSource& other) noexcept; + + friend bool operator==( + const CancellationSource& a, + const CancellationSource& b) noexcept; + + private: + explicit CancellationSource( + detail::CancellationStateSourcePtr&& state) noexcept; + + detail::CancellationStateSourcePtr state_; +}; + +bool operator==( + const CancellationSource& a, + const CancellationSource& b) noexcept; +bool operator!=( + const CancellationSource& a, + const CancellationSource& b) noexcept; + +class CancellationCallback { + using VoidFunction = folly::Function<void()>; + + public: + // Constructing a CancellationCallback object registers the callback + // with the specified CancellationToken such that the callback will be + // executed if the corresponding CancellationSource object has the + // requestCancellation() method called on it. + // + // If the CancellationToken object already had cancellation requested + // then the callback will be executed inline on the current thread before + // the constructor returns. Otherwise, the callback will be executed on + // in the execution context of the first thread to call requestCancellation() + // on a corresponding CancellationSource. + // + // The callback object must not throw any unhandled exceptions. Doing so + // will result in the program terminating via std::terminate(). + template < + typename Callable, + std::enable_if_t< + std::is_constructible<VoidFunction, Callable>::value, + int> = 0> + CancellationCallback(CancellationToken&& ct, Callable&& callable); + template < + typename Callable, + std::enable_if_t< + std::is_constructible<VoidFunction, Callable>::value, + int> = 0> + CancellationCallback(const CancellationToken& ct, Callable&& callable); + + // Deregisters the callback from the CancellationToken. + // + // If cancellation has been requested concurrently on another thread and the + // callback is currently executing then the destructor will block until after + // the callback has returned (otherwise it might be left with a dangling + // reference). + // + // You should generally try to implement your callback functions to be lock + // free to avoid deadlocks between the callback executing and the + // CancellationCallback destructor trying to deregister the callback. + // + // If the callback has not started executing yet then the callback will be + // deregistered from the CancellationToken before the destructor completes. + // + // Once the destructor returns you can be guaranteed that the callback will + // not be called by a subsequent call to 'requestCancellation()' on a + // CancellationSource associated with the CancellationToken passed to the + // constructor. + ~CancellationCallback(); + + // Not copyable/movable + CancellationCallback(const CancellationCallback&) = delete; + CancellationCallback(CancellationCallback&&) = delete; + CancellationCallback& operator=(const CancellationCallback&) = delete; + CancellationCallback& operator=(CancellationCallback&&) = delete; + + private: + friend class detail::CancellationState; + + void invokeCallback() noexcept; + + CancellationCallback* next_; + + // Pointer to the pointer that points to this node in the linked list. + // This could be the 'next_' of a previous CancellationCallback or could + // be the 'head_' pointer of the CancellationState. + // If this node is inserted in the list then this will be non-null. + CancellationCallback** prevNext_; + + detail::CancellationState* state_; + VoidFunction callback_; + + // Pointer to a flag stored on the stack of the caller to invokeCallback() + // that is used to indicate to the caller of invokeCallback() that the + // destructor has run and it is no longer valid to access the callback + // object. + bool* destructorHasRunInsideCallback_; + + // Flag used to signal that the callback has completed executing on another + // thread and it is now safe to exit the destructor. + std::atomic<bool> callbackCompleted_; +}; + +} // namespace folly + +#include <folly/CancellationToken-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/Chrono.h b/ios/Pods/Flipper-Folly/folly/Chrono.h new file mode 100644 index 000000000..3b3838b43 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Chrono.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <stdexcept> +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/lang/Exception.h> +#include <folly/portability/Time.h> + +/*** + * include or backport: + * * std::chrono::ceil + * * std::chrono::floor + * * std::chrono::round + */ + +#if __cpp_lib_chrono >= 201510 || _LIBCPP_STD_VER > 14 || _MSC_VER + +namespace folly { +namespace chrono { + +/* using override */ using std::chrono::abs; +/* using override */ using std::chrono::ceil; +/* using override */ using std::chrono::floor; +/* using override */ using std::chrono::round; +} // namespace chrono +} // namespace folly + +#else + +namespace folly { +namespace chrono { + +namespace detail { + +// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA +template <typename T> +struct is_duration : std::false_type {}; +template <typename Rep, typename Period> +struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {}; + +template <typename To, typename Duration> +constexpr To ceil_impl(Duration const& d, To const& t) { + return t < d ? t + To{1} : t; +} + +template <typename To, typename Duration> +constexpr To floor_impl(Duration const& d, To const& t) { + return t > d ? t - To{1} : t; +} + +template <typename To, typename Diff> +constexpr To round_impl(To const& t0, To const& t1, Diff diff0, Diff diff1) { + return diff0 < diff1 ? t0 : diff1 < diff0 ? t1 : t0.count() & 1 ? t1 : t0; +} + +template <typename To, typename Duration> +constexpr To round_impl(Duration const& d, To const& t0, To const& t1) { + return round_impl(t0, t1, d - t0, t1 - d); +} + +template <typename To, typename Duration> +constexpr To round_impl(Duration const& d, To const& t0) { + return round_impl(d, t0, t0 + To{1}); +} +} // namespace detail + +// mimic: std::chrono::abs, C++17 +template < + typename Rep, + typename Period, + typename = typename std::enable_if< + std::chrono::duration<Rep, Period>::min() < + std::chrono::duration<Rep, Period>::zero()>::type> +constexpr std::chrono::duration<Rep, Period> abs( + std::chrono::duration<Rep, Period> const& d) { + return d < std::chrono::duration<Rep, Period>::zero() ? -d : d; +} + +// mimic: std::chrono::ceil, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if<detail::is_duration<To>::value>::type> +constexpr To ceil(std::chrono::duration<Rep, Period> const& d) { + return detail::ceil_impl(d, std::chrono::duration_cast<To>(d)); +} + +// mimic: std::chrono::ceil, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/ceil, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if<detail::is_duration<To>::value>::type> +constexpr std::chrono::time_point<Clock, To> ceil( + std::chrono::time_point<Clock, Duration> const& tp) { + return std::chrono::time_point<Clock, To>{ceil<To>(tp.time_since_epoch())}; +} + +// mimic: std::chrono::floor, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/floor, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if<detail::is_duration<To>::value>::type> +constexpr To floor(std::chrono::duration<Rep, Period> const& d) { + return detail::floor_impl(d, std::chrono::duration_cast<To>(d)); +} + +// mimic: std::chrono::floor, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/floor, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if<detail::is_duration<To>::value>::type> +constexpr std::chrono::time_point<Clock, To> floor( + std::chrono::time_point<Clock, Duration> const& tp) { + return std::chrono::time_point<Clock, To>{floor<To>(tp.time_since_epoch())}; +} + +// mimic: std::chrono::round, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/round, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if< + detail::is_duration<To>::value && + !std::chrono::treat_as_floating_point<typename To::rep>::value>::type> +constexpr To round(std::chrono::duration<Rep, Period> const& d) { + return detail::round_impl(d, floor<To>(d)); +} + +// mimic: std::chrono::round, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/round, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if< + detail::is_duration<To>::value && + !std::chrono::treat_as_floating_point<typename To::rep>::value>::type> +constexpr std::chrono::time_point<Clock, To> round( + std::chrono::time_point<Clock, Duration> const& tp) { + return std::chrono::time_point<Clock, To>{round<To>(tp.time_since_epoch())}; +} +} // namespace chrono +} // namespace folly + +#endif + +namespace folly { +namespace chrono { + +struct coarse_steady_clock { + using rep = std::chrono::milliseconds::rep; + using period = std::chrono::milliseconds::period; + using duration = std::chrono::duration<rep, period>; + using time_point = std::chrono::time_point<coarse_steady_clock, duration>; + constexpr static bool is_steady = true; + + static time_point now() noexcept { +#ifndef CLOCK_MONOTONIC_COARSE + return time_point(std::chrono::duration_cast<duration>( + std::chrono::steady_clock::now().time_since_epoch())); +#else + timespec ts; + auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + if (kIsDebug && (ret != 0)) { + throw_exception<std::runtime_error>( + "Error using CLOCK_MONOTONIC_COARSE."); + } + + return time_point(std::chrono::duration_cast<duration>( + std::chrono::seconds(ts.tv_sec) + + std::chrono::nanoseconds(ts.tv_nsec))); +#endif + } +}; + +} // namespace chrono +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.cpp b/ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.cpp new file mode 100644 index 000000000..9cdf6d42c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ClockGettimeWrappers.h> +#include <folly/CPortability.h> +#include <folly/Likely.h> +#include <folly/portability/Time.h> + +#include <chrono> + +#include <ctime> + +#ifndef _WIN32 +#define _GNU_SOURCE 1 +#include <dlfcn.h> +#endif + +namespace folly { +namespace chrono { + +static int64_t clock_gettime_ns_fallback(clockid_t clock) { + struct timespec ts; + int r = clock_gettime(clock, &ts); + if (UNLIKELY(r != 0)) { + // Mimic what __clock_gettime_ns does (even though this can be a legit + // value). + return -1; + } + std::chrono::nanoseconds result = + std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec); + return result.count(); +} + +// Initialize with default behavior, which we might override on Linux hosts +// with VDSO support. +int (*clock_gettime)(clockid_t, timespec* ts) = &::clock_gettime; +int64_t (*clock_gettime_ns)(clockid_t) = &clock_gettime_ns_fallback; + +// In MSAN mode use glibc's versions as they are intercepted by the MSAN +// runtime which properly tracks memory initialization. +#if defined(FOLLY_HAVE_LINUX_VDSO) && !defined(FOLLY_SANITIZE_MEMORY) + +namespace { + +struct VdsoInitializer { + VdsoInitializer() { + m_handle = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + if (!m_handle) { + return; + } + + void* p = dlsym(m_handle, "__vdso_clock_gettime"); + if (p) { + folly::chrono::clock_gettime = (int (*)(clockid_t, timespec*))p; + } + p = dlsym(m_handle, "__vdso_clock_gettime_ns"); + if (p) { + folly::chrono::clock_gettime_ns = (int64_t(*)(clockid_t))p; + } + } + + ~VdsoInitializer() { + if (m_handle) { + clock_gettime = &::clock_gettime; + clock_gettime_ns = &clock_gettime_ns_fallback; + dlclose(m_handle); + } + } + + private: + void* m_handle; +}; + +const VdsoInitializer vdso_initializer; +} // namespace + +#endif +} // namespace chrono +} // namespace folly diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h b/ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.h similarity index 55% rename from ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h rename to ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.h index 9c5850ba4..8c403193e 100644 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h +++ b/ios/Pods/Flipper-Folly/folly/ClockGettimeWrappers.h @@ -1,11 +1,11 @@ /* - * Copyright 2019 Google + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,14 +14,16 @@ * limitations under the License. */ -#import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h> +#pragma once -/** Checkin refresh interval. **/ -FOUNDATION_EXPORT const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval; +#include <folly/portability/Time.h> -@interface FIRInstanceIDCheckinPreferences () +#include <time.h> -- (BOOL)hasPreCachedAuthCredentials; -- (void)setHasPreCachedAuthCredentials:(BOOL)hasPreCachedAuthCredentials; +namespace folly { +namespace chrono { -@end +extern int (*clock_gettime)(clockid_t, timespec* ts); +extern int64_t (*clock_gettime_ns)(clockid_t); +} // namespace chrono +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ConcurrentBitSet.h b/ios/Pods/Flipper-Folly/folly/ConcurrentBitSet.h new file mode 100644 index 000000000..2be6e2162 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ConcurrentBitSet.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <array> +#include <atomic> +#include <cassert> +#include <cstddef> +#include <limits> + +#include <folly/Portability.h> + +namespace folly { + +/** + * An atomic bitset of fixed size (specified at compile time). + * + * Formerly known as AtomicBitSet. It was renamed while fixing a bug + * to avoid any silent breakages during run time. + */ +template <size_t N> +class ConcurrentBitSet { + public: + /** + * Construct a ConcurrentBitSet; all bits are initially false. + */ + ConcurrentBitSet(); + + ConcurrentBitSet(const ConcurrentBitSet&) = delete; + ConcurrentBitSet& operator=(const ConcurrentBitSet&) = delete; + + /** + * Set bit idx to true, using the given memory order. Returns the + * previous value of the bit. + * + * Note that the operation is a read-modify-write operation due to the use + * of fetch_or. + */ + bool set(size_t idx, std::memory_order order = std::memory_order_seq_cst); + + /** + * Set bit idx to false, using the given memory order. Returns the + * previous value of the bit. + * + * Note that the operation is a read-modify-write operation due to the use + * of fetch_and. + */ + bool reset(size_t idx, std::memory_order order = std::memory_order_seq_cst); + + /** + * Set bit idx to the given value, using the given memory order. Returns + * the previous value of the bit. + * + * Note that the operation is a read-modify-write operation due to the use + * of fetch_and or fetch_or. + * + * Yes, this is an overload of set(), to keep as close to std::bitset's + * interface as possible. + */ + bool set( + size_t idx, + bool value, + std::memory_order order = std::memory_order_seq_cst); + + /** + * Read bit idx. + */ + bool test(size_t idx, std::memory_order order = std::memory_order_seq_cst) + const; + + /** + * Same as test() with the default memory order. + */ + bool operator[](size_t idx) const; + + /** + * Return the size of the bitset. + */ + constexpr size_t size() const { + return N; + } + + private: + // Pick the largest lock-free type available +#if (ATOMIC_LLONG_LOCK_FREE == 2) + typedef unsigned long long BlockType; +#elif (ATOMIC_LONG_LOCK_FREE == 2) + typedef unsigned long BlockType; +#else + // Even if not lock free, what can we do? + typedef unsigned int BlockType; +#endif + typedef std::atomic<BlockType> AtomicBlockType; + + static constexpr size_t kBitsPerBlock = + std::numeric_limits<BlockType>::digits; + + static constexpr size_t blockIndex(size_t bit) { + return bit / kBitsPerBlock; + } + + static constexpr size_t bitOffset(size_t bit) { + return bit % kBitsPerBlock; + } + + // avoid casts + static constexpr BlockType kOne = 1; + static constexpr size_t kNumBlocks = (N + kBitsPerBlock - 1) / kBitsPerBlock; + std::array<AtomicBlockType, kNumBlocks> data_; +}; + +// value-initialize to zero +template <size_t N> +inline ConcurrentBitSet<N>::ConcurrentBitSet() : data_() {} + +template <size_t N> +inline bool ConcurrentBitSet<N>::set(size_t idx, std::memory_order order) { + assert(idx < N); + BlockType mask = kOne << bitOffset(idx); + return data_[blockIndex(idx)].fetch_or(mask, order) & mask; +} + +template <size_t N> +inline bool ConcurrentBitSet<N>::reset(size_t idx, std::memory_order order) { + assert(idx < N); + BlockType mask = kOne << bitOffset(idx); + return data_[blockIndex(idx)].fetch_and(~mask, order) & mask; +} + +template <size_t N> +inline bool +ConcurrentBitSet<N>::set(size_t idx, bool value, std::memory_order order) { + return value ? set(idx, order) : reset(idx, order); +} + +template <size_t N> +inline bool ConcurrentBitSet<N>::test(size_t idx, std::memory_order order) + const { + assert(idx < N); + BlockType mask = kOne << bitOffset(idx); + return data_[blockIndex(idx)].load(order) & mask; +} + +template <size_t N> +inline bool ConcurrentBitSet<N>::operator[](size_t idx) const { + return test(idx); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList-inl.h b/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList-inl.h new file mode 100644 index 000000000..a0eec3076 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList-inl.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Xin Liu <xliux@fb.com> + +#pragma once + +#include <algorithm> +#include <atomic> +#include <climits> +#include <cmath> +#include <memory> +#include <mutex> +#include <type_traits> +#include <vector> + +#include <boost/random.hpp> +#include <glog/logging.h> + +#include <folly/Memory.h> +#include <folly/ThreadLocal.h> +#include <folly/synchronization/MicroSpinLock.h> + +namespace folly { +namespace detail { + +template <typename ValT, typename NodeT> +class csl_iterator; + +template <typename T> +class SkipListNode { + enum : uint16_t { + IS_HEAD_NODE = 1, + MARKED_FOR_REMOVAL = (1 << 1), + FULLY_LINKED = (1 << 2), + }; + + public: + typedef T value_type; + + SkipListNode(const SkipListNode&) = delete; + SkipListNode& operator=(const SkipListNode&) = delete; + + template < + typename NodeAlloc, + typename U, + typename = + typename std::enable_if<std::is_convertible<U, T>::value>::type> + static SkipListNode* + create(NodeAlloc& alloc, int height, U&& data, bool isHead = false) { + DCHECK(height >= 1 && height < 64) << height; + + size_t size = + sizeof(SkipListNode) + height * sizeof(std::atomic<SkipListNode*>); + auto storage = std::allocator_traits<NodeAlloc>::allocate(alloc, size); + // do placement new + return new (storage) + SkipListNode(uint8_t(height), std::forward<U>(data), isHead); + } + + template <typename NodeAlloc> + static void destroy(NodeAlloc& alloc, SkipListNode* node) { + size_t size = sizeof(SkipListNode) + + node->height_ * sizeof(std::atomic<SkipListNode*>); + node->~SkipListNode(); + std::allocator_traits<NodeAlloc>::deallocate(alloc, node, size); + } + + template <typename NodeAlloc> + struct DestroyIsNoOp : StrictConjunction< + AllocatorHasTrivialDeallocate<NodeAlloc>, + std::is_trivially_destructible<SkipListNode>> {}; + + // copy the head node to a new head node assuming lock acquired + SkipListNode* copyHead(SkipListNode* node) { + DCHECK(node != nullptr && height_ > node->height_); + setFlags(node->getFlags()); + for (uint8_t i = 0; i < node->height_; ++i) { + setSkip(i, node->skip(i)); + } + return this; + } + + inline SkipListNode* skip(int layer) const { + DCHECK_LT(layer, height_); + return skip_[layer].load(std::memory_order_consume); + } + + // next valid node as in the linked list + SkipListNode* next() { + SkipListNode* node; + for (node = skip(0); (node != nullptr && node->markedForRemoval()); + node = node->skip(0)) { + } + return node; + } + + void setSkip(uint8_t h, SkipListNode* next) { + DCHECK_LT(h, height_); + skip_[h].store(next, std::memory_order_release); + } + + value_type& data() { + return data_; + } + const value_type& data() const { + return data_; + } + int maxLayer() const { + return height_ - 1; + } + int height() const { + return height_; + } + + std::unique_lock<MicroSpinLock> acquireGuard() { + return std::unique_lock<MicroSpinLock>(spinLock_); + } + + bool fullyLinked() const { + return getFlags() & FULLY_LINKED; + } + bool markedForRemoval() const { + return getFlags() & MARKED_FOR_REMOVAL; + } + bool isHeadNode() const { + return getFlags() & IS_HEAD_NODE; + } + + void setIsHeadNode() { + setFlags(uint16_t(getFlags() | IS_HEAD_NODE)); + } + void setFullyLinked() { + setFlags(uint16_t(getFlags() | FULLY_LINKED)); + } + void setMarkedForRemoval() { + setFlags(uint16_t(getFlags() | MARKED_FOR_REMOVAL)); + } + + private: + // Note! this can only be called from create() as a placement new. + template <typename U> + SkipListNode(uint8_t height, U&& data, bool isHead) + : height_(height), data_(std::forward<U>(data)) { + spinLock_.init(); + setFlags(0); + if (isHead) { + setIsHeadNode(); + } + // need to explicitly init the dynamic atomic pointer array + for (uint8_t i = 0; i < height_; ++i) { + new (&skip_[i]) std::atomic<SkipListNode*>(nullptr); + } + } + + ~SkipListNode() { + for (uint8_t i = 0; i < height_; ++i) { + skip_[i].~atomic(); + } + } + + uint16_t getFlags() const { + return flags_.load(std::memory_order_consume); + } + void setFlags(uint16_t flags) { + flags_.store(flags, std::memory_order_release); + } + + // TODO(xliu): on x86_64, it's possible to squeeze these into + // skip_[0] to maybe save 8 bytes depending on the data alignments. + // NOTE: currently this is x86_64 only anyway, due to the + // MicroSpinLock. + std::atomic<uint16_t> flags_; + const uint8_t height_; + MicroSpinLock spinLock_; + + value_type data_; + + std::atomic<SkipListNode*> skip_[0]; +}; + +class SkipListRandomHeight { + enum { kMaxHeight = 64 }; + + public: + // make it a singleton. + static SkipListRandomHeight* instance() { + static SkipListRandomHeight instance_; + return &instance_; + } + + int getHeight(int maxHeight) const { + DCHECK_LE(maxHeight, kMaxHeight) << "max height too big!"; + double p = randomProb(); + for (int i = 0; i < maxHeight; ++i) { + if (p < lookupTable_[i]) { + return i + 1; + } + } + return maxHeight; + } + + size_t getSizeLimit(int height) const { + DCHECK_LT(height, kMaxHeight); + return sizeLimitTable_[height]; + } + + private: + SkipListRandomHeight() { + initLookupTable(); + } + + void initLookupTable() { + // set skip prob = 1/E + static const double kProbInv = exp(1); + static const double kProb = 1.0 / kProbInv; + static const size_t kMaxSizeLimit = std::numeric_limits<size_t>::max(); + + double sizeLimit = 1; + double p = lookupTable_[0] = (1 - kProb); + sizeLimitTable_[0] = 1; + for (int i = 1; i < kMaxHeight - 1; ++i) { + p *= kProb; + sizeLimit *= kProbInv; + lookupTable_[i] = lookupTable_[i - 1] + p; + sizeLimitTable_[i] = sizeLimit > kMaxSizeLimit + ? kMaxSizeLimit + : static_cast<size_t>(sizeLimit); + } + lookupTable_[kMaxHeight - 1] = 1; + sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit; + } + + static double randomProb() { + static ThreadLocal<boost::lagged_fibonacci2281> rng_; + return (*rng_)(); + } + + double lookupTable_[kMaxHeight]; + size_t sizeLimitTable_[kMaxHeight]; +}; + +template <typename NodeType, typename NodeAlloc, typename = void> +class NodeRecycler; + +template <typename NodeType, typename NodeAlloc> +class NodeRecycler< + NodeType, + NodeAlloc, + typename std::enable_if< + !NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> { + public: + explicit NodeRecycler(const NodeAlloc& alloc) + : refs_(0), dirty_(false), alloc_(alloc) { + lock_.init(); + } + + explicit NodeRecycler() : refs_(0), dirty_(false) { + lock_.init(); + } + + ~NodeRecycler() { + CHECK_EQ(refs(), 0); + if (nodes_) { + for (auto& node : *nodes_) { + NodeType::destroy(alloc_, node); + } + } + } + + void add(NodeType* node) { + std::lock_guard<MicroSpinLock> g(lock_); + if (nodes_.get() == nullptr) { + nodes_ = std::make_unique<std::vector<NodeType*>>(1, node); + } else { + nodes_->push_back(node); + } + DCHECK_GT(refs(), 0); + dirty_.store(true, std::memory_order_relaxed); + } + + int addRef() { + return refs_.fetch_add(1, std::memory_order_relaxed); + } + + int releaseRef() { + // We don't expect to clean the recycler immediately everytime it is OK + // to do so. Here, it is possible that multiple accessors all release at + // the same time but nobody would clean the recycler here. If this + // happens, the recycler will usually still get cleaned when + // such a race doesn't happen. The worst case is the recycler will + // eventually get deleted along with the skiplist. + if (LIKELY(!dirty_.load(std::memory_order_relaxed) || refs() > 1)) { + return refs_.fetch_add(-1, std::memory_order_relaxed); + } + + std::unique_ptr<std::vector<NodeType*>> newNodes; + { + std::lock_guard<MicroSpinLock> g(lock_); + if (nodes_.get() == nullptr || refs() > 1) { + return refs_.fetch_add(-1, std::memory_order_relaxed); + } + // once refs_ reaches 1 and there is no other accessor, it is safe to + // remove all the current nodes in the recycler, as we already acquired + // the lock here so no more new nodes can be added, even though new + // accessors may be added after that. + newNodes.swap(nodes_); + dirty_.store(false, std::memory_order_relaxed); + } + + // TODO(xliu) should we spawn a thread to do this when there are large + // number of nodes in the recycler? + for (auto& node : *newNodes) { + NodeType::destroy(alloc_, node); + } + + // decrease the ref count at the very end, to minimize the + // chance of other threads acquiring lock_ to clear the deleted + // nodes again. + return refs_.fetch_add(-1, std::memory_order_relaxed); + } + + NodeAlloc& alloc() { + return alloc_; + } + + private: + int refs() const { + return refs_.load(std::memory_order_relaxed); + } + + std::unique_ptr<std::vector<NodeType*>> nodes_; + std::atomic<int32_t> refs_; // current number of visitors to the list + std::atomic<bool> dirty_; // whether *nodes_ is non-empty + MicroSpinLock lock_; // protects access to *nodes_ + NodeAlloc alloc_; +}; + +// In case of arena allocator, no recycling is necessary, and it's possible +// to save on ConcurrentSkipList size. +template <typename NodeType, typename NodeAlloc> +class NodeRecycler< + NodeType, + NodeAlloc, + typename std::enable_if< + NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> { + public: + explicit NodeRecycler(const NodeAlloc& alloc) : alloc_(alloc) {} + + void addRef() {} + void releaseRef() {} + + void add(NodeType* /* node */) {} + + NodeAlloc& alloc() { + return alloc_; + } + + private: + NodeAlloc alloc_; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList.h b/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList.h new file mode 100644 index 000000000..ab75ce397 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ConcurrentSkipList.h @@ -0,0 +1,878 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Xin Liu <xliux@fb.com> +// +// A concurrent skip list (CSL) implementation. +// Ref: http://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf + +/* + +This implements a sorted associative container that supports only +unique keys. (Similar to std::set.) + +Features: + + 1. Small memory overhead: ~40% less memory overhead compared with + std::set (1.6 words per node versus 3). It has an minimum of 4 + words (7 words if there nodes got deleted) per-list overhead + though. + + 2. Read accesses (count, find iterator, skipper) are lock-free and + mostly wait-free (the only wait a reader may need to do is when + the node it is visiting is in a pending stage, i.e. deleting, + adding and not fully linked). Write accesses (remove, add) need + to acquire locks, but locks are local to the predecessor nodes + and/or successor nodes. + + 3. Good high contention performance, comparable single-thread + performance. In the multithreaded case (12 workers), CSL tested + 10x faster than a RWSpinLocked std::set for an averaged sized + list (1K - 1M nodes). + + Comparable read performance to std::set when single threaded, + especially when the list size is large, and scales better to + larger lists: when the size is small, CSL can be 20-50% slower on + find()/contains(). As the size gets large (> 1M elements), + find()/contains() can be 30% faster. + + Iterating through a skiplist is similar to iterating through a + linked list, thus is much (2-6x) faster than on a std::set + (tree-based). This is especially true for short lists due to + better cache locality. Based on that, it's also faster to + intersect two skiplists. + + 4. Lazy removal with GC support. The removed nodes get deleted when + the last Accessor to the skiplist is destroyed. + +Caveats: + + 1. Write operations are usually 30% slower than std::set in a single + threaded environment. + + 2. Need to have a head node for each list, which has a 4 word + overhead. + + 3. When the list is quite small (< 1000 elements), single threaded + benchmarks show CSL can be 10x slower than std:set. + + 4. The interface requires using an Accessor to access the skiplist. + (See below.) + + 5. Currently x64 only, due to use of MicroSpinLock. + + 6. Freed nodes will not be reclaimed as long as there are ongoing + uses of the list. + +Sample usage: + + typedef ConcurrentSkipList<int> SkipListT; + shared_ptr<SkipListT> sl(SkipListT::createInstance(init_head_height); + { + // It's usually good practice to hold an accessor only during + // its necessary life cycle (but not in a tight loop as + // Accessor creation incurs ref-counting overhead). + // + // Holding it longer delays garbage-collecting the deleted + // nodes in the list. + SkipListT::Accessor accessor(sl); + accessor.insert(23); + accessor.erase(2); + for (auto &elem : accessor) { + // use elem to access data + } + ... ... + } + + Another useful type is the Skipper accessor. This is useful if you + want to skip to locations in the way std::lower_bound() works, + i.e. it can be used for going through the list by skipping to the + node no less than a specified key. The Skipper keeps its location as + state, which makes it convenient for things like implementing + intersection of two sets efficiently, as it can start from the last + visited position. + + { + SkipListT::Accessor accessor(sl); + SkipListT::Skipper skipper(accessor); + skipper.to(30); + if (skipper) { + CHECK_LE(30, *skipper); + } + ... ... + // GC may happen when the accessor gets destructed. + } +*/ + +#pragma once + +#include <algorithm> +#include <atomic> +#include <limits> +#include <memory> +#include <type_traits> + +#include <glog/logging.h> + +#include <folly/ConcurrentSkipList-inl.h> +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/detail/Iterators.h> +#include <folly/synchronization/MicroSpinLock.h> + +namespace folly { + +template < + typename T, + typename Comp = std::less<T>, + // All nodes are allocated using provided SysAllocator, + // it should be thread-safe. + typename NodeAlloc = SysAllocator<void>, + int MAX_HEIGHT = 24> +class ConcurrentSkipList { + // MAX_HEIGHT needs to be at least 2 to suppress compiler + // warnings/errors (Werror=uninitialized tiggered due to preds_[1] + // being treated as a scalar in the compiler). + static_assert( + MAX_HEIGHT >= 2 && MAX_HEIGHT < 64, + "MAX_HEIGHT can only be in the range of [2, 64)"); + typedef std::unique_lock<folly::MicroSpinLock> ScopedLocker; + typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType; + + public: + typedef detail::SkipListNode<T> NodeType; + typedef T value_type; + typedef T key_type; + + typedef detail::csl_iterator<value_type, NodeType> iterator; + typedef detail::csl_iterator<const value_type, NodeType> const_iterator; + + class Accessor; + class Skipper; + + explicit ConcurrentSkipList(int height, const NodeAlloc& alloc) + : recycler_(alloc), + head_(NodeType::create(recycler_.alloc(), height, value_type(), true)), + size_(0) {} + + explicit ConcurrentSkipList(int height) + : recycler_(), + head_(NodeType::create(recycler_.alloc(), height, value_type(), true)), + size_(0) {} + + // Convenient function to get an Accessor to a new instance. + static Accessor create(int height, const NodeAlloc& alloc) { + return Accessor(createInstance(height, alloc)); + } + + static Accessor create(int height = 1) { + return Accessor(createInstance(height)); + } + + // Create a shared_ptr skiplist object with initial head height. + static std::shared_ptr<SkipListType> createInstance( + int height, + const NodeAlloc& alloc) { + return std::make_shared<ConcurrentSkipList>(height, alloc); + } + + static std::shared_ptr<SkipListType> createInstance(int height = 1) { + return std::make_shared<ConcurrentSkipList>(height); + } + + //=================================================================== + // Below are implementation details. + // Please see ConcurrentSkipList::Accessor for stdlib-like APIs. + //=================================================================== + + ~ConcurrentSkipList() { + if /* constexpr */ (NodeType::template DestroyIsNoOp<NodeAlloc>::value) { + // Avoid traversing the list if using arena allocator. + return; + } + for (NodeType* current = head_.load(std::memory_order_relaxed); current;) { + NodeType* tmp = current->skip(0); + NodeType::destroy(recycler_.alloc(), current); + current = tmp; + } + } + + private: + static bool greater(const value_type& data, const NodeType* node) { + return node && Comp()(node->data(), data); + } + + static bool less(const value_type& data, const NodeType* node) { + return (node == nullptr) || Comp()(data, node->data()); + } + + static int findInsertionPoint( + NodeType* cur, + int cur_layer, + const value_type& data, + NodeType* preds[], + NodeType* succs[]) { + int foundLayer = -1; + NodeType* pred = cur; + NodeType* foundNode = nullptr; + for (int layer = cur_layer; layer >= 0; --layer) { + NodeType* node = pred->skip(layer); + while (greater(data, node)) { + pred = node; + node = node->skip(layer); + } + if (foundLayer == -1 && !less(data, node)) { // the two keys equal + foundLayer = layer; + foundNode = node; + } + preds[layer] = pred; + + // if found, succs[0..foundLayer] need to point to the cached foundNode, + // as foundNode might be deleted at the same time thus pred->skip() can + // return nullptr or another node. + succs[layer] = foundNode ? foundNode : node; + } + return foundLayer; + } + + size_t size() const { + return size_.load(std::memory_order_relaxed); + } + + int height() const { + return head_.load(std::memory_order_consume)->height(); + } + + int maxLayer() const { + return height() - 1; + } + + size_t incrementSize(int delta) { + return size_.fetch_add(delta, std::memory_order_relaxed) + delta; + } + + // Returns the node if found, nullptr otherwise. + NodeType* find(const value_type& data) { + auto ret = findNode(data); + if (ret.second && !ret.first->markedForRemoval()) { + return ret.first; + } + return nullptr; + } + + // lock all the necessary nodes for changing (adding or removing) the list. + // returns true if all the lock acquried successfully and the related nodes + // are all validate (not in certain pending states), false otherwise. + bool lockNodesForChange( + int nodeHeight, + ScopedLocker guards[MAX_HEIGHT], + NodeType* preds[MAX_HEIGHT], + NodeType* succs[MAX_HEIGHT], + bool adding = true) { + NodeType *pred, *succ, *prevPred = nullptr; + bool valid = true; + for (int layer = 0; valid && layer < nodeHeight; ++layer) { + pred = preds[layer]; + DCHECK(pred != nullptr) << "layer=" << layer << " height=" << height() + << " nodeheight=" << nodeHeight; + succ = succs[layer]; + if (pred != prevPred) { + guards[layer] = pred->acquireGuard(); + prevPred = pred; + } + valid = !pred->markedForRemoval() && + pred->skip(layer) == succ; // check again after locking + + if (adding) { // when adding a node, the succ shouldn't be going away + valid = valid && (succ == nullptr || !succ->markedForRemoval()); + } + } + + return valid; + } + + // Returns a paired value: + // pair.first always stores the pointer to the node with the same input key. + // It could be either the newly added data, or the existed data in the + // list with the same key. + // pair.second stores whether the data is added successfully: + // 0 means not added, otherwise reutrns the new size. + template <typename U> + std::pair<NodeType*, size_t> addOrGetData(U&& data) { + NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT]; + NodeType* newNode; + size_t newSize; + while (true) { + int max_layer = 0; + int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer); + + if (layer >= 0) { + NodeType* nodeFound = succs[layer]; + DCHECK(nodeFound != nullptr); + if (nodeFound->markedForRemoval()) { + continue; // if it's getting deleted retry finding node. + } + // wait until fully linked. + while (UNLIKELY(!nodeFound->fullyLinked())) { + } + return std::make_pair(nodeFound, 0); + } + + // need to capped at the original height -- the real height may have grown + int nodeHeight = + detail::SkipListRandomHeight::instance()->getHeight(max_layer + 1); + + ScopedLocker guards[MAX_HEIGHT]; + if (!lockNodesForChange(nodeHeight, guards, preds, succs)) { + continue; // give up the locks and retry until all valid + } + + // locks acquired and all valid, need to modify the links under the locks. + newNode = NodeType::create( + recycler_.alloc(), nodeHeight, std::forward<U>(data)); + for (int k = 0; k < nodeHeight; ++k) { + newNode->setSkip(k, succs[k]); + preds[k]->setSkip(k, newNode); + } + + newNode->setFullyLinked(); + newSize = incrementSize(1); + break; + } + + int hgt = height(); + size_t sizeLimit = + detail::SkipListRandomHeight::instance()->getSizeLimit(hgt); + + if (hgt < MAX_HEIGHT && newSize > sizeLimit) { + growHeight(hgt + 1); + } + CHECK_GT(newSize, 0); + return std::make_pair(newNode, newSize); + } + + bool remove(const value_type& data) { + NodeType* nodeToDelete = nullptr; + ScopedLocker nodeGuard; + bool isMarked = false; + int nodeHeight = 0; + NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT]; + + while (true) { + int max_layer = 0; + int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer); + if (!isMarked && (layer < 0 || !okToDelete(succs[layer], layer))) { + return false; + } + + if (!isMarked) { + nodeToDelete = succs[layer]; + nodeHeight = nodeToDelete->height(); + nodeGuard = nodeToDelete->acquireGuard(); + if (nodeToDelete->markedForRemoval()) { + return false; + } + nodeToDelete->setMarkedForRemoval(); + isMarked = true; + } + + // acquire pred locks from bottom layer up + ScopedLocker guards[MAX_HEIGHT]; + if (!lockNodesForChange(nodeHeight, guards, preds, succs, false)) { + continue; // this will unlock all the locks + } + + for (int k = nodeHeight - 1; k >= 0; --k) { + preds[k]->setSkip(k, nodeToDelete->skip(k)); + } + + incrementSize(-1); + break; + } + recycle(nodeToDelete); + return true; + } + + const value_type* first() const { + auto node = head_.load(std::memory_order_consume)->skip(0); + return node ? &node->data() : nullptr; + } + + const value_type* last() const { + NodeType* pred = head_.load(std::memory_order_consume); + NodeType* node = nullptr; + for (int layer = maxLayer(); layer >= 0; --layer) { + do { + node = pred->skip(layer); + if (node) { + pred = node; + } + } while (node != nullptr); + } + return pred == head_.load(std::memory_order_relaxed) ? nullptr + : &pred->data(); + } + + static bool okToDelete(NodeType* candidate, int layer) { + DCHECK(candidate != nullptr); + return candidate->fullyLinked() && candidate->maxLayer() == layer && + !candidate->markedForRemoval(); + } + + // find node for insertion/deleting + int findInsertionPointGetMaxLayer( + const value_type& data, + NodeType* preds[], + NodeType* succs[], + int* max_layer) const { + *max_layer = maxLayer(); + return findInsertionPoint( + head_.load(std::memory_order_consume), *max_layer, data, preds, succs); + } + + // Find node for access. Returns a paired values: + // pair.first = the first node that no-less than data value + // pair.second = 1 when the data value is founded, or 0 otherwise. + // This is like lower_bound, but not exact: we could have the node marked for + // removal so still need to check that. + std::pair<NodeType*, int> findNode(const value_type& data) const { + return findNodeDownRight(data); + } + + // Find node by first stepping down then stepping right. Based on benchmark + // results, this is slightly faster than findNodeRightDown for better + // localality on the skipping pointers. + std::pair<NodeType*, int> findNodeDownRight(const value_type& data) const { + NodeType* pred = head_.load(std::memory_order_consume); + int ht = pred->height(); + NodeType* node = nullptr; + + bool found = false; + while (!found) { + // stepping down + for (; ht > 0 && less(data, node = pred->skip(ht - 1)); --ht) { + } + if (ht == 0) { + return std::make_pair(node, 0); // not found + } + // node <= data now, but we need to fix up ht + --ht; + + // stepping right + while (greater(data, node)) { + pred = node; + node = node->skip(ht); + } + found = !less(data, node); + } + return std::make_pair(node, found); + } + + // find node by first stepping right then stepping down. + // We still keep this for reference purposes. + std::pair<NodeType*, int> findNodeRightDown(const value_type& data) const { + NodeType* pred = head_.load(std::memory_order_consume); + NodeType* node = nullptr; + auto top = maxLayer(); + int found = 0; + for (int layer = top; !found && layer >= 0; --layer) { + node = pred->skip(layer); + while (greater(data, node)) { + pred = node; + node = node->skip(layer); + } + found = !less(data, node); + } + return std::make_pair(node, found); + } + + NodeType* lower_bound(const value_type& data) const { + auto node = findNode(data).first; + while (node != nullptr && node->markedForRemoval()) { + node = node->skip(0); + } + return node; + } + + void growHeight(int height) { + NodeType* oldHead = head_.load(std::memory_order_consume); + if (oldHead->height() >= height) { // someone else already did this + return; + } + + NodeType* newHead = + NodeType::create(recycler_.alloc(), height, value_type(), true); + + { // need to guard the head node in case others are adding/removing + // nodes linked to the head. + ScopedLocker g = oldHead->acquireGuard(); + newHead->copyHead(oldHead); + NodeType* expected = oldHead; + if (!head_.compare_exchange_strong( + expected, newHead, std::memory_order_release)) { + // if someone has already done the swap, just return. + NodeType::destroy(recycler_.alloc(), newHead); + return; + } + oldHead->setMarkedForRemoval(); + } + recycle(oldHead); + } + + void recycle(NodeType* node) { + recycler_.add(node); + } + + detail::NodeRecycler<NodeType, NodeAlloc> recycler_; + std::atomic<NodeType*> head_; + std::atomic<size_t> size_; +}; + +template <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT> +class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Accessor { + typedef detail::SkipListNode<T> NodeType; + typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType; + + public: + typedef T value_type; + typedef T key_type; + typedef T& reference; + typedef T* pointer; + typedef const T& const_reference; + typedef const T* const_pointer; + typedef size_t size_type; + typedef Comp key_compare; + typedef Comp value_compare; + + typedef typename SkipListType::iterator iterator; + typedef typename SkipListType::const_iterator const_iterator; + typedef typename SkipListType::Skipper Skipper; + + explicit Accessor(std::shared_ptr<ConcurrentSkipList> skip_list) + : slHolder_(std::move(skip_list)) { + sl_ = slHolder_.get(); + DCHECK(sl_ != nullptr); + sl_->recycler_.addRef(); + } + + // Unsafe initializer: the caller assumes the responsibility to keep + // skip_list valid during the whole life cycle of the Acessor. + explicit Accessor(ConcurrentSkipList* skip_list) : sl_(skip_list) { + DCHECK(sl_ != nullptr); + sl_->recycler_.addRef(); + } + + Accessor(const Accessor& accessor) + : sl_(accessor.sl_), slHolder_(accessor.slHolder_) { + sl_->recycler_.addRef(); + } + + Accessor& operator=(const Accessor& accessor) { + if (this != &accessor) { + slHolder_ = accessor.slHolder_; + sl_->recycler_.releaseRef(); + sl_ = accessor.sl_; + sl_->recycler_.addRef(); + } + return *this; + } + + ~Accessor() { + sl_->recycler_.releaseRef(); + } + + bool empty() const { + return sl_->size() == 0; + } + size_t size() const { + return sl_->size(); + } + size_type max_size() const { + return std::numeric_limits<size_type>::max(); + } + + // returns end() if the value is not in the list, otherwise returns an + // iterator pointing to the data, and it's guaranteed that the data is valid + // as far as the Accessor is hold. + iterator find(const key_type& value) { + return iterator(sl_->find(value)); + } + const_iterator find(const key_type& value) const { + return iterator(sl_->find(value)); + } + size_type count(const key_type& data) const { + return contains(data); + } + + iterator begin() const { + NodeType* head = sl_->head_.load(std::memory_order_consume); + return iterator(head->next()); + } + iterator end() const { + return iterator(nullptr); + } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + + template < + typename U, + typename = + typename std::enable_if<std::is_convertible<U, T>::value>::type> + std::pair<iterator, bool> insert(U&& data) { + auto ret = sl_->addOrGetData(std::forward<U>(data)); + return std::make_pair(iterator(ret.first), ret.second); + } + size_t erase(const key_type& data) { + return remove(data); + } + + iterator lower_bound(const key_type& data) const { + return iterator(sl_->lower_bound(data)); + } + + size_t height() const { + return sl_->height(); + } + + // first() returns pointer to the first element in the skiplist, or + // nullptr if empty. + // + // last() returns the pointer to the last element in the skiplist, + // nullptr if list is empty. + // + // Note: As concurrent writing can happen, first() is not + // guaranteed to be the min_element() in the list. Similarly + // last() is not guaranteed to be the max_element(), and both of them can + // be invalid (i.e. nullptr), so we name them differently from front() and + // tail() here. + const key_type* first() const { + return sl_->first(); + } + const key_type* last() const { + return sl_->last(); + } + + // Try to remove the last element in the skip list. + // + // Returns true if we removed it, false if either the list is empty + // or a race condition happened (i.e. the used-to-be last element + // was already removed by another thread). + bool pop_back() { + auto last = sl_->last(); + return last ? sl_->remove(*last) : false; + } + + std::pair<key_type*, bool> addOrGetData(const key_type& data) { + auto ret = sl_->addOrGetData(data); + return std::make_pair(&ret.first->data(), ret.second); + } + + SkipListType* skiplist() const { + return sl_; + } + + // legacy interfaces + // TODO:(xliu) remove these. + // Returns true if the node is added successfully, false if not, i.e. the + // node with the same key already existed in the list. + bool contains(const key_type& data) const { + return sl_->find(data); + } + bool add(const key_type& data) { + return sl_->addOrGetData(data).second; + } + bool remove(const key_type& data) { + return sl_->remove(data); + } + + private: + SkipListType* sl_; + std::shared_ptr<SkipListType> slHolder_; +}; + +// implements forward iterator concept. +template <typename ValT, typename NodeT> +class detail::csl_iterator : public detail::IteratorFacade< + csl_iterator<ValT, NodeT>, + ValT, + std::forward_iterator_tag> { + public: + typedef ValT value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef ptrdiff_t difference_type; + + explicit csl_iterator(NodeT* node = nullptr) : node_(node) {} + + template <typename OtherVal, typename OtherNode> + csl_iterator( + const csl_iterator<OtherVal, OtherNode>& other, + typename std::enable_if< + std::is_convertible<OtherVal, ValT>::value>::type* = nullptr) + : node_(other.node_) {} + + size_t nodeSize() const { + return node_ == nullptr ? 0 + : node_->height() * sizeof(NodeT*) + sizeof(*this); + } + + bool good() const { + return node_ != nullptr; + } + + private: + template <class, class> + friend class csl_iterator; + friend class detail:: + IteratorFacade<csl_iterator, ValT, std::forward_iterator_tag>; + + void increment() { + node_ = node_->next(); + } + bool equal(const csl_iterator& other) const { + return node_ == other.node_; + } + value_type& dereference() const { + return node_->data(); + } + + NodeT* node_; +}; + +// Skipper interface +template <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT> +class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Skipper { + typedef detail::SkipListNode<T> NodeType; + typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType; + typedef typename SkipListType::Accessor Accessor; + + public: + typedef T value_type; + typedef T& reference; + typedef T* pointer; + typedef ptrdiff_t difference_type; + + Skipper(const std::shared_ptr<SkipListType>& skipList) : accessor_(skipList) { + init(); + } + + Skipper(const Accessor& accessor) : accessor_(accessor) { + init(); + } + + void init() { + // need to cache the head node + NodeType* head_node = head(); + headHeight_ = head_node->height(); + for (int i = 0; i < headHeight_; ++i) { + preds_[i] = head_node; + succs_[i] = head_node->skip(i); + } + int max_layer = maxLayer(); + for (int i = 0; i < max_layer; ++i) { + hints_[i] = uint8_t(i + 1); + } + hints_[max_layer] = max_layer; + } + + // advance to the next node in the list. + Skipper& operator++() { + preds_[0] = succs_[0]; + succs_[0] = preds_[0]->skip(0); + int height = curHeight(); + for (int i = 1; i < height && preds_[0] == succs_[i]; ++i) { + preds_[i] = succs_[i]; + succs_[i] = preds_[i]->skip(i); + } + return *this; + } + + bool good() const { + return succs_[0] != nullptr; + } + + int maxLayer() const { + return headHeight_ - 1; + } + + int curHeight() const { + // need to cap the height to the cached head height, as the current node + // might be some newly inserted node and also during the time period the + // head height may have grown. + return succs_[0] ? std::min(headHeight_, succs_[0]->height()) : 0; + } + + const value_type& data() const { + DCHECK(succs_[0] != nullptr); + return succs_[0]->data(); + } + + value_type& operator*() const { + DCHECK(succs_[0] != nullptr); + return succs_[0]->data(); + } + + value_type* operator->() { + DCHECK(succs_[0] != nullptr); + return &succs_[0]->data(); + } + + /* + * Skip to the position whose data is no less than the parameter. + * (I.e. the lower_bound). + * + * Returns true if the data is found, false otherwise. + */ + bool to(const value_type& data) { + int layer = curHeight() - 1; + if (layer < 0) { + return false; // reaches the end of the list + } + + int lyr = hints_[layer]; + int max_layer = maxLayer(); + while (SkipListType::greater(data, succs_[lyr]) && lyr < max_layer) { + ++lyr; + } + hints_[layer] = lyr; // update the hint + + int foundLayer = SkipListType::findInsertionPoint( + preds_[lyr], lyr, data, preds_, succs_); + if (foundLayer < 0) { + return false; + } + + DCHECK(succs_[0] != nullptr) + << "lyr=" << lyr << "; max_layer=" << max_layer; + return !succs_[0]->markedForRemoval(); + } + + private: + NodeType* head() const { + return accessor_.skiplist()->head_.load(std::memory_order_consume); + } + + Accessor accessor_; + int headHeight_; + NodeType *succs_[MAX_HEIGHT], *preds_[MAX_HEIGHT]; + uint8_t hints_[MAX_HEIGHT]; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ConstexprMath.h b/ios/Pods/Flipper-Folly/folly/ConstexprMath.h new file mode 100644 index 000000000..4a70ff6cd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ConstexprMath.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> +#include <functional> +#include <limits> +#include <type_traits> + +namespace folly { +// TLDR: Prefer using operator< for ordering. And when +// a and b are equivalent objects, we return b to make +// sorting stable. +// See http://stepanovpapers.com/notes.pdf for details. +template <typename T> +constexpr T constexpr_max(T a) { + return a; +} +template <typename T, typename... Ts> +constexpr T constexpr_max(T a, T b, Ts... ts) { + return b < a ? constexpr_max(a, ts...) : constexpr_max(b, ts...); +} + +// When a and b are equivalent objects, we return a to +// make sorting stable. +template <typename T> +constexpr T constexpr_min(T a) { + return a; +} +template <typename T, typename... Ts> +constexpr T constexpr_min(T a, T b, Ts... ts) { + return b < a ? constexpr_min(b, ts...) : constexpr_min(a, ts...); +} + +template <typename T, typename Less> +constexpr T const& +constexpr_clamp(T const& v, T const& lo, T const& hi, Less less) { + return less(v, lo) ? lo : less(hi, v) ? hi : v; +} +template <typename T> +constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { + return constexpr_clamp(v, lo, hi, std::less<T>{}); +} + +namespace detail { + +template <typename T, typename = void> +struct constexpr_abs_helper {}; + +template <typename T> +struct constexpr_abs_helper< + T, + typename std::enable_if<std::is_floating_point<T>::value>::type> { + static constexpr T go(T t) { + return t < static_cast<T>(0) ? -t : t; + } +}; + +template <typename T> +struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value && + std::is_unsigned<T>::value>::type> { + static constexpr T go(T t) { + return t; + } +}; + +template <typename T> +struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value && + std::is_signed<T>::value>::type> { + static constexpr typename std::make_unsigned<T>::type go(T t) { + return typename std::make_unsigned<T>::type(t < static_cast<T>(0) ? -t : t); + } +}; +} // namespace detail + +template <typename T> +constexpr auto constexpr_abs(T t) + -> decltype(detail::constexpr_abs_helper<T>::go(t)) { + return detail::constexpr_abs_helper<T>::go(t); +} + +namespace detail { +template <typename T> +constexpr T constexpr_log2_(T a, T e) { + return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2)); +} + +template <typename T> +constexpr T constexpr_log2_ceil_(T l2, T t) { + return l2 + T(T(1) << l2 < t ? 1 : 0); +} + +template <typename T> +constexpr T constexpr_square_(T t) { + return t * t; +} +} // namespace detail + +template <typename T> +constexpr T constexpr_log2(T t) { + return detail::constexpr_log2_(T(0), t); +} + +template <typename T> +constexpr T constexpr_log2_ceil(T t) { + return detail::constexpr_log2_ceil_(constexpr_log2(t), t); +} + +template <typename T> +constexpr T constexpr_ceil(T t, T round) { + return round == T(0) + ? t + : ((t + (t < T(0) ? T(0) : round - T(1))) / round) * round; +} + +template <typename T> +constexpr T constexpr_pow(T base, std::size_t exp) { + return exp == 0 + ? T(1) + : exp == 1 ? base + : detail::constexpr_square_(constexpr_pow(base, exp / 2)) * + (exp % 2 ? base : T(1)); +} + +/// constexpr_find_last_set +/// +/// Return the 1-based index of the most significant bit which is set. +/// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)). +template <typename T> +constexpr std::size_t constexpr_find_last_set(T const t) { + using U = std::make_unsigned_t<T>; + return t == T(0) ? 0 : 1 + constexpr_log2(static_cast<U>(t)); +} + +namespace detail { +template <typename U> +constexpr std::size_t +constexpr_find_first_set_(std::size_t s, std::size_t a, U const u) { + return s == 0 ? a + : constexpr_find_first_set_( + s / 2, a + s * bool((u >> a) % (U(1) << s) == U(0)), u); +} +} // namespace detail + +/// constexpr_find_first_set +/// +/// Return the 1-based index of the least significant bit which is set. +/// For x > 0, the exponent in the largest power of two which does not divide x. +template <typename T> +constexpr std::size_t constexpr_find_first_set(T t) { + using U = std::make_unsigned_t<T>; + using size = std::integral_constant<std::size_t, sizeof(T) * 4>; + return t == T(0) + ? 0 + : 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast<U>(t)); +} + +template <typename T> +constexpr T constexpr_add_overflow_clamped(T a, T b) { + using L = std::numeric_limits<T>; + using M = std::intmax_t; + static_assert( + !std::is_integral<T>::value || sizeof(T) <= sizeof(M), + "Integral type too large!"); + // clang-format off + return + // don't do anything special for non-integral types. + !std::is_integral<T>::value ? a + b : + // for narrow integral types, just convert to intmax_t. + sizeof(T) < sizeof(M) + ? T(constexpr_clamp(M(a) + M(b), M(L::min()), M(L::max()))) : + // when a >= 0, cannot add more than `MAX - a` onto a. + !(a < 0) ? a + constexpr_min(b, T(L::max() - a)) : + // a < 0 && b >= 0, `a + b` will always be in valid range of type T. + !(b < 0) ? a + b : + // a < 0 && b < 0, keep the result >= MIN. + a + constexpr_max(b, T(L::min() - a)); + // clang-format on +} + +template <typename T> +constexpr T constexpr_sub_overflow_clamped(T a, T b) { + using L = std::numeric_limits<T>; + using M = std::intmax_t; + static_assert( + !std::is_integral<T>::value || sizeof(T) <= sizeof(M), + "Integral type too large!"); + // clang-format off + return + // don't do anything special for non-integral types. + !std::is_integral<T>::value ? a - b : + // for unsigned type, keep result >= 0. + std::is_unsigned<T>::value ? (a < b ? 0 : a - b) : + // for narrow signed integral types, just convert to intmax_t. + sizeof(T) < sizeof(M) + ? T(constexpr_clamp(M(a) - M(b), M(L::min()), M(L::max()))) : + // (a >= 0 && b >= 0) || (a < 0 && b < 0), `a - b` will always be valid. + (a < 0) == (b < 0) ? a - b : + // MIN < b, so `-b` should be in valid range (-MAX <= -b <= MAX), + // convert subtraction to addition. + L::min() < b ? constexpr_add_overflow_clamped(a, T(-b)) : + // -b = -MIN = (MAX + 1) and a <= -1, result is in valid range. + a < 0 ? a - b : + // -b = -MIN = (MAX + 1) and a >= 0, result > MAX. + L::max(); + // clang-format on +} + +// clamp_cast<> provides sane numeric conversions from float point numbers to +// integral numbers, and between different types of integral numbers. It helps +// to avoid unexpected bugs introduced by bad conversion, and undefined behavior +// like overflow when casting float point numbers to integral numbers. +// +// When doing clamp_cast<Dst>(value), if `value` is in valid range of Dst, +// it will give correct result in Dst, equal to `value`. +// +// If `value` is outside the representable range of Dst, it will be clamped to +// MAX or MIN in Dst, instead of being undefined behavior. +// +// Float NaNs are converted to 0 in integral type. +// +// Here's some comparision with static_cast<>: +// (with FB-internal gcc-5-glibc-2.23 toolchain) +// +// static_cast<int32_t>(NaN) = 6 +// clamp_cast<int32_t>(NaN) = 0 +// +// static_cast<int32_t>(9999999999.0f) = -348639895 +// clamp_cast<int32_t>(9999999999.0f) = 2147483647 +// +// static_cast<int32_t>(2147483647.0f) = -348639895 +// clamp_cast<int32_t>(2147483647.0f) = 2147483647 +// +// static_cast<uint32_t>(4294967295.0f) = 0 +// clamp_cast<uint32_t>(4294967295.0f) = 4294967295 +// +// static_cast<uint32_t>(-1) = 4294967295 +// clamp_cast<uint32_t>(-1) = 0 +// +// static_cast<int16_t>(32768u) = -32768 +// clamp_cast<int16_t>(32768u) = 32767 + +template <typename Dst, typename Src> +constexpr typename std::enable_if<std::is_integral<Src>::value, Dst>::type +constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits<Dst>; + // clang-format off + return + // Check if Src and Dst have same signedness. + std::is_signed<Src>::value == std::is_signed<Dst>::value + ? ( + // Src and Dst have same signedness. If sizeof(Src) <= sizeof(Dst), + // we can safely convert Src to Dst without any loss of accuracy. + sizeof(Src) <= sizeof(Dst) ? Dst(src) : + // If Src is larger in size, we need to clamp it to valid range in Dst. + Dst(constexpr_clamp(src, Src(L::min()), Src(L::max())))) + // Src and Dst have different signedness. + // Check if it's signed -> unsigend cast. + : std::is_signed<Src>::value && std::is_unsigned<Dst>::value + ? ( + // If src < 0, the result should be 0. + src < 0 ? Dst(0) : + // Otherwise, src >= 0. If src can fit into Dst, we can safely cast it + // without loss of accuracy. + sizeof(Src) <= sizeof(Dst) ? Dst(src) : + // If Src is larger in size than Dst, we need to ensure the result is + // at most Dst MAX. + Dst(constexpr_min(src, Src(L::max())))) + // It's unsigned -> signed cast. + : ( + // Since Src is unsigned, and Dst is signed, Src can fit into Dst only + // when sizeof(Src) < sizeof(Dst). + sizeof(Src) < sizeof(Dst) ? Dst(src) : + // If Src does not fit into Dst, we need to ensure the result is at most + // Dst MAX. + Dst(constexpr_min(src, Src(L::max())))); + // clang-format on +} + +namespace detail { +// Upper/lower bound values that could be accurately represented in both +// integral and float point types. +constexpr double kClampCastLowerBoundDoubleToInt64F = -9223372036854774784.0; +constexpr double kClampCastUpperBoundDoubleToInt64F = 9223372036854774784.0; +constexpr double kClampCastUpperBoundDoubleToUInt64F = 18446744073709549568.0; + +constexpr float kClampCastLowerBoundFloatToInt32F = -2147483520.0f; +constexpr float kClampCastUpperBoundFloatToInt32F = 2147483520.0f; +constexpr float kClampCastUpperBoundFloatToUInt32F = 4294967040.0f; + +// This works the same as constexpr_clamp, but the comparision are done in Src +// to prevent any implicit promotions. +template <typename D, typename S> +constexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) { + return src < sl ? dl : (src > su ? du : D(src)); +} +} // namespace detail + +template <typename Dst, typename Src> +constexpr typename std::enable_if<std::is_floating_point<Src>::value, Dst>::type +constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits<Dst>; + // clang-format off + return + // Special case: cast NaN into 0. + // Using a trick here to portably check for NaN: f != f only if f is NaN. + // see: https://stackoverflow.com/a/570694 + (src != src) ? Dst(0) : + // using `sizeof(Src) > sizeof(Dst)` as a heuristic that Dst can be + // represented in Src without loss of accuracy. + // see: https://en.wikipedia.org/wiki/Floating-point_arithmetic + sizeof(Src) > sizeof(Dst) ? + detail::constexpr_clamp_cast_helper( + src, Src(L::min()), Src(L::max()), L::min(), L::max()) : + // sizeof(Src) < sizeof(Dst) only happens when doing cast of + // 32bit float -> u/int64_t. + // Losslessly promote float into double, change into double -> u/int64_t. + sizeof(Src) < sizeof(Dst) ? ( + src >= 0.0 + ? constexpr_clamp_cast<Dst>( + constexpr_clamp_cast<std::uint64_t>(double(src))) + : constexpr_clamp_cast<Dst>( + constexpr_clamp_cast<std::int64_t>(double(src)))) : + // The following are for sizeof(Src) == sizeof(Dst). + std::is_same<Src, double>::value && std::is_same<Dst, int64_t>::value ? + detail::constexpr_clamp_cast_helper( + double(src), + detail::kClampCastLowerBoundDoubleToInt64F, + detail::kClampCastUpperBoundDoubleToInt64F, + L::min(), + L::max()) : + std::is_same<Src, double>::value && std::is_same<Dst, uint64_t>::value ? + detail::constexpr_clamp_cast_helper( + double(src), + 0.0, + detail::kClampCastUpperBoundDoubleToUInt64F, + L::min(), + L::max()) : + std::is_same<Src, float>::value && std::is_same<Dst, int32_t>::value ? + detail::constexpr_clamp_cast_helper( + float(src), + detail::kClampCastLowerBoundFloatToInt32F, + detail::kClampCastUpperBoundFloatToInt32F, + L::min(), + L::max()) : + detail::constexpr_clamp_cast_helper( + float(src), + 0.0f, + detail::kClampCastUpperBoundFloatToUInt32F, + L::min(), + L::max()); + // clang-format on +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Conv.cpp b/ios/Pods/Flipper-Folly/folly/Conv.cpp new file mode 100644 index 000000000..b6aba9301 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Conv.cpp @@ -0,0 +1,797 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Conv.h> +#include <array> + +namespace folly { +namespace detail { + +namespace { + +/** + * Finds the first non-digit in a string. The number of digits + * searched depends on the precision of the Tgt integral. Assumes the + * string starts with NO whitespace and NO sign. + * + * The semantics of the routine is: + * for (;; ++b) { + * if (b >= e || !isdigit(*b)) return b; + * } + * + * Complete unrolling marks bottom-line (i.e. entire conversion) + * improvements of 20%. + */ +inline const char* findFirstNonDigit(const char* b, const char* e) { + for (; b < e; ++b) { + auto const c = static_cast<unsigned>(*b) - '0'; + if (c >= 10) { + break; + } + } + return b; +} + +// Maximum value of number when represented as a string +template <class T> +struct MaxString { + static const char* const value; +}; + +template <> +const char* const MaxString<uint8_t>::value = "255"; +template <> +const char* const MaxString<uint16_t>::value = "65535"; +template <> +const char* const MaxString<uint32_t>::value = "4294967295"; +#if __SIZEOF_LONG__ == 4 +template <> +const char* const MaxString<unsigned long>::value = "4294967295"; +#else +template <> +const char* const MaxString<unsigned long>::value = "18446744073709551615"; +#endif +static_assert( + sizeof(unsigned long) >= 4, + "Wrong value for MaxString<unsigned long>::value," + " please update."); +template <> +const char* const MaxString<unsigned long long>::value = "18446744073709551615"; +static_assert( + sizeof(unsigned long long) >= 8, + "Wrong value for MaxString<unsigned long long>::value" + ", please update."); + +#if FOLLY_HAVE_INT128_T +template <> +const char* const MaxString<__uint128_t>::value = + "340282366920938463463374607431768211455"; +#endif + +/* + * Lookup tables that converts from a decimal character value to an integral + * binary value, shifted by a decimal "shift" multiplier. + * For all character values in the range '0'..'9', the table at those + * index locations returns the actual decimal value shifted by the multiplier. + * For all other values, the lookup table returns an invalid OOR value. + */ +// Out-of-range flag value, larger than the largest value that can fit in +// four decimal bytes (9999), but four of these added up together should +// still not overflow uint16_t. +constexpr int32_t OOR = 10000; + +alignas(16) constexpr uint16_t shift1[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1, // 40 + 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift10[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 10, // 40 + 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift100[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 100, // 40 + 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift1000[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1000, // 40 + 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +struct ErrorString { + const char* string; + bool quote; +}; + +// Keep this in sync with ConversionCode in Conv.h +constexpr const std::array< + ErrorString, + static_cast<std::size_t>(ConversionCode::NUM_ERROR_CODES)> + kErrorStrings{{ + {"Success", true}, + {"Empty input string", true}, + {"No digits found in input string", true}, + {"Integer overflow when parsing bool (must be 0 or 1)", true}, + {"Invalid value for bool", true}, + {"Non-digit character found", true}, + {"Invalid leading character", true}, + {"Overflow during conversion", true}, + {"Negative overflow during conversion", true}, + {"Unable to convert string to floating point value", true}, + {"Non-whitespace character found after end of conversion", true}, + {"Overflow during arithmetic conversion", false}, + {"Negative overflow during arithmetic conversion", false}, + {"Loss of precision during arithmetic conversion", false}, + }}; + +// Check if ASCII is really ASCII +using IsAscii = + bool_constant<'A' == 65 && 'Z' == 90 && 'a' == 97 && 'z' == 122>; + +// The code in this file that uses tolower() really only cares about +// 7-bit ASCII characters, so we can take a nice shortcut here. +inline char tolower_ascii(char in) { + return IsAscii::value ? in | 0x20 : char(std::tolower(in)); +} + +inline bool bool_str_cmp(const char** b, size_t len, const char* value) { + // Can't use strncasecmp, since we want to ensure that the full value matches + const char* p = *b; + const char* e = *b + len; + const char* v = value; + while (*v != '\0') { + if (p == e || tolower_ascii(*p) != *v) { // value is already lowercase + return false; + } + ++p; + ++v; + } + + *b = p; + return true; +} + +} // namespace + +Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept { + auto b = src->begin(), e = src->end(); + for (;; ++b) { + if (b >= e) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (!std::isspace(*b)) { + break; + } + } + + bool result; + auto len = size_t(e - b); + switch (*b) { + case '0': + case '1': { + result = false; + for (; b < e && isdigit(*b); ++b) { + if (result || (*b != '0' && *b != '1')) { + return makeUnexpected(ConversionCode::BOOL_OVERFLOW); + } + result = (*b == '1'); + } + break; + } + case 'y': + case 'Y': + result = true; + if (!bool_str_cmp(&b, len, "yes")) { + ++b; // accept the single 'y' character + } + break; + case 'n': + case 'N': + result = false; + if (!bool_str_cmp(&b, len, "no")) { + ++b; + } + break; + case 't': + case 'T': + result = true; + if (!bool_str_cmp(&b, len, "true")) { + ++b; + } + break; + case 'f': + case 'F': + result = false; + if (!bool_str_cmp(&b, len, "false")) { + ++b; + } + break; + case 'o': + case 'O': + if (bool_str_cmp(&b, len, "on")) { + result = true; + } else if (bool_str_cmp(&b, len, "off")) { + result = false; + } else { + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + } + break; + default: + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + } + + src->assign(b, e); + + return result; +} + +/** + * StringPiece to double, with progress information. Alters the + * StringPiece parameter to munch the already-parsed characters. + */ +template <class Tgt> +Expected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept { + using namespace double_conversion; + static StringToDoubleConverter conv( + StringToDoubleConverter::ALLOW_TRAILING_JUNK | + StringToDoubleConverter::ALLOW_LEADING_SPACES, + 0.0, + // return this for junk input string + std::numeric_limits<double>::quiet_NaN(), + nullptr, + nullptr); + + if (src->empty()) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + int length; + auto result = conv.StringToDouble( + src->data(), + static_cast<int>(src->size()), + &length); // processed char count + + if (!std::isnan(result)) { + // If we get here with length = 0, the input string is empty. + // If we get here with result = 0.0, it's either because the string + // contained only whitespace, or because we had an actual zero value + // (with potential trailing junk). If it was only whitespace, we + // want to raise an error; length will point past the last character + // that was processed, so we need to check if that character was + // whitespace or not. + if (length == 0 || + (result == 0.0 && std::isspace((*src)[size_t(length) - 1]))) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (length >= 2) { + const char* suffix = src->data() + length - 1; + // double_conversion doesn't update length correctly when there is an + // incomplete exponent specifier. Converting "12e-f-g" shouldn't consume + // any more than "12", but it will consume "12e-". + + // "123-" should only parse "123" + if (*suffix == '-' || *suffix == '+') { + --suffix; + --length; + } + // "12e-f-g" or "12euro" should only parse "12" + if (*suffix == 'e' || *suffix == 'E') { + --length; + } + } + src->advance(size_t(length)); + return Tgt(result); + } + + auto* e = src->end(); + auto* b = + std::find_if_not(src->begin(), e, [](char c) { return std::isspace(c); }); + + // There must be non-whitespace, otherwise we would have caught this above + assert(b < e); + auto size = size_t(e - b); + + bool negative = false; + if (*b == '-') { + negative = true; + ++b; + --size; + } + + result = 0.0; + + switch (tolower_ascii(*b)) { + case 'i': + if (size >= 3 && tolower_ascii(b[1]) == 'n' && + tolower_ascii(b[2]) == 'f') { + if (size >= 8 && tolower_ascii(b[3]) == 'i' && + tolower_ascii(b[4]) == 'n' && tolower_ascii(b[5]) == 'i' && + tolower_ascii(b[6]) == 't' && tolower_ascii(b[7]) == 'y') { + b += 8; + } else { + b += 3; + } + result = std::numeric_limits<Tgt>::infinity(); + } + break; + + case 'n': + if (size >= 3 && tolower_ascii(b[1]) == 'a' && + tolower_ascii(b[2]) == 'n') { + b += 3; + result = std::numeric_limits<Tgt>::quiet_NaN(); + } + break; + + default: + break; + } + + if (result == 0.0) { + // All bets are off + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + + if (negative) { + result = -result; + } + + src->assign(b, e); + + return Tgt(result); +} + +template Expected<float, ConversionCode> str_to_floating<float>( + StringPiece* src) noexcept; +template Expected<double, ConversionCode> str_to_floating<double>( + StringPiece* src) noexcept; + +/** + * This class takes care of additional processing needed for signed values, + * like leading sign character and overflow checks. + */ +template <typename T, bool IsSigned = std::is_signed<T>::value> +class SignedValueHandler; + +template <typename T> +class SignedValueHandler<T, true> { + public: + ConversionCode init(const char*& b) { + negative_ = false; + if (!std::isdigit(*b)) { + if (*b == '-') { + negative_ = true; + } else if (UNLIKELY(*b != '+')) { + return ConversionCode::INVALID_LEADING_CHAR; + } + ++b; + } + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return negative_ ? ConversionCode::NEGATIVE_OVERFLOW + : ConversionCode::POSITIVE_OVERFLOW; + } + + template <typename U> + Expected<T, ConversionCode> finalize(U value) { + T rv; + if (negative_) { + FOLLY_PUSH_WARNING + FOLLY_MSVC_DISABLE_WARNING(4146) + + // unary minus operator applied to unsigned type, result still unsigned + rv = T(-value); + + FOLLY_POP_WARNING + + if (UNLIKELY(rv > 0)) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + } else { + rv = T(value); + if (UNLIKELY(rv < 0)) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + } + return rv; + } + + private: + bool negative_; +}; + +// For unsigned types, we don't need any extra processing +template <typename T> +class SignedValueHandler<T, false> { + public: + ConversionCode init(const char*&) { + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return ConversionCode::POSITIVE_OVERFLOW; + } + + Expected<T, ConversionCode> finalize(T value) { + return value; + } +}; + +/** + * String represented as a pair of pointers to char to signed/unsigned + * integrals. Assumes NO whitespace before or after, and also that the + * string is composed entirely of digits (and an optional sign only for + * signed types). String may be empty, in which case digits_to returns + * an appropriate error. + */ +template <class Tgt> +inline Expected<Tgt, ConversionCode> digits_to( + const char* b, + const char* const e) noexcept { + using UT = typename std::make_unsigned<Tgt>::type; + assert(b <= e); + + SignedValueHandler<Tgt> sgn; + + auto err = sgn.init(b); + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + + auto size = size_t(e - b); + + /* Although the string is entirely made of digits, we still need to + * check for overflow. + */ + if (size > std::numeric_limits<UT>::digits10) { + // Leading zeros? + if (b < e && *b == '0') { + for (++b;; ++b) { + if (b == e) { + return Tgt(0); // just zeros, e.g. "0000" + } + if (*b != '0') { + size = size_t(e - b); + break; + } + } + } + if (size > std::numeric_limits<UT>::digits10 && + (size != std::numeric_limits<UT>::digits10 + 1 || + strncmp(b, MaxString<UT>::value, size) > 0)) { + return makeUnexpected(sgn.overflow()); + } + } + + // Here we know that the number won't overflow when + // converted. Proceed without checks. + + UT result = 0; + + for (; e - b >= 4; b += 4) { + result *= UT(10000); + const int32_t r0 = shift1000[static_cast<size_t>(b[0])]; + const int32_t r1 = shift100[static_cast<size_t>(b[1])]; + const int32_t r2 = shift10[static_cast<size_t>(b[2])]; + const int32_t r3 = shift1[static_cast<size_t>(b[3])]; + const auto sum = r0 + r1 + r2 + r3; + if (sum >= OOR) { + goto outOfRange; + } + result += UT(sum); + } + + switch (e - b) { + case 3: { + const int32_t r0 = shift100[static_cast<size_t>(b[0])]; + const int32_t r1 = shift10[static_cast<size_t>(b[1])]; + const int32_t r2 = shift1[static_cast<size_t>(b[2])]; + const auto sum = r0 + r1 + r2; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(1000 * result + sum); + break; + } + case 2: { + const int32_t r0 = shift10[static_cast<size_t>(b[0])]; + const int32_t r1 = shift1[static_cast<size_t>(b[1])]; + const auto sum = r0 + r1; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(100 * result + sum); + break; + } + case 1: { + const int32_t sum = shift1[static_cast<size_t>(b[0])]; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(10 * result + sum); + break; + } + default: + assert(b == e); + if (size == 0) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + break; + } + + return sgn.finalize(result); + +outOfRange: + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); +} + +template Expected<char, ConversionCode> digits_to<char>( + const char*, + const char*) noexcept; +template Expected<signed char, ConversionCode> digits_to<signed char>( + const char*, + const char*) noexcept; +template Expected<unsigned char, ConversionCode> digits_to<unsigned char>( + const char*, + const char*) noexcept; + +template Expected<short, ConversionCode> digits_to<short>( + const char*, + const char*) noexcept; +template Expected<unsigned short, ConversionCode> digits_to<unsigned short>( + const char*, + const char*) noexcept; + +template Expected<int, ConversionCode> digits_to<int>( + const char*, + const char*) noexcept; +template Expected<unsigned int, ConversionCode> digits_to<unsigned int>( + const char*, + const char*) noexcept; + +template Expected<long, ConversionCode> digits_to<long>( + const char*, + const char*) noexcept; +template Expected<unsigned long, ConversionCode> digits_to<unsigned long>( + const char*, + const char*) noexcept; + +template Expected<long long, ConversionCode> digits_to<long long>( + const char*, + const char*) noexcept; +template Expected<unsigned long long, ConversionCode> +digits_to<unsigned long long>(const char*, const char*) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> digits_to<__int128>( + const char*, + const char*) noexcept; +template Expected<unsigned __int128, ConversionCode> +digits_to<unsigned __int128>(const char*, const char*) noexcept; +#endif + +/** + * StringPiece to integrals, with progress information. Alters the + * StringPiece parameter to munch the already-parsed characters. + */ +template <class Tgt> +Expected<Tgt, ConversionCode> str_to_integral(StringPiece* src) noexcept { + using UT = typename std::make_unsigned<Tgt>::type; + + auto b = src->data(), past = src->data() + src->size(); + + for (;; ++b) { + if (UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (!std::isspace(*b)) { + break; + } + } + + SignedValueHandler<Tgt> sgn; + auto err = sgn.init(b); + + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + if (std::is_signed<Tgt>::value && UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + if (UNLIKELY(!isdigit(*b))) { + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); + } + + auto m = findFirstNonDigit(b + 1, past); + + auto tmp = digits_to<UT>(b, m); + + if (UNLIKELY(!tmp.hasValue())) { + return makeUnexpected( + tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow() + : tmp.error()); + } + + auto res = sgn.finalize(tmp.value()); + + if (res.hasValue()) { + src->advance(size_t(m - src->data())); + } + + return res; +} + +template Expected<char, ConversionCode> str_to_integral<char>( + StringPiece* src) noexcept; +template Expected<signed char, ConversionCode> str_to_integral<signed char>( + StringPiece* src) noexcept; +template Expected<unsigned char, ConversionCode> str_to_integral<unsigned char>( + StringPiece* src) noexcept; + +template Expected<short, ConversionCode> str_to_integral<short>( + StringPiece* src) noexcept; +template Expected<unsigned short, ConversionCode> +str_to_integral<unsigned short>(StringPiece* src) noexcept; + +template Expected<int, ConversionCode> str_to_integral<int>( + StringPiece* src) noexcept; +template Expected<unsigned int, ConversionCode> str_to_integral<unsigned int>( + StringPiece* src) noexcept; + +template Expected<long, ConversionCode> str_to_integral<long>( + StringPiece* src) noexcept; +template Expected<unsigned long, ConversionCode> str_to_integral<unsigned long>( + StringPiece* src) noexcept; + +template Expected<long long, ConversionCode> str_to_integral<long long>( + StringPiece* src) noexcept; +template Expected<unsigned long long, ConversionCode> +str_to_integral<unsigned long long>(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +template Expected<unsigned __int128, ConversionCode> +str_to_integral<unsigned __int128>(StringPiece* src) noexcept; +#endif + +} // namespace detail + +ConversionError makeConversionError(ConversionCode code, StringPiece input) { + using namespace detail; + static_assert( + std::is_unsigned<std::underlying_type<ConversionCode>::type>::value, + "ConversionCode should be unsigned"); + assert((std::size_t)code < kErrorStrings.size()); + const ErrorString& err = kErrorStrings[(std::size_t)code]; + if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) { + return {err.string, code}; + } + std::string tmp(err.string); + tmp.append(": "); + if (err.quote) { + tmp.append(1, '"'); + } + if (!input.empty()) { + tmp.append(input.data(), input.size()); + } + if (err.quote) { + tmp.append(1, '"'); + } + return {tmp, code}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Conv.h b/ios/Pods/Flipper-Folly/folly/Conv.h new file mode 100644 index 000000000..c3ff64596 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Conv.h @@ -0,0 +1,1663 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 provides a generic interface for converting objects to and from + * string-like types (std::string, fbstring, StringPiece), as well as + * range-checked conversions between numeric and enum types. The mechanisms are + * extensible, so that user-specified types can add folly::to support. + * + ******************************************************************************* + * TYPE -> STRING CONVERSIONS + ******************************************************************************* + * You can call the to<std::string> or to<fbstring>. These are variadic + * functions that convert their arguments to strings, and concatenate them to + * form a result. So, for example, + * + * auto str = to<std::string>(123, "456", 789); + * + * Sets str to "123456789". + * + * In addition to just concatenating the arguments, related functions can + * delimit them with some string: toDelim<std::string>(",", "123", 456, "789") + * will return the string "123,456,789". + * + * toAppend does not return a string; instead, it takes a pointer to a string as + * its last argument, and appends the result of the concatenation into it: + * std::string str = "123"; + * toAppend(456, "789", &str); // Now str is "123456789". + * + * The toAppendFit function acts like toAppend, but it precalculates the size + * required to perform the append operation, and reserves that space in the + * output string before actually inserting its arguments. This can sometimes + * save on string expansion, but beware: appending to the same string many times + * with toAppendFit is likely a pessimization, since it will resize the string + * once per append. + * + * The combination of the append and delim variants also exist: toAppendDelim + * and toAppendDelimFit are defined, with the obvious semantics. + * + ******************************************************************************* + * STRING -> TYPE CONVERSIONS + ******************************************************************************* + * Going in the other direction, and parsing a string into a C++ type, is also + * supported: + * to<int>("123"); // Returns 123. + * + * Out of range (e.g. to<std::uint8_t>("1000")), or invalidly formatted (e.g. + * to<int>("four")) inputs will throw. If throw-on-error is undesirable (for + * instance: you're dealing with untrusted input, and want to protect yourself + * from users sending you down a very slow exception-throwing path), you can use + * tryTo<T>, which will return an Expected<T, ConversionCode>. + * + * There are overloads of to() and tryTo() that take a StringPiece*. These parse + * out a type from the beginning of a string, and modify the passed-in + * StringPiece to indicate the portion of the string not consumed. + * + ******************************************************************************* + * NUMERIC / ENUM CONVERSIONS + ******************************************************************************* + * Conv also supports a to<T>(S) overload, where T and S are numeric or enum + * types, that checks to see that the target type can represent its argument, + * and will throw if it cannot. This includes cases where a floating point -> + * integral conversion is attempted on a value with a non-zero fractional + * component, and integral -> floating point conversions that would lose + * precision. Enum conversions are range-checked for the underlying type of the + * enum, but there is no check that the input value is a valid choice of enum + * value. + * + ******************************************************************************* + * CUSTOM TYPE CONVERSIONS + ******************************************************************************* + * Users may customize the string conversion functionality for their own data + * types, . The key functions you should implement are: + * // Two functions to allow conversion to your type from a string. + * Expected<StringPiece, ConversionCode> parseTo(folly::StringPiece in, + * YourType& out); + * YourErrorType makeConversionError(YourErrorType in, StringPiece in); + * // Two functions to allow conversion from your type to a string. + * template <class String> + * void toAppend(const YourType& in, String* out); + * size_t estimateSpaceNeeded(const YourType& in); + * + * These are documented below, inline. + */ + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cctype> +#include <climits> +#include <cstddef> +#include <limits> +#include <stdexcept> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <double-conversion/double-conversion.h> // V8 JavaScript implementation + +#include <folly/Demangle.h> +#include <folly/Expected.h> +#include <folly/FBString.h> +#include <folly/Likely.h> +#include <folly/Range.h> +#include <folly/Traits.h> +#include <folly/Unit.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> +#include <folly/lang/Pretty.h> +#include <folly/portability/Math.h> + +namespace folly { + +// Keep this in sync with kErrorStrings in Conv.cpp +enum class ConversionCode : unsigned char { + SUCCESS, + EMPTY_INPUT_STRING, + NO_DIGITS, + BOOL_OVERFLOW, + BOOL_INVALID_VALUE, + NON_DIGIT_CHAR, + INVALID_LEADING_CHAR, + POSITIVE_OVERFLOW, + NEGATIVE_OVERFLOW, + STRING_TO_FLOAT_ERROR, + NON_WHITESPACE_AFTER_END, + ARITH_POSITIVE_OVERFLOW, + ARITH_NEGATIVE_OVERFLOW, + ARITH_LOSS_OF_PRECISION, + NUM_ERROR_CODES, // has to be the last entry +}; + +struct ConversionErrorBase : std::range_error { + using std::range_error::range_error; +}; + +class ConversionError : public ConversionErrorBase { + public: + ConversionError(const std::string& str, ConversionCode code) + : ConversionErrorBase(str), code_(code) {} + + ConversionError(const char* str, ConversionCode code) + : ConversionErrorBase(str), code_(code) {} + + ConversionCode errorCode() const { + return code_; + } + + private: + ConversionCode code_; +}; + +/******************************************************************************* + * Custom Error Translation + * + * Your overloaded parseTo() function can return a custom error code on failure. + * ::folly::to() will call makeConversionError to translate that error code into + * an object to throw. makeConversionError is found by argument-dependent + * lookup. It should have this signature: + * + * namespace other_namespace { + * enum YourErrorCode { BAD_ERROR, WORSE_ERROR }; + * + * struct YourConversionError : ConversionErrorBase { + * YourConversionError(const char* what) : ConversionErrorBase(what) {} + * }; + * + * YourConversionError + * makeConversionError(YourErrorCode code, ::folly::StringPiece sp) { + * ... + * return YourConversionError(messageString); + * } + ******************************************************************************/ +ConversionError makeConversionError(ConversionCode code, StringPiece input); + +namespace detail { +/** + * Enforce that the suffix following a number is made up only of whitespace. + */ +inline ConversionCode enforceWhitespaceErr(StringPiece sp) { + for (auto c : sp) { + if (UNLIKELY(!std::isspace(c))) { + return ConversionCode::NON_WHITESPACE_AFTER_END; + } + } + return ConversionCode::SUCCESS; +} + +/** + * Keep this implementation around for prettyToDouble(). + */ +inline void enforceWhitespace(StringPiece sp) { + auto err = enforceWhitespaceErr(sp); + if (err != ConversionCode::SUCCESS) { + throw_exception(makeConversionError(err, sp)); + } +} +} // namespace detail + +/** + * The identity conversion function. + * tryTo<T>(T) returns itself for all types T. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_same<Tgt, typename std::decay<Src>::type>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(Src&& value) { + return std::forward<Src>(value); +} + +template <class Tgt, class Src> +typename std::enable_if< + std::is_same<Tgt, typename std::decay<Src>::type>::value, + Tgt>::type +to(Src&& value) { + return std::forward<Src>(value); +} + +/******************************************************************************* + * Arithmetic to boolean + ******************************************************************************/ + +/** + * Unchecked conversion from arithmetic to boolean. This is different from the + * other arithmetic conversions because we use the C convention of treating any + * non-zero value as true, instead of range checking. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_arithmetic<Src>::value && !std::is_same<Tgt, Src>::value && + std::is_same<Tgt, bool>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const Src& value) { + return value != Src(); +} + +template <class Tgt, class Src> +typename std::enable_if< + std::is_arithmetic<Src>::value && !std::is_same<Tgt, Src>::value && + std::is_same<Tgt, bool>::value, + Tgt>::type +to(const Src& value) { + return value != Src(); +} + +/******************************************************************************* + * Anything to string + ******************************************************************************/ + +namespace detail { + +#ifdef _MSC_VER +// MSVC can't quite figure out the LastElementImpl::call() stuff +// in the base implementation, so we have to use tuples instead, +// which result in significantly more templates being compiled, +// though the runtime performance is the same. + +template <typename... Ts> +auto getLastElement(Ts&&... ts) -> decltype(std::get<sizeof...(Ts) - 1>( + std::forward_as_tuple(std::forward<Ts>(ts)...))) { + return std::get<sizeof...(Ts) - 1>( + std::forward_as_tuple(std::forward<Ts>(ts)...)); +} + +inline void getLastElement() {} + +template <size_t size, typename... Ts> +struct LastElementType : std::tuple_element<size - 1, std::tuple<Ts...>> {}; + +template <> +struct LastElementType<0> { + using type = void; +}; + +template <class... Ts> +struct LastElement + : std::decay<typename LastElementType<sizeof...(Ts), Ts...>::type> {}; +#else +template <typename... Ts> +struct LastElementImpl { + static void call(Ignored<Ts>...) {} +}; + +template <typename Head, typename... Ts> +struct LastElementImpl<Head, Ts...> { + template <typename Last> + static Last call(Ignored<Ts>..., Last&& last) { + return std::forward<Last>(last); + } +}; + +template <typename... Ts> +auto getLastElement(const Ts&... ts) + -> decltype(LastElementImpl<Ts...>::call(ts...)) { + return LastElementImpl<Ts...>::call(ts...); +} + +template <class... Ts> +struct LastElement : std::decay<decltype( + LastElementImpl<Ts...>::call(std::declval<Ts>()...))> { +}; +#endif + +} // namespace detail + +/******************************************************************************* + * Conversions from integral types to string types. + ******************************************************************************/ + +#if FOLLY_HAVE_INT128_T +namespace detail { + +template <typename IntegerType> +constexpr unsigned int digitsEnough() { + // digits10 returns the number of decimal digits that this type can represent, + // not the number of characters required for the max value, so we need to add + // one. ex: char digits10 returns 2, because 256-999 cannot be represented, + // but we need 3. + auto const digits10 = std::numeric_limits<IntegerType>::digits10; + return static_cast<unsigned int>(digits10) + 1; +} + +inline size_t +unsafeTelescope128(char* buffer, size_t room, unsigned __int128 x) { + typedef unsigned __int128 Usrc; + size_t p = room - 1; + + while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed + const auto y = x / 10; + const auto digit = x % 10; + + buffer[p--] = static_cast<char>('0' + digit); + x = y; + } + + uint64_t xx = static_cast<uint64_t>(x); // Rest uses faster 64-bit division + + while (xx >= 10) { + const auto y = xx / 10ULL; + const auto digit = xx % 10ULL; + + buffer[p--] = static_cast<char>('0' + digit); + xx = y; + } + + buffer[p] = static_cast<char>('0' + xx); + + return p; +} + +} // namespace detail +#endif + +/** + * Returns the number of digits in the base 10 representation of an + * uint64_t. Useful for preallocating buffers and such. It's also used + * internally, see below. Measurements suggest that defining a + * separate overload for 32-bit integers is not worthwhile. + */ + +inline uint32_t digits10(uint64_t v) { +#ifdef __x86_64__ + + // For this arch we can get a little help from specialized CPU instructions + // which can count leading zeroes; 64 minus that is appx. log (base 2). + // Use that to approximate base-10 digits (log_10) and then adjust if needed. + + // 10^i, defined for i 0 through 19. + // This is 20 * 8 == 160 bytes, which fits neatly into 5 cache lines + // (assuming a cache line size of 64). + alignas(64) static const uint64_t powersOf10[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000UL, + }; + + // "count leading zeroes" operation not valid; for 0; special case this. + if (UNLIKELY(!v)) { + return 1; + } + + // bits is in the ballpark of log_2(v). + const uint32_t leadingZeroes = __builtin_clzll(v); + const auto bits = 63 - leadingZeroes; + + // approximate log_10(v) == log_10(2) * bits. + // Integer magic below: 77/256 is appx. 0.3010 (log_10(2)). + // The +1 is to make this the ceiling of the log_10 estimate. + const uint32_t minLength = 1 + ((bits * 77) >> 8); + + // return that log_10 lower bound, plus adjust if input >= 10^(that bound) + // in case there's a small error and we misjudged length. + return minLength + uint32_t(v >= powersOf10[minLength]); + +#else + + uint32_t result = 1; + while (true) { + if (LIKELY(v < 10)) { + return result; + } + if (LIKELY(v < 100)) { + return result + 1; + } + if (LIKELY(v < 1000)) { + return result + 2; + } + if (LIKELY(v < 10000)) { + return result + 3; + } + // Skip ahead by 4 orders of magnitude + v /= 10000U; + result += 4; + } + +#endif +} + +/** + * Copies the ASCII base 10 representation of v into buffer and + * returns the number of bytes written. Does NOT append a \0. Assumes + * the buffer points to digits10(v) bytes of valid memory. Note that + * uint64_t needs at most 20 bytes, uint32_t needs at most 10 bytes, + * uint16_t needs at most 5 bytes, and so on. Measurements suggest + * that defining a separate overload for 32-bit integers is not + * worthwhile. + * + * This primitive is unsafe because it makes the size assumption and + * because it does not add a terminating \0. + */ + +inline uint32_t uint64ToBufferUnsafe(uint64_t v, char* const buffer) { + auto const result = digits10(v); + // WARNING: using size_t or pointer arithmetic for pos slows down + // the loop below 20x. This is because several 32-bit ops can be + // done in parallel, but only fewer 64-bit ones. + uint32_t pos = result - 1; + while (v >= 10) { + // Keep these together so a peephole optimization "sees" them and + // computes them in one shot. + auto const q = v / 10; + auto const r = v % 10; + buffer[pos--] = static_cast<char>('0' + r); + v = q; + } + // Last digit is trivial to handle + buffer[pos] = static_cast<char>(v + '0'); + return result; +} + +/** + * A single char gets appended. + */ +template <class Tgt> +void toAppend(char value, Tgt* result) { + *result += value; +} + +template <class T> +constexpr typename std::enable_if<std::is_same<T, char>::value, size_t>::type +estimateSpaceNeeded(T) { + return 1; +} + +template <size_t N> +constexpr size_t estimateSpaceNeeded(const char (&)[N]) { + return N; +} + +/** + * Everything implicitly convertible to const char* gets appended. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_convertible<Src, const char*>::value && + IsSomeString<Tgt>::value>::type +toAppend(Src value, Tgt* result) { + // Treat null pointers like an empty string, as in: + // operator<<(std::ostream&, const char*). + const char* c = value; + if (c) { + result->append(value); + } +} + +template <class Src> +typename std::enable_if<std::is_convertible<Src, const char*>::value, size_t>:: + type + estimateSpaceNeeded(Src value) { + const char* c = value; + if (c) { + return folly::StringPiece(value).size(); + }; + return 0; +} + +template <class Src> +typename std::enable_if<IsSomeString<Src>::value, size_t>::type +estimateSpaceNeeded(Src const& value) { + return value.size(); +} + +template <class Src> +typename std::enable_if< + std::is_convertible<Src, folly::StringPiece>::value && + !IsSomeString<Src>::value && + !std::is_convertible<Src, const char*>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return folly::StringPiece(value).size(); +} + +template <> +inline size_t estimateSpaceNeeded(std::nullptr_t /* value */) { + return 0; +} + +template <class Src> +typename std::enable_if< + std::is_pointer<Src>::value && + IsSomeString<std::remove_pointer<Src>>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return value->size(); +} + +/** + * Strings get appended, too. + */ +template <class Tgt, class Src> +typename std::enable_if< + IsSomeString<Src>::value && IsSomeString<Tgt>::value>::type +toAppend(const Src& value, Tgt* result) { + result->append(value); +} + +/** + * and StringPiece objects too + */ +template <class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend( + StringPiece value, + Tgt* result) { + result->append(value.data(), value.size()); +} + +/** + * There's no implicit conversion from fbstring to other string types, + * so make a specialization. + */ +template <class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend( + const fbstring& value, + Tgt* result) { + result->append(value.data(), value.size()); +} + +#if FOLLY_HAVE_INT128_T +/** + * Special handling for 128 bit integers. + */ + +template <class Tgt> +void toAppend(__int128 value, Tgt* result) { + typedef unsigned __int128 Usrc; + char buffer[detail::digitsEnough<unsigned __int128>() + 1]; + size_t p; + + if (value < 0) { + p = detail::unsafeTelescope128(buffer, sizeof(buffer), -Usrc(value)); + buffer[--p] = '-'; + } else { + p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); + } + + result->append(buffer + p, buffer + sizeof(buffer)); +} + +template <class Tgt> +void toAppend(unsigned __int128 value, Tgt* result) { + char buffer[detail::digitsEnough<unsigned __int128>()]; + size_t p; + + p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); + + result->append(buffer + p, buffer + sizeof(buffer)); +} + +template <class T> +constexpr + typename std::enable_if<std::is_same<T, __int128>::value, size_t>::type + estimateSpaceNeeded(T) { + return detail::digitsEnough<__int128>(); +} + +template <class T> +constexpr typename std:: + enable_if<std::is_same<T, unsigned __int128>::value, size_t>::type + estimateSpaceNeeded(T) { + return detail::digitsEnough<unsigned __int128>(); +} + +#endif + +/** + * int32_t and int64_t to string (by appending) go through here. The + * result is APPENDED to a preexisting string passed as the second + * parameter. This should be efficient with fbstring because fbstring + * incurs no dynamic allocation below 23 bytes and no number has more + * than 22 bytes in its textual representation (20 for digits, one for + * sign, one for the terminating 0). + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_integral<Src>::value && std::is_signed<Src>::value && + IsSomeString<Tgt>::value && sizeof(Src) >= 4>::type +toAppend(Src value, Tgt* result) { + char buffer[20]; + if (value < 0) { + result->push_back('-'); + result->append( + buffer, + uint64ToBufferUnsafe(~static_cast<uint64_t>(value) + 1, buffer)); + } else { + result->append(buffer, uint64ToBufferUnsafe(uint64_t(value), buffer)); + } +} + +template <class Src> +typename std::enable_if< + std::is_integral<Src>::value && std::is_signed<Src>::value && + sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + if (value < 0) { + // When "value" is the smallest negative, negating it would evoke + // undefined behavior, so, instead of writing "-value" below, we write + // "~static_cast<uint64_t>(value) + 1" + return 1 + digits10(~static_cast<uint64_t>(value) + 1); + } + + return digits10(static_cast<uint64_t>(value)); +} + +/** + * As above, but for uint32_t and uint64_t. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_integral<Src>::value && !std::is_signed<Src>::value && + IsSomeString<Tgt>::value && sizeof(Src) >= 4>::type +toAppend(Src value, Tgt* result) { + char buffer[20]; + result->append(buffer, uint64ToBufferUnsafe(value, buffer)); +} + +template <class Src> +typename std::enable_if< + std::is_integral<Src>::value && !std::is_signed<Src>::value && + sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + return digits10(value); +} + +/** + * All small signed and unsigned integers to string go through 32-bit + * types int32_t and uint32_t, respectively. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_integral<Src>::value && IsSomeString<Tgt>::value && + sizeof(Src) < 4>::type +toAppend(Src value, Tgt* result) { + typedef + typename std::conditional<std::is_signed<Src>::value, int64_t, uint64_t>:: + type Intermediate; + toAppend<Tgt>(static_cast<Intermediate>(value), result); +} + +template <class Src> +typename std::enable_if< + std::is_integral<Src>::value && sizeof(Src) < 4 && + !std::is_same<Src, char>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + typedef + typename std::conditional<std::is_signed<Src>::value, int64_t, uint64_t>:: + type Intermediate; + return estimateSpaceNeeded(static_cast<Intermediate>(value)); +} + +/** + * Enumerated values get appended as integers. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type +toAppend(Src value, Tgt* result) { + toAppend(to_underlying(value), result); +} + +template <class Src> +typename std::enable_if<std::is_enum<Src>::value, size_t>::type +estimateSpaceNeeded(Src value) { + return estimateSpaceNeeded(to_underlying(value)); +} + +/******************************************************************************* + * Conversions from floating-point types to string types. + ******************************************************************************/ + +namespace detail { +constexpr int kConvMaxDecimalInShortestLow = -6; +constexpr int kConvMaxDecimalInShortestHigh = 21; +} // namespace detail + +/** Wrapper around DoubleToStringConverter **/ +template <class Tgt, class Src> +typename std::enable_if< + std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type +toAppend( + Src value, + Tgt* result, + double_conversion::DoubleToStringConverter::DtoaMode mode, + unsigned int numDigits) { + using namespace double_conversion; + DoubleToStringConverter conv( + DoubleToStringConverter::NO_FLAGS, + "Infinity", + "NaN", + 'E', + detail::kConvMaxDecimalInShortestLow, + detail::kConvMaxDecimalInShortestHigh, + 6, // max leading padding zeros + 1); // max trailing padding zeros + char buffer[256]; + StringBuilder builder(buffer, sizeof(buffer)); + switch (mode) { + case DoubleToStringConverter::SHORTEST: + conv.ToShortest(value, &builder); + break; + case DoubleToStringConverter::SHORTEST_SINGLE: + conv.ToShortestSingle(static_cast<float>(value), &builder); + break; + case DoubleToStringConverter::FIXED: + conv.ToFixed(value, int(numDigits), &builder); + break; + case DoubleToStringConverter::PRECISION: + default: + assert(mode == DoubleToStringConverter::PRECISION); + conv.ToPrecision(value, int(numDigits), &builder); + break; + } + const size_t length = size_t(builder.position()); + builder.Finalize(); + result->append(buffer, length); +} + +/** + * As above, but for floating point + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type +toAppend(Src value, Tgt* result) { + toAppend( + value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0); +} + +/** + * Upper bound of the length of the output from + * DoubleToStringConverter::ToShortest(double, StringBuilder*), + * as used in toAppend(double, string*). + */ +template <class Src> +typename std::enable_if<std::is_floating_point<Src>::value, size_t>::type +estimateSpaceNeeded(Src value) { + // kBase10MaximalLength is 17. We add 1 for decimal point, + // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point. + constexpr int kMaxMantissaSpace = + double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1; + // strlen("E-") + digits10(numeric_limits<double>::max_exponent10) + constexpr int kMaxExponentSpace = 2 + 3; + static const int kMaxPositiveSpace = std::max({ + // E.g. 1.1111111111111111E-100. + kMaxMantissaSpace + kMaxExponentSpace, + // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6. + kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow, + // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest + // number > 1 which ToShortest outputs in exponential notation, + // so 21 is the longest non-exponential number > 1. + detail::kConvMaxDecimalInShortestHigh, + }); + return size_t( + kMaxPositiveSpace + + (value < 0 ? 1 : 0)); // +1 for minus sign, if negative +} + +/** + * This can be specialized, together with adding specialization + * for estimateSpaceNeed for your type, so that we allocate + * as much as you need instead of the default + */ +template <class Src> +struct HasLengthEstimator : std::false_type {}; + +template <class Src> +constexpr typename std::enable_if< + !std::is_fundamental<Src>::value && +#if FOLLY_HAVE_INT128_T + // On OSX 10.10, is_fundamental<__int128> is false :-O + !std::is_same<__int128, Src>::value && + !std::is_same<unsigned __int128, Src>::value && +#endif + !IsSomeString<Src>::value && + !std::is_convertible<Src, const char*>::value && + !std::is_convertible<Src, StringPiece>::value && + !std::is_enum<Src>::value && !HasLengthEstimator<Src>::value, + size_t>::type +estimateSpaceNeeded(const Src&) { + return sizeof(Src) + 1; // dumbest best effort ever? +} + +namespace detail { + +template <class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value, size_t>::type +estimateSpaceToReserve(size_t sofar, Tgt*) { + return sofar; +} + +template <class T, class... Ts> +size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) { + return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...); +} + +template <class... Ts> +void reserveInTarget(const Ts&... vs) { + getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...)); +} + +template <class Delimiter, class... Ts> +void reserveInTargetDelim(const Delimiter& d, const Ts&... vs) { + static_assert(sizeof...(vs) >= 2, "Needs at least 2 args"); + size_t fordelim = (sizeof...(vs) - 2) * + estimateSpaceToReserve(0, d, static_cast<std::string*>(nullptr)); + getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...)); +} + +/** + * Variadic base case: append one element + */ +template <class T, class Tgt> +typename std::enable_if< + IsSomeString<typename std::remove_pointer<Tgt>::type>::value>::type +toAppendStrImpl(const T& v, Tgt result) { + toAppend(v, result); +} + +template <class T, class... Ts> +typename std::enable_if< + sizeof...(Ts) >= 2 && + IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppendStrImpl(const T& v, const Ts&... vs) { + toAppend(v, getLastElement(vs...)); + toAppendStrImpl(vs...); +} + +template <class Delimiter, class T, class Tgt> +typename std::enable_if< + IsSomeString<typename std::remove_pointer<Tgt>::type>::value>::type +toAppendDelimStrImpl(const Delimiter& /* delim */, const T& v, Tgt result) { + toAppend(v, result); +} + +template <class Delimiter, class T, class... Ts> +typename std::enable_if< + sizeof...(Ts) >= 2 && + IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { + // we are really careful here, calling toAppend with just one element does + // not try to estimate space needed (as we already did that). If we call + // toAppend(v, delim, ....) we would do unnecesary size calculation + toAppend(v, detail::getLastElement(vs...)); + toAppend(delim, detail::getLastElement(vs...)); + toAppendDelimStrImpl(delim, vs...); +} +} // namespace detail + +/** + * Variadic conversion to string. Appends each element in turn. + * If we have two or more things to append, we will not reserve + * the space for them and will depend on strings exponential growth. + * If you just append once consider using toAppendFit which reserves + * the space needed (but does not have exponential as a result). + * + * Custom implementations of toAppend() can be provided in the same namespace as + * the type to customize printing. estimateSpaceNeed() may also be provided to + * avoid reallocations in toAppendFit(): + * + * namespace other_namespace { + * + * template <class String> + * void toAppend(const OtherType&, String* out); + * + * // optional + * size_t estimateSpaceNeeded(const OtherType&); + * + * } + */ +template <class... Ts> +typename std::enable_if< + sizeof...(Ts) >= 3 && + IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppend(const Ts&... vs) { + ::folly::detail::toAppendStrImpl(vs...); +} + +#ifdef _MSC_VER +// Special case pid_t on MSVC, because it's a void* rather than an +// integral type. We can't do a global special case because this is already +// dangerous enough (as most pointers will implicitly convert to a void*) +// just doing it for MSVC. +template <class Tgt> +void toAppend(const pid_t a, Tgt* res) { + toAppend(uint64_t(a), res); +} +#endif + +/** + * Special version of the call that preallocates exaclty as much memory + * as need for arguments to be stored in target. This means we are + * not doing exponential growth when we append. If you are using it + * in a loop you are aiming at your foot with a big perf-destroying + * bazooka. + * On the other hand if you are appending to a string once, this + * will probably save a few calls to malloc. + */ +template <class... Ts> +typename std::enable_if<IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppendFit(const Ts&... vs) { + ::folly::detail::reserveInTarget(vs...); + toAppend(vs...); +} + +template <class Ts> +void toAppendFit(const Ts&) {} + +/** + * Variadic base case: do nothing. + */ +template <class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend( + Tgt* /* result */) {} + +/** + * Variadic base case: do nothing. + */ +template <class Delimiter, class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim( + const Delimiter& /* delim */, + Tgt* /* result */) {} + +/** + * 1 element: same as toAppend. + */ +template <class Delimiter, class T, class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type +toAppendDelim(const Delimiter& /* delim */, const T& v, Tgt* tgt) { + toAppend(v, tgt); +} + +/** + * Append to string with a delimiter in between elements. Check out + * comments for toAppend for details about memory allocation. + */ +template <class Delimiter, class... Ts> +typename std::enable_if< + sizeof...(Ts) >= 3 && + IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppendDelim(const Delimiter& delim, const Ts&... vs) { + detail::toAppendDelimStrImpl(delim, vs...); +} + +/** + * Detail in comment for toAppendFit + */ +template <class Delimiter, class... Ts> +typename std::enable_if<IsSomeString<typename std::remove_pointer< + typename detail::LastElement<const Ts&...>::type>::type>::value>::type +toAppendDelimFit(const Delimiter& delim, const Ts&... vs) { + detail::reserveInTargetDelim(delim, vs...); + toAppendDelim(delim, vs...); +} + +template <class De, class Ts> +void toAppendDelimFit(const De&, const Ts&) {} + +/** + * to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end + * for all types. + */ +template <class Tgt, class... Ts> +typename std::enable_if< + IsSomeString<Tgt>::value && + (sizeof...(Ts) != 1 || + !std::is_same<Tgt, typename detail::LastElement<const Ts&...>::type>:: + value), + Tgt>::type +to(const Ts&... vs) { + Tgt result; + toAppendFit(vs..., &result); + return result; +} + +/** + * Special version of to<SomeString> for floating point. When calling + * folly::to<SomeString>(double), generic implementation above will + * firstly reserve 24 (or 25 when negative value) bytes. This will + * introduce a malloc call for most mainstream string implementations. + * + * But for most cases, a floating point doesn't need 24 (or 25) bytes to + * be converted as a string. + * + * This special version will not do string reserve. + */ +template <class Tgt, class Src> +typename std::enable_if< + IsSomeString<Tgt>::value && std::is_floating_point<Src>::value, + Tgt>::type +to(Src value) { + Tgt result; + toAppend(value, &result); + return result; +} + +/** + * toDelim<SomeString>(SomeString str) returns itself. + */ +template <class Tgt, class Delim, class Src> +typename std::enable_if< + IsSomeString<Tgt>::value && + std::is_same<Tgt, typename std::decay<Src>::type>::value, + Tgt>::type +toDelim(const Delim& /* delim */, Src&& value) { + return std::forward<Src>(value); +} + +/** + * toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as + * back-end for all types. + */ +template <class Tgt, class Delim, class... Ts> +typename std::enable_if< + IsSomeString<Tgt>::value && + (sizeof...(Ts) != 1 || + !std::is_same<Tgt, typename detail::LastElement<const Ts&...>::type>:: + value), + Tgt>::type +toDelim(const Delim& delim, const Ts&... vs) { + Tgt result; + toAppendDelimFit(delim, vs..., &result); + return result; +} + +/******************************************************************************* + * Conversions from string types to integral types. + ******************************************************************************/ + +namespace detail { + +Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept; + +template <typename T> +Expected<T, ConversionCode> str_to_floating(StringPiece* src) noexcept; + +extern template Expected<float, ConversionCode> str_to_floating<float>( + StringPiece* src) noexcept; +extern template Expected<double, ConversionCode> str_to_floating<double>( + StringPiece* src) noexcept; + +template <class Tgt> +Expected<Tgt, ConversionCode> digits_to(const char* b, const char* e) noexcept; + +extern template Expected<char, ConversionCode> digits_to<char>( + const char*, + const char*) noexcept; +extern template Expected<signed char, ConversionCode> digits_to<signed char>( + const char*, + const char*) noexcept; +extern template Expected<unsigned char, ConversionCode> +digits_to<unsigned char>(const char*, const char*) noexcept; + +extern template Expected<short, ConversionCode> digits_to<short>( + const char*, + const char*) noexcept; +extern template Expected<unsigned short, ConversionCode> +digits_to<unsigned short>(const char*, const char*) noexcept; + +extern template Expected<int, ConversionCode> digits_to<int>( + const char*, + const char*) noexcept; +extern template Expected<unsigned int, ConversionCode> digits_to<unsigned int>( + const char*, + const char*) noexcept; + +extern template Expected<long, ConversionCode> digits_to<long>( + const char*, + const char*) noexcept; +extern template Expected<unsigned long, ConversionCode> +digits_to<unsigned long>(const char*, const char*) noexcept; + +extern template Expected<long long, ConversionCode> digits_to<long long>( + const char*, + const char*) noexcept; +extern template Expected<unsigned long long, ConversionCode> +digits_to<unsigned long long>(const char*, const char*) noexcept; + +#if FOLLY_HAVE_INT128_T +extern template Expected<__int128, ConversionCode> digits_to<__int128>( + const char*, + const char*) noexcept; +extern template Expected<unsigned __int128, ConversionCode> +digits_to<unsigned __int128>(const char*, const char*) noexcept; +#endif + +template <class T> +Expected<T, ConversionCode> str_to_integral(StringPiece* src) noexcept; + +extern template Expected<char, ConversionCode> str_to_integral<char>( + StringPiece* src) noexcept; +extern template Expected<signed char, ConversionCode> +str_to_integral<signed char>(StringPiece* src) noexcept; +extern template Expected<unsigned char, ConversionCode> +str_to_integral<unsigned char>(StringPiece* src) noexcept; + +extern template Expected<short, ConversionCode> str_to_integral<short>( + StringPiece* src) noexcept; +extern template Expected<unsigned short, ConversionCode> +str_to_integral<unsigned short>(StringPiece* src) noexcept; + +extern template Expected<int, ConversionCode> str_to_integral<int>( + StringPiece* src) noexcept; +extern template Expected<unsigned int, ConversionCode> +str_to_integral<unsigned int>(StringPiece* src) noexcept; + +extern template Expected<long, ConversionCode> str_to_integral<long>( + StringPiece* src) noexcept; +extern template Expected<unsigned long, ConversionCode> +str_to_integral<unsigned long>(StringPiece* src) noexcept; + +extern template Expected<long long, ConversionCode> str_to_integral<long long>( + StringPiece* src) noexcept; +extern template Expected<unsigned long long, ConversionCode> +str_to_integral<unsigned long long>(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +extern template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +extern template Expected<unsigned __int128, ConversionCode> +str_to_integral<unsigned __int128>(StringPiece* src) noexcept; +#endif + +template <typename T> +typename std:: + enable_if<std::is_same<T, bool>::value, Expected<T, ConversionCode>>::type + convertTo(StringPiece* src) noexcept { + return str_to_bool(src); +} + +template <typename T> +typename std::enable_if< + std::is_floating_point<T>::value, + Expected<T, ConversionCode>>::type +convertTo(StringPiece* src) noexcept { + return str_to_floating<T>(src); +} + +template <typename T> +typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value, + Expected<T, ConversionCode>>::type +convertTo(StringPiece* src) noexcept { + return str_to_integral<T>(src); +} + +} // namespace detail + +/** + * String represented as a pair of pointers to char to unsigned + * integrals. Assumes NO whitespace before or after. + */ +template <typename Tgt> +typename std::enable_if< + std::is_integral<Tgt>::value && !std::is_same<Tgt, bool>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const char* b, const char* e) { + return detail::digits_to<Tgt>(b, e); +} + +template <typename Tgt> +typename std::enable_if< + std::is_integral<Tgt>::value && !std::is_same<Tgt, bool>::value, + Tgt>::type +to(const char* b, const char* e) { + return tryTo<Tgt>(b, e).thenOrThrow( + [](Tgt res) { return res; }, + [=](ConversionCode code) { + return makeConversionError(code, StringPiece(b, e)); + }); +} + +/******************************************************************************* + * Conversions from string types to arithmetic types. + ******************************************************************************/ + +/** + * Parsing strings to numeric types. + */ +template <typename Tgt> +FOLLY_NODISCARD inline typename std::enable_if< + std::is_arithmetic<Tgt>::value, + Expected<StringPiece, ConversionCode>>::type +parseTo(StringPiece src, Tgt& out) { + return detail::convertTo<Tgt>(&src).then( + [&](Tgt res) { return void(out = res), src; }); +} + +/******************************************************************************* + * Integral / Floating Point to integral / Floating Point + ******************************************************************************/ + +namespace detail { + +/** + * Bool to integral/float doesn't need any special checks, and this + * overload means we aren't trying to see if a bool is less than + * an integer. + */ +template <class Tgt> +typename std::enable_if< + !std::is_same<Tgt, bool>::value && + (std::is_integral<Tgt>::value || std::is_floating_point<Tgt>::value), + Expected<Tgt, ConversionCode>>::type +convertTo(const bool& value) noexcept { + return static_cast<Tgt>(value ? 1 : 0); +} + +/** + * Checked conversion from integral to integral. The checks are only + * performed when meaningful, e.g. conversion from int to long goes + * unchecked. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_integral<Src>::value && !std::is_same<Tgt, Src>::value && + !std::is_same<Tgt, bool>::value && std::is_integral<Tgt>::value, + Expected<Tgt, ConversionCode>>::type +convertTo(const Src& value) noexcept { + if /* constexpr */ ( + std::make_unsigned_t<Tgt>(std::numeric_limits<Tgt>::max()) < + std::make_unsigned_t<Src>(std::numeric_limits<Src>::max())) { + if (greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)) { + return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + } + } + if /* constexpr */ ( + std::is_signed<Src>::value && + (!std::is_signed<Tgt>::value || sizeof(Src) > sizeof(Tgt))) { + if (less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)) { + return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + } + } + return static_cast<Tgt>(value); +} + +/** + * Checked conversion from floating to floating. The checks are only + * performed when meaningful, e.g. conversion from float to double goes + * unchecked. + */ +template <class Tgt, class Src> +typename std::enable_if< + std::is_floating_point<Tgt>::value && std::is_floating_point<Src>::value && + !std::is_same<Tgt, Src>::value, + Expected<Tgt, ConversionCode>>::type +convertTo(const Src& value) noexcept { + if /* constexpr */ ( + std::numeric_limits<Tgt>::max() < std::numeric_limits<Src>::max()) { + if (value > std::numeric_limits<Tgt>::max()) { + return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + } + if (value < std::numeric_limits<Tgt>::lowest()) { + return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + } + } + return static_cast<Tgt>(value); +} + +/** + * Check if a floating point value can safely be converted to an + * integer value without triggering undefined behaviour. + */ +template <typename Tgt, typename Src> +inline typename std::enable_if< + std::is_floating_point<Src>::value && std::is_integral<Tgt>::value && + !std::is_same<Tgt, bool>::value, + bool>::type +checkConversion(const Src& value) { + constexpr Src tgtMaxAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::max()); + constexpr Src tgtMinAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::min()); + if (value >= tgtMaxAsSrc) { + if (value > tgtMaxAsSrc) { + return false; + } + const Src mmax = folly::nextafter(tgtMaxAsSrc, Src()); + if (static_cast<Tgt>(value - mmax) > + std::numeric_limits<Tgt>::max() - static_cast<Tgt>(mmax)) { + return false; + } + } else if (std::is_signed<Tgt>::value && value <= tgtMinAsSrc) { + if (value < tgtMinAsSrc) { + return false; + } + const Src mmin = folly::nextafter(tgtMinAsSrc, Src()); + if (static_cast<Tgt>(value - mmin) < + std::numeric_limits<Tgt>::min() - static_cast<Tgt>(mmin)) { + return false; + } + } + return true; +} + +// Integers can always safely be converted to floating point values +template <typename Tgt, typename Src> +constexpr typename std::enable_if< + std::is_integral<Src>::value && std::is_floating_point<Tgt>::value, + bool>::type +checkConversion(const Src&) { + return true; +} + +// Also, floating point values can always be safely converted to bool +// Per the standard, any floating point value that is not zero will yield true +template <typename Tgt, typename Src> +constexpr typename std::enable_if< + std::is_floating_point<Src>::value && std::is_same<Tgt, bool>::value, + bool>::type +checkConversion(const Src&) { + return true; +} + +/** + * Checked conversion from integral to floating point and back. The + * result must be convertible back to the source type without loss of + * precision. This seems Draconian but sometimes is what's needed, and + * complements existing routines nicely. For various rounding + * routines, see <math>. + */ +template <typename Tgt, typename Src> +typename std::enable_if< + (std::is_integral<Src>::value && std::is_floating_point<Tgt>::value) || + (std::is_floating_point<Src>::value && std::is_integral<Tgt>::value), + Expected<Tgt, ConversionCode>>::type +convertTo(const Src& value) noexcept { + if (LIKELY(checkConversion<Tgt>(value))) { + Tgt result = static_cast<Tgt>(value); + if (LIKELY(checkConversion<Src>(result))) { + Src witness = static_cast<Src>(result); + if (LIKELY(value == witness)) { + return result; + } + } + } + return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION); +} + +template <typename Tgt, typename Src> +inline std::string errorValue(const Src& value) { + return to<std::string>("(", pretty_name<Tgt>(), ") ", value); +} + +template <typename Tgt, typename Src> +using IsArithToArith = bool_constant< + !std::is_same<Tgt, Src>::value && !std::is_same<Tgt, bool>::value && + std::is_arithmetic<Src>::value && std::is_arithmetic<Tgt>::value>; + +} // namespace detail + +template <typename Tgt, typename Src> +typename std::enable_if< + detail::IsArithToArith<Tgt, Src>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const Src& value) noexcept { + return detail::convertTo<Tgt>(value); +} + +template <typename Tgt, typename Src> +typename std::enable_if<detail::IsArithToArith<Tgt, Src>::value, Tgt>::type to( + const Src& value) { + return tryTo<Tgt>(value).thenOrThrow( + [](Tgt res) { return res; }, + [&](ConversionCode e) { + return makeConversionError(e, detail::errorValue<Tgt>(value)); + }); +} + +/******************************************************************************* + * Custom Conversions + * + * Any type can be used with folly::to by implementing parseTo. The + * implementation should be provided in the namespace of the type to facilitate + * argument-dependent lookup: + * + * namespace other_namespace { + * ::folly::Expected<::folly::StringPiece, SomeErrorCode> + * parseTo(::folly::StringPiece, OtherType&) noexcept; + * } + ******************************************************************************/ +template <class T> +FOLLY_NODISCARD typename std::enable_if< + std::is_enum<T>::value, + Expected<StringPiece, ConversionCode>>::type +parseTo(StringPiece in, T& out) noexcept { + typename std::underlying_type<T>::type tmp{}; + auto restOrError = parseTo(in, tmp); + out = static_cast<T>(tmp); // Harmless if parseTo fails + return restOrError; +} + +FOLLY_NODISCARD +inline Expected<StringPiece, ConversionCode> parseTo( + StringPiece in, + StringPiece& out) noexcept { + out = in; + return StringPiece{in.end(), in.end()}; +} + +FOLLY_NODISCARD +inline Expected<StringPiece, ConversionCode> parseTo( + StringPiece in, + std::string& out) { + out.clear(); + out.append(in.data(), in.size()); // TODO try/catch? + return StringPiece{in.end(), in.end()}; +} + +FOLLY_NODISCARD +inline Expected<StringPiece, ConversionCode> parseTo( + StringPiece in, + fbstring& out) { + out.clear(); + out.append(in.data(), in.size()); // TODO try/catch? + return StringPiece{in.end(), in.end()}; +} + +namespace detail { +template <typename Tgt> +using ParseToResult = decltype(parseTo(StringPiece{}, std::declval<Tgt&>())); + +struct CheckTrailingSpace { + Expected<Unit, ConversionCode> operator()(StringPiece sp) const { + auto e = enforceWhitespaceErr(sp); + if (UNLIKELY(e != ConversionCode::SUCCESS)) { + return makeUnexpected(e); + } + return unit; + } +}; + +template <class Error> +struct ReturnUnit { + template <class T> + constexpr Expected<Unit, Error> operator()(T&&) const { + return unit; + } +}; + +// Older versions of the parseTo customization point threw on error and +// returned void. Handle that. +template <class Tgt> +inline typename std::enable_if< + std::is_void<ParseToResult<Tgt>>::value, + Expected<StringPiece, ConversionCode>>::type +parseToWrap(StringPiece sp, Tgt& out) { + parseTo(sp, out); + return StringPiece(sp.end(), sp.end()); +} + +template <class Tgt> +inline typename std::enable_if< + !std::is_void<ParseToResult<Tgt>>::value, + ParseToResult<Tgt>>::type +parseToWrap(StringPiece sp, Tgt& out) { + return parseTo(sp, out); +} + +template <typename Tgt> +using ParseToError = ExpectedErrorType<decltype( + detail::parseToWrap(StringPiece{}, std::declval<Tgt&>()))>; + +} // namespace detail + +/** + * String or StringPiece to target conversion. Accepts leading and trailing + * whitespace, but no non-space trailing characters. + */ + +template <class Tgt> +inline typename std::enable_if< + !std::is_same<StringPiece, Tgt>::value, + Expected<Tgt, detail::ParseToError<Tgt>>>::type +tryTo(StringPiece src) { + Tgt result{}; + using Error = detail::ParseToError<Tgt>; + using Check = typename std::conditional< + std::is_arithmetic<Tgt>::value, + detail::CheckTrailingSpace, + detail::ReturnUnit<Error>>::type; + return parseTo(src, result).then(Check(), [&](Unit) { + return std::move(result); + }); +} + +template <class Tgt, class Src> +inline typename std::enable_if< + IsSomeString<Src>::value && !std::is_same<StringPiece, Tgt>::value, + Tgt>::type +to(Src const& src) { + return to<Tgt>(StringPiece(src.data(), src.size())); +} + +template <class Tgt> +inline + typename std::enable_if<!std::is_same<StringPiece, Tgt>::value, Tgt>::type + to(StringPiece src) { + Tgt result{}; + using Error = detail::ParseToError<Tgt>; + using Check = typename std::conditional< + std::is_arithmetic<Tgt>::value, + detail::CheckTrailingSpace, + detail::ReturnUnit<Error>>::type; + auto tmp = detail::parseToWrap(src, result); + return tmp + .thenOrThrow( + Check(), + [&](Error e) { throw_exception(makeConversionError(e, src)); }) + .thenOrThrow( + [&](Unit) { return std::move(result); }, + [&](Error e) { + throw_exception(makeConversionError(e, tmp.value())); + }); +} + +/** + * tryTo/to that take the strings by pointer so the caller gets information + * about how much of the string was consumed by the conversion. These do not + * check for trailing whitepsace. + */ +template <class Tgt> +Expected<Tgt, detail::ParseToError<Tgt>> tryTo(StringPiece* src) { + Tgt result; + return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt { + *src = sp; + return std::move(result); + }); +} + +template <class Tgt> +Tgt to(StringPiece* src) { + Tgt result{}; + using Error = detail::ParseToError<Tgt>; + return parseTo(*src, result) + .thenOrThrow( + [&, src](StringPiece sp) -> Tgt { + *src = sp; + return std::move(result); + }, + [=](Error e) { return makeConversionError(e, *src); }); +} + +/******************************************************************************* + * Enum to anything and back + ******************************************************************************/ + +template <class Tgt, class Src> +typename std::enable_if< + std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value && + !std::is_convertible<Tgt, StringPiece>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const Src& value) { + return tryTo<Tgt>(to_underlying(value)); +} + +template <class Tgt, class Src> +typename std::enable_if< + !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value && + !std::is_same<Src, Tgt>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const Src& value) { + using I = typename std::underlying_type<Tgt>::type; + return tryTo<I>(value).then([](I i) { return static_cast<Tgt>(i); }); +} + +template <class Tgt, class Src> +typename std::enable_if< + std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value && + !std::is_convertible<Tgt, StringPiece>::value, + Tgt>::type +to(const Src& value) { + return to<Tgt>(to_underlying(value)); +} + +template <class Tgt, class Src> +typename std::enable_if< + !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value && + !std::is_same<Src, Tgt>::value, + Tgt>::type +to(const Src& value) { + return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/CppAttributes.h b/ios/Pods/Flipper-Folly/folly/CppAttributes.h new file mode 100644 index 000000000..75bf8c489 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CppAttributes.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * GCC compatible wrappers around clang attributes. + * + * @author Dominik Gabi + */ + +#pragma once + +#include <folly/Portability.h> + +#ifndef __has_attribute +#define FOLLY_HAS_ATTRIBUTE(x) 0 +#else +#define FOLLY_HAS_ATTRIBUTE(x) __has_attribute(x) +#endif + +#ifndef __has_cpp_attribute +#define FOLLY_HAS_CPP_ATTRIBUTE(x) 0 +#else +#define FOLLY_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#endif + +#ifndef __has_extension +#define FOLLY_HAS_EXTENSION(x) 0 +#else +#define FOLLY_HAS_EXTENSION(x) __has_extension(x) +#endif + +/** + * Fallthrough to indicate that `break` was left out on purpose in a switch + * statement, e.g. + * + * switch (n) { + * case 22: + * case 33: // no warning: no statements between case labels + * f(); + * case 44: // warning: unannotated fall-through + * g(); + * FOLLY_FALLTHROUGH; // no warning: annotated fall-through + * } + */ +#if FOLLY_HAS_CPP_ATTRIBUTE(fallthrough) +#define FOLLY_FALLTHROUGH [[fallthrough]] +#elif FOLLY_HAS_CPP_ATTRIBUTE(clang::fallthrough) +#define FOLLY_FALLTHROUGH [[clang::fallthrough]] +#elif FOLLY_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +#define FOLLY_FALLTHROUGH [[gnu::fallthrough]] +#else +#define FOLLY_FALLTHROUGH +#endif + +/** + * Maybe_unused indicates that a function, variable or parameter might or + * might not be used, e.g. + * + * int foo(FOLLY_MAYBE_UNUSED int x) { + * #ifdef USE_X + * return x; + * #else + * return 0; + * #endif + * } + */ +#if FOLLY_HAS_CPP_ATTRIBUTE(maybe_unused) +#define FOLLY_MAYBE_UNUSED [[maybe_unused]] +#elif FOLLY_HAS_ATTRIBUTE(__unused__) || __GNUC__ +#define FOLLY_MAYBE_UNUSED __attribute__((__unused__)) +#else +#define FOLLY_MAYBE_UNUSED +#endif + +/** + * Nullable indicates that a return value or a parameter may be a `nullptr`, + * e.g. + * + * int* FOLLY_NULLABLE foo(int* a, int* FOLLY_NULLABLE b) { + * if (*a > 0) { // safe dereference + * return nullptr; + * } + * if (*b < 0) { // unsafe dereference + * return *a; + * } + * if (b != nullptr && *b == 1) { // safe checked dereference + * return new int(1); + * } + * return nullptr; + * } + * + * Ignores Clang's -Wnullability-extension since it correctly handles the case + * where the extension is not present. + */ +#if FOLLY_HAS_EXTENSION(nullability) +#define FOLLY_NULLABLE \ + FOLLY_PUSH_WARNING \ + FOLLY_CLANG_DISABLE_WARNING("-Wnullability-extension") \ + _Nullable FOLLY_POP_WARNING +#define FOLLY_NONNULL \ + FOLLY_PUSH_WARNING \ + FOLLY_CLANG_DISABLE_WARNING("-Wnullability-extension") \ + _Nonnull FOLLY_POP_WARNING +#else +#define FOLLY_NULLABLE +#define FOLLY_NONNULL +#endif + +/** + * "Cold" indicates to the compiler that a function is only expected to be + * called from unlikely code paths. It can affect decisions made by the + * optimizer both when processing the function body and when analyzing + * call-sites. + */ +#if __GNUC__ +#define FOLLY_COLD __attribute__((__cold__)) +#else +#define FOLLY_COLD +#endif + +/** + * no_unique_address indicates that a member variable can be optimized to + * occupy no space, rather than the minimum 1-byte used by default. + * + * class Empty {}; + * + * class NonEmpty1 { + * FOLLY_NO_UNIQUE_ADDRESS Empty e; + * int f; + * }; + * + * class NonEmpty2 { + * Empty e; + * int f; + * }; + * + * sizeof(NonEmpty1); // may be == sizeof(int) + * sizeof(NonEmpty2); // must be > sizeof(int) + */ +#if FOLLY_HAS_CPP_ATTRIBUTE(no_unique_address) +#define FOLLY_ATTR_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define FOLLY_ATTR_NO_UNIQUE_ADDRESS +#endif diff --git a/ios/Pods/Flipper-Folly/folly/CpuId.h b/ios/Pods/Flipper-Folly/folly/CpuId.h new file mode 100644 index 000000000..517ccb571 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/CpuId.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +#include <folly/Portability.h> + +#ifdef _MSC_VER +#include <intrin.h> +#endif + +namespace folly { + +/** + * Identification of an Intel CPU. + * Supports CPUID feature flags (EAX=1) and extended features (EAX=7, ECX=0). + * Values from + * http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html + */ +class CpuId { + public: + // Always inline in order for this to be usable from a __ifunc__. + // In shared library mode, a __ifunc__ runs at relocation time, while the + // PLT hasn't been fully populated yet; thus, ifuncs cannot use symbols + // with potentially external linkage. (This issue is less likely in opt + // mode since inlining happens more likely, and it doesn't happen for + // statically linked binaries which don't depend on the PLT) + FOLLY_ALWAYS_INLINE CpuId() { +#if defined(_MSC_VER) && (FOLLY_X64 || defined(_M_IX86)) + int reg[4]; + __cpuid(static_cast<int*>(reg), 0); + const int n = reg[0]; + if (n >= 1) { + __cpuid(static_cast<int*>(reg), 1); + f1c_ = uint32_t(reg[2]); + f1d_ = uint32_t(reg[3]); + } + if (n >= 7) { + __cpuidex(static_cast<int*>(reg), 7, 0); + f7b_ = uint32_t(reg[1]); + f7c_ = uint32_t(reg[2]); + } +#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && \ + defined(__GNUC__) + // The following block like the normal cpuid branch below, but gcc + // reserves ebx for use of its pic register so we must specially + // handle the save and restore to avoid clobbering the register + uint32_t n; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=a"(n) + : "a"(0) + : "ecx", "edx"); + if (n >= 1) { + uint32_t f1a; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=a"(f1a), "=c"(f1c_), "=d"(f1d_) + : "a"(1) + :); + } + if (n >= 7) { + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "movl %%ebx, %%eax\n\r" + "popl %%ebx" + : "=a"(f7b_), "=c"(f7c_) + : "a"(7), "c"(0) + : "edx"); + } +#elif FOLLY_X64 || defined(__i386__) + uint32_t n; + __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx"); + if (n >= 1) { + uint32_t f1a; + __asm__("cpuid" : "=a"(f1a), "=c"(f1c_), "=d"(f1d_) : "a"(1) : "ebx"); + } + if (n >= 7) { + uint32_t f7a; + __asm__("cpuid" + : "=a"(f7a), "=b"(f7b_), "=c"(f7c_) + : "a"(7), "c"(0) + : "edx"); + } +#endif + } + +#define FOLLY_DETAIL_CPUID_X(name, r, bit) \ + FOLLY_ALWAYS_INLINE bool name() const { \ + return ((r) & (1U << bit)) != 0; \ + } + +// cpuid(1): Processor Info and Feature Bits. +#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f1c_, bit) + FOLLY_DETAIL_CPUID_C(sse3, 0) + FOLLY_DETAIL_CPUID_C(pclmuldq, 1) + FOLLY_DETAIL_CPUID_C(dtes64, 2) + FOLLY_DETAIL_CPUID_C(monitor, 3) + FOLLY_DETAIL_CPUID_C(dscpl, 4) + FOLLY_DETAIL_CPUID_C(vmx, 5) + FOLLY_DETAIL_CPUID_C(smx, 6) + FOLLY_DETAIL_CPUID_C(eist, 7) + FOLLY_DETAIL_CPUID_C(tm2, 8) + FOLLY_DETAIL_CPUID_C(ssse3, 9) + FOLLY_DETAIL_CPUID_C(cnxtid, 10) + FOLLY_DETAIL_CPUID_C(fma, 12) + FOLLY_DETAIL_CPUID_C(cx16, 13) + FOLLY_DETAIL_CPUID_C(xtpr, 14) + FOLLY_DETAIL_CPUID_C(pdcm, 15) + FOLLY_DETAIL_CPUID_C(pcid, 17) + FOLLY_DETAIL_CPUID_C(dca, 18) + FOLLY_DETAIL_CPUID_C(sse41, 19) + FOLLY_DETAIL_CPUID_C(sse42, 20) + FOLLY_DETAIL_CPUID_C(x2apic, 21) + FOLLY_DETAIL_CPUID_C(movbe, 22) + FOLLY_DETAIL_CPUID_C(popcnt, 23) + FOLLY_DETAIL_CPUID_C(tscdeadline, 24) + FOLLY_DETAIL_CPUID_C(aes, 25) + FOLLY_DETAIL_CPUID_C(xsave, 26) + FOLLY_DETAIL_CPUID_C(osxsave, 27) + FOLLY_DETAIL_CPUID_C(avx, 28) + FOLLY_DETAIL_CPUID_C(f16c, 29) + FOLLY_DETAIL_CPUID_C(rdrand, 30) +#undef FOLLY_DETAIL_CPUID_C +#define FOLLY_DETAIL_CPUID_D(name, bit) FOLLY_DETAIL_CPUID_X(name, f1d_, bit) + FOLLY_DETAIL_CPUID_D(fpu, 0) + FOLLY_DETAIL_CPUID_D(vme, 1) + FOLLY_DETAIL_CPUID_D(de, 2) + FOLLY_DETAIL_CPUID_D(pse, 3) + FOLLY_DETAIL_CPUID_D(tsc, 4) + FOLLY_DETAIL_CPUID_D(msr, 5) + FOLLY_DETAIL_CPUID_D(pae, 6) + FOLLY_DETAIL_CPUID_D(mce, 7) + FOLLY_DETAIL_CPUID_D(cx8, 8) + FOLLY_DETAIL_CPUID_D(apic, 9) + FOLLY_DETAIL_CPUID_D(sep, 11) + FOLLY_DETAIL_CPUID_D(mtrr, 12) + FOLLY_DETAIL_CPUID_D(pge, 13) + FOLLY_DETAIL_CPUID_D(mca, 14) + FOLLY_DETAIL_CPUID_D(cmov, 15) + FOLLY_DETAIL_CPUID_D(pat, 16) + FOLLY_DETAIL_CPUID_D(pse36, 17) + FOLLY_DETAIL_CPUID_D(psn, 18) + FOLLY_DETAIL_CPUID_D(clfsh, 19) + FOLLY_DETAIL_CPUID_D(ds, 21) + FOLLY_DETAIL_CPUID_D(acpi, 22) + FOLLY_DETAIL_CPUID_D(mmx, 23) + FOLLY_DETAIL_CPUID_D(fxsr, 24) + FOLLY_DETAIL_CPUID_D(sse, 25) + FOLLY_DETAIL_CPUID_D(sse2, 26) + FOLLY_DETAIL_CPUID_D(ss, 27) + FOLLY_DETAIL_CPUID_D(htt, 28) + FOLLY_DETAIL_CPUID_D(tm, 29) + FOLLY_DETAIL_CPUID_D(pbe, 31) +#undef FOLLY_DETAIL_CPUID_D + + // cpuid(7): Extended Features. +#define FOLLY_DETAIL_CPUID_B(name, bit) FOLLY_DETAIL_CPUID_X(name, f7b_, bit) + FOLLY_DETAIL_CPUID_B(bmi1, 3) + FOLLY_DETAIL_CPUID_B(hle, 4) + FOLLY_DETAIL_CPUID_B(avx2, 5) + FOLLY_DETAIL_CPUID_B(smep, 7) + FOLLY_DETAIL_CPUID_B(bmi2, 8) + FOLLY_DETAIL_CPUID_B(erms, 9) + FOLLY_DETAIL_CPUID_B(invpcid, 10) + FOLLY_DETAIL_CPUID_B(rtm, 11) + FOLLY_DETAIL_CPUID_B(mpx, 14) + FOLLY_DETAIL_CPUID_B(avx512f, 16) + FOLLY_DETAIL_CPUID_B(avx512dq, 17) + FOLLY_DETAIL_CPUID_B(rdseed, 18) + FOLLY_DETAIL_CPUID_B(adx, 19) + FOLLY_DETAIL_CPUID_B(smap, 20) + FOLLY_DETAIL_CPUID_B(avx512ifma, 21) + FOLLY_DETAIL_CPUID_B(pcommit, 22) + FOLLY_DETAIL_CPUID_B(clflushopt, 23) + FOLLY_DETAIL_CPUID_B(clwb, 24) + FOLLY_DETAIL_CPUID_B(avx512pf, 26) + FOLLY_DETAIL_CPUID_B(avx512er, 27) + FOLLY_DETAIL_CPUID_B(avx512cd, 28) + FOLLY_DETAIL_CPUID_B(sha, 29) + FOLLY_DETAIL_CPUID_B(avx512bw, 30) + FOLLY_DETAIL_CPUID_B(avx512vl, 31) +#undef FOLLY_DETAIL_CPUID_B +#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f7c_, bit) + FOLLY_DETAIL_CPUID_C(prefetchwt1, 0) + FOLLY_DETAIL_CPUID_C(avx512vbmi, 1) +#undef FOLLY_DETAIL_CPUID_C + +#undef FOLLY_DETAIL_CPUID_X + + private: + uint32_t f1c_ = 0; + uint32_t f1d_ = 0; + uint32_t f7b_ = 0; + uint32_t f7c_ = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/DefaultKeepAliveExecutor.h b/ios/Pods/Flipper-Folly/folly/DefaultKeepAliveExecutor.h new file mode 100644 index 000000000..f483ee20b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/DefaultKeepAliveExecutor.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <future> + +#include <glog/logging.h> + +#include <folly/Executor.h> +#include <folly/synchronization/Baton.h> + +namespace folly { + +/// An Executor accepts units of work with add(), which should be +/// threadsafe. +class DefaultKeepAliveExecutor : public virtual Executor { + public: + virtual ~DefaultKeepAliveExecutor() { + DCHECK(!keepAlive_); + } + + folly::Executor::KeepAlive<> weakRef() { + return WeakRef::create(controlBlock_, this); + } + + protected: + void joinKeepAlive() { + DCHECK(keepAlive_); + keepAlive_.reset(); + keepAliveReleaseBaton_.wait(); + } + + void joinAndResetKeepAlive() { + joinKeepAlive(); + auto keepAliveCount = + controlBlock_->keepAliveCount_.exchange(1, std::memory_order_relaxed); + DCHECK_EQ(keepAliveCount, 0); + keepAliveReleaseBaton_.reset(); + keepAlive_ = makeKeepAlive(this); + } + + private: + struct ControlBlock { + std::atomic<ssize_t> keepAliveCount_{1}; + }; + + class WeakRef : public Executor { + public: + static folly::Executor::KeepAlive<> create( + std::shared_ptr<ControlBlock> controlBlock, + Executor* executor) { + return makeKeepAlive(new WeakRef(std::move(controlBlock), executor)); + } + + void add(Func f) override { + if (auto executor = lock()) { + executor->add(std::move(f)); + } + } + + void addWithPriority(Func f, int8_t priority) override { + if (auto executor = lock()) { + executor->addWithPriority(std::move(f), priority); + } + } + + virtual uint8_t getNumPriorities() const override { + return numPriorities_; + } + + private: + WeakRef(std::shared_ptr<ControlBlock> controlBlock, Executor* executor) + : controlBlock_(std::move(controlBlock)), + executor_(executor), + numPriorities_(executor->getNumPriorities()) {} + + bool keepAliveAcquire() override { + auto keepAliveCount = + keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + // We should never increment from 0 + DCHECK(keepAliveCount > 0); + return true; + } + + void keepAliveRelease() override { + auto keepAliveCount = + keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCount >= 1); + + if (keepAliveCount == 1) { + delete this; + } + } + + folly::Executor::KeepAlive<> lock() { + auto controlBlock = + controlBlock_->keepAliveCount_.load(std::memory_order_relaxed); + do { + if (controlBlock == 0) { + return {}; + } + } while (!controlBlock_->keepAliveCount_.compare_exchange_weak( + controlBlock, + controlBlock + 1, + std::memory_order_release, + std::memory_order_relaxed)); + + return makeKeepAlive(executor_); + } + + std::atomic<size_t> keepAliveCount_{1}; + + std::shared_ptr<ControlBlock> controlBlock_; + Executor* executor_; + + uint8_t numPriorities_; + }; + + bool keepAliveAcquire() override { + auto keepAliveCount = + controlBlock_->keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + // We should never increment from 0 + DCHECK(keepAliveCount > 0); + return true; + } + + void keepAliveRelease() override { + auto keepAliveCount = + controlBlock_->keepAliveCount_.fetch_sub(1, std::memory_order_acquire); + DCHECK(keepAliveCount >= 1); + + if (keepAliveCount == 1) { + keepAliveReleaseBaton_.post(); // std::memory_order_release + } + } + + std::shared_ptr<ControlBlock> controlBlock_{std::make_shared<ControlBlock>()}; + Baton<> keepAliveReleaseBaton_; + KeepAlive<DefaultKeepAliveExecutor> keepAlive_{makeKeepAlive(this)}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Demangle.cpp b/ios/Pods/Flipper-Folly/folly/Demangle.cpp new file mode 100644 index 000000000..0df902c00 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Demangle.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Demangle.h> + +#include <algorithm> +#include <cstring> + +#include <folly/detail/Demangle.h> +#include <folly/lang/CString.h> +#include <folly/portability/Config.h> + +#if FOLLY_DETAIL_HAVE_DEMANGLE_H + +#include <cxxabi.h> + +#endif + +namespace folly { + +#if FOLLY_DETAIL_HAVE_DEMANGLE_H + +fbstring demangle(const char* name) { + if (!name) { + return fbstring(); + } +#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE + // GCC's __cxa_demangle() uses on-stack data structures for the + // parser state which are linear in the number of components of the + // symbol. For extremely long symbols, this can cause a stack + // overflow. We set an arbitrary symbol length limit above which we + // just return the mangled name. + size_t mangledLen = strlen(name); + if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) { + return fbstring(name, mangledLen); + } +#endif + + int status; + size_t len = 0; + // malloc() memory for the demangled type name + char* demangled = abi::__cxa_demangle(name, nullptr, &len, &status); + if (status != 0) { + return name; + } + // len is the length of the buffer (including NUL terminator and maybe + // other junk) + return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString()); +} + +namespace { + +struct DemangleBuf { + char* dest; + size_t remaining; + size_t total; +}; + +void demangleCallback(const char* str, size_t size, void* p) { + DemangleBuf* buf = static_cast<DemangleBuf*>(p); + size_t n = std::min(buf->remaining, size); + memcpy(buf->dest, str, n); + buf->dest += n; + buf->remaining -= n; + buf->total += size; +} + +} // namespace + +size_t demangle(const char* name, char* out, size_t outSize) { +#ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE + size_t mangledLen = strlen(name); + if (mangledLen > FOLLY_DEMANGLE_MAX_SYMBOL_SIZE) { + if (outSize) { + size_t n = std::min(mangledLen, outSize - 1); + memcpy(out, name, n); + out[n] = '\0'; + } + return mangledLen; + } +#endif + + DemangleBuf dbuf; + dbuf.dest = out; + dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term + dbuf.total = 0; + + // Unlike most library functions, this returns 1 on success and 0 on failure + int status = + detail::cplus_demangle_v3_callback_wrapper(name, demangleCallback, &dbuf); + if (status == 0) { // failed, return original + return folly::strlcpy(out, name, outSize); + } + if (outSize != 0) { + *dbuf.dest = '\0'; + } + return dbuf.total; +} + +#else + +fbstring demangle(const char* name) { + return name; +} + +size_t demangle(const char* name, char* out, size_t outSize) { + return folly::strlcpy(out, name, outSize); +} + +#endif + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Demangle.h b/ios/Pods/Flipper-Folly/folly/Demangle.h new file mode 100644 index 000000000..da13fb178 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Demangle.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/FBString.h> + +namespace folly { + +/** + * Return the demangled (prettyfied) version of a C++ type. + * + * This function tries to produce a human-readable type, but the type name will + * be returned unchanged in case of error or if demangling isn't supported on + * your system. + * + * Use for debugging -- do not rely on demangle() returning anything useful. + * + * This function may allocate memory (and therefore throw std::bad_alloc). + */ +fbstring demangle(const char* name); +inline fbstring demangle(const std::type_info& type) { + return demangle(type.name()); +} + +/** + * Return the demangled (prettyfied) version of a C++ type in a user-provided + * buffer. + * + * The semantics are the same as for snprintf or strlcpy: bufSize is the size + * of the buffer, the string is always null-terminated, and the return value is + * the number of characters (not including the null terminator) that would have + * been written if the buffer was big enough. (So a return value >= bufSize + * indicates that the output was truncated) + * + * This function does not allocate memory and is async-signal-safe. + * + * Note that the underlying function for the fbstring-returning demangle is + * somewhat standard (abi::__cxa_demangle, which uses malloc), the underlying + * function for this version is less so (cplus_demangle_v3_callback from + * libiberty), so it is possible for the fbstring version to work, while this + * version returns the original, mangled name. + */ +size_t demangle(const char* name, char* out, size_t outSize); +inline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) { + return demangle(type.name(), buf, bufSize); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/DiscriminatedPtr.h b/ios/Pods/Flipper-Folly/folly/DiscriminatedPtr.h new file mode 100644 index 000000000..0f49bd688 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/DiscriminatedPtr.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Discriminated pointer: Type-safe pointer to one of several types. + * + * Similar to boost::variant, but has no space overhead over a raw pointer, as + * it relies on the fact that (on x86_64) there are 16 unused bits in a + * pointer. + * + * @author Tudor Bosman (tudorb@fb.com) + */ + +#pragma once + +#include <limits> +#include <stdexcept> + +#include <glog/logging.h> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/detail/DiscriminatedPtrDetail.h> + +#if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64 +#error "DiscriminatedPtr is x64, arm64 and ppc64 specific code." +#endif + +namespace folly { + +/** + * Discriminated pointer. + * + * Given a list of types, a DiscriminatedPtr<Types...> may point to an object + * of one of the given types, or may be empty. DiscriminatedPtr is type-safe: + * you may only get a pointer to the type that you put in, otherwise get + * throws an exception (and get_nothrow returns nullptr) + * + * This pointer does not do any kind of lifetime management -- it's not a + * "smart" pointer. You are responsible for deallocating any memory used + * to hold pointees, if necessary. + */ +template <typename... Types> +class DiscriminatedPtr { + // <, not <=, as our indexes are 1-based (0 means "empty") + static_assert( + sizeof...(Types) < std::numeric_limits<uint16_t>::max(), + "too many types"); + + public: + /** + * Create an empty DiscriminatedPtr. + */ + DiscriminatedPtr() : data_(0) {} + + /** + * Create a DiscriminatedPtr that points to an object of type T. + * Fails at compile time if T is not a valid type (listed in Types) + */ + template <typename T> + explicit DiscriminatedPtr(T* ptr) { + set(ptr, typeIndex<T>()); + } + + /** + * Set this DiscriminatedPtr to point to an object of type T. + * Fails at compile time if T is not a valid type (listed in Types) + */ + template <typename T> + void set(T* ptr) { + set(ptr, typeIndex<T>()); + } + + /** + * Get a pointer to the object that this DiscriminatedPtr points to, if it is + * of type T. Fails at compile time if T is not a valid type (listed in + * Types), and returns nullptr if this DiscriminatedPtr is empty or points to + * an object of a different type. + */ + template <typename T> + T* get_nothrow() noexcept { + void* p = LIKELY(hasType<T>()) ? ptr() : nullptr; + return static_cast<T*>(p); + } + + template <typename T> + const T* get_nothrow() const noexcept { + const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr; + return static_cast<const T*>(p); + } + + /** + * Get a pointer to the object that this DiscriminatedPtr points to, if it is + * of type T. Fails at compile time if T is not a valid type (listed in + * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty + * or points to an object of a different type. + */ + template <typename T> + T* get() { + if (UNLIKELY(!hasType<T>())) { + throw std::invalid_argument("Invalid type"); + } + return static_cast<T*>(ptr()); + } + + template <typename T> + const T* get() const { + if (UNLIKELY(!hasType<T>())) { + throw std::invalid_argument("Invalid type"); + } + return static_cast<const T*>(ptr()); + } + + /** + * Return true iff this DiscriminatedPtr is empty. + */ + bool empty() const { + return index() == 0; + } + + /** + * Return true iff the object pointed by this DiscriminatedPtr has type T, + * false otherwise. Fails at compile time if T is not a valid type (listed + * in Types...) + */ + template <typename T> + bool hasType() const { + return index() == typeIndex<T>(); + } + + /** + * Clear this DiscriminatedPtr, making it empty. + */ + void clear() { + data_ = 0; + } + + /** + * Assignment operator from a pointer of type T. + */ + template <typename T> + DiscriminatedPtr& operator=(T* ptr) { + set(ptr); + return *this; + } + + /** + * Apply a visitor to this object, calling the appropriate overload for + * the type currently stored in DiscriminatedPtr. Throws invalid_argument + * if the DiscriminatedPtr is empty. + * + * The visitor must meet the following requirements: + * + * - The visitor must allow invocation as a function by overloading + * operator(), unambiguously accepting all values of type T* (or const T*) + * for all T in Types... + * - All operations of the function object on T* (or const T*) must + * return the same type (or a static_assert will fire). + */ + template <typename V> + typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) { + size_t n = index(); + if (n == 0) { + throw std::invalid_argument("Empty DiscriminatedPtr"); + } + return dptr_detail::ApplyVisitor<V, Types...>()( + n, std::forward<V>(visitor), ptr()); + } + + template <typename V> + typename dptr_detail::ConstVisitorResult<V, Types...>::type apply( + V&& visitor) const { + size_t n = index(); + if (n == 0) { + throw std::invalid_argument("Empty DiscriminatedPtr"); + } + return dptr_detail::ApplyConstVisitor<V, Types...>()( + n, std::forward<V>(visitor), ptr()); + } + + private: + /** + * Get the 1-based type index of T in Types. + */ + template <typename T> + uint16_t typeIndex() const { + return uint16_t(dptr_detail::GetTypeIndex<T, Types...>::value); + } + + uint16_t index() const { + return data_ >> 48; + } + void* ptr() const { + return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1)); + } + + void set(void* p, uint16_t v) { + uintptr_t ip = reinterpret_cast<uintptr_t>(p); + CHECK(!(ip >> 48)); + ip |= static_cast<uintptr_t>(v) << 48; + data_ = ip; + } + + /** + * We store a pointer in the least significant 48 bits of data_, and a type + * index (0 = empty, or 1-based index in Types) in the most significant 16 + * bits. We rely on the fact that pointers have their most significant 16 + * bits clear on x86_64. + */ + uintptr_t data_; +}; + +template <typename Visitor, typename... Args> +decltype(auto) apply_visitor( + Visitor&& visitor, + const DiscriminatedPtr<Args...>& variant) { + return variant.apply(std::forward<Visitor>(visitor)); +} + +template <typename Visitor, typename... Args> +decltype(auto) apply_visitor( + Visitor&& visitor, + DiscriminatedPtr<Args...>& variant) { + return variant.apply(std::forward<Visitor>(visitor)); +} + +template <typename Visitor, typename... Args> +decltype(auto) apply_visitor( + Visitor&& visitor, + DiscriminatedPtr<Args...>&& variant) { + return variant.apply(std::forward<Visitor>(visitor)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/DynamicConverter.h b/ios/Pods/Flipper-Folly/folly/DynamicConverter.h new file mode 100644 index 000000000..04aec594e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/DynamicConverter.h @@ -0,0 +1,415 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author Nicholas Ormrod <njormrod@fb.com> + +#pragma once + +#include <iterator> +#include <type_traits> + +#include <boost/iterator/iterator_adaptor.hpp> +#include <boost/mpl/has_xxx.hpp> + +#include <folly/Likely.h> +#include <folly/Optional.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/dynamic.h> +#include <folly/lang/Exception.h> + +namespace folly { +template <typename T> +T convertTo(const dynamic&); +template <typename T> +dynamic toDynamic(const T&); +} // namespace folly + +/** + * convertTo returns a well-typed representation of the input dynamic. + * + * Example: + * + * dynamic d = dynamic::array( + * dynamic::array(1, 2, 3), + * dynamic::array(4, 5)); // a vector of vector of int + * auto vvi = convertTo<fbvector<fbvector<int>>>(d); + * + * See docs/DynamicConverter.md for supported types and customization + */ + +namespace folly { + +/////////////////////////////////////////////////////////////////////////////// +// traits + +namespace dynamicconverter_detail { + +BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type) +BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator) +BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type) +BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type) + +template <typename T> +struct iterator_class_is_container { + typedef std::reverse_iterator<typename T::iterator> some_iterator; + enum { + value = has_value_type<T>::value && + std::is_constructible<T, some_iterator, some_iterator>::value + }; +}; + +template <typename T> +using class_is_container = + Conjunction<has_iterator<T>, iterator_class_is_container<T>>; + +template <typename T> +using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>; + +template <typename T> +using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>; + +template <typename T> +using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>; + +template <typename T> +using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>; + +} // namespace dynamicconverter_detail + +/////////////////////////////////////////////////////////////////////////////// +// custom iterators + +/** + * We have iterators that dereference to dynamics, but need iterators + * that dereference to typename T. + * + * Implementation details: + * 1. We cache the value of the dereference operator. This is necessary + * because boost::iterator_adaptor requires *it to return a + * reference. + * 2. For const reasons, we cannot call operator= to refresh the + * cache: we must call the destructor then placement new. + */ + +namespace dynamicconverter_detail { + +template <typename T> +struct Dereferencer { + static inline void derefToCache( + Optional<T>* /* mem */, + const dynamic::const_item_iterator& /* it */) { + throw_exception<TypeError>("array", dynamic::Type::OBJECT); + } + + static inline void derefToCache( + Optional<T>* mem, + const dynamic::const_iterator& it) { + mem->emplace(convertTo<T>(*it)); + } +}; + +template <typename F, typename S> +struct Dereferencer<std::pair<F, S>> { + static inline void derefToCache( + Optional<std::pair<F, S>>* mem, + const dynamic::const_item_iterator& it) { + mem->emplace(convertTo<F>(it->first), convertTo<S>(it->second)); + } + + // Intentional duplication of the code in Dereferencer + template <typename T> + static inline void derefToCache( + Optional<T>* mem, + const dynamic::const_iterator& it) { + mem->emplace(convertTo<T>(*it)); + } +}; + +template <typename T, typename It> +class Transformer + : public boost:: + iterator_adaptor<Transformer<T, It>, It, typename T::value_type> { + friend class boost::iterator_core_access; + + typedef typename T::value_type ttype; + + mutable Optional<ttype> cache_; + + void increment() { + ++this->base_reference(); + cache_ = none; + } + + ttype& dereference() const { + if (!cache_) { + Dereferencer<ttype>::derefToCache(&cache_, this->base_reference()); + } + return cache_.value(); + } + + public: + explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {} +}; + +// conversion factory +template <typename T, typename It> +inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) { + return std::make_move_iterator(Transformer<T, It>(it)); +} + +} // namespace dynamicconverter_detail + +/////////////////////////////////////////////////////////////////////////////// +// DynamicConverter specializations + +/** + * Each specialization of DynamicConverter has the function + * 'static T convert(const dynamic&);' + */ + +// default - intentionally unimplemented +template <typename T, typename Enable = void> +struct DynamicConverter; + +// boolean +template <> +struct DynamicConverter<bool> { + static bool convert(const dynamic& d) { + return d.asBool(); + } +}; + +// integrals +template <typename T> +struct DynamicConverter< + T, + typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> { + static T convert(const dynamic& d) { + return folly::to<T>(d.asInt()); + } +}; + +// enums +template <typename T> +struct DynamicConverter< + T, + typename std::enable_if<std::is_enum<T>::value>::type> { + static T convert(const dynamic& d) { + using type = typename std::underlying_type<T>::type; + return static_cast<T>(DynamicConverter<type>::convert(d)); + } +}; + +// floating point +template <typename T> +struct DynamicConverter< + T, + typename std::enable_if<std::is_floating_point<T>::value>::type> { + static T convert(const dynamic& d) { + return folly::to<T>(d.asDouble()); + } +}; + +// fbstring +template <> +struct DynamicConverter<folly::fbstring> { + static folly::fbstring convert(const dynamic& d) { + return d.asString(); + } +}; + +// std::string +template <> +struct DynamicConverter<std::string> { + static std::string convert(const dynamic& d) { + return d.asString(); + } +}; + +// std::pair +template <typename F, typename S> +struct DynamicConverter<std::pair<F, S>> { + static std::pair<F, S> convert(const dynamic& d) { + if (d.isArray() && d.size() == 2) { + return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1])); + } else if (d.isObject() && d.size() == 1) { + auto it = d.items().begin(); + return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second)); + } else { + throw_exception<TypeError>("array (size 2) or object (size 1)", d.type()); + } + } +}; + +// non-associative containers +template <typename C> +struct DynamicConverter< + C, + typename std::enable_if< + dynamicconverter_detail::is_container<C>::value && + !dynamicconverter_detail::is_associative<C>::value>::type> { + static C convert(const dynamic& d) { + if (d.isArray()) { + return C( + dynamicconverter_detail::conversionIterator<C>(d.begin()), + dynamicconverter_detail::conversionIterator<C>(d.end())); + } else if (d.isObject()) { + return C( + dynamicconverter_detail::conversionIterator<C>(d.items().begin()), + dynamicconverter_detail::conversionIterator<C>(d.items().end())); + } else { + throw_exception<TypeError>("object or array", d.type()); + } + } +}; + +// associative containers +template <typename C> +struct DynamicConverter< + C, + typename std::enable_if< + dynamicconverter_detail::is_container<C>::value && + dynamicconverter_detail::is_associative<C>::value>::type> { + static C convert(const dynamic& d) { + C ret; // avoid direct initialization due to unordered_map's constructor + // causing memory corruption if the iterator throws an exception + if (d.isArray()) { + ret.insert( + dynamicconverter_detail::conversionIterator<C>(d.begin()), + dynamicconverter_detail::conversionIterator<C>(d.end())); + } else if (d.isObject()) { + ret.insert( + dynamicconverter_detail::conversionIterator<C>(d.items().begin()), + dynamicconverter_detail::conversionIterator<C>(d.items().end())); + } else { + throw_exception<TypeError>("object or array", d.type()); + } + return ret; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// DynamicConstructor specializations + +/** + * Each specialization of DynamicConstructor has the function + * 'static dynamic construct(const C&);' + */ + +// default +template <typename C, typename Enable = void> +struct DynamicConstructor { + static dynamic construct(const C& x) { + return dynamic(x); + } +}; + +// identity +template <typename C> +struct DynamicConstructor< + C, + typename std::enable_if<std::is_same<C, dynamic>::value>::type> { + static dynamic construct(const C& x) { + return x; + } +}; + +// enums +template <typename C> +struct DynamicConstructor< + C, + typename std::enable_if<std::is_enum<C>::value>::type> { + static dynamic construct(const C& x) { + return dynamic(to_underlying(x)); + } +}; + +// maps +template <typename C> +struct DynamicConstructor< + C, + typename std::enable_if< + !std::is_same<C, dynamic>::value && + dynamicconverter_detail::is_map<C>::value>::type> { + static dynamic construct(const C& x) { + dynamic d = dynamic::object; + for (const auto& pair : x) { + d.insert(toDynamic(pair.first), toDynamic(pair.second)); + } + return d; + } +}; + +// other ranges +template <typename C> +struct DynamicConstructor< + C, + typename std::enable_if< + !std::is_same<C, dynamic>::value && + !dynamicconverter_detail::is_map<C>::value && + !std::is_constructible<StringPiece, const C&>::value && + dynamicconverter_detail::is_range<C>::value>::type> { + static dynamic construct(const C& x) { + dynamic d = dynamic::array; + for (const auto& item : x) { + d.push_back(toDynamic(item)); + } + return d; + } +}; + +// pair +template <typename A, typename B> +struct DynamicConstructor<std::pair<A, B>, void> { + static dynamic construct(const std::pair<A, B>& x) { + dynamic d = dynamic::array; + d.push_back(toDynamic(x.first)); + d.push_back(toDynamic(x.second)); + return d; + } +}; + +// vector<bool> +template <> +struct DynamicConstructor<std::vector<bool>, void> { + static dynamic construct(const std::vector<bool>& x) { + dynamic d = dynamic::array; + // Intentionally specifying the type as bool here. + // std::vector<bool>'s iterators return a proxy which is a prvalue + // and hence cannot bind to an lvalue reference such as auto& + for (bool item : x) { + d.push_back(toDynamic(item)); + } + return d; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// implementation + +template <typename T> +T convertTo(const dynamic& d) { + return DynamicConverter<typename std::remove_cv<T>::type>::convert(d); +} + +template <typename T> +dynamic toDynamic(const T& x) { + return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Exception.h b/ios/Pods/Flipper-Folly/folly/Exception.h new file mode 100644 index 000000000..b050d641d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Exception.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <errno.h> + +#include <cstdio> +#include <stdexcept> +#include <system_error> + +#include <folly/Conv.h> +#include <folly/FBString.h> +#include <folly/Likely.h> +#include <folly/Portability.h> + +namespace folly { + +// Various helpers to throw appropriate std::system_error exceptions from C +// library errors (returned in errno, as positive return values (many POSIX +// functions), or as negative return values (Linux syscalls)) +// +// The *Explicit functions take an explicit value for errno. + +inline std::system_error makeSystemErrorExplicit(int err, const char* msg) { + // TODO: The C++ standard indicates that std::generic_category() should be + // used for POSIX errno codes. + // + // We should ideally change this to use std::generic_category() instead of + // std::system_category(). However, undertaking this change will require + // updating existing call sites that currently catch exceptions thrown by + // this code and currently expect std::system_category. + return std::system_error(err, std::system_category(), msg); +} + +template <class... Args> +std::system_error makeSystemErrorExplicit(int err, Args&&... args) { + return makeSystemErrorExplicit( + err, to<fbstring>(std::forward<Args>(args)...).c_str()); +} + +inline std::system_error makeSystemError(const char* msg) { + return makeSystemErrorExplicit(errno, msg); +} + +template <class... Args> +std::system_error makeSystemError(Args&&... args) { + return makeSystemErrorExplicit(errno, std::forward<Args>(args)...); +} + +// Helper to throw std::system_error +[[noreturn]] inline void throwSystemErrorExplicit(int err, const char* msg) { + throw_exception(makeSystemErrorExplicit(err, msg)); +} + +template <class... Args> +[[noreturn]] void throwSystemErrorExplicit(int err, Args&&... args) { + throw_exception(makeSystemErrorExplicit(err, std::forward<Args>(args)...)); +} + +// Helper to throw std::system_error from errno and components of a string +template <class... Args> +[[noreturn]] void throwSystemError(Args&&... args) { + throwSystemErrorExplicit(errno, std::forward<Args>(args)...); +} + +// Check a Posix return code (0 on success, error number on error), throw +// on error. +template <class... Args> +void checkPosixError(int err, Args&&... args) { + if (UNLIKELY(err != 0)) { + throwSystemErrorExplicit(err, std::forward<Args>(args)...); + } +} + +// Check a Linux kernel-style return code (>= 0 on success, negative error +// number on error), throw on error. +template <class... Args> +void checkKernelError(ssize_t ret, Args&&... args) { + if (UNLIKELY(ret < 0)) { + throwSystemErrorExplicit(int(-ret), std::forward<Args>(args)...); + } +} + +// Check a traditional Unix return code (-1 and sets errno on error), throw +// on error. +template <class... Args> +void checkUnixError(ssize_t ret, Args&&... args) { + if (UNLIKELY(ret == -1)) { + throwSystemError(std::forward<Args>(args)...); + } +} + +template <class... Args> +void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) { + if (UNLIKELY(ret == -1)) { + throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...); + } +} + +// Check the return code from a fopen-style function (returns a non-nullptr +// FILE* on success, nullptr on error, sets errno). Works with fopen, fdopen, +// freopen, tmpfile, etc. +template <class... Args> +void checkFopenError(FILE* fp, Args&&... args) { + if (UNLIKELY(!fp)) { + throwSystemError(std::forward<Args>(args)...); + } +} + +template <class... Args> +void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) { + if (UNLIKELY(!fp)) { + throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...); + } +} + +/** + * If cond is not true, raise an exception of type E. E must have a ctor that + * works with const char* (a description of the failure). + */ +#define CHECK_THROW(cond, E) \ + do { \ + if (!(cond)) { \ + folly::throw_exception<E>("Check failed: " #cond); \ + } \ + } while (0) + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ExceptionString.h b/ios/Pods/Flipper-Folly/folly/ExceptionString.h new file mode 100644 index 000000000..13042ead4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ExceptionString.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <exception> +#include <string> +#include <type_traits> + +#include <folly/Demangle.h> +#include <folly/FBString.h> +#include <folly/Portability.h> + +namespace folly { + +/** + * Debug string for an exception: include type and what(), if + * defined. + */ +inline fbstring exceptionStr(const std::exception& e) { +#if FOLLY_HAS_RTTI + fbstring rv(demangle(typeid(e))); + rv += ": "; +#else + fbstring rv("Exception (no RTTI available): "); +#endif + rv += e.what(); + return rv; +} + +inline fbstring exceptionStr(std::exception_ptr ep) { + if (!kHasExceptions) { + return "Exception (catch unavailable)"; + } + return catch_exception( + [&]() -> fbstring { + return catch_exception<std::exception const&>( + [&]() -> fbstring { std::rethrow_exception(ep); }, + [](auto&& e) { return exceptionStr(e); }); + }, + []() -> fbstring { return "<unknown exception>"; }); +} + +template <typename E> +auto exceptionStr(const E& e) -> typename std:: + enable_if<!std::is_base_of<std::exception, E>::value, fbstring>::type { +#if FOLLY_HAS_RTTI + return demangle(typeid(e)); +#else + (void)e; + return "Exception (no RTTI available)"; +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ExceptionWrapper-inl.h b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper-inl.h new file mode 100644 index 000000000..0d26d3e17 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper-inl.h @@ -0,0 +1,680 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * Author: Eric Niebler <eniebler@fb.com> + */ + +#include <folly/Portability.h> + +namespace folly { + +template <class Fn> +struct exception_wrapper::arg_type_ + : public arg_type_<decltype(&Fn::operator())> {}; +template <class Ret, class Class, class Arg> +struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> { + using type = Arg; +}; +template <class Ret, class Class, class Arg> +struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> { + using type = Arg; +}; +template <class Ret, class Arg> +struct exception_wrapper::arg_type_<Ret(Arg)> { + using type = Arg; +}; +template <class Ret, class Arg> +struct exception_wrapper::arg_type_<Ret (*)(Arg)> { + using type = Arg; +}; +template <class Ret, class Class> +struct exception_wrapper::arg_type_<Ret (Class::*)(...)> { + using type = AnyException; +}; +template <class Ret, class Class> +struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> { + using type = AnyException; +}; +template <class Ret> +struct exception_wrapper::arg_type_<Ret(...)> { + using type = AnyException; +}; +template <class Ret> +struct exception_wrapper::arg_type_<Ret (*)(...)> { + using type = AnyException; +}; + +template <class Ret, class... Args> +inline Ret exception_wrapper::noop_(Args...) { + return Ret(); +} + +inline std::type_info const* exception_wrapper::uninit_type_( + exception_wrapper const*) { + return &typeid(void); +} + +template <class Ex, typename... As> +inline exception_wrapper::Buffer::Buffer(in_place_type_t<Ex>, As&&... as_) { + ::new (static_cast<void*>(&buff_)) Ex(std::forward<As>(as_)...); +} + +template <class Ex> +inline Ex& exception_wrapper::Buffer::as() noexcept { + return *static_cast<Ex*>(static_cast<void*>(&buff_)); +} +template <class Ex> +inline Ex const& exception_wrapper::Buffer::as() const noexcept { + return *static_cast<Ex const*>(static_cast<void const*>(&buff_)); +} + +inline std::exception const* exception_wrapper::as_exception_or_null_( + std::exception const& ex) { + return &ex; +} +inline std::exception const* exception_wrapper::as_exception_or_null_( + AnyException) { + return nullptr; +} + +static_assert( + !kMicrosoftAbiVer || (kMicrosoftAbiVer >= 1900 && kMicrosoftAbiVer <= 2000), + "exception_wrapper is untested and possibly broken on your version of " + "MSVC"); + +inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( + std::exception_ptr const& ptr, + std::exception const& e) noexcept { + if (!kMicrosoftAbiVer) { + return reinterpret_cast<std::uintptr_t>(&e); + } else { + // On Windows, as of MSVC2017, all thrown exceptions are copied to the stack + // first. Thus, we cannot depend on exception references associated with an + // exception_ptr to be live for the duration of the exception_ptr. We need + // to directly access the heap allocated memory inside the exception_ptr. + // + // std::exception_ptr is an opaque reinterpret_cast of + // std::shared_ptr<__ExceptionPtr> + // __ExceptionPtr is a non-virtual class with two members, a union and a + // bool. The union contains the now-undocumented EHExceptionRecord, which + // contains a struct which contains a void* which points to the heap + // allocated exception. + // We derive the offset to pExceptionObject via manual means. + FOLLY_PACK_PUSH + struct Win32ExceptionPtr { + char offset[8 + 4 * sizeof(void*)]; + void* exceptionObject; + } FOLLY_PACK_ATTR; + FOLLY_PACK_POP + + auto* win32ExceptionPtr = + reinterpret_cast<std::shared_ptr<Win32ExceptionPtr> const*>(&ptr) + ->get(); + return reinterpret_cast<std::uintptr_t>(win32ExceptionPtr->exceptionObject); + } +} +inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( + std::exception_ptr const&, + AnyException e) noexcept { + return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1; +} +inline bool exception_wrapper::ExceptionPtr::has_exception_() const { + return 0 == exception_or_type_ % 2; +} +inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_() + const { + return reinterpret_cast<std::exception const*>(exception_or_type_); +} +inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const { + return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1); +} + +inline void exception_wrapper::ExceptionPtr::copy_( + exception_wrapper const* from, + exception_wrapper* to) { + ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_); +} +inline void exception_wrapper::ExceptionPtr::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(std::move(from->eptr_)); + delete_(from); +} +inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) { + that->eptr_.~ExceptionPtr(); + that->vptr_ = &uninit_; +} +[[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_( + exception_wrapper const* that) { + std::rethrow_exception(that->eptr_.ptr_); +} +inline std::type_info const* exception_wrapper::ExceptionPtr::type_( + exception_wrapper const* that) { + if (auto e = get_exception_(that)) { + return &typeid(*e); + } + return that->eptr_.as_type_(); +} +inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_( + exception_wrapper const* that) { + return that->eptr_.has_exception_() ? that->eptr_.as_exception_() : nullptr; +} +inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_( + exception_wrapper const* that) { + return *that; +} + +template <class Ex> +inline void exception_wrapper::InPlace<Ex>::copy_( + exception_wrapper const* from, + exception_wrapper* to) { + ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) + Ex(from->buff_.as<Ex>()); +} +template <class Ex> +inline void exception_wrapper::InPlace<Ex>::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) + Ex(std::move(from->buff_.as<Ex>())); + delete_(from); +} +template <class Ex> +inline void exception_wrapper::InPlace<Ex>::delete_(exception_wrapper* that) { + that->buff_.as<Ex>().~Ex(); + that->vptr_ = &uninit_; +} +template <class Ex> +[[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_( + exception_wrapper const* that) { + throw that->buff_.as<Ex>(); +} +template <class Ex> +inline std::type_info const* exception_wrapper::InPlace<Ex>::type_( + exception_wrapper const*) { + return &typeid(Ex); +} +template <class Ex> +inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_( + exception_wrapper const* that) { + return as_exception_or_null_(that->buff_.as<Ex>()); +} +template <class Ex> +inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_( + exception_wrapper const* that) { + try { + throw_(that); + } catch (Ex const& ex) { + return exception_wrapper{std::current_exception(), ex}; + } +} + +template <class Ex> +[[noreturn]] inline void exception_wrapper::SharedPtr::Impl<Ex>::throw_() + const { + throw ex_; +} +template <class Ex> +inline std::exception const* +exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept { + return as_exception_or_null_(ex_); +} +template <class Ex> +inline exception_wrapper +exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept { + try { + throw_(); + } catch (Ex& ex) { + return exception_wrapper{std::current_exception(), ex}; + } +} +inline void exception_wrapper::SharedPtr::copy_( + exception_wrapper const* from, + exception_wrapper* to) { + ::new (static_cast<void*>(std::addressof(to->sptr_))) SharedPtr(from->sptr_); +} +inline void exception_wrapper::SharedPtr::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast<void*>(std::addressof(to->sptr_))) + SharedPtr(std::move(from->sptr_)); + delete_(from); +} +inline void exception_wrapper::SharedPtr::delete_(exception_wrapper* that) { + that->sptr_.~SharedPtr(); + that->vptr_ = &uninit_; +} +[[noreturn]] inline void exception_wrapper::SharedPtr::throw_( + exception_wrapper const* that) { + that->sptr_.ptr_->throw_(); + folly::assume_unreachable(); +} +inline std::type_info const* exception_wrapper::SharedPtr::type_( + exception_wrapper const* that) { + return that->sptr_.ptr_->info_; +} +inline std::exception const* exception_wrapper::SharedPtr::get_exception_( + exception_wrapper const* that) { + return that->sptr_.ptr_->get_exception_(); +} +inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_( + exception_wrapper const* that) { + return that->sptr_.ptr_->get_exception_ptr_(); +} + +template <class Ex, typename... As> +inline exception_wrapper::exception_wrapper( + ThrownTag, + in_place_type_t<Ex>, + As&&... as) + : eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...)), + reinterpret_cast<std::uintptr_t>(std::addressof(typeid(Ex))) + 1u}, + vptr_(&ExceptionPtr::ops_) {} + +template <class Ex, typename... As> +inline exception_wrapper::exception_wrapper( + OnHeapTag, + in_place_type_t<Ex>, + As&&... as) + : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)}, + vptr_(&SharedPtr::ops_) {} + +template <class Ex, typename... As> +inline exception_wrapper::exception_wrapper( + InSituTag, + in_place_type_t<Ex>, + As&&... as) + : buff_{in_place_type<Ex>, std::forward<As>(as)...}, + vptr_(&InPlace<Ex>::ops_) {} + +inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept + : exception_wrapper{} { + (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw +} + +inline exception_wrapper::exception_wrapper( + exception_wrapper const& that) noexcept + : exception_wrapper{} { + that.vptr_->copy_(&that, this); // Copy into *this, won't throw + vptr_ = that.vptr_; +} + +// If `this == &that`, this move assignment operator leaves the object in a +// valid but unspecified state. +inline exception_wrapper& exception_wrapper::operator=( + exception_wrapper&& that) noexcept { + vptr_->delete_(this); // Free the current exception + (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw + return *this; +} + +inline exception_wrapper& exception_wrapper::operator=( + exception_wrapper const& that) noexcept { + exception_wrapper(that).swap(*this); + return *this; +} + +inline exception_wrapper::~exception_wrapper() { + reset(); +} + +template <class Ex> +inline exception_wrapper::exception_wrapper( + std::exception_ptr ptr, + Ex& ex) noexcept + : eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)}, vptr_(&ExceptionPtr::ops_) { + assert(eptr_.ptr_); +} + +namespace exception_wrapper_detail { +template <class Ex> +Ex&& dont_slice(Ex&& ex) { + assert(typeid(ex) == typeid(std::decay_t<Ex>) || + !"Dynamic and static exception types don't match. Exception would " + "be sliced when storing in exception_wrapper."); + return std::forward<Ex>(ex); +} +} // namespace exception_wrapper_detail + +template < + class Ex, + class Ex_, + FOLLY_REQUIRES_DEF(Conjunction< + exception_wrapper::IsStdException<Ex_>, + exception_wrapper::IsRegularExceptionType<Ex_>>::value)> +inline exception_wrapper::exception_wrapper(Ex&& ex) + : exception_wrapper{ + PlacementOf<Ex_>{}, + in_place_type<Ex_>, + exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} + +template < + class Ex, + class Ex_, + FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex_>::value)> +inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex) + : exception_wrapper{ + PlacementOf<Ex_>{}, + in_place_type<Ex_>, + exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} + +template < + class Ex, + typename... As, + FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex>::value)> +inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as) + : exception_wrapper{PlacementOf<Ex>{}, + in_place_type<Ex>, + std::forward<As>(as)...} {} + +inline void exception_wrapper::swap(exception_wrapper& that) noexcept { + exception_wrapper tmp(std::move(that)); + that = std::move(*this); + *this = std::move(tmp); +} + +inline exception_wrapper::operator bool() const noexcept { + return vptr_ != &uninit_; +} + +inline bool exception_wrapper::operator!() const noexcept { + return !static_cast<bool>(*this); +} + +inline void exception_wrapper::reset() { + vptr_->delete_(this); +} + +inline bool exception_wrapper::has_exception_ptr() const noexcept { + return vptr_ == &ExceptionPtr::ops_; +} + +inline std::exception* exception_wrapper::get_exception() noexcept { + return const_cast<std::exception*>(vptr_->get_exception_(this)); +} +inline std::exception const* exception_wrapper::get_exception() const noexcept { + return vptr_->get_exception_(this); +} + +template <typename Ex> +inline Ex* exception_wrapper::get_exception() noexcept { + Ex* object{nullptr}; + with_exception([&](Ex& ex) { object = &ex; }); + return object; +} + +template <typename Ex> +inline Ex const* exception_wrapper::get_exception() const noexcept { + Ex const* object{nullptr}; + with_exception([&](Ex const& ex) { object = &ex; }); + return object; +} + +inline std::exception_ptr exception_wrapper::to_exception_ptr() noexcept { + if (*this) { + // Computing an exception_ptr is expensive so cache the result. + return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_; + } + return {}; +} +inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept { + return vptr_->get_exception_ptr_(this).eptr_.ptr_; +} + +inline std::type_info const& exception_wrapper::none() noexcept { + return typeid(void); +} +inline std::type_info const& exception_wrapper::unknown() noexcept { + return typeid(Unknown); +} + +inline std::type_info const& exception_wrapper::type() const noexcept { + return *vptr_->type_(this); +} + +inline folly::fbstring exception_wrapper::what() const { + if (auto e = get_exception()) { + return class_name() + ": " + e->what(); + } + return class_name(); +} + +inline folly::fbstring exception_wrapper::class_name() const { + auto& ti = type(); + return ti == none() + ? "" + : ti == unknown() ? "<unknown exception>" : folly::demangle(ti); +} + +template <class Ex> +inline bool exception_wrapper::is_compatible_with() const noexcept { + return with_exception([](Ex const&) {}); +} + +[[noreturn]] inline void exception_wrapper::throw_exception() const { + vptr_->throw_(this); + onNoExceptionError(__func__); +} + +template <class Ex> +[[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { + try { + throw_exception(); + } catch (...) { + std::throw_with_nested(std::forward<Ex>(ex)); + } +} + +template <class CatchFn, bool IsConst> +struct exception_wrapper::ExceptionTypeOf { + using type = arg_type<std::decay_t<CatchFn>>; + static_assert( + std::is_reference<type>::value, + "Always catch exceptions by reference."); + static_assert( + !IsConst || std::is_const<std::remove_reference_t<type>>::value, + "handle() or with_exception() called on a const exception_wrapper " + "and asked to catch a non-const exception. Handler will never fire. " + "Catch exception by const reference to fix this."); +}; + +// Nests a throw in the proper try/catch blocks +template <bool IsConst> +struct exception_wrapper::HandleReduce { + bool* handled_; + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>; + return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] { + try { + th(); + } catch (Ex& e) { + // If we got here because a catch function threw, rethrow. + if (*handled_) { + throw; + } + *handled_ = true; + ca(e); + } + }; + } + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] { + try { + th(); + } catch (...) { + // If we got here because a catch function threw, rethrow. + if (*handled_) { + throw; + } + *handled_ = true; + ca(); + } + }; + } +}; + +// When all the handlers expect types derived from std::exception, we can +// sometimes invoke the handlers without throwing any exceptions. +template <bool IsConst> +struct exception_wrapper::HandleStdExceptReduce { + using StdEx = AddConstIf<IsConst, std::exception>; + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>; + return + [th = std::forward<ThrowFn>(th), &ca](auto&& continuation) -> StdEx* { + if (auto e = const_cast<StdEx*>(th(continuation))) { + if (auto e2 = dynamic_cast<std::add_pointer_t<Ex>>(e)) { + ca(*e2); + } else { + return e; + } + } + return nullptr; + }; + } + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + return [th = std::forward<ThrowFn>(th), &ca](auto &&) -> StdEx* { + // The following continuation causes ca() to execute if *this contains + // an exception /not/ derived from std::exception. + auto continuation = [&ca](StdEx* e) { + return e != nullptr ? e : ((void)ca(), nullptr); + }; + if (th(continuation) != nullptr) { + ca(); + } + return nullptr; + }; + } +}; + +// Called when some types in the catch clauses are not derived from +// std::exception. +template <class This, class... CatchFns> +inline void +exception_wrapper::handle_(std::false_type, This& this_, CatchFns&... fns) { + bool handled = false; + auto impl = exception_wrapper_detail::fold( + HandleReduce<std::is_const<This>::value>{&handled}, + [&] { this_.throw_exception(); }, + fns...); + impl(); +} + +// Called when all types in the catch clauses are either derived from +// std::exception or a catch-all clause. +template <class This, class... CatchFns> +inline void +exception_wrapper::handle_(std::true_type, This& this_, CatchFns&... fns) { + using StdEx = exception_wrapper_detail:: + AddConstIf<std::is_const<This>::value, std::exception>; + auto impl = exception_wrapper_detail::fold( + HandleStdExceptReduce<std::is_const<This>::value>{}, + [&](auto&& continuation) { + return continuation( + const_cast<StdEx*>(this_.vptr_->get_exception_(&this_))); + }, + fns...); + // This continuation gets evaluated if CatchFns... does not include a + // catch-all handler. It is a no-op. + auto continuation = [](StdEx* ex) { return ex; }; + if (nullptr != impl(continuation)) { + this_.throw_exception(); + } +} + +namespace exception_wrapper_detail { +template <class Ex, class Fn> +struct catch_fn { + Fn fn_; + auto operator()(Ex& ex) { + return fn_(ex); + } +}; + +template <class Ex, class Fn> +inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) { + return {std::move(fn)}; +} +template <class Fn> +inline Fn catch_(void const*, Fn fn) { + return fn; +} +} // namespace exception_wrapper_detail + +template <class Ex, class This, class Fn> +inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { + if (!this_) { + return false; + } + bool handled = true; + auto fn = exception_wrapper_detail::catch_( + static_cast<Ex*>(nullptr), std::move(fn_)); + auto&& all = [&](...) { handled = false; }; + handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all); + return handled; +} + +template <class Ex, class Fn> +inline bool exception_wrapper::with_exception(Fn fn) { + return with_exception_<Ex>(*this, std::move(fn)); +} +template <class Ex, class Fn> +inline bool exception_wrapper::with_exception(Fn fn) const { + return with_exception_<Ex const>(*this, std::move(fn)); +} + +template <class... CatchFns> +inline void exception_wrapper::handle(CatchFns... fns) { + using AllStdEx = + exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>; + if (!*this) { + onNoExceptionError(__func__); + } + this->handle_(AllStdEx{}, *this, fns...); +} +template <class... CatchFns> +inline void exception_wrapper::handle(CatchFns... fns) const { + using AllStdEx = + exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>; + if (!*this) { + onNoExceptionError(__func__); + } + this->handle_(AllStdEx{}, *this, fns...); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.cpp b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.cpp new file mode 100644 index 000000000..1cca54bc0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ExceptionWrapper.h> + +#include <iostream> + +#include <folly/GLog.h> + +namespace folly { + +exception_wrapper::VTable const exception_wrapper::uninit_{ + &noop_<void, exception_wrapper const*, exception_wrapper*>, + &noop_<void, exception_wrapper*, exception_wrapper*>, + &noop_<void, exception_wrapper*>, + &noop_<void, exception_wrapper const*>, + &uninit_type_, + &noop_<std::exception const*, exception_wrapper const*>, + &noop_<exception_wrapper, exception_wrapper const*>}; + +exception_wrapper::VTable const exception_wrapper::ExceptionPtr::ops_{ + copy_, + move_, + delete_, + throw_, + type_, + get_exception_, + get_exception_ptr_}; + +exception_wrapper::VTable const exception_wrapper::SharedPtr::ops_{ + copy_, + move_, + delete_, + throw_, + type_, + get_exception_, + get_exception_ptr_}; + +namespace { +std::exception const* get_std_exception_(std::exception_ptr eptr) noexcept { + try { + std::rethrow_exception(eptr); + } catch (const std::exception& ex) { + return &ex; + } catch (...) { + return nullptr; + } +} +} // namespace + +exception_wrapper exception_wrapper::from_exception_ptr( + std::exception_ptr const& ptr) noexcept { + if (!ptr) { + return exception_wrapper(); + } + try { + std::rethrow_exception(ptr); + } catch (std::exception& e) { + return exception_wrapper(std::current_exception(), e); + } catch (...) { + return exception_wrapper(std::current_exception()); + } +} + +exception_wrapper::exception_wrapper(std::exception_ptr ptr) noexcept + : exception_wrapper{} { + if (ptr) { + if (auto e = get_std_exception_(ptr)) { + LOG(DFATAL) + << "Performance error: Please construct exception_wrapper with a " + "reference to the std::exception along with the " + "std::exception_ptr."; + *this = exception_wrapper{std::move(ptr), *e}; + } else { + Unknown uk; + *this = exception_wrapper{ptr, uk}; + } + } +} + +[[noreturn]] void exception_wrapper::onNoExceptionError( + char const* const name) { + std::ios_base::Init ioinit_; // ensure std::cerr is alive + std::cerr << "Cannot use `" << name + << "` with an empty folly::exception_wrapper" << std::endl; + std::terminate(); +} + +fbstring exceptionStr(exception_wrapper const& ew) { + return ew.what(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.h b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.h new file mode 100644 index 000000000..7e529a2ba --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ExceptionWrapper.h @@ -0,0 +1,714 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Author: Eric Niebler <eniebler@fb.com> + */ + +#pragma once + +#include <cassert> +#include <cstdint> +#include <exception> +#include <iosfwd> +#include <memory> +#include <new> +#include <type_traits> +#include <typeinfo> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/Demangle.h> +#include <folly/ExceptionString.h> +#include <folly/FBString.h> +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/lang/Assume.h> + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wpotentially-evaluated-expression" +// GCC gets confused about lambda scopes and issues shadow-local warnings for +// parameters in totally different functions. +FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS +#endif + +#define FOLLY_EXCEPTION_WRAPPER_H_INCLUDED + +namespace folly { + +#define FOLLY_REQUIRES_DEF(...) \ + std::enable_if_t<static_cast<bool>(__VA_ARGS__), long> + +#define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__ + +namespace exception_wrapper_detail { + +template <template <class> class T, class... As> +using AllOf = StrictConjunction<T<As>...>; + +template <bool If, class T> +using AddConstIf = std::conditional_t<If, const T, T>; + +template <class Fn, class A> +FOLLY_ERASE auto fold(Fn&&, A&& a) { + return static_cast<A&&>(a); +} + +template <class Fn, class A, class B, class... Bs> +FOLLY_ERASE auto fold(Fn&& fn, A&& a, B&& b, Bs&&... bs) { + return fold( + // This looks like a use of fn after a move of fn, but in reality, this is + // just a cast and not a move. That's because regardless of which fold + // overload is selected, fn gets bound to a &&. Had fold taken fn by value + // there would indeed be a problem here. + static_cast<Fn&&>(fn), + static_cast<Fn&&>(fn)(static_cast<A&&>(a), static_cast<B&&>(b)), + static_cast<Bs&&>(bs)...); +} + +} // namespace exception_wrapper_detail + +//! Throwing exceptions can be a convenient way to handle errors. Storing +//! exceptions in an `exception_ptr` makes it easy to handle exceptions in a +//! different thread or at a later time. `exception_ptr` can also be used in a +//! very generic result/exception wrapper. +//! +//! However, there are some issues with throwing exceptions and +//! `std::exception_ptr`. These issues revolve around `throw` being expensive, +//! particularly in a multithreaded environment (see +//! ExceptionWrapperBenchmark.cpp). +//! +//! Imagine we have a library that has an API which returns a result/exception +//! wrapper. Let's consider some approaches for implementing this wrapper. +//! First, we could store a `std::exception`. This approach loses the derived +//! exception type, which can make exception handling more difficult for users +//! that prefer rethrowing the exception. We could use a `folly::dynamic` for +//! every possible type of exception. This is not very flexible - adding new +//! types of exceptions requires a change to the result/exception wrapper. We +//! could use an `exception_ptr`. However, constructing an `exception_ptr` as +//! well as accessing the error requires a call to throw. That means that there +//! will be two calls to throw in order to process the exception. For +//! performance sensitive applications, this may be unacceptable. +//! +//! `exception_wrapper` is designed to handle exception management for both +//! convenience and high performance use cases. `make_exception_wrapper` is +//! templated on derived type, allowing us to rethrow the exception properly for +//! users that prefer convenience. These explicitly named exception types can +//! therefore be handled without any peformance penalty. `exception_wrapper` is +//! also flexible enough to accept any type. If a caught exception is not of an +//! explicitly named type, then `std::exception_ptr` is used to preserve the +//! exception state. For performance sensitive applications, the accessor +//! methods can test or extract a pointer to a specific exception type with very +//! little overhead. +//! +//! \par Example usage: +//! \par +//! \code +//! exception_wrapper globalExceptionWrapper; +//! +//! // Thread1 +//! void doSomethingCrazy() { +//! int rc = doSomethingCrazyWithLameReturnCodes(); +//! if (rc == NAILED_IT) { +//! globalExceptionWrapper = exception_wrapper(); +//! } else if (rc == FACE_PLANT) { +//! globalExceptionWrapper = make_exception_wrapper<FacePlantException>(); +//! } else if (rc == FAIL_WHALE) { +//! globalExceptionWrapper = make_exception_wrapper<FailWhaleException>(); +//! } +//! } +//! +//! // Thread2: Exceptions are ok! +//! void processResult() { +//! try { +//! globalExceptionWrapper.throw_exception(); +//! } catch (const FacePlantException& e) { +//! LOG(ERROR) << "FACEPLANT!"; +//! } catch (const FailWhaleException& e) { +//! LOG(ERROR) << "FAILWHALE!"; +//! } +//! } +//! +//! // Thread2: Exceptions are bad! +//! void processResult() { +//! globalExceptionWrapper.handle( +//! [&](FacePlantException& faceplant) { +//! LOG(ERROR) << "FACEPLANT"; +//! }, +//! [&](FailWhaleException& failwhale) { +//! LOG(ERROR) << "FAILWHALE!"; +//! }, +//! [](...) { +//! LOG(FATAL) << "Unrecognized exception"; +//! }); +//! } +//! \endcode +class exception_wrapper final { + private: + struct FOLLY_EXPORT AnyException : std::exception { + std::type_info const* typeinfo_; + template <class T> + /* implicit */ AnyException(T&& t) noexcept : typeinfo_(&typeid(t)) {} + }; + + template <class Fn> + struct arg_type_; + template <class Fn> + using arg_type = _t<arg_type_<Fn>>; + + // exception_wrapper is implemented as a simple variant over four + // different representations: + // 0. Empty, no exception. + // 1. An small object stored in-situ. + // 2. A larger object stored on the heap and referenced with a + // std::shared_ptr. + // 3. A std::exception_ptr, together with either: + // a. A pointer to the referenced std::exception object, or + // b. A pointer to a std::type_info object for the referenced exception, + // or for an unspecified type if the type is unknown. + // This is accomplished with the help of a union and a pointer to a hand- + // rolled virtual table. This virtual table contains pointers to functions + // that know which field of the union is active and do the proper action. + // The class invariant ensures that the vtable ptr and the union stay in sync. + struct VTable { + void (*copy_)(exception_wrapper const*, exception_wrapper*); + void (*move_)(exception_wrapper*, exception_wrapper*); + void (*delete_)(exception_wrapper*); + void (*throw_)(exception_wrapper const*); + std::type_info const* (*type_)(exception_wrapper const*); + std::exception const* (*get_exception_)(exception_wrapper const*); + exception_wrapper (*get_exception_ptr_)(exception_wrapper const*); + }; + + [[noreturn]] static void onNoExceptionError(char const* name); + + template <class Ret, class... Args> + static Ret noop_(Args...); + + static std::type_info const* uninit_type_(exception_wrapper const*); + + static VTable const uninit_; + + template <class Ex> + using IsStdException = std::is_base_of<std::exception, std::decay_t<Ex>>; + template <bool B, class T> + using AddConstIf = exception_wrapper_detail::AddConstIf<B, T>; + template <class CatchFn> + using IsCatchAll = + std::is_same<arg_type<std::decay_t<CatchFn>>, AnyException>; + + struct Unknown {}; + + // Sadly, with the gcc-4.9 platform, std::logic_error and std::runtime_error + // do not fit here. They also don't have noexcept copy-ctors, so the internal + // storage wouldn't be used anyway. For the gcc-5 platform, both logic_error + // and runtime_error can be safely stored internally. + struct Buffer { + using Storage = + std::aligned_storage_t<2 * sizeof(void*), alignof(std::exception)>; + Storage buff_; + + Buffer() : buff_{} {} + + template <class Ex, typename... As> + Buffer(in_place_type_t<Ex>, As&&... as_); + template <class Ex> + Ex& as() noexcept; + template <class Ex> + Ex const& as() const noexcept; + }; + + struct ThrownTag {}; + struct InSituTag {}; + struct OnHeapTag {}; + + template <class T> + using PlacementOf = std::conditional_t< + !IsStdException<T>::value, + ThrownTag, + std::conditional_t< + sizeof(T) <= sizeof(Buffer::Storage) && + alignof(T) <= alignof(Buffer::Storage) && + noexcept(T(std::declval<T&&>())) && + noexcept(T(std::declval<T const&>())), + InSituTag, + OnHeapTag>>; + + static std::exception const* as_exception_or_null_(std::exception const& ex); + static std::exception const* as_exception_or_null_(AnyException); + + struct ExceptionPtr { + std::exception_ptr ptr_; + std::uintptr_t exception_or_type_; // odd for type_info + static_assert( + 1 < alignof(std::exception) && 1 < alignof(std::type_info), + "Surprise! std::exception and std::type_info don't have alignment " + "greater than one. as_int_ below will not work!"); + + static std::uintptr_t as_int_( + std::exception_ptr const& ptr, + std::exception const& e) noexcept; + static std::uintptr_t as_int_( + std::exception_ptr const& ptr, + AnyException e) noexcept; + bool has_exception_() const; + std::exception const* as_exception_() const; + std::type_info const* as_type_() const; + static void copy_(exception_wrapper const* from, exception_wrapper* to); + static void move_(exception_wrapper* from, exception_wrapper* to); + static void delete_(exception_wrapper* that); + [[noreturn]] static void throw_(exception_wrapper const* that); + static std::type_info const* type_(exception_wrapper const* that); + static std::exception const* get_exception_(exception_wrapper const* that); + static exception_wrapper get_exception_ptr_(exception_wrapper const* that); + static VTable const ops_; + }; + + template <class Ex> + struct InPlace { + static_assert(IsStdException<Ex>::value, "only deriving std::exception"); + static void copy_(exception_wrapper const* from, exception_wrapper* to); + static void move_(exception_wrapper* from, exception_wrapper* to); + static void delete_(exception_wrapper* that); + [[noreturn]] static void throw_(exception_wrapper const* that); + static std::type_info const* type_(exception_wrapper const*); + static std::exception const* get_exception_(exception_wrapper const* that); + static exception_wrapper get_exception_ptr_(exception_wrapper const* that); + static constexpr VTable const ops_{copy_, + move_, + delete_, + throw_, + type_, + get_exception_, + get_exception_ptr_}; + }; + + struct SharedPtr { + struct Base { + std::type_info const* info_; + Base() = default; + explicit Base(std::type_info const& info) : info_(&info) {} + virtual ~Base() {} + virtual void throw_() const = 0; + virtual std::exception const* get_exception_() const noexcept = 0; + virtual exception_wrapper get_exception_ptr_() const noexcept = 0; + }; + template <class Ex> + struct Impl final : public Base { + static_assert(IsStdException<Ex>::value, "only deriving std::exception"); + Ex ex_; + Impl() = default; + // clang-format off + template <typename... As> + explicit Impl(As&&... as) + : Base{typeid(Ex)}, ex_(std::forward<As>(as)...) {} + [[noreturn]] void throw_() const override; + // clang-format on + std::exception const* get_exception_() const noexcept override; + exception_wrapper get_exception_ptr_() const noexcept override; + }; + std::shared_ptr<Base> ptr_; + + static void copy_(exception_wrapper const* from, exception_wrapper* to); + static void move_(exception_wrapper* from, exception_wrapper* to); + static void delete_(exception_wrapper* that); + [[noreturn]] static void throw_(exception_wrapper const* that); + static std::type_info const* type_(exception_wrapper const* that); + static std::exception const* get_exception_(exception_wrapper const* that); + static exception_wrapper get_exception_ptr_(exception_wrapper const* that); + static VTable const ops_; + }; + + union { + Buffer buff_{}; + ExceptionPtr eptr_; + SharedPtr sptr_; + }; + VTable const* vptr_{&uninit_}; + + template <class Ex, typename... As> + exception_wrapper(ThrownTag, in_place_type_t<Ex>, As&&... as); + + template <class Ex, typename... As> + exception_wrapper(OnHeapTag, in_place_type_t<Ex>, As&&... as); + + template <class Ex, typename... As> + exception_wrapper(InSituTag, in_place_type_t<Ex>, As&&... as); + + template <class T> + struct IsRegularExceptionType + : StrictConjunction< + std::is_copy_constructible<T>, + Negation<std::is_base_of<exception_wrapper, T>>, + Negation<std::is_abstract<T>>> {}; + + template <class CatchFn, bool IsConst = false> + struct ExceptionTypeOf; + + template <bool IsConst> + struct HandleReduce; + + template <bool IsConst> + struct HandleStdExceptReduce; + + template <class This, class... CatchFns> + static void handle_(std::false_type, This& this_, CatchFns&... fns); + + template <class This, class... CatchFns> + static void handle_(std::true_type, This& this_, CatchFns&... fns); + + template <class Ex, class This, class Fn> + static bool with_exception_(This& this_, Fn fn_); + + public: + static exception_wrapper from_exception_ptr( + std::exception_ptr const& eptr) noexcept; + + //! Default-constructs an empty `exception_wrapper` + //! \post `type() == none()` + exception_wrapper() noexcept {} + + //! Move-constructs an `exception_wrapper` + //! \post `*this` contains the value of `that` prior to the move + //! \post `that.type() == none()` + exception_wrapper(exception_wrapper&& that) noexcept; + + //! Copy-constructs an `exception_wrapper` + //! \post `*this` contains a copy of `that`, and `that` is unmodified + //! \post `type() == that.type()` + exception_wrapper(exception_wrapper const& that) noexcept; + + //! Move-assigns an `exception_wrapper` + //! \pre `this != &that` + //! \post `*this` contains the value of `that` prior to the move + //! \post `that.type() == none()` + exception_wrapper& operator=(exception_wrapper&& that) noexcept; + + //! Copy-assigns an `exception_wrapper` + //! \post `*this` contains a copy of `that`, and `that` is unmodified + //! \post `type() == that.type()` + exception_wrapper& operator=(exception_wrapper const& that) noexcept; + + ~exception_wrapper(); + + //! \pre `ptr` is empty, or it holds a reference to an exception that is not + //! derived from `std::exception`. + //! \post `!ptr || bool(*this)` + //! \post `hasThrownException() == true` + //! \post `type() == unknown()` + explicit exception_wrapper(std::exception_ptr ptr) noexcept; + + //! \pre `ptr` holds a reference to `ex`. + //! \post `hasThrownException() == true` + //! \post `bool(*this)` + //! \post `type() == typeid(ex)` + template <class Ex> + exception_wrapper(std::exception_ptr ptr, Ex& ex) noexcept; + + //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)` + //! \post `bool(*this)` + //! \post `hasThrownException() == false` + //! \post `type() == typeid(ex)` + //! \note Exceptions of types derived from `std::exception` can be implicitly + //! converted to an `exception_wrapper`. + template < + class Ex, + class Ex_ = std::decay_t<Ex>, + FOLLY_REQUIRES( + Conjunction<IsStdException<Ex_>, IsRegularExceptionType<Ex_>>::value)> + /* implicit */ exception_wrapper(Ex&& ex); + + //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)` + //! \post `bool(*this)` + //! \post `hasThrownException() == false` + //! \post `type() == typeid(ex)` + //! \note Exceptions of types not derived from `std::exception` can still be + //! used to construct an `exception_wrapper`, but you must specify + //! `folly::in_place` as the first parameter. + template < + class Ex, + class Ex_ = std::decay_t<Ex>, + FOLLY_REQUIRES(IsRegularExceptionType<Ex_>::value)> + exception_wrapper(in_place_t, Ex&& ex); + + template < + class Ex, + typename... As, + FOLLY_REQUIRES(IsRegularExceptionType<Ex>::value)> + exception_wrapper(in_place_type_t<Ex>, As&&... as); + + //! Swaps the value of `*this` with the value of `that` + void swap(exception_wrapper& that) noexcept; + + //! \return `true` if `*this` is holding an exception. + explicit operator bool() const noexcept; + + //! \return `!bool(*this)` + bool operator!() const noexcept; + + //! Make this `exception_wrapper` empty + //! \post `!*this` + void reset(); + + //! \return `true` if this `exception_wrapper` holds a reference to an + //! exception that was thrown (i.e., if it was constructed with + //! a `std::exception_ptr`, or if `to_exception_ptr()` was called on a + //! (non-const) reference to `*this`). + bool has_exception_ptr() const noexcept; + + //! \return a pointer to the `std::exception` held by `*this`, if it holds + //! one; otherwise, returns `nullptr`. + //! \note This function does not mutate the `exception_wrapper` object. + //! \note This function never causes an exception to be thrown. + std::exception* get_exception() noexcept; + //! \overload + std::exception const* get_exception() const noexcept; + + //! \returns a pointer to the `Ex` held by `*this`, if it holds an object + //! whose type `From` permits `std::is_convertible<From*, Ex*>`; + //! otherwise, returns `nullptr`. + //! \note This function does not mutate the `exception_wrapper` object. + //! \note This function may cause an exception to be thrown and immediately + //! caught internally, affecting runtime performance. + template <typename Ex> + Ex* get_exception() noexcept; + //! \overload + template <typename Ex> + Ex const* get_exception() const noexcept; + + //! \return A `std::exception_ptr` that references either the exception held + //! by `*this`, or a copy of same. + //! \note This function may need to throw an exception to complete the action. + //! \note The non-const overload of this function mutates `*this` to cache the + //! computed `std::exception_ptr`; that is, this function may cause + //! `has_exception_ptr()` to change from `false` to `true`. + std::exception_ptr to_exception_ptr() noexcept; + //! \overload + std::exception_ptr to_exception_ptr() const noexcept; + + //! \return the `typeid` of an unspecified type used by + //! `exception_wrapper::type()` to denote an empty `exception_wrapper`. + static std::type_info const& none() noexcept; + //! \return the `typeid` of an unspecified type used by + //! `exception_wrapper::type()` to denote an `exception_wrapper` that + //! holds an exception of unknown type. + static std::type_info const& unknown() noexcept; + + //! Returns the `typeid` of the wrapped exception object. If there is no + //! wrapped exception object, returns `exception_wrapper::none()`. If + //! this instance wraps an exception of unknown type not derived from + //! `std::exception`, returns `exception_wrapper::unknown()`. + std::type_info const& type() const noexcept; + + //! \return If `get_exception() != nullptr`, `class_name() + ": " + + //! get_exception()->what()`; otherwise, `class_name()`. + folly::fbstring what() const; + + //! \return If `!*this`, the empty string; otherwise, if + //! `type() == unknown()`, the string `"<unknown exception>"`; otherwise, + //! the result of `type().name()` after demangling. + folly::fbstring class_name() const; + + //! \tparam Ex The expression type to check for compatibility with. + //! \return `true` if and only if `*this` wraps an exception that would be + //! caught with a `catch(Ex const&)` clause. + //! \note If `*this` is empty, this function returns `false`. + template <class Ex> + bool is_compatible_with() const noexcept; + + //! Throws the wrapped expression. + //! \pre `bool(*this)` + [[noreturn]] void throw_exception() const; + + //! Throws the wrapped expression nested into another exception. + //! \pre `bool(*this)` + //! \tparam ex Exception in *this will be thrown nested into ex; + // see std::throw_with_nested() for details on this semantic. + template <class Ex> + [[noreturn]] void throw_with_nested(Ex&& ex) const; + + //! Call `fn` with the wrapped exception (if any), if `fn` can accept it. + //! \par Example + //! \code + //! exception_wrapper ew{std::runtime_error("goodbye cruel world")}; + //! + //! assert( ew.with_exception([](std::runtime_error& e){/*...*/}) ); + //! + //! assert( !ew.with_exception([](int& e){/*...*/}) ); + //! + //! assert( !exception_wrapper{}.with_exception([](int& e){/*...*/}) ); + //! \endcode + //! \tparam Ex Optionally, the type of the exception that `fn` accepts. + //! \tparam Fn The type of a monomophic function object. + //! \param fn A function object to call with the wrapped exception + //! \return `true` if and only if `fn` was called. + //! \note Optionally, you may explicitly specify the type of the exception + //! that `fn` expects, as in + //! \code + //! ew.with_exception<std::runtime_error>([](auto&& e) { /*...*/; }); + //! \endcode + //! \note The handler may or may not be invoked with an active exception. + //! **Do not try to rethrow the exception with `throw;` from within your + //! handler -- that is, a throw expression with no operand.** This may + //! cause your process to terminate. (It is perfectly ok to throw from + //! a handler so long as you specify the exception to throw, as in + //! `throw e;`.) + template <class Ex = void const, class Fn> + bool with_exception(Fn fn); + //! \overload + template <class Ex = void const, class Fn> + bool with_exception(Fn fn) const; + + //! Handle the wrapped expression as if with a series of `catch` clauses, + //! propagating the exception if no handler matches. + //! \par Example + //! \code + //! exception_wrapper ew{std::runtime_error("goodbye cruel world")}; + //! + //! ew.handle( + //! [&](std::logic_error const& e) { + //! LOG(DFATAL) << "ruh roh"; + //! ew.throw_exception(); // rethrow the active exception without + //! // slicing it. Will not be caught by other + //! // handlers in this call. + //! }, + //! [&](std::exception const& e) { + //! LOG(ERROR) << ew.what(); + //! }); + //! \endcode + //! In the above example, any exception _not_ derived from `std::exception` + //! will be propagated. To specify a catch-all clause, pass a lambda that + //! takes a C-style elipses, as in: + //! \code + //! ew.handle(/*...* /, [](...) { /* handle unknown exception */ } ) + //! \endcode + //! \pre `!*this` + //! \tparam CatchFns... A pack of unary monomorphic function object types. + //! \param fns A pack of unary monomorphic function objects to be treated as + //! an ordered list of potential exception handlers. + //! \note The handlers may or may not be invoked with an active exception. + //! **Do not try to rethrow the exception with `throw;` from within your + //! handler -- that is, a throw expression with no operand.** This may + //! cause your process to terminate. (It is perfectly ok to throw from + //! a handler so long as you specify the exception to throw, as in + //! `throw e;`.) + template <class... CatchFns> + void handle(CatchFns... fns); + //! \overload + template <class... CatchFns> + void handle(CatchFns... fns) const; +}; + +template <class Ex> +constexpr exception_wrapper::VTable exception_wrapper::InPlace<Ex>::ops_; + +/** + * \return An `exception_wrapper` that wraps an instance of type `Ex` + * that has been constructed with arguments `std::forward<As>(as)...`. + */ +template <class Ex, typename... As> +exception_wrapper make_exception_wrapper(As&&... as) { + return exception_wrapper{in_place_type<Ex>, std::forward<As>(as)...}; +} + +/** + * Inserts `ew.what()` into the ostream `sout`. + * \return `sout` + */ +template <class Ch> +std::basic_ostream<Ch>& operator<<( + std::basic_ostream<Ch>& sout, + exception_wrapper const& ew) { + return sout << ew.what(); +} + +/** + * Swaps the value of `a` with the value of `b`. + */ +inline void swap(exception_wrapper& a, exception_wrapper& b) noexcept { + a.swap(b); +} + +// For consistency with exceptionStr() functions in ExceptionString.h +fbstring exceptionStr(exception_wrapper const& ew); + +namespace detail { +template <typename F> +inline exception_wrapper try_and_catch_(F&& f) { + return (f(), exception_wrapper()); +} + +template <typename F, typename Ex, typename... Exs> +inline exception_wrapper try_and_catch_(F&& f) { + try { + return try_and_catch_<F, Exs...>(std::forward<F>(f)); + } catch (Ex& ex) { + return exception_wrapper(std::current_exception(), ex); + } +} +} // namespace detail + +//! `try_and_catch` is a simple replacement for `try {} catch(){}`` that allows +//! you to specify which derived exceptions you would like to catch and store in +//! an `exception_wrapper`. +//! +//! Because we cannot build an equivalent of `std::current_exception()`, we need +//! to catch every derived exception that we are interested in catching. +//! +//! Exceptions should be listed in the reverse order that you would write your +//! catch statements (that is, `std::exception&` should be first). +//! +//! \par Example Usage: +//! \code +//! // This catches my runtime_error and if I call throw_exception() on ew, it +//! // will throw a runtime_error +//! auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() { +//! if (badThingHappens()) { +//! throw std::runtime_error("ZOMG!"); +//! } +//! }); +//! +//! // This will catch the exception and if I call throw_exception() on ew, it +//! // will throw a std::exception +//! auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() { +//! if (badThingHappens()) { +//! throw std::exception(); +//! } +//! }); +//! +//! // This will not catch the exception and it will be thrown. +//! auto ew = folly::try_and_catch<std::runtime_error>([=]() { +//! if (badThingHappens()) { +//! throw std::exception(); +//! } +//! }); +//! \endcode +template <typename... Exceptions, typename F> +exception_wrapper try_and_catch(F&& fn) { + return detail::try_and_catch_<F, Exceptions...>(std::forward<F>(fn)); +} +} // namespace folly + +#include <folly/ExceptionWrapper-inl.h> + +#undef FOLLY_REQUIRES +#undef FOLLY_REQUIRES_DEF +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/ios/Pods/Flipper-Folly/folly/Executor.cpp b/ios/Pods/Flipper-Folly/folly/Executor.cpp new file mode 100644 index 000000000..db94bd1d6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Executor.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Executor.h> +#include <folly/SingletonThreadLocal.h> + +#include <stdexcept> + +#include <glog/logging.h> + +namespace folly { + +void Executor::addWithPriority(Func, int8_t /* priority */) { + throw std::runtime_error( + "addWithPriority() is not implemented for this Executor"); +} + +bool Executor::keepAliveAcquire() { + return false; +} + +void Executor::keepAliveRelease() { + LOG(FATAL) << __func__ << "() should not be called for folly::Executor types " + << "which do not override keepAliveAcquire()"; +} + +namespace { +using BlockingContextSingletonT = + SingletonThreadLocal<folly::Optional<BlockingContext>>; +} // namespace + +folly::Optional<BlockingContext> getBlockingContext() { + return BlockingContextSingletonT::get(); +} + +BlockingGuard::BlockingGuard(folly::StringPiece executorName) + : previousContext_{BlockingContextSingletonT::get()} { + BlockingContextSingletonT::get() = BlockingContext{executorName}; +} + +BlockingGuard::BlockingGuard() + : previousContext_{BlockingContextSingletonT::get()} { + BlockingContextSingletonT::get() = folly::none; +} + +BlockingGuard::~BlockingGuard() { + BlockingContextSingletonT::get() = std::move(previousContext_); +} + +BlockingGuard makeBlockingDisallowedGuard(folly::StringPiece executorName) { + return BlockingGuard{executorName}; +} + +BlockingGuard makeBlockingAllowedGuard() { + return BlockingGuard{}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Executor.h b/ios/Pods/Flipper-Folly/folly/Executor.h new file mode 100644 index 000000000..674343187 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Executor.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <climits> +#include <utility> + +#include <folly/Function.h> +#include <folly/Optional.h> +#include <folly/Range.h> +#include <folly/Utility.h> + +namespace folly { +namespace pushmi { +// derive from this for types that need to find operator|() overloads by ADL +struct folly_pipeorigin {}; +} // namespace pushmi + +using Func = Function<void()>; + +namespace detail { + +class ExecutorKeepAliveBase { + public: + // A dummy keep-alive is a keep-alive to an executor which does not support + // the keep-alive mechanism. + static constexpr uintptr_t kDummyFlag = uintptr_t(1) << 0; + + // An alias keep-alive is a keep-alive to an executor to which there is + // known to be another keep-alive whose lifetime surrounds the lifetime of + // the alias. + static constexpr uintptr_t kAliasFlag = uintptr_t(1) << 1; + + static constexpr uintptr_t kFlagMask = kDummyFlag | kAliasFlag; + static constexpr uintptr_t kExecutorMask = ~kFlagMask; +}; + +} // namespace detail + +/// An Executor accepts units of work with add(), which should be +/// threadsafe. +class Executor { + public: + // Workaround for a linkage problem with explicitly defaulted dtor t22914621 + virtual ~Executor() {} + + /// Enqueue a function to executed by this executor. This and all + /// variants must be threadsafe. + virtual void add(Func) = 0; + + /// Enqueue a function with a given priority, where 0 is the medium priority + /// This is up to the implementation to enforce + virtual void addWithPriority(Func, int8_t priority); + + virtual uint8_t getNumPriorities() const { + return 1; + } + + static const int8_t LO_PRI = SCHAR_MIN; + static const int8_t MID_PRI = 0; + static const int8_t HI_PRI = SCHAR_MAX; + + /** + * Executor::KeepAlive is a safe pointer to an Executor. + * For any Executor that supports KeepAlive functionality, Executor's + * destructor will block until all the KeepAlive objects associated with that + * Executor are destroyed. + * For Executors that don't support the KeepAlive funcionality, KeepAlive + * doesn't provide such protection. + * + * KeepAlive should *always* be used instead of Executor*. KeepAlive can be + * implicitly constructed from Executor*. getKeepAliveToken() helper method + * can be used to construct a KeepAlive in templated code if you need to + * preserve the original Executor type. + */ + template <typename ExecutorT = Executor> + class KeepAlive : pushmi::folly_pipeorigin, + private detail::ExecutorKeepAliveBase { + public: + using KeepAliveFunc = Function<void(KeepAlive&&)>; + + KeepAlive() = default; + + ~KeepAlive() { + reset(); + } + + KeepAlive(KeepAlive&& other) noexcept + : storage_(std::exchange(other.storage_, 0)) {} + + KeepAlive(const KeepAlive& other) noexcept + : KeepAlive(getKeepAliveToken(other.get())) {} + + template < + typename OtherExecutor, + typename = typename std::enable_if< + std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> + /* implicit */ KeepAlive(KeepAlive<OtherExecutor>&& other) noexcept + : KeepAlive(other.get(), other.storage_ & kFlagMask) { + other.storage_ = 0; + } + + template < + typename OtherExecutor, + typename = typename std::enable_if< + std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> + /* implicit */ KeepAlive(const KeepAlive<OtherExecutor>& other) noexcept + : KeepAlive(getKeepAliveToken(other.get())) {} + + /* implicit */ KeepAlive(ExecutorT* executor) { + *this = getKeepAliveToken(executor); + } + + KeepAlive& operator=(KeepAlive&& other) { + reset(); + storage_ = std::exchange(other.storage_, 0); + return *this; + } + + KeepAlive& operator=(KeepAlive const& other) { + return operator=(folly::copy(other)); + } + + template < + typename OtherExecutor, + typename = typename std::enable_if< + std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> + KeepAlive& operator=(KeepAlive<OtherExecutor>&& other) { + return *this = KeepAlive(std::move(other)); + } + + template < + typename OtherExecutor, + typename = typename std::enable_if< + std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> + KeepAlive& operator=(const KeepAlive<OtherExecutor>& other) { + return *this = KeepAlive(other); + } + + void reset() { + if (Executor* executor = get()) { + auto const flags = std::exchange(storage_, 0) & kFlagMask; + if (!(flags & (kDummyFlag | kAliasFlag))) { + executor->keepAliveRelease(); + } + } + } + + explicit operator bool() const { + return storage_; + } + + ExecutorT* get() const { + return reinterpret_cast<ExecutorT*>(storage_ & kExecutorMask); + } + + ExecutorT& operator*() const { + return *get(); + } + + ExecutorT* operator->() const { + return get(); + } + + KeepAlive copy() const { + return isKeepAliveDummy(*this) // + ? makeKeepAliveDummy(get()) + : getKeepAliveToken(get()); + } + + KeepAlive get_alias() const { + return KeepAlive(storage_ | kAliasFlag); + } + + template <class KAF> + void add(KAF&& f) && { + static_assert( + is_invocable<KAF, KeepAlive&&>::value, + "Parameter to add must be void(KeepAlive&&)>"); + auto ex = get(); + ex->add([ka = std::move(*this), f = std::forward<KAF>(f)]() mutable { + f(std::move(ka)); + }); + } + + private: + friend class Executor; + template <typename OtherExecutor> + friend class KeepAlive; + + KeepAlive(ExecutorT* executor, uintptr_t flags) noexcept + : storage_(reinterpret_cast<uintptr_t>(executor) | flags) { + assert(executor); + assert(!(reinterpret_cast<uintptr_t>(executor) & ~kExecutorMask)); + assert(!(flags & kExecutorMask)); + } + + explicit KeepAlive(uintptr_t storage) noexcept : storage_(storage) {} + + // Combined storage for the executor pointer and for all flags. + uintptr_t storage_{reinterpret_cast<uintptr_t>(nullptr)}; + }; + + template <typename ExecutorT> + static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "getKeepAliveToken only works for folly::Executor implementations."); + if (!executor) { + return {}; + } + folly::Executor* executorPtr = executor; + if (executorPtr->keepAliveAcquire()) { + return makeKeepAlive<ExecutorT>(executor); + } + return makeKeepAliveDummy<ExecutorT>(executor); + } + + template <typename ExecutorT> + static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "getKeepAliveToken only works for folly::Executor implementations."); + return getKeepAliveToken(&executor); + } + + protected: + /** + * Returns true if the KeepAlive is constructed from an executor that does + * not support the keep alive ref-counting functionality + */ + template <typename ExecutorT> + static bool isKeepAliveDummy(const KeepAlive<ExecutorT>& keepAlive) { + return keepAlive.storage_ & KeepAlive<ExecutorT>::kDummyFlag; + } + + // Acquire a keep alive token. Should return false if keep-alive mechanism + // is not supported. + virtual bool keepAliveAcquire(); + // Release a keep alive token previously acquired by keepAliveAcquire(). + // Will never be called if keepAliveAcquire() returns false. + virtual void keepAliveRelease(); + + template <typename ExecutorT> + static KeepAlive<ExecutorT> makeKeepAlive(ExecutorT* executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "makeKeepAlive only works for folly::Executor implementations."); + return KeepAlive<ExecutorT>{executor, uintptr_t(0)}; + } + + private: + template <typename ExecutorT> + static KeepAlive<ExecutorT> makeKeepAliveDummy(ExecutorT* executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "makeKeepAliveDummy only works for folly::Executor implementations."); + return KeepAlive<ExecutorT>{executor, KeepAlive<ExecutorT>::kDummyFlag}; + } +}; + +/// Returns a keep-alive token which guarantees that Executor will keep +/// processing tasks until the token is released (if supported by Executor). +/// KeepAlive always contains a valid pointer to an Executor. +template <typename ExecutorT> +Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "getKeepAliveToken only works for folly::Executor implementations."); + return Executor::getKeepAliveToken(executor); +} + +template <typename ExecutorT> +Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) { + static_assert( + std::is_base_of<Executor, ExecutorT>::value, + "getKeepAliveToken only works for folly::Executor implementations."); + return getKeepAliveToken(&executor); +} + +template <typename ExecutorT> +Executor::KeepAlive<ExecutorT> getKeepAliveToken( + Executor::KeepAlive<ExecutorT>& ka) { + return ka.copy(); +} + +struct BlockingContext { + folly::StringPiece executorName; +}; + +class BlockingGuard; + +BlockingGuard makeBlockingDisallowedGuard(folly::StringPiece executorName); +BlockingGuard makeBlockingAllowedGuard(); + +class FOLLY_NODISCARD BlockingGuard { + public: + ~BlockingGuard(); + + private: + // Disallow blocking + BlockingGuard(folly::StringPiece executorName); + // Empty guard treated as temporarily allowing blocking + BlockingGuard(); + + friend BlockingGuard makeBlockingDisallowedGuard( + folly::StringPiece executorName); + friend BlockingGuard makeBlockingAllowedGuard(); + + folly::Optional<BlockingContext> previousContext_; +}; + +folly::Optional<BlockingContext> getBlockingContext(); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Expected.h b/ios/Pods/Flipper-Folly/folly/Expected.h new file mode 100644 index 000000000..4b0d4e1ec --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Expected.h @@ -0,0 +1,1501 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Like folly::Optional, but can store a value *or* an error. + * + * @author Eric Niebler (eniebler@fb.com) + */ + +#pragma once + +#include <cstddef> +#include <initializer_list> +#include <new> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/CppAttributes.h> +#include <folly/Likely.h> +#include <folly/Optional.h> +#include <folly/Portability.h> +#include <folly/Preprocessor.h> +#include <folly/Traits.h> +#include <folly/Unit.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> + +#define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__) + +#define FOLLY_REQUIRES_IMPL(...) \ + bool FOLLY_EXPECTED_ID(Requires) = false, \ + typename std::enable_if< \ + (FOLLY_EXPECTED_ID(Requires) || static_cast<bool>(__VA_ARGS__)), \ + int>::type = 0 + +#define FOLLY_REQUIRES_TRAILING(...) , FOLLY_REQUIRES_IMPL(__VA_ARGS__) + +#define FOLLY_REQUIRES(...) template <FOLLY_REQUIRES_IMPL(__VA_ARGS__)> + +namespace folly { + +/** + * Forward declarations + */ +template <class Error> +class Unexpected; + +template <class Error> +constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(Error&&); + +template <class Value, class Error> +class Expected; + +template <class Error, class Value> +constexpr Expected<typename std::decay<Value>::type, Error> makeExpected( + Value&&); + +/** + * Alias for an Expected type's associated value_type + */ +template <class Expected> +using ExpectedValueType = + typename std::remove_reference<Expected>::type::value_type; + +/** + * Alias for an Expected type's associated error_type + */ +template <class Expected> +using ExpectedErrorType = + typename std::remove_reference<Expected>::type::error_type; + +// Details... +namespace expected_detail { + +template <typename Value, typename Error> +struct PromiseReturn; + +template <template <class...> class Trait, class... Ts> +using StrictAllOf = StrictConjunction<Trait<Ts>...>; + +template <class T> +using IsCopyable = StrictConjunction< + std::is_copy_constructible<T>, + std::is_copy_assignable<T>>; + +template <class T> +using IsMovable = StrictConjunction< + std::is_move_constructible<T>, + std::is_move_assignable<T>>; + +template <class T> +using IsNothrowCopyable = StrictConjunction< + std::is_nothrow_copy_constructible<T>, + std::is_nothrow_copy_assignable<T>>; + +template <class T> +using IsNothrowMovable = StrictConjunction< + std::is_nothrow_move_constructible<T>, + std::is_nothrow_move_assignable<T>>; + +template <class From, class To> +using IsConvertible = StrictConjunction< + std::is_constructible<To, From>, + std::is_assignable<To&, From>>; + +template <class T, class U> +auto doEmplaceAssign(int, T& t, U&& u) + -> decltype(void(t = static_cast<U&&>(u))) { + t = static_cast<U&&>(u); +} + +template <class T, class U> +auto doEmplaceAssign(long, T& t, U&& u) + -> decltype(void(T(static_cast<U&&>(u)))) { + t.~T(); + ::new ((void*)std::addressof(t)) T(static_cast<U&&>(u)); +} + +template <class T, class... Us> +auto doEmplaceAssign(int, T& t, Us&&... us) + -> decltype(void(t = T(static_cast<Us&&>(us)...))) { + t = T(static_cast<Us&&>(us)...); +} + +template <class T, class... Us> +auto doEmplaceAssign(long, T& t, Us&&... us) + -> decltype(void(T(static_cast<Us&&>(us)...))) { + t.~T(); + ::new ((void*)std::addressof(t)) T(static_cast<Us&&>(us)...); +} + +struct EmptyTag {}; +struct ValueTag {}; +struct ErrorTag {}; +enum class Which : unsigned char { eEmpty, eValue, eError }; +enum class StorageType { ePODStruct, ePODUnion, eUnion }; + +template <class Value, class Error> +constexpr StorageType getStorageType() { + return StrictAllOf<is_trivially_copyable, Value, Error>::value + ? (sizeof(std::pair<Value, Error>) <= sizeof(void * [2]) && + StrictAllOf<std::is_trivial, Value, Error>::value + ? StorageType::ePODStruct + : StorageType::ePODUnion) + : StorageType::eUnion; +} + +template < + class Value, + class Error, + StorageType = expected_detail::getStorageType<Value, Error>()> // ePODUnion +struct ExpectedStorage { + using value_type = Value; + using error_type = Error; + union { + Value value_; + Error error_; + char ch_; + }; + Which which_; + + template <class E = Error, class = decltype(E{})> + constexpr ExpectedStorage() noexcept(noexcept(E{})) + : error_{}, which_(Which::eError) {} + explicit constexpr ExpectedStorage(EmptyTag) noexcept + : ch_{}, which_(Which::eEmpty) {} + template <class... Vs> + explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept( + noexcept(Value(static_cast<Vs&&>(vs)...))) + : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {} + template <class... Es> + explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept( + noexcept(Error(static_cast<Es&&>(es)...))) + : error_(static_cast<Es&&>(es)...), which_(Which::eError) {} + void clear() noexcept {} + static constexpr bool uninitializedByException() noexcept { + // Although which_ may temporarily be eEmpty during construction, it + // is always either eValue or eError for a fully-constructed Expected. + return false; + } + template <class... Vs> + void assignValue(Vs&&... vs) { + expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...); + which_ = Which::eValue; + } + template <class... Es> + void assignError(Es&&... es) { + expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...); + which_ = Which::eError; + } + template <class Other> + void assign(Other&& that) { + switch (that.which_) { + case Which::eValue: + this->assignValue(static_cast<Other&&>(that).value()); + break; + case Which::eError: + this->assignError(static_cast<Other&&>(that).error()); + break; + case Which::eEmpty: + default: + this->clear(); + break; + } + } + Value& value() & { + return value_; + } + const Value& value() const& { + return value_; + } + Value&& value() && { + return std::move(value_); + } + Error& error() & { + return error_; + } + const Error& error() const& { + return error_; + } + Error&& error() && { + return std::move(error_); + } +}; + +template <class Value, class Error> +struct ExpectedUnion { + union { + Value value_; + Error error_; + char ch_{}; + }; + Which which_ = Which::eEmpty; + + explicit constexpr ExpectedUnion(EmptyTag) noexcept {} + template <class... Vs> + explicit constexpr ExpectedUnion(ValueTag, Vs&&... vs) noexcept( + noexcept(Value(static_cast<Vs&&>(vs)...))) + : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {} + template <class... Es> + explicit constexpr ExpectedUnion(ErrorTag, Es&&... es) noexcept( + noexcept(Error(static_cast<Es&&>(es)...))) + : error_(static_cast<Es&&>(es)...), which_(Which::eError) {} + ExpectedUnion(const ExpectedUnion&) {} + ExpectedUnion(ExpectedUnion&&) noexcept {} + ExpectedUnion& operator=(const ExpectedUnion&) { + return *this; + } + ExpectedUnion& operator=(ExpectedUnion&&) noexcept { + return *this; + } + ~ExpectedUnion() {} + Value& value() & { + return value_; + } + const Value& value() const& { + return value_; + } + Value&& value() && { + return std::move(value_); + } + Error& error() & { + return error_; + } + const Error& error() const& { + return error_; + } + Error&& error() && { + return std::move(error_); + } +}; + +template <class Derived, bool, bool Noexcept> +struct CopyConstructible { + constexpr CopyConstructible() = default; + CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) { + static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that)); + } + constexpr CopyConstructible(CopyConstructible&&) = default; + CopyConstructible& operator=(const CopyConstructible&) = default; + CopyConstructible& operator=(CopyConstructible&&) = default; +}; + +template <class Derived, bool Noexcept> +struct CopyConstructible<Derived, false, Noexcept> { + constexpr CopyConstructible() = default; + CopyConstructible(const CopyConstructible&) = delete; + constexpr CopyConstructible(CopyConstructible&&) = default; + CopyConstructible& operator=(const CopyConstructible&) = default; + CopyConstructible& operator=(CopyConstructible&&) = default; +}; + +template <class Derived, bool, bool Noexcept> +struct MoveConstructible { + constexpr MoveConstructible() = default; + constexpr MoveConstructible(const MoveConstructible&) = default; + MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) { + static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that))); + } + MoveConstructible& operator=(const MoveConstructible&) = default; + MoveConstructible& operator=(MoveConstructible&&) = default; +}; + +template <class Derived, bool Noexcept> +struct MoveConstructible<Derived, false, Noexcept> { + constexpr MoveConstructible() = default; + constexpr MoveConstructible(const MoveConstructible&) = default; + MoveConstructible(MoveConstructible&&) = delete; + MoveConstructible& operator=(const MoveConstructible&) = default; + MoveConstructible& operator=(MoveConstructible&&) = default; +}; + +template <class Derived, bool, bool Noexcept> +struct CopyAssignable { + constexpr CopyAssignable() = default; + constexpr CopyAssignable(const CopyAssignable&) = default; + constexpr CopyAssignable(CopyAssignable&&) = default; + CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) { + static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that)); + return *this; + } + CopyAssignable& operator=(CopyAssignable&&) = default; +}; + +template <class Derived, bool Noexcept> +struct CopyAssignable<Derived, false, Noexcept> { + constexpr CopyAssignable() = default; + constexpr CopyAssignable(const CopyAssignable&) = default; + constexpr CopyAssignable(CopyAssignable&&) = default; + CopyAssignable& operator=(const CopyAssignable&) = delete; + CopyAssignable& operator=(CopyAssignable&&) = default; +}; + +template <class Derived, bool, bool Noexcept> +struct MoveAssignable { + constexpr MoveAssignable() = default; + constexpr MoveAssignable(const MoveAssignable&) = default; + constexpr MoveAssignable(MoveAssignable&&) = default; + MoveAssignable& operator=(const MoveAssignable&) = default; + MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) { + static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that))); + return *this; + } +}; + +template <class Derived, bool Noexcept> +struct MoveAssignable<Derived, false, Noexcept> { + constexpr MoveAssignable() = default; + constexpr MoveAssignable(const MoveAssignable&) = default; + constexpr MoveAssignable(MoveAssignable&&) = default; + MoveAssignable& operator=(const MoveAssignable&) = default; + MoveAssignable& operator=(MoveAssignable&& that) = delete; +}; + +template <class Value, class Error> +struct ExpectedStorage<Value, Error, StorageType::eUnion> + : ExpectedUnion<Value, Error>, + CopyConstructible< + ExpectedStorage<Value, Error, StorageType::eUnion>, + StrictAllOf<std::is_copy_constructible, Value, Error>::value, + StrictAllOf<std::is_nothrow_copy_constructible, Value, Error>::value>, + MoveConstructible< + ExpectedStorage<Value, Error, StorageType::eUnion>, + StrictAllOf<std::is_move_constructible, Value, Error>::value, + StrictAllOf<std::is_nothrow_move_constructible, Value, Error>::value>, + CopyAssignable< + ExpectedStorage<Value, Error, StorageType::eUnion>, + StrictAllOf<IsCopyable, Value, Error>::value, + StrictAllOf<IsNothrowCopyable, Value, Error>::value>, + MoveAssignable< + ExpectedStorage<Value, Error, StorageType::eUnion>, + StrictAllOf<IsMovable, Value, Error>::value, + StrictAllOf<IsNothrowMovable, Value, Error>::value> { + using value_type = Value; + using error_type = Error; + using Base = ExpectedUnion<Value, Error>; + template <class E = Error, class = decltype(E{})> + constexpr ExpectedStorage() noexcept(noexcept(E{})) : Base{ErrorTag{}} {} + ExpectedStorage(const ExpectedStorage&) = default; + ExpectedStorage(ExpectedStorage&&) = default; + ExpectedStorage& operator=(const ExpectedStorage&) = default; + ExpectedStorage& operator=(ExpectedStorage&&) = default; + using ExpectedUnion<Value, Error>::ExpectedUnion; + ~ExpectedStorage() { + clear(); + } + void clear() noexcept { + switch (this->which_) { + case Which::eValue: + this->value().~Value(); + break; + case Which::eError: + this->error().~Error(); + break; + case Which::eEmpty: + default: + break; + } + this->which_ = Which::eEmpty; + } + bool uninitializedByException() const noexcept { + return this->which_ == Which::eEmpty; + } + template <class... Vs> + void assignValue(Vs&&... vs) { + if (this->which_ == Which::eValue) { + expected_detail::doEmplaceAssign( + 0, this->value(), static_cast<Vs&&>(vs)...); + } else { + this->clear(); + ::new ((void*)std::addressof(this->value())) + Value(static_cast<Vs&&>(vs)...); + this->which_ = Which::eValue; + } + } + template <class... Es> + void assignError(Es&&... es) { + if (this->which_ == Which::eError) { + expected_detail::doEmplaceAssign( + 0, this->error(), static_cast<Es&&>(es)...); + } else { + this->clear(); + ::new ((void*)std::addressof(this->error())) + Error(static_cast<Es&&>(es)...); + this->which_ = Which::eError; + } + } + bool isSelfAssign(const ExpectedStorage* that) const { + return this == that; + } + constexpr bool isSelfAssign(const void*) const { + return false; + } + template <class Other> + void assign(Other&& that) { + if (isSelfAssign(&that)) { + return; + } + switch (that.which_) { + case Which::eValue: + this->assignValue(static_cast<Other&&>(that).value()); + break; + case Which::eError: + this->assignError(static_cast<Other&&>(that).error()); + break; + case Which::eEmpty: + default: + this->clear(); + break; + } + } +}; + +// For small (pointer-sized) trivial types, a struct is faster than a union. +template <class Value, class Error> +struct ExpectedStorage<Value, Error, StorageType::ePODStruct> { + using value_type = Value; + using error_type = Error; + Which which_; + Error error_; + Value value_; + + constexpr ExpectedStorage() noexcept + : which_(Which::eError), error_{}, value_{} {} + explicit constexpr ExpectedStorage(EmptyTag) noexcept + : which_(Which::eEmpty), error_{}, value_{} {} + template <class... Vs> + explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept( + noexcept(Value(static_cast<Vs&&>(vs)...))) + : which_(Which::eValue), error_{}, value_(static_cast<Vs&&>(vs)...) {} + template <class... Es> + explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept( + noexcept(Error(static_cast<Es&&>(es)...))) + : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_{} {} + void clear() noexcept {} + constexpr static bool uninitializedByException() noexcept { + return false; + } + template <class... Vs> + void assignValue(Vs&&... vs) { + expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...); + which_ = Which::eValue; + } + template <class... Es> + void assignError(Es&&... es) { + expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...); + which_ = Which::eError; + } + template <class Other> + void assign(Other&& that) { + switch (that.which_) { + case Which::eValue: + this->assignValue(static_cast<Other&&>(that).value()); + break; + case Which::eError: + this->assignError(static_cast<Other&&>(that).error()); + break; + case Which::eEmpty: + default: + this->clear(); + break; + } + } + Value& value() & { + return value_; + } + const Value& value() const& { + return value_; + } + Value&& value() && { + return std::move(value_); + } + Error& error() & { + return error_; + } + const Error& error() const& { + return error_; + } + Error&& error() && { + return std::move(error_); + } +}; + +namespace expected_detail_ExpectedHelper { +// Tricky hack so that Expected::then can handle lambdas that return void +template <class T> +inline T&& operator,(T&& t, Unit) noexcept { + return static_cast<T&&>(t); +} + +struct ExpectedHelper { + template <class Error, class T> + static constexpr Expected<T, Error> return_(T t) { + return folly::makeExpected<Error>(t); + } + template < + class Error, + class T, + class U FOLLY_REQUIRES_TRAILING( + expected_detail::IsConvertible<U&&, Error>::value)> + static constexpr Expected<T, Error> return_(Expected<T, U> t) { + return t; + } + + template <class This> + static typename std::decay<This>::type then_(This&& ex) { + return static_cast<This&&>(ex); + } + + FOLLY_PUSH_WARNING + // Don't warn about not using the overloaded comma operator. + FOLLY_MSVC_DISABLE_WARNING(4913) + template < + class This, + class Fn, + class... Fns, + class E = ExpectedErrorType<This>, + class T = ExpectedHelper> + static auto then_(This&& ex, Fn&& fn, Fns&&... fns) -> decltype(T::then_( + T::template return_<E>( + (std::declval<Fn>()(std::declval<This>().value()), unit)), + std::declval<Fns>()...)) { + if (LIKELY(ex.which_ == expected_detail::Which::eValue)) { + return T::then_( + T::template return_<E>( + // Uses the comma operator defined above IFF the lambda + // returns non-void. + (static_cast<Fn&&>(fn)(static_cast<This&&>(ex).value()), unit)), + static_cast<Fns&&>(fns)...); + } + return makeUnexpected(static_cast<This&&>(ex).error()); + } + + template < + class This, + class Yes, + class No, + class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())), + class Err = decltype(std::declval<No>()(std::declval<This>().error())) + FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)> + static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) { + if (LIKELY(ex.which_ == expected_detail::Which::eValue)) { + return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value())); + } + throw_exception(static_cast<No&&>(no)(static_cast<This&&>(ex).error())); + } + + template < + class This, + class Yes, + class No, + class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())), + class Err = decltype(std::declval<No>()(std::declval<This&>().error())) + FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)> + static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) { + if (LIKELY(ex.which_ == expected_detail::Which::eValue)) { + return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value())); + } + static_cast<No&&>(no)(ex.error()); + typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess bad; + throw_exception(bad(static_cast<This&&>(ex).error())); + } + FOLLY_POP_WARNING +}; +} // namespace expected_detail_ExpectedHelper +/* using override */ using expected_detail_ExpectedHelper::ExpectedHelper; + +struct UnexpectedTag {}; + +} // namespace expected_detail + +using unexpected_t = + expected_detail::UnexpectedTag (&)(expected_detail::UnexpectedTag); + +inline expected_detail::UnexpectedTag unexpected( + expected_detail::UnexpectedTag = {}) { + return {}; +} + +/** + * An exception type thrown by Expected on catastrophic logic errors. + */ +class FOLLY_EXPORT BadExpectedAccess : public std::logic_error { + public: + BadExpectedAccess() : std::logic_error("bad Expected access") {} +}; + +namespace expected_detail { +// empty +} // namespace expected_detail + +/** + * Unexpected - a helper type used to disambiguate the construction of + * Expected objects in the error state. + */ +template <class Error> +class Unexpected final { + template <class E> + friend class Unexpected; + template <class V, class E> + friend class Expected; + friend struct expected_detail::ExpectedHelper; + + public: + /** + * Unexpected::BadExpectedAccess - An exception type thrown by Expected + * when the user tries to access the nested value but the Expected object is + * actually storing an error code. + */ + class FOLLY_EXPORT BadExpectedAccess : public folly::BadExpectedAccess { + public: + explicit BadExpectedAccess(Error err) + : folly::BadExpectedAccess{}, error_(std::move(err)) {} + /** + * The error code that was held by the Expected object when the user + * erroneously requested the value. + */ + Error error() const { + return error_; + } + + private: + Error error_; + }; + + /** + * Constructors + */ + Unexpected() = default; + Unexpected(const Unexpected&) = default; + Unexpected(Unexpected&&) = default; + Unexpected& operator=(const Unexpected&) = default; + Unexpected& operator=(Unexpected&&) = default; + FOLLY_COLD constexpr /* implicit */ Unexpected(const Error& err) + : error_(err) {} + FOLLY_COLD constexpr /* implicit */ Unexpected(Error&& err) + : error_(std::move(err)) {} + + template <class Other FOLLY_REQUIRES_TRAILING( + std::is_constructible<Error, Other&&>::value)> + constexpr /* implicit */ Unexpected(Unexpected<Other> that) + : error_(std::move(that.error())) {} + + /** + * Assignment + */ + template <class Other FOLLY_REQUIRES_TRAILING( + std::is_assignable<Error&, Other&&>::value)> + Unexpected& operator=(Unexpected<Other> that) { + error_ = std::move(that.error()); + } + + /** + * Observers + */ + Error& error() & { + return error_; + } + const Error& error() const& { + return error_; + } + Error&& error() && { + return std::move(error_); + } + + private: + struct MakeBadExpectedAccess { + template <class E> + BadExpectedAccess operator()(E&& err) const { + return BadExpectedAccess(static_cast<E&&>(err)); + } + }; + + Error error_; +}; + +template < + class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)> +inline bool operator==( + const Unexpected<Error>& lhs, + const Unexpected<Error>& rhs) { + return lhs.error() == rhs.error(); +} + +template < + class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)> +inline bool operator!=( + const Unexpected<Error>& lhs, + const Unexpected<Error>& rhs) { + return !(lhs == rhs); +} + +/** + * For constructing an Unexpected object from an error code. Unexpected objects + * are implicitly convertible to Expected object in the error state. Usage is + * as follows: + * + * enum class MyErrorCode { BAD_ERROR, WORSE_ERROR }; + * Expected<int, MyErrorCode> myAPI() { + * int i = // ...; + * return i ? makeExpected<MyErrorCode>(i) + * : makeUnexpected(MyErrorCode::BAD_ERROR); + * } + */ +template <class Error> +constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected( + Error&& err) { + return Unexpected<typename std::decay<Error>::type>{ + static_cast<Error&&>(err)}; +} + +/** + * Expected - For holding a value or an error. Useful as an alternative to + * exceptions, for APIs where throwing on failure would be too expensive. + * + * Expected<Value, Error> is a variant over the types Value and Error. + * + * Expected does not offer support for references. Use + * Expected<std::reference_wrapper<T>, Error> if your API needs to return a + * reference or an error. + * + * Expected offers a continuation-based interface to reduce the boilerplate + * of checking error codes. The Expected::then member function takes a lambda + * that is to execute should the Expected object contain a value. The return + * value of the lambda is wrapped in an Expected and returned. If the lambda is + * not executed because the Expected contains an error, the error is returned + * immediately in a new Expected object. + * + * Expected<int, Error> funcTheFirst(); + * Expected<std::string, Error> funcTheSecond() { + * return funcTheFirst().then([](int i) { return std::to_string(i); }); + * } + * + * The above line of code could more verbosely written as: + * + * Expected<std::string, Error> funcTheSecond() { + * if (auto ex = funcTheFirst()) { + * return std::to_string(*ex); + * } + * return makeUnexpected(ex.error()); + * } + * + * Continuations can chain, like: + * + * Expected<D, Error> maybeD = someFunc() + * .then([](A a){return B(a);}) + * .then([](B b){return C(b);}) + * .then([](C c){return D(c);}); + * + * To avoid the redundant error checking that would happen if a call at the + * front of the chain returns an error, these call chains can be collaped into + * a single call to .then: + * + * Expected<D, Error> maybeD = someFunc() + * .then([](A a){return B(a);}, + * [](B b){return C(b);}, + * [](C c){return D(c);}); + * + * The result of .then() is wrapped into Expected< ~, Error > if it isn't + * of that form already. Consider the following code: + * + * extern Expected<std::string, Error> readLineFromIO(); + * extern Expected<int, Error> parseInt(std::string); + * extern int increment(int); + * + * Expected<int, Error> x = readLineFromIO().then(parseInt).then(increment); + * + * From the code above, we see that .then() works both with functions that + * return an Expected< ~, Error > (like parseInt) and with ones that return + * a plain value (like increment). In the case of parseInt, .then() returns + * the result of parseInt as-is. In the case of increment, it wraps the int + * that increment returns into an Expected< int, Error >. + * + * Sometimes when using a continuation you would prefer an exception to be + * thrown for a value-less Expected. For that you can use .thenOrThrow, as + * follows: + * + * B b = someFunc() + * .thenOrThrow([](A a){return B(a);}); + * + * The above call to thenOrThrow will invoke the lambda if the Expected returned + * by someFunc() contains a value. Otherwise, it will throw an exception of type + * Unexpected<Error>::BadExpectedAccess. If you prefer it throw an exception of + * a different type, you can pass a second lambda to thenOrThrow: + * + * B b = someFunc() + * .thenOrThrow([](A a){return B(a);}, + * [](Error e) {throw MyException(e);}); + * + * Like C++17's std::variant, Expected offers the almost-never-empty guarantee; + * that is, an Expected<Value, Error> almost always contains either a Value or + * and Error. Partially-formed Expected objects occur when an assignment to + * an Expected object that would change the type of the contained object (Value- + * to-Error or vice versa) throws. Trying to access either the contained value + * or error object causes Expected to throw folly::BadExpectedAccess. + * + * Expected models OptionalPointee, so calling 'get_pointer(ex)' will return a + * pointer to nullptr if the 'ex' is in the error state, and a pointer to the + * value otherwise: + * + * Expected<int, Error> maybeInt = ...; + * if (int* v = get_pointer(maybeInt)) { + * cout << *v << endl; + * } + */ +template <class Value, class Error> +class Expected final : expected_detail::ExpectedStorage<Value, Error> { + template <class, class> + friend class Expected; + template <class, class, expected_detail::StorageType> + friend struct expected_detail::ExpectedStorage; + friend struct expected_detail::ExpectedHelper; + using Base = expected_detail::ExpectedStorage<Value, Error>; + using MakeBadExpectedAccess = + typename Unexpected<Error>::MakeBadExpectedAccess; + Base& base() & { + return *this; + } + const Base& base() const& { + return *this; + } + Base&& base() && { + return std::move(*this); + } + + public: + using value_type = Value; + using error_type = Error; + + template <class U> + using rebind = Expected<U, Error>; + + static_assert( + !std::is_reference<Value>::value, + "Expected may not be used with reference types"); + static_assert( + !std::is_abstract<Value>::value, + "Expected may not be used with abstract types"); + + /* + * Constructors + */ + template <class B = Base, class = decltype(B{})> + Expected() noexcept(noexcept(B{})) : Base{} {} + Expected(const Expected& that) = default; + Expected(Expected&& that) = default; + + template < + class V, + class E FOLLY_REQUIRES_TRAILING( + !std::is_same<Expected<V, E>, Expected>::value && + std::is_constructible<Value, V&&>::value && + std::is_constructible<Error, E&&>::value)> + Expected(Expected<V, E> that) : Base{expected_detail::EmptyTag{}} { + this->assign(std::move(that)); + } + + FOLLY_REQUIRES(std::is_copy_constructible<Value>::value) + constexpr /* implicit */ Expected(const Value& val) noexcept( + noexcept(Value(val))) + : Base{expected_detail::ValueTag{}, val} {} + + FOLLY_REQUIRES(std::is_move_constructible<Value>::value) + constexpr /* implicit */ Expected(Value&& val) noexcept( + noexcept(Value(std::move(val)))) + : Base{expected_detail::ValueTag{}, std::move(val)} {} + + template <class T FOLLY_REQUIRES_TRAILING( + std::is_convertible<T, Value>::value && + !std::is_convertible<T, Error>::value)> + constexpr /* implicit */ Expected(T&& val) noexcept( + noexcept(Value(static_cast<T&&>(val)))) + : Base{expected_detail::ValueTag{}, static_cast<T&&>(val)} {} + + template <class... Ts FOLLY_REQUIRES_TRAILING( + std::is_constructible<Value, Ts&&...>::value)> + explicit constexpr Expected(in_place_t, Ts&&... ts) noexcept( + noexcept(Value(std::declval<Ts>()...))) + : Base{expected_detail::ValueTag{}, static_cast<Ts&&>(ts)...} {} + + template < + class U, + class... Ts FOLLY_REQUIRES_TRAILING( + std::is_constructible<Value, std::initializer_list<U>&, Ts&&...>:: + value)> + explicit constexpr Expected( + in_place_t, + std::initializer_list<U> il, + Ts&&... ts) noexcept(noexcept(Value(std::declval<Ts>()...))) + : Base{expected_detail::ValueTag{}, il, static_cast<Ts&&>(ts)...} {} + + // If overload resolution selects one of these deleted functions, that + // means you need to use makeUnexpected + /* implicit */ Expected(const Error&) = delete; + /* implicit */ Expected(Error&&) = delete; + + FOLLY_REQUIRES(std::is_copy_constructible<Error>::value) + constexpr Expected(unexpected_t, const Error& err) noexcept( + noexcept(Error(err))) + : Base{expected_detail::ErrorTag{}, err} {} + + FOLLY_REQUIRES(std::is_move_constructible<Error>::value) + constexpr Expected(unexpected_t, Error&& err) noexcept( + noexcept(Error(std::move(err)))) + : Base{expected_detail::ErrorTag{}, std::move(err)} {} + + FOLLY_REQUIRES(std::is_copy_constructible<Error>::value) + constexpr /* implicit */ Expected(const Unexpected<Error>& err) noexcept( + noexcept(Error(err.error()))) + : Base{expected_detail::ErrorTag{}, err.error()} {} + + FOLLY_REQUIRES(std::is_move_constructible<Error>::value) + constexpr /* implicit */ Expected(Unexpected<Error>&& err) noexcept( + noexcept(Error(std::move(err.error())))) + : Base{expected_detail::ErrorTag{}, std::move(err.error())} {} + + /* + * Assignment operators + */ + Expected& operator=(const Expected& that) = default; + Expected& operator=(Expected&& that) = default; + + template < + class V, + class E FOLLY_REQUIRES_TRAILING( + !std::is_same<Expected<V, E>, Expected>::value && + expected_detail::IsConvertible<V&&, Value>::value && + expected_detail::IsConvertible<E&&, Error>::value)> + Expected& operator=(Expected<V, E> that) { + this->assign(std::move(that)); + return *this; + } + + FOLLY_REQUIRES(expected_detail::IsCopyable<Value>::value) + Expected& operator=(const Value& val) noexcept( + expected_detail::IsNothrowCopyable<Value>::value) { + this->assignValue(val); + return *this; + } + + FOLLY_REQUIRES(expected_detail::IsMovable<Value>::value) + Expected& operator=(Value&& val) noexcept( + expected_detail::IsNothrowMovable<Value>::value) { + this->assignValue(std::move(val)); + return *this; + } + + template <class T FOLLY_REQUIRES_TRAILING( + std::is_convertible<T, Value>::value && + !std::is_convertible<T, Error>::value)> + Expected& operator=(T&& val) { + this->assignValue(static_cast<T&&>(val)); + return *this; + } + + FOLLY_REQUIRES(expected_detail::IsCopyable<Error>::value) + Expected& operator=(const Unexpected<Error>& err) noexcept( + expected_detail::IsNothrowCopyable<Error>::value) { + this->assignError(err.error()); + return *this; + } + + FOLLY_REQUIRES(expected_detail::IsMovable<Error>::value) + Expected& operator=(Unexpected<Error>&& err) noexcept( + expected_detail::IsNothrowMovable<Error>::value) { + this->assignError(std::move(err.error())); + return *this; + } + + // Used only when an Expected is used with coroutines on MSVC + /* implicit */ Expected(const expected_detail::PromiseReturn<Value, Error>& p) + : Expected{} { + p.promise_->value_ = this; + } + + template <class... Ts FOLLY_REQUIRES_TRAILING( + std::is_constructible<Value, Ts&&...>::value)> + void emplace(Ts&&... ts) { + this->assignValue(static_cast<Ts&&>(ts)...); + } + + /** + * swap + */ + void swap(Expected& that) noexcept( + expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) { + if (this->uninitializedByException() || that.uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + using std::swap; + if (*this) { + if (that) { + swap(this->value_, that.value_); + } else { + Error e(std::move(that.error_)); + that.assignValue(std::move(this->value_)); + this->assignError(std::move(e)); + } + } else { + if (!that) { + swap(this->error_, that.error_); + } else { + Error e(std::move(this->error_)); + this->assignValue(std::move(that.value_)); + that.assignError(std::move(e)); + } + } + } + + // If overload resolution selects one of these deleted functions, that + // means you need to use makeUnexpected + /* implicit */ Expected& operator=(const Error&) = delete; + /* implicit */ Expected& operator=(Error&&) = delete; + + /** + * Relational Operators + */ + template <class Val, class Err> + friend typename std::enable_if<IsEqualityComparable<Val>::value, bool>::type + operator==(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs); + template <class Val, class Err> + friend typename std::enable_if<IsLessThanComparable<Val>::value, bool>::type + operator<(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs); + + /* + * Accessors + */ + constexpr bool hasValue() const noexcept { + return LIKELY(expected_detail::Which::eValue == this->which_); + } + + constexpr bool hasError() const noexcept { + return UNLIKELY(expected_detail::Which::eError == this->which_); + } + + using Base::uninitializedByException; + + const Value& value() const& { + requireValue(); + return this->Base::value(); + } + + Value& value() & { + requireValue(); + return this->Base::value(); + } + + Value&& value() && { + requireValue(); + return std::move(this->Base::value()); + } + + const Error& error() const& { + requireError(); + return this->Base::error(); + } + + Error& error() & { + requireError(); + return this->Base::error(); + } + + Error&& error() && { + requireError(); + return std::move(this->Base::error()); + } + + // Return a copy of the value if set, or a given default if not. + template <class U> + Value value_or(U&& dflt) const& { + if (LIKELY(this->which_ == expected_detail::Which::eValue)) { + return this->value_; + } + return static_cast<U&&>(dflt); + } + + template <class U> + Value value_or(U&& dflt) && { + if (LIKELY(this->which_ == expected_detail::Which::eValue)) { + return std::move(this->value_); + } + return static_cast<U&&>(dflt); + } + + explicit constexpr operator bool() const noexcept { + return hasValue(); + } + + const Value& operator*() const& { + return this->value(); + } + + Value& operator*() & { + return this->value(); + } + + Value&& operator*() && { + return std::move(this->value()); + } + + const Value* operator->() const { + return std::addressof(this->value()); + } + + Value* operator->() { + return std::addressof(this->value()); + } + + const Value* get_pointer() const& noexcept { + return hasValue() ? std::addressof(this->value_) : nullptr; + } + + Value* get_pointer() & noexcept { + return hasValue() ? std::addressof(this->value_) : nullptr; + } + + Value* get_pointer() && = delete; + + /** + * then + */ + template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)> + auto then(Fns&&... fns) const& -> decltype( + expected_detail::ExpectedHelper::then_( + std::declval<const Base&>(), + std::declval<Fns>()...)) { + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return expected_detail::ExpectedHelper::then_( + base(), static_cast<Fns&&>(fns)...); + } + + template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)> + auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_( + std::declval<Base&>(), + std::declval<Fns>()...)) { + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return expected_detail::ExpectedHelper::then_( + base(), static_cast<Fns&&>(fns)...); + } + + template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)> + auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_( + std::declval<Base&&>(), + std::declval<Fns>()...)) { + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return expected_detail::ExpectedHelper::then_( + std::move(base()), static_cast<Fns&&>(fns)...); + } + + /** + * thenOrThrow + */ + template <class Yes, class No = MakeBadExpectedAccess> + auto thenOrThrow(Yes&& yes, No&& no = No{}) const& -> decltype( + std::declval<Yes>()(std::declval<const Value&>())) { + using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>())); + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return Ret(expected_detail::ExpectedHelper::thenOrThrow_( + base(), static_cast<Yes&&>(yes), static_cast<No&&>(no))); + } + + template <class Yes, class No = MakeBadExpectedAccess> + auto thenOrThrow(Yes&& yes, No&& no = No{}) & -> decltype( + std::declval<Yes>()(std::declval<Value&>())) { + using Ret = decltype(std::declval<Yes>()(std::declval<Value&>())); + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return Ret(expected_detail::ExpectedHelper::thenOrThrow_( + base(), static_cast<Yes&&>(yes), static_cast<No&&>(no))); + } + + template <class Yes, class No = MakeBadExpectedAccess> + auto thenOrThrow(Yes&& yes, No&& no = No{}) && -> decltype( + std::declval<Yes>()(std::declval<Value&&>())) { + using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>())); + if (this->uninitializedByException()) { + throw_exception<BadExpectedAccess>(); + } + return Ret(expected_detail::ExpectedHelper::thenOrThrow_( + std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no))); + } + + private: + void requireValue() const { + if (UNLIKELY(!hasValue())) { + if (LIKELY(hasError())) { + using Err = typename Unexpected<Error>::BadExpectedAccess; + throw_exception<Err>(this->error_); + } + throw_exception<BadExpectedAccess>(); + } + } + + void requireError() const { + if (UNLIKELY(!hasError())) { + throw_exception<BadExpectedAccess>(); + } + } + + expected_detail::Which which() const noexcept { + return this->which_; + } +}; + +template <class Value, class Error> +inline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type +operator==( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + if (UNLIKELY(lhs.uninitializedByException())) { + throw_exception<BadExpectedAccess>(); + } + if (UNLIKELY(lhs.which_ != rhs.which_)) { + return false; + } + if (UNLIKELY(lhs.hasError())) { + return true; // All error states are considered equal + } + return lhs.value_ == rhs.value_; +} + +template < + class Value, + class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)> +inline bool operator!=( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + return !(rhs == lhs); +} + +template <class Value, class Error> +inline typename std::enable_if<IsLessThanComparable<Value>::value, bool>::type +operator<( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + if (UNLIKELY( + lhs.uninitializedByException() || rhs.uninitializedByException())) { + throw_exception<BadExpectedAccess>(); + } + if (UNLIKELY(lhs.hasError())) { + return !rhs.hasError(); + } + if (UNLIKELY(rhs.hasError())) { + return false; + } + return lhs.value_ < rhs.value_; +} + +template < + class Value, + class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> +inline bool operator<=( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + return !(rhs < lhs); +} + +template < + class Value, + class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> +inline bool operator>( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + return rhs < lhs; +} + +template < + class Value, + class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> +inline bool operator>=( + const Expected<Value, Error>& lhs, + const Expected<Value, Error>& rhs) { + return !(lhs < rhs); +} + +/** + * swap Expected values + */ +template <class Value, class Error> +void swap(Expected<Value, Error>& lhs, Expected<Value, Error>& rhs) noexcept( + expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) { + lhs.swap(rhs); +} + +template <class Value, class Error> +const Value* get_pointer(const Expected<Value, Error>& ex) noexcept { + return ex.get_pointer(); +} + +template <class Value, class Error> +Value* get_pointer(Expected<Value, Error>& ex) noexcept { + return ex.get_pointer(); +} + +/** + * For constructing an Expected object from a value, with the specified + * Error type. Usage is as follows: + * + * enum MyErrorCode { BAD_ERROR, WORSE_ERROR }; + * Expected<int, MyErrorCode> myAPI() { + * int i = // ...; + * return i ? makeExpected<MyErrorCode>(i) : makeUnexpected(BAD_ERROR); + * } + */ +template <class Error, class Value> +constexpr Expected<typename std::decay<Value>::type, Error> makeExpected( + Value&& val) { + return Expected<typename std::decay<Value>::type, Error>{ + in_place, static_cast<Value&&>(val)}; +} + +// Suppress comparability of Optional<T> with T, despite implicit conversion. +template <class Value, class Error> +bool operator==(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator!=(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator<(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator<=(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator>=(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator>(const Expected<Value, Error>&, const Value& other) = delete; +template <class Value, class Error> +bool operator==(const Value& other, const Expected<Value, Error>&) = delete; +template <class Value, class Error> +bool operator!=(const Value& other, const Expected<Value, Error>&) = delete; +template <class Value, class Error> +bool operator<(const Value& other, const Expected<Value, Error>&) = delete; +template <class Value, class Error> +bool operator<=(const Value& other, const Expected<Value, Error>&) = delete; +template <class Value, class Error> +bool operator>=(const Value& other, const Expected<Value, Error>&) = delete; +template <class Value, class Error> +bool operator>(const Value& other, const Expected<Value, Error>&) = delete; + +} // namespace folly + +#undef FOLLY_REQUIRES +#undef FOLLY_REQUIRES_TRAILING + +// Enable the use of folly::Expected with `co_await` +// Inspired by https://github.com/toby-allsopp/coroutine_monad +#if FOLLY_HAS_COROUTINES +#include <experimental/coroutine> + +namespace folly { +namespace expected_detail { +template <typename Value, typename Error> +struct Promise; + +template <typename Value, typename Error> +struct PromiseReturn { + Optional<Expected<Value, Error>> storage_; + Promise<Value, Error>* promise_; + /* implicit */ PromiseReturn(Promise<Value, Error>& promise) noexcept + : promise_(&promise) { + promise_->value_ = &storage_; + } + PromiseReturn(PromiseReturn&& that) noexcept + : PromiseReturn{*that.promise_} {} + ~PromiseReturn() {} + /* implicit */ operator Expected<Value, Error>() & { + return std::move(*storage_); + } +}; + +template <typename Value, typename Error> +struct Promise { + Optional<Expected<Value, Error>>* value_ = nullptr; + Promise() = default; + Promise(Promise const&) = delete; + // This should work regardless of whether the compiler generates: + // folly::Expected<Value, Error> retobj{ p.get_return_object(); } // MSVC + // or: + // auto retobj = p.get_return_object(); // clang + PromiseReturn<Value, Error> get_return_object() noexcept { + return *this; + } + std::experimental::suspend_never initial_suspend() const noexcept { + return {}; + } + std::experimental::suspend_never final_suspend() const { + return {}; + } + template <typename U> + void return_value(U&& u) { + value_->emplace(static_cast<U&&>(u)); + } + void unhandled_exception() { + // Technically, throwing from unhandled_exception is underspecified: + // https://github.com/GorNishanov/CoroutineWording/issues/17 + throw; + } +}; + +template <typename Value, typename Error> +struct Awaitable { + Expected<Value, Error> o_; + + explicit Awaitable(Expected<Value, Error> o) : o_(std::move(o)) {} + + bool await_ready() const noexcept { + return o_.hasValue(); + } + Value await_resume() { + return std::move(o_.value()); + } + + // Explicitly only allow suspension into a Promise + template <typename U> + void await_suspend(std::experimental::coroutine_handle<Promise<U, Error>> h) { + *h.promise().value_ = makeUnexpected(std::move(o_.error())); + // Abort the rest of the coroutine. resume() is not going to be called + h.destroy(); + } +}; +} // namespace expected_detail + +template <typename Value, typename Error> +expected_detail::Awaitable<Value, Error> +/* implicit */ operator co_await(Expected<Value, Error> o) { + return expected_detail::Awaitable<Value, Error>{std::move(o)}; +} +} // namespace folly + +// This makes folly::Expected<Value> useable as a coroutine return type... +namespace std { +namespace experimental { +template <typename Value, typename Error, typename... Args> +struct coroutine_traits<folly::Expected<Value, Error>, Args...> { + using promise_type = folly::expected_detail::Promise<Value, Error>; +}; +} // namespace experimental +} // namespace std +#endif // FOLLY_HAS_COROUTINES diff --git a/ios/Pods/Flipper-Folly/folly/FBString.h b/ios/Pods/Flipper-Folly/folly/FBString.h new file mode 100644 index 000000000..c202c4237 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FBString.h @@ -0,0 +1,2864 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Andrei Alexandrescu (aalexandre) +// String type. + +#pragma once + +#include <atomic> +#include <cstddef> +#include <iosfwd> +#include <limits> +#include <stdexcept> +#include <type_traits> + +#if FOLLY_HAS_STRING_VIEW +#include <string_view> +#endif + +#include <folly/CPortability.h> +#include <folly/CppAttributes.h> +#include <folly/Likely.h> +#include <folly/Portability.h> + +#include <algorithm> +#include <cassert> +#include <cstring> +#include <string> +#include <utility> + +#include <folly/Traits.h> +#include <folly/hash/Hash.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Exception.h> +#include <folly/memory/Malloc.h> + +FOLLY_PUSH_WARNING +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +// When compiling with ASan, always heap-allocate the string even if +// it would fit in-situ, so that ASan can detect access to the string +// buffer after it has been invalidated (destroyed, resized, etc.). +// Note that this flag doesn't remove support for in-situ strings, as +// that would break ABI-compatibility and wouldn't allow linking code +// compiled with this flag with code compiled without. +#ifdef FOLLY_SANITIZE_ADDRESS +#define FBSTRING_DISABLE_SSO true +#else +#define FBSTRING_DISABLE_SSO false +#endif + +namespace fbstring_detail { + +template <class InIt, class OutIt> +inline std::pair<InIt, OutIt> copy_n( + InIt b, + typename std::iterator_traits<InIt>::difference_type n, + OutIt d) { + for (; n != 0; --n, ++b, ++d) { + *d = *b; + } + return std::make_pair(b, d); +} + +template <class Pod, class T> +inline void podFill(Pod* b, Pod* e, T c) { + assert(b && e && b <= e); + constexpr auto kUseMemset = sizeof(T) == 1; + if /* constexpr */ (kUseMemset) { + memset(b, c, size_t(e - b)); + } else { + auto const ee = b + ((e - b) & ~7u); + for (; b != ee; b += 8) { + b[0] = c; + b[1] = c; + b[2] = c; + b[3] = c; + b[4] = c; + b[5] = c; + b[6] = c; + b[7] = c; + } + // Leftovers + for (; b != e; ++b) { + *b = c; + } + } +} + +/* + * Lightly structured memcpy, simplifies copying PODs and introduces + * some asserts. Unfortunately using this function may cause + * measurable overhead (presumably because it adjusts from a begin/end + * convention to a pointer/size convention, so it does some extra + * arithmetic even though the caller might have done the inverse + * adaptation outside). + */ +template <class Pod> +inline void podCopy(const Pod* b, const Pod* e, Pod* d) { + assert(b != nullptr); + assert(e != nullptr); + assert(d != nullptr); + assert(e >= b); + assert(d >= e || d + (e - b) <= b); + memcpy(d, b, (e - b) * sizeof(Pod)); +} + +/* + * Lightly structured memmove, simplifies copying PODs and introduces + * some asserts + */ +template <class Pod> +inline void podMove(const Pod* b, const Pod* e, Pod* d) { + assert(e >= b); + memmove(d, b, (e - b) * sizeof(*b)); +} +} // namespace fbstring_detail + +/** + * Defines a special acquisition method for constructing fbstring + * objects. AcquireMallocatedString means that the user passes a + * pointer to a malloc-allocated string that the fbstring object will + * take into custody. + */ +enum class AcquireMallocatedString {}; + +/* + * fbstring_core_model is a mock-up type that defines all required + * signatures of a fbstring core. The fbstring class itself uses such + * a core object to implement all of the numerous member functions + * required by the standard. + * + * If you want to define a new core, copy the definition below and + * implement the primitives. Then plug the core into basic_fbstring as + * a template argument. + +template <class Char> +class fbstring_core_model { + public: + fbstring_core_model(); + fbstring_core_model(const fbstring_core_model &); + fbstring_core_model& operator=(const fbstring_core_model &) = delete; + ~fbstring_core_model(); + // Returns a pointer to string's buffer (currently only contiguous + // strings are supported). The pointer is guaranteed to be valid + // until the next call to a non-const member function. + const Char * data() const; + // Much like data(), except the string is prepared to support + // character-level changes. This call is a signal for + // e.g. reference-counted implementation to fork the data. The + // pointer is guaranteed to be valid until the next call to a + // non-const member function. + Char* mutableData(); + // Returns a pointer to string's buffer and guarantees that a + // readable '\0' lies right after the buffer. The pointer is + // guaranteed to be valid until the next call to a non-const member + // function. + const Char * c_str() const; + // Shrinks the string by delta characters. Asserts that delta <= + // size(). + void shrink(size_t delta); + // Expands the string by delta characters (i.e. after this call + // size() will report the old size() plus delta) but without + // initializing the expanded region. The expanded region is + // zero-terminated. Returns a pointer to the memory to be + // initialized (the beginning of the expanded portion). The caller + // is expected to fill the expanded area appropriately. + // If expGrowth is true, exponential growth is guaranteed. + // It is not guaranteed not to reallocate even if size() + delta < + // capacity(), so all references to the buffer are invalidated. + Char* expandNoinit(size_t delta, bool expGrowth); + // Expands the string by one character and sets the last character + // to c. + void push_back(Char c); + // Returns the string's size. + size_t size() const; + // Returns the string's capacity, i.e. maximum size that the string + // can grow to without reallocation. Note that for reference counted + // strings that's technically a lie - even assigning characters + // within the existing size would cause a reallocation. + size_t capacity() const; + // Returns true if the data underlying the string is actually shared + // across multiple strings (in a refcounted fashion). + bool isShared() const; + // Makes sure that at least minCapacity characters are available for + // the string without reallocation. For reference-counted strings, + // it should fork the data even if minCapacity < size(). + void reserve(size_t minCapacity); +}; +*/ + +/** + * This is the core of the string. The code should work on 32- and + * 64-bit and both big- and little-endianan architectures with any + * Char size. + * + * The storage is selected as follows (assuming we store one-byte + * characters on a 64-bit machine): (a) "small" strings between 0 and + * 23 chars are stored in-situ without allocation (the rightmost byte + * stores the size); (b) "medium" strings from 24 through 254 chars + * are stored in malloc-allocated memory that is copied eagerly; (c) + * "large" strings of 255 chars and above are stored in a similar + * structure as medium arrays, except that the string is + * reference-counted and copied lazily. the reference count is + * allocated right before the character array. + * + * The discriminator between these three strategies sits in two + * bits of the rightmost char of the storage: + * - If neither is set, then the string is small. Its length is represented by + * the lower-order bits on little-endian or the high-order bits on big-endian + * of that rightmost character. The value of these six bits is + * `maxSmallSize - size`, so this quantity must be subtracted from + * `maxSmallSize` to compute the `size` of the string (see `smallSize()`). + * This scheme ensures that when `size == `maxSmallSize`, the last byte in the + * storage is \0. This way, storage will be a null-terminated sequence of + * bytes, even if all 23 bytes of data are used on a 64-bit architecture. + * This enables `c_str()` and `data()` to simply return a pointer to the + * storage. + * + * - If the MSb is set, the string is medium width. + * + * - If the second MSb is set, then the string is large. On little-endian, + * these 2 bits are the 2 MSbs of MediumLarge::capacity_, while on + * big-endian, these 2 bits are the 2 LSbs. This keeps both little-endian + * and big-endian fbstring_core equivalent with merely different ops used + * to extract capacity/category. + */ +template <class Char> +class fbstring_core { + public: + fbstring_core() noexcept { + reset(); + } + + fbstring_core(const fbstring_core& rhs) { + assert(&rhs != this); + switch (rhs.category()) { + case Category::isSmall: + copySmall(rhs); + break; + case Category::isMedium: + copyMedium(rhs); + break; + case Category::isLarge: + copyLarge(rhs); + break; + default: + folly::assume_unreachable(); + } + assert(size() == rhs.size()); + assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0); + } + + fbstring_core& operator=(const fbstring_core& rhs) = delete; + + fbstring_core(fbstring_core&& goner) noexcept { + // Take goner's guts + ml_ = goner.ml_; + // Clean goner's carcass + goner.reset(); + } + + fbstring_core( + const Char* const data, + const size_t size, + bool disableSSO = FBSTRING_DISABLE_SSO) { + if (!disableSSO && size <= maxSmallSize) { + initSmall(data, size); + } else if (size <= maxMediumSize) { + initMedium(data, size); + } else { + initLarge(data, size); + } + assert(this->size() == size); + assert(size == 0 || memcmp(this->data(), data, size * sizeof(Char)) == 0); + } + + ~fbstring_core() noexcept { + if (category() == Category::isSmall) { + return; + } + destroyMediumLarge(); + } + + // Snatches a previously mallocated string. The parameter "size" + // is the size of the string, and the parameter "allocatedSize" + // is the size of the mallocated block. The string must be + // \0-terminated, so allocatedSize >= size + 1 and data[size] == '\0'. + // + // So if you want a 2-character string, pass malloc(3) as "data", + // pass 2 as "size", and pass 3 as "allocatedSize". + fbstring_core( + Char* const data, + const size_t size, + const size_t allocatedSize, + AcquireMallocatedString) { + if (size > 0) { + assert(allocatedSize >= size + 1); + assert(data[size] == '\0'); + // Use the medium string storage + ml_.data_ = data; + ml_.size_ = size; + // Don't forget about null terminator + ml_.setCapacity(allocatedSize - 1, Category::isMedium); + } else { + // No need for the memory + free(data); + reset(); + } + } + + // swap below doesn't test whether &rhs == this (and instead + // potentially does extra work) on the premise that the rarity of + // that situation actually makes the check more expensive than is + // worth. + void swap(fbstring_core& rhs) { + auto const t = ml_; + ml_ = rhs.ml_; + rhs.ml_ = t; + } + + // In C++11 data() and c_str() are 100% equivalent. + const Char* data() const { + return c_str(); + } + + Char* data() { + return c_str(); + } + + Char* mutableData() { + switch (category()) { + case Category::isSmall: + return small_; + case Category::isMedium: + return ml_.data_; + case Category::isLarge: + return mutableDataLarge(); + } + folly::assume_unreachable(); + } + + const Char* c_str() const { + const Char* ptr = ml_.data_; + // With this syntax, GCC and Clang generate a CMOV instead of a branch. + ptr = (category() == Category::isSmall) ? small_ : ptr; + return ptr; + } + + void shrink(const size_t delta) { + if (category() == Category::isSmall) { + shrinkSmall(delta); + } else if ( + category() == Category::isMedium || RefCounted::refs(ml_.data_) == 1) { + shrinkMedium(delta); + } else { + shrinkLarge(delta); + } + } + + FOLLY_NOINLINE + void reserve(size_t minCapacity, bool disableSSO = FBSTRING_DISABLE_SSO) { + switch (category()) { + case Category::isSmall: + reserveSmall(minCapacity, disableSSO); + break; + case Category::isMedium: + reserveMedium(minCapacity); + break; + case Category::isLarge: + reserveLarge(minCapacity); + break; + default: + folly::assume_unreachable(); + } + assert(capacity() >= minCapacity); + } + + Char* expandNoinit( + const size_t delta, + bool expGrowth = false, + bool disableSSO = FBSTRING_DISABLE_SSO); + + void push_back(Char c) { + *expandNoinit(1, /* expGrowth = */ true) = c; + } + + size_t size() const { + size_t ret = ml_.size_; + if /* constexpr */ (kIsLittleEndian) { + // We can save a couple instructions, because the category is + // small iff the last char, as unsigned, is <= maxSmallSize. + typedef typename std::make_unsigned<Char>::type UChar; + auto maybeSmallSize = size_t(maxSmallSize) - + size_t(static_cast<UChar>(small_[maxSmallSize])); + // With this syntax, GCC and Clang generate a CMOV instead of a branch. + ret = (static_cast<ssize_t>(maybeSmallSize) >= 0) ? maybeSmallSize : ret; + } else { + ret = (category() == Category::isSmall) ? smallSize() : ret; + } + return ret; + } + + size_t capacity() const { + switch (category()) { + case Category::isSmall: + return maxSmallSize; + case Category::isLarge: + // For large-sized strings, a multi-referenced chunk has no + // available capacity. This is because any attempt to append + // data would trigger a new allocation. + if (RefCounted::refs(ml_.data_) > 1) { + return ml_.size_; + } + break; + case Category::isMedium: + default: + break; + } + return ml_.capacity(); + } + + bool isShared() const { + return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1; + } + + private: + Char* c_str() { + Char* ptr = ml_.data_; + // With this syntax, GCC and Clang generate a CMOV instead of a branch. + ptr = (category() == Category::isSmall) ? small_ : ptr; + return ptr; + } + + void reset() { + setSmallSize(0); + } + + FOLLY_NOINLINE void destroyMediumLarge() noexcept { + auto const c = category(); + assert(c != Category::isSmall); + if (c == Category::isMedium) { + free(ml_.data_); + } else { + RefCounted::decrementRefs(ml_.data_); + } + } + + struct RefCounted { + std::atomic<size_t> refCount_; + Char data_[1]; + + constexpr static size_t getDataOffset() { + return offsetof(RefCounted, data_); + } + + static RefCounted* fromData(Char* p) { + return static_cast<RefCounted*>(static_cast<void*>( + static_cast<unsigned char*>(static_cast<void*>(p)) - + getDataOffset())); + } + + static size_t refs(Char* p) { + return fromData(p)->refCount_.load(std::memory_order_acquire); + } + + static void incrementRefs(Char* p) { + fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel); + } + + static void decrementRefs(Char* p) { + auto const dis = fromData(p); + size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel); + assert(oldcnt > 0); + if (oldcnt == 1) { + free(dis); + } + } + + static RefCounted* create(size_t* size) { + const size_t allocSize = + goodMallocSize(getDataOffset() + (*size + 1) * sizeof(Char)); + auto result = static_cast<RefCounted*>(checkedMalloc(allocSize)); + result->refCount_.store(1, std::memory_order_release); + *size = (allocSize - getDataOffset()) / sizeof(Char) - 1; + return result; + } + + static RefCounted* create(const Char* data, size_t* size) { + const size_t effectiveSize = *size; + auto result = create(size); + if (FOLLY_LIKELY(effectiveSize > 0)) { + fbstring_detail::podCopy(data, data + effectiveSize, result->data_); + } + return result; + } + + static RefCounted* reallocate( + Char* const data, + const size_t currentSize, + const size_t currentCapacity, + size_t* newCapacity) { + assert(*newCapacity > 0 && *newCapacity > currentSize); + const size_t allocNewCapacity = + goodMallocSize(getDataOffset() + (*newCapacity + 1) * sizeof(Char)); + auto const dis = fromData(data); + assert(dis->refCount_.load(std::memory_order_acquire) == 1); + auto result = static_cast<RefCounted*>(smartRealloc( + dis, + getDataOffset() + (currentSize + 1) * sizeof(Char), + getDataOffset() + (currentCapacity + 1) * sizeof(Char), + allocNewCapacity)); + assert(result->refCount_.load(std::memory_order_acquire) == 1); + *newCapacity = (allocNewCapacity - getDataOffset()) / sizeof(Char) - 1; + return result; + } + }; + + typedef uint8_t category_type; + + enum class Category : category_type { + isSmall = 0, + isMedium = kIsLittleEndian ? 0x80 : 0x2, + isLarge = kIsLittleEndian ? 0x40 : 0x1, + }; + + Category category() const { + // works for both big-endian and little-endian + return static_cast<Category>(bytes_[lastChar] & categoryExtractMask); + } + + struct MediumLarge { + Char* data_; + size_t size_; + size_t capacity_; + + size_t capacity() const { + return kIsLittleEndian ? capacity_ & capacityExtractMask : capacity_ >> 2; + } + + void setCapacity(size_t cap, Category cat) { + capacity_ = kIsLittleEndian + ? cap | (static_cast<size_t>(cat) << kCategoryShift) + : (cap << 2) | static_cast<size_t>(cat); + } + }; + + union { + uint8_t bytes_[sizeof(MediumLarge)]; // For accessing the last byte. + Char small_[sizeof(MediumLarge) / sizeof(Char)]; + MediumLarge ml_; + }; + + constexpr static size_t lastChar = sizeof(MediumLarge) - 1; + constexpr static size_t maxSmallSize = lastChar / sizeof(Char); + constexpr static size_t maxMediumSize = 254 / sizeof(Char); + constexpr static uint8_t categoryExtractMask = kIsLittleEndian ? 0xC0 : 0x3; + constexpr static size_t kCategoryShift = (sizeof(size_t) - 1) * 8; + constexpr static size_t capacityExtractMask = kIsLittleEndian + ? ~(size_t(categoryExtractMask) << kCategoryShift) + : 0x0 /* unused */; + + static_assert( + !(sizeof(MediumLarge) % sizeof(Char)), + "Corrupt memory layout for fbstring."); + + size_t smallSize() const { + assert(category() == Category::isSmall); + constexpr auto shift = kIsLittleEndian ? 0 : 2; + auto smallShifted = static_cast<size_t>(small_[maxSmallSize]) >> shift; + assert(static_cast<size_t>(maxSmallSize) >= smallShifted); + return static_cast<size_t>(maxSmallSize) - smallShifted; + } + + void setSmallSize(size_t s) { + // Warning: this should work with uninitialized strings too, + // so don't assume anything about the previous value of + // small_[maxSmallSize]. + assert(s <= maxSmallSize); + constexpr auto shift = kIsLittleEndian ? 0 : 2; + small_[maxSmallSize] = char((maxSmallSize - s) << shift); + small_[s] = '\0'; + assert(category() == Category::isSmall && size() == s); + } + + void copySmall(const fbstring_core&); + void copyMedium(const fbstring_core&); + void copyLarge(const fbstring_core&); + + void initSmall(const Char* data, size_t size); + void initMedium(const Char* data, size_t size); + void initLarge(const Char* data, size_t size); + + void reserveSmall(size_t minCapacity, bool disableSSO); + void reserveMedium(size_t minCapacity); + void reserveLarge(size_t minCapacity); + + void shrinkSmall(size_t delta); + void shrinkMedium(size_t delta); + void shrinkLarge(size_t delta); + + void unshare(size_t minCapacity = 0); + Char* mutableDataLarge(); +}; + +template <class Char> +inline void fbstring_core<Char>::copySmall(const fbstring_core& rhs) { + static_assert(offsetof(MediumLarge, data_) == 0, "fbstring layout failure"); + static_assert( + offsetof(MediumLarge, size_) == sizeof(ml_.data_), + "fbstring layout failure"); + static_assert( + offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_), + "fbstring layout failure"); + // Just write the whole thing, don't look at details. In + // particular we need to copy capacity anyway because we want + // to set the size (don't forget that the last character, + // which stores a short string's length, is shared with the + // ml_.capacity field). + ml_ = rhs.ml_; + assert(category() == Category::isSmall && this->size() == rhs.size()); +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::copyMedium( + const fbstring_core& rhs) { + // Medium strings are copied eagerly. Don't forget to allocate + // one extra Char for the null terminator. + auto const allocSize = goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char)); + ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize)); + // Also copies terminator. + fbstring_detail::podCopy( + rhs.ml_.data_, rhs.ml_.data_ + rhs.ml_.size_ + 1, ml_.data_); + ml_.size_ = rhs.ml_.size_; + ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium); + assert(category() == Category::isMedium); +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::copyLarge( + const fbstring_core& rhs) { + // Large strings are just refcounted + ml_ = rhs.ml_; + RefCounted::incrementRefs(ml_.data_); + assert(category() == Category::isLarge && size() == rhs.size()); +} + +// Small strings are bitblitted +template <class Char> +inline void fbstring_core<Char>::initSmall( + const Char* const data, + const size_t size) { + // Layout is: Char* data_, size_t size_, size_t capacity_ + static_assert( + sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t), + "fbstring has unexpected size"); + static_assert( + sizeof(Char*) == sizeof(size_t), "fbstring size assumption violation"); + // sizeof(size_t) must be a power of 2 + static_assert( + (sizeof(size_t) & (sizeof(size_t) - 1)) == 0, + "fbstring size assumption violation"); + +// If data is aligned, use fast word-wise copying. Otherwise, +// use conservative memcpy. +// The word-wise path reads bytes which are outside the range of +// the string, and makes ASan unhappy, so we disable it when +// compiling with ASan. +#ifndef FOLLY_SANITIZE_ADDRESS + if ((reinterpret_cast<size_t>(data) & (sizeof(size_t) - 1)) == 0) { + const size_t byteSize = size * sizeof(Char); + constexpr size_t wordWidth = sizeof(size_t); + switch ((byteSize + wordWidth - 1) / wordWidth) { // Number of words. + case 3: + ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2]; + FOLLY_FALLTHROUGH; + case 2: + ml_.size_ = reinterpret_cast<const size_t*>(data)[1]; + FOLLY_FALLTHROUGH; + case 1: + ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data)); + FOLLY_FALLTHROUGH; + case 0: + break; + } + } else +#endif + { + if (size != 0) { + fbstring_detail::podCopy(data, data + size, small_); + } + } + setSmallSize(size); +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::initMedium( + const Char* const data, + const size_t size) { + // Medium strings are allocated normally. Don't forget to + // allocate one extra Char for the terminating null. + auto const allocSize = goodMallocSize((1 + size) * sizeof(Char)); + ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize)); + if (FOLLY_LIKELY(size > 0)) { + fbstring_detail::podCopy(data, data + size, ml_.data_); + } + ml_.size_ = size; + ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium); + ml_.data_[size] = '\0'; +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::initLarge( + const Char* const data, + const size_t size) { + // Large strings are allocated differently + size_t effectiveCapacity = size; + auto const newRC = RefCounted::create(data, &effectiveCapacity); + ml_.data_ = newRC->data_; + ml_.size_ = size; + ml_.setCapacity(effectiveCapacity, Category::isLarge); + ml_.data_[size] = '\0'; +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::unshare(size_t minCapacity) { + assert(category() == Category::isLarge); + size_t effectiveCapacity = std::max(minCapacity, ml_.capacity()); + auto const newRC = RefCounted::create(&effectiveCapacity); + // If this fails, someone placed the wrong capacity in an + // fbstring. + assert(effectiveCapacity >= ml_.capacity()); + // Also copies terminator. + fbstring_detail::podCopy(ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_); + RefCounted::decrementRefs(ml_.data_); + ml_.data_ = newRC->data_; + ml_.setCapacity(effectiveCapacity, Category::isLarge); + // size_ remains unchanged. +} + +template <class Char> +inline Char* fbstring_core<Char>::mutableDataLarge() { + assert(category() == Category::isLarge); + if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique. + unshare(); + } + return ml_.data_; +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::reserveLarge( + size_t minCapacity) { + assert(category() == Category::isLarge); + if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique + // We must make it unique regardless; in-place reallocation is + // useless if the string is shared. In order to not surprise + // people, reserve the new block at current capacity or + // more. That way, a string's capacity never shrinks after a + // call to reserve. + unshare(minCapacity); + } else { + // String is not shared, so let's try to realloc (if needed) + if (minCapacity > ml_.capacity()) { + // Asking for more memory + auto const newRC = RefCounted::reallocate( + ml_.data_, ml_.size_, ml_.capacity(), &minCapacity); + ml_.data_ = newRC->data_; + ml_.setCapacity(minCapacity, Category::isLarge); + } + assert(capacity() >= minCapacity); + } +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::reserveMedium( + const size_t minCapacity) { + assert(category() == Category::isMedium); + // String is not shared + if (minCapacity <= ml_.capacity()) { + return; // nothing to do, there's enough room + } + if (minCapacity <= maxMediumSize) { + // Keep the string at medium size. Don't forget to allocate + // one extra Char for the terminating null. + size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char)); + // Also copies terminator. + ml_.data_ = static_cast<Char*>(smartRealloc( + ml_.data_, + (ml_.size_ + 1) * sizeof(Char), + (ml_.capacity() + 1) * sizeof(Char), + capacityBytes)); + ml_.setCapacity(capacityBytes / sizeof(Char) - 1, Category::isMedium); + } else { + // Conversion from medium to large string + fbstring_core nascent; + // Will recurse to another branch of this function + nascent.reserve(minCapacity); + nascent.ml_.size_ = ml_.size_; + // Also copies terminator. + fbstring_detail::podCopy( + ml_.data_, ml_.data_ + ml_.size_ + 1, nascent.ml_.data_); + nascent.swap(*this); + assert(capacity() >= minCapacity); + } +} + +template <class Char> +FOLLY_NOINLINE inline void fbstring_core<Char>::reserveSmall( + size_t minCapacity, + const bool disableSSO) { + assert(category() == Category::isSmall); + if (!disableSSO && minCapacity <= maxSmallSize) { + // small + // Nothing to do, everything stays put + } else if (minCapacity <= maxMediumSize) { + // medium + // Don't forget to allocate one extra Char for the terminating null + auto const allocSizeBytes = + goodMallocSize((1 + minCapacity) * sizeof(Char)); + auto const pData = static_cast<Char*>(checkedMalloc(allocSizeBytes)); + auto const size = smallSize(); + // Also copies terminator. + fbstring_detail::podCopy(small_, small_ + size + 1, pData); + ml_.data_ = pData; + ml_.size_ = size; + ml_.setCapacity(allocSizeBytes / sizeof(Char) - 1, Category::isMedium); + } else { + // large + auto const newRC = RefCounted::create(&minCapacity); + auto const size = smallSize(); + // Also copies terminator. + fbstring_detail::podCopy(small_, small_ + size + 1, newRC->data_); + ml_.data_ = newRC->data_; + ml_.size_ = size; + ml_.setCapacity(minCapacity, Category::isLarge); + assert(capacity() >= minCapacity); + } +} + +template <class Char> +inline Char* fbstring_core<Char>::expandNoinit( + const size_t delta, + bool expGrowth, /* = false */ + bool disableSSO /* = FBSTRING_DISABLE_SSO */) { + // Strategy is simple: make room, then change size + assert(capacity() >= size()); + size_t sz, newSz; + if (category() == Category::isSmall) { + sz = smallSize(); + newSz = sz + delta; + if (!disableSSO && FOLLY_LIKELY(newSz <= maxSmallSize)) { + setSmallSize(newSz); + return small_ + sz; + } + reserveSmall( + expGrowth ? std::max(newSz, 2 * maxSmallSize) : newSz, disableSSO); + } else { + sz = ml_.size_; + newSz = sz + delta; + if (FOLLY_UNLIKELY(newSz > capacity())) { + // ensures not shared + reserve(expGrowth ? std::max(newSz, 1 + capacity() * 3 / 2) : newSz); + } + } + assert(capacity() >= newSz); + // Category can't be small - we took care of that above + assert(category() == Category::isMedium || category() == Category::isLarge); + ml_.size_ = newSz; + ml_.data_[newSz] = '\0'; + assert(size() == newSz); + return ml_.data_ + sz; +} + +template <class Char> +inline void fbstring_core<Char>::shrinkSmall(const size_t delta) { + // Check for underflow + assert(delta <= smallSize()); + setSmallSize(smallSize() - delta); +} + +template <class Char> +inline void fbstring_core<Char>::shrinkMedium(const size_t delta) { + // Medium strings and unique large strings need no special + // handling. + assert(ml_.size_ >= delta); + ml_.size_ -= delta; + ml_.data_[ml_.size_] = '\0'; +} + +template <class Char> +inline void fbstring_core<Char>::shrinkLarge(const size_t delta) { + assert(ml_.size_ >= delta); + // Shared large string, must make unique. This is because of the + // durn terminator must be written, which may trample the shared + // data. + if (delta) { + fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this); + } + // No need to write the terminator. +} + +/** + * Dummy fbstring core that uses an actual std::string. This doesn't + * make any sense - it's just for testing purposes. + */ +template <class Char> +class dummy_fbstring_core { + public: + dummy_fbstring_core() {} + dummy_fbstring_core(const dummy_fbstring_core& another) + : backend_(another.backend_) {} + dummy_fbstring_core(const Char* s, size_t n) : backend_(s, n) {} + void swap(dummy_fbstring_core& rhs) { + backend_.swap(rhs.backend_); + } + const Char* data() const { + return backend_.data(); + } + Char* mutableData() { + return const_cast<Char*>(backend_.data()); + } + void shrink(size_t delta) { + assert(delta <= size()); + backend_.resize(size() - delta); + } + Char* expandNoinit(size_t delta) { + auto const sz = size(); + backend_.resize(size() + delta); + return backend_.data() + sz; + } + void push_back(Char c) { + backend_.push_back(c); + } + size_t size() const { + return backend_.size(); + } + size_t capacity() const { + return backend_.capacity(); + } + bool isShared() const { + return false; + } + void reserve(size_t minCapacity) { + backend_.reserve(minCapacity); + } + + private: + std::basic_string<Char> backend_; +}; + +/** + * This is the basic_string replacement. For conformity, + * basic_fbstring takes the same template parameters, plus the last + * one which is the core. + */ +template < + typename E, + class T = std::char_traits<E>, + class A = std::allocator<E>, + class Storage = fbstring_core<E>> +class basic_fbstring { + template <typename Ex, typename... Args> + FOLLY_ALWAYS_INLINE static void enforce(bool condition, Args&&... args) { + if (!condition) { + throw_exception<Ex>(static_cast<Args&&>(args)...); + } + } + + bool isSane() const { + return begin() <= end() && empty() == (size() == 0) && + empty() == (begin() == end()) && size() <= max_size() && + capacity() <= max_size() && size() <= capacity() && + begin()[size()] == '\0'; + } + + struct Invariant { + Invariant& operator=(const Invariant&) = delete; + explicit Invariant(const basic_fbstring& s) noexcept : s_(s) { + assert(s_.isSane()); + } + ~Invariant() noexcept { + assert(s_.isSane()); + } + + private: + const basic_fbstring& s_; + }; + + public: + // types + typedef T traits_type; + typedef typename traits_type::char_type value_type; + typedef A allocator_type; + typedef typename std::allocator_traits<A>::size_type size_type; + typedef typename std::allocator_traits<A>::difference_type difference_type; + + typedef typename std::allocator_traits<A>::value_type& reference; + typedef typename std::allocator_traits<A>::value_type const& const_reference; + typedef typename std::allocator_traits<A>::pointer pointer; + typedef typename std::allocator_traits<A>::const_pointer const_pointer; + + typedef E* iterator; + typedef const E* const_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + static constexpr size_type npos = size_type(-1); + typedef std::true_type IsRelocatable; + + private: + static void procrustes(size_type& n, size_type nmax) { + if (n > nmax) { + n = nmax; + } + } + + static size_type traitsLength(const value_type* s); + + public: + // C++11 21.4.2 construct/copy/destroy + + // Note: while the following two constructors can be (and previously were) + // collapsed into one constructor written this way: + // + // explicit basic_fbstring(const A& a = A()) noexcept { } + // + // This can cause Clang (at least version 3.7) to fail with the error: + // "chosen constructor is explicit in copy-initialization ... + // in implicit initialization of field '(x)' with omitted initializer" + // + // if used in a struct which is default-initialized. Hence the split into + // these two separate constructors. + + basic_fbstring() noexcept : basic_fbstring(A()) {} + + explicit basic_fbstring(const A&) noexcept {} + + basic_fbstring(const basic_fbstring& str) : store_(str.store_) {} + + // Move constructor + basic_fbstring(basic_fbstring&& goner) noexcept + : store_(std::move(goner.store_)) {} + + // This is defined for compatibility with std::string + template <typename A2> + /* implicit */ basic_fbstring(const std::basic_string<E, T, A2>& str) + : store_(str.data(), str.size()) {} + + basic_fbstring( + const basic_fbstring& str, + size_type pos, + size_type n = npos, + const A& /* a */ = A()) { + assign(str, pos, n); + } + + FOLLY_NOINLINE + /* implicit */ basic_fbstring(const value_type* s, const A& /*a*/ = A()) + : store_(s, traitsLength(s)) {} + + FOLLY_NOINLINE + basic_fbstring(const value_type* s, size_type n, const A& /*a*/ = A()) + : store_(s, n) {} + + FOLLY_NOINLINE + basic_fbstring(size_type n, value_type c, const A& /*a*/ = A()) { + auto const pData = store_.expandNoinit(n); + fbstring_detail::podFill(pData, pData + n, c); + } + + template <class InIt> + FOLLY_NOINLINE basic_fbstring( + InIt begin, + InIt end, + typename std::enable_if< + !std::is_same<InIt, value_type*>::value, + const A>::type& /*a*/ + = A()) { + assign(begin, end); + } + + // Specialization for const char*, const char* + FOLLY_NOINLINE + basic_fbstring(const value_type* b, const value_type* e, const A& /*a*/ = A()) + : store_(b, size_type(e - b)) {} + + // Nonstandard constructor + basic_fbstring( + value_type* s, + size_type n, + size_type c, + AcquireMallocatedString a) + : store_(s, n, c, a) {} + + // Construction from initialization list + FOLLY_NOINLINE + basic_fbstring(std::initializer_list<value_type> il) { + assign(il.begin(), il.end()); + } + + ~basic_fbstring() noexcept {} + + basic_fbstring& operator=(const basic_fbstring& lhs); + + // Move assignment + basic_fbstring& operator=(basic_fbstring&& goner) noexcept; + + // Compatibility with std::string + template <typename A2> + basic_fbstring& operator=(const std::basic_string<E, T, A2>& rhs) { + return assign(rhs.data(), rhs.size()); + } + + // Compatibility with std::string + std::basic_string<E, T, A> toStdString() const { + return std::basic_string<E, T, A>(data(), size()); + } + + basic_fbstring& operator=(const value_type* s) { + return assign(s); + } + + basic_fbstring& operator=(value_type c); + + // This actually goes directly against the C++ spec, but the + // value_type overload is dangerous, so we're explicitly deleting + // any overloads of operator= that could implicitly convert to + // value_type. + // Note that we do need to explicitly specify the template types because + // otherwise MSVC 2017 will aggressively pre-resolve value_type to + // traits_type::char_type, which won't compare as equal when determining + // which overload the implementation is referring to. + template <typename TP> + typename std::enable_if< + std::is_convertible< + TP, + typename basic_fbstring<E, T, A, Storage>::value_type>::value && + !std::is_same< + typename std::decay<TP>::type, + typename basic_fbstring<E, T, A, Storage>::value_type>::value, + basic_fbstring<E, T, A, Storage>&>::type + operator=(TP c) = delete; + + basic_fbstring& operator=(std::initializer_list<value_type> il) { + return assign(il.begin(), il.end()); + } + +#if FOLLY_HAS_STRING_VIEW + operator std::basic_string_view<value_type, traits_type>() const noexcept { + return {data(), size()}; + } +#endif + + // C++11 21.4.3 iterators: + iterator begin() { + return store_.mutableData(); + } + + const_iterator begin() const { + return store_.data(); + } + + const_iterator cbegin() const { + return begin(); + } + + iterator end() { + return store_.mutableData() + store_.size(); + } + + const_iterator end() const { + return store_.data() + store_.size(); + } + + const_iterator cend() const { + return end(); + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const { + return rbegin(); + } + + reverse_iterator rend() { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const { + return rend(); + } + + // Added by C++11 + // C++11 21.4.5, element access: + const value_type& front() const { + return *begin(); + } + const value_type& back() const { + assert(!empty()); + // Should be begin()[size() - 1], but that branches twice + return *(end() - 1); + } + value_type& front() { + return *begin(); + } + value_type& back() { + assert(!empty()); + // Should be begin()[size() - 1], but that branches twice + return *(end() - 1); + } + void pop_back() { + assert(!empty()); + store_.shrink(1); + } + + // C++11 21.4.4 capacity: + size_type size() const { + return store_.size(); + } + + size_type length() const { + return size(); + } + + size_type max_size() const { + return std::numeric_limits<size_type>::max(); + } + + void resize(size_type n, value_type c = value_type()); + + size_type capacity() const { + return store_.capacity(); + } + + void reserve(size_type res_arg = 0) { + enforce<std::length_error>(res_arg <= max_size(), ""); + store_.reserve(res_arg); + } + + void shrink_to_fit() { + // Shrink only if slack memory is sufficiently large + if (capacity() < size() * 3 / 2) { + return; + } + basic_fbstring(cbegin(), cend()).swap(*this); + } + + void clear() { + resize(0); + } + + bool empty() const { + return size() == 0; + } + + // C++11 21.4.5 element access: + const_reference operator[](size_type pos) const { + return *(begin() + pos); + } + + reference operator[](size_type pos) { + return *(begin() + pos); + } + + const_reference at(size_type n) const { + enforce<std::out_of_range>(n < size(), ""); + return (*this)[n]; + } + + reference at(size_type n) { + enforce<std::out_of_range>(n < size(), ""); + return (*this)[n]; + } + + // C++11 21.4.6 modifiers: + basic_fbstring& operator+=(const basic_fbstring& str) { + return append(str); + } + + basic_fbstring& operator+=(const value_type* s) { + return append(s); + } + + basic_fbstring& operator+=(const value_type c) { + push_back(c); + return *this; + } + + basic_fbstring& operator+=(std::initializer_list<value_type> il) { + append(il); + return *this; + } + + basic_fbstring& append(const basic_fbstring& str); + + basic_fbstring& + append(const basic_fbstring& str, const size_type pos, size_type n); + + basic_fbstring& append(const value_type* s, size_type n); + + basic_fbstring& append(const value_type* s) { + return append(s, traitsLength(s)); + } + + basic_fbstring& append(size_type n, value_type c); + + template <class InputIterator> + basic_fbstring& append(InputIterator first, InputIterator last) { + insert(end(), first, last); + return *this; + } + + basic_fbstring& append(std::initializer_list<value_type> il) { + return append(il.begin(), il.end()); + } + + void push_back(const value_type c) { // primitive + store_.push_back(c); + } + + basic_fbstring& assign(const basic_fbstring& str) { + if (&str == this) { + return *this; + } + return assign(str.data(), str.size()); + } + + basic_fbstring& assign(basic_fbstring&& str) { + return *this = std::move(str); + } + + basic_fbstring& + assign(const basic_fbstring& str, const size_type pos, size_type n); + + basic_fbstring& assign(const value_type* s, const size_type n); + + basic_fbstring& assign(const value_type* s) { + return assign(s, traitsLength(s)); + } + + basic_fbstring& assign(std::initializer_list<value_type> il) { + return assign(il.begin(), il.end()); + } + + template <class ItOrLength, class ItOrChar> + basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) { + return replace(begin(), end(), first_or_n, last_or_c); + } + + basic_fbstring& insert(size_type pos1, const basic_fbstring& str) { + return insert(pos1, str.data(), str.size()); + } + + basic_fbstring& insert( + size_type pos1, + const basic_fbstring& str, + size_type pos2, + size_type n) { + enforce<std::out_of_range>(pos2 <= str.length(), ""); + procrustes(n, str.length() - pos2); + return insert(pos1, str.data() + pos2, n); + } + + basic_fbstring& insert(size_type pos, const value_type* s, size_type n) { + enforce<std::out_of_range>(pos <= length(), ""); + insert(begin() + pos, s, s + n); + return *this; + } + + basic_fbstring& insert(size_type pos, const value_type* s) { + return insert(pos, s, traitsLength(s)); + } + + basic_fbstring& insert(size_type pos, size_type n, value_type c) { + enforce<std::out_of_range>(pos <= length(), ""); + insert(begin() + pos, n, c); + return *this; + } + + iterator insert(const_iterator p, const value_type c) { + const size_type pos = p - cbegin(); + insert(p, 1, c); + return begin() + pos; + } + + private: + typedef std::basic_istream<value_type, traits_type> istream_type; + istream_type& getlineImpl(istream_type& is, value_type delim); + + public: + friend inline istream_type& + getline(istream_type& is, basic_fbstring& str, value_type delim) { + return str.getlineImpl(is, delim); + } + + friend inline istream_type& getline(istream_type& is, basic_fbstring& str) { + return getline(is, str, '\n'); + } + + private: + iterator + insertImplDiscr(const_iterator i, size_type n, value_type c, std::true_type); + + template <class InputIter> + iterator + insertImplDiscr(const_iterator i, InputIter b, InputIter e, std::false_type); + + template <class FwdIterator> + iterator insertImpl( + const_iterator i, + FwdIterator s1, + FwdIterator s2, + std::forward_iterator_tag); + + template <class InputIterator> + iterator insertImpl( + const_iterator i, + InputIterator b, + InputIterator e, + std::input_iterator_tag); + + public: + template <class ItOrLength, class ItOrChar> + iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) { + using Sel = bool_constant<std::numeric_limits<ItOrLength>::is_specialized>; + return insertImplDiscr(p, first_or_n, last_or_c, Sel()); + } + + iterator insert(const_iterator p, std::initializer_list<value_type> il) { + return insert(p, il.begin(), il.end()); + } + + basic_fbstring& erase(size_type pos = 0, size_type n = npos) { + Invariant checker(*this); + + enforce<std::out_of_range>(pos <= length(), ""); + procrustes(n, length() - pos); + std::copy(begin() + pos + n, end(), begin() + pos); + resize(length() - n); + return *this; + } + + iterator erase(iterator position) { + const size_type pos(position - begin()); + enforce<std::out_of_range>(pos <= size(), ""); + erase(pos, 1); + return begin() + pos; + } + + iterator erase(iterator first, iterator last) { + const size_type pos(first - begin()); + erase(pos, last - first); + return begin() + pos; + } + + // Replaces at most n1 chars of *this, starting with pos1 with the + // content of str + basic_fbstring& + replace(size_type pos1, size_type n1, const basic_fbstring& str) { + return replace(pos1, n1, str.data(), str.size()); + } + + // Replaces at most n1 chars of *this, starting with pos1, + // with at most n2 chars of str starting with pos2 + basic_fbstring& replace( + size_type pos1, + size_type n1, + const basic_fbstring& str, + size_type pos2, + size_type n2) { + enforce<std::out_of_range>(pos2 <= str.length(), ""); + return replace( + pos1, n1, str.data() + pos2, std::min(n2, str.size() - pos2)); + } + + // Replaces at most n1 chars of *this, starting with pos, with chars from s + basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) { + return replace(pos, n1, s, traitsLength(s)); + } + + // Replaces at most n1 chars of *this, starting with pos, with n2 + // occurrences of c + // + // consolidated with + // + // Replaces at most n1 chars of *this, starting with pos, with at + // most n2 chars of str. str must have at least n2 chars. + template <class StrOrLength, class NumOrChar> + basic_fbstring& + replace(size_type pos, size_type n1, StrOrLength s_or_n2, NumOrChar n_or_c) { + Invariant checker(*this); + + enforce<std::out_of_range>(pos <= size(), ""); + procrustes(n1, length() - pos); + const iterator b = begin() + pos; + return replace(b, b + n1, s_or_n2, n_or_c); + } + + basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) { + return replace(i1, i2, str.data(), str.length()); + } + + basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) { + return replace(i1, i2, s, traitsLength(s)); + } + + private: + basic_fbstring& replaceImplDiscr( + iterator i1, + iterator i2, + const value_type* s, + size_type n, + std::integral_constant<int, 2>); + + basic_fbstring& replaceImplDiscr( + iterator i1, + iterator i2, + size_type n2, + value_type c, + std::integral_constant<int, 1>); + + template <class InputIter> + basic_fbstring& replaceImplDiscr( + iterator i1, + iterator i2, + InputIter b, + InputIter e, + std::integral_constant<int, 0>); + + private: + template <class FwdIterator> + bool replaceAliased( + iterator /* i1 */, + iterator /* i2 */, + FwdIterator /* s1 */, + FwdIterator /* s2 */, + std::false_type) { + return false; + } + + template <class FwdIterator> + bool replaceAliased( + iterator i1, + iterator i2, + FwdIterator s1, + FwdIterator s2, + std::true_type); + + template <class FwdIterator> + void replaceImpl( + iterator i1, + iterator i2, + FwdIterator s1, + FwdIterator s2, + std::forward_iterator_tag); + + template <class InputIterator> + void replaceImpl( + iterator i1, + iterator i2, + InputIterator b, + InputIterator e, + std::input_iterator_tag); + + public: + template <class T1, class T2> + basic_fbstring& + replace(iterator i1, iterator i2, T1 first_or_n_or_s, T2 last_or_c_or_n) { + constexpr bool num1 = std::numeric_limits<T1>::is_specialized, + num2 = std::numeric_limits<T2>::is_specialized; + using Sel = + std::integral_constant<int, num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>; + return replaceImplDiscr(i1, i2, first_or_n_or_s, last_or_c_or_n, Sel()); + } + + size_type copy(value_type* s, size_type n, size_type pos = 0) const { + enforce<std::out_of_range>(pos <= size(), ""); + procrustes(n, size() - pos); + + if (n != 0) { + fbstring_detail::podCopy(data() + pos, data() + pos + n, s); + } + return n; + } + + void swap(basic_fbstring& rhs) { + store_.swap(rhs.store_); + } + + const value_type* c_str() const { + return store_.c_str(); + } + + const value_type* data() const { + return c_str(); + } + + value_type* data() { + return store_.data(); + } + + allocator_type get_allocator() const { + return allocator_type(); + } + + size_type find(const basic_fbstring& str, size_type pos = 0) const { + return find(str.data(), pos, str.length()); + } + + size_type find(const value_type* needle, size_type pos, size_type nsize) + const; + + size_type find(const value_type* s, size_type pos = 0) const { + return find(s, pos, traitsLength(s)); + } + + size_type find(value_type c, size_type pos = 0) const { + return find(&c, pos, 1); + } + + size_type rfind(const basic_fbstring& str, size_type pos = npos) const { + return rfind(str.data(), pos, str.length()); + } + + size_type rfind(const value_type* s, size_type pos, size_type n) const; + + size_type rfind(const value_type* s, size_type pos = npos) const { + return rfind(s, pos, traitsLength(s)); + } + + size_type rfind(value_type c, size_type pos = npos) const { + return rfind(&c, pos, 1); + } + + size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const { + return find_first_of(str.data(), pos, str.length()); + } + + size_type find_first_of(const value_type* s, size_type pos, size_type n) + const; + + size_type find_first_of(const value_type* s, size_type pos = 0) const { + return find_first_of(s, pos, traitsLength(s)); + } + + size_type find_first_of(value_type c, size_type pos = 0) const { + return find_first_of(&c, pos, 1); + } + + size_type find_last_of(const basic_fbstring& str, size_type pos = npos) + const { + return find_last_of(str.data(), pos, str.length()); + } + + size_type find_last_of(const value_type* s, size_type pos, size_type n) const; + + size_type find_last_of(const value_type* s, size_type pos = npos) const { + return find_last_of(s, pos, traitsLength(s)); + } + + size_type find_last_of(value_type c, size_type pos = npos) const { + return find_last_of(&c, pos, 1); + } + + size_type find_first_not_of(const basic_fbstring& str, size_type pos = 0) + const { + return find_first_not_of(str.data(), pos, str.size()); + } + + size_type find_first_not_of(const value_type* s, size_type pos, size_type n) + const; + + size_type find_first_not_of(const value_type* s, size_type pos = 0) const { + return find_first_not_of(s, pos, traitsLength(s)); + } + + size_type find_first_not_of(value_type c, size_type pos = 0) const { + return find_first_not_of(&c, pos, 1); + } + + size_type find_last_not_of(const basic_fbstring& str, size_type pos = npos) + const { + return find_last_not_of(str.data(), pos, str.length()); + } + + size_type find_last_not_of(const value_type* s, size_type pos, size_type n) + const; + + size_type find_last_not_of(const value_type* s, size_type pos = npos) const { + return find_last_not_of(s, pos, traitsLength(s)); + } + + size_type find_last_not_of(value_type c, size_type pos = npos) const { + return find_last_not_of(&c, pos, 1); + } + + basic_fbstring substr(size_type pos = 0, size_type n = npos) const& { + enforce<std::out_of_range>(pos <= size(), ""); + return basic_fbstring(data() + pos, std::min(n, size() - pos)); + } + + basic_fbstring substr(size_type pos = 0, size_type n = npos) && { + enforce<std::out_of_range>(pos <= size(), ""); + erase(0, pos); + if (n < size()) { + resize(n); + } + return std::move(*this); + } + + int compare(const basic_fbstring& str) const { + // FIX due to Goncalo N M de Carvalho July 18, 2005 + return compare(0, size(), str); + } + + int compare(size_type pos1, size_type n1, const basic_fbstring& str) const { + return compare(pos1, n1, str.data(), str.size()); + } + + int compare(size_type pos1, size_type n1, const value_type* s) const { + return compare(pos1, n1, s, traitsLength(s)); + } + + int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) + const { + enforce<std::out_of_range>(pos1 <= size(), ""); + procrustes(n1, size() - pos1); + // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks! + const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2)); + return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; + } + + int compare( + size_type pos1, + size_type n1, + const basic_fbstring& str, + size_type pos2, + size_type n2) const { + enforce<std::out_of_range>(pos2 <= str.size(), ""); + return compare( + pos1, n1, str.data() + pos2, std::min(n2, str.size() - pos2)); + } + + // Code from Jean-Francois Bastien (03/26/2007) + int compare(const value_type* s) const { + // Could forward to compare(0, size(), s, traitsLength(s)) + // but that does two extra checks + const size_type n1(size()), n2(traitsLength(s)); + const int r = traits_type::compare(data(), s, std::min(n1, n2)); + return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; + } + + private: + // Data + Storage store_; +}; + +template <typename E, class T, class A, class S> +FOLLY_NOINLINE inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::traitsLength(const value_type* s) { + return s ? traits_type::length(s) + : (throw_exception<std::logic_error>( + "basic_fbstring: null pointer initializer not valid"), + 0); +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=( + const basic_fbstring& lhs) { + Invariant checker(*this); + + if (FOLLY_UNLIKELY(&lhs == this)) { + return *this; + } + + return assign(lhs.data(), lhs.size()); +} + +// Move assignment +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=( + basic_fbstring&& goner) noexcept { + if (FOLLY_UNLIKELY(&goner == this)) { + // Compatibility with std::basic_string<>, + // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support. + return *this; + } + // No need of this anymore + this->~basic_fbstring(); + // Move the goner into this + new (&store_) S(std::move(goner.store_)); + return *this; +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=( + value_type c) { + Invariant checker(*this); + + if (empty()) { + store_.expandNoinit(1); + } else if (store_.isShared()) { + basic_fbstring(1, c).swap(*this); + return *this; + } else { + store_.shrink(size() - 1); + } + front() = c; + return *this; +} + +template <typename E, class T, class A, class S> +inline void basic_fbstring<E, T, A, S>::resize( + const size_type n, + const value_type c /*= value_type()*/) { + Invariant checker(*this); + + auto size = this->size(); + if (n <= size) { + store_.shrink(size - n); + } else { + auto const delta = n - size; + auto pData = store_.expandNoinit(delta); + fbstring_detail::podFill(pData, pData + delta, c); + } + assert(this->size() == n); +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append( + const basic_fbstring& str) { +#ifndef NDEBUG + auto desiredSize = size() + str.size(); +#endif + append(str.data(), str.size()); + assert(size() == desiredSize); + return *this; +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append( + const basic_fbstring& str, + const size_type pos, + size_type n) { + const size_type sz = str.size(); + enforce<std::out_of_range>(pos <= sz, ""); + procrustes(n, sz - pos); + return append(str.data() + pos, n); +} + +template <typename E, class T, class A, class S> +FOLLY_NOINLINE inline basic_fbstring<E, T, A, S>& +basic_fbstring<E, T, A, S>::append(const value_type* s, size_type n) { + Invariant checker(*this); + + if (FOLLY_UNLIKELY(!n)) { + // Unlikely but must be done + return *this; + } + auto const oldSize = size(); + auto const oldData = data(); + auto pData = store_.expandNoinit(n, /* expGrowth = */ true); + + // Check for aliasing (rare). We could use "<=" here but in theory + // those do not work for pointers unless the pointers point to + // elements in the same array. For that reason we use + // std::less_equal, which is guaranteed to offer a total order + // over pointers. See discussion at http://goo.gl/Cy2ya for more + // info. + std::less_equal<const value_type*> le; + if (FOLLY_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) { + assert(le(s + n, oldData + oldSize)); + // expandNoinit() could have moved the storage, restore the source. + s = data() + (s - oldData); + fbstring_detail::podMove(s, s + n, pData); + } else { + fbstring_detail::podCopy(s, s + n, pData); + } + + assert(size() == oldSize + n); + return *this; +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append( + size_type n, + value_type c) { + Invariant checker(*this); + auto pData = store_.expandNoinit(n, /* expGrowth = */ true); + fbstring_detail::podFill(pData, pData + n, c); + return *this; +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign( + const basic_fbstring& str, + const size_type pos, + size_type n) { + const size_type sz = str.size(); + enforce<std::out_of_range>(pos <= sz, ""); + procrustes(n, sz - pos); + return assign(str.data() + pos, n); +} + +template <typename E, class T, class A, class S> +FOLLY_NOINLINE inline basic_fbstring<E, T, A, S>& +basic_fbstring<E, T, A, S>::assign(const value_type* s, const size_type n) { + Invariant checker(*this); + + if (n == 0) { + resize(0); + } else if (size() >= n) { + // s can alias this, we need to use podMove. + fbstring_detail::podMove(s, s + n, store_.mutableData()); + store_.shrink(size() - n); + assert(size() == n); + } else { + // If n is larger than size(), s cannot alias this string's + // storage. + resize(0); + // Do not use exponential growth here: assign() should be tight, + // to mirror the behavior of the equivalent constructor. + fbstring_detail::podCopy(s, s + n, store_.expandNoinit(n)); + } + + assert(size() == n); + return *this; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::istream_type& +basic_fbstring<E, T, A, S>::getlineImpl(istream_type& is, value_type delim) { + Invariant checker(*this); + + clear(); + size_t size = 0; + while (true) { + size_t avail = capacity() - size; + // fbstring has 1 byte extra capacity for the null terminator, + // and getline null-terminates the read string. + is.getline(store_.expandNoinit(avail), avail + 1, delim); + size += is.gcount(); + + if (is.bad() || is.eof() || !is.fail()) { + // Done by either failure, end of file, or normal read. + if (!is.bad() && !is.eof()) { + --size; // gcount() also accounts for the delimiter. + } + resize(size); + break; + } + + assert(size == this->size()); + assert(size == capacity()); + // Start at minimum allocation 63 + terminator = 64. + reserve(std::max<size_t>(63, 3 * size / 2)); + // Clear the error so we can continue reading. + is.clear(); + } + return is; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::find( + const value_type* needle, + const size_type pos, + const size_type nsize) const { + auto const size = this->size(); + // nsize + pos can overflow (eg pos == npos), guard against that by checking + // that nsize + pos does not wrap around. + if (nsize + pos > size || nsize + pos < pos) { + return npos; + } + + if (nsize == 0) { + return pos; + } + // Don't use std::search, use a Boyer-Moore-like trick by comparing + // the last characters first + auto const haystack = data(); + auto const nsize_1 = nsize - 1; + auto const lastNeedle = needle[nsize_1]; + + // Boyer-Moore skip value for the last char in the needle. Zero is + // not a valid value; skip will be computed the first time it's + // needed. + size_type skip = 0; + + const E* i = haystack + pos; + auto iEnd = haystack + size - nsize_1; + + while (i < iEnd) { + // Boyer-Moore: match the last element in the needle + while (i[nsize_1] != lastNeedle) { + if (++i == iEnd) { + // not found + return npos; + } + } + // Here we know that the last char matches + // Continue in pedestrian mode + for (size_t j = 0;;) { + assert(j < nsize); + if (i[j] != needle[j]) { + // Not found, we can skip + // Compute the skip value lazily + if (skip == 0) { + skip = 1; + while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) { + ++skip; + } + } + i += skip; + break; + } + // Check if done searching + if (++j == nsize) { + // Yay + return i - haystack; + } + } + } + return npos; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::iterator +basic_fbstring<E, T, A, S>::insertImplDiscr( + const_iterator i, + size_type n, + value_type c, + std::true_type) { + Invariant checker(*this); + + assert(i >= cbegin() && i <= cend()); + const size_type pos = i - cbegin(); + + auto oldSize = size(); + store_.expandNoinit(n, /* expGrowth = */ true); + auto b = begin(); + fbstring_detail::podMove(b + pos, b + oldSize, b + pos + n); + fbstring_detail::podFill(b + pos, b + pos + n, c); + + return b + pos; +} + +template <typename E, class T, class A, class S> +template <class InputIter> +inline typename basic_fbstring<E, T, A, S>::iterator +basic_fbstring<E, T, A, S>::insertImplDiscr( + const_iterator i, + InputIter b, + InputIter e, + std::false_type) { + return insertImpl( + i, b, e, typename std::iterator_traits<InputIter>::iterator_category()); +} + +template <typename E, class T, class A, class S> +template <class FwdIterator> +inline typename basic_fbstring<E, T, A, S>::iterator +basic_fbstring<E, T, A, S>::insertImpl( + const_iterator i, + FwdIterator s1, + FwdIterator s2, + std::forward_iterator_tag) { + Invariant checker(*this); + + assert(i >= cbegin() && i <= cend()); + const size_type pos = i - cbegin(); + auto n = std::distance(s1, s2); + assert(n >= 0); + + auto oldSize = size(); + store_.expandNoinit(n, /* expGrowth = */ true); + auto b = begin(); + fbstring_detail::podMove(b + pos, b + oldSize, b + pos + n); + std::copy(s1, s2, b + pos); + + return b + pos; +} + +template <typename E, class T, class A, class S> +template <class InputIterator> +inline typename basic_fbstring<E, T, A, S>::iterator +basic_fbstring<E, T, A, S>::insertImpl( + const_iterator i, + InputIterator b, + InputIterator e, + std::input_iterator_tag) { + const auto pos = i - cbegin(); + basic_fbstring temp(cbegin(), i); + for (; b != e; ++b) { + temp.push_back(*b); + } + temp.append(i, cend()); + swap(temp); + return begin() + pos; +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr( + iterator i1, + iterator i2, + const value_type* s, + size_type n, + std::integral_constant<int, 2>) { + assert(i1 <= i2); + assert(begin() <= i1 && i1 <= end()); + assert(begin() <= i2 && i2 <= end()); + return replace(i1, i2, s, s + n); +} + +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr( + iterator i1, + iterator i2, + size_type n2, + value_type c, + std::integral_constant<int, 1>) { + const size_type n1 = i2 - i1; + if (n1 > n2) { + std::fill(i1, i1 + n2, c); + erase(i1 + n2, i2); + } else { + std::fill(i1, i2, c); + insert(i2, n2 - n1, c); + } + assert(isSane()); + return *this; +} + +template <typename E, class T, class A, class S> +template <class InputIter> +inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr( + iterator i1, + iterator i2, + InputIter b, + InputIter e, + std::integral_constant<int, 0>) { + using Cat = typename std::iterator_traits<InputIter>::iterator_category; + replaceImpl(i1, i2, b, e, Cat()); + return *this; +} + +template <typename E, class T, class A, class S> +template <class FwdIterator> +inline bool basic_fbstring<E, T, A, S>::replaceAliased( + iterator i1, + iterator i2, + FwdIterator s1, + FwdIterator s2, + std::true_type) { + std::less_equal<const value_type*> le{}; + const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end()); + if (!aliased) { + return false; + } + // Aliased replace, copy to new string + basic_fbstring temp; + temp.reserve(size() - (i2 - i1) + std::distance(s1, s2)); + temp.append(begin(), i1).append(s1, s2).append(i2, end()); + swap(temp); + return true; +} + +template <typename E, class T, class A, class S> +template <class FwdIterator> +inline void basic_fbstring<E, T, A, S>::replaceImpl( + iterator i1, + iterator i2, + FwdIterator s1, + FwdIterator s2, + std::forward_iterator_tag) { + Invariant checker(*this); + + // Handle aliased replace + using Sel = bool_constant< + std::is_same<FwdIterator, iterator>::value || + std::is_same<FwdIterator, const_iterator>::value>; + if (replaceAliased(i1, i2, s1, s2, Sel())) { + return; + } + + auto const n1 = i2 - i1; + assert(n1 >= 0); + auto const n2 = std::distance(s1, s2); + assert(n2 >= 0); + + if (n1 > n2) { + // shrinks + std::copy(s1, s2, i1); + erase(i1 + n2, i2); + } else { + // grows + s1 = fbstring_detail::copy_n(s1, n1, i1).first; + insert(i2, s1, s2); + } + assert(isSane()); +} + +template <typename E, class T, class A, class S> +template <class InputIterator> +inline void basic_fbstring<E, T, A, S>::replaceImpl( + iterator i1, + iterator i2, + InputIterator b, + InputIterator e, + std::input_iterator_tag) { + basic_fbstring temp(begin(), i1); + temp.append(b, e).append(i2, end()); + swap(temp); +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::rfind( + const value_type* s, + size_type pos, + size_type n) const { + if (n > length()) { + return npos; + } + pos = std::min(pos, length() - n); + if (n == 0) { + return pos; + } + + const_iterator i(begin() + pos); + for (;; --i) { + if (traits_type::eq(*i, *s) && traits_type::compare(&*i, s, n) == 0) { + return i - begin(); + } + if (i == begin()) { + break; + } + } + return npos; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::find_first_of( + const value_type* s, + size_type pos, + size_type n) const { + if (pos > length() || n == 0) { + return npos; + } + const_iterator i(begin() + pos), finish(end()); + for (; i != finish; ++i) { + if (traits_type::find(s, n, *i) != nullptr) { + return i - begin(); + } + } + return npos; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::find_last_of( + const value_type* s, + size_type pos, + size_type n) const { + if (!empty() && n > 0) { + pos = std::min(pos, length() - 1); + const_iterator i(begin() + pos); + for (;; --i) { + if (traits_type::find(s, n, *i) != nullptr) { + return i - begin(); + } + if (i == begin()) { + break; + } + } + } + return npos; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::find_first_not_of( + const value_type* s, + size_type pos, + size_type n) const { + if (pos < length()) { + const_iterator i(begin() + pos), finish(end()); + for (; i != finish; ++i) { + if (traits_type::find(s, n, *i) == nullptr) { + return i - begin(); + } + } + } + return npos; +} + +template <typename E, class T, class A, class S> +inline typename basic_fbstring<E, T, A, S>::size_type +basic_fbstring<E, T, A, S>::find_last_not_of( + const value_type* s, + size_type pos, + size_type n) const { + if (!this->empty()) { + pos = std::min(pos, size() - 1); + const_iterator i(begin() + pos); + for (;; --i) { + if (traits_type::find(s, n, *i) == nullptr) { + return i - begin(); + } + if (i == begin()) { + break; + } + } + } + return npos; +} + +// non-member functions +// C++11 21.4.8.1/1 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + basic_fbstring<E, T, A, S> result; + result.reserve(lhs.size() + rhs.size()); + result.append(lhs).append(rhs); + return result; +} + +// C++11 21.4.8.1/2 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + basic_fbstring<E, T, A, S>&& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return std::move(lhs.append(rhs)); +} + +// C++11 21.4.8.1/3 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const basic_fbstring<E, T, A, S>& lhs, + basic_fbstring<E, T, A, S>&& rhs) { + if (rhs.capacity() >= lhs.size() + rhs.size()) { + // Good, at least we don't need to reallocate + return std::move(rhs.insert(0, lhs)); + } + // Meh, no go. Forward to operator+(const&, const&). + auto const& rhsC = rhs; + return lhs + rhsC; +} + +// C++11 21.4.8.1/4 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + basic_fbstring<E, T, A, S>&& lhs, + basic_fbstring<E, T, A, S>&& rhs) { + return std::move(lhs.append(rhs)); +} + +// C++11 21.4.8.1/5 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const E* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + // + basic_fbstring<E, T, A, S> result; + const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs); + result.reserve(len + rhs.size()); + result.append(lhs, len).append(rhs); + return result; +} + +// C++11 21.4.8.1/6 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const E* lhs, + basic_fbstring<E, T, A, S>&& rhs) { + // + const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs); + if (rhs.capacity() >= len + rhs.size()) { + // Good, at least we don't need to reallocate + rhs.insert(rhs.begin(), lhs, lhs + len); + return std::move(rhs); + } + // Meh, no go. Do it by hand since we have len already. + basic_fbstring<E, T, A, S> result; + result.reserve(len + rhs.size()); + result.append(lhs, len).append(rhs); + return result; +} + +// C++11 21.4.8.1/7 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + E lhs, + const basic_fbstring<E, T, A, S>& rhs) { + basic_fbstring<E, T, A, S> result; + result.reserve(1 + rhs.size()); + result.push_back(lhs); + result.append(rhs); + return result; +} + +// C++11 21.4.8.1/8 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + E lhs, + basic_fbstring<E, T, A, S>&& rhs) { + // + if (rhs.capacity() > rhs.size()) { + // Good, at least we don't need to reallocate + rhs.insert(rhs.begin(), lhs); + return std::move(rhs); + } + // Meh, no go. Forward to operator+(E, const&). + auto const& rhsC = rhs; + return lhs + rhsC; +} + +// C++11 21.4.8.1/9 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const basic_fbstring<E, T, A, S>& lhs, + const E* rhs) { + typedef typename basic_fbstring<E, T, A, S>::size_type size_type; + typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type; + + basic_fbstring<E, T, A, S> result; + const size_type len = traits_type::length(rhs); + result.reserve(lhs.size() + len); + result.append(lhs).append(rhs, len); + return result; +} + +// C++11 21.4.8.1/10 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + basic_fbstring<E, T, A, S>&& lhs, + const E* rhs) { + // + return std::move(lhs += rhs); +} + +// C++11 21.4.8.1/11 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + const basic_fbstring<E, T, A, S>& lhs, + E rhs) { + basic_fbstring<E, T, A, S> result; + result.reserve(lhs.size() + 1); + result.append(lhs); + result.push_back(rhs); + return result; +} + +// C++11 21.4.8.1/12 +template <typename E, class T, class A, class S> +inline basic_fbstring<E, T, A, S> operator+( + basic_fbstring<E, T, A, S>&& lhs, + E rhs) { + // + return std::move(lhs += rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator==( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; +} + +template <typename E, class T, class A, class S> +inline bool operator==( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs == lhs; +} + +template <typename E, class T, class A, class S> +inline bool operator==( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return lhs.compare(rhs) == 0; +} + +template <typename E, class T, class A, class S> +inline bool operator!=( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs == rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator!=( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs == rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator!=( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return !(lhs == rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator<( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return lhs.compare(rhs) < 0; +} + +template <typename E, class T, class A, class S> +inline bool operator<( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return lhs.compare(rhs) < 0; +} + +template <typename E, class T, class A, class S> +inline bool operator<( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs.compare(lhs) > 0; +} + +template <typename E, class T, class A, class S> +inline bool operator>( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs < lhs; +} + +template <typename E, class T, class A, class S> +inline bool operator>( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return rhs < lhs; +} + +template <typename E, class T, class A, class S> +inline bool operator>( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs < lhs; +} + +template <typename E, class T, class A, class S> +inline bool operator<=( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(rhs < lhs); +} + +template <typename E, class T, class A, class S> +inline bool operator<=( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return !(rhs < lhs); +} + +template <typename E, class T, class A, class S> +inline bool operator<=( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(rhs < lhs); +} + +template <typename E, class T, class A, class S> +inline bool operator>=( + const basic_fbstring<E, T, A, S>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs < rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator>=( + const basic_fbstring<E, T, A, S>& lhs, + const typename basic_fbstring<E, T, A, S>::value_type* rhs) { + return !(lhs < rhs); +} + +template <typename E, class T, class A, class S> +inline bool operator>=( + const typename basic_fbstring<E, T, A, S>::value_type* lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs < rhs); +} + +// C++11 21.4.8.8 +template <typename E, class T, class A, class S> +void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) { + lhs.swap(rhs); +} + +// TODO: make this faster. +template <typename E, class T, class A, class S> +inline std::basic_istream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type>& +operator>>( + std::basic_istream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type>& is, + basic_fbstring<E, T, A, S>& str) { + typedef std::basic_istream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type> + _istream_type; + typename _istream_type::sentry sentry(is); + size_t extracted = 0; + typename _istream_type::iostate err = _istream_type::goodbit; + if (sentry) { + auto n = is.width(); + if (n <= 0) { + n = str.max_size(); + } + str.erase(); + for (auto got = is.rdbuf()->sgetc(); extracted != size_t(n); ++extracted) { + if (got == T::eof()) { + err |= _istream_type::eofbit; + is.width(0); + break; + } + if (isspace(got)) { + break; + } + str.push_back(got); + got = is.rdbuf()->snextc(); + } + } + if (!extracted) { + err |= _istream_type::failbit; + } + if (err) { + is.setstate(err); + } + return is; +} + +template <typename E, class T, class A, class S> +inline std::basic_ostream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type>& +operator<<( + std::basic_ostream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type>& os, + const basic_fbstring<E, T, A, S>& str) { +#if _LIBCPP_VERSION + typedef std::basic_ostream< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type> + _ostream_type; + typename _ostream_type::sentry _s(os); + if (_s) { + typedef std::ostreambuf_iterator< + typename basic_fbstring<E, T, A, S>::value_type, + typename basic_fbstring<E, T, A, S>::traits_type> + _Ip; + size_t __len = str.size(); + bool __left = + (os.flags() & _ostream_type::adjustfield) == _ostream_type::left; + if (__pad_and_output( + _Ip(os), + str.data(), + __left ? str.data() + __len : str.data(), + str.data() + __len, + os, + os.fill()) + .failed()) { + os.setstate(_ostream_type::badbit | _ostream_type::failbit); + } + } +#elif defined(_MSC_VER) + typedef decltype(os.precision()) streamsize; + // MSVC doesn't define __ostream_insert + os.write(str.data(), static_cast<streamsize>(str.size())); +#else + std::__ostream_insert(os, str.data(), str.size()); +#endif + return os; +} + +template <typename E1, class T, class A, class S> +constexpr typename basic_fbstring<E1, T, A, S>::size_type + basic_fbstring<E1, T, A, S>::npos; + +// basic_string compatibility routines + +template <typename E, class T, class A, class S, class A2> +inline bool operator==( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator==( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs == lhs; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator!=( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return !(lhs == rhs); +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator!=( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs == rhs); +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator<( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) < 0; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator>( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) > 0; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator<( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs > lhs; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator>( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return rhs < lhs; +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator<=( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return !(lhs > rhs); +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator>=( + const basic_fbstring<E, T, A, S>& lhs, + const std::basic_string<E, T, A2>& rhs) { + return !(lhs < rhs); +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator<=( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs > rhs); +} + +template <typename E, class T, class A, class S, class A2> +inline bool operator>=( + const std::basic_string<E, T, A2>& lhs, + const basic_fbstring<E, T, A, S>& rhs) { + return !(lhs < rhs); +} + +typedef basic_fbstring<char> fbstring; + +// fbstring is relocatable +template <class T, class R, class A, class S> +FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>); + +// Compatibility function, to make sure toStdString(s) can be called +// to convert a std::string or fbstring variable s into type std::string +// with very little overhead if s was already std::string +inline std::string toStdString(const folly::fbstring& s) { + return std::string(s.data(), s.size()); +} + +inline const std::string& toStdString(const std::string& s) { + return s; +} + +// If called with a temporary, the compiler will select this overload instead +// of the above, so we don't return a (lvalue) reference to a temporary. +inline std::string&& toStdString(std::string&& s) { + return std::move(s); +} + +} // namespace folly + +// Hash functions to make fbstring usable with e.g. unordered_map + +#define FOLLY_FBSTRING_HASH1(T) \ + template <> \ + struct hash<::folly::basic_fbstring<T>> { \ + size_t operator()(const ::folly::basic_fbstring<T>& s) const { \ + return ::folly::hash::fnv32_buf(s.data(), s.size() * sizeof(T)); \ + } \ + }; + +// The C++11 standard says that these four are defined for basic_string +#define FOLLY_FBSTRING_HASH \ + FOLLY_FBSTRING_HASH1(char) \ + FOLLY_FBSTRING_HASH1(char16_t) \ + FOLLY_FBSTRING_HASH1(char32_t) \ + FOLLY_FBSTRING_HASH1(wchar_t) + +namespace std { + +FOLLY_FBSTRING_HASH + +} // namespace std + +#undef FOLLY_FBSTRING_HASH +#undef FOLLY_FBSTRING_HASH1 + +FOLLY_POP_WARNING + +#undef FBSTRING_DISABLE_SSO + +namespace folly { +template <class T> +struct IsSomeString; + +template <> +struct IsSomeString<fbstring> : std::true_type {}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/FBVector.h b/ios/Pods/Flipper-Folly/folly/FBVector.h new file mode 100644 index 000000000..29ef06b43 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FBVector.h @@ -0,0 +1,1758 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Nicholas Ormrod (njormrod) + * Andrei Alexandrescu (aalexandre) + * + * FBVector is Facebook's drop-in implementation of std::vector. It has special + * optimizations for use with relocatable types and jemalloc. + */ + +#pragma once + +//============================================================================= +// headers + +#include <algorithm> +#include <cassert> +#include <iterator> +#include <memory> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <folly/FormatTraits.h> +#include <folly/Likely.h> +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> +#include <folly/lang/Exception.h> +#include <folly/memory/Malloc.h> + +//============================================================================= +// forward declaration + +namespace folly { +template <class T, class Allocator = std::allocator<T>> +class fbvector; +} // namespace folly + +//============================================================================= +// unrolling + +#define FOLLY_FBV_UNROLL_PTR(first, last, OP) \ + do { \ + for (; (last) - (first) >= 4; (first) += 4) { \ + OP(((first) + 0)); \ + OP(((first) + 1)); \ + OP(((first) + 2)); \ + OP(((first) + 3)); \ + } \ + for (; (first) != (last); ++(first)) \ + OP((first)); \ + } while (0); + +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// // +// fbvector class // +// // +/////////////////////////////////////////////////////////////////////////////// + +namespace folly { + +template <class T, class Allocator> +class fbvector { + //=========================================================================== + //--------------------------------------------------------------------------- + // implementation + private: + typedef std::allocator_traits<Allocator> A; + + struct Impl : public Allocator { + // typedefs + typedef typename A::pointer pointer; + typedef typename A::size_type size_type; + + // data + pointer b_, e_, z_; + + // constructors + Impl() : Allocator(), b_(nullptr), e_(nullptr), z_(nullptr) {} + /* implicit */ Impl(const Allocator& alloc) + : Allocator(alloc), b_(nullptr), e_(nullptr), z_(nullptr) {} + /* implicit */ Impl(Allocator&& alloc) + : Allocator(std::move(alloc)), b_(nullptr), e_(nullptr), z_(nullptr) {} + + /* implicit */ Impl(size_type n, const Allocator& alloc = Allocator()) + : Allocator(alloc) { + init(n); + } + + Impl(Impl&& other) noexcept + : Allocator(std::move(other)), + b_(other.b_), + e_(other.e_), + z_(other.z_) { + other.b_ = other.e_ = other.z_ = nullptr; + } + + // destructor + ~Impl() { + destroy(); + } + + // allocation + // note that 'allocate' and 'deallocate' are inherited from Allocator + T* D_allocate(size_type n) { + if (usingStdAllocator) { + return static_cast<T*>(checkedMalloc(n * sizeof(T))); + } else { + return std::allocator_traits<Allocator>::allocate(*this, n); + } + } + + void D_deallocate(T* p, size_type n) noexcept { + if (usingStdAllocator) { + free(p); + } else { + std::allocator_traits<Allocator>::deallocate(*this, p, n); + } + } + + // helpers + void swapData(Impl& other) { + std::swap(b_, other.b_); + std::swap(e_, other.e_); + std::swap(z_, other.z_); + } + + // data ops + inline void destroy() noexcept { + if (b_) { + // THIS DISPATCH CODE IS DUPLICATED IN fbvector::D_destroy_range_a. + // It has been inlined here for speed. It calls the static fbvector + // methods to perform the actual destruction. + if (usingStdAllocator) { + S_destroy_range(b_, e_); + } else { + S_destroy_range_a(*this, b_, e_); + } + + D_deallocate(b_, size_type(z_ - b_)); + } + } + + void init(size_type n) { + if (UNLIKELY(n == 0)) { + b_ = e_ = z_ = nullptr; + } else { + size_type sz = folly::goodMallocSize(n * sizeof(T)) / sizeof(T); + b_ = D_allocate(sz); + e_ = b_; + z_ = b_ + sz; + } + } + + void set(pointer newB, size_type newSize, size_type newCap) { + z_ = newB + newCap; + e_ = newB + newSize; + b_ = newB; + } + + void reset(size_type newCap) { + destroy(); + auto rollback = makeGuard([&] { init(0); }); + init(newCap); + rollback.dismiss(); + } + void reset() { // same as reset(0) + destroy(); + b_ = e_ = z_ = nullptr; + } + } impl_; + + static void swap(Impl& a, Impl& b) { + using std::swap; + if (!usingStdAllocator) { + swap(static_cast<Allocator&>(a), static_cast<Allocator&>(b)); + } + a.swapData(b); + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // types and constants + public: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef T* iterator; + typedef const T* const_iterator; + typedef size_t size_type; + typedef typename std::make_signed<size_type>::type difference_type; + typedef Allocator allocator_type; + typedef typename A::pointer pointer; + typedef typename A::const_pointer const_pointer; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + private: + static constexpr bool should_pass_by_value = + is_trivially_copyable<T>::value && + sizeof(T) <= 16; // don't force large structures to be passed by value + typedef typename std::conditional<should_pass_by_value, T, const T&>::type VT; + typedef typename std::conditional<should_pass_by_value, T, T&&>::type MT; + + static constexpr bool usingStdAllocator = + std::is_same<Allocator, std::allocator<T>>::value; + typedef bool_constant< + usingStdAllocator || A::propagate_on_container_move_assignment::value> + moveIsSwap; + + //=========================================================================== + //--------------------------------------------------------------------------- + // allocator helpers + private: + //--------------------------------------------------------------------------- + // allocate + + T* M_allocate(size_type n) { + return impl_.D_allocate(n); + } + + //--------------------------------------------------------------------------- + // deallocate + + void M_deallocate(T* p, size_type n) noexcept { + impl_.D_deallocate(p, n); + } + + //--------------------------------------------------------------------------- + // construct + + // GCC is very sensitive to the exact way that construct is called. For + // that reason there are several different specializations of construct. + + template <typename U, typename... Args> + void M_construct(U* p, Args&&... args) { + if (usingStdAllocator) { + new (p) U(std::forward<Args>(args)...); + } else { + std::allocator_traits<Allocator>::construct( + impl_, p, std::forward<Args>(args)...); + } + } + + template <typename U, typename... Args> + static void S_construct(U* p, Args&&... args) { + new (p) U(std::forward<Args>(args)...); + } + + template <typename U, typename... Args> + static void S_construct_a(Allocator& a, U* p, Args&&... args) { + std::allocator_traits<Allocator>::construct( + a, p, std::forward<Args>(args)...); + } + + // scalar optimization + // TODO we can expand this optimization to: default copyable and assignable + template < + typename U, + typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type> + void M_construct(U* p, U arg) { + if (usingStdAllocator) { + *p = arg; + } else { + std::allocator_traits<Allocator>::construct(impl_, p, arg); + } + } + + template < + typename U, + typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type> + static void S_construct(U* p, U arg) { + *p = arg; + } + + template < + typename U, + typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type> + static void S_construct_a(Allocator& a, U* p, U arg) { + std::allocator_traits<Allocator>::construct(a, p, arg); + } + + // const& optimization + template < + typename U, + typename Enable = + typename std::enable_if<!std::is_scalar<U>::value>::type> + void M_construct(U* p, const U& value) { + if (usingStdAllocator) { + new (p) U(value); + } else { + std::allocator_traits<Allocator>::construct(impl_, p, value); + } + } + + template < + typename U, + typename Enable = + typename std::enable_if<!std::is_scalar<U>::value>::type> + static void S_construct(U* p, const U& value) { + new (p) U(value); + } + + template < + typename U, + typename Enable = + typename std::enable_if<!std::is_scalar<U>::value>::type> + static void S_construct_a(Allocator& a, U* p, const U& value) { + std::allocator_traits<Allocator>::construct(a, p, value); + } + + //--------------------------------------------------------------------------- + // destroy + + void M_destroy(T* p) noexcept { + if (usingStdAllocator) { + if (!std::is_trivially_destructible<T>::value) { + p->~T(); + } + } else { + std::allocator_traits<Allocator>::destroy(impl_, p); + } + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // algorithmic helpers + private: + //--------------------------------------------------------------------------- + // destroy_range + + // wrappers + void M_destroy_range_e(T* pos) noexcept { + D_destroy_range_a(pos, impl_.e_); + impl_.e_ = pos; + } + + // dispatch + // THIS DISPATCH CODE IS DUPLICATED IN IMPL. SEE IMPL FOR DETAILS. + void D_destroy_range_a(T* first, T* last) noexcept { + if (usingStdAllocator) { + S_destroy_range(first, last); + } else { + S_destroy_range_a(impl_, first, last); + } + } + + // allocator + static void S_destroy_range_a(Allocator& a, T* first, T* last) noexcept { + for (; first != last; ++first) { + std::allocator_traits<Allocator>::destroy(a, first); + } + } + + // optimized + static void S_destroy_range(T* first, T* last) noexcept { + if (!std::is_trivially_destructible<T>::value) { +#define FOLLY_FBV_OP(p) (p)->~T() + // EXPERIMENTAL DATA on fbvector<vector<int>> (where each vector<int> has + // size 0), were vector<int> to be relocatable. + // The unrolled version seems to work faster for small to medium sized + // fbvectors. It gets a 10% speedup on fbvectors of size 1024, 64, and + // 16. + // The simple loop version seems to work faster for large fbvectors. The + // unrolled version is about 6% slower on fbvectors on size 16384. + // The two methods seem tied for very large fbvectors. The unrolled + // version is about 0.5% slower on size 262144. + + // for (; first != last; ++first) first->~T(); + FOLLY_FBV_UNROLL_PTR(first, last, FOLLY_FBV_OP) +#undef FOLLY_FBV_OP + } + } + + //--------------------------------------------------------------------------- + // uninitialized_fill_n + + // wrappers + void M_uninitialized_fill_n_e(size_type sz) { + D_uninitialized_fill_n_a(impl_.e_, sz); + impl_.e_ += sz; + } + + void M_uninitialized_fill_n_e(size_type sz, VT value) { + D_uninitialized_fill_n_a(impl_.e_, sz, value); + impl_.e_ += sz; + } + + // dispatch + void D_uninitialized_fill_n_a(T* dest, size_type sz) { + if (usingStdAllocator) { + S_uninitialized_fill_n(dest, sz); + } else { + S_uninitialized_fill_n_a(impl_, dest, sz); + } + } + + void D_uninitialized_fill_n_a(T* dest, size_type sz, VT value) { + if (usingStdAllocator) { + S_uninitialized_fill_n(dest, sz, value); + } else { + S_uninitialized_fill_n_a(impl_, dest, sz, value); + } + } + + // allocator + template <typename... Args> + static void S_uninitialized_fill_n_a( + Allocator& a, + T* dest, + size_type sz, + Args&&... args) { + auto b = dest; + auto e = dest + sz; + auto rollback = makeGuard([&] { S_destroy_range_a(a, dest, b); }); + for (; b != e; ++b) { + std::allocator_traits<Allocator>::construct( + a, b, std::forward<Args>(args)...); + } + rollback.dismiss(); + } + + // optimized + static void S_uninitialized_fill_n(T* dest, size_type n) { + if (folly::IsZeroInitializable<T>::value) { + if (LIKELY(n != 0)) { + std::memset((void*)dest, 0, sizeof(T) * n); + } + } else { + auto b = dest; + auto e = dest + n; + auto rollback = makeGuard([&] { + --b; + for (; b >= dest; --b) { + b->~T(); + } + }); + for (; b != e; ++b) { + S_construct(b); + } + rollback.dismiss(); + } + } + + static void S_uninitialized_fill_n(T* dest, size_type n, const T& value) { + auto b = dest; + auto e = dest + n; + auto rollback = makeGuard([&] { S_destroy_range(dest, b); }); + for (; b != e; ++b) { + S_construct(b, value); + } + rollback.dismiss(); + } + + //--------------------------------------------------------------------------- + // uninitialized_copy + + // it is possible to add an optimization for the case where + // It = move(T*) and IsRelocatable<T> and Is0Initiailizable<T> + + // wrappers + template <typename It> + void M_uninitialized_copy_e(It first, It last) { + D_uninitialized_copy_a(impl_.e_, first, last); + impl_.e_ += std::distance(first, last); + } + + template <typename It> + void M_uninitialized_move_e(It first, It last) { + D_uninitialized_move_a(impl_.e_, first, last); + impl_.e_ += std::distance(first, last); + } + + // dispatch + template <typename It> + void D_uninitialized_copy_a(T* dest, It first, It last) { + if (usingStdAllocator) { + if (folly::is_trivially_copyable<T>::value) { + S_uninitialized_copy_bits(dest, first, last); + } else { + S_uninitialized_copy(dest, first, last); + } + } else { + S_uninitialized_copy_a(impl_, dest, first, last); + } + } + + template <typename It> + void D_uninitialized_move_a(T* dest, It first, It last) { + D_uninitialized_copy_a( + dest, std::make_move_iterator(first), std::make_move_iterator(last)); + } + + // allocator + template <typename It> + static void S_uninitialized_copy_a(Allocator& a, T* dest, It first, It last) { + auto b = dest; + auto rollback = makeGuard([&] { S_destroy_range_a(a, dest, b); }); + for (; first != last; ++first, ++b) { + std::allocator_traits<Allocator>::construct(a, b, *first); + } + rollback.dismiss(); + } + + // optimized + template <typename It> + static void S_uninitialized_copy(T* dest, It first, It last) { + auto b = dest; + auto rollback = makeGuard([&] { S_destroy_range(dest, b); }); + for (; first != last; ++first, ++b) { + S_construct(b, *first); + } + rollback.dismiss(); + } + + static void + S_uninitialized_copy_bits(T* dest, const T* first, const T* last) { + if (last != first) { + std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T)); + } + } + + static void S_uninitialized_copy_bits( + T* dest, + std::move_iterator<T*> first, + std::move_iterator<T*> last) { + T* bFirst = first.base(); + T* bLast = last.base(); + if (bLast != bFirst) { + std::memcpy((void*)dest, (void*)bFirst, (bLast - bFirst) * sizeof(T)); + } + } + + template <typename It> + static void S_uninitialized_copy_bits(T* dest, It first, It last) { + S_uninitialized_copy(dest, first, last); + } + + //--------------------------------------------------------------------------- + // copy_n + + // This function is "unsafe": it assumes that the iterator can be advanced at + // least n times. However, as a private function, that unsafety is managed + // wholly by fbvector itself. + + template <typename It> + static It S_copy_n(T* dest, It first, size_type n) { + auto e = dest + n; + for (; dest != e; ++dest, ++first) { + *dest = *first; + } + return first; + } + + static const T* S_copy_n(T* dest, const T* first, size_type n) { + if (is_trivially_copyable<T>::value) { + std::memcpy((void*)dest, (void*)first, n * sizeof(T)); + return first + n; + } else { + return S_copy_n<const T*>(dest, first, n); + } + } + + static std::move_iterator<T*> + S_copy_n(T* dest, std::move_iterator<T*> mIt, size_type n) { + if (is_trivially_copyable<T>::value) { + T* first = mIt.base(); + std::memcpy((void*)dest, (void*)first, n * sizeof(T)); + return std::make_move_iterator(first + n); + } else { + return S_copy_n<std::move_iterator<T*>>(dest, mIt, n); + } + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // relocation helpers + private: + // Relocation is divided into three parts: + // + // 1: relocate_move + // Performs the actual movement of data from point a to point b. + // + // 2: relocate_done + // Destroys the old data. + // + // 3: relocate_undo + // Destoys the new data and restores the old data. + // + // The three steps are used because there may be an exception after part 1 + // has completed. If that is the case, then relocate_undo can nullify the + // initial move. Otherwise, relocate_done performs the last bit of tidying + // up. + // + // The relocation trio may use either memcpy, move, or copy. It is decided + // by the following case statement: + // + // IsRelocatable && usingStdAllocator -> memcpy + // has_nothrow_move && usingStdAllocator -> move + // cannot copy -> move + // default -> copy + // + // If the class is non-copyable then it must be movable. However, if the + // move constructor is not noexcept, i.e. an error could be thrown, then + // relocate_undo will be unable to restore the old data, for fear of a + // second exception being thrown. This is a known and unavoidable + // deficiency. In lieu of a strong exception guarantee, relocate_undo does + // the next best thing: it provides a weak exception guarantee by + // destorying the new data, but leaving the old data in an indeterminate + // state. Note that that indeterminate state will be valid, since the + // old data has not been destroyed; it has merely been the source of a + // move, which is required to leave the source in a valid state. + + // wrappers + void M_relocate(T* newB) { + relocate_move(newB, impl_.b_, impl_.e_); + relocate_done(newB, impl_.b_, impl_.e_); + } + + // dispatch type trait + typedef bool_constant<folly::IsRelocatable<T>::value && usingStdAllocator> + relocate_use_memcpy; + + typedef bool_constant< + (std::is_nothrow_move_constructible<T>::value && usingStdAllocator) || + !std::is_copy_constructible<T>::value> + relocate_use_move; + + // move + void relocate_move(T* dest, T* first, T* last) { + relocate_move_or_memcpy(dest, first, last, relocate_use_memcpy()); + } + + void relocate_move_or_memcpy(T* dest, T* first, T* last, std::true_type) { + if (first != nullptr) { + std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T)); + } + } + + void relocate_move_or_memcpy(T* dest, T* first, T* last, std::false_type) { + relocate_move_or_copy(dest, first, last, relocate_use_move()); + } + + void relocate_move_or_copy(T* dest, T* first, T* last, std::true_type) { + D_uninitialized_move_a(dest, first, last); + } + + void relocate_move_or_copy(T* dest, T* first, T* last, std::false_type) { + D_uninitialized_copy_a(dest, first, last); + } + + // done + void relocate_done(T* /*dest*/, T* first, T* last) noexcept { + if (folly::IsRelocatable<T>::value && usingStdAllocator) { + // used memcpy; data has been relocated, do not call destructor + } else { + D_destroy_range_a(first, last); + } + } + + // undo + void relocate_undo(T* dest, T* first, T* last) noexcept { + if (folly::IsRelocatable<T>::value && usingStdAllocator) { + // used memcpy, old data is still valid, nothing to do + } else if ( + std::is_nothrow_move_constructible<T>::value && usingStdAllocator) { + // noexcept move everything back, aka relocate_move + relocate_move(first, dest, dest + (last - first)); + } else if (!std::is_copy_constructible<T>::value) { + // weak guarantee + D_destroy_range_a(dest, dest + (last - first)); + } else { + // used copy, old data is still valid + D_destroy_range_a(dest, dest + (last - first)); + } + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // construct/copy/destroy + public: + fbvector() = default; + + explicit fbvector(const Allocator& a) : impl_(a) {} + + explicit fbvector(size_type n, const Allocator& a = Allocator()) + : impl_(n, a) { + M_uninitialized_fill_n_e(n); + } + + fbvector(size_type n, VT value, const Allocator& a = Allocator()) + : impl_(n, a) { + M_uninitialized_fill_n_e(n, value); + } + + template < + class It, + class Category = typename std::iterator_traits<It>::iterator_category> + fbvector(It first, It last, const Allocator& a = Allocator()) + : fbvector(first, last, a, Category()) {} + + fbvector(const fbvector& other) + : impl_( + other.size(), + A::select_on_container_copy_construction(other.impl_)) { + M_uninitialized_copy_e(other.begin(), other.end()); + } + + fbvector(fbvector&& other) noexcept : impl_(std::move(other.impl_)) {} + + fbvector(const fbvector& other, const Allocator& a) + : fbvector(other.begin(), other.end(), a) {} + + /* may throw */ fbvector(fbvector&& other, const Allocator& a) : impl_(a) { + if (impl_ == other.impl_) { + impl_.swapData(other.impl_); + } else { + impl_.init(other.size()); + M_uninitialized_move_e(other.begin(), other.end()); + } + } + + fbvector(std::initializer_list<T> il, const Allocator& a = Allocator()) + : fbvector(il.begin(), il.end(), a) {} + + ~fbvector() = default; // the cleanup occurs in impl_ + + fbvector& operator=(const fbvector& other) { + if (UNLIKELY(this == &other)) { + return *this; + } + + if (!usingStdAllocator && + A::propagate_on_container_copy_assignment::value) { + if (impl_ != other.impl_) { + // can't use other's different allocator to clean up self + impl_.reset(); + } + (Allocator&)impl_ = (Allocator&)other.impl_; + } + + assign(other.begin(), other.end()); + return *this; + } + + fbvector& operator=(fbvector&& other) { + if (UNLIKELY(this == &other)) { + return *this; + } + moveFrom(std::move(other), moveIsSwap()); + return *this; + } + + fbvector& operator=(std::initializer_list<T> il) { + assign(il.begin(), il.end()); + return *this; + } + + template < + class It, + class Category = typename std::iterator_traits<It>::iterator_category> + void assign(It first, It last) { + assign(first, last, Category()); + } + + void assign(size_type n, VT value) { + if (n > capacity()) { + // Not enough space. Do not reserve in place, since we will + // discard the old values anyways. + if (dataIsInternalAndNotVT(value)) { + T copy(std::move(value)); + impl_.reset(n); + M_uninitialized_fill_n_e(n, copy); + } else { + impl_.reset(n); + M_uninitialized_fill_n_e(n, value); + } + } else if (n <= size()) { + auto newE = impl_.b_ + n; + std::fill(impl_.b_, newE, value); + M_destroy_range_e(newE); + } else { + std::fill(impl_.b_, impl_.e_, value); + M_uninitialized_fill_n_e(n - size(), value); + } + } + + void assign(std::initializer_list<T> il) { + assign(il.begin(), il.end()); + } + + allocator_type get_allocator() const noexcept { + return impl_; + } + + private: + // contract dispatch for iterator types fbvector(It first, It last) + template <class ForwardIterator> + fbvector( + ForwardIterator first, + ForwardIterator last, + const Allocator& a, + std::forward_iterator_tag) + : impl_(size_type(std::distance(first, last)), a) { + M_uninitialized_copy_e(first, last); + } + + template <class InputIterator> + fbvector( + InputIterator first, + InputIterator last, + const Allocator& a, + std::input_iterator_tag) + : impl_(a) { + for (; first != last; ++first) { + emplace_back(*first); + } + } + + // contract dispatch for allocator movement in operator=(fbvector&&) + void moveFrom(fbvector&& other, std::true_type) { + swap(impl_, other.impl_); + } + void moveFrom(fbvector&& other, std::false_type) { + if (impl_ == other.impl_) { + impl_.swapData(other.impl_); + } else { + impl_.reset(other.size()); + M_uninitialized_move_e(other.begin(), other.end()); + } + } + + // contract dispatch for iterator types in assign(It first, It last) + template <class ForwardIterator> + void assign( + ForwardIterator first, + ForwardIterator last, + std::forward_iterator_tag) { + const auto newSize = size_type(std::distance(first, last)); + if (newSize > capacity()) { + impl_.reset(newSize); + M_uninitialized_copy_e(first, last); + } else if (newSize <= size()) { + auto newEnd = std::copy(first, last, impl_.b_); + M_destroy_range_e(newEnd); + } else { + auto mid = S_copy_n(impl_.b_, first, size()); + M_uninitialized_copy_e<decltype(last)>(mid, last); + } + } + + template <class InputIterator> + void + assign(InputIterator first, InputIterator last, std::input_iterator_tag) { + auto p = impl_.b_; + for (; first != last && p != impl_.e_; ++first, ++p) { + *p = *first; + } + if (p != impl_.e_) { + M_destroy_range_e(p); + } else { + for (; first != last; ++first) { + emplace_back(*first); + } + } + } + + // contract dispatch for aliasing under VT optimization + bool dataIsInternalAndNotVT(const T& t) { + if (should_pass_by_value) { + return false; + } + return dataIsInternal(t); + } + bool dataIsInternal(const T& t) { + return UNLIKELY( + impl_.b_ <= std::addressof(t) && std::addressof(t) < impl_.e_); + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // iterators + public: + iterator begin() noexcept { + return impl_.b_; + } + const_iterator begin() const noexcept { + return impl_.b_; + } + iterator end() noexcept { + return impl_.e_; + } + const_iterator end() const noexcept { + return impl_.e_; + } + reverse_iterator rbegin() noexcept { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + reverse_iterator rend() noexcept { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + const_iterator cbegin() const noexcept { + return impl_.b_; + } + const_iterator cend() const noexcept { + return impl_.e_; + } + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(end()); + } + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(begin()); + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // capacity + public: + size_type size() const noexcept { + return size_type(impl_.e_ - impl_.b_); + } + + size_type max_size() const noexcept { + // good luck gettin' there + return ~size_type(0); + } + + void resize(size_type n) { + if (n <= size()) { + M_destroy_range_e(impl_.b_ + n); + } else { + reserve(n); + M_uninitialized_fill_n_e(n - size()); + } + } + + void resize(size_type n, VT t) { + if (n <= size()) { + M_destroy_range_e(impl_.b_ + n); + } else if (dataIsInternalAndNotVT(t) && n > capacity()) { + T copy(t); + reserve(n); + M_uninitialized_fill_n_e(n - size(), copy); + } else { + reserve(n); + M_uninitialized_fill_n_e(n - size(), t); + } + } + + size_type capacity() const noexcept { + return size_type(impl_.z_ - impl_.b_); + } + + bool empty() const noexcept { + return impl_.b_ == impl_.e_; + } + + void reserve(size_type n) { + if (n <= capacity()) { + return; + } + if (impl_.b_ && reserve_in_place(n)) { + return; + } + + auto newCap = folly::goodMallocSize(n * sizeof(T)) / sizeof(T); + auto newB = M_allocate(newCap); + { + auto rollback = makeGuard([&] { M_deallocate(newB, newCap); }); + M_relocate(newB); + rollback.dismiss(); + } + if (impl_.b_) { + M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_)); + } + impl_.z_ = newB + newCap; + impl_.e_ = newB + (impl_.e_ - impl_.b_); + impl_.b_ = newB; + } + + void shrink_to_fit() noexcept { + if (empty()) { + impl_.reset(); + return; + } + + auto const newCapacityBytes = folly::goodMallocSize(size() * sizeof(T)); + auto const newCap = newCapacityBytes / sizeof(T); + auto const oldCap = capacity(); + + if (newCap >= oldCap) { + return; + } + + void* p = impl_.b_; + // xallocx() will shrink to precisely newCapacityBytes (which was generated + // by goodMallocSize()) if it successfully shrinks in place. + if ((usingJEMalloc() && usingStdAllocator) && + newCapacityBytes >= folly::jemallocMinInPlaceExpandable && + xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) { + impl_.z_ += newCap - oldCap; + } else { + T* newB; // intentionally uninitialized + if (!catch_exception( + [&] { + newB = M_allocate(newCap); + return true; + }, + [&] { // + return false; + })) { + return; + } + if (!catch_exception( + [&] { + M_relocate(newB); + return true; + }, + [&] { + M_deallocate(newB, newCap); + return false; + })) { + return; + } + if (impl_.b_) { + M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_)); + } + impl_.z_ = newB + newCap; + impl_.e_ = newB + (impl_.e_ - impl_.b_); + impl_.b_ = newB; + } + } + + private: + bool reserve_in_place(size_type n) { + if (!usingStdAllocator || !usingJEMalloc()) { + return false; + } + + // jemalloc can never grow in place blocks smaller than 4096 bytes. + if ((impl_.z_ - impl_.b_) * sizeof(T) < + folly::jemallocMinInPlaceExpandable) { + return false; + } + + auto const newCapacityBytes = folly::goodMallocSize(n * sizeof(T)); + void* p = impl_.b_; + if (xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) { + impl_.z_ = impl_.b_ + newCapacityBytes / sizeof(T); + return true; + } + return false; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // element access + public: + reference operator[](size_type n) { + assert(n < size()); + return impl_.b_[n]; + } + const_reference operator[](size_type n) const { + assert(n < size()); + return impl_.b_[n]; + } + const_reference at(size_type n) const { + if (UNLIKELY(n >= size())) { + throw_exception<std::out_of_range>( + "fbvector: index is greater than size."); + } + return (*this)[n]; + } + reference at(size_type n) { + auto const& cThis = *this; + return const_cast<reference>(cThis.at(n)); + } + reference front() { + assert(!empty()); + return *impl_.b_; + } + const_reference front() const { + assert(!empty()); + return *impl_.b_; + } + reference back() { + assert(!empty()); + return impl_.e_[-1]; + } + const_reference back() const { + assert(!empty()); + return impl_.e_[-1]; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // data access + public: + T* data() noexcept { + return impl_.b_; + } + const T* data() const noexcept { + return impl_.b_; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // modifiers (common) + public: + template <class... Args> + reference emplace_back(Args&&... args) { + if (impl_.e_ != impl_.z_) { + M_construct(impl_.e_, std::forward<Args>(args)...); + ++impl_.e_; + } else { + emplace_back_aux(std::forward<Args>(args)...); + } + return back(); + } + + void push_back(const T& value) { + if (impl_.e_ != impl_.z_) { + M_construct(impl_.e_, value); + ++impl_.e_; + } else { + emplace_back_aux(value); + } + } + + void push_back(T&& value) { + if (impl_.e_ != impl_.z_) { + M_construct(impl_.e_, std::move(value)); + ++impl_.e_; + } else { + emplace_back_aux(std::move(value)); + } + } + + void pop_back() { + assert(!empty()); + --impl_.e_; + M_destroy(impl_.e_); + } + + void swap(fbvector& other) noexcept { + if (!usingStdAllocator && A::propagate_on_container_swap::value) { + swap(impl_, other.impl_); + } else { + impl_.swapData(other.impl_); + } + } + + void clear() noexcept { + M_destroy_range_e(impl_.b_); + } + + private: + // std::vector implements a similar function with a different growth + // strategy: empty() ? 1 : capacity() * 2. + // + // fbvector grows differently on two counts: + // + // (1) initial size + // Instead of growing to size 1 from empty, fbvector allocates at least + // 64 bytes. You may still use reserve to reserve a lesser amount of + // memory. + // (2) 1.5x + // For medium-sized vectors, the growth strategy is 1.5x. See the docs + // for details. + // This does not apply to very small or very large fbvectors. This is a + // heuristic. + // A nice addition to fbvector would be the capability of having a user- + // defined growth strategy, probably as part of the allocator. + // + + size_type computePushBackCapacity() const { + if (capacity() == 0) { + return std::max(64 / sizeof(T), size_type(1)); + } + if (capacity() < folly::jemallocMinInPlaceExpandable / sizeof(T)) { + return capacity() * 2; + } + if (capacity() > 4096 * 32 / sizeof(T)) { + return capacity() * 2; + } + return (capacity() * 3 + 1) / 2; + } + + template <class... Args> + void emplace_back_aux(Args&&... args) { + size_type byte_sz = + folly::goodMallocSize(computePushBackCapacity() * sizeof(T)); + if (usingStdAllocator && usingJEMalloc() && + ((impl_.z_ - impl_.b_) * sizeof(T) >= + folly::jemallocMinInPlaceExpandable)) { + // Try to reserve in place. + // Ask xallocx to allocate in place at least size()+1 and at most sz + // space. + // xallocx will allocate as much as possible within that range, which + // is the best possible outcome: if sz space is available, take it all, + // otherwise take as much as possible. If nothing is available, then + // fail. + // In this fashion, we never relocate if there is a possibility of + // expanding in place, and we never reallocate by less than the desired + // amount unless we cannot expand further. Hence we will not reallocate + // sub-optimally twice in a row (modulo the blocking memory being freed). + size_type lower = folly::goodMallocSize(sizeof(T) + size() * sizeof(T)); + size_type upper = byte_sz; + size_type extra = upper - lower; + + void* p = impl_.b_; + size_t actual; + + if ((actual = xallocx(p, lower, extra, 0)) >= lower) { + impl_.z_ = impl_.b_ + actual / sizeof(T); + M_construct(impl_.e_, std::forward<Args>(args)...); + ++impl_.e_; + return; + } + } + + // Reallocation failed. Perform a manual relocation. + size_type sz = byte_sz / sizeof(T); + auto newB = M_allocate(sz); + auto newE = newB + size(); + { + auto rollback1 = makeGuard([&] { M_deallocate(newB, sz); }); + if (folly::IsRelocatable<T>::value && usingStdAllocator) { + // For linear memory access, relocate before construction. + // By the test condition, relocate is noexcept. + // Note that there is no cleanup to do if M_construct throws - that's + // one of the beauties of relocation. + // Benchmarks for this code have high variance, and seem to be close. + relocate_move(newB, impl_.b_, impl_.e_); + M_construct(newE, std::forward<Args>(args)...); + ++newE; + } else { + M_construct(newE, std::forward<Args>(args)...); + ++newE; + auto rollback2 = makeGuard([&] { M_destroy(newE - 1); }); + M_relocate(newB); + rollback2.dismiss(); + } + rollback1.dismiss(); + } + if (impl_.b_) { + M_deallocate(impl_.b_, size()); + } + impl_.b_ = newB; + impl_.e_ = newE; + impl_.z_ = newB + sz; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // modifiers (erase) + public: + iterator erase(const_iterator position) { + return erase(position, position + 1); + } + + iterator erase(const_iterator first, const_iterator last) { + assert(isValid(first) && isValid(last)); + assert(first <= last); + if (first != last) { + if (last == end()) { + M_destroy_range_e((iterator)first); + } else { + if (folly::IsRelocatable<T>::value && usingStdAllocator) { + D_destroy_range_a((iterator)first, (iterator)last); + if (last - first >= cend() - last) { + std::memcpy((void*)first, (void*)last, (cend() - last) * sizeof(T)); + } else { + std::memmove( + (void*)first, (void*)last, (cend() - last) * sizeof(T)); + } + impl_.e_ -= (last - first); + } else { + std::copy( + std::make_move_iterator((iterator)last), + std::make_move_iterator(end()), + (iterator)first); + auto newEnd = impl_.e_ - std::distance(first, last); + M_destroy_range_e(newEnd); + } + } + } + return (iterator)first; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // modifiers (insert) + private: // we have the private section first because it defines some macros + bool isValid(const_iterator it) { + return cbegin() <= it && it <= cend(); + } + + size_type computeInsertCapacity(size_type n) { + size_type nc = std::max(computePushBackCapacity(), size() + n); + size_type ac = folly::goodMallocSize(nc * sizeof(T)) / sizeof(T); + return ac; + } + + //--------------------------------------------------------------------------- + // + // make_window takes an fbvector, and creates an uninitialized gap (a + // window) at the given position, of the given size. The fbvector must + // have enough capacity. + // + // Explanation by picture. + // + // 123456789______ + // ^ + // make_window here of size 3 + // + // 1234___56789___ + // + // If something goes wrong and the window must be destroyed, use + // undo_window to provide a weak exception guarantee. It destroys + // the right ledge. + // + // 1234___________ + // + //--------------------------------------------------------------------------- + // + // wrap_frame takes an inverse window and relocates an fbvector around it. + // The fbvector must have at least as many elements as the left ledge. + // + // Explanation by picture. + // + // START + // fbvector: inverse window: + // 123456789______ _____abcde_______ + // [idx][ n ] + // + // RESULT + // _______________ 12345abcde6789___ + // + //--------------------------------------------------------------------------- + // + // insert_use_fresh_memory returns true iff the fbvector should use a fresh + // block of memory for the insertion. If the fbvector does not have enough + // spare capacity, then it must return true. Otherwise either true or false + // may be returned. + // + //--------------------------------------------------------------------------- + // + // These three functions, make_window, wrap_frame, and + // insert_use_fresh_memory, can be combined into a uniform interface. + // Since that interface involves a lot of case-work, it is built into + // some macros: FOLLY_FBVECTOR_INSERT_(PRE|START|TRY|END) + // Macros are used in an attempt to let GCC perform better optimizations, + // especially control flow optimization. + // + + //--------------------------------------------------------------------------- + // window + + void make_window(iterator position, size_type n) { + // The result is guaranteed to be non-negative, so use an unsigned type: + size_type tail = size_type(std::distance(position, impl_.e_)); + + if (tail <= n) { + relocate_move(position + n, position, impl_.e_); + relocate_done(position + n, position, impl_.e_); + impl_.e_ += n; + } else { + if (folly::IsRelocatable<T>::value && usingStdAllocator) { + std::memmove((void*)(position + n), (void*)position, tail * sizeof(T)); + impl_.e_ += n; + } else { + D_uninitialized_move_a(impl_.e_, impl_.e_ - n, impl_.e_); + { + auto rollback = makeGuard([&] { + D_destroy_range_a(impl_.e_ - n, impl_.e_ + n); + impl_.e_ -= n; + }); + std::copy_backward( + std::make_move_iterator(position), + std::make_move_iterator(impl_.e_ - n), + impl_.e_); + rollback.dismiss(); + } + impl_.e_ += n; + D_destroy_range_a(position, position + n); + } + } + } + + void undo_window(iterator position, size_type n) noexcept { + D_destroy_range_a(position + n, impl_.e_); + impl_.e_ = position; + } + + //--------------------------------------------------------------------------- + // frame + + void wrap_frame(T* ledge, size_type idx, size_type n) { + assert(size() >= idx); + assert(n != 0); + + relocate_move(ledge, impl_.b_, impl_.b_ + idx); + { + auto rollback = makeGuard([&] { // + relocate_undo(ledge, impl_.b_, impl_.b_ + idx); + }); + relocate_move(ledge + idx + n, impl_.b_ + idx, impl_.e_); + rollback.dismiss(); + } + relocate_done(ledge, impl_.b_, impl_.b_ + idx); + relocate_done(ledge + idx + n, impl_.b_ + idx, impl_.e_); + } + + //--------------------------------------------------------------------------- + // use fresh? + + bool insert_use_fresh(bool at_end, size_type n) { + if (at_end) { + if (size() + n <= capacity()) { + return false; + } + if (reserve_in_place(size() + n)) { + return false; + } + return true; + } + + if (size() + n > capacity()) { + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + // interface + + template < + typename IsInternalFunc, + typename InsertInternalFunc, + typename ConstructFunc, + typename DestroyFunc> + iterator do_real_insert( + const_iterator cpos, + size_type n, + IsInternalFunc&& isInternalFunc, + InsertInternalFunc&& insertInternalFunc, + ConstructFunc&& constructFunc, + DestroyFunc&& destroyFunc) { + if (n == 0) { + return iterator(cpos); + } + bool at_end = cpos == cend(); + bool fresh = insert_use_fresh(at_end, n); + if (!at_end) { + if (!fresh && isInternalFunc()) { + // check for internal data (technically not required by the standard) + return insertInternalFunc(); + } + assert(isValid(cpos)); + } + T* position = const_cast<T*>(cpos); + size_type idx = size_type(std::distance(impl_.b_, position)); + T* b; + size_type newCap; /* intentionally uninitialized */ + + if (fresh) { + newCap = computeInsertCapacity(n); + b = M_allocate(newCap); + } else { + if (!at_end) { + make_window(position, n); + } else { + impl_.e_ += n; + } + b = impl_.b_; + } + + T* start = b + idx; + { + auto rollback = makeGuard([&] { + if (fresh) { + M_deallocate(b, newCap); + } else { + if (!at_end) { + undo_window(position, n); + } else { + impl_.e_ -= n; + } + } + }); + // construct the inserted elements + constructFunc(start); + rollback.dismiss(); + } + + if (fresh) { + { + auto rollback = makeGuard([&] { + // delete the inserted elements (exception has been thrown) + destroyFunc(start); + M_deallocate(b, newCap); + }); + wrap_frame(b, idx, n); + rollback.dismiss(); + } + if (impl_.b_) { + M_deallocate(impl_.b_, capacity()); + } + impl_.set(b, size() + n, newCap); + return impl_.b_ + idx; + } else { + return position; + } + } + + public: + template <class... Args> + iterator emplace(const_iterator cpos, Args&&... args) { + return do_real_insert( + cpos, + 1, + [&] { return false; }, + [&] { return iterator{}; }, + [&](iterator start) { + M_construct(start, std::forward<Args>(args)...); + }, + [&](iterator start) { M_destroy(start); }); + } + + iterator insert(const_iterator cpos, const T& value) { + return do_real_insert( + cpos, + 1, + [&] { return dataIsInternal(value); }, + [&] { return insert(cpos, T(value)); }, + [&](iterator start) { M_construct(start, value); }, + [&](iterator start) { M_destroy(start); }); + } + + iterator insert(const_iterator cpos, T&& value) { + return do_real_insert( + cpos, + 1, + [&] { return dataIsInternal(value); }, + [&] { return insert(cpos, T(std::move(value))); }, + [&](iterator start) { M_construct(start, std::move(value)); }, + [&](iterator start) { M_destroy(start); }); + } + + iterator insert(const_iterator cpos, size_type n, VT value) { + return do_real_insert( + cpos, + n, + [&] { return dataIsInternalAndNotVT(value); }, + [&] { return insert(cpos, n, T(value)); }, + [&](iterator start) { D_uninitialized_fill_n_a(start, n, value); }, + [&](iterator start) { D_destroy_range_a(start, start + n); }); + } + + template < + class It, + class Category = typename std::iterator_traits<It>::iterator_category> + iterator insert(const_iterator cpos, It first, It last) { + return insert(cpos, first, last, Category()); + } + + iterator insert(const_iterator cpos, std::initializer_list<T> il) { + return insert(cpos, il.begin(), il.end()); + } + + //--------------------------------------------------------------------------- + // insert dispatch for iterator types + private: + template <class FIt> + iterator + insert(const_iterator cpos, FIt first, FIt last, std::forward_iterator_tag) { + size_type n = size_type(std::distance(first, last)); + return do_real_insert( + cpos, + n, + [&] { return false; }, + [&] { return iterator{}; }, + [&](iterator start) { D_uninitialized_copy_a(start, first, last); }, + [&](iterator start) { D_destroy_range_a(start, start + n); }); + } + + template <class IIt> + iterator + insert(const_iterator cpos, IIt first, IIt last, std::input_iterator_tag) { + T* position = const_cast<T*>(cpos); + assert(isValid(position)); + size_type idx = std::distance(begin(), position); + + fbvector storage( + std::make_move_iterator(position), + std::make_move_iterator(end()), + A::select_on_container_copy_construction(impl_)); + M_destroy_range_e(position); + for (; first != last; ++first) { + emplace_back(*first); + } + insert( + cend(), + std::make_move_iterator(storage.begin()), + std::make_move_iterator(storage.end())); + return impl_.b_ + idx; + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // lexicographical functions + public: + bool operator==(const fbvector& other) const { + return size() == other.size() && std::equal(begin(), end(), other.begin()); + } + + bool operator!=(const fbvector& other) const { + return !(*this == other); + } + + bool operator<(const fbvector& other) const { + return std::lexicographical_compare( + begin(), end(), other.begin(), other.end()); + } + + bool operator>(const fbvector& other) const { + return other < *this; + } + + bool operator<=(const fbvector& other) const { + return !(*this > other); + } + + bool operator>=(const fbvector& other) const { + return !(*this < other); + } + + //=========================================================================== + //--------------------------------------------------------------------------- + // friends + private: + template <class _T, class _A> + friend _T* relinquish(fbvector<_T, _A>&); + + template <class _T, class _A> + friend void attach(fbvector<_T, _A>&, _T* data, size_t sz, size_t cap); + +}; // class fbvector + +//============================================================================= +//----------------------------------------------------------------------------- +// specialized functions + +template <class T, class A> +void swap(fbvector<T, A>& lhs, fbvector<T, A>& rhs) noexcept { + lhs.swap(rhs); +} + +//============================================================================= +//----------------------------------------------------------------------------- +// other + +namespace detail { + +// Format support. +template <class T, class A> +struct IndexableTraits<fbvector<T, A>> + : public IndexableTraitsSeq<fbvector<T, A>> {}; + +} // namespace detail + +template <class T, class A> +void compactResize(fbvector<T, A>* v, size_t sz) { + v->resize(sz); + v->shrink_to_fit(); +} + +// DANGER +// +// relinquish and attach are not a members function specifically so that it is +// awkward to call them. It is very easy to shoot yourself in the foot with +// these functions. +// +// If you call relinquish, then it is your responsibility to free the data +// and the storage, both of which may have been generated in a non-standard +// way through the fbvector's allocator. +// +// If you call attach, it is your responsibility to ensure that the fbvector +// is fresh (size and capacity both zero), and that the supplied data is +// capable of being manipulated by the allocator. +// It is acceptable to supply a stack pointer IF: +// (1) The vector's data does not outlive the stack pointer. This includes +// extension of the data's life through a move operation. +// (2) The pointer has enough capacity that the vector will never be +// relocated. +// (3) Insert is not called on the vector; these functions have leeway to +// relocate the vector even if there is enough capacity. +// (4) A stack pointer is compatible with the fbvector's allocator. +// + +template <class T, class A> +T* relinquish(fbvector<T, A>& v) { + T* ret = v.data(); + v.impl_.b_ = v.impl_.e_ = v.impl_.z_ = nullptr; + return ret; +} + +template <class T, class A> +void attach(fbvector<T, A>& v, T* data, size_t sz, size_t cap) { + assert(v.data() == nullptr); + v.impl_.b_ = data; + v.impl_.e_ = data + sz; + v.impl_.z_ = data + cap; +} + +#if __cpp_deduction_guides >= 201703 +template < + class InputIt, + class Allocator = + std::allocator<typename std::iterator_traits<InputIt>::value_type>> +fbvector(InputIt, InputIt, Allocator = Allocator()) + ->fbvector<typename std::iterator_traits<InputIt>::value_type, Allocator>; +#endif + +template <class T, class A, class U> +void erase(fbvector<T, A>& v, U value) { + v.erase(std::remove(v.begin(), v.end(), value), v.end()); +} + +template <class T, class A, class Predicate> +void erase_if(fbvector<T, A>& v, Predicate predicate) { + v.erase(std::remove_if(v.begin(), v.end(), predicate), v.end()); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/File.cpp b/ios/Pods/Flipper-Folly/folly/File.cpp new file mode 100644 index 000000000..2bcd98cb7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/File.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/File.h> + +#include <folly/Exception.h> +#include <folly/FileUtil.h> +#include <folly/Format.h> +#include <folly/ScopeGuard.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/SysFile.h> +#include <folly/portability/Unistd.h> + +#include <system_error> + +#include <glog/logging.h> + +namespace folly { + +File::File() noexcept : fd_(-1), ownsFd_(false) {} + +File::File(int fd, bool ownsFd) noexcept : fd_(fd), ownsFd_(ownsFd) { + CHECK_GE(fd, -1) << "fd must be -1 or non-negative"; + CHECK(fd != -1 || !ownsFd) << "cannot own -1"; +} + +File::File(const char* name, int flags, mode_t mode) + : fd_(::open(name, flags, mode)), ownsFd_(false) { + if (fd_ == -1) { + throwSystemError( + folly::format("open(\"{}\", {:#o}, 0{:#o}) failed", name, flags, mode) + .fbstr()); + } + ownsFd_ = true; +} + +File::File(const std::string& name, int flags, mode_t mode) + : File(name.c_str(), flags, mode) {} + +File::File(StringPiece name, int flags, mode_t mode) + : File(name.str(), flags, mode) {} + +File::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) { + other.release(); +} + +File& File::operator=(File&& other) { + closeNoThrow(); + swap(other); + return *this; +} + +File::~File() { + auto fd = fd_; + if (!closeNoThrow()) { // ignore most errors + DCHECK_NE(errno, EBADF) + << "closing fd " << fd << ", it may already " + << "have been closed. Another time, this might close the wrong FD."; + } +} + +/* static */ File File::temporary() { + // make a temp file with tmpfile(), dup the fd, then return it in a File. + FILE* tmpFile = tmpfile(); + checkFopenError(tmpFile, "tmpfile() failed"); + SCOPE_EXIT { + fclose(tmpFile); + }; + + int fd = ::dup(fileno(tmpFile)); + checkUnixError(fd, "dup() failed"); + + return File(fd, true); +} + +int File::release() noexcept { + int released = fd_; + fd_ = -1; + ownsFd_ = false; + return released; +} + +void File::swap(File& other) noexcept { + using std::swap; + swap(fd_, other.fd_); + swap(ownsFd_, other.ownsFd_); +} + +void swap(File& a, File& b) noexcept { + a.swap(b); +} + +File File::dup() const { + if (fd_ != -1) { + int fd = ::dup(fd_); + checkUnixError(fd, "dup() failed"); + + return File(fd, true); + } + + return File(); +} + +void File::close() { + if (!closeNoThrow()) { + throwSystemError("close() failed"); + } +} + +bool File::closeNoThrow() { + int r = ownsFd_ ? ::close(fd_) : 0; + release(); + return r == 0; +} + +void File::lock() { + doLock(LOCK_EX); +} +bool File::try_lock() { + return doTryLock(LOCK_EX); +} +void File::lock_shared() { + doLock(LOCK_SH); +} +bool File::try_lock_shared() { + return doTryLock(LOCK_SH); +} + +void File::doLock(int op) { + checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)"); +} + +bool File::doTryLock(int op) { + int r = flockNoInt(fd_, op | LOCK_NB); + // flock returns EWOULDBLOCK if already locked + if (r == -1 && errno == EWOULDBLOCK) { + return false; + } + checkUnixError(r, "flock() failed (try_lock)"); + return true; +} + +void File::unlock() { + checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)"); +} +void File::unlock_shared() { + unlock(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/File.h b/ios/Pods/Flipper-Folly/folly/File.h new file mode 100644 index 000000000..d6766a339 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/File.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <string> +#include <system_error> + +#include <folly/ExceptionWrapper.h> +#include <folly/Expected.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +/** + * A File represents an open file. + */ +class File { + public: + /** + * Creates an empty File object, for late initialization. + */ + File() noexcept; + + /** + * Create a File object from an existing file descriptor. + * Takes ownership of the file descriptor if ownsFd is true. + */ + explicit File(int fd, bool ownsFd = false) noexcept; + + /** + * Open and create a file object. Throws on error. + * Owns the file descriptor implicitly. + */ + explicit File(const char* name, int flags = O_RDONLY, mode_t mode = 0666); + explicit File( + const std::string& name, + int flags = O_RDONLY, + mode_t mode = 0666); + explicit File(StringPiece name, int flags = O_RDONLY, mode_t mode = 0666); + + /** + * All the constructors that are not noexcept can throw std::system_error. + * This is a helper method to use folly::Expected to chain a file open event + * to something else you want to do with the open fd. + */ + template <typename... Args> + static Expected<File, exception_wrapper> makeFile(Args&&... args) noexcept { + try { + return File(std::forward<Args>(args)...); + } catch (const std::system_error& se) { + return makeUnexpected(exception_wrapper(std::current_exception(), se)); + } + } + + ~File(); + + /** + * Create and return a temporary, owned file (uses tmpfile()). + */ + static File temporary(); + + /** + * Return the file descriptor, or -1 if the file was closed. + */ + int fd() const { + return fd_; + } + + /** + * Returns 'true' iff the file was successfully opened. + */ + explicit operator bool() const { + return fd_ != -1; + } + + /** + * Duplicate file descriptor and return File that owns it. + */ + File dup() const; + + /** + * If we own the file descriptor, close the file and throw on error. + * Otherwise, do nothing. + */ + void close(); + + /** + * Closes the file (if owned). Returns true on success, false (and sets + * errno) on error. + */ + bool closeNoThrow(); + + /** + * Returns and releases the file descriptor; no longer owned by this File. + * Returns -1 if the File object didn't wrap a file. + */ + int release() noexcept; + + /** + * Swap this File with another. + */ + void swap(File& other) noexcept; + + // movable + File(File&&) noexcept; + File& operator=(File&&); + + // FLOCK (INTERPROCESS) LOCKS + // + // NOTE THAT THESE LOCKS ARE flock() LOCKS. That is, they may only be used + // for inter-process synchronization -- an attempt to acquire a second lock + // on the same file descriptor from the same process may succeed. Attempting + // to acquire a second lock on a different file descriptor for the same file + // should fail, but some systems might implement flock() using fcntl() locks, + // in which case it will succeed. + void lock(); + bool try_lock(); + void unlock(); + + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); + + private: + void doLock(int op); + bool doTryLock(int op); + + // unique + File(const File&) = delete; + File& operator=(const File&) = delete; + + int fd_; + bool ownsFd_; +}; + +void swap(File& a, File& b) noexcept; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/FileUtil.cpp b/ios/Pods/Flipper-Folly/folly/FileUtil.cpp new file mode 100644 index 000000000..f7200fbf1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FileUtil.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/FileUtil.h> + +#include <cerrno> +#include <string> +#include <system_error> +#include <vector> + +#include <folly/detail/FileUtilDetail.h> +#include <folly/net/NetOps.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Stdlib.h> +#include <folly/portability/SysFile.h> +#include <folly/portability/SysStat.h> + +namespace folly { + +using namespace fileutil_detail; + +int openNoInt(const char* name, int flags, mode_t mode) { + return int(wrapNoInt(open, name, flags, mode)); +} + +static int filterCloseReturn(int r) { + // Ignore EINTR. On Linux, close() may only return EINTR after the file + // descriptor has been closed, so you must not retry close() on EINTR -- + // in the best case, you'll get EBADF, and in the worst case, you'll end up + // closing a different file (one opened from another thread). + // + // Interestingly enough, the Single Unix Specification says that the state + // of the file descriptor is unspecified if close returns EINTR. In that + // case, the safe thing to do is also not to retry close() -- leaking a file + // descriptor is definitely better than closing the wrong file. + if (r == -1 && errno == EINTR) { + return 0; + } + return r; +} + +int closeNoInt(int fd) { + return filterCloseReturn(close(fd)); +} + +int closeNoInt(NetworkSocket fd) { + return filterCloseReturn(netops::close(fd)); +} + +int fsyncNoInt(int fd) { + return int(wrapNoInt(fsync, fd)); +} + +int dupNoInt(int fd) { + return int(wrapNoInt(dup, fd)); +} + +int dup2NoInt(int oldfd, int newfd) { + return int(wrapNoInt(dup2, oldfd, newfd)); +} + +int fdatasyncNoInt(int fd) { +#if defined(__APPLE__) + return int(wrapNoInt(fcntl, fd, F_FULLFSYNC)); +#elif defined(__FreeBSD__) || defined(_MSC_VER) + return int(wrapNoInt(fsync, fd)); +#else + return int(wrapNoInt(fdatasync, fd)); +#endif +} + +int ftruncateNoInt(int fd, off_t len) { + return int(wrapNoInt(ftruncate, fd, len)); +} + +int truncateNoInt(const char* path, off_t len) { + return int(wrapNoInt(truncate, path, len)); +} + +int flockNoInt(int fd, int operation) { + return int(wrapNoInt(flock, fd, operation)); +} + +int shutdownNoInt(NetworkSocket fd, int how) { + return int(wrapNoInt(netops::shutdown, fd, how)); +} + +ssize_t readNoInt(int fd, void* buf, size_t count) { + return wrapNoInt(read, fd, buf, count); +} + +ssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset) { + return wrapNoInt(pread, fd, buf, count, offset); +} + +ssize_t readvNoInt(int fd, const iovec* iov, int count) { + return wrapNoInt(readv, fd, iov, count); +} + +ssize_t preadvNoInt(int fd, const iovec* iov, int count, off_t offset) { + return wrapNoInt(preadv, fd, iov, count, offset); +} + +ssize_t writeNoInt(int fd, const void* buf, size_t count) { + return wrapNoInt(write, fd, buf, count); +} + +ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) { + return wrapNoInt(pwrite, fd, buf, count, offset); +} + +ssize_t writevNoInt(int fd, const iovec* iov, int count) { + return wrapNoInt(writev, fd, iov, count); +} + +ssize_t pwritevNoInt(int fd, const iovec* iov, int count, off_t offset) { + return wrapNoInt(pwritev, fd, iov, count, offset); +} + +ssize_t readFull(int fd, void* buf, size_t count) { + return wrapFull(read, fd, buf, count); +} + +ssize_t preadFull(int fd, void* buf, size_t count, off_t offset) { + return wrapFull(pread, fd, buf, count, offset); +} + +ssize_t writeFull(int fd, const void* buf, size_t count) { + return wrapFull(write, fd, const_cast<void*>(buf), count); +} + +ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) { + return wrapFull(pwrite, fd, const_cast<void*>(buf), count, offset); +} + +ssize_t readvFull(int fd, iovec* iov, int count) { + return wrapvFull(readv, fd, iov, count); +} + +ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) { + return wrapvFull(preadv, fd, iov, count, offset); +} + +ssize_t writevFull(int fd, iovec* iov, int count) { + return wrapvFull(writev, fd, iov, count); +} + +ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) { + return wrapvFull(pwritev, fd, iov, count, offset); +} + +int writeFileAtomicNoThrow( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions, + SyncType syncType) { + // We write the data to a temporary file name first, then atomically rename + // it into place. This ensures that the file contents will always be valid, + // even if we crash or are killed partway through writing out data. + // + // Create a buffer that will contain two things: + // - A nul-terminated version of the filename + // - The temporary file name + std::vector<char> pathBuffer; + // Note that we have to explicitly pass in the size here to make + // sure the nul byte gets included in the data. + constexpr folly::StringPiece suffix(".XXXXXX\0", 8); + pathBuffer.resize((2 * filename.size()) + 1 + suffix.size()); + // Copy in the filename and then a nul terminator + memcpy(pathBuffer.data(), filename.data(), filename.size()); + pathBuffer[filename.size()] = '\0'; + const char* const filenameCStr = pathBuffer.data(); + // Now prepare the temporary path template + char* const tempPath = pathBuffer.data() + filename.size() + 1; + memcpy(tempPath, filename.data(), filename.size()); + memcpy(tempPath + filename.size(), suffix.data(), suffix.size()); + + auto tmpFD = mkstemp(tempPath); + if (tmpFD == -1) { + return errno; + } + bool success = false; + SCOPE_EXIT { + if (tmpFD != -1) { + close(tmpFD); + } + if (!success) { + unlink(tempPath); + } + }; + + auto rc = writevFull(tmpFD, iov, count); + if (rc == -1) { + return errno; + } + + rc = fchmod(tmpFD, permissions); + if (rc == -1) { + return errno; + } + + // To guarantee atomicity across power failues on POSIX file systems, + // the temporary file must be explicitly sync'ed before the rename. + if (syncType == SyncType::WITH_SYNC) { + rc = fsyncNoInt(tmpFD); + if (rc == -1) { + return errno; + } + } + + // Close the file before renaming to make sure all data has + // been successfully written. + rc = close(tmpFD); + tmpFD = -1; + if (rc == -1) { + return errno; + } + + rc = rename(tempPath, filenameCStr); + if (rc == -1) { + return errno; + } + success = true; + return 0; +} + +void writeFileAtomic( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions, + SyncType syncType) { + auto rc = writeFileAtomicNoThrow(filename, iov, count, permissions, syncType); + if (rc != 0) { + auto msg = std::string(__func__) + "() failed to update " + filename.str(); + throw std::system_error(rc, std::generic_category(), msg); + } +} + +void writeFileAtomic( + StringPiece filename, + ByteRange data, + mode_t permissions, + SyncType syncType) { + iovec iov; + iov.iov_base = const_cast<unsigned char*>(data.data()); + iov.iov_len = data.size(); + writeFileAtomic(filename, &iov, 1, permissions, syncType); +} + +void writeFileAtomic( + StringPiece filename, + StringPiece data, + mode_t permissions, + SyncType syncType) { + writeFileAtomic(filename, ByteRange(data), permissions, syncType); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/FileUtil.h b/ios/Pods/Flipper-Folly/folly/FileUtil.h new file mode 100644 index 000000000..7d1968016 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FileUtil.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/stat.h> +#include <sys/types.h> + +#include <cassert> +#include <limits> + +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/ScopeGuard.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/SysUio.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +/** + * Convenience wrappers around some commonly used system calls. The *NoInt + * wrappers retry on EINTR. The *Full wrappers retry on EINTR and also loop + * until all data is written. Note that *Full wrappers weaken the thread + * semantics of underlying system calls. + */ +int openNoInt(const char* name, int flags, mode_t mode = 0666); +// Two overloads, as we may be closing either a file or a socket. +int closeNoInt(int fd); +int closeNoInt(NetworkSocket fd); +int dupNoInt(int fd); +int dup2NoInt(int oldfd, int newfd); +int fsyncNoInt(int fd); +int fdatasyncNoInt(int fd); +int ftruncateNoInt(int fd, off_t len); +int truncateNoInt(const char* path, off_t len); +int flockNoInt(int fd, int operation); +int shutdownNoInt(NetworkSocket fd, int how); + +ssize_t readNoInt(int fd, void* buf, size_t count); +ssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset); +ssize_t readvNoInt(int fd, const iovec* iov, int count); +ssize_t preadvNoInt(int fd, const iovec* iov, int count, off_t offset); + +ssize_t writeNoInt(int fd, const void* buf, size_t count); +ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset); +ssize_t writevNoInt(int fd, const iovec* iov, int count); +ssize_t pwritevNoInt(int fd, const iovec* iov, int count, off_t offset); + +/** + * Wrapper around read() (and pread()) that, in addition to retrying on + * EINTR, will loop until all data is read. + * + * This wrapper is only useful for blocking file descriptors (for non-blocking + * file descriptors, you have to be prepared to deal with incomplete reads + * anyway), and only exists because POSIX allows read() to return an incomplete + * read if interrupted by a signal (instead of returning -1 and setting errno + * to EINTR). + * + * Note that this wrapper weakens the thread safety of read(): the file pointer + * is shared between threads, but the system call is atomic. If multiple + * threads are reading from a file at the same time, you don't know where your + * data came from in the file, but you do know that the returned bytes were + * contiguous. You can no longer make this assumption if using readFull(). + * You should probably use pread() when reading from the same file descriptor + * from multiple threads simultaneously, anyway. + * + * Note that readvFull and preadvFull require iov to be non-const, unlike + * readv and preadv. The contents of iov after these functions return + * is unspecified. + */ +FOLLY_NODISCARD ssize_t readFull(int fd, void* buf, size_t count); +FOLLY_NODISCARD ssize_t +preadFull(int fd, void* buf, size_t count, off_t offset); +FOLLY_NODISCARD ssize_t readvFull(int fd, iovec* iov, int count); +FOLLY_NODISCARD ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset); + +/** + * Similar to readFull and preadFull above, wrappers around write() and + * pwrite() that loop until all data is written. + * + * Generally, the write() / pwrite() system call may always write fewer bytes + * than requested, just like read(). In certain cases (such as when writing to + * a pipe), POSIX provides stronger guarantees, but not in the general case. + * For example, Linux (even on a 64-bit platform) won't write more than 2GB in + * one write() system call. + * + * Note that writevFull and pwritevFull require iov to be non-const, unlike + * writev and pwritev. The contents of iov after these functions return + * is unspecified. + * + * These functions return -1 on error, or the total number of bytes written + * (which is always the same as the number of requested bytes) on success. + */ +ssize_t writeFull(int fd, const void* buf, size_t count); +ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset); +ssize_t writevFull(int fd, iovec* iov, int count); +ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset); + +/** + * Read entire file (if num_bytes is defaulted) or no more than + * num_bytes (otherwise) into container *out. The container is assumed + * to be contiguous, with element size equal to 1, and offer size(), + * reserve(), and random access (e.g. std::vector<char>, std::string, + * fbstring). + * + * Returns: true on success or false on failure. In the latter case + * errno will be set appropriately by the failing system primitive. + */ +template <class Container> +bool readFile( + int fd, + Container& out, + size_t num_bytes = std::numeric_limits<size_t>::max()) { + static_assert( + sizeof(out[0]) == 1, + "readFile: only containers with byte-sized elements accepted"); + + size_t soFar = 0; // amount of bytes successfully read + SCOPE_EXIT { + assert(out.size() >= soFar); // resize better doesn't throw + out.resize(soFar); + }; + + // Obtain file size: + struct stat buf; + if (fstat(fd, &buf) == -1) { + return false; + } + // Some files (notably under /proc and /sys on Linux) lie about + // their size, so treat the size advertised by fstat under advise + // but don't rely on it. In particular, if the size is zero, we + // should attempt to read stuff. If not zero, we'll attempt to read + // one extra byte. + constexpr size_t initialAlloc = 1024 * 4; + out.resize(std::min( + buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc, num_bytes)); + + while (soFar < out.size()) { + const auto actual = readFull(fd, &out[soFar], out.size() - soFar); + if (actual == -1) { + return false; + } + soFar += actual; + if (soFar < out.size()) { + // File exhausted + break; + } + // Ew, allocate more memory. Use exponential growth to avoid + // quadratic behavior. Cap size to num_bytes. + out.resize(std::min(out.size() * 3 / 2, num_bytes)); + } + + return true; +} + +/** + * Same as above, but takes in a file name instead of fd + */ +template <class Container> +bool readFile( + const char* file_name, + Container& out, + size_t num_bytes = std::numeric_limits<size_t>::max()) { + assert(file_name); + + const auto fd = openNoInt(file_name, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return false; + } + + SCOPE_EXIT { + // Ignore errors when closing the file + closeNoInt(fd); + }; + + return readFile(fd, out, num_bytes); +} + +/** + * Writes container to file. The container is assumed to be + * contiguous, with element size equal to 1, and offering STL-like + * methods empty(), size(), and indexed access + * (e.g. std::vector<char>, std::string, fbstring, StringPiece). + * + * "flags" dictates the open flags to use. Default is to create file + * if it doesn't exist and truncate it. + * + * Returns: true on success or false on failure. In the latter case + * errno will be set appropriately by the failing system primitive. + * + * Note that this function may leave the file in a partially written state on + * failure. Use writeFileAtomic() if you want to ensure that the existing file + * state will be unchanged on error. + */ +template <class Container> +bool writeFile( + const Container& data, + const char* filename, + int flags = O_WRONLY | O_CREAT | O_TRUNC, + mode_t mode = 0666) { + static_assert( + sizeof(data[0]) == 1, "writeFile works with element size equal to 1"); + int fd = open(filename, flags, mode); + if (fd == -1) { + return false; + } + bool ok = data.empty() || + writeFull(fd, &data[0], data.size()) == static_cast<ssize_t>(data.size()); + return closeNoInt(fd) == 0 && ok; +} + +/* For atomic writes, do we sync to guarantee ordering or not? */ +enum class SyncType { + WITH_SYNC, + WITHOUT_SYNC, +}; + +/** + * Write file contents "atomically". + * + * This writes the data to a temporary file in the destination directory, and + * then renames it to the specified path. This guarantees that the specified + * file will be replaced the the specified contents on success, or will not be + * modified on failure. + * + * Note that on platforms that do not provide atomic filesystem rename + * functionality (e.g., Windows) this behavior may not be truly atomic. + * + * The default implementation does not sync the data to storage before + * the rename. Therefore, the write is *not* atomic in the event of a + * power failure or OS crash. To guarantee atomicity in these cases, + * specify syncType = WITH_SYNC, which will incur a performance cost + * of waiting for the data to be persisted to storage. Note that the + * return of the function does not guarantee the directory + * modifications have been written to disk; a further sync of the + * directory after the function returns is required to ensure the + * modification is durable. + */ +void writeFileAtomic( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions = 0644, + SyncType syncType = SyncType::WITHOUT_SYNC); +void writeFileAtomic( + StringPiece filename, + ByteRange data, + mode_t permissions = 0644, + SyncType syncType = SyncType::WITHOUT_SYNC); +void writeFileAtomic( + StringPiece filename, + StringPiece data, + mode_t permissions = 0644, + SyncType syncType = SyncType::WITHOUT_SYNC); + +/** + * A version of writeFileAtomic() that returns an errno value instead of + * throwing on error. + * + * Returns 0 on success or an errno value on error. + */ +int writeFileAtomicNoThrow( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions = 0644, + SyncType syncType = SyncType::WITHOUT_SYNC); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Fingerprint.cpp b/ios/Pods/Flipper-Folly/folly/Fingerprint.cpp new file mode 100644 index 000000000..4457c00d6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Fingerprint.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Fingerprint.h> + +#include <folly/Portability.h> +#include <folly/Utility.h> +#include <folly/detail/FingerprintPolynomial.h> + +#include <utility> + +namespace folly { +namespace detail { + +namespace { + +// The polynomials below were generated by a separate program that requires the +// NTL (Number Theory Library) from http://www.shoup.net/ntl/ +// +// Briefly: randomly generate a polynomial of degree D, test for +// irreducibility, repeat until you find an irreducible polynomial +// (roughly 1/D of all polynomials of degree D are irreducible, so +// this will succeed in D/2 tries on average; D is small (64..128) so +// this simple method works well) +// +// DO NOT REPLACE THE POLYNOMIALS USED, EVER, as that would change the value +// of every single fingerprint in existence. +template <size_t Deg> +struct FingerprintTablePoly; +template <> +struct FingerprintTablePoly<63> { + static constexpr uint64_t data[1] = {0xbf3736b51869e9b7}; +}; +template <> +struct FingerprintTablePoly<95> { + static constexpr uint64_t data[2] = {0x51555cb0aa8d39c3, 0xb679ec3700000000}; +}; +template <> +struct FingerprintTablePoly<127> { + static constexpr uint64_t data[2] = {0xc91bff9b8768b51b, 0x8c5d5853bd77b0d3}; +}; + +template <typename D, size_t S0, size_t... I0> +constexpr auto copy_table(D const (&table)[S0], std::index_sequence<I0...>) { + using array = std::array<D, S0>; + return array{{table[I0]...}}; +} +template <typename D, size_t S0> +constexpr auto copy_table(D const (&table)[S0]) { + return copy_table(table, std::make_index_sequence<S0>{}); +} + +template <typename D, size_t S0, size_t S1, size_t... I0> +constexpr auto copy_table( + D const (&table)[S0][S1], + std::index_sequence<I0...>) { + using array = std::array<std::array<D, S1>, S0>; + return array{{copy_table(table[I0])...}}; +} +template <typename D, size_t S0, size_t S1> +constexpr auto copy_table(D const (&table)[S0][S1]) { + return copy_table(table, std::make_index_sequence<S0>{}); +} + +template <typename D, size_t S0, size_t S1, size_t S2, size_t... I0> +constexpr auto copy_table( + D const (&table)[S0][S1][S2], + std::index_sequence<I0...>) { + using array = std::array<std::array<std::array<D, S2>, S1>, S0>; + return array{{copy_table(table[I0])...}}; +} +template <typename D, size_t S0, size_t S1, size_t S2> +constexpr auto copy_table(D const (&table)[S0][S1][S2]) { + return copy_table(table, std::make_index_sequence<S0>{}); +} + +template <size_t Deg> +constexpr poly_table<Deg> make_poly_table() { + FingerprintPolynomial<Deg> poly(FingerprintTablePoly<Deg>::data); + uint64_t table[8][256][poly_size(Deg)] = {}; + // table[i][q] is Q(X) * X^(k+8*i) mod P(X), + // where k is the number of bits in the fingerprint (and deg(P)) and + // Q(X) = q7*X^7 + q6*X^6 + ... + q1*X + q0 is a degree-7 polyonomial + // whose coefficients are the bits of q. + for (uint16_t x = 0; x < 256; x++) { + FingerprintPolynomial<Deg> t; + t.setHigh8Bits(uint8_t(x)); + for (auto& entry : table) { + t.mulXkmod(8, poly); + for (size_t j = 0; j < poly_size(Deg); ++j) { + entry[x][j] = t.get(j); + } + } + } + return copy_table(table); +} + +// private global variables marked constexpr to enforce that make_poly_table is +// really invoked at constexpr time, which would not otherwise be guaranteed +FOLLY_STORAGE_CONSTEXPR auto const poly_table_63 = make_poly_table<63>(); +FOLLY_STORAGE_CONSTEXPR auto const poly_table_95 = make_poly_table<95>(); +FOLLY_STORAGE_CONSTEXPR auto const poly_table_127 = make_poly_table<127>(); + +} // namespace + +template <> +const uint64_t FingerprintTable<64>::poly[poly_size(64)] = { + FingerprintTablePoly<63>::data[0]}; +template <> +const uint64_t FingerprintTable<96>::poly[poly_size(96)] = { + FingerprintTablePoly<95>::data[0], + FingerprintTablePoly<95>::data[1]}; +template <> +const uint64_t FingerprintTable<128>::poly[poly_size(128)] = { + FingerprintTablePoly<127>::data[0], + FingerprintTablePoly<127>::data[1]}; + +template <> +const poly_table<64> FingerprintTable<64>::table = poly_table_63; +template <> +const poly_table<96> FingerprintTable<96>::table = poly_table_95; +template <> +const poly_table<128> FingerprintTable<128>::table = poly_table_127; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Fingerprint.h b/ios/Pods/Flipper-Folly/folly/Fingerprint.h new file mode 100644 index 000000000..568606228 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Fingerprint.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Compute 64-, 96-, and 128-bit Rabin fingerprints, as described in + * Michael O. Rabin (1981) + * Fingerprinting by Random Polynomials + * Center for Research in Computing Technology, Harvard University + * Tech Report TR-CSE-03-01 + * + * The implementation follows the optimization described in + * Andrei Z. Broder (1993) + * Some applications of Rabin's fingerprinting method + * + * extended for fingerprints larger than 64 bits, and modified to use + * 64-bit instead of 32-bit integers for computation. + * + * The precomputed tables are in Fingerprint.cpp. + * + * Benchmarked on 10/13/2009 on a 2.5GHz quad-core Xeon L5420, + * - Fingerprint<64>::update64() takes about 12ns + * - Fingerprint<96>::update64() takes about 30ns + * - Fingerprint<128>::update128() takes about 30ns + * (unsurprisingly, Fingerprint<96> and Fingerprint<128> take the + * same amount of time, as they both use 128-bit operations; the least + * significant 32 bits of Fingerprint<96> will always be 0) + * + * @author Tudor Bosman (tudorb@facebook.com) + */ + +#pragma once + +#include <array> +#include <cstdint> + +#include <folly/Range.h> + +namespace folly { + +namespace detail { + +constexpr size_t poly_size(size_t bits) { + return 1 + (bits - 1) / 64; +} + +template <size_t Deg> +using poly_table = + std::array<std::array<std::array<uint64_t, poly_size(Deg)>, 256>, 8>; + +template <int BITS> +struct FingerprintTable { + static const uint64_t poly[poly_size(BITS)]; + static const poly_table<BITS> table; +}; + +template <int BITS> +const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)] = {}; +template <int BITS> +const poly_table<BITS> FingerprintTable<BITS>::table = {}; + +#ifndef _MSC_VER +// MSVC as of 2017 can't handle these extern specialization declarations, +// but they aren't needed for things to work right, so we just don't +// declare them in the header for MSVC. + +#define FOLLY_DECLARE_FINGERPRINT_TABLES(BITS) \ + template <> \ + const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)]; \ + template <> \ + const poly_table<BITS> FingerprintTable<BITS>::table + +FOLLY_DECLARE_FINGERPRINT_TABLES(64); +FOLLY_DECLARE_FINGERPRINT_TABLES(96); +FOLLY_DECLARE_FINGERPRINT_TABLES(128); + +#undef FOLLY_DECLARE_FINGERPRINT_TABLES +#endif + +} // namespace detail + +/** + * Compute the Rabin fingerprint. + * + * TODO(tudorb): Extend this to allow removing values from the computed + * fingerprint (so we can fingerprint a sliding window, as in the Rabin-Karp + * string matching algorithm) + * + * update* methods return *this, so you can chain them together: + * Fingerprint<96>().update8(x).update(str).update64(val).write(output); + */ +template <int BITS> +class Fingerprint { + public: + Fingerprint() { + // Use a non-zero starting value. We'll use (1 << (BITS-1)) + fp_[0] = 1ULL << 63; + for (int i = 1; i < size(); i++) { + fp_[i] = 0; + } + } + + Fingerprint& update8(uint8_t v) { + uint8_t out = shlor8(v); + xortab(detail::FingerprintTable<BITS>::table[0][out]); + return *this; + } + + // update32 and update64 are convenience functions to update the fingerprint + // with 4 and 8 bytes at a time. They are faster than calling update8 + // in a loop. They process the bytes in big-endian order. + Fingerprint& update32(uint32_t v) { + uint32_t out = shlor32(v); + for (int i = 0; i < 4; i++) { + xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]); + out >>= 8; + } + return *this; + } + + Fingerprint& update64(uint64_t v) { + uint64_t out = shlor64(v); + for (int i = 0; i < 8; i++) { + xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]); + out >>= 8; + } + return *this; + } + + Fingerprint& update(StringPiece str) { + // TODO(tudorb): We could be smart and do update64 or update32 if aligned + for (auto c : str) { + update8(uint8_t(c)); + } + return *this; + } + + /** + * Return the number of uint64s needed to hold the fingerprint value. + */ + constexpr static int size() { + return detail::poly_size(BITS); + } + + /** + * Write the computed fingeprint to an array of size() uint64_t's. + * For Fingerprint<64>, size()==1; we write 64 bits in out[0] + * For Fingerprint<96>, size()==2; we write 64 bits in out[0] and + * the most significant 32 bits of out[1] + * For Fingerprint<128>, size()==2; we write 64 bits in out[0] and + * 64 bits in out[1]. + */ + void write(uint64_t* out) const { + for (int i = 0; i < size(); i++) { + out[i] = fp_[i]; + } + } + + private: + // XOR the fingerprint with a value from one of the tables. + void xortab(std::array<uint64_t, detail::poly_size(BITS)> const& tab) { + for (int i = 0; i < size(); i++) { + fp_[i] ^= tab[i]; + } + } + + // Helper functions: shift the fingerprint value left by 8/32/64 bits, + // return the "out" value (the bits that were shifted out), and add "v" + // in the bits on the right. + uint8_t shlor8(uint8_t v); + uint32_t shlor32(uint32_t v); + uint64_t shlor64(uint64_t v); + + uint64_t fp_[detail::poly_size(BITS)]; +}; + +// Convenience functions + +/** + * Return the 64-bit Rabin fingerprint of a string. + */ +inline uint64_t fingerprint64(StringPiece str) { + uint64_t fp; + Fingerprint<64>().update(str).write(&fp); + return fp; +} + +/** + * Compute the 96-bit Rabin fingerprint of a string. + * Return the 64 most significant bits in *msb, and the 32 least significant + * bits in *lsb. + */ +inline void fingerprint96(StringPiece str, uint64_t* msb, uint32_t* lsb) { + uint64_t fp[2]; + Fingerprint<96>().update(str).write(fp); + *msb = fp[0]; + *lsb = (uint32_t)(fp[1] >> 32); +} + +/** + * Compute the 128-bit Rabin fingerprint of a string. + * Return the 64 most significant bits in *msb, and the 64 least significant + * bits in *lsb. + */ +inline void fingerprint128(StringPiece str, uint64_t* msb, uint64_t* lsb) { + uint64_t fp[2]; + Fingerprint<128>().update(str).write(fp); + *msb = fp[0]; + *lsb = fp[1]; +} + +template <> +inline uint8_t Fingerprint<64>::shlor8(uint8_t v) { + uint8_t out = (uint8_t)(fp_[0] >> 56); + fp_[0] = (fp_[0] << 8) | ((uint64_t)v); + return out; +} + +template <> +inline uint32_t Fingerprint<64>::shlor32(uint32_t v) { + uint32_t out = (uint32_t)(fp_[0] >> 32); + fp_[0] = (fp_[0] << 32) | ((uint64_t)v); + return out; +} + +template <> +inline uint64_t Fingerprint<64>::shlor64(uint64_t v) { + uint64_t out = fp_[0]; + fp_[0] = v; + return out; +} + +template <> +inline uint8_t Fingerprint<96>::shlor8(uint8_t v) { + uint8_t out = (uint8_t)(fp_[0] >> 56); + fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56); + fp_[1] = (fp_[1] << 8) | ((uint64_t)v << 32); + return out; +} + +template <> +inline uint32_t Fingerprint<96>::shlor32(uint32_t v) { + uint32_t out = (uint32_t)(fp_[0] >> 32); + fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32); + fp_[1] = ((uint64_t)v << 32); + return out; +} + +template <> +inline uint64_t Fingerprint<96>::shlor64(uint64_t v) { + uint64_t out = fp_[0]; + fp_[0] = fp_[1] | (v >> 32); + fp_[1] = v << 32; + return out; +} + +template <> +inline uint8_t Fingerprint<128>::shlor8(uint8_t v) { + uint8_t out = (uint8_t)(fp_[0] >> 56); + fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56); + fp_[1] = (fp_[1] << 8) | ((uint64_t)v); + return out; +} + +template <> +inline uint32_t Fingerprint<128>::shlor32(uint32_t v) { + uint32_t out = (uint32_t)(fp_[0] >> 32); + fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32); + fp_[1] = (fp_[1] << 32) | ((uint64_t)v); + return out; +} + +template <> +inline uint64_t Fingerprint<128>::shlor64(uint64_t v) { + uint64_t out = fp_[0]; + fp_[0] = fp_[1]; + fp_[1] = v; + return out; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/FixedString.h b/ios/Pods/Flipper-Folly/folly/FixedString.h new file mode 100644 index 000000000..5a4276bdb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FixedString.h @@ -0,0 +1,3067 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Eric Niebler (eniebler) +// Fixed-size string type, for constexpr string handling. + +#pragma once + +#include <cassert> +#include <cstddef> +#include <initializer_list> +#include <iosfwd> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +#include <folly/ConstexprMath.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> +#include <folly/lang/Ordering.h> +#include <folly/portability/Constexpr.h> + +namespace folly { + +template <class Char, std::size_t N> +class BasicFixedString; + +template <std::size_t N> +using FixedString = BasicFixedString<char, N>; + +namespace detail { +namespace fixedstring { + +// This is a template so that the class static npos can be defined in the +// header. +template <class = void> +struct FixedStringBase_ { + static constexpr std::size_t npos = static_cast<std::size_t>(-1); +}; + +template <class Void> +constexpr std::size_t FixedStringBase_<Void>::npos; + +using FixedStringBase = FixedStringBase_<>; + +// Intentionally NOT constexpr. By making this not constexpr, we make +// checkOverflow below ill-formed in a constexpr context when the condition +// it's testing for fails. In this way, precondition violations are reported +// at compile-time instead of at runtime. +[[noreturn]] inline void assertOutOfBounds() { + assert(!"Array index out of bounds in BasicFixedString"); + throw_exception<std::out_of_range>( + "Array index out of bounds in BasicFixedString"); +} + +constexpr std::size_t checkOverflow(std::size_t i, std::size_t max) { + return i <= max ? i : (void(assertOutOfBounds()), max); +} + +constexpr std::size_t checkOverflowOrNpos(std::size_t i, std::size_t max) { + return i == FixedStringBase::npos + ? max + : (i <= max ? i : (void(assertOutOfBounds()), max)); +} + +constexpr std::size_t checkOverflowIfDebug(std::size_t i, std::size_t size) { + return kIsDebug ? checkOverflow(i, size) : i; +} + +// Intentionally NOT constexpr. See note above for assertOutOfBounds +[[noreturn]] inline void assertNotNullTerminated() noexcept { + assert(!"Non-null terminated string used to initialize a BasicFixedString"); + std::terminate(); // Fail hard, fail fast. +} + +// Parsing help for human readers: the following is a constexpr noexcept +// function that accepts a reference to an array as a parameter and returns +// a reference to the same array. +template <class Char, std::size_t N> +constexpr const Char (&checkNullTerminated(const Char (&a)[N]) noexcept)[N] { + // Strange decltype(a)(a) used to make MSVC happy. + return a[N - 1u] == Char(0) +#ifndef NDEBUG + // In Debug mode, guard against embedded nulls: + && N - 1u == folly::detail::constexpr_strlen_internal(a, 0u) +#endif + ? decltype(a)(a) + : (assertNotNullTerminated(), decltype(a)(a)); +} + +template <class Left, class Right> +constexpr ordering compare_( + const Left& left, + std::size_t left_pos, + std::size_t left_size, + const Right& right, + std::size_t right_pos, + std::size_t right_size) noexcept { + return left_pos == left_size + ? (right_pos == right_size ? ordering::eq : ordering::lt) + : (right_pos == right_size ? ordering::gt + : (left[left_pos] < right[right_pos] + ? ordering::lt + : (left[left_pos] > right[right_pos] + ? ordering::gt + : fixedstring::compare_( + left, + left_pos + 1u, + left_size, + right, + right_pos + 1u, + right_size)))); +} + +template <class Left, class Right> +constexpr bool equal_( + const Left& left, + std::size_t left_size, + const Right& right, + std::size_t right_size) noexcept { + return left_size == right_size && + ordering::eq == compare_(left, 0u, left_size, right, 0u, right_size); +} + +template <class Char, class Left, class Right> +constexpr Char char_at_( + const Left& left, + std::size_t left_count, + const Right& right, + std::size_t right_count, + std::size_t i) noexcept { + return i < left_count + ? left[i] + : i < (left_count + right_count) ? right[i - left_count] : Char(0); +} + +template <class Char, class Left, class Right> +constexpr Char char_at_( + const Left& left, + std::size_t left_size, + std::size_t left_pos, + std::size_t left_count, + const Right& right, + std::size_t right_pos, + std::size_t right_count, + std::size_t i) noexcept { + return i < left_pos + ? left[i] + : (i < right_count + left_pos ? right[i - left_pos + right_pos] + : (i < left_size - left_count + right_count + ? left[i - right_count + left_count] + : Char(0))); +} + +template <class Left, class Right> +constexpr bool find_at_( + const Left& left, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return 0u == count || + (left[pos + count - 1u] == right[count - 1u] && + find_at_(left, right, pos, count - 1u)); +} + +template <class Char, class Right> +constexpr bool +find_one_of_at_(Char ch, const Right& right, std::size_t pos) noexcept { + return 0u != pos && + (ch == right[pos - 1u] || find_one_of_at_(ch, right, pos - 1u)); +} + +template <class Left, class Right> +constexpr std::size_t find_( + const Left& left, + std::size_t left_size, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return find_at_(left, right, pos, count) ? pos + : left_size <= pos + count + ? FixedStringBase::npos + : find_(left, left_size, right, pos + 1u, count); +} + +template <class Left, class Right> +constexpr std::size_t rfind_( + const Left& left, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return find_at_(left, right, pos, count) + ? pos + : 0u == pos ? FixedStringBase::npos + : rfind_(left, right, pos - 1u, count); +} + +template <class Left, class Right> +constexpr std::size_t find_first_of_( + const Left& left, + std::size_t left_size, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return find_one_of_at_(left[pos], right, count) ? pos + : left_size <= pos + 1u + ? FixedStringBase::npos + : find_first_of_(left, left_size, right, pos + 1u, count); +} + +template <class Left, class Right> +constexpr std::size_t find_first_not_of_( + const Left& left, + std::size_t left_size, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return !find_one_of_at_(left[pos], right, count) ? pos + : left_size <= pos + 1u + ? FixedStringBase::npos + : find_first_not_of_(left, left_size, right, pos + 1u, count); +} + +template <class Left, class Right> +constexpr std::size_t find_last_of_( + const Left& left, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return find_one_of_at_(left[pos], right, count) + ? pos + : 0u == pos ? FixedStringBase::npos + : find_last_of_(left, right, pos - 1u, count); +} + +template <class Left, class Right> +constexpr std::size_t find_last_not_of_( + const Left& left, + const Right& right, + std::size_t pos, + std::size_t count) noexcept { + return !find_one_of_at_(left[pos], right, count) + ? pos + : 0u == pos ? FixedStringBase::npos + : find_last_not_of_(left, right, pos - 1u, count); +} + +struct Helper { + template <class Char, class Left, class Right, std::size_t... Is> + static constexpr BasicFixedString<Char, sizeof...(Is)> concat_( + const Left& left, + std::size_t left_count, + const Right& right, + std::size_t right_count, + std::index_sequence<Is...> is) noexcept { + return {left, left_count, right, right_count, is}; + } + + template <class Char, class Left, class Right, std::size_t... Is> + static constexpr BasicFixedString<Char, sizeof...(Is)> replace_( + const Left& left, + std::size_t left_size, + std::size_t left_pos, + std::size_t left_count, + const Right& right, + std::size_t right_pos, + std::size_t right_count, + std::index_sequence<Is...> is) noexcept { + return {left, + left_size, + left_pos, + left_count, + right, + right_pos, + right_count, + is}; + } + + template <class Char, std::size_t N> + static constexpr const Char ( + &data_(const BasicFixedString<Char, N>& that) noexcept)[N + 1u] { + return that.data_; + } +}; + +template <class T> +constexpr void constexpr_swap(T& a, T& b) noexcept( + noexcept(a = T(std::move(a)))) { + T tmp((std::move(a))); + a = std::move(b); + b = std::move(tmp); +} + +// For constexpr reverse iteration over a BasicFixedString +template <class T> +struct ReverseIterator { + private: + T* p_ = nullptr; + struct dummy_ { + T* p_ = nullptr; + }; + using other = typename std::conditional< + std::is_const<T>::value, + ReverseIterator<typename std::remove_const<T>::type>, + dummy_>::type; + + public: + using value_type = typename std::remove_const<T>::type; + using reference = T&; + using pointer = T*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr ReverseIterator() = default; + constexpr ReverseIterator(const ReverseIterator&) = default; + constexpr ReverseIterator& operator=(const ReverseIterator&) = default; + constexpr explicit ReverseIterator(T* p) noexcept : p_(p) {} + constexpr /* implicit */ ReverseIterator(const other& that) noexcept + : p_(that.p_) {} + friend constexpr bool operator==( + ReverseIterator a, + ReverseIterator b) noexcept { + return a.p_ == b.p_; + } + friend constexpr bool operator!=( + ReverseIterator a, + ReverseIterator b) noexcept { + return !(a == b); + } + constexpr reference operator*() const { + return *(p_ - 1); + } + constexpr ReverseIterator& operator++() noexcept { + --p_; + return *this; + } + constexpr ReverseIterator operator++(int) noexcept { + auto tmp(*this); + --p_; + return tmp; + } + constexpr ReverseIterator& operator--() noexcept { + ++p_; + return *this; + } + constexpr ReverseIterator operator--(int) noexcept { + auto tmp(*this); + ++p_; + return tmp; + } + constexpr ReverseIterator& operator+=(std::ptrdiff_t i) noexcept { + p_ -= i; + return *this; + } + friend constexpr ReverseIterator operator+( + std::ptrdiff_t i, + ReverseIterator that) noexcept { + return ReverseIterator{that.p_ - i}; + } + friend constexpr ReverseIterator operator+( + ReverseIterator that, + std::ptrdiff_t i) noexcept { + return ReverseIterator{that.p_ - i}; + } + constexpr ReverseIterator& operator-=(std::ptrdiff_t i) noexcept { + p_ += i; + return *this; + } + friend constexpr ReverseIterator operator-( + ReverseIterator that, + std::ptrdiff_t i) noexcept { + return ReverseIterator{that.p_ + i}; + } + friend constexpr std::ptrdiff_t operator-( + ReverseIterator a, + ReverseIterator b) noexcept { + return b.p_ - a.p_; + } + constexpr reference operator[](std::ptrdiff_t i) const noexcept { + return *(*this + i); + } +}; + +} // namespace fixedstring +} // namespace detail + +// Defined in folly/hash/Hash.h +std::uint32_t hsieh_hash32_buf(const void* buf, std::size_t len); + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** * + * \class BasicFixedString + * + * \tparam Char The character type. Must be a scalar type. + * \tparam N The capacity and max size of string instances of this type. + * + * \brief A class for holding up to `N` characters of type `Char` that is + * amenable to `constexpr` string manipulation. It is guaranteed to not + * perform any dynamic allocation. + * + * `BasicFixedString` is a `std::string` work-alike that stores characters in an + * internal buffer. It has minor interface differences that make it easy to work + * with strings in a `constexpr` context. + * + * \par Example: + * \par + * \code + * constexpr auto hello = makeFixedString("hello"); // a FixedString<5> + * constexpr auto world = makeFixedString("world"); // a FixedString<5> + * constexpr auto hello_world = hello + ' ' + world + '!'; // a FixedString<12> + * static_assert(hello_world == "hello world!", "neato!"); + * \endcode + * \par + * `FixedString<N>` is an alias for `BasicFixedString<char, N>`. + * + * \par Constexpr and In-place Mutation + * \par + * On a C++14 compiler, `BasicFixedString` supports the full `std::string` + * interface as `constexpr` member functions. On a C++11 compiler, the mutating + * members are not `constexpr`, but non-mutating alternatives, which create a + * new string, can be used instead. For example, instead of this: + * \par + * \code + * constexpr FixedString<10> replace_example_cpp14() { + * FixedString<10> test{"****"}; + * test.replace(1, 2, "!!!!"); + * return test; // returns "*!!!!*" + * } + * \endcode + * \par + * You might write this instead: + * \par + * \code + * constexpr FixedString<10> replace_example_cpp11() { + * // GNU compilers have an extension that make it possible to create + * // FixedString objects with a `""_fs` user-defined literal. + * using namespace folly; + * return makeFixedString("****").creplace(1, 2, "!!!!"); // "*!!!!*" + * } + * \endcode + * + * \par User-defined Literals + * Instead of using the `folly::makeFixedString` helper function, you can use + * a user-defined literal to make `FixedString` instances. The UDL feature of + * C++ has some limitations that make this less than ideal; you must tell the + * compiler roughly how many characters are in the string. The suffixes `_fs4`, + * `_fs8`, `_fs16`, `_fs32`, `_fs64`, and `_fs128` exist to create instances + * of types `FixedString<4>`, `FixedString<8>`, etc. For example: + * \par + * \code + * using namespace folly::string_literals; + * constexpr auto hello = "hello"_fs8; // A FixedString<8> containing "hello" + * \endcode + * \par + * See Error Handling below for what to expect when you try to exceed the + * capacity of a `FixedString` by storing too many characters in it. + * \par + * If your compiler supports GNU extensions, there is one additional suffix you + * can use: `_fs`. This suffix always creates `FixedString` objects of exactly + * the right size. For example: + * \par + * \code + * using namespace folly::string_literals; + * // NOTE: Only works on compilers with GNU extensions enabled. Clang and + * // gcc support this (-Wgnu-string-literal-operator-template): + * constexpr auto hello = "hello"_fs; // A FixedString<5> containing "hello" + * \endcode + * + * \par Error Handling: + * The capacity of a `BasicFixedString` is set at compile time. When the user + * asks the string to exceed its capacity, one of three things will happen, + * depending on the context: + *\par + * -# If the attempt is made while evaluating a constant expression, the + * program will fail to compile. + * -# Otherwise, if the program is being run in debug mode, it will `assert`. + * -# Otherwise, the failed operation will throw a `std::out_of_range` + * exception. + *\par + * This is also the case if an invalid offset is passed to any member function, + * or if `pop_back` or `cpop_back` is called on an empty `BasicFixedString`. + * + * Member functions documented as having preconditions will assert in Debug + * mode (`!defined(NDEBUG)`) on precondition failures. Those documented with + * \b Throws clauses will throw the specified exception on failure. Those with + * both a precondition and a \b Throws clause will assert in Debug and throw + * in Release mode. + */ +template <class Char, std::size_t N> +class BasicFixedString : private detail::fixedstring::FixedStringBase { + private: + template <class, std::size_t> + friend class BasicFixedString; + friend struct detail::fixedstring::Helper; + + // FUTURE: use constexpr_log2 to fold instantiations of BasicFixedString + // together. All BasicFixedString<C, N> instantiations could share the + // implementation of BasicFixedString<C, M>, where M is the next highest power + // of 2 after N. + // + // Also, because of alignment of the data_ and size_ members, N should never + // be smaller than `(alignof(std::size_t)/sizeof(C))-1` (-1 because of the + // null terminator). OR, create a specialization for BasicFixedString<C, 0u> + // that does not have a size_ member, since it is unnecessary. + Char data_[N + 1u]; // +1 for the null terminator + std::size_t size_; // Nbr of chars, not incl. null terminator. size_ <= N. + + using Indices = std::make_index_sequence<N>; + + template <class That, std::size_t... Is> + constexpr BasicFixedString( + const That& that, + std::size_t size, + std::index_sequence<Is...>, + std::size_t pos = 0, + std::size_t count = npos) noexcept + : data_{(Is < (size - pos) && Is < count ? that[Is + pos] : Char(0))..., + Char(0)}, + size_{folly::constexpr_min(size - pos, count)} {} + + template <std::size_t... Is> + constexpr BasicFixedString( + std::size_t count, + Char ch, + std::index_sequence<Is...>) noexcept + : data_{((Is < count) ? ch : Char(0))..., Char(0)}, size_{count} {} + + // Concatenation constructor + template <class Left, class Right, std::size_t... Is> + constexpr BasicFixedString( + const Left& left, + std::size_t left_size, + const Right& right, + std::size_t right_size, + std::index_sequence<Is...>) noexcept + : data_{detail::fixedstring::char_at_<Char>( + left, + left_size, + right, + right_size, + Is)..., + Char(0)}, + size_{left_size + right_size} {} + + // Replace constructor + template <class Left, class Right, std::size_t... Is> + constexpr BasicFixedString( + const Left& left, + std::size_t left_size, + std::size_t left_pos, + std::size_t left_count, + const Right& right, + std::size_t right_pos, + std::size_t right_count, + std::index_sequence<Is...>) noexcept + : data_{detail::fixedstring::char_at_<Char>( + left, + left_size, + left_pos, + left_count, + right, + right_pos, + right_count, + Is)..., + Char(0)}, + size_{left_size - left_count + right_count} {} + + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = Char&; + using const_reference = const Char&; + using pointer = Char*; + using const_pointer = const Char*; + using iterator = Char*; + using const_iterator = const Char*; + using reverse_iterator = detail::fixedstring::ReverseIterator<Char>; + using const_reverse_iterator = + detail::fixedstring::ReverseIterator<const Char>; + + using detail::fixedstring::FixedStringBase::npos; + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Default construct + * \post `size() == 0` + * \post `at(0) == Char(0)` + */ + constexpr BasicFixedString() : data_{}, size_{} {} + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Copy construct + * \post `size() == that.size()` + * \post `0 == strncmp(data(), that.data(), size())` + * \post `at(size()) == Char(0)` + */ + constexpr BasicFixedString(const BasicFixedString& /*that*/) = default; + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct from a differently-sized BasicFixedString + * \pre `that.size() <= N` + * \post `size() == that.size()` + * \post `0 == strncmp(data(), that.data(), size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when that.size() > N. When M <= N, this + * constructor will never throw. + * \note Conversions from larger-capacity BasicFixedString objects to smaller + * ones (`M > N`) are allowed as long as the *size()* of the source string + * is small enough. + */ + template <std::size_t M> + constexpr /* implicit */ BasicFixedString( + const BasicFixedString<Char, M>& that) noexcept(M <= N) + : BasicFixedString{that, 0u, that.size_} {} + + // Why is this deleted? To avoid confusion with the constructor that takes + // a const Char* and a count. + template <std::size_t M> + constexpr BasicFixedString( + const BasicFixedString<Char, M>& that, + std::size_t pos) noexcept(false) = delete; + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct from an BasicFixedString, an offset, and a count + * \param that The source string + * \param pos The starting position in `that` + * \param count The number of characters to copy. If `npos`, `count` is taken + * to be `that.size()-pos`. + * \pre `pos <= that.size()` + * \pre `count <= that.size()-pos && count <= N` + * \post `size() == count` + * \post `0 == strncmp(data(), that.data()+pos, size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when pos+count > that.size(), or when + * `count > N` + */ + template <std::size_t M> + constexpr BasicFixedString( + const BasicFixedString<Char, M>& that, + std::size_t pos, + std::size_t count) noexcept(false) + : BasicFixedString{ + that.data_, + that.size_, + std::make_index_sequence<(M < N ? M : N)>{}, + pos, + detail::fixedstring::checkOverflow( + detail::fixedstring::checkOverflowOrNpos( + count, + that.size_ - + detail::fixedstring::checkOverflow(pos, that.size_)), + N)} {} + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct from a string literal + * \pre `M-1 <= N` + * \pre `that[M-1] == Char(0)` + * \post `0 == strncmp(data(), that, M-1)` + * \post `size() == M-1` + * \post `at(size()) == Char(0)` + */ + template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type> + constexpr /* implicit */ BasicFixedString(const Char (&that)[M]) noexcept + : BasicFixedString{detail::fixedstring::checkNullTerminated(that), + M - 1u, + std::make_index_sequence<M - 1u>{}} {} + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct from a `const Char*` and count + * \pre `that` points to an array of at least `count` characters. + * \pre `count <= N` + * \post `size() == count` + * \post `0 == strncmp(data(), that, size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when count > N + */ + constexpr BasicFixedString(const Char* that, std::size_t count) noexcept( + false) + : BasicFixedString{that, + detail::fixedstring::checkOverflow(count, N), + Indices{}} {} + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct an BasicFixedString that contains `count` characters, all + * of which are `ch`. + * \pre `count <= N` + * \post `size() == count` + * \post `npos == find_first_not_of(ch)` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when count > N + */ + constexpr BasicFixedString(std::size_t count, Char ch) noexcept(false) + : BasicFixedString{detail::fixedstring::checkOverflow(count, N), + ch, + Indices{}} {} + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct an BasicFixedString from a `std::initializer_list` of + * characters. + * \pre `il.size() <= N` + * \post `size() == count` + * \post `0 == strncmp(data(), il.begin(), size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when il.size() > N + */ + constexpr BasicFixedString(std::initializer_list<Char> il) noexcept(false) + : BasicFixedString{il.begin(), il.size()} {} + + constexpr BasicFixedString& operator=(const BasicFixedString&) noexcept = + default; + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assign from a `BasicFixedString<Char, M>`. + * \pre `that.size() <= N` + * \post `size() == that.size()` + * \post `0 == strncmp(data(), that.begin(), size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when that.size() > N. When M <= N, this + * assignment operator will never throw. + * \note Assignments from larger-capacity BasicFixedString objects to smaller + * ones (`M > N`) are allowed as long as the *size* of the source string is + * small enough. + * \return `*this` + */ + template <std::size_t M> + constexpr BasicFixedString& operator=( + const BasicFixedString<Char, M>& that) noexcept(M <= N) { + detail::fixedstring::checkOverflow(that.size_, N); + size_ = that.copy(data_, that.size_); + data_[size_] = Char(0); + return *this; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assign from a null-terminated array of characters. + * \pre `M < N` + * \pre `that` has no embedded null characters + * \pre `that[M-1]==Char(0)` + * \post `size() == M-1` + * \post `0 == strncmp(data(), that, size())` + * \post `at(size()) == Char(0)` + * \return `*this` + */ + template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type> + constexpr BasicFixedString& operator=(const Char (&that)[M]) noexcept { + return assign(detail::fixedstring::checkNullTerminated(that), M - 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assign from an `initializer_list` of characters. + * \pre `il.size() <= N` + * \post `size() == il.size()` + * \post `0 == strncmp(data(), il.begin(), size())` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when il.size() > N + * \return `*this` + */ + constexpr BasicFixedString& operator=( + std::initializer_list<Char> il) noexcept(false) { + detail::fixedstring::checkOverflow(il.size(), N); + for (std::size_t i = 0u; i < il.size(); ++i) { + data_[i] = il.begin()[i]; + } + size_ = il.size(); + data_[size_] = Char(0); + return *this; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Conversion to folly::Range + * \return `Range<Char*>{begin(), end()}` + */ + constexpr Range<Char*> toRange() noexcept { + return {begin(), end()}; + } + + /** + * \overload + */ + constexpr Range<const Char*> toRange() const noexcept { + return {begin(), end()}; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Conversion to std::basic_string<Char> + * \return `std::basic_string<Char>{begin(), end()}` + */ + /* implicit */ operator std::basic_string<Char>() const noexcept(false) { + return std::basic_string<Char>{begin(), end()}; + } + + std::basic_string<Char> toStdString() const noexcept(false) { + return std::basic_string<Char>{begin(), end()}; + } + + // Think hard about whether this is a good idea. It's certainly better than + // an implicit conversion to `const Char*` since `delete "hi"_fs` will fail + // to compile. But it creates ambiguities when passing a FixedString to an + // API that has overloads for `const char*` and `folly::Range`, for instance. + // using ArrayType = Char[N]; + // constexpr /* implicit */ operator ArrayType&() noexcept { + // return data_; + // } + + // using ConstArrayType = const Char[N]; + // constexpr /* implicit */ operator ConstArrayType&() const noexcept { + // return data_; + // } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assigns a sequence of `count` characters of value `ch`. + * \param count The count of characters. + * \param ch + * \pre `count <= N` + * \post `size() == count` + * \post `npos == find_first_not_of(ch)` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when count > N + * \return `*this` + */ + constexpr BasicFixedString& assign(std::size_t count, Char ch) noexcept( + false) { + detail::fixedstring::checkOverflow(count, N); + for (std::size_t i = 0u; i < count; ++i) { + data_[i] = ch; + } + size_ = count; + data_[size_] = Char(0); + return *this; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assigns characters from an `BasicFixedString` to this object. + * \note Equivalent to `assign(that, 0, that.size())` + */ + template <std::size_t M> + constexpr BasicFixedString& assign( + const BasicFixedString<Char, M>& that) noexcept(M <= N) { + return *this = that; + } + + // Why is this overload deleted? So users aren't confused by the difference + // between str.assign("foo", N) and str.assign("foo"_fs, N). In the former, + // N is a count of characters. In the latter, it would be a position, which + // totally changes the meaning of the code. + template <std::size_t M> + constexpr BasicFixedString& assign( + const BasicFixedString<Char, M>& that, + std::size_t pos) noexcept(false) = delete; + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assigns `count` characters from an `BasicFixedString` to this object, + * starting at position `pos` in the source object. + * \param that The source string. + * \param pos The starting position in the source string. + * \param count The number of characters to copy. If `npos`, `count` is taken + * to be `that.size()-pos`. + * \pre `pos <= that.size()` + * \pre `count <= that.size()-pos` + * \pre `count <= N` + * \post `size() == count` + * \post `0 == strncmp(data(), that.begin() + pos, count)` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when pos > that.size() or count > that.size()-pos + * or count > N. + * \return `*this` + */ + template <std::size_t M> + constexpr BasicFixedString& assign( + const BasicFixedString<Char, M>& that, + std::size_t pos, + std::size_t count) noexcept(false) { + detail::fixedstring::checkOverflow(pos, that.size_); + return assign( + that.data_ + pos, + detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos)); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assigns characters from an `BasicFixedString` to this object. + * \pre `that` contains no embedded nulls. + * \pre `that[M-1] == Char(0)` + * \note Equivalent to `assign(that, M - 1)` + */ + template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type> + constexpr BasicFixedString& assign(const Char (&that)[M]) noexcept { + return assign(detail::fixedstring::checkNullTerminated(that), M - 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Assigns `count` characters from a range of characters to this object. + * \param that A pointer to a range of characters. + * \param count The number of characters to copy. + * \pre `that` points to at least `count` characters. + * \pre `count <= N` + * \post `size() == count` + * \post `0 == strncmp(data(), that, count)` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range when count > N + * \return `*this` + */ + constexpr BasicFixedString& assign( + const Char* that, + std::size_t count) noexcept(false) { + detail::fixedstring::checkOverflow(count, N); + for (std::size_t i = 0u; i < count; ++i) { + data_[i] = that[i]; + } + size_ = count; + data_[size_] = Char(0); + return *this; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Swap the contents of this string with `that`. + */ + constexpr void swap(BasicFixedString& that) noexcept { + // less-than-or-equal here to copy the null terminator: + for (std::size_t i = 0u; i <= folly::constexpr_max(size_, that.size_); + ++i) { + detail::fixedstring::constexpr_swap(data_[i], that.data_[i]); + } + detail::fixedstring::constexpr_swap(size_, that.size_); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Return a pointer to a range of `size()+1` characters, the last of which + * is `Char(0)`. + */ + constexpr Char* data() noexcept { + return data_; + } + + /** + * \overload + */ + constexpr const Char* data() const noexcept { + return data_; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * \return `data()`. + */ + constexpr const Char* c_str() const noexcept { + return data_; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * \return `data()`. + */ + constexpr Char* begin() noexcept { + return data_; + } + + /** + * \overload + */ + constexpr const Char* begin() const noexcept { + return data_; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * \return `data()`. + */ + constexpr const Char* cbegin() const noexcept { + return begin(); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * \return `data() + size()`. + */ + constexpr Char* end() noexcept { + return data_ + size_; + } + + /** + * \overload + */ + constexpr const Char* end() const noexcept { + return data_ + size_; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * \return `data() + size()`. + */ + constexpr const Char* cend() const noexcept { + return end(); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Returns a reverse iterator to the first character of the reversed string. + * It corresponds to the last + 1 character of the non-reversed string. + */ + constexpr reverse_iterator rbegin() noexcept { + return reverse_iterator{data_ + size_}; + } + + /** + * \overload + */ + constexpr const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator{data_ + size_}; + } + + /** + * \note Equivalent to `rbegin()` on a const-qualified reference to `*this`. + */ + constexpr const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Returns a reverse iterator to the last + 1 character of the reversed + * string. It corresponds to the first character of the non-reversed string. + */ + constexpr reverse_iterator rend() noexcept { + return reverse_iterator{data_}; + } + + /** + * \overload + */ + constexpr const_reverse_iterator rend() const noexcept { + return const_reverse_iterator{data_}; + } + + /** + * \note Equivalent to `rend()` on a const-qualified reference to `*this`. + */ + constexpr const_reverse_iterator crend() const noexcept { + return rend(); + } + + /** + * \return The number of `Char` elements in the string. + */ + constexpr std::size_t size() const noexcept { + return size_; + } + + /** + * \return The number of `Char` elements in the string. + */ + constexpr std::size_t length() const noexcept { + return size_; + } + + /** + * \return True if and only if `size() == 0`. + */ + constexpr bool empty() const noexcept { + return 0u == size_; + } + + /** + * \return `N`. + */ + static constexpr std::size_t capacity() noexcept { + return N; + } + + /** + * \return `N`. + */ + static constexpr std::size_t max_size() noexcept { + return N; + } + + // We would need to reimplement folly::Hash to make this + // constexpr. :-( + std::uint32_t hash() const noexcept { + return folly::hsieh_hash32_buf(data_, size_); + } + + /** + * \note `at(size())` is allowed will return `Char(0)`. + * \return `*(data() + i)` + * \throw std::out_of_range when i > size() + */ + constexpr Char& at(std::size_t i) noexcept(false) { + return i <= size_ ? data_[i] + : (throw_exception<std::out_of_range>( + "Out of range in BasicFixedString::at"), + data_[size_]); + } + + /** + * \overload + */ + constexpr const Char& at(std::size_t i) const noexcept(false) { + return i <= size_ ? data_[i] + : (throw_exception<std::out_of_range>( + "Out of range in BasicFixedString::at"), + data_[size_]); + } + + /** + * \pre `i <= size()` + * \note `(*this)[size()]` is allowed will return `Char(0)`. + * \return `*(data() + i)` + */ + constexpr Char& operator[](std::size_t i) noexcept { + return data_[detail::fixedstring::checkOverflowIfDebug(i, size_)]; + } + + /** + * \overload + */ + constexpr const Char& operator[](std::size_t i) const noexcept { + return data_[detail::fixedstring::checkOverflowIfDebug(i, size_)]; + } + + /** + * \note Equivalent to `(*this)[0]` + */ + constexpr Char& front() noexcept { + return (*this)[0u]; + } + + /** + * \overload + */ + constexpr const Char& front() const noexcept { + return (*this)[0u]; + } + + /** + * \note Equivalent to `at(size()-1)` + * \pre `!empty()` + */ + constexpr Char& back() noexcept { + return data_[size_ - detail::fixedstring::checkOverflowIfDebug(1u, size_)]; + } + + /** + * \overload + */ + constexpr const Char& back() const noexcept { + return data_[size_ - detail::fixedstring::checkOverflowIfDebug(1u, size_)]; + } + + /** + * Clears the contents of this string. + * \post `size() == 0u` + * \post `at(size()) == Char(0)` + */ + constexpr void clear() noexcept { + data_[0u] = Char(0); + size_ = 0u; + } + + /** + * \note Equivalent to `append(1u, ch)`. + */ + constexpr void push_back(Char ch) noexcept(false) { + detail::fixedstring::checkOverflow(1u, N - size_); + data_[size_] = ch; + data_[++size_] = Char(0); + } + + /** + * \note Equivalent to `cappend(1u, ch)`. + */ + constexpr BasicFixedString<Char, N + 1u> cpush_back(Char ch) const noexcept { + return cappend(ch); + } + + /** + * Removes the last character from the string. + * \pre `!empty()` + * \post `size()` is one fewer than before calling `pop_back()`. + * \post `at(size()) == Char(0)` + * \post The characters in the half-open range `[0,size()-1)` are unmodified. + * \throw std::out_of_range if empty(). + */ + constexpr void pop_back() noexcept(false) { + detail::fixedstring::checkOverflow(1u, size_); + --size_; + data_[size_] = Char(0); + } + + /** + * Returns a new string with the first `size()-1` characters from this string. + * \pre `!empty()` + * \note Equivalent to `BasicFixedString<Char, N-1u>{*this, 0u, size()-1u}` + * \throw std::out_of_range if empty(). + */ + constexpr BasicFixedString<Char, N - 1u> cpop_back() const noexcept(false) { + return {*this, 0u, size_ - detail::fixedstring::checkOverflow(1u, size_)}; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Appends `count` copies of `ch` to this string. + * \pre `count + old_size <= N` + * \post The first `old_size` characters of the string are unmodified. + * \post `size() == old_size + count` + * \throw std::out_of_range if count > N - size(). + */ + constexpr BasicFixedString& append(std::size_t count, Char ch) noexcept( + false) { + detail::fixedstring::checkOverflow(count, N - size_); + for (std::size_t i = 0u; i < count; ++i) { + data_[size_ + i] = ch; + } + size_ += count; + data_[size_] = Char(0); + return *this; + } + + /** + * \note Equivalent to `append(*this, 0, that.size())`. + */ + template <std::size_t M> + constexpr BasicFixedString& append( + const BasicFixedString<Char, M>& that) noexcept(false) { + return append(that, 0u, that.size_); + } + + // Why is this overload deleted? So as not to get confused with + // append("null-terminated", N), where N would be a count instead + // of a position. + template <std::size_t M> + constexpr BasicFixedString& append( + const BasicFixedString<Char, M>& that, + std::size_t pos) noexcept(false) = delete; + + /** + * Appends `count` characters from another string to this one, starting at a + * given offset, `pos`. + * \param that The source string. + * \param pos The starting position in the source string. + * \param count The number of characters to append. If `npos`, `count` is + * taken to be `that.size()-pos`. + * \pre `pos <= that.size()` + * \pre `count <= that.size() - pos` + * \pre `old_size + count <= N` + * \post The first `old_size` characters of the string are unmodified. + * \post `size() == old_size + count` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range if pos + count > that.size() or if + * `old_size + count > N`. + */ + template <std::size_t M> + constexpr BasicFixedString& append( + const BasicFixedString<Char, M>& that, + std::size_t pos, + std::size_t count) noexcept(false) { + detail::fixedstring::checkOverflow(pos, that.size_); + count = detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos); + detail::fixedstring::checkOverflow(count, N - size_); + for (std::size_t i = 0u; i < count; ++i) { + data_[size_ + i] = that.data_[pos + i]; + } + size_ += count; + data_[size_] = Char(0); + return *this; + } + + /** + * \note Equivalent to `append(that, strlen(that))`. + */ + constexpr BasicFixedString& append(const Char* that) noexcept(false) { + return append(that, folly::constexpr_strlen(that)); + } + + /** + * Appends `count` characters from the specified character array. + * \pre `that` points to a range of at least `count` characters. + * \pre `count + old_size <= N` + * \post The first `old_size` characters of the string are unmodified. + * \post `size() == old_size + count` + * \post `at(size()) == Char(0)` + * \throw std::out_of_range if old_size + count > N. + */ + constexpr BasicFixedString& append( + const Char* that, + std::size_t count) noexcept(false) { + detail::fixedstring::checkOverflow(count, N - size_); + for (std::size_t i = 0u; i < count; ++i) { + data_[size_ + i] = that[i]; + } + size_ += count; + data_[size_] = Char(0); + return *this; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Creates a new string by appending a character to an existing string, which + * is left unmodified. + * \note Equivalent to `*this + ch` + */ + constexpr BasicFixedString<Char, N + 1u> cappend(Char ch) const noexcept { + return *this + ch; + } + + /** + * Creates a new string by appending a string to an existing string, which + * is left unmodified. + * \note Equivalent to `*this + ch` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> cappend( + const BasicFixedString<Char, M>& that) const noexcept { + return *this + that; + } + + // Deleted to avoid confusion with append("char*", N), where N is a count + // instead of a position. + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> cappend( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) = delete; + + /** + * Creates a new string by appending characters from one string to another, + * which is left unmodified. + * \note Equivalent to `*this + that.substr(pos, count)` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> cappend( + const BasicFixedString<Char, M>& that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return creplace(size_, 0u, that, pos, count); + } + + /** + * Creates a new string by appending a string literal to a string, + * which is left unmodified. + * \note Equivalent to `*this + that` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> cappend( + const Char (&that)[M]) const noexcept { + return creplace(size_, 0u, that); + } + + // Deleted to avoid confusion with append("char*", N), where N is a count + // instead of a position + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> cappend( + const Char (&that)[M], + std::size_t pos) const noexcept(false) = delete; + + /** + * Creates a new string by appending characters from one string to another, + * which is left unmodified. + * \note Equivalent to `*this + makeFixedString(that).substr(pos, count)` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> + cappend(const Char (&that)[M], std::size_t pos, std::size_t count) const + noexcept(false) { + return creplace(size_, 0u, that, pos, count); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Appends characters from a null-terminated string literal to this string. + * \note Equivalent to `append(that)`. + */ + constexpr BasicFixedString& operator+=(const Char* that) noexcept(false) { + return append(that); + } + + /** + * Appends characters from another string to this one. + * \note Equivalent to `append(that)`. + */ + template <std::size_t M> + constexpr BasicFixedString& operator+=( + const BasicFixedString<Char, M>& that) noexcept(false) { + return append(that, 0u, that.size_); + } + + /** + * Appends a character to this string. + * \note Equivalent to `push_back(ch)`. + */ + constexpr BasicFixedString& operator+=(Char ch) noexcept(false) { + push_back(ch); + return *this; + } + + /** + * Appends characters from an `initializer_list` to this string. + * \note Equivalent to `append(il.begin(), il.size())`. + */ + constexpr BasicFixedString& operator+=( + std::initializer_list<Char> il) noexcept(false) { + return append(il.begin(), il.size()); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Erase all characters from this string. + * \note Equivalent to `clear()` + * \return *this; + */ + constexpr BasicFixedString& erase() noexcept { + clear(); + return *this; + } + + /** + * Erases `count` characters from position `pos`. If `count` is `npos`, + * erases from `pos` to the end of the string. + * \pre `pos <= size()` + * \pre `count <= size() - pos || count == npos` + * \post `size() == old_size - min(count, old_size - pos)` + * \post `at(size()) == Char(0)` + * \return *this; + * \throw std::out_of_range when pos > size(). + */ + constexpr BasicFixedString& erase( + std::size_t pos, + std::size_t count = npos) noexcept(false) { + using A = const Char[1]; + constexpr A a{Char(0)}; + return replace( + pos, + detail::fixedstring::checkOverflowOrNpos( + count, size_ - detail::fixedstring::checkOverflow(pos, size_)), + a, + 0u); + } + + /** + * \note Equivalent to `erase(first - data(), 1)` + * \return A pointer to the first character after the erased character. + */ + constexpr Char* erase(const Char* first) noexcept(false) { + erase(first - data_, 1u); + return data_ + (first - data_); + } + + /** + * \note Equivalent to `erase(first - data(), last - first)` + * \return A pointer to the first character after the erased characters. + */ + constexpr Char* erase(const Char* first, const Char* last) noexcept(false) { + erase(first - data_, last - first); + return data_ + (first - data_); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Create a new string by erasing all the characters from this string. + * \note Equivalent to `BasicFixedString<Char, 0>{}` + */ + constexpr BasicFixedString<Char, 0u> cerase() const noexcept { + return {}; + } + + /** + * Create a new string by erasing all the characters after position `pos` from + * this string. + * \note Equivalent to `creplace(pos, min(count, pos - size()), "")` + */ + constexpr BasicFixedString cerase(std::size_t pos, std::size_t count = npos) + const noexcept(false) { + using A = const Char[1]; + return creplace( + pos, + detail::fixedstring::checkOverflowOrNpos( + count, size_ - detail::fixedstring::checkOverflow(pos, size_)), + A{Char(0)}); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Compare two strings for lexicographical ordering. + * \note Equivalent to + * `compare(0, size(), that.data(), that.size())` + */ + template <std::size_t M> + constexpr int compare(const BasicFixedString<Char, M>& that) const noexcept { + return compare(0u, size_, that, 0u, that.size_); + } + + /** + * Compare two strings for lexicographical ordering. + * \note Equivalent to + * `compare(this_pos, this_count, that.data(), that.size())` + */ + template <std::size_t M> + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that) const noexcept(false) { + return compare(this_pos, this_count, that, 0u, that.size_); + } + + /** + * Compare two strings for lexicographical ordering. + * \note Equivalent to + * `compare(this_pos, this_count, that.data() + that_pos, that_count)` + */ + template <std::size_t M> + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that, + std::size_t that_pos, + std::size_t that_count) const noexcept(false) { + return static_cast<int>(detail::fixedstring::compare_( + data_, + detail::fixedstring::checkOverflow(this_pos, size_), + detail::fixedstring::checkOverflow(this_count, size_ - this_pos) + + this_pos, + that.data_, + detail::fixedstring::checkOverflow(that_pos, that.size_), + detail::fixedstring::checkOverflow(that_count, that.size_ - that_pos) + + that_pos)); + } + + /** + * Compare two strings for lexicographical ordering. + * \note Equivalent to `compare(0, size(), that, strlen(that))` + */ + constexpr int compare(const Char* that) const noexcept { + return compare(0u, size_, that, folly::constexpr_strlen(that)); + } + + /** + * \overload + */ + constexpr int compare(Range<const Char*> that) const noexcept { + return compare(0u, size_, that.begin(), that.size()); + } + + /** + * Compare two strings for lexicographical ordering. + * \note Equivalent to + * `compare(this_pos, this_count, that, strlen(that))` + */ + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + const Char* that) const noexcept(false) { + return compare(this_pos, this_count, that, folly::constexpr_strlen(that)); + } + + /** + * \overload + */ + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + Range<const Char*> that) const noexcept(false) { + return compare(this_pos, this_count, that.begin(), that.size()); + } + + /** + * Compare two strings for lexicographical ordering. + * + * Let `A` be the the + * character sequence {`(*this)[this_pos]`, ... + * `(*this)[this_pos + this_count - 1]`}. Let `B` be the character sequence + * {`that[0]`, ...`that[count - 1]`}. Then... + * + * \return + * - `< 0` if `A` is ordered before the `B` + * - `> 0` if `B` is ordered before `A` + * - `0` if `A` equals `B`. + * + * \throw std::out_of_range if this_pos + this_count > size(). + */ + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + const Char* that, + std::size_t that_count) const noexcept(false) { + return static_cast<int>(detail::fixedstring::compare_( + data_, + detail::fixedstring::checkOverflow(this_pos, size_), + detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos) + + this_pos, + that, + 0u, + that_count)); + } + + constexpr int compare( + std::size_t this_pos, + std::size_t this_count, + Range<const Char*> that, + std::size_t that_count) const noexcept(false) { + return compare( + this_pos, + this_count, + that.begin(), + detail::fixedstring::checkOverflow(that_count, that.size())); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Return a substring from `pos` to the end of the string. + * \note Equivalent to `BasicFixedString{*this, pos}` + */ + constexpr BasicFixedString substr(std::size_t pos) const noexcept(false) { + return {*this, pos}; + } + + /** + * Return a substring from `pos` to the end of the string. + * \note Equivalent to `BasicFixedString{*this, pos, count}` + */ + constexpr BasicFixedString substr(std::size_t pos, std::size_t count) const + noexcept(false) { + return {*this, pos, count}; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Replace the characters in the range denoted by the half-open range + * [`first`, `last`) with the string `that`. + * \pre `first` and `last` point to characters within this string (including + * the terminating null). + * \note Equivalent to + * `replace(first - data(), last - first, that.data(), that.size())` + */ + template <std::size_t M> + constexpr BasicFixedString& replace( + const Char* first, + const Char* last, + const BasicFixedString<Char, M>& that) noexcept(false) { + return replace(first - data_, last - first, that, 0u, that.size_); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with the + * characters from string `that` starting at position `that_pos`. + * \pre `that_pos <= that.size()` + * \note Equivalent to + * <tt>replace(this_pos, this_count, that.data() + that_pos, + * that.size() - that_pos)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString& replace( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that, + std::size_t that_pos = 0u) noexcept(false) { + return replace(this_pos, this_count, that, that_pos, that.size_ - that_pos); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with + * `that_count` characters from string `that` starting at position + * `that_pos`. + * \pre `that_pos <= that.size() && that_count <= that.size() - that_pos` + * \note Equivalent to + * `replace(this_pos, this_count, that.data() + that_pos, that_count)` + */ + template <std::size_t M> + constexpr BasicFixedString& replace( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that, + std::size_t that_pos, + std::size_t that_count) noexcept(false) { + return *this = creplace(this_pos, this_count, that, that_pos, that_count); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with + * the characters from the string literal `that`. + * \note Equivalent to + * `replace(this_pos, this_count, that, strlen(that))` + */ + constexpr BasicFixedString& replace( + std::size_t this_pos, + std::size_t this_count, + const Char* that) noexcept(false) { + return replace(this_pos, this_count, that, folly::constexpr_strlen(that)); + } + + /** + * Replace the characters denoted by the half-open range [`first`,`last`) with + * the characters from the string literal `that`. + * \pre `first` and `last` point to characters within this string (including + * the terminating null). + * \note Equivalent to + * `replace(first - data(), last - first, that, strlen(that))` + */ + constexpr BasicFixedString& replace( + const Char* first, + const Char* last, + const Char* that) noexcept(false) { + return replace( + first - data_, last - first, that, folly::constexpr_strlen(that)); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with + * `that_count` characters from the character sequence pointed to by `that`. + * \param this_pos The starting offset within `*this` of the first character + * to be replaced. + * \param this_count The number of characters to be replaced. If `npos`, + * it is treated as if `this_count` were `size() - this_pos`. + * \param that A pointer to the replacement string. + * \param that_count The number of characters in the replacement string. + * \pre `this_pos <= size() && this_count <= size() - this_pos` + * \pre `that` points to a contiguous sequence of at least `that_count` + * characters + * \throw std::out_of_range on any of the following conditions: + * - `this_pos > size()` + * - `this_count > size() - this_pos` + * - `size() - this_count + that_count > N` + */ + constexpr BasicFixedString& replace( + std::size_t this_pos, + std::size_t this_count, + const Char* that, + std::size_t that_count) noexcept(false) { + return *this = detail::fixedstring::Helper::replace_<Char>( + data_, + size_, + detail::fixedstring::checkOverflow(this_pos, size_), + detail::fixedstring::checkOverflowOrNpos( + this_count, size_ - this_pos), + that, + 0u, + that_count, + Indices{}); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with + * `that_count` characters `ch`. + * \note Equivalent to + * `replace(this_pos, this_count, BasicFixedString{that_count, ch})` + */ + constexpr BasicFixedString& replace( + std::size_t this_pos, + std::size_t this_count, + std::size_t that_count, + Char ch) noexcept(false) { + return replace(this_pos, this_count, BasicFixedString{that_count, ch}); + } + + /** + * Replace the characters denoted by the half-open range [`first`,`last`) + * with `that_count` characters `ch`. + * \note Equivalent to + * `replace(first - data(), last - first, BasicFixedString{that_count, ch})` + */ + constexpr BasicFixedString& replace( + const Char* first, + const Char* last, + std::size_t that_count, + Char ch) noexcept(false) { + return replace( + first - data_, last - first, BasicFixedString{that_count, ch}); + } + + /** + * Replace the characters denoted by the half-open range [`first`,`last`) with + * the characters from the string literal `that`. + * \pre `first` and `last` point to characters within this string (including + * the terminating null). + * \note Equivalent to + * `replace(this_pos, this_count, il.begin(), il.size())` + */ + constexpr BasicFixedString& replace( + const Char* first, + const Char* last, + std::initializer_list<Char> il) noexcept(false) { + return replace(first - data_, last - first, il.begin(), il.size()); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct a new string by replacing `this_count` characters starting from + * position `this_pos` within this string with the characters from string + * `that` starting at position `that_pos`. + * \pre `that_pos <= that.size()` + * \note Equivalent to + * <tt>creplace(this_pos, this_count, that, that_pos, + * that.size() - that_pos)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> creplace( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that, + std::size_t that_pos = 0u) const noexcept(false) { + return creplace( + this_pos, + this_count, + that, + that_pos, + that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_)); + } + + /** + * Construct a new string by replacing `this_count` characters starting from + * position `this_pos` within this string with `that_count` characters from + * string `that` starting at position `that_pos`. + * \param this_pos The starting offset within `*this` of the first character + * to be replaced. + * \param this_count The number of characters to be replaced. If `npos`, + * it is treated as if `this_count` were `size() - this_pos`. + * \param that A string that contains the replacement string. + * \param that_pos The offset to the first character in the replacement + * string. + * \param that_count The number of characters in the replacement string. + * \pre `this_pos <= size() && this_count <= size() - this_pos` + * \pre `that_pos <= that.size() && that_count <= that.size() - that_pos` + * \post The size of the returned string is `size() - this_count + that_count` + * \note Equivalent to <tt>BasicFixedString<Char, N + M>{substr(0, this_pos) + + * that.substr(that_pos, that_count) + substr(this_pos + this_count)}</tt> + * \throw std::out_of_range on any of the following conditions: + * - `this_pos > size()` + * - `this_count > size() - this_pos` + * - `that_pos > that.size()` + * - `that_count > that.size() - that_pos` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> creplace( + std::size_t this_pos, + std::size_t this_count, + const BasicFixedString<Char, M>& that, + std::size_t that_pos, + std::size_t that_count) const noexcept(false) { + return detail::fixedstring::Helper::replace_<Char>( + data_, + size_, + detail::fixedstring::checkOverflow(this_pos, size_), + detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos), + that.data_, + detail::fixedstring::checkOverflow(that_pos, that.size_), + detail::fixedstring::checkOverflowOrNpos( + that_count, that.size_ - that_pos), + std::make_index_sequence<N + M>{}); + } + + /** + * Construct a new string by replacing the characters denoted by the half-open + * range [`first`,`last`) within this string with the characters from string + * `that` starting at position `that_pos`. + * \pre `that_pos <= that.size()` + * \note Equivalent to + * <tt>creplace(first - data(), last - first, that, that_pos, + * that.size() - that_pos)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> creplace( + const Char* first, + const Char* last, + const BasicFixedString<Char, M>& that, + std::size_t that_pos = 0u) const noexcept(false) { + return creplace( + first - data_, + last - first, + that, + that_pos, + that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_)); + } + + /** + * Construct a new string by replacing the characters denoted by the half-open + * range [`first`,`last`) within this string with the `that_count` + * characters from string `that` starting at position `that_pos`. + * \note Equivalent to + * <tt>creplace(first - data(), last - first, that, that_pos, + * that_count)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M> creplace( + const Char* first, + const Char* last, + const BasicFixedString<Char, M>& that, + std::size_t that_pos, + std::size_t that_count) const noexcept(false) { + return creplace(first - data_, last - first, that, that_pos, that_count); + } + + /** + * Construct a new string by replacing `this_count` characters starting from + * position `this_pos` within this string with `M-1` characters from + * character array `that`. + * \pre `strlen(that) == M-1` + * \note Equivalent to + * <tt>creplace(this_pos, this_count, that, 0, M - 1)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> creplace( + std::size_t this_pos, + std::size_t this_count, + const Char (&that)[M]) const noexcept(false) { + return creplace(this_pos, this_count, that, 0u, M - 1u); + } + + /** + * Replace `this_count` characters starting from position `this_pos` with + * `that_count` characters from the character array `that` starting at + * position `that_pos`. + * \param this_pos The starting offset within `*this` of the first character + * to be replaced. + * \param this_count The number of characters to be replaced. If `npos`, + * it is treated as if `this_count` were `size() - this_pos`. + * \param that An array of characters containing the replacement string. + * \param that_pos The starting offset of the replacement string. + * \param that_count The number of characters in the replacement string. If + * `npos`, it is treated as if `that_count` were `M - 1 - that_pos` + * \pre `this_pos <= size() && this_count <= size() - this_pos` + * \pre `that_pos <= M - 1 && that_count <= M - 1 - that_pos` + * \post The size of the returned string is `size() - this_count + that_count` + * \note Equivalent to <tt>BasicFixedString<Char, N + M - 1>{ + * substr(0, this_pos) + + * makeFixedString(that).substr(that_pos, that_count) + + * substr(this_pos + this_count)}</tt> + * \throw std::out_of_range on any of the following conditions: + * - `this_pos > size()` + * - `this_count > size() - this_pos` + * - `that_pos >= M` + * - `that_count >= M - that_pos` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> creplace( + std::size_t this_pos, + std::size_t this_count, + const Char (&that)[M], + std::size_t that_pos, + std::size_t that_count) const noexcept(false) { + return detail::fixedstring::Helper::replace_<Char>( + data_, + size_, + detail::fixedstring::checkOverflow(this_pos, size_), + detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos), + detail::fixedstring::checkNullTerminated(that), + detail::fixedstring::checkOverflow(that_pos, M - 1u), + detail::fixedstring::checkOverflowOrNpos(that_count, M - 1u - that_pos), + std::make_index_sequence<N + M - 1u>{}); + } + + /** + * Construct a new string by replacing the characters denoted by the half-open + * range [`first`,`last`) within this string with the first `M-1` + * characters from the character array `that`. + * \pre `strlen(that) == M-1` + * \note Equivalent to + * <tt>creplace(first - data(), last - first, that, 0, M-1)</tt> + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> + creplace(const Char* first, const Char* last, const Char (&that)[M]) const + noexcept(false) { + return creplace(first - data_, last - first, that, 0u, M - 1u); + } + + /** + * Construct a new string by replacing the characters denoted by the half-open + * range [`first`,`last`) within this string with the `that_count` + * characters from the character array `that` starting at position + * `that_pos`. + * \pre `strlen(that) == M-1` + * \note Equivalent to + * `creplace(first - data(), last - first, that, that_pos, that_count)` + */ + template <std::size_t M> + constexpr BasicFixedString<Char, N + M - 1u> creplace( + const Char* first, + const Char* last, + const Char (&that)[M], + std::size_t that_pos, + std::size_t that_count) const noexcept(false) { + return creplace(first - data_, last - first, that, that_pos, that_count); + } + + /** + * Copies `min(count, size())` characters starting from offset `0` + * from this string into the buffer pointed to by `dest`. + * \return The number of characters copied. + */ + constexpr std::size_t copy(Char* dest, std::size_t count) const noexcept { + return copy(dest, count, 0u); + } + + /** + * Copies `min(count, size() - pos)` characters starting from offset `pos` + * from this string into the buffer pointed to by `dest`. + * \pre `pos <= size()` + * \return The number of characters copied. + * \throw std::out_of_range if `pos > size()` + */ + constexpr std::size_t copy(Char* dest, std::size_t count, std::size_t pos) + const noexcept(false) { + detail::fixedstring::checkOverflow(pos, size_); + for (std::size_t i = 0u; i < count; ++i) { + if (i + pos == size_) { + return size_; + } + dest[i] = data_[i + pos]; + } + return count; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Resizes the current string. + * \note Equivalent to `resize(count, Char(0))` + */ + constexpr void resize(std::size_t count) noexcept(false) { + resize(count, Char(0)); + } + + /** + * Resizes the current string by setting the size to `count` and setting + * `data()[count]` to `Char(0)`. If `count > old_size`, the characters + * in the range [`old_size`,`count`) are set to `ch`. + */ + constexpr void resize(std::size_t count, Char ch) noexcept(false) { + detail::fixedstring::checkOverflow(count, N); + if (count == size_) { + } else if (count < size_) { + size_ = count; + data_[size_] = Char(0); + } else { + for (; size_ < count; ++size_) { + data_[size_] = ch; + } + data_[size_] = Char(0); + } + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the first occurrence of the character sequence `that` in this string. + * \note Equivalent to `find(that.data(), 0, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find(const BasicFixedString<Char, M>& that) const + noexcept { + return find(that, 0u); + } + + /** + * Finds the first occurrence of the character sequence `that` in this string, + * starting at offset `pos`. + * \pre `pos <= size()` + * \note Equivalent to `find(that.data(), pos, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return that.size_ <= size_ - detail::fixedstring::checkOverflow(pos, size_) + ? detail::fixedstring::find_(data_, size_, that.data_, pos, that.size_) + : npos; + } + + /** + * Finds the first occurrence of the character sequence `that` in this string. + * \note Equivalent to `find(that.data(), 0, strlen(that))` + */ + constexpr std::size_t find(const Char* that) const noexcept { + return find(that, 0u, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of the character sequence `that` in this string, + * starting at offset `pos`. + * \pre `pos <= size()` + * \note Equivalent to `find(that.data(), pos, strlen(that))` + */ + constexpr std::size_t find(const Char* that, std::size_t pos) const + noexcept(false) { + return find(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of the first `count` characters in the buffer + * pointed to by `that` in this string, starting at offset `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The lowest offset `i` such that `i >= pos` and + * `0 == strncmp(data() + i, that, count)`; or `npos` if there is no such + * offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t find( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return count <= size_ - detail::fixedstring::checkOverflow(pos, size_) + ? detail::fixedstring::find_(data_, size_, that, pos, count) + : npos; + } + + /** + * Finds the first occurrence of the character `ch` in this string. + * \note Equivalent to `find(&ch, 0, 1)` + */ + constexpr std::size_t find(Char ch) const noexcept { + return find(ch, 0u); + } + + /** + * Finds the first occurrence of the character character `c` in this string, + * starting at offset `pos`. + * \pre `pos <= size()` + * \note Equivalent to `find(&ch, pos, 1)` + */ + constexpr std::size_t find(Char ch, std::size_t pos) const noexcept(false) { + using A = const Char[1u]; + return 0u == size_ - detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_(data_, size_, A{ch}, pos, 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the last occurrence of characters in the string + * `that` in this string. + * \note Equivalent to `rfind(that.data(), size(), that.size())` + */ + template <std::size_t M> + constexpr std::size_t rfind(const BasicFixedString<Char, M>& that) const + noexcept { + return rfind(that, size_); + } + + /** + * Finds the last occurrence of characters in the string + * `that` in this string, starting at offset `pos`. + * \note Equivalent to `rfind(that.data(), pos, that.size())` + */ + template <std::size_t M> + constexpr std::size_t rfind( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return that.size_ <= size_ + ? detail::fixedstring::rfind_( + data_, + that.data_, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), + size_ - that.size_), + that.size_) + : npos; + } + + /** + * Finds the last occurrence of characters in the buffer + * pointed to by `that` in this string. + * \note Equivalent to `rfind(that, size(), strlen(that))` + */ + constexpr std::size_t rfind(const Char* that) const noexcept { + return rfind(that, size_, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of characters in the buffer + * pointed to by `that` in this string, starting at offset `pos`. + * \note Equivalent to `rfind(that, pos, strlen(that))` + */ + constexpr std::size_t rfind(const Char* that, std::size_t pos) const + noexcept(false) { + return rfind(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of the first `count` characters in the buffer + * pointed to by `that` in this string, starting at offset `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The largest offset `i` such that `i <= pos` and + * `i + count <= size()` and `0 == strncmp(data() + i, that, count)`; or + * `npos` if there is no such offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t rfind( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return count <= size_ + ? detail::fixedstring::rfind_( + data_, + that, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), + size_ - count), + count) + : npos; + } + + /** + * Finds the last occurrence of the character character `ch` in this string. + * \note Equivalent to `rfind(&ch, size(), 1)` + */ + constexpr std::size_t rfind(Char ch) const noexcept { + return rfind(ch, size_); + } + + /** + * Finds the last occurrence of the character character `ch` in this string, + * starting at offset `pos`. + * \pre `pos <= size()` + * \note Equivalent to `rfind(&ch, pos, 1)` + */ + constexpr std::size_t rfind(Char ch, std::size_t pos) const noexcept(false) { + using A = const Char[1u]; + return 0u == size_ + ? npos + : detail::fixedstring::rfind_( + data_, + A{ch}, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the first occurrence of any character in `that` in this string. + * \note Equivalent to `find_first_of(that.data(), 0, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_first_of( + const BasicFixedString<Char, M>& that) const noexcept { + return find_first_of(that, 0u); + } + + /** + * Finds the first occurrence of any character in `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_first_of(that.data(), pos, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_first_of( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return size_ == detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_first_of_( + data_, size_, that.data_, pos, that.size_); + } + + /** + * Finds the first occurrence of any character in the null-terminated + * character sequence pointed to by `that` in this string. + * \note Equivalent to `find_first_of(that, 0, strlen(that))` + */ + constexpr std::size_t find_first_of(const Char* that) const noexcept { + return find_first_of(that, 0u, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of any character in the null-terminated + * character sequence pointed to by `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_first_of(that, pos, strlen(that))` + */ + constexpr std::size_t find_first_of(const Char* that, std::size_t pos) const + noexcept(false) { + return find_first_of(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of any character in the first `count` characters + * in the buffer pointed to by `that` in this string, starting at offset + * `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The smallest offset `i` such that `i >= pos` and + * `std::find(that, that+count, at(i)) != that+count`; or + * `npos` if there is no such offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t find_first_of( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return size_ == detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_first_of_(data_, size_, that, pos, count); + } + + /** + * Finds the first occurrence of `ch` in this string. + * \note Equivalent to `find_first_of(&ch, 0, 1)` + */ + constexpr std::size_t find_first_of(Char ch) const noexcept { + return find_first_of(ch, 0u); + } + + /** + * Finds the first occurrence of `ch` in this string, + * starting at offset `pos`. + * \note Equivalent to `find_first_of(&ch, pos, 1)` + */ + constexpr std::size_t find_first_of(Char ch, std::size_t pos) const + noexcept(false) { + using A = const Char[1u]; + return size_ == detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_first_of_(data_, size_, A{ch}, pos, 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the first occurrence of any character not in `that` in this string. + * \note Equivalent to `find_first_not_of(that.data(), 0, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_first_not_of( + const BasicFixedString<Char, M>& that) const noexcept { + return find_first_not_of(that, 0u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the first occurrence of any character not in `that` in this string. + * \note Equivalent to `find_first_not_of(that.data(), 0, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_first_not_of( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return size_ == detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_first_not_of_( + data_, size_, that.data_, pos, that.size_); + } + + /** + * Finds the first occurrence of any character not in the null-terminated + * character sequence pointed to by `that` in this string. + * \note Equivalent to `find_first_not_of(that, 0, strlen(that))` + */ + constexpr std::size_t find_first_not_of(const Char* that) const noexcept { + return find_first_not_of(that, 0u, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of any character not in the null-terminated + * character sequence pointed to by `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_first_not_of(that, pos, strlen(that))` + */ + constexpr std::size_t find_first_not_of(const Char* that, std::size_t pos) + const noexcept(false) { + return find_first_not_of(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the first occurrence of any character not in the first `count` + * characters in the buffer pointed to by `that` in this string, starting at + * offset `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The smallest offset `i` such that `i >= pos` and + * `std::find(that, that+count, at(i)) == that+count`; or + * `npos` if there is no such offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t find_first_not_of( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return size_ == detail::fixedstring::checkOverflow(pos, size_) + ? npos + : detail::fixedstring::find_first_not_of_( + data_, size_, that, pos, count); + } + + /** + * Finds the first occurrence of any character other than `ch` in this string. + * \note Equivalent to `find_first_not_of(&ch, 0, 1)` + */ + constexpr std::size_t find_first_not_of(Char ch) const noexcept { + return find_first_not_of(ch, 0u); + } + + /** + * Finds the first occurrence of any character other than `ch` in this string, + * starting at offset `pos`. + * \note Equivalent to `find_first_not_of(&ch, pos, 1)` + */ + constexpr std::size_t find_first_not_of(Char ch, std::size_t pos) const + noexcept(false) { + using A = const Char[1u]; + return 1u <= size_ - detail::fixedstring::checkOverflow(pos, size_) + ? detail::fixedstring::find_first_not_of_(data_, size_, A{ch}, pos, 1u) + : npos; + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the last occurrence of any character in `that` in this string. + * \note Equivalent to `find_last_of(that.data(), size(), that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_last_of( + const BasicFixedString<Char, M>& that) const noexcept { + return find_last_of(that, size_); + } + + /** + * Finds the last occurrence of any character in `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_last_of(that.data(), pos, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_last_of( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return 0u == size_ + ? npos + : detail::fixedstring::find_last_of_( + data_, + that.data_, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + that.size_); + } + + /** + * Finds the last occurrence of any character in the null-terminated + * character sequence pointed to by `that` in this string. + * \note Equivalent to `find_last_of(that, size(), strlen(that))` + */ + constexpr std::size_t find_last_of(const Char* that) const noexcept { + return find_last_of(that, size_, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of any character in the null-terminated + * character sequence pointed to by `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_last_of(that, pos, strlen(that))` + */ + constexpr std::size_t find_last_of(const Char* that, std::size_t pos) const + noexcept(false) { + return find_last_of(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of any character in the first `count` characters + * in the buffer pointed to by `that` in this string, starting at offset + * `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The largest offset `i` such that `i <= pos` and + * `i < size()` and `std::find(that, that+count, at(i)) != that+count`; or + * `npos` if there is no such offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t find_last_of( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return 0u == size_ + ? npos + : detail::fixedstring::find_last_of_( + data_, + that, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + count); + } + + /** + * Finds the last occurrence of `ch` in this string. + * \note Equivalent to `find_last_of(&ch, size(), 1)` + */ + constexpr std::size_t find_last_of(Char ch) const noexcept { + return find_last_of(ch, size_); + } + + /** + * Finds the last occurrence of `ch` in this string, + * starting at offset `pos`. + * \note Equivalent to `find_last_of(&ch, pos, 1)` + */ + constexpr std::size_t find_last_of(Char ch, std::size_t pos) const + noexcept(false) { + using A = const Char[1u]; + return 0u == size_ + ? npos + : detail::fixedstring::find_last_of_( + data_, + A{ch}, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Finds the last occurrence of any character not in `that` in this string. + * \note Equivalent to `find_last_not_of(that.data(), size(), that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_last_not_of( + const BasicFixedString<Char, M>& that) const noexcept { + return find_last_not_of(that, size_); + } + + /** + * Finds the last occurrence of any character not in `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_last_not_of(that.data(), pos, that.size())` + */ + template <std::size_t M> + constexpr std::size_t find_last_not_of( + const BasicFixedString<Char, M>& that, + std::size_t pos) const noexcept(false) { + return 0u == size_ + ? npos + : detail::fixedstring::find_last_not_of_( + data_, + that.data_, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + that.size_); + } + + /** + * Finds the last occurrence of any character not in the null-terminated + * character sequence pointed to by `that` in this string. + * \note Equivalent to `find_last_not_of(that, size(), strlen(that))` + */ + constexpr std::size_t find_last_not_of(const Char* that) const noexcept { + return find_last_not_of(that, size_, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of any character not in the null-terminated + * character sequence pointed to by `that` in this string, + * starting at offset `pos` + * \note Equivalent to `find_last_not_of(that, pos, strlen(that))` + */ + constexpr std::size_t find_last_not_of(const Char* that, std::size_t pos) + const noexcept(false) { + return find_last_not_of(that, pos, folly::constexpr_strlen(that)); + } + + /** + * Finds the last occurrence of any character not in the first `count` + * characters in the buffer pointed to by `that` in this string, starting at + * offset `pos`. + * \pre `pos <= size()` + * \pre `that` points to a buffer containing at least `count` contiguous + * characters. + * \return The largest offset `i` such that `i <= pos` and + * `i < size()` and `std::find(that, that+count, at(i)) == that+count`; or + * `npos` if there is no such offset `i`. + * \throw std::out_of_range when `pos > size()` + */ + constexpr std::size_t find_last_not_of( + const Char* that, + std::size_t pos, + std::size_t count) const noexcept(false) { + return 0u == size_ + ? npos + : detail::fixedstring::find_last_not_of_( + data_, + that, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + count); + } + + /** + * Finds the last occurrence of any character other than `ch` in this string. + * \note Equivalent to `find_last_not_of(&ch, size(), 1)` + */ + constexpr std::size_t find_last_not_of(Char ch) const noexcept { + return find_last_not_of(ch, size_); + } + + /** + * Finds the last occurrence of any character other than `ch` in this string, + * starting at offset `pos`. + * \note Equivalent to `find_last_not_of(&ch, pos, 1)` + */ + constexpr std::size_t find_last_not_of(Char ch, std::size_t pos) const + noexcept(false) { + using A = const Char[1u]; + return 0u == size_ + ? npos + : detail::fixedstring::find_last_not_of_( + data_, + A{ch}, + folly::constexpr_min( + detail::fixedstring::checkOverflow(pos, size_), size_ - 1u), + 1u); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Asymmetric relational operators + */ + friend constexpr bool operator==( + const Char* a, + const BasicFixedString& b) noexcept { + return detail::fixedstring::equal_( + a, folly::constexpr_strlen(a), b.data_, b.size_); + } + + /** + * \overload + */ + friend constexpr bool operator==( + const BasicFixedString& a, + const Char* b) noexcept { + return b == a; + } + + /** + * \overload + */ + friend constexpr bool operator==( + Range<const Char*> a, + const BasicFixedString& b) noexcept { + return detail::fixedstring::equal_(a.begin(), a.size(), b.data_, b.size_); + } + + /** + * \overload + */ + friend constexpr bool operator==( + const BasicFixedString& a, + Range<const Char*> b) noexcept { + return b == a; + } + + friend constexpr bool operator!=( + const Char* a, + const BasicFixedString& b) noexcept { + return !(a == b); + } + + /** + * \overload + */ + friend constexpr bool operator!=( + const BasicFixedString& a, + const Char* b) noexcept { + return !(b == a); + } + + /** + * \overload + */ + friend constexpr bool operator!=( + Range<const Char*> a, + const BasicFixedString& b) noexcept { + return !(a == b); + } + + /** + * \overload + */ + friend constexpr bool operator!=( + const BasicFixedString& a, + Range<const Char*> b) noexcept { + return !(a == b); + } + + friend constexpr bool operator<( + const Char* a, + const BasicFixedString& b) noexcept { + return ordering::lt == + detail::fixedstring::compare_( + a, 0u, folly::constexpr_strlen(a), b.data_, 0u, b.size_); + } + + /** + * \overload + */ + friend constexpr bool operator<( + const BasicFixedString& a, + const Char* b) noexcept { + return ordering::lt == + detail::fixedstring::compare_( + a.data_, 0u, a.size_, b, 0u, folly::constexpr_strlen(b)); + } + + /** + * \overload + */ + friend constexpr bool operator<( + Range<const Char*> a, + const BasicFixedString& b) noexcept { + return ordering::lt == + detail::fixedstring::compare_( + a.begin(), 0u, a.size(), b.data_, 0u, b.size_); + } + + /** + * \overload + */ + friend constexpr bool operator<( + const BasicFixedString& a, + Range<const Char*> b) noexcept { + return ordering::lt == + detail::fixedstring::compare_( + a.data_, 0u, a.size_, b.begin(), 0u, b.size()); + } + + friend constexpr bool operator>( + const Char* a, + const BasicFixedString& b) noexcept { + return b < a; + } + + /** + * \overload + */ + friend constexpr bool operator>( + const BasicFixedString& a, + const Char* b) noexcept { + return b < a; + } + + /** + * \overload + */ + friend constexpr bool operator>( + Range<const Char*> a, + const BasicFixedString& b) noexcept { + return b < a; + } + + /** + * \overload + */ + friend constexpr bool operator>( + const BasicFixedString& a, + Range<const Char*> b) noexcept { + return b < a; + } + + friend constexpr bool operator<=( + const Char* a, + const BasicFixedString& b) noexcept { + return !(b < a); + } + + /** + * \overload + */ + friend constexpr bool operator<=( + const BasicFixedString& a, + const Char* b) noexcept { + return !(b < a); + } + + /** + * \overload + */ + friend constexpr bool operator<=( + Range<const Char*> const& a, + const BasicFixedString& b) noexcept { + return !(b < a); + } + + /** + * \overload + */ + friend constexpr bool operator<=( + const BasicFixedString& a, + Range<const Char*> b) noexcept { + return !(b < a); + } + + friend constexpr bool operator>=( + const Char* a, + const BasicFixedString& b) noexcept { + return !(a < b); + } + + /** + * \overload + */ + friend constexpr bool operator>=( + const BasicFixedString& a, + const Char* b) noexcept { + return !(a < b); + } + + /** + * \overload + */ + friend constexpr bool operator>=( + Range<const Char*> a, + const BasicFixedString& b) noexcept { + return !(a < b); + } + + /** + * \overload + */ + friend constexpr bool operator>=( + const BasicFixedString& a, + Range<const Char*> const& b) noexcept { + return !(a < b); + } + + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Asymmetric concatenation + */ + template <std::size_t M> + friend constexpr BasicFixedString<Char, N + M - 1u> operator+( + const Char (&a)[M], + const BasicFixedString& b) noexcept { + return detail::fixedstring::Helper::concat_<Char>( + detail::fixedstring::checkNullTerminated(a), + M - 1u, + b.data_, + b.size_, + std::make_index_sequence<N + M - 1u>{}); + } + + /** + * \overload + */ + template <std::size_t M> + friend constexpr BasicFixedString<Char, N + M - 1u> operator+( + const BasicFixedString& a, + const Char (&b)[M]) noexcept { + return detail::fixedstring::Helper::concat_<Char>( + a.data_, + a.size_, + detail::fixedstring::checkNullTerminated(b), + M - 1u, + std::make_index_sequence<N + M - 1u>{}); + } + + /** + * \overload + */ + friend constexpr BasicFixedString<Char, N + 1u> operator+( + Char a, + const BasicFixedString& b) noexcept { + using A = const Char[2u]; + return detail::fixedstring::Helper::concat_<Char>( + A{a, Char(0)}, + 1u, + b.data_, + b.size_, + std::make_index_sequence<N + 1u>{}); + } + + /** + * \overload + */ + friend constexpr BasicFixedString<Char, N + 1u> operator+( + const BasicFixedString& a, + Char b) noexcept { + using A = const Char[2u]; + return detail::fixedstring::Helper::concat_<Char>( + a.data_, + a.size_, + A{b, Char(0)}, + 1u, + std::make_index_sequence<N + 1u>{}); + } +}; + +template <class C, std::size_t N> +inline std::basic_ostream<C>& operator<<( + std::basic_ostream<C>& os, + const BasicFixedString<C, N>& string) { + using StreamSize = decltype(os.width()); + os.write(string.begin(), static_cast<StreamSize>(string.size())); + return os; +} + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Symmetric relational operators + */ +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator==( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) noexcept { + return detail::fixedstring::equal_( + detail::fixedstring::Helper::data_(a), + a.size(), + detail::fixedstring::Helper::data_(b), + b.size()); +} + +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator!=( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) { + return !(a == b); +} + +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator<( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) noexcept { + return ordering::lt == + detail::fixedstring::compare_( + detail::fixedstring::Helper::data_(a), + 0u, + a.size(), + detail::fixedstring::Helper::data_(b), + 0u, + b.size()); +} + +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator>( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) noexcept { + return b < a; +} + +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator<=( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) noexcept { + return !(b < a); +} + +template <class Char, std::size_t A, std::size_t B> +constexpr bool operator>=( + const BasicFixedString<Char, A>& a, + const BasicFixedString<Char, B>& b) noexcept { + return !(a < b); +} + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Symmetric concatenation + */ +template <class Char, std::size_t N, std::size_t M> +constexpr BasicFixedString<Char, N + M> operator+( + const BasicFixedString<Char, N>& a, + const BasicFixedString<Char, M>& b) noexcept { + return detail::fixedstring::Helper::concat_<Char>( + detail::fixedstring::Helper::data_(a), + a.size(), + detail::fixedstring::Helper::data_(b), + b.size(), + std::make_index_sequence<N + M>{}); +} + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Construct a `BasicFixedString` object from a null-terminated array of + * characters. The capacity and size of the string will be equal to one less + * than the size of the array. + * \pre `a` contains no embedded null characters. + * \pre `a[N-1] == Char(0)` + * \post For a returned string `s`, `s[i]==a[i]` for every `i` in [`0`,`N-1`]. + */ +template <class Char, std::size_t N> +constexpr BasicFixedString<Char, N - 1u> makeFixedString( + const Char (&a)[N]) noexcept { + return {a}; +} + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + * Swap function + */ +template <class Char, std::size_t N> +constexpr void swap( + BasicFixedString<Char, N>& a, + BasicFixedString<Char, N>& b) noexcept { + a.swap(b); +} + +inline namespace literals { +inline namespace string_literals { +inline namespace { +// "const std::size_t&" is so that folly::npos has the same address in every +// translation unit. This is to avoid potential violations of the ODR. +constexpr const std::size_t& npos = detail::fixedstring::FixedStringBase::npos; +} // namespace + +#if defined(__GNUC__) && !defined(__ICC) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template" + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** * + * User-defined literals for creating FixedString objects from string literals + * on the compilers that support it. + * + * \par Example: + * \par + * \code + * using namespace folly::string_literals; + * constexpr auto hello = "hello world!"_fs; + * \endcode + * + * \note This requires a GNU compiler extension + * (-Wgnu-string-literal-operator-template) supported by clang and gcc, + * proposed for standardization in + * <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0424r0.pdf>. + * \par + * For portable code, prefer the suffixes `_fs4`, `_fs8`, `_fs16`, `_fs32`, + * `_fs64`, and `_fs128` for creating instances of types `FixedString<4>`, + * `FixedString<8>`, `FixedString<16>`, etc. + */ +template <class Char, Char... Cs> +constexpr BasicFixedString<Char, sizeof...(Cs)> operator"" _fs() noexcept { + const Char a[] = {Cs..., Char(0)}; + return {+a, sizeof...(Cs)}; +} + +#pragma GCC diagnostic pop +#endif + +#define FOLLY_DEFINE_FIXED_STRING_UDL(N) \ + constexpr FixedString<N> operator"" _fs##N( \ + const char* that, std::size_t count) noexcept(false) { \ + return {that, count}; \ + } \ +/**/ + +// Define UDLs _fs4, _fs8, _fs16, etc for FixedString<[4, 8, 16, ...]> +FOLLY_DEFINE_FIXED_STRING_UDL(4) +FOLLY_DEFINE_FIXED_STRING_UDL(8) +FOLLY_DEFINE_FIXED_STRING_UDL(16) +FOLLY_DEFINE_FIXED_STRING_UDL(32) +FOLLY_DEFINE_FIXED_STRING_UDL(64) +FOLLY_DEFINE_FIXED_STRING_UDL(128) + +#undef FOLLY_DEFINE_FIXED_STRING_UDL +} // namespace string_literals +} // namespace literals + +// TODO: +// // numeric conversions: +// template <std::size_t N> +// constexpr int stoi(const FixedString<N>& str, int base = 10); +// template <std::size_t N> +// constexpr unsigned stou(const FixedString<N>& str, int base = 10); +// template <std::size_t N> +// constexpr long stol(const FixedString<N>& str, int base = 10); +// template <std::size_t N> +// constexpr unsigned long stoul(const FixedString<N>& str, int base = 10; +// template <std::size_t N> +// constexpr long long stoll(const FixedString<N>& str, int base = 10); +// template <std::size_t N> +// constexpr unsigned long long stoull(const FixedString<N>& str, +// int base = 10); +// template <std::size_t N> +// constexpr float stof(const FixedString<N>& str); +// template <std::size_t N> +// constexpr double stod(const FixedString<N>& str); +// template <std::size_t N> +// constexpr long double stold(const FixedString<N>& str); +// template <int val> +// constexpr FixedString</*...*/> to_fixed_string_i() noexcept; +// template <unsigned val> +// constexpr FixedString</*...*/> to_fixed_string_u() noexcept; +// template <long val> +// constexpr FixedString</*...*/> to_fixed_string_l() noexcept; +// template <unsigned long val> +// constexpr FixedString</*...*/> to_fixed_string_ul() noexcept; +// template <long long val> +// constexpr FixedString</*...*/> to_fixed_string_ll() noexcept +// template <unsigned long long val> +// constexpr FixedString</*...*/> to_fixed_string_ull() noexcept; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Format-inl.h b/ios/Pods/Flipper-Folly/folly/Format-inl.h new file mode 100644 index 000000000..235eeab42 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Format-inl.h @@ -0,0 +1,1139 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_FORMAT_H_ +#error This file may only be included from Format.h. +#endif + +#include <array> +#include <cinttypes> +#include <deque> +#include <map> +#include <unordered_map> +#include <vector> + +#include <folly/Exception.h> +#include <folly/FormatTraits.h> +#include <folly/MapUtil.h> +#include <folly/Traits.h> +#include <folly/lang/Exception.h> +#include <folly/portability/Windows.h> + +// Ignore -Wformat-nonliteral and -Wconversion warnings within this file +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wformat-nonliteral") +FOLLY_GNU_DISABLE_WARNING("-Wconversion") + +namespace folly { + +namespace detail { + +// Updates the end of the buffer after the comma separators have been added. +void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer); + +extern const std::array<std::array<char, 2>, 256> formatHexUpper; +extern const std::array<std::array<char, 2>, 256> formatHexLower; +extern const std::array<std::array<char, 3>, 512> formatOctal; +extern const std::array<std::array<char, 8>, 256> formatBinary; + +const size_t kMaxHexLength = 2 * sizeof(uintmax_t); +const size_t kMaxOctalLength = 3 * sizeof(uintmax_t); +const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t); + +/** + * Convert an unsigned to hex, using repr (which maps from each possible + * 2-hex-bytes value to the 2-character representation). + * + * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of + * the supplied buffer and returns the offset of the beginning of the string + * from the start of the buffer. The formatted string will be in range + * [buf+begin, buf+bufLen). + */ +template <class Uint> +size_t uintToHex( + char* buffer, + size_t bufLen, + Uint v, + std::array<std::array<char, 2>, 256> const& repr) { + // 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size + // warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1). + for (; !less_than<unsigned, 256>(v); v >>= 7, v >>= 1) { + auto b = v & 0xff; + bufLen -= 2; + buffer[bufLen] = repr[b][0]; + buffer[bufLen + 1] = repr[b][1]; + } + buffer[--bufLen] = repr[v][1]; + if (v >= 16) { + buffer[--bufLen] = repr[v][0]; + } + return bufLen; +} + +/** + * Convert an unsigned to hex, using lower-case letters for the digits + * above 9. See the comments for uintToHex. + */ +template <class Uint> +inline size_t uintToHexLower(char* buffer, size_t bufLen, Uint v) { + return uintToHex(buffer, bufLen, v, formatHexLower); +} + +/** + * Convert an unsigned to hex, using upper-case letters for the digits + * above 9. See the comments for uintToHex. + */ +template <class Uint> +inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) { + return uintToHex(buffer, bufLen, v, formatHexUpper); +} + +/** + * Convert an unsigned to octal. + * + * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of + * the supplied buffer and returns the offset of the beginning of the string + * from the start of the buffer. The formatted string will be in range + * [buf+begin, buf+bufLen). + */ +template <class Uint> +size_t uintToOctal(char* buffer, size_t bufLen, Uint v) { + auto& repr = formatOctal; + // 'v >>= 7, v >>= 2' is no more than a work around to get rid of shift size + // warning when Uint = uint8_t (it's false as v >= 512 implies sizeof(v) > 1). + for (; !less_than<unsigned, 512>(v); v >>= 7, v >>= 2) { + auto b = v & 0x1ff; + bufLen -= 3; + buffer[bufLen] = repr[b][0]; + buffer[bufLen + 1] = repr[b][1]; + buffer[bufLen + 2] = repr[b][2]; + } + buffer[--bufLen] = repr[v][2]; + if (v >= 8) { + buffer[--bufLen] = repr[v][1]; + } + if (v >= 64) { + buffer[--bufLen] = repr[v][0]; + } + return bufLen; +} + +/** + * Convert an unsigned to binary. + * + * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of + * the supplied buffer and returns the offset of the beginning of the string + * from the start of the buffer. The formatted string will be in range + * [buf+begin, buf+bufLen). + */ +template <class Uint> +size_t uintToBinary(char* buffer, size_t bufLen, Uint v) { + auto& repr = formatBinary; + if (v == 0) { + buffer[--bufLen] = '0'; + return bufLen; + } + for (; v; v >>= 7, v >>= 1) { + auto b = v & 0xff; + bufLen -= 8; + memcpy(buffer + bufLen, &(repr[b][0]), 8); + } + while (buffer[bufLen] == '0') { + ++bufLen; + } + return bufLen; +} + +} // namespace detail + +template <class Derived, bool containerMode, class... Args> +BaseFormatter<Derived, containerMode, Args...>::BaseFormatter( + StringPiece str, + Args&&... args) + : str_(str), values_(std::forward<Args>(args)...) {} + +template <class Derived, bool containerMode, class... Args> +template <class Output> +void BaseFormatter<Derived, containerMode, Args...>::operator()( + Output& out) const { + // Copy raw string (without format specifiers) to output; + // not as simple as we'd like, as we still need to translate "}}" to "}" + // and throw if we see any lone "}" + auto outputString = [&out](StringPiece s) { + auto p = s.begin(); + auto end = s.end(); + while (p != end) { + auto q = static_cast<const char*>(memchr(p, '}', size_t(end - p))); + if (!q) { + out(StringPiece(p, end)); + break; + } + ++q; + out(StringPiece(p, q)); + p = q; + + if (p == end || *p != '}') { + throw_exception<BadFormatArg>( + "folly::format: single '}' in format string"); + } + ++p; + } + }; + + auto p = str_.begin(); + auto end = str_.end(); + + int nextArg = 0; + bool hasDefaultArgIndex = false; + bool hasExplicitArgIndex = false; + while (p != end) { + auto q = static_cast<const char*>(memchr(p, '{', size_t(end - p))); + if (!q) { + outputString(StringPiece(p, end)); + break; + } + outputString(StringPiece(p, q)); + p = q + 1; + + if (p == end) { + throw_exception<BadFormatArg>( + "folly::format: '}' at end of format string"); + } + + // "{{" -> "{" + if (*p == '{') { + out(StringPiece(p, 1)); + ++p; + continue; + } + + // Format string + q = static_cast<const char*>(memchr(p, '}', size_t(end - p))); + if (q == nullptr) { + throw_exception<BadFormatArg>("folly::format: missing ending '}'"); + } + FormatArg arg(StringPiece(p, q)); + p = q + 1; + + int argIndex = 0; + auto piece = arg.splitKey<true>(); // empty key component is okay + if (containerMode) { // static + arg.enforce( + arg.width != FormatArg::kDynamicWidth, + "dynamic field width not supported in vformat()"); + if (piece.empty()) { + arg.setNextIntKey(nextArg++); + hasDefaultArgIndex = true; + } else { + arg.setNextKey(piece); + hasExplicitArgIndex = true; + } + } else { + if (piece.empty()) { + if (arg.width == FormatArg::kDynamicWidth) { + arg.enforce( + arg.widthIndex == FormatArg::kNoIndex, + "cannot provide width arg index without value arg index"); + int sizeArg = nextArg++; + arg.width = asDerived().getSizeArg(size_t(sizeArg), arg); + } + + argIndex = nextArg++; + hasDefaultArgIndex = true; + } else { + if (arg.width == FormatArg::kDynamicWidth) { + arg.enforce( + arg.widthIndex != FormatArg::kNoIndex, + "cannot provide value arg index without width arg index"); + arg.width = asDerived().getSizeArg(size_t(arg.widthIndex), arg); + } + + auto result = tryTo<int>(piece); + arg.enforce(result, "argument index must be integer"); + argIndex = *result; + arg.enforce(argIndex >= 0, "argument index must be non-negative"); + hasExplicitArgIndex = true; + } + } + + if (hasDefaultArgIndex && hasExplicitArgIndex) { + throw_exception<BadFormatArg>( + "folly::format: may not have both default and explicit arg indexes"); + } + + asDerived().doFormat(size_t(argIndex), arg, out); + } +} + +template <class Derived, bool containerMode, class... Args> +void writeTo( + FILE* fp, + const BaseFormatter<Derived, containerMode, Args...>& formatter) { + auto writer = [fp](StringPiece sp) { + size_t n = fwrite(sp.data(), 1, sp.size(), fp); + if (n < sp.size()) { + throwSystemError("Formatter writeTo", "fwrite failed"); + } + }; + formatter(writer); +} + +namespace format_value { + +template <class FormatCallback> +void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { + if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) { + throw_exception<BadFormatArg>("folly::format: invalid width"); + } + if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) { + throw_exception<BadFormatArg>("folly::format: invalid precision"); + } + + if (arg.precision != FormatArg::kDefaultPrecision && + val.size() > static_cast<size_t>(arg.precision)) { + val.reset(val.data(), static_cast<size_t>(arg.precision)); + } + + constexpr int padBufSize = 128; + char padBuf[padBufSize]; + + // Output padding, no more than padBufSize at once + auto pad = [&padBuf, &cb, padBufSize](int chars) { + while (chars) { + int n = std::min(chars, padBufSize); + cb(StringPiece(padBuf, size_t(n))); + chars -= n; + } + }; + + int padRemaining = 0; + if (arg.width != FormatArg::kDefaultWidth && + val.size() < static_cast<size_t>(arg.width)) { + char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill; + int padChars = static_cast<int>(arg.width - val.size()); + memset(padBuf, fill, size_t(std::min(padBufSize, padChars))); + + switch (arg.align) { + case FormatArg::Align::DEFAULT: + case FormatArg::Align::LEFT: + padRemaining = padChars; + break; + case FormatArg::Align::CENTER: + pad(padChars / 2); + padRemaining = padChars - padChars / 2; + break; + case FormatArg::Align::RIGHT: + case FormatArg::Align::PAD_AFTER_SIGN: + pad(padChars); + break; + case FormatArg::Align::INVALID: + default: + abort(); + break; + } + } + + cb(val); + + if (padRemaining) { + pad(padRemaining); + } +} + +template <class FormatCallback> +void formatNumber( + StringPiece val, + int prefixLen, + FormatArg& arg, + FormatCallback& cb) { + // precision means something different for numbers + arg.precision = FormatArg::kDefaultPrecision; + if (arg.align == FormatArg::Align::DEFAULT) { + arg.align = FormatArg::Align::RIGHT; + } else if (prefixLen && arg.align == FormatArg::Align::PAD_AFTER_SIGN) { + // Split off the prefix, then do any padding if necessary + cb(val.subpiece(0, size_t(prefixLen))); + val.advance(size_t(prefixLen)); + arg.width = std::max(arg.width - prefixLen, 0); + } + format_value::formatString(val, arg, cb); +} + +template < + class FormatCallback, + class Derived, + bool containerMode, + class... Args> +void formatFormatter( + const BaseFormatter<Derived, containerMode, Args...>& formatter, + FormatArg& arg, + FormatCallback& cb) { + if (arg.width == FormatArg::kDefaultWidth && + arg.precision == FormatArg::kDefaultPrecision) { + // nothing to do + formatter(cb); + } else if ( + arg.align != FormatArg::Align::LEFT && + arg.align != FormatArg::Align::DEFAULT) { + // We can only avoid creating a temporary string if we align left, + // as we'd need to know the size beforehand otherwise + format_value::formatString(formatter.fbstr(), arg, cb); + } else { + auto fn = [&arg, &cb](StringPiece sp) mutable { + int sz = static_cast<int>(sp.size()); + if (arg.precision != FormatArg::kDefaultPrecision) { + sz = std::min(arg.precision, sz); + sp.reset(sp.data(), size_t(sz)); + arg.precision -= sz; + } + if (!sp.empty()) { + cb(sp); + if (arg.width != FormatArg::kDefaultWidth) { + arg.width = std::max(arg.width - sz, 0); + } + } + }; + formatter(fn); + if (arg.width != FormatArg::kDefaultWidth && arg.width != 0) { + // Rely on formatString to do appropriate padding + format_value::formatString(StringPiece(), arg, cb); + } + } +} + +} // namespace format_value + +// Definitions for default FormatValue classes + +// Integral types (except bool) +template <class T> +class FormatValue< + T, + typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> { + public: + explicit FormatValue(T val) : val_(val) {} + + T getValue() const { + return val_; + } + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + arg.validate(FormatArg::Type::INTEGER); + doFormat(arg, cb); + } + + template <class FormatCallback> + void doFormat(FormatArg& arg, FormatCallback& cb) const { + char presentation = arg.presentation; + if (presentation == FormatArg::kDefaultPresentation) { + presentation = std::is_same<T, char>::value ? 'c' : 'd'; + } + + // Do all work as unsigned, we'll add the prefix ('0' or '0x' if necessary) + // and sign ourselves. + typedef typename std::make_unsigned<T>::type UT; + UT uval; + char sign; + if (std::is_signed<T>::value) { + if (folly::is_negative(val_)) { + // avoid unary negation of unsigned types, which may be warned against + // avoid ub signed integer overflow, which ubsan checks against + uval = UT(0 - static_cast<UT>(val_)); + sign = '-'; + } else { + uval = static_cast<UT>(val_); + switch (arg.sign) { + case FormatArg::Sign::PLUS_OR_MINUS: + sign = '+'; + break; + case FormatArg::Sign::SPACE_OR_MINUS: + sign = ' '; + break; + case FormatArg::Sign::DEFAULT: + case FormatArg::Sign::MINUS: + case FormatArg::Sign::INVALID: + default: + sign = '\0'; + break; + } + } + } else { + uval = static_cast<UT>(val_); + sign = '\0'; + + arg.enforce( + arg.sign == FormatArg::Sign::DEFAULT, + "sign specifications not allowed for unsigned values"); + } + + // 1 byte for sign, plus max of: + // #x: two byte "0x" prefix + kMaxHexLength + // #o: one byte "0" prefix + kMaxOctalLength + // #b: two byte "0b" prefix + kMaxBinaryLength + // n: 19 bytes + 1 NUL + // ,d: 26 bytes (including thousands separators!) + // + // Binary format must take the most space, so we use that. + // + // Note that we produce a StringPiece rather than NUL-terminating, + // so we don't need an extra byte for a NUL. + constexpr size_t valBufSize = 1 + 2 + detail::kMaxBinaryLength; + char valBuf[valBufSize]; + char* valBufBegin = nullptr; + char* valBufEnd = nullptr; + + int prefixLen = 0; + switch (presentation) { + case 'n': { + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + + arg.enforce( + !arg.thousandsSeparator, + "cannot use ',' with the '", + presentation, + "' specifier"); + + valBufBegin = valBuf + 1; // room for sign +#if defined(__ANDROID__) + int len = snprintf( + valBufBegin, + (valBuf + valBufSize) - valBufBegin, + "%" PRIuMAX, + static_cast<uintmax_t>(uval)); +#else + int len = snprintf( + valBufBegin, + size_t((valBuf + valBufSize) - valBufBegin), + "%ju", + static_cast<uintmax_t>(uval)); +#endif + // valBufSize should always be big enough, so this should never + // happen. + assert(len < valBuf + valBufSize - valBufBegin); + valBufEnd = valBufBegin + len; + break; + } + case 'd': + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + valBufBegin = valBuf + 1; // room for sign + + // Use uintToBuffer, faster than sprintf + valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin); + if (arg.thousandsSeparator) { + detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd); + } + break; + case 'c': + arg.enforce( + !arg.basePrefix, + "base prefix not allowed with '", + presentation, + "' specifier"); + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufBegin = valBuf + 1; // room for sign + *valBufBegin = static_cast<char>(uval); + valBufEnd = valBufBegin + 1; + break; + case 'o': + case 'O': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize; + valBufBegin = &valBuf[detail::uintToOctal(valBuf, valBufSize, uval)]; + if (arg.basePrefix) { + *--valBufBegin = '0'; + prefixLen = 1; + } + break; + case 'x': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize; + valBufBegin = &valBuf[detail::uintToHexLower(valBuf, valBufSize, uval)]; + if (arg.basePrefix) { + *--valBufBegin = 'x'; + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + case 'X': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize; + valBufBegin = &valBuf[detail::uintToHexUpper(valBuf, valBufSize, uval)]; + if (arg.basePrefix) { + *--valBufBegin = 'X'; + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + case 'b': + case 'B': + arg.enforce( + !arg.thousandsSeparator, + "thousands separator (',') not allowed with '", + presentation, + "' specifier"); + valBufEnd = valBuf + valBufSize; + valBufBegin = &valBuf[detail::uintToBinary(valBuf, valBufSize, uval)]; + if (arg.basePrefix) { + *--valBufBegin = presentation; // 0b or 0B + *--valBufBegin = '0'; + prefixLen = 2; + } + break; + default: + arg.error("invalid specifier '", presentation, "'"); + } + + if (sign) { + *--valBufBegin = sign; + ++prefixLen; + } + + format_value::formatNumber( + StringPiece(valBufBegin, valBufEnd), prefixLen, arg, cb); + } + + private: + T val_; +}; + +// Bool +template <> +class FormatValue<bool> { + public: + explicit FormatValue(bool val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + if (arg.presentation == FormatArg::kDefaultPresentation) { + arg.validate(FormatArg::Type::OTHER); + format_value::formatString(val_ ? "true" : "false", arg, cb); + } else { // number + FormatValue<int>(val_).format(arg, cb); + } + } + + private: + bool val_; +}; + +// double +template <> +class FormatValue<double> { + public: + explicit FormatValue(double val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + fbstring piece; + int prefixLen; + formatHelper(piece, prefixLen, arg); + format_value::formatNumber(piece, prefixLen, arg, cb); + } + + private: + void formatHelper(fbstring& piece, int& prefixLen, FormatArg& arg) const; + + double val_; +}; + +// float (defer to double) +template <> +class FormatValue<float> { + public: + explicit FormatValue(float val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue<double>(val_).format(arg, cb); + } + + private: + float val_; +}; + +// String-y types (implicitly convertible to StringPiece, except char*) +template <class T> +class FormatValue< + T, + typename std::enable_if< + (!std::is_pointer<T>::value || + !std::is_same< + char, + typename std::decay<typename std::remove_pointer<T>::type>::type>:: + value) && + std::is_convertible<T, StringPiece>::value>::type> { + public: + explicit FormatValue(StringPiece val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + if (arg.keyEmpty()) { + arg.validate(FormatArg::Type::OTHER); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation || + arg.presentation == 's', + "invalid specifier '", + arg.presentation, + "'"); + format_value::formatString(val_, arg, cb); + } else { + FormatValue<char>(val_.at(size_t(arg.splitIntKey()))).format(arg, cb); + } + } + + private: + StringPiece val_; +}; + +// Null +template <> +class FormatValue<std::nullptr_t> { + public: + explicit FormatValue(std::nullptr_t) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + arg.validate(FormatArg::Type::OTHER); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation, + "invalid specifier '", + arg.presentation, + "'"); + format_value::formatString("(null)", arg, cb); + } +}; + +// Partial specialization of FormatValue for char* +template <class T> +class FormatValue< + T*, + typename std::enable_if< + std::is_same<char, typename std::decay<T>::type>::value>::type> { + public: + explicit FormatValue(T* val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + if (arg.keyEmpty()) { + if (!val_) { + FormatValue<std::nullptr_t>(nullptr).format(arg, cb); + } else { + FormatValue<StringPiece>(val_).format(arg, cb); + } + } else { + FormatValue<typename std::decay<T>::type>(val_[arg.splitIntKey()]) + .format(arg, cb); + } + } + + private: + T* val_; +}; + +// Partial specialization of FormatValue for void* +template <class T> +class FormatValue< + T*, + typename std::enable_if< + std::is_same<void, typename std::decay<T>::type>::value>::type> { + public: + explicit FormatValue(T* val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + if (!val_) { + FormatValue<std::nullptr_t>(nullptr).format(arg, cb); + } else { + // Print as a pointer, in hex. + arg.validate(FormatArg::Type::OTHER); + arg.enforce( + arg.presentation == FormatArg::kDefaultPresentation, + "invalid specifier '", + arg.presentation, + "'"); + arg.basePrefix = true; + arg.presentation = 'x'; + if (arg.align == FormatArg::Align::DEFAULT) { + arg.align = FormatArg::Align::LEFT; + } + FormatValue<uintptr_t>(reinterpret_cast<uintptr_t>(val_)) + .doFormat(arg, cb); + } + } + + private: + T* val_; +}; + +template <class T, class = void> +class TryFormatValue { + public: + template <class FormatCallback> + static void + formatOrFail(T& /* value */, FormatArg& arg, FormatCallback& /* cb */) { + arg.error("No formatter available for this type"); + } +}; + +template <class T> +class TryFormatValue< + T, + typename std::enable_if< + 0 < sizeof(FormatValue<typename std::decay<T>::type>)>::type> { + public: + template <class FormatCallback> + static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { + FormatValue<typename std::decay<T>::type>(value).format(arg, cb); + } +}; + +// Partial specialization of FormatValue for other pointers +template <class T> +class FormatValue< + T*, + typename std::enable_if< + !std::is_same<char, typename std::decay<T>::type>::value && + !std::is_same<void, typename std::decay<T>::type>::value>::type> { + public: + explicit FormatValue(T* val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + if (arg.keyEmpty()) { + FormatValue<void*>((void*)val_).format(arg, cb); + } else { + TryFormatValue<T>::formatOrFail(val_[arg.splitIntKey()], arg, cb); + } + } + + private: + T* val_; +}; + +namespace detail { + +// std::array +template <class T, size_t N> +struct IndexableTraits<std::array<T, N>> + : public IndexableTraitsSeq<std::array<T, N>> {}; + +// std::vector +template <class T, class A> +struct IndexableTraits<std::vector<T, A>> + : public IndexableTraitsSeq<std::vector<T, A>> {}; + +// std::deque +template <class T, class A> +struct IndexableTraits<std::deque<T, A>> + : public IndexableTraitsSeq<std::deque<T, A>> {}; + +// std::map with integral keys +template <class K, class T, class C, class A> +struct IndexableTraits< + std::map<K, T, C, A>, + typename std::enable_if<std::is_integral<K>::value>::type> + : public IndexableTraitsAssoc<std::map<K, T, C, A>> {}; + +// std::unordered_map with integral keys +template <class K, class T, class H, class E, class A> +struct IndexableTraits< + std::unordered_map<K, T, H, E, A>, + typename std::enable_if<std::is_integral<K>::value>::type> + : public IndexableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {}; + +} // namespace detail + +// Partial specialization of FormatValue for integer-indexable containers +template <class T> +class FormatValue<T, typename detail::IndexableTraits<T>::enabled> { + public: + explicit FormatValue(const T& val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue<typename std::decay< + typename detail::IndexableTraits<T>::value_type>::type>( + detail::IndexableTraits<T>::at(val_, arg.splitIntKey())) + .format(arg, cb); + } + + private: + const T& val_; +}; + +template <class Container, class Value> +class FormatValue< + detail::DefaultValueWrapper<Container, Value>, + typename detail::IndexableTraits<Container>::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val) + : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue<typename std::decay< + typename detail::IndexableTraits<Container>::value_type>::type>( + detail::IndexableTraits<Container>::at( + val_.container, arg.splitIntKey(), val_.defaultValue)) + .format(arg, cb); + } + + private: + const detail::DefaultValueWrapper<Container, Value>& val_; +}; + +namespace detail { + +// Define enabled, key_type, convert from StringPiece to the key types +// that we support +template <class T> +struct KeyFromStringPiece; + +// std::string +template <> +struct KeyFromStringPiece<std::string> : public FormatTraitsBase { + typedef std::string key_type; + static std::string convert(StringPiece s) { + return s.toString(); + } + typedef void enabled; +}; + +// fbstring +template <> +struct KeyFromStringPiece<fbstring> : public FormatTraitsBase { + typedef fbstring key_type; + static fbstring convert(StringPiece s) { + return s.to<fbstring>(); + } +}; + +// StringPiece +template <> +struct KeyFromStringPiece<StringPiece> : public FormatTraitsBase { + typedef StringPiece key_type; + static StringPiece convert(StringPiece s) { + return s; + } +}; + +// Base class for associative types keyed by strings +template <class T> +struct KeyableTraitsAssoc : public FormatTraitsBase { + typedef typename T::key_type key_type; + typedef typename T::value_type::second_type value_type; + static const value_type& at(const T& map, StringPiece key) { + if (auto ptr = get_ptr(map, KeyFromStringPiece<key_type>::convert(key))) { + return *ptr; + } + throw_exception<FormatKeyNotFoundException>(key); + } + static const value_type& + at(const T& map, StringPiece key, const value_type& dflt) { + auto pos = map.find(KeyFromStringPiece<key_type>::convert(key)); + return pos != map.end() ? pos->second : dflt; + } +}; + +// Define enabled, key_type, value_type, at() for supported string-keyed +// types +template <class T, class Enabled = void> +struct KeyableTraits; + +// std::map with string key +template <class K, class T, class C, class A> +struct KeyableTraits< + std::map<K, T, C, A>, + typename KeyFromStringPiece<K>::enabled> + : public KeyableTraitsAssoc<std::map<K, T, C, A>> {}; + +// std::unordered_map with string key +template <class K, class T, class H, class E, class A> +struct KeyableTraits< + std::unordered_map<K, T, H, E, A>, + typename KeyFromStringPiece<K>::enabled> + : public KeyableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {}; + +} // namespace detail + +// Partial specialization of FormatValue for string-keyed containers +template <class T> +class FormatValue<T, typename detail::KeyableTraits<T>::enabled> { + public: + explicit FormatValue(const T& val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue<typename std::decay< + typename detail::KeyableTraits<T>::value_type>::type>( + detail::KeyableTraits<T>::at(val_, arg.splitKey())) + .format(arg, cb); + } + + private: + const T& val_; +}; + +template <class Container, class Value> +class FormatValue< + detail::DefaultValueWrapper<Container, Value>, + typename detail::KeyableTraits<Container>::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val) + : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue<typename std::decay< + typename detail::KeyableTraits<Container>::value_type>::type>( + detail::KeyableTraits<Container>::at( + val_.container, arg.splitKey(), val_.defaultValue)) + .format(arg, cb); + } + + private: + const detail::DefaultValueWrapper<Container, Value>& val_; +}; + +// Partial specialization of FormatValue for pairs +template <class A, class B> +class FormatValue<std::pair<A, B>> { + public: + explicit FormatValue(const std::pair<A, B>& val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + int key = arg.splitIntKey(); + switch (key) { + case 0: + FormatValue<typename std::decay<A>::type>(val_.first).format(arg, cb); + break; + case 1: + FormatValue<typename std::decay<B>::type>(val_.second).format(arg, cb); + break; + default: + arg.error("invalid index for pair"); + } + } + + private: + const std::pair<A, B>& val_; +}; + +// Partial specialization of FormatValue for tuples +template <class... Args> +class FormatValue<std::tuple<Args...>> { + typedef std::tuple<Args...> Tuple; + + public: + explicit FormatValue(const Tuple& val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + int key = arg.splitIntKey(); + arg.enforce(key >= 0, "tuple index must be non-negative"); + doFormat(size_t(key), arg, cb); + } + + private: + static constexpr size_t valueCount = std::tuple_size<Tuple>::value; + + template <size_t K, class Callback> + typename std::enable_if<K == valueCount>::type + doFormatFrom(size_t i, FormatArg& arg, Callback& /* cb */) const { + arg.error("tuple index out of range, max=", i); + } + + template <size_t K, class Callback> + typename std::enable_if<(K < valueCount)>::type + doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { + if (i == K) { + FormatValue<typename std::decay< + typename std::tuple_element<K, Tuple>::type>::type>(std::get<K>(val_)) + .format(arg, cb); + } else { + doFormatFrom<K + 1>(i, arg, cb); + } + } + + template <class Callback> + void doFormat(size_t i, FormatArg& arg, Callback& cb) const { + return doFormatFrom<0>(i, arg, cb); + } + + const Tuple& val_; +}; + +// Partial specialization of FormatValue for nested Formatters +template <bool containerMode, class... Args, template <bool, class...> class F> +class FormatValue< + F<containerMode, Args...>, + typename std::enable_if< + detail::IsFormatter<F<containerMode, Args...>>::value>::type> { + typedef typename F<containerMode, Args...>::BaseType FormatterValue; + + public: + explicit FormatValue(const FormatterValue& f) : f_(f) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + format_value::formatFormatter(f_, arg, cb); + } + + private: + const FormatterValue& f_; +}; + +/** + * Formatter objects can be appended to strings, and therefore they're + * compatible with folly::toAppend and folly::to. + */ +template <class Tgt, class Derived, bool containerMode, class... Args> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend( + const BaseFormatter<Derived, containerMode, Args...>& value, + Tgt* result) { + value.appendTo(*result); +} + +} // namespace folly + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/Format.cpp b/ios/Pods/Flipper-Folly/folly/Format.cpp new file mode 100644 index 000000000..89007e76f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Format.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Format.h> + +#include <cassert> + +#include <folly/ConstexprMath.h> +#include <folly/CppAttributes.h> +#include <folly/container/Array.h> + +#include <double-conversion/double-conversion.h> + +namespace folly { +namespace detail { + +// ctor for items in the align table +struct format_table_align_make_item { + static constexpr std::size_t size = 256; + constexpr FormatArg::Align operator()(std::size_t index) const { + // clang-format off + return + index == '<' ? FormatArg::Align::LEFT: + index == '>' ? FormatArg::Align::RIGHT : + index == '=' ? FormatArg::Align::PAD_AFTER_SIGN : + index == '^' ? FormatArg::Align::CENTER : + FormatArg::Align::INVALID; + // clang-format on + } +}; + +// ctor for items in the conv tables for representing parts of nonnegative +// integers into ascii digits of length Size, over a given base Base +template <std::size_t Base, std::size_t Size, bool Upper = false> +struct format_table_conv_make_item { + static_assert(Base <= 36, "Base is unrepresentable"); + struct make_item { + std::size_t index{}; + constexpr explicit make_item(std::size_t index_) : index(index_) {} // gcc49 + constexpr char alpha(std::size_t ord) const { + return static_cast<char>( + ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10)); + } + constexpr char operator()(std::size_t offset) const { + return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base); + } + }; + constexpr std::array<char, Size> operator()(std::size_t index) const { + return make_array_with<Size>(make_item{index}); + } +}; + +// ctor for items in the sign table +struct format_table_sign_make_item { + static constexpr std::size_t size = 256; + constexpr FormatArg::Sign operator()(std::size_t index) const { + // clang-format off + return + index == '+' ? FormatArg::Sign::PLUS_OR_MINUS : + index == '-' ? FormatArg::Sign::MINUS : + index == ' ' ? FormatArg::Sign::SPACE_OR_MINUS : + FormatArg::Sign::INVALID; + // clang-format on + } +}; + +// the tables +FOLLY_STORAGE_CONSTEXPR auto formatAlignTable = + make_array_with<256>(format_table_align_make_item{}); +FOLLY_STORAGE_CONSTEXPR auto formatSignTable = + make_array_with<256>(format_table_sign_make_item{}); +FOLLY_STORAGE_CONSTEXPR decltype(formatHexLower) formatHexLower = + make_array_with<256>(format_table_conv_make_item<16, 2, false>{}); +FOLLY_STORAGE_CONSTEXPR decltype(formatHexUpper) formatHexUpper = + make_array_with<256>(format_table_conv_make_item<16, 2, true>{}); +FOLLY_STORAGE_CONSTEXPR decltype(formatOctal) formatOctal = + make_array_with<512>(format_table_conv_make_item<8, 3>{}); +FOLLY_STORAGE_CONSTEXPR decltype(formatBinary) formatBinary = + make_array_with<256>(format_table_conv_make_item<2, 8>{}); + +} // namespace detail + +using namespace folly::detail; + +void FormatValue<double>::formatHelper( + fbstring& piece, + int& prefixLen, + FormatArg& arg) const { + using ::double_conversion::DoubleToStringConverter; + using ::double_conversion::StringBuilder; + + arg.validate(FormatArg::Type::FLOAT); + + if (arg.presentation == FormatArg::kDefaultPresentation) { + arg.presentation = 'g'; + } + + const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf"; + const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan"; + char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e'; + + if (arg.precision == FormatArg::kDefaultPrecision) { + arg.precision = 6; + } + + // 2+: for null terminator and optional sign shenanigans. + constexpr int bufLen = 2 + + constexpr_max(2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint + + DoubleToStringConverter::kMaxFixedDigitsAfterPoint, + constexpr_max( + 8 + DoubleToStringConverter::kMaxExponentialDigits, + 7 + DoubleToStringConverter::kMaxPrecisionDigits)); + char buf[bufLen]; + StringBuilder builder(buf + 1, bufLen - 1); + + char plusSign; + switch (arg.sign) { + case FormatArg::Sign::PLUS_OR_MINUS: + plusSign = '+'; + break; + case FormatArg::Sign::SPACE_OR_MINUS: + plusSign = ' '; + break; + case FormatArg::Sign::DEFAULT: + case FormatArg::Sign::MINUS: + case FormatArg::Sign::INVALID: + default: + plusSign = '\0'; + break; + }; + + auto flags = DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN | + (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT + : 0); + + double val = val_; + switch (arg.presentation) { + case '%': + val *= 100; + FOLLY_FALLTHROUGH; + case 'f': + case 'F': { + if (arg.precision > DoubleToStringConverter::kMaxFixedDigitsAfterPoint) { + arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint; + } + DoubleToStringConverter conv( + flags, + infinitySymbol, + nanSymbol, + exponentSymbol, + -4, + arg.precision, + 0, + 0); + arg.enforce( + conv.ToFixed(val, arg.precision, &builder), + "fixed double conversion failed"); + break; + } + case 'e': + case 'E': { + if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) { + arg.precision = DoubleToStringConverter::kMaxExponentialDigits; + } + + DoubleToStringConverter conv( + flags, + infinitySymbol, + nanSymbol, + exponentSymbol, + -4, + arg.precision, + 0, + 0); + arg.enforce(conv.ToExponential(val, arg.precision, &builder)); + break; + } + case 'n': // should be locale-aware, but isn't + case 'g': + case 'G': { + if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) { + arg.precision = DoubleToStringConverter::kMinPrecisionDigits; + } else if (arg.precision > DoubleToStringConverter::kMaxPrecisionDigits) { + arg.precision = DoubleToStringConverter::kMaxPrecisionDigits; + } + DoubleToStringConverter conv( + flags, + infinitySymbol, + nanSymbol, + exponentSymbol, + -4, + arg.precision, + 0, + 0); + arg.enforce(conv.ToShortest(val, &builder)); + break; + } + default: + arg.error("invalid specifier '", arg.presentation, "'"); + } + + auto len = builder.position(); + builder.Finalize(); + assert(len > 0); + + // Add '+' or ' ' sign if needed + char* p = buf + 1; + // anything that's neither negative nor nan + prefixLen = 0; + if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) { + *--p = plusSign; + ++len; + prefixLen = 1; + } else if (*p == '-') { + prefixLen = 1; + } + + piece = fbstring(p, size_t(len)); +} + +void FormatArg::initSlow() { + auto b = fullArgString.begin(); + auto end = fullArgString.end(); + + // Parse key + auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b))); + if (!p) { + key_ = StringPiece(b, end); + return; + } + key_ = StringPiece(b, p); + + if (*p == ':') { + // parse format spec + if (++p == end) { + return; + } + + // fill/align, or just align + Align a; + if (p + 1 != end && + (a = formatAlignTable[static_cast<unsigned char>(p[1])]) != + Align::INVALID) { + fill = *p; + align = a; + p += 2; + if (p == end) { + return; + } + } else if ( + (a = formatAlignTable[static_cast<unsigned char>(*p)]) != + Align::INVALID) { + align = a; + if (++p == end) { + return; + } + } + + Sign s; + auto uSign = static_cast<unsigned char>(*p); + if ((s = formatSignTable[uSign]) != Sign::INVALID) { + sign = s; + if (++p == end) { + return; + } + } + + if (*p == '#') { + basePrefix = true; + if (++p == end) { + return; + } + } + + if (*p == '0') { + enforce(align == Align::DEFAULT, "alignment specified twice"); + fill = '0'; + align = Align::PAD_AFTER_SIGN; + if (++p == end) { + return; + } + } + + auto readInt = [&] { + auto const c = p; + do { + ++p; + } while (p != end && *p >= '0' && *p <= '9'); + return to<int>(StringPiece(c, p)); + }; + + if (*p == '*') { + width = kDynamicWidth; + ++p; + + if (p == end) { + return; + } + + if (*p >= '0' && *p <= '9') { + widthIndex = readInt(); + } + + if (p == end) { + return; + } + } else if (*p >= '0' && *p <= '9') { + width = readInt(); + + if (p == end) { + return; + } + } + + if (*p == ',') { + thousandsSeparator = true; + if (++p == end) { + return; + } + } + + if (*p == '.') { + auto d = ++p; + while (p != end && *p >= '0' && *p <= '9') { + ++p; + } + if (p != d) { + precision = to<int>(StringPiece(d, p)); + if (p != end && *p == '.') { + trailingDot = true; + ++p; + } + } else { + trailingDot = true; + } + + if (p == end) { + return; + } + } + + presentation = *p; + if (++p == end) { + return; + } + } + + error("extra characters in format string"); +} + +void FormatArg::validate(Type type) const { + enforce(keyEmpty(), "index not allowed"); + switch (type) { + case Type::INTEGER: + enforce( + precision == kDefaultPrecision, "precision not allowed on integers"); + break; + case Type::FLOAT: + enforce( + !basePrefix, "base prefix ('#') specifier only allowed on integers"); + enforce( + !thousandsSeparator, + "thousands separator (',') only allowed on integers"); + break; + case Type::OTHER: + enforce( + align != Align::PAD_AFTER_SIGN, + "'='alignment only allowed on numbers"); + enforce(sign == Sign::DEFAULT, "sign specifier only allowed on numbers"); + enforce( + !basePrefix, "base prefix ('#') specifier only allowed on integers"); + enforce( + !thousandsSeparator, + "thousands separator (',') only allowed on integers"); + break; + } +} + +namespace detail { +void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) { + auto remaining_digits = uint32_t(*end_buffer - start_buffer); + uint32_t separator_size = (remaining_digits - 1) / 3; + uint32_t result_size = remaining_digits + separator_size; + *end_buffer = *end_buffer + separator_size; + + // get the end of the new string with the separators + uint32_t buffer_write_index = result_size - 1; + uint32_t buffer_read_index = remaining_digits - 1; + start_buffer[buffer_write_index + 1] = 0; + + bool done = false; + uint32_t next_group_size = 3; + + while (!done) { + uint32_t current_group_size = std::max<uint32_t>( + 1, std::min<uint32_t>(remaining_digits, next_group_size)); + + // write out the current group's digits to the buffer index + for (uint32_t i = 0; i < current_group_size; i++) { + start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--]; + } + + // if not finished, write the separator before the next group + if (buffer_write_index < buffer_write_index + 1) { + start_buffer[buffer_write_index--] = ','; + } else { + done = true; + } + + remaining_digits -= current_group_size; + } +} +} // namespace detail + +FormatKeyNotFoundException::FormatKeyNotFoundException(StringPiece key) + : std::out_of_range(kMessagePrefix.str() + key.str()) {} + +constexpr StringPiece const FormatKeyNotFoundException::kMessagePrefix; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Format.h b/ios/Pods/Flipper-Folly/folly/Format.h new file mode 100644 index 000000000..780126b16 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Format.h @@ -0,0 +1,498 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_FORMAT_H_ + +#include <cstdio> +#include <ios> +#include <stdexcept> +#include <tuple> +#include <type_traits> + +#include <folly/CPortability.h> +#include <folly/Conv.h> +#include <folly/FormatArg.h> +#include <folly/Range.h> +#include <folly/String.h> +#include <folly/Traits.h> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +// forward declarations +template <bool containerMode, class... Args> +class Formatter; +template <class... Args> +Formatter<false, Args...> format(StringPiece fmt, Args&&... args); +template <class C> +Formatter<true, C> vformat(StringPiece fmt, C&& container); +template <class T, class Enable = void> +class FormatValue; + +// meta-attribute to identify formatters in this sea of template weirdness +namespace detail { +class FormatterTag {}; +} // namespace detail + +/** + * Formatter class. + * + * Note that this class is tricky, as it keeps *references* to its lvalue + * arguments (while it takes ownership of the temporaries), and it doesn't + * copy the passed-in format string. Thankfully, you can't use this + * directly, you have to use format(...) below. + */ + +/* BaseFormatter class. + * Overridable behaviours: + * You may override the actual formatting of positional parameters in + * `doFormatArg`. The Formatter class provides the default implementation. + * + * You may also override `doFormat` and `getSizeArg`. These override points were + * added to permit static analysis of format strings, when it is inconvenient + * or impossible to instantiate a BaseFormatter with the correct storage + */ +template <class Derived, bool containerMode, class... Args> +class BaseFormatter { + public: + /** + * Append to output. out(StringPiece sp) may be called (more than once) + */ + template <class Output> + void operator()(Output& out) const; + + /** + * Append to a string. + */ + template <class Str> + typename std::enable_if<IsSomeString<Str>::value>::type appendTo( + Str& str) const { + auto appender = [&str](StringPiece s) { str.append(s.data(), s.size()); }; + (*this)(appender); + } + + /** + * Conversion to string + */ + std::string str() const { + std::string s; + appendTo(s); + return s; + } + + /** + * Conversion to fbstring + */ + fbstring fbstr() const { + fbstring s; + appendTo(s); + return s; + } + + /** + * Metadata to identify generated children of BaseFormatter + */ + typedef detail::FormatterTag IsFormatter; + typedef BaseFormatter BaseType; + + private: + typedef std::tuple<Args...> ValueTuple; + static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value; + + Derived const& asDerived() const { + return *static_cast<const Derived*>(this); + } + + template <size_t K, class Callback> + typename std::enable_if<K == valueCount>::type + doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const { + arg.error("argument index out of range, max=", i); + } + + template <size_t K, class Callback> + typename std::enable_if<(K < valueCount)>::type + doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { + if (i == K) { + asDerived().template doFormatArg<K>(arg, cb); + } else { + doFormatFrom<K + 1>(i, arg, cb); + } + } + + template <class Callback> + void doFormat(size_t i, FormatArg& arg, Callback& cb) const { + return doFormatFrom<0>(i, arg, cb); + } + + template <size_t K> + typename std::enable_if<K == valueCount, int>::type getSizeArgFrom( + size_t i, + const FormatArg& arg) const { + arg.error("argument index out of range, max=", i); + } + + template <class T> + typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value, + int>::type + getValue(const FormatValue<T>& format, const FormatArg&) const { + return static_cast<int>(format.getValue()); + } + + template <class T> + typename std::enable_if< + !std::is_integral<T>::value || std::is_same<T, bool>::value, + int>::type + getValue(const FormatValue<T>&, const FormatArg& arg) const { + arg.error("dynamic field width argument must be integral"); + } + + template <size_t K> + typename std::enable_if < + K<valueCount, int>::type getSizeArgFrom(size_t i, const FormatArg& arg) + const { + if (i == K) { + return getValue(getFormatValue<K>(), arg); + } + return getSizeArgFrom<K + 1>(i, arg); + } + + int getSizeArg(size_t i, const FormatArg& arg) const { + return getSizeArgFrom<0>(i, arg); + } + + StringPiece str_; + + protected: + explicit BaseFormatter(StringPiece str, Args&&... args); + + // Not copyable + BaseFormatter(const BaseFormatter&) = delete; + BaseFormatter& operator=(const BaseFormatter&) = delete; + + // Movable, but the move constructor and assignment operator are private, + // for the exclusive use of format() (below). This way, you can't create + // a Formatter object, but can handle references to it (for streaming, + // conversion to string, etc) -- which is good, as Formatter objects are + // dangerous (they may hold references). + BaseFormatter(BaseFormatter&&) = default; + BaseFormatter& operator=(BaseFormatter&&) = default; + + template <size_t K> + using ArgType = typename std::tuple_element<K, ValueTuple>::type; + + template <size_t K> + FormatValue<typename std::decay<ArgType<K>>::type> getFormatValue() const { + return FormatValue<typename std::decay<ArgType<K>>::type>( + std::get<K>(values_)); + } + + ValueTuple values_; +}; + +template <bool containerMode, class... Args> +class Formatter : public BaseFormatter< + Formatter<containerMode, Args...>, + containerMode, + Args...> { + private: + explicit Formatter(StringPiece& str, Args&&... args) + : BaseFormatter< + Formatter<containerMode, Args...>, + containerMode, + Args...>(str, std::forward<Args>(args)...) { + static_assert( + !containerMode || sizeof...(Args) == 1, + "Exactly one argument required in container mode"); + } + + template <size_t K, class Callback> + void doFormatArg(FormatArg& arg, Callback& cb) const { + this->template getFormatValue<K>().format(arg, cb); + } + + friend class BaseFormatter< + Formatter<containerMode, Args...>, + containerMode, + Args...>; + + template <class... A> + friend Formatter<false, A...> format(StringPiece fmt, A&&... arg); + template <class C> + friend Formatter<true, C> vformat(StringPiece fmt, C&& container); +}; + +/** + * Formatter objects can be written to streams. + */ +template <class C, bool containerMode, class... Args> +std::ostream& operator<<( + std::basic_ostream<C>& out, + const Formatter<containerMode, Args...>& formatter) { + auto writer = [&out](StringPiece sp) { + out.write(sp.data(), std::streamsize(sp.size())); + }; + formatter(writer); + return out; +} + +/** + * Formatter objects can be written to stdio FILEs. + */ +template <class Derived, bool containerMode, class... Args> +void writeTo( + FILE* fp, + const BaseFormatter<Derived, containerMode, Args...>& formatter); + +/** + * Create a formatter object. + * + * std::string formatted = format("{} {}", 23, 42).str(); + * LOG(INFO) << format("{} {}", 23, 42); + * writeTo(stdout, format("{} {}", 23, 42)); + */ +template <class... Args> +Formatter<false, Args...> format(StringPiece fmt, Args&&... args) { + return Formatter<false, Args...>(fmt, std::forward<Args>(args)...); +} + +/** + * Like format(), but immediately returns the formatted string instead of an + * intermediate format object. + */ +template <class... Args> +inline std::string sformat(StringPiece fmt, Args&&... args) { + return format(fmt, std::forward<Args>(args)...).str(); +} + +/** + * Create a formatter object that takes one argument (of container type) + * and uses that container to get argument values from. + * + * std::map<string, string> map { {"hello", "world"}, {"answer", "42"} }; + * + * The following are equivalent: + * format("{0[hello]} {0[answer]}", map); + * + * vformat("{hello} {answer}", map); + * + * but the latter is cleaner. + */ +template <class Container> +Formatter<true, Container> vformat(StringPiece fmt, Container&& container) { + return Formatter<true, Container>(fmt, std::forward<Container>(container)); +} + +/** + * Like vformat(), but immediately returns the formatted string instead of an + * intermediate format object. + */ +template <class Container> +inline std::string svformat(StringPiece fmt, Container&& container) { + return vformat(fmt, std::forward<Container>(container)).str(); +} + +/** + * Exception class thrown when a format key is not found in the given + * associative container keyed by strings. We inherit std::out_of_range for + * compatibility with callers that expect exception to be thrown directly + * by std::map or std::unordered_map. + * + * Having the key be at the end of the message string, we can access it by + * simply adding its offset to what(). Not storing separate std::string key + * makes the exception type small and noexcept-copyable like std::out_of_range, + * and therefore able to fit in-situ in exception_wrapper. + */ +class FOLLY_EXPORT FormatKeyNotFoundException : public std::out_of_range { + public: + explicit FormatKeyNotFoundException(StringPiece key); + + char const* key() const noexcept { + return what() + kMessagePrefix.size(); + } + + private: + static constexpr StringPiece const kMessagePrefix = "format key not found: "; +}; + +/** + * Wrap a sequence or associative container so that out-of-range lookups + * return a default value rather than throwing an exception. + * + * Usage: + * format("[no_such_key"], defaulted(map, 42)) -> 42 + */ +namespace detail { +template <class Container, class Value> +struct DefaultValueWrapper { + DefaultValueWrapper(const Container& container, const Value& defaultValue) + : container(container), defaultValue(defaultValue) {} + + const Container& container; + const Value& defaultValue; +}; +} // namespace detail + +template <class Container, class Value> +detail::DefaultValueWrapper<Container, Value> defaulted( + const Container& c, + const Value& v) { + return detail::DefaultValueWrapper<Container, Value>(c, v); +} + +/** + * Append formatted output to a string. + * + * std::string foo; + * format(&foo, "{} {}", 42, 23); + * + * Shortcut for toAppend(format(...), &foo); + */ +template <class Str, class... Args> +typename std::enable_if<IsSomeString<Str>::value>::type +format(Str* out, StringPiece fmt, Args&&... args) { + format(fmt, std::forward<Args>(args)...).appendTo(*out); +} + +/** + * Append vformatted output to a string. + */ +template <class Str, class Container> +typename std::enable_if<IsSomeString<Str>::value>::type +vformat(Str* out, StringPiece fmt, Container&& container) { + vformat(fmt, std::forward<Container>(container)).appendTo(*out); +} + +/** + * Utilities for all format value specializations. + */ +namespace format_value { + +/** + * Format a string in "val", obeying appropriate alignment, padding, width, + * and precision. Treats Align::DEFAULT as Align::LEFT, and + * Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for + * number-specific formatting. + */ +template <class FormatCallback> +void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb); + +/** + * Format a number in "val"; the first prefixLen characters form the prefix + * (sign, "0x" base prefix, etc) which must be left-aligned if the alignment + * is Align::PAD_AFTER_SIGN. Treats Align::DEFAULT as Align::LEFT. Ignores + * arg.precision, as that has a different meaning for numbers (not "maximum + * field width") + */ +template <class FormatCallback> +void formatNumber( + StringPiece val, + int prefixLen, + FormatArg& arg, + FormatCallback& cb); + +/** + * Format a Formatter object recursively. Behaves just like + * formatString(fmt.str(), arg, cb); but avoids creating a temporary + * string if possible. + */ +template < + class FormatCallback, + class Derived, + bool containerMode, + class... Args> +void formatFormatter( + const BaseFormatter<Derived, containerMode, Args...>& formatter, + FormatArg& arg, + FormatCallback& cb); + +} // namespace format_value + +/* + * Specialize folly::FormatValue for your type. + * + * FormatValue<T> is constructed with a (reference-collapsed) T&&, which is + * guaranteed to stay alive until the FormatValue object is destroyed, so you + * may keep a reference (or pointer) to it instead of making a copy. + * + * You must define + * template <class Callback> + * void format(FormatArg& arg, Callback& cb) const; + * with the following semantics: format the value using the given argument. + * + * arg is given by non-const reference for convenience -- it won't be reused, + * so feel free to modify it in place if necessary. (For example, wrap an + * existing conversion but change the default, or remove the "key" when + * extracting an element from a container) + * + * Call the callback to append data to the output. You may call the callback + * as many times as you'd like (or not at all, if you want to output an + * empty string) + */ + +namespace detail { + +template <class T, class Enable = void> +struct IsFormatter : public std::false_type {}; + +template <class T> +struct IsFormatter< + T, + typename std::enable_if< + std::is_same<typename T::IsFormatter, detail::FormatterTag>::value>:: + type> : public std::true_type {}; +} // namespace detail + +// Deprecated API. formatChecked() et. al. now behave identically to their +// non-Checked counterparts. +template <class... Args> +Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) { + return format(fmt, std::forward<Args>(args)...); +} +template <class... Args> +inline std::string sformatChecked(StringPiece fmt, Args&&... args) { + return formatChecked(fmt, std::forward<Args>(args)...).str(); +} +template <class Container> +Formatter<true, Container> vformatChecked( + StringPiece fmt, + Container&& container) { + return vformat(fmt, std::forward<Container>(container)); +} +template <class Container> +inline std::string svformatChecked(StringPiece fmt, Container&& container) { + return vformatChecked(fmt, std::forward<Container>(container)).str(); +} +template <class Str, class... Args> +typename std::enable_if<IsSomeString<Str>::value>::type +formatChecked(Str* out, StringPiece fmt, Args&&... args) { + formatChecked(fmt, std::forward<Args>(args)...).appendTo(*out); +} +template <class Str, class Container> +typename std::enable_if<IsSomeString<Str>::value>::type +vformatChecked(Str* out, StringPiece fmt, Container&& container) { + vformatChecked(fmt, std::forward<Container>(container)).appendTo(*out); +} + +} // namespace folly + +#include <folly/Format-inl.h> + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/FormatArg.h b/ios/Pods/Flipper-Folly/folly/FormatArg.h new file mode 100644 index 000000000..1ff843f96 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FormatArg.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdexcept> + +#include <folly/CPortability.h> +#include <folly/Conv.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/lang/Exception.h> + +namespace folly { + +class FOLLY_EXPORT BadFormatArg : public std::invalid_argument { + using invalid_argument::invalid_argument; +}; + +/** + * Parsed format argument. + */ +struct FormatArg { + /** + * Parse a format argument from a string. Keeps a reference to the + * passed-in string -- does not copy the given characters. + */ + explicit FormatArg(StringPiece sp) + : fullArgString(sp), + fill(kDefaultFill), + align(Align::DEFAULT), + sign(Sign::DEFAULT), + basePrefix(false), + thousandsSeparator(false), + trailingDot(false), + width(kDefaultWidth), + widthIndex(kNoIndex), + precision(kDefaultPrecision), + presentation(kDefaultPresentation), + nextKeyMode_(NextKeyMode::NONE) { + if (!sp.empty()) { + initSlow(); + } + } + + enum class Type { + INTEGER, + FLOAT, + OTHER, + }; + /** + * Validate the argument for the given type; throws on error. + */ + void validate(Type type) const; + + /** + * Throw an exception if the first argument is false. The exception + * message will contain the argument string as well as any passed-in + * arguments to enforce, formatted using folly::to<std::string>. + */ + template <typename Check, typename... Args> + void enforce(Check const& v, Args&&... args) const { + static_assert(std::is_constructible<bool, Check>::value, "not castable"); + if (UNLIKELY(!v)) { + error(std::forward<Args>(args)...); + } + } + + template <typename... Args> + std::string errorStr(Args&&... args) const; + template <typename... Args> + [[noreturn]] void error(Args&&... args) const; + + /** + * Full argument string, as passed in to the constructor. + */ + StringPiece fullArgString; + + /** + * Fill + */ + static constexpr char kDefaultFill = '\0'; + char fill; + + /** + * Alignment + */ + enum class Align : uint8_t { + DEFAULT, + LEFT, + RIGHT, + PAD_AFTER_SIGN, + CENTER, + INVALID, + }; + Align align; + + /** + * Sign + */ + enum class Sign : uint8_t { + DEFAULT, + PLUS_OR_MINUS, + MINUS, + SPACE_OR_MINUS, + INVALID, + }; + Sign sign; + + /** + * Output base prefix (0 for octal, 0x for hex) + */ + bool basePrefix; + + /** + * Output thousands separator (comma) + */ + bool thousandsSeparator; + + /** + * Force a trailing decimal on doubles which could be rendered as ints + */ + bool trailingDot; + + /** + * Field width and optional argument index + */ + static constexpr int kDefaultWidth = -1; + static constexpr int kDynamicWidth = -2; + static constexpr int kNoIndex = -1; + int width; + int widthIndex; + + /** + * Precision + */ + static constexpr int kDefaultPrecision = -1; + int precision; + + /** + * Presentation + */ + static constexpr char kDefaultPresentation = '\0'; + char presentation; + + /** + * Split a key component from "key", which must be non-empty (an exception + * is thrown otherwise). + */ + template <bool emptyOk = false> + StringPiece splitKey(); + + /** + * Is the entire key empty? + */ + bool keyEmpty() const { + return nextKeyMode_ == NextKeyMode::NONE && key_.empty(); + } + + /** + * Split an key component from "key", which must be non-empty and a valid + * integer (an exception is thrown otherwise). + */ + int splitIntKey(); + + void setNextIntKey(int val) { + assert(nextKeyMode_ == NextKeyMode::NONE); + nextKeyMode_ = NextKeyMode::INT; + nextIntKey_ = val; + } + + void setNextKey(StringPiece val) { + assert(nextKeyMode_ == NextKeyMode::NONE); + nextKeyMode_ = NextKeyMode::STRING; + nextKey_ = val; + } + + private: + void initSlow(); + template <bool emptyOk> + StringPiece doSplitKey(); + + StringPiece key_; + int nextIntKey_; + StringPiece nextKey_; + enum class NextKeyMode { + NONE, + INT, + STRING, + }; + NextKeyMode nextKeyMode_; +}; + +template <typename... Args> +inline std::string FormatArg::errorStr(Args&&... args) const { + return to<std::string>( + "invalid format argument {", + fullArgString, + "}: ", + std::forward<Args>(args)...); +} + +template <typename... Args> +[[noreturn]] inline void FormatArg::error(Args&&... args) const { + throw_exception<BadFormatArg>(errorStr(std::forward<Args>(args)...)); +} + +template <bool emptyOk> +inline StringPiece FormatArg::splitKey() { + enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected"); + return doSplitKey<emptyOk>(); +} + +template <bool emptyOk> +inline StringPiece FormatArg::doSplitKey() { + if (nextKeyMode_ == NextKeyMode::STRING) { + nextKeyMode_ = NextKeyMode::NONE; + if (!emptyOk) { // static + enforce(!nextKey_.empty(), "non-empty key required"); + } + return nextKey_; + } + + if (key_.empty()) { + if (!emptyOk) { // static + error("non-empty key required"); + } + return StringPiece(); + } + + const char* b = key_.begin(); + const char* e = key_.end(); + const char* p; + if (e[-1] == ']') { + --e; + p = static_cast<const char*>(memchr(b, '[', size_t(e - b))); + enforce(p != nullptr, "unmatched ']'"); + } else { + p = static_cast<const char*>(memchr(b, '.', size_t(e - b))); + } + if (p) { + key_.assign(p + 1, e); + } else { + p = e; + key_.clear(); + } + if (!emptyOk) { // static + enforce(b != p, "non-empty key required"); + } + return StringPiece(b, p); +} + +inline int FormatArg::splitIntKey() { + if (nextKeyMode_ == NextKeyMode::INT) { + nextKeyMode_ = NextKeyMode::NONE; + return nextIntKey_; + } + auto result = tryTo<int>(doSplitKey<true>()); + enforce(result, "integer key required"); + return *result; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/FormatTraits.h b/ios/Pods/Flipper-Folly/folly/FormatTraits.h new file mode 100644 index 000000000..b84715c30 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/FormatTraits.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <type_traits> + +namespace folly { +namespace detail { + +// Shortcut, so we don't have to use enable_if everywhere +struct FormatTraitsBase { + typedef void enabled; +}; + +// Traits that define enabled, value_type, and at() for anything +// indexable with integral keys: pointers, arrays, vectors, and maps +// with integral keys +template <class T, class Enable = void> +struct IndexableTraits; + +// Base class for sequences (vectors, deques) +template <class C> +struct IndexableTraitsSeq : public FormatTraitsBase { + typedef C container_type; + typedef typename C::value_type value_type; + + static const value_type& at(const C& c, int idx) { + return c.at(idx); + } + + static const value_type& at(const C& c, int idx, const value_type& dflt) { + return (idx >= 0 && size_t(idx) < c.size()) ? c.at(idx) : dflt; + } +}; + +// Base class for associative types (maps) +template <class C> +struct IndexableTraitsAssoc : public FormatTraitsBase { + typedef typename C::value_type::second_type value_type; + + static const value_type& at(const C& c, int idx) { + return c.at(static_cast<typename C::key_type>(idx)); + } + + static const value_type& at(const C& c, int idx, const value_type& dflt) { + auto pos = c.find(static_cast<typename C::key_type>(idx)); + return pos != c.end() ? pos->second : dflt; + } +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Function.h b/ios/Pods/Flipper-Folly/folly/Function.h new file mode 100644 index 000000000..ee470ec75 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Function.h @@ -0,0 +1,1088 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @author Eric Niebler (eniebler@fb.com), Sven Over (over@fb.com) + * Acknowledgements: Giuseppe Ottaviano (ott@fb.com) + */ + +/** + * @class Function + * + * @brief A polymorphic function wrapper that is not copyable and does not + * require the wrapped function to be copy constructible. + * + * `folly::Function` is a polymorphic function wrapper, similar to + * `std::function`. The template parameters of the `folly::Function` define + * the parameter signature of the wrapped callable, but not the specific + * type of the embedded callable. E.g. a `folly::Function<int(int)>` + * can wrap callables that return an `int` when passed an `int`. This can be a + * function pointer or any class object implementing one or both of + * + * int operator(int); + * int operator(int) const; + * + * If both are defined, the non-const one takes precedence. + * + * Unlike `std::function`, a `folly::Function` can wrap objects that are not + * copy constructible. As a consequence of this, `folly::Function` itself + * is not copyable, either. + * + * Another difference is that, unlike `std::function`, `folly::Function` treats + * const-ness of methods correctly. While a `std::function` allows to wrap + * an object that only implements a non-const `operator()` and invoke + * a const-reference of the `std::function`, `folly::Function` requires you to + * declare a function type as const in order to be able to execute it on a + * const-reference. + * + * For example: + * + * class Foo { + * public: + * void operator()() { + * // mutates the Foo object + * } + * }; + * + * class Bar { + * std::function<void(void)> foo_; // wraps a Foo object + * public: + * void mutateFoo() const + * { + * foo_(); + * } + * }; + * + * Even though `mutateFoo` is a const-method, so it can only reference `foo_` + * as const, it is able to call the non-const `operator()` of the Foo + * object that is embedded in the foo_ function. + * + * `folly::Function` will not allow you to do that. You will have to decide + * whether you need to invoke your wrapped callable from a const reference + * (like in the example above), in which case it will only wrap a + * `operator() const`. If your functor does not implement that, + * compilation will fail. If you do not require to be able to invoke the + * wrapped function in a const context, you can wrap any functor that + * implements either or both of const and non-const `operator()`. + * + * The template parameter of `folly::Function`, the `FunctionType`, can be + * const-qualified. Be aware that the const is part of the function signature. + * It does not mean that the function type is a const type. + * + * using FunctionType = R(Args...); + * using ConstFunctionType = R(Args...) const; + * + * In this example, `FunctionType` and `ConstFunctionType` are different + * types. `ConstFunctionType` is not the same as `const FunctionType`. + * As a matter of fact, trying to use the latter should emit a compiler + * warning or error, because it has no defined meaning. + * + * // This will not compile: + * folly::Function<void(void) const> func = Foo(); + * // because Foo does not have a member function of the form: + * // void operator()() const; + * + * // This will compile just fine: + * folly::Function<void(void)> func = Foo(); + * // and it will wrap the existing member function: + * // void operator()(); + * + * When should a const function type be used? As a matter of fact, you will + * probably not need to use const function types very often. See the following + * example: + * + * class Bar { + * folly::Function<void()> func_; + * folly::Function<void() const> constFunc_; + * + * void someMethod() { + * // Can call func_. + * func_(); + * // Can call constFunc_. + * constFunc_(); + * } + * + * void someConstMethod() const { + * // Can call constFunc_. + * constFunc_(); + * // However, cannot call func_ because a non-const method cannot + * // be called from a const one. + * } + * }; + * + * As you can see, whether the `folly::Function`'s function type should + * be declared const or not is identical to whether a corresponding method + * would be declared const or not. + * + * You only require a `folly::Function` to hold a const function type, if you + * intend to invoke it from within a const context. This is to ensure that + * you cannot mutate its inner state when calling in a const context. + * + * This is how the const/non-const choice relates to lambda functions: + * + * // Non-mutable lambdas: can be stored in a non-const... + * folly::Function<void(int)> print_number = + * [] (int number) { std::cout << number << std::endl; }; + * + * // ...as well as in a const folly::Function + * folly::Function<void(int) const> print_number_const = + * [] (int number) { std::cout << number << std::endl; }; + * + * // Mutable lambda: can only be stored in a non-const folly::Function: + * int number = 0; + * folly::Function<void()> print_number = + * [number] () mutable { std::cout << ++number << std::endl; }; + * // Trying to store the above mutable lambda in a + * // `folly::Function<void() const>` would lead to a compiler error: + * // error: no viable conversion from '(lambda at ...)' to + * // 'folly::Function<void () const>' + * + * Casting between const and non-const `folly::Function`s: + * conversion from const to non-const signatures happens implicitly. Any + * function that takes a `folly::Function<R(Args...)>` can be passed + * a `folly::Function<R(Args...) const>` without explicit conversion. + * This is safe, because casting from const to non-const only entails giving + * up the ability to invoke the function from a const context. + * Casting from a non-const to a const signature is potentially dangerous, + * as it means that a function that may change its inner state when invoked + * is made possible to call from a const context. Therefore this cast does + * not happen implicitly. The function `folly::constCastFunction` can + * be used to perform the cast. + * + * // Mutable lambda: can only be stored in a non-const folly::Function: + * int number = 0; + * folly::Function<void()> print_number = + * [number] () mutable { std::cout << ++number << std::endl; }; + * + * // const-cast to a const folly::Function: + * folly::Function<void() const> print_number_const = + * constCastFunction(std::move(print_number)); + * + * When to use const function types? + * Generally, only when you need them. When you use a `folly::Function` as a + * member of a struct or class, only use a const function signature when you + * need to invoke the function from const context. + * When passing a `folly::Function` to a function, the function should accept + * a non-const `folly::Function` whenever possible, i.e. when it does not + * need to pass on or store a const `folly::Function`. This is the least + * possible constraint: you can always pass a const `folly::Function` when + * the function accepts a non-const one. + * + * How does the const behaviour compare to `std::function`? + * `std::function` can wrap object with non-const invocation behaviour but + * exposes them as const. The equivalent behaviour can be achieved with + * `folly::Function` like so: + * + * std::function<void(void)> stdfunc = someCallable; + * + * folly::Function<void(void) const> uniqfunc = constCastFunction( + * folly::Function<void(void)>(someCallable) + * ); + * + * You need to wrap the callable first in a non-const `folly::Function` to + * select a non-const invoke operator (or the const one if no non-const one is + * present), and then move it into a const `folly::Function` using + * `constCastFunction`. + * The name of `constCastFunction` should warn you that something + * potentially dangerous is happening. As a matter of fact, using + * `std::function` always involves this potentially dangerous aspect, which + * is why it is not considered fully const-safe or even const-correct. + * However, in most of the cases you will not need the dangerous aspect at all. + * Either you do not require invocation of the function from a const context, + * in which case you do not need to use `constCastFunction` and just + * use the inner `folly::Function` in the example above, i.e. just use a + * non-const `folly::Function`. Or, you may need invocation from const, but + * the callable you are wrapping does not mutate its state (e.g. it is a class + * object and implements `operator() const`, or it is a normal, + * non-mutable lambda), in which case you can wrap the callable in a const + * `folly::Function` directly, without using `constCastFunction`. + * Only if you require invocation from a const context of a callable that + * may mutate itself when invoked you have to go through the above procedure. + * However, in that case what you do is potentially dangerous and requires + * the equivalent of a `const_cast`, hence you need to call + * `constCastFunction`. + */ + +#pragma once + +#include <functional> +#include <memory> +#include <new> +#include <type_traits> +#include <utility> + +#include <folly/CppAttributes.h> +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Exception.h> + +namespace folly { + +template <typename FunctionType> +class Function; + +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const> constCastFunction( + Function<ReturnType(Args...)>&&) noexcept; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const noexcept> constCastFunction( + Function<ReturnType(Args...) noexcept>&&) noexcept; +#endif + +namespace detail { +namespace function { + +enum class Op { MOVE, NUKE, HEAP }; + +union Data { + Data() {} + void* big; + std::aligned_storage<6 * sizeof(void*)>::type tiny; +}; + +template <typename Fun, typename = Fun*> +using IsSmall = Conjunction< + bool_constant<(sizeof(Fun) <= sizeof(Data::tiny))>, + std::is_nothrow_move_constructible<Fun>>; +using SmallTag = std::true_type; +using HeapTag = std::false_type; + +template <typename T> +struct NotFunction : std::true_type {}; +template <typename T> +struct NotFunction<Function<T>> : std::false_type {}; + +template <typename T> +using EnableIfNotFunction = + typename std::enable_if<NotFunction<T>::value>::type; + +struct CoerceTag {}; + +template <typename, typename T> +struct IsFunctionNullptrTestable : std::false_type {}; + +template <typename T> +struct IsFunctionNullptrTestable< + void_t<decltype( + static_cast<bool>(static_cast<T const&>(T(nullptr)) == nullptr))>, + T> : std::true_type {}; + +template <typename T> +constexpr std::enable_if_t< // + !IsFunctionNullptrTestable<void, T>::value, + std::false_type> +isEmptyFunction(T const&) { + return {}; +} +template <typename T> +constexpr std::enable_if_t<IsFunctionNullptrTestable<void, T>::value, bool> +isEmptyFunction(T const& t) { + return static_cast<bool>(t == nullptr); +} + +template <typename F, typename... Args> +using CallableResult = decltype(std::declval<F>()(std::declval<Args>()...)); + +template < + typename From, + typename To, + typename = typename std::enable_if< + !std::is_reference<To>::value || std::is_reference<From>::value>::type> +using SafeResultOf = decltype(static_cast<To>(std::declval<From>())); + +#if defined(_MSC_VER) +// Need a workaround for MSVC to avoid the inscrutable error: +// +// folly\function.h(...) : fatal error C1001: An internal error has +// occurred in the compiler. +// (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 258) +// To work around this problem, try simplifying or changing the program +// near the locations listed above. +template <typename T> +using CallArg = T&&; +#else +template <typename T> +using CallArg = conditional_t<is_trivially_copyable<T>::value, T, T&&>; +#endif + +template <typename F, typename R, typename... A> +class FunctionTraitsSharedProxy { + std::shared_ptr<Function<F>> sp_; + + public: + explicit FunctionTraitsSharedProxy(std::nullptr_t) noexcept {} + explicit FunctionTraitsSharedProxy(Function<F>&& func) + : sp_(func ? std::make_shared<Function<F>>(std::move(func)) + : std::shared_ptr<Function<F>>()) {} + R operator()(A&&... args) const { + if (!sp_) { + throw_exception<std::bad_function_call>(); + } + return (*sp_)(static_cast<A&&>(args)...); + } + + explicit operator bool() const noexcept { + return sp_ != nullptr; + } + + friend bool operator==( + FunctionTraitsSharedProxy<F, R, A...> const& proxy, + std::nullptr_t) noexcept { + return proxy.sp_ == nullptr; + } + friend bool operator!=( + FunctionTraitsSharedProxy<F, R, A...> const& proxy, + std::nullptr_t) noexcept { + return proxy.sp_ != nullptr; + } + + friend bool operator==( + std::nullptr_t, + FunctionTraitsSharedProxy<F, R, A...> const& proxy) noexcept { + return proxy.sp_ == nullptr; + } + friend bool operator!=( + std::nullptr_t, + FunctionTraitsSharedProxy<F, R, A...> const& proxy) noexcept { + return proxy.sp_ != nullptr; + } +}; + +template <typename FunctionType> +struct FunctionTraits; + +template <typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType(Args...)> { + using Call = ReturnType (*)(CallArg<Args>..., Data&); + using IsConst = std::false_type; + using ConstSignature = ReturnType(Args...) const; + using NonConstSignature = ReturnType(Args...); + using OtherSignature = ConstSignature; + + template <typename F> + using ResultOf = + SafeResultOf<CallableResult<std::decay_t<F>&, Args...>, ReturnType>; + + template <typename Fun> + static ReturnType callSmall(CallArg<Args>... args, Data& p) { + auto& fn = *static_cast<Fun*>(static_cast<void*>(&p.tiny)); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + template <typename Fun> + static ReturnType callBig(CallArg<Args>... args, Data& p) { + auto& fn = *static_cast<Fun*>(p.big); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + static ReturnType uninitCall(CallArg<Args>..., Data&) { + throw_exception<std::bad_function_call>(); + } + + ReturnType operator()(Args... args) { + auto& fn = *static_cast<Function<NonConstSignature>*>(this); + return fn.call_(static_cast<Args&&>(args)..., fn.data_); + } + + using SharedProxy = + FunctionTraitsSharedProxy<NonConstSignature, ReturnType, Args...>; +}; + +template <typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType(Args...) const> { + using Call = ReturnType (*)(CallArg<Args>..., Data&); + using IsConst = std::true_type; + using ConstSignature = ReturnType(Args...) const; + using NonConstSignature = ReturnType(Args...); + using OtherSignature = NonConstSignature; + + template <typename F> + using ResultOf = + SafeResultOf<CallableResult<const std::decay_t<F>&, Args...>, ReturnType>; + + template <typename Fun> + static ReturnType callSmall(CallArg<Args>... args, Data& p) { + auto& fn = *static_cast<const Fun*>(static_cast<void*>(&p.tiny)); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + template <typename Fun> + static ReturnType callBig(CallArg<Args>... args, Data& p) { + auto& fn = *static_cast<const Fun*>(p.big); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + static ReturnType uninitCall(CallArg<Args>..., Data&) { + throw_exception<std::bad_function_call>(); + } + + ReturnType operator()(Args... args) const { + auto& fn = *static_cast<const Function<ConstSignature>*>(this); + return fn.call_(static_cast<Args&&>(args)..., fn.data_); + } + + using SharedProxy = + FunctionTraitsSharedProxy<ConstSignature, ReturnType, Args...>; +}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType(Args...) noexcept> { + using Call = ReturnType (*)(CallArg<Args>..., Data&) noexcept; + using IsConst = std::false_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = ConstSignature; + + template <typename F> + using ResultOf = + SafeResultOf<CallableResult<std::decay_t<F>&, Args...>, ReturnType>; + + template <typename Fun> + static ReturnType callSmall(CallArg<Args>... args, Data& p) noexcept { + auto& fn = *static_cast<Fun*>(static_cast<void*>(&p.tiny)); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + template <typename Fun> + static ReturnType callBig(CallArg<Args>... args, Data& p) noexcept { + auto& fn = *static_cast<Fun*>(p.big); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + static ReturnType uninitCall(CallArg<Args>..., Data&) noexcept { + terminate_with<std::bad_function_call>(); + } + + ReturnType operator()(Args... args) noexcept { + auto& fn = *static_cast<Function<NonConstSignature>*>(this); + return fn.call_(static_cast<Args&&>(args)..., fn.data_); + } + + using SharedProxy = + FunctionTraitsSharedProxy<NonConstSignature, ReturnType, Args...>; +}; + +template <typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType(Args...) const noexcept> { + using Call = ReturnType (*)(CallArg<Args>..., Data&) noexcept; + using IsConst = std::true_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = NonConstSignature; + + template <typename F> + using ResultOf = + SafeResultOf<CallableResult<const std::decay_t<F>&, Args...>, ReturnType>; + + template <typename Fun> + static ReturnType callSmall(CallArg<Args>... args, Data& p) noexcept { + auto& fn = *static_cast<const Fun*>(static_cast<void*>(&p.tiny)); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + template <typename Fun> + static ReturnType callBig(CallArg<Args>... args, Data& p) noexcept { + auto& fn = *static_cast<const Fun*>(p.big); +#if __cpp_if_constexpr >= 201606L + if constexpr (std::is_void<ReturnType>::value) { + fn(static_cast<Args&&>(args)...); + } else { + return fn(static_cast<Args&&>(args)...); + } +#else + return static_cast<ReturnType>(fn(static_cast<Args&&>(args)...)); +#endif + } + + static ReturnType uninitCall(CallArg<Args>..., Data&) noexcept { + throw_exception<std::bad_function_call>(); + } + + ReturnType operator()(Args... args) const noexcept { + auto& fn = *static_cast<const Function<ConstSignature>*>(this); + return fn.call_(static_cast<Args&&>(args)..., fn.data_); + } + + using SharedProxy = + FunctionTraitsSharedProxy<ConstSignature, ReturnType, Args...>; +}; +#endif + +template <typename Fun> +std::size_t execSmall(Op o, Data* src, Data* dst) { + switch (o) { + case Op::MOVE: + ::new (static_cast<void*>(&dst->tiny)) + Fun(std::move(*static_cast<Fun*>(static_cast<void*>(&src->tiny)))); + FOLLY_FALLTHROUGH; + case Op::NUKE: + static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun(); + break; + case Op::HEAP: + break; + } + return 0U; +} + +template <typename Fun> +std::size_t execBig(Op o, Data* src, Data* dst) { + switch (o) { + case Op::MOVE: + dst->big = src->big; + src->big = nullptr; + break; + case Op::NUKE: + delete static_cast<Fun*>(src->big); + break; + case Op::HEAP: + break; + } + return sizeof(Fun); +} + +} // namespace function +} // namespace detail + +template <typename FunctionType> +class Function final : private detail::function::FunctionTraits<FunctionType> { + // These utility types are defined outside of the template to reduce + // the number of instantiations, and then imported in the class + // namespace for convenience. + using Data = detail::function::Data; + using Op = detail::function::Op; + using SmallTag = detail::function::SmallTag; + using HeapTag = detail::function::HeapTag; + using CoerceTag = detail::function::CoerceTag; + + using Traits = detail::function::FunctionTraits<FunctionType>; + using Call = typename Traits::Call; + using Exec = std::size_t (*)(Op, Data*, Data*); + + template <typename Fun> + using IsSmall = detail::function::IsSmall<Fun>; + + // The `data_` member is mutable to allow `constCastFunction` to work without + // invoking undefined behavior. Const-correctness is only violated when + // `FunctionType` is a const function type (e.g., `int() const`) and `*this` + // is the result of calling `constCastFunction`. + mutable Data data_{}; + Call call_{&Traits::uninitCall}; + Exec exec_{nullptr}; + + std::size_t exec(Op o, Data* src, Data* dst) const { + if (!exec_) { + return 0U; + } + return exec_(o, src, dst); + } + + friend Traits; + friend Function<typename Traits::ConstSignature> folly::constCastFunction<>( + Function<typename Traits::NonConstSignature>&&) noexcept; + friend class Function<typename Traits::OtherSignature>; + + template <typename Fun> + Function(Fun&& fun, SmallTag) noexcept { + using FunT = typename std::decay<Fun>::type; + if (!detail::function::isEmptyFunction(fun)) { + ::new (static_cast<void*>(&data_.tiny)) FunT(static_cast<Fun&&>(fun)); + call_ = &Traits::template callSmall<FunT>; + exec_ = &detail::function::execSmall<FunT>; + } + } + + template <typename Fun> + Function(Fun&& fun, HeapTag) { + using FunT = typename std::decay<Fun>::type; + if (!detail::function::isEmptyFunction(fun)) { + data_.big = new FunT(static_cast<Fun&&>(fun)); + call_ = &Traits::template callBig<FunT>; + exec_ = &detail::function::execBig<FunT>; + } + } + + template <typename Signature> + Function(Function<Signature>&& that, CoerceTag) + : Function(static_cast<Function<Signature>&&>(that), HeapTag{}) {} + + Function(Function<typename Traits::OtherSignature>&& that, CoerceTag) noexcept + : call_(that.call_), exec_(that.exec_) { + that.call_ = &Traits::uninitCall; + that.exec_ = nullptr; + exec(Op::MOVE, &that.data_, &data_); + } + + public: + /** + * Default constructor. Constructs an empty Function. + */ + Function() = default; + + // not copyable + Function(const Function&) = delete; + +#if __OBJC__ + // Make sure Objective C blocks are copied + template <class ReturnType, class... Args> + /*implicit*/ Function(ReturnType (^objCBlock)(Args... args)) + : Function([blockCopy = (ReturnType(^)(Args...))[objCBlock copy]]( + Args... args) { return blockCopy(args...); }){}; +#endif + + /** + * Move constructor + */ + Function(Function&& that) noexcept : call_(that.call_), exec_(that.exec_) { + // that must be uninitialized before exec() call in the case of self move + that.call_ = &Traits::uninitCall; + that.exec_ = nullptr; + exec(Op::MOVE, &that.data_, &data_); + } + + /** + * Constructs an empty `Function`. + */ + /* implicit */ Function(std::nullptr_t) noexcept {} + + /** + * Constructs a new `Function` from any callable object that is _not_ a + * `folly::Function`. This handles function pointers, pointers to static + * member functions, `std::reference_wrapper` objects, `std::function` + * objects, and arbitrary objects that implement `operator()` if the parameter + * signature matches (i.e. it returns an object convertible to `R` when called + * with `Args...`). + * + * \note `typename Traits::template ResultOf<Fun>` prevents this overload + * from being selected by overload resolution when `fun` is not a compatible + * function. + * + * \note The noexcept requires some explanation. `IsSmall` is true when the + * decayed type fits within the internal buffer and is noexcept-movable. But + * this ctor might copy, not move. What we need here, if this ctor does a + * copy, is that this ctor be noexcept when the copy is noexcept. That is not + * checked in `IsSmall`, and shouldn't be, because once the `Function` is + * constructed, the contained object is never copied. This check is for this + * ctor only, in the case that this ctor does a copy. + */ + template < + typename Fun, + typename = detail::function::EnableIfNotFunction<Fun>, + typename = typename Traits::template ResultOf<Fun>> + /* implicit */ Function(Fun fun) noexcept( + IsSmall<Fun>::value&& noexcept(Fun(std::declval<Fun>()))) + : Function(std::move(fun), IsSmall<Fun>{}) {} + + /** + * For move-constructing from a `folly::Function<X(Ys...) [const?]>`. + * For a `Function` with a `const` function type, the object must be + * callable from a `const`-reference, i.e. implement `operator() const`. + * For a `Function` with a non-`const` function type, the object will + * be called from a non-const reference, which means that it will execute + * a non-const `operator()` if it is defined, and falls back to + * `operator() const` otherwise. + */ + template < + typename Signature, + typename = typename Traits::template ResultOf<Function<Signature>>> + Function(Function<Signature>&& that) noexcept( + noexcept(Function(std::move(that), CoerceTag{}))) + : Function(std::move(that), CoerceTag{}) {} + + /** + * If `ptr` is null, constructs an empty `Function`. Otherwise, + * this constructor is equivalent to `Function(std::mem_fn(ptr))`. + */ + template < + typename Member, + typename Class, + // Prevent this overload from being selected when `ptr` is not a + // compatible member function pointer. + typename = decltype(Function(std::mem_fn((Member Class::*)0)))> + /* implicit */ Function(Member Class::*ptr) noexcept { + if (ptr) { + *this = std::mem_fn(ptr); + } + } + + ~Function() { + exec(Op::NUKE, &data_, nullptr); + } + + Function& operator=(const Function&) = delete; + +#if __OBJC__ + // Make sure Objective C blocks are copied + template <class ReturnType, class... Args> + /* implicit */ Function& operator=(ReturnType (^objCBlock)(Args... args)) { + (*this) = [blockCopy = (ReturnType(^)(Args...))[objCBlock copy]]( + Args... args) { return blockCopy(args...); }; + return *this; + } +#endif + + /** + * Move assignment operator + * + * \note Leaves `that` in a valid but unspecified state. If `&that == this` + * then `*this` is left in a valid but unspecified state. + */ + Function& operator=(Function&& that) noexcept { + // Q: Why is it safe to destroy and reconstruct this object in place? + // A: Two reasons: First, `Function` is a final class, so in doing this + // we aren't slicing off any derived parts. And second, the move + // operation is guaranteed not to throw so we always leave the object + // in a valid state. + // In the case of self-move (this == &that), this leaves the object in + // a default-constructed state. First the object is destroyed, then we + // pass the destroyed object to the move constructor. The first thing the + // move constructor does is default-construct the object. That object is + // "moved" into itself, which is a no-op for a default-constructed Function. + this->~Function(); + ::new (this) Function(std::move(that)); + return *this; + } + + /** + * Assigns a callable object to this `Function`. If the operation fails, + * `*this` is left unmodified. + * + * \note `typename = decltype(Function(std::declval<Fun>()))` prevents this + * overload from being selected by overload resolution when `fun` is not a + * compatible function. + */ + template <typename Fun, typename = decltype(Function(std::declval<Fun>()))> + Function& operator=(Fun fun) noexcept( + noexcept(/* implicit */ Function(std::declval<Fun>()))) { + // Doing this in place is more efficient when we can do so safely. + if (noexcept(/* implicit */ Function(std::declval<Fun>()))) { + // Q: Why is is safe to destroy and reconstruct this object in place? + // A: See the explanation in the move assignment operator. + this->~Function(); + ::new (this) Function(std::move(fun)); + } else { + // Construct a temporary and (nothrow) swap. + Function(std::move(fun)).swap(*this); + } + return *this; + } + + /** + * For assigning from a `Function<X(Ys..) [const?]>`. + */ + template < + typename Signature, + typename = typename Traits::template ResultOf<Function<Signature>>> + Function& operator=(Function<Signature>&& that) noexcept( + noexcept(Function(std::move(that)))) { + return (*this = Function(std::move(that))); + } + + /** + * Clears this `Function`. + */ + Function& operator=(std::nullptr_t) noexcept { + return (*this = Function()); + } + + /** + * If `ptr` is null, clears this `Function`. Otherwise, this assignment + * operator is equivalent to `*this = std::mem_fn(ptr)`. + */ + template <typename Member, typename Class> + auto operator=(Member Class::*ptr) noexcept + // Prevent this overload from being selected when `ptr` is not a + // compatible member function pointer. + -> decltype(operator=(std::mem_fn(ptr))) { + return ptr ? (*this = std::mem_fn(ptr)) : (*this = Function()); + } + + /** + * Call the wrapped callable object with the specified arguments. + */ + using Traits::operator(); + + /** + * Exchanges the callable objects of `*this` and `that`. + */ + void swap(Function& that) noexcept { + std::swap(*this, that); + } + + /** + * Returns `true` if this `Function` contains a callable, i.e. is + * non-empty. + */ + explicit operator bool() const noexcept { + return exec_ != nullptr; + } + + /** + * Returns the size of the allocation made to store the callable on the + * heap. If `0` is returned, there has been no additional memory + * allocation because the callable is stored within the `Function` object. + */ + std::size_t heapAllocatedMemory() const noexcept { + return exec(Op::HEAP, nullptr, nullptr); + } + + using typename Traits::SharedProxy; + + /** + * Move this `Function` into a copyable callable object, of which all copies + * share the state. + */ + SharedProxy asSharedProxy() && { + return SharedProxy{std::move(*this)}; + } + + /** + * Construct a `std::function` by moving in the contents of this `Function`. + * Note that the returned `std::function` will share its state (i.e. captured + * data) across all copies you make of it, so be very careful when copying. + */ + std::function<typename Traits::NonConstSignature> asStdFunction() && { + return std::move(*this).asSharedProxy(); + } +}; + +template <typename FunctionType> +void swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept { + lhs.swap(rhs); +} + +template <typename FunctionType> +bool operator==(const Function<FunctionType>& fn, std::nullptr_t) { + return !fn; +} + +template <typename FunctionType> +bool operator==(std::nullptr_t, const Function<FunctionType>& fn) { + return !fn; +} + +template <typename FunctionType> +bool operator!=(const Function<FunctionType>& fn, std::nullptr_t) { + return !(fn == nullptr); +} + +template <typename FunctionType> +bool operator!=(std::nullptr_t, const Function<FunctionType>& fn) { + return !(nullptr == fn); +} + +/** + * NOTE: See detailed note about `constCastFunction` at the top of the file. + * This is potentially dangerous and requires the equivalent of a `const_cast`. + */ +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const> constCastFunction( + Function<ReturnType(Args...)>&& that) noexcept { + return Function<ReturnType(Args...) const>{std::move(that), + detail::function::CoerceTag{}}; +} + +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const> constCastFunction( + Function<ReturnType(Args...) const>&& that) noexcept { + return std::move(that); +} + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const noexcept> constCastFunction( + Function<ReturnType(Args...) noexcept>&& that) noexcept { + return Function<ReturnType(Args...) const noexcept>{ + std::move(that), detail::function::CoerceTag{}}; +} + +template <typename ReturnType, typename... Args> +Function<ReturnType(Args...) const noexcept> constCastFunction( + Function<ReturnType(Args...) const noexcept>&& that) noexcept { + return std::move(that); +} +#endif + +/** + * @class FunctionRef + * + * @brief A reference wrapper for callable objects + * + * FunctionRef is similar to std::reference_wrapper, but the template parameter + * is the function signature type rather than the type of the referenced object. + * A folly::FunctionRef is cheap to construct as it contains only a pointer to + * the referenced callable and a pointer to a function which invokes the + * callable. + * + * The user of FunctionRef must be aware of the reference semantics: storing a + * copy of a FunctionRef is potentially dangerous and should be avoided unless + * the referenced object definitely outlives the FunctionRef object. Thus any + * function that accepts a FunctionRef parameter should only use it to invoke + * the referenced function and not store a copy of it. Knowing that FunctionRef + * itself has reference semantics, it is generally okay to use it to reference + * lambdas that capture by reference. + */ + +template <typename FunctionType> +class FunctionRef; + +template <typename ReturnType, typename... Args> +class FunctionRef<ReturnType(Args...)> final { + template <typename Arg> + using CallArg = detail::function::CallArg<Arg>; + + using Call = ReturnType (*)(CallArg<Args>..., void*); + + static ReturnType uninitCall(CallArg<Args>..., void*) { + throw_exception<std::bad_function_call>(); + } + + template <typename Fun> + static ReturnType call(CallArg<Args>... args, void* object) { + using Pointer = std::add_pointer_t<Fun>; + return static_cast<ReturnType>(invoke( + static_cast<Fun&&>(*static_cast<Pointer>(object)), + static_cast<Args&&>(args)...)); + } + + void* object_{nullptr}; + Call call_{&FunctionRef::uninitCall}; + + public: + /** + * Default constructor. Constructs an empty FunctionRef. + * + * Invoking it will throw std::bad_function_call. + */ + constexpr FunctionRef() = default; + + /** + * Like default constructor. Constructs an empty FunctionRef. + * + * Invoking it will throw std::bad_function_call. + */ + constexpr explicit FunctionRef(std::nullptr_t) noexcept {} + + /** + * Construct a FunctionRef from a reference to a callable object. + */ + template < + typename Fun, + typename std::enable_if< + Conjunction< + Negation<std::is_same<FunctionRef, std::decay_t<Fun>>>, + is_invocable_r<ReturnType, Fun&&, Args&&...>>::value, + int>::type = 0> + constexpr /* implicit */ FunctionRef(Fun&& fun) noexcept + // `Fun` may be a const type, in which case we have to do a const_cast + // to store the address in a `void*`. This is safe because the `void*` + // will be cast back to `Fun*` (which is a const pointer whenever `Fun` + // is a const type) inside `FunctionRef::call` + : object_( + const_cast<void*>(static_cast<void const*>(std::addressof(fun)))), + call_(&FunctionRef::template call<Fun>) {} + + ReturnType operator()(Args... args) const { + return call_(static_cast<Args&&>(args)..., object_); + } + + constexpr explicit operator bool() const noexcept { + return object_; + } + + constexpr friend bool operator==( + FunctionRef<ReturnType(Args...)> ref, + std::nullptr_t) noexcept { + return ref.object_ == nullptr; + } + constexpr friend bool operator!=( + FunctionRef<ReturnType(Args...)> ref, + std::nullptr_t) noexcept { + return ref.object_ != nullptr; + } + + constexpr friend bool operator==( + std::nullptr_t, + FunctionRef<ReturnType(Args...)> ref) noexcept { + return ref.object_ == nullptr; + } + constexpr friend bool operator!=( + std::nullptr_t, + FunctionRef<ReturnType(Args...)> ref) noexcept { + return ref.object_ != nullptr; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/GLog.h b/ios/Pods/Flipper-Folly/folly/GLog.h new file mode 100644 index 000000000..c8eae8b0b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/GLog.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <chrono> + +#include <glog/logging.h> + +#ifndef FB_LOG_EVERY_MS +/** + * Issues a LOG(severity) no more often than every + * milliseconds. Example: + * + * FB_LOG_EVERY_MS(INFO, 10000) << "At least ten seconds passed" + * " since you last saw this."; + * + * The implementation uses for statements to introduce variables in + * a nice way that doesn't mess surrounding statements. It is thread + * safe. Non-positive intervals will always log. + */ +#define FB_LOG_EVERY_MS(severity, milli_interval) \ + for (decltype(milli_interval) FB_LEM_once = 1, \ + FB_LEM_interval = (milli_interval); \ + FB_LEM_once;) \ + for (::std::chrono::milliseconds::rep FB_LEM_prev, \ + FB_LEM_now = FB_LEM_interval <= 0 \ + ? 0 \ + : ::std::chrono::duration_cast<::std::chrono::milliseconds>( \ + ::std::chrono::system_clock::now().time_since_epoch()) \ + .count(); \ + FB_LEM_once;) \ + for (static ::std::atomic<::std::chrono::milliseconds::rep> FB_LEM_hist; \ + FB_LEM_once; \ + FB_LEM_once = 0) \ + if (FB_LEM_interval > 0 && \ + (FB_LEM_now - \ + (FB_LEM_prev = \ + FB_LEM_hist.load(std::memory_order_acquire)) < \ + FB_LEM_interval || \ + !FB_LEM_hist.compare_exchange_strong(FB_LEM_prev, FB_LEM_now))) { \ + } else \ + LOG(severity) + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/GroupVarint.cpp b/ios/Pods/Flipper-Folly/folly/GroupVarint.cpp new file mode 100644 index 000000000..8d3aff970 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/GroupVarint.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/GroupVarint.h> + +#include <folly/container/Array.h> + +#if FOLLY_HAVE_GROUP_VARINT +namespace folly { + +const uint32_t GroupVarint32::kMask[] = { + 0xff, + 0xffff, + 0xffffff, + 0xffffffff, +}; + +const uint64_t GroupVarint64::kMask[] = { + 0xff, + 0xffff, + 0xffffff, + 0xffffffff, + 0xffffffffffULL, + 0xffffffffffffULL, + 0xffffffffffffffULL, + 0xffffffffffffffffULL, +}; + +namespace detail { + +struct group_varint_table_base_make_item { + constexpr std::size_t get_d(std::size_t index, std::size_t j) const { + return 1u + ((index >> (2 * j)) & 3u); + } + constexpr std::size_t get_offset(std::size_t index, std::size_t j) const { + // clang-format off + return + (j > 0 ? get_d(index, 0) : 0) + + (j > 1 ? get_d(index, 1) : 0) + + (j > 2 ? get_d(index, 2) : 0) + + (j > 3 ? get_d(index, 3) : 0) + + 0; + // clang-format on + } +}; + +struct group_varint_table_length_make_item : group_varint_table_base_make_item { + constexpr std::uint8_t operator()(std::size_t index) const { + return 1u + get_offset(index, 4); + } +}; + +// Reference: http://www.stepanovpapers.com/CIKM_2011.pdf +// +// From 17 encoded bytes, we may use between 5 and 17 bytes to encode 4 +// integers. The first byte is a key that indicates how many bytes each of +// the 4 integers takes: +// +// bit 0..1: length-1 of first integer +// bit 2..3: length-1 of second integer +// bit 4..5: length-1 of third integer +// bit 6..7: length-1 of fourth integer +// +// The value of the first byte is used as the index in a table which returns +// a mask value for the SSSE3 PSHUFB instruction, which takes an XMM register +// (16 bytes) and shuffles bytes from it into a destination XMM register +// (optionally setting some of them to 0) +// +// For example, if the key has value 4, that means that the first integer +// uses 1 byte, the second uses 2 bytes, the third and fourth use 1 byte each, +// so we set the mask value so that +// +// r[0] = a[0] +// r[1] = 0 +// r[2] = 0 +// r[3] = 0 +// +// r[4] = a[1] +// r[5] = a[2] +// r[6] = 0 +// r[7] = 0 +// +// r[8] = a[3] +// r[9] = 0 +// r[10] = 0 +// r[11] = 0 +// +// r[12] = a[4] +// r[13] = 0 +// r[14] = 0 +// r[15] = 0 + +struct group_varint_table_sse_mask_make_item + : group_varint_table_base_make_item { + constexpr auto partial_item(std::size_t d, std::size_t offset, std::size_t k) + const { + // if k < d, the j'th integer uses d bytes, consume them + // set remaining bytes in result to 0 + // 0xff: set corresponding byte in result to 0 + return std::uint32_t((k < d ? offset + k : std::size_t(0xff)) << (8 * k)); + } + + constexpr auto item_impl(std::size_t d, std::size_t offset) const { + // clang-format off + return + partial_item(d, offset, 0) | + partial_item(d, offset, 1) | + partial_item(d, offset, 2) | + partial_item(d, offset, 3) | + 0; + // clang-format on + } + + constexpr auto item(std::size_t index, std::size_t j) const { + return item_impl(get_d(index, j), get_offset(index, j)); + } + + constexpr auto operator()(std::size_t index) const { + return std::array<std::uint32_t, 4>{{ + item(index, 0), + item(index, 1), + item(index, 2), + item(index, 3), + }}; + } +}; + +#if FOLLY_SSE >= 3 +alignas(16) FOLLY_STORAGE_CONSTEXPR + decltype(groupVarintSSEMasks) groupVarintSSEMasks = + make_array_with<256>(group_varint_table_sse_mask_make_item{}); +#endif + +FOLLY_STORAGE_CONSTEXPR decltype(groupVarintLengths) groupVarintLengths = + make_array_with<256>(group_varint_table_length_make_item{}); + +} // namespace detail + +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/GroupVarint.h b/ios/Pods/Flipper-Folly/folly/GroupVarint.h new file mode 100644 index 000000000..2b3775c62 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/GroupVarint.h @@ -0,0 +1,668 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> +#include <limits> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/detail/GroupVarintDetail.h> +#include <folly/lang/Bits.h> +#include <folly/portability/Builtins.h> + +#if !defined(__GNUC__) && !defined(_MSC_VER) +#error GroupVarint.h requires GCC or MSVC +#endif + +#if FOLLY_X64 || defined(__i386__) || FOLLY_PPC64 || FOLLY_AARCH64 +#define FOLLY_HAVE_GROUP_VARINT 1 +#else +#define FOLLY_HAVE_GROUP_VARINT 0 +#endif + +#if FOLLY_HAVE_GROUP_VARINT + +#if FOLLY_SSE >= 3 +#include <nmmintrin.h> +namespace folly { +namespace detail { +extern const std::array<std::array<std::uint32_t, 4>, 256> groupVarintSSEMasks; +} // namespace detail +} // namespace folly +#endif + +namespace folly { +namespace detail { +extern const std::array<std::uint8_t, 256> groupVarintLengths; +} // namespace detail +} // namespace folly + +namespace folly { + +template <typename T> +class GroupVarint; + +/** + * GroupVarint encoding for 32-bit values. + * + * Encodes 4 32-bit integers at once, each using 1-4 bytes depending on size. + * There is one byte of overhead. (The first byte contains the lengths of + * the four integers encoded as two bits each; 00=1 byte .. 11=4 bytes) + * + * This implementation assumes little-endian and does unaligned 32-bit + * accesses, so it's basically not portable outside of the x86[_64] world. + */ +template <> +class GroupVarint<uint32_t> : public detail::GroupVarintBase<uint32_t> { + public: + /** + * Return the number of bytes used to encode these four values. + */ + static size_t size(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return kHeaderSize + kGroupSize + key(a) + key(b) + key(c) + key(d); + } + + /** + * Return the number of bytes used to encode four uint32_t values stored + * at consecutive positions in an array. + */ + static size_t size(const uint32_t* p) { + return size(p[0], p[1], p[2], p[3]); + } + + /** + * Return the number of bytes used to encode count (<= 4) values. + * If you clip a buffer after these many bytes, you can still decode + * the first "count" values correctly (if the remaining size() - + * partialSize() bytes are filled with garbage). + */ + static size_t partialSize(const type* p, size_t count) { + DCHECK_LE(count, kGroupSize); + size_t s = kHeaderSize + count; + for (; count; --count, ++p) { + s += key(*p); + } + return s; + } + + /** + * Return the number of values from *p that are valid from an encoded + * buffer of size bytes. + */ + static size_t partialCount(const char* p, size_t size) { + uint8_t v = uint8_t(*p); + size_t s = kHeaderSize; + s += 1 + b0key(v); + if (s > size) { + return 0; + } + s += 1 + b1key(v); + if (s > size) { + return 1; + } + s += 1 + b2key(v); + if (s > size) { + return 2; + } + s += 1 + b3key(v); + if (s > size) { + return 3; + } + return 4; + } + + /** + * Given a pointer to the beginning of an GroupVarint32-encoded block, + * return the number of bytes used by the encoding. + */ + static size_t encodedSize(const char* p) { + return kHeaderSize + kGroupSize + b0key(uint8_t(*p)) + b1key(uint8_t(*p)) + + b2key(uint8_t(*p)) + b3key(uint8_t(*p)); + } + + /** + * Encode four uint32_t values into the buffer pointed-to by p, and return + * the next position in the buffer (that is, one character past the last + * encoded byte). p needs to have at least size()+4 bytes available. + */ + static char* encode(char* p, uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + uint8_t b0key = key(a); + uint8_t b1key = key(b); + uint8_t b2key = key(c); + uint8_t b3key = key(d); + *p++ = (b3key << 6) | (b2key << 4) | (b1key << 2) | b0key; + storeUnaligned(p, a); + p += b0key + 1; + storeUnaligned(p, b); + p += b1key + 1; + storeUnaligned(p, c); + p += b2key + 1; + storeUnaligned(p, d); + p += b3key + 1; + return p; + } + + /** + * Encode four uint32_t values from the array pointed-to by src into the + * buffer pointed-to by p, similar to encode(p,a,b,c,d) above. + */ + static char* encode(char* p, const uint32_t* src) { + return encode(p, src[0], src[1], src[2], src[3]); + } + + /** + * Decode four uint32_t values from a buffer, and return the next position + * in the buffer (that is, one character past the last encoded byte). + * The buffer needs to have at least 3 extra bytes available (they + * may be read but ignored). + */ + static const char* decode_simple( + const char* p, + uint32_t* a, + uint32_t* b, + uint32_t* c, + uint32_t* d) { + size_t k = loadUnaligned<uint8_t>(p); + const char* end = p + detail::groupVarintLengths[k]; + ++p; + size_t k0 = b0key(k); + *a = loadUnaligned<uint32_t>(p) & kMask[k0]; + p += k0 + 1; + size_t k1 = b1key(k); + *b = loadUnaligned<uint32_t>(p) & kMask[k1]; + p += k1 + 1; + size_t k2 = b2key(k); + *c = loadUnaligned<uint32_t>(p) & kMask[k2]; + p += k2 + 1; + size_t k3 = b3key(k); + *d = loadUnaligned<uint32_t>(p) & kMask[k3]; + // p += k3+1; + return end; + } + + /** + * Decode four uint32_t values from a buffer and store them in the array + * pointed-to by dest, similar to decode(p,a,b,c,d) above. + */ + static const char* decode_simple(const char* p, uint32_t* dest) { + return decode_simple(p, dest, dest + 1, dest + 2, dest + 3); + } + +#if FOLLY_SSE >= 3 + /** + * Just like the non-SSSE3 decode below, but with the additional constraint + * that we must be able to read at least 17 bytes from the input pointer, p. + */ + static const char* decode(const char* p, uint32_t* dest) { + uint8_t key = uint8_t(p[0]); + __m128i val = _mm_loadu_si128((const __m128i*)(p + 1)); + __m128i mask = + _mm_load_si128((const __m128i*)detail::groupVarintSSEMasks[key].data()); + __m128i r = _mm_shuffle_epi8(val, mask); + _mm_storeu_si128((__m128i*)dest, r); + return p + detail::groupVarintLengths[key]; + } + + /** + * Just like decode_simple, but with the additional constraint that + * we must be able to read at least 17 bytes from the input pointer, p. + */ + static const char* + decode(const char* p, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) { + uint8_t key = uint8_t(p[0]); + __m128i val = _mm_loadu_si128((const __m128i*)(p + 1)); + __m128i mask = + _mm_load_si128((const __m128i*)detail::groupVarintSSEMasks[key].data()); + __m128i r = _mm_shuffle_epi8(val, mask); + + // Extracting 32 bits at a time out of an XMM register is a SSE4 feature +#if FOLLY_SSE >= 4 + *a = uint32_t(_mm_extract_epi32(r, 0)); + *b = uint32_t(_mm_extract_epi32(r, 1)); + *c = uint32_t(_mm_extract_epi32(r, 2)); + *d = uint32_t(_mm_extract_epi32(r, 3)); +#else /* !__SSE4__ */ + *a = _mm_extract_epi16(r, 0) + (_mm_extract_epi16(r, 1) << 16); + *b = _mm_extract_epi16(r, 2) + (_mm_extract_epi16(r, 3) << 16); + *c = _mm_extract_epi16(r, 4) + (_mm_extract_epi16(r, 5) << 16); + *d = _mm_extract_epi16(r, 6) + (_mm_extract_epi16(r, 7) << 16); +#endif /* __SSE4__ */ + + return p + detail::groupVarintLengths[key]; + } + +#else /* !__SSSE3__ */ + static const char* + decode(const char* p, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) { + return decode_simple(p, a, b, c, d); + } + + static const char* decode(const char* p, uint32_t* dest) { + return decode_simple(p, dest); + } +#endif /* __SSSE3__ */ + + private: + static uint8_t key(uint32_t x) { + // __builtin_clz is undefined for the x==0 case + return uint8_t(3 - (__builtin_clz(x | 1) / 8)); + } + static size_t b0key(size_t x) { + return x & 3; + } + static size_t b1key(size_t x) { + return (x >> 2) & 3; + } + static size_t b2key(size_t x) { + return (x >> 4) & 3; + } + static size_t b3key(size_t x) { + return (x >> 6) & 3; + } + + static const uint32_t kMask[]; +}; + +/** + * GroupVarint encoding for 64-bit values. + * + * Encodes 5 64-bit integers at once, each using 1-8 bytes depending on size. + * There are two bytes of overhead. (The first two bytes contain the lengths + * of the five integers encoded as three bits each; 000=1 byte .. 111 = 8 bytes) + * + * This implementation assumes little-endian and does unaligned 64-bit + * accesses, so it's basically not portable outside of the x86[_64] world. + */ +template <> +class GroupVarint<uint64_t> : public detail::GroupVarintBase<uint64_t> { + public: + /** + * Return the number of bytes used to encode these five values. + */ + static size_t + size(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) { + return kHeaderSize + kGroupSize + key(a) + key(b) + key(c) + key(d) + + key(e); + } + + /** + * Return the number of bytes used to encode five uint64_t values stored + * at consecutive positions in an array. + */ + static size_t size(const uint64_t* p) { + return size(p[0], p[1], p[2], p[3], p[4]); + } + + /** + * Return the number of bytes used to encode count (<= 4) values. + * If you clip a buffer after these many bytes, you can still decode + * the first "count" values correctly (if the remaining size() - + * partialSize() bytes are filled with garbage). + */ + static size_t partialSize(const type* p, size_t count) { + DCHECK_LE(count, kGroupSize); + size_t s = kHeaderSize + count; + for (; count; --count, ++p) { + s += key(*p); + } + return s; + } + + /** + * Return the number of values from *p that are valid from an encoded + * buffer of size bytes. + */ + static size_t partialCount(const char* p, size_t size) { + uint16_t v = loadUnaligned<uint16_t>(p); + size_t s = kHeaderSize; + s += 1 + b0key(v); + if (s > size) { + return 0; + } + s += 1 + b1key(v); + if (s > size) { + return 1; + } + s += 1 + b2key(v); + if (s > size) { + return 2; + } + s += 1 + b3key(v); + if (s > size) { + return 3; + } + s += 1 + b4key(v); + if (s > size) { + return 4; + } + return 5; + } + + /** + * Given a pointer to the beginning of an GroupVarint64-encoded block, + * return the number of bytes used by the encoding. + */ + static size_t encodedSize(const char* p) { + uint16_t n = loadUnaligned<uint16_t>(p); + return kHeaderSize + kGroupSize + b0key(n) + b1key(n) + b2key(n) + + b3key(n) + b4key(n); + } + + /** + * Encode five uint64_t values into the buffer pointed-to by p, and return + * the next position in the buffer (that is, one character past the last + * encoded byte). p needs to have at least size()+8 bytes available. + */ + static char* + encode(char* p, uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) { + uint16_t b0key = key(a); + uint16_t b1key = key(b); + uint16_t b2key = key(c); + uint16_t b3key = key(d); + uint16_t b4key = key(e); + storeUnaligned<uint16_t>( + p, + uint16_t( + (b4key << 12) | (b3key << 9) | (b2key << 6) | (b1key << 3) | + b0key)); + p += 2; + storeUnaligned(p, a); + p += b0key + 1; + storeUnaligned(p, b); + p += b1key + 1; + storeUnaligned(p, c); + p += b2key + 1; + storeUnaligned(p, d); + p += b3key + 1; + storeUnaligned(p, e); + p += b4key + 1; + return p; + } + + /** + * Encode five uint64_t values from the array pointed-to by src into the + * buffer pointed-to by p, similar to encode(p,a,b,c,d,e) above. + */ + static char* encode(char* p, const uint64_t* src) { + return encode(p, src[0], src[1], src[2], src[3], src[4]); + } + + /** + * Decode five uint64_t values from a buffer, and return the next position + * in the buffer (that is, one character past the last encoded byte). + * The buffer needs to have at least 7 bytes available (they may be read + * but ignored). + */ + static const char* decode( + const char* p, + uint64_t* a, + uint64_t* b, + uint64_t* c, + uint64_t* d, + uint64_t* e) { + uint16_t k = loadUnaligned<uint16_t>(p); + p += 2; + uint8_t k0 = b0key(k); + *a = loadUnaligned<uint64_t>(p) & kMask[k0]; + p += k0 + 1; + uint8_t k1 = b1key(k); + *b = loadUnaligned<uint64_t>(p) & kMask[k1]; + p += k1 + 1; + uint8_t k2 = b2key(k); + *c = loadUnaligned<uint64_t>(p) & kMask[k2]; + p += k2 + 1; + uint8_t k3 = b3key(k); + *d = loadUnaligned<uint64_t>(p) & kMask[k3]; + p += k3 + 1; + uint8_t k4 = b4key(k); + *e = loadUnaligned<uint64_t>(p) & kMask[k4]; + p += k4 + 1; + return p; + } + + /** + * Decode five uint64_t values from a buffer and store them in the array + * pointed-to by dest, similar to decode(p,a,b,c,d,e) above. + */ + static const char* decode(const char* p, uint64_t* dest) { + return decode(p, dest, dest + 1, dest + 2, dest + 3, dest + 4); + } + + private: + enum { kHeaderBytes = 2 }; + + static uint8_t key(uint64_t x) { + // __builtin_clzll is undefined for the x==0 case + return uint8_t(7 - (__builtin_clzll(x | 1) / 8)); + } + + static uint8_t b0key(uint16_t x) { + return x & 7u; + } + static uint8_t b1key(uint16_t x) { + return (x >> 3) & 7u; + } + static uint8_t b2key(uint16_t x) { + return (x >> 6) & 7u; + } + static uint8_t b3key(uint16_t x) { + return (x >> 9) & 7u; + } + static uint8_t b4key(uint16_t x) { + return (x >> 12) & 7u; + } + + static const uint64_t kMask[]; +}; + +typedef GroupVarint<uint32_t> GroupVarint32; +typedef GroupVarint<uint64_t> GroupVarint64; + +/** + * Simplify use of GroupVarint* for the case where data is available one + * entry at a time (instead of one group at a time). Handles buffering + * and an incomplete last chunk. + * + * Output is a function object that accepts character ranges: + * out(StringPiece) appends the given character range to the output. + */ +template <class T, class Output> +class GroupVarintEncoder { + public: + typedef GroupVarint<T> Base; + typedef T type; + + explicit GroupVarintEncoder(Output out) : out_(out), count_(0) {} + + ~GroupVarintEncoder() { + finish(); + } + + /** + * Add a value to the encoder. + */ + void add(type val) { + buf_[count_++] = val; + if (count_ == Base::kGroupSize) { + char* p = Base::encode(tmp_, buf_); + out_(StringPiece(tmp_, p)); + count_ = 0; + } + } + + /** + * Finish encoding, flushing any buffered values if necessary. + * After finish(), the encoder is immediately ready to encode more data + * to the same output. + */ + void finish() { + if (count_) { + // This is not strictly necessary, but it makes testing easy; + // uninitialized bytes are guaranteed to be recorded as taking one byte + // (not more). + for (size_t i = count_; i < Base::kGroupSize; i++) { + buf_[i] = 0; + } + Base::encode(tmp_, buf_); + out_(StringPiece(tmp_, Base::partialSize(buf_, count_))); + count_ = 0; + } + } + + /** + * Return the appender that was used. + */ + Output& output() { + return out_; + } + const Output& output() const { + return out_; + } + + /** + * Reset the encoder, disregarding any state (except what was already + * flushed to the output, of course). + */ + void clear() { + count_ = 0; + } + + private: + Output out_; + char tmp_[Base::kMaxSize]; + type buf_[Base::kGroupSize]; + size_t count_; +}; + +/** + * Simplify use of GroupVarint* for the case where the last group in the + * input may be incomplete (but the exact size of the input is known). + * Allows for extracting values one at a time. + */ +template <typename T> +class GroupVarintDecoder { + public: + typedef GroupVarint<T> Base; + typedef T type; + + GroupVarintDecoder() = default; + + explicit GroupVarintDecoder(StringPiece data, size_t maxCount = (size_t)-1) + : rrest_(data.end()), + p_(data.data()), + end_(data.end()), + limit_(end_), + pos_(0), + count_(0), + remaining_(maxCount) {} + + void reset(StringPiece data, size_t maxCount = (size_t)-1) { + rrest_ = data.end(); + p_ = data.data(); + end_ = data.end(); + limit_ = end_; + pos_ = 0; + count_ = 0; + remaining_ = maxCount; + } + + /** + * Read and return the next value. + */ + bool next(type* val) { + if (pos_ == count_) { + // refill + size_t rem = size_t(end_ - p_); + if (rem == 0 || remaining_ == 0) { + return false; + } + // next() attempts to read one full group at a time, and so we must have + // at least enough bytes readable after its end to handle the case if the + // last group is full. + // + // The best way to ensure this is to ensure that data has at least + // Base::kMaxSize - 1 bytes readable *after* the end, otherwise we'll copy + // into a temporary buffer. + if (limit_ - p_ < Base::kMaxSize) { + memcpy(tmp_, p_, rem); + p_ = tmp_; + end_ = p_ + rem; + limit_ = tmp_ + sizeof(tmp_); + } + pos_ = 0; + const char* n = Base::decode(p_, buf_); + if (n <= end_) { + // Full group could be decoded + if (remaining_ >= Base::kGroupSize) { + remaining_ -= Base::kGroupSize; + count_ = Base::kGroupSize; + p_ = n; + } else { + count_ = remaining_; + remaining_ = 0; + p_ += Base::partialSize(buf_, count_); + } + } else { + // Can't decode a full group + count_ = Base::partialCount(p_, size_t(end_ - p_)); + if (remaining_ >= count_) { + remaining_ -= count_; + p_ = end_; + } else { + count_ = remaining_; + remaining_ = 0; + p_ += Base::partialSize(buf_, count_); + } + if (count_ == 0) { + return false; + } + } + } + *val = buf_[pos_++]; + return true; + } + + StringPiece rest() const { + // This is only valid after next() returned false + CHECK(pos_ == count_ && (p_ == end_ || remaining_ == 0)); + // p_ may point to the internal buffer (tmp_), but we want + // to return subpiece of the original data + size_t size = size_t(end_ - p_); + return StringPiece(rrest_ - size, rrest_); + } + + private: + const char* rrest_; + const char* p_; + const char* end_; + const char* limit_; + char tmp_[2 * Base::kMaxSize]; + type buf_[Base::kGroupSize]; + size_t pos_; + size_t count_; + size_t remaining_; +}; + +typedef GroupVarintDecoder<uint32_t> GroupVarint32Decoder; +typedef GroupVarintDecoder<uint64_t> GroupVarint64Decoder; + +} // namespace folly + +#endif // FOLLY_HAVE_GROUP_VARINT diff --git a/ios/Pods/Flipper-Folly/folly/Hash.h b/ios/Pods/Flipper-Folly/folly/Hash.h new file mode 100644 index 000000000..fba2bbc7f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Hash.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// shims: +#include <folly/hash/Hash.h> diff --git a/ios/Pods/Flipper-Folly/folly/IPAddress.cpp b/ios/Pods/Flipper-Folly/folly/IPAddress.cpp new file mode 100644 index 000000000..00ba3ea33 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddress.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/IPAddress.h> + +#include <limits> +#include <ostream> +#include <string> +#include <vector> + +#include <folly/Format.h> +#include <folly/String.h> +#include <folly/detail/IPAddressSource.h> + +using std::ostream; +using std::string; +using std::vector; + +namespace folly { + +// free functions +size_t hash_value(const IPAddress& addr) { + return addr.hash(); +} +ostream& operator<<(ostream& os, const IPAddress& addr) { + os << addr.str(); + return os; +} +void toAppend(IPAddress addr, string* result) { + result->append(addr.str()); +} +void toAppend(IPAddress addr, fbstring* result) { + result->append(addr.str()); +} + +bool IPAddress::validate(StringPiece ip) noexcept { + return IPAddressV4::validate(ip) || IPAddressV6::validate(ip); +} + +// public static +IPAddressV4 IPAddress::createIPv4(const IPAddress& addr) { + if (addr.isV4()) { + return addr.asV4(); + } else { + return addr.asV6().createIPv4(); + } +} + +// public static +IPAddressV6 IPAddress::createIPv6(const IPAddress& addr) { + if (addr.isV6()) { + return addr.asV6(); + } else { + return addr.asV4().createIPv6(); + } +} + +namespace { +vector<string> splitIpSlashCidr(StringPiece ipSlashCidr) { + vector<string> vec; + split("/", ipSlashCidr, vec); + return vec; +} +} // namespace + +// public static +CIDRNetwork IPAddress::createNetwork( + StringPiece ipSlashCidr, + int defaultCidr, /* = -1 */ + bool applyMask /* = true */) { + auto const ret = + IPAddress::tryCreateNetwork(ipSlashCidr, defaultCidr, applyMask); + + if (ret.hasValue()) { + return ret.value(); + } + + if (ret.error() == CIDRNetworkError::INVALID_DEFAULT_CIDR) { + throw std::range_error("defaultCidr must be <= UINT8_MAX"); + } + + if (ret.error() == CIDRNetworkError::INVALID_IP_SLASH_CIDR) { + throw IPAddressFormatException(sformat( + "Invalid ipSlashCidr specified. Expected IP/CIDR format, got '{}'", + ipSlashCidr)); + } + + // Handler the remaining error cases. We re-parse the ip/mask pair + // to make error messages more meaningful + auto const vec = splitIpSlashCidr(ipSlashCidr); + + switch (ret.error()) { + case CIDRNetworkError::INVALID_IP: + CHECK_GE(vec.size(), 1); + throw IPAddressFormatException( + sformat("Invalid IP address {}", vec.at(0))); + case CIDRNetworkError::INVALID_CIDR: + CHECK_GE(vec.size(), 2); + throw IPAddressFormatException( + sformat("Mask value '{}' not a valid mask", vec.at(1))); + case CIDRNetworkError::CIDR_MISMATCH: { + auto const subnet = IPAddress::tryFromString(vec.at(0)).value(); + auto cidr = static_cast<uint8_t>( + (defaultCidr > -1) ? defaultCidr : (subnet.isV4() ? 32 : 128)); + + throw IPAddressFormatException(sformat( + "CIDR value '{}' is > network bit count '{}'", + vec.size() == 2 ? vec.at(1) : to<string>(cidr), + subnet.bitCount())); + } + case CIDRNetworkError::INVALID_DEFAULT_CIDR: + case CIDRNetworkError::INVALID_IP_SLASH_CIDR: + default: + // unreachable + break; + } + + CHECK(0); + + return CIDRNetwork{}; +} + +// public static +Expected<CIDRNetwork, CIDRNetworkError> IPAddress::tryCreateNetwork( + StringPiece ipSlashCidr, + int defaultCidr, + bool applyMask) { + if (defaultCidr > std::numeric_limits<uint8_t>::max()) { + return makeUnexpected(CIDRNetworkError::INVALID_DEFAULT_CIDR); + } + + auto const vec = splitIpSlashCidr(ipSlashCidr); + auto const elemCount = vec.size(); + + if (elemCount == 0 || // weird invalid string + elemCount > 2) { // invalid string (IP/CIDR/extras) + return makeUnexpected(CIDRNetworkError::INVALID_IP_SLASH_CIDR); + } + + auto const subnet = IPAddress::tryFromString(vec.at(0)); + if (subnet.hasError()) { + return makeUnexpected(CIDRNetworkError::INVALID_IP); + } + + auto cidr = static_cast<uint8_t>( + (defaultCidr > -1) ? defaultCidr : (subnet.value().isV4() ? 32 : 128)); + + if (elemCount == 2) { + auto const maybeCidr = tryTo<uint8_t>(vec.at(1)); + if (maybeCidr.hasError()) { + return makeUnexpected(CIDRNetworkError::INVALID_CIDR); + } + cidr = maybeCidr.value(); + } + + if (cidr > subnet.value().bitCount()) { + return makeUnexpected(CIDRNetworkError::CIDR_MISMATCH); + } + + return std::make_pair( + applyMask ? subnet.value().mask(cidr) : subnet.value(), cidr); +} + +// public static +std::string IPAddress::networkToString(const CIDRNetwork& network) { + return sformat("{}/{}", network.first.str(), network.second); +} + +// public static +IPAddress IPAddress::fromBinary(ByteRange bytes) { + if (bytes.size() == 4) { + return IPAddress(IPAddressV4::fromBinary(bytes)); + } else if (bytes.size() == 16) { + return IPAddress(IPAddressV6::fromBinary(bytes)); + } else { + string hexval = detail::Bytes::toHex(bytes.data(), bytes.size()); + throw IPAddressFormatException( + sformat("Invalid address with hex value '{}'", hexval)); + } +} + +Expected<IPAddress, IPAddressFormatError> IPAddress::tryFromBinary( + ByteRange bytes) noexcept { + // Check IPv6 first since it's our main protocol. + if (bytes.size() == 16) { + return IPAddressV6::tryFromBinary(bytes); + } else if (bytes.size() == 4) { + return IPAddressV4::tryFromBinary(bytes); + } else { + return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY); + } +} + +// public static +IPAddress IPAddress::fromLong(uint32_t src) { + return IPAddress(IPAddressV4::fromLong(src)); +} +IPAddress IPAddress::fromLongHBO(uint32_t src) { + return IPAddress(IPAddressV4::fromLongHBO(src)); +} + +// default constructor +IPAddress::IPAddress() : addr_(), family_(AF_UNSPEC) {} + +// public string constructor +IPAddress::IPAddress(StringPiece str) : addr_(), family_(AF_UNSPEC) { + auto maybeIp = tryFromString(str); + if (maybeIp.hasError()) { + throw IPAddressFormatException( + to<std::string>("Invalid IP address '", str, "'")); + } + *this = maybeIp.value(); +} + +Expected<IPAddress, IPAddressFormatError> IPAddress::tryFromString( + StringPiece str) noexcept { + // need to check for V4 address second, since IPv4-mapped IPv6 addresses may + // contain a period + if (str.find(':') != string::npos) { + return IPAddressV6::tryFromString(str); + } else if (str.find('.') != string::npos) { + return IPAddressV4::tryFromString(str); + } else { + return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY); + } +} + +// public sockaddr constructor +IPAddress::IPAddress(const sockaddr* addr) : addr_(), family_(AF_UNSPEC) { + if (addr == nullptr) { + throw IPAddressFormatException("sockaddr == nullptr"); + } + family_ = addr->sa_family; + switch (addr->sa_family) { + case AF_INET: { + auto v4addr = reinterpret_cast<const sockaddr_in*>(addr); + addr_.ipV4Addr = IPAddressV4(v4addr->sin_addr); + break; + } + case AF_INET6: { + auto v6addr = reinterpret_cast<const sockaddr_in6*>(addr); + addr_.ipV6Addr = IPAddressV6(*v6addr); + break; + } + default: + throw InvalidAddressFamilyException(addr->sa_family); + } +} + +// public ipv4 constructor +IPAddress::IPAddress(const IPAddressV4 ipV4Addr) noexcept + : addr_(ipV4Addr), family_(AF_INET) {} + +// public ipv4 constructor +IPAddress::IPAddress(const in_addr ipV4Addr) noexcept + : addr_(IPAddressV4(ipV4Addr)), family_(AF_INET) {} + +// public ipv6 constructor +IPAddress::IPAddress(const IPAddressV6& ipV6Addr) noexcept + : addr_(ipV6Addr), family_(AF_INET6) {} + +// public ipv6 constructor +IPAddress::IPAddress(const in6_addr& ipV6Addr) noexcept + : addr_(IPAddressV6(ipV6Addr)), family_(AF_INET6) {} + +// Assign from V4 address +IPAddress& IPAddress::operator=(const IPAddressV4& ipv4_addr) noexcept { + addr_ = IPAddressV46(ipv4_addr); + family_ = AF_INET; + return *this; +} + +// Assign from V6 address +IPAddress& IPAddress::operator=(const IPAddressV6& ipv6_addr) noexcept { + addr_ = IPAddressV46(ipv6_addr); + family_ = AF_INET6; + return *this; +} + +// public +bool IPAddress::inSubnet(StringPiece cidrNetwork) const { + auto subnetInfo = IPAddress::createNetwork(cidrNetwork); + return inSubnet(subnetInfo.first, subnetInfo.second); +} + +// public +bool IPAddress::inSubnet(const IPAddress& subnet, uint8_t cidr) const { + if (bitCount() == subnet.bitCount()) { + if (isV4()) { + return asV4().inSubnet(subnet.asV4(), cidr); + } else { + return asV6().inSubnet(subnet.asV6(), cidr); + } + } + // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4 + // address and vice-versa + if (isV6()) { + const IPAddressV6& v6addr = asV6(); + const IPAddressV4& v4subnet = subnet.asV4(); + if (v6addr.is6To4()) { + return v6addr.getIPv4For6To4().inSubnet(v4subnet, cidr); + } + } else if (subnet.isV6()) { + const IPAddressV6& v6subnet = subnet.asV6(); + const IPAddressV4& v4addr = asV4(); + if (v6subnet.is6To4()) { + return v4addr.inSubnet(v6subnet.getIPv4For6To4(), cidr); + } + } + return false; +} + +// public +bool IPAddress::inSubnetWithMask(const IPAddress& subnet, ByteRange mask) + const { + auto mkByteArray4 = [&]() -> ByteArray4 { + ByteArray4 ba{{0}}; + std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 4)); + return ba; + }; + + if (bitCount() == subnet.bitCount()) { + if (isV4()) { + return asV4().inSubnetWithMask(subnet.asV4(), mkByteArray4()); + } else { + ByteArray16 ba{{0}}; + std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 16)); + return asV6().inSubnetWithMask(subnet.asV6(), ba); + } + } + + // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4 + // address and vice-versa + if (isV6()) { + const IPAddressV6& v6addr = asV6(); + const IPAddressV4& v4subnet = subnet.asV4(); + if (v6addr.is6To4()) { + return v6addr.getIPv4For6To4().inSubnetWithMask(v4subnet, mkByteArray4()); + } + } else if (subnet.isV6()) { + const IPAddressV6& v6subnet = subnet.asV6(); + const IPAddressV4& v4addr = asV4(); + if (v6subnet.is6To4()) { + return v4addr.inSubnetWithMask(v6subnet.getIPv4For6To4(), mkByteArray4()); + } + } + return false; +} + +uint8_t IPAddress::getNthMSByte(size_t byteIndex) const { + const auto highestIndex = byteCount() - 1; + if (byteIndex > highestIndex) { + throw std::invalid_argument(sformat( + "Byte index must be <= {} for addresses of type: {}", + highestIndex, + detail::familyNameStr(family()))); + } + if (isV4()) { + return asV4().bytes()[byteIndex]; + } + return asV6().bytes()[byteIndex]; +} + +// public +bool operator==(const IPAddress& addr1, const IPAddress& addr2) { + if (addr1.empty() || addr2.empty()) { + return addr1.empty() == addr2.empty(); + } + if (addr1.family() == addr2.family()) { + if (addr1.isV6()) { + return (addr1.asV6() == addr2.asV6()); + } else if (addr1.isV4()) { + return (addr1.asV4() == addr2.asV4()); + } else { + CHECK_EQ(addr1.family(), AF_UNSPEC); + // Two default initialized AF_UNSPEC addresses should be considered equal. + // AF_UNSPEC is the only other value for which an IPAddress can be + // created, in the default constructor case. + return true; + } + } + // addr1 is v4 mapped v6 address, addr2 is v4 + if (addr1.isIPv4Mapped() && addr2.isV4()) { + if (IPAddress::createIPv4(addr1) == addr2.asV4()) { + return true; + } + } + // addr2 is v4 mapped v6 address, addr1 is v4 + if (addr2.isIPv4Mapped() && addr1.isV4()) { + if (IPAddress::createIPv4(addr2) == addr1.asV4()) { + return true; + } + } + // we only compare IPv4 and IPv6 addresses + return false; +} + +bool operator<(const IPAddress& addr1, const IPAddress& addr2) { + if (addr1.empty() || addr2.empty()) { + return addr1.empty() < addr2.empty(); + } + if (addr1.family() == addr2.family()) { + if (addr1.isV6()) { + return (addr1.asV6() < addr2.asV6()); + } else if (addr1.isV4()) { + return (addr1.asV4() < addr2.asV4()); + } else { + CHECK_EQ(addr1.family(), AF_UNSPEC); + // Two default initialized AF_UNSPEC addresses can not be less than each + // other. AF_UNSPEC is the only other value for which an IPAddress can be + // created, in the default constructor case. + return false; + } + } + if (addr1.isV6()) { + // means addr2 is v4, convert it to a mapped v6 address and compare + return addr1.asV6() < addr2.asV4().createIPv6(); + } + if (addr2.isV6()) { + // means addr2 is v6, convert addr1 to v4 mapped and compare + return addr1.asV4().createIPv6() < addr2.asV6(); + } + return false; +} + +CIDRNetwork IPAddress::longestCommonPrefix( + const CIDRNetwork& one, + const CIDRNetwork& two) { + if (one.first.family() != two.first.family()) { + throw std::invalid_argument(sformat( + "Can't compute longest common prefix between addresses of different" + "families. Passed: {} and {}", + detail::familyNameStr(one.first.family()), + detail::familyNameStr(two.first.family()))); + } + if (one.first.isV4()) { + auto prefix = IPAddressV4::longestCommonPrefix( + {one.first.asV4(), one.second}, {two.first.asV4(), two.second}); + return {IPAddress(prefix.first), prefix.second}; + } else if (one.first.isV6()) { + auto prefix = IPAddressV6::longestCommonPrefix( + {one.first.asV6(), one.second}, {two.first.asV6(), two.second}); + return {IPAddress(prefix.first), prefix.second}; + } else { + throw std::invalid_argument("Unknown address family"); + } +} + +// clang-format off +[[noreturn]] void IPAddress::asV4Throw() const { + auto fam = detail::familyNameStr(family()); + throw InvalidAddressFamilyException( + sformat("Can't convert address with family {} to AF_INET address", fam)); +} + +[[noreturn]] void IPAddress::asV6Throw() const { + auto fam = detail::familyNameStr(family()); + throw InvalidAddressFamilyException( + sformat("Can't convert address with family {} to AF_INET6 address", fam)); +} +// clang-format on + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/IPAddress.h b/ios/Pods/Flipper-Folly/folly/IPAddress.h new file mode 100644 index 000000000..a91e1b30f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddress.h @@ -0,0 +1,562 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <iosfwd> +#include <memory> +#include <string> +#include <type_traits> +#include <utility> // std::pair + +#include <folly/ConstexprMath.h> +#include <folly/IPAddressException.h> +#include <folly/IPAddressV4.h> +#include <folly/IPAddressV6.h> +#include <folly/Range.h> +#include <folly/detail/IPAddress.h> +#include <folly/lang/Exception.h> + +namespace folly { + +class IPAddress; + +/** + * Pair of IPAddress, netmask + */ +typedef std::pair<IPAddress, uint8_t> CIDRNetwork; + +/** + * Provides a unified interface for IP addresses. + * + * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are + * compared as V4 addresses. + * + * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO + * if working in host byte order. + * + * Example usage: + * @code + * IPAddress v4addr("192.0.2.129"); + * IPAddress v6map("::ffff:192.0.2.129"); + * CHECK(v4addr.inSubnet("192.0.2.0/24") == + * v4addr.inSubnet(IPAddress("192.0.2.0"), 24)); + * CHECK(v4addr.inSubnet("192.0.2.128/30")); + * CHECK(!v4addr.inSubnet("192.0.2.128/32")); + * CHECK(v4addr.asV4().toLong() == 2164392128); + * CHECK(v4addr.asV4().toLongHBO() == 3221226113); + * CHECK(v4addr.isV4()); + * CHECK(v6addr.isV6()); + * CHECK(v4addr == v6map); + * CHECK(v6map.isIPv4Mapped()); + * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map)); + * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6()); + * @encode + */ +class IPAddress { + private: + template <typename F> + auto pick(F f) const { + return isV4() ? f(asV4()) : isV6() ? f(asV6()) : f(asNone()); + } + + class IPAddressNone { + public: + bool isZero() const { + return true; + } + size_t bitCount() const { + return 0; + } + std::string toJson() const { + return "{family:'AF_UNSPEC', addr:'', hash:0}"; + } + std::size_t hash() const { + return std::hash<uint64_t>{}(0); + } + bool isLoopback() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + bool isLinkLocal() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + bool isLinkLocalBroadcast() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + bool isNonroutable() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + bool isPrivate() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + bool isMulticast() const { + throw_exception<InvalidAddressFamilyException>("empty address"); + } + IPAddress mask(uint8_t numBits) const { + (void)numBits; + return IPAddress(); + } + std::string str() const { + return ""; + } + std::string toFullyQualified() const { + return ""; + } + void toFullyQualifiedAppend(std::string& out) const { + (void)out; + return; + } + uint8_t version() const { + return 0; + } + const unsigned char* bytes() const { + return nullptr; + } + }; + + IPAddressNone const& asNone() const { + if (!empty()) { + throw_exception<InvalidAddressFamilyException>("not empty"); + } + return addr_.ipNoneAddr; + } + + public: + // returns true iff the input string can be parsed as an ip-address + static bool validate(StringPiece ip) noexcept; + + // return the V4 representation of the address, converting it from V6 to V4 if + // needed. Note that this will throw an IPAddressFormatException if the V6 + // address is not IPv4Mapped. + static IPAddressV4 createIPv4(const IPAddress& addr); + + // return the V6 representation of the address, converting it from V4 to V6 if + // needed. + static IPAddressV6 createIPv6(const IPAddress& addr); + + /** + * Create a network and mask from a CIDR formatted address string. + * @param [in] ipSlashCidr IP/CIDR formatted string to split + * @param [in] defaultCidr default value if no /N specified (if defaultCidr + * is -1, will use /32 for IPv4 and /128 for IPv6) + * @param [in] mask apply mask on the address or not, + * e.g. 192.168.13.46/24 => 192.168.13.0/24 + * @return either pair with IPAddress network and uint8_t mask or + * CIDRNetworkError + */ + static Expected<CIDRNetwork, CIDRNetworkError> tryCreateNetwork( + StringPiece ipSlashCidr, + int defaultCidr = -1, + bool mask = true); + + /** + * Create a network and mask from a CIDR formatted address string. + * Same as tryCreateNetwork() but throws IPAddressFormatException on error. + * The implementation calls tryCreateNetwork(...) underneath + * + * @throws IPAddressFormatException if invalid address + * @return pair with IPAddress network and uint8_t mask + */ + static CIDRNetwork createNetwork( + StringPiece ipSlashCidr, + int defaultCidr = -1, + bool mask = true); + + /** + * Return a string representation of a CIDR block created with createNetwork. + * @param [in] network, pair of address and cidr + * + * @return string representing the netblock + */ + static std::string networkToString(const CIDRNetwork& network); + + /** + * Create a new IPAddress instance from the provided binary data + * in network byte order. + * @throws IPAddressFormatException if len is not 4 or 16 + */ + static IPAddress fromBinary(ByteRange bytes); + + /** + * Non-throwing version of fromBinary(). + * On failure returns IPAddressFormatError. + */ + static Expected<IPAddress, IPAddressFormatError> tryFromBinary( + ByteRange bytes) noexcept; + + /** + * Tries to create a new IPAddress instance from provided string and + * returns it on success. Returns IPAddressFormatError on failure. + */ + static Expected<IPAddress, IPAddressFormatError> tryFromString( + StringPiece str) noexcept; + + /** + * Create an IPAddress from a 32bit long (network byte order). + * @throws IPAddressFormatException + */ + static IPAddress fromLong(uint32_t src); + // Same as above, but host byte order + static IPAddress fromLongHBO(uint32_t src); + + // Given 2 IPAddress,mask pairs extract the longest common IPAddress, + // mask pair + static CIDRNetwork longestCommonPrefix( + const CIDRNetwork& one, + const CIDRNetwork& two); + + /** + * Constructs an uninitialized IPAddress. + */ + IPAddress(); + + /** + * Parse an IPAddress from a string representation. + * + * Formats accepted are exactly the same as the ones accepted by inet_pton(), + * using AF_INET6 if the string contains colons, and AF_INET otherwise; + * with the exception that the whole address can optionally be enclosed + * in square brackets. + * + * @throws IPAddressFormatException + */ + explicit IPAddress(StringPiece str); + + /** + * Create an IPAddress from a sockaddr. + * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6 + */ + explicit IPAddress(const sockaddr* addr); + + // Create an IPAddress from a V4 address + /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept; + /* implicit */ IPAddress(const in_addr addr) noexcept; + + // Create an IPAddress from a V6 address + /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept; + /* implicit */ IPAddress(const in6_addr& addr) noexcept; + + // Assign from V4 address + IPAddress& operator=(const IPAddressV4& ipv4_addr) noexcept; + + // Assign from V6 address + IPAddress& operator=(const IPAddressV6& ipv6_addr) noexcept; + + /** + * Converts an IPAddress to an IPAddressV4 instance. + * @note This is not some handy convenience wrapper to convert an IPv4 address + * to a mapped IPv6 address. If you want that use + * IPAddress::createIPv6(addr) + * @throws InvalidAddressFamilyException is not a V4 instance + */ + const IPAddressV4& asV4() const { + if (UNLIKELY(!isV4())) { + asV4Throw(); + } + return addr_.ipV4Addr; + } + + /** + * Converts an IPAddress to an IPAddressV6 instance. + * @throws InvalidAddressFamilyException is not a V6 instance + */ + const IPAddressV6& asV6() const { + if (UNLIKELY(!isV6())) { + asV6Throw(); + } + return addr_.ipV6Addr; + } + + // Return sa_family_t of IPAddress + sa_family_t family() const { + return family_; + } + + // Populate sockaddr_storage with an appropriate value + int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const { + if (dest == nullptr) { + throw IPAddressFormatException("dest must not be null"); + } + memset(dest, 0, sizeof(sockaddr_storage)); + dest->ss_family = family(); + + if (isV4()) { + sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest); + sin->sin_addr = asV4().toAddr(); + sin->sin_port = port; +#if defined(__APPLE__) + sin->sin_len = sizeof(*sin); +#endif + return sizeof(*sin); + } else if (isV6()) { + sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest); + sin->sin6_addr = asV6().toAddr(); + sin->sin6_port = port; + sin->sin6_scope_id = asV6().getScopeId(); +#if defined(__APPLE__) + sin->sin6_len = sizeof(*sin); +#endif + return sizeof(*sin); + } else { + throw InvalidAddressFamilyException(family()); + } + } + + /** + * Check if the address is found in the specified CIDR netblock. + * + * This will return false if the specified cidrNet is V4, but the address is + * V6. It will also return false if the specified cidrNet is V6 but the + * address is V4. This method will do the right thing in the case of a v6 + * mapped v4 address. + * + * @note This is slower than the below counterparts. If perf is important use + * one of the two argument variations below. + * @param [in] ipSlashCidr address in "192.168.1.0/24" format + * @throws IPAddressFormatException if no /mask + * @return true if address is part of specified subnet with cidr + */ + bool inSubnet(StringPiece cidrNetwork) const; + + /** + * Check if an IPAddress belongs to a subnet. + * @param [in] subnet Subnet to check against (e.g. 192.168.1.0) + * @param [in] cidr CIDR for subnet (e.g. 24 for /24) + * @return true if address is part of specified subnet with cidr + */ + bool inSubnet(const IPAddress& subnet, uint8_t cidr) const; + + /** + * Check if an IPAddress belongs to the subnet with the given mask. + * This is the same as inSubnet but the mask is provided instead of looked up + * from the cidr. + * @param [in] subnet Subnet to check against + * @param [in] mask The netmask for the subnet + * @return true if address is part of the specified subnet with mask + */ + bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const; + + // @return true if address is a v4 mapped address + bool isIPv4Mapped() const { + return isV6() && asV6().isIPv4Mapped(); + } + + // @return true if address is uninitialized + bool empty() const { + return family_ == AF_UNSPEC; + } + + // @return true if address is initialized + explicit operator bool() const { + return !empty(); + } + + // @return true if this is an IPAddressV4 instance + bool isV4() const { + return family_ == AF_INET; + } + + // @return true if this is an IPAddressV6 instance + bool isV6() const { + return family_ == AF_INET6; + } + + // @return true if this address is all zeros + bool isZero() const { + return pick([&](auto& _) { return _.isZero(); }); + } + + // Number of bits in the address representation. + size_t bitCount() const { + return pick([&](auto& _) { return _.bitCount(); }); + } + // Number of bytes in the address representation. + size_t byteCount() const { + return bitCount() / 8; + } + // get nth most significant bit - 0 indexed + bool getNthMSBit(size_t bitIndex) const { + return detail::getNthMSBitImpl(*this, bitIndex, family()); + } + // get nth most significant byte - 0 indexed + uint8_t getNthMSByte(size_t byteIndex) const; + // get nth bit - 0 indexed + bool getNthLSBit(size_t bitIndex) const { + return getNthMSBit(bitCount() - bitIndex - 1); + } + // get nth byte - 0 indexed + uint8_t getNthLSByte(size_t byteIndex) const { + return getNthMSByte(byteCount() - byteIndex - 1); + } + /** + * Get human-readable string representation of the address. + * + * This prints a string representation of the address, for human consumption + * or logging. The string will take the form of a JSON object that looks like: + * {family:'AF_INET|AF_INET6', addr:'address', hash:long}. + */ + std::string toJson() const { + return pick([&](auto& _) { return _.toJson(); }); + } + + // Hash of address + std::size_t hash() const { + return pick([&](auto& _) { return _.hash(); }); + } + + // Return true if the address qualifies as localhost. + bool isLoopback() const { + return pick([&](auto& _) { return _.isLoopback(); }); + } + + // Return true if the address qualifies as link local + bool isLinkLocal() const { + return pick([&](auto& _) { return _.isLinkLocal(); }); + } + + // Return true if the address qualifies as broadcast. + bool isLinkLocalBroadcast() const { + return pick([&](auto& _) { return _.isLinkLocalBroadcast(); }); + } + + /** + * Return true if the address is a special purpose address, as per rfc6890 + * (i.e. 0.0.0.0). + * For V6, true if the address is not in one of global scope blocks: + * 2000::/3, ffxe::/16. + */ + bool isNonroutable() const { + return pick([&](auto& _) { return _.isNonroutable(); }); + } + + /** + * Return true if the address is private, as per rfc1918 and rfc4193 + * (for example, 192.168.xxx.xxx or fc00::/7 addresses) + */ + bool isPrivate() const { + return pick([&](auto& _) { return _.isPrivate(); }); + } + + // Return true if the address is a multicast address. + bool isMulticast() const { + return pick([&](auto& _) { return _.isMulticast(); }); + } + + /** + * Creates IPAddress instance with all but most significant numBits set to 0. + * @param [in] numBits number of bits to mask + * @throws abort if numBits > bitCount() + * @return IPAddress instance with bits set to 0 + */ + IPAddress mask(uint8_t numBits) const { + return pick([&](auto& _) { return IPAddress(_.mask(numBits)); }); + } + + /** + * Provides a string representation of address. + * @note The string representation is calculated on demand. + * @throws IPAddressFormatException on inet_ntop error + */ + std::string str() const { + return pick([&](auto& _) { return _.str(); }); + } + + /** + * Return the fully qualified string representation of the address. + * For V4 addresses this is the same as calling str(). For V6 addresses + * this is the hex representation with : characters inserted every 4 digits. + */ + std::string toFullyQualified() const { + return pick([&](auto& _) { return _.toFullyQualified(); }); + } + + /// Same as toFullyQualified but append to an output string. + void toFullyQualifiedAppend(std::string& out) const { + return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); }); + } + + // Address version (0 if empty, or 4 or 6 if nonempty) + uint8_t version() const { + return pick([&](auto& _) { return _.version(); }); + } + + /** + * Access to address bytes, in network byte order. + */ + const unsigned char* bytes() const { + return pick([&](auto& _) { return _.bytes(); }); + } + + private: + [[noreturn]] void asV4Throw() const; + [[noreturn]] void asV6Throw() const; + + typedef union IPAddressV46 { + IPAddressNone ipNoneAddr; + IPAddressV4 ipV4Addr; + IPAddressV6 ipV6Addr; + IPAddressV46() noexcept : ipNoneAddr() {} + explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {} + explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {} + } IPAddressV46; + IPAddressV46 addr_; + sa_family_t family_; +}; + +// boost::hash uses hash_value() so this allows boost::hash to work +// automatically for IPAddress +std::size_t hash_value(const IPAddress& addr); +std::ostream& operator<<(std::ostream& os, const IPAddress& addr); +// Define toAppend() to allow IPAddress to be used with folly::to<string> +void toAppend(IPAddress addr, std::string* result); +void toAppend(IPAddress addr, fbstring* result); + +/** + * Return true if two addresses are equal. + * + * @note This takes into consideration V4 mapped addresses as well. If one + * address is v4 mapped we compare the v4 addresses. + * + * @return true if the two addresses are equal. + */ +bool operator==(const IPAddress& addr1, const IPAddress& addr2); +// Return true if addr1 < addr2 +bool operator<(const IPAddress& addr1, const IPAddress& addr2); +// Derived operators +inline bool operator!=(const IPAddress& a, const IPAddress& b) { + return !(a == b); +} +inline bool operator>(const IPAddress& a, const IPAddress& b) { + return b < a; +} +inline bool operator<=(const IPAddress& a, const IPAddress& b) { + return !(a > b); +} +inline bool operator>=(const IPAddress& a, const IPAddress& b) { + return !(a < b); +} + +} // namespace folly + +namespace std { +template <> +struct hash<folly::IPAddress> { + size_t operator()(const folly::IPAddress& addr) const { + return addr.hash(); + } +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/IPAddressException.h b/ios/Pods/Flipper-Folly/folly/IPAddressException.h new file mode 100644 index 000000000..b98b2fb81 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddressException.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <exception> +#include <string> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/detail/IPAddress.h> + +namespace folly { + +/** + * Error codes for non-throwing interface of IPAddress family of functions. + */ +enum class IPAddressFormatError { INVALID_IP, UNSUPPORTED_ADDR_FAMILY }; + +/** + * Wraps error from parsing IP/MASK string + */ +enum class CIDRNetworkError { + INVALID_DEFAULT_CIDR, + INVALID_IP_SLASH_CIDR, + INVALID_IP, + INVALID_CIDR, + CIDR_MISMATCH, +}; + +/** + * Exception for invalid IP addresses. + */ +class FOLLY_EXPORT IPAddressFormatException : public std::exception { + public: + explicit IPAddressFormatException(std::string msg) noexcept + : msg_(std::move(msg)) {} + IPAddressFormatException(const IPAddressFormatException&) = default; + IPAddressFormatException(IPAddressFormatException&&) = default; + IPAddressFormatException& operator=(const IPAddressFormatException&) = + default; + IPAddressFormatException& operator=(IPAddressFormatException&&) = default; + + ~IPAddressFormatException() noexcept override {} + const char* what() const noexcept override { + return msg_.c_str(); + } + + private: + std::string msg_; +}; + +class FOLLY_EXPORT InvalidAddressFamilyException + : public IPAddressFormatException { + public: + explicit InvalidAddressFamilyException(std::string msg) noexcept + : IPAddressFormatException(std::move(msg)) {} + explicit InvalidAddressFamilyException(sa_family_t family) noexcept + : InvalidAddressFamilyException( + "Address family " + detail::familyNameStr(family) + + " is not AF_INET or AF_INET6") {} + InvalidAddressFamilyException(const InvalidAddressFamilyException&) = default; + InvalidAddressFamilyException(InvalidAddressFamilyException&&) = default; + InvalidAddressFamilyException& operator=( + const InvalidAddressFamilyException&) = default; + InvalidAddressFamilyException& operator=(InvalidAddressFamilyException&&) = + default; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/IPAddressV4.cpp b/ios/Pods/Flipper-Folly/folly/IPAddressV4.cpp new file mode 100644 index 000000000..d7c1fe050 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddressV4.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/IPAddressV4.h> + +#include <ostream> +#include <string> + +#include <folly/Format.h> +#include <folly/IPAddress.h> +#include <folly/IPAddressV6.h> +#include <folly/detail/IPAddressSource.h> + +using std::ostream; +using std::string; + +namespace folly { + +// free functions +size_t hash_value(const IPAddressV4& addr) { + return addr.hash(); +} +ostream& operator<<(ostream& os, const IPAddressV4& addr) { + os << addr.str(); + return os; +} +void toAppend(IPAddressV4 addr, string* result) { + result->append(addr.str()); +} +void toAppend(IPAddressV4 addr, fbstring* result) { + result->append(addr.str()); +} + +bool IPAddressV4::validate(StringPiece ip) noexcept { + return tryFromString(ip).hasValue(); +} + +// public static +IPAddressV4 IPAddressV4::fromLong(uint32_t src) { + in_addr addr; + addr.s_addr = src; + return IPAddressV4(addr); +} + +IPAddressV4 IPAddressV4::fromLongHBO(uint32_t src) { + in_addr addr; + addr.s_addr = htonl(src); + return IPAddressV4(addr); +} + +// static public +uint32_t IPAddressV4::toLong(StringPiece ip) { + auto str = ip.str(); + in_addr addr; + if (inet_pton(AF_INET, str.c_str(), &addr) != 1) { + throw IPAddressFormatException( + sformat("Can't convert invalid IP '{}' to long", ip)); + } + return addr.s_addr; +} + +// static public +uint32_t IPAddressV4::toLongHBO(StringPiece ip) { + return ntohl(IPAddressV4::toLong(ip)); +} + +// public default constructor +IPAddressV4::IPAddressV4() = default; + +// ByteArray4 constructor +IPAddressV4::IPAddressV4(const ByteArray4& src) noexcept : addr_(src) {} + +// public string constructor +IPAddressV4::IPAddressV4(StringPiece addr) : addr_() { + auto maybeIp = tryFromString(addr); + if (maybeIp.hasError()) { + throw IPAddressFormatException( + to<std::string>("Invalid IPv4 address '", addr, "'")); + } + *this = maybeIp.value(); +} + +Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromString( + StringPiece str) noexcept { + struct in_addr inAddr; + if (inet_pton(AF_INET, str.str().c_str(), &inAddr) != 1) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } + return IPAddressV4(inAddr); +} + +// in_addr constructor +IPAddressV4::IPAddressV4(const in_addr src) noexcept : addr_(src) {} + +IPAddressV4 IPAddressV4::fromBinary(ByteRange bytes) { + auto maybeIp = tryFromBinary(bytes); + if (maybeIp.hasError()) { + throw IPAddressFormatException(to<std::string>( + "Invalid IPv4 binary data: length must be 4 bytes, got ", + bytes.size())); + } + return maybeIp.value(); +} + +Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromBinary( + ByteRange bytes) noexcept { + IPAddressV4 addr; + auto setResult = addr.trySetFromBinary(bytes); + if (setResult.hasError()) { + return makeUnexpected(setResult.error()); + } + return addr; +} + +Expected<Unit, IPAddressFormatError> IPAddressV4::trySetFromBinary( + ByteRange bytes) noexcept { + if (bytes.size() != 4) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } + memcpy(&addr_.inAddr_.s_addr, bytes.data(), sizeof(in_addr)); + return folly::unit; +} + +// static +IPAddressV4 IPAddressV4::fromInverseArpaName(const std::string& arpaname) { + auto piece = StringPiece(arpaname); + // input must be something like 1.0.168.192.in-addr.arpa + if (!piece.removeSuffix(".in-addr.arpa")) { + throw IPAddressFormatException( + sformat("input does not end with '.in-addr.arpa': '{}'", arpaname)); + } + std::vector<StringPiece> pieces; + split(".", piece, pieces); + if (pieces.size() != 4) { + throw IPAddressFormatException(sformat("Invalid input. Got {}", piece)); + } + // reverse 1.0.168.192 -> 192.168.0.1 + return IPAddressV4(join(".", pieces.rbegin(), pieces.rend())); +} +IPAddressV6 IPAddressV4::createIPv6() const { + ByteArray16 ba{}; + ba[10] = 0xff; + ba[11] = 0xff; + std::memcpy(&ba[12], bytes(), 4); + return IPAddressV6(ba); +} + +// public +IPAddressV6 IPAddressV4::getIPv6For6To4() const { + ByteArray16 ba{}; + ba[0] = (uint8_t)((IPAddressV6::PREFIX_6TO4 & 0xFF00) >> 8); + ba[1] = (uint8_t)(IPAddressV6::PREFIX_6TO4 & 0x00FF); + std::memcpy(&ba[2], bytes(), 4); + return IPAddressV6(ba); +} + +// public +string IPAddressV4::toJson() const { + return sformat("{{family:'AF_INET', addr:'{}', hash:{}}}", str(), hash()); +} + +// public +bool IPAddressV4::inSubnet(StringPiece cidrNetwork) const { + auto subnetInfo = IPAddress::createNetwork(cidrNetwork); + auto addr = subnetInfo.first; + if (!addr.isV4()) { + throw IPAddressFormatException( + sformat("Address '{}' is not a V4 address", addr.toJson())); + } + return inSubnetWithMask(addr.asV4(), fetchMask(subnetInfo.second)); +} + +// public +bool IPAddressV4::inSubnetWithMask( + const IPAddressV4& subnet, + const ByteArray4 cidrMask) const { + const auto mask = detail::Bytes::mask(toByteArray(), cidrMask); + const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask); + return (mask == subMask); +} + +// public +bool IPAddressV4::isLoopback() const { + static IPAddressV4 loopback_addr("127.0.0.0"); + return inSubnetWithMask(loopback_addr, fetchMask(8)); +} + +// public +bool IPAddressV4::isLinkLocal() const { + static IPAddressV4 linklocal_addr("169.254.0.0"); + return inSubnetWithMask(linklocal_addr, fetchMask(16)); +} + +// public +bool IPAddressV4::isNonroutable() const { + auto ip = toLongHBO(); + return isPrivate() || + (/* align */ true && ip <= 0x00FFFFFF) || // 0.0.0.0-0.255.255.255 + (ip >= 0xC0000000 && ip <= 0xC00000FF) || // 192.0.0.0-192.0.0.255 + (ip >= 0xC0000200 && ip <= 0xC00002FF) || // 192.0.2.0-192.0.2.255 + (ip >= 0xC6120000 && ip <= 0xC613FFFF) || // 198.18.0.0-198.19.255.255 + (ip >= 0xC6336400 && ip <= 0xC63364FF) || // 198.51.100.0-198.51.100.255 + (ip >= 0xCB007100 && ip <= 0xCB0071FF) || // 203.0.113.0-203.0.113.255 + (ip >= 0xE0000000 && ip <= 0xFFFFFFFF) || // 224.0.0.0-255.255.255.255 + false; +} + +// public +bool IPAddressV4::isPrivate() const { + auto ip = toLongHBO(); + return // some ranges below + (ip >= 0x0A000000 && ip <= 0x0AFFFFFF) || // 10.0.0.0-10.255.255.255 + (ip >= 0x7F000000 && ip <= 0x7FFFFFFF) || // 127.0.0.0-127.255.255.255 + (ip >= 0xA9FE0000 && ip <= 0xA9FEFFFF) || // 169.254.0.0-169.254.255.255 + (ip >= 0xAC100000 && ip <= 0xAC1FFFFF) || // 172.16.0.0-172.31.255.255 + (ip >= 0xC0A80000 && ip <= 0xC0A8FFFF) || // 192.168.0.0-192.168.255.255 + false; +} + +// public +bool IPAddressV4::isMulticast() const { + return (toLongHBO() & 0xf0000000) == 0xe0000000; +} + +// public +IPAddressV4 IPAddressV4::mask(size_t numBits) const { + static const auto bits = bitCount(); + if (numBits > bits) { + throw IPAddressFormatException( + sformat("numBits({}) > bitsCount({})", numBits, bits)); + } + + ByteArray4 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_); + return IPAddressV4(ba); +} + +// public +string IPAddressV4::str() const { + return detail::fastIpv4ToString(addr_.inAddr_); +} + +// public +void IPAddressV4::toFullyQualifiedAppend(std::string& out) const { + detail::fastIpv4AppendToString(addr_.inAddr_, out); +} + +// public +string IPAddressV4::toInverseArpaName() const { + return sformat( + "{}.{}.{}.{}.in-addr.arpa", + addr_.bytes_[3], + addr_.bytes_[2], + addr_.bytes_[1], + addr_.bytes_[0]); +} + +// public +uint8_t IPAddressV4::getNthMSByte(size_t byteIndex) const { + const auto highestIndex = byteCount() - 1; + if (byteIndex > highestIndex) { + throw std::invalid_argument(sformat( + "Byte index must be <= {} for addresses of type: {}", + highestIndex, + detail::familyNameStr(AF_INET))); + } + return bytes()[byteIndex]; +} +// protected +ByteArray4 IPAddressV4::fetchMask(size_t numBits) { + static const size_t bits = bitCount(); + if (numBits > bits) { + throw IPAddressFormatException("IPv4 addresses are 32 bits"); + } + auto const val = Endian::big(uint32_t(~uint64_t(0) << (32 - numBits))); + ByteArray4 arr; + std::memcpy(arr.data(), &val, sizeof(val)); + return arr; +} +// public static +CIDRNetworkV4 IPAddressV4::longestCommonPrefix( + const CIDRNetworkV4& one, + const CIDRNetworkV4& two) { + auto prefix = detail::Bytes::longestCommonPrefix( + one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second); + return {IPAddressV4(prefix.first), prefix.second}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/IPAddressV4.h b/ios/Pods/Flipper-Folly/folly/IPAddressV4.h new file mode 100644 index 000000000..35dc61f59 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddressV4.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstring> + +#include <array> +#include <functional> +#include <iosfwd> + +#include <folly/Expected.h> +#include <folly/FBString.h> +#include <folly/IPAddressException.h> +#include <folly/Range.h> +#include <folly/detail/IPAddress.h> +#include <folly/hash/Hash.h> + +namespace folly { + +class IPAddress; +class IPAddressV4; +class IPAddressV6; + +/** + * Pair of IPAddressV4, netmask + */ +typedef std::pair<IPAddressV4, uint8_t> CIDRNetworkV4; + +/** + * Specialization for IPv4 addresses + */ +typedef std::array<uint8_t, 4> ByteArray4; + +/** + * IPv4 variation of IPAddress. + * + * Added methods: toLong, toLongHBO and createIPv6 + * + * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO + * if working in host byte order. + * + * @see IPAddress + */ +class IPAddressV4 { + public: + // Max size of std::string returned by toFullyQualified. + static constexpr size_t kMaxToFullyQualifiedSize = + 4 /*words*/ * 3 /*max chars per word*/ + 3 /*separators*/; + + // returns true iff the input string can be parsed as an ipv4-address + static bool validate(StringPiece ip) noexcept; + + // create an IPAddressV4 instance from a uint32_t (network byte order) + static IPAddressV4 fromLong(uint32_t src); + // same as above but host byte order + static IPAddressV4 fromLongHBO(uint32_t src); + + /** + * Create a new IPAddress instance from the provided binary data. + * @throws IPAddressFormatException if the input length is not 4 bytes. + */ + static IPAddressV4 fromBinary(ByteRange bytes); + + /** + * Non-throwing version of fromBinary(). + * On failure returns IPAddressFormatError. + */ + static Expected<IPAddressV4, IPAddressFormatError> tryFromBinary( + ByteRange bytes) noexcept; + + /** + * Tries to create a new IPAddressV4 instance from provided string and + * returns it on success. Returns IPAddressFormatError on failure. + */ + static Expected<IPAddressV4, IPAddressFormatError> tryFromString( + StringPiece str) noexcept; + + /** + * Returns the address as a Range. + */ + ByteRange toBinary() const { + return ByteRange((const unsigned char*)&addr_.inAddr_.s_addr, 4); + } + + /** + * Create a new IPAddress instance from the in-addr.arpa representation. + * @throws IPAddressFormatException if the input is not a valid in-addr.arpa + * representation + */ + static IPAddressV4 fromInverseArpaName(const std::string& arpaname); + + /** + * Convert a IPv4 address string to a long in network byte order. + * @param [in] ip the address to convert + * @return the long representation of the address + */ + static uint32_t toLong(StringPiece ip); + // Same as above, but in host byte order. + // This is slightly slower than toLong. + static uint32_t toLongHBO(StringPiece ip); + + /** + * Default constructor for IPAddressV4. + * + * The address value will be 0.0.0.0 + */ + IPAddressV4(); + + // Create an IPAddressV4 from a string + // @throws IPAddressFormatException + explicit IPAddressV4(StringPiece addr); + + // ByteArray4 constructor + explicit IPAddressV4(const ByteArray4& src) noexcept; + + // in_addr constructor + explicit IPAddressV4(const in_addr src) noexcept; + + // Return the V6 mapped representation of the address. + IPAddressV6 createIPv6() const; + + /** + * Return a V6 address in the format of an 6To4 address. + */ + IPAddressV6 getIPv6For6To4() const; + + // Return the long (network byte order) representation of the address. + uint32_t toLong() const { + return toAddr().s_addr; + } + + // Return the long (host byte order) representation of the address. + // This is slightly slower than toLong. + uint32_t toLongHBO() const { + return ntohl(toLong()); + } + + /** + * @see IPAddress#bitCount + * @returns 32 + */ + static constexpr size_t bitCount() { + return 32; + } + + /** + * @See IPAddress#toJson + */ + std::string toJson() const; + + size_t hash() const { + static const uint32_t seed = AF_INET; + uint32_t hashed = hash::fnv32_buf(&addr_, 4); + return hash::hash_combine(seed, hashed); + } + + // @see IPAddress#inSubnet + // @throws IPAddressFormatException if string doesn't contain a V4 address + bool inSubnet(StringPiece cidrNetwork) const; + + // return true if address is in subnet + bool inSubnet(const IPAddressV4& subnet, uint8_t cidr) const { + return inSubnetWithMask(subnet, fetchMask(cidr)); + } + bool inSubnetWithMask(const IPAddressV4& subnet, const ByteArray4 mask) const; + + // @see IPAddress#isLoopback + bool isLoopback() const; + + // @see IPAddress#isLinkLocal + bool isLinkLocal() const; + + // @see IPAddress#isNonroutable + bool isNonroutable() const; + + // @see IPAddress#isPrivate + bool isPrivate() const; + + // @see IPAddress#isMulticast + bool isMulticast() const; + + // @see IPAddress#isZero + bool isZero() const { + constexpr auto zero = ByteArray4{{}}; + return 0 == std::memcmp(bytes(), zero.data(), zero.size()); + } + + bool isLinkLocalBroadcast() const { + return (INADDR_BROADCAST == toLongHBO()); + } + + // @see IPAddress#mask + IPAddressV4 mask(size_t numBits) const; + + // @see IPAddress#str + std::string str() const; + + std::string toInverseArpaName() const; + + // return underlying in_addr structure + in_addr toAddr() const { + return addr_.inAddr_; + } + + sockaddr_in toSockAddr() const { + sockaddr_in addr; + memset(&addr, 0, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + memcpy(&addr.sin_addr, &addr_.inAddr_, sizeof(in_addr)); + return addr; + } + + ByteArray4 toByteArray() const { + ByteArray4 ba{{0}}; + std::memcpy(ba.data(), bytes(), 4); + return ba; + } + + // @see IPAddress#toFullyQualified + std::string toFullyQualified() const { + return str(); + } + + // @see IPAddress#toFullyQualifiedAppend + void toFullyQualifiedAppend(std::string& out) const; + + // @see IPAddress#version + uint8_t version() const { + return 4; + } + + /** + * Return the mask associated with the given number of bits. + * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should + * be {0xff, 0xff, 0xff, 0x00}. + * @param [in] numBits bitmask to retrieve + * @throws abort if numBits == 0 or numBits > bitCount() + * @return mask associated with numBits + */ + static ByteArray4 fetchMask(size_t numBits); + + // Given 2 IPAddressV4, mask pairs extract the longest common IPAddress, + // mask pair + static CIDRNetworkV4 longestCommonPrefix( + const CIDRNetworkV4& one, + const CIDRNetworkV4& two); + // Number of bytes in the address representation. + static size_t byteCount() { + return 4; + } + // get nth most significant bit - 0 indexed + bool getNthMSBit(size_t bitIndex) const { + return detail::getNthMSBitImpl(*this, bitIndex, AF_INET); + } + // get nth most significant byte - 0 indexed + uint8_t getNthMSByte(size_t byteIndex) const; + // get nth bit - 0 indexed + bool getNthLSBit(size_t bitIndex) const { + return getNthMSBit(bitCount() - bitIndex - 1); + } + // get nth byte - 0 indexed + uint8_t getNthLSByte(size_t byteIndex) const { + return getNthMSByte(byteCount() - byteIndex - 1); + } + + const unsigned char* bytes() const { + return addr_.bytes_.data(); + } + + private: + union AddressStorage { + static_assert( + sizeof(in_addr) == sizeof(ByteArray4), + "size of in_addr and ByteArray4 are different"); + in_addr inAddr_; + ByteArray4 bytes_; + AddressStorage() { + std::memset(this, 0, sizeof(AddressStorage)); + } + explicit AddressStorage(const ByteArray4 bytes) : bytes_(bytes) {} + explicit AddressStorage(const in_addr addr) : inAddr_(addr) {} + } addr_; + + /** + * Set the current IPAddressV4 object to have the address specified by bytes. + * Returns IPAddressFormatError if bytes.size() is not 4. + */ + Expected<Unit, IPAddressFormatError> trySetFromBinary( + ByteRange bytes) noexcept; +}; + +// boost::hash uses hash_value() so this allows boost::hash to work +// automatically for IPAddressV4 +size_t hash_value(const IPAddressV4& addr); +std::ostream& operator<<(std::ostream& os, const IPAddressV4& addr); +// Define toAppend() to allow IPAddressV4 to be used with to<string> +void toAppend(IPAddressV4 addr, std::string* result); +void toAppend(IPAddressV4 addr, fbstring* result); + +/** + * Return true if two addresses are equal. + */ +inline bool operator==(const IPAddressV4& addr1, const IPAddressV4& addr2) { + return (addr1.toLong() == addr2.toLong()); +} +// Return true if addr1 < addr2 +inline bool operator<(const IPAddressV4& addr1, const IPAddressV4& addr2) { + return (addr1.toLongHBO() < addr2.toLongHBO()); +} +// Derived operators +inline bool operator!=(const IPAddressV4& a, const IPAddressV4& b) { + return !(a == b); +} +inline bool operator>(const IPAddressV4& a, const IPAddressV4& b) { + return b < a; +} +inline bool operator<=(const IPAddressV4& a, const IPAddressV4& b) { + return !(a > b); +} +inline bool operator>=(const IPAddressV4& a, const IPAddressV4& b) { + return !(a < b); +} + +} // namespace folly + +namespace std { +template <> +struct hash<folly::IPAddressV4> { + size_t operator()(const folly::IPAddressV4 addr) const { + return addr.hash(); + } +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/IPAddressV6.cpp b/ios/Pods/Flipper-Folly/folly/IPAddressV6.cpp new file mode 100644 index 000000000..5bcdc7bb8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddressV6.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/IPAddressV6.h> + +#include <ostream> +#include <string> + +#include <folly/Format.h> +#include <folly/IPAddress.h> +#include <folly/IPAddressV4.h> +#include <folly/MacAddress.h> +#include <folly/detail/IPAddressSource.h> + +#ifndef _WIN32 +#include <net/if.h> +#else +// Because of the massive pain that is libnl, this can't go into the socket +// portability header as you can't include <linux/if.h> and <net/if.h> in +// the same translation unit without getting errors -_-... +#include <iphlpapi.h> // @manual +#include <ntddndis.h> // @manual + +// Alias the max size of an interface name to what posix expects. +#define IFNAMSIZ IF_NAMESIZE +#endif + +using std::ostream; +using std::string; + +namespace folly { + +// public static const +const uint32_t IPAddressV6::PREFIX_TEREDO = 0x20010000; +const uint32_t IPAddressV6::PREFIX_6TO4 = 0x2002; + +// free functions +size_t hash_value(const IPAddressV6& addr) { + return addr.hash(); +} +ostream& operator<<(ostream& os, const IPAddressV6& addr) { + os << addr.str(); + return os; +} +void toAppend(IPAddressV6 addr, string* result) { + result->append(addr.str()); +} +void toAppend(IPAddressV6 addr, fbstring* result) { + result->append(addr.str()); +} + +bool IPAddressV6::validate(StringPiece ip) noexcept { + return tryFromString(ip).hasValue(); +} + +// public default constructor +IPAddressV6::IPAddressV6() = default; + +// public string constructor +IPAddressV6::IPAddressV6(StringPiece addr) { + auto maybeIp = tryFromString(addr); + if (maybeIp.hasError()) { + throw IPAddressFormatException( + to<std::string>("Invalid IPv6 address '", addr, "'")); + } + *this = maybeIp.value(); +} + +Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromString( + StringPiece str) noexcept { + auto ip = str.str(); + + // Allow addresses surrounded in brackets + if (ip.size() < 2) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } + if (ip.front() == '[' && ip.back() == ']') { + ip = ip.substr(1, ip.size() - 2); + } + + struct addrinfo* result; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (::getaddrinfo(ip.c_str(), nullptr, &hints, &result) == 0) { + SCOPE_EXIT { + ::freeaddrinfo(result); + }; + const struct sockaddr_in6* sa = + reinterpret_cast<struct sockaddr_in6*>(result->ai_addr); + return IPAddressV6(*sa); + } + return makeUnexpected(IPAddressFormatError::INVALID_IP); +} + +// in6_addr constructor +IPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {} + +// sockaddr_in6 constructor +IPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept + : addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {} + +// ByteArray16 constructor +IPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {} + +// link-local constructor +IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {} + +IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { + // The link-local address uses modified EUI-64 format, + // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A + const auto* macBytes = mac.bytes(); + memcpy(&bytes_.front(), "\xfe\x80\x00\x00\x00\x00\x00\x00", 8); + bytes_[8] = uint8_t(macBytes[0] ^ 0x02); + bytes_[9] = macBytes[1]; + bytes_[10] = macBytes[2]; + bytes_[11] = 0xff; + bytes_[12] = 0xfe; + bytes_[13] = macBytes[3]; + bytes_[14] = macBytes[4]; + bytes_[15] = macBytes[5]; +} + +Optional<MacAddress> IPAddressV6::getMacAddressFromLinkLocal() const { + // Returned MacAddress must be constructed from a link-local IPv6 address. + if (!isLinkLocal()) { + return folly::none; + } + return getMacAddressFromEUI64(); +} + +Optional<MacAddress> IPAddressV6::getMacAddressFromEUI64() const { + if (!(addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + return folly::none; + } + // The auto configured address uses modified EUI-64 format, + // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A + std::array<uint8_t, MacAddress::SIZE> bytes; + // Step 1: first 8 bytes are network prefix, and can be stripped + // Step 2: invert the universal/local (U/L) flag (bit 7) + bytes[0] = addr_.bytes_[8] ^ 0x02; + // Step 3: copy these bytes as they are + bytes[1] = addr_.bytes_[9]; + bytes[2] = addr_.bytes_[10]; + // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12] + // Step 5: copy the rest. + bytes[3] = addr_.bytes_[13]; + bytes[4] = addr_.bytes_[14]; + bytes[5] = addr_.bytes_[15]; + return Optional<MacAddress>(MacAddress::fromBinary(range(bytes))); +} + +IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) { + auto maybeIp = tryFromBinary(bytes); + if (maybeIp.hasError()) { + throw IPAddressFormatException(to<std::string>( + "Invalid IPv6 binary data: length must be 16 bytes, got ", + bytes.size())); + } + return maybeIp.value(); +} + +Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromBinary( + ByteRange bytes) noexcept { + IPAddressV6 addr; + auto setResult = addr.trySetFromBinary(bytes); + if (setResult.hasError()) { + return makeUnexpected(setResult.error()); + } + return addr; +} + +Expected<Unit, IPAddressFormatError> IPAddressV6::trySetFromBinary( + ByteRange bytes) noexcept { + if (bytes.size() != 16) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } + memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr)); + scope_ = 0; + return unit; +} + +// static +IPAddressV6 IPAddressV6::fromInverseArpaName(const std::string& arpaname) { + auto piece = StringPiece(arpaname); + if (!piece.removeSuffix(".ip6.arpa")) { + throw IPAddressFormatException(sformat( + "Invalid input. Should end with 'ip6.arpa'. Got '{}'", arpaname)); + } + std::vector<StringPiece> pieces; + split(".", piece, pieces); + if (pieces.size() != 32) { + throw IPAddressFormatException(sformat("Invalid input. Got '{}'", piece)); + } + std::array<char, IPAddressV6::kToFullyQualifiedSize> ip; + size_t pos = 0; + int count = 0; + for (size_t i = 1; i <= pieces.size(); i++) { + ip[pos] = pieces[pieces.size() - i][0]; + pos++; + count++; + // add ':' every 4 chars + if (count == 4 && pos < ip.size()) { + ip[pos++] = ':'; + count = 0; + } + } + return IPAddressV6(folly::range(ip)); +} + +// public +IPAddressV4 IPAddressV6::createIPv4() const { + if (!isIPv4Mapped()) { + throw IPAddressFormatException("addr is not v4-to-v6-mapped"); + } + const unsigned char* by = bytes(); + return IPAddressV4(detail::Bytes::mkAddress4(&by[12])); +} + +// convert two uint8_t bytes into a uint16_t as hibyte.lobyte +static inline uint16_t unpack(uint8_t lobyte, uint8_t hibyte) { + return uint16_t((uint16_t(hibyte) << 8) | lobyte); +} + +// given a src string, unpack count*2 bytes into dest +// dest must have as much storage as count +static inline void +unpackInto(const unsigned char* src, uint16_t* dest, size_t count) { + for (size_t i = 0, hi = 1, lo = 0; i < count; i++) { + dest[i] = unpack(src[hi], src[lo]); + hi += 2; + lo += 2; + } +} + +// public +IPAddressV4 IPAddressV6::getIPv4For6To4() const { + if (!is6To4()) { + throw IPAddressV6::TypeError( + sformat("Invalid IP '{}': not a 6to4 address", str())); + } + // convert 16x8 bytes into first 4x16 bytes + uint16_t ints[4] = {0, 0, 0, 0}; + unpackInto(bytes(), ints, 4); + // repack into 4x8 + union { + unsigned char bytes[4]; + in_addr addr; + } ipv4; + ipv4.bytes[0] = (uint8_t)((ints[1] & 0xFF00) >> 8); + ipv4.bytes[1] = (uint8_t)(ints[1] & 0x00FF); + ipv4.bytes[2] = (uint8_t)((ints[2] & 0xFF00) >> 8); + ipv4.bytes[3] = (uint8_t)(ints[2] & 0x00FF); + return IPAddressV4(ipv4.addr); +} + +// public +bool IPAddressV6::isIPv4Mapped() const { + // v4 mapped addresses have their first 10 bytes set to 0, the next 2 bytes + // set to 255 (0xff); + const unsigned char* by = bytes(); + + // check if first 10 bytes are 0 + for (int i = 0; i < 10; i++) { + if (by[i] != 0x00) { + return false; + } + } + // check if bytes 11 and 12 are 255 + return by[10] == 0xff && by[11] == 0xff; +} + +// public +IPAddressV6::Type IPAddressV6::type() const { + // convert 16x8 bytes into first 2x16 bytes + uint16_t ints[2] = {0, 0}; + unpackInto(bytes(), ints, 2); + + if ((((uint32_t)ints[0] << 16) | ints[1]) == IPAddressV6::PREFIX_TEREDO) { + return Type::TEREDO; + } + + if ((uint32_t)ints[0] == IPAddressV6::PREFIX_6TO4) { + return Type::T6TO4; + } + + return Type::NORMAL; +} + +// public +string IPAddressV6::toJson() const { + return sformat("{{family:'AF_INET6', addr:'{}', hash:{}}}", str(), hash()); +} + +// public +size_t IPAddressV6::hash() const { + if (isIPv4Mapped()) { + /* An IPAddress containing this object would be equal (i.e. operator==) + to an IPAddress containing the corresponding IPv4. + So we must make sure that the hash values are the same as well */ + return IPAddress::createIPv4(*this).hash(); + } + + static const uint64_t seed = AF_INET6; + uint64_t hash1 = 0, hash2 = 0; + hash::SpookyHashV2::Hash128(&addr_, 16, &hash1, &hash2); + return hash::hash_combine(seed, hash1, hash2); +} + +// public +bool IPAddressV6::inSubnet(StringPiece cidrNetwork) const { + auto subnetInfo = IPAddress::createNetwork(cidrNetwork); + auto addr = subnetInfo.first; + if (!addr.isV6()) { + throw IPAddressFormatException( + sformat("Address '{}' is not a V6 address", addr.toJson())); + } + return inSubnetWithMask(addr.asV6(), fetchMask(subnetInfo.second)); +} + +// public +bool IPAddressV6::inSubnetWithMask( + const IPAddressV6& subnet, + const ByteArray16& cidrMask) const { + const auto mask = detail::Bytes::mask(toByteArray(), cidrMask); + const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask); + return (mask == subMask); +} + +// public +bool IPAddressV6::isLoopback() const { + // Check if v4 mapped is loopback + if (isIPv4Mapped() && createIPv4().isLoopback()) { + return true; + } + auto socka = toSockAddr(); + return IN6_IS_ADDR_LOOPBACK(&socka.sin6_addr); +} + +bool IPAddressV6::isRoutable() const { + return + // 2000::/3 is the only assigned global unicast block + inBinarySubnet({{0x20, 0x00}}, 3) || + // ffxe::/16 are global scope multicast addresses, + // which are eligible to be routed over the internet + (isMulticast() && getMulticastScope() == 0xe); +} + +bool IPAddressV6::isLinkLocalBroadcast() const { + static const IPAddressV6 kLinkLocalBroadcast("ff02::1"); + return *this == kLinkLocalBroadcast; +} + +// public +bool IPAddressV6::isPrivate() const { + // Check if mapped is private + if (isIPv4Mapped() && createIPv4().isPrivate()) { + return true; + } + return isLoopback() || inBinarySubnet({{0xfc, 0x00}}, 7); +} + +// public +bool IPAddressV6::isLinkLocal() const { + return inBinarySubnet({{0xfe, 0x80}}, 10); +} + +bool IPAddressV6::isMulticast() const { + return addr_.bytes_[0] == 0xff; +} + +uint8_t IPAddressV6::getMulticastFlags() const { + DCHECK(isMulticast()); + return uint8_t((addr_.bytes_[1] >> 4) & 0xf); +} + +uint8_t IPAddressV6::getMulticastScope() const { + DCHECK(isMulticast()); + return uint8_t(addr_.bytes_[1] & 0xf); +} + +IPAddressV6 IPAddressV6::getSolicitedNodeAddress() const { + // Solicted node addresses must be constructed from unicast (or anycast) + // addresses + DCHECK(!isMulticast()); + + uint8_t bytes[16] = { + 0xff, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xff, + addr_.bytes_[13], + addr_.bytes_[14], + addr_.bytes_[15], + }; + return IPAddressV6::fromBinary(ByteRange(bytes, 16)); +} + +// public +IPAddressV6 IPAddressV6::mask(size_t numBits) const { + static const auto bits = bitCount(); + if (numBits > bits) { + throw IPAddressFormatException( + sformat("numBits({}) > bitCount({})", numBits, bits)); + } + ByteArray16 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_); + return IPAddressV6(ba); +} + +// public +string IPAddressV6::str() const { + char buffer[INET6_ADDRSTRLEN + IFNAMSIZ + 1]; + + if (!inet_ntop(AF_INET6, toAddr().s6_addr, buffer, INET6_ADDRSTRLEN)) { + throw IPAddressFormatException(sformat( + "Invalid address with hex '{}' with error {}", + detail::Bytes::toHex(bytes(), 16), + errnoStr(errno))); + } + + auto scopeId = getScopeId(); + if (scopeId != 0) { + auto len = strlen(buffer); + buffer[len] = '%'; + + auto errsv = errno; + if (!if_indextoname(scopeId, buffer + len + 1)) { + // if we can't map the if because eg. it no longer exists, + // append the if index instead + snprintf(buffer + len + 1, IFNAMSIZ, "%u", scopeId); + } + errno = errsv; + } + + return string(buffer); +} + +// public +string IPAddressV6::toFullyQualified() const { + return detail::fastIpv6ToString(addr_.in6Addr_); +} + +// public +void IPAddressV6::toFullyQualifiedAppend(std::string& out) const { + detail::fastIpv6AppendToString(addr_.in6Addr_, out); +} + +// public +string IPAddressV6::toInverseArpaName() const { + constexpr folly::StringPiece lut = "0123456789abcdef"; + std::array<char, 32> a; + int j = 0; + for (int i = 15; i >= 0; i--) { + a[j] = (lut[bytes()[i] & 0xf]); + a[j + 1] = (lut[bytes()[i] >> 4]); + j += 2; + } + return sformat("{}.ip6.arpa", join(".", a)); +} + +// public +uint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const { + const auto highestIndex = byteCount() - 1; + if (byteIndex > highestIndex) { + throw std::invalid_argument(sformat( + "Byte index must be <= {} for addresses of type: {}", + highestIndex, + detail::familyNameStr(AF_INET6))); + } + return bytes()[byteIndex]; +} + +// protected +ByteArray16 IPAddressV6::fetchMask(size_t numBits) { + static const size_t bits = bitCount(); + if (numBits > bits) { + throw IPAddressFormatException("IPv6 addresses are 128 bits."); + } + if (numBits == 0) { + return {{0}}; + } + constexpr auto _0s = uint64_t(0); + constexpr auto _1s = ~_0s; + auto const fragment = Endian::big(_1s << ((128 - numBits) % 64)); + auto const hi = numBits <= 64 ? fragment : _1s; + auto const lo = numBits <= 64 ? _0s : fragment; + uint64_t const parts[] = {hi, lo}; + ByteArray16 arr; + std::memcpy(arr.data(), parts, sizeof(parts)); + return arr; +} + +// public static +CIDRNetworkV6 IPAddressV6::longestCommonPrefix( + const CIDRNetworkV6& one, + const CIDRNetworkV6& two) { + auto prefix = detail::Bytes::longestCommonPrefix( + one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second); + return {IPAddressV6(prefix.first), prefix.second}; +} + +// protected +bool IPAddressV6::inBinarySubnet( + const std::array<uint8_t, 2> addr, + size_t numBits) const { + auto masked = mask(numBits); + return (std::memcmp(addr.data(), masked.bytes(), 2) == 0); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/IPAddressV6.h b/ios/Pods/Flipper-Folly/folly/IPAddressV6.h new file mode 100644 index 000000000..31d17d13c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IPAddressV6.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstring> + +#include <array> +#include <functional> +#include <iosfwd> +#include <map> +#include <stdexcept> + +#include <folly/Expected.h> +#include <folly/FBString.h> +#include <folly/IPAddressException.h> +#include <folly/Optional.h> +#include <folly/Range.h> +#include <folly/detail/IPAddress.h> +#include <folly/hash/Hash.h> + +namespace folly { + +class IPAddress; +class IPAddressV4; +class IPAddressV6; +class MacAddress; + +/** + * Pair of IPAddressV6, netmask + */ +typedef std::pair<IPAddressV6, uint8_t> CIDRNetworkV6; + +/** + * Specialization for IPv6 addresses + */ +typedef std::array<uint8_t, 16> ByteArray16; + +/** + * IPv6 variation of IPAddress. + * + * Added methods: createIPv4, getIPv4For6To4, is6To4, + * isTeredo, isIPv4Mapped, tryCreateIPv4, type + * + * @see IPAddress + * + * Notes on scope ID parsing: + * + * getaddrinfo() uses if_nametoindex() to convert interface names + * into a numerical index. For instance, + * "fe80::202:c9ff:fec1:ee08%eth0" may return scope ID 2 on some + * hosts, but other numbers on other hosts. It will fail entirely on + * hosts without an eth0 interface. + * + * Serializing / Deserializing IPAddressB6's on different hosts + * that use link-local scoping probably won't work. + */ +class IPAddressV6 { + public: + // V6 Address Type + enum Type { + TEREDO, + T6TO4, + NORMAL, + }; + // A constructor parameter to indicate that we should create a link-local + // IPAddressV6. + enum LinkLocalTag { + LINK_LOCAL, + }; + // Thrown when a type assertion fails + typedef std::runtime_error TypeError; + + // Binary prefix for teredo networks + static const uint32_t PREFIX_TEREDO; + // Binary prefix for 6to4 networks + static const uint32_t PREFIX_6TO4; + + // Size of std::string returned by toFullyQualified. + static constexpr size_t kToFullyQualifiedSize = + 8 /*words*/ * 4 /*hex chars per word*/ + 7 /*separators*/; + + // returns true iff the input string can be parsed as an ipv6-address + static bool validate(StringPiece ip) noexcept; + + /** + * Create a new IPAddress instance from the provided binary data. + * @throws IPAddressFormatException if the input length is not 16 bytes. + */ + static IPAddressV6 fromBinary(ByteRange bytes); + + /** + * Non-throwing version of fromBinary(). + * On failure returns IPAddressFormatError. + */ + static Expected<IPAddressV6, IPAddressFormatError> tryFromBinary( + ByteRange bytes) noexcept; + + /** + * Tries to create a new IPAddressV6 instance from provided string and + * returns it on success. Returns IPAddressFormatError on failure. + */ + static Expected<IPAddressV6, IPAddressFormatError> tryFromString( + StringPiece str) noexcept; + + /** + * Create a new IPAddress instance from the ip6.arpa representation. + * @throws IPAddressFormatException if the input is not a valid ip6.arpa + * representation + */ + static IPAddressV6 fromInverseArpaName(const std::string& arpaname); + + /** + * Returns the address as a Range. + */ + ByteRange toBinary() const { + return ByteRange((const unsigned char*)&addr_.in6Addr_.s6_addr, 16); + } + + /** + * Default constructor for IPAddressV6. + * + * The address value will be ::0 + */ + IPAddressV6(); + + // Create an IPAddressV6 from a string + // @throws IPAddressFormatException + // + explicit IPAddressV6(StringPiece addr); + + // ByteArray16 constructor + explicit IPAddressV6(const ByteArray16& src) noexcept; + + // in6_addr constructor + explicit IPAddressV6(const in6_addr& src) noexcept; + + // sockaddr_in6 constructor + explicit IPAddressV6(const sockaddr_in6& src) noexcept; + + /** + * Create a link-local IPAddressV6 from the specified ethernet MAC address. + */ + IPAddressV6(LinkLocalTag tag, MacAddress mac); + + // return the mapped V4 address + // @throws IPAddressFormatException if !isIPv4Mapped + IPAddressV4 createIPv4() const; + + /** + * Return a V4 address if this is a 6To4 address. + * @throws TypeError if not a 6To4 address + */ + IPAddressV4 getIPv4For6To4() const; + + // Return true if a 6TO4 address + bool is6To4() const { + return type() == IPAddressV6::Type::T6TO4; + } + + // Return true if a TEREDO address + bool isTeredo() const { + return type() == IPAddressV6::Type::TEREDO; + } + + // return true if this is v4-to-v6-mapped + bool isIPv4Mapped() const; + + // Return the V6 address type + Type type() const; + + /** + * @see IPAddress#bitCount + * @returns 128 + */ + static constexpr size_t bitCount() { + return 128; + } + + /** + * @see IPAddress#toJson + */ + std::string toJson() const; + + size_t hash() const; + + // @see IPAddress#inSubnet + // @throws IPAddressFormatException if string doesn't contain a V6 address + bool inSubnet(StringPiece cidrNetwork) const; + + // return true if address is in subnet + bool inSubnet(const IPAddressV6& subnet, uint8_t cidr) const { + return inSubnetWithMask(subnet, fetchMask(cidr)); + } + bool inSubnetWithMask(const IPAddressV6& subnet, const ByteArray16& mask) + const; + + // @see IPAddress#isLoopback + bool isLoopback() const; + + // @see IPAddress#isNonroutable + bool isNonroutable() const { + return !isRoutable(); + } + + /** + * Return true if this address is routable. + */ + bool isRoutable() const; + + // @see IPAddress#isPrivate + bool isPrivate() const; + + /** + * Return true if this is a link-local IPv6 address. + * + * Note that this only returns true for addresses in the fe80::/10 range. + * It returns false for the loopback address (::1), even though this address + * is also effectively has link-local scope. It also returns false for + * link-scope and interface-scope multicast addresses. + */ + bool isLinkLocal() const; + + /** + * Return the mac address if this is a link-local IPv6 address. + * + * @return an Optional<MacAddress> union representing the mac address. + * + * If the address is not a link-local one it will return an empty Optional. + * You can use Optional::value() to check whether the mac address is not null. + */ + Optional<MacAddress> getMacAddressFromLinkLocal() const; + + /** + * Return the mac address if this is an auto-configured IPv6 address based on + * EUI-64 + * + * @return an Optional<MacAddress> union representing the mac address. + * If the address is not based on EUI-64 it will return an empty Optional. + * You can use Optional::value() to check whether the mac address is not null. + */ + Optional<MacAddress> getMacAddressFromEUI64() const; + + /** + * Return true if this is a multicast address. + */ + bool isMulticast() const; + + /** + * Return the flags for a multicast address. + * This method may only be called on multicast addresses. + */ + uint8_t getMulticastFlags() const; + + /** + * Return the scope for a multicast address. + * This method may only be called on multicast addresses. + */ + uint8_t getMulticastScope() const; + + // @see IPAddress#isZero + bool isZero() const { + constexpr auto zero = ByteArray16{{}}; + return 0 == std::memcmp(bytes(), zero.data(), zero.size()); + } + + bool isLinkLocalBroadcast() const; + + // @see IPAddress#mask + IPAddressV6 mask(size_t numBits) const; + + // return underlying in6_addr structure + in6_addr toAddr() const { + return addr_.in6Addr_; + } + + uint16_t getScopeId() const { + return scope_; + } + void setScopeId(uint16_t scope) { + scope_ = scope; + } + + sockaddr_in6 toSockAddr() const { + sockaddr_in6 addr; + memset(&addr, 0, sizeof(sockaddr_in6)); + addr.sin6_family = AF_INET6; + addr.sin6_scope_id = scope_; + memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr)); + return addr; + } + + ByteArray16 toByteArray() const { + ByteArray16 ba{{0}}; + std::memcpy(ba.data(), bytes(), 16); + return ba; + } + + // @see IPAddress#toFullyQualified + std::string toFullyQualified() const; + + // @see IPAddress#toFullyQualifiedAppend + void toFullyQualifiedAppend(std::string& out) const; + + std::string toInverseArpaName() const; + + // @see IPAddress#str + std::string str() const; + + // @see IPAddress#version + uint8_t version() const { + return 6; + } + + /** + * Return the solicited-node multicast address for this address. + */ + IPAddressV6 getSolicitedNodeAddress() const; + + /** + * Return the mask associated with the given number of bits. + * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should + * be {0xff, 0xff, 0xff, 0x00}. + * @param [in] numBits bitmask to retrieve + * @throws abort if numBits == 0 or numBits > bitCount() + * @return mask associated with numBits + */ + static ByteArray16 fetchMask(size_t numBits); + // Given 2 IPAddressV6,mask pairs extract the longest common IPAddress, + // mask pair + static CIDRNetworkV6 longestCommonPrefix( + const CIDRNetworkV6& one, + const CIDRNetworkV6& two); + // Number of bytes in the address representation. + static constexpr size_t byteCount() { + return 16; + } + + // get nth most significant bit - 0 indexed + bool getNthMSBit(size_t bitIndex) const { + return detail::getNthMSBitImpl(*this, bitIndex, AF_INET6); + } + // get nth most significant byte - 0 indexed + uint8_t getNthMSByte(size_t byteIndex) const; + // get nth bit - 0 indexed + bool getNthLSBit(size_t bitIndex) const { + return getNthMSBit(bitCount() - bitIndex - 1); + } + // get nth byte - 0 indexed + uint8_t getNthLSByte(size_t byteIndex) const { + return getNthMSByte(byteCount() - byteIndex - 1); + } + + const unsigned char* bytes() const { + return addr_.in6Addr_.s6_addr; + } + + protected: + /** + * Helper that returns true if the address is in the binary subnet specified + * by addr. + */ + bool inBinarySubnet(const std::array<uint8_t, 2> addr, size_t numBits) const; + + private: + auto tie() const { + return std::tie(addr_.bytes_, scope_); + } + + public: + friend inline bool operator==(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() == b.tie(); + } + friend inline bool operator!=(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() != b.tie(); + } + friend inline bool operator<(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() < b.tie(); + } + friend inline bool operator>(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() > b.tie(); + } + friend inline bool operator<=(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() <= b.tie(); + } + friend inline bool operator>=(const IPAddressV6& a, const IPAddressV6& b) { + return a.tie() >= b.tie(); + } + + private: + union AddressStorage { + in6_addr in6Addr_; + ByteArray16 bytes_; + AddressStorage() { + std::memset(this, 0, sizeof(AddressStorage)); + } + explicit AddressStorage(const ByteArray16& bytes) : bytes_(bytes) {} + explicit AddressStorage(const in6_addr& addr) : in6Addr_(addr) {} + explicit AddressStorage(MacAddress mac); + } addr_; + + // Link-local scope id. This should always be 0 for IPAddresses that + // are *not* link-local. + uint16_t scope_{0}; + + /** + * Set the current IPAddressV6 object to have the address specified by bytes. + * Returns IPAddressFormatError if bytes.size() is not 16. + */ + Expected<Unit, IPAddressFormatError> trySetFromBinary( + ByteRange bytes) noexcept; +}; + +// boost::hash uses hash_value() so this allows boost::hash to work +// automatically for IPAddressV6 +std::size_t hash_value(const IPAddressV6& addr); +std::ostream& operator<<(std::ostream& os, const IPAddressV6& addr); +// Define toAppend() to allow IPAddressV6 to be used with to<string> +void toAppend(IPAddressV6 addr, std::string* result); +void toAppend(IPAddressV6 addr, fbstring* result); + +} // namespace folly + +namespace std { +template <> +struct hash<folly::IPAddressV6> { + size_t operator()(const folly::IPAddressV6& addr) const { + return addr.hash(); + } +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/Indestructible.h b/ios/Pods/Flipper-Folly/folly/Indestructible.h new file mode 100644 index 000000000..6e6c06478 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Indestructible.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <type_traits> +#include <utility> + +#include <folly/Traits.h> + +namespace folly { + +/*** + * Indestructible + * + * When you need a Meyers singleton that will not get destructed, even at + * shutdown, and you also want the object stored inline. + * + * Use like: + * + * void doSomethingWithExpensiveData(); + * + * void doSomethingWithExpensiveData() { + * static const Indestructible<map<string, int>> data{ + * map<string, int>{{"key1", 17}, {"key2", 19}, {"key3", 23}}, + * }; + * callSomethingTakingAMapByRef(*data); + * } + * + * This should be used only for Meyers singletons, and, even then, only when + * the instance does not need to be destructed ever. + * + * This should not be used more generally, e.g., as member fields, etc. + * + * This is designed as an alternative, but with one fewer allocation at + * construction time and one fewer pointer dereference at access time, to the + * Meyers singleton pattern of: + * + * void doSomethingWithExpensiveData() { + * static const auto data = // never `delete`d + * new map<string, int>{{"key1", 17}, {"key2", 19}, {"key3", 23}}; + * callSomethingTakingAMapByRef(*data); + * } + */ + +template <typename T> +class Indestructible final { + public: + template <typename S = T, typename = decltype(S())> + constexpr Indestructible() noexcept(noexcept(T())) {} + + /** + * Constructor accepting a single argument by forwarding reference, this + * allows using list initialzation without the overhead of things like + * in_place, etc and also works with std::initializer_list constructors + * which can't be deduced, the default parameter helps there. + * + * auto i = folly::Indestructible<std::map<int, int>>{{{1, 2}}}; + * + * This provides convenience + * + * There are two versions of this constructor - one for when the element is + * implicitly constructible from the given argument and one for when the + * type is explicitly but not implicitly constructible from the given + * argument. + */ + template < + typename U = T, + std::enable_if_t<std::is_constructible<T, U&&>::value>* = nullptr, + std::enable_if_t< + !std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>* = + nullptr, + std::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr> + explicit constexpr Indestructible(U&& u) noexcept( + noexcept(T(std::declval<U>()))) + : storage_(std::forward<U>(u)) {} + template < + typename U = T, + std::enable_if_t<std::is_constructible<T, U&&>::value>* = nullptr, + std::enable_if_t< + !std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>* = + nullptr, + std::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr> + /* implicit */ constexpr Indestructible(U&& u) noexcept( + noexcept(T(std::declval<U>()))) + : storage_(std::forward<U>(u)) {} + + template <typename... Args, typename = decltype(T(std::declval<Args>()...))> + explicit constexpr Indestructible(Args&&... args) noexcept( + noexcept(T(std::declval<Args>()...))) + : storage_(std::forward<Args>(args)...) {} + template < + typename U, + typename... Args, + typename = decltype( + T(std::declval<std::initializer_list<U>&>(), + std::declval<Args>()...))> + explicit constexpr Indestructible(std::initializer_list<U> il, Args... args) noexcept( + noexcept( + T(std::declval<std::initializer_list<U>&>(), + std::declval<Args>()...))) + : storage_(il, std::forward<Args>(args)...) {} + + ~Indestructible() = default; + + Indestructible(Indestructible const&) = delete; + Indestructible& operator=(Indestructible const&) = delete; + + Indestructible(Indestructible&& other) noexcept( + noexcept(T(std::declval<T>()))) + : storage_(std::move(other.storage_.value)) { + other.erased_ = true; + } + Indestructible& operator=(Indestructible&& other) noexcept( + noexcept(T(std::declval<T>()))) { + storage_.value = std::move(other.storage_.value); + other.erased_ = true; + } + + T* get() noexcept { + check(); + return &storage_.value; + } + T const* get() const noexcept { + check(); + return &storage_.value; + } + T& operator*() noexcept { + return *get(); + } + T const& operator*() const noexcept { + return *get(); + } + T* operator->() noexcept { + return get(); + } + T const* operator->() const noexcept { + return get(); + } + + private: + void check() const noexcept { + assert(!erased_); + } + + union Storage { + T value; + + template <typename S = T, typename = decltype(S())> + constexpr Storage() noexcept(noexcept(T())) : value() {} + + template <typename... Args, typename = decltype(T(std::declval<Args>()...))> + explicit constexpr Storage(Args&&... args) noexcept( + noexcept(T(std::declval<Args>()...))) + : value(std::forward<Args>(args)...) {} + + ~Storage() {} + }; + + Storage storage_{}; + bool erased_{false}; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/IndexedMemPool.h b/ios/Pods/Flipper-Folly/folly/IndexedMemPool.h new file mode 100644 index 000000000..11b30f653 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IndexedMemPool.h @@ -0,0 +1,560 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <assert.h> +#include <errno.h> +#include <stdint.h> + +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/portability/SysMman.h> +#include <folly/portability/Unistd.h> +#include <folly/synchronization/AtomicStruct.h> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +namespace detail { +template <typename Pool> +struct IndexedMemPoolRecycler; +} // namespace detail + +template < + typename T, + bool EagerRecycleWhenTrivial = false, + bool EagerRecycleWhenNotTrivial = true> +struct IndexedMemPoolTraits { + static constexpr bool eagerRecycle() { + return std::is_trivial<T>::value ? EagerRecycleWhenTrivial + : EagerRecycleWhenNotTrivial; + } + + /// Called when the element pointed to by ptr is allocated for the + /// first time. + static void initialize(T* ptr) { + if (!eagerRecycle()) { + new (ptr) T(); + } + } + + /// Called when the element pointed to by ptr is freed at the pool + /// destruction time. + static void cleanup(T* ptr) { + if (!eagerRecycle()) { + ptr->~T(); + } + } + + /// Called when the element is allocated with the arguments forwarded from + /// IndexedMemPool::allocElem. + template <typename... Args> + static void onAllocate(T* ptr, Args&&... args) { + static_assert( + sizeof...(Args) == 0 || eagerRecycle(), + "emplace-style allocation requires eager recycle, " + "which is defaulted only for non-trivial types"); + if (eagerRecycle()) { + new (ptr) T(std::forward<Args>(args)...); + } + } + + /// Called when the element is recycled. + static void onRecycle(T* ptr) { + if (eagerRecycle()) { + ptr->~T(); + } + } +}; + +/// IndexedMemPool traits that implements the lazy lifecycle strategy. In this +/// strategy elements are default-constructed the first time they are allocated, +/// and destroyed when the pool itself is destroyed. +template <typename T> +using IndexedMemPoolTraitsLazyRecycle = IndexedMemPoolTraits<T, false, false>; + +/// IndexedMemPool traits that implements the eager lifecycle strategy. In this +/// strategy elements are constructed when they are allocated from the pool and +/// destroyed when recycled. +template <typename T> +using IndexedMemPoolTraitsEagerRecycle = IndexedMemPoolTraits<T, true, true>; + +/// Instances of IndexedMemPool dynamically allocate and then pool their +/// element type (T), returning 4-byte integer indices that can be passed +/// to the pool's operator[] method to access or obtain pointers to the +/// actual elements. The memory backing items returned from the pool +/// will always be readable, even if items have been returned to the pool. +/// These two features are useful for lock-free algorithms. The indexing +/// behavior makes it easy to build tagged pointer-like-things, since +/// a large number of elements can be managed using fewer bits than a +/// full pointer. The access-after-free behavior makes it safe to read +/// from T-s even after they have been recycled, since it is guaranteed +/// that the memory won't have been returned to the OS and unmapped +/// (the algorithm must still use a mechanism to validate that the read +/// was correct, but it doesn't have to worry about page faults), and if +/// the elements use internal sequence numbers it can be guaranteed that +/// there won't be an ABA match due to the element being overwritten with +/// a different type that has the same bit pattern. +/// +/// The object lifecycle strategy is controlled by the Traits parameter. +/// One strategy, implemented by IndexedMemPoolTraitsEagerRecycle, is to +/// construct objects when they are allocated from the pool and destroy +/// them when they are recycled. In this mode allocIndex and allocElem +/// have emplace-like semantics. In another strategy, implemented by +/// IndexedMemPoolTraitsLazyRecycle, objects are default-constructed the +/// first time they are removed from the pool, and deleted when the pool +/// itself is deleted. By default the first mode is used for non-trivial +/// T, and the second is used for trivial T. Clients can customize the +/// object lifecycle by providing their own Traits implementation. +/// See IndexedMemPoolTraits for a Traits example. +/// +/// IMPORTANT: Space for extra elements is allocated to account for those +/// that are inaccessible because they are in other local lists, so the +/// actual number of items that can be allocated ranges from capacity to +/// capacity + (NumLocalLists_-1)*LocalListLimit_. This is important if +/// you are trying to maximize the capacity of the pool while constraining +/// the bit size of the resulting pointers, because the pointers will +/// actually range up to the boosted capacity. See maxIndexForCapacity +/// and capacityForMaxIndex. +/// +/// To avoid contention, NumLocalLists_ free lists of limited (less than +/// or equal to LocalListLimit_) size are maintained, and each thread +/// retrieves and returns entries from its associated local list. If the +/// local list becomes too large then elements are placed in bulk in a +/// global free list. This allows items to be efficiently recirculated +/// from consumers to producers. AccessSpreader is used to access the +/// local lists, so there is no performance advantage to having more +/// local lists than L1 caches. +/// +/// The pool mmap-s the entire necessary address space when the pool is +/// constructed, but delays element construction. This means that only +/// elements that are actually returned to the caller get paged into the +/// process's resident set (RSS). +template < + typename T, + uint32_t NumLocalLists_ = 32, + uint32_t LocalListLimit_ = 200, + template <typename> class Atom = std::atomic, + typename Traits = IndexedMemPoolTraits<T>> +struct IndexedMemPool { + typedef T value_type; + + typedef std::unique_ptr<T, detail::IndexedMemPoolRecycler<IndexedMemPool>> + UniquePtr; + + IndexedMemPool(const IndexedMemPool&) = delete; + IndexedMemPool& operator=(const IndexedMemPool&) = delete; + + static_assert(LocalListLimit_ <= 255, "LocalListLimit must fit in 8 bits"); + enum { + NumLocalLists = NumLocalLists_, + LocalListLimit = LocalListLimit_, + }; + + static_assert( + std::is_nothrow_default_constructible<Atom<uint32_t>>::value, + "Atom must be nothrow default constructible"); + + // these are public because clients may need to reason about the number + // of bits required to hold indices from a pool, given its capacity + + static constexpr uint32_t maxIndexForCapacity(uint32_t capacity) { + // index of std::numeric_limits<uint32_t>::max() is reserved for isAllocated + // tracking + return uint32_t(std::min( + uint64_t(capacity) + (NumLocalLists - 1) * LocalListLimit, + uint64_t(std::numeric_limits<uint32_t>::max() - 1))); + } + + static constexpr uint32_t capacityForMaxIndex(uint32_t maxIndex) { + return maxIndex - (NumLocalLists - 1) * LocalListLimit; + } + + /// Constructs a pool that can allocate at least _capacity_ elements, + /// even if all the local lists are full + explicit IndexedMemPool(uint32_t capacity) + : actualCapacity_(maxIndexForCapacity(capacity)), + size_(0), + globalHead_(TaggedPtr{}) { + const size_t needed = sizeof(Slot) * (actualCapacity_ + 1); + size_t pagesize = size_t(sysconf(_SC_PAGESIZE)); + mmapLength_ = ((needed - 1) & ~(pagesize - 1)) + pagesize; + assert(needed <= mmapLength_ && mmapLength_ < needed + pagesize); + assert((mmapLength_ % pagesize) == 0); + + slots_ = static_cast<Slot*>(mmap( + nullptr, + mmapLength_, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)); + if (slots_ == MAP_FAILED) { + assert(errno == ENOMEM); + throw std::bad_alloc(); + } + } + + /// Destroys all of the contained elements + ~IndexedMemPool() { + using A = Atom<uint32_t>; + for (uint32_t i = maxAllocatedIndex(); i > 0; --i) { + Traits::cleanup(&slots_[i].elem); + slots_[i].localNext.~A(); + slots_[i].globalNext.~A(); + } + munmap(slots_, mmapLength_); + } + + /// Returns a lower bound on the number of elements that may be + /// simultaneously allocated and not yet recycled. Because of the + /// local lists it is possible that more elements than this are returned + /// successfully + uint32_t capacity() { + return capacityForMaxIndex(actualCapacity_); + } + + /// Returns the maximum index of elements ever allocated in this pool + /// including elements that have been recycled. + uint32_t maxAllocatedIndex() const { + // Take the minimum since it is possible that size_ > actualCapacity_. + // This can happen if there are multiple concurrent requests + // when size_ == actualCapacity_ - 1. + return std::min(uint32_t(size_), uint32_t(actualCapacity_)); + } + + /// Finds a slot with a non-zero index, emplaces a T there if we're + /// using the eager recycle lifecycle mode, and returns the index, + /// or returns 0 if no elements are available. Passes a pointer to + /// the element to Traits::onAllocate before the slot is marked as + /// allocated. + template <typename... Args> + uint32_t allocIndex(Args&&... args) { + auto idx = localPop(localHead()); + if (idx != 0) { + Slot& s = slot(idx); + Traits::onAllocate(&s.elem, std::forward<Args>(args)...); + markAllocated(s); + } + return idx; + } + + /// If an element is available, returns a std::unique_ptr to it that will + /// recycle the element to the pool when it is reclaimed, otherwise returns + /// a null (falsy) std::unique_ptr. Passes a pointer to the element to + /// Traits::onAllocate before the slot is marked as allocated. + template <typename... Args> + UniquePtr allocElem(Args&&... args) { + auto idx = allocIndex(std::forward<Args>(args)...); + T* ptr = idx == 0 ? nullptr : &slot(idx).elem; + return UniquePtr(ptr, typename UniquePtr::deleter_type(this)); + } + + /// Gives up ownership previously granted by alloc() + void recycleIndex(uint32_t idx) { + assert(isAllocated(idx)); + localPush(localHead(), idx); + } + + /// Provides access to the pooled element referenced by idx + T& operator[](uint32_t idx) { + return slot(idx).elem; + } + + /// Provides access to the pooled element referenced by idx + const T& operator[](uint32_t idx) const { + return slot(idx).elem; + } + + /// If elem == &pool[idx], then pool.locateElem(elem) == idx. Also, + /// pool.locateElem(nullptr) == 0 + uint32_t locateElem(const T* elem) const { + if (!elem) { + return 0; + } + + static_assert(std::is_standard_layout<Slot>::value, "offsetof needs POD"); + + auto slot = reinterpret_cast<const Slot*>( + reinterpret_cast<const char*>(elem) - offsetof(Slot, elem)); + auto rv = uint32_t(slot - slots_); + + // this assert also tests that rv is in range + assert(elem == &(*this)[rv]); + return rv; + } + + /// Returns true iff idx has been alloc()ed and not recycleIndex()ed + bool isAllocated(uint32_t idx) const { + return slot(idx).localNext.load(std::memory_order_acquire) == uint32_t(-1); + } + + private: + ///////////// types + + struct Slot { + T elem; + Atom<uint32_t> localNext; + Atom<uint32_t> globalNext; + + Slot() : localNext{}, globalNext{} {} + }; + + struct TaggedPtr { + uint32_t idx; + + // size is bottom 8 bits, tag in top 24. g++'s code generation for + // bitfields seems to depend on the phase of the moon, plus we can + // do better because we can rely on other checks to avoid masking + uint32_t tagAndSize; + + enum : uint32_t { + SizeBits = 8, + SizeMask = (1U << SizeBits) - 1, + TagIncr = 1U << SizeBits, + }; + + uint32_t size() const { + return tagAndSize & SizeMask; + } + + TaggedPtr withSize(uint32_t repl) const { + assert(repl <= LocalListLimit); + return TaggedPtr{idx, (tagAndSize & ~SizeMask) | repl}; + } + + TaggedPtr withSizeIncr() const { + assert(size() < LocalListLimit); + return TaggedPtr{idx, tagAndSize + 1}; + } + + TaggedPtr withSizeDecr() const { + assert(size() > 0); + return TaggedPtr{idx, tagAndSize - 1}; + } + + TaggedPtr withIdx(uint32_t repl) const { + return TaggedPtr{repl, tagAndSize + TagIncr}; + } + + TaggedPtr withEmpty() const { + return withIdx(0).withSize(0); + } + }; + + struct alignas(hardware_destructive_interference_size) LocalList { + AtomicStruct<TaggedPtr, Atom> head; + + LocalList() : head(TaggedPtr{}) {} + }; + + ////////// fields + + /// the number of bytes allocated from mmap, which is a multiple of + /// the page size of the machine + size_t mmapLength_; + + /// the actual number of slots that we will allocate, to guarantee + /// that we will satisfy the capacity requested at construction time. + /// They will be numbered 1..actualCapacity_ (note the 1-based counting), + /// and occupy slots_[1..actualCapacity_]. + uint32_t actualCapacity_; + + /// this records the number of slots that have actually been constructed. + /// To allow use of atomic ++ instead of CAS, we let this overflow. + /// The actual number of constructed elements is min(actualCapacity_, + /// size_) + Atom<uint32_t> size_; + + /// raw storage, only 1..min(size_,actualCapacity_) (inclusive) are + /// actually constructed. Note that slots_[0] is not constructed or used + alignas(hardware_destructive_interference_size) Slot* slots_; + + /// use AccessSpreader to find your list. We use stripes instead of + /// thread-local to avoid the need to grow or shrink on thread start + /// or join. These are heads of lists chained with localNext + LocalList local_[NumLocalLists]; + + /// this is the head of a list of node chained by globalNext, that are + /// themselves each the head of a list chained by localNext + alignas(hardware_destructive_interference_size) + AtomicStruct<TaggedPtr, Atom> globalHead_; + + ///////////// private methods + + uint32_t slotIndex(uint32_t idx) const { + assert( + 0 < idx && idx <= actualCapacity_ && + idx <= size_.load(std::memory_order_acquire)); + return idx; + } + + Slot& slot(uint32_t idx) { + return slots_[slotIndex(idx)]; + } + + const Slot& slot(uint32_t idx) const { + return slots_[slotIndex(idx)]; + } + + // localHead references a full list chained by localNext. s should + // reference slot(localHead), it is passed as a micro-optimization + void globalPush(Slot& s, uint32_t localHead) { + while (true) { + TaggedPtr gh = globalHead_.load(std::memory_order_acquire); + s.globalNext.store(gh.idx, std::memory_order_relaxed); + if (globalHead_.compare_exchange_strong(gh, gh.withIdx(localHead))) { + // success + return; + } + } + } + + // idx references a single node + void localPush(AtomicStruct<TaggedPtr, Atom>& head, uint32_t idx) { + Slot& s = slot(idx); + TaggedPtr h = head.load(std::memory_order_acquire); + bool recycled = false; + while (true) { + s.localNext.store(h.idx, std::memory_order_release); + if (!recycled) { + Traits::onRecycle(&slot(idx).elem); + recycled = true; + } + + if (h.size() == LocalListLimit) { + // push will overflow local list, steal it instead + if (head.compare_exchange_strong(h, h.withEmpty())) { + // steal was successful, put everything in the global list + globalPush(s, idx); + return; + } + } else { + // local list has space + if (head.compare_exchange_strong(h, h.withIdx(idx).withSizeIncr())) { + // success + return; + } + } + // h was updated by failing CAS + } + } + + // returns 0 if empty + uint32_t globalPop() { + while (true) { + TaggedPtr gh = globalHead_.load(std::memory_order_acquire); + if (gh.idx == 0 || + globalHead_.compare_exchange_strong( + gh, + gh.withIdx( + slot(gh.idx).globalNext.load(std::memory_order_relaxed)))) { + // global list is empty, or pop was successful + return gh.idx; + } + } + } + + // returns 0 if allocation failed + uint32_t localPop(AtomicStruct<TaggedPtr, Atom>& head) { + while (true) { + TaggedPtr h = head.load(std::memory_order_acquire); + if (h.idx != 0) { + // local list is non-empty, try to pop + Slot& s = slot(h.idx); + auto next = s.localNext.load(std::memory_order_relaxed); + if (head.compare_exchange_strong(h, h.withIdx(next).withSizeDecr())) { + // success + return h.idx; + } + continue; + } + + uint32_t idx = globalPop(); + if (idx == 0) { + // global list is empty, allocate and construct new slot + if (size_.load(std::memory_order_relaxed) >= actualCapacity_ || + (idx = ++size_) > actualCapacity_) { + // allocation failed + return 0; + } + Slot& s = slot(idx); + // Atom is enforced above to be nothrow-default-constructible + // As an optimization, use default-initialization (no parens) rather + // than direct-initialization (with parens): these locations are + // stored-to before they are loaded-from + new (&s.localNext) Atom<uint32_t>; + new (&s.globalNext) Atom<uint32_t>; + Traits::initialize(&s.elem); + return idx; + } + + Slot& s = slot(idx); + auto next = s.localNext.load(std::memory_order_relaxed); + if (head.compare_exchange_strong( + h, h.withIdx(next).withSize(LocalListLimit))) { + // global list moved to local list, keep head for us + return idx; + } + // local bulk push failed, return idx to the global list and try again + globalPush(s, idx); + } + } + + AtomicStruct<TaggedPtr, Atom>& localHead() { + auto stripe = AccessSpreader<Atom>::current(NumLocalLists); + return local_[stripe].head; + } + + void markAllocated(Slot& slot) { + slot.localNext.store(uint32_t(-1), std::memory_order_release); + } + + public: + static constexpr std::size_t kSlotSize = sizeof(Slot); +}; + +namespace detail { + +/// This is a stateful Deleter functor, which allows std::unique_ptr +/// to track elements allocated from an IndexedMemPool by tracking the +/// associated pool. See IndexedMemPool::allocElem. +template <typename Pool> +struct IndexedMemPoolRecycler { + Pool* pool; + + explicit IndexedMemPoolRecycler(Pool* pool) : pool(pool) {} + + IndexedMemPoolRecycler(const IndexedMemPoolRecycler<Pool>& rhs) = default; + IndexedMemPoolRecycler& operator=(const IndexedMemPoolRecycler<Pool>& rhs) = + default; + + void operator()(typename Pool::value_type* elem) const { + pool->recycleIndex(pool->locateElem(elem)); + } +}; + +} // namespace detail + +} // namespace folly + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/IntrusiveList.h b/ios/Pods/Flipper-Folly/folly/IntrusiveList.h new file mode 100644 index 000000000..9e392a4d5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/IntrusiveList.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/* + * This file contains convenience aliases that make boost::intrusive::list + * easier to use. + */ + +#include <boost/intrusive/list.hpp> + +namespace folly { + +/** + * An auto-unlink intrusive list hook. + */ +using IntrusiveListHook = boost::intrusive::list_member_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>>; + +/** + * An intrusive list. + * + * An IntrusiveList always uses an auto-unlink hook. + * Beware that IntrusiveList::size() is an O(n) operation, since it has to walk + * the entire list. + * + * Example usage: + * + * class Foo { + * // Note that the listHook member variable needs to be visible + * // to the code that defines the IntrusiveList instantiation. + * // The list hook can be made public, or you can make the other class a + * // friend. + * IntrusiveListHook listHook; + * }; + * + * using FooList = IntrusiveList<Foo, &Foo::listHook>; + * + * Foo *foo = new Foo(); + * FooList myList; + * myList.push_back(*foo); + * + * Note that each IntrusiveListHook can only be part of a single list at any + * given time. If you need the same object to be stored in two lists at once, + * you need to use two different IntrusiveListHook member variables. + * + * The elements stored in the list must contain an IntrusiveListHook member + * variable. + */ +template <typename T, IntrusiveListHook T::*PtrToMember> +using IntrusiveList = boost::intrusive::list< + T, + boost::intrusive::member_hook<T, IntrusiveListHook, PtrToMember>, + boost::intrusive::constant_time_size<false>>; + +/** + * A safe-link intrusive list hook. + */ +using SafeIntrusiveListHook = boost::intrusive::list_member_hook< + boost::intrusive::link_mode<boost::intrusive::safe_link>>; + +/** + * An intrusive list with const-time size() method. + * + * A CountedIntrusiveList always uses a safe-link hook. + * CountedIntrusiveList::size() is an O(1) operation. Users of this type + * of lists need to remove a member from a list by calling one of the + * methods on the list (e.g., erase(), pop_front(), etc.), rather than + * calling unlink on the member's list hook. Given references to a + * list and a member, a constant-time removal operation can be + * accomplished by list.erase(list.iterator_to(member)). Also, when a + * member is destroyed, it is NOT automatically removed from the list. + * + * Example usage: + * + * class Foo { + * // Note that the listHook member variable needs to be visible + * // to the code that defines the CountedIntrusiveList instantiation. + * // The list hook can be made public, or you can make the other class a + * // friend. + * SafeIntrusiveListHook listHook; + * }; + * + * using FooList = CountedIntrusiveList<Foo, &Foo::listHook> FooList; + * + * Foo *foo = new Foo(); + * FooList myList; + * myList.push_back(*foo); + * myList.pop_front(); + * + * Note that each SafeIntrusiveListHook can only be part of a single list at any + * given time. If you need the same object to be stored in two lists at once, + * you need to use two different SafeIntrusiveListHook member variables. + * + * The elements stored in the list must contain an SafeIntrusiveListHook member + * variable. + */ +template <typename T, SafeIntrusiveListHook T::*PtrToMember> +using CountedIntrusiveList = boost::intrusive::list< + T, + boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>, + boost::intrusive::constant_time_size<true>>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Lazy.h b/ios/Pods/Flipper-Folly/folly/Lazy.h new file mode 100644 index 000000000..11f984e46 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Lazy.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> +#include <utility> + +#include <folly/Optional.h> +#include <folly/functional/Invoke.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +/* + * Lazy -- for delayed initialization of a value. The value's + * initialization will be computed on demand at its first use, but + * will not be recomputed if its value is requested again. The value + * may still be mutated after its initialization if the lazy is not + * declared const. + * + * The value is created using folly::lazy, usually with a lambda, and + * its value is requested using operator(). + * + * Note that the value is not safe for concurrent accesses by multiple + * threads, even if you declare it const. See note below. + * + * + * Example Usage: + * + * void foo() { + * auto const val = folly::lazy([&]{ + * return something_expensive(blah()); + * }); + * + * if (condition1) { + * use(val()); + * } + * if (condition2) { + * useMaybeAgain(val()); + * } else { + * // Unneeded in this branch. + * } + * } + * + * + * Rationale: + * + * - operator() is used to request the value instead of an implicit + * conversion because the slight syntactic overhead in common + * seems worth the increased clarity. + * + * - Lazy values do not model CopyConstructible because it is + * unclear what semantics would be desirable. Either copies + * should share the cached value (adding overhead to cases that + * don't need to support copies), or they could recompute the + * value unnecessarily. Sharing with mutable lazies would also + * leave them with non-value semantics despite looking + * value-like. + * + * - Not thread safe for const accesses. Many use cases for lazy + * values are local variables on the stack, where multiple + * threads shouldn't even be able to reach the value. It still + * is useful to indicate/check that the value doesn't change with + * const, particularly when it is captured by a large family of + * lambdas. Adding internal synchronization seems like it would + * pessimize the most common use case in favor of less likely use + * cases. + * + */ + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +template <class Func> +struct Lazy { + typedef invoke_result_t<Func> result_type; + + static_assert( + !std::is_const<Func>::value, + "Func should not be a const-qualified type"); + static_assert( + !std::is_reference<Func>::value, + "Func should not be a reference type"); + + explicit Lazy(Func&& f) : func_(std::move(f)) {} + explicit Lazy(const Func& f) : func_(f) {} + + Lazy(Lazy&& o) : value_(std::move(o.value_)), func_(std::move(o.func_)) {} + + Lazy(const Lazy&) = delete; + Lazy& operator=(const Lazy&) = delete; + Lazy& operator=(Lazy&&) = delete; + + const result_type& operator()() const { + ensure_initialized(); + + return *value_; + } + + result_type& operator()() { + ensure_initialized(); + + return *value_; + } + + private: + void ensure_initialized() const { + if (!value_) { + value_ = func_(); + } + } + + mutable Optional<result_type> value_; + mutable Func func_; +}; + +} // namespace detail + +////////////////////////////////////////////////////////////////////// + +template <class Func> +auto lazy(Func&& fun) { + return detail::Lazy<remove_cvref_t<Func>>(std::forward<Func>(fun)); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Likely.h b/ios/Pods/Flipper-Folly/folly/Likely.h new file mode 100644 index 000000000..e3597fff6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Likely.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#if __GNUC__ +#define FOLLY_DETAIL_BUILTIN_EXPECT(b, t) (__builtin_expect(b, t)) +#else +#define FOLLY_DETAIL_BUILTIN_EXPECT(b, t) b +#endif + +// Likeliness annotations +// +// Useful when the author has better knowledge than the compiler of whether +// the branch condition is overwhelmingly likely to take a specific value. +// +// Useful when the author has better knowledge than the compiler of which code +// paths are designed as the fast path and which are designed as the slow path, +// and to force the compiler to optimize for the fast path, even when it is not +// overwhelmingly likely. + +#define FOLLY_LIKELY(x) FOLLY_DETAIL_BUILTIN_EXPECT((x), 1) +#define FOLLY_UNLIKELY(x) FOLLY_DETAIL_BUILTIN_EXPECT((x), 0) + +// Un-namespaced annotations + +#undef LIKELY +#undef UNLIKELY + +#if defined(__GNUC__) +#define LIKELY(x) (__builtin_expect((x), 1)) +#define UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif diff --git a/ios/Pods/Flipper-Folly/folly/LockTraits.h b/ios/Pods/Flipper-Folly/folly/LockTraits.h new file mode 100644 index 000000000..f3c36d961 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/LockTraits.h @@ -0,0 +1,671 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 module provides a traits class for describing properties about mutex + * classes. + * + * This is a primitive for building higher-level abstractions that can work + * with a variety of mutex classes. For instance, this allows + * folly::Synchronized to support a number of different mutex types. + */ +#pragma once + +#include <chrono> +#include <type_traits> + +#include <folly/functional/Invoke.h> + +// Android, OSX, and Cygwin don't have timed mutexes +#if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \ + defined(__CYGWIN__) +#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 0 +#else +#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 1 +#endif + +namespace folly { +namespace detail { + +namespace member { +FOLLY_CREATE_MEMBER_INVOKER(lock_invoker, lock); +FOLLY_CREATE_MEMBER_INVOKER(try_lock_for_invoker, try_lock_for); +FOLLY_CREATE_MEMBER_INVOKER(lock_shared_invoker, lock_shared); +FOLLY_CREATE_MEMBER_INVOKER(lock_upgrade_invoker, lock_upgrade); +} // namespace member + +/** + * An enum to describe the "level" of a mutex. The supported levels are + * Unique - a normal mutex that supports only exclusive locking + * Shared - a shared mutex which has shared locking and unlocking functions; + * Upgrade - a mutex that has all the methods of the two above along with + * support for upgradable locking + */ +enum class MutexLevel { UNIQUE, SHARED, UPGRADE }; + +/** + * A template dispatch mechanism that is used to determine the level of the + * mutex based on its interface. As decided by LockInterfaceDispatcher. + */ +template <bool is_unique, bool is_shared, bool is_upgrade> +struct MutexLevelValueImpl; +template <> +struct MutexLevelValueImpl<true, false, false> { + static constexpr MutexLevel value = MutexLevel::UNIQUE; +}; +template <> +struct MutexLevelValueImpl<true, true, false> { + static constexpr MutexLevel value = MutexLevel::SHARED; +}; +template <> +struct MutexLevelValueImpl<true, true, true> { + static constexpr MutexLevel value = MutexLevel::UPGRADE; +}; + +/** + * An internal helper class to help identify the interface supported by the + * mutex. This is used in conjunction with the above MutexLevel + * specializations and the LockTraitsImpl to determine what functions are + * supported by objects of type Mutex + */ +template <class Mutex> +class LockInterfaceDispatcher { + private: + // assert that the mutex type has basic lock and unlock functions + static_assert( + folly::is_invocable_v<member::lock_invoker, Mutex>, + "The mutex type must support lock and unlock functions"); + + using duration = std::chrono::milliseconds; + + public: + static constexpr bool has_lock_unique = true; + static constexpr bool has_lock_timed = + folly::is_invocable_v<member::try_lock_for_invoker, Mutex, duration>; + static constexpr bool has_lock_shared = + folly::is_invocable_v<member::lock_shared_invoker, Mutex>; + static constexpr bool has_lock_upgrade = + folly::is_invocable_v<member::lock_upgrade_invoker, Mutex>; +}; + +/** + * LockTraitsImpl is the base that is used to desribe the interface used by + * different mutex types. It accepts a MutexLevel argument and a boolean to + * show whether the mutex is a timed mutex or not. The implementations are + * partially specialized and inherit from the other implementations to get + * similar functionality + */ +template <class Mutex, MutexLevel level, bool is_timed> +struct LockTraitsImpl; + +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> { + static constexpr bool is_timed{false}; + static constexpr bool is_shared{false}; + static constexpr bool is_upgrade{false}; + + /** + * Acquire the lock exclusively. + */ + static void lock(Mutex& mutex) { + mutex.lock(); + } + + /** + * Release an exclusively-held lock. + */ + static void unlock(Mutex& mutex) { + mutex.unlock(); + } + + /** + * Try to acquire the mutex + */ + static bool try_lock(Mutex& mutex) { + return mutex.try_lock(); + } +}; + +/** + * Higher level mutexes have all the capabilities of the lower levels so + * inherit + */ +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::SHARED, false> + : public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> { + static constexpr bool is_timed{false}; + static constexpr bool is_shared{true}; + static constexpr bool is_upgrade{false}; + + /** + * Acquire the lock in shared (read) mode. + */ + static void lock_shared(Mutex& mutex) { + mutex.lock_shared(); + } + + /** + * Release a lock held in shared mode. + */ + static void unlock_shared(Mutex& mutex) { + mutex.unlock_shared(); + } + + /** + * Try to acquire the mutex in shared mode + */ + static bool try_lock_shared(Mutex& mutex) { + return mutex.try_lock_shared(); + } +}; + +/** + * The following methods are supported. There are a few methods + * + * m.lock_upgrade() + * m.unlock_upgrade() + * m.try_lock_upgrade() + * + * m.unlock_upgrade_and_lock() + * + * m.unlock_and_lock_upgrade() + * m.unlock_and_lock_shared() + * m.unlock_upgrade_and_lock_shared() + * + * m.try_lock_upgrade_for(rel_time) + * m.try_unlock_upgrade_and_lock_for(rel_time) + * + * Upgrading a shared lock is likely to deadlock when there is more than one + * thread performing an upgrade. This applies both to upgrading a shared lock + * to an upgrade lock and to upgrading a shared lock to a unique lock. + * + * Therefore, none of the following methods is supported: + * unlock_shared_and_lock_upgrade + * unlock_shared_and_lock + * try_unlock_shared_and_lock_upgrade + * try_unlock_shared_and_lock + * try_unlock_shared_and_lock_upgrade_for + * try_unlock_shared_and_lock_for + */ +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false> + : public LockTraitsImpl<Mutex, MutexLevel::SHARED, false> { + static constexpr bool is_timed{false}; + static constexpr bool is_shared{true}; + static constexpr bool is_upgrade{true}; + + /** + * Acquire the lock in upgradable mode. + */ + static void lock_upgrade(Mutex& mutex) { + mutex.lock_upgrade(); + } + + /** + * Release the lock in upgrade mode + */ + static void unlock_upgrade(Mutex& mutex) { + mutex.unlock_upgrade(); + } + + /** + * Try and acquire the lock in upgrade mode + */ + static bool try_lock_upgrade(Mutex& mutex) { + return mutex.try_lock_upgrade(); + } + + /** + * Upgrade from an upgradable state to an exclusive state + */ + static void unlock_upgrade_and_lock(Mutex& mutex) { + mutex.unlock_upgrade_and_lock(); + } + + /** + * Downgrade from an exclusive state to an upgrade state + */ + static void unlock_and_lock_upgrade(Mutex& mutex) { + mutex.unlock_and_lock_upgrade(); + } + + /** + * Downgrade from an exclusive state to a shared state + */ + static void unlock_and_lock_shared(Mutex& mutex) { + mutex.unlock_and_lock_shared(); + } + + /** + * Downgrade from an upgrade state to a shared state + */ + static void unlock_upgrade_and_lock_shared(Mutex& mutex) { + mutex.unlock_upgrade_and_lock_shared(); + } +}; + +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> + : public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> { + static constexpr bool is_timed{true}; + static constexpr bool is_shared{false}; + static constexpr bool is_upgrade{false}; + + /** + * Acquire the lock exclusively, with a timeout. + * + * Returns true or false indicating if the lock was acquired or not. + */ + template <class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return mutex.try_lock_for(timeout); + } +}; + +/** + * Note that there is no deadly diamond here because all the structs only have + * static functions and static bools which are going to be overridden by the + * lowest level implementation + */ +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::SHARED, true> + : public LockTraitsImpl<Mutex, MutexLevel::SHARED, false>, + public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> { + static constexpr bool is_timed{true}; + static constexpr bool is_shared{true}; + static constexpr bool is_upgrade{false}; + + /** + * Acquire the lock exclusively, with a timeout. + * + * Returns true or false indicating if the lock was acquired or not. + */ + template <class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return mutex.try_lock_for(timeout); + } + + /** + * Acquire the lock in shared (read) mode, with a timeout. + * + * Returns true or false indicating if the lock was acquired or not. + */ + template <class Rep, class Period> + static bool try_lock_shared_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return mutex.try_lock_shared_for(timeout); + } +}; + +template <class Mutex> +struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true> + : public LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>, + public LockTraitsImpl<Mutex, MutexLevel::SHARED, true> { + static constexpr bool is_timed{true}; + static constexpr bool is_shared{true}; + static constexpr bool is_upgrade{true}; + + /** + * Acquire the lock in upgrade mode with a timeout + * + * Returns true or false indicating whether the lock was acquired or not + */ + template <class Rep, class Period> + static bool try_lock_upgrade_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return mutex.try_lock_upgrade_for(timeout); + } + + /** + * Try to upgrade from an upgradable state to an exclusive state. + * + * Returns true or false indicating whether the lock was acquired or not + */ + template <class Rep, class Period> + static bool try_unlock_upgrade_and_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return mutex.try_unlock_upgrade_and_lock_for(timeout); + } +}; + +/** + * Unlock helpers + * + * These help in determining whether it is safe for Synchronized::LockedPtr + * instances to be move assigned from one another. It is safe if they both + * have the same unlock policy, and it is not if they don't have the same + * unlock policy. For example + * + * auto wlock = synchronized.wlock(); + * wlock.unlock(); + * + * wlock = synchronized.rlock(); + * + * This code would try to release the shared lock with a call to unlock(), + * resulting in possibly undefined behavior. By allowing the LockPolicy + * classes (defined below) to know what their unlocking behavior is, we can + * prevent against this by disabling unsafe conversions to and from + * incompatible LockedPtr types (they are incompatible if the underlying + * LockPolicy has different unlock policies. + */ +template <template <typename...> class LockTraits> +struct UnlockPolicyExclusive { + constexpr static bool allows_concurrent_access = false; + + template <typename Mutex> + static void unlock(Mutex& mutex) { + LockTraits<Mutex>::unlock(mutex); + } +}; +template <template <typename...> class LockTraits> +struct UnlockPolicyShared { + constexpr static bool allows_concurrent_access = true; + + template <typename Mutex> + static void unlock(Mutex& mutex) { + LockTraits<Mutex>::unlock_shared(mutex); + } +}; +template <template <typename...> class LockTraits> +struct UnlockPolicyUpgrade { + constexpr static bool allows_concurrent_access = true; + + template <typename Mutex> + static void unlock(Mutex& mutex) { + LockTraits<Mutex>::unlock_upgrade(mutex); + } +}; + +} // namespace detail + +/** + * LockTraits describes details about a particular mutex type. + * + * The default implementation automatically attempts to detect traits + * based on the presence of various member functions. + * + * You can specialize LockTraits to provide custom behavior for lock + * classes that do not use the standard method names + * (lock()/unlock()/lock_shared()/unlock_shared()/try_lock_for()) + * + * + * LockTraits contains the following members variables: + * - static constexpr bool is_shared + * True if the lock supports separate shared vs exclusive locking states. + * - static constexpr bool is_timed + * True if the lock supports acquiring the lock with a timeout. + * - static constexpr bool is_upgrade + * True if the lock supports an upgradable state + * + * The following static methods always exist: + * - lock(Mutex& mutex) + * - unlock(Mutex& mutex) + * - try_lock(Mutex& mutex) + * + * The following static methods may exist, depending on is_shared, is_timed + * and is_upgrade: + * - lock_shared() + * - try_lock_shared() + * + * - try_lock_for() + * - try_lock_shared_for() + * + * - lock_upgrade() + * - try_lock_upgrade() + * - unlock_upgrade_and_lock() + * - unlock_and_lock_upgrade() + * - unlock_and_lock_shared() + * - unlock_upgrade_and_lock_shared() + * + * - try_lock_upgrade_for() + * - try_unlock_upgrade_and_lock_for() + * + * - unlock_shared() + * - unlock_upgrade() + */ + +/** + * Decoupling LockTraits and LockTraitsBase so that if people want to fully + * specialize LockTraits then they can inherit from LockTraitsBase instead + * of LockTraits with all the same goodies :) + */ +template <class Mutex> +struct LockTraitsBase + : public detail::LockTraitsImpl< + Mutex, + detail::MutexLevelValueImpl< + detail::LockInterfaceDispatcher<Mutex>::has_lock_unique, + detail::LockInterfaceDispatcher<Mutex>::has_lock_shared, + detail::LockInterfaceDispatcher<Mutex>::has_lock_upgrade>::value, + detail::LockInterfaceDispatcher<Mutex>::has_lock_timed> {}; + +template <class Mutex> +struct LockTraits : public LockTraitsBase<Mutex> {}; + +/* + * Lock policy classes. + * + * These can be used as template parameters to provide compile-time + * selection over the type of lock operation to perform. + */ +/** + * A lock policy that performs exclusive lock operations. + */ +struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>; + + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::lock(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return LockTraits<Mutex>::try_lock_for(mutex, timeout); + } +}; + +/** + * A lock policy that performs shared lock operations. + * This policy only works with shared mutex types. + */ +struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>; + + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::lock_shared(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout); + } +}; + +/** + * A lock policy with the following mapping + * + * lock() -> lock_upgrade() + * unlock() -> unlock_upgrade() + * try_lock_for -> try_lock_upgrade_for() + */ +struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>; + + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::lock_upgrade(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return LockTraits<Mutex>::try_lock_upgrade_for(mutex, timeout); + } +}; + +/***************************************************************************** + * Policies for optional mutex locking + ****************************************************************************/ +/** + * A lock policy that tries to acquire write locks and returns true or false + * based on whether the lock operation succeeds + */ +struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>; + + template <class Mutex> + static bool lock(Mutex& mutex) { + return LockTraits<Mutex>::try_lock(mutex); + } +}; + +/** + * A lock policy that tries to acquire a read lock and returns true or false + * based on whether the lock operation succeeds + */ +struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>; + + template <class Mutex> + static bool lock(Mutex& mutex) { + return LockTraits<Mutex>::try_lock_shared(mutex); + } +}; + +/** + * A lock policy that tries to acquire an upgrade lock and returns true or + * false based on whether the lock operation succeeds + */ +struct LockPolicyTryUpgrade : detail::UnlockPolicyUpgrade<LockTraits> { + using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>; + + template <class Mutex> + static bool lock(Mutex& mutex) { + return LockTraits<Mutex>::try_lock_upgrade(mutex); + } +}; + +/***************************************************************************** + * Policies for all the transitions from possible mutex levels + ****************************************************************************/ +/** + * A lock policy with the following mapping + * + * lock() -> unlock_upgrade_and_lock() + * unlock() -> unlock() + * try_lock_for -> try_unlock_upgrade_and_lock_for() + */ +struct LockPolicyFromUpgradeToExclusive : LockPolicyExclusive { + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::unlock_upgrade_and_lock(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>& timeout) { + return LockTraits<Mutex>::try_unlock_upgrade_and_lock_for(mutex, timeout); + } +}; + +/** + * A lock policy with the following mapping + * + * lock() -> unlock_and_lock_upgrade() + * unlock() -> unlock_upgrade() + * try_lock_for -> unlock_and_lock_upgrade() + */ +struct LockPolicyFromExclusiveToUpgrade : LockPolicyUpgrade { + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::unlock_and_lock_upgrade(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>&) { + LockTraits<Mutex>::unlock_and_lock_upgrade(mutex); + + // downgrade should be non blocking and should succeed + return true; + } +}; + +/** + * A lock policy with the following mapping + * + * lock() -> unlock_upgrade_and_lock_shared() + * unlock() -> unlock_shared() + * try_lock_for -> unlock_upgrade_and_lock_shared() + */ +struct LockPolicyFromUpgradeToShared : LockPolicyShared { + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>&) { + LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex); + + // downgrade should be non blocking and should succeed + return true; + } +}; + +/** + * A lock policy with the following mapping + * + * lock() -> unlock_and_lock_shared() + * unlock() -> unlock_shared() + * try_lock_for() -> unlock_and_lock_shared() + */ +struct LockPolicyFromExclusiveToShared : LockPolicyShared { + template <class Mutex> + static std::true_type lock(Mutex& mutex) { + LockTraits<Mutex>::unlock_and_lock_shared(mutex); + return std::true_type{}; + } + template <class Mutex, class Rep, class Period> + static bool try_lock_for( + Mutex& mutex, + const std::chrono::duration<Rep, Period>&) { + LockTraits<Mutex>::unlock_and_lock_shared(mutex); + + // downgrade should be non blocking and should succeed + return true; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MPMCPipeline.h b/ios/Pods/Flipper-Folly/folly/MPMCPipeline.h new file mode 100644 index 000000000..a672b2899 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MPMCPipeline.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <utility> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/detail/MPMCPipelineDetail.h> + +namespace folly { + +/** + * Helper tag template to use amplification > 1 + */ +template <class T, size_t Amp> +class MPMCPipelineStage; + +/** + * Multi-Producer, Multi-Consumer pipeline. + * + * A N-stage pipeline is a combination of N+1 MPMC queues (see MPMCQueue.h). + * + * At each stage, you may dequeue the results from the previous stage (possibly + * from multiple threads) and enqueue results to the next stage. Regardless of + * the order of completion, data is delivered to the next stage in the original + * order. Each input is matched with a "ticket" which must be produced + * when enqueueing to the next stage. + * + * A given stage must produce exactly K ("amplification factor", default K=1) + * results for every input. This is enforced by requiring that each ticket + * is used exactly K times. + * + * Usage: + * + * // arguments are queue sizes + * MPMCPipeline<int, std::string, int> pipeline(10, 10, 10); + * + * pipeline.blockingWrite(42); + * + * { + * int val; + * auto ticket = pipeline.blockingReadStage<0>(val); + * pipeline.blockingWriteStage<0>(ticket, folly::to<std::string>(val)); + * } + * + * { + * std::string val; + * auto ticket = pipeline.blockingReadStage<1>(val); + * int ival = 0; + * try { + * ival = folly::to<int>(val); + * } catch (...) { + * // We must produce exactly 1 output even on exception! + * } + * pipeline.blockingWriteStage<1>(ticket, ival); + * } + * + * int result; + * pipeline.blockingRead(result); + * // result == 42 + * + * To specify amplification factors greater than 1, use + * MPMCPipelineStage<T, amplification> instead of T in the declaration: + * + * MPMCPipeline<int, + * MPMCPipelineStage<std::string, 2>, + * MPMCPipelineStage<int, 4>> + * + * declares a two-stage pipeline: the first stage produces 2 strings + * for each input int, the second stage produces 4 ints for each input string, + * so, overall, the pipeline produces 2*4 = 8 ints for each input int. + * + * Implementation details: we use N+1 MPMCQueue objects; each intermediate + * queue connects two adjacent stages. The MPMCQueue implementation is abused; + * instead of using it as a queue, we insert in the output queue at the + * position determined by the input queue's popTicket_. We guarantee that + * all slots are filled (and therefore the queue doesn't freeze) because + * we require that each step produces exactly K outputs for every input. + */ +template <class In, class... Stages> +class MPMCPipeline { + typedef std::tuple<detail::PipelineStageInfo<Stages>...> StageInfos; + typedef std::tuple< + detail::MPMCPipelineStageImpl<In>, + detail::MPMCPipelineStageImpl< + typename detail::PipelineStageInfo<Stages>::value_type>...> + StageTuple; + static constexpr size_t kAmplification = + detail::AmplificationProduct<StageInfos>::value; + + class TicketBaseDebug { + public: + TicketBaseDebug() noexcept : owner_(nullptr), value_(0xdeadbeeffaceb00c) {} + TicketBaseDebug(TicketBaseDebug&& other) noexcept + : owner_(std::exchange(other.owner_, nullptr)), + value_(std::exchange(other.value_, 0xdeadbeeffaceb00c)) {} + explicit TicketBaseDebug(MPMCPipeline* owner, uint64_t value) noexcept + : owner_(owner), value_(value) {} + void check_owner(MPMCPipeline* owner) const { + CHECK(owner == owner_); + } + + MPMCPipeline* owner_; + uint64_t value_; + }; + + class TicketBaseNDebug { + public: + TicketBaseNDebug() = default; + TicketBaseNDebug(TicketBaseNDebug&&) = default; + explicit TicketBaseNDebug(MPMCPipeline*, uint64_t value) noexcept + : value_(value) {} + void check_owner(MPMCPipeline*) const {} + + uint64_t value_; + }; + + using TicketBase = + std::conditional_t<kIsDebug, TicketBaseDebug, TicketBaseNDebug>; + + public: + /** + * Ticket, returned by blockingReadStage, must be given back to + * blockingWriteStage. Tickets are not thread-safe. + */ + template <size_t Stage> + class Ticket : TicketBase { + public: + ~Ticket() noexcept { + CHECK_EQ(remainingUses_, 0) << "All tickets must be completely used!"; + } + + Ticket() noexcept : remainingUses_(0) {} + + Ticket(Ticket&& other) noexcept + : TicketBase(static_cast<TicketBase&&>(other)), + remainingUses_(std::exchange(other.remainingUses_, 0)) {} + + Ticket& operator=(Ticket&& other) noexcept { + if (this != &other) { + this->~Ticket(); + new (this) Ticket(std::move(other)); + } + return *this; + } + + private: + friend class MPMCPipeline; + size_t remainingUses_; + + Ticket(MPMCPipeline* owner, size_t amplification, uint64_t value) noexcept + : TicketBase(owner, value * amplification), + remainingUses_(amplification) {} + + uint64_t use(MPMCPipeline* owner) { + CHECK_GT(remainingUses_--, 0); + TicketBase::check_owner(owner); + return TicketBase::value_++; + } + }; + + /** + * Default-construct pipeline. Useful to move-assign later, + * just like MPMCQueue, see MPMCQueue.h for more details. + */ + MPMCPipeline() = default; + + /** + * Construct a pipeline with N+1 queue sizes. + */ + template <class... Sizes> + explicit MPMCPipeline(Sizes... sizes) : stages_(sizes...) {} + + /** + * Push an element into (the first stage of) the pipeline. Blocking. + */ + template <class... Args> + void blockingWrite(Args&&... args) { + std::get<0>(stages_).blockingWrite(std::forward<Args>(args)...); + } + + /** + * Try to push an element into (the first stage of) the pipeline. + * Non-blocking. + */ + template <class... Args> + bool write(Args&&... args) { + return std::get<0>(stages_).write(std::forward<Args>(args)...); + } + + /** + * Read an element for stage Stage and obtain a ticket. Blocking. + */ + template <size_t Stage> + Ticket<Stage> blockingReadStage( + typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) { + return Ticket<Stage>( + this, + std::tuple_element<Stage, StageInfos>::type::kAmplification, + std::get<Stage>(stages_).blockingRead(elem)); + } + + /** + * Try to read an element for stage Stage and obtain a ticket. + * Non-blocking. + */ + template <size_t Stage> + bool readStage( + Ticket<Stage>& ticket, + typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) { + uint64_t tval; + if (!std::get<Stage>(stages_).readAndGetTicket(tval, elem)) { + return false; + } + ticket = Ticket<Stage>( + this, + std::tuple_element<Stage, StageInfos>::type::kAmplification, + tval); + return true; + } + + /** + * Complete an element in stage Stage (pushing it for stage Stage+1). + * Blocking. + */ + template <size_t Stage, class... Args> + void blockingWriteStage(Ticket<Stage>& ticket, Args&&... args) { + std::get<Stage + 1>(stages_).blockingWriteWithTicket( + ticket.use(this), std::forward<Args>(args)...); + } + + /** + * Pop an element from (the final stage of) the pipeline. Blocking. + */ + void blockingRead(typename std::tuple_element<sizeof...(Stages), StageTuple>:: + type::value_type& elem) { + std::get<sizeof...(Stages)>(stages_).blockingRead(elem); + } + + /** + * Try to pop an element from (the final stage of) the pipeline. + * Non-blocking. + */ + bool read(typename std::tuple_element<sizeof...(Stages), StageTuple>::type:: + value_type& elem) { + return std::get<sizeof...(Stages)>(stages_).read(elem); + } + + /** + * Estimate queue size, measured as values from the last stage. + * (so if the pipeline has an amplification factor > 1, pushing an element + * into the first stage will cause sizeGuess() to be == amplification factor) + * Elements "in flight" (currently processed as part of a stage, so not + * in any queue) are also counted. + */ + ssize_t sizeGuess() const noexcept { + return ssize_t( + std::get<0>(stages_).writeCount() * kAmplification - + std::get<sizeof...(Stages)>(stages_).readCount()); + } + + private: + StageTuple stages_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MPMCQueue.h b/ios/Pods/Flipper-Folly/folly/MPMCQueue.h new file mode 100644 index 000000000..9571b259b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MPMCQueue.h @@ -0,0 +1,1536 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <cassert> +#include <cstring> +#include <limits> +#include <type_traits> + +#include <folly/Traits.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/detail/TurnSequencer.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +namespace detail { + +template <typename T, template <typename> class Atom> +struct SingleElementQueue; + +template <typename T> +class MPMCPipelineStageImpl; + +/// MPMCQueue base CRTP template +template <typename> +class MPMCQueueBase; + +} // namespace detail + +/// MPMCQueue<T> is a high-performance bounded concurrent queue that +/// supports multiple producers, multiple consumers, and optional blocking. +/// The queue has a fixed capacity, for which all memory will be allocated +/// up front. The bulk of the work of enqueuing and dequeuing can be +/// performed in parallel. +/// +/// MPMCQueue is linearizable. That means that if a call to write(A) +/// returns before a call to write(B) begins, then A will definitely end up +/// in the queue before B, and if a call to read(X) returns before a call +/// to read(Y) is started, that X will be something from earlier in the +/// queue than Y. This also means that if a read call returns a value, you +/// can be sure that all previous elements of the queue have been assigned +/// a reader (that reader might not yet have returned, but it exists). +/// +/// The underlying implementation uses a ticket dispenser for the head and +/// the tail, spreading accesses across N single-element queues to produce +/// a queue with capacity N. The ticket dispensers use atomic increment, +/// which is more robust to contention than a CAS loop. Each of the +/// single-element queues uses its own CAS to serialize access, with an +/// adaptive spin cutoff. When spinning fails on a single-element queue +/// it uses futex()'s _BITSET operations to reduce unnecessary wakeups +/// even if multiple waiters are present on an individual queue (such as +/// when the MPMCQueue's capacity is smaller than the number of enqueuers +/// or dequeuers). +/// +/// In benchmarks (contained in tao/queues/ConcurrentQueueTests) +/// it handles 1 to 1, 1 to N, N to 1, and N to M thread counts better +/// than any of the alternatives present in fbcode, for both small (~10) +/// and large capacities. In these benchmarks it is also faster than +/// tbb::concurrent_bounded_queue for all configurations. When there are +/// many more threads than cores, MPMCQueue is _much_ faster than the tbb +/// queue because it uses futex() to block and unblock waiting threads, +/// rather than spinning with sched_yield. +/// +/// NOEXCEPT INTERACTION: tl;dr; If it compiles you're fine. Ticket-based +/// queues separate the assignment of queue positions from the actual +/// construction of the in-queue elements, which means that the T +/// constructor used during enqueue must not throw an exception. This is +/// enforced at compile time using type traits, which requires that T be +/// adorned with accurate noexcept information. If your type does not +/// use noexcept, you will have to wrap it in something that provides +/// the guarantee. We provide an alternate safe implementation for types +/// that don't use noexcept but that are marked folly::IsRelocatable +/// and std::is_nothrow_constructible, which is common for folly types. +/// In particular, if you can declare FOLLY_ASSUME_FBVECTOR_COMPATIBLE +/// then your type can be put in MPMCQueue. +/// +/// If you have a pool of N queue consumers that you want to shut down +/// after the queue has drained, one way is to enqueue N sentinel values +/// to the queue. If the producer doesn't know how many consumers there +/// are you can enqueue one sentinel and then have each consumer requeue +/// two sentinels after it receives it (by requeuing 2 the shutdown can +/// complete in O(log P) time instead of O(P)). +template < + typename T, + template <typename> class Atom = std::atomic, + bool Dynamic = false> +class MPMCQueue : public detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic>> { + friend class detail::MPMCPipelineStageImpl<T>; + using Slot = detail::SingleElementQueue<T, Atom>; + + public: + explicit MPMCQueue(size_t queueCapacity) + : detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic>>(queueCapacity) { + this->stride_ = this->computeStride(queueCapacity); + this->slots_ = new Slot[queueCapacity + 2 * this->kSlotPadding]; + } + + MPMCQueue() noexcept {} +}; + +/// *** The dynamic version of MPMCQueue is deprecated. *** +/// Use UnboundedQueue instead. + +/// The dynamic version of MPMCQueue allows dynamic expansion of queue +/// capacity, such that a queue may start with a smaller capacity than +/// specified and expand only if needed. Users may optionally specify +/// the initial capacity and the expansion multiplier. +/// +/// The design uses a seqlock to enforce mutual exclusion among +/// expansion attempts. Regular operations read up-to-date queue +/// information (slots array, capacity, stride) inside read-only +/// seqlock sections, which are unimpeded when no expansion is in +/// progress. +/// +/// An expansion computes a new capacity, allocates a new slots array, +/// and updates stride. No information needs to be copied from the +/// current slots array to the new one. When this happens, new slots +/// will not have sequence numbers that match ticket numbers. The +/// expansion needs to compute a ticket offset such that operations +/// that use new arrays can adjust the calculations of slot indexes +/// and sequence numbers that take into account that the new slots +/// start with sequence numbers of zero. The current ticket offset is +/// packed with the seqlock in an atomic 64-bit integer. The initial +/// offset is zero. +/// +/// Lagging write and read operations with tickets lower than the +/// ticket offset of the current slots array (i.e., the minimum ticket +/// number that can be served by the current array) must use earlier +/// closed arrays instead of the current one. Information about closed +/// slots arrays (array address, capacity, stride, and offset) is +/// maintained in a logarithmic-sized structure. Each entry in that +/// structure never needs to be changed once set. The number of closed +/// arrays is half the value of the seqlock (when unlocked). +/// +/// The acquisition of the seqlock to perform an expansion does not +/// prevent the issuing of new push and pop tickets concurrently. The +/// expansion must set the new ticket offset to a value that couldn't +/// have been issued to an operation that has already gone through a +/// seqlock read-only section (and hence obtained information for +/// older closed arrays). +/// +/// Note that the total queue capacity can temporarily exceed the +/// specified capacity when there are lagging consumers that haven't +/// yet consumed all the elements in closed arrays. Users should not +/// rely on the capacity of dynamic queues for synchronization, e.g., +/// they should not expect that a thread will definitely block on a +/// call to blockingWrite() when the queue size is known to be equal +/// to its capacity. +/// +/// Note that some writeIfNotFull() and tryWriteUntil() operations may +/// fail even if the size of the queue is less than its maximum +/// capacity and despite the success of expansion, if the operation +/// happens to acquire a ticket that belongs to a closed array. This +/// is a transient condition. Typically, one or two ticket values may +/// be subject to such condition per expansion. +/// +/// The dynamic version is a partial specialization of MPMCQueue with +/// Dynamic == true +template <typename T, template <typename> class Atom> +class MPMCQueue<T, Atom, true> + : public detail::MPMCQueueBase<MPMCQueue<T, Atom, true>> { + friend class detail::MPMCQueueBase<MPMCQueue<T, Atom, true>>; + using Slot = detail::SingleElementQueue<T, Atom>; + + struct ClosedArray { + uint64_t offset_{0}; + Slot* slots_{nullptr}; + size_t capacity_{0}; + int stride_{0}; + }; + + public: + explicit MPMCQueue(size_t queueCapacity) + : detail::MPMCQueueBase<MPMCQueue<T, Atom, true>>(queueCapacity) { + size_t cap = std::min<size_t>(kDefaultMinDynamicCapacity, queueCapacity); + initQueue(cap, kDefaultExpansionMultiplier); + } + + explicit MPMCQueue( + size_t queueCapacity, + size_t minCapacity, + size_t expansionMultiplier) + : detail::MPMCQueueBase<MPMCQueue<T, Atom, true>>(queueCapacity) { + minCapacity = std::max<size_t>(1, minCapacity); + size_t cap = std::min<size_t>(minCapacity, queueCapacity); + expansionMultiplier = std::max<size_t>(2, expansionMultiplier); + initQueue(cap, expansionMultiplier); + } + + MPMCQueue() noexcept { + dmult_ = 0; + closed_ = nullptr; + } + + MPMCQueue(MPMCQueue<T, Atom, true>&& rhs) noexcept { + this->capacity_ = rhs.capacity_; + new (&this->dslots_) + Atom<Slot*>(rhs.dslots_.load(std::memory_order_relaxed)); + new (&this->dstride_) + Atom<int>(rhs.dstride_.load(std::memory_order_relaxed)); + this->dstate_.store( + rhs.dstate_.load(std::memory_order_relaxed), std::memory_order_relaxed); + this->dcapacity_.store( + rhs.dcapacity_.load(std::memory_order_relaxed), + std::memory_order_relaxed); + this->pushTicket_.store( + rhs.pushTicket_.load(std::memory_order_relaxed), + std::memory_order_relaxed); + this->popTicket_.store( + rhs.popTicket_.load(std::memory_order_relaxed), + std::memory_order_relaxed); + this->pushSpinCutoff_.store( + rhs.pushSpinCutoff_.load(std::memory_order_relaxed), + std::memory_order_relaxed); + this->popSpinCutoff_.store( + rhs.popSpinCutoff_.load(std::memory_order_relaxed), + std::memory_order_relaxed); + dmult_ = rhs.dmult_; + closed_ = rhs.closed_; + + rhs.capacity_ = 0; + rhs.dslots_.store(nullptr, std::memory_order_relaxed); + rhs.dstride_.store(0, std::memory_order_relaxed); + rhs.dstate_.store(0, std::memory_order_relaxed); + rhs.dcapacity_.store(0, std::memory_order_relaxed); + rhs.pushTicket_.store(0, std::memory_order_relaxed); + rhs.popTicket_.store(0, std::memory_order_relaxed); + rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed); + rhs.popSpinCutoff_.store(0, std::memory_order_relaxed); + rhs.dmult_ = 0; + rhs.closed_ = nullptr; + } + + MPMCQueue<T, Atom, true> const& operator=(MPMCQueue<T, Atom, true>&& rhs) { + if (this != &rhs) { + this->~MPMCQueue(); + new (this) MPMCQueue(std::move(rhs)); + } + return *this; + } + + ~MPMCQueue() { + if (closed_ != nullptr) { + for (int i = getNumClosed(this->dstate_.load()) - 1; i >= 0; --i) { + delete[] closed_[i].slots_; + } + delete[] closed_; + } + using AtomInt = Atom<int>; + this->dstride_.~AtomInt(); + using AtomSlot = Atom<Slot*>; + // Sort of a hack to get ~MPMCQueueBase to free dslots_ + auto slots = this->dslots_.load(); + this->dslots_.~AtomSlot(); + this->slots_ = slots; + } + + size_t allocatedCapacity() const noexcept { + return this->dcapacity_.load(std::memory_order_relaxed); + } + + template <typename... Args> + void blockingWrite(Args&&... args) noexcept { + uint64_t ticket = this->pushTicket_++; + Slot* slots; + size_t cap; + int stride; + uint64_t state; + uint64_t offset; + do { + if (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + continue; + } + if (maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride)) { + // There was an expansion after this ticket was issued. + break; + } + if (slots[this->idx((ticket - offset), cap, stride)].mayEnqueue( + this->turn(ticket - offset, cap))) { + // A slot is ready. No need to expand. + break; + } else if ( + this->popTicket_.load(std::memory_order_relaxed) + cap > ticket) { + // May block, but a pop is in progress. No need to expand. + // Get seqlock read section info again in case an expansion + // occurred with an equal or higher ticket. + continue; + } else { + // May block. See if we can expand. + if (tryExpand(state, cap)) { + // This or another thread started an expansion. Get updated info. + continue; + } else { + // Can't expand. + break; + } + } + } while (true); + this->enqueueWithTicketBase( + ticket - offset, slots, cap, stride, std::forward<Args>(args)...); + } + + void blockingReadWithTicket(uint64_t& ticket, T& elem) noexcept { + ticket = this->popTicket_++; + Slot* slots; + size_t cap; + int stride; + uint64_t state; + uint64_t offset; + while (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + } + // If there was an expansion after the corresponding push ticket + // was issued, adjust accordingly + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + this->dequeueWithTicketBase(ticket - offset, slots, cap, stride, elem); + } + + private: + enum { + kSeqlockBits = 6, + kDefaultMinDynamicCapacity = 10, + kDefaultExpansionMultiplier = 10, + }; + + size_t dmult_; + + // Info about closed slots arrays for use by lagging operations + ClosedArray* closed_; + + void initQueue(const size_t cap, const size_t mult) { + new (&this->dstride_) Atom<int>(this->computeStride(cap)); + Slot* slots = new Slot[cap + 2 * this->kSlotPadding]; + new (&this->dslots_) Atom<Slot*>(slots); + this->dstate_.store(0); + this->dcapacity_.store(cap); + dmult_ = mult; + size_t maxClosed = 0; + for (size_t expanded = cap; expanded < this->capacity_; expanded *= mult) { + ++maxClosed; + } + closed_ = (maxClosed > 0) ? new ClosedArray[maxClosed] : nullptr; + } + + bool tryObtainReadyPushTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + uint64_t state; + do { + ticket = this->pushTicket_.load(std::memory_order_acquire); // A + if (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + continue; + } + + // If there was an expansion with offset greater than this ticket, + // adjust accordingly + uint64_t offset; + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + + if (slots[this->idx((ticket - offset), cap, stride)].mayEnqueue( + this->turn(ticket - offset, cap))) { + // A slot is ready. + if (this->pushTicket_.compare_exchange_strong(ticket, ticket + 1)) { + // Adjust ticket + ticket -= offset; + return true; + } else { + continue; + } + } else { + if (ticket != this->pushTicket_.load(std::memory_order_relaxed)) { // B + // Try again. Ticket changed. + continue; + } + // Likely to block. + // Try to expand unless the ticket is for a closed array + if (offset == getOffset(state)) { + if (tryExpand(state, cap)) { + // This or another thread started an expansion. Get up-to-date info. + continue; + } + } + return false; + } + } while (true); + } + + bool tryObtainPromisedPushTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + uint64_t state; + do { + ticket = this->pushTicket_.load(std::memory_order_acquire); + auto numPops = this->popTicket_.load(std::memory_order_acquire); + if (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + continue; + } + + const auto curCap = cap; + // If there was an expansion with offset greater than this ticket, + // adjust accordingly + uint64_t offset; + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + + int64_t n = ticket - numPops; + + if (n >= static_cast<ssize_t>(cap)) { + if ((cap == curCap) && tryExpand(state, cap)) { + // This or another thread started an expansion. Start over. + continue; + } + // Can't expand. + ticket -= offset; + return false; + } + + if (this->pushTicket_.compare_exchange_strong(ticket, ticket + 1)) { + // Adjust ticket + ticket -= offset; + return true; + } + } while (true); + } + + bool tryObtainReadyPopTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + uint64_t state; + do { + ticket = this->popTicket_.load(std::memory_order_relaxed); + if (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + continue; + } + + // If there was an expansion after the corresponding push ticket + // was issued, adjust accordingly + uint64_t offset; + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + + if (slots[this->idx((ticket - offset), cap, stride)].mayDequeue( + this->turn(ticket - offset, cap))) { + if (this->popTicket_.compare_exchange_strong(ticket, ticket + 1)) { + // Adjust ticket + ticket -= offset; + return true; + } + } else { + return false; + } + } while (true); + } + + bool tryObtainPromisedPopTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + uint64_t state; + do { + ticket = this->popTicket_.load(std::memory_order_acquire); + auto numPushes = this->pushTicket_.load(std::memory_order_acquire); + if (!trySeqlockReadSection(state, slots, cap, stride)) { + asm_volatile_pause(); + continue; + } + + uint64_t offset; + // If there was an expansion after the corresponding push + // ticket was issued, adjust accordingly + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + + if (ticket >= numPushes) { + ticket -= offset; + return false; + } + if (this->popTicket_.compare_exchange_strong(ticket, ticket + 1)) { + ticket -= offset; + return true; + } + } while (true); + } + + /// Enqueues an element with a specific ticket number + template <typename... Args> + void enqueueWithTicket(const uint64_t ticket, Args&&... args) noexcept { + Slot* slots; + size_t cap; + int stride; + uint64_t state; + uint64_t offset; + + while (!trySeqlockReadSection(state, slots, cap, stride)) { + } + + // If there was an expansion after this ticket was issued, adjust + // accordingly + maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride); + + this->enqueueWithTicketBase( + ticket - offset, slots, cap, stride, std::forward<Args>(args)...); + } + + uint64_t getOffset(const uint64_t state) const noexcept { + return state >> kSeqlockBits; + } + + int getNumClosed(const uint64_t state) const noexcept { + return (state & ((1 << kSeqlockBits) - 1)) >> 1; + } + + /// Try to expand the queue. Returns true if this expansion was + /// successful or a concurent expansion is in progress. Returns + /// false if the queue has reached its maximum capacity or + /// allocation has failed. + bool tryExpand(const uint64_t state, const size_t cap) noexcept { + if (cap == this->capacity_) { + return false; + } + // Acquire seqlock + uint64_t oldval = state; + assert((state & 1) == 0); + if (this->dstate_.compare_exchange_strong(oldval, state + 1)) { + assert(cap == this->dcapacity_.load()); + uint64_t ticket = + 1 + std::max(this->pushTicket_.load(), this->popTicket_.load()); + size_t newCapacity = std::min(dmult_ * cap, this->capacity_); + Slot* newSlots = + new (std::nothrow) Slot[newCapacity + 2 * this->kSlotPadding]; + if (newSlots == nullptr) { + // Expansion failed. Restore the seqlock + this->dstate_.store(state); + return false; + } + // Successful expansion + // calculate the current ticket offset + uint64_t offset = getOffset(state); + // calculate index in closed array + int index = getNumClosed(state); + assert((index << 1) < (1 << kSeqlockBits)); + // fill the info for the closed slots array + closed_[index].offset_ = offset; + closed_[index].slots_ = this->dslots_.load(); + closed_[index].capacity_ = cap; + closed_[index].stride_ = this->dstride_.load(); + // update the new slots array info + this->dslots_.store(newSlots); + this->dcapacity_.store(newCapacity); + this->dstride_.store(this->computeStride(newCapacity)); + // Release the seqlock and record the new ticket offset + this->dstate_.store((ticket << kSeqlockBits) + (2 * (index + 1))); + return true; + } else { // failed to acquire seqlock + // Someone acaquired the seqlock. Go back to the caller and get + // up-to-date info. + return true; + } + } + + /// Seqlock read-only section + bool trySeqlockReadSection( + uint64_t& state, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + state = this->dstate_.load(std::memory_order_acquire); + if (state & 1) { + // Locked. + return false; + } + // Start read-only section. + slots = this->dslots_.load(std::memory_order_relaxed); + cap = this->dcapacity_.load(std::memory_order_relaxed); + stride = this->dstride_.load(std::memory_order_relaxed); + // End of read-only section. Validate seqlock. + std::atomic_thread_fence(std::memory_order_acquire); + return (state == this->dstate_.load(std::memory_order_relaxed)); + } + + /// If there was an expansion after ticket was issued, update local variables + /// of the lagging operation using the most recent closed array with + /// offset <= ticket and return true. Otherwise, return false; + bool maybeUpdateFromClosed( + const uint64_t state, + const uint64_t ticket, + uint64_t& offset, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + offset = getOffset(state); + if (ticket >= offset) { + return false; + } + for (int i = getNumClosed(state) - 1; i >= 0; --i) { + offset = closed_[i].offset_; + if (offset <= ticket) { + slots = closed_[i].slots_; + cap = closed_[i].capacity_; + stride = closed_[i].stride_; + return true; + } + } + // A closed array with offset <= ticket should have been found + assert(false); + return false; + } +}; + +namespace detail { + +/// CRTP specialization of MPMCQueueBase +template < + template <typename T, template <typename> class Atom, bool Dynamic> + class Derived, + typename T, + template <typename> class Atom, + bool Dynamic> +class MPMCQueueBase<Derived<T, Atom, Dynamic>> { + // Note: Using CRTP static casts in several functions of this base + // template instead of making called functions virtual or duplicating + // the code of calling functions in the derived partially specialized + // template + + static_assert( + std::is_nothrow_constructible<T, T&&>::value || + folly::IsRelocatable<T>::value, + "T must be relocatable or have a noexcept move constructor"); + + public: + typedef T value_type; + + using Slot = detail::SingleElementQueue<T, Atom>; + + explicit MPMCQueueBase(size_t queueCapacity) + : capacity_(queueCapacity), + dstate_(0), + dcapacity_(0), + pushTicket_(0), + popTicket_(0), + pushSpinCutoff_(0), + popSpinCutoff_(0) { + if (queueCapacity == 0) { + throw std::invalid_argument( + "MPMCQueue with explicit capacity 0 is impossible" + // Stride computation in derived classes would sigfpe if capacity is 0 + ); + } + + // ideally this would be a static assert, but g++ doesn't allow it + assert( + alignof(MPMCQueue<T, Atom>) >= hardware_destructive_interference_size); + assert( + static_cast<uint8_t*>(static_cast<void*>(&popTicket_)) - + static_cast<uint8_t*>(static_cast<void*>(&pushTicket_)) >= + static_cast<ptrdiff_t>(hardware_destructive_interference_size)); + } + + /// A default-constructed queue is useful because a usable (non-zero + /// capacity) queue can be moved onto it or swapped with it + MPMCQueueBase() noexcept + : capacity_(0), + slots_(nullptr), + stride_(0), + dstate_(0), + dcapacity_(0), + pushTicket_(0), + popTicket_(0), + pushSpinCutoff_(0), + popSpinCutoff_(0) {} + + /// IMPORTANT: The move constructor is here to make it easier to perform + /// the initialization phase, it is not safe to use when there are any + /// concurrent accesses (this is not checked). + MPMCQueueBase(MPMCQueueBase<Derived<T, Atom, Dynamic>>&& rhs) noexcept + : capacity_(rhs.capacity_), + slots_(rhs.slots_), + stride_(rhs.stride_), + dstate_(rhs.dstate_.load(std::memory_order_relaxed)), + dcapacity_(rhs.dcapacity_.load(std::memory_order_relaxed)), + pushTicket_(rhs.pushTicket_.load(std::memory_order_relaxed)), + popTicket_(rhs.popTicket_.load(std::memory_order_relaxed)), + pushSpinCutoff_(rhs.pushSpinCutoff_.load(std::memory_order_relaxed)), + popSpinCutoff_(rhs.popSpinCutoff_.load(std::memory_order_relaxed)) { + // relaxed ops are okay for the previous reads, since rhs queue can't + // be in concurrent use + + // zero out rhs + rhs.capacity_ = 0; + rhs.slots_ = nullptr; + rhs.stride_ = 0; + rhs.dstate_.store(0, std::memory_order_relaxed); + rhs.dcapacity_.store(0, std::memory_order_relaxed); + rhs.pushTicket_.store(0, std::memory_order_relaxed); + rhs.popTicket_.store(0, std::memory_order_relaxed); + rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed); + rhs.popSpinCutoff_.store(0, std::memory_order_relaxed); + } + + /// IMPORTANT: The move operator is here to make it easier to perform + /// the initialization phase, it is not safe to use when there are any + /// concurrent accesses (this is not checked). + MPMCQueueBase<Derived<T, Atom, Dynamic>> const& operator=( + MPMCQueueBase<Derived<T, Atom, Dynamic>>&& rhs) { + if (this != &rhs) { + this->~MPMCQueueBase(); + new (this) MPMCQueueBase(std::move(rhs)); + } + return *this; + } + + MPMCQueueBase(const MPMCQueueBase&) = delete; + MPMCQueueBase& operator=(const MPMCQueueBase&) = delete; + + /// MPMCQueue can only be safely destroyed when there are no + /// pending enqueuers or dequeuers (this is not checked). + ~MPMCQueueBase() { + delete[] slots_; + } + + /// Returns the number of writes (including threads that are blocked waiting + /// to write) minus the number of reads (including threads that are blocked + /// waiting to read). So effectively, it becomes: + /// elements in queue + pending(calls to write) - pending(calls to read). + /// If nothing is pending, then the method returns the actual number of + /// elements in the queue. + /// The returned value can be negative if there are no writers and the queue + /// is empty, but there is one reader that is blocked waiting to read (in + /// which case, the returned size will be -1). + ssize_t size() const noexcept { + // since both pushes and pops increase monotonically, we can get a + // consistent snapshot either by bracketing a read of popTicket_ with + // two reads of pushTicket_ that return the same value, or the other + // way around. We maximize our chances by alternately attempting + // both bracketings. + uint64_t pushes = pushTicket_.load(std::memory_order_acquire); // A + uint64_t pops = popTicket_.load(std::memory_order_acquire); // B + while (true) { + uint64_t nextPushes = pushTicket_.load(std::memory_order_acquire); // C + if (pushes == nextPushes) { + // pushTicket_ didn't change from A (or the previous C) to C, + // so we can linearize at B (or D) + return ssize_t(pushes - pops); + } + pushes = nextPushes; + uint64_t nextPops = popTicket_.load(std::memory_order_acquire); // D + if (pops == nextPops) { + // popTicket_ didn't chance from B (or the previous D), so we + // can linearize at C + return ssize_t(pushes - pops); + } + pops = nextPops; + } + } + + /// Returns true if there are no items available for dequeue + bool isEmpty() const noexcept { + return size() <= 0; + } + + /// Returns true if there is currently no empty space to enqueue + bool isFull() const noexcept { + // careful with signed -> unsigned promotion, since size can be negative + return size() >= static_cast<ssize_t>(capacity_); + } + + /// Returns is a guess at size() for contexts that don't need a precise + /// value, such as stats. More specifically, it returns the number of writes + /// minus the number of reads, but after reading the number of writes, more + /// writers could have came before the number of reads was sampled, + /// and this method doesn't protect against such case. + /// The returned value can be negative. + ssize_t sizeGuess() const noexcept { + return writeCount() - readCount(); + } + + /// Doesn't change + size_t capacity() const noexcept { + return capacity_; + } + + /// Doesn't change for non-dynamic + size_t allocatedCapacity() const noexcept { + return capacity_; + } + + /// Returns the total number of calls to blockingWrite or successful + /// calls to write, including those blockingWrite calls that are + /// currently blocking + uint64_t writeCount() const noexcept { + return pushTicket_.load(std::memory_order_acquire); + } + + /// Returns the total number of calls to blockingRead or successful + /// calls to read, including those blockingRead calls that are currently + /// blocking + uint64_t readCount() const noexcept { + return popTicket_.load(std::memory_order_acquire); + } + + /// Enqueues a T constructed from args, blocking until space is + /// available. Note that this method signature allows enqueue via + /// move, if args is a T rvalue, via copy, if args is a T lvalue, or + /// via emplacement if args is an initializer list that can be passed + /// to a T constructor. + template <typename... Args> + void blockingWrite(Args&&... args) noexcept { + enqueueWithTicketBase( + pushTicket_++, slots_, capacity_, stride_, std::forward<Args>(args)...); + } + + /// If an item can be enqueued with no blocking, does so and returns + /// true, otherwise returns false. This method is similar to + /// writeIfNotFull, but if you don't have a specific need for that + /// method you should use this one. + /// + /// One of the common usages of this method is to enqueue via the + /// move constructor, something like q.write(std::move(x)). If write + /// returns false because the queue is full then x has not actually been + /// consumed, which looks strange. To understand why it is actually okay + /// to use x afterward, remember that std::move is just a typecast that + /// provides an rvalue reference that enables use of a move constructor + /// or operator. std::move doesn't actually move anything. It could + /// more accurately be called std::rvalue_cast or std::move_permission. + template <typename... Args> + bool write(Args&&... args) noexcept { + uint64_t ticket; + Slot* slots; + size_t cap; + int stride; + if (static_cast<Derived<T, Atom, Dynamic>*>(this)->tryObtainReadyPushTicket( + ticket, slots, cap, stride)) { + // we have pre-validated that the ticket won't block + enqueueWithTicketBase( + ticket, slots, cap, stride, std::forward<Args>(args)...); + return true; + } else { + return false; + } + } + + template <class Clock, typename... Args> + bool tryWriteUntil( + const std::chrono::time_point<Clock>& when, + Args&&... args) noexcept { + uint64_t ticket; + Slot* slots; + size_t cap; + int stride; + if (tryObtainPromisedPushTicketUntil(ticket, slots, cap, stride, when)) { + // we have pre-validated that the ticket won't block, or rather that + // it won't block longer than it takes another thread to dequeue an + // element from the slot it identifies. + enqueueWithTicketBase( + ticket, slots, cap, stride, std::forward<Args>(args)...); + return true; + } else { + return false; + } + } + + /// If the queue is not full, enqueues and returns true, otherwise + /// returns false. Unlike write this method can be blocked by another + /// thread, specifically a read that has linearized (been assigned + /// a ticket) but not yet completed. If you don't really need this + /// function you should probably use write. + /// + /// MPMCQueue isn't lock-free, so just because a read operation has + /// linearized (and isFull is false) doesn't mean that space has been + /// made available for another write. In this situation write will + /// return false, but writeIfNotFull will wait for the dequeue to finish. + /// This method is required if you are composing queues and managing + /// your own wakeup, because it guarantees that after every successful + /// write a readIfNotEmpty will succeed. + template <typename... Args> + bool writeIfNotFull(Args&&... args) noexcept { + uint64_t ticket; + Slot* slots; + size_t cap; + int stride; + if (static_cast<Derived<T, Atom, Dynamic>*>(this) + ->tryObtainPromisedPushTicket(ticket, slots, cap, stride)) { + // some other thread is already dequeuing the slot into which we + // are going to enqueue, but we might have to wait for them to finish + enqueueWithTicketBase( + ticket, slots, cap, stride, std::forward<Args>(args)...); + return true; + } else { + return false; + } + } + + /// Moves a dequeued element onto elem, blocking until an element + /// is available + void blockingRead(T& elem) noexcept { + uint64_t ticket; + static_cast<Derived<T, Atom, Dynamic>*>(this)->blockingReadWithTicket( + ticket, elem); + } + + /// Same as blockingRead() but also records the ticket nunmer + void blockingReadWithTicket(uint64_t& ticket, T& elem) noexcept { + assert(capacity_ != 0); + ticket = popTicket_++; + dequeueWithTicketBase(ticket, slots_, capacity_, stride_, elem); + } + + /// If an item can be dequeued with no blocking, does so and returns + /// true, otherwise returns false. + bool read(T& elem) noexcept { + uint64_t ticket; + return readAndGetTicket(ticket, elem); + } + + /// Same as read() but also records the ticket nunmer + bool readAndGetTicket(uint64_t& ticket, T& elem) noexcept { + Slot* slots; + size_t cap; + int stride; + if (static_cast<Derived<T, Atom, Dynamic>*>(this)->tryObtainReadyPopTicket( + ticket, slots, cap, stride)) { + // the ticket has been pre-validated to not block + dequeueWithTicketBase(ticket, slots, cap, stride, elem); + return true; + } else { + return false; + } + } + + template <class Clock, typename... Args> + bool tryReadUntil( + const std::chrono::time_point<Clock>& when, + T& elem) noexcept { + uint64_t ticket; + Slot* slots; + size_t cap; + int stride; + if (tryObtainPromisedPopTicketUntil(ticket, slots, cap, stride, when)) { + // we have pre-validated that the ticket won't block, or rather that + // it won't block longer than it takes another thread to enqueue an + // element on the slot it identifies. + dequeueWithTicketBase(ticket, slots, cap, stride, elem); + return true; + } else { + return false; + } + } + + /// If the queue is not empty, dequeues and returns true, otherwise + /// returns false. If the matching write is still in progress then this + /// method may block waiting for it. If you don't rely on being able + /// to dequeue (such as by counting completed write) then you should + /// prefer read. + bool readIfNotEmpty(T& elem) noexcept { + uint64_t ticket; + Slot* slots; + size_t cap; + int stride; + if (static_cast<Derived<T, Atom, Dynamic>*>(this) + ->tryObtainPromisedPopTicket(ticket, slots, cap, stride)) { + // the matching enqueue already has a ticket, but might not be done + dequeueWithTicketBase(ticket, slots, cap, stride, elem); + return true; + } else { + return false; + } + } + + protected: + enum { + /// Once every kAdaptationFreq we will spin longer, to try to estimate + /// the proper spin backoff + kAdaptationFreq = 128, + + /// To avoid false sharing in slots_ with neighboring memory + /// allocations, we pad it with this many SingleElementQueue-s at + /// each end + kSlotPadding = + (hardware_destructive_interference_size - 1) / sizeof(Slot) + 1 + }; + + /// The maximum number of items in the queue at once + alignas(hardware_destructive_interference_size) size_t capacity_; + + /// Anonymous union for use when Dynamic = false and true, respectively + union { + /// An array of capacity_ SingleElementQueue-s, each of which holds + /// either 0 or 1 item. We over-allocate by 2 * kSlotPadding and don't + /// touch the slots at either end, to avoid false sharing + Slot* slots_; + /// Current dynamic slots array of dcapacity_ SingleElementQueue-s + Atom<Slot*> dslots_; + }; + + /// Anonymous union for use when Dynamic = false and true, respectively + union { + /// The number of slots_ indices that we advance for each ticket, to + /// avoid false sharing. Ideally slots_[i] and slots_[i + stride_] + /// aren't on the same cache line + int stride_; + /// Current stride + Atom<int> dstride_; + }; + + /// The following two memebers are used by dynamic MPMCQueue. + /// Ideally they should be in MPMCQueue<T,Atom,true>, but we get + /// better cache locality if they are in the same cache line as + /// dslots_ and dstride_. + /// + /// Dynamic state. A packed seqlock and ticket offset + Atom<uint64_t> dstate_; + /// Dynamic capacity + Atom<size_t> dcapacity_; + + /// Enqueuers get tickets from here + alignas(hardware_destructive_interference_size) Atom<uint64_t> pushTicket_; + + /// Dequeuers get tickets from here + alignas(hardware_destructive_interference_size) Atom<uint64_t> popTicket_; + + /// This is how many times we will spin before using FUTEX_WAIT when + /// the queue is full on enqueue, adaptively computed by occasionally + /// spinning for longer and smoothing with an exponential moving average + alignas( + hardware_destructive_interference_size) Atom<uint32_t> pushSpinCutoff_; + + /// The adaptive spin cutoff when the queue is empty on dequeue + alignas(hardware_destructive_interference_size) Atom<uint32_t> popSpinCutoff_; + + /// Alignment doesn't prevent false sharing at the end of the struct, + /// so fill out the last cache line + char pad_[hardware_destructive_interference_size - sizeof(Atom<uint32_t>)]; + + /// We assign tickets in increasing order, but we don't want to + /// access neighboring elements of slots_ because that will lead to + /// false sharing (multiple cores accessing the same cache line even + /// though they aren't accessing the same bytes in that cache line). + /// To avoid this we advance by stride slots per ticket. + /// + /// We need gcd(capacity, stride) to be 1 so that we will use all + /// of the slots. We ensure this by only considering prime strides, + /// which either have no common divisors with capacity or else have + /// a zero remainder after dividing by capacity. That is sufficient + /// to guarantee correctness, but we also want to actually spread the + /// accesses away from each other to avoid false sharing (consider a + /// stride of 7 with a capacity of 8). To that end we try a few taking + /// care to observe that advancing by -1 is as bad as advancing by 1 + /// when in comes to false sharing. + /// + /// The simple way to avoid false sharing would be to pad each + /// SingleElementQueue, but since we have capacity_ of them that could + /// waste a lot of space. + static int computeStride(size_t capacity) noexcept { + static const int smallPrimes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; + + int bestStride = 1; + size_t bestSep = 1; + for (int stride : smallPrimes) { + if ((stride % capacity) == 0 || (capacity % stride) == 0) { + continue; + } + size_t sep = stride % capacity; + sep = std::min(sep, capacity - sep); + if (sep > bestSep) { + bestStride = stride; + bestSep = sep; + } + } + return bestStride; + } + + /// Returns the index into slots_ that should be used when enqueuing or + /// dequeuing with the specified ticket + size_t idx(uint64_t ticket, size_t cap, int stride) noexcept { + return ((ticket * stride) % cap) + kSlotPadding; + } + + /// Maps an enqueue or dequeue ticket to the turn should be used at the + /// corresponding SingleElementQueue + uint32_t turn(uint64_t ticket, size_t cap) noexcept { + assert(cap != 0); + return uint32_t(ticket / cap); + } + + /// Tries to obtain a push ticket for which SingleElementQueue::enqueue + /// won't block. Returns true on immediate success, false on immediate + /// failure. + bool tryObtainReadyPushTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + ticket = pushTicket_.load(std::memory_order_acquire); // A + slots = slots_; + cap = capacity_; + stride = stride_; + while (true) { + if (!slots[idx(ticket, cap, stride)].mayEnqueue(turn(ticket, cap))) { + // if we call enqueue(ticket, ...) on the SingleElementQueue + // right now it would block, but this might no longer be the next + // ticket. We can increase the chance of tryEnqueue success under + // contention (without blocking) by rechecking the ticket dispenser + auto prev = ticket; + ticket = pushTicket_.load(std::memory_order_acquire); // B + if (prev == ticket) { + // mayEnqueue was bracketed by two reads (A or prev B or prev + // failing CAS to B), so we are definitely unable to enqueue + return false; + } + } else { + // we will bracket the mayEnqueue check with a read (A or prev B + // or prev failing CAS) and the following CAS. If the CAS fails + // it will effect a load of pushTicket_ + if (pushTicket_.compare_exchange_strong(ticket, ticket + 1)) { + return true; + } + } + } + } + + /// Tries until when to obtain a push ticket for which + /// SingleElementQueue::enqueue won't block. Returns true on success, false + /// on failure. + /// ticket is filled on success AND failure. + template <class Clock> + bool tryObtainPromisedPushTicketUntil( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride, + const std::chrono::time_point<Clock>& when) noexcept { + bool deadlineReached = false; + while (!deadlineReached) { + if (static_cast<Derived<T, Atom, Dynamic>*>(this) + ->tryObtainPromisedPushTicket(ticket, slots, cap, stride)) { + return true; + } + // ticket is a blocking ticket until the preceding ticket has been + // processed: wait until this ticket's turn arrives. We have not reserved + // this ticket so we will have to re-attempt to get a non-blocking ticket + // if we wake up before we time-out. + deadlineReached = + !slots[idx(ticket, cap, stride)].tryWaitForEnqueueTurnUntil( + turn(ticket, cap), + pushSpinCutoff_, + (ticket % kAdaptationFreq) == 0, + when); + } + return false; + } + + /// Tries to obtain a push ticket which can be satisfied if all + /// in-progress pops complete. This function does not block, but + /// blocking may be required when using the returned ticket if some + /// other thread's pop is still in progress (ticket has been granted but + /// pop has not yet completed). + bool tryObtainPromisedPushTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + auto numPushes = pushTicket_.load(std::memory_order_acquire); // A + slots = slots_; + cap = capacity_; + stride = stride_; + while (true) { + ticket = numPushes; + const auto numPops = popTicket_.load(std::memory_order_acquire); // B + // n will be negative if pops are pending + const int64_t n = int64_t(numPushes - numPops); + if (n >= static_cast<ssize_t>(capacity_)) { + // Full, linearize at B. We don't need to recheck the read we + // performed at A, because if numPushes was stale at B then the + // real numPushes value is even worse + return false; + } + if (pushTicket_.compare_exchange_strong(numPushes, numPushes + 1)) { + return true; + } + } + } + + /// Tries to obtain a pop ticket for which SingleElementQueue::dequeue + /// won't block. Returns true on immediate success, false on immediate + /// failure. + bool tryObtainReadyPopTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + ticket = popTicket_.load(std::memory_order_acquire); + slots = slots_; + cap = capacity_; + stride = stride_; + while (true) { + if (!slots[idx(ticket, cap, stride)].mayDequeue(turn(ticket, cap))) { + auto prev = ticket; + ticket = popTicket_.load(std::memory_order_acquire); + if (prev == ticket) { + return false; + } + } else { + if (popTicket_.compare_exchange_strong(ticket, ticket + 1)) { + return true; + } + } + } + } + + /// Tries until when to obtain a pop ticket for which + /// SingleElementQueue::dequeue won't block. Returns true on success, false + /// on failure. + /// ticket is filled on success AND failure. + template <class Clock> + bool tryObtainPromisedPopTicketUntil( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride, + const std::chrono::time_point<Clock>& when) noexcept { + bool deadlineReached = false; + while (!deadlineReached) { + if (static_cast<Derived<T, Atom, Dynamic>*>(this) + ->tryObtainPromisedPopTicket(ticket, slots, cap, stride)) { + return true; + } + // ticket is a blocking ticket until the preceding ticket has been + // processed: wait until this ticket's turn arrives. We have not reserved + // this ticket so we will have to re-attempt to get a non-blocking ticket + // if we wake up before we time-out. + deadlineReached = + !slots[idx(ticket, cap, stride)].tryWaitForDequeueTurnUntil( + turn(ticket, cap), + pushSpinCutoff_, + (ticket % kAdaptationFreq) == 0, + when); + } + return false; + } + + /// Similar to tryObtainReadyPopTicket, but returns a pop ticket whose + /// corresponding push ticket has already been handed out, rather than + /// returning one whose corresponding push ticket has already been + /// completed. This means that there is a possibility that the caller + /// will block when using the ticket, but it allows the user to rely on + /// the fact that if enqueue has succeeded, tryObtainPromisedPopTicket + /// will return true. The "try" part of this is that we won't have + /// to block waiting for someone to call enqueue, although we might + /// have to block waiting for them to finish executing code inside the + /// MPMCQueue itself. + bool tryObtainPromisedPopTicket( + uint64_t& ticket, + Slot*& slots, + size_t& cap, + int& stride) noexcept { + auto numPops = popTicket_.load(std::memory_order_acquire); // A + slots = slots_; + cap = capacity_; + stride = stride_; + while (true) { + ticket = numPops; + const auto numPushes = pushTicket_.load(std::memory_order_acquire); // B + if (numPops >= numPushes) { + // Empty, or empty with pending pops. Linearize at B. We don't + // need to recheck the read we performed at A, because if numPops + // is stale then the fresh value is larger and the >= is still true + return false; + } + if (popTicket_.compare_exchange_strong(numPops, numPops + 1)) { + return true; + } + } + } + + // Given a ticket, constructs an enqueued item using args + template <typename... Args> + void enqueueWithTicketBase( + uint64_t ticket, + Slot* slots, + size_t cap, + int stride, + Args&&... args) noexcept { + slots[idx(ticket, cap, stride)].enqueue( + turn(ticket, cap), + pushSpinCutoff_, + (ticket % kAdaptationFreq) == 0, + std::forward<Args>(args)...); + } + + // To support tracking ticket numbers in MPMCPipelineStageImpl + template <typename... Args> + void enqueueWithTicket(uint64_t ticket, Args&&... args) noexcept { + enqueueWithTicketBase( + ticket, slots_, capacity_, stride_, std::forward<Args>(args)...); + } + + // Given a ticket, dequeues the corresponding element + void dequeueWithTicketBase( + uint64_t ticket, + Slot* slots, + size_t cap, + int stride, + T& elem) noexcept { + assert(cap != 0); + slots[idx(ticket, cap, stride)].dequeue( + turn(ticket, cap), + popSpinCutoff_, + (ticket % kAdaptationFreq) == 0, + elem); + } +}; + +/// SingleElementQueue implements a blocking queue that holds at most one +/// item, and that requires its users to assign incrementing identifiers +/// (turns) to each enqueue and dequeue operation. Note that the turns +/// used by SingleElementQueue are doubled inside the TurnSequencer +template <typename T, template <typename> class Atom> +struct SingleElementQueue { + ~SingleElementQueue() noexcept { + if ((sequencer_.uncompletedTurnLSB() & 1) == 1) { + // we are pending a dequeue, so we have a constructed item + destroyContents(); + } + } + + /// enqueue using in-place noexcept construction + template < + typename... Args, + typename = typename std::enable_if< + std::is_nothrow_constructible<T, Args...>::value>::type> + void enqueue( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + Args&&... args) noexcept { + sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); + new (&contents_) T(std::forward<Args>(args)...); + sequencer_.completeTurn(turn * 2); + } + + /// enqueue using move construction, either real (if + /// is_nothrow_move_constructible) or simulated using relocation and + /// default construction (if IsRelocatable and is_nothrow_constructible) + template < + typename = typename std::enable_if< + (folly::IsRelocatable<T>::value && + std::is_nothrow_constructible<T>::value) || + std::is_nothrow_constructible<T, T&&>::value>::type> + void enqueue( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T&& goner) noexcept { + enqueueImpl( + turn, + spinCutoff, + updateSpinCutoff, + std::move(goner), + typename std::conditional< + std::is_nothrow_constructible<T, T&&>::value, + ImplByMove, + ImplByRelocation>::type()); + } + + /// Waits until either: + /// 1: the dequeue turn preceding the given enqueue turn has arrived + /// 2: the given deadline has arrived + /// Case 1 returns true, case 2 returns false. + template <class Clock> + bool tryWaitForEnqueueTurnUntil( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + const std::chrono::time_point<Clock>& when) noexcept { + return sequencer_.tryWaitForTurn( + turn * 2, spinCutoff, updateSpinCutoff, &when) != + TurnSequencer<Atom>::TryWaitResult::TIMEDOUT; + } + + bool mayEnqueue(const uint32_t turn) const noexcept { + return sequencer_.isTurn(turn * 2); + } + + void dequeue( + uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T& elem) noexcept { + dequeueImpl( + turn, + spinCutoff, + updateSpinCutoff, + elem, + typename std::conditional< + folly::IsRelocatable<T>::value, + ImplByRelocation, + ImplByMove>::type()); + } + + /// Waits until either: + /// 1: the enqueue turn preceding the given dequeue turn has arrived + /// 2: the given deadline has arrived + /// Case 1 returns true, case 2 returns false. + template <class Clock> + bool tryWaitForDequeueTurnUntil( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + const std::chrono::time_point<Clock>& when) noexcept { + return sequencer_.tryWaitForTurn( + turn * 2 + 1, spinCutoff, updateSpinCutoff, &when) != + TurnSequencer<Atom>::TryWaitResult::TIMEDOUT; + } + + bool mayDequeue(const uint32_t turn) const noexcept { + return sequencer_.isTurn(turn * 2 + 1); + } + + private: + /// Storage for a T constructed with placement new + aligned_storage_for_t<T> contents_; + + /// Even turns are pushes, odd turns are pops + TurnSequencer<Atom> sequencer_; + + T* ptr() noexcept { + return static_cast<T*>(static_cast<void*>(&contents_)); + } + + void destroyContents() noexcept { + try { + ptr()->~T(); + } catch (...) { + // g++ doesn't seem to have std::is_nothrow_destructible yet + } + if (kIsDebug) { + memset(&contents_, 'Q', sizeof(T)); + } + } + + /// Tag classes for dispatching to enqueue/dequeue implementation. + struct ImplByRelocation {}; + struct ImplByMove {}; + + /// enqueue using nothrow move construction. + void enqueueImpl( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T&& goner, + ImplByMove) noexcept { + sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); + new (&contents_) T(std::move(goner)); + sequencer_.completeTurn(turn * 2); + } + + /// enqueue by simulating nothrow move with relocation, followed by + /// default construction to a noexcept relocation. + void enqueueImpl( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T&& goner, + ImplByRelocation) noexcept { + sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); + memcpy( + static_cast<void*>(&contents_), + static_cast<void const*>(&goner), + sizeof(T)); + sequencer_.completeTurn(turn * 2); + new (&goner) T(); + } + + /// dequeue by destructing followed by relocation. This version is preferred, + /// because as much work as possible can be done before waiting. + void dequeueImpl( + uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T& elem, + ImplByRelocation) noexcept { + try { + elem.~T(); + } catch (...) { + // unlikely, but if we don't complete our turn the queue will die + } + sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff); + memcpy( + static_cast<void*>(&elem), + static_cast<void const*>(&contents_), + sizeof(T)); + sequencer_.completeTurn(turn * 2 + 1); + } + + /// dequeue by nothrow move assignment. + void dequeueImpl( + uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + T& elem, + ImplByMove) noexcept { + sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff); + elem = std::move(*ptr()); + destroyContents(); + sequencer_.completeTurn(turn * 2 + 1); + } +}; + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MacAddress.cpp b/ios/Pods/Flipper-Folly/folly/MacAddress.cpp new file mode 100644 index 000000000..5072d2062 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MacAddress.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/MacAddress.h> + +#include <cassert> +#include <ostream> + +#include <folly/Exception.h> +#include <folly/Format.h> +#include <folly/IPAddressV6.h> +#include <folly/String.h> + +using std::invalid_argument; +using std::string; + +namespace folly { + +const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))}; +const MacAddress MacAddress::ZERO; + +MacAddress::MacAddress(StringPiece str) { + memset(&bytes_, 0, 8); + parse(str); +} + +MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) { + // This method should only be used for multicast addresses. + assert(v6addr.isMulticast()); + + uint8_t bytes[SIZE]; + bytes[0] = 0x33; + bytes[1] = 0x33; + memcpy(bytes + 2, v6addr.bytes() + 12, 4); + return fromBinary(ByteRange(bytes, SIZE)); +} + +string MacAddress::toString() const { + static const char hexValues[] = "0123456789abcdef"; + string result; + result.resize(17); + result[0] = hexValues[getByte(0) >> 4]; + result[1] = hexValues[getByte(0) & 0xf]; + result[2] = ':'; + result[3] = hexValues[getByte(1) >> 4]; + result[4] = hexValues[getByte(1) & 0xf]; + result[5] = ':'; + result[6] = hexValues[getByte(2) >> 4]; + result[7] = hexValues[getByte(2) & 0xf]; + result[8] = ':'; + result[9] = hexValues[getByte(3) >> 4]; + result[10] = hexValues[getByte(3) & 0xf]; + result[11] = ':'; + result[12] = hexValues[getByte(4) >> 4]; + result[13] = hexValues[getByte(4) & 0xf]; + result[14] = ':'; + result[15] = hexValues[getByte(5) >> 4]; + result[16] = hexValues[getByte(5) & 0xf]; + return result; +} + +void MacAddress::parse(StringPiece str) { + // Helper function to convert a single hex char into an integer + auto isSeparatorChar = [](char c) { return c == ':' || c == '-'; }; + + uint8_t parsed[SIZE]; + auto p = str.begin(); + for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) { + if (p == str.end()) { + throw invalid_argument( + sformat("invalid MAC address '{}': not enough digits", str)); + } + + // Skip over ':' or '-' separators between bytes + if (byteIndex != 0 && isSeparatorChar(*p)) { + ++p; + if (p == str.end()) { + throw invalid_argument( + sformat("invalid MAC address '{}': not enough digits", str)); + } + } + + // Parse the upper nibble + uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)]; + if (upper & 0x10) { + throw invalid_argument( + sformat("invalid MAC address '{}': contains non-hex digit", str)); + } + ++p; + + // Parse the lower nibble + uint8_t lower; + if (p == str.end()) { + lower = upper; + upper = 0; + } else { + lower = detail::hexTable[static_cast<uint8_t>(*p)]; + if (lower & 0x10) { + // Also accept ':', '-', or '\0', to handle the case where one + // of the bytes was represented by just a single digit. + if (isSeparatorChar(*p)) { + lower = upper; + upper = 0; + } else { + throw invalid_argument( + sformat("invalid MAC address '{}': contains non-hex digit", str)); + } + } + ++p; + } + + // Update parsed with the newly parsed byte + parsed[byteIndex] = (upper << 4) | lower; + } + + if (p != str.end()) { + // String is too long to be a MAC address + throw invalid_argument( + sformat("invalid MAC address '{}': found trailing characters", str)); + } + + // Only update now that we have successfully parsed the entire + // string. This way we remain unchanged on error. + setFromBinary(ByteRange(parsed, SIZE)); +} + +void MacAddress::setFromBinary(ByteRange value) { + if (value.size() != SIZE) { + throw invalid_argument( + sformat("MAC address must be 6 bytes long, got ", value.size())); + } + memcpy(bytes_ + 2, value.begin(), SIZE); +} + +std::ostream& operator<<(std::ostream& os, MacAddress address) { + os << address.toString(); + return os; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MacAddress.h b/ios/Pods/Flipper-Folly/folly/MacAddress.h new file mode 100644 index 000000000..993d1dde2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MacAddress.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <iosfwd> + +#include <folly/Conv.h> +#include <folly/Range.h> +#include <folly/lang/Bits.h> + +namespace folly { + +class IPAddressV6; + +/* + * MacAddress represents an IEEE 802 MAC address. + */ +class MacAddress { + public: + static constexpr size_t SIZE = 6; + static const MacAddress BROADCAST; + static const MacAddress ZERO; + + /* + * Construct a zero-initialized MacAddress. + */ + MacAddress() { + memset(&bytes_, 0, 8); + } + + /* + * Parse a MacAddress from a human-readable string. + * The string must contain 6 one- or two-digit hexadecimal + * numbers, separated by dashes or colons. + * Examples: 00:02:C9:C8:F9:68 or 0-2-c9-c8-f9-68 + */ + explicit MacAddress(StringPiece str); + + /* + * Construct a MAC address from its 6-byte binary value + */ + static MacAddress fromBinary(ByteRange value) { + MacAddress ret; + ret.setFromBinary(value); + return ret; + } + + /* + * Construct a MacAddress from a uint64_t in network byte order. + * + * The first two bytes are ignored, and the MAC address is taken from the + * latter 6 bytes. + * + * This is a static method rather than a constructor to avoid confusion + * between host and network byte order constructors. + */ + static MacAddress fromNBO(uint64_t value) { + return MacAddress(value); + } + + /* + * Construct a MacAddress from a uint64_t in host byte order. + * + * The most significant two bytes are ignored, and the MAC address is taken + * from the least significant 6 bytes. + * + * This is a static method rather than a constructor to avoid confusion + * between host and network byte order constructors. + */ + static MacAddress fromHBO(uint64_t value) { + return MacAddress(Endian::big(value)); + } + + /* + * Construct the multicast MacAddress for the specified multicast IPv6 + * address. + */ + static MacAddress createMulticast(IPAddressV6 addr); + + /* + * Get a pointer to the MAC address' binary value. + * + * The returned value points to internal storage inside the MacAddress + * object. It is only valid as long as the MacAddress, and its contents may + * change if the MacAddress is updated. + */ + const uint8_t* bytes() const { + return bytes_ + 2; + } + + /* + * Return the address as a uint64_t, in network byte order. + * + * The first two bytes will be 0, and the subsequent 6 bytes will contain + * the address in network byte order. + */ + uint64_t u64NBO() const { + return packedBytes(); + } + + /* + * Return the address as a uint64_t, in host byte order. + * + * The two most significant bytes will be 0, and the remaining 6 bytes will + * contain the address. The most significant of these 6 bytes will contain + * the first byte that appear on the wire, and the least significant byte + * will contain the last byte. + */ + uint64_t u64HBO() const { + // Endian::big() does what we want here, even though we are converting + // from big-endian to host byte order. This swaps if and only if + // the host byte order is little endian. + return Endian::big(packedBytes()); + } + + /* + * Return a human-readable representation of the MAC address. + */ + std::string toString() const; + + /* + * Update the current MacAddress object from a human-readable string. + */ + void parse(StringPiece str); + + /* + * Update the current MacAddress object from a 6-byte binary representation. + */ + void setFromBinary(ByteRange value); + + bool isBroadcast() const { + return *this == BROADCAST; + } + bool isMulticast() const { + return getByte(0) & 0x1; + } + bool isUnicast() const { + return !isMulticast(); + } + + /* + * Return true if this MAC address is locally administered. + * + * Locally administered addresses are assigned by the local network + * administrator, and are not guaranteed to be globally unique. (It is + * similar to IPv4's private address space.) + * + * Note that isLocallyAdministered() will return true for the broadcast + * address, since it has the locally administered bit set. + */ + bool isLocallyAdministered() const { + return getByte(0) & 0x2; + } + + // Comparison operators. + + bool operator==(const MacAddress& other) const { + // All constructors and modifying methods make sure padding is 0, + // so we don't need to mask these bytes out when comparing here. + return packedBytes() == other.packedBytes(); + } + + bool operator<(const MacAddress& other) const { + return u64HBO() < other.u64HBO(); + } + + bool operator!=(const MacAddress& other) const { + return !(*this == other); + } + + bool operator>(const MacAddress& other) const { + return other < *this; + } + + bool operator>=(const MacAddress& other) const { + return !(*this < other); + } + + bool operator<=(const MacAddress& other) const { + return !(*this > other); + } + + private: + explicit MacAddress(uint64_t valueNBO) { + memcpy(&bytes_, &valueNBO, 8); + // Set the pad bytes to 0. + // This allows us to easily compare two MacAddresses, + // without having to worry about differences in the padding. + bytes_[0] = 0; + bytes_[1] = 0; + } + + /* We store the 6 bytes starting at bytes_[2] (most significant) + through bytes_[7] (least). + bytes_[0] and bytes_[1] are always equal to 0 to simplify comparisons. + */ + unsigned char bytes_[8]; + + inline uint64_t getByte(size_t index) const { + return bytes_[index + 2]; + } + + uint64_t packedBytes() const { + uint64_t u64; + memcpy(&u64, bytes_, 8); + return u64; + } +}; + +/* Define toAppend() so to<string> will work */ +template <class Tgt> +typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend( + MacAddress address, + Tgt* result) { + toAppend(address.toString(), result); +} + +std::ostream& operator<<(std::ostream& os, MacAddress address); + +} // namespace folly + +namespace std { + +// Provide an implementation for std::hash<MacAddress> +template <> +struct hash<folly::MacAddress> { + size_t operator()(const folly::MacAddress& address) const { + return std::hash<uint64_t>()(address.u64HBO()); + } +}; + +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/MapUtil.h b/ios/Pods/Flipper-Folly/folly/MapUtil.h new file mode 100644 index 000000000..1bda5a500 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MapUtil.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Conv.h> +#include <folly/Optional.h> +#include <folly/functional/Invoke.h> +#include <tuple> + +namespace folly { + +/** + * Given a map and a key, return the value corresponding to the key in the map, + * or a given default value if the key doesn't exist in the map. + */ +template <typename Map, typename Key> +typename Map::mapped_type get_default(const Map& map, const Key& key) { + auto pos = map.find(key); + return (pos != map.end()) ? (pos->second) : (typename Map::mapped_type{}); +} +template < + class Map, + typename Key = typename Map::key_type, + typename Value = typename Map::mapped_type, + typename std::enable_if<!is_invocable_v<Value>>::type* = nullptr> +typename Map::mapped_type +get_default(const Map& map, const Key& key, Value&& dflt) { + using M = typename Map::mapped_type; + auto pos = map.find(key); + return (pos != map.end()) ? (pos->second) : M(std::forward<Value>(dflt)); +} + +/** + * Give a map and a key, return the value corresponding to the key in the map, + * or a given default value if the key doesn't exist in the map. + */ +template < + class Map, + typename Key = typename Map::key_type, + typename Func, + typename = typename std::enable_if< + is_invocable_r_v<typename Map::mapped_type, Func>>::type> +typename Map::mapped_type +get_default(const Map& map, const Key& key, Func&& dflt) { + auto pos = map.find(key); + return pos != map.end() ? pos->second : dflt(); +} + +/** + * Given a map and a key, return the value corresponding to the key in the map, + * or throw an exception of the specified type. + */ +template < + class E = std::out_of_range, + class Map, + typename Key = typename Map::key_type> +const typename Map::mapped_type& get_or_throw( + const Map& map, + const Key& key, + const std::string& exceptionStrPrefix = std::string()) { + auto pos = map.find(key); + if (pos != map.end()) { + return pos->second; + } + throw_exception<E>(folly::to<std::string>(exceptionStrPrefix, key)); +} + +template < + class E = std::out_of_range, + class Map, + typename Key = typename Map::key_type> +typename Map::mapped_type& get_or_throw( + Map& map, + const Key& key, + const std::string& exceptionStrPrefix = std::string()) { + auto pos = map.find(key); + if (pos != map.end()) { + return pos->second; + } + throw_exception<E>(folly::to<std::string>(exceptionStrPrefix, key)); +} + +/** + * Given a map and a key, return a Optional<V> if the key exists and None if the + * key does not exist in the map. + */ +template <class Map, typename Key = typename Map::key_type> +folly::Optional<typename Map::mapped_type> get_optional( + const Map& map, + const Key& key) { + auto pos = map.find(key); + if (pos != map.end()) { + return folly::Optional<typename Map::mapped_type>(pos->second); + } else { + return folly::none; + } +} + +/** + * Given a map and a key, return a reference to the value corresponding to the + * key in the map, or the given default reference if the key doesn't exist in + * the map. + */ +template <class Map, typename Key = typename Map::key_type> +const typename Map::mapped_type& get_ref_default( + const Map& map, + const Key& key, + const typename Map::mapped_type& dflt) { + auto pos = map.find(key); + return (pos != map.end() ? pos->second : dflt); +} + +/** + * Passing a temporary default value returns a dangling reference when it is + * returned. Lifetime extension is broken by the indirection. + * The caller must ensure that the default value outlives the reference returned + * by get_ref_default(). + */ +template <class Map, typename Key = typename Map::key_type> +const typename Map::mapped_type& get_ref_default( + const Map& map, + const Key& key, + typename Map::mapped_type&& dflt) = delete; + +template <class Map, typename Key = typename Map::key_type> +const typename Map::mapped_type& get_ref_default( + const Map& map, + const Key& key, + const typename Map::mapped_type&& dflt) = delete; + +/** + * Given a map and a key, return a reference to the value corresponding to the + * key in the map, or the given default reference if the key doesn't exist in + * the map. + */ +template < + class Map, + typename Key = typename Map::key_type, + typename Func, + typename = typename std::enable_if< + is_invocable_r_v<const typename Map::mapped_type&, Func>>::type, + typename = typename std::enable_if< + std::is_reference<invoke_result_t<Func>>::value>::type> +const typename Map::mapped_type& +get_ref_default(const Map& map, const Key& key, Func&& dflt) { + auto pos = map.find(key); + return (pos != map.end() ? pos->second : dflt()); +} + +/** + * Given a map and a key, return a pointer to the value corresponding to the + * key in the map, or nullptr if the key doesn't exist in the map. + */ +template <class Map, typename Key = typename Map::key_type> +const typename Map::mapped_type* get_ptr(const Map& map, const Key& key) { + auto pos = map.find(key); + return (pos != map.end() ? &pos->second : nullptr); +} + +/** + * Non-const overload of the above. + */ +template <class Map, typename Key = typename Map::key_type> +typename Map::mapped_type* get_ptr(Map& map, const Key& key) { + auto pos = map.find(key); + return (pos != map.end() ? &pos->second : nullptr); +} + +// TODO: Remove the return type computations when clang 3.5 and gcc 5.1 are +// the minimum supported versions. +namespace detail { +template < + class T, + size_t pathLength, + class = typename std::enable_if<(pathLength > 0)>::type> +struct NestedMapType { + using type = typename NestedMapType<T, pathLength - 1>::type::mapped_type; +}; + +template <class T> +struct NestedMapType<T, 1> { + using type = typename T::mapped_type; +}; + +template <typename... KeysDefault> +struct DefaultType; + +template <typename Default> +struct DefaultType<Default> { + using type = Default; +}; + +template <typename Key, typename... KeysDefault> +struct DefaultType<Key, KeysDefault...> { + using type = typename DefaultType<KeysDefault...>::type; +}; + +template <class... KeysDefault> +auto extract_default(const KeysDefault&... keysDefault) -> + typename DefaultType<KeysDefault...>::type const& { + return std::get<sizeof...(KeysDefault) - 1>(std::tie(keysDefault...)); +} +} // namespace detail + +/** + * Given a map of maps and a path of keys, return a Optional<V> if the nested + * key exists and None if the nested keys does not exist in the map. + */ +template <class Map, class Key1, class Key2, class... Keys> +auto get_optional( + const Map& map, + const Key1& key1, + const Key2& key2, + const Keys&... keys) + -> folly::Optional< + typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type> { + auto pos = map.find(key1); + return pos != map.end() ? get_optional(pos->second, key2, keys...) + : folly::none; +} + +/** + * Given a map of maps and a path of keys, return a pointer to the nested value, + * or nullptr if the key doesn't exist in the map. + */ +template <class Map, class Key1, class Key2, class... Keys> +auto get_ptr( + const Map& map, + const Key1& key1, + const Key2& key2, + const Keys&... keys) -> + typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type const* { + auto pos = map.find(key1); + return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr; +} + +template <class Map, class Key1, class Key2, class... Keys> +auto get_ptr(Map& map, const Key1& key1, const Key2& key2, const Keys&... keys) + -> typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type* { + auto pos = map.find(key1); + return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr; +} + +/** + * Given a map and a path of keys, return the value corresponding to the nested + * value, or a given default value if the path doesn't exist in the map. + * The default value is the last parameter, and is copied when returned. + */ +template < + class Map, + class Key1, + class Key2, + class... KeysDefault, + typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type> +auto get_default( + const Map& map, + const Key1& key1, + const Key2& key2, + const KeysDefault&... keysDefault) -> + typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type { + if (const auto* ptr = get_ptr(map, key1)) { + return get_default(*ptr, key2, keysDefault...); + } + return detail::extract_default(keysDefault...); +} + +/** + * Given a map and a path of keys, return a reference to the value corresponding + * to the nested value, or the given default reference if the path doesn't exist + * in the map. + * The default value is the last parameter, and must be a lvalue reference. + */ +template < + class Map, + class Key1, + class Key2, + class... KeysDefault, + typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type, + typename = typename std::enable_if<std::is_lvalue_reference< + typename detail::DefaultType<KeysDefault...>::type>::value>::type> +auto get_ref_default( + const Map& map, + const Key1& key1, + const Key2& key2, + KeysDefault&&... keysDefault) -> + typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type + const& { + if (const auto* ptr = get_ptr(map, key1)) { + return get_ref_default(*ptr, key2, keysDefault...); + } + return detail::extract_default(keysDefault...); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Math.h b/ios/Pods/Flipper-Folly/folly/Math.h new file mode 100644 index 000000000..3228adbc1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Math.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Some arithmetic functions that seem to pop up or get hand-rolled a lot. + * So far they are all focused on integer division. + */ + +#pragma once + +#include <stdint.h> + +#include <limits> +#include <type_traits> + +namespace folly { + +namespace detail { + +template <typename T> +inline constexpr T divFloorBranchless(T num, T denom) { + // floor != trunc when the answer isn't exact and truncation went the + // wrong way (truncation went toward positive infinity). That happens + // when the true answer is negative, which happens when num and denom + // have different signs. The following code compiles branch-free on + // many platforms. + return (num / denom) + + ((num % denom) != 0 ? 1 : 0) * + (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 0); +} + +template <typename T> +inline constexpr T divFloorBranchful(T num, T denom) { + // First case handles negative result by preconditioning numerator. + // Preconditioning decreases the magnitude of the numerator, which is + // itself sign-dependent. Second case handles zero or positive rational + // result, where trunc and floor are the same. + return std::is_signed<T>::value && (num ^ denom) < 0 && num != 0 + ? (num + (num > 0 ? -1 : 1)) / denom - 1 + : num / denom; +} + +template <typename T> +inline constexpr T divCeilBranchless(T num, T denom) { + // ceil != trunc when the answer isn't exact (truncation occurred) + // and truncation went away from positive infinity. That happens when + // the true answer is positive, which happens when num and denom have + // the same sign. + return (num / denom) + + ((num % denom) != 0 ? 1 : 0) * + (std::is_signed<T>::value && (num ^ denom) < 0 ? 0 : 1); +} + +template <typename T> +inline constexpr T divCeilBranchful(T num, T denom) { + // First case handles negative or zero rational result, where trunc and ceil + // are the same. + // Second case handles positive result by preconditioning numerator. + // Preconditioning decreases the magnitude of the numerator, which is + // itself sign-dependent. + return (std::is_signed<T>::value && (num ^ denom) < 0) || num == 0 + ? num / denom + : (num + (num > 0 ? -1 : 1)) / denom + 1; +} + +template <typename T> +inline constexpr T divRoundAwayBranchless(T num, T denom) { + // away != trunc whenever truncation actually occurred, which is when + // there is a non-zero remainder. If the unrounded result is negative + // then fixup moves it toward negative infinity. If the unrounded + // result is positive then adjustment makes it larger. + return (num / denom) + + ((num % denom) != 0 ? 1 : 0) * + (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 1); +} + +template <typename T> +inline constexpr T divRoundAwayBranchful(T num, T denom) { + // First case of second ternary operator handles negative rational + // result, which is the same as divFloor. Second case of second ternary + // operator handles positive result, which is the same as divCeil. + // Zero case is separated for simplicity. + return num == 0 ? 0 + : (num + (num > 0 ? -1 : 1)) / denom + + (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 1); +} + +template <typename N, typename D> +using IdivResultType = typename std::enable_if< + std::is_integral<N>::value && std::is_integral<D>::value && + !std::is_same<N, bool>::value && !std::is_same<D, bool>::value, + decltype(N{1} / D{1})>::type; +} // namespace detail + +#if defined(__arm__) && !FOLLY_AARCH64 +constexpr auto kIntegerDivisionGivesRemainder = false; +#else +constexpr auto kIntegerDivisionGivesRemainder = true; +#endif + +/** + * Returns num/denom, rounded toward negative infinity. Put another way, + * returns the largest integral value that is less than or equal to the + * exact (not rounded) fraction num/denom. + * + * The matching remainder (num - divFloor(num, denom) * denom) can be + * negative only if denom is negative, unlike in truncating division. + * Note that for unsigned types this is the same as the normal integer + * division operator. divFloor is equivalent to python's integral division + * operator //. + * + * This function undergoes the same integer promotion rules as a + * built-in operator, except that we don't allow bool -> int promotion. + * This function is undefined if denom == 0. It is also undefined if the + * result type T is a signed type, num is std::numeric_limits<T>::min(), + * and denom is equal to -1 after conversion to the result type. + */ +template <typename N, typename D> +inline constexpr detail::IdivResultType<N, D> divFloor(N num, D denom) { + using R = decltype(num / denom); + return detail::IdivResultType<N, D>( + kIntegerDivisionGivesRemainder && std::is_signed<R>::value + ? detail::divFloorBranchless<R>(num, denom) + : detail::divFloorBranchful<R>(num, denom)); +} + +/** + * Returns num/denom, rounded toward positive infinity. Put another way, + * returns the smallest integral value that is greater than or equal to + * the exact (not rounded) fraction num/denom. + * + * This function undergoes the same integer promotion rules as a + * built-in operator, except that we don't allow bool -> int promotion. + * This function is undefined if denom == 0. It is also undefined if the + * result type T is a signed type, num is std::numeric_limits<T>::min(), + * and denom is equal to -1 after conversion to the result type. + */ +template <typename N, typename D> +inline constexpr detail::IdivResultType<N, D> divCeil(N num, D denom) { + using R = decltype(num / denom); + return detail::IdivResultType<N, D>( + kIntegerDivisionGivesRemainder && std::is_signed<R>::value + ? detail::divCeilBranchless<R>(num, denom) + : detail::divCeilBranchful<R>(num, denom)); +} + +/** + * Returns num/denom, rounded toward zero. If num and denom are non-zero + * and have different signs (so the unrounded fraction num/denom is + * negative), returns divCeil, otherwise returns divFloor. If T is an + * unsigned type then this is always equal to divFloor. + * + * Note that this is the same as the normal integer division operator, + * at least since C99 (before then the rounding for negative results was + * implementation defined). This function is here for completeness and + * as a place to hang this comment. + * + * This function undergoes the same integer promotion rules as a + * built-in operator, except that we don't allow bool -> int promotion. + * This function is undefined if denom == 0. It is also undefined if the + * result type T is a signed type, num is std::numeric_limits<T>::min(), + * and denom is equal to -1 after conversion to the result type. + */ +template <typename N, typename D> +inline constexpr detail::IdivResultType<N, D> divTrunc(N num, D denom) { + return detail::IdivResultType<N, D>(num / denom); +} + +/** + * Returns num/denom, rounded away from zero. If num and denom are + * non-zero and have different signs (so the unrounded fraction num/denom + * is negative), returns divFloor, otherwise returns divCeil. If T is + * an unsigned type then this is always equal to divCeil. + * + * This function undergoes the same integer promotion rules as a + * built-in operator, except that we don't allow bool -> int promotion. + * This function is undefined if denom == 0. It is also undefined if the + * result type T is a signed type, num is std::numeric_limits<T>::min(), + * and denom is equal to -1 after conversion to the result type. + */ +template <typename N, typename D> +inline constexpr detail::IdivResultType<N, D> divRoundAway(N num, D denom) { + using R = decltype(num / denom); + return detail::IdivResultType<N, D>( + kIntegerDivisionGivesRemainder && std::is_signed<R>::value + ? detail::divRoundAwayBranchless<R>(num, denom) + : detail::divRoundAwayBranchful<R>(num, denom)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Memory.h b/ios/Pods/Flipper-Folly/folly/Memory.h new file mode 100644 index 000000000..94462eab5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Memory.h @@ -0,0 +1,755 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <cerrno> +#include <cstddef> +#include <cstdlib> +#include <exception> +#include <limits> +#include <memory> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <folly/ConstexprMath.h> +#include <folly/Likely.h> +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Align.h> +#include <folly/lang/Exception.h> +#include <folly/portability/Config.h> +#include <folly/portability/Malloc.h> + +namespace folly { + +/// allocateBytes and deallocateBytes work like a checkedMalloc/free pair, +/// but take advantage of sized deletion when available +inline void* allocateBytes(size_t n) { + return ::operator new(n); +} + +inline void deallocateBytes(void* p, size_t n) { +#if __cpp_sized_deallocation + return ::operator delete(p, n); +#else + (void)n; + return ::operator delete(p); +#endif +} + +#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || \ + (defined(__ANDROID__) && (__ANDROID_API__ > 16)) || \ + (defined(__APPLE__) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6 || \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0)) || \ + defined(__FreeBSD__) || defined(__wasm32__) + +inline void* aligned_malloc(size_t size, size_t align) { + // use posix_memalign, but mimic the behaviour of memalign + void* ptr = nullptr; + int rc = posix_memalign(&ptr, align, size); + return rc == 0 ? (errno = 0, ptr) : (errno = rc, nullptr); +} + +inline void aligned_free(void* aligned_ptr) { + free(aligned_ptr); +} + +#elif defined(_WIN32) + +inline void* aligned_malloc(size_t size, size_t align) { + return _aligned_malloc(size, align); +} + +inline void aligned_free(void* aligned_ptr) { + _aligned_free(aligned_ptr); +} + +#else + +inline void* aligned_malloc(size_t size, size_t align) { + return memalign(align, size); +} + +inline void aligned_free(void* aligned_ptr) { + free(aligned_ptr); +} + +#endif + +namespace detail { +template <typename Alloc, size_t kAlign, bool kAllocate> +void rawOverAlignedImpl(Alloc const& alloc, size_t n, void*& raw) { + static_assert((kAlign & (kAlign - 1)) == 0, "Align must be a power of 2"); + + using AllocTraits = std::allocator_traits<Alloc>; + using T = typename AllocTraits::value_type; + + constexpr bool kCanBypass = std::is_same<Alloc, std::allocator<T>>::value; + + // BaseType is a type that gives us as much alignment as we need if + // we can get it naturally, otherwise it is aligned as max_align_t. + // kBaseAlign is both the alignment and size of this type. + constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t)); + using BaseType = std::aligned_storage_t<kBaseAlign, kBaseAlign>; + using BaseAllocTraits = + typename AllocTraits::template rebind_traits<BaseType>; + using BaseAlloc = typename BaseAllocTraits::allocator_type; + static_assert( + sizeof(BaseType) == kBaseAlign && alignof(BaseType) == kBaseAlign, ""); + +#if __cpp_sized_deallocation + if (kCanBypass && kAlign == kBaseAlign) { + // until std::allocator uses sized deallocation, it is worth the + // effort to bypass it when we are able + if (kAllocate) { + raw = ::operator new(n * sizeof(T)); + } else { + ::operator delete(raw, n * sizeof(T)); + } + return; + } +#endif + + if (kCanBypass && kAlign > kBaseAlign) { + // allocating as BaseType isn't sufficient to get alignment, but + // since we can bypass Alloc we can use something like posix_memalign + if (kAllocate) { + raw = aligned_malloc(n * sizeof(T), kAlign); + } else { + aligned_free(raw); + } + return; + } + + // we're not allowed to bypass Alloc, or we don't want to + BaseAlloc a(alloc); + + // allocation size is counted in sizeof(BaseType) + size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / sizeof(BaseType); + if (kAlign <= kBaseAlign) { + // rebinding Alloc to BaseType is sufficient to get us the alignment + // we want, happy path + if (kAllocate) { + raw = static_cast<void*>( + std::addressof(*BaseAllocTraits::allocate(a, quanta))); + } else { + BaseAllocTraits::deallocate( + a, + std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to( + *static_cast<BaseType*>(raw)), + quanta); + } + return; + } + + // Overaligned and custom allocator, our only option is to + // overallocate and store a delta to the actual allocation just + // before the returned ptr. + // + // If we give ourselves kAlign extra bytes, then since + // sizeof(BaseType) divides kAlign we can meet alignment while + // getting a prefix of one BaseType. If we happen to get a + // kAlign-aligned block, then we can return a pointer to underlying + // + kAlign, otherwise there will be at least kBaseAlign bytes in + // the unused prefix of the first kAlign-aligned block. + if (kAllocate) { + char* base = reinterpret_cast<char*>(std::addressof( + *BaseAllocTraits::allocate(a, quanta + kAlign / sizeof(BaseType)))); + size_t byteDelta = + kAlign - (reinterpret_cast<uintptr_t>(base) & (kAlign - 1)); + raw = static_cast<void*>(base + byteDelta); + static_cast<size_t*>(raw)[-1] = byteDelta; + } else { + size_t byteDelta = static_cast<size_t*>(raw)[-1]; + char* base = static_cast<char*>(raw) - byteDelta; + BaseAllocTraits::deallocate( + a, + std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to( + *reinterpret_cast<BaseType*>(base)), + quanta + kAlign / sizeof(BaseType)); + } +} +} // namespace detail + +// Works like std::allocator_traits<Alloc>::allocate, but handles +// over-aligned types. Feel free to manually specify any power of two as +// the Align template arg. Must be matched with deallocateOverAligned. +// allocationBytesForOverAligned will give you the number of bytes that +// this function actually requests. +template < + typename Alloc, + size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)> +typename std::allocator_traits<Alloc>::pointer allocateOverAligned( + Alloc const& alloc, + size_t n) { + void* raw = nullptr; + detail::rawOverAlignedImpl<Alloc, kAlign, true>(alloc, n, raw); + return std::pointer_traits<typename std::allocator_traits<Alloc>::pointer>:: + pointer_to( + *static_cast<typename std::allocator_traits<Alloc>::value_type*>( + raw)); +} + +template < + typename Alloc, + size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)> +void deallocateOverAligned( + Alloc const& alloc, + typename std::allocator_traits<Alloc>::pointer ptr, + size_t n) { + void* raw = static_cast<void*>(std::addressof(*ptr)); + detail::rawOverAlignedImpl<Alloc, kAlign, false>(alloc, n, raw); +} + +template < + typename Alloc, + size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)> +size_t allocationBytesForOverAligned(size_t n) { + static_assert((kAlign & (kAlign - 1)) == 0, "Align must be a power of 2"); + + using AllocTraits = std::allocator_traits<Alloc>; + using T = typename AllocTraits::value_type; + + constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t)); + + if (kAlign > kBaseAlign && std::is_same<Alloc, std::allocator<T>>::value) { + return n * sizeof(T); + } else { + size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / kBaseAlign; + if (kAlign > kBaseAlign) { + quanta += kAlign / kBaseAlign; + } + return quanta * kBaseAlign; + } +} + +/** + * For exception safety and consistency with make_shared. Erase me when + * we have std::make_unique(). + * + * @author Louis Brandy (ldbrandy@fb.com) + * @author Xu Ning (xning@fb.com) + */ + +#if __cplusplus >= 201402L || __cpp_lib_make_unique >= 201304L || \ + (__ANDROID__ && __cplusplus >= 201300L) || _MSC_VER >= 1900 + +/* using override */ using std::make_unique; + +#else + +template <typename T, typename... Args> +typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type +make_unique(Args&&... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +// Allows 'make_unique<T[]>(10)'. (N3690 s20.9.1.4 p3-4) +template <typename T> +typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type +make_unique(const size_t n) { + return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); +} + +// Disallows 'make_unique<T[10]>()'. (N3690 s20.9.1.4 p5) +template <typename T, typename... Args> +typename std::enable_if<std::extent<T>::value != 0, std::unique_ptr<T>>::type +make_unique(Args&&...) = delete; + +#endif + +/** + * static_function_deleter + * + * So you can write this: + * + * using RSA_deleter = folly::static_function_deleter<RSA, &RSA_free>; + * auto rsa = std::unique_ptr<RSA, RSA_deleter>(RSA_new()); + * RSA_generate_key_ex(rsa.get(), bits, exponent, nullptr); + * rsa = nullptr; // calls RSA_free(rsa.get()) + * + * This would be sweet as well for BIO, but unfortunately BIO_free has signature + * int(BIO*) while we require signature void(BIO*). So you would need to make a + * wrapper for it: + * + * inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); } + * using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>; + * auto buf = std::unique_ptr<BIO, BIO_deleter>(BIO_new(BIO_s_mem())); + * buf = nullptr; // calls BIO_free(buf.get()) + */ + +template <typename T, void (*f)(T*)> +struct static_function_deleter { + void operator()(T* t) const { + f(t); + } +}; + +/** + * to_shared_ptr + * + * Convert unique_ptr to shared_ptr without specifying the template type + * parameter and letting the compiler deduce it. + * + * So you can write this: + * + * auto sptr = to_shared_ptr(getSomethingUnique<T>()); + * + * Instead of this: + * + * auto sptr = shared_ptr<T>(getSomethingUnique<T>()); + * + * Useful when `T` is long, such as: + * + * using T = foobar::FooBarAsyncClient; + */ +template <typename T, typename D> +std::shared_ptr<T> to_shared_ptr(std::unique_ptr<T, D>&& ptr) { + return std::shared_ptr<T>(std::move(ptr)); +} + +/** + * to_weak_ptr + * + * Make a weak_ptr and return it from a shared_ptr without specifying the + * template type parameter and letting the compiler deduce it. + * + * So you can write this: + * + * auto wptr = to_weak_ptr(getSomethingShared<T>()); + * + * Instead of this: + * + * auto wptr = weak_ptr<T>(getSomethingShared<T>()); + * + * Useful when `T` is long, such as: + * + * using T = foobar::FooBarAsyncClient; + */ +template <typename T> +std::weak_ptr<T> to_weak_ptr(const std::shared_ptr<T>& ptr) { + return std::weak_ptr<T>(ptr); +} + +namespace detail { +template <typename T> +struct lift_void_to_char { + using type = T; +}; +template <> +struct lift_void_to_char<void> { + using type = char; +}; +} // namespace detail + +/** + * SysAllocator + * + * Resembles std::allocator, the default Allocator, but wraps std::malloc and + * std::free. + */ +template <typename T> +class SysAllocator { + private: + using Self = SysAllocator<T>; + + public: + using value_type = T; + + constexpr SysAllocator() = default; + + constexpr SysAllocator(SysAllocator const&) = default; + + template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0> + constexpr SysAllocator(SysAllocator<U> const&) noexcept {} + + T* allocate(size_t count) { + using lifted = typename detail::lift_void_to_char<T>::type; + auto const p = std::malloc(sizeof(lifted) * count); + if (!p) { + throw_exception<std::bad_alloc>(); + } + return static_cast<T*>(p); + } + void deallocate(T* p, size_t /* count */) { + std::free(p); + } + + friend bool operator==(Self const&, Self const&) noexcept { + return true; + } + friend bool operator!=(Self const&, Self const&) noexcept { + return false; + } +}; + +class DefaultAlign { + private: + using Self = DefaultAlign; + std::size_t align_; + + public: + explicit DefaultAlign(std::size_t align) noexcept : align_(align) { + assert(!(align_ < sizeof(void*)) && bool("bad align: too small")); + assert(!(align_ & (align_ - 1)) && bool("bad align: not power-of-two")); + } + std::size_t operator()(std::size_t align) const noexcept { + return align_ < align ? align : align_; + } + + friend bool operator==(Self const& a, Self const& b) noexcept { + return a.align_ == b.align_; + } + friend bool operator!=(Self const& a, Self const& b) noexcept { + return a.align_ != b.align_; + } +}; + +template <std::size_t Align> +class FixedAlign { + private: + static_assert(!(Align < sizeof(void*)), "bad align: too small"); + static_assert(!(Align & (Align - 1)), "bad align: not power-of-two"); + using Self = FixedAlign<Align>; + + public: + constexpr std::size_t operator()(std::size_t align) const noexcept { + return Align < align ? align : Align; + } + + friend bool operator==(Self const&, Self const&) noexcept { + return true; + } + friend bool operator!=(Self const&, Self const&) noexcept { + return false; + } +}; + +/** + * AlignedSysAllocator + * + * Resembles std::allocator, the default Allocator, but wraps aligned_malloc and + * aligned_free. + * + * Accepts a policy parameter for providing the alignment, which must: + * * be invocable as std::size_t(std::size_t) noexcept + * * taking the type alignment and returning the allocation alignment + * * be noexcept-copy-constructible + * * have noexcept operator== + * * have noexcept operator!= + * * not be final + * + * DefaultAlign and FixedAlign<std::size_t>, provided above, are valid policies. + */ +template <typename T, typename Align = DefaultAlign> +class AlignedSysAllocator : private Align { + private: + using Self = AlignedSysAllocator<T, Align>; + + template <typename, typename> + friend class AlignedSysAllocator; + + constexpr Align const& align() const { + return *this; + } + + public: + static_assert(std::is_nothrow_copy_constructible<Align>::value, ""); + static_assert(is_nothrow_invocable_r_v<std::size_t, Align, std::size_t>, ""); + + using value_type = T; + + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + + using Align::Align; + + // TODO: remove this ctor, which is is no longer required as of under gcc7 + template < + typename S = Align, + std::enable_if_t<std::is_default_constructible<S>::value, int> = 0> + constexpr AlignedSysAllocator() noexcept(noexcept(Align())) : Align() {} + + constexpr AlignedSysAllocator(AlignedSysAllocator const&) = default; + + template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0> + constexpr AlignedSysAllocator( + AlignedSysAllocator<U, Align> const& other) noexcept + : Align(other.align()) {} + + T* allocate(size_t count) { + using lifted = typename detail::lift_void_to_char<T>::type; + auto const a = align()(alignof(lifted)); + auto const p = aligned_malloc(sizeof(lifted) * count, a); + if (!p) { + if (FOLLY_UNLIKELY(errno != ENOMEM)) { + std::terminate(); + } + throw_exception<std::bad_alloc>(); + } + return static_cast<T*>(p); + } + void deallocate(T* p, size_t /* count */) { + aligned_free(p); + } + + friend bool operator==(Self const& a, Self const& b) noexcept { + return a.align() == b.align(); + } + friend bool operator!=(Self const& a, Self const& b) noexcept { + return a.align() != b.align(); + } +}; + +/** + * CxxAllocatorAdaptor + * + * A type conforming to C++ concept Allocator, delegating operations to an + * unowned Inner which has this required interface: + * + * void* allocate(std::size_t) + * void deallocate(void*, std::size_t) + * + * Note that Inner is *not* a C++ Allocator. + */ +template <typename T, class Inner> +class CxxAllocatorAdaptor { + private: + using Self = CxxAllocatorAdaptor<T, Inner>; + + template <typename U, typename UAlloc> + friend class CxxAllocatorAdaptor; + + std::reference_wrapper<Inner> ref_; + + public: + using value_type = T; + + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + + constexpr explicit CxxAllocatorAdaptor(Inner& ref) : ref_(ref) {} + + constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor const&) = default; + + template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0> + constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor<U, Inner> const& other) + : ref_(other.ref_) {} + + T* allocate(std::size_t n) { + using lifted = typename detail::lift_void_to_char<T>::type; + return static_cast<T*>(ref_.get().allocate(sizeof(lifted) * n)); + } + void deallocate(T* p, std::size_t n) { + using lifted = typename detail::lift_void_to_char<T>::type; + ref_.get().deallocate(p, sizeof(lifted) * n); + } + + friend bool operator==(Self const& a, Self const& b) noexcept { + return std::addressof(a.ref_.get()) == std::addressof(b.ref_.get()); + } + friend bool operator!=(Self const& a, Self const& b) noexcept { + return std::addressof(a.ref_.get()) != std::addressof(b.ref_.get()); + } +}; + +/* + * allocator_delete + * + * A deleter which automatically works with a given allocator. + * + * Derives from the allocator to take advantage of the empty base + * optimization when possible. + */ +template <typename Alloc> +class allocator_delete : private std::remove_reference<Alloc>::type { + private: + using allocator_type = typename std::remove_reference<Alloc>::type; + using allocator_traits = std::allocator_traits<allocator_type>; + using value_type = typename allocator_traits::value_type; + using pointer = typename allocator_traits::pointer; + + public: + allocator_delete() = default; + allocator_delete(allocator_delete const&) = default; + allocator_delete(allocator_delete&&) = default; + allocator_delete& operator=(allocator_delete const&) = default; + allocator_delete& operator=(allocator_delete&&) = default; + + explicit allocator_delete(const allocator_type& alloc) + : allocator_type(alloc) {} + + explicit allocator_delete(allocator_type&& alloc) + : allocator_type(std::move(alloc)) {} + + template <typename U> + allocator_delete(const allocator_delete<U>& other) + : allocator_type(other.get_allocator()) {} + + allocator_type const& get_allocator() const { + return *this; + } + + void operator()(pointer p) const { + auto alloc = get_allocator(); + allocator_traits::destroy(alloc, p); + allocator_traits::deallocate(alloc, p, 1); + } +}; + +/** + * allocate_unique, like std::allocate_shared but for std::unique_ptr + */ +template <typename T, typename Alloc, typename... Args> +std::unique_ptr<T, allocator_delete<Alloc>> allocate_unique( + Alloc const& alloc, + Args&&... args) { + using traits = std::allocator_traits<Alloc>; + struct DeferCondDeallocate { + bool& cond; + Alloc& copy; + T* p; + ~DeferCondDeallocate() { + if (FOLLY_UNLIKELY(!cond)) { + traits::deallocate(copy, p, 1); + } + } + }; + auto copy = alloc; + auto const p = traits::allocate(copy, 1); + { + bool constructed = false; + DeferCondDeallocate handler{constructed, copy, p}; + traits::construct(copy, p, static_cast<Args&&>(args)...); + constructed = true; + } + return {p, allocator_delete<Alloc>(std::move(copy))}; +} + +struct SysBufferDeleter { + void operator()(void* ptr) { + std::free(ptr); + } +}; +using SysBufferUniquePtr = std::unique_ptr<void, SysBufferDeleter>; + +inline SysBufferUniquePtr allocate_sys_buffer(std::size_t size) { + auto p = std::malloc(size); + if (!p) { + throw_exception<std::bad_alloc>(); + } + return {p, {}}; +} + +/** + * AllocatorHasTrivialDeallocate + * + * Unambiguously inherits std::integral_constant<bool, V> for some bool V. + * + * Describes whether a C++ Aallocator has trivial, i.e. no-op, deallocate(). + * + * Also may be used to describe types which may be used with + * CxxAllocatorAdaptor. + */ +template <typename Alloc> +struct AllocatorHasTrivialDeallocate : std::false_type {}; + +template <typename T, class Alloc> +struct AllocatorHasTrivialDeallocate<CxxAllocatorAdaptor<T, Alloc>> + : AllocatorHasTrivialDeallocate<Alloc> {}; + +namespace detail { +// note that construct and destroy here are methods, not short names for +// the constructor and destructor +FOLLY_CREATE_MEMBER_INVOKER(AllocatorConstruct_, construct); +FOLLY_CREATE_MEMBER_INVOKER(AllocatorDestroy_, destroy); + +template <typename Void, typename Alloc, typename... Args> +struct AllocatorCustomizesConstruct_ + : folly::is_invocable<AllocatorConstruct_, Alloc, Args...> {}; + +template <typename Alloc, typename... Args> +struct AllocatorCustomizesConstruct_< + void_t<typename Alloc::folly_has_default_object_construct>, + Alloc, + Args...> : Negation<typename Alloc::folly_has_default_object_construct> {}; + +template <typename Void, typename Alloc, typename... Args> +struct AllocatorCustomizesDestroy_ + : folly::is_invocable<AllocatorDestroy_, Alloc, Args...> {}; + +template <typename Alloc, typename... Args> +struct AllocatorCustomizesDestroy_< + void_t<typename Alloc::folly_has_default_object_destroy>, + Alloc, + Args...> : Negation<typename Alloc::folly_has_default_object_destroy> {}; +} // namespace detail + +/** + * AllocatorHasDefaultObjectConstruct + * + * AllocatorHasDefaultObjectConstruct<A, T, Args...> unambiguously + * inherits std::integral_constant<bool, V>, where V will be true iff + * the effect of std::allocator_traits<A>::construct(a, p, args...) is + * the same as new (static_cast<void*>(p)) T(args...). If true then + * any optimizations applicable to object construction (relying on + * std::is_trivially_copyable<T>, for example) can be applied to objects + * in an allocator-aware container using an allocation of type A. + * + * Allocator types can override V by declaring a type alias for + * folly_has_default_object_construct. It is helpful to do this if you + * define a custom allocator type that defines a construct method, but + * that method doesn't do anything except call placement new. + */ +template <typename Alloc, typename T, typename... Args> +struct AllocatorHasDefaultObjectConstruct + : Negation< + detail::AllocatorCustomizesConstruct_<void, Alloc, T*, Args...>> {}; + +template <typename Value, typename T, typename... Args> +struct AllocatorHasDefaultObjectConstruct<std::allocator<Value>, T, Args...> + : std::true_type {}; + +/** + * AllocatorHasDefaultObjectDestroy + * + * AllocatorHasDefaultObjectDestroy<A, T> unambiguously inherits + * std::integral_constant<bool, V>, where V will be true iff the effect + * of std::allocator_traits<A>::destroy(a, p) is the same as p->~T(). + * If true then optimizations applicable to object destruction (relying + * on std::is_trivially_destructible<T>, for example) can be applied to + * objects in an allocator-aware container using an allocator of type A. + * + * Allocator types can override V by declaring a type alias for + * folly_has_default_object_destroy. It is helpful to do this if you + * define a custom allocator type that defines a destroy method, but that + * method doesn't do anything except call the object's destructor. + */ +template <typename Alloc, typename T> +struct AllocatorHasDefaultObjectDestroy + : Negation<detail::AllocatorCustomizesDestroy_<void, Alloc, T*>> {}; + +template <typename Value, typename T> +struct AllocatorHasDefaultObjectDestroy<std::allocator<Value>, T> + : std::true_type {}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MicroLock.cpp b/ios/Pods/Flipper-Folly/folly/MicroLock.cpp new file mode 100644 index 000000000..9fe6044b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MicroLock.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/MicroLock.h> +#include <thread> + +#include <folly/portability/Asm.h> + +namespace folly { + +void MicroLockCore::lockSlowPath( + uint32_t oldWord, + detail::Futex<>* wordPtr, + uint32_t slotHeldBit, + unsigned maxSpins, + unsigned maxYields) { + uint32_t newWord; + unsigned spins = 0; + uint32_t slotWaitBit = slotHeldBit << 1; + uint32_t needWaitBit = 0; + +retry: + if ((oldWord & slotHeldBit) != 0) { + ++spins; + if (spins > maxSpins + maxYields) { + // Somebody appears to have the lock. Block waiting for the + // holder to unlock the lock. We set heldbit(slot) so that the + // lock holder knows to FUTEX_WAKE us. + newWord = oldWord | slotWaitBit; + if (newWord != oldWord) { + if (!wordPtr->compare_exchange_weak( + oldWord, + newWord, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + goto retry; + } + } + detail::futexWait(wordPtr, newWord, slotHeldBit); + needWaitBit = slotWaitBit; + } else if (spins > maxSpins) { + // sched_yield(), but more portable + std::this_thread::yield(); + } else { + folly::asm_volatile_pause(); + } + oldWord = wordPtr->load(std::memory_order_relaxed); + goto retry; + } + + newWord = oldWord | slotHeldBit | needWaitBit; + if (!wordPtr->compare_exchange_weak( + oldWord, + newWord, + std::memory_order_acquire, + std::memory_order_relaxed)) { + goto retry; + } +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MicroLock.h b/ios/Pods/Flipper-Folly/folly/MicroLock.h new file mode 100644 index 000000000..231496f65 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MicroLock.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <climits> +#include <cstdint> + +#include <folly/Portability.h> +#include <folly/detail/Futex.h> + +namespace folly { + +/** + * Tiny exclusive lock that packs four lock slots into a single + * byte. Each slot is an independent real, sleeping lock. The default + * lock and unlock functions operate on slot zero, which modifies only + * the low two bits of the host byte. + * + * You should zero-initialize the bits of a MicroLock that you intend + * to use. + * + * If you're not space-constrained, prefer std::mutex, which will + * likely be faster, since it has more than two bits of information to + * work with. + * + * You are free to put a MicroLock in a union with some other object. + * If, for example, you want to use the bottom two bits of a pointer + * as a lock, you can put a MicroLock in a union with the pointer and + * limit yourself to MicroLock slot zero, which will use the two + * least-significant bits in the bottom byte. + * + * (Note that such a union is safe only because MicroLock is based on + * a character type, and even under a strict interpretation of C++'s + * aliasing rules, character types may alias anything.) + * + * MicroLock uses a dirty trick: it actually operates on the full + * 32-bit, four-byte-aligned bit of memory into which it is embedded. + * It never modifies bits outside the ones it's defined to modify, but + * it _accesses_ all the bits in the 32-bit memory location for + * purposes of futex management. + * + * The MaxSpins template parameter controls the number of times we + * spin trying to acquire the lock. MaxYields controls the number of + * times we call sched_yield; once we've tried to acquire the lock + * MaxSpins + MaxYields times, we sleep on the lock futex. + * By adjusting these parameters, you can make MicroLock behave as + * much or as little like a conventional spinlock as you'd like. + * + * Performance + * ----------- + * + * With the default template options, the timings for uncontended + * acquire-then-release come out as follows on Intel(R) Xeon(R) CPU + * E5-2660 0 @ 2.20GHz, in @mode/opt, as of the master tree at Tue, 01 + * Mar 2016 19:48:15. + * + * ======================================================================== + * folly/test/SmallLocksBenchmark.cpp relative time/iter iters/s + * ======================================================================== + * MicroSpinLockUncontendedBenchmark 13.46ns 74.28M + * PicoSpinLockUncontendedBenchmark 14.99ns 66.71M + * MicroLockUncontendedBenchmark 27.06ns 36.96M + * StdMutexUncontendedBenchmark 25.18ns 39.72M + * VirtualFunctionCall 1.72ns 579.78M + * ======================================================================== + * + * (The virtual dispatch benchmark is provided for scale.) + * + * While the uncontended case for MicroLock is competitive with the + * glibc 2.2.0 implementation of std::mutex, std::mutex is likely to be + * faster in the contended case, because we need to wake up all waiters + * when we release. + * + * Make sure to benchmark your particular workload. + * + */ + +class MicroLockCore { + protected: + uint8_t lock_; + inline detail::Futex<>* word() const; // Well, halfword on 64-bit systems + inline uint32_t baseShift(unsigned slot) const; + inline uint32_t heldBit(unsigned slot) const; + inline uint32_t waitBit(unsigned slot) const; + static void lockSlowPath( + uint32_t oldWord, + detail::Futex<>* wordPtr, + uint32_t slotHeldBit, + unsigned maxSpins, + unsigned maxYields); + + public: + FOLLY_DISABLE_ADDRESS_SANITIZER inline void unlock(unsigned slot); + inline void unlock() { + unlock(0); + } + // Initializes all the slots. + inline void init() { + lock_ = 0; + } +}; + +inline detail::Futex<>* MicroLockCore::word() const { + uintptr_t lockptr = (uintptr_t)&lock_; + lockptr &= ~(sizeof(uint32_t) - 1); + return (detail::Futex<>*)lockptr; +} + +inline unsigned MicroLockCore::baseShift(unsigned slot) const { + assert(slot < CHAR_BIT / 2); + + unsigned offset_bytes = (unsigned)((uintptr_t)&lock_ - (uintptr_t)word()); + + return ( + unsigned)(kIsLittleEndian ? offset_bytes * CHAR_BIT + slot * 2 : CHAR_BIT * (sizeof(uint32_t) - offset_bytes - 1) + slot * 2); +} + +inline uint32_t MicroLockCore::heldBit(unsigned slot) const { + return 1U << (baseShift(slot) + 0); +} + +inline uint32_t MicroLockCore::waitBit(unsigned slot) const { + return 1U << (baseShift(slot) + 1); +} + +void MicroLockCore::unlock(unsigned slot) { + detail::Futex<>* wordPtr = word(); + uint32_t oldWord; + uint32_t newWord; + + oldWord = wordPtr->load(std::memory_order_relaxed); + do { + assert(oldWord & heldBit(slot)); + newWord = oldWord & ~(heldBit(slot) | waitBit(slot)); + } while (!wordPtr->compare_exchange_weak( + oldWord, newWord, std::memory_order_release, std::memory_order_relaxed)); + + if (oldWord & waitBit(slot)) { + detail::futexWake(wordPtr, 1, heldBit(slot)); + } +} + +template <unsigned MaxSpins = 1000, unsigned MaxYields = 0> +class MicroLockBase : public MicroLockCore { + public: + FOLLY_DISABLE_ADDRESS_SANITIZER inline void lock(unsigned slot); + inline void lock() { + lock(0); + } + FOLLY_DISABLE_ADDRESS_SANITIZER inline bool try_lock(unsigned slot); + inline bool try_lock() { + return try_lock(0); + } +}; + +template <unsigned MaxSpins, unsigned MaxYields> +bool MicroLockBase<MaxSpins, MaxYields>::try_lock(unsigned slot) { + // N.B. You might think that try_lock is just the fast path of lock, + // but you'd be wrong. Keep in mind that other parts of our host + // word might be changing while we take the lock! We're not allowed + // to fail spuriously if the lock is in fact not held, even if other + // people are concurrently modifying other parts of the word. + // + // We need to loop until we either see firm evidence that somebody + // else has the lock (by looking at heldBit) or see our CAS succeed. + // A failed CAS by itself does not indicate lock-acquire failure. + + detail::Futex<>* wordPtr = word(); + uint32_t oldWord = wordPtr->load(std::memory_order_relaxed); + do { + if (oldWord & heldBit(slot)) { + return false; + } + } while (!wordPtr->compare_exchange_weak( + oldWord, + oldWord | heldBit(slot), + std::memory_order_acquire, + std::memory_order_relaxed)); + + return true; +} + +template <unsigned MaxSpins, unsigned MaxYields> +void MicroLockBase<MaxSpins, MaxYields>::lock(unsigned slot) { + static_assert(MaxSpins + MaxYields < (unsigned)-1, "overflow"); + + detail::Futex<>* wordPtr = word(); + uint32_t oldWord; + oldWord = wordPtr->load(std::memory_order_relaxed); + if ((oldWord & heldBit(slot)) == 0 && + wordPtr->compare_exchange_weak( + oldWord, + oldWord | heldBit(slot), + std::memory_order_acquire, + std::memory_order_relaxed)) { + // Fast uncontended case: memory_order_acquire above is our barrier + } else { + // lockSlowPath doesn't have any slot-dependent computation; it + // just shifts the input bit. Make sure its shifting produces the + // same result a call to waitBit for our slot would. + assert(heldBit(slot) << 1 == waitBit(slot)); + // lockSlowPath emits its own memory barrier + lockSlowPath(oldWord, wordPtr, heldBit(slot), MaxSpins, MaxYields); + } +} + +typedef MicroLockBase<> MicroLock; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/MicroSpinLock.h b/ios/Pods/Flipper-Folly/folly/MicroSpinLock.h new file mode 100644 index 000000000..d42b0d4d9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MicroSpinLock.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/MicroSpinLock.h> // @shim diff --git a/ios/Pods/Flipper-Folly/folly/MoveWrapper.h b/ios/Pods/Flipper-Folly/folly/MoveWrapper.h new file mode 100644 index 000000000..58c29478a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/MoveWrapper.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +namespace folly { + +/** C++11 closures don't support move-in capture. Nor does std::bind. + facepalm. + + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html + + "[...] a work-around that should make people's stomach crawl: + write a wrapper that performs move-on-copy, much like the deprecated + auto_ptr" + + Unlike auto_ptr, this doesn't require a heap allocation. + */ +template <class T> +class MoveWrapper { + public: + /** If value can be default-constructed, why not? + Then we don't have to move it in */ + MoveWrapper() = default; + + /// Move a value in. + explicit MoveWrapper(T&& t) : value(std::move(t)) {} + + /// copy is move + MoveWrapper(const MoveWrapper& other) : value(std::move(other.value)) {} + + /// move is also move + MoveWrapper(MoveWrapper&& other) : value(std::move(other.value)) {} + + const T& operator*() const { + return value; + } + T& operator*() { + return value; + } + + const T* operator->() const { + return &value; + } + T* operator->() { + return &value; + } + + /// move the value out (sugar for std::move(*moveWrapper)) + T&& move() { + return std::move(value); + } + + // If you want these you're probably doing it wrong, though they'd be + // easy enough to implement + MoveWrapper& operator=(MoveWrapper const&) = delete; + MoveWrapper& operator=(MoveWrapper&&) = delete; + + private: + mutable T value; +}; + +/// Make a MoveWrapper from the argument. Because the name "makeMoveWrapper" +/// is already quite transparent in its intent, this will work for lvalues as +/// if you had wrapped them in std::move. +template <class T, class T0 = typename std::remove_reference<T>::type> +MoveWrapper<T0> makeMoveWrapper(T&& t) { + return MoveWrapper<T0>(std::forward<T0>(t)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Optional.h b/ios/Pods/Flipper-Folly/folly/Optional.h new file mode 100644 index 000000000..42181e43d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Optional.h @@ -0,0 +1,688 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/* + * Optional - For conditional initialization of values, like boost::optional, + * but with support for move semantics and emplacement. Reference type support + * has not been included due to limited use cases and potential confusion with + * semantics of assignment: Assigning to an optional reference could quite + * reasonably copy its value or redirect the reference. + * + * Optional can be useful when a variable might or might not be needed: + * + * Optional<Logger> maybeLogger = ...; + * if (maybeLogger) { + * maybeLogger->log("hello"); + * } + * + * Optional enables a 'null' value for types which do not otherwise have + * nullability, especially useful for parameter passing: + * + * void testIterator(const unique_ptr<Iterator>& it, + * initializer_list<int> idsExpected, + * Optional<initializer_list<int>> ranksExpected = none) { + * for (int i = 0; it->next(); ++i) { + * EXPECT_EQ(it->doc().id(), idsExpected[i]); + * if (ranksExpected) { + * EXPECT_EQ(it->doc().rank(), (*ranksExpected)[i]); + * } + * } + * } + * + * Optional models OptionalPointee, so calling 'get_pointer(opt)' will return a + * pointer to nullptr if the 'opt' is empty, and a pointer to the value if it is + * not: + * + * Optional<int> maybeInt = ...; + * if (int* v = get_pointer(maybeInt)) { + * cout << *v << endl; + * } + */ + +#include <cstddef> +#include <functional> +#include <new> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> + +namespace folly { + +template <class Value> +class Optional; + +namespace detail { +template <class Value> +struct OptionalPromiseReturn; +} // namespace detail + +struct None { + enum class _secret { _token }; + + /** + * No default constructor to support both `op = {}` and `op = none` + * as syntax for clearing an Optional, just like std::nullopt_t. + */ + explicit constexpr None(_secret) {} +}; +constexpr None none{None::_secret::_token}; + +class FOLLY_EXPORT OptionalEmptyException : public std::runtime_error { + public: + OptionalEmptyException() + : std::runtime_error("Empty Optional cannot be unwrapped") {} +}; + +template <class Value> +class Optional { + public: + typedef Value value_type; + + static_assert( + !std::is_reference<Value>::value, + "Optional may not be used with reference types"); + static_assert( + !std::is_abstract<Value>::value, + "Optional may not be used with abstract types"); + + constexpr Optional() noexcept {} + + Optional(const Optional& src) noexcept( + std::is_nothrow_copy_constructible<Value>::value) { + if (src.hasValue()) { + construct(src.value()); + } + } + + Optional(Optional&& src) noexcept( + std::is_nothrow_move_constructible<Value>::value) { + if (src.hasValue()) { + construct(std::move(src.value())); + src.reset(); + } + } + + constexpr /* implicit */ Optional(const None&) noexcept {} + + constexpr /* implicit */ Optional(Value&& newValue) noexcept( + std::is_nothrow_move_constructible<Value>::value) { + construct(std::move(newValue)); + } + + constexpr /* implicit */ Optional(const Value& newValue) noexcept( + std::is_nothrow_copy_constructible<Value>::value) { + construct(newValue); + } + + template <typename... Args> + constexpr explicit Optional(in_place_t, Args&&... args) noexcept( + std::is_nothrow_constructible<Value, Args...>::value) + : Optional{PrivateConstructor{}, std::forward<Args>(args)...} {} + + template <typename U, typename... Args> + constexpr explicit Optional( + in_place_t, + std::initializer_list<U> il, + Args&&... args) noexcept(std:: + is_nothrow_constructible< + Value, + std::initializer_list<U>, + Args...>::value) + : Optional{PrivateConstructor{}, il, std::forward<Args>(args)...} {} + + // Used only when an Optional is used with coroutines on MSVC + /* implicit */ Optional(const detail::OptionalPromiseReturn<Value>& p) + : Optional{} { + p.promise_->value_ = this; + } + + void assign(const None&) { + reset(); + } + + void assign(Optional&& src) { + if (this != &src) { + if (src.hasValue()) { + assign(std::move(src.value())); + src.reset(); + } else { + reset(); + } + } + } + + void assign(const Optional& src) { + if (src.hasValue()) { + assign(src.value()); + } else { + reset(); + } + } + + void assign(Value&& newValue) { + if (hasValue()) { + storage_.value = std::move(newValue); + } else { + construct(std::move(newValue)); + } + } + + void assign(const Value& newValue) { + if (hasValue()) { + storage_.value = newValue; + } else { + construct(newValue); + } + } + + Optional& operator=(None) noexcept { + reset(); + return *this; + } + + template <class Arg> + Optional& operator=(Arg&& arg) { + assign(std::forward<Arg>(arg)); + return *this; + } + + Optional& operator=(Optional&& other) noexcept( + std::is_nothrow_move_assignable<Value>::value) { + assign(std::move(other)); + return *this; + } + + Optional& operator=(const Optional& other) noexcept( + std::is_nothrow_copy_assignable<Value>::value) { + assign(other); + return *this; + } + + template <class... Args> + Value& emplace(Args&&... args) { + reset(); + construct(std::forward<Args>(args)...); + return value(); + } + + template <class U, class... Args> + typename std::enable_if< + std::is_constructible<Value, std::initializer_list<U>&, Args&&...>::value, + Value&>::type + emplace(std::initializer_list<U> ilist, Args&&... args) { + reset(); + construct(ilist, std::forward<Args>(args)...); + return value(); + } + + void reset() noexcept { + storage_.clear(); + } + + void clear() noexcept { + reset(); + } + + void swap(Optional& that) noexcept(IsNothrowSwappable<Value>::value) { + if (hasValue() && that.hasValue()) { + using std::swap; + swap(value(), that.value()); + } else if (hasValue()) { + that.emplace(std::move(value())); + reset(); + } else if (that.hasValue()) { + emplace(std::move(that.value())); + that.reset(); + } + } + + constexpr const Value& value() const& { + require_value(); + return storage_.value; + } + + constexpr Value& value() & { + require_value(); + return storage_.value; + } + + constexpr Value&& value() && { + require_value(); + return std::move(storage_.value); + } + + constexpr const Value&& value() const&& { + require_value(); + return std::move(storage_.value); + } + + const Value* get_pointer() const& { + return storage_.hasValue ? &storage_.value : nullptr; + } + Value* get_pointer() & { + return storage_.hasValue ? &storage_.value : nullptr; + } + Value* get_pointer() && = delete; + + constexpr bool has_value() const noexcept { + return storage_.hasValue; + } + + constexpr bool hasValue() const noexcept { + return has_value(); + } + + constexpr explicit operator bool() const noexcept { + return has_value(); + } + + constexpr const Value& operator*() const& { + return value(); + } + constexpr Value& operator*() & { + return value(); + } + constexpr const Value&& operator*() const&& { + return std::move(value()); + } + constexpr Value&& operator*() && { + return std::move(value()); + } + + constexpr const Value* operator->() const { + return &value(); + } + constexpr Value* operator->() { + return &value(); + } + + // Return a copy of the value if set, or a given default if not. + template <class U> + constexpr Value value_or(U&& dflt) const& { + if (storage_.hasValue) { + return storage_.value; + } + + return std::forward<U>(dflt); + } + + template <class U> + constexpr Value value_or(U&& dflt) && { + if (storage_.hasValue) { + return std::move(storage_.value); + } + + return std::forward<U>(dflt); + } + + private: + template <class T> + friend constexpr Optional<std::decay_t<T>> make_optional(T&&); + template <class T, class... Args> + friend constexpr Optional<T> make_optional(Args&&... args); + template <class T, class U, class... As> + friend constexpr Optional<T> make_optional(std::initializer_list<U>, As&&...); + + /** + * Construct the optional in place, this is duplicated as a non-explicit + * constructor to allow returning values that are non-movable from + * make_optional using list initialization. + * + * Until C++17, at which point this will become unnecessary because of + * specified prvalue elision. + */ + struct PrivateConstructor { + explicit PrivateConstructor() = default; + }; + template <typename... Args> + constexpr Optional(PrivateConstructor, Args&&... args) noexcept( + std::is_constructible<Value, Args&&...>::value) { + construct(std::forward<Args>(args)...); + } + + void require_value() const { + if (!storage_.hasValue) { + throw_exception<OptionalEmptyException>(); + } + } + + template <class... Args> + void construct(Args&&... args) { + const void* ptr = &storage_.value; + // For supporting const types. + new (const_cast<void*>(ptr)) Value(std::forward<Args>(args)...); + storage_.hasValue = true; + } + + struct StorageTriviallyDestructible { + union { + char emptyState; + Value value; + }; + bool hasValue; + + constexpr StorageTriviallyDestructible() + : emptyState('\0'), hasValue{false} {} + void clear() { + hasValue = false; + } + }; + + struct StorageNonTriviallyDestructible { + union { + char emptyState; + Value value; + }; + bool hasValue; + + StorageNonTriviallyDestructible() : hasValue{false} {} + ~StorageNonTriviallyDestructible() { + clear(); + } + + void clear() { + if (hasValue) { + hasValue = false; + value.~Value(); + } + } + }; + + using Storage = typename std::conditional< + std::is_trivially_destructible<Value>::value, + StorageTriviallyDestructible, + StorageNonTriviallyDestructible>::type; + + Storage storage_; +}; + +template <class T> +const T* get_pointer(const Optional<T>& opt) { + return opt.get_pointer(); +} + +template <class T> +T* get_pointer(Optional<T>& opt) { + return opt.get_pointer(); +} + +template <class T> +void swap(Optional<T>& a, Optional<T>& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +template <class T> +constexpr Optional<std::decay_t<T>> make_optional(T&& v) { + using PrivateConstructor = + typename folly::Optional<std::decay_t<T>>::PrivateConstructor; + return {PrivateConstructor{}, std::forward<T>(v)}; +} + +template <class T, class... Args> +constexpr folly::Optional<T> make_optional(Args&&... args) { + using PrivateConstructor = typename folly::Optional<T>::PrivateConstructor; + return {PrivateConstructor{}, std::forward<Args>(args)...}; +} + +template <class T, class U, class... Args> +constexpr folly::Optional<T> make_optional( + std::initializer_list<U> il, + Args&&... args) { + using PrivateConstructor = typename folly::Optional<T>::PrivateConstructor; + return {PrivateConstructor{}, il, std::forward<Args>(args)...}; +} + +/////////////////////////////////////////////////////////////////////////////// +// Comparisons. + +template <class U, class V> +constexpr bool operator==(const Optional<U>& a, const V& b) { + return a.hasValue() && a.value() == b; +} + +template <class U, class V> +constexpr bool operator!=(const Optional<U>& a, const V& b) { + return !(a == b); +} + +template <class U, class V> +constexpr bool operator==(const U& a, const Optional<V>& b) { + return b.hasValue() && b.value() == a; +} + +template <class U, class V> +constexpr bool operator!=(const U& a, const Optional<V>& b) { + return !(a == b); +} + +template <class U, class V> +constexpr bool operator==(const Optional<U>& a, const Optional<V>& b) { + if (a.hasValue() != b.hasValue()) { + return false; + } + if (a.hasValue()) { + return a.value() == b.value(); + } + return true; +} + +template <class U, class V> +constexpr bool operator!=(const Optional<U>& a, const Optional<V>& b) { + return !(a == b); +} + +template <class U, class V> +constexpr bool operator<(const Optional<U>& a, const Optional<V>& b) { + if (a.hasValue() != b.hasValue()) { + return a.hasValue() < b.hasValue(); + } + if (a.hasValue()) { + return a.value() < b.value(); + } + return false; +} + +template <class U, class V> +constexpr bool operator>(const Optional<U>& a, const Optional<V>& b) { + return b < a; +} + +template <class U, class V> +constexpr bool operator<=(const Optional<U>& a, const Optional<V>& b) { + return !(b < a); +} + +template <class U, class V> +constexpr bool operator>=(const Optional<U>& a, const Optional<V>& b) { + return !(a < b); +} + +// Suppress comparability of Optional<T> with T, despite implicit conversion. +template <class V> +bool operator<(const Optional<V>&, const V& other) = delete; +template <class V> +bool operator<=(const Optional<V>&, const V& other) = delete; +template <class V> +bool operator>=(const Optional<V>&, const V& other) = delete; +template <class V> +bool operator>(const Optional<V>&, const V& other) = delete; +template <class V> +bool operator<(const V& other, const Optional<V>&) = delete; +template <class V> +bool operator<=(const V& other, const Optional<V>&) = delete; +template <class V> +bool operator>=(const V& other, const Optional<V>&) = delete; +template <class V> +bool operator>(const V& other, const Optional<V>&) = delete; + +// Comparisons with none +template <class V> +constexpr bool operator==(const Optional<V>& a, None) noexcept { + return !a.hasValue(); +} +template <class V> +constexpr bool operator==(None, const Optional<V>& a) noexcept { + return !a.hasValue(); +} +template <class V> +constexpr bool operator<(const Optional<V>&, None) noexcept { + return false; +} +template <class V> +constexpr bool operator<(None, const Optional<V>& a) noexcept { + return a.hasValue(); +} +template <class V> +constexpr bool operator>(const Optional<V>& a, None) noexcept { + return a.hasValue(); +} +template <class V> +constexpr bool operator>(None, const Optional<V>&) noexcept { + return false; +} +template <class V> +constexpr bool operator<=(None, const Optional<V>&) noexcept { + return true; +} +template <class V> +constexpr bool operator<=(const Optional<V>& a, None) noexcept { + return !a.hasValue(); +} +template <class V> +constexpr bool operator>=(const Optional<V>&, None) noexcept { + return true; +} +template <class V> +constexpr bool operator>=(None, const Optional<V>& a) noexcept { + return !a.hasValue(); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace folly + +// Allow usage of Optional<T> in std::unordered_map and std::unordered_set +FOLLY_NAMESPACE_STD_BEGIN +template <class T> +struct hash<folly::Optional<T>> { + size_t operator()(folly::Optional<T> const& obj) const { + if (!obj.hasValue()) { + return 0; + } + return hash<typename remove_const<T>::type>()(*obj); + } +}; +FOLLY_NAMESPACE_STD_END + +// Enable the use of folly::Optional with `co_await` +// Inspired by https://github.com/toby-allsopp/coroutine_monad +#if FOLLY_HAS_COROUTINES +#include <experimental/coroutine> + +namespace folly { +namespace detail { +template <typename Value> +struct OptionalPromise; + +template <typename Value> +struct OptionalPromiseReturn { + Optional<Value> storage_; + OptionalPromise<Value>* promise_; + /* implicit */ OptionalPromiseReturn(OptionalPromise<Value>& promise) noexcept + : promise_(&promise) { + promise.value_ = &storage_; + } + OptionalPromiseReturn(OptionalPromiseReturn&& that) noexcept + : OptionalPromiseReturn{*that.promise_} {} + ~OptionalPromiseReturn() {} + /* implicit */ operator Optional<Value>() & { + return std::move(storage_); + } +}; + +template <typename Value> +struct OptionalPromise { + Optional<Value>* value_ = nullptr; + OptionalPromise() = default; + OptionalPromise(OptionalPromise const&) = delete; + // This should work regardless of whether the compiler generates: + // folly::Optional<Value> retobj{ p.get_return_object(); } // MSVC + // or: + // auto retobj = p.get_return_object(); // clang + OptionalPromiseReturn<Value> get_return_object() noexcept { + return *this; + } + std::experimental::suspend_never initial_suspend() const noexcept { + return {}; + } + std::experimental::suspend_never final_suspend() const { + return {}; + } + template <typename U> + void return_value(U&& u) { + *value_ = static_cast<U&&>(u); + } + void unhandled_exception() { + // Technically, throwing from unhandled_exception is underspecified: + // https://github.com/GorNishanov/CoroutineWording/issues/17 + throw; + } +}; + +template <typename Value> +struct OptionalAwaitable { + Optional<Value> o_; + bool await_ready() const noexcept { + return o_.hasValue(); + } + Value await_resume() { + return std::move(o_.value()); + } + + // Explicitly only allow suspension into an OptionalPromise + template <typename U> + void await_suspend( + std::experimental::coroutine_handle<OptionalPromise<U>> h) const { + // Abort the rest of the coroutine. resume() is not going to be called + h.destroy(); + } +}; +} // namespace detail + +template <typename Value> +detail::OptionalAwaitable<Value> +/* implicit */ operator co_await(Optional<Value> o) { + return {std::move(o)}; +} +} // namespace folly + +// This makes folly::Optional<Value> useable as a coroutine return type.. +namespace std { +namespace experimental { +template <typename Value, typename... Args> +struct coroutine_traits<folly::Optional<Value>, Args...> { + using promise_type = folly::detail::OptionalPromise<Value>; +}; +} // namespace experimental +} // namespace std +#endif // FOLLY_HAS_COROUTINES diff --git a/ios/Pods/Flipper-Folly/folly/Overload.h b/ios/Pods/Flipper-Folly/folly/Overload.h new file mode 100644 index 000000000..5b3e0b2dd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Overload.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> +#include <utility> + +/** + * folly implementation of `std::overload` like functionality + * + * Example: + * struct One {}; + * struct Two {}; + * boost::variant<One, Two> value; + * + * variant_match(value, + * [] (const One& one) { ... }, + * [] (const Two& two) { ... }); + */ + +namespace folly { + +namespace detail { +template <typename...> +struct Overload; + +template <typename Case, typename... Cases> +struct Overload<Case, Cases...> : Overload<Cases...>, Case { + Overload(Case c, Cases... cs) + : Overload<Cases...>(std::move(cs)...), Case(std::move(c)) {} + + using Case::operator(); + using Overload<Cases...>::operator(); +}; + +template <typename Case> +struct Overload<Case> : Case { + explicit Overload(Case c) : Case(std::move(c)) {} + + using Case::operator(); +}; +} // namespace detail + +/* + * Combine multiple `Cases` in one function object + */ +template <typename... Cases> +decltype(auto) overload(Cases&&... cases) { + return detail::Overload<typename std::decay<Cases>::type...>{ + std::forward<Cases>(cases)...}; +} + +/* + * Match `Variant` with one of the `Cases` + * + * Note: you can also use `[] (const auto&) {...}` as default case + * + */ +template <typename Variant, typename... Cases> +decltype(auto) variant_match(Variant&& variant, Cases&&... cases) { + return apply_visitor( + overload(std::forward<Cases>(cases)...), std::forward<Variant>(variant)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/PackedSyncPtr.h b/ios/Pods/Flipper-Folly/folly/PackedSyncPtr.h new file mode 100644 index 000000000..7a4894cda --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/PackedSyncPtr.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/synchronization/SmallLocks.h> + +#if !FOLLY_X64 && !FOLLY_PPC64 && !FOLLY_AARCH64 +#error "PackedSyncPtr is x64, ppc64 or aarch64 specific code." +#endif + +/* + * An 8-byte pointer with an integrated spin lock and 15-bit integer + * (you can use this for a size of the allocation, if you want, or + * something else, or nothing). + * + * This is using an x64-specific detail about the effective virtual + * address space. Long story short: the upper two bytes of all our + * pointers will be zero in reality---and if you have a couple billion + * such pointers in core, it makes pretty good sense to try to make + * use of that memory. The exact details can be perused here: + * + * http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses + * + * This is not a "smart" pointer: nothing automagical is going on + * here. Locking is up to the user. Resource deallocation is up to + * the user. Locks are never acquired or released outside explicit + * calls to lock() and unlock(). + * + * Change the value of the raw pointer with set(), but you must hold + * the lock when calling this function if multiple threads could be + * using this class. + * + * TODO(jdelong): should we use the low order bit for the lock, so we + * get a whole 16-bits for our integer? (There's also 2 more bits + * down there if the pointer comes from malloc.) + * + * @author Spencer Ahrens <sahrens@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +namespace folly { + +template <class T> +class PackedSyncPtr { + // This just allows using this class even with T=void. Attempting + // to use the operator* or operator[] on a PackedSyncPtr<void> will + // still properly result in a compile error. + typedef typename std::add_lvalue_reference<T>::type reference; + + public: + /* + * If you default construct one of these, you must call this init() + * function before using it. + * + * (We are avoiding a constructor to ensure gcc allows us to put + * this class in packed structures.) + */ + void init(T* initialPtr = nullptr, uint16_t initialExtra = 0) { + auto intPtr = reinterpret_cast<uintptr_t>(initialPtr); + CHECK(!(intPtr >> 48)); + data_.init(intPtr); + setExtra(initialExtra); + } + + /* + * Sets a new pointer. You must hold the lock when calling this + * function, or else be able to guarantee no other threads could be + * using this PackedSyncPtr<>. + */ + void set(T* t) { + auto intPtr = reinterpret_cast<uintptr_t>(t); + auto shiftedExtra = uintptr_t(extra()) << 48; + CHECK(!(intPtr >> 48)); + data_.setData(intPtr | shiftedExtra); + } + + /* + * Get the pointer. + * + * You can call any of these without holding the lock, with the + * normal types of behavior you'll get on x64 from reading a pointer + * without locking. + */ + T* get() const { + return reinterpret_cast<T*>(data_.getData() & (-1ull >> 16)); + } + T* operator->() const { + return get(); + } + reference operator*() const { + return *get(); + } + reference operator[](std::ptrdiff_t i) const { + return get()[i]; + } + + // Synchronization (logically const, even though this mutates our + // locked state: you can lock a const PackedSyncPtr<T> to read it). + void lock() const { + data_.lock(); + } + void unlock() const { + data_.unlock(); + } + bool try_lock() const { + return data_.try_lock(); + } + + /* + * Access extra data stored in unused bytes of the pointer. + * + * It is ok to call this without holding the lock. + */ + uint16_t extra() const { + return data_.getData() >> 48; + } + + /* + * Don't try to put anything into this that has the high bit set: + * that's what we're using for the mutex. + * + * Don't call this without holding the lock. + */ + void setExtra(uint16_t extra) { + CHECK(!(extra & 0x8000)); + auto ptr = data_.getData() & (-1ull >> 16); + data_.setData((uintptr_t(extra) << 48) | ptr); + } + + private: + PicoSpinLock<uintptr_t> data_; +} FOLLY_PACK_ATTR; + +static_assert( + std::is_pod<PackedSyncPtr<void>>::value, + "PackedSyncPtr must be kept a POD type."); +static_assert( + sizeof(PackedSyncPtr<void>) == 8, + "PackedSyncPtr should be only 8 bytes---something is " + "messed up"); + +template <typename T> +std::ostream& operator<<(std::ostream& os, const PackedSyncPtr<T>& ptr) { + os << "PackedSyncPtr(" << ptr.get() << ", " << ptr.extra() << ")"; + return os; +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Padded.h b/ios/Pods/Flipper-Folly/folly/Padded.h new file mode 100644 index 000000000..005b42a03 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Padded.h @@ -0,0 +1,559 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <functional> +#include <iterator> +#include <limits> +#include <type_traits> + +#include <boost/iterator/iterator_adaptor.hpp> + +#include <folly/Portability.h> +#include <folly/Traits.h> + +/** + * Code that aids in storing data aligned on block (possibly cache-line) + * boundaries, perhaps with padding. + * + * Class Node represents one block. Given an iterator to a container of + * Node, class Iterator encapsulates an iterator to the underlying elements. + * Adaptor converts a sequence of Node into a sequence of underlying elements + * (not fully compatible with STL container requirements, see comments + * near the Node class declaration). + */ + +namespace folly { +namespace padded { + +/** + * A Node is a fixed-size container of as many objects of type T as would + * fit in a region of memory of size NS. The last NS % sizeof(T) + * bytes are ignored and uninitialized. + * + * Node only works for trivial types, which is usually not a concern. This + * is intentional: Node itself is trivial, which means that it can be + * serialized / deserialized using a simple memcpy. + */ +template <class T, size_t NS, class Enable = void> +class Node; + +namespace detail { +// Shortcut to avoid writing the long enable_if expression every time +template <class T, size_t NS, class Enable = void> +struct NodeValid; +template <class T, size_t NS> +struct NodeValid< + T, + NS, + typename std::enable_if<( + std::is_trivial<T>::value && sizeof(T) <= NS && + NS % alignof(T) == 0)>::type> { + typedef void type; +}; +} // namespace detail + +template <class T, size_t NS> +class Node<T, NS, typename detail::NodeValid<T, NS>::type> { + public: + typedef T value_type; + static constexpr size_t kNodeSize = NS; + static constexpr size_t kElementCount = NS / sizeof(T); + static constexpr size_t kPaddingBytes = NS % sizeof(T); + + T* data() { + return storage_.data; + } + const T* data() const { + return storage_.data; + } + + bool operator==(const Node& other) const { + return memcmp(data(), other.data(), sizeof(T) * kElementCount) == 0; + } + bool operator!=(const Node& other) const { + return !(*this == other); + } + + /** + * Return the number of nodes needed to represent n values. Rounds up. + */ + static constexpr size_t nodeCount(size_t n) { + return (n + kElementCount - 1) / kElementCount; + } + + /** + * Return the total byte size needed to represent n values, rounded up + * to the nearest full node. + */ + static constexpr size_t paddedByteSize(size_t n) { + return nodeCount(n) * NS; + } + + /** + * Return the number of bytes used for padding n values. + * Note that, even if n is a multiple of kElementCount, this may + * return non-zero if kPaddingBytes != 0, as the padding at the end of + * the last node is not included in the result. + */ + static constexpr size_t paddingBytes(size_t n) { + return ( + n ? (kPaddingBytes + + (kElementCount - 1 - (n - 1) % kElementCount) * sizeof(T)) + : 0); + } + + /** + * Return the minimum byte size needed to represent n values. + * Does not round up. Even if n is a multiple of kElementCount, this + * may be different from paddedByteSize() if kPaddingBytes != 0, as + * the padding at the end of the last node is not included in the result. + * Note that the calculation below works for n=0 correctly (returns 0). + */ + static constexpr size_t unpaddedByteSize(size_t n) { + return paddedByteSize(n) - paddingBytes(n); + } + + private: + union Storage { + unsigned char bytes[NS]; + T data[kElementCount]; + } storage_; +}; + +// We must define kElementCount and kPaddingBytes to work around a bug +// in gtest that odr-uses them. +template <class T, size_t NS> +constexpr size_t + Node<T, NS, typename detail::NodeValid<T, NS>::type>::kNodeSize; +template <class T, size_t NS> +constexpr size_t + Node<T, NS, typename detail::NodeValid<T, NS>::type>::kElementCount; +template <class T, size_t NS> +constexpr size_t + Node<T, NS, typename detail::NodeValid<T, NS>::type>::kPaddingBytes; + +template <class Iter> +class Iterator; + +namespace detail { + +template <typename Void, typename Container, typename... Args> +struct padded_emplace_back_or_push_back_ { + static decltype(auto) go(Container& container, Args&&... args) { + using Value = typename Container::value_type; + return container.push_back(Value(std::forward<Args>(args)...)); + } +}; + +template <typename Container, typename... Args> +struct padded_emplace_back_or_push_back_< + void_t<decltype( + std::declval<Container&>().emplace_back(std::declval<Args>()...))>, + Container, + Args...> { + static decltype(auto) go(Container& container, Args&&... args) { + return container.emplace_back(std::forward<Args>(args)...); + } +}; + +template <typename Container, typename... Args> +decltype(auto) padded_emplace_back_or_push_back( + Container& container, + Args&&... args) { + using impl = padded_emplace_back_or_push_back_<void, Container, Args...>; + return impl::go(container, std::forward<Args>(args)...); +} + +// Helper class to transfer the constness from From (a lvalue reference) +// and create a lvalue reference to To. +// +// TransferReferenceConstness<const string&, int> -> const int& +// TransferReferenceConstness<string&, int> -> int& +// TransferReferenceConstness<string&, const int> -> const int& +template <class From, class To, class Enable = void> +struct TransferReferenceConstness; + +template <class From, class To> +struct TransferReferenceConstness< + From, + To, + typename std::enable_if<std::is_const< + typename std::remove_reference<From>::type>::value>::type> { + typedef typename std::add_lvalue_reference< + typename std::add_const<To>::type>::type type; +}; + +template <class From, class To> +struct TransferReferenceConstness< + From, + To, + typename std::enable_if<!std::is_const< + typename std::remove_reference<From>::type>::value>::type> { + typedef typename std::add_lvalue_reference<To>::type type; +}; + +// Helper class template to define a base class for Iterator (below) and save +// typing. +template <class Iter> +struct IteratorBase { + typedef boost::iterator_adaptor< + // CRTC + Iterator<Iter>, + // Base iterator type + Iter, + // Value type + typename std::iterator_traits<Iter>::value_type::value_type, + // Category or traversal + boost::use_default, + // Reference type + typename detail::TransferReferenceConstness< + typename std::iterator_traits<Iter>::reference, + typename std::iterator_traits<Iter>::value_type::value_type>::type> + type; +}; + +} // namespace detail + +/** + * Wrapper around iterators to Node to return iterators to the underlying + * node elements. + */ +template <class Iter> +class Iterator : public detail::IteratorBase<Iter>::type { + typedef typename detail::IteratorBase<Iter>::type Super; + + public: + typedef typename std::iterator_traits<Iter>::value_type Node; + + Iterator() : pos_(0) {} + + explicit Iterator(Iter base) : Super(base), pos_(0) {} + + // Return the current node and the position inside the node + const Node& node() const { + return *this->base_reference(); + } + size_t pos() const { + return pos_; + } + + private: + typename Super::reference dereference() const { + return (*this->base_reference()).data()[pos_]; + } + + bool equal(const Iterator& other) const { + return ( + this->base_reference() == other.base_reference() && pos_ == other.pos_); + } + + void advance(typename Super::difference_type n) { + constexpr ssize_t elementCount = Node::kElementCount; // signed! + ssize_t newPos = pos_ + n; + if (newPos >= 0 && newPos < elementCount) { + pos_ = newPos; + return; + } + ssize_t nblocks = newPos / elementCount; + newPos %= elementCount; + if (newPos < 0) { + --nblocks; // negative + newPos += elementCount; + } + this->base_reference() += nblocks; + pos_ = newPos; + } + + void increment() { + if (++pos_ == Node::kElementCount) { + ++this->base_reference(); + pos_ = 0; + } + } + + void decrement() { + if (--pos_ == -1) { + --this->base_reference(); + pos_ = Node::kElementCount - 1; + } + } + + typename Super::difference_type distance_to(const Iterator& other) const { + constexpr ssize_t elementCount = Node::kElementCount; // signed! + ssize_t nblocks = + std::distance(this->base_reference(), other.base_reference()); + return nblocks * elementCount + (other.pos_ - pos_); + } + + friend class boost::iterator_core_access; + ssize_t pos_; // signed for easier advance() implementation +}; + +/** + * Given a container to Node, return iterators to the first element in + * the first Node / one past the last element in the last Node. + * Note that the last node is assumed to be full; if that's not the case, + * subtract from end() as appropriate. + */ + +template <class Container> +Iterator<typename Container::const_iterator> cbegin(const Container& c) { + return Iterator<typename Container::const_iterator>(std::begin(c)); +} + +template <class Container> +Iterator<typename Container::const_iterator> cend(const Container& c) { + return Iterator<typename Container::const_iterator>(std::end(c)); +} + +template <class Container> +Iterator<typename Container::const_iterator> begin(const Container& c) { + return cbegin(c); +} + +template <class Container> +Iterator<typename Container::const_iterator> end(const Container& c) { + return cend(c); +} + +template <class Container> +Iterator<typename Container::iterator> begin(Container& c) { + return Iterator<typename Container::iterator>(std::begin(c)); +} + +template <class Container> +Iterator<typename Container::iterator> end(Container& c) { + return Iterator<typename Container::iterator>(std::end(c)); +} + +/** + * Adaptor around a STL sequence container. + * + * Converts a sequence of Node into a sequence of its underlying elements + * (with enough functionality to make it useful, although it's not fully + * compatible with the STL containre requiremenets, see below). + * + * Provides iterators (of the same category as those of the underlying + * container), size(), front(), back(), push_back(), pop_back(), and const / + * non-const versions of operator[] (if the underlying container supports + * them). Does not provide push_front() / pop_front() or arbitrary insert / + * emplace / erase. Also provides reserve() / capacity() if supported by the + * underlying container. + * + * Yes, it's called Adaptor, not Adapter, as that's the name used by the STL + * and by boost. Deal with it. + * + * Internally, we hold a container of Node and the number of elements in + * the last block. We don't keep empty blocks, so the number of elements in + * the last block is always between 1 and Node::kElementCount (inclusive). + * (this is true if the container is empty as well to make push_back() simpler, + * see the implementation of the size() method for details). + */ +template <class Container> +class Adaptor { + public: + typedef typename Container::value_type Node; + typedef typename Node::value_type value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef Iterator<typename Container::iterator> iterator; + typedef Iterator<typename Container::const_iterator> const_iterator; + typedef typename const_iterator::difference_type difference_type; + typedef typename Container::size_type size_type; + + static constexpr size_t kElementsPerNode = Node::kElementCount; + // Constructors + Adaptor() : lastCount_(Node::kElementCount) {} + explicit Adaptor(Container c, size_t lastCount = Node::kElementCount) + : c_(std::move(c)), lastCount_(lastCount) {} + explicit Adaptor(size_t n, const value_type& value = value_type()) + : c_(Node::nodeCount(n), fullNode(value)) { + const auto count = n % Node::kElementCount; + lastCount_ = count != 0 ? count : Node::kElementCount; + } + + Adaptor(const Adaptor&) = default; + Adaptor& operator=(const Adaptor&) = default; + Adaptor(Adaptor&& other) noexcept + : c_(std::move(other.c_)), lastCount_(other.lastCount_) { + other.lastCount_ = Node::kElementCount; + } + Adaptor& operator=(Adaptor&& other) { + if (this != &other) { + c_ = std::move(other.c_); + lastCount_ = other.lastCount_; + other.lastCount_ = Node::kElementCount; + } + return *this; + } + + // Iterators + const_iterator cbegin() const { + return const_iterator(c_.begin()); + } + const_iterator cend() const { + auto it = const_iterator(c_.end()); + if (lastCount_ != Node::kElementCount) { + it -= (Node::kElementCount - lastCount_); + } + return it; + } + const_iterator begin() const { + return cbegin(); + } + const_iterator end() const { + return cend(); + } + iterator begin() { + return iterator(c_.begin()); + } + iterator end() { + auto it = iterator(c_.end()); + if (lastCount_ != Node::kElementCount) { + it -= difference_type(Node::kElementCount - lastCount_); + } + return it; + } + void swap(Adaptor& other) { + using std::swap; + swap(c_, other.c_); + swap(lastCount_, other.lastCount_); + } + bool empty() const { + return c_.empty(); + } + size_type size() const { + return ( + c_.empty() ? 0 : (c_.size() - 1) * Node::kElementCount + lastCount_); + } + size_type max_size() const { + return ( + (c_.max_size() <= + std::numeric_limits<size_type>::max() / Node::kElementCount) + ? c_.max_size() * Node::kElementCount + : std::numeric_limits<size_type>::max()); + } + + const value_type& front() const { + assert(!empty()); + return c_.front().data()[0]; + } + value_type& front() { + assert(!empty()); + return c_.front().data()[0]; + } + + const value_type& back() const { + assert(!empty()); + return c_.back().data()[lastCount_ - 1]; + } + value_type& back() { + assert(!empty()); + return c_.back().data()[lastCount_ - 1]; + } + + template <typename... Args> + void emplace_back(Args&&... args) { + new (allocate_back()) value_type(std::forward<Args>(args)...); + } + + void push_back(value_type x) { + emplace_back(std::move(x)); + } + + void pop_back() { + assert(!empty()); + if (--lastCount_ == 0) { + c_.pop_back(); + lastCount_ = Node::kElementCount; + } + } + + void clear() { + c_.clear(); + lastCount_ = Node::kElementCount; + } + + void reserve(size_type n) { + assert(n >= 0); + c_.reserve(Node::nodeCount(n)); + } + + size_type capacity() const { + return c_.capacity() * Node::kElementCount; + } + + const value_type& operator[](size_type idx) const { + return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount]; + } + value_type& operator[](size_type idx) { + return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount]; + } + + /** + * Return the underlying container and number of elements in the last block, + * and clear *this. Useful when you want to process the data as Nodes + * (again) and want to avoid copies. + */ + std::pair<Container, size_t> move() { + std::pair<Container, size_t> p(std::move(c_), lastCount_); + lastCount_ = Node::kElementCount; + return p; + } + + /** + * Return a const reference to the underlying container and the current + * number of elements in the last block. + */ + std::pair<const Container&, size_t> peek() const { + return std::make_pair(std::cref(c_), lastCount_); + } + + void padToFullNode(const value_type& padValue) { + // the if is necessary because c_ may be empty so we can't call c_.back() + if (lastCount_ != Node::kElementCount) { + auto last = c_.back().data(); + std::fill(last + lastCount_, last + Node::kElementCount, padValue); + lastCount_ = Node::kElementCount; + } + } + + private: + value_type* allocate_back() { + if (lastCount_ == Node::kElementCount) { + detail::padded_emplace_back_or_push_back(c_); + lastCount_ = 0; + } + return &c_.back().data()[lastCount_++]; + } + + static Node fullNode(const value_type& value) { + Node n; + std::fill(n.data(), n.data() + kElementsPerNode, value); + return n; + } + Container c_; // container of Nodes + size_t lastCount_; // number of elements in last Node +}; + +} // namespace padded +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Poly-inl.h b/ios/Pods/Flipper-Folly/folly/Poly-inl.h new file mode 100644 index 000000000..205c2aef5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Poly-inl.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace folly { +namespace detail { + +template <class I> +inline PolyVal<I>::PolyVal(PolyVal&& that) noexcept { + that.vptr_->ops_(Op::eMove, &that, static_cast<Data*>(this)); + vptr_ = std::exchange(that.vptr_, vtable<I>()); +} + +template <class I> +inline PolyVal<I>::PolyVal(PolyOrNonesuch const& that) { + that.vptr_->ops_( + Op::eCopy, const_cast<Data*>(that._data_()), PolyAccess::data(*this)); + vptr_ = that.vptr_; +} + +template <class I> +inline PolyVal<I>::~PolyVal() { + vptr_->ops_(Op::eNuke, this, nullptr); +} + +template <class I> +inline Poly<I>& PolyVal<I>::operator=(PolyVal that) noexcept { + vptr_->ops_(Op::eNuke, _data_(), nullptr); + that.vptr_->ops_(Op::eMove, that._data_(), _data_()); + vptr_ = std::exchange(that.vptr_, vtable<I>()); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>> +inline PolyVal<I>::PolyVal(T&& t) { + using U = std::decay_t<T>; + static_assert( + std::is_copy_constructible<U>::value || !Copyable::value, + "This Poly<> requires copyability, and the source object is not " + "copyable"); + // The static and dynamic types should match; otherwise, this will slice. + assert(typeid(t) == typeid(std::decay_t<T>) || + !"Dynamic and static exception types don't match. Object would " + "be sliced when storing in Poly."); + if (inSitu<U>()) { + auto const buff = static_cast<void*>(&_data_()->buff_); + ::new (buff) U(static_cast<T&&>(t)); + } else { + _data_()->pobj_ = new U(static_cast<T&&>(t)); + } + vptr_ = vtableFor<I, U>(); +} + +template <class I> +template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>> +inline PolyVal<I>::PolyVal(Poly<I2> that) { + static_assert( + !Copyable::value || std::is_copy_constructible<Poly<I2>>::value, + "This Poly<> requires copyability, and the source object is not " + "copyable"); + auto* that_vptr = PolyAccess::vtable(that); + if (that_vptr->state_ != State::eEmpty) { + that_vptr->ops_(Op::eMove, PolyAccess::data(that), _data_()); + vptr_ = &select<I>(*std::exchange(that_vptr, vtable<std::decay_t<I2>>())); + } +} + +template <class I> +template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>> +inline Poly<I>& PolyVal<I>::operator=(T&& t) { + *this = PolyVal(static_cast<T&&>(t)); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>> +inline Poly<I>& PolyVal<I>::operator=(Poly<I2> that) { + *this = PolyVal(std::move(that)); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +inline void PolyVal<I>::swap(Poly<I>& that) noexcept { + switch (vptr_->state_) { + case State::eEmpty: + *this = std::move(that); + break; + case State::eOnHeap: + if (State::eOnHeap == that.vptr_->state_) { + std::swap(_data_()->pobj_, that._data_()->pobj_); + std::swap(vptr_, that.vptr_); + return; + } + FOLLY_FALLTHROUGH; + case State::eInSitu: + std::swap( + *this, static_cast<PolyVal<I>&>(that)); // NOTE: qualified, not ADL + } +} + +template <class I> +inline AddCvrefOf<PolyRoot<I>, I>& PolyRef<I>::_polyRoot_() const noexcept { + return const_cast<AddCvrefOf<PolyRoot<I>, I>&>( + static_cast<PolyRoot<I> const&>(*this)); +} + +template <class I> +constexpr RefType PolyRef<I>::refType() noexcept { + using J = std::remove_reference_t<I>; + return std::is_rvalue_reference<I>::value + ? RefType::eRvalue + : std::is_const<J>::value ? RefType::eConstLvalue : RefType::eLvalue; +} + +template <class I> +template <class That, class I2> +inline PolyRef<I>::PolyRef(That&& that, Type<I2>) { + auto* that_vptr = PolyAccess::vtable(PolyAccess::root(that)); + detail::State const that_state = that_vptr->state_; + if (that_state == State::eEmpty) { + throw BadPolyAccess(); + } + auto* that_data = PolyAccess::data(PolyAccess::root(that)); + _data_()->pobj_ = that_state == State::eInSitu + ? const_cast<void*>(static_cast<void const*>(&that_data->buff_)) + : that_data->pobj_; + this->vptr_ = &select<std::decay_t<I>>( + *static_cast<VTable<std::decay_t<I2>> const*>(that_vptr->ops_( + Op::eRefr, nullptr, reinterpret_cast<void*>(refType())))); +} + +template <class I> +inline PolyRef<I>::PolyRef(PolyRef const& that) noexcept { + _data_()->pobj_ = that._data_()->pobj_; + this->vptr_ = that.vptr_; +} + +template <class I> +inline Poly<I>& PolyRef<I>::operator=(PolyRef const& that) noexcept { + _data_()->pobj_ = that._data_()->pobj_; + this->vptr_ = that.vptr_; + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>> +inline PolyRef<I>::PolyRef(T&& t) noexcept { + _data_()->pobj_ = + const_cast<void*>(static_cast<void const*>(std::addressof(t))); + this->vptr_ = vtableFor<std::decay_t<I>, AddCvrefOf<std::decay_t<T>, I>>(); +} + +template <class I> +template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>> +inline PolyRef<I>::PolyRef(Poly<I2>&& that) noexcept( + std::is_reference<I2>::value) + : PolyRef{that, Type<I2>{}} { + static_assert( + Disjunction<std::is_reference<I2>, std::is_rvalue_reference<I>>::value, + "Attempting to construct a Poly that is a reference to a temporary. " + "This is probably a mistake."); +} + +template <class I> +template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>> +inline Poly<I>& PolyRef<I>::operator=(T&& t) noexcept { + *this = PolyRef(static_cast<T&&>(t)); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>> +inline Poly<I>& PolyRef<I>::operator=(Poly<I2>&& that) noexcept( + std::is_reference<I2>::value) { + *this = PolyRef(std::move(that)); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int>> +inline Poly<I>& PolyRef<I>::operator=(Poly<I2>& that) noexcept( + std::is_reference<I2>::value) { + *this = PolyRef(that); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int>> +inline Poly<I>& PolyRef<I>::operator=(Poly<I2> const& that) noexcept( + std::is_reference<I2>::value) { + *this = PolyRef(that); + return static_cast<Poly<I>&>(*this); +} + +template <class I> +inline void PolyRef<I>::swap(Poly<I>& that) noexcept { + std::swap(_data_()->pobj_, that._data_()->pobj_); + std::swap(this->vptr_, that.vptr_); +} + +template <class I> +inline AddCvrefOf<PolyImpl<I>, I>& PolyRef<I>::get() const noexcept { + return const_cast<AddCvrefOf<PolyImpl<I>, I>&>( + static_cast<PolyImpl<I> const&>(*this)); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Poly.h b/ios/Pods/Flipper-Folly/folly/Poly.h new file mode 100644 index 000000000..cccf1a83b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Poly.h @@ -0,0 +1,1171 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: [x] "cast" from Poly<C&> to Poly<C&&> +// TODO: [ ] copy/move from Poly<C&>/Poly<C&&> to Poly<C> +// TODO: [ ] copy-on-write? +// TODO: [ ] down- and cross-casting? (Possible?) +// TODO: [ ] shared ownership? (Dubious.) +// TODO: [ ] can games be played with making the VTable a member of a struct +// with strange alignment such that the address of the VTable can +// be used to tell whether the object is stored in-situ or not? + +#pragma once + +#include <cassert> +#include <new> +#include <type_traits> +#include <typeinfo> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/CppAttributes.h> +#include <folly/Traits.h> +#include <folly/detail/TypeList.h> +#include <folly/lang/Assume.h> + +#if !defined(__cpp_inline_variables) +#define FOLLY_INLINE_CONSTEXPR constexpr +#else +#define FOLLY_INLINE_CONSTEXPR inline constexpr +#endif + +#include <folly/PolyException.h> +#include <folly/detail/PolyDetail.h> + +namespace folly { +template <class I> +struct Poly; + +// MSVC workaround +template <class Node, class Tfx, class Access> +struct PolySelf_ { + using type = decltype(Access::template self_<Node, Tfx>()); +}; + +/** + * Within the definition of interface `I`, `PolySelf<Base>` is an alias for + * the instance of `Poly` that is currently being instantiated. It is + * one of: `Poly<J>`, `Poly<J&&>`, `Poly<J&>`, or `Poly<J const&>`; where + * `J` is either `I` or some interface that extends `I`. + * + * It can be used within interface definitions to declare members that accept + * other `Poly` objects of the same type as `*this`. + * + * The first parameter may optionally be cv- and/or reference-qualified, in + * which case, the qualification is applies to the type of the interface in the + * resulting `Poly<>` instance. The second template parameter controls whether + * or not the interface is decayed before the cv-ref qualifiers of the first + * argument are applied. For example, given the following: + * + * struct Foo { + * template <class Base> + * struct Interface : Base { + * using A = PolySelf<Base>; + * using B = PolySelf<Base &>; + * using C = PolySelf<Base const &>; + * using X = PolySelf<Base, PolyDecay>; + * using Y = PolySelf<Base &, PolyDecay>; + * using Z = PolySelf<Base const &, PolyDecay>; + * }; + * // ... + * }; + * struct Bar : PolyExtends<Foo> { + * // ... + * }; + * + * Then for `Poly<Bar>`, the typedefs are aliases for the following types: + * - `A` is `Poly<Bar>` + * - `B` is `Poly<Bar &>` + * - `C` is `Poly<Bar const &>` + * - `X` is `Poly<Bar>` + * - `Y` is `Poly<Bar &>` + * - `Z` is `Poly<Bar const &>` + * + * And for `Poly<Bar &>`, the typedefs are aliases for the following types: + * - `A` is `Poly<Bar &>` + * - `B` is `Poly<Bar &>` + * - `C` is `Poly<Bar &>` + * - `X` is `Poly<Bar>` + * - `Y` is `Poly<Bar &>` + * - `Z` is `Poly<Bar const &>` + */ +template < + class Node, + class Tfx = detail::MetaIdentity, + class Access = detail::PolyAccess> +using PolySelf = _t<PolySelf_<Node, Tfx, Access>>; + +/** + * When used in conjunction with `PolySelf`, controls how to construct `Poly` + * types related to the one currently being instantiated. + * + * \sa PolySelf + */ +using PolyDecay = detail::MetaQuote<std::decay_t>; + +#if !FOLLY_POLY_NTTP_AUTO + +/** + * Use `FOLLY_POLY_MEMBERS(MEMS...)` on pre-C++17 compilers to specify a + * comma-separated list of member function bindings. + * + * For example: + * + * struct IFooBar { + * template <class Base> + * struct Interface : Base { + * int foo() const { return folly::poly_call<0>(*this); } + * void bar() { folly::poly_call<1>(*this); } + * }; + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&T::foo, &T::bar); + * }; + */ +#define FOLLY_POLY_MEMBERS(...) \ + typename decltype(::folly::detail::deduceMembers( \ + __VA_ARGS__))::template Members<__VA_ARGS__> + +/** + * Use `FOLLY_POLY_MEMBER(SIG, MEM)` on pre-C++17 compilers to specify a member + * function binding that needs to be disambiguated because of overloads. `SIG` + * should the (possibly const-qualified) signature of the `MEM` member function + * pointer. + * + * For example: + * + * struct IFoo { + * template <class Base> struct Interface : Base { + * int foo() const { return folly::poly_call<0>(*this); } + * }; + * template <class T> using Members = FOLLY_POLY_MEMBERS( + * // This works even if T::foo is overloaded: + * FOLLY_POLY_MEMBER(int()const, &T::foo) + * ); + * }; + */ +#define FOLLY_POLY_MEMBER(SIG, MEM) \ + ::folly::detail::MemberDef< \ + ::folly::detail::Member<decltype(::folly::sig<SIG>(MEM)), MEM>>::value + +/** + * A list of member function bindings. + */ +template <class... Ts> +using PolyMembers = detail::TypeList<Ts...>; + +#else +#define FOLLY_POLY_MEMBER(SIG, MEM) ::folly::sig<SIG>(MEM) +#define FOLLY_POLY_MEMBERS(...) ::folly::PolyMembers<__VA_ARGS__> + +template <auto... Ps> +struct PolyMembers {}; + +#endif + +/** + * Used in the definition of a `Poly` interface to say that the current + * interface is an extension of a set of zero or more interfaces. + * + * Example: + * + * struct IFoo { + * template <class Base> struct Interface : Base { + * void foo() { folly::poly_call<0>(*this); } + * }; + * template <class T> using Members = FOLLY_POLY_MEMBERS(&T::foo); + * } + * struct IBar : PolyExtends<IFoo> { + * template <class Base> struct Interface : Base { + * void bar(int i) { folly::poly_call<0>(*this, i); } + * }; + * template <class T> using Members = FOLLY_POLY_MEMBERS(&T::bar); + * } + */ +template <class... I> +struct PolyExtends : virtual I... { + using Subsumptions = detail::TypeList<I...>; + + template <class Base> + struct Interface : Base { + Interface() = default; + using Base::Base; + }; + + template <class...> + using Members = PolyMembers<>; +}; + +//////////////////////////////////////////////////////////////////////////////// +/** + * Call the N-th member of the currently-being-defined interface. When the + * first parameter is an object of type `PolySelf<Base>` (as opposed to `*this`) + * you must explicitly specify which interface through which to dispatch. + * For instance: + * + * struct IAddable { + * template <class Base> + * struct Interface : Base { + * friend PolySelf<Base, Decay> + * operator+(PolySelf<Base> const& a, PolySelf<Base> const& b) { + * return folly::poly_call<0, IAddable>(a, b); + * } + * }; + * template <class T> + * static auto plus_(T const& a, T const& b) -> decltype(a + b) { + * return a + b; + * } + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&plus_<std::decay_t<T>>); + * }; + * + * \sa PolySelf + */ +template <std::size_t N, typename This, typename... As> +auto poly_call(This&& _this, As&&... as) + -> decltype(detail::PolyAccess::call<N>( + static_cast<This&&>(_this), + static_cast<As&&>(as)...)) { + return detail::PolyAccess::call<N>( + static_cast<This&&>(_this), static_cast<As&&>(as)...); +} + +/// \overload +template <std::size_t N, class I, class Tail, typename... As> +decltype(auto) poly_call(detail::PolyNode<I, Tail>&& _this, As&&... as) { + using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>; + return detail::PolyAccess::call<N>( + static_cast<This&&>(_this), static_cast<As&&>(as)...); +} + +/// \overload +template <std::size_t N, class I, class Tail, typename... As> +decltype(auto) poly_call(detail::PolyNode<I, Tail>& _this, As&&... as) { + using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>; + return detail::PolyAccess::call<N>( + static_cast<This&>(_this), static_cast<As&&>(as)...); +} + +/// \overload +template <std::size_t N, class I, class Tail, typename... As> +decltype(auto) poly_call(detail::PolyNode<I, Tail> const& _this, As&&... as) { + using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>; + return detail::PolyAccess::call<N>( + static_cast<This const&>(_this), static_cast<As&&>(as)...); +} + +/// \overload +template < + std::size_t N, + class I, + class Poly, + typename... As, + std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0> +auto poly_call(Poly&& _this, As&&... as) -> decltype(poly_call<N, I>( + static_cast<Poly&&>(_this).get(), + static_cast<As&&>(as)...)) { + return poly_call<N, I>( + static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...); +} + +/// \cond +/// \overload +template <std::size_t N, class I, typename... As> +[[noreturn]] detail::Bottom poly_call(detail::ArchetypeBase const&, As&&...) { + assume_unreachable(); +} +/// \endcond + +//////////////////////////////////////////////////////////////////////////////// +/** + * Try to cast the `Poly` object to the requested type. If the `Poly` stores an + * object of that type, return a reference to the object; otherwise, throw an + * exception. + * \tparam T The (unqualified) type to which to cast the `Poly` object. + * \tparam Poly The type of the `Poly` object. + * \param that The `Poly` object to be cast. + * \return A reference to the `T` object stored in or refered to by `that`. + * \throw BadPolyAccess if `that` is empty. + * \throw BadPolyCast if `that` does not store or refer to an object of type + * `T`. + */ +template <class T, class I> +detail::AddCvrefOf<T, I>&& poly_cast(detail::PolyRoot<I>&& that) { + return detail::PolyAccess::cast<T>(std::move(that)); +} + +/// \overload +template <class T, class I> +detail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>& that) { + return detail::PolyAccess::cast<T>(that); +} + +/// \overload +template <class T, class I> +detail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const& that) { + return detail::PolyAccess::cast<T>(that); +} + +/// \cond +/// \overload +template <class T, class I> +[[noreturn]] detail::AddCvrefOf<T, I>&& poly_cast(detail::ArchetypeRoot<I>&&) { + assume_unreachable(); +} + +/// \overload +template <class T, class I> +[[noreturn]] detail::AddCvrefOf<T, I>& poly_cast(detail::ArchetypeRoot<I>&) { + assume_unreachable(); +} + +/// \overload +template <class T, class I> +[[noreturn]] detail::AddCvrefOf<T, I> const& poly_cast( + detail::ArchetypeRoot<I> const&) { + assume_unreachable(); +} +/// \endcond + +/// \overload +template < + class T, + class Poly, + std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0> +constexpr auto poly_cast(Poly&& that) + -> decltype(poly_cast<T>(std::declval<Poly>().get())) { + return poly_cast<T>(static_cast<Poly&&>(that).get()); +} + +//////////////////////////////////////////////////////////////////////////////// +/** + * Returns a reference to the `std::type_info` object corresponding to the + * object currently stored in `that`. If `that` is empty, returns + * `typeid(void)`. + */ +template <class I> +std::type_info const& poly_type(detail::PolyRoot<I> const& that) noexcept { + return detail::PolyAccess::type(that); +} + +/// \cond +/// \overload +[[noreturn]] inline std::type_info const& poly_type( + detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +/// \overload +template <class Poly, std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0> +constexpr auto poly_type(Poly const& that) noexcept + -> decltype(poly_type(that.get())) { + return poly_type(that.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +/** + * Returns `true` if `that` is not currently storing an object; `false`, + * otherwise. + */ +template <class I> +bool poly_empty(detail::PolyRoot<I> const& that) noexcept { + return detail::State::eEmpty == detail::PolyAccess::vtable(that)->state_; +} + +/// \overload +template <class I> +constexpr bool poly_empty(detail::PolyRoot<I&&> const&) noexcept { + return false; +} + +/// \overload +template <class I> +constexpr bool poly_empty(detail::PolyRoot<I&> const&) noexcept { + return false; +} + +/// \overload +template <class I> +constexpr bool poly_empty(Poly<I&&> const&) noexcept { + return false; +} + +/// \overload +template <class I> +constexpr bool poly_empty(Poly<I&> const&) noexcept { + return false; +} + +/// \cond +[[noreturn]] inline bool poly_empty(detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +//////////////////////////////////////////////////////////////////////////////// +/** + * Given a `Poly<I&>`, return a `Poly<I&&>`. Otherwise, when `I` is not a + * reference type, returns a `Poly<I>&&` when given a `Poly<I>&`, like + * `std::move`. + */ +template < + class I, + std::enable_if_t<Negation<std::is_reference<I>>::value, int> = 0> +constexpr Poly<I>&& poly_move(detail::PolyRoot<I>& that) noexcept { + return static_cast<Poly<I>&&>(static_cast<Poly<I>&>(that)); +} + +/// \overload +template <class I, std::enable_if_t<Negation<std::is_const<I>>::value, int> = 0> +Poly<I&&> poly_move(detail::PolyRoot<I&> const& that) noexcept { + return detail::PolyAccess::move(that); +} + +/// \overload +template <class I> +Poly<I const&> poly_move(detail::PolyRoot<I const&> const& that) noexcept { + return detail::PolyAccess::move(that); +} + +/// \cond +/// \overload +[[noreturn]] inline detail::ArchetypeBase poly_move( + detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +/// \overload +template <class Poly, std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0> +constexpr auto poly_move(Poly& that) noexcept + -> decltype(poly_move(that.get())) { + return poly_move(that.get()); +} + +/// \cond +namespace detail { +/** + * The implementation for `Poly` for when the interface type is not + * reference-like qualified, as in `Poly<SemiRegular>`. + */ +template <class I> +struct PolyVal : PolyImpl<I> { + private: + friend PolyAccess; + + struct NoneSuch {}; + using Copyable = std::is_copy_constructible<PolyImpl<I>>; + using PolyOrNonesuch = If<Copyable::value, PolyVal, NoneSuch>; + + using PolyRoot<I>::vptr_; + + PolyRoot<I>& _polyRoot_() noexcept { + return *this; + } + PolyRoot<I> const& _polyRoot_() const noexcept { + return *this; + } + + Data* _data_() noexcept { + return PolyAccess::data(*this); + } + Data const* _data_() const noexcept { + return PolyAccess::data(*this); + } + + public: + /** + * Default constructor. + * \post `poly_empty(*this) == true` + */ + PolyVal() = default; + /** + * Move constructor. + * \post `poly_empty(that) == true` + */ + PolyVal(PolyVal&& that) noexcept; + /** + * A copy constructor if `I` is copyable; otherwise, a useless constructor + * from a private, incomplete type. + */ + /* implicit */ PolyVal(PolyOrNonesuch const& that); + + ~PolyVal(); + + /** + * Inherit any constructors defined by any of the interfaces. + */ + using PolyImpl<I>::PolyImpl; + + /** + * Copy assignment, destroys the object currently held (if any) and makes + * `*this` equal to `that` by stealing its guts. + */ + Poly<I>& operator=(PolyVal that) noexcept; + + /** + * Construct a Poly<I> from a concrete type that satisfies the I concept + */ + template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0> + /* implicit */ PolyVal(T&& t); + + /** + * Construct a `Poly` from a compatible `Poly`. "Compatible" here means: the + * other interface extends this one either directly or indirectly. + */ + template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int> = 0> + /* implicit */ PolyVal(Poly<I2> that); + + /** + * Assign to this `Poly<I>` from a concrete type that satisfies the `I` + * concept. + */ + template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0> + Poly<I>& operator=(T&& t); + + /** + * Assign a compatible `Poly` to `*this`. "Compatible" here means: the + * other interface extends this one either directly or indirectly. + */ + template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int> = 0> + Poly<I>& operator=(Poly<I2> that); + + /** + * Swaps the values of two `Poly` objects. + */ + void swap(Poly<I>& that) noexcept; +}; + +//////////////////////////////////////////////////////////////////////////////// +/** + * The implementation of `Poly` for when the interface type is + * reference-quelified, like `Poly<SemuRegular &>`. + */ +template <class I> +struct PolyRef : private PolyImpl<I> { + private: + friend PolyAccess; + + AddCvrefOf<PolyRoot<I>, I>& _polyRoot_() const noexcept; + + Data* _data_() noexcept { + return PolyAccess::data(*this); + } + Data const* _data_() const noexcept { + return PolyAccess::data(*this); + } + + static constexpr RefType refType() noexcept; + + protected: + template <class That, class I2> + PolyRef(That&& that, Type<I2>); + + public: + /** + * Copy constructor + * \post `&poly_cast<T>(*this) == &poly_cast<T>(that)`, where `T` is the + * type of the object held by `that`. + */ + PolyRef(PolyRef const& that) noexcept; + + /** + * Copy assignment + * \post `&poly_cast<T>(*this) == &poly_cast<T>(that)`, where `T` is the + * type of the object held by `that`. + */ + Poly<I>& operator=(PolyRef const& that) noexcept; + + /** + * Construct a `Poly<I>` from a concrete type that satisfies concept `I`. + * \post `!poly_empty(*this)` + */ + template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0> + /* implicit */ PolyRef(T&& t) noexcept; + + /** + * Construct a `Poly<I>` from a compatible `Poly<I2>`. + */ + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int> = 0> + /* implicit */ PolyRef(Poly<I2>&& that) noexcept( + std::is_reference<I2>::value); + + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int> = 0> + /* implicit */ PolyRef(Poly<I2>& that) noexcept(std::is_reference<I2>::value) + : PolyRef{that, Type<I2>{}} {} + + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int> = 0> + /* implicit */ PolyRef(Poly<I2> const& that) noexcept( + std::is_reference<I2>::value) + : PolyRef{that, Type<I2>{}} {} + + /** + * Assign to a `Poly<I>` from a concrete type that satisfies concept `I`. + * \post `!poly_empty(*this)` + */ + template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0> + Poly<I>& operator=(T&& t) noexcept; + + /** + * Assign to `*this` from another compatible `Poly`. + */ + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int> = 0> + Poly<I>& operator=(Poly<I2>&& that) noexcept(std::is_reference<I2>::value); + + /** + * \overload + */ + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int> = 0> + Poly<I>& operator=(Poly<I2>& that) noexcept(std::is_reference<I2>::value); + + /** + * \overload + */ + template < + class I2, + std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int> = 0> + Poly<I>& operator=(Poly<I2> const& that) noexcept( + std::is_reference<I2>::value); + + /** + * Swap which object this `Poly` references ("shallow" swap). + */ + void swap(Poly<I>& that) noexcept; + + /** + * Get a reference to the interface, with correct `const`-ness applied. + */ + AddCvrefOf<PolyImpl<I>, I>& get() const noexcept; + + /** + * Get a reference to the interface, with correct `const`-ness applied. + */ + AddCvrefOf<PolyImpl<I>, I>& operator*() const noexcept { + return get(); + } + + /** + * Get a pointer to the interface, with correct `const`-ness applied. + */ + auto operator-> () const noexcept { + return &get(); + } +}; + +template <class I> +using PolyValOrRef = If<std::is_reference<I>::value, PolyRef<I>, PolyVal<I>>; +} // namespace detail +/// \endcond + +/** + * `Poly` is a class template that makes it relatively easy to define a + * type-erasing polymorphic object wrapper. + * + * \par Type-erasure + * + * \par + * `std::function` is one example of a type-erasing polymorphic object wrapper; + * `folly::exception_wrapper` is another. Type-erasure is often used as an + * alternative to dynamic polymorphism via inheritance-based virtual dispatch. + * The distinguishing characteristic of type-erasing wrappers are: + * \li **Duck typing:** Types do not need to inherit from an abstract base + * class in order to be assignable to a type-erasing wrapper; they merely + * need to satisfy a particular interface. + * \li **Value semantics:** Type-erasing wrappers are objects that can be + * passed around _by value_. This is in contrast to abstract base classes + * which must be passed by reference or by pointer or else suffer from + * _slicing_, which causes them to lose their polymorphic behaviors. + * Reference semantics make it difficult to reason locally about code. + * \li **Automatic memory management:** When dealing with inheritance-based + * dynamic polymorphism, it is often necessary to allocate and manage + * objects on the heap. This leads to a proliferation of `shared_ptr`s and + * `unique_ptr`s in APIs, complicating their point-of-use. APIs that take + * type-erasing wrappers, on the other hand, can often store small objects + * in-situ, with no dynamic allocation. The memory management, if any, is + * handled for you, and leads to cleaner APIs: consumers of your API don't + * need to pass `shared_ptr<AbstractBase>`; they can simply pass any object + * that satisfies the interface you require. (`std::function` is a + * particularly compelling example of this benefit. Far worse would be an + * inheritance-based callable solution like + * `shared_ptr<ICallable<void(int)>>`. ) + * + * \par Example: Defining a type-erasing function wrapper with `folly::Poly` + * + * \par + * Defining a polymorphic wrapper with `Poly` is a matter of defining two + * things: + * \li An *interface*, consisting of public member functions, and + * \li A *mapping* from a concrete type to a set of member function bindings. + * + * Below is a (heavily commented) example of a simple implementation of a + * `std::function`-like polymorphic wrapper. Its interface has only a simgle + * member function: `operator()` + * + * // An interface for a callable object of a particular signature, Fun + * // (most interfaces don't need to be templates, FWIW). + * template <class Fun> + * struct IFunction; + * + * template <class R, class... As> + * struct IFunction<R(As...)> { + * // An interface is defined as a nested class template called + * // Interface that takes a single template parameter, Base, from + * // which it inherits. + * template <class Base> + * struct Interface : Base { + * // The Interface has public member functions. These become the + * // public interface of the resulting Poly instantiation. + * // (Implementation note: Poly<IFunction<Sig>> will publicly + * // inherit from this struct, which is what gives it the right + * // member functions.) + * R operator()(As... as) const { + * // The definition of each member function in your interface will + * // always consist of a single line dispatching to + * // folly::poly_call<N>. The "N" corresponds to the N-th member + * // function in the list of member function bindings, Members, + * // defined below. The first argument will always be *this, and the + * // rest of the arguments should simply forward (if necessary) the + * // member function's arguments. + * return static_cast<R>( + * folly::poly_call<0>(*this, std::forward<As>(as)...)); + * } + * }; + * + * // The "Members" alias template is a comma-separated list of bound + * // member functions for a given concrete type "T". The + * // "FOLLY_POLY_MEMBERS" macro accepts a comma-separated list, and the + * // (optional) "FOLLY_POLY_MEMBER" macro lets you disambiguate overloads + * // by explicitly specifying the function signature the target member + * // function should have. In this case, we require "T" to have a + * // function call operator with the signature `R(As...) const`. + * // + * // If you are using a C++17-compatible compiler, you can do away with + * // the macros and write this as: + * // + * // template <class T> + * // using Members = folly::PolyMembers< + * // folly::sig<R(As...) const>(&T::operator())>; + * // + * // And since `folly::sig` is only needed for disambiguation in case of + * // overloads, if you are not concerned about objects with overloaded + * // function call operators, it could be further simplified to: + * // + * // template <class T> + * // using Members = folly::PolyMembers<&T::operator()>; + * // + * template <class T> + * using Members = FOLLY_POLY_MEMBERS( + * FOLLY_POLY_MEMBER(R(As...) const, &T::operator())); + * }; + * + * // Now that we have defined the interface, we can pass it to Poly to + * // create our type-erasing wrapper: + * template <class Fun> + * using Function = Poly<IFunction<Fun>>; + * + * \par + * Given the above definition of `Function`, users can now initialize instances + * of (say) `Function<int(int, int)>` with function objects like + * `std::plus<int>` and `std::multiplies<int>`, as below: + * + * Function<int(int, int)> fun = std::plus<int>{}; + * assert(5 == fun(2, 3)); + * fun = std::multiplies<int>{}; + * assert(6 = fun(2, 3)); + * + * \par Defining an interface with C++17 + * + * \par + * With C++17, defining an interface to be used with `Poly` is fairly + * straightforward. As in the `Function` example above, there is a struct with + * a nested `Interface` class template and a nested `Members` alias template. + * No macros are needed with C++17. + * \par + * Imagine we were defining something like a Java-style iterator. If we are + * using a C++17 compiler, our interface would look something like this: + * + * template <class Value> + * struct IJavaIterator { + * template <class Base> + * struct Interface : Base { + * bool Done() const { return folly::poly_call<0>(*this); } + * Value Current() const { return folly::poly_call<1>(*this); } + * void Next() { folly::poly_call<2>(*this); } + * }; + * // NOTE: This works in C++17 only: + * template <class T> + * using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>; + * }; + * + * template <class Value> + * using JavaIterator = Poly<IJavaIterator>; + * + * \par + * Given the above definition, `JavaIterator<int>` can be used to hold instances + * of any type that has `Done`, `Current`, and `Next` member functions with the + * correct (or compatible) signatures. + * + * \par + * The presence of overloaded member functions complicates this picture. Often, + * property members are faked in C++ with `const` and non-`const` member + * function overloads, like in the interface specified below: + * + * struct IIntProperty { + * template <class Base> + * struct Interface : Base { + * int Value() const { return folly::poly_call<0>(*this); } + * void Value(int i) { folly::poly_call<1>(*this, i); } + * }; + * // NOTE: This works in C++17 only: + * template <class T> + * using Members = folly::PolyMembers< + * folly::sig<int() const>(&T::Value), + * folly::sig<void(int)>(&T::Value)>; + * }; + * + * using IntProperty = Poly<IIntProperty>; + * + * \par + * Now, any object that has `Value` members of compatible signatures can be + * assigned to instances of `IntProperty` object. Note how `folly::sig` is used + * to disambiguate the overloads of `&T::Value`. + * + * \par Defining an interface with C++14 + * + * \par + * In C++14, the nice syntax above doesn't work, so we have to resort to macros. + * The two examples above would look like this: + * + * template <class Value> + * struct IJavaIterator { + * template <class Base> + * struct Interface : Base { + * bool Done() const { return folly::poly_call<0>(*this); } + * Value Current() const { return folly::poly_call<1>(*this); } + * void Next() { folly::poly_call<2>(*this); } + * }; + * // NOTE: This works in C++14 and C++17: + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next); + * }; + * + * template <class Value> + * using JavaIterator = Poly<IJavaIterator>; + * + * \par + * and + * + * struct IIntProperty { + * template <class Base> + * struct Interface : Base { + * int Value() const { return folly::poly_call<0>(*this); } + * void Value(int i) { return folly::poly_call<1>(*this, i); } + * }; + * // NOTE: This works in C++14 and C++17: + * template <class T> + * using Members = FOLLY_POLY_MEMBERS( + * FOLLY_POLY_MEMBER(int() const, &T::Value), + * FOLLY_POLY_MEMBER(void(int), &T::Value)); + * }; + * + * using IntProperty = Poly<IIntProperty>; + * + * \par Extending interfaces + * + * \par + * One typical advantage of inheritance-based solutions to runtime polymorphism + * is that one polymorphic interface could extend another through inheritance. + * The same can be accomplished with type-erasing polymorphic wrappers. In + * the `Poly` library, you can use `folly::PolyExtends` to say that one + * interface extends another. + * + * struct IFoo { + * template <class Base> + * struct Interface : Base { + * void Foo() const { return folly::poly_call<0>(*this); } + * }; + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&T::Foo); + * }; + * + * // The IFooBar interface extends the IFoo interface + * struct IFooBar : PolyExtends<IFoo> { + * template <class Base> + * struct Interface : Base { + * void Bar() const { return folly::poly_call<0>(*this); } + * }; + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&T::Bar); + * }; + * + * using FooBar = Poly<IFooBar>; + * + * \par + * Given the above defintion, instances of type `FooBar` have both `Foo()` and + * `Bar()` member functions. + * + * \par + * The sensible conversions exist between a wrapped derived type and a wrapped + * base type. For instance, assuming `IDerived` extends `IBase` with + * `PolyExtends`: + * + * Poly<IDerived> derived = ...; + * Poly<IBase> base = derived; // This conversion is OK. + * + * \par + * As you would expect, there is no conversion in the other direction, and at + * present there is no `Poly` equivalent to `dynamic_cast`. + * + * \par Type-erasing polymorphic reference wrappers + * + * \par + * Sometimes you don't need to own a copy of an object; a reference will do. For + * that you can use `Poly` to capture a _reference_ to an object satisfying an + * interface rather than the whole object itself. The syntax is intuitive. + * + * int i = 42; + * // Capture a mutable reference to an object of any IRegular type: + * Poly<IRegular &> intRef = i; + * assert(42 == folly::poly_cast<int>(intRef)); + * // Assert that we captured the address of "i": + * assert(&i == &folly::poly_cast<int>(intRef)); + * + * \par + * A reference-like `Poly` has a different interface than a value-like `Poly`. + * Rather than calling member functions with the `obj.fun()` syntax, you would + * use the `obj->fun()` syntax. This is for the sake of `const`-correctness. + * For example, consider the code below: + * + * struct IFoo { + * template <class Base> + * struct Interface { + * void Foo() { folly::poly_call<0>(*this); } + * }; + * template <class T> + * using Members = folly::PolyMembers<&T::Foo>; + * }; + * + * struct SomeFoo { + * void Foo() { std::printf("SomeFoo::Foo\n"); } + * }; + * + * SomeFoo foo; + * Poly<IFoo &> const anyFoo = foo; + * anyFoo->Foo(); // prints "SomeFoo::Foo" + * + * \par + * Notice in the above code that the `Foo` member function is non-`const`. + * Notice also that the `anyFoo` object is `const`. However, since it has + * captured a non-`const` reference to the `foo` object, it should still be + * possible to dispatch to the non-`const` `Foo` member function. When + * instantiated with a reference type, `Poly` has an overloaded `operator->` + * member that returns a pointer to the `IFoo` interface with the correct + * `const`-ness, which makes this work. + * + * \par + * The same mechanism also prevents users from calling non-`const` member + * functions on `Poly` objects that have captured `const` references, which + * would violate `const`-correctness. + * + * \par + * Sensible conversions exist between non-reference and reference `Poly`s. For + * instance: + * + * Poly<IRegular> value = 42; + * Poly<IRegular &> mutable_ref = value; + * Poly<IRegular const &> const_ref = mutable_ref; + * + * assert(&poly_cast<int>(value) == &poly_cast<int>(mutable_ref)); + * assert(&poly_cast<int>(value) == &poly_cast<int>(const_ref)); + * + * \par Non-member functions (C++17) + * + * \par + * If you wanted to write the interface `ILogicallyNegatable`, which captures + * all types that can be negated with unary `operator!`, you could do it + * as we've shown above, by binding `&T::operator!` in the nested `Members` + * alias template, but that has the problem that it won't work for types that + * have defined unary `operator!` as a free function. To handle this case, + * the `Poly` library lets you use a free function instead of a member function + * when creating a binding. + * + * \par + * With C++17 you may use a lambda to create a binding, as shown in the example + * below: + * + * struct ILogicallyNegatable { + * template <class Base> + * struct Interface : Base { + * bool operator!() const { return folly::poly_call<0>(*this); } + * }; + * template <class T> + * using Members = folly::PolyMembers< + * +[](T const& t) -> decltype(!t) { return !t; }>; + * }; + * + * \par + * This requires some explanation. The unary `operator+` in front of the lambda + * is necessary! It causes the lambda to decay to a C-style function pointer, + * which is one of the types that `folly::PolyMembers` accepts. The `decltype` + * in the lambda return type is also necessary. Through the magic of SFINAE, it + * will cause `Poly<ILogicallyNegatable>` to reject any types that don't support + * unary `operator!`. + * + * \par + * If you are using a free function to create a binding, the first parameter is + * implicitly the `this` parameter. It will receive the type-erased object. + * + * \par Non-member functions (C++14) + * + * \par + * If you are using a C++14 compiler, the defintion of `ILogicallyNegatable` + * above will fail because lambdas are not `constexpr`. We can get the same + * effect by writing the lambda as a named free function, as show below: + * + * struct ILogicallyNegatable { + * template <class Base> + * struct Interface : Base { + * bool operator!() const { return folly::poly_call<0>(*this); } + * }; + * + * template <class T> + * static auto negate(T const& t) -> decltype(!t) { return !t; } + * + * template <class T> + * using Members = FOLLY_POLY_MEMBERS(&negate<T>); + * }; + * + * \par + * As with the example that uses the lambda in the preceding section, the first + * parameter is implicitly the `this` parameter. It will receive the type-erased + * object. + * + * \par Multi-dispatch + * + * \par + * What if you want to create an `IAddable` interface for things that can be + * added? Adding requires _two_ objects, both of which are type-erased. This + * interface requires dispatching on both objects, doing the addition only + * if the types are the same. For this we make use of the `PolySelf` template + * alias to define an interface that takes more than one object of the the + * erased type. + * + * struct IAddable { + * template <class Base> + * struct Interface : Base { + * friend PolySelf<Base, Decay> + * operator+(PolySelf<Base> const& a, PolySelf<Base> const& b) { + * return folly::poly_call<0, IAddable>(a, b); + * } + * }; + * + * template <class T> + * using Members = folly::PolyMembers< + * +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>; + * }; + * + * \par + * Given the above defintion of `IAddable` we would be able to do the following: + * + * Poly<IAddable> a = 2, b = 3; + * Poly<IAddable> c = a + b; + * assert(poly_cast<int>(c) == 5); + * + * \par + * If `a` and `b` stored objects of different types, a `BadPolyCast` exception + * would be thrown. + * + * \par Move-only types + * + * \par + * If you want to store move-only types, then your interface should extend the + * `IMoveOnly` interface. + * + * \par Implementation notes + * \par + * `Poly` will store "small" objects in an internal buffer, avoiding the cost of + * of dynamic allocations. At present, this size is not configurable; it is + * pegged at the size of two `double`s. + * + * \par + * `Poly` objects are always nothrow movable. If you store an object in one that + * has a potentially throwing move contructor, the object will be stored on the + * heap, even if it could fit in the internal storage of the `Poly` object. + * (So be sure to give your objects nothrow move constructors!) + * + * \par + * `Poly` implements type-erasure in a manner very similar to how the compiler + * accomplishes virtual dispatch. Every `Poly` object contains a pointer to a + * table of function pointers. Member function calls involve a double- + * indirection: once through the v-pointer, and other indirect function call + * through the function pointer. + */ +template <class I> +struct Poly final : detail::PolyValOrRef<I> { + friend detail::PolyAccess; + Poly() = default; + using detail::PolyValOrRef<I>::PolyValOrRef; + using detail::PolyValOrRef<I>::operator=; +}; + +/** + * Swap two `Poly<I>` instances. + */ +template <class I> +void swap(Poly<I>& left, Poly<I>& right) noexcept { + left.swap(right); +} + +/** + * Pseudo-function template handy for disambiguating function overloads. + * + * For example, given: + * struct S { + * int property() const; + * void property(int); + * }; + * + * You can get a member function pointer to the first overload with: + * folly::sig<int()const>(&S::property); + * + * This is arguably a nicer syntax that using the built-in `static_cast`: + * static_cast<int (S::*)() const>(&S::property); + * + * `sig` is also more permissive than `static_cast` about `const`. For instance, + * the following also works: + * folly::sig<int()>(&S::property); + * + * The above is permitted + */ +template <class Sig> +FOLLY_INLINE_CONSTEXPR detail::Sig<Sig> const sig = {}; + +} // namespace folly + +#include <folly/Poly-inl.h> + +#undef FOLLY_INLINE_CONSTEXPR diff --git a/ios/Pods/Flipper-Folly/folly/PolyException.h b/ios/Pods/Flipper-Folly/folly/PolyException.h new file mode 100644 index 000000000..92090e7a3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/PolyException.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <exception> +#include <typeinfo> + +#include <folly/CPortability.h> + +namespace folly { + +/** + * Exception type that is thrown on invalid access of an empty `Poly` object. + */ +struct FOLLY_EXPORT BadPolyAccess : std::exception { + BadPolyAccess() = default; + char const* what() const noexcept override { + return "BadPolyAccess"; + } +}; + +/** + * Exception type that is thrown when attempting to extract from a `Poly` a + * value of the wrong type. + */ +struct FOLLY_EXPORT BadPolyCast : std::bad_cast { + BadPolyCast() = default; + char const* what() const noexcept override { + return "BadPolyCast"; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Portability.h b/ios/Pods/Flipper-Folly/folly/Portability.h new file mode 100644 index 000000000..b0c8c9938 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Portability.h @@ -0,0 +1,569 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// MSCV 2017 __cplusplus definition by default does not track the C++ version. +// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ +#if !defined(_MSC_VER) || _MSC_VER >= 2000 +static_assert(__cplusplus >= 201402L, "__cplusplus >= 201402L"); +#endif + +#if defined(__GNUC__) && !defined(__clang__) +static_assert(__GNUC__ >= 5, "__GNUC__ >= 5"); +#endif + +#include <cstddef> + +#include <folly/CPortability.h> +#include <folly/portability/Config.h> + +// Unaligned loads and stores +namespace folly { +#if FOLLY_HAVE_UNALIGNED_ACCESS +constexpr bool kHasUnalignedAccess = true; +#else +constexpr bool kHasUnalignedAccess = false; +#endif +} // namespace folly + +// compiler specific attribute translation +// msvc should come first, so if clang is in msvc mode it gets the right defines + +// NOTE: this will only do checking in msvc with versions that support /analyze +#if _MSC_VER +#ifdef _USE_ATTRIBUTES_FOR_SAL +#undef _USE_ATTRIBUTES_FOR_SAL +#endif +/* nolint */ +#define _USE_ATTRIBUTES_FOR_SAL 1 +#include <sal.h> // @manual +#define FOLLY_PRINTF_FORMAT _Printf_format_string_ +#define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) /**/ +#else +#define FOLLY_PRINTF_FORMAT /**/ +#define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((__format__(__printf__, format_param, dots_param))) +#endif + +// warn unused result +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(nodiscard) +#define FOLLY_NODISCARD [[nodiscard]] +#endif +#endif +#if !defined FOLLY_NODISCARD +#if defined(_MSC_VER) && (_MSC_VER >= 1700) +#define FOLLY_NODISCARD _Check_return_ +#elif defined(__GNUC__) +#define FOLLY_NODISCARD __attribute__((__warn_unused_result__)) +#else +#define FOLLY_NODISCARD +#endif +#endif + +// target +#ifdef _MSC_VER +#define FOLLY_TARGET_ATTRIBUTE(target) +#else +#define FOLLY_TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) +#endif + +// detection for 64 bit +#if defined(__x86_64__) || defined(_M_X64) +#define FOLLY_X64 1 +#else +#define FOLLY_X64 0 +#endif + +#if defined(__arm__) +#define FOLLY_ARM 1 +#else +#define FOLLY_ARM 0 +#endif + +#if defined(__aarch64__) +#define FOLLY_AARCH64 1 +#else +#define FOLLY_AARCH64 0 +#endif + +#if defined(__powerpc64__) +#define FOLLY_PPC64 1 +#else +#define FOLLY_PPC64 0 +#endif + +#if defined(__s390x__) +#define FOLLY_S390X 1 +#else +#define FOLLY_S390X 0 +#endif + +namespace folly { +constexpr bool kIsArchArm = FOLLY_ARM == 1; +constexpr bool kIsArchAmd64 = FOLLY_X64 == 1; +constexpr bool kIsArchAArch64 = FOLLY_AARCH64 == 1; +constexpr bool kIsArchPPC64 = FOLLY_PPC64 == 1; +constexpr bool kIsArchS390X = FOLLY_S390X == 1; +} // namespace folly + +namespace folly { + +/** + * folly::kIsLibrarySanitizeAddress reports if folly was compiled with ASAN + * enabled. Note that for compilation units outside of folly that include + * folly/Portability.h, the value of kIsLibrarySanitizeAddress may be different + * from whether or not the current compilation unit is being compiled with ASAN. + */ +#if FOLLY_LIBRARY_SANITIZE_ADDRESS +constexpr bool kIsLibrarySanitizeAddress = true; +#else +constexpr bool kIsLibrarySanitizeAddress = false; +#endif + +#if FOLLY_SANITIZE_ADDRESS +constexpr bool kIsSanitizeAddress = true; +#else +constexpr bool kIsSanitizeAddress = false; +#endif + +#if FOLLY_SANITIZE_THREAD +constexpr bool kIsSanitizeThread = true; +#else +constexpr bool kIsSanitizeThread = false; +#endif + +#if FOLLY_SANITIZE +constexpr bool kIsSanitize = true; +#else +constexpr bool kIsSanitize = false; +#endif +} // namespace folly + +// packing is very ugly in msvc +#ifdef _MSC_VER +#define FOLLY_PACK_ATTR /**/ +#define FOLLY_PACK_PUSH __pragma(pack(push, 1)) +#define FOLLY_PACK_POP __pragma(pack(pop)) +#elif defined(__GNUC__) +#define FOLLY_PACK_ATTR __attribute__((__packed__)) +#define FOLLY_PACK_PUSH /**/ +#define FOLLY_PACK_POP /**/ +#else +#define FOLLY_PACK_ATTR /**/ +#define FOLLY_PACK_PUSH /**/ +#define FOLLY_PACK_POP /**/ +#endif + +// Generalize warning push/pop. +#if defined(__GNUC__) || defined(__clang__) +// Clang & GCC +#define FOLLY_PUSH_WARNING _Pragma("GCC diagnostic push") +#define FOLLY_POP_WARNING _Pragma("GCC diagnostic pop") +#define FOLLY_GNU_DISABLE_WARNING_INTERNAL2(warningName) #warningName +#define FOLLY_GNU_DISABLE_WARNING(warningName) \ + _Pragma( \ + FOLLY_GNU_DISABLE_WARNING_INTERNAL2(GCC diagnostic ignored warningName)) +#ifdef __clang__ +#define FOLLY_CLANG_DISABLE_WARNING(warningName) \ + FOLLY_GNU_DISABLE_WARNING(warningName) +#define FOLLY_GCC_DISABLE_WARNING(warningName) +#else +#define FOLLY_CLANG_DISABLE_WARNING(warningName) +#define FOLLY_GCC_DISABLE_WARNING(warningName) \ + FOLLY_GNU_DISABLE_WARNING(warningName) +#endif +#define FOLLY_MSVC_DISABLE_WARNING(warningNumber) +#elif defined(_MSC_VER) +#define FOLLY_PUSH_WARNING __pragma(warning(push)) +#define FOLLY_POP_WARNING __pragma(warning(pop)) +// Disable the GCC warnings. +#define FOLLY_GNU_DISABLE_WARNING(warningName) +#define FOLLY_GCC_DISABLE_WARNING(warningName) +#define FOLLY_CLANG_DISABLE_WARNING(warningName) +#define FOLLY_MSVC_DISABLE_WARNING(warningNumber) \ + __pragma(warning(disable : warningNumber)) +#else +#define FOLLY_PUSH_WARNING +#define FOLLY_POP_WARNING +#define FOLLY_GNU_DISABLE_WARNING(warningName) +#define FOLLY_GCC_DISABLE_WARNING(warningName) +#define FOLLY_CLANG_DISABLE_WARNING(warningName) +#define FOLLY_MSVC_DISABLE_WARNING(warningNumber) +#endif + +#ifdef FOLLY_HAVE_SHADOW_LOCAL_WARNINGS +#define FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS \ + FOLLY_GNU_DISABLE_WARNING("-Wshadow-compatible-local") \ + FOLLY_GNU_DISABLE_WARNING("-Wshadow-local") \ + FOLLY_GNU_DISABLE_WARNING("-Wshadow") +#else +#define FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS /* empty */ +#endif + +/* Platform specific TLS support + * gcc implements __thread + * msvc implements __declspec(thread) + * the semantics are the same + * (but remember __thread has different semantics when using emutls (ex. apple)) + */ +#if defined(_MSC_VER) +#define FOLLY_TLS __declspec(thread) +#elif defined(__GNUC__) +#define FOLLY_TLS __thread +#else +#error cannot define platform specific thread local storage +#endif + +// disable FOLLY_TLS on 32 bit Apple/iOS +#if defined(__APPLE__) && FOLLY_MOBILE +#if (__SIZEOF_POINTER__ == 4) +#undef FOLLY_TLS +#endif +#endif + +// It turns out that GNU libstdc++ and LLVM libc++ differ on how they implement +// the 'std' namespace; the latter uses inline namespaces. Wrap this decision +// up in a macro to make forward-declarations easier. +#if FOLLY_USE_LIBCPP +#include <__config> // @manual +#define FOLLY_NAMESPACE_STD_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD +#define FOLLY_NAMESPACE_STD_END _LIBCPP_END_NAMESPACE_STD +#else +#define FOLLY_NAMESPACE_STD_BEGIN namespace std { +#define FOLLY_NAMESPACE_STD_END } +#endif + +// If the new c++ ABI is used, __cxx11 inline namespace needs to be added to +// some types, e.g. std::list. +#if _GLIBCXX_USE_CXX11_ABI +#define FOLLY_GLIBCXX_NAMESPACE_CXX11_BEGIN \ + inline _GLIBCXX_BEGIN_NAMESPACE_CXX11 +#define FOLLY_GLIBCXX_NAMESPACE_CXX11_END _GLIBCXX_END_NAMESPACE_CXX11 +#else +#define FOLLY_GLIBCXX_NAMESPACE_CXX11_BEGIN +#define FOLLY_GLIBCXX_NAMESPACE_CXX11_END +#endif + +// MSVC specific defines +// mainly for posix compat +#ifdef _MSC_VER +#include <folly/portability/SysTypes.h> + +// Hide a GCC specific thing that breaks MSVC if left alone. +#define __extension__ + +// We have compiler support for the newest of the new, but +// MSVC doesn't tell us that. +// +// Clang pretends to be MSVC on Windows, but it refuses to compile +// SSE4.2 intrinsics unless -march argument is specified. +// So cannot unconditionally define __SSE4_2__ in clang. +#ifndef __clang__ +#define __SSE4_2__ 1 +// compiler specific to compiler specific +// nolint +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + +#endif + +// Define FOLLY_HAS_EXCEPTIONS +#if __cpp_exceptions >= 199711 || FOLLY_HAS_FEATURE(cxx_exceptions) +#define FOLLY_HAS_EXCEPTIONS 1 +#elif __GNUC__ +#if __EXCEPTIONS +#define FOLLY_HAS_EXCEPTIONS 1 +#else // __EXCEPTIONS +#define FOLLY_HAS_EXCEPTIONS 0 +#endif // __EXCEPTIONS +#elif FOLLY_MICROSOFT_ABI_VER +#if _CPPUNWIND +#define FOLLY_HAS_EXCEPTIONS 1 +#else // _CPPUNWIND +#define FOLLY_HAS_EXCEPTIONS 0 +#endif // _CPPUNWIND +#else +#define FOLLY_HAS_EXCEPTIONS 1 // default assumption for unknown platforms +#endif + +// Debug +namespace folly { +#ifdef NDEBUG +constexpr auto kIsDebug = false; +#else +constexpr auto kIsDebug = true; +#endif +} // namespace folly + +// Exceptions +namespace folly { +#if FOLLY_HAS_EXCEPTIONS +constexpr auto kHasExceptions = true; +#else +constexpr auto kHasExceptions = false; +#endif +} // namespace folly + +// Endianness +namespace folly { +#ifdef _MSC_VER +// It's MSVC, so we just have to guess ... and allow an override +#ifdef FOLLY_ENDIAN_BE +constexpr auto kIsLittleEndian = false; +#else +constexpr auto kIsLittleEndian = true; +#endif +#else +constexpr auto kIsLittleEndian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; +#endif +constexpr auto kIsBigEndian = !kIsLittleEndian; +} // namespace folly + +#ifndef FOLLY_SSE +#if defined(__SSE4_2__) +#define FOLLY_SSE 4 +#define FOLLY_SSE_MINOR 2 +#elif defined(__SSE4_1__) +#define FOLLY_SSE 4 +#define FOLLY_SSE_MINOR 1 +#elif defined(__SSE4__) +#define FOLLY_SSE 4 +#define FOLLY_SSE_MINOR 0 +#elif defined(__SSE3__) +#define FOLLY_SSE 3 +#define FOLLY_SSE_MINOR 0 +#elif defined(__SSE2__) +#define FOLLY_SSE 2 +#define FOLLY_SSE_MINOR 0 +#elif defined(__SSE__) +#define FOLLY_SSE 1 +#define FOLLY_SSE_MINOR 0 +#else +#define FOLLY_SSE 0 +#define FOLLY_SSE_MINOR 0 +#endif +#endif + +#ifndef FOLLY_SSSE +#if defined(__SSSE3__) +#define FOLLY_SSSE 3 +#else +#define FOLLY_SSSE 0 +#endif +#endif + +#define FOLLY_SSE_PREREQ(major, minor) \ + (FOLLY_SSE > major || FOLLY_SSE == major && FOLLY_SSE_MINOR >= minor) + +#ifndef FOLLY_NEON +#if defined(__ARM_NEON) || defined(__ARM_NEON__) +#define FOLLY_NEON 1 +#endif +#endif + +#if FOLLY_UNUSUAL_GFLAGS_NAMESPACE +namespace FOLLY_GFLAGS_NAMESPACE {} +namespace gflags { +using namespace FOLLY_GFLAGS_NAMESPACE; +} // namespace gflags +#endif + +// for TARGET_OS_IPHONE +#ifdef __APPLE__ +#include <TargetConditionals.h> // @manual +#endif + +// RTTI may not be enabled for this compilation unit. +#if defined(__GXX_RTTI) || defined(__cpp_rtti) || \ + (defined(_MSC_VER) && defined(_CPPRTTI)) +#define FOLLY_HAS_RTTI 1 +#else +#define FOLLY_HAS_RTTI 0 +#endif + +namespace folly { +constexpr bool const kHasRtti = FOLLY_HAS_RTTI; +} // namespace folly + +#if defined(__APPLE__) || defined(_MSC_VER) +#define FOLLY_STATIC_CTOR_PRIORITY_MAX +#else +// 101 is the highest priority allowed by the init_priority attribute. +// This priority is already used by JEMalloc and other memory allocators so +// we will take the next one. +#define FOLLY_STATIC_CTOR_PRIORITY_MAX __attribute__((__init_priority__(102))) +#endif + +namespace folly { + +#if __OBJC__ +constexpr auto kIsObjC = true; +#else +constexpr auto kIsObjC = false; +#endif + +#if FOLLY_MOBILE +constexpr auto kIsMobile = true; +#else +constexpr auto kIsMobile = false; +#endif + +#if defined(__linux__) && !FOLLY_MOBILE +constexpr auto kIsLinux = true; +#else +constexpr auto kIsLinux = false; +#endif + +#if defined(_WIN32) +constexpr auto kIsWindows = true; +#else +constexpr auto kIsWindows = false; +#endif + +#if __GLIBCXX__ +constexpr auto kIsGlibcxx = true; +#else +constexpr auto kIsGlibcxx = false; +#endif + +#if __GLIBCXX__ && _GLIBCXX_RELEASE // major version, 7+ +constexpr auto kGlibcxxVer = _GLIBCXX_RELEASE; +#else +constexpr auto kGlibcxxVer = 0; +#endif + +#if _LIBCPP_VERSION +constexpr auto kIsLibcpp = true; +#else +constexpr auto kIsLibcpp = false; +#endif + +#if FOLLY_USE_LIBSTDCPP +constexpr auto kIsLibstdcpp = true; +#else +constexpr auto kIsLibstdcpp = false; +#endif + +#if _MSC_VER +constexpr auto kMscVer = _MSC_VER; +#else +constexpr auto kMscVer = 0; +#endif + +#if __GNUC__ +constexpr auto kGnuc = __GNUC__; +#else +constexpr auto kGnuc = 0; +#endif + +#if __clang__ +constexpr auto kIsClang = true; +#else +constexpr auto kIsClang = false; +#endif + +#if FOLLY_MICROSOFT_ABI_VER +constexpr auto kMicrosoftAbiVer = FOLLY_MICROSOFT_ABI_VER; +#else +constexpr auto kMicrosoftAbiVer = 0; +#endif + +// cpplib is an implementation of the standard library, and is the one typically +// used with the msvc compiler +#if _CPPLIB_VER +constexpr auto kCpplibVer = _CPPLIB_VER; +#else +constexpr auto kCpplibVer = 0; +#endif +} // namespace folly + +// MSVC does not permit: +// +// extern int const num; +// constexpr int const num = 3; +// +// Instead: +// +// extern int const num; +// FOLLY_STORAGE_CONSTEXPR int const num = 3; +// +// True as of MSVC 2017. +#if _MSC_VER +#define FOLLY_STORAGE_CONSTEXPR +#else +#define FOLLY_STORAGE_CONSTEXPR constexpr +#endif + +#if __cplusplus >= 201703L +// folly::coro requires C++17 support +#if __cpp_coroutines >= 201703L && __has_include(<experimental/coroutine>) +#define FOLLY_HAS_COROUTINES 1 +// This is mainly to workaround bugs triggered by LTO, when stack allocated +// variables in await_suspend end up on a coroutine frame. +#define FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES FOLLY_NOINLINE +#elif _MSC_VER && _RESUMABLE_FUNCTIONS_SUPPORTED +// NOTE: MSVC 2017 does not currently support the full Coroutines TS since it +// does not yet support symmetric-transfer. +#define FOLLY_HAS_COROUTINES 0 +#else +#define FOLLY_HAS_COROUTINES 0 +#endif +#else +#define FOLLY_HAS_COROUTINES 0 +#endif // __cplusplus >= 201703L + +// MSVC 2017.5 && C++17 +#if __cpp_noexcept_function_type >= 201510 || \ + (_MSC_FULL_VER >= 191225816 && _MSVC_LANG > 201402) +#define FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE 1 +#endif + +#if __cpp_inline_variables >= 201606L +#define FOLLY_HAS_INLINE_VARIABLES 1 +#define FOLLY_INLINE_VARIABLE inline +#else +#define FOLLY_HAS_INLINE_VARIABLES 0 +#define FOLLY_INLINE_VARIABLE +#endif + +// feature test __cpp_lib_string_view is defined in <string>, which is +// too heavy to include here. MSVC __has_include support arrived later +// than string_view, so we need an alternate case for it. +#ifdef __has_include +#if __has_include(<string_view>) && __cplusplus >= 201703L +#define FOLLY_HAS_STRING_VIEW 1 +#else +#define FOLLY_HAS_STRING_VIEW 0 +#endif +#else // __has_include +#if _MSC_VER >= 1910 && (_MSVC_LANG > 201402 || __cplusplus > 201402) +#define FOLLY_HAS_STRING_VIEW 1 +#else +#define FOLLY_HAS_STRING_VIEW 0 +#endif +#endif // __has_include + +#if defined(__linux__) +#define FOLLY_ELF_NATIVE_CLASS __ELF_NATIVE_CLASS +#elif defined(__FreeBSD__) +#if defined(__LP64__) +#define FOLLY_ELF_NATIVE_CLASS 64 +#else // __linux__ +#define FOLLY_ELF_NATIVE_CLASS 32 +#endif +#endif // __linux__ diff --git a/ios/Pods/Flipper-Folly/folly/Preprocessor.h b/ios/Pods/Flipper-Folly/folly/Preprocessor.h new file mode 100644 index 000000000..292bd9fff --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Preprocessor.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Andrei Alexandrescu + +#pragma once + +#include <folly/CPortability.h> + +/** + * Necessarily evil preprocessor-related amenities. + */ + +// MSVC's preprocessor is a pain, so we have to +// forcefully expand the VA args in some places. +#define FB_VA_GLUE(a, b) a b + +/** + * FB_ONE_OR_NONE(hello, world) expands to hello and + * FB_ONE_OR_NONE(hello) expands to nothing. This macro is used to + * insert or eliminate text based on the presence of another argument. + */ +#define FB_ONE_OR_NONE(a, ...) FB_VA_GLUE(FB_THIRD, (a, ##__VA_ARGS__, a)) +#define FB_THIRD(a, b, ...) __VA_ARGS__ + +/** + * Helper macro that extracts the first argument out of a list of any + * number of arguments. + */ +#define FB_ARG_1(a, ...) a + +/** + * Helper macro that extracts the second argument out of a list of any + * number of arguments. If only one argument is given, it returns + * that. + */ +#ifdef _MSC_VER +// GCC refuses to expand this correctly if this macro itself was +// called with FB_VA_GLUE :( +#define FB_ARG_2_OR_1(...) \ + FB_VA_GLUE(FB_ARG_2_OR_1_IMPL, (__VA_ARGS__, __VA_ARGS__)) +#else +#define FB_ARG_2_OR_1(...) FB_ARG_2_OR_1_IMPL(__VA_ARGS__, __VA_ARGS__) +#endif +// Support macro for the above +#define FB_ARG_2_OR_1_IMPL(a, b, ...) b + +/** + * Helper macro that provides a way to pass argument with commas in it to + * some other macro whose syntax doesn't allow using extra parentheses. + * Example: + * + * #define MACRO(type, name) type name + * MACRO(FB_SINGLE_ARG(std::pair<size_t, size_t>), x); + * + */ +#define FB_SINGLE_ARG(...) __VA_ARGS__ + +#define FOLLY_PP_DETAIL_APPEND_VA_ARG(...) , ##__VA_ARGS__ + +/** + * Helper macro that just ignores its parameters. + */ +#define FOLLY_IGNORE(...) + +/** + * Helper macro that just ignores its parameters and inserts a semicolon. + */ +#define FOLLY_SEMICOLON(...) ; + +/** + * FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with + * str and ending with a number that varies with the line. + */ +#ifndef FB_ANONYMOUS_VARIABLE +#define FB_CONCATENATE_IMPL(s1, s2) s1##s2 +#define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2) +#ifdef __COUNTER__ +// Modular builds build each module with its own preprocessor state, meaning +// `__COUNTER__` no longer provides a unique number across a TU. Instead of +// calling back to just `__LINE__`, use a mix of `__COUNTER__` and `__LINE__` +// to try provide as much uniqueness as possible. +#if FOLLY_HAS_FEATURE(modules) +#define FB_ANONYMOUS_VARIABLE(str) \ + FB_CONCATENATE(FB_CONCATENATE(FB_CONCATENATE(str, __COUNTER__), _), __LINE__) +#else +#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__) +#endif +#else +#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__) +#endif +#endif + +/** + * Use FOLLY_PP_STRINGIZE(x) when you'd want to do what #x does inside + * another macro expansion. + */ +#define FOLLY_PP_STRINGIZE(x) #x + +#define FOLLY_PP_DETAIL_NARGS_1(dummy, _7, _6, _5, _4, _3, _2, _1, _0, ...) _0 +#define FOLLY_PP_DETAIL_NARGS(...) \ + FOLLY_PP_DETAIL_NARGS_1(dummy, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) + +#define FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, ...) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_REC_7(fn, a, ...) \ + fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, __VA_ARGS__) + +#define FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, ...) \ + FOLLY_PP_DETAIL_FOR_EACH_REC_##n(fn, __VA_ARGS__) +#define FOLLY_PP_DETAIL_FOR_EACH_1(fn, n, ...) \ + FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, __VA_ARGS__) + +/** + * FOLLY_PP_FOR_EACH + * + * Used to invoke a preprocessor macro, the name of which is passed as the + * first argument, once for each subsequent variadic argument. + * + * At present, supports [0, 8) arguments. + * + * This input: + * + * #define DOIT(a) go_do_it(a); + * FOLLY_PP_FOR_EACH(DOIT, 3, 5, 7) + * #undef DOIT + * + * Expands to this output (with whitespace adjusted for clarity): + * + * go_do_it(3); + * go_do_it(5); + * go_do_it(7); + */ +#define FOLLY_PP_FOR_EACH(fn, ...) \ + FOLLY_PP_DETAIL_FOR_EACH_1( \ + fn, FOLLY_PP_DETAIL_NARGS(__VA_ARGS__), __VA_ARGS__) diff --git a/ios/Pods/Flipper-Folly/folly/ProducerConsumerQueue.h b/ios/Pods/Flipper-Folly/folly/ProducerConsumerQueue.h new file mode 100644 index 000000000..15cf70a2b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ProducerConsumerQueue.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author Bo Hu (bhu@fb.com) +// @author Jordan DeLong (delong.j@fb.com) + +#pragma once + +#include <atomic> +#include <cassert> +#include <cstdlib> +#include <memory> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <folly/concurrency/CacheLocality.h> + +namespace folly { + +/* + * ProducerConsumerQueue is a one producer and one consumer queue + * without locks. + */ +template <class T> +struct ProducerConsumerQueue { + typedef T value_type; + + ProducerConsumerQueue(const ProducerConsumerQueue&) = delete; + ProducerConsumerQueue& operator=(const ProducerConsumerQueue&) = delete; + + // size must be >= 2. + // + // Also, note that the number of usable slots in the queue at any + // given time is actually (size-1), so if you start with an empty queue, + // isFull() will return true after size-1 insertions. + explicit ProducerConsumerQueue(uint32_t size) + : size_(size), + records_(static_cast<T*>(std::malloc(sizeof(T) * size))), + readIndex_(0), + writeIndex_(0) { + assert(size >= 2); + if (!records_) { + throw std::bad_alloc(); + } + } + + ~ProducerConsumerQueue() { + // We need to destruct anything that may still exist in our queue. + // (No real synchronization needed at destructor time: only one + // thread can be doing this.) + if (!std::is_trivially_destructible<T>::value) { + size_t readIndex = readIndex_; + size_t endIndex = writeIndex_; + while (readIndex != endIndex) { + records_[readIndex].~T(); + if (++readIndex == size_) { + readIndex = 0; + } + } + } + + std::free(records_); + } + + template <class... Args> + bool write(Args&&... recordArgs) { + auto const currentWrite = writeIndex_.load(std::memory_order_relaxed); + auto nextRecord = currentWrite + 1; + if (nextRecord == size_) { + nextRecord = 0; + } + if (nextRecord != readIndex_.load(std::memory_order_acquire)) { + new (&records_[currentWrite]) T(std::forward<Args>(recordArgs)...); + writeIndex_.store(nextRecord, std::memory_order_release); + return true; + } + + // queue is full + return false; + } + + // move (or copy) the value at the front of the queue to given variable + bool read(T& record) { + auto const currentRead = readIndex_.load(std::memory_order_relaxed); + if (currentRead == writeIndex_.load(std::memory_order_acquire)) { + // queue is empty + return false; + } + + auto nextRecord = currentRead + 1; + if (nextRecord == size_) { + nextRecord = 0; + } + record = std::move(records_[currentRead]); + records_[currentRead].~T(); + readIndex_.store(nextRecord, std::memory_order_release); + return true; + } + + // pointer to the value at the front of the queue (for use in-place) or + // nullptr if empty. + T* frontPtr() { + auto const currentRead = readIndex_.load(std::memory_order_relaxed); + if (currentRead == writeIndex_.load(std::memory_order_acquire)) { + // queue is empty + return nullptr; + } + return &records_[currentRead]; + } + + // queue must not be empty + void popFront() { + auto const currentRead = readIndex_.load(std::memory_order_relaxed); + assert(currentRead != writeIndex_.load(std::memory_order_acquire)); + + auto nextRecord = currentRead + 1; + if (nextRecord == size_) { + nextRecord = 0; + } + records_[currentRead].~T(); + readIndex_.store(nextRecord, std::memory_order_release); + } + + bool isEmpty() const { + return readIndex_.load(std::memory_order_acquire) == + writeIndex_.load(std::memory_order_acquire); + } + + bool isFull() const { + auto nextRecord = writeIndex_.load(std::memory_order_acquire) + 1; + if (nextRecord == size_) { + nextRecord = 0; + } + if (nextRecord != readIndex_.load(std::memory_order_acquire)) { + return false; + } + // queue is full + return true; + } + + // * If called by consumer, then true size may be more (because producer may + // be adding items concurrently). + // * If called by producer, then true size may be less (because consumer may + // be removing items concurrently). + // * It is undefined to call this from any other thread. + size_t sizeGuess() const { + int ret = writeIndex_.load(std::memory_order_acquire) - + readIndex_.load(std::memory_order_acquire); + if (ret < 0) { + ret += size_; + } + return ret; + } + + // maximum number of items in the queue. + size_t capacity() const { + return size_ - 1; + } + + private: + using AtomicIndex = std::atomic<unsigned int>; + + char pad0_[hardware_destructive_interference_size]; + const uint32_t size_; + T* const records_; + + alignas(hardware_destructive_interference_size) AtomicIndex readIndex_; + alignas(hardware_destructive_interference_size) AtomicIndex writeIndex_; + + char pad1_[hardware_destructive_interference_size - sizeof(AtomicIndex)]; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/RWSpinLock.h b/ios/Pods/Flipper-Folly/folly/RWSpinLock.h new file mode 100644 index 000000000..4154f393b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/RWSpinLock.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/RWSpinLock.h> // @shim diff --git a/ios/Pods/Flipper-Folly/folly/Random-inl.h b/ios/Pods/Flipper-Folly/folly/Random-inl.h new file mode 100644 index 000000000..9434f0a54 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Random-inl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_RANDOM_H_ +#error This file may only be included from folly/Random.h +#endif + +namespace folly { + +namespace detail { + +// Return the state size needed by RNG, expressed as a number of uint32_t +// integers. Specialized for all templates specified in the C++11 standard. +// For some (mersenne_twister_engine), this is exported as a state_size static +// data member; for others, the standard shows formulas. + +template <class RNG, typename = void> +struct StateSize { + // A sane default. + using type = std::integral_constant<size_t, 512>; +}; + +template <class RNG> +struct StateSize<RNG, void_t<decltype(RNG::state_size)>> { + using type = std::integral_constant<size_t, RNG::state_size>; +}; + +template <class UIntType, UIntType a, UIntType c, UIntType m> +struct StateSize<std::linear_congruential_engine<UIntType, a, c, m>> { + // From the standard [rand.eng.lcong], this is ceil(log2(m) / 32) + 3, + // which is the same as ceil(ceil(log2(m) / 32) + 3, and + // ceil(log2(m)) <= std::numeric_limits<UIntType>::digits + using type = std::integral_constant< + size_t, + (std::numeric_limits<UIntType>::digits + 31) / 32 + 3>; +}; + +template <class UIntType, size_t w, size_t s, size_t r> +struct StateSize<std::subtract_with_carry_engine<UIntType, w, s, r>> { + // [rand.eng.sub]: r * ceil(w / 32) + using type = std::integral_constant<size_t, r*((w + 31) / 32)>; +}; + +template <typename RNG> +using StateSizeT = _t<StateSize<RNG>>; + +template <class RNG> +struct SeedData { + SeedData() { + Random::secureRandom(seedData.data(), seedData.size() * sizeof(uint32_t)); + } + + static constexpr size_t stateSize = StateSizeT<RNG>::value; + std::array<uint32_t, stateSize> seedData; +}; + +} // namespace detail + +template <class RNG, class /* EnableIf */> +void Random::seed(RNG& rng) { + detail::SeedData<RNG> sd; + std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData)); + rng.seed(s); +} + +template <class RNG, class /* EnableIf */> +auto Random::create() -> RNG { + detail::SeedData<RNG> sd; + std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData)); + return RNG(s); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Random.cpp b/ios/Pods/Flipper-Folly/folly/Random.cpp new file mode 100644 index 000000000..00dbf7f94 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Random.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Random.h> + +#include <array> +#include <atomic> +#include <mutex> +#include <random> + +#include <folly/CppAttributes.h> +#include <folly/File.h> +#include <folly/FileUtil.h> +#include <folly/SingletonThreadLocal.h> +#include <folly/ThreadLocal.h> +#include <folly/detail/FileUtilDetail.h> +#include <folly/portability/Config.h> +#include <folly/portability/SysTime.h> +#include <folly/portability/Unistd.h> +#include <glog/logging.h> + +#ifdef _MSC_VER +#include <wincrypt.h> // @manual +#endif + +#if FOLLY_HAVE_GETRANDOM +#include <sys/random.h> +#endif + +namespace folly { + +namespace { + +void readRandomDevice(void* data, size_t size) { +#ifdef _MSC_VER + static auto const cryptoProv = [] { + HCRYPTPROV prov; + if (!CryptAcquireContext( + &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + if (GetLastError() == NTE_BAD_KEYSET) { + // Mostly likely cause of this is that no key container + // exists yet, so try to create one. + PCHECK(CryptAcquireContext( + &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET)); + } else { + LOG(FATAL) << "Failed to acquire the default crypto context."; + } + } + return prov; + }(); + CHECK(size <= std::numeric_limits<DWORD>::max()); + PCHECK(CryptGenRandom(cryptoProv, (DWORD)size, (BYTE*)data)); +#else + ssize_t bytesRead = 0; + auto gen = [](int, void* buf, size_t buflen) { +#if FOLLY_HAVE_GETRANDOM + auto flags = 0u; + return ::getrandom(buf, buflen, flags); +#else + [](...) {}(buf, buflen); + errno = ENOSYS; + return -1; +#endif + }; + bytesRead = fileutil_detail::wrapFull(gen, -1, data, size); + if (bytesRead == -1 && errno == ENOSYS) { + // Keep the random device open for the duration of the program. + static int randomFd = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC); + PCHECK(randomFd >= 0); + bytesRead = readFull(randomFd, data, size); + } + PCHECK(bytesRead >= 0); + CHECK_EQ(size_t(bytesRead), size); +#endif +} + +class BufferedRandomDevice { + public: + static constexpr size_t kDefaultBufferSize = 128; + + static void notifyNewGlobalEpoch() { + globalEpoch_.fetch_add(1, std::memory_order_relaxed); + } + + explicit BufferedRandomDevice(size_t bufferSize = kDefaultBufferSize); + + void get(void* data, size_t size) { + auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed); + if (LIKELY(globalEpoch == epoch_ && size <= remaining())) { + memcpy(data, ptr_, size); + ptr_ += size; + } else { + getSlow(static_cast<unsigned char*>(data), size); + } + } + + private: + void getSlow(unsigned char* data, size_t size); + + inline size_t remaining() const { + return size_t(buffer_.get() + bufferSize_ - ptr_); + } + + static std::atomic<size_t> globalEpoch_; + + size_t epoch_{size_t(-1)}; // refill on first use + const size_t bufferSize_; + std::unique_ptr<unsigned char[]> buffer_; + unsigned char* ptr_; +}; + +std::atomic<size_t> BufferedRandomDevice::globalEpoch_{0}; +struct RandomTag {}; + +BufferedRandomDevice::BufferedRandomDevice(size_t bufferSize) + : bufferSize_(bufferSize), + buffer_(new unsigned char[bufferSize]), + ptr_(buffer_.get() + bufferSize) { // refill on first use + FOLLY_MAYBE_UNUSED static auto const init = [] { + detail::AtFork::registerHandler( + nullptr, + /*prepare*/ []() { return true; }, + /*parent*/ []() {}, + /*child*/ + []() { + // Ensure child and parent do not share same entropy pool. + BufferedRandomDevice::notifyNewGlobalEpoch(); + }); + return 0; + }(); +} + +void BufferedRandomDevice::getSlow(unsigned char* data, size_t size) { + auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed); + if (globalEpoch != epoch_) { + epoch_ = globalEpoch_; + ptr_ = buffer_.get() + bufferSize_; + } + + DCHECK_GT(size, remaining()); + if (size >= bufferSize_) { + // Just read directly. + readRandomDevice(data, size); + return; + } + + size_t copied = remaining(); + memcpy(data, ptr_, copied); + data += copied; + size -= copied; + + // refill + readRandomDevice(buffer_.get(), bufferSize_); + ptr_ = buffer_.get(); + + memcpy(data, ptr_, size); + ptr_ += size; +} + +} // namespace + +void Random::secureRandom(void* data, size_t size) { + using Single = SingletonThreadLocal<BufferedRandomDevice, RandomTag>; + Single::get().get(data, size); +} + +ThreadLocalPRNG::result_type ThreadLocalPRNG::operator()() { + struct Wrapper { + Random::DefaultGenerator object{Random::create()}; + }; + using Single = SingletonThreadLocal<Wrapper, RandomTag>; + return Single::get().object(); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Random.h b/ios/Pods/Flipper-Folly/folly/Random.h new file mode 100644 index 000000000..907ff7bf5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Random.h @@ -0,0 +1,373 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_RANDOM_H_ + +#include <array> +#include <cstdint> +#include <random> +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> + +#if FOLLY_HAVE_EXTRANDOM_SFMT19937 +#include <ext/random> +#endif + +namespace folly { + +/** + * A PRNG with one instance per thread. This PRNG uses a mersenne twister random + * number generator and is seeded from /dev/urandom. It should not be used for + * anything which requires security, only for statistical randomness. + * + * An instance of this class represents the current threads PRNG. This means + * copying an instance of this class across threads will result in corruption + * + * Most users will use the Random class which implicitly creates this class. + * However, if you are worried about performance, you can memoize the TLS + * lookups that get the per thread state by manually using this class: + * + * ThreadLocalPRNG rng; + * for (...) { + * Random::rand32(rng); + * } + */ +class ThreadLocalPRNG { + public: + using result_type = uint32_t; + + result_type operator()(); + + static constexpr result_type min() { + return std::numeric_limits<result_type>::min(); + } + static constexpr result_type max() { + return std::numeric_limits<result_type>::max(); + } +}; + +class Random { + private: + template <class RNG> + using ValidRNG = typename std:: + enable_if<std::is_unsigned<invoke_result_t<RNG&>>::value, RNG>::type; + + template <class T> + class SecureRNG { + public: + using result_type = typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value, + T>::type; + + result_type operator()() { + return Random::secureRandom<result_type>(); + } + + static constexpr result_type min() { + return std::numeric_limits<result_type>::min(); + } + + static constexpr result_type max() { + return std::numeric_limits<result_type>::max(); + } + }; + + public: + // Default generator type. +#if FOLLY_HAVE_EXTRANDOM_SFMT19937 + typedef __gnu_cxx::sfmt19937 DefaultGenerator; +#else + typedef std::mt19937 DefaultGenerator; +#endif + + /** + * Get secure random bytes. (On Linux and OSX, this means /dev/urandom). + */ + static void secureRandom(void* data, size_t size); + + /** + * Shortcut to get a secure random value of integral type. + */ + template <class T> + static typename std::enable_if< + std::is_integral<T>::value && !std::is_same<T, bool>::value, + T>::type + secureRandom() { + T val; + secureRandom(&val, sizeof(val)); + return val; + } + + /** + * Returns a secure random uint32_t + */ + static uint32_t secureRand32() { + return secureRandom<uint32_t>(); + } + + /** + * Returns a secure random uint32_t in [0, max). If max == 0, returns 0. + */ + static uint32_t secureRand32(uint32_t max) { + SecureRNG<uint32_t> srng; + return rand32(max, srng); + } + + /** + * Returns a secure random uint32_t in [min, max). If min == max, returns 0. + */ + static uint32_t secureRand32(uint32_t min, uint32_t max) { + SecureRNG<uint32_t> srng; + return rand32(min, max, srng); + } + + /** + * Returns a secure random uint64_t + */ + static uint64_t secureRand64() { + return secureRandom<uint64_t>(); + } + + /** + * Returns a secure random uint64_t in [0, max). If max == 0, returns 0. + */ + static uint64_t secureRand64(uint64_t max) { + SecureRNG<uint64_t> srng; + return rand64(max, srng); + } + + /** + * Returns a secure random uint64_t in [min, max). If min == max, returns 0. + */ + static uint64_t secureRand64(uint64_t min, uint64_t max) { + SecureRNG<uint64_t> srng; + return rand64(min, max, srng); + } + + /** + * Returns true 1/n of the time. If n == 0, always returns false + */ + static bool secureOneIn(uint32_t n) { + SecureRNG<uint32_t> srng; + return rand32(0, n, srng) == 0; + } + + /** + * Returns a secure double in [0, 1) + */ + static double secureRandDouble01() { + SecureRNG<uint64_t> srng; + return randDouble01(srng); + } + + /** + * Returns a secure double in [min, max), if min == max, returns 0. + */ + static double secureRandDouble(double min, double max) { + SecureRNG<uint64_t> srng; + return randDouble(min, max, srng); + } + + /** + * (Re-)Seed an existing RNG with a good seed. + * + * Note that you should usually use ThreadLocalPRNG unless you need + * reproducibility (such as during a test), in which case you'd want + * to create a RNG with a good seed in production, and seed it yourself + * in test. + */ + template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>> + static void seed(RNG& rng); + + /** + * Create a new RNG, seeded with a good seed. + * + * Note that you should usually use ThreadLocalPRNG unless you need + * reproducibility (such as during a test), in which case you'd want + * to create a RNG with a good seed in production, and seed it yourself + * in test. + */ + template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>> + static RNG create(); + + /** + * Returns a random uint32_t + */ + static uint32_t rand32() { + return rand32(ThreadLocalPRNG()); + } + + /** + * Returns a random uint32_t given a specific RNG + */ + template <class RNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint32_t rand32(RNG&& rng) { + return rng(); + } + + /** + * Returns a random uint32_t in [0, max). If max == 0, returns 0. + */ + static uint32_t rand32(uint32_t max) { + return rand32(0, max, ThreadLocalPRNG()); + } + + /** + * Returns a random uint32_t in [0, max) given a specific RNG. + * If max == 0, returns 0. + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint32_t rand32(uint32_t max, RNG&& rng) { + return rand32(0, max, rng); + } + + /** + * Returns a random uint32_t in [min, max). If min == max, returns 0. + */ + static uint32_t rand32(uint32_t min, uint32_t max) { + return rand32(min, max, ThreadLocalPRNG()); + } + + /** + * Returns a random uint32_t in [min, max) given a specific RNG. + * If min == max, returns 0. + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint32_t rand32(uint32_t min, uint32_t max, RNG&& rng) { + if (min == max) { + return 0; + } + return std::uniform_int_distribution<uint32_t>(min, max - 1)(rng); + } + + /** + * Returns a random uint64_t + */ + static uint64_t rand64() { + return rand64(ThreadLocalPRNG()); + } + + /** + * Returns a random uint64_t + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint64_t rand64(RNG&& rng) { + return ((uint64_t)rng() << 32) | rng(); + } + + /** + * Returns a random uint64_t in [0, max). If max == 0, returns 0. + */ + static uint64_t rand64(uint64_t max) { + return rand64(0, max, ThreadLocalPRNG()); + } + + /** + * Returns a random uint64_t in [0, max). If max == 0, returns 0. + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint64_t rand64(uint64_t max, RNG&& rng) { + return rand64(0, max, rng); + } + + /** + * Returns a random uint64_t in [min, max). If min == max, returns 0. + */ + static uint64_t rand64(uint64_t min, uint64_t max) { + return rand64(min, max, ThreadLocalPRNG()); + } + + /** + * Returns a random uint64_t in [min, max). If min == max, returns 0. + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static uint64_t rand64(uint64_t min, uint64_t max, RNG&& rng) { + if (min == max) { + return 0; + } + return std::uniform_int_distribution<uint64_t>(min, max - 1)(rng); + } + + /** + * Returns true 1/n of the time. If n == 0, always returns false + */ + static bool oneIn(uint32_t n) { + return oneIn(n, ThreadLocalPRNG()); + } + + /** + * Returns true 1/n of the time. If n == 0, always returns false + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static bool oneIn(uint32_t n, RNG&& rng) { + if (n < 2) { + return n; + } + return rand32(0, n, rng) == 0; + } + + /** + * Returns a double in [0, 1) + */ + static double randDouble01() { + return randDouble01(ThreadLocalPRNG()); + } + + /** + * Returns a double in [0, 1) + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static double randDouble01(RNG&& rng) { + return std::generate_canonical<double, std::numeric_limits<double>::digits>( + rng); + } + + /** + * Returns a double in [min, max), if min == max, returns 0. + */ + static double randDouble(double min, double max) { + return randDouble(min, max, ThreadLocalPRNG()); + } + + /** + * Returns a double in [min, max), if min == max, returns 0. + */ + template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>> + static double randDouble(double min, double max, RNG&& rng) { + if (std::fabs(max - min) < std::numeric_limits<double>::epsilon()) { + return 0; + } + return std::uniform_real_distribution<double>(min, max)(rng); + } +}; + +/* + * Return a good seed for a random number generator. + * Note that this is a legacy function, as it returns a 32-bit value, which + * is too small to be useful as a "real" RNG seed. Use the functions in class + * Random instead. + */ +inline uint32_t randomNumberSeed() { + return Random::rand32(); +} + +} // namespace folly + +#include <folly/Random-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/Range.h b/ios/Pods/Flipper-Folly/folly/Range.h new file mode 100644 index 000000000..9b08d31f3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Range.h @@ -0,0 +1,1568 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author Mark Rabkin (mrabkin@fb.com) +// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) + +#pragma once + +#include <folly/Portability.h> +#include <folly/hash/SpookyHashV2.h> +#include <folly/lang/Exception.h> +#include <folly/portability/Constexpr.h> +#include <folly/portability/String.h> + +#include <algorithm> +#include <array> +#include <cassert> +#include <climits> +#include <cstddef> +#include <cstring> +#include <iosfwd> +#include <iterator> +#include <stdexcept> +#include <string> +#include <type_traits> + +#if FOLLY_HAS_STRING_VIEW +#include <string_view> // @manual +#endif + +#include <folly/CpuId.h> +#include <folly/Likely.h> +#include <folly/Traits.h> +#include <folly/detail/RangeCommon.h> +#include <folly/detail/RangeSse42.h> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +/** + * Ubiquitous helper template for knowing what's a string. + */ +template <class T> +struct IsSomeString : std::false_type {}; + +template <typename Alloc> +struct IsSomeString<std::basic_string<char, std::char_traits<char>, Alloc>> + : std::true_type {}; + +template <class Iter> +class Range; + +/** + * Finds the first occurrence of needle in haystack. The algorithm is on + * average faster than O(haystack.size() * needle.size()) but not as fast + * as Boyer-Moore. On the upside, it does not do any upfront + * preprocessing and does not allocate memory. + */ +template < + class Iter, + class Comp = std::equal_to<typename Range<Iter>::value_type>> +inline size_t +qfind(const Range<Iter>& haystack, const Range<Iter>& needle, Comp eq = Comp()); + +/** + * Finds the first occurrence of needle in haystack. The result is the + * offset reported to the beginning of haystack, or string::npos if + * needle wasn't found. + */ +template <class Iter> +size_t qfind( + const Range<Iter>& haystack, + const typename Range<Iter>::value_type& needle); + +/** + * Finds the last occurrence of needle in haystack. The result is the + * offset reported to the beginning of haystack, or string::npos if + * needle wasn't found. + */ +template <class Iter> +size_t rfind( + const Range<Iter>& haystack, + const typename Range<Iter>::value_type& needle); + +/** + * Finds the first occurrence of any element of needle in + * haystack. The algorithm is O(haystack.size() * needle.size()). + */ +template <class Iter> +inline size_t qfind_first_of( + const Range<Iter>& haystack, + const Range<Iter>& needle); + +/** + * Small internal helper - returns the value just before an iterator. + */ +namespace detail { + +/* + * Use IsCharPointer<T>::type to enable const char* or char*. + * Use IsCharPointer<T>::const_type to enable only const char*. + */ +template <class T> +struct IsCharPointer {}; + +template <> +struct IsCharPointer<char*> { + typedef int type; +}; + +template <> +struct IsCharPointer<const char*> { + typedef int const_type; + typedef int type; +}; + +} // namespace detail + +/** + * Range abstraction keeping a pair of iterators. We couldn't use + * boost's similar range abstraction because we need an API identical + * with the former StringPiece class, which is used by a lot of other + * code. This abstraction does fulfill the needs of boost's + * range-oriented algorithms though. + * + * (Keep memory lifetime in mind when using this class, since it + * doesn't manage the data it refers to - just like an iterator + * wouldn't.) + */ +template <class Iter> +class Range { + private: + template <typename Alloc> + using string = std::basic_string<char, std::char_traits<char>, Alloc>; + + public: + typedef std::size_t size_type; + typedef Iter iterator; + typedef Iter const_iterator; + typedef typename std::remove_reference< + typename std::iterator_traits<Iter>::reference>::type value_type; + using difference_type = typename std::iterator_traits<Iter>::difference_type; + typedef typename std::iterator_traits<Iter>::reference reference; + + /** + * For MutableStringPiece and MutableByteRange we define StringPiece + * and ByteRange as const_range_type (for everything else its just + * identity). We do that to enable operations such as find with + * args which are const. + */ + typedef typename std::conditional< + std::is_same<Iter, char*>::value || + std::is_same<Iter, unsigned char*>::value, + Range<const value_type*>, + Range<Iter>>::type const_range_type; + + typedef std::char_traits<typename std::remove_const<value_type>::type> + traits_type; + + static const size_type npos; + + // Works for all iterators + constexpr Range() : b_(), e_() {} + + constexpr Range(const Range&) = default; + constexpr Range(Range&&) = default; + + public: + // Works for all iterators + constexpr Range(Iter start, Iter end) : b_(start), e_(end) {} + + // Works only for random-access iterators + constexpr Range(Iter start, size_t size) : b_(start), e_(start + size) {} + + /* implicit */ Range(std::nullptr_t) = delete; + + constexpr /* implicit */ Range(Iter str) + : b_(str), e_(str + constexpr_strlen(str)) { + static_assert( + std::is_same<int, typename detail::IsCharPointer<Iter>::type>::value, + "This constructor is only available for character ranges"); + } + + template < + class Alloc, + class T = Iter, + typename detail::IsCharPointer<T>::const_type = 0> + /* implicit */ Range(const string<Alloc>& str) + : b_(str.data()), e_(b_ + str.size()) {} + + template < + class Alloc, + class T = Iter, + typename detail::IsCharPointer<T>::const_type = 0> + Range(const string<Alloc>& str, typename string<Alloc>::size_type startFrom) { + if (UNLIKELY(startFrom > str.size())) { + throw_exception<std::out_of_range>("index out of range"); + } + b_ = str.data() + startFrom; + e_ = str.data() + str.size(); + } + + template < + class Alloc, + class T = Iter, + typename detail::IsCharPointer<T>::const_type = 0> + Range( + const string<Alloc>& str, + typename string<Alloc>::size_type startFrom, + typename string<Alloc>::size_type size) { + if (UNLIKELY(startFrom > str.size())) { + throw_exception<std::out_of_range>("index out of range"); + } + b_ = str.data() + startFrom; + if (str.size() - startFrom < size) { + e_ = str.data() + str.size(); + } else { + e_ = b_ + size; + } + } + + Range(const Range& other, size_type first, size_type length = npos) + : Range(other.subpiece(first, length)) {} + + template < + class Container, + class = typename std::enable_if< + std::is_same<Iter, typename Container::const_pointer>::value>::type, + class = decltype( + Iter(std::declval<Container const&>().data()), + Iter( + std::declval<Container const&>().data() + + std::declval<Container const&>().size()))> + /* implicit */ constexpr Range(Container const& container) + : b_(container.data()), e_(b_ + container.size()) {} + + template < + class Container, + class = typename std::enable_if< + std::is_same<Iter, typename Container::const_pointer>::value>::type, + class = decltype( + Iter(std::declval<Container const&>().data()), + Iter( + std::declval<Container const&>().data() + + std::declval<Container const&>().size()))> + Range(Container const& container, typename Container::size_type startFrom) { + auto const cdata = container.data(); + auto const csize = container.size(); + if (UNLIKELY(startFrom > csize)) { + throw_exception<std::out_of_range>("index out of range"); + } + b_ = cdata + startFrom; + e_ = cdata + csize; + } + + template < + class Container, + class = typename std::enable_if< + std::is_same<Iter, typename Container::const_pointer>::value>::type, + class = decltype( + Iter(std::declval<Container const&>().data()), + Iter( + std::declval<Container const&>().data() + + std::declval<Container const&>().size()))> + Range( + Container const& container, + typename Container::size_type startFrom, + typename Container::size_type size) { + auto const cdata = container.data(); + auto const csize = container.size(); + if (UNLIKELY(startFrom > csize)) { + throw_exception<std::out_of_range>("index out of range"); + } + b_ = cdata + startFrom; + if (csize - startFrom < size) { + e_ = cdata + csize; + } else { + e_ = b_ + size; + } + } + + // Allow implicit conversion from Range<const char*> (aka StringPiece) to + // Range<const unsigned char*> (aka ByteRange), as they're both frequently + // used to represent ranges of bytes. Allow explicit conversion in the other + // direction. + template < + class OtherIter, + typename std::enable_if< + (std::is_same<Iter, const unsigned char*>::value && + (std::is_same<OtherIter, const char*>::value || + std::is_same<OtherIter, char*>::value)), + int>::type = 0> + /* implicit */ Range(const Range<OtherIter>& other) + : b_(reinterpret_cast<const unsigned char*>(other.begin())), + e_(reinterpret_cast<const unsigned char*>(other.end())) {} + + template < + class OtherIter, + typename std::enable_if< + (std::is_same<Iter, unsigned char*>::value && + std::is_same<OtherIter, char*>::value), + int>::type = 0> + /* implicit */ Range(const Range<OtherIter>& other) + : b_(reinterpret_cast<unsigned char*>(other.begin())), + e_(reinterpret_cast<unsigned char*>(other.end())) {} + + template < + class OtherIter, + typename std::enable_if< + (std::is_same<Iter, const char*>::value && + (std::is_same<OtherIter, const unsigned char*>::value || + std::is_same<OtherIter, unsigned char*>::value)), + int>::type = 0> + explicit Range(const Range<OtherIter>& other) + : b_(reinterpret_cast<const char*>(other.begin())), + e_(reinterpret_cast<const char*>(other.end())) {} + + template < + class OtherIter, + typename std::enable_if< + (std::is_same<Iter, char*>::value && + std::is_same<OtherIter, unsigned char*>::value), + int>::type = 0> + explicit Range(const Range<OtherIter>& other) + : b_(reinterpret_cast<char*>(other.begin())), + e_(reinterpret_cast<char*>(other.end())) {} + + // Allow implicit conversion from Range<From> to Range<To> if From is + // implicitly convertible to To. + template < + class OtherIter, + typename std::enable_if< + (!std::is_same<Iter, OtherIter>::value && + std::is_convertible<OtherIter, Iter>::value), + int>::type = 0> + constexpr /* implicit */ Range(const Range<OtherIter>& other) + : b_(other.begin()), e_(other.end()) {} + + // Allow explicit conversion from Range<From> to Range<To> if From is + // explicitly convertible to To. + template < + class OtherIter, + typename std::enable_if< + (!std::is_same<Iter, OtherIter>::value && + !std::is_convertible<OtherIter, Iter>::value && + std::is_constructible<Iter, const OtherIter&>::value), + int>::type = 0> + constexpr explicit Range(const Range<OtherIter>& other) + : b_(other.begin()), e_(other.end()) {} + + /** + * Allow explicit construction of Range() from a std::array of a + * convertible type. + * + * For instance, this allows constructing StringPiece from a + * std::array<char, N> or a std::array<const char, N> + */ + template < + class T, + size_t N, + typename = typename std::enable_if< + std::is_convertible<const T*, Iter>::value>::type> + constexpr explicit Range(const std::array<T, N>& array) + : b_{array.empty() ? nullptr : &array.at(0)}, + e_{array.empty() ? nullptr : &array.at(0) + N} {} + template < + class T, + size_t N, + typename = + typename std::enable_if<std::is_convertible<T*, Iter>::value>::type> + constexpr explicit Range(std::array<T, N>& array) + : b_{array.empty() ? nullptr : &array.at(0)}, + e_{array.empty() ? nullptr : &array.at(0) + N} {} + + Range& operator=(const Range& rhs) & = default; + Range& operator=(Range&& rhs) & = default; + + template < + class Alloc, + class T = Iter, + typename detail::IsCharPointer<T>::const_type = 0> + Range& operator=(string<Alloc>&& rhs) = delete; + + void clear() { + b_ = Iter(); + e_ = Iter(); + } + + void assign(Iter start, Iter end) { + b_ = start; + e_ = end; + } + + void reset(Iter start, size_type size) { + b_ = start; + e_ = start + size; + } + + // Works only for Range<const char*> + template <typename Alloc> + void reset(const string<Alloc>& str) { + reset(str.data(), str.size()); + } + + constexpr size_type size() const { +#if __clang__ || !__GNUC__ || __GNUC__ >= 7 + assert(b_ <= e_); +#endif + return size_type(e_ - b_); + } + constexpr size_type walk_size() const { + return size_type(std::distance(b_, e_)); + } + constexpr bool empty() const { + return b_ == e_; + } + constexpr Iter data() const { + return b_; + } + constexpr Iter start() const { + return b_; + } + constexpr Iter begin() const { + return b_; + } + constexpr Iter end() const { + return e_; + } + constexpr Iter cbegin() const { + return b_; + } + constexpr Iter cend() const { + return e_; + } + value_type& front() { + assert(b_ < e_); + return *b_; + } + value_type& back() { + assert(b_ < e_); + return *std::prev(e_); + } + const value_type& front() const { + assert(b_ < e_); + return *b_; + } + const value_type& back() const { + assert(b_ < e_); + return *std::prev(e_); + } + + private: + // It would be nice to be able to implicit convert to any target type + // T for which either an (Iter, Iter) or (Iter, size_type) noexcept + // constructor was available, and explicitly convert to any target + // type for which those signatures were available but not noexcept. + // The problem is that this creates ambiguity when there is also a + // T constructor that takes a type U that is implicitly convertible + // from Range. + // + // To avoid ambiguity, we need to avoid having explicit operator T + // and implicit operator U coexist when T is constructible from U. + // U cannot be deduced when searching for operator T (and C++ won't + // perform an existential search for it), so we must limit the implicit + // target types to a finite set that we can enumerate. + // + // At the moment the set of implicit target types consists of just + // std::string_view (when it is available). +#if FOLLY_HAS_STRING_VIEW + struct NotStringView {}; + template <typename ValueType> + struct StringViewType + : std::conditional< + std::is_pod<std::remove_const_t<ValueType>>::value, + std::basic_string_view<std::remove_const_t<ValueType>>, + NotStringView> {}; + + template <typename Target> + struct IsConstructibleViaStringView + : Conjunction< + std::is_constructible< + _t<StringViewType<value_type>>, + Iter const&, + size_type>, + std::is_constructible<Target, _t<StringViewType<value_type>>>> {}; +#else + template <typename Target> + using IsConstructibleViaStringView = std::false_type; +#endif + + public: + /// explicit operator conversion to any compatible type + /// + /// A compatible type is one which is constructible with an iterator and a + /// size (preferred), or a pair of iterators (fallback), passed by const-ref. + /// + /// Participates in overload resolution precisely when the target type is + /// compatible. This allows std::is_constructible compile-time checks to work. + template < + typename Tgt, + std::enable_if_t< + std::is_constructible<Tgt, Iter const&, size_type>::value && + !IsConstructibleViaStringView<Tgt>::value, + int> = 0> + constexpr explicit operator Tgt() const noexcept( + std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) { + return Tgt(b_, walk_size()); + } + template < + typename Tgt, + std::enable_if_t< + !std::is_constructible<Tgt, Iter const&, size_type>::value && + std::is_constructible<Tgt, Iter const&, Iter const&>::value && + !IsConstructibleViaStringView<Tgt>::value, + int> = 0> + constexpr explicit operator Tgt() const noexcept( + std::is_nothrow_constructible<Tgt, Iter const&, Iter const&>::value) { + return Tgt(b_, e_); + } + +#if FOLLY_HAS_STRING_VIEW + /// implicit operator conversion to std::string_view + template < + typename Tgt, + typename ValueType = value_type, + std::enable_if_t< + StrictConjunction< + std::is_same<Tgt, _t<StringViewType<ValueType>>>, + std::is_constructible< + _t<StringViewType<ValueType>>, + Iter const&, + size_type>>::value, + int> = 0> + constexpr operator Tgt() const noexcept( + std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) { + return Tgt(b_, walk_size()); + } +#endif + + /// explicit non-operator conversion to any compatible type + /// + /// A compatible type is one which is constructible with an iterator and a + /// size (preferred), or a pair of iterators (fallback), passed by const-ref. + /// + /// Participates in overload resolution precisely when the target type is + /// compatible. This allows is_invocable compile-time checks to work. + /// + /// Provided in addition to the explicit operator conversion to permit passing + /// additional arguments to the target type constructor. A canonical example + /// of an additional argument might be an allocator, where the target type is + /// some specialization of std::vector or std::basic_string in a context which + /// requires a non-default-constructed allocator. + template <typename Tgt, typename... Args> + constexpr std::enable_if_t< + std::is_constructible<Tgt, Iter const&, size_type>::value, + Tgt> + to(Args&&... args) const noexcept( + std::is_nothrow_constructible<Tgt, Iter const&, size_type, Args&&...>:: + value) { + return Tgt(b_, walk_size(), static_cast<Args&&>(args)...); + } + template <typename Tgt, typename... Args> + constexpr std::enable_if_t< + !std::is_constructible<Tgt, Iter const&, size_type>::value && + std::is_constructible<Tgt, Iter const&, Iter const&>::value, + Tgt> + to(Args&&... args) const noexcept( + std::is_nothrow_constructible<Tgt, Iter const&, Iter const&, Args&&...>:: + value) { + return Tgt(b_, e_, static_cast<Args&&>(args)...); + } + + // Works only for Range<const char*> and Range<char*> + std::string str() const { + return to<std::string>(); + } + std::string toString() const { + return to<std::string>(); + } + + const_range_type castToConst() const { + return const_range_type(*this); + } + + int compare(const const_range_type& o) const { + const size_type tsize = this->size(); + const size_type osize = o.size(); + const size_type msize = std::min(tsize, osize); + int r = traits_type::compare(data(), o.data(), msize); + if (r == 0 && tsize != osize) { + // We check the signed bit of the subtraction and bit shift it + // to produce either 0 or 2. The subtraction yields the + // comparison values of either -1 or 1. + r = (static_cast<int>((osize - tsize) >> (CHAR_BIT * sizeof(size_t) - 1)) + << 1) - + 1; + } + return r; + } + + value_type& operator[](size_t i) { + assert(i < size()); + return b_[i]; + } + + const value_type& operator[](size_t i) const { + assert(i < size()); + return b_[i]; + } + + value_type& at(size_t i) { + if (i >= size()) { + throw_exception<std::out_of_range>("index out of range"); + } + return b_[i]; + } + + const value_type& at(size_t i) const { + if (i >= size()) { + throw_exception<std::out_of_range>("index out of range"); + } + return b_[i]; + } + + // Do NOT use this function, which was left behind for backwards + // compatibility. Use SpookyHashV2 instead -- it is faster, and produces + // a 64-bit hash, which means dramatically fewer collisions in large maps. + // (The above advice does not apply if you are targeting a 32-bit system.) + // + // Works only for Range<const char*> and Range<char*> + // + // + // ** WANT TO GET RID OF THIS LINT? ** + // + // A) Use a better hash function (*cough*folly::Hash*cough*), but + // only if you don't serialize data in a format that depends on + // this formula (ie the writer and reader assume this exact hash + // function is used). + // + // B) If you have to use this exact function then make your own hasher + // object and copy the body over (see thrift example: D3972362). + // https://github.com/facebook/fbthrift/commit/f8ed502e24ab4a32a9d5f266580 + [[deprecated( + "Replace with folly::Hash if the hash is not serialized")]] uint32_t + hash() const { + // Taken from fbi/nstring.h: + // Quick and dirty bernstein hash...fine for short ascii strings + uint32_t hash = 5381; + for (size_t ix = 0; ix < size(); ix++) { + hash = ((hash << 5) + hash) + b_[ix]; + } + return hash; + } + + void advance(size_type n) { + if (UNLIKELY(n > size())) { + throw_exception<std::out_of_range>("index out of range"); + } + b_ += n; + } + + void subtract(size_type n) { + if (UNLIKELY(n > size())) { + throw_exception<std::out_of_range>("index out of range"); + } + e_ -= n; + } + + Range subpiece(size_type first, size_type length = npos) const { + if (UNLIKELY(first > size())) { + throw_exception<std::out_of_range>("index out of range"); + } + + return Range(b_ + first, std::min(length, size() - first)); + } + + // unchecked versions + void uncheckedAdvance(size_type n) { + assert(n <= size()); + b_ += n; + } + + void uncheckedSubtract(size_type n) { + assert(n <= size()); + e_ -= n; + } + + Range uncheckedSubpiece(size_type first, size_type length = npos) const { + assert(first <= size()); + return Range(b_ + first, std::min(length, size() - first)); + } + + void pop_front() { + assert(b_ < e_); + ++b_; + } + + void pop_back() { + assert(b_ < e_); + --e_; + } + + // string work-alike functions + size_type find(const_range_type str) const { + return qfind(castToConst(), str); + } + + size_type find(const_range_type str, size_t pos) const { + if (pos > size()) { + return std::string::npos; + } + size_t ret = qfind(castToConst().subpiece(pos), str); + return ret == npos ? ret : ret + pos; + } + + size_type find(Iter s, size_t pos, size_t n) const { + if (pos > size()) { + return std::string::npos; + } + auto forFinding = castToConst(); + size_t ret = qfind( + pos ? forFinding.subpiece(pos) : forFinding, const_range_type(s, n)); + return ret == npos ? ret : ret + pos; + } + + // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor + size_type find(const Iter s) const { + return qfind(castToConst(), const_range_type(s)); + } + + // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor + size_type find(const Iter s, size_t pos) const { + if (pos > size()) { + return std::string::npos; + } + size_type ret = qfind(castToConst().subpiece(pos), const_range_type(s)); + return ret == npos ? ret : ret + pos; + } + + size_type find(value_type c) const { + return qfind(castToConst(), c); + } + + size_type rfind(value_type c) const { + return folly::rfind(castToConst(), c); + } + + size_type find(value_type c, size_t pos) const { + if (pos > size()) { + return std::string::npos; + } + size_type ret = qfind(castToConst().subpiece(pos), c); + return ret == npos ? ret : ret + pos; + } + + size_type find_first_of(const_range_type needles) const { + return qfind_first_of(castToConst(), needles); + } + + size_type find_first_of(const_range_type needles, size_t pos) const { + if (pos > size()) { + return std::string::npos; + } + size_type ret = qfind_first_of(castToConst().subpiece(pos), needles); + return ret == npos ? ret : ret + pos; + } + + // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor + size_type find_first_of(Iter needles) const { + return find_first_of(const_range_type(needles)); + } + + // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor + size_type find_first_of(Iter needles, size_t pos) const { + return find_first_of(const_range_type(needles), pos); + } + + size_type find_first_of(Iter needles, size_t pos, size_t n) const { + return find_first_of(const_range_type(needles, n), pos); + } + + size_type find_first_of(value_type c) const { + return find(c); + } + + size_type find_first_of(value_type c, size_t pos) const { + return find(c, pos); + } + + /** + * Determine whether the range contains the given subrange or item. + * + * Note: Call find() directly if the index is needed. + */ + bool contains(const const_range_type& other) const { + return find(other) != std::string::npos; + } + + bool contains(const value_type& other) const { + return find(other) != std::string::npos; + } + + void swap(Range& rhs) { + std::swap(b_, rhs.b_); + std::swap(e_, rhs.e_); + } + + /** + * Does this Range start with another range? + */ + bool startsWith(const const_range_type& other) const { + return size() >= other.size() && + castToConst().subpiece(0, other.size()) == other; + } + bool startsWith(value_type c) const { + return !empty() && front() == c; + } + + template <class Comp> + bool startsWith(const const_range_type& other, Comp&& eq) const { + if (size() < other.size()) { + return false; + } + auto const trunc = subpiece(0, other.size()); + return std::equal( + trunc.begin(), trunc.end(), other.begin(), std::forward<Comp>(eq)); + } + + /** + * Does this Range end with another range? + */ + bool endsWith(const const_range_type& other) const { + return size() >= other.size() && + castToConst().subpiece(size() - other.size()) == other; + } + bool endsWith(value_type c) const { + return !empty() && back() == c; + } + + template <class Comp> + bool endsWith(const const_range_type& other, Comp&& eq) const { + if (size() < other.size()) { + return false; + } + auto const trunc = subpiece(size() - other.size()); + return std::equal( + trunc.begin(), trunc.end(), other.begin(), std::forward<Comp>(eq)); + } + + template <class Comp> + bool equals(const const_range_type& other, Comp&& eq) const { + return size() == other.size() && + std::equal(begin(), end(), other.begin(), std::forward<Comp>(eq)); + } + + /** + * Remove the items in [b, e), as long as this subrange is at the beginning + * or end of the Range. + * + * Required for boost::algorithm::trim() + */ + void erase(Iter b, Iter e) { + if (b == b_) { + b_ = e; + } else if (e == e_) { + e_ = b; + } else { + throw_exception<std::out_of_range>("index out of range"); + } + } + + /** + * Remove the given prefix and return true if the range starts with the given + * prefix; return false otherwise. + */ + bool removePrefix(const const_range_type& prefix) { + return startsWith(prefix) && (b_ += prefix.size(), true); + } + bool removePrefix(value_type prefix) { + return startsWith(prefix) && (++b_, true); + } + + /** + * Remove the given suffix and return true if the range ends with the given + * suffix; return false otherwise. + */ + bool removeSuffix(const const_range_type& suffix) { + return endsWith(suffix) && (e_ -= suffix.size(), true); + } + bool removeSuffix(value_type suffix) { + return endsWith(suffix) && (--e_, true); + } + + /** + * Replaces the content of the range, starting at position 'pos', with + * contents of 'replacement'. Entire 'replacement' must fit into the + * range. Returns false if 'replacements' does not fit. Example use: + * + * char in[] = "buffer"; + * auto msp = MutablesStringPiece(input); + * EXPECT_TRUE(msp.replaceAt(2, "tt")); + * EXPECT_EQ(msp, "butter"); + * + * // not enough space + * EXPECT_FALSE(msp.replace(msp.size() - 1, "rr")); + * EXPECT_EQ(msp, "butter"); // unchanged + */ + bool replaceAt(size_t pos, const_range_type replacement) { + if (size() < pos + replacement.size()) { + return false; + } + + std::copy(replacement.begin(), replacement.end(), begin() + pos); + + return true; + } + + /** + * Replaces all occurences of 'source' with 'dest'. Returns number + * of replacements made. Source and dest have to have the same + * length. Throws if the lengths are different. If 'source' is a + * pattern that is overlapping with itself, we perform sequential + * replacement: "aaaaaaa".replaceAll("aa", "ba") --> "bababaa" + * + * Example use: + * + * char in[] = "buffer"; + * auto msp = MutablesStringPiece(input); + * EXPECT_EQ(msp.replaceAll("ff","tt"), 1); + * EXPECT_EQ(msp, "butter"); + */ + size_t replaceAll(const_range_type source, const_range_type dest) { + if (source.size() != dest.size()) { + throw_exception<std::invalid_argument>( + "replacement must have the same size as source"); + } + + if (dest.empty()) { + return 0; + } + + size_t pos = 0; + size_t num_replaced = 0; + size_type found = std::string::npos; + while ((found = find(source, pos)) != std::string::npos) { + replaceAt(found, dest); + pos += source.size(); + ++num_replaced; + } + + return num_replaced; + } + + /** + * Splits this `Range` `[b, e)` in the position `i` dictated by the next + * occurence of `delimiter`. + * + * Returns a new `Range` `[b, i)` and adjusts this range to start right after + * the delimiter's position. This range will be empty if the delimiter is not + * found. If called on an empty `Range`, both this and the returned `Range` + * will be empty. + * + * Example: + * + * folly::StringPiece s("sample string for split_next"); + * auto p = s.split_step(' '); + * + * // prints "string for split_next" + * cout << s << endl; + * + * // prints "sample" + * cout << p << endl; + * + * Example 2: + * + * void tokenize(StringPiece s, char delimiter) { + * while (!s.empty()) { + * cout << s.split_step(delimiter); + * } + * } + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + Range split_step(value_type delimiter) { + auto i = std::find(b_, e_, delimiter); + Range result(b_, i); + + b_ = i == e_ ? e_ : std::next(i); + + return result; + } + + Range split_step(Range delimiter) { + auto i = find(delimiter); + Range result(b_, i == std::string::npos ? size() : i); + + b_ = result.end() == e_ + ? e_ + : std::next( + result.end(), + typename std::iterator_traits<Iter>::difference_type( + delimiter.size())); + + return result; + } + + /** + * Convenience method that calls `split_step()` and passes the result to a + * functor, returning whatever the functor does. Any additional arguments + * `args` passed to this function are perfectly forwarded to the functor. + * + * Say you have a functor with this signature: + * + * Foo fn(Range r) { } + * + * `split_step()`'s return type will be `Foo`. It works just like: + * + * auto result = fn(myRange.split_step(' ')); + * + * A functor returning `void` is also supported. + * + * Example: + * + * void do_some_parsing(folly::StringPiece s) { + * auto version = s.split_step(' ', [&](folly::StringPiece x) { + * if (x.empty()) { + * throw std::invalid_argument("empty string"); + * } + * return std::strtoull(x.begin(), x.end(), 16); + * }); + * + * // ... + * } + * + * struct Foo { + * void parse(folly::StringPiece s) { + * s.split_step(' ', parse_field, bar, 10); + * s.split_step('\t', parse_field, baz, 20); + * + * auto const kludge = [](folly::StringPiece x, int &out, int def) { + * if (x == "null") { + * out = 0; + * } else { + * parse_field(x, out, def); + * } + * }; + * + * s.split_step('\t', kludge, gaz); + * s.split_step(' ', kludge, foo); + * } + * + * private: + * int bar; + * int baz; + * int gaz; + * int foo; + * + * static parse_field(folly::StringPiece s, int &out, int def) { + * try { + * out = folly::to<int>(s); + * } catch (std::exception const &) { + * value = def; + * } + * } + * }; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + template <typename TProcess, typename... Args> + auto split_step(value_type delimiter, TProcess&& process, Args&&... args) + -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...)) { + return process(split_step(delimiter), std::forward<Args>(args)...); + } + + template <typename TProcess, typename... Args> + auto split_step(Range delimiter, TProcess&& process, Args&&... args) + -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...)) { + return process(split_step(delimiter), std::forward<Args>(args)...); + } + + private: + Iter b_, e_; +}; + +template <class Iter> +const typename Range<Iter>::size_type Range<Iter>::npos = std::string::npos; + +template <class Iter> +void swap(Range<Iter>& lhs, Range<Iter>& rhs) { + lhs.swap(rhs); +} + +/** + * Create a range from two iterators, with type deduction. + */ +template <class Iter> +constexpr Range<Iter> range(Iter first, Iter last) { + return Range<Iter>(first, last); +} + +/* + * Creates a range to reference the contents of a contiguous-storage container. + */ +// Use pointers for types with '.data()' member +template <class Collection> +constexpr auto range(Collection& v) -> Range<decltype(v.data())> { + return Range<decltype(v.data())>(v.data(), v.data() + v.size()); +} +template <class Collection> +constexpr auto range(Collection const& v) -> Range<decltype(v.data())> { + return Range<decltype(v.data())>(v.data(), v.data() + v.size()); +} +template <class Collection> +constexpr auto crange(Collection const& v) -> Range<decltype(v.data())> { + return Range<decltype(v.data())>(v.data(), v.data() + v.size()); +} + +template <class T, size_t n> +constexpr Range<T*> range(T (&array)[n]) { + return Range<T*>(array, array + n); +} +template <class T, size_t n> +constexpr Range<T const*> range(T const (&array)[n]) { + return Range<T const*>(array, array + n); +} +template <class T, size_t n> +constexpr Range<T const*> crange(T const (&array)[n]) { + return Range<T const*>(array, array + n); +} + +template <class T, size_t n> +constexpr Range<T*> range(std::array<T, n>& array) { + return Range<T*>{array}; +} +template <class T, size_t n> +constexpr Range<T const*> range(std::array<T, n> const& array) { + return Range<T const*>{array}; +} +template <class T, size_t n> +constexpr Range<T const*> crange(std::array<T, n> const& array) { + return Range<T const*>{array}; +} + +typedef Range<const char*> StringPiece; +typedef Range<char*> MutableStringPiece; +typedef Range<const unsigned char*> ByteRange; +typedef Range<unsigned char*> MutableByteRange; + +template <class C> +std::basic_ostream<C>& operator<<( + std::basic_ostream<C>& os, + Range<C const*> piece) { + using StreamSize = decltype(os.width()); + os.write(piece.start(), static_cast<StreamSize>(piece.size())); + return os; +} + +template <class C> +std::basic_ostream<C>& operator<<(std::basic_ostream<C>& os, Range<C*> piece) { + using StreamSize = decltype(os.width()); + os.write(piece.start(), static_cast<StreamSize>(piece.size())); + return os; +} + +/** + * Templated comparison operators + */ + +template <class Iter> +inline bool operator==(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; +} + +template <class Iter> +inline bool operator!=(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return !(operator==(lhs, rhs)); +} + +template <class Iter> +inline bool operator<(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return lhs.compare(rhs) < 0; +} + +template <class Iter> +inline bool operator<=(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return lhs.compare(rhs) <= 0; +} + +template <class Iter> +inline bool operator>(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return lhs.compare(rhs) > 0; +} + +template <class Iter> +inline bool operator>=(const Range<Iter>& lhs, const Range<Iter>& rhs) { + return lhs.compare(rhs) >= 0; +} + +/** + * Specializations of comparison operators for StringPiece + */ + +namespace detail { + +template <class A, class B> +struct ComparableAsStringPiece { + enum { + value = (std::is_convertible<A, StringPiece>::value && + std::is_same<B, StringPiece>::value) || + (std::is_convertible<B, StringPiece>::value && + std::is_same<A, StringPiece>::value) + }; +}; + +} // namespace detail + +/** + * operator== through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator==( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) == StringPiece(rhs); +} + +/** + * operator!= through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator!=( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) != StringPiece(rhs); +} + +/** + * operator< through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator<( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) < StringPiece(rhs); +} + +/** + * operator> through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator>( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) > StringPiece(rhs); +} + +/** + * operator< through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator<=( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) <= StringPiece(rhs); +} + +/** + * operator> through conversion for Range<const char*> + */ +template <class T, class U> +std::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator>=( + const T& lhs, + const U& rhs) { + return StringPiece(lhs) >= StringPiece(rhs); +} + +/** + * Finds substrings faster than brute force by borrowing from Boyer-Moore + */ +template <class Iter, class Comp> +size_t qfind(const Range<Iter>& haystack, const Range<Iter>& needle, Comp eq) { + // Don't use std::search, use a Boyer-Moore-like trick by comparing + // the last characters first + auto const nsize = needle.size(); + if (haystack.size() < nsize) { + return std::string::npos; + } + if (!nsize) { + return 0; + } + auto const nsize_1 = nsize - 1; + auto const lastNeedle = needle[nsize_1]; + + // Boyer-Moore skip value for the last char in the needle. Zero is + // not a valid value; skip will be computed the first time it's + // needed. + std::string::size_type skip = 0; + + auto i = haystack.begin(); + auto iEnd = haystack.end() - nsize_1; + + while (i < iEnd) { + // Boyer-Moore: match the last element in the needle + while (!eq(i[nsize_1], lastNeedle)) { + if (++i == iEnd) { + // not found + return std::string::npos; + } + } + // Here we know that the last char matches + // Continue in pedestrian mode + for (size_t j = 0;;) { + assert(j < nsize); + if (!eq(i[j], needle[j])) { + // Not found, we can skip + // Compute the skip value lazily + if (skip == 0) { + skip = 1; + while (skip <= nsize_1 && !eq(needle[nsize_1 - skip], lastNeedle)) { + ++skip; + } + } + i += skip; + break; + } + // Check if done searching + if (++j == nsize) { + // Yay + return size_t(i - haystack.begin()); + } + } + } + return std::string::npos; +} + +namespace detail { + +inline size_t qfind_first_byte_of( + const StringPiece haystack, + const StringPiece needles) { + static auto const qfind_first_byte_of_fn = folly::CpuId().sse42() + ? qfind_first_byte_of_sse42 + : qfind_first_byte_of_nosse; + return qfind_first_byte_of_fn(haystack, needles); +} + +} // namespace detail + +template <class Iter, class Comp> +size_t qfind_first_of( + const Range<Iter>& haystack, + const Range<Iter>& needles, + Comp eq) { + auto ret = std::find_first_of( + haystack.begin(), haystack.end(), needles.begin(), needles.end(), eq); + return ret == haystack.end() ? std::string::npos : ret - haystack.begin(); +} + +struct AsciiCaseSensitive { + bool operator()(char lhs, char rhs) const { + return lhs == rhs; + } +}; + +/** + * Check if two ascii characters are case insensitive equal. + * The difference between the lower/upper case characters are the 6-th bit. + * We also check they are alpha chars, in case of xor = 32. + */ +struct AsciiCaseInsensitive { + bool operator()(char lhs, char rhs) const { + char k = lhs ^ rhs; + if (k == 0) { + return true; + } + if (k != 32) { + return false; + } + k = lhs | rhs; + return (k >= 'a' && k <= 'z'); + } +}; + +template <class Iter> +size_t qfind( + const Range<Iter>& haystack, + const typename Range<Iter>::value_type& needle) { + auto pos = std::find(haystack.begin(), haystack.end(), needle); + return pos == haystack.end() ? std::string::npos : pos - haystack.data(); +} + +template <class Iter> +size_t rfind( + const Range<Iter>& haystack, + const typename Range<Iter>::value_type& needle) { + for (auto i = haystack.size(); i-- > 0;) { + if (haystack[i] == needle) { + return i; + } + } + return std::string::npos; +} + +// specialization for StringPiece +template <> +inline size_t qfind(const Range<const char*>& haystack, const char& needle) { + // memchr expects a not-null pointer, early return if the range is empty. + if (haystack.empty()) { + return std::string::npos; + } + auto pos = static_cast<const char*>( + ::memchr(haystack.data(), needle, haystack.size())); + return pos == nullptr ? std::string::npos : pos - haystack.data(); +} + +template <> +inline size_t rfind(const Range<const char*>& haystack, const char& needle) { + // memchr expects a not-null pointer, early return if the range is empty. + if (haystack.empty()) { + return std::string::npos; + } + auto pos = static_cast<const char*>( + ::memrchr(haystack.data(), needle, haystack.size())); + return pos == nullptr ? std::string::npos : pos - haystack.data(); +} + +// specialization for ByteRange +template <> +inline size_t qfind( + const Range<const unsigned char*>& haystack, + const unsigned char& needle) { + // memchr expects a not-null pointer, early return if the range is empty. + if (haystack.empty()) { + return std::string::npos; + } + auto pos = static_cast<const unsigned char*>( + ::memchr(haystack.data(), needle, haystack.size())); + return pos == nullptr ? std::string::npos : pos - haystack.data(); +} + +template <> +inline size_t rfind( + const Range<const unsigned char*>& haystack, + const unsigned char& needle) { + // memchr expects a not-null pointer, early return if the range is empty. + if (haystack.empty()) { + return std::string::npos; + } + auto pos = static_cast<const unsigned char*>( + ::memrchr(haystack.data(), needle, haystack.size())); + return pos == nullptr ? std::string::npos : pos - haystack.data(); +} + +template <class Iter> +size_t qfind_first_of(const Range<Iter>& haystack, const Range<Iter>& needles) { + return qfind_first_of(haystack, needles, AsciiCaseSensitive()); +} + +// specialization for StringPiece +template <> +inline size_t qfind_first_of( + const Range<const char*>& haystack, + const Range<const char*>& needles) { + return detail::qfind_first_byte_of(haystack, needles); +} + +// specialization for ByteRange +template <> +inline size_t qfind_first_of( + const Range<const unsigned char*>& haystack, + const Range<const unsigned char*>& needles) { + return detail::qfind_first_byte_of( + StringPiece(haystack), StringPiece(needles)); +} + +template <class Key, class Enable> +struct hasher; + +template <class T> +struct hasher< + folly::Range<T*>, + std::enable_if_t<std::is_integral<T>::value, void>> { + using folly_is_avalanching = std::true_type; + + size_t operator()(folly::Range<T*> r) const { + // std::is_integral<T> is too restrictive, but is sufficient to + // guarantee we can just hash all of the underlying bytes to get a + // suitable hash of T. Something like absl::is_uniquely_represented<T> + // would be better. std::is_pod is not enough, because POD types + // can contain pointers and padding. Also, floating point numbers + // may be == without being bit-identical. + return hash::SpookyHashV2::Hash64(r.begin(), r.size() * sizeof(T), 0); + } +}; + +/** + * _sp is a user-defined literal suffix to make an appropriate Range + * specialization from a literal string. + * + * Modeled after C++17's `sv` suffix. + */ +inline namespace literals { +inline namespace string_piece_literals { +constexpr Range<char const*> operator"" _sp( + char const* str, + size_t len) noexcept { + return Range<char const*>(str, len); +} + +#if __cpp_char8_t >= 201811L +constexpr Range<char8_t const*> operator"" _sp( + char8_t const* str, + size_t len) noexcept { + return Range<char8_t const*>(str, len); +} +#endif + +constexpr Range<char16_t const*> operator"" _sp( + char16_t const* str, + size_t len) noexcept { + return Range<char16_t const*>(str, len); +} + +constexpr Range<char32_t const*> operator"" _sp( + char32_t const* str, + size_t len) noexcept { + return Range<char32_t const*>(str, len); +} + +constexpr Range<wchar_t const*> operator"" _sp( + wchar_t const* str, + size_t len) noexcept { + return Range<wchar_t const*>(str, len); +} +} // namespace string_piece_literals +} // namespace literals + +} // namespace folly + +FOLLY_POP_WARNING + +FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(folly::Range) + +// Tell the range-v3 library that this type should satisfy +// the view concept (a lightweight, non-owning range). +namespace ranges { +template <class T> +extern const bool enable_view; + +template <class Iter> +FOLLY_INLINE_VARIABLE constexpr bool enable_view<::folly::Range<Iter>> = true; +} // namespace ranges diff --git a/ios/Pods/Flipper-Folly/folly/Replaceable.h b/ios/Pods/Flipper-Folly/folly/Replaceable.h new file mode 100644 index 000000000..c0879f25d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Replaceable.h @@ -0,0 +1,639 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <initializer_list> +#include <new> +#include <type_traits> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/lang/Launder.h> + +/** + * An instance of `Replaceable<T>` wraps an instance of `T`. + * + * You access the inner `T` instance with `operator*` and `operator->` (as if + * it were a smart pointer). + * + * `Replaceable<T>` adds no indirection cost and performs no allocations. + * + * `Replaceable<T>` has the same size and alignment as `T`. + * + * You can replace the `T` within a `Replaceable<T>` using the `emplace` method + * (presuming that it is constructible and destructible without throwing + * exceptions). If the destructor or constructor you're using could throw an + * exception you should use `Optional<T>` instead, as it's not a logic error + * for that to be empty. + * + * Frequently Asked Questions + * ========================== + * + * Why does this need to be so complicated? + * ---------------------------------------- + * + * If a `T` instance contains `const`-qualified member variables or reference + * member variables we can't safely replace a `T` instance by destructing it + * manually and using placement new. This is because compilers are permitted to + * assume that the `const` or reference members of a named, referenced, or + * pointed-to object do not change. + * + * For pointed-to objects in allocated storage you can use the pointer returned + * by placement new or use the `launder` function to get a pointer to the new + * object. Note that `launder` doesn't affect its argument, it's still + * undefined behaviour to use the original pointer. And none of this helps if + * the object is a local or a member variable because the destructor call will + * not have been laundered. In summary, this is the only way to use placement + * new that is both simple and safe: + * + * T* pT = new T(...); + * pT->~T(); + * pT = ::new (pT) T(...); + * delete pT; + * + * What are the other safe solutions to this problem? + * -------------------------------------------------- + * + * * Ask the designer of `T` to de-`const` and -`reference` the members of `T`. + * - Makes `T` harder to reason about + * - Can reduce the performance of `T` methods + * - They can refuse to make the change + * * Put the `T` on the heap and use a raw/unique/shared pointer. + * - Adds a level of indirection, costing performance. + * - Harder to reason about your code as you need to check for nullptr. + * * Put the `T` in an `Optional`. + * - Harder to reason about your code as you need to check for None. + * * Pass the problem on, making the new code also not-replaceable + * - Contagion is not really a solution + * + * Are there downsides to this? + * ---------------------------- + * + * There is a potential performance penalty after converting `T` to + * `Replaceable<T>` if you have non-`T`-member-function code which repeatedly + * examines the value of a `const` or `reference` data member of `T`, because + * the compiler now has to look at the value each time whereas previously it + * was permitted to load it once up-front and presume that it could never + * change. + * + * Usage notes + * =========== + * + * Don't store a reference to the `T` within a `Replaceable<T>` unless you can + * show that its lifetime does not cross an `emplace` call. For safety a + * reasonable rule is to always use `operator*()` to get a fresh temporary each + * time you need a `T&. + * + * If you store a pointer to the `T` within a `Replaceable<T>` you **must** + * launder it after each call to `emplace` before using it. Again you can + * reasonably choose to always use `operator->()` to get a fresh temporary each + * time you need a `T*. + * + * Thus far I haven't thought of a good reason to use `Replaceable<T>` or + * `Replaceable<T> const&` as a function parameter type. + * + * `Replaceable<T>&` can make sense to pass to a function that conditionally + * replaces the `T`, where `T` has `const` or reference member variables. + * + * The main use of `Replaceable<T>` is as a class member type or a local type + * in long-running functions. + * + * It's probably time to rethink your design choices if you end up with + * `Replaceable<Replaceable<T>>`, `Optional<Replaceable<T>>`, + * `Replaceable<Optional<T>>`, `unique_ptr<Replaceable<T>>` etc. except as a + * result of template expansion. + */ + +namespace folly { +template <class T> +class Replaceable; + +namespace replaceable_detail { +/* Mixin templates to give `replaceable<T>` the following properties: + * + * 1. Trivial destructor if `T` has a trivial destructor; user-provided + * otherwise + * 2. Move constructor if `T` has a move constructor; deleted otherwise + * 3. Move assignment operator if `T` has a move constructor; deleted + * otherwise + * 4. Copy constructor if `T` has a copy constructor; deleted otherwise + * 5. Copy assignment operator if `T` has a copy constructor; deleted + * otherwise + * + * Has to be done in this way because we can't `enable_if` them away + */ +template < + class T, + bool = std::is_destructible<T>::value, + bool = std::is_trivially_destructible<T>::value> +struct dtor_mixin; + +/* Destructible and trivially destructible */ +template <class T> +struct dtor_mixin<T, true, true> {}; + +/* Destructible and not trivially destructible */ +template <class T> +struct dtor_mixin<T, true, false> { + dtor_mixin() = default; + dtor_mixin(dtor_mixin&&) = default; + dtor_mixin(dtor_mixin const&) = default; + dtor_mixin& operator=(dtor_mixin&&) = default; + dtor_mixin& operator=(dtor_mixin const&) = default; + ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) { + T* destruct_ptr = launder(reinterpret_cast<T*>( + reinterpret_cast<Replaceable<T>*>(this)->storage_)); + destruct_ptr->~T(); + } +}; + +/* Not destructible */ +template <class T, bool A> +struct dtor_mixin<T, false, A> { + dtor_mixin() = default; + dtor_mixin(dtor_mixin&&) = default; + dtor_mixin(dtor_mixin const&) = default; + dtor_mixin& operator=(dtor_mixin&&) = default; + dtor_mixin& operator=(dtor_mixin const&) = default; + ~dtor_mixin() = delete; +}; + +template < + class T, + bool = std::is_default_constructible<T>::value, + bool = std::is_move_constructible<T>::value> +struct default_and_move_ctor_mixin; + +/* Not default-constructible and not move-constructible */ +template <class T> +struct default_and_move_ctor_mixin<T, false, false> { + default_and_move_ctor_mixin() = delete; + default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete; + default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) = + default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) = + default; + + protected: + inline explicit default_and_move_ctor_mixin(int) {} +}; + +/* Default-constructible and move-constructible */ +template <class T> +struct default_and_move_ctor_mixin<T, true, true> { + inline default_and_move_ctor_mixin() noexcept( + std::is_nothrow_constructible<T>::value) { + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T(); + } + inline default_and_move_ctor_mixin( + default_and_move_ctor_mixin&& + other) noexcept(std::is_nothrow_constructible<T, T&&>::value) { + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) + T(*std::move(reinterpret_cast<Replaceable<T>&>(other))); + } + default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) = + default; + inline default_and_move_ctor_mixin& operator=( + default_and_move_ctor_mixin const&) = default; + + protected: + inline explicit default_and_move_ctor_mixin(int) {} +}; + +/* Default-constructible and not move-constructible */ +template <class T> +struct default_and_move_ctor_mixin<T, true, false> { + inline default_and_move_ctor_mixin() noexcept( + std::is_nothrow_constructible<T>::value) { + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T(); + } + default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete; + default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) = + default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) = + default; + + protected: + inline explicit default_and_move_ctor_mixin(int) {} +}; + +/* Not default-constructible but is move-constructible */ +template <class T> +struct default_and_move_ctor_mixin<T, false, true> { + default_and_move_ctor_mixin() = delete; + inline default_and_move_ctor_mixin( + default_and_move_ctor_mixin&& + other) noexcept(std::is_nothrow_constructible<T, T&&>::value) { + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) + T(*std::move(reinterpret_cast<Replaceable<T>&>(other))); + } + default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) = + default; + default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) = + default; + + protected: + inline explicit default_and_move_ctor_mixin(int) {} +}; + +template < + class T, + bool = (std::is_destructible<T>::value) && + (std::is_move_constructible<T>::value)> +struct move_assignment_mixin; + +/* Not (destructible and move-constructible) */ +template <class T> +struct move_assignment_mixin<T, false> { + move_assignment_mixin() = default; + move_assignment_mixin(move_assignment_mixin&&) = default; + move_assignment_mixin(move_assignment_mixin const&) = default; + move_assignment_mixin& operator=(move_assignment_mixin&&) = delete; + move_assignment_mixin& operator=(move_assignment_mixin const&) = default; +}; + +/* Both destructible and move-constructible */ +template <class T> +struct move_assignment_mixin<T, true> { + move_assignment_mixin() = default; + move_assignment_mixin(move_assignment_mixin&&) = default; + move_assignment_mixin(move_assignment_mixin const&) = default; + inline move_assignment_mixin& + operator=(move_assignment_mixin&& other) noexcept( + std::is_nothrow_destructible<T>::value&& + std::is_nothrow_move_constructible<T>::value) { + T* destruct_ptr = launder(reinterpret_cast<T*>( + reinterpret_cast<Replaceable<T>*>(this)->storage_)); + destruct_ptr->~T(); + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) + T(*std::move(reinterpret_cast<Replaceable<T>&>(other))); + return *this; + } + move_assignment_mixin& operator=(move_assignment_mixin const&) = default; +}; + +template <class T, bool = std::is_copy_constructible<T>::value> +struct copy_ctor_mixin; + +/* Not copy-constructible */ +template <class T> +struct copy_ctor_mixin<T, false> { + copy_ctor_mixin() = default; + copy_ctor_mixin(copy_ctor_mixin&&) = default; + copy_ctor_mixin(copy_ctor_mixin const&) = delete; + copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default; + copy_ctor_mixin& operator=(copy_ctor_mixin const&) = delete; +}; + +/* Copy-constructible */ +template <class T> +struct copy_ctor_mixin<T, true> { + copy_ctor_mixin() = default; + inline copy_ctor_mixin(copy_ctor_mixin const& other) noexcept( + std::is_nothrow_constructible<T, T const&>::value) { + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) + T(*reinterpret_cast<Replaceable<T> const&>(other)); + } + copy_ctor_mixin(copy_ctor_mixin&&) = default; + copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default; + copy_ctor_mixin& operator=(copy_ctor_mixin const&) = default; +}; + +template < + class T, + bool = (std::is_destructible<T>::value) && + (std::is_copy_constructible<T>::value)> +struct copy_assignment_mixin; + +/* Not (destructible and copy-constructible) */ +template <class T> +struct copy_assignment_mixin<T, false> { + copy_assignment_mixin() = default; + copy_assignment_mixin(copy_assignment_mixin&&) = default; + copy_assignment_mixin(copy_assignment_mixin const&) = default; + copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default; + copy_assignment_mixin& operator=(copy_assignment_mixin const&) = delete; +}; + +/* Both destructible and copy-constructible */ +template <class T> +struct copy_assignment_mixin<T, true> { + copy_assignment_mixin() = default; + copy_assignment_mixin(copy_assignment_mixin&&) = default; + copy_assignment_mixin(copy_assignment_mixin const&) = default; + copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default; + inline copy_assignment_mixin& + operator=(copy_assignment_mixin const& other) noexcept( + std::is_nothrow_destructible<T>::value&& + std::is_nothrow_copy_constructible<T>::value) { + T* destruct_ptr = launder(reinterpret_cast<T*>( + reinterpret_cast<Replaceable<T>*>(this)->storage_)); + destruct_ptr->~T(); + ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) + T(*reinterpret_cast<Replaceable<T> const&>(other)); + return *this; + } +}; + +template <typename T> +struct is_constructible_from_replaceable + : bool_constant< + std::is_constructible<T, Replaceable<T>&>::value || + std::is_constructible<T, Replaceable<T>&&>::value || + std::is_constructible<T, const Replaceable<T>&>::value || + std::is_constructible<T, const Replaceable<T>&&>::value> {}; + +template <typename T> +struct is_convertible_from_replaceable + : bool_constant< + std::is_convertible<Replaceable<T>&, T>::value || + std::is_convertible<Replaceable<T>&&, T>::value || + std::is_convertible<const Replaceable<T>&, T>::value || + std::is_convertible<const Replaceable<T>&&, T>::value> {}; +} // namespace replaceable_detail + +template <class T> +using is_replaceable = detail::is_instantiation_of<Replaceable, T>; + +// Function to make a Replaceable with a type deduced from its input +template <class T> +constexpr Replaceable<std::decay_t<T>> make_replaceable(T&& t) { + return Replaceable<std::decay_t<T>>(std::forward<T>(t)); +} + +template <class T, class... Args> +constexpr Replaceable<T> make_replaceable(Args&&... args) { + return Replaceable<T>(in_place, std::forward<Args>(args)...); +} + +template <class T, class U, class... Args> +constexpr Replaceable<T> make_replaceable( + std::initializer_list<U> il, + Args&&... args) { + return Replaceable<T>(in_place, il, std::forward<Args>(args)...); +} + +template <class T> +class alignas(T) Replaceable + : public replaceable_detail::dtor_mixin<T>, + public replaceable_detail::default_and_move_ctor_mixin<T>, + public replaceable_detail::copy_ctor_mixin<T>, + public replaceable_detail::move_assignment_mixin<T>, + public replaceable_detail::copy_assignment_mixin<T> { + using ctor_base = replaceable_detail::default_and_move_ctor_mixin<T>; + + public: + using value_type = T; + + /* Rule-of-zero default- copy- and move- constructors. The ugly code to make + * these work are above, in namespace folly::replaceable_detail. + */ + constexpr Replaceable() = default; + constexpr Replaceable(const Replaceable&) = default; + constexpr Replaceable(Replaceable&&) = default; + + /* Rule-of-zero copy- and move- assignment operators. The ugly code to make + * these work are above, in namespace folly::replaceable_detail. + * + * Note - these destruct the `T` and then in-place construct a new one based + * on what is in the other replaceable; they do not invoke the assignment + * operator of `T`. + */ + Replaceable& operator=(const Replaceable&) = default; + Replaceable& operator=(Replaceable&&) = default; + + /* Rule-of-zero destructor. The ugly code to make this work is above, in + * namespace folly::replaceable_detail. + */ + ~Replaceable() = default; + + /** + * Constructors; these are modeled very closely on the definition of + * `std::optional` in C++17. + */ + template < + class... Args, + std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0> + constexpr explicit Replaceable(in_place_t, Args&&... args) + // clang-format off + noexcept(std::is_nothrow_constructible<T, Args&&...>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(std::forward<Args>(args)...); + } + + template < + class U, + class... Args, + std::enable_if_t< + std::is_constructible<T, std::initializer_list<U>, Args&&...>::value, + int> = 0> + constexpr explicit Replaceable( + in_place_t, + std::initializer_list<U> il, + Args&&... args) + // clang-format off + noexcept(std::is_nothrow_constructible< + T, + std::initializer_list<U>, + Args&&...>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(il, std::forward<Args>(args)...); + } + + template < + class U = T, + std::enable_if_t< + std::is_constructible<T, U&&>::value && + !std::is_same<std::decay_t<U>, in_place_t>::value && + !std::is_same<Replaceable<T>, std::decay_t<U>>::value && + std::is_convertible<U&&, T>::value, + int> = 0> + constexpr /* implicit */ Replaceable(U&& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U&&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(std::forward<U>(other)); + } + + template < + class U = T, + std::enable_if_t< + std::is_constructible<T, U&&>::value && + !std::is_same<std::decay_t<U>, in_place_t>::value && + !std::is_same<Replaceable<T>, std::decay_t<U>>::value && + !std::is_convertible<U&&, T>::value, + int> = 0> + constexpr explicit Replaceable(U&& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U&&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(std::forward<U>(other)); + } + + template < + class U, + std::enable_if_t< + std::is_constructible<T, const U&>::value && + !replaceable_detail::is_constructible_from_replaceable< + T>::value && + !replaceable_detail::is_convertible_from_replaceable<T>::value && + std::is_convertible<const U&, T>::value, + int> = 0> + /* implicit */ Replaceable(const Replaceable<U>& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U const&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(*other); + } + + template < + class U, + std::enable_if_t< + std::is_constructible<T, const U&>::value && + !replaceable_detail::is_constructible_from_replaceable< + T>::value && + !replaceable_detail::is_convertible_from_replaceable<T>::value && + !std::is_convertible<const U&, T>::value, + int> = 0> + explicit Replaceable(const Replaceable<U>& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U const&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(*other); + } + + template < + class U, + std::enable_if_t< + std::is_constructible<T, U&&>::value && + !replaceable_detail::is_constructible_from_replaceable< + T>::value && + !replaceable_detail::is_convertible_from_replaceable<T>::value && + std::is_convertible<U&&, T>::value, + int> = 0> + /* implicit */ Replaceable(Replaceable<U>&& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U&&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(std::move(*other)); + } + + template < + class U, + std::enable_if_t< + std::is_constructible<T, U&&>::value && + !replaceable_detail::is_constructible_from_replaceable< + T>::value && + !replaceable_detail::is_convertible_from_replaceable<T>::value && + !std::is_convertible<U&&, T>::value, + int> = 0> + explicit Replaceable(Replaceable<U>&& other) + // clang-format off + noexcept(std::is_nothrow_constructible<T, U&&>::value) + // clang-format on + : ctor_base(0) { + ::new (storage_) T(std::move(*other)); + } + + /** + * `emplace` destructs the contained object and in-place constructs the + * replacement. + * + * The destructor must not throw (as usual). The constructor must not throw + * because that would violate the invariant that a `Replaceable<T>` always + * contains a T instance. + * + * As these methods are `noexcept` the program will be terminated if an + * exception is thrown. If you are encountering this issue you should look at + * using `Optional` instead. + */ + template <class... Args> + T& emplace(Args&&... args) noexcept { + T* destruct_ptr = launder(reinterpret_cast<T*>(storage_)); + destruct_ptr->~T(); + return *::new (storage_) T(std::forward<Args>(args)...); + } + + template <class U, class... Args> + T& emplace(std::initializer_list<U> il, Args&&... args) noexcept { + T* destruct_ptr = launder(reinterpret_cast<T*>(storage_)); + destruct_ptr->~T(); + return *::new (storage_) T(il, std::forward<Args>(args)...); + } + + /** + * `swap` just calls `swap(T&, T&)`. + */ + void swap(Replaceable& other) noexcept(IsNothrowSwappable<Replaceable>{}) { + using std::swap; + swap(*(*this), *other); + } + + /** + * Methods to access the contained object. Intended to be very unsurprising. + */ + constexpr const T* operator->() const { + return launder(reinterpret_cast<T const*>(storage_)); + } + + constexpr T* operator->() { + return launder(reinterpret_cast<T*>(storage_)); + } + + constexpr const T& operator*() const& { + return *launder(reinterpret_cast<T const*>(storage_)); + } + + constexpr T& operator*() & { + return *launder(reinterpret_cast<T*>(storage_)); + } + + constexpr T&& operator*() && { + return std::move(*launder(reinterpret_cast<T*>(storage_))); + } + + constexpr const T&& operator*() const&& { + return std::move(*launder(reinterpret_cast<T const*>(storage_))); + } + + private: + friend struct replaceable_detail::dtor_mixin<T>; + friend struct replaceable_detail::default_and_move_ctor_mixin<T>; + friend struct replaceable_detail::copy_ctor_mixin<T>; + friend struct replaceable_detail::move_assignment_mixin<T>; + friend struct replaceable_detail::copy_assignment_mixin<T>; + aligned_storage_for_t<T> storage_[1]; +}; + +#if __cpp_deduction_guides >= 201703 +template <class T> +Replaceable(T)->Replaceable<T>; +#endif +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ScopeGuard.cpp b/ios/Pods/Flipper-Folly/folly/ScopeGuard.cpp new file mode 100644 index 000000000..5678ea8d2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ScopeGuard.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ScopeGuard.h> + +#include <iostream> + +/*static*/ void folly::detail::ScopeGuardImplBase::warnAboutToCrash() noexcept { + // Ensure the availability of std::cerr + std::ios_base::Init ioInit; + std::cerr + << "This program will now terminate because a folly::ScopeGuard callback " + "threw an \nexception.\n"; +} diff --git a/ios/Pods/Flipper-Folly/folly/ScopeGuard.h b/ios/Pods/Flipper-Folly/folly/ScopeGuard.h new file mode 100644 index 000000000..26da53d1e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ScopeGuard.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <cstdlib> +#include <functional> +#include <new> +#include <type_traits> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Preprocessor.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> +#include <folly/lang/UncaughtExceptions.h> + +namespace folly { + +namespace detail { + +class ScopeGuardImplBase { + public: + void dismiss() noexcept { + dismissed_ = true; + } + + protected: + ScopeGuardImplBase() noexcept : dismissed_(false) {} + + static void warnAboutToCrash() noexcept; + static ScopeGuardImplBase makeEmptyScopeGuard() noexcept { + return ScopeGuardImplBase{}; + } + + template <typename T> + static const T& asConst(const T& t) noexcept { + return t; + } + + bool dismissed_; +}; + +template <typename FunctionType, bool InvokeNoexcept> +class ScopeGuardImpl : public ScopeGuardImplBase { + public: + explicit ScopeGuardImpl(FunctionType& fn) noexcept( + std::is_nothrow_copy_constructible<FunctionType>::value) + : ScopeGuardImpl( + asConst(fn), + makeFailsafe( + std::is_nothrow_copy_constructible<FunctionType>{}, + &fn)) {} + + explicit ScopeGuardImpl(const FunctionType& fn) noexcept( + std::is_nothrow_copy_constructible<FunctionType>::value) + : ScopeGuardImpl( + fn, + makeFailsafe( + std::is_nothrow_copy_constructible<FunctionType>{}, + &fn)) {} + + explicit ScopeGuardImpl(FunctionType&& fn) noexcept( + std::is_nothrow_move_constructible<FunctionType>::value) + : ScopeGuardImpl( + std::move_if_noexcept(fn), + makeFailsafe( + std::is_nothrow_move_constructible<FunctionType>{}, + &fn)) {} + + ScopeGuardImpl(ScopeGuardImpl&& other) noexcept( + std::is_nothrow_move_constructible<FunctionType>::value) + : function_(std::move_if_noexcept(other.function_)) { + // If the above line attempts a copy and the copy throws, other is + // left owning the cleanup action and will execute it (or not) depending + // on the value of other.dismissed_. The following lines only execute + // if the move/copy succeeded, in which case *this assumes ownership of + // the cleanup action and dismisses other. + dismissed_ = std::exchange(other.dismissed_, true); + } + + ~ScopeGuardImpl() noexcept(InvokeNoexcept) { + if (!dismissed_) { + execute(); + } + } + + private: + static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept { + return makeEmptyScopeGuard(); + } + + template <typename Fn> + static auto makeFailsafe(std::false_type, Fn* fn) noexcept + -> ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept> { + return ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept>{ + std::ref(*fn)}; + } + + template <typename Fn> + explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe) + : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) { + failsafe.dismiss(); + } + + void* operator new(std::size_t) = delete; + + void execute() noexcept(InvokeNoexcept) { + if (InvokeNoexcept) { + using R = decltype(function_()); + auto catcher = []() -> R { warnAboutToCrash(), std::terminate(); }; + catch_exception(function_, catcher); + } else { + function_(); + } + } + + FunctionType function_; +}; + +template <typename F, bool INE> +using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type, INE>; + +} // namespace detail + +/** + * ScopeGuard is a general implementation of the "Initialization is + * Resource Acquisition" idiom. Basically, it guarantees that a function + * is executed upon leaving the current scope unless otherwise told. + * + * The makeGuard() function is used to create a new ScopeGuard object. + * It can be instantiated with a lambda function, a std::function<void()>, + * a functor, or a void(*)() function pointer. + * + * + * Usage example: Add a friend to memory if and only if it is also added + * to the db. + * + * void User::addFriend(User& newFriend) { + * // add the friend to memory + * friends_.push_back(&newFriend); + * + * // If the db insertion that follows fails, we should + * // remove it from memory. + * auto guard = makeGuard([&] { friends_.pop_back(); }); + * + * // this will throw an exception upon error, which + * // makes the ScopeGuard execute UserCont::pop_back() + * // once the Guard's destructor is called. + * db_->addFriend(GetName(), newFriend.GetName()); + * + * // an exception was not thrown, so don't execute + * // the Guard. + * guard.dismiss(); + * } + * + * Examine ScopeGuardTest.cpp for some more sample usage. + * + * Stolen from: + * Andrei's and Petru Marginean's CUJ article: + * http://drdobbs.com/184403758 + * and the loki library: + * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer + * and triendl.kj article: + * http://www.codeproject.com/KB/cpp/scope_guard.aspx + */ +template <typename F> +FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept( + noexcept(detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)))) { + return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)); +} + +namespace detail { + +#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \ + defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \ + defined(FOLLY_EXCEPTION_COUNT_USE_STD) + +/** + * ScopeGuard used for executing a function when leaving the current scope + * depending on the presence of a new uncaught exception. + * + * If the executeOnException template parameter is true, the function is + * executed if a new uncaught exception is present at the end of the scope. + * If the parameter is false, then the function is executed if no new uncaught + * exceptions are present at the end of the scope. + * + * Used to implement SCOPE_FAIL and SCOPE_SUCCESS below. + */ +template <typename FunctionType, bool ExecuteOnException> +class ScopeGuardForNewException { + public: + explicit ScopeGuardForNewException(const FunctionType& fn) : guard_(fn) {} + + explicit ScopeGuardForNewException(FunctionType&& fn) + : guard_(std::move(fn)) {} + + ScopeGuardForNewException(ScopeGuardForNewException&& other) = default; + + ~ScopeGuardForNewException() noexcept(ExecuteOnException) { + if (ExecuteOnException != (exceptionCounter_ < uncaught_exceptions())) { + guard_.dismiss(); + } + } + + private: + void* operator new(std::size_t) = delete; + void operator delete(void*) = delete; + + ScopeGuardImpl<FunctionType, ExecuteOnException> guard_; + int exceptionCounter_{uncaught_exceptions()}; +}; + +/** + * Internal use for the macro SCOPE_FAIL below + */ +enum class ScopeGuardOnFail {}; + +template <typename FunctionType> +ScopeGuardForNewException<typename std::decay<FunctionType>::type, true> +operator+(detail::ScopeGuardOnFail, FunctionType&& fn) { + return ScopeGuardForNewException< + typename std::decay<FunctionType>::type, + true>(std::forward<FunctionType>(fn)); +} + +/** + * Internal use for the macro SCOPE_SUCCESS below + */ +enum class ScopeGuardOnSuccess {}; + +template <typename FunctionType> +ScopeGuardForNewException<typename std::decay<FunctionType>::type, false> +operator+(ScopeGuardOnSuccess, FunctionType&& fn) { + return ScopeGuardForNewException< + typename std::decay<FunctionType>::type, + false>(std::forward<FunctionType>(fn)); +} + +#endif // native uncaught_exception() supported + +/** + * Internal use for the macro SCOPE_EXIT below + */ +enum class ScopeGuardOnExit {}; + +template <typename FunctionType> +ScopeGuardImpl<typename std::decay<FunctionType>::type, true> operator+( + detail::ScopeGuardOnExit, + FunctionType&& fn) { + return ScopeGuardImpl<typename std::decay<FunctionType>::type, true>( + std::forward<FunctionType>(fn)); +} +} // namespace detail + +} // namespace folly + +#define SCOPE_EXIT \ + auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) = \ + ::folly::detail::ScopeGuardOnExit() + [&]() noexcept + +#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \ + defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \ + defined(FOLLY_EXCEPTION_COUNT_USE_STD) +#define SCOPE_FAIL \ + auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) = \ + ::folly::detail::ScopeGuardOnFail() + [&]() noexcept + +#define SCOPE_SUCCESS \ + auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) = \ + ::folly::detail::ScopeGuardOnSuccess() + [&]() +#endif // native uncaught_exception() supported diff --git a/ios/Pods/Flipper-Folly/folly/SharedMutex.cpp b/ios/Pods/Flipper-Folly/folly/SharedMutex.cpp new file mode 100644 index 000000000..ff50b5ec6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SharedMutex.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/SharedMutex.h> + +namespace folly { +// Explicitly instantiate SharedMutex here: +template class SharedMutexImpl<true>; +template class SharedMutexImpl<false>; + +namespace detail { +std::unique_lock<std::mutex> sharedMutexAnnotationGuard(void* ptr) { + if (folly::kIsSanitizeThread) { + // On TSAN builds, we have an array of mutexes and index into them based on + // the address. If the array is of prime size things will work out okay + // without a complicated hash function. + static constexpr std::size_t kNumAnnotationMutexes = 251; + static std::array<std::mutex, kNumAnnotationMutexes> kAnnotationMutexes{}; + auto index = reinterpret_cast<uintptr_t>(ptr) % kNumAnnotationMutexes; + return std::unique_lock<std::mutex>(kAnnotationMutexes[index]); + } else { + return std::unique_lock<std::mutex>(); + } +} +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/SharedMutex.h b/ios/Pods/Flipper-Folly/folly/SharedMutex.h new file mode 100644 index 000000000..7aa6179e0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SharedMutex.h @@ -0,0 +1,1760 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author Nathan Bronson (ngbronson@fb.com) + +#pragma once + +#include <stdint.h> + +#include <atomic> +#include <thread> +#include <type_traits> + +#include <folly/CPortability.h> +#include <folly/Likely.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/detail/Futex.h> +#include <folly/portability/Asm.h> +#include <folly/portability/SysResource.h> +#include <folly/synchronization/AtomicRef.h> +#include <folly/synchronization/SanitizeThread.h> + +// SharedMutex is a reader-writer lock. It is small, very fast, scalable +// on multi-core, and suitable for use when readers or writers may block. +// Unlike most other reader-writer locks, its throughput with concurrent +// readers scales linearly; it is able to acquire and release the lock +// in shared mode without cache line ping-ponging. It is suitable for +// a wide range of lock hold times because it starts with spinning, +// proceeds to using sched_yield with a preemption heuristic, and then +// waits using futex and precise wakeups. +// +// SharedMutex provides all of the methods of folly::RWSpinLock, +// boost::shared_mutex, boost::upgrade_mutex, and C++14's +// std::shared_timed_mutex. All operations that can block are available +// in try, try-for, and try-until (system_clock or steady_clock) versions. +// +// SharedMutexReadPriority gives priority to readers, +// SharedMutexWritePriority gives priority to writers. SharedMutex is an +// alias for SharedMutexWritePriority, because writer starvation is more +// likely than reader starvation for the read-heavy workloads targeted +// by SharedMutex. +// +// In my tests SharedMutex is as good or better than the other +// reader-writer locks in use at Facebook for almost all use cases, +// sometimes by a wide margin. (If it is rare that there are actually +// concurrent readers then RWSpinLock can be a few nanoseconds faster.) +// I compared it to folly::RWSpinLock, folly::RWTicketSpinLock64, +// boost::shared_mutex, pthread_rwlock_t, and a RWLock that internally uses +// spinlocks to guard state and pthread_mutex_t+pthread_cond_t to block. +// (Thrift's ReadWriteMutex is based underneath on pthread_rwlock_t.) +// It is generally as good or better than the rest when evaluating size, +// speed, scalability, or latency outliers. In the corner cases where +// it is not the fastest (such as single-threaded use or heavy write +// contention) it is never very much worse than the best. See the bottom +// of folly/test/SharedMutexTest.cpp for lots of microbenchmark results. +// +// Comparison to folly::RWSpinLock: +// +// * SharedMutex is faster than RWSpinLock when there are actually +// concurrent read accesses (sometimes much faster), and ~5 nanoseconds +// slower when there is not actually any contention. SharedMutex is +// faster in every (benchmarked) scenario where the shared mode of +// the lock is actually useful. +// +// * Concurrent shared access to SharedMutex scales linearly, while total +// RWSpinLock throughput drops as more threads try to access the lock +// in shared mode. Under very heavy read contention SharedMutex can +// be two orders of magnitude faster than RWSpinLock (or any reader +// writer lock that doesn't use striping or deferral). +// +// * SharedMutex can safely protect blocking calls, because after an +// initial period of spinning it waits using futex(). +// +// * RWSpinLock prioritizes readers, SharedMutex has both reader- and +// writer-priority variants, but defaults to write priority. +// +// * RWSpinLock's upgradeable mode blocks new readers, while SharedMutex's +// doesn't. Both semantics are reasonable. The boost documentation +// doesn't explicitly talk about this behavior (except by omitting +// any statement that those lock modes conflict), but the boost +// implementations do allow new readers while the upgradeable mode +// is held. See https://github.com/boostorg/thread/blob/master/ +// include/boost/thread/pthread/shared_mutex.hpp +// +// * RWSpinLock::UpgradedHolder maps to SharedMutex::UpgradeHolder +// (UpgradeableHolder would be even more pedantically correct). +// SharedMutex's holders have fewer methods (no reset) and are less +// tolerant (promotion and downgrade crash if the donor doesn't own +// the lock, and you must use the default constructor rather than +// passing a nullptr to the pointer constructor). +// +// Both SharedMutex and RWSpinLock provide "exclusive", "upgrade", +// and "shared" modes. At all times num_threads_holding_exclusive + +// num_threads_holding_upgrade <= 1, and num_threads_holding_exclusive == +// 0 || num_threads_holding_shared == 0. RWSpinLock has the additional +// constraint that num_threads_holding_shared cannot increase while +// num_threads_holding_upgrade is non-zero. +// +// Comparison to the internal RWLock: +// +// * SharedMutex doesn't allow a maximum reader count to be configured, +// so it can't be used as a semaphore in the same way as RWLock. +// +// * SharedMutex is 4 bytes, RWLock is 256. +// +// * SharedMutex is as fast or faster than RWLock in all of my +// microbenchmarks, and has positive rather than negative scalability. +// +// * RWLock and SharedMutex are both writer priority locks. +// +// * SharedMutex avoids latency outliers as well as RWLock. +// +// * SharedMutex uses different names (t != 0 below): +// +// RWLock::lock(0) => SharedMutex::lock() +// +// RWLock::lock(t) => SharedMutex::try_lock_for(milliseconds(t)) +// +// RWLock::tryLock() => SharedMutex::try_lock() +// +// RWLock::unlock() => SharedMutex::unlock() +// +// RWLock::enter(0) => SharedMutex::lock_shared() +// +// RWLock::enter(t) => +// SharedMutex::try_lock_shared_for(milliseconds(t)) +// +// RWLock::tryEnter() => SharedMutex::try_lock_shared() +// +// RWLock::leave() => SharedMutex::unlock_shared() +// +// * RWLock allows the reader count to be adjusted by a value other +// than 1 during enter() or leave(). SharedMutex doesn't currently +// implement this feature. +// +// * RWLock's methods are marked const, SharedMutex's aren't. +// +// Reader-writer locks have the potential to allow concurrent access +// to shared read-mostly data, but in practice they often provide no +// improvement over a mutex. The problem is the cache coherence protocol +// of modern CPUs. Coherence is provided by making sure that when a cache +// line is written it is present in only one core's cache. Since a memory +// write is required to acquire a reader-writer lock in shared mode, the +// cache line holding the lock is invalidated in all of the other caches. +// This leads to cache misses when another thread wants to acquire or +// release the lock concurrently. When the RWLock is colocated with the +// data it protects (common), cache misses can also continue occur when +// a thread that already holds the lock tries to read the protected data. +// +// Ideally, a reader-writer lock would allow multiple cores to acquire +// and release the lock in shared mode without incurring any cache misses. +// This requires that each core records its shared access in a cache line +// that isn't read or written by other read-locking cores. (Writers will +// have to check all of the cache lines.) Typical server hardware when +// this comment was written has 16 L1 caches and cache lines of 64 bytes, +// so a lock striped over all L1 caches would occupy a prohibitive 1024 +// bytes. Nothing says that we need a separate set of per-core memory +// locations for each lock, however. Each SharedMutex instance is only +// 4 bytes, but all locks together share a 2K area in which they make a +// core-local record of lock acquisitions. +// +// SharedMutex's strategy of using a shared set of core-local stripes has +// a potential downside, because it means that acquisition of any lock in +// write mode can conflict with acquisition of any lock in shared mode. +// If a lock instance doesn't actually experience concurrency then this +// downside will outweight the upside of improved scalability for readers. +// To avoid this problem we dynamically detect concurrent accesses to +// SharedMutex, and don't start using the deferred mode unless we actually +// observe concurrency. See kNumSharedToStartDeferring. +// +// It is explicitly allowed to call unlock_shared() from a different +// thread than lock_shared(), so long as they are properly paired. +// unlock_shared() needs to find the location at which lock_shared() +// recorded the lock, which might be in the lock itself or in any of +// the shared slots. If you can conveniently pass state from lock +// acquisition to release then the fastest mechanism is to std::move +// the SharedMutex::ReadHolder instance or an SharedMutex::Token (using +// lock_shared(Token&) and unlock_shared(Token&)). The guard or token +// will tell unlock_shared where in deferredReaders[] to look for the +// deferred lock. The Token-less version of unlock_shared() works in all +// cases, but is optimized for the common (no inter-thread handoff) case. +// +// In both read- and write-priority mode, a waiting lock() (exclusive mode) +// only blocks readers after it has waited for an active upgrade lock to be +// released; until the upgrade lock is released (or upgraded or downgraded) +// readers will still be able to enter. Preferences about lock acquisition +// are not guaranteed to be enforced perfectly (even if they were, there +// is theoretically the chance that a thread could be arbitrarily suspended +// between calling lock() and SharedMutex code actually getting executed). +// +// try_*_for methods always try at least once, even if the duration +// is zero or negative. The duration type must be compatible with +// std::chrono::steady_clock. try_*_until methods also always try at +// least once. std::chrono::system_clock and std::chrono::steady_clock +// are supported. +// +// If you have observed by profiling that your SharedMutex-s are getting +// cache misses on deferredReaders[] due to another SharedMutex user, then +// you can use the tag type to create your own instantiation of the type. +// The contention threshold (see kNumSharedToStartDeferring) should make +// this unnecessary in all but the most extreme cases. Make sure to check +// that the increased icache and dcache footprint of the tagged result is +// worth it. + +// SharedMutex's use of thread local storage is an optimization, so +// for the case where thread local storage is not supported, define it +// away. + +// Note about TSAN (ThreadSanitizer): the SharedMutexWritePriority version +// (the default) of this mutex is annotated appropriately so that TSAN can +// perform lock inversion analysis. However, the SharedMutexReadPriority version +// is not annotated. This is because TSAN's lock order heuristic +// assumes that two calls to lock_shared must be ordered, which leads +// to too many false positives for the reader-priority case. +// +// Suppose thread A holds a SharedMutexWritePriority lock in shared mode and an +// independent thread B is waiting for exclusive access. Then a thread C's +// lock_shared can't proceed until A has released the lock. Discounting +// situations that never use exclusive mode (so no lock is necessary at all) +// this means that without higher-level reasoning it is not safe to ignore +// reader <-> reader interactions. +// +// This reasoning does not apply to SharedMutexReadPriority, because there are +// no actions by a thread B that can make C need to wait for A. Since the +// overwhelming majority of SharedMutex instances use write priority, we +// restrict the TSAN annotations to only SharedMutexWritePriority. + +#ifndef FOLLY_SHAREDMUTEX_TLS +#if !FOLLY_MOBILE +#define FOLLY_SHAREDMUTEX_TLS FOLLY_TLS +#else +#define FOLLY_SHAREDMUTEX_TLS +#endif +#endif + +namespace folly { + +struct SharedMutexToken { + enum class Type : uint16_t { + INVALID = 0, + INLINE_SHARED, + DEFERRED_SHARED, + }; + + Type type_; + uint16_t slot_; +}; + +namespace detail { +// Returns a guard that gives permission for the current thread to +// annotate, and adjust the annotation bits in, the SharedMutex at ptr. +std::unique_lock<std::mutex> sharedMutexAnnotationGuard(void* ptr); +} // namespace detail + +template < + bool ReaderPriority, + typename Tag_ = void, + template <typename> class Atom = std::atomic, + bool BlockImmediately = false, + bool AnnotateForThreadSanitizer = kIsSanitizeThread && !ReaderPriority> +class SharedMutexImpl { + public: + static constexpr bool kReaderPriority = ReaderPriority; + + typedef Tag_ Tag; + + typedef SharedMutexToken Token; + + class FOLLY_NODISCARD ReadHolder; + class FOLLY_NODISCARD UpgradeHolder; + class FOLLY_NODISCARD WriteHolder; + + constexpr SharedMutexImpl() noexcept : state_(0) {} + + SharedMutexImpl(const SharedMutexImpl&) = delete; + SharedMutexImpl(SharedMutexImpl&&) = delete; + SharedMutexImpl& operator=(const SharedMutexImpl&) = delete; + SharedMutexImpl& operator=(SharedMutexImpl&&) = delete; + + // It is an error to destroy an SharedMutex that still has + // any outstanding locks. This is checked if NDEBUG isn't defined. + // SharedMutex's exclusive mode can be safely used to guard the lock's + // own destruction. If, for example, you acquire the lock in exclusive + // mode and then observe that the object containing the lock is no longer + // needed, you can unlock() and then immediately destroy the lock. + // See https://sourceware.org/bugzilla/show_bug.cgi?id=13690 for a + // description about why this property needs to be explicitly mentioned. + ~SharedMutexImpl() { + auto state = state_.load(std::memory_order_relaxed); + if (UNLIKELY((state & kHasS) != 0)) { + cleanupTokenlessSharedDeferred(state); + } + + if (folly::kIsDebug) { + // These asserts check that everybody has released the lock before it + // is destroyed. If you arrive here while debugging that is likely + // the problem. (You could also have general heap corruption.) + + // if a futexWait fails to go to sleep because the value has been + // changed, we don't necessarily clean up the wait bits, so it is + // possible they will be set here in a correct system + assert((state & ~(kWaitingAny | kMayDefer | kAnnotationCreated)) == 0); + if ((state & kMayDefer) != 0) { + for (uint32_t slot = 0; slot < kMaxDeferredReaders; ++slot) { + auto slotValue = + deferredReader(slot)->load(std::memory_order_relaxed); + assert(!slotValueIsThis(slotValue)); + (void)slotValue; + } + } + } + annotateDestroy(); + } + + // Checks if an exclusive lock could succeed so that lock elision could be + // enabled. Different from the two eligible_for_lock_{upgrade|shared}_elision + // functions, this is a conservative check since kMayDefer indicates + // "may-existing" deferred readers. + bool eligible_for_lock_elision() const { + // We rely on the transaction for linearization. Wait bits are + // irrelevant because a successful transaction will be in and out + // without affecting the wakeup. kBegunE is also okay for a similar + // reason. + auto state = state_.load(std::memory_order_relaxed); + return (state & (kHasS | kMayDefer | kHasE | kHasU)) == 0; + } + + // Checks if an upgrade lock could succeed so that lock elision could be + // enabled. + bool eligible_for_lock_upgrade_elision() const { + auto state = state_.load(std::memory_order_relaxed); + return (state & (kHasE | kHasU)) == 0; + } + + // Checks if a shared lock could succeed so that lock elision could be + // enabled. + bool eligible_for_lock_shared_elision() const { + // No need to honor kBegunE because a transaction doesn't block anybody + auto state = state_.load(std::memory_order_relaxed); + return (state & kHasE) == 0; + } + + void lock() { + WaitForever ctx; + (void)lockExclusiveImpl(kHasSolo, ctx); + annotateAcquired(annotate_rwlock_level::wrlock); + } + + bool try_lock() { + WaitNever ctx; + auto result = lockExclusiveImpl(kHasSolo, ctx); + annotateTryAcquired(result, annotate_rwlock_level::wrlock); + return result; + } + + template <class Rep, class Period> + bool try_lock_for(const std::chrono::duration<Rep, Period>& duration) { + WaitForDuration<Rep, Period> ctx(duration); + auto result = lockExclusiveImpl(kHasSolo, ctx); + annotateTryAcquired(result, annotate_rwlock_level::wrlock); + return result; + } + + template <class Clock, class Duration> + bool try_lock_until( + const std::chrono::time_point<Clock, Duration>& absDeadline) { + WaitUntilDeadline<Clock, Duration> ctx{absDeadline}; + auto result = lockExclusiveImpl(kHasSolo, ctx); + annotateTryAcquired(result, annotate_rwlock_level::wrlock); + return result; + } + + void unlock() { + annotateReleased(annotate_rwlock_level::wrlock); + // It is possible that we have a left-over kWaitingNotS if the last + // unlock_shared() that let our matching lock() complete finished + // releasing before lock()'s futexWait went to sleep. Clean it up now + auto state = (state_ &= ~(kWaitingNotS | kPrevDefer | kHasE)); + assert((state & ~(kWaitingAny | kAnnotationCreated)) == 0); + wakeRegisteredWaiters(state, kWaitingE | kWaitingU | kWaitingS); + } + + // Managing the token yourself makes unlock_shared a bit faster + + void lock_shared() { + WaitForever ctx; + (void)lockSharedImpl(nullptr, ctx); + annotateAcquired(annotate_rwlock_level::rdlock); + } + + void lock_shared(Token& token) { + WaitForever ctx; + (void)lockSharedImpl(&token, ctx); + annotateAcquired(annotate_rwlock_level::rdlock); + } + + bool try_lock_shared() { + WaitNever ctx; + auto result = lockSharedImpl(nullptr, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + bool try_lock_shared(Token& token) { + WaitNever ctx; + auto result = lockSharedImpl(&token, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Rep, class Period> + bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& duration) { + WaitForDuration<Rep, Period> ctx(duration); + auto result = lockSharedImpl(nullptr, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Rep, class Period> + bool try_lock_shared_for( + const std::chrono::duration<Rep, Period>& duration, + Token& token) { + WaitForDuration<Rep, Period> ctx(duration); + auto result = lockSharedImpl(&token, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Clock, class Duration> + bool try_lock_shared_until( + const std::chrono::time_point<Clock, Duration>& absDeadline) { + WaitUntilDeadline<Clock, Duration> ctx{absDeadline}; + auto result = lockSharedImpl(nullptr, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Clock, class Duration> + bool try_lock_shared_until( + const std::chrono::time_point<Clock, Duration>& absDeadline, + Token& token) { + WaitUntilDeadline<Clock, Duration> ctx{absDeadline}; + auto result = lockSharedImpl(&token, ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + void unlock_shared() { + annotateReleased(annotate_rwlock_level::rdlock); + + auto state = state_.load(std::memory_order_acquire); + + // kPrevDefer can only be set if HasE or BegunE is set + assert((state & (kPrevDefer | kHasE | kBegunE)) != kPrevDefer); + + // lock() strips kMayDefer immediately, but then copies it to + // kPrevDefer so we can tell if the pre-lock() lock_shared() might + // have deferred + if ((state & (kMayDefer | kPrevDefer)) == 0 || + !tryUnlockTokenlessSharedDeferred()) { + // Matching lock_shared() couldn't have deferred, or the deferred + // lock has already been inlined by applyDeferredReaders() + unlockSharedInline(); + } + } + + void unlock_shared(Token& token) { + annotateReleased(annotate_rwlock_level::rdlock); + + assert( + token.type_ == Token::Type::INLINE_SHARED || + token.type_ == Token::Type::DEFERRED_SHARED); + + if (token.type_ != Token::Type::DEFERRED_SHARED || + !tryUnlockSharedDeferred(token.slot_)) { + unlockSharedInline(); + } + if (folly::kIsDebug) { + token.type_ = Token::Type::INVALID; + } + } + + void unlock_and_lock_shared() { + annotateReleased(annotate_rwlock_level::wrlock); + annotateAcquired(annotate_rwlock_level::rdlock); + // We can't use state_ -=, because we need to clear 2 bits (1 of which + // has an uncertain initial state) and set 1 other. We might as well + // clear the relevant wake bits at the same time. Note that since S + // doesn't block the beginning of a transition to E (writer priority + // can cut off new S, reader priority grabs BegunE and blocks deferred + // S) we need to wake E as well. + auto state = state_.load(std::memory_order_acquire); + do { + assert( + (state & ~(kWaitingAny | kPrevDefer | kAnnotationCreated)) == kHasE); + } while (!state_.compare_exchange_strong( + state, (state & ~(kWaitingAny | kPrevDefer | kHasE)) + kIncrHasS)); + if ((state & (kWaitingE | kWaitingU | kWaitingS)) != 0) { + futexWakeAll(kWaitingE | kWaitingU | kWaitingS); + } + } + + void unlock_and_lock_shared(Token& token) { + unlock_and_lock_shared(); + token.type_ = Token::Type::INLINE_SHARED; + } + + void lock_upgrade() { + WaitForever ctx; + (void)lockUpgradeImpl(ctx); + // For TSAN: treat upgrade locks as equivalent to read locks + annotateAcquired(annotate_rwlock_level::rdlock); + } + + bool try_lock_upgrade() { + WaitNever ctx; + auto result = lockUpgradeImpl(ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Rep, class Period> + bool try_lock_upgrade_for( + const std::chrono::duration<Rep, Period>& duration) { + WaitForDuration<Rep, Period> ctx(duration); + auto result = lockUpgradeImpl(ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + template <class Clock, class Duration> + bool try_lock_upgrade_until( + const std::chrono::time_point<Clock, Duration>& absDeadline) { + WaitUntilDeadline<Clock, Duration> ctx{absDeadline}; + auto result = lockUpgradeImpl(ctx); + annotateTryAcquired(result, annotate_rwlock_level::rdlock); + return result; + } + + void unlock_upgrade() { + annotateReleased(annotate_rwlock_level::rdlock); + auto state = (state_ -= kHasU); + assert((state & (kWaitingNotS | kHasSolo)) == 0); + wakeRegisteredWaiters(state, kWaitingE | kWaitingU); + } + + void unlock_upgrade_and_lock() { + // no waiting necessary, so waitMask is empty + WaitForever ctx; + (void)lockExclusiveImpl(0, ctx); + annotateReleased(annotate_rwlock_level::rdlock); + annotateAcquired(annotate_rwlock_level::wrlock); + } + + void unlock_upgrade_and_lock_shared() { + // No need to annotate for TSAN here because we model upgrade and shared + // locks as the same. + auto state = (state_ -= kHasU - kIncrHasS); + assert((state & (kWaitingNotS | kHasSolo)) == 0); + wakeRegisteredWaiters(state, kWaitingE | kWaitingU); + } + + void unlock_upgrade_and_lock_shared(Token& token) { + unlock_upgrade_and_lock_shared(); + token.type_ = Token::Type::INLINE_SHARED; + } + + void unlock_and_lock_upgrade() { + annotateReleased(annotate_rwlock_level::wrlock); + annotateAcquired(annotate_rwlock_level::rdlock); + // We can't use state_ -=, because we need to clear 2 bits (1 of + // which has an uncertain initial state) and set 1 other. We might + // as well clear the relevant wake bits at the same time. + auto state = state_.load(std::memory_order_acquire); + while (true) { + assert( + (state & ~(kWaitingAny | kPrevDefer | kAnnotationCreated)) == kHasE); + auto after = + (state & ~(kWaitingNotS | kWaitingS | kPrevDefer | kHasE)) + kHasU; + if (state_.compare_exchange_strong(state, after)) { + if ((state & kWaitingS) != 0) { + futexWakeAll(kWaitingS); + } + return; + } + } + } + + private: + typedef typename folly::detail::Futex<Atom> Futex; + + // Internally we use four kinds of wait contexts. These are structs + // that provide a doWait method that returns true if a futex wake + // was issued that intersects with the waitMask, false if there was a + // timeout and no more waiting should be performed. Spinning occurs + // before the wait context is invoked. + + struct WaitForever { + bool canBlock() { + return true; + } + bool canTimeOut() { + return false; + } + bool shouldTimeOut() { + return false; + } + + bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) { + detail::futexWait(&futex, expected, waitMask); + return true; + } + }; + + struct WaitNever { + bool canBlock() { + return false; + } + bool canTimeOut() { + return true; + } + bool shouldTimeOut() { + return true; + } + + bool doWait( + Futex& /* futex */, + uint32_t /* expected */, + uint32_t /* waitMask */) { + return false; + } + }; + + template <class Rep, class Period> + struct WaitForDuration { + std::chrono::duration<Rep, Period> duration_; + bool deadlineComputed_; + std::chrono::steady_clock::time_point deadline_; + + explicit WaitForDuration(const std::chrono::duration<Rep, Period>& duration) + : duration_(duration), deadlineComputed_(false) {} + + std::chrono::steady_clock::time_point deadline() { + if (!deadlineComputed_) { + deadline_ = std::chrono::steady_clock::now() + duration_; + deadlineComputed_ = true; + } + return deadline_; + } + + bool canBlock() { + return duration_.count() > 0; + } + bool canTimeOut() { + return true; + } + + bool shouldTimeOut() { + return std::chrono::steady_clock::now() > deadline(); + } + + bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) { + auto result = + detail::futexWaitUntil(&futex, expected, deadline(), waitMask); + return result != folly::detail::FutexResult::TIMEDOUT; + } + }; + + template <class Clock, class Duration> + struct WaitUntilDeadline { + std::chrono::time_point<Clock, Duration> absDeadline_; + + bool canBlock() { + return true; + } + bool canTimeOut() { + return true; + } + bool shouldTimeOut() { + return Clock::now() > absDeadline_; + } + + bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) { + auto result = + detail::futexWaitUntil(&futex, expected, absDeadline_, waitMask); + return result != folly::detail::FutexResult::TIMEDOUT; + } + }; + + void annotateLazyCreate() { + if (AnnotateForThreadSanitizer && + (state_.load() & kAnnotationCreated) == 0) { + auto guard = detail::sharedMutexAnnotationGuard(this); + // check again + if ((state_.load() & kAnnotationCreated) == 0) { + state_.fetch_or(kAnnotationCreated); + annotate_benign_race_sized( + &state_, sizeof(state_), "init TSAN", __FILE__, __LINE__); + annotate_rwlock_create(this, __FILE__, __LINE__); + } + } + } + + void annotateDestroy() { + if (AnnotateForThreadSanitizer) { + annotateLazyCreate(); + annotate_rwlock_destroy(this, __FILE__, __LINE__); + } + } + + void annotateAcquired(annotate_rwlock_level w) { + if (AnnotateForThreadSanitizer) { + annotateLazyCreate(); + annotate_rwlock_acquired(this, w, __FILE__, __LINE__); + } + } + + void annotateTryAcquired(bool result, annotate_rwlock_level w) { + if (AnnotateForThreadSanitizer) { + annotateLazyCreate(); + annotate_rwlock_try_acquired(this, w, result, __FILE__, __LINE__); + } + } + + void annotateReleased(annotate_rwlock_level w) { + if (AnnotateForThreadSanitizer) { + assert((state_.load() & kAnnotationCreated) != 0); + annotate_rwlock_released(this, w, __FILE__, __LINE__); + } + } + + // 32 bits of state + Futex state_{}; + + // S count needs to be on the end, because we explicitly allow it to + // underflow. This can occur while we are in the middle of applying + // deferred locks (we remove them from deferredReaders[] before + // inlining them), or during token-less unlock_shared() if a racing + // lock_shared();unlock_shared() moves the deferredReaders slot while + // the first unlock_shared() is scanning. The former case is cleaned + // up before we finish applying the locks. The latter case can persist + // until destruction, when it is cleaned up. + static constexpr uint32_t kIncrHasS = 1 << 11; + static constexpr uint32_t kHasS = ~(kIncrHasS - 1); + + // Set if annotation has been completed for this instance. That annotation + // (and setting this bit afterward) must be guarded by one of the mutexes in + // annotationCreationGuards. + static constexpr uint32_t kAnnotationCreated = 1 << 10; + + // If false, then there are definitely no deferred read locks for this + // instance. Cleared after initialization and when exclusively locked. + static constexpr uint32_t kMayDefer = 1 << 9; + + // lock() cleared kMayDefer as soon as it starts draining readers (so + // that it doesn't have to do a second CAS once drain completes), but + // unlock_shared() still needs to know whether to scan deferredReaders[] + // or not. We copy kMayDefer to kPrevDefer when setting kHasE or + // kBegunE, and clear it when clearing those bits. + static constexpr uint32_t kPrevDefer = 1 << 8; + + // Exclusive-locked blocks all read locks and write locks. This bit + // may be set before all readers have finished, but in that case the + // thread that sets it won't return to the caller until all read locks + // have been released. + static constexpr uint32_t kHasE = 1 << 7; + + // Exclusive-draining means that lock() is waiting for existing readers + // to leave, but that new readers may still acquire shared access. + // This is only used in reader priority mode. New readers during + // drain must be inline. The difference between this and kHasU is that + // kBegunE prevents kMayDefer from being set. + static constexpr uint32_t kBegunE = 1 << 6; + + // At most one thread may have either exclusive or upgrade lock + // ownership. Unlike exclusive mode, ownership of the lock in upgrade + // mode doesn't preclude other threads holding the lock in shared mode. + // boost's concept for this doesn't explicitly say whether new shared + // locks can be acquired one lock_upgrade has succeeded, but doesn't + // list that as disallowed. RWSpinLock disallows new read locks after + // lock_upgrade has been acquired, but the boost implementation doesn't. + // We choose the latter. + static constexpr uint32_t kHasU = 1 << 5; + + // There are three states that we consider to be "solo", in that they + // cannot coexist with other solo states. These are kHasE, kBegunE, + // and kHasU. Note that S doesn't conflict with any of these, because + // setting the kHasE is only one of the two steps needed to actually + // acquire the lock in exclusive mode (the other is draining the existing + // S holders). + static constexpr uint32_t kHasSolo = kHasE | kBegunE | kHasU; + + // Once a thread sets kHasE it needs to wait for the current readers + // to exit the lock. We give this a separate wait identity from the + // waiting to set kHasE so that we can perform partial wakeups (wake + // one instead of wake all). + static constexpr uint32_t kWaitingNotS = 1 << 4; + + // When waking writers we can either wake them all, in which case we + // can clear kWaitingE, or we can call futexWake(1). futexWake tells + // us if anybody woke up, but even if we detect that nobody woke up we + // can't clear the bit after the fact without issuing another wakeup. + // To avoid thundering herds when there are lots of pending lock() + // without needing to call futexWake twice when there is only one + // waiter, kWaitingE actually encodes if we have observed multiple + // concurrent waiters. Tricky: ABA issues on futexWait mean that when + // we see kWaitingESingle we can't assume that there is only one. + static constexpr uint32_t kWaitingESingle = 1 << 2; + static constexpr uint32_t kWaitingEMultiple = 1 << 3; + static constexpr uint32_t kWaitingE = kWaitingESingle | kWaitingEMultiple; + + // kWaitingU is essentially a 1 bit saturating counter. It always + // requires a wakeAll. + static constexpr uint32_t kWaitingU = 1 << 1; + + // All blocked lock_shared() should be awoken, so it is correct (not + // suboptimal) to wakeAll if there are any shared readers. + static constexpr uint32_t kWaitingS = 1 << 0; + + // kWaitingAny is a mask of all of the bits that record the state of + // threads, rather than the state of the lock. It is convenient to be + // able to mask them off during asserts. + static constexpr uint32_t kWaitingAny = + kWaitingNotS | kWaitingE | kWaitingU | kWaitingS; + + // The reader count at which a reader will attempt to use the lock + // in deferred mode. If this value is 2, then the second concurrent + // reader will set kMayDefer and use deferredReaders[]. kMayDefer is + // cleared during exclusive access, so this threshold must be reached + // each time a lock is held in exclusive mode. + static constexpr uint32_t kNumSharedToStartDeferring = 2; + + // The typical number of spins that a thread will wait for a state + // transition. There is no bound on the number of threads that can wait + // for a writer, so we are pretty conservative here to limit the chance + // that we are starving the writer of CPU. Each spin is 6 or 7 nanos, + // almost all of which is in the pause instruction. + static constexpr uint32_t kMaxSpinCount = !BlockImmediately ? 1000 : 2; + + // The maximum number of soft yields before falling back to futex. + // If the preemption heuristic is activated we will fall back before + // this. A soft yield takes ~900 nanos (two sched_yield plus a call + // to getrusage, with checks of the goal at each step). Soft yields + // aren't compatible with deterministic execution under test (unlike + // futexWaitUntil, which has a capricious but deterministic back end). + static constexpr uint32_t kMaxSoftYieldCount = !BlockImmediately ? 1000 : 0; + + // If AccessSpreader assigns indexes from 0..k*n-1 on a system where some + // level of the memory hierarchy is symmetrically divided into k pieces + // (NUMA nodes, last-level caches, L1 caches, ...), then slot indexes + // that are the same after integer division by k share that resource. + // Our strategy for deferred readers is to probe up to numSlots/4 slots, + // using the full granularity of AccessSpreader for the start slot + // and then search outward. We can use AccessSpreader::current(n) + // without managing our own spreader if kMaxDeferredReaders <= + // AccessSpreader::kMaxCpus, which is currently 128. + // + // Our 2-socket E5-2660 machines have 8 L1 caches on each chip, + // with 64 byte cache lines. That means we need 64*16 bytes of + // deferredReaders[] to give each L1 its own playground. On x86_64 + // each DeferredReaderSlot is 8 bytes, so we need kMaxDeferredReaders + // * kDeferredSeparationFactor >= 64 * 16 / 8 == 128. If + // kDeferredSearchDistance * kDeferredSeparationFactor <= + // 64 / 8 then we will search only within a single cache line, which + // guarantees we won't have inter-L1 contention. We give ourselves + // a factor of 2 on the core count, which should hold us for a couple + // processor generations. deferredReaders[] is 2048 bytes currently. + public: + static constexpr uint32_t kMaxDeferredReaders = 64; + static constexpr uint32_t kDeferredSearchDistance = 2; + static constexpr uint32_t kDeferredSeparationFactor = 4; + + private: + static_assert( + !(kMaxDeferredReaders & (kMaxDeferredReaders - 1)), + "kMaxDeferredReaders must be a power of 2"); + static_assert( + !(kDeferredSearchDistance & (kDeferredSearchDistance - 1)), + "kDeferredSearchDistance must be a power of 2"); + + // The number of deferred locks that can be simultaneously acquired + // by a thread via the token-less methods without performing any heap + // allocations. Each of these costs 3 pointers (24 bytes, probably) + // per thread. There's not much point in making this larger than + // kDeferredSearchDistance. + static constexpr uint32_t kTokenStackTLSCapacity = 2; + + // We need to make sure that if there is a lock_shared() + // and lock_shared(token) followed by unlock_shared() and + // unlock_shared(token), the token-less unlock doesn't null + // out deferredReaders[token.slot_]. If we allowed that, then + // unlock_shared(token) wouldn't be able to assume that its lock + // had been inlined by applyDeferredReaders when it finds that + // deferredReaders[token.slot_] no longer points to this. We accomplish + // this by stealing bit 0 from the pointer to record that the slot's + // element has no token, hence our use of uintptr_t in deferredReaders[]. + static constexpr uintptr_t kTokenless = 0x1; + + // This is the starting location for Token-less unlock_shared(). + static FOLLY_SHAREDMUTEX_TLS uint32_t tls_lastTokenlessSlot; + + // Last deferred reader slot used. + static FOLLY_SHAREDMUTEX_TLS uint32_t tls_lastDeferredReaderSlot; + + // Only indexes divisible by kDeferredSeparationFactor are used. + // If any of those elements points to a SharedMutexImpl, then it + // should be considered that there is a shared lock on that instance. + // See kTokenless. + public: + typedef Atom<uintptr_t> DeferredReaderSlot; + + private: + alignas(hardware_destructive_interference_size) static DeferredReaderSlot + deferredReaders[kMaxDeferredReaders * kDeferredSeparationFactor]; + + // Performs an exclusive lock, waiting for state_ & waitMask to be + // zero first + template <class WaitContext> + bool lockExclusiveImpl(uint32_t preconditionGoalMask, WaitContext& ctx) { + uint32_t state = state_.load(std::memory_order_acquire); + if (LIKELY( + (state & (preconditionGoalMask | kMayDefer | kHasS)) == 0 && + state_.compare_exchange_strong(state, (state | kHasE) & ~kHasU))) { + return true; + } else { + return lockExclusiveImpl(state, preconditionGoalMask, ctx); + } + } + + template <class WaitContext> + bool lockExclusiveImpl( + uint32_t& state, + uint32_t preconditionGoalMask, + WaitContext& ctx) { + while (true) { + if (UNLIKELY((state & preconditionGoalMask) != 0) && + !waitForZeroBits(state, preconditionGoalMask, kWaitingE, ctx) && + ctx.canTimeOut()) { + return false; + } + + uint32_t after = (state & kMayDefer) == 0 ? 0 : kPrevDefer; + if (!kReaderPriority || (state & (kMayDefer | kHasS)) == 0) { + // Block readers immediately, either because we are in write + // priority mode or because we can acquire the lock in one + // step. Note that if state has kHasU, then we are doing an + // unlock_upgrade_and_lock() and we should clear it (reader + // priority branch also does this). + after |= (state | kHasE) & ~(kHasU | kMayDefer); + } else { + after |= (state | kBegunE) & ~(kHasU | kMayDefer); + } + if (state_.compare_exchange_strong(state, after)) { + auto before = state; + state = after; + + // If we set kHasE (writer priority) then no new readers can + // arrive. If we set kBegunE then they can still enter, but + // they must be inline. Either way we need to either spin on + // deferredReaders[] slots, or inline them so that we can wait on + // kHasS to zero itself. deferredReaders[] is pointers, which on + // x86_64 are bigger than futex() can handle, so we inline the + // deferred locks instead of trying to futexWait on each slot. + // Readers are responsible for rechecking state_ after recording + // a deferred read to avoid atomicity problems between the state_ + // CAS and applyDeferredReader's reads of deferredReaders[]. + if (UNLIKELY((before & kMayDefer) != 0)) { + applyDeferredReaders(state, ctx); + } + while (true) { + assert((state & (kHasE | kBegunE)) != 0 && (state & kHasU) == 0); + if (UNLIKELY((state & kHasS) != 0) && + !waitForZeroBits(state, kHasS, kWaitingNotS, ctx) && + ctx.canTimeOut()) { + // Ugh. We blocked new readers and other writers for a while, + // but were unable to complete. Move on. On the plus side + // we can clear kWaitingNotS because nobody else can piggyback + // on it. + state = (state_ &= ~(kPrevDefer | kHasE | kBegunE | kWaitingNotS)); + wakeRegisteredWaiters(state, kWaitingE | kWaitingU | kWaitingS); + return false; + } + + if (kReaderPriority && (state & kHasE) == 0) { + assert((state & kBegunE) != 0); + if (!state_.compare_exchange_strong( + state, (state & ~kBegunE) | kHasE)) { + continue; + } + } + + return true; + } + } + } + } + + template <class WaitContext> + bool waitForZeroBits( + uint32_t& state, + uint32_t goal, + uint32_t waitMask, + WaitContext& ctx) { + uint32_t spinCount = 0; + while (true) { + state = state_.load(std::memory_order_acquire); + if ((state & goal) == 0) { + return true; + } + asm_volatile_pause(); + ++spinCount; + if (UNLIKELY(spinCount >= kMaxSpinCount)) { + return ctx.canBlock() && + yieldWaitForZeroBits(state, goal, waitMask, ctx); + } + } + } + + template <class WaitContext> + bool yieldWaitForZeroBits( + uint32_t& state, + uint32_t goal, + uint32_t waitMask, + WaitContext& ctx) { +#ifdef RUSAGE_THREAD + struct rusage usage; + std::memset(&usage, 0, sizeof(usage)); + long before = -1; +#endif + for (uint32_t yieldCount = 0; yieldCount < kMaxSoftYieldCount; + ++yieldCount) { + for (int softState = 0; softState < 3; ++softState) { + if (softState < 2) { + std::this_thread::yield(); + } else { +#ifdef RUSAGE_THREAD + getrusage(RUSAGE_THREAD, &usage); +#endif + } + if (((state = state_.load(std::memory_order_acquire)) & goal) == 0) { + return true; + } + if (ctx.shouldTimeOut()) { + return false; + } + } +#ifdef RUSAGE_THREAD + if (before >= 0 && usage.ru_nivcsw >= before + 2) { + // One involuntary csw might just be occasional background work, + // but if we get two in a row then we guess that there is someone + // else who can profitably use this CPU. Fall back to futex + break; + } + before = usage.ru_nivcsw; +#endif + } + return futexWaitForZeroBits(state, goal, waitMask, ctx); + } + + template <class WaitContext> + bool futexWaitForZeroBits( + uint32_t& state, + uint32_t goal, + uint32_t waitMask, + WaitContext& ctx) { + assert( + waitMask == kWaitingNotS || waitMask == kWaitingE || + waitMask == kWaitingU || waitMask == kWaitingS); + + while (true) { + state = state_.load(std::memory_order_acquire); + if ((state & goal) == 0) { + return true; + } + + auto after = state; + if (waitMask == kWaitingE) { + if ((state & kWaitingESingle) != 0) { + after |= kWaitingEMultiple; + } else { + after |= kWaitingESingle; + } + } else { + after |= waitMask; + } + + // CAS is better than atomic |= here, because it lets us avoid + // setting the wait flag when the goal is concurrently achieved + if (after != state && !state_.compare_exchange_strong(state, after)) { + continue; + } + + if (!ctx.doWait(state_, after, waitMask)) { + // timed out + return false; + } + } + } + + // Wakes up waiters registered in state_ as appropriate, clearing the + // awaiting bits for anybody that was awoken. Tries to perform direct + // single wakeup of an exclusive waiter if appropriate + void wakeRegisteredWaiters(uint32_t& state, uint32_t wakeMask) { + if (UNLIKELY((state & wakeMask) != 0)) { + wakeRegisteredWaitersImpl(state, wakeMask); + } + } + + void wakeRegisteredWaitersImpl(uint32_t& state, uint32_t wakeMask) { + // If there are multiple lock() pending only one of them will actually + // get to wake up, so issuing futexWakeAll will make a thundering herd. + // There's nothing stopping us from issuing futexWake(1) instead, + // so long as the wait bits are still an accurate reflection of + // the waiters. If we notice (via futexWake's return value) that + // nobody woke up then we can try again with the normal wake-all path. + // Note that we can't just clear the bits at that point; we need to + // clear the bits and then issue another wakeup. + // + // It is possible that we wake an E waiter but an outside S grabs the + // lock instead, at which point we should wake pending U and S waiters. + // Rather than tracking state to make the failing E regenerate the + // wakeup, we just disable the optimization in the case that there + // are waiting U or S that we are eligible to wake. + if ((wakeMask & kWaitingE) == kWaitingE && + (state & wakeMask) == kWaitingE && + detail::futexWake(&state_, 1, kWaitingE) > 0) { + // somebody woke up, so leave state_ as is and clear it later + return; + } + + if ((state & wakeMask) != 0) { + auto prev = state_.fetch_and(~wakeMask); + if ((prev & wakeMask) != 0) { + futexWakeAll(wakeMask); + } + state = prev & ~wakeMask; + } + } + + void futexWakeAll(uint32_t wakeMask) { + detail::futexWake(&state_, std::numeric_limits<int>::max(), wakeMask); + } + + DeferredReaderSlot* deferredReader(uint32_t slot) { + return &deferredReaders[slot * kDeferredSeparationFactor]; + } + + uintptr_t tokenfulSlotValue() { + return reinterpret_cast<uintptr_t>(this); + } + + uintptr_t tokenlessSlotValue() { + return tokenfulSlotValue() | kTokenless; + } + + bool slotValueIsThis(uintptr_t slotValue) { + return (slotValue & ~kTokenless) == tokenfulSlotValue(); + } + + // Clears any deferredReaders[] that point to this, adjusting the inline + // shared lock count to compensate. Does some spinning and yielding + // to avoid the work. Always finishes the application, even if ctx + // times out. + template <class WaitContext> + void applyDeferredReaders(uint32_t& state, WaitContext& ctx) { + uint32_t slot = 0; + + uint32_t spinCount = 0; + while (true) { + while (!slotValueIsThis( + deferredReader(slot)->load(std::memory_order_acquire))) { + if (++slot == kMaxDeferredReaders) { + return; + } + } + asm_volatile_pause(); + if (UNLIKELY(++spinCount >= kMaxSpinCount)) { + applyDeferredReaders(state, ctx, slot); + return; + } + } + } + + template <class WaitContext> + void applyDeferredReaders(uint32_t& state, WaitContext& ctx, uint32_t slot) { +#ifdef RUSAGE_THREAD + struct rusage usage; + std::memset(&usage, 0, sizeof(usage)); + long before = -1; +#endif + for (uint32_t yieldCount = 0; yieldCount < kMaxSoftYieldCount; + ++yieldCount) { + for (int softState = 0; softState < 3; ++softState) { + if (softState < 2) { + std::this_thread::yield(); + } else { +#ifdef RUSAGE_THREAD + getrusage(RUSAGE_THREAD, &usage); +#endif + } + while (!slotValueIsThis( + deferredReader(slot)->load(std::memory_order_acquire))) { + if (++slot == kMaxDeferredReaders) { + return; + } + } + if (ctx.shouldTimeOut()) { + // finish applying immediately on timeout + break; + } + } +#ifdef RUSAGE_THREAD + if (before >= 0 && usage.ru_nivcsw >= before + 2) { + // heuristic says run queue is not empty + break; + } + before = usage.ru_nivcsw; +#endif + } + + uint32_t movedSlotCount = 0; + for (; slot < kMaxDeferredReaders; ++slot) { + auto slotPtr = deferredReader(slot); + auto slotValue = slotPtr->load(std::memory_order_acquire); + if (slotValueIsThis(slotValue) && + slotPtr->compare_exchange_strong(slotValue, 0)) { + ++movedSlotCount; + } + } + + if (movedSlotCount > 0) { + state = (state_ += movedSlotCount * kIncrHasS); + } + assert((state & (kHasE | kBegunE)) != 0); + + // if state + kIncrHasS overflows (off the end of state) then either + // we have 2^(32-9) readers (almost certainly an application bug) + // or we had an underflow (also a bug) + assert(state < state + kIncrHasS); + } + + // It is straightfoward to make a token-less lock_shared() and + // unlock_shared() either by making the token-less version always use + // INLINE_SHARED mode or by removing the token version. Supporting + // deferred operation for both types is trickier than it appears, because + // the purpose of the token it so that unlock_shared doesn't have to + // look in other slots for its deferred lock. Token-less unlock_shared + // might place a deferred lock in one place and then release a different + // slot that was originally used by the token-ful version. If this was + // important we could solve the problem by differentiating the deferred + // locks so that cross-variety release wouldn't occur. The best way + // is probably to steal a bit from the pointer, making deferredLocks[] + // an array of Atom<uintptr_t>. + + template <class WaitContext> + bool lockSharedImpl(Token* token, WaitContext& ctx) { + uint32_t state = state_.load(std::memory_order_relaxed); + if ((state & (kHasS | kMayDefer | kHasE)) == 0 && + state_.compare_exchange_strong(state, state + kIncrHasS)) { + if (token != nullptr) { + token->type_ = Token::Type::INLINE_SHARED; + } + return true; + } + return lockSharedImpl(state, token, ctx); + } + + template <class WaitContext> + bool lockSharedImpl(uint32_t& state, Token* token, WaitContext& ctx); + + // Updates the state in/out argument as if the locks were made inline, + // but does not update state_ + void cleanupTokenlessSharedDeferred(uint32_t& state) { + for (uint32_t i = 0; i < kMaxDeferredReaders; ++i) { + auto slotPtr = deferredReader(i); + auto slotValue = slotPtr->load(std::memory_order_relaxed); + if (slotValue == tokenlessSlotValue()) { + slotPtr->store(0, std::memory_order_relaxed); + state += kIncrHasS; + if ((state & kHasS) == 0) { + break; + } + } + } + } + + bool tryUnlockTokenlessSharedDeferred(); + + bool tryUnlockSharedDeferred(uint32_t slot) { + assert(slot < kMaxDeferredReaders); + auto slotValue = tokenfulSlotValue(); + return deferredReader(slot)->compare_exchange_strong(slotValue, 0); + } + + uint32_t unlockSharedInline() { + uint32_t state = (state_ -= kIncrHasS); + assert( + (state & (kHasE | kBegunE | kMayDefer)) != 0 || + state < state + kIncrHasS); + if ((state & kHasS) == 0) { + // Only the second half of lock() can be blocked by a non-zero + // reader count, so that's the only thing we need to wake + wakeRegisteredWaiters(state, kWaitingNotS); + } + return state; + } + + template <class WaitContext> + bool lockUpgradeImpl(WaitContext& ctx) { + uint32_t state; + do { + if (!waitForZeroBits(state, kHasSolo, kWaitingU, ctx)) { + return false; + } + } while (!state_.compare_exchange_strong(state, state | kHasU)); + return true; + } + + public: + class FOLLY_NODISCARD ReadHolder { + ReadHolder() : lock_(nullptr) {} + + public: + explicit ReadHolder(const SharedMutexImpl* lock) + : lock_(const_cast<SharedMutexImpl*>(lock)) { + if (lock_) { + lock_->lock_shared(token_); + } + } + + explicit ReadHolder(const SharedMutexImpl& lock) + : lock_(const_cast<SharedMutexImpl*>(&lock)) { + lock_->lock_shared(token_); + } + + ReadHolder(ReadHolder&& rhs) noexcept + : lock_(rhs.lock_), token_(rhs.token_) { + rhs.lock_ = nullptr; + } + + // Downgrade from upgrade mode + explicit ReadHolder(UpgradeHolder&& upgraded) : lock_(upgraded.lock_) { + assert(upgraded.lock_ != nullptr); + upgraded.lock_ = nullptr; + lock_->unlock_upgrade_and_lock_shared(token_); + } + + // Downgrade from exclusive mode + explicit ReadHolder(WriteHolder&& writer) : lock_(writer.lock_) { + assert(writer.lock_ != nullptr); + writer.lock_ = nullptr; + lock_->unlock_and_lock_shared(token_); + } + + ReadHolder& operator=(ReadHolder&& rhs) noexcept { + std::swap(lock_, rhs.lock_); + std::swap(token_, rhs.token_); + return *this; + } + + ReadHolder(const ReadHolder& rhs) = delete; + ReadHolder& operator=(const ReadHolder& rhs) = delete; + + ~ReadHolder() { + unlock(); + } + + void unlock() { + if (lock_) { + lock_->unlock_shared(token_); + lock_ = nullptr; + } + } + + private: + friend class UpgradeHolder; + friend class WriteHolder; + SharedMutexImpl* lock_; + SharedMutexToken token_; + }; + + class FOLLY_NODISCARD UpgradeHolder { + UpgradeHolder() : lock_(nullptr) {} + + public: + explicit UpgradeHolder(SharedMutexImpl* lock) : lock_(lock) { + if (lock_) { + lock_->lock_upgrade(); + } + } + + explicit UpgradeHolder(SharedMutexImpl& lock) : lock_(&lock) { + lock_->lock_upgrade(); + } + + // Downgrade from exclusive mode + explicit UpgradeHolder(WriteHolder&& writer) : lock_(writer.lock_) { + assert(writer.lock_ != nullptr); + writer.lock_ = nullptr; + lock_->unlock_and_lock_upgrade(); + } + + UpgradeHolder(UpgradeHolder&& rhs) noexcept : lock_(rhs.lock_) { + rhs.lock_ = nullptr; + } + + UpgradeHolder& operator=(UpgradeHolder&& rhs) noexcept { + std::swap(lock_, rhs.lock_); + return *this; + } + + UpgradeHolder(const UpgradeHolder& rhs) = delete; + UpgradeHolder& operator=(const UpgradeHolder& rhs) = delete; + + ~UpgradeHolder() { + unlock(); + } + + void unlock() { + if (lock_) { + lock_->unlock_upgrade(); + lock_ = nullptr; + } + } + + private: + friend class WriteHolder; + friend class ReadHolder; + SharedMutexImpl* lock_; + }; + + class FOLLY_NODISCARD WriteHolder { + WriteHolder() : lock_(nullptr) {} + + public: + explicit WriteHolder(SharedMutexImpl* lock) : lock_(lock) { + if (lock_) { + lock_->lock(); + } + } + + explicit WriteHolder(SharedMutexImpl& lock) : lock_(&lock) { + lock_->lock(); + } + + // Promotion from upgrade mode + explicit WriteHolder(UpgradeHolder&& upgrade) : lock_(upgrade.lock_) { + assert(upgrade.lock_ != nullptr); + upgrade.lock_ = nullptr; + lock_->unlock_upgrade_and_lock(); + } + + // README: + // + // It is intended that WriteHolder(ReadHolder&& rhs) do not exist. + // + // Shared locks (read) can not safely upgrade to unique locks (write). + // That upgrade path is a well-known recipe for deadlock, so we explicitly + // disallow it. + // + // If you need to do a conditional mutation, you have a few options: + // 1. Check the condition under a shared lock and release it. + // Then maybe check the condition again under a unique lock and maybe do + // the mutation. + // 2. Check the condition once under an upgradeable lock. + // Then maybe upgrade the lock to a unique lock and do the mutation. + // 3. Check the condition and maybe perform the mutation under a unique + // lock. + // + // Relevant upgradeable lock notes: + // * At most one upgradeable lock can be held at a time for a given shared + // mutex, just like a unique lock. + // * An upgradeable lock may be held concurrently with any number of shared + // locks. + // * An upgradeable lock may be upgraded atomically to a unique lock. + + WriteHolder(WriteHolder&& rhs) noexcept : lock_(rhs.lock_) { + rhs.lock_ = nullptr; + } + + WriteHolder& operator=(WriteHolder&& rhs) noexcept { + std::swap(lock_, rhs.lock_); + return *this; + } + + WriteHolder(const WriteHolder& rhs) = delete; + WriteHolder& operator=(const WriteHolder& rhs) = delete; + + ~WriteHolder() { + unlock(); + } + + void unlock() { + if (lock_) { + lock_->unlock(); + lock_ = nullptr; + } + } + + private: + friend class ReadHolder; + friend class UpgradeHolder; + SharedMutexImpl* lock_; + }; + + // Adapters for Synchronized<> + friend void acquireRead(SharedMutexImpl& lock) { + lock.lock_shared(); + } + friend void acquireReadWrite(SharedMutexImpl& lock) { + lock.lock(); + } + friend void releaseRead(SharedMutexImpl& lock) { + lock.unlock_shared(); + } + friend void releaseReadWrite(SharedMutexImpl& lock) { + lock.unlock(); + } + friend bool acquireRead(SharedMutexImpl& lock, unsigned int ms) { + return lock.try_lock_shared_for(std::chrono::milliseconds(ms)); + } + friend bool acquireReadWrite(SharedMutexImpl& lock, unsigned int ms) { + return lock.try_lock_for(std::chrono::milliseconds(ms)); + } +}; + +typedef SharedMutexImpl<true> SharedMutexReadPriority; +typedef SharedMutexImpl<false> SharedMutexWritePriority; +typedef SharedMutexWritePriority SharedMutex; +typedef SharedMutexImpl<false, void, std::atomic, false, false> + SharedMutexSuppressTSAN; + +// Prevent the compiler from instantiating these in other translation units. +// They are instantiated once in SharedMutex.cpp +extern template class SharedMutexImpl<true>; +extern template class SharedMutexImpl<false>; + +template < + bool ReaderPriority, + typename Tag_, + template <typename> class Atom, + bool BlockImmediately, + bool AnnotateForThreadSanitizer> +alignas(hardware_destructive_interference_size) typename SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>::DeferredReaderSlot + SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>::deferredReaders + [kMaxDeferredReaders * kDeferredSeparationFactor] = {}; + +template < + bool ReaderPriority, + typename Tag_, + template <typename> class Atom, + bool BlockImmediately, + bool AnnotateForThreadSanitizer> +FOLLY_SHAREDMUTEX_TLS uint32_t SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>::tls_lastTokenlessSlot = 0; + +template < + bool ReaderPriority, + typename Tag_, + template <typename> class Atom, + bool BlockImmediately, + bool AnnotateForThreadSanitizer> +FOLLY_SHAREDMUTEX_TLS uint32_t SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>::tls_lastDeferredReaderSlot = 0; + +template < + bool ReaderPriority, + typename Tag_, + template <typename> class Atom, + bool BlockImmediately, + bool AnnotateForThreadSanitizer> +bool SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>::tryUnlockTokenlessSharedDeferred() { + auto bestSlot = + make_atomic_ref(tls_lastTokenlessSlot).load(std::memory_order_relaxed); + for (uint32_t i = 0; i < kMaxDeferredReaders; ++i) { + auto slotPtr = deferredReader(bestSlot ^ i); + auto slotValue = slotPtr->load(std::memory_order_relaxed); + if (slotValue == tokenlessSlotValue() && + slotPtr->compare_exchange_strong(slotValue, 0)) { + make_atomic_ref(tls_lastTokenlessSlot) + .store(bestSlot ^ i, std::memory_order_relaxed); + return true; + } + } + return false; +} + +template < + bool ReaderPriority, + typename Tag_, + template <typename> class Atom, + bool BlockImmediately, + bool AnnotateForThreadSanitizer> +template <class WaitContext> +bool SharedMutexImpl< + ReaderPriority, + Tag_, + Atom, + BlockImmediately, + AnnotateForThreadSanitizer>:: + lockSharedImpl(uint32_t& state, Token* token, WaitContext& ctx) { + while (true) { + if (UNLIKELY((state & kHasE) != 0) && + !waitForZeroBits(state, kHasE, kWaitingS, ctx) && ctx.canTimeOut()) { + return false; + } + + uint32_t slot = make_atomic_ref(tls_lastDeferredReaderSlot) + .load(std::memory_order_relaxed); + uintptr_t slotValue = 1; // any non-zero value will do + + bool canAlreadyDefer = (state & kMayDefer) != 0; + bool aboveDeferThreshold = + (state & kHasS) >= (kNumSharedToStartDeferring - 1) * kIncrHasS; + bool drainInProgress = ReaderPriority && (state & kBegunE) != 0; + if (canAlreadyDefer || (aboveDeferThreshold && !drainInProgress)) { + /* Try using the most recent slot first. */ + slotValue = deferredReader(slot)->load(std::memory_order_relaxed); + if (slotValue != 0) { + // starting point for our empty-slot search, can change after + // calling waitForZeroBits + uint32_t bestSlot = + (uint32_t)folly::AccessSpreader<Atom>::current(kMaxDeferredReaders); + + // deferred readers are already enabled, or it is time to + // enable them if we can find a slot + for (uint32_t i = 0; i < kDeferredSearchDistance; ++i) { + slot = bestSlot ^ i; + assert(slot < kMaxDeferredReaders); + slotValue = deferredReader(slot)->load(std::memory_order_relaxed); + if (slotValue == 0) { + // found empty slot + make_atomic_ref(tls_lastDeferredReaderSlot) + .store(slot, std::memory_order_relaxed); + break; + } + } + } + } + + if (slotValue != 0) { + // not yet deferred, or no empty slots + if (state_.compare_exchange_strong(state, state + kIncrHasS)) { + // successfully recorded the read lock inline + if (token != nullptr) { + token->type_ = Token::Type::INLINE_SHARED; + } + return true; + } + // state is updated, try again + continue; + } + + // record that deferred readers might be in use if necessary + if ((state & kMayDefer) == 0) { + if (!state_.compare_exchange_strong(state, state | kMayDefer)) { + // keep going if CAS failed because somebody else set the bit + // for us + if ((state & (kHasE | kMayDefer)) != kMayDefer) { + continue; + } + } + // state = state | kMayDefer; + } + + // try to use the slot + bool gotSlot = deferredReader(slot)->compare_exchange_strong( + slotValue, + token == nullptr ? tokenlessSlotValue() : tokenfulSlotValue()); + + // If we got the slot, we need to verify that an exclusive lock + // didn't happen since we last checked. If we didn't get the slot we + // need to recheck state_ anyway to make sure we don't waste too much + // work. It is also possible that since we checked state_ someone + // has acquired and released the write lock, clearing kMayDefer. + // Both cases are covered by looking for the readers-possible bit, + // because it is off when the exclusive lock bit is set. + state = state_.load(std::memory_order_acquire); + + if (!gotSlot) { + continue; + } + + if (token == nullptr) { + make_atomic_ref(tls_lastTokenlessSlot) + .store(slot, std::memory_order_relaxed); + } + + if ((state & kMayDefer) != 0) { + assert((state & kHasE) == 0); + // success + if (token != nullptr) { + token->type_ = Token::Type::DEFERRED_SHARED; + token->slot_ = (uint16_t)slot; + } + return true; + } + + // release the slot before retrying + if (token == nullptr) { + // We can't rely on slot. Token-less slot values can be freed by + // any unlock_shared(), so we need to do the full deferredReader + // search during unlock. Unlike unlock_shared(), we can't trust + // kPrevDefer here. This deferred lock isn't visible to lock() + // (that's the whole reason we're undoing it) so there might have + // subsequently been an unlock() and lock() with no intervening + // transition to deferred mode. + if (!tryUnlockTokenlessSharedDeferred()) { + unlockSharedInline(); + } + } else { + if (!tryUnlockSharedDeferred(slot)) { + unlockSharedInline(); + } + } + + // We got here not because the lock was unavailable, but because + // we lost a compare-and-swap. Try-lock is typically allowed to + // have spurious failures, but there is no lock efficiency gain + // from exploiting that freedom here. + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Singleton-inl.h b/ios/Pods/Flipper-Folly/folly/Singleton-inl.h new file mode 100644 index 000000000..c54bc7229 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Singleton-inl.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace folly { + +namespace detail { + +template <typename T> +template <typename Tag, typename VaultTag> +struct SingletonHolder<T>::Impl : SingletonHolder<T> { + Impl() + : SingletonHolder<T>( + {typeid(T), typeid(Tag)}, + *SingletonVault::singleton<VaultTag>()) {} +}; + +template <typename T> +template <typename Tag, typename VaultTag> +inline SingletonHolder<T>& SingletonHolder<T>::singleton() { + return detail::createGlobal<Impl<Tag, VaultTag>, void>(); +} + +[[noreturn]] void singletonWarnDoubleRegistrationAndAbort( + const TypeDescriptor& type); + +template <typename T> +void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) { + std::lock_guard<std::mutex> entry_lock(mutex_); + + if (state_ != SingletonHolderState::NotRegistered) { + /* Possible causes: + * + * You have two instances of the same + * folly::Singleton<Class>. Probably because you define the + * singleton in a header included in multiple places? In general, + * folly::Singleton shouldn't be in the header, only off in some + * anonymous namespace in a cpp file. Code needing the singleton + * will find it when that code references folly::Singleton<Class>. + * + * Alternatively, you could have 2 singletons with the same type + * defined with a different name in a .cpp (source) file. For + * example: + * + * Singleton<int> a([] { return new int(3); }); + * Singleton<int> b([] { return new int(4); }); + * + * Adding tags should fix this (see documentation in the header). + * + */ + singletonWarnDoubleRegistrationAndAbort(type()); + } + + create_ = std::move(c); + teardown_ = std::move(t); + + state_ = SingletonHolderState::Dead; +} + +template <typename T> +void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) { + if (state_ == SingletonHolderState::NotRegistered) { + detail::singletonWarnRegisterMockEarlyAndAbort(type()); + } + if (state_ == SingletonHolderState::Living) { + destroyInstance(); + } + + { + auto creationOrder = vault_.creationOrder_.wlock(); + + auto it = std::find(creationOrder->begin(), creationOrder->end(), type()); + if (it != creationOrder->end()) { + creationOrder->erase(it); + } + } + + std::lock_guard<std::mutex> entry_lock(mutex_); + + create_ = std::move(c); + teardown_ = std::move(t); +} + +template <typename T> +T* SingletonHolder<T>::get() { + if (LIKELY( + state_.load(std::memory_order_acquire) == + SingletonHolderState::Living)) { + return instance_ptr_; + } + createInstance(); + + if (instance_weak_.expired()) { + detail::singletonThrowGetInvokedAfterDestruction(type()); + } + + return instance_ptr_; +} + +template <typename T> +std::weak_ptr<T> SingletonHolder<T>::get_weak() { + if (UNLIKELY( + state_.load(std::memory_order_acquire) != + SingletonHolderState::Living)) { + createInstance(); + } + + return instance_weak_; +} + +template <typename T> +std::shared_ptr<T> SingletonHolder<T>::try_get() { + if (UNLIKELY( + state_.load(std::memory_order_acquire) != + SingletonHolderState::Living)) { + createInstance(); + } + + return instance_weak_.lock(); +} + +template <typename T> +folly::ReadMostlySharedPtr<T> SingletonHolder<T>::try_get_fast() { + if (UNLIKELY( + state_.load(std::memory_order_acquire) != + SingletonHolderState::Living)) { + createInstance(); + } + + return instance_weak_fast_.lock(); +} + +template <typename T> +template <typename Func> +invoke_result_t<Func, T*> detail::SingletonHolder<T>::apply(Func f) { + return f(try_get().get()); +} + +template <typename T> +void SingletonHolder<T>::vivify() { + if (UNLIKELY( + state_.load(std::memory_order_relaxed) != + SingletonHolderState::Living)) { + createInstance(); + } +} + +template <typename T> +bool SingletonHolder<T>::hasLiveInstance() { + return !instance_weak_.expired(); +} + +template <typename T> +void SingletonHolder<T>::preDestroyInstance( + ReadMostlyMainPtrDeleter<>& deleter) { + instance_copy_ = instance_; + deleter.add(std::move(instance_)); +} + +template <typename T> +void SingletonHolder<T>::destroyInstance() { + state_ = SingletonHolderState::Dead; + instance_.reset(); + instance_copy_.reset(); + if (destroy_baton_) { + constexpr std::chrono::seconds kDestroyWaitTime{5}; + auto const wait_options = + destroy_baton_->wait_options().logging_enabled(false); + auto last_reference_released = + destroy_baton_->try_wait_for(kDestroyWaitTime, wait_options); + if (last_reference_released) { + teardown_(instance_ptr_); + } else { + print_destructor_stack_trace_->store(true); + detail::singletonWarnDestroyInstanceLeak(type(), instance_ptr_); + } + } +} + +template <typename T> +SingletonHolder<T>::SingletonHolder( + TypeDescriptor typeDesc, + SingletonVault& vault) noexcept + : SingletonHolderBase(typeDesc), vault_(vault) {} + +template <typename T> +bool SingletonHolder<T>::creationStarted() { + // If alive, then creation was of course started. + // This is flipped after creating_thread_ was set, and before it was reset. + if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { + return true; + } + + // Not yet built. Is it currently in progress? + if (creating_thread_.load(std::memory_order_acquire) != std::thread::id()) { + return true; + } + + return false; +} + +template <typename T> +void SingletonHolder<T>::createInstance() { + if (creating_thread_.load(std::memory_order_acquire) == + std::this_thread::get_id()) { + detail::singletonWarnCreateCircularDependencyAndAbort(type()); + } + + std::lock_guard<std::mutex> entry_lock(mutex_); + if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { + return; + } + if (state_.load(std::memory_order_acquire) == + SingletonHolderState::NotRegistered) { + detail::singletonWarnCreateUnregisteredAndAbort(type()); + } + + if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { + return; + } + + SCOPE_EXIT { + // Clean up creator thread when complete, and also, in case of errors here, + // so that subsequent attempts don't think this is still in the process of + // being built. + creating_thread_.store(std::thread::id(), std::memory_order_release); + }; + + creating_thread_.store(std::this_thread::get_id(), std::memory_order_release); + + auto state = vault_.state_.rlock(); + if (vault_.type_ != SingletonVault::Type::Relaxed && + !state->registrationComplete) { + detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type()); + } + if (state->state == detail::SingletonVaultState::Type::Quiescing) { + return; + } + + auto destroy_baton = std::make_shared<folly::Baton<>>(); + auto print_destructor_stack_trace = + std::make_shared<std::atomic<bool>>(false); + + // Can't use make_shared -- no support for a custom deleter, sadly. + std::shared_ptr<T> instance( + create_(), + [destroy_baton, print_destructor_stack_trace, type = type()](T*) mutable { + destroy_baton->post(); + if (print_destructor_stack_trace->load()) { + detail::singletonPrintDestructionStackTrace(type); + } + }); + + // We should schedule destroyInstances() only after the singleton was + // created. This will ensure it will be destroyed before singletons, + // not managed by folly::Singleton, which were initialized in its + // constructor + SingletonVault::scheduleDestroyInstances(); + + instance_weak_ = instance; + instance_ptr_ = instance.get(); + instance_.reset(std::move(instance)); + instance_weak_fast_ = instance_; + + destroy_baton_ = std::move(destroy_baton); + print_destructor_stack_trace_ = std::move(print_destructor_stack_trace); + + // This has to be the last step, because once state is Living other threads + // may access instance and instance_weak w/o synchronization. + state_.store(SingletonHolderState::Living, std::memory_order_release); + + vault_.creationOrder_.wlock()->push_back(type()); +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Singleton.cpp b/ios/Pods/Flipper-Folly/folly/Singleton.cpp new file mode 100644 index 000000000..f722bc4b6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Singleton.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Singleton.h> +#include <folly/portability/Config.h> + +#ifndef _WIN32 +#include <dlfcn.h> +#endif + +#include <atomic> +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <string> + +#include <folly/Demangle.h> +#include <folly/Format.h> +#include <folly/ScopeGuard.h> +#include <folly/detail/SingletonStackTrace.h> + +#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__) +#define FOLLY_SINGLETON_HAVE_DLSYM 1 +#endif + +namespace folly { + +#if FOLLY_SINGLETON_HAVE_DLSYM +namespace detail { +static void singleton_hs_init_weak(int* argc, char** argv[]) + __attribute__((__weakref__("hs_init"))); +} // namespace detail +#endif + +SingletonVault::Type SingletonVault::defaultVaultType() { +#if FOLLY_SINGLETON_HAVE_DLSYM + bool isPython = dlsym(RTLD_DEFAULT, "Py_Main"); + bool isHaskel = + detail::singleton_hs_init_weak || dlsym(RTLD_DEFAULT, "hs_init"); + bool isJVM = dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"); + bool isD = dlsym(RTLD_DEFAULT, "_d_run_main"); + + return isPython || isHaskel || isJVM || isD ? Type::Relaxed : Type::Strict; +#else + return Type::Relaxed; +#endif +} + +namespace detail { + +std::string TypeDescriptor::name() const { + auto ret = demangle(ti_.name()); + if (tag_ti_ != std::type_index(typeid(DefaultTag))) { + ret += "/"; + ret += demangle(tag_ti_.name()); + } + return ret.toStdString(); +} + +// clang-format off +[[noreturn]] void singletonWarnDoubleRegistrationAndAbort( + const TypeDescriptor& type) { + // Ensure the availability of std::cerr + std::ios_base::Init ioInit; + std::cerr << "Double registration of singletons of the same " + "underlying type; check for multiple definitions " + "of type folly::Singleton<" + << type.name() << ">\n"; + std::abort(); +} + +[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort( + const TypeDescriptor& type) { + // Ensure the availability of std::cerr + std::ios_base::Init ioInit; + std::cerr << "Double registration of singletons of the same " + "underlying type; check for multiple definitions " + "of type folly::LeakySingleton<" + << type.name() << ">\n"; + std::abort(); +} + +[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort( + const TypeDescriptor& type) { + auto trace = detail::getSingletonStackTrace(); + LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name() + << "\n" + << "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)"); +} + +[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort( + const TypeDescriptor& type) { + LOG(FATAL) << "Registering mock before singleton was registered: " + << type.name(); +} + +void singletonWarnDestroyInstanceLeak( + const TypeDescriptor& type, + const void* ptr) { + LOG(ERROR) << "Singleton of type " << type.name() << " has a " + << "living reference at destroyInstances time; beware! Raw " + << "pointer is " << ptr << ". It is very likely " + << "that some other singleton is holding a shared_ptr to it. " + << "This singleton will be leaked (even if a shared_ptr to it " + << "is eventually released)." + << "Make sure dependencies between these singletons are " + << "properly defined."; +} + +[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort( + const TypeDescriptor& type) { + LOG(FATAL) << "circular singleton dependency: " << type.name(); +} + +[[noreturn]] void singletonWarnCreateUnregisteredAndAbort( + const TypeDescriptor& type) { + auto trace = detail::getSingletonStackTrace(); + LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name() + << "\n" + << "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)"); +} + +[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort( + const TypeDescriptor& type) { + auto trace = detail::getSingletonStackTrace(); + LOG(FATAL) << "Singleton " << type.name() << " requested before " + << "registrationComplete() call.\n" + << "This usually means that either main() never called " + << "folly::init, or singleton was requested before main() " + << "(which is not allowed).\n" + << "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)"); +} + +void singletonPrintDestructionStackTrace(const TypeDescriptor& type) { + auto trace = detail::getSingletonStackTrace(); + LOG(ERROR) << "Singleton " << type.name() << " was released.\n" + << "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)"); +} + +[[noreturn]] void singletonThrowNullCreator(const std::type_info& type) { + auto const msg = sformat( + "nullptr_t should be passed if you want {} to be default constructed", + demangle(type)); + throw std::logic_error(msg); +} + +[[noreturn]] void singletonThrowGetInvokedAfterDestruction( + const TypeDescriptor& type) { + throw std::runtime_error( + "Raw pointer to a singleton requested after its destruction." + " Singleton type is: " + + type.name()); +} +// clang-format on + +} // namespace detail + +namespace { + +struct FatalHelper { + ~FatalHelper() { + if (!leakedSingletons_.empty()) { + std::string leakedTypes; + for (const auto& singleton : leakedSingletons_) { + leakedTypes += "\t" + singleton.name() + "\n"; + } + LOG(DFATAL) << "Singletons of the following types had living references " + << "after destroyInstances was finished:\n" + << leakedTypes + << "beware! It is very likely that those singleton instances " + << "are leaked."; + } + } + + std::vector<detail::TypeDescriptor> leakedSingletons_; +}; + +#if defined(__APPLE__) || defined(_MSC_VER) +// OS X doesn't support constructor priorities. +FatalHelper fatalHelper; +#else +FatalHelper __attribute__((__init_priority__(101))) fatalHelper; +#endif + +} // namespace + +SingletonVault::~SingletonVault() { + destroyInstances(); +} + +void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) { + auto state = state_.rlock(); + state->check(detail::SingletonVaultState::Type::Running); + + if (UNLIKELY(state->registrationComplete)) { + LOG(ERROR) << "Registering singleton after registrationComplete()."; + } + + auto singletons = singletons_.wlock(); + CHECK_THROW( + singletons->emplace(entry->type(), entry).second, std::logic_error); +} + +void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) { + auto state = state_.rlock(); + state->check(detail::SingletonVaultState::Type::Running); + + if (UNLIKELY(state->registrationComplete)) { + LOG(ERROR) << "Registering for eager-load after registrationComplete()."; + } + + CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error); + + auto eagerInitSingletons = eagerInitSingletons_.wlock(); + eagerInitSingletons->insert(entry); +} + +void SingletonVault::registrationComplete() { + std::atexit([]() { SingletonVault::singleton()->destroyInstances(); }); + + auto state = state_.wlock(); + state->check(detail::SingletonVaultState::Type::Running); + + if (state->registrationComplete) { + return; + } + + auto singletons = singletons_.rlock(); + if (type_ == Type::Strict) { + for (const auto& p : *singletons) { + if (p.second->hasLiveInstance()) { + throw std::runtime_error( + "Singleton " + p.first.name() + + " created before registration was complete."); + } + } + } + + state->registrationComplete = true; +} + +void SingletonVault::doEagerInit() { + { + auto state = state_.rlock(); + state->check(detail::SingletonVaultState::Type::Running); + if (UNLIKELY(!state->registrationComplete)) { + throw std::logic_error("registrationComplete() not yet called"); + } + } + + auto eagerInitSingletons = eagerInitSingletons_.rlock(); + for (auto* single : *eagerInitSingletons) { + single->createInstance(); + } +} + +void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) { + { + auto state = state_.rlock(); + state->check(detail::SingletonVaultState::Type::Running); + if (UNLIKELY(!state->registrationComplete)) { + throw std::logic_error("registrationComplete() not yet called"); + } + } + + auto eagerInitSingletons = eagerInitSingletons_.rlock(); + auto countdown = + std::make_shared<std::atomic<size_t>>(eagerInitSingletons->size()); + for (auto* single : *eagerInitSingletons) { + // countdown is retained by shared_ptr, and will be alive until last lambda + // is done. notifyBaton is provided by the caller, and expected to remain + // present (if it's non-nullptr). singletonSet can go out of scope but + // its values, which are SingletonHolderBase pointers, are alive as long as + // SingletonVault is not being destroyed. + exe.add([=] { + // decrement counter and notify if requested, whether initialization + // was successful, was skipped (already initialized), or exception thrown. + SCOPE_EXIT { + if (--(*countdown) == 0) { + if (done != nullptr) { + done->post(); + } + } + }; + // if initialization is in progress in another thread, don't try to init + // here. Otherwise the current thread will block on 'createInstance'. + if (!single->creationStarted()) { + single->createInstance(); + } + }); + } +} + +void SingletonVault::destroyInstances() { + auto stateW = state_.wlock(); + if (stateW->state == detail::SingletonVaultState::Type::Quiescing) { + return; + } + stateW->state = detail::SingletonVaultState::Type::Quiescing; + + auto stateR = stateW.moveFromWriteToRead(); + { + auto singletons = singletons_.rlock(); + auto creationOrder = creationOrder_.rlock(); + + CHECK_GE(singletons->size(), creationOrder->size()); + + // Release all ReadMostlyMainPtrs at once + { + ReadMostlyMainPtrDeleter<> deleter; + for (auto& singleton_type : *creationOrder) { + singletons->at(singleton_type)->preDestroyInstance(deleter); + } + } + + for (auto type_iter = creationOrder->rbegin(); + type_iter != creationOrder->rend(); + ++type_iter) { + singletons->at(*type_iter)->destroyInstance(); + } + + for (auto& singleton_type : *creationOrder) { + auto instance = singletons->at(singleton_type); + if (!instance->hasLiveInstance()) { + continue; + } + + fatalHelper.leakedSingletons_.push_back(instance->type()); + } + } + + { + auto creationOrder = creationOrder_.wlock(); + creationOrder->clear(); + } +} + +void SingletonVault::reenableInstances() { + auto state = state_.wlock(); + + state->check(detail::SingletonVaultState::Type::Quiescing); + + state->state = detail::SingletonVaultState::Type::Running; +} + +void SingletonVault::scheduleDestroyInstances() { + // Add a dependency on folly::ThreadLocal to make sure all its static + // singletons are initalized first. + threadlocal_detail::StaticMeta<void, void>::instance(); + std::atexit([] { SingletonVault::singleton()->destroyInstances(); }); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Singleton.h b/ios/Pods/Flipper-Folly/folly/Singleton.h new file mode 100644 index 000000000..e4dee36bb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Singleton.h @@ -0,0 +1,792 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// SingletonVault - a library to manage the creation and destruction +// of interdependent singletons. +// +// Recommended usage of this class: suppose you have a class +// called MyExpensiveService, and you only want to construct one (ie, +// it's a singleton), but you only want to construct it if it is used. +// +// In your .h file: +// class MyExpensiveService { +// // Caution - may return a null ptr during startup and shutdown. +// static std::shared_ptr<MyExpensiveService> getInstance(); +// .... +// }; +// +// In your .cpp file: +// namespace { struct PrivateTag {}; } +// static folly::Singleton<MyExpensiveService, PrivateTag> the_singleton; +// std::shared_ptr<MyExpensiveService> MyExpensiveService::getInstance() { +// return the_singleton.try_get(); +// } +// +// Code in other modules can access it via: +// +// auto instance = MyExpensiveService::getInstance(); +// +// Advanced usage and notes: +// +// You can also access a singleton instance with +// `Singleton<ObjectType, TagType>::try_get()`. We recommend +// that you prefer the form `the_singleton.try_get()` because it ensures that +// `the_singleton` is used and cannot be garbage-collected during linking: this +// is necessary because the constructor of `the_singleton` is what registers it +// to the SingletonVault. +// +// The singleton will be created on demand. If the constructor for +// MyExpensiveService actually makes use of *another* Singleton, then +// the right thing will happen -- that other singleton will complete +// construction before get() returns. However, in the event of a +// circular dependency, a runtime error will occur. +// +// You can have multiple singletons of the same underlying type, but +// each must be given a unique tag. If no tag is specified a default tag is +// used. We recommend that you use a tag from an anonymous namespace private to +// your implementation file, as this ensures that the singleton is only +// available via your interface and not also through Singleton<T>::try_get() +// +// namespace { +// struct Tag1 {}; +// struct Tag2 {}; +// folly::Singleton<MyExpensiveService> s_default; +// folly::Singleton<MyExpensiveService, Tag1> s1; +// folly::Singleton<MyExpensiveService, Tag2> s2; +// } +// ... +// MyExpensiveService* svc_default = s_default.get(); +// MyExpensiveService* svc1 = s1.get(); +// MyExpensiveService* svc2 = s2.get(); +// +// By default, the singleton instance is constructed via new and +// deleted via delete, but this is configurable: +// +// namespace { folly::Singleton<MyExpensiveService> the_singleton(create, +// destroy); } +// +// Where create and destroy are functions, Singleton<T>::CreateFunc +// Singleton<T>::TeardownFunc. +// +// For example, if you need to pass arguments to your class's constructor: +// class X { +// public: +// X(int a1, std::string a2); +// // ... +// } +// Make your singleton like this: +// folly::Singleton<X> singleton_x([]() { return new X(42, "foo"); }); +// +// The above examples detail a situation where an expensive singleton is loaded +// on-demand (thus only if needed). However if there is an expensive singleton +// that will likely be needed, and initialization takes a potentially long time, +// e.g. while initializing, parsing some files, talking to remote services, +// making uses of other singletons, and so on, the initialization of those can +// be scheduled up front, or "eagerly". +// +// In that case the singleton can be declared this way: +// +// namespace { +// auto the_singleton = +// folly::Singleton<MyExpensiveService>(/* optional create, destroy args */) +// .shouldEagerInit(); +// } +// +// This way the singleton's instance is built at program initialization, +// if the program opted-in to that feature by calling "doEagerInit" or +// "doEagerInitVia" during its startup. +// +// What if you need to destroy all of your singletons? Say, some of +// your singletons manage threads, but you need to fork? Or your unit +// test wants to clean up all global state? Then you can call +// SingletonVault::singleton()->destroyInstances(), which invokes the +// TeardownFunc for each singleton, in the reverse order they were +// created. It is your responsibility to ensure your singletons can +// handle cases where the singletons they depend on go away, however. +// Singletons won't be recreated after destroyInstances call. If you +// want to re-enable singleton creation (say after fork was called) you +// should call reenableInstances. + +#pragma once + +#include <folly/Exception.h> +#include <folly/Executor.h> +#include <folly/Memory.h> +#include <folly/Synchronized.h> +#include <folly/detail/Singleton.h> +#include <folly/detail/StaticSingletonManager.h> +#include <folly/experimental/ReadMostlySharedPtr.h> +#include <folly/hash/Hash.h> +#include <folly/lang/Exception.h> +#include <folly/memory/SanitizeLeak.h> +#include <folly/synchronization/Baton.h> +#include <folly/synchronization/RWSpinLock.h> + +#include <algorithm> +#include <atomic> +#include <condition_variable> +#include <functional> +#include <list> +#include <memory> +#include <mutex> +#include <string> +#include <thread> +#include <typeindex> +#include <typeinfo> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include <glog/logging.h> + +// use this guard to handleSingleton breaking change in 3rd party code +#ifndef FOLLY_SINGLETON_TRY_GET +#define FOLLY_SINGLETON_TRY_GET +#endif + +namespace folly { + +// For actual usage, please see the Singleton<T> class at the bottom +// of this file; that is what you will actually interact with. + +// SingletonVault is the class that manages singleton instances. It +// is unaware of the underlying types of singletons, and simply +// manages lifecycles and invokes CreateFunc and TeardownFunc when +// appropriate. In general, you won't need to interact with the +// SingletonVault itself. +// +// A vault goes through a few stages of life: +// +// 1. Registration phase; singletons can be registered: +// a) Strict: no singleton can be created in this stage. +// b) Relaxed: singleton can be created (the default vault is Relaxed). +// 2. registrationComplete() has been called; singletons can no +// longer be registered, but they can be created. +// 3. A vault can return to stage 1 when destroyInstances is called. +// +// In general, you don't need to worry about any of the above; just +// ensure registrationComplete() is called near the top of your main() +// function, otherwise no singletons can be instantiated. + +class SingletonVault; + +namespace detail { + +// A TypeDescriptor is the unique handle for a given singleton. It is +// a combinaiton of the type and of the optional name, and is used as +// a key in unordered_maps. +class TypeDescriptor { + public: + TypeDescriptor(const std::type_info& ti, const std::type_info& tag_ti) + : ti_(ti), tag_ti_(tag_ti) {} + + TypeDescriptor(const TypeDescriptor& other) + : ti_(other.ti_), tag_ti_(other.tag_ti_) {} + + TypeDescriptor& operator=(const TypeDescriptor& other) { + if (this != &other) { + ti_ = other.ti_; + tag_ti_ = other.tag_ti_; + } + + return *this; + } + + std::string name() const; + + friend class TypeDescriptorHasher; + + bool operator==(const TypeDescriptor& other) const { + return ti_ == other.ti_ && tag_ti_ == other.tag_ti_; + } + + private: + std::type_index ti_; + std::type_index tag_ti_; +}; + +class TypeDescriptorHasher { + public: + size_t operator()(const TypeDescriptor& ti) const { + return folly::hash::hash_combine(ti.ti_, ti.tag_ti_); + } +}; + +[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort( + const TypeDescriptor& type); + +[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort( + const TypeDescriptor& type); + +[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort( + const TypeDescriptor& type); + +void singletonWarnDestroyInstanceLeak( + const TypeDescriptor& type, + const void* ptr); + +[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort( + const TypeDescriptor& type); + +[[noreturn]] void singletonWarnCreateUnregisteredAndAbort( + const TypeDescriptor& type); + +[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort( + const TypeDescriptor& type); + +void singletonPrintDestructionStackTrace(const TypeDescriptor& type); + +[[noreturn]] void singletonThrowNullCreator(const std::type_info& type); + +[[noreturn]] void singletonThrowGetInvokedAfterDestruction( + const TypeDescriptor& type); + +struct SingletonVaultState { + // The two stages of life for a vault, as mentioned in the class comment. + enum class Type { + Running, + Quiescing, + }; + + Type state{Type::Running}; + bool registrationComplete{false}; + + // Each singleton in the vault can be in two states: dead + // (registered but never created), living (CreateFunc returned an instance). + + void check( + Type expected, + const char* msg = "Unexpected singleton state change") const { + if (expected != state) { + throw_exception<std::logic_error>(msg); + } + } +}; + +// This interface is used by SingletonVault to interact with SingletonHolders. +// Having a non-template interface allows SingletonVault to keep a list of all +// SingletonHolders. +class SingletonHolderBase { + public: + explicit SingletonHolderBase(TypeDescriptor typeDesc) noexcept + : type_(typeDesc) {} + virtual ~SingletonHolderBase() = default; + + TypeDescriptor type() const { + return type_; + } + virtual bool hasLiveInstance() = 0; + virtual void createInstance() = 0; + virtual bool creationStarted() = 0; + virtual void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) = 0; + virtual void destroyInstance() = 0; + + private: + TypeDescriptor type_; +}; + +// An actual instance of a singleton, tracking the instance itself, +// its state as described above, and the create and teardown +// functions. +template <typename T> +struct SingletonHolder : public SingletonHolderBase { + public: + typedef std::function<void(T*)> TeardownFunc; + typedef std::function<T*(void)> CreateFunc; + + template <typename Tag, typename VaultTag> + inline static SingletonHolder<T>& singleton(); + + inline T* get(); + inline std::weak_ptr<T> get_weak(); + inline std::shared_ptr<T> try_get(); + inline folly::ReadMostlySharedPtr<T> try_get_fast(); + template <typename Func> + inline invoke_result_t<Func, T*> apply(Func f); + inline void vivify(); + + void registerSingleton(CreateFunc c, TeardownFunc t); + void registerSingletonMock(CreateFunc c, TeardownFunc t); + bool hasLiveInstance() override; + void createInstance() override; + bool creationStarted() override; + void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) override; + void destroyInstance() override; + + private: + template <typename Tag, typename VaultTag> + struct Impl; + + SingletonHolder(TypeDescriptor type, SingletonVault& vault) noexcept; + + enum class SingletonHolderState { + NotRegistered, + Dead, + Living, + }; + + SingletonVault& vault_; + + // mutex protects the entire entry during construction/destruction + std::mutex mutex_; + + // State of the singleton entry. If state is Living, instance_ptr and + // instance_weak can be safely accessed w/o synchronization. + std::atomic<SingletonHolderState> state_{SingletonHolderState::NotRegistered}; + + // the thread creating the singleton (only valid while creating an object) + std::atomic<std::thread::id> creating_thread_{}; + + // The singleton itself and related functions. + + // holds a ReadMostlyMainPtr to singleton instance, set when state is changed + // from Dead to Living. Reset when state is changed from Living to Dead. + folly::ReadMostlyMainPtr<T> instance_; + // used to release all ReadMostlyMainPtrs at once + folly::ReadMostlySharedPtr<T> instance_copy_; + // weak_ptr to the singleton instance, set when state is changed from Dead + // to Living. We never write to this object after initialization, so it is + // safe to read it from different threads w/o synchronization if we know + // that state is set to Living + std::weak_ptr<T> instance_weak_; + // Fast equivalent of instance_weak_ + folly::ReadMostlyWeakPtr<T> instance_weak_fast_; + // Time we wait on destroy_baton after releasing Singleton shared_ptr. + std::shared_ptr<folly::Baton<>> destroy_baton_; + T* instance_ptr_ = nullptr; + CreateFunc create_ = nullptr; + TeardownFunc teardown_ = nullptr; + + std::shared_ptr<std::atomic<bool>> print_destructor_stack_trace_; + + SingletonHolder(const SingletonHolder&) = delete; + SingletonHolder& operator=(const SingletonHolder&) = delete; + SingletonHolder& operator=(SingletonHolder&&) = delete; + SingletonHolder(SingletonHolder&&) = delete; +}; + +} // namespace detail + +class SingletonVault { + public: + enum class Type { + Strict, // Singletons can't be created before registrationComplete() + Relaxed, // Singletons can be created before registrationComplete() + }; + + /** + * Clears all singletons in the given vault at ctor and dtor times. + * Useful for unit-tests that need to clear the world. + * + * This need can arise when a unit-test needs to swap out an object used by a + * singleton for a test-double, but the singleton needing its dependency to be + * swapped has a type or a tag local to some other translation unit and + * unavailable in the current translation unit. + * + * Other, better approaches to this need are "plz 2 refactor" .... + */ + struct ScopedExpunger { + SingletonVault* vault; + explicit ScopedExpunger(SingletonVault* v) : vault(v) { + expunge(); + } + ~ScopedExpunger() { + expunge(); + } + void expunge() { + vault->destroyInstances(); + vault->reenableInstances(); + } + }; + + static Type defaultVaultType(); + + explicit SingletonVault(Type type = defaultVaultType()) noexcept + : type_(type) {} + + // Destructor is only called by unit tests to check destroyInstances. + ~SingletonVault(); + + typedef std::function<void(void*)> TeardownFunc; + typedef std::function<void*(void)> CreateFunc; + + // Ensure that Singleton has not been registered previously and that + // registration is not complete. If validations succeeds, + // register a singleton of a given type with the create and teardown + // functions. + void registerSingleton(detail::SingletonHolderBase* entry); + + /** + * Called by `Singleton<T>.shouldEagerInit()` to ensure the instance + * is built when `doEagerInit[Via]` is called; see those methods + * for more info. + */ + void addEagerInitSingleton(detail::SingletonHolderBase* entry); + + // Mark registration is complete; no more singletons can be + // registered at this point. + void registrationComplete(); + + /** + * Initialize all singletons which were marked as eager-initialized + * (using `shouldEagerInit()`). No return value. Propagates exceptions + * from constructors / create functions, as is the usual case when calling + * for example `Singleton<Foo>::get_weak()`. + */ + void doEagerInit(); + + /** + * Schedule eager singletons' initializations through the given executor. + * If baton ptr is not null, its `post` method is called after all + * early initialization has completed. + * + * If exceptions are thrown during initialization, this method will still + * `post` the baton to indicate completion. The exception will not propagate + * and future attempts to `try_get` or `get_weak` the failed singleton will + * retry initialization. + * + * Sample usage: + * + * folly::IOThreadPoolExecutor executor(max_concurrency_level); + * folly::Baton<> done; + * doEagerInitVia(executor, &done); + * done.wait(); // or 'try_wait_for', etc. + * + */ + void doEagerInitVia(Executor& exe, folly::Baton<>* done = nullptr); + + // Destroy all singletons; when complete, the vault can't create + // singletons once again until reenableInstances() is called. + void destroyInstances(); + + // Enable re-creating singletons after destroyInstances() was called. + void reenableInstances(); + + // For testing; how many registered and living singletons we have. + size_t registeredSingletonCount() const { + return singletons_.rlock()->size(); + } + + /** + * Flips to true if eager initialization was used, and has completed. + * Never set to true if "doEagerInit()" or "doEagerInitVia" never called. + */ + bool eagerInitComplete() const; + + size_t livingSingletonCount() const { + auto singletons = singletons_.rlock(); + + size_t ret = 0; + for (const auto& p : *singletons) { + if (p.second->hasLiveInstance()) { + ++ret; + } + } + + return ret; + } + + // A well-known vault; you can actually have others, but this is the + // default. + static SingletonVault* singleton() { + return singleton<>(); + } + + // Gets singleton vault for any Tag. Non-default tag should be used in unit + // tests only. + template <typename VaultTag = detail::DefaultTag> + static SingletonVault* singleton() { + return &detail::createGlobal<SingletonVault, VaultTag>(); + } + + void setType(Type type) { + type_ = type; + } + + private: + template <typename T> + friend struct detail::SingletonHolder; + + // This method only matters if registrationComplete() is never called. + // Otherwise destroyInstances is scheduled to be executed atexit. + // + // Initializes static object, which calls destroyInstances on destruction. + // Used to have better deletion ordering with singleton not managed by + // folly::Singleton. The desruction will happen in the following order: + // 1. Singletons, not managed by folly::Singleton, which were created after + // any of the singletons managed by folly::Singleton was requested. + // 2. All singletons managed by folly::Singleton + // 3. Singletons, not managed by folly::Singleton, which were created before + // any of the singletons managed by folly::Singleton was requested. + static void scheduleDestroyInstances(); + + typedef std::unordered_map< + detail::TypeDescriptor, + detail::SingletonHolderBase*, + detail::TypeDescriptorHasher> + SingletonMap; + + // Use SharedMutexSuppressTSAN to suppress noisy lock inversions when building + // with TSAN. If TSAN is not enabled, SharedMutexSuppressTSAN is equivalent + // to a normal SharedMutex. + Synchronized<SingletonMap, SharedMutexSuppressTSAN> singletons_; + Synchronized< + std::unordered_set<detail::SingletonHolderBase*>, + SharedMutexSuppressTSAN> + eagerInitSingletons_; + Synchronized<std::vector<detail::TypeDescriptor>, SharedMutexSuppressTSAN> + creationOrder_; + + // Using SharedMutexReadPriority is important here, because we want to make + // sure we don't block nested singleton creation happening concurrently with + // destroyInstances(). + Synchronized<detail::SingletonVaultState, SharedMutexReadPriority> state_; + + Type type_; +}; + +// This is the wrapper class that most users actually interact with. +// It allows for simple access to registering and instantiating +// singletons. Create instances of this class in the global scope of +// type Singleton<T> to register your singleton for later access via +// Singleton<T>::try_get(). +template < + typename T, + typename Tag = detail::DefaultTag, + typename VaultTag = detail::DefaultTag /* for testing */> +class Singleton { + public: + typedef std::function<T*(void)> CreateFunc; + typedef std::function<void(T*)> TeardownFunc; + + // Generally your program life cycle should be fine with calling + // get() repeatedly rather than saving the reference, and then not + // call get() during process shutdown. + [[deprecated("Replaced by try_get")]] static T* get() { + return getEntry().get(); + } + + // If, however, you do need to hold a reference to the specific + // singleton, you can try to do so with a weak_ptr. Avoid this when + // possible but the inability to lock the weak pointer can be a + // signal that the vault has been destroyed. + [[deprecated("Replaced by try_get")]] static std::weak_ptr<T> get_weak() { + return getEntry().get_weak(); + } + + // Preferred alternative to get_weak, it returns shared_ptr that can be + // stored; a singleton won't be destroyed unless shared_ptr is destroyed. + // Avoid holding these shared_ptrs beyond the scope of a function; + // don't put them in member variables, always use try_get() instead + // + // try_get() can return nullptr if the singleton was destroyed, caller is + // responsible for handling nullptr return + static std::shared_ptr<T> try_get() { + return getEntry().try_get(); + } + + static folly::ReadMostlySharedPtr<T> try_get_fast() { + return getEntry().try_get_fast(); + } + + /** + * Applies a callback to the possibly-nullptr singleton instance, returning + * the callback's result. That is, the following two are functionally + * equivalent: + * singleton.apply(std::ref(f)); + * f(singleton.try_get().get()); + * + * For example, the following returns the singleton + * instance directly without any extra operations on the instance: + * auto ret = Singleton<T>::apply([](auto* v) { return v; }); + */ + template <typename Func> + static invoke_result_t<Func, T*> apply(Func f) { + return getEntry().apply(std::ref(f)); + } + + // Quickly ensure the instance exists. + static void vivify() { + getEntry().vivify(); + } + + explicit Singleton( + std::nullptr_t /* _ */ = nullptr, + typename Singleton::TeardownFunc t = nullptr) + : Singleton([]() { return new T; }, std::move(t)) {} + + explicit Singleton( + typename Singleton::CreateFunc c, + typename Singleton::TeardownFunc t = nullptr) { + if (c == nullptr) { + detail::singletonThrowNullCreator(typeid(T)); + } + + auto vault = SingletonVault::singleton<VaultTag>(); + getEntry().registerSingleton(std::move(c), getTeardownFunc(std::move(t))); + vault->registerSingleton(&getEntry()); + } + + /** + * Should be instantiated as soon as "doEagerInit[Via]" is called. + * Singletons are usually lazy-loaded (built on-demand) but for those which + * are known to be needed, to avoid the potential lag for objects that take + * long to construct during runtime, there is an option to make sure these + * are built up-front. + * + * Use like: + * Singleton<Foo> gFooInstance = Singleton<Foo>(...).shouldEagerInit(); + * + * Or alternately, define the singleton as usual, and say + * gFooInstance.shouldEagerInit(); + * + * at some point prior to calling registrationComplete(). + * Then doEagerInit() or doEagerInitVia(Executor*) can be called. + */ + Singleton& shouldEagerInit() { + auto vault = SingletonVault::singleton<VaultTag>(); + vault->addEagerInitSingleton(&getEntry()); + return *this; + } + + /** + * Construct and inject a mock singleton which should be used only from tests. + * Unlike regular singletons which are initialized once per process lifetime, + * mock singletons live for the duration of a test. This means that one + * process running multiple tests can initialize and register the same + * singleton multiple times. This functionality should be used only from tests + * since it relaxes validation and performance in order to be able to perform + * the injection. The returned mock singleton is functionality identical to + * regular singletons. + */ + static void make_mock( + std::nullptr_t /* c */ = nullptr, + typename Singleton<T>::TeardownFunc t = nullptr) { + make_mock([]() { return new T; }, t); + } + + static void make_mock( + CreateFunc c, + typename Singleton<T>::TeardownFunc t = nullptr) { + if (c == nullptr) { + detail::singletonThrowNullCreator(typeid(T)); + } + + auto& entry = getEntry(); + + entry.registerSingletonMock(c, getTeardownFunc(t)); + } + + private: + inline static detail::SingletonHolder<T>& getEntry() { + return detail::SingletonHolder<T>::template singleton<Tag, VaultTag>(); + } + + // Construct TeardownFunc. + static typename detail::SingletonHolder<T>::TeardownFunc getTeardownFunc( + TeardownFunc t) { + if (t == nullptr) { + return [](T* v) { delete v; }; + } else { + return t; + } + } +}; + +template <typename T, typename Tag = detail::DefaultTag> +class LeakySingleton { + public: + using CreateFunc = std::function<T*()>; + + LeakySingleton() : LeakySingleton([] { return new T(); }) {} + + explicit LeakySingleton(CreateFunc createFunc) { + auto& entry = entryInstance(); + if (entry.state != State::NotRegistered) { + detail::singletonWarnLeakyDoubleRegistrationAndAbort(entry.type_); + } + entry.createFunc = createFunc; + entry.state = State::Dead; + } + + static T& get() { + return instance(); + } + + static void make_mock(std::nullptr_t /* c */ = nullptr) { + make_mock([]() { return new T; }); + } + + static void make_mock(CreateFunc createFunc) { + if (createFunc == nullptr) { + detail::singletonThrowNullCreator(typeid(T)); + } + + auto& entry = entryInstance(); + if (entry.ptr) { + annotate_object_leaked(std::exchange(entry.ptr, nullptr)); + } + entry.createFunc = createFunc; + entry.state = State::Dead; + } + + private: + enum class State { NotRegistered, Dead, Living }; + + struct Entry { + Entry() noexcept {} + Entry(const Entry&) = delete; + Entry& operator=(const Entry&) = delete; + + std::atomic<State> state{State::NotRegistered}; + T* ptr{nullptr}; + CreateFunc createFunc; + std::mutex mutex; + detail::TypeDescriptor type_{typeid(T), typeid(Tag)}; + }; + + static Entry& entryInstance() { + return detail::createGlobal<Entry, Tag>(); + } + + static T& instance() { + auto& entry = entryInstance(); + if (UNLIKELY(entry.state != State::Living)) { + createInstance(); + } + + return *entry.ptr; + } + + static void createInstance() { + auto& entry = entryInstance(); + + std::lock_guard<std::mutex> lg(entry.mutex); + if (entry.state == State::Living) { + return; + } + + if (entry.state == State::NotRegistered) { + detail::singletonWarnLeakyInstantiatingNotRegisteredAndAbort(entry.type_); + } + + entry.ptr = entry.createFunc(); + entry.state = State::Living; + } +}; +} // namespace folly + +#include <folly/Singleton-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/SingletonThreadLocal.h b/ios/Pods/Flipper-Folly/folly/SingletonThreadLocal.h new file mode 100644 index 000000000..76fa7660a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SingletonThreadLocal.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <thread> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> + +#include <folly/ScopeGuard.h> +#include <folly/ThreadLocal.h> +#include <folly/detail/Iterators.h> +#include <folly/detail/Singleton.h> +#include <folly/detail/UniqueInstance.h> +#include <folly/functional/Invoke.h> + +// we do not want to use FOLLY_TLS here for mobile +#if !FOLLY_MOBILE && defined(FOLLY_TLS) +#define FOLLY_STL_USE_FOLLY_TLS 1 +#else +#undef FOLLY_STL_USE_FOLLY_TLS +#endif + +namespace folly { + +/// SingletonThreadLocal +/// +/// Useful for a per-thread leaky-singleton model in libraries and applications. +/// +/// By "leaky" it is meant that the T instances held by the instantiation +/// SingletonThreadLocal<T> will survive until their owning thread exits. +/// Therefore, they can safely be used before main() begins and after main() +/// ends, and they can also safely be used in an application that spawns many +/// temporary threads throughout its life. +/// +/// Example: +/// +/// struct UsefulButHasExpensiveCtor { +/// UsefulButHasExpensiveCtor(); // this is expensive +/// Result operator()(Arg arg); +/// }; +/// +/// Result useful(Arg arg) { +/// using Useful = UsefulButHasExpensiveCtor; +/// auto& useful = folly::SingletonThreadLocal<Useful>::get(); +/// return useful(arg); +/// } +/// +/// As an example use-case, the random generators in <random> are expensive to +/// construct. And their constructors are deterministic, but many cases require +/// that they be randomly seeded. So folly::Random makes good canonical uses of +/// folly::SingletonThreadLocal so that a seed is computed from the secure +/// random device once per thread, and the random generator is constructed with +/// the seed once per thread. +/// +/// Keywords to help people find this class in search: +/// Thread Local Singleton ThreadLocalSingleton +template < + typename T, + typename Tag = detail::DefaultTag, + typename Make = detail::DefaultMake<T>, + typename TLTag = std:: + conditional_t<std::is_same<Tag, detail::DefaultTag>::value, void, Tag>> +class SingletonThreadLocal { + private: + static detail::UniqueInstance unique; + + struct Wrapper; + + struct LocalCache { + Wrapper* cache; + }; + static_assert(std::is_pod<LocalCache>::value, "non-pod"); + + struct LocalLifetime; + + struct Wrapper { + using Object = invoke_result_t<Make>; + static_assert(std::is_convertible<Object&, T&>::value, "inconvertible"); + + using LocalCacheSet = std::unordered_set<LocalCache*>; + + // keep as first field, to save 1 instr in the fast path + Object object{Make{}()}; + + // per-cache refcounts, the number of lifetimes tracking that cache + std::unordered_map<LocalCache*, size_t> caches; + + // per-lifetime cache tracking; 1-M lifetimes may track 1-N caches + std::unordered_map<LocalLifetime*, LocalCacheSet> lifetimes; + + /* implicit */ operator T&() { + return object; + } + + ~Wrapper() { + for (auto& kvp : caches) { + kvp.first->cache = nullptr; + } + } + }; + + using WrapperTL = ThreadLocal<Wrapper, TLTag>; + + struct LocalLifetime { + ~LocalLifetime() { + auto& wrapper = getWrapper(); + auto& lifetimes = wrapper.lifetimes[this]; + for (auto cache : lifetimes) { + auto const it = wrapper.caches.find(cache); + if (!--it->second) { + wrapper.caches.erase(it); + cache->cache = nullptr; + } + } + wrapper.lifetimes.erase(this); + } + + void track(LocalCache& cache) { + auto& wrapper = getWrapper(); + cache.cache = &wrapper; + auto const inserted = wrapper.lifetimes[this].insert(&cache); + wrapper.caches[&cache] += inserted.second; + } + }; + + SingletonThreadLocal() = delete; + + FOLLY_ALWAYS_INLINE static WrapperTL& getWrapperTL() { + return detail::createGlobal<WrapperTL, Tag>(); + } + + FOLLY_NOINLINE static Wrapper& getWrapper() { + (void)unique; // force the object not to be thrown out as unused + return *getWrapperTL(); + } + +#ifdef FOLLY_STL_USE_FOLLY_TLS + FOLLY_NOINLINE static Wrapper& getSlow(LocalCache& cache) { + if (threadlocal_detail::StaticMetaBase::dying()) { + return getWrapper(); + } + static thread_local LocalLifetime lifetime; + lifetime.track(cache); // idempotent + return FOLLY_LIKELY(!!cache.cache) ? *cache.cache : getWrapper(); + } +#endif + + public: + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& get() { +#ifdef FOLLY_STL_USE_FOLLY_TLS + static thread_local LocalCache cache; + return FOLLY_LIKELY(!!cache.cache) ? *cache.cache : getSlow(cache); +#else + return getWrapper(); +#endif + } + + class Accessor { + private: + using Inner = typename WrapperTL::Accessor; + using IteratorBase = typename Inner::Iterator; + using IteratorTag = std::bidirectional_iterator_tag; + + Inner inner_; + + explicit Accessor(Inner inner) noexcept : inner_(std::move(inner)) {} + + public: + friend class SingletonThreadLocal<T, Tag, Make, TLTag>; + + class Iterator + : public detail:: + IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag> { + private: + using Super = + detail::IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag>; + using Super::Super; + + public: + friend class Accessor; + + T& dereference() const { + return const_cast<Iterator*>(this)->base()->object; + } + + std::thread::id getThreadId() const { + return this->base().getThreadId(); + } + + uint64_t getOSThreadId() const { + return this->base().getOSThreadId(); + } + }; + + Accessor(const Accessor&) = delete; + Accessor& operator=(const Accessor&) = delete; + Accessor(Accessor&&) = default; + Accessor& operator=(Accessor&&) = default; + + Iterator begin() const { + return Iterator(inner_.begin()); + } + + Iterator end() const { + return Iterator(inner_.end()); + } + }; + + // Must use a unique Tag, takes a lock that is one per Tag + static Accessor accessAllThreads() { + return Accessor(getWrapperTL().accessAllThreads()); + } +}; + +template <typename T, typename Tag, typename Make, typename TLTag> +detail::UniqueInstance SingletonThreadLocal<T, Tag, Make, TLTag>::unique{ + "folly::SingletonThreadLocal", + tag_t<T, Tag>{}, + tag_t<Make, TLTag>{}}; + +} // namespace folly + +/// FOLLY_DECLARE_REUSED +/// +/// Useful for local variables of container types, where it is desired to avoid +/// the overhead associated with the local variable entering and leaving scope. +/// Rather, where it is desired that the memory be reused between invocations +/// of the same scope in the same thread rather than deallocated and reallocated +/// between invocations of the same scope in the same thread. Note that the +/// container will always be cleared between invocations; it is only the backing +/// memory allocation which is reused. +/// +/// Example: +/// +/// void traverse_perform(int root); +/// template <typename F> +/// void traverse_each_child_r(int root, F const&); +/// void traverse_depthwise(int root) { +/// // preserves some of the memory backing these per-thread data structures +/// FOLLY_DECLARE_REUSED(seen, std::unordered_set<int>); +/// FOLLY_DECLARE_REUSED(work, std::vector<int>); +/// // example algorithm that uses these per-thread data structures +/// work.push_back(root); +/// while (!work.empty()) { +/// root = work.back(); +/// work.pop_back(); +/// seen.insert(root); +/// traverse_perform(root); +/// traverse_each_child_r(root, [&](int item) { +/// if (!seen.count(item)) { +/// work.push_back(item); +/// } +/// }); +/// } +/// } +#define FOLLY_DECLARE_REUSED(name, ...) \ + struct __folly_reused_type_##name { \ + __VA_ARGS__ object; \ + }; \ + auto& name = \ + ::folly::SingletonThreadLocal<__folly_reused_type_##name>::get().object; \ + auto __folly_reused_g_##name = ::folly::makeGuard([&] { name.clear(); }) diff --git a/ios/Pods/Flipper-Folly/folly/SocketAddress.cpp b/ios/Pods/Flipper-Folly/folly/SocketAddress.cpp new file mode 100644 index 000000000..ba143b6bc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SocketAddress.cpp @@ -0,0 +1,776 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include <folly/SocketAddress.h> + +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <sstream> +#include <string> +#include <system_error> + +#include <boost/functional/hash.hpp> + +#include <folly/CppAttributes.h> +#include <folly/Exception.h> +#include <folly/Format.h> +#include <folly/hash/Hash.h> +#include <folly/net/NetOps.h> +#include <folly/net/NetworkSocket.h> + +namespace { + +/** + * A structure to free a struct addrinfo when it goes out of scope. + */ +struct ScopedAddrInfo { + explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {} + ~ScopedAddrInfo() { + freeaddrinfo(info); + } + + struct addrinfo* info; +}; + +/** + * A simple data structure for parsing a host-and-port string. + * + * Accepts a string of the form "<host>:<port>" or just "<port>", + * and contains two string pointers to the host and the port portion of the + * string. + * + * The HostAndPort may contain pointers into the original string. It is + * responsible for the user to ensure that the input string is valid for the + * lifetime of the HostAndPort structure. + */ +struct HostAndPort { + HostAndPort(const char* str, bool hostRequired) + : host(nullptr), port(nullptr), allocated(nullptr) { + // Look for the last colon + const char* colon = strrchr(str, ':'); + if (colon == nullptr) { + // No colon, just a port number. + if (hostRequired) { + throw std::invalid_argument( + "expected a host and port string of the " + "form \"<host>:<port>\""); + } + port = str; + return; + } + + // We have to make a copy of the string so we can modify it + // and change the colon to a NUL terminator. + allocated = strdup(str); + if (!allocated) { + throw std::bad_alloc(); + } + + char* allocatedColon = allocated + (colon - str); + *allocatedColon = '\0'; + host = allocated; + port = allocatedColon + 1; + // bracketed IPv6 address, remove the brackets + // allocatedColon[-1] is fine, as allocatedColon >= host and + // *allocatedColon != *host therefore allocatedColon > host + if (*host == '[' && allocatedColon[-1] == ']') { + allocatedColon[-1] = '\0'; + ++host; + } + } + + ~HostAndPort() { + free(allocated); + } + + const char* host; + const char* port; + char* allocated; +}; + +} // namespace + +namespace folly { + +bool SocketAddress::isPrivateAddress() const { + auto family = getFamily(); + if (family == AF_INET || family == AF_INET6) { + return storage_.addr.isPrivate() || + (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal()); + } else if (external_) { + // Unix addresses are always local to a host. Return true, + // since this conforms to the semantics of returning true for IP loopback + // addresses. + return true; + } + return false; +} + +bool SocketAddress::isLoopbackAddress() const { + auto family = getFamily(); + if (family == AF_INET || family == AF_INET6) { + return storage_.addr.isLoopback(); + } else if (external_) { + // Return true for UNIX addresses, since they are always local to a host. + return true; + } + return false; +} + +void SocketAddress::setFromHostPort(const char* host, uint16_t port) { + ScopedAddrInfo results(getAddrInfo(host, port, 0)); + setFromAddrInfo(results.info); +} + +void SocketAddress::setFromIpPort(const char* ip, uint16_t port) { + ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST)); + setFromAddrInfo(results.info); +} + +void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) { + if (external_) { + storage_.un.free(); + external_ = false; + } + storage_.addr = ipAddr; + port_ = port; +} + +void SocketAddress::setFromLocalPort(uint16_t port) { + ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG)); + setFromLocalAddr(results.info); +} + +void SocketAddress::setFromLocalPort(const char* port) { + ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG)); + setFromLocalAddr(results.info); +} + +void SocketAddress::setFromLocalIpPort(const char* addressAndPort) { + HostAndPort hp(addressAndPort, false); + ScopedAddrInfo results( + getAddrInfo(hp.host, hp.port, AI_NUMERICHOST | AI_ADDRCONFIG)); + setFromLocalAddr(results.info); +} + +void SocketAddress::setFromIpPort(const char* addressAndPort) { + HostAndPort hp(addressAndPort, true); + ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST)); + setFromAddrInfo(results.info); +} + +void SocketAddress::setFromHostPort(const char* hostAndPort) { + HostAndPort hp(hostAndPort, true); + ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0)); + setFromAddrInfo(results.info); +} + +int SocketAddress::getPortFrom(const struct sockaddr* address) { + switch (address->sa_family) { + case AF_INET: + return ntohs(((sockaddr_in*)address)->sin_port); + + case AF_INET6: + return ntohs(((sockaddr_in6*)address)->sin6_port); + + default: + return -1; + } +} + +const char* SocketAddress::getFamilyNameFrom( + const struct sockaddr* address, + const char* defaultResult) { +#define GETFAMILYNAMEFROM_IMPL(Family) \ + case Family: \ + return #Family + + switch (address->sa_family) { + GETFAMILYNAMEFROM_IMPL(AF_INET); + GETFAMILYNAMEFROM_IMPL(AF_INET6); + GETFAMILYNAMEFROM_IMPL(AF_UNIX); + GETFAMILYNAMEFROM_IMPL(AF_UNSPEC); + + default: + return defaultResult; + } + +#undef GETFAMILYNAMEFROM_IMPL +} + +void SocketAddress::setFromPath(StringPiece path) { + // Before we touch storage_, check to see if the length is too big. + // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here, + // but sizeof() just uses its type, and does't evaluate it. + if (path.size() > sizeof(storage_.un.addr->sun_path)) { + throw std::invalid_argument( + "socket path too large to fit into sockaddr_un"); + } + + if (!external_) { + storage_.un.init(); + external_ = true; + } + + size_t len = path.size(); + storage_.un.len = socklen_t(offsetof(struct sockaddr_un, sun_path) + len); + memcpy(storage_.un.addr->sun_path, path.data(), len); + // If there is room, put a terminating NUL byte in sun_path. In general the + // path should be NUL terminated, although getsockname() and getpeername() + // may return Unix socket addresses with paths that fit exactly in sun_path + // with no terminating NUL. + if (len < sizeof(storage_.un.addr->sun_path)) { + storage_.un.addr->sun_path[len] = '\0'; + } +} + +void SocketAddress::setFromPeerAddress(NetworkSocket socket) { + setFromSocket(socket, netops::getpeername); +} + +void SocketAddress::setFromLocalAddress(NetworkSocket socket) { + setFromSocket(socket, netops::getsockname); +} + +void SocketAddress::setFromSockaddr(const struct sockaddr* address) { + uint16_t port; + + if (address->sa_family == AF_INET) { + port = ntohs(((sockaddr_in*)address)->sin_port); + } else if (address->sa_family == AF_INET6) { + port = ntohs(((sockaddr_in6*)address)->sin6_port); + } else if (address->sa_family == AF_UNIX) { + // We need an explicitly specified length for AF_UNIX addresses, + // to be able to distinguish anonymous addresses from addresses + // in Linux's abstract namespace. + throw std::invalid_argument( + "SocketAddress::setFromSockaddr(): the address " + "length must be explicitly specified when " + "setting AF_UNIX addresses"); + } else { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with unsupported address type"); + } + + setFromIpAddrPort(folly::IPAddress(address), port); +} + +void SocketAddress::setFromSockaddr( + const struct sockaddr* address, + socklen_t addrlen) { + // Check the length to make sure we can access address->sa_family + if (addrlen < + (offsetof(struct sockaddr, sa_family) + sizeof(address->sa_family))) { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with length too short for a sockaddr"); + } + + if (address->sa_family == AF_INET) { + if (addrlen < sizeof(struct sockaddr_in)) { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with length too short for a sockaddr_in"); + } + setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address)); + } else if (address->sa_family == AF_INET6) { + if (addrlen < sizeof(struct sockaddr_in6)) { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with length too short for a sockaddr_in6"); + } + setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address)); + } else if (address->sa_family == AF_UNIX) { + setFromSockaddr( + reinterpret_cast<const struct sockaddr_un*>(address), addrlen); + } else { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with unsupported address type"); + } +} + +void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) { + assert(address->sin_family == AF_INET); + setFromSockaddr((sockaddr*)address); +} + +void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) { + assert(address->sin6_family == AF_INET6); + setFromSockaddr((sockaddr*)address); +} + +void SocketAddress::setFromSockaddr( + const struct sockaddr_un* address, + socklen_t addrlen) { + assert(address->sun_family == AF_UNIX); + if (addrlen > sizeof(struct sockaddr_un)) { + throw std::invalid_argument( + "SocketAddress::setFromSockaddr() called " + "with length too long for a sockaddr_un"); + } + + if (!external_) { + storage_.un.init(); + } + external_ = true; + memcpy(storage_.un.addr, address, size_t(addrlen)); + updateUnixAddressLength(addrlen); + + // Fill the rest with 0s, just for safety + if (addrlen < sizeof(struct sockaddr_un)) { + auto p = reinterpret_cast<char*>(storage_.un.addr); + memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen); + } +} + +const folly::IPAddress& SocketAddress::getIPAddress() const { + auto family = getFamily(); + if (family != AF_INET && family != AF_INET6) { + throw InvalidAddressFamilyException(family); + } + return storage_.addr; +} + +socklen_t SocketAddress::getActualSize() const { + if (external_) { + return storage_.un.len; + } + switch (getFamily()) { + case AF_UNSPEC: + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + throw std::invalid_argument( + "SocketAddress::getActualSize() called " + "with unrecognized address family"); + } +} + +std::string SocketAddress::getFullyQualified() const { + if (!isFamilyInet()) { + throw std::invalid_argument("Can't get address str for non ip address"); + } + return storage_.addr.toFullyQualified(); +} + +std::string SocketAddress::getAddressStr() const { + if (!isFamilyInet()) { + throw std::invalid_argument("Can't get address str for non ip address"); + } + return storage_.addr.str(); +} + +bool SocketAddress::isFamilyInet() const { + auto family = getFamily(); + return family == AF_INET || family == AF_INET6; +} + +void SocketAddress::getAddressStr(char* buf, size_t buflen) const { + auto ret = getAddressStr(); + size_t len = std::min(buflen - 1, ret.size()); + memcpy(buf, ret.data(), len); + buf[len] = '\0'; +} + +uint16_t SocketAddress::getPort() const { + switch (getFamily()) { + case AF_INET: + case AF_INET6: + return port_; + default: + throw std::invalid_argument( + "SocketAddress::getPort() called on non-IP " + "address"); + } +} + +void SocketAddress::setPort(uint16_t port) { + switch (getFamily()) { + case AF_INET: + case AF_INET6: + port_ = port; + return; + default: + throw std::invalid_argument( + "SocketAddress::setPort() called on non-IP " + "address"); + } +} + +void SocketAddress::convertToIPv4() { + if (!tryConvertToIPv4()) { + throw std::invalid_argument( + "convertToIPv4() called on an addresse that is " + "not an IPv4-mapped address"); + } +} + +bool SocketAddress::tryConvertToIPv4() { + if (!isIPv4Mapped()) { + return false; + } + + storage_.addr = folly::IPAddress::createIPv4(storage_.addr); + return true; +} + +bool SocketAddress::mapToIPv6() { + if (getFamily() != AF_INET) { + return false; + } + + storage_.addr = folly::IPAddress::createIPv6(storage_.addr); + return true; +} + +std::string SocketAddress::getHostStr() const { + return getIpString(0); +} + +std::string SocketAddress::getPath() const { + if (!external_) { + throw std::invalid_argument( + "SocketAddress: attempting to get path " + "for a non-Unix address"); + } + + if (storage_.un.pathLength() == 0) { + // anonymous address + return std::string(); + } + if (storage_.un.addr->sun_path[0] == '\0') { + // abstract namespace + return std::string( + storage_.un.addr->sun_path, size_t(storage_.un.pathLength())); + } + + return std::string( + storage_.un.addr->sun_path, + strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength()))); +} + +std::string SocketAddress::describe() const { + if (external_) { + if (storage_.un.pathLength() == 0) { + return "<anonymous unix address>"; + } + + if (storage_.un.addr->sun_path[0] == '\0') { + // Linux supports an abstract namespace for unix socket addresses + return "<abstract unix address>"; + } + + return std::string( + storage_.un.addr->sun_path, + strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength()))); + } + switch (getFamily()) { + case AF_UNSPEC: + return "<uninitialized address>"; + case AF_INET: { + char buf[NI_MAXHOST + 16]; + getAddressStr(buf, sizeof(buf)); + size_t iplen = strlen(buf); + snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort()); + return buf; + } + case AF_INET6: { + char buf[NI_MAXHOST + 18]; + buf[0] = '['; + getAddressStr(buf + 1, sizeof(buf) - 1); + size_t iplen = strlen(buf); + snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort()); + return buf; + } + default: { + char buf[64]; + snprintf(buf, sizeof(buf), "<unknown address family %d>", getFamily()); + return buf; + } + } +} + +bool SocketAddress::operator==(const SocketAddress& other) const { + if (external_ != other.external_ || other.getFamily() != getFamily()) { + return false; + } + if (external_) { + // anonymous addresses are never equal to any other addresses + if (storage_.un.pathLength() == 0 || other.storage_.un.pathLength() == 0) { + return false; + } + + if (storage_.un.len != other.storage_.un.len) { + return false; + } + int cmp = memcmp( + storage_.un.addr->sun_path, + other.storage_.un.addr->sun_path, + size_t(storage_.un.pathLength())); + return cmp == 0; + } + + switch (getFamily()) { + case AF_INET: + case AF_INET6: + return (other.storage_.addr == storage_.addr) && (other.port_ == port_); + case AF_UNSPEC: + return other.storage_.addr.empty(); + default: + throw_exception<std::invalid_argument>( + "SocketAddress: unsupported address family for comparison"); + } +} + +bool SocketAddress::prefixMatch( + const SocketAddress& other, + unsigned prefixLength) const { + if (other.getFamily() != getFamily()) { + return false; + } + uint8_t mask_length = 128; + switch (getFamily()) { + case AF_INET: + mask_length = 32; + FOLLY_FALLTHROUGH; + case AF_INET6: { + auto prefix = folly::IPAddress::longestCommonPrefix( + {storage_.addr, mask_length}, {other.storage_.addr, mask_length}); + return prefix.second >= prefixLength; + } + default: + return false; + } +} + +size_t SocketAddress::hash() const { + size_t seed = folly::hash::twang_mix64(getFamily()); + + if (external_) { + enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) }; + const char* path = storage_.un.addr->sun_path; + auto pathLength = storage_.un.pathLength(); + // TODO: this probably could be made more efficient + for (off_t n = 0; n < pathLength; ++n) { + boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n]))); + } + } + + switch (getFamily()) { + case AF_INET: + case AF_INET6: { + boost::hash_combine(seed, port_); + boost::hash_combine(seed, storage_.addr.hash()); + break; + } + case AF_UNIX: + assert(external_); + break; + case AF_UNSPEC: + assert(storage_.addr.empty()); + boost::hash_combine(seed, storage_.addr.hash()); + break; + default: + throw_exception<std::invalid_argument>( + "SocketAddress: unsupported address family for comparison"); + } + + return seed; +} + +struct addrinfo* +SocketAddress::getAddrInfo(const char* host, uint16_t port, int flags) { + // getaddrinfo() requires the port number as a string + char portString[sizeof("65535")]; + snprintf(portString, sizeof(portString), "%" PRIu16, port); + + return getAddrInfo(host, portString, flags); +} + +struct addrinfo* +SocketAddress::getAddrInfo(const char* host, const char* port, int flags) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags; + + struct addrinfo* results; + int error = getaddrinfo(host, port, &hints, &results); + if (error != 0) { + auto os = folly::sformat( + "Failed to resolve address for '{}': {} (error={})", + host, + gai_strerror(error), + error); + throw std::system_error(error, std::generic_category(), os); + } + + return results; +} + +void SocketAddress::setFromAddrInfo(const struct addrinfo* info) { + setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen)); +} + +void SocketAddress::setFromLocalAddr(const struct addrinfo* info) { + // If an IPv6 address is present, prefer to use it, since IPv4 addresses + // can be mapped into IPv6 space. + for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) { + if (ai->ai_family == AF_INET6) { + setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen)); + return; + } + } + + // Otherwise, just use the first address in the list. + setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen)); +} + +void SocketAddress::setFromSocket( + NetworkSocket socket, + int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)) { + // Try to put the address into a local storage buffer. + sockaddr_storage tmp_sock; + socklen_t addrLen = sizeof(tmp_sock); + if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) { + folly::throwSystemError("setFromSocket() failed"); + } + + setFromSockaddr((sockaddr*)&tmp_sock, addrLen); +} + +std::string SocketAddress::getIpString(int flags) const { + char addrString[NI_MAXHOST]; + getIpString(addrString, sizeof(addrString), flags); + return std::string(addrString); +} + +void SocketAddress::getIpString(char* buf, size_t buflen, int flags) const { + auto family = getFamily(); + if (family != AF_INET && family != AF_INET6) { + throw std::invalid_argument( + "SocketAddress: attempting to get IP address " + "for a non-IP address"); + } + + sockaddr_storage tmp_sock; + storage_.addr.toSockaddrStorage(&tmp_sock, port_); + int rc = getnameinfo( + (sockaddr*)&tmp_sock, + sizeof(sockaddr_storage), + buf, + buflen, + nullptr, + 0, + flags); + if (rc != 0) { + auto os = sformat( + "getnameinfo() failed in getIpString() error = {}", gai_strerror(rc)); + throw std::system_error(rc, std::generic_category(), os); + } +} + +void SocketAddress::updateUnixAddressLength(socklen_t addrlen) { + if (addrlen < offsetof(struct sockaddr_un, sun_path)) { + throw std::invalid_argument( + "SocketAddress: attempted to set a Unix socket " + "with a length too short for a sockaddr_un"); + } + + storage_.un.len = addrlen; + if (storage_.un.pathLength() == 0) { + // anonymous address + return; + } + + if (storage_.un.addr->sun_path[0] == '\0') { + // abstract namespace. honor the specified length + } else { + // Call strnlen(), just in case the length was overspecified. + size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path); + size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength); + storage_.un.len = + socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength); + } +} + +bool SocketAddress::operator<(const SocketAddress& other) const { + if (getFamily() != other.getFamily()) { + return getFamily() < other.getFamily(); + } + + if (external_) { + // Anonymous addresses can't be compared to anything else. + // Return that they are never less than anything. + // + // Note that this still meets the requirements for a strict weak + // ordering, so we can use this operator<() with standard C++ containers. + auto thisPathLength = storage_.un.pathLength(); + if (thisPathLength == 0) { + return false; + } + auto otherPathLength = other.storage_.un.pathLength(); + if (otherPathLength == 0) { + return true; + } + + // Compare based on path length first, for efficiency + if (thisPathLength != otherPathLength) { + return thisPathLength < otherPathLength; + } + int cmp = memcmp( + storage_.un.addr->sun_path, + other.storage_.un.addr->sun_path, + size_t(thisPathLength)); + return cmp < 0; + } + switch (getFamily()) { + case AF_INET: + case AF_INET6: { + if (port_ != other.port_) { + return port_ < other.port_; + } + + return storage_.addr < other.storage_.addr; + } + case AF_UNSPEC: + default: + throw std::invalid_argument( + "SocketAddress: unsupported address family for comparing"); + } +} + +size_t hash_value(const SocketAddress& address) { + return address.hash(); +} + +std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) { + os << addr.describe(); + return os; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/SocketAddress.h b/ios/Pods/Flipper-Folly/folly/SocketAddress.h new file mode 100644 index 000000000..5c0a33300 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SocketAddress.h @@ -0,0 +1,632 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/types.h> + +#include <cassert> +#include <cstddef> +#include <iosfwd> +#include <string> + +#include <folly/IPAddress.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Sockets.h> + +namespace folly { + +class SocketAddress { + public: + SocketAddress() = default; + + /** + * Construct a SocketAddress from a hostname and port. + * + * Note: If the host parameter is not a numeric IP address, hostname + * resolution will be performed, which can be quite slow. + * + * Raises std::system_error on error. + * + * @param host The IP address (or hostname, if allowNameLookup is true) + * @param port The port (in host byte order) + * @pram allowNameLookup If true, attempt to perform hostname lookup + * if the hostname does not appear to be a numeric IP address. + * This is potentially a very slow operation, so is disabled by + * default. + */ + SocketAddress(const char* host, uint16_t port, bool allowNameLookup = false) { + // Initialize the address family first, + // since setFromHostPort() and setFromIpPort() will check it. + + if (allowNameLookup) { + setFromHostPort(host, port); + } else { + setFromIpPort(host, port); + } + } + + SocketAddress( + const std::string& host, + uint16_t port, + bool allowNameLookup = false) { + // Initialize the address family first, + // since setFromHostPort() and setFromIpPort() will check it. + + if (allowNameLookup) { + setFromHostPort(host.c_str(), port); + } else { + setFromIpPort(host.c_str(), port); + } + } + + SocketAddress(const IPAddress& ipAddr, uint16_t port) { + setFromIpAddrPort(ipAddr, port); + } + + SocketAddress(const SocketAddress& addr) { + port_ = addr.port_; + if (addr.getFamily() == AF_UNIX) { + storage_.un.init(addr.storage_.un); + } else { + storage_ = addr.storage_; + } + external_ = addr.external_; + } + + SocketAddress& operator=(const SocketAddress& addr) { + if (!external_) { + if (addr.getFamily() != AF_UNIX) { + storage_ = addr.storage_; + } else { + storage_ = addr.storage_; + storage_.un.init(addr.storage_.un); + } + } else { + if (addr.getFamily() == AF_UNIX) { + storage_.un.copy(addr.storage_.un); + } else { + storage_.un.free(); + storage_ = addr.storage_; + } + } + port_ = addr.port_; + external_ = addr.external_; + return *this; + } + + SocketAddress(SocketAddress&& addr) noexcept { + storage_ = addr.storage_; + port_ = addr.port_; + external_ = addr.external_; + addr.external_ = false; + } + + SocketAddress& operator=(SocketAddress&& addr) { + std::swap(storage_, addr.storage_); + std::swap(port_, addr.port_); + std::swap(external_, addr.external_); + return *this; + } + + ~SocketAddress() { + if (external_) { + storage_.un.free(); + } + } + + bool isInitialized() const { + return (getFamily() != AF_UNSPEC); + } + + /** + * Return whether this address is within private network. + * + * According to RFC1918, the 10/8 prefix, 172.16/12 prefix, and 192.168/16 + * prefix are reserved for private networks. + * fc00::/7 is the IPv6 version, defined in RFC4139. IPv6 link-local + * addresses (fe80::/10) are also considered private addresses. + * + * The loopback addresses 127/8 and ::1 are also regarded as private networks + * for the purpose of this function. + * + * Returns true if this is a private network address, and false otherwise. + */ + bool isPrivateAddress() const; + + /** + * Return whether this address is a loopback address. + */ + bool isLoopbackAddress() const; + + void reset() { + if (external_) { + storage_.un.free(); + } + storage_.addr = folly::IPAddress(); + external_ = false; + } + + /** + * Initialize this SocketAddress from a hostname and port. + * + * Note: If the host parameter is not a numeric IP address, hostname + * resolution will be performed, which can be quite slow. + * + * If the hostname resolves to multiple addresses, only the first will be + * returned. + * + * Raises std::system_error on error. + * + * @param host The hostname or IP address + * @param port The port (in host byte order) + */ + void setFromHostPort(const char* host, uint16_t port); + + void setFromHostPort(const std::string& host, uint16_t port) { + setFromHostPort(host.c_str(), port); + } + + /** + * Initialize this SocketAddress from an IP address and port. + * + * This is similar to setFromHostPort(), but only accepts numeric IP + * addresses. If the IP string does not look like an IP address, it throws a + * std::invalid_argument rather than trying to perform a hostname resolution. + * + * Raises std::system_error on error. + * + * @param ip The IP address, as a human-readable string. + * @param port The port (in host byte order) + */ + void setFromIpPort(const char* ip, uint16_t port); + + void setFromIpPort(const std::string& ip, uint16_t port) { + setFromIpPort(ip.c_str(), port); + } + + /** + * Initialize this SocketAddress from an IPAddress struct and port. + * + * @param ip The IP address in IPAddress format + * @param port The port (in host byte order) + */ + void setFromIpAddrPort(const IPAddress& ip, uint16_t port); + + /** + * Initialize this SocketAddress from a local port number. + * + * This is intended to be used by server code to determine the address to + * listen on. + * + * If the current machine has any IPv6 addresses configured, an IPv6 address + * will be returned (since connections from IPv4 clients can be mapped to the + * IPv6 address). If the machine does not have any IPv6 addresses, an IPv4 + * address will be returned. + */ + void setFromLocalPort(uint16_t port); + + /** + * Initialize this SocketAddress from a local port number. + * + * This version of setFromLocalPort() accepts the port as a string. A + * std::invalid_argument will be raised if the string does not refer to a port + * number. Non-numeric service port names are not accepted. + */ + void setFromLocalPort(const char* port); + void setFromLocalPort(const std::string& port) { + return setFromLocalPort(port.c_str()); + } + + /** + * Initialize this SocketAddress from a local port number and optional IP + * address. + * + * The addressAndPort string may be specified either as "<ip>:<port>", or + * just as "<port>". If the IP is not specified, the address will be + * initialized to 0, so that a server socket bound to this address will + * accept connections on all local IP addresses. + * + * Both the IP address and port number must be numeric. DNS host names and + * non-numeric service port names are not accepted. + */ + void setFromLocalIpPort(const char* addressAndPort); + void setFromLocalIpPort(const std::string& addressAndPort) { + return setFromLocalIpPort(addressAndPort.c_str()); + } + + /** + * Initialize this SocketAddress from an IP address and port number. + * + * The addressAndPort string must be of the form "<ip>:<port>". E.g., + * "10.0.0.1:1234". + * + * Both the IP address and port number must be numeric. DNS host names and + * non-numeric service port names are not accepted. + */ + void setFromIpPort(const char* addressAndPort); + void setFromIpPort(const std::string& addressAndPort) { + return setFromIpPort(addressAndPort.c_str()); + } + + /** + * Initialize this SocketAddress from a host name and port number. + * + * The addressAndPort string must be of the form "<host>:<port>". E.g., + * "www.facebook.com:443". + * + * If the host name is not a numeric IP address, a DNS lookup will be + * performed. Beware that the DNS lookup may be very slow. The port number + * must be numeric; non-numeric service port names are not accepted. + */ + void setFromHostPort(const char* hostAndPort); + void setFromHostPort(const std::string& hostAndPort) { + return setFromHostPort(hostAndPort.c_str()); + } + + /** + * Returns the port number from the given socketaddr structure. + * + * Currently only IPv4 and IPv6 are supported. + * + * Returns -1 for unsupported socket families. + */ + static int getPortFrom(const struct sockaddr* address); + + /** + * Returns the family name from the given socketaddr structure (e.g.: AF_INET6 + * for IPv6). + * + * Returns `defaultResult` for unsupported socket families. + */ + static const char* getFamilyNameFrom( + const struct sockaddr* address, + const char* defaultResult = nullptr); + + /** + * Initialize this SocketAddress from a local unix path. + * + * Raises std::invalid_argument on error. + */ + void setFromPath(StringPiece path); + + void setFromPath(const char* path, size_t length) { + setFromPath(StringPiece{path, length}); + } + + /** + * Construct a SocketAddress from a local unix socket path. + * + * Raises std::invalid_argument on error. + * + * @param path The Unix domain socket path. + */ + static SocketAddress makeFromPath(StringPiece path) { + SocketAddress addr; + addr.setFromPath(path); + return addr; + } + + /** + * Initialize this SocketAddress from a socket's peer address. + * + * Raises std::system_error on error. + */ + void setFromPeerAddress(NetworkSocket socket); + + /** + * Initialize this SocketAddress from a socket's local address. + * + * Raises std::system_error on error. + */ + void setFromLocalAddress(NetworkSocket socket); + + /** + * Initialize this folly::SocketAddress from a struct sockaddr. + * + * Raises std::system_error on error. + * + * This method is not supported for AF_UNIX addresses. For unix addresses, + * the address length must be explicitly specified. + * + * @param address A struct sockaddr. The size of the address is implied + * from address->sa_family. + */ + void setFromSockaddr(const struct sockaddr* address); + + /** + * Initialize this SocketAddress from a struct sockaddr. + * + * Raises std::system_error on error. + * + * @param address A struct sockaddr. + * @param addrlen The length of address data available. This must be long + * enough for the full address type required by + * address->sa_family. + */ + void setFromSockaddr(const struct sockaddr* address, socklen_t addrlen); + + /** + * Initialize this SocketAddress from a struct sockaddr_in. + */ + void setFromSockaddr(const struct sockaddr_in* address); + + /** + * Initialize this SocketAddress from a struct sockaddr_in6. + */ + void setFromSockaddr(const struct sockaddr_in6* address); + + /** + * Initialize this SocketAddress from a struct sockaddr_un. + * + * Note that the addrlen parameter is necessary to properly detect anonymous + * addresses, which have 0 valid path bytes, and may not even have a NUL + * character at the start of the path. + * + * @param address A struct sockaddr_un. + * @param addrlen The length of address data. This should include all of + * the valid bytes of sun_path, not including any NUL + * terminator. + */ + void setFromSockaddr(const struct sockaddr_un* address, socklen_t addrlen); + + /** + * Fill in a given sockaddr_storage with the ip or unix address. + * + * Returns the actual size of the storage used. + */ + socklen_t getAddress(sockaddr_storage* addr) const { + if (!external_) { + return storage_.addr.toSockaddrStorage(addr, htons(port_)); + } else { + memcpy(addr, storage_.un.addr, sizeof(*storage_.un.addr)); + return storage_.un.len; + } + } + + const folly::IPAddress& getIPAddress() const; + + // Deprecated: getAddress() above returns the same size as getActualSize() + socklen_t getActualSize() const; + + sa_family_t getFamily() const { + assert(external_ || AF_UNIX != storage_.addr.family()); + return external_ ? sa_family_t(AF_UNIX) : storage_.addr.family(); + } + + bool empty() const { + return getFamily() == AF_UNSPEC; + } + + /** + * Get a string representation of the IPv4 or IPv6 address. + * + * Raises std::invalid_argument if an error occurs (for example, if + * the address is not an IPv4 or IPv6 address). + */ + std::string getAddressStr() const; + + /** + * Get a string representation of the IPv4 or IPv6 address. + * + * Raises std::invalid_argument if an error occurs (for example, if + * the address is not an IPv4 or IPv6 address). + */ + void getAddressStr(char* buf, size_t buflen) const; + + /** + * Return true if it is a valid IPv4 or IPv6 address. + */ + bool isFamilyInet() const; + + /** + * For v4 & v6 addresses, return the fully qualified address string + */ + std::string getFullyQualified() const; + + /** + * Get the IPv4 or IPv6 port for this address. + * + * Raises std::invalid_argument if this is not an IPv4 or IPv6 address. + * + * @return Returns the port, in host byte order. + */ + uint16_t getPort() const; + + /** + * Set the IPv4 or IPv6 port for this address. + * + * Raises std::invalid_argument if this is not an IPv4 or IPv6 address. + */ + void setPort(uint16_t port); + + /** + * Return true if this is an IPv4-mapped IPv6 address. + */ + bool isIPv4Mapped() const { + return (getFamily() == AF_INET6 && storage_.addr.isIPv4Mapped()); + } + + /** + * Convert an IPv4-mapped IPv6 address to an IPv4 address. + * + * Raises std::invalid_argument if this is not an IPv4-mapped IPv6 address. + */ + void convertToIPv4(); + + /** + * Try to convert an address to IPv4. + * + * This attempts to convert an address to an IPv4 address if possible. + * If the address is an IPv4-mapped IPv6 address, it is converted to an IPv4 + * address and true is returned. Otherwise nothing is done, and false is + * returned. + */ + bool tryConvertToIPv4(); + + /** + * Convert an IPv4 address to IPv6 [::ffff:a.b.c.d] + */ + + bool mapToIPv6(); + + /** + * Get string representation of the host name (or IP address if the host name + * cannot be resolved). + * + * Warning: Using this method is strongly discouraged. It performs a + * DNS lookup, which may block for many seconds. + * + * Raises std::invalid_argument if an error occurs. + */ + std::string getHostStr() const; + + /** + * Get the path name for a Unix domain socket. + * + * Returns a std::string containing the path. For anonymous sockets, an + * empty string is returned. + * + * For addresses in the abstract namespace (Linux-specific), a std::string + * containing binary data is returned. In this case the first character will + * always be a NUL character. + * + * Raises std::invalid_argument if called on a non-Unix domain socket. + */ + std::string getPath() const; + + /** + * Get human-readable string representation of the address. + * + * This prints a string representation of the address, for human consumption. + * For IP addresses, the string is of the form "<IP>:<port>". + */ + std::string describe() const; + + bool operator==(const SocketAddress& other) const; + bool operator!=(const SocketAddress& other) const { + return !(*this == other); + } + + /** + * Check whether the first N bits of this address match the first N + * bits of another address. + * @note returns false if the addresses are not from the same + * address family or if the family is neither IPv4 nor IPv6 + */ + bool prefixMatch(const SocketAddress& other, unsigned prefixLength) const; + + /** + * Use this operator for storing maps based on SocketAddress. + */ + bool operator<(const SocketAddress& other) const; + + /** + * Compuate a hash of a SocketAddress. + */ + size_t hash() const; + + private: + /** + * Unix socket addresses require more storage than IPv4 and IPv6 addresses, + * and are comparatively little-used. + * + * Therefore SocketAddress' internal storage_ member variable doesn't + * contain room for a full unix address, to avoid wasting space in the common + * case. When we do need to store a Unix socket address, we use this + * ExternalUnixAddr structure to allocate a struct sockaddr_un separately on + * the heap. + */ + struct ExternalUnixAddr { + struct sockaddr_un* addr; + socklen_t len; + + socklen_t pathLength() const { + return socklen_t(len - offsetof(struct sockaddr_un, sun_path)); + } + + void init() { + addr = new struct sockaddr_un; + addr->sun_family = AF_UNIX; + len = 0; + } + void init(const ExternalUnixAddr& other) { + addr = new struct sockaddr_un; + len = other.len; + memcpy(addr, other.addr, size_t(len)); + } + void copy(const ExternalUnixAddr& other) { + len = other.len; + memcpy(addr, other.addr, size_t(len)); + } + void free() { + delete addr; + } + }; + + struct addrinfo* getAddrInfo(const char* host, uint16_t port, int flags); + struct addrinfo* getAddrInfo(const char* host, const char* port, int flags); + void setFromAddrInfo(const struct addrinfo* info); + void setFromLocalAddr(const struct addrinfo* info); + void setFromSocket( + NetworkSocket socket, + int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)); + std::string getIpString(int flags) const; + void getIpString(char* buf, size_t buflen, int flags) const; + + void updateUnixAddressLength(socklen_t addrlen); + + /* + * storage_ contains room for a full IPv4 or IPv6 address, so they can be + * stored inline without a separate allocation on the heap. + * + * If we need to store a Unix socket address, ExternalUnixAddr is a shim to + * track a struct sockaddr_un allocated separately on the heap. + */ + union AddrStorage { + folly::IPAddress addr; + ExternalUnixAddr un; + AddrStorage() : addr() {} + } storage_{}; + // IPAddress class does nto save zone or port, and must be saved here + uint16_t port_; + + bool external_{false}; +}; + +/** + * Hash a SocketAddress object. + * + * boost::hash uses hash_value(), so this allows boost::hash to automatically + * work for SocketAddress. + */ +size_t hash_value(const SocketAddress& address); + +std::ostream& operator<<(std::ostream& os, const SocketAddress& addr); +} // namespace folly + +namespace std { + +// Provide an implementation for std::hash<SocketAddress> +template <> +struct hash<folly::SocketAddress> { + size_t operator()(const folly::SocketAddress& addr) const { + return addr.hash(); + } +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/SpinLock.h b/ios/Pods/Flipper-Folly/folly/SpinLock.h new file mode 100644 index 000000000..9bc7b8daf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SpinLock.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * N.B. You most likely do _not_ want to use SpinLock or any other + * kind of spinlock. Use std::mutex instead. + * + * In short, spinlocks in preemptive multi-tasking operating systems + * have serious problems and fast mutexes like std::mutex are almost + * certainly the better choice, because letting the OS scheduler put a + * thread to sleep is better for system responsiveness and throughput + * than wasting a timeslice repeatedly querying a lock held by a + * thread that's blocked, and you can't prevent userspace + * programs blocking. + * + * Spinlocks in an operating system kernel make much more sense than + * they do in userspace. + */ + +#pragma once + +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/synchronization/SmallLocks.h> + +namespace folly { + +class SpinLock { + public: + FOLLY_ALWAYS_INLINE SpinLock() noexcept { + lock_.init(); + } + FOLLY_ALWAYS_INLINE void lock() const noexcept { + lock_.lock(); + } + FOLLY_ALWAYS_INLINE void unlock() const noexcept { + lock_.unlock(); + } + FOLLY_ALWAYS_INLINE bool try_lock() const noexcept { + return lock_.try_lock(); + } + + private: + mutable folly::MicroSpinLock lock_; +}; + +template <typename LOCK> +class SpinLockGuardImpl { + public: + FOLLY_ALWAYS_INLINE explicit SpinLockGuardImpl(LOCK& lock) noexcept( + noexcept(lock.lock())) + : lock_(lock) { + lock_.lock(); + } + + SpinLockGuardImpl(const SpinLockGuardImpl&) = delete; + SpinLockGuardImpl& operator=(const SpinLockGuardImpl&) = delete; + + FOLLY_ALWAYS_INLINE ~SpinLockGuardImpl() { + lock_.unlock(); + } + + private: + LOCK& lock_; +}; + +typedef SpinLockGuardImpl<SpinLock> SpinLockGuard; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/String-inl.h b/ios/Pods/Flipper-Folly/folly/String-inl.h new file mode 100644 index 000000000..04841caa0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/String-inl.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <iterator> +#include <stdexcept> + +#include <folly/CppAttributes.h> + +#ifndef FOLLY_STRING_H_ +#error This file may only be included from String.h +#endif + +namespace folly { + +namespace detail { +// Map from character code to value of one-character escape sequence +// ('\n' = 10 maps to 'n'), 'O' if the character should be printed as +// an octal escape sequence, or 'P' if the character is printable and +// should be printed as is. +extern const std::array<char, 256> cEscapeTable; +} // namespace detail + +template <class String> +void cEscape(StringPiece str, String& out) { + char esc[4]; + esc[0] = '\\'; + out.reserve(out.size() + str.size()); + auto p = str.begin(); + auto last = p; // last regular character + // We advance over runs of regular characters (printable, not double-quote or + // backslash) and copy them in one go; this is faster than calling push_back + // repeatedly. + while (p != str.end()) { + char c = *p; + unsigned char v = static_cast<unsigned char>(c); + char e = detail::cEscapeTable[v]; + if (e == 'P') { // printable + ++p; + } else if (e == 'O') { // octal + out.append(&*last, size_t(p - last)); + esc[1] = '0' + ((v >> 6) & 7); + esc[2] = '0' + ((v >> 3) & 7); + esc[3] = '0' + (v & 7); + out.append(esc, 4); + ++p; + last = p; + } else { // special 1-character escape + out.append(&*last, size_t(p - last)); + esc[1] = e; + out.append(esc, 2); + ++p; + last = p; + } + } + out.append(&*last, size_t(p - last)); +} + +namespace detail { +// Map from the character code of the character following a backslash to +// the unescaped character if a valid one-character escape sequence +// ('n' maps to 10 = '\n'), 'O' if this is the first character of an +// octal escape sequence, 'X' if this is the first character of a +// hexadecimal escape sequence, or 'I' if this escape sequence is invalid. +extern const std::array<char, 256> cUnescapeTable; + +// Map from the character code to the hex value, or 16 if invalid hex char. +extern const std::array<unsigned char, 256> hexTable; +} // namespace detail + +template <class String> +void cUnescape(StringPiece str, String& out, bool strict) { + out.reserve(out.size() + str.size()); + auto p = str.begin(); + auto last = p; // last regular character (not part of an escape sequence) + // We advance over runs of regular characters (not backslash) and copy them + // in one go; this is faster than calling push_back repeatedly. + while (p != str.end()) { + char c = *p; + if (c != '\\') { // normal case + ++p; + continue; + } + out.append(&*last, p - last); + ++p; + if (p == str.end()) { // backslash at end of string + if (strict) { + throw_exception<std::invalid_argument>("incomplete escape sequence"); + } + out.push_back('\\'); + last = p; + continue; + } + char e = detail::cUnescapeTable[static_cast<unsigned char>(*p)]; + if (e == 'O') { // octal + unsigned char val = 0; + for (int i = 0; i < 3 && p != str.end() && *p >= '0' && *p <= '7'; + ++i, ++p) { + val <<= 3; + val |= (*p - '0'); + } + out.push_back(val); + last = p; + } else if (e == 'X') { // hex + ++p; + if (p == str.end()) { // \x at end of string + if (strict) { + throw_exception<std::invalid_argument>( + "incomplete hex escape sequence"); + } + out.append("\\x"); + last = p; + continue; + } + unsigned char val = 0; + unsigned char h; + for (; (p != str.end() && + (h = detail::hexTable[static_cast<unsigned char>(*p)]) < 16); + ++p) { + val <<= 4; + val |= h; + } + out.push_back(val); + last = p; + } else if (e == 'I') { // invalid + if (strict) { + throw_exception<std::invalid_argument>("invalid escape sequence"); + } + out.push_back('\\'); + out.push_back(*p); + ++p; + last = p; + } else { // standard escape sequence, \' etc + out.push_back(e); + ++p; + last = p; + } + } + out.append(&*last, p - last); +} + +namespace detail { +// Map from character code to escape mode: +// 0 = pass through +// 1 = unused +// 2 = pass through in PATH mode +// 3 = space, replace with '+' in QUERY mode +// 4 = percent-encode +extern const std::array<unsigned char, 256> uriEscapeTable; +} // namespace detail + +template <class String> +void uriEscape(StringPiece str, String& out, UriEscapeMode mode) { + static const char hexValues[] = "0123456789abcdef"; + char esc[3]; + esc[0] = '%'; + // Preallocate assuming that 25% of the input string will be escaped + out.reserve(out.size() + str.size() + 3 * (str.size() / 4)); + auto p = str.begin(); + auto last = p; // last regular character + // We advance over runs of passthrough characters and copy them in one go; + // this is faster than calling push_back repeatedly. + unsigned char minEncode = static_cast<unsigned char>(mode); + while (p != str.end()) { + char c = *p; + unsigned char v = static_cast<unsigned char>(c); + unsigned char discriminator = detail::uriEscapeTable[v]; + if (LIKELY(discriminator <= minEncode)) { + ++p; + } else if (mode == UriEscapeMode::QUERY && discriminator == 3) { + out.append(&*last, size_t(p - last)); + out.push_back('+'); + ++p; + last = p; + } else { + out.append(&*last, size_t(p - last)); + esc[1] = hexValues[v >> 4]; + esc[2] = hexValues[v & 0x0f]; + out.append(esc, 3); + ++p; + last = p; + } + } + out.append(&*last, size_t(p - last)); +} + +template <class String> +void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) { + out.reserve(out.size() + str.size()); + auto p = str.begin(); + auto last = p; + // We advance over runs of passthrough characters and copy them in one go; + // this is faster than calling push_back repeatedly. + while (p != str.end()) { + char c = *p; + switch (c) { + case '%': { + if (UNLIKELY(std::distance(p, str.end()) < 3)) { + throw_exception<std::invalid_argument>( + "incomplete percent encode sequence"); + } + auto h1 = detail::hexTable[static_cast<unsigned char>(p[1])]; + auto h2 = detail::hexTable[static_cast<unsigned char>(p[2])]; + if (UNLIKELY(h1 == 16 || h2 == 16)) { + throw_exception<std::invalid_argument>( + "invalid percent encode sequence"); + } + out.append(&*last, size_t(p - last)); + out.push_back((h1 << 4) | h2); + p += 3; + last = p; + break; + } + case '+': + if (mode == UriEscapeMode::QUERY) { + out.append(&*last, size_t(p - last)); + out.push_back(' '); + ++p; + last = p; + break; + } + // else fallthrough + FOLLY_FALLTHROUGH; + default: + ++p; + break; + } + } + out.append(&*last, size_t(p - last)); +} + +namespace detail { + +/* + * The following functions are type-overloaded helpers for + * internalSplit(). + */ +inline size_t delimSize(char) { + return 1; +} +inline size_t delimSize(StringPiece s) { + return s.size(); +} +inline bool atDelim(const char* s, char c) { + return *s == c; +} +inline bool atDelim(const char* s, StringPiece sp) { + return !std::memcmp(s, sp.start(), sp.size()); +} + +// These are used to short-circuit internalSplit() in the case of +// 1-character strings. +inline char delimFront(char c) { + // This one exists only for compile-time; it should never be called. + std::abort(); + return c; +} +inline char delimFront(StringPiece s) { + assert(!s.empty() && s.start() != nullptr); + return *s.start(); +} + +/* + * Shared implementation for all the split() overloads. + * + * This uses some external helpers that are overloaded to let this + * algorithm be more performant if the deliminator is a single + * character instead of a whole string. + * + * @param ignoreEmpty iff true, don't copy empty segments to output + */ +template <class OutStringT, class DelimT, class OutputIterator> +void internalSplit( + DelimT delim, + StringPiece sp, + OutputIterator out, + bool ignoreEmpty) { + assert(sp.empty() || sp.start() != nullptr); + + const char* s = sp.start(); + const size_t strSize = sp.size(); + const size_t dSize = delimSize(delim); + + if (dSize > strSize || dSize == 0) { + if (!ignoreEmpty || strSize > 0) { + *out++ = to<OutStringT>(sp); + } + return; + } + if (std::is_same<DelimT, StringPiece>::value && dSize == 1) { + // Call the char version because it is significantly faster. + return internalSplit<OutStringT>(delimFront(delim), sp, out, ignoreEmpty); + } + + size_t tokenStartPos = 0; + size_t tokenSize = 0; + for (size_t i = 0; i <= strSize - dSize; ++i) { + if (atDelim(&s[i], delim)) { + if (!ignoreEmpty || tokenSize > 0) { + *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize)); + } + + tokenStartPos = i + dSize; + tokenSize = 0; + i += dSize - 1; + } else { + ++tokenSize; + } + } + tokenSize = strSize - tokenStartPos; + if (!ignoreEmpty || tokenSize > 0) { + *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize)); + } +} + +template <class String> +StringPiece prepareDelim(const String& s) { + return StringPiece(s); +} +inline char prepareDelim(char c) { + return c; +} + +template <class OutputType> +void toOrIgnore(StringPiece input, OutputType& output) { + output = folly::to<OutputType>(input); +} + +inline void toOrIgnore(StringPiece, decltype(std::ignore)&) {} + +template <bool exact, class Delim, class OutputType> +bool splitFixed(const Delim& delimiter, StringPiece input, OutputType& output) { + static_assert( + exact || std::is_same<OutputType, StringPiece>::value || + IsSomeString<OutputType>::value || + std::is_same<OutputType, decltype(std::ignore)>::value, + "split<false>() requires that the last argument be a string type " + "or std::ignore"); + if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) { + return false; + } + toOrIgnore(input, output); + return true; +} + +template <bool exact, class Delim, class OutputType, class... OutputTypes> +bool splitFixed( + const Delim& delimiter, + StringPiece input, + OutputType& outHead, + OutputTypes&... outTail) { + size_t cut = input.find(delimiter); + if (UNLIKELY(cut == std::string::npos)) { + return false; + } + StringPiece head(input.begin(), input.begin() + cut); + StringPiece tail( + input.begin() + cut + detail::delimSize(delimiter), input.end()); + if (LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) { + toOrIgnore(head, outHead); + return true; + } + return false; +} + +} // namespace detail + +////////////////////////////////////////////////////////////////////// + +template <class Delim, class String, class OutputType> +void split( + const Delim& delimiter, + const String& input, + std::vector<OutputType>& out, + bool ignoreEmpty) { + detail::internalSplit<OutputType>( + detail::prepareDelim(delimiter), + StringPiece(input), + std::back_inserter(out), + ignoreEmpty); +} + +template <class Delim, class String, class OutputType> +void split( + const Delim& delimiter, + const String& input, + fbvector<OutputType, std::allocator<OutputType>>& out, + bool ignoreEmpty) { + detail::internalSplit<OutputType>( + detail::prepareDelim(delimiter), + StringPiece(input), + std::back_inserter(out), + ignoreEmpty); +} + +template < + class OutputValueType, + class Delim, + class String, + class OutputIterator> +void splitTo( + const Delim& delimiter, + const String& input, + OutputIterator out, + bool ignoreEmpty) { + detail::internalSplit<OutputValueType>( + detail::prepareDelim(delimiter), StringPiece(input), out, ignoreEmpty); +} + +template <bool exact, class Delim, class... OutputTypes> +typename std::enable_if< + StrictConjunction<IsConvertible<OutputTypes>...>::value && + sizeof...(OutputTypes) >= 1, + bool>::type +split(const Delim& delimiter, StringPiece input, OutputTypes&... outputs) { + return detail::splitFixed<exact>( + detail::prepareDelim(delimiter), input, outputs...); +} + +namespace detail { + +/* + * If a type can have its string size determined cheaply, we can more + * efficiently append it in a loop (see internalJoinAppend). Note that the + * struct need not conform to the std::string api completely (ex. does not need + * to implement append()). + */ +template <class T> +struct IsSizableString { + enum { + value = IsSomeString<T>::value || std::is_same<T, StringPiece>::value + }; +}; + +template <class Iterator> +struct IsSizableStringContainerIterator + : IsSizableString<typename std::iterator_traits<Iterator>::value_type> {}; + +template <class Delim, class Iterator, class String> +void internalJoinAppend( + Delim delimiter, + Iterator begin, + Iterator end, + String& output) { + assert(begin != end); + if (std::is_same<Delim, StringPiece>::value && delimSize(delimiter) == 1) { + internalJoinAppend(delimFront(delimiter), begin, end, output); + return; + } + toAppend(*begin, &output); + while (++begin != end) { + toAppend(delimiter, *begin, &output); + } +} + +template <class Delim, class Iterator, class String> +typename std::enable_if<IsSizableStringContainerIterator<Iterator>::value>::type +internalJoin(Delim delimiter, Iterator begin, Iterator end, String& output) { + output.clear(); + if (begin == end) { + return; + } + const size_t dsize = delimSize(delimiter); + Iterator it = begin; + size_t size = it->size(); + while (++it != end) { + size += dsize + it->size(); + } + output.reserve(size); + internalJoinAppend(delimiter, begin, end, output); +} + +template <class Delim, class Iterator, class String> +typename std::enable_if< + !IsSizableStringContainerIterator<Iterator>::value>::type +internalJoin(Delim delimiter, Iterator begin, Iterator end, String& output) { + output.clear(); + if (begin == end) { + return; + } + internalJoinAppend(delimiter, begin, end, output); +} + +} // namespace detail + +template <class Delim, class Iterator, class String> +void join( + const Delim& delimiter, + Iterator begin, + Iterator end, + String& output) { + detail::internalJoin(detail::prepareDelim(delimiter), begin, end, output); +} + +template <class OutputString> +void backslashify( + folly::StringPiece input, + OutputString& output, + bool hex_style) { + static const char hexValues[] = "0123456789abcdef"; + output.clear(); + output.reserve(3 * input.size()); + for (unsigned char c : input) { + // less than space or greater than '~' are considered unprintable + if (c < 0x20 || c > 0x7e || c == '\\') { + bool hex_append = false; + output.push_back('\\'); + if (hex_style) { + hex_append = true; + } else { + if (c == '\r') { + output += 'r'; + } else if (c == '\n') { + output += 'n'; + } else if (c == '\t') { + output += 't'; + } else if (c == '\a') { + output += 'a'; + } else if (c == '\b') { + output += 'b'; + } else if (c == '\0') { + output += '0'; + } else if (c == '\\') { + output += '\\'; + } else { + hex_append = true; + } + } + if (hex_append) { + output.push_back('x'); + output.push_back(hexValues[(c >> 4) & 0xf]); + output.push_back(hexValues[c & 0xf]); + } + } else { + output += c; + } + } +} + +template <class String1, class String2> +void humanify(const String1& input, String2& output) { + size_t numUnprintable = 0; + size_t numPrintablePrefix = 0; + for (unsigned char c : input) { + if (c < 0x20 || c > 0x7e || c == '\\') { + ++numUnprintable; + } + if (numUnprintable == 0) { + ++numPrintablePrefix; + } + } + + // hexlify doubles a string's size; backslashify can potentially + // explode it by 4x. Now, the printable range of the ascii + // "spectrum" is around 95 out of 256 values, so a "random" binary + // string should be around 60% unprintable. We use a 50% hueristic + // here, so if a string is 60% unprintable, then we just use hex + // output. Otherwise we backslash. + // + // UTF8 is completely ignored; as a result, utf8 characters will + // likely be \x escaped (since most common glyphs fit in two bytes). + // This is a tradeoff of complexity/speed instead of a convenience + // that likely would rarely matter. Moreover, this function is more + // about displaying underlying bytes, not about displaying glyphs + // from languages. + if (numUnprintable == 0) { + output = input; + } else if (5 * numUnprintable >= 3 * input.size()) { + // However! If we have a "meaningful" prefix of printable + // characters, say 20% of the string, we backslashify under the + // assumption viewing the prefix as ascii is worth blowing the + // output size up a bit. + if (5 * numPrintablePrefix >= input.size()) { + backslashify(input, output); + } else { + output = "0x"; + hexlify(input, output, true /* append output */); + } + } else { + backslashify(input, output); + } +} + +template <class InputString, class OutputString> +bool hexlify( + const InputString& input, + OutputString& output, + bool append_output) { + if (!append_output) { + output.clear(); + } + + static char hexValues[] = "0123456789abcdef"; + auto j = output.size(); + output.resize(2 * input.size() + output.size()); + for (size_t i = 0; i < input.size(); ++i) { + int ch = input[i]; + output[j++] = hexValues[(ch >> 4) & 0xf]; + output[j++] = hexValues[ch & 0xf]; + } + return true; +} + +template <class InputString, class OutputString> +bool unhexlify(const InputString& input, OutputString& output) { + if (input.size() % 2 != 0) { + return false; + } + output.resize(input.size() / 2); + int j = 0; + + for (size_t i = 0; i < input.size(); i += 2) { + int highBits = detail::hexTable[static_cast<uint8_t>(input[i])]; + int lowBits = detail::hexTable[static_cast<uint8_t>(input[i + 1])]; + if ((highBits | lowBits) & 0x10) { + // One of the characters wasn't a hex digit + return false; + } + output[j++] = (highBits << 4) + lowBits; + } + return true; +} + +namespace detail { +/** + * Hex-dump at most 16 bytes starting at offset from a memory area of size + * bytes. Return the number of bytes actually dumped. + */ +size_t +hexDumpLine(const void* ptr, size_t offset, size_t size, std::string& line); +} // namespace detail + +template <class OutIt> +void hexDump(const void* ptr, size_t size, OutIt out) { + size_t offset = 0; + std::string line; + while (offset < size) { + offset += detail::hexDumpLine(ptr, offset, size, line); + *out++ = line; + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/String.cpp b/ios/Pods/Flipper-Folly/folly/String.cpp new file mode 100644 index 000000000..950264a8e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/String.cpp @@ -0,0 +1,786 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/String.h> + +#include <cctype> +#include <cerrno> +#include <cstdarg> +#include <cstring> +#include <iterator> +#include <sstream> +#include <stdexcept> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/container/Array.h> + +namespace folly { + +static_assert(IsConvertible<float>::value, ""); +static_assert(IsConvertible<int>::value, ""); +static_assert(IsConvertible<bool>::value, ""); +static_assert(IsConvertible<int>::value, ""); +static_assert(!IsConvertible<std::vector<int>>::value, ""); + +namespace detail { + +struct string_table_c_escape_make_item { + constexpr char operator()(std::size_t index) const { + // clang-format off + return + index == '"' ? '"' : + index == '\\' ? '\\' : + index == '?' ? '?' : + index == '\n' ? 'n' : + index == '\r' ? 'r' : + index == '\t' ? 't' : + index < 32 || index > 126 ? 'O' : // octal + 'P'; // printable + // clang-format on + } +}; + +struct string_table_c_unescape_make_item { + constexpr char operator()(std::size_t index) const { + // clang-format off + return + index == '\'' ? '\'' : + index == '?' ? '?' : + index == '\\' ? '\\' : + index == '"' ? '"' : + index == 'a' ? '\a' : + index == 'b' ? '\b' : + index == 'f' ? '\f' : + index == 'n' ? '\n' : + index == 'r' ? '\r' : + index == 't' ? '\t' : + index == 'v' ? '\v' : + index >= '0' && index <= '7' ? 'O' : // octal + index == 'x' ? 'X' : // hex + 'I'; // invalid + // clang-format on + } +}; + +struct string_table_hex_make_item { + constexpr unsigned char operator()(std::size_t index) const { + // clang-format off + return static_cast<unsigned char>( + index >= '0' && index <= '9' ? index - '0' : + index >= 'a' && index <= 'f' ? index - 'a' + 10 : + index >= 'A' && index <= 'F' ? index - 'A' + 10 : + 16); + // clang-format on + } +}; + +struct string_table_uri_escape_make_item { + // 0 = passthrough + // 1 = unused + // 2 = safe in path (/) + // 3 = space (replace with '+' in query) + // 4 = always percent-encode + constexpr unsigned char operator()(std::size_t index) const { + // clang-format off + return + index >= '0' && index <= '9' ? 0 : + index >= 'A' && index <= 'Z' ? 0 : + index >= 'a' && index <= 'z' ? 0 : + index == '-' ? 0 : + index == '_' ? 0 : + index == '.' ? 0 : + index == '~' ? 0 : + index == '/' ? 2 : + index == ' ' ? 3 : + 4; + // clang-format on + } +}; + +FOLLY_STORAGE_CONSTEXPR decltype(cEscapeTable) cEscapeTable = + make_array_with<256>(string_table_c_escape_make_item{}); +FOLLY_STORAGE_CONSTEXPR decltype(cUnescapeTable) cUnescapeTable = + make_array_with<256>(string_table_c_unescape_make_item{}); +FOLLY_STORAGE_CONSTEXPR decltype(hexTable) hexTable = + make_array_with<256>(string_table_hex_make_item{}); +FOLLY_STORAGE_CONSTEXPR decltype(uriEscapeTable) uriEscapeTable = + make_array_with<256>(string_table_uri_escape_make_item{}); + +} // namespace detail + +static inline bool is_oddspace(char c) { + return c == '\n' || c == '\t' || c == '\r'; +} + +StringPiece ltrimWhitespace(StringPiece sp) { + // Spaces other than ' ' characters are less common but should be + // checked. This configuration where we loop on the ' ' + // separately from oddspaces was empirically fastest. + + while (true) { + while (!sp.empty() && sp.front() == ' ') { + sp.pop_front(); + } + if (!sp.empty() && is_oddspace(sp.front())) { + sp.pop_front(); + continue; + } + + return sp; + } +} + +StringPiece rtrimWhitespace(StringPiece sp) { + // Spaces other than ' ' characters are less common but should be + // checked. This configuration where we loop on the ' ' + // separately from oddspaces was empirically fastest. + + while (true) { + while (!sp.empty() && sp.back() == ' ') { + sp.pop_back(); + } + if (!sp.empty() && is_oddspace(sp.back())) { + sp.pop_back(); + continue; + } + + return sp; + } +} + +namespace { + +int stringAppendfImplHelper( + char* buf, + size_t bufsize, + const char* format, + va_list args) { + va_list args_copy; + va_copy(args_copy, args); + int bytes_used = vsnprintf(buf, bufsize, format, args_copy); + va_end(args_copy); + return bytes_used; +} + +void stringAppendfImpl(std::string& output, const char* format, va_list args) { + // Very simple; first, try to avoid an allocation by using an inline + // buffer. If that fails to hold the output string, allocate one on + // the heap, use it instead. + // + // It is hard to guess the proper size of this buffer; some + // heuristics could be based on the number of format characters, or + // static analysis of a codebase. Or, we can just pick a number + // that seems big enough for simple cases (say, one line of text on + // a terminal) without being large enough to be concerning as a + // stack variable. + std::array<char, 128> inline_buffer; + + int bytes_used = stringAppendfImplHelper( + inline_buffer.data(), inline_buffer.size(), format, args); + if (bytes_used < 0) { + throw std::runtime_error(to<std::string>( + "Invalid format string; snprintf returned negative " + "with format string: ", + format)); + } + + if (static_cast<size_t>(bytes_used) < inline_buffer.size()) { + output.append(inline_buffer.data(), size_t(bytes_used)); + return; + } + + // Couldn't fit. Heap allocate a buffer, oh well. + std::unique_ptr<char[]> heap_buffer(new char[size_t(bytes_used + 1)]); + int final_bytes_used = stringAppendfImplHelper( + heap_buffer.get(), size_t(bytes_used + 1), format, args); + // The second call can take fewer bytes if, for example, we were printing a + // string buffer with null-terminating char using a width specifier - + // vsnprintf("%.*s", buf.size(), buf) + CHECK(bytes_used >= final_bytes_used); + + // We don't keep the trailing '\0' in our output string + output.append(heap_buffer.get(), size_t(final_bytes_used)); +} + +} // namespace + +std::string stringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVPrintf(format, ap); +} + +std::string stringVPrintf(const char* format, va_list ap) { + std::string ret; + stringAppendfImpl(ret, format, ap); + return ret; +} + +// Basic declarations; allow for parameters of strings and string +// pieces to be specified. +std::string& stringAppendf(std::string* output, const char* format, ...) { + va_list ap; + va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVAppendf(output, format, ap); +} + +std::string& +stringVAppendf(std::string* output, const char* format, va_list ap) { + stringAppendfImpl(*output, format, ap); + return *output; +} + +void stringPrintf(std::string* output, const char* format, ...) { + va_list ap; + va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVPrintf(output, format, ap); +} + +void stringVPrintf(std::string* output, const char* format, va_list ap) { + output->clear(); + stringAppendfImpl(*output, format, ap); +} + +namespace { + +struct PrettySuffix { + const char* suffix; + double val; +}; + +const PrettySuffix kPrettyTimeSuffixes[] = { + {"s ", 1e0L}, + {"ms", 1e-3L}, + {"us", 1e-6L}, + {"ns", 1e-9L}, + {"ps", 1e-12L}, + {"s ", 0}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyTimeHmsSuffixes[] = { + {"h ", 60L * 60L}, + {"m ", 60L}, + {"s ", 1e0L}, + {"ms", 1e-3L}, + {"us", 1e-6L}, + {"ns", 1e-9L}, + {"ps", 1e-12L}, + {"s ", 0}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyBytesMetricSuffixes[] = { + {"EB", 1e18L}, + {"PB", 1e15L}, + {"TB", 1e12L}, + {"GB", 1e9L}, + {"MB", 1e6L}, + {"kB", 1e3L}, + {"B ", 0L}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyBytesBinarySuffixes[] = { + {"EB", int64_t(1) << 60}, + {"PB", int64_t(1) << 50}, + {"TB", int64_t(1) << 40}, + {"GB", int64_t(1) << 30}, + {"MB", int64_t(1) << 20}, + {"kB", int64_t(1) << 10}, + {"B ", 0L}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyBytesBinaryIECSuffixes[] = { + {"EiB", int64_t(1) << 60}, + {"PiB", int64_t(1) << 50}, + {"TiB", int64_t(1) << 40}, + {"GiB", int64_t(1) << 30}, + {"MiB", int64_t(1) << 20}, + {"KiB", int64_t(1) << 10}, + {"B ", 0L}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyUnitsMetricSuffixes[] = { + {"qntl", 1e18L}, + {"qdrl", 1e15L}, + {"tril", 1e12L}, + {"bil", 1e9L}, + {"M", 1e6L}, + {"k", 1e3L}, + {" ", 0}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyUnitsBinarySuffixes[] = { + {"E", int64_t(1) << 60}, + {"P", int64_t(1) << 50}, + {"T", int64_t(1) << 40}, + {"G", int64_t(1) << 30}, + {"M", int64_t(1) << 20}, + {"k", int64_t(1) << 10}, + {" ", 0}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettyUnitsBinaryIECSuffixes[] = { + {"Ei", int64_t(1) << 60}, + {"Pi", int64_t(1) << 50}, + {"Ti", int64_t(1) << 40}, + {"Gi", int64_t(1) << 30}, + {"Mi", int64_t(1) << 20}, + {"Ki", int64_t(1) << 10}, + {" ", 0}, + {nullptr, 0}, +}; + +const PrettySuffix kPrettySISuffixes[] = { + {"Y", 1e24L}, {"Z", 1e21L}, {"E", 1e18L}, {"P", 1e15L}, {"T", 1e12L}, + {"G", 1e9L}, {"M", 1e6L}, {"k", 1e3L}, {"h", 1e2L}, {"da", 1e1L}, + {"d", 1e-1L}, {"c", 1e-2L}, {"m", 1e-3L}, {"u", 1e-6L}, {"n", 1e-9L}, + {"p", 1e-12L}, {"f", 1e-15L}, {"a", 1e-18L}, {"z", 1e-21L}, {"y", 1e-24L}, + {" ", 0}, {nullptr, 0}, +}; + +const PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = { + kPrettyTimeSuffixes, + kPrettyTimeHmsSuffixes, + kPrettyBytesMetricSuffixes, + kPrettyBytesBinarySuffixes, + kPrettyBytesBinaryIECSuffixes, + kPrettyUnitsMetricSuffixes, + kPrettyUnitsBinarySuffixes, + kPrettyUnitsBinaryIECSuffixes, + kPrettySISuffixes, +}; + +} // namespace + +std::string prettyPrint(double val, PrettyType type, bool addSpace) { + char buf[100]; + + // pick the suffixes to use + assert(type >= 0); + assert(type < PRETTY_NUM_TYPES); + const PrettySuffix* suffixes = kPrettySuffixes[type]; + + // find the first suffix we're bigger than -- then use it + double abs_val = fabs(val); + for (int i = 0; suffixes[i].suffix; ++i) { + if (abs_val >= suffixes[i].val) { + snprintf( + buf, + sizeof buf, + "%.4g%s%s", + (suffixes[i].val ? (val / suffixes[i].val) : val), + (addSpace ? " " : ""), + suffixes[i].suffix); + return std::string(buf); + } + } + + // no suffix, we've got a tiny value -- just print it in sci-notation + snprintf(buf, sizeof buf, "%.4g", val); + return std::string(buf); +} + +// TODO: +// 1) Benchmark & optimize +double prettyToDouble( + folly::StringPiece* const prettyString, + const PrettyType type) { + auto value = folly::to<double>(prettyString); + while (!prettyString->empty() && std::isspace(prettyString->front())) { + prettyString->advance(1); // Skipping spaces between number and suffix + } + const PrettySuffix* suffixes = kPrettySuffixes[type]; + int longestPrefixLen = -1; + int bestPrefixId = -1; + for (int j = 0; suffixes[j].suffix; ++j) { + if (suffixes[j].suffix[0] == ' ') { // Checking for " " -> number rule. + if (longestPrefixLen == -1) { + longestPrefixLen = 0; // No characters to skip + bestPrefixId = j; + } + } else if (prettyString->startsWith(suffixes[j].suffix)) { + int suffixLen = int(strlen(suffixes[j].suffix)); + // We are looking for a longest suffix matching prefix of the string + // after numeric value. We need this in case suffixes have common prefix. + if (suffixLen > longestPrefixLen) { + longestPrefixLen = suffixLen; + bestPrefixId = j; + } + } + } + if (bestPrefixId == -1) { // No valid suffix rule found + throw std::invalid_argument(folly::to<std::string>( + "Unable to parse suffix \"", *prettyString, "\"")); + } + prettyString->advance(size_t(longestPrefixLen)); + return suffixes[bestPrefixId].val ? value * suffixes[bestPrefixId].val + : value; +} + +double prettyToDouble(folly::StringPiece prettyString, const PrettyType type) { + double result = prettyToDouble(&prettyString, type); + detail::enforceWhitespace(prettyString); + return result; +} + +std::string hexDump(const void* ptr, size_t size) { + std::ostringstream os; + hexDump(ptr, size, std::ostream_iterator<StringPiece>(os, "\n")); + return os.str(); +} + +// There are two variants of `strerror_r` function, one returns +// `int`, and another returns `char*`. Selecting proper version using +// preprocessor macros portably is extremely hard. +// +// For example, on Android function signature depends on `__USE_GNU` and +// `__ANDROID_API__` macros (https://git.io/fjBBE). +// +// So we are using C++ overloading trick: we pass a pointer of +// `strerror_r` to `invoke_strerror_r` function, and C++ compiler +// selects proper function. + +FOLLY_MAYBE_UNUSED +static std::string invoke_strerror_r( + int (*strerror_r)(int, char*, size_t), + int err, + char* buf, + size_t buflen) { + // Using XSI-compatible strerror_r + int r = strerror_r(err, buf, buflen); + + // OSX/FreeBSD use EINVAL and Linux uses -1 so just check for non-zero + if (r != 0) { + return to<std::string>( + "Unknown error ", err, " (strerror_r failed with error ", errno, ")"); + } else { + return buf; + } +} + +FOLLY_MAYBE_UNUSED +static std::string invoke_strerror_r( + char* (*strerror_r)(int, char*, size_t), + int err, + char* buf, + size_t buflen) { + // Using GNU strerror_r + return strerror_r(err, buf, buflen); +} + +std::string errnoStr(int err) { + int savedErrno = errno; + + // Ensure that we reset errno upon exit. + auto guard(makeGuard([&] { errno = savedErrno; })); + + char buf[1024]; + buf[0] = '\0'; + + std::string result; + + // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/strerror_r.3.html + // http://www.kernel.org/doc/man-pages/online/pages/man3/strerror.3.html +#if defined(_WIN32) && (defined(__MINGW32__) || defined(_MSC_VER)) + // mingw64 has no strerror_r, but Windows has strerror_s, which C11 added + // as well. So maybe we should use this across all platforms (together + // with strerrorlen_s). Note strerror_r and _s have swapped args. + int r = strerror_s(buf, sizeof(buf), err); + if (r != 0) { + result = to<std::string>( + "Unknown error ", err, " (strerror_r failed with error ", errno, ")"); + } else { + result.assign(buf); + } +#else + // Using any strerror_r + result.assign(invoke_strerror_r(strerror_r, err, buf, sizeof(buf))); +#endif + + return result; +} + +namespace { + +void toLowerAscii8(char& c) { + // Branchless tolower, based on the input-rotating trick described + // at http://www.azillionmonkeys.com/qed/asmexample.html + // + // This algorithm depends on an observation: each uppercase + // ASCII character can be converted to its lowercase equivalent + // by adding 0x20. + + // Step 1: Clear the high order bit. We'll deal with it in Step 5. + auto rotated = uint8_t(c & 0x7f); + // Currently, the value of rotated, as a function of the original c is: + // below 'A': 0- 64 + // 'A'-'Z': 65- 90 + // above 'Z': 91-127 + + // Step 2: Add 0x25 (37) + rotated += 0x25; + // Now the value of rotated, as a function of the original c is: + // below 'A': 37-101 + // 'A'-'Z': 102-127 + // above 'Z': 128-164 + + // Step 3: clear the high order bit + rotated &= 0x7f; + // below 'A': 37-101 + // 'A'-'Z': 102-127 + // above 'Z': 0- 36 + + // Step 4: Add 0x1a (26) + rotated += 0x1a; + // below 'A': 63-127 + // 'A'-'Z': 128-153 + // above 'Z': 25- 62 + + // At this point, note that only the uppercase letters have been + // transformed into values with the high order bit set (128 and above). + + // Step 5: Shift the high order bit 2 spaces to the right: the spot + // where the only 1 bit in 0x20 is. But first, how we ignored the + // high order bit of the original c in step 1? If that bit was set, + // we may have just gotten a false match on a value in the range + // 128+'A' to 128+'Z'. To correct this, need to clear the high order + // bit of rotated if the high order bit of c is set. Since we don't + // care about the other bits in rotated, the easiest thing to do + // is invert all the bits in c and bitwise-and them with rotated. + rotated &= ~c; + rotated >>= 2; + + // Step 6: Apply a mask to clear everything except the 0x20 bit + // in rotated. + rotated &= 0x20; + + // At this point, rotated is 0x20 if c is 'A'-'Z' and 0x00 otherwise + + // Step 7: Add rotated to c + c += char(rotated); +} + +void toLowerAscii32(uint32_t& c) { + // Besides being branchless, the algorithm in toLowerAscii8() has another + // interesting property: None of the addition operations will cause + // an overflow in the 8-bit value. So we can pack four 8-bit values + // into a uint32_t and run each operation on all four values in parallel + // without having to use any CPU-specific SIMD instructions. + uint32_t rotated = c & uint32_t(0x7f7f7f7fL); + rotated += uint32_t(0x25252525L); + rotated &= uint32_t(0x7f7f7f7fL); + rotated += uint32_t(0x1a1a1a1aL); + + // Step 5 involves a shift, so some bits will spill over from each + // 8-bit value into the next. But that's okay, because they're bits + // that will be cleared by the mask in step 6 anyway. + rotated &= ~c; + rotated >>= 2; + rotated &= uint32_t(0x20202020L); + c += rotated; +} + +void toLowerAscii64(uint64_t& c) { + // 64-bit version of toLower32 + uint64_t rotated = c & uint64_t(0x7f7f7f7f7f7f7f7fL); + rotated += uint64_t(0x2525252525252525L); + rotated &= uint64_t(0x7f7f7f7f7f7f7f7fL); + rotated += uint64_t(0x1a1a1a1a1a1a1a1aL); + rotated &= ~c; + rotated >>= 2; + rotated &= uint64_t(0x2020202020202020L); + c += rotated; +} + +} // namespace + +void toLowerAscii(char* str, size_t length) { + static const size_t kAlignMask64 = 7; + static const size_t kAlignMask32 = 3; + + // Convert a character at a time until we reach an address that + // is at least 32-bit aligned + auto n = (size_t)str; + n &= kAlignMask32; + n = std::min(n, length); + size_t offset = 0; + if (n != 0) { + n = std::min(4 - n, length); + do { + toLowerAscii8(str[offset]); + offset++; + } while (offset < n); + } + + n = (size_t)(str + offset); + n &= kAlignMask64; + if ((n != 0) && (offset + 4 <= length)) { + // The next address is 32-bit aligned but not 64-bit aligned. + // Convert the next 4 bytes in order to get to the 64-bit aligned + // part of the input. + toLowerAscii32(*(uint32_t*)(str + offset)); + offset += 4; + } + + // Convert 8 characters at a time + while (offset + 8 <= length) { + toLowerAscii64(*(uint64_t*)(str + offset)); + offset += 8; + } + + // Convert 4 characters at a time + while (offset + 4 <= length) { + toLowerAscii32(*(uint32_t*)(str + offset)); + offset += 4; + } + + // Convert any characters remaining after the last 4-byte aligned group + while (offset < length) { + toLowerAscii8(str[offset]); + offset++; + } +} + +namespace detail { + +size_t +hexDumpLine(const void* ptr, size_t offset, size_t size, std::string& line) { + static char hexValues[] = "0123456789abcdef"; + // Line layout: + // 8: address + // 1: space + // (1+2)*16: hex bytes, each preceded by a space + // 1: space separating the two halves + // 3: " |" + // 16: characters + // 1: "|" + // Total: 78 + line.clear(); + line.reserve(78); + const uint8_t* p = reinterpret_cast<const uint8_t*>(ptr) + offset; + size_t n = std::min(size - offset, size_t(16)); + line.push_back(hexValues[(offset >> 28) & 0xf]); + line.push_back(hexValues[(offset >> 24) & 0xf]); + line.push_back(hexValues[(offset >> 20) & 0xf]); + line.push_back(hexValues[(offset >> 16) & 0xf]); + line.push_back(hexValues[(offset >> 12) & 0xf]); + line.push_back(hexValues[(offset >> 8) & 0xf]); + line.push_back(hexValues[(offset >> 4) & 0xf]); + line.push_back(hexValues[offset & 0xf]); + line.push_back(' '); + + for (size_t i = 0; i < n; i++) { + if (i == 8) { + line.push_back(' '); + } + + line.push_back(' '); + line.push_back(hexValues[(p[i] >> 4) & 0xf]); + line.push_back(hexValues[p[i] & 0xf]); + } + + // 3 spaces for each byte we're not printing, one separating the halves + // if necessary + line.append(3 * (16 - n) + (n <= 8), ' '); + line.append(" |"); + + for (size_t i = 0; i < n; i++) { + char c = (p[i] >= 32 && p[i] <= 126 ? static_cast<char>(p[i]) : '.'); + line.push_back(c); + } + line.append(16 - n, ' '); + line.push_back('|'); + DCHECK_EQ(line.size(), 78u); + + return n; +} + +} // namespace detail + +std::string stripLeftMargin(std::string s) { + std::vector<StringPiece> pieces; + split("\n", s, pieces); + auto piecer = range(pieces); + + auto piece = (piecer.end() - 1); + auto needle = std::find_if(piece->begin(), piece->end(), [](char c) { + return c != ' ' && c != '\t'; + }); + if (needle == piece->end()) { + (piecer.end() - 1)->clear(); + } + piece = piecer.begin(); + needle = std::find_if(piece->begin(), piece->end(), [](char c) { + return c != ' ' && c != '\t'; + }); + if (needle == piece->end()) { + piecer.erase(piecer.begin(), piecer.begin() + 1); + } + + const auto sentinel = std::numeric_limits<size_t>::max(); + auto indent = sentinel; + size_t max_length = 0; + for (piece = piecer.begin(); piece != piecer.end(); piece++) { + needle = std::find_if(piece->begin(), piece->end(), [](char c) { + return c != ' ' && c != '\t'; + }); + if (needle != piece->end()) { + indent = std::min<size_t>(indent, size_t(needle - piece->begin())); + } else { + max_length = std::max<size_t>(piece->size(), max_length); + } + } + indent = indent == sentinel ? max_length : indent; + for (piece = piecer.begin(); piece != piecer.end(); piece++) { + if (piece->size() < indent) { + piece->clear(); + } else { + piece->erase(piece->begin(), piece->begin() + indent); + } + } + return join("\n", piecer); +} + +} // namespace folly + +#ifdef FOLLY_DEFINED_DMGL +#undef FOLLY_DEFINED_DMGL +#undef DMGL_NO_OPTS +#undef DMGL_PARAMS +#undef DMGL_ANSI +#undef DMGL_JAVA +#undef DMGL_VERBOSE +#undef DMGL_TYPES +#undef DMGL_RET_POSTFIX +#endif diff --git a/ios/Pods/Flipper-Folly/folly/String.h b/ios/Pods/Flipper-Folly/folly/String.h new file mode 100644 index 000000000..976e2bce4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/String.h @@ -0,0 +1,631 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_STRING_H_ + +#include <cstdarg> +#include <exception> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include <folly/Conv.h> +#include <folly/ExceptionString.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> + +namespace folly { + +/** + * C-Escape a string, making it suitable for representation as a C string + * literal. Appends the result to the output string. + * + * Backslashes all occurrences of backslash and double-quote: + * " -> \" + * \ -> \\ + * + * Replaces all non-printable ASCII characters with backslash-octal + * representation: + * <ASCII 254> -> \376 + * + * Note that we use backslash-octal instead of backslash-hex because the octal + * representation is guaranteed to consume no more than 3 characters; "\3760" + * represents two characters, one with value 254, and one with value 48 ('0'), + * whereas "\xfe0" represents only one character (with value 4064, which leads + * to implementation-defined behavior). + */ +template <class String> +void cEscape(StringPiece str, String& out); + +/** + * Similar to cEscape above, but returns the escaped string. + */ +template <class String> +String cEscape(StringPiece str) { + String out; + cEscape(str, out); + return out; +} + +/** + * C-Unescape a string; the opposite of cEscape above. Appends the result + * to the output string. + * + * Recognizes the standard C escape sequences: + * + * \' \" \? \\ \a \b \f \n \r \t \v + * \[0-7]+ + * \x[0-9a-fA-F]+ + * + * In strict mode (default), throws std::invalid_argument if it encounters + * an unrecognized escape sequence. In non-strict mode, it leaves + * the escape sequence unchanged. + */ +template <class String> +void cUnescape(StringPiece str, String& out, bool strict = true); + +/** + * Similar to cUnescape above, but returns the escaped string. + */ +template <class String> +String cUnescape(StringPiece str, bool strict = true) { + String out; + cUnescape(str, out, strict); + return out; +} + +/** + * URI-escape a string. Appends the result to the output string. + * + * Alphanumeric characters and other characters marked as "unreserved" in RFC + * 3986 ( -_.~ ) are left unchanged. In PATH mode, the forward slash (/) is + * also left unchanged. In QUERY mode, spaces are replaced by '+'. All other + * characters are percent-encoded. + */ +enum class UriEscapeMode : unsigned char { + // The values are meaningful, see generate_escape_tables.py + ALL = 0, + QUERY = 1, + PATH = 2 +}; +template <class String> +void uriEscape( + StringPiece str, + String& out, + UriEscapeMode mode = UriEscapeMode::ALL); + +/** + * Similar to uriEscape above, but returns the escaped string. + */ +template <class String> +String uriEscape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) { + String out; + uriEscape(str, out, mode); + return out; +} + +/** + * URI-unescape a string. Appends the result to the output string. + * + * In QUERY mode, '+' are replaced by space. %XX sequences are decoded if + * XX is a valid hex sequence, otherwise we throw invalid_argument. + */ +template <class String> +void uriUnescape( + StringPiece str, + String& out, + UriEscapeMode mode = UriEscapeMode::ALL); + +/** + * Similar to uriUnescape above, but returns the unescaped string. + */ +template <class String> +String uriUnescape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) { + String out; + uriUnescape(str, out, mode); + return out; +} + +/** + * stringPrintf is much like printf but deposits its result into a + * string. Two signatures are supported: the first simply returns the + * resulting string, and the second appends the produced characters to + * the specified string and returns a reference to it. + */ +std::string stringPrintf(FOLLY_PRINTF_FORMAT const char* format, ...) + FOLLY_PRINTF_FORMAT_ATTR(1, 2); + +/* Similar to stringPrintf, with different signature. */ +void stringPrintf(std::string* out, FOLLY_PRINTF_FORMAT const char* format, ...) + FOLLY_PRINTF_FORMAT_ATTR(2, 3); + +std::string& stringAppendf( + std::string* output, + FOLLY_PRINTF_FORMAT const char* format, + ...) FOLLY_PRINTF_FORMAT_ATTR(2, 3); + +/** + * Similar to stringPrintf, but accepts a va_list argument. + * + * As with vsnprintf() itself, the value of ap is undefined after the call. + * These functions do not call va_end() on ap. + */ +std::string stringVPrintf(const char* format, va_list ap); +void stringVPrintf(std::string* out, const char* format, va_list ap); +std::string& stringVAppendf(std::string* out, const char* format, va_list ap); + +/** + * Backslashify a string, that is, replace non-printable characters + * with C-style (but NOT C compliant) "\xHH" encoding. If hex_style + * is false, then shorthand notations like "\0" will be used instead + * of "\x00" for the most common backslash cases. + * + * There are two forms, one returning the input string, and one + * creating output in the specified output string. + * + * This is mainly intended for printing to a terminal, so it is not + * particularly optimized. + * + * Do *not* use this in situations where you expect to be able to feed + * the string to a C or C++ compiler, as there are nuances with how C + * parses such strings that lead to failures. This is for display + * purposed only. If you want a string you can embed for use in C or + * C++, use cEscape instead. This function is for display purposes + * only. + */ +template <class OutputString> +void backslashify( + folly::StringPiece input, + OutputString& output, + bool hex_style = false); + +template <class OutputString = std::string> +OutputString backslashify(StringPiece input, bool hex_style = false) { + OutputString output; + backslashify(input, output, hex_style); + return output; +} + +/** + * Take a string and "humanify" it -- that is, make it look better. + * Since "better" is subjective, caveat emptor. The basic approach is + * to count the number of unprintable characters. If there are none, + * then the output is the input. If there are relatively few, or if + * there is a long "enough" prefix of printable characters, use + * backslashify. If it is mostly binary, then simply hex encode. + * + * This is an attempt to make a computer smart, and so likely is wrong + * most of the time. + */ +template <class String1, class String2> +void humanify(const String1& input, String2& output); + +template <class String> +String humanify(const String& input) { + String output; + humanify(input, output); + return output; +} + +/** + * Same functionality as Python's binascii.hexlify. Returns true + * on successful conversion. + * + * If append_output is true, append data to the output rather than + * replace it. + */ +template <class InputString, class OutputString> +bool hexlify( + const InputString& input, + OutputString& output, + bool append = false); + +template <class OutputString = std::string> +OutputString hexlify(ByteRange input) { + OutputString output; + if (!hexlify(input, output)) { + // hexlify() currently always returns true, so this can't really happen + throw_exception<std::runtime_error>("hexlify failed"); + } + return output; +} + +template <class OutputString = std::string> +OutputString hexlify(StringPiece input) { + return hexlify<OutputString>(ByteRange{input}); +} + +/** + * Same functionality as Python's binascii.unhexlify. Returns true + * on successful conversion. + */ +template <class InputString, class OutputString> +bool unhexlify(const InputString& input, OutputString& output); + +template <class OutputString = std::string> +OutputString unhexlify(StringPiece input) { + OutputString output; + if (!unhexlify(input, output)) { + // unhexlify() fails if the input has non-hexidecimal characters, + // or if it doesn't consist of a whole number of bytes + throw_exception<std::domain_error>("unhexlify() called with non-hex input"); + } + return output; +} + +/** + * A pretty-printer for numbers that appends suffixes of units of the + * given type. It prints 4 sig-figs of value with the most + * appropriate unit. + * + * If `addSpace' is true, we put a space between the units suffix and + * the value. + * + * Current types are: + * PRETTY_TIME - s, ms, us, ns, etc. + * PRETTY_TIME_HMS - h, m, s, ms, us, ns, etc. + * PRETTY_BYTES_METRIC - kB, MB, GB, etc (goes up by 10^3 = 1000 each time) + * PRETTY_BYTES - kB, MB, GB, etc (goes up by 2^10 = 1024 each time) + * PRETTY_BYTES_IEC - KiB, MiB, GiB, etc + * PRETTY_UNITS_METRIC - k, M, G, etc (goes up by 10^3 = 1000 each time) + * PRETTY_UNITS_BINARY - k, M, G, etc (goes up by 2^10 = 1024 each time) + * PRETTY_UNITS_BINARY_IEC - Ki, Mi, Gi, etc + * PRETTY_SI - full SI metric prefixes from yocto to Yotta + * http://en.wikipedia.org/wiki/Metric_prefix + * + * @author Mark Rabkin <mrabkin@fb.com> + */ +enum PrettyType { + PRETTY_TIME, + PRETTY_TIME_HMS, + + PRETTY_BYTES_METRIC, + PRETTY_BYTES_BINARY, + PRETTY_BYTES = PRETTY_BYTES_BINARY, + PRETTY_BYTES_BINARY_IEC, + PRETTY_BYTES_IEC = PRETTY_BYTES_BINARY_IEC, + + PRETTY_UNITS_METRIC, + PRETTY_UNITS_BINARY, + PRETTY_UNITS_BINARY_IEC, + + PRETTY_SI, + PRETTY_NUM_TYPES, +}; + +std::string prettyPrint(double val, PrettyType, bool addSpace = true); + +/** + * This utility converts StringPiece in pretty format (look above) to double, + * with progress information. Alters the StringPiece parameter + * to get rid of the already-parsed characters. + * Expects string in form <floating point number> {space}* [<suffix>] + * If string is not in correct format, utility finds longest valid prefix and + * if there at least one, returns double value based on that prefix and + * modifies string to what is left after parsing. Throws and std::range_error + * exception if there is no correct parse. + * Examples(for PRETTY_UNITS_METRIC): + * '10M' => 10 000 000 + * '10 M' => 10 000 000 + * '10' => 10 + * '10 Mx' => 10 000 000, prettyString == "x" + * 'abc' => throws std::range_error + */ +double prettyToDouble( + folly::StringPiece* const prettyString, + const PrettyType type); + +/** + * Same as prettyToDouble(folly::StringPiece*, PrettyType), but + * expects whole string to be correctly parseable. Throws std::range_error + * otherwise + */ +double prettyToDouble(folly::StringPiece prettyString, const PrettyType type); + +/** + * Write a hex dump of size bytes starting at ptr to out. + * + * The hex dump is formatted as follows: + * + * for the string "abcdefghijklmnopqrstuvwxyz\x02" +00000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 |abcdefghijklmnop| +00000010 71 72 73 74 75 76 77 78 79 7a 02 |qrstuvwxyz. | + * + * that is, we write 16 bytes per line, both as hex bytes and as printable + * characters. Non-printable characters are replaced with '.' + * Lines are written to out one by one (one StringPiece at a time) without + * delimiters. + */ +template <class OutIt> +void hexDump(const void* ptr, size_t size, OutIt out); + +/** + * Return the hex dump of size bytes starting at ptr as a string. + */ +std::string hexDump(const void* ptr, size_t size); + +/** + * Return a string containing the description of the given errno value. + * Takes care not to overwrite the actual system errno, so calling + * errnoStr(errno) is valid. + */ +std::string errnoStr(int err); + +/* + * Split a string into a list of tokens by delimiter. + * + * The split interface here supports different output types, selected + * at compile time: StringPiece, fbstring, or std::string. If you are + * using a vector to hold the output, it detects the type based on + * what your vector contains. If the output vector is not empty, split + * will append to the end of the vector. + * + * You can also use splitTo() to write the output to an arbitrary + * OutputIterator (e.g. std::inserter() on a std::set<>), in which + * case you have to tell the function the type. (Rationale: + * OutputIterators don't have a value_type, so we can't detect the + * type in splitTo without being told.) + * + * Examples: + * + * std::vector<folly::StringPiece> v; + * folly::split(":", "asd:bsd", v); + * + * std::set<StringPiece> s; + * folly::splitTo<StringPiece>(":", "asd:bsd:asd:csd", + * std::inserter(s, s.begin())); + * + * Split also takes a flag (ignoreEmpty) that indicates whether adjacent + * delimiters should be treated as one single separator (ignoring empty tokens) + * or not (generating empty tokens). + */ + +template <class Delim, class String, class OutputType> +void split( + const Delim& delimiter, + const String& input, + std::vector<OutputType>& out, + const bool ignoreEmpty = false); + +template <class T, class Allocator> +class fbvector; + +template <class Delim, class String, class OutputType> +void split( + const Delim& delimiter, + const String& input, + folly::fbvector<OutputType, std::allocator<OutputType>>& out, + const bool ignoreEmpty = false); + +template < + class OutputValueType, + class Delim, + class String, + class OutputIterator> +void splitTo( + const Delim& delimiter, + const String& input, + OutputIterator out, + const bool ignoreEmpty = false); + +/* + * Split a string into a fixed number of string pieces and/or numeric types + * by delimiter. Conversions are supported for any type which folly:to<> can + * target, including all overloads of parseTo(). Returns 'true' if the fields + * were all successfully populated. Returns 'false' if there were too few + * fields in the input, or too many fields if exact=true. Casting exceptions + * will not be caught. + * + * Examples: + * + * folly::StringPiece name, key, value; + * if (folly::split('\t', line, name, key, value)) + * ... + * + * folly::StringPiece name; + * double value; + * int id; + * if (folly::split('\t', line, name, value, id)) + * ... + * + * The 'exact' template parameter specifies how the function behaves when too + * many fields are present in the input string. When 'exact' is set to its + * default value of 'true', a call to split will fail if the number of fields in + * the input string does not exactly match the number of output parameters + * passed. If 'exact' is overridden to 'false', all remaining fields will be + * stored, unsplit, in the last field, as shown below: + * + * folly::StringPiece x, y. + * if (folly::split<false>(':', "a:b:c", x, y)) + * assert(x == "a" && y == "b:c"); + * + * Note that this will likely not work if the last field's target is of numeric + * type, in which case folly::to<> will throw an exception. + */ +namespace detail { +template <typename Void, typename OutputType> +struct IsConvertible : std::false_type {}; + +template <> +struct IsConvertible<void, decltype(std::ignore)> : std::true_type {}; + +template <typename OutputType> +struct IsConvertible< + void_t<decltype(parseTo(StringPiece{}, std::declval<OutputType&>()))>, + OutputType> : std::true_type {}; +} // namespace detail +template <typename OutputType> +struct IsConvertible : detail::IsConvertible<void, OutputType> {}; + +template <bool exact = true, class Delim, class... OutputTypes> +typename std::enable_if< + StrictConjunction<IsConvertible<OutputTypes>...>::value && + sizeof...(OutputTypes) >= 1, + bool>::type +split(const Delim& delimiter, StringPiece input, OutputTypes&... outputs); + +/* + * Join list of tokens. + * + * Stores a string representation of tokens in the same order with + * deliminer between each element. + */ + +template <class Delim, class Iterator, class String> +void join(const Delim& delimiter, Iterator begin, Iterator end, String& output); + +template <class Delim, class Container, class String> +void join(const Delim& delimiter, const Container& container, String& output) { + join(delimiter, container.begin(), container.end(), output); +} + +template <class Delim, class Value, class String> +void join( + const Delim& delimiter, + const std::initializer_list<Value>& values, + String& output) { + join(delimiter, values.begin(), values.end(), output); +} + +template <class Delim, class Container> +std::string join(const Delim& delimiter, const Container& container) { + std::string output; + join(delimiter, container.begin(), container.end(), output); + return output; +} + +template <class Delim, class Value> +std::string join( + const Delim& delimiter, + const std::initializer_list<Value>& values) { + std::string output; + join(delimiter, values.begin(), values.end(), output); + return output; +} + +template < + class Delim, + class Iterator, + typename std::enable_if<std::is_base_of< + std::forward_iterator_tag, + typename std::iterator_traits<Iterator>::iterator_category>::value>:: + type* = nullptr> +std::string join(const Delim& delimiter, Iterator begin, Iterator end) { + std::string output; + join(delimiter, begin, end, output); + return output; +} + +/** + * Returns a subpiece with all whitespace removed from the front of @sp. + * Whitespace means any of [' ', '\n', '\r', '\t']. + */ +StringPiece ltrimWhitespace(StringPiece sp); + +/** + * Returns a subpiece with all whitespace removed from the back of @sp. + * Whitespace means any of [' ', '\n', '\r', '\t']. + */ +StringPiece rtrimWhitespace(StringPiece sp); + +/** + * Returns a subpiece with all whitespace removed from the back and front of + * @sp. Whitespace means any of [' ', '\n', '\r', '\t']. + */ +inline StringPiece trimWhitespace(StringPiece sp) { + return ltrimWhitespace(rtrimWhitespace(sp)); +} + +/** + * Returns a subpiece with all whitespace removed from the front of @sp. + * Whitespace means any of [' ', '\n', '\r', '\t']. + * DEPRECATED: @see ltrimWhitespace @see rtrimWhitespace + */ +inline StringPiece skipWhitespace(StringPiece sp) { + return ltrimWhitespace(sp); +} + +/** + * Returns a subpiece with all characters the provided @toTrim returns true + * for removed from the front of @sp. + */ +template <typename ToTrim> +StringPiece ltrim(StringPiece sp, ToTrim toTrim) { + while (!sp.empty() && toTrim(sp.front())) { + sp.pop_front(); + } + + return sp; +} + +/** + * Returns a subpiece with all characters the provided @toTrim returns true + * for removed from the back of @sp. + */ +template <typename ToTrim> +StringPiece rtrim(StringPiece sp, ToTrim toTrim) { + while (!sp.empty() && toTrim(sp.back())) { + sp.pop_back(); + } + + return sp; +} + +/** + * Returns a subpiece with all characters the provided @toTrim returns true + * for removed from the back and front of @sp. + */ +template <typename ToTrim> +StringPiece trim(StringPiece sp, ToTrim toTrim) { + return ltrim(rtrim(sp, std::ref(toTrim)), std::ref(toTrim)); +} + +/** + * Strips the leading and the trailing whitespace-only lines. Then looks for + * the least indented non-whitespace-only line and removes its amount of + * leading whitespace from every line. Assumes leading whitespace is either all + * spaces or all tabs. + * + * Purpose: including a multiline string literal in source code, indented to + * the level expected from context. + */ +std::string stripLeftMargin(std::string s); + +/** + * Fast, in-place lowercasing of ASCII alphabetic characters in strings. + * Leaves all other characters unchanged, including those with the 0x80 + * bit set. + * @param str String to convert + * @param length Length of str, in bytes + */ +void toLowerAscii(char* str, size_t length); + +inline void toLowerAscii(MutableStringPiece str) { + toLowerAscii(str.begin(), str.size()); +} + +inline void toLowerAscii(std::string& str) { + // str[0] is legal also if the string is empty. + toLowerAscii(&str[0], str.size()); +} + +} // namespace folly + +#include <folly/String-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/Subprocess.cpp b/ios/Pods/Flipper-Folly/folly/Subprocess.cpp new file mode 100644 index 000000000..8b3f4cf76 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Subprocess.cpp @@ -0,0 +1,1016 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <folly/Subprocess.h> + +#if defined(__linux__) +#include <sys/prctl.h> +#endif +#include <fcntl.h> + +#include <algorithm> +#include <array> +#include <system_error> +#include <thread> + +#include <boost/container/flat_set.hpp> +#include <boost/range/adaptors.hpp> + +#include <glog/logging.h> + +#include <folly/Conv.h> +#include <folly/Exception.h> +#include <folly/ScopeGuard.h> +#include <folly/String.h> +#include <folly/io/Cursor.h> +#include <folly/lang/Assume.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Stdlib.h> +#include <folly/portability/SysSyscall.h> +#include <folly/portability/Unistd.h> +#include <folly/system/Shell.h> + +constexpr int kExecFailure = 127; +constexpr int kChildFailure = 126; + +namespace folly { + +ProcessReturnCode ProcessReturnCode::make(int status) { + if (!WIFEXITED(status) && !WIFSIGNALED(status)) { + throw std::runtime_error( + to<std::string>("Invalid ProcessReturnCode: ", status)); + } + return ProcessReturnCode(status); +} + +ProcessReturnCode::ProcessReturnCode(ProcessReturnCode&& p) noexcept + : rawStatus_(p.rawStatus_) { + p.rawStatus_ = ProcessReturnCode::RV_NOT_STARTED; +} + +ProcessReturnCode& ProcessReturnCode::operator=( + ProcessReturnCode&& p) noexcept { + rawStatus_ = p.rawStatus_; + p.rawStatus_ = ProcessReturnCode::RV_NOT_STARTED; + return *this; +} + +ProcessReturnCode::State ProcessReturnCode::state() const { + if (rawStatus_ == RV_NOT_STARTED) { + return NOT_STARTED; + } + if (rawStatus_ == RV_RUNNING) { + return RUNNING; + } + if (WIFEXITED(rawStatus_)) { + return EXITED; + } + if (WIFSIGNALED(rawStatus_)) { + return KILLED; + } + assume_unreachable(); +} + +void ProcessReturnCode::enforce(State expected) const { + State s = state(); + if (s != expected) { + throw std::logic_error(to<std::string>( + "Bad use of ProcessReturnCode; state is ", s, " expected ", expected)); + } +} + +int ProcessReturnCode::exitStatus() const { + enforce(EXITED); + return WEXITSTATUS(rawStatus_); +} + +int ProcessReturnCode::killSignal() const { + enforce(KILLED); + return WTERMSIG(rawStatus_); +} + +bool ProcessReturnCode::coreDumped() const { + enforce(KILLED); + return WCOREDUMP(rawStatus_); +} + +std::string ProcessReturnCode::str() const { + switch (state()) { + case NOT_STARTED: + return "not started"; + case RUNNING: + return "running"; + case EXITED: + return to<std::string>("exited with status ", exitStatus()); + case KILLED: + return to<std::string>( + "killed by signal ", + killSignal(), + (coreDumped() ? " (core dumped)" : "")); + } + assume_unreachable(); +} + +CalledProcessError::CalledProcessError(ProcessReturnCode rc) + : SubprocessError(rc.str()), returnCode_(rc) {} + +static inline std::string toSubprocessSpawnErrorMessage( + char const* executable, + int errCode, + int errnoValue) { + auto prefix = errCode == kExecFailure ? "failed to execute " + : "error preparing to execute "; + return to<std::string>(prefix, executable, ": ", errnoStr(errnoValue)); +} + +SubprocessSpawnError::SubprocessSpawnError( + const char* executable, + int errCode, + int errnoValue) + : SubprocessError( + toSubprocessSpawnErrorMessage(executable, errCode, errnoValue)), + errnoValue_(errnoValue) {} + +namespace { + +// Copy pointers to the given strings in a format suitable for posix_spawn +std::unique_ptr<const char* []> cloneStrings( + const std::vector<std::string>& s) { + std::unique_ptr<const char*[]> d(new const char*[s.size() + 1]); + for (size_t i = 0; i < s.size(); i++) { + d[i] = s[i].c_str(); + } + d[s.size()] = nullptr; + return d; +} + +// Check a wait() status, throw on non-successful +void checkStatus(ProcessReturnCode returnCode) { + if (returnCode.state() != ProcessReturnCode::EXITED || + returnCode.exitStatus() != 0) { + throw CalledProcessError(returnCode); + } +} + +} // namespace + +Subprocess::Options& Subprocess::Options::fd(int fd, int action) { + if (action == Subprocess::PIPE) { + if (fd == 0) { + action = Subprocess::PIPE_IN; + } else if (fd == 1 || fd == 2) { + action = Subprocess::PIPE_OUT; + } else { + throw std::invalid_argument( + to<std::string>("Only fds 0, 1, 2 are valid for action=PIPE: ", fd)); + } + } + fdActions_[fd] = action; + return *this; +} + +Subprocess::Subprocess() = default; + +Subprocess::Subprocess( + const std::vector<std::string>& argv, + const Options& options, + const char* executable, + const std::vector<std::string>* env) { + if (argv.empty()) { + throw std::invalid_argument("argv must not be empty"); + } + if (!executable) { + executable = argv[0].c_str(); + } + spawn(cloneStrings(argv), executable, options, env); +} + +Subprocess::Subprocess( + const std::string& cmd, + const Options& options, + const std::vector<std::string>* env) { + if (options.usePath_) { + throw std::invalid_argument("usePath() not allowed when running in shell"); + } + + std::vector<std::string> argv = {"/bin/sh", "-c", cmd}; + spawn(cloneStrings(argv), argv[0].c_str(), options, env); +} + +Subprocess Subprocess::fromExistingProcess(pid_t pid) { + Subprocess sp; + sp.pid_ = pid; + sp.returnCode_ = ProcessReturnCode::makeRunning(); + return sp; +} + +Subprocess::~Subprocess() { + CHECK_NE(returnCode_.state(), ProcessReturnCode::RUNNING) + << "Subprocess destroyed without reaping child"; +} + +namespace { + +struct ChildErrorInfo { + int errCode; + int errnoValue; +}; + +[[noreturn]] void childError(int errFd, int errCode, int errnoValue) { + ChildErrorInfo info = {errCode, errnoValue}; + // Write the error information over the pipe to our parent process. + // We can't really do anything else if this write call fails. + writeNoInt(errFd, &info, sizeof(info)); + // exit + _exit(errCode); +} + +} // namespace + +void Subprocess::setAllNonBlocking() { + for (auto& p : pipes_) { + int fd = p.pipe.fd(); + int flags = ::fcntl(fd, F_GETFL); + checkUnixError(flags, "fcntl"); + int r = ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); + checkUnixError(r, "fcntl"); + } +} + +void Subprocess::spawn( + std::unique_ptr<const char*[]> argv, + const char* executable, + const Options& optionsIn, + const std::vector<std::string>* env) { + if (optionsIn.usePath_ && env) { + throw std::invalid_argument( + "usePath() not allowed when overriding environment"); + } + + // Make a copy, we'll mutate options + Options options(optionsIn); + + // On error, close all pipes_ (ignoring errors, but that seems fine here). + auto pipesGuard = makeGuard([this] { pipes_.clear(); }); + + // Create a pipe to use to receive error information from the child, + // in case it fails before calling exec() + int errFds[2]; +#if FOLLY_HAVE_PIPE2 + checkUnixError(::pipe2(errFds, O_CLOEXEC), "pipe2"); +#else + checkUnixError(::pipe(errFds), "pipe"); +#endif + SCOPE_EXIT { + CHECK_ERR(::close(errFds[0])); + if (errFds[1] >= 0) { + CHECK_ERR(::close(errFds[1])); + } + }; + +#if !FOLLY_HAVE_PIPE2 + // Ask the child to close the read end of the error pipe. + checkUnixError(fcntl(errFds[0], F_SETFD, FD_CLOEXEC), "set FD_CLOEXEC"); + // Set the close-on-exec flag on the write side of the pipe. + // This way the pipe will be closed automatically in the child if execve() + // succeeds. If the exec fails the child can write error information to the + // pipe. + checkUnixError(fcntl(errFds[1], F_SETFD, FD_CLOEXEC), "set FD_CLOEXEC"); +#endif + + // Perform the actual work of setting up pipes then forking and + // executing the child. + spawnInternal(std::move(argv), executable, options, env, errFds[1]); + + // After spawnInternal() returns the child is alive. We have to be very + // careful about throwing after this point. We are inside the constructor, + // so if we throw the Subprocess object will have never existed, and the + // destructor will never be called. + // + // We should only throw if we got an error via the errFd, and we know the + // child has exited and can be immediately waited for. In all other cases, + // we have no way of cleaning up the child. + + // Close writable side of the errFd pipe in the parent process + CHECK_ERR(::close(errFds[1])); + errFds[1] = -1; + + // Read from the errFd pipe, to tell if the child ran into any errors before + // calling exec() + readChildErrorPipe(errFds[0], executable); + + // If we spawned a detached child, wait on the intermediate child process. + // It always exits immediately. + if (options.detach_) { + wait(); + } + + // We have fully succeeded now, so release the guard on pipes_ + pipesGuard.dismiss(); +} + +// With -Wclobbered, gcc complains about vfork potentially cloberring the +// childDir variable, even though we only use it on the child side of the +// vfork. + +FOLLY_PUSH_WARNING +FOLLY_GCC_DISABLE_WARNING("-Wclobbered") +void Subprocess::spawnInternal( + std::unique_ptr<const char*[]> argv, + const char* executable, + Options& options, + const std::vector<std::string>* env, + int errFd) { + // Parent work, pre-fork: create pipes + std::vector<int> childFds; + // Close all of the childFds as we leave this scope + SCOPE_EXIT { + // These are only pipes, closing them shouldn't fail + for (int cfd : childFds) { + CHECK_ERR(::close(cfd)); + } + }; + + int r; + for (auto& p : options.fdActions_) { + if (p.second == PIPE_IN || p.second == PIPE_OUT) { + int fds[2]; + // We're setting both ends of the pipe as close-on-exec. The child + // doesn't need to reset the flag on its end, as we always dup2() the fd, + // and dup2() fds don't share the close-on-exec flag. +#if FOLLY_HAVE_PIPE2 + // If possible, set close-on-exec atomically. Otherwise, a concurrent + // Subprocess invocation can fork() between "pipe" and "fnctl", + // causing FDs to leak. + r = ::pipe2(fds, O_CLOEXEC); + checkUnixError(r, "pipe2"); +#else + r = ::pipe(fds); + checkUnixError(r, "pipe"); + r = fcntl(fds[0], F_SETFD, FD_CLOEXEC); + checkUnixError(r, "set FD_CLOEXEC"); + r = fcntl(fds[1], F_SETFD, FD_CLOEXEC); + checkUnixError(r, "set FD_CLOEXEC"); +#endif + pipes_.emplace_back(); + Pipe& pipe = pipes_.back(); + pipe.direction = p.second; + int cfd; + if (p.second == PIPE_IN) { + // Child gets reading end + pipe.pipe = folly::File(fds[1], /*ownsFd=*/true); + cfd = fds[0]; + } else { + pipe.pipe = folly::File(fds[0], /*ownsFd=*/true); + cfd = fds[1]; + } + p.second = cfd; // ensure it gets dup2()ed + pipe.childFd = p.first; + childFds.push_back(cfd); + } + } + + // This should already be sorted, as options.fdActions_ is + DCHECK(std::is_sorted(pipes_.begin(), pipes_.end())); + + // Note that the const casts below are legit, per + // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html + + auto argVec = const_cast<char**>(argv.get()); + + // Set up environment + std::unique_ptr<const char*[]> envHolder; + char** envVec; + if (env) { + envHolder = cloneStrings(*env); + envVec = const_cast<char**>(envHolder.get()); + } else { + envVec = environ; + } + + // Block all signals around vfork; see http://ewontfix.com/7/. + // + // As the child may run in the same address space as the parent until + // the actual execve() system call, any (custom) signal handlers that + // the parent has might alter parent's memory if invoked in the child, + // with undefined results. So we block all signals in the parent before + // vfork(), which will cause them to be blocked in the child as well (we + // rely on the fact that Linux, just like all sane implementations, only + // clones the calling thread). Then, in the child, we reset all signals + // to their default dispositions (while still blocked), and unblock them + // (so the exec()ed process inherits the parent's signal mask) + // + // The parent also unblocks all signals as soon as vfork() returns. + sigset_t allBlocked; + r = sigfillset(&allBlocked); + checkUnixError(r, "sigfillset"); + sigset_t oldSignals; + + r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals); + checkPosixError(r, "pthread_sigmask"); + SCOPE_EXIT { + // Restore signal mask + r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); + CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail + }; + + // Call c_str() here, as it's not necessarily safe after fork. + const char* childDir = + options.childDir_.empty() ? nullptr : options.childDir_.c_str(); + + pid_t pid; +#ifdef __linux__ + if (options.cloneFlags_) { + pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr); + } else { +#endif + if (options.detach_) { + // If we are detaching we must use fork() instead of vfork() for the first + // fork, since we aren't going to simply call exec() in the child. + pid = fork(); + } else { + pid = vfork(); + } +#ifdef __linux__ + } +#endif + checkUnixError(pid, errno, "failed to fork"); + if (pid == 0) { + // Fork a second time if detach_ was requested. + // This must be done before signals are restored in prepareChild() + if (options.detach_) { +#ifdef __linux__ + if (options.cloneFlags_) { + pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr); + } else { +#endif + pid = vfork(); +#ifdef __linux__ + } +#endif + if (pid == -1) { + // Inform our parent process of the error so it can throw in the parent. + childError(errFd, kChildFailure, errno); + } else if (pid != 0) { + // We are the intermediate process. Exit immediately. + // Our child will still inform the original parent of success/failure + // through errFd. The pid of the grandchild process never gets + // propagated back up to the original parent. In the future we could + // potentially send it back using errFd if we needed to. + _exit(0); + } + } + + int errnoValue = prepareChild(options, &oldSignals, childDir); + if (errnoValue != 0) { + childError(errFd, kChildFailure, errnoValue); + } + + errnoValue = runChild(executable, argVec, envVec, options); + // If we get here, exec() failed. + childError(errFd, kExecFailure, errnoValue); + } + + // Child is alive. We have to be very careful about throwing after this + // point. We are inside the constructor, so if we throw the Subprocess + // object will have never existed, and the destructor will never be called. + // + // We should only throw if we got an error via the errFd, and we know the + // child has exited and can be immediately waited for. In all other cases, + // we have no way of cleaning up the child. + pid_ = pid; + returnCode_ = ProcessReturnCode::makeRunning(); +} +FOLLY_POP_WARNING + +int Subprocess::prepareChild( + const Options& options, + const sigset_t* sigmask, + const char* childDir) const { + // While all signals are blocked, we must reset their + // dispositions to default. + for (int sig = 1; sig < NSIG; ++sig) { + ::signal(sig, SIG_DFL); + } + + { + // Unblock signals; restore signal mask. + int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr); + if (r != 0) { + return r; // pthread_sigmask() returns an errno value + } + } + + // Change the working directory, if one is given + if (childDir) { + if (::chdir(childDir) == -1) { + return errno; + } + } + + // We don't have to explicitly close the parent's end of all pipes, + // as they all have the FD_CLOEXEC flag set and will be closed at + // exec time. + + // Close all fds that we're supposed to close. + for (auto& p : options.fdActions_) { + if (p.second == CLOSE) { + if (::close(p.first) == -1) { + return errno; + } + } else if (p.second != p.first) { + if (::dup2(p.second, p.first) == -1) { + return errno; + } + } + } + + // If requested, close all other file descriptors. Don't close + // any fds in options.fdActions_, and don't touch stdin, stdout, stderr. + // Ignore errors. + if (options.closeOtherFds_) { + for (int fd = getdtablesize() - 1; fd >= 3; --fd) { + if (options.fdActions_.count(fd) == 0) { + ::close(fd); + } + } + } + +#if defined(__linux__) + // Opt to receive signal on parent death, if requested + if (options.parentDeathSignal_ != 0) { + const auto parentDeathSignal = + static_cast<unsigned long>(options.parentDeathSignal_); + if (prctl(PR_SET_PDEATHSIG, parentDeathSignal, 0, 0, 0) == -1) { + return errno; + } + } +#endif + + if (options.processGroupLeader_) { +#if !defined(__FreeBSD__) + if (setpgrp() == -1) { +#else + if (setpgrp(getpid(), getpgrp()) == -1) { +#endif + return errno; + } + } + + // The user callback comes last, so that the child is otherwise all set up. + if (options.dangerousPostForkPreExecCallback_) { + if (int error = (*options.dangerousPostForkPreExecCallback_)()) { + return error; + } + } + + return 0; +} + +int Subprocess::runChild( + const char* executable, + char** argv, + char** env, + const Options& options) const { + // Now, finally, exec. + if (options.usePath_) { + ::execvp(executable, argv); + } else { + ::execve(executable, argv, env); + } + return errno; +} + +void Subprocess::readChildErrorPipe(int pfd, const char* executable) { + ChildErrorInfo info; + auto rc = readNoInt(pfd, &info, sizeof(info)); + if (rc == 0) { + // No data means the child executed successfully, and the pipe + // was closed due to the close-on-exec flag being set. + return; + } else if (rc != sizeof(ChildErrorInfo)) { + // An error occurred trying to read from the pipe, or we got a partial read. + // Neither of these cases should really occur in practice. + // + // We can't get any error data from the child in this case, and we don't + // know if it is successfully running or not. All we can do is to return + // normally, as if the child executed successfully. If something bad + // happened the caller should at least get a non-normal exit status from + // the child. + LOG(ERROR) << "unexpected error trying to read from child error pipe " + << "rc=" << rc << ", errno=" << errno; + return; + } + + // We got error data from the child. The child should exit immediately in + // this case, so wait on it to clean up. + wait(); + + // Throw to signal the error + throw SubprocessSpawnError(executable, info.errCode, info.errnoValue); +} + +ProcessReturnCode Subprocess::poll(struct rusage* ru) { + returnCode_.enforce(ProcessReturnCode::RUNNING); + DCHECK_GT(pid_, 0); + int status; + pid_t found = ::wait4(pid_, &status, WNOHANG, ru); + // The spec guarantees that EINTR does not occur with WNOHANG, so the only + // two remaining errors are ECHILD (other code reaped the child?), or + // EINVAL (cosmic rays?), both of which merit an abort: + PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)"; + if (found != 0) { + // Though the child process had quit, this call does not close the pipes + // since its descendants may still be using them. + returnCode_ = ProcessReturnCode::make(status); + pid_ = -1; + } + return returnCode_; +} + +bool Subprocess::pollChecked() { + if (poll().state() == ProcessReturnCode::RUNNING) { + return false; + } + checkStatus(returnCode_); + return true; +} + +ProcessReturnCode Subprocess::wait() { + returnCode_.enforce(ProcessReturnCode::RUNNING); + DCHECK_GT(pid_, 0); + int status; + pid_t found; + do { + found = ::waitpid(pid_, &status, 0); + } while (found == -1 && errno == EINTR); + // The only two remaining errors are ECHILD (other code reaped the + // child?), or EINVAL (cosmic rays?), and both merit an abort: + PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, 0)"; + // Though the child process had quit, this call does not close the pipes + // since its descendants may still be using them. + DCHECK_EQ(found, pid_); + returnCode_ = ProcessReturnCode::make(status); + pid_ = -1; + return returnCode_; +} + +void Subprocess::waitChecked() { + wait(); + checkStatus(returnCode_); +} + +ProcessReturnCode Subprocess::waitTimeout(TimeoutDuration timeout) { + returnCode_.enforce(ProcessReturnCode::RUNNING); + DCHECK_GT(pid_, 0) << "The subprocess has been waited already"; + + auto pollUntil = std::chrono::steady_clock::now() + timeout; + auto sleepDuration = std::chrono::milliseconds{2}; + constexpr auto maximumSleepDuration = std::chrono::milliseconds{100}; + + for (;;) { + // Always call waitpid once after the full timeout has elapsed. + auto now = std::chrono::steady_clock::now(); + + int status; + pid_t found; + do { + found = ::waitpid(pid_, &status, WNOHANG); + } while (found == -1 && errno == EINTR); + PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)"; + if (found) { + // Just on the safe side, make sure it's the actual pid we are waiting. + DCHECK_EQ(found, pid_); + returnCode_ = ProcessReturnCode::make(status); + // Change pid_ to -1 to detect programming error like calling + // this method multiple times. + pid_ = -1; + return returnCode_; + } + if (now > pollUntil) { + // Timed out: still running(). + return returnCode_; + } + // The subprocess is still running, sleep for increasing periods of time. + std::this_thread::sleep_for(sleepDuration); + sleepDuration = + std::min(maximumSleepDuration, sleepDuration + sleepDuration); + } +} + +void Subprocess::sendSignal(int signal) { + returnCode_.enforce(ProcessReturnCode::RUNNING); + int r = ::kill(pid_, signal); + checkUnixError(r, "kill"); +} + +ProcessReturnCode Subprocess::waitOrTerminateOrKill( + TimeoutDuration waitTimeout, + TimeoutDuration sigtermTimeout) { + returnCode_.enforce(ProcessReturnCode::RUNNING); + DCHECK_GT(pid_, 0) << "The subprocess has been waited already"; + + this->waitTimeout(waitTimeout); + + if (returnCode_.running()) { + return terminateOrKill(sigtermTimeout); + } + return returnCode_; +} + +ProcessReturnCode Subprocess::terminateOrKill(TimeoutDuration sigtermTimeout) { + returnCode_.enforce(ProcessReturnCode::RUNNING); + DCHECK_GT(pid_, 0) << "The subprocess has been waited already"; + // 1. Send SIGTERM to kill the process + terminate(); + // 2. check whether subprocess has terminated using non-blocking waitpid + waitTimeout(sigtermTimeout); + if (!returnCode_.running()) { + return returnCode_; + } + // 3. If we are at this point, we have waited enough time after + // sending SIGTERM, we have to use nuclear option SIGKILL to kill + // the subprocess. + LOG(INFO) << "Send SIGKILL to " << pid_; + kill(); + // 4. SIGKILL should kill the process otherwise there must be + // something seriously wrong, just use blocking wait to wait for the + // subprocess to finish. + return wait(); +} + +pid_t Subprocess::pid() const { + return pid_; +} + +namespace { + +ByteRange queueFront(const IOBufQueue& queue) { + auto* p = queue.front(); + if (!p) { + return ByteRange{}; + } + return io::Cursor(p).peekBytes(); +} + +// fd write +bool handleWrite(int fd, IOBufQueue& queue) { + for (;;) { + auto b = queueFront(queue); + if (b.empty()) { + return true; // EOF + } + + ssize_t n = writeNoInt(fd, b.data(), b.size()); + if (n == -1 && errno == EAGAIN) { + return false; + } + checkUnixError(n, "write"); + queue.trimStart(n); + } +} + +// fd read +bool handleRead(int fd, IOBufQueue& queue) { + for (;;) { + auto p = queue.preallocate(100, 65000); + ssize_t n = readNoInt(fd, p.first, p.second); + if (n == -1 && errno == EAGAIN) { + return false; + } + checkUnixError(n, "read"); + if (n == 0) { + return true; + } + queue.postallocate(n); + } +} + +bool discardRead(int fd) { + static const size_t bufSize = 65000; + // Thread unsafe, but it doesn't matter. + static std::unique_ptr<char[]> buf(new char[bufSize]); + + for (;;) { + ssize_t n = readNoInt(fd, buf.get(), bufSize); + if (n == -1 && errno == EAGAIN) { + return false; + } + checkUnixError(n, "read"); + if (n == 0) { + return true; + } + } +} + +} // namespace + +std::pair<std::string, std::string> Subprocess::communicate(StringPiece input) { + IOBufQueue inputQueue; + inputQueue.wrapBuffer(input.data(), input.size()); + + auto outQueues = communicateIOBuf(std::move(inputQueue)); + auto outBufs = + std::make_pair(outQueues.first.move(), outQueues.second.move()); + std::pair<std::string, std::string> out; + if (outBufs.first) { + outBufs.first->coalesce(); + out.first.assign( + reinterpret_cast<const char*>(outBufs.first->data()), + outBufs.first->length()); + } + if (outBufs.second) { + outBufs.second->coalesce(); + out.second.assign( + reinterpret_cast<const char*>(outBufs.second->data()), + outBufs.second->length()); + } + return out; +} + +std::pair<IOBufQueue, IOBufQueue> Subprocess::communicateIOBuf( + IOBufQueue input) { + // If the user supplied a non-empty input buffer, make sure + // that stdin is a pipe so we can write the data. + if (!input.empty()) { + // findByChildFd() will throw std::invalid_argument if no pipe for + // STDIN_FILENO exists + findByChildFd(STDIN_FILENO); + } + + std::pair<IOBufQueue, IOBufQueue> out; + + auto readCallback = [&](int pfd, int cfd) -> bool { + if (cfd == STDOUT_FILENO) { + return handleRead(pfd, out.first); + } else if (cfd == STDERR_FILENO) { + return handleRead(pfd, out.second); + } else { + // Don't close the file descriptor, the child might not like SIGPIPE, + // just read and throw the data away. + return discardRead(pfd); + } + }; + + auto writeCallback = [&](int pfd, int cfd) -> bool { + if (cfd == STDIN_FILENO) { + return handleWrite(pfd, input); + } else { + // If we don't want to write to this fd, just close it. + return true; + } + }; + + communicate(std::move(readCallback), std::move(writeCallback)); + + return out; +} + +void Subprocess::communicate( + FdCallback readCallback, + FdCallback writeCallback) { + // This serves to prevent wait() followed by communicate(), but if you + // legitimately need that, send a patch to delete this line. + returnCode_.enforce(ProcessReturnCode::RUNNING); + setAllNonBlocking(); + + std::vector<pollfd> fds; + fds.reserve(pipes_.size()); + std::vector<size_t> toClose; // indexes into pipes_ + toClose.reserve(pipes_.size()); + + while (!pipes_.empty()) { + fds.clear(); + toClose.clear(); + + for (auto& p : pipes_) { + pollfd pfd; + pfd.fd = p.pipe.fd(); + // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the + // child's point of view. + if (!p.enabled) { + // Still keeping fd in watched set so we get notified of POLLHUP / + // POLLERR + pfd.events = 0; + } else if (p.direction == PIPE_IN) { + pfd.events = POLLOUT; + } else { + pfd.events = POLLIN; + } + fds.push_back(pfd); + } + + int r; + do { + r = ::poll(fds.data(), fds.size(), -1); + } while (r == -1 && errno == EINTR); + checkUnixError(r, "poll"); + + for (size_t i = 0; i < pipes_.size(); ++i) { + auto& p = pipes_[i]; + auto parentFd = p.pipe.fd(); + DCHECK_EQ(fds[i].fd, parentFd); + short events = fds[i].revents; + + bool closed = false; + if (events & POLLOUT) { + DCHECK(!(events & POLLIN)); + if (writeCallback(parentFd, p.childFd)) { + toClose.push_back(i); + closed = true; + } + } + + // Call read callback on POLLHUP, to give it a chance to read (and act + // on) end of file + if (events & (POLLIN | POLLHUP)) { + DCHECK(!(events & POLLOUT)); + if (readCallback(parentFd, p.childFd)) { + toClose.push_back(i); + closed = true; + } + } + + if ((events & (POLLHUP | POLLERR)) && !closed) { + toClose.push_back(i); + closed = true; + } + } + + // Close the fds in reverse order so the indexes hold after erase() + for (int idx : boost::adaptors::reverse(toClose)) { + auto pos = pipes_.begin() + idx; + pos->pipe.close(); // Throws on error + pipes_.erase(pos); + } + } +} + +void Subprocess::enableNotifications(int childFd, bool enabled) { + pipes_[findByChildFd(childFd)].enabled = enabled; +} + +bool Subprocess::notificationsEnabled(int childFd) const { + return pipes_[findByChildFd(childFd)].enabled; +} + +size_t Subprocess::findByChildFd(int childFd) const { + auto pos = std::lower_bound( + pipes_.begin(), pipes_.end(), childFd, [](const Pipe& pipe, int fd) { + return pipe.childFd < fd; + }); + if (pos == pipes_.end() || pos->childFd != childFd) { + throw std::invalid_argument( + folly::to<std::string>("child fd not found ", childFd)); + } + return pos - pipes_.begin(); +} + +void Subprocess::closeParentFd(int childFd) { + int idx = findByChildFd(childFd); + pipes_[idx].pipe.close(); // May throw + pipes_.erase(pipes_.begin() + idx); +} + +std::vector<Subprocess::ChildPipe> Subprocess::takeOwnershipOfPipes() { + std::vector<Subprocess::ChildPipe> pipes; + for (auto& p : pipes_) { + pipes.emplace_back(p.childFd, std::move(p.pipe)); + } + // release memory + std::vector<Pipe>().swap(pipes_); + return pipes; +} + +namespace { + +class Initializer { + public: + Initializer() { + // We like EPIPE, thanks. + ::signal(SIGPIPE, SIG_IGN); + } +}; + +Initializer initializer; + +} // namespace + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Subprocess.h b/ios/Pods/Flipper-Folly/folly/Subprocess.h new file mode 100644 index 000000000..3c64ddfb5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Subprocess.h @@ -0,0 +1,1012 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Subprocess library, modeled after Python's subprocess module + * (http://docs.python.org/2/library/subprocess.html) + * + * This library defines one class (Subprocess) which represents a child + * process. Subprocess has two constructors: one that takes a vector<string> + * and executes the given executable without using the shell, and one + * that takes a string and executes the given command using the shell. + * Subprocess allows you to redirect the child's standard input, standard + * output, and standard error to/from child descriptors in the parent, + * or to create communication pipes between the child and the parent. + * + * The simplest example is a thread-safe [1] version of the system() library + * function: + * Subprocess(cmd).wait(); + * which executes the command using the default shell and waits for it + * to complete, returning the exit status. + * + * A thread-safe [1] version of popen() (type="r", to read from the child): + * Subprocess proc(cmd, Subprocess::Options().pipeStdout()); + * // read from proc.stdoutFd() + * proc.wait(); + * + * A thread-safe [1] version of popen() (type="w", to write to the child): + * Subprocess proc(cmd, Subprocess::Options().pipeStdin()); + * // write to proc.stdinFd() + * proc.wait(); + * + * If you want to redirect both stdin and stdout to pipes, you can, but note + * that you're subject to a variety of deadlocks. You'll want to use + * nonblocking I/O, like the callback version of communicate(). + * + * The string or IOBuf-based variants of communicate() are the simplest way + * to communicate with a child via its standard input, standard output, and + * standard error. They buffer everything in memory, so they are not great + * for large amounts of data (or long-running processes), but they are much + * simpler than the callback version. + * + * == A note on thread-safety == + * + * [1] "thread-safe" refers ONLY to the fact that Subprocess is very careful + * to fork in a way that does not cause grief in multithreaded programs. + * + * Caveat: If your system does not have the atomic pipe2 system call, it is + * not safe to concurrently call Subprocess from different threads. + * Therefore, it is best to have a single thread be responsible for spawning + * subprocesses. + * + * A particular instances of Subprocess is emphatically **not** thread-safe. + * If you need to simultaneously communicate via the pipes, and interact + * with the Subprocess state, your best bet is to: + * - takeOwnershipOfPipes() to separate the pipe I/O from the subprocess. + * - Only interact with the Subprocess from one thread at a time. + * + * The current implementation of communicate() cannot be safely interrupted. + * To do so correctly, one would need to use EventFD, or open a dedicated + * pipe to be messaged from a different thread -- in particular, kill() will + * not do, since a descendant may keep the pipes open indefinitely. + * + * So, once you call communicate(), you must wait for it to return, and not + * touch the pipes from other threads. closeParentFd() is emphatically + * unsafe to call concurrently, and even sendSignal() is not a good idea. + * You can perhaps give the Subprocess's PID to a different thread before + * starting communicate(), and use that PID to send a signal without + * accessing the Subprocess object. In that case, you will need a mutex + * that ensures you don't wait() before you sent said signal. In a + * nutshell, don't do this. + * + * In fact, signals are inherently concurrency-unsafe on Unix: if you signal + * a PID, while another thread is in waitpid(), the signal may fire either + * before or after the process is reaped. This means that your signal can, + * in pathological circumstances, be delivered to the wrong process (ouch!). + * To avoid this, you should only use non-blocking waits (i.e. poll()), and + * make sure to serialize your signals (i.e. kill()) with the waits -- + * either wait & signal from the same thread, or use a mutex. + */ + +#pragma once + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <chrono> +#include <exception> +#include <string> +#include <vector> + +#include <boost/container/flat_map.hpp> +#include <boost/operators.hpp> + +#include <folly/Exception.h> +#include <folly/File.h> +#include <folly/FileUtil.h> +#include <folly/Function.h> +#include <folly/MapUtil.h> +#include <folly/Optional.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/gen/String.h> +#include <folly/io/IOBufQueue.h> +#include <folly/portability/SysResource.h> + +namespace folly { + +/** + * Class to wrap a process return code. + */ +class Subprocess; +class ProcessReturnCode { + public: + enum State { + // Subprocess starts in the constructor, so this state designates only + // default-initialized or moved-out ProcessReturnCodes. + NOT_STARTED, + RUNNING, + EXITED, + KILLED, + }; + + static ProcessReturnCode makeNotStarted() { + return ProcessReturnCode(RV_NOT_STARTED); + } + + static ProcessReturnCode makeRunning() { + return ProcessReturnCode(RV_RUNNING); + } + + static ProcessReturnCode make(int status); + + // Default-initialized for convenience. Subprocess::returnCode() will + // never produce this value. + ProcessReturnCode() : rawStatus_(RV_NOT_STARTED) {} + + // Trivially copyable + ProcessReturnCode(const ProcessReturnCode& p) = default; + ProcessReturnCode& operator=(const ProcessReturnCode& p) = default; + // Non-default move: In order for Subprocess to be movable, the "moved + // out" state must not be "running", or ~Subprocess() will abort. + ProcessReturnCode(ProcessReturnCode&& p) noexcept; + ProcessReturnCode& operator=(ProcessReturnCode&& p) noexcept; + + /** + * Process state. One of: + * NOT_STARTED: process hasn't been started successfully + * RUNNING: process is currently running + * EXITED: process exited (successfully or not) + * KILLED: process was killed by a signal. + */ + State state() const; + + /** + * Helper wrappers around state(). + */ + bool notStarted() const { + return state() == NOT_STARTED; + } + bool running() const { + return state() == RUNNING; + } + bool exited() const { + return state() == EXITED; + } + bool killed() const { + return state() == KILLED; + } + + /** + * Exit status. Only valid if state() == EXITED; throws otherwise. + */ + int exitStatus() const; + + /** + * Signal that caused the process's termination. Only valid if + * state() == KILLED; throws otherwise. + */ + int killSignal() const; + + /** + * Was a core file generated? Only valid if state() == KILLED; throws + * otherwise. + */ + bool coreDumped() const; + + /** + * String representation; one of + * "not started" + * "running" + * "exited with status <status>" + * "killed by signal <signal>" + * "killed by signal <signal> (core dumped)" + */ + std::string str() const; + + /** + * Helper function to enforce a precondition based on this. + * Throws std::logic_error if in an unexpected state. + */ + void enforce(State expected) const; + + private: + explicit ProcessReturnCode(int rv) : rawStatus_(rv) {} + static constexpr int RV_NOT_STARTED = -2; + static constexpr int RV_RUNNING = -1; + + int rawStatus_; +}; + +/** + * Base exception thrown by the Subprocess methods. + */ +class FOLLY_EXPORT SubprocessError : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +/** + * Exception thrown by *Checked methods of Subprocess. + */ +class FOLLY_EXPORT CalledProcessError : public SubprocessError { + public: + explicit CalledProcessError(ProcessReturnCode rc); + ~CalledProcessError() throw() override = default; + ProcessReturnCode returnCode() const { + return returnCode_; + } + + private: + ProcessReturnCode returnCode_; +}; + +/** + * Exception thrown if the subprocess cannot be started. + */ +class FOLLY_EXPORT SubprocessSpawnError : public SubprocessError { + public: + SubprocessSpawnError(const char* executable, int errCode, int errnoValue); + ~SubprocessSpawnError() throw() override = default; + int errnoValue() const { + return errnoValue_; + } + + private: + int errnoValue_; +}; + +/** + * Subprocess. + */ +class Subprocess { + public: + static const int CLOSE = -1; + static const int PIPE = -2; + static const int PIPE_IN = -3; + static const int PIPE_OUT = -4; + + /** + * See Subprocess::Options::dangerousPostForkPreExecCallback() for usage. + * Every derived class should include the following warning: + * + * DANGER: This class runs after fork in a child processes. Be fast, the + * parent thread is waiting, but remember that other parent threads are + * running and may mutate your state. Avoid mutating any data belonging to + * the parent. Avoid interacting with non-POD data that originated in the + * parent. Avoid any libraries that may internally reference non-POD data. + * Especially beware parent mutexes -- for example, glog's LOG() uses one. + */ + struct DangerousPostForkPreExecCallback { + virtual ~DangerousPostForkPreExecCallback() {} + // This must return 0 on success, or an `errno` error code. + virtual int operator()() = 0; + }; + + /** + * Class representing various options: file descriptor behavior, and + * whether to use $PATH for searching for the executable, + * + * By default, we don't use $PATH, file descriptors are closed if + * the close-on-exec flag is set (fcntl FD_CLOEXEC) and inherited + * otherwise. + */ + class Options { + friend class Subprocess; + + public: + Options() {} // E.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58328 + + /** + * Change action for file descriptor fd. + * + * "action" may be another file descriptor number (dup2()ed before the + * child execs), or one of CLOSE, PIPE_IN, and PIPE_OUT. + * + * CLOSE: close the file descriptor in the child + * PIPE_IN: open a pipe *from* the child + * PIPE_OUT: open a pipe *to* the child + * + * PIPE is a shortcut; same as PIPE_IN for stdin (fd 0), same as + * PIPE_OUT for stdout (fd 1) or stderr (fd 2), and an error for + * other file descriptors. + */ + Options& fd(int fd, int action); + + /** + * Shortcut to change the action for standard input. + */ + Options& stdinFd(int action) { + return fd(STDIN_FILENO, action); + } + + /** + * Shortcut to change the action for standard output. + */ + Options& stdoutFd(int action) { + return fd(STDOUT_FILENO, action); + } + + /** + * Shortcut to change the action for standard error. + * Note that stderr(1) will redirect the standard error to the same + * file descriptor as standard output; the equivalent of bash's "2>&1" + */ + Options& stderrFd(int action) { + return fd(STDERR_FILENO, action); + } + + Options& pipeStdin() { + return fd(STDIN_FILENO, PIPE_IN); + } + Options& pipeStdout() { + return fd(STDOUT_FILENO, PIPE_OUT); + } + Options& pipeStderr() { + return fd(STDERR_FILENO, PIPE_OUT); + } + + /** + * Close all other fds (other than standard input, output, error, + * and file descriptors explicitly specified with fd()). + * + * This is potentially slow; it's generally a better idea to + * set the close-on-exec flag on all file descriptors that shouldn't + * be inherited by the child. + * + * Even with this option set, standard input, output, and error are + * not closed; use stdin(CLOSE), stdout(CLOSE), stderr(CLOSE) if you + * desire this. + */ + Options& closeOtherFds() { + closeOtherFds_ = true; + return *this; + } + + /** + * Use the search path ($PATH) when searching for the executable. + */ + Options& usePath() { + usePath_ = true; + return *this; + } + + /** + * Change the child's working directory, after the vfork. + */ + Options& chdir(const std::string& dir) { + childDir_ = dir; + return *this; + } + +#if defined(__linux__) + /** + * Child will receive a signal when the parent *thread* exits. + * + * This is especially important when this option is used but the calling + * thread does not block for the duration of the subprocess. If the original + * thread that created the subprocess ends then the subprocess will + * terminate. For example, thread pool executors which can reap unused + * threads may trigger this behavior. + */ + Options& parentDeathSignal(int sig) { + parentDeathSignal_ = sig; + return *this; + } +#endif + + /** + * Child will be made a process group leader when it starts. Upside: one + * can reliably kill all its non-daemonizing descendants. Downside: the + * child will not receive Ctrl-C etc during interactive use. + */ + Options& processGroupLeader() { + processGroupLeader_ = true; + return *this; + } + + /** + * Detach the spawned process, to allow destroying the Subprocess object + * without waiting for the child process to finish. + * + * This causes the code to fork twice before executing the command. + * The intermediate child process will exit immediately, causing the process + * running the executable to be reparented to init (pid 1). + * + * Subprocess objects created with detach() enabled will already be in an + * "EXITED" state when the constructor returns. The caller should not call + * wait() or poll() on the Subprocess, and pid() will return -1. + */ + Options& detach() { + detach_ = true; + return *this; + } + + /** + * *** READ THIS WHOLE DOCBLOCK BEFORE USING *** + * + * Run this callback in the child after the fork, just before the + * exec(), and after the child's state has been completely set up: + * - signal handlers have been reset to default handling and unblocked + * - the working directory was set + * - closed any file descriptors specified via Options() + * - set child process flags (see code) + * + * This is EXTREMELY DANGEROUS. For example, this innocuous-looking code + * can cause a fraction of your Subprocess launches to hang forever: + * + * LOG(INFO) << "Hello from the child"; + * + * The reason is that glog has an internal mutex. If your fork() happens + * when the parent has the mutex locked, the child will wait forever. + * + * == GUIDELINES == + * + * - Be quick -- the parent thread is blocked until you exit. + * - Remember that other parent threads are running, and may mutate your + * state. + * - Avoid mutating any data belonging to the parent. + * - Avoid interacting with non-POD data that came from the parent. + * - Avoid any libraries that may internally reference non-POD state. + * - Especially beware parent mutexes, e.g. LOG() uses a global mutex. + * - Avoid invoking the parent's destructors (you can accidentally + * delete files, terminate network connections, etc). + * - Read http://ewontfix.com/7/ + */ + Options& dangerousPostForkPreExecCallback( + DangerousPostForkPreExecCallback* cob) { + dangerousPostForkPreExecCallback_ = cob; + return *this; + } + +#if defined(__linux__) + /** + * This is an experimental feature, it is best you don't use it at this + * point of time. + * Although folly would support cloning with custom flags in some form, this + * API might change in the near future. So use the following assuming it is + * experimental. (Apr 11, 2017) + * + * This unlocks Subprocess to support clone flags, many of them need + * CAP_SYS_ADMIN permissions. It might also require you to go through the + * implementation to understand what happens before, between and after the + * fork-and-exec. + * + * `man 2 clone` would be a starting point for knowing about the available + * flags. + */ + using clone_flags_t = uint64_t; + Options& useCloneWithFlags(clone_flags_t cloneFlags) noexcept { + cloneFlags_ = cloneFlags; + return *this; + } +#endif + + private: + typedef boost::container::flat_map<int, int> FdMap; + FdMap fdActions_; + bool closeOtherFds_{false}; + bool usePath_{false}; + bool processGroupLeader_{false}; + bool detach_{false}; + std::string childDir_; // "" keeps the parent's working directory +#if defined(__linux__) + int parentDeathSignal_{0}; +#endif + DangerousPostForkPreExecCallback* dangerousPostForkPreExecCallback_{ + nullptr}; +#if defined(__linux__) + // none means `vfork()` instead of a custom `clone()` + // Optional<> is used because value of '0' means do clone without any flags. + Optional<clone_flags_t> cloneFlags_; +#endif + }; + + // Non-copiable, but movable + Subprocess(const Subprocess&) = delete; + Subprocess& operator=(const Subprocess&) = delete; + Subprocess(Subprocess&&) = default; + Subprocess& operator=(Subprocess&&) = default; + + /** + * Create an uninitialized subprocess. + * + * In this state it can only be destroyed, or assigned to using the move + * assignment operator. + */ + Subprocess(); + + /** + * Create a subprocess from the given arguments. argv[0] must be listed. + * If not-null, executable must be the actual executable + * being used (otherwise it's the same as argv[0]). + * + * If env is not-null, it must contain name=value strings to be used + * as the child's environment; otherwise, we inherit the environment + * from the parent. env must be null if options.usePath is set. + */ + explicit Subprocess( + const std::vector<std::string>& argv, + const Options& options = Options(), + const char* executable = nullptr, + const std::vector<std::string>* env = nullptr); + ~Subprocess(); + + /** + * Create a Subprocess object for an existing child process ID. + * + * The process ID must refer to an immediate child process of the current + * process. This allows using the poll() and wait() APIs on a process ID + * that was not originally spawned by Subprocess. + */ + static Subprocess fromExistingProcess(pid_t pid); + + /** + * Create a subprocess run as a shell command (as shell -c 'command') + * + * The shell to use is taken from the environment variable $SHELL, + * or /bin/sh if $SHELL is unset. + */ + // clang-format off + [[deprecated( + "Prefer not running in a shell or use `shellify`.")]] + explicit Subprocess( + const std::string& cmd, + const Options& options = Options(), + const std::vector<std::string>* env = nullptr); + // clang-format on + + //// + //// The methods below only manipulate the process state, and do not + //// affect its communication pipes. + //// + + /** + * Return the child's pid, or -1 if the child wasn't successfully spawned + * or has already been wait()ed upon. + */ + pid_t pid() const; + + /** + * Return the child's status (as per wait()) if the process has already + * been waited on, -1 if the process is still running, or -2 if the + * process hasn't been successfully started. NOTE that this does not call + * waitpid() or Subprocess::poll(), but simply returns the status stored + * in the Subprocess object. + */ + ProcessReturnCode returnCode() const { + return returnCode_; + } + + /** + * Poll the child's status and return it. Return the exit status if the + * subprocess had quit, or RUNNING otherwise. Throws an std::logic_error + * if called on a Subprocess whose status is no longer RUNNING. No other + * exceptions are possible. Aborts on egregious violations of contract, + * e.g. if you wait for the underlying process without going through this + * Subprocess instance. + */ + ProcessReturnCode poll(struct rusage* ru = nullptr); + + /** + * Poll the child's status. If the process is still running, return false. + * Otherwise, return true if the process exited with status 0 (success), + * or throw CalledProcessError if the process exited with a non-zero status. + */ + bool pollChecked(); + + /** + * Wait for the process to terminate and return its status. Like poll(), + * the only exception this can throw is std::logic_error if you call this + * on a Subprocess whose status is not RUNNING. Aborts on egregious + * violations of contract, like an out-of-band waitpid(p.pid(), 0, 0). + */ + ProcessReturnCode wait(); + + /** + * Wait for the process to terminate, throw if unsuccessful. + */ + void waitChecked(); + + using TimeoutDuration = std::chrono::milliseconds; + + /** + * Call `waitpid` non-blockingly up to `timeout`. Throws std::logic_error if + * called on a Subprocess whose status is not RUNNING. + * + * The return code will be running() if waiting timed out. + */ + ProcessReturnCode waitTimeout(TimeoutDuration timeout); + + /** + * Send a signal to the child. Shortcuts for the commonly used Unix + * signals are below. + */ + void sendSignal(int signal); + void terminate() { + sendSignal(SIGTERM); + } + void kill() { + sendSignal(SIGKILL); + } + + /** + * Call `waitpid` non-blockingly up to `waitTimeout`. If the process hasn't + * terminated after that, fall back on `terminateOrKill` with + * `sigtermTimeoutSeconds`. + */ + ProcessReturnCode waitOrTerminateOrKill( + TimeoutDuration waitTimeout, + TimeoutDuration sigtermTimeout); + + /** + * Send the SIGTERM to terminate the process, poll `waitpid` non-blockingly + * several times up to `sigtermTimeout`. If the process hasn't terminated + * after that, send SIGKILL to kill the process and call `waitpid` blockingly. + * Return the exit code of process. + */ + ProcessReturnCode terminateOrKill(TimeoutDuration sigtermTimeout); + + //// + //// The methods below only affect the process's communication pipes, but + //// not its return code or state (they do not poll() or wait()). + //// + + /** + * Communicate with the child until all pipes to/from the child are closed. + * + * The input buffer is written to the process' stdin pipe, and data is read + * from the stdout and stderr pipes. Non-blocking I/O is performed on all + * pipes simultaneously to avoid deadlocks. + * + * The stdin pipe will be closed after the full input buffer has been written. + * An error will be thrown if a non-empty input buffer is supplied but stdin + * was not configured as a pipe. + * + * Returns a pair of buffers containing the data read from stdout and stderr. + * If stdout or stderr is not a pipe, an empty IOBuf queue will be returned + * for the respective buffer. + * + * Note that communicate() and communicateIOBuf() both return when all + * pipes to/from the child are closed; the child might stay alive after + * that, so you must still wait(). + * + * communicateIOBuf() uses IOBufQueue for buffering (which has the + * advantage that it won't try to allocate all data at once), but it does + * store the subprocess's entire output in memory before returning. + * + * communicate() uses strings for simplicity. + */ + std::pair<IOBufQueue, IOBufQueue> communicateIOBuf( + IOBufQueue input = IOBufQueue()); + + std::pair<std::string, std::string> communicate( + StringPiece input = StringPiece()); + + /** + * Communicate with the child until all pipes to/from the child are closed. + * + * == Semantics == + * + * readCallback(pfd, cfd) will be called whenever there's data available + * on any pipe *from* the child (PIPE_OUT). pfd is the file descriptor + * in the parent (that you use to read from); cfd is the file descriptor + * in the child (used for identifying the stream; 1 = child's standard + * output, 2 = child's standard error, etc) + * + * writeCallback(pfd, cfd) will be called whenever a pipe *to* the child is + * writable (PIPE_IN). pfd is the file descriptor in the parent (that you + * use to write to); cfd is the file descriptor in the child (used for + * identifying the stream; 0 = child's standard input, etc) + * + * The read and write callbacks must read from / write to pfd and return + * false during normal operation. Return true to tell communicate() to + * close the pipe. For readCallback, this might send SIGPIPE to the + * child, or make its writes fail with EPIPE, so you should generally + * avoid returning true unless you've reached end-of-file. + * + * communicate() returns when all pipes to/from the child are closed; the + * child might stay alive after that, so you must still wait(). + * Conversely, the child may quit long before its pipes are closed, since + * its descendants can keep them alive forever. + * + * Most users won't need to use this callback version; the simpler version + * of communicate (which buffers data in memory) will probably work fine. + * + * == Things you must get correct == + * + * 1) You MUST consume all data passed to readCallback (or return true to + * close the pipe). Similarly, you MUST write to a writable pipe (or + * return true to close the pipe). To do otherwise is an error that can + * result in a deadlock. You must do this even for pipes you are not + * interested in. + * + * 2) pfd is nonblocking, so be prepared for read() / write() to return -1 + * and set errno to EAGAIN (in which case you should return false). Use + * readNoInt() from FileUtil.h to handle interrupted reads for you. + * + * 3) Your callbacks MUST NOT call any of the Subprocess methods that + * manipulate the pipe FDs. Check the docblocks, but, for example, + * neither closeParentFd (return true instead) nor takeOwnershipOfPipes + * are safe. Stick to reading/writing from pfd, as appropriate. + * + * == Good to know == + * + * 1) See ReadLinesCallback for an easy way to consume the child's output + * streams line-by-line (or tokenized by another delimiter). + * + * 2) "Wait until the descendants close the pipes" is usually the behavior + * you want, since the descendants may have something to say even if the + * immediate child is dead. If you need to be able to force-close all + * parent FDs, communicate() will NOT work for you. Do it your own way by + * using takeOwnershipOfPipes(). + * + * Why not? You can return "true" from your callbacks to sever active + * pipes, but inactive ones can remain open indefinitely. It is + * impossible to safely close inactive pipes while another thread is + * blocked in communicate(). This is BY DESIGN. Racing communicate()'s + * read/write callbacks can result in wrong I/O and data corruption. This + * class would need internal synchronization and timeouts, a poor and + * expensive implementation choice, in order to make closeParentFd() + * thread-safe. + */ + using FdCallback = folly::Function<bool(int, int)>; + void communicate(FdCallback readCallback, FdCallback writeCallback); + + /** + * A readCallback for Subprocess::communicate() that helps you consume + * lines (or other delimited pieces) from your subprocess's file + * descriptors. Use the readLinesCallback() helper to get template + * deduction. For example: + * + * subprocess.communicate( + * Subprocess::readLinesCallback( + * [](int fd, folly::StringPiece s) { + * std::cout << fd << " said: " << s; + * return false; // Keep reading from the child + * } + * ), + * [](int pdf, int cfd){ return true; } // Don't write to the child + * ); + * + * If a file line exceeds maxLineLength, your callback will get some + * initial chunks of maxLineLength with no trailing delimiters. The final + * chunk of a line is delimiter-terminated iff the delimiter was present + * in the input. In particular, the last line in a file always lacks a + * delimiter -- so if a file ends on a delimiter, the final line is empty. + * + * Like a regular communicate() callback, your fdLineCb() normally returns + * false. It may return true to tell Subprocess to close the underlying + * file descriptor. The child process may then receive SIGPIPE or get + * EPIPE errors on writes. + */ + template <class Callback> + class ReadLinesCallback { + private: + // Binds an FD to the client-provided FD+line callback + struct StreamSplitterCallback { + StreamSplitterCallback(Callback& cb, int fd) : cb_(cb), fd_(fd) {} + // The return value semantics are inverted vs StreamSplitter + bool operator()(StringPiece s) { + return !cb_(fd_, s); + } + Callback& cb_; + int fd_; + }; + typedef gen::StreamSplitter<StreamSplitterCallback> LineSplitter; + + public: + explicit ReadLinesCallback( + Callback&& fdLineCb, + uint64_t maxLineLength = 0, // No line length limit by default + char delimiter = '\n', + uint64_t bufSize = 1024) + : fdLineCb_(std::forward<Callback>(fdLineCb)), + maxLineLength_(maxLineLength), + delimiter_(delimiter), + bufSize_(bufSize) {} + + bool operator()(int pfd, int cfd) { + // Make a splitter for this cfd if it doesn't already exist + auto it = fdToSplitter_.find(cfd); + auto& splitter = (it != fdToSplitter_.end()) + ? it->second + : fdToSplitter_ + .emplace( + cfd, + LineSplitter( + delimiter_, + StreamSplitterCallback(fdLineCb_, cfd), + maxLineLength_)) + .first->second; + // Read as much as we can from this FD + char buf[bufSize_]; + while (true) { + ssize_t ret = readNoInt(pfd, buf, bufSize_); + if (ret == -1 && errno == EAGAIN) { // No more data for now + return false; + } + checkUnixError(ret, "read"); + if (ret == 0) { // Reached end-of-file + splitter.flush(); // Ignore return since the file is over anyway + return true; + } + if (!splitter(StringPiece(buf, ret))) { + return true; // The callback told us to stop + } + } + } + + private: + Callback fdLineCb_; + const uint64_t maxLineLength_; + const char delimiter_; + const uint64_t bufSize_; + // We lazily make splitters for all cfds that get used. + std::unordered_map<int, LineSplitter> fdToSplitter_; + }; + + // Helper to enable template deduction + template <class Callback> + static auto readLinesCallback( + Callback&& fdLineCb, + uint64_t maxLineLength = 0, // No line length limit by default + char delimiter = '\n', + uint64_t bufSize = 1024) + -> ReadLinesCallback<typename std::decay<Callback>::type> { + return ReadLinesCallback<typename std::decay<Callback>::type>( + std::forward<Callback>(fdLineCb), maxLineLength, delimiter, bufSize); + } + + /** + * communicate() callbacks can use this to temporarily enable/disable + * notifications (callbacks) for a pipe to/from the child. By default, + * all are enabled. Useful for "chatty" communication -- you want to + * disable write callbacks until you receive the expected message. + * + * Disabling a pipe does not free you from the requirement to consume all + * incoming data. Failing to do so will easily create deadlock bugs. + * + * Throws if the childFd is not known. + */ + void enableNotifications(int childFd, bool enabled); + + /** + * Are notifications for one pipe to/from child enabled? Throws if the + * childFd is not known. + */ + bool notificationsEnabled(int childFd) const; + + //// + //// The following methods are meant for the cases when communicate() is + //// not suitable. You should not need them when you call communicate(), + //// and, in fact, it is INHERENTLY UNSAFE to use closeParentFd() or + //// takeOwnershipOfPipes() from a communicate() callback. + //// + + /** + * Close the parent file descriptor given a file descriptor in the child. + * DO NOT USE from communicate() callbacks; make them return true instead. + */ + void closeParentFd(int childFd); + + /** + * Set all pipes from / to child to be non-blocking. communicate() does + * this for you. + */ + void setAllNonBlocking(); + + /** + * Get parent file descriptor corresponding to the given file descriptor + * in the child. Throws if childFd isn't a pipe (PIPE_IN / PIPE_OUT). + * Do not close() the returned file descriptor; use closeParentFd, above. + */ + int parentFd(int childFd) const { + return pipes_[findByChildFd(childFd)].pipe.fd(); + } + int stdinFd() const { + return parentFd(0); + } + int stdoutFd() const { + return parentFd(1); + } + int stderrFd() const { + return parentFd(2); + } + + /** + * The child's pipes are logically separate from the process metadata + * (they may even be kept alive by the child's descendants). This call + * lets you manage the pipes' lifetime separetely from the lifetime of the + * child process. + * + * After this call, the Subprocess instance will have no knowledge of + * these pipes, and the caller assumes responsibility for managing their + * lifetimes. Pro-tip: prefer to explicitly close() the pipes, since + * folly::File would otherwise silently suppress I/O errors. + * + * No, you may NOT call this from a communicate() callback. + */ + struct ChildPipe { + ChildPipe(int fd, folly::File&& ppe) : childFd(fd), pipe(std::move(ppe)) {} + int childFd; + folly::File pipe; // Owns the parent FD + }; + std::vector<ChildPipe> takeOwnershipOfPipes(); + + private: + // spawn() sets up a pipe to read errors from the child, + // then calls spawnInternal() to do the bulk of the work. Once + // spawnInternal() returns it reads the error pipe to see if the child + // encountered any errors. + void spawn( + std::unique_ptr<const char*[]> argv, + const char* executable, + const Options& options, + const std::vector<std::string>* env); + void spawnInternal( + std::unique_ptr<const char*[]> argv, + const char* executable, + Options& options, + const std::vector<std::string>* env, + int errFd); + + // Actions to run in child. + // Note that this runs after vfork(), so tread lightly. + // Returns 0 on success, or an errno value on failure. + int prepareChild( + const Options& options, + const sigset_t* sigmask, + const char* childDir) const; + int runChild( + const char* executable, + char** argv, + char** env, + const Options& options) const; + + /** + * Read from the error pipe, and throw SubprocessSpawnError if the child + * failed before calling exec(). + */ + void readChildErrorPipe(int pfd, const char* executable); + + // Returns an index into pipes_. Throws std::invalid_argument if not found. + size_t findByChildFd(const int childFd) const; + + pid_t pid_{-1}; + ProcessReturnCode returnCode_; + + /** + * Represents a pipe between this process, and the child process (or its + * descendant). To interact with these pipes, you can use communicate(), + * or use parentFd() and related methods, or separate them from the + * Subprocess instance entirely via takeOwnershipOfPipes(). + */ + struct Pipe : private boost::totally_ordered<Pipe> { + folly::File pipe; // Our end of the pipe, wrapped in a File to auto-close. + int childFd = -1; // Identifies the pipe: what FD is this in the child? + int direction = PIPE_IN; // one of PIPE_IN / PIPE_OUT + bool enabled = true; // Are notifications enabled in communicate()? + + bool operator<(const Pipe& other) const { + return childFd < other.childFd; + } + bool operator==(const Pipe& other) const { + return childFd == other.childFd; + } + }; + + // Populated at process start according to fdActions, empty after + // takeOwnershipOfPipes(). Sorted by childFd. Can only have elements + // erased, but not inserted, after being populated. + // + // The number of pipes between parent and child is assumed to be small, + // so we're happy with a vector here, even if it means linear erase. + std::vector<Pipe> pipes_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Synchronized.h b/ios/Pods/Flipper-Folly/folly/Synchronized.h new file mode 100644 index 000000000..0e9aeea5a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Synchronized.h @@ -0,0 +1,1892 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 module implements a Synchronized abstraction useful in + * mutex-based concurrency. + * + * The Synchronized<T, Mutex> class is the primary public API exposed by this + * module. See folly/docs/Synchronized.md for a more complete explanation of + * this class and its benefits. + */ + +#pragma once + +#include <folly/Function.h> +#include <folly/Likely.h> +#include <folly/LockTraits.h> +#include <folly/Preprocessor.h> +#include <folly/SharedMutex.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/container/Foreach.h> +#include <folly/functional/ApplyTuple.h> +#include <glog/logging.h> + +#include <array> +#include <mutex> +#include <tuple> +#include <type_traits> +#include <utility> + +namespace folly { + +template <class LockedType, class Mutex, class LockPolicy> +class LockedPtrBase; +template <class LockedType, class LockPolicy> +class LockedPtr; + +/** + * Public version of LockInterfaceDispatcher that contains the MutexLevel enum + * for the passed in mutex type + * + * This is decoupled from MutexLevelValueImpl in LockTraits.h because this + * ensures that a heterogenous mutex with a different API can be used. For + * example - if a mutex does not have a lock_shared() method but the + * LockTraits specialization for it supports a static non member + * lock_shared(Mutex&) it can be used as a shared mutex and will provide + * rlock() and wlock() functions. + */ +template <class Mutex> +using MutexLevelValue = detail::MutexLevelValueImpl< + true, + LockTraits<Mutex>::is_shared, + LockTraits<Mutex>::is_upgrade>; + +/** + * SynchronizedBase is a helper parent class for Synchronized<T>. + * + * It provides wlock() and rlock() methods for shared mutex types, + * or lock() methods for purely exclusive mutex types. + */ +template <class Subclass, detail::MutexLevel level> +class SynchronizedBase; + +/** + * SynchronizedBase specialization for shared mutex types. + * + * This class provides wlock() and rlock() methods for acquiring the lock and + * accessing the data. + */ +template <class Subclass> +class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> { + public: + using WLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>; + using ConstWLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyExclusive>; + + using RLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyShared>; + using ConstRLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyShared>; + + using TryWLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>; + using ConstTryWLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>; + + using TryRLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryShared>; + using ConstTryRLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyTryShared>; + + // These aliases are deprecated. + // TODO: Codemod them away. + using LockedPtr = WLockedPtr; + using ConstLockedPtr = ConstRLockedPtr; + + /** + * Acquire an exclusive lock, and return a LockedPtr that can be used to + * safely access the datum. + * + * LockedPtr offers operator -> and * to provide access to the datum. + * The lock will be released when the LockedPtr is destroyed. + */ + LockedPtr wlock() { + return LockedPtr(static_cast<Subclass*>(this)); + } + ConstWLockedPtr wlock() const { + return ConstWLockedPtr(static_cast<const Subclass*>(this)); + } + + /** + * Attempts to acquire the lock in exclusive mode. If acquisition is + * unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + TryWLockedPtr tryWLock() { + return TryWLockedPtr{static_cast<Subclass*>(this)}; + } + ConstTryWLockedPtr tryWLock() const { + return ConstTryWLockedPtr{static_cast<const Subclass*>(this)}; + } + + /** + * Acquire a read lock. The returned LockedPtr will have force const + * access to the data unless the lock is acquired in non-const + * context and asNonConstUnsafe() is used. + */ + RLockedPtr rlock() { + return RLockedPtr(static_cast<Subclass*>(this)); + } + ConstLockedPtr rlock() const { + return ConstLockedPtr(static_cast<const Subclass*>(this)); + } + + /** + * Attempts to acquire the lock in shared mode. If acquisition is + * unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + TryRLockedPtr tryRLock() { + return TryRLockedPtr{static_cast<Subclass*>(this)}; + } + ConstTryRLockedPtr tryRLock() const { + return ConstTryRLockedPtr{static_cast<const Subclass*>(this)}; + } + + /** + * Attempts to acquire the lock, or fails if the timeout elapses first. + * If acquisition is unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + template <class Rep, class Period> + LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) { + return LockedPtr(static_cast<Subclass*>(this), timeout); + } + template <class Rep, class Period> + LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) const { + return LockedPtr(static_cast<const Subclass*>(this), timeout); + } + + /** + * Attempts to acquire the lock, or fails if the timeout elapses first. + * If acquisition is unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + template <class Rep, class Period> + RLockedPtr rlock(const std::chrono::duration<Rep, Period>& timeout) { + return RLockedPtr(static_cast<Subclass*>(this), timeout); + } + template <class Rep, class Period> + ConstRLockedPtr rlock( + const std::chrono::duration<Rep, Period>& timeout) const { + return ConstRLockedPtr(static_cast<const Subclass*>(this), timeout); + } + + /** + * Invoke a function while holding the lock exclusively. + * + * A reference to the datum will be passed into the function as its only + * argument. + * + * This can be used with a lambda argument for easily defining small critical + * sections in the code. For example: + * + * auto value = obj.withWLock([](auto& data) { + * data.doStuff(); + * return data.getValue(); + * }); + */ + template <class Function> + auto withWLock(Function&& function) { + return function(*wlock()); + } + template <class Function> + auto withWLock(Function&& function) const { + return function(*wlock()); + } + + /** + * Invoke a function while holding the lock exclusively. + * + * This is similar to withWLock(), but the function will be passed a + * LockedPtr rather than a reference to the data itself. + * + * This allows scopedUnlock() to be called on the LockedPtr argument if + * desired. + */ + template <class Function> + auto withWLockPtr(Function&& function) { + return function(wlock()); + } + template <class Function> + auto withWLockPtr(Function&& function) const { + return function(wlock()); + } + + /** + * Invoke a function while holding an the lock in shared mode. + * + * A const reference to the datum will be passed into the function as its + * only argument. + */ + template <class Function> + auto withRLock(Function&& function) const { + return function(*rlock()); + } + + template <class Function> + auto withRLockPtr(Function&& function) { + return function(rlock()); + } + + template <class Function> + auto withRLockPtr(Function&& function) const { + return function(rlock()); + } +}; + +/** + * SynchronizedBase specialization for upgrade mutex types. + * + * This class provides all the functionality provided by the SynchronizedBase + * specialization for shared mutexes and a ulock() method that returns an + * upgrade lock RAII proxy + */ +template <class Subclass> +class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE> + : public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> { + public: + using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>; + using ConstUpgradeLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyUpgrade>; + + using TryUpgradeLockedPtr = + ::folly::LockedPtr<Subclass, LockPolicyTryUpgrade>; + using ConstTryUpgradeLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyTryUpgrade>; + + /** + * Acquire an upgrade lock. The returned LockedPtr will have force + * const access to the data unless the lock is acquired in non-const + * context and asNonConstUnsafe() is used. + */ + UpgradeLockedPtr ulock() { + return UpgradeLockedPtr(static_cast<Subclass*>(this)); + } + ConstUpgradeLockedPtr ulock() const { + return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this)); + } + + /** + * Attempts to acquire the lock in upgrade mode. If acquisition is + * unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + TryUpgradeLockedPtr tryULock() { + return TryUpgradeLockedPtr{static_cast<Subclass*>(this)}; + } + + /** + * Acquire an upgrade lock and return a LockedPtr that can be used to safely + * access the datum + * + * And the const version + */ + template <class Rep, class Period> + UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) { + return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout); + } + + /** + * Invoke a function while holding the lock. + * + * A reference to the datum will be passed into the function as its only + * argument. + * + * This can be used with a lambda argument for easily defining small critical + * sections in the code. For example: + * + * auto value = obj.withULock([](auto& data) { + * data.doStuff(); + * return data.getValue(); + * }); + * + * This is probably not the function you want. If the intent is to read the + * data object and determine whether you should upgrade to a write lock then + * the withULockPtr() method should be called instead, since it gives access + * to the LockedPtr proxy (which can be upgraded via the + * moveFromUpgradeToWrite() method) + */ + template <class Function> + auto withULock(Function&& function) { + return function(*ulock()); + } + template <class Function> + auto withULock(Function&& function) const { + return function(*ulock()); + } + + /** + * Invoke a function while holding the lock exclusively. + * + * This is similar to withULock(), but the function will be passed a + * LockedPtr rather than a reference to the data itself. + * + * This allows scopedUnlock() and getUniqueLock() to be called on the + * LockedPtr argument. + * + * This also allows you to upgrade the LockedPtr proxy to a write state so + * that changes can be made to the underlying data + */ + template <class Function> + auto withULockPtr(Function&& function) { + return function(ulock()); + } + template <class Function> + auto withULockPtr(Function&& function) const { + return function(ulock()); + } +}; + +/** + * SynchronizedBase specialization for non-shared mutex types. + * + * This class provides lock() methods for acquiring the lock and accessing the + * data. + */ +template <class Subclass> +class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> { + public: + using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>; + using ConstLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyExclusive>; + + using TryLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>; + using ConstTryLockedPtr = + ::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>; + + /** + * Acquire a lock, and return a LockedPtr that can be used to safely access + * the datum. + */ + LockedPtr lock() { + return LockedPtr(static_cast<Subclass*>(this)); + } + + /** + * Acquire a lock, and return a ConstLockedPtr that can be used to safely + * access the datum. + */ + ConstLockedPtr lock() const { + return ConstLockedPtr(static_cast<const Subclass*>(this)); + } + + /** + * Attempts to acquire the lock in exclusive mode. If acquisition is + * unsuccessful, the returned LockedPtr will be null. + * + * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for + * validity.) + */ + TryLockedPtr tryLock() { + return TryLockedPtr{static_cast<Subclass*>(this)}; + } + ConstTryLockedPtr tryLock() const { + return ConstTryLockedPtr{static_cast<const Subclass*>(this)}; + } + + /** + * Attempts to acquire the lock, or fails if the timeout elapses first. + * If acquisition is unsuccessful, the returned LockedPtr will be null. + */ + template <class Rep, class Period> + LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) { + return LockedPtr(static_cast<Subclass*>(this), timeout); + } + + /** + * Attempts to acquire the lock, or fails if the timeout elapses first. + * If acquisition is unsuccessful, the returned LockedPtr will be null. + */ + template <class Rep, class Period> + ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const { + return ConstLockedPtr(static_cast<const Subclass*>(this), timeout); + } + + /** + * Invoke a function while holding the lock. + * + * A reference to the datum will be passed into the function as its only + * argument. + * + * This can be used with a lambda argument for easily defining small critical + * sections in the code. For example: + * + * auto value = obj.withLock([](auto& data) { + * data.doStuff(); + * return data.getValue(); + * }); + */ + template <class Function> + auto withLock(Function&& function) { + return function(*lock()); + } + template <class Function> + auto withLock(Function&& function) const { + return function(*lock()); + } + + /** + * Invoke a function while holding the lock exclusively. + * + * This is similar to withWLock(), but the function will be passed a + * LockedPtr rather than a reference to the data itself. + * + * This allows scopedUnlock() and getUniqueLock() to be called on the + * LockedPtr argument. + */ + template <class Function> + auto withLockPtr(Function&& function) { + return function(lock()); + } + template <class Function> + auto withLockPtr(Function&& function) const { + return function(lock()); + } +}; + +/** + * Synchronized<T> encapsulates an object of type T (a "datum") paired + * with a mutex. The only way to access the datum is while the mutex + * is locked, and Synchronized makes it virtually impossible to do + * otherwise. The code that would access the datum in unsafe ways + * would look odd and convoluted, thus readily alerting the human + * reviewer. In contrast, the code that uses Synchronized<T> correctly + * looks simple and intuitive. + * + * The second parameter must be a mutex type. Any mutex type supported by + * LockTraits<Mutex> can be used. By default any class with lock() and + * unlock() methods will work automatically. LockTraits can be specialized to + * teach Synchronized how to use other custom mutex types. See the + * documentation in LockTraits.h for additional details. + * + * Supported mutexes that work by default include std::mutex, + * std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex, + * folly::SharedMutex, folly::RWSpinLock, and folly::SpinLock. + */ +template <class T, class Mutex = SharedMutex> +struct Synchronized : public SynchronizedBase< + Synchronized<T, Mutex>, + MutexLevelValue<Mutex>::value> { + private: + using Base = + SynchronizedBase<Synchronized<T, Mutex>, MutexLevelValue<Mutex>::value>; + static constexpr bool nxCopyCtor{ + std::is_nothrow_copy_constructible<T>::value}; + static constexpr bool nxMoveCtor{ + std::is_nothrow_move_constructible<T>::value}; + + // used to disable copy construction and assignment + class NonImplementedType; + + public: + using LockedPtr = typename Base::LockedPtr; + using ConstLockedPtr = typename Base::ConstLockedPtr; + using DataType = T; + using MutexType = Mutex; + + /** + * Default constructor leaves both members call their own default + * constructor. + */ + Synchronized() = default; + + public: + /** + * Copy constructor; deprecated + * + * Enabled only when the data type is copy-constructible. + * + * Takes a shared-or-exclusive lock on the source mutex while performing the + * copy-construction of the destination data from the source data. No lock is + * taken on the destination mutex. + * + * May throw even when the data type is is nothrow-copy-constructible because + * acquiring a lock may throw. + */ + /* implicit */ Synchronized(typename std::conditional< + std::is_copy_constructible<T>::value, + const Synchronized&, + NonImplementedType>::type rhs) /* may throw */ + : Synchronized(rhs.copy()) {} + + /** + * Move constructor; deprecated + * + * Move-constructs from the source data without locking either the source or + * the destination mutex. + * + * Semantically, assumes that the source object is a true rvalue and therefore + * that no synchronization is required for accessing it. + */ + Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor) + : Synchronized(std::move(rhs.datum_)) {} + + /** + * Constructor taking a datum as argument copies it. There is no + * need to lock the constructing object. + */ + explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {} + + /** + * Constructor taking a datum rvalue as argument moves it. Again, + * there is no need to lock the constructing object. + */ + explicit Synchronized(T&& rhs) noexcept(nxMoveCtor) + : datum_(std::move(rhs)) {} + + /** + * Lets you construct non-movable types in-place. Use the constexpr + * instance `in_place` as the first argument. + */ + template <typename... Args> + explicit Synchronized(in_place_t, Args&&... args) + : datum_(std::forward<Args>(args)...) {} + + /** + * Lets you construct the synchronized object and also pass construction + * parameters to the underlying mutex if desired + */ + template <typename... DatumArgs, typename... MutexArgs> + Synchronized( + std::piecewise_construct_t, + std::tuple<DatumArgs...> datumArgs, + std::tuple<MutexArgs...> mutexArgs) + : Synchronized{std::piecewise_construct, + std::move(datumArgs), + std::move(mutexArgs), + std::make_index_sequence<sizeof...(DatumArgs)>{}, + std::make_index_sequence<sizeof...(MutexArgs)>{}} {} + + /** + * Copy assignment operator; deprecated + * + * Enabled only when the data type is copy-constructible and move-assignable. + * + * Move-assigns from a copy of the source data. + * + * Takes a shared-or-exclusive lock on the source mutex while copying the + * source data to a temporary. Takes an exclusive lock on the destination + * mutex while move-assigning from the temporary. + * + * This technique consts an extra temporary but avoids the need to take locks + * on both mutexes together. + */ + Synchronized& operator=(typename std::conditional< + std::is_copy_constructible<T>::value && + std::is_move_assignable<T>::value, + const Synchronized&, + NonImplementedType>::type rhs) { + return *this = rhs.copy(); + } + + /** + * Move assignment operator; deprecated + * + * Takes an exclusive lock on the destination mutex while move-assigning the + * destination data from the source data. The source mutex is not locked or + * otherwise accessed. + * + * Semantically, assumes that the source object is a true rvalue and therefore + * that no synchronization is required for accessing it. + */ + Synchronized& operator=(Synchronized&& rhs) { + return *this = std::move(rhs.datum_); + } + + /** + * Lock object, assign datum. + */ + Synchronized& operator=(const T& rhs) { + if (&datum_ != &rhs) { + auto guard = LockedPtr{this}; + datum_ = rhs; + } + return *this; + } + + /** + * Lock object, move-assign datum. + */ + Synchronized& operator=(T&& rhs) { + if (&datum_ != &rhs) { + auto guard = LockedPtr{this}; + datum_ = std::move(rhs); + } + return *this; + } + + /** + * Acquire an appropriate lock based on the context. + * + * If the mutex is a shared mutex, and the Synchronized instance is const, + * this acquires a shared lock. Otherwise this acquires an exclusive lock. + * + * In general, prefer using the explicit rlock() and wlock() methods + * for read-write locks, and lock() for purely exclusive locks. + * + * contextualLock() is primarily intended for use in other template functions + * that do not necessarily know the lock type. + */ + LockedPtr contextualLock() { + return LockedPtr(this); + } + ConstLockedPtr contextualLock() const { + return ConstLockedPtr(this); + } + template <class Rep, class Period> + LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) { + return LockedPtr(this, timeout); + } + template <class Rep, class Period> + ConstLockedPtr contextualLock( + const std::chrono::duration<Rep, Period>& timeout) const { + return ConstLockedPtr(this, timeout); + } + /** + * contextualRLock() acquires a read lock if the mutex type is shared, + * or a regular exclusive lock for non-shared mutex types. + * + * contextualRLock() when you know that you prefer a read lock (if + * available), even if the Synchronized<T> object itself is non-const. + */ + ConstLockedPtr contextualRLock() const { + return ConstLockedPtr(this); + } + template <class Rep, class Period> + ConstLockedPtr contextualRLock( + const std::chrono::duration<Rep, Period>& timeout) const { + return ConstLockedPtr(this, timeout); + } + + /** + * This accessor offers a LockedPtr. In turn, LockedPtr offers + * operator-> returning a pointer to T. The operator-> keeps + * expanding until it reaches a pointer, so syncobj->foo() will lock + * the object and call foo() against it. + * + * NOTE: This API is planned to be deprecated in an upcoming diff. + * Prefer using lock(), wlock(), or rlock() instead. + */ + [[deprecated("use explicit lock(), wlock(), or rlock() instead")]] LockedPtr + operator->() { + return LockedPtr(this); + } + + /** + * Obtain a ConstLockedPtr. + * + * NOTE: This API is planned to be deprecated in an upcoming diff. + * Prefer using lock(), wlock(), or rlock() instead. + */ + [[deprecated( + "use explicit lock(), wlock(), or rlock() instead")]] ConstLockedPtr + operator->() const { + return ConstLockedPtr(this); + } + + /** + * Attempts to acquire for a given number of milliseconds. If + * acquisition is unsuccessful, the returned LockedPtr is nullptr. + * + * NOTE: This API is deprecated. Use lock(), wlock(), or rlock() instead. + * In the future it will be marked with a deprecation attribute to emit + * build-time warnings, and then it will be removed entirely. + */ + LockedPtr timedAcquire(unsigned int milliseconds) { + return LockedPtr(this, std::chrono::milliseconds(milliseconds)); + } + + /** + * Attempts to acquire for a given number of milliseconds. If + * acquisition is unsuccessful, the returned ConstLockedPtr is nullptr. + * + * NOTE: This API is deprecated. Use lock(), wlock(), or rlock() instead. + * In the future it will be marked with a deprecation attribute to emit + * build-time warnings, and then it will be removed entirely. + */ + ConstLockedPtr timedAcquire(unsigned int milliseconds) const { + return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds)); + } + + /** + * Swaps with another Synchronized. Protected against + * self-swap. Only data is swapped. Locks are acquired in increasing + * address order. + */ + void swap(Synchronized& rhs) { + if (this == &rhs) { + return; + } + if (this > &rhs) { + return rhs.swap(*this); + } + auto guard1 = LockedPtr{this}; + auto guard2 = LockedPtr{&rhs}; + + using std::swap; + swap(datum_, rhs.datum_); + } + + /** + * Swap with another datum. Recommended because it keeps the mutex + * held only briefly. + */ + void swap(T& rhs) { + LockedPtr guard(this); + + using std::swap; + swap(datum_, rhs); + } + + /** + * Assign another datum and return the original value. Recommended + * because it keeps the mutex held only briefly. + */ + T exchange(T&& rhs) { + swap(rhs); + return std::move(rhs); + } + + /** + * Copies datum to a given target. + */ + void copyInto(T& target) const { + ConstLockedPtr guard(this); + target = datum_; + } + + /** + * Returns a fresh copy of the datum. + */ + T copy() const { + ConstLockedPtr guard(this); + return datum_; + } + + /** + * Returns a reference to the datum without acquiring a lock. + * + * Provided as a backdoor for call-sites where it is known safe to be used. + * For example, when it is known that only one thread has access to the + * Synchronized instance. + * + * To be used with care - this method explicitly overrides the normal safety + * guarantees provided by the rest of the Synchronized API. + */ + T& unsafeGetUnlocked() { + return datum_; + } + const T& unsafeGetUnlocked() const { + return datum_; + } + + private: + template <class LockedType, class MutexType, class LockPolicy> + friend class folly::LockedPtrBase; + template <class LockedType, class LockPolicy> + friend class folly::LockedPtr; + + /** + * Helper constructors to enable Synchronized for + * non-default constructible types T. + * Guards are created in actual public constructors and are alive + * for the time required to construct the object + */ + Synchronized( + const Synchronized& rhs, + const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor) + : datum_(rhs.datum_) {} + + Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept( + nxMoveCtor) + : datum_(std::move(rhs.datum_)) {} + + template < + typename... DatumArgs, + typename... MutexArgs, + std::size_t... IndicesOne, + std::size_t... IndicesTwo> + Synchronized( + std::piecewise_construct_t, + std::tuple<DatumArgs...> datumArgs, + std::tuple<MutexArgs...> mutexArgs, + std::index_sequence<IndicesOne...>, + std::index_sequence<IndicesTwo...>) + : datum_{std::get<IndicesOne>(std::move(datumArgs))...}, + mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {} + + // Synchronized data members + T datum_; + mutable Mutex mutex_; +}; + +template <class SynchronizedType, class LockPolicy> +class ScopedUnlocker; + +namespace detail { +/* + * A helper alias that resolves to "const T" if the template parameter + * is a const Synchronized<T>, or "T" if the parameter is not const. + */ +template <class SynchronizedType, bool AllowsConcurrentAccess> +using SynchronizedDataType = typename std::conditional< + AllowsConcurrentAccess || std::is_const<SynchronizedType>::value, + typename SynchronizedType::DataType const, + typename SynchronizedType::DataType>::type; +/* + * A helper alias that resolves to a ConstLockedPtr if the template parameter + * is a const Synchronized<T>, or a LockedPtr if the parameter is not const. + */ +template <class SynchronizedType> +using LockedPtrType = typename std::conditional< + std::is_const<SynchronizedType>::value, + typename SynchronizedType::ConstLockedPtr, + typename SynchronizedType::LockedPtr>::type; + +template < + typename Synchronized, + typename LockFunc, + typename TryLockFunc, + typename... Args> +class SynchronizedLocker { + public: + using LockedPtr = invoke_result_t<LockFunc&, Synchronized&, const Args&...>; + + template <typename LockFuncType, typename TryLockFuncType, typename... As> + SynchronizedLocker( + Synchronized& sync, + LockFuncType&& lockFunc, + TryLockFuncType tryLockFunc, + As&&... as) + : synchronized{sync}, + lockFunc_{std::forward<LockFuncType>(lockFunc)}, + tryLockFunc_{std::forward<TryLockFuncType>(tryLockFunc)}, + args_{std::forward<As>(as)...} {} + + auto lock() const { + auto args = std::tuple<const Args&...>{args_}; + return apply(lockFunc_, std::tuple_cat(std::tie(synchronized), args)); + } + auto tryLock() const { + return tryLockFunc_(synchronized); + } + + private: + Synchronized& synchronized; + LockFunc lockFunc_; + TryLockFunc tryLockFunc_; + std::tuple<Args...> args_; +}; + +template < + typename Synchronized, + typename LockFunc, + typename TryLockFunc, + typename... Args> +auto makeSynchronizedLocker( + Synchronized& synchronized, + LockFunc&& lockFunc, + TryLockFunc&& tryLockFunc, + Args&&... args) { + using LockFuncType = std::decay_t<LockFunc>; + using TryLockFuncType = std::decay_t<TryLockFunc>; + return SynchronizedLocker< + Synchronized, + LockFuncType, + TryLockFuncType, + std::decay_t<Args>...>{synchronized, + std::forward<LockFunc>(lockFunc), + std::forward<TryLockFunc>(tryLockFunc), + std::forward<Args>(args)...}; +} + +/** + * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe + * manner. + * + * The function uses the "smart and polite" algorithm from this link + * http://howardhinnant.github.io/dining_philosophers.html#Polite + * + * The gist of the algorithm is that it locks a mutex, then tries to lock the + * other mutexes in a non-blocking manner. If all the locks succeed, we are + * done, if not, we release the locks we have held, yield to allow other + * threads to continue and then block on the mutex that we failed to acquire. + * + * This allows dynamically yielding ownership of all the mutexes but one, so + * that other threads can continue doing work and locking the other mutexes. + * See the benchmarks in folly/test/SynchronizedBenchmark.cpp for more. + */ +template <typename... SynchronizedLocker> +auto lock(SynchronizedLocker... lockersIn) + -> std::tuple<typename SynchronizedLocker::LockedPtr...> { + // capture the list of lockers as a tuple + auto lockers = std::forward_as_tuple(lockersIn...); + + // make a list of null LockedPtr instances that we will return to the caller + auto lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{}; + + // start by locking the first thing in the list + std::get<0>(lockedPtrs) = std::get<0>(lockers).lock(); + auto indexLocked = 0; + + while (true) { + auto couldLockAll = true; + + for_each(lockers, [&](auto& locker, auto index) { + // if we should try_lock on the current locker then do so + if (index != indexLocked) { + auto lockedPtr = locker.tryLock(); + + // if we were unable to lock this mutex, + // + // 1. release all the locks, + // 2. yield control to another thread to be nice + // 3. block on the mutex we failed to lock, acquire the lock + // 4. break out and set the index of the current mutex to indicate + // which mutex we have locked + if (!lockedPtr) { + // writing lockedPtrs = decltype(lockedPtrs){} does not compile on + // gcc, I believe this is a bug D7676798 + lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{}; + + std::this_thread::yield(); + fetch(lockedPtrs, index) = locker.lock(); + indexLocked = index; + couldLockAll = false; + + return loop_break; + } + + // else store the locked mutex in the list we return + fetch(lockedPtrs, index) = std::move(lockedPtr); + } + + return loop_continue; + }); + + if (couldLockAll) { + return lockedPtrs; + } + } +} + +template <typename Synchronized, typename... Args> +auto wlock(Synchronized& synchronized, Args&&... args) { + return detail::makeSynchronizedLocker( + synchronized, + [](auto& s, auto&&... a) { + return s.wlock(std::forward<decltype(a)>(a)...); + }, + [](auto& s) { return s.tryWLock(); }, + std::forward<Args>(args)...); +} +template <typename Synchronized, typename... Args> +auto rlock(Synchronized& synchronized, Args&&... args) { + return detail::makeSynchronizedLocker( + synchronized, + [](auto& s, auto&&... a) { + return s.rlock(std::forward<decltype(a)>(a)...); + }, + [](auto& s) { return s.tryRLock(); }, + std::forward<Args>(args)...); +} +template <typename Synchronized, typename... Args> +auto ulock(Synchronized& synchronized, Args&&... args) { + return detail::makeSynchronizedLocker( + synchronized, + [](auto& s, auto&&... a) { + return s.ulock(std::forward<decltype(a)>(a)...); + }, + [](auto& s) { return s.tryULock(); }, + std::forward<Args>(args)...); +} +template <typename Synchronized, typename... Args> +auto lock(Synchronized& synchronized, Args&&... args) { + return detail::makeSynchronizedLocker( + synchronized, + [](auto& s, auto&&... a) { + return s.lock(std::forward<decltype(a)>(a)...); + }, + [](auto& s) { return s.tryLock(); }, + std::forward<Args>(args)...); +} + +} // namespace detail + +/** + * A helper base class for implementing LockedPtr. + * + * The main reason for having this as a separate class is so we can specialize + * it for std::mutex, so we can expose a std::unique_lock to the caller + * when std::mutex is being used. This allows callers to use a + * std::condition_variable with the mutex from a Synchronized<T, std::mutex>. + * + * We don't use std::unique_lock with other Mutex types since it makes the + * LockedPtr class slightly larger, and it makes the logic to support + * ScopedUnlocker slightly more complicated. std::mutex is the only one that + * really seems to benefit from the unique_lock. std::condition_variable + * itself only supports std::unique_lock<std::mutex>, so there doesn't seem to + * be any real benefit to exposing the unique_lock with other mutex types. + * + * Note that the SynchronizedType template parameter may or may not be const + * qualified. + */ +template <class SynchronizedType, class Mutex, class LockPolicy> +class LockedPtrBase { + public: + using MutexType = Mutex; + friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>; + + /** + * Friend all instantiations of LockedPtr and LockedPtrBase + */ + template <typename S, typename L> + friend class folly::LockedPtr; + template <typename S, typename M, typename L> + friend class LockedPtrBase; + + /** + * Destructor releases. + */ + ~LockedPtrBase() { + if (parent_) { + LockPolicy::unlock(parent_->mutex_); + } + } + + /** + * Unlock the synchronized data. + * + * The LockedPtr can no longer be dereferenced after unlock() has been + * called. isValid() will return false on an unlocked LockedPtr. + * + * unlock() can only be called on a LockedPtr that is valid. + */ + void unlock() { + DCHECK(parent_ != nullptr); + LockPolicy::unlock(parent_->mutex_); + parent_ = nullptr; + } + + protected: + LockedPtrBase() {} + explicit LockedPtrBase(SynchronizedType* parent) : parent_(parent) { + DCHECK(parent); + if (!LockPolicy::lock(parent_->mutex_)) { + parent_ = nullptr; + } + } + template <class Rep, class Period> + LockedPtrBase( + SynchronizedType* parent, + const std::chrono::duration<Rep, Period>& timeout) { + if (LockPolicy::try_lock_for(parent->mutex_, timeout)) { + this->parent_ = parent; + } + } + LockedPtrBase(LockedPtrBase&& rhs) noexcept + : parent_{std::exchange(rhs.parent_, nullptr)} {} + LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept { + assignImpl(*this, rhs); + return *this; + } + + /** + * Templated move construct and assignment operators + * + * These allow converting LockedPtr types that have the same unlocking + * policy to each other. This allows us to write code like + * + * auto wlock = sync.wlock(); + * wlock.unlock(); + * + * auto ulock = sync.ulock(); + * wlock = ulock.moveFromUpgradeToWrite(); + */ + template <typename LockPolicyType> + LockedPtrBase( + LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept + : parent_{std::exchange(rhs.parent_, nullptr)} {} + template <typename LockPolicyType> + LockedPtrBase& operator=( + LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept { + assignImpl(*this, rhs); + return *this; + } + + /** + * Implementation for the assignment operator + */ + template <typename LockPolicyLhs, typename LockPolicyRhs> + void assignImpl( + LockedPtrBase<SynchronizedType, Mutex, LockPolicyLhs>& lhs, + LockedPtrBase<SynchronizedType, Mutex, LockPolicyRhs>& rhs) noexcept { + if (lhs.parent_) { + LockPolicy::unlock(lhs.parent_->mutex_); + } + + lhs.parent_ = std::exchange(rhs.parent_, nullptr); + } + + using UnlockerData = SynchronizedType*; + + /** + * Get a pointer to the Synchronized object from the UnlockerData. + * + * In the generic case UnlockerData is just the Synchronized pointer, + * so we return it as is. (This function is more interesting in the + * std::mutex specialization below.) + */ + static SynchronizedType* getSynchronized(UnlockerData data) { + return data; + } + + UnlockerData releaseLock() { + DCHECK(parent_ != nullptr); + auto current = parent_; + parent_ = nullptr; + LockPolicy::unlock(current->mutex_); + return current; + } + void reacquireLock(UnlockerData&& data) { + DCHECK(parent_ == nullptr); + parent_ = data; + LockPolicy::lock(parent_->mutex_); + } + + SynchronizedType* parent_ = nullptr; +}; + +/** + * LockedPtrBase specialization for use with std::mutex. + * + * When std::mutex is used we use a std::unique_lock to hold the mutex. + * This makes it possible to use std::condition_variable with a + * Synchronized<T, std::mutex>. + */ +template <class SynchronizedType, class LockPolicy> +class LockedPtrBase<SynchronizedType, std::mutex, LockPolicy> { + public: + using MutexType = std::mutex; + friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>; + + /** + * Friend all instantiations of LockedPtr and LockedPtrBase + */ + template <typename S, typename L> + friend class folly::LockedPtr; + template <typename S, typename M, typename L> + friend class LockedPtrBase; + + /** + * Destructor releases. + */ + ~LockedPtrBase() { + // The std::unique_lock will automatically release the lock when it is + // destroyed, so we don't need to do anything extra here. + } + + LockedPtrBase(LockedPtrBase&& rhs) noexcept + : lock_{std::move(rhs.lock_)}, + parent_{std::exchange(rhs.parent_, nullptr)} {} + LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept { + assignImpl(*this, rhs); + return *this; + } + + /** + * Templated move construct and assignment operators + * + * These allow converting LockedPtr types that have the same unlocking + * policy to each other. + */ + template <typename LockPolicyType> + LockedPtrBase(LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&& + other) noexcept + : lock_{std::move(other.lock_)}, + parent_{std::exchange(other.parent_, nullptr)} {} + template <typename LockPolicyType> + LockedPtrBase& operator=( + LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&& + rhs) noexcept { + assignImpl(*this, rhs); + return *this; + } + + /** + * Implementation for the assignment operator + */ + template <typename LockPolicyLhs, typename LockPolicyRhs> + void assignImpl( + LockedPtrBase<SynchronizedType, std::mutex, LockPolicyLhs>& lhs, + LockedPtrBase<SynchronizedType, std::mutex, LockPolicyRhs>& + rhs) noexcept { + lhs.lock_ = std::move(rhs.lock_); + lhs.parent_ = std::exchange(rhs.parent_, nullptr); + } + + /** + * Get a reference to the std::unique_lock. + * + * This is provided so that callers can use Synchronized<T, std::mutex> + * with a std::condition_variable. + * + * While this API could be used to bypass the normal Synchronized APIs and + * manually interact with the underlying unique_lock, this is strongly + * discouraged. + */ + std::unique_lock<std::mutex>& getUniqueLock() { + return lock_; + } + + /** + * Unlock the synchronized data. + * + * The LockedPtr can no longer be dereferenced after unlock() has been + * called. isValid() will return false on an unlocked LockedPtr. + * + * unlock() can only be called on a LockedPtr that is valid. + */ + void unlock() { + DCHECK(parent_ != nullptr); + lock_.unlock(); + parent_ = nullptr; + } + + protected: + LockedPtrBase() {} + explicit LockedPtrBase(SynchronizedType* parent) + : lock_{parent->mutex_, std::adopt_lock}, parent_{parent} { + DCHECK(parent); + if (!LockPolicy::lock(parent_->mutex_)) { + parent_ = nullptr; + lock_.release(); + } + } + + using UnlockerData = + std::pair<std::unique_lock<std::mutex>, SynchronizedType*>; + + static SynchronizedType* getSynchronized(const UnlockerData& data) { + return data.second; + } + + UnlockerData releaseLock() { + DCHECK(parent_ != nullptr); + UnlockerData data(std::move(lock_), parent_); + parent_ = nullptr; + data.first.unlock(); + return data; + } + void reacquireLock(UnlockerData&& data) { + lock_ = std::move(data.first); + lock_.lock(); + parent_ = data.second; + } + + // The specialization for std::mutex does have to store slightly more + // state than the default implementation. + std::unique_lock<std::mutex> lock_; + SynchronizedType* parent_ = nullptr; +}; + +/** + * This class temporarily unlocks a LockedPtr in a scoped manner. + */ +template <class SynchronizedType, class LockPolicy> +class ScopedUnlocker { + public: + explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p) + : ptr_(p), data_(ptr_->releaseLock()) {} + ScopedUnlocker(const ScopedUnlocker&) = delete; + ScopedUnlocker& operator=(const ScopedUnlocker&) = delete; + ScopedUnlocker(ScopedUnlocker&& other) noexcept + : ptr_(std::exchange(other.ptr_, nullptr)), + data_(std::move(other.data_)) {} + ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete; + + ~ScopedUnlocker() { + if (ptr_) { + ptr_->reacquireLock(std::move(data_)); + } + } + + /** + * Return a pointer to the Synchronized object used by this ScopedUnlocker. + */ + SynchronizedType* getSynchronized() const { + return LockedPtr<SynchronizedType, LockPolicy>::getSynchronized(data_); + } + + private: + using Data = typename LockedPtr<SynchronizedType, LockPolicy>::UnlockerData; + LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr}; + Data data_; +}; + +/** + * A LockedPtr keeps a Synchronized<T> object locked for the duration of + * LockedPtr's existence. + * + * It provides access the datum's members directly by using operator->() and + * operator*(). + * + * The LockPolicy parameter controls whether or not the lock is acquired in + * exclusive or shared mode. + */ +template <class SynchronizedType, class LockPolicy> +class LockedPtr : public LockedPtrBase< + SynchronizedType, + typename SynchronizedType::MutexType, + LockPolicy> { + private: + using Base = LockedPtrBase< + SynchronizedType, + typename SynchronizedType::MutexType, + LockPolicy>; + constexpr static bool AllowsConcurrentAccess = + LockPolicy::allows_concurrent_access; + using UnlockerData = typename Base::UnlockerData; + // CDataType is the DataType with the appropriate const-qualification + using CDataType = + detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>; + // Enable only if the unlock policy of the other LockPolicy is the same as + // ours + template <typename LockPolicyOther> + using EnableIfSameUnlockPolicy = std::enable_if_t<std::is_same< + typename LockPolicy::UnlockPolicy, + typename LockPolicyOther::UnlockPolicy>::value>; + + // friend other LockedPtr types + template <typename SynchronizedTypeOther, typename LockPolicyOther> + friend class LockedPtr; + + public: + using DataType = typename SynchronizedType::DataType; + using MutexType = typename SynchronizedType::MutexType; + using Synchronized = typename std::remove_const<SynchronizedType>::type; + friend class ScopedUnlocker<SynchronizedType, LockPolicy>; + + /** + * Creates an uninitialized LockedPtr. + * + * Dereferencing an uninitialized LockedPtr is not allowed. + */ + LockedPtr() {} + + /** + * Takes a Synchronized<T> and locks it. + */ + explicit LockedPtr(SynchronizedType* parent) : Base(parent) {} + + /** + * Takes a Synchronized<T> and attempts to lock it, within the specified + * timeout. + * + * Blocks until the lock is acquired or until the specified timeout expires. + * If the timeout expired without acquiring the lock, the LockedPtr will be + * null, and LockedPtr::isNull() will return true. + */ + template <class Rep, class Period> + LockedPtr( + SynchronizedType* parent, + const std::chrono::duration<Rep, Period>& timeout) + : Base(parent, timeout) {} + + /** + * Move constructor. + */ + LockedPtr(LockedPtr&& rhs) noexcept = default; + template < + typename LockPolicyType, + EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr> + LockedPtr(LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept + : Base{std::move(other)} {} + + /** + * Move assignment operator. + */ + LockedPtr& operator=(LockedPtr&& rhs) noexcept = default; + template < + typename LockPolicyType, + EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr> + LockedPtr& operator=( + LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept { + Base::operator=(std::move(other)); + return *this; + } + + /* + * Copy constructor and assignment operator are deleted. + */ + LockedPtr(const LockedPtr& rhs) = delete; + LockedPtr& operator=(const LockedPtr& rhs) = delete; + + /** + * Destructor releases. + */ + ~LockedPtr() {} + + /** + * Check if this LockedPtr is uninitialized, or points to valid locked data. + * + * This method can be used to check if a timed-acquire operation succeeded. + * If an acquire operation times out it will result in a null LockedPtr. + * + * A LockedPtr is always either null, or holds a lock to valid data. + * Methods such as scopedUnlock() reset the LockedPtr to null for the + * duration of the unlock. + */ + bool isNull() const { + return this->parent_ == nullptr; + } + + /** + * Explicit boolean conversion. + * + * Returns !isNull() + */ + explicit operator bool() const { + return this->parent_ != nullptr; + } + + /** + * Access the locked data. + * + * This method should only be used if the LockedPtr is valid. + */ + CDataType* operator->() const { + return &this->parent_->datum_; + } + + /** + * Access the locked data. + * + * This method should only be used if the LockedPtr is valid. + */ + CDataType& operator*() const { + return this->parent_->datum_; + } + + /** + * Locks that allow concurrent access (shared, upgrade) force const + * access with the standard accessors even if the Synchronized + * object is non-const. + * + * In some cases non-const access can be needed, for example: + * + * - Under an upgrade lock, to get references that will be mutated + * after upgrading to a write lock. + * + * - Under an read lock, if some mutating operations on the data + * are thread safe (e.g. mutating the value in an associative + * container with reference stability). + * + * asNonConstUnsafe() returns a non-const reference to the data if + * the parent Synchronized object was non-const at the point of lock + * acquisition. + */ + template <typename = void> + DataType& asNonConstUnsafe() const { + static_assert( + AllowsConcurrentAccess && !std::is_const<SynchronizedType>::value, + "asNonConstUnsafe() is only available on non-exclusive locks" + " acquired in a non-const context"); + + return this->parent_->datum_; + } + + /** + * Temporarily unlock the LockedPtr, and reset it to null. + * + * Returns an helper object that will re-lock and restore the LockedPtr when + * the helper is destroyed. The LockedPtr may not be dereferenced for as + * long as this helper object exists. + */ + ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() { + return ScopedUnlocker<SynchronizedType, LockPolicy>(this); + } + + /*************************************************************************** + * Upgrade lock methods. + * These are disabled via SFINAE when the mutex is not an upgrade mutex. + **************************************************************************/ + /** + * Move the locked ptr from an upgrade state to an exclusive state. The + * current lock is left in a null state. + */ + template < + typename SyncType = SynchronizedType, + typename = typename std::enable_if< + LockTraits<typename SyncType::MutexType>::is_upgrade>::type> + LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive> + moveFromUpgradeToWrite() { + return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>( + std::exchange(this->parent_, nullptr)); + } + + /** + * Move the locked ptr from an exclusive state to an upgrade state. The + * current lock is left in a null state. + */ + template < + typename SyncType = SynchronizedType, + typename = typename std::enable_if< + LockTraits<typename SyncType::MutexType>::is_upgrade>::type> + LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade> + moveFromWriteToUpgrade() { + return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>( + std::exchange(this->parent_, nullptr)); + } + + /** + * Move the locked ptr from an upgrade state to a shared state. The + * current lock is left in a null state. + */ + template < + typename SyncType = SynchronizedType, + typename = typename std::enable_if< + LockTraits<typename SyncType::MutexType>::is_upgrade>::type> + LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared> + moveFromUpgradeToRead() { + return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>( + std::exchange(this->parent_, nullptr)); + } + + /** + * Move the locked ptr from an exclusive state to a shared state. The + * current lock is left in a null state. + */ + template < + typename SyncType = SynchronizedType, + typename = typename std::enable_if< + LockTraits<typename SyncType::MutexType>::is_upgrade>::type> + LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared> + moveFromWriteToRead() { + return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>( + std::exchange(this->parent_, nullptr)); + } +}; + +/** + * Helper functions that should be passed to either a lock() or synchronized() + * invocation, these return implementation defined structs that will be used + * to lock the synchronized instance appropriately. + * + * lock(wlock(one), rlock(two), wlock(three)); + * synchronized([](auto one, two) { ... }, wlock(one), rlock(two)); + * + * For example in the above rlock() produces an implementation defined read + * locking helper instance and wlock() a write locking helper + * + * Subsequent arguments passed to these locking helpers, after the first, will + * be passed by const-ref to the corresponding function on the synchronized + * instance. This means that if the function accepts these parameters by + * value, they will be copied. Note that it is not necessary that the primary + * locking function will be invoked at all (for eg. the implementation might + * just invoke the try*Lock() method) + * + * // Try to acquire the lock for one second + * synchronized([](auto) { ... }, wlock(one, 1s)); + * + * // The timed lock acquire might never actually be called, if it is not + * // needed by the underlying deadlock avoiding algorithm + * synchronized([](auto, auto) { ... }, rlock(one), wlock(two, 1s)); + * + * Note that the arguments passed to to *lock() calls will be passed by + * const-ref to the function invocation, as the implementation might use them + * many times + */ +template <typename D, typename M, typename... Args> +auto wlock(Synchronized<D, M>& synchronized, Args&&... args) { + return detail::wlock(synchronized, std::forward<Args>(args)...); +} +template <typename D, typename M, typename... Args> +auto wlock(const Synchronized<D, M>& synchronized, Args&&... args) { + return detail::wlock(synchronized, std::forward<Args>(args)...); +} +template <typename Data, typename Mutex, typename... Args> +auto rlock(const Synchronized<Data, Mutex>& synchronized, Args&&... args) { + return detail::rlock(synchronized, std::forward<Args>(args)...); +} +template <typename D, typename M, typename... Args> +auto ulock(Synchronized<D, M>& synchronized, Args&&... args) { + return detail::ulock(synchronized, std::forward<Args>(args)...); +} +template <typename D, typename M, typename... Args> +auto lock(Synchronized<D, M>& synchronized, Args&&... args) { + return detail::lock(synchronized, std::forward<Args>(args)...); +} +template <typename D, typename M, typename... Args> +auto lock(const Synchronized<D, M>& synchronized, Args&&... args) { + return detail::lock(synchronized, std::forward<Args>(args)...); +} + +/** + * Acquire locks for multiple Synchronized<> objects, in a deadlock-safe + * manner. + * + * Wrap the synchronized instances with the appropriate locking strategy by + * using one of the four strategies - folly::lock (exclusive acquire for + * exclusive only mutexes), folly::rlock (shared acquire for shareable + * mutexes), folly::wlock (exclusive acquire for shareable mutexes) or + * folly::ulock (upgrade acquire for upgrade mutexes) (see above) + * + * The locks will be acquired and the passed callable will be invoked with the + * LockedPtr instances in the order that they were passed to the function + */ +template <typename Func, typename... SynchronizedLockers> +decltype(auto) synchronized(Func&& func, SynchronizedLockers&&... lockers) { + return apply( + std::forward<Func>(func), + lock(std::forward<SynchronizedLockers>(lockers)...)); +} + +/** + * Acquire locks on many lockables or synchronized instances in such a way + * that the sequence of calls within the function does not cause deadlocks. + * + * This can often result in a performance boost as compared to simply + * acquiring your locks in an ordered manner. Even for very simple cases. + * The algorithm tried to adjust to contention by blocking on the mutex it + * thinks is the best fit, leaving all other mutexes open to be locked by + * other threads. See the benchmarks in folly/test/SynchronizedBenchmark.cpp + * for more + * + * This works differently as compared to the locking algorithm in libstdc++ + * and is the recommended way to acquire mutexes in a generic order safe + * manner. Performance benchmarks show that this does better than the one in + * libstdc++ even for the simple cases + * + * Usage is the same as std::lock() for arbitrary lockables + * + * folly::lock(one, two, three); + * + * To make it work with folly::Synchronized you have to specify how you want + * the locks to be acquired, use the folly::wlock(), folly::rlock(), + * folly::ulock() and folly::lock() helpers defined below + * + * auto [one, two] = lock(folly::wlock(a), folly::rlock(b)); + * + * Note that you can/must avoid the folly:: namespace prefix on the lock() + * function if you use the helpers, ADL lookup is done to find the lock function + * + * This will execute the deadlock avoidance algorithm and acquire a write lock + * for a and a read lock for b + */ +template <typename LockableOne, typename LockableTwo, typename... Lockables> +void lock(LockableOne& one, LockableTwo& two, Lockables&... lockables) { + auto locker = [](auto& lockable) { + using Lockable = std::remove_reference_t<decltype(lockable)>; + return detail::makeSynchronizedLocker( + lockable, + [](auto& l) { return std::unique_lock<Lockable>{l}; }, + [](auto& l) { + auto lock = std::unique_lock<Lockable>{l, std::defer_lock}; + lock.try_lock(); + return lock; + }); + }; + auto locks = lock(locker(one), locker(two), locker(lockables)...); + + // release ownership of the locks from the RAII lock wrapper returned by the + // function above + for_each(locks, [&](auto& lock) { lock.release(); }); +} + +/** + * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe + * manner. + * + * The locks are acquired in order from lowest address to highest address. + * (Note that this is not necessarily the same algorithm used by std::lock().) + * For parameters that are const and support shared locks, a read lock is + * acquired. Otherwise an exclusive lock is acquired. + * + * use lock() with folly::wlock(), folly::rlock() and folly::ulock() for + * arbitrary locking without causing a deadlock (as much as possible), with the + * same effects as std::lock() + */ +template <class Sync1, class Sync2> +std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>> +acquireLocked(Sync1& l1, Sync2& l2) { + if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) { + auto p1 = l1.contextualLock(); + auto p2 = l2.contextualLock(); + return std::make_tuple(std::move(p1), std::move(p2)); + } else { + auto p2 = l2.contextualLock(); + auto p1 = l1.contextualLock(); + return std::make_tuple(std::move(p1), std::move(p2)); + } +} + +/** + * A version of acquireLocked() that returns a std::pair rather than a + * std::tuple, which is easier to use in many places. + */ +template <class Sync1, class Sync2> +std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>> +acquireLockedPair(Sync1& l1, Sync2& l2) { + auto lockedPtrs = acquireLocked(l1, l2); + return {std::move(std::get<0>(lockedPtrs)), + std::move(std::get<1>(lockedPtrs))}; +} + +/************************************************************************ + * NOTE: All APIs below this line will be deprecated in upcoming diffs. + ************************************************************************/ + +// Non-member swap primitive +template <class T, class M> +void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) { + lhs.swap(rhs); +} + +/** + * Disambiguate the name var by concatenating the line number of the original + * point of expansion. This avoids shadowing warnings for nested + * SYNCHRONIZEDs. The name is consistent if used multiple times within + * another macro. + * Only for internal use. + */ +#define SYNCHRONIZED_VAR(var) FB_CONCATENATE(SYNCHRONIZED_##var##_, __LINE__) + +namespace detail { +struct [[deprecated( + "use explicit lock(), wlock(), or rlock() instead")]] SYNCHRONIZED_macro_is_deprecated{}; +} + +/** + * NOTE: This API is deprecated. Use lock(), wlock(), rlock() or the withLock + * functions instead. In the future it will be marked with a deprecation + * attribute to emit build-time warnings, and then it will be removed entirely. + * + * SYNCHRONIZED is the main facility that makes Synchronized<T> + * helpful. It is a pseudo-statement that introduces a scope where the + * object is locked. Inside that scope you get to access the unadorned + * datum. + * + * Example: + * + * Synchronized<vector<int>> svector; + * ... + * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... } + * or + * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... } + * + * Refer to folly/docs/Synchronized.md for a detailed explanation and more + * examples. + */ +#define SYNCHRONIZED(...) \ + FOLLY_PUSH_WARNING \ + FOLLY_GNU_DISABLE_WARNING("-Wshadow") \ + FOLLY_MSVC_DISABLE_WARNING(4189) /* initialized but unreferenced */ \ + FOLLY_MSVC_DISABLE_WARNING(4456) /* declaration hides local */ \ + FOLLY_MSVC_DISABLE_WARNING(4457) /* declaration hides parameter */ \ + FOLLY_MSVC_DISABLE_WARNING(4458) /* declaration hides member */ \ + FOLLY_MSVC_DISABLE_WARNING(4459) /* declaration hides global */ \ + FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS \ + if (bool SYNCHRONIZED_VAR(state) = false) { \ + ::folly::detail::SYNCHRONIZED_macro_is_deprecated{}; \ + } else \ + for (auto SYNCHRONIZED_VAR(lockedPtr) = \ + (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).contextualLock(); \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) \ + for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) = \ + *SYNCHRONIZED_VAR(lockedPtr).operator->(); \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) \ + FOLLY_POP_WARNING + +/** + * NOTE: This API is deprecated. Use lock(), wlock(), rlock() or the withLock + * functions instead. In the future it will be marked with a deprecation + * attribute to emit build-time warnings, and then it will be removed entirely. + */ +#define TIMED_SYNCHRONIZED(timeout, ...) \ + if (bool SYNCHRONIZED_VAR(state) = false) { \ + ::folly::detail::SYNCHRONIZED_macro_is_deprecated{}; \ + } else \ + for (auto SYNCHRONIZED_VAR(lockedPtr) = \ + (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).timedAcquire(timeout); \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) \ + for (auto FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) = \ + (!SYNCHRONIZED_VAR(lockedPtr) \ + ? nullptr \ + : SYNCHRONIZED_VAR(lockedPtr).operator->()); \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) + +/** + * NOTE: This API is deprecated. Use lock(), wlock(), rlock() or the withLock + * functions instead. In the future it will be marked with a deprecation + * attribute to emit build-time warnings, and then it will be removed entirely. + * + * Similar to SYNCHRONIZED, but only uses a read lock. + */ +#define SYNCHRONIZED_CONST(...) \ + SYNCHRONIZED( \ + FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \ + as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__)))) + +/** + * NOTE: This API is deprecated. Use lock(), wlock(), rlock() or the withLock + * functions instead. In the future it will be marked with a deprecation + * attribute to emit build-time warnings, and then it will be removed entirely. + * + * Similar to TIMED_SYNCHRONIZED, but only uses a read lock. + */ +#define TIMED_SYNCHRONIZED_CONST(timeout, ...) \ + TIMED_SYNCHRONIZED( \ + timeout, \ + FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \ + as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__)))) + +/** + * NOTE: This API is deprecated. Use lock(), wlock(), rlock() or the withLock + * functions instead. In the future it will be marked with a deprecation + * attribute to emit build-time warnings, and then it will be removed entirely. + * + * Synchronizes two Synchronized objects (they may encapsulate + * different data). Synchronization is done in increasing address of + * object order, so there is no deadlock risk. + */ +#define SYNCHRONIZED_DUAL(n1, e1, n2, e2) \ + if (bool SYNCHRONIZED_VAR(state) = false) { \ + ::folly::detail::SYNCHRONIZED_macro_is_deprecated{}; \ + } else \ + for (auto SYNCHRONIZED_VAR(ptrs) = acquireLockedPair(e1, e2); \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) \ + for (auto& n1 = *SYNCHRONIZED_VAR(ptrs).first; !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) \ + for (auto& n2 = *SYNCHRONIZED_VAR(ptrs).second; \ + !SYNCHRONIZED_VAR(state); \ + SYNCHRONIZED_VAR(state) = true) + +} /* namespace folly */ diff --git a/ios/Pods/Flipper-Folly/folly/SynchronizedPtr.h b/ios/Pods/Flipper-Folly/folly/SynchronizedPtr.h new file mode 100644 index 000000000..53bb09d98 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/SynchronizedPtr.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Synchronized.h> + +/* `SynchronizedPtr` is a variation on the `Synchronized` idea that's useful for + * some cases where you want to protect a pointed-to object (or an object within + * some pointer-like wrapper). If you would otherwise need to use + * `Synchronized<smart_ptr<Synchronized<T>>>` consider using + * `SynchronizedPtr<smart_ptr<T>>`as it is a bit easier to use and it works when + * you want the `T` object at runtime to actually a subclass of `T`. + * + * You can access the contained `T` with `.rlock()`, and `.wlock()`, and the + * pointer or pointer-like wrapper with `.wlockPointer()`. The corresponding + * `with...` methods take a callback, invoke it with a `T const&`, `T&` or + * `smart_ptr<T>&` respectively, and return the callback's result. + */ +namespace folly { +template <typename LockHolder, typename Element> +struct SynchronizedPtrLockedElement { + explicit SynchronizedPtrLockedElement(LockHolder&& holder) + : holder_(std::move(holder)) {} + + Element& operator*() const { + return **holder_; + } + + Element* operator->() const { + return &**holder_; + } + + explicit operator bool() const { + return static_cast<bool>(*holder_); + } + + private: + LockHolder holder_; +}; + +template <typename PointerType, typename MutexType = SharedMutex> +class SynchronizedPtr { + using inner_type = Synchronized<PointerType, MutexType>; + inner_type inner_; + + public: + using pointer_type = PointerType; + using element_type = typename std::pointer_traits<pointer_type>::element_type; + using const_element_type = typename std::add_const<element_type>::type; + using read_locked_element = SynchronizedPtrLockedElement< + typename inner_type::ConstLockedPtr, + const_element_type>; + using write_locked_element = SynchronizedPtrLockedElement< + typename inner_type::LockedPtr, + element_type>; + using write_locked_pointer = typename inner_type::LockedPtr; + + template <typename... Args> + explicit SynchronizedPtr(Args... args) + : inner_(std::forward<Args>(args)...) {} + + SynchronizedPtr() = default; + SynchronizedPtr(SynchronizedPtr const&) = default; + SynchronizedPtr(SynchronizedPtr&&) = default; + SynchronizedPtr& operator=(SynchronizedPtr const&) = default; + SynchronizedPtr& operator=(SynchronizedPtr&&) = default; + + // Methods to provide appropriately locked and const-qualified access to the + // element. + + read_locked_element rlock() const { + return read_locked_element(inner_.rlock()); + } + + template <class Function> + auto withRLock(Function&& function) const { + return function(*rlock()); + } + + write_locked_element wlock() { + return write_locked_element(inner_.wlock()); + } + + template <class Function> + auto withWLock(Function&& function) { + return function(*wlock()); + } + + // Methods to provide write-locked access to the pointer. We deliberately make + // it difficult to get a read-locked pointer because that provides read-locked + // non-const access to the element, and the purpose of this class is to + // discourage that. + write_locked_pointer wlockPointer() { + return inner_.wlock(); + } + + template <class Function> + auto withWLockPointer(Function&& function) { + return function(*wlockPointer()); + } +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ThreadCachedInt.h b/ios/Pods/Flipper-Folly/folly/ThreadCachedInt.h new file mode 100644 index 000000000..51f2f91a4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ThreadCachedInt.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Higher performance (up to 10x) atomic increment using thread caching. + * + * @author Spencer Ahrens (sahrens) + */ + +#pragma once + +#include <atomic> + +#include <folly/Likely.h> +#include <folly/ThreadLocal.h> + +namespace folly { + +// Note that readFull requires holding a lock and iterating through all of the +// thread local objects with the same Tag, so if you have a lot of +// ThreadCachedInt's you should considering breaking up the Tag space even +// further. +template <class IntT, class Tag = IntT> +class ThreadCachedInt { + struct IntCache; + + public: + explicit ThreadCachedInt(IntT initialVal = 0, uint32_t cacheSize = 1000) + : target_(initialVal), cacheSize_(cacheSize) {} + + ThreadCachedInt(const ThreadCachedInt&) = delete; + ThreadCachedInt& operator=(const ThreadCachedInt&) = delete; + + void increment(IntT inc) { + auto cache = cache_.get(); + if (UNLIKELY(cache == nullptr)) { + cache = new IntCache(*this); + cache_.reset(cache); + } + cache->increment(inc); + } + + // Quickly grabs the current value which may not include some cached + // increments. + IntT readFast() const { + return target_.load(std::memory_order_relaxed); + } + + // Reads the current value plus all the cached increments. Requires grabbing + // a lock, so this is significantly slower than readFast(). + IntT readFull() const { + // This could race with thread destruction and so the access lock should be + // acquired before reading the current value + const auto accessor = cache_.accessAllThreads(); + IntT ret = readFast(); + for (const auto& cache : accessor) { + if (!cache.reset_.load(std::memory_order_acquire)) { + ret += cache.val_.load(std::memory_order_relaxed); + } + } + return ret; + } + + // Quickly reads and resets current value (doesn't reset cached increments). + IntT readFastAndReset() { + return target_.exchange(0, std::memory_order_release); + } + + // This function is designed for accumulating into another counter, where you + // only want to count each increment once. It can still get the count a + // little off, however, but it should be much better than calling readFull() + // and set(0) sequentially. + IntT readFullAndReset() { + // This could race with thread destruction and so the access lock should be + // acquired before reading the current value + auto accessor = cache_.accessAllThreads(); + IntT ret = readFastAndReset(); + for (auto& cache : accessor) { + if (!cache.reset_.load(std::memory_order_acquire)) { + ret += cache.val_.load(std::memory_order_relaxed); + cache.reset_.store(true, std::memory_order_release); + } + } + return ret; + } + + void setCacheSize(uint32_t newSize) { + cacheSize_.store(newSize, std::memory_order_release); + } + + uint32_t getCacheSize() const { + return cacheSize_.load(); + } + + ThreadCachedInt& operator+=(IntT inc) { + increment(inc); + return *this; + } + ThreadCachedInt& operator-=(IntT inc) { + increment(-inc); + return *this; + } + // pre-increment (we don't support post-increment) + ThreadCachedInt& operator++() { + increment(1); + return *this; + } + ThreadCachedInt& operator--() { + increment(IntT(-1)); + return *this; + } + + // Thread-safe set function. + // This is a best effort implementation. In some edge cases, there could be + // data loss (missing counts) + void set(IntT newVal) { + for (auto& cache : cache_.accessAllThreads()) { + cache.reset_.store(true, std::memory_order_release); + } + target_.store(newVal, std::memory_order_release); + } + + private: + std::atomic<IntT> target_; + std::atomic<uint32_t> cacheSize_; + ThreadLocalPtr<IntCache, Tag, AccessModeStrict> + cache_; // Must be last for dtor ordering + + // This should only ever be modified by one thread + struct IntCache { + ThreadCachedInt* parent_; + mutable std::atomic<IntT> val_; + mutable uint32_t numUpdates_; + std::atomic<bool> reset_; + + explicit IntCache(ThreadCachedInt& parent) + : parent_(&parent), val_(0), numUpdates_(0), reset_(false) {} + + void increment(IntT inc) { + if (LIKELY(!reset_.load(std::memory_order_acquire))) { + // This thread is the only writer to val_, so it's fine do do + // a relaxed load and do the addition non-atomically. + val_.store( + val_.load(std::memory_order_relaxed) + inc, + std::memory_order_release); + } else { + val_.store(inc, std::memory_order_relaxed); + reset_.store(false, std::memory_order_release); + } + ++numUpdates_; + if (UNLIKELY( + numUpdates_ > + parent_->cacheSize_.load(std::memory_order_acquire))) { + flush(); + } + } + + void flush() const { + parent_->target_.fetch_add(val_, std::memory_order_release); + val_.store(0, std::memory_order_release); + numUpdates_ = 0; + } + + ~IntCache() { + flush(); + } + }; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ThreadLocal.h b/ios/Pods/Flipper-Folly/folly/ThreadLocal.h new file mode 100644 index 000000000..aa2c22352 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ThreadLocal.h @@ -0,0 +1,493 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Improved thread local storage for non-trivial types (similar speed as + * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster + * than boost::thread_specific_ptr). + * + * Also includes an accessor interface to walk all the thread local child + * objects of a parent. accessAllThreads() initializes an accessor which holds + * a global lock *that blocks all creation and destruction of ThreadLocal + * objects with the same Tag* and can be used as an iterable container. + * accessAllThreads() can race with destruction of thread-local elements. We + * provide a strict mode which is dangerous because it requires the access lock + * to be held while destroying thread-local elements which could cause + * deadlocks. We gate this mode behind the AccessModeStrict template parameter. + * + * Intended use is for frequent write, infrequent read data access patterns such + * as counters. + * + * There are two classes here - ThreadLocal and ThreadLocalPtr. ThreadLocalPtr + * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin + * wrapper around ThreadLocalPtr that manages allocation automatically. + * + * @author Spencer Ahrens (sahrens) + */ + +#pragma once + +#include <iterator> +#include <thread> +#include <type_traits> +#include <utility> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/SharedMutex.h> +#include <folly/detail/ThreadLocalDetail.h> + +namespace folly { + +template <class T, class Tag, class AccessMode> +class ThreadLocalPtr; + +template <class T, class Tag = void, class AccessMode = void> +class ThreadLocal { + public: + constexpr ThreadLocal() : constructor_([]() { return new T(); }) {} + + template <typename F, std::enable_if_t<is_invocable_r_v<T*, F>, int> = 0> + explicit ThreadLocal(F&& constructor) + : constructor_(std::forward<F>(constructor)) {} + + FOLLY_ERASE T* get() const { + auto const ptr = tlp_.get(); + return FOLLY_LIKELY(!!ptr) ? ptr : makeTlp(); + } + + T* operator->() const { + return get(); + } + + T& operator*() const { + return *get(); + } + + void reset(T* newPtr = nullptr) { + tlp_.reset(newPtr); + } + + typedef typename ThreadLocalPtr<T, Tag, AccessMode>::Accessor Accessor; + Accessor accessAllThreads() const { + return tlp_.accessAllThreads(); + } + + // movable + ThreadLocal(ThreadLocal&&) = default; + ThreadLocal& operator=(ThreadLocal&&) = default; + + private: + // non-copyable + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; + + FOLLY_NOINLINE T* makeTlp() const { + auto const ptr = constructor_(); + tlp_.reset(ptr); + return ptr; + } + + mutable ThreadLocalPtr<T, Tag, AccessMode> tlp_; + std::function<T*()> constructor_; +}; + +/* + * The idea here is that __thread is faster than pthread_getspecific, so we + * keep a __thread array of pointers to objects (ThreadEntry::elements) where + * each array has an index for each unique instance of the ThreadLocalPtr + * object. Each ThreadLocalPtr object has a unique id that is an index into + * these arrays so we can fetch the correct object from thread local storage + * very efficiently. + * + * In order to prevent unbounded growth of the id space and thus huge + * ThreadEntry::elements, arrays, for example due to continuous creation and + * destruction of ThreadLocalPtr objects, we keep a set of all active + * instances. When an instance is destroyed we remove it from the active + * set and insert the id into freeIds_ for reuse. These operations require a + * global mutex, but only happen at construction and destruction time. + * + * We use a single global pthread_key_t per Tag to manage object destruction and + * memory cleanup upon thread exit because there is a finite number of + * pthread_key_t's available per machine. + * + * NOTE: Apple platforms don't support the same semantics for __thread that + * Linux does (and it's only supported at all on i386). For these, use + * pthread_setspecific()/pthread_getspecific() for the per-thread + * storage. Windows (MSVC and GCC) does support the same semantics + * with __declspec(thread) + */ + +template <class T, class Tag = void, class AccessMode = void> +class ThreadLocalPtr { + private: + typedef threadlocal_detail::StaticMeta<Tag, AccessMode> StaticMeta; + + using AccessAllThreadsEnabled = Negation<std::is_same<Tag, void>>; + + public: + constexpr ThreadLocalPtr() : id_() {} + + ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : id_(std::move(other.id_)) {} + + ThreadLocalPtr& operator=(ThreadLocalPtr&& other) { + assert(this != &other); + destroy(); + id_ = std::move(other.id_); + return *this; + } + + ~ThreadLocalPtr() { + destroy(); + } + + T* get() const { + threadlocal_detail::ElementWrapper& w = StaticMeta::get(&id_); + return static_cast<T*>(w.ptr); + } + + T* operator->() const { + return get(); + } + + T& operator*() const { + return *get(); + } + + T* release() { + auto rlock = getAccessAllThreadsLockReadHolderIfEnabled(); + + threadlocal_detail::ElementWrapper& w = StaticMeta::get(&id_); + + return static_cast<T*>(w.release()); + } + + void reset(T* newPtr = nullptr) { + auto rlock = getAccessAllThreadsLockReadHolderIfEnabled(); + + auto guard = makeGuard([&] { delete newPtr; }); + threadlocal_detail::ElementWrapper* w = &StaticMeta::get(&id_); + + w->dispose(TLPDestructionMode::THIS_THREAD); + // need to get a new ptr since the + // ThreadEntry::elements array can be reallocated + w = &StaticMeta::get(&id_); + w->cleanup(); + guard.dismiss(); + w->set(newPtr); + } + + explicit operator bool() const { + return get() != nullptr; + } + + /** + * reset() that transfers ownership from a smart pointer + */ + template < + typename SourceT, + typename Deleter, + typename = typename std::enable_if< + std::is_convertible<SourceT*, T*>::value>::type> + void reset(std::unique_ptr<SourceT, Deleter> source) { + auto deleter = [delegate = source.get_deleter()]( + T* ptr, TLPDestructionMode) { delegate(ptr); }; + reset(source.release(), deleter); + } + + /** + * reset() that transfers ownership from a smart pointer with the default + * deleter + */ + template < + typename SourceT, + typename = typename std::enable_if< + std::is_convertible<SourceT*, T*>::value>::type> + void reset(std::unique_ptr<SourceT> source) { + reset(source.release()); + } + + /** + * reset() with a custom deleter: + * deleter(T* ptr, TLPDestructionMode mode) + * "mode" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus + * deleting pointers for all threads), and THIS_THREAD if we're only deleting + * the member for one thread (because of thread exit or reset()). + * Invoking the deleter must not throw. + */ + template <class Deleter> + void reset(T* newPtr, const Deleter& deleter) { + auto rlock = getAccessAllThreadsLockReadHolderIfEnabled(); + + auto guard = makeGuard([&] { + if (newPtr) { + deleter(newPtr, TLPDestructionMode::THIS_THREAD); + } + }); + threadlocal_detail::ElementWrapper* w = &StaticMeta::get(&id_); + w->dispose(TLPDestructionMode::THIS_THREAD); + // need to get a new ptr since the + // ThreadEntry::elements array can be reallocated + w = &StaticMeta::get(&id_); + w->cleanup(); + guard.dismiss(); + w->set(newPtr, deleter); + } + + // Holds a global lock for iteration through all thread local child objects. + // Can be used as an iterable container. + // Use accessAllThreads() to obtain one. + class Accessor { + friend class ThreadLocalPtr<T, Tag, AccessMode>; + + threadlocal_detail::StaticMetaBase& meta_; + SharedMutex* accessAllThreadsLock_; + std::mutex* lock_; + uint32_t id_; + + public: + class Iterator; + friend class Iterator; + + // The iterators obtained from Accessor are bidirectional iterators. + class Iterator { + friend class Accessor; + const Accessor* accessor_; + threadlocal_detail::ThreadEntryNode* e_; + + void increment() { + e_ = e_->getNext(); + incrementToValid(); + } + + void decrement() { + e_ = e_->getPrev(); + decrementToValid(); + } + + const T& dereference() const { + return *static_cast<T*>( + e_->getThreadEntry()->elements[accessor_->id_].ptr); + } + + T& dereference() { + return *static_cast<T*>( + e_->getThreadEntry()->elements[accessor_->id_].ptr); + } + + bool equal(const Iterator& other) const { + return (accessor_->id_ == other.accessor_->id_ && e_ == other.e_); + } + + explicit Iterator(const Accessor* accessor) + : accessor_(accessor), + e_(&accessor_->meta_.head_.elements[accessor_->id_].node) {} + + // we just need to check the ptr since it can be set to nullptr + // even if the entry is part of the list + bool valid() const { + return (e_->getThreadEntry()->elements[accessor_->id_].ptr); + } + + void incrementToValid() { + for (; e_ != &accessor_->meta_.head_.elements[accessor_->id_].node && + !valid(); + e_ = e_->getNext()) { + } + } + + void decrementToValid() { + for (; e_ != &accessor_->meta_.head_.elements[accessor_->id_].node && + !valid(); + e_ = e_->getPrev()) { + } + } + + public: + using difference_type = ssize_t; + using value_type = T; + using reference = T const&; + using pointer = T const*; + using iterator_category = std::bidirectional_iterator_tag; + + Iterator& operator++() { + increment(); + return *this; + } + + Iterator& operator++(int) { + Iterator copy(*this); + increment(); + return copy; + } + + Iterator& operator--() { + decrement(); + return *this; + } + + Iterator& operator--(int) { + Iterator copy(*this); + decrement(); + return copy; + } + + T& operator*() { + return dereference(); + } + + T const& operator*() const { + return dereference(); + } + + T* operator->() { + return &dereference(); + } + + T const* operator->() const { + return &dereference(); + } + + bool operator==(Iterator const& rhs) const { + return equal(rhs); + } + + bool operator!=(Iterator const& rhs) const { + return !equal(rhs); + } + + std::thread::id getThreadId() const { + return e_->getThreadEntry()->tid(); + } + + uint64_t getOSThreadId() const { + return e_->getThreadEntry()->tid_os; + } + }; + + ~Accessor() { + release(); + } + + Iterator begin() const { + return ++Iterator(this); + } + + Iterator end() const { + return Iterator(this); + } + + Accessor(const Accessor&) = delete; + Accessor& operator=(const Accessor&) = delete; + + Accessor(Accessor&& other) noexcept + : meta_(other.meta_), + accessAllThreadsLock_(other.accessAllThreadsLock_), + lock_(other.lock_), + id_(other.id_) { + other.id_ = 0; + other.accessAllThreadsLock_ = nullptr; + other.lock_ = nullptr; + } + + Accessor& operator=(Accessor&& other) noexcept { + // Each Tag has its own unique meta, and accessors with different Tags + // have different types. So either *this is empty, or this and other + // have the same tag. But if they have the same tag, they have the same + // meta (and lock), so they'd both hold the lock at the same time, + // which is impossible, which leaves only one possible scenario -- + // *this is empty. Assert it. + assert(&meta_ == &other.meta_); + assert(lock_ == nullptr); + using std::swap; + swap(accessAllThreadsLock_, other.accessAllThreadsLock_); + swap(lock_, other.lock_); + swap(id_, other.id_); + } + + Accessor() + : meta_(threadlocal_detail::StaticMeta<Tag, AccessMode>::instance()), + accessAllThreadsLock_(nullptr), + lock_(nullptr), + id_(0) {} + + private: + explicit Accessor(uint32_t id) + : meta_(threadlocal_detail::StaticMeta<Tag, AccessMode>::instance()), + accessAllThreadsLock_(&meta_.accessAllThreadsLock_), + lock_(&meta_.lock_) { + accessAllThreadsLock_->lock(); + lock_->lock(); + id_ = id; + } + + void release() { + if (lock_) { + lock_->unlock(); + DCHECK(accessAllThreadsLock_ != nullptr); + accessAllThreadsLock_->unlock(); + id_ = 0; + lock_ = nullptr; + accessAllThreadsLock_ = nullptr; + } + } + }; + + // accessor allows a client to iterate through all thread local child + // elements of this ThreadLocal instance. Holds a global lock for each <Tag> + Accessor accessAllThreads() const { + static_assert( + AccessAllThreadsEnabled::value, + "Must use a unique Tag to use the accessAllThreads feature"); + return Accessor(id_.getOrAllocate(StaticMeta::instance())); + } + + private: + void destroy() { + StaticMeta::instance().destroy(&id_); + } + + // non-copyable + ThreadLocalPtr(const ThreadLocalPtr&) = delete; + ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete; + + static auto getAccessAllThreadsLockReadHolderIfEnabled() { + return SharedMutex::ReadHolder( + AccessAllThreadsEnabled::value + ? &StaticMeta::instance().accessAllThreadsLock_ + : nullptr); + } + + mutable typename StaticMeta::EntryID id_; +}; + +namespace threadlocal_detail { +template <typename> +struct static_meta_of; + +template <typename T, typename Tag, typename AccessMode> +struct static_meta_of<ThreadLocalPtr<T, Tag, AccessMode>> { + using type = StaticMeta<Tag, AccessMode>; +}; + +template <typename T, typename Tag, typename AccessMode> +struct static_meta_of<ThreadLocal<T, Tag, AccessMode>> { + using type = StaticMeta<Tag, AccessMode>; +}; + +} // namespace threadlocal_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/TimeoutQueue.cpp b/ios/Pods/Flipper-Folly/folly/TimeoutQueue.cpp new file mode 100644 index 000000000..706715f71 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/TimeoutQueue.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/TimeoutQueue.h> +#include <algorithm> +#include <vector> + +namespace folly { + +TimeoutQueue::Id +TimeoutQueue::add(int64_t now, int64_t delay, Callback callback) { + Id id = nextId_++; + timeouts_.insert({id, now + delay, -1, std::move(callback)}); + return id; +} + +TimeoutQueue::Id +TimeoutQueue::addRepeating(int64_t now, int64_t interval, Callback callback) { + Id id = nextId_++; + timeouts_.insert({id, now + interval, interval, std::move(callback)}); + return id; +} + +int64_t TimeoutQueue::nextExpiration() const { + return ( + timeouts_.empty() ? std::numeric_limits<int64_t>::max() + : timeouts_.get<BY_EXPIRATION>().begin()->expiration); +} + +bool TimeoutQueue::erase(Id id) { + return timeouts_.get<BY_ID>().erase(id); +} + +int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) { + auto& byExpiration = timeouts_.get<BY_EXPIRATION>(); + int64_t nextExp; + do { + const auto end = byExpiration.upper_bound(now); + std::vector<Event> expired; + std::move(byExpiration.begin(), end, std::back_inserter(expired)); + byExpiration.erase(byExpiration.begin(), end); + for (const auto& event : expired) { + // Reinsert if repeating, do this before executing callbacks + // so the callbacks have a chance to call erase + if (event.repeatInterval >= 0) { + timeouts_.insert({event.id, + now + event.repeatInterval, + event.repeatInterval, + event.callback}); + } + } + + // Call callbacks + for (const auto& event : expired) { + event.callback(event.id, now); + } + nextExp = nextExpiration(); + } while (!onceOnly && nextExp <= now); + return nextExp; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/TimeoutQueue.h b/ios/Pods/Flipper-Folly/folly/TimeoutQueue.h new file mode 100644 index 000000000..af880c30e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/TimeoutQueue.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Simple timeout queue. Call user-specified callbacks when their timeouts + * expire. + * + * This class assumes that "time" is an int64_t and doesn't care about time + * units (seconds, milliseconds, etc). You call runOnce() / runLoop() using + * the same time units that you use to specify callbacks. + * + * @author Tudor Bosman (tudorb@fb.com) + */ + +#pragma once + +#include <cstdint> +#include <functional> + +#include <boost/multi_index/indexed_by.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/multi_index_container.hpp> + +namespace folly { + +class TimeoutQueue { + public: + typedef int64_t Id; + typedef std::function<void(Id, int64_t)> Callback; + + TimeoutQueue() : nextId_(1) {} + + /** + * Add a one-time timeout event that will fire "delay" time units from "now" + * (that is, the first time that run*() is called with a time value >= now + * + delay). + */ + Id add(int64_t now, int64_t delay, Callback callback); + + /** + * Add a repeating timeout event that will fire every "interval" time units + * (it will first fire when run*() is called with a time value >= + * now + interval). + * + * run*() will always invoke each repeating event at most once, even if + * more than one "interval" period has passed. + */ + Id addRepeating(int64_t now, int64_t interval, Callback callback); + + /** + * Erase a given timeout event, returns true if the event was actually + * erased and false if it didn't exist in our queue. + */ + bool erase(Id id); + + /** + * Process all events that are due at times <= "now" by calling their + * callbacks. + * + * Callbacks are allowed to call back into the queue and add / erase events; + * they might create more events that are already due. In this case, + * runOnce() will only go through the queue once, and return a "next + * expiration" time in the past or present (<= now); runLoop() + * will process the queue again, until there are no events already due. + * + * Note that it is then possible for runLoop to never return if + * callbacks re-add themselves to the queue (or if you have repeating + * callbacks with an interval of 0). + * + * Return the time that the next event will be due (same as + * nextExpiration(), below) + */ + int64_t runOnce(int64_t now) { + return runInternal(now, true); + } + int64_t runLoop(int64_t now) { + return runInternal(now, false); + } + + /** + * Return the time that the next event will be due. + */ + int64_t nextExpiration() const; + + private: + int64_t runInternal(int64_t now, bool onceOnly); + TimeoutQueue(const TimeoutQueue&) = delete; + TimeoutQueue& operator=(const TimeoutQueue&) = delete; + + struct Event { + Id id; + int64_t expiration; + int64_t repeatInterval; + Callback callback; + }; + + typedef boost::multi_index_container< + Event, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::member<Event, Id, &Event::id>>, + boost::multi_index::ordered_non_unique< + boost::multi_index::member<Event, int64_t, &Event::expiration>>>> + Set; + + enum { + BY_ID = 0, + BY_EXPIRATION = 1, + }; + + Set timeouts_; + Id nextId_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/TokenBucket.h b/ios/Pods/Flipper-Folly/folly/TokenBucket.h new file mode 100644 index 000000000..d80d8ebbc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/TokenBucket.h @@ -0,0 +1,518 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <chrono> +#include <thread> + +#include <folly/Likely.h> +#include <folly/Optional.h> +#include <folly/concurrency/CacheLocality.h> + +namespace folly { + +/** + * Thread-safe (atomic) token bucket implementation. + * + * A token bucket (http://en.wikipedia.org/wiki/Token_bucket) models a stream + * of events with an average rate and some amount of burstiness. The canonical + * example is a packet switched network: the network can accept some number of + * bytes per second and the bytes come in finite packets (bursts). A token + * bucket stores up to a fixed number of tokens (the burst size). Some number + * of tokens are removed when an event occurs. The tokens are replenished at a + * fixed rate. Failure to allocate tokens implies resource is unavailable and + * caller needs to implement its own retry mechanism. For simple cases where + * caller is okay with a FIFO starvation-free scheduling behavior, there are + * also APIs to 'borrow' from the future effectively assigning a start time to + * the caller when it should proceed with using the resource. It is also + * possible to 'return' previously allocated tokens to make them available to + * other users. Returns in excess of burstSize are considered expired and + * will not be available to later callers. + * + * This implementation records the last time it was updated. This allows the + * token bucket to add tokens "just in time" when tokens are requested. + * + * The "dynamic" base variant allows the token generation rate and maximum + * burst size to change with every token consumption. + * + * @tparam Clock Clock type, must be steady i.e. monotonic. + */ +template <typename Clock = std::chrono::steady_clock> +class BasicDynamicTokenBucket { + static_assert(Clock::is_steady, "clock must be steady"); + + public: + /** + * Constructor. + * + * @param zeroTime Initial time at which to consider the token bucket + * starting to fill. Defaults to 0, so by default token + * buckets are "full" after construction. + */ + explicit BasicDynamicTokenBucket(double zeroTime = 0) noexcept + : zeroTime_(zeroTime) {} + + /** + * Copy constructor. + * + * Thread-safe. (Copy constructors of derived classes may not be thread-safe + * however.) + */ + BasicDynamicTokenBucket(const BasicDynamicTokenBucket& other) noexcept + : zeroTime_(other.zeroTime_.load()) {} + + /** + * Copy-assignment operator. + * + * Warning: not thread safe for the object being assigned to (including + * self-assignment). Thread-safe for the other object. + */ + BasicDynamicTokenBucket& operator=( + const BasicDynamicTokenBucket& other) noexcept { + zeroTime_ = other.zeroTime_.load(); + return *this; + } + + /** + * Re-initialize token bucket. + * + * Thread-safe. + * + * @param zeroTime Initial time at which to consider the token bucket + * starting to fill. Defaults to 0, so by default token + * bucket is reset to "full". + */ + void reset(double zeroTime = 0) noexcept { + zeroTime_ = zeroTime; + } + + /** + * Returns the current time in seconds since Epoch. + */ + static double defaultClockNow() noexcept { + auto const now = Clock::now().time_since_epoch(); + return std::chrono::duration<double>(now).count(); + } + + /** + * Attempts to consume some number of tokens. Tokens are first added to the + * bucket based on the time elapsed since the last attempt to consume tokens. + * Note: Attempts to consume more tokens than the burst size will always + * fail. + * + * Thread-safe. + * + * @param toConsume The number of tokens to consume. + * @param rate Number of tokens to generate per second. + * @param burstSize Maximum burst size. Must be greater than 0. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + * @return True if the rate limit check passed, false otherwise. + */ + bool consume( + double toConsume, + double rate, + double burstSize, + double nowInSeconds = defaultClockNow()) { + assert(rate > 0); + assert(burstSize > 0); + + if (nowInSeconds <= zeroTime_.load()) { + return 0; + } + + return consumeImpl( + rate, burstSize, nowInSeconds, [toConsume](double& tokens) { + if (tokens < toConsume) { + return false; + } + tokens -= toConsume; + return true; + }); + } + + /** + * Similar to consume, but always consumes some number of tokens. If the + * bucket contains enough tokens - consumes toConsume tokens. Otherwise the + * bucket is drained. + * + * Thread-safe. + * + * @param toConsume The number of tokens to consume. + * @param rate Number of tokens to generate per second. + * @param burstSize Maximum burst size. Must be greater than 0. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + * @return number of tokens that were consumed. + */ + double consumeOrDrain( + double toConsume, + double rate, + double burstSize, + double nowInSeconds = defaultClockNow()) { + assert(rate > 0); + assert(burstSize > 0); + + if (nowInSeconds <= zeroTime_.load()) { + return 0; + } + + double consumed; + consumeImpl( + rate, burstSize, nowInSeconds, [&consumed, toConsume](double& tokens) { + if (tokens < toConsume) { + consumed = tokens; + tokens = 0.0; + } else { + consumed = toConsume; + tokens -= toConsume; + } + return true; + }); + return consumed; + } + + /** + * Return extra tokens back to the bucket. This will move the zeroTime_ + * value back based on the rate. + * + * Thread-safe. + */ + void returnTokens(double tokensToReturn, double rate) { + assert(rate > 0); + assert(tokensToReturn > 0); + + returnTokensImpl(tokensToReturn, rate); + } + + /** + * Like consumeOrDrain but the call will always satisfy the asked for count. + * It does so by borrowing tokens from the future (zeroTime_ will move + * forward) if the currently available count isn't sufficient. + * + * Returns a folly::Optional<double>. The optional wont be set if the request + * cannot be satisfied: only case is when it is larger than burstSize. The + * value of the optional is a double indicating the time in seconds that the + * caller needs to wait at which the reservation becomes valid. The caller + * could simply sleep for the returned duration to smooth out the allocation + * to match the rate limiter or do some other computation in the meantime. In + * any case, any regular consume or consumeOrDrain calls will fail to allocate + * any tokens until the future time is reached. + * + * Note: It is assumed the caller will not ask for a very large count nor use + * it immediately (if not waiting inline) as that would break the burst + * prevention the limiter is meant to be used for. + * + * Thread-safe. + */ + Optional<double> consumeWithBorrowNonBlocking( + double toConsume, + double rate, + double burstSize, + double nowInSeconds = defaultClockNow()) { + assert(rate > 0); + assert(burstSize > 0); + + if (burstSize < toConsume) { + return folly::none; + } + + while (toConsume > 0) { + double consumed = + consumeOrDrain(toConsume, rate, burstSize, nowInSeconds); + if (consumed > 0) { + toConsume -= consumed; + } else { + double zeroTimeNew = returnTokensImpl(-toConsume, rate); + double napTime = std::max(0.0, zeroTimeNew - nowInSeconds); + return napTime; + } + } + return 0; + } + + /** + * Convenience wrapper around non-blocking borrow to sleep inline until + * reservation is valid. + */ + bool consumeWithBorrowAndWait( + double toConsume, + double rate, + double burstSize, + double nowInSeconds = defaultClockNow()) { + auto res = + consumeWithBorrowNonBlocking(toConsume, rate, burstSize, nowInSeconds); + if (res.value_or(0) > 0) { + int64_t napUSec = res.value() * 1000000; + std::this_thread::sleep_for(std::chrono::microseconds(napUSec)); + } + return res.has_value(); + } + + /** + * Returns the number of tokens currently available. + * + * Thread-safe (but returned value may immediately be outdated). + */ + double available( + double rate, + double burstSize, + double nowInSeconds = defaultClockNow()) const noexcept { + assert(rate > 0); + assert(burstSize > 0); + + double zt = this->zeroTime_.load(); + if (nowInSeconds <= zt) { + return 0; + } + return std::min((nowInSeconds - zt) * rate, burstSize); + } + + private: + template <typename TCallback> + bool consumeImpl( + double rate, + double burstSize, + double nowInSeconds, + const TCallback& callback) { + auto zeroTimeOld = zeroTime_.load(); + double zeroTimeNew; + do { + auto tokens = std::min((nowInSeconds - zeroTimeOld) * rate, burstSize); + if (!callback(tokens)) { + return false; + } + zeroTimeNew = nowInSeconds - tokens / rate; + } while ( + UNLIKELY(!zeroTime_.compare_exchange_weak(zeroTimeOld, zeroTimeNew))); + + return true; + } + + /** + * Adjust zeroTime based on rate and tokenCount and return the new value of + * zeroTime_. Note: Token count can be negative to move the zeroTime_ value + * into the future. + */ + double returnTokensImpl(double tokenCount, double rate) { + auto zeroTimeOld = zeroTime_.load(); + double zeroTimeNew; + do { + zeroTimeNew = zeroTimeOld - tokenCount / rate; + } while ( + UNLIKELY(!zeroTime_.compare_exchange_weak(zeroTimeOld, zeroTimeNew))); + return zeroTimeNew; + } + + alignas(hardware_destructive_interference_size) std::atomic<double> zeroTime_; +}; + +/** + * Specialization of BasicDynamicTokenBucket with a fixed token + * generation rate and a fixed maximum burst size. + */ +template <typename Clock = std::chrono::steady_clock> +class BasicTokenBucket { + static_assert(Clock::is_steady, "clock must be steady"); + + private: + using Impl = BasicDynamicTokenBucket<Clock>; + + public: + /** + * Construct a token bucket with a specific maximum rate and burst size. + * + * @param genRate Number of tokens to generate per second. + * @param burstSize Maximum burst size. Must be greater than 0. + * @param zeroTime Initial time at which to consider the token bucket + * starting to fill. Defaults to 0, so by default token + * bucket is "full" after construction. + */ + BasicTokenBucket( + double genRate, + double burstSize, + double zeroTime = 0) noexcept + : tokenBucket_(zeroTime), rate_(genRate), burstSize_(burstSize) { + assert(rate_ > 0); + assert(burstSize_ > 0); + } + + /** + * Copy constructor. + * + * Warning: not thread safe! + */ + BasicTokenBucket(const BasicTokenBucket& other) noexcept = default; + + /** + * Copy-assignment operator. + * + * Warning: not thread safe! + */ + BasicTokenBucket& operator=(const BasicTokenBucket& other) noexcept = default; + + /** + * Returns the current time in seconds since Epoch. + */ + static double defaultClockNow() noexcept(noexcept(Impl::defaultClockNow())) { + return Impl::defaultClockNow(); + } + + /** + * Change rate and burst size. + * + * Warning: not thread safe! + * + * @param genRate Number of tokens to generate per second. + * @param burstSize Maximum burst size. Must be greater than 0. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + */ + void reset( + double genRate, + double burstSize, + double nowInSeconds = defaultClockNow()) noexcept { + assert(genRate > 0); + assert(burstSize > 0); + const double availTokens = available(nowInSeconds); + rate_ = genRate; + burstSize_ = burstSize; + setCapacity(availTokens, nowInSeconds); + } + + /** + * Change number of tokens in bucket. + * + * Warning: not thread safe! + * + * @param tokens Desired number of tokens in bucket after the call. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + */ + void setCapacity(double tokens, double nowInSeconds) noexcept { + tokenBucket_.reset(nowInSeconds - tokens / rate_); + } + + /** + * Attempts to consume some number of tokens. Tokens are first added to the + * bucket based on the time elapsed since the last attempt to consume tokens. + * Note: Attempts to consume more tokens than the burst size will always + * fail. + * + * Thread-safe. + * + * @param toConsume The number of tokens to consume. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + * @return True if the rate limit check passed, false otherwise. + */ + bool consume(double toConsume, double nowInSeconds = defaultClockNow()) { + return tokenBucket_.consume(toConsume, rate_, burstSize_, nowInSeconds); + } + + /** + * Similar to consume, but always consumes some number of tokens. If the + * bucket contains enough tokens - consumes toConsume tokens. Otherwise the + * bucket is drained. + * + * Thread-safe. + * + * @param toConsume The number of tokens to consume. + * @param nowInSeconds Current time in seconds. Should be monotonically + * increasing from the nowInSeconds specified in + * this token bucket's constructor. + * @return number of tokens that were consumed. + */ + double consumeOrDrain( + double toConsume, + double nowInSeconds = defaultClockNow()) { + return tokenBucket_.consumeOrDrain( + toConsume, rate_, burstSize_, nowInSeconds); + } + + /** + * Returns extra token back to the bucket. + */ + void returnTokens(double tokensToReturn) { + return tokenBucket_.returnTokens(tokensToReturn, rate_); + } + + /** + * Reserve tokens and return time to wait for in order for the reservation to + * be compatible with the bucket configuration. + */ + Optional<double> consumeWithBorrowNonBlocking( + double toConsume, + double nowInSeconds = defaultClockNow()) { + return tokenBucket_.consumeWithBorrowNonBlocking( + toConsume, rate_, burstSize_, nowInSeconds); + } + + /** + * Reserve tokens. Blocks if need be until reservation is satisfied. + */ + bool consumeWithBorrowAndWait( + double toConsume, + double nowInSeconds = defaultClockNow()) { + return tokenBucket_.consumeWithBorrowAndWait( + toConsume, rate_, burstSize_, nowInSeconds); + } + + /** + * Returns the number of tokens currently available. + * + * Thread-safe (but returned value may immediately be outdated). + */ + double available(double nowInSeconds = defaultClockNow()) const { + return tokenBucket_.available(rate_, burstSize_, nowInSeconds); + } + + /** + * Returns the number of tokens generated per second. + * + * Thread-safe (but returned value may immediately be outdated). + */ + double rate() const noexcept { + return rate_; + } + + /** + * Returns the maximum burst size. + * + * Thread-safe (but returned value may immediately be outdated). + */ + double burst() const noexcept { + return burstSize_; + } + + private: + Impl tokenBucket_; + double rate_; + double burstSize_; +}; + +using TokenBucket = BasicTokenBucket<>; +using DynamicTokenBucket = BasicDynamicTokenBucket<>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Traits.h b/ios/Pods/Flipper-Folly/folly/Traits.h new file mode 100644 index 000000000..6ec60301d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Traits.h @@ -0,0 +1,838 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Andrei Alexandrescu + +#pragma once + +#include <functional> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> + +#include <folly/Portability.h> + +#define FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(classname, type_name) \ + template <typename TTheClass_> \ + struct classname##__folly_traits_impl__ { \ + template <typename UTheClass_> \ + static constexpr bool test(typename UTheClass_::type_name*) { \ + return true; \ + } \ + template <typename> \ + static constexpr bool test(...) { \ + return false; \ + } \ + }; \ + template <typename TTheClass_> \ + using classname = typename std::conditional< \ + classname##__folly_traits_impl__<TTheClass_>::template test<TTheClass_>( \ + nullptr), \ + std::true_type, \ + std::false_type>::type + +#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, cv_qual) \ + template <typename TTheClass_, typename RTheReturn_, typename... TTheArgs_> \ + struct classname##__folly_traits_impl__< \ + TTheClass_, \ + RTheReturn_(TTheArgs_...) cv_qual> { \ + template < \ + typename UTheClass_, \ + RTheReturn_ (UTheClass_::*)(TTheArgs_...) cv_qual> \ + struct sfinae {}; \ + template <typename UTheClass_> \ + static std::true_type test(sfinae<UTheClass_, &UTheClass_::func_name>*); \ + template <typename> \ + static std::false_type test(...); \ + } + +/* + * The FOLLY_CREATE_HAS_MEMBER_FN_TRAITS is used to create traits + * classes that check for the existence of a member function with + * a given name and signature. It currently does not support + * checking for inherited members. + * + * Such classes receive two template parameters: the class to be checked + * and the signature of the member function. A static boolean field + * named `value` (which is also constexpr) tells whether such member + * function exists. + * + * Each traits class created is bound only to the member name, not to + * its signature nor to the type of the class containing it. + * + * Say you need to know if a given class has a member function named + * `test` with the following signature: + * + * int test() const; + * + * You'd need this macro to create a traits class to check for a member + * named `test`, and then use this traits class to check for the signature: + * + * namespace { + * + * FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test); + * + * } // unnamed-namespace + * + * void some_func() { + * cout << "Does class Foo have a member int test() const? " + * << boolalpha << has_test_traits<Foo, int() const>::value; + * } + * + * You can use the same traits class to test for a completely different + * signature, on a completely different class, as long as the member name + * is the same: + * + * void some_func() { + * cout << "Does class Foo have a member int test()? " + * << boolalpha << has_test_traits<Foo, int()>::value; + * cout << "Does class Foo have a member int test() const? " + * << boolalpha << has_test_traits<Foo, int() const>::value; + * cout << "Does class Bar have a member double test(const string&, long)? " + * << boolalpha << has_test_traits<Bar, double(const string&, long)>::value; + * } + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ +#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(classname, func_name) \ + template <typename, typename> \ + struct classname##__folly_traits_impl__; \ + FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, ); \ + FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, const); \ + FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL( \ + classname, func_name, /* nolint */ volatile); \ + FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL( \ + classname, func_name, /* nolint */ volatile const); \ + template <typename TTheClass_, typename TTheSignature_> \ + using classname = \ + decltype(classname##__folly_traits_impl__<TTheClass_, TTheSignature_>:: \ + template test<TTheClass_>(nullptr)) + +namespace folly { + +template <typename...> +struct tag_t {}; + +#if __cplusplus >= 201703L + +template <typename... T> +inline constexpr tag_t<T...> tag; + +#endif + +#if __cpp_lib_bool_constant || _MSC_VER + +using std::bool_constant; + +#else + +// mimic: std::bool_constant, C++17 +template <bool B> +using bool_constant = std::integral_constant<bool, B>; + +#endif + +template <std::size_t I> +using index_constant = std::integral_constant<std::size_t, I>; + +namespace detail { + +/** + * A type trait to check if a given type is an instantiation of a class + * template. + * + * Note that this only works with template type parameters. It does not work + * with non-type template parameters, template template parameters, or alias + * templates. + */ +template <template <typename...> class, typename> +struct is_instantiation_of : std::false_type {}; +template <template <typename...> class C, typename... T> +struct is_instantiation_of<C, C<T...>> : std::true_type {}; +template <template <typename...> class C, typename T> +constexpr bool is_instantiation_of_v = is_instantiation_of<C, T>::value; + +} // namespace detail + +namespace detail { + +template <bool, typename T> +struct is_constexpr_default_constructible_; +template <typename T> +struct is_constexpr_default_constructible_<false, T> { + using type = std::false_type; +}; +template <typename T> +struct is_constexpr_default_constructible_<true, T> { + static constexpr int take(T) { + return 0; + } + template <int = take(T{})> + static std::true_type sfinae(int); + static std::false_type sfinae(...); + using type = decltype(sfinae(0)); +}; + +} // namespace detail + +// is_constexpr_default_constructible +// is_constexpr_default_constructible_v +// +// A type trait, with associated variable template, which determines whether +// its type parameter is constexpr default-constructible, that is, default- +// constructible in a constexpr context. +// +// Instantiations of is_constexpr_default_constructible unambiguously inherit +// std::integral_constant<bool, V> for some bool V. +template <typename T> +struct is_constexpr_default_constructible + : detail::is_constexpr_default_constructible_< + std::is_default_constructible<T>::value, + T>::type {}; +template <typename T> +FOLLY_INLINE_VARIABLE constexpr bool is_constexpr_default_constructible_v = + is_constexpr_default_constructible<T>::value; + +/*** + * _t + * + * Instead of: + * + * using decayed = typename std::decay<T>::type; + * + * With the C++14 standard trait aliases, we could use: + * + * using decayed = std::decay_t<T>; + * + * Without them, we could use: + * + * using decayed = _t<std::decay<T>>; + * + * Also useful for any other library with template types having dependent + * member types named `type`, like the standard trait types. + */ +template <typename T> +using _t = typename T::type; + +/** + * A type trait to remove all const volatile and reference qualifiers on a + * type T + */ +template <typename T> +struct remove_cvref { + using type = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; +}; +template <typename T> +using remove_cvref_t = typename remove_cvref<T>::type; + +namespace detail { +template <typename Src> +struct like_ { + template <typename Dst> + using apply = Dst; +}; +template <typename Src> +struct like_<Src const> { + template <typename Dst> + using apply = Dst const; +}; +template <typename Src> +struct like_<Src volatile> { + template <typename Dst> + using apply = Dst volatile; +}; +template <typename Src> +struct like_<Src const volatile> { + template <typename Dst> + using apply = Dst const volatile; +}; +template <typename Src> +struct like_<Src&> { + template <typename Dst> + using apply = typename like_<Src>::template apply<Dst>&; +}; +template <typename Src> +struct like_<Src&&> { + template <typename Dst> + using apply = typename like_<Src>::template apply<Dst>&&; +}; +} // namespace detail + +// mimic: like_t, p0847r0 +template <typename Src, typename Dst> +using like_t = typename detail::like_<Src>::template apply<remove_cvref_t<Dst>>; + +// mimic: like, p0847r0 +template <typename Src, typename Dst> +struct like { + using type = like_t<Src, Dst>; +}; + +/** + * type_t + * + * A type alias for the first template type argument. `type_t` is useful for + * controlling class-template and function-template partial specialization. + * + * Example: + * + * template <typename Value> + * class Container { + * public: + * template <typename... Args> + * Container( + * type_t<in_place_t, decltype(Value(std::declval<Args>()...))>, + * Args&&...); + * }; + * + * void_t + * + * A type alias for `void`. `void_t` is useful for controling class-template + * and function-template partial specialization. + * + * Example: + * + * // has_value_type<T>::value is true if T has a nested type `value_type` + * template <class T, class = void> + * struct has_value_type + * : std::false_type {}; + * + * template <class T> + * struct has_value_type<T, folly::void_t<typename T::value_type>> + * : std::true_type {}; + */ + +/** + * There is a bug in libstdc++, libc++, and MSVC's STL that causes it to + * ignore unused template parameter arguments in template aliases and does not + * cause substitution failures. This defect has been recorded here: + * http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558. + * + * This causes the implementation of std::void_t to be buggy, as it is likely + * defined as something like the following: + * + * template <typename...> + * using void_t = void; + * + * This causes the compiler to ignore all the template arguments and does not + * help when one wants to cause substitution failures. Rather declarations + * which have void_t in orthogonal specializations are treated as the same. + * For example, assuming the possible `T` types are only allowed to have + * either the alias `one` or `two` and never both or none: + * + * template <typename T, + * typename std::void_t<std::decay_t<T>::one>* = nullptr> + * void foo(T&&) {} + * template <typename T, + * typename std::void_t<std::decay_t<T>::two>* = nullptr> + * void foo(T&&) {} + * + * The second foo() will be a redefinition because it conflicts with the first + * one; void_t does not cause substitution failures - the template types are + * just ignored. + */ + +namespace traits_detail { +template <class T, class...> +struct type_t_ { + using type = T; +}; +} // namespace traits_detail + +template <class T, class... Ts> +using type_t = typename traits_detail::type_t_<T, Ts...>::type; +template <class... Ts> +using void_t = type_t<void, Ts...>; + +template <typename T> +using aligned_storage_for_t = + typename std::aligned_storage<sizeof(T), alignof(T)>::type; + +// Older versions of libstdc++ do not provide std::is_trivially_copyable +#if defined(__clang__) && !defined(_LIBCPP_VERSION) +template <class T> +struct is_trivially_copyable : bool_constant<__is_trivially_copyable(T)> {}; +#else +template <class T> +using is_trivially_copyable = std::is_trivially_copyable<T>; +#endif + +template <class T> +FOLLY_INLINE_VARIABLE constexpr bool is_trivially_copyable_v = + is_trivially_copyable<T>::value; + +/** + * IsRelocatable<T>::value describes the ability of moving around + * memory a value of type T by using memcpy (as opposed to the + * conservative approach of calling the copy constructor and then + * destroying the old temporary. Essentially for a relocatable type, + * the following two sequences of code should be semantically + * equivalent: + * + * void move1(T * from, T * to) { + * new(to) T(from); + * (*from).~T(); + * } + * + * void move2(T * from, T * to) { + * memcpy(to, from, sizeof(T)); + * } + * + * Most C++ types are relocatable; the ones that aren't would include + * internal pointers or (very rarely) would need to update remote + * pointers to pointers tracking them. All C++ primitive types and + * type constructors are relocatable. + * + * This property can be used in a variety of optimizations. Currently + * fbvector uses this property intensively. + * + * The default conservatively assumes the type is not + * relocatable. Several specializations are defined for known + * types. You may want to add your own specializations. Do so in + * namespace folly and make sure you keep the specialization of + * IsRelocatable<SomeStruct> in the same header as SomeStruct. + * + * You may also declare a type to be relocatable by including + * `typedef std::true_type IsRelocatable;` + * in the class header. + * + * It may be unset in a base class by overriding the typedef to false_type. + */ +/* + * IsZeroInitializable describes the property that default construction is the + * same as memset(dst, 0, sizeof(T)). + */ + +namespace traits_detail { + +#define FOLLY_HAS_TRUE_XXX(name) \ + FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(has_##name, name); \ + template <class T> \ + struct name##_is_true : std::is_same<typename T::name, std::true_type> {}; \ + template <class T> \ + struct has_true_##name : std::conditional< \ + has_##name<T>::value, \ + name##_is_true<T>, \ + std::false_type>::type {} + +FOLLY_HAS_TRUE_XXX(IsRelocatable); +FOLLY_HAS_TRUE_XXX(IsZeroInitializable); + +#undef FOLLY_HAS_TRUE_XXX + +} // namespace traits_detail + +struct Ignore { + Ignore() = default; + template <class T> + constexpr /* implicit */ Ignore(const T&) {} + template <class T> + const Ignore& operator=(T const&) const { + return *this; + } +}; + +template <class...> +using Ignored = Ignore; + +namespace traits_detail_IsEqualityComparable { +Ignore operator==(Ignore, Ignore); + +template <class T, class U = T> +struct IsEqualityComparable + : std::is_convertible< + decltype(std::declval<T>() == std::declval<U>()), + bool> {}; +} // namespace traits_detail_IsEqualityComparable + +/* using override */ using traits_detail_IsEqualityComparable:: + IsEqualityComparable; + +namespace traits_detail_IsLessThanComparable { +Ignore operator<(Ignore, Ignore); + +template <class T, class U = T> +struct IsLessThanComparable + : std::is_convertible< + decltype(std::declval<T>() < std::declval<U>()), + bool> {}; +} // namespace traits_detail_IsLessThanComparable + +/* using override */ using traits_detail_IsLessThanComparable:: + IsLessThanComparable; + +namespace traits_detail_IsNothrowSwappable { +#if defined(__cpp_lib_is_swappable) || (_CPPLIB_VER && _HAS_CXX17) +// MSVC already implements the C++17 P0185R1 proposal which adds +// std::is_nothrow_swappable, so use it instead if C++17 mode is +// enabled. +template <typename T> +using IsNothrowSwappable = std::is_nothrow_swappable<T>; +#elif _CPPLIB_VER +// MSVC defines the base even if C++17 is disabled, and MSVC has +// issues with our fallback implementation due to over-eager +// evaluation of noexcept. +template <typename T> +using IsNothrowSwappable = std::_Is_nothrow_swappable<T>; +#else +/* using override */ using std::swap; + +template <class T> +struct IsNothrowSwappable + : bool_constant<std::is_nothrow_move_constructible<T>::value&& noexcept( + swap(std::declval<T&>(), std::declval<T&>()))> {}; +#endif +} // namespace traits_detail_IsNothrowSwappable + +/* using override */ using traits_detail_IsNothrowSwappable::IsNothrowSwappable; + +template <class T> +struct IsRelocatable : std::conditional< + traits_detail::has_IsRelocatable<T>::value, + traits_detail::has_true_IsRelocatable<T>, + // TODO add this line (and some tests for it) when we + // upgrade to gcc 4.7 + // std::is_trivially_move_constructible<T>::value || + is_trivially_copyable<T>>::type {}; + +template <class T> +struct IsZeroInitializable + : std::conditional< + traits_detail::has_IsZeroInitializable<T>::value, + traits_detail::has_true_IsZeroInitializable<T>, + bool_constant<!std::is_class<T>::value>>::type {}; + +namespace detail { +template <bool> +struct conditional_; +template <> +struct conditional_<false> { + template <typename, typename T> + using apply = T; +}; +template <> +struct conditional_<true> { + template <typename T, typename> + using apply = T; +}; +} // namespace detail + +// conditional_t +// +// Like std::conditional_t but with only two total class template instances, +// rather than as many class template instances as there are uses. +// +// As one effect, the result can be used in deducible contexts, allowing +// deduction of conditional_t<V, T, F> to work when T or F is a template param. +template <bool V, typename T, typename F> +using conditional_t = typename detail::conditional_<V>::template apply<T, F>; + +template <typename...> +struct Conjunction : std::true_type {}; +template <typename T> +struct Conjunction<T> : T {}; +template <typename T, typename... TList> +struct Conjunction<T, TList...> + : std::conditional<T::value, Conjunction<TList...>, T>::type {}; + +template <typename...> +struct Disjunction : std::false_type {}; +template <typename T> +struct Disjunction<T> : T {}; +template <typename T, typename... TList> +struct Disjunction<T, TList...> + : std::conditional<T::value, T, Disjunction<TList...>>::type {}; + +template <typename T> +struct Negation : bool_constant<!T::value> {}; + +template <bool... Bs> +struct Bools { + using valid_type = bool; + static constexpr std::size_t size() { + return sizeof...(Bs); + } +}; + +// Lighter-weight than Conjunction, but evaluates all sub-conditions eagerly. +template <class... Ts> +struct StrictConjunction + : std::is_same<Bools<Ts::value...>, Bools<(Ts::value || true)...>> {}; + +template <class... Ts> +struct StrictDisjunction + : Negation< + std::is_same<Bools<Ts::value...>, Bools<(Ts::value && false)...>>> {}; + +namespace detail { +template <typename, typename> +struct is_transparent_ : std::false_type {}; +template <typename T> +struct is_transparent_<void_t<typename T::is_transparent>, T> : std::true_type { +}; +} // namespace detail + +// is_transparent +// +// To test whether a less, equal-to, or hash type follows the is-transparent +// protocol used by containers with optional heterogeneous access. +template <typename T> +struct is_transparent : detail::is_transparent_<void, T> {}; + +} // namespace folly + +/** + * Use this macro ONLY inside namespace folly. When using it with a + * regular type, use it like this: + * + * // Make sure you're at namespace ::folly scope + * template <> FOLLY_ASSUME_RELOCATABLE(MyType) + * + * When using it with a template type, use it like this: + * + * // Make sure you're at namespace ::folly scope + * template <class T1, class T2> + * FOLLY_ASSUME_RELOCATABLE(MyType<T1, T2>) + */ +#define FOLLY_ASSUME_RELOCATABLE(...) \ + struct IsRelocatable<__VA_ARGS__> : std::true_type {} + +/** + * The FOLLY_ASSUME_FBVECTOR_COMPATIBLE* macros below encode the + * assumption that the type is relocatable per IsRelocatable + * above. Many types can be assumed to satisfy this condition, but + * it is the responsibility of the user to state that assumption. + * User-defined classes will not be optimized for use with + * fbvector (see FBVector.h) unless they state that assumption. + * + * Use FOLLY_ASSUME_FBVECTOR_COMPATIBLE with regular types like this: + * + * FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType) + * + * The versions FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1, _2, _3, and _4 + * allow using the macro for describing templatized classes with 1, 2, + * 3, and 4 template parameters respectively. For template classes + * just use the macro with the appropriate number and pass the name of + * the template to it. Example: + * + * template <class T1, class T2> class MyType { ... }; + * ... + * // Make sure you're at global scope + * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType) + */ + +// Use this macro ONLY at global level (no namespace) +#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE(...) \ + namespace folly { \ + template <> \ + FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__); \ + } +// Use this macro ONLY at global level (no namespace) +#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(...) \ + namespace folly { \ + template <class T1> \ + FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1>); \ + } +// Use this macro ONLY at global level (no namespace) +#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...) \ + namespace folly { \ + template <class T1, class T2> \ + FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2>); \ + } +// Use this macro ONLY at global level (no namespace) +#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...) \ + namespace folly { \ + template <class T1, class T2, class T3> \ + FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3>); \ + } +// Use this macro ONLY at global level (no namespace) +#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...) \ + namespace folly { \ + template <class T1, class T2, class T3, class T4> \ + FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3, T4>); \ + } + +namespace folly { + +// STL commonly-used types +template <class T, class U> +struct IsRelocatable<std::pair<T, U>> + : bool_constant<IsRelocatable<T>::value && IsRelocatable<U>::value> {}; + +// Is T one of T1, T2, ..., Tn? +template <typename T, typename... Ts> +using IsOneOf = StrictDisjunction<std::is_same<T, Ts>...>; + +/* + * Complementary type traits for integral comparisons. + * + * For instance, `if(x < 0)` yields an error in clang for unsigned types + * when -Werror is used due to -Wtautological-compare + * + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + +// same as `x < 0` +template <typename T> +constexpr bool is_negative(T x) { + return std::is_signed<T>::value && x < T(0); +} + +// same as `x <= 0` +template <typename T> +constexpr bool is_non_positive(T x) { + return !x || folly::is_negative(x); +} + +// same as `x > 0` +template <typename T> +constexpr bool is_positive(T x) { + return !is_non_positive(x); +} + +// same as `x >= 0` +template <typename T> +constexpr bool is_non_negative(T x) { + return !x || is_positive(x); +} + +namespace detail { + +// folly::to integral specializations can end up generating code +// inside what are really static ifs (not executed because of the templated +// types) that violate -Wsign-compare and/or -Wbool-compare so suppress them +// in order to not prevent all calling code from using it. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wsign-compare") +FOLLY_GCC_DISABLE_WARNING("-Wbool-compare") +FOLLY_MSVC_DISABLE_WARNING(4287) // unsigned/negative constant mismatch +FOLLY_MSVC_DISABLE_WARNING(4388) // sign-compare +FOLLY_MSVC_DISABLE_WARNING(4804) // bool-compare + +template <typename RHS, RHS rhs, typename LHS> +bool less_than_impl(LHS const lhs) { + // clang-format off + return + // Ensure signed and unsigned values won't be compared directly. + (!std::is_signed<RHS>::value && is_negative(lhs)) ? true : + (!std::is_signed<LHS>::value && is_negative(rhs)) ? false : + rhs > std::numeric_limits<LHS>::max() ? true : + rhs <= std::numeric_limits<LHS>::min() ? false : + lhs < rhs; + // clang-format on +} + +template <typename RHS, RHS rhs, typename LHS> +bool greater_than_impl(LHS const lhs) { + // clang-format off + return + // Ensure signed and unsigned values won't be compared directly. + (!std::is_signed<RHS>::value && is_negative(lhs)) ? false : + (!std::is_signed<LHS>::value && is_negative(rhs)) ? true : + rhs > std::numeric_limits<LHS>::max() ? false : + rhs < std::numeric_limits<LHS>::min() ? true : + lhs > rhs; + // clang-format on +} + +FOLLY_POP_WARNING + +} // namespace detail + +template <typename RHS, RHS rhs, typename LHS> +bool less_than(LHS const lhs) { + return detail:: + less_than_impl<RHS, rhs, typename std::remove_reference<LHS>::type>(lhs); +} + +template <typename RHS, RHS rhs, typename LHS> +bool greater_than(LHS const lhs) { + return detail:: + greater_than_impl<RHS, rhs, typename std::remove_reference<LHS>::type>( + lhs); +} +} // namespace folly + +// Assume nothing when compiling with MSVC. +#ifndef _MSC_VER +FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::unique_ptr) +FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::shared_ptr) +#endif + +/* Some combinations of compilers and C++ libraries make __int128 and + * unsigned __int128 available but do not correctly define their standard type + * traits. + * + * If FOLLY_SUPPLY_MISSING_INT128_TRAITS is defined, we define these traits + * here. + * + * @author: Phil Willoughby <philwill@fb.com> + */ +#if FOLLY_SUPPLY_MISSING_INT128_TRAITS +FOLLY_NAMESPACE_STD_BEGIN +template <> +struct is_arithmetic<__int128> : ::std::true_type {}; +template <> +struct is_arithmetic<unsigned __int128> : ::std::true_type {}; +template <> +struct is_integral<__int128> : ::std::true_type {}; +template <> +struct is_integral<unsigned __int128> : ::std::true_type {}; +template <> +struct make_unsigned<__int128> { + typedef unsigned __int128 type; +}; +template <> +struct make_signed<__int128> { + typedef __int128 type; +}; +template <> +struct make_unsigned<unsigned __int128> { + typedef unsigned __int128 type; +}; +template <> +struct make_signed<unsigned __int128> { + typedef __int128 type; +}; +template <> +struct is_signed<__int128> : ::std::true_type {}; +template <> +struct is_unsigned<unsigned __int128> : ::std::true_type {}; +FOLLY_NAMESPACE_STD_END +#endif // FOLLY_SUPPLY_MISSING_INT128_TRAITS + +namespace folly { + +/** + * Extension point for containers to provide an order such that if entries are + * inserted into a new instance in that order, iteration order of the new + * instance matches the original's. This can be useful for containers that have + * defined but non-FIFO iteration order, such as F14Vector*. + * + * Should return an iterable view (a type that provides begin() and end()). + * + * Containers should provide overloads in their own namespace; resolution is + * expected to be done via ADL. + */ +template <typename Container> +const Container& order_preserving_reinsertion_view(const Container& container) { + return container; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Try-inl.h b/ios/Pods/Flipper-Folly/folly/Try-inl.h new file mode 100644 index 000000000..f0e29a70b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Try-inl.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> + +#include <stdexcept> +#include <tuple> +#include <utility> + +namespace folly { + +template <class T> +Try<T>::Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value) + : contains_(t.contains_) { + if (contains_ == Contains::VALUE) { + new (&value_) T(std::move(t.value_)); + } else if (contains_ == Contains::EXCEPTION) { + new (&e_) exception_wrapper(std::move(t.e_)); + } +} + +template <class T> +template <class T2> +Try<T>::Try(typename std::enable_if< + std::is_same<Unit, T2>::value, + Try<void> const&>::type t) noexcept + : contains_(Contains::NOTHING) { + if (t.hasValue()) { + contains_ = Contains::VALUE; + new (&value_) T(); + } else if (t.hasException()) { + contains_ = Contains::EXCEPTION; + new (&e_) exception_wrapper(t.exception()); + } +} + +template <class T> +Try<T>& Try<T>::operator=(Try<T>&& t) noexcept( + std::is_nothrow_move_constructible<T>::value) { + if (this == &t) { + return *this; + } + + destroy(); + + if (t.contains_ == Contains::VALUE) { + new (&value_) T(std::move(t.value_)); + } else if (t.contains_ == Contains::EXCEPTION) { + new (&e_) exception_wrapper(std::move(t.e_)); + } + + contains_ = t.contains_; + + return *this; +} + +template <class T> +Try<T>::Try(const Try<T>& t) noexcept( + std::is_nothrow_copy_constructible<T>::value) { + static_assert( + std::is_copy_constructible<T>::value, + "T must be copyable for Try<T> to be copyable"); + contains_ = t.contains_; + if (contains_ == Contains::VALUE) { + new (&value_) T(t.value_); + } else if (contains_ == Contains::EXCEPTION) { + new (&e_) exception_wrapper(t.e_); + } +} + +template <class T> +Try<T>& Try<T>::operator=(const Try<T>& t) noexcept( + std::is_nothrow_copy_constructible<T>::value) { + static_assert( + std::is_copy_constructible<T>::value, + "T must be copyable for Try<T> to be copyable"); + + if (this == &t) { + return *this; + } + + destroy(); + + if (t.contains_ == Contains::VALUE) { + new (&value_) T(t.value_); + } else if (t.contains_ == Contains::EXCEPTION) { + new (&e_) exception_wrapper(t.e_); + } + + contains_ = t.contains_; + + return *this; +} + +template <class T> +Try<T>::~Try() { + if (LIKELY(contains_ == Contains::VALUE)) { + value_.~T(); + } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) { + e_.~exception_wrapper(); + } +} + +template <typename T> +template <typename... Args> +T& Try<T>::emplace(Args&&... args) noexcept( + std::is_nothrow_constructible<T, Args&&...>::value) { + this->destroy(); + new (&value_) T(static_cast<Args&&>(args)...); + contains_ = Contains::VALUE; + return value_; +} + +template <typename T> +template <typename... Args> +exception_wrapper& Try<T>::emplaceException(Args&&... args) noexcept( + std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { + this->destroy(); + new (&e_) exception_wrapper(static_cast<Args&&>(args)...); + contains_ = Contains::EXCEPTION; + return e_; +} + +template <class T> +T& Try<T>::value() & { + throwIfFailed(); + return value_; +} + +template <class T> +T&& Try<T>::value() && { + throwIfFailed(); + return std::move(value_); +} + +template <class T> +const T& Try<T>::value() const& { + throwIfFailed(); + return value_; +} + +template <class T> +const T&& Try<T>::value() const&& { + throwIfFailed(); + return std::move(value_); +} + +template <class T> +void Try<T>::throwIfFailed() const { + switch (contains_) { + case Contains::VALUE: + return; + case Contains::EXCEPTION: + e_.throw_exception(); + case Contains::NOTHING: + default: + throw_exception<UsingUninitializedTry>(); + } +} + +template <class T> +void Try<T>::destroy() noexcept { + auto oldContains = std::exchange(contains_, Contains::NOTHING); + if (LIKELY(oldContains == Contains::VALUE)) { + value_.~T(); + } else if (UNLIKELY(oldContains == Contains::EXCEPTION)) { + e_.~exception_wrapper(); + } +} + +Try<void>& Try<void>::operator=(const Try<void>& t) noexcept { + if (t.hasException()) { + if (hasException()) { + e_ = t.e_; + } else { + new (&e_) exception_wrapper(t.e_); + hasValue_ = false; + } + } else { + if (hasException()) { + e_.~exception_wrapper(); + hasValue_ = true; + } + } + return *this; +} + +template <typename... Args> +exception_wrapper& Try<void>::emplaceException(Args&&... args) noexcept( + std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { + if (hasException()) { + e_.~exception_wrapper(); + } + new (&e_) exception_wrapper(static_cast<Args&&>(args)...); + hasValue_ = false; + return e_; +} + +void Try<void>::throwIfFailed() const { + if (hasException()) { + e_.throw_exception(); + } +} + +template <typename F> +typename std::enable_if< + !std::is_same<invoke_result_t<F>, void>::value, + Try<invoke_result_t<F>>>::type +makeTryWithNoUnwrap(F&& f) { + using ResultType = invoke_result_t<F>; + try { + return Try<ResultType>(f()); + } catch (std::exception& e) { + return Try<ResultType>(exception_wrapper(std::current_exception(), e)); + } catch (...) { + return Try<ResultType>(exception_wrapper(std::current_exception())); + } +} + +template <typename F> +typename std:: + enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type + makeTryWithNoUnwrap(F&& f) { + try { + f(); + return Try<void>(); + } catch (std::exception& e) { + return Try<void>(exception_wrapper(std::current_exception(), e)); + } catch (...) { + return Try<void>(exception_wrapper(std::current_exception())); + } +} + +template <typename F> +typename std:: + enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type + makeTryWith(F&& f) { + return makeTryWithNoUnwrap(std::forward<F>(f)); +} + +template <typename F> +typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>:: + type + makeTryWith(F&& f) { + using ResultType = invoke_result_t<F>; + try { + return f(); + } catch (std::exception& e) { + return ResultType(exception_wrapper(std::current_exception(), e)); + } catch (...) { + return ResultType(exception_wrapper(std::current_exception())); + } +} + +template <typename T, typename... Args> +T* tryEmplace(Try<T>& t, Args&&... args) noexcept { + try { + return std::addressof(t.emplace(static_cast<Args&&>(args)...)); + } catch (const std::exception& ex) { + t.emplaceException(std::current_exception(), ex); + return nullptr; + } catch (...) { + t.emplaceException(std::current_exception()); + return nullptr; + } +} + +void tryEmplace(Try<void>& t) noexcept { + t.emplace(); +} + +template <typename T, typename Func> +T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept { + static_assert( + std::is_constructible<T, folly::invoke_result_t<Func>>::value, + "Unable to initialise a value of type T with the result of 'func'"); + try { + return std::addressof(t.emplace(static_cast<Func&&>(func)())); + } catch (const std::exception& ex) { + t.emplaceException(std::current_exception(), ex); + return nullptr; + } catch (...) { + t.emplaceException(std::current_exception()); + return nullptr; + } +} + +template <typename Func> +bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept { + static_assert( + std::is_void<folly::invoke_result_t<Func>>::value, + "Func returns non-void. Cannot be used to emplace Try<void>"); + try { + static_cast<Func&&>(func)(); + t.emplace(); + return true; + } catch (const std::exception& ex) { + t.emplaceException(std::current_exception(), ex); + return false; + } catch (...) { + t.emplaceException(std::current_exception()); + return false; + } +} + +namespace try_detail { + +/** + * Trait that removes the layer of Try abstractions from the passed in type + */ +template <typename Type> +struct RemoveTry; +template <template <typename...> class TupleType, typename... Types> +struct RemoveTry<TupleType<folly::Try<Types>...>> { + using type = TupleType<Types...>; +}; + +template <std::size_t... Indices, typename Tuple> +auto unwrapTryTupleImpl(std::index_sequence<Indices...>, Tuple&& instance) { + using std::get; + using ReturnType = typename RemoveTry<typename std::decay<Tuple>::type>::type; + return ReturnType{(get<Indices>(std::forward<Tuple>(instance)).value())...}; +} +} // namespace try_detail + +template <typename Tuple> +auto unwrapTryTuple(Tuple&& instance) { + using TupleDecayed = typename std::decay<Tuple>::type; + using Seq = std::make_index_sequence<std::tuple_size<TupleDecayed>::value>; + return try_detail::unwrapTryTupleImpl(Seq{}, std::forward<Tuple>(instance)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Try.h b/ios/Pods/Flipper-Folly/folly/Try.h new file mode 100644 index 000000000..5404ec2ea --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Try.h @@ -0,0 +1,700 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/Unit.h> +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Exception.h> +#include <exception> +#include <stdexcept> +#include <type_traits> +#include <utility> + +namespace folly { + +class FOLLY_EXPORT TryException : public std::logic_error { + public: + using std::logic_error::logic_error; +}; + +class FOLLY_EXPORT UsingUninitializedTry : public TryException { + public: + UsingUninitializedTry() : TryException("Using uninitialized try") {} +}; + +/* + * Try<T> is a wrapper that contains either an instance of T, an exception, or + * nothing. Exceptions are stored as exception_wrappers so that the user can + * minimize rethrows if so desired. + * + * To represent success or a captured exception, use Try<Unit>. + */ +template <class T> +class Try { + static_assert( + !std::is_reference<T>::value, + "Try may not be used with reference types"); + + enum class Contains { + VALUE, + EXCEPTION, + NOTHING, + }; + + public: + /* + * The value type for the Try + */ + typedef T element_type; + + /* + * Construct an empty Try + */ + Try() noexcept : contains_(Contains::NOTHING) {} + + /* + * Construct a Try with a value by copy + * + * @param v The value to copy in + */ + explicit Try(const T& v) noexcept( + std::is_nothrow_copy_constructible<T>::value) + : contains_(Contains::VALUE), value_(v) {} + + /* + * Construct a Try with a value by move + * + * @param v The value to move in + */ + explicit Try(T&& v) noexcept(std::is_nothrow_move_constructible<T>::value) + : contains_(Contains::VALUE), value_(std::move(v)) {} + + template <typename... Args> + explicit Try(in_place_t, Args&&... args) noexcept( + std::is_nothrow_constructible<T, Args&&...>::value) + : contains_(Contains::VALUE), value_(static_cast<Args&&>(args)...) {} + + /// Implicit conversion from Try<void> to Try<Unit> + template <class T2 = T> + /* implicit */ + Try(typename std::enable_if<std::is_same<Unit, T2>::value, Try<void> const&>:: + type t) noexcept; + + /* + * Construct a Try with an exception_wrapper + * + * @param e The exception_wrapper + */ + explicit Try(exception_wrapper e) noexcept + : contains_(Contains::EXCEPTION), e_(std::move(e)) {} + + // Move constructor + Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value); + // Move assigner + Try& operator=(Try<T>&& t) noexcept( + std::is_nothrow_move_constructible<T>::value); + + // Copy constructor + Try(const Try& t) noexcept(std::is_nothrow_copy_constructible<T>::value); + // Copy assigner + Try& operator=(const Try& t) noexcept( + std::is_nothrow_copy_constructible<T>::value); + + ~Try(); + + /* + * In-place construct the value in the Try object. + * + * Destroys any previous value prior to constructing the new value. + * Leaves *this in an empty state if the construction of T throws. + * + * @returns reference to the newly constructed value. + */ + template <typename... Args> + T& emplace(Args&&... args) noexcept( + std::is_nothrow_constructible<T, Args&&...>::value); + + /* + * In-place construct an exception in the Try object. + * + * Destroys any previous value prior to constructing the new value. + * Leaves *this in an empty state if the construction of the exception_wrapper + * throws. + * + * Any arguments passed to emplaceException() are forwarded on to the + * exception_wrapper constructor. + * + * @returns reference to the newly constructed exception_wrapper. + */ + template <typename... Args> + exception_wrapper& emplaceException(Args&&... args) noexcept( + std::is_nothrow_constructible<exception_wrapper, Args&&...>::value); + + /* + * Get a mutable reference to the contained value. If the Try contains an + * exception it will be rethrown. + * + * @returns mutable reference to the contained value + */ + T& value() &; + /* + * Get a rvalue reference to the contained value. If the Try contains an + * exception it will be rethrown. + * + * @returns rvalue reference to the contained value + */ + T&& value() &&; + /* + * Get a const reference to the contained value. If the Try contains an + * exception it will be rethrown. + * + * @returns const reference to the contained value + */ + const T& value() const&; + /* + * Get a const rvalue reference to the contained value. If the Try contains an + * exception it will be rethrown. + * + * @returns const rvalue reference to the contained value + */ + const T&& value() const&&; + + /* + * If the Try contains an exception, rethrow it. Otherwise do nothing. + */ + void throwIfFailed() const; + + /* + * Const dereference operator. If the Try contains an exception it will be + * rethrown. + * + * @returns const reference to the contained value + */ + const T& operator*() const& { + return value(); + } + /* + * Dereference operator. If the Try contains an exception it will be rethrown. + * + * @returns mutable reference to the contained value + */ + T& operator*() & { + return value(); + } + /* + * Mutable rvalue dereference operator. If the Try contains an exception it + * will be rethrown. + * + * @returns rvalue reference to the contained value + */ + T&& operator*() && { + return std::move(value()); + } + /* + * Const rvalue dereference operator. If the Try contains an exception it + * will be rethrown. + * + * @returns rvalue reference to the contained value + */ + const T&& operator*() const&& { + return std::move(value()); + } + + /* + * Const arrow operator. If the Try contains an exception it will be + * rethrown. + * + * @returns const reference to the contained value + */ + const T* operator->() const { + return &value(); + } + /* + * Arrow operator. If the Try contains an exception it will be rethrown. + * + * @returns mutable reference to the contained value + */ + T* operator->() { + return &value(); + } + + /* + * @returns True if the Try contains a value, false otherwise + */ + bool hasValue() const { + return contains_ == Contains::VALUE; + } + /* + * @returns True if the Try contains an exception, false otherwise + */ + bool hasException() const { + return contains_ == Contains::EXCEPTION; + } + + /* + * @returns True if the Try contains an exception of type Ex, false otherwise + */ + template <class Ex> + bool hasException() const { + return hasException() && e_.is_compatible_with<Ex>(); + } + + exception_wrapper& exception() & { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return e_; + } + + exception_wrapper&& exception() && { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return std::move(e_); + } + + const exception_wrapper& exception() const& { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return e_; + } + + const exception_wrapper&& exception() const&& { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return std::move(e_); + } + + /* + * @returns a pointer to the `std::exception` held by `*this`, if one is held; + * otherwise, returns `nullptr`. + */ + std::exception* tryGetExceptionObject() { + return hasException() ? e_.get_exception() : nullptr; + } + std::exception const* tryGetExceptionObject() const { + return hasException() ? e_.get_exception() : nullptr; + } + + /* + * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose + * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise, + * returns `nullptr`. + */ + template <class E> + E* tryGetExceptionObject() { + return hasException() ? e_.get_exception<E>() : nullptr; + } + template <class E> + E const* tryGetExceptionObject() const { + return hasException() ? e_.get_exception<E>() : nullptr; + } + + /* + * If the Try contains an exception and it is of type Ex, execute func(Ex) + * + * @param func a function that takes a single parameter of type const Ex& + * + * @returns True if the Try held an Ex and func was executed, false otherwise + */ + template <class Ex, class F> + bool withException(F func) { + if (!hasException()) { + return false; + } + return e_.with_exception<Ex>(std::move(func)); + } + template <class Ex, class F> + bool withException(F func) const { + if (!hasException()) { + return false; + } + return e_.with_exception<Ex>(std::move(func)); + } + + /* + * If the Try contains an exception and it is of type compatible with Ex as + * deduced from the first parameter of func, execute func(Ex) + * + * @param func a function that takes a single parameter of type const Ex& + * + * @returns True if the Try held an Ex and func was executed, false otherwise + */ + template <class F> + bool withException(F func) { + if (!hasException()) { + return false; + } + return e_.with_exception(std::move(func)); + } + template <class F> + bool withException(F func) const { + if (!hasException()) { + return false; + } + return e_.with_exception(std::move(func)); + } + + template <bool isTry, typename R> + typename std::enable_if<isTry, R>::type get() { + return std::forward<R>(*this); + } + + template <bool isTry, typename R> + typename std::enable_if<!isTry, R>::type get() { + return std::forward<R>(value()); + } + + private: + void destroy() noexcept; + + Contains contains_; + union { + T value_; + exception_wrapper e_; + }; +}; + +/* + * Specialization of Try for void value type. Encapsulates either success or an + * exception. + */ +template <> +class Try<void> { + public: + /* + * The value type for the Try + */ + typedef void element_type; + + // Construct a Try holding a successful and void result + Try() noexcept : hasValue_(true) {} + + /* + * Construct a Try with an exception_wrapper + * + * @param e The exception_wrapper + */ + explicit Try(exception_wrapper e) noexcept + : hasValue_(false), e_(std::move(e)) {} + + // Copy assigner + inline Try& operator=(const Try<void>& t) noexcept; + + // Copy constructor + Try(const Try<void>& t) noexcept : hasValue_(t.hasValue_) { + if (t.hasException()) { + new (&e_) exception_wrapper(t.e_); + } + } + + ~Try() { + if (hasException()) { + e_.~exception_wrapper(); + } + } + + /* + * In-place construct a 'void' value into this Try object. + * + * This has the effect of clearing any existing exception stored in the + * Try object. + */ + void emplace() noexcept { + if (hasException()) { + e_.~exception_wrapper(); + hasValue_ = true; + } + } + + /* + * In-place construct an exception in the Try object. + * + * Destroys any previous value prior to constructing the new value. + * Leaves *this in an empty state if the construction of the exception_wrapper + * throws. + * + * Any arguments passed to emplaceException() are forwarded on to the + * exception_wrapper constructor. + * + * @returns reference to the newly constructed exception_wrapper. + */ + template <typename... Args> + exception_wrapper& emplaceException(Args&&... args) noexcept( + std::is_nothrow_constructible<exception_wrapper, Args&&...>::value); + + // If the Try contains an exception, throws it + void value() const { + throwIfFailed(); + } + // Dereference operator. If the Try contains an exception, throws it + void operator*() const { + return value(); + } + + // If the Try contains an exception, throws it + inline void throwIfFailed() const; + + // @returns False if the Try contains an exception, true otherwise + bool hasValue() const { + return hasValue_; + } + // @returns True if the Try contains an exception, false otherwise + bool hasException() const { + return !hasValue_; + } + + // @returns True if the Try contains an exception of type Ex, false otherwise + template <class Ex> + bool hasException() const { + return hasException() && e_.is_compatible_with<Ex>(); + } + + /* + * @throws TryException if the Try doesn't contain an exception + * + * @returns mutable reference to the exception contained by this Try + */ + exception_wrapper& exception() & { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return e_; + } + + exception_wrapper&& exception() && { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return std::move(e_); + } + + const exception_wrapper& exception() const& { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return e_; + } + + const exception_wrapper&& exception() const&& { + if (!hasException()) { + throw_exception<TryException>("Try does not contain an exception"); + } + return std::move(e_); + } + + /* + * @returns a pointer to the `std::exception` held by `*this`, if one is held; + * otherwise, returns `nullptr`. + */ + std::exception* tryGetExceptionObject() { + return hasException() ? e_.get_exception() : nullptr; + } + std::exception const* tryGetExceptionObject() const { + return hasException() ? e_.get_exception() : nullptr; + } + + /* + * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose + * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise, + * returns `nullptr`. + */ + template <class E> + E* tryGetExceptionObject() { + return hasException() ? e_.get_exception<E>() : nullptr; + } + template <class E> + E const* tryGetExceptionObject() const { + return hasException() ? e_.get_exception<E>() : nullptr; + } + + /* + * If the Try contains an exception and it is of type Ex, execute func(Ex) + * + * @param func a function that takes a single parameter of type const Ex& + * + * @returns True if the Try held an Ex and func was executed, false otherwise + */ + template <class Ex, class F> + bool withException(F func) { + if (!hasException()) { + return false; + } + return e_.with_exception<Ex>(std::move(func)); + } + template <class Ex, class F> + bool withException(F func) const { + if (!hasException()) { + return false; + } + return e_.with_exception<Ex>(std::move(func)); + } + + /* + * If the Try contains an exception and it is of type compatible with Ex as + * deduced from the first parameter of func, execute func(Ex) + * + * @param func a function that takes a single parameter of type const Ex& + * + * @returns True if the Try held an Ex and func was executed, false otherwise + */ + template <class F> + bool withException(F func) { + if (!hasException()) { + return false; + } + return e_.with_exception(std::move(func)); + } + template <class F> + bool withException(F func) const { + if (!hasException()) { + return false; + } + return e_.with_exception(std::move(func)); + } + + template <bool, typename R> + R get() { + return std::forward<R>(*this); + } + + private: + bool hasValue_; + union { + exception_wrapper e_; + }; +}; + +template <typename T> +struct isTry : std::false_type {}; + +template <typename T> +struct isTry<Try<T>> : std::true_type {}; + +/* + * @param f a function to execute and capture the result of (value or exception) + * + * @returns Try holding the result of f + */ +template <typename F> +typename std::enable_if< + !std::is_same<invoke_result_t<F>, void>::value, + Try<invoke_result_t<F>>>::type +makeTryWithNoUnwrap(F&& f); + +/* + * Specialization of makeTryWith for void return + * + * @param f a function to execute and capture the result of + * + * @returns Try<void> holding the result of f + */ +template <typename F> +typename std:: + enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type + makeTryWithNoUnwrap(F&& f); + +/* + * @param f a function to execute and capture the result of (value or exception) + * + * @returns Try holding the result of f + */ +template <typename F> +typename std:: + enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type + makeTryWith(F&& f); + +/* + * Specialization of makeTryWith for functions that return Try<T> + * Causes makeTryWith to not double-wrap the try. + * + * @param f a function to execute and capture the result of + * + * @returns result of f if f did not throw. Otherwise Try<T> containing + * exception + */ +template <typename F> +typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>:: + type + makeTryWith(F&& f); + +/* + * Try to in-place construct a new value from the specified arguments. + * + * If T's constructor throws an exception then this is caught and the Try<T> + * object is initialised to hold that exception. + * + * @param args Are passed to T's constructor. + */ +template <typename T, typename... Args> +T* tryEmplace(Try<T>& t, Args&&... args) noexcept; + +/* + * Overload of tryEmplace() for Try<void>. + */ +inline void tryEmplace(Try<void>& t) noexcept; + +/* + * Try to in-place construct a new value from the result of a function. + * + * If the function completes successfully then attempts to in-place construct + * a value of type, T, passing the result of the function as the only parameter. + * + * If either the call to the function completes with an exception or the + * constructor completes with an exception then the exception is caught and + * stored in the Try object. + * + * @returns A pointer to the newly constructed object if it completed + * successfully, otherwise returns nullptr if the operation completed with + * an exception. + */ +template <typename T, typename Func> +T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept; + +/* + * Specialization of tryEmplaceWith() for Try<void>. + * + * Calls func() and if it doesn't throw an exception then calls t.emplace(). + * If func() throws then captures the exception in t using t.emplaceException(). + * + * Func must be callable with zero parameters and must return void. + * + * @returns true if func() completed without an exception, false if func() + * threw an exception. + */ +template <typename Func> +bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept; + +/** + * Tuple<Try<Type>...> -> std::tuple<Type...> + * + * Unwraps a tuple-like type containing a sequence of Try<Type> instances to + * std::tuple<Type> + */ +template <typename Tuple> +auto unwrapTryTuple(Tuple&&); + +} // namespace folly + +#include <folly/Try-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/UTF8String.h b/ios/Pods/Flipper-Folly/folly/UTF8String.h new file mode 100644 index 000000000..17333764b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/UTF8String.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <string> + +#include <boost/regex/pending/unicode_iterator.hpp> + +#include <folly/Range.h> + +namespace folly { + +template < + class Iterator = const char*, + class Base = folly::Range<boost::u8_to_u32_iterator<Iterator>>> +class UTF8Range : public Base { + public: + /* implicit */ UTF8Range(const folly::Range<Iterator> baseRange) + : Base( + boost::u8_to_u32_iterator<Iterator>( + baseRange.begin(), + baseRange.begin(), + baseRange.end()), + boost::u8_to_u32_iterator<Iterator>( + baseRange.end(), + baseRange.begin(), + baseRange.end())) {} + /* implicit */ UTF8Range(const std::string& baseString) + : Base(folly::Range<Iterator>(baseString)) {} +}; + +using UTF8StringPiece = UTF8Range<const char*>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Unicode.cpp b/ios/Pods/Flipper-Folly/folly/Unicode.cpp new file mode 100644 index 000000000..e75864520 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Unicode.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Unicode.h> +#include <folly/Conv.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +std::string codePointToUtf8(char32_t cp) { + std::string result; + + // Based on description from http://en.wikipedia.org/wiki/UTF-8. + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast<char>(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast<char>(0x80 | (0x3f & cp)); + result[0] = static_cast<char>(0xC0 | (cp >> 6)); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast<char>(0x80 | (0x3f & cp)); + result[1] = (0x80 | static_cast<char>((0x3f & (cp >> 6)))); + result[0] = (0xE0 | static_cast<char>(cp >> 12)); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast<char>(0x80 | (0x3f & cp)); + result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast<char>(0xF0 | (cp >> 18)); + } + + return result; +} + +char32_t utf8ToCodePoint( + const unsigned char*& p, + const unsigned char* const e, + bool skipOnError) { + /* The following encodings are valid, except for the 5 and 6 byte + * combinations: + * 0xxxxxxx + * 110xxxxx 10xxxxxx + * 1110xxxx 10xxxxxx 10xxxxxx + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + + const auto skip = [&] { + ++p; + return U'\ufffd'; + }; + + if (p >= e) { + if (skipOnError) { + return skip(); + } + throw std::runtime_error("folly::utf8ToCodePoint empty/invalid string"); + } + + unsigned char fst = *p; + if (!(fst & 0x80)) { + // trivial case + return *p++; + } + + static const uint32_t bitMask[] = { + (1 << 7) - 1, + (1 << 11) - 1, + (1 << 16) - 1, + (1 << 21) - 1, + }; + + // upper control bits are masked out later + uint32_t d = fst; + + if ((fst & 0xC0) != 0xC0) { + if (skipOnError) { + return skip(); + } + throw std::runtime_error( + to<std::string>("folly::utf8ToCodePoint i=0 d=", d)); + } + + fst <<= 1; + + for (unsigned int i = 1; i != 4 && p + i < e; ++i) { + const unsigned char tmp = p[i]; + + if ((tmp & 0xC0) != 0x80) { + if (skipOnError) { + return skip(); + } + throw std::runtime_error(to<std::string>( + "folly::utf8ToCodePoint i=", i, " tmp=", (uint32_t)tmp)); + } + + d = (d << 6) | (tmp & 0x3F); + fst <<= 1; + + if (!(fst & 0x80)) { + d &= bitMask[i]; + + // overlong, could have been encoded with i bytes + if ((d & ~bitMask[i - 1]) == 0) { + if (skipOnError) { + return skip(); + } + throw std::runtime_error( + to<std::string>("folly::utf8ToCodePoint i=", i, " d=", d)); + } + + // check for surrogates only needed for 3 bytes + if (i == 2) { + if ((d >= 0xD800 && d <= 0xDFFF) || d > 0x10FFFF) { + if (skipOnError) { + return skip(); + } + throw std::runtime_error( + to<std::string>("folly::utf8ToCodePoint i=", i, " d=", d)); + } + } + + p += i + 1; + return d; + } + } + + if (skipOnError) { + return skip(); + } + throw std::runtime_error("folly::utf8ToCodePoint encoding length maxed out"); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Unicode.h b/ios/Pods/Flipper-Folly/folly/Unicode.h new file mode 100644 index 000000000..bafe63854 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Unicode.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Some utility routines relating to unicode. + +#pragma once + +#include <string> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +/* + * Encode a single unicode code point into a UTF-8 byte sequence. + * + * Return value is undefined if `cp' is an invalid code point. + */ +std::string codePointToUtf8(char32_t cp); + +/* + * Decode a single unicode code point from UTF-8 byte sequence. + */ +char32_t utf8ToCodePoint( + const unsigned char*& p, + const unsigned char* const e, + bool skipOnError); + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Unit.h b/ios/Pods/Flipper-Folly/folly/Unit.h new file mode 100644 index 000000000..45ce444f5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Unit.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +namespace folly { + +/// In functional programming, the degenerate case is often called "unit". In +/// C++, "void" is often the best analogue. However, because of the syntactic +/// special-casing required for void, it is frequently a liability for template +/// metaprogramming. So, instead of writing specializations to handle cases like +/// SomeContainer<void>, a library author may instead rule that out and simply +/// have library users use SomeContainer<Unit>. Contained values may be ignored. +/// Much easier. +/// +/// "void" is the type that admits of no values at all. It is not possible to +/// construct a value of this type. +/// "unit" is the type that admits of precisely one unique value. It is +/// possible to construct a value of this type, but it is always the same value +/// every time, so it is uninteresting. +struct Unit { + constexpr bool operator==(const Unit& /*other*/) const { + return true; + } + constexpr bool operator!=(const Unit& /*other*/) const { + return false; + } +}; + +constexpr Unit unit{}; + +template <typename T> +struct lift_unit { + using type = T; +}; +template <> +struct lift_unit<void> { + using type = Unit; +}; +template <typename T> +using lift_unit_t = typename lift_unit<T>::type; + +template <typename T> +struct drop_unit { + using type = T; +}; +template <> +struct drop_unit<Unit> { + using type = void; +}; +template <typename T> +using drop_unit_t = typename drop_unit<T>::type; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Uri-inl.h b/ios/Pods/Flipper-Folly/folly/Uri-inl.h new file mode 100644 index 000000000..bd286f525 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Uri-inl.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_URI_H_ +#error This file may only be included from folly/Uri.h +#endif + +#include <functional> +#include <tuple> + +#include <folly/Conv.h> +#include <folly/hash/Hash.h> + +namespace folly { + +namespace uri_detail { + +using UriTuple = std::tuple< + const std::string&, + const std::string&, + const std::string&, + const std::string&, + uint16_t, + const std::string&, + const std::string&, + const std::string&>; + +inline UriTuple as_tuple(const folly::Uri& k) { + return UriTuple( + k.scheme(), + k.username(), + k.password(), + k.host(), + k.port(), + k.path(), + k.query(), + k.fragment()); +} + +} // namespace uri_detail + +template <class String> +String Uri::toString() const { + String str; + if (hasAuthority_) { + toAppend(scheme_, "://", &str); + if (!password_.empty()) { + toAppend(username_, ":", password_, "@", &str); + } else if (!username_.empty()) { + toAppend(username_, "@", &str); + } + toAppend(host_, &str); + if (port_ != 0) { + toAppend(":", port_, &str); + } + } else { + toAppend(scheme_, ":", &str); + } + toAppend(path_, &str); + if (!query_.empty()) { + toAppend("?", query_, &str); + } + if (!fragment_.empty()) { + toAppend("#", fragment_, &str); + } + return str; +} + +} // namespace folly + +namespace std { + +template <> +struct hash<folly::Uri> { + std::size_t operator()(const folly::Uri& k) const { + return std::hash<folly::uri_detail::UriTuple>{}( + folly::uri_detail::as_tuple(k)); + } +}; + +template <> +struct equal_to<folly::Uri> { + bool operator()(const folly::Uri& a, const folly::Uri& b) const { + return folly::uri_detail::as_tuple(a) == folly::uri_detail::as_tuple(b); + } +}; + +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/Uri.cpp b/ios/Pods/Flipper-Folly/folly/Uri.cpp new file mode 100644 index 000000000..26bada812 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Uri.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Uri.h> + +#include <algorithm> +#include <cctype> + +#include <boost/regex.hpp> + +namespace folly { + +namespace { + +std::string submatch(const boost::cmatch& m, int idx) { + const auto& sub = m[idx]; + return std::string(sub.first, sub.second); +} + +} // namespace + +Uri::Uri(StringPiece str) : hasAuthority_(false), port_(0) { + static const boost::regex uriRegex( + "([a-zA-Z][a-zA-Z0-9+.-]*):" // scheme: + "([^?#]*)" // authority and path + "(?:\\?([^#]*))?" // ?query + "(?:#(.*))?"); // #fragment + static const boost::regex authorityAndPathRegex("//([^/]*)(/.*)?"); + + boost::cmatch match; + if (UNLIKELY(!boost::regex_match(str.begin(), str.end(), match, uriRegex))) { + throw std::invalid_argument(to<std::string>("invalid URI ", str)); + } + + scheme_ = submatch(match, 1); + std::transform(scheme_.begin(), scheme_.end(), scheme_.begin(), ::tolower); + + StringPiece authorityAndPath(match[2].first, match[2].second); + boost::cmatch authorityAndPathMatch; + if (!boost::regex_match( + authorityAndPath.begin(), + authorityAndPath.end(), + authorityAndPathMatch, + authorityAndPathRegex)) { + // Does not start with //, doesn't have authority + hasAuthority_ = false; + path_ = authorityAndPath.str(); + } else { + static const boost::regex authorityRegex( + "(?:([^@:]*)(?::([^@]*))?@)?" // username, password + "(\\[[^\\]]*\\]|[^\\[:]*)" // host (IP-literal (e.g. '['+IPv6+']', + // dotted-IPv4, or named host) + "(?::(\\d*))?"); // port + + const auto authority = authorityAndPathMatch[1]; + boost::cmatch authorityMatch; + if (!boost::regex_match( + authority.first, + authority.second, + authorityMatch, + authorityRegex)) { + throw std::invalid_argument(to<std::string>( + "invalid URI authority ", + StringPiece(authority.first, authority.second))); + } + + StringPiece port(authorityMatch[4].first, authorityMatch[4].second); + if (!port.empty()) { + try { + port_ = to<uint16_t>(port); + } catch (ConversionError const& e) { + throw std::invalid_argument( + to<std::string>("invalid URI port: ", e.what())); + } + } + + hasAuthority_ = true; + username_ = submatch(authorityMatch, 1); + password_ = submatch(authorityMatch, 2); + host_ = submatch(authorityMatch, 3); + path_ = submatch(authorityAndPathMatch, 2); + } + + query_ = submatch(match, 3); + fragment_ = submatch(match, 4); +} + +std::string Uri::authority() const { + std::string result; + + // Port is 5 characters max and we have up to 3 delimiters. + result.reserve(host().size() + username().size() + password().size() + 8); + + if (!username().empty() || !password().empty()) { + result.append(username()); + + if (!password().empty()) { + result.push_back(':'); + result.append(password()); + } + + result.push_back('@'); + } + + result.append(host()); + + if (port() != 0) { + result.push_back(':'); + toAppend(port(), &result); + } + + return result; +} + +std::string Uri::hostname() const { + if (!host_.empty() && host_[0] == '[') { + // If it starts with '[', then it should end with ']', this is ensured by + // regex + return host_.substr(1, host_.size() - 2); + } + return host_; +} + +const std::vector<std::pair<std::string, std::string>>& Uri::getQueryParams() { + if (!query_.empty() && queryParams_.empty()) { + // Parse query string + static const boost::regex queryParamRegex( + "(^|&)" /*start of query or start of parameter "&"*/ + "([^=&]*)=?" /*parameter name and "=" if value is expected*/ + "([^=&]*)" /*parameter value*/ + "(?=(&|$))" /*forward reference, next should be end of query or + start of next parameter*/); + const boost::cregex_iterator paramBeginItr( + query_.data(), query_.data() + query_.size(), queryParamRegex); + boost::cregex_iterator paramEndItr; + for (auto itr = paramBeginItr; itr != paramEndItr; ++itr) { + if (itr->length(2) == 0) { + // key is empty, ignore it + continue; + } + queryParams_.emplace_back( + std::string((*itr)[2].first, (*itr)[2].second), // parameter name + std::string((*itr)[3].first, (*itr)[3].second) // parameter value + ); + } + } + return queryParams_; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Uri.h b/ios/Pods/Flipper-Folly/folly/Uri.h new file mode 100644 index 000000000..a8bfa63b3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Uri.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_URI_H_ + +#include <string> +#include <vector> + +#include <folly/String.h> + +namespace folly { + +/** + * Class representing a URI. + * + * Consider http://www.facebook.com/foo/bar?key=foo#anchor + * + * The URI is broken down into its parts: scheme ("http"), authority + * (ie. host and port, in most cases: "www.facebook.com"), path + * ("/foo/bar"), query ("key=foo") and fragment ("anchor"). The scheme is + * lower-cased. + * + * If this Uri represents a URL, note that, to prevent ambiguity, the component + * parts are NOT percent-decoded; you should do this yourself with + * uriUnescape() (for the authority and path) and uriUnescape(..., + * UriEscapeMode::QUERY) (for the query, but probably only after splitting at + * '&' to identify the individual parameters). + */ +class Uri { + public: + /** + * Parse a Uri from a string. Throws std::invalid_argument on parse error. + */ + explicit Uri(StringPiece str); + + const std::string& scheme() const { + return scheme_; + } + const std::string& username() const { + return username_; + } + const std::string& password() const { + return password_; + } + /** + * Get host part of URI. If host is an IPv6 address, square brackets will be + * returned, for example: "[::1]". + */ + const std::string& host() const { + return host_; + } + /** + * Get host part of URI. If host is an IPv6 address, square brackets will not + * be returned, for exmaple "::1"; otherwise it returns the same thing as + * host(). + * + * hostname() is what one needs to call if passing the host to any other tool + * or API that connects to that host/port; e.g. getaddrinfo() only understands + * IPv6 host without square brackets + */ + std::string hostname() const; + uint16_t port() const { + return port_; + } + const std::string& path() const { + return path_; + } + const std::string& query() const { + return query_; + } + const std::string& fragment() const { + return fragment_; + } + + std::string authority() const; + + template <class String> + String toString() const; + + std::string str() const { + return toString<std::string>(); + } + fbstring fbstr() const { + return toString<fbstring>(); + } + + void setPort(uint16_t port) { + hasAuthority_ = true; + port_ = port; + } + + /** + * Get query parameters as key-value pairs. + * e.g. for URI containing query string: key1=foo&key2=&key3&=bar&=bar= + * In returned list, there are 3 entries: + * "key1" => "foo" + * "key2" => "" + * "key3" => "" + * Parts "=bar" and "=bar=" are ignored, as they are not valid query + * parameters. "=bar" is missing parameter name, while "=bar=" has more than + * one equal signs, we don't know which one is the delimiter for key and + * value. + * + * Note, this method is not thread safe, it might update internal state, but + * only the first call to this method update the state. After the first call + * is finished, subsequent calls to this method are thread safe. + * + * @return query parameter key-value pairs in a vector, each element is a + * pair of which the first element is parameter name and the second + * one is parameter value + */ + const std::vector<std::pair<std::string, std::string>>& getQueryParams(); + + private: + std::string scheme_; + std::string username_; + std::string password_; + std::string host_; + bool hasAuthority_; + uint16_t port_; + std::string path_; + std::string query_; + std::string fragment_; + std::vector<std::pair<std::string, std::string>> queryParams_; +}; + +} // namespace folly + +#include <folly/Uri-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/Utility.h b/ios/Pods/Flipper-Folly/folly/Utility.h new file mode 100644 index 000000000..f2446a862 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Utility.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> +#include <limits> +#include <type_traits> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/Portability.h> +#include <folly/Traits.h> + +namespace folly { + +/** + * copy + * + * Usable when you have a function with two overloads: + * + * class MyData; + * void something(MyData&&); + * void something(const MyData&); + * + * Where the purpose is to make copies and moves explicit without having to + * spell out the full type names - in this case, for copies, to invoke copy + * constructors. + * + * When the caller wants to pass a copy of an lvalue, the caller may: + * + * void foo() { + * MyData data; + * something(folly::copy(data)); // explicit copy + * something(std::move(data)); // explicit move + * something(data); // const& - neither move nor copy + * } + * + * Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This + * can be used to to force a move, where just using std::move would not: + * + * folly::copy(std::move(data)); // force-move, not just a cast to && + * + * Note: The following text appears in the standard: + * + * > In several places in this Clause the operation //DECAY_COPY(x)// is used. + * > All such uses mean call the function `decay_copy(x)` and use the result, + * > where `decay_copy` is defined as follows: + * > + * > template <class T> decay_t<T> decay_copy(T&& v) + * > { return std::forward<T>(v); } + * > + * > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf + * > 30.2.6 `decay_copy` [thread.decaycopy]. + * + * We mimic it, with a `noexcept` specifier for good measure. + */ + +template <typename T> +constexpr typename std::decay<T>::type copy(T&& value) noexcept( + noexcept(typename std::decay<T>::type(std::forward<T>(value)))) { + return std::forward<T>(value); +} + +/** + * A simple helper for getting a constant reference to an object. + * + * Example: + * + * std::vector<int> v{1,2,3}; + * // The following two lines are equivalent: + * auto a = const_cast<const std::vector<int>&>(v).begin(); + * auto b = folly::as_const(v).begin(); + * + * Like C++17's std::as_const. See http://wg21.link/p0007 + */ +#if __cpp_lib_as_const || _LIBCPP_STD_VER > 14 || _MSC_VER + +/* using override */ using std::as_const; + +#else + +template <class T> +constexpr T const& as_const(T& t) noexcept { + return t; +} + +template <class T> +void as_const(T const&&) = delete; + +#endif + +// mimic: forward_like, p0847r0 +template <typename Src, typename Dst> +constexpr like_t<Src, Dst>&& forward_like(Dst&& dst) noexcept { + return static_cast<like_t<Src, Dst>&&>(std::forward<Dst>(dst)); +} + +/** + * Backports from C++17 of: + * std::in_place_t + * std::in_place_type_t + * std::in_place_index_t + * std::in_place + * std::in_place_type + * std::in_place_index + */ + +struct in_place_tag {}; +template <class> +struct in_place_type_tag {}; +template <std::size_t> +struct in_place_index_tag {}; + +using in_place_t = in_place_tag (&)(in_place_tag); +template <class T> +using in_place_type_t = in_place_type_tag<T> (&)(in_place_type_tag<T>); +template <std::size_t I> +using in_place_index_t = in_place_index_tag<I> (&)(in_place_index_tag<I>); + +inline in_place_tag in_place(in_place_tag = {}) { + return {}; +} +template <class T> +inline in_place_type_tag<T> in_place_type(in_place_type_tag<T> = {}) { + return {}; +} +template <std::size_t I> +inline in_place_index_tag<I> in_place_index(in_place_index_tag<I> = {}) { + return {}; +} + +/** + * Initializer lists are a powerful compile time syntax introduced in C++11 + * but due to their often conflicting syntax they are not used by APIs for + * construction. + * + * Further standard conforming compilers *strongly* favor an + * std::initializer_list overload for construction if one exists. The + * following is a simple tag used to disambiguate construction with + * initializer lists and regular uniform initialization. + * + * For example consider the following case + * + * class Something { + * public: + * explicit Something(int); + * Something(std::intiializer_list<int>); + * + * operator int(); + * }; + * + * ... + * Something something{1}; // SURPRISE!! + * + * The last call to instantiate the Something object will go to the + * initializer_list overload. Which may be surprising to users. + * + * If however this tag was used to disambiguate such construction it would be + * easy for users to see which construction overload their code was referring + * to. For example + * + * class Something { + * public: + * explicit Something(int); + * Something(folly::initlist_construct_t, std::initializer_list<int>); + * + * operator int(); + * }; + * + * ... + * Something something_one{1}; // not the initializer_list overload + * Something something_two{folly::initlist_construct, {1}}; // correct + */ +struct initlist_construct_t {}; +constexpr initlist_construct_t initlist_construct{}; + +// sorted_unique_t, sorted_unique +// +// A generic tag type and value to indicate that some constructor or method +// accepts a container in which the values are sorted and unique. +// +// Example: +// +// void takes_numbers(folly::sorted_unique_t, std::vector<int> alist) { +// assert(std::is_sorted(alist.begin(), alist.end())); +// assert(std::unique(alist.begin(), alist.end()) == alist.end()); +// for (i : alist) { +// // some behavior which safe only when alist is sorted and unique +// } +// } +// void takes_numbers(std::vector<int> alist) { +// std::sort(alist.begin(), alist.end()); +// alist.erase(std::unique(alist.begin(), alist.end()), alist.end()); +// takes_numbers(folly::sorted_unique, alist); +// } +// +// mimic: std::sorted_unique_t, std::sorted_unique, p0429r6 +struct sorted_unique_t {}; +constexpr sorted_unique_t sorted_unique; + +// sorted_equivalent_t, sorted_equivalent +// +// A generic tag type and value to indicate that some constructor or method +// accepts a container in which the values are sorted but not necessarily +// unique. +// +// Example: +// +// void takes_numbers(folly::sorted_equivalent_t, std::vector<int> alist) { +// assert(std::is_sorted(alist.begin(), alist.end())); +// for (i : alist) { +// // some behavior which safe only when alist is sorted +// } +// } +// void takes_numbers(std::vector<int> alist) { +// std::sort(alist.begin(), alist.end()); +// takes_numbers(folly::sorted_equivalent, alist); +// } +// +// mimic: std::sorted_equivalent_t, std::sorted_equivalent, p0429r6 +struct sorted_equivalent_t {}; +constexpr sorted_equivalent_t sorted_equivalent; + +template <typename T> +struct transparent : T { + using is_transparent = void; + using T::T; +}; + +/** + * A simple function object that passes its argument through unchanged. + * + * Example: + * + * int i = 42; + * int &j = Identity()(i); + * assert(&i == &j); + * + * Warning: passing a prvalue through Identity turns it into an xvalue, + * which can effect whether lifetime extension occurs or not. For instance: + * + * auto&& x = std::make_unique<int>(42); + * cout << *x ; // OK, x refers to a valid unique_ptr. + * + * auto&& y = Identity()(std::make_unique<int>(42)); + * cout << *y ; // ERROR: y did not lifetime-extend the unique_ptr. It + * // is no longer valid + */ +struct Identity { + template <class T> + constexpr T&& operator()(T&& x) const noexcept { + return static_cast<T&&>(x); + } +}; + +namespace moveonly_ { // Protection from unintended ADL. + +/** + * Disallow copy but not move in derived types. This is essentially + * boost::noncopyable (the implementation is almost identical) but it + * doesn't delete move constructor and move assignment. + */ +class MoveOnly { + protected: + constexpr MoveOnly() = default; + ~MoveOnly() = default; + + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly&) = delete; +}; + +} // namespace moveonly_ + +using MoveOnly = moveonly_::MoveOnly; + +template <typename T> +constexpr auto to_signed(T const& t) -> typename std::make_signed<T>::type { + using S = typename std::make_signed<T>::type; + // note: static_cast<S>(t) would be more straightforward, but it would also be + // implementation-defined behavior and that is typically to be avoided; the + // following code optimized into the same thing, though + constexpr auto m = static_cast<T>(std::numeric_limits<S>::max()); + return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t); +} + +template <typename T> +constexpr auto to_unsigned(T const& t) -> typename std::make_unsigned<T>::type { + using U = typename std::make_unsigned<T>::type; + return static_cast<U>(t); +} + +template <typename Src> +class to_narrow_convertible { + public: + static_assert(std::is_integral<Src>::value, "not an integer"); + + explicit constexpr to_narrow_convertible(Src const& value) noexcept + : value_(value) {} +#if __cplusplus >= 201703L + explicit to_narrow_convertible(to_narrow_convertible const&) = default; + explicit to_narrow_convertible(to_narrow_convertible&&) = default; +#else + to_narrow_convertible(to_narrow_convertible const&) = default; + to_narrow_convertible(to_narrow_convertible&&) = default; +#endif + to_narrow_convertible& operator=(to_narrow_convertible const&) = default; + to_narrow_convertible& operator=(to_narrow_convertible&&) = default; + + template < + typename Dst, + std::enable_if_t< + std::is_integral<Dst>::value && + std::is_signed<Dst>::value == std::is_signed<Src>::value, + int> = 0> + /* implicit */ constexpr operator Dst() const noexcept { + FOLLY_PUSH_WARNING + FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments + FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables + FOLLY_GNU_DISABLE_WARNING("-Wconversion") + return value_; + FOLLY_POP_WARNING + } + + private: + Src value_; +}; + +// to_narrow +// +// A utility for performing explicit possibly-narrowing integral conversion +// without specifying the destination type. Does not permit changing signs. +// Sometimes preferable to static_cast<Dst>(src) to document the intended +// semantics of the cast. +// +// Models explicit conversion with an elided destination type. Sits in between +// a stricter explicit conversion with a named destination type and a more +// lenient implicit conversion. Implemented with implicit conversion in order +// to take advantage of the undefined-behavior sanitizer's inspection of all +// implicit conversions - it checks for truncation, with suppressions in place +// for warnings which guard against narrowing implicit conversions. +template <typename Src> +constexpr auto to_narrow(Src const& src) -> to_narrow_convertible<Src> { + return to_narrow_convertible<Src>{src}; +} + +template <class E> +constexpr std::underlying_type_t<E> to_underlying(E e) noexcept { + static_assert(std::is_enum<E>::value, "not an enum type"); + return static_cast<std::underlying_type_t<E>>(e); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/Varint.h b/ios/Pods/Flipper-Folly/folly/Varint.h new file mode 100644 index 000000000..4dc47a418 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/Varint.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +#include <folly/Conv.h> +#include <folly/Expected.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Range.h> + +namespace folly { + +/** + * Variable-length integer encoding, using a little-endian, base-128 + * representation. + * + * The MSb is set on all bytes except the last. + * + * Details: + * https://developers.google.com/protocol-buffers/docs/encoding#varints + * + * If you want to encode multiple values, GroupVarint (in GroupVarint.h) + * is faster and likely smaller. + */ + +/** + * Maximum length (in bytes) of the varint encoding of a 32-bit value. + */ +constexpr size_t kMaxVarintLength32 = 5; + +/** + * Maximum length (in bytes) of the varint encoding of a 64-bit value. + */ +constexpr size_t kMaxVarintLength64 = 10; + +/** + * Encode a value in the given buffer, returning the number of bytes used + * for encoding. + * buf must have enough space to represent the value (at least + * kMaxVarintLength64 bytes to encode arbitrary 64-bit values) + */ +size_t encodeVarint(uint64_t val, uint8_t* buf); + +/** + * Determine the number of bytes needed to represent "val". + * 32-bit values need at most 5 bytes. + * 64-bit values need at most 10 bytes. + */ +int encodeVarintSize(uint64_t val); + +/** + * Decode a value from a given buffer, advances data past the returned value. + * Throws on error. + */ +template <class T> +uint64_t decodeVarint(Range<T*>& data); + +enum class DecodeVarintError { + TooManyBytes = 0, + TooFewBytes = 1, +}; + +/** + * A variant of decodeVarint() that does not throw on error. Useful in contexts + * where only part of a serialized varint may be attempted to be decoded, e.g., + * when a serialized varint arrives on the boundary of a network packet. + */ +template <class T> +Expected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data); + +/** + * ZigZag encoding that maps signed integers with a small absolute value + * to unsigned integers with a small (positive) values. Without this, + * encoding negative values using Varint would use up 9 or 10 bytes. + * + * if x >= 0, encodeZigZag(x) == 2*x + * if x < 0, encodeZigZag(x) == -2*x + 1 + */ + +inline uint64_t encodeZigZag(int64_t val) { + // Bit-twiddling magic stolen from the Google protocol buffer document; + // val >> 63 is an arithmetic shift because val is signed + auto uval = static_cast<uint64_t>(val); + return static_cast<uint64_t>((uval << 1) ^ (val >> 63)); +} + +inline int64_t decodeZigZag(uint64_t val) { + return static_cast<int64_t>((val >> 1) ^ -(val & 1)); +} + +// Implementation below + +inline size_t encodeVarint(uint64_t val, uint8_t* buf) { + uint8_t* p = buf; + while (val >= 128) { + *p++ = 0x80 | (val & 0x7f); + val >>= 7; + } + *p++ = uint8_t(val); + return size_t(p - buf); +} + +inline int encodeVarintSize(uint64_t val) { + if (folly::kIsArchAmd64) { + // __builtin_clzll is undefined for 0 + int highBit = 64 - __builtin_clzll(val | 1); + return (highBit + 6) / 7; + } else { + int s = 1; + while (val >= 128) { + ++s; + val >>= 7; + } + return s; + } +} + +template <class T> +inline uint64_t decodeVarint(Range<T*>& data) { + auto expected = tryDecodeVarint(data); + if (!expected) { + throw std::invalid_argument( + expected.error() == DecodeVarintError::TooManyBytes + ? "Invalid varint value: too many bytes." + : "Invalid varint value: too few bytes."); + } + return *expected; +} + +template <class T> +inline Expected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data) { + static_assert( + std::is_same<typename std::remove_cv<T>::type, char>::value || + std::is_same<typename std::remove_cv<T>::type, unsigned char>::value, + "Only character ranges are supported"); + + const int8_t* begin = reinterpret_cast<const int8_t*>(data.begin()); + const int8_t* end = reinterpret_cast<const int8_t*>(data.end()); + const int8_t* p = begin; + uint64_t val = 0; + + // end is always greater than or equal to begin, so this subtraction is safe + if (LIKELY(size_t(end - begin) >= kMaxVarintLength64)) { // fast path + int64_t b; + do { + b = *p++; + val = (b & 0x7f); + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 7; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 14; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 21; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 28; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 35; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 42; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 49; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 56; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x01) << 63; + if (b >= 0) { + break; + } + return makeUnexpected(DecodeVarintError::TooManyBytes); + } while (false); + } else { + int shift = 0; + while (p != end && *p < 0) { + val |= static_cast<uint64_t>(*p++ & 0x7f) << shift; + shift += 7; + } + if (p == end) { + return makeUnexpected(DecodeVarintError::TooFewBytes); + } + val |= static_cast<uint64_t>(*p++) << shift; + } + + data.uncheckedAdvance(p - begin); + return val; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/VirtualExecutor.h b/ios/Pods/Flipper-Folly/folly/VirtualExecutor.h new file mode 100644 index 000000000..fc28c2e49 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/VirtualExecutor.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/DefaultKeepAliveExecutor.h> + +namespace folly { + +/** + * VirtualExecutor implements a light-weight view onto existing Executor. + * + * Multiple VirtualExecutors can be backed by a single Executor. + * + * VirtualExecutor's destructor blocks until all tasks scheduled through it are + * complete. Executor's destructor also blocks until all VirtualExecutors + * backed by it are released. + */ +class VirtualExecutor : public DefaultKeepAliveExecutor { + auto wrapFunc(Func f) { + class FuncAndKeepAlive { + public: + FuncAndKeepAlive(Func&& f, VirtualExecutor* executor) + : keepAlive_(getKeepAliveToken(executor)), f_(std::move(f)) {} + + void operator()() { + f_(); + } + + private: + Executor::KeepAlive<VirtualExecutor> keepAlive_; + Func f_; + }; + + return FuncAndKeepAlive(std::move(f), this); + } + + public: + explicit VirtualExecutor(KeepAlive<> executor) + : executor_(std::move(executor)) { + assert(!isKeepAliveDummy(executor_)); + } + + explicit VirtualExecutor(Executor* executor) + : VirtualExecutor(getKeepAliveToken(executor)) {} + + explicit VirtualExecutor(Executor& executor) + : VirtualExecutor(getKeepAliveToken(executor)) {} + + VirtualExecutor(const VirtualExecutor&) = delete; + VirtualExecutor& operator=(const VirtualExecutor&) = delete; + + uint8_t getNumPriorities() const override { + return executor_->getNumPriorities(); + } + + void add(Func f) override { + executor_->add(wrapFunc(std::move(f))); + } + + void addWithPriority(Func f, int8_t priority) override { + executor_->addWithPriority(wrapFunc(std::move(f)), priority); + } + + ~VirtualExecutor() override { + joinKeepAlive(); + } + + private: + const KeepAlive<> executor_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/chrono/Conv.h b/ios/Pods/Flipper-Folly/folly/chrono/Conv.h new file mode 100644 index 000000000..88c6b769d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/chrono/Conv.h @@ -0,0 +1,684 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Conversions between std::chrono types and POSIX time types. + * + * These conversions will fail with a ConversionError if an overflow would + * occur performing the conversion. (e.g., if the input value cannot fit in + * the destination type). However they allow loss of precision (e.g., + * converting nanoseconds to a struct timeval which only has microsecond + * granularity, or a struct timespec to std::chrono::minutes). + */ + +#pragma once + +#include <chrono> +#include <type_traits> + +#include <folly/Conv.h> +#include <folly/Expected.h> +#include <folly/Utility.h> +#include <folly/portability/SysTime.h> +#include <folly/portability/SysTypes.h> + +namespace folly { +namespace detail { + +template <typename T> +struct is_duration : std::false_type {}; +template <typename Rep, typename Period> +struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {}; +template <typename T> +struct is_time_point : std::false_type {}; +template <typename Clock, typename Duration> +struct is_time_point<std::chrono::time_point<Clock, Duration>> + : std::true_type {}; +template <typename T> +struct is_std_chrono_type { + static constexpr bool value = + is_duration<T>::value || is_time_point<T>::value; +}; +template <typename T> +struct is_posix_time_type { + static constexpr bool value = std::is_same<T, struct timespec>::value || + std::is_same<T, struct timeval>::value; +}; +template <typename Tgt, typename Src> +struct is_chrono_conversion { + static constexpr bool value = + ((is_std_chrono_type<Tgt>::value && is_posix_time_type<Src>::value) || + (is_posix_time_type<Tgt>::value && is_std_chrono_type<Src>::value)); +}; + +/** + * This converts a number in some input type to time_t while ensuring that it + * fits in the range of numbers representable by time_t. + * + * This is similar to the normal folly::tryTo() behavior when converting + * arthmetic types to an integer type, except that it does not complain about + * floating point conversions losing precision. + */ +template <typename Src> +Expected<time_t, ConversionCode> chronoRangeCheck(Src value) { + if (value > std::numeric_limits<time_t>::max()) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + if (std::is_signed<Src>::value) { + if (value < std::numeric_limits<time_t>::lowest()) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + } + + return static_cast<time_t>(value); +} + +/** + * Convert a std::chrono::duration with second granularity to a pair of + * (seconds, subseconds) + * + * The SubsecondRatio template parameter specifies what type of subseconds to + * return. This must have a numerator of 1. + */ +template <typename SubsecondRatio, typename Rep> +static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( + const std::chrono::duration<Rep, std::ratio<1, 1>>& duration) { + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + auto sec = chronoRangeCheck(duration.count()); + if (sec.hasError()) { + return makeUnexpected(sec.error()); + } + + time_t secValue = sec.value(); + long subsec = 0L; + if (std::is_floating_point<Rep>::value) { + auto fraction = (duration.count() - secValue); + subsec = static_cast<long>(fraction * SubsecondRatio::den); + if (duration.count() < 0 && fraction < 0) { + if (secValue == std::numeric_limits<time_t>::lowest()) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + secValue -= 1; + subsec += SubsecondRatio::den; + } + } + return std::pair<time_t, long>{secValue, subsec}; +} + +/** + * Convert a std::chrono::duration with subsecond granularity to a pair of + * (seconds, subseconds) + */ +template <typename SubsecondRatio, typename Rep, std::intmax_t Denominator> +static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( + const std::chrono::duration<Rep, std::ratio<1, Denominator>>& duration) { + static_assert(Denominator != 1, "special case expecting den != 1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + auto sec = chronoRangeCheck(duration.count() / Denominator); + if (sec.hasError()) { + return makeUnexpected(sec.error()); + } + auto secTimeT = static_cast<time_t>(sec.value()); + + auto remainder = duration.count() - (secTimeT * Denominator); + long subsec = (remainder * SubsecondRatio::den) / Denominator; + if (UNLIKELY(duration.count() < 0) && remainder != 0) { + if (secTimeT == std::numeric_limits<time_t>::lowest()) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + secTimeT -= 1; + subsec += SubsecondRatio::den; + } + + return std::pair<time_t, long>{secTimeT, subsec}; +} + +/** + * Convert a std::chrono::duration with coarser-than-second granularity to a + * pair of (seconds, subseconds) + */ +template <typename SubsecondRatio, typename Rep, std::intmax_t Numerator> +static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( + const std::chrono::duration<Rep, std::ratio<Numerator, 1>>& duration) { + static_assert(Numerator != 1, "special case expecting num!=1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + constexpr auto maxValue = std::numeric_limits<time_t>::max() / Numerator; + constexpr auto minValue = std::numeric_limits<time_t>::lowest() / Numerator; + if (duration.count() > maxValue) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + if (duration.count() < minValue) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + + // Note that we can't use chronoRangeCheck() here since we have to check + // if (duration.count() * Numerator) would overflow (which we do above). + auto secOriginalRep = (duration.count() * Numerator); + auto sec = static_cast<time_t>(secOriginalRep); + + long subsec = 0L; + if (std::is_floating_point<Rep>::value) { + auto fraction = secOriginalRep - sec; + subsec = static_cast<long>(fraction * SubsecondRatio::den); + if (duration.count() < 0 && fraction < 0) { + if (sec == std::numeric_limits<time_t>::lowest()) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + sec -= 1; + subsec += SubsecondRatio::den; + } + } + return std::pair<time_t, long>{sec, subsec}; +} + +/* + * Helper classes for picking an intermediate duration type to use + * when doing conversions to/from durations where neither the numerator nor + * denominator are 1. + */ +template <typename T, bool IsFloatingPoint, bool IsSigned> +struct IntermediateTimeRep {}; +template <typename T, bool IsSigned> +struct IntermediateTimeRep<T, true, IsSigned> { + using type = T; +}; +template <typename T> +struct IntermediateTimeRep<T, false, true> { + using type = intmax_t; +}; +template <typename T> +struct IntermediateTimeRep<T, false, false> { + using type = uintmax_t; +}; +// For IntermediateDuration we always use 1 as the numerator, and the original +// Period denominator. This ensures that we do not lose precision when +// performing the conversion. +template <typename Rep, typename Period> +using IntermediateDuration = std::chrono::duration< + typename IntermediateTimeRep< + Rep, + std::is_floating_point<Rep>::value, + std::is_signed<Rep>::value>::type, + std::ratio<1, Period::den>>; + +/** + * Convert a std::chrono::duration to a pair of (seconds, subseconds) + * + * This overload is only used for unusual durations where neither the numerator + * nor denominator are 1. + */ +template <typename SubsecondRatio, typename Rep, typename Period> +Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( + const std::chrono::duration<Rep, Period>& duration) { + static_assert(Period::num != 1, "should use special-case code when num==1"); + static_assert(Period::den != 1, "should use special-case code when den==1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + // Perform this conversion by first converting to a duration where the + // numerator is 1, then convert to the output type. + using IntermediateType = IntermediateDuration<Rep, Period>; + using IntermediateRep = typename IntermediateType::rep; + + // Check to see if we would have overflow converting to the intermediate + // type. + constexpr auto maxInput = + std::numeric_limits<IntermediateRep>::max() / Period::num; + if (duration.count() > maxInput) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + constexpr auto minInput = + std::numeric_limits<IntermediateRep>::min() / Period::num; + if (duration.count() < minInput) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + auto intermediate = + IntermediateType{static_cast<IntermediateRep>(duration.count()) * + static_cast<IntermediateRep>(Period::num)}; + + return durationToPosixTime<SubsecondRatio>(intermediate); +} + +/** + * Check for overflow when converting to a duration type that is second + * granularity or finer (e.g., nanoseconds, milliseconds, seconds) + * + * This assumes the input is normalized, with subseconds >= 0 and subseconds + * less than 1 second. + */ +template <bool IsFloatingPoint> +struct CheckOverflowToDuration { + template < + typename Tgt, + typename SubsecondRatio, + typename Seconds, + typename Subseconds> + static ConversionCode check(Seconds seconds, Subseconds subseconds) { + static_assert( + Tgt::period::num == 1, + "this implementation should only be used for subsecond granularity " + "duration types"); + static_assert( + !std::is_floating_point<typename Tgt::rep>::value, "incorrect usage"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + if (LIKELY(seconds >= 0)) { + constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max(); + constexpr auto maxSeconds = maxCount / Tgt::period::den; + + auto unsignedSeconds = to_unsigned(seconds); + if (LIKELY(unsignedSeconds < maxSeconds)) { + return ConversionCode::SUCCESS; + } + + if (UNLIKELY(unsignedSeconds == maxSeconds)) { + constexpr auto maxRemainder = + maxCount - (maxSeconds * Tgt::period::den); + constexpr auto maxSubseconds = + (maxRemainder * SubsecondRatio::den) / Tgt::period::den; + if (subseconds <= 0) { + return ConversionCode::SUCCESS; + } + if (to_unsigned(subseconds) <= maxSubseconds) { + return ConversionCode::SUCCESS; + } + } + return ConversionCode::POSITIVE_OVERFLOW; + } else if (std::is_unsigned<typename Tgt::rep>::value) { + return ConversionCode::NEGATIVE_OVERFLOW; + } else { + constexpr auto minCount = + to_signed(std::numeric_limits<typename Tgt::rep>::lowest()); + constexpr auto minSeconds = (minCount / Tgt::period::den); + if (LIKELY(seconds >= minSeconds)) { + return ConversionCode::SUCCESS; + } + + if (UNLIKELY(seconds == minSeconds - 1)) { + constexpr auto maxRemainder = + minCount - (minSeconds * Tgt::period::den) + Tgt::period::den; + constexpr auto maxSubseconds = + (maxRemainder * SubsecondRatio::den) / Tgt::period::den; + if (subseconds <= 0) { + return ConversionCode::NEGATIVE_OVERFLOW; + } + if (subseconds >= maxSubseconds) { + return ConversionCode::SUCCESS; + } + } + return ConversionCode::NEGATIVE_OVERFLOW; + } + } +}; + +template <> +struct CheckOverflowToDuration<true> { + template < + typename Tgt, + typename SubsecondRatio, + typename Seconds, + typename Subseconds> + static ConversionCode check( + Seconds /* seconds */, + Subseconds /* subseconds */) { + static_assert( + std::is_floating_point<typename Tgt::rep>::value, "incorrect usage"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + // We expect floating point types to have much a wider representable range + // than integer types, so we don't bother actually checking the input + // integer value here. + static_assert( + std::numeric_limits<typename Tgt::rep>::max() >= + std::numeric_limits<Seconds>::max(), + "unusually limited floating point type"); + static_assert( + std::numeric_limits<typename Tgt::rep>::lowest() <= + std::numeric_limits<Seconds>::lowest(), + "unusually limited floating point type"); + + return ConversionCode::SUCCESS; + } +}; + +/** + * Convert a timeval or a timespec to a std::chrono::duration with second + * granularity. + * + * The SubsecondRatio template parameter specifies what type of subseconds to + * return. This must have a numerator of 1. + * + * The input must be in normalized form: the subseconds field must be greater + * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1 + * second). + */ +template < + typename SubsecondRatio, + typename Seconds, + typename Subseconds, + typename Rep> +auto posixTimeToDuration( + Seconds seconds, + Subseconds subseconds, + std::chrono::duration<Rep, std::ratio<1, 1>> dummy) + -> Expected<decltype(dummy), ConversionCode> { + using Tgt = decltype(dummy); + static_assert(Tgt::period::num == 1, "special case expecting num==1"); + static_assert(Tgt::period::den == 1, "special case expecting den==1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + auto outputSeconds = tryTo<typename Tgt::rep>(seconds); + if (outputSeconds.hasError()) { + return makeUnexpected(outputSeconds.error()); + } + + if (std::is_floating_point<typename Tgt::rep>::value) { + return Tgt{typename Tgt::rep(seconds) + + (typename Tgt::rep(subseconds) / SubsecondRatio::den)}; + } + + // If the value is negative, we have to round up a non-zero subseconds value + if (UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) { + if (UNLIKELY( + outputSeconds.value() == + std::numeric_limits<typename Tgt::rep>::lowest())) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + return Tgt{outputSeconds.value() + 1}; + } + + return Tgt{outputSeconds.value()}; +} + +/** + * Convert a timeval or a timespec to a std::chrono::duration with subsecond + * granularity + */ +template < + typename SubsecondRatio, + typename Seconds, + typename Subseconds, + typename Rep, + std::intmax_t Denominator> +auto posixTimeToDuration( + Seconds seconds, + Subseconds subseconds, + std::chrono::duration<Rep, std::ratio<1, Denominator>> dummy) + -> Expected<decltype(dummy), ConversionCode> { + using Tgt = decltype(dummy); + static_assert(Tgt::period::num == 1, "special case expecting num==1"); + static_assert(Tgt::period::den != 1, "special case expecting den!=1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + auto errorCode = detail::CheckOverflowToDuration< + std::is_floating_point<typename Tgt::rep>::value>:: + template check<Tgt, SubsecondRatio>(seconds, subseconds); + if (errorCode != ConversionCode::SUCCESS) { + return makeUnexpected(errorCode); + } + + if (LIKELY(seconds >= 0)) { + return std::chrono::duration_cast<Tgt>( + std::chrono::duration<typename Tgt::rep>{seconds}) + + std::chrono::duration_cast<Tgt>( + std::chrono::duration<typename Tgt::rep, SubsecondRatio>{ + subseconds}); + } else { + // For negative numbers we have to round subseconds up towards zero, even + // though it is a positive value, since the overall value is negative. + return std::chrono::duration_cast<Tgt>( + std::chrono::duration<typename Tgt::rep>{seconds + 1}) - + std::chrono::duration_cast<Tgt>( + std::chrono::duration<typename Tgt::rep, SubsecondRatio>{ + SubsecondRatio::den - subseconds}); + } +} + +/** + * Convert a timeval or a timespec to a std::chrono::duration with + * granularity coarser than 1 second. + */ +template < + typename SubsecondRatio, + typename Seconds, + typename Subseconds, + typename Rep, + std::intmax_t Numerator> +auto posixTimeToDuration( + Seconds seconds, + Subseconds subseconds, + std::chrono::duration<Rep, std::ratio<Numerator, 1>> dummy) + -> Expected<decltype(dummy), ConversionCode> { + using Tgt = decltype(dummy); + static_assert(Tgt::period::num != 1, "special case expecting num!=1"); + static_assert(Tgt::period::den == 1, "special case expecting den==1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + if (UNLIKELY(seconds < 0) && subseconds > 0) { + // Increment seconds by one to handle truncation of negative numbers + // properly. + if (UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + seconds += 1; + } + + if (std::is_floating_point<typename Tgt::rep>::value) { + // Convert to the floating point type before performing the division + return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num}; + } else { + // Perform the division as an integer, and check that the result fits in + // the output integer type + auto outputValue = (seconds / Tgt::period::num); + auto expectedOuput = tryTo<typename Tgt::rep>(outputValue); + if (expectedOuput.hasError()) { + return makeUnexpected(expectedOuput.error()); + } + + return Tgt{expectedOuput.value()}; + } +} + +/** + * Convert a timeval or timespec to a std::chrono::duration + * + * This overload is only used for unusual durations where neither the numerator + * nor denominator are 1. + */ +template < + typename SubsecondRatio, + typename Seconds, + typename Subseconds, + typename Rep, + std::intmax_t Denominator, + std::intmax_t Numerator> +auto posixTimeToDuration( + Seconds seconds, + Subseconds subseconds, + std::chrono::duration<Rep, std::ratio<Numerator, Denominator>> dummy) + -> Expected<decltype(dummy), ConversionCode> { + using Tgt = decltype(dummy); + static_assert( + Tgt::period::num != 1, "should use special-case code when num==1"); + static_assert( + Tgt::period::den != 1, "should use special-case code when den==1"); + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + // Cast through an intermediate type with subsecond granularity. + // Note that this could fail due to overflow during the initial conversion + // even if the result is representable in the output POSIX-style types. + // + // Note that for integer type conversions going through this intermediate + // type can result in slight imprecision due to truncating the intermediate + // calculation to an integer. + using IntermediateType = + IntermediateDuration<typename Tgt::rep, typename Tgt::period>; + auto intermediate = posixTimeToDuration<SubsecondRatio>( + seconds, subseconds, IntermediateType{}); + if (intermediate.hasError()) { + return makeUnexpected(intermediate.error()); + } + // Now convert back to the target duration. Use tryTo() to confirm that the + // result fits in the target representation type. + return tryTo<typename Tgt::rep>( + intermediate.value().count() / Tgt::period::num) + .then([](typename Tgt::rep tgt) { return Tgt{tgt}; }); +} + +template < + typename Tgt, + typename SubsecondRatio, + typename Seconds, + typename Subseconds> +Expected<Tgt, ConversionCode> tryPosixTimeToDuration( + Seconds seconds, + Subseconds subseconds) { + static_assert( + SubsecondRatio::num == 1, "subsecond numerator should always be 1"); + + // Normalize the input if required + if (UNLIKELY(subseconds < 0)) { + const auto overflowSeconds = (subseconds / SubsecondRatio::den); + const auto remainder = (subseconds % SubsecondRatio::den); + if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds > + seconds) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + seconds = seconds - 1 + overflowSeconds; + subseconds = to_narrow(remainder + SubsecondRatio::den); + } else if (UNLIKELY(subseconds >= SubsecondRatio::den)) { + const auto overflowSeconds = (subseconds / SubsecondRatio::den); + const auto remainder = (subseconds % SubsecondRatio::den); + if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + seconds += overflowSeconds; + subseconds = to_narrow(remainder); + } + + return posixTimeToDuration<SubsecondRatio>(seconds, subseconds, Tgt{}); +} + +} // namespace detail + +/** + * struct timespec to std::chrono::duration + */ +template <typename Tgt> +typename std::enable_if< + detail::is_duration<Tgt>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const struct timespec& ts) { + return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec); +} + +/** + * struct timeval to std::chrono::duration + */ +template <typename Tgt> +typename std::enable_if< + detail::is_duration<Tgt>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const struct timeval& tv) { + return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec); +} + +/** + * timespec or timeval to std::chrono::time_point + */ +template <typename Tgt, typename Src> +typename std::enable_if< + detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const Src& value) { + return tryTo<typename Tgt::duration>(value).then( + [](typename Tgt::duration result) { return Tgt(result); }); +} + +/** + * std::chrono::duration to struct timespec + */ +template <typename Tgt, typename Rep, typename Period> +typename std::enable_if< + std::is_same<Tgt, struct timespec>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const std::chrono::duration<Rep, Period>& duration) { + auto result = detail::durationToPosixTime<std::nano>(duration); + if (result.hasError()) { + return makeUnexpected(result.error()); + } + + struct timespec ts; + ts.tv_sec = result.value().first; + ts.tv_nsec = result.value().second; + return ts; +} + +/** + * std::chrono::duration to struct timeval + */ +template <typename Tgt, typename Rep, typename Period> +typename std::enable_if< + std::is_same<Tgt, struct timeval>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const std::chrono::duration<Rep, Period>& duration) { + auto result = detail::durationToPosixTime<std::micro>(duration); + if (result.hasError()) { + return makeUnexpected(result.error()); + } + + struct timeval tv; + tv.tv_sec = result.value().first; + tv.tv_usec = result.value().second; + return tv; +} + +/** + * std::chrono::time_point to timespec or timeval + */ +template <typename Tgt, typename Clock, typename Duration> +typename std::enable_if< + detail::is_posix_time_type<Tgt>::value, + Expected<Tgt, ConversionCode>>::type +tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) { + return tryTo<Tgt>(timePoint.time_since_epoch()); +} + +/** + * For all chrono conversions, to() wraps tryTo() + */ +template <typename Tgt, typename Src> +typename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>:: + type + to(const Src& value) { + return tryTo<Tgt>(value).thenOrThrow( + [](Tgt res) { return res; }, + [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); }); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/chrono/Hardware.h b/ios/Pods/Flipper-Folly/folly/chrono/Hardware.h new file mode 100644 index 000000000..801c46803 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/chrono/Hardware.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +#include <chrono> +#include <cstdint> + +#if defined(_MSC_VER) +extern "C" std::uint64_t __rdtsc(); +#pragma intrinsic(__rdtsc) +#endif + +namespace folly { + +inline std::uint64_t hardware_timestamp() { +#if defined(_MSC_VER) + return __rdtsc(); +#elif defined(__GNUC__) && (defined(__i386__) || FOLLY_X64) + return __builtin_ia32_rdtsc(); +#else + // use steady_clock::now() as an approximation for the timestamp counter on + // non-x86 systems + return std::chrono::steady_clock::now().time_since_epoch().count(); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h b/ios/Pods/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h new file mode 100644 index 000000000..a4bfc6610 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h @@ -0,0 +1,402 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/PackedSyncPtr.h> +#include <folly/concurrency/detail/AtomicSharedPtr-detail.h> +#include <folly/synchronization/AtomicStruct.h> +#include <folly/synchronization/detail/AtomicUtils.h> +#include <atomic> +#include <thread> + +/* + * This is an implementation of the std::atomic_shared_ptr TS + * http://en.cppreference.com/w/cpp/experimental/atomic_shared_ptr + * https://isocpp.org/files/papers/N4162.pdf + * + * AFAIK, the only other implementation is Anthony Williams from + * Just::thread library: + * + * https://bitbucket.org/anthonyw/atomic_shared_ptr + * + * implementation details: + * + * Basically, three things need to be atomically exchanged to make this work: + * * the local count + * * the pointer to the control block + * * the aliased pointer, if any. + * + * The Williams version does it with DWcas: 32 bits for local count, 64 + * bits for control block ptr, and he changes the shared_ptr + * implementation to also store the aliased pointers using a linked list + * like structure, and provides 32-bit index accessors to them (like + * IndexedMemPool trick). + * + * This version instead stores the 48 bits of address, plus 16 bits of + * local count in a single 8byte pointer. This avoids 'lock cmpxchg16b', + * which is much slower than 'lock xchg' in the normal 'store' case. In + * the less-common aliased pointer scenaro, we just allocate it in a new + * block, and store a pointer to that instead. + * + * Note that even if we only want to use the 3-bits of pointer alignment, + * this trick should still work - Any more than 4 concurrent accesses + * will have to go to an external map count instead (slower, but lots of + * concurrent access will be slow anyway due to bouncing cachelines). + * + * As a perf optimization, we currently batch up local count and only + * move it global every once in a while. This means load() is usually + * only a single atomic operation, instead of 3. For this trick to work, + * we probably need at least 8 bits to make batching worth it. + */ + +// A note on noexcept: If the pointer is an aliased pointer, +// store() will allocate. Otherwise is noexcept. +namespace folly { + +template < + typename T, + template <typename> class Atom = std::atomic, + typename CountedDetail = detail::shared_ptr_internals> +class atomic_shared_ptr { + using SharedPtr = typename CountedDetail::template CountedPtr<T>; + using BasePtr = typename CountedDetail::counted_base; + using PackedPtr = folly::PackedSyncPtr<BasePtr>; + + public: + atomic_shared_ptr() noexcept { + init(); + } + explicit atomic_shared_ptr(SharedPtr foo) /* noexcept */ + : atomic_shared_ptr() { + store(std::move(foo)); + } + atomic_shared_ptr(const atomic_shared_ptr<T>&) = delete; + + ~atomic_shared_ptr() { + store(SharedPtr(nullptr)); + } + void operator=(SharedPtr desired) /* noexcept */ { + store(std::move(desired)); + } + void operator=(const atomic_shared_ptr<T>&) = delete; + + bool is_lock_free() const noexcept { + // lock free unless more than EXTERNAL_OFFSET threads are + // contending and they all get unlucky and scheduled out during + // load(). + // + // TODO: Could use a lock-free external map to fix this + // corner case. + return true; + } + + SharedPtr load(std::memory_order order = std::memory_order_seq_cst) const + noexcept { + auto local = takeOwnedBase(order); + return get_shared_ptr(local, false); + } + + /* implicit */ operator SharedPtr() const { + return load(); + } + + void store( + SharedPtr n, + std::memory_order order = std::memory_order_seq_cst) /* noexcept */ { + auto newptr = get_newptr(std::move(n)); + auto old = ptr_.exchange(newptr, order); + release_external(old); + } + + SharedPtr exchange( + SharedPtr n, + std::memory_order order = std::memory_order_seq_cst) /* noexcept */ { + auto newptr = get_newptr(std::move(n)); + auto old = ptr_.exchange(newptr, order); + + SharedPtr old_ptr; + + if (old.get()) { + old_ptr = get_shared_ptr(old); + release_external(old); + } + + return old_ptr; + } + + bool compare_exchange_weak( + SharedPtr& expected, + const SharedPtr& n, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_weak( + expected, n, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_weak( + SharedPtr& expected, + const SharedPtr& n, + std::memory_order success, + std::memory_order failure) /* noexcept */ { + auto newptr = get_newptr(n); + PackedPtr oldptr, expectedptr; + + oldptr = takeOwnedBase(success); + if (!owners_eq(oldptr, CountedDetail::get_counted_base(expected))) { + expected = get_shared_ptr(oldptr, false); + release_external(newptr); + return false; + } + expectedptr = oldptr; // Need oldptr to release if failed + if (ptr_.compare_exchange_weak(expectedptr, newptr, success, failure)) { + if (oldptr.get()) { + release_external(oldptr, -1); + } + return true; + } else { + if (oldptr.get()) { + expected = get_shared_ptr(oldptr, false); + } else { + expected = SharedPtr(nullptr); + } + release_external(newptr); + return false; + } + } + bool compare_exchange_weak( + SharedPtr& expected, + SharedPtr&& desired, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_weak( + expected, desired, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_weak( + SharedPtr& expected, + SharedPtr&& desired, + std::memory_order success, + std::memory_order failure) /* noexcept */ { + return compare_exchange_weak(expected, desired, success, failure); + } + bool compare_exchange_strong( + SharedPtr& expected, + const SharedPtr& n, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_strong( + expected, n, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_strong( + SharedPtr& expected, + const SharedPtr& n, + std::memory_order success, + std::memory_order failure) /* noexcept */ { + auto local_expected = expected; + do { + if (compare_exchange_weak(expected, n, success, failure)) { + return true; + } + } while (local_expected == expected); + + return false; + } + bool compare_exchange_strong( + SharedPtr& expected, + SharedPtr&& desired, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_strong( + expected, desired, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_strong( + SharedPtr& expected, + SharedPtr&& desired, + std::memory_order success, + std::memory_order failure) /* noexcept */ { + return compare_exchange_strong(expected, desired, success, failure); + } + + private: + // Matches packed_sync_pointer. Must be > max number of local + // counts. This is the max number of threads that can access this + // atomic_shared_ptr at once before we start blocking. + static constexpr unsigned EXTERNAL_OFFSET{0x2000}; + // Bit signifying aliased constructor + static constexpr unsigned ALIASED_PTR{0x4000}; + + mutable AtomicStruct<PackedPtr, Atom> ptr_; + + void add_external(BasePtr* res, int64_t c = 0) const { + assert(res); + CountedDetail::inc_shared_count(res, EXTERNAL_OFFSET + c); + } + void release_external(PackedPtr& res, int64_t c = 0) const { + if (!res.get()) { + return; + } + int64_t count = get_local_count(res) + c; + int64_t diff = EXTERNAL_OFFSET - count; + assert(diff >= 0); + CountedDetail::template release_shared<T>(res.get(), diff); + } + PackedPtr get_newptr(const SharedPtr& n) const { + BasePtr* newval; + unsigned count = 0; + if (!n) { + newval = nullptr; + } else { + newval = CountedDetail::get_counted_base(n); + if (n.get() != CountedDetail::template get_shared_ptr<T>(newval)) { + // This is an aliased sharedptr. Make an un-aliased one + // by wrapping in *another* shared_ptr. + auto data = CountedDetail::template make_ptr<SharedPtr>(n); + newval = CountedDetail::get_counted_base(data); + count = ALIASED_PTR; + // (add external must happen before data goes out of scope) + add_external(newval); + } else { + add_external(newval); + } + } + + PackedPtr newptr; + newptr.init(newval, count); + + return newptr; + } + PackedPtr get_newptr(SharedPtr&& n) const { + BasePtr* newval; + unsigned count = 0; + if (!n) { + newval = nullptr; + } else { + newval = CountedDetail::get_counted_base(n); + if (n.get() != CountedDetail::template get_shared_ptr<T>(newval)) { + // This is an aliased sharedptr. Make an un-aliased one + // by wrapping in *another* shared_ptr. + auto data = CountedDetail::template make_ptr<SharedPtr>(std::move(n)); + newval = CountedDetail::get_counted_base(data); + count = ALIASED_PTR; + CountedDetail::release_ptr(data); + add_external(newval, -1); + } else { + CountedDetail::release_ptr(n); + add_external(newval, -1); + } + } + + PackedPtr newptr; + newptr.init(newval, count); + + return newptr; + } + void init() { + PackedPtr data; + data.init(); + ptr_.store(data); + } + + unsigned int get_local_count(const PackedPtr& p) const { + return p.extra() & ~ALIASED_PTR; + } + + // Check pointer equality considering wrapped aliased pointers. + bool owners_eq(PackedPtr& p1, BasePtr* p2) { + bool aliased1 = p1.extra() & ALIASED_PTR; + if (aliased1) { + auto p1a = CountedDetail::template get_shared_ptr_from_counted_base<T>( + p1.get(), false); + return CountedDetail::get_counted_base(p1a) == p2; + } + return p1.get() == p2; + } + + SharedPtr get_shared_ptr(const PackedPtr& p, bool inc = true) const { + bool aliased = p.extra() & ALIASED_PTR; + + auto res = CountedDetail::template get_shared_ptr_from_counted_base<T>( + p.get(), inc); + if (aliased) { + auto aliasedp = + CountedDetail::template get_shared_ptr_from_counted_base<SharedPtr>( + p.get()); + res = *aliasedp; + } + return res; + } + + /* Get a reference to the pointer, either from the local batch or + * from the global count. + * + * return is the base ptr, and the previous local count, if it is + * needed for compare_and_swap later. + */ + PackedPtr takeOwnedBase(std::memory_order order) const noexcept { + PackedPtr local, newlocal; + local = ptr_.load(std::memory_order_acquire); + while (true) { + if (!local.get()) { + return local; + } + newlocal = local; + if (get_local_count(newlocal) + 1 > EXTERNAL_OFFSET) { + // spinlock in the rare case we have more than + // EXTERNAL_OFFSET threads trying to access at once. + std::this_thread::yield(); + // Force DeterministicSchedule to choose a different thread + local = ptr_.load(std::memory_order_acquire); + } else { + newlocal.setExtra(newlocal.extra() + 1); + assert(get_local_count(newlocal) > 0); + if (ptr_.compare_exchange_weak(local, newlocal, order)) { + break; + } + } + } + + // Check if we need to push a batch from local -> global + auto batchcount = EXTERNAL_OFFSET / 2; + if (get_local_count(newlocal) > batchcount) { + CountedDetail::inc_shared_count(newlocal.get(), batchcount); + putOwnedBase(newlocal.get(), batchcount, order); + } + + return newlocal; + } + + void putOwnedBase(BasePtr* p, unsigned int count, std::memory_order mo) const + noexcept { + PackedPtr local = ptr_.load(std::memory_order_acquire); + while (true) { + if (local.get() != p) { + break; + } + auto newlocal = local; + if (get_local_count(local) > count) { + newlocal.setExtra(local.extra() - count); + } else { + // Otherwise it may be the same pointer, but someone else won + // the compare_exchange below, local count was already made + // global. We decrement the global count directly instead of + // the local one. + break; + } + if (ptr_.compare_exchange_weak(local, newlocal, mo)) { + return; + } + } + + CountedDetail::template release_shared<T>(p, count); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.cpp b/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.cpp new file mode 100644 index 000000000..f25800a02 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/concurrency/CacheLocality.h> + +#ifndef _MSC_VER +#define _GNU_SOURCE 1 // for RTLD_NOLOAD +#include <dlfcn.h> +#endif +#include <fstream> + +#include <folly/Conv.h> +#include <folly/Exception.h> +#include <folly/FileUtil.h> +#include <folly/Format.h> +#include <folly/ScopeGuard.h> + +namespace folly { + +///////////// CacheLocality + +/// Returns the CacheLocality information best for this machine +static CacheLocality getSystemLocalityInfo() { + if (kIsLinux) { + try { + return CacheLocality::readFromProcCpuinfo(); + } catch (...) { + // keep trying + } + } + + long numCpus = sysconf(_SC_NPROCESSORS_CONF); + if (numCpus <= 0) { + // This shouldn't happen, but if it does we should try to keep + // going. We are probably not going to be able to parse /sys on + // this box either (although we will try), which means we are going + // to fall back to the SequentialThreadId splitter. On my 16 core + // (x hyperthreading) dev box 16 stripes is enough to get pretty good + // contention avoidance with SequentialThreadId, and there is little + // improvement from going from 32 to 64. This default gives us some + // wiggle room + numCpus = 32; + } + return CacheLocality::uniform(size_t(numCpus)); +} + +template <> +const CacheLocality& CacheLocality::system<std::atomic>() { + static auto* cache = new CacheLocality(getSystemLocalityInfo()); + return *cache; +} + +// Each level of cache has sharing sets, which are the set of cpus +// that share a common cache at that level. These are available in a +// hex bitset form (/sys/devices/system/cpu/cpu0/index0/shared_cpu_map, +// for example). They are also available in a human-readable list form, +// as in /sys/devices/system/cpu/cpu0/index0/shared_cpu_list. The list +// is a comma-separated list of numbers and ranges, where the ranges are +// a pair of decimal numbers separated by a '-'. +// +// To sort the cpus for optimum locality we don't really need to parse +// the sharing sets, we just need a unique representative from the +// equivalence class. The smallest value works fine, and happens to be +// the first decimal number in the file. We load all of the equivalence +// class information from all of the cpu*/index* directories, order the +// cpus first by increasing last-level cache equivalence class, then by +// the smaller caches. Finally, we break ties with the cpu number itself. + +/// Returns the first decimal number in the string, or throws an exception +/// if the string does not start with a number terminated by ',', '-', +/// '\n', or eos. +static size_t parseLeadingNumber(const std::string& line) { + auto raw = line.c_str(); + char* end; + unsigned long val = strtoul(raw, &end, 10); + if (end == raw || (*end != ',' && *end != '-' && *end != '\n' && *end != 0)) { + throw std::runtime_error( + to<std::string>("error parsing list '", line, "'").c_str()); + } + return val; +} + +CacheLocality CacheLocality::readFromSysfsTree( + const std::function<std::string(std::string)>& mapping) { + // number of equivalence classes per level + std::vector<size_t> numCachesByLevel; + + // the list of cache equivalence classes, where equivalance classes + // are named by the smallest cpu in the class + std::vector<std::vector<size_t>> equivClassesByCpu; + + std::vector<size_t> cpus; + + while (true) { + auto cpu = cpus.size(); + std::vector<size_t> levels; + for (size_t index = 0;; ++index) { + auto dir = + sformat("/sys/devices/system/cpu/cpu{}/cache/index{}/", cpu, index); + auto cacheType = mapping(dir + "type"); + auto equivStr = mapping(dir + "shared_cpu_list"); + if (cacheType.empty() || equivStr.empty()) { + // no more caches + break; + } + if (cacheType[0] == 'I') { + // cacheType in { "Data", "Instruction", "Unified" }. skip icache + continue; + } + auto equiv = parseLeadingNumber(equivStr); + auto level = levels.size(); + levels.push_back(equiv); + + if (equiv == cpu) { + // we only want to count the equiv classes once, so we do it when + // we first encounter them + while (numCachesByLevel.size() <= level) { + numCachesByLevel.push_back(0); + } + numCachesByLevel[level]++; + } + } + + if (levels.empty()) { + // no levels at all for this cpu, we must be done + break; + } + equivClassesByCpu.emplace_back(std::move(levels)); + cpus.push_back(cpu); + } + + if (cpus.empty()) { + throw std::runtime_error("unable to load cache sharing info"); + } + + std::sort(cpus.begin(), cpus.end(), [&](size_t lhs, size_t rhs) -> bool { + // sort first by equiv class of cache with highest index, + // direction doesn't matter. If different cpus have + // different numbers of caches then this code might produce + // a sub-optimal ordering, but it won't crash + auto& lhsEquiv = equivClassesByCpu[lhs]; + auto& rhsEquiv = equivClassesByCpu[rhs]; + for (ssize_t i = ssize_t(std::min(lhsEquiv.size(), rhsEquiv.size())) - 1; + i >= 0; + --i) { + auto idx = size_t(i); + if (lhsEquiv[idx] != rhsEquiv[idx]) { + return lhsEquiv[idx] < rhsEquiv[idx]; + } + } + + // break ties deterministically by cpu + return lhs < rhs; + }); + + // the cpus are now sorted by locality, with neighboring entries closer + // to each other than entries that are far away. For striping we want + // the inverse map, since we are starting with the cpu + std::vector<size_t> indexes(cpus.size()); + for (size_t i = 0; i < cpus.size(); ++i) { + indexes[cpus[i]] = i; + } + + return CacheLocality{ + cpus.size(), std::move(numCachesByLevel), std::move(indexes)}; +} + +CacheLocality CacheLocality::readFromSysfs() { + return readFromSysfsTree([](std::string name) { + std::ifstream xi(name.c_str()); + std::string rv; + std::getline(xi, rv); + return rv; + }); +} + +static bool procCpuinfoLineRelevant(std::string const& line) { + return line.size() > 4 && (line[0] == 'p' || line[0] == 'c'); +} + +CacheLocality CacheLocality::readFromProcCpuinfoLines( + std::vector<std::string> const& lines) { + size_t physicalId = 0; + size_t coreId = 0; + std::vector<std::tuple<size_t, size_t, size_t>> cpus; + size_t maxCpu = 0; + for (auto iter = lines.rbegin(); iter != lines.rend(); ++iter) { + auto& line = *iter; + if (!procCpuinfoLineRelevant(line)) { + continue; + } + + auto sepIndex = line.find(':'); + if (sepIndex == std::string::npos || sepIndex + 2 > line.size()) { + continue; + } + auto arg = line.substr(sepIndex + 2); + + // "physical id" is socket, which is the most important locality + // context. "core id" is a real core, so two "processor" entries with + // the same physical id and core id are hyperthreads of each other. + // "processor" is the top line of each record, so when we hit it in + // the reverse order then we can emit a record. + if (line.find("physical id") == 0) { + physicalId = parseLeadingNumber(arg); + } else if (line.find("core id") == 0) { + coreId = parseLeadingNumber(arg); + } else if (line.find("processor") == 0) { + auto cpu = parseLeadingNumber(arg); + maxCpu = std::max(cpu, maxCpu); + cpus.emplace_back(physicalId, coreId, cpu); + } + } + + if (cpus.empty()) { + throw std::runtime_error("no CPUs parsed from /proc/cpuinfo"); + } + if (maxCpu != cpus.size() - 1) { + throw std::runtime_error( + "offline CPUs not supported for /proc/cpuinfo cache locality source"); + } + + std::sort(cpus.begin(), cpus.end()); + size_t cpusPerCore = 1; + while (cpusPerCore < cpus.size() && + std::get<0>(cpus[cpusPerCore]) == std::get<0>(cpus[0]) && + std::get<1>(cpus[cpusPerCore]) == std::get<1>(cpus[0])) { + ++cpusPerCore; + } + + // we can't tell the real cache hierarchy from /proc/cpuinfo, but it + // works well enough to assume there are 3 levels, L1 and L2 per-core + // and L3 per socket + std::vector<size_t> numCachesByLevel; + numCachesByLevel.push_back(cpus.size() / cpusPerCore); + numCachesByLevel.push_back(cpus.size() / cpusPerCore); + numCachesByLevel.push_back(std::get<0>(cpus.back()) + 1); + + std::vector<size_t> indexes(cpus.size()); + for (size_t i = 0; i < cpus.size(); ++i) { + indexes[std::get<2>(cpus[i])] = i; + } + + return CacheLocality{ + cpus.size(), std::move(numCachesByLevel), std::move(indexes)}; +} + +CacheLocality CacheLocality::readFromProcCpuinfo() { + std::vector<std::string> lines; + { + std::ifstream xi("/proc/cpuinfo"); + if (xi.fail()) { + throw std::runtime_error("unable to open /proc/cpuinfo"); + } + char buf[8192]; + while (xi.good() && lines.size() < 20000) { + xi.getline(buf, sizeof(buf)); + std::string str(buf); + if (procCpuinfoLineRelevant(str)) { + lines.emplace_back(std::move(str)); + } + } + } + return readFromProcCpuinfoLines(lines); +} + +CacheLocality CacheLocality::uniform(size_t numCpus) { + CacheLocality rv; + + rv.numCpus = numCpus; + + // one cache shared by all cpus + rv.numCachesByLevel.push_back(numCpus); + + // no permutations in locality index mapping + for (size_t cpu = 0; cpu < numCpus; ++cpu) { + rv.localityIndexByCpu.push_back(cpu); + } + + return rv; +} + +////////////// Getcpu + +Getcpu::Func Getcpu::resolveVdsoFunc() { +#if !defined(FOLLY_HAVE_LINUX_VDSO) || defined(FOLLY_SANITIZE_MEMORY) + return nullptr; +#else + void* h = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + if (h == nullptr) { + return nullptr; + } + + auto func = Getcpu::Func(dlsym(h, "__vdso_getcpu")); + if (func == nullptr) { + // technically a null result could either be a failure or a successful + // lookup of a symbol with the null value, but the second can't actually + // happen for this symbol. No point holding the handle forever if + // we don't need the code + dlclose(h); + } + + return func; +#endif +} + +#ifdef FOLLY_CL_USE_FOLLY_TLS +/////////////// SequentialThreadId +template struct SequentialThreadId<std::atomic>; +#endif + +/////////////// AccessSpreader +template struct AccessSpreader<std::atomic>; + +SimpleAllocator::SimpleAllocator(size_t allocSize, size_t sz) + : allocSize_{allocSize}, sz_(sz) {} + +SimpleAllocator::~SimpleAllocator() { + std::lock_guard<std::mutex> g(m_); + for (auto& block : blocks_) { + folly::aligned_free(block); + } +} + +void* SimpleAllocator::allocateHard() { + // Allocate a new slab. + mem_ = static_cast<uint8_t*>(folly::aligned_malloc(allocSize_, allocSize_)); + if (!mem_) { + throw_exception<std::bad_alloc>(); + } + end_ = mem_ + allocSize_; + blocks_.push_back(mem_); + + // Install a pointer to ourselves as the allocator. + *reinterpret_cast<SimpleAllocator**>(mem_) = this; + static_assert(max_align_v >= sizeof(SimpleAllocator*), "alignment too small"); + mem_ += std::min(sz_, max_align_v); + + // New allocation. + auto mem = mem_; + mem_ += sz_; + assert(intptr_t(mem) % 128 != 0); + return mem; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.h b/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.h new file mode 100644 index 000000000..ea42a0181 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/CacheLocality.h @@ -0,0 +1,563 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <array> +#include <atomic> +#include <cassert> +#include <functional> +#include <limits> +#include <mutex> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <vector> + +#include <folly/Indestructible.h> +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/hash/Hash.h> +#include <folly/lang/Align.h> +#include <folly/lang/Exception.h> +#include <folly/system/ThreadId.h> + +#if !FOLLY_MOBILE && defined(FOLLY_TLS) +#define FOLLY_CL_USE_FOLLY_TLS 1 +#else +#undef FOLLY_CL_USE_FOLLY_TLS +#endif + +namespace folly { + +// This file contains several classes that might be useful if you are +// trying to dynamically optimize cache locality: CacheLocality reads +// cache sharing information from sysfs to determine how CPUs should be +// grouped to minimize contention, Getcpu provides fast access to the +// current CPU via __vdso_getcpu, and AccessSpreader uses these two to +// optimally spread accesses among a predetermined number of stripes. +// +// AccessSpreader<>::current(n) microbenchmarks at 22 nanos, which is +// substantially less than the cost of a cache miss. This means that we +// can effectively use it to reduce cache line ping-pong on striped data +// structures such as IndexedMemPool or statistics counters. +// +// Because CacheLocality looks at all of the cache levels, it can be +// used for different levels of optimization. AccessSpreader(2) does +// per-chip spreading on a dual socket system. AccessSpreader(numCpus) +// does perfect per-cpu spreading. AccessSpreader(numCpus / 2) does +// perfect L1 spreading in a system with hyperthreading enabled. + +struct CacheLocality { + /// 1 more than the maximum value that can be returned from sched_getcpu + /// or getcpu. This is the number of hardware thread contexts provided + /// by the processors + size_t numCpus; + + /// Holds the number of caches present at each cache level (0 is + /// the closest to the cpu). This is the number of AccessSpreader + /// stripes needed to avoid cross-cache communication at the specified + /// layer. numCachesByLevel.front() is the number of L1 caches and + /// numCachesByLevel.back() is the number of last-level caches. + std::vector<size_t> numCachesByLevel; + + /// A map from cpu (from sched_getcpu or getcpu) to an index in the + /// range 0..numCpus-1, where neighboring locality indices are more + /// likely to share caches then indices far away. All of the members + /// of a particular cache level be contiguous in their locality index. + /// For example, if numCpus is 32 and numCachesByLevel.back() is 2, + /// then cpus with a locality index < 16 will share one last-level + /// cache and cpus with a locality index >= 16 will share the other. + std::vector<size_t> localityIndexByCpu; + + /// Returns the best CacheLocality information available for the current + /// system, cached for fast access. This will be loaded from sysfs if + /// possible, otherwise it will be correct in the number of CPUs but + /// not in their sharing structure. + /// + /// If you are into yo dawgs, this is a shared cache of the local + /// locality of the shared caches. + /// + /// The template parameter here is used to allow injection of a + /// repeatable CacheLocality structure during testing. Rather than + /// inject the type of the CacheLocality provider into every data type + /// that transitively uses it, all components select between the default + /// sysfs implementation and a deterministic implementation by keying + /// off the type of the underlying atomic. See DeterministicScheduler. + template <template <typename> class Atom = std::atomic> + static const CacheLocality& system(); + + /// Reads CacheLocality information from a tree structured like + /// the sysfs filesystem. The provided function will be evaluated + /// for each sysfs file that needs to be queried. The function + /// should return a string containing the first line of the file + /// (not including the newline), or an empty string if the file does + /// not exist. The function will be called with paths of the form + /// /sys/devices/system/cpu/cpu*/cache/index*/{type,shared_cpu_list} . + /// Throws an exception if no caches can be parsed at all. + static CacheLocality readFromSysfsTree( + const std::function<std::string(std::string)>& mapping); + + /// Reads CacheLocality information from the real sysfs filesystem. + /// Throws an exception if no cache information can be loaded. + static CacheLocality readFromSysfs(); + + /// readFromProcCpuinfo(), except input is taken from memory rather + /// than the file system. + static CacheLocality readFromProcCpuinfoLines( + std::vector<std::string> const& lines); + + /// Returns an estimate of the CacheLocality information by reading + /// /proc/cpuinfo. This isn't as accurate as readFromSysfs(), but + /// is a lot faster because the info isn't scattered across + /// hundreds of files. Throws an exception if no cache information + /// can be loaded. + static CacheLocality readFromProcCpuinfo(); + + /// Returns a usable (but probably not reflective of reality) + /// CacheLocality structure with the specified number of cpus and a + /// single cache level that associates one cpu per cache. + static CacheLocality uniform(size_t numCpus); +}; + +/// Knows how to derive a function pointer to the VDSO implementation of +/// getcpu(2), if available +struct Getcpu { + /// Function pointer to a function with the same signature as getcpu(2). + typedef int (*Func)(unsigned* cpu, unsigned* node, void* unused); + + /// Returns a pointer to the VDSO implementation of getcpu(2), if + /// available, or nullptr otherwise. This function may be quite + /// expensive, be sure to cache the result. + static Func resolveVdsoFunc(); +}; + +#ifdef FOLLY_CL_USE_FOLLY_TLS +template <template <typename> class Atom> +struct SequentialThreadId { + /// Returns the thread id assigned to the current thread + static unsigned get() { + auto rv = currentId; + if (UNLIKELY(rv == 0)) { + rv = currentId = ++prevId; + } + return rv; + } + + private: + static Atom<unsigned> prevId; + + static FOLLY_TLS unsigned currentId; +}; + +template <template <typename> class Atom> +Atom<unsigned> SequentialThreadId<Atom>::prevId(0); + +template <template <typename> class Atom> +FOLLY_TLS unsigned SequentialThreadId<Atom>::currentId(0); + +// Suppress this instantiation in other translation units. It is +// instantiated in CacheLocality.cpp +extern template struct SequentialThreadId<std::atomic>; +#endif + +struct HashingThreadId { + static unsigned get() { + return hash::twang_32from64(getCurrentThreadID()); + } +}; + +/// A class that lazily binds a unique (for each implementation of Atom) +/// identifier to a thread. This is a fallback mechanism for the access +/// spreader if __vdso_getcpu can't be loaded +template <typename ThreadId> +struct FallbackGetcpu { + /// Fills the thread id into the cpu and node out params (if they + /// are non-null). This method is intended to act like getcpu when a + /// fast-enough form of getcpu isn't available or isn't desired + static int getcpu(unsigned* cpu, unsigned* node, void* /* unused */) { + auto id = ThreadId::get(); + if (cpu) { + *cpu = id; + } + if (node) { + *node = id; + } + return 0; + } +}; + +#ifdef FOLLY_CL_USE_FOLLY_TLS +typedef FallbackGetcpu<SequentialThreadId<std::atomic>> FallbackGetcpuType; +#else +typedef FallbackGetcpu<HashingThreadId> FallbackGetcpuType; +#endif + +/// AccessSpreader arranges access to a striped data structure in such a +/// way that concurrently executing threads are likely to be accessing +/// different stripes. It does NOT guarantee uncontended access. +/// Your underlying algorithm must be thread-safe without spreading, this +/// is merely an optimization. AccessSpreader::current(n) is typically +/// much faster than a cache miss (12 nanos on my dev box, tested fast +/// in both 2.6 and 3.2 kernels). +/// +/// If available (and not using the deterministic testing implementation) +/// AccessSpreader uses the getcpu system call via VDSO and the +/// precise locality information retrieved from sysfs by CacheLocality. +/// This provides optimal anti-sharing at a fraction of the cost of a +/// cache miss. +/// +/// When there are not as many stripes as processors, we try to optimally +/// place the cache sharing boundaries. This means that if you have 2 +/// stripes and run on a dual-socket system, your 2 stripes will each get +/// all of the cores from a single socket. If you have 16 stripes on a +/// 16 core system plus hyperthreading (32 cpus), each core will get its +/// own stripe and there will be no cache sharing at all. +/// +/// AccessSpreader has a fallback mechanism for when __vdso_getcpu can't be +/// loaded, or for use during deterministic testing. Using sched_getcpu +/// or the getcpu syscall would negate the performance advantages of +/// access spreading, so we use a thread-local value and a shared atomic +/// counter to spread access out. On systems lacking both a fast getcpu() +/// and TLS, we hash the thread id to spread accesses. +/// +/// AccessSpreader is templated on the template type that is used +/// to implement atomics, as a way to instantiate the underlying +/// heuristics differently for production use and deterministic unit +/// testing. See DeterministicScheduler for more. If you aren't using +/// DeterministicScheduler, you can just use the default template parameter +/// all of the time. +template <template <typename> class Atom = std::atomic> +struct AccessSpreader { + /// Returns the stripe associated with the current CPU. The returned + /// value will be < numStripes. + static size_t current(size_t numStripes) { + // widthAndCpuToStripe[0] will actually work okay (all zeros), but + // something's wrong with the caller + assert(numStripes > 0); + + unsigned cpu; + getcpuFunc(&cpu, nullptr, nullptr); + return widthAndCpuToStripe[std::min(size_t(kMaxCpus), numStripes)] + [cpu % kMaxCpus]; + } + +#ifdef FOLLY_CL_USE_FOLLY_TLS + /// Returns the stripe associated with the current CPU. The returned + /// value will be < numStripes. + /// This function caches the current cpu in a thread-local variable for a + /// certain small number of calls, which can make the result imprecise, but + /// it is more efficient (amortized 2 ns on my dev box, compared to 12 ns for + /// current()). + static size_t cachedCurrent(size_t numStripes) { + return widthAndCpuToStripe[std::min(size_t(kMaxCpus), numStripes)] + [cpuCache.cpu()]; + } +#else + /// Fallback implementation when thread-local storage isn't available. + static size_t cachedCurrent(size_t numStripes) { + return current(numStripes); + } +#endif + + /// Returns the maximum stripe value that can be returned under any + /// dynamic configuration, based on the current compile-time platform + static constexpr size_t maxStripeValue() { + return kMaxCpus; + } + + private: + /// If there are more cpus than this nothing will crash, but there + /// might be unnecessary sharing + enum { + // Android phones with 8 cores exist today; 16 for future-proofing. + kMaxCpus = kIsMobile ? 16 : 256, + }; + + typedef uint8_t CompactStripe; + + static_assert( + (kMaxCpus & (kMaxCpus - 1)) == 0, + "kMaxCpus should be a power of two so modulo is fast"); + static_assert( + kMaxCpus - 1 <= std::numeric_limits<CompactStripe>::max(), + "stripeByCpu element type isn't wide enough"); + + /// Points to the getcpu-like function we are using to obtain the + /// current cpu. It should not be assumed that the returned cpu value + /// is in range. We use a static for this so that we can prearrange a + /// valid value in the pre-constructed state and avoid the need for a + /// conditional on every subsequent invocation (not normally a big win, + /// but 20% on some inner loops here). + static Getcpu::Func getcpuFunc; + + /// For each level of splitting up to kMaxCpus, maps the cpu (mod + /// kMaxCpus) to the stripe. Rather than performing any inequalities + /// or modulo on the actual number of cpus, we just fill in the entire + /// array. + static CompactStripe widthAndCpuToStripe[kMaxCpus + 1][kMaxCpus]; + + /// Caches the current CPU and refreshes the cache every so often. + class CpuCache { + public: + unsigned cpu() { + if (UNLIKELY(cachedCpuUses_-- == 0)) { + unsigned cpu; + AccessSpreader::getcpuFunc(&cpu, nullptr, nullptr); + cachedCpu_ = cpu % kMaxCpus; + cachedCpuUses_ = kMaxCachedCpuUses - 1; + } + return cachedCpu_; + } + + private: + static constexpr unsigned kMaxCachedCpuUses = 32; + + unsigned cachedCpu_{0}; + unsigned cachedCpuUses_{0}; + }; + +#ifdef FOLLY_CL_USE_FOLLY_TLS + static FOLLY_TLS CpuCache cpuCache; +#endif + + static bool initialized; + + /// Returns the best getcpu implementation for Atom + static Getcpu::Func pickGetcpuFunc() { + auto best = Getcpu::resolveVdsoFunc(); + return best ? best : &FallbackGetcpuType::getcpu; + } + + /// Always claims to be on CPU zero, node zero + static int degenerateGetcpu(unsigned* cpu, unsigned* node, void*) { + if (cpu != nullptr) { + *cpu = 0; + } + if (node != nullptr) { + *node = 0; + } + return 0; + } + + // The function to call for fast lookup of getcpu is a singleton, as + // is the precomputed table of locality information. AccessSpreader + // is used in very tight loops, however (we're trying to race an L1 + // cache miss!), so the normal singleton mechanisms are noticeably + // expensive. Even a not-taken branch guarding access to getcpuFunc + // slows AccessSpreader::current from 12 nanos to 14. As a result, we + // populate the static members with simple (but valid) values that can + // be filled in by the linker, and then follow up with a normal static + // initializer call that puts in the proper version. This means that + // when there are initialization order issues we will just observe a + // zero stripe. Once a sanitizer gets smart enough to detect this as + // a race or undefined behavior, we can annotate it. + + static bool initialize() { + getcpuFunc = pickGetcpuFunc(); + + auto& cacheLocality = CacheLocality::system<Atom>(); + auto n = cacheLocality.numCpus; + for (size_t width = 0; width <= kMaxCpus; ++width) { + auto& row = widthAndCpuToStripe[width]; + auto numStripes = std::max(size_t{1}, width); + for (size_t cpu = 0; cpu < kMaxCpus && cpu < n; ++cpu) { + auto index = cacheLocality.localityIndexByCpu[cpu]; + assert(index < n); + // as index goes from 0..n, post-transform value goes from + // 0..numStripes + row[cpu] = static_cast<CompactStripe>((index * numStripes) / n); + assert(row[cpu] < numStripes); + } + size_t filled = n; + while (filled < kMaxCpus) { + size_t len = std::min(filled, kMaxCpus - filled); + std::memcpy(&row[filled], &row[0], len); + filled += len; + } + for (size_t cpu = n; cpu < kMaxCpus; ++cpu) { + assert(row[cpu] == row[cpu - n]); + } + } + return true; + } +}; + +template <template <typename> class Atom> +Getcpu::Func AccessSpreader<Atom>::getcpuFunc = + AccessSpreader<Atom>::degenerateGetcpu; + +template <template <typename> class Atom> +typename AccessSpreader<Atom>::CompactStripe + AccessSpreader<Atom>::widthAndCpuToStripe[kMaxCpus + 1][kMaxCpus] = {}; + +#ifdef FOLLY_CL_USE_FOLLY_TLS +template <template <typename> class Atom> +FOLLY_TLS + typename AccessSpreader<Atom>::CpuCache AccessSpreader<Atom>::cpuCache; +#endif + +template <template <typename> class Atom> +bool AccessSpreader<Atom>::initialized = AccessSpreader<Atom>::initialize(); + +// Suppress this instantiation in other translation units. It is +// instantiated in CacheLocality.cpp +extern template struct AccessSpreader<std::atomic>; + +/** + * A simple freelist allocator. Allocates things of size sz, from + * slabs of size allocSize. Takes a lock on each + * allocation/deallocation. + */ +class SimpleAllocator { + std::mutex m_; + uint8_t* mem_{nullptr}; + uint8_t* end_{nullptr}; + void* freelist_{nullptr}; + size_t allocSize_; + size_t sz_; + std::vector<void*> blocks_; + + public: + SimpleAllocator(size_t allocSize, size_t sz); + ~SimpleAllocator(); + void* allocateHard(); + + // Inline fast-paths. + void* allocate() { + std::lock_guard<std::mutex> g(m_); + // Freelist allocation. + if (freelist_) { + auto mem = freelist_; + freelist_ = *static_cast<void**>(freelist_); + return mem; + } + + // Bump-ptr allocation. + if (intptr_t(mem_) % 128 == 0) { + // Avoid allocating pointers that may look like malloc + // pointers. + mem_ += std::min(sz_, max_align_v); + } + if (mem_ && (mem_ + sz_ <= end_)) { + auto mem = mem_; + mem_ += sz_; + + assert(intptr_t(mem) % 128 != 0); + return mem; + } + + return allocateHard(); + } + void deallocate(void* mem) { + std::lock_guard<std::mutex> g(m_); + *static_cast<void**>(mem) = freelist_; + freelist_ = mem; + } +}; + +/** + * An allocator that can be used with CacheLocality to allocate + * core-local memory. + * + * There is actually nothing special about the memory itself (it is + * not bound to numa nodes or anything), but the allocator guarantees + * that memory allocatd from the same stripe will only come from cache + * lines also allocated to the same stripe. This means multiple + * things using CacheLocality can allocate memory in smaller-than + * cacheline increments, and be assured that it won't cause more false + * sharing than it otherwise would. + * + * Note that allocation and deallocation takes a per-sizeclass lock. + */ +template <size_t Stripes> +class CoreRawAllocator { + public: + class Allocator { + static constexpr size_t AllocSize{4096}; + + uint8_t sizeClass(size_t size) { + if (size <= 8) { + return 0; + } else if (size <= 16) { + return 1; + } else if (size <= 32) { + return 2; + } else if (size <= 64) { + return 3; + } else { // punt to malloc. + return 4; + } + } + + std::array<SimpleAllocator, 4> allocators_{ + {{AllocSize, 8}, {AllocSize, 16}, {AllocSize, 32}, {AllocSize, 64}}}; + + public: + void* allocate(size_t size) { + auto cl = sizeClass(size); + if (cl == 4) { + // Align to a cacheline + size = size + (hardware_destructive_interference_size - 1); + size &= ~size_t(hardware_destructive_interference_size - 1); + void* mem = + aligned_malloc(size, hardware_destructive_interference_size); + if (!mem) { + throw_exception<std::bad_alloc>(); + } + return mem; + } + return allocators_[cl].allocate(); + } + void deallocate(void* mem, size_t = 0) { + if (!mem) { + return; + } + + // See if it came from this allocator or malloc. + if (intptr_t(mem) % 128 != 0) { + auto addr = + reinterpret_cast<void*>(intptr_t(mem) & ~intptr_t(AllocSize - 1)); + auto allocator = *static_cast<SimpleAllocator**>(addr); + allocator->deallocate(mem); + } else { + aligned_free(mem); + } + } + }; + + Allocator* get(size_t stripe) { + assert(stripe < Stripes); + return &allocators_[stripe]; + } + + private: + Allocator allocators_[Stripes]; +}; + +template <typename T, size_t Stripes> +CxxAllocatorAdaptor<T, typename CoreRawAllocator<Stripes>::Allocator> +getCoreAllocator(size_t stripe) { + // We cannot make sure that the allocator will be destroyed after + // all the objects allocated with it, so we leak it. + static Indestructible<CoreRawAllocator<Stripes>> allocator; + return CxxAllocatorAdaptor<T, typename CoreRawAllocator<Stripes>::Allocator>( + *allocator->get(stripe)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h b/ios/Pods/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h new file mode 100644 index 000000000..6d5ae8957 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h @@ -0,0 +1,641 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> +#include <folly/concurrency/detail/ConcurrentHashMap-detail.h> +#include <folly/synchronization/Hazptr.h> +#include <atomic> +#include <mutex> + +namespace folly { + +/** + * Implementations of high-performance Concurrent Hashmaps that + * support erase and update. + * + * Readers are always wait-free. + * Writers are sharded, but take a lock. + * + * Multithreaded performance beats anything except the lock-free + * atomic maps (AtomicUnorderedMap, AtomicHashMap), BUT only + * if you can perfectly size the atomic maps, and you don't + * need erase(). If you don't know the size in advance or + * your workload frequently calls erase(), this is the + * better choice. + * + * The interface is as close to std::unordered_map as possible, but there + * are a handful of changes: + * + * * Iterators hold hazard pointers to the returned elements. Elements can only + * be accessed while Iterators are still valid! + * + * * Therefore operator[] and at() return copies, since they do not + * return an iterator. The returned value is const, to remind you + * that changes do not affect the value in the map. + * + * * erase() calls the hash function, and may fail if the hash + * function throws an exception. + * + * * clear() initializes new segments, and is not noexcept. + * + * * The interface adds assign_if_equal, since find() doesn't take a lock. + * + * * Only const version of find() is supported, and const iterators. + * Mutation must use functions provided, like assign(). + * + * * iteration iterates over all the buckets in the table, unlike + * std::unordered_map which iterates over a linked list of elements. + * If the table is sparse, this may be more expensive. + * + * * Allocator must be stateless. + * + * 1: ConcurrentHashMap, based on Java's ConcurrentHashMap. + * Very similar to std::unodered_map in performance. + * + * 2: ConcurrentHashMapSIMD, based on F14ValueMap. If the map is + * larger than the cache size, it has superior performance due to + * vectorized key lookup. + * + * + * + * USAGE FAQs + * + * Q: Is simultaneous iteration and erase() threadsafe? + * Example: + * + * ConcurrentHashMap<int, int> map; + * + * Thread 1: auto it = map.begin(); + * while (it != map.end()) { + * // Do something with it + * it++; + * } + * + * Thread 2: map.insert(2, 2); map.erase(2); + * + * A: Yes, this is safe. However, the iterating thread is not + * garanteed to see (or not see) concurrent insertions and erasures. + * Inserts may cause a rehash, but the old table is still valid as + * long as any iterator pointing to it exists. + * + * Q: How do I update an existing object atomically? + * + * A: assign_if_equal is the recommended way - readers will see the + * old value until the new value is completely constructed and + * inserted. + * + * Q: Why does map.erase() not actually destroy elements? + * + * A: Hazard Pointers are used to improve the performance of + * concurrent access. They can be thought of as a simple Garbage + * Collector. To reduce the GC overhead, a GC pass is only run after + * reaching a cetain memory bound. erase() will remove the element + * from being accessed via the map, but actual destruction may happen + * later, after iterators that may point to it have been deleted. + * + * The only guarantee is that a GC pass will be run on map destruction + * - no elements will remain after map destruction. + * + * Q: Are pointers to values safe to access *without* holding an + * iterator? + * + * A: The SIMD version guarantees that references to elements are + * stable across rehashes, the non-SIMD version does *not*. Note that + * unless you hold an iterator, you need to ensure there are no + * concurrent deletes/updates to that key if you are accessing it via + * reference. + */ + +template < + typename KeyType, + typename ValueType, + typename HashFn = std::hash<KeyType>, + typename KeyEqual = std::equal_to<KeyType>, + typename Allocator = std::allocator<uint8_t>, + uint8_t ShardBits = 8, + template <typename> class Atom = std::atomic, + class Mutex = std::mutex, + template < + typename, + typename, + uint8_t, + typename, + typename, + typename, + template <typename> class, + class> class Impl = detail::concurrenthashmap::bucket::BucketTable> +class ConcurrentHashMap { + using SegmentT = detail::ConcurrentHashMapSegment< + KeyType, + ValueType, + ShardBits, + HashFn, + KeyEqual, + Allocator, + Atom, + Mutex, + Impl>; + + float load_factor_ = SegmentT::kDefaultLoadFactor; + + static constexpr uint64_t NumShards = (1 << ShardBits); + + public: + class ConstIterator; + + typedef KeyType key_type; + typedef ValueType mapped_type; + typedef std::pair<const KeyType, ValueType> value_type; + typedef std::size_t size_type; + typedef HashFn hasher; + typedef KeyEqual key_equal; + typedef ConstIterator const_iterator; + + /* + * Construct a ConcurrentHashMap with 1 << ShardBits shards, size + * and max_size given. Both size and max_size will be rounded up to + * the next power of two, if they are not already a power of two, so + * that we can index in to Shards efficiently. + * + * Insertion functions will throw bad_alloc if max_size is exceeded. + */ + explicit ConcurrentHashMap(size_t size = 8, size_t max_size = 0) { + size_ = folly::nextPowTwo(size); + if (max_size != 0) { + max_size_ = folly::nextPowTwo(max_size); + } + CHECK(max_size_ == 0 || max_size_ >= size_); + for (uint64_t i = 0; i < NumShards; i++) { + segments_[i].store(nullptr, std::memory_order_relaxed); + } + } + + ConcurrentHashMap(ConcurrentHashMap&& o) noexcept + : size_(o.size_), max_size_(o.max_size_) { + for (uint64_t i = 0; i < NumShards; i++) { + segments_[i].store( + o.segments_[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + o.segments_[i].store(nullptr, std::memory_order_relaxed); + } + cohort_.store(o.cohort(), std::memory_order_relaxed); + o.cohort_.store(nullptr, std::memory_order_relaxed); + } + + ConcurrentHashMap& operator=(ConcurrentHashMap&& o) { + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_relaxed); + if (seg) { + seg->~SegmentT(); + Allocator().deallocate((uint8_t*)seg, sizeof(SegmentT)); + } + segments_[i].store( + o.segments_[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + o.segments_[i].store(nullptr, std::memory_order_relaxed); + } + size_ = o.size_; + max_size_ = o.max_size_; + cohort_shutdown_cleanup(); + cohort_.store(o.cohort(), std::memory_order_relaxed); + o.cohort_.store(nullptr, std::memory_order_relaxed); + return *this; + } + + ~ConcurrentHashMap() { + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_relaxed); + if (seg) { + seg->~SegmentT(); + Allocator().deallocate((uint8_t*)seg, sizeof(SegmentT)); + } + } + cohort_shutdown_cleanup(); + } + + bool empty() const noexcept { + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_acquire); + if (seg) { + if (!seg->empty()) { + return false; + } + } + } + return true; + } + + ConstIterator find(const KeyType& k) const { + auto segment = pickSegment(k); + ConstIterator res(this, segment); + auto seg = segments_[segment].load(std::memory_order_acquire); + if (!seg || !seg->find(res.it_, k)) { + res.segment_ = NumShards; + } + return res; + } + + ConstIterator cend() const noexcept { + return ConstIterator(NumShards); + } + + ConstIterator cbegin() const noexcept { + return ConstIterator(this); + } + + ConstIterator end() const noexcept { + return cend(); + } + + ConstIterator begin() const noexcept { + return cbegin(); + } + + std::pair<ConstIterator, bool> insert( + std::pair<key_type, mapped_type>&& foo) { + auto segment = pickSegment(foo.first); + std::pair<ConstIterator, bool> res( + std::piecewise_construct, + std::forward_as_tuple(this, segment), + std::forward_as_tuple(false)); + res.second = ensureSegment(segment)->insert(res.first.it_, std::move(foo)); + return res; + } + + template <typename Key, typename Value> + std::pair<ConstIterator, bool> insert(Key&& k, Value&& v) { + auto segment = pickSegment(k); + std::pair<ConstIterator, bool> res( + std::piecewise_construct, + std::forward_as_tuple(this, segment), + std::forward_as_tuple(false)); + res.second = ensureSegment(segment)->insert( + res.first.it_, std::forward<Key>(k), std::forward<Value>(v)); + return res; + } + + template <typename Key, typename... Args> + std::pair<ConstIterator, bool> try_emplace(Key&& k, Args&&... args) { + auto segment = pickSegment(k); + std::pair<ConstIterator, bool> res( + std::piecewise_construct, + std::forward_as_tuple(this, segment), + std::forward_as_tuple(false)); + res.second = ensureSegment(segment)->try_emplace( + res.first.it_, std::forward<Key>(k), std::forward<Args>(args)...); + return res; + } + + template <typename... Args> + std::pair<ConstIterator, bool> emplace(Args&&... args) { + using Node = typename SegmentT::Node; + auto node = (Node*)Allocator().allocate(sizeof(Node)); + new (node) Node(ensureCohort(), std::forward<Args>(args)...); + auto segment = pickSegment(node->getItem().first); + std::pair<ConstIterator, bool> res( + std::piecewise_construct, + std::forward_as_tuple(this, segment), + std::forward_as_tuple(false)); + res.second = ensureSegment(segment)->emplace( + res.first.it_, node->getItem().first, node); + if (!res.second) { + node->~Node(); + Allocator().deallocate((uint8_t*)node, sizeof(Node)); + } + return res; + } + + /* + * The bool component will always be true if the map has been updated via + * either insertion or assignment. Note that this is different from the + * std::map::insert_or_assign interface. + */ + template <typename Key, typename Value> + std::pair<ConstIterator, bool> insert_or_assign(Key&& k, Value&& v) { + auto segment = pickSegment(k); + std::pair<ConstIterator, bool> res( + std::piecewise_construct, + std::forward_as_tuple(this, segment), + std::forward_as_tuple(false)); + res.second = ensureSegment(segment)->insert_or_assign( + res.first.it_, std::forward<Key>(k), std::forward<Value>(v)); + return res; + } + + template <typename Key, typename Value> + folly::Optional<ConstIterator> assign(Key&& k, Value&& v) { + auto segment = pickSegment(k); + ConstIterator res(this, segment); + auto seg = segments_[segment].load(std::memory_order_acquire); + if (!seg) { + return none; + } else { + auto r = + seg->assign(res.it_, std::forward<Key>(k), std::forward<Value>(v)); + if (!r) { + return none; + } + } + return std::move(res); + } + + // Assign to desired if and only if key k is equal to expected + template <typename Key, typename Value> + folly::Optional<ConstIterator> + assign_if_equal(Key&& k, const ValueType& expected, Value&& desired) { + auto segment = pickSegment(k); + ConstIterator res(this, segment); + auto seg = segments_[segment].load(std::memory_order_acquire); + if (!seg) { + return none; + } else { + auto r = seg->assign_if_equal( + res.it_, + std::forward<Key>(k), + expected, + std::forward<Value>(desired)); + if (!r) { + return none; + } + } + return std::move(res); + } + + // Copying wrappers around insert and find. + // Only available for copyable types. + const ValueType operator[](const KeyType& key) { + auto item = insert(key, ValueType()); + return item.first->second; + } + + const ValueType at(const KeyType& key) const { + auto item = find(key); + if (item == cend()) { + throw std::out_of_range("at(): value out of range"); + } + return item->second; + } + + // TODO update assign interface, operator[], at + + size_type erase(const key_type& k) { + auto segment = pickSegment(k); + auto seg = segments_[segment].load(std::memory_order_acquire); + if (!seg) { + return 0; + } else { + return seg->erase(k); + } + } + + // Calls the hash function, and therefore may throw. + ConstIterator erase(ConstIterator& pos) { + auto segment = pickSegment(pos->first); + ConstIterator res(this, segment); + ensureSegment(segment)->erase(res.it_, pos.it_); + res.next(); // May point to segment end, and need to advance. + return res; + } + + // Erase if and only if key k is equal to expected + size_type erase_if_equal(const key_type& k, const ValueType& expected) { + auto segment = pickSegment(k); + auto seg = segments_[segment].load(std::memory_order_acquire); + if (!seg) { + return 0; + } + return seg->erase_if_equal(k, expected); + } + + // NOT noexcept, initializes new shard segments vs. + void clear() { + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_acquire); + if (seg) { + seg->clear(); + } + } + } + + void reserve(size_t count) { + count = count >> ShardBits; + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_acquire); + if (seg) { + seg->rehash(count); + } + } + } + + // This is a rolling size, and is not exact at any moment in time. + size_t size() const noexcept { + size_t res = 0; + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_acquire); + if (seg) { + res += seg->size(); + } + } + return res; + } + + float max_load_factor() const { + return load_factor_; + } + + void max_load_factor(float factor) { + for (uint64_t i = 0; i < NumShards; i++) { + auto seg = segments_[i].load(std::memory_order_acquire); + if (seg) { + seg->max_load_factor(factor); + } + } + } + + class ConstIterator { + public: + friend class ConcurrentHashMap; + + const value_type& operator*() const { + return *it_; + } + + const value_type* operator->() const { + return &*it_; + } + + ConstIterator& operator++() { + ++it_; + next(); + return *this; + } + + bool operator==(const ConstIterator& o) const { + return it_ == o.it_ && segment_ == o.segment_; + } + + bool operator!=(const ConstIterator& o) const { + return !(*this == o); + } + + ConstIterator& operator=(const ConstIterator& o) = delete; + + ConstIterator& operator=(ConstIterator&& o) noexcept { + if (this != &o) { + it_ = std::move(o.it_); + segment_ = std::exchange(o.segment_, uint64_t(NumShards)); + parent_ = std::exchange(o.parent_, nullptr); + } + return *this; + } + + ConstIterator(const ConstIterator& o) = delete; + + ConstIterator(ConstIterator&& o) noexcept + : it_(std::move(o.it_)), + segment_(std::exchange(o.segment_, uint64_t(NumShards))), + parent_(std::exchange(o.parent_, nullptr)) {} + + ConstIterator(const ConcurrentHashMap* parent, uint64_t segment) + : segment_(segment), parent_(parent) {} + + private: + // cbegin iterator + explicit ConstIterator(const ConcurrentHashMap* parent) + : it_(parent->ensureSegment(0)->cbegin()), + segment_(0), + parent_(parent) { + // Always iterate to the first element, could be in any shard. + next(); + } + + // cend iterator + explicit ConstIterator(uint64_t shards) : it_(nullptr), segment_(shards) {} + + void next() { + while (segment_ < parent_->NumShards && + it_ == parent_->ensureSegment(segment_)->cend()) { + SegmentT* seg{nullptr}; + while (!seg) { + segment_++; + if (segment_ < parent_->NumShards) { + seg = parent_->segments_[segment_].load(std::memory_order_acquire); + if (!seg) { + continue; + } + it_ = seg->cbegin(); + } + break; + } + } + } + + typename SegmentT::Iterator it_; + uint64_t segment_; + const ConcurrentHashMap* parent_; + }; + + private: + uint64_t pickSegment(const KeyType& k) const { + auto h = HashFn()(k); + // Use the lowest bits for our shard bits. + // + // This works well even if the hash function is biased towards the + // low bits: The sharding will happen in the segments_ instead of + // in the segment buckets, so we'll still get write sharding as + // well. + // + // Low-bit bias happens often for std::hash using small numbers, + // since the integer hash function is the identity function. + return h & (NumShards - 1); + } + + SegmentT* ensureSegment(uint64_t i) const { + SegmentT* seg = segments_[i].load(std::memory_order_acquire); + if (!seg) { + auto b = ensureCohort(); + SegmentT* newseg = (SegmentT*)Allocator().allocate(sizeof(SegmentT)); + newseg = new (newseg) + SegmentT(size_ >> ShardBits, load_factor_, max_size_ >> ShardBits, b); + if (!segments_[i].compare_exchange_strong(seg, newseg)) { + // seg is updated with new value, delete ours. + newseg->~SegmentT(); + Allocator().deallocate((uint8_t*)newseg, sizeof(SegmentT)); + } else { + seg = newseg; + } + } + return seg; + } + + hazptr_obj_cohort<Atom>* cohort() const noexcept { + return cohort_.load(std::memory_order_acquire); + } + + hazptr_obj_cohort<Atom>* ensureCohort() const { + auto b = cohort(); + if (!b) { + auto storage = Allocator().allocate(sizeof(hazptr_obj_cohort<Atom>)); + auto newcohort = new (storage) hazptr_obj_cohort<Atom>(); + if (cohort_.compare_exchange_strong(b, newcohort)) { + b = newcohort; + } else { + newcohort->~hazptr_obj_cohort<Atom>(); + Allocator().deallocate(storage, sizeof(hazptr_obj_cohort<Atom>)); + } + } + return b; + } + + void cohort_shutdown_cleanup() { + auto b = cohort(); + if (b) { + b->~hazptr_obj_cohort<Atom>(); + Allocator().deallocate((uint8_t*)b, sizeof(hazptr_obj_cohort<Atom>)); + } + } + + mutable Atom<SegmentT*> segments_[NumShards]; + size_t size_{0}; + size_t max_size_{0}; + mutable Atom<hazptr_obj_cohort<Atom>*> cohort_{nullptr}; +}; + +#if FOLLY_SSE_PREREQ(4, 2) && !FOLLY_MOBILE +template < + typename KeyType, + typename ValueType, + typename HashFn = std::hash<KeyType>, + typename KeyEqual = std::equal_to<KeyType>, + typename Allocator = std::allocator<uint8_t>, + uint8_t ShardBits = 8, + template <typename> class Atom = std::atomic, + class Mutex = std::mutex> +using ConcurrentHashMapSIMD = ConcurrentHashMap< + KeyType, + ValueType, + HashFn, + KeyEqual, + Allocator, + ShardBits, + Atom, + Mutex, + detail::concurrenthashmap::simd::SIMDTable>; +#endif + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h b/ios/Pods/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h new file mode 100644 index 000000000..c6076d761 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <array> +#include <memory> + +#include <folly/concurrency/AtomicSharedPtr.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/container/Enumerate.h> +#include <folly/synchronization/Hazptr.h> + +namespace folly { + +/** + * This class creates core-local caches for a given shared_ptr, to + * mitigate contention when acquiring/releasing it. + * + * It has the same thread-safety guarantees as shared_ptr: it is safe + * to concurrently call get(), but reset()s must be synchronized with + * reads and other resets(). + * + * @author Giuseppe Ottaviano <ott@fb.com> + */ +template <class T, size_t kNumSlots = 64> +class CoreCachedSharedPtr { + public: + explicit CoreCachedSharedPtr(const std::shared_ptr<T>& p = nullptr) { + reset(p); + } + + void reset(const std::shared_ptr<T>& p = nullptr) { + // Allocate each Holder in a different CoreRawAllocator stripe to + // prevent false sharing. Their control blocks will be adjacent + // thanks to allocate_shared(). + for (auto slot : folly::enumerate(slots_)) { + auto alloc = getCoreAllocator<Holder, kNumSlots>(slot.index); + auto holder = std::allocate_shared<Holder>(alloc, p); + *slot = std::shared_ptr<T>(holder, p.get()); + } + } + + std::shared_ptr<T> get() const { + return slots_[AccessSpreader<>::current(kNumSlots)]; + } + + private: + using Holder = std::shared_ptr<T>; + + template <class, size_t> + friend class CoreCachedWeakPtr; + + std::array<std::shared_ptr<T>, kNumSlots> slots_; +}; + +template <class T, size_t kNumSlots = 64> +class CoreCachedWeakPtr { + public: + explicit CoreCachedWeakPtr(const CoreCachedSharedPtr<T, kNumSlots>& p) { + for (auto slot : folly::enumerate(slots_)) { + *slot = p.slots_[slot.index]; + } + } + + std::weak_ptr<T> get() const { + return slots_[AccessSpreader<>::current(kNumSlots)]; + } + + private: + std::array<std::weak_ptr<T>, kNumSlots> slots_; +}; + +/** + * This class creates core-local caches for a given shared_ptr, to + * mitigate contention when acquiring/releasing it. + * + * All methods are threadsafe. Hazard pointers are used to avoid + * use-after-free for concurrent reset() and get() operations. + * + * Concurrent reset()s are sequenced with respect to each other: the + * sharded shared_ptrs will always all be set to the same value. + * get()s will never see a newer pointer on one core, and an older + * pointer on another after a subsequent thread migration. + */ +template <class T, size_t kNumSlots = 64> +class AtomicCoreCachedSharedPtr { + public: + explicit AtomicCoreCachedSharedPtr(const std::shared_ptr<T>& p = nullptr) { + reset(p); + } + + ~AtomicCoreCachedSharedPtr() { + auto slots = slots_.load(std::memory_order_acquire); + // Delete of AtomicCoreCachedSharedPtr must be synchronized, no + // need for stlots->retire(). + if (slots) { + delete slots; + } + } + + void reset(const std::shared_ptr<T>& p = nullptr) { + auto newslots = folly::make_unique<Slots>(); + // Allocate each Holder in a different CoreRawAllocator stripe to + // prevent false sharing. Their control blocks will be adjacent + // thanks to allocate_shared(). + for (auto slot : folly::enumerate(newslots->slots_)) { + auto alloc = getCoreAllocator<Holder, kNumSlots>(slot.index); + auto holder = std::allocate_shared<Holder>(alloc, p); + *slot = std::shared_ptr<T>(holder, p.get()); + } + + auto oldslots = slots_.exchange(newslots.release()); + if (oldslots) { + oldslots->retire(); + } + } + + std::shared_ptr<T> get() const { + folly::hazptr_local<1> hazptr; + auto slots = hazptr[0].get_protected(slots_); + if (!slots) { + return nullptr; + } + return (slots->slots_)[AccessSpreader<>::current(kNumSlots)]; + } + + private: + using Holder = std::shared_ptr<T>; + struct Slots : folly::hazptr_obj_base<Slots> { + std::array<std::shared_ptr<T>, kNumSlots> slots_; + }; + std::atomic<Slots*> slots_{nullptr}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h b/ios/Pods/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h new file mode 100644 index 000000000..403d932b7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h @@ -0,0 +1,739 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/concurrency/CacheLocality.h> +#include <folly/concurrency/UnboundedQueue.h> + +#include <glog/logging.h> + +#include <atomic> +#include <chrono> + +namespace folly { + +/// DynamicBoundedQueue supports: +/// - Dynamic memory usage that grows and shrink in proportion to the +/// number of elements in the queue. +/// - Adjustable capacity that helps throttle pathological cases of +/// producer-consumer imbalance that may lead to excessive memory +/// usage. +/// - The adjustable capacity can also help prevent deadlock by +/// allowing users to temporarily increase capacity substantially to +/// guarantee accommodating producer requests that cannot wait. +/// - SPSC, SPMC, MPSC, MPMC variants. +/// - Blocking and spinning-only variants. +/// - Inter-operable non-waiting, timed until, timed for, and waiting +/// variants of producer and consumer operations. +/// - Optional variable element weights. +/// +/// Element Weights +/// - Queue elements may have variable weights (calculated using a +/// template parameter) that are by default 1. +/// - Element weights count towards the queue's capacity. +/// - Elements weights are not priorities and do not affect element +/// order. Queues with variable element weights follow FIFO order, +/// the same as default queues. +/// +/// When to use DynamicBoundedQueue: +/// - If a small maximum capacity may lead to deadlock or performance +/// degradation under bursty patterns and a larger capacity is +/// sufficient. +/// - If the typical queue size is expected to be much lower than the +/// maximum capacity +/// - If an unbounded queue is susceptible to growing too much. +/// - If support for variable element weights is needed. +/// +/// When not to use DynamicBoundedQueue? +/// - If dynamic memory allocation is unacceptable or if the maximum +/// capacity needs to be small, then use fixed-size MPMCQueue or (if +/// non-blocking SPSC) ProducerConsumerQueue. +/// - If there is no risk of the queue growing too much, then use +/// UnboundedQueue. +/// +/// Setting capacity +/// - The general rule is to set the capacity as high as acceptable. +/// The queue performs best when it is not near full capacity. +/// - The implementation may allow extra slack in capacity (~10%) for +/// amortizing some costly steps. Therefore, precise capacity is not +/// guaranteed and cannot be relied on for synchronization; i.e., +/// this queue cannot be used as a semaphore. +/// +/// Performance expectations: +/// - As long as the queue size is below capacity in the common case, +/// performance is comparable to MPMCQueue and better in cases of +/// higher producer demand. +/// - Performance degrades gracefully at full capacity. +/// - It is recommended to measure performance with different variants +/// when applicable, e.g., DMPMC vs DMPSC. Depending on the use +/// case, sometimes the variant with the higher sequential overhead +/// may yield better results due to, for example, more favorable +/// producer-consumer balance or favorable timing for avoiding +/// costly blocking. +/// - See DynamicBoundedQueueTest.cpp for some benchmark results. +/// +/// Template parameters: +/// - T: element type +/// - SingleProducer: true if there can be only one producer at a +/// time. +/// - SingleConsumer: true if there can be only one consumer at a +/// time. +/// - MayBlock: true if producers or consumers may block. +/// - LgSegmentSize (default 8): Log base 2 of number of elements per +/// UnboundedQueue segment. +/// - LgAlign (default 7): Log base 2 of alignment directive; can be +/// used to balance scalability (avoidance of false sharing) with +/// memory efficiency. +/// - WeightFn (DefaultWeightFn<T>): A customizable weight computing type +/// for computing the weights of elements. The default weight is 1. +/// +/// Template Aliases: +/// DSPSCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// DMPSCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// DSPMCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// DMPMCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// +/// Functions: +/// Constructor +/// Takes a capacity value as an argument. +/// +/// Producer functions: +/// void enqueue(const T&); +/// void enqueue(T&&); +/// Adds an element to the end of the queue. Waits until +/// capacity is available if necessary. +/// bool try_enqueue(const T&); +/// bool try_enqueue(T&&); +/// Tries to add an element to the end of the queue if +/// capacity allows it. Returns true if successful. Otherwise +/// Returns false. +/// bool try_enqueue_until(const T&, time_point& deadline); +/// bool try_enqueue_until(T&&, time_point& deadline); +/// Tries to add an element to the end of the queue if +/// capacity allows it until the specified deadline. Returns +/// true if successful, otherwise false. +/// bool try_enqueue_for(const T&, duration&); +/// bool try_enqueue_for(T&&, duration&); +/// Tries to add an element to the end of the queue if +/// capacity allows until the expiration of the specified +/// duration. Returns true if successful, otherwise false. +/// +/// Consumer functions: +/// void dequeue(T&); +/// Extracts an element from the front of the queue. Waits +/// until an element is available if necessary. +/// bool try_dequeue(T&); +/// Tries to extracts an element from the front of the queue +/// if available. Returns true if successful, otherwise false. +/// bool try_dequeue_until(T&, time_point& deadline); +/// Tries to extracts an element from the front of the queue +/// if available until the specified daedline. Returns true +/// if successful. Otherwise Returns false. +/// bool try_dequeue_for(T&, duration&); +/// Tries to extracts an element from the front of the queue +/// if available until the expiration of the specified +/// duration. Returns true if successful. Otherwise Returns +/// false. +/// +/// Secondary functions: +/// void reset_capacity(size_t capacity); +/// Changes the capacity of the queue. Does not affect the +/// current contents of the queue. Guaranteed only to affect +/// subsequent enqueue operations. May or may not affect +/// concurrent operations. Capacity must be at least 1000. +/// Weight weight(); +/// Returns an estimate of the total weight of the elements in +/// the queue. +/// size_t size(); +/// Returns an estimate of the total number of elements. +/// bool empty(); +/// Returns true only if the queue was empty during the call. +/// Note: weight(), size(), and empty() are guaranteed to be +/// accurate only if there are no concurrent changes to the queue. +/// +/// Usage example with default weight: +/// @code +/// /* DMPSC, doesn't block, 1024 int elements per segment */ +/// DMPSCQueue<int, false, 10> q(100000); +/// ASSERT_TRUE(q.empty()); +/// ASSERT_EQ(q.size(), 0); +/// q.enqueue(1)); +/// ASSERT_TRUE(q.try_enqueue(2)); +/// ASSERT_TRUE(q.try_enqueue_until(3, deadline)); +/// ASSERT_TRUE(q.try_enqueue(4, duration)); +/// // ... enqueue more elements until capacity is full +/// // See above comments about imprecise capacity guarantees +/// ASSERT_FALSE(q.try_enqueue(100001)); // can't enqueue but can't wait +/// size_t sz = q.size(); +/// ASSERT_GE(sz, 100000); +/// q.reset_capacity(1000000000); // set huge capacity +/// ASSERT_TRUE(q.try_enqueue(100001)); // now enqueue succeeds +/// q.reset_capacity(100000); // set capacity back to 100,000 +/// ASSERT_FALSE(q.try_enqueue(100002)); +/// ASSERT_EQ(q.size(), sz + 1); +/// int v; +/// q.dequeue(v); +/// ASSERT_EQ(v, 1); +/// ASSERT_TRUE(q.try_dequeue(v)); +/// ASSERT_EQ(v, 2); +/// ASSERT_TRUE(q.try_dequeue_until(v, deadline)); +/// ASSERT_EQ(v, 3); +/// ASSERT_TRUE(q.try_dequeue_for(v, duration)); +/// ASSERT_EQ(v, 4); +/// ASSERT_EQ(q.size(), sz - 3); +/// @endcode +/// +/// Usage example with custom weights: +/// @code +/// struct CustomWeightFn { +/// uint64_t operator()(int val) { return val / 100; } +/// }; +/// DMPMCQueue<int, false, 10, CustomWeightFn> q(20); +/// ASSERT_TRUE(q.empty()); +/// q.enqueue(100); +/// ASSERT_TRUE(q.try_enqueue(200)); +/// ASSERT_TRUE(q.try_enqueue_until(500, now() + seconds(1))); +/// ASSERT_EQ(q.size(), 3); +/// ASSERT_EQ(q.weight(), 8); +/// ASSERT_FALSE(q.try_enqueue_for(1700, microseconds(1))); +/// q.reset_capacity(1000000); // set capacity to 1000000 instead of 20 +/// ASSERT_TRUE(q.try_enqueue_for(1700, microseconds(1))); +/// q.reset_capacity(20); // set capacity to 20 again +/// ASSERT_FALSE(q.try_enqueue(100)); +/// ASSERT_EQ(q.size(), 4); +/// ASSERT_EQ(q.weight(), 25); +/// int v; +/// q.dequeue(v); +/// ASSERT_EQ(v, 100); +/// ASSERT_TRUE(q.try_dequeue(v)); +/// ASSERT_EQ(v, 200); +/// ASSERT_TRUE(q.try_dequeue_until(v, now() + seconds(1))); +/// ASSERT_EQ(v, 500); +/// ASSERT_EQ(q.size(), 1); +/// ASSERT_EQ(q.weight(), 17); +/// @endcode +/// +/// Design: +/// - The implementation is on top of UnboundedQueue. +/// - The main FIFO functionality is in UnboundedQueue. +/// DynamicBoundedQueue manages keeping the total queue weight +/// within the specified capacity. +/// - For the sake of scalability, the data structures are designed to +/// minimize interference between producers on one side and +/// consumers on the other. +/// - Producers add to a debit variable the weight of the added +/// element and check capacity. +/// - Consumers add to a credit variable the weight of the removed +/// element. +/// - Producers, for the sake of scalability, use fetch_add to add to +/// the debit variable and subtract if it exceeded capacity, +/// rather than using compare_exchange to avoid overshooting. +/// - Consumers, infrequently, transfer credit to a transfer variable +/// and unblock any blocked producers. The transfer variable can be +/// used by producers to decrease their debit when needed. +/// - Note that a low capacity will trigger frequent credit transfer +/// by consumers that may degrade performance. Capacity should not +/// be set too low. +/// - Transfer of credit by consumers is triggered when the amount of +/// credit reaches a threshold (1/10 of capacity). +/// - The waiting of consumers is handled in UnboundedQueue. +/// The waiting of producers is handled in this template. +/// - For a producer operation, if the difference between debit and +/// capacity (plus some slack to account for the transfer threshold) +/// does not accommodate the weight of the new element, it first +/// tries to transfer credit that may have already been made +/// available by consumers. If this is insufficient and MayBlock is +/// true, then the producer uses a futex to block until new credit +/// is transferred by a consumer. +/// +/// Memory Usage: +/// - Aside from three cache lines for managing capacity, the memory +/// for queue elements is managed using UnboundedQueue and grows and +/// shrinks dynamically with the number of elements. +/// - The template parameter LgAlign can be used to reduce memory usage +/// at the cost of increased chance of false sharing. + +template <typename T> +struct DefaultWeightFn { + template <typename Arg> + uint64_t operator()(Arg&&) const noexcept { + return 1; + } +}; + +template < + typename T, + bool SingleProducer, + bool SingleConsumer, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = 7, + typename WeightFn = DefaultWeightFn<T>, + template <typename> class Atom = std::atomic> +class DynamicBoundedQueue { + using Weight = uint64_t; + + enum WaitingState : uint32_t { + NOTWAITING = 0, + WAITING = 1, + }; + + static constexpr bool SPSC = SingleProducer && SingleConsumer; + static constexpr size_t Align = 1u << LgAlign; + + static_assert(LgAlign < 16, "LgAlign must be < 16"); + + /// Data members + + // Read mostly by producers + alignas(Align) Atom<Weight> debit_; // written frequently only by producers + Atom<Weight> capacity_; // written rarely by capacity resets + + // Read mostly by consumers + alignas(Align) Atom<Weight> credit_; // written frequently only by consumers + Atom<Weight> threshold_; // written rarely only by capacity resets + + // Normally written and read rarely by producers and consumers + // May be read frequently by producers when capacity is full + alignas(Align) Atom<Weight> transfer_; + detail::Futex<Atom> waiting_; + + // Underlying unbounded queue + UnboundedQueue< + T, + SingleProducer, + SingleConsumer, + MayBlock, + LgSegmentSize, + LgAlign, + Atom> + q_; + + public: + /** constructor */ + explicit DynamicBoundedQueue(Weight capacity) + : debit_(0), + capacity_(capacity + threshold(capacity)), // capacity slack + credit_(0), + threshold_(threshold(capacity)), + transfer_(0), + waiting_(0) {} + + /** destructor */ + ~DynamicBoundedQueue() {} + + /// Enqueue functions + + /** enqueue */ + FOLLY_ALWAYS_INLINE void enqueue(const T& v) { + enqueueImpl(v); + } + + FOLLY_ALWAYS_INLINE void enqueue(T&& v) { + enqueueImpl(std::move(v)); + } + + /** try_enqueue */ + FOLLY_ALWAYS_INLINE bool try_enqueue(const T& v) { + return tryEnqueueImpl(v); + } + + FOLLY_ALWAYS_INLINE bool try_enqueue(T&& v) { + return tryEnqueueImpl(std::move(v)); + } + + /** try_enqueue_until */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_enqueue_until( + const T& v, + const std::chrono::time_point<Clock, Duration>& deadline) { + return tryEnqueueUntilImpl(v, deadline); + } + + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_enqueue_until( + T&& v, + const std::chrono::time_point<Clock, Duration>& deadline) { + return tryEnqueueUntilImpl(std::move(v), deadline); + } + + /** try_enqueue_for */ + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool try_enqueue_for( + const T& v, + const std::chrono::duration<Rep, Period>& duration) { + return tryEnqueueForImpl(v, duration); + } + + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool try_enqueue_for( + T&& v, + const std::chrono::duration<Rep, Period>& duration) { + return tryEnqueueForImpl(std::move(v), duration); + } + + /// Dequeue functions + + /** dequeue */ + FOLLY_ALWAYS_INLINE void dequeue(T& elem) { + q_.dequeue(elem); + addCredit(WeightFn()(elem)); + } + + /** try_dequeue */ + FOLLY_ALWAYS_INLINE bool try_dequeue(T& elem) { + if (q_.try_dequeue(elem)) { + addCredit(WeightFn()(elem)); + return true; + } + return false; + } + + /** try_dequeue_until */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_dequeue_until( + T& elem, + const std::chrono::time_point<Clock, Duration>& deadline) { + if (q_.try_dequeue_until(elem, deadline)) { + addCredit(WeightFn()(elem)); + return true; + } + return false; + } + + /** try_dequeue_for */ + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool try_dequeue_for( + T& elem, + const std::chrono::duration<Rep, Period>& duration) { + if (q_.try_dequeue_for(elem, duration)) { + addCredit(WeightFn()(elem)); + return true; + } + return false; + } + + /// Secondary functions + + /** reset_capacity */ + void reset_capacity(Weight capacity) noexcept { + Weight thresh = threshold(capacity); + capacity_.store(capacity + thresh, std::memory_order_release); + threshold_.store(thresh, std::memory_order_release); + } + + /** weight */ + Weight weight() const noexcept { + auto d = getDebit(); + auto c = getCredit(); + auto t = getTransfer(); + return d > (c + t) ? d - (c + t) : 0; + } + + /** size */ + size_t size() const noexcept { + return q_.size(); + } + + /** empty */ + bool empty() const noexcept { + return q_.empty(); + } + + private: + /// Private functions /// + + // Calculation of threshold to move credits in bulk from consumers + // to producers + constexpr Weight threshold(Weight capacity) const noexcept { + return capacity / 10; + } + + // Functions called frequently by producers + + template <typename Arg> + FOLLY_ALWAYS_INLINE void enqueueImpl(Arg&& v) { + tryEnqueueUntilImpl( + std::forward<Arg>(v), std::chrono::steady_clock::time_point::max()); + } + + template <typename Arg> + FOLLY_ALWAYS_INLINE bool tryEnqueueImpl(Arg&& v) { + return tryEnqueueUntilImpl( + std::forward<Arg>(v), std::chrono::steady_clock::time_point::min()); + } + + template <typename Clock, typename Duration, typename Arg> + FOLLY_ALWAYS_INLINE bool tryEnqueueUntilImpl( + Arg&& v, + const std::chrono::time_point<Clock, Duration>& deadline) { + Weight weight = WeightFn()(std::forward<Arg>(v)); + if (LIKELY(tryAddDebit(weight))) { + q_.enqueue(std::forward<Arg>(v)); + return true; + } + return tryEnqueueUntilSlow(std::forward<Arg>(v), deadline); + } + + template <typename Rep, typename Period, typename Arg> + FOLLY_ALWAYS_INLINE bool tryEnqueueForImpl( + Arg&& v, + const std::chrono::duration<Rep, Period>& duration) { + if (LIKELY(tryEnqueueImpl(std::forward<Arg>(v)))) { + return true; + } + auto deadline = std::chrono::steady_clock::now() + duration; + return tryEnqueueUntilSlow(std::forward<Arg>(v), deadline); + } + + FOLLY_ALWAYS_INLINE bool tryAddDebit(Weight weight) noexcept { + Weight capacity = getCapacity(); + Weight before = fetchAddDebit(weight); + if (LIKELY(before + weight <= capacity)) { + return true; + } else { + subDebit(weight); + return false; + } + } + + FOLLY_ALWAYS_INLINE Weight getCapacity() const noexcept { + return capacity_.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE Weight fetchAddDebit(Weight weight) noexcept { + Weight before; + if (SingleProducer) { + before = getDebit(); + debit_.store(before + weight, std::memory_order_relaxed); + } else { + before = debit_.fetch_add(weight, std::memory_order_acq_rel); + } + return before; + } + + FOLLY_ALWAYS_INLINE Weight getDebit() const noexcept { + return debit_.load(std::memory_order_acquire); + } + + // Functions called frequently by consumers + + FOLLY_ALWAYS_INLINE void addCredit(Weight weight) noexcept { + Weight before = fetchAddCredit(weight); + Weight thresh = getThreshold(); + if (before + weight >= thresh && before < thresh) { + transferCredit(); + } + } + + FOLLY_ALWAYS_INLINE Weight fetchAddCredit(Weight weight) noexcept { + Weight before; + if (SingleConsumer) { + before = getCredit(); + credit_.store(before + weight, std::memory_order_relaxed); + } else { + before = credit_.fetch_add(weight, std::memory_order_acq_rel); + } + return before; + } + + FOLLY_ALWAYS_INLINE Weight getCredit() const noexcept { + return credit_.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE Weight getThreshold() const noexcept { + return threshold_.load(std::memory_order_acquire); + } + + /** Functions called infrequently by producers */ + + void subDebit(Weight weight) noexcept { + Weight before; + if (SingleProducer) { + before = getDebit(); + debit_.store(before - weight, std::memory_order_relaxed); + } else { + before = debit_.fetch_sub(weight, std::memory_order_acq_rel); + } + DCHECK_GE(before, weight); + } + + template <typename Clock, typename Duration, typename Arg> + bool tryEnqueueUntilSlow( + Arg&& v, + const std::chrono::time_point<Clock, Duration>& deadline) { + Weight weight = WeightFn()(std::forward<Arg>(v)); + if (canEnqueue(deadline, weight)) { + q_.enqueue(std::forward<Arg>(v)); + return true; + } else { + return false; + } + } + + template <typename Clock, typename Duration> + bool canEnqueue( + const std::chrono::time_point<Clock, Duration>& deadline, + Weight weight) noexcept { + Weight capacity = getCapacity(); + while (true) { + tryReduceDebit(); + Weight debit = getDebit(); + if ((debit + weight <= capacity) && tryAddDebit(weight)) { + return true; + } + if (deadline < Clock::time_point::max() && Clock::now() >= deadline) { + return false; + } + if (MayBlock) { + if (canBlock(weight, capacity)) { + detail::futexWaitUntil(&waiting_, WAITING, deadline); + } + } else { + asm_volatile_pause(); + } + } + } + + bool canBlock(Weight weight, Weight capacity) noexcept { + waiting_.store(WAITING, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); + tryReduceDebit(); + Weight debit = getDebit(); + return debit + weight > capacity; + } + + bool tryReduceDebit() noexcept { + Weight w = takeTransfer(); + if (w > 0) { + subDebit(w); + } + return w > 0; + } + + Weight takeTransfer() noexcept { + Weight w = getTransfer(); + if (w > 0) { + w = transfer_.exchange(0, std::memory_order_acq_rel); + } + return w; + } + + Weight getTransfer() const noexcept { + return transfer_.load(std::memory_order_acquire); + } + + /** Functions called infrequently by consumers */ + + void transferCredit() noexcept { + Weight credit = takeCredit(); + transfer_.fetch_add(credit, std::memory_order_acq_rel); + if (MayBlock) { + std::atomic_thread_fence(std::memory_order_seq_cst); + waiting_.store(NOTWAITING, std::memory_order_relaxed); + detail::futexWake(&waiting_); + } + } + + Weight takeCredit() noexcept { + Weight credit; + if (SingleConsumer) { + credit = credit_.load(std::memory_order_relaxed); + credit_.store(0, std::memory_order_relaxed); + } else { + credit = credit_.exchange(0, std::memory_order_acq_rel); + } + return credit; + } + +}; // DynamicBoundedQueue + +/// Aliases + +/** DSPSCQueue */ +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = 7, + typename WeightFn = DefaultWeightFn<T>, + template <typename> class Atom = std::atomic> +using DSPSCQueue = DynamicBoundedQueue< + T, + true, + true, + MayBlock, + LgSegmentSize, + LgAlign, + WeightFn, + Atom>; + +/** DMPSCQueue */ +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = 7, + typename WeightFn = DefaultWeightFn<T>, + template <typename> class Atom = std::atomic> +using DMPSCQueue = DynamicBoundedQueue< + T, + false, + true, + MayBlock, + LgSegmentSize, + LgAlign, + WeightFn, + Atom>; + +/** DSPMCQueue */ +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = 7, + typename WeightFn = DefaultWeightFn<T>, + template <typename> class Atom = std::atomic> +using DSPMCQueue = DynamicBoundedQueue< + T, + true, + false, + MayBlock, + LgSegmentSize, + LgAlign, + WeightFn, + Atom>; + +/** DMPMCQueue */ +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = 7, + typename WeightFn = DefaultWeightFn<T>, + template <typename> class Atom = std::atomic> +using DMPMCQueue = DynamicBoundedQueue< + T, + false, + false, + MayBlock, + LgSegmentSize, + LgAlign, + WeightFn, + Atom>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h b/ios/Pods/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h new file mode 100644 index 000000000..e70957ec4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <vector> + +#include <folly/Memory.h> +#include <folly/concurrency/UnboundedQueue.h> +#include <folly/lang/Align.h> + +namespace folly { + +/// PriorityUnboundedQueueSet +/// +/// A set of per-priority queues, and an interface for accessing them. +/// +/// Functions: +/// Consumer operations: +/// bool try_dequeue(T&); +/// Optional<T> try_dequeue(); +/// Tries to extract an element from the front of the least-priority +/// backing queue which has an element, if any. +/// T const* try_peek(); +/// Returns a pointer to the element at the front of the least-priority +/// backing queue which has an element, if any. Only allowed when +/// SingleConsumer is true. +/// Note: +/// Queues at lower priority are tried before queues at higher priority. +/// +/// Secondary functions: +/// queue& at_priority(size_t); +/// queue const& at_priority(size_t) const; +/// Returns a reference to the owned queue at the given priority. +/// size_t size() const; +/// Returns an estimate of the total size of the owned queues. +/// bool empty() const; +/// Returns true only if all of the owned queues were empty during the +/// call. +/// Note: size() and empty() are guaranteed to be accurate only if the +/// owned queues are not changed concurrently. +template < + typename T, + bool SingleProducer, + bool SingleConsumer, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +class PriorityUnboundedQueueSet { + public: + using queue = UnboundedQueue< + T, + SingleProducer, + SingleConsumer, + MayBlock, + LgSegmentSize, + LgAlign, + Atom>; + + explicit PriorityUnboundedQueueSet(size_t priorities) : queues_(priorities) {} + + PriorityUnboundedQueueSet(PriorityUnboundedQueueSet const&) = delete; + PriorityUnboundedQueueSet(PriorityUnboundedQueueSet&&) = delete; + PriorityUnboundedQueueSet& operator=(PriorityUnboundedQueueSet const&) = + delete; + PriorityUnboundedQueueSet& operator=(PriorityUnboundedQueueSet&&) = delete; + + queue& at_priority(size_t priority) { + return queues_.at(priority); + } + + queue const& at_priority(size_t priority) const { + return queues_.at(priority); + } + + bool try_dequeue(T& item) noexcept { + for (auto& q : queues_) { + if (q.try_dequeue(item)) { + return true; + } + } + return false; + } + + Optional<T> try_dequeue() noexcept { + for (auto& q : queues_) { + if (auto item = q.try_dequeue()) { + return item; + } + } + return none; + } + + T const* try_peek() noexcept { + DCHECK(SingleConsumer); + for (auto& q : queues_) { + if (auto ptr = q.try_peek()) { + return ptr; + } + } + return nullptr; + } + + size_t size() const noexcept { + size_t size = 0; + for (auto& q : queues_) { + size += q.size(); + } + return size; + } + + bool empty() const noexcept { + for (auto& q : queues_) { + if (!q.empty()) { + return false; + } + } + return true; + } + + size_t priorities() const noexcept { + return queues_.size(); + } + + private: + // queue_alloc custom allocator is necessary until C++17 + // http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3396.htm + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65122 + // https://bugs.llvm.org/show_bug.cgi?id=22634 + using queue_alloc = AlignedSysAllocator<queue, FixedAlign<alignof(queue)>>; + std::vector<queue, queue_alloc> queues_; +}; // PriorityUnboundedQueueSet + +/* Aliases */ + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using PriorityUSPSCQueueSet = PriorityUnboundedQueueSet< + T, + true, + true, + MayBlock, + LgSegmentSize, + LgAlign, + Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using PriorityUMPSCQueueSet = PriorityUnboundedQueueSet< + T, + false, + true, + MayBlock, + LgSegmentSize, + LgAlign, + Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using PriorityUSPMCQueueSet = PriorityUnboundedQueueSet< + T, + true, + false, + MayBlock, + LgSegmentSize, + LgAlign, + Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using PriorityUMPMCQueueSet = PriorityUnboundedQueueSet< + T, + false, + false, + MayBlock, + LgSegmentSize, + LgAlign, + Atom>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/concurrency/UnboundedQueue.h b/ios/Pods/Flipper-Folly/folly/concurrency/UnboundedQueue.h new file mode 100644 index 000000000..fc56aba7c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/concurrency/UnboundedQueue.h @@ -0,0 +1,888 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <chrono> +#include <memory> + +#include <glog/logging.h> + +#include <folly/ConstexprMath.h> +#include <folly/Optional.h> +#include <folly/Traits.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/lang/Align.h> +#include <folly/synchronization/Hazptr.h> +#include <folly/synchronization/SaturatingSemaphore.h> +#include <folly/synchronization/WaitOptions.h> +#include <folly/synchronization/detail/Spin.h> + +namespace folly { + +/// UnboundedQueue supports a variety of options for unbounded +/// dynamically expanding an shrinking queues, including variations of: +/// - Single vs. multiple producers +/// - Single vs. multiple consumers +/// - Blocking vs. spin-waiting +/// - Non-waiting, timed, and waiting consumer operations. +/// Producer operations never wait or fail (unless out-of-memory). +/// +/// Template parameters: +/// - T: element type +/// - SingleProducer: true if there can be only one producer at a +/// time. +/// - SingleConsumer: true if there can be only one consumer at a +/// time. +/// - MayBlock: true if consumers may block, false if they only +/// spin. A performance tuning parameter. +/// - LgSegmentSize (default 8): Log base 2 of number of elements per +/// segment. A performance tuning parameter. See below. +/// - LgAlign (default 7): Log base 2 of alignment directive; can be +/// used to balance scalability (avoidance of false sharing) with +/// memory efficiency. +/// +/// When to use UnboundedQueue: +/// - If a small bound may lead to deadlock or performance degradation +/// under bursty patterns. +/// - If there is no risk of the queue growing too much. +/// +/// When not to use UnboundedQueue: +/// - If there is risk of the queue growing too much and a large bound +/// is acceptable, then use DynamicBoundedQueue. +/// - If the queue must not allocate on enqueue or it must have a +/// small bound, then use fixed-size MPMCQueue or (if non-blocking +/// SPSC) ProducerConsumerQueue. +/// +/// Template Aliases: +/// USPSCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// UMPSCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// USPMCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// UMPMCQueue<T, MayBlock, LgSegmentSize, LgAlign> +/// +/// Functions: +/// Producer operations never wait or fail (unless OOM) +/// void enqueue(const T&); +/// void enqueue(T&&); +/// Adds an element to the end of the queue. +/// +/// Consumer operations: +/// void dequeue(T&); +/// T dequeue(); +/// Extracts an element from the front of the queue. Waits +/// until an element is available if needed. +/// bool try_dequeue(T&); +/// folly::Optional<T> try_dequeue(); +/// Tries to extract an element from the front of the queue +/// if available. +/// bool try_dequeue_until(T&, time_point& deadline); +/// folly::Optional<T> try_dequeue_until(time_point& deadline); +/// Tries to extract an element from the front of the queue +/// if available until the specified deadline. +/// bool try_dequeue_for(T&, duration&); +/// folly::Optional<T> try_dequeue_for(duration&); +/// Tries to extract an element from the front of the queue if +/// available until the expiration of the specified duration. +/// const T* try_peek(); +/// Returns pointer to the element at the front of the queue +/// if available, or nullptr if the queue is empty. Only for +/// SPSC and MPSC. +/// +/// Secondary functions: +/// size_t size(); +/// Returns an estimate of the size of the queue. +/// bool empty(); +/// Returns true only if the queue was empty during the call. +/// Note: size() and empty() are guaranteed to be accurate only if +/// the queue is not changed concurrently. +/// +/// Usage examples: +/// @code +/// /* UMPSC, doesn't block, 1024 int elements per segment */ +/// UMPSCQueue<int, false, 10> q; +/// q.enqueue(1); +/// q.enqueue(2); +/// q.enqueue(3); +/// ASSERT_FALSE(q.empty()); +/// ASSERT_EQ(q.size(), 3); +/// int v; +/// q.dequeue(v); +/// ASSERT_EQ(v, 1); +/// ASSERT_TRUE(try_dequeue(v)); +/// ASSERT_EQ(v, 2); +/// ASSERT_TRUE(try_dequeue_until(v, now() + seconds(1))); +/// ASSERT_EQ(v, 3); +/// ASSERT_TRUE(q.empty()); +/// ASSERT_EQ(q.size(), 0); +/// ASSERT_FALSE(try_dequeue(v)); +/// ASSERT_FALSE(try_dequeue_for(v, microseconds(100))); +/// @endcode +/// +/// Design: +/// - The queue is composed of one or more segments. Each segment has +/// a fixed size of 2^LgSegmentSize entries. Each segment is used +/// exactly once. +/// - Each entry is composed of a futex and a single element. +/// - The queue contains two 64-bit ticket variables. The producer +/// ticket counts the number of producer tickets issued so far, and +/// the same for the consumer ticket. Each ticket number corresponds +/// to a specific entry in a specific segment. +/// - The queue maintains two pointers, head and tail. Head points to +/// the segment that corresponds to the current consumer +/// ticket. Similarly, tail pointer points to the segment that +/// corresponds to the producer ticket. +/// - Segments are organized as a singly linked list. +/// - The producer with the first ticket in the current producer +/// segment has primary responsibility for allocating and linking +/// the next segment. Other producers and connsumers may help do so +/// when needed if that thread is delayed. +/// - The producer with the last ticket in the current producer +/// segment is primarily responsible for advancing the tail pointer +/// to the next segment. Other producers and consumers may help do +/// so when needed if that thread is delayed. +/// - Similarly, the consumer with the last ticket in the current +/// consumer segment is primarily responsible for advancing the head +/// pointer to the next segment. Other consumers may help do so when +/// needed if that thread is delayed. +/// - The tail pointer must not lag behind the head pointer. +/// Otherwise, the algorithm cannot be certain about the removal of +/// segment and would have to incur higher costs to ensure safe +/// reclamation. Consumers must ensure that head never overtakes +/// tail. +/// +/// Memory Usage: +/// - An empty queue contains one segment. A nonempty queue contains +/// one or two more segment than fits its contents. +/// - Removed segments are not reclaimed until there are no threads, +/// producers or consumers, with references to them or their +/// predecessors. That is, a lagging thread may delay the reclamation +/// of a chain of removed segments. +/// - The template parameter LgAlign can be used to reduce memory usage +/// at the cost of increased chance of false sharing. +/// +/// Performance considerations: +/// - All operations take constant time, excluding the costs of +/// allocation, reclamation, interference from other threads, and +/// waiting for actions by other threads. +/// - In general, using the single producer and or single consumer +/// variants yield better performance than the MP and MC +/// alternatives. +/// - SPSC without blocking is the fastest configuration. It doesn't +/// include any read-modify-write atomic operations, full fences, or +/// system calls in the critical path. +/// - MP adds a fetch_add to the critical path of each producer operation. +/// - MC adds a fetch_add or compare_exchange to the critical path of +/// each consumer operation. +/// - The possibility of consumers blocking, even if they never do, +/// adds a compare_exchange to the critical path of each producer +/// operation. +/// - MPMC, SPMC, MPSC require the use of a deferred reclamation +/// mechanism to guarantee that segments removed from the linked +/// list, i.e., unreachable from the head pointer, are reclaimed +/// only after they are no longer needed by any lagging producers or +/// consumers. +/// - The overheads of segment allocation and reclamation are intended +/// to be mostly out of the critical path of the queue's throughput. +/// - If the template parameter LgSegmentSize is changed, it should be +/// set adequately high to keep the amortized cost of allocation and +/// reclamation low. +/// - It is recommended to measure performance with different variants +/// when applicable, e.g., UMPMC vs UMPSC. Depending on the use +/// case, sometimes the variant with the higher sequential overhead +/// may yield better results due to, for example, more favorable +/// producer-consumer balance or favorable timing for avoiding +/// costly blocking. + +template < + typename T, + bool SingleProducer, + bool SingleConsumer, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +class UnboundedQueue { + using Ticket = uint64_t; + class Entry; + class Segment; + + static constexpr bool SPSC = SingleProducer && SingleConsumer; + static constexpr size_t Stride = SPSC || (LgSegmentSize <= 1) ? 1 : 27; + static constexpr size_t SegmentSize = 1u << LgSegmentSize; + static constexpr size_t Align = 1u << LgAlign; + + static_assert( + std::is_nothrow_destructible<T>::value, + "T must be nothrow_destructible"); + static_assert((Stride & 1) == 1, "Stride must be odd"); + static_assert(LgSegmentSize < 32, "LgSegmentSize must be < 32"); + static_assert(LgAlign < 16, "LgAlign must be < 16"); + + using Sem = folly::SaturatingSemaphore<MayBlock, Atom>; + + struct Consumer { + Atom<Segment*> head; + Atom<Ticket> ticket; + hazptr_obj_cohort<Atom> cohort; + explicit Consumer(Segment* s) : head(s), ticket(0) { + s->set_cohort_no_tag(&cohort); // defined in hazptr_obj + } + }; + struct Producer { + Atom<Segment*> tail; + Atom<Ticket> ticket; + explicit Producer(Segment* s) : tail(s), ticket(0) {} + }; + + alignas(Align) Consumer c_; + alignas(Align) Producer p_; + + public: + /** constructor */ + UnboundedQueue() + : c_(new Segment(0)), p_(c_.head.load(std::memory_order_relaxed)) {} + + /** destructor */ + ~UnboundedQueue() { + cleanUpRemainingItems(); + reclaimRemainingSegments(); + } + + /** enqueue */ + FOLLY_ALWAYS_INLINE void enqueue(const T& arg) { + enqueueImpl(arg); + } + + FOLLY_ALWAYS_INLINE void enqueue(T&& arg) { + enqueueImpl(std::move(arg)); + } + + /** dequeue */ + FOLLY_ALWAYS_INLINE void dequeue(T& item) noexcept { + item = dequeueImpl(); + } + + FOLLY_ALWAYS_INLINE T dequeue() noexcept { + return dequeueImpl(); + } + + /** try_dequeue */ + FOLLY_ALWAYS_INLINE bool try_dequeue(T& item) noexcept { + auto o = try_dequeue(); + if (LIKELY(o.has_value())) { + item = std::move(*o); + return true; + } + return false; + } + + FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue() noexcept { + return tryDequeueUntil(std::chrono::steady_clock::time_point::min()); + } + + /** try_dequeue_until */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_dequeue_until( + T& item, + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + folly::Optional<T> o = try_dequeue_until(deadline); + + if (LIKELY(o.has_value())) { + item = std::move(*o); + return true; + } + + return false; + } + + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_until( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + return tryDequeueUntil(deadline); + } + + /** try_dequeue_for */ + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool try_dequeue_for( + T& item, + const std::chrono::duration<Rep, Period>& duration) noexcept { + folly::Optional<T> o = try_dequeue_for(duration); + + if (LIKELY(o.has_value())) { + item = std::move(*o); + return true; + } + + return false; + } + + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_for( + const std::chrono::duration<Rep, Period>& duration) noexcept { + folly::Optional<T> o = try_dequeue(); + if (LIKELY(o.has_value())) { + return o; + } + return tryDequeueUntil(std::chrono::steady_clock::now() + duration); + } + + /** try_peek */ + FOLLY_ALWAYS_INLINE const T* try_peek() noexcept { + /* This function is supported only for USPSC and UMPSC queues. */ + DCHECK(SingleConsumer); + return tryPeekUntil(std::chrono::steady_clock::time_point::min()); + } + + /** size */ + size_t size() const noexcept { + auto p = producerTicket(); + auto c = consumerTicket(); + return p > c ? p - c : 0; + } + + /** empty */ + bool empty() const noexcept { + auto c = consumerTicket(); + auto p = producerTicket(); + return p <= c; + } + + private: + /** enqueueImpl */ + template <typename Arg> + FOLLY_ALWAYS_INLINE void enqueueImpl(Arg&& arg) { + if (SPSC) { + Segment* s = tail(); + enqueueCommon(s, std::forward<Arg>(arg)); + } else { + // Using hazptr_holder instead of hazptr_local because it is + // possible that the T ctor happens to use hazard pointers. + hazptr_holder<Atom> hptr; + Segment* s = hptr.get_protected(p_.tail); + enqueueCommon(s, std::forward<Arg>(arg)); + } + } + + /** enqueueCommon */ + template <typename Arg> + FOLLY_ALWAYS_INLINE void enqueueCommon(Segment* s, Arg&& arg) { + Ticket t = fetchIncrementProducerTicket(); + if (!SingleProducer) { + s = findSegment(s, t); + } + DCHECK_GE(t, s->minTicket()); + DCHECK_LT(t, s->minTicket() + SegmentSize); + size_t idx = index(t); + Entry& e = s->entry(idx); + e.putItem(std::forward<Arg>(arg)); + if (responsibleForAlloc(t)) { + allocNextSegment(s); + } + if (responsibleForAdvance(t)) { + advanceTail(s); + } + } + + /** dequeueImpl */ + FOLLY_ALWAYS_INLINE T dequeueImpl() noexcept { + if (SPSC) { + Segment* s = head(); + return dequeueCommon(s); + } else { + // Using hazptr_holder instead of hazptr_local because it is + // possible to call the T dtor and it may happen to use hazard + // pointers. + hazptr_holder<Atom> hptr; + Segment* s = hptr.get_protected(c_.head); + return dequeueCommon(s); + } + } + + /** dequeueCommon */ + FOLLY_ALWAYS_INLINE T dequeueCommon(Segment* s) noexcept { + Ticket t = fetchIncrementConsumerTicket(); + if (!SingleConsumer) { + s = findSegment(s, t); + } + size_t idx = index(t); + Entry& e = s->entry(idx); + auto res = e.takeItem(); + if (responsibleForAdvance(t)) { + advanceHead(s); + } + return res; + } + + /** tryDequeueUntil */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntil( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + if (SingleConsumer) { + Segment* s = head(); + return tryDequeueUntilSC(s, deadline); + } else { + // Using hazptr_holder instead of hazptr_local because it is + // possible to call ~T() and it may happen to use hazard pointers. + hazptr_holder<Atom> hptr; + Segment* s = hptr.get_protected(c_.head); + return tryDequeueUntilMC(s, deadline); + } + } + + /** tryDequeueUntilSC */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntilSC( + Segment* s, + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + Ticket t = consumerTicket(); + DCHECK_GE(t, s->minTicket()); + DCHECK_LT(t, (s->minTicket() + SegmentSize)); + size_t idx = index(t); + Entry& e = s->entry(idx); + if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) { + return folly::Optional<T>(); + } + setConsumerTicket(t + 1); + folly::Optional<T> ret = e.takeItem(); + if (responsibleForAdvance(t)) { + advanceHead(s); + } + return ret; + } + + /** tryDequeueUntilMC */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntilMC( + Segment* s, + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + while (true) { + Ticket t = consumerTicket(); + if (UNLIKELY(t >= (s->minTicket() + SegmentSize))) { + s = getAllocNextSegment(s, t); + DCHECK(s); + continue; + } + size_t idx = index(t); + Entry& e = s->entry(idx); + if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) { + return folly::Optional<T>(); + } + if (!c_.ticket.compare_exchange_weak( + t, t + 1, std::memory_order_acq_rel, std::memory_order_acquire)) { + continue; + } + folly::Optional<T> ret = e.takeItem(); + if (responsibleForAdvance(t)) { + advanceHead(s); + } + return ret; + } + } + + /** tryDequeueWaitElem */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool tryDequeueWaitElem( + Entry& e, + Ticket t, + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + if (LIKELY(e.tryWaitUntil(deadline))) { + return true; + } + return t < producerTicket(); + } + + /** tryPeekUntil */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE const T* tryPeekUntil( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + Segment* s = head(); + Ticket t = consumerTicket(); + DCHECK_GE(t, s->minTicket()); + DCHECK_LT(t, (s->minTicket() + SegmentSize)); + size_t idx = index(t); + Entry& e = s->entry(idx); + if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) { + return nullptr; + } + return e.peekItem(); + } + + /** findSegment */ + FOLLY_ALWAYS_INLINE + Segment* findSegment(Segment* s, const Ticket t) noexcept { + while (UNLIKELY(t >= (s->minTicket() + SegmentSize))) { + s = getAllocNextSegment(s, t); + DCHECK(s); + } + return s; + } + + /** getAllocNextSegment */ + Segment* getAllocNextSegment(Segment* s, Ticket t) noexcept { + Segment* next = s->nextSegment(); + if (!next) { + DCHECK_GE(t, s->minTicket() + SegmentSize); + auto diff = t - (s->minTicket() + SegmentSize); + if (diff > 0) { + auto dur = std::chrono::microseconds(diff); + auto deadline = std::chrono::steady_clock::now() + dur; + WaitOptions opt; + opt.spin_max(dur); + detail::spin_pause_until( + deadline, opt, [s] { return s->nextSegment(); }); + next = s->nextSegment(); + if (next) { + return next; + } + } + next = allocNextSegment(s); + } + DCHECK(next); + return next; + } + + /** allocNextSegment */ + Segment* allocNextSegment(Segment* s) { + auto t = s->minTicket() + SegmentSize; + Segment* next = new Segment(t); + next->set_cohort_no_tag(&c_.cohort); // defined in hazptr_obj + next->acquire_ref_safe(); // defined in hazptr_obj_base_linked + if (!s->casNextSegment(next)) { + delete next; + next = s->nextSegment(); + } + DCHECK(next); + return next; + } + + /** advanceTail */ + void advanceTail(Segment* s) noexcept { + if (SPSC) { + Segment* next = s->nextSegment(); + DCHECK(next); + setTail(next); + } else { + Ticket t = s->minTicket() + SegmentSize; + advanceTailToTicket(t); + } + } + + /** advanceTailToTicket */ + void advanceTailToTicket(Ticket t) noexcept { + Segment* s = tail(); + while (s->minTicket() < t) { + Segment* next = s->nextSegment(); + if (!next) { + next = allocNextSegment(s); + } + DCHECK(next); + casTail(s, next); + s = tail(); + } + } + + /** advanceHead */ + void advanceHead(Segment* s) noexcept { + if (SPSC) { + while (tail() == s) { + /* Wait for producer to advance tail. */ + asm_volatile_pause(); + } + Segment* next = s->nextSegment(); + DCHECK(next); + setHead(next); + reclaimSegment(s); + } else { + Ticket t = s->minTicket() + SegmentSize; + advanceHeadToTicket(t); + } + } + + /** advanceHeadToTicket */ + void advanceHeadToTicket(Ticket t) noexcept { + /* Tail must not lag behind head. Otherwise, the algorithm cannot + be certain about removal of segments. */ + advanceTailToTicket(t); + Segment* s = head(); + if (SingleConsumer) { + DCHECK_EQ(s->minTicket() + SegmentSize, t); + Segment* next = s->nextSegment(); + DCHECK(next); + setHead(next); + reclaimSegment(s); + } else { + while (s->minTicket() < t) { + Segment* next = s->nextSegment(); + DCHECK(next); + if (casHead(s, next)) { + reclaimSegment(s); + s = next; + } + } + } + } + + /** reclaimSegment */ + void reclaimSegment(Segment* s) noexcept { + if (SPSC) { + delete s; + } else { + s->retire(); // defined in hazptr_obj_base_linked + } + } + + /** cleanUpRemainingItems */ + void cleanUpRemainingItems() { + auto end = producerTicket(); + auto s = head(); + for (auto t = consumerTicket(); t < end; ++t) { + if (t >= s->minTicket() + SegmentSize) { + s = s->nextSegment(); + } + DCHECK_LT(t, (s->minTicket() + SegmentSize)); + auto idx = index(t); + auto& e = s->entry(idx); + e.destroyItem(); + } + } + + /** reclaimRemainingSegments */ + void reclaimRemainingSegments() { + auto h = head(); + auto s = h->nextSegment(); + h->setNextSegment(nullptr); + reclaimSegment(h); + while (s) { + auto next = s->nextSegment(); + delete s; + s = next; + } + } + + FOLLY_ALWAYS_INLINE size_t index(Ticket t) const noexcept { + return (t * Stride) & (SegmentSize - 1); + } + + FOLLY_ALWAYS_INLINE bool responsibleForAlloc(Ticket t) const noexcept { + return (t & (SegmentSize - 1)) == 0; + } + + FOLLY_ALWAYS_INLINE bool responsibleForAdvance(Ticket t) const noexcept { + return (t & (SegmentSize - 1)) == (SegmentSize - 1); + } + + FOLLY_ALWAYS_INLINE Segment* head() const noexcept { + return c_.head.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE Segment* tail() const noexcept { + return p_.tail.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE Ticket producerTicket() const noexcept { + return p_.ticket.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE Ticket consumerTicket() const noexcept { + return c_.ticket.load(std::memory_order_acquire); + } + + void setHead(Segment* s) noexcept { + DCHECK(SingleConsumer); + c_.head.store(s, std::memory_order_relaxed); + } + + void setTail(Segment* s) noexcept { + DCHECK(SPSC); + p_.tail.store(s, std::memory_order_release); + } + + bool casHead(Segment*& s, Segment* next) noexcept { + DCHECK(!SingleConsumer); + return c_.head.compare_exchange_strong( + s, next, std::memory_order_release, std::memory_order_acquire); + } + + void casTail(Segment*& s, Segment* next) noexcept { + DCHECK(!SPSC); + p_.tail.compare_exchange_strong( + s, next, std::memory_order_release, std::memory_order_relaxed); + } + + FOLLY_ALWAYS_INLINE void setProducerTicket(Ticket t) noexcept { + p_.ticket.store(t, std::memory_order_release); + } + + FOLLY_ALWAYS_INLINE void setConsumerTicket(Ticket t) noexcept { + c_.ticket.store(t, std::memory_order_release); + } + + FOLLY_ALWAYS_INLINE Ticket fetchIncrementConsumerTicket() noexcept { + if (SingleConsumer) { + Ticket oldval = consumerTicket(); + setConsumerTicket(oldval + 1); + return oldval; + } else { // MC + return c_.ticket.fetch_add(1, std::memory_order_acq_rel); + } + } + + FOLLY_ALWAYS_INLINE Ticket fetchIncrementProducerTicket() noexcept { + if (SingleProducer) { + Ticket oldval = producerTicket(); + setProducerTicket(oldval + 1); + return oldval; + } else { // MP + return p_.ticket.fetch_add(1, std::memory_order_acq_rel); + } + } + + /** + * Entry + */ + class Entry { + Sem flag_; + aligned_storage_for_t<T> item_; + + public: + template <typename Arg> + FOLLY_ALWAYS_INLINE void putItem(Arg&& arg) { + new (&item_) T(std::forward<Arg>(arg)); + flag_.post(); + } + + FOLLY_ALWAYS_INLINE T takeItem() noexcept { + flag_.wait(); + return getItem(); + } + + FOLLY_ALWAYS_INLINE const T* peekItem() noexcept { + flag_.wait(); + return itemPtr(); + } + + template <typename Clock, typename Duration> + FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool tryWaitUntil( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + // wait-options from benchmarks on contended queues: + static constexpr auto const opt = + Sem::wait_options().spin_max(std::chrono::microseconds(10)); + return flag_.try_wait_until(deadline, opt); + } + + FOLLY_ALWAYS_INLINE void destroyItem() noexcept { + itemPtr()->~T(); + } + + private: + FOLLY_ALWAYS_INLINE T getItem() noexcept { + T ret = std::move(*(itemPtr())); + destroyItem(); + return ret; + } + + FOLLY_ALWAYS_INLINE T* itemPtr() noexcept { + return static_cast<T*>(static_cast<void*>(&item_)); + } + }; // Entry + + /** + * Segment + */ + class Segment : public hazptr_obj_base_linked<Segment, Atom> { + Atom<Segment*> next_{nullptr}; + const Ticket min_; + alignas(Align) Entry b_[SegmentSize]; + + public: + explicit Segment(const Ticket t) noexcept : min_(t) {} + + Segment* nextSegment() const noexcept { + return next_.load(std::memory_order_acquire); + } + + void setNextSegment(Segment* next) { + next_.store(next, std::memory_order_relaxed); + } + + bool casNextSegment(Segment* next) noexcept { + Segment* expected = nullptr; + return next_.compare_exchange_strong( + expected, next, std::memory_order_release, std::memory_order_relaxed); + } + + FOLLY_ALWAYS_INLINE Ticket minTicket() const noexcept { + DCHECK_EQ((min_ & (SegmentSize - 1)), Ticket(0)); + return min_; + } + + FOLLY_ALWAYS_INLINE Entry& entry(size_t index) noexcept { + return b_[index]; + } + + template <typename S> + void push_links(bool m, S& s) { + if (m == false) { // next_ is immutable + auto p = nextSegment(); + if (p) { + s.push(p); + } + } + } + }; // Segment + +}; // UnboundedQueue + +/* Aliases */ + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using USPSCQueue = + UnboundedQueue<T, true, true, MayBlock, LgSegmentSize, LgAlign, Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using UMPSCQueue = + UnboundedQueue<T, false, true, MayBlock, LgSegmentSize, LgAlign, Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using USPMCQueue = + UnboundedQueue<T, true, false, MayBlock, LgSegmentSize, LgAlign, Atom>; + +template < + typename T, + bool MayBlock, + size_t LgSegmentSize = 8, + size_t LgAlign = constexpr_log2(hardware_destructive_interference_size), + template <typename> class Atom = std::atomic> +using UMPMCQueue = + UnboundedQueue<T, false, false, MayBlock, LgSegmentSize, LgAlign, Atom>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Access.h b/ios/Pods/Flipper-Folly/folly/container/Access.h new file mode 100644 index 000000000..f830822dc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Access.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <initializer_list> +#include <iterator> + +/** + * include or backport: + * * std::size + * * std::empty + * * std::data + */ + +#if __cpp_lib_nonmember_container_access >= 201411 || _MSC_VER + +namespace folly { + +/* using override */ using std::data; +/* using override */ using std::empty; +/* using override */ using std::size; + +} // namespace folly + +#else + +namespace folly { + +// mimic: std::size, C++17 +template <typename C> +constexpr auto size(C const& c) -> decltype(c.size()) { + return c.size(); +} +template <typename T, std::size_t N> +constexpr std::size_t size(T const (&)[N]) noexcept { + return N; +} + +// mimic: std::empty, C++17 +template <typename C> +constexpr auto empty(C const& c) -> decltype(c.empty()) { + return c.empty(); +} +template <typename T, std::size_t N> +constexpr bool empty(T const (&)[N]) noexcept { + // while zero-length arrays are not allowed in the language, some compilers + // may permit them in some cases + return N == 0; +} +template <typename E> +constexpr bool empty(std::initializer_list<E> il) noexcept { + return il.size() == 0; +} + +// mimic: std::data, C++17 +template <typename C> +constexpr auto data(C& c) -> decltype(c.data()) { + return c.data(); +} +template <typename C> +constexpr auto data(C const& c) -> decltype(c.data()) { + return c.data(); +} +template <typename T, std::size_t N> +constexpr T* data(T (&a)[N]) noexcept { + return a; +} +template <typename E> +constexpr E const* data(std::initializer_list<E> il) noexcept { + return il.begin(); +} + +} // namespace folly + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/container/Array.h b/ios/Pods/Flipper-Folly/folly/container/Array.h new file mode 100644 index 000000000..ed0ce81d0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Array.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <array> +#include <type_traits> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> + +namespace folly { + +namespace array_detail { +template <class T> +using is_ref_wrapper = detail::is_instantiation_of<std::reference_wrapper, T>; + +template <typename T> +using not_ref_wrapper = + folly::Negation<is_ref_wrapper<typename std::decay<T>::type>>; + +template <typename D, typename...> +struct return_type_helper { + using type = D; +}; +template <typename... TList> +struct return_type_helper<void, TList...> { + static_assert( + folly::Conjunction<not_ref_wrapper<TList>...>::value, + "TList cannot contain reference_wrappers when D is void"); + using type = typename std::common_type<TList...>::type; +}; + +template <typename D, typename... TList> +using return_type = std:: + array<typename return_type_helper<D, TList...>::type, sizeof...(TList)>; +} // namespace array_detail + +template <typename D = void, typename... TList> +constexpr array_detail::return_type<D, TList...> make_array(TList&&... t) { + using value_type = + typename array_detail::return_type_helper<D, TList...>::type; + return {{static_cast<value_type>(std::forward<TList>(t))...}}; +} + +namespace array_detail { +template <typename MakeItem, std::size_t... Index> +FOLLY_ERASE constexpr auto make_array_with_( + MakeItem const& make, + std::index_sequence<Index...>) { + return std::array<decltype(make(0)), sizeof...(Index)>{{make(Index)...}}; +} +} // namespace array_detail + +// make_array_with +// +// Constructs a std::array<..., Size> with elements m(i) for i in [0, Size). +template <std::size_t Size, typename MakeItem> +constexpr auto make_array_with(MakeItem const& make) { + return array_detail::make_array_with_(make, std::make_index_sequence<Size>{}); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/BitIterator.h b/ios/Pods/Flipper-Folly/folly/container/BitIterator.h new file mode 100644 index 000000000..cb5b7cca3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/BitIterator.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BitIterator + * Wrapper around an iterator over an integral type that iterates + * over its underlying bits in MSb to LSb order + * + * findFirstSet(BitIterator begin, BitIterator end) + * return a BitIterator pointing to the first 1 bit in [begin, end), or + * end if all bits in [begin, end) are 0 + * + * @author Tudor Bosman (tudorb@fb.com) + */ + +#pragma once + +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstring> +#include <iterator> +#include <limits> +#include <type_traits> + +#include <boost/iterator/iterator_adaptor.hpp> + +#include <folly/Portability.h> +#include <folly/container/detail/BitIteratorDetail.h> +#include <folly/lang/Bits.h> + +namespace folly { + +/** + * Fast bit iteration facility. + */ + +template <class BaseIter> +class BitIterator; +template <class BaseIter> +BitIterator<BaseIter> findFirstSet( + BitIterator<BaseIter>, + BitIterator<BaseIter>); +/** + * Wrapper around an iterator over an integer type that iterates + * over its underlying bits in LSb to MSb order. + * + * BitIterator models the same iterator concepts as the base iterator. + */ +template <class BaseIter> +class BitIterator : public bititerator_detail::BitIteratorBase<BaseIter>::type { + public: + /** + * Return the number of bits in an element of the underlying iterator. + */ + static unsigned int bitsPerBlock() { + return std::numeric_limits<typename std::make_unsigned< + typename std::iterator_traits<BaseIter>::value_type>::type>::digits; + } + + /** + * Construct a BitIterator that points at a given bit offset (default 0) + * in iter. + */ + explicit BitIterator(const BaseIter& iter, size_t bitOff = 0) + : bititerator_detail::BitIteratorBase<BaseIter>::type(iter), + bitOffset_(bitOff) { + assert(bitOffset_ < bitsPerBlock()); + } + + size_t bitOffset() const { + return bitOffset_; + } + + void advanceToNextBlock() { + bitOffset_ = 0; + ++this->base_reference(); + } + + BitIterator& operator=(const BaseIter& other) { + this->~BitIterator(); + new (this) BitIterator(other); + return *this; + } + + private: + friend class boost::iterator_core_access; + friend BitIterator findFirstSet<>(BitIterator, BitIterator); + + typedef bititerator_detail::BitReference< + typename std::iterator_traits<BaseIter>::reference, + typename std::iterator_traits<BaseIter>::value_type> + BitRef; + + void advanceInBlock(size_t n) { + bitOffset_ += n; + assert(bitOffset_ < bitsPerBlock()); + } + + BitRef dereference() const { + return BitRef(*this->base_reference(), bitOffset_); + } + + void advance(ssize_t n) { + size_t bpb = bitsPerBlock(); + ssize_t blocks = n / ssize_t(bpb); + bitOffset_ += n % bpb; + if (bitOffset_ >= bpb) { + bitOffset_ -= bpb; + ++blocks; + } + this->base_reference() += blocks; + } + + void increment() { + if (++bitOffset_ == bitsPerBlock()) { + advanceToNextBlock(); + } + } + + void decrement() { + if (bitOffset_-- == 0) { + bitOffset_ = bitsPerBlock() - 1; + --this->base_reference(); + } + } + + bool equal(const BitIterator& other) const { + return ( + bitOffset_ == other.bitOffset_ && + this->base_reference() == other.base_reference()); + } + + ssize_t distance_to(const BitIterator& other) const { + return ssize_t( + (other.base_reference() - this->base_reference()) * bitsPerBlock() + + other.bitOffset_ - bitOffset_); + } + + size_t bitOffset_; +}; + +/** + * Helper function, so you can write + * auto bi = makeBitIterator(container.begin()); + */ +template <class BaseIter> +BitIterator<BaseIter> makeBitIterator(const BaseIter& iter) { + return BitIterator<BaseIter>(iter); +} + +/** + * Find first bit set in a range of bit iterators. + * 4.5x faster than the obvious std::find(begin, end, true); + */ +template <class BaseIter> +BitIterator<BaseIter> findFirstSet( + BitIterator<BaseIter> begin, + BitIterator<BaseIter> end) { + // shortcut to avoid ugly static_cast<> + static const typename std::iterator_traits<BaseIter>::value_type one = 1; + + while (begin.base() != end.base()) { + typename std::iterator_traits<BaseIter>::value_type v = *begin.base(); + // mask out the bits that don't matter (< begin.bitOffset) + v &= ~((one << begin.bitOffset()) - 1); + size_t firstSet = findFirstSet(v); + if (firstSet) { + --firstSet; // now it's 0-based + assert(firstSet >= begin.bitOffset()); + begin.advanceInBlock(firstSet - begin.bitOffset()); + return begin; + } + begin.advanceToNextBlock(); + } + + // now begin points to the same block as end + if (end.bitOffset() != 0) { // assume end is dereferenceable + typename std::iterator_traits<BaseIter>::value_type v = *begin.base(); + // mask out the bits that don't matter (< begin.bitOffset) + v &= ~((one << begin.bitOffset()) - 1); + // mask out the bits that don't matter (>= end.bitOffset) + v &= (one << end.bitOffset()) - 1; + size_t firstSet = findFirstSet(v); + if (firstSet) { + --firstSet; // now it's 0-based + assert(firstSet >= begin.bitOffset()); + begin.advanceInBlock(firstSet - begin.bitOffset()); + return begin; + } + } + + return end; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Enumerate.h b/ios/Pods/Flipper-Folly/folly/container/Enumerate.h new file mode 100644 index 000000000..55f2d263d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Enumerate.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <iterator> +#include <memory> + +#include <folly/CPortability.h> +#include <folly/portability/SysTypes.h> + +/** + * Similar to Python's enumerate(), folly::enumerate() can be used to + * iterate a range with a for-range loop, and it also allows to + * retrieve the count of iterations so far. + * + * For example: + * + * for (auto&& it : folly::enumerate(vec)) { + * // *it is a reference to the current element. Const if vec is const. + * // it->member can be used as well. + * // it.index contains the iteration count. + * } + * + * If the iteration variable is const, the reference is too. + * + * for (const auto&& it : folly::enumerate(vec)) { + * // *it is always a const reference. + * } + * + * @author Giuseppe Ottaviano <ott@fb.com> + */ + +namespace folly { + +namespace detail { + +template <class T> +struct MakeConst { + using type = const T; +}; +template <class T> +struct MakeConst<T&> { + using type = const T&; +}; +template <class T> +struct MakeConst<T*> { + using type = const T*; +}; + +// Raw pointers don't have an operator->() member function, so the +// second overload will be SFINAEd out in that case. Otherwise, the +// second is preferred in the partial order for getPointer(_, 0). +template <class Iterator> +FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, long) + -> decltype(std::addressof(*it)) { + return std::addressof(*it); +} +template <class Iterator> +FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, int) + -> decltype(it.operator->()) { + return it.operator->(); +} + +template <class Iterator> +class Enumerator { + public: + explicit Enumerator(Iterator it) : it_(std::move(it)) {} + + class Proxy { + public: + using difference_type = ssize_t; + using value_type = typename std::iterator_traits<Iterator>::value_type; + using reference = typename std::iterator_traits<Iterator>::reference; + using pointer = typename std::iterator_traits<Iterator>::pointer; + using iterator_category = std::input_iterator_tag; + + FOLLY_ALWAYS_INLINE explicit Proxy(const Enumerator& e) + : it_(e.it_), index(e.idx_) {} + + // Non-const Proxy: Forward constness from Iterator. + FOLLY_ALWAYS_INLINE reference operator*() { + return *it_; + } + FOLLY_ALWAYS_INLINE pointer operator->() { + return getPointer(it_, 0); + } + + // Const Proxy: Force const references. + FOLLY_ALWAYS_INLINE typename MakeConst<reference>::type operator*() const { + return *it_; + } + FOLLY_ALWAYS_INLINE typename MakeConst<pointer>::type operator->() const { + return getPointer(it_, 0); + } + + private: + const Iterator& it_; + + public: + const size_t index; + }; + + FOLLY_ALWAYS_INLINE Proxy operator*() const { + return Proxy(*this); + } + + FOLLY_ALWAYS_INLINE Enumerator& operator++() { + ++it_; + ++idx_; + return *this; + } + + template <typename OtherIterator> + FOLLY_ALWAYS_INLINE bool operator==( + const Enumerator<OtherIterator>& rhs) const { + return it_ == rhs.it_; + } + + template <typename OtherIterator> + FOLLY_ALWAYS_INLINE bool operator!=( + const Enumerator<OtherIterator>& rhs) const { + return !(it_ == rhs.it_); + } + + private: + template <typename OtherIterator> + friend class Enumerator; + + Iterator it_; + size_t idx_ = 0; +}; + +template <class Range> +class RangeEnumerator { + Range r_; + using BeginIteratorType = decltype(std::declval<Range>().begin()); + using EndIteratorType = decltype(std::declval<Range>().end()); + + public: + explicit RangeEnumerator(Range&& r) : r_(std::forward<Range>(r)) {} + + Enumerator<BeginIteratorType> begin() { + return Enumerator<BeginIteratorType>(r_.begin()); + } + Enumerator<EndIteratorType> end() { + return Enumerator<EndIteratorType>(r_.end()); + } +}; + +} // namespace detail + +template <class Range> +detail::RangeEnumerator<Range> enumerate(Range&& r) { + return detail::RangeEnumerator<Range>(std::forward<Range>(r)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/EvictingCacheMap.h b/ios/Pods/Flipper-Folly/folly/container/EvictingCacheMap.h new file mode 100644 index 000000000..7d5f85a7a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/EvictingCacheMap.h @@ -0,0 +1,565 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <exception> +#include <functional> + +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/unordered_set.hpp> +#include <boost/iterator/iterator_adaptor.hpp> +#include <boost/utility.hpp> + +#include <folly/lang/Exception.h> + +namespace folly { + +/** + * A general purpose LRU evicting cache. Designed to support constant time + * set/get operations. It maintains a doubly linked list of items that are + * threaded through an index (a hash map). The access ordered is maintained + * on the list by moving an element to the front of list on a get. New elements + * are added to the front of the list. The index size is set to half the + * capacity (setting capacity to 0 is a special case. see notes at the end of + * this section). So assuming uniform distribution of keys, set/get are both + * constant time operations. + * + * On reaching capacity limit, clearSize_ LRU items are evicted at a time. If + * a callback is specified with setPruneHook, it is invoked for each eviction. + * + * This is NOT a thread-safe implementation. + * + * Configurability: capacity of the cache, number of items to evict, eviction + * callback and the hasher to hash the keys can all be supplied by the caller. + * + * If at a given state, N1 - N6 are the nodes in MRU to LRU order and hashing + * to index keys as {(N1,N5)->H1, (N4,N2,N6)->H2, N3->Hi}, the datastructure + * layout is as below. N1 .. N6 is a list threaded through the hash. + * Assuming, each the number of nodes hashed to each index key is bounded, the + * following operations run in constant time. + * i) get computes the index key, walks the list of elements hashed to + * the key and moves it to the front of the list, if found. + * ii) set inserts a new node into the list and places the same node on to the + * list of elements hashing to the corresponding index key. + * ii) prune deletes nodes from the end of the list as well from the index. + * + * +----+ +----+ +----+ + * | H1 | <-> | N1 | <-> | N5 | + * +----+ +----+ +----+ + * ^ ^ ^ + * | ___/ \ + * | / \ + * |_ /________ \___ + * / | \ + * / | \ + * v v v + * +----+ +----+ +----+ +----+ + * | H2 | <-> | N4 | <-> | N2 | <-> | N6 | + * +----+ +----+ +----+ +----+ + * . ^ ^ + * . | | + * . | | + * . | _____| + * . | / + * v v + * +----+ +----+ + * | Hi | <-> | N3 | + * +----+ +----+ + * + * N.B 1 : Changing the capacity with setMaxSize does not change the index size + * and it could end up in too many elements indexed to the same slot in index. + * The set/get performance will get worse in this case. So it is best to avoid + * resizing. + * + * N.B 2 : Setting capacity to 0, using setMaxSize or initialization, turns off + * evictions based on sizeof the cache making it an INFINITE size cache + * unless evictions of LRU items are triggered by calling prune() by clients + * (using their own eviction criteria). + */ +template < + class TKey, + class TValue, + class THash = std::hash<TKey>, + class TKeyEqual = std::equal_to<TKey>> +class EvictingCacheMap { + private: + // typedefs for brevity + struct Node; + struct KeyHasher; + struct KeyValueEqual; + typedef boost::intrusive::link_mode<boost::intrusive::safe_link> link_mode; + typedef boost::intrusive::unordered_set< + Node, + boost::intrusive::hash<KeyHasher>, + boost::intrusive::equal<KeyValueEqual>> + NodeMap; + typedef boost::intrusive::list<Node> NodeList; + typedef std::pair<const TKey, TValue> TPair; + + public: + typedef std::function<void(TKey, TValue&&)> PruneHookCall; + + // iterator base : returns TPair on dereference + template <typename Value, typename TIterator> + class iterator_base : public boost::iterator_adaptor< + iterator_base<Value, TIterator>, + TIterator, + Value, + boost::bidirectional_traversal_tag> { + public: + iterator_base() {} + + explicit iterator_base(TIterator it) + : iterator_base::iterator_adaptor_(it) {} + + template < + typename V, + typename I, + std::enable_if_t< + std::is_same<V const, Value>::value && + std::is_convertible<I, TIterator>::value, + int> = 0> + /* implicit */ iterator_base(iterator_base<V, I> const& other) + : iterator_base::iterator_adaptor_(other.base()) {} + + Value& dereference() const { + return this->base_reference()->pr; + } + }; + + // iterators + typedef iterator_base<TPair, typename NodeList::iterator> iterator; + typedef iterator_base<const TPair, typename NodeList::const_iterator> + const_iterator; + typedef iterator_base<TPair, typename NodeList::reverse_iterator> + reverse_iterator; + typedef iterator_base<const TPair, typename NodeList::const_reverse_iterator> + const_reverse_iterator; + + // the default map typedefs + using key_type = TKey; + using mapped_type = TValue; + using hasher = THash; + + /** + * Construct a EvictingCacheMap + * @param maxSize maximum size of the cache map. Once the map size exceeds + * maxSize, the map will begin to evict. + * @param clearSize the number of elements to clear at a time when the + * eviction size is reached. + */ + explicit EvictingCacheMap( + std::size_t maxSize, + std::size_t clearSize = 1, + const THash& keyHash = THash(), + const TKeyEqual& keyEqual = TKeyEqual()) + : nIndexBuckets_(std::max(maxSize / 2, std::size_t(kMinNumIndexBuckets))), + indexBuckets_(new typename NodeMap::bucket_type[nIndexBuckets_]), + indexTraits_(indexBuckets_.get(), nIndexBuckets_), + keyHash_(keyHash), + keyEqual_(keyEqual), + index_(indexTraits_, keyHash_, keyEqual_), + maxSize_(maxSize), + clearSize_(clearSize) {} + + EvictingCacheMap(const EvictingCacheMap&) = delete; + EvictingCacheMap& operator=(const EvictingCacheMap&) = delete; + EvictingCacheMap(EvictingCacheMap&&) = default; + EvictingCacheMap& operator=(EvictingCacheMap&&) = default; + + ~EvictingCacheMap() { + setPruneHook(nullptr); + // ignore any potential exceptions from pruneHook_ + pruneWithFailSafeOption(size(), nullptr, true); + } + + /** + * Adjust the max size of EvictingCacheMap. Note that this does not update + * nIndexBuckets_ accordingly. This API can cause performance to get very + * bad, e.g., the nIndexBuckets_ is still 100 after maxSize is updated to 1M. + * + * Calling this function with an arugment of 0 removes the limit on the cache + * size and elements are not evicted unless clients explicitly call prune. + * + * If you intend to resize dynamically using this, then picking an index size + * that works well and initializing with corresponding maxSize is the only + * reasonable option. + * + * @param maxSize new maximum size of the cache map. + * @param pruneHook callback to use on eviction. + */ + void setMaxSize(size_t maxSize, PruneHookCall pruneHook = nullptr) { + if (maxSize != 0 && maxSize < size()) { + // Prune the excess elements with our new constraints. + prune(std::max(size() - maxSize, clearSize_), pruneHook); + } + maxSize_ = maxSize; + } + + size_t getMaxSize() const { + return maxSize_; + } + + void setClearSize(size_t clearSize) { + clearSize_ = clearSize; + } + + /** + * Check for existence of a specific key in the map. This operation has + * no effect on LRU order. + * @param key key to search for + * @return true if exists, false otherwise + */ + bool exists(const TKey& key) const { + return findInIndex(key) != index_.end(); + } + + /** + * Get the value associated with a specific key. This function always + * promotes a found value to the head of the LRU. + * @param key key associated with the value + * @return the value if it exists + * @throw std::out_of_range exception of the key does not exist + */ + TValue& get(const TKey& key) { + auto it = find(key); + if (it == end()) { + throw_exception<std::out_of_range>("Key does not exist"); + } + return it->second; + } + + /** + * Get the iterator associated with a specific key. This function always + * promotes a found value to the head of the LRU. + * @param key key to associate with value + * @return the iterator of the object (a std::pair of const TKey, TValue) or + * end() if it does not exist + */ + iterator find(const TKey& key) { + auto it = findInIndex(key); + if (it == index_.end()) { + return end(); + } + lru_.erase(lru_.iterator_to(*it)); + lru_.push_front(*it); + return iterator(lru_.iterator_to(*it)); + } + + /** + * Get the value associated with a specific key. This function never + * promotes a found value to the head of the LRU. + * @param key key associated with the value + * @return the value if it exists + * @throw std::out_of_range exception of the key does not exist + */ + const TValue& getWithoutPromotion(const TKey& key) const { + auto it = findWithoutPromotion(key); + if (it == end()) { + throw_exception<std::out_of_range>("Key does not exist"); + } + return it->second; + } + + TValue& getWithoutPromotion(const TKey& key) { + auto const& cThis = *this; + return const_cast<TValue&>(cThis.getWithoutPromotion(key)); + } + + /** + * Get the iterator associated with a specific key. This function never + * promotes a found value to the head of the LRU. + * @param key key to associate with value + * @return the iterator of the object (a std::pair of const TKey, TValue) or + * end() if it does not exist + */ + const_iterator findWithoutPromotion(const TKey& key) const { + auto it = findInIndex(key); + return (it == index_.end()) ? end() : const_iterator(lru_.iterator_to(*it)); + } + + iterator findWithoutPromotion(const TKey& key) { + auto it = findInIndex(key); + return (it == index_.end()) ? end() : iterator(lru_.iterator_to(*it)); + } + + /** + * Erase the key-value pair associated with key if it exists. + * @param key key associated with the value + * @return true if the key existed and was erased, else false + */ + bool erase(const TKey& key) { + auto it = findInIndex(key); + if (it != index_.end()) { + erase(const_iterator(lru_.iterator_to(*it))); + return true; + } + return false; + } + + /** + * Erase the key-value pair associated with pos + * @param pos iterator to the element to be erased + * @return iterator to the following element or end() if pos was the last + * element + */ + iterator erase(const_iterator pos) { + auto* node = const_cast<Node*>(&(*pos.base())); + std::unique_ptr<Node> nptr(node); + index_.erase(index_.iterator_to(*node)); + return iterator(lru_.erase(pos.base())); + } + + /** + * Set a key-value pair in the dictionary + * @param key key to associate with value + * @param value value to associate with the key + * @param promote boolean flag indicating whether or not to move something + * to the front of an LRU. This only really matters if you're setting + * a value that already exists. + * @param pruneHook callback to use on eviction (if it occurs). + */ + void set( + const TKey& key, + TValue value, + bool promote = true, + PruneHookCall pruneHook = nullptr) { + auto it = findInIndex(key); + if (it != index_.end()) { + it->pr.second = std::move(value); + if (promote) { + lru_.erase(lru_.iterator_to(*it)); + lru_.push_front(*it); + } + } else { + auto node = new Node(key, std::move(value)); + index_.insert(*node); + lru_.push_front(*node); + + // no evictions if maxSize_ is 0 i.e. unlimited capacity + if (maxSize_ > 0 && size() > maxSize_) { + prune(clearSize_, pruneHook); + } + } + } + + /** + * Insert a new key-value pair in the dictionary if no element exists for key + * @param key key to associate with value + * @param value value to associate with the key + * @param pruneHook callback to use on eviction (if it occurs). + * @return a pair consisting of an iterator to the inserted element (or to the + * element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair<iterator, bool> + insert(const TKey& key, TValue value, PruneHookCall pruneHook = nullptr) { + auto node = std::make_unique<Node>(key, std::move(value)); + auto pair = index_.insert(*node); + if (pair.second) { + lru_.push_front(*node); + node.release(); + + // no evictions if maxSize_ is 0 i.e. unlimited capacity + if (maxSize_ > 0 && size() > maxSize_) { + prune(clearSize_, pruneHook); + } + } + return std::make_pair(iterator(lru_.iterator_to(*pair.first)), pair.second); + } + + /** + * Get the number of elements in the dictionary + * @return the size of the dictionary + */ + std::size_t size() const { + return index_.size(); + } + + /** + * Typical empty function + * @return true if empty, false otherwise + */ + bool empty() const { + return index_.empty(); + } + + void clear(PruneHookCall pruneHook = nullptr) { + prune(size(), pruneHook); + } + + /** + * Set the prune hook, which is the function invoked on the key and value + * on each eviction. Will throw If the pruneHook throws, unless the + * EvictingCacheMap object is being destroyed in which case it will + * be ignored. + * @param pruneHook new callback to use on eviction. + * @param promote boolean flag indicating whether or not to move something + * to the front of an LRU. + * @return the iterator of the object (a std::pair of const TKey, TValue) or + * end() if it does not exist + */ + void setPruneHook(PruneHookCall pruneHook) { + pruneHook_ = pruneHook; + } + + /** + * Prune the minimum of pruneSize and size() from the back of the LRU. + * Will throw if pruneHook throws. + * @param pruneSize minimum number of elements to prune + * @param pruneHook a custom pruneHook function + */ + void prune(std::size_t pruneSize, PruneHookCall pruneHook = nullptr) { + // do not swallow exceptions for prunes not triggered from destructor + pruneWithFailSafeOption(pruneSize, pruneHook, false); + } + + // Iterators and such + iterator begin() { + return iterator(lru_.begin()); + } + iterator end() { + return iterator(lru_.end()); + } + const_iterator begin() const { + return const_iterator(lru_.begin()); + } + const_iterator end() const { + return const_iterator(lru_.end()); + } + + const_iterator cbegin() const { + return const_iterator(lru_.cbegin()); + } + const_iterator cend() const { + return const_iterator(lru_.cend()); + } + + reverse_iterator rbegin() { + return reverse_iterator(lru_.rbegin()); + } + reverse_iterator rend() { + return reverse_iterator(lru_.rend()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(lru_.rbegin()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(lru_.rend()); + } + + const_reverse_iterator crbegin() const { + return const_reverse_iterator(lru_.crbegin()); + } + const_reverse_iterator crend() const { + return const_reverse_iterator(lru_.crend()); + } + + private: + struct Node : public boost::intrusive::unordered_set_base_hook<link_mode>, + public boost::intrusive::list_base_hook<link_mode> { + Node(const TKey& key, TValue&& value) + : pr(std::make_pair(key, std::move(value))) {} + TPair pr; + }; + + struct KeyHasher { + KeyHasher(const THash& keyHash) : hash(keyHash) {} + std::size_t operator()(const Node& node) const { + return hash(node.pr.first); + } + std::size_t operator()(const TKey& key) const { + return hash(key); + } + THash hash; + }; + + struct KeyValueEqual { + KeyValueEqual(const TKeyEqual& keyEqual) : equal(keyEqual) {} + bool operator()(const TKey& lhs, const Node& rhs) const { + return equal(lhs, rhs.pr.first); + } + bool operator()(const Node& lhs, const TKey& rhs) const { + return equal(lhs.pr.first, rhs); + } + bool operator()(const Node& lhs, const Node& rhs) const { + return equal(lhs.pr.first, rhs.pr.first); + } + TKeyEqual equal; + }; + + /** + * Get the iterator in in the index associated with a specific key. This is + * merely a search in the index and does not promote the object. + * @param key key to associate with value + * @return the NodeMap::iterator to the Node containing the object + * (a std::pair of const TKey, TValue) or index_.end() if it does not exist + */ + typename NodeMap::iterator findInIndex(const TKey& key) { + return index_.find(key, KeyHasher(keyHash_), KeyValueEqual(keyEqual_)); + } + + typename NodeMap::const_iterator findInIndex(const TKey& key) const { + return index_.find(key, KeyHasher(keyHash_), KeyValueEqual(keyEqual_)); + } + + /** + * Prune the minimum of pruneSize and size() from the back of the LRU. + * @param pruneSize minimum number of elements to prune + * @param pruneHook a custom pruneHook function + * @param failSafe true if exceptions are to ignored, false by default + */ + void pruneWithFailSafeOption( + std::size_t pruneSize, + PruneHookCall pruneHook, + bool failSafe) { + auto& ph = (nullptr == pruneHook) ? pruneHook_ : pruneHook; + + for (std::size_t i = 0; i < pruneSize && !lru_.empty(); i++) { + auto* node = &(*lru_.rbegin()); + std::unique_ptr<Node> nptr(node); + + lru_.erase(lru_.iterator_to(*node)); + index_.erase(index_.iterator_to(*node)); + if (ph) { + try { + ph(node->pr.first, std::move(node->pr.second)); + } catch (...) { + if (!failSafe) { + throw; + } + } + } + } + } + + static const std::size_t kMinNumIndexBuckets = 100; + PruneHookCall pruneHook_; + std::size_t nIndexBuckets_; + std::unique_ptr<typename NodeMap::bucket_type[]> indexBuckets_; + typename NodeMap::bucket_traits indexTraits_; + THash keyHash_; + TKeyEqual keyEqual_; + NodeMap index_; + NodeList lru_; + std::size_t maxSize_; + std::size_t clearSize_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/F14Map-fwd.h b/ios/Pods/Flipper-Folly/folly/container/F14Map-fwd.h new file mode 100644 index 000000000..c76906cf6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/F14Map-fwd.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <utility> + +#include <folly/container/detail/F14Defaults.h> +#include <folly/memory/MemoryResource.h> + +namespace folly { +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>> +class F14NodeMap; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>> +class F14ValueMap; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>> +class F14VectorMap; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>> +class F14FastMap; + +#if FOLLY_HAS_MEMORY_RESOURCE +namespace pmr { +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14NodeMap = folly::F14NodeMap< + Key, + Mapped, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator< + std::pair<Key const, Mapped>>>; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14ValueMap = folly::F14ValueMap< + Key, + Mapped, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator< + std::pair<Key const, Mapped>>>; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14VectorMap = folly::F14VectorMap< + Key, + Mapped, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator< + std::pair<Key const, Mapped>>>; + +template < + typename Key, + typename Mapped, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14FastMap = folly::F14FastMap< + Key, + Mapped, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator< + std::pair<Key const, Mapped>>>; +} // namespace pmr +#endif // FOLLY_HAS_MEMORY_RESOURCE + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/F14Map.h b/ios/Pods/Flipper-Folly/folly/container/F14Map.h new file mode 100644 index 000000000..89c647682 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/F14Map.h @@ -0,0 +1,1447 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/** + * F14NodeMap, F14ValueMap, and F14VectorMap + * + * F14FastMap conditionally works like F14ValueMap or F14VectorMap + * + * See F14.md + * + * @author Nathan Bronson <ngbronson@fb.com> + * @author Xiao Shi <xshi@fb.com> + */ + +#include <cstddef> +#include <initializer_list> +#include <stdexcept> +#include <tuple> + +#include <folly/Range.h> +#include <folly/Traits.h> +#include <folly/lang/Exception.h> +#include <folly/lang/SafeAssert.h> + +#include <folly/container/F14Map-fwd.h> +#include <folly/container/detail/F14Policy.h> +#include <folly/container/detail/F14Table.h> +#include <folly/container/detail/Util.h> + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +//////// Common case for supported platforms + +namespace folly { +namespace f14 { +namespace detail { + +template <typename Policy> +class F14BasicMap { + template <typename K, typename T> + using EnableHeterogeneousFind = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Key, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value, + T>; + + template <typename K, typename T> + using EnableHeterogeneousInsert = std::enable_if_t< + EligibleForHeterogeneousInsert< + typename Policy::Key, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value, + T>; + + template <typename K, typename T> + using EnableHeterogeneousErase = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value && + !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value && + !std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>::value, + T>; + + public: + //// PUBLIC - Member types + + using key_type = typename Policy::Key; + using mapped_type = typename Policy::Mapped; + using value_type = typename Policy::Value; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = typename Policy::Hasher; + using key_equal = typename Policy::KeyEqual; + using allocator_type = typename Policy::Alloc; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = typename Policy::AllocTraits::pointer; + using const_pointer = typename Policy::AllocTraits::const_pointer; + using iterator = typename Policy::Iter; + using const_iterator = typename Policy::ConstIter; + + private: + using ItemIter = typename Policy::ItemIter; + + public: + //// PUBLIC - Member functions + + F14BasicMap() noexcept(Policy::kDefaultConstructIsNoexcept) + : F14BasicMap(0) {} + + explicit F14BasicMap( + std::size_t initialCapacity, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} {} + + explicit F14BasicMap(std::size_t initialCapacity, allocator_type const& alloc) + : F14BasicMap(initialCapacity, hasher{}, key_equal{}, alloc) {} + + explicit F14BasicMap( + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : F14BasicMap(initialCapacity, hash, key_equal{}, alloc) {} + + explicit F14BasicMap(allocator_type const& alloc) + : F14BasicMap(0, hasher{}, key_equal{}, alloc) {} + + template <typename InputIt> + F14BasicMap( + InputIt first, + InputIt last, + std::size_t initialCapacity = 0, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} { + initialInsert(first, last, initialCapacity); + } + + template <typename InputIt> + F14BasicMap( + InputIt first, + InputIt last, + std::size_t initialCapacity, + allocator_type const& alloc) + : table_{initialCapacity, hasher{}, key_equal{}, alloc} { + initialInsert(first, last, initialCapacity); + } + + template <typename InputIt> + F14BasicMap( + InputIt first, + InputIt last, + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : table_{initialCapacity, hash, key_equal{}, alloc} { + initialInsert(first, last, initialCapacity); + } + + F14BasicMap(F14BasicMap const& rhs) = default; + + F14BasicMap(F14BasicMap const& rhs, allocator_type const& alloc) + : table_{rhs.table_, alloc} {} + + F14BasicMap(F14BasicMap&& rhs) = default; + + F14BasicMap(F14BasicMap&& rhs, allocator_type const& alloc) noexcept( + Policy::kAllocIsAlwaysEqual) + : table_{std::move(rhs.table_), alloc} {} + + F14BasicMap( + std::initializer_list<value_type> init, + std::size_t initialCapacity = 0, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicMap( + std::initializer_list<value_type> init, + std::size_t initialCapacity, + allocator_type const& alloc) + : table_{initialCapacity, hasher{}, key_equal{}, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicMap( + std::initializer_list<value_type> init, + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : table_{initialCapacity, hash, key_equal{}, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicMap& operator=(F14BasicMap const&) = default; + + F14BasicMap& operator=(F14BasicMap&&) = default; + + F14BasicMap& operator=(std::initializer_list<value_type> ilist) { + clear(); + bulkInsert(ilist.begin(), ilist.end(), false); + return *this; + } + + allocator_type get_allocator() const noexcept { + return table_.alloc(); + } + + //// PUBLIC - Iterators + + iterator begin() noexcept { + return table_.makeIter(table_.begin()); + } + const_iterator begin() const noexcept { + return cbegin(); + } + const_iterator cbegin() const noexcept { + return table_.makeConstIter(table_.begin()); + } + + iterator end() noexcept { + return table_.makeIter(table_.end()); + } + const_iterator end() const noexcept { + return cend(); + } + const_iterator cend() const noexcept { + return table_.makeConstIter(table_.end()); + } + + //// PUBLIC - Capacity + + bool empty() const noexcept { + return table_.empty(); + } + + std::size_t size() const noexcept { + return table_.size(); + } + + std::size_t max_size() const noexcept { + return table_.max_size(); + } + + //// PUBLIC - Modifiers + + void clear() noexcept { + table_.clear(); + } + + std::pair<iterator, bool> insert(value_type const& value) { + return emplace(value); + } + + template <typename P> + std::enable_if_t< + std::is_constructible<value_type, P&&>::value, + std::pair<iterator, bool>> + insert(P&& value) { + return emplace(std::forward<P>(value)); + } + + // TODO(T31574848): Work around libstdc++ versions (e.g., GCC < 6) with no + // implementation of N4387 ("perfect initialization" for pairs and tuples). + template <typename U1, typename U2> + std::enable_if_t< + std::is_constructible<key_type, U1 const&>::value && + std::is_constructible<mapped_type, U2 const&>::value, + std::pair<iterator, bool>> + insert(std::pair<U1, U2> const& value) { + return emplace(value); + } + + // TODO(T31574848) + template <typename U1, typename U2> + std::enable_if_t< + std::is_constructible<key_type, U1&&>::value && + std::is_constructible<mapped_type, U2&&>::value, + std::pair<iterator, bool>> + insert(std::pair<U1, U2>&& value) { + return emplace(std::move(value)); + } + + std::pair<iterator, bool> insert(value_type&& value) { + return emplace(std::move(value)); + } + + // std::unordered_map's hinted insertion API is misleading. No + // implementation I've seen actually uses the hint. Code restructuring + // by the caller to use the hinted API is at best unnecessary, and at + // worst a pessimization. It is used, however, so we provide it. + + iterator insert(const_iterator /*hint*/, value_type const& value) { + return insert(value).first; + } + + template <typename P> + std::enable_if_t<std::is_constructible<value_type, P&&>::value, iterator> + insert(const_iterator /*hint*/, P&& value) { + return insert(std::forward<P>(value)).first; + } + + iterator insert(const_iterator /*hint*/, value_type&& value) { + return insert(std::move(value)).first; + } + + template <class... Args> + iterator emplace_hint(const_iterator /*hint*/, Args&&... args) { + return emplace(std::forward<Args>(args)...).first; + } + + private: + template <class InputIt> + FOLLY_ALWAYS_INLINE void + bulkInsert(InputIt first, InputIt last, bool autoReserve) { + if (autoReserve) { + auto n = std::distance(first, last); + if (n == 0) { + return; + } + table_.reserveForInsert(n); + } + while (first != last) { + insert(*first); + ++first; + } + } + + template <class InputIt> + void initialInsert(InputIt first, InputIt last, std::size_t initialCapacity) { + FOLLY_SAFE_DCHECK(empty() && bucket_count() >= initialCapacity, ""); + + // It's possible that there are a lot of duplicates in first..last and + // so we will oversize ourself. The common case, however, is that + // we can avoid a lot of rehashing if we pre-expand. The behavior + // is easy to disable at a particular call site by asking for an + // initialCapacity of 1. + bool autoReserve = + std::is_same< + typename std::iterator_traits<InputIt>::iterator_category, + std::random_access_iterator_tag>::value && + initialCapacity == 0; + bulkInsert(first, last, autoReserve); + } + + public: + template <class InputIt> + void insert(InputIt first, InputIt last) { + // Bulk reserve is a heuristic choice, so it can backfire. We restrict + // ourself to situations that mimic bulk construction without an + // explicit initialCapacity. + bool autoReserve = + std::is_same< + typename std::iterator_traits<InputIt>::iterator_category, + std::random_access_iterator_tag>::value && + bucket_count() == 0; + bulkInsert(first, last, autoReserve); + } + + void insert(std::initializer_list<value_type> ilist) { + insert(ilist.begin(), ilist.end()); + } + + template <typename M> + std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj) { + auto rv = try_emplace(key, std::forward<M>(obj)); + if (!rv.second) { + rv.first->second = std::forward<M>(obj); + } + return rv; + } + + template <typename M> + std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj) { + auto rv = try_emplace(std::move(key), std::forward<M>(obj)); + if (!rv.second) { + rv.first->second = std::forward<M>(obj); + } + return rv; + } + + template <typename M> + iterator + insert_or_assign(const_iterator /*hint*/, key_type const& key, M&& obj) { + return insert_or_assign(key, std::move(obj)).first; + } + + template <typename M> + iterator insert_or_assign(const_iterator /*hint*/, key_type&& key, M&& obj) { + return insert_or_assign(std::move(key), std::move(obj)).first; + } + + template <typename K, typename M> + EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert_or_assign( + K&& key, + M&& obj) { + auto rv = try_emplace(std::forward<K>(key), std::forward<M>(obj)); + if (!rv.second) { + rv.first->second = std::forward<M>(obj); + } + return rv; + } + + private: + template <typename Arg> + using UsableAsKey = + EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>; + + public: + template <typename... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + auto rv = folly::detail::callWithExtractedKey<key_type, UsableAsKey>( + table_.alloc(), + [&](auto&&... inner) { + return table_.tryEmplaceValue( + std::forward<decltype(inner)>(inner)...); + }, + std::forward<Args>(args)...); + return std::make_pair(table_.makeIter(rv.first), rv.second); + } + + template <typename... Args> + std::pair<iterator, bool> try_emplace(key_type const& key, Args&&... args) { + auto rv = table_.tryEmplaceValue( + key, + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return std::make_pair(table_.makeIter(rv.first), rv.second); + } + + template <typename... Args> + std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args) { + auto rv = table_.tryEmplaceValue( + key, + std::piecewise_construct, + std::forward_as_tuple(std::move(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + return std::make_pair(table_.makeIter(rv.first), rv.second); + } + + template <typename... Args> + iterator + try_emplace(const_iterator /*hint*/, key_type const& key, Args&&... args) { + auto rv = table_.tryEmplaceValue( + key, + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return table_.makeIter(rv.first); + } + + template <typename... Args> + iterator + try_emplace(const_iterator /*hint*/, key_type&& key, Args&&... args) { + auto rv = table_.tryEmplaceValue( + key, + std::piecewise_construct, + std::forward_as_tuple(std::move(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + return table_.makeIter(rv.first); + } + + template <typename K, typename... Args> + EnableHeterogeneousInsert<K, std::pair<iterator, bool>> try_emplace( + K&& key, + Args&&... args) { + auto rv = table_.tryEmplaceValue( + key, + std::piecewise_construct, + std::forward_as_tuple(std::forward<K>(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + return std::make_pair(table_.makeIter(rv.first), rv.second); + } + + FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) { + return eraseInto(pos, [](key_type&&, mapped_type&&) {}); + } + + // This form avoids ambiguity when key_type has a templated constructor + // that accepts const_iterator + FOLLY_ALWAYS_INLINE iterator erase(iterator pos) { + return eraseInto(pos, [](key_type&&, mapped_type&&) {}); + } + + iterator erase(const_iterator first, const_iterator last) { + return eraseInto(first, last, [](key_type&&, mapped_type&&) {}); + } + + size_type erase(key_type const& key) { + return eraseInto(key, [](key_type&&, mapped_type&&) {}); + } + + template <typename K> + EnableHeterogeneousErase<K, size_type> erase(K const& key) { + return eraseInto(key, [](key_type&&, mapped_type&&) {}); + } + + protected: + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE void tableEraseIterInto( + ItemIter pos, + BeforeDestroy&& beforeDestroy) { + table_.eraseIterInto(pos, [&](value_type&& v) { + auto p = Policy::moveValue(v); + beforeDestroy(std::move(p.first), std::move(p.second)); + }); + } + + template <typename K, typename BeforeDestroy> + FOLLY_ALWAYS_INLINE std::size_t tableEraseKeyInto( + K const& key, + BeforeDestroy&& beforeDestroy) { + return table_.eraseKeyInto(key, [&](value_type&& v) { + auto p = Policy::moveValue(v); + beforeDestroy(std::move(p.first), std::move(p.second)); + }); + } + + public: + // eraseInto contains the same overloads as erase but provides + // an additional callback argument which is called with an rvalue + // reference (not const) to the key and an rvalue reference to the + // mapped value directly before it is destroyed. This can be used + // to extract an entry out of a F14Map while avoiding a copy. + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) { + // If we are inlined then gcc and clang can optimize away all of the + // work of itemPos.advance() if our return value is discarded. + auto itemPos = table_.unwrapIter(pos); + tableEraseIterInto(itemPos, beforeDestroy); + itemPos.advanceLikelyDead(); + return table_.makeIter(itemPos); + } + + // This form avoids ambiguity when key_type has a templated constructor + // that accepts const_iterator + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(iterator pos, BeforeDestroy&& beforeDestroy) { + const_iterator cpos{pos}; + return eraseInto(cpos, beforeDestroy); + } + + template <typename BeforeDestroy> + iterator eraseInto( + const_iterator first, + const_iterator last, + BeforeDestroy&& beforeDestroy) { + auto itemFirst = table_.unwrapIter(first); + auto itemLast = table_.unwrapIter(last); + while (itemFirst != itemLast) { + tableEraseIterInto(itemFirst, beforeDestroy); + itemFirst.advance(); + } + return table_.makeIter(itemFirst); + } + + template <typename BeforeDestroy> + size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) { + return tableEraseKeyInto(key, beforeDestroy); + } + + template <typename K, typename BeforeDestroy> + EnableHeterogeneousErase<K, size_type> eraseInto( + K const& key, + BeforeDestroy&& beforeDestroy) { + return tableEraseKeyInto(key, beforeDestroy); + } + + //// PUBLIC - Lookup + + FOLLY_ALWAYS_INLINE mapped_type& at(key_type const& key) { + return at(*this, key); + } + + FOLLY_ALWAYS_INLINE mapped_type const& at(key_type const& key) const { + return at(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, mapped_type&> at(K const& key) { + return at(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, mapped_type const&> at(K const& key) const { + return at(*this, key); + } + + mapped_type& operator[](key_type const& key) { + return try_emplace(key).first->second; + } + + mapped_type& operator[](key_type&& key) { + return try_emplace(std::move(key)).first->second; + } + + template <typename K> + EnableHeterogeneousInsert<K, mapped_type&> operator[](K&& key) { + return try_emplace(std::forward<K>(key)).first->second; + } + + FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const { + return contains(key) ? 1 : 0; + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count( + K const& key) const { + return contains(key) ? 1 : 0; + } + + // prehash(key) does the work of evaluating hash_function()(key) + // (including additional bit-mixing for non-avalanching hash functions), + // wraps the result of that work in a token for later reuse, and + // begins prefetching the first steps of looking for key into the + // local CPU cache. + // + // The returned token may be used at any time, may be used more than + // once, and may be used in other F14 sets and maps. Tokens are + // transferrable between any F14 containers (maps and sets) with the + // same key_type and equal hash_function()s. + // + // Hash tokens are not hints -- it is a bug to call any method on this + // class with a token t and key k where t isn't the result of a call + // to prehash(k2) with k2 == k. + F14HashToken prehash(key_type const& key) const { + return table_.prehash(key); + } + + template <typename K> + EnableHeterogeneousFind<K, F14HashToken> prehash(K const& key) const { + return table_.prehash(key); + } + + FOLLY_ALWAYS_INLINE iterator find(key_type const& key) { + return table_.makeIter(table_.find(key)); + } + + FOLLY_ALWAYS_INLINE const_iterator find(key_type const& key) const { + return table_.makeConstIter(table_.find(key)); + } + + FOLLY_ALWAYS_INLINE iterator + find(F14HashToken const& token, key_type const& key) { + return table_.makeIter(table_.find(token, key)); + } + + FOLLY_ALWAYS_INLINE const_iterator + find(F14HashToken const& token, key_type const& key) const { + return table_.makeConstIter(table_.find(token, key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(K const& key) { + return table_.makeIter(table_.find(key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find( + K const& key) const { + return table_.makeConstIter(table_.find(key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find( + F14HashToken const& token, + K const& key) { + return table_.makeIter(table_.find(token, key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find( + F14HashToken const& token, + K const& key) const { + return table_.makeConstIter(table_.find(token, key)); + } + + FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const { + return !table_.find(key).atEnd(); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains( + K const& key) const { + return !table_.find(key).atEnd(); + } + + FOLLY_ALWAYS_INLINE bool contains( + F14HashToken const& token, + key_type const& key) const { + return !table_.find(token, key).atEnd(); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains( + F14HashToken const& token, + K const& key) const { + return !table_.find(token, key).atEnd(); + } + + std::pair<iterator, iterator> equal_range(key_type const& key) { + return equal_range(*this, key); + } + + std::pair<const_iterator, const_iterator> equal_range( + key_type const& key) const { + return equal_range(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range( + K const& key) { + return equal_range(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>> + equal_range(K const& key) const { + return equal_range(*this, key); + } + + //// PUBLIC - Bucket interface + + std::size_t bucket_count() const noexcept { + return table_.bucket_count(); + } + + std::size_t max_bucket_count() const noexcept { + return table_.max_bucket_count(); + } + + //// PUBLIC - Hash policy + + float load_factor() const noexcept { + return table_.load_factor(); + } + + float max_load_factor() const noexcept { + return table_.max_load_factor(); + } + + void max_load_factor(float v) { + table_.max_load_factor(v); + } + + void rehash(std::size_t bucketCapacity) { + // The standard's rehash() requires understanding the max load factor, + // which is easy to get wrong. Since we don't actually allow adjustment + // of max_load_factor there is no difference. + reserve(bucketCapacity); + } + + void reserve(std::size_t capacity) { + table_.reserve(capacity); + } + + //// PUBLIC - Observers + + hasher hash_function() const { + return table_.hasher(); + } + + key_equal key_eq() const { + return table_.keyEqual(); + } + + //// PUBLIC - F14 Extensions + + // containsEqualValue returns true iff there is an element in the map + // that compares equal to value using operator==. It is undefined + // behavior to call this function if operator== on key_type can ever + // return true when the same keys passed to key_eq() would return false + // (the opposite is allowed). + bool containsEqualValue(value_type const& value) const { + auto it = table_.findMatching( + value.first, [&](auto& key) { return value.first == key; }); + return !it.atEnd() && value.second == table_.valueAtItem(it.citem()).second; + } + + // Get memory footprint, not including sizeof(*this). + std::size_t getAllocatedMemorySize() const { + return table_.getAllocatedMemorySize(); + } + + // Enumerates classes of allocated memory blocks currently owned + // by this table, calling visitor(allocationSize, allocationCount). + // This can be used to get a more accurate indication of memory footprint + // than getAllocatedMemorySize() if you have some way of computing the + // internal fragmentation of the allocator, such as JEMalloc's nallocx. + // The visitor might be called twice with the same allocationSize. The + // visitor's computation should produce the same result for visitor(8, + // 2) as for two calls to visitor(8, 1), for example. The visitor may + // be called with a zero allocationCount. + template <typename V> + void visitAllocationClasses(V&& visitor) const { + return table_.visitAllocationClasses(visitor); + } + + // Calls visitor with two value_type const*, b and e, such that every + // entry in the table is included in exactly one of the ranges [b,e). + // This can be used to efficiently iterate elements in bulk when crossing + // an API boundary that supports contiguous blocks of items. + template <typename V> + void visitContiguousRanges(V&& visitor) const; + + F14TableStats computeStats() const noexcept { + return table_.computeStats(); + } + + private: + template <typename Self, typename K> + FOLLY_ALWAYS_INLINE static auto& at(Self& self, K const& key) { + auto iter = self.find(key); + if (iter == self.end()) { + throw_exception<std::out_of_range>("at() did not find key"); + } + return iter->second; + } + + template <typename Self, typename K> + static auto equal_range(Self& self, K const& key) { + auto first = self.find(key); + auto last = first; + if (last != self.end()) { + ++last; + } + return std::make_pair(first, last); + } + + protected: + F14Table<Policy> table_; +}; +} // namespace detail +} // namespace f14 + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14ValueMap + : public f14::detail::F14BasicMap<f14::detail::MapPolicyWithDefaults< + f14::detail::ValueContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc>> { + protected: + using Policy = f14::detail::MapPolicyWithDefaults< + f14::detail::ValueContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc>; + + private: + using Super = f14::detail::F14BasicMap<Policy>; + + public: + using typename Super::value_type; + + F14ValueMap() = default; + + using Super::Super; + + F14ValueMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14ValueMap& rhs) noexcept(Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + this->table_.visitContiguousItemRanges(visitor); + } +}; + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14NodeMap + : public f14::detail::F14BasicMap<f14::detail::MapPolicyWithDefaults< + f14::detail::NodeContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc>> { + protected: + using Policy = f14::detail::MapPolicyWithDefaults< + f14::detail::NodeContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc>; + + private: + using Super = f14::detail::F14BasicMap<Policy>; + + public: + using typename Super::value_type; + + F14NodeMap() = default; + + using Super::Super; + + F14NodeMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14NodeMap& rhs) noexcept(Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + this->table_.visitItems([&](typename Policy::Item ptr) { + value_type const* b = std::addressof(*ptr); + visitor(b, b + 1); + }); + } + + // TODO extract and node_handle insert +}; + +namespace f14 { +namespace detail { +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc, + typename EligibleForPerturbedInsertionOrder> +class F14VectorMapImpl : public F14BasicMap<MapPolicyWithDefaults< + VectorContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc, + EligibleForPerturbedInsertionOrder>> { + protected: + using Policy = MapPolicyWithDefaults< + VectorContainerPolicy, + Key, + Mapped, + Hasher, + KeyEqual, + Alloc, + EligibleForPerturbedInsertionOrder>; + + private: + using Super = F14BasicMap<Policy>; + + template <typename K, typename T> + using EnableHeterogeneousVectorErase = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value && + !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value && + !std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>::value && + !std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>:: + value && + !std::is_same<typename Policy::ConstReverseIter, remove_cvref_t<K>>:: + value, + T>; + + public: + using typename Super::const_iterator; + using typename Super::iterator; + using typename Super::key_type; + using typename Super::mapped_type; + using typename Super::value_type; + + F14VectorMapImpl() = default; + + // inherit constructors + using Super::Super; + + F14VectorMapImpl& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + iterator begin() { + return this->table_.linearBegin(this->size()); + } + const_iterator begin() const { + return cbegin(); + } + const_iterator cbegin() const { + return this->table_.linearBegin(this->size()); + } + + iterator end() { + return this->table_.linearEnd(); + } + const_iterator end() const { + return cend(); + } + const_iterator cend() const { + return this->table_.linearEnd(); + } + + private: + template <typename BeforeDestroy> + void eraseUnderlying( + typename Policy::ItemIter underlying, + BeforeDestroy&& beforeDestroy) { + Alloc& a = this->table_.alloc(); + auto values = this->table_.values_; + + // Remove the ptr from the base table and destroy the value. + auto index = underlying.item(); + // The item still needs to be hashable during this call, so we must destroy + // the value _afterwards_. + this->tableEraseIterInto(underlying, beforeDestroy); + Policy::AllocTraits::destroy(a, std::addressof(values[index])); + + // move the last element in values_ down and fix up the inbound index + auto tailIndex = this->size(); + if (tailIndex != index) { + auto tail = this->table_.find( + VectorContainerIndexSearch{static_cast<uint32_t>(tailIndex)}); + tail.item() = index; + auto p = std::addressof(values[index]); + assume(p != nullptr); + this->table_.transfer(a, std::addressof(values[tailIndex]), p, 1); + } + } + + template <typename K, typename BeforeDestroy> + std::size_t eraseUnderlyingKey(K const& key, BeforeDestroy&& beforeDestroy) { + auto underlying = this->table_.find(key); + if (underlying.atEnd()) { + return 0; + } else { + eraseUnderlying(underlying, beforeDestroy); + return 1; + } + } + + public: + FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) { + return eraseInto(pos, [](key_type&&, mapped_type&&) {}); + } + + // This form avoids ambiguity when key_type has a templated constructor + // that accepts const_iterator + FOLLY_ALWAYS_INLINE iterator erase(iterator pos) { + return eraseInto(pos, [](key_type&&, mapped_type&&) {}); + } + + iterator erase(const_iterator first, const_iterator last) { + return eraseInto(first, last, [](key_type&&, mapped_type&&) {}); + } + + std::size_t erase(key_type const& key) { + return eraseInto(key, [](key_type&&, mapped_type&&) {}); + } + + template <typename K> + EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) { + return eraseInto(key, [](key_type&&, mapped_type&&) {}); + } + + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) { + auto index = this->table_.iterToIndex(pos); + auto underlying = this->table_.find(VectorContainerIndexSearch{index}); + eraseUnderlying(underlying, beforeDestroy); + return index == 0 ? end() : this->table_.indexToIter(index - 1); + } + + // This form avoids ambiguity when key_type has a templated constructor + // that accepts const_iterator + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(iterator pos, BeforeDestroy&& beforeDestroy) { + const_iterator cpos{pos}; + return eraseInto(cpos, beforeDestroy); + } + + template <typename BeforeDestroy> + iterator eraseInto( + const_iterator first, + const_iterator last, + BeforeDestroy&& beforeDestroy) { + while (first != last) { + first = eraseInto(first, beforeDestroy); + } + auto index = this->table_.iterToIndex(first); + return index == 0 ? end() : this->table_.indexToIter(index - 1); + } + + template <typename BeforeDestroy> + std::size_t eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) { + return eraseUnderlyingKey(key, beforeDestroy); + } + + template <typename K, typename BeforeDestroy> + EnableHeterogeneousVectorErase<K, std::size_t> eraseInto( + K const& key, + BeforeDestroy&& beforeDestroy) { + return eraseUnderlyingKey(key, beforeDestroy); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + auto n = this->table_.size(); + if (n > 0) { + value_type const* b = std::addressof(this->table_.values_[0]); + visitor(b, b + n); + } + } +}; +} // namespace detail +} // namespace f14 + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14VectorMap : public f14::detail::F14VectorMapImpl< + Key, + Mapped, + Hasher, + KeyEqual, + Alloc, + std::false_type> { + using Super = f14::detail:: + F14VectorMapImpl<Key, Mapped, Hasher, KeyEqual, Alloc, std::false_type>; + + public: + using typename Super::const_iterator; + using typename Super::iterator; + using typename Super::value_type; + using reverse_iterator = typename Super::Policy::ReverseIter; + using const_reverse_iterator = typename Super::Policy::ConstReverseIter; + + F14VectorMap() = default; + + // inherit constructors + using Super::Super; + + F14VectorMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14VectorMap& rhs) noexcept(Super::Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + // ITERATION ORDER + // + // Deterministic iteration order for insert-only workloads is part of + // F14VectorMap's supported API: iterator is LIFO and reverse_iterator + // is FIFO. + // + // If there have been no calls to erase() then iterator and + // const_iterator enumerate entries in the opposite of insertion order. + // begin()->first is the key most recently inserted. reverse_iterator + // and reverse_const_iterator, therefore, enumerate in LIFO (insertion) + // order for insert-only workloads. Deterministic iteration order is + // only guaranteed if no keys were removed since the last time the + // map was empty. Iteration order is preserved across rehashes and + // F14VectorMap copies and moves. + // + // iterator uses LIFO order so that erasing while iterating with begin() + // and end() is safe using the erase(it++) idiom, which is supported + // by std::map and std::unordered_map. erase(iter) invalidates iter + // and all iterators before iter in the non-reverse iteration order. + // Every successful erase invalidates all reverse iterators. + // + // No erase is provided for reverse_iterator or const_reverse_iterator + // to make it harder to shoot yourself in the foot by erasing while + // reverse-iterating. You can write that as map.erase(map.iter(riter)) + // if you really need it. + + reverse_iterator rbegin() { + return this->table_.values_; + } + const_reverse_iterator rbegin() const { + return crbegin(); + } + const_reverse_iterator crbegin() const { + return this->table_.values_; + } + + reverse_iterator rend() { + return this->table_.values_ + this->table_.size(); + } + const_reverse_iterator rend() const { + return crend(); + } + const_reverse_iterator crend() const { + return this->table_.values_ + this->table_.size(); + } + + // explicit conversions between iterator and reverse_iterator + iterator iter(reverse_iterator riter) { + return this->table_.iter(riter); + } + const_iterator iter(const_reverse_iterator riter) const { + return this->table_.iter(riter); + } + + reverse_iterator riter(iterator it) { + return this->table_.riter(it); + } + const_reverse_iterator riter(const_iterator it) const { + return this->table_.riter(it); + } +}; + +template <typename K, typename M, typename H, typename E, typename A> +Range<typename F14VectorMap<K, M, H, E, A>::const_reverse_iterator> +order_preserving_reinsertion_view(const F14VectorMap<K, M, H, E, A>& c) { + return {c.rbegin(), c.rend()}; +} + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14FastMap : public std::conditional_t< + sizeof(std::pair<Key const, Mapped>) < 24, + F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>, + f14::detail::F14VectorMapImpl< + Key, + Mapped, + Hasher, + KeyEqual, + Alloc, + std::true_type>> { + using Super = std::conditional_t< + sizeof(std::pair<Key const, Mapped>) < 24, + F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>, + f14::detail::F14VectorMapImpl< + Key, + Mapped, + Hasher, + KeyEqual, + Alloc, + std::true_type>>; + + public: + using typename Super::value_type; + + F14FastMap() = default; + + using Super::Super; + + F14FastMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14FastMap& rhs) noexcept(Super::Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } +}; +} // namespace folly + +#else // !if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +//////// Compatibility for unsupported platforms (not x86_64 and not aarch64) +#include <folly/container/detail/F14MapFallback.h> + +#endif // if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE else + +namespace folly { +namespace f14 { +namespace detail { +template <typename M> +bool mapsEqual(M const& lhs, M const& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + for (auto& kv : lhs) { + if (!rhs.containsEqualValue(kv)) { + return false; + } + } + return true; +} +} // namespace detail +} // namespace f14 + +template <typename K, typename M, typename H, typename E, typename A> +bool operator==( + F14ValueMap<K, M, H, E, A> const& lhs, + F14ValueMap<K, M, H, E, A> const& rhs) { + return mapsEqual(lhs, rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator!=( + F14ValueMap<K, M, H, E, A> const& lhs, + F14ValueMap<K, M, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator==( + F14NodeMap<K, M, H, E, A> const& lhs, + F14NodeMap<K, M, H, E, A> const& rhs) { + return mapsEqual(lhs, rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator!=( + F14NodeMap<K, M, H, E, A> const& lhs, + F14NodeMap<K, M, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator==( + F14VectorMap<K, M, H, E, A> const& lhs, + F14VectorMap<K, M, H, E, A> const& rhs) { + return mapsEqual(lhs, rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator!=( + F14VectorMap<K, M, H, E, A> const& lhs, + F14VectorMap<K, M, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator==( + F14FastMap<K, M, H, E, A> const& lhs, + F14FastMap<K, M, H, E, A> const& rhs) { + return mapsEqual(lhs, rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +bool operator!=( + F14FastMap<K, M, H, E, A> const& lhs, + F14FastMap<K, M, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +void swap( + F14ValueMap<K, M, H, E, A>& lhs, + F14ValueMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +void swap( + F14NodeMap<K, M, H, E, A>& lhs, + F14NodeMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +void swap( + F14VectorMap<K, M, H, E, A>& lhs, + F14VectorMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename M, typename H, typename E, typename A> +void swap( + F14FastMap<K, M, H, E, A>& lhs, + F14FastMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template < + typename K, + typename M, + typename H, + typename E, + typename A, + typename Pred> +void erase_if(F14ValueMap<K, M, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template < + typename K, + typename M, + typename H, + typename E, + typename A, + typename Pred> +void erase_if(F14NodeMap<K, M, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template < + typename K, + typename M, + typename H, + typename E, + typename A, + typename Pred> +void erase_if(F14VectorMap<K, M, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template < + typename K, + typename M, + typename H, + typename E, + typename A, + typename Pred> +void erase_if(F14FastMap<K, M, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/F14Set-fwd.h b/ios/Pods/Flipper-Folly/folly/container/F14Set-fwd.h new file mode 100644 index 000000000..db484fa16 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/F14Set-fwd.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/container/detail/F14Defaults.h> +#include <folly/memory/MemoryResource.h> + +namespace folly { +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<Key>> +class F14NodeSet; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<Key>> +class F14ValueSet; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<Key>> +class F14VectorSet; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>, + typename Alloc = f14::DefaultAlloc<Key>> +class F14FastSet; + +#if FOLLY_HAS_MEMORY_RESOURCE +namespace pmr { +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14NodeSet = folly::F14NodeSet< + Key, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator<Key>>; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14ValueSet = folly::F14ValueSet< + Key, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator<Key>>; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14VectorSet = folly::F14VectorSet< + Key, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator<Key>>; + +template < + typename Key, + typename Hasher = f14::DefaultHasher<Key>, + typename KeyEqual = f14::DefaultKeyEqual<Key>> +using F14FastSet = folly::F14FastSet< + Key, + Hasher, + KeyEqual, + folly::detail::std_pmr::polymorphic_allocator<Key>>; +} // namespace pmr +#endif // FOLLY_HAS_MEMORY_RESOURCE + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/F14Set.h b/ios/Pods/Flipper-Folly/folly/container/F14Set.h new file mode 100644 index 000000000..3c4eabe78 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/F14Set.h @@ -0,0 +1,1143 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/** + * F14NodeSet, F14ValueSet, and F14VectorSet + * + * F14FastSet conditionally works like F14ValueSet or F14VectorSet + * + * See F14.md + * + * @author Nathan Bronson <ngbronson@fb.com> + * @author Xiao Shi <xshi@fb.com> + */ + +#include <cstddef> +#include <initializer_list> +#include <tuple> + +#include <folly/lang/SafeAssert.h> + +#include <folly/container/F14Set-fwd.h> +#include <folly/container/detail/F14Policy.h> +#include <folly/container/detail/F14Table.h> +#include <folly/container/detail/Util.h> + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +//////// Common case for supported platforms + +namespace folly { +namespace f14 { +namespace detail { + +template <typename Policy> +class F14BasicSet { + template <typename K, typename T> + using EnableHeterogeneousFind = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value, + T>; + + template <typename K, typename T> + using EnableHeterogeneousInsert = std::enable_if_t< + EligibleForHeterogeneousInsert< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value, + T>; + + template <typename K, typename T> + using EnableHeterogeneousErase = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value && + !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value, + T>; + + public: + //// PUBLIC - Member types + + using key_type = typename Policy::Value; + using value_type = key_type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = typename Policy::Hasher; + using key_equal = typename Policy::KeyEqual; + using allocator_type = typename Policy::Alloc; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = typename Policy::AllocTraits::pointer; + using const_pointer = typename Policy::AllocTraits::const_pointer; + using iterator = typename Policy::Iter; + using const_iterator = iterator; + + private: + using ItemIter = typename Policy::ItemIter; + + public: + //// PUBLIC - Member functions + + F14BasicSet() noexcept(Policy::kDefaultConstructIsNoexcept) + : F14BasicSet(0) {} + + explicit F14BasicSet( + std::size_t initialCapacity, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} {} + + explicit F14BasicSet(std::size_t initialCapacity, allocator_type const& alloc) + : F14BasicSet(initialCapacity, hasher{}, key_equal{}, alloc) {} + + explicit F14BasicSet( + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : F14BasicSet(initialCapacity, hash, key_equal{}, alloc) {} + + explicit F14BasicSet(allocator_type const& alloc) + : F14BasicSet(0, hasher{}, key_equal{}, alloc) {} + + template <typename InputIt> + F14BasicSet( + InputIt first, + InputIt last, + std::size_t initialCapacity = 0, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} { + initialInsert(first, last, initialCapacity); + } + + template <typename InputIt> + F14BasicSet( + InputIt first, + InputIt last, + std::size_t initialCapacity, + allocator_type const& alloc) + : table_{initialCapacity, hasher{}, key_equal{}, alloc} { + initialInsert(first, last, initialCapacity); + } + + template <typename InputIt> + F14BasicSet( + InputIt first, + InputIt last, + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : table_{initialCapacity, hash, key_equal{}, alloc} { + initialInsert(first, last, initialCapacity); + } + + F14BasicSet(F14BasicSet const& rhs) = default; + + F14BasicSet(F14BasicSet const& rhs, allocator_type const& alloc) + : table_(rhs.table_, alloc) {} + + F14BasicSet(F14BasicSet&& rhs) = default; + + F14BasicSet(F14BasicSet&& rhs, allocator_type const& alloc) noexcept( + Policy::kAllocIsAlwaysEqual) + : table_{std::move(rhs.table_), alloc} {} + + F14BasicSet( + std::initializer_list<value_type> init, + std::size_t initialCapacity = 0, + hasher const& hash = hasher{}, + key_equal const& eq = key_equal{}, + allocator_type const& alloc = allocator_type{}) + : table_{initialCapacity, hash, eq, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicSet( + std::initializer_list<value_type> init, + std::size_t initialCapacity, + allocator_type const& alloc) + : table_{initialCapacity, hasher{}, key_equal{}, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicSet( + std::initializer_list<value_type> init, + std::size_t initialCapacity, + hasher const& hash, + allocator_type const& alloc) + : table_{initialCapacity, hash, key_equal{}, alloc} { + initialInsert(init.begin(), init.end(), initialCapacity); + } + + F14BasicSet& operator=(F14BasicSet const&) = default; + + F14BasicSet& operator=(F14BasicSet&&) = default; + + F14BasicSet& operator=(std::initializer_list<value_type> ilist) { + clear(); + bulkInsert(ilist.begin(), ilist.end(), false); + return *this; + } + + allocator_type get_allocator() const noexcept { + return table_.alloc(); + } + + //// PUBLIC - Iterators + + iterator begin() noexcept { + return cbegin(); + } + const_iterator begin() const noexcept { + return cbegin(); + } + const_iterator cbegin() const noexcept { + return table_.makeIter(table_.begin()); + } + + iterator end() noexcept { + return cend(); + } + const_iterator end() const noexcept { + return cend(); + } + const_iterator cend() const noexcept { + return table_.makeIter(table_.end()); + } + + //// PUBLIC - Capacity + + bool empty() const noexcept { + return table_.empty(); + } + + std::size_t size() const noexcept { + return table_.size(); + } + + std::size_t max_size() const noexcept { + return table_.max_size(); + } + + //// PUBLIC - Modifiers + + void clear() noexcept { + table_.clear(); + } + + std::pair<iterator, bool> insert(value_type const& value) { + return emplace(value); + } + + std::pair<iterator, bool> insert(value_type&& value) { + return emplace(std::move(value)); + } + + // std::unordered_set's hinted insertion API is misleading. No + // implementation I've seen actually uses the hint. Code restructuring + // by the caller to use the hinted API is at best unnecessary, and at + // worst a pessimization. It is used, however, so we provide it. + + iterator insert(const_iterator /*hint*/, value_type const& value) { + return insert(value).first; + } + + iterator insert(const_iterator /*hint*/, value_type&& value) { + return insert(std::move(value)).first; + } + + template <typename K> + EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert(K&& value) { + return emplace(std::forward<K>(value)); + } + + private: + template <class InputIt> + FOLLY_ALWAYS_INLINE void + bulkInsert(InputIt first, InputIt last, bool autoReserve) { + if (autoReserve) { + auto n = std::distance(first, last); + if (n == 0) { + return; + } + table_.reserveForInsert(n); + } + while (first != last) { + insert(*first); + ++first; + } + } + + template <class InputIt> + void initialInsert(InputIt first, InputIt last, std::size_t initialCapacity) { + FOLLY_SAFE_DCHECK(empty() && bucket_count() >= initialCapacity, ""); + + // It's possible that there are a lot of duplicates in first..last and + // so we will oversize ourself. The common case, however, is that + // we can avoid a lot of rehashing if we pre-expand. The behavior + // is easy to disable at a particular call site by asking for an + // initialCapacity of 1. + bool autoReserve = + std::is_same< + typename std::iterator_traits<InputIt>::iterator_category, + std::random_access_iterator_tag>::value && + initialCapacity == 0; + bulkInsert(first, last, autoReserve); + } + + public: + template <class InputIt> + void insert(InputIt first, InputIt last) { + // Bulk reserve is a heuristic choice, so it can backfire. We restrict + // ourself to situations that mimic bulk construction without an + // explicit initialCapacity. + bool autoReserve = + std::is_same< + typename std::iterator_traits<InputIt>::iterator_category, + std::random_access_iterator_tag>::value && + bucket_count() == 0; + bulkInsert(first, last, autoReserve); + } + + void insert(std::initializer_list<value_type> ilist) { + insert(ilist.begin(), ilist.end()); + } + + private: + template <typename Arg> + using UsableAsKey = + EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>; + + public: + template <class... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + auto rv = folly::detail::callWithConstructedKey<key_type, UsableAsKey>( + table_.alloc(), + [&](auto&&... inner) { + return table_.tryEmplaceValue( + std::forward<decltype(inner)>(inner)...); + }, + std::forward<Args>(args)...); + return std::make_pair(table_.makeIter(rv.first), rv.second); + } + + template <class... Args> + iterator emplace_hint(const_iterator /*hint*/, Args&&... args) { + return emplace(std::forward<Args>(args)...).first; + } + + FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) { + return eraseInto(pos, [](value_type&&) {}); + } + + iterator erase(const_iterator first, const_iterator last) { + return eraseInto(first, last, [](value_type&&) {}); + } + + size_type erase(key_type const& key) { + return eraseInto(key, [](value_type&&) {}); + } + + template <typename K> + EnableHeterogeneousErase<K, size_type> erase(K const& key) { + return eraseInto(key, [](value_type&&) {}); + } + + // eraseInto contains the same overloads as erase but provides + // an additional callback argument which is called with an rvalue + // reference to the item directly before it is destroyed. This can be + // used to extract an item out of a F14Set while avoiding a copy. + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) { + auto itemPos = table_.unwrapIter(pos); + table_.eraseIterInto(itemPos, beforeDestroy); + + // If we are inlined then gcc and clang can optimize away all of the + // work of ++pos if the caller discards it. + itemPos.advanceLikelyDead(); + return table_.makeIter(itemPos); + } + + template <typename BeforeDestroy> + iterator eraseInto( + const_iterator first, + const_iterator last, + BeforeDestroy&& beforeDestroy) { + while (first != last) { + first = eraseInto(first, beforeDestroy); + } + return first; + } + + template <typename BeforeDestroy> + size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) { + return table_.eraseKeyInto(key, beforeDestroy); + } + + template <typename K, typename BeforeDestroy> + EnableHeterogeneousErase<K, size_type> eraseInto( + K const& key, + BeforeDestroy&& beforeDestroy) { + return table_.eraseKeyInto(key, beforeDestroy); + } + + //// PUBLIC - Lookup + + FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const { + return contains(key) ? 1 : 0; + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count( + K const& key) const { + return contains(key) ? 1 : 0; + } + + // prehash(key) does the work of evaluating hash_function()(key) + // (including additional bit-mixing for non-avalanching hash functions), + // wraps the result of that work in a token for later reuse, and + // begins prefetching of the first steps of looking for key into the + // local CPU cache. + // + // The returned token may be used at any time, may be used more than + // once, and may be used in other F14 sets and maps. Tokens are + // transferrable between any F14 containers (maps and sets) with the + // same key_type and equal hash_function()s. + // + // Hash tokens are not hints -- it is a bug to call any method on this + // class with a token t and key k where t isn't the result of a call + // to prehash(k2) with k2 == k. + F14HashToken prehash(key_type const& key) const { + return table_.prehash(key); + } + + template <typename K> + EnableHeterogeneousFind<K, F14HashToken> prehash(K const& key) const { + return table_.prehash(key); + } + + FOLLY_ALWAYS_INLINE iterator find(key_type const& key) { + return const_cast<F14BasicSet const*>(this)->find(key); + } + + FOLLY_ALWAYS_INLINE const_iterator find(key_type const& key) const { + return table_.makeIter(table_.find(key)); + } + + FOLLY_ALWAYS_INLINE iterator + find(F14HashToken const& token, key_type const& key) { + return const_cast<F14BasicSet const*>(this)->find(token, key); + } + + FOLLY_ALWAYS_INLINE const_iterator + find(F14HashToken const& token, key_type const& key) const { + return table_.makeIter(table_.find(token, key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(K const& key) { + return const_cast<F14BasicSet const*>(this)->find(key); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find( + K const& key) const { + return table_.makeIter(table_.find(key)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find( + F14HashToken const& token, + K const& key) { + return const_cast<F14BasicSet const*>(this)->find(token, key); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find( + F14HashToken const& token, + K const& key) const { + return table_.makeIter(table_.find(token, key)); + } + + FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const { + return !table_.find(key).atEnd(); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains( + K const& key) const { + return !table_.find(key).atEnd(); + } + + FOLLY_ALWAYS_INLINE bool contains( + F14HashToken const& token, + key_type const& key) const { + return !table_.find(token, key).atEnd(); + } + + template <typename K> + FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains( + F14HashToken const& token, + K const& key) const { + return !table_.find(token, key).atEnd(); + } + + std::pair<iterator, iterator> equal_range(key_type const& key) { + return equal_range(*this, key); + } + + std::pair<const_iterator, const_iterator> equal_range( + key_type const& key) const { + return equal_range(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range( + K const& key) { + return equal_range(*this, key); + } + + template <typename K> + EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>> + equal_range(K const& key) const { + return equal_range(*this, key); + } + + //// PUBLIC - Bucket interface + + std::size_t bucket_count() const noexcept { + return table_.bucket_count(); + } + + std::size_t max_bucket_count() const noexcept { + return table_.max_bucket_count(); + } + + //// PUBLIC - Hash policy + + float load_factor() const noexcept { + return table_.load_factor(); + } + + float max_load_factor() const noexcept { + return table_.max_load_factor(); + } + + void max_load_factor(float v) { + table_.max_load_factor(v); + } + + void rehash(std::size_t bucketCapacity) { + // The standard's rehash() requires understanding the max load factor, + // which is easy to get wrong. Since we don't actually allow adjustment + // of max_load_factor there is no difference. + reserve(bucketCapacity); + } + + void reserve(std::size_t capacity) { + table_.reserve(capacity); + } + + //// PUBLIC - Observers + + hasher hash_function() const { + return table_.hasher(); + } + + key_equal key_eq() const { + return table_.keyEqual(); + } + + //// PUBLIC - F14 Extensions + + // containsEqualValue returns true iff there is an element in the set + // that compares equal to key using operator==. It is undefined + // behjavior to call this function if operator== on key_type can ever + // return true when the same keys passed to key_eq() would return false + // (the opposite is allowed). When using the default key_eq this function + // is equivalent to contains(). + bool containsEqualValue(value_type const& value) const { + return !table_.findMatching(value, [&](auto& k) { return value == k; }) + .atEnd(); + } + + // Get memory footprint, not including sizeof(*this). + std::size_t getAllocatedMemorySize() const { + return table_.getAllocatedMemorySize(); + } + + // Enumerates classes of allocated memory blocks currently owned + // by this table, calling visitor(allocationSize, allocationCount). + // This can be used to get a more accurate indication of memory footprint + // than getAllocatedMemorySize() if you have some way of computing the + // internal fragmentation of the allocator, such as JEMalloc's nallocx. + // The visitor might be called twice with the same allocationSize. The + // visitor's computation should produce the same result for visitor(8, + // 2) as for two calls to visitor(8, 1), for example. The visitor may + // be called with a zero allocationCount. + template <typename V> + void visitAllocationClasses(V&& visitor) const { + return table_.visitAllocationClasses(visitor); + } + + // Calls visitor with two value_type const*, b and e, such that every + // entry in the table is included in exactly one of the ranges [b,e). + // This can be used to efficiently iterate elements in bulk when crossing + // an API boundary that supports contiguous blocks of items. + template <typename V> + void visitContiguousRanges(V&& visitor) const; + + F14TableStats computeStats() const noexcept { + return table_.computeStats(); + } + + private: + template <typename Self, typename K> + static auto equal_range(Self& self, K const& key) { + auto first = self.find(key); + auto last = first; + if (last != self.end()) { + ++last; + } + return std::make_pair(first, last); + } + + protected: + F14Table<Policy> table_; +}; +} // namespace detail +} // namespace f14 + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14ValueSet + : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults< + f14::detail::ValueContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc>> { + protected: + using Policy = f14::detail::SetPolicyWithDefaults< + f14::detail::ValueContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc>; + + private: + using Super = f14::detail::F14BasicSet<Policy>; + + public: + using typename Super::value_type; + + F14ValueSet() = default; + + using Super::Super; + + F14ValueSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14ValueSet& rhs) noexcept(Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + this->table_.visitContiguousItemRanges(std::forward<V>(visitor)); + } +}; + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14NodeSet + : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults< + f14::detail::NodeContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc>> { + protected: + using Policy = f14::detail::SetPolicyWithDefaults< + f14::detail::NodeContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc>; + + private: + using Super = f14::detail::F14BasicSet<Policy>; + + public: + using typename Super::value_type; + + F14NodeSet() = default; + + using Super::Super; + + F14NodeSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14NodeSet& rhs) noexcept(Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + this->table_.visitItems([&](typename Policy::Item ptr) { + value_type const* b = std::addressof(*ptr); + visitor(b, b + 1); + }); + } +}; + +namespace f14 { +namespace detail { +template < + typename Key, + typename Hasher, + typename KeyEqual, + typename Alloc, + typename EligibleForPerturbedInsertionOrder> +class F14VectorSetImpl : public F14BasicSet<SetPolicyWithDefaults< + VectorContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc, + EligibleForPerturbedInsertionOrder>> { + protected: + using Policy = SetPolicyWithDefaults< + VectorContainerPolicy, + Key, + Hasher, + KeyEqual, + Alloc, + EligibleForPerturbedInsertionOrder>; + + private: + using Super = F14BasicSet<Policy>; + + template <typename K, typename T> + using EnableHeterogeneousVectorErase = std::enable_if_t< + EligibleForHeterogeneousFind< + typename Policy::Value, + typename Policy::Hasher, + typename Policy::KeyEqual, + K>::value && + !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value && + !std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>::value, + T>; + + public: + using typename Super::const_iterator; + using typename Super::iterator; + using typename Super::key_type; + using typename Super::value_type; + + F14VectorSetImpl() = default; + + using Super::Super; + + F14VectorSetImpl& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + iterator begin() { + return cbegin(); + } + const_iterator begin() const { + return cbegin(); + } + const_iterator cbegin() const { + return this->table_.linearBegin(this->size()); + } + + iterator end() { + return cend(); + } + const_iterator end() const { + return cend(); + } + const_iterator cend() const { + return this->table_.linearEnd(); + } + + private: + template <typename BeforeDestroy> + void eraseUnderlying( + typename Policy::ItemIter underlying, + BeforeDestroy&& beforeDestroy) { + Alloc& a = this->table_.alloc(); + auto values = this->table_.values_; + + // destroy the value and remove the ptr from the base table + auto index = underlying.item(); + this->table_.eraseIterInto(underlying, beforeDestroy); + Policy::AllocTraits::destroy(a, std::addressof(values[index])); + + // move the last element in values_ down and fix up the inbound index + auto tailIndex = this->size(); + if (tailIndex != index) { + auto tail = this->table_.find( + VectorContainerIndexSearch{static_cast<uint32_t>(tailIndex)}); + tail.item() = index; + auto p = std::addressof(values[index]); + assume(p != nullptr); + this->table_.transfer(a, std::addressof(values[tailIndex]), p, 1); + } + } + + template <typename K, typename BeforeDestroy> + std::size_t eraseUnderlyingKey(K const& key, BeforeDestroy&& beforeDestroy) { + auto underlying = this->table_.find(key); + if (underlying.atEnd()) { + return 0; + } else { + eraseUnderlying(underlying, beforeDestroy); + return 1; + } + } + + public: + FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) { + return eraseInto(pos, [](value_type&&) {}); + } + + iterator erase(const_iterator first, const_iterator last) { + return eraseInto(first, last, [](value_type&&) {}); + } + + std::size_t erase(key_type const& key) { + return eraseInto(key, [](value_type&&) {}); + } + + template <typename K> + EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) { + return eraseInto(key, [](value_type&&) {}); + } + + template <typename BeforeDestroy> + FOLLY_ALWAYS_INLINE iterator + eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) { + auto underlying = this->table_.find( + VectorContainerIndexSearch{this->table_.iterToIndex(pos)}); + eraseUnderlying(underlying, beforeDestroy); + return ++pos; + } + + template <typename BeforeDestroy> + iterator eraseInto( + const_iterator first, + const_iterator last, + BeforeDestroy&& beforeDestroy) { + while (first != last) { + first = eraseInto(first, beforeDestroy); + } + return first; + } + + template <typename BeforeDestroy> + std::size_t eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) { + return eraseUnderlyingKey(key, beforeDestroy); + } + + template <typename K, typename BeforeDestroy> + EnableHeterogeneousVectorErase<K, std::size_t> eraseInto( + K const& key, + BeforeDestroy&& beforeDestroy) { + return eraseUnderlyingKey(key, beforeDestroy); + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + auto n = this->table_.size(); + if (n > 0) { + value_type const* b = std::addressof(this->table_.values_[0]); + visitor(b, b + n); + } + } +}; +} // namespace detail +} // namespace f14 + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14VectorSet + : public f14::detail:: + F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::false_type> { + using Super = f14::detail:: + F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::false_type>; + + public: + using typename Super::const_iterator; + using typename Super::iterator; + using typename Super::value_type; + using reverse_iterator = typename Super::Policy::ReverseIter; + using const_reverse_iterator = reverse_iterator; + + F14VectorSet() = default; + + using Super::Super; + + F14VectorSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14VectorSet& rhs) noexcept(Super::Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } + + // ITERATION ORDER + // + // Deterministic iteration order for insert-only workloads is part of + // F14VectorSet's supported API: iterator is LIFO and reverse_iterator + // is FIFO. + // + // If there have been no calls to erase() then iterator and + // const_iterator enumerate entries in the opposite of insertion order. + // begin()->first is the key most recently inserted. reverse_iterator + // and reverse_const_iterator, therefore, enumerate in LIFO (insertion) + // order for insert-only workloads. Deterministic iteration order is + // only guaranteed if no keys were removed since the last time the + // set was empty. Iteration order is preserved across rehashes and + // F14VectorSet copies and moves. + // + // iterator uses LIFO order so that erasing while iterating with begin() + // and end() is safe using the erase(it++) idiom, which is supported + // by std::set and std::unordered_set. erase(iter) invalidates iter + // and all iterators before iter in the non-reverse iteration order. + // Every successful erase invalidates all reverse iterators. + // + // No erase is provided for reverse_iterator (AKA const_reverse_iterator) + // to make it harder to shoot yourself in the foot by erasing while + // reverse-iterating. You can write that as set.erase(set.iter(riter)) + // if you need it. + + reverse_iterator rbegin() { + return this->table_.values_; + } + const_reverse_iterator rbegin() const { + return crbegin(); + } + const_reverse_iterator crbegin() const { + return this->table_.values_; + } + + reverse_iterator rend() { + return this->table_.values_ + this->table_.size(); + } + const_reverse_iterator rend() const { + return crend(); + } + const_reverse_iterator crend() const { + return this->table_.values_ + this->table_.size(); + } + + // explicit conversions between iterator and reverse_iterator + iterator iter(reverse_iterator riter) { + return this->table_.iter(riter); + } + const_iterator iter(const_reverse_iterator riter) const { + return this->table_.iter(riter); + } + + reverse_iterator riter(iterator it) { + return this->table_.riter(it); + } + const_reverse_iterator riter(const_iterator it) const { + return this->table_.riter(it); + } +}; + +template <typename K, typename H, typename E, typename A> +Range<typename F14VectorSet<K, H, E, A>::const_reverse_iterator> +order_preserving_reinsertion_view(const F14VectorSet<K, H, E, A>& c) { + return {c.rbegin(), c.rend()}; +} + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14FastSet + : public std::conditional_t< + sizeof(Key) < 24, + F14ValueSet<Key, Hasher, KeyEqual, Alloc>, + f14::detail:: + F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::true_type>> { + using Super = std::conditional_t< + sizeof(Key) < 24, + F14ValueSet<Key, Hasher, KeyEqual, Alloc>, + f14::detail:: + F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::true_type>>; + + public: + using typename Super::value_type; + + F14FastSet() = default; + + using Super::Super; + + F14FastSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } + + void swap(F14FastSet& rhs) noexcept(Super::Policy::kSwapIsNoexcept) { + this->table_.swap(rhs.table_); + } +}; +} // namespace folly + +#else // !if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +//////// Compatibility for unsupported platforms (not x86_64 and not aarch64) +#include <folly/container/detail/F14SetFallback.h> + +#endif // if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE else + +namespace folly { +namespace f14 { +namespace detail { +template <typename S> +bool setsEqual(S const& lhs, S const& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + for (auto& k : lhs) { + if (!rhs.containsEqualValue(k)) { + return false; + } + } + return true; +} +} // namespace detail +} // namespace f14 + +template <typename K, typename H, typename E, typename A> +bool operator==( + F14ValueSet<K, H, E, A> const& lhs, + F14ValueSet<K, H, E, A> const& rhs) { + return setsEqual(lhs, rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator!=( + F14ValueSet<K, H, E, A> const& lhs, + F14ValueSet<K, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator==( + F14NodeSet<K, H, E, A> const& lhs, + F14NodeSet<K, H, E, A> const& rhs) { + return setsEqual(lhs, rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator!=( + F14NodeSet<K, H, E, A> const& lhs, + F14NodeSet<K, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator==( + F14VectorSet<K, H, E, A> const& lhs, + F14VectorSet<K, H, E, A> const& rhs) { + return setsEqual(lhs, rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator!=( + F14VectorSet<K, H, E, A> const& lhs, + F14VectorSet<K, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator==( + F14FastSet<K, H, E, A> const& lhs, + F14FastSet<K, H, E, A> const& rhs) { + return setsEqual(lhs, rhs); +} + +template <typename K, typename H, typename E, typename A> +bool operator!=( + F14FastSet<K, H, E, A> const& lhs, + F14FastSet<K, H, E, A> const& rhs) { + return !(lhs == rhs); +} + +template <typename K, typename H, typename E, typename A> +void swap(F14ValueSet<K, H, E, A>& lhs, F14ValueSet<K, H, E, A>& rhs) noexcept( + noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename H, typename E, typename A> +void swap(F14NodeSet<K, H, E, A>& lhs, F14NodeSet<K, H, E, A>& rhs) noexcept( + noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename H, typename E, typename A> +void swap( + F14VectorSet<K, H, E, A>& lhs, + F14VectorSet<K, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename H, typename E, typename A> +void swap(F14FastSet<K, H, E, A>& lhs, F14FastSet<K, H, E, A>& rhs) noexcept( + noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +template <typename K, typename H, typename E, typename A, typename Pred> +void erase_if(F14ValueSet<K, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template <typename K, typename H, typename E, typename A, typename Pred> +void erase_if(F14NodeSet<K, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template <typename K, typename H, typename E, typename A, typename Pred> +void erase_if(F14VectorSet<K, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +template <typename K, typename H, typename E, typename A, typename Pred> +void erase_if(F14FastSet<K, H, E, A>& c, Pred pred) { + f14::detail::erase_if_impl(c, pred); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Foreach-inl.h b/ios/Pods/Flipper-Folly/folly/container/Foreach-inl.h new file mode 100644 index 000000000..0f788da75 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Foreach-inl.h @@ -0,0 +1,329 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cassert> +#include <cstdint> +#include <initializer_list> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> + +namespace folly { + +namespace for_each_detail { + +namespace adl { + +/* using override */ +using std::begin; +/* using override */ +using std::end; +/* using override */ +using std::get; + +/** + * The adl_ functions below lookup the function name in the namespace of the + * type of the object being passed into the function. If no function with that + * name exists for the passed object then the default std:: versions are going + * to be called + */ +template <std::size_t Index, typename Type> +auto adl_get(Type&& instance) -> decltype(get<Index>(std::declval<Type>())) { + return get<Index>(std::forward<Type>(instance)); +} +template <typename Type> +auto adl_begin(Type&& instance) -> decltype(begin(instance)) { + return begin(instance); +} +template <typename Type> +auto adl_end(Type&& instance) -> decltype(end(instance)) { + return end(instance); +} + +} // namespace adl + +/** + * Enable if the tuple supports fetching via a member get<>() + */ +template <typename T> +using EnableIfMemberGetFound = + void_t<decltype(std::declval<T>().template get<0>())>; +template <typename, typename T> +struct IsMemberGetFound : std::false_type {}; +template <typename T> +struct IsMemberGetFound<EnableIfMemberGetFound<T>, T> : std::true_type {}; + +/** + * A get that tries member get<> first and if that is not found tries ADL get<>. + * This mechanism is as found in the structured bindings proposal here 11.5.3. + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf + */ +template < + std::size_t Index, + typename Type, + std::enable_if_t<!IsMemberGetFound<void, Type>::value, int> = 0> +auto get_impl(Type&& instance) + -> decltype(adl::adl_get<Index>(static_cast<Type&&>(instance))) { + return adl::adl_get<Index>(static_cast<Type&&>(instance)); +} +template < + std::size_t Index, + typename Type, + std::enable_if_t<IsMemberGetFound<void, Type>::value, int> = 0> +auto get_impl(Type&& instance) + -> decltype(static_cast<Type&&>(instance).template get<Index>()) { + return static_cast<Type&&>(instance).template get<Index>(); +} + +/** + * Check if the sequence is a tuple + */ +template <typename Type, typename T = typename std::decay<Type>::type> +using EnableIfTuple = void_t< + decltype(get_impl<0>(std::declval<T>())), + decltype(std::tuple_size<T>::value)>; +template <typename, typename T> +struct IsTuple : std::false_type {}; +template <typename T> +struct IsTuple<EnableIfTuple<T>, T> : std::true_type {}; + +/** + * Check if the sequence is a range + */ +template <typename Type, typename T = typename std::decay<Type>::type> +using EnableIfRange = void_t< + decltype(adl::adl_begin(std::declval<T>())), + decltype(adl::adl_end(std::declval<T>()))>; +template <typename, typename T> +struct IsRange : std::false_type {}; +template <typename T> +struct IsRange<EnableIfRange<T>, T> : std::true_type {}; + +struct TupleTag {}; +struct RangeTag {}; + +/** + * Should ideally check if it is a tuple and if not return void, but msvc fails + */ +template <typename Sequence> +using SequenceTag = + std::conditional_t<IsRange<void, Sequence>::value, RangeTag, TupleTag>; + +struct BeginAddTag {}; +struct IndexingTag {}; + +template <typename Func, typename Item, typename Iter> +using ForEachImplTag = std::conditional_t< + is_invocable_v<Func, Item, index_constant<0>, Iter>, + index_constant<3>, + std::conditional_t< + is_invocable_v<Func, Item, index_constant<0>>, + index_constant<2>, + std::conditional_t< + is_invocable_v<Func, Item>, + index_constant<1>, + void>>>; + +template < + typename Func, + typename... Args, + std::enable_if_t<is_invocable_r_v<LoopControl, Func, Args...>, int> = 0> +LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) { + return static_cast<Func&&>(f)(static_cast<Args&&>(a)...); +} +template < + typename Func, + typename... Args, + std::enable_if_t<!is_invocable_r_v<LoopControl, Func, Args...>, int> = 0> +LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) { + static_assert( + std::is_void<invoke_result_t<Func, Args...>>::value, + "return either LoopControl or void"); + return static_cast<Func&&>(f)(static_cast<Args&&>(a)...), loop_continue; +} + +/** + * Implementations for the runtime function + */ +template <typename Sequence, typename Func> +void for_each_range_impl(index_constant<3>, Sequence&& range, Func& func) { + auto first = adl::adl_begin(range); + auto last = adl::adl_end(range); + for (auto index = std::size_t{0}; first != last; ++index) { + auto next = std::next(first); + auto control = invoke_returning_loop_control(func, *first, index, first); + if (loop_break == control) { + break; + } + first = next; + } +} +template <typename Sequence, typename Func> +void for_each_range_impl(index_constant<2>, Sequence&& range, Func& func) { + // make a three arg adaptor for the function passed in so that the main + // implementation function can be used + auto three_arg_adaptor = [&func]( + auto&& ele, auto index, auto) -> decltype(auto) { + return func(std::forward<decltype(ele)>(ele), index); + }; + for_each_range_impl( + index_constant<3>{}, std::forward<Sequence>(range), three_arg_adaptor); +} + +template <typename Sequence, typename Func> +void for_each_range_impl(index_constant<1>, Sequence&& range, Func& func) { + // make a three argument adaptor for the function passed in that just ignores + // the second and third argument + auto three_arg_adaptor = [&func](auto&& ele, auto, auto) -> decltype(auto) { + return func(std::forward<decltype(ele)>(ele)); + }; + for_each_range_impl( + index_constant<3>{}, std::forward<Sequence>(range), three_arg_adaptor); +} + +/** + * Handlers for iteration + */ +template <typename Sequence, typename Func, std::size_t... Indices> +void for_each_tuple_impl( + std::index_sequence<Indices...>, + Sequence&& seq, + Func& func) { + using _ = int[]; + + // unroll the loop in an initializer list construction parameter expansion + // pack + auto control = loop_continue; + + // cast to void to ignore the result; use the int[] initialization to do the + // loop execution, the ternary conditional will decide whether or not to + // evaluate the result + // + // if func does not return loop-control, expect the optimizer to see through + // invoke_returning_loop_control always returning loop_continue + void( + _{(((control == loop_continue) + ? (control = invoke_returning_loop_control( + func, + get_impl<Indices>(std::forward<Sequence>(seq)), + index_constant<Indices>{})) + : (loop_continue)), + 0)...}); +} + +/** + * The two top level compile time loop iteration functions handle the dispatch + * based on the number of arguments the passed in function can be passed, if 2 + * arguments can be passed then the implementation dispatches work further to + * the implementation classes above. If not then an adaptor is constructed + * which is passed on to the 2 argument specialization, which then in turn + * forwards implementation to the implementation classes above + */ +template <typename Sequence, typename Func> +void for_each_tuple_impl(index_constant<2>, Sequence&& seq, Func& func) { + // pass the length as an index sequence to the implementation as an + // optimization over manual template "tail recursion" unrolling + using size = std::tuple_size<typename std::decay<Sequence>::type>; + for_each_tuple_impl( + std::make_index_sequence<size::value>{}, + std::forward<Sequence>(seq), + func); +} +template <typename Sequence, typename Func> +void for_each_tuple_impl(index_constant<1>, Sequence&& seq, Func& func) { + // make an adaptor for the function passed in, in case it can only be passed + // on argument + auto two_arg_adaptor = [&func](auto&& ele, auto) -> decltype(auto) { + return func(std::forward<decltype(ele)>(ele)); + }; + for_each_tuple_impl( + index_constant<2>{}, std::forward<Sequence>(seq), two_arg_adaptor); +} + +/** + * Top level handlers for the for_each loop, with one overload for tuples and + * one overload for ranges + * + * This implies that if type is both a range and a tuple, it is treated as a + * range rather than as a tuple + */ +template <typename Sequence, typename Func> +void for_each_impl(TupleTag, Sequence&& range, Func& func) { + using type = decltype(get_impl<0>(std::declval<Sequence>())); + using tag = ForEachImplTag<Func, type, void>; + static_assert(!std::is_same<tag, void>::value, "unknown invocability"); + for_each_tuple_impl(tag{}, std::forward<Sequence>(range), func); +} +template <typename Sequence, typename Func> +void for_each_impl(RangeTag, Sequence&& range, Func& func) { + using iter = decltype(adl::adl_begin(std::declval<Sequence>())); + using type = decltype(*std::declval<iter>()); + using tag = ForEachImplTag<Func, type, iter>; + static_assert(!std::is_same<tag, void>::value, "unknown invocability"); + for_each_range_impl(tag{}, std::forward<Sequence>(range), func); +} + +template <typename Sequence, typename Index> +decltype(auto) fetch_impl(IndexingTag, Sequence&& sequence, Index&& index) { + return std::forward<Sequence>(sequence)[std::forward<Index>(index)]; +} +template <typename Sequence, typename Index> +decltype(auto) fetch_impl(BeginAddTag, Sequence&& sequence, Index index) { + return *(adl::adl_begin(std::forward<Sequence>(sequence)) + index); +} + +template <typename Sequence, typename Index> +decltype(auto) fetch_impl(TupleTag, Sequence&& sequence, Index index) { + return get_impl<index>(std::forward<Sequence>(sequence)); +} +template <typename Sequence, typename Index> +decltype(auto) fetch_impl(RangeTag, Sequence&& sequence, Index&& index) { + using iter = decltype(adl::adl_begin(std::declval<Sequence>())); + using iter_traits = std::iterator_traits<remove_cvref_t<iter>>; + using iter_cat = typename iter_traits::iterator_category; + using tag = std::conditional_t< + std::is_same<iter_cat, std::random_access_iterator_tag>::value, + BeginAddTag, + IndexingTag>; + return fetch_impl( + tag{}, std::forward<Sequence>(sequence), std::forward<Index>(index)); +} + +} // namespace for_each_detail + +template <typename Sequence, typename Func> +constexpr Func for_each(Sequence&& sequence, Func func) { + namespace fed = for_each_detail; + using tag = fed::SequenceTag<Sequence>; + fed::for_each_impl(tag{}, std::forward<Sequence>(sequence), func); + return func; +} + +template <typename Sequence, typename Index> +constexpr decltype(auto) fetch(Sequence&& sequence, Index&& index) { + namespace fed = for_each_detail; + using tag = fed::SequenceTag<Sequence>; + return for_each_detail::fetch_impl( + tag{}, std::forward<Sequence>(sequence), std::forward<Index>(index)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Foreach.h b/ios/Pods/Flipper-Folly/folly/container/Foreach.h new file mode 100644 index 000000000..e35a8f5eb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Foreach.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/Preprocessor.h> + +#include <type_traits> + +namespace folly { + +/** + * @function for_each + * + * folly::for_each is a generalized iteration algorithm. Example: + * + * auto one = std::make_tuple(1, 2, 3); + * auto two = std::vector<int>{1, 2, 3}; + * auto func = [](auto element, auto index) { + * cout << index << " : " << element << endl; + * }; + * folly::for_each(one, func); + * folly::for_each(two, func); + * + * The for_each function allows iteration through sequences, these can either be + * runtime sequences (i.e. entities for which std::begin and std::end work) or + * compile time sequences (as deemed by the presence of std::tuple_length<> and + * member get<> or ADL get<> functions). + * + * If a sequence type is both a runtime sequence (aka range) and a compile-time + * sequence (aka tuple), then it is treated as a range in preference to a tuple. + * An example of such a type is std::array. + * + * The function is made to provide a convenient library based alternative to the + * proposal p0589r0, which aims to generalize the range based for loop even + * further to work with compile time sequences. + * + * A drawback of using range based for loops is that sometimes you do not have + * access to the index within the range. This provides easy access to that, even + * with compile time sequences. + * + * And breaking out is easy: + * + * auto range_one = std::vector<int>{1, 2, 3}; + * auto range_two = std::make_tuple(1, 2, 3); + * auto func = [](auto ele, auto index) { + * cout << "Element at index " << index << " : " << ele; + * if (index == 1) { + * return folly::loop_break; + * } + * return folly::loop_continue; + * }; + * folly_for_each(range_one, func); + * folly_for_each(range_two, func); + * + * A simple use case would be when using futures, if the user was doing calls to + * n servers then they would accept the callback with the futures like this: + * + * auto vec = std::vector<std::future<int>>{request_one(), ...}; + * when_all(vec.begin(), vec.end()).then([](auto futures) { + * folly::for_each(futures, [](auto& fut) { ... }); + * }); + * + * Now when this code switches to use tuples instead of the runtime std::vector, + * then the loop does not need to change, the code will still work just fine: + * + * when_all(future_one, future_two, future_three).then([](auto futures) { + * folly::for_each(futures, [](auto& fut) { ... }); + * }); + */ +template <typename Range, typename Func> +constexpr Func for_each(Range&& range, Func func); + +/** + * The user should return loop_break and loop_continue if they want to iterate + * in such a way that they can preemptively stop the loop and break out when + * certain conditions are met. + */ +namespace for_each_detail { +enum class LoopControl : bool { BREAK, CONTINUE }; +} // namespace for_each_detail + +constexpr auto loop_break = for_each_detail::LoopControl::BREAK; +constexpr auto loop_continue = for_each_detail::LoopControl::CONTINUE; + +/** + * Utility method to help access elements of a sequence with one uniform + * interface. + * + * This can be useful for example when you are looping through a sequence and + * want to modify another sequence based on the information in the current + * sequence: + * + * auto range_one = std::make_tuple(1, 2, 3); + * auto range_two = std::make_tuple(4, 5, 6); + * folly::for_each(range_one, [&range_two](auto ele, auto index) { + * folly::fetch(range_two, index) = ele; + * }); + * + * For ranges, this works by first trying to use the iterator class if the + * iterator has been marked to be a random access iterator. This should be + * inspectable via the std::iterator_traits traits class. If the iterator class + * is not present or is not a random access iterator then the implementation + * falls back to trying to use the indexing operator (operator[]) to fetch the + * required element. + */ +template <typename Sequence, typename Index> +constexpr decltype(auto) fetch(Sequence&& sequence, Index&& index); + +} // namespace folly + +/** + * Everything below is deprecated. + */ + +/* + * Form a local variable name from "FOR_EACH_" x __LINE__, so that + * FOR_EACH can be nested without creating shadowed declarations. + */ +#define _FE_ANON(x) FB_CONCATENATE(FOR_EACH_, FB_CONCATENATE(x, __LINE__)) + +/* + * If you just want the element values, please use: + * + * for (auto&& element : collection) + * + * If you need access to the iterators please write an explicit iterator loop + */ +#define FOR_EACH(i, c) \ + if (bool _FE_ANON(s1_) = false) { \ + } else \ + for (auto&& _FE_ANON(s2_) = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = true) \ + for (auto i = _FE_ANON(s2_).begin(); i != _FE_ANON(s2_).end(); ++i) + +/* + * If you just want the element values, please use this (ranges-v3) construct: + * + * for (auto&& element : collection | views::reverse) + * + * If you need access to the iterators please write an explicit iterator loop + */ +#define FOR_EACH_R(i, c) \ + if (bool _FE_ANON(s1_) = false) { \ + } else \ + for (auto&& _FE_ANON(s2_) = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = true) \ + for (auto i = _FE_ANON(s2_).rbegin(); i != _FE_ANON(s2_).rend(); ++i) + +/* + * If you just want the element values, please use this construct: + * + * for (auto&& element : folly::enumerate(collection)) + * + * If you need access to the iterators please write an explicit iterator loop + * and use a counter variable + */ +#define FOR_EACH_ENUMERATE(count, i, c) \ + if (bool _FE_ANON(s1_) = false) { \ + } else \ + for (auto&& FOR_EACH_state2 = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = true) \ + if (size_t _FE_ANON(n1_) = 0) { \ + } else if (const size_t& count = _FE_ANON(n1_)) { \ + } else \ + for (auto i = FOR_EACH_state2.begin(); i != FOR_EACH_state2.end(); \ + ++_FE_ANON(n1_), ++i) +/** + * If you just want the keys, please use this (ranges-v3) construct: + * + * for (auto&& element : collection | views::keys) + * + * If you just want the values, please use this (ranges-v3) construct: + * + * for (auto&& element : collection | views::values) + * + * If you need to see both, use: + * + * for (auto&& element : collection) { + * auto const& key = element.first; + * auto& value = element.second; + * ...... + * } + * + */ +#define FOR_EACH_KV(k, v, c) \ + if (unsigned int _FE_ANON(s1_) = 0) { \ + } else \ + for (auto&& _FE_ANON(s2_) = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = 1) \ + for (auto _FE_ANON(s3_) = _FE_ANON(s2_).begin(); \ + _FE_ANON(s3_) != _FE_ANON(s2_).end(); \ + _FE_ANON(s1_) == 2 ? ((_FE_ANON(s1_) = 0), ++_FE_ANON(s3_)) \ + : (_FE_ANON(s3_) = _FE_ANON(s2_).end())) \ + for (auto& k = _FE_ANON(s3_)->first; !_FE_ANON(s1_); ++_FE_ANON(s1_)) \ + for (auto& v = _FE_ANON(s3_)->second; !_FE_ANON(s1_); ++_FE_ANON(s1_)) + +namespace folly { +namespace detail { + +/** + * notThereYet helps the FOR_EACH_RANGE macro by opportunistically + * using "<" instead of "!=" whenever available when checking for loop + * termination. This makes e.g. examples such as FOR_EACH_RANGE (i, + * 10, 5) execute zero iterations instead of looping virtually + * forever. At the same time, some iterator types define "!=" but not + * "<". The notThereYet function will dispatch differently for those. + * + * The code below uses `<` for a conservative subset of types for which + * it is known to be valid. + */ + +template <class T, class U> +typename std::enable_if< + (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) || + (std::is_pointer<T>::value && std::is_pointer<U>::value), + bool>::type +notThereYet(T& iter, const U& end) { + return iter < end; +} + +template <class T, class U> +typename std::enable_if< + !((std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) || + (std::is_pointer<T>::value && std::is_pointer<U>::value)), + bool>::type +notThereYet(T& iter, const U& end) { + return iter != end; +} + +} // namespace detail +} // namespace folly + +/* + * Look at the Ranges-v3 views and you'll probably find an easier way to build + * the view you want but the equivalent is roughly: + * + * for (auto& element : make_subrange(begin, end)) + */ +#define FOR_EACH_RANGE(i, begin, end) \ + for (auto i = (true ? (begin) : (end)); \ + ::folly::detail::notThereYet(i, (end)); \ + ++i) + +#include <folly/container/Foreach-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h b/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h new file mode 100644 index 000000000..bbe2a057e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +template <typename T, typename Enable = void> +struct HeterogeneousAccessEqualTo; + +template <typename T, typename Enable = void> +struct HeterogeneousAccessHash; + +template <typename CharT> +struct TransparentStringEqualTo; + +template <typename CharT> +struct TransparentStringHash; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess.h b/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess.h new file mode 100644 index 000000000..ffebe752e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/HeterogeneousAccess.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <string> + +#include <folly/Range.h> +#include <folly/Traits.h> +#include <folly/container/HeterogeneousAccess-fwd.h> +#include <folly/hash/Hash.h> + +namespace folly { + +// folly::HeterogeneousAccessEqualTo<T>, and +// folly::HeterogeneousAccessHash<T> are functors suitable as defaults +// for containers that support heterogeneous access. When possible, they +// will be marked as transparent. When no transparent implementation +// is available then they fall back to std::equal_to and std::hash +// respectively. Since the fallbacks are not marked as transparent, +// heterogeneous lookup won't be available in that case. A corresponding +// HeterogeneousAccessLess<T> could be easily added if desired. +// +// If T can be implicitly converted to a StringPiece or +// to a Range<T::value_type const*> that is hashable, then +// HeterogeneousAccess{EqualTo,Hash}<T> will be transparent without any +// additional work. In practice this is true for T that can be convered to +// StringPiece or Range<IntegralType const*>. This includes std::string, +// std::string_view (when available), std::array, folly::Range, +// std::vector, and folly::small_vector. +// +// Additional specializations of HeterogeneousAccess*<T> should go in +// the header that declares T. Don't forget to typedef is_transparent to +// void and folly_is_avalanching to std::true_type in the specializations. + +template <typename T, typename Enable> +struct HeterogeneousAccessEqualTo : std::equal_to<T> {}; + +template <typename T, typename Enable> +struct HeterogeneousAccessHash : std::hash<T> { + using folly_is_avalanching = IsAvalanchingHasher<std::hash<T>, T>; +}; + +//////// strings + +namespace detail { + +template <typename T, typename Enable = void> +struct ValueTypeForTransparentConversionToRange { + using type = char; +}; + +// We assume that folly::hasher<folly::Range<T const*>> won't be enabled +// when it would be lower quality than std::hash<U> for a U that is +// convertible to folly::Range<T const*>. +template <typename T> +struct ValueTypeForTransparentConversionToRange< + T, + void_t<decltype( + std::declval<hasher<Range<typename T::value_type const*>>>()( + std::declval<Range<typename T::value_type const*>>()))>> { + using type = std::remove_const_t<typename T::value_type>; +}; + +template <typename T> +using TransparentlyConvertibleToRange = std::is_convertible< + T, + Range<typename ValueTypeForTransparentConversionToRange<T>::type const*>>; + +template <typename T> +struct TransparentRangeEqualTo { + using is_transparent = void; + + template <typename U1, typename U2> + bool operator()(U1 const& lhs, U2 const& rhs) const { + return Range<T const*>{lhs} == Range<T const*>{rhs}; + } + + // This overload is not required for functionality, but + // guarantees that replacing std::equal_to<std::string> with + // HeterogeneousAccessEqualTo<std::string> is truly zero overhead + bool operator()(std::string const& lhs, std::string const& rhs) const { + return lhs == rhs; + } +}; + +template <typename T> +struct TransparentRangeHash { + using is_transparent = void; + using folly_is_avalanching = std::true_type; + + private: + template <typename U> + static std::size_t hashImpl(Range<U const*> piece) { + return hasher<Range<U const*>>{}(piece); + } + + static std::size_t hashImpl(StringPiece piece) { +#if defined(_GLIBCXX_STRING) + return std::_Hash_impl::hash(piece.begin(), piece.size()); +#elif defined(_LIBCPP_STRING) + return std::__do_string_hash(piece.begin(), piece.end()); +#else + return hasher<StringPiece>{}(piece); +#endif + } + + public: + template <typename U> + std::size_t operator()(U const& stringish) const { + return hashImpl(Range<T const*>{stringish}); + } + + // Neither this overload nor the platform-conditional compilation + // is required for functionality, but implementing it this + // way guarantees that replacing std::hash<std::string> with + // HeterogeneousAccessHash<std::string> is actually zero overhead + // in the case that the underlying implementations make different + // optimality tradeoffs (short versus long string performance, for + // example). If folly::hasher<StringPiece> dominated the performance + // of std::hash<std::string> then we should consider using it all of + // the time. + std::size_t operator()(std::string const& str) const { +#if defined(_GLIBCXX_STRING) || defined(_LIBCPP_STRING) + return std::hash<std::string>{}(str); +#else + return hasher<StringPiece>{}(str); +#endif + } +}; + +} // namespace detail + +template <typename T> +struct HeterogeneousAccessEqualTo< + T, + std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>> + : detail::TransparentRangeEqualTo< + typename detail::ValueTypeForTransparentConversionToRange<T>::type> { +}; + +template <typename T> +struct HeterogeneousAccessHash< + T, + std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>> + : detail::TransparentRangeHash< + typename detail::ValueTypeForTransparentConversionToRange<T>::type> { +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Iterator.h b/ios/Pods/Flipper-Folly/folly/container/Iterator.h new file mode 100644 index 000000000..294406fa8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Iterator.h @@ -0,0 +1,496 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <folly/Utility.h> +#include <folly/lang/RValueReferenceWrapper.h> + +namespace folly { + +/** + * Argument tuple for variadic emplace/constructor calls. Stores arguments by + * (decayed) value. Restores original argument types with reference qualifiers + * and adornments at unpack time to emulate perfect forwarding. + * + * Uses inheritance instead of a type alias to std::tuple so that emplace + * iterators with implicit unpacking disabled can distinguish between + * emplace_args and std::tuple parameters. + * + * @seealso folly::make_emplace_args + * @seealso folly::get_emplace_arg + */ +template <typename... Args> +struct emplace_args : public std::tuple<std::decay_t<Args>...> { + using storage_type = std::tuple<std::decay_t<Args>...>; + using storage_type::storage_type; +}; + +/** + * Pack arguments in a tuple for assignment to a folly::emplace_iterator, + * folly::front_emplace_iterator, or folly::back_emplace_iterator. The + * iterator's operator= will unpack the tuple and pass the unpacked arguments + * to the container's emplace function, which in turn forwards the arguments to + * the (multi-argument) constructor of the target class. + * + * Argument tuples generated with folly::make_emplace_args will be unpacked + * before being passed to the container's emplace function, even for iterators + * where implicit_unpack is set to false (so they will not implicitly unpack + * std::pair or std::tuple arguments to operator=). + * + * Arguments are copied (lvalues) or moved (rvalues). To avoid copies and moves, + * wrap references using std::ref(), std::cref(), and folly::rref(). Beware of + * dangling references, especially references to temporary objects created with + * folly::rref(). + * + * Note that an argument pack created with folly::make_emplace_args is different + * from an argument pack created with std::make_pair or std::make_tuple. + * Specifically, passing a std::pair&& or std::tuple&& to an emplace iterator's + * operator= will pass rvalue references to all fields of that tuple to the + * container's emplace function, while passing an emplace_args&& to operator= + * will cast those field references to the exact argument types as passed to + * folly::make_emplace_args previously. If all arguments have been wrapped by + * std::reference_wrappers or folly::rvalue_reference_wrappers, the result will + * be the same as if the container's emplace function had been called directly + * (perfect forwarding), with no temporary copies of the arguments. + * + * @seealso folly::rref + * + * @example + * class Widget { Widget(int, int); }; + * std::vector<Widget> makeWidgets(const std::vector<int>& in) { + * std::vector<Widget> out; + * std::transform( + * in.begin(), + * in.end(), + * folly::back_emplacer(out), + * [](int i) { return folly::make_emplace_args(i, i); }); + * return out; + * } + */ +template <typename... Args> +emplace_args<Args...> make_emplace_args(Args&&... args) noexcept( + noexcept(emplace_args<Args...>(std::forward<Args>(args)...))) { + return emplace_args<Args...>(std::forward<Args>(args)...); +} + +namespace detail { +template <typename Arg> +decltype(auto) unwrap_emplace_arg(Arg&& arg) noexcept { + return std::forward<Arg>(arg); +} +template <typename Arg> +decltype(auto) unwrap_emplace_arg(std::reference_wrapper<Arg> arg) noexcept { + return arg.get(); +} +template <typename Arg> +decltype(auto) unwrap_emplace_arg( + folly::rvalue_reference_wrapper<Arg> arg) noexcept { + return std::move(arg).get(); +} +} // namespace detail + +/** + * Getter function for unpacking a single emplace argument. + * + * Calling get_emplace_arg on an emplace_args rvalue reference results in + * perfect forwarding of the original input types. A special case are + * std::reference_wrapper and folly::rvalue_reference_wrapper objects within + * folly::emplace_args. These are also unwrapped so that the bare reference is + * returned. + * + * std::get is not a customization point in the standard library, so the + * cleanest solution was to define our own getter function. + */ +template <size_t I, typename... Args> +decltype(auto) get_emplace_arg(emplace_args<Args...>&& args) noexcept { + using Out = std::tuple<Args...>; + return detail::unwrap_emplace_arg( + std::forward<std::tuple_element_t<I, Out>>(std::get<I>(args))); +} +template <size_t I, typename... Args> +decltype(auto) get_emplace_arg(emplace_args<Args...>& args) noexcept { + return detail::unwrap_emplace_arg(std::get<I>(args)); +} +template <size_t I, typename... Args> +decltype(auto) get_emplace_arg(const emplace_args<Args...>& args) noexcept { + return detail::unwrap_emplace_arg(std::get<I>(args)); +} +template <size_t I, typename Args> +decltype(auto) get_emplace_arg(Args&& args) noexcept { + return std::get<I>(std::move(args)); +} +template <size_t I, typename Args> +decltype(auto) get_emplace_arg(Args& args) noexcept { + return std::get<I>(args); +} +template <size_t I, typename Args> +decltype(auto) get_emplace_arg(const Args& args) noexcept { + return std::get<I>(args); +} + +namespace detail { +/** + * Emplace implementation class for folly::emplace_iterator. + */ +template <typename Container> +struct Emplace { + Emplace(Container& c, typename Container::iterator i) + : container(std::addressof(c)), iter(std::move(i)) {} + template <typename... Args> + void emplace(Args&&... args) { + iter = container->emplace(iter, std::forward<Args>(args)...); + ++iter; + } + Container* container; + typename Container::iterator iter; +}; + +/** + * Emplace implementation class for folly::hint_emplace_iterator. + */ +template <typename Container> +struct EmplaceHint { + EmplaceHint(Container& c, typename Container::iterator i) + : container(std::addressof(c)), iter(std::move(i)) {} + template <typename... Args> + void emplace(Args&&... args) { + iter = container->emplace_hint(iter, std::forward<Args>(args)...); + ++iter; + } + Container* container; + typename Container::iterator iter; +}; + +/** + * Emplace implementation class for folly::front_emplace_iterator. + */ +template <typename Container> +struct EmplaceFront { + explicit EmplaceFront(Container& c) : container(std::addressof(c)) {} + template <typename... Args> + void emplace(Args&&... args) { + container->emplace_front(std::forward<Args>(args)...); + } + Container* container; +}; + +/** + * Emplace implementation class for folly::back_emplace_iterator. + */ +template <typename Container> +struct EmplaceBack { + explicit EmplaceBack(Container& c) : container(std::addressof(c)) {} + template <typename... Args> + void emplace(Args&&... args) { + container->emplace_back(std::forward<Args>(args)...); + } + Container* container; +}; + +/** + * Generic base class and implementation of all emplace iterator classes. + * + * Uses the curiously recurring template pattern (CRTP) to cast `this*` to + * `Derived*`; i.e., to implement covariant return types in a generic manner. + */ +template <typename Derived, typename EmplaceImpl, bool implicit_unpack> +class emplace_iterator_base; + +/** + * Partial specialization of emplace_iterator_base with implicit unpacking + * disabled. + */ +template <typename Derived, typename EmplaceImpl> +class emplace_iterator_base<Derived, EmplaceImpl, false> + : protected EmplaceImpl /* protected implementation inheritance */ { + public: + // Iterator traits. + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + using container_type = + std::remove_reference_t<decltype(*EmplaceImpl::container)>; + + using EmplaceImpl::EmplaceImpl; + + /** + * Canonical output operator. Forwards single argument straight to container's + * emplace function. + */ + template <typename T> + Derived& operator=(T&& arg) { + this->emplace(std::forward<T>(arg)); + return static_cast<Derived&>(*this); + } + + /** + * Special output operator for packed arguments. Unpacks args and performs + * variadic call to container's emplace function. + */ + template <typename... Args> + Derived& operator=(emplace_args<Args...>& args) { + return unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(const emplace_args<Args...>& args) { + return unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(emplace_args<Args...>&& args) { + return unpackAndEmplace( + std::move(args), std::index_sequence_for<Args...>{}); + } + + // No-ops. + Derived& operator*() { + return static_cast<Derived&>(*this); + } + Derived& operator++() { + return static_cast<Derived&>(*this); + } + Derived& operator++(int) { + return static_cast<Derived&>(*this); + } + + // We need all of these explicit defaults because the custom operator= + // overloads disable implicit generation of these functions. + emplace_iterator_base(const emplace_iterator_base&) = default; + emplace_iterator_base(emplace_iterator_base&&) noexcept = default; + emplace_iterator_base& operator=(emplace_iterator_base&) = default; + emplace_iterator_base& operator=(const emplace_iterator_base&) = default; + emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default; + + protected: + template <typename Args, std::size_t... I> + Derived& unpackAndEmplace(Args& args, std::index_sequence<I...>) { + this->emplace(get_emplace_arg<I>(args)...); + return static_cast<Derived&>(*this); + } + template <typename Args, std::size_t... I> + Derived& unpackAndEmplace(const Args& args, std::index_sequence<I...>) { + this->emplace(get_emplace_arg<I>(args)...); + return static_cast<Derived&>(*this); + } + template <typename Args, std::size_t... I> + Derived& unpackAndEmplace(Args&& args, std::index_sequence<I...>) { + this->emplace(get_emplace_arg<I>(std::move(args))...); + return static_cast<Derived&>(*this); + } +}; + +/** + * Partial specialization of emplace_iterator_base with implicit unpacking + * enabled. + * + * Uses inheritance rather than SFINAE. operator= requires a single argument, + * which makes it very tricky to use std::enable_if or similar. + */ +template <typename Derived, typename EmplaceImpl> +class emplace_iterator_base<Derived, EmplaceImpl, true> + : public emplace_iterator_base<Derived, EmplaceImpl, false> { + private: + using Base = emplace_iterator_base<Derived, EmplaceImpl, false>; + + public: + using Base::Base; + using Base::operator=; + + /** + * Special output operator for arguments packed into a std::pair. Unpacks + * the pair and performs variadic call to container's emplace function. + */ + template <typename... Args> + Derived& operator=(std::pair<Args...>& args) { + return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(const std::pair<Args...>& args) { + return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(std::pair<Args...>&& args) { + return this->unpackAndEmplace( + std::move(args), std::index_sequence_for<Args...>{}); + } + + /** + * Special output operator for arguments packed into a std::tuple. Unpacks + * the tuple and performs variadic call to container's emplace function. + */ + template <typename... Args> + Derived& operator=(std::tuple<Args...>& args) { + return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(const std::tuple<Args...>& args) { + return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{}); + } + template <typename... Args> + Derived& operator=(std::tuple<Args...>&& args) { + return this->unpackAndEmplace( + std::move(args), std::index_sequence_for<Args...>{}); + } + + // We need all of these explicit defaults because the custom operator= + // overloads disable implicit generation of these functions. + emplace_iterator_base(const emplace_iterator_base&) = default; + emplace_iterator_base(emplace_iterator_base&&) noexcept = default; + emplace_iterator_base& operator=(emplace_iterator_base&) = default; + emplace_iterator_base& operator=(const emplace_iterator_base&) = default; + emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default; +}; + +/** + * Concrete instantiation of emplace_iterator_base. All emplace iterator + * classes; folly::emplace_iterator, folly::hint_emplace_iterator, + * folly::front_emplace_iterator, and folly::back_emplace_iterator; are just + * type aliases of this class. + * + * It is not possible to alias emplace_iterator_base directly, because type + * aliases cannot be used for CRTP. + */ +template < + template <typename> class EmplaceImplT, + typename Container, + bool implicit_unpack> +class emplace_iterator_impl + : public emplace_iterator_base< + emplace_iterator_impl<EmplaceImplT, Container, implicit_unpack>, + EmplaceImplT<Container>, + implicit_unpack> { + private: + using Base = emplace_iterator_base< + emplace_iterator_impl, + EmplaceImplT<Container>, + implicit_unpack>; + + public: + using Base::Base; + using Base::operator=; + + // We need all of these explicit defaults because the custom operator= + // overloads disable implicit generation of these functions. + emplace_iterator_impl(const emplace_iterator_impl&) = default; + emplace_iterator_impl(emplace_iterator_impl&&) noexcept = default; + emplace_iterator_impl& operator=(emplace_iterator_impl&) = default; + emplace_iterator_impl& operator=(const emplace_iterator_impl&) = default; + emplace_iterator_impl& operator=(emplace_iterator_impl&&) noexcept = default; +}; +} // namespace detail + +/** + * Behaves just like std::insert_iterator except that it calls emplace() + * instead of insert(). Uses perfect forwarding. + */ +template <typename Container, bool implicit_unpack = true> +using emplace_iterator = + detail::emplace_iterator_impl<detail::Emplace, Container, implicit_unpack>; + +/** + * Behaves just like std::insert_iterator except that it calls emplace_hint() + * instead of insert(). Uses perfect forwarding. + */ +template <typename Container, bool implicit_unpack = true> +using hint_emplace_iterator = detail:: + emplace_iterator_impl<detail::EmplaceHint, Container, implicit_unpack>; + +/** + * Behaves just like std::front_insert_iterator except that it calls + * emplace_front() instead of insert(). Uses perfect forwarding. + */ +template <typename Container, bool implicit_unpack = true> +using front_emplace_iterator = detail:: + emplace_iterator_impl<detail::EmplaceFront, Container, implicit_unpack>; + +/** + * Behaves just like std::back_insert_iterator except that it calls + * emplace_back() instead of insert(). Uses perfect forwarding. + */ +template <typename Container, bool implicit_unpack = true> +using back_emplace_iterator = detail:: + emplace_iterator_impl<detail::EmplaceBack, Container, implicit_unpack>; + +/** + * Convenience function to construct a folly::emplace_iterator, analogous to + * std::inserter(). + * + * Setting implicit_unpack to false will disable implicit unpacking of + * single std::pair and std::tuple arguments to the iterator's operator=. That + * may be desirable in case of constructors that expect a std::pair or + * std::tuple argument. + */ +template <bool implicit_unpack = true, typename Container> +emplace_iterator<Container, implicit_unpack> emplacer( + Container& c, + typename Container::iterator i) { + return emplace_iterator<Container, implicit_unpack>(c, std::move(i)); +} + +/** + * Convenience function to construct a folly::hint_emplace_iterator, analogous + * to std::inserter(). + * + * Setting implicit_unpack to false will disable implicit unpacking of + * single std::pair and std::tuple arguments to the iterator's operator=. That + * may be desirable in case of constructors that expect a std::pair or + * std::tuple argument. + */ +template <bool implicit_unpack = true, typename Container> +hint_emplace_iterator<Container, implicit_unpack> hint_emplacer( + Container& c, + typename Container::iterator i) { + return hint_emplace_iterator<Container, implicit_unpack>(c, std::move(i)); +} + +/** + * Convenience function to construct a folly::front_emplace_iterator, analogous + * to std::front_inserter(). + * + * Setting implicit_unpack to false will disable implicit unpacking of + * single std::pair and std::tuple arguments to the iterator's operator=. That + * may be desirable in case of constructors that expect a std::pair or + * std::tuple argument. + */ +template <bool implicit_unpack = true, typename Container> +front_emplace_iterator<Container, implicit_unpack> front_emplacer( + Container& c) { + return front_emplace_iterator<Container, implicit_unpack>(c); +} + +/** + * Convenience function to construct a folly::back_emplace_iterator, analogous + * to std::back_inserter(). + * + * Setting implicit_unpack to false will disable implicit unpacking of + * single std::pair and std::tuple arguments to the iterator's operator=. That + * may be desirable in case of constructors that expect a std::pair or + * std::tuple argument. + */ +template <bool implicit_unpack = true, typename Container> +back_emplace_iterator<Container, implicit_unpack> back_emplacer(Container& c) { + return back_emplace_iterator<Container, implicit_unpack>(c); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/Merge.h b/ios/Pods/Flipper-Folly/folly/container/Merge.h new file mode 100644 index 000000000..b10cda73f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/Merge.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * folly::merge() is an implementation of std::merge with one additonal + * guarantee: if the input ranges overlap, the order that values *from the two + * different ranges* appear in the output is well defined (std::merge only + * guarantees relative ordering is maintained within a single input range). + * This semantic is very useful when the output container removes duplicates + * (such as std::map) to guarantee that elements from b override elements from + * a. + * + * ex. Let's say we have two vector<pair<int, int>> as input, and we are + * merging into a vector<pair<int, int>>. The comparator is returns true if the + * first argument has a lesser 'first' value in the pair. + * + * a = {{1, 1}, {2, 2}, {3, 3}}; + * b = {{1, 2}, {2, 3}}; + * + * folly::merge<...>(a.begin(), a.end(), b.begin(), b.end(), outputIter) is + * guaranteed to produce {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}}. That is, + * if comp(it_a, it_b) == comp(it_b, it_a) == false, we first insert the element + * from a. + */ + +#pragma once + +#include <algorithm> + +namespace folly { + +template <class InputIt1, class InputIt2, class OutputIt, class Compare> +OutputIt merge( + InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first, + Compare comp) { + for (; first1 != last1; ++d_first) { + if (first2 == last2) { + return std::copy(first1, last1, d_first); + } + if (comp(*first2, *first1)) { + *d_first = *first2; + ++first2; + } else { + *d_first = *first1; + ++first1; + } + } + return std::copy(first2, last2, d_first); +} + +template <class InputIt1, class InputIt2, class OutputIt> +OutputIt merge( + InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first) { + for (; first1 != last1; ++d_first) { + if (first2 == last2) { + return std::copy(first1, last1, d_first); + } + if (*first2 < *first1) { + *d_first = *first2; + ++first2; + } else { + *d_first = *first1; + ++first1; + } + } + return std::copy(first2, last2, d_first); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/SparseByteSet.h b/ios/Pods/Flipper-Folly/folly/container/SparseByteSet.h new file mode 100644 index 000000000..7bf7a8a12 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/SparseByteSet.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +#include <glog/logging.h> + +namespace folly { + +/*** + * SparseByteSet + * + * A special-purpose data structure representing an insert-only set of bytes. + * May have better performance than std::bitset<256>, depending on workload. + * + * Operations: + * - add(byte) + * - contains(byte) + * + * Performance: + * - The entire capacity of the set is inline; the set never allocates. + * - The constructor zeros only the first two bytes of the object. + * - add and contains both run in constant time w.r.t. the size of the set. + * Constant time - not amortized constant - and with small constant factor. + * + * This data structure is ideal for on-stack use. + * + * Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis + * of Computer Algorithms" (1974), but the best description is here: + * http://research.swtch.com/sparse + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7319 + */ +class SparseByteSet { + public: + // There are this many possible values: + static constexpr uint16_t kCapacity = 256; + + // No init of byte-arrays required! + SparseByteSet() : size_(0) {} + + /*** + * add(byte) + * + * O(1), non-amortized. + */ + inline bool add(uint8_t i) { + bool r = !contains(i); + if (r) { + DCHECK_LT(size_, kCapacity); + dense_[size_] = i; + sparse_[i] = uint8_t(size_); + size_++; + } + return r; + } + + /*** + * contains(byte) + * + * O(1), non-amortized. + */ + inline bool contains(uint8_t i) const { + return sparse_[i] < size_ && dense_[sparse_[i]] == i; + } + + private: + uint16_t size_; // can't use uint8_t because it would overflow if all + // possible values were inserted. + uint8_t sparse_[kCapacity]; + uint8_t dense_[kCapacity]; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/BitIteratorDetail.h b/ios/Pods/Flipper-Folly/folly/container/detail/BitIteratorDetail.h new file mode 100644 index 000000000..8a5b4ee70 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/BitIteratorDetail.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <iterator> +#include <type_traits> + +#include <boost/iterator/iterator_adaptor.hpp> + +namespace folly { + +template <class BaseIter> +class BitIterator; + +namespace bititerator_detail { + +// Reference to a bit. +// Templatize on both parent reference and value types to capture +// const-ness correctly and to work with the case where Ref is a +// reference-like type (not T&), just like our BitReference here. +template <class Ref, class Value> +class BitReference { + public: + BitReference(Ref r, size_t bit) : ref_(r), bit_(bit) {} + + /* implicit */ operator bool() const { + return ref_ & (one_ << bit_); + } + + BitReference& operator=(bool b) { + if (b) { + set(); + } else { + clear(); + } + return *this; + } + + void set() { + ref_ |= (one_ << bit_); + } + + void clear() { + ref_ &= ~(one_ << bit_); + } + + void flip() { + ref_ ^= (one_ << bit_); + } + + private: + // shortcut to avoid writing static_cast everywhere + const static Value one_ = 1; + + Ref ref_; + size_t bit_; +}; + +template <class BaseIter> +struct BitIteratorBase { + static_assert( + std::is_integral< + typename std::iterator_traits<BaseIter>::value_type>::value, + "BitIterator may only be used with integral types"); + typedef boost::iterator_adaptor< + BitIterator<BaseIter>, // Derived + BaseIter, // Base + bool, // Value + typename std::iterator_traits< + BaseIter>::iterator_category, // CategoryOrTraversal + bititerator_detail::BitReference< + typename std::iterator_traits<BaseIter>::reference, + typename std::iterator_traits<BaseIter>::value_type>, // Reference + ssize_t> + type; +}; + +} // namespace bititerator_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14Defaults.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14Defaults.h new file mode 100644 index 000000000..bfc5a2839 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14Defaults.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/container/HeterogeneousAccess-fwd.h> + +namespace folly { +namespace f14 { +template <typename T> +using DefaultHasher = HeterogeneousAccessHash<T>; + +template <typename T> +using DefaultKeyEqual = HeterogeneousAccessEqualTo<T>; + +template <typename T> +using DefaultAlloc = std::allocator<T>; +} // namespace f14 +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h new file mode 100644 index 000000000..c917c5dba --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +// F14 has been implemented for SSE2 and NEON (so far). +// +// This platform detection is a bit of a mess because it combines the +// detection of supported platforms (FOLLY_SSE >= 2 || FOLLY_NEON) with +// the selection of platforms on which we want to use it. +// +// Currently no 32-bit ARM versions are desired because we don't want to +// need a separate build for chips that don't have NEON. AARCH64 support +// is enabled for non-mobile platforms, but on mobile platforms there +// are downstream iteration order effects that have not yet been resolved. +// +// If FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE differs across compilation +// units the program will fail to link due to a missing definition of +// folly::container::detail::F14LinkCheck<X>::check() for some X. +#if (FOLLY_SSE >= 2 || (FOLLY_NEON && FOLLY_AARCH64)) && !FOLLY_MOBILE +#define FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE 1 +#else +#define FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE 0 +#endif + +#if FOLLY_SSE_PREREQ(4, 2) || __ARM_FEATURE_CRC32 +#define FOLLY_F14_CRC_INTRINSIC_AVAILABLE 1 +#else +#define FOLLY_F14_CRC_INTRINSIC_AVAILABLE 0 +#endif + +namespace folly { +namespace f14 { +namespace detail { + +enum class F14IntrinsicsMode { None, Simd, SimdAndCrc }; + +static constexpr F14IntrinsicsMode getF14IntrinsicsMode() { +#if !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + return F14IntrinsicsMode::None; +#elif !FOLLY_F14_CRC_INTRINSIC_AVAILABLE + return F14IntrinsicsMode::Simd; +#else + return F14IntrinsicsMode::SimdAndCrc; +#endif +} + +} // namespace detail +} // namespace f14 +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14MapFallback.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14MapFallback.h new file mode 100644 index 000000000..1c4942572 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14MapFallback.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/** + * This file is intended to be included only by F14Map.h. It contains fallback + * implementations of F14Map types for platforms that do not support the + * required SIMD instructions, based on std::unordered_map. + */ + +#include <unordered_map> + +namespace folly { + +namespace f14 { +namespace detail { +template <typename K, typename M, typename H, typename E, typename A> +class F14BasicMap : public std::unordered_map<K, M, H, E, A> { + using Super = std::unordered_map<K, M, H, E, A>; + + public: + using typename Super::pointer; + using typename Super::value_type; + + F14BasicMap() = default; + + using Super::Super; + + //// PUBLIC - F14 Extensions + + bool containsEqualValue(value_type const& value) const { + auto slot = this->bucket(value.first); + auto e = this->end(slot); + for (auto b = this->begin(slot); b != e; ++b) { + if (b->first == value.first) { + return b->second == value.second; + } + } + return false; + } + + // exact for libstdc++, approximate for others + std::size_t getAllocatedMemorySize() const { + std::size_t rv = 0; + visitAllocationClasses( + [&](std::size_t bytes, std::size_t n) { rv += bytes * n; }); + return rv; + } + + // exact for libstdc++, approximate for others + template <typename V> + void visitAllocationClasses(V&& visitor) const { + auto bc = this->bucket_count(); + if (bc > 1) { + visitor(bc * sizeof(pointer), 1); + } + if (this->size() > 0) { + visitor(sizeof(StdNodeReplica<K, value_type, H>), this->size()); + } + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + for (value_type const& entry : *this) { + value_type const* b = std::addressof(entry); + visitor(b, b + 1); + } + } +}; +} // namespace detail +} // namespace f14 + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14ValueMap + : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14ValueMap() = default; + + using Super::Super; + + F14ValueMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14NodeMap + : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14NodeMap() = default; + + using Super::Super; + + F14NodeMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14VectorMap + : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14VectorMap() = default; + + using Super::Super; + + F14VectorMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template < + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc> +class F14FastMap + : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14FastMap() = default; + + using Super::Super; + + F14FastMap& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14Mask.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14Mask.h new file mode 100644 index 000000000..ec301a8c1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14Mask.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <cstdint> + +#include <folly/Bits.h> +#include <folly/ConstexprMath.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/lang/Assume.h> +#include <folly/lang/SafeAssert.h> + +#if (FOLLY_SSE >= 2 || (FOLLY_NEON && FOLLY_AARCH64)) && !FOLLY_MOBILE + +namespace folly { +namespace f14 { +namespace detail { + +template <typename T> +FOLLY_ALWAYS_INLINE static unsigned findFirstSetNonZero(T mask) { + assume(mask != 0); + if (sizeof(mask) == sizeof(unsigned)) { + return __builtin_ctz(static_cast<unsigned>(mask)); + } else { + return __builtin_ctzll(mask); + } +} + +#if FOLLY_NEON +using MaskType = uint64_t; + +constexpr unsigned kMaskSpacing = 4; +#else // SSE2 +using MaskType = uint32_t; + +constexpr unsigned kMaskSpacing = 1; +#endif + +template <unsigned BitCount> +struct FullMask { + static constexpr MaskType value = + (FullMask<BitCount - 1>::value << kMaskSpacing) + 1; +}; + +template <> +struct FullMask<1> : std::integral_constant<MaskType, 1> {}; + +#if FOLLY_ARM +// Mask iteration is different for ARM because that is the only platform +// for which the mask is bigger than a register. + +// Iterates a mask, optimized for the case that only a few bits are set +class SparseMaskIter { + static_assert(kMaskSpacing == 4, ""); + + uint32_t interleavedMask_; + + public: + explicit SparseMaskIter(MaskType mask) + : interleavedMask_{static_cast<uint32_t>(((mask >> 32) << 2) | mask)} {} + + bool hasNext() { + return interleavedMask_ != 0; + } + + unsigned next() { + FOLLY_SAFE_DCHECK(hasNext(), ""); + unsigned i = findFirstSetNonZero(interleavedMask_); + interleavedMask_ &= (interleavedMask_ - 1); + return ((i >> 2) | (i << 2)) & 0xf; + } +}; + +// Iterates a mask, optimized for the case that most bits are set +class DenseMaskIter { + static_assert(kMaskSpacing == 4, ""); + + std::size_t count_; + unsigned index_; + uint8_t const* tags_; + + public: + explicit DenseMaskIter(uint8_t const* tags, MaskType mask) { + if (mask == 0) { + count_ = 0; + } else { + count_ = popcount(static_cast<uint32_t>(((mask >> 32) << 2) | mask)); + if (LIKELY((mask & 1) != 0)) { + index_ = 0; + } else { + index_ = findFirstSetNonZero(mask) / kMaskSpacing; + } + tags_ = tags; + } + } + + bool hasNext() { + return count_ > 0; + } + + unsigned next() { + auto rv = index_; + --count_; + if (count_ > 0) { + do { + ++index_; + } while ((tags_[index_] & 0x80) == 0); + } + FOLLY_SAFE_DCHECK(index_ < 16, ""); + return rv; + } +}; + +#else +// Iterates a mask, optimized for the case that only a few bits are set +class SparseMaskIter { + MaskType mask_; + + public: + explicit SparseMaskIter(MaskType mask) : mask_{mask} {} + + bool hasNext() { + return mask_ != 0; + } + + unsigned next() { + FOLLY_SAFE_DCHECK(hasNext(), ""); + unsigned i = findFirstSetNonZero(mask_); + mask_ &= (mask_ - 1); + return i / kMaskSpacing; + } +}; + +// Iterates a mask, optimized for the case that most bits are set +class DenseMaskIter { + MaskType mask_; + unsigned index_{0}; + + public: + explicit DenseMaskIter(uint8_t const*, MaskType mask) : mask_{mask} {} + + bool hasNext() { + return mask_ != 0; + } + + unsigned next() { + FOLLY_SAFE_DCHECK(hasNext(), ""); + if (LIKELY((mask_ & 1) != 0)) { + mask_ >>= kMaskSpacing; + return index_++; + } else { + unsigned s = findFirstSetNonZero(mask_); + unsigned rv = index_ + (s / kMaskSpacing); + mask_ >>= (s + kMaskSpacing); + index_ = rv + 1; + return rv; + } + } +}; +#endif + +// Iterates a mask, returning pairs of [begin,end) index covering blocks +// of set bits +class MaskRangeIter { + MaskType mask_; + unsigned shift_{0}; + + public: + explicit MaskRangeIter(MaskType mask) { + // If kMaskSpacing is > 1 then there will be empty bits even for + // contiguous ranges. Fill them in. + mask_ = mask * ((1 << kMaskSpacing) - 1); + } + + bool hasNext() { + return mask_ != 0; + } + + std::pair<unsigned, unsigned> next() { + FOLLY_SAFE_DCHECK(hasNext(), ""); + auto s = shift_; + unsigned b = findFirstSetNonZero(mask_); + unsigned e = findFirstSetNonZero(~(mask_ | (mask_ - 1))); + mask_ >>= e; + shift_ = s + e; + return std::make_pair((s + b) / kMaskSpacing, (s + e) / kMaskSpacing); + } +}; + +// Holds the result of an index query that has an optional result, +// interpreting a mask of 0 to be the empty answer and the index of the +// last set bit to be the non-empty answer +class LastOccupiedInMask { + MaskType mask_; + + public: + explicit LastOccupiedInMask(MaskType mask) : mask_{mask} {} + + bool hasIndex() const { + return mask_ != 0; + } + + unsigned index() const { + assume(mask_ != 0); + return (findLastSet(mask_) - 1) / kMaskSpacing; + } +}; + +// Holds the result of an index query that has an optional result, +// interpreting a mask of 0 to be the empty answer and the index of the +// first set bit to be the non-empty answer +class FirstEmptyInMask { + MaskType mask_; + + public: + explicit FirstEmptyInMask(MaskType mask) : mask_{mask} {} + + bool hasIndex() const { + return mask_ != 0; + } + + unsigned index() const { + FOLLY_SAFE_DCHECK(mask_ != 0, ""); + return findFirstSetNonZero(mask_) / kMaskSpacing; + } +}; + +} // namespace detail +} // namespace f14 +} // namespace folly + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14Policy.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14Policy.h new file mode 100644 index 000000000..124f3fa5c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14Policy.h @@ -0,0 +1,1585 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <type_traits> +#include <utility> + +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Unit.h> +#include <folly/container/HeterogeneousAccess.h> +#include <folly/container/detail/F14Table.h> +#include <folly/hash/Hash.h> +#include <folly/lang/Align.h> +#include <folly/lang/SafeAssert.h> +#include <folly/memory/Malloc.h> + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +namespace folly { +namespace f14 { +namespace detail { + +template <typename Ptr> +using NonConstPtr = typename std::pointer_traits<Ptr>::template rebind< + std::remove_const_t<typename std::pointer_traits<Ptr>::element_type>>; + +template <typename KeyType, typename MappedType> +using MapValueType = std::pair<KeyType const, MappedType>; + +template <typename KeyType, typename MappedTypeOrVoid> +using SetOrMapValueType = std::conditional_t< + std::is_same<MappedTypeOrVoid, void>::value, + KeyType, + MapValueType<KeyType, MappedTypeOrVoid>>; + +template <typename T> +using IsNothrowMoveAndDestroy = Conjunction< + std::is_nothrow_move_constructible<T>, + std::is_nothrow_destructible<T>>; + +// Used to enable EBO for Hasher, KeyEqual, and Alloc. std::tuple of +// all empty objects is empty in libstdc++ but not libc++. +template < + char Tag, + typename T, + bool Inherit = std::is_empty<T>::value && !std::is_final<T>::value> +struct ObjectHolder { + T value_; + + template <typename... Args> + ObjectHolder(Args&&... args) : value_{std::forward<Args>(args)...} {} + + T& operator*() { + return value_; + } + T const& operator*() const { + return value_; + } +}; + +template <char Tag, typename T> +struct ObjectHolder<Tag, T, true> : T { + template <typename... Args> + ObjectHolder(Args&&... args) : T{std::forward<Args>(args)...} {} + + T& operator*() { + return *this; + } + T const& operator*() const { + return *this; + } +}; + +// Policy provides the functionality of hasher, key_equal, and +// allocator_type. In addition, it can add indirection to the values +// contained in the base table by defining a non-trivial value() method. +// +// To facilitate stateful implementations it is guaranteed that there +// will be a 1:1 relationship between BaseTable and Policy instance: +// policies will only be copied when their owning table is copied, and +// they will only be moved when their owning table is moved. +// +// Key equality will have the user-supplied search key as its first +// argument and the table contents as its second. Heterogeneous lookup +// should be handled on the first argument. +// +// Item is the data stored inline in the hash table's chunks. The policy +// controls how this is mapped to the corresponding Value. +// +// The policies defined in this file work for either set or map types. +// Most of the functionality is identical. A few methods detect the +// collection type by checking to see if MappedType is void, and then use +// SFINAE to select the appropriate implementation. +template < + typename KeyType, + typename MappedTypeOrVoid, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid, + typename ItemType> +struct BasePolicy + : private ObjectHolder< + 'H', + Defaulted<HasherOrVoid, DefaultHasher<KeyType>>>, + private ObjectHolder< + 'E', + Defaulted<KeyEqualOrVoid, DefaultKeyEqual<KeyType>>>, + private ObjectHolder< + 'A', + Defaulted< + AllocOrVoid, + DefaultAlloc<SetOrMapValueType<KeyType, MappedTypeOrVoid>>>> { + //////// user-supplied types + + using Key = KeyType; + using Mapped = MappedTypeOrVoid; + using Value = SetOrMapValueType<Key, Mapped>; + using Item = ItemType; + using Hasher = Defaulted<HasherOrVoid, DefaultHasher<Key>>; + using KeyEqual = Defaulted<KeyEqualOrVoid, DefaultKeyEqual<Key>>; + using Alloc = Defaulted<AllocOrVoid, DefaultAlloc<Value>>; + using AllocTraits = std::allocator_traits<Alloc>; + + using ByteAlloc = typename AllocTraits::template rebind_alloc<uint8_t>; + using ByteAllocTraits = typename std::allocator_traits<ByteAlloc>; + using BytePtr = typename ByteAllocTraits::pointer; + + //////// info about user-supplied types + + static_assert( + std::is_same<typename AllocTraits::value_type, Value>::value, + "wrong allocator value_type"); + + private: + using HasherHolder = ObjectHolder<'H', Hasher>; + using KeyEqualHolder = ObjectHolder<'E', KeyEqual>; + using AllocHolder = ObjectHolder<'A', Alloc>; + + // emulate c++17's std::allocator_traits<A>::is_always_equal + + template <typename A, typename = void> + struct AllocIsAlwaysEqual : std::is_empty<A> {}; + + template <typename A> + struct AllocIsAlwaysEqual<A, typename A::is_always_equal> + : A::is_always_equal {}; + + public: + static constexpr bool kAllocIsAlwaysEqual = AllocIsAlwaysEqual<Alloc>::value; + + static constexpr bool kDefaultConstructIsNoexcept = + std::is_nothrow_default_constructible<Hasher>::value && + std::is_nothrow_default_constructible<KeyEqual>::value && + std::is_nothrow_default_constructible<Alloc>::value; + + static constexpr bool kSwapIsNoexcept = kAllocIsAlwaysEqual && + IsNothrowSwappable<Hasher>{} && IsNothrowSwappable<KeyEqual>{}; + + static constexpr bool isAvalanchingHasher() { + return IsAvalanchingHasher<Hasher, Key>::value; + } + + //////// internal types and constants + + using InternalSizeType = std::size_t; + + // if false, F14Table will be smaller but F14Table::begin() won't work + static constexpr bool kEnableItemIteration = true; + + using Chunk = F14Chunk<Item>; + using ChunkPtr = typename std::pointer_traits< + typename AllocTraits::pointer>::template rebind<Chunk>; + using ItemIter = F14ItemIter<ChunkPtr>; + + static constexpr bool kIsMap = !std::is_same<Key, Value>::value; + static_assert( + kIsMap == !std::is_void<MappedTypeOrVoid>::value, + "Assumption for the kIsMap check violated."); + + using MappedOrBool = std::conditional_t<kIsMap, Mapped, bool>; + + // if true, bucket_count() after reserve(n) will be as close as possible + // to n for multi-chunk tables + static constexpr bool kContinuousCapacity = false; + + //////// methods + + BasePolicy(Hasher const& hasher, KeyEqual const& keyEqual, Alloc const& alloc) + : HasherHolder{hasher}, KeyEqualHolder{keyEqual}, AllocHolder{alloc} {} + + BasePolicy(BasePolicy const& rhs) + : HasherHolder{rhs.hasher()}, + KeyEqualHolder{rhs.keyEqual()}, + AllocHolder{ + AllocTraits::select_on_container_copy_construction(rhs.alloc())} {} + + BasePolicy(BasePolicy const& rhs, Alloc const& alloc) + : HasherHolder{rhs.hasher()}, + KeyEqualHolder{rhs.keyEqual()}, + AllocHolder{alloc} {} + + BasePolicy(BasePolicy&& rhs) noexcept + : HasherHolder{std::move(rhs.hasher())}, + KeyEqualHolder{std::move(rhs.keyEqual())}, + AllocHolder{std::move(rhs.alloc())} {} + + BasePolicy(BasePolicy&& rhs, Alloc const& alloc) noexcept + : HasherHolder{std::move(rhs.hasher())}, + KeyEqualHolder{std::move(rhs.keyEqual())}, + AllocHolder{alloc} {} + + private: + template <typename Src> + void maybeAssignAlloc(std::true_type, Src&& src) { + alloc() = std::forward<Src>(src); + } + + template <typename Src> + void maybeAssignAlloc(std::false_type, Src&&) {} + + template <typename A> + void maybeSwapAlloc(std::true_type, A& rhs) { + using std::swap; + swap(alloc(), rhs); + } + + template <typename A> + void maybeSwapAlloc(std::false_type, A&) {} + + public: + BasePolicy& operator=(BasePolicy const& rhs) { + hasher() = rhs.hasher(); + keyEqual() = rhs.keyEqual(); + maybeAssignAlloc( + typename AllocTraits::propagate_on_container_copy_assignment{}, + rhs.alloc()); + return *this; + } + + BasePolicy& operator=(BasePolicy&& rhs) noexcept { + hasher() = std::move(rhs.hasher()); + keyEqual() = std::move(rhs.keyEqual()); + maybeAssignAlloc( + typename AllocTraits::propagate_on_container_move_assignment{}, + std::move(rhs.alloc())); + return *this; + } + + void swapBasePolicy(BasePolicy& rhs) { + using std::swap; + swap(hasher(), rhs.hasher()); + swap(keyEqual(), rhs.keyEqual()); + maybeSwapAlloc( + typename AllocTraits::propagate_on_container_swap{}, rhs.alloc()); + } + + Hasher& hasher() { + return *static_cast<HasherHolder&>(*this); + } + Hasher const& hasher() const { + return *static_cast<HasherHolder const&>(*this); + } + KeyEqual& keyEqual() { + return *static_cast<KeyEqualHolder&>(*this); + } + KeyEqual const& keyEqual() const { + return *static_cast<KeyEqualHolder const&>(*this); + } + Alloc& alloc() { + return *static_cast<AllocHolder&>(*this); + } + Alloc const& alloc() const { + return *static_cast<AllocHolder const&>(*this); + } + + template <typename K> + std::size_t computeKeyHash(K const& key) const { + static_assert( + isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value, ""); + static_assert( + !isAvalanchingHasher() || + sizeof(decltype(hasher()(key))) >= sizeof(std::size_t), + "hasher is not avalanching if it doesn't return enough bits"); + return hasher()(key); + } + + Key const& keyForValue(Key const& v) const { + return v; + } + Key const& keyForValue(std::pair<Key const, MappedOrBool> const& p) const { + return p.first; + } + Key const& keyForValue(std::pair<Key&&, MappedOrBool&&> const& p) const { + return p.first; + } + + // map's choice of pair<K const, T> as value_type is unfortunate, + // because it means we either need a proxy iterator, a pointless key + // copy when moving items during rehash, or some sort of UB hack. + // + // This code implements the hack. Use moveValue(v) instead of + // std::move(v) as the source of a move construction. enable_if_t is + // used so that this works for maps while being a no-op for sets. + template <typename Dummy = int> + static std::pair<Key&&, MappedOrBool&&> moveValue( + std::pair<Key const, MappedOrBool>& value, + std::enable_if_t<kIsMap, Dummy> = 0) { + return {std::move(const_cast<Key&>(value.first)), std::move(value.second)}; + } + + template <typename Dummy = int> + static Value&& moveValue(Value& value, std::enable_if_t<!kIsMap, Dummy> = 0) { + return std::move(value); + } + + template <typename P> + bool + beforeBuild(std::size_t /*size*/, std::size_t /*capacity*/, P&& /*rhs*/) { + return false; + } + + template <typename P> + void afterBuild( + bool /*undoState*/, + bool /*success*/, + std::size_t /*size*/, + std::size_t /*capacity*/, + P&& /*rhs*/) {} + + std::size_t alignedAllocSize(std::size_t n) const { + if (kRequiredVectorAlignment <= alignof(max_align_t) || + std::is_same<ByteAlloc, std::allocator<uint8_t>>::value) { + return n; + } else { + return n + kRequiredVectorAlignment; + } + } + + bool beforeRehash( + std::size_t /*size*/, + std::size_t /*oldCapacity*/, + std::size_t /*newCapacity*/, + std::size_t chunkAllocSize, + BytePtr& outChunkAllocation) { + outChunkAllocation = + allocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{alloc()}, chunkAllocSize); + return false; + } + + void afterRehash( + bool /*undoState*/, + bool /*success*/, + std::size_t /*size*/, + std::size_t /*oldCapacity*/, + std::size_t /*newCapacity*/, + BytePtr chunkAllocation, + std::size_t chunkAllocSize) { + // on success, this will be the old allocation, on failure the new one + if (chunkAllocation != nullptr) { + deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize); + } + } + + void beforeClear(std::size_t /*size*/, std::size_t /*capacity*/) {} + + void afterClear(std::size_t /*size*/, std::size_t /*capacity*/) {} + + void beforeReset(std::size_t /*size*/, std::size_t /*capacity*/) {} + + void afterReset( + std::size_t /*size*/, + std::size_t /*capacity*/, + BytePtr chunkAllocation, + std::size_t chunkAllocSize) { + deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize); + } + + void prefetchValue(Item const&) const { + // Subclass should disable with prefetchBeforeRehash(), + // prefetchBeforeCopy(), and prefetchBeforeDestroy(). if they don't + // override this method, because neither gcc nor clang can figure + // out that DenseMaskIter with an empty body can be elided. + FOLLY_SAFE_DCHECK(false, "should be disabled"); + } + + void afterDestroyWithoutDeallocate(Value* addr, std::size_t n) { + if (kIsLibrarySanitizeAddress) { + memset(static_cast<void*>(addr), 0x66, sizeof(Value) * n); + } + } +}; + +// BaseIter is a convenience for concrete set and map implementations +template <typename ValuePtr, typename Item> +class BaseIter : public std::iterator< + std::forward_iterator_tag, + std::remove_const_t< + typename std::pointer_traits<ValuePtr>::element_type>, + std::ptrdiff_t, + ValuePtr, + decltype(*std::declval<ValuePtr>())> { + protected: + using Chunk = F14Chunk<Item>; + using ChunkPtr = + typename std::pointer_traits<ValuePtr>::template rebind<Chunk>; + using ItemIter = F14ItemIter<ChunkPtr>; + + using ValueConstPtr = typename std::pointer_traits<ValuePtr>::template rebind< + std::add_const_t<typename std::pointer_traits<ValuePtr>::element_type>>; +}; + +//////// ValueContainer + +template < + typename Key, + typename Mapped, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid> +class ValueContainerPolicy; + +template <typename ValuePtr> +using ValueContainerIteratorBase = BaseIter< + ValuePtr, + std::remove_const_t<typename std::pointer_traits<ValuePtr>::element_type>>; + +template <typename ValuePtr> +class ValueContainerIterator : public ValueContainerIteratorBase<ValuePtr> { + using Super = ValueContainerIteratorBase<ValuePtr>; + using ItemIter = typename Super::ItemIter; + using ValueConstPtr = typename Super::ValueConstPtr; + + public: + using pointer = typename Super::pointer; + using reference = typename Super::reference; + using value_type = typename Super::value_type; + + ValueContainerIterator() = default; + ValueContainerIterator(ValueContainerIterator const&) = default; + ValueContainerIterator(ValueContainerIterator&&) = default; + ValueContainerIterator& operator=(ValueContainerIterator const&) = default; + ValueContainerIterator& operator=(ValueContainerIterator&&) = default; + ~ValueContainerIterator() = default; + + /*implicit*/ operator ValueContainerIterator<ValueConstPtr>() const { + return ValueContainerIterator<ValueConstPtr>{underlying_}; + } + + reference operator*() const { + return underlying_.item(); + } + + pointer operator->() const { + return std::pointer_traits<pointer>::pointer_to(**this); + } + + ValueContainerIterator& operator++() { + underlying_.advance(); + return *this; + } + + ValueContainerIterator operator++(int) { + auto cur = *this; + ++*this; + return cur; + } + + bool operator==(ValueContainerIterator<ValueConstPtr> const& rhs) const { + return underlying_ == rhs.underlying_; + } + bool operator!=(ValueContainerIterator<ValueConstPtr> const& rhs) const { + return !(*this == rhs); + } + + private: + ItemIter underlying_; + + explicit ValueContainerIterator(ItemIter const& underlying) + : underlying_{underlying} {} + + template <typename K, typename M, typename H, typename E, typename A> + friend class ValueContainerPolicy; + + template <typename P> + friend class ValueContainerIterator; +}; + +template < + typename Key, + typename MappedTypeOrVoid, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid> +class ValueContainerPolicy : public BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + SetOrMapValueType<Key, MappedTypeOrVoid>> { + public: + using Super = BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + SetOrMapValueType<Key, MappedTypeOrVoid>>; + using Alloc = typename Super::Alloc; + using AllocTraits = typename Super::AllocTraits; + using Item = typename Super::Item; + using ItemIter = typename Super::ItemIter; + using Value = typename Super::Value; + + private: + using ByteAlloc = typename Super::ByteAlloc; + + using Super::kIsMap; + + public: + using ConstIter = ValueContainerIterator<typename AllocTraits::const_pointer>; + using Iter = std::conditional_t< + kIsMap, + ValueContainerIterator<typename AllocTraits::pointer>, + ConstIter>; + + //////// F14Table policy + + static constexpr bool prefetchBeforeRehash() { + return false; + } + + static constexpr bool prefetchBeforeCopy() { + return false; + } + + static constexpr bool prefetchBeforeDestroy() { + return false; + } + + static constexpr bool destroyItemOnClear() { + return !std::is_trivially_destructible<Item>::value || + !AllocatorHasDefaultObjectDestroy<Alloc, Item>::value; + } + + // inherit constructors + using Super::Super; + + void swapPolicy(ValueContainerPolicy& rhs) { + this->swapBasePolicy(rhs); + } + + using Super::keyForValue; + static_assert( + std::is_same<Item, Value>::value, + "Item and Value should be the same type for ValueContainerPolicy."); + + std::size_t computeItemHash(Item const& item) const { + return this->computeKeyHash(keyForValue(item)); + } + + template <typename K> + bool keyMatchesItem(K const& key, Item const& item) const { + return this->keyEqual()(key, keyForValue(item)); + } + + Value const& buildArgForItem(Item const& item) const& { + return item; + } + + // buildArgForItem(Item&)&& is used when moving between unequal allocators + decltype(auto) buildArgForItem(Item& item) && { + return Super::moveValue(item); + } + + Value const& valueAtItem(Item const& item) const { + return item; + } + + Value&& valueAtItemForExtract(Item& item) { + return std::move(item); + } + + template <typename Table, typename... Args> + void constructValueAtItem(Table&&, Item* itemAddr, Args&&... args) { + Alloc& a = this->alloc(); + // GCC < 6 doesn't use the fact that itemAddr came from a reference + // to avoid a null-check in the placement new. folly::assume-ing it + // here gets rid of that branch. The branch is very predictable, + // but spoils some further optimizations. All clang versions that + // compile folly seem to be okay. + // + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(itemAddr != nullptr); + AllocTraits::construct(a, itemAddr, std::forward<Args>(args)...); + } + + template <typename T> + std::enable_if_t<IsNothrowMoveAndDestroy<T>::value> + complainUnlessNothrowMoveAndDestroy() {} + + template <typename T> + [[deprecated( + "mark {key_type,mapped_type} {move constructor,destructor} noexcept, or use F14Node* if they aren't")]] std:: + enable_if_t<!IsNothrowMoveAndDestroy<T>::value> + complainUnlessNothrowMoveAndDestroy() {} + + void moveItemDuringRehash(Item* itemAddr, Item& src) { + complainUnlessNothrowMoveAndDestroy<Key>(); + complainUnlessNothrowMoveAndDestroy<lift_unit_t<MappedTypeOrVoid>>(); + + constructValueAtItem(0, itemAddr, Super::moveValue(src)); + if (destroyItemOnClear()) { + if (kIsMap) { + // Laundering in the standard is only described as a solution + // for changes to const fields due to the creation of a new + // object lifetime (destroy and then placement new in the same + // location), but it seems highly likely that it will also cause + // the compiler to drop such assumptions that are violated due + // to our UB const_cast in moveValue. + destroyItem(*launder(std::addressof(src))); + } else { + destroyItem(src); + } + } + } + + void destroyItem(Item& item) noexcept { + Alloc& a = this->alloc(); + auto ptr = std::addressof(item); + AllocTraits::destroy(a, ptr); + this->afterDestroyWithoutDeallocate(ptr, 1); + } + + template <typename V> + void visitPolicyAllocationClasses( + std::size_t chunkAllocSize, + std::size_t /*size*/, + std::size_t /*capacity*/, + V&& visitor) const { + if (chunkAllocSize > 0) { + visitor( + allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>( + chunkAllocSize), + 1); + } + } + + //////// F14BasicMap/Set policy + + FOLLY_ALWAYS_INLINE Iter makeIter(ItemIter const& underlying) const { + return Iter{underlying}; + } + ConstIter makeConstIter(ItemIter const& underlying) const { + return ConstIter{underlying}; + } + ItemIter const& unwrapIter(ConstIter const& iter) const { + return iter.underlying_; + } +}; + +//////// NodeContainer + +template < + typename Key, + typename Mapped, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid> +class NodeContainerPolicy; + +template <typename ValuePtr> +class NodeContainerIterator : public BaseIter<ValuePtr, NonConstPtr<ValuePtr>> { + using Super = BaseIter<ValuePtr, NonConstPtr<ValuePtr>>; + using ItemIter = typename Super::ItemIter; + using ValueConstPtr = typename Super::ValueConstPtr; + + public: + using pointer = typename Super::pointer; + using reference = typename Super::reference; + using value_type = typename Super::value_type; + + NodeContainerIterator() = default; + NodeContainerIterator(NodeContainerIterator const&) = default; + NodeContainerIterator(NodeContainerIterator&&) = default; + NodeContainerIterator& operator=(NodeContainerIterator const&) = default; + NodeContainerIterator& operator=(NodeContainerIterator&&) = default; + ~NodeContainerIterator() = default; + + /*implicit*/ operator NodeContainerIterator<ValueConstPtr>() const { + return NodeContainerIterator<ValueConstPtr>{underlying_}; + } + + reference operator*() const { + return *underlying_.item(); + } + + pointer operator->() const { + return std::pointer_traits<pointer>::pointer_to(**this); + } + + NodeContainerIterator& operator++() { + underlying_.advance(); + return *this; + } + + NodeContainerIterator operator++(int) { + auto cur = *this; + ++*this; + return cur; + } + + bool operator==(NodeContainerIterator<ValueConstPtr> const& rhs) const { + return underlying_ == rhs.underlying_; + } + bool operator!=(NodeContainerIterator<ValueConstPtr> const& rhs) const { + return !(*this == rhs); + } + + private: + ItemIter underlying_; + + explicit NodeContainerIterator(ItemIter const& underlying) + : underlying_{underlying} {} + + template <typename K, typename M, typename H, typename E, typename A> + friend class NodeContainerPolicy; + + template <typename P> + friend class NodeContainerIterator; +}; + +template < + typename Key, + typename MappedTypeOrVoid, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid> +class NodeContainerPolicy + : public BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + typename std::allocator_traits<Defaulted< + AllocOrVoid, + DefaultAlloc<std::conditional_t< + std::is_void<MappedTypeOrVoid>::value, + Key, + MapValueType<Key, MappedTypeOrVoid>>>>>::pointer> { + public: + using Super = BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + typename std::allocator_traits<Defaulted< + AllocOrVoid, + DefaultAlloc<std::conditional_t< + std::is_void<MappedTypeOrVoid>::value, + Key, + MapValueType<Key, MappedTypeOrVoid>>>>>::pointer>; + using Alloc = typename Super::Alloc; + using AllocTraits = typename Super::AllocTraits; + using Item = typename Super::Item; + using ItemIter = typename Super::ItemIter; + using Value = typename Super::Value; + + private: + using ByteAlloc = typename Super::ByteAlloc; + + using Super::kIsMap; + + public: + using ConstIter = NodeContainerIterator<typename AllocTraits::const_pointer>; + using Iter = std::conditional_t< + kIsMap, + NodeContainerIterator<typename AllocTraits::pointer>, + ConstIter>; + + //////// F14Table policy + + static constexpr bool prefetchBeforeRehash() { + return true; + } + + static constexpr bool prefetchBeforeCopy() { + return true; + } + + static constexpr bool prefetchBeforeDestroy() { + return !std::is_trivially_destructible<Value>::value; + } + + static constexpr bool destroyItemOnClear() { + return true; + } + + // inherit constructors + using Super::Super; + + void swapPolicy(NodeContainerPolicy& rhs) { + this->swapBasePolicy(rhs); + } + + using Super::keyForValue; + + std::size_t computeItemHash(Item const& item) const { + return this->computeKeyHash(keyForValue(*item)); + } + + template <typename K> + bool keyMatchesItem(K const& key, Item const& item) const { + return this->keyEqual()(key, keyForValue(*item)); + } + + Value const& buildArgForItem(Item const& item) const& { + return *item; + } + + // buildArgForItem(Item&)&& is used when moving between unequal allocators + decltype(auto) buildArgForItem(Item& item) && { + return Super::moveValue(*item); + } + + Value const& valueAtItem(Item const& item) const { + return *item; + } + + Value&& valueAtItemForExtract(Item& item) { + return std::move(*item); + } + + template <typename Table, typename... Args> + void constructValueAtItem(Table&&, Item* itemAddr, Args&&... args) { + Alloc& a = this->alloc(); + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(itemAddr != nullptr); + new (itemAddr) Item{AllocTraits::allocate(a, 1)}; + auto p = std::addressof(**itemAddr); + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(p != nullptr); + auto rollback = makeGuard([&] { AllocTraits::deallocate(a, p, 1); }); + AllocTraits::construct(a, p, std::forward<Args>(args)...); + rollback.dismiss(); + } + + void moveItemDuringRehash(Item* itemAddr, Item& src) { + // This is basically *itemAddr = src; src = nullptr, but allowing + // for fancy pointers. + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(itemAddr != nullptr); + new (itemAddr) Item{std::move(src)}; + src = nullptr; + src.~Item(); + } + + void prefetchValue(Item const& item) const { + prefetchAddr(std::addressof(*item)); + } + + template <typename T> + std::enable_if_t<std::is_nothrow_destructible<T>::value> + complainUnlessNothrowDestroy() {} + + template <typename T> + [[deprecated("Mark key and mapped type destructor nothrow")]] std:: + enable_if_t<!std::is_nothrow_destructible<T>::value> + complainUnlessNothrowDestroy() {} + + void destroyItem(Item& item) noexcept { + complainUnlessNothrowDestroy<Key>(); + complainUnlessNothrowDestroy<lift_unit_t<MappedTypeOrVoid>>(); + if (item != nullptr) { + Alloc& a = this->alloc(); + AllocTraits::destroy(a, std::addressof(*item)); + AllocTraits::deallocate(a, item, 1); + } + item.~Item(); + } + + template <typename V> + void visitPolicyAllocationClasses( + std::size_t chunkAllocSize, + std::size_t size, + std::size_t /*capacity*/, + V&& visitor) const { + if (chunkAllocSize > 0) { + visitor( + allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>( + chunkAllocSize), + 1); + } + if (size > 0) { + visitor(sizeof(Value), size); + } + } + + //////// F14BasicMap/Set policy + + FOLLY_ALWAYS_INLINE Iter makeIter(ItemIter const& underlying) const { + return Iter{underlying}; + } + ConstIter makeConstIter(ItemIter const& underlying) const { + return Iter{underlying}; + } + ItemIter const& unwrapIter(ConstIter const& iter) const { + return iter.underlying_; + } +}; + +//////// VectorContainer + +template < + typename Key, + typename MappedTypeOrVoid, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid, + typename EligibleForPerturbedInsertionOrder> +class VectorContainerPolicy; + +template <typename ValuePtr> +class VectorContainerIterator : public BaseIter<ValuePtr, uint32_t> { + using Super = BaseIter<ValuePtr, uint32_t>; + using ValueConstPtr = typename Super::ValueConstPtr; + + public: + using pointer = typename Super::pointer; + using reference = typename Super::reference; + using value_type = typename Super::value_type; + + VectorContainerIterator() = default; + VectorContainerIterator(VectorContainerIterator const&) = default; + VectorContainerIterator(VectorContainerIterator&&) = default; + VectorContainerIterator& operator=(VectorContainerIterator const&) = default; + VectorContainerIterator& operator=(VectorContainerIterator&&) = default; + ~VectorContainerIterator() = default; + + /*implicit*/ operator VectorContainerIterator<ValueConstPtr>() const { + return VectorContainerIterator<ValueConstPtr>{current_, lowest_}; + } + + reference operator*() const { + return *current_; + } + + pointer operator->() const { + return current_; + } + + VectorContainerIterator& operator++() { + if (UNLIKELY(current_ == lowest_)) { + current_ = nullptr; + } else { + --current_; + } + return *this; + } + + VectorContainerIterator operator++(int) { + auto cur = *this; + ++*this; + return cur; + } + + bool operator==(VectorContainerIterator<ValueConstPtr> const& rhs) const { + return current_ == rhs.current_; + } + bool operator!=(VectorContainerIterator<ValueConstPtr> const& rhs) const { + return !(*this == rhs); + } + + private: + ValuePtr current_; + ValuePtr lowest_; + + explicit VectorContainerIterator(ValuePtr current, ValuePtr lowest) + : current_(current), lowest_(lowest) {} + + std::size_t index() const { + return current_ - lowest_; + } + + template < + typename K, + typename M, + typename H, + typename E, + typename A, + typename P> + friend class VectorContainerPolicy; + + template <typename P> + friend class VectorContainerIterator; +}; + +struct VectorContainerIndexSearch { + uint32_t index_; +}; + +template < + typename Key, + typename MappedTypeOrVoid, + typename HasherOrVoid, + typename KeyEqualOrVoid, + typename AllocOrVoid, + typename EligibleForPerturbedInsertionOrder> +class VectorContainerPolicy : public BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + uint32_t> { + public: + using Super = BasePolicy< + Key, + MappedTypeOrVoid, + HasherOrVoid, + KeyEqualOrVoid, + AllocOrVoid, + uint32_t>; + using Value = typename Super::Value; + using Alloc = typename Super::Alloc; + using AllocTraits = typename Super::AllocTraits; + using ByteAlloc = typename Super::ByteAlloc; + using ByteAllocTraits = typename Super::ByteAllocTraits; + using BytePtr = typename Super::BytePtr; + using Hasher = typename Super::Hasher; + using Item = typename Super::Item; + using ItemIter = typename Super::ItemIter; + using KeyEqual = typename Super::KeyEqual; + + using Super::kAllocIsAlwaysEqual; + + private: + using Super::kIsMap; + + public: + static constexpr bool kEnableItemIteration = false; + + static constexpr bool kContinuousCapacity = true; + + using InternalSizeType = Item; + + using ConstIter = + VectorContainerIterator<typename AllocTraits::const_pointer>; + using Iter = std::conditional_t< + kIsMap, + VectorContainerIterator<typename AllocTraits::pointer>, + ConstIter>; + using ConstReverseIter = typename AllocTraits::const_pointer; + using ReverseIter = std:: + conditional_t<kIsMap, typename AllocTraits::pointer, ConstReverseIter>; + + using ValuePtr = typename AllocTraits::pointer; + + //////// F14Table policy + + static constexpr bool prefetchBeforeRehash() { + return true; + } + + static constexpr bool prefetchBeforeCopy() { + return false; + } + + static constexpr bool prefetchBeforeDestroy() { + return false; + } + + static constexpr bool destroyItemOnClear() { + return false; + } + + private: + static constexpr bool valueIsTriviallyCopyable() { + return AllocatorHasDefaultObjectConstruct<Alloc, Value, Value>::value && + AllocatorHasDefaultObjectDestroy<Alloc, Value>::value && + is_trivially_copyable<Value>::value; + } + + public: + VectorContainerPolicy( + Hasher const& hasher, + KeyEqual const& keyEqual, + Alloc const& alloc) + : Super{hasher, keyEqual, alloc} {} + + VectorContainerPolicy(VectorContainerPolicy const& rhs) : Super{rhs} { + // values_ will get allocated later to do the copy + } + + VectorContainerPolicy(VectorContainerPolicy const& rhs, Alloc const& alloc) + : Super{rhs, alloc} { + // values_ will get allocated later to do the copy + } + + VectorContainerPolicy(VectorContainerPolicy&& rhs) noexcept + : Super{std::move(rhs)}, values_{rhs.values_} { + rhs.values_ = nullptr; + } + + VectorContainerPolicy( + VectorContainerPolicy&& rhs, + Alloc const& alloc) noexcept + : Super{std::move(rhs), alloc} { + if (kAllocIsAlwaysEqual || this->alloc() == rhs.alloc()) { + // common case + values_ = rhs.values_; + rhs.values_ = nullptr; + } else { + // table must be constructed in new memory + values_ = nullptr; + } + } + + VectorContainerPolicy& operator=(VectorContainerPolicy const& rhs) { + if (this != &rhs) { + FOLLY_SAFE_DCHECK(values_ == nullptr, ""); + Super::operator=(rhs); + } + return *this; + } + + VectorContainerPolicy& operator=(VectorContainerPolicy&& rhs) noexcept { + if (this != &rhs) { + FOLLY_SAFE_DCHECK(values_ == nullptr, ""); + bool transfer = + AllocTraits::propagate_on_container_move_assignment::value || + kAllocIsAlwaysEqual || this->alloc() == rhs.alloc(); + Super::operator=(std::move(rhs)); + if (transfer) { + values_ = rhs.values_; + rhs.values_ = nullptr; + } + } + return *this; + } + + void swapPolicy(VectorContainerPolicy& rhs) { + using std::swap; + this->swapBasePolicy(rhs); + swap(values_, rhs.values_); + } + + template <typename K> + std::size_t computeKeyHash(K const& key) const { + static_assert( + Super::isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value, + ""); + return this->hasher()(key); + } + + std::size_t computeKeyHash(VectorContainerIndexSearch const& key) const { + return computeItemHash(key.index_); + } + + using Super::keyForValue; + + std::size_t computeItemHash(Item const& item) const { + return this->computeKeyHash(keyForValue(values_[item])); + } + + bool keyMatchesItem(VectorContainerIndexSearch const& key, Item const& item) + const { + return key.index_ == item; + } + + template <typename K> + bool keyMatchesItem(K const& key, Item const& item) const { + return this->keyEqual()(key, keyForValue(values_[item])); + } + + Key const& keyForValue(VectorContainerIndexSearch const& arg) const { + return keyForValue(values_[arg.index_]); + } + + VectorContainerIndexSearch buildArgForItem(Item const& item) const { + return {item}; + } + + Value const& valueAtItem(Item const& item) const { + return values_[item]; + } + + Value&& valueAtItemForExtract(Item& item) { + return std::move(values_[item]); + } + + template <typename Table> + void constructValueAtItem( + Table&&, + Item* itemAddr, + VectorContainerIndexSearch arg) { + *itemAddr = arg.index_; + } + + template <typename Table, typename... Args> + void constructValueAtItem(Table&& table, Item* itemAddr, Args&&... args) { + Alloc& a = this->alloc(); + auto size = static_cast<InternalSizeType>(table.size()); + FOLLY_SAFE_DCHECK( + table.size() < std::numeric_limits<InternalSizeType>::max(), ""); + *itemAddr = size; + auto dst = std::addressof(values_[size]); + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(dst != nullptr); + AllocTraits::construct(a, dst, std::forward<Args>(args)...); + + constexpr bool perturb = FOLLY_F14_PERTURB_INSERTION_ORDER; + if (EligibleForPerturbedInsertionOrder::value && perturb && + !tlsPendingSafeInserts()) { + // Pick a random victim. We have to do this post-construction + // because the item and tag are already set in the table before + // calling constructValueAtItem, so if there is a tag collision + // find may evaluate values_[size] during the search. + auto i = static_cast<InternalSizeType>(tlsMinstdRand(size + 1)); + if (i != size) { + auto& lhsItem = *itemAddr; + auto rhsIter = table.find( + VectorContainerIndexSearch{static_cast<InternalSizeType>(i)}); + FOLLY_SAFE_DCHECK(!rhsIter.atEnd(), ""); + auto& rhsItem = rhsIter.item(); + FOLLY_SAFE_DCHECK(lhsItem == size, ""); + FOLLY_SAFE_DCHECK(rhsItem == i, ""); + + aligned_storage_for_t<Value> tmp; + Value* tmpValue = static_cast<Value*>(static_cast<void*>(&tmp)); + transfer(a, std::addressof(values_[i]), tmpValue, 1); + transfer( + a, std::addressof(values_[size]), std::addressof(values_[i]), 1); + transfer(a, tmpValue, std::addressof(values_[size]), 1); + lhsItem = i; + rhsItem = size; + } + } + } + + void moveItemDuringRehash(Item* itemAddr, Item& src) { + *itemAddr = src; + } + + void prefetchValue(Item const& item) const { + prefetchAddr(std::addressof(values_[item])); + } + + void destroyItem(Item&) noexcept {} + + template <typename T> + std::enable_if_t<IsNothrowMoveAndDestroy<T>::value> + complainUnlessNothrowMoveAndDestroy() {} + + template <typename T> + [[deprecated( + "mark {key_type,mapped_type} {move constructor,destructor} noexcept, or use F14Node* if they aren't")]] std:: + enable_if_t<!IsNothrowMoveAndDestroy<T>::value> + complainUnlessNothrowMoveAndDestroy() {} + + void transfer(Alloc& a, Value* src, Value* dst, std::size_t n) { + complainUnlessNothrowMoveAndDestroy<Key>(); + complainUnlessNothrowMoveAndDestroy<lift_unit_t<MappedTypeOrVoid>>(); + + auto origSrc = src; + if (valueIsTriviallyCopyable()) { + std::memcpy( + static_cast<void*>(dst), + static_cast<void const*>(src), + n * sizeof(Value)); + } else { + for (std::size_t i = 0; i < n; ++i, ++src, ++dst) { + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(dst != nullptr); + AllocTraits::construct(a, dst, Super::moveValue(*src)); + if (kIsMap) { + AllocTraits::destroy(a, launder(src)); + } else { + AllocTraits::destroy(a, src); + } + } + } + this->afterDestroyWithoutDeallocate(origSrc, n); + } + + template <typename P, typename V> + bool beforeBuildImpl(std::size_t size, P&& rhs, V const& constructorArgFor) { + Alloc& a = this->alloc(); + + FOLLY_SAFE_DCHECK(values_ != nullptr, ""); + + auto src = std::addressof(rhs.values_[0]); + Value* dst = std::addressof(values_[0]); + + if (valueIsTriviallyCopyable()) { + std::memcpy( + static_cast<void*>(dst), + static_cast<void const*>(src), + size * sizeof(Value)); + } else { + for (std::size_t i = 0; i < size; ++i, ++src, ++dst) { + try { + // TODO(T31574848): clean up assume-s used to optimize placement new + assume(dst != nullptr); + AllocTraits::construct(a, dst, constructorArgFor(*src)); + } catch (...) { + for (Value* cleanup = std::addressof(values_[0]); cleanup != dst; + ++cleanup) { + AllocTraits::destroy(a, cleanup); + } + throw; + } + } + } + return true; + } + + bool beforeBuild( + std::size_t size, + std::size_t /*capacity*/, + VectorContainerPolicy const& rhs) { + return beforeBuildImpl(size, rhs, [](Value const& v) { return v; }); + } + + bool beforeBuild( + std::size_t size, + std::size_t /*capacity*/, + VectorContainerPolicy&& rhs) { + return beforeBuildImpl( + size, rhs, [](Value& v) { return Super::moveValue(v); }); + } + + template <typename P> + void afterBuild( + bool /*undoState*/, + bool success, + std::size_t /*size*/, + std::size_t /*capacity*/, + P&& /*rhs*/) { + // buildArgForItem can be used to construct a new item trivially, + // so no failure between beforeBuild and afterBuild should be possible + FOLLY_SAFE_DCHECK(success, ""); + } + + private: + // Returns the byte offset of the first Value in a unified allocation + // that first holds prefixBytes of data, where prefixBytes comes from + // Chunk storage and may be only 4-byte aligned due to sub-chunk + // allocation. + static std::size_t valuesOffset(std::size_t prefixBytes) { + FOLLY_SAFE_DCHECK((prefixBytes % alignof(Item)) == 0, ""); + if (alignof(Value) > alignof(Item)) { + prefixBytes = -(-prefixBytes & ~(alignof(Value) - 1)); + } + FOLLY_SAFE_DCHECK((prefixBytes % alignof(Value)) == 0, ""); + return prefixBytes; + } + + // Returns the total number of bytes that should be allocated to store + // prefixBytes of Chunks and valueCapacity values. + static std::size_t allocSize( + std::size_t prefixBytes, + std::size_t valueCapacity) { + return valuesOffset(prefixBytes) + sizeof(Value) * valueCapacity; + } + + public: + ValuePtr beforeRehash( + std::size_t size, + std::size_t oldCapacity, + std::size_t newCapacity, + std::size_t chunkAllocSize, + BytePtr& outChunkAllocation) { + FOLLY_SAFE_DCHECK( + size <= oldCapacity && ((values_ == nullptr) == (oldCapacity == 0)) && + newCapacity > 0 && + newCapacity <= (std::numeric_limits<Item>::max)(), + ""); + + outChunkAllocation = + allocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{Super::alloc()}, allocSize(chunkAllocSize, newCapacity)); + + ValuePtr before = values_; + ValuePtr after = std::pointer_traits<ValuePtr>::pointer_to( + *static_cast<Value*>(static_cast<void*>( + &*outChunkAllocation + valuesOffset(chunkAllocSize)))); + + if (size > 0) { + Alloc& a = this->alloc(); + transfer(a, std::addressof(before[0]), std::addressof(after[0]), size); + } + + values_ = after; + return before; + } + + FOLLY_NOINLINE void afterFailedRehash(ValuePtr state, std::size_t size) { + // state holds the old storage + Alloc& a = this->alloc(); + if (size > 0) { + transfer(a, std::addressof(values_[0]), std::addressof(state[0]), size); + } + values_ = state; + } + + void afterRehash( + ValuePtr state, + bool success, + std::size_t size, + std::size_t oldCapacity, + std::size_t newCapacity, + BytePtr chunkAllocation, + std::size_t chunkAllocSize) { + if (!success) { + afterFailedRehash(state, size); + } + + // on success, chunkAllocation is the old allocation, on failure it is the + // new one + if (chunkAllocation != nullptr) { + deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{Super::alloc()}, + chunkAllocation, + allocSize(chunkAllocSize, (success ? oldCapacity : newCapacity))); + } + } + + void beforeClear(std::size_t size, std::size_t capacity) { + FOLLY_SAFE_DCHECK( + size <= capacity && ((values_ == nullptr) == (capacity == 0)), ""); + Alloc& a = this->alloc(); + for (std::size_t i = 0; i < size; ++i) { + AllocTraits::destroy(a, std::addressof(values_[i])); + } + } + + void beforeReset(std::size_t size, std::size_t capacity) { + beforeClear(size, capacity); + } + + void afterReset( + std::size_t /*size*/, + std::size_t capacity, + BytePtr chunkAllocation, + std::size_t chunkAllocSize) { + if (chunkAllocation != nullptr) { + deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>( + ByteAlloc{Super::alloc()}, + chunkAllocation, + allocSize(chunkAllocSize, capacity)); + values_ = nullptr; + } + } + + template <typename V> + void visitPolicyAllocationClasses( + std::size_t chunkAllocSize, + std::size_t /*size*/, + std::size_t capacity, + V&& visitor) const { + FOLLY_SAFE_DCHECK((chunkAllocSize == 0) == (capacity == 0), ""); + if (chunkAllocSize > 0) { + visitor( + allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>( + allocSize(chunkAllocSize, capacity)), + 1); + } + } + + // Iterator stuff + + Iter linearBegin(std::size_t size) const { + return Iter{(size > 0 ? values_ + size - 1 : nullptr), values_}; + } + + Iter linearEnd() const { + return Iter{nullptr, nullptr}; + } + + //////// F14BasicMap/Set policy + + Iter makeIter(ItemIter const& underlying) const { + if (underlying.atEnd()) { + return linearEnd(); + } else { + assume(values_ + underlying.item() != nullptr); + assume(values_ != nullptr); + return Iter{values_ + underlying.item(), values_}; + } + } + + ConstIter makeConstIter(ItemIter const& underlying) const { + return makeIter(underlying); + } + + Item iterToIndex(ConstIter const& iter) const { + auto n = iter.index(); + assume(n <= std::numeric_limits<Item>::max()); + return static_cast<Item>(n); + } + + Iter indexToIter(Item index) const { + return Iter{values_ + index, values_}; + } + + Iter iter(ReverseIter it) { + return Iter{it, values_}; + } + + ConstIter iter(ConstReverseIter it) const { + return ConstIter{it, values_}; + } + + ReverseIter riter(Iter it) { + return it.current_; + } + + ConstReverseIter riter(ConstIter it) const { + return it.current_; + } + + ValuePtr values_{nullptr}; +}; + +template < + template <typename, typename, typename, typename, typename, typename...> + class Policy, + typename Key, + typename Mapped, + typename Hasher, + typename KeyEqual, + typename Alloc, + typename... Args> +using MapPolicyWithDefaults = Policy< + Key, + Mapped, + VoidDefault<Hasher, DefaultHasher<Key>>, + VoidDefault<KeyEqual, DefaultKeyEqual<Key>>, + VoidDefault<Alloc, DefaultAlloc<std::pair<Key const, Mapped>>>, + Args...>; + +template < + template <typename, typename, typename, typename, typename, typename...> + class Policy, + typename Key, + typename Hasher, + typename KeyEqual, + typename Alloc, + typename... Args> +using SetPolicyWithDefaults = Policy< + Key, + void, + VoidDefault<Hasher, DefaultHasher<Key>>, + VoidDefault<KeyEqual, DefaultKeyEqual<Key>>, + VoidDefault<Alloc, DefaultAlloc<Key>>, + Args...>; + +} // namespace detail +} // namespace f14 +} // namespace folly + +#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14SetFallback.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14SetFallback.h new file mode 100644 index 000000000..1e6bce6c2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14SetFallback.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/** + * This file is intended to be included only by F14Set.h. It contains fallback + * implementations of F14Set types for platforms that do not support the + * required SIMD instructions, based on std::unordered_set. + */ + +#include <unordered_set> + +namespace folly { + +namespace f14 { +namespace detail { +template <typename K, typename H, typename E, typename A> +class F14BasicSet : public std::unordered_set<K, H, E, A> { + using Super = std::unordered_set<K, H, E, A>; + + public: + using typename Super::pointer; + using typename Super::value_type; + + F14BasicSet() = default; + + using Super::Super; + + //// PUBLIC - F14 Extensions + + bool containsEqualValue(value_type const& value) const { + auto slot = this->bucket(value); + auto e = this->end(slot); + for (auto b = this->begin(slot); b != e; ++b) { + if (*b == value) { + return true; + } + } + return false; + } + + // exact for libstdc++, approximate for others + std::size_t getAllocatedMemorySize() const { + std::size_t rv = 0; + visitAllocationClasses( + [&](std::size_t bytes, std::size_t n) { rv += bytes * n; }); + return rv; + } + + // exact for libstdc++, approximate for others + template <typename V> + void visitAllocationClasses(V&& visitor) const { + auto bc = this->bucket_count(); + if (bc > 1) { + visitor(bc * sizeof(pointer), 1); + } + if (this->size() > 0) { + visitor(sizeof(StdNodeReplica<K, value_type, H>), this->size()); + } + } + + template <typename V> + void visitContiguousRanges(V&& visitor) const { + for (value_type const& entry : *this) { + value_type const* b = std::addressof(entry); + visitor(b, b + 1); + } + } +}; +} // namespace detail +} // namespace f14 + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14NodeSet + : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14NodeSet() = default; + + using Super::Super; + + F14NodeSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14ValueSet + : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14ValueSet() : Super() {} + + using Super::Super; + + F14ValueSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14VectorSet + : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14VectorSet() = default; + + using Super::Super; + + F14VectorSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +template <typename Key, typename Hasher, typename KeyEqual, typename Alloc> +class F14FastSet + : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> { + using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>; + + public: + using typename Super::value_type; + + F14FastSet() = default; + + using Super::Super; + + F14FastSet& operator=(std::initializer_list<value_type> ilist) { + Super::operator=(ilist); + return *this; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.cpp b/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.cpp new file mode 100644 index 000000000..d26b7d743 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/container/detail/F14Table.h> + +#include <atomic> +#include <chrono> + +namespace folly { +namespace f14 { +namespace detail { + +// If you get a link failure that leads you here, your build has varying +// compiler flags across compilation units in a way that would break F14. +// SIMD (SSE2 or NEON) needs to be either on everywhere or off everywhere +// that uses F14. If SIMD is on then hardware CRC needs to be enabled +// everywhere or disabled everywhere. +void F14LinkCheck<getF14IntrinsicsMode()>::check() noexcept {} + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE +EmptyTagVectorType kEmptyTagVector = {}; +#endif + +//// Debug and ASAN stuff + +#if defined(FOLLY_TLS) && (!defined(NDEBUG) || FOLLY_LIBRARY_SANITIZE_ADDRESS) +#define FOLLY_F14_DETAIL_TLS_SIZE_T FOLLY_TLS std::size_t +#else +#define FOLLY_F14_DETAIL_TLS_SIZE_T std::atomic<std::size_t> +#endif + +bool tlsPendingSafeInserts(std::ptrdiff_t delta) { + static FOLLY_F14_DETAIL_TLS_SIZE_T value{0}; + + FOLLY_SAFE_DCHECK(delta >= -1, ""); + std::size_t v = value; + if (delta > 0 || (delta == -1 && v > 0)) { + v += delta; + v = std::min(std::numeric_limits<std::size_t>::max() / 2, v); + value = v; + } + return v != 0; +} + +std::size_t tlsMinstdRand(std::size_t n) { + FOLLY_SAFE_DCHECK(n > 0, ""); + + static FOLLY_F14_DETAIL_TLS_SIZE_T state{0}; + auto s = static_cast<uint32_t>(state); + if (s == 0) { + uint64_t seed = static_cast<uint64_t>( + std::chrono::steady_clock::now().time_since_epoch().count()); + s = hash::twang_32from64(seed); + } + + s = static_cast<uint32_t>((s * uint64_t{48271}) % uint64_t{2147483647}); + state = s; + return std::size_t{s} % n; +} + +} // namespace detail +} // namespace f14 +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.h b/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.h new file mode 100644 index 000000000..4ed2e4f89 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/F14Table.h @@ -0,0 +1,2394 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <cstdint> +#include <cstring> + +#include <array> +#include <iterator> +#include <limits> +#include <memory> +#include <new> +#include <type_traits> +#include <utility> +#include <vector> + +#if FOLLY_HAS_STRING_VIEW +#include <string_view> // @manual +#endif + +#include <folly/Bits.h> +#include <folly/ConstexprMath.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Align.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Exception.h> +#include <folly/lang/Launder.h> +#include <folly/lang/Pretty.h> +#include <folly/lang/SafeAssert.h> +#include <folly/portability/Builtins.h> + +#include <folly/container/HeterogeneousAccess.h> +#include <folly/container/detail/F14Defaults.h> +#include <folly/container/detail/F14IntrinsicsAvailability.h> +#include <folly/container/detail/F14Mask.h> + +#if FOLLY_LIBRARY_SANITIZE_ADDRESS && defined(FOLLY_TLS) +#define FOLLY_F14_TLS_IF_ASAN FOLLY_TLS +#else +#define FOLLY_F14_TLS_IF_ASAN +#endif + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE +#if FOLLY_NEON +#include <arm_acle.h> // __crc32cd +#else +#include <nmmintrin.h> // _mm_crc32_u64 +#endif +#else +#ifdef _WIN32 +#include <intrin.h> // _mul128 in fallback bit mixer +#endif +#endif + +#if FOLLY_NEON +#include <arm_neon.h> // uint8x16t intrinsics +#else // SSE2 +#include <emmintrin.h> // _mm_set1_epi8 +#include <immintrin.h> // __m128i intrinsics +#include <xmmintrin.h> // _mm_prefetch +#endif + +#endif + +#ifndef FOLLY_F14_PERTURB_INSERTION_ORDER +#define FOLLY_F14_PERTURB_INSERTION_ORDER folly::kIsDebug +#endif + +namespace folly { + +struct F14TableStats { + char const* policy; + std::size_t size{0}; + std::size_t valueSize{0}; + std::size_t bucketCount{0}; + std::size_t chunkCount{0}; + std::vector<std::size_t> chunkOccupancyHisto; + std::vector<std::size_t> chunkOutboundOverflowHisto; + std::vector<std::size_t> chunkHostedOverflowHisto; + std::vector<std::size_t> keyProbeLengthHisto; + std::vector<std::size_t> missProbeLengthHisto; + std::size_t totalBytes{0}; + std::size_t overheadBytes{0}; + + private: + template <typename T> + static auto computeHelper(T const* m) -> decltype(m->computeStats()) { + return m->computeStats(); + } + + static F14TableStats computeHelper(...) { + return {}; + } + + public: + template <typename T> + static F14TableStats compute(T const& m) { + return computeHelper(&m); + } +}; + +namespace f14 { +namespace detail { + +template <F14IntrinsicsMode> +struct F14LinkCheck {}; + +template <> +struct F14LinkCheck<getF14IntrinsicsMode()> { + // The purpose of this method is to trigger a link failure if + // compilation flags vary across compilation units. The definition + // is in F14Table.cpp, so only one of F14LinkCheck<None>::check, + // F14LinkCheck<Simd>::check, or F14LinkCheck<SimdAndCrc>::check will + // be available at link time. + // + // To cause a link failure the function must be invoked in code that + // is not optimized away, so we call it on a couple of cold paths + // (exception handling paths in copy construction and rehash). LTO may + // remove it entirely, but that's fine. + static void check() noexcept; +}; + +bool tlsPendingSafeInserts(std::ptrdiff_t delta = 0); +std::size_t tlsMinstdRand(std::size_t n); + +#if defined(_LIBCPP_VERSION) + +template <typename K, typename V, typename H> +struct StdNodeReplica { + void* next; + std::size_t hash; + V value; +}; + +#else + +template <typename H> +struct StdIsFastHash : std::true_type {}; +template <> +struct StdIsFastHash<std::hash<long double>> : std::false_type {}; +template <typename... Args> +struct StdIsFastHash<std::hash<std::basic_string<Args...>>> : std::false_type { +}; +#if FOLLY_HAS_STRING_VIEW +template <typename... Args> +struct StdIsFastHash<std::hash<std::basic_string_view<Args...>>> + : std::false_type {}; +#endif + +// mimic internal node of unordered containers in STL to estimate the size +template <typename K, typename V, typename H, typename Enable = void> +struct StdNodeReplica { + void* next; + V value; +}; +template <typename K, typename V, typename H> +struct StdNodeReplica< + K, + V, + H, + std::enable_if_t< + !StdIsFastHash<H>::value || !is_nothrow_invocable_v<H, K>>> { + void* next; + V value; + std::size_t hash; +}; + +#endif + +template <class Container, class Predicate> +void erase_if_impl(Container& c, Predicate& predicate) { + for (auto i = c.begin(), last = c.end(); i != last;) { + auto prev = i++; + if (predicate(*prev)) { + c.erase(prev); + } + } +} + +} // namespace detail +} // namespace f14 + +#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE +namespace f14 { +namespace detail { +template <typename Policy> +class F14Table; +} // namespace detail +} // namespace f14 + +class F14HashToken final { + public: + F14HashToken() = default; + + private: + using HashPair = std::pair<std::size_t, std::size_t>; + + explicit F14HashToken(HashPair hp) : hp_(hp) {} + explicit operator HashPair() const { + return hp_; + } + + HashPair hp_; + + template <typename Policy> + friend class f14::detail::F14Table; +}; + +namespace f14 { +namespace detail { + +//// Defaults should be selected using void +template <typename Arg, typename Default> +using VoidDefault = + std::conditional_t<std::is_same<Arg, Default>::value, void, Arg>; + +template <typename Arg, typename Default> +using Defaulted = + std::conditional_t<std::is_same<Arg, void>::value, Default, Arg>; + +template < + typename TableKey, + typename Hasher, + typename KeyEqual, + typename ArgKey> +struct EligibleForHeterogeneousFind + : Conjunction< + is_transparent<Hasher>, + is_transparent<KeyEqual>, + is_invocable<Hasher, ArgKey const&>, + is_invocable<KeyEqual, ArgKey const&, TableKey const&>> {}; + +template < + typename TableKey, + typename Hasher, + typename KeyEqual, + typename ArgKey> +using EligibleForHeterogeneousInsert = Conjunction< + EligibleForHeterogeneousFind<TableKey, Hasher, KeyEqual, ArgKey>, + std::is_constructible<TableKey, ArgKey>>; + +//////////////// + +template <typename T> +FOLLY_ALWAYS_INLINE static void prefetchAddr(T const* ptr) { +#ifndef _WIN32 + __builtin_prefetch(static_cast<void const*>(ptr)); +#elif FOLLY_NEON + __prefetch(static_cast<void const*>(ptr)); +#else + _mm_prefetch( + static_cast<char const*>(static_cast<void const*>(ptr)), _MM_HINT_T0); +#endif +} + +#if FOLLY_NEON +using TagVector = uint8x16_t; +#else // SSE2 +using TagVector = __m128i; +#endif + +// We could use unaligned loads to relax this requirement, but that +// would be both a performance penalty and require a bulkier packed +// ItemIter format +constexpr std::size_t kRequiredVectorAlignment = + constexpr_max(std::size_t{16}, alignof(max_align_t)); + +using EmptyTagVectorType = std::aligned_storage_t< + sizeof(TagVector) + kRequiredVectorAlignment, + alignof(max_align_t)>; + +extern EmptyTagVectorType kEmptyTagVector; + +template <typename ItemType> +struct alignas(kRequiredVectorAlignment) F14Chunk { + using Item = ItemType; + + // For our 16 byte vector alignment (and assuming alignof(Item) >= + // 4) kCapacity of 14 is the most space efficient. Slightly smaller + // or larger capacities can help with cache alignment in a couple of + // cases without wasting too much space, but once the items are larger + // then we're unlikely to get much benefit anyway. The only case we + // optimize is using kCapacity of 12 for 4 byte items, which makes the + // chunk take exactly 1 cache line, and adding 16 bytes of padding for + // 16 byte items so that a chunk takes exactly 4 cache lines. + static constexpr unsigned kCapacity = sizeof(Item) == 4 ? 12 : 14; + + static constexpr unsigned kDesiredCapacity = kCapacity - 2; + + static constexpr unsigned kAllocatedCapacity = + kCapacity + (sizeof(Item) == 16 ? 1 : 0); + + // If kCapacity == 12 then we get 16 bits of capacityScale by using + // tag 12 and 13, otherwise we only get 4 bits of control_ + static constexpr std::size_t kCapacityScaleBits = kCapacity == 12 ? 16 : 4; + static constexpr std::size_t kCapacityScaleShift = kCapacityScaleBits - 4; + + static constexpr MaskType kFullMask = FullMask<kCapacity>::value; + + // Non-empty tags have their top bit set. tags_ array might be bigger + // than kCapacity to keep alignment of first item. + std::array<uint8_t, 14> tags_; + + // Bits 0..3 of chunk 0 record the scaling factor between the number of + // chunks and the max size without rehash. Bits 4-7 in any chunk are a + // 4-bit counter of the number of values in this chunk that were placed + // because they overflowed their desired chunk (hostedOverflowCount). + uint8_t control_; + + // The number of values that would have been placed into this chunk if + // there had been space, including values that also overflowed previous + // full chunks. This value saturates; once it becomes 255 it no longer + // increases nor decreases. + uint8_t outboundOverflowCount_; + + std::array<aligned_storage_for_t<Item>, kAllocatedCapacity> rawItems_; + + static F14Chunk* emptyInstance() { + auto raw = reinterpret_cast<char*>(&kEmptyTagVector); + if (kRequiredVectorAlignment > alignof(max_align_t)) { + auto delta = kRequiredVectorAlignment - + (reinterpret_cast<uintptr_t>(raw) % kRequiredVectorAlignment); + raw += delta; + } + auto rv = reinterpret_cast<F14Chunk*>(raw); + FOLLY_SAFE_DCHECK( + (reinterpret_cast<uintptr_t>(rv) % kRequiredVectorAlignment) == 0, ""); + return rv; + } + + void clear() { + // tags_ = {}; control_ = 0; outboundOverflowCount_ = 0; + + // gcc < 6 doesn't exploit chunk alignment to generate the optimal + // SSE clear from memset. This is very hot code, so it is worth + // handling that case specially. +#if FOLLY_SSE >= 2 && __GNUC__ <= 5 && !__clang__ + // this doesn't violate strict aliasing rules because __m128i is + // tagged as __may_alias__ + auto* v = static_cast<__m128i*>(static_cast<void*>(&tags_[0])); + _mm_store_si128(v, _mm_setzero_si128()); +#else + std::memset(&tags_[0], '\0', 16); +#endif + } + + void copyOverflowInfoFrom(F14Chunk const& rhs) { + FOLLY_SAFE_DCHECK(hostedOverflowCount() == 0, ""); + control_ += static_cast<uint8_t>(rhs.control_ & 0xf0); + outboundOverflowCount_ = rhs.outboundOverflowCount_; + } + + unsigned hostedOverflowCount() const { + return control_ >> 4; + } + + static constexpr uint8_t kIncrHostedOverflowCount = 0x10; + static constexpr uint8_t kDecrHostedOverflowCount = + static_cast<uint8_t>(-0x10); + + void adjustHostedOverflowCount(uint8_t op) { + control_ += op; + } + + bool eof() const { + return capacityScale() != 0; + } + + std::size_t capacityScale() const { + if (kCapacityScaleBits == 4) { + return control_ & 0xf; + } else { + uint16_t v; + std::memcpy(&v, &tags_[12], 2); + return v; + } + } + + void setCapacityScale(std::size_t scale) { + FOLLY_SAFE_DCHECK( + this != emptyInstance() && scale > 0 && + scale < (std::size_t{1} << kCapacityScaleBits), + ""); + if (kCapacityScaleBits == 4) { + control_ = static_cast<uint8_t>((control_ & ~0xf) | scale); + } else { + uint16_t v = static_cast<uint16_t>(scale); + std::memcpy(&tags_[12], &v, 2); + } + } + + void markEof(std::size_t scale) { + folly::assume(control_ == 0); + setCapacityScale(scale); + } + + unsigned outboundOverflowCount() const { + return outboundOverflowCount_; + } + + void incrOutboundOverflowCount() { + if (outboundOverflowCount_ != 255) { + ++outboundOverflowCount_; + } + } + + void decrOutboundOverflowCount() { + if (outboundOverflowCount_ != 255) { + --outboundOverflowCount_; + } + } + + std::size_t tag(std::size_t index) const { + return tags_[index]; + } + + void setTag(std::size_t index, std::size_t tag) { + FOLLY_SAFE_DCHECK( + this != emptyInstance() && tag >= 0x80 && tag <= 0xff, ""); + FOLLY_SAFE_CHECK(tags_[index] == 0, ""); + tags_[index] = static_cast<uint8_t>(tag); + } + + void clearTag(std::size_t index) { + FOLLY_SAFE_CHECK((tags_[index] & 0x80) != 0, ""); + tags_[index] = 0; + } + +#if FOLLY_NEON + //////// + // Tag filtering using NEON intrinsics + + SparseMaskIter tagMatchIter(std::size_t needle) const { + FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, ""); + uint8x16_t tagV = vld1q_u8(&tags_[0]); + auto needleV = vdupq_n_u8(static_cast<uint8_t>(needle)); + auto eqV = vceqq_u8(tagV, needleV); + // get info from every byte into the bottom half of every uint16_t + // by shifting right 4, then round to get it into a 64-bit vector + uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(eqV), 4); + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask; + return SparseMaskIter(mask); + } + + MaskType occupiedMask() const { + uint8x16_t tagV = vld1q_u8(&tags_[0]); + // signed shift extends top bit to all bits + auto occupiedV = + vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_u8(tagV), 7)); + uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(occupiedV), 4); + return vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask; + } +#else + //////// + // Tag filtering using SSE2 intrinsics + + TagVector const* tagVector() const { + return static_cast<TagVector const*>(static_cast<void const*>(&tags_[0])); + } + + SparseMaskIter tagMatchIter(std::size_t needle) const { + FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, ""); + auto tagV = _mm_load_si128(tagVector()); + + // TRICKY! It may seem strange to have a std::size_t needle and narrow + // it at the last moment, rather than making HashPair::second be a + // uint8_t, but the latter choice sometimes leads to a performance + // problem. + // + // On architectures with SSE2 but not AVX2, _mm_set1_epi8 expands + // to multiple instructions. One of those is a MOVD of either 4 or + // 8 byte width. Only the bottom byte of that move actually affects + // the result, but if a 1-byte needle has been spilled then this will + // be a 4 byte load. GCC 5.5 has been observed to reload needle + // (or perhaps fuse a reload and part of a previous static_cast) + // needle using a MOVZX with a 1 byte load in parallel with the MOVD. + // This combination causes a failure of store-to-load forwarding, + // which has a big performance penalty (60 nanoseconds per find on + // a microbenchmark). Keeping needle >= 4 bytes avoids the problem + // and also happens to result in slightly more compact assembly. + auto needleV = _mm_set1_epi8(static_cast<uint8_t>(needle)); + auto eqV = _mm_cmpeq_epi8(tagV, needleV); + auto mask = _mm_movemask_epi8(eqV) & kFullMask; + return SparseMaskIter{mask}; + } + + MaskType occupiedMask() const { + auto tagV = _mm_load_si128(tagVector()); + return _mm_movemask_epi8(tagV) & kFullMask; + } +#endif + + DenseMaskIter occupiedIter() const { + return DenseMaskIter{&tags_[0], occupiedMask()}; + } + + MaskRangeIter occupiedRangeIter() const { + return MaskRangeIter{occupiedMask()}; + } + + LastOccupiedInMask lastOccupied() const { + return LastOccupiedInMask{occupiedMask()}; + } + + FirstEmptyInMask firstEmpty() const { + return FirstEmptyInMask{occupiedMask() ^ kFullMask}; + } + + bool occupied(std::size_t index) const { + FOLLY_SAFE_DCHECK(tags_[index] == 0 || (tags_[index] & 0x80) != 0, ""); + return tags_[index] != 0; + } + + Item* itemAddr(std::size_t i) const { + return static_cast<Item*>( + const_cast<void*>(static_cast<void const*>(&rawItems_[i]))); + } + + Item& item(std::size_t i) { + FOLLY_SAFE_DCHECK(this->occupied(i), ""); + return *launder(itemAddr(i)); + } + + Item const& citem(std::size_t i) const { + FOLLY_SAFE_DCHECK(this->occupied(i), ""); + return *launder(itemAddr(i)); + } + + static F14Chunk& owner(Item& item, std::size_t index) { + auto rawAddr = + static_cast<uint8_t*>(static_cast<void*>(std::addressof(item))) - + offsetof(F14Chunk, rawItems_) - index * sizeof(Item); + auto chunkAddr = static_cast<F14Chunk*>(static_cast<void*>(rawAddr)); + FOLLY_SAFE_DCHECK(std::addressof(item) == chunkAddr->itemAddr(index), ""); + return *chunkAddr; + } +}; + +//////////////// + +// PackedChunkItemPtr points to an Item in an F14Chunk, allowing both the +// Item& and its index to be recovered. It sorts by the address of the +// item, and it only works for items that are in a properly-aligned chunk. + +// generic form, not actually packed +template <typename Ptr> +class PackedChunkItemPtr { + public: + PackedChunkItemPtr(Ptr p, std::size_t i) noexcept : ptr_{p}, index_{i} { + FOLLY_SAFE_DCHECK(ptr_ != nullptr || index_ == 0, ""); + } + + Ptr ptr() const { + return ptr_; + } + + std::size_t index() const { + return index_; + } + + bool operator<(PackedChunkItemPtr const& rhs) const { + FOLLY_SAFE_DCHECK(ptr_ != rhs.ptr_ || index_ == rhs.index_, ""); + return ptr_ < rhs.ptr_; + } + + bool operator==(PackedChunkItemPtr const& rhs) const { + FOLLY_SAFE_DCHECK(ptr_ != rhs.ptr_ || index_ == rhs.index_, ""); + return ptr_ == rhs.ptr_; + } + + bool operator!=(PackedChunkItemPtr const& rhs) const { + return !(*this == rhs); + } + + private: + Ptr ptr_; + std::size_t index_; +}; + +// Bare pointer form, packed into a uintptr_t. Uses only bits wasted by +// alignment, so it works on 32-bit and 64-bit platforms +template <typename T> +class PackedChunkItemPtr<T*> { + static_assert((alignof(F14Chunk<T>) % 16) == 0, ""); + + // Chunks are 16-byte aligned, so we can maintain a packed pointer to a + // chunk item by packing the 4-bit item index into the least significant + // bits of a pointer to the chunk itself. This makes ItemIter::pack + // more expensive, however, since it has to compute the chunk address. + // + // Chunk items have varying alignment constraints, so it would seem + // to be that we can't do a similar trick while using only bit masking + // operations on the Item* itself. It happens to be, however, that if + // sizeof(Item) is not a multiple of 16 then we can recover a portion + // of the index bits from the knowledge that the Item-s are stored in + // an array that is itself 16-byte aligned. + // + // If kAlignBits is the number of trailing zero bits in sizeof(Item) + // (up to 4), then we can borrow those bits to store kAlignBits of the + // index directly. We can recover (4 - kAlignBits) bits of the index + // from the item pointer itself, by defining/observing that + // + // A = kAlignBits (A <= 4) + // + // S = (sizeof(Item) % 16) >> A (shifted-away bits are all zero) + // + // R = (itemPtr % 16) >> A (shifted-away bits are all zero) + // + // M = 16 >> A + // + // itemPtr % 16 = (index * sizeof(Item)) % 16 + // + // (R * 2^A) % 16 = (index * (sizeof(Item) % 16)) % 16 + // + // (R * 2^A) % 16 = (index * 2^A * S) % 16 + // + // R % M = (index * S) % M + // + // S is relatively prime with M, so a multiplicative inverse is easy + // to compute + // + // Sinv = S^(M - 1) % M + // + // (R * Sinv) % M = index % M + // + // This lets us recover the bottom bits of the index. When sizeof(T) + // is 8-byte aligned kSizeInverse will always be 1. When sizeof(T) + // is 4-byte aligned kSizeInverse will be either 1 or 3. + + // returns pow(x, y) % m + static constexpr uintptr_t powerMod(uintptr_t x, uintptr_t y, uintptr_t m) { + return y == 0 ? 1 : (x * powerMod(x, y - 1, m)) % m; + } + + static constexpr uintptr_t kIndexBits = 4; + static constexpr uintptr_t kIndexMask = (uintptr_t{1} << kIndexBits) - 1; + + static constexpr uintptr_t kAlignBits = constexpr_min( + uintptr_t{4}, + constexpr_find_first_set(uintptr_t{sizeof(T)}) - 1); + + static constexpr uintptr_t kAlignMask = (uintptr_t{1} << kAlignBits) - 1; + + static constexpr uintptr_t kModulus = uintptr_t{1} + << (kIndexBits - kAlignBits); + static constexpr uintptr_t kSizeInverse = + powerMod(sizeof(T) >> kAlignBits, kModulus - 1, kModulus); + + public: + PackedChunkItemPtr(T* p, std::size_t i) noexcept { + uintptr_t encoded = i >> (kIndexBits - kAlignBits); + assume((encoded & ~kAlignMask) == 0); + raw_ = reinterpret_cast<uintptr_t>(p) | encoded; + FOLLY_SAFE_DCHECK(p == ptr(), ""); + FOLLY_SAFE_DCHECK(i == index(), ""); + } + + T* ptr() const { + return reinterpret_cast<T*>(raw_ & ~kAlignMask); + } + + std::size_t index() const { + auto encoded = (raw_ & kAlignMask) << (kIndexBits - kAlignBits); + auto deduced = + ((raw_ >> kAlignBits) * kSizeInverse) & (kIndexMask >> kAlignBits); + return encoded | deduced; + } + + bool operator<(PackedChunkItemPtr const& rhs) const { + return raw_ < rhs.raw_; + } + bool operator==(PackedChunkItemPtr const& rhs) const { + return raw_ == rhs.raw_; + } + bool operator!=(PackedChunkItemPtr const& rhs) const { + return !(*this == rhs); + } + + private: + uintptr_t raw_; +}; + +template <typename ChunkPtr> +class F14ItemIter { + private: + using Chunk = typename std::pointer_traits<ChunkPtr>::element_type; + + public: + using Item = typename Chunk::Item; + using ItemPtr = typename std::pointer_traits<ChunkPtr>::template rebind<Item>; + using ItemConstPtr = + typename std::pointer_traits<ChunkPtr>::template rebind<Item const>; + + using Packed = PackedChunkItemPtr<ItemPtr>; + + //// PUBLIC + + F14ItemIter() noexcept : itemPtr_{nullptr}, index_{0} {} + + // default copy and move constructors and assignment operators are correct + + explicit F14ItemIter(Packed const& packed) + : itemPtr_{packed.ptr()}, index_{packed.index()} {} + + F14ItemIter(ChunkPtr chunk, std::size_t index) + : itemPtr_{std::pointer_traits<ItemPtr>::pointer_to(chunk->item(index))}, + index_{index} { + FOLLY_SAFE_DCHECK(index < Chunk::kCapacity, ""); + assume( + std::pointer_traits<ItemPtr>::pointer_to(chunk->item(index)) != + nullptr); + assume(itemPtr_ != nullptr); + } + + FOLLY_ALWAYS_INLINE void advanceImpl(bool checkEof, bool likelyDead) { + auto c = chunk(); + + // common case is packed entries + while (index_ > 0) { + --index_; + --itemPtr_; + if (LIKELY(c->occupied(index_))) { + return; + } + } + + // It's fairly common for an iterator to be advanced and then become + // dead, for example in the return value from erase(iter) or in + // the last step of a loop. We'd like to make sure that the entire + // advance() method can be eliminated by the compiler's dead code + // elimination pass. To do that it must eliminate the loops, which + // requires it to prove that they have no side effects. It's easy + // to show that there are no escaping stores, but at the moment + // compilers also consider an infinite loop to be a side effect. + // (There are parts of the standard that would allow them to treat + // this as undefined behavior, but at the moment they don't exploit + // those clauses.) + // + // The following loop should really be a while loop, which would + // save a register, some instructions, and a conditional branch, + // but by writing it as a for loop the compiler can prove to itself + // that it will eventually terminate. (No matter that even if the + // loop executed in a single cycle it would take about 200 years to + // run all 2^64 iterations.) + // + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82776 has the bug we + // filed about the issue. while (true) { + for (std::size_t i = 1; !likelyDead || i != 0; ++i) { + if (checkEof) { + // exhausted the current chunk + if (UNLIKELY(c->eof())) { + FOLLY_SAFE_DCHECK(index_ == 0, ""); + itemPtr_ = nullptr; + return; + } + } else { + FOLLY_SAFE_DCHECK(!c->eof(), ""); + } + --c; + auto last = c->lastOccupied(); + if (checkEof && !likelyDead) { + prefetchAddr(&*c - 1); + } + if (LIKELY(last.hasIndex())) { + index_ = last.index(); + itemPtr_ = std::pointer_traits<ItemPtr>::pointer_to(c->item(index_)); + return; + } + } + } + + void precheckedAdvance() { + advanceImpl(false, false); + } + + FOLLY_ALWAYS_INLINE void advance() { + advanceImpl(true, false); + } + + FOLLY_ALWAYS_INLINE void advanceLikelyDead() { + advanceImpl(true, true); + } + + ChunkPtr chunk() const { + return std::pointer_traits<ChunkPtr>::pointer_to( + Chunk::owner(*itemPtr_, index_)); + } + + std::size_t index() const { + return index_; + } + + Item* itemAddr() const { + return std::addressof(*itemPtr_); + } + Item& item() const { + return *itemPtr_; + } + Item const& citem() const { + return *itemPtr_; + } + + bool atEnd() const { + return itemPtr_ == nullptr; + } + + Packed pack() const { + return Packed{itemPtr_, static_cast<uint8_t>(index_)}; + } + + bool operator==(F14ItemIter const& rhs) const { + // this form makes iter == end() into a single null check after inlining + // and constant propagation + return itemPtr_ == rhs.itemPtr_; + } + + bool operator!=(F14ItemIter const& rhs) const { + return !(*this == rhs); + } + + private: + ItemPtr itemPtr_; + std::size_t index_; +}; + +//////////////// + +template <typename SizeType, typename ItemIter, bool EnablePackedItemIter> +struct SizeAndPackedBegin { + SizeType size_{0}; + + private: + typename ItemIter::Packed packedBegin_{ItemIter{}.pack()}; + + public: + typename ItemIter::Packed& packedBegin() { + return packedBegin_; + } + + typename ItemIter::Packed const& packedBegin() const { + return packedBegin_; + } +}; + +template <typename SizeType, typename ItemIter> +struct SizeAndPackedBegin<SizeType, ItemIter, false> { + SizeType size_{0}; + + [[noreturn]] typename ItemIter::Packed& packedBegin() { + assume_unreachable(); + } + + [[noreturn]] typename ItemIter::Packed const& packedBegin() const { + assume_unreachable(); + } +}; + +template <typename Policy> +class F14Table : public Policy { + public: + using Item = typename Policy::Item; + + using value_type = typename Policy::Value; + using allocator_type = typename Policy::Alloc; + + private: + using Alloc = typename Policy::Alloc; + using AllocTraits = typename Policy::AllocTraits; + using Hasher = typename Policy::Hasher; + using InternalSizeType = typename Policy::InternalSizeType; + using KeyEqual = typename Policy::KeyEqual; + + using Policy::kAllocIsAlwaysEqual; + using Policy::kContinuousCapacity; + using Policy::kDefaultConstructIsNoexcept; + using Policy::kEnableItemIteration; + using Policy::kSwapIsNoexcept; + + using Policy::destroyItemOnClear; + using Policy::isAvalanchingHasher; + using Policy::prefetchBeforeCopy; + using Policy::prefetchBeforeDestroy; + using Policy::prefetchBeforeRehash; + + using ByteAlloc = typename AllocTraits::template rebind_alloc<uint8_t>; + using BytePtr = typename std::allocator_traits<ByteAlloc>::pointer; + + using Chunk = F14Chunk<Item>; + using ChunkPtr = + typename std::pointer_traits<BytePtr>::template rebind<Chunk>; + + using HashPair = typename F14HashToken::HashPair; + + public: + using ItemIter = F14ItemIter<ChunkPtr>; + + private: + //////// begin fields + + ChunkPtr chunks_{Chunk::emptyInstance()}; + InternalSizeType chunkMask_{0}; + SizeAndPackedBegin<InternalSizeType, ItemIter, kEnableItemIteration> + sizeAndPackedBegin_; + + //////// end fields + + void swapContents(F14Table& rhs) noexcept { + using std::swap; + swap(chunks_, rhs.chunks_); + swap(chunkMask_, rhs.chunkMask_); + swap(sizeAndPackedBegin_.size_, rhs.sizeAndPackedBegin_.size_); + if (kEnableItemIteration) { + swap( + sizeAndPackedBegin_.packedBegin(), + rhs.sizeAndPackedBegin_.packedBegin()); + } + } + + public: + F14Table( + std::size_t initialCapacity, + Hasher const& hasher, + KeyEqual const& keyEqual, + Alloc const& alloc) + : Policy{hasher, keyEqual, alloc} { + if (initialCapacity > 0) { + reserve(initialCapacity); + } + } + + F14Table(F14Table const& rhs) : Policy{rhs} { + buildFromF14Table(rhs); + } + + F14Table(F14Table const& rhs, Alloc const& alloc) : Policy{rhs, alloc} { + buildFromF14Table(rhs); + } + + F14Table(F14Table&& rhs) noexcept( + std::is_nothrow_move_constructible<Hasher>::value&& + std::is_nothrow_move_constructible<KeyEqual>::value&& + std::is_nothrow_move_constructible<Alloc>::value) + : Policy{std::move(rhs)} { + swapContents(rhs); + } + + F14Table(F14Table&& rhs, Alloc const& alloc) noexcept(kAllocIsAlwaysEqual) + : Policy{std::move(rhs), alloc} { + if (kAllocIsAlwaysEqual || this->alloc() == rhs.alloc()) { + // move storage (common case) + swapContents(rhs); + } else { + // new storage because allocators unequal, move values (rare case) + buildFromF14Table(std::move(rhs)); + } + } + + F14Table& operator=(F14Table const& rhs) { + if (this != &rhs) { + reset(); + static_cast<Policy&>(*this) = rhs; + buildFromF14Table(rhs); + } + return *this; + } + + F14Table& operator=(F14Table&& rhs) noexcept( + std::is_nothrow_move_assignable<Hasher>::value&& + std::is_nothrow_move_assignable<KeyEqual>::value && + (kAllocIsAlwaysEqual || + (AllocTraits::propagate_on_container_move_assignment::value && + std::is_nothrow_move_assignable<Alloc>::value))) { + if (this != &rhs) { + reset(); + static_cast<Policy&>(*this) = std::move(rhs); + if (AllocTraits::propagate_on_container_move_assignment::value || + kAllocIsAlwaysEqual || this->alloc() == rhs.alloc()) { + // move storage (common case) + swapContents(rhs); + } else { + // new storage because allocators unequal, move values (rare case) + buildFromF14Table(std::move(rhs)); + } + } + return *this; + } + + ~F14Table() { + reset(); + } + + void swap(F14Table& rhs) noexcept(kSwapIsNoexcept) { + // If propagate_on_container_swap is false and allocators are + // not equal, the only way to accomplish a swap would be to do + // dynamic allocation and then move (or swap) each contained value. + // AllocatorAwareContainer-s are not supposed to attempt this, but + // rather are supposed to have undefined behavior in that case. + FOLLY_SAFE_CHECK( + AllocTraits::propagate_on_container_swap::value || + kAllocIsAlwaysEqual || this->alloc() == rhs.alloc(), + "swap is undefined for unequal non-propagating allocators"); + this->swapPolicy(rhs); + swapContents(rhs); + } + + private: + //////// hash helpers + + // Hash values are used to compute the desired position, which is the + // chunk index at which we would like to place a value (if there is no + // overflow), and the tag, which is an additional 7 bits of entropy. + // + // The standard's definition of hash function quality only refers to + // the probability of collisions of the entire hash value, not to the + // probability of collisions of the results of shifting or masking the + // hash value. Some hash functions, however, provide this stronger + // guarantee (not quite the same as the definition of avalanching, + // but similar). + // + // If the user-supplied hasher is an avalanching one (each bit of the + // hash value has a 50% chance of being the same for differing hash + // inputs), then we can just take 7 bits of the hash value for the tag + // and the rest for the desired position. Avalanching hashers also + // let us map hash value to array index position with just a bitmask + // without risking clumping. (Many hash tables just accept the risk + // and do it regardless.) + // + // std::hash<std::string> avalanches in all implementations we've + // examined: libstdc++-v3 uses MurmurHash2, and libc++ uses CityHash + // or MurmurHash2. The other std::hash specializations, however, do not + // have this property. std::hash for integral and pointer values is the + // identity function on libstdc++-v3 and libc++, in particular. In our + // experience it is also fairly common for user-defined specializations + // of std::hash to combine fields in an ad-hoc way that does not evenly + // distribute entropy among the bits of the result (a + 37 * b, for + // example, where a and b are integer fields). + // + // For hash functions we don't trust to avalanche, we repair things by + // applying a bit mixer to the user-supplied hash. + +#if FOLLY_X64 || FOLLY_AARCH64 + // 64-bit + static HashPair splitHash(std::size_t hash) { + static_assert(sizeof(std::size_t) == sizeof(uint64_t), ""); + std::size_t tag; + if (!isAvalanchingHasher()) { +#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE +#if FOLLY_SSE_PREREQ(4, 2) + // SSE4.2 CRC + std::size_t c = _mm_crc32_u64(0, hash); + tag = (c >> 24) | 0x80; + hash += c; +#else + // CRC is optional on armv8 (-march=armv8-a+crc), standard on armv8.1 + std::size_t c = __crc32cd(0, hash); + tag = (c >> 24) | 0x80; + hash += c; +#endif +#else + // The mixer below is not fully avalanching for all 64 bits of + // output, but looks quite good for bits 18..63 and puts plenty + // of entropy even lower when considering multiple bits together + // (like the tag). Importantly, when under register pressure it + // uses fewer registers, instructions, and immediate constants + // than the alternatives, resulting in compact code that is more + // easily inlinable. In one instantiation a modified Murmur mixer + // was 48 bytes of assembly (even after using the same multiplicand + // for both steps) and this one was 27 bytes, for example. + auto const kMul = 0xc4ceb9fe1a85ec53ULL; +#ifdef _WIN32 + __int64 signedHi; + __int64 signedLo = _mul128( + static_cast<__int64>(hash), static_cast<__int64>(kMul), &signedHi); + auto hi = static_cast<uint64_t>(signedHi); + auto lo = static_cast<uint64_t>(signedLo); +#else + auto hi = static_cast<uint64_t>( + (static_cast<unsigned __int128>(hash) * kMul) >> 64); + auto lo = hash * kMul; +#endif + hash = hi ^ lo; + hash *= kMul; + tag = ((hash >> 15) & 0x7f) | 0x80; + hash >>= 22; +#endif + } else { + // we don't trust the top bit + tag = (hash >> 56) | 0x80; + } + return std::make_pair(hash, tag); + } +#else + // 32-bit + static HashPair splitHash(std::size_t hash) { + static_assert(sizeof(std::size_t) == sizeof(uint32_t), ""); + uint8_t tag; + if (!isAvalanchingHasher()) { +#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE +#if FOLLY_SSE_PREREQ(4, 2) + // SSE4.2 CRC + auto c = _mm_crc32_u32(0, hash); + tag = static_cast<uint8_t>(~(c >> 25)); + hash += c; +#else + auto c = __crc32cw(0, hash); + tag = static_cast<uint8_t>(~(c >> 25)); + hash += c; +#endif +#else + // finalizer for 32-bit murmur2 + hash ^= hash >> 13; + hash *= 0x5bd1e995; + hash ^= hash >> 15; + tag = static_cast<uint8_t>(~(hash >> 25)); +#endif + } else { + // we don't trust the top bit + tag = (hash >> 24) | 0x80; + } + return std::make_pair(hash, tag); + } +#endif + + //////// memory management helpers + + static std::size_t computeCapacity( + std::size_t chunkCount, + std::size_t scale) { + FOLLY_SAFE_DCHECK(!(chunkCount > 1 && scale == 0), ""); + FOLLY_SAFE_DCHECK( + scale < (std::size_t{1} << Chunk::kCapacityScaleBits), ""); + FOLLY_SAFE_DCHECK((chunkCount & (chunkCount - 1)) == 0, ""); + return (((chunkCount - 1) >> Chunk::kCapacityScaleShift) + 1) * scale; + } + + std::pair<std::size_t, std::size_t> computeChunkCountAndScale( + std::size_t desiredCapacity, + bool continuousSingleChunkCapacity, + bool continuousMultiChunkCapacity) const { + if (desiredCapacity <= Chunk::kCapacity) { + // we can go to 100% capacity in a single chunk with no problem + if (!continuousSingleChunkCapacity) { + if (desiredCapacity <= 2) { + desiredCapacity = 2; + } else if (desiredCapacity <= 6) { + desiredCapacity = 6; + } else { + desiredCapacity = Chunk::kCapacity; + } + } + auto rv = std::make_pair(std::size_t{1}, desiredCapacity); + FOLLY_SAFE_DCHECK( + computeCapacity(rv.first, rv.second) == desiredCapacity, ""); + return rv; + } else { + std::size_t minChunks = + (desiredCapacity - 1) / Chunk::kDesiredCapacity + 1; + std::size_t chunkPow = findLastSet(minChunks - 1); + if (chunkPow == 8 * sizeof(std::size_t)) { + throw_exception<std::bad_alloc>(); + } + + std::size_t chunkCount = std::size_t{1} << chunkPow; + + // Let cc * scale be the actual capacity. + // cc = ((chunkCount - 1) >> kCapacityScaleShift) + 1. + // If chunkPow >= kCapacityScaleShift, then cc = chunkCount >> + // kCapacityScaleShift = 1 << (chunkPow - kCapacityScaleShift), + // otherwise it equals 1 = 1 << 0. Let cc = 1 << ss. + std::size_t ss = chunkPow >= Chunk::kCapacityScaleShift + ? chunkPow - Chunk::kCapacityScaleShift + : 0; + + std::size_t scale; + if (continuousMultiChunkCapacity) { + // (1 << ss) * scale >= desiredCapacity + scale = ((desiredCapacity - 1) >> ss) + 1; + } else { + // (1 << ss) * scale == chunkCount * kDesiredCapacity + scale = Chunk::kDesiredCapacity << (chunkPow - ss); + } + + std::size_t actualCapacity = computeCapacity(chunkCount, scale); + FOLLY_SAFE_DCHECK(actualCapacity >= desiredCapacity, ""); + if (actualCapacity > max_size()) { + throw_exception<std::bad_alloc>(); + } + + return std::make_pair(chunkCount, scale); + } + } + + static std::size_t chunkAllocSize( + std::size_t chunkCount, + std::size_t capacityScale) { + FOLLY_SAFE_DCHECK(chunkCount > 0, ""); + FOLLY_SAFE_DCHECK(!(chunkCount > 1 && capacityScale == 0), ""); + if (chunkCount == 1) { + static_assert(offsetof(Chunk, rawItems_) == 16, ""); + return 16 + sizeof(Item) * computeCapacity(1, capacityScale); + } else { + return sizeof(Chunk) * chunkCount; + } + } + + ChunkPtr initializeChunks( + BytePtr raw, + std::size_t chunkCount, + std::size_t capacityScale) { + static_assert(std::is_trivial<Chunk>::value, "F14Chunk should be POD"); + auto chunks = static_cast<Chunk*>(static_cast<void*>(&*raw)); + for (std::size_t i = 0; i < chunkCount; ++i) { + chunks[i].clear(); + } + chunks[0].markEof(capacityScale); + return std::pointer_traits<ChunkPtr>::pointer_to(*chunks); + } + + std::size_t itemCount() const noexcept { + if (chunkMask_ == 0) { + return computeCapacity(1, chunks_->capacityScale()); + } else { + return (chunkMask_ + 1) * Chunk::kCapacity; + } + } + + public: + ItemIter begin() const noexcept { + FOLLY_SAFE_DCHECK(kEnableItemIteration, ""); + return ItemIter{sizeAndPackedBegin_.packedBegin()}; + } + + ItemIter end() const noexcept { + return ItemIter{}; + } + + bool empty() const noexcept { + return size() == 0; + } + + InternalSizeType size() const noexcept { + return sizeAndPackedBegin_.size_; + } + + std::size_t max_size() const noexcept { + auto& a = this->alloc(); + return std::min<std::size_t>( + (std::numeric_limits<InternalSizeType>::max)(), + AllocTraits::max_size(a)); + } + + std::size_t bucket_count() const noexcept { + return computeCapacity(chunkMask_ + 1, chunks_->capacityScale()); + } + + std::size_t max_bucket_count() const noexcept { + return max_size(); + } + + float load_factor() const noexcept { + return empty() + ? 0.0f + : static_cast<float>(size()) / static_cast<float>(bucket_count()); + } + + float max_load_factor() const noexcept { + return 1.0f; + } + + void max_load_factor(float) noexcept { + // Probing hash tables can't run load factors >= 1 (unlike chaining + // tables). In addition, we have measured that there is little or + // no performance advantage to running a smaller load factor (cache + // locality losses outweigh the small reduction in probe lengths, + // often making it slower). Therefore, we've decided to just fix + // max_load_factor at 1.0f regardless of what the user requests. + // This has an additional advantage that we don't have to store it. + // Taking alignment into consideration this makes every F14 table + // 8 bytes smaller, and is part of the reason an empty F14NodeMap + // is almost half the size of an empty std::unordered_map (32 vs + // 56 bytes). + // + // I don't have a strong opinion on whether we should remove this + // method or leave a stub, let ngbronson or xshi know if you have a + // compelling argument either way. + } + + private: + // Our probe strategy is to advance through additional chunks with + // a stride that is key-specific. This is called double hashing, + // and is a well known and high quality probing strategy. So long as + // the stride and the chunk count are relatively prime, we will visit + // every chunk once and then return to the original chunk, letting us + // detect and end the cycle. The chunk count is a power of two, so + // we can satisfy the relatively prime part by choosing an odd stride. + // We've already computed a high quality secondary hash value for the + // tag, so we just use it for the second probe hash as well. + // + // At the maximum load factor of 12/14, expected probe length for a + // find hit is 1.041, with 99% of keys found in the first three chunks. + // Expected probe length for a find miss (or insert) is 1.275, with a + // p99 probe length of 4 (fewer than 1% of failing find look at 5 or + // more chunks). + // + // This code is structured so you can try various ways of encoding + // the current probe state. For example, at the moment the probe's + // state is the position in the cycle and the resulting chunk index is + // computed from that inside probeCurrentIndex. We could also make the + // probe state the chunk index, and then increment it by hp.second * + // 2 + 1 in probeAdvance. Wrapping can be applied early or late as + // well. This particular code seems to be easier for the optimizer + // to understand. + // + // We could also implement probing strategies that resulted in the same + // tour for every key initially assigned to a chunk (linear probing or + // quadratic), but that results in longer probe lengths. In particular, + // the cache locality wins of linear probing are not worth the increase + // in probe lengths (extra work and less branch predictability) in + // our experiments. + + std::size_t probeDelta(HashPair hp) const { + return 2 * hp.second + 1; + } + + template <typename K> + FOLLY_ALWAYS_INLINE ItemIter findImpl(HashPair hp, K const& key) const { + std::size_t index = hp.first; + std::size_t step = probeDelta(hp); + for (std::size_t tries = 0; tries <= chunkMask_; ++tries) { + ChunkPtr chunk = chunks_ + (index & chunkMask_); + if (sizeof(Chunk) > 64) { + prefetchAddr(chunk->itemAddr(8)); + } + auto hits = chunk->tagMatchIter(hp.second); + while (hits.hasNext()) { + auto i = hits.next(); + if (LIKELY(this->keyMatchesItem(key, chunk->item(i)))) { + // Tag match and key match were both successful. The chance + // of a false tag match is 1/128 for each key in the chunk + // (with a proper hash function). + return ItemIter{chunk, i}; + } + } + if (LIKELY(chunk->outboundOverflowCount() == 0)) { + // No keys that wanted to be placed in this chunk were denied + // entry, so our search is over. This is the common case. + break; + } + index += step; + } + // Loop exit because tries is exhausted is rare, but possible. + // That means that for every chunk there is currently a key present + // in the map that visited that chunk on its probe search but ended + // up somewhere else, and we have searched every chunk. + return ItemIter{}; + } + + public: + // Prehashing splits the work of find(key) into two calls, enabling you + // to manually implement loop pipelining for hot bulk lookups. prehash + // computes the hash and prefetches the first computed memory location, + // and the two-arg find(F14HashToken,K) performs the rest of the search. + template <typename K> + F14HashToken prehash(K const& key) const { + FOLLY_SAFE_DCHECK(chunks_ != nullptr, ""); + auto hp = splitHash(this->computeKeyHash(key)); + ChunkPtr firstChunk = chunks_ + (hp.first & chunkMask_); + prefetchAddr(firstChunk); + return F14HashToken(std::move(hp)); + } + + template <typename K> + FOLLY_ALWAYS_INLINE ItemIter find(K const& key) const { + auto hp = splitHash(this->computeKeyHash(key)); + return findImpl(hp, key); + } + + template <typename K> + FOLLY_ALWAYS_INLINE ItemIter + find(F14HashToken const& token, K const& key) const { + FOLLY_SAFE_DCHECK( + splitHash(this->computeKeyHash(key)) == static_cast<HashPair>(token), + ""); + return findImpl(static_cast<HashPair>(token), key); + } + + // Searches for a key using a key predicate that is a refinement + // of key equality. func(k) should return true only if k is equal + // to key according to key_eq(), but is allowed to apply additional + // constraints. + template <typename K, typename F> + FOLLY_ALWAYS_INLINE ItemIter findMatching(K const& key, F&& func) const { + auto hp = splitHash(this->computeKeyHash(key)); + std::size_t index = hp.first; + std::size_t step = probeDelta(hp); + for (std::size_t tries = 0; tries <= chunkMask_; ++tries) { + ChunkPtr chunk = chunks_ + (index & chunkMask_); + if (sizeof(Chunk) > 64) { + prefetchAddr(chunk->itemAddr(8)); + } + auto hits = chunk->tagMatchIter(hp.second); + while (hits.hasNext()) { + auto i = hits.next(); + if (LIKELY( + func(this->keyForValue(this->valueAtItem(chunk->item(i)))))) { + return ItemIter{chunk, i}; + } + } + if (LIKELY(chunk->outboundOverflowCount() == 0)) { + break; + } + index += step; + } + return ItemIter{}; + } + + private: + void adjustSizeAndBeginAfterInsert(ItemIter iter) { + if (kEnableItemIteration) { + // packedBegin is the max of all valid ItemIter::pack() + auto packed = iter.pack(); + if (sizeAndPackedBegin_.packedBegin() < packed) { + sizeAndPackedBegin_.packedBegin() = packed; + } + } + + ++sizeAndPackedBegin_.size_; + } + + // Ignores hp if pos.chunk()->hostedOverflowCount() == 0 + void eraseBlank(ItemIter iter, HashPair hp) { + iter.chunk()->clearTag(iter.index()); + + if (iter.chunk()->hostedOverflowCount() != 0) { + // clean up + std::size_t index = hp.first; + std::size_t delta = probeDelta(hp); + uint8_t hostedOp = 0; + while (true) { + ChunkPtr chunk = chunks_ + (index & chunkMask_); + if (chunk == iter.chunk()) { + chunk->adjustHostedOverflowCount(hostedOp); + break; + } + chunk->decrOutboundOverflowCount(); + hostedOp = Chunk::kDecrHostedOverflowCount; + index += delta; + } + } + } + + void adjustSizeAndBeginBeforeErase(ItemIter iter) { + --sizeAndPackedBegin_.size_; + if (kEnableItemIteration) { + if (iter.pack() == sizeAndPackedBegin_.packedBegin()) { + if (size() == 0) { + iter = ItemIter{}; + } else { + iter.precheckedAdvance(); + } + sizeAndPackedBegin_.packedBegin() = iter.pack(); + } + } + } + + template <typename... Args> + void insertAtBlank(ItemIter pos, HashPair hp, Args&&... args) { + try { + auto dst = pos.itemAddr(); + this->constructValueAtItem(*this, dst, std::forward<Args>(args)...); + } catch (...) { + eraseBlank(pos, hp); + throw; + } + adjustSizeAndBeginAfterInsert(pos); + } + + ItemIter allocateTag(uint8_t* fullness, HashPair hp) { + ChunkPtr chunk; + std::size_t index = hp.first; + std::size_t delta = probeDelta(hp); + uint8_t hostedOp = 0; + while (true) { + index &= chunkMask_; + chunk = chunks_ + index; + if (LIKELY(fullness[index] < Chunk::kCapacity)) { + break; + } + chunk->incrOutboundOverflowCount(); + hostedOp = Chunk::kIncrHostedOverflowCount; + index += delta; + } + unsigned itemIndex = fullness[index]++; + FOLLY_SAFE_DCHECK(!chunk->occupied(itemIndex), ""); + chunk->setTag(itemIndex, hp.second); + chunk->adjustHostedOverflowCount(hostedOp); + return ItemIter{chunk, itemIndex}; + } + + ChunkPtr lastOccupiedChunk() const { + FOLLY_SAFE_DCHECK(size() > 0, ""); + if (kEnableItemIteration) { + return begin().chunk(); + } else { + return chunks_ + chunkMask_; + } + } + + template <typename T> + void directBuildFrom(T&& src) { + FOLLY_SAFE_DCHECK(src.size() > 0 && chunkMask_ == src.chunkMask_, ""); + + // We use std::forward<T> to allow portions of src to be moved out by + // either beforeBuild or afterBuild, but we are just relying on good + // behavior of our Policy superclass to ensure that any particular + // field of this is a donor at most once. + + auto undoState = + this->beforeBuild(src.size(), bucket_count(), std::forward<T>(src)); + bool success = false; + SCOPE_EXIT { + this->afterBuild( + undoState, success, src.size(), bucket_count(), std::forward<T>(src)); + }; + + // Copy can fail part-way through if a Value copy constructor throws. + // Failing afterBuild is limited in its cleanup power in this case, + // because it can't enumerate the items that were actually copied. + // Fortunately we can divide the situation into cases where all of + // the state is owned by the table itself (F14Node and F14Value), + // for which clearImpl() can do partial cleanup, and cases where all + // of the values are owned by the policy (F14Vector), in which case + // partial failure should not occur. Sorry for the subtle invariants + // in the Policy API. + + if (is_trivially_copyable<Item>::value && !this->destroyItemOnClear() && + itemCount() == src.itemCount()) { + FOLLY_SAFE_DCHECK(chunkMask_ == src.chunkMask_, ""); + + auto scale = chunks_->capacityScale(); + + // most happy path + auto n = chunkAllocSize(chunkMask_ + 1, scale); + std::memcpy(&chunks_[0], &src.chunks_[0], n); + sizeAndPackedBegin_.size_ = src.size(); + if (kEnableItemIteration) { + auto srcBegin = src.begin(); + sizeAndPackedBegin_.packedBegin() = + ItemIter{chunks_ + (srcBegin.chunk() - src.chunks_), + srcBegin.index()} + .pack(); + } + if (kContinuousCapacity) { + // capacityScale might not match even if itemCount matches + chunks_->setCapacityScale(scale); + } + } else { + // Happy path, no rehash but pack items toward bottom of chunk + // and use copy constructor. Don't try to optimize by using + // lastOccupiedChunk() because there may be higher unoccupied chunks + // with the overflow bit set. + auto srcChunk = &src.chunks_[chunkMask_]; + Chunk* dstChunk = &chunks_[chunkMask_]; + do { + dstChunk->copyOverflowInfoFrom(*srcChunk); + + auto iter = srcChunk->occupiedIter(); + if (prefetchBeforeCopy()) { + for (auto piter = iter; piter.hasNext();) { + this->prefetchValue(srcChunk->citem(piter.next())); + } + } + + std::size_t dstI = 0; + for (; iter.hasNext(); ++dstI) { + auto srcI = iter.next(); + auto&& srcArg = + std::forward<T>(src).buildArgForItem(srcChunk->item(srcI)); + auto dst = dstChunk->itemAddr(dstI); + this->constructValueAtItem( + 0, dst, std::forward<decltype(srcArg)>(srcArg)); + dstChunk->setTag(dstI, srcChunk->tag(srcI)); + ++sizeAndPackedBegin_.size_; + } + + --srcChunk; + --dstChunk; + } while (size() != src.size()); + + // reset doesn't care about packedBegin, so we don't fix it until the end + if (kEnableItemIteration) { + std::size_t maxChunkIndex = src.lastOccupiedChunk() - src.chunks_; + sizeAndPackedBegin_.packedBegin() = + ItemIter{chunks_ + maxChunkIndex, + chunks_[maxChunkIndex].lastOccupied().index()} + .pack(); + } + } + + success = true; + } + + template <typename T> + void rehashBuildFrom(T&& src) { + FOLLY_SAFE_DCHECK(src.chunkMask_ > chunkMask_, ""); + + // 1 byte per chunk means < 1 bit per value temporary overhead + std::array<uint8_t, 256> stackBuf; + uint8_t* fullness; + auto cc = chunkMask_ + 1; + if (cc <= stackBuf.size()) { + fullness = stackBuf.data(); + } else { + ByteAlloc a{this->alloc()}; + fullness = &*std::allocator_traits<ByteAlloc>::allocate(a, cc); + } + SCOPE_EXIT { + if (cc > stackBuf.size()) { + ByteAlloc a{this->alloc()}; + std::allocator_traits<ByteAlloc>::deallocate( + a, + std::pointer_traits<typename std::allocator_traits< + ByteAlloc>::pointer>::pointer_to(*fullness), + cc); + } + }; + std::memset(fullness, '\0', cc); + + // We use std::forward<T> to allow portions of src to be moved out by + // either beforeBuild or afterBuild, but we are just relying on good + // behavior of our Policy superclass to ensure that any particular + // field of this is a donor at most once. + + // Exception safety requires beforeBuild to happen after all of the + // allocate() calls. + auto undoState = + this->beforeBuild(src.size(), bucket_count(), std::forward<T>(src)); + bool success = false; + SCOPE_EXIT { + this->afterBuild( + undoState, success, src.size(), bucket_count(), std::forward<T>(src)); + }; + + // The current table is at a valid state at all points for policies + // in which non-trivial values are owned by the main table (F14Node + // and F14Value), so reset() will clean things up properly if we + // fail partway through. For the case that the policy manages value + // lifecycle (F14Vector) then nothing after beforeBuild can throw and + // we don't have to worry about partial failure. + + std::size_t srcChunkIndex = src.lastOccupiedChunk() - src.chunks_; + while (true) { + auto srcChunk = &src.chunks_[srcChunkIndex]; + auto iter = srcChunk->occupiedIter(); + if (prefetchBeforeRehash()) { + for (auto piter = iter; piter.hasNext();) { + this->prefetchValue(srcChunk->item(piter.next())); + } + } + if (srcChunk->hostedOverflowCount() == 0) { + // all items are in their preferred chunk (no probing), so we + // don't need to compute any hash values + while (iter.hasNext()) { + auto i = iter.next(); + auto& srcItem = srcChunk->item(i); + auto&& srcArg = std::forward<T>(src).buildArgForItem(srcItem); + HashPair hp{srcChunkIndex, srcChunk->tag(i)}; + insertAtBlank( + allocateTag(fullness, hp), + hp, + std::forward<decltype(srcArg)>(srcArg)); + } + } else { + // any chunk's items might be in here + while (iter.hasNext()) { + auto i = iter.next(); + auto& srcItem = srcChunk->item(i); + auto&& srcArg = std::forward<T>(src).buildArgForItem(srcItem); + auto const& srcKey = src.keyForValue(srcArg); + auto hp = splitHash(this->computeKeyHash(srcKey)); + FOLLY_SAFE_CHECK(hp.second == srcChunk->tag(i), ""); + insertAtBlank( + allocateTag(fullness, hp), + hp, + std::forward<decltype(srcArg)>(srcArg)); + } + } + if (srcChunkIndex == 0) { + break; + } + --srcChunkIndex; + } + + success = true; + } + + template <typename T> + FOLLY_NOINLINE void buildFromF14Table(T&& src) { + FOLLY_SAFE_DCHECK(bucket_count() == 0, ""); + if (src.size() == 0) { + return; + } + + // Use the source's capacity, unless it is oversized. + auto upperLimit = computeChunkCountAndScale(src.size(), false, false); + auto ccas = std::make_pair( + std::size_t{src.chunkMask_} + 1, src.chunks_->capacityScale()); + FOLLY_SAFE_DCHECK( + ccas.first >= upperLimit.first, + "rounded chunk count can't be bigger than actual"); + if (ccas.first > upperLimit.first || ccas.second > upperLimit.second) { + ccas = upperLimit; + } + rehashImpl(0, 1, 0, ccas.first, ccas.second); + + try { + if (chunkMask_ == src.chunkMask_) { + directBuildFrom(std::forward<T>(src)); + } else { + rehashBuildFrom(std::forward<T>(src)); + } + } catch (...) { + reset(); + F14LinkCheck<getF14IntrinsicsMode()>::check(); + throw; + } + } + + void reserveImpl(std::size_t desiredCapacity) { + desiredCapacity = std::max<std::size_t>(desiredCapacity, size()); + if (desiredCapacity == 0) { + reset(); + return; + } + + auto origChunkCount = chunkMask_ + 1; + auto origCapacityScale = chunks_->capacityScale(); + auto origCapacity = computeCapacity(origChunkCount, origCapacityScale); + + // This came from an explicit reserve() or rehash() call, so there's + // a good chance the capacity is exactly right. To avoid O(n^2) + // behavior, we don't do rehashes that decrease the size by less + // than 1/8, and if we have a requested increase of less than 1/8 we + // instead go to the next power of two. + + if (desiredCapacity <= origCapacity && + desiredCapacity >= origCapacity - origCapacity / 8) { + return; + } + bool attemptExact = + !(desiredCapacity > origCapacity && + desiredCapacity < origCapacity + origCapacity / 8); + + std::size_t newChunkCount; + std::size_t newCapacityScale; + std::tie(newChunkCount, newCapacityScale) = computeChunkCountAndScale( + desiredCapacity, attemptExact, kContinuousCapacity && attemptExact); + auto newCapacity = computeCapacity(newChunkCount, newCapacityScale); + + if (origCapacity != newCapacity) { + rehashImpl( + size(), + origChunkCount, + origCapacityScale, + newChunkCount, + newCapacityScale); + } + } + + FOLLY_NOINLINE void reserveForInsertImpl( + std::size_t capacityMinusOne, + std::size_t origChunkCount, + std::size_t origCapacityScale, + std::size_t origCapacity) { + FOLLY_SAFE_DCHECK(capacityMinusOne >= size(), ""); + std::size_t capacity = capacityMinusOne + 1; + + // we want to grow by between 2^0.5 and 2^1.5 ending at a "good" + // size, so we grow by 2^0.5 and then round up + + // 1.01101_2 = 1.40625 + std::size_t minGrowth = origCapacity + (origCapacity >> 2) + + (origCapacity >> 3) + (origCapacity >> 5); + capacity = std::max<std::size_t>(capacity, minGrowth); + + std::size_t newChunkCount; + std::size_t newCapacityScale; + std::tie(newChunkCount, newCapacityScale) = + computeChunkCountAndScale(capacity, false, false); + + FOLLY_SAFE_DCHECK( + computeCapacity(newChunkCount, newCapacityScale) > origCapacity, ""); + + rehashImpl( + size(), + origChunkCount, + origCapacityScale, + newChunkCount, + newCapacityScale); + } + + void rehashImpl( + std::size_t origSize, + std::size_t origChunkCount, + std::size_t origCapacityScale, + std::size_t newChunkCount, + std::size_t newCapacityScale) { + auto origChunks = chunks_; + auto origCapacity = computeCapacity(origChunkCount, origCapacityScale); + auto origAllocSize = chunkAllocSize(origChunkCount, origCapacityScale); + auto newCapacity = computeCapacity(newChunkCount, newCapacityScale); + auto newAllocSize = chunkAllocSize(newChunkCount, newCapacityScale); + + BytePtr rawAllocation; + auto undoState = this->beforeRehash( + origSize, origCapacity, newCapacity, newAllocSize, rawAllocation); + chunks_ = initializeChunks(rawAllocation, newChunkCount, newCapacityScale); + + FOLLY_SAFE_DCHECK( + newChunkCount < std::numeric_limits<InternalSizeType>::max(), ""); + chunkMask_ = static_cast<InternalSizeType>(newChunkCount - 1); + + bool success = false; + SCOPE_EXIT { + // this SCOPE_EXIT reverts chunks_ and chunkMask_ if necessary + BytePtr finishedRawAllocation = nullptr; + std::size_t finishedAllocSize = 0; + if (LIKELY(success)) { + if (origCapacity > 0) { + finishedRawAllocation = std::pointer_traits<BytePtr>::pointer_to( + *static_cast<uint8_t*>(static_cast<void*>(&*origChunks))); + finishedAllocSize = origAllocSize; + } + } else { + finishedRawAllocation = rawAllocation; + finishedAllocSize = newAllocSize; + chunks_ = origChunks; + FOLLY_SAFE_DCHECK( + origChunkCount < std::numeric_limits<InternalSizeType>::max(), ""); + chunkMask_ = static_cast<InternalSizeType>(origChunkCount - 1); + F14LinkCheck<getF14IntrinsicsMode()>::check(); + } + + this->afterRehash( + std::move(undoState), + success, + origSize, + origCapacity, + newCapacity, + finishedRawAllocation, + finishedAllocSize); + }; + + if (origSize == 0) { + // nothing to do + } else if (origChunkCount == 1 && newChunkCount == 1) { + // no mask, no chunk scan, no hash computation, no probing + auto srcChunk = origChunks; + auto dstChunk = chunks_; + std::size_t srcI = 0; + std::size_t dstI = 0; + while (dstI < origSize) { + if (LIKELY(srcChunk->occupied(srcI))) { + dstChunk->setTag(dstI, srcChunk->tag(srcI)); + this->moveItemDuringRehash( + dstChunk->itemAddr(dstI), srcChunk->item(srcI)); + ++dstI; + } + ++srcI; + } + if (kEnableItemIteration) { + sizeAndPackedBegin_.packedBegin() = ItemIter{dstChunk, dstI - 1}.pack(); + } + } else { + // 1 byte per chunk means < 1 bit per value temporary overhead + std::array<uint8_t, 256> stackBuf; + uint8_t* fullness; + if (newChunkCount <= stackBuf.size()) { + fullness = stackBuf.data(); + } else { + ByteAlloc a{this->alloc()}; + // may throw + fullness = + &*std::allocator_traits<ByteAlloc>::allocate(a, newChunkCount); + } + std::memset(fullness, '\0', newChunkCount); + SCOPE_EXIT { + if (newChunkCount > stackBuf.size()) { + ByteAlloc a{this->alloc()}; + std::allocator_traits<ByteAlloc>::deallocate( + a, + std::pointer_traits<typename std::allocator_traits< + ByteAlloc>::pointer>::pointer_to(*fullness), + newChunkCount); + } + }; + + auto srcChunk = origChunks + origChunkCount - 1; + std::size_t remaining = origSize; + while (remaining > 0) { + auto iter = srcChunk->occupiedIter(); + if (prefetchBeforeRehash()) { + for (auto piter = iter; piter.hasNext();) { + this->prefetchValue(srcChunk->item(piter.next())); + } + } + while (iter.hasNext()) { + --remaining; + auto srcI = iter.next(); + Item& srcItem = srcChunk->item(srcI); + auto hp = splitHash( + this->computeItemHash(const_cast<Item const&>(srcItem))); + FOLLY_SAFE_CHECK(hp.second == srcChunk->tag(srcI), ""); + + auto dstIter = allocateTag(fullness, hp); + this->moveItemDuringRehash(dstIter.itemAddr(), srcItem); + } + --srcChunk; + } + + if (kEnableItemIteration) { + // this code replaces size invocations of adjustSizeAndBeginAfterInsert + std::size_t i = chunkMask_; + while (fullness[i] == 0) { + --i; + } + sizeAndPackedBegin_.packedBegin() = + ItemIter{chunks_ + i, std::size_t{fullness[i]} - 1}.pack(); + } + } + + success = true; + } + + // Randomization to help expose bugs when running tests in debug or + // sanitizer builds + + FOLLY_ALWAYS_INLINE void debugModeOnReserve(std::size_t capacity) { + if (kIsLibrarySanitizeAddress || kIsDebug) { + if (capacity > size()) { + tlsPendingSafeInserts(static_cast<std::ptrdiff_t>(capacity - size())); + } + } + } + + void debugModeSpuriousRehash() { + auto cc = chunkMask_ + 1; + auto ss = chunks_->capacityScale(); + rehashImpl(size(), cc, ss, cc, ss); + } + + FOLLY_ALWAYS_INLINE void debugModeBeforeInsert() { + // When running under ASAN, we add a spurious rehash with 1/size() + // probability before every insert. This means that finding reference + // stability problems for F14Value and F14Vector is much more likely. + // The most common pattern that causes this is + // + // auto& ref = map[k1]; map[k2] = foo(ref); + // + // One way to fix this is to call map.reserve(N) before such a + // sequence, where N is the number of keys that might be inserted + // within the section that retains references plus the existing size. + if (kIsLibrarySanitizeAddress && !tlsPendingSafeInserts() && size() > 0 && + tlsMinstdRand(size()) == 0) { + debugModeSpuriousRehash(); + } + } + + FOLLY_ALWAYS_INLINE void debugModeAfterInsert() { + if (kIsLibrarySanitizeAddress || kIsDebug) { + tlsPendingSafeInserts(-1); + } + } + + FOLLY_ALWAYS_INLINE void debugModePerturbSlotInsertOrder( + ChunkPtr chunk, + std::size_t& itemIndex) { + FOLLY_SAFE_DCHECK(!chunk->occupied(itemIndex), ""); + constexpr bool perturbSlot = FOLLY_F14_PERTURB_INSERTION_ORDER; + if (perturbSlot && !tlsPendingSafeInserts()) { + std::size_t e = chunkMask_ == 0 ? bucket_count() : Chunk::kCapacity; + std::size_t i = itemIndex + tlsMinstdRand(e - itemIndex); + if (!chunk->occupied(i)) { + itemIndex = i; + } + } + } + + public: + // user has no control over max_load_factor + + void rehash(std::size_t capacity) { + reserve(capacity); + } + + void reserve(std::size_t capacity) { + // We want to support the pattern + // map.reserve(map.size() + 2); auto& r1 = map[k1]; auto& r2 = map[k2]; + debugModeOnReserve(capacity); + reserveImpl(capacity); + } + + void reserveForInsert(size_t incoming = 1) { + FOLLY_SAFE_DCHECK(incoming > 0, ""); + + auto needed = size() + incoming; + auto chunkCount = chunkMask_ + 1; + auto scale = chunks_->capacityScale(); + auto existing = computeCapacity(chunkCount, scale); + if (needed - 1 >= existing) { + reserveForInsertImpl(needed - 1, chunkCount, scale, existing); + } + } + + // Returns pos,true if construct, pos,false if found. key is only used + // during the search; all constructor args for an inserted value come + // from args... key won't be accessed after args are touched. + template <typename K, typename... Args> + std::pair<ItemIter, bool> tryEmplaceValue(K const& key, Args&&... args) { + const auto hp = splitHash(this->computeKeyHash(key)); + + if (size() > 0) { + auto existing = findImpl(hp, key); + if (!existing.atEnd()) { + return std::make_pair(existing, false); + } + } + + debugModeBeforeInsert(); + + reserveForInsert(); + + std::size_t index = hp.first; + ChunkPtr chunk = chunks_ + (index & chunkMask_); + auto firstEmpty = chunk->firstEmpty(); + + if (!firstEmpty.hasIndex()) { + std::size_t delta = probeDelta(hp); + do { + chunk->incrOutboundOverflowCount(); + index += delta; + chunk = chunks_ + (index & chunkMask_); + firstEmpty = chunk->firstEmpty(); + } while (!firstEmpty.hasIndex()); + chunk->adjustHostedOverflowCount(Chunk::kIncrHostedOverflowCount); + } + std::size_t itemIndex = firstEmpty.index(); + + debugModePerturbSlotInsertOrder(chunk, itemIndex); + + chunk->setTag(itemIndex, hp.second); + ItemIter iter{chunk, itemIndex}; + + // insertAtBlank will clear the tag if the constructor throws + insertAtBlank(iter, hp, std::forward<Args>(args)...); + + debugModeAfterInsert(); + + return std::make_pair(iter, true); + } + + private: + template <bool Reset> + void clearImpl() noexcept { + if (chunks_ == Chunk::emptyInstance()) { + FOLLY_SAFE_DCHECK(empty() && bucket_count() == 0, ""); + return; + } + + // turn clear into reset if the table is >= 16 chunks so that + // we don't get too low a load factor + bool willReset = Reset || chunkMask_ + 1 >= 16; + + auto origSize = size(); + auto origCapacity = bucket_count(); + if (willReset) { + this->beforeReset(origSize, origCapacity); + } else { + this->beforeClear(origSize, origCapacity); + } + + if (!empty()) { + if (destroyItemOnClear()) { + for (std::size_t ci = 0; ci <= chunkMask_; ++ci) { + ChunkPtr chunk = chunks_ + ci; + auto iter = chunk->occupiedIter(); + if (prefetchBeforeDestroy()) { + for (auto piter = iter; piter.hasNext();) { + this->prefetchValue(chunk->item(piter.next())); + } + } + while (iter.hasNext()) { + this->destroyItem(chunk->item(iter.next())); + } + } + } + if (!willReset) { + // It's okay to do this in a separate loop because we only do it + // when the chunk count is small. That avoids a branch when we + // are promoting a clear to a reset for a large table. + auto scale = chunks_[0].capacityScale(); + for (std::size_t ci = 0; ci <= chunkMask_; ++ci) { + chunks_[ci].clear(); + } + chunks_[0].markEof(scale); + } + if (kEnableItemIteration) { + sizeAndPackedBegin_.packedBegin() = ItemIter{}.pack(); + } + sizeAndPackedBegin_.size_ = 0; + } + + if (willReset) { + BytePtr rawAllocation = std::pointer_traits<BytePtr>::pointer_to( + *static_cast<uint8_t*>(static_cast<void*>(&*chunks_))); + std::size_t rawSize = + chunkAllocSize(chunkMask_ + 1, chunks_->capacityScale()); + + chunks_ = Chunk::emptyInstance(); + chunkMask_ = 0; + + this->afterReset(origSize, origCapacity, rawAllocation, rawSize); + } else { + this->afterClear(origSize, origCapacity); + } + } + + void eraseImpl(ItemIter pos, HashPair hp) { + this->destroyItem(pos.item()); + adjustSizeAndBeginBeforeErase(pos); + eraseBlank(pos, hp); + } + + public: + // The item needs to still be hashable during this call. If you want + // to intercept the value before it is destroyed (to extract it, for + // example), do so in the beforeDestroy callback. + template <typename BeforeDestroy> + void eraseIterInto(ItemIter pos, BeforeDestroy&& beforeDestroy) { + HashPair hp{}; + if (pos.chunk()->hostedOverflowCount() != 0) { + hp = splitHash(this->computeItemHash(pos.citem())); + } + beforeDestroy(this->valueAtItemForExtract(pos.item())); + eraseImpl(pos, hp); + } + + template <typename K, typename BeforeDestroy> + std::size_t eraseKeyInto(K const& key, BeforeDestroy&& beforeDestroy) { + if (UNLIKELY(size() == 0)) { + return 0; + } + auto hp = splitHash(this->computeKeyHash(key)); + auto iter = findImpl(hp, key); + if (!iter.atEnd()) { + beforeDestroy(this->valueAtItemForExtract(iter.item())); + eraseImpl(iter, hp); + return 1; + } else { + return 0; + } + } + + void clear() noexcept { + if (kIsLibrarySanitizeAddress) { + // force recycling of heap memory + auto bc = bucket_count(); + reset(); + try { + reserveImpl(bc); + } catch (std::bad_alloc const&) { + // ASAN mode only, keep going + } + } else { + clearImpl<false>(); + } + } + + // Like clear(), but always frees all dynamic storage allocated + // by the table. + void reset() noexcept { + clearImpl<true>(); + } + + // Get memory footprint, not including sizeof(*this). + std::size_t getAllocatedMemorySize() const { + std::size_t sum = 0; + visitAllocationClasses( + [&sum](std::size_t bytes, std::size_t n) { sum += bytes * n; }); + return sum; + } + + // Enumerates classes of allocated memory blocks currently owned + // by this table, calling visitor(allocationSize, allocationCount). + // This can be used to get a more accurate indication of memory footprint + // than getAllocatedMemorySize() if you have some way of computing the + // internal fragmentation of the allocator, such as JEMalloc's nallocx. + // The visitor might be called twice with the same allocationSize. The + // visitor's computation should produce the same result for visitor(8, + // 2) as for two calls to visitor(8, 1), for example. The visitor may + // be called with a zero allocationCount. + template <typename V> + void visitAllocationClasses(V&& visitor) const { + auto scale = chunks_->capacityScale(); + this->visitPolicyAllocationClasses( + scale == 0 ? 0 : chunkAllocSize(chunkMask_ + 1, scale), + size(), + bucket_count(), + visitor); + } + + // visitor should take an Item const& + template <typename V> + void visitItems(V&& visitor) const { + if (empty()) { + return; + } + std::size_t maxChunkIndex = lastOccupiedChunk() - chunks_; + auto chunk = &chunks_[0]; + for (std::size_t i = 0; i <= maxChunkIndex; ++i, ++chunk) { + auto iter = chunk->occupiedIter(); + if (prefetchBeforeCopy()) { + for (auto piter = iter; piter.hasNext();) { + this->prefetchValue(chunk->citem(piter.next())); + } + } + while (iter.hasNext()) { + visitor(chunk->citem(iter.next())); + } + } + } + + // visitor should take two Item const* + template <typename V> + void visitContiguousItemRanges(V&& visitor) const { + if (empty()) { + return; + } + std::size_t maxChunkIndex = lastOccupiedChunk() - chunks_; + auto chunk = &chunks_[0]; + for (std::size_t i = 0; i <= maxChunkIndex; ++i, ++chunk) { + for (auto iter = chunk->occupiedRangeIter(); iter.hasNext();) { + auto be = iter.next(); + FOLLY_SAFE_DCHECK( + chunk->occupied(be.first) && chunk->occupied(be.second - 1), ""); + Item const* b = chunk->itemAddr(be.first); + visitor(b, b + (be.second - be.first)); + } + } + } + + private: + static std::size_t& histoAt( + std::vector<std::size_t>& histo, + std::size_t index) { + if (histo.size() <= index) { + histo.resize(index + 1); + } + return histo.at(index); + } + + public: + // Expensive + F14TableStats computeStats() const { + F14TableStats stats; + + if (kIsDebug && kEnableItemIteration) { + // validate iteration + std::size_t n = 0; + ItemIter prev; + for (auto iter = begin(); iter != end(); iter.advance()) { + FOLLY_SAFE_DCHECK(n == 0 || iter.pack() < prev.pack(), ""); + ++n; + prev = iter; + } + FOLLY_SAFE_DCHECK(n == size(), ""); + } + + FOLLY_SAFE_DCHECK( + (chunks_ == Chunk::emptyInstance()) == (bucket_count() == 0), ""); + + std::size_t n1 = 0; + std::size_t n2 = 0; + auto cc = bucket_count() == 0 ? 0 : chunkMask_ + 1; + for (std::size_t ci = 0; ci < cc; ++ci) { + ChunkPtr chunk = chunks_ + ci; + FOLLY_SAFE_DCHECK(chunk->eof() == (ci == 0), ""); + + auto iter = chunk->occupiedIter(); + + std::size_t chunkOccupied = 0; + for (auto piter = iter; piter.hasNext(); piter.next()) { + ++chunkOccupied; + } + n1 += chunkOccupied; + + histoAt(stats.chunkOccupancyHisto, chunkOccupied)++; + histoAt( + stats.chunkOutboundOverflowHisto, chunk->outboundOverflowCount())++; + histoAt(stats.chunkHostedOverflowHisto, chunk->hostedOverflowCount())++; + + while (iter.hasNext()) { + auto ii = iter.next(); + ++n2; + + { + auto& item = chunk->citem(ii); + auto hp = splitHash(this->computeItemHash(item)); + FOLLY_SAFE_DCHECK(chunk->tag(ii) == hp.second, ""); + + std::size_t dist = 1; + std::size_t index = hp.first; + std::size_t delta = probeDelta(hp); + while ((index & chunkMask_) != ci) { + index += delta; + ++dist; + } + + histoAt(stats.keyProbeLengthHisto, dist)++; + } + + // misses could have any tag, so we do the dumb but accurate + // thing and just try them all + for (std::size_t ti = 0; ti < 256; ++ti) { + uint8_t tag = static_cast<uint8_t>(ti == 0 ? 1 : 0); + HashPair hp{ci, tag}; + + std::size_t dist = 1; + std::size_t index = hp.first; + std::size_t delta = probeDelta(hp); + for (std::size_t tries = 0; tries <= chunkMask_ && + chunks_[index & chunkMask_].outboundOverflowCount() != 0; + ++tries) { + index += delta; + ++dist; + } + + histoAt(stats.missProbeLengthHisto, dist)++; + } + } + } + + FOLLY_SAFE_DCHECK(n1 == size(), ""); + FOLLY_SAFE_DCHECK(n2 == size(), ""); + + stats.policy = pretty_name<Policy>(); + stats.size = size(); + stats.valueSize = sizeof(value_type); + stats.bucketCount = bucket_count(); + stats.chunkCount = cc; + + stats.totalBytes = sizeof(*this) + getAllocatedMemorySize(); + stats.overheadBytes = stats.totalBytes - size() * sizeof(value_type); + + return stats; + } +}; +} // namespace detail +} // namespace f14 + +#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE + +namespace f14 { +namespace test { +inline void disableInsertOrderRandomization() { + if (kIsLibrarySanitizeAddress || kIsDebug) { + detail::tlsPendingSafeInserts(static_cast<std::ptrdiff_t>( + (std::numeric_limits<std::size_t>::max)() / 2)); + } +} +} // namespace test +} // namespace f14 +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/container/detail/Util.h b/ios/Pods/Flipper-Folly/folly/container/detail/Util.h new file mode 100644 index 000000000..10186ec18 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/container/detail/Util.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <folly/Traits.h> +#include <folly/functional/ApplyTuple.h> + +// Utility functions for container implementors + +namespace folly { +namespace detail { + +template <typename KeyType, typename Alloc> +struct TemporaryEmplaceKey { + TemporaryEmplaceKey(TemporaryEmplaceKey const&) = delete; + TemporaryEmplaceKey(TemporaryEmplaceKey&&) = delete; + + template <typename... Args> + TemporaryEmplaceKey(Alloc& a, std::tuple<Args...>&& args) : alloc_(a) { + auto p = &value(); + apply( + [&, p](auto&&... inner) { + std::allocator_traits<Alloc>::construct( + alloc_, p, std::forward<decltype(inner)>(inner)...); + }, + std::move(args)); + } + + ~TemporaryEmplaceKey() { + std::allocator_traits<Alloc>::destroy(alloc_, &value()); + } + + KeyType& value() { + return *static_cast<KeyType*>(static_cast<void*>(&raw_)); + } + + Alloc& alloc_; + std::aligned_storage_t<sizeof(KeyType), alignof(KeyType)> raw_; +}; + +// A map's emplace(args...) function takes arguments that can be used to +// construct a pair<key_type const, mapped_type>, but that construction +// only needs to take place if the key is not present in the container. +// callWithExtractedKey helps to handle this efficiently by looking for a +// reference to the key within the args list. If the search is successful +// then the search can be performed without constructing any temporaries. +// If the search is not successful then callWithExtractedKey constructs +// a temporary key_type and a new argument list suitable for constructing +// the entire value_type if necessary. +// +// callWithExtractedKey(a, f, args...) will call f(k, args'...), where +// k is the key and args'... is an argument list that can be used to +// construct a pair of key and mapped value. Note that this means f gets +// the key twice. +// +// In some cases a temporary key must be constructed. This is accomplished +// with std::allocator_traits<>::construct, and the temporary will be +// destroyed with std::allocator_traits<>::destroy. Using the allocator's +// construct method reduces unnecessary copies for pmr allocators. +// +// callWithExtractedKey supports heterogeneous lookup with the UsableAsKey +// template parameter. If a single key argument of type K is found in +// args... then it will be passed directly to f if it is either KeyType or +// if UsableAsKey<remove_cvref_t<K>>::value is true. If you don't care +// about heterogeneous lookup you can just pass a single-arg template +// that extends std::false_type. + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename Arg1, + typename... Args2, + std::enable_if_t< + std::is_same<remove_cvref_t<Arg1>, KeyType>::value || + UsableAsKey<remove_cvref_t<Arg1>>::value, + int> = 0> +auto callWithExtractedKey( + Alloc&, + Func&& f, + std::piecewise_construct_t, + std::tuple<Arg1>&& first_args, + std::tuple<Args2...>&& second_args) { + // we found a usable key in the args :) + auto const& key = std::get<0>(first_args); + return f( + key, + std::piecewise_construct, + std::tuple<Arg1&&>(std::move(first_args)), + std::tuple<Args2&&...>(std::move(second_args))); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename... Args1, + typename... Args2> +auto callWithExtractedKey( + Alloc& a, + Func&& f, + std::piecewise_construct_t, + std::tuple<Args1...>&& first_args, + std::tuple<Args2...>&& second_args) { + // we will need to materialize a temporary key :( + TemporaryEmplaceKey<KeyType, Alloc> key( + a, std::tuple<Args1&&...>(std::move(first_args))); + return f( + const_cast<KeyType const&>(key.value()), + std::piecewise_construct, + std::forward_as_tuple(std::move(key.value())), + std::tuple<Args2&&...>(std::move(second_args))); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func> +auto callWithExtractedKey(Alloc& a, Func&& f) { + return callWithExtractedKey<KeyType, UsableAsKey>( + a, + std::forward<Func>(f), + std::piecewise_construct, + std::tuple<>{}, + std::tuple<>{}); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename U1, + typename U2> +auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) { + return callWithExtractedKey<KeyType, UsableAsKey>( + a, + std::forward<Func>(f), + std::piecewise_construct, + std::forward_as_tuple(std::forward<U1>(x)), + std::forward_as_tuple(std::forward<U2>(y))); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename U1, + typename U2> +auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) { + return callWithExtractedKey<KeyType, UsableAsKey>( + a, + std::forward<Func>(f), + std::piecewise_construct, + std::forward_as_tuple(p.first), + std::forward_as_tuple(p.second)); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename U1, + typename U2> +auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) { + return callWithExtractedKey<KeyType, UsableAsKey>( + a, + std::forward<Func>(f), + std::piecewise_construct, + std::forward_as_tuple(std::move(p.first)), + std::forward_as_tuple(std::move(p.second))); +} + +// callWithConstructedKey is the set container analogue of +// callWithExtractedKey + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename Arg, + std::enable_if_t< + std::is_same<remove_cvref_t<Arg>, KeyType>::value || + UsableAsKey<remove_cvref_t<Arg>>::value, + int> = 0> +auto callWithConstructedKey(Alloc&, Func&& f, Arg&& arg) { + // we found a usable key in the args :) + auto const& key = arg; + return f(key, std::forward<Arg>(arg)); +} + +template < + typename KeyType, + template <typename> class UsableAsKey, + typename Alloc, + typename Func, + typename... Args> +auto callWithConstructedKey(Alloc& a, Func&& f, Args&&... args) { + // we will need to materialize a temporary key :( + TemporaryEmplaceKey<KeyType, Alloc> key( + a, std::forward_as_tuple(std::forward<Args>(args)...)); + return f(const_cast<KeyType const&>(key.value()), std::move(key.value())); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.cpp b/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.cpp new file mode 100644 index 000000000..aa66abd7e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/AsyncTrace.h> +#include <folly/Portability.h> + +namespace folly { +namespace async_tracing { +FOLLY_ATTR_WEAK void logSetGlobalCPUExecutor(Executor*) noexcept {} +FOLLY_ATTR_WEAK void logSetGlobalCPUExecutorToImmutable() noexcept {} +FOLLY_ATTR_WEAK void logGetGlobalCPUExecutor(Executor*) noexcept {} +FOLLY_ATTR_WEAK void logGetImmutableCPUExecutor(Executor*) noexcept {} +FOLLY_ATTR_WEAK void logSetGlobalIOExecutor(IOExecutor*) noexcept {} +FOLLY_ATTR_WEAK void logGetGlobalIOExecutor(IOExecutor*) noexcept {} +FOLLY_ATTR_WEAK void logGetImmutableIOExecutor(IOExecutor*) noexcept {} +FOLLY_ATTR_WEAK void logSemiFutureVia(Executor*, Executor*) noexcept {} +FOLLY_ATTR_WEAK void logFutureVia(Executor*, Executor*) noexcept {} +FOLLY_ATTR_WEAK void logBlockingOperation(std::chrono::milliseconds) noexcept {} +} // namespace async_tracing +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.h b/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.h new file mode 100644 index 000000000..c9bc2dbaf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AsyncTrace.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> + +#include <folly/Optional.h> + +namespace folly { +class Executor; +class IOExecutor; +namespace async_tracing { +void logSetGlobalCPUExecutor(Executor*) noexcept; +void logSetGlobalCPUExecutorToImmutable() noexcept; +void logGetGlobalCPUExecutor(Executor*) noexcept; +void logGetImmutableCPUExecutor(Executor*) noexcept; +void logSetGlobalIOExecutor(IOExecutor*) noexcept; +void logGetGlobalIOExecutor(IOExecutor*) noexcept; +void logGetImmutableIOExecutor(IOExecutor*) noexcept; +void logSemiFutureVia(Executor*, Executor*) noexcept; +void logFutureVia(Executor*, Executor*) noexcept; +void logBlockingOperation(std::chrono::milliseconds) noexcept; +} // namespace async_tracing +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AtFork.cpp b/ios/Pods/Flipper-Folly/folly/detail/AtFork.cpp new file mode 100644 index 000000000..8afb90dca --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AtFork.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/AtFork.h> + +#include <list> +#include <mutex> + +#include <folly/ScopeGuard.h> +#include <folly/lang/Exception.h> +#include <folly/portability/PThread.h> +#include <folly/synchronization/SanitizeThread.h> + +namespace folly { + +namespace detail { + +namespace { + +struct AtForkTask { + void const* handle; + folly::Function<bool()> prepare; + folly::Function<void()> parent; + folly::Function<void()> child; +}; + +class AtForkList { + public: + static AtForkList& instance() { + static auto instance = new AtForkList(); + return *instance; + } + + static void prepare() noexcept { + instance().tasksLock.lock(); + while (true) { + auto& tasks = instance().tasks; + auto task = tasks.rbegin(); + for (; task != tasks.rend(); ++task) { + if (!task->prepare()) { + break; + } + } + if (task == tasks.rend()) { + return; + } + for (auto untask = tasks.rbegin(); untask != task; ++untask) { + untask->parent(); + } + } + } + + static void parent() noexcept { + auto& tasks = instance().tasks; + for (auto& task : tasks) { + task.parent(); + } + instance().tasksLock.unlock(); + } + + static void child() noexcept { + // if we fork a multithreaded process + // some of the TSAN mutexes might be locked + // so we just enable ignores for everything + // while handling the child callbacks + // This might still be an issue if we do not exec right away + annotate_ignore_thread_sanitizer_guard g(__FILE__, __LINE__); + + auto& tasks = instance().tasks; + for (auto& task : tasks) { + task.child(); + } + instance().tasksLock.unlock(); + } + + std::mutex tasksLock; + std::list<AtForkTask> tasks; + + private: + AtForkList() { +#if FOLLY_HAVE_PTHREAD_ATFORK + int ret = pthread_atfork( + &AtForkList::prepare, &AtForkList::parent, &AtForkList::child); + if (ret != 0) { + throw_exception<std::system_error>( + ret, std::generic_category(), "pthread_atfork failed"); + } +#elif !__ANDROID__ && !defined(_MSC_VER) +// pthread_atfork is not part of the Android NDK at least as of n9d. If +// something is trying to call native fork() directly at all with Android's +// process management model, this is probably the least of the problems. +// +// But otherwise, this is a problem. +#warning pthread_atfork unavailable +#endif + } +}; +} // namespace + +void AtFork::init() { + AtForkList::instance(); +} + +void AtFork::registerHandler( + void const* handle, + folly::Function<bool()> prepare, + folly::Function<void()> parent, + folly::Function<void()> child) { + std::lock_guard<std::mutex> lg(AtForkList::instance().tasksLock); + AtForkList::instance().tasks.push_back( + {handle, std::move(prepare), std::move(parent), std::move(child)}); +} + +void AtFork::unregisterHandler(void const* handle) { + if (!handle) { + return; + } + auto& list = AtForkList::instance(); + std::lock_guard<std::mutex> lg(list.tasksLock); + for (auto it = list.tasks.begin(); it != list.tasks.end(); ++it) { + if (it->handle == handle) { + list.tasks.erase(it); + return; + } + } +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AtFork.h b/ios/Pods/Flipper-Folly/folly/detail/AtFork.h new file mode 100644 index 000000000..b47efe6e6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AtFork.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Function.h> + +namespace folly { + +namespace detail { + +struct AtFork { + static void init(); + static void registerHandler( + void const* handle, + folly::Function<bool()> prepare, + folly::Function<void()> parent, + folly::Function<void()> child); + static void unregisterHandler(void const* handle); +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AtomicHashUtils.h b/ios/Pods/Flipper-Folly/folly/detail/AtomicHashUtils.h new file mode 100644 index 000000000..9ffce4c09 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AtomicHashUtils.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <thread> + +#include <folly/portability/Asm.h> + +// Some utilities used by AtomicHashArray and AtomicHashMap +// + +namespace folly { +namespace detail { + +template <typename Cond> +void atomic_hash_spin_wait(Cond condition) { + constexpr size_t kPauseLimit = 10000; + for (size_t i = 0; condition(); ++i) { + if (i < kPauseLimit) { + folly::asm_volatile_pause(); + } else { + std::this_thread::yield(); + } + } +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h b/ios/Pods/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h new file mode 100644 index 000000000..0958871e1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cassert> +#include <cstdint> +#include <system_error> + +#include <folly/portability/SysMman.h> +#include <folly/portability/Unistd.h> + +namespace folly { +namespace detail { + +class MMapAlloc { + private: + size_t computeSize(size_t size) { + long pagesize = sysconf(_SC_PAGESIZE); + size_t mmapLength = ((size - 1) & ~(pagesize - 1)) + pagesize; + assert(size <= mmapLength && mmapLength < size + pagesize); + assert((mmapLength % pagesize) == 0); + return mmapLength; + } + + public: + void* allocate(size_t size) { + auto len = computeSize(size); + + int extraflags = 0; +#if defined(MAP_POPULATE) + extraflags |= MAP_POPULATE; +#endif + // MAP_HUGETLB is a perf win, but requires cooperation from the + // deployment environment (and a change to computeSize()). + void* mem = static_cast<void*>(mmap( + nullptr, + len, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | extraflags, + -1, + 0)); + if (mem == reinterpret_cast<void*>(-1)) { + throw std::system_error(errno, std::system_category()); + } +#if !defined(MAP_POPULATE) && defined(MADV_WILLNEED) + madvise(mem, size, MADV_WILLNEED); +#endif + + return mem; + } + + void deallocate(void* p, size_t size) { + auto len = computeSize(size); + munmap(p, len); + } +}; + +template <typename Allocator> +struct GivesZeroFilledMemory : public std::false_type {}; + +template <> +struct GivesZeroFilledMemory<MMapAlloc> : public std::true_type {}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Demangle.cpp b/ios/Pods/Flipper-Folly/folly/detail/Demangle.cpp new file mode 100644 index 000000000..1f33be742 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Demangle.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/Demangle.h> + +// Do not include <libiberty.h> (binutils) and <string.h> (glibc) in the same +// translation unit since they contain conflicting declarations for the symbol +// `basename`. +// +// So we extract the inclusion of `<demangle.h>` which includes `<libiberty.h>` +// to here, isolating it. +#if FOLLY_DETAIL_HAVE_DEMANGLE_H +// Work around an issue with conflicting `basename` definitions in glibc and +// binutils headers. +#define HAVE_DECL_BASENAME 1 +#include <demangle.h> // @manual +#undef HAVE_DECL_BASENAME +#endif + +namespace folly { +namespace detail { + +int cplus_demangle_v3_callback_wrapper( + char const* const mangled, + void (*const cbref)(char const*, std::size_t, void*), + void* const opaque) { +#if FOLLY_DETAIL_HAVE_DEMANGLE_H + auto const options = DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES; + return cplus_demangle_v3_callback(mangled, options, cbref, opaque); +#else + (void)mangled; + (void)cbref; + (void)opaque; + return 0; +#endif +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Demangle.h b/ios/Pods/Flipper-Folly/folly/detail/Demangle.h new file mode 100644 index 000000000..b11ffc7d7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Demangle.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> + +#if __has_include(<demangle.h>) +#define FOLLY_DETAIL_HAVE_DEMANGLE_H 1 +#else +#define FOLLY_DETAIL_HAVE_DEMANGLE_H 0 +#endif + +namespace folly { +namespace detail { + +extern int cplus_demangle_v3_callback_wrapper( + char const* mangled, + void (*cbref)(char const*, std::size_t, void*), + void* opaque); + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h b/ios/Pods/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h new file mode 100644 index 000000000..11a80a11e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> +#include <utility> + +#include <folly/functional/Invoke.h> + +namespace folly { +namespace dptr_detail { + +/** + * Given a target type and a list of types, return the 1-based index of the + * type in the list of types. Fail to compile if the target type doesn't + * appear in the list. + * + * GetIndex<int, void, char, int>::value == 3 + * GetIndex<int, void, char>::value -> fails to compile + */ +template <typename... Types> +struct GetTypeIndex; + +// When recursing, we never reach the 0- or 1- template argument base case +// unless the target type is not in the list. If the target type is in the +// list, we stop recursing when it is at the head of the remaining type +// list via the GetTypeIndex<T, T, Types...> partial specialization. +template <typename T, typename... Types> +struct GetTypeIndex<T, T, Types...> { + static const size_t value = 1; +}; + +template <typename T, typename U, typename... Types> +struct GetTypeIndex<T, U, Types...> { + static const size_t value = 1 + GetTypeIndex<T, Types...>::value; +}; + +// Generalize std::is_same for variable number of type arguments +template <typename... Types> +struct IsSameType; + +template <> +struct IsSameType<> { + static const bool value = true; +}; + +template <typename T> +struct IsSameType<T> { + static const bool value = true; +}; + +template <typename T, typename U, typename... Types> +struct IsSameType<T, U, Types...> { + static const bool value = + std::is_same<T, U>::value && IsSameType<U, Types...>::value; +}; + +// Define type as the type of all T in (non-empty) Types..., asserting that +// all types in Types... are the same. +template <typename... Types> +struct SameType; + +template <typename T, typename... Types> +struct SameType<T, Types...> { + typedef T type; + static_assert( + IsSameType<T, Types...>::value, + "Not all types in pack are the same"); +}; + +// Determine the result type of applying a visitor of type V on a pointer +// to type T. +template <typename V, typename T> +struct VisitorResult1 { + typedef invoke_result_t<V, T*> type; +}; + +// Determine the result type of applying a visitor of type V on a const pointer +// to type T. +template <typename V, typename T> +struct ConstVisitorResult1 { + typedef invoke_result_t<V, const T*> type; +}; + +// Determine the result type of applying a visitor of type V on pointers of +// all types in Types..., asserting that the type is the same for all types +// in Types... +template <typename V, typename... Types> +struct VisitorResult { + typedef + typename SameType<typename VisitorResult1<V, Types>::type...>::type type; +}; + +// Determine the result type of applying a visitor of type V on const pointers +// of all types in Types..., asserting that the type is the same for all types +// in Types... +template <typename V, typename... Types> +struct ConstVisitorResult { + typedef + typename SameType<typename ConstVisitorResult1<V, Types>::type...>::type + type; +}; + +template <size_t index, typename V, typename R, typename... Types> +struct ApplyVisitor1; + +template <typename V, typename R, typename T, typename... Types> +struct ApplyVisitor1<1, V, R, T, Types...> { + R operator()(size_t, V&& visitor, void* ptr) const { + return visitor(static_cast<T*>(ptr)); + } +}; + +template <size_t index, typename V, typename R, typename T, typename... Types> +struct ApplyVisitor1<index, V, R, T, Types...> { + R operator()(size_t runtimeIndex, V&& visitor, void* ptr) const { + return runtimeIndex == 1 + ? visitor(static_cast<T*>(ptr)) + : ApplyVisitor1<index - 1, V, R, Types...>()( + runtimeIndex - 1, std::forward<V>(visitor), ptr); + } +}; + +template <size_t index, typename V, typename R, typename... Types> +struct ApplyConstVisitor1; + +template <typename V, typename R, typename T, typename... Types> +struct ApplyConstVisitor1<1, V, R, T, Types...> { + R operator()(size_t, V&& visitor, void* ptr) const { + return visitor(static_cast<const T*>(ptr)); + } +}; + +template <size_t index, typename V, typename R, typename T, typename... Types> +struct ApplyConstVisitor1<index, V, R, T, Types...> { + R operator()(size_t runtimeIndex, V&& visitor, void* ptr) const { + return runtimeIndex == 1 + ? visitor(static_cast<const T*>(ptr)) + : ApplyConstVisitor1<index - 1, V, R, Types...>()( + runtimeIndex - 1, std::forward<V>(visitor), ptr); + } +}; + +template <typename V, typename... Types> +using ApplyVisitor = ApplyVisitor1< + sizeof...(Types), + V, + typename VisitorResult<V, Types...>::type, + Types...>; + +template <typename V, typename... Types> +using ApplyConstVisitor = ApplyConstVisitor1< + sizeof...(Types), + V, + typename ConstVisitorResult<V, Types...>::type, + Types...>; + +} // namespace dptr_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/FileUtilDetail.h b/ios/Pods/Flipper-Folly/folly/detail/FileUtilDetail.h new file mode 100644 index 000000000..24cc69fb8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/FileUtilDetail.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <cerrno> + +#include <folly/portability/SysUio.h> +#include <folly/portability/Unistd.h> + +/** + * Helper functions and templates for FileUtil.cpp. Declared here so + * they can be unittested. + */ +namespace folly { +namespace fileutil_detail { + +// Wrap call to f(args) in loop to retry on EINTR +template <class F, class... Args> +ssize_t wrapNoInt(F f, Args... args) { + ssize_t r; + do { + r = f(args...); + } while (r == -1 && errno == EINTR); + return r; +} + +inline void incr(ssize_t /* n */) {} +inline void incr(ssize_t n, off_t& offset) { + offset += off_t(n); +} + +// Wrap call to read/pread/write/pwrite(fd, buf, count, offset?) to retry on +// incomplete reads / writes. The variadic argument magic is there to support +// an additional argument (offset) for pread / pwrite; see the incr() functions +// above which do nothing if the offset is not present and increment it if it +// is. +template <class F, class... Offset> +ssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) { + char* b = static_cast<char*>(buf); + ssize_t totalBytes = 0; + ssize_t r; + do { + r = f(fd, b, count, offset...); + if (r == -1) { + if (errno == EINTR) { + continue; + } + return r; + } + + totalBytes += r; + b += r; + count -= r; + incr(r, offset...); + } while (r != 0 && count); // 0 means EOF + + return totalBytes; +} + +// Wrap call to readv/preadv/writev/pwritev(fd, iov, count, offset?) to +// retry on incomplete reads / writes. +template <class F, class... Offset> +ssize_t wrapvFull(F f, int fd, iovec* iov, int count, Offset... offset) { + ssize_t totalBytes = 0; + ssize_t r; + do { + r = f(fd, iov, std::min<int>(count, kIovMax), offset...); + if (r == -1) { + if (errno == EINTR) { + continue; + } + return r; + } + + if (r == 0) { + break; // EOF + } + + totalBytes += r; + incr(r, offset...); + while (r != 0 && count != 0) { + if (r >= ssize_t(iov->iov_len)) { + r -= ssize_t(iov->iov_len); + ++iov; + --count; + } else { + iov->iov_base = static_cast<char*>(iov->iov_base) + r; + iov->iov_len -= r; + r = 0; + } + } + } while (count); + + return totalBytes; +} + +} // namespace fileutil_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/FingerprintPolynomial.h b/ios/Pods/Flipper-Folly/folly/detail/FingerprintPolynomial.h new file mode 100644 index 000000000..38acdd579 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/FingerprintPolynomial.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stddef.h> +#include <cstdint> + +namespace folly { +namespace detail { + +/** + * Representation of a polynomial of degree DEG over GF(2) (that is, + * with binary coefficients). + * + * Probably of no use outside of Fingerprint code; used by + * GenerateFingerprintTables and the unittest. + */ +template <int DEG> +class FingerprintPolynomial { + public: + static constexpr int size() { + return 1 + DEG / 64; + } + + constexpr FingerprintPolynomial() {} + + constexpr explicit FingerprintPolynomial(const uint64_t (&vals)[size()]) { + for (int i = 0; i < size(); i++) { + val_[i] = vals[i]; + } + } + + constexpr uint64_t get(size_t i) const { + return val_[i]; + } + + constexpr void add(const FingerprintPolynomial<DEG>& other) { + for (int i = 0; i < size(); i++) { + val_[i] ^= other.val_[i]; + } + } + + // Multiply by X. The actual degree must be < DEG. + constexpr void mulX() { + uint64_t b = 0; + for (int i = size() - 1; i >= 0; i--) { + uint64_t nb = val_[i] >> 63; + val_[i] = (val_[i] << 1) | b; + b = nb; + } + } + + // Compute (this * X) mod P(X), where P(X) is a monic polynomial of degree + // DEG+1 (represented as a FingerprintPolynomial<DEG> object, with the + // implicit coefficient of X^(DEG+1)==1) + // + // This is a bit tricky. If k=DEG+1: + // Let P(X) = X^k + p_(k-1) * X^(k-1) + ... + p_1 * X + p_0 + // Let this = A(X) = a_(k-1) * X^(k-1) + ... + a_1 * X + a_0 + // Then: + // A(X) * X + // = a_(k-1) * X^k + (a_(k-2) * X^(k-1) + ... + a_1 * X^2 + a_0 * X) + // = a_(k-1) * X^k + (the binary representation of A, left shift by 1) + // + // if a_(k-1) = 0, we can ignore the first term. + // if a_(k-1) = 1, then: + // X^k mod P(X) + // = X^k - P(X) + // = P(X) - X^k + // = p_(k-1) * X^(k-1) + ... + p_1 * X + p_0 + // = exactly the binary representation passed in as an argument to this + // function! + // + // So A(X) * X mod P(X) is: + // the binary representation of A, left shift by 1, + // XOR p if a_(k-1) == 1 + constexpr void mulXmod(const FingerprintPolynomial<DEG>& p) { + bool needXOR = (val_[0] & (1ULL << 63)); + val_[0] &= ~(1ULL << 63); + mulX(); + if (needXOR) { + add(p); + } + } + + // Compute (this * X^k) mod P(X) by repeatedly multiplying by X (see above) + constexpr void mulXkmod(int k, const FingerprintPolynomial<DEG>& p) { + for (int i = 0; i < k; i++) { + mulXmod(p); + } + } + + // add X^k, where k <= DEG + constexpr void addXk(int k) { + int word_offset = (DEG - k) / 64; + int bit_offset = 63 - (DEG - k) % 64; + val_[word_offset] ^= (1ULL << bit_offset); + } + + // Set the highest 8 bits to val. + // If val is interpreted as polynomial of degree 7, then this sets *this + // to val * X^(DEG-7) + constexpr void setHigh8Bits(uint8_t val) { + val_[0] = ((uint64_t)val) << (64 - 8); + for (int i = 1; i < size(); i++) { + val_[i] = 0; + } + } + + private: + // Internal representation: big endian + // val_[0] contains the highest order coefficients, with bit 63 as the + // highest order coefficient + // + // If DEG+1 is not a multiple of 64, val_[size()-1] only uses the highest + // order (DEG+1)%64 bits (the others are always 0) + uint64_t val_[size()] = {}; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Futex-inl.h b/ios/Pods/Flipper-Folly/folly/detail/Futex-inl.h new file mode 100644 index 000000000..8d34ac6a2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Futex-inl.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/detail/Futex.h> +#include <folly/synchronization/ParkingLot.h> + +namespace folly { +namespace detail { + +/** Optimal when TargetClock is the same type as Clock. + * + * Otherwise, both Clock::now() and TargetClock::now() must be invoked. */ +template <typename TargetClock, typename Clock, typename Duration> +typename TargetClock::time_point time_point_conv( + std::chrono::time_point<Clock, Duration> const& time) { + using std::chrono::duration_cast; + using TimePoint = std::chrono::time_point<Clock, Duration>; + using TargetDuration = typename TargetClock::duration; + using TargetTimePoint = typename TargetClock::time_point; + if (time == TimePoint::max()) { + return TargetTimePoint::max(); + } else if (std::is_same<Clock, TargetClock>::value) { + // in place of time_point_cast, which cannot compile without if-constexpr + auto const delta = time.time_since_epoch(); + return TargetTimePoint(duration_cast<TargetDuration>(delta)); + } else { + // different clocks with different epochs, so non-optimal case + auto const delta = time - Clock::now(); + return TargetClock::now() + duration_cast<TargetDuration>(delta); + } +} + +/** + * Available overloads, with definitions elsewhere + * + * These functions are treated as ADL-extension points, the templates above + * call these functions without them having being pre-declared. This works + * because ADL lookup finds the definitions of these functions when you pass + * the relevant arguments + */ +int futexWakeImpl( + const Futex<std::atomic>* futex, + int count, + uint32_t wakeMask); +FutexResult futexWaitImpl( + const Futex<std::atomic>* futex, + uint32_t expected, + std::chrono::system_clock::time_point const* absSystemTime, + std::chrono::steady_clock::time_point const* absSteadyTime, + uint32_t waitMask); + +int futexWakeImpl( + const Futex<EmulatedFutexAtomic>* futex, + int count, + uint32_t wakeMask); +FutexResult futexWaitImpl( + const Futex<EmulatedFutexAtomic>* futex, + uint32_t expected, + std::chrono::system_clock::time_point const* absSystemTime, + std::chrono::steady_clock::time_point const* absSteadyTime, + uint32_t waitMask); + +template <typename Futex, typename Deadline> +typename std::enable_if<Deadline::clock::is_steady, FutexResult>::type +futexWaitImpl( + Futex* futex, + uint32_t expected, + Deadline const& deadline, + uint32_t waitMask) { + return futexWaitImpl(futex, expected, nullptr, &deadline, waitMask); +} + +template <typename Futex, typename Deadline> +typename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type +futexWaitImpl( + Futex* futex, + uint32_t expected, + Deadline const& deadline, + uint32_t waitMask) { + return futexWaitImpl(futex, expected, &deadline, nullptr, waitMask); +} + +template <typename Futex> +FutexResult +futexWait(const Futex* futex, uint32_t expected, uint32_t waitMask) { + auto rv = futexWaitImpl(futex, expected, nullptr, nullptr, waitMask); + assert(rv != FutexResult::TIMEDOUT); + return rv; +} + +template <typename Futex> +int futexWake(const Futex* futex, int count, uint32_t wakeMask) { + return futexWakeImpl(futex, count, wakeMask); +} + +template <typename Futex, class Clock, class Duration> +FutexResult futexWaitUntil( + const Futex* futex, + uint32_t expected, + std::chrono::time_point<Clock, Duration> const& deadline, + uint32_t waitMask) { + using Target = typename std::conditional< + Clock::is_steady, + std::chrono::steady_clock, + std::chrono::system_clock>::type; + auto const converted = time_point_conv<Target>(deadline); + return converted == Target::time_point::max() + ? futexWaitImpl(futex, expected, nullptr, nullptr, waitMask) + : futexWaitImpl(futex, expected, converted, waitMask); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Futex.cpp b/ios/Pods/Flipper-Folly/folly/detail/Futex.cpp new file mode 100644 index 000000000..0d3be6c98 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Futex.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/Futex.h> +#include <folly/ScopeGuard.h> +#include <folly/hash/Hash.h> +#include <folly/portability/SysSyscall.h> +#include <array> +#include <cerrno> +#include <cstdint> +#include <cstring> + +#include <folly/synchronization/ParkingLot.h> + +#ifdef __linux__ +#include <linux/futex.h> +#endif + +using namespace std::chrono; + +namespace folly { +namespace detail { + +namespace { + +//////////////////////////////////////////////////// +// native implementation using the futex() syscall + +#ifdef __linux__ + +/// Certain toolchains (like Android's) don't include the full futex API in +/// their headers even though they support it. Make sure we have our constants +/// even if the headers don't have them. +#ifndef FUTEX_WAIT_BITSET +#define FUTEX_WAIT_BITSET 9 +#endif +#ifndef FUTEX_WAKE_BITSET +#define FUTEX_WAKE_BITSET 10 +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#ifndef FUTEX_CLOCK_REALTIME +#define FUTEX_CLOCK_REALTIME 256 +#endif + +int nativeFutexWake(const void* addr, int count, uint32_t wakeMask) { + int rv = syscall( + __NR_futex, + addr, /* addr1 */ + FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */ + count, /* val */ + nullptr, /* timeout */ + nullptr, /* addr2 */ + wakeMask); /* val3 */ + + /* NOTE: we ignore errors on wake for the case of a futex + guarding its own destruction, similar to this + glibc bug with sem_post/sem_wait: + https://sourceware.org/bugzilla/show_bug.cgi?id=12674 */ + if (rv < 0) { + return 0; + } + return rv; +} + +template <class Clock> +struct timespec timeSpecFromTimePoint(time_point<Clock> absTime) { + auto epoch = absTime.time_since_epoch(); + if (epoch.count() < 0) { + // kernel timespec_valid requires non-negative seconds and nanos in [0,1G) + epoch = Clock::duration::zero(); + } + + // timespec-safe seconds and nanoseconds; + // chrono::{nano,}seconds are `long long int` + // whereas timespec uses smaller types + using time_t_seconds = duration<std::time_t, seconds::period>; + using long_nanos = duration<long int, nanoseconds::period>; + + auto secs = duration_cast<time_t_seconds>(epoch); + auto nanos = duration_cast<long_nanos>(epoch - secs); + struct timespec result = {secs.count(), nanos.count()}; + return result; +} + +FutexResult nativeFutexWaitImpl( + const void* addr, + uint32_t expected, + system_clock::time_point const* absSystemTime, + steady_clock::time_point const* absSteadyTime, + uint32_t waitMask) { + assert(absSystemTime == nullptr || absSteadyTime == nullptr); + + int op = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG; + struct timespec ts; + struct timespec* timeout = nullptr; + + if (absSystemTime != nullptr) { + op |= FUTEX_CLOCK_REALTIME; + ts = timeSpecFromTimePoint(*absSystemTime); + timeout = &ts; + } else if (absSteadyTime != nullptr) { + ts = timeSpecFromTimePoint(*absSteadyTime); + timeout = &ts; + } + + // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET requires an absolute timeout + // value - http://locklessinc.com/articles/futex_cheat_sheet/ + int rv = syscall( + __NR_futex, + addr, /* addr1 */ + op, /* op */ + expected, /* val */ + timeout, /* timeout */ + nullptr, /* addr2 */ + waitMask); /* val3 */ + + if (rv == 0) { + return FutexResult::AWOKEN; + } else { + switch (errno) { + case ETIMEDOUT: + assert(timeout != nullptr); + return FutexResult::TIMEDOUT; + case EINTR: + return FutexResult::INTERRUPTED; + case EWOULDBLOCK: + return FutexResult::VALUE_CHANGED; + default: + assert(false); + // EINVAL, EACCESS, or EFAULT. EINVAL means there was an invalid + // op (should be impossible) or an invalid timeout (should have + // been sanitized by timeSpecFromTimePoint). EACCESS or EFAULT + // means *addr points to invalid memory, which is unlikely because + // the caller should have segfaulted already. We can either + // crash, or return a value that lets the process continue for + // a bit. We choose the latter. VALUE_CHANGED probably turns the + // caller into a spin lock. + return FutexResult::VALUE_CHANGED; + } + } +} + +#endif // __linux__ + +/////////////////////////////////////////////////////// +// compatibility implementation using standard C++ API + +using Lot = ParkingLot<uint32_t>; +Lot parkingLot; + +int emulatedFutexWake(const void* addr, int count, uint32_t waitMask) { + int woken = 0; + parkingLot.unpark(addr, [&](const uint32_t& mask) { + if ((mask & waitMask) == 0) { + return UnparkControl::RetainContinue; + } + assert(count > 0); + count--; + woken++; + return count > 0 ? UnparkControl::RemoveContinue + : UnparkControl::RemoveBreak; + }); + return woken; +} + +template <typename F> +FutexResult emulatedFutexWaitImpl( + F* futex, + uint32_t expected, + system_clock::time_point const* absSystemTime, + steady_clock::time_point const* absSteadyTime, + uint32_t waitMask) { + static_assert( + std::is_same<F, const Futex<std::atomic>>::value || + std::is_same<F, const Futex<EmulatedFutexAtomic>>::value, + "Type F must be either Futex<std::atomic> or Futex<EmulatedFutexAtomic>"); + ParkResult res; + if (absSystemTime) { + res = parkingLot.park_until( + futex, + waitMask, + [&] { return *futex == expected; }, + [] {}, + *absSystemTime); + } else if (absSteadyTime) { + res = parkingLot.park_until( + futex, + waitMask, + [&] { return *futex == expected; }, + [] {}, + *absSteadyTime); + } else { + res = parkingLot.park( + futex, waitMask, [&] { return *futex == expected; }, [] {}); + } + switch (res) { + case ParkResult::Skip: + return FutexResult::VALUE_CHANGED; + case ParkResult::Unpark: + return FutexResult::AWOKEN; + case ParkResult::Timeout: + return FutexResult::TIMEDOUT; + } + + return FutexResult::INTERRUPTED; +} + +} // namespace + +///////////////////////////////// +// Futex<> overloads + +int futexWakeImpl( + const Futex<std::atomic>* futex, + int count, + uint32_t wakeMask) { +#ifdef __linux__ + return nativeFutexWake(futex, count, wakeMask); +#else + return emulatedFutexWake(futex, count, wakeMask); +#endif +} + +int futexWakeImpl( + const Futex<EmulatedFutexAtomic>* futex, + int count, + uint32_t wakeMask) { + return emulatedFutexWake(futex, count, wakeMask); +} + +FutexResult futexWaitImpl( + const Futex<std::atomic>* futex, + uint32_t expected, + system_clock::time_point const* absSystemTime, + steady_clock::time_point const* absSteadyTime, + uint32_t waitMask) { +#ifdef __linux__ + return nativeFutexWaitImpl( + futex, expected, absSystemTime, absSteadyTime, waitMask); +#else + return emulatedFutexWaitImpl( + futex, expected, absSystemTime, absSteadyTime, waitMask); +#endif +} + +FutexResult futexWaitImpl( + const Futex<EmulatedFutexAtomic>* futex, + uint32_t expected, + system_clock::time_point const* absSystemTime, + steady_clock::time_point const* absSteadyTime, + uint32_t waitMask) { + return emulatedFutexWaitImpl( + futex, expected, absSystemTime, absSteadyTime, waitMask); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Futex.h b/ios/Pods/Flipper-Folly/folly/detail/Futex.h new file mode 100644 index 000000000..f0edfbbc2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Futex.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cassert> +#include <chrono> +#include <cstdint> +#include <limits> +#include <type_traits> + +#include <folly/portability/Unistd.h> + +namespace folly { +namespace detail { + +enum class FutexResult { + VALUE_CHANGED, /* futex value didn't match expected */ + AWOKEN, /* wakeup by matching futex wake, or spurious wakeup */ + INTERRUPTED, /* wakeup by interrupting signal */ + TIMEDOUT, /* wakeup by expiring deadline */ +}; + +/** + * Futex is an atomic 32 bit unsigned integer that provides access to the + * futex() syscall on that value. It is templated in such a way that it + * can interact properly with DeterministicSchedule testing. + * + * If you don't know how to use futex(), you probably shouldn't be using + * this class. Even if you do know how, you should have a good reason + * (and benchmarks to back you up). + * + * Because of the semantics of the futex syscall, the futex family of + * functions are available as free functions rather than member functions + */ +template <template <typename> class Atom = std::atomic> +using Futex = Atom<std::uint32_t>; + +/** + * Puts the thread to sleep if this->load() == expected. Returns true when + * it is returning because it has consumed a wake() event, false for any + * other return (signal, this->load() != expected, or spurious wakeup). + */ +template <typename Futex> +FutexResult +futexWait(const Futex* futex, uint32_t expected, uint32_t waitMask = -1); + +/** + * Similar to futexWait but also accepts a deadline until when the wait call + * may block. + * + * Optimal clock types: std::chrono::system_clock, std::chrono::steady_clock. + * NOTE: On some systems steady_clock is just an alias for system_clock, + * and is not actually steady. + * + * For any other clock type, now() will be invoked twice. + */ +template <typename Futex, class Clock, class Duration> +FutexResult futexWaitUntil( + const Futex* futex, + uint32_t expected, + std::chrono::time_point<Clock, Duration> const& deadline, + uint32_t waitMask = -1); + +/** + * Wakes up to count waiters where (waitMask & wakeMask) != 0, returning the + * number of awoken threads, or -1 if an error occurred. Note that when + * constructing a concurrency primitive that can guard its own destruction, it + * is likely that you will want to ignore EINVAL here (as well as making sure + * that you never touch the object after performing the memory store that is + * the linearization point for unlock or control handoff). See + * https://sourceware.org/bugzilla/show_bug.cgi?id=13690 + */ +template <typename Futex> +int futexWake( + const Futex* futex, + int count = std::numeric_limits<int>::max(), + uint32_t wakeMask = -1); + +/** A std::atomic subclass that can be used to force Futex to emulate + * the underlying futex() syscall. This is primarily useful to test or + * benchmark the emulated implementation on systems that don't need it. */ +template <typename T> +struct EmulatedFutexAtomic : public std::atomic<T> { + EmulatedFutexAtomic() noexcept = default; + constexpr /* implicit */ EmulatedFutexAtomic(T init) noexcept + : std::atomic<T>(init) {} + // It doesn't copy or move + EmulatedFutexAtomic(EmulatedFutexAtomic&& rhs) = delete; +}; + +} // namespace detail +} // namespace folly + +#include <folly/detail/Futex-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/detail/GroupVarintDetail.h b/ios/Pods/Flipper-Folly/folly/detail/GroupVarintDetail.h new file mode 100644 index 000000000..b24af6f0a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/GroupVarintDetail.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stddef.h> +#include <stdint.h> + +namespace folly { + +template <typename T> +class GroupVarint; + +namespace detail { + +template <typename T> +struct GroupVarintTraits; + +template <> +struct GroupVarintTraits<uint32_t> { + enum : uint32_t { + kGroupSize = 4, + kHeaderSize = 1, + }; +}; + +template <> +struct GroupVarintTraits<uint64_t> { + enum : uint32_t { + kGroupSize = 5, + kHeaderSize = 2, + }; +}; + +template <typename T> +class GroupVarintBase { + protected: + typedef GroupVarintTraits<T> Traits; + enum : uint32_t { kHeaderSize = Traits::kHeaderSize }; + + public: + typedef T type; + + /** + * Number of integers encoded / decoded in one pass. + */ + enum : uint32_t { kGroupSize = Traits::kGroupSize }; + + /** + * Maximum encoded size. + */ + enum : uint32_t { kMaxSize = kHeaderSize + sizeof(type) * kGroupSize }; + + /** + * Maximum size for n values. + */ + static size_t maxSize(size_t n) { + // Full groups + size_t total = (n / kGroupSize) * kFullGroupSize; + // Incomplete last group, if any + n %= kGroupSize; + if (n) { + total += kHeaderSize + n * sizeof(type); + } + return total; + } + + /** + * Size of n values starting at p. + */ + static size_t totalSize(const T* p, size_t n) { + size_t size = 0; + for (; n >= kGroupSize; n -= kGroupSize, p += kGroupSize) { + size += Derived::size(p); + } + if (n) { + size += Derived::partialSize(p, n); + } + return size; + } + + private: + typedef GroupVarint<T> Derived; + enum { kFullGroupSize = kHeaderSize + kGroupSize * sizeof(type) }; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/IPAddress.cpp b/ios/Pods/Flipper-Folly/folly/detail/IPAddress.cpp new file mode 100644 index 000000000..154f7771a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/IPAddress.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/IPAddress.h> + +#include <folly/Format.h> + +namespace folly { +namespace detail { + +std::string familyNameStrDefault(sa_family_t family) { + return sformat("sa_family_t({})", family); +} + +[[noreturn]] void getNthMSBitImplThrow(size_t bitCount, sa_family_t family) { + throw std::invalid_argument(sformat( + "Bit index must be < {} for addresses of type: {}", + bitCount, + familyNameStr(family))); +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/IPAddress.h b/ios/Pods/Flipper-Folly/folly/detail/IPAddress.h new file mode 100644 index 000000000..c4fe2270e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/IPAddress.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/types.h> + +#include <string> + +#include <folly/portability/Sockets.h> + +namespace folly { +namespace detail { + +std::string familyNameStrDefault(sa_family_t family); + +inline std::string familyNameStr(sa_family_t family) { + switch (family) { + case AF_INET: + return "AF_INET"; + case AF_INET6: + return "AF_INET6"; + case AF_UNSPEC: + return "AF_UNSPEC"; + case AF_UNIX: + return "AF_UNIX"; + default: + return familyNameStrDefault(family); + } +} + +[[noreturn]] void getNthMSBitImplThrow(size_t bitCount, sa_family_t family); + +template <typename IPAddrType> +inline bool +getNthMSBitImpl(const IPAddrType& ip, size_t bitIndex, sa_family_t family) { + if (bitIndex >= ip.bitCount()) { + getNthMSBitImplThrow(ip.bitCount(), family); + } + // Underlying bytes are in n/w byte order + return (ip.getNthMSByte(bitIndex / 8) & (0x80 >> (bitIndex % 8))) != 0; +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/IPAddressSource.h b/ios/Pods/Flipper-Folly/folly/detail/IPAddressSource.h new file mode 100644 index 000000000..e8cbb8777 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/IPAddressSource.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <glog/logging.h> +#include <sys/types.h> +#include <algorithm> +#include <array> +#include <cstring> +#include <string> +#include <type_traits> + +#include <folly/Format.h> +#include <folly/detail/IPAddress.h> + +// BSDish platforms don't provide standard access to s6_addr16 +#ifndef s6_addr16 +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) +#define s6_addr16 __u6_addr.__u6_addr16 +#endif +#endif + +namespace folly { +namespace detail { + +/** + * Helper for working with unsigned char* or uint8_t* ByteArray values + */ +struct Bytes { + // mask the values from two byte arrays, returning a new byte array + template <std::size_t N> + static std::array<uint8_t, N> mask( + const std::array<uint8_t, N>& a, + const std::array<uint8_t, N>& b) { + static_assert(N > 0, "Can't mask an empty ByteArray"); + std::size_t asize = a.size(); + std::array<uint8_t, N> ba{{0}}; + for (std::size_t i = 0; i < asize; i++) { + ba[i] = uint8_t(a[i] & b[i]); + } + return ba; + } + + template <std::size_t N> + static std::pair<std::array<uint8_t, N>, uint8_t> longestCommonPrefix( + const std::array<uint8_t, N>& one, + uint8_t oneMask, + const std::array<uint8_t, N>& two, + uint8_t twoMask) { + static constexpr auto kBitCount = N * 8; + static constexpr std::array<uint8_t, 8> kMasks{{ + 0x80, // /1 + 0xc0, // /2 + 0xe0, // /3 + 0xf0, // /4 + 0xf8, // /5 + 0xfc, // /6 + 0xfe, // /7 + 0xff // /8 + }}; + if (oneMask > kBitCount || twoMask > kBitCount) { + throw std::invalid_argument(sformat( + "Invalid mask length: {}. Mask length must be <= {}", + std::max(oneMask, twoMask), + kBitCount)); + } + + auto mask = std::min(oneMask, twoMask); + uint8_t byteIndex = 0; + std::array<uint8_t, N> ba{{0}}; + // Compare a byte at a time. Note - I measured compared this with + // going multiple bytes at a time (8, 4, 2 and 1). It turns out + // to be 20 - 25% slower for 4 and 16 byte arrays. + while (byteIndex * 8 < mask && one[byteIndex] == two[byteIndex]) { + ba[byteIndex] = one[byteIndex]; + ++byteIndex; + } + auto bitIndex = std::min(mask, uint8_t(byteIndex * 8)); + uint8_t bI = uint8_t(bitIndex / 8); + uint8_t bM = uint8_t(bitIndex % 8); + // Compute the bit up to which the two byte arrays match in the + // unmatched byte. + // Here the check is bitIndex < mask since the 0th mask entry in + // kMasks array holds the mask for masking the MSb in this byte. + // We could instead make it hold so that no 0th entry masks no + // bits but thats a useless iteration. + while (bitIndex < mask && + ((one[bI] & kMasks[bM]) == (two[bI] & kMasks[bM]))) { + ba[bI] = uint8_t(one[bI] & kMasks[bM]); + ++bitIndex; + bI = uint8_t(bitIndex / 8); + bM = uint8_t(bitIndex % 8); + } + return {ba, bitIndex}; + } + + // create an in_addr from an uint8_t* + static inline in_addr mkAddress4(const uint8_t* src) { + union { + in_addr addr; + uint8_t bytes[4]; + } addr; + std::memset(&addr, 0, 4); + std::memcpy(addr.bytes, src, 4); + return addr.addr; + } + + // create an in6_addr from an uint8_t* + static inline in6_addr mkAddress6(const uint8_t* src) { + in6_addr addr; + std::memset(&addr, 0, 16); + std::memcpy(addr.s6_addr, src, 16); + return addr; + } + + // convert an uint8_t* to its hex value + static std::string toHex(const uint8_t* src, std::size_t len) { + static const char* const lut = "0123456789abcdef"; + std::string out(len * 2, 0); + for (std::size_t i = 0; i < len; i++) { + const unsigned char c = src[i]; + out[i * 2 + 0] = lut[c >> 4]; + out[i * 2 + 1] = lut[c & 15]; + } + return out; + } + + private: + Bytes() = delete; + ~Bytes() = delete; +}; + +// +// Write a maximum amount of base-converted character digits, of a +// given base, from an unsigned integral type into a byte buffer of +// sufficient size. +// +// This function does not append null terminators. +// +// Output buffer size must be guaranteed by caller (indirectly +// controlled by DigitCount template parameter). +// +// Having these parameters at compile time allows compiler to +// precompute several of the values, use smaller instructions, and +// better optimize surrounding code. +// +// IntegralType: +// - Something like uint8_t, uint16_t, etc +// +// DigitCount is the maximum number of digits to be printed +// - This is tied to IntegralType and Base. For example: +// - uint8_t in base 10 will print at most 3 digits ("255") +// - uint16_t in base 16 will print at most 4 hex digits ("FFFF") +// +// Base is the desired output base of the string +// - Base 10 will print [0-9], base 16 will print [0-9a-f] +// +// PrintAllDigits: +// - Whether or not leading zeros should be printed +// +template < + class IntegralType, + IntegralType DigitCount, + IntegralType Base = IntegralType(10), + bool PrintAllDigits = false, + class = typename std::enable_if< + std::is_integral<IntegralType>::value && + std::is_unsigned<IntegralType>::value, + bool>::type> +inline void writeIntegerString(IntegralType val, char** buffer) { + char* buf = *buffer; + + if (!PrintAllDigits && val == 0) { + *(buf++) = '0'; + *buffer = buf; + return; + } + + IntegralType powerToPrint = 1; + for (IntegralType i = 1; i < DigitCount; ++i) { + powerToPrint *= Base; + } + + bool found = PrintAllDigits; + while (powerToPrint) { + if (found || powerToPrint <= val) { + IntegralType value = IntegralType(val / powerToPrint); + if (Base == 10 || value < 10) { + value += '0'; + } else { + value += ('a' - 10); + } + *(buf++) = char(value); + val %= powerToPrint; + found = true; + } + + powerToPrint /= Base; + } + + *buffer = buf; +} + +inline size_t fastIpV4ToBufferUnsafe(const in_addr& inAddr, char* str) { + const uint8_t* octets = reinterpret_cast<const uint8_t*>(&inAddr.s_addr); + char* buf = str; + + writeIntegerString<uint8_t, 3>(octets[0], &buf); + *(buf++) = '.'; + writeIntegerString<uint8_t, 3>(octets[1], &buf); + *(buf++) = '.'; + writeIntegerString<uint8_t, 3>(octets[2], &buf); + *(buf++) = '.'; + writeIntegerString<uint8_t, 3>(octets[3], &buf); + + return buf - str; +} + +inline std::string fastIpv4ToString(const in_addr& inAddr) { + char str[sizeof("255.255.255.255")]; + return std::string(str, fastIpV4ToBufferUnsafe(inAddr, str)); +} + +inline void fastIpv4AppendToString(const in_addr& inAddr, std::string& out) { + char str[sizeof("255.255.255.255")]; + out.append(str, fastIpV4ToBufferUnsafe(inAddr, str)); +} + +inline size_t fastIpv6ToBufferUnsafe(const in6_addr& in6Addr, char* str) { +#ifdef _MSC_VER + const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.u.Word); +#else + const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.s6_addr16); +#endif + char* buf = str; + + for (int i = 0; i < 8; ++i) { + writeIntegerString< + uint16_t, + 4, // at most 4 hex digits per ushort + 16, // base 16 (hex) + true>(htons(bytes[i]), &buf); + + if (i != 7) { + *(buf++) = ':'; + } + } + + return buf - str; +} + +inline std::string fastIpv6ToString(const in6_addr& in6Addr) { + char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")]; + return std::string(str, fastIpv6ToBufferUnsafe(in6Addr, str)); +} + +inline void fastIpv6AppendToString(const in6_addr& in6Addr, std::string& out) { + char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")]; + out.append(str, fastIpv6ToBufferUnsafe(in6Addr, str)); +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Iterators.h b/ios/Pods/Flipper-Folly/folly/detail/Iterators.h new file mode 100644 index 000000000..3fee1dbb7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Iterators.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <iterator> +#include <type_traits> + +/* + * This contains stripped-down workalikes of some Boost classes: + * + * iterator_adaptor + * iterator_facade + * + * Rationale: the boost headers providing those classes are surprisingly large. + * The bloat comes from the headers themselves, but more so, their transitive + * includes. + * + * These implementations are simple and minimal. They may be missing features + * provided by the Boost classes mentioned above. Also at this time they only + * support forward-iterators. They provide just enough for the few uses within + * Folly libs; more features will be slapped in here if and when they're needed. + * + * These classes may possibly add features as well. Care is taken not to + * change functionality where it's expected to be the same (e.g. `dereference` + * will do the same thing). + * + * These are currently only intended for use within Folly, hence their living + * under detail. Use outside Folly is not recommended. + * + * To see how to use these classes, find the instances where this is used within + * Folly libs. Common use cases can also be found in `IteratorsTest.cpp`. + */ + +namespace folly { +namespace detail { + +/** + * Currently this only supports forward and bidirectional iteration. The + * derived class must must have definitions for these methods: + * + * void increment(); + * void decrement(); // optional, to be used with bidirectional + * reference dereference() const; + * bool equal([appropriate iterator type] const& rhs) const; + * + * These names are consistent with those used by the Boost iterator + * facade / adaptor classes to ease migration classes in this file. + * + * Template parameters: + * D: the deriving class (CRTP) + * V: value type + * Tag: the iterator category, one of: + * std::forward_iterator_tag + * std::bidirectional_iterator_tag + */ +template <class D, class V, class Tag> +class IteratorFacade { + public: + using value_type = V; + using reference = value_type&; + using pointer = value_type*; + using difference_type = ssize_t; + using iterator_category = Tag; + + bool operator==(D const& rhs) const { + return asDerivedConst().equal(rhs); + } + + bool operator!=(D const& rhs) const { + return !operator==(rhs); + } + + /* + * Allow for comparisons between this and an iterator of some other class. + * (e.g. a const_iterator version of this, the probable use case). + * Does a conversion of D (or D reference) to D2, if one exists (otherwise + * this is disabled). Disabled if D and D2 are the same, to disambiguate + * this and the `operator==(D const&) const` method above. + */ + + template < + class D2, + std::enable_if_t<!std::is_same<D, D2>::value, int> = 0, + std::enable_if_t<std::is_convertible<D, D2>::value, int> = 0> + bool operator==(D2 const& rhs) const { + return D2(asDerivedConst()) == rhs; + } + + template <class D2> + bool operator!=(D2 const& rhs) const { + return !operator==(rhs); + } + + V& operator*() const { + return asDerivedConst().dereference(); + } + + V* operator->() const { + return std::addressof(operator*()); + } + + D& operator++() { + asDerived().increment(); + return asDerived(); + } + + D operator++(int) { + auto ret = asDerived(); // copy + asDerived().increment(); + return ret; + } + + D& operator--() { + asDerived().decrement(); + return asDerived(); + } + + D operator--(int) { + auto ret = asDerived(); // copy + asDerived().decrement(); + return ret; + } + + private: + D& asDerived() { + return static_cast<D&>(*this); + } + + D const& asDerivedConst() const { + return static_cast<D const&>(*this); + } +}; + +/** + * Wrap one iterator while providing an interator interface with e.g. a + * different value_type. + * + * Template parameters: + * D: the deriving class (CRTP) + * I: the wrapper iterator type + * V: value type + */ +template <class D, class I, class V, class Tag> +class IteratorAdaptor : public IteratorFacade<D, V, Tag> { + public: + using Super = IteratorFacade<D, V, Tag>; + using value_type = typename Super::value_type; + using iterator_category = typename Super::iterator_category; + using reference = typename Super::reference; + using pointer = typename Super::pointer; + using difference_type = typename Super::difference_type; + + explicit IteratorAdaptor(I base) : base_(base) {} + + void increment() { + ++base_; + } + + void decrement() { + --base_; + } + + V& dereference() const { + return *base_; + } + + bool equal(D const& rhs) const { + return base_ == rhs.base_; + } + + I const& base() const { + return base_; + } + I& base() { + return base_; + } + + private: + I base_; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/MPMCPipelineDetail.h b/ios/Pods/Flipper-Folly/folly/detail/MPMCPipelineDetail.h new file mode 100644 index 000000000..e62e96e66 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/MPMCPipelineDetail.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/MPMCQueue.h> + +namespace folly { + +template <class T, class... Stages> +class MPMCPipeline; + +template <class T, size_t Amp> +class MPMCPipelineStage { + public: + typedef T value_type; + static constexpr size_t kAmplification = Amp; +}; + +namespace detail { + +/** + * Helper template to determine value type and amplification whether or not + * we use MPMCPipelineStage<> + */ +template <class T> +struct PipelineStageInfo { + static constexpr size_t kAmplification = 1; + typedef T value_type; +}; + +template <class T, size_t Amp> +struct PipelineStageInfo<MPMCPipelineStage<T, Amp>> { + static constexpr size_t kAmplification = Amp; + typedef T value_type; +}; + +/** + * Wrapper around MPMCQueue (friend) that keeps track of tickets. + */ +template <class T> +class MPMCPipelineStageImpl { + public: + typedef T value_type; + template <class U, class... Stages> + friend class MPMCPipeline; + + // Implicit so that MPMCPipeline construction works + /* implicit */ MPMCPipelineStageImpl(size_t capacity) : queue_(capacity) {} + MPMCPipelineStageImpl() {} + + // only use on first stage, uses queue_.pushTicket_ instead of existing + // ticket + template <class... Args> + void blockingWrite(Args&&... args) noexcept { + queue_.blockingWrite(std::forward<Args>(args)...); + } + + template <class... Args> + bool write(Args&&... args) noexcept { + return queue_.write(std::forward<Args>(args)...); + } + + template <class... Args> + void blockingWriteWithTicket(uint64_t ticket, Args&&... args) noexcept { + queue_.enqueueWithTicket(ticket, std::forward<Args>(args)...); + } + + uint64_t blockingRead(T& elem) noexcept { + uint64_t ticket; + queue_.blockingReadWithTicket(ticket, elem); + return ticket; + } + + bool read(T& elem) noexcept { // only use on last stage, won't track ticket + return queue_.read(elem); + } + + template <class... Args> + bool readAndGetTicket(uint64_t& ticket, T& elem) noexcept { + return queue_.readAndGetTicket(ticket, elem); + } + + // See MPMCQueue<T>::writeCount; only works for the first stage + uint64_t writeCount() const noexcept { + return queue_.writeCount(); + } + + uint64_t readCount() const noexcept { + return queue_.readCount(); + } + + private: + MPMCQueue<T> queue_; +}; + +// Product of amplifications of a tuple of PipelineStageInfo<X> +template <class Tuple> +struct AmplificationProduct; + +template <> +struct AmplificationProduct<std::tuple<>> { + static constexpr size_t value = 1; +}; + +template <class T, class... Ts> +struct AmplificationProduct<std::tuple<T, Ts...>> { + static constexpr size_t value = + T::kAmplification * AmplificationProduct<std::tuple<Ts...>>::value; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.cpp b/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.cpp new file mode 100644 index 000000000..a68a0a962 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/MemoryIdler.h> + +#include <folly/GLog.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/concurrency/CacheLocality.h> +#include <folly/memory/MallctlHelper.h> +#include <folly/memory/Malloc.h> +#include <folly/portability/PThread.h> +#include <folly/portability/SysMman.h> +#include <folly/portability/Unistd.h> +#include <folly/synchronization/CallOnce.h> + +#include <climits> +#include <cstdio> +#include <cstring> +#include <utility> + +namespace folly { +namespace detail { + +AtomicStruct<std::chrono::steady_clock::duration> + MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5)); + +void MemoryIdler::flushLocalMallocCaches() { + if (!usingJEMalloc()) { + return; + } + if (!mallctl || !mallctlnametomib || !mallctlbymib) { + FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed"; + return; + } + + try { + // Not using mallctlCall as this will fail if tcache is disabled. + mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0); + + // By default jemalloc has 4 arenas per cpu, and then assigns each + // thread to one of those arenas. This means that in any service + // that doesn't perform a lot of context switching, the chances that + // another thread will be using the current thread's arena (and hence + // doing the appropriate dirty-page purging) are low. Some good + // tuned configurations (such as that used by hhvm) use fewer arenas + // and then pin threads to avoid contended access. In that case, + // purging the arenas is counter-productive. We use the heuristic + // that if narenas <= 2 * num_cpus then we shouldn't do anything here, + // which detects when the narenas has been reduced from the default + unsigned narenas; + unsigned arenaForCurrent; + size_t mib[3]; + size_t miblen = 3; + + mallctlRead("opt.narenas", &narenas); + mallctlRead("thread.arena", &arenaForCurrent); + if (narenas > 2 * CacheLocality::system().numCpus && + mallctlnametomib("arena.0.purge", mib, &miblen) == 0) { + mib[1] = static_cast<size_t>(arenaForCurrent); + mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0); + } + } catch (const std::runtime_error& ex) { + FB_LOG_EVERY_MS(WARNING, 10000) << ex.what(); + } +} + +// Stack madvise isn't Linux or glibc specific, but the system calls +// and arithmetic (and bug compatibility) are not portable. The set of +// platforms could be increased if it was useful. +#if (FOLLY_X64 || FOLLY_PPC64) && defined(_GNU_SOURCE) && \ + defined(__linux__) && !FOLLY_MOBILE && !FOLLY_SANITIZE_ADDRESS + +static FOLLY_TLS uintptr_t tls_stackLimit; +static FOLLY_TLS size_t tls_stackSize; + +static size_t pageSize() { + static const size_t s_pageSize = sysconf(_SC_PAGESIZE); + return s_pageSize; +} + +static void fetchStackLimits() { + int err; + pthread_attr_t attr; + if ((err = pthread_getattr_np(pthread_self(), &attr))) { + // some restricted environments can't access /proc + static folly::once_flag flag; + folly::call_once(flag, [err]() { + LOG(WARNING) << "pthread_getaddr_np failed errno=" << err; + }); + + tls_stackSize = 1; + return; + } + SCOPE_EXIT { + pthread_attr_destroy(&attr); + }; + + void* addr; + size_t rawSize; + if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) { + // unexpected, but it is better to continue in prod than do nothing + FB_LOG_EVERY_MS(ERROR, 10000) << "pthread_attr_getstack error " << err; + assert(false); + tls_stackSize = 1; + return; + } + if (rawSize >= (1ULL << 32)) { + // Avoid unmapping huge swaths of memory if there is an insane + // stack size. The boundary of sanity is somewhat arbitrary: 4GB. + // + // If we went into /proc to find the actual contiguous mapped pages + // before unmapping we wouldn't care about the stack size at all, + // but our current strategy is to unmap the entire range that might + // be used for the stack even if it hasn't been fully faulted-in. + // + // Very large stack size is a bug (hence the assert), but we can + // carry on if we are in prod. + FB_LOG_EVERY_MS(ERROR, 10000) + << "pthread_attr_getstack returned insane stack size " << rawSize; + assert(false); + tls_stackSize = 1; + return; + } + assert(addr != nullptr); + assert(rawSize >= PTHREAD_STACK_MIN); + + // glibc subtracts guard page from stack size, even though pthread docs + // seem to imply the opposite + size_t guardSize; + if (pthread_attr_getguardsize(&attr, &guardSize) != 0) { + guardSize = 0; + } + assert(rawSize > guardSize); + + // stack goes down, so guard page adds to the base addr + tls_stackLimit = reinterpret_cast<uintptr_t>(addr) + guardSize; + tls_stackSize = rawSize - guardSize; + + assert((tls_stackLimit & (pageSize() - 1)) == 0); +} + +FOLLY_NOINLINE static uintptr_t getStackPtr() { + char marker; + auto rv = reinterpret_cast<uintptr_t>(&marker); + return rv; +} + +void MemoryIdler::unmapUnusedStack(size_t retain) { + if (tls_stackSize == 0) { + fetchStackLimits(); + } + if (tls_stackSize <= std::max(static_cast<size_t>(1), retain)) { + // covers both missing stack info, and impossibly large retain + return; + } + + auto sp = getStackPtr(); + assert(sp >= tls_stackLimit); + assert(sp - tls_stackLimit < tls_stackSize); + + auto end = (sp - retain) & ~(pageSize() - 1); + if (end <= tls_stackLimit) { + // no pages are eligible for unmapping + return; + } + + size_t len = end - tls_stackLimit; + assert((len & (pageSize() - 1)) == 0); + if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) { + // It is likely that the stack vma hasn't been fully grown. In this + // case madvise will apply dontneed to the present vmas, then return + // errno of ENOMEM. + // If thread stack pages are backed by locked or huge pages, madvise will + // fail with EINVAL. (EINVAL may also be returned if the address or length + // are bad.) Warn in debug mode, since MemoryIdler may not function as + // expected. + // We can also get an EAGAIN, theoretically. + PLOG_IF(WARNING, kIsDebug && errno == EINVAL) << "madvise failed"; + assert(errno == EAGAIN || errno == ENOMEM || errno == EINVAL); + } +} + +#else + +void MemoryIdler::unmapUnusedStack(size_t /* retain */) {} + +#endif + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.h b/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.h new file mode 100644 index 000000000..4b4d797b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/MemoryIdler.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <chrono> + +#include <folly/detail/Futex.h> +#include <folly/hash/Hash.h> +#include <folly/synchronization/AtomicStruct.h> +#include <folly/system/ThreadId.h> + +namespace folly { +namespace detail { + +/// MemoryIdler provides helper routines that allow routines to return +/// some assigned memory resources back to the system. The intended +/// use is that when a thread is waiting for a long time (perhaps it +/// is in a LIFO thread pool and hasn't been needed for a long time) +/// it should release its thread-local malloc caches (both jemalloc and +/// tcmalloc use these for better performance) and unmap the stack pages +/// that contain no useful data. +struct MemoryIdler { + /// Returns memory from thread-local allocation pools to the global + /// pool, if we know how to for the current malloc implementation. + /// jemalloc is supported. + static void flushLocalMallocCaches(); + + enum { + /// This value is a tradeoff between reclaiming memory and triggering + /// a page fault immediately on wakeup. Note that the actual unit + /// of idling for the stack is pages, so the actual stack that + /// will be available on wakeup without a page fault is between + /// kDefaultStackToRetain and kDefaultStackToRetain + PageSize - + /// 1 bytes. + kDefaultStackToRetain = 1024, + }; + + /// Uses madvise to discard the portion of the thread's stack that + /// currently doesn't hold any data, trying to ensure that no page + /// faults will occur during the next retain bytes of stack allocation + static void unmapUnusedStack(size_t retain = kDefaultStackToRetain); + + /// The system-wide default for the amount of time a blocking + /// thread should wait before reclaiming idle memory. Set this to + /// Duration::max() to never wait. The default value is 5 seconds. + /// Endpoints using this idle timeout might randomly wait longer to + /// avoid synchronizing their flushes. + static AtomicStruct<std::chrono::steady_clock::duration> defaultIdleTimeout; + + /// Selects a timeout pseudo-randomly chosen to be between + /// idleTimeout and idleTimeout * (1 + timeoutVariationFraction), to + /// smooth out the behavior in a bursty system + template <typename IdleTime = std::chrono::steady_clock::duration> + static IdleTime getVariationTimeout( + IdleTime const& idleTimeout = + defaultIdleTimeout.load(std::memory_order_acquire), + float timeoutVariationFrac = 0.5) { + if (idleTimeout <= IdleTime::zero() || timeoutVariationFrac <= 0) { + return idleTimeout; + } + + // hash the pthread_t and the time to get the adjustment + // Standard hash func isn't very good, so bit mix the result + uint64_t h = folly::hash::twang_mix64(folly::hash::hash_combine( + getCurrentThreadID(), + std::chrono::system_clock::now().time_since_epoch().count())); + + // multiplying the duration by a floating point doesn't work, grr + auto extraFrac = timeoutVariationFrac / + static_cast<float>(std::numeric_limits<uint64_t>::max()) * h; + auto tics = uint64_t(idleTimeout.count() * (1 + extraFrac)); + return IdleTime(tics); + } + + /// Equivalent to fut.futexWait(expected, waitMask), but calls + /// flushLocalMallocCaches() and unmapUnusedStack(stackToRetain) + /// after idleTimeout has passed (if it has passed). Internally uses + /// fut.futexWait and fut.futexWaitUntil. The actual timeout will be + /// pseudo-randomly chosen to be between idleTimeout and idleTimeout * + /// (1 + timeoutVariationFraction), to smooth out the behavior in a + /// system with bursty requests. The default is to wait up to 50% + /// extra, so on average 25% extra. + template < + typename Futex, + typename IdleTime = std::chrono::steady_clock::duration> + static FutexResult futexWait( + Futex& fut, + uint32_t expected, + uint32_t waitMask = -1, + IdleTime const& idleTimeout = + defaultIdleTimeout.load(std::memory_order_acquire), + size_t stackToRetain = kDefaultStackToRetain, + float timeoutVariationFrac = 0.5) { + FutexResult pre; + if (futexWaitPreIdle( + pre, + fut, + expected, + std::chrono::steady_clock::time_point::max(), + waitMask, + idleTimeout, + stackToRetain, + timeoutVariationFrac)) { + return pre; + } + + using folly::detail::futexWait; + return futexWait(&fut, expected, waitMask); + } + + /// Equivalent to fut.futexWaitUntil(expected, deadline, waitMask), but + /// calls flushLocalMallocCaches() and unmapUnusedStack(stackToRetain) + /// after idleTimeout has passed (if it has passed). Internally uses + /// fut.futexWaitUntil. The actual timeout will be pseudo-randomly + /// chosen to be between idleTimeout and idleTimeout * + /// (1 + timeoutVariationFraction), to smooth out the behavior in a + /// system with bursty requests. The default is to wait up to 50% + /// extra, so on average 25% extra. + template < + typename Futex, + typename Deadline, + typename IdleTime = std::chrono::steady_clock::duration> + static FutexResult futexWaitUntil( + Futex& fut, + uint32_t expected, + Deadline const& deadline, + uint32_t waitMask = -1, + IdleTime const& idleTimeout = + defaultIdleTimeout.load(std::memory_order_acquire), + size_t stackToRetain = kDefaultStackToRetain, + float timeoutVariationFrac = 0.5) { + FutexResult pre; + if (futexWaitPreIdle( + pre, + fut, + expected, + deadline, + waitMask, + idleTimeout, + stackToRetain, + timeoutVariationFrac)) { + return pre; + } + + using folly::detail::futexWaitUntil; + return futexWaitUntil(&fut, expected, deadline, waitMask); + } + + private: + template <typename Futex, typename Deadline, typename IdleTime> + static bool futexWaitPreIdle( + FutexResult& _ret, + Futex& fut, + uint32_t expected, + Deadline const& deadline, + uint32_t waitMask, + IdleTime idleTimeout, + size_t stackToRetain, + float timeoutVariationFrac) { + // idleTimeout < 0 means no flush behavior + if (idleTimeout < IdleTime::zero()) { + return false; + } + + // idleTimeout == 0 means flush immediately, without variation + // idleTimeout > 0 means flush after delay, with variation + if (idleTimeout > IdleTime::zero()) { + idleTimeout = std::max( + IdleTime::zero(), + getVariationTimeout(idleTimeout, timeoutVariationFrac)); + } + if (idleTimeout > IdleTime::zero()) { + auto idleDeadline = Deadline::clock::now() + idleTimeout; + if (idleDeadline < deadline) { + using folly::detail::futexWaitUntil; + auto rv = futexWaitUntil(&fut, expected, idleDeadline, waitMask); + if (rv != FutexResult::TIMEDOUT) { + // finished before timeout hit, no flush + _ret = rv; + return true; + } + } + } + + // flush, then wait + flushLocalMallocCaches(); + unmapUnusedStack(stackToRetain); + return false; + } +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/PolyDetail.h b/ios/Pods/Flipper-Folly/folly/detail/PolyDetail.h new file mode 100644 index 000000000..a5456a39a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/PolyDetail.h @@ -0,0 +1,984 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <new> +#include <tuple> +#include <type_traits> +#include <typeinfo> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/detail/TypeList.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Exception.h> +#include <folly/lang/StaticConst.h> + +#include <folly/PolyException.h> + +#if defined(__cpp_template_auto) || \ + defined(__cpp_nontype_template_parameter_auto) +#define FOLLY_POLY_NTTP_AUTO 1 +#else +#define FOLLY_POLY_NTTP_AUTO 0 +#endif + +namespace folly { +/// \cond +namespace detail { +template <class I> +struct PolyRoot; + +using RRef_ = MetaQuoteTrait<std::add_rvalue_reference>; +using LRef_ = MetaQuoteTrait<std::add_lvalue_reference>; + +template <typename T> +struct XRef_ : Type<MetaQuoteTrait<Type>> {}; +template <typename T> +using XRef = _t<XRef_<T>>; +template <typename T> +struct XRef_<T&&> : Type<MetaCompose<RRef_, XRef<T>>> {}; +template <typename T> +struct XRef_<T&> : Type<MetaCompose<LRef_, XRef<T>>> {}; +template <typename T> +struct XRef_<T const> : Type<MetaQuoteTrait<std::add_const>> {}; + +template <class A, class B> +using AddCvrefOf = MetaApply<XRef<B>, A>; +} // namespace detail +/// \endcond + +template <class I> +struct Poly; + +template <class T, class I> +detail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>&); + +template <class T, class I> +detail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const&); + +#if !FOLLY_POLY_NTTP_AUTO +#define FOLLY_AUTO class +template <class... Ts> +using PolyMembers = detail::TypeList<Ts...>; +#else +#define FOLLY_AUTO auto +template <auto...> +struct PolyMembers; +#endif + +/// \cond +namespace detail { +/* ***************************************************************************** + * IMPLEMENTATION NOTES + * + +Building the Interface +---------------------- + +Here is a high-level description of how Poly works. Users write an interface +such as: + + struct Mine { + template <class Base> + struct Interface { + int Exec() const { + return folly::poly_call<0>(*this); + } + } + template <class T> + using Members = folly::PolyMembers<&T::Exec>; + }; + +Then they instantiate Poly<Mine>, which will have an Exec member function +of the correct signature. The Exec member function comes from +Mine::Interface<PolyNode<Mine, PolyRoot<Mine>>>, from which Poly<Mine> inherits. +Here's what each piece means: + +- PolyRoot<I>: stores Data, which is a union of a void* (used when the Poly is + storing an object on the heap or a reference) and some aligned storage (used + when the Poly is storing an object in-situ). PolyRoot also stores a vtable + pointer for interface I, which is a pointer to a struct containing function + pointers. The function pointers are bound member functions (e.g., + SomeType::Exec). More on the vtable pointer and how it is generated below. + +- PolyNode: provides the hooks used by folly::poly_call to dispatch to the +correctly bound member function for this interface. In the context of an +interface I, folly::poly_call<K>(*this, args...) will: + 1. Fetch the vtable pointer from PolyRoot, + 2. Select the I portion of that vtable (which, in the case of interface + extension, may only be a part of the total vtable), + 3. Fetch the K-th function pointer from that vtable part, + 4. Call through that function pointer, passing Data (from PolyRoot) and any + additional arguments in the folly::poly_call<K> invocation. + +In the case of interface extension -- for instance, if interface Mine extended +interface Yours by inheriting from PolyExtends<Yours> -- then interface Mine +will have a list of base interfaces in a typelist called "Subsumptions". +Poly<Mine> will fold all the subsumed interfaces together, linearly inheriting +from them. To take the example of an interface Mine that extends Yours, +Poly<Mine> would inherit from this type: + + Mine::Interface< + PolyNode<Mine, + Your::Interface< + PolyNode<Your, PolyRoot<Mine>>>>> + +Through linear inheritance, Poly<Mine> ends up with the public member functions +of both interfaces, Mine and Yours. + +VTables +------- + +As mentioned above, PolyRoot<I> stores a vtable pointer for interface I. The +vtable is a struct whose members are function pointers. How are the types of +those function pointers computed from the interface? A dummy type is created, +Archetype<I>, in much the same way that Poly<I>'s base type is computed. Instead +of PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types +also provide hooks for folly::poly_call, but they are dummy hooks that do +nothing. (Actually, they call std::terminate; they will never be called.) Once +Archetype<I> has been constructed, it is a concrete type that has all the +member functions of the interface and its subsumed interfaces. That type is +passed to Mine::Members, which takes the address of Archetype<I>::Exec and +inspects the resulting member function type. This is done for each member in the +interface. From a list of [member] function pointers, it is a simple matter of +metaprogramming to build a struct of function pointers. std::tuple is used for +this. + +An extra field is added to the tuple for a function that handles all of the +"special" operations: destruction, copying, moving, getting the type +information, getting the address of the stored object, and fetching a fixed-up +vtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc). + +Subsumed interfaces are handled by having VTable<IDerived> inherit from +BasePtr<IBase>, where BasePtr<IBase> has only one member of type +VTable<IBase> const*. + +Now that the type of VTable<I> is computed, how are the fields populated? +Poly<I> default-constructs to an empty state. Its vtable pointer points to a +vtable whose fields are initialized with the addresses of functions that do +nothing but throw a BadPolyAccess exception. That way, if you call a member +function on an empty Poly, you get an exception. The function pointer +corresponding to the "special" operations points to a no-op function; copying, +moving and destroying an empty Poly does nothing. + +On the other hand, when you pass an object of type T satisfying interface I to +Poly<I>'s constructor or assignment operator, a vtable for {I,T} is reified by +passing type T to I::Members, thereby creating a list of bindings for T's member +functions. The address of this vtable gets stored in the PolyRoot<I> subobject, +imbuing the Poly object with the behaviors of type T. The T object itself gets +stored either on the heap or in the aligned storage within the Poly object +itself, depending on the size of T and whether or not it has a noexcept move +constructor. +*/ + +template <class T, template <class...> class U> +struct IsInstanceOf : std::false_type {}; + +template <class... Ts, template <class...> class U> +struct IsInstanceOf<U<Ts...>, U> : std::true_type {}; + +template <class Then> +decltype(auto) if_constexpr(std::true_type, Then then) { + return then(Identity{}); +} + +template <class Then> +void if_constexpr(std::false_type, Then) {} + +template <class Then, class Else> +decltype(auto) if_constexpr(std::true_type, Then then, Else) { + return then(Identity{}); +} + +template <class Then, class Else> +decltype(auto) if_constexpr(std::false_type, Then, Else else_) { + return else_(Identity{}); +} + +enum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr }; + +enum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue }; + +struct Data; + +template <class I> +struct PolyVal; + +template <class I> +struct PolyRef; + +struct PolyAccess; + +template <class T> +using IsPoly = IsInstanceOf<remove_cvref_t<T>, Poly>; + +// Given an interface I and a concrete type T that satisfies the interface +// I, create a list of member function bindings from members of T to members +// of I. +template <class I, class T> +using MembersOf = typename I::template Members<remove_cvref_t<T>>; + +// Given an interface I and a base type T, create a type that implements +// the interface I in terms of the capabilities of T. +template <class I, class T> +using InterfaceOf = typename I::template Interface<T>; + +#if !FOLLY_POLY_NTTP_AUTO +template <class T, T V> +using Member = std::integral_constant<T, V>; + +template <class M> +using MemberType = typename M::value_type; + +template <class M> +inline constexpr MemberType<M> memberValue() noexcept { + return M::value; +} + +template <class... Ts> +struct MakeMembers { + template <Ts... Vs> + using Members = PolyMembers<Member<Ts, Vs>...>; +}; + +template <class... Ts> +MakeMembers<Ts...> deduceMembers(Ts...); + +template <class Member, class = MemberType<Member>> +struct MemberDef; + +template <class Member, class R, class D, class... As> +struct MemberDef<Member, R (D::*)(As...)> { + static R value(D& d, As... as) { + return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...); + } +}; + +template <class Member, class R, class D, class... As> +struct MemberDef<Member, R (D::*)(As...) const> { + static R value(D const& d, As... as) { + return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...); + } +}; + +#else +template <auto M> +using MemberType = decltype(M); + +template <auto M> +inline constexpr MemberType<M> memberValue() noexcept { + return M; +} +#endif + +struct PolyBase {}; + +template <class I, class = void> +struct SubsumptionsOf_ { + using type = TypeList<>; +}; + +template <class I> +using InclusiveSubsumptionsOf = TypePushFront<_t<SubsumptionsOf_<I>>, I>; + +template <class I> +struct SubsumptionsOf_<I, void_t<typename I::Subsumptions>> { + using type = TypeJoin<TypeTransform< + typename I::Subsumptions, + MetaQuote<InclusiveSubsumptionsOf>>>; +}; + +template <class I> +using SubsumptionsOf = TypeReverseUnique<_t<SubsumptionsOf_<I>>>; + +struct Bottom { + template <class T> + [[noreturn]] /* implicit */ operator T &&() const { + std::terminate(); + } +}; + +using ArchetypeNode = MetaQuote<InterfaceOf>; + +template <class I> +struct ArchetypeRoot; + +template <class I> +using Archetype = + TypeFold<InclusiveSubsumptionsOf<I>, ArchetypeRoot<I>, ArchetypeNode>; + +struct ArchetypeBase : Bottom { + ArchetypeBase() = default; + template <class T> + /* implicit */ ArchetypeBase(T&&); + template <std::size_t, class... As> + [[noreturn]] Bottom _polyCall_(As&&...) const { + std::terminate(); + } + + friend bool operator==(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator!=(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator<(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator<=(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator>(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator>=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator++(ArchetypeBase const&); + friend Bottom operator++(ArchetypeBase const&, int); + friend Bottom operator--(ArchetypeBase const&); + friend Bottom operator--(ArchetypeBase const&, int); + friend Bottom operator+(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator+=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator-(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator-=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator*(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator*=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator/(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator/=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator%(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator%=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator<<(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator<<=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator>>(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator>>=(ArchetypeBase const&, ArchetypeBase const&); +}; + +template <class I> +struct ArchetypeRoot : ArchetypeBase { + template <class Node, class Tfx> + using _polySelf_ = Archetype<AddCvrefOf<MetaApply<Tfx, I>, Node>>; + using _polyInterface_ = I; +}; + +struct Data { + Data() = default; + // Suppress compiler-generated copy ops to not copy anything: + Data(Data const&) {} + Data& operator=(Data const&) { + return *this; + } + union { + void* pobj_ = nullptr; + std::aligned_storage_t<sizeof(double[2])> buff_; + }; +}; + +template <class U, class I> +using Arg = + If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value, + Poly<AddCvrefOf<I, U const&>>, + U>; + +template <class U, class I> +using Ret = + If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value, + AddCvrefOf<Poly<I>, U>, + U>; + +template <class Member, class I> +struct SignatureOf_; + +template <class R, class C, class... As, class I> +struct SignatureOf_<R (C::*)(As...), I> { + using type = Ret<R, I> (*)(Data&, Arg<As, I>...); +}; + +template <class R, class C, class... As, class I> +struct SignatureOf_<R (C::*)(As...) const, I> { + using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); +}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <class R, class C, class... As, class I> +struct SignatureOf_<R (C::*)(As...) noexcept, I> { + using type = std::add_pointer_t<Ret<R, I>(Data&, Arg<As, I>...) noexcept>; +}; + +template <class R, class C, class... As, class I> +struct SignatureOf_<R (C::*)(As...) const noexcept, I> { + using type = + std::add_pointer_t<Ret<R, I>(Data const&, Arg<As, I>...) noexcept>; +}; +#endif + +template <class R, class This, class... As, class I> +struct SignatureOf_<R (*)(This&, As...), I> { + using type = Ret<R, I> (*)(Data&, Arg<As, I>...); +}; + +template <class R, class This, class... As, class I> +struct SignatureOf_<R (*)(This const&, As...), I> { + using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); +}; + +template <FOLLY_AUTO Arch, class I> +using SignatureOf = _t<SignatureOf_<MemberType<Arch>, I>>; + +template <FOLLY_AUTO User, class I, class Sig = SignatureOf<User, I>> +struct ArgTypes_; + +template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args> +struct ArgTypes_<User, I, Ret (*)(Data, Args...)> { + using type = TypeList<Args...>; +}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args> +struct ArgTypes_<User, I, Ret (*)(Data, Args...) noexcept> { + using type = TypeList<Args...>; +}; +#endif + +template <FOLLY_AUTO User, class I> +using ArgTypes = _t<ArgTypes_<User, I>>; + +template <class R, class... Args> +using FnPtr = R (*)(Args...); + +struct ThrowThunk { + template <class R, class... Args> + constexpr /* implicit */ operator FnPtr<R, Args...>() const noexcept { + struct _ { + static R call(Args...) { + throw_exception<BadPolyAccess>(); + } + }; + return &_::call; + } +}; + +inline constexpr ThrowThunk throw_() noexcept { + return ThrowThunk{}; +} + +template <class T> +inline constexpr bool inSitu() noexcept { + return !std::is_reference<T>::value && + sizeof(std::decay_t<T>) <= sizeof(Data) && + std::is_nothrow_move_constructible<std::decay_t<T>>::value; +} + +template <class T> +T& get(Data& d) noexcept { + if (inSitu<T>()) { + return *(std::add_pointer_t<T>)static_cast<void*>(&d.buff_); + } else { + return *static_cast<std::add_pointer_t<T>>(d.pobj_); + } +} + +template <class T> +T const& get(Data const& d) noexcept { + if (inSitu<T>()) { + return *(std::add_pointer_t<T const>)static_cast<void const*>(&d.buff_); + } else { + return *static_cast<std::add_pointer_t<T const>>(d.pobj_); + } +} + +enum class State : short { eEmpty, eInSitu, eOnHeap }; + +template <class T> +struct IsPolyRef : std::false_type {}; + +template <class T> +struct IsPolyRef<Poly<T&>> : std::true_type {}; + +template <class Arg, class U> +decltype(auto) convert(U&& u) { + return detail::if_constexpr( + StrictConjunction< + IsPolyRef<remove_cvref_t<U>>, + Negation<std::is_convertible<U, Arg>>>(), + [&](auto id) -> decltype(auto) { + return poly_cast<remove_cvref_t<Arg>>(id(u).get()); + }, + [&](auto id) -> U&& { return static_cast<U&&>(id(u)); }); +} + +template <class Fun> +struct IsConstMember : std::false_type {}; + +template <class R, class C, class... As> +struct IsConstMember<R (C::*)(As...) const> : std::true_type {}; + +template <class R, class C, class... As> +struct IsConstMember<R (*)(C const&, As...)> : std::true_type {}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template <class R, class C, class... As> +struct IsConstMember<R (C::*)(As...) const noexcept> : std::true_type {}; + +template <class R, class C, class... As> +struct IsConstMember<R (*)(C const&, As...) noexcept> : std::true_type {}; +#endif + +template < + class T, + FOLLY_AUTO User, + class I, + class = ArgTypes<User, I>, + class = Bool<true>> +struct ThunkFn { + template <class R, class D, class... As> + constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept { + return nullptr; + } +}; + +template <class T, FOLLY_AUTO User, class I, class... Args> +struct ThunkFn< + T, + User, + I, + TypeList<Args...>, + Bool< + !std::is_const<std::remove_reference_t<T>>::value || + IsConstMember<MemberType<User>>::value>> { + template <class R, class D, class... As> + constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept { + struct _ { + static R call(D& d, As... as) { + return folly::invoke( + memberValue<User>(), + get<T>(d), + convert<Args>(static_cast<As&&>(as))...); + } + }; + return &_::call; + } +}; + +template < + class I, + class = MembersOf<I, Archetype<I>>, + class = SubsumptionsOf<I>> +struct VTable; + +template <class T, FOLLY_AUTO User, class I> +inline constexpr ThunkFn<T, User, I> thunk() noexcept { + return ThunkFn<T, User, I>{}; +} + +template <class I> +constexpr VTable<I> const* vtable() noexcept { + return &StaticConst<VTable<I>>::value; +} + +template <class I, class T> +struct VTableFor : VTable<I> { + constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {} +}; + +template <class I, class T> +constexpr VTable<I> const* vtableFor() noexcept { + return &StaticConst<VTableFor<I, T>>::value; +} + +template <class I, class T> +constexpr void* vtableForRef(RefType ref) { + switch (ref) { + case RefType::eRvalue: + return const_cast<VTable<I>*>(vtableFor<I, T&&>()); + case RefType::eLvalue: + return const_cast<VTable<I>*>(vtableFor<I, T&>()); + case RefType::eConstLvalue: + return const_cast<VTable<I>*>(vtableFor<I, T const&>()); + } + return nullptr; +} + +template < + class I, + class T, + std::enable_if_t<std::is_reference<T>::value, int> = 0> +void* execOnHeap(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + break; + case Op::eMove: + case Op::eCopy: + static_cast<Data*>(to)->pobj_ = from->pobj_; + break; + case Op::eType: + return const_cast<void*>(static_cast<void const*>(&typeid(T))); + case Op::eAddr: + if (*static_cast<std::type_info const*>(to) == typeid(T)) { + return from->pobj_; + } + throw_exception<BadPolyCast>(); + case Op::eRefr: + return vtableForRef<I, remove_cvref_t<T>>( + static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); + } + return nullptr; +} + +template < + class I, + class T, + std::enable_if_t<Negation<std::is_reference<T>>::value, int> = 0> +void* execOnHeap(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + delete &get<T>(*from); + break; + case Op::eMove: + static_cast<Data*>(to)->pobj_ = std::exchange(from->pobj_, nullptr); + break; + case Op::eCopy: + detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) { + static_cast<Data*>(to)->pobj_ = new T(id(get<T>(*from))); + }); + break; + case Op::eType: + return const_cast<void*>(static_cast<void const*>(&typeid(T))); + case Op::eAddr: + if (*static_cast<std::type_info const*>(to) == typeid(T)) { + return from->pobj_; + } + throw_exception<BadPolyCast>(); + case Op::eRefr: + return vtableForRef<I, remove_cvref_t<T>>( + static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); + } + return nullptr; +} + +template <class I, class T> +void* execInSitu(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + get<T>(*from).~T(); + break; + case Op::eMove: + ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_)) + T(std::move(get<T>(*from))); + get<T>(*from).~T(); + break; + case Op::eCopy: + detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) { + ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_)) + T(id(get<T>(*from))); + }); + break; + case Op::eType: + return const_cast<void*>(static_cast<void const*>(&typeid(T))); + case Op::eAddr: + if (*static_cast<std::type_info const*>(to) == typeid(T)) { + return &from->buff_; + } + throw_exception<BadPolyCast>(); + case Op::eRefr: + return vtableForRef<I, remove_cvref_t<T>>( + static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); + } + return nullptr; +} + +inline void* noopExec(Op op, Data*, void*) { + if (op == Op::eAddr) + throw_exception<BadPolyAccess>(); + return const_cast<void*>(static_cast<void const*>(&typeid(void))); +} + +template <class I> +struct BasePtr { + VTable<I> const* vptr_; +}; + +template <class I, class T> +constexpr void* (*getOpsImpl(std::true_type) noexcept)(Op, Data*, void*) { + return &execInSitu<I, T>; +} + +template <class I, class T> +constexpr void* (*getOpsImpl(std::false_type) noexcept)(Op, Data*, void*) { + return &execOnHeap<I, T>; +} + +template <class I, class T> +constexpr void* (*getOps() noexcept)(Op, Data*, void*) { + return getOpsImpl<I, T>(std::integral_constant<bool, inSitu<T>()>{}); +} + +template <class I, FOLLY_AUTO... Arch, class... S> +struct VTable<I, PolyMembers<Arch...>, TypeList<S...>> + : BasePtr<S>..., std::tuple<SignatureOf<Arch, I>...> { + private: + template <class T, FOLLY_AUTO... User> + constexpr VTable(Type<T>, PolyMembers<User...>) noexcept + : BasePtr<S>{vtableFor<S, T>()}..., + std::tuple<SignatureOf<Arch, I>...>{thunk<T, User, I>()...}, + state_{inSitu<T>() ? State::eInSitu : State::eOnHeap}, + ops_{getOps<I, T>()} {} + + public: + constexpr VTable() noexcept + : BasePtr<S>{vtable<S>()}..., + std::tuple<SignatureOf<Arch, I>...>{ + static_cast<SignatureOf<Arch, I>>(throw_())...}, + state_{State::eEmpty}, + ops_{&noopExec} {} + + template <class T> + explicit constexpr VTable(Type<T>) noexcept + : VTable{Type<T>{}, MembersOf<I, T>{}} {} + + State state_; + void* (*ops_)(Op, Data*, void*); +}; + +template <class I> +constexpr VTable<I> const& select(VTable<_t<Type<I>>> const& vtbl) noexcept { + return vtbl; +} + +template <class I> +constexpr VTable<I> const& select(BasePtr<_t<Type<I>>> const& base) noexcept { + return *base.vptr_; +} + +struct PolyAccess { + template <std::size_t N, typename This, typename... As> + static auto call(This&& _this, As&&... args) + -> decltype(static_cast<This&&>(_this).template _polyCall_<N>( + static_cast<As&&>(args)...)) { + static_assert( + !IsInstanceOf<std::decay_t<This>, Poly>::value, + "When passing a Poly<> object to call(), you must explicitly " + "say which Interface to dispatch to, as in " + "call<0, MyInterface>(self, args...)"); + return static_cast<This&&>(_this).template _polyCall_<N>( + static_cast<As&&>(args)...); + } + + template <class Poly> + using Iface = typename remove_cvref_t<Poly>::_polyInterface_; + + template <class Node, class Tfx = MetaIdentity> + static typename remove_cvref_t<Node>::template _polySelf_<Node, Tfx> self_(); + + template <class T, class Poly, class I = Iface<Poly>> + static decltype(auto) cast(Poly&& _this) { + using Ret = AddCvrefOf<AddCvrefOf<T, I>, Poly&&>; + return static_cast<Ret>( + *static_cast<std::add_pointer_t<Ret>>(_this.vptr_->ops_( + Op::eAddr, + const_cast<Data*>(static_cast<Data const*>(&_this)), + const_cast<void*>(static_cast<void const*>(&typeid(T)))))); + } + + template <class Poly> + static decltype(auto) root(Poly&& _this) noexcept { + return static_cast<Poly&&>(_this)._polyRoot_(); + } + + template <class I> + static std::type_info const& type(PolyRoot<I> const& _this) noexcept { + return *static_cast<std::type_info const*>( + _this.vptr_->ops_(Op::eType, nullptr, nullptr)); + } + + template <class I> + static VTable<remove_cvref_t<I>> const* vtable( + PolyRoot<I> const& _this) noexcept { + return _this.vptr_; + } + + template <class I> + static Data* data(PolyRoot<I>& _this) noexcept { + return &_this; + } + + template <class I> + static Data const* data(PolyRoot<I> const& _this) noexcept { + return &_this; + } + + template <class I> + static Poly<I&&> move(PolyRoot<I&> const& _this) noexcept { + return Poly<I&&>{_this, Type<I&>{}}; + } + + template <class I> + static Poly<I const&> move(PolyRoot<I const&> const& _this) noexcept { + return Poly<I const&>{_this, Type<I const&>{}}; + } +}; + +template <class I, class Tail> +struct PolyNode : Tail { + private: + friend PolyAccess; + using Tail::Tail; + + template <std::size_t K, typename... As> + decltype(auto) _polyCall_(As&&... as) { + return std::get<K>(select<I>(*PolyAccess::vtable(*this)))( + *PolyAccess::data(*this), static_cast<As&&>(as)...); + } + template <std::size_t K, typename... As> + decltype(auto) _polyCall_(As&&... as) const { + return std::get<K>(select<I>(*PolyAccess::vtable(*this)))( + *PolyAccess::data(*this), static_cast<As&&>(as)...); + } +}; + +struct MakePolyNode { + template <class I, class State> + using apply = InterfaceOf<I, PolyNode<I, State>>; +}; + +template <class I> +struct PolyRoot : private PolyBase, private Data { + friend PolyAccess; + friend Poly<I>; + friend PolyVal<I>; + friend PolyRef<I>; + template <class Node, class Tfx> + using _polySelf_ = Poly<AddCvrefOf<MetaApply<Tfx, I>, Node>>; + using _polyInterface_ = I; + + private: + PolyRoot& _polyRoot_() noexcept { + return *this; + } + PolyRoot const& _polyRoot_() const noexcept { + return *this; + } + VTable<std::decay_t<I>> const* vptr_ = vtable<std::decay_t<I>>(); +}; + +template <class I> +using PolyImpl = TypeFold< + InclusiveSubsumptionsOf<remove_cvref_t<I>>, + PolyRoot<I>, + MakePolyNode>; + +// A const-qualified function type means the user is trying to disambiguate +// a member function pointer. +template <class Fun> // Fun = R(As...) const +struct Sig { + template <class T> + constexpr Fun T::*operator()(Fun T::*t) const /* nolint */ volatile noexcept { + return t; + } + template <class F, class T> + constexpr F T::*operator()(F T::*t) const /* nolint */ volatile noexcept { + return t; + } +}; + +// A functon type with no arguments means the user is trying to disambiguate +// a member function pointer. +template <class R> +struct Sig<R()> : Sig<R() const> { + using Fun = R(); + using Sig<R() const>::operator(); + + template <class T> + constexpr Fun T::*operator()(Fun T::*t) const noexcept { + return t; + } +}; + +template <class R, class... As> +struct SigImpl : Sig<R(As...) const> { + using Fun = R(As...); + using Sig<R(As...) const>::operator(); + + template <class T> + constexpr Fun T::*operator()(Fun T::*t) const noexcept { + return t; + } + constexpr Fun* operator()(Fun* t) const noexcept { + return t; + } + template <class F> + constexpr F* operator()(F* t) const noexcept { + return t; + } +}; + +// The user could be trying to disambiguate either a member or a free function. +template <class R, class... As> +struct Sig<R(As...)> : SigImpl<R, As...> {}; + +// This case is like the one above, except we want to add an overload that +// handles the case of a free function where the first argument is more +// const-qualified than the user explicitly specified. +template <class R, class A, class... As> +struct Sig<R(A&, As...)> : SigImpl<R, A&, As...> { + using CCFun = R(A const&, As...); + using SigImpl<R, A&, As...>::operator(); + + constexpr CCFun* operator()(CCFun* t) const /* nolint */ volatile noexcept { + return t; + } +}; + +template <class T, class I, class = void> +struct ModelsInterface2_ : std::false_type {}; + +template <class T, class I> +struct ModelsInterface2_< + T, + I, + void_t< + std::enable_if_t< + std::is_constructible<AddCvrefOf<std::decay_t<T>, I>, T>::value>, + MembersOf<std::decay_t<I>, std::decay_t<T>>>> : std::true_type {}; + +template <class T, class I, class = void> +struct ModelsInterface_ : std::false_type {}; + +template <class T, class I> +struct ModelsInterface_< + T, + I, + std::enable_if_t< + Negation<std::is_base_of<PolyBase, std::decay_t<T>>>::value>> + : ModelsInterface2_<T, I> {}; + +template <class T, class I> +struct ModelsInterface : ModelsInterface_<T, I> {}; + +template <class I1, class I2> +struct ValueCompatible : std::is_base_of<I1, I2> {}; + +// This prevents PolyRef's converting constructors and assignment operators +// from being considered as copy constructors and assignment operators: +template <class I1> +struct ValueCompatible<I1, I1> : std::false_type {}; + +template <class I1, class I2, class I2Ref> +struct ReferenceCompatible : std::is_constructible<I1, I2Ref> {}; + +// This prevents PolyRef's converting constructors and assignment operators +// from being considered as copy constructors and assignment operators: +template <class I1, class I2Ref> +struct ReferenceCompatible<I1, I1, I2Ref> : std::false_type {}; + +} // namespace detail +/// \endcond +} // namespace folly + +#undef FOLLY_AUTO diff --git a/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.cpp b/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.cpp new file mode 100644 index 000000000..fba8341c6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/RangeCommon.h> + +#include <bitset> + +#include <folly/container/SparseByteSet.h> + +namespace folly { + +namespace detail { + +size_t qfind_first_byte_of_bitset( + const StringPieceLite haystack, + const StringPieceLite needles) { + std::bitset<256> s; + for (auto needle : needles) { + s[(uint8_t)needle] = true; + } + for (size_t index = 0; index < haystack.size(); ++index) { + if (s[(uint8_t)haystack[index]]) { + return index; + } + } + return std::string::npos; +} + +size_t qfind_first_byte_of_byteset( + const StringPieceLite haystack, + const StringPieceLite needles) { + SparseByteSet s; + for (auto needle : needles) { + s.add(uint8_t(needle)); + } + for (size_t index = 0; index < haystack.size(); ++index) { + if (s.contains(uint8_t(haystack[index]))) { + return index; + } + } + return std::string::npos; +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.h b/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.h new file mode 100644 index 000000000..9d0d2944c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/RangeCommon.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <cassert> +#include <string> + +#include <folly/Likely.h> + +namespace folly { + +namespace detail { + +/*** + * The qfind_first_byte_of_* functions are declared here, before Range.h, so + * they cannot take StringPiece values. But they're there to operate on + * StringPiece values. Dependency cycles: fun. + * + * StringPieceLite is here to break that dependency cycle. + */ +class StringPieceLite { + public: + StringPieceLite(const char* b, const char* e) : b_(b), e_(e) {} + template <typename Range> + /* implicit */ StringPieceLite(const Range& r) + : StringPieceLite(r.data(), r.data() + r.size()) {} + const char* data() const { + return b_; + } + const char* begin() const { + return b_; + } + const char* end() const { + return e_; + } + size_t size() const { + return size_t(e_ - b_); + } + bool empty() const { + return size() == 0; + } + const char& operator[](size_t i) const { + assert(size() > i); + return b_[i]; + } + template <typename Range> + explicit operator Range() const { + return Range(begin(), end()); + } + + private: + const char* b_; + const char* e_; +}; + +inline size_t qfind_first_byte_of_std( + const StringPieceLite haystack, + const StringPieceLite needles) { + auto ret = std::find_first_of( + haystack.begin(), + haystack.end(), + needles.begin(), + needles.end(), + [](char a, char b) { return a == b; }); + return ret == haystack.end() ? std::string::npos : ret - haystack.begin(); +} + +size_t qfind_first_byte_of_bitset( + const StringPieceLite haystack, + const StringPieceLite needles); + +size_t qfind_first_byte_of_byteset( + const StringPieceLite haystack, + const StringPieceLite needles); + +inline size_t qfind_first_byte_of_nosse( + const StringPieceLite haystack, + const StringPieceLite needles) { + if (UNLIKELY(needles.empty() || haystack.empty())) { + return std::string::npos; + } + // The thresholds below were empirically determined by benchmarking. + // This is not an exact science since it depends on the CPU, the size of + // needles, and the size of haystack. + if ((needles.size() >= 4 && haystack.size() <= 10) || + (needles.size() >= 16 && haystack.size() <= 64) || needles.size() >= 32) { + return qfind_first_byte_of_byteset(haystack, needles); + } + return qfind_first_byte_of_std(haystack, needles); +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.cpp b/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.cpp new file mode 100644 index 000000000..fda9c2b72 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/RangeSse42.h> + +#include <cassert> + +#include <folly/Portability.h> + +// Essentially, two versions of this file: one with an SSE42 implementation +// and one with a fallback implementation. We determine which version to use by +// testing for the presence of the required headers. +// +// TODO: Maybe this should be done by the build system.... +#if !FOLLY_SSE_PREREQ(4, 2) +namespace folly { +namespace detail { +size_t qfind_first_byte_of_sse42( + const StringPieceLite haystack, + const StringPieceLite needles) { + return qfind_first_byte_of_nosse(haystack, needles); +} +} // namespace detail +} // namespace folly +#else +#include <cstdint> +#include <limits> +#include <string> + +#include <emmintrin.h> +#include <nmmintrin.h> +#include <smmintrin.h> + +#include <folly/Likely.h> +#include <folly/detail/Sse.h> + +namespace folly { +namespace detail { + +// It's okay if pages are bigger than this (as powers of two), but they should +// not be smaller. +static constexpr size_t kMinPageSize = 4096; +static_assert( + kMinPageSize >= 16, + "kMinPageSize must be at least SSE register size"); + +template <typename T> +static inline uintptr_t page_for(T* addr) { + return reinterpret_cast<uintptr_t>(addr) / kMinPageSize; +} + +static inline size_t nextAlignedIndex(const char* arr) { + auto firstPossible = reinterpret_cast<uintptr_t>(arr) + 1; + return 1 + // add 1 because the index starts at 'arr' + ((firstPossible + 15) & ~0xF) // round up to next multiple of 16 + - firstPossible; +} + +// helper method for case where needles.size() <= 16 +size_t qfind_first_byte_of_needles16( + const StringPieceLite haystack, + const StringPieceLite needles) { + assert(haystack.size() > 0u); + assert(needles.size() > 0u); + assert(needles.size() <= 16u); + if ((needles.size() <= 2 && haystack.size() >= 256) || + // must bail if we can't even SSE-load a single segment of haystack + (haystack.size() < 16 && + page_for(haystack.end() - 1) != page_for(haystack.data() + 15)) || + // can't load needles into SSE register if it could cross page boundary + page_for(needles.end() - 1) != page_for(needles.data() + 15)) { + return detail::qfind_first_byte_of_nosse(haystack, needles); + } + + auto arr2 = _mm_loadu_si128_unchecked( + reinterpret_cast<const __m128i*>(needles.data())); + // do an unaligned load for first block of haystack + auto arr1 = _mm_loadu_si128_unchecked( + reinterpret_cast<const __m128i*>(haystack.data())); + auto index = + _mm_cmpestri(arr2, int(needles.size()), arr1, int(haystack.size()), 0); + if (index < 16) { + return size_t(index); + } + + // Now, we can do aligned loads hereafter... + size_t i = nextAlignedIndex(haystack.data()); + for (; i < haystack.size(); i += 16) { + arr1 = _mm_load_si128_unchecked( + reinterpret_cast<const __m128i*>(haystack.data() + i)); + index = _mm_cmpestri( + arr2, int(needles.size()), arr1, int(haystack.size() - i), 0); + if (index < 16) { + return i + index; + } + } + return std::string::npos; +} + +// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first +// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned. +// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the +// block. +template <bool HAYSTACK_ALIGNED> +size_t scanHaystackBlock( + const StringPieceLite haystack, + const StringPieceLite needles, + uint64_t blockStartIdx) { + assert(needles.size() > 16u); // should handled by *needles16() method + assert( + blockStartIdx + 16 <= haystack.size() || + (page_for(haystack.data() + blockStartIdx) == + page_for(haystack.data() + blockStartIdx + 15))); + + __m128i arr1; + if (HAYSTACK_ALIGNED) { + arr1 = _mm_load_si128_unchecked( + reinterpret_cast<const __m128i*>(haystack.data() + blockStartIdx)); + } else { + arr1 = _mm_loadu_si128_unchecked( + reinterpret_cast<const __m128i*>(haystack.data() + blockStartIdx)); + } + + // This load is safe because needles.size() >= 16 + auto arr2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(needles.data())); + auto b = + _mm_cmpestri(arr2, 16, arr1, int(haystack.size() - blockStartIdx), 0); + + size_t j = nextAlignedIndex(needles.data()); + for (; j < needles.size(); j += 16) { + arr2 = _mm_load_si128_unchecked( + reinterpret_cast<const __m128i*>(needles.data() + j)); + + auto index = _mm_cmpestri( + arr2, + int(needles.size() - j), + arr1, + int(haystack.size() - blockStartIdx), + 0); + b = std::min(index, b); + } + + if (b < 16) { + return blockStartIdx + b; + } + return std::string::npos; +} + +size_t qfind_first_byte_of_sse42( + const StringPieceLite haystack, + const StringPieceLite needles); + +size_t qfind_first_byte_of_sse42( + const StringPieceLite haystack, + const StringPieceLite needles) { + if (UNLIKELY(needles.empty() || haystack.empty())) { + return std::string::npos; + } else if (needles.size() <= 16) { + // we can save some unnecessary load instructions by optimizing for + // the common case of needles.size() <= 16 + return qfind_first_byte_of_needles16(haystack, needles); + } + + if (haystack.size() < 16 && + page_for(haystack.end() - 1) != page_for(haystack.data() + 16)) { + // We can't safely SSE-load haystack. Use a different approach. + if (haystack.size() <= 2) { + return qfind_first_byte_of_std(haystack, needles); + } + return qfind_first_byte_of_byteset(haystack, needles); + } + + auto ret = scanHaystackBlock<false>(haystack, needles, 0); + if (ret != std::string::npos) { + return ret; + } + + size_t i = nextAlignedIndex(haystack.data()); + for (; i < haystack.size(); i += 16) { + ret = scanHaystackBlock<true>(haystack, needles, i); + if (ret != std::string::npos) { + return ret; + } + } + + return std::string::npos; +} +} // namespace detail +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.h b/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.h new file mode 100644 index 000000000..3f7c7e519 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/RangeSse42.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> + +#include <folly/detail/RangeCommon.h> + +namespace folly { + +namespace detail { + +size_t qfind_first_byte_of_sse42( + const StringPieceLite haystack, + const StringPieceLite needles); +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Singleton.h b/ios/Pods/Flipper-Folly/folly/detail/Singleton.h new file mode 100644 index 000000000..14bdec38f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Singleton.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +#include <folly/Traits.h> + +namespace folly { +namespace detail { + +struct DefaultTag {}; + +template <typename T> +struct DefaultMake { + struct Heap { + std::unique_ptr<T> ptr{std::make_unique<T>()}; + /* implicit */ operator T&() { + return *ptr; + } + }; + + using is_returnable = StrictDisjunction< + bool_constant<__cplusplus >= 201703ULL>, + std::is_copy_constructible<T>, + std::is_move_constructible<T>>; + using type = std::conditional_t<is_returnable::value, T, Heap>; + + type operator()() const { + return type(); + } +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.cpp b/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.cpp new file mode 100644 index 000000000..24074cef6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/SingletonStackTrace.h> + +#include <folly/portability/Config.h> + +#if FOLLY_USE_SYMBOLIZER +#include <folly/experimental/symbolizer/Symbolizer.h> // @manual +#endif + +namespace folly { +namespace detail { + +std::string getSingletonStackTrace() { +#if FOLLY_USE_SYMBOLIZER + + // Get and symbolize stack trace + constexpr size_t kMaxStackTraceDepth = 100; + symbolizer::FrameArray<kMaxStackTraceDepth> addresses; + + if (!getStackTraceSafe(addresses)) { + return ""; + } else { + constexpr size_t kDefaultCapacity = 500; + symbolizer::ElfCache elfCache(kDefaultCapacity); + + symbolizer::Symbolizer symbolizer(&elfCache); + symbolizer.symbolize(addresses); + + symbolizer::StringSymbolizePrinter printer; + printer.println(addresses); + return printer.str(); + } + +#else + + return ""; + +#endif +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.h b/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.h new file mode 100644 index 000000000..0f7f4ccbe --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/SingletonStackTrace.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <string> + +#include <folly/CPortability.h> + +namespace folly { +namespace detail { + +// empty-string indicates stack-trace functionality is not available +std::string getSingletonStackTrace(); + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/SlowFingerprint.h b/ios/Pods/Flipper-Folly/folly/detail/SlowFingerprint.h new file mode 100644 index 000000000..8467fdba8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/SlowFingerprint.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Fingerprint.h> +#include <folly/Range.h> +#include <folly/detail/FingerprintPolynomial.h> + +namespace folly { +namespace detail { + +/** + * Slow, one-bit-at-a-time implementation of the Rabin fingerprint. + * + * This is useful as a reference implementation to test the Broder optimization + * for correctness in the unittest; it's probably too slow for any real use. + */ +template <int BITS> +class SlowFingerprint { + public: + SlowFingerprint() : poly_(FingerprintTable<BITS>::poly) { + // Use the same starting value as Fingerprint, (1 << (BITS-1)) + fp_.addXk(BITS - 1); + } + + SlowFingerprint& update8(uint8_t v) { + updateLSB(v, 8); + return *this; + } + + SlowFingerprint& update32(uint32_t v) { + updateLSB(v, 32); + return *this; + } + + SlowFingerprint& update64(uint64_t v) { + updateLSB(v, 64); + return *this; + } + + SlowFingerprint& update(const folly::StringPiece str) { + const char* p = str.start(); + for (int i = str.size(); i != 0; p++, i--) { + update8(static_cast<uint8_t>(*p)); + } + return *this; + } + + void write(uint64_t* out) const { + for (int i = 0; i < fp_.size(); ++i) { + out[i] = fp_.get(i); + } + } + + private: + void updateBit(bool bit) { + fp_.mulXmod(poly_); + if (bit) { + fp_.addXk(0); + } + } + + void updateLSB(uint64_t val, int bits) { + val <<= (64 - bits); + for (; bits != 0; --bits) { + updateBit(val & (1ULL << 63)); + val <<= 1; + } + } + + const FingerprintPolynomial<BITS - 1> poly_; + FingerprintPolynomial<BITS - 1> fp_; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.cpp b/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.cpp new file mode 100644 index 000000000..0db501c7a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/SocketFastOpen.h> + +#include <folly/portability/Sockets.h> + +#include <cerrno> +#include <cstdio> + +namespace folly { +namespace detail { + +#if FOLLY_ALLOW_TFO && defined(__linux__) + +// Sometimes these flags are not present in the headers, +// so define them if not present. +#if !defined(MSG_FASTOPEN) +#define MSG_FASTOPEN 0x20000000 +#endif + +#if !defined(TCP_FASTOPEN) +#define TCP_FASTOPEN 23 +#endif + +#if !defined(TCPI_OPT_SYN_DATA) +#define TCPI_OPT_SYN_DATA 32 +#endif + +ssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) { + flags |= MSG_FASTOPEN; + return netops::sendmsg(sockfd, msg, flags); +} + +int tfo_enable(NetworkSocket sockfd, size_t max_queue_size) { + return netops::setsockopt( + sockfd, SOL_TCP, TCP_FASTOPEN, &max_queue_size, sizeof(max_queue_size)); +} + +bool tfo_succeeded(NetworkSocket sockfd) { + // Call getsockopt to check if TFO was used. + struct tcp_info info; + socklen_t info_len = sizeof(info); + errno = 0; + if (netops::getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) != + 0) { + // errno is set from getsockopt + return false; + } + return info.tcpi_options & TCPI_OPT_SYN_DATA; +} + +#elif FOLLY_ALLOW_TFO && defined(__APPLE__) + +ssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) { + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = nullptr; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = (struct sockaddr*)msg->msg_name; + endpoints.sae_dstaddrlen = msg->msg_namelen; + int ret = connectx( + sockfd.toFd(), + &endpoints, + SAE_ASSOCID_ANY, + CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, + nullptr, + 0, + nullptr, + nullptr); + + if (ret != 0) { + return ret; + } + ret = netops::sendmsg(sockfd, msg, flags); + return ret; +} + +int tfo_enable(NetworkSocket sockfd, size_t max_queue_size) { + return netops::setsockopt( + sockfd, + IPPROTO_TCP, + TCP_FASTOPEN, + &max_queue_size, + sizeof(max_queue_size)); +} + +bool tfo_succeeded(NetworkSocket /* sockfd */) { + errno = EOPNOTSUPP; + return false; +} + +#else + +ssize_t tfo_sendmsg( + NetworkSocket /* sockfd */, + const struct msghdr* /* msg */, + int /* flags */) { + errno = EOPNOTSUPP; + return -1; +} + +int tfo_enable(NetworkSocket /* sockfd */, size_t /* max_queue_size */) { + errno = ENOPROTOOPT; + return -1; +} + +bool tfo_succeeded(NetworkSocket /* sockfd */) { + errno = EOPNOTSUPP; + return false; +} + +#endif +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.h b/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.h new file mode 100644 index 000000000..77a291423 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/SocketFastOpen.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/types.h> + +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Sockets.h> + +#if !defined(FOLLY_ALLOW_TFO) +#if defined(__linux__) || defined(__APPLE__) +// only allow for linux right now +#define FOLLY_ALLOW_TFO 1 +#endif +#endif + +namespace folly { +namespace detail { +/** + * tfo_sendto has the same semantics as sendmsg, but is used to + * send with TFO data. + */ +ssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags); + +/** + * Enable TFO on a listening socket. + */ +int tfo_enable(NetworkSocket sockfd, size_t max_queue_size); + +/** + * Check if TFO succeeded in being used. + */ +bool tfo_succeeded(NetworkSocket sockfd); +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Sse.cpp b/ios/Pods/Flipper-Folly/folly/detail/Sse.cpp new file mode 100644 index 000000000..bfd9b4da4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Sse.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/Sse.h> + +namespace folly { +namespace detail { + +#if FOLLY_SSE_PREREQ(2, 0) + +FOLLY_DISABLE_SANITIZERS __m128i _mm_loadu_si128_nosan(__m128i const* const p) { + return _mm_loadu_si128(p); +} + +FOLLY_DISABLE_SANITIZERS __m128i _mm_load_si128_nosan(__m128i const* const p) { + return _mm_load_si128(p); +} + +#endif + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/Sse.h b/ios/Pods/Flipper-Folly/folly/detail/Sse.h new file mode 100644 index 000000000..5b85221ba --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/Sse.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +#if FOLLY_SSE_PREREQ(2, 0) + +#include <emmintrin.h> + +#endif + +namespace folly { +namespace detail { + +#if FOLLY_SSE_PREREQ(2, 0) + +__m128i _mm_loadu_si128_nosan(__m128i const* const p); +FOLLY_ERASE __m128i _mm_loadu_si128_unchecked(__m128i const* const p) { + return kIsSanitize ? _mm_loadu_si128_nosan(p) : _mm_loadu_si128(p); +} + +__m128i _mm_load_si128_nosan(__m128i const* const p); +FOLLY_ERASE __m128i _mm_load_si128_unchecked(__m128i const* const p) { + return kIsSanitize ? _mm_load_si128_nosan(p) : _mm_load_si128(p); +} + +#endif + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.cpp b/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.cpp new file mode 100644 index 000000000..6627b226f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/StaticSingletonManager.h> + +#include <mutex> +#include <typeindex> +#include <unordered_map> + +namespace folly { +namespace detail { + +namespace { + +class StaticSingletonManagerWithRttiImpl { + public: + using Make = void*(); + using Cache = std::atomic<void*>; + + void* create(std::type_info const& key, Make& make, Cache& cache) { + auto const ptr = entry(key).get(make); + cache.store(ptr, std::memory_order_release); + return ptr; + } + + private: + struct Entry { + void* ptr{}; + std::mutex mutex; + + void* get(Make& make) { + std::lock_guard<std::mutex> lock(mutex); + return ptr ? ptr : (ptr = make()); + } + }; + + Entry& entry(std::type_info const& key) { + std::lock_guard<std::mutex> lock(mutex_); + auto& e = map_[key]; + return e ? *e : *(e = new Entry()); + } + + std::unordered_map<std::type_index, Entry*> map_; + std::mutex mutex_; +}; + +} // namespace + +void* StaticSingletonManagerWithRtti::create_(Arg& arg) { + // This Leaky Meyers Singleton must always live in the .cpp file. + static auto& instance = *new StaticSingletonManagerWithRttiImpl(); + return instance.create(*arg.key, arg.make, arg.cache); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.h b/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.h new file mode 100644 index 000000000..3e6ea3ef3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/StaticSingletonManager.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <typeinfo> + +#include <folly/CPortability.h> +#include <folly/Indestructible.h> +#include <folly/Likely.h> +#include <folly/detail/Singleton.h> +#include <folly/lang/TypeInfo.h> + +namespace folly { +namespace detail { + +// Does not support dynamic loading but works without rtti. +class StaticSingletonManagerSansRtti { + public: + template <typename T, typename Tag> + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() { + static std::atomic<T*> cache{}; + auto const pointer = cache.load(std::memory_order_acquire); + return FOLLY_LIKELY(!!pointer) ? *pointer : create_<T, Tag>(cache); + } + + private: + template <typename T, typename Tag> + FOLLY_EXPORT FOLLY_NOINLINE static T& create_(std::atomic<T*>& cache) { + static Indestructible<T> instance; + cache.store(&*instance, std::memory_order_release); + return *instance; + } +}; + +// This internal-use-only class is used to create all leaked Meyers singletons. +// It guarantees that only one instance of every such singleton will ever be +// created, even when requested from different compilation units linked +// dynamically. +// +// Supports dynamic loading but requires rtti. +class StaticSingletonManagerWithRtti { + public: + template <typename T, typename Tag> + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() { + // gcc and clang behave poorly if typeid is hidden behind a non-constexpr + // function, but typeid is not constexpr under msvc + static Arg arg{{nullptr}, FOLLY_TYPE_INFO_OF(tag_t<T, Tag>), make<T>}; + auto const v = arg.cache.load(std::memory_order_acquire); + auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg); + return *static_cast<T*>(p); + } + + private: + using Key = std::type_info; + using Make = void*(); + using Cache = std::atomic<void*>; + struct Arg { + Cache cache; // should be first field + Key const* key; + Make& make; + }; + + template <typename T> + static void* make() { + return new T(); + } + + template <bool Noexcept> + FOLLY_ERASE static void* create_(Arg& arg) noexcept(Noexcept) { + return create_(arg); + } + FOLLY_NOINLINE static void* create_(Arg& arg); +}; + +using StaticSingletonManager = std::conditional_t< + kHasRtti, + StaticSingletonManagerWithRtti, + StaticSingletonManagerSansRtti>; + +template <typename T, typename Tag> +FOLLY_ERASE T& createGlobal() { + return StaticSingletonManager::create<T, Tag>(); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.cpp b/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.cpp new file mode 100644 index 000000000..8756c1313 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/ThreadLocalDetail.h> +#include <folly/synchronization/CallOnce.h> + +#include <list> +#include <mutex> + +constexpr auto kSmallGrowthFactor = 1.1; +constexpr auto kBigGrowthFactor = 1.7; + +namespace folly { +namespace threadlocal_detail { + +void ThreadEntryNode::initIfZero(bool locked) { + if (UNLIKELY(!next)) { + if (LIKELY(locked)) { + parent->meta->pushBackLocked(parent, id); + } else { + parent->meta->pushBackUnlocked(parent, id); + } + } +} + +void ThreadEntryNode::push_back(ThreadEntry* head) { + // get the head prev and next nodes + ThreadEntryNode* hnode = &head->elements[id].node; + + // update current + next = head; + prev = hnode->prev; + + // hprev + ThreadEntryNode* hprev = &hnode->prev->elements[id].node; + hprev->next = parent; + hnode->prev = parent; +} + +void ThreadEntryNode::eraseZero() { + if (LIKELY(prev != nullptr)) { + // get the prev and next nodes + ThreadEntryNode* nprev = &prev->elements[id].node; + ThreadEntryNode* nnext = &next->elements[id].node; + + // update the prev and next + nnext->prev = prev; + nprev->next = next; + + // set the prev and next to nullptr + next = prev = nullptr; + } +} + +StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict) + : nextId_(1), threadEntry_(threadEntry), strict_(strict) { + int ret = pthread_key_create(&pthreadKey_, &onThreadExit); + checkPosixError(ret, "pthread_key_create failed"); + PthreadKeyUnregister::registerKey(pthreadKey_); +} + +ThreadEntryList* StaticMetaBase::getThreadEntryList() { +#ifdef FOLLY_TLD_USE_FOLLY_TLS + static FOLLY_TLS ThreadEntryList threadEntryListSingleton; + return &threadEntryListSingleton; +#else + class PthreadKey { + public: + PthreadKey() { + int ret = pthread_key_create(&pthreadKey_, nullptr); + checkPosixError(ret, "pthread_key_create failed"); + PthreadKeyUnregister::registerKey(pthreadKey_); + } + + FOLLY_ALWAYS_INLINE pthread_key_t get() const { + return pthreadKey_; + } + + private: + pthread_key_t pthreadKey_; + }; + + auto& instance = detail::createGlobal<PthreadKey, void>(); + + ThreadEntryList* threadEntryList = + static_cast<ThreadEntryList*>(pthread_getspecific(instance.get())); + + if (UNLIKELY(!threadEntryList)) { + threadEntryList = new ThreadEntryList(); + int ret = pthread_setspecific(instance.get(), threadEntryList); + checkPosixError(ret, "pthread_setspecific failed"); + } + + return threadEntryList; +#endif +} + +bool StaticMetaBase::dying() { + for (auto te = getThreadEntryList()->head; te; te = te->listNext) { + if (te->removed_) { + return true; + } + } + return false; +} + +void StaticMetaBase::onThreadExit(void* ptr) { + auto threadEntry = static_cast<ThreadEntry*>(ptr); + + { + auto& meta = *threadEntry->meta; + + // Make sure this ThreadEntry is available if ThreadLocal A is accessed in + // ThreadLocal B destructor. + pthread_setspecific(meta.pthreadKey_, threadEntry); + SharedMutex::ReadHolder rlock(nullptr); + if (meta.strict_) { + rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_); + } + { + std::lock_guard<std::mutex> g(meta.lock_); + // mark it as removed + threadEntry->removed_ = true; + auto elementsCapacity = threadEntry->getElementsCapacity(); + for (size_t i = 0u; i < elementsCapacity; ++i) { + threadEntry->elements[i].node.eraseZero(); + } + // No need to hold the lock any longer; the ThreadEntry is private to this + // thread now that it's been removed from meta. + } + // NOTE: User-provided deleter / object dtor itself may be using ThreadLocal + // with the same Tag, so dispose() calls below may (re)create some of the + // elements or even increase elementsCapacity, thus multiple cleanup rounds + // may be required. + for (bool shouldRun = true; shouldRun;) { + shouldRun = false; + auto elementsCapacity = threadEntry->getElementsCapacity(); + FOR_EACH_RANGE (i, 0, elementsCapacity) { + if (threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) { + threadEntry->elements[i].cleanup(); + shouldRun = true; + } + } + } + pthread_setspecific(meta.pthreadKey_, nullptr); + } + + auto threadEntryList = threadEntry->list; + DCHECK_GT(threadEntryList->count, 0u); + + --threadEntryList->count; + + if (threadEntryList->count) { + return; + } + + // dispose all the elements + for (bool shouldRunOuter = true; shouldRunOuter;) { + shouldRunOuter = false; + auto tmp = threadEntryList->head; + while (tmp) { + auto& meta = *tmp->meta; + pthread_setspecific(meta.pthreadKey_, tmp); + SharedMutex::ReadHolder rlock(nullptr); + if (meta.strict_) { + rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_); + } + for (bool shouldRunInner = true; shouldRunInner;) { + shouldRunInner = false; + auto elementsCapacity = tmp->getElementsCapacity(); + FOR_EACH_RANGE (i, 0, elementsCapacity) { + if (tmp->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) { + tmp->elements[i].cleanup(); + shouldRunInner = true; + shouldRunOuter = true; + } + } + } + pthread_setspecific(meta.pthreadKey_, nullptr); + tmp = tmp->listNext; + } + } + + // free the entry list + auto head = threadEntryList->head; + threadEntryList->head = nullptr; + while (head) { + auto tmp = head; + head = head->listNext; + if (tmp->elements) { + free(tmp->elements); + tmp->elements = nullptr; + tmp->setElementsCapacity(0); + } + +#ifndef FOLLY_TLD_USE_FOLLY_TLS + delete tmp; +#endif + } + +#ifndef FOLLY_TLD_USE_FOLLY_TLS + delete threadEntryList; +#endif +} + +uint32_t StaticMetaBase::elementsCapacity() const { + ThreadEntry* threadEntry = (*threadEntry_)(); + + return FOLLY_LIKELY(!!threadEntry) ? threadEntry->getElementsCapacity() : 0; +} + +uint32_t StaticMetaBase::allocate(EntryID* ent) { + uint32_t id; + auto& meta = *this; + std::lock_guard<std::mutex> g(meta.lock_); + + id = ent->value.load(); + if (id != kEntryIDInvalid) { + return id; + } + + if (!meta.freeIds_.empty()) { + id = meta.freeIds_.back(); + meta.freeIds_.pop_back(); + } else { + id = meta.nextId_++; + } + + uint32_t old_id = ent->value.exchange(id); + DCHECK_EQ(old_id, kEntryIDInvalid); + + reserveHeadUnlocked(id); + + return id; +} + +void StaticMetaBase::destroy(EntryID* ent) { + try { + auto& meta = *this; + + // Elements in other threads that use this id. + std::vector<ElementWrapper> elements; + + { + SharedMutex::WriteHolder wlock(nullptr); + if (meta.strict_) { + /* + * In strict mode, the logic guarantees per-thread instances are + * destroyed by the moment ThreadLocal<> dtor returns. + * In order to achieve that, we should wait until concurrent + * onThreadExit() calls (that might acquire ownership over per-thread + * instances in order to destroy them) are finished. + */ + wlock = SharedMutex::WriteHolder(meta.accessAllThreadsLock_); + } + + { + std::lock_guard<std::mutex> g(meta.lock_); + uint32_t id = ent->value.exchange(kEntryIDInvalid); + if (id == kEntryIDInvalid) { + return; + } + + auto& node = meta.head_.elements[id].node; + while (!node.empty()) { + auto* next = node.getNext(); + next->eraseZero(); + + ThreadEntry* e = next->parent; + auto elementsCapacity = e->getElementsCapacity(); + if (id < elementsCapacity && e->elements[id].ptr) { + elements.push_back(e->elements[id]); + + /* + * Writing another thread's ThreadEntry from here is fine; + * the only other potential reader is the owning thread -- + * from onThreadExit (which grabs the lock, so is properly + * synchronized with us) or from get(), which also grabs + * the lock if it needs to resize the elements vector. + * + * We can't conflict with reads for a get(id), because + * it's illegal to call get on a thread local that's + * destructing. + */ + e->elements[id].ptr = nullptr; + e->elements[id].deleter1 = nullptr; + e->elements[id].ownsDeleter = false; + } + } + meta.freeIds_.push_back(id); + } + } + // Delete elements outside the locks. + for (ElementWrapper& elem : elements) { + if (elem.dispose(TLPDestructionMode::ALL_THREADS)) { + elem.cleanup(); + } + } + } catch (...) { // Just in case we get a lock error or something anyway... + LOG(WARNING) << "Destructor discarding an exception that was thrown."; + } +} + +ElementWrapper* StaticMetaBase::reallocate( + ThreadEntry* threadEntry, + uint32_t idval, + size_t& newCapacity) { + size_t prevCapacity = threadEntry->getElementsCapacity(); + + // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent + // very slow start. + auto smallCapacity = static_cast<size_t>((idval + 5) * kSmallGrowthFactor); + auto bigCapacity = static_cast<size_t>((idval + 5) * kBigGrowthFactor); + + newCapacity = + (threadEntry->meta && + (bigCapacity <= threadEntry->meta->head_.getElementsCapacity())) + ? bigCapacity + : smallCapacity; + + assert(newCapacity > prevCapacity); + ElementWrapper* reallocated = nullptr; + + // Need to grow. Note that we can't call realloc, as elements is + // still linked in meta, so another thread might access invalid memory + // after realloc succeeds. We'll copy by hand and update our ThreadEntry + // under the lock. + if (usingJEMalloc()) { + bool success = false; + size_t newByteSize = nallocx(newCapacity * sizeof(ElementWrapper), 0); + + // Try to grow in place. + // + // Note that xallocx(MALLOCX_ZERO) will only zero newly allocated memory, + // even if a previous allocation allocated more than we requested. + // This is fine; we always use MALLOCX_ZERO with jemalloc and we + // always expand our allocation to the real size. + if (prevCapacity * sizeof(ElementWrapper) >= jemallocMinInPlaceExpandable) { + success = + (xallocx(threadEntry->elements, newByteSize, 0, MALLOCX_ZERO) == + newByteSize); + } + + // In-place growth failed. + if (!success) { + success = + ((reallocated = static_cast<ElementWrapper*>( + mallocx(newByteSize, MALLOCX_ZERO))) != nullptr); + } + + if (success) { + // Expand to real size + assert(newByteSize / sizeof(ElementWrapper) >= newCapacity); + newCapacity = newByteSize / sizeof(ElementWrapper); + } else { + throw std::bad_alloc(); + } + } else { // no jemalloc + // calloc() is simpler than malloc() followed by memset(), and + // potentially faster when dealing with a lot of memory, as it can get + // already-zeroed pages from the kernel. + reallocated = static_cast<ElementWrapper*>( + calloc(newCapacity, sizeof(ElementWrapper))); + if (!reallocated) { + throw std::bad_alloc(); + } + } + + return reallocated; +} + +/** + * Reserve enough space in the ThreadEntry::elements for the item + * @id to fit in. + */ + +void StaticMetaBase::reserve(EntryID* id) { + auto& meta = *this; + ThreadEntry* threadEntry = (*threadEntry_)(); + size_t prevCapacity = threadEntry->getElementsCapacity(); + + uint32_t idval = id->getOrAllocate(meta); + if (prevCapacity > idval) { + return; + } + + size_t newCapacity; + ElementWrapper* reallocated = reallocate(threadEntry, idval, newCapacity); + + // Success, update the entry + { + std::lock_guard<std::mutex> g(meta.lock_); + + if (reallocated) { + /* + * Note: we need to hold the meta lock when copying data out of + * the old vector, because some other thread might be + * destructing a ThreadLocal and writing to the elements vector + * of this thread. + */ + if (prevCapacity != 0) { + memcpy( + reallocated, + threadEntry->elements, + sizeof(*reallocated) * prevCapacity); + } + std::swap(reallocated, threadEntry->elements); + } + + for (size_t i = prevCapacity; i < newCapacity; i++) { + threadEntry->elements[i].node.initZero(threadEntry, i); + } + + threadEntry->setElementsCapacity(newCapacity); + } + + free(reallocated); +} + +void StaticMetaBase::reserveHeadUnlocked(uint32_t id) { + if (head_.getElementsCapacity() <= id) { + size_t prevCapacity = head_.getElementsCapacity(); + size_t newCapacity; + ElementWrapper* reallocated = reallocate(&head_, id, newCapacity); + + if (reallocated) { + if (prevCapacity != 0) { + memcpy( + reallocated, head_.elements, sizeof(*reallocated) * prevCapacity); + } + std::swap(reallocated, head_.elements); + } + + for (size_t i = prevCapacity; i < newCapacity; i++) { + head_.elements[i].node.init(&head_, i); + } + + head_.setElementsCapacity(newCapacity); + free(reallocated); + } +} + +void StaticMetaBase::pushBackLocked(ThreadEntry* t, uint32_t id) { + if (LIKELY(!t->removed_)) { + std::lock_guard<std::mutex> g(lock_); + auto* node = &t->elements[id].node; + node->push_back(&head_); + } +} + +void StaticMetaBase::pushBackUnlocked(ThreadEntry* t, uint32_t id) { + if (LIKELY(!t->removed_)) { + auto* node = &t->elements[id].node; + node->push_back(&head_); + } +} + +FOLLY_STATIC_CTOR_PRIORITY_MAX +PthreadKeyUnregister PthreadKeyUnregister::instance_; +} // namespace threadlocal_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.h b/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.h new file mode 100644 index 000000000..196de58da --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/ThreadLocalDetail.h @@ -0,0 +1,525 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <limits.h> + +#include <atomic> +#include <functional> +#include <mutex> +#include <string> +#include <thread> +#include <vector> + +#include <glog/logging.h> + +#include <folly/Exception.h> +#include <folly/Function.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/SharedMutex.h> +#include <folly/container/Foreach.h> +#include <folly/detail/AtFork.h> +#include <folly/memory/Malloc.h> +#include <folly/portability/PThread.h> +#include <folly/synchronization/MicroSpinLock.h> +#include <folly/system/ThreadId.h> + +#include <folly/detail/StaticSingletonManager.h> + +// In general, emutls cleanup is not guaranteed to play nice with the way +// StaticMeta mixes direct pthread calls and the use of __thread. This has +// caused problems on multiple platforms so don't use __thread there. +// +// XXX: Ideally we would instead determine if emutls is in use at runtime as it +// is possible to configure glibc on Linux to use emutls regardless. +#if !FOLLY_MOBILE && !defined(__APPLE__) && !defined(_MSC_VER) +#define FOLLY_TLD_USE_FOLLY_TLS 1 +#else +#undef FOLLY_TLD_USE_FOLLY_TLS +#endif + +namespace folly { + +enum class TLPDestructionMode { THIS_THREAD, ALL_THREADS }; +struct AccessModeStrict {}; + +namespace threadlocal_detail { + +constexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max(); + +struct ThreadEntry; +/* This represents a node in doubly linked list where all the nodes + * are part of an ElementWrapper struct that has the same id. + * we cannot use prev and next as ThreadEntryNode pointers since the + * ThreadEntry::elements can be reallocated and the pointers will change + * in this case. So we keep a pointer to the parent ThreadEntry struct + * one for the prev and next and also the id. + * We will traverse and update the list only when holding the + * StaticMetaBase::lock_ + */ +struct ThreadEntryNode { + uint32_t id; + ThreadEntry* parent; + ThreadEntry* prev; + ThreadEntry* next; + + void initIfZero(bool locked); + + void init(ThreadEntry* entry, uint32_t newId) { + id = newId; + parent = prev = next = entry; + } + + void initZero(ThreadEntry* entry, uint32_t newId) { + id = newId; + parent = entry; + prev = next = nullptr; + } + + // if the list this node is part of is empty + FOLLY_ALWAYS_INLINE bool empty() const { + return (next == parent); + } + + FOLLY_ALWAYS_INLINE bool zero() const { + return (!prev); + } + + FOLLY_ALWAYS_INLINE ThreadEntry* getThreadEntry() { + return parent; + } + + FOLLY_ALWAYS_INLINE ThreadEntryNode* getPrev(); + + FOLLY_ALWAYS_INLINE ThreadEntryNode* getNext(); + + void push_back(ThreadEntry* head); + + void eraseZero(); +}; + +/** + * POD wrapper around an element (a void*) and an associated deleter. + * This must be POD, as we memset() it to 0 and memcpy() it around. + */ +struct ElementWrapper { + using DeleterFunType = void(void*, TLPDestructionMode); + + bool dispose(TLPDestructionMode mode) { + if (ptr == nullptr) { + return false; + } + + DCHECK(deleter1 != nullptr); + ownsDeleter ? (*deleter2)(ptr, mode) : (*deleter1)(ptr, mode); + return true; + } + + void* release() { + auto retPtr = ptr; + + if (ptr != nullptr) { + cleanup(); + } + + return retPtr; + } + + template <class Ptr> + void set(Ptr p) { + auto guard = makeGuard([&] { delete p; }); + DCHECK(ptr == nullptr); + DCHECK(deleter1 == nullptr); + + if (p) { + node.initIfZero(true /*locked*/); + ptr = p; + deleter1 = [](void* pt, TLPDestructionMode) { + delete static_cast<Ptr>(pt); + }; + ownsDeleter = false; + guard.dismiss(); + } + } + + template <class Ptr, class Deleter> + void set(Ptr p, const Deleter& d) { + auto guard = makeGuard([&] { + if (p) { + d(p, TLPDestructionMode::THIS_THREAD); + } + }); + DCHECK(ptr == nullptr); + DCHECK(deleter2 == nullptr); + if (p) { + node.initIfZero(true /*locked*/); + ptr = p; + auto d2 = d; // gcc-4.8 doesn't decay types correctly in lambda captures + deleter2 = new std::function<DeleterFunType>( + [d2](void* pt, TLPDestructionMode mode) { + d2(static_cast<Ptr>(pt), mode); + }); + ownsDeleter = true; + guard.dismiss(); + } + } + + void cleanup() { + if (ownsDeleter) { + delete deleter2; + } + ptr = nullptr; + deleter1 = nullptr; + ownsDeleter = false; + } + + void* ptr; + union { + DeleterFunType* deleter1; + std::function<DeleterFunType>* deleter2; + }; + bool ownsDeleter; + ThreadEntryNode node; +}; + +struct StaticMetaBase; +struct ThreadEntryList; + +/** + * Per-thread entry. Each thread using a StaticMeta object has one. + * This is written from the owning thread only (under the lock), read + * from the owning thread (no lock necessary), and read from other threads + * (under the lock). + * StaticMetaBase::head_ elementsCapacity can be read from any thread on + * reallocate (no lock) + */ +struct ThreadEntry { + ElementWrapper* elements{nullptr}; + std::atomic<size_t> elementsCapacity{0}; + ThreadEntryList* list{nullptr}; + ThreadEntry* listNext{nullptr}; + StaticMetaBase* meta{nullptr}; + bool removed_{false}; + uint64_t tid_os{}; + aligned_storage_for_t<std::thread::id> tid_data{}; + + size_t getElementsCapacity() const noexcept { + return elementsCapacity.load(std::memory_order_relaxed); + } + + void setElementsCapacity(size_t capacity) noexcept { + elementsCapacity.store(capacity, std::memory_order_relaxed); + } + + std::thread::id& tid() { + return *reinterpret_cast<std::thread::id*>(&tid_data); + } +}; + +struct ThreadEntryList { + ThreadEntry* head{nullptr}; + size_t count{0}; +}; + +struct PthreadKeyUnregisterTester; + +FOLLY_ALWAYS_INLINE ThreadEntryNode* ThreadEntryNode::getPrev() { + return &prev->elements[id].node; +} + +FOLLY_ALWAYS_INLINE ThreadEntryNode* ThreadEntryNode::getNext() { + return &next->elements[id].node; +} + +/** + * We want to disable onThreadExit call at the end of shutdown, we don't care + * about leaking memory at that point. + * + * Otherwise if ThreadLocal is used in a shared library, onThreadExit may be + * called after dlclose(). + * + * This class has one single static instance; however since it's so widely used, + * directly or indirectly, by so many classes, we need to take care to avoid + * problems stemming from the Static Initialization/Destruction Order Fiascos. + * Therefore this class needs to be constexpr-constructible, so as to avoid + * the need for this to participate in init/destruction order. + */ +class PthreadKeyUnregister { + public: + static constexpr size_t kMaxKeys = 1UL << 16; + + ~PthreadKeyUnregister() { + // If static constructor priorities are not supported then + // ~PthreadKeyUnregister logic is not safe. +#if !defined(__APPLE__) && !defined(_MSC_VER) + MSLGuard lg(lock_); + while (size_) { + pthread_key_delete(keys_[--size_]); + } +#endif + } + + static void registerKey(pthread_key_t key) { + instance_.registerKeyImpl(key); + } + + private: + /** + * Only one global instance should exist, hence this is private. + * See also the important note at the top of this class about `constexpr` + * usage. + */ + constexpr PthreadKeyUnregister() : lock_(), size_(0), keys_() {} + friend struct folly::threadlocal_detail::PthreadKeyUnregisterTester; + + void registerKeyImpl(pthread_key_t key) { + MSLGuard lg(lock_); + if (size_ == kMaxKeys) { + throw std::logic_error("pthread_key limit has already been reached"); + } + keys_[size_++] = key; + } + + MicroSpinLock lock_; + size_t size_; + pthread_key_t keys_[kMaxKeys]; + + static PthreadKeyUnregister instance_; +}; + +struct StaticMetaBase { + // Represents an ID of a thread local object. Initially set to the maximum + // uint. This representation allows us to avoid a branch in accessing TLS data + // (because if you test capacity > id if id = maxint then the test will always + // fail). It allows us to keep a constexpr constructor and avoid SIOF. + class EntryID { + public: + std::atomic<uint32_t> value; + + constexpr EntryID() : value(kEntryIDInvalid) {} + + EntryID(EntryID&& other) noexcept : value(other.value.load()) { + other.value = kEntryIDInvalid; + } + + EntryID& operator=(EntryID&& other) noexcept { + assert(this != &other); + value = other.value.load(); + other.value = kEntryIDInvalid; + return *this; + } + + EntryID(const EntryID& other) = delete; + EntryID& operator=(const EntryID& other) = delete; + + uint32_t getOrInvalid() { + // It's OK for this to be relaxed, even though we're effectively doing + // double checked locking in using this value. We only care about the + // uniqueness of IDs, getOrAllocate does not modify any other memory + // this thread will use. + return value.load(std::memory_order_relaxed); + } + + uint32_t getOrAllocate(StaticMetaBase& meta) { + uint32_t id = getOrInvalid(); + if (id != kEntryIDInvalid) { + return id; + } + // The lock inside allocate ensures that a single value is allocated + return meta.allocate(this); + } + }; + + StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict); + + FOLLY_EXPORT static ThreadEntryList* getThreadEntryList(); + + static bool dying(); + + static void onThreadExit(void* ptr); + + // returns the elementsCapacity for the + // current thread ThreadEntry struct + uint32_t elementsCapacity() const; + + uint32_t allocate(EntryID* ent); + + void destroy(EntryID* ent); + + /** + * Reserve enough space in the ThreadEntry::elements for the item + * @id to fit in. + */ + void reserve(EntryID* id); + + ElementWrapper& getElement(EntryID* ent); + + // reserve an id in the head_ ThreadEntry->elements + // array if not already there + void reserveHeadUnlocked(uint32_t id); + + // push back an entry in the doubly linked list + // that corresponds to idx id + void pushBackLocked(ThreadEntry* t, uint32_t id); + void pushBackUnlocked(ThreadEntry* t, uint32_t id); + + // static helper method to reallocate the ThreadEntry::elements + // returns != nullptr if the ThreadEntry::elements was reallocated + // nullptr if the ThreadEntry::elements was just extended + // and throws stdd:bad_alloc if memory cannot be allocated + static ElementWrapper* + reallocate(ThreadEntry* threadEntry, uint32_t idval, size_t& newCapacity); + + uint32_t nextId_; + std::vector<uint32_t> freeIds_; + std::mutex lock_; + SharedMutex accessAllThreadsLock_; + pthread_key_t pthreadKey_; + ThreadEntry head_; + ThreadEntry* (*threadEntry_)(); + bool strict_; +}; + +// Held in a singleton to track our global instances. +// We have one of these per "Tag", by default one for the whole system +// (Tag=void). +// +// Creating and destroying ThreadLocalPtr objects, as well as thread exit +// for threads that use ThreadLocalPtr objects collide on a lock inside +// StaticMeta; you can specify multiple Tag types to break that lock. +template <class Tag, class AccessMode> +struct StaticMeta final : StaticMetaBase { + StaticMeta() + : StaticMetaBase( + &StaticMeta::getThreadEntrySlow, + std::is_same<AccessMode, AccessModeStrict>::value) { + detail::AtFork::registerHandler( + this, + /*prepare*/ &StaticMeta::preFork, + /*parent*/ &StaticMeta::onForkParent, + /*child*/ &StaticMeta::onForkChild); + } + + static StaticMeta<Tag, AccessMode>& instance() { + // Leak it on exit, there's only one per process and we don't have to + // worry about synchronization with exiting threads. + return detail::createGlobal<StaticMeta<Tag, AccessMode>, void>(); + } + + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static ElementWrapper& get(EntryID* ent) { + // Eliminate as many branches and as much extra code as possible in the + // cached fast path, leaving only one branch here and one indirection below. + uint32_t id = ent->getOrInvalid(); +#ifdef FOLLY_TLD_USE_FOLLY_TLS + static FOLLY_TLS ThreadEntry* threadEntry{}; + static FOLLY_TLS size_t capacity{}; +#else + ThreadEntry* threadEntry{}; + size_t capacity{}; +#endif + if (FOLLY_UNLIKELY(capacity <= id)) { + getSlowReserveAndCache(ent, id, threadEntry, capacity); + } + return threadEntry->elements[id]; + } + + FOLLY_NOINLINE static void getSlowReserveAndCache( + EntryID* ent, + uint32_t& id, + ThreadEntry*& threadEntry, + size_t& capacity) { + auto& inst = instance(); + threadEntry = inst.threadEntry_(); + if (UNLIKELY(threadEntry->getElementsCapacity() <= id)) { + inst.reserve(ent); + id = ent->getOrInvalid(); + } + capacity = threadEntry->getElementsCapacity(); + assert(capacity > id); + } + + FOLLY_EXPORT FOLLY_NOINLINE static ThreadEntry* getThreadEntrySlow() { + auto& meta = instance(); + auto key = meta.pthreadKey_; + ThreadEntry* threadEntry = + static_cast<ThreadEntry*>(pthread_getspecific(key)); + if (!threadEntry) { + ThreadEntryList* threadEntryList = StaticMeta::getThreadEntryList(); +#ifdef FOLLY_TLD_USE_FOLLY_TLS + static FOLLY_TLS ThreadEntry threadEntrySingleton; + threadEntry = &threadEntrySingleton; +#else + threadEntry = new ThreadEntry(); +#endif + // if the ThreadEntry already exists + // but pthread_getspecific returns NULL + // do not add the same entry twice to the list + // since this would create a loop in the list + if (!threadEntry->list) { + threadEntry->list = threadEntryList; + threadEntry->listNext = threadEntryList->head; + threadEntryList->head = threadEntry; + } + + threadEntry->tid() = std::this_thread::get_id(); + threadEntry->tid_os = folly::getOSThreadID(); + + // if we're adding a thread entry + // we need to increment the list count + // even if the entry is reused + threadEntryList->count++; + + threadEntry->meta = &meta; + int ret = pthread_setspecific(key, threadEntry); + checkPosixError(ret, "pthread_setspecific failed"); + } + return threadEntry; + } + + static bool preFork() { + return instance().lock_.try_lock(); // Make sure it's created + } + + static void onForkParent() { + instance().lock_.unlock(); + } + + static void onForkChild() { + // only the current thread survives + auto& head = instance().head_; + // init the circular lists + auto elementsCapacity = head.getElementsCapacity(); + for (size_t i = 0u; i < elementsCapacity; ++i) { + head.elements[i].node.init(&head, static_cast<uint32_t>(i)); + } + // init the thread entry + ThreadEntry* threadEntry = instance().threadEntry_(); + elementsCapacity = threadEntry->getElementsCapacity(); + for (size_t i = 0u; i < elementsCapacity; ++i) { + if (!threadEntry->elements[i].node.zero()) { + threadEntry->elements[i].node.initZero( + threadEntry, static_cast<uint32_t>(i)); + threadEntry->elements[i].node.initIfZero(false /*locked*/); + } + } + + instance().lock_.unlock(); + } +}; +} // namespace threadlocal_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/TurnSequencer.h b/ios/Pods/Flipper-Folly/folly/detail/TurnSequencer.h new file mode 100644 index 000000000..9f882cdbc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/TurnSequencer.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <limits> + +#include <folly/Portability.h> +#include <folly/chrono/Hardware.h> +#include <folly/detail/Futex.h> +#include <folly/portability/Asm.h> +#include <folly/portability/Unistd.h> + +#include <glog/logging.h> + +namespace folly { + +namespace detail { + +/// A TurnSequencer allows threads to order their execution according to +/// a monotonically increasing (with wraparound) "turn" value. The two +/// operations provided are to wait for turn T, and to move to the next +/// turn. Every thread that is waiting for T must have arrived before +/// that turn is marked completed (for MPMCQueue only one thread waits +/// for any particular turn, so this is trivially true). +/// +/// TurnSequencer's state_ holds 26 bits of the current turn (shifted +/// left by 6), along with a 6 bit saturating value that records the +/// maximum waiter minus the current turn. Wraparound of the turn space +/// is expected and handled. This allows us to atomically adjust the +/// number of outstanding waiters when we perform a FUTEX_WAKE operation. +/// Compare this strategy to sem_t's separate num_waiters field, which +/// isn't decremented until after the waiting thread gets scheduled, +/// during which time more enqueues might have occurred and made pointless +/// FUTEX_WAKE calls. +/// +/// TurnSequencer uses futex() directly. It is optimized for the +/// case that the highest awaited turn is 32 or less higher than the +/// current turn. We use the FUTEX_WAIT_BITSET variant, which lets +/// us embed 32 separate wakeup channels in a single futex. See +/// http://locklessinc.com/articles/futex_cheat_sheet for a description. +/// +/// We only need to keep exact track of the delta between the current +/// turn and the maximum waiter for the 32 turns that follow the current +/// one, because waiters at turn t+32 will be awoken at turn t. At that +/// point they can then adjust the delta using the higher base. Since we +/// need to encode waiter deltas of 0 to 32 inclusive, we use 6 bits. +/// We actually store waiter deltas up to 63, since that might reduce +/// the number of CAS operations a tiny bit. +/// +/// To avoid some futex() calls entirely, TurnSequencer uses an adaptive +/// spin cutoff before waiting. The overheads (and convergence rate) +/// of separately tracking the spin cutoff for each TurnSequencer would +/// be prohibitive, so the actual storage is passed in as a parameter and +/// updated atomically. This also lets the caller use different adaptive +/// cutoffs for different operations (read versus write, for example). +/// To avoid contention, the spin cutoff is only updated when requested +/// by the caller. +/// +/// On x86 the latency of a spin loop varies dramatically across +/// architectures due to changes in the PAUSE instruction. Skylake +/// increases the latency by about a factor of 15 compared to previous +/// architectures. To work around this, on x86 we measure spins using +/// RDTSC rather than a loop counter. +template <template <typename> class Atom> +struct TurnSequencer { + explicit TurnSequencer(const uint32_t firstTurn = 0) noexcept + : state_(encode(firstTurn << kTurnShift, 0)) {} + + /// Returns true iff a call to waitForTurn(turn, ...) won't block + bool isTurn(const uint32_t turn) const noexcept { + auto state = state_.load(std::memory_order_acquire); + return decodeCurrentSturn(state) == (turn << kTurnShift); + } + + enum class TryWaitResult { SUCCESS, PAST, TIMEDOUT }; + + /// See tryWaitForTurn + /// Requires that `turn` is not a turn in the past. + void waitForTurn( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff) noexcept { + const auto ret = tryWaitForTurn(turn, spinCutoff, updateSpinCutoff); + DCHECK(ret == TryWaitResult::SUCCESS); + } + + // Internally we always work with shifted turn values, which makes the + // truncation and wraparound work correctly. This leaves us bits at + // the bottom to store the number of waiters. We call shifted turns + // "sturns" inside this class. + + /// Blocks the current thread until turn has arrived. + /// If updateSpinCutoff is true then this will spin for up to + /// kMaxSpinLimit before blocking and will adjust spinCutoff based + /// on the results, otherwise it will spin for at most spinCutoff. + /// Returns SUCCESS if the wait succeeded, PAST if the turn is in the + /// past or TIMEDOUT if the absTime time value is not nullptr and is + /// reached before the turn arrives + template < + class Clock = std::chrono::steady_clock, + class Duration = typename Clock::duration> + TryWaitResult tryWaitForTurn( + const uint32_t turn, + Atom<uint32_t>& spinCutoff, + const bool updateSpinCutoff, + const std::chrono::time_point<Clock, Duration>* absTime = + nullptr) noexcept { + uint32_t prevThresh = spinCutoff.load(std::memory_order_relaxed); + const uint32_t effectiveSpinCutoff = + updateSpinCutoff || prevThresh == 0 ? kMaxSpinLimit : prevThresh; + + uint64_t begin = 0; + uint32_t tries; + const uint32_t sturn = turn << kTurnShift; + for (tries = 0;; ++tries) { + uint32_t state = state_.load(std::memory_order_acquire); + uint32_t current_sturn = decodeCurrentSturn(state); + if (current_sturn == sturn) { + break; + } + + // wrap-safe version of (current_sturn >= sturn) + if (sturn - current_sturn >= std::numeric_limits<uint32_t>::max() / 2) { + // turn is in the past + return TryWaitResult::PAST; + } + + // the first effectSpinCutoff tries are spins, after that we will + // record ourself as a waiter and block with futexWait + if (kSpinUsingHardwareClock) { + auto now = hardware_timestamp(); + if (tries == 0) { + begin = now; + } + if (tries == 0 || now < begin + effectiveSpinCutoff) { + asm_volatile_pause(); + continue; + } + } else { + if (tries < effectiveSpinCutoff) { + asm_volatile_pause(); + continue; + } + } + + uint32_t current_max_waiter_delta = decodeMaxWaitersDelta(state); + uint32_t our_waiter_delta = (sturn - current_sturn) >> kTurnShift; + uint32_t new_state; + if (our_waiter_delta <= current_max_waiter_delta) { + // state already records us as waiters, probably because this + // isn't our first time around this loop + new_state = state; + } else { + new_state = encode(current_sturn, our_waiter_delta); + if (state != new_state && + !state_.compare_exchange_strong(state, new_state)) { + continue; + } + } + if (absTime) { + auto futexResult = detail::futexWaitUntil( + &state_, new_state, *absTime, futexChannel(turn)); + if (futexResult == FutexResult::TIMEDOUT) { + return TryWaitResult::TIMEDOUT; + } + } else { + detail::futexWait(&state_, new_state, futexChannel(turn)); + } + } + + if (updateSpinCutoff || prevThresh == 0) { + // if we hit kMaxSpinLimit then spinning was pointless, so the right + // spinCutoff is kMinSpinLimit + uint32_t target; + uint64_t elapsed = !kSpinUsingHardwareClock || tries == 0 + ? tries + : hardware_timestamp() - begin; + if (tries >= kMaxSpinLimit) { + target = kMinSpinLimit; + } else { + // to account for variations, we allow ourself to spin 2*N when + // we think that N is actually required in order to succeed + target = std::min( + uint32_t{kMaxSpinLimit}, + std::max( + uint32_t{kMinSpinLimit}, static_cast<uint32_t>(elapsed) * 2)); + } + + if (prevThresh == 0) { + // bootstrap + spinCutoff.store(target); + } else { + // try once, keep moving if CAS fails. Exponential moving average + // with alpha of 7/8 + // Be careful that the quantity we add to prevThresh is signed. + spinCutoff.compare_exchange_weak( + prevThresh, prevThresh + int(target - prevThresh) / 8); + } + } + + return TryWaitResult::SUCCESS; + } + + /// Unblocks a thread running waitForTurn(turn + 1) + void completeTurn(const uint32_t turn) noexcept { + uint32_t state = state_.load(std::memory_order_acquire); + while (true) { + DCHECK(state == encode(turn << kTurnShift, decodeMaxWaitersDelta(state))); + uint32_t max_waiter_delta = decodeMaxWaitersDelta(state); + uint32_t new_state = encode( + (turn + 1) << kTurnShift, + max_waiter_delta == 0 ? 0 : max_waiter_delta - 1); + if (state_.compare_exchange_strong(state, new_state)) { + if (max_waiter_delta != 0) { + detail::futexWake( + &state_, std::numeric_limits<int>::max(), futexChannel(turn + 1)); + } + break; + } + // failing compare_exchange_strong updates first arg to the value + // that caused the failure, so no need to reread state_ + } + } + + /// Returns the least-most significant byte of the current uncompleted + /// turn. The full 32 bit turn cannot be recovered. + uint8_t uncompletedTurnLSB() const noexcept { + return uint8_t(state_.load(std::memory_order_acquire) >> kTurnShift); + } + + private: + static constexpr bool kSpinUsingHardwareClock = kIsArchAmd64; + static constexpr uint32_t kCyclesPerSpinLimit = + kSpinUsingHardwareClock ? 1 : 10; + + /// kTurnShift counts the bits that are stolen to record the delta + /// between the current turn and the maximum waiter. It needs to be big + /// enough to record wait deltas of 0 to 32 inclusive. Waiters more + /// than 32 in the future will be woken up 32*n turns early (since + /// their BITSET will hit) and will adjust the waiter count again. + /// We go a bit beyond and let the waiter count go up to 63, which is + /// free and might save us a few CAS + static constexpr uint32_t kTurnShift = 6; + static constexpr uint32_t kWaitersMask = (1 << kTurnShift) - 1; + + /// The minimum spin duration that we will adaptively select. The value + /// here is cycles, adjusted to the way in which the limit will actually + /// be applied. + static constexpr uint32_t kMinSpinLimit = 200 / kCyclesPerSpinLimit; + + /// The maximum spin duration that we will adaptively select, and the + /// spin duration that will be used when probing to get a new data + /// point for the adaptation + static constexpr uint32_t kMaxSpinLimit = 20000 / kCyclesPerSpinLimit; + + /// This holds both the current turn, and the highest waiting turn, + /// stored as (current_turn << 6) | min(63, max(waited_turn - current_turn)) + Futex<Atom> state_; + + /// Returns the bitmask to pass futexWait or futexWake when communicating + /// about the specified turn + uint32_t futexChannel(uint32_t turn) const noexcept { + return 1u << (turn & 31); + } + + uint32_t decodeCurrentSturn(uint32_t state) const noexcept { + return state & ~kWaitersMask; + } + + uint32_t decodeMaxWaitersDelta(uint32_t state) const noexcept { + return state & kWaitersMask; + } + + uint32_t encode(uint32_t currentSturn, uint32_t maxWaiterD) const noexcept { + return currentSturn | std::min(uint32_t{kWaitersMask}, maxWaiterD); + } +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/TypeList.h b/ios/Pods/Flipper-Folly/folly/detail/TypeList.h new file mode 100644 index 000000000..be2ee3338 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/TypeList.h @@ -0,0 +1,553 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <utility> + +#include <folly/Traits.h> +#include <folly/Utility.h> + +/** + * \file TypeList.h + * \author Eric Niebler + * + * The file contains facilities for manipulating lists of types, and for + * defining and composing operations over types. + * + * The type-operations behave like compile-time functions: they accept types as + * input and produce types as output. A simple example is a template alias, like + * `std::add_pointer_t`. However, templates are not themselves first class + * citizens of the language; they cannot be easily "returned" from a + * metafunction, and passing them to a metafunction is awkward and often + * requires the user to help the C++ parser by adding hints like `typename` + * and `template` to disambiguate the syntax. That makes higher-ordered + * metaprogramming difficult. (There is no simple way to e.g., compose two + * template aliases and pass the result as an argument to another template.) + * + * Instead, we wrap template aliases in a ordinary class, which _can_ be passed + * and returned simply from metafunctions. This is like Boost.MPL's notion of a + * "metafunction class"[1], and we adopt that terminology here. + * + * For the Folly.TypeList library, a metafunction class is a protocol that + * all the components of Folly.TypeList expect and agree upon. It is a class + * type that has a nested template alias called `Apply`. So for instance, + * `std::add_pointer_t` as a Folly metafunction class would look like this: + * + * struct AddPointer { + * template <class T> + * using apply = T*; + * }; + * + * Folly.TypeList gives a simple way to "lift" an ordinary template alias into + * a metafunction class: `MetaQuote`. The above `AddPointer` could instead be + * written as: + * + * using AddPointer = folly::MetaQuote<std::add_pointer_t>; + * + * \par Naming + * + * A word about naming. Components in Folly.TypeList fall into two buckets: + * utilities for manipulating lists of types, and utilities for manipulating + * metafunction classes. The former have names that start with `Type`, as in + * `TypeList` and `TypeTransform`. The latter have names that start with `Meta`, + * as in `MetaQuote` and `MetaApply`. + * + * [1] Boost.MPL Metafunction Class: + * http://www.boost.org/libs/mpl/doc/refmanual/metafunction-class.html + */ + +namespace folly { +namespace detail { + +/** + * Handy shortcuts for some standard facilities + */ +template <bool B> +using Bool = bool_constant<B>; +using True = std::true_type; +using False = std::false_type; + +/** + * Given a metafunction class `Fn` and arguments `Ts...`, invoke `Fn` + * with `Ts...`. + */ +template <class Fn, class... Ts> +using MetaApply = typename Fn::template apply<Ts...>; + +/** + * A list of types. + */ +template <class... Ts> +struct TypeList { + /** + * An alias for this list of types + */ + using type = TypeList; + + /** + * \return the number of types in this list. + */ + static constexpr std::size_t size() noexcept { + return sizeof...(Ts); + } + + /** + * This list of types is also a metafunction class that accepts another + * metafunction class and invokes it with all the types in the list. + */ + template <class Fn> + using apply = MetaApply<Fn, Ts...>; +}; + +/** + * A wrapper for a type + */ +template <class T> +struct Type { + /** + * An alias for the wrapped type + */ + using type = T; + + /** + * This wrapper is a metafunction class that, when applied with any number + * of arguments, returns the wrapped type. + */ + template <class...> + using apply = T; +}; + +/** + * An empty struct. + */ +struct Empty {}; + +/// \cond +namespace impl { +template <bool B> +struct If_ { + template <class T, class U> + using apply = T; +}; +template <> +struct If_<false> { + template <class T, class U> + using apply = U; +}; +} // namespace impl +/// \endcond + +/** + * Like std::conditional, but with fewer template instantiations + */ +template <bool If_, class Then, class Else> +using If = MetaApply<impl::If_<If_>, Then, Else>; + +/** + * Defers the evaluation of an alias. + * + * Given a template `C` and arguments `Ts...`, then + * - If `C<Ts...>` is well-formed, `MetaApply<MetaDefer<C, Ts...>>` is well- + * formed and is an alias for `C<Ts...>`. + * - Otherwise, `MetaApply<MetaDefer<C, Ts...>>` is ill-formed. + */ +template <template <class...> class C, class... Ts> +class MetaDefer { + template <template <class...> class D, class = D<Ts...>> + static char (&try_(int))[1]; + + template <template <class...> class D, class = void> + static char (&try_(long))[2]; + struct Result { + using type = C<Ts...>; + }; + + public: + template <class... Us> + using apply = _t<If<sizeof(try_<C>(0)) - 1 || sizeof...(Us), Empty, Result>>; +}; + +/** + * Compose two metafunction classes into one by chaining. + * + * `MetaApply<MetaCompose<P, Q>, Ts...>` is equivalent to + * `MetaApply<P, MetaApply<Q, Ts...>>`. + */ +template <class P, class Q> +struct MetaCompose { + template <class... Ts> + using apply = MetaApply<P, MetaApply<Q, Ts...>>; +}; + +/** + * A metafunction class that always returns its argument unmodified. + * + * `MetaApply<MetaIdentity, int>` is equivalent to `int`. + */ +struct MetaIdentity { + template <class T> + using apply = T; +}; + +/** + * Lifts a class template or an alias template to be a metafunction class. + * + * `MetaApply<MetaQuote<C>, Ts...>` is equivalent to `C<Ts...>`. + */ +template <template <class...> class C> +struct MetaQuote { + template <class... Ts> + using apply = MetaApply<MetaDefer<C, Ts...>>; +}; + +/// \cond +// Specialization for TypeList since it doesn't need to go through MetaDefer +template <> +struct MetaQuote<TypeList> { + template <class... Ts> + using apply = TypeList<Ts...>; +}; +/// \endcond + +/** + * Lifts a trait class template to be a metafunction class. + * + * `MetaApply<MetaQuoteTrait<C>, Ts...>` is equivalent to + * `typename C<Ts...>::type`. + */ +template <template <class...> class C> +using MetaQuoteTrait = MetaCompose<MetaQuote<_t>, MetaQuote<C>>; + +/** + * Partially evaluate the metafunction class `Fn` by binding the arguments + * `Ts...` to the front of the argument list. + * + * `MetaApply<MetaBindFront<Fn, Ts...>, Us...>` is equivalent to + * `MetaApply<Fn, Ts..., Us...>`. + */ +template <class Fn, class... Ts> +struct MetaBindFront { + template <class... Us> + using apply = MetaApply<Fn, Ts..., Us...>; +}; + +/** + * Partially evaluate the metafunction class `Fn` by binding the arguments + * `Ts...` to the back of the argument list. + * + * `MetaApply<MetaBindBack<Fn, Ts...>, Us...>` is equivalent to + * `MetaApply<Fn, Us..., Ts...>`. + */ +template <class Fn, class... Ts> +struct MetaBindBack { + template <class... Us> + using apply = MetaApply<Fn, Us..., Ts...>; +}; + +/** + * Given a metafunction class `Fn` that expects a single `TypeList` argument, + * turn it into a metafunction class that takes `N` arguments, wraps them in + * a `TypeList`, and calls `Fn` with it. + * + * `MetaApply<MetaCurry<Fn>, Ts...>` is equivalent to + * `MetaApply<Fn, TypeList<Ts...>>`. + */ +template <class Fn> +using MetaCurry = MetaCompose<Fn, MetaQuote<TypeList>>; + +/** + * Given a metafunction class `Fn` that expects `N` arguments, + * turn it into a metafunction class that takes a single `TypeList` arguments + * and calls `Fn` with the types in the `TypeList`. + * + * `MetaApply<MetaUncurry<Fn>, TypeList<Ts...>>` is equivalent to + * `MetaApply<Fn, Ts...>`. + */ +template <class Fn> +using MetaUncurry = MetaBindBack<MetaQuote<MetaApply>, Fn>; + +/** + * Given a `TypeList` and some arguments, append those arguments to the end of + * the `TypeList`. + * + * `TypePushBack<TypeList<Ts...>, Us...>` is equivalent to + * `TypeList<Ts..., Us...>`. + */ +template <class List, class... Ts> +using TypePushBack = MetaApply<List, MetaBindBack<MetaQuote<TypeList>, Ts...>>; + +/** + * Given a `TypeList` and some arguments, prepend those arguments to the start + * of the `TypeList`. + * + * `TypePushFront<TypeList<Ts...>, Us...>` is equivalent to + * `TypeList<Us..., Ts...>`. + */ +template <class List, class... Ts> +using TypePushFront = + MetaApply<List, MetaBindFront<MetaQuote<TypeList>, Ts...>>; + +/** + * Given a metafunction class `Fn` and a `TypeList`, call `Fn` with the types + * in the `TypeList`. + */ +template <class Fn, class List> +using MetaUnpack = MetaApply<List, Fn>; + +/// \cond +namespace impl { +template <class Fn> +struct TypeTransform_ { + template <class... Ts> + using apply = TypeList<MetaApply<Fn, Ts>...>; +}; +} // namespace impl +/// \endcond + +/** + * Transform all the elements in a `TypeList` with the metafunction class `Fn`. + * + * `TypeTransform<TypeList<Ts..>, Fn>` is equivalent to + * `TypeList<MetaApply<Fn, Ts>...>`. + */ +template <class List, class Fn> +using TypeTransform = MetaApply<List, impl::TypeTransform_<Fn>>; + +/** + * Given a binary metafunction class, convert it to another binary metafunction + * class with the argument order reversed. + */ +template <class Fn> +struct MetaFlip { + template <class A, class B> + using apply = MetaApply<Fn, B, A>; +}; + +/// \cond +namespace impl { +template <class Fn> +struct FoldR_ { + template <class... Ts> + struct Lambda : MetaIdentity {}; + template <class A, class... Ts> + struct Lambda<A, Ts...> { + template <class State> + using apply = MetaApply<Fn, A, MetaApply<Lambda<Ts...>, State>>; + }; + template <class A, class B, class C, class D, class... Ts> + struct Lambda<A, B, C, D, Ts...> { // manually unroll 4 elements + template <class State> + using apply = MetaApply< + Fn, + A, + MetaApply< + Fn, + B, + MetaApply< + Fn, + C, + MetaApply<Fn, D, MetaApply<Lambda<Ts...>, State>>>>>; + }; + template <class... Ts> + using apply = Lambda<Ts...>; +}; +} // namespace impl +/// \endcond + +/** + * Given a `TypeList`, an initial state, and a binary function, reduce the + * `TypeList` by applying the function to each element and the current state, + * producing a new state to be used with the next element. This is a "right" + * fold in functional parlance. + * + * `TypeFold<TypeList<A, B, C>, X, Fn>` is equivalent to + * `MetaApply<Fn, A, MetaApply<Fn, B, MetaApply<Fn, C, X>>>`. + */ +template <class List, class State, class Fn> +using TypeFold = MetaApply<MetaApply<List, impl::FoldR_<Fn>>, State>; + +/// \cond +namespace impl { +template <class Fn> +struct FoldL_ { + template <class... Ts> + struct Lambda : MetaIdentity {}; + template <class A, class... Ts> + struct Lambda<A, Ts...> { + template <class State> + using apply = MetaApply<Lambda<Ts...>, MetaApply<Fn, State, A>>; + }; + template <class A, class B, class C, class D, class... Ts> + struct Lambda<A, B, C, D, Ts...> { // manually unroll 4 elements + template <class State> + using apply = MetaApply< + Lambda<Ts...>, + MetaApply< + Fn, + MetaApply<Fn, MetaApply<Fn, MetaApply<Fn, State, A>, B>, C>, + D>>; + }; + template <class... Ts> + using apply = Lambda<Ts...>; +}; +} // namespace impl +/// \endcond + +/** + * Given a `TypeList`, an initial state, and a binary function, reduce the + * `TypeList` by applying the function to each element and the current state, + * producing a new state to be used with the next element. This is a "left" + * fold, in functional parlance. + * + * `TypeReverseFold<TypeList<A, B, C>, X, Fn>` is equivalent to + * `MetaApply<Fn, MetaApply<Fn, MetaApply<Fn, X, C>, B, A>`. + */ +template <class List, class State, class Fn> +using TypeReverseFold = MetaApply<MetaApply<List, impl::FoldL_<Fn>>, State>; + +namespace impl { +template <class List> +struct Inherit_; +template <class... Ts> +struct Inherit_<TypeList<Ts...>> : Ts... { + using type = Inherit_; +}; +} // namespace impl + +/** + * Given a `TypeList`, create a type that inherits from all the types in the + * list. + * + * Requires: all of the types in the list are non-final class types, and the + * types are all unique. + */ +template <class List> +using Inherit = impl::Inherit_<List>; + +/// \cond +namespace impl { +// Avoid instantiating std::is_base_of when we have an intrinsic. +#if defined(__GNUC__) || defined(_MSC_VER) +template <class T, class... Set> +using In_ = Bool<__is_base_of(Type<T>, Inherit<TypeList<Type<Set>...>>)>; +#else +template <class T, class... Set> +using In_ = std::is_base_of<Type<T>, Inherit<TypeList<Type<Set>...>>>; +#endif + +template <class T> +struct InsertFront_ { + template <class... Set> + using apply = + If<In_<T, Set...>::value, TypeList<Set...>, TypeList<T, Set...>>; +}; + +struct Unique_ { + template <class T, class List> + using apply = MetaApply<List, impl::InsertFront_<T>>; +}; +} // namespace impl +/// \endcond + +/** + * Given a `TypeList`, produce a new list of types removing duplicates, keeping + * the first seen element. + * + * `TypeUnique<TypeList<int, short, int>>` is equivalent to + * `TypeList<int, short>`. + * + * \note This algorithm is O(N^2). + */ +template <class List> +using TypeUnique = TypeFold<List, TypeList<>, impl::Unique_>; + +/** + * Given a `TypeList`, produce a new list of types removing duplicates, keeping + * the last seen element. + * + * `TypeUnique<TypeList<int, short, int>>` is equivalent to + * `TypeList<short, int>`. + * + * \note This algorithm is O(N^2). + */ +template <class List> +using TypeReverseUnique = + TypeReverseFold<List, TypeList<>, MetaFlip<impl::Unique_>>; + +/// \cond +namespace impl { +template <class T> +struct AsTypeList_ {}; +template <template <class...> class T, class... Ts> +struct AsTypeList_<T<Ts...>> { + using type = TypeList<Ts...>; +}; +template <class T, T... Is> +struct AsTypeList_<std::integer_sequence<T, Is...>> { + using type = TypeList<std::integral_constant<T, Is>...>; +}; +} // namespace impl +/// \endcond + +/** + * Convert a type to a list of types. Given a type `T`: + * - If `T` is of the form `C<Ts...>`, where `C` is a class template and + * `Ts...` is a list of types, the result is `TypeList<Ts...>`. + * - Else, if `T` is of the form `std::integer_sequence<T, Is...>`, then + * the result is `TypeList<std::integral_constant<T, Is>...>`. + * - Otherwise, `asTypeList<T>` is ill-formed. + */ +template <class T> +using AsTypeList = _t<impl::AsTypeList_<T>>; + +/// \cond +namespace impl { +// TODO For a list of N lists, this algorithm is O(N). It does no unrolling. +struct Join_ { + template <class Fn> + struct Lambda { + template <class... Ts> + using apply = MetaBindBack<Fn, Ts...>; + }; + template <class List, class Fn> + using apply = MetaApply<List, Lambda<Fn>>; +}; +} // namespace impl +/// \endcond + +/** + * Given a `TypeList` of `TypeList`s, flatten the lists into a single list. + * + * `TypeJoin<TypeList<TypeList<As...>, TypeList<Bs...>>>` is equivalent to + * `TypeList<As..., Bs...>` + */ +template <class List> +using TypeJoin = MetaApply<TypeFold<List, MetaQuote<TypeList>, impl::Join_>>; + +/** + * Given several `TypeList`s, flatten the lists into a single list. + * + * \note This is just the curried form of `TypeJoin`. (See `MetaCurry`.) + * + * `TypeConcat<TypeList<As...>, TypeList<Bs...>>` is equivalent to + * `TypeList<As..., Bs...>` + */ +template <class... Ts> +using TypeConcat = TypeJoin<TypeList<Ts...>>; +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.cpp b/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.cpp new file mode 100644 index 000000000..cd9e4df65 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/detail/UniqueInstance.h> + +#include <cstdlib> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> + +#include <folly/Demangle.h> +#include <folly/lang/Exception.h> + +namespace folly { +namespace detail { + +namespace { + +using Ptr = std::type_info const*; +struct PtrRange { + Ptr const* b; + Ptr const* e; +}; + +template <typename Value> +PtrRange ptr_range_key(Value value) { + auto const data = value.ptrs; + return {data, data + value.key_size}; +} + +template <typename Value> +PtrRange ptr_range_mapped(Value value) { + auto const data = value.ptrs + value.key_size; + return {data, data + value.mapped_size}; +} + +bool equal(PtrRange lhs, PtrRange rhs) { + auto const cmp = [](auto a, auto b) { return *a == *b; }; + return std::equal(lhs.b, lhs.e, rhs.b, rhs.e, cmp); +} + +std::string join(PtrRange types) { + std::ostringstream ret; + for (auto t = types.b; t != types.e; ++t) { + if (t != types.b) { + ret << ", "; + } + ret << demangle((*t)->name()); + } + return ret.str(); +} + +template <typename Value> +std::string render(Value value) { + auto const key_s = join(ptr_range_key(value)); + auto const mapped_s = join(ptr_range_mapped(value)); + std::ostringstream ret; + ret << value.tmpl << "<" << key_s << ", " << mapped_s << ">"; + return ret.str(); +} + +} // namespace + +void UniqueInstance::enforce( + char const* tmpl, + Ptr const* ptrs, + std::uint32_t key_size, + std::uint32_t mapped_size, + Value& global) noexcept { + Value const local{tmpl, ptrs, key_size, mapped_size}; + + if (!global.tmpl) { + global = local; + return; + } + if (!equal(ptr_range_key(global), ptr_range_key(local))) { + throw_exception<std::logic_error>("mismatched unique instance"); + } + if (std::strcmp(global.tmpl, local.tmpl) == 0 && + equal(ptr_range_mapped(global), ptr_range_mapped(local))) { + return; + } + + auto const key = ptr_range_key(local); + + std::ios_base::Init io_init; + std::cerr << "Overloaded unique instance over <" << join(key) << ", ...> " + << "with differing trailing arguments:\n" + << " " << render(global) << "\n" + << " " << render(local) << "\n"; + std::abort(); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.h b/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.h new file mode 100644 index 000000000..fded57d6f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/detail/UniqueInstance.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> +#include <typeinfo> + +#include <folly/detail/StaticSingletonManager.h> + +namespace folly { +namespace detail { + +class UniqueInstance { + public: + template <typename... Key, typename... Mapped> + FOLLY_EXPORT explicit UniqueInstance( + char const* tmpl, + tag_t<Key...>, + tag_t<Mapped...>) noexcept { + static Ptr const ptrs[] = {&typeid(Key)..., &typeid(Mapped)...}; + auto& global = createGlobal<Value, tag_t<Tag, Key...>>(); + enforce(tmpl, ptrs, sizeof...(Key), sizeof...(Mapped), global); + } + + UniqueInstance(UniqueInstance const&) = delete; + UniqueInstance(UniqueInstance&&) = delete; + UniqueInstance& operator=(UniqueInstance const&) = delete; + UniqueInstance& operator=(UniqueInstance&&) = delete; + + private: + struct Tag {}; + + using Ptr = std::type_info const*; + struct PtrRange { + Ptr const* b; + Ptr const* e; + }; + struct Value { + char const* tmpl; + Ptr const* ptrs; + std::uint32_t key_size; + std::uint32_t mapped_size; + }; + + // Under Clang, this call signature shrinks the aligned and padded size of + // call-sites, as compared to a call signature taking Value or Value const&. + static void enforce( + char const* tmpl, + Ptr const* ptrs, + std::uint32_t key_size, + std::uint32_t mapped_size, + Value& global) noexcept; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/dynamic-inl.h b/ios/Pods/Flipper-Folly/folly/dynamic-inl.h new file mode 100644 index 000000000..63916c47e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/dynamic-inl.h @@ -0,0 +1,1265 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> + +#include <folly/CPortability.h> +#include <folly/Conv.h> +#include <folly/Format.h> +#include <folly/Likely.h> +#include <folly/detail/Iterators.h> +#include <folly/lang/Exception.h> + +namespace folly { +namespace detail { +struct DynamicHasher { + using is_transparent = void; + + size_t operator()(dynamic const& d) const { + return d.hash(); + } + + template <typename T> + std::enable_if_t<std::is_convertible<T, StringPiece>::value, size_t> + operator()(T const& val) const { + // keep consistent with dynamic::hash() for strings + return Hash()(static_cast<StringPiece>(val)); + } +}; + +struct DynamicKeyEqual { + using is_transparent = void; + + bool operator()(const dynamic& lhs, const dynamic& rhs) const { + return std::equal_to<dynamic>()(lhs, rhs); + } + + // Dynamic objects contains a map<dynamic, dynamic>. At least one of the + // operands should be a dynamic. Hence, an operator() where both operands are + // convertible to StringPiece is unnecessary. + template <typename A, typename B> + std::enable_if_t< + std::is_convertible<A, StringPiece>::value && + std::is_convertible<B, StringPiece>::value, + bool> + operator()(A const& lhs, B const& rhs) const = delete; + + template <typename A> + std::enable_if_t<std::is_convertible<A, StringPiece>::value, bool> operator()( + A const& lhs, + dynamic const& rhs) const { + return FOLLY_LIKELY(rhs.type() == dynamic::Type::STRING) && + std::equal_to<StringPiece>()(lhs, rhs.stringPiece()); + } + + template <typename B> + std::enable_if_t<std::is_convertible<B, StringPiece>::value, bool> operator()( + dynamic const& lhs, + B const& rhs) const { + return FOLLY_LIKELY(lhs.type() == dynamic::Type::STRING) && + std::equal_to<StringPiece>()(lhs.stringPiece(), rhs); + } +}; +} // namespace detail +} // namespace folly + +////////////////////////////////////////////////////////////////////// + +namespace std { + +template <> +struct hash<::folly::dynamic> { + size_t operator()(::folly::dynamic const& d) const { + return d.hash(); + } +}; + +} // namespace std + +////////////////////////////////////////////////////////////////////// + +// This is a higher-order preprocessor macro to aid going from runtime +// types to the compile time type system. +#define FB_DYNAMIC_APPLY(type, apply) \ + do { \ + switch ((type)) { \ + case NULLT: \ + apply(std::nullptr_t); \ + break; \ + case ARRAY: \ + apply(Array); \ + break; \ + case BOOL: \ + apply(bool); \ + break; \ + case DOUBLE: \ + apply(double); \ + break; \ + case INT64: \ + apply(int64_t); \ + break; \ + case OBJECT: \ + apply(ObjectImpl); \ + break; \ + case STRING: \ + apply(std::string); \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +////////////////////////////////////////////////////////////////////// + +namespace folly { + +struct FOLLY_EXPORT TypeError : std::runtime_error { + explicit TypeError(const std::string& expected, dynamic::Type actual); + explicit TypeError( + const std::string& expected, + dynamic::Type actual1, + dynamic::Type actual2); +}; + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +// This helper is used in destroy() to be able to run destructors on +// types like "int64_t" without a compiler error. +struct Destroy { + template <class T> + static void destroy(T* t) { + t->~T(); + } +}; + +/* + * Helper for implementing numeric conversions in operators on + * numbers. Just promotes to double when one of the arguments is + * double, or throws if either is not a numeric type. + */ +template <template <class> class Op> +dynamic numericOp(dynamic const& a, dynamic const& b) { + if (!a.isNumber() || !b.isNumber()) { + throw_exception<TypeError>("numeric", a.type(), b.type()); + } + if (a.isDouble() || b.isDouble()) { + return Op<double>()(a.asDouble(), b.asDouble()); + } + return Op<int64_t>()(a.asInt(), b.asInt()); +} + +} // namespace detail + +////////////////////////////////////////////////////////////////////// + +/* + * We're doing this instead of a simple member typedef to avoid the + * undefined behavior of parameterizing F14NodeMap<> with an + * incomplete type. + * + * Note: Later we may add separate order tracking here (a multi-index + * type of thing.) + */ +struct dynamic::ObjectImpl : F14NodeMap< + dynamic, + dynamic, + detail::DynamicHasher, + detail::DynamicKeyEqual> {}; + +////////////////////////////////////////////////////////////////////// + +// Helper object for creating objects conveniently. See object and +// the dynamic::dynamic(ObjectMaker&&) ctor. +struct dynamic::ObjectMaker { + friend struct dynamic; + + explicit ObjectMaker() : val_(dynamic::object) {} + explicit ObjectMaker(dynamic key, dynamic val) : val_(dynamic::object) { + val_.insert(std::move(key), std::move(val)); + } + + // Make sure no one tries to save one of these into an lvalue with + // auto or anything like that. + ObjectMaker(ObjectMaker&&) = default; + ObjectMaker(ObjectMaker const&) = delete; + ObjectMaker& operator=(ObjectMaker const&) = delete; + ObjectMaker& operator=(ObjectMaker&&) = delete; + + // This returns an rvalue-reference instead of an lvalue-reference + // to allow constructs like this to moved instead of copied: + // dynamic a = dynamic::object("a", "b")("c", "d") + ObjectMaker&& operator()(dynamic key, dynamic val) { + val_.insert(std::move(key), std::move(val)); + return std::move(*this); + } + + private: + dynamic val_; +}; + +inline void dynamic::array(EmptyArrayTag) {} + +template <class... Args> +inline dynamic dynamic::array(Args&&... args) { + return dynamic(Array{std::forward<Args>(args)...}); +} + +inline dynamic::ObjectMaker dynamic::object() { + return ObjectMaker(); +} +inline dynamic::ObjectMaker dynamic::object(dynamic a, dynamic b) { + return ObjectMaker(std::move(a), std::move(b)); +} + +////////////////////////////////////////////////////////////////////// + +struct dynamic::item_iterator : detail::IteratorAdaptor< + dynamic::item_iterator, + dynamic::ObjectImpl::iterator, + std::pair<dynamic const, dynamic>, + std::forward_iterator_tag> { + using Super = detail::IteratorAdaptor< + dynamic::item_iterator, + dynamic::ObjectImpl::iterator, + std::pair<dynamic const, dynamic>, + std::forward_iterator_tag>; + /* implicit */ item_iterator(dynamic::ObjectImpl::iterator b) : Super(b) {} + + using object_type = dynamic::ObjectImpl; +}; + +struct dynamic::value_iterator : detail::IteratorAdaptor< + dynamic::value_iterator, + dynamic::ObjectImpl::iterator, + dynamic, + std::forward_iterator_tag> { + using Super = detail::IteratorAdaptor< + dynamic::value_iterator, + dynamic::ObjectImpl::iterator, + dynamic, + std::forward_iterator_tag>; + /* implicit */ value_iterator(dynamic::ObjectImpl::iterator b) : Super(b) {} + + using object_type = dynamic::ObjectImpl; + + dynamic& dereference() const { + return base()->second; + } +}; + +struct dynamic::const_item_iterator + : detail::IteratorAdaptor< + dynamic::const_item_iterator, + dynamic::ObjectImpl::const_iterator, + std::pair<dynamic const, dynamic> const, + std::forward_iterator_tag> { + using Super = detail::IteratorAdaptor< + dynamic::const_item_iterator, + dynamic::ObjectImpl::const_iterator, + std::pair<dynamic const, dynamic> const, + std::forward_iterator_tag>; + /* implicit */ const_item_iterator(dynamic::ObjectImpl::const_iterator b) + : Super(b) {} + /* implicit */ const_item_iterator(const_item_iterator const& i) + : Super(i.base()) {} + /* implicit */ const_item_iterator(item_iterator i) : Super(i.base()) {} + + using object_type = dynamic::ObjectImpl const; +}; + +struct dynamic::const_key_iterator : detail::IteratorAdaptor< + dynamic::const_key_iterator, + dynamic::ObjectImpl::const_iterator, + dynamic const, + std::forward_iterator_tag> { + using Super = detail::IteratorAdaptor< + dynamic::const_key_iterator, + dynamic::ObjectImpl::const_iterator, + dynamic const, + std::forward_iterator_tag>; + /* implicit */ const_key_iterator(dynamic::ObjectImpl::const_iterator b) + : Super(b) {} + + using object_type = dynamic::ObjectImpl const; + + dynamic const& dereference() const { + return base()->first; + } +}; + +struct dynamic::const_value_iterator : detail::IteratorAdaptor< + dynamic::const_value_iterator, + dynamic::ObjectImpl::const_iterator, + dynamic const, + std::forward_iterator_tag> { + using Super = detail::IteratorAdaptor< + dynamic::const_value_iterator, + dynamic::ObjectImpl::const_iterator, + dynamic const, + std::forward_iterator_tag>; + /* implicit */ const_value_iterator(dynamic::ObjectImpl::const_iterator b) + : Super(b) {} + /* implicit */ const_value_iterator(value_iterator i) : Super(i.base()) {} + /* implicit */ const_value_iterator(dynamic::ObjectImpl::iterator i) + : Super(i) {} + + using object_type = dynamic::ObjectImpl const; + + dynamic const& dereference() const { + return base()->second; + } +}; + +////////////////////////////////////////////////////////////////////// + +inline dynamic::dynamic() : dynamic(nullptr) {} + +inline dynamic::dynamic(std::nullptr_t) : type_(NULLT) {} + +inline dynamic::dynamic(void (*)(EmptyArrayTag)) : type_(ARRAY) { + new (&u_.array) Array(); +} + +inline dynamic::dynamic(ObjectMaker (*)()) : type_(OBJECT) { + new (getAddress<ObjectImpl>()) ObjectImpl(); +} + +inline dynamic::dynamic(StringPiece s) : type_(STRING) { + new (&u_.string) std::string(s.data(), s.size()); +} + +inline dynamic::dynamic(char const* s) : type_(STRING) { + new (&u_.string) std::string(s); +} + +inline dynamic::dynamic(std::string s) : type_(STRING) { + new (&u_.string) std::string(std::move(s)); +} + +inline dynamic::dynamic(ObjectMaker&& maker) : type_(OBJECT) { + new (getAddress<ObjectImpl>()) + ObjectImpl(std::move(*maker.val_.getAddress<ObjectImpl>())); +} + +inline dynamic::dynamic(dynamic const& o) : type_(NULLT) { + *this = o; +} + +inline dynamic::dynamic(dynamic&& o) noexcept : type_(NULLT) { + *this = std::move(o); +} + +inline dynamic::~dynamic() noexcept { + destroy(); +} + +// Integral types except bool convert to int64_t, float types to double. +template <class T> +struct dynamic::NumericTypeHelper< + T, + typename std::enable_if<std::is_integral<T>::value>::type> { + static_assert( + !kIsObjC || sizeof(T) > sizeof(char), + "char-sized types are ambiguous in objc; cast to bool or wider type"); + using type = int64_t; +}; +template <> +struct dynamic::NumericTypeHelper<bool> { + using type = bool; +}; +template <> +struct dynamic::NumericTypeHelper<float> { + using type = double; +}; +template <> +struct dynamic::NumericTypeHelper<double> { + using type = double; +}; + +inline dynamic::dynamic(std::vector<bool>::reference b) + : dynamic(static_cast<bool>(b)) {} +inline dynamic::dynamic(VectorBoolConstRefCtorType b) + : dynamic(static_cast<bool>(b)) {} + +template < + class T, + class NumericType /* = typename NumericTypeHelper<T>::type */> +dynamic::dynamic(T t) { + type_ = TypeInfo<NumericType>::type; + new (getAddress<NumericType>()) NumericType(NumericType(t)); +} + +template <class Iterator> +dynamic::dynamic(Iterator first, Iterator last) : type_(ARRAY) { + new (&u_.array) Array(first, last); +} + +////////////////////////////////////////////////////////////////////// + +inline dynamic::const_iterator dynamic::begin() const { + return get<Array>().begin(); +} +inline dynamic::const_iterator dynamic::end() const { + return get<Array>().end(); +} + +inline dynamic::iterator dynamic::begin() { + return get<Array>().begin(); +} +inline dynamic::iterator dynamic::end() { + return get<Array>().end(); +} + +template <class It> +struct dynamic::IterableProxy { + typedef It iterator; + typedef typename It::value_type value_type; + typedef typename It::object_type object_type; + + /* implicit */ IterableProxy(object_type* o) : o_(o) {} + + It begin() const { + return o_->begin(); + } + + It end() const { + return o_->end(); + } + + private: + object_type* o_; +}; + +inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys() + const { + return &(get<ObjectImpl>()); +} + +inline dynamic::IterableProxy<dynamic::const_value_iterator> dynamic::values() + const { + return &(get<ObjectImpl>()); +} + +inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items() + const { + return &(get<ObjectImpl>()); +} + +inline dynamic::IterableProxy<dynamic::value_iterator> dynamic::values() { + return &(get<ObjectImpl>()); +} + +inline dynamic::IterableProxy<dynamic::item_iterator> dynamic::items() { + return &(get<ObjectImpl>()); +} + +inline bool dynamic::isString() const { + return get_nothrow<std::string>() != nullptr; +} +inline bool dynamic::isObject() const { + return get_nothrow<ObjectImpl>() != nullptr; +} +inline bool dynamic::isBool() const { + return get_nothrow<bool>() != nullptr; +} +inline bool dynamic::isArray() const { + return get_nothrow<Array>() != nullptr; +} +inline bool dynamic::isDouble() const { + return get_nothrow<double>() != nullptr; +} +inline bool dynamic::isInt() const { + return get_nothrow<int64_t>() != nullptr; +} +inline bool dynamic::isNull() const { + return get_nothrow<std::nullptr_t>() != nullptr; +} +inline bool dynamic::isNumber() const { + return isInt() || isDouble(); +} + +inline dynamic::Type dynamic::type() const { + return type_; +} + +inline std::string dynamic::asString() const { + return asImpl<std::string>(); +} +inline double dynamic::asDouble() const { + return asImpl<double>(); +} +inline int64_t dynamic::asInt() const { + return asImpl<int64_t>(); +} +inline bool dynamic::asBool() const { + return asImpl<bool>(); +} + +inline const std::string& dynamic::getString() const& { + return get<std::string>(); +} +inline double dynamic::getDouble() const& { + return get<double>(); +} +inline int64_t dynamic::getInt() const& { + return get<int64_t>(); +} +inline bool dynamic::getBool() const& { + return get<bool>(); +} + +inline std::string& dynamic::getString() & { + return get<std::string>(); +} +inline double& dynamic::getDouble() & { + return get<double>(); +} +inline int64_t& dynamic::getInt() & { + return get<int64_t>(); +} +inline bool& dynamic::getBool() & { + return get<bool>(); +} + +inline std::string&& dynamic::getString() && { + return std::move(get<std::string>()); +} +inline double dynamic::getDouble() && { + return get<double>(); +} +inline int64_t dynamic::getInt() && { + return get<int64_t>(); +} +inline bool dynamic::getBool() && { + return get<bool>(); +} + +inline const char* dynamic::data() const& { + return get<std::string>().data(); +} +inline const char* dynamic::c_str() const& { + return get<std::string>().c_str(); +} +inline StringPiece dynamic::stringPiece() const { + return get<std::string>(); +} + +template <class T> +struct dynamic::CompareOp { + static bool comp(T const& a, T const& b) { + return a < b; + } +}; +template <> +struct dynamic::CompareOp<dynamic::ObjectImpl> { + static bool comp(ObjectImpl const&, ObjectImpl const&) { + // This code never executes; it is just here for the compiler. + return false; + } +}; +template <> +struct dynamic::CompareOp<std::nullptr_t> { + static bool comp(std::nullptr_t const&, std::nullptr_t const&) { + return true; + } +}; + +inline dynamic& dynamic::operator+=(dynamic const& o) { + if (type() == STRING && o.type() == STRING) { + *getAddress<std::string>() += *o.getAddress<std::string>(); + return *this; + } + *this = detail::numericOp<std::plus>(*this, o); + return *this; +} + +inline dynamic& dynamic::operator-=(dynamic const& o) { + *this = detail::numericOp<std::minus>(*this, o); + return *this; +} + +inline dynamic& dynamic::operator*=(dynamic const& o) { + *this = detail::numericOp<std::multiplies>(*this, o); + return *this; +} + +inline dynamic& dynamic::operator/=(dynamic const& o) { + *this = detail::numericOp<std::divides>(*this, o); + return *this; +} + +#define FB_DYNAMIC_INTEGER_OP(op) \ + inline dynamic& dynamic::operator op(dynamic const& o) { \ + if (!isInt() || !o.isInt()) { \ + throw_exception<TypeError>("int64", type(), o.type()); \ + } \ + *getAddress<int64_t>() op o.asInt(); \ + return *this; \ + } + +FB_DYNAMIC_INTEGER_OP(%=) +FB_DYNAMIC_INTEGER_OP(|=) +FB_DYNAMIC_INTEGER_OP(&=) +FB_DYNAMIC_INTEGER_OP(^=) + +#undef FB_DYNAMIC_INTEGER_OP + +inline dynamic& dynamic::operator++() { + ++get<int64_t>(); + return *this; +} + +inline dynamic& dynamic::operator--() { + --get<int64_t>(); + return *this; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic const&> dynamic::operator[]( + K&& idx) const& { + return at(std::forward<K>(idx)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::operator[]( + K&& idx) & { + if (!isObject() && !isArray()) { + throw_exception<TypeError>("object/array", type()); + } + if (isArray()) { + return at(std::forward<K>(idx)); + } + auto& obj = get<ObjectImpl>(); + auto ret = obj.emplace(std::forward<K>(idx), nullptr); + return ret.first->second; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&&> dynamic::operator[]( + K&& idx) && { + return std::move((*this)[std::forward<K>(idx)]); +} + +inline dynamic const& dynamic::operator[](StringPiece k) const& { + return at(k); +} + +inline dynamic&& dynamic::operator[](StringPiece k) && { + return std::move((*this)[k]); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault( + K&& k, + const dynamic& v) const& { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(std::forward<K>(k)); + return it == obj.end() ? v : it->second; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault( + K&& k, + dynamic&& v) const& { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(std::forward<K>(k)); + // Avoid clang bug with ternary + if (it == obj.end()) { + return std::move(v); + } else { + return it->second; + } +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault( + K&& k, + const dynamic& v) && { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(std::forward<K>(k)); + // Avoid clang bug with ternary + if (it == obj.end()) { + return v; + } else { + return std::move(it->second); + } +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault( + K&& k, + dynamic&& v) && { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(std::forward<K>(k)); + return std::move(it == obj.end() ? v : it->second); +} + +template <typename K, typename V> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault( + K&& k, + V&& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(std::forward<K>(k), std::forward<V>(v)).first->second; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault( + K&& k, + dynamic&& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(std::forward<K>(k), std::move(v)).first->second; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault( + K&& k, + const dynamic& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(std::forward<K>(k), v).first->second; +} + +template <typename V> +dynamic& dynamic::setDefault(StringPiece k, V&& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(k, std::forward<V>(v)).first->second; +} + +inline dynamic& dynamic::setDefault(StringPiece k, dynamic&& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(k, std::move(v)).first->second; +} + +inline dynamic& dynamic::setDefault(StringPiece k, const dynamic& v) { + auto& obj = get<ObjectImpl>(); + return obj.emplace(k, v).first->second; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic const*> dynamic::get_ptr( + K&& k) const& { + return get_ptrImpl(std::forward<K>(k)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic*> dynamic::get_ptr( + K&& idx) & { + return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx)); +} + +inline dynamic* dynamic::get_ptr(StringPiece idx) & { + return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx)); +} + +// clang-format off +inline +dynamic::resolved_json_pointer<dynamic> +dynamic::try_get_ptr(json_pointer const& jsonPtr) & { + auto ret = const_cast<dynamic const*>(this)->try_get_ptr(jsonPtr); + if (ret.hasValue()) { + return json_pointer_resolved_value<dynamic>{ + const_cast<dynamic*>(ret.value().parent), + const_cast<dynamic*>(ret.value().value), + ret.value().parent_key, ret.value().parent_index}; + } else { + return makeUnexpected( + json_pointer_resolution_error<dynamic>{ + ret.error().error_code, + ret.error().index, + const_cast<dynamic*>(ret.error().context)} + ); + } +} +// clang-format on + +inline dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) & { + return const_cast<dynamic*>( + const_cast<dynamic const*>(this)->get_ptr(jsonPtr)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic const&> dynamic::at( + K&& k) const& { + return atImpl(std::forward<K>(k)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::at(K&& idx) & { + return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic&&> dynamic::at(K&& idx) && { + return std::move(at(idx)); +} + +inline dynamic& dynamic::at(StringPiece idx) & { + return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx)); +} + +inline dynamic&& dynamic::at(StringPiece idx) && { + return std::move(at(idx)); +} + +inline bool dynamic::empty() const { + if (isNull()) { + return true; + } + return !size(); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic::const_item_iterator> +dynamic::find(K&& key) const { + return get<ObjectImpl>().find(std::forward<K>(key)); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, dynamic::item_iterator> +dynamic::find(K&& key) { + return get<ObjectImpl>().find(std::forward<K>(key)); +} + +inline dynamic::const_item_iterator dynamic::find(StringPiece key) const { + return get<ObjectImpl>().find(key); +} + +inline dynamic::item_iterator dynamic::find(StringPiece key) { + return get<ObjectImpl>().find(key); +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, std::size_t> dynamic::count( + K&& key) const { + return find(std::forward<K>(key)) != items().end() ? 1u : 0u; +} + +inline std::size_t dynamic::count(StringPiece key) const { + return find(key) != items().end() ? 1u : 0u; +} + +template <class K, class V> +inline dynamic::IfNotIterator<K, void> dynamic::insert(K&& key, V&& val) { + auto& obj = get<ObjectImpl>(); + obj[std::forward<K>(key)] = std::forward<V>(val); +} + +template <class T> +inline dynamic::iterator dynamic::insert(const_iterator pos, T&& value) { + auto& arr = get<Array>(); + return arr.insert(pos, std::forward<T>(value)); +} + +inline void dynamic::update(const dynamic& mergeObj) { + if (!isObject() || !mergeObj.isObject()) { + throw_exception<TypeError>("object", type(), mergeObj.type()); + } + + for (const auto& pair : mergeObj.items()) { + (*this)[pair.first] = pair.second; + } +} + +inline void dynamic::update_missing(const dynamic& mergeObj1) { + if (!isObject() || !mergeObj1.isObject()) { + throw_exception<TypeError>("object", type(), mergeObj1.type()); + } + + // Only add if not already there + for (const auto& pair : mergeObj1.items()) { + if ((*this).find(pair.first) == (*this).items().end()) { + (*this)[pair.first] = pair.second; + } + } +} + +inline void dynamic::merge_patch(const dynamic& patch) { + auto& self = *this; + if (!patch.isObject()) { + self = patch; + return; + } + // if we are not an object, erase all contents, reset to object + if (!isObject()) { + self = object; + } + for (const auto& pair : patch.items()) { + if (pair.second.isNull()) { + // if name could be found in current object, remove it + auto it = self.find(pair.first); + if (it != self.items().end()) { + self.erase(it); + } + } else { + self[pair.first].merge_patch(pair.second); + } + } +} + +inline dynamic dynamic::merge( + const dynamic& mergeObj1, + const dynamic& mergeObj2) { + // No checks on type needed here because they are done in update_missing + // Note that we do update_missing here instead of update() because + // it will prevent the extra writes that would occur with update() + auto ret = mergeObj2; + ret.update_missing(mergeObj1); + return ret; +} + +template <typename K> +dynamic::IfIsNonStringDynamicConvertible<K, std::size_t> dynamic::erase( + K&& key) { + auto& obj = get<ObjectImpl>(); + return obj.erase(std::forward<K>(key)); +} +inline std::size_t dynamic::erase(StringPiece key) { + auto& obj = get<ObjectImpl>(); + return obj.erase(key); +} + +inline dynamic::iterator dynamic::erase(const_iterator it) { + auto& arr = get<Array>(); + // std::vector doesn't have an erase method that works on const iterators, + // even though the standard says it should, so this hack converts to a + // non-const iterator before calling erase. + return get<Array>().erase(arr.begin() + (it - arr.begin())); +} + +inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) { + return const_key_iterator(get<ObjectImpl>().erase(it.base())); +} + +inline dynamic::const_key_iterator dynamic::erase( + const_key_iterator first, + const_key_iterator last) { + return const_key_iterator(get<ObjectImpl>().erase(first.base(), last.base())); +} + +inline dynamic::value_iterator dynamic::erase(const_value_iterator it) { + return value_iterator(get<ObjectImpl>().erase(it.base())); +} + +inline dynamic::value_iterator dynamic::erase( + const_value_iterator first, + const_value_iterator last) { + return value_iterator(get<ObjectImpl>().erase(first.base(), last.base())); +} + +inline dynamic::item_iterator dynamic::erase(const_item_iterator it) { + return item_iterator(get<ObjectImpl>().erase(it.base())); +} + +inline dynamic::item_iterator dynamic::erase( + const_item_iterator first, + const_item_iterator last) { + return item_iterator(get<ObjectImpl>().erase(first.base(), last.base())); +} + +inline void dynamic::resize(std::size_t sz, dynamic const& c) { + auto& arr = get<Array>(); + arr.resize(sz, c); +} + +inline void dynamic::push_back(dynamic const& v) { + auto& arr = get<Array>(); + arr.push_back(v); +} + +inline void dynamic::push_back(dynamic&& v) { + auto& arr = get<Array>(); + arr.push_back(std::move(v)); +} + +inline void dynamic::pop_back() { + auto& arr = get<Array>(); + arr.pop_back(); +} + +inline const dynamic& dynamic::back() const { + auto& arr = get<Array>(); + return arr.back(); +} + +////////////////////////////////////////////////////////////////////// + +inline dynamic::dynamic(Array&& r) : type_(ARRAY) { + new (&u_.array) Array(std::move(r)); +} + +#define FOLLY_DYNAMIC_DEC_TYPEINFO(T, val) \ + template <> \ + struct dynamic::TypeInfo<T> { \ + static const char* const name; \ + static constexpr dynamic::Type type = val; \ + }; \ + // + +FOLLY_DYNAMIC_DEC_TYPEINFO(std::nullptr_t, dynamic::NULLT) +FOLLY_DYNAMIC_DEC_TYPEINFO(bool, dynamic::BOOL) +FOLLY_DYNAMIC_DEC_TYPEINFO(std::string, dynamic::STRING) +FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::Array, dynamic::ARRAY) +FOLLY_DYNAMIC_DEC_TYPEINFO(double, dynamic::DOUBLE) +FOLLY_DYNAMIC_DEC_TYPEINFO(int64_t, dynamic::INT64) +FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::ObjectImpl, dynamic::OBJECT) + +#undef FOLLY_DYNAMIC_DEC_TYPEINFO + +template <class T> +T dynamic::asImpl() const { + switch (type()) { + case INT64: + return to<T>(*get_nothrow<int64_t>()); + case DOUBLE: + return to<T>(*get_nothrow<double>()); + case BOOL: + return to<T>(*get_nothrow<bool>()); + case STRING: + return to<T>(*get_nothrow<std::string>()); + case NULLT: + case ARRAY: + case OBJECT: + default: + throw_exception<TypeError>("int/double/bool/string", type()); + } +} + +// Return a T* to our type, or null if we're not that type. +// clang-format off +template <class T> +T* dynamic::get_nothrow() & noexcept { + if (type_ != TypeInfo<T>::type) { + return nullptr; + } + return getAddress<T>(); +} +// clang-format on + +template <class T> +T const* dynamic::get_nothrow() const& noexcept { + return const_cast<dynamic*>(this)->get_nothrow<T>(); +} + +// Return T* for where we can put a T, without type checking. (Memory +// might be uninitialized, even.) +template <class T> +T* dynamic::getAddress() noexcept { + return GetAddrImpl<T>::get(u_); +} + +template <class T> +T const* dynamic::getAddress() const noexcept { + return const_cast<dynamic*>(this)->getAddress<T>(); +} + +template <class T> +struct dynamic::GetAddrImpl {}; +template <> +struct dynamic::GetAddrImpl<std::nullptr_t> { + static std::nullptr_t* get(Data& d) noexcept { + return &d.nul; + } +}; +template <> +struct dynamic::GetAddrImpl<dynamic::Array> { + static Array* get(Data& d) noexcept { + return &d.array; + } +}; +template <> +struct dynamic::GetAddrImpl<bool> { + static bool* get(Data& d) noexcept { + return &d.boolean; + } +}; +template <> +struct dynamic::GetAddrImpl<int64_t> { + static int64_t* get(Data& d) noexcept { + return &d.integer; + } +}; +template <> +struct dynamic::GetAddrImpl<double> { + static double* get(Data& d) noexcept { + return &d.doubl; + } +}; +template <> +struct dynamic::GetAddrImpl<std::string> { + static std::string* get(Data& d) noexcept { + return &d.string; + } +}; +template <> +struct dynamic::GetAddrImpl<dynamic::ObjectImpl> { + static_assert( + sizeof(ObjectImpl) <= sizeof(Data::objectBuffer), + "In your implementation, F14NodeMap<> apparently takes different" + " amount of space depending on its template parameters. This is " + "weird. Make objectBuffer bigger if you want to compile dynamic."); + + static ObjectImpl* get(Data& d) noexcept { + void* data = &d.objectBuffer; + return static_cast<ObjectImpl*>(data); + } +}; + +template <class T> +T& dynamic::get() { + if (auto* p = get_nothrow<T>()) { + return *p; + } + throw_exception<TypeError>(TypeInfo<T>::name, type()); +} + +template <class T> +T const& dynamic::get() const { + return const_cast<dynamic*>(this)->get<T>(); +} + +////////////////////////////////////////////////////////////////////// + +/* + * Helper for implementing operator<<. Throws if the type shouldn't + * support it. + */ +template <class T> +struct dynamic::PrintImpl { + static void print(dynamic const&, std::ostream& out, T const& t) { + out << t; + } +}; +// Otherwise, null, being (void*)0, would print as 0. +template <> +struct dynamic::PrintImpl<std::nullptr_t> { + static void + print(dynamic const& /* d */, std::ostream& out, std::nullptr_t const&) { + out << "null"; + } +}; +template <> +struct dynamic::PrintImpl<dynamic::ObjectImpl> { + static void + print(dynamic const& d, std::ostream& out, dynamic::ObjectImpl const&) { + d.print_as_pseudo_json(out); + } +}; +template <> +struct dynamic::PrintImpl<dynamic::Array> { + static void + print(dynamic const& d, std::ostream& out, dynamic::Array const&) { + d.print_as_pseudo_json(out); + } +}; + +inline void dynamic::print(std::ostream& out) const { +#define FB_X(T) PrintImpl<T>::print(*this, out, *getAddress<T>()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X +} + +inline std::ostream& operator<<(std::ostream& out, dynamic const& d) { + d.print(out); + return out; +} + +////////////////////////////////////////////////////////////////////// + +// Secialization of FormatValue so dynamic objects can be formatted +template <> +class FormatValue<dynamic> { + public: + explicit FormatValue(const dynamic& val) : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + switch (val_.type()) { + case dynamic::NULLT: + FormatValue<std::nullptr_t>(nullptr).format(arg, cb); + break; + case dynamic::BOOL: + FormatValue<bool>(val_.asBool()).format(arg, cb); + break; + case dynamic::INT64: + FormatValue<int64_t>(val_.asInt()).format(arg, cb); + break; + case dynamic::STRING: + FormatValue<std::string>(val_.asString()).format(arg, cb); + break; + case dynamic::DOUBLE: + FormatValue<double>(val_.asDouble()).format(arg, cb); + break; + case dynamic::ARRAY: + FormatValue(val_.at(arg.splitIntKey())).format(arg, cb); + break; + case dynamic::OBJECT: + FormatValue(val_.at(arg.splitKey().toString())).format(arg, cb); + break; + } + } + + private: + const dynamic& val_; +}; + +template <class V> +class FormatValue<detail::DefaultValueWrapper<dynamic, V>> { + public: + explicit FormatValue(const detail::DefaultValueWrapper<dynamic, V>& val) + : val_(val) {} + + template <class FormatCallback> + void format(FormatArg& arg, FormatCallback& cb) const { + auto& c = val_.container; + switch (c.type()) { + case dynamic::NULLT: + case dynamic::BOOL: + case dynamic::INT64: + case dynamic::STRING: + case dynamic::DOUBLE: + FormatValue<dynamic>(c).format(arg, cb); + break; + case dynamic::ARRAY: { + int key = arg.splitIntKey(); + if (key >= 0 && size_t(key) < c.size()) { + FormatValue<dynamic>(c.at(key)).format(arg, cb); + } else { + FormatValue<V>(val_.defaultValue).format(arg, cb); + } + break; + } + case dynamic::OBJECT: { + auto pos = c.find(arg.splitKey()); + if (pos != c.items().end()) { + FormatValue<dynamic>(pos->second).format(arg, cb); + } else { + FormatValue<V>(val_.defaultValue).format(arg, cb); + } + break; + } + } + } + + private: + const detail::DefaultValueWrapper<dynamic, V>& val_; +}; + +} // namespace folly + +#undef FB_DYNAMIC_APPLY diff --git a/ios/Pods/Flipper-Folly/folly/dynamic.cpp b/ios/Pods/Flipper-Folly/folly/dynamic.cpp new file mode 100644 index 000000000..3d8a00003 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/dynamic.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/dynamic.h> + +#include <numeric> + +#include <glog/logging.h> + +#include <folly/Format.h> +#include <folly/container/Enumerate.h> +#include <folly/hash/Hash.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Exception.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +#define FOLLY_DYNAMIC_DEF_TYPEINFO(T, str) \ + const char* const dynamic::TypeInfo<T>::name = str; \ + // + +FOLLY_DYNAMIC_DEF_TYPEINFO(std::nullptr_t, "null") +FOLLY_DYNAMIC_DEF_TYPEINFO(bool, "boolean") +FOLLY_DYNAMIC_DEF_TYPEINFO(std::string, "string") +FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array, "array") +FOLLY_DYNAMIC_DEF_TYPEINFO(double, "double") +FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t, "int64") +FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl, "object") + +#undef FOLLY_DYNAMIC_DEF_TYPEINFO + +const char* dynamic::typeName() const { + return typeName(type_); +} + +TypeError::TypeError(const std::string& expected, dynamic::Type actual) + : std::runtime_error(sformat( + "TypeError: expected dynamic type `{}', but had type `{}'", + expected, + dynamic::typeName(actual))) {} + +TypeError::TypeError( + const std::string& expected, + dynamic::Type actual1, + dynamic::Type actual2) + : std::runtime_error(sformat( + "TypeError: expected dynamic types `{}, but had types `{}' and `{}'", + expected, + dynamic::typeName(actual1), + dynamic::typeName(actual2))) {} + +// This is a higher-order preprocessor macro to aid going from runtime +// types to the compile time type system. +#define FB_DYNAMIC_APPLY(type, apply) \ + do { \ + switch ((type)) { \ + case NULLT: \ + apply(std::nullptr_t); \ + break; \ + case ARRAY: \ + apply(Array); \ + break; \ + case BOOL: \ + apply(bool); \ + break; \ + case DOUBLE: \ + apply(double); \ + break; \ + case INT64: \ + apply(int64_t); \ + break; \ + case OBJECT: \ + apply(ObjectImpl); \ + break; \ + case STRING: \ + apply(std::string); \ + break; \ + default: \ + CHECK(0); \ + abort(); \ + } \ + } while (0) + +bool dynamic::operator<(dynamic const& o) const { + if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) { + throw_exception<TypeError>("object", type_); + } + if (type_ != o.type_) { + return type_ < o.type_; + } + +#define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), *o.getAddress<T>()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X +} + +bool dynamic::operator==(dynamic const& o) const { + if (type() != o.type()) { + if (isNumber() && o.isNumber()) { + auto& integ = isInt() ? *this : o; + auto& doubl = isInt() ? o : *this; + return integ.asInt() == doubl.asDouble(); + } + return false; + } + +#define FB_X(T) return *getAddress<T>() == *o.getAddress<T>(); + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X +} + +dynamic& dynamic::operator=(dynamic const& o) { + if (&o != this) { + if (type_ == o.type_) { +#define FB_X(T) *getAddress<T>() = *o.getAddress<T>() + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + } else { + destroy(); +#define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>()) + FB_DYNAMIC_APPLY(o.type_, FB_X); +#undef FB_X + type_ = o.type_; + } + } + return *this; +} + +dynamic& dynamic::operator=(dynamic&& o) noexcept { + if (&o != this) { + if (type_ == o.type_) { +#define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + } else { + destroy(); +#define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>())) + FB_DYNAMIC_APPLY(o.type_, FB_X); +#undef FB_X + type_ = o.type_; + } + } + return *this; +} + +dynamic const& dynamic::atImpl(dynamic const& idx) const& { + if (auto* parray = get_nothrow<Array>()) { + if (!idx.isInt()) { + throw_exception<TypeError>("int64", idx.type()); + } + if (idx < 0 || idx >= parray->size()) { + throw_exception<std::out_of_range>("out of range in dynamic array"); + } + return (*parray)[size_t(idx.asInt())]; + } else if (auto* pobject = get_nothrow<ObjectImpl>()) { + auto it = pobject->find(idx); + if (it == pobject->end()) { + throw_exception<std::out_of_range>( + sformat("couldn't find key {} in dynamic object", idx.asString())); + } + return it->second; + } else { + throw_exception<TypeError>("object/array", type()); + } +} + +dynamic const& dynamic::at(StringPiece idx) const& { + auto* pobject = get_nothrow<ObjectImpl>(); + if (!pobject) { + throw_exception<TypeError>("object", type()); + } + auto it = pobject->find(idx); + if (it == pobject->end()) { + throw_exception<std::out_of_range>( + sformat("couldn't find key {} in dynamic object", idx)); + } + return it->second; +} + +dynamic& dynamic::operator[](StringPiece k) & { + auto& obj = get<ObjectImpl>(); + auto ret = obj.emplace(k, nullptr); + return ret.first->second; +} + +dynamic dynamic::getDefault(StringPiece k, const dynamic& v) const& { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(k); + return it == obj.end() ? v : it->second; +} + +dynamic dynamic::getDefault(StringPiece k, dynamic&& v) const& { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(k); + // Avoid clang bug with ternary + if (it == obj.end()) { + return std::move(v); + } else { + return it->second; + } +} + +dynamic dynamic::getDefault(StringPiece k, const dynamic& v) && { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(k); + // Avoid clang bug with ternary + if (it == obj.end()) { + return v; + } else { + return std::move(it->second); + } +} + +dynamic dynamic::getDefault(StringPiece k, dynamic&& v) && { + auto& obj = get<ObjectImpl>(); + auto it = obj.find(k); + return std::move(it == obj.end() ? v : it->second); +} + +const dynamic* dynamic::get_ptrImpl(dynamic const& idx) const& { + if (auto* parray = get_nothrow<Array>()) { + if (!idx.isInt()) { + throw_exception<TypeError>("int64", idx.type()); + } + if (idx < 0 || idx >= parray->size()) { + return nullptr; + } + return &(*parray)[size_t(idx.asInt())]; + } else if (auto* pobject = get_nothrow<ObjectImpl>()) { + auto it = pobject->find(idx); + if (it == pobject->end()) { + return nullptr; + } + return &it->second; + } else { + throw_exception<TypeError>("object/array", type()); + } +} + +const dynamic* dynamic::get_ptr(StringPiece idx) const& { + auto* pobject = get_nothrow<ObjectImpl>(); + if (!pobject) { + throw_exception<TypeError>("object", type()); + } + auto it = pobject->find(idx); + if (it == pobject->end()) { + return nullptr; + } + return &it->second; +} + +std::size_t dynamic::size() const { + if (auto* ar = get_nothrow<Array>()) { + return ar->size(); + } + if (auto* obj = get_nothrow<ObjectImpl>()) { + return obj->size(); + } + if (auto* str = get_nothrow<std::string>()) { + return str->size(); + } + throw_exception<TypeError>("array/object/string", type()); +} + +dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) { + auto& arr = get<Array>(); + return get<Array>().erase( + arr.begin() + (first - arr.begin()), arr.begin() + (last - arr.begin())); +} + +std::size_t dynamic::hash() const { + switch (type()) { + case NULLT: + return 0xBAAAAAAD; + case OBJECT: { + // Accumulate using addition instead of using hash_range (as in the ARRAY + // case), as we need a commutative hash operation since unordered_map's + // iteration order is unspecified. + auto h = std::hash<std::pair<dynamic const, dynamic>>{}; + return std::accumulate( + items().begin(), + items().end(), + size_t{0x0B1EC7}, + [&](auto acc, auto const& item) { return acc + h(item); }); + } + case ARRAY: + return folly::hash::hash_range(begin(), end()); + case INT64: + return std::hash<int64_t>()(getInt()); + case DOUBLE: + return std::hash<double>()(getDouble()); + case BOOL: + return std::hash<bool>()(getBool()); + case STRING: + // keep consistent with detail::DynamicHasher + return Hash()(getString()); + } + assume_unreachable(); +} + +char const* dynamic::typeName(Type t) { +#define FB_X(T) return TypeInfo<T>::name + FB_DYNAMIC_APPLY(t, FB_X); +#undef FB_X +} + +void dynamic::destroy() noexcept { + // This short-circuit speeds up some microbenchmarks. + if (type_ == NULLT) { + return; + } + +#define FB_X(T) detail::Destroy::destroy(getAddress<T>()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + type_ = NULLT; + u_.nul = nullptr; +} + +dynamic dynamic::merge_diff(const dynamic& source, const dynamic& target) { + if (!source.isObject() || source.type() != target.type()) { + return target; + } + + dynamic diff = object; + + // added/modified keys + for (const auto& pair : target.items()) { + auto it = source.find(pair.first); + if (it == source.items().end()) { + diff[pair.first] = pair.second; + } else { + diff[pair.first] = merge_diff(source[pair.first], target[pair.first]); + } + } + + // removed keys + for (const auto& pair : source.items()) { + auto it = target.find(pair.first); + if (it == target.items().end()) { + diff[pair.first] = nullptr; + } + } + + return diff; +} + +// clang-format off +dynamic::resolved_json_pointer<dynamic const> +// clang-format on +dynamic::try_get_ptr(json_pointer const& jsonPtr) const& { + using err_code = json_pointer_resolution_error_code; + using error = json_pointer_resolution_error<dynamic const>; + + auto const& tokens = jsonPtr.tokens(); + if (tokens.empty()) { + return json_pointer_resolved_value<dynamic const>{ + nullptr, this, {nullptr, nullptr}, 0}; + } + + dynamic const* curr = this; + dynamic const* prev = nullptr; + + size_t curr_idx{0}; + StringPiece curr_key{}; + + for (auto it : enumerate(tokens)) { + // hit bottom but pointer not exhausted yet + if (!curr) { + return makeUnexpected( + error{err_code::json_pointer_out_of_bounds, it.index, prev}); + } + prev = curr; + // handle lookup in array + if (auto const* parray = curr->get_nothrow<dynamic::Array>()) { + if (it->size() > 1 && it->at(0) == '0') { + return makeUnexpected( + error{err_code::index_has_leading_zero, it.index, prev}); + } + // if last element of pointer is '-', this is an append operation + if (it->size() == 1 && it->at(0) == '-') { + // was '-' the last token in pointer? + if (it.index == tokens.size() - 1) { + return makeUnexpected( + error{err_code::append_requested, it.index, prev}); + } + // Cannot resolve past '-' in an array + curr = nullptr; + continue; + } + auto const idx = tryTo<size_t>(*it); + if (!idx.hasValue()) { + return makeUnexpected( + error{err_code::index_not_numeric, it.index, prev}); + } + if (idx.value() < parray->size()) { + curr = &(*parray)[idx.value()]; + curr_idx = idx.value(); + } else { + return makeUnexpected( + error{err_code::index_out_of_bounds, it.index, prev}); + } + continue; + } + // handle lookup in object + if (auto const* pobject = curr->get_nothrow<dynamic::ObjectImpl>()) { + auto const sub_it = pobject->find(*it); + if (sub_it == pobject->end()) { + return makeUnexpected(error{err_code::key_not_found, it.index, prev}); + } + curr = &sub_it->second; + curr_key = *it; + continue; + } + return makeUnexpected( + error{err_code::element_not_object_or_array, it.index, prev}); + } + return json_pointer_resolved_value<dynamic const>{ + prev, curr, curr_key, curr_idx}; +} + +const dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) const& { + using err_code = json_pointer_resolution_error_code; + + auto ret = try_get_ptr(jsonPtr); + if (ret.hasValue()) { + return ret.value().value; + } + + auto const ctx = ret.error().context; + auto const objType = ctx ? ctx->type() : Type::NULLT; + + switch (ret.error().error_code) { + case err_code::key_not_found: + return nullptr; + case err_code::index_out_of_bounds: + return nullptr; + case err_code::append_requested: + return nullptr; + case err_code::index_not_numeric: + throw std::invalid_argument("array index is not numeric"); + case err_code::index_has_leading_zero: + throw std::invalid_argument( + "leading zero not allowed when indexing arrays"); + case err_code::element_not_object_or_array: + throw_exception<TypeError>("object/array", objType); + case err_code::json_pointer_out_of_bounds: + return nullptr; + case err_code::other: + default: + return nullptr; + } + assume_unreachable(); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/dynamic.h b/ios/Pods/Flipper-Folly/folly/dynamic.h new file mode 100644 index 000000000..bbca41099 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/dynamic.h @@ -0,0 +1,796 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 is a runtime dynamically typed value. It holds types from a + * specific predetermined set of types (ints, bools, arrays, etc). In + * particular, it can be used as a convenient in-memory representation + * for complete json objects. + * + * In general you can try to use these objects as if they were the + * type they represent (although in some cases with a slightly less + * complete interface than the raw type), and it'll just throw a + * TypeError if it is used in an illegal way. + * + * Some examples: + * + * dynamic twelve = 12; + * dynamic str = "string"; + * dynamic map = dynamic::object; + * map[str] = twelve; + * map[str + "another_str"] = dynamic::array("array", "of", 4, "elements"); + * map.insert("null_element", nullptr); + * ++map[str]; + * assert(map[str] == 13); + * + * // Building a complex object with a sub array inline: + * dynamic d = dynamic::object + * ("key", "value") + * ("key2", dynamic::array("a", "array")) + * ; + * + * Also see folly/json.h for the serialization and deserialization + * functions for JSON. + * + * Additional documentation is in folly/docs/Dynamic.md. + * + * @author Jordan DeLong <delong.j@fb.com> + */ + +#pragma once + +#include <cstdint> +#include <memory> +#include <ostream> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <boost/operators.hpp> + +#include <folly/Expected.h> +#include <folly/Range.h> +#include <folly/Traits.h> +#include <folly/container/F14Map.h> +#include <folly/json_pointer.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +struct dynamic; +struct TypeError; + +////////////////////////////////////////////////////////////////////// + +struct dynamic : private boost::operators<dynamic> { + enum Type { + NULLT, + ARRAY, + BOOL, + DOUBLE, + INT64, + OBJECT, + STRING, + }; + template <class T, class Enable = void> + struct NumericTypeHelper; + + /* + * We support direct iteration of arrays, and indirect iteration of objects. + * See begin(), end(), keys(), values(), and items() for more. + * + * Array iterators dereference as the elements in the array. + * Object key iterators dereference as the keys in the object. + * Object value iterators dereference as the values in the object. + * Object item iterators dereference as pairs of (key, value). + */ + private: + typedef std::vector<dynamic> Array; + + /* + * Violating spec, std::vector<bool>::const_reference is not bool in libcpp: + * http://howardhinnant.github.io/onvectorbool.html + * + * This is used to add a public ctor which is only enabled under libcpp taking + * std::vector<bool>::const_reference without using the preprocessor. + */ + struct VectorBoolConstRefFake : std::false_type {}; + using VectorBoolConstRefCtorType = std::conditional_t< + std::is_same<std::vector<bool>::const_reference, bool>::value, + VectorBoolConstRefFake, + std::vector<bool>::const_reference>; + + public: + typedef Array::iterator iterator; + typedef Array::const_iterator const_iterator; + typedef dynamic value_type; + + struct const_key_iterator; + struct const_value_iterator; + struct const_item_iterator; + + struct value_iterator; + struct item_iterator; + + /* + * Creation routines for making dynamic objects and arrays. Objects + * are maps from key to value (so named due to json-related origins + * here). + * + * Example: + * + * // Make a fairly complex dynamic: + * dynamic d = dynamic::object("key", "value1") + * ("key2", dynamic::array("value", + * "with", + * 4, + * "words")); + * + * // Build an object in a few steps: + * dynamic d = dynamic::object; + * d["key"] = 12; + * d["something_else"] = dynamic::array(1, 2, 3, nullptr); + */ + private: + struct EmptyArrayTag {}; + struct ObjectMaker; + + public: + static void array(EmptyArrayTag); + template <class... Args> + static dynamic array(Args&&... args); + + static ObjectMaker object(); + static ObjectMaker object(dynamic, dynamic); + + /** + * Default constructor, initializes with nullptr. + */ + dynamic(); + + /* + * String compatibility constructors. + */ + /* implicit */ dynamic(std::nullptr_t); + /* implicit */ dynamic(StringPiece val); + /* implicit */ dynamic(char const* val); + /* implicit */ dynamic(std::string val); + + /* + * This is part of the plumbing for array() and object(), above. + * Used to create a new array or object dynamic. + */ + /* implicit */ dynamic(void (*)(EmptyArrayTag)); + /* implicit */ dynamic(ObjectMaker (*)()); + /* implicit */ dynamic(ObjectMaker const&) = delete; + /* implicit */ dynamic(ObjectMaker&&); + + /* + * Constructors for integral and float types. + * Other types are SFINAEd out with NumericTypeHelper. + */ + template <class T, class NumericType = typename NumericTypeHelper<T>::type> + /* implicit */ dynamic(T t); + + /* + * If v is vector<bool>, v[idx] is a proxy object implicitly convertible to + * bool. Calling a function f(dynamic) with f(v[idx]) would require a double + * implicit conversion (reference -> bool -> dynamic) which is not allowed, + * hence we explicitly accept the reference proxy. + */ + /* implicit */ dynamic(std::vector<bool>::reference val); + /* implicit */ dynamic(VectorBoolConstRefCtorType val); + + /* + * Create a dynamic that is an array of the values from the supplied + * iterator range. + */ + template <class Iterator> + explicit dynamic(Iterator first, Iterator last); + + dynamic(dynamic const&); + dynamic(dynamic&&) noexcept; + ~dynamic() noexcept; + + /* + * "Deep" equality comparison. This will compare all the way down + * an object or array, and is potentially expensive. + */ + bool operator==(dynamic const& o) const; + + /* + * For all types except object this returns the natural ordering on + * those types. For objects, we throw TypeError. + */ + bool operator<(dynamic const& o) const; + + /* + * General operators. + * + * These throw TypeError when used with types or type combinations + * that don't support them. + * + * These functions may also throw if you use 64-bit integers with + * doubles when the integers are too big to fit in a double. + */ + dynamic& operator+=(dynamic const&); + dynamic& operator-=(dynamic const&); + dynamic& operator*=(dynamic const&); + dynamic& operator/=(dynamic const&); + dynamic& operator%=(dynamic const&); + dynamic& operator|=(dynamic const&); + dynamic& operator&=(dynamic const&); + dynamic& operator^=(dynamic const&); + dynamic& operator++(); + dynamic& operator--(); + + /* + * Assignment from other dynamics. Because of the implicit conversion + * to dynamic from its potential types, you can use this to change the + * type pretty intuitively. + * + * Basic guarantee only. + */ + dynamic& operator=(dynamic const&); + dynamic& operator=(dynamic&&) noexcept; + + /* + * For simple dynamics (not arrays or objects), this prints the + * value to an std::ostream in the expected way. Respects the + * formatting manipulators that have been sent to the stream + * already. + * + * If the dynamic holds an object or array, this prints them in a + * format very similar to JSON. (It will in fact actually be JSON + * as long as the dynamic validly represents a JSON object---i.e. it + * can't have non-string keys.) + */ + friend std::ostream& operator<<(std::ostream&, dynamic const&); + + /* + * Returns true if this dynamic is of the specified type. + */ + bool isString() const; + bool isObject() const; + bool isBool() const; + bool isNull() const; + bool isArray() const; + bool isDouble() const; + bool isInt() const; + + /* + * Returns: isInt() || isDouble(). + */ + bool isNumber() const; + + /* + * Returns the type of this dynamic. + */ + Type type() const; + + /* + * Returns the type of this dynamic as a printable string. + */ + const char* typeName() const; + + /* + * Extract a value while trying to convert to the specified type. + * Throws exceptions if we cannot convert from the real type to the + * requested type. + * + * Note you can only use this to access integral types or strings, + * since arrays and objects are generally best dealt with as a + * dynamic. + */ + std::string asString() const; + double asDouble() const; + int64_t asInt() const; + bool asBool() const; + + /* + * Extract the value stored in this dynamic without type conversion. + * + * These will throw a TypeError if the dynamic has a different type. + */ + const std::string& getString() const&; + double getDouble() const&; + int64_t getInt() const&; + bool getBool() const&; + std::string& getString() &; + double& getDouble() &; + int64_t& getInt() &; + bool& getBool() &; + std::string&& getString() &&; + double getDouble() &&; + int64_t getInt() &&; + bool getBool() &&; + + /* + * It is occasionally useful to access a string's internal pointer + * directly, without the type conversion of `asString()`. + * + * These will throw a TypeError if the dynamic is not a string. + */ + const char* data() const&; + const char* data() && = delete; + const char* c_str() const&; + const char* c_str() && = delete; + StringPiece stringPiece() const; + + /* + * Returns: true if this dynamic is null, an empty array, an empty + * object, or an empty string. + */ + bool empty() const; + + /* + * If this is an array or an object, returns the number of elements + * contained. If it is a string, returns the length. Otherwise + * throws TypeError. + */ + std::size_t size() const; + + /* + * You can iterate over the values of the array. Calling these on + * non-arrays will throw a TypeError. + */ + const_iterator begin() const; + const_iterator end() const; + iterator begin(); + iterator end(); + + private: + /* + * Helper object returned by keys(), values(), and items(). + */ + template <class T> + struct IterableProxy; + + /* + * Helper for heterogeneous lookup and mutation on objects: at(), find(), + * count(), erase(), operator[] + */ + template <typename K, typename T> + using IfIsNonStringDynamicConvertible = std::enable_if_t< + !std::is_convertible<K, StringPiece>::value && + std::is_convertible<K, dynamic>::value, + T>; + + template <typename K, typename T> + using IfNotIterator = + std::enable_if_t<!std::is_convertible<K, iterator>::value, T>; + + public: + /* + * You can iterate over the keys, values, or items (std::pair of key and + * value) in an object. Calling these on non-objects will throw a TypeError. + */ + IterableProxy<const_key_iterator> keys() const; + IterableProxy<const_value_iterator> values() const; + IterableProxy<const_item_iterator> items() const; + IterableProxy<value_iterator> values(); + IterableProxy<item_iterator> items(); + + /* + * AssociativeContainer-style find interface for objects. Throws if + * this is not an object. + * + * Returns: items().end() if the key is not present, or a + * const_item_iterator pointing to the item. + */ + template <typename K> + IfIsNonStringDynamicConvertible<K, const_item_iterator> find(K&&) const; + template <typename K> + IfIsNonStringDynamicConvertible<K, item_iterator> find(K&&); + + const_item_iterator find(StringPiece) const; + item_iterator find(StringPiece); + + /* + * If this is an object, returns whether it contains a field with + * the given name. Otherwise throws TypeError. + */ + template <typename K> + IfIsNonStringDynamicConvertible<K, std::size_t> count(K&&) const; + + std::size_t count(StringPiece) const; + + /* + * For objects or arrays, provides access to sub-fields by index or + * field name. + * + * Using these with dynamic objects that are not arrays or objects + * will throw a TypeError. Using an index that is out of range or + * object-element that's not present throws std::out_of_range. + */ + private: + dynamic const& atImpl(dynamic const&) const&; + + public: + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic const&> at(K&&) const&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&> at(K&&) &; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&&> at(K&&) &&; + + dynamic const& at(StringPiece) const&; + dynamic& at(StringPiece) &; + dynamic&& at(StringPiece) &&; + + /* + * Locate element using JSON pointer, per RFC 6901 + */ + + enum class json_pointer_resolution_error_code : uint8_t { + other = 0, + // key not found in object + key_not_found, + // array index out of bounds + index_out_of_bounds, + // special index "-" requesting append + append_requested, + // indexes in arrays must be numeric + index_not_numeric, + // indexes in arrays should not have leading zero + index_has_leading_zero, + // element not subscribable, i.e. neither object or array + element_not_object_or_array, + // hit document boundary, but pointer not exhausted yet + json_pointer_out_of_bounds, + }; + + template <typename Dynamic> + struct json_pointer_resolution_error { + // error code encountered while resolving JSON pointer + json_pointer_resolution_error_code error_code{}; + // index of the JSON pointer's token that caused the error + size_t index{0}; + // Last correctly resolved element in object. You can use it, + // for example, to add last element to the array + Dynamic* context{nullptr}; + }; + + template <typename Dynamic> + struct json_pointer_resolved_value { + // parent element of the value in dynamic, if exist + Dynamic* parent{nullptr}; + // pointer to the value itself + Dynamic* value{nullptr}; + // if parent isObject, this is the key in object to get value + StringPiece parent_key; + // if parent isArray, this is the index in array to get value + size_t parent_index{0}; + }; + + // clang-format off + template <typename Dynamic> + using resolved_json_pointer = Expected< + json_pointer_resolved_value<Dynamic>, + json_pointer_resolution_error<Dynamic>>; + + resolved_json_pointer<dynamic const> + try_get_ptr(json_pointer const&) const&; + resolved_json_pointer<dynamic> + try_get_ptr(json_pointer const&) &; + resolved_json_pointer<dynamic const> + try_get_ptr(json_pointer const&) const&& = delete; + resolved_json_pointer<dynamic const> + try_get_ptr(json_pointer const&) && = delete; + // clang-format on + + /* + * The following versions return nullptr if element could not be located. + * Throws if pointer does not match the shape of the document, e.g. uses + * string to index in array. + */ + const dynamic* get_ptr(json_pointer const&) const&; + dynamic* get_ptr(json_pointer const&) &; + const dynamic* get_ptr(json_pointer const&) const&& = delete; + dynamic* get_ptr(json_pointer const&) && = delete; + + /* + * Like 'at', above, except it returns either a pointer to the contained + * object or nullptr if it wasn't found. This allows a key to be tested for + * containment and retrieved in one operation. Example: + * + * if (auto* found = d.get_ptr(key)) + * // use *found; + * + * Using these with dynamic objects that are not arrays or objects + * will throw a TypeError. + */ + private: + const dynamic* get_ptrImpl(dynamic const&) const&; + + public: + template <typename K> + IfIsNonStringDynamicConvertible<K, const dynamic*> get_ptr(K&&) const&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic*> get_ptr(K&&) &; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic*> get_ptr(K&&) && = delete; + + const dynamic* get_ptr(StringPiece) const&; + dynamic* get_ptr(StringPiece) &; + dynamic* get_ptr(StringPiece) && = delete; + + /* + * This works for access to both objects and arrays. + * + * In the case of an array, the index must be an integer, and this + * will throw std::out_of_range if it is less than zero or greater + * than size(). + * + * In the case of an object, the non-const overload inserts a null + * value if the key isn't present. The const overload will throw + * std::out_of_range if the key is not present. + * + * These functions do not invalidate iterators except when a null value + * is inserted into an object as described above. + */ + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&> operator[](K&&) &; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic const&> operator[](K&&) const&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&&> operator[](K&&) &&; + + dynamic& operator[](StringPiece) &; + dynamic const& operator[](StringPiece) const&; + dynamic&& operator[](StringPiece) &&; + + /* + * Only defined for objects, throws TypeError otherwise. + * + * getDefault will return the value associated with the supplied key, the + * supplied default otherwise. setDefault will set the key to the supplied + * default if it is not yet set, otherwise leaving it. setDefault returns + * a reference to the existing value if present, the new value otherwise. + */ + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic> getDefault( + K&& k, + const dynamic& v = dynamic::object) const&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic> getDefault(K&& k, dynamic&& v) + const&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic> getDefault( + K&& k, + const dynamic& v = dynamic::object) &&; + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic> getDefault(K&& k, dynamic&& v) &&; + + dynamic getDefault(StringPiece k, const dynamic& v = dynamic::object) const&; + dynamic getDefault(StringPiece k, dynamic&& v) const&; + dynamic getDefault(StringPiece k, const dynamic& v = dynamic::object) &&; + dynamic getDefault(StringPiece k, dynamic&& v) &&; + + template <typename K, typename V> + IfIsNonStringDynamicConvertible<K, dynamic&> setDefault(K&& k, V&& v); + template <typename V> + dynamic& setDefault(StringPiece k, V&& v); + // MSVC 2015 Update 3 needs these extra overloads because if V were a + // defaulted template parameter, it causes MSVC to consider v an rvalue + // reference rather than a universal reference, resulting in it not being + // able to find the correct overload to construct a dynamic with. + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&> setDefault(K&& k, dynamic&& v); + template <typename K> + IfIsNonStringDynamicConvertible<K, dynamic&> setDefault( + K&& k, + const dynamic& v = dynamic::object); + + dynamic& setDefault(StringPiece k, dynamic&& v); + dynamic& setDefault(StringPiece k, const dynamic& v = dynamic::object); + + /* + * Resizes an array so it has at n elements, using the supplied + * default to fill new elements. Throws TypeError if this dynamic + * is not an array. + * + * May invalidate iterators. + * + * Post: size() == n + */ + void resize(std::size_t n, dynamic const& = nullptr); + + /* + * Inserts the supplied key-value pair to an object, or throws if + * it's not an object. If the key already exists, insert will overwrite the + * value, i.e., similar to insert_or_assign. + * + * Invalidates iterators. + */ + template <class K, class V> + IfNotIterator<K, void> insert(K&&, V&& val); + + /* + * Inserts the supplied value into array, or throw if not array + * Shifts existing values in the array to the right + * + * Invalidates iterators. + */ + template <class T> + iterator insert(const_iterator pos, T&& value); + + /* + * These functions merge two folly dynamic objects. + * The "update" and "update_missing" functions extend the object by + * inserting the key/value pairs of mergeObj into the current object. + * For update, if key is duplicated between the two objects, it + * will overwrite with the value of the object being inserted (mergeObj). + * For "update_missing", it will prefer the value in the original object + * + * The "merge" function creates a new object consisting of the key/value + * pairs of both mergeObj1 and mergeObj2 + * If the key is duplicated between the two objects, + * it will prefer value in the second object (mergeObj2) + */ + void update(const dynamic& mergeObj); + void update_missing(const dynamic& other); + static dynamic merge(const dynamic& mergeObj1, const dynamic& mergeObj2); + + /* + * Implement recursive version of RFC7386: JSON merge patch. This modifies + * the current object. + */ + void merge_patch(const dynamic& patch); + + /* + * Computes JSON merge patch (RFC7386) needed to mutate from source to target + */ + static dynamic merge_diff(const dynamic& source, const dynamic& target); + + /* + * Erase an element from a dynamic object, by key. + * + * Invalidates iterators to the element being erased. + * + * Returns the number of elements erased (i.e. 1 or 0). + */ + template <typename K> + IfIsNonStringDynamicConvertible<K, std::size_t> erase(K&&); + + std::size_t erase(StringPiece); + + /* + * Erase an element from a dynamic object or array, using an + * iterator or an iterator range. + * + * In arrays, invalidates iterators to elements after the element + * being erased. In objects, invalidates iterators to the elements + * being erased. + * + * Returns a new iterator to the first element beyond any elements + * removed, or end() if there are none. (The iteration order does + * not change.) + */ + iterator erase(const_iterator it); + iterator erase(const_iterator first, const_iterator last); + + const_key_iterator erase(const_key_iterator it); + const_key_iterator erase(const_key_iterator first, const_key_iterator last); + + value_iterator erase(const_value_iterator it); + value_iterator erase(const_value_iterator first, const_value_iterator last); + + item_iterator erase(const_item_iterator it); + item_iterator erase(const_item_iterator first, const_item_iterator last); + /* + * Append elements to an array. If this is not an array, throws + * TypeError. + * + * Invalidates iterators. + */ + void push_back(dynamic const&); + void push_back(dynamic&&); + + /* + * Remove an element from the back of an array. If this is not an array, + * throws TypeError. + * + * Does not invalidate iterators. + */ + void pop_back(); + + /* + * Return reference to the last element in an array. If this is not + * an array, throws TypeError. + */ + const dynamic& back() const; + + /* + * Get a hash code. This function is called by a std::hash<> + * specialization, also. + * + * Throws TypeError if this is an object, array, or null. + */ + std::size_t hash() const; + + private: + friend struct TypeError; + struct ObjectImpl; + template <class T> + struct TypeInfo; + template <class T> + struct CompareOp; + template <class T> + struct GetAddrImpl; + template <class T> + struct PrintImpl; + + explicit dynamic(Array&& array); + + template <class T> + T const& get() const; + template <class T> + T& get(); + // clang-format off + template <class T> + T* get_nothrow() & noexcept; + // clang-format on + template <class T> + T const* get_nothrow() const& noexcept; + // clang-format off + template <class T> + T* get_nothrow() && noexcept = delete; + // clang-format on + template <class T> + T* getAddress() noexcept; + template <class T> + T const* getAddress() const noexcept; + + template <class T> + T asImpl() const; + + static char const* typeName(Type); + void destroy() noexcept; + void print(std::ostream&) const; + void print_as_pseudo_json(std::ostream&) const; // see json.cpp + + private: + Type type_; + union Data { + explicit Data() : nul(nullptr) {} + ~Data() {} + + std::nullptr_t nul; + Array array; + bool boolean; + double doubl; + int64_t integer; + std::string string; + + /* + * Objects are placement new'd here. We have to use a char buffer + * because we don't know the type here (F14NodeMap<> with + * dynamic would be parameterizing a std:: template with an + * incomplete type right now). (Note that in contrast we know it + * is ok to do this with fbvector because we own it.) + */ + aligned_storage_for_t<F14NodeMap<int, int>> objectBuffer; + } u_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace folly + +#include <folly/dynamic-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/executors/Async.h b/ios/Pods/Flipper-Folly/folly/executors/Async.h new file mode 100644 index 000000000..a53e6ac01 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/Async.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <folly/executors/GlobalExecutor.h> +#include <folly/futures/Future.h> + +namespace folly { + +template <class F> +auto async(F&& fn) { + return folly::via<F>(getCPUExecutor().get(), std::forward<F>(fn)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.cpp new file mode 100644 index 000000000..7f2bc2ad8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/CPUThreadPoolExecutor.h> + +#include <folly/Memory.h> +#include <folly/executors/task_queue/PriorityLifoSemMPMCQueue.h> +#include <folly/executors/task_queue/PriorityUnboundedBlockingQueue.h> +#include <folly/executors/task_queue/UnboundedBlockingQueue.h> +#include <folly/portability/GFlags.h> + +DEFINE_bool( + dynamic_cputhreadpoolexecutor, + true, + "CPUThreadPoolExecutor will dynamically create and destroy threads"); + +namespace folly { + +namespace { +// queue_alloc custom allocator is necessary until C++17 +// http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3396.htm +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65122 +// https://bugs.llvm.org/show_bug.cgi?id=22634 +using default_queue = UnboundedBlockingQueue<CPUThreadPoolExecutor::CPUTask>; +using default_queue_alloc = + AlignedSysAllocator<default_queue, FixedAlign<alignof(default_queue)>>; + +constexpr folly::StringPiece executorName = "CPUThreadPoolExecutor"; +} // namespace + +const size_t CPUThreadPoolExecutor::kDefaultMaxQueueSize = 1 << 14; + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + size_t numThreads, + std::unique_ptr<BlockingQueue<CPUTask>> taskQueue, + std::shared_ptr<ThreadFactory> threadFactory) + : ThreadPoolExecutor( + numThreads, + FLAGS_dynamic_cputhreadpoolexecutor ? 0 : numThreads, + std::move(threadFactory)), + taskQueue_(taskQueue.release()) { + setNumThreads(numThreads); + registerThreadPoolExecutor(this); +} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + std::pair<size_t, size_t> numThreads, + std::unique_ptr<BlockingQueue<CPUTask>> taskQueue, + std::shared_ptr<ThreadFactory> threadFactory) + : ThreadPoolExecutor( + numThreads.first, + numThreads.second, + std::move(threadFactory)), + taskQueue_(taskQueue.release()) { + setNumThreads(numThreads.first); + registerThreadPoolExecutor(this); +} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory) + : ThreadPoolExecutor( + numThreads, + FLAGS_dynamic_cputhreadpoolexecutor ? 0 : numThreads, + std::move(threadFactory)), + taskQueue_(std::allocate_shared<default_queue>(default_queue_alloc{})) { + setNumThreads(numThreads); + registerThreadPoolExecutor(this); +} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + std::pair<size_t, size_t> numThreads, + std::shared_ptr<ThreadFactory> threadFactory) + : ThreadPoolExecutor( + numThreads.first, + numThreads.second, + std::move(threadFactory)), + taskQueue_(std::allocate_shared<default_queue>(default_queue_alloc{})) { + setNumThreads(numThreads.first); + registerThreadPoolExecutor(this); +} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor(size_t numThreads) + : CPUThreadPoolExecutor( + numThreads, + std::make_shared<NamedThreadFactory>("CPUThreadPool")) {} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + size_t numThreads, + int8_t numPriorities, + std::shared_ptr<ThreadFactory> threadFactory) + : CPUThreadPoolExecutor( + numThreads, + std::make_unique<PriorityUnboundedBlockingQueue<CPUTask>>( + numPriorities), + std::move(threadFactory)) {} + +CPUThreadPoolExecutor::CPUThreadPoolExecutor( + size_t numThreads, + int8_t numPriorities, + size_t maxQueueSize, + std::shared_ptr<ThreadFactory> threadFactory) + : CPUThreadPoolExecutor( + numThreads, + std::make_unique<PriorityLifoSemMPMCQueue<CPUTask>>( + numPriorities, + maxQueueSize), + std::move(threadFactory)) {} + +CPUThreadPoolExecutor::~CPUThreadPoolExecutor() { + deregisterThreadPoolExecutor(this); + stop(); + CHECK(threadsToStop_ == 0); +} + +void CPUThreadPoolExecutor::add(Func func) { + add(std::move(func), std::chrono::milliseconds(0)); +} + +void CPUThreadPoolExecutor::add( + Func func, + std::chrono::milliseconds expiration, + Func expireCallback) { + auto result = taskQueue_->add( + CPUTask(std::move(func), expiration, std::move(expireCallback))); + if (!result.reusedThread) { + ensureActiveThreads(); + } +} + +void CPUThreadPoolExecutor::addWithPriority(Func func, int8_t priority) { + add(std::move(func), priority, std::chrono::milliseconds(0)); +} + +void CPUThreadPoolExecutor::add( + Func func, + int8_t priority, + std::chrono::milliseconds expiration, + Func expireCallback) { + CHECK(getNumPriorities() > 0); + auto result = taskQueue_->addWithPriority( + CPUTask(std::move(func), expiration, std::move(expireCallback)), + priority); + if (!result.reusedThread) { + ensureActiveThreads(); + } +} + +uint8_t CPUThreadPoolExecutor::getNumPriorities() const { + return taskQueue_->getNumPriorities(); +} + +size_t CPUThreadPoolExecutor::getTaskQueueSize() const { + return taskQueue_->size(); +} + +BlockingQueue<CPUThreadPoolExecutor::CPUTask>* +CPUThreadPoolExecutor::getTaskQueue() { + return taskQueue_.get(); +} + +// threadListLock_ must be writelocked. +bool CPUThreadPoolExecutor::tryDecrToStop() { + auto toStop = threadsToStop_.load(std::memory_order_relaxed); + if (toStop <= 0) { + return false; + } + threadsToStop_.store(toStop - 1, std::memory_order_relaxed); + return true; +} + +bool CPUThreadPoolExecutor::taskShouldStop(folly::Optional<CPUTask>& task) { + if (tryDecrToStop()) { + return true; + } + if (task) { + return false; + } else { + return tryTimeoutThread(); + } + return true; +} + +void CPUThreadPoolExecutor::threadRun(ThreadPtr thread) { + this->threadPoolHook_.registerThread(); + auto guard = folly::makeBlockingDisallowedGuard(executorName); + + thread->startupBaton.post(); + while (true) { + auto task = taskQueue_->try_take_for(threadTimeout_); + + // Handle thread stopping, either by task timeout, or + // by 'poison' task added in join() or stop(). + if (UNLIKELY(!task || task.value().poison)) { + // Actually remove the thread from the list. + SharedMutex::WriteHolder w{&threadListLock_}; + if (taskShouldStop(task)) { + for (auto& o : observers_) { + o->threadStopped(thread.get()); + } + threadList_.remove(thread); + stoppedThreads_.add(thread); + return; + } else { + continue; + } + } + + runTask(thread, std::move(task.value())); + + if (UNLIKELY(threadsToStop_ > 0 && !isJoin_)) { + SharedMutex::WriteHolder w{&threadListLock_}; + if (tryDecrToStop()) { + threadList_.remove(thread); + stoppedThreads_.add(thread); + return; + } + } + } +} + +void CPUThreadPoolExecutor::stopThreads(size_t n) { + threadsToStop_ += n; + for (size_t i = 0; i < n; i++) { + taskQueue_->addWithPriority(CPUTask(), Executor::LO_PRI); + } +} + +// threadListLock_ is read (or write) locked. +size_t CPUThreadPoolExecutor::getPendingTaskCountImpl() const { + return taskQueue_->size(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h new file mode 100644 index 000000000..b3a6c2b70 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/executors/ThreadPoolExecutor.h> + +DECLARE_bool(dynamic_cputhreadpoolexecutor); + +namespace folly { + +/** + * A Thread pool for CPU bound tasks. + * + * @note A single queue backed by folly/LifoSem and folly/MPMC queue. + * Because of this contention can be quite high, + * since all the worker threads and all the producer threads hit + * the same queue. MPMC queue excels in this situation but dictates a max queue + * size. + * + * @note The default queue throws when full (folly::QueueBehaviorIfFull::THROW), + * so add() can fail. Furthermore, join() can also fail if the queue is full, + * because it enqueues numThreads poison tasks to stop the threads. If join() is + * needed to be guaranteed to succeed PriorityLifoSemMPMCQueue can be used + * instead, initializing the lowest priority's (LO_PRI) capacity to at least + * numThreads. Poisons use LO_PRI so if that priority is not used for any user + * task join() is guaranteed not to encounter a full queue. + * + * @note If a blocking queue (folly::QueueBehaviorIfFull::BLOCK) is used, and + * tasks executing on a given thread pool schedule more tasks, deadlock is + * possible if the queue becomes full. Deadlock is also possible if there is + * a circular dependency among multiple thread pools with blocking queues. + * To avoid this situation, use non-blocking queue(s), or schedule tasks only + * from threads not belonging to the given thread pool(s), or use + * folly::IOThreadPoolExecutor. + * + * @note LifoSem wakes up threads in Lifo order - i.e. there are only few + * threads as necessary running, and we always try to reuse the same few threads + * for better cache locality. + * Inactive threads have their stack madvised away. This works quite well in + * combination with Lifosem - it almost doesn't matter if more threads than are + * necessary are specified at startup. + * + * @note Supports priorities - priorities are implemented as multiple queues - + * each worker thread checks the highest priority queue first. Threads + * themselves don't have priorities set, so a series of long running low + * priority tasks could still hog all the threads. (at last check pthreads + * thread priorities didn't work very well). + */ +class CPUThreadPoolExecutor : public ThreadPoolExecutor { + public: + struct CPUTask; + + CPUThreadPoolExecutor( + size_t numThreads, + std::unique_ptr<BlockingQueue<CPUTask>> taskQueue, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("CPUThreadPool")); + + CPUThreadPoolExecutor( + std::pair<size_t, size_t> numThreads, + std::unique_ptr<BlockingQueue<CPUTask>> taskQueue, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("CPUThreadPool")); + + explicit CPUThreadPoolExecutor(size_t numThreads); + + CPUThreadPoolExecutor( + size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory); + + CPUThreadPoolExecutor( + std::pair<size_t, size_t> numThreads, + std::shared_ptr<ThreadFactory> threadFactory); + + CPUThreadPoolExecutor( + size_t numThreads, + int8_t numPriorities, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("CPUThreadPool")); + + CPUThreadPoolExecutor( + size_t numThreads, + int8_t numPriorities, + size_t maxQueueSize, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("CPUThreadPool")); + + ~CPUThreadPoolExecutor() override; + + void add(Func func) override; + void add( + Func func, + std::chrono::milliseconds expiration, + Func expireCallback = nullptr) override; + + void addWithPriority(Func func, int8_t priority) override; + void add( + Func func, + int8_t priority, + std::chrono::milliseconds expiration, + Func expireCallback = nullptr); + + size_t getTaskQueueSize() const; + + uint8_t getNumPriorities() const override; + + struct CPUTask : public ThreadPoolExecutor::Task { + // Must be noexcept move constructible so it can be used in MPMCQueue + + explicit CPUTask( + Func&& f, + std::chrono::milliseconds expiration, + Func&& expireCallback) + : Task(std::move(f), expiration, std::move(expireCallback)), + poison(false) {} + CPUTask() + : Task(nullptr, std::chrono::milliseconds(0), nullptr), poison(true) {} + + bool poison; + }; + + static const size_t kDefaultMaxQueueSize; + + protected: + BlockingQueue<CPUTask>* getTaskQueue(); + + private: + void threadRun(ThreadPtr thread) override; + void stopThreads(size_t n) override; + size_t getPendingTaskCountImpl() const override final; + + bool tryDecrToStop(); + bool taskShouldStop(folly::Optional<CPUTask>&); + + // shared_ptr for type erased dtor to handle extended alignment. + std::shared_ptr<BlockingQueue<CPUTask>> taskQueue_; + std::atomic<ssize_t> threadsToStop_{0}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/Codel.cpp b/ios/Pods/Flipper-Folly/folly/executors/Codel.cpp new file mode 100644 index 000000000..ea8c36bee --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/Codel.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/Codel.h> + +#include <folly/portability/GFlags.h> +#include <algorithm> +#include <stdexcept> + +DEFINE_int32(codel_interval, 100, "Codel default interval time in ms"); +DEFINE_int32(codel_target_delay, 5, "Target codel queueing delay in ms"); + +using namespace std::chrono; + +namespace folly { + +Codel::Codel() + : Codel(Codel::Options() + .setInterval(milliseconds(FLAGS_codel_interval)) + .setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {} + +Codel::Codel(const Options& options) + : codelMinDelayNs_(0), + codelIntervalTimeNs_( + duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()) + .count()), + targetDelay_(options.targetDelay()), + interval_(options.interval()), + codelResetDelay_(true), + overloaded_(false) {} + +bool Codel::overloaded(nanoseconds delay) { + bool ret = false; + auto now = steady_clock::now(); + + // Avoid another thread updating the value at the same time we are using it + // to calculate the overloaded state + auto minDelay = nanoseconds(codelMinDelayNs_); + // Get a snapshot of the parameters to determine overload condition + auto opts = getOptions(); + auto sloughTimeout = getSloughTimeout(opts.targetDelay()); + + if (now > steady_clock::time_point(nanoseconds(codelIntervalTimeNs_)) && + // testing before exchanging is more cacheline-friendly + (!codelResetDelay_.load(std::memory_order_acquire) && + !codelResetDelay_.exchange(true))) { + codelIntervalTimeNs_ = + duration_cast<nanoseconds>((now + opts.interval()).time_since_epoch()) + .count(); + + if (minDelay > opts.targetDelay()) { + overloaded_ = true; + } else { + overloaded_ = false; + } + } + // Care must be taken that only a single thread resets codelMinDelay_, + // and that it happens after the interval reset above + if (codelResetDelay_.load(std::memory_order_acquire) && + codelResetDelay_.exchange(false)) { + codelMinDelayNs_ = delay.count(); + // More than one request must come in during an interval before codel + // starts dropping requests + return false; + } else if (delay < nanoseconds(codelMinDelayNs_)) { + codelMinDelayNs_ = delay.count(); + } + + // Here is where we apply different logic than codel proper. Instead of + // adapting the interval until the next drop, we slough off requests with + // queueing delay > 2*target_delay while in the overloaded regime. This + // empirically works better for our services than the codel approach of + // increasingly often dropping packets. + if (overloaded_ && delay > sloughTimeout) { + ret = true; + } + + return ret; +} + +int Codel::getLoad() { + // it might be better to use the average delay instead of minDelay, but we'd + // have to track it. aspiring bootcamper? + auto opts = getOptions(); + return std::min<int>( + 100, 100 * getMinDelay() / getSloughTimeout(opts.targetDelay())); +} + +void Codel::setOptions(Options const& options) { + // Carry out some basic sanity checks. + auto delay = options.targetDelay(); + auto interval = options.interval(); + + if (interval <= delay || delay <= milliseconds::zero() || + interval <= milliseconds::zero()) { + throw std::invalid_argument("Invalid arguments provided"); + } + interval_.store(interval, std::memory_order_relaxed); + targetDelay_.store(delay, std::memory_order_relaxed); +} + +const Codel::Options Codel::getOptions() const { + auto interval = interval_.load(std::memory_order_relaxed); + auto delay = targetDelay_.load(std::memory_order_relaxed); + // Enforcing the invariant that targetDelay <= interval. A violation could + // potentially occur if either parameter was updated by another concurrent + // thread via the setOptions() method. + delay = std::min(delay, interval); + + return Codel::Options().setTargetDelay(delay).setInterval(interval); +} + +nanoseconds Codel::getMinDelay() { + return nanoseconds(codelMinDelayNs_); +} + +milliseconds Codel::getSloughTimeout(milliseconds delay) const { + return delay * 2; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/Codel.h b/ios/Pods/Flipper-Folly/folly/executors/Codel.h new file mode 100644 index 000000000..308a518d9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/Codel.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <chrono> + +#include <folly/portability/GFlags.h> + +DECLARE_int32(codel_interval); +DECLARE_int32(codel_target_delay); + +namespace folly { + +/// CoDel (controlled delay) is an active queue management algorithm from +/// networking for battling bufferbloat. +/// +/// Services also have queues (of requests, not packets) and suffer from +/// queueing delay when overloaded. This class adapts the codel algorithm for +/// services. +/// +/// Codel is discussed in depth on the web [1,2], but a basic sketch of the +/// algorithm is this: if every request has experienced queueing delay greater +/// than the target (5ms) during the past interval (100ms), then we shed load. +/// +/// We have adapted the codel algorithm. TCP sheds load by changing windows in +/// reaction to dropped packets. Codel in a network setting drops packets at +/// increasingly shorter intervals (100 / sqrt(n)) to achieve a linear change +/// in throughput. In our experience a different scheme works better for +/// services: when overloaded slough off requests that we dequeue which have +/// exceeded an alternate timeout (2 * target_delay). +/// +/// So in summary, to use this class, calculate the time each request spent in +/// the queue and feed that delay to overloaded(), which will tell you whether +/// to expire this request. +/// +/// You can also ask for an instantaneous load estimate and the minimum delay +/// observed during this interval. +/// +/// +/// 1. http://queue.acm.org/detail.cfm?id=2209336 +/// 2. https://en.wikipedia.org/wiki/CoDel +class Codel { + public: + class Options { + public: + std::chrono::milliseconds interval() const { + return interval_; + } + + Options& setInterval(std::chrono::milliseconds value) { + interval_ = value; + return *this; + } + + std::chrono::milliseconds targetDelay() const { + return targetDelay_; + } + + Options& setTargetDelay(std::chrono::milliseconds value) { + targetDelay_ = value; + return *this; + } + + private: + std::chrono::milliseconds interval_; + std::chrono::milliseconds targetDelay_; + }; + + Codel(); + + /// Preferable construction method + explicit Codel(const Options& options); + + /// Returns true if this request should be expired to reduce overload. + /// In detail, this returns true if min_delay > target_delay for the + /// interval, and this delay > 2 * target_delay. + /// + /// As you may guess, we observe the clock so this is time sensitive. Call + /// it promptly after calculating queueing delay. + bool overloaded(std::chrono::nanoseconds delay); + + /// Get the queue load, as seen by the codel algorithm + /// Gives a rough guess at how bad the queue delay is. + /// + /// min(100%, min_delay / (2 * target_delay)) + /// + /// Return: 0 = no delay, 100 = At the queueing limit + int getLoad(); + + /// Update the target delay and interval parameters by passing them + /// in as an Options instance. Note that target delay must be strictly + /// smaller than the interval. This is a no-op if invalid arguments are + /// provided. + /// + /// NOTE : Calls to setOptions must be externally synchronized since there + /// is no internal locking for parameter updates. Codel only guarantees + /// internal synchronization between calls to getOptions() and other members + /// but not between concurrent calls to getOptions(). + /// + /// Throws std::runtime_error if arguments are invalid. + void setOptions(Options const& options); + + /// Return a consistent snapshot of the two parameters used by Codel. Since + /// parameters may be updated with the setOptions() method provided above, + /// it is necessary to ensure that reads of the parameters return a consistent + /// pair in which the invariant of targetDelay <= interval is guaranteed; the + /// targetDelay value that is returned is the minimum of targetDelay and + /// interval. + const Options getOptions() const; + + std::chrono::nanoseconds getMinDelay(); + + /// Returns the timeout condition for overload given a target delay period. + std::chrono::milliseconds getSloughTimeout( + std::chrono::milliseconds delay) const; + + private: + std::atomic<uint64_t> codelMinDelayNs_; + std::atomic<uint64_t> codelIntervalTimeNs_; + + std::atomic<std::chrono::milliseconds> targetDelay_; + std::atomic<std::chrono::milliseconds> interval_; + + // flag to make overloaded() thread-safe, since we only want + // to reset the delay once per time period + std::atomic<bool> codelResetDelay_; + + std::atomic<bool> overloaded_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/DrivableExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/DrivableExecutor.h new file mode 100644 index 000000000..2dd56c1c1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/DrivableExecutor.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> + +namespace folly { + +/* + * A DrivableExecutor can be driven via its drive() method + * Examples include EventBase (via loopOnce()) and ManualExecutor + * (via makeProgress()). + * + * This interface is most handy in conjunction with + * Future<T>::getVia(DrivableExecutor*) and + * Future<T>::waitVia(DrivableExecutor*) + * + * These call drive() * repeatedly until the Future is fulfilled. + * getVia() returns the value (or throws the exception) and waitVia() returns + * the same Future for chainability. + * + * These will be most helpful in tests, for instance if you need to pump a mock + * EventBase until Futures complete. + */ + +class DrivableExecutor : public virtual Executor { + public: + ~DrivableExecutor() override = default; + + // Make progress on this Executor's work. + // + // Drive *must not* busy wait if there is no work to do. Instead, + // sleep (using a semaphore or similar) until at least one event is + // processed. + // I.e. make_future().via(foo).then(...).getVia(DrivableExecutor) + // must not spin, even though nothing happens on the drivable + // executor. + virtual void drive() = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.cpp new file mode 100644 index 000000000..5cc7f9946 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <algorithm> +#include <array> +#include <atomic> +#include <chrono> +#include <cstddef> +#include <exception> +#include <limits> +#include <memory> +#include <queue> +#include <utility> +#include <vector> + +#include <folly/ScopeGuard.h> +#include <folly/executors/EDFThreadPoolExecutor.h> + +namespace folly { +namespace { +constexpr folly::StringPiece executorName = "EDFThreadPoolExecutor"; +} + +class EDFThreadPoolExecutor::Task { + public: + explicit Task(Func&& f, int repeat, uint64_t deadline) + : f_(std::move(f)), total_(repeat), deadline_(deadline) {} + + explicit Task(std::vector<Func>&& fs, uint64_t deadline) + : fs_(std::move(fs)), total_(fs_.size()), deadline_(deadline) {} + + uint64_t getDeadline() const { + return deadline_; + } + + bool isDone() const { + return iter_.load(std::memory_order_relaxed) >= total_; + } + + int next() { + if (isDone()) { + return -1; + } + + int result = iter_.fetch_add(1, std::memory_order_relaxed); + return result < total_ ? result : -1; + } + + void run(int i) { + folly::RequestContextScopeGuard guard(context_); + if (f_) { + f_(); + if (i >= total_ - 1) { + std::exchange(f_, nullptr); + } + } else { + DCHECK(0 <= i && i < total_); + fs_[i](); + std::exchange(fs_[i], nullptr); + } + } + + Func f_; + std::vector<Func> fs_; + std::atomic<int> iter_{0}; + int total_; + uint64_t deadline_; + TaskStats stats_; + std::shared_ptr<RequestContext> context_ = RequestContext::saveContext(); + std::chrono::steady_clock::time_point enqueueTime_ = + std::chrono::steady_clock::now(); +}; + +class EDFThreadPoolExecutor::TaskQueue { + public: + using TaskPtr = std::shared_ptr<Task>; + + // This is not a `Synchronized` because we perform a few "peek" operations. + struct Bucket { + SharedMutex mutex; + + struct Compare { + bool operator()(const TaskPtr& lhs, const TaskPtr& rhs) const { + return lhs->getDeadline() > rhs->getDeadline(); + } + }; + + std::priority_queue<TaskPtr, std::vector<TaskPtr>, Compare> tasks; + std::atomic<bool> empty{true}; + }; + + static constexpr std::size_t kNumBuckets = 2 << 5; + + explicit TaskQueue() + : buckets_{}, curDeadline_(kLatestDeadline), numItems_(0) {} + + void push(TaskPtr task) { + auto deadline = task->getDeadline(); + auto& bucket = getBucket(deadline); + { + SharedMutex::WriteHolder guard(&bucket.mutex); + bucket.tasks.push(std::move(task)); + bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed); + } + + numItems_.fetch_add(1, std::memory_order_seq_cst); + + // Update current earliest deadline if necessary + uint64_t curDeadline = curDeadline_.load(std::memory_order_relaxed); + do { + if (curDeadline <= deadline) { + break; + } + } while (!curDeadline_.compare_exchange_weak( + curDeadline, deadline, std::memory_order_relaxed)); + } + + TaskPtr pop() { + bool needDeadlineUpdate = false; + for (;;) { + if (numItems_.load(std::memory_order_seq_cst) == 0) { + return nullptr; + } + + auto curDeadline = curDeadline_.load(std::memory_order_relaxed); + auto& bucket = getBucket(curDeadline); + + if (needDeadlineUpdate || bucket.empty.load(std::memory_order_relaxed)) { + // Try setting the next earliest deadline. However no need to + // enforce as there might be insertion happening. + // If there is no next deadline, we set deadline to `kLatestDeadline`. + curDeadline_.compare_exchange_weak( + curDeadline, + findNextDeadline(curDeadline), + std::memory_order_relaxed); + needDeadlineUpdate = false; + continue; + } + + { + // Fast path. Take bucket reader lock. + SharedMutex::ReadHolder guard(&bucket.mutex); + if (bucket.tasks.empty()) { + continue; + } + const auto& task = bucket.tasks.top(); + if (!task->isDone() && task->getDeadline() == curDeadline) { + return task; + } + // If the task is finished already, fall through to remove it. + } + + { + // Take the writer lock to clean up the finished task. + SharedMutex::WriteHolder guard(&bucket.mutex); + if (bucket.tasks.empty()) { + continue; + } + const auto& task = bucket.tasks.top(); + if (task->isDone()) { + // Current task finished. Remove from the queue. + bucket.tasks.pop(); + bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed); + numItems_.fetch_sub(1, std::memory_order_seq_cst); + } + } + + // We may have finished processing the current task / bucket. Going back + // to the beginning of the loop to find the next bucket. + needDeadlineUpdate = true; + } + } + + std::size_t size() const { + return numItems_.load(std::memory_order_seq_cst); + } + + private: + Bucket& getBucket(uint64_t deadline) { + return buckets_[deadline % kNumBuckets]; + } + + uint64_t findNextDeadline(uint64_t prevDeadline) { + auto begin = prevDeadline % kNumBuckets; + + uint64_t earliestDeadline = kLatestDeadline; + for (std::size_t i = 0; i < kNumBuckets; ++i) { + auto& bucket = buckets_[(begin + i) % kNumBuckets]; + + // Peek without locking first. + if (bucket.empty.load(std::memory_order_relaxed)) { + continue; + } + + SharedMutex::ReadHolder guard(&bucket.mutex); + auto curDeadline = curDeadline_.load(std::memory_order_relaxed); + if (prevDeadline != curDeadline) { + // Bail out early if something already happened + return curDeadline; + } + + // Verify again after locking + if (bucket.tasks.empty()) { + continue; + } + + const auto& task = bucket.tasks.top(); + auto deadline = task->getDeadline(); + + if (deadline < earliestDeadline) { + earliestDeadline = deadline; + } + + if ((deadline <= prevDeadline) || + (deadline - prevDeadline < kNumBuckets)) { + // Found the next highest priority, or new tasks were added. + // No need to scan anymore. + break; + } + } + + return earliestDeadline; + } + + std::array<Bucket, kNumBuckets> buckets_; + std::atomic<uint64_t> curDeadline_; + + // All operations performed on `numItems_` explicitly specify memory + // ordering of `std::memory_order_seq_cst`. This is due to `numItems_` + // performing Dekker's algorithm with `numIdleThreads_` prior to consumer + // threads (workers) wait on `sem_`. + std::atomic<std::size_t> numItems_; +}; + +EDFThreadPoolExecutor::EDFThreadPoolExecutor( + std::size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory) + : ThreadPoolExecutor(numThreads, numThreads, std::move(threadFactory)), + taskQueue_(std::make_unique<TaskQueue>()) { + setNumThreads(numThreads); + registerThreadPoolExecutor(this); +} + +EDFThreadPoolExecutor::~EDFThreadPoolExecutor() { + deregisterThreadPoolExecutor(this); + stop(); +} + +void EDFThreadPoolExecutor::add(Func f) { + add(std::move(f), kLatestDeadline); +} + +void EDFThreadPoolExecutor::add(Func f, uint64_t deadline) { + add(std::move(f), 1, deadline); +} + +void EDFThreadPoolExecutor::add(Func f, std::size_t total, uint64_t deadline) { + if (UNLIKELY(isJoin_.load(std::memory_order_relaxed) || total == 0)) { + return; + } + + taskQueue_->push(std::make_shared<Task>(std::move(f), total, deadline)); + + auto numIdleThreads = numIdleThreads_.load(std::memory_order_seq_cst); + if (numIdleThreads > 0) { + // If idle threads are available notify them, otherwise all worker threads + // are running and will get around to this task in time. + sem_.post(std::min(total, numIdleThreads)); + } +} + +void EDFThreadPoolExecutor::add(std::vector<Func> fs, uint64_t deadline) { + if (UNLIKELY(fs.empty())) { + return; + } + + auto total = fs.size(); + taskQueue_->push(std::make_shared<Task>(std::move(fs), deadline)); + + auto numIdleThreads = numIdleThreads_.load(std::memory_order_seq_cst); + if (numIdleThreads > 0) { + // If idle threads are available notify them, otherwise all worker threads + // are running and will get around to this task in time. + sem_.post(std::min(total, numIdleThreads)); + } +} + +folly::Executor::KeepAlive<> EDFThreadPoolExecutor::deadlineExecutor( + uint64_t deadline) { + class DeadlineExecutor : public folly::Executor { + public: + static KeepAlive<> create( + uint64_t deadline, + KeepAlive<EDFThreadPoolExecutor> executor) { + return makeKeepAlive(new DeadlineExecutor(deadline, std::move(executor))); + } + + void add(folly::Func f) override { + executor_->add(std::move(f), deadline_); + } + + bool keepAliveAcquire() override { + const auto count = + keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + DCHECK_GT(count, 0); + return true; + } + + void keepAliveRelease() override { + const auto count = + keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK_GT(count, 0); + if (count == 1) { + delete this; + } + } + + private: + DeadlineExecutor( + uint64_t deadline, + KeepAlive<EDFThreadPoolExecutor> executor) + : deadline_(deadline), executor_(std::move(executor)) {} + + std::atomic<size_t> keepAliveCount_{1}; + uint64_t deadline_; + KeepAlive<EDFThreadPoolExecutor> executor_; + }; + return DeadlineExecutor::create(deadline, getKeepAliveToken(this)); +} + +void EDFThreadPoolExecutor::threadRun(ThreadPtr thread) { + this->threadPoolHook_.registerThread(); + auto guard = folly::makeBlockingDisallowedGuard(executorName); + + thread->startupBaton.post(); + for (;;) { + auto task = take(); + + // Handle thread stopping + if (UNLIKELY(!task)) { + // Actually remove the thread from the list. + SharedMutex::WriteHolder w{&threadListLock_}; + for (auto& o : observers_) { + o->threadStopped(thread.get()); + } + threadList_.remove(thread); + stoppedThreads_.add(thread); + return; + } + + int iter = task->next(); + if (UNLIKELY(iter < 0)) { + // This task is already finished + continue; + } + + thread->idle = false; + auto startTime = std::chrono::steady_clock::now(); + task->stats_.waitTime = startTime - task->enqueueTime_; + try { + task->run(iter); + } catch (const std::exception& e) { + LOG(ERROR) << "EDFThreadPoolExecutor: func threw unhandled " + << typeid(e).name() << " exception: " << e.what(); + } catch (...) { + LOG(ERROR) + << "EDFThreadPoolExecutor: func threw unhandled non-exception object"; + } + task->stats_.runTime = std::chrono::steady_clock::now() - startTime; + thread->idle = true; + thread->lastActiveTime = std::chrono::steady_clock::now(); + thread->taskStatsCallbacks->callbackList.withRLock([&](auto& callbacks) { + *thread->taskStatsCallbacks->inCallback = true; + SCOPE_EXIT { + *thread->taskStatsCallbacks->inCallback = false; + }; + try { + for (auto& callback : callbacks) { + callback(task->stats_); + } + } catch (const std::exception& e) { + LOG(ERROR) << "EDFThreadPoolExecutor: task stats callback threw " + "unhandled " + << typeid(e).name() << " exception: " << e.what(); + } catch (...) { + LOG(ERROR) << "EDFThreadPoolExecutor: task stats callback threw " + "unhandled non-exception object"; + } + }); + } +} + +// threadListLock_ is writelocked. +void EDFThreadPoolExecutor::stopThreads(std::size_t numThreads) { + threadsToStop_.fetch_add(numThreads, std::memory_order_relaxed); + sem_.post(numThreads); +} + +// threadListLock_ is read (or write) locked. +std::size_t EDFThreadPoolExecutor::getPendingTaskCountImpl() const { + return taskQueue_->size(); +} + +bool EDFThreadPoolExecutor::shouldStop() { + // in normal cases, only do a read (prevents cache line bounces) + if (threadsToStop_.load(std::memory_order_relaxed) <= 0 || + isJoin_.load(std::memory_order_relaxed)) { + return false; + } + // modify only if needed + if (threadsToStop_.fetch_sub(1, std::memory_order_relaxed) > 0) { + return true; + } else { + threadsToStop_.fetch_add(1, std::memory_order_relaxed); + return false; + } +} + +std::shared_ptr<EDFThreadPoolExecutor::Task> EDFThreadPoolExecutor::take() { + if (UNLIKELY(shouldStop())) { + return nullptr; + } + + if (auto task = taskQueue_->pop()) { + return task; + } + + if (UNLIKELY(isJoin_.load(std::memory_order_relaxed))) { + return nullptr; + } + + // No tasks on the horizon, so go sleep + numIdleThreads_.fetch_add(1, std::memory_order_seq_cst); + + SCOPE_EXIT { + numIdleThreads_.fetch_sub(1, std::memory_order_seq_cst); + }; + + for (;;) { + if (UNLIKELY(shouldStop())) { + return nullptr; + } + + if (auto task = taskQueue_->pop()) { + // It's possible to return a finished task here, in which case + // the worker will call this function again. + return task; + } + + if (UNLIKELY(isJoin_.load(std::memory_order_relaxed))) { + return nullptr; + } + + sem_.wait(); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h new file mode 100644 index 000000000..e88958526 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstddef> +#include <memory> +#include <vector> + +#include <folly/executors/SoftRealTimeExecutor.h> +#include <folly/executors/ThreadPoolExecutor.h> +#include <folly/synchronization/LifoSem.h> + +namespace folly { + +/** + * `EDFThreadPoolExecutor` is a `SoftRealTimeExecutor` that implements + * the earliest-deadline-first scheduling policy. + */ +class EDFThreadPoolExecutor : public SoftRealTimeExecutor, + public ThreadPoolExecutor { + public: + class Task; + class TaskQueue; + + static constexpr uint64_t kEarliestDeadline = 0; + static constexpr uint64_t kLatestDeadline = + std::numeric_limits<uint64_t>::max(); + + explicit EDFThreadPoolExecutor( + std::size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("EDFThreadPool")); + + ~EDFThreadPoolExecutor() override; + + using ThreadPoolExecutor::add; + + void add(Func f) override; + void add(Func f, uint64_t deadline) override; + void add(Func f, std::size_t total, uint64_t deadline); + void add(std::vector<Func> fs, uint64_t deadline); + + folly::Executor::KeepAlive<> deadlineExecutor(uint64_t deadline); + + protected: + void threadRun(ThreadPtr thread) override; + void stopThreads(std::size_t numThreads) override; + std::size_t getPendingTaskCountImpl() const override final; + + private: + bool shouldStop(); + std::shared_ptr<Task> take(); + + std::unique_ptr<TaskQueue> taskQueue_; + LifoSem sem_; + std::atomic<int> threadsToStop_{0}; + + // All operations performed on `numIdleThreads_` explicitly specify memory + // ordering of `std::memory_order_seq_cst`. This is due to `numIdleThreads_` + // performing Dekker's algorithm with `numItems` prior to consumer threads + // (workers) wait on `sem_`. + std::atomic<std::size_t> numIdleThreads_{0}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h new file mode 100644 index 000000000..f1907568f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <glog/logging.h> + +namespace folly { +namespace detail { +template <typename Callback> +class ExecutorWithPriorityImpl : public virtual Executor { + public: + static Executor::KeepAlive<ExecutorWithPriorityImpl<std::decay_t<Callback>>> + create(Executor::KeepAlive<Executor> executor, Callback&& callback) { + return makeKeepAlive(new ExecutorWithPriorityImpl<std::decay_t<Callback>>( + executor, std::move(callback))); + } + ExecutorWithPriorityImpl(ExecutorWithPriorityImpl const&) = delete; + ExecutorWithPriorityImpl& operator=(ExecutorWithPriorityImpl const&) = delete; + ExecutorWithPriorityImpl(ExecutorWithPriorityImpl&&) = delete; + ExecutorWithPriorityImpl& operator=(ExecutorWithPriorityImpl&&) = delete; + + void add(Func func) override { + int8_t priority = callback_(); + executor_->addWithPriority(std::move(func), priority); + } + + protected: + bool keepAliveAcquire() override { + auto keepAliveCounter = + keepAliveCounter_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCounter > 0); + return true; + } + + void keepAliveRelease() override { + auto keepAliveCounter = + keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCounter > 0); + if (keepAliveCounter == 1) { + delete this; + } + } + + private: + ExecutorWithPriorityImpl( + Executor::KeepAlive<Executor> executor, + Callback&& callback) + : executor_(std::move(executor)), callback_(std::move(callback)) {} + std::atomic<ssize_t> keepAliveCounter_{1}; + Executor::KeepAlive<Executor> executor_; + Callback callback_; +}; +} // namespace detail + +template <typename Callback> +Executor::KeepAlive<> ExecutorWithPriority::createDynamic( + Executor::KeepAlive<Executor> executor, + Callback&& callback) { + return detail::ExecutorWithPriorityImpl<std::decay_t<Callback>>::create( + executor, std::move(callback)); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.cpp b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.cpp new file mode 100644 index 000000000..aeef80187 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/ExecutorWithPriority.h> + +namespace folly { +Executor::KeepAlive<> ExecutorWithPriority::create( + Executor::KeepAlive<Executor> executor, + int8_t priority) { + return ExecutorWithPriority::createDynamic( + executor, [priority]() { return priority; }); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.h b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.h new file mode 100644 index 000000000..00fe252e6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ExecutorWithPriority.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> +#include <atomic> + +namespace folly { +class ExecutorWithPriority { + public: + template <typename Callback> + static Executor::KeepAlive<> createDynamic( + Executor::KeepAlive<Executor> executor, + Callback&& callback); + + static Executor::KeepAlive<> create( + Executor::KeepAlive<Executor> executor, + int8_t priority); +}; +} // namespace folly + +#include <folly/executors/ExecutorWithPriority-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/executors/FiberIOExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/FiberIOExecutor.h new file mode 100644 index 000000000..01af00b1d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/FiberIOExecutor.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/executors/IOExecutor.h> +#include <folly/fibers/FiberManagerMap.h> + +namespace folly { + +/** + * @class FiberIOExecutor + * @brief An IOExecutor that executes funcs under mapped fiber context + * + * A FiberIOExecutor wraps an IOExecutor, but executes funcs on the FiberManager + * mapped to the underlying IOExector's event base. + */ +class FiberIOExecutor : public IOExecutor { + public: + explicit FiberIOExecutor( + const std::shared_ptr<IOExecutor>& ioExecutor, + fibers::FiberManager::Options opts = fibers::FiberManager::Options()) + : ioExecutor_(ioExecutor), options_(std::move(opts)) {} + + virtual void add(folly::Function<void()> f) override { + auto eventBase = ioExecutor_->getEventBase(); + folly::fibers::getFiberManager(*eventBase, options_).add(std::move(f)); + } + + virtual folly::EventBase* getEventBase() override { + return ioExecutor_->getEventBase(); + } + + private: + std::shared_ptr<IOExecutor> ioExecutor_; + fibers::FiberManager::Options options_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/FutureExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/FutureExecutor.h new file mode 100644 index 000000000..014c8cd2f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/FutureExecutor.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/functional/Invoke.h> +#include <folly/futures/Future.h> + +namespace folly { + +template <typename ExecutorImpl> +class FutureExecutor : public ExecutorImpl { + public: + template <typename... Args> + explicit FutureExecutor(Args&&... args) + : ExecutorImpl(std::forward<Args>(args)...) {} + + /* + * Given a function func that returns a Future<T>, adds that function to the + * contained Executor and returns a Future<T> which will be fulfilled with + * func's result once it has been executed. + * + * For example: auto f = futureExecutor.addFuture([](){ + * return doAsyncWorkAndReturnAFuture(); + * }); + */ + template <typename F> + typename std::enable_if< + folly::isFuture<invoke_result_t<F>>::value, + invoke_result_t<F>>::type + addFuture(F func) { + using T = typename invoke_result_t<F>::value_type; + folly::Promise<T> promise; + auto future = promise.getFuture(); + ExecutorImpl::add([promise = std::move(promise), + func = std::move(func)]() mutable { + func().then([promise = std::move(promise)](folly::Try<T>&& t) mutable { + promise.setTry(std::move(t)); + }); + }); + return future; + } + + /* + * Similar to addFuture above, but takes a func that returns some non-Future + * type T. + * + * For example: auto f = futureExecutor.addFuture([]() { + * return 42; + * }); + */ + template <typename F> + typename std::enable_if< + !folly::isFuture<invoke_result_t<F>>::value, + folly::Future<folly::lift_unit_t<invoke_result_t<F>>>>::type + addFuture(F func) { + using T = folly::lift_unit_t<invoke_result_t<F>>; + folly::Promise<T> promise; + auto future = promise.getFuture(); + ExecutorImpl::add( + [promise = std::move(promise), func = std::move(func)]() mutable { + promise.setWith(std::move(func)); + }); + return future; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.cpp new file mode 100644 index 000000000..2fb6921ad --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <memory> +#include <thread> + +#include <folly/Function.h> +#include <folly/SharedMutex.h> +#include <folly/Singleton.h> +#include <folly/detail/AsyncTrace.h> +#include <folly/executors/CPUThreadPoolExecutor.h> +#include <folly/executors/GlobalExecutor.h> +#include <folly/executors/IOExecutor.h> +#include <folly/executors/IOThreadPoolExecutor.h> +#include <folly/system/HardwareConcurrency.h> + +using namespace folly; + +namespace { + +class GlobalTag {}; + +// aka InlineExecutor +class DefaultCPUExecutor : public Executor { + public: + FOLLY_NOINLINE void add(Func f) override { + f(); + } +}; + +Singleton<std::shared_ptr<DefaultCPUExecutor>> gDefaultGlobalCPUExecutor([] { + return new std::shared_ptr<DefaultCPUExecutor>(new DefaultCPUExecutor{}); +}); + +Singleton<std::shared_ptr<CPUThreadPoolExecutor>, GlobalTag> + gImmutableGlobalCPUExecutor([] { + return new std::shared_ptr<CPUThreadPoolExecutor>( + new CPUThreadPoolExecutor( + folly::hardware_concurrency(), + std::make_shared<NamedThreadFactory>("GlobalCPUThreadPool"))); + }); + +Singleton<std::shared_ptr<IOThreadPoolExecutor>, GlobalTag> + gImmutableGlobalIOExecutor([] { + return new std::shared_ptr<IOThreadPoolExecutor>(new IOThreadPoolExecutor( + folly::hardware_concurrency(), + std::make_shared<NamedThreadFactory>("GlobalIOThreadPool"))); + }); + +template <class ExecutorBase> +std::shared_ptr<ExecutorBase> getImmutable(); + +template <> +std::shared_ptr<Executor> getImmutable() { + if (auto executorPtrPtr = gImmutableGlobalCPUExecutor.try_get()) { + return *executorPtrPtr; + } + return nullptr; +} + +template <> +std::shared_ptr<IOExecutor> getImmutable() { + if (auto executorPtrPtr = gImmutableGlobalIOExecutor.try_get()) { + return *executorPtrPtr; + } + return nullptr; +} + +template <class ExecutorBase> +class GlobalExecutor { + public: + explicit GlobalExecutor( + Function<std::shared_ptr<ExecutorBase>()> constructDefault) + : getDefault_(std::move(constructDefault)) {} + + std::shared_ptr<ExecutorBase> get() { + SharedMutex::ReadHolder guard(mutex_); + if (auto executor = executor_.lock()) { + return executor; // Fast path. + } + + return getDefault_(); + } + + void set(std::weak_ptr<ExecutorBase> executor) { + SharedMutex::WriteHolder guard(mutex_); + executor_.swap(executor); + } + + // Replace the constructDefault function to use the immutable singleton + // rather than the default singleton + void setFromImmutable() { + SharedMutex::WriteHolder guard(mutex_); + + getDefault_ = [] { return getImmutable<ExecutorBase>(); }; + executor_ = std::weak_ptr<ExecutorBase>{}; + } + + private: + SharedMutex mutex_; + std::weak_ptr<ExecutorBase> executor_; + Function<std::shared_ptr<ExecutorBase>()> getDefault_; +}; + +LeakySingleton<GlobalExecutor<Executor>> gGlobalCPUExecutor([] { + return new GlobalExecutor<Executor>( + // Default global CPU executor is an InlineExecutor. + [] { + if (auto executorPtrPtr = gDefaultGlobalCPUExecutor.try_get()) { + return *executorPtrPtr; + } + return std::shared_ptr<DefaultCPUExecutor>{}; + }); +}); + +LeakySingleton<GlobalExecutor<IOExecutor>> gGlobalIOExecutor([] { + return new GlobalExecutor<IOExecutor>( + // Default global IO executor is an IOThreadPoolExecutor. + [] { return getImmutable<IOExecutor>(); }); +}); + +} // namespace + +namespace folly { + +Executor::KeepAlive<> getGlobalCPUExecutor() { + auto executorPtr = getImmutable<Executor>(); + if (!executorPtr) { + throw std::runtime_error("Requested global CPU executor during shutdown."); + } + async_tracing::logGetImmutableCPUExecutor(executorPtr.get()); + return folly::getKeepAliveToken(executorPtr.get()); +} + +Executor::KeepAlive<> getGlobalIOExecutor() { + auto executorPtr = getImmutable<IOExecutor>(); + if (!executorPtr) { + throw std::runtime_error("Requested global IO executor during shutdown."); + } + async_tracing::logGetImmutableIOExecutor(executorPtr.get()); + return folly::getKeepAliveToken(executorPtr.get()); +} + +std::shared_ptr<Executor> getCPUExecutor() { + auto& singleton = gGlobalCPUExecutor.get(); + auto executor = singleton.get(); + async_tracing::logGetGlobalCPUExecutor(executor.get()); + return executor; +} + +void setCPUExecutorToGlobalCPUExecutor() { + async_tracing::logSetGlobalCPUExecutorToImmutable(); + gGlobalCPUExecutor.get().setFromImmutable(); +} + +void setCPUExecutor(std::weak_ptr<Executor> executor) { + async_tracing::logSetGlobalCPUExecutor(executor.lock().get()); + gGlobalCPUExecutor.get().set(std::move(executor)); +} + +std::shared_ptr<IOExecutor> getIOExecutor() { + auto& singleton = gGlobalIOExecutor.get(); + auto executor = singleton.get(); + async_tracing::logGetGlobalIOExecutor(executor.get()); + return executor; +} + +void setIOExecutor(std::weak_ptr<IOExecutor> executor) { + async_tracing::logSetGlobalIOExecutor(executor.lock().get()); + gGlobalIOExecutor.get().set(std::move(executor)); +} + +EventBase* getEventBase() { + auto executor = getIOExecutor(); + if (FOLLY_LIKELY(!!executor)) { + return executor->getEventBase(); + } + + return nullptr; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.h new file mode 100644 index 000000000..942b2f64b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/GlobalExecutor.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/Executor.h> +#include <folly/executors/IOExecutor.h> + +namespace folly { + +/** + * Return the global executor. + * The global executor is a CPU thread pool and is immutable. + * + * May return an invalid KeepAlive on shutdown. + */ +folly::Executor::KeepAlive<> getGlobalCPUExecutor(); + +/** + * Return the global IO executor. + * The global executor is an IO thread pool and is immutable. + * + * May return an invalid KeepAlive on shutdown. + */ +folly::Executor::KeepAlive<> getGlobalIOExecutor(); + +/** + * Retrieve the global Executor. If there is none, a default InlineExecutor + * will be constructed and returned. This is named CPUExecutor to distinguish + * it from IOExecutor below and to hint that it's intended for CPU-bound tasks. + * + * Can return nullptr on shutdown. + */ +std::shared_ptr<folly::Executor> getCPUExecutor(); + +/** + * Set an Executor to be the global Executor which will be returned by + * subsequent calls to getCPUExecutor(). + */ +void setCPUExecutor(std::weak_ptr<folly::Executor> executor); + +/** + * Set the CPU executor to the immutable default returned by + * getGlobalCPUExecutor. + */ +void setCPUExecutorToGlobalCPUExecutor(); + +/** + * Retrieve the global IOExecutor. If there is none, a default + * IOThreadPoolExecutor will be constructed and returned. + * + * IOExecutors differ from Executors in that they drive and provide access to + * one or more EventBases. + * + * Can return nullptr on shutdown. + */ +std::shared_ptr<IOExecutor> getIOExecutor(); + +/** + * Set an IOExecutor to be the global IOExecutor which will be returned by + * subsequent calls to getIOExecutor(). + */ +void setIOExecutor(std::weak_ptr<IOExecutor> executor); + +/** + * Retrieve an event base from the global IOExecutor + * + * NOTE: This is not shutdown-safe, the returned pointer may be + * invalid during shutdown. + */ +folly::EventBase* getEventBase(); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.cpp b/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.cpp new file mode 100644 index 000000000..b27f3e83e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/GlobalThreadPoolList.h> + +#include <memory> +#include <string> +#include <vector> + +#include <folly/CppAttributes.h> +#include <folly/Indestructible.h> +#include <folly/Synchronized.h> +#include <folly/ThreadLocal.h> + +namespace folly { + +namespace { + +class ThreadListHook { + public: + ThreadListHook(ThreadPoolListHook* poolId, std::thread::id threadId); + ~ThreadListHook(); + + private: + ThreadListHook() = default; + ThreadPoolListHook* poolId_; + std::thread::id threadId_; +}; + +class GlobalThreadPoolListImpl { + public: + GlobalThreadPoolListImpl() = default; + + void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); + + void unregisterThreadPool(ThreadPoolListHook* threadPoolId); + + void registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + void unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + private: + struct PoolInfo { + ThreadPoolListHook* addr; + std::string name; + std::vector<std::thread::id> threads; + }; + + struct Pools { + // Just a vector since ease of access from gdb is the most important + // property + std::vector<PoolInfo> poolsInfo_; + + std::vector<std::thread::id>* FOLLY_NULLABLE + getThreadVector(void* threadPoolId) { + for (auto& elem : vector()) { + if (elem.addr == threadPoolId) { + return &elem.threads; + } + } + + return nullptr; + } + + std::vector<PoolInfo>& vector() { + return poolsInfo_; + } + }; + + Pools pools_; +}; + +class GlobalThreadPoolList { + public: + GlobalThreadPoolList() = default; + + static GlobalThreadPoolList& instance(); + + void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); + + void unregisterThreadPool(ThreadPoolListHook* threadPoolId); + + void registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + void unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + GlobalThreadPoolList(GlobalThreadPoolList const&) = delete; + void operator=(GlobalThreadPoolList const&) = delete; + + private: + folly::Synchronized<GlobalThreadPoolListImpl> globalListImpl_; + folly::ThreadLocalPtr<ThreadListHook> threadHook_; +}; + +} // namespace + +GlobalThreadPoolList& GlobalThreadPoolList::instance() { + static folly::Indestructible<GlobalThreadPoolList> ret; + return *ret; +} + +void GlobalThreadPoolList::registerThreadPool( + ThreadPoolListHook* threadPoolId, + std::string name) { + globalListImpl_.wlock()->registerThreadPool(threadPoolId, name); +} + +void GlobalThreadPoolList::unregisterThreadPool( + ThreadPoolListHook* threadPoolId) { + globalListImpl_.wlock()->unregisterThreadPool(threadPoolId); +} + +void GlobalThreadPoolList::registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + DCHECK(!threadHook_); + threadHook_.reset(make_unique<ThreadListHook>(threadPoolId, threadId)); + + globalListImpl_.wlock()->registerThreadPoolThread(threadPoolId, threadId); +} + +void GlobalThreadPoolList::unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + (void)threadPoolId; + (void)threadId; + globalListImpl_.wlock()->unregisterThreadPoolThread(threadPoolId, threadId); +} + +void GlobalThreadPoolListImpl::registerThreadPool( + ThreadPoolListHook* threadPoolId, + std::string name) { + PoolInfo info; + info.name = name; + info.addr = threadPoolId; + pools_.vector().push_back(info); +} + +void GlobalThreadPoolListImpl::unregisterThreadPool( + ThreadPoolListHook* threadPoolId) { + auto& vector = pools_.vector(); + vector.erase( + std::remove_if( + vector.begin(), + vector.end(), + [=](PoolInfo& i) { return i.addr == threadPoolId; }), + vector.end()); +} + +void GlobalThreadPoolListImpl::registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + auto vec = pools_.getThreadVector(threadPoolId); + if (vec == nullptr) { + return; + } + + vec->push_back(threadId); +} + +void GlobalThreadPoolListImpl::unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + auto vec = pools_.getThreadVector(threadPoolId); + if (vec == nullptr) { + return; + } + + vec->erase(std::remove(vec->begin(), vec->end(), threadId), vec->end()); +} + +ThreadListHook::ThreadListHook( + ThreadPoolListHook* poolId, + std::thread::id threadId) { + poolId_ = poolId; + threadId_ = threadId; +} + +ThreadListHook::~ThreadListHook() { + GlobalThreadPoolList::instance().unregisterThreadPoolThread( + poolId_, threadId_); +} + +ThreadPoolListHook::ThreadPoolListHook(std::string name) { + GlobalThreadPoolList::instance().registerThreadPool(this, name); +} + +ThreadPoolListHook::~ThreadPoolListHook() { + GlobalThreadPoolList::instance().unregisterThreadPool(this); +} + +void ThreadPoolListHook::registerThread() { + GlobalThreadPoolList::instance().registerThreadPoolThread( + this, std::this_thread::get_id()); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.h b/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.h new file mode 100644 index 000000000..8ad82ee95 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/GlobalThreadPoolList.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <string> +#include <vector> + +#include <folly/Indestructible.h> +#include <folly/Synchronized.h> +#include <folly/ThreadLocal.h> + +namespace folly { + +/** + * A hook for tracking which threads belong to which thread pools. + * This is used only by a gdb extension to aid in debugging. You won't be able + * to see any useful information from within C++ code. + * + * An instance of ThreadPoolListHook should be created in the thread pool class + * that you want to keep track of. Then, to register a thread you call + * registerThread() on your instance of ThreadPoolListHook from that thread. + * + * When a thread exits it will be removed from the list + * When the thread pool is destroyed, it will be removed from the list + */ +class ThreadPoolListHook { + public: + /** + * Name is used to identify the thread pool when listing threads. + */ + explicit ThreadPoolListHook(std::string name); + ~ThreadPoolListHook(); + + /** + * Call this from any new thread that the thread pool creates. + */ + void registerThread(); + + ThreadPoolListHook(const ThreadPoolListHook& other) = delete; + ThreadPoolListHook& operator=(const ThreadPoolListHook&) = delete; + + private: + ThreadPoolListHook(); +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/IOExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/IOExecutor.h new file mode 100644 index 000000000..2dec869d7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/IOExecutor.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> + +namespace folly { +class EventBase; +} // namespace folly + +namespace folly { + +// An IOExecutor is an executor that operates on at least one EventBase. One of +// these EventBases should be accessible via getEventBase(). The event base +// returned by a call to getEventBase() is implementation dependent. +// +// Note that IOExecutors don't necessarily loop on the base themselves - for +// instance, EventBase itself is an IOExecutor but doesn't drive itself. +// +// Implementations of IOExecutor are eligible to become the global IO executor, +// returned on every call to getIOExecutor(), via setIOExecutor(). +// These functions are declared in GlobalExecutor.h +// +// If getIOExecutor is called and none has been set, a default global +// IOThreadPoolExecutor will be created and returned. +class IOExecutor : public virtual folly::Executor { + public: + ~IOExecutor() override = default; + virtual folly::EventBase* getEventBase() = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/IOObjectCache.h b/ios/Pods/Flipper-Folly/folly/executors/IOObjectCache.h new file mode 100644 index 000000000..f5e35464b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/IOObjectCache.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <map> + +#include <folly/ThreadLocal.h> +#include <folly/executors/GlobalExecutor.h> +#include <folly/io/async/EventBase.h> + +namespace folly { + +/* + * IOObjectCache manages objects of type T that are dependent on an EventBase + * provided by the global IOExecutor. + * + * Provide a factory that creates T objects given an EventBase, and get() will + * lazily create T objects based on an EventBase from the global IOExecutor. + * These are stored thread locally - for a given pair of event base and calling + * thread there will only be one T object created. + * + * The primary use case is for managing objects that need to do async IO on an + * event base (e.g. thrift clients) that can be used outside the IO thread + * without much hassle. For instance, you could use this to manage Thrift + * clients that are only ever called from within other threads without the + * calling thread needing to know anything about the IO threads that the clients + * will do their work on. + */ +template <class T> +class IOObjectCache { + public: + typedef std::function<std::shared_ptr<T>(folly::EventBase*)> TFactory; + + IOObjectCache() = default; + explicit IOObjectCache(TFactory factory) : factory_(std::move(factory)) {} + + std::shared_ptr<T> get() { + CHECK(factory_); + auto eb = getIOExecutor()->getEventBase(); + CHECK(eb); + auto it = cache_->find(eb); + if (it == cache_->end()) { + auto p = cache_->insert(std::make_pair(eb, factory_(eb))); + it = p.first; + } + return it->second; + } + + void setFactory(TFactory factory) { + factory_ = std::move(factory); + } + + private: + folly::ThreadLocal<std::map<folly::EventBase*, std::shared_ptr<T>>> cache_; + TFactory factory_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.cpp new file mode 100644 index 000000000..178c78d07 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/IOThreadPoolExecutor.h> + +#include <glog/logging.h> + +#include <folly/detail/MemoryIdler.h> +#include <folly/portability/GFlags.h> + +DEFINE_bool( + dynamic_iothreadpoolexecutor, + true, + "IOThreadPoolExecutor will dynamically create threads"); + +namespace folly { + +using folly::detail::MemoryIdler; + +/* Class that will free jemalloc caches and madvise the stack away + * if the event loop is unused for some period of time + */ +class MemoryIdlerTimeout : public AsyncTimeout, public EventBase::LoopCallback { + public: + explicit MemoryIdlerTimeout(EventBase* b) : AsyncTimeout(b), base_(b) {} + + void timeoutExpired() noexcept override { + idled = true; + } + + void runLoopCallback() noexcept override { + if (idled) { + MemoryIdler::flushLocalMallocCaches(); + MemoryIdler::unmapUnusedStack(MemoryIdler::kDefaultStackToRetain); + + idled = false; + } else { + std::chrono::steady_clock::duration idleTimeout = + MemoryIdler::defaultIdleTimeout.load(std::memory_order_acquire); + + idleTimeout = MemoryIdler::getVariationTimeout(idleTimeout); + + scheduleTimeout(static_cast<uint32_t>( + std::chrono::duration_cast<std::chrono::milliseconds>(idleTimeout) + .count())); + } + + // reschedule this callback for the next event loop. + base_->runBeforeLoop(this); + } + + private: + EventBase* base_; + bool idled{false}; +}; + +IOThreadPoolExecutor::IOThreadPoolExecutor( + size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory, + EventBaseManager* ebm, + bool waitForAll) + : ThreadPoolExecutor( + numThreads, + FLAGS_dynamic_iothreadpoolexecutor ? 0 : numThreads, + std::move(threadFactory), + waitForAll), + nextThread_(0), + eventBaseManager_(ebm) { + setNumThreads(numThreads); + registerThreadPoolExecutor(this); +} + +IOThreadPoolExecutor::~IOThreadPoolExecutor() { + deregisterThreadPoolExecutor(this); + stop(); +} + +void IOThreadPoolExecutor::add(Func func) { + add(std::move(func), std::chrono::milliseconds(0)); +} + +void IOThreadPoolExecutor::add( + Func func, + std::chrono::milliseconds expiration, + Func expireCallback) { + ensureActiveThreads(); + SharedMutex::ReadHolder r{&threadListLock_}; + if (threadList_.get().empty()) { + throw std::runtime_error("No threads available"); + } + auto ioThread = pickThread(); + + auto task = Task(std::move(func), expiration, std::move(expireCallback)); + auto wrappedFunc = [ioThread, task = std::move(task)]() mutable { + runTask(ioThread, std::move(task)); + ioThread->pendingTasks--; + }; + + ioThread->pendingTasks++; + ioThread->eventBase->runInEventBaseThread(std::move(wrappedFunc)); +} + +std::shared_ptr<IOThreadPoolExecutor::IOThread> +IOThreadPoolExecutor::pickThread() { + auto& me = *thisThread_; + auto& ths = threadList_.get(); + // When new task is added to IOThreadPoolExecutor, a thread is chosen for it + // to be executed on, thisThread_ is by default chosen, however, if the new + // task is added by the clean up operations on thread destruction, thisThread_ + // is not an available thread anymore, thus, always check whether or not + // thisThread_ is an available thread before choosing it. + if (me && std::find(ths.cbegin(), ths.cend(), me) != ths.cend()) { + return me; + } + auto n = ths.size(); + if (n == 0) { + // XXX I think the only way this can happen is if somebody calls + // getEventBase (1) from one of the executor's threads while the executor + // is stopping or getting downsized to zero or (2) from outside the executor + // when it has no threads. In the first case, it's not obvious what the + // correct behavior should be-- do we really want to return ourselves even + // though we're about to exit? (The comment above seems to imply no.) In + // the second case, `!me` so we'll crash anyway. + return me; + } + auto thread = ths[nextThread_.fetch_add(1, std::memory_order_relaxed) % n]; + return std::static_pointer_cast<IOThread>(thread); +} + +EventBase* IOThreadPoolExecutor::getEventBase() { + ensureActiveThreads(); + SharedMutex::ReadHolder r{&threadListLock_}; + if (threadList_.get().empty()) { + throw std::runtime_error("No threads available"); + } + return pickThread()->eventBase; +} + +EventBase* IOThreadPoolExecutor::getEventBase( + ThreadPoolExecutor::ThreadHandle* h) { + auto thread = dynamic_cast<IOThread*>(h); + + if (thread) { + return thread->eventBase; + } + + return nullptr; +} + +EventBaseManager* IOThreadPoolExecutor::getEventBaseManager() { + return eventBaseManager_; +} + +std::shared_ptr<ThreadPoolExecutor::Thread> IOThreadPoolExecutor::makeThread() { + return std::make_shared<IOThread>(this); +} + +void IOThreadPoolExecutor::threadRun(ThreadPtr thread) { + this->threadPoolHook_.registerThread(); + + const auto ioThread = std::static_pointer_cast<IOThread>(thread); + ioThread->eventBase = eventBaseManager_->getEventBase(); + thisThread_.reset(new std::shared_ptr<IOThread>(ioThread)); + + auto idler = std::make_unique<MemoryIdlerTimeout>(ioThread->eventBase); + ioThread->eventBase->runBeforeLoop(idler.get()); + + ioThread->eventBase->runInEventBaseThread( + [thread] { thread->startupBaton.post(); }); + while (ioThread->shouldRun) { + ioThread->eventBase->loopForever(); + } + if (isJoin_) { + while (ioThread->pendingTasks > 0) { + ioThread->eventBase->loopOnce(); + } + } + idler.reset(); + if (isWaitForAll_) { + // some tasks, like thrift asynchronous calls, create additional + // event base hookups, let's wait till all of them complete. + ioThread->eventBase->loop(); + } + + std::lock_guard<std::mutex> guard(ioThread->eventBaseShutdownMutex_); + ioThread->eventBase = nullptr; + eventBaseManager_->clearEventBase(); +} + +// threadListLock_ is writelocked +void IOThreadPoolExecutor::stopThreads(size_t n) { + std::vector<ThreadPtr> stoppedThreads; + stoppedThreads.reserve(n); + for (size_t i = 0; i < n; i++) { + const auto ioThread = + std::static_pointer_cast<IOThread>(threadList_.get()[i]); + for (auto& o : observers_) { + o->threadStopped(ioThread.get()); + } + ioThread->shouldRun = false; + stoppedThreads.push_back(ioThread); + std::lock_guard<std::mutex> guard(ioThread->eventBaseShutdownMutex_); + if (ioThread->eventBase) { + ioThread->eventBase->terminateLoopSoon(); + } + } + for (const auto& thread : stoppedThreads) { + stoppedThreads_.add(thread); + threadList_.remove(thread); + } +} + +// threadListLock_ is readlocked +size_t IOThreadPoolExecutor::getPendingTaskCountImpl() const { + size_t count = 0; + for (const auto& thread : threadList_.get()) { + auto ioThread = std::static_pointer_cast<IOThread>(thread); + size_t pendingTasks = ioThread->pendingTasks; + if (pendingTasks > 0 && !ioThread->idle) { + pendingTasks--; + } + count += pendingTasks; + } + return count; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h new file mode 100644 index 000000000..934b6249b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +#include <folly/Portability.h> +#include <folly/executors/IOExecutor.h> +#include <folly/executors/ThreadPoolExecutor.h> +#include <folly/io/async/EventBaseManager.h> + +namespace folly { + +FOLLY_PUSH_WARNING +// Suppress "IOThreadPoolExecutor inherits DefaultKeepAliveExecutor +// keepAliveAcquire/keepAliveRelease via dominance" +FOLLY_MSVC_DISABLE_WARNING(4250) + +/** + * A Thread Pool for IO bound tasks + * + * @note Uses event_fd for notification, and waking an epoll loop. + * There is one queue (NotificationQueue specifically) per thread/epoll. + * If the thread is already running and not waiting on epoll, + * we don't make any additional syscalls to wake up the loop, + * just put the new task in the queue. + * If any thread has been waiting for more than a few seconds, + * its stack is madvised away. Currently however tasks are scheduled round + * robin on the queues, so unless there is no work going on, + * this isn't very effective. + * Since there is one queue per thread, there is hardly any contention + * on the queues - so a simple spinlock around an std::deque is used for + * the tasks. There is no max queue size. + * By default, there is one thread per core - it usually doesn't make sense to + * have more IO threads than this, assuming they don't block. + * + * @note ::getEventBase() will return an EventBase you can schedule IO work on + * directly, chosen round-robin. + * + * @note N.B. For this thread pool, stop() behaves like join() because + * outstanding tasks belong to the event base and will be executed upon its + * destruction. + */ +class IOThreadPoolExecutor : public ThreadPoolExecutor, public IOExecutor { + public: + explicit IOThreadPoolExecutor( + size_t numThreads, + std::shared_ptr<ThreadFactory> threadFactory = + std::make_shared<NamedThreadFactory>("IOThreadPool"), + folly::EventBaseManager* ebm = folly::EventBaseManager::get(), + bool waitForAll = false); + + ~IOThreadPoolExecutor() override; + + void add(Func func) override; + void add( + Func func, + std::chrono::milliseconds expiration, + Func expireCallback = nullptr) override; + + folly::EventBase* getEventBase() override; + + static folly::EventBase* getEventBase(ThreadPoolExecutor::ThreadHandle*); + + folly::EventBaseManager* getEventBaseManager(); + + private: + struct alignas(folly::cacheline_align_v) IOThread : public Thread { + IOThread(IOThreadPoolExecutor* pool) + : Thread(pool), shouldRun(true), pendingTasks(0) {} + std::atomic<bool> shouldRun; + std::atomic<size_t> pendingTasks; + folly::EventBase* eventBase; + std::mutex eventBaseShutdownMutex_; + }; + + ThreadPtr makeThread() override; + std::shared_ptr<IOThread> pickThread(); + void threadRun(ThreadPtr thread) override; + void stopThreads(size_t n) override; + size_t getPendingTaskCountImpl() const override final; + + std::atomic<size_t> nextThread_; + folly::ThreadLocal<std::shared_ptr<IOThread>> thisThread_; + folly::EventBaseManager* eventBaseManager_; +}; + +FOLLY_POP_WARNING + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.cpp new file mode 100644 index 000000000..b8c3f211c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/InlineExecutor.h> + +#include <folly/Indestructible.h> + +namespace folly { + +InlineExecutor& InlineExecutor::instance_slow() noexcept { + static auto instance = Indestructible<InlineExecutor>{}; + cache.store(&*instance, std::memory_order_release); + return *instance; +} + +std::atomic<InlineExecutor*> InlineExecutor::cache; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.h new file mode 100644 index 000000000..316743ecf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/InlineExecutor.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +#include <folly/CPortability.h> +#include <folly/CppAttributes.h> +#include <folly/Executor.h> + +namespace folly { + +/// When work is "queued", execute it immediately inline. +/// Usually when you think you want this, you actually want a +/// QueuedImmediateExecutor. +class InlineExecutor : public Executor { + public: + FOLLY_ERASE static InlineExecutor& instance() noexcept { + auto const value = cache.load(std::memory_order_acquire); + return value ? *value : instance_slow(); + } + + void add(Func f) override { + f(); + } + + private: + FOLLY_COLD static InlineExecutor& instance_slow() noexcept; + + static std::atomic<InlineExecutor*> cache; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.cpp new file mode 100644 index 000000000..075e6ebfb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/ManualExecutor.h> + +#include <cstring> +#include <string> +#include <tuple> + +namespace folly { + +ManualExecutor::~ManualExecutor() { + while (keepAliveCount_.load(std::memory_order_relaxed)) { + drive(); + } + drain(); +} + +void ManualExecutor::add(Func callback) { + std::lock_guard<std::mutex> lock(lock_); + funcs_.emplace(std::move(callback)); + sem_.post(); +} + +size_t ManualExecutor::run() { + size_t count; + size_t n; + Func func; + + { + std::lock_guard<std::mutex> lock(lock_); + + while (!scheduledFuncs_.empty()) { + auto& sf = scheduledFuncs_.top(); + if (sf.time > now_) { + break; + } + funcs_.emplace(sf.moveOutFunc()); + scheduledFuncs_.pop(); + } + + n = funcs_.size(); + } + + for (count = 0; count < n; count++) { + { + std::lock_guard<std::mutex> lock(lock_); + if (funcs_.empty()) { + break; + } + + // Balance the semaphore so it doesn't grow without bound + // if nobody is calling wait(). + // This may fail (with EAGAIN), that's fine. + sem_.tryWait(); + + func = std::move(funcs_.front()); + funcs_.pop(); + } + func(); + func = nullptr; + } + + return count; +} + +size_t ManualExecutor::drain() { + size_t tasksRun = 0; + size_t tasksForSingleRun = 0; + while ((tasksForSingleRun = run()) != 0) { + tasksRun += tasksForSingleRun; + } + return tasksRun; +} + +void ManualExecutor::wait() { + while (true) { + { + std::lock_guard<std::mutex> lock(lock_); + if (!funcs_.empty()) { + break; + } + } + + sem_.wait(); + } +} + +void ManualExecutor::advanceTo(TimePoint const& t) { + if (t > now_) { + now_ = t; + } + run(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.h new file mode 100644 index 000000000..f66c0b365 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdio> +#include <memory> +#include <mutex> +#include <queue> + +#include <folly/executors/DrivableExecutor.h> +#include <folly/executors/ScheduledExecutor.h> +#include <folly/executors/SequencedExecutor.h> +#include <folly/synchronization/LifoSem.h> + +namespace folly { +/// A ManualExecutor only does work when you turn the crank, by calling +/// run() or indirectly with makeProgress() or waitFor(). +/// +/// The clock for a manual executor starts at 0 and advances only when you +/// ask it to. i.e. time is also under manual control. +/// +/// NB No attempt has been made to make anything other than add and schedule +/// threadsafe. +class ManualExecutor : public DrivableExecutor, + public ScheduledExecutor, + public SequencedExecutor { + public: + ~ManualExecutor(); + + void add(Func) override; + + /// Do work. Returns the number of functions that were executed (maybe 0). + /// Non-blocking, in the sense that we don't wait for work (we can't + /// control whether one of the functions blocks). + /// This is stable, it will not chase an ever-increasing tail of work. + /// This also means, there may be more work available to perform at the + /// moment that this returns. + size_t run(); + + // Do work until there is no more work to do. + // Returns the number of functions that were executed (maybe 0). + // Unlike run, this method is not stable. It will chase an infinite tail of + // work so should be used with care. + // There will be no work available to perform at the moment that this + // returns. + size_t drain(); + + /// Wait for work to do. + void wait(); + + /// Wait for work to do, and do it. + void makeProgress() { + wait(); + run(); + } + + /// Implements DrivableExecutor + void drive() override { + makeProgress(); + } + + /// makeProgress until this Future is ready. + template <class F> + void waitFor(F const& f) { + // TODO(5427828) +#if 0 + while (!f.isReady()) + makeProgress(); +#else + while (!f.isReady()) { + run(); + } +#endif + } + + void scheduleAt(Func&& f, TimePoint const& t) override { + std::lock_guard<std::mutex> lock(lock_); + scheduledFuncs_.emplace(t, std::move(f)); + sem_.post(); + } + + /// Advance the clock. The clock never advances on its own. + /// Advancing the clock causes some work to be done, if work is available + /// to do (perhaps newly available because of the advanced clock). + /// If dur is <= 0 this is a noop. + void advance(Duration const& dur) { + advanceTo(now_ + dur); + } + + /// Advance the clock to this absolute time. If t is <= now(), + /// this is a noop. + void advanceTo(TimePoint const& t); + + TimePoint now() override { + return now_; + } + + /// Flush the function queue. Destroys all stored functions without + /// executing them. Returns number of removed functions. + std::size_t clear() { + std::queue<Func> funcs; + std::priority_queue<ScheduledFunc> scheduled_funcs; + + { + std::lock_guard<std::mutex> lock(lock_); + funcs_.swap(funcs); + scheduledFuncs_.swap(scheduled_funcs); + } + + return funcs.size() + scheduled_funcs.size(); + } + + bool keepAliveAcquire() override { + keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + return true; + } + + void keepAliveRelease() override { + if (keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel) == 1) { + add([] {}); + } + } + + private: + std::mutex lock_; + std::queue<Func> funcs_; + LifoSem sem_; + + // helper class to enable ordering of scheduled events in the priority + // queue + struct ScheduledFunc { + TimePoint time; + size_t ordinal; + Func mutable func; + + ScheduledFunc(TimePoint const& t, Func&& f) : time(t), func(std::move(f)) { + static size_t seq = 0; + ordinal = seq++; + } + + bool operator<(ScheduledFunc const& b) const { + // Earlier-scheduled things must be *higher* priority + // in the max-based std::priority_queue + if (time == b.time) { + return ordinal > b.ordinal; + } + return time > b.time; + } + + Func&& moveOutFunc() const { + return std::move(func); + } + }; + std::priority_queue<ScheduledFunc> scheduledFuncs_; + TimePoint now_ = TimePoint::min(); + + std::atomic<ssize_t> keepAliveCount_{0}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.cpp new file mode 100644 index 000000000..9bee2eac6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/QueuedImmediateExecutor.h> + +#include <folly/Indestructible.h> + +namespace folly { + +QueuedImmediateExecutor& QueuedImmediateExecutor::instance() { + static auto instance = Indestructible<QueuedImmediateExecutor>{}; + return *instance; +} + +void QueuedImmediateExecutor::add(Func callback) { + auto& q = *q_; + q.push(std::move(callback)); + if (q.size() == 1) { + while (!q.empty()) { + q.front()(); + q.pop(); + } + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h new file mode 100644 index 000000000..332661ce5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <queue> + +#include <folly/Executor.h> +#include <folly/ThreadLocal.h> + +namespace folly { + +/** + * Runs inline like InlineExecutor, but with a queue so that any tasks added + * to this executor by one of its own callbacks will be queued instead of + * executed inline (nested). This is usually better behavior than Inline. + */ +class QueuedImmediateExecutor : public Executor { + public: + static QueuedImmediateExecutor& instance(); + + void add(Func callback) override; + + private: + folly::ThreadLocal<std::queue<Func>> q_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ScheduledExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/ScheduledExecutor.h new file mode 100644 index 000000000..84c2eb449 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ScheduledExecutor.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <memory> +#include <stdexcept> + +#include <folly/Executor.h> +#include <folly/lang/Exception.h> + +namespace folly { +// An executor that supports timed scheduling. Like RxScheduler. +class ScheduledExecutor : public virtual Executor { + public: + // Reality is that better than millisecond resolution is very hard to + // achieve. However, we reserve the right to be incredible. + typedef std::chrono::microseconds Duration; + typedef std::chrono::steady_clock::time_point TimePoint; + + ~ScheduledExecutor() override = default; + + void add(Func) override = 0; + + /// Alias for add() (for Rx consistency) + void schedule(Func&& a) { + add(std::move(a)); + } + + /// Schedule a Func to be executed after dur time has elapsed + /// Expect millisecond resolution at best. + void schedule(Func&& a, Duration const& dur) { + scheduleAt(std::move(a), now() + dur); + } + + /// Schedule a Func to be executed at time t, or as soon afterward as + /// possible. Expect millisecond resolution at best. Must be threadsafe. + virtual void scheduleAt(Func&& /* a */, TimePoint const& /* t */) { + throw_exception<std::logic_error>("unimplemented"); + } + + /// Get this executor's notion of time. Must be threadsafe. + virtual TimePoint now() { + return std::chrono::steady_clock::now(); + } +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/SequencedExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/SequencedExecutor.h new file mode 100644 index 000000000..1f3a5b426 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/SequencedExecutor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> + +namespace folly { + +// SequencedExecutor is an executor that provides the following guarantee: +// if add(A) and add(B) were sequenced (i.e. add(B) was called after add(A) call +// had returned to the caller) then execution of A and B will be sequenced +// (i.e. B() can be called only after A() returns) too. +class SequencedExecutor : public virtual Executor {}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.cpp new file mode 100644 index 000000000..dfdd849f9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/SerialExecutor.h> + +#include <glog/logging.h> + +#include <folly/ExceptionString.h> + +namespace folly { + +SerialExecutor::SerialExecutor(KeepAlive<Executor> parent) + : parent_(std::move(parent)) {} + +SerialExecutor::~SerialExecutor() { + DCHECK(!keepAliveCounter_); +} + +Executor::KeepAlive<SerialExecutor> SerialExecutor::create( + KeepAlive<Executor> parent) { + return makeKeepAlive<SerialExecutor>(new SerialExecutor(std::move(parent))); +} + +SerialExecutor::UniquePtr SerialExecutor::createUnique( + std::shared_ptr<Executor> parent) { + auto executor = new SerialExecutor(getKeepAliveToken(parent.get())); + return {executor, Deleter{std::move(parent)}}; +} + +bool SerialExecutor::keepAliveAcquire() { + auto keepAliveCounter = + keepAliveCounter_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCounter > 0); + return true; +} + +void SerialExecutor::keepAliveRelease() { + auto keepAliveCounter = + keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCounter > 0); + if (keepAliveCounter == 1) { + delete this; + } +} + +void SerialExecutor::add(Func func) { + queue_.enqueue(Task{std::move(func), RequestContext::saveContext()}); + parent_->add([keepAlive = getKeepAliveToken(this)] { keepAlive->run(); }); +} + +void SerialExecutor::addWithPriority(Func func, int8_t priority) { + queue_.enqueue(Task{std::move(func), RequestContext::saveContext()}); + parent_->addWithPriority( + [keepAlive = getKeepAliveToken(this)] { keepAlive->run(); }, priority); +} + +void SerialExecutor::run() { + // We want scheduled_ to guard side-effects of completed tasks, so we can't + // use std::memory_order_relaxed here. + if (scheduled_.fetch_add(1, std::memory_order_acquire) > 0) { + return; + } + + do { + Task task; + queue_.dequeue(task); + + try { + folly::RequestContextScopeGuard ctxGuard(std::move(task.ctx)); + auto func = std::move(task.func); + func(); + } catch (std::exception const& ex) { + LOG(ERROR) << "SerialExecutor: func threw unhandled exception " + << folly::exceptionStr(ex); + } catch (...) { + LOG(ERROR) << "SerialExecutor: func threw unhandled non-exception " + "object"; + } + + // We want scheduled_ to guard side-effects of completed tasks, so we can't + // use std::memory_order_relaxed here. + } while (scheduled_.fetch_sub(1, std::memory_order_release) > 1); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.h new file mode 100644 index 000000000..eed17987d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/SerialExecutor.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <memory> +#include <mutex> + +#include <folly/concurrency/UnboundedQueue.h> +#include <folly/executors/GlobalExecutor.h> +#include <folly/executors/SequencedExecutor.h> +#include <folly/io/async/Request.h> + +namespace folly { + +/** + * @class SerialExecutor + * + * @brief Executor that guarantees serial non-concurrent execution of added + * tasks + * + * SerialExecutor is similar to boost asio's strand concept. A SerialExecutor + * has a parent executor which is given at construction time (defaults to + * folly's global CPUExecutor). Tasks added to SerialExecutor are executed + * in the parent executor, however strictly non-concurrently and in the order + * they were added. + * + * SerialExecutor tries to schedule its tasks fairly. Every task submitted to + * it results in one task submitted to the parent executor. Whenever the parent + * executor executes one of those, one of the tasks submitted to SerialExecutor + * is marked for execution, which means it will either be executed at once, + * or if a task is currently being executed already, after that. + * + * The SerialExecutor may be deleted at any time. All tasks that have been + * submitted will still be executed with the same guarantees, as long as the + * parent executor is executing tasks. + */ + +class SerialExecutor : public SequencedExecutor { + public: + SerialExecutor(SerialExecutor const&) = delete; + SerialExecutor& operator=(SerialExecutor const&) = delete; + SerialExecutor(SerialExecutor&&) = delete; + SerialExecutor& operator=(SerialExecutor&&) = delete; + + static KeepAlive<SerialExecutor> create( + KeepAlive<Executor> parent = getKeepAliveToken(getCPUExecutor().get())); + + class Deleter { + public: + Deleter() {} + + void operator()(SerialExecutor* executor) { + executor->keepAliveRelease(); + } + + private: + friend class SerialExecutor; + explicit Deleter(std::shared_ptr<Executor> parent) + : parent_(std::move(parent)) {} + + std::shared_ptr<Executor> parent_; + }; + + using UniquePtr = std::unique_ptr<SerialExecutor, Deleter>; + [[deprecated("Replaced by create")]] static UniquePtr createUnique( + std::shared_ptr<Executor> parent = getCPUExecutor()); + + /** + * Add one task for execution in the parent executor + */ + void add(Func func) override; + + /** + * Add one task for execution in the parent executor, and use the given + * priority for one task submission to parent executor. + * + * Since in-order execution of tasks submitted to SerialExecutor is + * guaranteed, the priority given here does not necessarily reflect the + * execution priority of the task submitted with this call to + * `addWithPriority`. The given priority is passed on to the parent executor + * for the execution of one of the SerialExecutor's tasks. + */ + void addWithPriority(Func func, int8_t priority) override; + uint8_t getNumPriorities() const override { + return parent_->getNumPriorities(); + } + + protected: + bool keepAliveAcquire() override; + + void keepAliveRelease() override; + + private: + struct Task { + Func func; + std::shared_ptr<RequestContext> ctx; + }; + + explicit SerialExecutor(KeepAlive<Executor> parent); + ~SerialExecutor() override; + + void run(); + + KeepAlive<Executor> parent_; + std::atomic<std::size_t> scheduled_{0}; + /** + * Unbounded multi producer single consumer queue where consumers don't block + * on dequeue. + */ + folly::UnboundedQueue<Task, false, true, false> queue_; + + std::atomic<ssize_t> keepAliveCounter_{1}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h new file mode 100644 index 000000000..05ea49ec4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> + +namespace folly { + +// `SoftRealTimeExecutor` is an executor that performs some priority-based +// scheduling with a deadline assigned to each task. __Soft__ real-time +// means that not every deadline is guaranteed to be met. +class SoftRealTimeExecutor : public virtual Executor { + void add(Func) override = 0; + + // Add a task with an assigned abstract deadline. + // + // NOTE: The type of `deadline` was chosen to be an integral rather than + // a typed time point or duration (e.g., `std::chrono::time_point`) to allow + // for flexbility. While the deadline for a task may be a time point, + // it could also be a duration or the size of the task, which emulates + // rate-monotonic scheduling that prioritizes small tasks. It also enables + // for exmaple, tiered scheduling (strictly prioritizing a category of tasks) + // by assigning the high-bit of the deadline. + virtual void add(Func, uint64_t deadline) = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.cpp new file mode 100644 index 000000000..ee220bdcd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/ThreadPoolExecutor.h> + +#include <folly/executors/GlobalThreadPoolList.h> +#include <folly/synchronization/AsymmetricMemoryBarrier.h> + +namespace folly { + +using SyncVecThreadPoolExecutors = + folly::Synchronized<std::vector<ThreadPoolExecutor*>>; + +SyncVecThreadPoolExecutors& getSyncVecThreadPoolExecutors() { + static Indestructible<SyncVecThreadPoolExecutors> storage; + return *storage; +} + +void ThreadPoolExecutor::registerThreadPoolExecutor(ThreadPoolExecutor* tpe) { + getSyncVecThreadPoolExecutors().wlock()->push_back(tpe); +} + +void ThreadPoolExecutor::deregisterThreadPoolExecutor(ThreadPoolExecutor* tpe) { + getSyncVecThreadPoolExecutors().withWLock([tpe](auto& tpes) { + tpes.erase(std::remove(tpes.begin(), tpes.end(), tpe), tpes.end()); + }); +} + +DEFINE_int64( + threadtimeout_ms, + 60000, + "Idle time before ThreadPoolExecutor threads are joined"); + +ThreadPoolExecutor::ThreadPoolExecutor( + size_t /* maxThreads */, + size_t minThreads, + std::shared_ptr<ThreadFactory> threadFactory, + bool isWaitForAll) + : threadFactory_(std::move(threadFactory)), + isWaitForAll_(isWaitForAll), + taskStatsCallbacks_(std::make_shared<TaskStatsCallbackRegistry>()), + threadPoolHook_("folly::ThreadPoolExecutor"), + minThreads_(minThreads), + threadTimeout_(FLAGS_threadtimeout_ms) {} + +ThreadPoolExecutor::~ThreadPoolExecutor() { + joinKeepAliveOnce(); + CHECK_EQ(0, threadList_.get().size()); +} + +ThreadPoolExecutor::Task::Task( + Func&& func, + std::chrono::milliseconds expiration, + Func&& expireCallback) + : func_(std::move(func)), + expiration_(expiration), + expireCallback_(std::move(expireCallback)), + context_(folly::RequestContext::saveContext()) { + // Assume that the task in enqueued on creation + enqueueTime_ = std::chrono::steady_clock::now(); +} + +void ThreadPoolExecutor::runTask(const ThreadPtr& thread, Task&& task) { + thread->idle = false; + auto startTime = std::chrono::steady_clock::now(); + task.stats_.waitTime = startTime - task.enqueueTime_; + if (task.expiration_ > std::chrono::milliseconds(0) && + task.stats_.waitTime >= task.expiration_) { + task.stats_.expired = true; + if (task.expireCallback_ != nullptr) { + task.expireCallback_(); + } + } else { + folly::RequestContextScopeGuard rctx(task.context_); + try { + task.func_(); + } catch (const std::exception& e) { + LOG(ERROR) << "ThreadPoolExecutor: func threw unhandled " + << typeid(e).name() << " exception: " << e.what(); + } catch (...) { + LOG(ERROR) << "ThreadPoolExecutor: func threw unhandled non-exception " + "object"; + } + task.stats_.runTime = std::chrono::steady_clock::now() - startTime; + } + thread->idle = true; + thread->lastActiveTime = std::chrono::steady_clock::now(); + thread->taskStatsCallbacks->callbackList.withRLock([&](auto& callbacks) { + *thread->taskStatsCallbacks->inCallback = true; + SCOPE_EXIT { + *thread->taskStatsCallbacks->inCallback = false; + }; + try { + for (auto& callback : callbacks) { + callback(task.stats_); + } + } catch (const std::exception& e) { + LOG(ERROR) << "ThreadPoolExecutor: task stats callback threw " + "unhandled " + << typeid(e).name() << " exception: " << e.what(); + } catch (...) { + LOG(ERROR) << "ThreadPoolExecutor: task stats callback threw " + "unhandled non-exception object"; + } + }); +} + +void ThreadPoolExecutor::add(Func, std::chrono::milliseconds, Func) { + throw std::runtime_error( + "add() with expiration is not implemented for this Executor"); +} + +size_t ThreadPoolExecutor::numThreads() const { + return maxThreads_.load(std::memory_order_relaxed); +} + +size_t ThreadPoolExecutor::numActiveThreads() const { + return activeThreads_.load(std::memory_order_relaxed); +} + +// Set the maximum number of running threads. +void ThreadPoolExecutor::setNumThreads(size_t numThreads) { + /* Since ThreadPoolExecutor may be dynamically adjusting the number of + threads, we adjust the relevant variables instead of changing + the number of threads directly. Roughly: + + If numThreads < minthreads reset minThreads to numThreads. + + If numThreads < active threads, reduce number of running threads. + + If the number of pending tasks is > 0, then increase the currently + active number of threads such that we can run all the tasks, or reach + numThreads. + + Note that if there are observers, we actually have to create all + the threads, because some observer implementations need to 'observe' + all thread creation (see tests for an example of this) + */ + + size_t numThreadsToJoin = 0; + { + SharedMutex::WriteHolder w{&threadListLock_}; + auto pending = getPendingTaskCountImpl(); + maxThreads_.store(numThreads, std::memory_order_relaxed); + auto active = activeThreads_.load(std::memory_order_relaxed); + auto minthreads = minThreads_.load(std::memory_order_relaxed); + if (numThreads < minthreads) { + minthreads = numThreads; + minThreads_.store(numThreads, std::memory_order_relaxed); + } + if (active > numThreads) { + numThreadsToJoin = active - numThreads; + if (numThreadsToJoin > active - minthreads) { + numThreadsToJoin = active - minthreads; + } + ThreadPoolExecutor::removeThreads(numThreadsToJoin, false); + activeThreads_.store( + active - numThreadsToJoin, std::memory_order_relaxed); + } else if (pending > 0 || !observers_.empty() || active < minthreads) { + size_t numToAdd = std::min(pending, numThreads - active); + if (!observers_.empty()) { + numToAdd = numThreads - active; + } + if (active + numToAdd < minthreads) { + numToAdd = minthreads - active; + } + ThreadPoolExecutor::addThreads(numToAdd); + activeThreads_.store(active + numToAdd, std::memory_order_relaxed); + } + } + + /* We may have removed some threads, attempt to join them */ + joinStoppedThreads(numThreadsToJoin); +} + +// threadListLock_ is writelocked +void ThreadPoolExecutor::addThreads(size_t n) { + std::vector<ThreadPtr> newThreads; + for (size_t i = 0; i < n; i++) { + newThreads.push_back(makeThread()); + } + for (auto& thread : newThreads) { + // TODO need a notion of failing to create the thread + // and then handling for that case + thread->handle = threadFactory_->newThread( + std::bind(&ThreadPoolExecutor::threadRun, this, thread)); + threadList_.add(thread); + } + for (auto& thread : newThreads) { + thread->startupBaton.wait( + folly::Baton<>::wait_options().logging_enabled(false)); + } + for (auto& o : observers_) { + for (auto& thread : newThreads) { + o->threadStarted(thread.get()); + } + } +} + +// threadListLock_ is writelocked +void ThreadPoolExecutor::removeThreads(size_t n, bool isJoin) { + isJoin_ = isJoin; + stopThreads(n); +} + +void ThreadPoolExecutor::joinStoppedThreads(size_t n) { + for (size_t i = 0; i < n; i++) { + auto thread = stoppedThreads_.take(); + thread->handle.join(); + } +} + +void ThreadPoolExecutor::stop() { + joinKeepAliveOnce(); + size_t n = 0; + { + SharedMutex::WriteHolder w{&threadListLock_}; + maxThreads_.store(0, std::memory_order_release); + activeThreads_.store(0, std::memory_order_release); + n = threadList_.get().size(); + removeThreads(n, false); + n += threadsToJoin_.load(std::memory_order_relaxed); + threadsToJoin_.store(0, std::memory_order_relaxed); + } + joinStoppedThreads(n); + CHECK_EQ(0, threadList_.get().size()); + CHECK_EQ(0, stoppedThreads_.size()); +} + +void ThreadPoolExecutor::join() { + joinKeepAliveOnce(); + size_t n = 0; + { + SharedMutex::WriteHolder w{&threadListLock_}; + maxThreads_.store(0, std::memory_order_release); + activeThreads_.store(0, std::memory_order_release); + n = threadList_.get().size(); + removeThreads(n, true); + n += threadsToJoin_.load(std::memory_order_relaxed); + threadsToJoin_.store(0, std::memory_order_relaxed); + } + joinStoppedThreads(n); + CHECK_EQ(0, threadList_.get().size()); + CHECK_EQ(0, stoppedThreads_.size()); +} + +void ThreadPoolExecutor::withAll(FunctionRef<void(ThreadPoolExecutor&)> f) { + getSyncVecThreadPoolExecutors().withRLock([f](auto& tpes) { + for (auto tpe : tpes) { + f(*tpe); + } + }); +} + +ThreadPoolExecutor::PoolStats ThreadPoolExecutor::getPoolStats() const { + const auto now = std::chrono::steady_clock::now(); + SharedMutex::ReadHolder r{&threadListLock_}; + ThreadPoolExecutor::PoolStats stats; + size_t activeTasks = 0; + size_t idleAlive = 0; + for (const auto& thread : threadList_.get()) { + if (thread->idle) { + const std::chrono::nanoseconds idleTime = now - thread->lastActiveTime; + stats.maxIdleTime = std::max(stats.maxIdleTime, idleTime); + idleAlive++; + } else { + activeTasks++; + } + } + stats.pendingTaskCount = getPendingTaskCountImpl(); + stats.totalTaskCount = stats.pendingTaskCount + activeTasks; + + stats.threadCount = maxThreads_.load(std::memory_order_relaxed); + stats.activeThreadCount = + activeThreads_.load(std::memory_order_relaxed) - idleAlive; + stats.idleThreadCount = stats.threadCount - stats.activeThreadCount; + return stats; +} + +size_t ThreadPoolExecutor::getPendingTaskCount() const { + SharedMutex::ReadHolder r{&threadListLock_}; + return getPendingTaskCountImpl(); +} + +std::string ThreadPoolExecutor::getName() const { + auto ntf = dynamic_cast<NamedThreadFactory*>(threadFactory_.get()); + if (ntf == nullptr) { + return folly::demangle(typeid(*this).name()).toStdString(); + } + + return ntf->getNamePrefix(); +} + +std::atomic<uint64_t> ThreadPoolExecutor::Thread::nextId(0); + +void ThreadPoolExecutor::subscribeToTaskStats(TaskStatsCallback cb) { + if (*taskStatsCallbacks_->inCallback) { + throw std::runtime_error("cannot subscribe in task stats callback"); + } + taskStatsCallbacks_->callbackList.wlock()->push_back(std::move(cb)); +} + +BlockingQueueAddResult ThreadPoolExecutor::StoppedThreadQueue::add( + ThreadPoolExecutor::ThreadPtr item) { + std::lock_guard<std::mutex> guard(mutex_); + queue_.push(std::move(item)); + return sem_.post(); +} + +ThreadPoolExecutor::ThreadPtr ThreadPoolExecutor::StoppedThreadQueue::take() { + while (true) { + { + std::lock_guard<std::mutex> guard(mutex_); + if (!queue_.empty()) { + auto item = std::move(queue_.front()); + queue_.pop(); + return item; + } + } + sem_.wait(); + } +} + +folly::Optional<ThreadPoolExecutor::ThreadPtr> +ThreadPoolExecutor::StoppedThreadQueue::try_take_for( + std::chrono::milliseconds time) { + while (true) { + { + std::lock_guard<std::mutex> guard(mutex_); + if (!queue_.empty()) { + auto item = std::move(queue_.front()); + queue_.pop(); + return item; + } + } + if (!sem_.try_wait_for(time)) { + return folly::none; + } + } +} + +size_t ThreadPoolExecutor::StoppedThreadQueue::size() { + std::lock_guard<std::mutex> guard(mutex_); + return queue_.size(); +} + +void ThreadPoolExecutor::addObserver(std::shared_ptr<Observer> o) { + { + SharedMutex::WriteHolder r{&threadListLock_}; + observers_.push_back(o); + for (auto& thread : threadList_.get()) { + o->threadPreviouslyStarted(thread.get()); + } + } + while (activeThreads_.load(std::memory_order_relaxed) < + maxThreads_.load(std::memory_order_relaxed)) { + ensureActiveThreads(); + } +} + +void ThreadPoolExecutor::removeObserver(std::shared_ptr<Observer> o) { + SharedMutex::WriteHolder r{&threadListLock_}; + for (auto& thread : threadList_.get()) { + o->threadNotYetStopped(thread.get()); + } + + for (auto it = observers_.begin(); it != observers_.end(); it++) { + if (*it == o) { + observers_.erase(it); + return; + } + } + DCHECK(false); +} + +// Idle threads may have destroyed themselves, attempt to join +// them here +void ThreadPoolExecutor::ensureJoined() { + auto tojoin = threadsToJoin_.load(std::memory_order_relaxed); + if (tojoin) { + { + SharedMutex::WriteHolder w{&threadListLock_}; + tojoin = threadsToJoin_.load(std::memory_order_relaxed); + threadsToJoin_.store(0, std::memory_order_relaxed); + } + joinStoppedThreads(tojoin); + } +} + +// threadListLock_ must be write locked. +bool ThreadPoolExecutor::tryTimeoutThread() { + // Try to stop based on idle thread timeout (try_take_for), + // if there are at least minThreads running. + if (!minActive()) { + return false; + } + + // Remove thread from active count + activeThreads_.store( + activeThreads_.load(std::memory_order_relaxed) - 1, + std::memory_order_relaxed); + + // There is a memory ordering constraint w.r.t the queue + // implementation's add() and getPendingTaskCountImpl() - while many + // queues have seq_cst ordering, some do not, so add an explicit + // barrier. tryTimeoutThread is the slow path and only happens once + // every thread timeout; use asymmetric barrier to keep add() fast. + asymmetricHeavyBarrier(); + + // If this is based on idle thread timeout, then + // adjust vars appropriately (otherwise stop() or join() + // does this). + if (getPendingTaskCountImpl() > 0) { + // There are still pending tasks, we can't stop yet. + // re-up active threads and return. + activeThreads_.store( + activeThreads_.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + return false; + } + + threadsToJoin_.store( + threadsToJoin_.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + + return true; +} + +// If we can't ensure that we were able to hand off a task to a thread, +// attempt to start a thread that handled the task, if we aren't already +// running the maximum number of threads. +void ThreadPoolExecutor::ensureActiveThreads() { + ensureJoined(); + + // Matches barrier in tryTimeoutThread(). Ensure task added + // is seen before loading activeThreads_ below. + asymmetricLightBarrier(); + + // Fast path assuming we are already at max threads. + auto active = activeThreads_.load(std::memory_order_relaxed); + auto total = maxThreads_.load(std::memory_order_relaxed); + + if (active >= total) { + return; + } + + SharedMutex::WriteHolder w{&threadListLock_}; + // Double check behind lock. + active = activeThreads_.load(std::memory_order_relaxed); + total = maxThreads_.load(std::memory_order_relaxed); + if (active >= total) { + return; + } + ThreadPoolExecutor::addThreads(1); + activeThreads_.store(active + 1, std::memory_order_relaxed); +} + +// If an idle thread times out, only join it if there are at least +// minThreads threads. +bool ThreadPoolExecutor::minActive() { + return activeThreads_.load(std::memory_order_relaxed) > + minThreads_.load(std::memory_order_relaxed); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.h new file mode 100644 index 000000000..4382fe15c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ThreadPoolExecutor.h @@ -0,0 +1,340 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <mutex> +#include <queue> + +#include <folly/DefaultKeepAliveExecutor.h> +#include <folly/Memory.h> +#include <folly/SharedMutex.h> +#include <folly/executors/GlobalThreadPoolList.h> +#include <folly/executors/task_queue/LifoSemMPMCQueue.h> +#include <folly/executors/thread_factory/NamedThreadFactory.h> +#include <folly/io/async/Request.h> +#include <folly/portability/GFlags.h> +#include <folly/synchronization/Baton.h> + +#include <glog/logging.h> + +namespace folly { + +/* Base class for implementing threadpool based executors. + * + * Dynamic thread behavior: + * + * ThreadPoolExecutors may vary their actual running number of threads + * between minThreads_ and maxThreads_, tracked by activeThreads_. + * The actual implementation of joining an idle thread is left to the + * ThreadPoolExecutors' subclass (typically by LifoSem try_take_for + * timing out). Idle threads should be removed from threadList_, and + * threadsToJoin incremented, and activeThreads_ decremented. + * + * On task add(), if an executor can garantee there is an active + * thread that will handle the task, then nothing needs to be done. + * If not, then ensureActiveThreads() should be called to possibly + * start another pool thread, up to maxThreads_. + * + * ensureJoined() is called on add(), such that we can join idle + * threads that were destroyed (which can't be joined from + * themselves). + * + * Thread pool stats accounting: + * + * Derived classes must register instances to keep stats on all thread + * pools by calling registerThreadPoolExecutor(this) on constructions + * and deregisterThreadPoolExecutor(this) on destruction. + * + * Registration must be done wherever getPendingTaskCountImpl is implemented + * and getPendingTaskCountImpl should be marked 'final' to avoid data races. + */ +class ThreadPoolExecutor : public DefaultKeepAliveExecutor { + public: + explicit ThreadPoolExecutor( + size_t maxThreads, + size_t minThreads, + std::shared_ptr<ThreadFactory> threadFactory, + bool isWaitForAll = false); + + ~ThreadPoolExecutor() override; + + void add(Func func) override = 0; + virtual void + add(Func func, std::chrono::milliseconds expiration, Func expireCallback); + + void setThreadFactory(std::shared_ptr<ThreadFactory> threadFactory) { + CHECK(numThreads() == 0); + threadFactory_ = std::move(threadFactory); + } + + std::shared_ptr<ThreadFactory> getThreadFactory() const { + return threadFactory_; + } + + size_t numThreads() const; + void setNumThreads(size_t numThreads); + + // Return actual number of active threads -- this could be different from + // numThreads() due to ThreadPoolExecutor's dynamic behavior. + size_t numActiveThreads() const; + + /* + * stop() is best effort - there is no guarantee that unexecuted tasks won't + * be executed before it returns. Specifically, IOThreadPoolExecutor's stop() + * behaves like join(). + */ + void stop(); + void join(); + + /** + * Execute f against all ThreadPoolExecutors, primarily for retrieving and + * exporting stats. + */ + static void withAll(FunctionRef<void(ThreadPoolExecutor&)> f); + + struct PoolStats { + PoolStats() + : threadCount(0), + idleThreadCount(0), + activeThreadCount(0), + pendingTaskCount(0), + totalTaskCount(0), + maxIdleTime(0) {} + size_t threadCount, idleThreadCount, activeThreadCount; + uint64_t pendingTaskCount, totalTaskCount; + std::chrono::nanoseconds maxIdleTime; + }; + + PoolStats getPoolStats() const; + size_t getPendingTaskCount() const; + std::string getName() const; + + struct TaskStats { + TaskStats() : expired(false), waitTime(0), runTime(0) {} + bool expired; + std::chrono::nanoseconds waitTime; + std::chrono::nanoseconds runTime; + }; + + using TaskStatsCallback = std::function<void(TaskStats)>; + void subscribeToTaskStats(TaskStatsCallback cb); + + /** + * Base class for threads created with ThreadPoolExecutor. + * Some subclasses have methods that operate on these + * handles. + */ + class ThreadHandle { + public: + virtual ~ThreadHandle() = default; + }; + + /** + * Observer interface for thread start/stop. + * Provides hooks so actions can be taken when + * threads are created + */ + class Observer { + public: + virtual void threadStarted(ThreadHandle*) = 0; + virtual void threadStopped(ThreadHandle*) = 0; + virtual void threadPreviouslyStarted(ThreadHandle* h) { + threadStarted(h); + } + virtual void threadNotYetStopped(ThreadHandle* h) { + threadStopped(h); + } + virtual ~Observer() = default; + }; + + void addObserver(std::shared_ptr<Observer>); + void removeObserver(std::shared_ptr<Observer>); + + void setThreadDeathTimeout(std::chrono::milliseconds timeout) { + threadTimeout_ = timeout; + } + + protected: + // Prerequisite: threadListLock_ writelocked + void addThreads(size_t n); + // Prerequisite: threadListLock_ writelocked + void removeThreads(size_t n, bool isJoin); + + struct TaskStatsCallbackRegistry; + + struct alignas(folly::cacheline_align_v) Thread : public ThreadHandle { + explicit Thread(ThreadPoolExecutor* pool) + : id(nextId++), + handle(), + idle(true), + lastActiveTime(std::chrono::steady_clock::now()), + taskStatsCallbacks(pool->taskStatsCallbacks_) {} + + ~Thread() override = default; + + static std::atomic<uint64_t> nextId; + uint64_t id; + std::thread handle; + bool idle; + std::chrono::steady_clock::time_point lastActiveTime; + folly::Baton<> startupBaton; + std::shared_ptr<TaskStatsCallbackRegistry> taskStatsCallbacks; + }; + + typedef std::shared_ptr<Thread> ThreadPtr; + + struct Task { + explicit Task( + Func&& func, + std::chrono::milliseconds expiration, + Func&& expireCallback); + Func func_; + TaskStats stats_; + std::chrono::steady_clock::time_point enqueueTime_; + std::chrono::milliseconds expiration_; + Func expireCallback_; + std::shared_ptr<folly::RequestContext> context_; + }; + + static void runTask(const ThreadPtr& thread, Task&& task); + + // The function that will be bound to pool threads. It must call + // thread->startupBaton.post() when it's ready to consume work. + virtual void threadRun(ThreadPtr thread) = 0; + + // Stop n threads and put their ThreadPtrs in the stoppedThreads_ queue + // and remove them from threadList_, either synchronize or asynchronize + // Prerequisite: threadListLock_ writelocked + virtual void stopThreads(size_t n) = 0; + + // Join n stopped threads and remove them from waitingForJoinThreads_ queue. + // Should not hold a lock because joining thread operation may invoke some + // cleanup operations on the thread, and those cleanup operations may + // require a lock on ThreadPoolExecutor. + void joinStoppedThreads(size_t n); + + // Create a suitable Thread struct + virtual ThreadPtr makeThread() { + return std::make_shared<Thread>(this); + } + + static void registerThreadPoolExecutor(ThreadPoolExecutor* tpe); + static void deregisterThreadPoolExecutor(ThreadPoolExecutor* tpe); + + // Prerequisite: threadListLock_ readlocked or writelocked + virtual size_t getPendingTaskCountImpl() const = 0; + + class ThreadList { + public: + void add(const ThreadPtr& state) { + auto it = std::lower_bound( + vec_.begin(), + vec_.end(), + state, + // compare method is a static method of class + // and therefore cannot be inlined by compiler + // as a template predicate of the STL algorithm + // but wrapped up with the lambda function (lambda will be inlined) + // compiler can inline compare method as well + [&](const ThreadPtr& ts1, const ThreadPtr& ts2) -> bool { // inline + return compare(ts1, ts2); + }); + vec_.insert(it, state); + } + + void remove(const ThreadPtr& state) { + auto itPair = std::equal_range( + vec_.begin(), + vec_.end(), + state, + // the same as above + [&](const ThreadPtr& ts1, const ThreadPtr& ts2) -> bool { // inline + return compare(ts1, ts2); + }); + CHECK(itPair.first != vec_.end()); + CHECK(std::next(itPair.first) == itPair.second); + vec_.erase(itPair.first); + } + + const std::vector<ThreadPtr>& get() const { + return vec_; + } + + private: + static bool compare(const ThreadPtr& ts1, const ThreadPtr& ts2) { + return ts1->id < ts2->id; + } + + std::vector<ThreadPtr> vec_; + }; + + class StoppedThreadQueue : public BlockingQueue<ThreadPtr> { + public: + BlockingQueueAddResult add(ThreadPtr item) override; + ThreadPtr take() override; + size_t size() override; + folly::Optional<ThreadPtr> try_take_for( + std::chrono::milliseconds /*timeout */) override; + + private: + folly::LifoSem sem_; + std::mutex mutex_; + std::queue<ThreadPtr> queue_; + }; + + std::shared_ptr<ThreadFactory> threadFactory_; + const bool isWaitForAll_; // whether to wait till event base loop exits + + ThreadList threadList_; + SharedMutex threadListLock_; + StoppedThreadQueue stoppedThreads_; + std::atomic<bool> isJoin_{false}; // whether the current downsizing is a join + + struct TaskStatsCallbackRegistry { + folly::ThreadLocal<bool> inCallback; + folly::Synchronized<std::vector<TaskStatsCallback>> callbackList; + }; + std::shared_ptr<TaskStatsCallbackRegistry> taskStatsCallbacks_; + std::vector<std::shared_ptr<Observer>> observers_; + folly::ThreadPoolListHook threadPoolHook_; + + // Dynamic thread sizing functions and variables + void ensureActiveThreads(); + void ensureJoined(); + bool minActive(); + bool tryTimeoutThread(); + + // These are only modified while holding threadListLock_, but + // are read without holding the lock. + std::atomic<size_t> maxThreads_{0}; + std::atomic<size_t> minThreads_{0}; + std::atomic<size_t> activeThreads_{0}; + + std::atomic<size_t> threadsToJoin_{0}; + std::chrono::milliseconds threadTimeout_{0}; + + void joinKeepAliveOnce() { + if (!std::exchange(keepAliveJoined_, true)) { + joinKeepAlive(); + } + } + + bool keepAliveJoined_{false}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.cpp new file mode 100644 index 000000000..7bf6ed715 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/ThreadedExecutor.h> + +#include <chrono> + +#include <glog/logging.h> + +#include <folly/executors/thread_factory/NamedThreadFactory.h> +#include <folly/system/ThreadName.h> + +namespace folly { + +template <typename F> +static auto with_unique_lock(std::mutex& m, F&& f) -> decltype(f()) { + std::unique_lock<std::mutex> lock(m); + return f(); +} + +ThreadedExecutor::ThreadedExecutor(std::shared_ptr<ThreadFactory> threadFactory) + : threadFactory_(std::move(threadFactory)) { + controlt_ = std::thread([this] { control(); }); +} + +ThreadedExecutor::~ThreadedExecutor() { + stopping_.store(true, std::memory_order_release); + notify(); + controlt_.join(); + CHECK(running_.empty()); + CHECK(finished_.empty()); +} + +void ThreadedExecutor::add(Func func) { + CHECK(!stopping_.load(std::memory_order_acquire)); + with_unique_lock(enqueuedm_, [&] { enqueued_.push_back(std::move(func)); }); + notify(); +} + +std::shared_ptr<ThreadFactory> ThreadedExecutor::newDefaultThreadFactory() { + return std::make_shared<NamedThreadFactory>("Threaded"); +} + +void ThreadedExecutor::notify() { + with_unique_lock(controlm_, [&] { controls_ = true; }); + controlc_.notify_one(); +} + +void ThreadedExecutor::control() { + folly::setThreadName("ThreadedCtrl"); + auto looping = true; + while (looping) { + controlWait(); + looping = controlPerformAll(); + } +} + +void ThreadedExecutor::controlWait() { + constexpr auto kMaxWait = std::chrono::seconds(10); + std::unique_lock<std::mutex> lock(controlm_); + controlc_.wait_for(lock, kMaxWait, [&] { return controls_; }); + controls_ = false; +} + +void ThreadedExecutor::work(Func& func) { + func(); + auto id = std::this_thread::get_id(); + with_unique_lock(finishedm_, [&] { finished_.push_back(id); }); + notify(); +} + +void ThreadedExecutor::controlJoinFinishedThreads() { + std::deque<std::thread::id> finishedt; + with_unique_lock(finishedm_, [&] { std::swap(finishedt, finished_); }); + for (auto id : finishedt) { + running_[id].join(); + running_.erase(id); + } +} + +void ThreadedExecutor::controlLaunchEnqueuedTasks() { + std::deque<Func> enqueuedt; + with_unique_lock(enqueuedm_, [&] { std::swap(enqueuedt, enqueued_); }); + for (auto& f : enqueuedt) { + auto th = threadFactory_->newThread( + [this, f = std::move(f)]() mutable { work(f); }); + auto id = th.get_id(); + running_[id] = std::move(th); + } +} + +bool ThreadedExecutor::controlPerformAll() { + auto stopping = stopping_.load(std::memory_order_acquire); + controlJoinFinishedThreads(); + controlLaunchEnqueuedTasks(); + return !stopping || !running_.empty(); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.h new file mode 100644 index 000000000..1fba0e513 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/ThreadedExecutor.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <condition_variable> +#include <deque> +#include <map> +#include <memory> +#include <mutex> +#include <thread> + +#include <folly/Executor.h> +#include <folly/executors/thread_factory/ThreadFactory.h> + +namespace folly { + +/*** + * ThreadedExecutor + * + * An executor for blocking tasks. + * + * This executor runs each task in its own thread. It works well for tasks + * which mostly sleep, but works poorly for tasks which mostly compute. + * + * For each task given to the executor with `add`, the executor spawns a new + * thread for that task, runs the task in that thread, and joins the thread + * after the task has completed. + * + * Spawning and joining task threads are done in the executor's internal + * control thread. Calls to `add` put the tasks to be run into a queue, where + * the control thread will find them. + * + * There is currently no limitation on, or throttling of, concurrency. + * + * This executor is not currently optimized for performance. For example, it + * makes no attempt to re-use task threads. Rather, it exists primarily to + * offload sleep-heavy tasks from the CPU executor, where they might otherwise + * be run. + */ +class ThreadedExecutor : public virtual folly::Executor { + public: + explicit ThreadedExecutor( + std::shared_ptr<ThreadFactory> threadFactory = newDefaultThreadFactory()); + ~ThreadedExecutor() override; + + ThreadedExecutor(ThreadedExecutor const&) = delete; + ThreadedExecutor(ThreadedExecutor&&) = delete; + + ThreadedExecutor& operator=(ThreadedExecutor const&) = delete; + ThreadedExecutor& operator=(ThreadedExecutor&&) = delete; + + void add(Func func) override; + + private: + static std::shared_ptr<ThreadFactory> newDefaultThreadFactory(); + + void notify(); + void control(); + void controlWait(); + bool controlPerformAll(); + void controlJoinFinishedThreads(); + void controlLaunchEnqueuedTasks(); + + void work(Func& func); + + std::shared_ptr<ThreadFactory> threadFactory_; + + std::atomic<bool> stopping_{false}; + + std::mutex controlm_; + std::condition_variable controlc_; + bool controls_ = false; + std::thread controlt_; + + std::mutex enqueuedm_; + std::deque<Func> enqueued_; + + // Accessed only by the control thread, so no synchronization. + std::map<std::thread::id, std::thread> running_; + + std::mutex finishedm_; + std::deque<std::thread::id> finished_; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.cpp new file mode 100644 index 000000000..e356902dd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/TimedDrivableExecutor.h> + +#include <cstring> +#include <ctime> +#include <string> +#include <tuple> + +namespace folly { + +void TimedDrivableExecutor::add(Func callback) { + queue_.enqueue(std::move(callback)); +} + +void TimedDrivableExecutor::drive() noexcept { + wait(); + run(); +} + +size_t TimedDrivableExecutor::run() noexcept { + size_t count = 0; + size_t n = queue_.size(); + + // If we have waited already, then func_ may have a value + if (func_) { + auto f = std::move(func_); + f(); + count = 1; + } + + while (count < n && queue_.try_dequeue(func_)) { + auto f = std::move(func_); + f(); + ++count; + } + + return count; +} + +size_t TimedDrivableExecutor::drain() noexcept { + size_t tasksRun = 0; + size_t tasksForSingleRun = 0; + while ((tasksForSingleRun = run()) != 0) { + tasksRun += tasksForSingleRun; + } + return tasksRun; +} + +void TimedDrivableExecutor::wait() noexcept { + if (!func_) { + queue_.dequeue(func_); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.h new file mode 100644 index 000000000..4f8c7162f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/TimedDrivableExecutor.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> + +#include <folly/concurrency/UnboundedQueue.h> +#include <folly/executors/DrivableExecutor.h> + +namespace folly { + +/* + * A DrivableExecutor can be driven via its drive() method or its driveUntil() + * that drives until some time point. + */ +class TimedDrivableExecutor : public DrivableExecutor { + public: + ~TimedDrivableExecutor() noexcept { + // Drain on destruction so that if work is added here during the collapse + // of a future train, it will propagate. + drain(); + } + + /// Implements DrivableExecutor + void drive() noexcept override; + + // Make progress if there is work to do and return true. Otherwise return + // false. + bool try_drive() noexcept { + return try_wait() && run() > 0; + } + + // Make progress on this Executor's work. Acts as drive, except it will only + // wait for a period of timeout for work to be enqueued. If no work is + // enqueued by that point, it will return. + template <typename Rep, typename Period> + bool try_drive_for( + const std::chrono::duration<Rep, Period>& timeout) noexcept { + return try_wait_for(timeout) && run() > 0; + } + + // Make progress on this Executor's work. Acts as drive, except it will only + // wait until deadline for work to be enqueued. If no work is enqueued by + // that point, it will return. + template <typename Clock, typename Duration> + bool try_drive_until( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + return try_wait_until(deadline) && run() > 0; + } + + void add(Func) override; + + /// Do work. Returns the number of functions that were executed (maybe 0). + /// Non-blocking, in the sense that we don't wait for work (we can't + /// control whether one of the functions blocks). + /// This is stable, it will not chase an ever-increasing tail of work. + /// This also means, there may be more work available to perform at the + /// moment that this returns. + size_t run() noexcept; + + // Do work until there is no more work to do. + // Returns the number of functions that were executed (maybe 0). + // Unlike run, this method is not stable. It will chase an infinite tail of + // work so should be used with care. + // There will be no work available to perform at the moment that this + // returns. + size_t drain() noexcept; + + /// Wait for work to do. + void wait() noexcept; + + // Return true if there is work to do, false otherwise + bool try_wait() noexcept { + return func_ || queue_.try_dequeue(func_); + } + + /// Wait for work to do or for a period of timeout, whichever is sooner. + template <typename Rep, typename Period> + bool try_wait_for( + const std::chrono::duration<Rep, Period>& timeout) noexcept { + return func_ || queue_.try_dequeue_for(func_, timeout); + } + + /// Wait for work to do or until deadline passes, whichever is sooner. + template <typename Clock, typename Duration> + bool try_wait_until( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + return func_ || queue_.try_dequeue_until(func_, deadline); + } + + private: + UMPSCQueue<Func, true> queue_; + Func func_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.cpp b/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.cpp new file mode 100644 index 000000000..e8417f7ef --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/executors/TimekeeperScheduledExecutor.h> +#include <folly/futures/Future.h> + +namespace folly { + +/* static */ Executor::KeepAlive<TimekeeperScheduledExecutor> +TimekeeperScheduledExecutor::create( + Executor::KeepAlive<> parent, + Function<std::shared_ptr<Timekeeper>()> getTimekeeper) { + return makeKeepAlive<TimekeeperScheduledExecutor>( + new TimekeeperScheduledExecutor( + std::move(parent), std::move(getTimekeeper))); +} + +void TimekeeperScheduledExecutor::run(Func func) { + try { + func(); + } catch (std::exception const& ex) { + LOG(ERROR) << "func threw unhandled exception " << folly::exceptionStr(ex); + } catch (...) { + LOG(ERROR) << "func threw unhandled non-exception object"; + } +} + +void TimekeeperScheduledExecutor::add(Func func) { + parent_->add( + [keepAlive = getKeepAliveToken(this), f = std::move(func)]() mutable { + keepAlive->run(std::move(f)); + }); +} + +void TimekeeperScheduledExecutor::scheduleAt( + Func&& func, + ScheduledExecutor::TimePoint const& t) { + auto delay = std::chrono::duration_cast<folly::Duration>( + t - std::chrono::steady_clock::now()); + if (delay.count() > 0) { + auto tk = getTimekeeper_(); + if (UNLIKELY(!tk)) { + throw TimekeeperScheduledExecutorNoTimekeeper(); + } + tk->after(delay) + .via(parent_.copy()) + .thenValue([keepAlive = getKeepAliveToken(this), f = std::move(func)]( + auto&&) mutable { keepAlive->run(std::move(f)); }); + } else { + add(std::move(func)); + } +} + +bool TimekeeperScheduledExecutor::keepAliveAcquire() { + auto keepAliveCounter = + keepAliveCounter_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCounter > 0); + return true; +} + +void TimekeeperScheduledExecutor::keepAliveRelease() { + auto keepAliveCounter = + keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCounter > 0); + if (keepAliveCounter == 1) { + delete this; + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h b/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h new file mode 100644 index 000000000..10b2ab05f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <glog/logging.h> +#include <atomic> + +#include <folly/executors/ScheduledExecutor.h> +#include <folly/futures/Future.h> + +namespace folly { + +struct FOLLY_EXPORT TimekeeperScheduledExecutorNoTimekeeper + : public std::logic_error { + TimekeeperScheduledExecutorNoTimekeeper() + : std::logic_error("No Timekeeper available") {} +}; + +// This class turns a Executor into a ScheduledExecutor. +class TimekeeperScheduledExecutor : public ScheduledExecutor { + public: + TimekeeperScheduledExecutor(TimekeeperScheduledExecutor const&) = delete; + TimekeeperScheduledExecutor& operator=(TimekeeperScheduledExecutor const&) = + delete; + TimekeeperScheduledExecutor(TimekeeperScheduledExecutor&&) = delete; + TimekeeperScheduledExecutor& operator=(TimekeeperScheduledExecutor&&) = + delete; + + static Executor::KeepAlive<TimekeeperScheduledExecutor> create( + Executor::KeepAlive<> parent, + Function<std::shared_ptr<Timekeeper>()> getTimekeeper = + detail::getTimekeeperSingleton); + + virtual void add(Func func) override; + + virtual void scheduleAt(Func&& func, ScheduledExecutor::TimePoint const& t) + override; + + protected: + bool keepAliveAcquire() override; + void keepAliveRelease() override; + + private: + TimekeeperScheduledExecutor( + KeepAlive<Executor>&& parent, + Function<std::shared_ptr<Timekeeper>()> getTimekeeper) + : parent_(std::move(parent)), getTimekeeper_(std::move(getTimekeeper)) {} + + ~TimekeeperScheduledExecutor() { + DCHECK(!keepAliveCounter_); + } + + void run(Func); + + KeepAlive<Executor> parent_; + Function<std::shared_ptr<Timekeeper>()> getTimekeeper_; + std::atomic<ssize_t> keepAliveCounter_{1}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h b/ios/Pods/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h new file mode 100644 index 000000000..2037384cf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <exception> +#include <stdexcept> + +#include <glog/logging.h> + +#include <folly/CPortability.h> +#include <folly/Optional.h> + +namespace folly { + +// Some queue implementations (for example, LifoSemMPMCQueue or +// PriorityLifoSemMPMCQueue) support both blocking (BLOCK) and +// non-blocking (THROW) behaviors. +enum class QueueBehaviorIfFull { THROW, BLOCK }; + +class FOLLY_EXPORT QueueFullException : public std::runtime_error { + using std::runtime_error::runtime_error; // Inherit constructors. +}; + +struct BlockingQueueAddResult { + BlockingQueueAddResult(bool reused = false) : reusedThread(reused) {} + bool reusedThread; +}; + +template <class T> +class BlockingQueue { + public: + virtual ~BlockingQueue() = default; + // Adds item to the queue (with priority). + // + // Returns true if an existing thread was able to work on it (used + // for dynamically sizing thread pools), false otherwise. Return false + // if this feature is not supported. + virtual BlockingQueueAddResult add(T item) = 0; + virtual BlockingQueueAddResult addWithPriority( + T item, + int8_t /* priority */) { + return add(std::move(item)); + } + virtual uint8_t getNumPriorities() { + return 1; + } + virtual T take() = 0; + virtual folly::Optional<T> try_take_for(std::chrono::milliseconds time) = 0; + virtual size_t size() = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h b/ios/Pods/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h new file mode 100644 index 000000000..0aafeeaca --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/MPMCQueue.h> +#include <folly/executors/task_queue/BlockingQueue.h> +#include <folly/synchronization/LifoSem.h> + +namespace folly { + +template <class T, QueueBehaviorIfFull kBehavior = QueueBehaviorIfFull::THROW> +class LifoSemMPMCQueue : public BlockingQueue<T> { + public: + // Note: The queue pre-allocates all memory for max_capacity + explicit LifoSemMPMCQueue(size_t max_capacity) : queue_(max_capacity) {} + + BlockingQueueAddResult add(T item) override { + switch (kBehavior) { // static + case QueueBehaviorIfFull::THROW: + if (!queue_.writeIfNotFull(std::move(item))) { + throw QueueFullException("LifoSemMPMCQueue full, can't add item"); + } + break; + case QueueBehaviorIfFull::BLOCK: + queue_.blockingWrite(std::move(item)); + break; + } + return sem_.post(); + } + + T take() override { + T item; + while (!queue_.readIfNotEmpty(item)) { + sem_.wait(); + } + return item; + } + + folly::Optional<T> try_take_for(std::chrono::milliseconds time) override { + T item; + while (!queue_.readIfNotEmpty(item)) { + if (!sem_.try_wait_for(time)) { + return folly::none; + } + } + return item; + } + + size_t capacity() { + return queue_.capacity(); + } + + size_t size() override { + return queue_.size(); + } + + private: + folly::LifoSem sem_; + folly::MPMCQueue<T> queue_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h b/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h new file mode 100644 index 000000000..a01efb253 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> +#include <folly/MPMCQueue.h> +#include <folly/Range.h> +#include <folly/executors/task_queue/BlockingQueue.h> +#include <folly/synchronization/LifoSem.h> +#include <glog/logging.h> + +namespace folly { + +template <class T, QueueBehaviorIfFull kBehavior = QueueBehaviorIfFull::THROW> +class PriorityLifoSemMPMCQueue : public BlockingQueue<T> { + public: + // Note A: The queue pre-allocates all memory for max_capacity + // Note B: To use folly::Executor::*_PRI, for numPriorities == 2 + // MID_PRI and HI_PRI are treated at the same priority level. + PriorityLifoSemMPMCQueue(uint8_t numPriorities, size_t max_capacity) { + queues_.reserve(numPriorities); + for (int8_t i = 0; i < numPriorities; i++) { + queues_.emplace_back(max_capacity); + } + } + + PriorityLifoSemMPMCQueue(folly::Range<const size_t*> capacities) { + CHECK_LT(capacities.size(), 256) << "At most 255 priorities supported"; + + queues_.reserve(capacities.size()); + for (auto capacity : capacities) { + queues_.emplace_back(capacity); + } + } + + uint8_t getNumPriorities() override { + return queues_.size(); + } + + // Add at medium priority by default + BlockingQueueAddResult add(T item) override { + return addWithPriority(std::move(item), folly::Executor::MID_PRI); + } + + BlockingQueueAddResult addWithPriority(T item, int8_t priority) override { + int mid = getNumPriorities() / 2; + size_t queue = priority < 0 + ? std::max(0, mid + priority) + : std::min(getNumPriorities() - 1, mid + priority); + CHECK_LT(queue, queues_.size()); + switch (kBehavior) { // static + case QueueBehaviorIfFull::THROW: + if (!queues_[queue].writeIfNotFull(std::move(item))) { + throw QueueFullException("LifoSemMPMCQueue full, can't add item"); + } + break; + case QueueBehaviorIfFull::BLOCK: + queues_[queue].blockingWrite(std::move(item)); + break; + } + return sem_.post(); + } + + T take() override { + T item; + while (true) { + if (nonBlockingTake(item)) { + return item; + } + sem_.wait(); + } + } + + folly::Optional<T> try_take_for(std::chrono::milliseconds time) override { + T item; + while (true) { + if (nonBlockingTake(item)) { + return item; + } + if (!sem_.try_wait_for(time)) { + return folly::none; + } + } + } + + bool nonBlockingTake(T& item) { + for (auto it = queues_.rbegin(); it != queues_.rend(); it++) { + if (it->readIfNotEmpty(item)) { + return true; + } + } + return false; + } + + size_t size() override { + size_t size = 0; + for (auto& q : queues_) { + size += q.size(); + } + return size; + } + + size_t sizeGuess() const { + size_t size = 0; + for (auto& q : queues_) { + size += q.sizeGuess(); + } + return size; + } + + private: + folly::LifoSem sem_; + std::vector<folly::MPMCQueue<T>> queues_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h b/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h new file mode 100644 index 000000000..bae3c52c9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/ConstexprMath.h> +#include <folly/Executor.h> +#include <folly/concurrency/PriorityUnboundedQueueSet.h> +#include <folly/executors/task_queue/BlockingQueue.h> +#include <folly/lang/Exception.h> +#include <folly/synchronization/LifoSem.h> + +namespace folly { + +template <class T> +class PriorityUnboundedBlockingQueue : public BlockingQueue<T> { + public: + // Note: To use folly::Executor::*_PRI, for numPriorities == 2 + // MID_PRI and HI_PRI are treated at the same priority level. + explicit PriorityUnboundedBlockingQueue(uint8_t numPriorities) + : queue_(numPriorities) {} + + uint8_t getNumPriorities() override { + return queue_.priorities(); + } + + // Add at medium priority by default + BlockingQueueAddResult add(T item) override { + return addWithPriority(std::move(item), folly::Executor::MID_PRI); + } + + BlockingQueueAddResult addWithPriority(T item, int8_t priority) override { + queue_.at_priority(translatePriority(priority)).enqueue(std::move(item)); + return sem_.post(); + } + + T take() override { + sem_.wait(); + return dequeue(); + } + + folly::Optional<T> try_take() { + if (!sem_.try_wait()) { + return none; + } + return dequeue(); + } + + folly::Optional<T> try_take_for(std::chrono::milliseconds time) override { + if (!sem_.try_wait_for(time)) { + return none; + } + return dequeue(); + } + + size_t size() override { + return queue_.size(); + } + + size_t sizeGuess() const { + return queue_.size(); + } + + private: + size_t translatePriority(int8_t const priority) { + size_t const priorities = queue_.priorities(); + assert(priorities <= 255); + int8_t const hi = (priorities + 1) / 2 - 1; + int8_t const lo = hi - (priorities - 1); + return hi - constexpr_clamp(priority, lo, hi); + } + + T dequeue() { + // must follow a successful sem wait + if (auto obj = queue_.try_dequeue()) { + return std::move(*obj); + } + terminate_with<std::logic_error>("bug in task queue"); + } + + LifoSem sem_; + PriorityUMPMCQueueSet<T, /* MayBlock = */ true> queue_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h b/ios/Pods/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h new file mode 100644 index 000000000..3f52856d6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/concurrency/UnboundedQueue.h> +#include <folly/executors/task_queue/BlockingQueue.h> +#include <folly/synchronization/LifoSem.h> + +namespace folly { + +template <class T> +class UnboundedBlockingQueue : public BlockingQueue<T> { + public: + virtual ~UnboundedBlockingQueue() {} + + BlockingQueueAddResult add(T item) override { + queue_.enqueue(std::move(item)); + return sem_.post(); + } + + T take() override { + sem_.wait(); + return queue_.dequeue(); + } + + folly::Optional<T> try_take_for(std::chrono::milliseconds time) override { + if (!sem_.try_wait_for(time)) { + return folly::none; + } + return queue_.dequeue(); + } + + size_t size() override { + return queue_.size(); + } + + private: + LifoSem sem_; + UMPMCQueue<T, false, 6> queue_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h new file mode 100644 index 000000000..d1db3615d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <thread> + +#include <folly/ScopeGuard.h> +#include <folly/executors/thread_factory/ThreadFactory.h> + +namespace folly { + +class InitThreadFactory : public ThreadFactory { + public: + explicit InitThreadFactory( + std::shared_ptr<ThreadFactory> threadFactory, + Func&& threadInitializer, + Func&& threadFinializer = [] {}) + : threadFactory_(std::move(threadFactory)), + threadInitFini_(std::make_shared<ThreadInitFini>( + std::move(threadInitializer), + std::move(threadFinializer))) {} + + std::thread newThread(Func&& func) override { + return threadFactory_->newThread( + [func = std::move(func), threadInitFini = threadInitFini_]() mutable { + threadInitFini->initializer(); + SCOPE_EXIT { + threadInitFini->finalizer(); + }; + func(); + }); + } + + private: + std::shared_ptr<ThreadFactory> threadFactory_; + struct ThreadInitFini { + ThreadInitFini(Func&& init, Func&& fini) + : initializer(std::move(init)), finalizer(std::move(fini)) {} + + Func initializer; + Func finalizer; + }; + std::shared_ptr<ThreadInitFini> threadInitFini_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h new file mode 100644 index 000000000..89c2bd57a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <string> +#include <thread> + +#include <folly/Conv.h> +#include <folly/Range.h> +#include <folly/executors/thread_factory/ThreadFactory.h> +#include <folly/system/ThreadName.h> + +namespace folly { + +class NamedThreadFactory : public ThreadFactory { + public: + explicit NamedThreadFactory(folly::StringPiece prefix) + : prefix_(prefix.str()), suffix_(0) {} + + std::thread newThread(Func&& func) override { + auto name = folly::to<std::string>(prefix_, suffix_++); + return std::thread( + [func = std::move(func), name = std::move(name)]() mutable { + folly::setThreadName(name); + func(); + }); + } + + void setNamePrefix(folly::StringPiece prefix) { + prefix_ = prefix.str(); + } + + std::string getNamePrefix() { + return prefix_; + } + + private: + std::string prefix_; + std::atomic<uint64_t> suffix_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h new file mode 100644 index 000000000..6787873c2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/executors/thread_factory/ThreadFactory.h> + +#include <glog/logging.h> + +#include <folly/String.h> +#include <folly/portability/SysResource.h> +#include <folly/portability/SysTime.h> + +namespace folly { + +/** + * A ThreadFactory that sets nice values for each thread. The main + * use case for this class is if there are multiple + * CPUThreadPoolExecutors in a single process, or between multiple + * processes, where some should have a higher priority than the others. + * + * Note that per-thread nice values are not POSIX standard, but both + * pthreads and linux support per-thread nice. The default linux + * scheduler uses these values to do smart thread prioritization. + * sched_priority function calls only affect real-time schedulers. + */ +class PriorityThreadFactory : public ThreadFactory { + public: + explicit PriorityThreadFactory( + std::shared_ptr<ThreadFactory> factory, + int priority) + : factory_(std::move(factory)), priority_(priority) {} + + std::thread newThread(Func&& func) override { + int priority = priority_; + return factory_->newThread([priority, func = std::move(func)]() mutable { + if (setpriority(PRIO_PROCESS, 0, priority) != 0) { + LOG(ERROR) << "setpriority failed (are you root?) with error " << errno, + errnoStr(errno); + } + func(); + }); + } + + private: + std::shared_ptr<ThreadFactory> factory_; + int priority_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h new file mode 100644 index 000000000..5e54fcd32 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <folly/Executor.h> + +#include <thread> + +namespace folly { + +class ThreadFactory { + public: + virtual ~ThreadFactory() = default; + virtual std::thread newThread(Func&& func) = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h b/ios/Pods/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h new file mode 100644 index 000000000..560f26a68 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstdint> +#include <memory> +#include <mutex> + +#include <folly/Indestructible.h> +#include <folly/experimental/ReadMostlySharedPtr.h> +#include <folly/synchronization/Rcu.h> + +namespace folly { + +namespace detail { +struct AtomicReadMostlyTag; +extern Indestructible<std::mutex> atomicReadMostlyMu; +extern Indestructible<rcu_domain<AtomicReadMostlyTag>> atomicReadMostlyDomain; +} // namespace detail + +/* + * What atomic_shared_ptr is to shared_ptr, AtomicReadMostlyMainPtr is to + * ReadMostlyMainPtr; it allows racy conflicting accesses to one. This gives + * true shared_ptr-like semantics, including reclamation at the point where the + * last pointer to an object goes away. + * + * It's about the same speed (slightly slower) as ReadMostlyMainPtr. The most + * significant feature they share is avoiding reader-reader contention and + * atomic RMWs in the absence of writes. + */ +template <typename T> +class AtomicReadMostlyMainPtr { + public: + AtomicReadMostlyMainPtr() : curMainPtrIndex_(0) {} + + explicit AtomicReadMostlyMainPtr(std::shared_ptr<T> ptr) + : curMainPtrIndex_(0) { + mainPtrs_[0] = ReadMostlyMainPtr<T>{std::move(ptr)}; + } + + void operator=(std::shared_ptr<T> desired) { + store(std::move(desired)); + } + + bool is_lock_free() const { + return false; + } + + ReadMostlySharedPtr<T> load( + std::memory_order order = std::memory_order_seq_cst) const { + auto token = detail::atomicReadMostlyDomain->lock_shared(); + // Synchronization point with the store in storeLocked(). + auto index = curMainPtrIndex_.load(order); + auto result = mainPtrs_[index].getShared(); + detail::atomicReadMostlyDomain->unlock_shared(std::move(token)); + return result; + } + + void store( + std::shared_ptr<T> ptr, + std::memory_order order = std::memory_order_seq_cst) { + std::shared_ptr<T> old; + { + std::lock_guard<std::mutex> lg(*detail::atomicReadMostlyMu); + old = exchangeLocked(std::move(ptr), order); + } + // If ~T() runs (triggered by the shared_ptr refcount decrement), it's here, + // after dropping the lock. This avoids a possible (albeit esoteric) + // deadlock if ~T() modifies the AtomicReadMostlyMainPtr that used to point + // to it. + } + + std::shared_ptr<T> exchange( + std::shared_ptr<T> ptr, + std::memory_order order = std::memory_order_seq_cst) { + std::lock_guard<std::mutex> lg(*detail::atomicReadMostlyMu); + return exchangeLocked(std::move(ptr), order); + } + + bool compare_exchange_weak( + std::shared_ptr<T>& expected, + const std::shared_ptr<T>& desired, + std::memory_order successOrder = std::memory_order_seq_cst, + std::memory_order failureOrder = std::memory_order_seq_cst) { + return compare_exchange_strong( + expected, desired, successOrder, failureOrder); + } + + bool compare_exchange_strong( + std::shared_ptr<T>& expected, + const std::shared_ptr<T>& desired, + std::memory_order successOrder = std::memory_order_seq_cst, + std::memory_order failureOrder = std::memory_order_seq_cst) { + // See the note at the end of store; we need to defer any destruction we + // might trigger until after the lock is released. + // This is not actually needed down the success path (the reference passed + // in as expected is another pointer to the same object, so we won't + // decrement the refcount to 0), but "never decrement a refcount while + // holding a lock" is an easier rule to keep in our heads, and costs us + // nothing. + std::shared_ptr<T> prev; + std::shared_ptr<T> expectedDup; + { + std::lock_guard<std::mutex> lg(*detail::atomicReadMostlyMu); + auto index = curMainPtrIndex_.load(failureOrder); + ReadMostlyMainPtr<T>& oldMain = mainPtrs_[index]; + if (oldMain.get() != expected.get()) { + expectedDup = std::move(expected); + expected = oldMain.getStdShared(); + return false; + } + prev = exchangeLocked(desired, successOrder); + } + return true; + } + + private: + // Must hold the global mutex. + std::shared_ptr<T> exchangeLocked( + std::shared_ptr<T> ptr, + std::memory_order order = std::memory_order_seq_cst) { + // This is where the tricky bits happen; all modifications of the mainPtrs_ + // and index happen here. We maintain the invariant that, on entry to this + // method, all read-side critical sections in progress are using the version + // indicated by curMainPtrIndex_, and the other version is nulled out. + // (Readers can still hold a ReadMostlySharedPtr to the thing the old + // version used to point to; they just can't access the old version to get + // that handle any more). + auto index = curMainPtrIndex_.load(std::memory_order_relaxed); + ReadMostlyMainPtr<T>& oldMain = mainPtrs_[index]; + ReadMostlyMainPtr<T>& newMain = mainPtrs_[1 - index]; + DCHECK(newMain.get() == nullptr) + << "Invariant should ensure that at most one version is non-null"; + newMain.reset(std::move(ptr)); + // If order is acq_rel, it should degrade to just release, since this is a + // store rather than an RMW. (Of course, this is such a slow method that we + // don't really care, but precision is its own reward. If TSAN one day + // understands asymmetric barriers, this will also improve its error + // detection here). We get our "acquire-y-ness" from the mutex. + auto realOrder = + (order == std::memory_order_acq_rel ? std::memory_order_release + : order); + // After this, read-side critical sections can access both versions, but + // new ones will use newMain. + // This is also synchronization point with loads. + curMainPtrIndex_.store(1 - index, realOrder); + // Wait for all read-side critical sections using oldMain to finish. + detail::atomicReadMostlyDomain->synchronize(); + // We've reestablished the first half of the invariant (all readers are + // using newMain), now let's establish the other one (that the other pointer + // is null). + auto result = oldMain.getStdShared(); + oldMain.reset(); + return result; + } + + // The right way to think of this implementation is as an + // std::atomic<ReadMostlyMainPtr<T>*>, protected by RCU. There's only two + // tricky parts: + // 1. We give ourselves our own RCU domain, and synchronize on modification, + // so that we don't do any batching of deallocations. This gives + // shared_ptr-like eager reclamation semantics. + // 2. Instead of putting the ReadMostlyMainPtrs on the heap, we keep them as + // part of the same object to improve locality. + + // Really, just a 0/1 index. This is also the synchronization point for memory + // orders. + std::atomic<uint8_t> curMainPtrIndex_; + + // Both the ReadMostlyMainPtrs themselves and the domain have nontrivial + // indirections even on the read path, and asymmetric barriers on the write + // path. Some of these could be fused as a later optimization, at the cost of + // having to put more tricky threading primitives in this class that are + // currently abstracted out by those. + ReadMostlyMainPtr<T> mainPtrs_[2]; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/AutoTimer.h b/ios/Pods/Flipper-Folly/folly/experimental/AutoTimer.h new file mode 100644 index 000000000..078db0896 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/AutoTimer.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <string> +#include <type_traits> + +#include <folly/Conv.h> +#include <folly/Format.h> +#include <folly/Optional.h> +#include <folly/String.h> +#include <glog/logging.h> + +namespace folly { + +// Default logger +enum class GoogleLoggerStyle { SECONDS, PRETTY }; +template <GoogleLoggerStyle> +struct GoogleLogger; + +/** + * Automatically times a block of code, printing a specified log message on + * destruction or whenever the log() method is called. For example: + * + * AutoTimer t("Foo() completed"); + * doWork(); + * t.log("Do work finished"); + * doMoreWork(); + * + * This would print something like: + * "Do work finished in 1.2 seconds" + * "Foo() completed in 4.3 seconds" + * + * You can customize what you use as the logger and clock. The logger needs + * to have an operator()(StringPiece, std::chrono::duration<double>) that + * gets a message and a duration. The clock needs to model Clock from + * std::chrono. + * + * The default logger logs usings glog. It only logs if the message is + * non-empty, so you can also just use this class for timing, e.g.: + * + * AutoTimer t; + * doWork() + * const auto how_long = t.log(); + */ +template < + class Logger = GoogleLogger<GoogleLoggerStyle::PRETTY>, + class Clock = std::chrono::high_resolution_clock> +class AutoTimer final { + public: + using DoubleSeconds = std::chrono::duration<double>; + + explicit AutoTimer( + std::string&& msg = "", + const DoubleSeconds& minTimetoLog = DoubleSeconds::zero(), + Logger&& logger = Logger()) + : destructionMessage_(std::move(msg)), + minTimeToLog_(minTimetoLog), + logger_(std::move(logger)) {} + + // It doesn't really make sense to copy AutoTimer + // Movable to make sure the helper method for creating an AutoTimer works. + AutoTimer(const AutoTimer&) = delete; + AutoTimer(AutoTimer&&) = default; + AutoTimer& operator=(const AutoTimer&) = delete; + AutoTimer& operator=(AutoTimer&&) = default; + + ~AutoTimer() { + if (destructionMessage_) { + log(destructionMessage_.value()); + } + } + + DoubleSeconds log(StringPiece msg = "") { + return logImpl(Clock::now(), msg); + } + + template <typename... Args> + DoubleSeconds log(Args&&... args) { + auto now = Clock::now(); + return logImpl(now, to<std::string>(std::forward<Args>(args)...)); + } + + template <typename... Args> + DoubleSeconds logFormat(Args&&... args) { + auto now = Clock::now(); + return logImpl(now, format(std::forward<Args>(args)...).str()); + } + + private: + // We take in the current time so that we don't measure time to call + // to<std::string> or format() in the duration. + DoubleSeconds logImpl(std::chrono::time_point<Clock> now, StringPiece msg) { + auto duration = now - start_; + if (duration >= minTimeToLog_) { + logger_(msg, duration); + } + start_ = Clock::now(); // Don't measure logging time + return duration; + } + + Optional<std::string> destructionMessage_; + std::chrono::time_point<Clock> start_ = Clock::now(); + DoubleSeconds minTimeToLog_; + Logger logger_; +}; + +template < + class Logger = GoogleLogger<GoogleLoggerStyle::PRETTY>, + class Clock = std::chrono::high_resolution_clock> +auto makeAutoTimer( + std::string&& msg = "", + const std::chrono::duration<double>& minTimeToLog = + std::chrono::duration<double>::zero(), + Logger&& logger = Logger()) { + return AutoTimer<Logger, Clock>( + std::move(msg), minTimeToLog, std::move(logger)); +} + +template <GoogleLoggerStyle Style> +struct GoogleLogger final { + void operator()(StringPiece msg, const std::chrono::duration<double>& sec) + const { + if (msg.empty()) { + return; + } + if (Style == GoogleLoggerStyle::PRETTY) { + LOG(INFO) << msg << " in " + << prettyPrint(sec.count(), PrettyType::PRETTY_TIME); + } else { + LOG(INFO) << msg << " in " << sec.count() << " seconds"; + } + } +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/BitVectorCoding.h b/ios/Pods/Flipper-Folly/folly/experimental/BitVectorCoding.h new file mode 100644 index 000000000..2fcda2bc0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/BitVectorCoding.h @@ -0,0 +1,452 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/experimental/Bits.h> +#include <folly/experimental/CodingDetail.h> +#include <folly/experimental/Instructions.h> +#include <folly/experimental/Select64.h> +#include <folly/lang/Bits.h> +#include <glog/logging.h> + +#if !FOLLY_X64 +#error BitVectorCoding.h requires x86_64 +#endif + +namespace folly { +namespace compression { + +static_assert(kIsLittleEndian, "BitVectorCoding.h requires little endianness"); + +template <class Pointer> +struct BitVectorCompressedListBase { + BitVectorCompressedListBase() = default; + + template <class OtherPointer> + BitVectorCompressedListBase( + const BitVectorCompressedListBase<OtherPointer>& other) + : size(other.size), + upperBound(other.upperBound), + data(other.data), + bits(reinterpret_cast<Pointer>(other.bits)), + skipPointers(reinterpret_cast<Pointer>(other.skipPointers)), + forwardPointers(reinterpret_cast<Pointer>(other.forwardPointers)) {} + + template <class T = Pointer> + auto free() -> decltype(::free(T(nullptr))) { + return ::free(data.data()); + } + + size_t size = 0; + size_t upperBound = 0; + + folly::Range<Pointer> data; + + Pointer bits = nullptr; + Pointer skipPointers = nullptr; + Pointer forwardPointers = nullptr; +}; + +typedef BitVectorCompressedListBase<const uint8_t*> BitVectorCompressedList; +typedef BitVectorCompressedListBase<uint8_t*> MutableBitVectorCompressedList; + +template < + class Value, + class SkipValue, + size_t kSkipQuantum = 0, + size_t kForwardQuantum = 0> +struct BitVectorEncoder { + static_assert( + std::is_integral<Value>::value && std::is_unsigned<Value>::value, + "Value should be unsigned integral"); + + typedef BitVectorCompressedList CompressedList; + typedef MutableBitVectorCompressedList MutableCompressedList; + + typedef Value ValueType; + typedef SkipValue SkipValueType; + struct Layout; + + static constexpr size_t skipQuantum = kSkipQuantum; + static constexpr size_t forwardQuantum = kForwardQuantum; + + template <class RandomAccessIterator> + static MutableCompressedList encode( + RandomAccessIterator begin, + RandomAccessIterator end) { + if (begin == end) { + return MutableCompressedList(); + } + BitVectorEncoder encoder(size_t(end - begin), *(end - 1)); + for (; begin != end; ++begin) { + encoder.add(*begin); + } + return encoder.finish(); + } + + explicit BitVectorEncoder(const MutableCompressedList& result) + : bits_(result.bits), + skipPointers_(result.skipPointers), + forwardPointers_(result.forwardPointers), + result_(result) { + memset(result.data.data(), 0, result.data.size()); + } + + BitVectorEncoder(size_t size, ValueType upperBound) + : BitVectorEncoder( + Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {} + + void add(ValueType value) { + CHECK_LT(value, std::numeric_limits<ValueType>::max()); + // Also works when lastValue_ == -1. + CHECK_GT(value + 1, lastValue_ + 1) + << "BitVectorCoding only supports stricly monotone lists"; + + auto block = bits_ + (value / 64) * sizeof(uint64_t); + size_t inner = value % 64; + folly::Bits<folly::Unaligned<uint64_t>>::set( + reinterpret_cast<folly::Unaligned<uint64_t>*>(block), inner); + + if (skipQuantum != 0) { + size_t nextSkipPointerSize = value / skipQuantum; + while (skipPointersSize_ < nextSkipPointerSize) { + auto pos = skipPointersSize_++; + folly::storeUnaligned<SkipValueType>( + skipPointers_ + pos * sizeof(SkipValueType), size_); + } + } + + if (forwardQuantum != 0) { + if (size_ != 0 && (size_ % forwardQuantum == 0)) { + const auto pos = size_ / forwardQuantum - 1; + folly::storeUnaligned<SkipValueType>( + forwardPointers_ + pos * sizeof(SkipValueType), value); + } + } + + lastValue_ = value; + ++size_; + } + + const MutableCompressedList& finish() const { + CHECK_EQ(size_, result_.size); + // TODO(ott): Relax this assumption. + CHECK_EQ(result_.upperBound, lastValue_); + return result_; + } + + private: + uint8_t* const bits_ = nullptr; + uint8_t* const skipPointers_ = nullptr; + uint8_t* const forwardPointers_ = nullptr; + + ValueType lastValue_ = -1; + size_t size_ = 0; + size_t skipPointersSize_ = 0; + + MutableCompressedList result_; +}; + +template < + class Value, + class SkipValue, + size_t kSkipQuantum, + size_t kForwardQuantum> +struct BitVectorEncoder<Value, SkipValue, kSkipQuantum, kForwardQuantum>:: + Layout { + static Layout fromUpperBoundAndSize(size_t upperBound, size_t size) { + Layout layout; + layout.size = size; + layout.upperBound = upperBound; + + size_t bitVectorSizeInBytes = (upperBound / 8) + 1; + layout.bits = bitVectorSizeInBytes; + + if (skipQuantum != 0) { + size_t numSkipPointers = upperBound / skipQuantum; + layout.skipPointers = numSkipPointers * sizeof(SkipValueType); + } + if (forwardQuantum != 0) { + size_t numForwardPointers = size / forwardQuantum; + layout.forwardPointers = numForwardPointers * sizeof(SkipValueType); + } + + CHECK_LT(size, std::numeric_limits<SkipValueType>::max()); + + return layout; + } + + size_t bytes() const { + return bits + skipPointers + forwardPointers; + } + + template <class Range> + BitVectorCompressedListBase<typename Range::iterator> openList( + Range& buf) const { + BitVectorCompressedListBase<typename Range::iterator> result; + result.size = size; + result.upperBound = upperBound; + result.data = buf.subpiece(0, bytes()); + auto advance = [&](size_t n) { + auto begin = buf.data(); + buf.advance(n); + return begin; + }; + + result.bits = advance(bits); + result.skipPointers = advance(skipPointers); + result.forwardPointers = advance(forwardPointers); + CHECK_EQ(buf.data() - result.data.data(), bytes()); + + return result; + } + + MutableCompressedList allocList() const { + uint8_t* buf = nullptr; + if (size > 0) { + buf = static_cast<uint8_t*>(malloc(bytes() + 7)); + } + folly::MutableByteRange bufRange(buf, bytes()); + return openList(bufRange); + } + + size_t size = 0; + size_t upperBound = 0; + + // Sizes in bytes. + size_t bits = 0; + size_t skipPointers = 0; + size_t forwardPointers = 0; +}; + +template < + class Encoder, + class Instructions = instructions::Default, + bool kUnchecked = false> +class BitVectorReader : detail::ForwardPointers<Encoder::forwardQuantum>, + detail::SkipPointers<Encoder::skipQuantum> { + public: + typedef Encoder EncoderType; + typedef typename Encoder::ValueType ValueType; + // A bitvector can only be as large as its largest value. + typedef typename Encoder::ValueType SizeType; + typedef typename Encoder::SkipValueType SkipValueType; + + explicit BitVectorReader(const typename Encoder::CompressedList& list) + : detail::ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers), + detail::SkipPointers<Encoder::skipQuantum>(list.skipPointers), + bits_(list.bits), + size_(list.size), + upperBound_( + (kUnchecked || UNLIKELY(list.size == 0)) ? 0 : list.upperBound) { + reset(); + } + + void reset() { + // Pretend the bitvector is prefixed by a block of zeroes. + block_ = 0; + position_ = static_cast<SizeType>(-1); + outer_ = static_cast<SizeType>(-sizeof(uint64_t)); + value_ = kInvalidValue; + } + + bool next() { + if (!kUnchecked && UNLIKELY(position() + 1 >= size_)) { + return setDone(); + } + + while (block_ == 0) { + outer_ += sizeof(uint64_t); + block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_); + } + + ++position_; + auto inner = Instructions::ctz(block_); + block_ = Instructions::blsr(block_); + + return setValue(inner); + } + + bool skip(SizeType n) { + if (n == 0) { + return valid(); + } + + if (!kUnchecked && position() + n >= size_) { + return setDone(); + } + // Small skip optimization. + if (LIKELY(n < kLinearScanThreshold)) { + for (size_t i = 0; i < n; ++i) { + next(); + } + return true; + } + + position_ += n; + + // Use forward pointer. + if (Encoder::forwardQuantum > 0 && n > Encoder::forwardQuantum) { + const size_t steps = position_ / Encoder::forwardQuantum; + const size_t dest = folly::loadUnaligned<SkipValueType>( + this->forwardPointers_ + (steps - 1) * sizeof(SkipValueType)); + + reposition(dest); + n = position_ + 1 - steps * Encoder::forwardQuantum; + } + + size_t cnt; + // Find necessary block. + while ((cnt = Instructions::popcount(block_)) < n) { + n -= cnt; + outer_ += sizeof(uint64_t); + block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_); + } + + // Skip to the n-th one in the block. + DCHECK_GT(n, 0); + auto inner = select64<Instructions>(block_, n - 1); + block_ &= (uint64_t(-1) << inner) << 1; + + return setValue(inner); + } + + bool skipTo(ValueType v) { + // Also works when value_ == kInvalidValue. + if (v != kInvalidValue) { + DCHECK_GE(v + 1, value_ + 1); + } + + if (!kUnchecked && v > upperBound_) { + return setDone(); + } else if (v == value_) { + return true; + } + + // Small skip optimization. + if (v - value_ < kLinearScanThreshold) { + do { + next(); + } while (value() < v); + + return true; + } + + if (Encoder::skipQuantum > 0 && v - value_ > Encoder::skipQuantum) { + size_t q = v / Encoder::skipQuantum; + auto skipPointer = folly::loadUnaligned<SkipValueType>( + this->skipPointers_ + (q - 1) * sizeof(SkipValueType)); + position_ = static_cast<SizeType>(skipPointer) - 1; + + reposition(q * Encoder::skipQuantum); + } + + // Find the value. + size_t outer = v / 64 * sizeof(uint64_t); + + while (outer_ != outer) { + position_ += Instructions::popcount(block_); + outer_ += sizeof(uint64_t); + block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_); + DCHECK_LE(outer_, outer); + } + + uint64_t mask = ~((uint64_t(1) << (v % 64)) - 1); + position_ += Instructions::popcount(block_ & ~mask) + 1; + block_ &= mask; + + while (block_ == 0) { + outer_ += sizeof(uint64_t); + block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_); + } + + auto inner = Instructions::ctz(block_); + block_ = Instructions::blsr(block_); + + setValue(inner); + return true; + } + + SizeType size() const { + return size_; + } + + bool valid() const { + return position() < size(); // Also checks that position() != -1. + } + + SizeType position() const { + return position_; + } + ValueType value() const { + DCHECK(valid()); + return value_; + } + + bool jump(SizeType n) { + reset(); + return skip(n + 1); + } + + bool jumpTo(ValueType v) { + reset(); + return skipTo(v); + } + + bool setDone() { + value_ = kInvalidValue; + position_ = size_; + return false; + } + + private: + // Must hold kInvalidValue + 1 == 0. + constexpr static ValueType kInvalidValue = -1; + + bool setValue(size_t inner) { + value_ = static_cast<ValueType>(8 * outer_ + inner); + return true; + } + + void reposition(size_t dest) { + outer_ = dest / 64 * 8; + // We maintain the invariant that outer_ is divisible by 8. + block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_); + block_ &= ~((uint64_t(1) << (dest % 64)) - 1); + } + + constexpr static size_t kLinearScanThreshold = 4; + + const uint8_t* const bits_; + uint64_t block_; + SizeType outer_; + SizeType position_; + ValueType value_; + + const SizeType size_; + const ValueType upperBound_; +}; + +} // namespace compression +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/Bits.h b/ios/Pods/Flipper-Folly/folly/experimental/Bits.h new file mode 100644 index 000000000..7791da185 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/Bits.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <limits> +#include <type_traits> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/lang/Bits.h> + +namespace folly { + +template <class T> +struct UnalignedNoASan : public Unaligned<T> {}; + +// As a general rule, bit operations work on unsigned values only; +// right-shift is arithmetic for signed values, and that can lead to +// unpleasant bugs. + +namespace detail { + +/** + * Helper class to make Bits<T> (below) work with both aligned values + * (T, where T is an unsigned integral type) or unaligned values + * (Unaligned<T>, where T is an unsigned integral type) + */ +template <class T, class Enable = void> +struct BitsTraits; + +// Partial specialization for Unaligned<T>, where T is unsigned integral +// loadRMW is the same as load, but it indicates that it loads for a +// read-modify-write operation (we write back the bits we won't change); +// silence the GCC warning in that case. +template <class T> +struct BitsTraits< + Unaligned<T>, + typename std::enable_if<(std::is_integral<T>::value)>::type> { + typedef T UnderlyingType; + static T load(const Unaligned<T>& x) { + return x.value; + } + static void store(Unaligned<T>& x, T v) { + x.value = v; + } + static T loadRMW(const Unaligned<T>& x) { + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wuninitialized") + FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized") + return x.value; + FOLLY_POP_WARNING + } +}; + +// Special version that allows one to disable address sanitizer on demand. +template <class T> +struct BitsTraits< + UnalignedNoASan<T>, + typename std::enable_if<(std::is_integral<T>::value)>::type> { + typedef T UnderlyingType; + static T FOLLY_DISABLE_ADDRESS_SANITIZER load(const UnalignedNoASan<T>& x) { + return x.value; + } + static void FOLLY_DISABLE_ADDRESS_SANITIZER + store(UnalignedNoASan<T>& x, T v) { + x.value = v; + } + static T FOLLY_DISABLE_ADDRESS_SANITIZER + loadRMW(const UnalignedNoASan<T>& x) { + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wuninitialized") + FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized") + return x.value; + FOLLY_POP_WARNING + } +}; + +// Partial specialization for T, where T is unsigned integral +template <class T> +struct BitsTraits< + T, + typename std::enable_if<(std::is_integral<T>::value)>::type> { + typedef T UnderlyingType; + static T load(const T& x) { + return x; + } + static void store(T& x, T v) { + x = v; + } + static T loadRMW(const T& x) { + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wuninitialized") + FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized") + return x; + FOLLY_POP_WARNING + } +}; + +} // namespace detail + +/** + * Wrapper class with static methods for various bit-level operations, + * treating an array of T as an array of bits (in little-endian order). + * (T is either an unsigned integral type or Unaligned<X>, where X is + * an unsigned integral type) + */ +template <class T, class Traits = detail::BitsTraits<T>> +struct Bits { + typedef typename Traits::UnderlyingType UnderlyingType; + typedef T type; + static_assert(sizeof(T) == sizeof(UnderlyingType), "Size mismatch"); + + /** + * Number of bits in a block. + */ + static constexpr size_t bitsPerBlock = std::numeric_limits< + typename std::make_unsigned<UnderlyingType>::type>::digits; + + /** + * Byte index of the given bit. + */ + static constexpr size_t blockIndex(size_t bit) { + return bit / bitsPerBlock; + } + + /** + * Offset in block of the given bit. + */ + static constexpr size_t bitOffset(size_t bit) { + return bit % bitsPerBlock; + } + + /** + * Number of blocks used by the given number of bits. + */ + static constexpr size_t blockCount(size_t nbits) { + return nbits / bitsPerBlock + (nbits % bitsPerBlock != 0); + } + + /** + * Set the given bit. + */ + static void set(T* p, size_t bit); + + /** + * Clear the given bit. + */ + static void clear(T* p, size_t bit); + + /** + * Test the given bit. + */ + static bool test(const T* p, size_t bit); + + /** + * Set count contiguous bits starting at bitStart to the values + * from the least significant count bits of value; little endian. + * (value & 1 becomes the bit at bitStart, etc) + * Precondition: count <= sizeof(T) * 8 + * Precondition: value can fit in 'count' bits + */ + static void set(T* p, size_t bitStart, size_t count, UnderlyingType value); + + /** + * Get count contiguous bits starting at bitStart. + * Precondition: count <= sizeof(T) * 8 + */ + static UnderlyingType get(const T* p, size_t bitStart, size_t count); + + /** + * Count the number of bits set in a range of blocks. + */ + static size_t count(const T* begin, const T* end); + + private: + // Same as set, assumes all bits are in the same block. + // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8) + static void + innerSet(T* p, size_t bitStart, size_t count, UnderlyingType value); + + // Same as get, assumes all bits are in the same block. + // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8) + static UnderlyingType innerGet(const T* p, size_t bitStart, size_t count); + + static constexpr UnderlyingType zero = UnderlyingType(0); + static constexpr UnderlyingType one = UnderlyingType(1); + + using UnsignedType = typename std::make_unsigned<UnderlyingType>::type; + static constexpr UnderlyingType ones(size_t count) { + return (count < bitsPerBlock) + ? static_cast<UnderlyingType>((UnsignedType{1} << count) - 1) + : ~zero; + } +}; + +template <class T, class Traits> +inline void Bits<T, Traits>::set(T* p, size_t bit) { + T& block = p[blockIndex(bit)]; + Traits::store(block, Traits::loadRMW(block) | (one << bitOffset(bit))); +} + +template <class T, class Traits> +inline void Bits<T, Traits>::clear(T* p, size_t bit) { + T& block = p[blockIndex(bit)]; + Traits::store(block, Traits::loadRMW(block) & ~(one << bitOffset(bit))); +} + +template <class T, class Traits> +inline void Bits<T, Traits>::set( + T* p, + size_t bitStart, + size_t count, + UnderlyingType value) { + DCHECK_LE(count, sizeof(UnderlyingType) * 8); + size_t cut = bitsPerBlock - count; + if (cut != 8 * sizeof(UnderlyingType)) { + using U = typename std::make_unsigned<UnderlyingType>::type; + DCHECK_EQ(value, UnderlyingType(U(value) << cut) >> cut); + } + size_t idx = blockIndex(bitStart); + size_t offset = bitOffset(bitStart); + if (std::is_signed<UnderlyingType>::value) { + value &= ones(count); + } + if (offset + count <= bitsPerBlock) { + innerSet(p + idx, offset, count, value); + } else { + size_t countInThisBlock = bitsPerBlock - offset; + size_t countInNextBlock = count - countInThisBlock; + + UnderlyingType thisBlock = UnderlyingType(value & ones(countInThisBlock)); + UnderlyingType nextBlock = UnderlyingType(value >> countInThisBlock); + if (std::is_signed<UnderlyingType>::value) { + nextBlock &= ones(countInNextBlock); + } + innerSet(p + idx, offset, countInThisBlock, thisBlock); + innerSet(p + idx + 1, 0, countInNextBlock, nextBlock); + } +} + +template <class T, class Traits> +inline void Bits<T, Traits>::innerSet( + T* p, + size_t offset, + size_t count, + UnderlyingType value) { + // Mask out bits and set new value + UnderlyingType v = Traits::loadRMW(*p); + v &= ~(ones(count) << offset); + v |= (value << offset); + Traits::store(*p, v); +} + +template <class T, class Traits> +inline bool Bits<T, Traits>::test(const T* p, size_t bit) { + return Traits::load(p[blockIndex(bit)]) & (one << bitOffset(bit)); +} + +template <class T, class Traits> +inline auto Bits<T, Traits>::get(const T* p, size_t bitStart, size_t count) + -> UnderlyingType { + if (count == 0) { + return UnderlyingType{}; + } + + DCHECK_LE(count, sizeof(UnderlyingType) * 8); + size_t idx = blockIndex(bitStart); + size_t offset = bitOffset(bitStart); + UnderlyingType ret; + if (offset + count <= bitsPerBlock) { + ret = innerGet(p + idx, offset, count); + } else { + size_t countInThisBlock = bitsPerBlock - offset; + size_t countInNextBlock = count - countInThisBlock; + UnderlyingType thisBlockValue = innerGet(p + idx, offset, countInThisBlock); + UnderlyingType nextBlockValue = innerGet(p + idx + 1, 0, countInNextBlock); + ret = (nextBlockValue << countInThisBlock) | thisBlockValue; + } + if (std::is_signed<UnderlyingType>::value) { + size_t emptyBits = bitsPerBlock - count; + ret <<= emptyBits; + ret >>= emptyBits; + } + return ret; +} + +template <class T, class Traits> +inline auto Bits<T, Traits>::innerGet(const T* p, size_t offset, size_t count) + -> UnderlyingType { + return (Traits::load(*p) >> offset) & ones(count); +} + +template <class T, class Traits> +inline size_t Bits<T, Traits>::count(const T* begin, const T* end) { + size_t n = 0; + for (; begin != end; ++begin) { + n += popcount(Traits::load(*begin)); + } + return n; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/CodingDetail.h b/ios/Pods/Flipper-Folly/folly/experimental/CodingDetail.h new file mode 100644 index 000000000..ee7f2970a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/CodingDetail.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Giuseppe Ottaviano <ott@fb.com> + * + * Shared utils for BitVectorCoding.h and EliasFanoCoding.h. + */ + +#pragma once + +#include <stddef.h> + +namespace folly { +namespace compression { +namespace detail { + +/** + * Helpers to store pointers to forward and skip pointer arrays only + * if they are used, that is, the quantum is nonzero. If it is 0, the + * class is empty, and the member is static to keep the syntax valid, + * thus it will take no space in a derived class thanks to empty base + * class optimization. + */ +template <size_t> +class ForwardPointers { + protected: + explicit ForwardPointers(const unsigned char* ptr) : forwardPointers_(ptr) {} + const unsigned char* const forwardPointers_; +}; +template <> +class ForwardPointers<0> { + protected: + explicit ForwardPointers(const unsigned char*) {} + constexpr static const unsigned char* const forwardPointers_{}; +}; + +template <size_t> +class SkipPointers { + protected: + explicit SkipPointers(const unsigned char* ptr) : skipPointers_(ptr) {} + const unsigned char* const skipPointers_; +}; +template <> +class SkipPointers<0> { + protected: + explicit SkipPointers(const unsigned char*) {} + constexpr static const unsigned char* const skipPointers_{}; +}; +} // namespace detail +} // namespace compression +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser-inl.h b/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser-inl.h new file mode 100644 index 000000000..1869511fd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser-inl.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +#include <boost/function_types/parameter_types.hpp> +#include <boost/mpl/equal.hpp> +#include <boost/mpl/pop_front.hpp> +#include <boost/mpl/transform.hpp> +#include <boost/mpl/vector.hpp> + +#include <folly/Conv.h> +#include <folly/Traits.h> + +namespace folly { + +// Auto-conversion of key/value based on callback signature, documented in +// DynamicParser.h. +namespace detail { +class IdentifyCallable { + public: + enum class Kind { Function, MemberFunction }; + template <typename Fn> + constexpr static Kind getKind() { + return test<Fn>(nullptr); + } + + private: + template <typename Fn> + using IsMemFn = std::is_member_pointer<decltype(&Fn::operator())>; + template <typename Fn> + constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type test( + IsMemFn<Fn>*) { + return IdentifyCallable::Kind::MemberFunction; + } + template <typename> + constexpr static Kind test(...) { + return IdentifyCallable::Kind::Function; + } +}; + +template <IdentifyCallable::Kind, typename Fn> +struct ArgumentTypesByKind {}; +template <typename Fn> +struct ArgumentTypesByKind<IdentifyCallable::Kind::MemberFunction, Fn> { + using type = typename boost::mpl::template pop_front< + typename boost::function_types::template parameter_types<decltype( + &Fn::operator())>::type>::type; +}; +template <typename Fn> +struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> { + using type = typename boost::function_types::template parameter_types<Fn>; +}; + +template <typename Fn> +using ArgumentTypes = + typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type; + +// At present, works for lambdas or plain old functions, but can be +// extended. The comparison deliberately strips cv-qualifiers and +// reference, leaving that choice up to the caller. +template <typename Fn, typename... Args> +struct HasArgumentTypes + : boost::mpl::template equal< + typename boost::mpl:: + transform<ArgumentTypes<Fn>, remove_cvref<boost::mpl::_1>>::type, + boost::mpl::vector<Args...>>::type {}; +template <typename... Args> +using EnableForArgTypes = + typename std::enable_if<HasArgumentTypes<Args...>::value, void>::type; + +// No arguments +template <typename Fn> +EnableForArgTypes<Fn> +invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) { + f(); +} + +// 1 argument -- pass only the value +// +// folly::dynamic (no conversion) +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic> +invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { + fn(v); +} +// int64_t +template <typename Fn> +EnableForArgTypes<Fn, int64_t> +invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { + fn(v.asInt()); +} +// bool +template <typename Fn> +EnableForArgTypes<Fn, bool> +invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { + fn(v.asBool()); +} +// double +template <typename Fn> +EnableForArgTypes<Fn, double> +invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { + fn(v.asDouble()); +} +// std::string +template <typename Fn> +EnableForArgTypes<Fn, std::string> +invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) { + fn(v.asString()); +} + +// +// 2 arguments -- pass both the key and the value. +// + +// Pass the key as folly::dynamic, without conversion +// +// folly::dynamic, folly::dynamic (no conversion of value, either) +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic, folly::dynamic> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k, v); +} +// folly::dynamic, int64_t +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic, int64_t> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k, v.asInt()); +} +// folly::dynamic, bool +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic, bool> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k, v.asBool()); +} +// folly::dynamic, double +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic, double> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k, v.asDouble()); +} +// folly::dynamic, std::string +template <typename Fn> +EnableForArgTypes<Fn, folly::dynamic, std::string> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k, v.asString()); +} + +// Convert the key to std::string. +// +// std::string, folly::dynamic (no conversion of value) +template <typename Fn> +EnableForArgTypes<Fn, std::string, folly::dynamic> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asString(), v); +} +// std::string, int64_t +template <typename Fn> +EnableForArgTypes<Fn, std::string, int64_t> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asString(), v.asInt()); +} +// std::string, bool +template <typename Fn> +EnableForArgTypes<Fn, std::string, bool> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asString(), v.asBool()); +} +// std::string, double +template <typename Fn> +EnableForArgTypes<Fn, std::string, double> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asString(), v.asDouble()); +} +// std::string, std::string +template <typename Fn> +EnableForArgTypes<Fn, std::string, std::string> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asString(), v.asString()); +} + +// Convert the key to int64_t (good for arrays). +// +// int64_t, folly::dynamic (no conversion of value) +template <typename Fn> +EnableForArgTypes<Fn, int64_t, folly::dynamic> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asInt(), v); +} +// int64_t, int64_t +template <typename Fn> +EnableForArgTypes<Fn, int64_t, int64_t> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asInt(), v.asInt()); +} +// int64_t, bool +template <typename Fn> +EnableForArgTypes<Fn, int64_t, bool> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asInt(), v.asBool()); +} +// int64_t, double +template <typename Fn> +EnableForArgTypes<Fn, int64_t, double> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asInt(), v.asDouble()); +} +// int64_t, std::string +template <typename Fn> +EnableForArgTypes<Fn, int64_t, std::string> +invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) { + fn(k.asInt(), v.asString()); +} +} // namespace detail + +template <typename Fn> +void DynamicParser::optional(const folly::dynamic& key, Fn fn) { + wrapError(&key, [&]() { + if (auto vp = value().get_ptr(key)) { + parse(key, *vp, fn); + } + }); +} + +// +// Implementation of DynamicParser template & inline methods. +// + +template <typename Fn> +void DynamicParser::required(const folly::dynamic& key, Fn fn) { + wrapError(&key, [&]() { + auto vp = value().get_ptr(key); + if (!vp) { + throw std::runtime_error(folly::to<std::string>( + "Couldn't find key ", + detail::toPseudoJson(key), + " in dynamic object")); + } + parse(key, *vp, fn); + }); +} + +template <typename Fn> +void DynamicParser::objectItems(Fn fn) { + wrapError(nullptr, [&]() { + for (const auto& kv : value().items()) { // .items() can throw + parse(kv.first, kv.second, fn); + } + }); +} + +template <typename Fn> +void DynamicParser::arrayItems(Fn fn) { + wrapError(nullptr, [&]() { + size_t i = 0; + for (const auto& v : value()) { // Iteration can throw + parse(i, v, fn); // i => dynamic cannot throw + ++i; + } + }); +} + +template <typename Fn> +void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) { + try { + fn(); + } catch (DynamicParserLogicError&) { + // When the parser is misused, we throw all the way up to the user, + // instead of reporting it as if the input is invalid. + throw; + } catch (DynamicParserParseError&) { + // We are just bubbling up a parse error for OnError::THROW. + throw; + } catch (const std::exception& ex) { + reportError(lookup_k, ex); + } +} + +template <typename Fn> +void DynamicParser::parse( + const folly::dynamic& k, + const folly::dynamic& v, + Fn fn) { + auto guard = stack_.push(k, v); // User code can nest parser calls. + wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); }); +} + +inline const folly::dynamic& DynamicParser::ParserStack::key() const { + if (!key_) { + throw DynamicParserLogicError("Only call key() inside parsing callbacks."); + } + return *key_; +} + +inline const folly::dynamic& DynamicParser::ParserStack::value() const { + if (!value_) { + throw DynamicParserLogicError( + "Parsing nullptr, or parsing after releaseErrors()"); + } + return *value_; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser.h b/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser.h new file mode 100644 index 000000000..37049fcc9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/DynamicParser.h @@ -0,0 +1,405 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/CPortability.h> +#include <folly/ScopeGuard.h> +#include <folly/dynamic.h> + +namespace folly { + +/** + * DynamicParser provides a tiny DSL for easily, correctly, and losslessly + * parsing a folly::dynamic into any other representation. + * + * To make this concrete, this lets you take a JSON config that potentially + * contains user errors, and parse __all__ of its valid parts, while + * automatically and __reversibly__ recording any parts that cause errors: + * + * {"my values": { + * "an int": "THIS WILL BE RECORDED AS AN ERROR, BUT WE'LL PARSE THE REST", + * "a double": 3.1415, + * "keys & values": { + * "the sky is blue": true, + * "THIS WILL ALSO BE RECORDED AS AN ERROR": "cheese", + * "2+2=5": false, + * } + * }} + * + * To parse this JSON, you need no exception handling, it is as easy as: + * + * folly::dynamic d = ...; // Input + * int64_t integer; // Three outputs + * double real; + * std::map<std::string, bool> enabled_widgets; + * DynamicParser p(DynamicParser::OnError::RECORD, &d); + * p.required("my values", [&]() { + * p.optional("an int", [&](int64_t v) { integer = v; }); + * p.required("a double", [&](double v) { real = v; }); + * p.optional("keys & values", [&]() { + * p.objectItems([&](std::string widget, bool enabled) { + * enabled_widgets.emplace(widget, enabled); + * }); + * }); + * }); + * + * Your code in the lambdas can throw, and this will be reported just like + * missing key and type conversion errors, with precise context on what part + * of the folly::dynamic caused the error. No need to throw: + * std::runtime_error("Value X at key Y caused a flux capacitor overload") + * This will do: + * std::runtime_error("Flux capacitor overload") + * + * == Keys and values are auto-converted to match your callback == + * + * DynamicParser's optional(), required(), objectItems(), and + * arrayItems() automatically convert the current key and value to match the + * signature of the provided callback. parser.key() and parser.value() can + * be used to access the same data without conversion. + * + * The following types are supported -- you should generally take arguments + * by-value, or by-const-reference for dynamics & strings you do not copy. + * + * Key: folly::dynamic (no conversion), std::string, int64_t + * Value: folly::dynamic (no conversion), int64_t, bool, double, std::string + * + * There are 21 supported callback signatures, of three kinds: + * + * 1: No arguments -- useful if you will just call more parser methods. + * + * 5: The value alone -- the common case for optional() and required(). + * [&](whatever_t value) {} + * + * 15: Both the key and the value converted according to the rules above: + * [&](whatever_t key, whatever_t) {} + * + * NB: The key alone should be rarely needed, but these callback styles + * provide it with no conversion overhead, and only minimal verbosity: + * [&](const std::string& k, const folly::dynamic&) {} + * [&]() { auto k = p.key().asString(); } + * + * == How `releaseErrors()` can make your parse lossless == + * + * If you write parsing code by hand, you usually end up with error-handling + * resembling that of OnError::THROW -- the first error you hit aborts the + * whole parse, and you report it. + * + * OnError::RECORD offers a more user-friendly alternative for "parse, + * serialize, re-parse" pipelines, akin to what web-forms do. All + * exception-causing parts are losslessly recorded in a parallel + * folly::dynamic, available via releaseErrors() at the end of the parse. + * + * Suppose we fail to look up "key1" at the root, and hit a value error in + * "key2": {"subkey2": ...}. The error report will have the form: + * + * {"nested": { + * "key_errors": {"key1": "explanatory message"}, + * "value": <whole input>, + * "nested": { "key2": { "nested": { + * "subkey2": {"value": <original value>, "error": "message"} + * } } } + * }} + * + * Errors in array items are handled just the same, but using integer keys. + * + * The advantage of this approach is that your parsing can throw wherever, + * and DynamicParser isolates it, allowing the good parts to parse. + * + * Put another way, this makes it easy to implement a transformation that + * splits a `folly::dynamic` into a "parsed" part (which might be your + * struct meant for runtime use), and a matching "errors" part. As long as + * your successful parses are lossless, you can always reconstruct the + * original input from the parse output and the recorded "errors". + * + * == Limitations == + * + * - The input dynamic should be an object or array. wrapError() could be + * exposed to allow parsing single scalars, but this would not be a + * significant usability improvement over try-catch. + * + * - Do NOT try to parse the same part of the input dynamic twice. You + * might report multiple value errors, which is currently unsupported. + * + * - optional() does not support defaulting. This is unavoidable, since + * DynamicParser does not dictate how you record parsed data. If your + * parse writes into an output struct, then it ought to be initialized at + * construction time. If your output is initialized to default values, + * then you need no "default" feature. If it is not initialized, you are + * in trouble anyway. Suppose your optional() parse hits an error. What + * does your output contain? + * - Uninitialized data :( + * - You rely on an optional() feature to fall back to parsing some + * default dynamic. Sadly, the default hits a parse error. Now what? + * Since there is no good way to default, DynamicParser leaves it out. + * + * == Future: un-parsed items == + * + * DynamicParser could support erroring on un-parsed items -- the parts of + * the folly::dynamic, which were never asked for. Here is an ok design: + * + * (i) At the start of parsing any value, the user may call: + * parser.recursivelyForbidUnparsed(); + * parser.recursivelyAllowUnparsed(); + * parser.locallyForbidUnparsed(); + * parser.locallyAllowUnparsed(); + * + * (ii) At the end of the parse, any unparsed items are dumped to "errors". + * For example, failing to parse index 1 out of ["v1", "v2", "v3"] yields: + * "nested": {1: {"unparsed": "v2"}} + * or perhaps more verbosely: + * "nested": {1: {"error": "unparsed value", "value": "v2"}} + * + * By default, unparsed items are allowed. Calling a "forbid" function after + * some keys have already been parsed is allowed to fail (this permits a + * lazy implementation, which has minimal overhead when "forbid" is not + * requested). + * + * == Future: multiple value errors == + * + * The present contract is that exactly one value error is reported per + * location in the input (multiple key lookup errors are, of course, + * supported). If the need arises, multiple value errors could easily be + * supported by replacing the "error" string with an "errors" array. + */ + +namespace detail { +// Why do DynamicParser error messages use folly::dynamic pseudo-JSON? +// Firstly, the input dynamic need not correspond to valid JSON. Secondly, +// wrapError() uses integer-keyed objects to report arrary-indexing errors. +std::string toPseudoJson(const folly::dynamic& d); +} // namespace detail + +/** + * With DynamicParser::OnError::THROW, reports the first error. + * It is forbidden to call releaseErrors() if you catch this. + */ +struct FOLLY_EXPORT DynamicParserParseError : public std::runtime_error { + explicit DynamicParserParseError(folly::dynamic error) + : std::runtime_error(folly::to<std::string>( + "DynamicParserParseError: ", + detail::toPseudoJson(error))), + error_(std::move(error)) {} + /** + * Structured just like releaseErrors(), but with only 1 error inside: + * {"nested": {"key1": {"nested": {"key2": {"error": "err", "value": 5}}}}} + * or: + * {"nested": {"key1": {"key_errors": {"key3": "err"}, "value": 7}}} + */ + const folly::dynamic& error() const { + return error_; + } + + private: + folly::dynamic error_; +}; + +/** + * When DynamicParser is used incorrectly, it will throw this exception + * instead of reporting an error via releaseErrors(). It is unsafe to call + * any parser methods after catching a LogicError. + */ +struct FOLLY_EXPORT DynamicParserLogicError : public std::logic_error { + template <typename... Args> + explicit DynamicParserLogicError(Args&&... args) + : std::logic_error(folly::to<std::string>(std::forward<Args>(args)...)) {} +}; + +class DynamicParser { + public: + enum class OnError { + // After parsing, releaseErrors() reports all parse errors. + // Throws DynamicParserLogicError on programmer errors. + RECORD, + // Throws DynamicParserParseError on the first parse error, or + // DynamicParserLogicError on programmer errors. + THROW, + }; + + // You MUST NOT destroy `d` before the parser. + DynamicParser(OnError on_error, const folly::dynamic* d) + : onError_(on_error), stack_(d) {} // Always access input through stack_ + + /** + * Once you finished the entire parse, returns a structured description of + * all parse errors (see top-of-file docblock). May ONLY be called once. + * May NOT be called if the parse threw any kind of exception. Returns an + * empty object for successful OnError::THROW parsers. + */ + folly::dynamic releaseErrors() { + return stack_.releaseErrors(); + } + + /** + * Error-wraps fn(auto-converted key & value) if d[key] is set. The + * top-of-file docblock explains the auto-conversion. + */ + template <typename Fn> + void optional(const folly::dynamic& key, Fn); + + // Like optional(), but reports an error if d[key] does not exist. + template <typename Fn> + void required(const folly::dynamic& key, Fn); + + /** + * Iterate over the current object's keys and values. Report each item's + * errors under its own key in a matching sub-object of "errors". + */ + template <typename Fn> + void objectItems(Fn); + + /** + * Like objectItems() -- arrays are treated identically to objects with + * integer keys from 0 to size() - 1. + */ + template <typename Fn> + void arrayItems(Fn); + + /** + * The key currently being parsed (integer if inside an array). Throws if + * called outside of a parser callback. + */ + inline const folly::dynamic& key() const { + return stack_.key(); + } + /** + * The value currently being parsed (initially, the input dynamic). + * Throws if parsing nullptr, or parsing after releaseErrors(). + */ + inline const folly::dynamic& value() const { + return stack_.value(); + } + + /** + * By default, DynamicParser's "nested" object coerces all keys to + * strings, whether from arrayItems() or from p.optional(some_int, ...), + * to allow errors be serialized to JSON. If you are parsing non-JSON + * dynamic objects with non-string keys, this is problematic. When set to + * true, "nested" objects will report integer keys for errors coming from + * inside arrays, or the original key type from inside values of objects. + */ + DynamicParser& setAllowNonStringKeyErrors(bool b) { + allowNonStringKeyErrors_ = b; + return *this; + } + + private: + /** + * If `fn` throws an exception, wrapError() catches it and inserts an + * enriched description into stack_.errors_. If lookup_key is non-null, + * reports a key lookup error in "key_errors", otherwise reportse a value + * error in "error". + * + * Not public because that would encourage users to report multiple errors + * per input part, which is currently unsupported. It does not currently + * seem like normal user code should need this. + */ + template <typename Fn> + void wrapError(const folly::dynamic* lookup_key, Fn); + + void reportError(const folly::dynamic* lookup_k, const std::exception& ex); + + template <typename Fn> + void parse(const folly::dynamic& key, const folly::dynamic& value, Fn fn); + + // All of the above business logic obtains the part of the folly::dynamic + // it is examining (and the location for reporting errors) via this class, + // which lets it correctly handle nesting. + struct ParserStack { + struct Pop { + explicit Pop(ParserStack* sp) + : key_(sp->key_), value_(sp->value_), stackPtr_(sp) {} + void operator()() noexcept; // ScopeGuard requires noexcept + private: + const folly::dynamic* key_; + const folly::dynamic* value_; + ParserStack* stackPtr_; + }; + struct PopGuard { + explicit PopGuard(ParserStack* sp) : pop_(in_place, sp) {} + ~PopGuard() { + pop_ && ((*pop_)(), true); + } + + private: + Optional<Pop> pop_; + }; + + explicit ParserStack(const folly::dynamic* input) + : value_(input), + errors_(folly::dynamic::object()), + subErrors_({&errors_}) {} + + // Not copiable or movable due to numerous internal pointers + ParserStack(const ParserStack&) = delete; + ParserStack& operator=(const ParserStack&) = delete; + ParserStack(ParserStack&&) = delete; + ParserStack& operator=(ParserStack&&) = delete; + + // Lets user code nest parser calls by recording current key+value and + // returning an RAII guard to restore the old one. `noexcept` since it + // is used unwrapped. + PopGuard push(const folly::dynamic& k, const folly::dynamic& v) noexcept; + + // Throws DynamicParserLogicError if used outside of a parsing function. + inline const folly::dynamic& key() const; + // Throws DynamicParserLogicError if used after releaseErrors(). + inline const folly::dynamic& value() const; + + // Lazily creates new "nested" sub-objects in errors_. + folly::dynamic& errors(bool allow_non_string_keys) noexcept; + + // The user invokes this at most once after the parse is done. + folly::dynamic releaseErrors(); + + // Invoked on error when using OnError::THROW. + [[noreturn]] void throwErrors(); + + private: + friend struct Pop; + + folly::dynamic releaseErrorsImpl(); // for releaseErrors() & throwErrors() + + // Null outside of a parsing function. + const folly::dynamic* key_{nullptr}; + // Null on errors: when the input was nullptr, or after releaseErrors(). + const folly::dynamic* value_; + + // An object containing some of these keys: + // "key_errors" -- {"key": "description of error looking up said key"} + // "error" -- why did we fail to parse this value? + // "value" -- a copy of the input causing the error, and + // "nested" -- {"key" or integer for arrays: <another errors_ object>} + // + // "nested" will contain identically structured objects with keys (array + // indices) identifying the origin of the errors. Of course, "input" + // would no longer refer to the whole input, but to a part. + folly::dynamic errors_; + // We only materialize errors_ sub-objects when needed. This stores keys + // for unmaterialized errors, from outermost to innermost. + std::vector<const folly::dynamic*> unmaterializedSubErrorKeys_; + // Materialized errors, from outermost to innermost + std::vector<folly::dynamic*> subErrors_; // Point into errors_ + }; + + OnError onError_; + ParserStack stack_; + bool allowNonStringKeyErrors_{false}; // See the setter's docblock. +}; + +} // namespace folly + +#include <folly/experimental/DynamicParser-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/experimental/EliasFanoCoding.h b/ios/Pods/Flipper-Folly/folly/experimental/EliasFanoCoding.h new file mode 100644 index 000000000..5a602f4f2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/EliasFanoCoding.h @@ -0,0 +1,841 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Philip Pronin (philipp@fb.com) + * + * Based on the paper by Sebastiano Vigna, + * "Quasi-succinct indices" (arxiv:1206.4300). + */ + +#pragma once + +#include <algorithm> +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/experimental/CodingDetail.h> +#include <folly/experimental/Instructions.h> +#include <folly/experimental/Select64.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Bits.h> +#include <glog/logging.h> + +#if !FOLLY_X64 +#error EliasFanoCoding.h requires x86_64 +#endif + +namespace folly { +namespace compression { + +static_assert(kIsLittleEndian, "EliasFanoCoding.h requires little endianness"); + +constexpr size_t kCacheLineSize = 64; + +template <class Pointer> +struct EliasFanoCompressedListBase { + EliasFanoCompressedListBase() = default; + + template <class OtherPointer> + EliasFanoCompressedListBase( + const EliasFanoCompressedListBase<OtherPointer>& other) + : size(other.size), + numLowerBits(other.numLowerBits), + upperSizeBytes(other.upperSizeBytes), + data(other.data), + skipPointers(reinterpret_cast<Pointer>(other.skipPointers)), + forwardPointers(reinterpret_cast<Pointer>(other.forwardPointers)), + lower(reinterpret_cast<Pointer>(other.lower)), + upper(reinterpret_cast<Pointer>(other.upper)) {} + + template <class T = Pointer> + auto free() -> decltype(::free(T(nullptr))) { + return ::free(data.data()); + } + + size_t size = 0; + uint8_t numLowerBits = 0; + size_t upperSizeBytes = 0; + + // WARNING: EliasFanoCompressedList has no ownership of data. The 7 bytes + // following the last byte should be readable if kUpperFirst = false, 8 bytes + // otherwise. + folly::Range<Pointer> data; + + Pointer skipPointers = nullptr; + Pointer forwardPointers = nullptr; + Pointer lower = nullptr; + Pointer upper = nullptr; +}; + +typedef EliasFanoCompressedListBase<const uint8_t*> EliasFanoCompressedList; +typedef EliasFanoCompressedListBase<uint8_t*> MutableEliasFanoCompressedList; + +template < + class Value, + // SkipValue must be wide enough to be able to represent the list length. + class SkipValue = uint64_t, + size_t kSkipQuantum = 0, // 0 = disabled + size_t kForwardQuantum = 0, // 0 = disabled + bool kUpperFirst = false> +struct EliasFanoEncoderV2 { + static_assert( + std::is_integral<Value>::value && std::is_unsigned<Value>::value, + "Value should be unsigned integral"); + + typedef EliasFanoCompressedList CompressedList; + typedef MutableEliasFanoCompressedList MutableCompressedList; + + typedef Value ValueType; + typedef SkipValue SkipValueType; + struct Layout; + + static constexpr size_t skipQuantum = kSkipQuantum; + static constexpr size_t forwardQuantum = kForwardQuantum; + + static uint8_t defaultNumLowerBits(size_t upperBound, size_t size) { + if (UNLIKELY(size == 0 || upperBound < size)) { + return 0; + } + // Result that should be returned is "floor(log(upperBound / size))". + // In order to avoid expensive division, we rely on + // "floor(a) - floor(b) - 1 <= floor(a - b) <= floor(a) - floor(b)". + // Assuming "candidate = floor(log(upperBound)) - floor(log(upperBound))", + // then result is either "candidate - 1" or "candidate". + auto candidate = folly::findLastSet(upperBound) - folly::findLastSet(size); + // NOTE: As size != 0, "candidate" is always < 64. + return (size > (upperBound >> candidate)) ? candidate - 1 : candidate; + } + + // Requires: input range (begin, end) is sorted (encoding + // crashes if it's not). + // WARNING: encode() mallocates EliasFanoCompressedList::data. As + // EliasFanoCompressedList has no ownership of it, you need to call + // free() explicitly. + template <class RandomAccessIterator> + static MutableCompressedList encode( + RandomAccessIterator begin, + RandomAccessIterator end) { + if (begin == end) { + return MutableCompressedList(); + } + EliasFanoEncoderV2 encoder(size_t(end - begin), *(end - 1)); + for (; begin != end; ++begin) { + encoder.add(*begin); + } + return encoder.finish(); + } + + explicit EliasFanoEncoderV2(const MutableCompressedList& result) + : lower_(result.lower), + upper_(result.upper), + skipPointers_(reinterpret_cast<SkipValueType*>(result.skipPointers)), + forwardPointers_( + reinterpret_cast<SkipValueType*>(result.forwardPointers)), + result_(result) { + std::fill(result.data.begin(), result.data.end(), '\0'); + } + + EliasFanoEncoderV2(size_t size, ValueType upperBound) + : EliasFanoEncoderV2( + Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {} + + void add(ValueType value) { + CHECK_LT(value, std::numeric_limits<ValueType>::max()); + CHECK_GE(value, lastValue_); + + const auto numLowerBits = result_.numLowerBits; + const ValueType upperBits = value >> numLowerBits; + + // Upper sequence consists of upperBits 0-bits and (size_ + 1) 1-bits. + const size_t pos = upperBits + size_; + upper_[pos / 8] |= 1U << (pos % 8); + // Append numLowerBits bits to lower sequence. + if (numLowerBits != 0) { + const ValueType lowerBits = value & ((ValueType(1) << numLowerBits) - 1); + writeBits56(lower_, size_ * numLowerBits, numLowerBits, lowerBits); + } + + fillSkipPointersUpTo(upperBits); + + if /* constexpr */ (forwardQuantum != 0) { + if ((size_ + 1) % forwardQuantum == 0) { + const auto k = size_ / forwardQuantum; + // Store the number of preceding 0-bits. + forwardPointers_[k] = upperBits; + } + } + + lastValue_ = value; + ++size_; + } + + const MutableCompressedList& finish() { + CHECK_EQ(size_, result_.size); + const ValueType upperBitsUniverse = + (8 * result_.upperSizeBytes - result_.size); + if (upperBitsUniverse > 0) { + // Populate skip pointers up to the universe upper bound. + fillSkipPointersUpTo(upperBitsUniverse - 1); + } + return result_; + } + + private: + void fillSkipPointersUpTo(ValueType fillBoundary) { + if /* constexpr */ (skipQuantum != 0) { + while ((skipPointersSize_ + 1) * skipQuantum <= fillBoundary) { + // Store the number of preceding 1-bits. + skipPointers_[skipPointersSize_++] = static_cast<SkipValueType>(size_); + } + } + } + // Writes value (with len up to 56 bits) to data starting at pos-th bit. + static void + writeBits56(unsigned char* data, size_t pos, uint8_t len, uint64_t value) { + DCHECK_LE(uint32_t(len), 56); + DCHECK_EQ(0, value & ~((uint64_t(1) << len) - 1)); + unsigned char* const ptr = data + (pos / 8); + uint64_t ptrv = folly::loadUnaligned<uint64_t>(ptr); + ptrv |= value << (pos % 8); + folly::storeUnaligned<uint64_t>(ptr, ptrv); + } + + unsigned char* lower_ = nullptr; + unsigned char* upper_ = nullptr; + SkipValueType* skipPointers_ = nullptr; + SkipValueType* forwardPointers_ = nullptr; + + ValueType lastValue_ = 0; + size_t size_ = 0; + size_t skipPointersSize_ = 0; + + MutableCompressedList result_; +}; + +template < + class Value, + class SkipValue, + size_t kSkipQuantum, + size_t kForwardQuantum, + bool kUpperFirst> +struct EliasFanoEncoderV2< + Value, + SkipValue, + kSkipQuantum, + kForwardQuantum, + kUpperFirst>::Layout { + static Layout fromUpperBoundAndSize(size_t upperBound, size_t size) { + // numLowerBits can be at most 56 because of detail::writeBits56. + const uint8_t numLowerBits = + std::min(defaultNumLowerBits(upperBound, size), uint8_t(56)); + // *** Upper bits. + // Upper bits are stored using unary delta encoding. + // For example, (3 5 5 9) will be encoded as 1000011001000_2. + const size_t upperSizeBits = + (upperBound >> numLowerBits) + // Number of 0-bits to be stored. + size; // 1-bits. + const size_t upper = (upperSizeBits + 7) / 8; + + // *** Validity checks. + // Shift by numLowerBits must be valid. + CHECK_LT(numLowerBits, 8 * sizeof(Value)); + CHECK_LT(size, std::numeric_limits<SkipValueType>::max()); + CHECK_LT( + upperBound >> numLowerBits, std::numeric_limits<SkipValueType>::max()); + + return fromInternalSizes(numLowerBits, upper, size); + } + + static Layout + fromInternalSizes(uint8_t numLowerBits, size_t upper, size_t size) { + Layout layout; + layout.size = size; + layout.numLowerBits = numLowerBits; + + layout.lower = (numLowerBits * size + 7) / 8; + layout.upper = upper; + + // *** Skip pointers. + // Store (1-indexed) position of every skipQuantum-th + // 0-bit in upper bits sequence. + if /* constexpr */ (skipQuantum != 0) { + // 8 * upper is used here instead of upperSizeBits, as that is + // more serialization-friendly way (upperSizeBits doesn't need + // to be known by this function, unlike upper). + + size_t numSkipPointers = (8 * upper - size) / skipQuantum; + layout.skipPointers = numSkipPointers * sizeof(SkipValueType); + } + + // *** Forward pointers. + // Store (1-indexed) position of every forwardQuantum-th + // 1-bit in upper bits sequence. + if /* constexpr */ (forwardQuantum != 0) { + size_t numForwardPointers = size / forwardQuantum; + layout.forwardPointers = numForwardPointers * sizeof(SkipValueType); + } + + return layout; + } + + size_t bytes() const { + return lower + upper + skipPointers + forwardPointers; + } + + template <class Range> + EliasFanoCompressedListBase<typename Range::iterator> openList( + Range& buf) const { + EliasFanoCompressedListBase<typename Range::iterator> result; + result.size = size; + result.numLowerBits = numLowerBits; + result.upperSizeBytes = upper; + result.data = buf.subpiece(0, bytes()); + + auto advance = [&](size_t n) { + auto begin = buf.data(); + buf.advance(n); + return begin; + }; + + result.skipPointers = advance(skipPointers); + result.forwardPointers = advance(forwardPointers); + if /* constexpr */ (kUpperFirst) { + result.upper = advance(upper); + result.lower = advance(lower); + } else { + result.lower = advance(lower); + result.upper = advance(upper); + } + + return result; + } + + MutableCompressedList allocList() const { + uint8_t* buf = nullptr; + // WARNING: Current read/write logic assumes that the 7 bytes + // following the upper bytes and the 8 bytes following the lower bytes + // sequences are readable (stored value doesn't matter and won't be + // changed), so we allocate additional 8 bytes, but do not include them in + // size of returned value. + if (size > 0) { + buf = static_cast<uint8_t*>(malloc(bytes() + 8)); + } + folly::MutableByteRange bufRange(buf, bytes()); + return openList(bufRange); + } + + size_t size = 0; + uint8_t numLowerBits = 0; + + // Sizes in bytes. + size_t lower = 0; + size_t upper = 0; + size_t skipPointers = 0; + size_t forwardPointers = 0; +}; + +namespace detail { + +template <class Encoder, class Instructions, class SizeType> +class UpperBitsReader : ForwardPointers<Encoder::forwardQuantum>, + SkipPointers<Encoder::skipQuantum> { + typedef typename Encoder::SkipValueType SkipValueType; + + public: + typedef typename Encoder::ValueType ValueType; + + explicit UpperBitsReader(const typename Encoder::CompressedList& list) + : ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers), + SkipPointers<Encoder::skipQuantum>(list.skipPointers), + start_(list.upper) { + reset(); + } + + void reset() { + // Pretend the bitvector is prefixed by a block of zeroes. + block_ = 0; + position_ = static_cast<SizeType>(-1); + outer_ = static_cast<OuterType>(-sizeof(block_t)); + value_ = 0; + } + + SizeType position() const { + return position_; + } + ValueType value() const { + return value_; + } + + ValueType previous() { + size_t inner; + block_t block; + getPreviousInfo(block, inner, outer_); + block_ = folly::loadUnaligned<block_t>(start_ + outer_); + block_ ^= block; + --position_; + return setValue(inner); + } + + ValueType next() { + // Skip to the first non-zero block. + while (block_ == 0) { + outer_ += sizeof(block_t); + block_ = folly::loadUnaligned<block_t>(start_ + outer_); + } + + ++position_; + size_t inner = Instructions::ctz(block_); + block_ = Instructions::blsr(block_); + + return setValue(inner); + } + + ValueType skip(SizeType n) { + DCHECK_GT(n, 0); + + position_ += n; // n 1-bits will be read. + + // Use forward pointer. + if (Encoder::forwardQuantum > 0 && n > Encoder::forwardQuantum) { + const size_t steps = position_ / Encoder::forwardQuantum; + const size_t dest = folly::loadUnaligned<SkipValueType>( + this->forwardPointers_ + (steps - 1) * sizeof(SkipValueType)); + + reposition(dest + steps * Encoder::forwardQuantum); + n = position_ + 1 - steps * Encoder::forwardQuantum; // n is > 0. + } + + size_t cnt; + // Find necessary block. + while ((cnt = Instructions::popcount(block_)) < n) { + n -= cnt; + outer_ += sizeof(block_t); + block_ = folly::loadUnaligned<block_t>(start_ + outer_); + } + + // Skip to the n-th one in the block. + DCHECK_GT(n, 0); + size_t inner = select64<Instructions>(block_, n - 1); + block_ &= (block_t(-1) << inner) << 1; + + return setValue(inner); + } + + // Skip to the first element that is >= v and located *after* the current + // one (so even if current value equals v, position will be increased by 1). + ValueType skipToNext(ValueType v) { + DCHECK_GE(v, value_); + + // Use skip pointer. + if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) { + const size_t steps = v / Encoder::skipQuantum; + const size_t dest = folly::loadUnaligned<SkipValueType>( + this->skipPointers_ + (steps - 1) * sizeof(SkipValueType)); + + reposition(dest + Encoder::skipQuantum * steps); + position_ = dest - 1; + + // Correct value_ will be set during the next() call at the end. + + // NOTE: Corresponding block of lower bits sequence may be + // prefetched here (via __builtin_prefetch), but experiments + // didn't show any significant improvements. + } + + // Skip by blocks. + size_t cnt; + size_t skip = v - (8 * outer_ - position_ - 1); + + constexpr size_t kBitsPerBlock = 8 * sizeof(block_t); + while ((cnt = Instructions::popcount(~block_)) < skip) { + skip -= cnt; + position_ += kBitsPerBlock - cnt; + outer_ += sizeof(block_t); + block_ = folly::loadUnaligned<block_t>(start_ + outer_); + } + + if (LIKELY(skip)) { + auto inner = select64<Instructions>(~block_, skip - 1); + position_ += inner - skip + 1; + block_ &= block_t(-1) << inner; + } + + next(); + return value_; + } + + /** + * Prepare to skip to `value`. This is a constant-time operation that will + * prefetch memory required for a `skipTo(value)` call. + * + * @return position of reader + */ + SizeType prepareSkipTo(ValueType v) const { + auto position = position_; + + if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) { + auto outer = outer_; + const size_t steps = v / Encoder::skipQuantum; + const size_t dest = folly::loadUnaligned<SkipValueType>( + this->skipPointers_ + (steps - 1) * sizeof(SkipValueType)); + + position = dest - 1; + outer = (dest + Encoder::skipQuantum * steps) / 8; + + // Prefetch up to the beginning of where we linear search. After that, + // hardware prefetching will outperform our own. In addition, this + // simplifies calculating what to prefetch as we don't have to calculate + // the entire destination address. Two cache lines are prefetched because + // this results in fewer cycles used (based on practical results) than + // one. However, three cache lines does not have any additional effect. + const auto addr = start_ + outer; + __builtin_prefetch(addr); + __builtin_prefetch(addr + kCacheLineSize); + } + + return position; + } + + ValueType previousValue() const { + block_t block; + size_t inner; + OuterType outer; + getPreviousInfo(block, inner, outer); + return static_cast<ValueType>(8 * outer + inner - (position_ - 1)); + } + + // Returns true if we're at the beginning of the list, or previousValue() != + // value(). + bool isAtBeginningOfRun() const { + DCHECK_NE(position(), static_cast<SizeType>(-1)); + if (position_ == 0) { + return true; + } + size_t bitPos = size_t(value_) + position_ - 1; + return (start_[bitPos / 8] & (1 << (bitPos % 8))) == 0; + } + + void setDone(SizeType endPos) { + position_ = endPos; + } + + private: + ValueType setValue(size_t inner) { + value_ = static_cast<ValueType>(8 * outer_ + inner - position_); + return value_; + } + + void reposition(SizeType dest) { + outer_ = dest / 8; + block_ = folly::loadUnaligned<block_t>(start_ + outer_); + block_ &= ~((block_t(1) << (dest % 8)) - 1); + } + + using block_t = uint64_t; + // The size in bytes of the upper bits is limited by n + universe / 8, + // so a type that can hold either sizes or values is sufficient. + using OuterType = typename std::common_type<ValueType, SizeType>::type; + + void getPreviousInfo(block_t& block, size_t& inner, OuterType& outer) const { + DCHECK_NE(position(), std::numeric_limits<SizeType>::max()); + DCHECK_GT(position(), 0); + + outer = outer_; + block = folly::loadUnaligned<block_t>(start_ + outer); + inner = size_t(value_) - 8 * outer_ + position_; + block &= (block_t(1) << inner) - 1; + while (UNLIKELY(block == 0)) { + DCHECK_GT(outer, 0); + outer -= std::min<OuterType>(sizeof(block_t), outer); + block = folly::loadUnaligned<block_t>(start_ + outer); + } + inner = 8 * sizeof(block_t) - 1 - Instructions::clz(block); + } + + const unsigned char* const start_; + block_t block_; + SizeType position_; // Index of current value (= #reads - 1). + OuterType outer_; // Outer offset: number of consumed bytes in upper. + ValueType value_; +}; + +} // namespace detail + +// If kUnchecked = true the caller must guarantee that all the operations return +// valid elements, i.e., they would never return false if checked. +// +// If the list length is known to be representable with a type narrower than the +// SkipValueType used in the format, the reader footprint can be reduced by +// passing the type as SizeType. +template < + class Encoder, + class Instructions = instructions::Default, + bool kUnchecked = false, + class SizeType = typename Encoder::SkipValueType> +class EliasFanoReader { + public: + typedef Encoder EncoderType; + typedef typename Encoder::ValueType ValueType; + + explicit EliasFanoReader(const typename Encoder::CompressedList& list) + : upper_(list), + lower_(list.lower), + size_(list.size), + numLowerBits_(list.numLowerBits) { + DCHECK(Instructions::supported()); + // To avoid extra branching during skipTo() while reading + // upper sequence we need to know the last element. + // If kUnchecked == true, we do not check that skipTo() is called + // within the bounds, so we can avoid initializing lastValue_. + if (kUnchecked || UNLIKELY(list.size == 0)) { + lastValue_ = 0; + return; + } + ValueType lastUpperValue = ValueType(8 * list.upperSizeBytes - size_); + auto it = list.upper + list.upperSizeBytes - 1; + DCHECK_NE(*it, 0); + lastUpperValue -= 8 - folly::findLastSet(*it); + lastValue_ = readLowerPart(size_ - 1) | (lastUpperValue << numLowerBits_); + } + + void reset() { + upper_.reset(); + value_ = kInvalidValue; + } + + bool previous() { + if (!kUnchecked && UNLIKELY(position() == 0)) { + reset(); + return false; + } + upper_.previous(); + value_ = + readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_); + return true; + } + + bool next() { + if (!kUnchecked && UNLIKELY(position() + 1 >= size_)) { + return setDone(); + } + upper_.next(); + value_ = + readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_); + return true; + } + + /** + * Advances by n elements. n = 0 is allowed and has no effect. Returns false + * if the end of the list is reached. + */ + bool skip(SizeType n) { + if (n == 0) { + return valid(); + } + + if (kUnchecked || LIKELY(position() + n < size_)) { + if (LIKELY(n < kLinearScanThreshold)) { + for (SizeType i = 0; i < n; ++i) { + upper_.next(); + } + } else { + upper_.skip(n); + } + value_ = + readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_); + return true; + } + + return setDone(); + } + + /** + * Skips to the first element >= value whose position is greater or equal to + * the current position. Requires that value >= value() (or that the reader is + * at position -1). Returns false if no such element exists. + */ + bool skipTo(ValueType value) { + if (value != kInvalidValue) { + DCHECK_GE(value + 1, value_ + 1); + } + + if (!kUnchecked && value > lastValue_) { + return setDone(); + } else if (value == value_) { + return true; + } + + ValueType upperValue = (value >> numLowerBits_); + ValueType upperSkip = upperValue - upper_.value(); + // The average density of ones in upper bits is 1/2. + // LIKELY here seems to make things worse, even for small skips. + if (upperSkip < 2 * kLinearScanThreshold) { + do { + upper_.next(); + } while (UNLIKELY(upper_.value() < upperValue)); + } else { + upper_.skipToNext(upperValue); + } + + iterateTo(value); + return true; + } + + /** + * Prepare to skip to `value` by prefetching appropriate memory in both the + * upper and lower bits. + */ + void prepareSkipTo(ValueType value) const { + // Also works when value_ == kInvalidValue. + if (value != kInvalidValue) { + DCHECK_GE(value + 1, value_ + 1); + } + + if ((!kUnchecked && value > lastValue_) || (value == value_)) { + return; + } + + // Do minimal computation required to prefetch address used in + // `readLowerPart()`. + ValueType upperValue = (value >> numLowerBits_); + const auto upperPosition = upper_.prepareSkipTo(upperValue); + const auto addr = lower_ + (upperPosition * numLowerBits_ / 8); + __builtin_prefetch(addr); + __builtin_prefetch(addr + kCacheLineSize); + } + + /** + * Jumps to the element at position n. The reader can be in any state. Returns + * false if n >= size(). + */ + bool jump(SizeType n) { + if (n + 1 < upper_.position() + 1) { // Also works if position() == -1. + reset(); + n += 1; // Initial position is -1. + } else { + n -= upper_.position(); + } + return skip(n); + } + + /** + * Jumps to the first element >= value. The reader can be in any + * state. Returns false if no such element exists. + * + * If all the values in the list can be assumed distinct, setting + * assumeDistinct = true can enable some optimizations. + */ + bool jumpTo(ValueType value, bool assumeDistinct = false) { + if (value == value_) { + if (assumeDistinct == true) { + return true; + } + + // We might be in the middle of a run, iterate backwards to the beginning. + auto valueLower = Instructions::bzhi(value_, numLowerBits_); + while (!upper_.isAtBeginningOfRun() && + readLowerPart(upper_.position() - 1) == valueLower) { + upper_.previous(); + } + return true; + } + + // We need to reset if we're not in the initial state and the jump is + // backwards. + if (position() != static_cast<SizeType>(-1) && + value < value_) { // If position() == size() value_ is kInvalidValue. + reset(); + } + return skipTo(value); + } + + ValueType lastValue() const { + CHECK(!kUnchecked); + return lastValue_; + } + + ValueType previousValue() const { + DCHECK_GT(position(), 0); + DCHECK_LT(position(), size()); + return readLowerPart(upper_.position() - 1) | + (upper_.previousValue() << numLowerBits_); + } + + SizeType size() const { + return size_; + } + + bool valid() const { + return position() < size(); // Also checks that position() != -1. + } + + SizeType position() const { + return upper_.position(); + } + ValueType value() const { + DCHECK(valid()); + return value_; + } + + private: + // Must hold kInvalidValue + 1 == 0. + constexpr static ValueType kInvalidValue = -1; + + bool setDone() { + value_ = kInvalidValue; + upper_.setDone(size_); + return false; + } + + ValueType readLowerPart(SizeType i) const { + DCHECK_LT(i, size_); + const size_t pos = i * numLowerBits_; + const unsigned char* ptr = lower_ + (pos / 8); + const uint64_t ptrv = folly::loadUnaligned<uint64_t>(ptr); + // This removes the branch in the fallback implementation of + // bzhi. The condition is verified at encoding time. + assume(numLowerBits_ < sizeof(ValueType) * 8); + return Instructions::bzhi(ptrv >> (pos % 8), numLowerBits_); + } + + void iterateTo(ValueType value) { + while (true) { + value_ = + readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_); + if (LIKELY(value_ >= value)) { + break; + } + upper_.next(); + } + } + + constexpr static size_t kLinearScanThreshold = 8; + + detail::UpperBitsReader<Encoder, Instructions, SizeType> upper_; + const uint8_t* lower_; + SizeType size_; + ValueType value_ = kInvalidValue; + ValueType lastValue_; + uint8_t numLowerBits_; +}; + +} // namespace compression +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/EnvUtil.h b/ios/Pods/Flipper-Folly/folly/experimental/EnvUtil.h new file mode 100644 index 000000000..4ae7e0741 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/EnvUtil.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <map> +#include <string> +#include <unordered_map> +#include <vector> + +#include <folly/CPortability.h> +#include <folly/Memory.h> + +namespace folly { +namespace experimental { + +// Class to model the process environment in idiomatic C++ +// +// Changes to the modeled environment do not change the process environment +// unless `setAsCurrentEnvironment()` is called. +struct EnvironmentState { + using EnvType = std::unordered_map<std::string, std::string>; + + // Returns an EnvironmentState containing a copy of the current process + // environment. Subsequent changes to the process environment do not + // alter the stored model. If the process environment is altered during the + // execution of this method the results are not defined. + // + // Throws MalformedEnvironment if the process environment cannot be modeled. + static EnvironmentState fromCurrentEnvironment(); + + // Returns an empty EnvironmentState + static EnvironmentState empty() { + return {}; + } + + explicit EnvironmentState(EnvType const& env) : env_(env) {} + explicit EnvironmentState(EnvType&& env) : env_(std::move(env)) {} + + // Get the model environment for querying. + EnvType const& operator*() const { + return env_; + } + EnvType const* operator->() const { + return &env_; + } + + // Get the model environment for mutation or querying. + EnvType& operator*() { + return env_; + } + EnvType* operator->() { + return &env_; + } + + // Update the process environment with the one in the stored model. + // Subsequent changes to the model do not alter the process environment. The + // state of the process environment during execution of this method is not + // defined. If the process environment is altered by another thread during the + // execution of this method the results are not defined. + void setAsCurrentEnvironment(); + + // Get a copy of the model environment in the form used by `folly::Subprocess` + std::vector<std::string> toVector() const; + + // Get a copy of the model environment in the form commonly used by C routines + // such as execve, execle, etc. Example usage: + // + // EnvironmentState forChild{}; + // ... manipulate `forChild` as needed ... + // execve("/bin/program",pArgs,forChild.toPointerArray().get()); + std::unique_ptr<char*, void (*)(char**)> toPointerArray() const; + + private: + EnvironmentState() {} + EnvType env_; +}; + +struct FOLLY_EXPORT MalformedEnvironment : std::runtime_error { + using std::runtime_error::runtime_error; +}; +} // namespace experimental + +namespace test { +// RAII class allowing scoped changes to the process environment. The +// environment state at the time of its construction is restored at the time +// of its destruction. +struct EnvVarSaver { + EnvVarSaver() + : state_(std::make_unique<experimental::EnvironmentState>( + experimental::EnvironmentState::fromCurrentEnvironment())) {} + + EnvVarSaver(EnvVarSaver&& other) noexcept : state_(std::move(other.state_)) {} + + EnvVarSaver& operator=(EnvVarSaver&& other) noexcept { + state_ = std::move(other.state_); + return *this; + } + + ~EnvVarSaver() { + if (state_) { + state_->setAsCurrentEnvironment(); + } + } + + private: + std::unique_ptr<experimental::EnvironmentState> state_; +}; +} // namespace test +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/EventCount.h b/ios/Pods/Flipper-Folly/folly/experimental/EventCount.h new file mode 100644 index 000000000..a133d659e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/EventCount.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <climits> +#include <thread> + +#include <glog/logging.h> + +#include <folly/Likely.h> +#include <folly/detail/Futex.h> +#include <folly/lang/Bits.h> +#include <folly/portability/SysTime.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +/** + * Event count: a condition variable for lock free algorithms. + * + * See http://www.1024cores.net/home/lock-free-algorithms/eventcounts for + * details. + * + * Event counts allow you to convert a non-blocking lock-free / wait-free + * algorithm into a blocking one, by isolating the blocking logic. You call + * prepareWait() before checking your condition and then either cancelWait() + * or wait() depending on whether the condition was true. When another + * thread makes the condition true, it must call notify() / notifyAll() just + * like a regular condition variable. + * + * If "<" denotes the happens-before relationship, consider 2 threads (T1 and + * T2) and 3 events: + * - E1: T1 returns from prepareWait + * - E2: T1 calls wait + * (obviously E1 < E2, intra-thread) + * - E3: T2 calls notifyAll + * + * If E1 < E3, then E2's wait will complete (and T1 will either wake up, + * or not block at all) + * + * This means that you can use an EventCount in the following manner: + * + * Waiter: + * if (!condition()) { // handle fast path first + * for (;;) { + * auto key = eventCount.prepareWait(); + * if (condition()) { + * eventCount.cancelWait(); + * break; + * } else { + * eventCount.wait(key); + * } + * } + * } + * + * (This pattern is encapsulated in await()) + * + * Poster: + * make_condition_true(); + * eventCount.notifyAll(); + * + * Note that, just like with regular condition variables, the waiter needs to + * be tolerant of spurious wakeups and needs to recheck the condition after + * being woken up. Also, as there is no mutual exclusion implied, "checking" + * the condition likely means attempting an operation on an underlying + * data structure (push into a lock-free queue, etc) and returning true on + * success and false on failure. + */ +class EventCount { + public: + EventCount() noexcept : val_(0) {} + + class Key { + friend class EventCount; + explicit Key(uint32_t e) noexcept : epoch_(e) {} + uint32_t epoch_; + }; + + void notify() noexcept; + void notifyAll() noexcept; + Key prepareWait() noexcept; + void cancelWait() noexcept; + void wait(Key key) noexcept; + + /** + * Wait for condition() to become true. Will clean up appropriately if + * condition() throws, and then rethrow. + */ + template <class Condition> + void await(Condition condition); + + private: + void doNotify(int n) noexcept; + EventCount(const EventCount&) = delete; + EventCount(EventCount&&) = delete; + EventCount& operator=(const EventCount&) = delete; + EventCount& operator=(EventCount&&) = delete; + + // This requires 64-bit + static_assert(sizeof(int) == 4, "bad platform"); + static_assert(sizeof(uint32_t) == 4, "bad platform"); + static_assert(sizeof(uint64_t) == 8, "bad platform"); + static_assert(sizeof(std::atomic<uint64_t>) == 8, "bad platform"); + static_assert(sizeof(detail::Futex<std::atomic>) == 4, "bad platform"); + + static constexpr size_t kEpochOffset = kIsLittleEndian ? 1 : 0; + + // val_ stores the epoch in the most significant 32 bits and the + // waiter count in the least significant 32 bits. + std::atomic<uint64_t> val_; + + static constexpr uint64_t kAddWaiter = uint64_t(1); + static constexpr uint64_t kSubWaiter = uint64_t(-1); + static constexpr size_t kEpochShift = 32; + static constexpr uint64_t kAddEpoch = uint64_t(1) << kEpochShift; + static constexpr uint64_t kWaiterMask = kAddEpoch - 1; +}; + +inline void EventCount::notify() noexcept { + doNotify(1); +} + +inline void EventCount::notifyAll() noexcept { + doNotify(INT_MAX); +} + +inline void EventCount::doNotify(int n) noexcept { + uint64_t prev = val_.fetch_add(kAddEpoch, std::memory_order_acq_rel); + if (UNLIKELY(prev & kWaiterMask)) { + detail::futexWake( + reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset, n); + } +} + +inline EventCount::Key EventCount::prepareWait() noexcept { + uint64_t prev = val_.fetch_add(kAddWaiter, std::memory_order_acq_rel); + return Key(prev >> kEpochShift); +} + +inline void EventCount::cancelWait() noexcept { + // memory_order_relaxed would suffice for correctness, but the faster + // #waiters gets to 0, the less likely it is that we'll do spurious wakeups + // (and thus system calls). + uint64_t prev = val_.fetch_add(kSubWaiter, std::memory_order_seq_cst); + DCHECK_NE((prev & kWaiterMask), 0); +} + +inline void EventCount::wait(Key key) noexcept { + while ((val_.load(std::memory_order_acquire) >> kEpochShift) == key.epoch_) { + detail::futexWait( + reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset, + key.epoch_); + } + // memory_order_relaxed would suffice for correctness, but the faster + // #waiters gets to 0, the less likely it is that we'll do spurious wakeups + // (and thus system calls) + uint64_t prev = val_.fetch_add(kSubWaiter, std::memory_order_seq_cst); + DCHECK_NE((prev & kWaiterMask), 0); +} + +template <class Condition> +void EventCount::await(Condition condition) { + if (condition()) { + return; // fast path + } + + // condition() is the only thing that may throw, everything else is + // noexcept, so we can hoist the try/catch block outside of the loop + try { + for (;;) { + auto key = prepareWait(); + if (condition()) { + cancelWait(); + break; + } else { + wait(key); + } + } + } catch (...) { + cancelWait(); + throw; + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/ExecutionObserver.h b/ios/Pods/Flipper-Folly/folly/experimental/ExecutionObserver.h new file mode 100644 index 000000000..ca5ec5f58 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/ExecutionObserver.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +namespace folly { + +/** + * Observes the execution of a task. + */ +class ExecutionObserver { + public: + virtual ~ExecutionObserver() {} + + /** + * Called when a task is about to start executing. + * + * @param id Unique id for the task which is starting. + */ + virtual void starting(uintptr_t id) noexcept = 0; + + /** + * Called when a task is ready to run. + * + * @param id Unique id for the task which is ready to run. + */ + virtual void runnable(uintptr_t id) noexcept = 0; + + /** + * Called just after a task stops executing. + * + * @param id Unique id for the task which stopped. + */ + virtual void stopped(uintptr_t id) noexcept = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h b/ios/Pods/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h new file mode 100644 index 000000000..f999a8061 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h @@ -0,0 +1,430 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <chrono> +#include <memory> +#include <mutex> +#include <queue> + +#include <folly/Optional.h> +#include <folly/detail/Futex.h> +#include <folly/experimental/flat_combining/FlatCombining.h> +#include <glog/logging.h> + +namespace folly { + +/// Thread-safe priority queue based on flat combining. If the +/// constructor parameter maxSize is greater than 0 (default = 0), +/// then the queue is bounded. This template provides blocking, +/// non-blocking, and timed variants of each of push(), pop(), and +/// peek() operations. The empty() and size() functions are inherently +/// non-blocking. +/// +/// PriorityQueue must support the interface of std::priority_queue, +/// specifically empty(), size(), push(), top(), and pop(). Mutex +/// must meet the standard Lockable requirements. +/// +/// By default FlatCombining uses a dedicated combiner thread, which +/// yields better latency and throughput under high contention but +/// higher overheads under low contention. If the constructor +/// parameter dedicated is false, then there will be no dedicated +/// combiner thread and any requester may do combining of operations +/// requested by other threads. For more details see the comments for +/// FlatCombining. +/// +/// Usage examples: +/// @code +/// FlatCombiningPriorityQueue<int> pq(1); +/// CHECK(pq.empty()); +/// CHECK(pq.size() == 0); +/// int v; +/// CHECK(!try_pop(v)); +/// CHECK(!try_pop_until(v, now() + seconds(1))); +/// CHECK(!try_peek(v)); +/// CHECK(!try_peek_until(v, now() + seconds(1))); +/// pq.push(10); +/// CHECK(!pq.empty()); +/// CHECK(pq.size() == 1); +/// CHECK(!pq.try_push(20)); +/// CHECK(!pq.try_push_until(20), now() + seconds(1))); +/// peek(v); +/// CHECK_EQ(v, 10); +/// CHECK(pq.size() == 1); +/// pop(v); +/// CHECK_EQ(v, 10); +/// CHECK(pq.empty()); +/// @encode + +template < + typename T, + typename PriorityQueue = std::priority_queue<T>, + typename Mutex = std::mutex, + template <typename> class Atom = std::atomic> +class FlatCombiningPriorityQueue + : public folly::FlatCombining< + FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>, + Mutex, + Atom> { + using FCPQ = FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>; + using FC = folly::FlatCombining<FCPQ, Mutex, Atom>; + + public: + template < + typename... PQArgs, + typename = decltype(PriorityQueue(std::declval<PQArgs>()...))> + explicit FlatCombiningPriorityQueue( + // Concurrent priority queue parameter + const size_t maxSize = 0, + // Flat combining parameters + const bool dedicated = true, + const uint32_t numRecs = 0, + const uint32_t maxOps = 0, + // (Sequential) PriorityQueue Parameters + PQArgs... args) + : FC(dedicated, numRecs, maxOps), + maxSize_(maxSize), + pq_(std::forward<PQArgs>(args)...) {} + + /// Returns true iff the priority queue is empty + bool empty() const { + bool res; + auto fn = [&] { res = pq_.empty(); }; + const_cast<FCPQ*>(this)->requestFC(fn); + return res; + } + + /// Returns the number of items in the priority queue + size_t size() const { + size_t res; + auto fn = [&] { res = pq_.size(); }; + const_cast<FCPQ*>(this)->requestFC(fn); + return res; + } + + /// Non-blocking push. Succeeds if there is space in the priority + /// queue to insert the new item. Tries once if no time point is + /// provided or until the provided time_point is reached. If + /// successful, inserts the provided item in the priority queue + /// according to its priority. + bool try_push(const T& val) { + return try_push_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::min()); + } + + /// Non-blocking pop. Succeeds if the priority queue is + /// nonempty. Tries once if no time point is provided or until the + /// provided time_point is reached. If successful, copies the + /// highest priority item and removes it from the priority queue. + bool try_pop(T& val) { + return try_pop_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::min()); + } + + /// Non-blocking peek. Succeeds if the priority queue is + /// nonempty. Tries once if no time point is provided or until the + /// provided time_point is reached. If successful, copies the + /// highest priority item without removing it. + bool try_peek(T& val) { + return try_peek_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::min()); + } + + /// Blocking push. Inserts the provided item in the priority + /// queue. If it is full, this function blocks until there is space + /// for the new item. + void push(const T& val) { + try_push_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::max()); + } + + /// Blocking pop. Copies the highest priority item and removes + /// it. If the priority queue is empty, this function blocks until + /// it is nonempty. + void pop(T& val) { + try_pop_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::max()); + } + + /// Blocking peek. Copies the highest priority item without + /// removing it. If the priority queue is empty, this function + /// blocks until it is nonempty. + void peek(T& val) { + try_peek_impl( + val, std::chrono::time_point<std::chrono::steady_clock>::max()); + } + + folly::Optional<T> try_pop() { + T val; + if (try_pop(val)) { + return std::move(val); + } + return folly::none; + } + + folly::Optional<T> try_peek() { + T val; + if (try_peek(val)) { + return std::move(val); + } + return folly::none; + } + + template <typename Rep, typename Period> + folly::Optional<T> try_pop_for( + const std::chrono::duration<Rep, Period>& timeout) { + T val; + if (try_pop(val) || + try_pop_impl(val, std::chrono::steady_clock::now() + timeout)) { + return std::move(val); + } + return folly::none; + } + + template <typename Rep, typename Period> + bool try_push_for( + const T& val, + const std::chrono::duration<Rep, Period>& timeout) { + return ( + try_push(val) || + try_push_impl(val, std::chrono::steady_clock::now() + timeout)); + } + + template <typename Rep, typename Period> + folly::Optional<T> try_peek_for( + const std::chrono::duration<Rep, Period>& timeout) { + T val; + if (try_peek(val) || + try_peek_impl(val, std::chrono::steady_clock::now() + timeout)) { + return std::move(val); + } + return folly::none; + } + + template <typename Clock, typename Duration> + folly::Optional<T> try_pop_until( + const std::chrono::time_point<Clock, Duration>& deadline) { + T val; + if (try_pop_impl(val, deadline)) { + return std::move(val); + } + return folly::none; + } + + template <typename Clock, typename Duration> + bool try_push_until( + const T& val, + const std::chrono::time_point<Clock, Duration>& deadline) { + return try_push_impl(val, deadline); + } + + template <typename Clock, typename Duration> + folly::Optional<T> try_peek_until( + const std::chrono::time_point<Clock, Duration>& deadline) { + T val; + if (try_peek_impl(val, deadline)) { + return std::move(val); + } + return folly::none; + } + + private: + size_t maxSize_; + PriorityQueue pq_; + detail::Futex<Atom> empty_{}; + detail::Futex<Atom> full_{}; + + bool isTrue(detail::Futex<Atom>& futex) { + return futex.load(std::memory_order_relaxed) != 0; + } + + void setFutex(detail::Futex<Atom>& futex, uint32_t val) { + futex.store(val, std::memory_order_relaxed); + } + + bool futexSignal(detail::Futex<Atom>& futex) { + if (isTrue(futex)) { + setFutex(futex, 0); + return true; + } else { + return false; + } + } + + template <typename Clock, typename Duration> + bool try_push_impl( + const T& val, + const std::chrono::time_point<Clock, Duration>& when); + + template <typename Clock, typename Duration> + bool try_pop_impl( + T& val, + const std::chrono::time_point<Clock, Duration>& when); + + template <typename Clock, typename Duration> + bool try_peek_impl( + T& val, + const std::chrono::time_point<Clock, Duration>& when); +}; + +/// Implementation + +template < + typename T, + typename PriorityQueue, + typename Mutex, + template <typename> class Atom> +template <typename Clock, typename Duration> +inline bool +FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_push_impl( + const T& val, + const std::chrono::time_point<Clock, Duration>& when) { + while (true) { + bool res; + bool wake; + + auto fn = [&] { + if (maxSize_ > 0 && pq_.size() == maxSize_) { + setFutex(full_, 1); + res = false; + return; + } + DCHECK(maxSize_ == 0 || pq_.size() < maxSize_); + try { + pq_.push(val); + wake = futexSignal(empty_); + res = true; + return; + } catch (const std::bad_alloc&) { + setFutex(full_, 1); + res = false; + return; + } + }; + this->requestFC(fn); + + if (res) { + if (wake) { + detail::futexWake(&empty_); + } + return true; + } + if (when == std::chrono::time_point<Clock>::min()) { + return false; + } + while (isTrue(full_)) { + if (when == std::chrono::time_point<Clock>::max()) { + detail::futexWait(&full_, 1); + } else { + if (Clock::now() > when) { + return false; + } else { + detail::futexWaitUntil(&full_, 1, when); + } + } + } // inner while loop + } // outer while loop +} + +template < + typename T, + typename PriorityQueue, + typename Mutex, + template <typename> class Atom> +template <typename Clock, typename Duration> +inline bool +FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_pop_impl( + T& val, + const std::chrono::time_point<Clock, Duration>& when) { + while (true) { + bool res; + bool wake; + + auto fn = [&] { + res = !pq_.empty(); + if (res) { + val = pq_.top(); + pq_.pop(); + wake = futexSignal(full_); + } else { + setFutex(empty_, 1); + } + }; + this->requestFC(fn); + + if (res) { + if (wake) { + detail::futexWake(&full_); + } + return true; + } + while (isTrue(empty_)) { + if (when == std::chrono::time_point<Clock>::max()) { + detail::futexWait(&empty_, 1); + } else { + if (Clock::now() > when) { + return false; + } else { + detail::futexWaitUntil(&empty_, 1, when); + } + } + } // inner while loop + } // outer while loop +} + +template < + typename T, + typename PriorityQueue, + typename Mutex, + template <typename> class Atom> +template <typename Clock, typename Duration> +inline bool +FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_peek_impl( + T& val, + const std::chrono::time_point<Clock, Duration>& when) { + while (true) { + bool res; + + auto fn = [&] { + res = !pq_.empty(); + if (res) { + val = pq_.top(); + } else { + setFutex(empty_, 1); + } + }; + this->requestFC(fn); + + if (res) { + return true; + } + while (isTrue(empty_)) { + if (when == std::chrono::time_point<Clock>::max()) { + detail::futexWait(&empty_, 1); + } else { + if (Clock::now() > when) { + return false; + } else { + detail::futexWaitUntil(&empty_, 1, when); + } + } + } // inner while loop + } // outer while loop +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/FunctionScheduler.h b/ios/Pods/Flipper-Folly/folly/experimental/FunctionScheduler.h new file mode 100644 index 000000000..06588e9ee --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/FunctionScheduler.h @@ -0,0 +1,381 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Function.h> +#include <folly/Range.h> +#include <folly/hash/Hash.h> +#include <chrono> +#include <condition_variable> +#include <mutex> +#include <thread> +#include <unordered_map> +#include <vector> + +namespace folly { + +/** + * Schedules any number of functions to run at various intervals. E.g., + * + * FunctionScheduler fs; + * + * fs.addFunction([&] { LOG(INFO) << "tick..."; }, seconds(1), "ticker"); + * fs.addFunction(std::bind(&TestClass::doStuff, this), minutes(5), "stuff"); + * fs.start(); + * ........ + * fs.cancelFunction("ticker"); + * fs.addFunction([&] { LOG(INFO) << "tock..."; }, minutes(3), "tocker"); + * ........ + * fs.shutdown(); + * + * + * Note: the class uses only one thread - if you want to use more than one + * thread, either use multiple FunctionScheduler objects, or check out + * ThreadedRepeatingFunctionRunner.h for a much simpler contract of + * "run each function periodically in its own thread". + * + * start() schedules the functions, while shutdown() terminates further + * scheduling. + */ +class FunctionScheduler { + public: + FunctionScheduler(); + ~FunctionScheduler(); + + /** + * By default steady is false, meaning schedules may lag behind overtime. + * This could be due to long running tasks or time drift because of randomness + * in thread wakeup time. + * By setting steady to true, FunctionScheduler will attempt to catch up. + * i.e. more like a cronjob + * + * NOTE: it's only safe to set this before calling start() + */ + void setSteady(bool steady) { + steady_ = steady; + } + + /* + * Parameters to control the function interval. + * + * If isPoisson is true, then use std::poisson_distribution to pick the + * interval between each invocation of the function. + * + * If isPoisson is false, then always use the fixed interval specified to + * addFunction(). + */ + struct LatencyDistribution { + bool isPoisson; + double poissonMean; + + LatencyDistribution(bool poisson, double mean) + : isPoisson(poisson), poissonMean(mean) {} + }; + + /** + * Adds a new function to the FunctionScheduler. + * + * Functions will not be run until start() is called. When start() is + * called, each function will be run after its specified startDelay. + * Functions may also be added after start() has been called, in which case + * startDelay is still honored. + * + * Throws an exception on error. In particular, each function must have a + * unique name--two functions cannot be added with the same name. + */ + void addFunction( + Function<void()>&& cb, + std::chrono::milliseconds interval, + StringPiece nameID = StringPiece(), + std::chrono::milliseconds startDelay = std::chrono::milliseconds(0)); + + /* + * Add a new function to the FunctionScheduler with a specified + * LatencyDistribution + */ + void addFunction( + Function<void()>&& cb, + std::chrono::milliseconds interval, + const LatencyDistribution& latencyDistr, + StringPiece nameID = StringPiece(), + std::chrono::milliseconds startDelay = std::chrono::milliseconds(0)); + + /** + * Adds a new function to the FunctionScheduler to run only once. + */ + void addFunctionOnce( + Function<void()>&& cb, + StringPiece nameID = StringPiece(), + std::chrono::milliseconds startDelay = std::chrono::milliseconds(0)); + + /** + * Add a new function to the FunctionScheduler with the time + * interval being distributed uniformly within the given interval + * [minInterval, maxInterval]. + */ + void addFunctionUniformDistribution( + Function<void()>&& cb, + std::chrono::milliseconds minInterval, + std::chrono::milliseconds maxInterval, + StringPiece nameID, + std::chrono::milliseconds startDelay); + + /** + * Add a new function to the FunctionScheduler whose start times are attempted + * to be scheduled so that they are congruent modulo the interval. + * Note: The scheduling of the next run time happens right before the function + * invocation, so the first time a function takes more time than the interval, + * it will be reinvoked immediately. + */ + void addFunctionConsistentDelay( + Function<void()>&& cb, + std::chrono::milliseconds interval, + StringPiece nameID = StringPiece(), + std::chrono::milliseconds startDelay = std::chrono::milliseconds(0)); + + /** + * A type alias for function that is called to determine the time + * interval for the next scheduled run. + */ + using IntervalDistributionFunc = Function<std::chrono::milliseconds()>; + /** + * A type alias for function that returns the next run time, given the current + * run time and the current start time. + */ + using NextRunTimeFunc = Function<std::chrono::steady_clock::time_point( + std::chrono::steady_clock::time_point, + std::chrono::steady_clock::time_point)>; + + /** + * Add a new function to the FunctionScheduler. The scheduling interval + * is determined by the interval distribution functor, which is called + * every time the next function execution is scheduled. This allows + * for supporting custom interval distribution algorithms in addition + * to built in constant interval; and Poisson and jitter distributions + * (@see FunctionScheduler::addFunction and + * @see FunctionScheduler::addFunctionJitterInterval). + */ + void addFunctionGenericDistribution( + Function<void()>&& cb, + IntervalDistributionFunc&& intervalFunc, + const std::string& nameID, + const std::string& intervalDescr, + std::chrono::milliseconds startDelay); + + /** + * Like addFunctionGenericDistribution, adds a new function to the + * FunctionScheduler, but the next run time is determined directly by the + * given functor, rather than by adding an interval. + */ + void addFunctionGenericNextRunTimeFunctor( + Function<void()>&& cb, + NextRunTimeFunc&& fn, + const std::string& nameID, + const std::string& intervalDescr, + std::chrono::milliseconds startDelay); + + /** + * Cancels the function with the specified name, so it will no longer be run. + * + * Returns false if no function exists with the specified name. + */ + bool cancelFunction(StringPiece nameID); + bool cancelFunctionAndWait(StringPiece nameID); + + /** + * All functions registered will be canceled. + */ + void cancelAllFunctions(); + void cancelAllFunctionsAndWait(); + + /** + * Resets the specified function's timer. + * When resetFunctionTimer is called, the specified function's timer will + * be reset with the same parameters it was passed initially, including + * its startDelay. If the startDelay was 0, the function will be invoked + * immediately. + * + * Returns false if no function exists with the specified name. + */ + bool resetFunctionTimer(StringPiece nameID); + + /** + * Starts the scheduler. + * + * Returns false if the scheduler was already running. + */ + bool start(); + + /** + * Stops the FunctionScheduler. + * + * It may be restarted later by calling start() again. + * Returns false if the scheduler was not running. + */ + bool shutdown(); + + /** + * Set the name of the worker thread. + */ + void setThreadName(StringPiece threadName); + + private: + struct RepeatFunc { + Function<void()> cb; + NextRunTimeFunc nextRunTimeFunc; + std::chrono::steady_clock::time_point nextRunTime; + std::string name; + std::chrono::milliseconds startDelay; + std::string intervalDescr; + bool runOnce; + + RepeatFunc( + Function<void()>&& cback, + IntervalDistributionFunc&& intervalFn, + const std::string& nameID, + const std::string& intervalDistDescription, + std::chrono::milliseconds delay, + bool once) + : RepeatFunc( + std::move(cback), + getNextRunTimeFunc(std::move(intervalFn)), + nameID, + intervalDistDescription, + delay, + once) {} + + RepeatFunc( + Function<void()>&& cback, + NextRunTimeFunc&& nextRunTimeFn, + const std::string& nameID, + const std::string& intervalDistDescription, + std::chrono::milliseconds delay, + bool once) + : cb(std::move(cback)), + nextRunTimeFunc(std::move(nextRunTimeFn)), + nextRunTime(), + name(nameID), + startDelay(delay), + intervalDescr(intervalDistDescription), + runOnce(once) {} + + static NextRunTimeFunc getNextRunTimeFunc( + IntervalDistributionFunc&& intervalFn) { + return [intervalFn = std::move(intervalFn)]( + std::chrono::steady_clock::time_point /* curNextRunTime */, + std::chrono::steady_clock::time_point curTime) mutable { + return curTime + intervalFn(); + }; + } + + std::chrono::steady_clock::time_point getNextRunTime() const { + return nextRunTime; + } + void setNextRunTimeStrict(std::chrono::steady_clock::time_point curTime) { + nextRunTime = nextRunTimeFunc(nextRunTime, curTime); + } + void setNextRunTimeSteady() { + nextRunTime = nextRunTimeFunc(nextRunTime, nextRunTime); + } + void resetNextRunTime(std::chrono::steady_clock::time_point curTime) { + nextRunTime = curTime + startDelay; + } + void cancel() { + // Simply reset cb to an empty function. + cb = {}; + } + bool isValid() const { + return bool(cb); + } + }; + + struct RunTimeOrder { + bool operator()( + const std::unique_ptr<RepeatFunc>& f1, + const std::unique_ptr<RepeatFunc>& f2) const { + return f1->getNextRunTime() > f2->getNextRunTime(); + } + }; + + typedef std::vector<std::unique_ptr<RepeatFunc>> FunctionHeap; + typedef std::unordered_map<StringPiece, RepeatFunc*, Hash> FunctionMap; + + void run(); + void runOneFunction( + std::unique_lock<std::mutex>& lock, + std::chrono::steady_clock::time_point now); + void cancelFunction(const std::unique_lock<std::mutex>& lock, RepeatFunc* it); + void addFunctionToHeap( + const std::unique_lock<std::mutex>& lock, + std::unique_ptr<RepeatFunc> func); + + template <typename RepeatFuncNextRunTimeFunc> + void addFunctionToHeapChecked( + Function<void()>&& cb, + RepeatFuncNextRunTimeFunc&& fn, + const std::string& nameID, + const std::string& intervalDescr, + std::chrono::milliseconds startDelay, + bool runOnce); + + void addFunctionInternal( + Function<void()>&& cb, + NextRunTimeFunc&& fn, + const std::string& nameID, + const std::string& intervalDescr, + std::chrono::milliseconds startDelay, + bool runOnce); + void addFunctionInternal( + Function<void()>&& cb, + IntervalDistributionFunc&& fn, + const std::string& nameID, + const std::string& intervalDescr, + std::chrono::milliseconds startDelay, + bool runOnce); + + // Return true if the current function is being canceled + bool cancelAllFunctionsWithLock(std::unique_lock<std::mutex>& lock); + bool cancelFunctionWithLock( + std::unique_lock<std::mutex>& lock, + StringPiece nameID); + + std::thread thread_; + + // Mutex to protect our member variables. + std::mutex mutex_; + bool running_{false}; + + // The functions to run. + // This is a heap, ordered by next run time. + FunctionHeap functions_; + FunctionMap functionsMap_; + RunTimeOrder fnCmp_; + + // The function currently being invoked by the running thread. + // This is null when the running thread is idle + RepeatFunc* currentFunction_{nullptr}; + + // Condition variable that is signalled whenever a new function is added + // or when the FunctionScheduler is stopped. + std::condition_variable runningCondvar_; + + std::string threadName_; + bool steady_{false}; + bool cancellingCurrentFunction_{false}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/FutureDAG.h b/ios/Pods/Flipper-Folly/folly/experimental/FutureDAG.h new file mode 100644 index 000000000..7e1cb206f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/FutureDAG.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Executor.h> +#include <folly/futures/Future.h> +#include <folly/futures/SharedPromise.h> + +namespace folly { + +class FutureDAG : public std::enable_shared_from_this<FutureDAG> { + public: + static std::shared_ptr<FutureDAG> create( + Executor::KeepAlive<> defaultExecutor) { + return std::shared_ptr<FutureDAG>( + new FutureDAG(std::move(defaultExecutor))); + } + + typedef size_t Handle; + typedef std::function<Future<Unit>()> FutureFunc; + + Handle add(FutureFunc func, Executor::KeepAlive<> executor) { + nodes.emplace_back(std::move(func), executor); + return nodes.size() - 1; + } + + void remove(Handle a) { + if (a >= nodes.size()) { + return; + } + + if (nodes[a].hasDependents) { + for (auto& node : nodes) { + auto& deps = node.dependencies; + deps.erase( + std::remove(std::begin(deps), std::end(deps), a), std::end(deps)); + for (Handle& handle : deps) { + if (handle > a) { + handle--; + } + } + } + } + + nodes.erase(nodes.begin() + a); + } + + void reset() { + // Delete all but source node, and reset dependency properties + Handle source_node; + std::unordered_set<Handle> memo; + for (auto& node : nodes) { + for (Handle handle : node.dependencies) { + memo.insert(handle); + } + } + for (Handle handle = 0; handle < nodes.size(); handle++) { + if (memo.find(handle) == memo.end()) { + source_node = handle; + } + } + + nodes.erase(nodes.begin(), nodes.begin() + source_node); + nodes.erase(nodes.begin() + 1, nodes.end()); + nodes[0].hasDependents = false; + nodes[0].dependencies.clear(); + } + + void dependency(Handle a, Handle b) { + nodes[b].dependencies.push_back(a); + nodes[a].hasDependents = true; + } + + void clean_state(Handle source, Handle sink) { + for (auto handle : nodes[sink].dependencies) { + nodes[handle].hasDependents = false; + } + nodes[0].hasDependents = false; + remove(source); + remove(sink); + } + + Future<Unit> go() { + if (hasCycle()) { + return makeFuture<Unit>(std::runtime_error("Cycle in FutureDAG graph")); + } + std::vector<Handle> rootNodes; + std::vector<Handle> leafNodes; + for (Handle handle = 0; handle < nodes.size(); handle++) { + if (nodes[handle].dependencies.empty()) { + rootNodes.push_back(handle); + } + if (!nodes[handle].hasDependents) { + leafNodes.push_back(handle); + } + } + + auto sinkHandle = add([] { return Future<Unit>(); }, defaultExecutor_); + for (auto handle : leafNodes) { + dependency(handle, sinkHandle); + } + + auto sourceHandle = add(nullptr, defaultExecutor_); + for (auto handle : rootNodes) { + dependency(sourceHandle, handle); + } + + for (Handle handle = 0; handle < nodes.size() - 1; handle++) { + std::vector<Future<Unit>> dependencies; + for (auto depHandle : nodes[handle].dependencies) { + dependencies.push_back(nodes[depHandle].promise.getFuture()); + } + + collect(dependencies) + .via(nodes[handle].executor) + .thenValue([this, handle](std::vector<Unit>&&) { + nodes[handle].func().then([this, handle](Try<Unit>&& t) { + nodes[handle].promise.setTry(std::move(t)); + }); + }) + .thenError([this, handle](exception_wrapper ew) { + nodes[handle].promise.setException(std::move(ew)); + }); + } + + nodes[sourceHandle].promise.setValue(); + return nodes[sinkHandle].promise.getFuture().thenValue( + [that = shared_from_this(), sourceHandle, sinkHandle](Unit) { + that->clean_state(sourceHandle, sinkHandle); + }); + } + + private: + FutureDAG(Executor::KeepAlive<> defaultExecutor) + : defaultExecutor_{std::move(defaultExecutor)} {} + + bool hasCycle() { + // Perform a modified topological sort to detect cycles + std::vector<std::vector<Handle>> dependencies; + for (auto& node : nodes) { + dependencies.push_back(node.dependencies); + } + + std::vector<size_t> dependents(nodes.size()); + for (auto& dependencyEdges : dependencies) { + for (auto handle : dependencyEdges) { + dependents[handle]++; + } + } + + std::vector<Handle> handles; + for (Handle handle = 0; handle < nodes.size(); handle++) { + if (!nodes[handle].hasDependents) { + handles.push_back(handle); + } + } + + while (!handles.empty()) { + auto handle = handles.back(); + handles.pop_back(); + while (!dependencies[handle].empty()) { + auto dependency = dependencies[handle].back(); + dependencies[handle].pop_back(); + if (--dependents[dependency] == 0) { + handles.push_back(dependency); + } + } + } + + for (auto& dependencyEdges : dependencies) { + if (!dependencyEdges.empty()) { + return true; + } + } + + return false; + } + + struct Node { + Node(FutureFunc&& funcArg, Executor::KeepAlive<> executorArg) + : func(std::move(funcArg)), executor(std::move(executorArg)) {} + + FutureFunc func{nullptr}; + Executor::KeepAlive<> executor; + SharedPromise<Unit> promise; + std::vector<Handle> dependencies; + bool hasDependents{false}; + bool visited{false}; + }; + + std::vector<Node> nodes; + Executor::KeepAlive<> defaultExecutor_; +}; + +// Polymorphic functor implementation +template <typename T> +class FutureDAGFunctor { + public: + std::shared_ptr<FutureDAG> dag; + T state; + std::vector<T> dep_states; + T result() { + return state; + } + // execReset() runs DAG & clears all nodes except for source + void execReset() { + this->dag->go().get(); + this->dag->reset(); + } + void exec() { + this->dag->go().get(); + } + virtual void operator()() {} + explicit FutureDAGFunctor(T init_val, Executor::KeepAlive<> defaultExecutor) + : dag(FutureDAG::create(std::move(defaultExecutor))), state(init_val) {} + FutureDAGFunctor(Executor::KeepAlive<> defaultExecutor) + : dag(FutureDAG::create(std::move(defaultExecutor))), state() {} + virtual ~FutureDAGFunctor() {} +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/Instructions.h b/ios/Pods/Flipper-Folly/folly/experimental/Instructions.h new file mode 100644 index 000000000..8e3dde78f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/Instructions.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <glog/logging.h> + +#ifdef _MSC_VER +#include <immintrin.h> +#endif + +#include <folly/CpuId.h> +#include <folly/Portability.h> +#include <folly/lang/Assume.h> +#include <folly/portability/Builtins.h> + +namespace folly { +namespace compression { +namespace instructions { + +// NOTE: It's recommended to compile EF coding with -msse4.2, starting +// with Nehalem, Intel CPUs support POPCNT instruction and gcc will emit +// it for __builtin_popcountll intrinsic. +// But we provide an alternative way for the client code: it can switch to +// the appropriate version of EliasFanoReader<> at runtime (client should +// implement this switching logic itself) by specifying instruction set to +// use explicitly. + +struct Default { + static bool supported(const folly::CpuId& /* cpuId */ = {}) { + return true; + } + static FOLLY_ALWAYS_INLINE uint64_t popcount(uint64_t value) { + return uint64_t(__builtin_popcountll(value)); + } + static FOLLY_ALWAYS_INLINE int ctz(uint64_t value) { + DCHECK_GT(value, 0u); + return __builtin_ctzll(value); + } + static FOLLY_ALWAYS_INLINE int clz(uint64_t value) { + DCHECK_GT(value, 0u); + return __builtin_clzll(value); + } + static FOLLY_ALWAYS_INLINE uint64_t blsr(uint64_t value) { + return value & (value - 1); + } + + // Extract `length` bits starting from `start` from value. Only bits [0:63] + // will be extracted. All higher order bits in the + // result will be zeroed. If no bits are extracted, return 0. + static FOLLY_ALWAYS_INLINE uint64_t + bextr(uint64_t value, uint32_t start, uint32_t length) { + if (start > 63) { + return 0ULL; + } + if (start + length > 64) { + length = 64 - start; + } + + return (value >> start) & + ((length == 64) ? (~0ULL) : ((1ULL << length) - 1ULL)); + } + + // Clear high bits starting at position index. + static FOLLY_ALWAYS_INLINE uint64_t bzhi(uint64_t value, uint32_t index) { + if (index > 63) { + return 0; + } + return value & ((uint64_t(1) << index) - 1); + } +}; + +struct Nehalem : public Default { + static bool supported(const folly::CpuId& cpuId = {}) { + return cpuId.popcnt(); + } + + static FOLLY_ALWAYS_INLINE uint64_t popcount(uint64_t value) { +// POPCNT is supported starting with Intel Nehalem, AMD K10. +#if defined(__GNUC__) + // GCC and Clang won't inline the intrinsics. + uint64_t result; + asm("popcntq %1, %0" : "=r"(result) : "r"(value)); + return result; +#else + return uint64_t(_mm_popcnt_u64(value)); +#endif + } +}; + +struct Haswell : public Nehalem { + static bool supported(const folly::CpuId& cpuId = {}) { + return Nehalem::supported(cpuId) && cpuId.bmi1() && cpuId.bmi2(); + } + + static FOLLY_ALWAYS_INLINE uint64_t blsr(uint64_t value) { +// BMI1 is supported starting with Intel Haswell, AMD Piledriver. +// BLSR combines two instructions into one and reduces register pressure. +#if defined(__GNUC__) + // GCC and Clang won't inline the intrinsics. + uint64_t result; + asm("blsrq %1, %0" : "=r"(result) : "r"(value)); + return result; +#else + return _blsr_u64(value); +#endif + } + + static FOLLY_ALWAYS_INLINE uint64_t + bextr(uint64_t value, uint32_t start, uint32_t length) { +#if defined(__GNUC__) + // GCC and Clang won't inline the intrinsics. + // Encode parameters in `pattern` where `pattern[0:7]` is `start` and + // `pattern[8:15]` is `length`. + // Ref: Intel Advanced Vector Extensions Programming Reference + uint64_t pattern = start & 0xFF; + pattern = pattern | ((length & 0xFF) << 8); + uint64_t result; + asm("bextrq %2, %1, %0" : "=r"(result) : "r"(value), "r"(pattern)); + return result; +#else + return _bextr_u64(value, start, length); +#endif + } + + static FOLLY_ALWAYS_INLINE uint64_t bzhi(uint64_t value, uint32_t index) { +#if defined(__GNUC__) + // GCC and Clang won't inline the intrinsics. + const uint64_t index64 = index; + uint64_t result; + asm("bzhiq %2, %1, %0" : "=r"(result) : "r"(value), "r"(index64)); + return result; +#else + return _bzhi_u64(value, index); +#endif + } +}; + +enum class Type { + DEFAULT, + NEHALEM, + HASWELL, +}; + +inline Type detect() { + const static Type type = [] { + if (instructions::Haswell::supported()) { + VLOG(2) << "Will use folly::compression::instructions::Haswell"; + return Type::HASWELL; + } else if (instructions::Nehalem::supported()) { + VLOG(2) << "Will use folly::compression::instructions::Nehalem"; + return Type::NEHALEM; + } else { + VLOG(2) << "Will use folly::compression::instructions::Default"; + return Type::DEFAULT; + } + }(); + return type; +} + +template <class F> +auto dispatch(Type type, F&& f) -> decltype(f(std::declval<Default>())) { + switch (type) { + case Type::HASWELL: + return f(Haswell()); + case Type::NEHALEM: + return f(Nehalem()); + case Type::DEFAULT: + return f(Default()); + } + + assume_unreachable(); +} + +template <class F> +auto dispatch(F&& f) -> decltype(f(std::declval<Default>())) { + return dispatch(detect(), std::forward<F>(f)); +} + +} // namespace instructions +} // namespace compression +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/JSONSchema.h b/ios/Pods/Flipper-Folly/folly/experimental/JSONSchema.h new file mode 100644 index 000000000..0c6874a80 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/JSONSchema.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <folly/Range.h> +#include <folly/dynamic.h> + +/** + * Validation according to the draft v4 standard: http://json-schema.org/ + * + * If your schema is invalid, then it won't validate anything. For example, if + * you set "type": "invalid_type" in your schema, then it won't check for any + * type, as if you had left that property out. If you want to make sure your + * schema is valid, you can optionally validate it first according to the + * metaschema. + * + * Limitations: + * - We don't support fetching schemas via HTTP. + * - We don't support remote $refs. + * - We don't support $ref via id (only by path). + * - We don't support UTF-8 for string lengths, i.e. we will count bytes for + * schemas that use minLength/maxLength. + */ + +namespace folly { +namespace jsonschema { + +/** + * Interface for a schema validator. + */ +struct Validator { + virtual ~Validator() = 0; + + /** + * Check whether the given value passes the schema. Throws if it fails. + */ + virtual void validate(const dynamic& value) const = 0; + + /** + * Check whether the given value passes the schema. Returns an + * exception_wrapper indicating success or what the failure was. + */ + virtual exception_wrapper try_validate(const dynamic& value) const + noexcept = 0; +}; + +/** + * Make a validator that can be used to check various json. Thread-safe. + */ +std::unique_ptr<Validator> makeValidator(const dynamic& schema); + +/** + * Makes a validator for schemas. You should probably check your schema with + * this before you use makeValidator(). + */ +std::shared_ptr<Validator> makeSchemaValidator(); +} // namespace jsonschema +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h b/ios/Pods/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h new file mode 100644 index 000000000..7ea59a442 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html + +#pragma once + +#include <folly/CPortability.h> +#include <folly/memory/Malloc.h> +#include <folly/portability/Config.h> +#include <folly/portability/Memory.h> +#include <folly/portability/SysMman.h> + +#include <cstddef> +#include <cstdint> + +namespace folly { + +/** + * An allocator which uses Jemalloc to create a dedicated huge page arena, + * backed by 2MB huge pages (on linux x86-64). + * + * This allocator is specifically intended for linux with the transparent + * huge page support set to 'madvise' and defrag policy set to 'madvise' + * or 'defer+madvise'. + * These can be controller via /sys/kernel/mm/transparent_hugepage/enabled + * and /sys/kernel/mm/transparent_hugepage/defrag. + * + * The allocator reserves a fixed-size area using mmap, and sets the + * MADV_HUGEPAGE page attribute using the madvise system call. + * A custom jemalloc hook is installed which is called when creating a new + * extent of memory. This will allocate from the reserved area if possible, + * and otherwise fall back to the default method. + * Jemalloc does not use allocated extents across different arenas without + * first unmapping them, and the advice flags are cleared on munmap. + * A regular malloc will never end up allocating memory from this arena. + * + * If binary isn't linked with jemalloc, the logic falls back to malloc / free. + * + * Please note that as per kernel contract, page faults on an madvised region + * will block, so we pre-allocate all the huge pages by touching the pages. + * So, please only allocate as much you need as this will never be freed + * during the lifetime of the application. If we run out of the free huge pages, + * then huge page allocator falls back to the 4K regular pages. + * + * 1GB Huge Pages are not supported at this point. + */ +class JemallocHugePageAllocator { + public: + static bool init(int nr_pages); + + static void* allocate(size_t size) { + // If uninitialized, flags_ will be 0 and the mallocx behavior + // will match that of a regular malloc + return hugePagesSupported ? mallocx(size, flags_) : malloc(size); + } + + static void* reallocate(void* p, size_t size) { + return hugePagesSupported ? rallocx(p, size, flags_) : realloc(p, size); + } + + static void deallocate(void* p, size_t = 0) { + hugePagesSupported ? dallocx(p, flags_) : free(p); + } + + static bool initialized() { + return flags_ != 0; + } + + static size_t freeSpace(); + static bool addressInArena(void* address); + + private: + static int flags_; + static bool hugePagesSupported; +}; + +// STL compatible huge page allocator, for use with STL-style containers +template <typename T> +class CxxHugePageAllocator { + private: + using Self = CxxHugePageAllocator<T>; + + public: + using value_type = T; + + CxxHugePageAllocator() {} + + template <typename U> + explicit CxxHugePageAllocator(CxxHugePageAllocator<U> const&) {} + + T* allocate(std::size_t n) { + return static_cast<T*>(JemallocHugePageAllocator::allocate(sizeof(T) * n)); + } + void deallocate(T* p, std::size_t n) { + JemallocHugePageAllocator::deallocate(p, sizeof(T) * n); + } + + friend bool operator==(Self const&, Self const&) noexcept { + return true; + } + friend bool operator!=(Self const&, Self const&) noexcept { + return false; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h b/ios/Pods/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h new file mode 100644 index 000000000..b3b46e349 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html + +#pragma once + +#include <folly/CPortability.h> +#include <folly/portability/Config.h> +#include <folly/portability/Malloc.h> + +#if defined(FOLLY_USE_JEMALLOC) && !FOLLY_SANITIZE + +#include <folly/portability/SysMman.h> + +#if (JEMALLOC_VERSION_MAJOR > 3) && defined(MADV_DONTDUMP) +#define FOLLY_JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED 1 +#if (JEMALLOC_VERSION_MAJOR == 4) +#define FOLLY_JEMALLOC_NODUMP_ALLOCATOR_CHUNK +#define JEMALLOC_CHUNK_OR_EXTENT chunk +#else +#define FOLLY_JEMALLOC_NODUMP_ALLOCATOR_EXTENT +#define JEMALLOC_CHUNK_OR_EXTENT extent +#endif +#endif + +#endif // FOLLY_USE_JEMALLOC + +#include <cstddef> + +namespace folly { + +/** + * An allocator which uses Jemalloc to create an dedicated arena to allocate + * memory from. The only special property set on the allocated memory is that + * the memory is not dump-able. + * + * This is done by setting MADV_DONTDUMP using the `madvise` system call. A + * custom hook installed which is called when allocating a new chunk / extent of + * memory. All it does is call the original jemalloc hook to allocate the + * memory and then set the advise on it before returning the pointer to the + * allocated memory. Jemalloc does not use allocated chunks / extents across + * different arenas, without `munmap`-ing them first, and the advises are not + * sticky i.e. they are unset if `munmap` is done. Also this arena can't be used + * by any other part of the code by just calling `malloc`. + * + * If target system doesn't support MADV_DONTDUMP or jemalloc doesn't support + * custom arena hook, JemallocNodumpAllocator would fall back to using malloc / + * free. Such behavior can be identified by using + * !defined(FOLLY_JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED). + * + * Similarly, if binary isn't linked with jemalloc, the logic would fall back to + * malloc / free. + */ +class JemallocNodumpAllocator { + public: + enum class State { + ENABLED, + DISABLED, + }; + + // To be used as IOBuf::FreeFunction, userData should be set to + // reinterpret_cast<void*>(getFlags()). + static void deallocate(void* p, void* userData); + + explicit JemallocNodumpAllocator(State state = State::ENABLED); + + void* allocate(size_t size); + void* reallocate(void* p, size_t size); + void deallocate(void* p, size_t = 0); + + unsigned getArenaIndex() const { + return arena_index_; + } + int getFlags() const { + return flags_; + } + + private: +#ifdef FOLLY_JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED +#ifdef FOLLY_JEMALLOC_NODUMP_ALLOCATOR_CHUNK + static chunk_alloc_t* original_alloc_; + static void* alloc( + void* chunk, +#else + static extent_hooks_t extent_hooks_; + static extent_alloc_t* original_alloc_; + static void* alloc( + extent_hooks_t* extent, + void* new_addr, +#endif + size_t size, + size_t alignment, + bool* zero, + bool* commit, + unsigned arena_ind); +#endif // FOLLY_JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED + + bool extend_and_setup_arena(); + + unsigned arena_index_{0}; + int flags_{0}; +}; + +/** + * JemallocNodumpAllocator singleton. + */ +JemallocNodumpAllocator& globalJemallocNodumpAllocator(); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h b/ios/Pods/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h new file mode 100644 index 000000000..14f11dc4a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cmath> +#include <cstring> +#include <memory> +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/detail/TurnSequencer.h> +#include <folly/portability/Unistd.h> + +namespace folly { +namespace detail { + +template <typename T, template <typename> class Atom> +class RingBufferSlot; +} // namespace detail + +/// LockFreeRingBuffer<T> is a fixed-size, concurrent ring buffer with the +/// following semantics: +/// +/// 1. Writers cannot block on other writers UNLESS they are <capacity> writes +/// apart from each other (writing to the same slot after a wrap-around) +/// 2. Writers cannot block on readers +/// 3. Readers can wait for writes that haven't occurred yet +/// 4. Readers can detect if they are lagging behind +/// +/// In this sense, reads from this buffer are best-effort but writes +/// are guaranteed. +/// +/// Another way to think about this is as an unbounded stream of writes. The +/// buffer contains the last <capacity> writes but readers can attempt to read +/// any part of the stream, even outside this window. The read API takes a +/// Cursor that can point anywhere in this stream of writes. Reads from the +/// "future" can optionally block but reads from the "past" will always fail. +/// + +template <typename T, template <typename> class Atom = std::atomic> +class LockFreeRingBuffer { + static_assert( + std::is_nothrow_default_constructible<T>::value, + "Element type must be nothrow default constructible"); + + public: + /// Opaque pointer to a past or future write. + /// Can be moved relative to its current location but not in absolute terms. + struct Cursor { + explicit Cursor(uint64_t initialTicket) noexcept : ticket(initialTicket) {} + + /// Returns true if this cursor now points to a different + /// write, false otherwise. + bool moveForward(uint64_t steps = 1) noexcept { + uint64_t prevTicket = ticket; + ticket += steps; + return prevTicket != ticket; + } + + /// Returns true if this cursor now points to a previous + /// write, false otherwise. + bool moveBackward(uint64_t steps = 1) noexcept { + uint64_t prevTicket = ticket; + if (steps > ticket) { + ticket = 0; + } else { + ticket -= steps; + } + return prevTicket != ticket; + } + + protected: // for test visibility reasons + uint64_t ticket; + friend class LockFreeRingBuffer; + }; + + explicit LockFreeRingBuffer(uint32_t capacity) noexcept + : capacity_(capacity), + slots_(new detail::RingBufferSlot<T, Atom>[capacity]), + ticket_(0) {} + + LockFreeRingBuffer(const LockFreeRingBuffer&) = delete; + LockFreeRingBuffer& operator=(const LockFreeRingBuffer&) = delete; + + /// Perform a single write of an object of type T. + /// Writes can block iff a previous writer has not yet completed a write + /// for the same slot (before the most recent wrap-around). + template <typename V> + void write(V& value) noexcept { + uint64_t ticket = ticket_.fetch_add(1); + slots_[idx(ticket)].write(turn(ticket), value); + } + + /// Perform a single write of an object of type T. + /// Writes can block iff a previous writer has not yet completed a write + /// for the same slot (before the most recent wrap-around). + /// Returns a Cursor pointing to the just-written T. + template <typename V> + Cursor writeAndGetCursor(V& value) noexcept { + uint64_t ticket = ticket_.fetch_add(1); + slots_[idx(ticket)].write(turn(ticket), value); + return Cursor(ticket); + } + + /// Read the value at the cursor. + /// Returns true if the read succeeded, false otherwise. If the return + /// value is false, dest is to be considered partially read and in an + /// inconsistent state. Readers are advised to discard it. + template <typename V> + bool tryRead(V& dest, const Cursor& cursor) noexcept { + return slots_[idx(cursor.ticket)].tryRead(dest, turn(cursor.ticket)); + } + + /// Read the value at the cursor or block if the write has not occurred yet. + /// Returns true if the read succeeded, false otherwise. If the return + /// value is false, dest is to be considered partially read and in an + /// inconsistent state. Readers are advised to discard it. + template <typename V> + bool waitAndTryRead(V& dest, const Cursor& cursor) noexcept { + return slots_[idx(cursor.ticket)].waitAndTryRead(dest, turn(cursor.ticket)); + } + + /// Returns a Cursor pointing to the first write that has not occurred yet. + Cursor currentHead() noexcept { + return Cursor(ticket_.load()); + } + + /// Returns a Cursor pointing to a currently readable write. + /// skipFraction is a value in the [0, 1] range indicating how far into the + /// currently readable window to place the cursor. 0 means the + /// earliest readable write, 1 means the latest readable write (if any). + Cursor currentTail(double skipFraction = 0.0) noexcept { + assert(skipFraction >= 0.0 && skipFraction <= 1.0); + uint64_t ticket = ticket_.load(); + + uint64_t backStep = llround((1.0 - skipFraction) * capacity_); + + // always try to move at least one step backward to something readable + backStep = std::max<uint64_t>(1, backStep); + + // can't go back more steps than we've taken + backStep = std::min(ticket, backStep); + + return Cursor(ticket - backStep); + } + + ~LockFreeRingBuffer() {} + + private: + const uint32_t capacity_; + + const std::unique_ptr<detail::RingBufferSlot<T, Atom>[]> slots_; + + Atom<uint64_t> ticket_; + + uint32_t idx(uint64_t ticket) noexcept { + return ticket % capacity_; + } + + uint32_t turn(uint64_t ticket) noexcept { + return (uint32_t)(ticket / capacity_); + } +}; // LockFreeRingBuffer + +namespace detail { +template <typename T, template <typename> class Atom> +class RingBufferSlot { + template <typename V> + void copy(V& dest, T& src) { + dest = src; + } + + public: + explicit RingBufferSlot() noexcept : sequencer_(), data() {} + + template <typename V> + void write(const uint32_t turn, V& value) noexcept { + Atom<uint32_t> cutoff(0); + sequencer_.waitForTurn(turn * 2, cutoff, false); + + // Change to an odd-numbered turn to indicate write in process + sequencer_.completeTurn(turn * 2); + + data = std::move(value); + sequencer_.completeTurn(turn * 2 + 1); + // At (turn + 1) * 2 + } + + template <typename V> + bool waitAndTryRead(V& dest, uint32_t turn) noexcept { + uint32_t desired_turn = (turn + 1) * 2; + Atom<uint32_t> cutoff(0); + if (sequencer_.tryWaitForTurn(desired_turn, cutoff, false) != + TurnSequencer<Atom>::TryWaitResult::SUCCESS) { + return false; + } + copy(dest, data); + + // if it's still the same turn, we read the value successfully + return sequencer_.isTurn(desired_turn); + } + + template <typename V> + bool tryRead(V& dest, uint32_t turn) noexcept { + // The write that started at turn 0 ended at turn 2 + if (!sequencer_.isTurn((turn + 1) * 2)) { + return false; + } + copy(dest, data); + + // if it's still the same turn, we read the value successfully + return sequencer_.isTurn((turn + 1) * 2); + } + + private: + TurnSequencer<Atom> sequencer_; + T data; +}; // RingBufferSlot + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/MasterPtr.h b/ios/Pods/Flipper-Folly/folly/experimental/MasterPtr.h new file mode 100644 index 000000000..61731f34a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/MasterPtr.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/synchronization/Baton.h> + +#include <glog/logging.h> + +namespace folly { + +template <typename T> +class MasterPtrRef; + +/** + * MasterPtr should be used to achieve deterministic destruction of objects with + * shared ownership. + * Once an object is managed by a MasterPtr, shared_ptrs can be obtained + * pointing to that object. However destroying those shared_ptrs will never call + * the object destructor inline. To destroy the object, join() method should be + * called on MasterPtr which will wait for all shared_ptrs to be released and + * then call the object destructor inline. + */ +template <typename T> +class MasterPtr { + public: + MasterPtr(); + MasterPtr(std::unique_ptr<T> ptr) { + set(std::move(ptr)); + } + ~MasterPtr() { + if (innerPtr_) { + LOG(FATAL) << "MasterPtr has to be joined explicitly."; + } + } + + // Attempts to lock a pointer. Returns null if pointer is not set or if join() + // was called (even if the call to join() hasn't returned yet). + std::shared_ptr<T> lock() const { + if (auto outerPtr = outerPtrWeak_.lock()) { + return *outerPtr; + } + return nullptr; + } + + // Waits until all the refereces obtained via lock() are released. Then + // destroys the object in the current thread. + // Can not be called concurrently with set(). + void join() { + if (!innerPtr_) { + return; + } + + outerPtrShared_.reset(); + joinBaton_.wait(); + innerPtr_.reset(); + } + + // Sets the pointer. Can not be called concurrently with lock() or join() or + // ref(). + void set(std::unique_ptr<T> ptr) { + if (innerPtr_) { + LOG(FATAL) << "MasterPtr has to be joined before being set."; + } + + if (!ptr) { + return; + } + + innerPtr_ = std::move(ptr); + joinBaton_.reset(); + auto innerPtrShared = + std::shared_ptr<T>(innerPtr_.get(), [&](T*) { joinBaton_.post(); }); + outerPtrShared_ = + std::make_shared<std::shared_ptr<T>>(std::move(innerPtrShared)); + outerPtrWeak_ = outerPtrShared_; + } + + // Gets a non-owning reference to the pointer. join() does *NOT* wait for + // outstanding MasterPtrRef objects to be released. + MasterPtrRef<T> ref() const { + return MasterPtrRef<T>(outerPtrWeak_); + } + + private: + friend class MasterPtrRef<T>; + folly::Baton<> joinBaton_; + std::shared_ptr<std::shared_ptr<T>> outerPtrShared_; + std::weak_ptr<std::shared_ptr<T>> outerPtrWeak_; + std::unique_ptr<T> innerPtr_; +}; + +template <typename T> +class MasterPtrRef { + public: + // Attempts to lock a pointer. Returns null if pointer is not set or if join() + // was called (even if the call to join() hasn't returned yet). + std::shared_ptr<T> lock() const { + if (auto outerPtr = outerPtrWeak_.lock()) { + return *outerPtr; + } + return nullptr; + } + + private: + friend class MasterPtr<T>; + /* implicit */ MasterPtrRef(std::weak_ptr<std::shared_ptr<T>> outerPtrWeak) + : outerPtrWeak_(std::move(outerPtrWeak)) {} + + std::weak_ptr<std::shared_ptr<T>> outerPtrWeak_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/NestedCommandLineApp.h b/ios/Pods/Flipper-Folly/folly/experimental/NestedCommandLineApp.h new file mode 100644 index 000000000..cef1248c2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/NestedCommandLineApp.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <set> +#include <stdexcept> + +#include <folly/CPortability.h> +#include <folly/String.h> +#include <folly/experimental/ProgramOptions.h> + +namespace folly { + +/** + * Exception that commands may throw to force the program to exit cleanly + * with a given exit code. NestedCommandLineApp::run() catches this and + * makes run() print the given message on stderr (followed by a newline, unless + * empty; the message is only allowed when exiting with a non-zero status), and + * return the exit code. (Other exceptions will propagate out of run()) + */ +class FOLLY_EXPORT ProgramExit : public std::runtime_error { + public: + explicit ProgramExit(int status, const std::string& msg = std::string()); + int status() const { + return status_; + } + + private: + int status_; +}; + +/** + * App that uses a nested command line, of the form: + * + * program [--global_options...] command [--command_options...] command_args... + */ +class NestedCommandLineApp { + public: + typedef std::function<void( + const std::string& command, + const boost::program_options::variables_map& options, + const std::vector<std::string>& args)> + InitFunction; + + typedef std::function<void( + const boost::program_options::variables_map& options, + const std::vector<std::string>&)> + Command; + + static constexpr StringPiece const kHelpCommand = "help"; + static constexpr StringPiece const kVersionCommand = "version"; + /** + * Initialize the app. + * + * If programName is not set, we try to guess (readlink("/proc/self/exe")). + * + * version is the version string printed when given the --version flag. + * + * initFunction, if specified, is called after parsing the command line, + * right before executing the command. + */ + explicit NestedCommandLineApp( + std::string programName = std::string(), + std::string version = std::string(), + std::string programHeading = std::string(), + std::string programHelpFooter = std::string(), + InitFunction initFunction = InitFunction()); + + /** + * Add GFlags to the list of supported options with the given style. + */ + void addGFlags(ProgramOptionsStyle style = ProgramOptionsStyle::GNU) { + globalOptions_.add(getGFlags(style)); + } + + /** + * Return the global options object, so you can add options. + */ + boost::program_options::options_description& globalOptions() { + return globalOptions_; + } + + /** + * Add a command. + * + * name: command name + * argStr: description of arguments in help strings + * (<filename> <N>) + * shortHelp: one-line summary help string + * fullHelp: full help string + * command: function to run + * + * Returns a reference to the options_description object that you can + * use to add options for this command. + */ + boost::program_options::options_description& addCommand( + std::string name, + std::string argStr, + std::string shortHelp, + std::string fullHelp, + Command command); + + /** + * Add an alias; running the command newName will have the same effect + * as running oldName. + */ + void addAlias(std::string newName, std::string oldName); + + /** + * Run the command and return; the return code is 0 on success or + * non-zero on error, so it is idiomatic to call this at the end of main(): + * return app.run(argc, argv); + * + * On successful exit, run() will check for errors on stdout (and flush + * it) to help command-line applications that need to write to stdout + * (failing to write to stdout is an error). If there is an error on stdout, + * we'll print a helpful message on stderr and return an error status (1). + */ + int run(int argc, const char* const argv[]); + int run(const std::vector<std::string>& args); + + /** + * Return true if name represent known built-in command (help, version) + */ + bool isBuiltinCommand(const std::string& name) const; + + private: + void doRun(const std::vector<std::string>& args); + const std::string& resolveAlias(const std::string& name) const; + + struct CommandInfo { + std::string argStr; + std::string shortHelp; + std::string fullHelp; + Command command; + boost::program_options::options_description options; + }; + + const std::pair<const std::string, CommandInfo>& findCommand( + const std::string& name) const; + + void displayHelp( + const boost::program_options::variables_map& options, + const std::vector<std::string>& args) const; + + void displayVersion() const; + + std::string programName_; + std::string programHeading_; + std::string programHelpFooter_; + std::string version_; + InitFunction initFunction_; + boost::program_options::options_description globalOptions_; + std::map<std::string, CommandInfo> commands_; + std::map<std::string, std::string> aliases_; + std::set<folly::StringPiece> builtinCommands_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/ProgramOptions.h b/ios/Pods/Flipper-Folly/folly/experimental/ProgramOptions.h new file mode 100644 index 000000000..f095f4dfa --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/ProgramOptions.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <boost/program_options.hpp> + +#include <folly/Optional.h> +#include <folly/portability/GFlags.h> + +namespace folly { + +enum class ProgramOptionsStyle { + GFLAGS, + GNU, +}; + +// Add all GFlags to the given options_description. +// Use this *instead of* gflags::ParseCommandLineFlags(). +// +// in GFLAGS style, the flags are named as per gflags conventions: +// names_with_underscores +// boolean flags have a "no" prefix +// +// in GNU style, the flags are named as per GNU conventions: +// names-with-dashes +// boolean flags have a "no-" prefix +// +// Consider (for example) a boolean flag: +// DEFINE_bool(flying_pigs, false, "..."); +// +// In GFLAGS style, the corresponding flags are named +// flying_pigs +// noflying_pigs +// +// In GNU style, the corresponding flags are named +// flying-pigs +// no-flying-pigs +// +// You may not pass arguments to boolean flags, so you must use the +// "no" / "no-" prefix to set them to false; "--flying_pigs false" +// and "--flying_pigs=false" are not allowed, to prevent ambiguity. +boost::program_options::options_description getGFlags( + ProgramOptionsStyle style = ProgramOptionsStyle::GNU); + +// Helper when parsing nested command lines: +// +// program [--common_options...] command [--command_options...] args +// +// The result has "command" set to the first positional argument, if any, +// and "rest" set to the remaining options and arguments. Note that any +// unrecognized flags must appear after the command name. +// +// You may pass "rest" to parseNestedCommandLine again, etc. +struct NestedCommandLineParseResult { + NestedCommandLineParseResult() {} + + boost::program_options::parsed_options options{nullptr}; + + Optional<std::string> command; + std::vector<std::string> rest; +}; + +NestedCommandLineParseResult parseNestedCommandLine( + int argc, + const char* const argv[], + const boost::program_options::options_description& desc); + +NestedCommandLineParseResult parseNestedCommandLine( + const std::vector<std::string>& cmdline, + const boost::program_options::options_description& desc); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h b/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h new file mode 100644 index 000000000..ebd782f9d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/experimental/QuotientMultiSet.h> + +#include <folly/Format.h> +#include <folly/Portability.h> +#include <folly/experimental/Bits.h> +#include <folly/experimental/Select64.h> +#include <folly/lang/Bits.h> +#include <folly/lang/SafeAssert.h> +#include <glog/logging.h> + +#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED + +namespace folly { + +namespace qms_detail { + +/* + * Reference: Faster Remainder by Direct Computation: Applications to Compilers + * and Software Libraries, Software: Practice and Experience 49 (6), 2019. + */ +FOLLY_ALWAYS_INLINE UInt64InverseType getInverse(uint64_t divisor) { + UInt64InverseType fraction = UInt64InverseType(-1); + fraction /= divisor; + fraction += 1; + return fraction; +} + +FOLLY_ALWAYS_INLINE uint64_t +mul128(UInt64InverseType lowbits, uint64_t divisor) { + UInt64InverseType bottomHalf = + (lowbits & UINT64_C(0xFFFFFFFFFFFFFFFF)) * divisor; + bottomHalf >>= 64; + UInt64InverseType topHalf = (lowbits >> 64) * divisor; + UInt64InverseType bothHalves = bottomHalf + topHalf; + bothHalves >>= 64; + return static_cast<uint64_t>(bothHalves); +} + +FOLLY_ALWAYS_INLINE std::pair<uint64_t, uint64_t> getQuotientAndRemainder( + uint64_t dividend, + uint64_t divisor, + UInt64InverseType inverse) { + if (FOLLY_UNLIKELY(divisor == 1)) { + return {dividend, 0}; + } + auto quotient = mul128(inverse, dividend); + auto remainder = dividend - quotient * divisor; + DCHECK_LT(remainder, divisor); + return {quotient, remainder}; +} + +// Max value for given bits. +FOLLY_ALWAYS_INLINE uint64_t maxValue(uint32_t nbits) { + return nbits == 64 ? std::numeric_limits<uint64_t>::max() + : (uint64_t(1) << nbits) - 1; +} + +} // namespace qms_detail + +template <class Instructions> +struct QuotientMultiSet<Instructions>::Metadata { + // Total number of blocks. + uint64_t numBlocks; + uint64_t numKeys; + uint64_t divisor; + uint8_t keyBits; + uint8_t remainderBits; + + std::string debugString() const { + return sformat( + "Number of blocks: {}\n" + "Number of elements: {}\n" + "Divisor: {}\n" + "Key bits: {}\n" + "Remainder bits: {}", + numBlocks, + numKeys, + divisor, + keyBits, + remainderBits); + } +} FOLLY_PACK_ATTR; + +template <class Instructions> +struct QuotientMultiSet<Instructions>::Block { + static const Block* get(const char* data) { + return reinterpret_cast<const Block*>(data); + } + + static BlockPtr make(size_t remainderBits) { + auto ptr = reinterpret_cast<Block*>(calloc(blockSize(remainderBits), 1)); + return {ptr, free}; + } + + uint64_t payload; + uint64_t occupieds; + uint64_t offset; + uint64_t runends; + char remainders[0]; + + static uint64_t blockSize(size_t remainderBits) { + return sizeof(Block) + remainderBits * 8; + } + + FOLLY_ALWAYS_INLINE bool isOccupied(size_t offsetInBlock) const { + return ((occupieds >> offsetInBlock) & uint64_t(1)) != 0; + } + + FOLLY_ALWAYS_INLINE bool isRunend(size_t offsetInBlock) const { + return ((runends >> offsetInBlock) & uint64_t(1)) != 0; + } + + FOLLY_ALWAYS_INLINE uint64_t getRemainder( + size_t offsetInBlock, + size_t remainderBits, + size_t remainderMask) const { + DCHECK_LE(remainderBits, 56); + const size_t bitPos = offsetInBlock * remainderBits; + const uint64_t remainderWord = + loadUnaligned<uint64_t>(remainders + (bitPos / 8)); + return (remainderWord >> (bitPos % 8)) & remainderMask; + } + + void setOccupied(size_t offsetInBlock) { + occupieds |= uint64_t(1) << offsetInBlock; + } + + void setRunend(size_t offsetInBlock) { + runends |= uint64_t(1) << offsetInBlock; + } + + void + setRemainder(size_t offsetInBlock, size_t remainderBits, uint64_t remainder) { + DCHECK_LT(offsetInBlock, kBlockSize); + if (FOLLY_UNLIKELY(remainderBits == 0)) { + return; + } + Bits<uint64_t>::set( + reinterpret_cast<uint64_t*>(remainders), + offsetInBlock * remainderBits, + remainderBits, + remainder); + } +} FOLLY_PACK_ATTR; + +template <class Instructions> +QuotientMultiSet<Instructions>::QuotientMultiSet(StringPiece data) { + static_assert( + kIsLittleEndian, "QuotientMultiSet requires little endianness."); + StringPiece sp = data; + CHECK_GE(sp.size(), sizeof(Metadata)); + sp.advance(sp.size() - sizeof(Metadata)); + metadata_ = reinterpret_cast<const Metadata*>(sp.data()); + VLOG(2) << "Metadata: " << metadata_->debugString(); + + numBlocks_ = metadata_->numBlocks; + numSlots_ = numBlocks_ * kBlockSize; + divisor_ = metadata_->divisor; + fraction_ = qms_detail::getInverse(divisor_); + keyBits_ = metadata_->keyBits; + maxKey_ = qms_detail::maxValue(keyBits_); + remainderBits_ = metadata_->remainderBits; + remainderMask_ = qms_detail::maxValue(remainderBits_); + blockSize_ = Block::blockSize(remainderBits_); + + CHECK_EQ(data.size(), numBlocks_ * blockSize_ + sizeof(Metadata)); + data_ = data.data(); +} + +template <class Instructions> +auto QuotientMultiSet<Instructions>::equalRange(uint64_t key) const + -> SlotRange { + if (key > maxKey_) { + return {0, 0}; + } + const auto qr = qms_detail::getQuotientAndRemainder(key, divisor_, fraction_); + const auto& quotient = qr.first; + const auto& remainder = qr.second; + const size_t blockIndex = quotient / kBlockSize; + const size_t offsetInBlock = quotient % kBlockSize; + + if (FOLLY_UNLIKELY(blockIndex >= numBlocks_)) { + return {0, 0}; + } + + const auto* occBlock = getBlock(blockIndex); + __builtin_prefetch(reinterpret_cast<const char*>(&occBlock->occupieds) + 64); + const auto firstRunend = occBlock->offset; + if (!occBlock->isOccupied(offsetInBlock)) { + // Return a position that depends on the contents of the block so + // we can create a dependency in benchmarks. + return {firstRunend, firstRunend}; + } + + // Look for the right runend for the given key. + const uint64_t occupiedRank = Instructions::popcount( + Instructions::bzhi(occBlock->occupieds, offsetInBlock)); + auto runend = findRunend(occupiedRank, firstRunend); + auto& slot = runend.first; + auto& block = runend.second; + + // Iterates over the run backwards to find the slots whose remainder + // matches the key. + SlotRange range = {slot + 1, slot + 1}; + while (true) { + uint64_t slotRemainder = + block->getRemainder(slot % kBlockSize, remainderBits_, remainderMask_); + + if (slotRemainder > remainder) { + range.begin = slot; + range.end = slot; + } else if (slotRemainder == remainder) { + range.begin = slot; + } else { + break; + } + + if (FOLLY_UNLIKELY(slot % kBlockSize == 0)) { + // Reached block start and the run starts from a prev block. + size_t slotBlockIndex = slot / kBlockSize; + if (slotBlockIndex > blockIndex) { + block = getBlock(slotBlockIndex - 1); + } else { + break; + } + } + + --slot; + // Encounters the previous run. + if (block->isRunend(slot % kBlockSize)) { + break; + } + } + + return range; +} + +template <class Instructions> +auto QuotientMultiSet<Instructions>::findRunend( + uint64_t occupiedRank, + uint64_t firstRunend) const -> std::pair<uint64_t, const Block*> { + // Look for the right runend. + size_t slotBlockIndex = firstRunend / kBlockSize; + auto block = getBlock(slotBlockIndex); + uint64_t runendWord = + block->runends & (uint64_t(-1) << (firstRunend % kBlockSize)); + + while (true) { + DCHECK_LE(slotBlockIndex, numBlocks_); + + const size_t numRuns = Instructions::popcount(runendWord); + if (FOLLY_LIKELY(numRuns > occupiedRank)) { + break; + } + occupiedRank -= numRuns; + ++slotBlockIndex; + block = getBlock(slotBlockIndex); + runendWord = block->runends; + } + + return {slotBlockIndex * kBlockSize + + select64<Instructions>(runendWord, occupiedRank), + block}; +} + +template <class Instructions> +uint64_t QuotientMultiSet<Instructions>::getBlockPayload( + uint64_t blockIndex) const { + DCHECK_LT(blockIndex, numBlocks_); + return getBlock(blockIndex)->payload; +} + +template <class Instructions> +QuotientMultiSet<Instructions>::Iterator::Iterator( + const QuotientMultiSet<Instructions>* qms) + : qms_(qms), + key_(0), + occBlockIndex_(-1), + occOffsetInBlock_(0), + occWord_(0), + occBlock_(nullptr), + pos_(-1) {} + +template <class Instructions> +bool QuotientMultiSet<Instructions>::Iterator::next() { + if (pos_ == size_t(-1) || + qms_->getBlock(pos_ / kBlockSize)->isRunend(pos_ % kBlockSize)) { + // Move to start of next run. + if (!nextOccupied()) { + return setEnd(); + } + + // Next run either starts at pos + 1 or the start of block + // specified by occupied slot. + pos_ = std::max<uint64_t>(pos_ + 1, occBlockIndex_ * kBlockSize); + } else { + // Move to next slot since a run must be contiguous. + pos_++; + } + + const Block* block = qms_->getBlock(pos_ / kBlockSize); + uint64_t quotient = + (occBlockIndex_ * kBlockSize + occOffsetInBlock_) * qms_->divisor_; + key_ = quotient + + block->getRemainder( + pos_ % kBlockSize, qms_->remainderBits_, qms_->remainderMask_); + + DCHECK_LT(pos_, qms_->numBlocks_ * kBlockSize); + return true; +} + +template <class Instructions> +bool QuotientMultiSet<Instructions>::Iterator::skipTo(uint64_t key) { + if (key > qms_->maxKey_) { + return false; + } + const auto qr = + qms_detail::getQuotientAndRemainder(key, qms_->divisor_, qms_->fraction_); + const auto& quotient = qr.first; + occBlockIndex_ = quotient / kBlockSize; + occOffsetInBlock_ = quotient % kBlockSize; + + if (FOLLY_UNLIKELY(occBlockIndex_ >= qms_->numBlocks_)) { + return setEnd(); + } + + occBlock_ = qms_->getBlock(occBlockIndex_); + occWord_ = occBlock_->occupieds & (uint64_t(-1) << occOffsetInBlock_); + if (!nextOccupied()) { + return setEnd(); + } + + // Search for the next runend. + uint64_t occupiedRank = Instructions::popcount( + Instructions::bzhi(occBlock_->occupieds, occOffsetInBlock_)); + auto runend = qms_->findRunend(occupiedRank, occBlock_->offset); + auto& slot = runend.first; + auto& block = runend.second; + uint64_t slotBlockIndex = slot / kBlockSize; + uint64_t slotOffsetInBlock = slot % kBlockSize; + pos_ = slot; + + uint64_t nextQuotient = + (occBlockIndex_ * kBlockSize + occOffsetInBlock_) * qms_->divisor_; + uint64_t nextRemainder = block->getRemainder( + slotOffsetInBlock, qms_->remainderBits_, qms_->remainderMask_); + + if (nextQuotient + nextRemainder < key) { + // Lower bound element is at the start of next run. + return next(); + } + + // Iterate over the run backwards to find the first key that is larger than + // or equal to the given key. + while (true) { + uint64_t slotRemainder = block->getRemainder( + slotOffsetInBlock, qms_->remainderBits_, qms_->remainderMask_); + if (nextQuotient + slotRemainder < key) { + break; + } + pos_ = slot; + nextRemainder = slotRemainder; + if (FOLLY_UNLIKELY(slotOffsetInBlock == 0)) { + // Reached block start and the run starts from a prev block. + if (slotBlockIndex > occBlockIndex_) { + --slotBlockIndex; + block = qms_->getBlock(slotBlockIndex); + slotOffsetInBlock = kBlockSize; + } else { + break; + } + } + --slot; + --slotOffsetInBlock; + + // Encounters the previous run. + if (block->isRunend(slotOffsetInBlock)) { + break; + } + } + + key_ = nextQuotient + nextRemainder; + return true; +} + +template <class Instructions> +bool QuotientMultiSet<Instructions>::Iterator::nextOccupied() { + while (FOLLY_UNLIKELY(occWord_ == 0)) { + if (FOLLY_UNLIKELY(++occBlockIndex_ >= qms_->numBlocks_)) { + return false; + } + occBlock_ = qms_->getBlock(occBlockIndex_); + occWord_ = occBlock_->occupieds; + } + + occOffsetInBlock_ = Instructions::ctz(occWord_); + occWord_ = Instructions::blsr(occWord_); + return true; +} + +} // namespace folly + +#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED diff --git a/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet.h b/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet.h new file mode 100644 index 000000000..73506316e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/QuotientMultiSet.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <deque> +#include <utility> + +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/experimental/Instructions.h> +#include <folly/io/IOBuf.h> +#include <folly/io/IOBufQueue.h> + +// A 128-bit integer type is needed for fast division. +#define FOLLY_QUOTIENT_MULTI_SET_SUPPORTED FOLLY_HAVE_INT128_T + +#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED + +namespace folly { + +namespace qms_detail { + +using UInt64InverseType = __uint128_t; + +} // namespace qms_detail + +/** + * A space-efficient static data structure to store a non-decreasing sequence of + * b-bit integers. If the integers are uniformly distributed lookup is O(1)-time + * and performs a single random memory lookup with high probability. + * + * Space for n keys is bounded by (5 + b - log(n / loadFactor)) / loadFactor + * bits per element, which makes it particularly efficient for very dense + * sets. Note that 1 bit is taken up by the user-provided block payloads, and 1 + * depends on how close the table size is to a power of 2. Experimentally, + * performance is good up to load factor 95%. + * + * Lookup returns a range of positions in the table. The intended use case is to + * store hashes, as the first layer of a multi-layer hash table. If b is sized + * to floor(log(n)) + k, the probability of a false positive (a non-empty range + * is returned for a non-existent key) is approximately 2^-k, which makes it + * competitive with a Bloom filter for low FP probabilities, with the additional + * benefit that it also returns a range of positions to restrict the search in + * subsequent layers. + * + * The data structure is inspired by the Rank-Select Quotient Filter + * introduced in + * + * Prashant Pandey, Michael A. Bender, Rob Johnson and Robert Patro, + * A General-Purpose Counting Filter: Making Every Bit Count, SIGMOD, 2017 + * + * Besides being static, QuotientMultiSet differs from the data structure from + * the paper in the following ways: + * + * - The table size can be arbitrary, rather than just powers-of-2. This can + * waste up to a bit for each residual, but it prevents 2x overhead when the + * desired table size is slightly larger than a power of 2. + * + * - Within each block all the holes are moved at the end. This enables + * efficient iteration, and makes the returned positions a contiguous range + * for each block, which allows to use them to index into a secondary data + * structure. An arbitrary 64-bit payload can be attached to each block; for + * example, this can be used to store the number of elements up to that block, + * so that positions can be translated to the element rank. Alternatively, the + * payload can be used to address blocks in the secondary data structure. + * + * - Correctness does not depend on the keys being uniformly distributed. + * However, performance does, as for arbitrary keys the worst-case lookup time + * can be linear. + * + * Implemented by Matt Ma based on a prototype by Giuseppe Ottaviano and + * Sebastiano Vigna. + * + * Data layout: + * ------------------------------------------------------------------------ + * | Block | Block | Block | Block | ... | Block | + * ------------------------------------------------------------------------ + * / | + * ------------------------------------------------------------------------ + * | Payload | Occupieds | Offset | Runends | Remainders * 64 | + * ------------------------------------------------------------------------ + * + * Each block contains 64 slots. Keys mapping to the same slot are stored + * contiguously in a run. The occupieds and runends bitvectors are the + * concatenation of the corresponding words in each block. + * + * - Occupieds bit indicates whether there is a key mapping to this quotient. + * + * - Offset stores the position of the runend of the first run in this block. + * + * - Runends bit indicates whether the slot is the end of some run. 1s in + * occupieds and runends bits are in 1-1 correspondence: the i-th 1 in the + * runends vector marks the run end of the i-th 1 in the occupieds. + */ + +template <class Instructions = compression::instructions::Default> +class QuotientMultiSet final { + public: + explicit QuotientMultiSet(StringPiece data); + + // Each block contains 64 elements. + static constexpr size_t kBlockSize = 64; + + // Position range of given key. End is not included. Range can be empty if the + // key is not found, in which case the values of begin and end are + // unspecified. + struct SlotRange { + size_t begin = 0; + size_t end = 0; + + explicit operator bool() const { + DCHECK_LE(begin, end); + return begin < end; + } + }; + + class Iterator; + + // Get the position range for the given key. + SlotRange equalRange(uint64_t key) const; + + // Get payload of given block. + uint64_t getBlockPayload(uint64_t blockIndex) const; + + friend class QuotientMultiSetBuilder; + + private: + // Metadata to describe a quotient table. + struct Metadata; + + // Block contains payload, occupieds, runends, offsets and 64 remainders. + struct Block; + using BlockPtr = std::unique_ptr<Block, decltype(free)*>; + + const Block* getBlock(size_t blockIndex) const { + return Block::get(data_ + blockIndex * blockSize_); + } + + FOLLY_ALWAYS_INLINE std::pair<uint64_t, const Block*> findRunend( + uint64_t occupiedRank, + uint64_t startPos) const; + + const Metadata* metadata_; + const char* data_; + // Total number of blocks. + size_t numBlocks_; + size_t numSlots_; + // Number of bytes per block. + size_t blockSize_; + // Divisor for mapping from keys to slots. + uint64_t divisor_; + // fraction_ = 1 / divisor_. + qms_detail::UInt64InverseType fraction_; + // Number of key bits. + size_t keyBits_; + uint64_t maxKey_; + // Number of remainder bits. + size_t remainderBits_; + uint64_t remainderMask_; +}; + +template <class Instructions> +class QuotientMultiSet<Instructions>::Iterator { + public: + explicit Iterator(const QuotientMultiSet<Instructions>* qms); + + // Advance to the next key. + bool next(); + + // Skip forward to the first key >= the given key. + bool skipTo(uint64_t key); + + bool done() const { + return pos_ == qms_->numSlots_; + } + + // Return current key. + uint64_t key() const { + return key_; + } + + // Return current position in quotient multiset. + size_t pos() const { + return pos_; + } + + private: + // Position the iterator at the end and return false. + // Shortcut for use when implementing doNext, etc: return setEnd(); + bool setEnd() { + pos_ = qms_->numSlots_; + return false; + } + + // Move to next occupied. + bool nextOccupied(); + + const QuotientMultiSet<Instructions>* qms_; + uint64_t key_; + + // State members for the quotient occupied position. + // Block index of key_'s occupied slot. + size_t occBlockIndex_; + // Block offset of key_'s occupied slot. + uint64_t occOffsetInBlock_; + // Occupied words of the occupiedBlock_ after quotientBlockOffset_. + uint64_t occWord_; + // Block of the current occupied slot. + const Block* occBlock_; + + // State member for the actual key position. + // Position of the current key_. + size_t pos_; +}; + +/* + * Class to build a QuotientMultiSet. + * + * The builder requires inserting elements in non-decreasing order. + * Example usage: + * QuotientMultiSetBuilder builder(...); + * while (...) { + * if (builder.insert(key)) { + * builder.setBlockPayload(payload); + * } + * if (builder.numReadyBlocks() > N) { + * buff = builder.flush(); + * write(buff); + * } + * } + * buff = builder.close(); + * write(buff) + */ +class QuotientMultiSetBuilder final { + public: + QuotientMultiSetBuilder( + size_t keyBits, + size_t expectedElements, + double loadFactor = kDefaultMaxLoadFactor); + ~QuotientMultiSetBuilder(); + + using Metadata = QuotientMultiSet<>::Metadata; + using Block = QuotientMultiSet<>::Block; + + // Keeps load factor <= 0.95. + constexpr static double kDefaultMaxLoadFactor = 0.95; + + constexpr static size_t kBlockSize = QuotientMultiSet<>::kBlockSize; + + // Returns whether the key's slot is in a newly created block. + // Only allows insert keys in nondecreasing order. + bool insert(uint64_t key); + + // Set payload of the latest created block. + // Can only be called immediately after an add() that returns true. + void setBlockPayload(uint64_t payload); + + // Return all ready blocks till now. The ownership of these blocks will be + // transferred to the caller. + void flush(IOBufQueue& buff); + + // Return all remaining blocks since last flush call and the final quotient + // table metadata. The ownership of these blocks will be transferred to the + // caller. + void close(folly::IOBufQueue& buff); + + size_t numReadyBlocks() { + return readyBlocks_; + } + + private: + using BlockPtr = QuotientMultiSet<>::BlockPtr; + + struct BlockWithState { + BlockWithState(BlockPtr ptr, size_t idx) + : block(std::move(ptr)), index(idx), ready(false) {} + + BlockPtr block; + size_t index; + bool ready; + }; + + // Allocate space for blocks until limitIndex (included). + bool maybeAllocateBlocks(size_t limitIndex); + + // Close the previous run. + void closePreviousRun(); + + // Move ready blocks to given IOBufQueue. + void moveReadyBlocks(IOBufQueue& buff); + + // Get block for given block index. + BlockWithState& getBlock(uint64_t blockIndex) { + CHECK_GE(blockIndex, blocks_.front().index); + return blocks_[blockIndex - blocks_.front().index]; + } + + // Number of key bits. + const size_t keyBits_; + const uint64_t maxKey_; + + // Total number of blocks. + size_t numBlocks_ = 0; + // Number of bytes per block. + size_t blockSize_ = 0; + // Divisor for mapping from keys to slots. + uint64_t divisor_; + // fraction_ = 1 / divisor_. + qms_detail::UInt64InverseType fraction_; + // Number of remainder bits. + uint64_t remainderBits_; + + size_t numKeys_ = 0; + size_t numRuns_ = 0; + + uint64_t prevKey_ = 0; + // Next slot to be used. + size_t nextSlot_ = 0; + // The actual start of previous run. + size_t prevRunStart_ = 0; + // The quotient of previous run. + size_t prevOccupiedQuotient_ = 0; + // Number of ready blocks in deque. + size_t readyBlocks_ = 0; + + // Contains blocks since last flush call. + std::deque<BlockWithState> blocks_; + + IOBufQueue buff_; +}; + +} // namespace folly + +#include <folly/experimental/QuotientMultiSet-inl.h> + +#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED diff --git a/ios/Pods/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h b/ios/Pods/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h new file mode 100644 index 000000000..a378db91e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h @@ -0,0 +1,452 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* -*- Mode: C++; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#pragma once + +#include <atomic> + +#include <folly/experimental/TLRefCount.h> + +namespace folly { + +template <typename T, typename RefCount> +class ReadMostlyMainPtr; +template <typename T, typename RefCount> +class ReadMostlyWeakPtr; +template <typename T, typename RefCount> +class ReadMostlySharedPtr; +template <typename RefCount> +class ReadMostlyMainPtrDeleter; + +using DefaultRefCount = TLRefCount; + +namespace detail { + +template <typename T, typename RefCount = DefaultRefCount> +class ReadMostlySharedPtrCore { + public: + T* get() { + return ptrRaw_; + } + + std::shared_ptr<T> getShared() { + return ptr_; + } + + bool incref() { + return ++count_ > 0; + } + + void decref() { + if (--count_ == 0) { + ptrRaw_ = nullptr; + ptr_.reset(); + + decrefWeak(); + } + } + + void increfWeak() { + auto value = ++weakCount_; + DCHECK_GT(value, 0); + } + + void decrefWeak() { + if (--weakCount_ == 0) { + delete this; + } + } + + size_t useCount() const { + return *count_; + } + + ~ReadMostlySharedPtrCore() noexcept { + assert(*count_ == 0); + assert(*weakCount_ == 0); + } + + private: + friend class ReadMostlyMainPtr<T, RefCount>; + friend class ReadMostlyMainPtrDeleter<RefCount>; + + explicit ReadMostlySharedPtrCore(std::shared_ptr<T> ptr) + : ptrRaw_(ptr.get()), ptr_(std::move(ptr)) {} + + T* ptrRaw_; + RefCount count_; + RefCount weakCount_; + std::shared_ptr<T> ptr_; +}; + +} // namespace detail + +template <typename T, typename RefCount = DefaultRefCount> +class ReadMostlyMainPtr { + public: + ReadMostlyMainPtr() {} + + explicit ReadMostlyMainPtr(std::shared_ptr<T> ptr) { + reset(std::move(ptr)); + } + + ReadMostlyMainPtr(const ReadMostlyMainPtr&) = delete; + ReadMostlyMainPtr& operator=(const ReadMostlyMainPtr&) = delete; + + ReadMostlyMainPtr(ReadMostlyMainPtr&& other) noexcept { + *this = std::move(other); + } + + ReadMostlyMainPtr& operator=(ReadMostlyMainPtr&& other) noexcept { + std::swap(impl_, other.impl_); + + return *this; + } + + bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const { + return get() == other.get(); + } + + bool operator==(T* other) const { + return get() == other; + } + + bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const { + return get() == other.get(); + } + + ~ReadMostlyMainPtr() noexcept { + reset(); + } + + void reset() noexcept { + if (impl_) { + impl_->count_.useGlobal(); + impl_->weakCount_.useGlobal(); + impl_->decref(); + impl_ = nullptr; + } + } + + void reset(std::shared_ptr<T> ptr) { + reset(); + if (ptr) { + impl_ = new detail::ReadMostlySharedPtrCore<T, RefCount>(std::move(ptr)); + } + } + + T* get() const { + if (impl_) { + return impl_->ptrRaw_; + } else { + return nullptr; + } + } + + std::shared_ptr<T> getStdShared() const { + if (impl_) { + return impl_->getShared(); + } else { + return {}; + } + } + + T& operator*() const { + return *get(); + } + + T* operator->() const { + return get(); + } + + ReadMostlySharedPtr<T, RefCount> getShared() const { + return ReadMostlySharedPtr<T, RefCount>(*this); + } + + explicit operator bool() const { + return impl_ != nullptr; + } + + private: + friend class ReadMostlyWeakPtr<T, RefCount>; + friend class ReadMostlySharedPtr<T, RefCount>; + friend class ReadMostlyMainPtrDeleter<RefCount>; + + detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr}; +}; + +template <typename T, typename RefCount = DefaultRefCount> +class ReadMostlyWeakPtr { + public: + ReadMostlyWeakPtr() {} + + explicit ReadMostlyWeakPtr(const ReadMostlyMainPtr<T, RefCount>& mainPtr) { + reset(mainPtr.impl_); + } + + explicit ReadMostlyWeakPtr(const ReadMostlySharedPtr<T, RefCount>& ptr) { + reset(ptr.impl_); + } + + ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) { + *this = other; + } + + ReadMostlyWeakPtr& operator=(const ReadMostlyWeakPtr& other) { + reset(other.impl_); + return *this; + } + + ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& mainPtr) { + reset(mainPtr.impl_); + return *this; + } + + ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept { + *this = other; + } + + ReadMostlyWeakPtr& operator=(ReadMostlyWeakPtr&& other) noexcept { + std::swap(impl_, other.impl_); + return *this; + } + + ~ReadMostlyWeakPtr() noexcept { + reset(nullptr); + } + + ReadMostlySharedPtr<T, RefCount> lock() { + return ReadMostlySharedPtr<T, RefCount>(*this); + } + + private: + friend class ReadMostlySharedPtr<T, RefCount>; + + void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) { + if (impl_) { + impl_->decrefWeak(); + } + impl_ = impl; + if (impl_) { + impl_->increfWeak(); + } + } + + detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr}; +}; + +template <typename T, typename RefCount = DefaultRefCount> +class ReadMostlySharedPtr { + public: + ReadMostlySharedPtr() {} + + explicit ReadMostlySharedPtr(const ReadMostlyWeakPtr<T, RefCount>& weakPtr) { + reset(weakPtr.impl_); + } + + // Generally, this shouldn't be used. + explicit ReadMostlySharedPtr(const ReadMostlyMainPtr<T, RefCount>& mainPtr) { + reset(mainPtr.impl_); + } + + ReadMostlySharedPtr(const ReadMostlySharedPtr& other) { + *this = other; + } + + ReadMostlySharedPtr& operator=(const ReadMostlySharedPtr& other) { + reset(other.impl_); + return *this; + } + + ReadMostlySharedPtr& operator=(const ReadMostlyWeakPtr<T, RefCount>& other) { + reset(other.impl_); + return *this; + } + + ReadMostlySharedPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& other) { + reset(other.impl_); + return *this; + } + + ReadMostlySharedPtr(ReadMostlySharedPtr&& other) noexcept { + *this = std::move(other); + } + + ~ReadMostlySharedPtr() noexcept { + reset(nullptr); + } + + ReadMostlySharedPtr& operator=(ReadMostlySharedPtr&& other) noexcept { + std::swap(ptr_, other.ptr_); + std::swap(impl_, other.impl_); + return *this; + } + + bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const { + return get() == other.get(); + } + + bool operator==(T* other) const { + return get() == other; + } + + bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const { + return get() == other.get(); + } + + void reset() { + reset(nullptr); + } + + T* get() const { + return ptr_; + } + + std::shared_ptr<T> getStdShared() const { + if (impl_) { + return impl_->getShared(); + } else { + return {}; + } + } + + T& operator*() const { + return *get(); + } + + T* operator->() const { + return get(); + } + + size_t use_count() const { + return impl_->useCount(); + } + + bool unique() const { + return use_count() == 1; + } + + explicit operator bool() const { + return impl_ != nullptr; + } + + private: + friend class ReadMostlyWeakPtr<T, RefCount>; + + void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) { + if (impl_) { + impl_->decref(); + impl_ = nullptr; + ptr_ = nullptr; + } + + if (impl && impl->incref()) { + impl_ = impl; + ptr_ = impl->get(); + } + } + + T* ptr_{nullptr}; + detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr}; +}; + +/** + * This can be used to destroy multiple ReadMostlyMainPtrs at once. + */ +template <typename RefCount = DefaultRefCount> +class ReadMostlyMainPtrDeleter { + public: + ~ReadMostlyMainPtrDeleter() noexcept { + RefCount::useGlobal(refCounts_); + for (auto& decref : decrefs_) { + decref(); + } + } + + template <typename T> + void add(ReadMostlyMainPtr<T, RefCount> ptr) noexcept { + if (!ptr.impl_) { + return; + } + + refCounts_.push_back(&ptr.impl_->count_); + refCounts_.push_back(&ptr.impl_->weakCount_); + decrefs_.push_back([impl = ptr.impl_] { impl->decref(); }); + ptr.impl_ = nullptr; + } + + private: + std::vector<RefCount*> refCounts_; + std::vector<folly::Function<void()>> decrefs_; +}; + +template <typename T, typename RefCount> +inline bool operator==( + const ReadMostlyMainPtr<T, RefCount>& ptr, + std::nullptr_t) { + return ptr.get() == nullptr; +} + +template <typename T, typename RefCount> +inline bool operator==( + std::nullptr_t, + const ReadMostlyMainPtr<T, RefCount>& ptr) { + return ptr.get() == nullptr; +} + +template <typename T, typename RefCount> +inline bool operator==( + const ReadMostlySharedPtr<T, RefCount>& ptr, + std::nullptr_t) { + return ptr.get() == nullptr; +} + +template <typename T, typename RefCount> +inline bool operator==( + std::nullptr_t, + const ReadMostlySharedPtr<T, RefCount>& ptr) { + return ptr.get() == nullptr; +} + +template <typename T, typename RefCount> +inline bool operator!=( + const ReadMostlyMainPtr<T, RefCount>& ptr, + std::nullptr_t) { + return !(ptr == nullptr); +} + +template <typename T, typename RefCount> +inline bool operator!=( + std::nullptr_t, + const ReadMostlyMainPtr<T, RefCount>& ptr) { + return !(ptr == nullptr); +} + +template <typename T, typename RefCount> +inline bool operator!=( + const ReadMostlySharedPtr<T, RefCount>& ptr, + std::nullptr_t) { + return !(ptr == nullptr); +} + +template <typename T, typename RefCount> +inline bool operator!=( + std::nullptr_t, + const ReadMostlySharedPtr<T, RefCount>& ptr) { + return !(ptr == nullptr); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h b/ios/Pods/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h new file mode 100644 index 000000000..48bfa600a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h @@ -0,0 +1,1221 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <climits> +#include <cmath> +#include <iomanip> +#include <iostream> +#include <mutex> + +#include <folly/Random.h> +#include <folly/SpinLock.h> +#include <folly/ThreadLocal.h> +#include <folly/detail/Futex.h> +#include <folly/lang/Align.h> +#include <folly/synchronization/Hazptr.h> +#include <folly/synchronization/WaitOptions.h> +#include <folly/synchronization/detail/Spin.h> + +/// ------ Concurrent Priority Queue Implementation ------ +// The concurrent priority queue implementation is based on the +// Mound data structure (Mounds: Array-Based Concurrent Priority Queues +// by Yujie Liu and Michael Spear, ICPP 2012) +// +/// --- Overview --- +// This relaxed implementation extends the Mound algorithm, and provides +// following features: +// - Arbitrary priorities. +// - Unbounded size. +// - Push, pop, empty, size functions. [TODO: Non-waiting and timed wait pop] +// - Supports blocking. +// - Fast and Scalable. +// +/// --- Mound --- +// A Mound is a heap where each element is a sorted linked list. +// First nodes in the lists maintain the heap property. Push randomly +// selects a leaf at the bottom level, then uses binary search to find +// a place to insert the new node to the head of the list. Pop gets +// the node from the head of the list at the root, then swap the +// list down until the heap feature holds. To use Mound in our +// implementation, we need to solve the following problems: +// - 1. Lack of general relaxed implementations. Mound is appealing +// for relaxed priority queue implementation because pop the whole +// list from the root is straightforward. One thread pops the list +// and following threads can pop from the list until its empty. +// Those pops only trigger one swap done operation. Thus reduce +// the latency for pop and reduce the contention for Mound. +// The difficulty is to provide a scalable and fast mechanism +// to let threads concurrently get elements from the list. +// - 2. Lack of control of list length. The length for every +// lists is critical for the performance. Mound suffers from not +// only the extreme cases(Push with increasing priorities, Mound +// becomes a sorted linked list; Push with decreasing priorities, +// Mound becomes to a regular heap), but also the common case(for +// random generated priorities, Mound degrades to the regular heap +// after millions of push/pop operations). The difficulty is to +// stabilize the list length without losing the accuracy and performance. +// - 3. Does not support blocking. Blocking is an important feature. +// Mound paper does not mention it. Designing the new algorithm for +// efficient blocking is challenging. +// - 4. Memory management. Mound allows optimistic reads. We need to +// protect the node from been reclaimed. +// +/// --- Design --- +// Our implementation extends Mound algorithm to support +// efficient relaxed pop. We employ a shared buffer algorithm to +// share the popped list. Our algorithm makes popping from shared +// buffer as fast as fetch_and_add. We improve the performance +// and compact the heap structure by stabilizing the size of each list. +// The implementation exposes the template parameter to set the +// preferred list length. Under the hood, we provide algorithms for +// fast inserting, pruning, and merging. The blocking algorithm is +// tricky. It allows one producer only wakes one consumer at a time. +// It also does not block the producer. For optimistic read, we use +// hazard pointer to protect the node from been reclaimed. We optimize the +// check-lock-check pattern by using test-test-and-set spin lock. + +/// --- Template Parameters: --- +// 1. PopBatch could be 0 or a positive integer. +// If it is 0, only pop one node at a time. +// This is the strict implementation. It guarantees the return +// priority is alway the highest. If it is > 0, we keep +// up to that number of nodes in a shared buffer to be consumed by +// subsequent pop operations. +// +// 2. ListTargetSize represents the minimal length for the list. It +// solves the problem when inserting to Mound with +// decreasing priority order (degrade to a heap). Moreover, +// it maintains the Mound structure stable after trillions of +// operations, which causes unbalanced problem in the original +// Mound algorithm. We set the prunning length and merging lengtyh +// based on this parameter. +// +/// --- Interface --- +// void push(const T& val) +// void pop(T& val) +// size_t size() +// bool empty() + +namespace folly { + +template < + typename T, + bool MayBlock = false, + bool SupportsSize = false, + size_t PopBatch = 16, + size_t ListTargetSize = 25, + typename Mutex = folly::SpinLock, + template <typename> class Atom = std::atomic> +class RelaxedConcurrentPriorityQueue { + // Max height of the tree + static constexpr uint32_t MAX_LEVELS = 32; + // The default minimum value + static constexpr T MIN_VALUE = std::numeric_limits<T>::min(); + + // Align size for the shared buffer node + static constexpr size_t Align = 1u << 7; + static constexpr int LevelForForceInsert = 3; + static constexpr int LevelForTraverseParent = 7; + + static_assert(PopBatch <= 256, "PopBatch must be <= 256"); + static_assert( + ListTargetSize >= 1 && ListTargetSize <= 256, + "TargetSize must be in the range [1, 256]"); + + // The maximal length for the list + static constexpr size_t PruningSize = ListTargetSize * 2; + // When pop from Mound, tree elements near the leaf + // level are likely be very small (the length of the list). When + // swapping down after pop a list, we check the size of the + // children to decide whether to merge them to their parent. + static constexpr size_t MergingSize = ListTargetSize; + + /// List Node structure + struct Node : public folly::hazptr_obj_base<Node, Atom> { + Node* next; + T val; + }; + + /// Mound Element (Tree node), head points to a linked list + struct MoundElement { + // Reading (head, size) without acquiring the lock + Atom<Node*> head; + Atom<size_t> size; + alignas(Align) Mutex lock; + MoundElement() { // initializer + head.store(nullptr, std::memory_order_relaxed); + size.store(0, std::memory_order_relaxed); + } + }; + + /// The pos strcture simplify the implementation + struct Position { + uint32_t level; + uint32_t index; + }; + + /// Node for shared buffer should be aligned + struct BufferNode { + alignas(Align) Atom<Node*> pnode; + }; + + /// Data members + + // Mound structure -> 2D array to represent a tree + MoundElement* levels_[MAX_LEVELS]; + // Record the current leaf level (root is 0) + Atom<uint32_t> bottom_; + // It is used when expanding the tree + Atom<uint32_t> guard_; + + // Mound with shared buffer + // Following two members are accessed by consumers + std::unique_ptr<BufferNode[]> shared_buffer_; + alignas(Align) Atom<int> top_loc_; + + /// Blocking algorithm + // Numbers of futexs in the array + static constexpr size_t NumFutex = 128; + // The index gap for accessing futex in the array + static constexpr size_t Stride = 33; + std::unique_ptr<folly::detail::Futex<Atom>[]> futex_array_; + alignas(Align) Atom<uint32_t> cticket_; + alignas(Align) Atom<uint32_t> pticket_; + + // Two counters to calculate size of the queue + alignas(Align) Atom<size_t> counter_p_; + alignas(Align) Atom<size_t> counter_c_; + + public: + /// Constructor + RelaxedConcurrentPriorityQueue() + : cticket_(1), pticket_(1), counter_p_(0), counter_c_(0) { + if (MayBlock) { + futex_array_.reset(new folly::detail::Futex<Atom>[NumFutex]); + } + + if (PopBatch > 0) { + top_loc_ = -1; + shared_buffer_.reset(new BufferNode[PopBatch]); + for (size_t i = 0; i < PopBatch; i++) { + shared_buffer_[i].pnode = nullptr; + } + } + bottom_.store(0, std::memory_order_relaxed); + guard_.store(0, std::memory_order_relaxed); + // allocate the root MoundElement and initialize Mound + levels_[0] = new MoundElement[1]; // default MM for MoundElement + for (uint32_t i = 1; i < MAX_LEVELS; i++) { + levels_[i] = nullptr; + } + } + + ~RelaxedConcurrentPriorityQueue() { + if (PopBatch > 0) { + deleteSharedBuffer(); + } + if (MayBlock) { + futex_array_.reset(); + } + Position pos; + pos.level = pos.index = 0; + deleteAllNodes(pos); + // default MM for MoundElement + for (int i = getBottomLevel(); i >= 0; i--) { + delete[] levels_[i]; + } + } + + void push(const T& val) { + moundPush(val); + if (SupportsSize) { + counter_p_.fetch_add(1, std::memory_order_relaxed); + } + } + + void pop(T& val) { + moundPop(val); + if (SupportsSize) { + counter_c_.fetch_add(1, std::memory_order_relaxed); + } + } + + /// Note: size() and empty() are guaranteed to be accurate only if + /// the queue is not changed concurrently. + /// Returns an estimate of the size of the queue + size_t size() { + DCHECK(SupportsSize); + size_t p = counter_p_.load(std::memory_order_acquire); + size_t c = counter_c_.load(std::memory_order_acquire); + return (p > c) ? p - c : 0; + } + + /// Returns true only if the queue was empty during the call. + bool empty() { + return isEmpty(); + } + + private: + uint32_t getBottomLevel() { + return bottom_.load(std::memory_order_acquire); + } + + /// This function is only called by the destructor + void deleteSharedBuffer() { + DCHECK(PopBatch > 0); + // delete nodes in the buffer + int loc = top_loc_.load(std::memory_order_relaxed); + while (loc >= 0) { + Node* n = shared_buffer_[loc--].pnode.load(std::memory_order_relaxed); + delete n; + } + // delete buffer + shared_buffer_.reset(); + } + + /// This function is only called by the destructor + void deleteAllNodes(const Position& pos) { + if (getElementSize(pos) == 0) { + // current list is empty, do not need to check + // its children again. + return; + } + + Node* curList = getList(pos); + setTreeNode(pos, nullptr); + while (curList != nullptr) { // reclaim nodes + Node* n = curList; + curList = curList->next; + delete n; + } + + if (!isLeaf(pos)) { + deleteAllNodes(leftOf(pos)); + deleteAllNodes(rightOf(pos)); + } + } + + /// Check the first node in TreeElement keeps the heap structure. + bool isHeap(const Position& pos) { + if (isLeaf(pos)) { + return true; + } + Position lchild = leftOf(pos); + Position rchild = rightOf(pos); + return isHeap(lchild) && isHeap(rchild) && + readValue(pos) >= readValue(lchild) && + readValue(pos) >= readValue(rchild); + } + + /// Current position is leaf? + FOLLY_ALWAYS_INLINE bool isLeaf(const Position& pos) { + return pos.level == getBottomLevel(); + } + + /// Current element is the root? + FOLLY_ALWAYS_INLINE bool isRoot(const Position& pos) { + return pos.level == 0; + } + + /// Locate the parent node + FOLLY_ALWAYS_INLINE Position parentOf(const Position& pos) { + Position res; + res.level = pos.level - 1; + res.index = pos.index / 2; + return res; + } + + /// Locate the left child + FOLLY_ALWAYS_INLINE Position leftOf(const Position& pos) { + Position res; + res.level = pos.level + 1; + res.index = pos.index * 2; + return res; + } + + /// Locate the right child + FOLLY_ALWAYS_INLINE Position rightOf(const Position& pos) { + Position res; + res.level = pos.level + 1; + res.index = pos.index * 2 + 1; + return res; + } + + /// get the list size in current MoundElement + FOLLY_ALWAYS_INLINE size_t getElementSize(const Position& pos) { + return levels_[pos.level][pos.index].size.load(std::memory_order_relaxed); + } + + /// Set the size of current MoundElement + FOLLY_ALWAYS_INLINE void setElementSize( + const Position& pos, + const uint32_t& v) { + levels_[pos.level][pos.index].size.store(v, std::memory_order_relaxed); + } + + /// Extend the tree level + void grow(uint32_t btm) { + while (true) { + if (guard_.fetch_add(1, std::memory_order_acq_rel) == 0) { + break; + } + // someone already expanded the tree + if (btm != getBottomLevel()) { + return; + } + std::this_thread::yield(); + } + // double check the bottom has not changed yet + if (btm != getBottomLevel()) { + guard_.store(0, std::memory_order_release); + return; + } + // create and initialize the new level + uint32_t tmp_btm = getBottomLevel(); + uint32_t size = 1 << (tmp_btm + 1); + MoundElement* new_level = new MoundElement[size]; // MM + levels_[tmp_btm + 1] = new_level; + bottom_.store(tmp_btm + 1, std::memory_order_release); + guard_.store(0, std::memory_order_release); + } + + /// TODO: optimization + // This function is important, it selects a position to insert the + // node, there are two execution paths when this function returns. + // 1. It returns a position with head node has lower priority than the target. + // Thus it could be potentially used as the starting element to do the binary + // search to find the fit position. (slow path) + // 2. It returns a position, which is not the best fit. + // But it prevents aggressively grow the Mound. (fast path) + Position selectPosition( + const T& val, + bool& path, + uint32_t& seed, + folly::hazptr_holder<Atom>& hptr) { + while (true) { + uint32_t b = getBottomLevel(); + int bound = 1 << b; // number of elements in this level + int steps = 1 + b * b; // probe the length + ++seed; + uint32_t index = seed % bound; + + for (int i = 0; i < steps; i++) { + int loc = (index + i) % bound; + Position pos; + pos.level = b; + pos.index = loc; + // the first round, we do the quick check + if (optimisticReadValue(pos, hptr) <= val) { + path = false; + seed = ++loc; + return pos; + } else if ( + b > LevelForForceInsert && getElementSize(pos) < ListTargetSize) { + // [fast path] conservative implementation + // it makes sure every tree element should + // have more than the given number of nodes. + seed = ++loc; + path = true; + return pos; + } + if (b != getBottomLevel()) { + break; + } + } + // failed too many times grow + if (b == getBottomLevel()) { + grow(b); + } + } + } + + /// Swap two Tree Elements (head, size) + void swapList(const Position& a, const Position& b) { + Node* tmp = getList(a); + setTreeNode(a, getList(b)); + setTreeNode(b, tmp); + + // need to swap the tree node meta-data + uint32_t sa = getElementSize(a); + uint32_t sb = getElementSize(b); + setElementSize(a, sb); + setElementSize(b, sa); + } + + FOLLY_ALWAYS_INLINE void lockNode(const Position& pos) { + levels_[pos.level][pos.index].lock.lock(); + } + + FOLLY_ALWAYS_INLINE void unlockNode(const Position& pos) { + levels_[pos.level][pos.index].lock.unlock(); + } + + FOLLY_ALWAYS_INLINE bool trylockNode(const Position& pos) { + return levels_[pos.level][pos.index].lock.try_lock(); + } + + FOLLY_ALWAYS_INLINE T + optimisticReadValue(const Position& pos, folly::hazptr_holder<Atom>& hptr) { + Node* tmp = hptr.get_protected(levels_[pos.level][pos.index].head); + return (tmp == nullptr) ? MIN_VALUE : tmp->val; + } + + // Get the value from the head of the list as the elementvalue + FOLLY_ALWAYS_INLINE T readValue(const Position& pos) { + Node* tmp = getList(pos); + return (tmp == nullptr) ? MIN_VALUE : tmp->val; + } + + FOLLY_ALWAYS_INLINE Node* getList(const Position& pos) { + return levels_[pos.level][pos.index].head.load(std::memory_order_relaxed); + } + + FOLLY_ALWAYS_INLINE void setTreeNode(const Position& pos, Node* t) { + levels_[pos.level][pos.index].head.store(t, std::memory_order_relaxed); + } + + // Merge two sorted lists + Node* mergeList(Node* base, Node* source) { + if (base == nullptr) { + return source; + } else if (source == nullptr) { + return base; + } + + Node *res, *p; + // choose the head node + if (base->val >= source->val) { + res = base; + base = base->next; + p = res; + } else { + res = source; + source = source->next; + p = res; + } + + while (base != nullptr && source != nullptr) { + if (base->val >= source->val) { + p->next = base; + base = base->next; + } else { + p->next = source; + source = source->next; + } + p = p->next; + } + if (base == nullptr) { + p->next = source; + } else { + p->next = base; + } + return res; + } + + /// Merge list t to the Element Position + void mergeListTo(const Position& pos, Node* t, const size_t& list_length) { + Node* head = getList(pos); + setTreeNode(pos, mergeList(head, t)); + uint32_t ns = getElementSize(pos) + list_length; + setElementSize(pos, ns); + } + + bool pruningLeaf(const Position& pos) { + if (getElementSize(pos) <= PruningSize) { + unlockNode(pos); + return true; + } + + int b = getBottomLevel(); + int leaves = 1 << b; + int cnodes = 0; + for (int i = 0; i < leaves; i++) { + Position tmp; + tmp.level = b; + tmp.index = i; + if (getElementSize(tmp) != 0) { + cnodes++; + } + if (cnodes > leaves * 2 / 3) { + break; + } + } + + if (cnodes <= leaves * 2 / 3) { + unlockNode(pos); + return true; + } + return false; + } + + /// Split the current list into two lists, + /// then split the tail list and merge to two children. + void startPruning(const Position& pos) { + if (isLeaf(pos) && pruningLeaf(pos)) { + return; + } + + // split the list, record the tail + Node* pruning_head = getList(pos); + int steps = ListTargetSize; // keep in the original list + for (int i = 0; i < steps - 1; i++) { + pruning_head = pruning_head->next; + } + Node* t = pruning_head; + pruning_head = pruning_head->next; + t->next = nullptr; + int tail_length = getElementSize(pos) - steps; + setElementSize(pos, steps); + + // split the tail list into two lists + // evenly merge to two children + if (pos.level != getBottomLevel()) { + // split the rest into two lists + int left_length = (tail_length + 1) / 2; + int right_length = tail_length - left_length; + Node *to_right, *to_left = pruning_head; + for (int i = 0; i < left_length - 1; i++) { + pruning_head = pruning_head->next; + } + to_right = pruning_head->next; + pruning_head->next = nullptr; + + Position lchild = leftOf(pos); + Position rchild = rightOf(pos); + if (left_length != 0) { + lockNode(lchild); + mergeListTo(lchild, to_left, left_length); + } + if (right_length != 0) { + lockNode(rchild); + mergeListTo(rchild, to_right, right_length); + } + unlockNode(pos); + if (left_length != 0 && getElementSize(lchild) > PruningSize) { + startPruning(lchild); + } else if (left_length != 0) { + unlockNode(lchild); + } + if (right_length != 0 && getElementSize(rchild) > PruningSize) { + startPruning(rchild); + } else if (right_length != 0) { + unlockNode(rchild); + } + } else { // time to grow the Mound + grow(pos.level); + // randomly choose a child to insert + if (steps % 2 == 1) { + Position rchild = rightOf(pos); + lockNode(rchild); + mergeListTo(rchild, pruning_head, tail_length); + unlockNode(pos); + unlockNode(rchild); + } else { + Position lchild = leftOf(pos); + lockNode(lchild); + mergeListTo(lchild, pruning_head, tail_length); + unlockNode(pos); + unlockNode(lchild); + } + } + } + + // This function insert the new node (always) at the head of the + // current list. It needs to lock the parent & current + // This function may cause the list becoming tooooo long, so we + // provide pruning algorithm. + bool regularInsert(const Position& pos, const T& val, Node* newNode) { + // insert to the root node + if (isRoot(pos)) { + lockNode(pos); + T nv = readValue(pos); + if (LIKELY(nv <= val)) { + newNode->next = getList(pos); + setTreeNode(pos, newNode); + uint32_t sz = getElementSize(pos); + setElementSize(pos, sz + 1); + if (UNLIKELY(sz > PruningSize)) { + startPruning(pos); + } else { + unlockNode(pos); + } + return true; + } + unlockNode(pos); + return false; + } + + // insert to an inner node + Position parent = parentOf(pos); + if (!trylockNode(parent)) { + return false; + } + if (!trylockNode(pos)) { + unlockNode(parent); + return false; + } + T pv = readValue(parent); + T nv = readValue(pos); + if (LIKELY(pv > val && nv <= val)) { + // improve the accuracy by getting the node(R) with less priority than the + // new value from parent level, insert the new node to the parent list + // and insert R to the current list. + // It only happens at >= LevelForTraverseParent for reducing contention + uint32_t sz = getElementSize(pos); + if (pos.level >= LevelForTraverseParent) { + Node* start = getList(parent); + while (start->next != nullptr && start->next->val >= val) { + start = start->next; + } + if (start->next != nullptr) { + newNode->next = start->next; + start->next = newNode; + while (start->next->next != nullptr) { + start = start->next; + } + newNode = start->next; + start->next = nullptr; + } + unlockNode(parent); + + Node* curList = getList(pos); + if (curList == nullptr) { + newNode->next = nullptr; + setTreeNode(pos, newNode); + } else { + Node* p = curList; + if (p->val <= newNode->val) { + newNode->next = curList; + setTreeNode(pos, newNode); + } else { + while (p->next != nullptr && p->next->val >= newNode->val) { + p = p->next; + } + newNode->next = p->next; + p->next = newNode; + } + } + setElementSize(pos, sz + 1); + } else { + unlockNode(parent); + newNode->next = getList(pos); + setTreeNode(pos, newNode); + setElementSize(pos, sz + 1); + } + if (UNLIKELY(sz > PruningSize)) { + startPruning(pos); + } else { + unlockNode(pos); + } + return true; + } + unlockNode(parent); + unlockNode(pos); + return false; + } + + bool forceInsertToRoot(Node* newNode) { + Position pos; + pos.level = pos.index = 0; + std::unique_lock<Mutex> lck( + levels_[pos.level][pos.index].lock, std::try_to_lock); + if (!lck.owns_lock()) { + return false; + } + uint32_t sz = getElementSize(pos); + if (sz >= ListTargetSize) { + return false; + } + + Node* curList = getList(pos); + if (curList == nullptr) { + newNode->next = nullptr; + setTreeNode(pos, newNode); + } else { + Node* p = curList; + if (p->val <= newNode->val) { + newNode->next = curList; + setTreeNode(pos, newNode); + } else { + while (p->next != nullptr && p->next->val >= newNode->val) { + p = p->next; + } + newNode->next = p->next; + p->next = newNode; + } + } + setElementSize(pos, sz + 1); + return true; + } + + // This function forces the new node inserting to the current position + // if the element does not hold the enough nodes. It is safe to + // lock just one position to insert, because it won't be the first + // node to sustain the heap structure. + bool forceInsert(const Position& pos, const T& val, Node* newNode) { + if (isRoot(pos)) { + return forceInsertToRoot(newNode); + } + + while (true) { + std::unique_lock<Mutex> lck( + levels_[pos.level][pos.index].lock, std::try_to_lock); + if (!lck.owns_lock()) { + if (getElementSize(pos) < ListTargetSize && readValue(pos) >= val) { + continue; + } else { + return false; + } + } + T nv = readValue(pos); + uint32_t sz = getElementSize(pos); + // do not allow the new node to be the first one + // do not allow the list size tooooo big + if (UNLIKELY(nv < val || sz >= ListTargetSize)) { + return false; + } + + Node* p = getList(pos); + // find a place to insert the node + while (p->next != nullptr && p->next->val > val) { + p = p->next; + } + newNode->next = p->next; + p->next = newNode; + // do not forget to change the metadata + setElementSize(pos, sz + 1); + return true; + } + } + + void binarySearchPosition( + Position& cur, + const T& val, + folly::hazptr_holder<Atom>& hptr) { + Position parent, mid; + if (cur.level == 0) { + return; + } + // start from the root + parent.level = parent.index = 0; + + while (true) { // binary search + mid.level = (cur.level + parent.level) / 2; + mid.index = cur.index >> (cur.level - mid.level); + + T mv = optimisticReadValue(mid, hptr); + if (val < mv) { + parent = mid; + } else { + cur = mid; + } + + if (mid.level == 0 || // the root + ((parent.level + 1 == cur.level) && parent.level != 0)) { + return; + } + } + } + + // The push keeps the length of each element stable + void moundPush(const T& val) { + Position cur; + folly::hazptr_holder<Atom> hptr; + Node* newNode = new Node; + newNode->val = val; + uint32_t seed = folly::Random::rand32() % (1 << 21); + + while (true) { + // shell we go the fast path? + bool go_fast_path = false; + // chooice the right node to start + cur = selectPosition(val, go_fast_path, seed, hptr); + if (go_fast_path) { + if (LIKELY(forceInsert(cur, val, newNode))) { + if (MayBlock) { + blockingPushImpl(); + } + return; + } else { + continue; + } + } + + binarySearchPosition(cur, val, hptr); + if (LIKELY(regularInsert(cur, val, newNode))) { + if (MayBlock) { + blockingPushImpl(); + } + return; + } + } + } + + int popToSharedBuffer(const uint32_t rsize, Node* head) { + Position pos; + pos.level = pos.index = 0; + + int num = std::min(rsize, (uint32_t)PopBatch); + for (int i = num - 1; i >= 0; i--) { + // wait until this block is empty + while (shared_buffer_[i].pnode.load(std::memory_order_relaxed) != nullptr) + ; + shared_buffer_[i].pnode.store(head, std::memory_order_relaxed); + head = head->next; + } + if (num > 0) { + top_loc_.store(num - 1, std::memory_order_release); + } + setTreeNode(pos, head); + return rsize - num; + } + + void mergeDown(const Position& pos) { + if (isLeaf(pos)) { + unlockNode(pos); + return; + } + + // acquire locks for L and R and compare + Position lchild = leftOf(pos); + Position rchild = rightOf(pos); + lockNode(lchild); + lockNode(rchild); + // read values + T nv = readValue(pos); + T lv = readValue(lchild); + T rv = readValue(rchild); + if (nv >= lv && nv >= rv) { + unlockNode(pos); + unlockNode(lchild); + unlockNode(rchild); + return; + } + + // If two children contains nodes less than the + // threshold, we merge two children to the parent + // and do merge down on both of them. + size_t sum = + getElementSize(rchild) + getElementSize(lchild) + getElementSize(pos); + if (sum <= MergingSize) { + Node* l1 = mergeList(getList(rchild), getList(lchild)); + setTreeNode(pos, mergeList(l1, getList(pos))); + setElementSize(pos, sum); + setTreeNode(lchild, nullptr); + setElementSize(lchild, 0); + setTreeNode(rchild, nullptr); + setElementSize(rchild, 0); + unlockNode(pos); + mergeDown(lchild); + mergeDown(rchild); + return; + } + // pull from right + if (rv >= lv && rv > nv) { + swapList(rchild, pos); + unlockNode(pos); + unlockNode(lchild); + mergeDown(rchild); + } else if (lv >= rv && lv > nv) { + // pull from left + swapList(lchild, pos); + unlockNode(pos); + unlockNode(rchild); + mergeDown(lchild); + } + } + + bool deferSettingRootSize(Position& pos) { + if (isLeaf(pos)) { + setElementSize(pos, 0); + unlockNode(pos); + return true; + } + + // acquire locks for L and R and compare + Position lchild = leftOf(pos); + Position rchild = rightOf(pos); + lockNode(lchild); + lockNode(rchild); + if (getElementSize(lchild) == 0 && getElementSize(rchild) == 0) { + setElementSize(pos, 0); + unlockNode(pos); + unlockNode(lchild); + unlockNode(rchild); + return true; + } else { + // read values + T lv = readValue(lchild); + T rv = readValue(rchild); + if (lv >= rv) { + swapList(lchild, pos); + setElementSize(lchild, 0); + unlockNode(pos); + unlockNode(rchild); + pos = lchild; + } else { + swapList(rchild, pos); + setElementSize(rchild, 0); + unlockNode(pos); + unlockNode(lchild); + pos = rchild; + } + return false; + } + } + + bool moundPopMany(T& val) { + // pop from the root + Position pos; + pos.level = pos.index = 0; + // the root is nullptr, return false + Node* head = getList(pos); + if (head == nullptr) { + unlockNode(pos); + return false; + } + + // shared buffer already filled by other threads + if (PopBatch > 0 && top_loc_.load(std::memory_order_acquire) >= 0) { + unlockNode(pos); + return false; + } + + uint32_t sz = getElementSize(pos); + // get the one node first + val = head->val; + Node* p = head; + head = head->next; + sz--; + + if (PopBatch > 0) { + sz = popToSharedBuffer(sz, head); + } else { + setTreeNode(pos, head); + } + + bool done = false; + if (LIKELY(sz == 0)) { + done = deferSettingRootSize(pos); + } else { + setElementSize(pos, sz); + } + + if (LIKELY(!done)) { + mergeDown(pos); + } + + p->retire(); + return true; + } + + void blockingPushImpl() { + auto p = pticket_.fetch_add(1, std::memory_order_acq_rel); + auto loc = getFutexArrayLoc(p); + uint32_t curfutex = futex_array_[loc].load(std::memory_order_acquire); + + while (true) { + uint32_t ready = p << 1; // get the lower 31 bits + // avoid the situation that push has larger ticket already set the value + if (UNLIKELY( + ready + 1 < curfutex || + ((curfutex > ready) && (curfutex - ready > 0x40000000)))) { + return; + } + + if (futex_array_[loc].compare_exchange_strong(curfutex, ready)) { + if (curfutex & + 1) { // One or more consumers may be blocked on this futex + detail::futexWake(&futex_array_[loc]); + } + return; + } else { + curfutex = futex_array_[loc].load(std::memory_order_acquire); + } + } + } + + // This could guarentee the Mound is empty + FOLLY_ALWAYS_INLINE bool isMoundEmpty() { + Position pos; + pos.level = pos.index = 0; + return getElementSize(pos) == 0; + } + + // Return true if the shared buffer is empty + FOLLY_ALWAYS_INLINE bool isSharedBufferEmpty() { + return top_loc_.load(std::memory_order_acquire) < 0; + } + + FOLLY_ALWAYS_INLINE bool isEmpty() { + if (PopBatch > 0) { + return isMoundEmpty() && isSharedBufferEmpty(); + } + return isMoundEmpty(); + } + + FOLLY_ALWAYS_INLINE bool futexIsReady(const size_t& curticket) { + auto loc = getFutexArrayLoc(curticket); + auto curfutex = futex_array_[loc].load(std::memory_order_acquire); + uint32_t short_cticket = curticket & 0x7FFFFFFF; + uint32_t futex_ready = curfutex >> 1; + // handle unsigned 31 bits overflow + return futex_ready >= short_cticket || + short_cticket - futex_ready > 0x40000000; + } + + template <typename Clock, typename Duration> + FOLLY_NOINLINE bool trySpinBeforeBlock( + const size_t& curticket, + const std::chrono::time_point<Clock, Duration>& deadline, + const folly::WaitOptions& opt = wait_options()) { + return folly::detail::spin_pause_until(deadline, opt, [=] { + return futexIsReady(curticket); + }) == folly::detail::spin_result::success; + } + + void tryBlockingPop(const size_t& curticket) { + auto loc = getFutexArrayLoc(curticket); + auto curfutex = futex_array_[loc].load(std::memory_order_acquire); + if (curfutex & + 1) { /// The last round consumers are still waiting, go to sleep + detail::futexWait(&futex_array_[loc], curfutex); + } + if (trySpinBeforeBlock( + curticket, + std::chrono::time_point<std::chrono::steady_clock>::max())) { + return; /// Spin until the push ticket is ready + } + while (true) { + curfutex = futex_array_[loc].load(std::memory_order_acquire); + if (curfutex & + 1) { /// The last round consumers are still waiting, go to sleep + detail::futexWait(&futex_array_[loc], curfutex); + } else if (!futexIsReady(curticket)) { // current ticket < pop ticket + uint32_t blocking_futex = curfutex + 1; + if (futex_array_[loc].compare_exchange_strong( + curfutex, blocking_futex)) { + detail::futexWait(&futex_array_[loc], blocking_futex); + } + } else { + return; + } + } + } + + void blockingPopImpl() { + auto ct = cticket_.fetch_add(1, std::memory_order_acq_rel); + // fast path check + if (futexIsReady(ct)) { + return; + } + // Blocking + tryBlockingPop(ct); + } + + bool tryPopFromMound(T& val) { + if (isMoundEmpty()) { + return false; + } + Position pos; + pos.level = pos.index = 0; + + // lock the root + if (trylockNode(pos)) { + return moundPopMany(val); + } + return false; + } + + FOLLY_ALWAYS_INLINE static folly::WaitOptions wait_options() { + return {}; + } + + template <typename Clock, typename Duration> + FOLLY_NOINLINE bool tryWait( + const std::chrono::time_point<Clock, Duration>& deadline, + const folly::WaitOptions& opt = wait_options()) { + // Fast path, by quick check the status + switch (folly::detail::spin_pause_until( + deadline, opt, [=] { return !isEmpty(); })) { + case folly::detail::spin_result::success: + return true; + case folly::detail::spin_result::timeout: + return false; + case folly::detail::spin_result::advance: + break; + } + + // Spinning strategy + while (true) { + auto res = + folly::detail::spin_yield_until(deadline, [=] { return !isEmpty(); }); + if (res == folly::detail::spin_result::success) { + return true; + } else if (res == folly::detail::spin_result::timeout) { + return false; + } + } + return true; + } + + bool tryPopFromSharedBuffer(T& val) { + int get_or = -1; + if (!isSharedBufferEmpty()) { + get_or = top_loc_.fetch_sub(1, std::memory_order_acq_rel); + if (get_or >= 0) { + Node* c = shared_buffer_[get_or].pnode.load(std::memory_order_relaxed); + shared_buffer_[get_or].pnode.store(nullptr, std::memory_order_release); + val = c->val; + c->retire(); + return true; + } + } + return false; + } + + size_t getFutexArrayLoc(size_t s) { + return ((s - 1) * Stride) & (NumFutex - 1); + } + + void moundPop(T& val) { + if (MayBlock) { + blockingPopImpl(); + } + + if (PopBatch > 0) { + if (tryPopFromSharedBuffer(val)) { + return; + } + } + + while (true) { + if (LIKELY(tryPopFromMound(val))) { + return; + } + tryWait(std::chrono::time_point<std::chrono::steady_clock>::max()); + if (PopBatch > 0 && tryPopFromSharedBuffer(val)) { + return; + } + } + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h b/ios/Pods/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h new file mode 100644 index 000000000..dbc2f99d2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <folly/experimental/TimerFD.h> +#include <folly/io/async/TimeoutManager.h> + +namespace folly { +// single timeout timerfd based TimeoutManager +class STTimerFDTimeoutManager : public TimeoutManager, TimerFD { + public: + explicit STTimerFDTimeoutManager(folly::EventBase* eventBase); + ~STTimerFDTimeoutManager() override; + + /** + * Attaches/detaches TimeoutManager to AsyncTimeout + */ + void attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) final; + void detachTimeoutManager(AsyncTimeout* obj) final; + + /** + * Schedules AsyncTimeout to fire after `timeout` milliseconds + */ + bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) final; + + /** + * Schedules AsyncTimeout to fire after `timeout` microseconds + */ + bool scheduleTimeoutHighRes(AsyncTimeout* obj, timeout_type_high_res timeout) + final; + + /** + * Cancels the AsyncTimeout, if scheduled + */ + void cancelTimeout(AsyncTimeout* obj) final; + + /** + * This is used to mark the beginning of a new loop cycle by the + * first handler fired within that cycle. + */ + void bumpHandlingTime() final; + + /** + * Helper method to know whether we are running in the timeout manager + * thread + */ + bool isInTimeoutManagerThread() final { + return eventBase_->isInEventBaseThread(); + } + + // from TimerFD + void onTimeout() noexcept final; + + private: + static void setActive(AsyncTimeout* obj, bool active); + + folly::EventBase* eventBase_{nullptr}; + AsyncTimeout* obj_{nullptr}; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/Select64.h b/ios/Pods/Flipper-Folly/folly/experimental/Select64.h new file mode 100644 index 000000000..286af17b2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/Select64.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <array> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/experimental/Instructions.h> + +namespace folly { + +namespace detail { + +// kSelectInByte +// +// Described in: +// http://dsiutils.di.unimi.it/docs/it/unimi/dsi/bits/Fast.html#selectInByte +// +// A precomputed tabled containing the positions of the set bits in the binary +// representations of all 8-bit unsigned integers. +// +// For i: [0, 256) ranging over all 8-bit unsigned integers and for j: [0, 8) +// ranging over all 0-based bit positions in an 8-bit unsigned integer, the +// table entry kSelectInByte[i][j] is the 0-based bit position of the j-th set +// bit in the binary representation of i, or 8 if it has fewer than j set bits. +// +// Example: i: 17 (b00010001), j: [0, 8) +// kSelectInByte[b00010001][0] = 0 +// kSelectInByte[b00010001][1] = 4 +// kSelectInByte[b00010001][2] = 8 +// ... +// kSelectInByte[b00010001][7] = 8 +extern std::array<std::array<std::uint8_t, 256>, 8> const kSelectInByte; + +} // namespace detail + +/** + * Returns the position of the k-th 1 in the 64-bit word x. + * k is 0-based, so k=0 returns the position of the first 1. + * + * Uses the broadword selection algorithm by Vigna [1], improved by Gog + * and Petri [2] and Vigna [3]. + * + * [1] Sebastiano Vigna. Broadword Implementation of Rank/Select + * Queries. WEA, 2008 + * + * [2] Simon Gog, Matthias Petri. Optimized succinct data structures + * for massive data. Softw. Pract. Exper., 2014 + * + * [3] Sebastiano Vigna. MG4J 5.2.1. http://mg4j.di.unimi.it/ + */ +template <class Instructions> +inline uint64_t select64(uint64_t x, uint64_t k) { + DCHECK_LT(k, Instructions::popcount(x)); + + constexpr uint64_t kOnesStep4 = 0x1111111111111111ULL; + constexpr uint64_t kOnesStep8 = 0x0101010101010101ULL; + constexpr uint64_t kMSBsStep8 = 0x80ULL * kOnesStep8; + + auto s = x; + s = s - ((s & 0xA * kOnesStep4) >> 1); + s = (s & 0x3 * kOnesStep4) + ((s >> 2) & 0x3 * kOnesStep4); + s = (s + (s >> 4)) & 0xF * kOnesStep8; + uint64_t byteSums = s * kOnesStep8; + + uint64_t kStep8 = k * kOnesStep8; + uint64_t geqKStep8 = (((kStep8 | kMSBsStep8) - byteSums) & kMSBsStep8); + uint64_t place = Instructions::popcount(geqKStep8) * 8; + uint64_t byteRank = k - (((byteSums << 8) >> place) & uint64_t(0xFF)); + return place + detail::kSelectInByte[byteRank][((x >> place) & 0xFF)]; +} + +template <> +FOLLY_ALWAYS_INLINE uint64_t +select64<compression::instructions::Haswell>(uint64_t x, uint64_t k) { +#if defined(__GNUC__) + // GCC and Clang won't inline the intrinsics. + uint64_t result = uint64_t(1) << k; + + asm("pdep %1, %0, %0\n\t" + "tzcnt %0, %0" + : "+r"(result) + : "r"(x)); + + return result; +#else + return _tzcnt_u64(_pdep_u64(1ULL << k, x)); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h b/ios/Pods/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h new file mode 100644 index 000000000..63022965f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/lang/Bits.h> +#include <glog/logging.h> + +#include <atomic> + +namespace folly { + +/// SingleWriterFixedHashMap: +/// +/// Minimal single-writer fixed hash map implementation that supports: +/// - Copy construction with optional capacity expansion. +/// - Concurrent read-only lookup. +/// - Concurrent read-only iteration. +/// +/// Assumes that higher level code: +/// - Checks availability of empty slots before calling insert +/// - Manages expansion and/or cleanup of tombstones +/// +/// Notes on algorithm: +/// - Tombstones are used to mark previously occupied slots. +/// - A slot with a tombstone can only be reused for the same key. The +/// reason for that is to enforce that once a key occupies a slot, +/// that key cannot use any other slot for the lifetime of the +/// map. This is to guarantee that when readers iterate over the map +/// they do not encounter any key more than once. +/// +/// Writer-only operations: +/// - insert() +/// - erase() +/// - used() +/// - available() +/// +template <typename Key, typename Value> +class SingleWriterFixedHashMap { +#if __cpp_lib_atomic_is_always_lock_free + static_assert( + std::atomic<Value>::is_always_lock_free, + "This implementation depends on having fast atomic " + "data-race-free loads and stores of Value type."); +#endif + static_assert( + std::is_trivial<Key>::value, + "This implementation depends on using a single key instance " + "for all insert and erase operations. The reason is to allow " + "readers to read keys data-race-free concurrently with possible " + "concurrent insert and erase operations on the keys."); + + class Elem; + + enum class State : uint8_t { EMPTY, VALID, TOMBSTONE }; + + size_t capacity_; + size_t used_{0}; + std::atomic<size_t> size_{0}; + std::unique_ptr<Elem[]> elem_; + + public: + class Iterator; + + explicit SingleWriterFixedHashMap(size_t capacity) + : capacity_(folly::nextPowTwo(capacity)) {} + + explicit SingleWriterFixedHashMap( + size_t capacity, + const SingleWriterFixedHashMap& o) + : capacity_(folly::nextPowTwo(capacity)) { + if (o.empty()) { + return; + } + elem_ = std::make_unique<Elem[]>(capacity_); + for (size_t i = 0; i < o.capacity_; ++i) { + Elem& e = o.elem_[i]; + if (e.valid()) { + insert(e.key(), e.value()); + } + } + } + + FOLLY_ALWAYS_INLINE Iterator begin() const { + return empty() ? end() : Iterator(*this); + } + + FOLLY_ALWAYS_INLINE Iterator end() const { + return Iterator(*this, capacity_); + } + + size_t capacity() const { + return capacity_; + } + + /* not data-race-free, to be called only by the single writer */ + size_t used() const { + return used_; + } + + /* not-data race-free, to be called only by the single writer */ + size_t available() const { + return capacity_ - used_; + } + + /* data-race-free, can be called by readers */ + FOLLY_ALWAYS_INLINE size_t size() const { + return size_.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE bool empty() const { + return size() == 0; + } + + bool insert(Key key, Value value) { + if (!elem_) { + elem_ = std::make_unique<Elem[]>(capacity_); + } + DCHECK_LT(used_, capacity_); + if (writer_find(key) < capacity_) { + return false; + } + size_t index = hash(key); + auto attempts = capacity_; + size_t mask = capacity_ - 1; + while (attempts--) { + Elem& e = elem_[index]; + auto state = e.state(); + if (state == State::EMPTY || + (state == State::TOMBSTONE && e.key() == key)) { + if (state == State::EMPTY) { + e.setKey(key); + ++used_; + DCHECK_LE(used_, capacity_); + } + e.setValue(value); + e.setValid(); + setSize(size() + 1); + DCHECK_LE(size(), used_); + return true; + } + index = (index + 1) & mask; + } + CHECK(false) << "No available slots"; + folly::assume_unreachable(); + } + + void erase(Iterator& it) { + DCHECK_NE(it, end()); + Elem& e = elem_[it.index_]; + erase_internal(e); + } + + bool erase(Key key) { + size_t index = writer_find(key); + if (index == capacity_) { + return false; + } + Elem& e = elem_[index]; + erase_internal(e); + return true; + } + + FOLLY_ALWAYS_INLINE Iterator find(Key key) const { + size_t index = reader_find(key); + return Iterator(*this, index); + } + + FOLLY_ALWAYS_INLINE bool contains(Key key) const { + return reader_find(key) < capacity_; + } + + private: + FOLLY_ALWAYS_INLINE size_t hash(Key key) const { + size_t mask = capacity_ - 1; + size_t index = std::hash<Key>()(key) & mask; + DCHECK_LT(index, capacity_); + return index; + } + + void setSize(size_t size) { + size_.store(size, std::memory_order_release); + } + + FOLLY_ALWAYS_INLINE size_t reader_find(Key key) const { + return find_internal(key); + } + + size_t writer_find(Key key) { + return find_internal(key); + } + + FOLLY_ALWAYS_INLINE size_t find_internal(Key key) const { + if (!empty()) { + size_t index = hash(key); + auto attempts = capacity_; + size_t mask = capacity_ - 1; + while (attempts--) { + Elem& e = elem_[index]; + auto state = e.state(); + if (state == State::VALID && e.key() == key) { + return index; + } + if (state == State::EMPTY) { + break; + } + index = (index + 1) & mask; + } + } + return capacity_; + } + + void erase_internal(Elem& e) { + e.erase(); + DCHECK_GT(size(), 0); + setSize(size() - 1); + } + + /// Elem + class Elem { + std::atomic<State> state_; + Key key_; + std::atomic<Value> value_; + + public: + Elem() : state_(State::EMPTY) {} + + FOLLY_ALWAYS_INLINE State state() const { + return state_.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE bool valid() const { + return state() == State::VALID; + } + + FOLLY_ALWAYS_INLINE Key key() const { + return key_; + } + + FOLLY_ALWAYS_INLINE Value value() const { + return value_.load(std::memory_order_relaxed); + } + + void setKey(Key key) { + key_ = key; + } + + void setValue(Value value) { + value_.store(value, std::memory_order_relaxed); + } + + void setValid() { + state_.store(State::VALID, std::memory_order_release); + } + + void erase() { + state_.store(State::TOMBSTONE, std::memory_order_release); + } + }; // Elem + + public: + /// Iterator + class Iterator { + Elem* elem_; + size_t capacity_; + size_t index_; + + public: + FOLLY_ALWAYS_INLINE Key key() const { + DCHECK_LT(index_, capacity_); + Elem& e = elem_[index_]; + return e.key(); + } + + FOLLY_ALWAYS_INLINE Value value() const { + DCHECK_LT(index_, capacity_); + Elem& e = elem_[index_]; + return e.value(); + } + + FOLLY_ALWAYS_INLINE Iterator& operator++() { + DCHECK_LT(index_, capacity_); + ++index_; + next(); + return *this; + } + + FOLLY_ALWAYS_INLINE bool operator==(const Iterator& o) const { + DCHECK(elem_ == o.elem_ || elem_ == nullptr || o.elem_ == nullptr); + DCHECK_EQ(capacity_, o.capacity_); + DCHECK_LE(index_, capacity_); + return index_ == o.index_; + } + + FOLLY_ALWAYS_INLINE bool operator!=(const Iterator& o) const { + return !(*this == o); + } + + private: + friend class SingleWriterFixedHashMap; + + explicit Iterator(const SingleWriterFixedHashMap& m, size_t i = 0) + : elem_(m.elem_.get()), capacity_(m.capacity_), index_(i) { + if (index_ < capacity_) { + next(); + } + } + + FOLLY_ALWAYS_INLINE void next() { + while (index_ < capacity_ && !elem_[index_].valid()) { + ++index_; + } + } + }; // Iterator +}; // SingleWriterFixedHashMap + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h b/ios/Pods/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h new file mode 100644 index 000000000..ef3c21fe0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <array> +#include <atomic> +#include <type_traits> +#include <unordered_set> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Synchronized.h> +#include <folly/Utility.h> +#include <folly/detail/StaticSingletonManager.h> +#include <folly/detail/ThreadLocalDetail.h> + +namespace folly { + +// SingletonRelaxedCounter +// +// A singleton-per-tag relaxed counter. Optimized for increment/decrement +// runtime performance under contention and inlined fast path code size. +// +// The cost of computing the value of the counter is linear in the number of +// threads which perform increments/decrements, and computing the value of the +// counter is exclusive with thread exit and dlclose. The result of this +// computation is not a point-in-time snapshot of increments and decrements +// summed, but is an approximation which may exclude any subset of increments +// and decrements that do not happen before the start of the computation. +// +// Templated over the integral types. When templated over an unsigned integral +// type, it is assumed that decrements do not exceed increments, and if within +// computation of the value of the counter more decrements are observed to +// exceed increments then the excess decrements are ignored. This avoids the +// scenario of incrementing and decrementing once each in different threads, +// and concurrently observing a computed value of the counter of 2^64 - 1. +// +// Templated over the tag types. Each unique pair of integral type and tag type +// is a different counter. +// +// Implementation: +// Uses a thread-local counter when possible to avoid contention, and a global +// counter as a fallback. The total count at any given time is computed by +// summing over the global counter plus all of the thread-local counters; since +// the total sum is not a snapshot of the value at any given point in time, it +// is a relaxed sum; when the system quiesces (i.e., when no concurrent +// increments or decrements are happening and no threads are going through +// thread exit phase), the sum is exact. +template <typename Int, typename Tag> +class SingletonRelaxedCounter { + public: + static void add(Int value) { + mutate(+to_signed(value)); + } + static void sub(Int value) { + mutate(-to_signed(value)); + } + + static Int count() { + auto const& global = Global::instance(); + auto count = global.fallback.load(std::memory_order_relaxed); + auto const tracking = global.tracking.rlock(); + for (auto const& kvp : tracking->locals) { + count += kvp.first->load(std::memory_order_relaxed); + } + return std::is_unsigned<Int>::value + ? to_unsigned(std::max(Signed(0), count)) + : count; + } + + private: + using Signed = std::make_signed_t<Int>; + using Counter = std::atomic<Signed>; + + struct CounterAndCache { + Counter counter; // valid during LocalLifetime object lifetime + Counter* cache; // points to counter when counter is valid + }; + + struct CounterRefAndLocal { + Counter* counter; // refers either to local counter or to global counter + bool local; // if true, definitely local; if false, could be global + }; + + struct LocalLifetime; + + struct Global { + struct Tracking { + using CounterSet = std::unordered_set<Counter*>; + std::unordered_map<Counter*, size_t> locals; // for summing + std::unordered_map<LocalLifetime*, CounterSet> lifetimes; + }; + + Counter fallback; // used instead of local during thread destruction + folly::Synchronized<Tracking> tracking; + + static Global& instance() { + return folly::detail::createGlobal<Global, Tag>(); + } + }; + + // manages local().cache, global().tracking, and moving outstanding counts + // from local().counter to global().counter during thread destruction + // + // the counter-set is within Global to reduce per-thread overhead for threads + // which do not participate in counter mutations, rather than being a member + // field of LocalLifetime; this comes at the cost of the slow path always + // acquiring a unique lock on the global mutex + struct LocalLifetime { + ~LocalLifetime() { + auto& global = Global::instance(); + auto const tracking = global.tracking.wlock(); + auto& lifetimes = tracking->lifetimes[this]; + for (auto ctr : lifetimes) { + auto const it = tracking->locals.find(ctr); + if (!--it->second) { + tracking->locals.erase(it); + auto const current = ctr->load(std::memory_order_relaxed); + global.fallback.fetch_add(current, std::memory_order_relaxed); + } + } + tracking->lifetimes.erase(this); + } + + void track(CounterAndCache& state) { + auto& global = Global::instance(); + state.cache = &state.counter; + auto const tracking = global.tracking.wlock(); + auto const inserted = tracking->lifetimes[this].insert(&state.counter); + tracking->locals[&state.counter] += inserted.second; + } + }; + + FOLLY_ALWAYS_INLINE static void mutate(Signed v) { + auto cl = counter(); + auto& c = *cl.counter; + if (cl.local) { + // splitting load/store on the local counter is faster than fetch-and-add + c.store(c.load(std::memory_order_relaxed) + v, std::memory_order_relaxed); + } else { + // but is not allowed on the global counter because mutations may be lost + c.fetch_add(v, std::memory_order_relaxed); + } + } + + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static CounterAndCache& local() { + // this is a member function local instead of a class member because of + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66944 + static thread_local CounterAndCache instance; + return instance; + } + + FOLLY_EXPORT FOLLY_ALWAYS_INLINE static LocalLifetime& lifetime() { + static thread_local LocalLifetime lifetime; + return lifetime; + } + + FOLLY_NOINLINE static Counter* counterSlow(CounterAndCache& state) { + if (threadlocal_detail::StaticMetaBase::dying()) { + return &Global::instance().fallback; + } + lifetime().track(state); // idempotent + auto const cache = state.cache; + return FOLLY_LIKELY(!!cache) ? cache : &Global::instance().fallback; + } + + FOLLY_ALWAYS_INLINE static CounterRefAndLocal counter() { + auto& state = local(); + auto const cache = state.cache; // a copy! null before/after LocalLifetime + auto const counter = FOLLY_LIKELY(!!cache) ? cache : counterSlow(state); + // cache is a stale nullptr after the first call to counterSlow(); this is + // intentional for the side-effect of shrinking the inline fast path + return CounterRefAndLocal{counter, !!cache}; + } +}; + +template <typename Counted> +class SingletonRelaxedCountableAccess; + +// SingletonRelaxedCountable +// +// A CRTP base class for making the instances of a type within a process be +// globally counted. The running counter is a relaxed counter. +// +// To avoid adding any new names from the base class to the counted type, the +// count is exposed via a separate type SingletonRelaxedCountableAccess. +// +// This type is a convenience interface around SingletonRelaxedCounter. +template <typename Counted> +class SingletonRelaxedCountable { + public: + SingletonRelaxedCountable() { + static_assert( + std::is_base_of<SingletonRelaxedCountable, Counted>::value, "non-crtp"); + Counter::add(1); + } + ~SingletonRelaxedCountable() { + static_assert( + std::is_base_of<SingletonRelaxedCountable, Counted>::value, "non-crtp"); + Counter::sub(1); + } + + SingletonRelaxedCountable(const SingletonRelaxedCountable&) + : SingletonRelaxedCountable() {} + SingletonRelaxedCountable(SingletonRelaxedCountable&&) + : SingletonRelaxedCountable() {} + + SingletonRelaxedCountable& operator=(const SingletonRelaxedCountable&) = + default; + SingletonRelaxedCountable& operator=(SingletonRelaxedCountable&&) = default; + + private: + friend class SingletonRelaxedCountableAccess<Counted>; + + struct Tag; + using Counter = SingletonRelaxedCounter<size_t, Tag>; +}; + +// SingletonRelaxedCountableAccess +// +// Provides access to the running count of instances of a type using the CRTP +// base class SingletonRelaxedCountable. +template <typename Counted> +class SingletonRelaxedCountableAccess { + public: + static size_t count() { + return SingletonRelaxedCountable<Counted>::Counter::count(); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StampedPtr.h b/ios/Pods/Flipper-Folly/folly/experimental/StampedPtr.h new file mode 100644 index 000000000..d150941ff --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StampedPtr.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/lang/SafeAssert.h> + +#include <stdint.h> + +namespace folly { + +/** + * StampedPtr packs both a pointer to T and a uint16_t into a 64-bit value, + * exploiting the fact that current addresses are limited to 48 bits on + * all current x86-64 and ARM64 processors. + * + * For both x86-64 and ARM64, 64-bit pointers have a canonical + * form in which the upper 16 bits are equal to bit 47. Intel has + * announced a 57-bit addressing mode (see https://software.intel.com/ + * sites/default/files/managed/2b/80/5-level_paging_white_paper.pdf), + * but it is not yet available. The first problematic platform will + * probably be ARMv8.2, which supports 52-bit virtual addresses. + * + * This code works on all of the platforms I have available for test, + * and probably on all currently-shipping platforms that have a hope of + * compiling folly. Rather than enumerating the supported platforms via + * ifdef, this code dynamically validates its packing assumption in debug + * builds on each call to a mutating function. Presumably by the time we + * are running this process in an operating system image that can address + * more than 256TB of memory, RAM cost and the latency of 128-bit CAS + * will have improved enough that this optimization is no longer impactful. + * + * A common approach to this kind of packing seems to be to just assume + * the top 16 bits are zero, but https://github.com/LuaJIT/LuaJIT/issues/49 + * indicates that ARM64 platforms in the wild are actually setting bit 47 + * in their stack addresses. That means that we need to extend bit 47 to + * do the right thing (it's not expensive, it compiles to one instruction + * on x86-64 and arm64). + * + * Compare to PackedSyncPtr and DiscriminatedPtr, which perform similar + * packing but add additional functionality. The name is taken from + * Java's AtomicStampedReference. Unlike PackedSyncPtr, which tries to + * act pointer-like, this class acts more like a pair whose elements are + * named ptr and stamp. It also allows direct access to the internal + * raw field: since we're already at the metal you might want to play + * additional games. It is guaranteed that a zero raw value gets decoded + * as a (ptr,stamp) of (nullptr,0). + */ +template <typename T> +struct StampedPtr { + /** + * The packing is not guaranteed, except that it is guaranteed that + * raw == 0 iff ptr() == nullptr && stamp() == 0. + */ + uint64_t raw; + + /* IMPORTANT: default initialization doesn't result in a sane state */ + + T* ptr() const { + return unpackPtr(raw); + } + + uint16_t stamp() const { + return unpackStamp(raw); + } + + void set(T* ptr, uint16_t stamp) { + raw = pack(ptr, stamp); + } + + void setPtr(T* ptr) { + raw = pack(ptr, unpackStamp(raw)); + } + + void setStamp(uint16_t stamp) { + raw = pack(unpackPtr(raw), stamp); + } + + static T* unpackPtr(uint64_t raw) { + // Canonical form means we need to extend bit 47 of the pointer to + // bits 48..63 (unless the operating system never hands those pointers + // to us, which is difficult to prove). Signed right-shift of a + // negative number is implementation-defined in C++ (not undefined!), + // but actually does the right thing on all the platforms I can find. + auto extended = static_cast<int64_t>(raw) >> kInternalStampBits; + return reinterpret_cast<T*>(static_cast<intptr_t>(extended)); + } + + static uint16_t unpackStamp(uint64_t raw) { + return static_cast<uint16_t>(raw); + } + + static uint64_t pack(T* ptr, uint16_t stamp) { + auto shifted = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)) + << kInternalStampBits; + uint64_t raw = shifted | stamp; + FOLLY_SAFE_DCHECK(unpackPtr(raw) == ptr, "ptr mismatch."); + FOLLY_SAFE_DCHECK(unpackStamp(raw) == stamp, "stamp mismatch."); + return raw; + } + + private: + // On 32-bit platforms it works okay to store a ptr in the top 48 + // bits of a 64-bit value, but it will result in unnecessary work. + // If we align the pointer part at word granularity when we have the + // space then no shifting will ever be needed. + static constexpr unsigned kInternalStampBits = sizeof(void*) == 4 ? 32 : 16; +}; + +template <typename T> +StampedPtr<T> makeStampedPtr(T* ptr, uint16_t stamp) { + return StampedPtr<T>{StampedPtr<T>::pack(ptr, stamp)}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedCommon.h b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedCommon.h new file mode 100644 index 000000000..4fbb4f943 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedCommon.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Pavlo Kushnir (pavlo) + +#pragma once + +#include <memory> + +#include <folly/Range.h> + +namespace folly { + +template <class Alloc> +StringPiece stringPieceDup(StringPiece piece, const Alloc& alloc) { + auto size = piece.size(); + auto keyDup = + typename Alloc::template rebind<char>::other(alloc).allocate(size); + if (size) { + memcpy( + keyDup, piece.data(), size * sizeof(typename StringPiece::value_type)); + } + return StringPiece(keyDup, size); +} + +template <class Alloc> +void stringPieceDel(StringPiece piece, const Alloc& alloc) { + typename Alloc::template rebind<char>::other(alloc).deallocate( + const_cast<char*>(piece.data()), piece.size()); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedMap.h b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedMap.h new file mode 100644 index 000000000..183d5435a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedMap.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Pavlo Kushnir (pavlo) + +#pragma once + +#include <initializer_list> +#include <map> +#include <memory> + +#include <folly/Range.h> +#include <folly/experimental/StringKeyedCommon.h> + +namespace folly { + +/** + * Wrapper class for map<string, Value> that can + * perform lookup operations with StringPiece, not only string. + * + * It uses kind of hack: string pointed by StringPiece is copied when + * StringPiece is inserted into map + */ +template < + class Value, + class Compare = std::less<StringPiece>, + class Alloc = std::allocator<std::pair<const StringPiece, Value>>> +class StringKeyedMap : private std::map<StringPiece, Value, Compare, Alloc> { + private: + using Base = std::map<StringPiece, Value, Compare, Alloc>; + + public: + typedef typename Base::key_type key_type; + typedef typename Base::mapped_type mapped_type; + typedef typename Base::value_type value_type; + typedef typename Base::key_compare key_compare; + typedef typename Base::allocator_type allocator_type; + typedef typename Base::reference reference; + typedef typename Base::const_reference const_reference; + typedef typename Base::pointer pointer; + typedef typename Base::const_pointer const_pointer; + typedef typename Base::iterator iterator; + typedef typename Base::const_iterator const_iterator; + typedef typename Base::reverse_iterator reverse_iterator; + typedef typename Base::const_reverse_iterator const_reverse_iterator; + typedef typename Base::difference_type difference_type; + typedef typename Base::size_type size_type; + + using Base::get_allocator; + + // Ctors in the same order as + // http://cplusplus.com/reference/map/map/map/ + explicit StringKeyedMap( + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : Base(comp, alloc) {} + + explicit StringKeyedMap(const allocator_type& alloc) : Base(alloc) {} + + template <class InputIterator> + explicit StringKeyedMap( + InputIterator b, + InputIterator e, + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : Base(comp, alloc) { + for (; b != e; ++b) { + // emplace() will carry the duplication + emplace(b->first, b->second); + } + } + + StringKeyedMap(const StringKeyedMap& rhs) + : StringKeyedMap(rhs, rhs.get_allocator()) {} + + StringKeyedMap(const StringKeyedMap& rhs, const allocator_type& a) + : StringKeyedMap(rhs.begin(), rhs.end(), rhs.key_comp(), a) {} + + StringKeyedMap(StringKeyedMap&& other) noexcept : Base(std::move(other)) {} + + StringKeyedMap(StringKeyedMap&& other, const allocator_type& /* a */) noexcept + : Base(std::move(other) /*, a*/ /* not supported by gcc */) {} + + StringKeyedMap( + std::initializer_list<value_type> il, + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : StringKeyedMap(il.begin(), il.end(), comp, alloc) {} + + StringKeyedMap& operator=(const StringKeyedMap& other) & { + if (this == &other) { + return *this; + } + return *this = StringKeyedMap(other); + } + + StringKeyedMap& operator=(StringKeyedMap&& other) & noexcept { + assert(this != &other); + clear(); + Base::operator=(std::move(other)); + return *this; + } + + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::crbegin; + using Base::crend; + using Base::empty; + using Base::end; + using Base::max_size; + using Base::rbegin; + using Base::rend; + using Base::size; + + bool operator==(StringKeyedMap const& other) const { + Base const& lhs = *this; + Base const& rhs = static_cast<Base const&>(other); + return lhs == rhs; + } + + // no need for copy/move overload as StringPiece is small struct + mapped_type& operator[](StringPiece key) { + auto it = find(key); + if (it != end()) { + return it->second; + } + // operator[] will create new (key, value) pair + // we need to allocate memory for key + return Base::operator[](stringPieceDup(key, get_allocator())); + } + + using Base::at; + using Base::count; + using Base::find; + using Base::lower_bound; + using Base::upper_bound; + + template <class... Args> + std::pair<iterator, bool> emplace(StringPiece key, Args&&... args) { + auto it = find(key); + if (it != end()) { + return {it, false}; + } + return Base::emplace( + stringPieceDup(key, get_allocator()), std::forward<Args>(args)...); + } + + std::pair<iterator, bool> insert(value_type val) { + auto it = find(val.first); + if (it != end()) { + return {it, false}; + } + return Base::insert(std::make_pair( + stringPieceDup(val.first, get_allocator()), std::move(val.second))); + } + + iterator erase(const_iterator position) { + auto key = position->first; + auto result = Base::erase(position); + stringPieceDel(key, get_allocator()); + return result; + } + + size_type erase(StringPiece key) { + auto it = find(key); + if (it == end()) { + return 0; + } + erase(it); + return 1; + } + + void clear() noexcept { + for (auto& it : *this) { + stringPieceDel(it.first, get_allocator()); + } + Base::clear(); + } + + using Base::swap; + + ~StringKeyedMap() { + // Here we assume that map doesn't use keys in destructor + for (auto& it : *this) { + stringPieceDel(it.first, get_allocator()); + } + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedSet.h b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedSet.h new file mode 100644 index 000000000..44c92e606 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedSet.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @author: Pavlo Kushnir (pavlo) + +#pragma once + +#include <initializer_list> +#include <memory> +#include <set> + +#include <folly/Range.h> +#include <folly/experimental/StringKeyedCommon.h> + +namespace folly { + +/** + * Wrapper class for set<string> that can + * perform lookup operations with StringPiece, not only string. + * + * It uses kind of hack: string pointed by StringPiece is copied when + * StringPiece is inserted into set + */ +template < + class Compare = std::less<StringPiece>, + class Alloc = std::allocator<StringPiece>> +class StringKeyedSetBase : private std::set<StringPiece, Compare, Alloc> { + private: + using Base = std::set<StringPiece, Compare, Alloc>; + + public: + typedef typename Base::key_type key_type; + typedef typename Base::value_type value_type; + typedef typename Base::key_compare key_compare; + typedef typename Base::allocator_type allocator_type; + typedef typename Base::reference reference; + typedef typename Base::const_reference const_reference; + typedef typename Base::pointer pointer; + typedef typename Base::const_pointer const_pointer; + typedef typename Base::iterator iterator; + typedef typename Base::const_iterator const_iterator; + typedef typename Base::reverse_iterator reverse_iterator; + typedef typename Base::const_reverse_iterator const_reverse_iterator; + typedef typename Base::size_type size_type; + typedef typename Base::difference_type difference_type; + + explicit StringKeyedSetBase( + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : Base(comp, alloc) {} + + explicit StringKeyedSetBase(const allocator_type& alloc) : Base(alloc) {} + + template <class InputIterator> + StringKeyedSetBase( + InputIterator b, + InputIterator e, + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : Base(comp, alloc) { + for (; b != e; ++b) { + emplace(*b); + } + } + + StringKeyedSetBase(const StringKeyedSetBase& rhs) + : StringKeyedSetBase(rhs, rhs.get_allocator()) {} + + StringKeyedSetBase(const StringKeyedSetBase& rhs, const allocator_type& a) + : StringKeyedSetBase(rhs.begin(), rhs.end(), rhs.key_comp(), a) {} + + StringKeyedSetBase(StringKeyedSetBase&& other) noexcept + : Base(std::move(other)) { + assert(other.empty()); + } + + StringKeyedSetBase( + StringKeyedSetBase&& other, + const allocator_type& alloc) noexcept + : Base(std::move(other), alloc) { + assert(other.empty()); + } + + StringKeyedSetBase( + std::initializer_list<value_type> il, + const key_compare& comp = key_compare(), + const allocator_type& alloc = allocator_type()) + : StringKeyedSetBase(il.begin(), il.end(), comp, alloc) {} + + StringKeyedSetBase& operator=(const StringKeyedSetBase& other) { + if (this == &other) { + return *this; + } + return *this = StringKeyedSetBase(other); + } + + StringKeyedSetBase& operator=(StringKeyedSetBase&& other) noexcept { + assert(this != &other); + clear(); + Base::operator=(std::move(other)); + assert(other.empty()); + return *this; + } + + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::count; + using Base::empty; + using Base::end; + using Base::find; + using Base::lower_bound; + using Base::max_size; + using Base::size; + using Base::upper_bound; + + bool operator==(StringKeyedSetBase const& other) const { + Base const& lhs = *this; + Base const& rhs = static_cast<Base const&>(other); + return lhs == rhs; + } + + template <class... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + auto key = StringPiece(std::forward<Args>(args)...); + auto it = find(key); + if (it != end()) { + return {it, false}; + } + return Base::emplace(stringPieceDup(key, get_allocator())); + } + + std::pair<iterator, bool> insert(value_type val) { + auto it = find(val); + if (it != end()) { + return {it, false}; + } + return Base::insert(stringPieceDup(val, get_allocator())); + } + + iterator erase(const_iterator position) { + auto key = *position; + auto result = Base::erase(position); + stringPieceDel(key, get_allocator()); + return result; + } + + size_type erase(StringPiece key) { + auto it = find(key); + if (it == end()) { + return 0; + } + erase(it); + return 1; + } + + void clear() noexcept { + for (auto it : *this) { + stringPieceDel(it, get_allocator()); + } + Base::clear(); + } + + using Base::get_allocator; + + void swap(StringKeyedSetBase& other) & { + return Base::swap(other); + } + + ~StringKeyedSetBase() { + // Here we assume that set doesn't use keys in destructor + for (auto it : *this) { + stringPieceDel(it, get_allocator()); + } + } +}; + +using StringKeyedSet = StringKeyedSetBase<>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h new file mode 100644 index 000000000..c4f6aa404 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Range.h> +#include <folly/container/F14Map.h> + +namespace folly { + +template < + class Mapped, + class Hash = f14::DefaultHasher<std::string>, + class Eq = f14::DefaultKeyEqual<std::string>, + class Alloc = f14::DefaultAlloc<std::pair<std::string const, Mapped>>> +struct StringKeyedUnorderedMap + : public F14NodeMap<std::string, Mapped, Hash, Eq, Alloc> { + using Super = F14NodeMap<std::string, Mapped, Hash, Eq, Alloc>; + + static_assert(is_transparent<Hash>::value, "not transparent"); + static_assert(is_transparent<Eq>::value, "not transparent"); + + public: + using Super::Super; + StringKeyedUnorderedMap() : Super() {} + + // TODO(T31574848): Work around libstdc++ versions (e.g., GCC < 6) with no + // implementation of N4387 ("perfect initialization" for pairs and tuples) to + // support existing callsites that list-initialize: + // m.insert({sp, x}); + std::pair<typename Super::iterator, bool> insert( + std::pair<StringPiece, Mapped> const& p) { + return this->emplace(p.first, p.second); + } + std::pair<typename Super::iterator, bool> insert( + std::pair<StringPiece, Mapped>&& p) { + return this->emplace(std::move(p)); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h new file mode 100644 index 000000000..165e49c0c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/container/F14Set.h> + +namespace folly { + +template < + class Hash = f14::DefaultHasher<std::string>, + class Eq = f14::DefaultKeyEqual<std::string>, + class Alloc = f14::DefaultAlloc<std::string>> +using BasicStringKeyedUnorderedSet = F14NodeSet<std::string, Hash, Eq, Alloc>; + +using StringKeyedUnorderedSet = BasicStringKeyedUnorderedSet<>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/TLRefCount.h b/ios/Pods/Flipper-Folly/folly/experimental/TLRefCount.h new file mode 100644 index 000000000..f182391be --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/TLRefCount.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/ThreadLocal.h> +#include <folly/synchronization/AsymmetricMemoryBarrier.h> +#include <folly/synchronization/detail/Sleeper.h> + +namespace folly { + +class TLRefCount { + public: + using Int = int64_t; + + TLRefCount() + : localCount_([&]() { return new LocalRefCount(*this); }), + collectGuard_(this, [](void*) {}) {} + + ~TLRefCount() noexcept { + assert(globalCount_.load() == 0); + assert(state_.load() == State::GLOBAL); + } + + // This can't increment from 0. + Int operator++() noexcept { + auto& localCount = *localCount_; + + if (++localCount) { + return 42; + } + + if (state_.load() == State::GLOBAL_TRANSITION) { + std::lock_guard<std::mutex> lg(globalMutex_); + } + + assert(state_.load() == State::GLOBAL); + + auto value = globalCount_.load(); + do { + if (value == 0) { + return 0; + } + } while (!globalCount_.compare_exchange_weak(value, value + 1)); + + return value + 1; + } + + Int operator--() noexcept { + auto& localCount = *localCount_; + + if (--localCount) { + return 42; + } + + if (state_.load() == State::GLOBAL_TRANSITION) { + std::lock_guard<std::mutex> lg(globalMutex_); + } + + assert(state_.load() == State::GLOBAL); + + return globalCount_-- - 1; + } + + Int operator*() const { + if (state_ != State::GLOBAL) { + return 42; + } + return globalCount_.load(); + } + + void useGlobal() noexcept { + std::array<TLRefCount*, 1> ptrs{{this}}; + useGlobal(ptrs); + } + + template <typename Container> + static void useGlobal(const Container& refCountPtrs) { +#ifdef FOLLY_SANITIZE_THREAD + // TSAN has a limitation for the number of locks held concurrently, so it's + // safer to call useGlobal() serially. + if (refCountPtrs.size() > 1) { + for (auto refCountPtr : refCountPtrs) { + refCountPtr->useGlobal(); + } + return; + } +#endif + + std::vector<std::unique_lock<std::mutex>> lgs_; + for (auto refCountPtr : refCountPtrs) { + lgs_.emplace_back(refCountPtr->globalMutex_); + + refCountPtr->state_ = State::GLOBAL_TRANSITION; + } + + asymmetricHeavyBarrier(); + + for (auto refCountPtr : refCountPtrs) { + std::weak_ptr<void> collectGuardWeak = refCountPtr->collectGuard_; + + // Make sure we can't create new LocalRefCounts + refCountPtr->collectGuard_.reset(); + + while (!collectGuardWeak.expired()) { + auto accessor = refCountPtr->localCount_.accessAllThreads(); + for (auto& count : accessor) { + count.collect(); + } + } + + refCountPtr->state_ = State::GLOBAL; + } + } + + private: + using AtomicInt = std::atomic<Int>; + + enum class State { + LOCAL, + GLOBAL_TRANSITION, + GLOBAL, + }; + + class LocalRefCount { + public: + explicit LocalRefCount(TLRefCount& refCount) : refCount_(refCount) { + std::lock_guard<std::mutex> lg(refCount.globalMutex_); + + collectGuard_ = refCount.collectGuard_; + } + + ~LocalRefCount() { + collect(); + } + + void collect() { + { + std::lock_guard<std::mutex> lg(collectMutex_); + + if (!collectGuard_) { + return; + } + + collectCount_ = count_.load(); + refCount_.globalCount_.fetch_add(collectCount_); + collectGuard_.reset(); + } + // We only care about seeing inUpdate if we've observed the new count_ + // value set by the update() call, so memory_order_relaxed is enough. + if (inUpdate_.load(std::memory_order_relaxed)) { + folly::detail::Sleeper sleeper; + while (inUpdate_.load(std::memory_order_acquire)) { + sleeper.wait(); + } + } + } + + bool operator++() { + return update(1); + } + + bool operator--() { + return update(-1); + } + + private: + bool update(Int delta) { + if (UNLIKELY(refCount_.state_.load() != State::LOCAL)) { + return false; + } + + // This is equivalent to atomic fetch_add. We know that this operation + // is always performed from a single thread. asymmetricLightBarrier() + // makes things faster than atomic fetch_add on platforms with native + // support. + auto count = count_.load(std::memory_order_relaxed) + delta; + inUpdate_.store(true, std::memory_order_relaxed); + SCOPE_EXIT { + inUpdate_.store(false, std::memory_order_release); + }; + count_.store(count, std::memory_order_release); + + asymmetricLightBarrier(); + + if (UNLIKELY(refCount_.state_.load() != State::LOCAL)) { + std::lock_guard<std::mutex> lg(collectMutex_); + + if (collectGuard_) { + return true; + } + if (collectCount_ != count) { + return false; + } + } + + return true; + } + + AtomicInt count_{0}; + std::atomic<bool> inUpdate_{false}; + TLRefCount& refCount_; + + std::mutex collectMutex_; + Int collectCount_{0}; + std::shared_ptr<void> collectGuard_; + }; + + std::atomic<State> state_{State::LOCAL}; + folly::ThreadLocal<LocalRefCount, TLRefCount> localCount_; + std::atomic<int64_t> globalCount_{1}; + std::mutex globalMutex_; + std::shared_ptr<void> collectGuard_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/TestUtil.h b/ios/Pods/Flipper-Folly/folly/experimental/TestUtil.h new file mode 100644 index 000000000..3e78e5eb2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/TestUtil.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <map> +#include <string> + +#include <folly/Range.h> +#include <folly/ScopeGuard.h> +#include <folly/experimental/io/FsUtil.h> + +namespace folly { +namespace test { + +/** + * Temporary file. + * + * By default, the file is created in a system-specific location (the value + * of the TMPDIR environment variable, or /tmp), but you can override that + * with a different (non-empty) directory passed to the constructor. + * + * By default, the file is closed and deleted when the TemporaryFile object + * is destroyed, but both these behaviors can be overridden with arguments + * to the constructor. + */ +class TemporaryFile { + public: + enum class Scope { + PERMANENT, + UNLINK_IMMEDIATELY, + UNLINK_ON_DESTRUCTION, + }; + explicit TemporaryFile( + StringPiece namePrefix = StringPiece(), + fs::path dir = fs::path(), + Scope scope = Scope::UNLINK_ON_DESTRUCTION, + bool closeOnDestruction = true); + ~TemporaryFile(); + + // Movable, but not copyable + TemporaryFile(TemporaryFile&& other) noexcept { + assign(other); + } + + TemporaryFile& operator=(TemporaryFile&& other) { + if (this != &other) { + reset(); + assign(other); + } + return *this; + } + + void close(); + int fd() const { + return fd_; + } + const fs::path& path() const; + void reset(); + + private: + Scope scope_; + bool closeOnDestruction_; + int fd_; + fs::path path_; + + void assign(TemporaryFile& other) { + scope_ = other.scope_; + closeOnDestruction_ = other.closeOnDestruction_; + fd_ = std::exchange(other.fd_, -1); + path_ = other.path_; + } +}; + +/** + * Temporary directory. + * + * By default, the temporary directory is created in a system-specific + * location (the value of the TMPDIR environment variable, or /tmp), but you + * can override that with a non-empty directory passed to the constructor. + * + * By default, the directory is recursively deleted when the TemporaryDirectory + * object is destroyed, but that can be overridden with an argument + * to the constructor. + */ + +class TemporaryDirectory { + public: + enum class Scope { + PERMANENT, + DELETE_ON_DESTRUCTION, + }; + explicit TemporaryDirectory( + StringPiece namePrefix = StringPiece(), + fs::path dir = fs::path(), + Scope scope = Scope::DELETE_ON_DESTRUCTION); + ~TemporaryDirectory(); + + // Movable, but not copiable + TemporaryDirectory(TemporaryDirectory&&) = default; + TemporaryDirectory& operator=(TemporaryDirectory&&) = default; + + const fs::path& path() const { + return *path_; + } + + private: + Scope scope_; + std::unique_ptr<fs::path> path_; +}; + +/** + * Changes into a temporary directory, and deletes it with all its contents + * upon destruction, also changing back to the original working directory. + */ +class ChangeToTempDir { + public: + ChangeToTempDir(); + ~ChangeToTempDir(); + + // Movable, but not copiable + ChangeToTempDir(ChangeToTempDir&&) = default; + ChangeToTempDir& operator=(ChangeToTempDir&&) = default; + + const fs::path& path() const { + return dir_.path(); + } + + private: + TemporaryDirectory dir_; + fs::path orig_; +}; + +namespace detail { +struct SavedState { + void* previousThreadLocalHandler; + int previousCrtReportMode; +}; +SavedState disableInvalidParameters(); +void enableInvalidParameters(SavedState state); +} // namespace detail + +// Ok, so fun fact: The CRT on windows will actually abort +// on certain failed parameter validation checks in debug +// mode rather than simply returning -1 as it does in release +// mode. We can however, ensure consistent behavior by +// registering our own thread-local invalid parameter handler +// for the duration of the call, and just have that handler +// immediately return. We also have to disable CRT asertion +// alerts for the duration of the call, otherwise we get +// the abort-retry-ignore window. +template <typename Func> +auto msvcSuppressAbortOnInvalidParams(Func func) -> decltype(func()) { + auto savedState = detail::disableInvalidParameters(); + SCOPE_EXIT { + detail::enableInvalidParameters(savedState); + }; + return func(); +} + +/** + * Easy PCRE regex matching. Note that pattern must match the ENTIRE target, + * so use .* at the start and end of the pattern, as appropriate. See + * http://regex101.com/ for a PCRE simulator. + */ +#define EXPECT_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \ + EXPECT_PRED2( \ + ::folly::test::detail::hasPCREPatternMatch, \ + pattern_stringpiece, \ + target_stringpiece) +#define EXPECT_NO_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \ + EXPECT_PRED2( \ + ::folly::test::detail::hasNoPCREPatternMatch, \ + pattern_stringpiece, \ + target_stringpiece) + +namespace detail { +bool hasPCREPatternMatch(StringPiece pattern, StringPiece target); +bool hasNoPCREPatternMatch(StringPiece pattern, StringPiece target); +} // namespace detail + +/** + * Use these patterns together with CaptureFD and EXPECT_PCRE_MATCH() to + * test for the presence (or absence) of log lines at a particular level: + * + * CaptureFD stderr(2); + * LOG(INFO) << "All is well"; + * EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), stderr.readIncremental()); + * LOG(ERROR) << "Uh-oh"; + * EXPECT_PCRE_MATCH(glogErrorPattern(), stderr.readIncremental()); + */ +inline std::string glogErrorPattern() { + return ".*(^|\n)E[0-9].*"; +} +inline std::string glogWarningPattern() { + return ".*(^|\n)W[0-9].*"; +} +// Error OR warning +inline std::string glogErrOrWarnPattern() { + return ".*(^|\n)[EW][0-9].*"; +} + +/** + * Temporarily capture a file descriptor by redirecting it into a file. + * You can consume its entire output thus far via read(), incrementally + * via readIncremental(), or via callback using chunk_cob. + * Great for testing logging (see also glog*Pattern()). + */ +class CaptureFD { + private: + struct NoOpChunkCob { + void operator()(StringPiece) {} + }; + + public: + using ChunkCob = std::function<void(folly::StringPiece)>; + + /** + * chunk_cob is is guaranteed to consume all the captured output. It is + * invoked on each readIncremental(), and also on FD release to capture + * as-yet unread lines. Chunks can be empty. + */ + explicit CaptureFD(int fd, ChunkCob chunk_cob = NoOpChunkCob()); + ~CaptureFD(); + + /** + * Restore the captured FD to its original state. It can be useful to do + * this before the destructor so that you can read() the captured data and + * log about it to the formerly captured stderr or stdout. + */ + void release(); + + /** + * Reads the whole file into a string, but does not remove the redirect. + */ + std::string read() const; + + /** + * Read any bytes that were appended to the file since the last + * readIncremental. Great for testing line-by-line output. + */ + std::string readIncremental(); + + private: + ChunkCob chunkCob_; + TemporaryFile file_; + + int fd_; + int oldFDCopy_; // equal to fd_ after restore() + + off_t readOffset_; // for incremental reading +}; + +} // namespace test +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h b/ios/Pods/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h new file mode 100644 index 000000000..334272452 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/experimental/STTimerFDTimeoutManager.h> +#include <folly/futures/Future.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/HHWheelTimer.h> +#include <thread> + +namespace folly { + +class ThreadWheelTimekeeperHighRes : public Timekeeper { + public: + explicit ThreadWheelTimekeeperHighRes( + std::chrono::microseconds intervalDuration = std::chrono::microseconds( + HHWheelTimerHighRes::DEFAULT_TICK_INTERVAL)); + ~ThreadWheelTimekeeperHighRes() override; + + /// Implement the Timekeeper interface + SemiFuture<Unit> after(HighResDuration) override; + + protected: + folly::EventBase eventBase_; + STTimerFDTimeoutManager timeoutMgr_; + std::thread thread_; + HHWheelTimerHighRes::UniquePtr wheelTimer_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h b/ios/Pods/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h new file mode 100644 index 000000000..2ab76196c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Function.h> +#include <condition_variable> +#include <thread> +#include <vector> + +namespace folly { + +/** + * For each function `fn` you add to this object, `fn` will be run in a loop + * in its own thread, with the thread sleeping between invocations of `fn` + * for the duration returned by `fn`'s previous run. + * + * To clean up these threads, invoke `stop()`, which will interrupt sleeping + * threads. `stop()` will wait for already-running functions to return. + * + * == Alternatives == + * + * If you want to multiplex multiple functions on the same thread, you can + * either use EventBase with AsyncTimeout objects, or FunctionScheduler for + * a slightly simpler API. + * + * == Thread-safety == + * + * This type follows the common rule that: + * (1) const member functions are safe to call concurrently with const + * member functions, but + * (2) non-const member functions are not safe to call concurrently with + * any member functions. + * + * == Pitfalls == + * + * Threads and classes don't mix well in C++, so you have to be very careful + * if you want to have ThreadedRepeatingFunctionRunner as a member of your + * class. A reasonable pattern looks like this: + * + * // Your class must be `final` because inheriting from a class with + * // threads can cause all sorts of subtle issues: + * // - Your base class might start threads that attempt to access derived + * // class state **before** that state was constructed. + * // - Your base class's destructor will only be able to stop threads + * // **after** the derived class state was destroyed -- and that state + * // might be accessed by the threads. + * // In short, any derived class would have to do work to manage the + * // threads itself, which makes inheritance a poor means of composition. + * struct MyClass final { + * // Note that threads are NOT added in the constructor, for two reasons: + * // + * // (1) If you first added some threads, and then had additional + * // initialization (e.g. derived class constructors), `this` might + * // not be fully constructed by the time the function threads + * // started running, causing heisenbugs. + * // + * // (2) If your constructor threw after thread creation, the class + * // destructor would not be invoked, potentially leaving the + * // threads running too long. + * // + * // It is much safer to have explicit two-step initialization, or to + * // lazily add threads the first time they are needed. + * MyClass() : count_(0) {} + * + * // You must stop the threads as early as possible in the destruction + * // process (or even before). If MyClass had derived classes, the final + * // derived class MUST always call stop() as the first thing in its + * // destructor -- otherwise, the worker threads might access already- + * // destroyed state. + * ~MyClass() { + * threads_.stop(); // Stop threads BEFORE destroying any state they use. + * } + * + * // See the constructor for why two-stage initialization is preferred. + * void init() { + * threads_.add(bind(&MyClass::incrementCount, this)); + * } + * + * std::chrono::milliseconds incrementCount() { + * ++count_; + * return 10; + * } + * + * private: + * std::atomic<int> count_; + * // CAUTION: Declare last since the threads access other members of `this`. + * ThreadedRepeatingFunctionRunner threads_; + * }; + */ +class ThreadedRepeatingFunctionRunner final { + public: + // Returns how long to wait before the next repetition. Must not throw. + using RepeatingFn = folly::Function<std::chrono::milliseconds() noexcept>; + + ThreadedRepeatingFunctionRunner(); + ~ThreadedRepeatingFunctionRunner(); + + /** + * Ideally, you will call this before initiating the destruction of the + * host object. Otherwise, this should be the first thing in the + * destruction sequence. If it comes any later, worker threads may access + * class state that had already been destroyed. + */ + void stop(); + + /** + * Run your noexcept function `f` in a background loop, sleeping between + * calls for a duration returned by `f`. Optionally waits for + * `initialSleep` before calling `f` for the first time. Names the thread + * using up to the first 15 chars of `name`. + * + * DANGER: If a non-final class has a ThreadedRepeatingFunctionRunner + * member (which, by the way, must be declared last in the class), then + * you must not call add() in your constructor. Otherwise, your thread + * risks accessing uninitialized data belonging to a child class. To + * avoid this design bug, prefer to use two-stage initialization to start + * your threads. + */ + void add( + std::string name, + RepeatingFn f, + std::chrono::milliseconds initialSleep = std::chrono::milliseconds(0)); + + size_t size() const { + return threads_.size(); + } + + private: + // Returns true if this is the first stop(). + bool stopImpl(); + + // Sleep for a duration, or until stop() is called. + bool waitFor(std::chrono::milliseconds duration) noexcept; + + // Noexcept allows us to get a good backtrace on crashes -- otherwise, + // std::terminate would get called **outside** of the thread function. + void executeInLoop( + RepeatingFn, + std::chrono::milliseconds initialSleep) noexcept; + + std::mutex stopMutex_; + bool stopping_{false}; // protected by stopMutex_ + std::condition_variable stopCv_; + + std::vector<std::thread> threads_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/TimerFD.h b/ios/Pods/Flipper-Folly/folly/experimental/TimerFD.h new file mode 100644 index 000000000..b261befb1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/TimerFD.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#if defined(__linux__) && !defined(__ANDROID__) +#define FOLLY_HAVE_TIMERFD +#endif + +#include <folly/io/async/EventBase.h> +#ifdef FOLLY_HAVE_TIMERFD +#include <folly/io/async/EventHandler.h> +#else +#include <folly/io/async/AsyncTimeout.h> +#endif +#include <chrono> + +namespace folly { +#ifdef FOLLY_HAVE_TIMERFD +// timerfd wrapper +class TimerFD : public folly::EventHandler, public DelayedDestruction { + public: + explicit TimerFD(folly::EventBase* eventBase); + ~TimerFD() override; + + virtual void onTimeout() noexcept = 0; + void schedule(std::chrono::microseconds timeout); + void cancel(); + + // from folly::EventHandler + void handlerReady(uint16_t events) noexcept override; + + protected: + void close(); + + private: + TimerFD(folly::EventBase* eventBase, int fd); + static int createTimerFd(); + + // use 0 to stop the timer + bool setTimer(std::chrono::microseconds useconds); + + int fd_{-1}; +}; +#else +// alternative implementation using a folly::AsyncTimeout +class TimerFD { + public: + explicit TimerFD(folly::EventBase* eventBase); + virtual ~TimerFD(); + + virtual void onTimeout() = 0; + void schedule(std::chrono::microseconds timeout); + void cancel(); + + protected: + void close() {} + + private: + class TimerFDAsyncTimeout : public folly::AsyncTimeout { + public: + TimerFDAsyncTimeout(folly::EventBase* eventBase, TimerFD* timerFd); + ~TimerFDAsyncTimeout() override = default; + + // from folly::AsyncTimeout + void timeoutExpired() noexcept final; + + private: + TimerFD* timerFd_; + }; + + TimerFDAsyncTimeout timeout_; +}; +#endif +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h b/ios/Pods/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h new file mode 100644 index 000000000..9a1a9a773 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <folly/experimental/TimerFD.h> +#include <folly/io/async/DelayedDestruction.h> +#include <map> + +namespace folly { +// generic TimerFD based timeout manager +class TimerFDTimeoutManager : public TimerFD { + public: + using UniquePtr = + std::unique_ptr<TimerFDTimeoutManager, DelayedDestruction::Destructor>; + using SharedPtr = std::shared_ptr<TimerFDTimeoutManager>; + + public: + class Callback + : public boost::intrusive::list_base_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>> { + public: + Callback() = default; + explicit Callback(TimerFDTimeoutManager* mgr) : mgr_(mgr) {} + virtual ~Callback() = default; + + virtual void timeoutExpired() noexcept = 0; + virtual void callbackCanceled() noexcept { + timeoutExpired(); + } + + const std::chrono::microseconds& getExpirationTime() const { + return expirationTime_; + } + + void setExpirationTime( + TimerFDTimeoutManager* mgr, + const std::chrono::microseconds& expirationTime) { + mgr_ = mgr; + expirationTime_ = expirationTime; + } + + std::chrono::microseconds getTimeRemaining() const { + return getTimeRemaining(std::chrono::steady_clock::now()); + } + + std::chrono::microseconds getTimeRemaining( + std::chrono::steady_clock::time_point now) const { + auto nowMs = std::chrono::duration_cast<std::chrono::microseconds>( + now.time_since_epoch()); + if (expirationTime_ > nowMs) { + return std::chrono::duration_cast<std::chrono::microseconds>( + expirationTime_ - nowMs); + } + + return std::chrono::microseconds(0); + } + + void scheduleTimeout(std::chrono::microseconds timeout) { + if (mgr_) { + mgr_->scheduleTimeout(this, timeout); + } + } + + bool cancelTimeout() { + return mgr_->cancelTimeout(this); + } + + private: + TimerFDTimeoutManager* mgr_{nullptr}; + std::chrono::microseconds expirationTime_{0}; + }; + + explicit TimerFDTimeoutManager(folly::EventBase* eventBase); + ~TimerFDTimeoutManager() override; + + // from TimerFD + void onTimeout() noexcept final; + + size_t cancelAll(); + void scheduleTimeout(Callback* callback, std::chrono::microseconds timeout); + bool cancelTimeout(Callback* callback); + + template <class F> + void scheduleTimeoutFn(F fn, std::chrono::microseconds timeout) { + struct Wrapper : Callback { + explicit Wrapper(F f) : fn_(std::move(f)) {} + void timeoutExpired() noexcept override { + try { + fn_(); + } catch (std::exception const& e) { + LOG(ERROR) << "HHWheelTimerBase timeout callback threw an exception: " + << e.what(); + } catch (...) { + LOG(ERROR) + << "HHWheelTimerBase timeout callback threw a non-exception."; + } + delete this; + } + F fn_; + }; + Wrapper* w = new Wrapper(std::move(fn)); + scheduleTimeout(w, timeout); + } + + size_t count() const; + + private: + void processExpiredTimers(); + void scheduleNextTimer(); + + std::chrono::steady_clock::time_point getCurTime() { + return std::chrono::steady_clock::now(); + } + + // we can attempt to schedule new entries while in processExpiredTimers + // we want to reschedule the timers once we're done with the processing + bool processingExpired_{false}; + + typedef boost::intrusive:: + list<Callback, boost::intrusive::constant_time_size<false>> + CallbackList; + std::map<std::chrono::microseconds, CallbackList> callbacks_; + CallbackList inProgressList_; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/experimental/TupleOps.h b/ios/Pods/Flipper-Folly/folly/experimental/TupleOps.h new file mode 100644 index 000000000..086f9a8cf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/experimental/TupleOps.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <limits> +#include <tuple> +#include <type_traits> + +// tupleRange<start, n>(tuple): select n elements starting at index start +// in the given tuple +// tupleRange<start>(tuple): select all elements starting at index start +// until the end of the given tuple +// tuplePrepend(x, tuple): return a tuple obtained by prepending x to the +// given tuple. +// +// In Lisp lingo, std::get<0> is car, tupleRange<1> is cdr, and tuplePrepend +// is cons. + +namespace folly { + +// TemplateSeq<T, ...> is a type parametrized by sizeof...(Xs) values of type +// T. Used to destructure the values into a template parameter pack; +// see the example in TupleSelect, below. +template <class T, T... xs> +struct TemplateSeq { + template <T x> + using Prepend = TemplateSeq<T, x, xs...>; +}; + +// TemplateRange<T, start, n>::type is +// TemplateSeq<T, start+1, start+2, ..., start+n-1> +template <class T, T start, T n, class Enable = void> +struct TemplateRange; + +template <class T, T start, T n> +struct TemplateRange<T, start, n, typename std::enable_if<(n > 0)>::type> { + using type = + typename TemplateRange<T, start + 1, n - 1>::type::template Prepend< + start>; +}; + +template <class T, T start, T n> +struct TemplateRange<T, start, n, typename std::enable_if<(n <= 0)>::type> { + using type = TemplateSeq<T>; +}; + +// Similar to TemplateRange, given a tuple T, +// TemplateTupleRange<T, start, n>::type is +// TemplateSeq<size_t, start, start+1, ..., start+k-1> +// where k = min(tuple_size<T>::value - start, n) +// (that is, it's a TemplateSeq of at most n elements, but won't extend +// past the end of the given tuple) +template < + class T, + std::size_t start = 0, + std::size_t n = std::numeric_limits<std::size_t>::max(), + std::size_t size = + std::tuple_size<typename std::remove_reference<T>::type>::value, + class Enable = typename std::enable_if<(start <= size)>::type> +struct TemplateTupleRange { + using type = typename TemplateRange< + std::size_t, + start, + (n <= size - start ? n : size - start)>::type; +}; + +namespace detail { + +// Helper class to select a subset of a tuple +template <class S> +struct TupleSelect; +template <std::size_t... Ns> +struct TupleSelect<TemplateSeq<std::size_t, Ns...>> { + template <class T> + static auto select(T&& v) + -> decltype(std::make_tuple(std::get<Ns>(std::forward<T>(v))...)) { + return std::make_tuple(std::get<Ns>(std::forward<T>(v))...); + } +}; + +} // namespace detail + +// Return a tuple consisting of the elements at a range of indices. +// +// Use as tupleRange<start, n>(t) to return a tuple of (at most) n +// elements starting at index start in tuple t. +// If only start is specified (tupleRange<start>(t)), returns all elements +// starting at index start until the end of the tuple t. +// Won't compile if start > size of t. +// Will return fewer elements (size - start) if start + n > size of t. +template < + std::size_t start = 0, + std::size_t n = std::numeric_limits<std::size_t>::max(), + class T, + class Seq = typename TemplateTupleRange<T, start, n>::type> +auto tupleRange(T&& v) + -> decltype(detail::TupleSelect<Seq>::select(std::forward<T>(v))) { + return detail::TupleSelect<Seq>::select(std::forward<T>(v)); +} + +// Return a tuple obtained by prepending car to the tuple cdr. +template <class T, class U> +auto tuplePrepend(T&& car, U&& cdr) -> decltype(std::tuple_cat( + std::make_tuple(std::forward<T>(car)), + std::forward<U>(cdr))) { + return std::tuple_cat( + std::make_tuple(std::forward<T>(car)), std::forward<U>(cdr)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/functional/ApplyTuple.h b/ios/Pods/Flipper-Folly/folly/functional/ApplyTuple.h new file mode 100644 index 000000000..f7901aeb6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/functional/ApplyTuple.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <tuple> +#include <utility> + +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +/** + * Helper to generate an index sequence from a tuple like type + */ +template <typename Tuple> +using index_sequence_for_tuple = + std::make_index_sequence<std::tuple_size<Tuple>::value>; + +namespace detail { +namespace apply_tuple { +namespace adl { +using std::get; + +struct ApplyInvoke { + template <typename T> + using seq = index_sequence_for_tuple<std::remove_reference_t<T>>; + + template <typename F, typename T, std::size_t... I> + static constexpr auto + invoke_(F&& f, T&& t, std::index_sequence<I...>) noexcept( + is_nothrow_invocable<F&&, decltype(get<I>(std::declval<T>()))...>::value) + -> invoke_result_t<F&&, decltype(get<I>(std::declval<T>()))...> { + return invoke(static_cast<F&&>(f), get<I>(static_cast<T&&>(t))...); + } +}; + +template < + typename Tuple, + std::size_t... Indices, + typename ReturnTuple = + std::tuple<decltype(get<Indices>(std::declval<Tuple>()))...>> +auto forward_tuple(Tuple&& tuple, std::index_sequence<Indices...>) + -> ReturnTuple { + return ReturnTuple{get<Indices>(std::forward<Tuple>(tuple))...}; +} +} // namespace adl +} // namespace apply_tuple +} // namespace detail + +struct ApplyInvoke : private detail::apply_tuple::adl::ApplyInvoke { + public: + template <typename F, typename T> + constexpr auto operator()(F&& f, T&& t) const noexcept( + noexcept(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{}))) + -> decltype(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{})) { + return invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{}); + } +}; + +////////////////////////////////////////////////////////////////////// + +// libc++ v3.9 has std::apply +// android ndk r15c libc++ claims to be v3.9 but is missing std::apply +#if __cpp_lib_apply >= 201603 || \ + (((__ANDROID__ && _LIBCPP_VERSION > 3900) || \ + (!__ANDROID__ && _LIBCPP_VERSION > 3800)) && \ + _LIBCPP_STD_VER > 14) || \ + (_MSC_VER && _HAS_CXX17) + +/* using override */ using std::apply; + +#else // __cpp_lib_apply >= 201603 + +// mimic: std::apply, C++17 +template <typename F, typename Tuple> +constexpr decltype(auto) apply(F&& func, Tuple&& tuple) { + return ApplyInvoke{}(static_cast<F&&>(func), static_cast<Tuple&&>(tuple)); +} + +#endif // __cpp_lib_apply >= 201603 + +/** + * Get a tuple of references from the passed tuple, forwarding will be applied + * on the individual types of the tuple based on the value category of the + * passed tuple + * + * For example + * + * forward_tuple(std::make_tuple(1, 2)) + * + * Returns a std::tuple<int&&, int&&>, + * + * auto tuple = std::make_tuple(1, 2); + * forward_tuple(tuple) + * + * Returns a std::tuple<int&, int&> + */ +template <typename Tuple> +auto forward_tuple(Tuple&& tuple) noexcept + -> decltype(detail::apply_tuple::adl::forward_tuple( + std::declval<Tuple>(), + std::declval< + index_sequence_for_tuple<std::remove_reference_t<Tuple>>>())) { + return detail::apply_tuple::adl::forward_tuple( + std::forward<Tuple>(tuple), + index_sequence_for_tuple<std::remove_reference_t<Tuple>>{}); +} + +/** + * Mimic the invoke suite of traits for tuple based apply invocation + */ +template <typename F, typename Tuple> +struct apply_result : invoke_result<ApplyInvoke, F, Tuple> {}; +template <typename F, typename Tuple> +using apply_result_t = invoke_result_t<ApplyInvoke, F, Tuple>; +template <typename F, typename Tuple> +struct is_applicable : is_invocable<ApplyInvoke, F, Tuple> {}; +template <typename F, typename Tuple> +FOLLY_INLINE_VARIABLE constexpr bool is_applicable_v = + is_applicable<F, Tuple>::value; +template <typename R, typename F, typename Tuple> +struct is_applicable_r : is_invocable_r<R, ApplyInvoke, F, Tuple> {}; +template <typename R, typename F, typename Tuple> +FOLLY_INLINE_VARIABLE constexpr bool is_applicable_r_v = + is_applicable_r<R, F, Tuple>::value; +template <typename F, typename Tuple> +struct is_nothrow_applicable : is_nothrow_invocable<ApplyInvoke, F, Tuple> {}; +template <typename F, typename Tuple> +FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_applicable_v = + is_nothrow_applicable<F, Tuple>::value; +template <typename R, typename F, typename Tuple> +struct is_nothrow_applicable_r + : is_nothrow_invocable_r<R, ApplyInvoke, F, Tuple> {}; +template <typename R, typename F, typename Tuple> +FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_applicable_r_v = + is_nothrow_applicable_r<R, F, Tuple>::value; + +namespace detail { +namespace apply_tuple { + +template <class F> +class Uncurry { + public: + explicit Uncurry(F&& func) : func_(std::move(func)) {} + explicit Uncurry(const F& func) : func_(func) {} + + template <class Tuple> + auto operator()(Tuple&& tuple) const + -> decltype(apply(std::declval<F>(), std::forward<Tuple>(tuple))) { + return apply(func_, std::forward<Tuple>(tuple)); + } + + private: + F func_; +}; +} // namespace apply_tuple +} // namespace detail + +/** + * Wraps a function taking N arguments into a function which accepts a tuple of + * N arguments. Note: This function will also accept an std::pair if N == 2. + * + * For example, given the below code: + * + * std::vector<std::tuple<int, int, int>> rows = ...; + * auto test = [](std::tuple<int, int, int>& row) { + * return std::get<0>(row) * std::get<1>(row) * std::get<2>(row) == 24; + * }; + * auto found = std::find_if(rows.begin(), rows.end(), test); + * + * + * 'test' could be rewritten as: + * + * auto test = + * folly::uncurry([](int a, int b, int c) { return a * b * c == 24; }); + * + */ +template <class F> +auto uncurry(F&& f) + -> detail::apply_tuple::Uncurry<typename std::decay<F>::type> { + return detail::apply_tuple::Uncurry<typename std::decay<F>::type>( + std::forward<F>(f)); +} + +#if __cpp_lib_make_from_tuple || (_MSC_VER >= 1910 && _MSVC_LANG > 201402) + +/* using override */ using std::make_from_tuple; + +#else + +namespace detail { +namespace apply_tuple { +template <class T> +struct Construct { + template <class... Args> + constexpr T operator()(Args&&... args) const { + return T(std::forward<Args>(args)...); + } +}; +} // namespace apply_tuple +} // namespace detail + +// mimic: std::make_from_tuple, C++17 +template <class T, class Tuple> +constexpr T make_from_tuple(Tuple&& t) { + return apply(detail::apply_tuple::Construct<T>(), std::forward<Tuple>(t)); +} + +#endif + +////////////////////////////////////////////////////////////////////// +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/functional/Invoke.h b/ios/Pods/Flipper-Folly/folly/functional/Invoke.h new file mode 100644 index 000000000..beb887992 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/functional/Invoke.h @@ -0,0 +1,478 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <type_traits> + +#include <boost/preprocessor/control/expr_iif.hpp> +#include <boost/preprocessor/facilities/is_empty_variadic.hpp> +#include <boost/preprocessor/list/for_each.hpp> +#include <boost/preprocessor/logical/not.hpp> +#include <boost/preprocessor/tuple/to_list.hpp> + +#include <folly/Portability.h> +#include <folly/Preprocessor.h> +#include <folly/Traits.h> + +/** + * include or backport: + * * std::invoke + * * std::invoke_result + * * std::invoke_result_t + * * std::is_invocable + * * std::is_invocable_v + * * std::is_invocable_r + * * std::is_invocable_r_v + * * std::is_nothrow_invocable + * * std::is_nothrow_invocable_v + * * std::is_nothrow_invocable_r + * * std::is_nothrow_invocable_r_v + */ + +#if __cpp_lib_invoke >= 201411 || _MSC_VER + +namespace folly { + +/* using override */ using std::invoke; +} + +#else + +namespace folly { + +// mimic: std::invoke, C++17 +template <typename F, typename... Args> +FOLLY_ERASE constexpr auto invoke(F&& f, Args&&... args) noexcept( + noexcept(static_cast<F&&>(f)(static_cast<Args&&>(args)...))) + -> decltype(static_cast<F&&>(f)(static_cast<Args&&>(args)...)) { + return static_cast<F&&>(f)(static_cast<Args&&>(args)...); +} +template <typename M, typename C, typename... Args> +FOLLY_ERASE constexpr auto invoke(M(C::*d), Args&&... args) + -> decltype(std::mem_fn(d)(static_cast<Args&&>(args)...)) { + return std::mem_fn(d)(static_cast<Args&&>(args)...); +} + +} // namespace folly + +#endif + +// Only available in >= MSVC 2017 15.3 in C++17 +#if __cpp_lib_is_invocable >= 201703 || \ + (_MSC_VER >= 1911 && _MSVC_LANG > 201402) + +namespace folly { + +/* using override */ using std::invoke_result; +/* using override */ using std::invoke_result_t; +/* using override */ using std::is_invocable; +/* using override */ using std::is_invocable_r; +/* using override */ using std::is_invocable_r_v; +/* using override */ using std::is_invocable_v; +/* using override */ using std::is_nothrow_invocable; +/* using override */ using std::is_nothrow_invocable_r; +/* using override */ using std::is_nothrow_invocable_r_v; +/* using override */ using std::is_nothrow_invocable_v; + +} // namespace folly + +#else + +namespace folly { + +namespace invoke_detail { + +template <typename F, typename... Args> +using invoke_result_ = + decltype(invoke(std::declval<F>(), std::declval<Args>()...)); + +template <typename F, typename... Args> +struct invoke_nothrow_ + : bool_constant<noexcept( + invoke(std::declval<F>(), std::declval<Args>()...))> {}; + +// from: http://en.cppreference.com/w/cpp/types/result_of, CC-BY-SA + +template <typename Void, typename F, typename... Args> +struct invoke_result {}; + +template <typename F, typename... Args> +struct invoke_result<void_t<invoke_result_<F, Args...>>, F, Args...> { + using type = invoke_result_<F, Args...>; +}; + +template <typename Void, typename F, typename... Args> +struct is_invocable : std::false_type {}; + +template <typename F, typename... Args> +struct is_invocable<void_t<invoke_result_<F, Args...>>, F, Args...> + : std::true_type {}; + +template <typename Void, typename R, typename F, typename... Args> +struct is_invocable_r : std::false_type {}; + +template <typename R, typename F, typename... Args> +struct is_invocable_r<void_t<invoke_result_<F, Args...>>, R, F, Args...> + : std::is_convertible<invoke_result_<F, Args...>, R> {}; + +template <typename Void, typename F, typename... Args> +struct is_nothrow_invocable : std::false_type {}; + +template <typename F, typename... Args> +struct is_nothrow_invocable<void_t<invoke_result_<F, Args...>>, F, Args...> + : invoke_nothrow_<F, Args...> {}; + +template <typename Void, typename R, typename F, typename... Args> +struct is_nothrow_invocable_r : std::false_type {}; + +template <typename R, typename F, typename... Args> +struct is_nothrow_invocable_r<void_t<invoke_result_<F, Args...>>, R, F, Args...> + : StrictConjunction< + std::is_convertible<invoke_result_<F, Args...>, R>, + invoke_nothrow_<F, Args...>> {}; + +} // namespace invoke_detail + +// mimic: std::invoke_result, C++17 +template <typename F, typename... Args> +struct invoke_result : invoke_detail::invoke_result<void, F, Args...> {}; + +// mimic: std::invoke_result_t, C++17 +template <typename F, typename... Args> +using invoke_result_t = typename invoke_result<F, Args...>::type; + +// mimic: std::is_invocable, C++17 +template <typename F, typename... Args> +struct is_invocable : invoke_detail::is_invocable<void, F, Args...> {}; + +// mimic: std::is_invocable_v, C++17 +template <typename F, typename... Args> +FOLLY_INLINE_VARIABLE constexpr bool is_invocable_v = + is_invocable<F, Args...>::value; + +// mimic: std::is_invocable_r, C++17 +template <typename R, typename F, typename... Args> +struct is_invocable_r : invoke_detail::is_invocable_r<void, R, F, Args...> {}; + +// mimic: std::is_invocable_r_v, C++17 +template <typename R, typename F, typename... Args> +FOLLY_INLINE_VARIABLE constexpr bool is_invocable_r_v = + is_invocable_r<R, F, Args...>::value; + +// mimic: std::is_nothrow_invocable, C++17 +template <typename F, typename... Args> +struct is_nothrow_invocable + : invoke_detail::is_nothrow_invocable<void, F, Args...> {}; + +// mimic: std::is_nothrow_invocable_v, C++17 +template <typename F, typename... Args> +FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_invocable_v = + is_nothrow_invocable<F, Args...>::value; + +// mimic: std::is_nothrow_invocable_r, C++17 +template <typename R, typename F, typename... Args> +struct is_nothrow_invocable_r + : invoke_detail::is_nothrow_invocable_r<void, R, F, Args...> {}; + +// mimic: std::is_nothrow_invocable_r_v, C++17 +template <typename R, typename F, typename... Args> +FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_invocable_r_v = + is_nothrow_invocable_r<R, F, Args...>::value; + +} // namespace folly + +#endif + +namespace folly { + +namespace detail { + +struct invoke_private_overload; + +template <bool, typename Invoke> +struct invoke_traits_base_ {}; +template <typename Invoke> +struct invoke_traits_base_<false, Invoke> {}; +template <typename Invoke> +struct invoke_traits_base_<true, Invoke> { + FOLLY_INLINE_VARIABLE static constexpr Invoke invoke{}; +}; +template <typename Invoke> +using invoke_traits_base = + invoke_traits_base_<is_constexpr_default_constructible_v<Invoke>, Invoke>; + +} // namespace detail + +// invoke_traits +// +// A traits container struct with the following member types, type aliases, and +// variables: +// +// * invoke_result +// * invoke_result_t +// * is_invocable +// * is_invocable_v +// * is_invocable_r +// * is_invocable_r_v +// * is_nothrow_invocable +// * is_nothrow_invocable_v +// * is_nothrow_invocable_r +// * is_nothrow_invocable_r_v +// +// These members have behavior matching the behavior of C++17's corresponding +// invocation traits types, type aliases, and variables, but using invoke_type +// as the invocable argument passed to the usual nivocation traits. +// +// The traits container struct also has a member type alias: +// +// * invoke_type +// +// And an invocable variable as a default-constructed instance of invoke_type, +// if the latter is constexpr default-constructible: +// +// * invoke +template <typename Invoke> +struct invoke_traits : detail::invoke_traits_base<Invoke> { + public: + using invoke_type = Invoke; + + // If invoke_type is constexpr default-constructible: + // + // inline static constexpr invoke_type invoke{}; + + template <typename... Args> + struct invoke_result : folly::invoke_result<Invoke, Args...> {}; + template <typename... Args> + using invoke_result_t = folly::invoke_result_t<Invoke, Args...>; + template <typename... Args> + struct is_invocable : folly::is_invocable<Invoke, Args...> {}; + template <typename... Args> + FOLLY_INLINE_VARIABLE static constexpr bool is_invocable_v = + is_invocable<Args...>::value; + template <typename R, typename... Args> + struct is_invocable_r : folly::is_invocable_r<R, Invoke, Args...> {}; + template <typename R, typename... Args> + FOLLY_INLINE_VARIABLE static constexpr bool is_invocable_r_v = + is_invocable_r<R, Args...>::value; + template <typename... Args> + struct is_nothrow_invocable : folly::is_nothrow_invocable<Invoke, Args...> {}; + template <typename... Args> + FOLLY_INLINE_VARIABLE static constexpr bool is_nothrow_invocable_v = + is_nothrow_invocable<Args...>::value; + template <typename R, typename... Args> + struct is_nothrow_invocable_r + : folly::is_nothrow_invocable_r<R, Invoke, Args...> {}; + template <typename R, typename... Args> + FOLLY_INLINE_VARIABLE static constexpr bool is_nothrow_invocable_r_v = + is_nothrow_invocable_r<R, Args...>::value; +}; + +} // namespace folly + +#define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING_1(_, funcname, ns) \ + using ns::funcname; + +#define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING(_, funcname, ...) \ + BOOST_PP_EXPR_IIF( \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(__VA_ARGS__)), \ + BOOST_PP_LIST_FOR_EACH( \ + FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING_1, \ + funcname, \ + BOOST_PP_TUPLE_TO_LIST((__VA_ARGS__)))) + +/*** + * FOLLY_CREATE_FREE_INVOKER + * + * Used to create an invoker type bound to a specific free-invocable name. + * + * Example: + * + * FOLLY_CREATE_FREE_INVOKER(foo_invoker, foo); + * + * The type `foo_invoker` is generated in the current namespace and may be used + * as follows: + * + * namespace Deep { + * struct CanFoo {}; + * int foo(CanFoo const&, Bar&) { return 1; } + * int foo(CanFoo&&, Car&&) noexcept { return 2; } + * } + * + * using traits = folly::invoke_traits<foo_invoker>; + * + * traits::invoke(Deep::CanFoo{}, Car{}) // 2 + * + * traits::invoke_result<Deep::CanFoo, Bar&> // has member + * traits::invoke_result_t<Deep::CanFoo, Bar&> // int + * traits::invoke_result<Deep::CanFoo, Bar&&> // empty + * traits::invoke_result_t<Deep::CanFoo, Bar&&> // error + * + * traits::is_invocable_v<CanFoo, Bar&> // true + * traits::is_invocable_v<CanFoo, Bar&&> // false + * + * traits::is_invocable_r_v<int, CanFoo, Bar&> // true + * traits::is_invocable_r_v<char*, CanFoo, Bar&> // false + * + * traits::is_nothrow_invocable_v<CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<CanFoo, Car&&> // true + * + * traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true + * traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false + * + * When a name has one or more primary definition in a fixed set of namespaces + * and alternate definitions in the namespaces of its arguments, the primary + * definitions may automatically be found as follows: + * + * FOLLY_CREATE_FREE_INVOKER(swap_invoker, swap, std); + * + * In this case, `swap_invoke_traits::invoke(int&, int&)` will use the primary + * definition found in `namespace std` relative to the current namespace, which + * may be equivalent to `namespace ::std`. In contrast: + * + * namespace Deep { + * struct HasData {}; + * void swap(HasData&, HasData&) { throw 7; } + * } + * + * using traits = invoke_traits<swap_invoker>; + * + * HasData a, b; + * traits::invoke(a, b); // throw 7 + */ +#define FOLLY_CREATE_FREE_INVOKER(classname, funcname, ...) \ + namespace classname##__folly_detail_invoke_ns { \ + FOLLY_MAYBE_UNUSED void funcname( \ + ::folly::detail::invoke_private_overload&); \ + FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING(_, funcname, __VA_ARGS__) \ + struct __folly_detail_invoke_obj { \ + template <typename... Args> \ + FOLLY_ERASE_HACK_GCC constexpr auto operator()(Args&&... args) const \ + noexcept(noexcept(funcname(static_cast<Args&&>(args)...))) \ + -> decltype(funcname(static_cast<Args&&>(args)...)) { \ + return funcname(static_cast<Args&&>(args)...); \ + } \ + }; \ + } \ + struct classname \ + : classname##__folly_detail_invoke_ns::__folly_detail_invoke_obj {} + +/*** + * FOLLY_CREATE_MEMBER_INVOKER + * + * Used to create an invoker type bound to a specific member-invocable name. + * + * Example: + * + * FOLLY_CREATE_MEMBER_INVOKER(foo_invoker, foo); + * + * The type `foo_invoker` is generated in the current namespace and may be used + * as follows: + * + * struct CanFoo { + * int foo(Bar&) { return 1; } + * int foo(Car&&) noexcept { return 2; } + * }; + * + * using traits = folly::invoke_traits<foo_invoker>; + * + * traits::invoke(CanFoo{}, Car{}) // 2 + * + * traits::invoke_result<CanFoo, Bar&> // has member + * traits::invoke_result_t<CanFoo, Bar&> // int + * traits::invoke_result<CanFoo, Bar&&> // empty + * traits::invoke_result_t<CanFoo, Bar&&> // error + * + * traits::is_invocable_v<CanFoo, Bar&> // true + * traits::is_invocable_v<CanFoo, Bar&&> // false + * + * traits::is_invocable_r_v<int, CanFoo, Bar&> // true + * traits::is_invocable_r_v<char*, CanFoo, Bar&> // false + * + * traits::is_nothrow_invocable_v<CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<CanFoo, Car&&> // true + * + * traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false + * traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true + * traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false + */ +#define FOLLY_CREATE_MEMBER_INVOKER(classname, membername) \ + struct classname { \ + template <typename O, typename... Args> \ + FOLLY_ERASE_HACK_GCC constexpr auto operator()(O&& o, Args&&... args) \ + const noexcept(noexcept( \ + static_cast<O&&>(o).membername(static_cast<Args&&>(args)...))) \ + -> decltype(static_cast<O&&>(o).membername( \ + static_cast<Args&&>(args)...)) { \ + return static_cast<O&&>(o).membername(static_cast<Args&&>(args)...); \ + } \ + } + +/*** + * FOLLY_CREATE_STATIC_MEMBER_INVOKER + * + * Used to create an invoker type template bound to a specific static-member- + * invocable name. + * + * Example: + * + * FOLLY_CREATE_STATIC_MEMBER_INVOKER(foo_invoker, foo); + * + * The type template `foo_invoker` is generated in the current namespace and + * may be used as follows: + * + * struct CanFoo { + * static int foo(Bar&) { return 1; } + * static int foo(Car&&) noexcept { return 2; } + * }; + * + * using traits = folly::invoke_traits<foo_invoker<CanFoo>>; + * + * traits::invoke(Car{}) // 2 + * + * traits::invoke_result<Bar&> // has member + * traits::invoke_result_t<Bar&> // int + * traits::invoke_result<Bar&&> // empty + * traits::invoke_result_t<Bar&&> // error + * + * traits::is_invocable_v<Bar&> // true + * traits::is_invocable_v<Bar&&> // false + * + * traits::is_invocable_r_v<int, Bar&> // true + * traits::is_invocable_r_v<char*, Bar&> // false + * + * traits::is_nothrow_invocable_v<Bar&> // false + * traits::is_nothrow_invocable_v<Car&&> // true + * + * traits::is_nothrow_invocable_v<int, Bar&> // false + * traits::is_nothrow_invocable_v<char*, Bar&> // false + * traits::is_nothrow_invocable_v<int, Car&&> // true + * traits::is_nothrow_invocable_v<char*, Car&&> // false + */ +#define FOLLY_CREATE_STATIC_MEMBER_INVOKER(classname, membername) \ + template <typename T> \ + struct classname { \ + template <typename... Args> \ + FOLLY_ERASE constexpr auto operator()(Args&&... args) const \ + noexcept(noexcept(T::membername(static_cast<Args&&>(args)...))) \ + -> decltype(T::membername(static_cast<Args&&>(args)...)) { \ + return T::membername(static_cast<Args&&>(args)...); \ + } \ + } diff --git a/ios/Pods/Flipper-Folly/folly/functional/Partial.h b/ios/Pods/Flipper-Folly/folly/functional/Partial.h new file mode 100644 index 000000000..aedde87ea --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/functional/Partial.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> + +#include <tuple> +#include <utility> + +namespace folly { + +namespace detail { +namespace partial { + +// helper type to make sure that the templated constructor in Partial does +// not accidentally act as copy or move constructor +struct PartialConstructFromCallable {}; + +template <typename F, typename Tuple> +class Partial { + using Indexes = std::make_index_sequence<std::tuple_size<Tuple>{}>; + + template <typename Self, std::size_t... I, typename... Args> + static auto + invokeForward(Self&& self, std::index_sequence<I...>, Args&&... args) + -> decltype(invoke( + std::declval<Self>().f_, + std::get<I>(std::declval<Self>().stored_args_)..., + std::declval<Args>()...)) { + return invoke( + std::forward<Self>(self).f_, + std::get<I>(std::forward<Self>(self).stored_args_)..., + std::forward<Args>(args)...); + } + + public: + template <typename Callable, typename... Args> + Partial(PartialConstructFromCallable, Callable&& callable, Args&&... args) + : f_(std::forward<Callable>(callable)), + stored_args_(std::forward<Args>(args)...) {} + + template <typename... CArgs> + auto operator()(CArgs&&... cargs) & -> decltype(invokeForward( + std::declval<Partial&>(), + Indexes{}, + std::declval<CArgs>()...)) { + return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...); + } + template <typename... CArgs> + auto operator()(CArgs&&... cargs) const& -> decltype(invokeForward( + std::declval<const Partial&>(), + Indexes{}, + std::declval<CArgs>()...)) { + return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...); + } + template <typename... As> + auto operator()(As&&... a) && -> decltype(invokeForward( + std::declval<Partial&&>(), + Indexes{}, + std::declval<As>()...)) { + return invokeForward(std::move(*this), Indexes{}, std::forward<As>(a)...); + } + template <typename... As> + auto operator()(As&&... as) const&& -> decltype(invokeForward( + std::declval<const Partial&&>(), + Indexes{}, + std::declval<As>()...)) { + return invokeForward(std::move(*this), Indexes{}, std::forward<As>(as)...); + } + + private: + // the stored callable + F f_; + // the stored arguments, these will be forwarded along with the actual + // argumnets to the callable above + Tuple stored_args_; +}; + +} // namespace partial +} // namespace detail + +/** + * Partially applies arguments to a callable + * + * `partial` takes a callable and zero or more additional arguments and returns + * a callable object, which when called with zero or more arguments, will invoke + * the original callable with the additional arguments passed to `partial`, + * followed by those passed to the call. + * + * E.g. `partial(Foo, 1, 2)(3)` is equivalent to `Foo(1, 2, 3)`. + * + * `partial` can be used to bind a class method to an instance: + * `partial(&Foo::method, foo_pointer)` returns a callable object that can be + * invoked in the same way as `foo_pointer->method`. In case the first + * argument in a call to `partial` is a member pointer, the second argument + * can be a reference, pointer or any object that can be dereferenced to + * an object of type Foo (like `std::shared_ptr` or `std::unique_ptr`). + * + * `partial` is similar to `std::bind`, but you don't have to use placeholders + * to have arguments passed on. Any number of arguments passed to the object + * returned by `partial` when called will be added to those passed to `partial` + * and passed to the original callable. + */ +template <typename F, typename... Args> +auto partial(F&& f, Args&&... args) -> detail::partial::Partial< + typename std::decay<F>::type, + std::tuple<typename std::decay<Args>::type...>> { + return {detail::partial::PartialConstructFromCallable{}, + std::forward<F>(f), + std::forward<Args>(args)...}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Barrier.cpp b/ios/Pods/Flipper-Folly/folly/futures/Barrier.cpp new file mode 100644 index 000000000..2212e5a8f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Barrier.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/futures/Barrier.h> +#include <folly/lang/Exception.h> + +#include <glog/logging.h> + +namespace folly { +namespace futures { + +Barrier::Barrier(uint32_t n) + : size_(n), controlBlock_(allocateControlBlock()) {} + +Barrier::~Barrier() { + auto block = controlBlock_.load(std::memory_order_relaxed); + auto prev = block->valueAndReaderCount.load(std::memory_order_relaxed); + DCHECK_EQ(prev >> kReaderShift, 0u); + auto val = prev & kValueMask; + auto p = promises(block); + + for (uint32_t i = 0; i < val; ++i) { + p[i].setException( + folly::make_exception_wrapper<std::runtime_error>("Barrier destroyed")); + } + + freeControlBlock(controlBlock_); +} + +auto Barrier::allocateControlBlock() -> ControlBlock* { + auto storage = malloc(controlBlockSize(size_)); + if (!storage) { + throw_exception<std::bad_alloc>(); + } + auto block = ::new (storage) ControlBlock(); + + auto p = promises(block); + uint32_t i = 0; + try { + for (i = 0; i < size_; ++i) { + new (p + i) BoolPromise(); + } + } catch (...) { + for (; i != 0; --i) { + p[i - 1].~BoolPromise(); + } + throw; + } + + return block; +} + +void Barrier::freeControlBlock(ControlBlock* block) { + auto p = promises(block); + for (uint32_t i = size_; i != 0; --i) { + p[i - 1].~BoolPromise(); + } + free(block); +} + +folly::Future<bool> Barrier::wait() { + // Load the current control block first. As we know there is at least + // one thread in the current epoch (us), this means that the value is + // < size_, so controlBlock_ can't change until we bump the value below. + auto block = controlBlock_.load(std::memory_order_acquire); + auto p = promises(block); + + // Bump the value and record ourselves as reader. + // This ensures that block stays allocated, as the reader count is > 0. + auto prev = block->valueAndReaderCount.fetch_add( + kReader + 1, std::memory_order_acquire); + + auto prevValue = static_cast<uint32_t>(prev & kValueMask); + DCHECK_LT(prevValue, size_); + auto future = p[prevValue].getFuture(); + + if (prevValue + 1 == size_) { + // Need to reset the barrier before fulfilling any futures. This is + // when the epoch is flipped to the next. + controlBlock_.store(allocateControlBlock(), std::memory_order_release); + + p[0].setValue(true); + for (uint32_t i = 1; i < size_; ++i) { + p[i].setValue(false); + } + } + + // Free the control block if we're the last reader at max value. + prev = + block->valueAndReaderCount.fetch_sub(kReader, std::memory_order_acq_rel); + if (prev == (kReader | uint64_t(size_))) { + freeControlBlock(block); + } + + return future; +} + +} // namespace futures +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Barrier.h b/ios/Pods/Flipper-Folly/folly/futures/Barrier.h new file mode 100644 index 000000000..915494c6d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Barrier.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstdint> + +#include <folly/futures/Future.h> +#include <folly/futures/Promise.h> + +namespace folly { +namespace futures { + +// A folly::Future-istic Barrier synchronization primitive +// +// The barrier is initialized with a count N. +// +// The first N-1 calls to wait() return uncompleted futures. +// +// The Nth call to wait() completes the previous N-1 futures successfully, +// returns a future that is already completed successfully, and resets the +// barrier; the barrier may be reused immediately, as soon as at least one +// of the future completions has been observed. +// +// Of these N futures, exactly one is completed with true, while the others are +// completed with false; it is unspecified which future completes with true. +// (This may be used to elect a "leader" among a group of threads.) +// +// If the barrier is destroyed, any futures already returned by wait() will +// complete with an error. +class Barrier { + public: + explicit Barrier(uint32_t n); + ~Barrier(); + + folly::Future<bool> wait(); + + private: + typedef folly::Promise<bool> BoolPromise; + + static constexpr uint64_t kReaderShift = 32; + static constexpr uint64_t kReader = uint64_t(1) << kReaderShift; + static constexpr uint64_t kValueMask = kReader - 1; + + // For each "epoch" that the barrier is active, we have a different + // ControlBlock. The ControlBlock contains the current barrier value + // and the number of readers (currently inside wait()) packed into a + // 64-bit value. + // + // The ControlBlock is allocated as long as either: + // - there are threads currently inside wait() (reader count > 0), or + // - the value has not yet reached size_ (value < size_) + // + // The array of size_ Promise objects is allocated immediately following + // valueAndReaderCount. + + struct ControlBlock { + // Reader count in most significant 32 bits + // Value in least significant 32 bits + std::atomic<uint64_t> valueAndReaderCount{0}; + }; + + struct ControlBlockAndPromise { + ControlBlock cb; + BoolPromise promises[1]; + }; + + static BoolPromise* promises(ControlBlock* cb) { + return reinterpret_cast<ControlBlockAndPromise*>(cb)->promises; + } + + static size_t controlBlockSize(size_t n) { + return offsetof(ControlBlockAndPromise, promises) + n * sizeof(BoolPromise); + } + + ControlBlock* allocateControlBlock(); + void freeControlBlock(ControlBlock* b); + + uint32_t size_; + std::atomic<ControlBlock*> controlBlock_; +}; + +} // namespace futures +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Future-inl.h b/ios/Pods/Flipper-Folly/folly/futures/Future-inl.h new file mode 100644 index 000000000..dcaed234b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Future-inl.h @@ -0,0 +1,2608 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <cassert> +#include <chrono> +#include <thread> +#include <utility> + +#include <folly/Optional.h> +#include <folly/Traits.h> +#include <folly/detail/AsyncTrace.h> +#include <folly/executors/ExecutorWithPriority.h> +#include <folly/executors/GlobalExecutor.h> +#include <folly/executors/InlineExecutor.h> +#include <folly/executors/QueuedImmediateExecutor.h> +#include <folly/futures/detail/Core.h> +#include <folly/synchronization/Baton.h> + +#if FOLLY_FUTURE_USING_FIBER +#include <folly/fibers/Baton.h> +#endif + +namespace folly { + +class Timekeeper; + +namespace futures { +namespace detail { +#if FOLLY_FUTURE_USING_FIBER +typedef folly::fibers::Baton FutureBatonType; +#else +typedef folly::Baton<> FutureBatonType; +#endif +} // namespace detail +} // namespace futures + +namespace detail { +std::shared_ptr<Timekeeper> getTimekeeperSingleton(); +} // namespace detail + +namespace futures { +namespace detail { + +// InvokeResultWrapper and wrapInvoke enable wrapping a result value in its +// nearest Future-type counterpart capable of also carrying an exception. +// e.g. +// (semi)Future<T> -> (semi)Future<T> (no change) +// Try<T> -> Try<T> (no change) +// void -> Try<folly::Unit> +// T -> Try<T> +template <typename T> +struct InvokeResultWrapperBase { + template <typename F> + static T wrapResult(F fn) { + return T(fn()); + } + static T wrapException(exception_wrapper&& e) { + return T(std::move(e)); + } +}; +template <typename T> +struct InvokeResultWrapper : InvokeResultWrapperBase<Try<T>> {}; +template <typename T> +struct InvokeResultWrapper<Try<T>> : InvokeResultWrapperBase<Try<T>> {}; +template <typename T> +struct InvokeResultWrapper<SemiFuture<T>> + : InvokeResultWrapperBase<SemiFuture<T>> {}; +template <typename T> +struct InvokeResultWrapper<Future<T>> : InvokeResultWrapperBase<Future<T>> {}; +template <> +struct InvokeResultWrapper<void> : InvokeResultWrapperBase<Try<Unit>> { + template <typename F> + static Try<Unit> wrapResult(F fn) { + fn(); + return Try<Unit>(unit); + } +}; + +template <typename T, typename F> +auto wrapInvoke(folly::Try<T>&& t, F&& f) { + auto fn = [&]() { + return std::forward<F>(f)( + t.template get< + false, + typename futures::detail::valueCallableResult<T, F>::FirstArg>()); + }; + using FnResult = decltype(fn()); + using Wrapper = InvokeResultWrapper<FnResult>; + if (t.hasException()) { + return Wrapper::wrapException(std::move(t).exception()); + } + return Wrapper::wrapResult(fn); +} + +// Guarantees that the stored functor is destructed before the stored promise +// may be fulfilled. Assumes the stored functor to be noexcept-destructible. +template <typename T, typename F> +class CoreCallbackState { + using DF = std::decay_t<F>; + + public: + CoreCallbackState(Promise<T>&& promise, F&& func) noexcept( + noexcept(DF(std::declval<F&&>()))) + : func_(std::forward<F>(func)), promise_(std::move(promise)) { + assert(before_barrier()); + } + + CoreCallbackState(CoreCallbackState&& that) noexcept( + noexcept(DF(std::declval<F&&>()))) { + if (that.before_barrier()) { + new (&func_) DF(std::forward<F>(that.func_)); + promise_ = that.stealPromise(); + } + } + + CoreCallbackState& operator=(CoreCallbackState&&) = delete; + + ~CoreCallbackState() { + if (before_barrier()) { + stealPromise(); + } + } + + template <typename... Args> + auto invoke(Args&&... args) noexcept( + noexcept(std::declval<F&&>()(std::declval<Args&&>()...))) { + assert(before_barrier()); + return std::forward<F>(func_)(std::forward<Args>(args)...); + } + + template <typename... Args> + auto tryInvoke(Args&&... args) noexcept { + return makeTryWith([&] { return invoke(std::forward<Args>(args)...); }); + } + + void setTry(Executor::KeepAlive<>&& keepAlive, Try<T>&& t) { + stealPromise().setTry(std::move(keepAlive), std::move(t)); + } + + void setException(Executor::KeepAlive<>&& keepAlive, exception_wrapper&& ew) { + setTry(std::move(keepAlive), Try<T>(std::move(ew))); + } + + Promise<T> stealPromise() noexcept { + assert(before_barrier()); + func_.~DF(); + return std::move(promise_); + } + + private: + bool before_barrier() const noexcept { + return !promise_.isFulfilled(); + } + + union { + DF func_; + }; + Promise<T> promise_{Promise<T>::makeEmpty()}; +}; + +template <typename T, typename F> +auto makeCoreCallbackState(Promise<T>&& p, F&& f) noexcept( + noexcept(CoreCallbackState<T, F>( + std::declval<Promise<T>&&>(), + std::declval<F&&>()))) { + return CoreCallbackState<T, F>(std::move(p), std::forward<F>(f)); +} + +template <typename T, typename R, typename... Args> +auto makeCoreCallbackState(Promise<T>&& p, R (&f)(Args...)) noexcept { + return CoreCallbackState<T, R (*)(Args...)>(std::move(p), &f); +} + +template <class T> +FutureBase<T>::FutureBase(SemiFuture<T>&& other) noexcept : core_(other.core_) { + other.core_ = nullptr; +} + +template <class T> +FutureBase<T>::FutureBase(Future<T>&& other) noexcept : core_(other.core_) { + other.core_ = nullptr; +} + +template <class T> +template <class T2, typename> +FutureBase<T>::FutureBase(T2&& val) + : core_(Core::make(Try<T>(std::forward<T2>(val)))) {} + +template <class T> +template <typename T2> +FutureBase<T>::FutureBase( + typename std::enable_if<std::is_same<Unit, T2>::value>::type*) + : core_(Core::make(Try<T>(T()))) {} + +template <class T> +void FutureBase<T>::assign(FutureBase<T>&& other) noexcept { + detach(); + core_ = std::exchange(other.core_, nullptr); +} + +template <class T> +FutureBase<T>::~FutureBase() { + detach(); +} + +template <class T> +T& FutureBase<T>::value() & { + return result().value(); +} + +template <class T> +T const& FutureBase<T>::value() const& { + return result().value(); +} + +template <class T> +T&& FutureBase<T>::value() && { + return std::move(result().value()); +} + +template <class T> +T const&& FutureBase<T>::value() const&& { + return std::move(result().value()); +} + +template <class T> +Try<T>& FutureBase<T>::result() & { + return getCoreTryChecked(); +} + +template <class T> +Try<T> const& FutureBase<T>::result() const& { + return getCoreTryChecked(); +} + +template <class T> +Try<T>&& FutureBase<T>::result() && { + return std::move(getCoreTryChecked()); +} + +template <class T> +Try<T> const&& FutureBase<T>::result() const&& { + return std::move(getCoreTryChecked()); +} + +template <class T> +bool FutureBase<T>::isReady() const { + return getCore().hasResult(); +} + +template <class T> +bool FutureBase<T>::hasValue() const { + return result().hasValue(); +} + +template <class T> +bool FutureBase<T>::hasException() const { + return result().hasException(); +} + +template <class T> +void FutureBase<T>::detach() { + if (core_) { + core_->detachFuture(); + core_ = nullptr; + } +} + +template <class T> +void FutureBase<T>::throwIfInvalid() const { + if (!core_) { + throw_exception<FutureInvalid>(); + } +} + +template <class T> +void FutureBase<T>::throwIfContinued() const { + if (!core_ || core_->hasCallback()) { + throw_exception<FutureAlreadyContinued>(); + } +} + +template <class T> +Optional<Try<T>> FutureBase<T>::poll() { + auto& core = getCore(); + return core.hasResult() ? Optional<Try<T>>(std::move(core.getTry())) + : Optional<Try<T>>(); +} + +template <class T> +void FutureBase<T>::raise(exception_wrapper exception) { + getCore().raise(std::move(exception)); +} + +template <class T> +void FutureBase<T>::setCallback_( + CoreCallback&& func, + futures::detail::InlineContinuation allowInline) { + throwIfContinued(); + getCore().setCallback( + std::move(func), RequestContext::saveContext(), allowInline); +} + +template <class T> +FutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept + : core_(nullptr) {} + +// MSVC 2017 Update 7 released with a bug that causes issues expanding to an +// empty parameter pack when invoking a templated member function. It should +// be fixed for MSVC 2017 Update 8. +// TODO: Remove. +namespace detail_msvc_15_7_workaround { +template <typename R, std::size_t S> +using IfArgsSizeIs = std::enable_if_t<R::Arg::ArgsSize::value == S, int>; +template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0> +decltype(auto) +invoke(R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) { + return state.invoke(); +} +template <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0> +decltype(auto) invoke(R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) { + using Arg1 = typename R::Arg::ArgList::Tail::FirstArg; + return state.invoke( + std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>()); +} +template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0> +decltype(auto) +tryInvoke(R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) { + return state.tryInvoke(); +} +template <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0> +decltype(auto) +tryInvoke(R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) { + using Arg1 = typename R::Arg::ArgList::Tail::FirstArg; + return state.tryInvoke( + std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>()); +} +} // namespace detail_msvc_15_7_workaround + +// then + +// Variant: returns a value +// e.g. f.then([](Try<T>&& t){ return t.value(); }); +template <class T> +template <typename F, typename R> +typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type +FutureBase<T>::thenImplementation( + F&& func, + R, + futures::detail::InlineContinuation allowInline) { + static_assert(R::Arg::ArgsSize::value == 2, "Then must take two arguments"); + typedef typename R::ReturnsFuture::Inner B; + + Promise<B> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + + // grab the Future now before we lose our handle on the Promise + auto sf = p.getSemiFuture(); + sf.setExecutor(folly::Executor::KeepAlive<>{this->getExecutor()}); + auto f = Future<B>(sf.core_); + sf.core_ = nullptr; + + /* This is a bit tricky. + + We can't just close over *this in case this Future gets moved. So we + make a new dummy Future. We could figure out something more + sophisticated that avoids making a new Future object when it can, as an + optimization. But this is correct. + + core_ can't be moved, it is explicitly disallowed (as is copying). But + if there's ever a reason to allow it, this is one place that makes that + assumption and would need to be fixed. We use a standard shared pointer + for core_ (by copying it in), which means in essence obj holds a shared + pointer to itself. But this shouldn't leak because Promise will not + outlive the continuation, because Promise will setException() with a + broken Promise if it is destructed before completed. We could use a + weak pointer but it would have to be converted to a shared pointer when + func is executed (because the Future returned by func may possibly + persist beyond the callback, if it gets moved), and so it is an + optimization to just make it shared from the get-go. + + Two subtle but important points about this design. futures::detail::Core + has no back pointers to Future or Promise, so if Future or Promise get + moved (and they will be moved in performant code) we don't have to do + anything fancy. And because we store the continuation in the + futures::detail::Core, not in the Future, we can execute the continuation + even after the Future has gone out of scope. This is an intentional design + decision. It is likely we will want to be able to cancel a continuation + in some circumstances, but I think it should be explicit not implicit + in the destruction of the Future used to create it. + */ + this->setCallback_( + [state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T>&& t) mutable { + if (!R::Arg::isTry() && t.hasException()) { + state.setException(std::move(ka), std::move(t.exception())); + } else { + auto propagateKA = ka.copy(); + state.setTry(std::move(propagateKA), makeTryWith([&] { + return detail_msvc_15_7_workaround::invoke( + R{}, state, std::move(ka), std::move(t)); + })); + } + }, + allowInline); + return f; +} + +// Pass through a simple future as it needs no deferral adaptation +template <class T> +Future<T> chainExecutor(Executor::KeepAlive<>, Future<T>&& f) { + return std::move(f); +} + +// Correctly chain a SemiFuture for deferral +template <class T> +Future<T> chainExecutor(Executor::KeepAlive<> e, SemiFuture<T>&& f) { + if (!e) { + e = folly::getKeepAliveToken(InlineExecutor::instance()); + } + return std::move(f).via(e); +} + +// Variant: returns a Future +// e.g. f.then([](T&& t){ return makeFuture<T>(t); }); +template <class T> +template <typename F, typename R> +typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type +FutureBase<T>::thenImplementation( + F&& func, + R, + futures::detail::InlineContinuation allowInline) { + static_assert(R::Arg::ArgsSize::value == 2, "Then must take two arguments"); + typedef typename R::ReturnsFuture::Inner B; + + Promise<B> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + + // grab the Future now before we lose our handle on the Promise + auto sf = p.getSemiFuture(); + auto e = getKeepAliveToken(this->getExecutor()); + sf.setExecutor(std::move(e)); + auto f = Future<B>(sf.core_); + sf.core_ = nullptr; + + this->setCallback_( + [state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T>&& t) mutable { + if (!R::Arg::isTry() && t.hasException()) { + state.setException(std::move(ka), std::move(t.exception())); + } else { + // Ensure that if function returned a SemiFuture we correctly chain + // potential deferral. + auto tf2 = detail_msvc_15_7_workaround::tryInvoke( + R{}, state, ka.copy(), std::move(t)); + if (tf2.hasException()) { + state.setException(std::move(ka), std::move(tf2.exception())); + } else { + auto statePromise = state.stealPromise(); + auto tf3 = chainExecutor(std::move(ka), *std::move(tf2)); + std::exchange(statePromise.core_, nullptr) + ->setProxy(std::exchange(tf3.core_, nullptr)); + } + } + }, + allowInline); + + return f; +} + +class WaitExecutor final : public folly::Executor { + public: + void add(Func func) override { + auto wQueue = queue_.wlock(); + if (wQueue->detached) { + return; + } + bool empty = wQueue->funcs.empty(); + wQueue->funcs.push_back(std::move(func)); + if (empty) { + baton_.post(); + } + } + + void drive() { + baton_.wait(); +#if FOLLY_FUTURE_USING_FIBER + fibers::runInMainContext([&]() { +#endif + baton_.reset(); + auto funcs = std::move(queue_.wlock()->funcs); + for (auto& func : funcs) { + std::exchange(func, nullptr)(); + } +#if FOLLY_FUTURE_USING_FIBER + }); +#endif + } + + using Clock = std::chrono::steady_clock; + + bool driveUntil(Clock::time_point deadline) { + if (!baton_.try_wait_until(deadline)) { + return false; + } +#if FOLLY_FUTURE_USING_FIBER + return fibers::runInMainContext([&]() { +#endif + baton_.reset(); + auto funcs = std::move(queue_.wlock()->funcs); + for (auto& func : funcs) { + std::exchange(func, nullptr)(); + } + return true; +#if FOLLY_FUTURE_USING_FIBER + }); +#endif + } + + void detach() { + // Make sure we don't hold the lock while destroying funcs. + [&] { + auto wQueue = queue_.wlock(); + wQueue->detached = true; + return std::move(wQueue->funcs); + }(); + } + + static KeepAlive<WaitExecutor> create() { + return makeKeepAlive<WaitExecutor>(new WaitExecutor()); + } + + private: + WaitExecutor() {} + + bool keepAliveAcquire() override { + auto keepAliveCount = + keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCount > 0); + return true; + } + + void keepAliveRelease() override { + auto keepAliveCount = + keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCount > 0); + if (keepAliveCount == 1) { + delete this; + } + } + + struct Queue { + std::vector<Func> funcs; + bool detached{false}; + }; + + folly::Synchronized<Queue> queue_; + FutureBatonType baton_; + + std::atomic<ssize_t> keepAliveCount_{1}; +}; + +// Vector-like structure to play with window, +// which otherwise expects a vector of size `times`, +// which would be expensive with large `times` sizes. +struct WindowFakeVector { + using iterator = std::vector<size_t>::iterator; + + WindowFakeVector(size_t size) : size_(size) {} + + size_t operator[](const size_t index) const { + return index; + } + size_t size() const { + return size_; + } + + private: + size_t size_; +}; +} // namespace detail +} // namespace futures + +template <class T> +SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) { + return makeSemiFuture(Try<typename std::decay<T>::type>(std::forward<T>(t))); +} + +// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T> +template <class F> +typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F>>::value, + SemiFuture<typename invoke_result_t<F>::value_type>>::type +makeSemiFutureWith(F&& func) { + using InnerType = typename isFutureOrSemiFuture<invoke_result_t<F>>::Inner; + try { + return std::forward<F>(func)(); + } catch (std::exception& e) { + return makeSemiFuture<InnerType>( + exception_wrapper(std::current_exception(), e)); + } catch (...) { + return makeSemiFuture<InnerType>( + exception_wrapper(std::current_exception())); + } +} + +// makeSemiFutureWith(T()) -> SemiFuture<T> +// makeSemiFutureWith(void()) -> SemiFuture<Unit> +template <class F> +typename std::enable_if< + !(isFutureOrSemiFuture<invoke_result_t<F>>::value), + SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type +makeSemiFutureWith(F&& func) { + using LiftedResult = lift_unit_t<invoke_result_t<F>>; + return makeSemiFuture<LiftedResult>( + makeTryWith([&func]() mutable { return std::forward<F>(func)(); })); +} + +template <class T> +SemiFuture<T> makeSemiFuture(std::exception_ptr const& e) { + return makeSemiFuture(Try<T>(e)); +} + +template <class T> +SemiFuture<T> makeSemiFuture(exception_wrapper ew) { + return makeSemiFuture(Try<T>(std::move(ew))); +} + +template <class T, class E> +typename std:: + enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type + makeSemiFuture(E const& e) { + return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e))); +} + +template <class T> +SemiFuture<T> makeSemiFuture(Try<T> t) { + return SemiFuture<T>(SemiFuture<T>::Core::make(std::move(t))); +} + +// This must be defined after the constructors to avoid a bug in MSVC +// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244 +inline SemiFuture<Unit> makeSemiFuture() { + return makeSemiFuture(Unit{}); +} + +template <class T> +SemiFuture<T> SemiFuture<T>::makeEmpty() { + return SemiFuture<T>(futures::detail::EmptyConstruct{}); +} + +template <class T> +futures::detail::DeferredWrapper SemiFuture<T>::stealDeferredExecutor() { + return this->getCore().stealDeferredExecutor(); +} + +template <class T> +void SemiFuture<T>::releaseDeferredExecutor(Core* core) { + if (!core || core->hasCallback()) { + return; + } + if (auto executor = core->stealDeferredExecutor()) { + executor.get()->detach(); + } +} + +template <class T> +SemiFuture<T>::~SemiFuture() { + releaseDeferredExecutor(this->core_); +} + +template <class T> +SemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept + : futures::detail::FutureBase<T>(std::move(other)) {} + +template <class T> +SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept + : futures::detail::FutureBase<T>(std::move(other)) { + // SemiFuture should not have an executor on construction + if (this->core_) { + this->setExecutor(futures::detail::KeepAliveOrDeferred{}); + } +} + +template <class T> +SemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept { + releaseDeferredExecutor(this->core_); + this->assign(std::move(other)); + return *this; +} + +template <class T> +SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept { + releaseDeferredExecutor(this->core_); + this->assign(std::move(other)); + // SemiFuture should not have an executor on construction + if (this->core_) { + this->setExecutor(Executor::KeepAlive<>{}); + } + return *this; +} + +template <class T> +Future<T> SemiFuture<T>::via(Executor::KeepAlive<> executor) && { + folly::async_tracing::logSemiFutureVia(this->getExecutor(), executor.get()); + + if (!executor) { + throw_exception<FutureNoExecutor>(); + } + + if (auto deferredExecutor = this->getDeferredExecutor()) { + deferredExecutor->setExecutor(executor.copy()); + } + + auto newFuture = Future<T>(this->core_); + this->core_ = nullptr; + newFuture.setExecutor(std::move(executor)); + + return newFuture; +} + +template <class T> +Future<T> SemiFuture<T>::via( + Executor::KeepAlive<> executor, + int8_t priority) && { + return std::move(*this).via( + ExecutorWithPriority::create(std::move(executor), priority)); +} + +template <class T> +Future<T> SemiFuture<T>::toUnsafeFuture() && { + return std::move(*this).via(&InlineExecutor::instance()); +} + +template <class T> +template <typename F> +SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type> +SemiFuture<T>::defer(F&& func) && { + auto deferredExecutorPtr = this->getDeferredExecutor(); + futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() { + if (deferredExecutorPtr) { + return futures::detail::KeepAliveOrDeferred{deferredExecutorPtr->copy()}; + } else { + auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred( + futures::detail::DeferredExecutor::create()); + this->setExecutor(newDeferredExecutor.copy()); + return newDeferredExecutor; + } + }(); + + auto sf = Future<T>(this->core_).thenTryInline(std::forward<F>(func)).semi(); + this->core_ = nullptr; + // Carry deferred executor through chain as constructor from Future will + // nullify it + sf.setExecutor(std::move(deferredExecutor)); + return sf; +} + +template <class T> +template <typename F> +SemiFuture< + typename futures::detail::tryExecutorCallableResult<T, F>::value_type> +SemiFuture<T>::deferExTry(F&& func) && { + auto deferredExecutorPtr = this->getDeferredExecutor(); + futures::detail::DeferredWrapper deferredExecutor = [&]() mutable { + if (deferredExecutorPtr) { + return deferredExecutorPtr->copy(); + } else { + auto newDeferredExecutor = futures::detail::DeferredExecutor::create(); + this->setExecutor( + futures::detail::KeepAliveOrDeferred{newDeferredExecutor->copy()}); + return newDeferredExecutor; + } + }(); + + auto sf = Future<T>(this->core_) + .thenExTryInline([func = std::forward<F>(func)]( + folly::Executor::KeepAlive<>&& keepAlive, + folly::Try<T>&& val) mutable { + return std::forward<F>(func)( + std::move(keepAlive), std::forward<decltype(val)>(val)); + }) + .semi(); + this->core_ = nullptr; + // Carry deferred executor through chain as constructor from Future will + // nullify it + sf.setExecutor( + futures::detail::KeepAliveOrDeferred{std::move(deferredExecutor)}); + return sf; +} + +template <class T> +template <typename F> +SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type> +SemiFuture<T>::deferValue(F&& func) && { + return std::move(*this).defer( + [f = std::forward<F>(func)](folly::Try<T>&& t) mutable { + return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f)); + }); +} + +template <class T> +template <typename F> +SemiFuture< + typename futures::detail::valueExecutorCallableResult<T, F>::value_type> +SemiFuture<T>::deferExValue(F&& func) && { + return std::move(*this).deferExTry( + [f = std::forward<F>(func)]( + folly::Executor::KeepAlive<> ka, folly::Try<T>&& t) mutable { + return std::forward<F>(f)( + ka, + t.template get< + false, + typename futures::detail::valueExecutorCallableResult<T, F>:: + ValueArg>()); + }); +} + +template <class T> +template <class ExceptionType, class F> +SemiFuture<T> SemiFuture<T>::deferError(tag_t<ExceptionType>, F&& func) && { + return std::move(*this).defer( + [func = std::forward<F>(func)](Try<T>&& t) mutable { + if (auto e = t.template tryGetExceptionObject<ExceptionType>()) { + return makeSemiFutureWith( + [&]() mutable { return std::forward<F>(func)(*e); }); + } else { + return makeSemiFuture<T>(std::move(t)); + } + }); +} + +template <class T> +template <class F> +SemiFuture<T> SemiFuture<T>::deferError(F&& func) && { + return std::move(*this).defer( + [func = std::forward<F>(func)](Try<T> t) mutable { + if (t.hasException()) { + return makeSemiFutureWith([&]() mutable { + return std::forward<F>(func)(std::move(t.exception())); + }); + } else { + return makeSemiFuture<T>(std::move(t)); + } + }); +} + +template <class T> +SemiFuture<Unit> SemiFuture<T>::unit() && { + return std::move(*this).deferValue([](T&&) {}); +} + +template <typename T> +SemiFuture<T> SemiFuture<T>::delayed(HighResDuration dur, Timekeeper* tk) && { + return collectAllSemiFuture(*this, futures::sleep(dur, tk)) + .deferValue([](std::tuple<Try<T>, Try<Unit>> tup) { + Try<T>& t = std::get<0>(tup); + return makeFuture<T>(std::move(t)); + }); +} + +template <class T> +Future<T> Future<T>::makeEmpty() { + return Future<T>(futures::detail::EmptyConstruct{}); +} + +template <class T> +Future<T>::Future(Future<T>&& other) noexcept + : futures::detail::FutureBase<T>(std::move(other)) {} + +template <class T> +Future<T>& Future<T>::operator=(Future<T>&& other) noexcept { + this->assign(std::move(other)); + return *this; +} + +// unwrap + +template <class T> +template <class F> +typename std:: + enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type + Future<T>::unwrap() && { + return std::move(*this).thenValue( + [](Future<typename isFuture<T>::Inner> internal_future) { + return internal_future; + }); +} + +template <class T> +Future<T> Future<T>::via(Executor::KeepAlive<> executor) && { + folly::async_tracing::logFutureVia(this->getExecutor(), executor.get()); + + this->setExecutor(std::move(executor)); + + auto newFuture = Future<T>(this->core_); + this->core_ = nullptr; + return newFuture; +} + +template <class T> +Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) && { + return std::move(*this).via( + ExecutorWithPriority::create(std::move(executor), priority)); +} + +template <class T> +Future<T> Future<T>::via(Executor::KeepAlive<> executor) & { + folly::async_tracing::logFutureVia(this->getExecutor(), executor.get()); + + this->throwIfInvalid(); + Promise<T> p; + auto sf = p.getSemiFuture(); + auto func = [p = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable { + p.setTry(std::move(t)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(func)>; + this->thenImplementation( + std::move(func), R{}, futures::detail::InlineContinuation::forbid); + // Construct future from semifuture manually because this may not have + // an executor set due to legacy code. This means we can bypass the executor + // check in SemiFuture::via + auto f = Future<T>(sf.core_); + sf.core_ = nullptr; + return std::move(f).via(std::move(executor)); +} + +template <class T> +Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) & { + return this->via(ExecutorWithPriority::create(std::move(executor), priority)); +} + +template <typename T> +template <typename R, typename Caller, typename... Args> +Future<typename isFuture<R>::Inner> Future<T>::then( + R (Caller::*func)(Args...), + Caller* instance) && { + using FirstArg = + remove_cvref_t<typename futures::detail::ArgType<Args...>::FirstArg>; + + return std::move(*this).thenTry([instance, func](Try<T>&& t) { + return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...); + }); +} + +template <class T> +template <typename F> +Future<typename futures::detail::tryCallableResult<T, F>::value_type> +Future<T>::thenTry(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + folly::Executor::KeepAlive<>&&, + folly::Try<T>&& t) mutable { + return std::forward<F>(f)(std::move(t)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid); +} + +template <class T> +template <typename F> +Future<typename futures::detail::tryCallableResult<T, F>::value_type> +Future<T>::thenTryInline(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + folly::Executor::KeepAlive<>&&, + folly::Try<T>&& t) mutable { + return std::forward<F>(f)(std::move(t)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit); +} + +template <class T> +template <typename F> +Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type> +Future<T>::thenExTry(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable { + // Enforce that executor cannot be null + DCHECK(ka); + return std::forward<F>(f)(std::move(ka), std::move(t)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid); +} + +template <class T> +template <typename F> +Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type> +Future<T>::thenExTryInline(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable { + // Enforce that executor cannot be null + DCHECK(ka); + return std::forward<F>(f)(std::move(ka), std::move(t)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit); +} + +template <class T> +template <typename F> +Future<typename futures::detail::valueCallableResult<T, F>::value_type> +Future<T>::thenValue(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable { + return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid); +} + +template <class T> +template <typename F> +Future<typename futures::detail::valueCallableResult<T, F>::value_type> +Future<T>::thenValueInline(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable { + return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f)); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit); +} + +template <class T> +template <typename F> +Future<typename futures::detail::valueExecutorCallableResult<T, F>::value_type> +Future<T>::thenExValue(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable { + // Enforce that executor cannot be null + DCHECK(ka); + return std::forward<F>(f)( + std::move(ka), + t.template get< + false, + typename futures::detail::valueExecutorCallableResult<T, F>:: + ValueArg>()); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid); +} + +template <class T> +template <typename F> +Future<typename futures::detail::valueExecutorCallableResult<T, F>::value_type> +Future<T>::thenExValueInline(F&& func) && { + auto lambdaFunc = [f = std::forward<F>(func)]( + Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable { + // Enforce that executor cannot be null + DCHECK(ka); + return std::forward<F>(f)( + std::move(ka), + t.template get< + false, + typename futures::detail::valueExecutorCallableResult<T, F>:: + ValueArg>()); + }; + using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>; + return this->thenImplementation( + std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit); +} + +template <class T> +template <class ExceptionType, class F> +typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, + Future<T>>::type +Future<T>::thenError(tag_t<ExceptionType>, F&& func) && { + Promise<T> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + auto sf = p.getSemiFuture(); + auto* ePtr = this->getExecutor(); + auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); + + this->setCallback_([state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T>&& t) mutable { + if (auto ex = t.template tryGetExceptionObject< + std::remove_reference_t<ExceptionType>>()) { + auto tf2 = state.tryInvoke(std::move(*ex)); + if (tf2.hasException()) { + state.setException(std::move(ka), std::move(tf2.exception())); + } else { + tf2->setCallback_( + [p = state.stealPromise()]( + Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable { + p.setTry(std::move(innerKA), std::move(t3)); + }); + } + } else { + state.setTry(std::move(ka), std::move(t)); + } + }); + + return std::move(sf).via(std::move(e)); +} + +template <class T> +template <class ExceptionType, class F> +typename std::enable_if< + !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, + Future<T>>::type +Future<T>::thenError(tag_t<ExceptionType>, F&& func) && { + Promise<T> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + auto sf = p.getSemiFuture(); + auto* ePtr = this->getExecutor(); + auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); + + this->setCallback_([state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T>&& t) mutable { + if (auto ex = t.template tryGetExceptionObject< + std::remove_reference_t<ExceptionType>>()) { + state.setTry(std::move(ka), makeTryWith([&] { + return state.invoke(std::move(*ex)); + })); + } else { + state.setTry(std::move(ka), std::move(t)); + } + }); + + return std::move(sf).via(std::move(e)); +} + +template <class T> +template <class F> +typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value, + Future<T>>::type +Future<T>::thenError(F&& func) && { + auto* ePtr = this->getExecutor(); + auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); + + Promise<T> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + auto sf = p.getSemiFuture(); + this->setCallback_([state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T> t) mutable { + if (t.hasException()) { + auto tf2 = state.tryInvoke(std::move(t.exception())); + if (tf2.hasException()) { + state.setException(std::move(ka), std::move(tf2.exception())); + } else { + tf2->setCallback_( + [p = state.stealPromise()]( + Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable { + p.setTry(std::move(innerKA), std::move(t3)); + }); + } + } else { + state.setTry(std::move(ka), std::move(t)); + } + }); + + return std::move(sf).via(std::move(e)); +} + +template <class T> +template <class F> +typename std::enable_if< + !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value, + Future<T>>::type +Future<T>::thenError(F&& func) && { + auto* ePtr = this->getExecutor(); + auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); + + Promise<T> p; + p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler()); + auto sf = p.getSemiFuture(); + this->setCallback_([state = futures::detail::makeCoreCallbackState( + std::move(p), std::forward<F>(func))]( + Executor::KeepAlive<>&& ka, Try<T>&& t) mutable { + if (t.hasException()) { + state.setTry(std::move(ka), makeTryWith([&] { + return state.invoke(std::move(t.exception())); + })); + } else { + state.setTry(std::move(ka), std::move(t)); + } + }); + + return std::move(sf).via(std::move(e)); +} + +template <class T> +Future<Unit> Future<T>::then() && { + return std::move(*this).thenValue([](T&&) {}); +} + +template <class T> +template <class F> +Future<T> Future<T>::ensure(F&& func) && { + return std::move(*this).thenTry( + [funcw = std::forward<F>(func)](Try<T>&& t) mutable { + std::forward<F>(funcw)(); + return makeFuture(std::move(t)); + }); +} + +template <class T> +template <class F> +Future<T> +Future<T>::onTimeout(HighResDuration dur, F&& func, Timekeeper* tk) && { + return std::move(*this).within(dur, tk).thenError( + tag_t<FutureTimeout>{}, + [funcw = std::forward<F>(func)](auto const&) mutable { + return std::forward<F>(funcw)(); + }); +} + +template <class Func> +auto via(Executor::KeepAlive<> x, Func&& func) -> Future< + typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner> { + return via(std::move(x)) + .thenValue([f = std::forward<Func>(func)](auto&&) mutable { + return std::forward<Func>(f)(); + }); +} + +// makeFuture + +template <class T> +Future<typename std::decay<T>::type> makeFuture(T&& t) { + return makeFuture(Try<typename std::decay<T>::type>(std::forward<T>(t))); +} + +inline Future<Unit> makeFuture() { + return makeFuture(Unit{}); +} + +// makeFutureWith(Future<T>()) -> Future<T> +template <class F> +typename std:: + enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type + makeFutureWith(F&& func) { + using InnerType = typename isFuture<invoke_result_t<F>>::Inner; + try { + return std::forward<F>(func)(); + } catch (std::exception& e) { + return makeFuture<InnerType>( + exception_wrapper(std::current_exception(), e)); + } catch (...) { + return makeFuture<InnerType>(exception_wrapper(std::current_exception())); + } +} + +// makeFutureWith(T()) -> Future<T> +// makeFutureWith(void()) -> Future<Unit> +template <class F> +typename std::enable_if< + !(isFuture<invoke_result_t<F>>::value), + Future<lift_unit_t<invoke_result_t<F>>>>::type +makeFutureWith(F&& func) { + using LiftedResult = lift_unit_t<invoke_result_t<F>>; + return makeFuture<LiftedResult>( + makeTryWith([&func]() mutable { return std::forward<F>(func)(); })); +} + +template <class T> +Future<T> makeFuture(std::exception_ptr const& e) { + return makeFuture(Try<T>(e)); +} + +template <class T> +Future<T> makeFuture(exception_wrapper ew) { + return makeFuture(Try<T>(std::move(ew))); +} + +template <class T, class E> +typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>:: + type + makeFuture(E const& e) { + return makeFuture(Try<T>(make_exception_wrapper<E>(e))); +} + +template <class T> +Future<T> makeFuture(Try<T> t) { + return Future<T>(Future<T>::Core::make(std::move(t))); +} + +// via +Future<Unit> via(Executor::KeepAlive<> executor) { + return makeFuture().via(std::move(executor)); +} + +Future<Unit> via(Executor::KeepAlive<> executor, int8_t priority) { + return makeFuture().via(std::move(executor), priority); +} + +namespace futures { +namespace detail { + +template <typename V, typename... Fs, std::size_t... Is> +FOLLY_ERASE void foreach_(std::index_sequence<Is...>, V&& v, Fs&&... fs) { + using _ = int[]; + void(_{0, (void(v(index_constant<Is>{}, static_cast<Fs&&>(fs))), 0)...}); +} +template <typename V, typename... Fs> +FOLLY_ERASE void foreach(V&& v, Fs&&... fs) { + using _ = std::index_sequence_for<Fs...>; + foreach_(_{}, static_cast<V&&>(v), static_cast<Fs&&>(fs)...); +} + +template <typename T> +futures::detail::DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) { + return future.getDeferredExecutor(); +} + +template <typename T> +futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future) { + return future.stealDeferredExecutor(); +} + +template <typename T> +futures::detail::DeferredWrapper stealDeferredExecutor(Future<T>&) { + return {}; +} + +template <typename... Ts> +void stealDeferredExecutorsVariadic( + std::vector<futures::detail::DeferredWrapper>& executors, + Ts&... ts) { + foreach( + [&](auto, auto& future) { + if (auto executor = stealDeferredExecutor(future)) { + executors.push_back(std::move(executor)); + } + }, + ts...); +} + +template <class InputIterator> +void stealDeferredExecutors( + std::vector<futures::detail::DeferredWrapper>& executors, + InputIterator first, + InputIterator last) { + for (auto it = first; it != last; ++it) { + if (auto executor = stealDeferredExecutor(*it)) { + executors.push_back(std::move(executor)); + } + } +} +} // namespace detail +} // namespace futures + +// collectAll (variadic) + +template <typename... Fs> +SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAll(Fs&&... fs) { + using Result = std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>; + struct Context { + ~Context() { + p.setValue(std::move(results)); + } + Promise<Result> p; + Result results; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutorsVariadic(executors, fs...); + + auto ctx = std::make_shared<Context>(); + futures::detail::foreach( + [&](auto i, auto&& f) { + f.setCallback_([i, ctx](auto&&, auto&& t) { + std::get<i.value>(ctx->results) = std::move(t); + }); + }, + static_cast<Fs&&>(fs)...); + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + auto work = [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }; + future = std::move(future).defer(work); + auto deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +template <typename... Fs> +SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAllSemiFuture(Fs&&... fs) { + return collectAll(std::forward<Fs>(fs)...); +} + +template <typename... Fs> +Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAllUnsafe(Fs&&... fs) { + return collectAllSemiFuture(std::forward<Fs>(fs)...).toUnsafeFuture(); +} + +// collectAll (iterator) + +template <class InputIterator> +SemiFuture<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAll(InputIterator first, InputIterator last) { + using F = typename std::iterator_traits<InputIterator>::value_type; + using T = typename F::value_type; + + struct Context { + explicit Context(size_t n) : results(n), count(n) {} + ~Context() { + futures::detail::setTry( + p, std::move(ka), Try<std::vector<Try<T>>>(std::move(results))); + } + Promise<std::vector<Try<T>>> p; + Executor::KeepAlive<> ka; + std::vector<Try<T>> results; + std::atomic<size_t> count; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + auto ctx = std::make_shared<Context>(size_t(std::distance(first, last))); + + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_( + [i, ctx](Executor::KeepAlive<>&& ka, Try<T>&& t) { + ctx->results[i] = std::move(t); + if (ctx->count.fetch_sub(1, std::memory_order_acq_rel) == 1) { + ctx->ka = std::move(ka); + } + }, + futures::detail::InlineContinuation::permit); + } + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + future = std::move(future).defer( + [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }); + auto deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +template <class InputIterator> +Future<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAllUnsafe(InputIterator first, InputIterator last) { + return collectAll(first, last).toUnsafeFuture(); +} + +template <class InputIterator> +SemiFuture<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAllSemiFuture(InputIterator first, InputIterator last) { + return collectAll(first, last); +} + +// collect (iterator) + +template <class InputIterator> +SemiFuture<std::vector< + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collect(InputIterator first, InputIterator last) { + using F = typename std::iterator_traits<InputIterator>::value_type; + using T = typename F::value_type; + + struct Context { + explicit Context(size_t n) : result(n) { + finalResult.reserve(n); + } + ~Context() { + if (!threw.load(std::memory_order_relaxed)) { + // map Optional<T> -> T + std::transform( + result.begin(), + result.end(), + std::back_inserter(finalResult), + [](Optional<T>& o) { return std::move(o.value()); }); + p.setValue(std::move(finalResult)); + } + } + Promise<std::vector<T>> p; + std::vector<Optional<T>> result; + std::vector<T> finalResult; + std::atomic<bool> threw{false}; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + auto ctx = std::make_shared<Context>(std::distance(first, last)); + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) { + if (t.hasException()) { + if (!ctx->threw.exchange(true, std::memory_order_relaxed)) { + ctx->p.setException(std::move(t.exception())); + } + } else if (!ctx->threw.load(std::memory_order_relaxed)) { + ctx->result[i] = std::move(t.value()); + } + }); + } + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + auto work = [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }; + future = std::move(future).defer(work); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +template <class InputIterator> +Future<std::vector< + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collectUnsafe(InputIterator first, InputIterator last) { + return collect(first, last).toUnsafeFuture(); +} + +template <class InputIterator> +SemiFuture<std::vector< + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collectSemiFuture(InputIterator first, InputIterator last) { + return collect(first, last); +} + +// collect (variadic) + +template <typename... Fs> +SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect( + Fs&&... fs) { + using Result = std::tuple<typename remove_cvref_t<Fs>::value_type...>; + struct Context { + ~Context() { + if (!threw.load(std::memory_order_relaxed)) { + p.setValue(unwrapTryTuple(std::move(results))); + } + } + Promise<Result> p; + std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...> results; + std::atomic<bool> threw{false}; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutorsVariadic(executors, fs...); + + auto ctx = std::make_shared<Context>(); + futures::detail::foreach( + [&](auto i, auto&& f) { + f.setCallback_([i, ctx](Executor::KeepAlive<>&&, auto&& t) { + if (t.hasException()) { + if (!ctx->threw.exchange(true, std::memory_order_relaxed)) { + ctx->p.setException(std::move(t.exception())); + } + } else if (!ctx->threw.load(std::memory_order_relaxed)) { + std::get<i.value>(ctx->results) = std::move(t); + } + }); + }, + static_cast<Fs&&>(fs)...); + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + auto work = [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }; + future = std::move(future).defer(work); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +template <typename... Fs> +SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> +collectSemiFuture(Fs&&... fs) { + return collect(std::forward<Fs>(fs)...); +} + +template <typename... Fs> +Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collectUnsafe( + Fs&&... fs) { + return collect(std::forward<Fs>(fs)...).toUnsafeFuture(); +} + +template <class Collection> +auto collectSemiFuture(Collection&& c) + -> decltype(collectSemiFuture(c.begin(), c.end())) { + return collectSemiFuture(c.begin(), c.end()); +} + +// collectAny (iterator) + +// TODO(T26439406): Make return SemiFuture +template <class InputIterator> +SemiFuture<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAnySemiFuture(InputIterator first, InputIterator last) { + return collectAny(first, last); +} + +template <class InputIterator> +Future<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAnyUnsafe(InputIterator first, InputIterator last) { + return collectAny(first, last).toUnsafeFuture(); +} + +template <class InputIterator> +SemiFuture<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAny(InputIterator first, InputIterator last) { + using F = typename std::iterator_traits<InputIterator>::value_type; + using T = typename F::value_type; + + struct Context { + Promise<std::pair<size_t, Try<T>>> p; + std::atomic<bool> done{false}; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + auto ctx = std::make_shared<Context>(); + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) { + if (!ctx->done.exchange(true, std::memory_order_relaxed)) { + ctx->p.setValue(std::make_pair(i, std::move(t))); + } + }); + } + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + future = std::move(future).defer( + [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +// collectAnyWithoutException (iterator) + +template <class InputIterator> +SemiFuture<std::pair< + size_t, + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collectAnyWithoutException(InputIterator first, InputIterator last) { + using F = typename std::iterator_traits<InputIterator>::value_type; + using T = typename F::value_type; + + struct Context { + Context(size_t n) : nTotal(n) {} + Promise<std::pair<size_t, T>> p; + std::atomic<bool> done{false}; + std::atomic<size_t> nFulfilled{0}; + size_t nTotal; + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + auto ctx = std::make_shared<Context>(size_t(std::distance(first, last))); + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) { + if (!t.hasException() && + !ctx->done.exchange(true, std::memory_order_relaxed)) { + ctx->p.setValue(std::make_pair(i, std::move(t.value()))); + } else if ( + ctx->nFulfilled.fetch_add(1, std::memory_order_relaxed) + 1 == + ctx->nTotal) { + ctx->p.setException(t.exception()); + } + }); + } + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + future = std::move(future).defer( + [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +// collectN (iterator) + +template <class InputIterator> +SemiFuture<std::vector<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>> +collectN(InputIterator first, InputIterator last, size_t n) { + using F = typename std::iterator_traits<InputIterator>::value_type; + using T = typename F::value_type; + using Result = std::vector<std::pair<size_t, Try<T>>>; + + struct Context { + explicit Context(size_t numFutures, size_t min_) + : v(numFutures), min(min_) {} + + std::vector<Optional<Try<T>>> v; + size_t min; + std::atomic<size_t> completed = {0}; // # input futures completed + std::atomic<size_t> stored = {0}; // # output values stored + Promise<Result> p; + }; + + assert(n > 0); + assert(std::distance(first, last) >= 0); + + if (size_t(std::distance(first, last)) < n) { + return SemiFuture<Result>( + exception_wrapper(std::runtime_error("Not enough futures"))); + } + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + // for each completed Future, increase count and add to vector, until we + // have n completed futures at which point we fulfil our Promise with the + // vector + auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)), n); + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) { + // relaxed because this guards control but does not guard data + auto const c = 1 + ctx->completed.fetch_add(1, std::memory_order_relaxed); + if (c > ctx->min) { + return; + } + ctx->v[i] = std::move(t); + + // release because the stored values in all threads must be visible below + // acquire because no stored value is permitted to be fetched early + auto const s = 1 + ctx->stored.fetch_add(1, std::memory_order_acq_rel); + if (s < ctx->min) { + return; + } + Result result; + result.reserve(ctx->completed.load()); + for (size_t j = 0; j < ctx->v.size(); ++j) { + auto& entry = ctx->v[j]; + if (entry.hasValue()) { + result.emplace_back(j, std::move(entry).value()); + } + } + ctx->p.setTry(Try<Result>(std::move(result))); + }); + } + + auto future = ctx->p.getSemiFuture(); + if (!executors.empty()) { + future = std::move(future).defer( + [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +// reduce (iterator) + +template <class It, class T, class F> +Future<T> reduce(It first, It last, T&& initial, F&& func) { + if (first == last) { + return makeFuture(std::forward<T>(initial)); + } + + typedef typename std::iterator_traits<It>::value_type::value_type ItT; + typedef typename std:: + conditional<is_invocable<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type + Arg; + typedef isTry<Arg> IsTry; + + auto sfunc = std::make_shared<std::decay_t<F>>(std::forward<F>(func)); + + auto f = std::move(*first).thenTry( + [initial = std::forward<T>(initial), sfunc](Try<ItT>&& head) mutable { + return (*sfunc)( + std::move(initial), head.template get<IsTry::value, Arg&&>()); + }); + + for (++first; first != last; ++first) { + f = collectAllSemiFuture(f, *first).toUnsafeFuture().thenValue( + [sfunc](std::tuple<Try<T>, Try<ItT>>&& t) { + return (*sfunc)( + std::move(std::get<0>(t).value()), + // Either return a ItT&& or a Try<ItT>&& depending + // on the type of the argument of func. + std::get<1>(t).template get<IsTry::value, Arg&&>()); + }); + } + + return f; +} + +// window (collection) + +template <class Collection, class F, class ItT, class Result> +std::vector<Future<Result>> window(Collection input, F func, size_t n) { + // Use global QueuedImmediateExecutor singleton to avoid stack overflow. + auto executor = &QueuedImmediateExecutor::instance(); + return window(executor, std::move(input), std::move(func), n); +} + +template <class F> +auto window(size_t times, F func, size_t n) + -> std::vector<invoke_result_t<F, size_t>> { + return window(futures::detail::WindowFakeVector(times), std::move(func), n); +} + +template <class Collection, class F, class ItT, class Result> +std::vector<Future<Result>> +window(Executor::KeepAlive<> executor, Collection input, F func, size_t n) { + struct WindowContext { + WindowContext( + Executor::KeepAlive<> executor_, + Collection&& input_, + F&& func_) + : executor(std::move(executor_)), + input(std::move(input_)), + promises(input.size()), + func(std::move(func_)) {} + std::atomic<size_t> i{0}; + Executor::KeepAlive<> executor; + Collection input; + std::vector<Promise<Result>> promises; + F func; + + static void spawn(std::shared_ptr<WindowContext> ctx) { + size_t i = ctx->i.fetch_add(1, std::memory_order_relaxed); + if (i < ctx->input.size()) { + auto fut = makeSemiFutureWith( + [&] { return ctx->func(std::move(ctx->input[i])); }) + .via(ctx->executor.get()); + + fut.setCallback_([ctx = std::move(ctx), i]( + Executor::KeepAlive<>&&, Try<Result>&& t) mutable { + ctx->promises[i].setTry(std::move(t)); + // Chain another future onto this one + spawn(std::move(ctx)); + }); + } + } + }; + + auto max = std::min(n, input.size()); + + auto ctx = std::make_shared<WindowContext>( + executor.copy(), std::move(input), std::move(func)); + + // Start the first n Futures + for (size_t i = 0; i < max; ++i) { + executor->add([ctx]() mutable { WindowContext::spawn(std::move(ctx)); }); + } + + std::vector<Future<Result>> futures; + futures.reserve(ctx->promises.size()); + for (auto& promise : ctx->promises) { + futures.emplace_back(promise.getSemiFuture().via(executor.copy())); + } + + return futures; +} + +// reduce + +template <class T> +template <class I, class F> +Future<I> Future<T>::reduce(I&& initial, F&& func) && { + return std::move(*this).thenValue( + [minitial = std::forward<I>(initial), + mfunc = std::forward<F>(func)](T&& vals) mutable { + auto ret = std::move(minitial); + for (auto& val : vals) { + ret = mfunc(std::move(ret), std::move(val)); + } + return ret; + }); +} + +// unorderedReduce (iterator) + +template <class It, class T, class F> +SemiFuture<T> unorderedReduceSemiFuture(It first, It last, T initial, F func) { + using ItF = typename std::iterator_traits<It>::value_type; + using ItT = typename ItF::value_type; + using Arg = MaybeTryArg<F, T, ItT>; + + if (first == last) { + return makeFuture(std::move(initial)); + } + + typedef isTry<Arg> IsTry; + + struct Context { + Context(T&& memo, F&& fn, size_t n) + : lock_(), + memo_(makeFuture<T>(std::move(memo))), + func_(std::move(fn)), + numThens_(0), + numFutures_(n), + promise_() {} + + folly::MicroSpinLock lock_; // protects memo_ and numThens_ + Future<T> memo_; + F func_; + size_t numThens_; // how many Futures completed and called .then() + size_t numFutures_; // how many Futures in total + Promise<T> promise_; + }; + + struct Fulfill { + void operator()(Promise<T>&& p, T&& v) const { + p.setValue(std::move(v)); + } + void operator()(Promise<T>&& p, Future<T>&& f) const { + f.setCallback_( + [p = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable { + p.setTry(std::move(t)); + }); + } + }; + + std::vector<futures::detail::DeferredWrapper> executors; + futures::detail::stealDeferredExecutors(executors, first, last); + + auto ctx = std::make_shared<Context>( + std::move(initial), std::move(func), std::distance(first, last)); + for (size_t i = 0; first != last; ++first, ++i) { + first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<ItT>&& t) { + (void)i; + // Futures can be completed in any order, simultaneously. + // To make this non-blocking, we create a new Future chain in + // the order of completion to reduce the values. + // The spinlock just protects chaining a new Future, not actually + // executing the reduce, which should be really fast. + Promise<T> p; + auto f = p.getFuture(); + { + folly::MSLGuard lock(ctx->lock_); + f = std::exchange(ctx->memo_, std::move(f)); + if (++ctx->numThens_ == ctx->numFutures_) { + // After reducing the value of the last Future, fulfill the Promise + ctx->memo_.setCallback_([ctx](Executor::KeepAlive<>&&, Try<T>&& t2) { + ctx->promise_.setValue(std::move(t2)); + }); + } + } + f.setCallback_([ctx, mp = std::move(p), mt = std::move(t)]( + Executor::KeepAlive<>&&, Try<T>&& v) mutable { + if (v.hasValue()) { + try { + Fulfill{}( + std::move(mp), + ctx->func_( + std::move(v.value()), + mt.template get<IsTry::value, Arg&&>())); + } catch (std::exception& e) { + mp.setException(exception_wrapper(std::current_exception(), e)); + } catch (...) { + mp.setException(exception_wrapper(std::current_exception())); + } + } else { + mp.setTry(std::move(v)); + } + }); + }); + } + + auto future = ctx->promise_.getSemiFuture(); + if (!executors.empty()) { + future = std::move(future).defer( + [](Try<typename decltype(future)::value_type>&& t) { + return std::move(t).value(); + }); + const auto& deferredExecutor = futures::detail::getDeferredExecutor(future); + deferredExecutor->setNestedExecutors(std::move(executors)); + } + return future; +} + +template <class It, class T, class F> +Future<T> unorderedReduce(It first, It last, T initial, F func) { + return unorderedReduceSemiFuture( + first, last, std::move(initial), std::move(func)) + .via(&InlineExecutor::instance()); +} + +// within + +template <class T> +Future<T> Future<T>::within(HighResDuration dur, Timekeeper* tk) && { + return std::move(*this).within(dur, FutureTimeout(), tk); +} + +template <class T> +template <class E> +Future<T> Future<T>::within(HighResDuration dur, E e, Timekeeper* tk) && { + if (this->isReady()) { + return std::move(*this); + } + + auto* ePtr = this->getExecutor(); + auto exe = + folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); + return std::move(*this).semi().within(dur, e, tk).via(std::move(exe)); +} + +template <class T> +template <typename E> +SemiFuture<T> +SemiFuture<T>::within(HighResDuration dur, E e, Timekeeper* tk) && { + if (this->isReady()) { + return std::move(*this); + } + + struct Context { + explicit Context(E ex) : exception(std::move(ex)) {} + E exception; + SemiFuture<Unit> thisFuture; + SemiFuture<Unit> afterFuture; + Promise<T> promise; + std::atomic<bool> token{false}; + }; + + std::shared_ptr<Timekeeper> tks; + if (LIKELY(!tk)) { + tks = folly::detail::getTimekeeperSingleton(); + tk = tks.get(); + } + + if (UNLIKELY(!tk)) { + return makeSemiFuture<T>(FutureNoTimekeeper()); + } + + auto ctx = std::make_shared<Context>(std::move(e)); + + ctx->thisFuture = std::move(*this).defer([ctx](Try<T>&& t) { + if (!ctx->token.exchange(true, std::memory_order_relaxed)) { + ctx->promise.setTry(std::move(t)); + ctx->afterFuture.cancel(); + } + }); + + // Have time keeper use a weak ptr to hold ctx, + // so that ctx can be deallocated as soon as the future job finished. + ctx->afterFuture = + tk->after(dur).defer([weakCtx = to_weak_ptr(ctx)](Try<Unit>&& t) mutable { + if (t.hasException() && + t.exception().is_compatible_with<FutureCancellation>()) { + // This got cancelled by thisFuture so we can just return. + return; + } + + auto lockedCtx = weakCtx.lock(); + if (!lockedCtx) { + // ctx already released. "this" completed first, cancel "after" + return; + } + // "after" completed first, cancel "this" + lockedCtx->thisFuture.raise(FutureTimeout()); + if (!lockedCtx->token.exchange(true, std::memory_order_relaxed)) { + if (t.hasException()) { + lockedCtx->promise.setException(std::move(t.exception())); + } else { + lockedCtx->promise.setException(std::move(lockedCtx->exception)); + } + } + }); + + // Properly propagate interrupt values through futures chained after within() + ctx->promise.setInterruptHandler( + [weakCtx = to_weak_ptr(ctx)](const exception_wrapper& ex) { + if (auto lockedCtx = weakCtx.lock()) { + lockedCtx->thisFuture.raise(ex); + } + }); + + // Construct the future to return, create a fresh DeferredExecutor and + // nest the other two inside it, in case they already carry nested executors. + auto fut = ctx->promise.getSemiFuture(); + auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred( + futures::detail::DeferredExecutor::create()); + fut.setExecutor(std::move(newDeferredExecutor)); + + std::vector<folly::futures::detail::DeferredWrapper> nestedExecutors; + nestedExecutors.emplace_back(ctx->thisFuture.stealDeferredExecutor()); + nestedExecutors.emplace_back(ctx->afterFuture.stealDeferredExecutor()); + futures::detail::getDeferredExecutor(fut)->setNestedExecutors( + std::move(nestedExecutors)); + return fut; +} + +// delayed + +template <class T> +Future<T> Future<T>::delayed(HighResDuration dur, Timekeeper* tk) && { + auto e = this->getExecutor(); + return collectAllSemiFuture(*this, futures::sleep(dur, tk)) + .via(e ? e : &InlineExecutor::instance()) + .thenValue([](std::tuple<Try<T>, Try<Unit>>&& tup) { + return makeFuture<T>(std::get<0>(std::move(tup))); + }); +} + +namespace futures { +namespace detail { + +template <class FutureType, typename T = typename FutureType::value_type> +void waitImpl(FutureType& f) { + if (std::is_base_of<Future<T>, FutureType>::value) { + f = std::move(f).via(&InlineExecutor::instance()); + } + // short-circuit if there's nothing to do + if (f.isReady()) { + return; + } + + Promise<T> promise; + auto ret = convertFuture(promise.getSemiFuture(), f); + FutureBatonType baton; + f.setCallback_([&baton, promise = std::move(promise)]( + Executor::KeepAlive<>&&, Try<T>&& t) mutable { + promise.setTry(std::move(t)); + baton.post(); + }); + f = std::move(ret); + baton.wait(); + assert(f.isReady()); +} + +template <class T> +Future<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f) { + // Carry executor from f, inserting an inline executor if it did not have one + auto* exe = f.getExecutor(); + auto newFut = std::move(sf).via(exe ? exe : &InlineExecutor::instance()); + newFut.core_->setInterruptHandlerNoLock(f.core_->getInterruptHandler()); + return newFut; +} + +template <class T> +SemiFuture<T> convertFuture(SemiFuture<T>&& sf, const SemiFuture<T>&) { + return std::move(sf); +} + +template <class FutureType, typename T = typename FutureType::value_type> +void waitImpl(FutureType& f, HighResDuration dur) { + if (std::is_base_of<Future<T>, FutureType>::value) { + f = std::move(f).via(&InlineExecutor::instance()); + } + // short-circuit if there's nothing to do + if (f.isReady()) { + return; + } + + Promise<T> promise; + auto ret = convertFuture(promise.getSemiFuture(), f); + auto baton = std::make_shared<FutureBatonType>(); + f.setCallback_([baton, promise = std::move(promise)]( + Executor::KeepAlive<>&&, Try<T>&& t) mutable { + promise.setTry(std::move(t)); + baton->post(); + }); + f = std::move(ret); + if (baton->try_wait_for(dur)) { + assert(f.isReady()); + } +} + +template <class T> +void waitViaImpl(Future<T>& f, DrivableExecutor* e) { + // Set callback so to ensure that the via executor has something on it + // so that once the preceding future triggers this callback, drive will + // always have a callback to satisfy it + if (f.isReady()) { + return; + } + f = std::move(f).via(e).thenTry([](Try<T>&& t) { return std::move(t); }); + while (!f.isReady()) { + e->drive(); + } + assert(f.isReady()); + f = std::move(f).via(&InlineExecutor::instance()); +} + +template <class T, typename Rep, typename Period> +void waitViaImpl( + Future<T>& f, + TimedDrivableExecutor* e, + const std::chrono::duration<Rep, Period>& timeout) { + // Set callback so to ensure that the via executor has something on it + // so that once the preceding future triggers this callback, drive will + // always have a callback to satisfy it + if (f.isReady()) { + return; + } + // Chain operations, ensuring that the executor is kept alive for the duration + f = std::move(f).via(e).thenValue( + [keepAlive = getKeepAliveToken(e)](T&& t) { return std::move(t); }); + auto now = std::chrono::steady_clock::now(); + auto deadline = now + timeout; + while (!f.isReady() && (now < deadline)) { + e->try_drive_until(deadline); + now = std::chrono::steady_clock::now(); + } + assert(f.isReady() || (now >= deadline)); + if (f.isReady()) { + f = std::move(f).via(&InlineExecutor::instance()); + } +} + +} // namespace detail +} // namespace futures + +template <class T> +SemiFuture<T>& SemiFuture<T>::wait() & { + if (auto deferredExecutor = this->getDeferredExecutor()) { + // Make sure that the last callback in the future chain will be run on the + // WaitExecutor. + Promise<T> promise; + auto ret = promise.getSemiFuture(); + setCallback_( + [p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable { + p.setTry(std::move(r)); + }); + auto waitExecutor = futures::detail::WaitExecutor::create(); + deferredExecutor->setExecutor(waitExecutor.copy()); + while (!ret.isReady()) { + waitExecutor->drive(); + } + waitExecutor->detach(); + this->detach(); + *this = std::move(ret); + } else { + futures::detail::waitImpl(*this); + } + return *this; +} + +template <class T> +SemiFuture<T>&& SemiFuture<T>::wait() && { + return std::move(wait()); +} + +template <class T> +SemiFuture<T>& SemiFuture<T>::wait(HighResDuration dur) & { + if (auto deferredExecutor = this->getDeferredExecutor()) { + // Make sure that the last callback in the future chain will be run on the + // WaitExecutor. + Promise<T> promise; + auto ret = promise.getSemiFuture(); + setCallback_( + [p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable { + p.setTry(std::move(r)); + }); + auto waitExecutor = futures::detail::WaitExecutor::create(); + auto deadline = futures::detail::WaitExecutor::Clock::now() + dur; + deferredExecutor->setExecutor(waitExecutor.copy()); + while (!ret.isReady()) { + if (!waitExecutor->driveUntil(deadline)) { + break; + } + } + waitExecutor->detach(); + this->detach(); + *this = std::move(ret); + } else { + futures::detail::waitImpl(*this, dur); + } + return *this; +} + +template <class T> +bool SemiFuture<T>::wait(HighResDuration dur) && { + auto future = std::move(*this); + future.wait(dur); + return future.isReady(); +} + +template <class T> +T SemiFuture<T>::get() && { + return std::move(*this).getTry().value(); +} + +template <class T> +T SemiFuture<T>::get(HighResDuration dur) && { + return std::move(*this).getTry(dur).value(); +} + +template <class T> +Try<T> SemiFuture<T>::getTry() && { + wait(); + auto future = folly::Future<T>(this->core_); + this->core_ = nullptr; + return std::move(std::move(future).getTry()); +} + +template <class T> +Try<T> SemiFuture<T>::getTry(HighResDuration dur) && { + wait(dur); + auto future = folly::Future<T>(this->core_); + this->core_ = nullptr; + + if (!future.isReady()) { + throw_exception<FutureTimeout>(); + } + return std::move(std::move(future).getTry()); +} + +template <class T> +Future<T>& Future<T>::wait() & { + futures::detail::waitImpl(*this); + return *this; +} + +template <class T> +Future<T>&& Future<T>::wait() && { + futures::detail::waitImpl(*this); + return std::move(*this); +} + +template <class T> +Future<T>& Future<T>::wait(HighResDuration dur) & { + futures::detail::waitImpl(*this, dur); + return *this; +} + +template <class T> +Future<T>&& Future<T>::wait(HighResDuration dur) && { + futures::detail::waitImpl(*this, dur); + return std::move(*this); +} + +template <class T> +Future<T>& Future<T>::waitVia(DrivableExecutor* e) & { + futures::detail::waitViaImpl(*this, e); + return *this; +} + +template <class T> +Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && { + futures::detail::waitViaImpl(*this, e); + return std::move(*this); +} + +template <class T> +Future<T>& Future<T>::waitVia(TimedDrivableExecutor* e, HighResDuration dur) & { + futures::detail::waitViaImpl(*this, e, dur); + return *this; +} + +template <class T> +Future<T>&& Future<T>::waitVia( + TimedDrivableExecutor* e, + HighResDuration dur) && { + futures::detail::waitViaImpl(*this, e, dur); + return std::move(*this); +} + +template <class T> +T Future<T>::get() && { + wait(); + return copy(std::move(*this)).value(); +} + +template <class T> +T Future<T>::get(HighResDuration dur) && { + wait(dur); + auto future = copy(std::move(*this)); + if (!future.isReady()) { + throw_exception<FutureTimeout>(); + } + return std::move(future).value(); +} + +template <class T> +Try<T>& Future<T>::getTry() { + return result(); +} + +template <class T> +T Future<T>::getVia(DrivableExecutor* e) { + return std::move(waitVia(e).value()); +} + +template <class T> +T Future<T>::getVia(TimedDrivableExecutor* e, HighResDuration dur) { + waitVia(e, dur); + if (!this->isReady()) { + throw_exception<FutureTimeout>(); + } + return std::move(value()); +} + +template <class T> +Try<T>& Future<T>::getTryVia(DrivableExecutor* e) { + return waitVia(e).getTry(); +} + +template <class T> +Try<T>& Future<T>::getTryVia(TimedDrivableExecutor* e, HighResDuration dur) { + waitVia(e, dur); + if (!this->isReady()) { + throw_exception<FutureTimeout>(); + } + return result(); +} + +namespace futures { +namespace detail { +template <class T> +struct TryEquals { + static bool equals(const Try<T>& t1, const Try<T>& t2) { + return t1.value() == t2.value(); + } +}; +} // namespace detail +} // namespace futures + +template <class T> +Future<bool> Future<T>::willEqual(Future<T>& f) { + return collectAllSemiFuture(*this, f).toUnsafeFuture().thenValue( + [](const std::tuple<Try<T>, Try<T>>& t) { + if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) { + return futures::detail::TryEquals<T>::equals( + std::get<0>(t), std::get<1>(t)); + } else { + return false; + } + }); +} + +template <class T> +template <class F> +Future<T> Future<T>::filter(F&& predicate) && { + return std::move(*this).thenValue([p = std::forward<F>(predicate)](T val) { + T const& valConstRef = val; + if (!p(valConstRef)) { + throw_exception<FuturePredicateDoesNotObtain>(); + } + return val; + }); +} + +template <class F> +auto when(bool p, F&& thunk) + -> decltype(std::declval<invoke_result_t<F>>().unit()) { + return p ? std::forward<F>(thunk)().unit() : makeFuture(); +} + +template <class P, class F> +typename std:: + enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type + whileDo(P&& predicate, F&& thunk) { + if (predicate()) { + auto future = thunk(); + return std::move(future).deferExValue( + [predicate = std::forward<P>(predicate), + thunk = std::forward<F>(thunk)](auto&& ex, auto&&) mutable { + return whileDo(std::forward<P>(predicate), std::forward<F>(thunk)) + .via(std::move(ex)); + }); + } + return makeSemiFuture(); +} + +template <class P, class F> +typename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type +whileDo(P&& predicate, F&& thunk) { + if (predicate()) { + auto future = thunk(); + return std::move(future).thenValue( + [predicate = std::forward<P>(predicate), + thunk = std::forward<F>(thunk)](auto&&) mutable { + return whileDo(std::forward<P>(predicate), std::forward<F>(thunk)); + }); + } + return makeFuture(); +} + +template <class F> +auto times(const int n, F&& thunk) { + return folly::whileDo( + [n, count = std::make_unique<std::atomic<int>>(0)]() mutable { + return count->fetch_add(1, std::memory_order_relaxed) < n; + }, + std::forward<F>(thunk)); +} + +namespace futures { +template <class It, class F, class ItT, class Tag, class Result> +std::vector<Future<Result>> mapValue(It first, It last, F func) { + std::vector<Future<Result>> results; + results.reserve(std::distance(first, last)); + for (auto it = first; it != last; it++) { + results.push_back(std::move(*it).thenValue(func)); + } + return results; +} + +template <class It, class F, class ItT, class Tag, class Result> +std::vector<Future<Result>> mapTry(It first, It last, F func, int) { + std::vector<Future<Result>> results; + results.reserve(std::distance(first, last)); + for (auto it = first; it != last; it++) { + results.push_back(std::move(*it).thenTry(func)); + } + return results; +} + +template <class It, class F, class ItT, class Tag, class Result> +std::vector<Future<Result>> +mapValue(Executor& exec, It first, It last, F func) { + std::vector<Future<Result>> results; + results.reserve(std::distance(first, last)); + for (auto it = first; it != last; it++) { + results.push_back(std::move(*it).via(&exec).thenValue(func)); + } + return results; +} + +template <class It, class F, class ItT, class Tag, class Result> +std::vector<Future<Result>> +mapTry(Executor& exec, It first, It last, F func, int) { + std::vector<Future<Result>> results; + results.reserve(std::distance(first, last)); + for (auto it = first; it != last; it++) { + results.push_back(std::move(*it).via(&exec).thenTry(func)); + } + return results; +} + +template <typename F, class Ensure> +auto ensure(F&& f, Ensure&& ensure) { + return makeSemiFuture() + .deferValue([f = std::forward<F>(f)](auto) mutable { return f(); }) + .defer([ensure = std::forward<Ensure>(ensure)](auto resultTry) mutable { + ensure(); + return std::move(resultTry).value(); + }); +} + +template <class T> +void detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut) { + std::move(fut).via(exec).detach(); +} + +template <class T> +void detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut) { + detachOn(folly::getGlobalCPUExecutor(), std::move(fut)); +} + +} // namespace futures + +template <class Clock> +SemiFuture<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) { + auto now = Clock::now(); + + if (when <= now) { + return makeSemiFuture(); + } + + return after(std::chrono::duration_cast<HighResDuration>(when - now)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Future-pre.h b/ios/Pods/Flipper-Folly/folly/futures/Future-pre.h new file mode 100644 index 000000000..851d9284c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Future-pre.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// included by Future.h, do not include directly. + +namespace folly { + +template <class> +class Promise; + +template <class T> +class SemiFuture; + +template <typename T> +struct isSemiFuture : std::false_type { + using Inner = lift_unit_t<T>; +}; + +template <typename T> +struct isSemiFuture<SemiFuture<T>> : std::true_type { + typedef T Inner; +}; + +template <typename T> +struct isFuture : std::false_type { + using Inner = lift_unit_t<T>; +}; + +template <typename T> +struct isFuture<Future<T>> : std::true_type { + typedef T Inner; +}; + +template <typename T> +struct isFutureOrSemiFuture : std::false_type { + using Inner = lift_unit_t<T>; + using Return = Inner; +}; + +template <typename T> +struct isFutureOrSemiFuture<Try<T>> : std::false_type { + using Inner = lift_unit_t<T>; + using Return = Inner; +}; + +template <typename T> +struct isFutureOrSemiFuture<Future<T>> : std::true_type { + typedef T Inner; + using Return = Future<Inner>; +}; + +template <typename T> +struct isFutureOrSemiFuture<Future<Try<T>>> : std::true_type { + typedef T Inner; + using Return = Future<Inner>; +}; + +template <typename T> +struct isFutureOrSemiFuture<SemiFuture<T>> : std::true_type { + typedef T Inner; + using Return = SemiFuture<Inner>; +}; + +template <typename T> +struct isFutureOrSemiFuture<SemiFuture<Try<T>>> : std::true_type { + typedef T Inner; + using Return = SemiFuture<Inner>; +}; + +namespace futures { +namespace detail { + +template <class> +class Core; + +template <typename...> +struct ArgType; + +template <typename Arg, typename... Args> +struct ArgType<Arg, Args...> { + typedef Arg FirstArg; + typedef ArgType<Args...> Tail; +}; + +template <> +struct ArgType<> { + typedef void FirstArg; +}; + +template <bool isTry_, typename F, typename... Args> +struct argResult { + using Function = F; + using ArgList = ArgType<Args...>; + using Result = invoke_result_t<F, Args...>; + using ArgsSize = index_constant<sizeof...(Args)>; + static constexpr bool isTry() { + return isTry_; + } +}; + +template <typename T, typename F> +struct callableResult { + typedef typename std::conditional< + is_invocable_v<F>, + detail::argResult<false, F>, + typename std::conditional< + is_invocable_v<F, T&&>, + detail::argResult<false, F, T&&>, + detail::argResult<true, F, Try<T>&&>>::type>::type Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; +}; + +template <typename T, typename F> +struct executorCallableResult { + typedef typename std::conditional< + is_invocable_v<F, Executor::KeepAlive<>&&>, + detail::argResult<false, F, Executor::KeepAlive<>&&>, + typename std::conditional< + is_invocable_v<F, Executor::KeepAlive<>&&, T&&>, + detail::argResult<false, F, Executor::KeepAlive<>&&, T&&>, + detail::argResult<true, F, Executor::KeepAlive<>&&, Try<T>&&>>:: + type>::type Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; +}; + +template < + typename T, + typename F, + typename = std::enable_if_t<is_invocable_v<F, Try<T>&&>>> +struct tryCallableResult { + typedef detail::argResult<true, F, Try<T>&&> Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef typename ReturnsFuture::Inner value_type; + typedef Future<value_type> Return; +}; + +template < + typename T, + typename F, + typename = std::enable_if_t<is_invocable_v<F, Executor*, Try<T>&&>>> +struct tryExecutorCallableResult { + typedef detail::argResult<true, F, Executor::KeepAlive<>&&, Try<T>&&> Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef typename ReturnsFuture::Inner value_type; + typedef Future<value_type> Return; +}; + +template <typename T, typename F> +struct valueCallableResult { + typedef detail::argResult<false, F, T&&> Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef typename ReturnsFuture::Inner value_type; + typedef typename Arg::ArgList::FirstArg FirstArg; + typedef Future<value_type> Return; +}; + +template <typename T, typename F> +struct valueExecutorCallableResult { + typedef detail::argResult<false, F, Executor::KeepAlive<>&&, T&&> Arg; + typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; + typedef typename ReturnsFuture::Inner value_type; + typedef typename Arg::ArgList::Tail::FirstArg ValueArg; + typedef Future<value_type> Return; +}; + +template <typename L> +struct Extract : Extract<decltype(&L::operator())> {}; + +template <typename Class, typename R, typename... Args> +struct Extract<R (Class::*)(Args...) const> { + typedef isFutureOrSemiFuture<R> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType<Args...>::FirstArg FirstArg; +}; + +template <typename Class, typename R, typename... Args> +struct Extract<R (Class::*)(Args...)> { + typedef isFutureOrSemiFuture<R> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType<Args...>::FirstArg FirstArg; +}; + +template <typename R, typename... Args> +struct Extract<R (*)(Args...)> { + typedef isFutureOrSemiFuture<R> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType<Args...>::FirstArg FirstArg; +}; + +template <typename R, typename... Args> +struct Extract<R (&)(Args...)> { + typedef isFutureOrSemiFuture<R> ReturnsFuture; + typedef Future<typename ReturnsFuture::Inner> Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType<Args...>::FirstArg FirstArg; +}; + +class DeferredExecutor; + +template <class T, class F> +auto makeExecutorLambda( + F&& func, + typename std::enable_if<is_invocable_v<F>, int>::type = 0) { + return + [func = std::forward<F>(func)](Executor::KeepAlive<>&&, auto&&) mutable { + return std::forward<F>(func)(); + }; +} + +template <class T, class F> +auto makeExecutorLambda( + F&& func, + typename std::enable_if<!is_invocable_v<F>, int>::type = 0) { + using R = futures::detail::callableResult<T, F&&>; + return [func = std::forward<F>(func)]( + Executor::KeepAlive<>&&, + typename R::Arg::ArgList::FirstArg&& param) mutable { + return std::forward<F>(func)(std::forward<decltype(param)>(param)); + }; +} + +} // namespace detail +} // namespace futures + +class Timekeeper; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Future.cpp b/ios/Pods/Flipper-Folly/folly/futures/Future.cpp new file mode 100644 index 000000000..d48ee3b5e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Future.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/futures/Future.h> +#include <folly/Likely.h> +#include <folly/futures/ThreadWheelTimekeeper.h> + +namespace folly { +namespace futures { + +SemiFuture<Unit> sleep(HighResDuration dur, Timekeeper* tk) { + std::shared_ptr<Timekeeper> tks; + if (LIKELY(!tk)) { + tks = folly::detail::getTimekeeperSingleton(); + tk = tks.get(); + } + + if (UNLIKELY(!tk)) { + return makeSemiFuture<Unit>(FutureNoTimekeeper()); + } + + return tk->after(dur); +} + +Future<Unit> sleepUnsafe(HighResDuration dur, Timekeeper* tk) { + return sleep(dur, tk).toUnsafeFuture(); +} + +#if FOLLY_FUTURE_USING_FIBER + +namespace { +template <typename Ptr> +class FutureWaiter : public fibers::Baton::Waiter { + public: + FutureWaiter(Promise<Unit> promise, Ptr baton) + : promise_(std::move(promise)), baton_(std::move(baton)) { + baton_->setWaiter(*this); + } + + void post() override { + promise_.setValue(); + delete this; + } + + private: + Promise<Unit> promise_; + Ptr baton_; +}; +} // namespace + +SemiFuture<Unit> wait(std::unique_ptr<fibers::Baton> baton) { + Promise<Unit> promise; + auto sf = promise.getSemiFuture(); + new FutureWaiter<std::unique_ptr<fibers::Baton>>( + std::move(promise), std::move(baton)); + return sf; +} +SemiFuture<Unit> wait(std::shared_ptr<fibers::Baton> baton) { + Promise<Unit> promise; + auto sf = promise.getSemiFuture(); + new FutureWaiter<std::shared_ptr<fibers::Baton>>( + std::move(promise), std::move(baton)); + return sf; +} + +#endif + +} // namespace futures +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Future.h b/ios/Pods/Flipper-Folly/folly/futures/Future.h new file mode 100644 index 000000000..58cf1e2db --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Future.h @@ -0,0 +1,2693 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <exception> +#include <functional> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> + +#include <folly/Optional.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/Try.h> +#include <folly/Unit.h> +#include <folly/Utility.h> +#include <folly/executors/DrivableExecutor.h> +#include <folly/executors/TimedDrivableExecutor.h> +#include <folly/functional/Invoke.h> +#include <folly/futures/Portability.h> +#include <folly/futures/Promise.h> +#include <folly/futures/detail/Types.h> +#include <folly/lang/Exception.h> + +#if FOLLY_HAS_COROUTINES +#include <folly/experimental/coro/Traits.h> +#include <experimental/coroutine> +#endif + +// boring predeclarations and details +#include <folly/futures/Future-pre.h> + +namespace folly { + +class FOLLY_EXPORT FutureException : public std::logic_error { + public: + using std::logic_error::logic_error; +}; + +class FOLLY_EXPORT FutureInvalid : public FutureException { + public: + FutureInvalid() : FutureException("Future invalid") {} +}; + +/// At most one continuation may be attached to any given Future. +/// +/// If a continuation is attached to a future to which another continuation has +/// already been attached, then an instance of FutureAlreadyContinued will be +/// thrown instead. +class FOLLY_EXPORT FutureAlreadyContinued : public FutureException { + public: + FutureAlreadyContinued() : FutureException("Future already continued") {} +}; + +class FOLLY_EXPORT FutureNotReady : public FutureException { + public: + FutureNotReady() : FutureException("Future not ready") {} +}; + +class FOLLY_EXPORT FutureCancellation : public FutureException { + public: + FutureCancellation() : FutureException("Future was cancelled") {} +}; + +class FOLLY_EXPORT FutureTimeout : public FutureException { + public: + FutureTimeout() : FutureException("Timed out") {} +}; + +class FOLLY_EXPORT FuturePredicateDoesNotObtain : public FutureException { + public: + FuturePredicateDoesNotObtain() + : FutureException("Predicate does not obtain") {} +}; + +class FOLLY_EXPORT FutureNoTimekeeper : public FutureException { + public: + FutureNoTimekeeper() : FutureException("No timekeeper available") {} +}; + +class FOLLY_EXPORT FutureNoExecutor : public FutureException { + public: + FutureNoExecutor() : FutureException("No executor provided to via") {} +}; + +template <class T> +class Future; + +template <class T> +class SemiFuture; + +template <class T> +class FutureSplitter; + +#if FOLLY_FUTURE_USING_FIBER + +namespace fibers { +class Baton; +} + +#endif + +namespace futures { +namespace detail { +template <class T> +class FutureBase { + protected: + using Core = futures::detail::Core<T>; + using CoreCallback = typename Core::Callback; + + public: + typedef T value_type; + + /// Construct from a value (perfect forwarding) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + template < + class T2 = T, + typename = typename std::enable_if< + !isFuture<typename std::decay<T2>::type>::value && + !isSemiFuture<typename std::decay<T2>::type>::value && + std::is_constructible<Try<T>, T2>::value>::type> + /* implicit */ FutureBase(T2&& val); + + /// Construct a (logical) FutureBase-of-void. + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + template <class T2 = T> + /* implicit */ FutureBase( + typename std::enable_if<std::is_same<Unit, T2>::value>::type*); + + template < + class... Args, + typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>:: + type = 0> + explicit FutureBase(in_place_t, Args&&... args) + : core_(Core::make(in_place, std::forward<Args>(args)...)) {} + + FutureBase(FutureBase<T> const&) = delete; + FutureBase(SemiFuture<T>&&) noexcept; + FutureBase(Future<T>&&) noexcept; + + // not copyable + FutureBase(Future<T> const&) = delete; + FutureBase(SemiFuture<T> const&) = delete; + + ~FutureBase(); + + /// true if this has a shared state; + /// false if this has been either moved-out or created without a shared state. + bool valid() const noexcept { + return core_ != nullptr; + } + + /// Returns a reference to the result value if it is ready, with a reference + /// category and const-qualification like those of the future. + /// + /// Does not `wait()`; see `get()` for that. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - `isReady() == true` (else throws FutureNotReady) + /// + /// Postconditions: + /// + /// - If an exception has been captured (i.e., if `hasException() == true`), + /// throws that exception. + /// - This call does not mutate the future's value. + /// - However calling code may mutate that value (including moving it out by + /// move-constructing or move-assigning another value from it), for + /// example, via the `&` or the `&&` overloads or via casts. + T& value() &; + T const& value() const&; + T&& value() &&; + T const&& value() const&&; + + /// Returns a reference to the result's Try if it is ready, with a reference + /// category and const-qualification like those of the future. + /// + /// Does not `wait()`; see `get()` for that. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - `isReady() == true` (else throws FutureNotReady) + /// + /// Postconditions: + /// + /// - This call does not mutate the future's result. + /// - However calling code may mutate that result (including moving it out by + /// move-constructing or move-assigning another result from it), for + /// example, via the `&` or the `&&` overloads or via casts. + Try<T>& result() &; + Try<T> const& result() const&; + Try<T>&& result() &&; + Try<T> const&& result() const&&; + + /// True when the result (or exception) is ready; see value(), result(), etc. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + bool isReady() const; + + /// True if the result is a value (not an exception) on a future for which + /// isReady returns true. + /// + /// Equivalent to result().hasValue() + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - `isReady() == true` (else throws FutureNotReady) + bool hasValue() const; + + /// True if the result is an exception (not a value) on a future for which + /// isReady returns true. + /// + /// Equivalent to result().hasException() + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - `isReady() == true` (else throws FutureNotReady) + bool hasException() const; + + /// Returns either an Optional holding the result or an empty Optional + /// depending on whether or not (respectively) the promise has been + /// fulfilled (i.e., `isReady() == true`). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (note however that this moves-out the result when + /// it returns a populated `Try<T>`, which effects any subsequent use of + /// that result, e.g., `poll()`, `result()`, `value()`, `get()`, etc.) + Optional<Try<T>> poll(); + + /// This is not the method you're looking for. + /// + /// This needs to be public because it's used by make* and when*, and it's + /// not worth listing all those and their fancy template signatures as + /// friends. But it's not for public consumption. + void setCallback_( + CoreCallback&& func, + InlineContinuation = InlineContinuation::forbid); + + /// Provides a threadsafe back-channel so the consumer's thread can send an + /// interrupt-object to the producer's thread. + /// + /// If the promise-holder registers an interrupt-handler and consumer thread + /// raises an interrupt early enough (details below), the promise-holder + /// will typically halt its work, fulfilling the future with an exception + /// or some special non-exception value. + /// + /// However this interrupt request is voluntary, asynchronous, & advisory: + /// + /// - Voluntary: the producer will see the interrupt only if the producer uses + /// a `Promise` object and registers an interrupt-handler; + /// see `Promise::setInterruptHandler()`. + /// - Asynchronous: the producer will see the interrupt only if `raise()` is + /// called before (or possibly shortly after) the producer is done producing + /// its result, which is asynchronous with respect to the call to `raise()`. + /// - Advisory: the producer's interrupt-handler can do whatever it wants, + /// including ignore the interrupt or perform some action other than halting + /// its producer-work. + /// + /// Guidelines: + /// + /// - It is ideal if the promise-holder can both halt its work and fulfill the + /// promise early, typically with the same exception that was delivered to + /// the promise-holder in the form of an interrupt. + /// - If the promise-holder does not do this, and if it holds the promise + /// alive for a long time, then the whole continuation chain will not be + /// invoked and the whole future chain will be kept alive for that long time + /// as well. + /// - It is also ideal if the promise-holder can invalidate the promise. + /// - The promise-holder must also track whether it has set a result in the + /// interrupt handler so that it does not attempt to do so outside the + /// interrupt handler, and must track whether it has set a result in its + /// normal flow so that it does not attempt to do so in the interrupt + /// handler, since setting a result twice is an error. Because the interrupt + /// handler can be invoked in some other thread, this tracking may have to + /// be done with some form of concurrency control. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - has no visible effect if `raise()` was previously called on `this` or + /// any other Future/SemiFuture that uses the same shared state as `this`. + /// - has no visible effect if the producer never (either in the past or in + /// the future) registers an interrupt-handler. + /// - has no visible effect if the producer fulfills its promise (sets the + /// result) before (or possibly also shortly after) receiving the interrupt. + /// - otherwise the promise-holder's interrupt-handler is called, passing the + /// exception (within an `exception_wrapper`). + /// + /// The specific thread used to invoke the producer's interrupt-handler (if + /// it is called at all) depends on timing: + /// + /// - if the interrupt-handler is registered prior to `raise()` (or possibly + /// concurrently within the call to `raise()`), the interrupt-handler will + /// be executed using this current thread within the call to `raise()`. + /// - if the interrupt-handler is registered after `raise()` (and possibly + /// concurrently within the call to `raise()`), the interrupt-handler will + /// be executed using the producer's thread within the call to + /// `Promise::setInterruptHandler()`. + /// + /// Synchronizes between `raise()` (in the consumer's thread) + /// and `Promise::setInterruptHandler()` (in the producer's thread). + void raise(exception_wrapper interrupt); + + /// Raises the specified exception-interrupt. + /// See `raise(exception_wrapper)` for details. + template <class E> + void raise(E&& exception) { + raise(make_exception_wrapper<typename std::remove_reference<E>::type>( + std::forward<E>(exception))); + } + + /// Raises a FutureCancellation interrupt. + /// See `raise(exception_wrapper)` for details. + void cancel() { + raise(FutureCancellation()); + } + + protected: + friend class Promise<T>; + template <class> + friend class SemiFuture; + template <class> + friend class Future; + + // Throws FutureInvalid if there is no shared state object; else returns it + // by ref. + // + // Implementation methods should usually use this instead of `this->core_`. + // The latter should be used only when you need the possibly-null pointer. + Core& getCore() { + return getCoreImpl(*this); + } + Core const& getCore() const { + return getCoreImpl(*this); + } + + template <typename Self> + static decltype(auto) getCoreImpl(Self& self) { + if (!self.core_) { + throw_exception<FutureInvalid>(); + } + return *self.core_; + } + + Try<T>& getCoreTryChecked() { + return getCoreTryChecked(*this); + } + Try<T> const& getCoreTryChecked() const { + return getCoreTryChecked(*this); + } + + template <typename Self> + static decltype(auto) getCoreTryChecked(Self& self) { + auto& core = self.getCore(); + if (!core.hasResult()) { + throw_exception<FutureNotReady>(); + } + return core.getTry(); + } + + // shared core state object + // usually you should use `getCore()` instead of directly accessing `core_`. + Core* core_; + + explicit FutureBase(Core* obj) : core_(obj) {} + + explicit FutureBase(futures::detail::EmptyConstruct) noexcept; + + void detach(); + + void throwIfInvalid() const; + void throwIfContinued() const; + + void assign(FutureBase<T>&& other) noexcept; + + Executor* getExecutor() const { + return getCore().getExecutor(); + } + + DeferredExecutor* getDeferredExecutor() const { + return getCore().getDeferredExecutor(); + } + + // Sets the Executor within the Core state object of `this`. + // Must be called either before attaching a callback or after the callback + // has already been invoked, but not concurrently with anything which might + // trigger invocation of the callback. + void setExecutor(futures::detail::KeepAliveOrDeferred x) { + getCore().setExecutor(std::move(x)); + } + + // Variant: returns a value + // e.g. f.thenTry([](Try<T> t){ return t.value(); }); + template <typename F, typename R> + typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type + thenImplementation(F&& func, R, InlineContinuation); + + // Variant: returns a Future + // e.g. f.thenTry([](Try<T> t){ return makeFuture<T>(t); }); + template <typename F, typename R> + typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type + thenImplementation(F&& func, R, InlineContinuation); +}; +template <class T> +Future<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f); + +class DeferredExecutor; + +template <typename T> +DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future); + +template <typename T> +futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future); +} // namespace detail + +template <class T> +void detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut); + +template <class T> +void detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut); +} // namespace futures + +/// The interface (along with Future) for the consumer-side of a +/// producer/consumer pair. +/// +/// Future vs. SemiFuture: +/// +/// - The consumer-side should generally start with a SemiFuture, not a Future. +/// - Example, when a library creates and returns a future, it should usually +/// return a `SemiFuture`, not a Future. +/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be +/// specified by the library's caller (using `.via()`). +/// - A SemiFuture is converted to a Future using `.via()`. +/// - Use `makePromiseContract()` when creating both a Promise and an associated +/// SemiFuture/Future. +/// +/// When practical, prefer SemiFuture/Future's nonblocking style/pattern: +/// +/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the +/// continuations are deferred until the result is available. +/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc. +/// - the two styles cannot be mixed within the same future; use one or the +/// other. +/// +/// SemiFuture/Future also provide a back-channel so an interrupt can +/// be sent from consumer to producer; see SemiFuture/Future's `raise()` +/// and Promise's `setInterruptHandler()`. +/// +/// The consumer-side SemiFuture/Future objects should generally be accessed +/// via a single thread. That thread is referred to as the 'consumer thread.' +template <class T> +class SemiFuture : private futures::detail::FutureBase<T> { + private: + using Base = futures::detail::FutureBase<T>; + using DeferredExecutor = futures::detail::DeferredExecutor; + using TimePoint = std::chrono::system_clock::time_point; + + public: + ~SemiFuture(); + + /// Creates/returns an invalid SemiFuture, that is, one with no shared state. + /// + /// Postcondition: + /// + /// - `RESULT.valid() == false` + static SemiFuture<T> makeEmpty(); + + /// Type of the value that the producer, when successful, produces. + using typename Base::value_type; + + /// Construct a SemiFuture from a value (perfect forwarding) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + /// - `hasException() == false` + /// - `value()`, `get()`, `result()` will return the forwarded `T` + template < + class T2 = T, + typename = typename std::enable_if< + !isFuture<typename std::decay<T2>::type>::value && + !isSemiFuture<typename std::decay<T2>::type>::value && + std::is_constructible<Try<T>, T2>::value>::type> + /* implicit */ SemiFuture(T2&& val) : Base(std::forward<T2>(val)) {} + + /// Construct a (logical) SemiFuture-of-void. + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + template <class T2 = T> + /* implicit */ SemiFuture( + typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr) + : Base(p) {} + + /// Construct a SemiFuture from a `T` constructed from `args` + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + /// - `hasException() == false` + /// - `value()`, `get()`, `result()` will return the newly constructed `T` + template < + class... Args, + typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>:: + type = 0> + explicit SemiFuture(in_place_t, Args&&... args) + : Base(in_place, std::forward<Args>(args)...) {} + + SemiFuture(SemiFuture<T> const&) = delete; + // movable + SemiFuture(SemiFuture<T>&&) noexcept; + // safe move-constructabilty from Future + /* implicit */ SemiFuture(Future<T>&&) noexcept; + + using Base::cancel; + using Base::hasException; + using Base::hasValue; + using Base::isReady; + using Base::poll; + using Base::raise; + using Base::result; + using Base::setCallback_; + using Base::valid; + using Base::value; + + SemiFuture& operator=(SemiFuture const&) = delete; + SemiFuture& operator=(SemiFuture&&) noexcept; + SemiFuture& operator=(Future<T>&&) noexcept; + + /// Blocks until the promise is fulfilled, either by value (which is returned) + /// or exception (which is thrown). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - must not have a continuation, e.g., via `.thenValue()` or similar + /// + /// Postconditions: + /// + /// - `valid() == false` + T get() &&; + + /// Blocks until the semifuture is fulfilled, or until `dur` elapses. Returns + /// the value (moved-out), or throws the exception (which might be a + /// FutureTimeout exception). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + T get(HighResDuration dur) &&; + + /// Blocks until the future is fulfilled. Returns the Try of the result + /// (moved-out). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + Try<T> getTry() &&; + + /// Blocks until the future is fulfilled, or until `dur` elapses. + /// Returns the Try of the result (moved-out), or throws FutureTimeout + /// exception. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + Try<T> getTry(HighResDuration dur) &&; + + /// Blocks the caller's thread until this Future `isReady()`, i.e., until the + /// asynchronous producer has stored a result or exception. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `&RESULT == this` + SemiFuture<T>& wait() &; + + /// Blocks the caller's thread until this Future `isReady()`, i.e., until the + /// asynchronous producer has stored a result or exception. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (but the calling code can trivially move-out `*this` + /// by assigning or constructing the result into a distinct object). + /// - `&RESULT == this` + /// - `isReady() == true` + SemiFuture<T>&& wait() &&; + + /// Blocks until the future is fulfilled, or `dur` elapses. + /// Returns true if the future was fulfilled. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + bool wait(HighResDuration dur) &&; + + /// Returns a Future which will call back on the other side of executor. + Future<T> via(Executor::KeepAlive<> executor) &&; + Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&; + + /// Defer work to run on the consumer of the future. + /// Function must take a Try as a parameter. + /// This work will be run either on an executor that the caller sets on the + /// SemiFuture, or inline with the call to .get(). + /// + /// NB: This is a custom method because boost-blocking executors is a + /// special-case for work deferral in folly. With more general boost-blocking + /// support all executors would boost block and we would simply use some form + /// of driveable executor here. + /// + /// All forms of defer will run the continuation inline with the execution of + /// the previous callback in the chain if the callback attached to the + /// previous future that triggers execution of func runs on the same executor + /// that func would be executed on. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <typename F> + SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type> + defer(F&& func) &&; + + /// Defer work to run on the consumer of the future. + /// Function must take a const Executor::KeepAlive<>& and a Try as parameters. + /// + /// As for defer(F&& func) except as the first parameter to func a KeepAlive + /// representing the executor running the work will be provided. + template <typename F> + SemiFuture< + typename futures::detail::tryExecutorCallableResult<T, F>::value_type> + deferExTry(F&& func) &&; + + /// Defer work to run on the consumer of the future. + /// Function must take a Try as a parameter. + /// + /// As for defer(F&& func) but supporting function references. + template <typename R, typename... Args> + auto defer(R (&func)(Args...)) && { + return std::move(*this).defer(&func); + } + + /// Defer for functions taking a T rather than a Try<T>. + /// + /// All forms of defer will run the continuation inline with the execution of + /// the previous callback in the chain if the callback attached to the + /// previous future that triggers execution of func runs on the same executor + /// that func would be executed on. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <typename F> + SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type> + deferValue(F&& func) &&; + + /// Defer for functions taking a T rather than a Try<T>. + /// Function must take a const Executor::KeepAlive<>& and a T as parameters. + /// + /// As for deferValue(F&& func) except as the first parameter to func a + /// KeepAlive representing the executor running the work will be provided. + template <typename F> + SemiFuture< + typename futures::detail::valueExecutorCallableResult<T, F>::value_type> + deferExValue(F&& func) &&; + + /// Defer work to run on the consumer of the future. + /// Function must take a T as a parameter. + /// + /// As for deferValue(F&& func) but supporting function references. + template <typename R, typename... Args> + auto deferValue(R (&func)(Args...)) && { + return std::move(*this).deferValue(&func); + } + + /// Set an error continuation for this SemiFuture where the continuation can + /// be called with a known exception type and returns a `T`, `Future<T>`, or + /// `SemiFuture<T>`. + /// + /// Example: + /// + /// ``` + /// makeSemiFuture() + /// .defer([] { + /// throw std::runtime_error("oh no!"); + /// return 42; + /// }) + /// .deferError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) { + /// LOG(INFO) << "std::runtime_error: " << e.what(); + /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) + /// }); + /// ``` + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <class ExceptionType, class F> + SemiFuture<T> deferError(tag_t<ExceptionType>, F&& func) &&; + + /// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function + /// references. + template <class ExceptionType, class R, class... Args> + SemiFuture<T> deferError(tag_t<ExceptionType> tag, R (&func)(Args...)) && { + return std::move(*this).deferError(tag, &func); + } + + /// As for deferError(tag_t<ExceptionType>, F&& func) but makes the exception + /// explicit as a template argument rather than using a tag type. + template <class ExceptionType, class F> + SemiFuture<T> deferError(F&& func) && { + return std::move(*this).deferError( + tag_t<ExceptionType>{}, std::forward<F>(func)); + } + + /// Set an error continuation for this SemiFuture where the continuation can + /// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or + /// `SemiFuture<T>`. + /// + /// Example: + /// + /// makeSemiFuture() + /// .defer([] { + /// throw std::runtime_error("oh no!"); + /// return 42; + /// }) + /// .deferError([] (exception_wrapper&& e) { + /// LOG(INFO) << e.what(); + /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <class F> + SemiFuture<T> deferError(F&& func) &&; + + /// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function + /// references. + template <class R, class... Args> + SemiFuture<T> deferError(R (&func)(Args...)) && { + return std::move(*this).deferError(&func); + } + + /// Convenience method for ignoring the value and creating a Future<Unit>. + /// Exceptions still propagate. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + SemiFuture<Unit> unit() &&; + + /// If this SemiFuture completes within duration dur from now, propagate its + /// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout + /// exception. + /// + /// The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + SemiFuture<T> within(HighResDuration dur, Timekeeper* tk = nullptr) && { + return std::move(*this).within(dur, FutureTimeout(), tk); + } + + /// If this SemiFuture completes within duration dur from now, propagate its + /// value. Otherwise satisfy the returned SemiFuture with exception e. + /// + /// The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class E> + SemiFuture<T> within(HighResDuration dur, E e, Timekeeper* tk = nullptr) &&; + + /// Delay the completion of this SemiFuture for at least this duration from + /// now. The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + SemiFuture<T> delayed(HighResDuration dur, Timekeeper* tk = nullptr) &&; + + /// Returns a future that completes inline, as if the future had no executor. + /// Intended for porting legacy code without behavioral change, and for rare + /// cases where this is really the intended behavior. + /// Future is unsafe in the sense that the executor it completes on is + /// non-deterministic in the standard case. + /// For new code, or to update code that temporarily uses this, please + /// use via and pass a meaningful executor. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + Future<T> toUnsafeFuture() &&; + +#if FOLLY_HAS_COROUTINES + + // Customise the co_viaIfAsync() operator so that SemiFuture<T> can be + // directly awaited within a folly::coro::Task coroutine. + friend Future<T> co_viaIfAsync( + folly::Executor::KeepAlive<> executor, + SemiFuture<T>&& future) noexcept { + return std::move(future).via(std::move(executor)); + } + +#endif + + private: + friend class Promise<T>; + template <class> + friend class futures::detail::FutureBase; + template <class> + friend class SemiFuture; + template <class> + friend class Future; + friend futures::detail::DeferredWrapper + futures::detail::stealDeferredExecutor<T>(SemiFuture<T>&); + friend DeferredExecutor* futures::detail::getDeferredExecutor<T>( + SemiFuture<T>&); + + using Base::setExecutor; + using Base::throwIfInvalid; + using typename Base::Core; + + template <class T2> + friend SemiFuture<T2> makeSemiFuture(Try<T2>); + + explicit SemiFuture(Core* obj) : Base(obj) {} + + explicit SemiFuture(futures::detail::EmptyConstruct) noexcept + : Base(futures::detail::EmptyConstruct{}) {} + + // Throws FutureInvalid if !this->core_ + futures::detail::DeferredWrapper stealDeferredExecutor(); + + /// Blocks until the future is fulfilled, or `dur` elapses. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `&RESULT == this` + /// - `isReady()` will be indeterminate - may or may not be true + SemiFuture<T>& wait(HighResDuration dur) &; + + static void releaseDeferredExecutor(Core* core); +}; + +template <class T> +std::pair<Promise<T>, SemiFuture<T>> makePromiseContract() { + auto p = Promise<T>(); + auto f = p.getSemiFuture(); + return std::make_pair(std::move(p), std::move(f)); +} + +/// The interface (along with SemiFuture) for the consumer-side of a +/// producer/consumer pair. +/// +/// Future vs. SemiFuture: +/// +/// - The consumer-side should generally start with a SemiFuture, not a Future. +/// - Example, when a library creates and returns a future, it should usually +/// return a `SemiFuture`, not a Future. +/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be +/// specified by the library's caller (using `.via()`). +/// - A SemiFuture is converted to a Future using `.via()`. +/// - Use `makePromiseContract()` when creating both a Promise and an associated +/// SemiFuture/Future. +/// +/// When practical, prefer SemiFuture/Future's nonblocking style/pattern: +/// +/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the +/// continuations are deferred until the result is available. +/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc. +/// - the two styles cannot be mixed within the same future; use one or the +/// other. +/// +/// SemiFuture/Future also provide a back-channel so an interrupt can +/// be sent from consumer to producer; see SemiFuture/Future's `raise()` +/// and Promise's `setInterruptHandler()`. +/// +/// The consumer-side SemiFuture/Future objects should generally be accessed +/// via a single thread. That thread is referred to as the 'consumer thread.' +template <class T> +class Future : private futures::detail::FutureBase<T> { + private: + using Base = futures::detail::FutureBase<T>; + + public: + /// Type of the value that the producer, when successful, produces. + using typename Base::value_type; + + /// Construct a Future from a value (perfect forwarding) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + /// - `value()`, `get()`, `result()` will return the forwarded `T` + template < + class T2 = T, + typename = typename std::enable_if< + !isFuture<typename std::decay<T2>::type>::value && + !isSemiFuture<typename std::decay<T2>::type>::value && + std::is_constructible<Try<T>, T2>::value>::type> + /* implicit */ Future(T2&& val) : Base(std::forward<T2>(val)) {} + + /// Construct a (logical) Future-of-void. + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + template <class T2 = T> + /* implicit */ Future( + typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr) + : Base(p) {} + + /// Construct a Future from a `T` constructed from `args` + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `isReady() == true` + /// - `hasValue() == true` + /// - `hasException() == false` + /// - `value()`, `get()`, `result()` will return the newly constructed `T` + template < + class... Args, + typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>:: + type = 0> + explicit Future(in_place_t, Args&&... args) + : Base(in_place, std::forward<Args>(args)...) {} + + Future(Future<T> const&) = delete; + // movable + Future(Future<T>&&) noexcept; + + // converting move + template < + class T2, + typename std::enable_if< + !std::is_same<T, typename std::decay<T2>::type>::value && + std::is_constructible<T, T2&&>::value && + std::is_convertible<T2&&, T>::value, + int>::type = 0> + /* implicit */ Future(Future<T2>&& other) + : Future(std::move(other).thenValue( + [](T2&& v) { return T(std::move(v)); })) {} + + template < + class T2, + typename std::enable_if< + !std::is_same<T, typename std::decay<T2>::type>::value && + std::is_constructible<T, T2&&>::value && + !std::is_convertible<T2&&, T>::value, + int>::type = 0> + explicit Future(Future<T2>&& other) + : Future(std::move(other).thenValue( + [](T2&& v) { return T(std::move(v)); })) {} + + template < + class T2, + typename std::enable_if< + !std::is_same<T, typename std::decay<T2>::type>::value && + std::is_constructible<T, T2&&>::value, + int>::type = 0> + Future& operator=(Future<T2>&& other) { + return operator=( + std::move(other).thenValue([](T2&& v) { return T(std::move(v)); })); + } + + using Base::cancel; + using Base::hasException; + using Base::hasValue; + using Base::isReady; + using Base::poll; + using Base::raise; + using Base::result; + using Base::setCallback_; + using Base::valid; + using Base::value; + + /// Creates/returns an invalid Future, that is, one with no shared state. + /// + /// Postcondition: + /// + /// - `RESULT.valid() == false` + static Future<T> makeEmpty(); + + // not copyable + Future& operator=(Future const&) = delete; + + // movable + Future& operator=(Future&&) noexcept; + + /// Call e->drive() repeatedly until the future is fulfilled. + /// + /// Examples of DrivableExecutor include EventBase and ManualExecutor. + /// + /// Returns the fulfilled value (moved-out) or throws the fulfilled exception. + T getVia(DrivableExecutor* e); + + /// Call e->drive() repeatedly until the future is fulfilled, or `dur` + /// elapses. + /// + /// Returns the fulfilled value (moved-out), throws the fulfilled exception, + /// or on timeout throws FutureTimeout. + T getVia(TimedDrivableExecutor* e, HighResDuration dur); + + /// Call e->drive() repeatedly until the future is fulfilled. Examples + /// of DrivableExecutor include EventBase and ManualExecutor. Returns a + /// reference to the Try of the value. + Try<T>& getTryVia(DrivableExecutor* e); + + /// getTryVia but will wait only until `dur` elapses. Returns the + /// Try of the value (moved-out) or may throw a FutureTimeout exception. + Try<T>& getTryVia(TimedDrivableExecutor* e, HighResDuration dur); + + /// Unwraps the case of a Future<Future<T>> instance, and returns a simple + /// Future<T> instance. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F = T> + typename std:: + enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type + unwrap() &&; + + /// Returns a Future which will call back on the other side of executor. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + Future<T> via(Executor::KeepAlive<> executor) &&; + Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&; + + /// Returns a Future which will call back on the other side of executor. + /// + /// When practical, use the rvalue-qualified overload instead - it's faster. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `RESULT.valid() == true` + /// - when `this` gets fulfilled, it automatically fulfills RESULT + Future<T> via(Executor::KeepAlive<> executor) &; + Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &; + + /// When this Future has completed, execute func which is a function that + /// can be called with either `T&&` or `Try<T>&&`. + /// + /// Func shall return either another Future or a value. + /// + /// thenInline will run the continuation inline with the execution of the + /// previous callback in the chain if the callback attached to the previous + /// future that triggers execution of func runs on the same executor that func + /// would be executed on. + /// + /// A Future for the return type of func is returned. + /// + /// Versions of these functions with Inline in the name will run the + /// continuation inline if the executor the previous task completes on matches + /// the executor the next is to be enqueued on to. + /// + /// Future<string> f2 = f1.thenTry([](Try<T>&&) { return string("foo"); }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <typename F> + Future<typename futures::detail::tryCallableResult<T, F>::value_type> then( + F&& func) && { + return std::move(*this).thenTry(std::forward<F>(func)); + } + template <typename F> + Future<typename futures::detail::tryCallableResult<T, F>::value_type> + thenInline(F&& func) && { + return std::move(*this).thenTryInline(std::forward<F>(func)); + } + + /// Variant where func is an member function + /// + /// struct Worker { R doWork(Try<T>); } + /// + /// Worker *w; + /// Future<R> f2 = f1.thenTry(&Worker::doWork, w); + /// + /// This is just sugar for + /// + /// f1.thenTry(std::bind(&Worker::doWork, w)); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <typename R, typename Caller, typename... Args> + Future<typename isFuture<R>::Inner> then( + R (Caller::*func)(Args...), + Caller* instance) &&; + + /// Execute the callback via the given Executor. The executor doesn't stick. + /// + /// Contrast + /// + /// f.via(x).then(b).then(c) + /// + /// with + /// + /// f.then(x, b).then(c) + /// + /// In the former both b and c execute via x. In the latter, only b executes + /// via x, and c executes via the same executor (if any) that f had. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class Arg> + auto then(Executor::KeepAlive<> x, Arg&& arg) && = delete; + + /// When this Future has completed, execute func which is a function that + /// can be called with `Try<T>&&` (often a lambda with parameter type + /// `auto&&` or `auto`). + /// + /// Func shall return either another Future or a value. + /// + /// Versions of these functions with Inline in the name will run the + /// continuation inline with the execution of the previous callback in the + /// chain if the callback attached to the previous future that triggers + /// execution of func runs on the same executor that func would be executed + /// on. + /// + /// A Future for the return type of func is returned. + /// + /// Future<string> f2 = std::move(f1).thenTry([](auto&& t) { + /// ... + /// return string("foo"); + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <typename F> + Future<typename futures::detail::tryCallableResult<T, F>::value_type> thenTry( + F&& func) &&; + + template <typename F> + Future<typename futures::detail::tryCallableResult<T, F>::value_type> + thenTryInline(F&& func) &&; + + template <typename F> + Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type> + thenExTry(F&& func) &&; + + template <typename F> + Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type> + thenExTryInline(F&& func) &&; + + template <typename R, typename... Args> + auto thenTry(R (&func)(Args...)) && { + return std::move(*this).thenTry(&func); + } + + template <typename R, typename... Args> + auto thenTryInline(R (&func)(Args...)) && { + return std::move(*this).thenTryInline(&func); + } + + /// When this Future has completed, execute func which is a function that + /// can be called with `T&&` (often a lambda with parameter type + /// `auto&&` or `auto`). + /// + /// Func shall return either another Future or a value. + /// + /// Versions of these functions with Inline in the name will run the + /// continuation inline with the execution of the previous callback in the + /// chain if the callback attached to the previous future that triggers + /// execution of func runs on the same executor that func would be executed + /// on. + /// + /// A Future for the return type of func is returned. + /// + /// Future<string> f2 = f1.thenValue([](auto&& v) { + /// ... + /// return string("foo"); + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <typename F> + Future<typename futures::detail::valueCallableResult<T, F>::value_type> + thenValue(F&& func) &&; + + template <typename F> + Future<typename futures::detail::valueCallableResult<T, F>::value_type> + thenValueInline(F&& func) &&; + + template <typename F> + Future< + typename futures::detail::valueExecutorCallableResult<T, F>::value_type> + thenExValue(F&& func) &&; + + template <typename F> + Future< + typename futures::detail::valueExecutorCallableResult<T, F>::value_type> + thenExValueInline(F&& func) &&; + + template <typename R, typename... Args> + auto thenValue(R (&func)(Args...)) && { + return std::move(*this).thenValue(&func); + } + + template <typename R, typename... Args> + auto thenValueInline(R (&func)(Args...)) && { + return std::move(*this).thenValueInline(&func); + } + + /// Set an error continuation for this Future where the continuation can + /// be called with a known exception type and returns a `T`, `Future<T>`, or + /// `SemiFuture<T>`. + /// + /// Example: + /// + /// makeFuture() + /// .thenTry([] { + /// throw std::runtime_error("oh no!"); + /// return 42; + /// }) + /// .thenError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) { + /// LOG(INFO) << "std::runtime_error: " << e.what(); + /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <class ExceptionType, class F> + typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, + Future<T>>::type + thenError(tag_t<ExceptionType>, F&& func) &&; + + template <class ExceptionType, class F> + typename std::enable_if< + !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value, + Future<T>>::type + thenError(tag_t<ExceptionType>, F&& func) &&; + + template <class ExceptionType, class R, class... Args> + Future<T> thenError(tag_t<ExceptionType> tag, R (&func)(Args...)) && { + return std::move(*this).thenError(tag, &func); + } + + template <class ExceptionType, class F> + Future<T> thenError(F&& func) && { + return std::move(*this).thenError( + tag_t<ExceptionType>{}, std::forward<F>(func)); + } + + /// Set an error continuation for this Future where the continuation can + /// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or + /// `SemiFuture<T>`. + /// + /// Example: + /// + /// makeFuture() + /// .thenTry([] { + /// throw std::runtime_error("oh no!"); + /// return 42; + /// }) + /// .thenError([] (exception_wrapper&& e) { + /// LOG(INFO) << e.what(); + /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1) + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + template <class F> + typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value, + Future<T>>::type + thenError(F&& func) &&; + + template <class F> + typename std::enable_if< + !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value, + Future<T>>::type + thenError(F&& func) &&; + + template <class R, class... Args> + Future<T> thenError(R (&func)(Args...)) && { + return std::move(*this).thenError(&func); + } + + /// Convenience method for ignoring the value and creating a Future<Unit>. + /// Exceptions still propagate. + /// This function is identical to .unit(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + Future<Unit> then() &&; + + /// Convenience method for ignoring the value and creating a Future<Unit>. + /// Exceptions still propagate. + /// This function is identical to parameterless .then(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + Future<Unit> unit() && { + return std::move(*this).then(); + } + + /// Set an error continuation for this Future. The continuation should take an + /// argument of the type that you want to catch, and should return a value of + /// the same type as this Future, or a Future of that type (see overload + /// below). + /// + /// Example: + /// + /// makeFuture() + /// .thenValue([] { + /// throw std::runtime_error("oh no!"); + /// return 42; + /// }) + /// .thenError<std::runtime_error>([] (std::runtime_error& e) { + /// LOG(INFO) << "std::runtime_error: " << e.what(); + /// return -1; // or makeFuture<int>(-1) + /// }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + [[deprecated( + "onError loses the attached executor and is weakly typed. Please move to thenError instead.")]] + typename std::enable_if< + !is_invocable_v<F, exception_wrapper> && + !futures::detail::Extract<F>::ReturnsFuture::value, + Future<T>>::type + onError(F&& func) && = delete; + + /// Overload of onError where the error continuation returns a Future<T> + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + [[deprecated( + "onError loses the attached executor and is weakly typed. Please move to thenError instead.")]] + typename std::enable_if< + !is_invocable_v<F, exception_wrapper> && + futures::detail::Extract<F>::ReturnsFuture::value, + Future<T>>::type + onError(F&& func) && = delete; + + /// Overload of onError that takes exception_wrapper and returns Future<T> + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + [[deprecated( + "onError loses the attached executor and is weakly typed. Please move to thenError instead.")]] + typename std::enable_if< + is_invocable_v<F, exception_wrapper> && + futures::detail::Extract<F>::ReturnsFuture::value, + Future<T>>::type + onError(F&& func) && = delete; + + /// Overload of onError that takes exception_wrapper and returns T + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + [[deprecated( + "onError loses the attached executor and is weakly typed. Please move to thenError instead.")]] + typename std::enable_if< + is_invocable_v<F, exception_wrapper> && + !futures::detail::Extract<F>::ReturnsFuture::value, + Future<T>>::type + onError(F&& func) && = delete; + + template <class R, class... Args> + Future<T> onError(R (&func)(Args...)) && = delete; + + // clang-format off + template <class F> + [[deprecated( + "onError loses the attached executor and is weakly typed. Please move to thenError instead.")]] + Future<T> onError(F&& func) & = delete; + + /// func is like std::function<void()> and is executed unconditionally, and + /// the value/exception is passed through to the resulting Future. + /// func shouldn't throw, but if it does it will be captured and propagated, + /// and discard any value/exception that this Future has obtained. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + Future<T> ensure(F&& func) &&; + // clang-format on + + /// Like thenError, but for timeouts. example: + /// + /// Future<int> f = makeFuture<int>(42) + /// .delayed(long_time) + /// .onTimeout(short_time, + /// [] { return -1; }); + /// + /// or perhaps + /// + /// Future<int> f = makeFuture<int>(42) + /// .delayed(long_time) + /// .onTimeout(short_time, + /// [] { return makeFuture<int>(some_exception); }); + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + Future<T> onTimeout(HighResDuration, F&& func, Timekeeper* = nullptr) &&; + + /// If this Future completes within duration dur from now, propagate its + /// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout + /// exception. + /// + /// The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + Future<T> within(HighResDuration dur, Timekeeper* tk = nullptr) &&; + + /// If this SemiFuture completes within duration dur from now, propagate its + /// value. Otherwise satisfy the returned SemiFuture with exception e. + /// + /// The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class E> + Future<T> + within(HighResDuration dur, E exception, Timekeeper* tk = nullptr) &&; + + /// Delay the completion of this Future for at least this duration from + /// now. The optional Timekeeper is as with futures::sleep(). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + /// - `RESULT.valid() == true` + Future<T> delayed(HighResDuration, Timekeeper* = nullptr) &&; + + /// Blocks until the future is fulfilled. Returns the value (moved-out), or + /// throws the exception. The future must not already have a continuation. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + T get() &&; + + /// Blocks until the future is fulfilled, or until `dur` elapses. Returns the + /// value (moved-out), or throws the exception (which might be a FutureTimeout + /// exception). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == false` + T get(HighResDuration dur) &&; + + /// A reference to the Try of the value + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// - `isReady() == true` (else throws FutureNotReady) + Try<T>& getTry(); + + /// Blocks until this Future is complete. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` + /// - `&RESULT == this` + /// - `isReady() == true` + Future<T>& wait() &; + + /// Blocks until this Future is complete. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (but the calling code can trivially move-out `*this` + /// by assigning or constructing the result into a distinct object). + /// - `&RESULT == this` + /// - `isReady() == true` + Future<T>&& wait() &&; + + /// Blocks until this Future is complete, or `dur` elapses. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (so you may call `wait(...)` repeatedly) + /// - `&RESULT == this` + /// - `isReady()` will be indeterminate - may or may not be true + Future<T>& wait(HighResDuration dur) &; + + /// Blocks until this Future is complete or until `dur` passes. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (but the calling code can trivially move-out `*this` + /// by assigning or constructing the result into a distinct object). + /// - `&RESULT == this` + /// - `isReady()` will be indeterminate - may or may not be true + Future<T>&& wait(HighResDuration dur) &&; + + /// Call e->drive() repeatedly until the future is fulfilled. Examples + /// of DrivableExecutor include EventBase and ManualExecutor. Returns a + /// reference to this Future so that you can chain calls if desired. + /// value (moved-out), or throws the exception. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (does not move-out `*this`) + /// - `&RESULT == this` + Future<T>& waitVia(DrivableExecutor* e) &; + + /// Overload of waitVia() for rvalue Futures + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (but the calling code can trivially move-out `*this` + /// by assigning or constructing the result into a distinct object). + /// - `&RESULT == this` + Future<T>&& waitVia(DrivableExecutor* e) &&; + + /// As waitVia but may return early after dur passes. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (does not move-out `*this`) + /// - `&RESULT == this` + Future<T>& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &; + + /// Overload of waitVia() for rvalue Futures + /// As waitVia but may return early after dur passes. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - `valid() == true` (but the calling code can trivially move-out `*this` + /// by assigning or constructing the result into a distinct object). + /// - `&RESULT == this` + Future<T>&& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &&; + + /// If the value in this Future is equal to the given Future, when they have + /// both completed, the value of the resulting Future<bool> will be true. It + /// will be false otherwise (including when one or both Futures have an + /// exception) + Future<bool> willEqual(Future<T>&); + + /// predicate behaves like std::function<bool(T const&)> + /// If the predicate does not obtain with the value, the result + /// is a folly::FuturePredicateDoesNotObtain exception + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class F> + Future<T> filter(F&& predicate) &&; + + /// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example + /// the result of collect or collectAll + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws FutureInvalid) + /// + /// Postconditions: + /// + /// - Calling code should act as if `valid() == false`, + /// i.e., as if `*this` was moved into RESULT. + /// - `RESULT.valid() == true` + template <class I, class F> + Future<I> reduce(I&& initial, F&& func) &&; + + /// Moves-out `*this`, creating/returning a corresponding SemiFuture. + /// Result will behave like `*this` except result won't have an Executor. + /// + /// Postconditions: + /// + /// - `RESULT.valid() ==` the original value of `this->valid()` + /// - RESULT will not have an Executor regardless of whether `*this` had one + SemiFuture<T> semi() && { + return SemiFuture<T>{std::move(*this)}; + } + +#if FOLLY_HAS_COROUTINES + + // Overload needed to customise behaviour of awaiting a Future<T> + // inside a folly::coro::Task coroutine. + friend Future<T> co_viaIfAsync( + folly::Executor::KeepAlive<> executor, + Future<T>&& future) noexcept { + return std::move(future).via(std::move(executor)); + } + +#endif + + protected: + friend class Promise<T>; + template <class> + friend class futures::detail::FutureBase; + template <class> + friend class Future; + template <class> + friend class SemiFuture; + template <class> + friend class FutureSplitter; + + using Base::setExecutor; + using Base::throwIfContinued; + using Base::throwIfInvalid; + using typename Base::Core; + + explicit Future(Core* obj) : Base(obj) {} + + explicit Future(futures::detail::EmptyConstruct) noexcept + : Base(futures::detail::EmptyConstruct{}) {} + + template <class T2> + friend Future<T2> makeFuture(Try<T2>); + + template <class FT> + friend Future<FT> futures::detail::convertFuture( + SemiFuture<FT>&& sf, + const Future<FT>& f); + + using Base::detach; + template <class T2> + friend void futures::detachOn( + folly::Executor::KeepAlive<> exec, + folly::SemiFuture<T2>&& fut); +}; + +/// A Timekeeper handles the details of keeping time and fulfilling delay +/// promises. The returned Future<Unit> will either complete after the +/// elapsed time, or in the event of some kind of exceptional error may hold +/// an exception. These Futures respond to cancellation. If you use a lot of +/// Delays and many of them ultimately are unneeded (as would be the case for +/// Delays that are used to trigger timeouts of async operations), then you +/// can and should cancel them to reclaim resources. +/// +/// Users will typically get one of these via Future::sleep(HighResDuration dur) +/// or use them implicitly behind the scenes by passing a timeout to some Future +/// operation. +/// +/// Although we don't formally alias Delay = Future<Unit>, +/// that's an appropriate term for it. People will probably also call these +/// Timeouts, and that's ok I guess, but that term is so overloaded I thought +/// it made sense to introduce a cleaner term. +/// +/// Remember that HighResDuration is a std::chrono duration (millisecond +/// resolution at the time of writing). When writing code that uses specific +/// durations, prefer using the explicit std::chrono type, e.g. +/// std::chrono::milliseconds over HighResDuration. This makes the code more +/// legible and means you won't be unpleasantly surprised if we redefine +/// HighResDuration to microseconds, or something. +/// +/// timekeeper.after(std::chrono::duration_cast<HighResDuration>(someNanoseconds)) +class Timekeeper { + public: + virtual ~Timekeeper() = default; + + /// Returns a future that will complete after the given duration with the + /// elapsed time. Exceptional errors can happen but they must be + /// exceptional. Use the steady (monotonic) clock. + /// + /// The consumer thread may cancel this Future to reclaim resources. + virtual SemiFuture<Unit> after(HighResDuration dur) = 0; + + /// Unsafe version of after that returns an inline Future. + /// Any work added to this future will run inline on the Timekeeper's thread. + /// This can potentially cause problems with timing. + /// + /// Please migrate to use after + a call to via with a valid, non-inline + /// executor. + Future<Unit> afterUnsafe(HighResDuration dur) { + return after(dur).toUnsafeFuture(); + } + + /// Returns a future that will complete at the requested time. + /// + /// You may cancel this SemiFuture to reclaim resources. + /// + /// NB This is sugar for `after(when - now)`, so while you are welcome to + /// use a std::chrono::system_clock::time_point it will not track changes to + /// the system clock but rather execute that many milliseconds in the future + /// according to the steady clock. + template <class Clock> + SemiFuture<Unit> at(std::chrono::time_point<Clock> when); + + /// Unsafe version of at that returns an inline Future. + /// Any work added to this future will run inline on the Timekeeper's thread. + /// This can potentially cause problems with timing. + /// + /// Please migrate to use at + a call to via with a valid, non-inline + /// executor. + template <class Clock> + Future<Unit> atUnsafe(std::chrono::time_point<Clock> when) { + return at(when).toUnsafeFuture(); + } +}; + +template <class T> +std::pair<Promise<T>, Future<T>> makePromiseContract(Executor::KeepAlive<> e) { + auto p = Promise<T>(); + auto f = p.getSemiFuture().via(std::move(e)); + return std::make_pair(std::move(p), std::move(f)); +} + +template <class F> +auto makeAsyncTask(folly::Executor::KeepAlive<> ka, F&& func) { + return + [func = std::forward<F>(func), ka = std::move(ka)](auto&& param) mutable { + return via( + ka, + [func = std::move(func), + param = std::forward<decltype(param)>(param)]() mutable { + return func(std::forward<decltype(param)>(param)); + }); + }; +} + +/// This namespace is for utility functions that would usually be static +/// members of Future, except they don't make sense there because they don't +/// depend on the template type (rather, on the type of their arguments in +/// some cases). This is the least-bad naming scheme we could think of. Some +/// of the functions herein have really-likely-to-collide names, like "map" +/// and "sleep". +namespace futures { +/// Returns a Future that will complete after the specified duration. The +/// HighResDuration typedef of a `std::chrono` duration type indicates the +/// resolution you can expect to be meaningful (milliseconds at the time of +/// writing). Normally you wouldn't need to specify a Timekeeper, we will +/// use the global futures timekeeper (we run a thread whose job it is to +/// keep time for futures timeouts) but we provide the option for power +/// users. +/// +/// The Timekeeper thread will be lazily created the first time it is +/// needed. If your program never uses any timeouts or other time-based +/// Futures you will pay no Timekeeper thread overhead. +SemiFuture<Unit> sleep(HighResDuration, Timekeeper* = nullptr); +[[deprecated( + "futures::sleep now returns a SemiFuture<Unit>. " + "sleepUnsafe is deprecated. " + "Please call futures::sleep and apply an executor with .via")]] Future<Unit> +sleepUnsafe(HighResDuration, Timekeeper* = nullptr); + +/** + * Set func as the callback for each input Future and return a vector of + * Futures containing the results in the input order. + */ +template < + class It, + class F, + class ItT = typename std::iterator_traits<It>::value_type, + class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>, + class Result = typename decltype( + std::declval<ItT>().thenValue(std::declval<F>()))::value_type> +std::vector<Future<Result>> mapValue(It first, It last, F func); + +/** + * Set func as the callback for each input Future and return a vector of + * Futures containing the results in the input order. + */ +template < + class It, + class F, + class ItT = typename std::iterator_traits<It>::value_type, + class Tag = + std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>, + class Result = typename decltype( + std::declval<ItT>().thenTry(std::declval<F>()))::value_type> +std::vector<Future<Result>> mapTry(It first, It last, F func, int = 0); + +/** + * Set func as the callback for each input Future and return a vector of + * Futures containing the results in the input order and completing on + * exec. + */ +template < + class It, + class F, + class ItT = typename std::iterator_traits<It>::value_type, + class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>, + class Result = + typename decltype(std::move(std::declval<ItT>()) + .via(std::declval<Executor*>()) + .thenValue(std::declval<F>()))::value_type> +std::vector<Future<Result>> mapValue(Executor& exec, It first, It last, F func); + +/** + * Set func as the callback for each input Future and return a vector of + * Futures containing the results in the input order and completing on + * exec. + */ +template < + class It, + class F, + class ItT = typename std::iterator_traits<It>::value_type, + class Tag = + std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>, + class Result = + typename decltype(std::move(std::declval<ItT>()) + .via(std::declval<Executor*>()) + .thenTry(std::declval<F>()))::value_type> +std::vector<Future<Result>> +mapTry(Executor& exec, It first, It last, F func, int = 0); + +// Sugar for the most common case +template <class Collection, class F> +auto mapValue(Collection&& c, F&& func) + -> decltype(mapValue(c.begin(), c.end(), func)) { + return mapValue(c.begin(), c.end(), std::forward<F>(func)); +} + +template <class Collection, class F> +auto mapTry(Collection&& c, F&& func) + -> decltype(mapTry(c.begin(), c.end(), func)) { + return mapTry(c.begin(), c.end(), std::forward<F>(func)); +} + +// Sugar for the most common case +template <class Collection, class F> +auto mapValue(Executor& exec, Collection&& c, F&& func) + -> decltype(mapValue(exec, c.begin(), c.end(), func)) { + return mapValue(exec, c.begin(), c.end(), std::forward<F>(func)); +} + +template <class Collection, class F> +auto mapTry(Executor& exec, Collection&& c, F&& func) + -> decltype(mapTry(exec, c.begin(), c.end(), func)) { + return mapTry(exec, c.begin(), c.end(), std::forward<F>(func)); +} + +/// Carry out the computation contained in the given future if +/// the predicate holds. +/// +/// thunk behaves like std::function<Future<T2>(void)> or +/// std::function<SemiFuture<T2>(void)> +template <class F> +auto when(bool p, F&& thunk) + -> decltype(std::declval<invoke_result_t<F>>().unit()); + +#if FOLLY_FUTURE_USING_FIBER + +SemiFuture<Unit> wait(std::unique_ptr<fibers::Baton> baton); +SemiFuture<Unit> wait(std::shared_ptr<fibers::Baton> baton); + +#endif + +/** + * Returns a lazy SemiFuture constructed by f, which also ensures that ensure is + * called before completion. + * f doesn't get called until the SemiFuture is activated (e.g. through a .get() + * or .via() call). If f gets called, ensure is guaranteed to be called as well. + */ +template <typename F, class Ensure> +auto ensure(F&& f, Ensure&& ensure); + +} // namespace futures + +/** + Make a completed SemiFuture by moving in a value. e.g. + + string foo = "foo"; + auto f = makeSemiFuture(std::move(foo)); + + or + + auto f = makeSemiFuture<string>("foo"); +*/ +template <class T> +SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t); + +/** Make a completed void SemiFuture. */ +SemiFuture<Unit> makeSemiFuture(); + +/** + Make a SemiFuture by executing a function. + + If the function returns a value of type T, makeSemiFutureWith + returns a completed SemiFuture<T>, capturing the value returned + by the function. + + If the function returns a SemiFuture<T> already, makeSemiFutureWith + returns just that. + + Either way, if the function throws, a failed Future is + returned that captures the exception. +*/ + +// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T> +template <class F> +typename std::enable_if< + isFutureOrSemiFuture<invoke_result_t<F>>::value, + SemiFuture<typename invoke_result_t<F>::value_type>>::type +makeSemiFutureWith(F&& func); + +// makeSemiFutureWith(T()) -> SemiFuture<T> +// makeSemiFutureWith(void()) -> SemiFuture<Unit> +template <class F> +typename std::enable_if< + !(isFutureOrSemiFuture<invoke_result_t<F>>::value), + SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type +makeSemiFutureWith(F&& func); + +/// Make a failed Future from an exception_ptr. +/// Because the Future's type cannot be inferred you have to specify it, e.g. +/// +/// auto f = makeSemiFuture<string>(std::current_exception()); +template <class T> +[[deprecated("use makeSemiFuture(exception_wrapper)")]] SemiFuture<T> +makeSemiFuture(std::exception_ptr const& e); + +/// Make a failed SemiFuture from an exception_wrapper. +template <class T> +SemiFuture<T> makeSemiFuture(exception_wrapper ew); + +/** Make a SemiFuture from an exception type E that can be passed to + std::make_exception_ptr(). */ +template <class T, class E> +typename std:: + enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type + makeSemiFuture(E const& e); + +/** Make a Future out of a Try */ +template <class T> +SemiFuture<T> makeSemiFuture(Try<T> t); + +/** + Make a completed Future by moving in a value. e.g. + + string foo = "foo"; + auto f = makeFuture(std::move(foo)); + + or + + auto f = makeFuture<string>("foo"); + + NOTE: This function is deprecated. Please use makeSemiFuture and pass the + appropriate executor to .via on the returned SemiFuture to get a + valid Future where necessary. +*/ +template <class T> +Future<typename std::decay<T>::type> makeFuture(T&& t); + +/** + Make a completed void Future. + + NOTE: This function is deprecated. Please use makeSemiFuture and pass the + appropriate executor to .via on the returned SemiFuture to get a + valid Future where necessary. + */ +Future<Unit> makeFuture(); + +/** + Make a Future by executing a function. + + If the function returns a value of type T, makeFutureWith + returns a completed Future<T>, capturing the value returned + by the function. + + If the function returns a Future<T> already, makeFutureWith + returns just that. + + Either way, if the function throws, a failed Future is + returned that captures the exception. + + Calling makeFutureWith(func) is equivalent to calling + makeFuture().then(func). + + NOTE: This function is deprecated. Please use makeSemiFutureWith and pass the + appropriate executor to .via on the returned SemiFuture to get a + valid Future where necessary. +*/ + +// makeFutureWith(Future<T>()) -> Future<T> +template <class F> +typename std:: + enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type + makeFutureWith(F&& func); + +// makeFutureWith(T()) -> Future<T> +// makeFutureWith(void()) -> Future<Unit> +template <class F> +typename std::enable_if< + !(isFuture<invoke_result_t<F>>::value), + Future<lift_unit_t<invoke_result_t<F>>>>::type +makeFutureWith(F&& func); + +/// Make a failed Future from an exception_ptr. +/// Because the Future's type cannot be inferred you have to specify it, e.g. +/// +/// auto f = makeFuture<string>(std::current_exception()); +template <class T> +[[deprecated("use makeSemiFuture(exception_wrapper)")]] Future<T> makeFuture( + std::exception_ptr const& e); + +/// Make a failed Future from an exception_wrapper. +/// NOTE: This function is deprecated. Please use makeSemiFuture and pass the +/// appropriate executor to .via on the returned SemiFuture to get a +/// valid Future where necessary. +template <class T> +Future<T> makeFuture(exception_wrapper ew); + +/** Make a Future from an exception type E that can be passed to + std::make_exception_ptr(). + + NOTE: This function is deprecated. Please use makeSemiFuture and pass the + appropriate executor to .via on the returned SemiFuture to get a + valid Future where necessary. + */ +template <class T, class E> +typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>:: + type + makeFuture(E const& e); + +/** + Make a Future out of a Try + + NOTE: This function is deprecated. Please use makeSemiFuture and pass the + appropriate executor to .via on the returned SemiFuture to get a + valid Future where necessary. + */ +template <class T> +Future<T> makeFuture(Try<T> t); + +/* + * Return a new Future that will call back on the given Executor. + * This is just syntactic sugar for makeFuture().via(executor) + * + * @param executor the Executor to call back on + * @param priority optionally, the priority to add with. Defaults to 0 which + * represents medium priority. + * + * @returns a void Future that will call back on the given executor + */ +inline Future<Unit> via(Executor::KeepAlive<> executor); +inline Future<Unit> via(Executor::KeepAlive<> executor, int8_t priority); + +/// Execute a function via the given executor and return a future. +/// This is semantically equivalent to via(executor).then(func), but +/// easier to read and slightly more efficient. +template <class Func> +auto via(Executor::KeepAlive<>, Func&& func) -> Future< + typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner>; + +/** When all the input Futures complete, the returned Future will complete. + Errors do not cause early termination; this Future will always succeed + after all its Futures have finished (whether successfully or with an + error). + + The Futures are moved in, so your copies are invalid. If you need to + chain further from these Futures, use the variant with an output iterator. + + This function is thread-safe for Futures running on different threads. But + if you are doing anything non-trivial after, you will probably want to + follow with `via(executor)` because it will complete in whichever thread the + last Future completes in. + + The return type for Future<T> input is a SemiFuture<std::vector<Try<T>>> + for collectX and collectXSemiFuture. + + collectXUnsafe returns an inline Future that erases the executor from the + incoming Futures/SemiFutures. collectXUnsafe should be phased out and + replaced with collectX(...).via(e) where e is a valid non-inline executor. + */ +template <class InputIterator> +SemiFuture<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAllSemiFuture(InputIterator first, InputIterator last); + +/// Sugar for the most common case +template <class Collection> +auto collectAllSemiFuture(Collection&& c) + -> decltype(collectAllSemiFuture(c.begin(), c.end())) { + return collectAllSemiFuture(c.begin(), c.end()); +} + +// Unsafe variant, see above comment for details +template <class InputIterator> +Future<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAllUnsafe(InputIterator first, InputIterator last); + +// Unsafe variant sugar, see above comment for details +template <class Collection> +auto collectAllUnsafe(Collection&& c) + -> decltype(collectAllUnsafe(c.begin(), c.end())) { + return collectAllUnsafe(c.begin(), c.end()); +} + +template <class InputIterator> +SemiFuture<std::vector< + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAll(InputIterator first, InputIterator last); + +template <class Collection> +auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) { + return collectAll(c.begin(), c.end()); +} + +/// This version takes a varying number of Futures instead of an iterator. +/// The return type for (Future<T1>, Future<T2>, ...) input +/// is a SemiFuture<std::tuple<Try<T1>, Try<T2>, ...>>. +/// The Futures are moved in, so your copies are invalid. +template <typename... Fs> +SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAllSemiFuture(Fs&&... fs); + +// Unsafe variant of collectAll, see coment above for details. Returns +// a Future<std::tuple<Try<T1>, Try<T2>, ...>> on the Inline executor. +template <typename... Fs> +Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAllUnsafe(Fs&&... fs); + +template <typename... Fs> +SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> +collectAll(Fs&&... fs); + +/// Like collectAll, but will short circuit on the first exception. Thus, the +/// type of the returned SemiFuture is std::vector<T> instead of +/// std::vector<Try<T>> +template <class InputIterator> +SemiFuture<std::vector< + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collect(InputIterator first, InputIterator last); + +/// Sugar for the most common case +template <class Collection> +auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) { + return collect(c.begin(), c.end()); +} + +// Unsafe variant of collect. Returns a Future<std::vector<T>> that +// completes inline. +template <class InputIterator> +Future<std::vector< + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collectUnsafe(InputIterator first, InputIterator last); + +/// Sugar for the most common unsafe case. Returns a Future<std::vector<T>> +// that completes inline. +template <class Collection> +auto collectUnsafe(Collection&& c) + -> decltype(collectUnsafe(c.begin(), c.end())) { + return collectUnsafe(c.begin(), c.end()); +} + +/// Like collectAll, but will short circuit on the first exception. Thus, the +/// type of the returned SemiFuture is std::tuple<T1, T2, ...> instead of +/// std::tuple<Try<T1>, Try<T2>, ...> +template <typename... Fs> +SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect( + Fs&&... fs); + +/** The result is a pair of the index of the first Future to complete and + the Try. If multiple Futures complete at the same time (or are already + complete when passed in), the "winner" is chosen non-deterministically. + + This function is thread-safe for Futures running on different threads. + */ +template <class InputIterator> +SemiFuture<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAny(InputIterator first, InputIterator last); +// Unsafe variant of collectAny, Returns a Future that completes inline. +template <class InputIterator> +Future<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAnyUnsafe(InputIterator first, InputIterator last); +template <class InputIterator> +SemiFuture<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> +collectAnySemiFuture(InputIterator first, InputIterator last); + +/// Sugar for the most common case +template <class Collection> +auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) { + return collectAny(c.begin(), c.end()); +} +// Unsafe variant of common form of collectAny, Returns a Future that completes +// inline. +template <class Collection> +auto collectAnyUnsafe(Collection&& c) + -> decltype(collectAnyUnsafe(c.begin(), c.end())) { + return collectAnyUnsafe(c.begin(), c.end()); +} +template <class Collection> +auto collectAnySemiFuture(Collection&& c) + -> decltype(collectAnySemiFuture(c.begin(), c.end())) { + return collectAnySemiFuture(c.begin(), c.end()); +} + +/** Similar to collectAny, collectAnyWithoutException return the first Future to + * complete without exceptions. If none of the future complete without + * exceptions, the last exception will be returned as a result. + */ +template <class InputIterator> +SemiFuture<std::pair< + size_t, + typename std::iterator_traits<InputIterator>::value_type::value_type>> +collectAnyWithoutException(InputIterator first, InputIterator last); + +/// Sugar for the most common case +template <class Collection> +auto collectAnyWithoutException(Collection&& c) + -> decltype(collectAnyWithoutException(c.begin(), c.end())) { + return collectAnyWithoutException(c.begin(), c.end()); +} + +/** when n Futures have completed, the Future completes with a vector of + the index and Try of those n Futures (the indices refer to the original + order, but the result vector will be in an arbitrary order) + + Not thread safe. + */ +template <class InputIterator> +SemiFuture<std::vector<std::pair< + size_t, + Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>> +collectN(InputIterator first, InputIterator last, size_t n); + +/// Sugar for the most common case +template <class Collection> +auto collectN(Collection&& c, size_t n) + -> decltype(collectN(c.begin(), c.end(), n)) { + return collectN(c.begin(), c.end(), n); +} + +/** window creates up to n Futures using the values + in the collection, and then another Future for each Future + that completes + + this is basically a sliding window of Futures of size n + + func must return a Future for each value in input + */ +template < + class Collection, + class F, + class ItT = typename std::iterator_traits< + typename Collection::iterator>::value_type, + class Result = typename invoke_result_t<F, ItT&&>::value_type> +std::vector<Future<Result>> window(Collection input, F func, size_t n); + +template < + class Collection, + class F, + class ItT = typename std::iterator_traits< + typename Collection::iterator>::value_type, + class Result = typename invoke_result_t<F, ItT&&>::value_type> +std::vector<Future<Result>> +window(Executor::KeepAlive<> executor, Collection input, F func, size_t n); + +template <typename F, typename T, typename ItT> +using MaybeTryArg = typename std:: + conditional<is_invocable_v<F, T&&, Try<ItT>&&>, Try<ItT>, ItT>::type; + +/** repeatedly calls func on every result, e.g. + reduce(reduce(reduce(T initial, result of first), result of second), ...) + + The type of the final result is a Future of the type of the initial value. + + Func can either return a T, or a Future<T> + + func is called in order of the input, see unorderedReduce if that is not + a requirement + */ +template <class It, class T, class F> +Future<T> reduce(It first, It last, T&& initial, F&& func); + +/// Sugar for the most common case +template <class Collection, class T, class F> +auto reduce(Collection&& c, T&& initial, F&& func) -> decltype(folly::reduce( + c.begin(), + c.end(), + std::forward<T>(initial), + std::forward<F>(func))) { + return folly::reduce( + c.begin(), c.end(), std::forward<T>(initial), std::forward<F>(func)); +} + +/** like reduce, but calls func on finished futures as they complete + does NOT keep the order of the input + */ +template <class It, class T, class F> +Future<T> unorderedReduce(It first, It last, T initial, F func); + +/// Sugar for the most common case +template <class Collection, class T, class F> +auto unorderedReduce(Collection&& c, T&& initial, F&& func) + -> decltype(folly::unorderedReduce( + c.begin(), + c.end(), + std::forward<T>(initial), + std::forward<F>(func))) { + return folly::unorderedReduce( + c.begin(), c.end(), std::forward<T>(initial), std::forward<F>(func)); +} + +/// Carry out the computation contained in the given future if +/// while the predicate continues to hold. +/// +/// if thunk behaves like std::function<Future<T2>(void)> +/// returns Future<Unit> +/// if thunk behaves like std::function<SemiFuture<T2>(void)> +/// returns SemiFuture<Unit> +/// predicate behaves like std::function<bool(void)> +template <class P, class F> +typename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type +whileDo(P&& predicate, F&& thunk); +template <class P, class F> +typename std:: + enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type + whileDo(P&& predicate, F&& thunk); + +/// Repeat the given future (i.e., the computation it contains) n times. +/// +/// thunk behaves like +/// std::function<Future<T2>(void)> +/// or +/// std::function<SemiFuture<T2>(void)> +template <class F> +auto times(int n, F&& thunk); +} // namespace folly + +#if FOLLY_HAS_COROUTINES + +namespace folly { +namespace detail { + +template <typename T> +class FutureAwaitable { + public: + explicit FutureAwaitable(folly::Future<T>&& future) noexcept + : future_(std::move(future)) {} + + bool await_ready() { + if (future_.isReady()) { + result_ = std::move(future_.getTry()); + return true; + } + return false; + } + + T await_resume() { + return std::move(result_).value(); + } + + Try<T> await_resume_try() { + return std::move(result_); + } + + FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend( + std::experimental::coroutine_handle<> h) { + // FutureAwaitable may get destroyed as soon as the callback is executed. + // Make sure the future object doesn't get destroyed until setCallback_ + // returns. + auto future = std::move(future_); + future.setCallback_( + [this, h](Executor::KeepAlive<>&&, Try<T>&& result) mutable { + result_ = std::move(result); + h.resume(); + }); + } + + private: + folly::Future<T> future_; + folly::Try<T> result_; +}; + +} // namespace detail + +template <typename T> +inline detail::FutureAwaitable<T> +/* implicit */ operator co_await(Future<T>&& future) noexcept { + return detail::FutureAwaitable<T>(std::move(future)); +} + +} // namespace folly +#endif + +#include <folly/futures/Future-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/futures/FutureSplitter.h b/ios/Pods/Flipper-Folly/folly/futures/FutureSplitter.h new file mode 100644 index 000000000..139107b4b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/FutureSplitter.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/futures/Future.h> +#include <folly/futures/SharedPromise.h> +#include <folly/lang/Exception.h> + +namespace folly { + +class FOLLY_EXPORT FutureSplitterInvalid : public FutureException { + public: + FutureSplitterInvalid() + : FutureException("No Future in this FutureSplitter") {} +}; + +/* + * FutureSplitter provides a `getFuture()' method which can be called multiple + * times, returning a new Future each time. These futures are completed when the + * original Future passed to the FutureSplitter constructor is completed, and + * are completed on the same executor (if any) and at the same priority as the + * original Future. Calls to `getFuture()' after that time return a completed + * Future. + */ +template <class T> +class FutureSplitter { + public: + /** + * Default constructor for convenience only. It is an error to call + * `getFuture()` on a default-constructed FutureSplitter which has not had + * a correctly-constructed FutureSplitter copy- or move-assigned into it. + */ + FutureSplitter() = default; + + /** + * Provide a way to split a Future<T>. + */ + explicit FutureSplitter(Future<T>&& future) + : promise_(std::make_shared<SharedPromise<T>>()), + e_(getExecutorFrom(future)) { + std::move(future).thenTry([promise = promise_](Try<T>&& theTry) { + promise->setTry(std::move(theTry)); + }); + } + + /** + * This can be called an unlimited number of times per FutureSplitter. + */ + Future<T> getFuture() { + if (promise_ == nullptr) { + throw_exception<FutureSplitterInvalid>(); + } + return promise_->getSemiFuture().via(e_); + } + + /** + * This can be called an unlimited number of times per FutureSplitter. + */ + SemiFuture<T> getSemiFuture() { + if (promise_ == nullptr) { + throw_exception<FutureSplitterInvalid>(); + } + return promise_->getSemiFuture(); + } + + private: + std::shared_ptr<SharedPromise<T>> promise_; + Executor::KeepAlive<> e_; + + static Executor* getExecutorFrom(Future<T>& f) { + // If the passed future had a null executor, use an inline executor + // to ensure that .via is safe + auto* e = f.getExecutor(); + return e ? e : &InlineExecutor::instance(); + } +}; + +/** + * Convenience function, allowing us to exploit template argument deduction to + * improve readability. + */ +template <class T> +FutureSplitter<T> splitFuture(Future<T>&& future) { + return FutureSplitter<T>{std::move(future)}; +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.cpp b/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.cpp new file mode 100644 index 000000000..a2b220a42 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/futures/ManualTimekeeper.h> + +namespace folly { + +ManualTimekeeper::ManualTimekeeper() : now_{std::chrono::steady_clock::now()} {} + +SemiFuture<Unit> ManualTimekeeper::after(HighResDuration dur) { + auto contract = folly::makePromiseContract<Unit>(); + if (dur.count() == 0) { + contract.first.setValue(folly::unit); + } else { + schedule_.withWLock([&contract, this, &dur](auto& schedule) { + schedule.insert(std::make_pair(now_ + dur, std::move(contract.first))); + }); + } + return std::move(contract.second); +} + +void ManualTimekeeper::advance(Duration dur) { + now_ += dur; + schedule_.withWLock([this](auto& schedule) { + auto start = schedule.begin(); + auto end = schedule.upper_bound(now_); + for (auto iter = start; iter != end; iter++) { + iter->second.setValue(folly::unit); + } + schedule.erase(start, end); + }); +} + +std::chrono::steady_clock::time_point ManualTimekeeper::now() const { + return now_; +} + +std::size_t ManualTimekeeper::numScheduled() const { + return schedule_.withRLock([](const auto& sched) { return sched.size(); }); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.h b/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.h new file mode 100644 index 000000000..50299ee1e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/ManualTimekeeper.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Synchronized.h> +#include <folly/futures/Future.h> +#include <folly/futures/Promise.h> + +#include <chrono> +#include <map> + +namespace folly { + +// Manually controlled Timekeeper for unit testing. +// +// We assume advance(), now(), and numScheduled() are called from only a single +// thread, while after() can safely be called from multiple threads. +class ManualTimekeeper : public folly::Timekeeper { + public: + explicit ManualTimekeeper(); + + /// The returned future is completed when someone calls advance and pushes the + /// executor's clock to a value greater than or equal to (now() + dur) + SemiFuture<Unit> after(folly::HighResDuration dur) override; + + /// Advance the timekeeper's clock to (now() + dur). All futures with target + /// time points less than or equal to (now() + dur) are fulfilled after the + /// call to advance() returns + void advance(folly::Duration dur); + + /// Returns the current clock value in the timekeeper. This is advanced only + /// when someone calls advance() + std::chrono::steady_clock::time_point now() const; + + /// Returns the number of futures that are pending and have not yet been + /// fulfilled + std::size_t numScheduled() const; + + private: + Executor::KeepAlive<Executor> executor_; + std::chrono::steady_clock::time_point now_; + folly::Synchronized< + std::multimap<std::chrono::steady_clock::time_point, Promise<Unit>>> + schedule_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Portability.h b/ios/Pods/Flipper-Folly/folly/futures/Portability.h new file mode 100644 index 000000000..98dab9a48 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Portability.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +#if FOLLY_MOBILE || defined(__APPLE__) +#define FOLLY_FUTURE_USING_FIBER 0 +#else +#define FOLLY_FUTURE_USING_FIBER 1 +#endif diff --git a/ios/Pods/Flipper-Folly/folly/futures/Promise-inl.h b/ios/Pods/Flipper-Folly/folly/futures/Promise-inl.h new file mode 100644 index 000000000..ace284ec6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Promise-inl.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <thread> +#include <utility> + +#include <folly/executors/InlineExecutor.h> +#include <folly/futures/detail/Core.h> +#include <folly/lang/Pretty.h> + +namespace folly { + +namespace futures { +namespace detail { +template <typename T> +void coreDetachPromiseMaybeWithResult(Core<T>& core) { + if (!core.hasResult()) { + core.setResult(Try<T>(exception_wrapper(BrokenPromise(pretty_name<T>())))); + } + core.detachPromise(); +} +template <typename T> +void setTry(Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t) { + p.setTry(std::move(ka), std::move(t)); +} +} // namespace detail +} // namespace futures + +template <class T> +Promise<T> Promise<T>::makeEmpty() noexcept { + return Promise<T>(futures::detail::EmptyConstruct{}); +} + +template <class T> +Promise<T>::Promise() : retrieved_(false), core_(Core::make()) {} + +template <class T> +Promise<T>::Promise(Promise<T>&& other) noexcept + : retrieved_(std::exchange(other.retrieved_, false)), + core_(std::exchange(other.core_, nullptr)) {} + +template <class T> +Promise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept { + detach(); + retrieved_ = std::exchange(other.retrieved_, false); + core_ = std::exchange(other.core_, nullptr); + return *this; +} + +template <class T> +void Promise<T>::throwIfFulfilled() const { + if (getCore().hasResult()) { + throw_exception<PromiseAlreadySatisfied>(); + } +} + +template <class T> +Promise<T>::Promise(futures::detail::EmptyConstruct) noexcept + : retrieved_(false), core_(nullptr) {} + +template <class T> +Promise<T>::~Promise() { + detach(); +} + +template <class T> +void Promise<T>::detach() { + if (core_) { + if (!retrieved_) { + core_->detachFuture(); + } + futures::detail::coreDetachPromiseMaybeWithResult(*core_); + core_ = nullptr; + } +} + +template <class T> +SemiFuture<T> Promise<T>::getSemiFuture() { + if (retrieved_) { + throw_exception<FutureAlreadyRetrieved>(); + } + retrieved_ = true; + return SemiFuture<T>(&getCore()); +} + +template <class T> +Future<T> Promise<T>::getFuture() { + // An InlineExecutor approximates the old behaviour of continuations + // running inine on setting the value of the promise. + return getSemiFuture().via(&InlineExecutor::instance()); +} + +template <class T> +template <class E> +typename std::enable_if<std::is_base_of<std::exception, E>::value>::type +Promise<T>::setException(E const& e) { + setException(make_exception_wrapper<E>(e)); +} + +template <class T> +void Promise<T>::setException(exception_wrapper ew) { + setTry(Try<T>(std::move(ew))); +} + +template <class T> +template <typename F> +void Promise<T>::setInterruptHandler(F&& fn) { + getCore().setInterruptHandler(std::forward<F>(fn)); +} + +template <class T> +void Promise<T>::setTry(Try<T>&& t) { + throwIfFulfilled(); + core_->setResult(std::move(t)); +} + +template <class T> +void Promise<T>::setTry(Executor::KeepAlive<>&& ka, Try<T>&& t) { + throwIfFulfilled(); + core_->setResult(std::move(ka), std::move(t)); +} + +template <class T> +template <class M> +void Promise<T>::setValue(M&& v) { + static_assert(!std::is_same<T, void>::value, "Use setValue() instead"); + + setTry(Try<T>(std::forward<M>(v))); +} + +template <class T> +template <class F> +void Promise<T>::setWith(F&& func) { + throwIfFulfilled(); + setTry(makeTryWith(std::forward<F>(func))); +} + +template <class T> +bool Promise<T>::isFulfilled() const noexcept { + if (core_) { + return core_->hasResult(); + } + return true; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/Promise.h b/ios/Pods/Flipper-Folly/folly/futures/Promise.h new file mode 100644 index 000000000..502e077c2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Promise.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> + +#include <folly/Portability.h> +#include <folly/Try.h> +#include <folly/futures/detail/Core.h> +#include <folly/lang/Exception.h> + +namespace folly { + +class FOLLY_EXPORT PromiseException : public std::logic_error { + public: + using std::logic_error::logic_error; +}; + +class FOLLY_EXPORT PromiseInvalid : public PromiseException { + public: + PromiseInvalid() : PromiseException("Promise invalid") {} +}; + +class FOLLY_EXPORT PromiseAlreadySatisfied : public PromiseException { + public: + PromiseAlreadySatisfied() : PromiseException("Promise already satisfied") {} +}; + +class FOLLY_EXPORT FutureAlreadyRetrieved : public PromiseException { + public: + FutureAlreadyRetrieved() : PromiseException("Future already retrieved") {} +}; + +class FOLLY_EXPORT BrokenPromise : public PromiseException { + public: + explicit BrokenPromise(const std::string& type) + : PromiseException("Broken promise for type name `" + type + '`') {} + + explicit BrokenPromise(const char* type) : BrokenPromise(std::string(type)) {} +}; + +// forward declaration +template <class T> +class SemiFuture; +template <class T> +class Future; +template <class T> +class Promise; + +namespace futures { +namespace detail { +template <class T> +class FutureBase; +struct EmptyConstruct {}; +template <typename T, typename F> +class CoreCallbackState; +template <typename T> +void setTry(Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t); +} // namespace detail +} // namespace futures + +/// Promises and futures provide a potentially nonblocking mechanism +/// to execute a producer/consumer operation concurrently, with +/// threading/pools controlled via an executor. There are multiple potential +/// patterns for using promises and futures including some that block the +/// caller, though that is discouraged; it should be used only when necessary. +/// +/// One typical pattern uses a series of calls to set up a small, limited +/// program that... +/// +/// - ...performs the desired operations (based on a lambda)... +/// - ...on an asynchronously provided input (an exception or a value)... +/// - ...lazily, when that input is ready (without blocking the caller)... +/// - ...using appropriate execution resources (determined by the executor)... +/// - ...then after constructing the 'program,' launches the asynchronous +/// producer. +/// +/// That usage pattern looks roughly like this: +/// +/// auto [p, f] = makePromiseContract(executor); +/// g = std::move(f).then([](MyValue&& x) { +/// ...executor runs this code if/when a MyValue is ready... +/// }); +/// ...launch the async producer that eventually calls p.setResult()... +/// +/// This is just one of many potential usage patterns. It has the desired +/// property of being nonblocking to the caller. Of course the `.then()` +/// code is deferred until the produced value (or exception) is ready, +/// but no code actually blocks pending completion of other operations. +/// +/// The promise/future mechanism is limited to a single object of some arbitrary +/// type. It also supports a (logically) void result, i.e., in cases where the +/// continuation/consumer (the `.then()` code if using the above pattern) is not +/// expecting a value because the 'producer' is running for its side-effects. +/// +/// The primary data movement is from producer to consumer, however Promise and +/// Future also provide a mechanism where the consumer can send an interruption +/// message to the producer. The meaning and response to that interruption +/// message is controlled by the promise; see `Promise::setInterruptHandler()`. +/// +/// Neither Promise nor Future is thread-safe. All internal interactions +/// between a promise and its associated future are thread-safe, provided that +/// callers otherwise honor the promise's contract and the future's contract. +/// +/// Logically there are up to three threads (though in practice there are often +/// fewer - one thread might take on more than one role): +/// +/// - Set-up thread: thread used to construct the Promise, and often also to +/// set up the SemiFuture/Future. +/// - Producer thread: thread that produces the result. +/// - Consumer thread: thread in which the continuation is invoked (a +/// continuation is a callback provided to `.then` or to a variant). +/// +/// For description purposes, the term 'shared state' is used to describe the +/// logical state shared by the promise and the future. This 'third object' +/// represents things like whether the result has been fulfilled, the value or +/// exception in that result, and the data needed to handle interruption +/// requests. +/// +/// A promise can be in various logical states: +/// +/// - valid vs. invalid (has vs. does not have a shared state, respectfully). +/// - fulfilled vs. unfulfilled (an invalid promise is always fulfilled; a valid +/// promise is fulfilled if the shared-state has a result). +/// +/// A promise `p` may optionally have an associated future. This future, if it +/// exists, may be either a SemiFuture or a Future, and is defined as the +/// future (if any) that holds the same shared state as promise `p`. +/// The associated future is initially the future returned from +/// `p.getFuture()` or `p.getSemiFuture()`, but various operations +/// may transfer the shared state from one future to another. +template <class T> +class Promise { + public: + /// Returns an invalid promise. + /// + /// Postconditions: + /// + /// - `RESULT.valid() == false` + /// - `RESULT.isFulfilled() == true` + static Promise<T> makeEmpty() noexcept; + + /// Constructs a valid but unfulfilled promise. + /// + /// Postconditions: + /// + /// - `valid() == true` (it will have a shared state) + /// - `isFulfilled() == false` (its shared state won't have a result) + Promise(); + + /// Postconditions: + /// + /// - If `valid()` and `!isFulfilled()`, the associated future (if any) will + /// be completed with a `BrokenPromise` exception *as if* by + /// `setException(...)`. + /// - If `valid()`, releases, possibly destroying, the shared state. + ~Promise(); + + // not copyable + Promise(Promise const&) = delete; + Promise& operator=(Promise const&) = delete; + + /// Move ctor + /// + /// Postconditions: + /// + /// - `this` will have whatever shared-state was previously held by `other` + /// (if any) + /// - `other.valid()` will be false (`other` will not have any shared state) + Promise(Promise<T>&& other) noexcept; + + /// Move assignment + /// + /// Postconditions: + /// + /// - If `valid()` and `!isFulfilled()`, the associated future (if any) will + /// be completed with a `BrokenPromise` exception *as if* by + /// `setException(...)`. + /// - If `valid()`, releases, possibly destroying, the original shared state. + /// - `this` will have whatever shared-state was previously held by `other` + /// (if any) + /// - `other.valid()` will be false (`other` will not have any shared state) + Promise& operator=(Promise<T>&& other) noexcept; + + /// Return a SemiFuture associated with this Promise, sharing the same shared + /// state as `this`. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - neither getSemiFuture() nor getFuture() may have been called previously + /// on `this` Promise (else throws FutureAlreadyRetrieved) + /// + /// Postconditions: + /// + /// - `RESULT.valid() == true` + /// - RESULT will share the same shared-state as `this` + /// + /// DEPRECATED: use `folly::makePromiseContract()` instead. + SemiFuture<T> getSemiFuture(); + + /// Return a Future associated with this Promise, sharing the same shared + /// state as `this`. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - neither getSemiFuture() nor getFuture() may have been called previously + /// on `this` Promise (else throws FutureAlreadyRetrieved) + /// + /// Postconditions: + /// + /// - `RESULT.valid() == true` + /// - RESULT will share the same shared-state as `this` + /// + /// DEPRECATED: use `folly::makePromiseContract()` instead. If you can't use + /// that, use `this->getSemiFuture()` then get a Future by calling `.via()` + /// with an appropriate executor. + Future<T> getFuture(); + + /// Fulfill the Promise with an exception_wrapper. + /// + /// Sample usage: + /// + /// Promise<MyValue> p = ... + /// ... + /// auto const ep = std::exception_ptr(); + /// auto const ew = exception_wrapper::from_exception_ptr(ep); + /// p.setException(ew); + /// + /// Functionally equivalent to `setTry(Try<T>(std::move(ew)))` + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) + /// + /// Postconditions: + /// + /// - `isFulfilled() == true` + /// - `valid() == true` (unchanged) + /// - The associated future (if any) will complete with the exception. + void setException(exception_wrapper ew); + + /// Fulfill the Promise with exception `e` *as if* by + /// `setException(make_exception_wrapper<E>(e))`. + /// + /// Please see `setException(exception_wrapper)` for semantics/contract. + template <class E> + typename std::enable_if<std::is_base_of<std::exception, E>::value>::type + setException(E const& e); + + /// Sets a handler for the producer to receive a (logical) interruption + /// request (exception) sent from the consumer via `future.raise()`. + /// + /// Details: The consumer calls `future.raise()` when it wishes to send a + /// logical interruption message (an exception), and that exception/message + /// is passed to `fn()`. The thread used to call `fn()` depends on timing + /// (see Postconditions for threading details). + /// + /// Handler `fn()` can do anything you want, but if you bother to set one + /// then you probably will want to (more or less immediately) fulfill the + /// promise with an exception (or other special value) indicating how the + /// interrupt was handled. + /// + /// This call silently does nothing if `isFulfilled()`. + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `fn` must be copyable and must be invocable with + /// `exception_wrapper const&` + /// - the code within `fn()` must be safe to run either synchronously within + /// the `setInterruptHandler()` call or asynchronously within the consumer + /// thread's call to `future.raise()`. + /// - the code within `fn()` must also be safe to run after this promise is + /// fulfilled; this may have lifetime/race-case ramifications, e.g., if the + /// code of `fn()` might access producer-resources that will be destroyed, + /// then the destruction of those producer-resources must be deferred beyond + /// the moment when this promise is fulfilled. + /// + /// Postconditions: + /// + /// - if the consumer calls `future.raise()` early enough (up to a particular + /// moment within the `setInterruptHandler()` call), `fn()` will be called + /// synchronously (in the current thread, during this call). + /// - if the consumer calls `future.raise()` after that moment within + /// `setInterruptHandler()` but before this promise is fulfilled, `fn()` + /// will be called asynchronously (in the consumer's thread, within the call + /// to `future.raise()`). + /// - if the consumer calls `future.raise()` after this promise is fulfilled, + /// `fn()` may or may not be called at all, and if it is called, it will be + /// called asynchronously (within the consumer's call to `future.raise()`). + /// + /// IMPORTANT: `fn()` should return quickly since it could block this call + /// to `promise.setInterruptHandler()` and/or a concurrent call to + /// `future.raise()`. Those two functions contend on the same lock; those + /// calls could block if `fn()` is invoked within one of those while the + /// lock is held. + template <typename F> + void setInterruptHandler(F&& fn); + + /// Fulfills a (logically) void Promise, that is, Promise<Unit>. + /// (If you want a void-promise, use Promise<Unit>, not Promise<void>.) + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) + /// + /// Postconditions: + /// + /// - `isFulfilled() == true` + /// - `valid() == true` (unchanged) + template <class B = T> + typename std::enable_if<std::is_same<Unit, B>::value, void>::type setValue() { + setTry(Try<T>(T())); + } + + /// Fulfill the Promise with the specified value using perfect forwarding. + /// + /// Functionally equivalent to `setTry(Try<T>(std::forward<M>(value)))` + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) + /// + /// Postconditions: + /// + /// - `isFulfilled() == true` + /// - `valid() == true` (unchanged) + /// - The associated future will see the value, e.g., in its continuation. + template <class M> + void setValue(M&& value); + + /// Fulfill the Promise with the specified Try (value or exception). + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) + /// + /// Postconditions: + /// + /// - `isFulfilled() == true` + /// - `valid() == true` (unchanged) + /// - The associated future will see the result, e.g., in its continuation. + void setTry(Try<T>&& t); + + /// Fulfill this Promise with the result of a function that takes no + /// arguments and returns something implicitly convertible to T. + /// + /// Example: + /// + /// p.setWith([] { do something that may throw; return a T; }); + /// + /// Functionally equivalent to `setTry(makeTryWith(static_cast<F&&>(func)));` + /// + /// Preconditions: + /// + /// - `valid() == true` (else throws PromiseInvalid) + /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) + /// + /// Postconditions: + /// + /// - `func()` will be run synchronously (in this thread, during this call) + /// - If `func()` returns, the return value will be captured as if via + /// `setValue()` + /// - If `func()` throws, the exception will be captured as if via + /// `setException()` + /// - `isFulfilled() == true` + /// - `valid() == true` (unchanged) + /// - The associated future will see the result, e.g., in its continuation. + template <class F> + void setWith(F&& func); + + /// true if this has a shared state; + /// false if this has been consumed/moved-out. + bool valid() const noexcept { + return core_ != nullptr; + } + + /// True if either this promise was fulfilled or is invalid. + /// + /// - True if `!valid()` + /// - True if `valid()` and this was fulfilled (a prior call to `setValue()`, + /// `setTry()`, `setException()`, or `setWith()`) + bool isFulfilled() const noexcept; + + private: + template <class> + friend class futures::detail::FutureBase; + template <class> + friend class SemiFuture; + template <class> + friend class Future; + template <class, class> + friend class futures::detail::CoreCallbackState; + friend void futures::detail::setTry<T>( + Promise<T>& p, + Executor::KeepAlive<>&& ka, + Try<T>&& t); + + // Whether the Future has been retrieved (a one-time operation). + bool retrieved_; + + using Core = futures::detail::Core<T>; + + // Throws PromiseInvalid if there is no shared state object; else returns it + // by ref. + // + // Implementation methods should usually use this instead of `this->core_`. + // The latter should be used only when you need the possibly-null pointer. + Core& getCore() { + return getCoreImpl(core_); + } + Core const& getCore() const { + return getCoreImpl(core_); + } + + template <typename CoreT> + static CoreT& getCoreImpl(CoreT* core) { + if (!core) { + throw_exception<PromiseInvalid>(); + } + return *core; + } + + /// Fulfill the Promise with the specified Try (value or exception) and + /// propagate the completing executor. + void setTry(Executor::KeepAlive<>&& ka, Try<T>&& t); + + // shared core state object + // usually you should use `getCore()` instead of directly accessing `core_`. + Core* core_; + + explicit Promise(futures::detail::EmptyConstruct) noexcept; + + void throwIfFulfilled() const; + void detach(); +}; + +} // namespace folly + +#include <folly/futures/Future.h> +#include <folly/futures/Promise-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/futures/Retrying.h b/ios/Pods/Flipper-Folly/folly/futures/Retrying.h new file mode 100644 index 000000000..9c2b3a3da --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/Retrying.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Random.h> +#include <folly/functional/Invoke.h> +#include <folly/futures/Future.h> + +namespace folly { +namespace futures { + +/** + * retrying + * + * Given a policy and a future-factory, creates futures according to the + * policy. + * + * The policy must be moveable - retrying will move it a lot - and callable of + * any of the forms: + * - Future<bool>(size_t, exception_wrapper) + * - SemiFuture<bool>(size_t, exception_wrapper) + * - bool(size_t, exception_wrapper) + * Internally, the latter is transformed into the former in the obvious way. + * The first parameter is the attempt number of the next prospective attempt; + * the second parameter is the most recent exception. The policy returns a + * (Semi)Future<bool> which, when completed with true, indicates that a retry + * is desired. + * + * If the callable or policy returns a SemiFuture, then retrying returns a + * SemiFuture. Note that, consistent with other SemiFuture-returning functions + * the implication of this statement is that retrying should be assumed to be + * lazy: it may do nothing until .wait()/.get() is called on the result or + * an executor is attached with .via. + * + * We provide a few generic policies: + * - Basic + * - CappedJitteredexponentialBackoff + * + * Custom policies may use the most recent try number and exception to decide + * whether to retry and optionally to do something interesting like delay + * before the retry. Users may pass inline lambda expressions as policies, or + * may define their own data types meeting the above requirements. Users are + * responsible for managing the lifetimes of anything pointed to or referred to + * from inside the policy. + * + * For example, one custom policy may try up to k times, but only if the most + * recent exception is one of a few types or has one of a few error codes + * indicating that the failure was transitory. + * + * Cancellation is not supported. + * + * If both FF and Policy inline executes, then it is possible to hit a stack + * overflow due to the recursive nature of the retry implementation + */ +template <class Policy, class FF> +auto retrying(Policy&& p, FF&& ff); + +namespace detail { + +struct retrying_policy_raw_tag {}; +struct retrying_policy_fut_tag {}; + +template <class Policy> +struct retrying_policy_traits { + using result = invoke_result_t<Policy, size_t, const exception_wrapper&>; + using is_raw = std::is_same<result, bool>; + using is_fut = std::is_same<result, Future<bool>>; + using is_semi_fut = std::is_same<result, SemiFuture<bool>>; + using tag = typename std::conditional< + is_raw::value, + retrying_policy_raw_tag, + typename std::conditional< + is_fut::value || is_semi_fut::value, + retrying_policy_fut_tag, + void>::type>::type; +}; + +template <class Policy, class FF, class Prom> +void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) { + using F = invoke_result_t<FF, size_t>; + using T = typename F::value_type; + auto f = makeFutureWith([&] { return ff(k++); }); + std::move(f).thenTry([k, + prom = std::move(prom), + pm = std::forward<Policy>(p), + ffm = std::forward<FF>(ff)](Try<T>&& t) mutable { + if (t.hasValue()) { + prom.setValue(std::move(t).value()); + return; + } + auto& x = t.exception(); + auto q = makeFutureWith([&] { return pm(k, x); }); + std::move(q).thenTry([k, + prom = std::move(prom), + xm = std::move(x), + pm = std::move(pm), + ffm = std::move(ffm)](Try<bool> shouldRetry) mutable { + if (shouldRetry.hasValue() && shouldRetry.value()) { + retryingImpl(k, std::move(pm), std::move(ffm), std::move(prom)); + } else if (shouldRetry.hasValue()) { + prom.setException(std::move(xm)); + } else { + prom.setException(std::move(shouldRetry.exception())); + } + }); + }); +} + +template <class Policy, class FF> +typename std::enable_if< + !(isSemiFuture<invoke_result_t<FF, size_t>>::value || + isSemiFuture<invoke_result_t<Policy, size_t, exception_wrapper>>::value), + invoke_result_t<FF, size_t>>::type +retrying(size_t k, Policy&& p, FF&& ff) { + using F = invoke_result_t<FF, size_t>; + using T = typename F::value_type; + auto prom = Promise<T>(); + auto f = prom.getFuture(); + retryingImpl( + k, std::forward<Policy>(p), std::forward<FF>(ff), std::move(prom)); + return f; +} + +template <class Policy, class FF> +typename std::enable_if< + isSemiFuture<invoke_result_t<FF, size_t>>::value || + isSemiFuture<invoke_result_t<Policy, size_t, exception_wrapper>>::value, + SemiFuture<typename isFutureOrSemiFuture< + invoke_result_t<FF, size_t>>::Inner>>::type +retrying(size_t k, Policy&& p, FF&& ff) { + auto sf = folly::makeSemiFuture().deferExValue( + [k, p = std::forward<Policy>(p), ff = std::forward<FF>(ff)]( + Executor::KeepAlive<> ka, auto&&) mutable { + auto futureP = [p = std::forward<Policy>(p), ka]( + size_t kk, exception_wrapper e) { + return p(kk, std::move(e)).via(ka); + }; + auto futureFF = [ff = std::forward<FF>(ff), ka = std::move(ka)]( + size_t v) { return ff(v).via(ka); }; + return retrying(k, std::move(futureP), std::move(futureFF)); + }); + return sf; +} + +template <class Policy, class FF> +invoke_result_t<FF, size_t> +retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) { + auto q = [pm = std::forward<Policy>(p)](size_t k, exception_wrapper x) { + return makeFuture<bool>(pm(k, x)); + }; + return retrying(0, std::move(q), std::forward<FF>(ff)); +} + +template <class Policy, class FF> +auto retrying(Policy&& p, FF&& ff, retrying_policy_fut_tag) { + return retrying(0, std::forward<Policy>(p), std::forward<FF>(ff)); +} + +// jittered exponential backoff, clamped to [backoff_min, backoff_max] +template <class URNG> +Duration retryingJitteredExponentialBackoffDur( + size_t n, + Duration backoff_min, + Duration backoff_max, + double jitter_param, + URNG& rng) { + auto dist = std::normal_distribution<double>(0.0, jitter_param); + auto jitter = std::exp(dist(rng)); + auto backoff_rep = jitter * backoff_min.count() * std::pow(2, n - 1); + if (UNLIKELY(backoff_rep >= std::numeric_limits<Duration::rep>::max())) { + return std::max(backoff_min, backoff_max); + } + auto backoff = Duration(Duration::rep(backoff_rep)); + return std::max(backoff_min, std::min(backoff_max, backoff)); +} + +template <class Policy, class URNG> +std::function<Future<bool>(size_t, const exception_wrapper&)> +retryingPolicyCappedJitteredExponentialBackoff( + size_t max_tries, + Duration backoff_min, + Duration backoff_max, + double jitter_param, + URNG&& rng, + Policy&& p) { + return [pm = std::forward<Policy>(p), + max_tries, + backoff_min, + backoff_max, + jitter_param, + rngp = std::forward<URNG>(rng)]( + size_t n, const exception_wrapper& ex) mutable { + if (n == max_tries) { + return makeFuture(false); + } + return pm(n, ex).thenValue( + [n, backoff_min, backoff_max, jitter_param, rngp = std::move(rngp)]( + bool v) mutable { + if (!v) { + return makeFuture(false); + } + auto backoff = detail::retryingJitteredExponentialBackoffDur( + n, backoff_min, backoff_max, jitter_param, rngp); + return futures::sleep(backoff).toUnsafeFuture().thenValue( + [](auto&&) { return true; }); + }); + }; +} + +template <class Policy, class URNG> +std::function<Future<bool>(size_t, const exception_wrapper&)> +retryingPolicyCappedJitteredExponentialBackoff( + size_t max_tries, + Duration backoff_min, + Duration backoff_max, + double jitter_param, + URNG&& rng, + Policy&& p, + retrying_policy_raw_tag) { + auto q = [pm = std::forward<Policy>(p)]( + size_t n, const exception_wrapper& e) { + return makeFuture(pm(n, e)); + }; + return retryingPolicyCappedJitteredExponentialBackoff( + max_tries, + backoff_min, + backoff_max, + jitter_param, + std::forward<URNG>(rng), + std::move(q)); +} + +template <class Policy, class URNG> +std::function<Future<bool>(size_t, const exception_wrapper&)> +retryingPolicyCappedJitteredExponentialBackoff( + size_t max_tries, + Duration backoff_min, + Duration backoff_max, + double jitter_param, + URNG&& rng, + Policy&& p, + retrying_policy_fut_tag) { + return retryingPolicyCappedJitteredExponentialBackoff( + max_tries, + backoff_min, + backoff_max, + jitter_param, + std::forward<URNG>(rng), + std::forward<Policy>(p)); +} + +} // namespace detail + +template <class Policy, class FF> +auto retrying(Policy&& p, FF&& ff) { + using tag = typename detail::retrying_policy_traits<Policy>::tag; + return detail::retrying(std::forward<Policy>(p), std::forward<FF>(ff), tag()); +} + +inline std::function<bool(size_t, const exception_wrapper&)> +retryingPolicyBasic(size_t max_tries) { + return [=](size_t n, const exception_wrapper&) { return n < max_tries; }; +} + +template <class Policy, class URNG> +std::function<Future<bool>(size_t, const exception_wrapper&)> +retryingPolicyCappedJitteredExponentialBackoff( + size_t max_tries, + Duration backoff_min, + Duration backoff_max, + double jitter_param, + URNG&& rng, + Policy&& p) { + using tag = typename detail::retrying_policy_traits<Policy>::tag; + return detail::retryingPolicyCappedJitteredExponentialBackoff( + max_tries, + backoff_min, + backoff_max, + jitter_param, + std::forward<URNG>(rng), + std::forward<Policy>(p), + tag()); +} + +inline std::function<Future<bool>(size_t, const exception_wrapper&)> +retryingPolicyCappedJitteredExponentialBackoff( + size_t max_tries, + Duration backoff_min, + Duration backoff_max, + double jitter_param) { + auto p = [](size_t, const exception_wrapper&) { return true; }; + return retryingPolicyCappedJitteredExponentialBackoff( + max_tries, + backoff_min, + backoff_max, + jitter_param, + ThreadLocalPRNG(), + std::move(p)); +} + +} // namespace futures +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/SharedPromise-inl.h b/ios/Pods/Flipper-Folly/folly/futures/SharedPromise-inl.h new file mode 100644 index 000000000..3cbeadb8b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/SharedPromise-inl.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +template <class T> +size_t SharedPromise<T>::size() const { + std::lock_guard<std::mutex> g(mutex_); + return size_.value; +} + +template <class T> +SemiFuture<T> SharedPromise<T>::getSemiFuture() const { + std::lock_guard<std::mutex> g(mutex_); + size_.value++; + if (hasResult()) { + return makeFuture<T>(Try<T>(try_.value)); + } else { + promises_.emplace_back(); + if (interruptHandler_) { + promises_.back().setInterruptHandler(interruptHandler_); + } + return promises_.back().getSemiFuture(); + } +} + +template <class T> +Future<T> SharedPromise<T>::getFuture() const { + return getSemiFuture().via(&InlineExecutor::instance()); +} + +template <class T> +template <class E> +typename std::enable_if<std::is_base_of<std::exception, E>::value>::type +SharedPromise<T>::setException(E const& e) { + setTry(Try<T>(e)); +} + +template <class T> +void SharedPromise<T>::setException(exception_wrapper ew) { + setTry(Try<T>(std::move(ew))); +} + +template <class T> +void SharedPromise<T>::setInterruptHandler( + std::function<void(exception_wrapper const&)> fn) { + std::lock_guard<std::mutex> g(mutex_); + if (hasResult()) { + return; + } + interruptHandler_ = fn; + for (auto& p : promises_) { + p.setInterruptHandler(fn); + } +} + +template <class T> +template <class M> +void SharedPromise<T>::setValue(M&& v) { + setTry(Try<T>(std::forward<M>(v))); +} + +template <class T> +template <class F> +void SharedPromise<T>::setWith(F&& func) { + setTry(makeTryWith(std::forward<F>(func))); +} + +template <class T> +void SharedPromise<T>::setTry(Try<T>&& t) { + std::vector<Promise<T>> promises; + + { + std::lock_guard<std::mutex> g(mutex_); + if (hasResult()) { + throw_exception<PromiseAlreadySatisfied>(); + } + try_.value = std::move(t); + promises.swap(promises_); + } + + for (auto& p : promises) { + p.setTry(Try<T>(try_.value)); + } +} + +template <class T> +bool SharedPromise<T>::isFulfilled() const { + std::lock_guard<std::mutex> g(mutex_); + return hasResult(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/SharedPromise.h b/ios/Pods/Flipper-Folly/folly/futures/SharedPromise.h new file mode 100644 index 000000000..bf65124d9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/SharedPromise.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/executors/InlineExecutor.h> +#include <folly/futures/Promise.h> +#include <folly/lang/Exception.h> + +namespace folly { + +/* + * SharedPromise provides the same interface as Promise, but you can extract + * multiple Futures from it, i.e. you can call getFuture() as many times as + * you'd like. When the SharedPromise is fulfilled, all of the Futures are + * completed. Calls to getFuture() after the SharedPromise is fulfilled return + * a completed Future. If you find yourself constructing collections of Promises + * and fulfilling them simultaneously with the same value, consider this + * utility instead. Likewise, if you find yourself in need of setting multiple + * callbacks on the same Future (which is indefinitely unsupported), consider + * refactoring to use SharedPromise to "split" the Future. + * + * The SharedPromise must be kept alive manually. Consider FutureSplitter for + * automatic lifetime management. + */ +template <class T> +class SharedPromise { + public: + /** + * Return a Future tied to the shared core state. Unlike Promise::getFuture, + * this can be called an unlimited number of times per SharedPromise. + */ + SemiFuture<T> getSemiFuture() const; + + /** + * Return a Future tied to the shared core state. Unlike Promise::getFuture, + * this can be called an unlimited number of times per SharedPromise. + * NOTE: This function is deprecated. Please use getSemiFuture and pass the + * appropriate executor to .via on the returned SemiFuture to get a + * valid Future where necessary. + */ + Future<T> getFuture() const; + + /** Return the number of Futures associated with this SharedPromise */ + size_t size() const; + + /** Fulfill the SharedPromise with an exception_wrapper */ + void setException(exception_wrapper ew); + + /** Fulfill the SharedPromise with an exception type E, which can be passed to + make_exception_wrapper(). Useful for originating exceptions. If you + caught an exception the exception_wrapper form is more appropriate. + */ + template <class E> + typename std::enable_if<std::is_base_of<std::exception, E>::value>::type + setException(E const&); + + /// Set an interrupt handler to handle interrupts. See the documentation for + /// Future::raise(). Your handler can do whatever it wants, but if you + /// bother to set one then you probably will want to fulfill the SharedPromise + /// with an exception (or special value) indicating how the interrupt was + /// handled. + void setInterruptHandler(std::function<void(exception_wrapper const&)>); + + /// Sugar to fulfill this SharedPromise<Unit> + template <class B = T> + typename std::enable_if<std::is_same<Unit, B>::value, void>::type setValue() { + setTry(Try<T>(T())); + } + + /** Set the value (use perfect forwarding for both move and copy) */ + template <class M> + void setValue(M&& value); + + void setTry(Try<T>&& t); + + /** Fulfill this SharedPromise with the result of a function that takes no + arguments and returns something implicitly convertible to T. + Captures exceptions. e.g. + + p.setWith([] { do something that may throw; return a T; }); + */ + template <class F> + void setWith(F&& func); + + bool isFulfilled() const; + + private: + // this allows SharedPromise move-ctor/move-assign to be defaulted + struct Mutex : std::mutex { + Mutex() = default; + Mutex(Mutex&&) noexcept {} + Mutex& operator=(Mutex&&) noexcept { + return *this; + } + }; + + template <typename V> + struct Defaulted { + using Noexcept = StrictConjunction< + std::is_nothrow_default_constructible<V>, + std::is_nothrow_move_constructible<V>, + std::is_nothrow_move_assignable<V>>; + V value{V()}; + Defaulted() = default; + Defaulted(Defaulted&& that) noexcept(Noexcept::value) + : value(std::exchange(that.value, V())) {} + Defaulted& operator=(Defaulted&& that) noexcept(Noexcept::value) { + value = std::exchange(that.value, V()); + return *this; + } + }; + + bool hasResult() const { + return try_.value.hasValue() || try_.value.hasException(); + } + + mutable Mutex mutex_; + mutable Defaulted<size_t> size_; + Defaulted<Try<T>> try_; + mutable std::vector<Promise<T>> promises_; + std::function<void(exception_wrapper const&)> interruptHandler_; +}; + +} // namespace folly + +#include <folly/futures/Future.h> +#include <folly/futures/SharedPromise-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.cpp b/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.cpp new file mode 100644 index 000000000..144c3a9e2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/futures/ThreadWheelTimekeeper.h> +#include <folly/futures/WTCallback.h> + +#include <folly/Chrono.h> +#include <folly/Singleton.h> +#include <folly/futures/Future.h> +#include <future> + +namespace folly { + +namespace { +Singleton<ThreadWheelTimekeeper> timekeeperSingleton_; +} + +ThreadWheelTimekeeper::ThreadWheelTimekeeper() + : thread_([this] { eventBase_.loopForever(); }), + wheelTimer_( + HHWheelTimer::newTimer(&eventBase_, std::chrono::milliseconds(1))) { + eventBase_.waitUntilRunning(); + eventBase_.runInEventBaseThread([this] { + // 15 characters max + eventBase_.setName("FutureTimekeepr"); + }); +} + +ThreadWheelTimekeeper::~ThreadWheelTimekeeper() { + eventBase_.runInEventBaseThreadAndWait([this] { + wheelTimer_->cancelAll(); + eventBase_.terminateLoopSoon(); + }); + thread_.join(); +} + +SemiFuture<Unit> ThreadWheelTimekeeper::after(HighResDuration dur) { + auto cob = WTCallback<HHWheelTimer>::create(&eventBase_); + auto f = cob->getSemiFuture(); + // + // Even shared_ptr of cob is captured in lambda this is still somewhat *racy* + // because it will be released once timeout is scheduled. So technically there + // is no gurantee that EventBase thread can safely call timeout callback. + // However due to fact that we are having circular reference here: + // WTCallback->Promise->Core->WTCallbak, so three of them won't go away until + // we break the circular reference. The break happens either in + // WTCallback::timeoutExpired or WTCallback::interruptHandler. Former means + // timeout callback is being safely executed. Latter captures shared_ptr of + // WTCallback again in another lambda for canceling timeout. The moment + // canceling timeout is executed in EventBase thread, the actual timeout + // callback has either been executed, or will never be executed. So we are + // fine here. + // + eventBase_.runInEventBaseThread([this, cob, dur] { + wheelTimer_->scheduleTimeout(cob.get(), folly::chrono::ceil<Duration>(dur)); + }); + return f; +} + +namespace detail { + +std::shared_ptr<Timekeeper> getTimekeeperSingleton() { + return timekeeperSingleton_.try_get(); +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h b/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h new file mode 100644 index 000000000..3057cd33d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/futures/Future.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/HHWheelTimer.h> +#include <thread> + +namespace folly { + +/// The default Timekeeper implementation which uses a HHWheelTimer on an +/// EventBase in a dedicated thread. Users needn't deal with this directly, it +/// is used by default by Future methods that work with timeouts. +class ThreadWheelTimekeeper : public Timekeeper { + public: + /// But it doesn't *have* to be a singleton. + ThreadWheelTimekeeper(); + ~ThreadWheelTimekeeper() override; + + /// Implement the Timekeeper interface + SemiFuture<Unit> after(HighResDuration) override; + + protected: + folly::EventBase eventBase_; + std::thread thread_; + HHWheelTimer::UniquePtr wheelTimer_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/WTCallback.h b/ios/Pods/Flipper-Folly/folly/futures/WTCallback.h new file mode 100644 index 000000000..8841a73ef --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/WTCallback.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Chrono.h> +#include <folly/futures/Future.h> +#include <folly/io/async/HHWheelTimer.h> +#include <future> + +namespace folly { +// Our Callback object for HHWheelTimer +template <class TBase> +struct WTCallback : public std::enable_shared_from_this<WTCallback<TBase>>, + public TBase::Callback { + struct PrivateConstructorTag {}; + + public: + WTCallback(PrivateConstructorTag, EventBase* base) : base_(base) {} + + // Only allow creation by this factory, to ensure heap allocation. + static std::shared_ptr<WTCallback> create(EventBase* base) { + // optimization opportunity: memory pool + auto cob = std::make_shared<WTCallback>(PrivateConstructorTag{}, base); + // Capture shared_ptr of cob in lambda so that Core inside Promise will + // hold a ref count to it. The ref count will be released when Core goes + // away which happens when both Promise and Future go away + cob->promise_.setInterruptHandler( + [cob](exception_wrapper ew) { cob->interruptHandler(std::move(ew)); }); + return cob; + } + + SemiFuture<Unit> getSemiFuture() { + return promise_.getSemiFuture(); + } + + FOLLY_NODISCARD Promise<Unit> stealPromise() { + // Don't need promise anymore. Break the circular reference as promise_ + // is holding a ref count to us via Core. Core won't go away until both + // Promise and Future go away. + return std::move(promise_); + } + + protected: + folly::Synchronized<EventBase*> base_; + Promise<Unit> promise_; + + void timeoutExpired() noexcept override { + base_ = nullptr; + // Don't need Promise anymore, break the circular reference + auto promise = stealPromise(); + if (!promise.isFulfilled()) { + promise.setValue(); + } + } + + void callbackCanceled() noexcept override { + base_ = nullptr; + // Don't need Promise anymore, break the circular reference + auto promise = stealPromise(); + if (!promise.isFulfilled()) { + promise.setException(FutureNoTimekeeper{}); + } + } + + void interruptHandler(exception_wrapper ew) { + auto rBase = base_.rlock(); + if (!*rBase) { + return; + } + // Capture shared_ptr of self in lambda, if we don't do this, object + // may go away before the lambda is executed from event base thread. + // This is not racing with timeoutExpired anymore because this is called + // through Future, which means Core is still alive and keeping a ref count + // on us, so what timeouExpired is doing won't make the object go away + (*rBase)->runInEventBaseThread([me = std::enable_shared_from_this< + WTCallback<TBase>>::shared_from_this(), + ew = std::move(ew)]() mutable { + me->cancelTimeout(); + // Don't need Promise anymore, break the circular reference + auto promise = me->stealPromise(); + if (!promise.isFulfilled()) { + promise.setException(std::move(ew)); + } + }); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/detail/Core.h b/ios/Pods/Flipper-Folly/folly/futures/detail/Core.h new file mode 100644 index 000000000..823162146 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/detail/Core.h @@ -0,0 +1,1033 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <mutex> +#include <stdexcept> +#include <utility> +#include <vector> + +#include <boost/variant.hpp> + +#include <folly/Executor.h> +#include <folly/Function.h> +#include <folly/Optional.h> +#include <folly/ScopeGuard.h> +#include <folly/Try.h> +#include <folly/Utility.h> +#include <folly/futures/detail/Types.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Exception.h> +#include <folly/synchronization/AtomicUtil.h> +#include <folly/synchronization/MicroSpinLock.h> +#include <glog/logging.h> + +#include <folly/io/async/Request.h> + +namespace folly { +namespace futures { +namespace detail { + +/// See `Core` for details +enum class State : uint8_t { + Start = 1 << 0, + OnlyResult = 1 << 1, + OnlyCallback = 1 << 2, + OnlyCallbackAllowInline = 1 << 3, + Proxy = 1 << 4, + Done = 1 << 5, + Empty = 1 << 6, +}; +constexpr State operator&(State a, State b) { + return State(uint8_t(a) & uint8_t(b)); +} +constexpr State operator|(State a, State b) { + return State(uint8_t(a) | uint8_t(b)); +} +constexpr State operator^(State a, State b) { + return State(uint8_t(a) ^ uint8_t(b)); +} +constexpr State operator~(State a) { + return State(~uint8_t(a)); +} + +/// SpinLock is and must stay a 1-byte object because of how Core is laid out. +struct SpinLock : private MicroSpinLock { + SpinLock() : MicroSpinLock{0} {} + + using MicroSpinLock::lock; + using MicroSpinLock::unlock; +}; +static_assert(sizeof(SpinLock) == 1, "missized"); + +class DeferredExecutor; + +class UniqueDeleter { + public: + void operator()(DeferredExecutor* ptr); +}; + +using DeferredWrapper = std::unique_ptr<DeferredExecutor, UniqueDeleter>; + +/** + * Wrapper type that represents either a KeepAlive or a DeferredExecutor. + * Acts as if a type-safe tagged union of the two using knowledge that the two + * can safely be distinguished. + */ +class KeepAliveOrDeferred { + public: + KeepAliveOrDeferred(Executor::KeepAlive<> ka) : storage_{std::move(ka)} { + DCHECK(!isDeferred()); + } + + KeepAliveOrDeferred(DeferredWrapper deferred) + : storage_{std::move(deferred)} {} + + KeepAliveOrDeferred() {} + + ~KeepAliveOrDeferred() {} + + KeepAliveOrDeferred(KeepAliveOrDeferred&& other) + : storage_{std::move(other.storage_)} {} + + KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) { + storage_ = std::move(other.storage_); + return *this; + } + + DeferredExecutor* getDeferredExecutor() const { + if (!isDeferred()) { + return nullptr; + } + return asDeferred().get(); + } + + Executor* getKeepAliveExecutor() const { + if (isDeferred()) { + return nullptr; + } + return asKeepAlive().get(); + } + + Executor::KeepAlive<> stealKeepAlive() && { + if (isDeferred()) { + return Executor::KeepAlive<>{}; + } + return std::move(asKeepAlive()); + } + + std::unique_ptr<DeferredExecutor, UniqueDeleter> stealDeferred() && { + if (!isDeferred()) { + return std::unique_ptr<DeferredExecutor, UniqueDeleter>{}; + } + return std::move(asDeferred()); + } + + bool isDeferred() const { + return boost::get<DeferredWrapper>(&storage_) != nullptr; + } + + bool isKeepAlive() const { + return !isDeferred(); + } + + KeepAliveOrDeferred copy() const; + + explicit operator bool() const { + return getDeferredExecutor() || getKeepAliveExecutor(); + } + + private: + boost::variant<DeferredWrapper, Executor::KeepAlive<>> storage_; + + friend class DeferredExecutor; + + Executor::KeepAlive<>& asKeepAlive() { + return boost::get<Executor::KeepAlive<>>(storage_); + } + + const Executor::KeepAlive<>& asKeepAlive() const { + return boost::get<Executor::KeepAlive<>>(storage_); + } + + DeferredWrapper& asDeferred() { + return boost::get<DeferredWrapper>(storage_); + } + + const DeferredWrapper& asDeferred() const { + return boost::get<DeferredWrapper>(storage_); + } +}; + +/** + * Defer work until executor is actively boosted. + */ +class DeferredExecutor final { + public: + // addFrom will: + // * run func inline if there is a stored executor and completingKA matches + // the stored executor + // * enqueue func into the stored executor if one exists + // * store func until an executor is set otherwise + void addFrom( + Executor::KeepAlive<>&& completingKA, + Executor::KeepAlive<>::KeepAliveFunc func) { + auto state = state_.load(std::memory_order_acquire); + if (state == State::DETACHED) { + return; + } + + // If we are completing on the current executor, call inline, otherwise + // add + auto addWithInline = + [&](Executor::KeepAlive<>::KeepAliveFunc&& addFunc) mutable { + if (completingKA.get() == executor_.get()) { + addFunc(std::move(completingKA)); + } else { + executor_.copy().add(std::move(addFunc)); + } + }; + + if (state == State::HAS_EXECUTOR) { + addWithInline(std::move(func)); + return; + } + DCHECK(state == State::EMPTY); + func_ = std::move(func); + if (folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + State::HAS_FUNCTION, + std::memory_order_release, + std::memory_order_acquire)) { + return; + } + DCHECK(state == State::DETACHED || state == State::HAS_EXECUTOR); + if (state == State::DETACHED) { + std::exchange(func_, nullptr); + return; + } + addWithInline(std::exchange(func_, nullptr)); + } + + Executor* getExecutor() const { + assert(executor_.get()); + return executor_.get(); + } + + void setExecutor(folly::Executor::KeepAlive<> executor) { + if (nestedExecutors_) { + auto nestedExecutors = std::exchange(nestedExecutors_, nullptr); + for (auto& nestedExecutor : *nestedExecutors) { + assert(nestedExecutor.get()); + nestedExecutor.get()->setExecutor(executor.copy()); + } + } + executor_ = std::move(executor); + auto state = state_.load(std::memory_order_acquire); + if (state == State::EMPTY && + folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + State::HAS_EXECUTOR, + std::memory_order_release, + std::memory_order_acquire)) { + return; + } + + DCHECK(state == State::HAS_FUNCTION); + state_.store(State::HAS_EXECUTOR, std::memory_order_release); + executor_.copy().add(std::exchange(func_, nullptr)); + } + + void setNestedExecutors(std::vector<DeferredWrapper> executors) { + DCHECK(!nestedExecutors_); + nestedExecutors_ = + std::make_unique<std::vector<DeferredWrapper>>(std::move(executors)); + } + + void detach() { + if (nestedExecutors_) { + auto nestedExecutors = std::exchange(nestedExecutors_, nullptr); + for (auto& nestedExecutor : *nestedExecutors) { + assert(nestedExecutor.get()); + nestedExecutor.get()->detach(); + } + } + auto state = state_.load(std::memory_order_acquire); + if (state == State::EMPTY && + folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + State::DETACHED, + std::memory_order_release, + std::memory_order_acquire)) { + return; + } + + DCHECK(state == State::HAS_FUNCTION); + state_.store(State::DETACHED, std::memory_order_release); + std::exchange(func_, nullptr); + } + + DeferredWrapper copy() { + acquire(); + return DeferredWrapper(this); + } + + static DeferredWrapper create() { + return DeferredWrapper(new DeferredExecutor{}); + } + + private: + DeferredExecutor() {} + friend class UniqueDeleter; + + bool acquire() { + auto keepAliveCount = + keepAliveCount_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCount > 0); + return true; + } + + void release() { + auto keepAliveCount = + keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel); + DCHECK(keepAliveCount > 0); + if (keepAliveCount == 1) { + delete this; + } + } + + enum class State { EMPTY, HAS_FUNCTION, HAS_EXECUTOR, DETACHED }; + std::atomic<State> state_{State::EMPTY}; + Executor::KeepAlive<>::KeepAliveFunc func_; + folly::Executor::KeepAlive<> executor_; + std::unique_ptr<std::vector<DeferredWrapper>> nestedExecutors_; + std::atomic<ssize_t> keepAliveCount_{1}; +}; + +inline void UniqueDeleter::operator()(DeferredExecutor* ptr) { + if (ptr) { + ptr->release(); + } +} + +inline KeepAliveOrDeferred KeepAliveOrDeferred::copy() const { + if (isDeferred()) { + if (auto def = getDeferredExecutor()) { + return KeepAliveOrDeferred{def->copy()}; + } else { + return KeepAliveOrDeferred{}; + } + } else { + return KeepAliveOrDeferred{asKeepAlive()}; + } +} + +/// The shared state object for Future and Promise. +/// +/// Nomenclature: +/// +/// - "result": a `Try` object which, when set, contains a `T` or exception. +/// - "move-out the result": used to mean the `Try` object and/or its contents +/// are moved-out by a move-constructor or move-assignment. After the result +/// is set, Core itself never modifies (including moving out) the result; +/// however the docs refer to both since caller-code can move-out the result +/// implicitly (see below for examples) whereas other types of modifications +/// are more explicit in the caller code. +/// - "callback": a function provided by the future which Core may invoke. The +/// thread in which the callback is invoked depends on the executor; if there +/// is no executor or an inline executor the thread-choice depends on timing. +/// - "executor": an object which may in the future invoke a provided function +/// (some executors may, as a matter of policy, simply destroy provided +/// functions without executing them). +/// - "consumer thread": the thread which currently owns the Future and which +/// may provide the callback and/or the interrupt. +/// - "producer thread": the thread which owns the Future and which may provide +/// the result and which may provide the interrupt handler. +/// - "interrupt": if provided, an object managed by (if non-empty) +/// `exception_wrapper`. +/// - "interrupt handler": if provided, a function-object passed to +/// `promise.setInterruptHandler()`. Core invokes the interrupt handler with +/// the interrupt when both are provided (and, best effort, if there is not +/// yet any result). +/// +/// Core holds three sets of data, each of which is concurrency-controlled: +/// +/// - The primary producer-to-consumer info-flow: this info includes the result, +/// callback, executor, and a priority for running the callback. Management of +/// and concurrency control for this info is by an FSM based on `enum class +/// State`. All state transitions are atomic; other producer-to-consumer data +/// is sometimes modified within those transitions; see below for details. +/// - The consumer-to-producer interrupt-request flow: this info includes an +/// interrupt-handler and an interrupt. Concurrency of this info is controlled +/// by a Spin Lock (`interruptLock_`). +/// - Lifetime control info: this includes two reference counts, both which are +/// internally synchronized (atomic). +/// +/// The FSM to manage the primary producer-to-consumer info-flow has these +/// allowed (atomic) transitions: +/// +/// +----------------------------------------------------------------+ +/// | ---> OnlyResult ----- | +/// | / \ | +/// | (setResult()) (setCallback()) | +/// | / \ | +/// | Start ---------> ------> Done | +/// | \ \ / | +/// | \ (setCallback()) (setResult()) | +/// | \ \ / | +/// | \ ---> OnlyCallback --- | +/// | \ or OnlyCallbackAllowInline | +/// | \ \ | +/// | (setProxy()) (setProxy()) | +/// | \ \ | +/// | \ ------> Empty | +/// | \ / | +/// | \ (setCallback()) | +/// | \ / | +/// | ---------> Proxy ---------- | +/// +----------------------------------------------------------------+ +/// +/// States and the corresponding producer-to-consumer data status & ownership: +/// +/// - Start: has neither result nor callback. While in this state, the producer +/// thread may set the result (`setResult()`) or the consumer thread may set +/// the callback (`setCallback()`). +/// - OnlyResult: producer thread has set the result and must never access it. +/// The result is logically owned by, and possibly modified or moved-out by, +/// the consumer thread. Callers of the future object can do arbitrary +/// modifications, including moving-out, via continuations or via non-const +/// and/or rvalue-qualified `future.result()`, `future.value()`, etc. +/// Future/SemiFuture proper also move-out the result in some cases, e.g., +/// in `wait()`, `get()`, when passing through values or results from core to +/// core, as `then-value` and `then-error`, etc. +/// - OnlyCallback: consumer thread has set a callback/continuation. From this +/// point forward only the producer thread can safely access that callback +/// (see `setResult()` and `doCallback()` where the producer thread can both +/// read and modify the callback). +/// - OnlyCallbackAllowInline: as for OnlyCallback but the core is allowed to +/// run the callback inline with the setResult call, and therefore in the +/// execution context and on the executor that executed the callback on the +/// previous core, rather than adding the callback to the current Core's +/// executor. This will only happen if the executor on which the previous +/// callback is executing, and on which it is calling setResult, is the same +/// as the executor the current core would add the callback to. +/// - Proxy: producer thread has set a proxy core which the callback should be +/// proxied to. +/// - Done: callback can be safely accessed only within `doCallback()`, which +/// gets called on exactly one thread exactly once just after the transition +/// to Done. The future object will have determined whether that callback +/// has/will move-out the result, but either way the result remains logically +/// owned exclusively by the consumer thread (the code of Future/SemiFuture, +/// of the continuation, and/or of callers of `future.result()`, etc.). +/// - Empty: the core successfully proxied the callback and is now empty. +/// +/// Start state: +/// +/// - Start: e.g., `Core<X>::make()`. +/// - (See also `Core<X>::make(x)` which logically transitions Start => +/// OnlyResult within the underlying constructor.) +/// +/// Terminal states: +/// +/// - OnlyResult: a terminal state when a callback is never attached, and also +/// sometimes when a callback is provided, e.g., sometimes when +/// `future.wait()` and/or `future.get()` are used. +/// - Done: a terminal state when `future.then()` is used, and sometimes also +/// when `future.wait()` and/or `future.get()` are used. +/// - Proxy: a terminal state if proxy core was set, but callback was never set. +/// - Empty: a terminal state when proxying a callback was successful. +/// +/// Notes and caveats: +/// +/// - Unfortunately, there are things that users can do to break concurrency and +/// we can't detect that. However users should be ok if they follow move +/// semantics religiously wrt threading. +/// - Futures and/or Promises can and usually will migrate between threads, +/// though this usually happens within the API code. For example, an async +/// operation will probably make a promise-future pair (see overloads of +/// `makePromiseContract()`), then move the Promise into another thread that +/// will eventually fulfill it. +/// - Things get slightly more complicated with executors and via, but the +/// principle is the same. +/// - In general, as long as the user doesn't access a future or promise object +/// from more than one thread at a time there won't be any problems. +template <typename T> +class Core final { + static_assert( + !std::is_void<T>::value, + "void futures are not supported. Use Unit instead."); + + public: + using Result = Try<T>; + using Callback = folly::Function<void(Executor::KeepAlive<>&&, Result&&)>; + + /// State will be Start + static Core* make() { + return new Core(); + } + + /// State will be OnlyResult + /// Result held will be move-constructed from `t` + static Core* make(Try<T>&& t) { + return new Core(std::move(t)); + } + + /// State will be OnlyResult + /// Result held will be the `T` constructed from forwarded `args` + template <typename... Args> + static Core<T>* make(in_place_t, Args&&... args) { + return new Core<T>(in_place, std::forward<Args>(args)...); + } + + // not copyable + Core(Core const&) = delete; + Core& operator=(Core const&) = delete; + + // not movable (see comment in the implementation of Future::then) + Core(Core&&) noexcept = delete; + Core& operator=(Core&&) = delete; + + /// May call from any thread + bool hasCallback() const noexcept { + constexpr auto allowed = + State::OnlyCallback | State::OnlyCallbackAllowInline | State::Done; + auto const state = state_.load(std::memory_order_acquire); + return State() != (state & allowed); + } + + /// May call from any thread + /// + /// True if state is OnlyResult or Done. + /// + /// Identical to `this->ready()` + bool hasResult() const noexcept { + constexpr auto allowed = State::OnlyResult | State::Done; + auto core = this; + auto state = core->state_.load(std::memory_order_acquire); + while (state == State::Proxy) { + core = core->proxy_; + state = core->state_.load(std::memory_order_acquire); + } + return State() != (state & allowed); + } + + /// May call from any thread + /// + /// True if state is OnlyResult or Done. + /// + /// Identical to `this->hasResult()` + bool ready() const noexcept { + return hasResult(); + } + + /// Call only from consumer thread (since the consumer thread can modify the + /// referenced Try object; see non-const overloads of `future.result()`, + /// etc., and certain Future-provided callbacks which move-out the result). + /// + /// Unconditionally returns a reference to the result. + /// + /// State dependent preconditions: + /// + /// - Start, OnlyCallback or OnlyCallbackAllowInline: Never safe - do not + /// call. (Access in those states + /// would be undefined behavior since the producer thread can, in those + /// states, asynchronously set the referenced Try object.) + /// - OnlyResult: Always safe. (Though the consumer thread should not use the + /// returned reference after it attaches a callback unless it knows that + /// the callback does not move-out the referenced result.) + /// - Done: Safe but sometimes unusable. (Always returns a valid reference, + /// but the referenced result may or may not have been modified, including + /// possibly moved-out, depending on what the callback did; some but not + /// all callbacks modify (possibly move-out) the result.) + Try<T>& getTry() { + DCHECK(hasResult()); + auto core = this; + while (core->state_.load(std::memory_order_relaxed) == State::Proxy) { + core = core->proxy_; + } + return core->result_; + } + Try<T> const& getTry() const { + DCHECK(hasResult()); + auto core = this; + while (core->state_.load(std::memory_order_relaxed) == State::Proxy) { + core = core->proxy_; + } + return core->result_; + } + + /// Call only from consumer thread. + /// Call only once - else undefined behavior. + /// + /// See FSM graph for allowed transitions. + /// + /// If it transitions to Done, synchronously initiates a call to the callback, + /// and might also synchronously execute that callback (e.g., if there is no + /// executor or if the executor is inline). + void setCallback( + Callback&& func, + std::shared_ptr<folly::RequestContext>&& context, + futures::detail::InlineContinuation allowInline) { + DCHECK(!hasCallback()); + + ::new (&callback_) Callback(std::move(func)); + ::new (&context_) Context(std::move(context)); + + auto state = state_.load(std::memory_order_acquire); + State nextState = allowInline == futures::detail::InlineContinuation::permit + ? State::OnlyCallbackAllowInline + : State::OnlyCallback; + + if (state == State::Start) { + if (folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + nextState, + std::memory_order_release, + std::memory_order_acquire)) { + return; + } + assume(state == State::OnlyResult || state == State::Proxy); + } + + if (state == State::OnlyResult) { + state_.store(State::Done, std::memory_order_relaxed); + doCallback(Executor::KeepAlive<>{}, state); + return; + } + + if (state == State::Proxy) { + return proxyCallback(state); + } + + terminate_with<std::logic_error>("setCallback unexpected state"); + } + + /// Call only from producer thread. + /// Call only once - else undefined behavior. + /// + /// See FSM graph for allowed transitions. + /// + /// This can not be called concurrently with setResult(). + void setProxy(Core* proxy) { + DCHECK(!hasResult()); + + proxy_ = proxy; + + auto state = state_.load(std::memory_order_acquire); + switch (state) { + case State::Start: + if (folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + State::Proxy, + std::memory_order_release, + std::memory_order_acquire)) { + break; + } + assume( + state == State::OnlyCallback || + state == State::OnlyCallbackAllowInline); + FOLLY_FALLTHROUGH; + + case State::OnlyCallback: + case State::OnlyCallbackAllowInline: + proxyCallback(state); + break; + case State::OnlyResult: + case State::Proxy: + case State::Done: + case State::Empty: + default: + terminate_with<std::logic_error>("setCallback unexpected state"); + } + + detachOne(); + } + + /// Call only from producer thread. + /// Call only once - else undefined behavior. + /// + /// See FSM graph for allowed transitions. + /// + /// If it transitions to Done, synchronously initiates a call to the callback, + /// and might also synchronously execute that callback (e.g., if there is no + /// executor or if the executor is inline). + void setResult(Try<T>&& t) { + setResult(Executor::KeepAlive<>{}, std::move(t)); + } + + /// Call only from producer thread. + /// Call only once - else undefined behavior. + /// + /// See FSM graph for allowed transitions. + /// + /// If it transitions to Done, synchronously initiates a call to the callback, + /// and might also synchronously execute that callback (e.g., if there is no + /// executor, if the executor is inline or if completingKA represents the + /// same executor as does executor_). + void setResult(Executor::KeepAlive<>&& completingKA, Try<T>&& t) { + DCHECK(!hasResult()); + + ::new (&result_) Result(std::move(t)); + + auto state = state_.load(std::memory_order_acquire); + switch (state) { + case State::Start: + if (folly::atomic_compare_exchange_strong_explicit( + &state_, + &state, + State::OnlyResult, + std::memory_order_release, + std::memory_order_acquire)) { + return; + } + assume( + state == State::OnlyCallback || + state == State::OnlyCallbackAllowInline); + FOLLY_FALLTHROUGH; + + case State::OnlyCallback: + case State::OnlyCallbackAllowInline: + state_.store(State::Done, std::memory_order_relaxed); + doCallback(std::move(completingKA), state); + return; + case State::OnlyResult: + case State::Proxy: + case State::Done: + case State::Empty: + default: + terminate_with<std::logic_error>("setResult unexpected state"); + } + } + + /// Called by a destructing Future (in the consumer thread, by definition). + /// Calls `delete this` if there are no more references to `this` + /// (including if `detachPromise()` is called previously or concurrently). + void detachFuture() noexcept { + detachOne(); + } + + /// Called by a destructing Promise (in the producer thread, by definition). + /// Calls `delete this` if there are no more references to `this` + /// (including if `detachFuture()` is called previously or concurrently). + void detachPromise() noexcept { + DCHECK(hasResult()); + detachOne(); + } + + /// Call only from consumer thread, either before attaching a callback or + /// after the callback has already been invoked, but not concurrently with + /// anything which might trigger invocation of the callback. + void setExecutor(KeepAliveOrDeferred&& x) { + DCHECK( + state_ != State::OnlyCallback && + state_ != State::OnlyCallbackAllowInline); + executor_ = std::move(x); + } + + Executor* getExecutor() const { + if (!executor_.isKeepAlive()) { + return nullptr; + } + return executor_.getKeepAliveExecutor(); + } + + DeferredExecutor* getDeferredExecutor() const { + if (!executor_.isDeferred()) { + return {}; + } + + return executor_.getDeferredExecutor(); + } + + DeferredWrapper stealDeferredExecutor() { + if (executor_.isKeepAlive()) { + return {}; + } + + return std::move(executor_).stealDeferred(); + } + + /// Call only from consumer thread + /// + /// Eventual effect is to pass `e` to the Promise's interrupt handler, either + /// synchronously within this call or asynchronously within + /// `setInterruptHandler()`, depending on which happens first (a coin-toss if + /// the two calls are racing). + /// + /// Has no effect if it was called previously. + /// Has no effect if State is OnlyResult or Done. + void raise(exception_wrapper e) { + std::lock_guard<SpinLock> lock(interruptLock_); + if (!interrupt_ && !hasResult()) { + interrupt_ = std::make_unique<exception_wrapper>(std::move(e)); + if (interruptHandler_) { + interruptHandler_(*interrupt_); + } + } + } + + std::function<void(exception_wrapper const&)> getInterruptHandler() { + if (!interruptHandlerSet_.load(std::memory_order_acquire)) { + return nullptr; + } + std::lock_guard<SpinLock> lock(interruptLock_); + return interruptHandler_; + } + + /// Call only from producer thread + /// + /// May invoke `fn()` (passing the interrupt) synchronously within this call + /// (if `raise()` preceded or perhaps if `raise()` is called concurrently). + /// + /// Has no effect if State is OnlyResult or Done. + /// + /// Note: `fn()` must not touch resources that are destroyed immediately after + /// `setResult()` is called. Reason: it is possible for `fn()` to get called + /// asynchronously (in the consumer thread) after the producer thread calls + /// `setResult()`. + template <typename F> + void setInterruptHandler(F&& fn) { + std::lock_guard<SpinLock> lock(interruptLock_); + if (!hasResult()) { + if (interrupt_) { + fn(as_const(*interrupt_)); + } else { + setInterruptHandlerNoLock(std::forward<F>(fn)); + } + } + } + + void setInterruptHandlerNoLock( + std::function<void(exception_wrapper const&)> fn) { + interruptHandlerSet_.store(true, std::memory_order_relaxed); + interruptHandler_ = std::move(fn); + } + + private: + Core() : state_(State::Start), attached_(2) {} + + explicit Core(Try<T>&& t) + : result_(std::move(t)), state_(State::OnlyResult), attached_(1) {} + + template <typename... Args> + explicit Core(in_place_t, Args&&... args) noexcept( + std::is_nothrow_constructible<T, Args&&...>::value) + : result_(in_place, std::forward<Args>(args)...), + state_(State::OnlyResult), + attached_(1) {} + + ~Core() { + DCHECK(attached_ == 0); + auto state = state_.load(std::memory_order_relaxed); + switch (state) { + case State::OnlyResult: + FOLLY_FALLTHROUGH; + + case State::Done: + result_.~Result(); + break; + + case State::Proxy: + proxy_->detachFuture(); + break; + + case State::Empty: + break; + + case State::Start: + case State::OnlyCallback: + case State::OnlyCallbackAllowInline: + default: + terminate_with<std::logic_error>("~Core unexpected state"); + } + } + + // Helper class that stores a pointer to the `Core` object and calls + // `derefCallback` and `detachOne` in the destructor. + class CoreAndCallbackReference { + public: + explicit CoreAndCallbackReference(Core* core) noexcept : core_(core) {} + + ~CoreAndCallbackReference() noexcept { + detach(); + } + + CoreAndCallbackReference(CoreAndCallbackReference const& o) = delete; + CoreAndCallbackReference& operator=(CoreAndCallbackReference const& o) = + delete; + CoreAndCallbackReference& operator=(CoreAndCallbackReference&&) = delete; + + CoreAndCallbackReference(CoreAndCallbackReference&& o) noexcept + : core_(std::exchange(o.core_, nullptr)) {} + + Core* getCore() const noexcept { + return core_; + } + + private: + void detach() noexcept { + if (core_) { + core_->derefCallback(); + core_->detachOne(); + } + } + + Core* core_{nullptr}; + }; + + // May be called at most once. + void doCallback(Executor::KeepAlive<>&& completingKA, State priorState) { + DCHECK(state_ == State::Done); + + auto executor = std::exchange(executor_, KeepAliveOrDeferred{}); + + // Customise inline behaviour + // If addCompletingKA is non-null, then we are allowing inline execution + auto doAdd = [](Executor::KeepAlive<>&& addCompletingKA, + KeepAliveOrDeferred&& currentExecutor, + auto&& keepAliveFunc) mutable { + if (auto deferredExecutorPtr = currentExecutor.getDeferredExecutor()) { + deferredExecutorPtr->addFrom( + std::move(addCompletingKA), std::move(keepAliveFunc)); + } else { + // If executors match call inline + auto currentKeepAlive = std::move(currentExecutor).stealKeepAlive(); + if (addCompletingKA.get() == currentKeepAlive.get()) { + keepAliveFunc(std::move(currentKeepAlive)); + } else { + std::move(currentKeepAlive).add(std::move(keepAliveFunc)); + } + } + }; + + if (executor) { + // If we are not allowing inline, clear the completing KA to disallow + if (!(priorState == State::OnlyCallbackAllowInline)) { + completingKA = Executor::KeepAlive<>{}; + } + exception_wrapper ew; + // We need to reset `callback_` after it was executed (which can happen + // through the executor or, if `Executor::add` throws, below). The + // executor might discard the function without executing it (now or + // later), in which case `callback_` also needs to be reset. + // The `Core` has to be kept alive throughout that time, too. Hence we + // increment `attached_` and `callbackReferences_` by two, and construct + // exactly two `CoreAndCallbackReference` objects, which call + // `derefCallback` and `detachOne` in their destructor. One will guard + // this scope, the other one will guard the lambda passed to the executor. + attached_.fetch_add(2, std::memory_order_relaxed); + callbackReferences_.fetch_add(2, std::memory_order_relaxed); + CoreAndCallbackReference guard_local_scope(this); + CoreAndCallbackReference guard_lambda(this); + try { + doAdd( + std::move(completingKA), + std::move(executor), + [core_ref = + std::move(guard_lambda)](Executor::KeepAlive<>&& ka) mutable { + auto cr = std::move(core_ref); + Core* const core = cr.getCore(); + RequestContextScopeGuard rctx(std::move(core->context_)); + core->callback_(std::move(ka), std::move(core->result_)); + }); + } catch (const std::exception& e) { + ew = exception_wrapper(std::current_exception(), e); + } catch (...) { + ew = exception_wrapper(std::current_exception()); + } + if (ew) { + RequestContextScopeGuard rctx(std::move(context_)); + result_ = Try<T>(std::move(ew)); + callback_(Executor::KeepAlive<>{}, std::move(result_)); + } + } else { + attached_.fetch_add(1, std::memory_order_relaxed); + SCOPE_EXIT { + context_.~Context(); + callback_.~Callback(); + detachOne(); + }; + RequestContextScopeGuard rctx(std::move(context_)); + callback_(std::move(completingKA), std::move(result_)); + } + } + + void proxyCallback(State priorState) { + // If the state of the core being proxied had a callback that allows inline + // execution, maintain this information in the proxy + futures::detail::InlineContinuation allowInline = + (priorState == State::OnlyCallbackAllowInline + ? futures::detail::InlineContinuation::permit + : futures::detail::InlineContinuation::forbid); + state_.store(State::Empty, std::memory_order_relaxed); + proxy_->setExecutor(std::move(executor_)); + proxy_->setCallback(std::move(callback_), std::move(context_), allowInline); + proxy_->detachFuture(); + context_.~Context(); + callback_.~Callback(); + } + + void detachOne() noexcept { + auto a = attached_.fetch_sub(1, std::memory_order_acq_rel); + assert(a >= 1); + if (a == 1) { + delete this; + } + } + + void derefCallback() noexcept { + auto c = callbackReferences_.fetch_sub(1, std::memory_order_acq_rel); + assert(c >= 1); + if (c == 1) { + context_.~Context(); + callback_.~Callback(); + } + } + + using Context = std::shared_ptr<RequestContext>; + + union { + Callback callback_; + }; + // place result_ next to increase the likelihood that the value will be + // contained entirely in one cache line + union { + Result result_; + Core* proxy_; + }; + std::atomic<State> state_; + std::atomic<unsigned char> attached_; + std::atomic<unsigned char> callbackReferences_{0}; + std::atomic<bool> interruptHandlerSet_{false}; + SpinLock interruptLock_; + KeepAliveOrDeferred executor_; + union { + Context context_; + }; + std::unique_ptr<exception_wrapper> interrupt_{}; + std::function<void(exception_wrapper const&)> interruptHandler_{nullptr}; +}; +} // namespace detail +} // namespace futures + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/futures/detail/Types.h b/ios/Pods/Flipper-Folly/folly/futures/detail/Types.h new file mode 100644 index 000000000..a2b8d0270 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/futures/detail/Types.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> + +namespace folly { + +/// folly::Duration is an alias for the best resolution we offer/work with. +/// However, it is not intended to be used for client code - you should use a +/// descriptive std::chrono::duration type instead. e.g. do not write this: +/// +/// futures::sleep(Duration(1000))... +/// +/// rather this: +/// +/// futures::sleep(std::chrono::milliseconds(1000)); +/// +/// or this: +/// +/// futures::sleep(std::chrono::seconds(1)); +using Duration = std::chrono::milliseconds; +using HighResDuration = std::chrono::microseconds; + +namespace futures { +namespace detail { +enum class InlineContinuation { permit, forbid }; +} // namespace detail +} // namespace futures +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/Base-inl.h b/ios/Pods/Flipper-Folly/folly/gen/Base-inl.h new file mode 100644 index 000000000..b7922f635 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Base-inl.h @@ -0,0 +1,2733 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_BASE_H_ +#error This file may only be included from folly/gen/Base.h +#endif + +#include <folly/Portability.h> +#include <folly/container/F14Map.h> +#include <folly/container/F14Set.h> +#include <folly/functional/Invoke.h> + +#if FOLLY_USE_RANGEV3 +#include <range/v3/view/filter.hpp> +#include <range/v3/view/transform.hpp> +#endif + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { +namespace gen { + +/** + * ArgumentReference - For determining ideal argument type to receive a value. + */ +template <class T> +struct ArgumentReference : public std::conditional< + std::is_reference<T>::value, + T, // T& -> T&, T&& -> T&&, const T& -> const T& + typename std::conditional< + std::is_const<T>::value, + T&, // const int -> const int& + T&& // int -> int&& + >::type> {}; + +/** + * Group - The output objects from the GroupBy operator + */ +template <class Key, class Value> +class Group : public GenImpl<Value&&, Group<Key, Value>> { + public: + static_assert( + !std::is_reference<Key>::value && !std::is_reference<Value>::value, + "Key and Value must be decayed types"); + + typedef std::vector<Value> VectorType; + typedef Key KeyType; + typedef Value ValueType; + + Group(Key key, VectorType values) + : key_(std::move(key)), values_(std::move(values)) {} + + const Key& key() const { + return key_; + } + + size_t size() const { + return values_.size(); + } + const VectorType& values() const { + return values_; + } + VectorType& values() { + return values_; + } + + VectorType operator|(const detail::Collect<VectorType>&) const { + return values(); + } + + VectorType operator|(const detail::CollectTemplate<std::vector>&) const { + return values(); + } + + template <class Body> + void foreach(Body&& body) const { + for (auto& value : values_) { + body(std::move(value)); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + for (auto& value : values_) { + if (!handler(std::move(value))) { + return false; + } + } + return true; + } + + // GroupBy only takes in finite generators, so we only have finite groups + static constexpr bool infinite = false; + + private: + Key key_; + mutable VectorType values_; +}; + +namespace detail { + +// Classes used for the implementation of Sources, Operators, and Sinks + +/* + ******************************* Sources *************************************** + */ + +/* + * ReferencedSource - Generate values from an STL-like container using + * iterators from .begin() until .end(). Value type defaults to the type of + * *container->begin(). For std::vector<int>, this would be int&. Note that the + * value here is a reference, so the values in the vector will be passed by + * reference to downstream operators. + * + * This type is primarily used through the 'from' helper method, like: + * + * string& longestName = from(names) + * | maxBy([](string& s) { return s.size() }); + */ +template <class Container, class Value> +class ReferencedSource + : public GenImpl<Value, ReferencedSource<Container, Value>> { + Container* container_; + + public: + explicit ReferencedSource(Container* container) : container_(container) {} + + template <class Body> + void foreach(Body&& body) const { + for (auto& value : *container_) { + body(std::forward<Value>(value)); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + for (auto& value : *container_) { + if (!handler(std::forward<Value>(value))) { + return false; + } + } + return true; + } + + // from takes in a normal stl structure, which are all finite + static constexpr bool infinite = false; +}; + +/** + * CopiedSource - For producing values from eagerly from a sequence of values + * whose storage is owned by this class. Useful for preparing a generator for + * use after a source collection will no longer be available, or for when the + * values are specified literally with an initializer list. + * + * This type is primarily used through the 'fromCopy' function, like: + * + * auto sourceCopy = fromCopy(makeAVector()); + * auto sum = sourceCopy | sum; + * auto max = sourceCopy | max; + * + * Though it is also used for the initializer_list specialization of from(). + */ +template <class StorageType, class Container> +class CopiedSource + : public GenImpl<const StorageType&, CopiedSource<StorageType, Container>> { + static_assert( + !std::is_reference<StorageType>::value, + "StorageType must be decayed"); + + public: + // Generator objects are often copied during normal construction as they are + // encapsulated by downstream generators. It would be bad if this caused + // a copy of the entire container each time, and since we're only exposing a + // const reference to the value, it's safe to share it between multiple + // generators. + static_assert( + !std::is_reference<Container>::value, + "Can't copy into a reference"); + std::shared_ptr<const Container> copy_; + + public: + typedef Container ContainerType; + + template <class SourceContainer> + explicit CopiedSource(const SourceContainer& container) + : copy_(new Container(begin(container), end(container))) {} + + explicit CopiedSource(Container&& container) + : copy_(new Container(std::move(container))) {} + + // To enable re-use of cached results. + CopiedSource(const CopiedSource<StorageType, Container>& source) + : copy_(source.copy_) {} + + template <class Body> + void foreach(Body&& body) const { + for (const auto& value : *copy_) { + body(value); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + // The collection may be reused by others, we can't allow it to be changed. + for (const auto& value : *copy_) { + if (!handler(value)) { + return false; + } + } + return true; + } + + // from takes in a normal stl structure, which are all finite + static constexpr bool infinite = false; +}; + +/** + * RangeSource - For producing values from a folly::Range. Useful for referring + * to a slice of some container. + * + * This type is primarily used through the 'from' function, like: + * + * auto rangeSource = from(folly::range(v.begin(), v.end())); + * auto sum = rangeSource | sum; + * + * Reminder: Be careful not to invalidate iterators when using ranges like this. + */ +template <class Iterator> +class RangeSource : public GenImpl< + typename Range<Iterator>::reference, + RangeSource<Iterator>> { + Range<Iterator> range_; + + public: + RangeSource() = default; + explicit RangeSource(Range<Iterator> range) : range_(std::move(range)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + for (auto& value : range_) { + if (!handler(value)) { + return false; + } + } + return true; + } + + template <class Body> + void foreach(Body&& body) const { + for (auto& value : range_) { + body(value); + } + } + + // folly::Range only supports finite ranges + static constexpr bool infinite = false; +}; + +/** + * Sequence - For generating values from beginning value, incremented along the + * way with the ++ and += operators. Iteration may continue indefinitely. + * Value type specified explicitly. + * + * This type is primarily used through the 'seq' and 'range' function, like: + * + * int total = seq(1, 10) | sum; + * auto indexes = range(0, 10); + * auto endless = seq(0); // 0, 1, 2, 3, ... + */ +template <class Value, class SequenceImpl> +class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> { + static_assert( + !std::is_reference<Value>::value && !std::is_const<Value>::value, + "Value mustn't be const or ref."); + Value start_; + SequenceImpl impl_; + + public: + explicit Sequence(Value start, SequenceImpl impl) + : start_(std::move(start)), impl_(std::move(impl)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + for (Value current = start_; impl_.test(current); impl_.step(current)) { + if (!handler(current)) { + return false; + } + } + return true; + } + + template <class Body> + void foreach(Body&& body) const { + for (Value current = start_; impl_.test(current); impl_.step(current)) { + body(current); + } + } + + // Let the implementation say if we are infinite or not + static constexpr bool infinite = SequenceImpl::infinite; +}; + +/** + * Sequence implementations (range, sequence, infinite, with/without step) + **/ +template <class Value> +class RangeImpl { + Value end_; + + public: + explicit RangeImpl(Value end) : end_(std::move(end)) {} + bool test(const Value& current) const { + return current < end_; + } + void step(Value& current) const { + ++current; + } + static constexpr bool infinite = false; +}; + +template <class Value, class Distance> +class RangeWithStepImpl { + Value end_; + Distance step_; + + public: + explicit RangeWithStepImpl(Value end, Distance step) + : end_(std::move(end)), step_(std::move(step)) {} + bool test(const Value& current) const { + return current < end_; + } + void step(Value& current) const { + current += step_; + } + static constexpr bool infinite = false; +}; + +template <class Value> +class SeqImpl { + Value end_; + + public: + explicit SeqImpl(Value end) : end_(std::move(end)) {} + bool test(const Value& current) const { + return current <= end_; + } + void step(Value& current) const { + ++current; + } + static constexpr bool infinite = false; +}; + +template <class Value, class Distance> +class SeqWithStepImpl { + Value end_; + Distance step_; + + public: + explicit SeqWithStepImpl(Value end, Distance step) + : end_(std::move(end)), step_(std::move(step)) {} + bool test(const Value& current) const { + return current <= end_; + } + void step(Value& current) const { + current += step_; + } + static constexpr bool infinite = false; +}; + +template <class Value> +class InfiniteImpl { + public: + bool test(const Value& /* current */) const { + return true; + } + void step(Value& current) const { + ++current; + } + static constexpr bool infinite = true; +}; + +/** + * GenratorBuilder - Helper for GENERTATOR macro. + **/ +template <class Value> +struct GeneratorBuilder { + template <class Source, class Yield = detail::Yield<Value, Source>> + Yield operator+(Source&& source) { + return Yield(std::forward<Source>(source)); + } +}; + +/** + * Yield - For producing values from a user-defined generator by way of a + * 'yield' function. + **/ +template <class Value, class Source> +class Yield : public GenImpl<Value, Yield<Value, Source>> { + Source source_; + + public: + explicit Yield(Source source) : source_(std::move(source)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + struct Break {}; + auto body = [&](Value value) { + if (!handler(std::forward<Value>(value))) { + throw Break(); + } + }; + try { + source_(body); + return true; + } catch (Break&) { + return false; + } + } + + template <class Body> + void foreach(Body&& body) const { + source_(std::forward<Body>(body)); + } +}; + +template <class Value> +class Empty : public GenImpl<Value, Empty<Value>> { + public: + template <class Handler> + bool apply(Handler&&) const { + return true; + } + + template <class Body> + void foreach(Body&&) const {} + + // No values, so finite + static constexpr bool infinite = false; +}; + +template <class Value> +class SingleReference : public GenImpl<Value&, SingleReference<Value>> { + static_assert( + !std::is_reference<Value>::value, + "SingleReference requires non-ref types"); + Value* ptr_; + + public: + explicit SingleReference(Value& ref) : ptr_(&ref) {} + + template <class Handler> + bool apply(Handler&& handler) const { + return handler(*ptr_); + } + + template <class Body> + void foreach(Body&& body) const { + body(*ptr_); + } + + // One value, so finite + static constexpr bool infinite = false; +}; + +template <class Value> +class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> { + static_assert( + !std::is_reference<Value>::value, + "SingleCopy requires non-ref types"); + Value value_; + + public: + explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + return handler(value_); + } + + template <class Body> + void foreach(Body&& body) const { + body(value_); + } + + // One value, so finite + static constexpr bool infinite = false; +}; + +/* + ***************************** Operators *************************************** + */ + +/** + * Map - For producing a sequence of values by passing each value from a source + * collection through a predicate. + * + * This type is usually used through the 'map' or 'mapped' helper function: + * + * auto squares = seq(1, 10) | map(square) | as<std::vector>(); + */ +template <class Predicate> +class Map : public Operator<Map<Predicate>> { + Predicate pred_; + + public: + Map() = default; + + explicit Map(Predicate pred) : pred_(std::move(pred)) {} + + template < + class Value, + class Source, + class Result = + typename ArgumentReference<invoke_result_t<Predicate, Value>>::type> + class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { + Source source_; + Predicate pred_; + + public: + explicit Generator(Source source, const Predicate& pred) + : source_(std::move(source)), pred_(pred) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach( + [&](Value value) { body(pred_(std::forward<Value>(value))); }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) { + return handler(pred_(std::forward<Value>(value))); + }); + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), pred_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), pred_); + } +}; + +/** + * Filter - For filtering values from a source sequence by a predicate. + * + * This type is usually used through the 'filter' helper function, like: + * + * auto nonEmpty = from(strings) + * | filter([](const string& str) -> bool { + * return !str.empty(); + * }); + * + * Note that if no predicate is provided, the values are casted to bool and + * filtered based on that. So if pointers is a vector of pointers, + * + * auto nonNull = from(pointers) | filter(); + * + * will give a vector of all the pointers != nullptr. + */ +template <class Predicate> +class Filter : public Operator<Filter<Predicate>> { + Predicate pred_; + + public: + Filter() = default; + explicit Filter(Predicate pred) : pred_(std::move(pred)) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + Predicate pred_; + + public: + explicit Generator(Source source, const Predicate& pred) + : source_(std::move(source)), pred_(pred) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach([&](Value value) { + // NB: Argument not forwarded to avoid accidental move-construction + if (pred_(value)) { + body(std::forward<Value>(value)); + } + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) -> bool { + // NB: Argument not forwarded to avoid accidental move-construction + if (pred_(value)) { + return handler(std::forward<Value>(value)); + } + return true; + }); + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), pred_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), pred_); + } +}; + +/** + * Until - For producing values from a source until a predicate is satisfied. + * + * This type is usually used through the 'until' helper function, like: + * + * auto best = from(sortedItems) + * | until([](Item& item) { return item.score > 100; }) + * | as<std::vector>(); + */ +template <class Predicate> +class Until : public Operator<Until<Predicate>> { + Predicate pred_; + + public: + Until() = default; + explicit Until(Predicate pred) : pred_(std::move(pred)) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + Predicate pred_; + + public: + explicit Generator(Source source, const Predicate& pred) + : source_(std::move(source)), pred_(pred) {} + + template <class Handler> + bool apply(Handler&& handler) const { + bool cancelled = false; + source_.apply([&](Value value) -> bool { + if (pred_(value)) { // un-forwarded to disable move + return false; + } + if (!handler(std::forward<Value>(value))) { + cancelled = true; + return false; + } + return true; + }); + return !cancelled; + } + + // Theoretically an 'until' might stop an infinite + static constexpr bool infinite = false; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), pred_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), pred_); + } +}; + +/** + * Take - For producing up to N values from a source. + * + * This type is usually used through the 'take' helper function, like: + * + * auto best = from(docs) + * | orderByDescending(scoreDoc) + * | take(10); + */ +class Take : public Operator<Take> { + size_t count_; + + public: + explicit Take(size_t count) : count_(count) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + size_t count_; + + public: + explicit Generator(Source source, size_t count) + : source_(std::move(source)), count_(count) {} + + template <class Handler> + bool apply(Handler&& handler) const { + if (count_ == 0) { + return false; + } + size_t n = count_; + bool cancelled = false; + source_.apply([&](Value value) -> bool { + if (!handler(std::forward<Value>(value))) { + cancelled = true; + return false; + } + return --n; + }); + return !cancelled; + } + + // take will stop an infinite generator + static constexpr bool infinite = false; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), count_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), count_); + } +}; + +/** + * Visit - For calling a function on each item before passing it down the + * pipeline. + * + * This type is usually used through the 'visit' helper function: + * + * auto printedValues = seq(1) | visit(debugPrint); + * // nothing printed yet + * auto results = take(10) | as<std::vector>(); + * // results now populated, 10 values printed + */ +template <class Visitor> +class Visit : public Operator<Visit<Visitor>> { + Visitor visitor_; + + public: + Visit() = default; + + explicit Visit(Visitor visitor) : visitor_(std::move(visitor)) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + Visitor visitor_; + + public: + explicit Generator(Source source, const Visitor& visitor) + : source_(std::move(source)), visitor_(visitor) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach([&](Value value) { + visitor_(value); // not forwarding to avoid accidental moves + body(std::forward<Value>(value)); + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) { + visitor_(value); // not forwarding to avoid accidental moves + return handler(std::forward<Value>(value)); + }); + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), visitor_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), visitor_); + } +}; + +/** + * Stride - For producing every Nth value from a source. + * + * This type is usually used through the 'stride' helper function, like: + * + * auto half = from(samples) + * | stride(2); + */ +class Stride : public Operator<Stride> { + size_t stride_; + + public: + explicit Stride(size_t stride) : stride_(stride) { + if (stride == 0) { + throw std::invalid_argument("stride must not be 0"); + } + } + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + size_t stride_; + + public: + explicit Generator(Source source, size_t stride) + : source_(std::move(source)), stride_(stride) {} + + template <class Handler> + bool apply(Handler&& handler) const { + size_t distance = stride_; + return source_.apply([&](Value value) -> bool { + if (++distance >= stride_) { + if (!handler(std::forward<Value>(value))) { + return false; + } + distance = 0; + } + return true; + }); + } + + template <class Body> + void foreach(Body&& body) const { + size_t distance = stride_; + source_.foreach([&](Value value) { + if (++distance >= stride_) { + body(std::forward<Value>(value)); + distance = 0; + } + }); + } + + // Taking every Nth of an infinite list is still infinte + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), stride_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), stride_); + } +}; + +/** + * Sample - For taking a random sample of N elements from a sequence + * (without replacement). + */ +template <class Random> +class Sample : public Operator<Sample<Random>> { + size_t count_; + Random rng_; + + public: + explicit Sample(size_t count, Random rng) + : count_(count), rng_(std::move(rng)) {} + + template < + class Value, + class Source, + class Rand, + class StorageType = typename std::decay<Value>::type> + class Generator : public GenImpl< + StorageType&&, + Generator<Value, Source, Rand, StorageType>> { + static_assert(!Source::infinite, "Cannot sample infinite source!"); + // It's too easy to bite ourselves if random generator is only 16-bit + static_assert( + Random::max() >= std::numeric_limits<int32_t>::max() - 1, + "Random number generator must support big values"); + Source source_; + size_t count_; + mutable Rand rng_; + + public: + explicit Generator(Source source, size_t count, Random rng) + : source_(std::move(source)), count_(count), rng_(std::move(rng)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + if (count_ == 0) { + return false; + } + std::vector<StorageType> v; + v.reserve(count_); + // use reservoir sampling to give each source value an equal chance + // of appearing in our output. + size_t n = 1; + source_.foreach([&](Value value) -> void { + if (v.size() < count_) { + v.push_back(std::forward<Value>(value)); + } else { + // alternatively, we could create a std::uniform_int_distribution + // instead of using modulus, but benchmarks show this has + // substantial overhead. + size_t index = rng_() % n; + if (index < v.size()) { + v[index] = std::forward<Value>(value); + } + } + ++n; + }); + + // output is unsorted! + for (auto& val : v) { + if (!handler(std::move(val))) { + return false; + } + } + return true; + } + + // Only takes N elements, so finite + static constexpr bool infinite = false; + }; + + template < + class Source, + class Value, + class Gen = Generator<Value, Source, Random>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), count_, rng_); + } + + template < + class Source, + class Value, + class Gen = Generator<Value, Source, Random>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), count_, rng_); + } +}; + +/** + * Skip - For skipping N items from the beginning of a source generator. + * + * This type is usually used through the 'skip' helper function, like: + * + * auto page = from(results) + * | skip(pageSize * startPage) + * | take(10); + */ +class Skip : public Operator<Skip> { + size_t count_; + + public: + explicit Skip(size_t count) : count_(count) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + size_t count_; + + public: + explicit Generator(Source source, size_t count) + : source_(std::move(source)), count_(count) {} + + template <class Body> + void foreach(Body&& body) const { + if (count_ == 0) { + source_.foreach(body); + return; + } + size_t n = 0; + source_.foreach([&](Value value) { + if (n < count_) { + ++n; + } else { + body(std::forward<Value>(value)); + } + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + if (count_ == 0) { + return source_.apply(std::forward<Handler>(handler)); + } + size_t n = 0; + return source_.apply([&](Value value) -> bool { + if (n < count_) { + ++n; + return true; + } + return handler(std::forward<Value>(value)); + }); + } + + // Skipping N items of an infinite source is still infinite + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), count_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), count_); + } +}; + +/** + * Order - For ordering a sequence of values from a source by key. + * The key is extracted by the given selector functor, and this key is then + * compared using the specified comparator. + * + * This type is usually used through the 'order' helper function, like: + * + * auto closest = from(places) + * | orderBy([](Place& p) { + * return -distance(p.location, here); + * }) + * | take(10); + */ +template <class Selector, class Comparer> +class Order : public Operator<Order<Selector, Comparer>> { + Selector selector_; + Comparer comparer_; + + public: + Order() = default; + + explicit Order(Selector selector) : selector_(std::move(selector)) {} + + Order(Selector selector, Comparer comparer) + : selector_(std::move(selector)), comparer_(std::move(comparer)) {} + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type, + class Result = invoke_result_t<Selector, Value>> + class Generator : public GenImpl< + StorageType&&, + Generator<Value, Source, StorageType, Result>> { + static_assert(!Source::infinite, "Cannot sort infinite source!"); + Source source_; + Selector selector_; + Comparer comparer_; + + typedef std::vector<StorageType> VectorType; + + VectorType asVector() const { + auto comparer = [&](const StorageType& a, const StorageType& b) { + return comparer_(selector_(a), selector_(b)); + }; + auto vals = source_ | as<VectorType>(); + std::sort(vals.begin(), vals.end(), comparer); + return vals; + } + + public: + Generator(Source source, Selector selector, Comparer comparer) + : source_(std::move(source)), + selector_(std::move(selector)), + comparer_(std::move(comparer)) {} + + VectorType operator|(const Collect<VectorType>&) const { + return asVector(); + } + + VectorType operator|(const CollectTemplate<std::vector>&) const { + return asVector(); + } + + template <class Body> + void foreach(Body&& body) const { + for (auto& value : asVector()) { + body(std::move(value)); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + auto comparer = [&](const StorageType& a, const StorageType& b) { + // swapped for minHeap + return comparer_(selector_(b), selector_(a)); + }; + auto heap = source_ | as<VectorType>(); + std::make_heap(heap.begin(), heap.end(), comparer); + while (!heap.empty()) { + std::pop_heap(heap.begin(), heap.end(), comparer); + if (!handler(std::move(heap.back()))) { + return false; + } + heap.pop_back(); + } + return true; + } + + // Can only be run on and produce finite generators + static constexpr bool infinite = false; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), selector_, comparer_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), selector_, comparer_); + } +}; + +/** + * GroupBy - Group values by a given key selector, producing a sequence of + * groups. + * + * This type is usually used through the 'groupBy' helper function, like: + * + * auto bests + * = from(places) + * | groupBy([](const Place& p) { + * return p.city; + * }) + * | [](Group<std::string, Place>&& g) { + * cout << g.key() << ": " << (g | first).description; + * }; + */ +template <class Selector> +class GroupBy : public Operator<GroupBy<Selector>> { + Selector selector_; + + public: + GroupBy() {} + + explicit GroupBy(Selector selector) : selector_(std::move(selector)) {} + + template < + class Value, + class Source, + class ValueDecayed = typename std::decay<Value>::type, + class Key = invoke_result_t<Selector, Value>, + class KeyDecayed = typename std::decay<Key>::type> + class Generator + : public GenImpl< + Group<KeyDecayed, ValueDecayed>&&, + Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> { + static_assert(!Source::infinite, "Cannot group infinite source!"); + Source source_; + Selector selector_; + + public: + Generator(Source source, Selector selector) + : source_(std::move(source)), selector_(std::move(selector)) {} + + typedef Group<KeyDecayed, ValueDecayed> GroupType; + + template <class Handler> + bool apply(Handler&& handler) const { + folly::F14FastMap<KeyDecayed, typename GroupType::VectorType> groups; + source_ | [&](Value value) { + const Value& cv = value; + auto& group = groups[selector_(cv)]; + group.push_back(std::forward<Value>(value)); + }; + for (auto& kg : groups) { + GroupType group(kg.first, std::move(kg.second)); + if (!handler(std::move(group))) { + return false; + } + kg.second.clear(); + } + return true; + } + + // Can only be run on and produce finite generators + static constexpr bool infinite = false; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), selector_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), selector_); + } +}; + +/** + * GroupByAdjacent - Group adjacent values by a given key selector, producing a + * sequence of groups. This differs from GroupBy in that only contiguous sets + * of values with the same key are considered part of the same group. Unlike + * GroupBy, this can be used on infinite sequences. + * + * This type is usually used through the 'groupByAdjacent' helper function: + * + * auto tens + * = seq(0) + * | groupByAdjacent([](int i){ return (i / 10) % 2; }) + * + * This example results in a list like [ 0:[0-9], 1:[10-19], 0:[20-29], ... ] + */ +template <class Selector> +class GroupByAdjacent : public Operator<GroupByAdjacent<Selector>> { + Selector selector_; + + public: + GroupByAdjacent() {} + + explicit GroupByAdjacent(Selector selector) + : selector_(std::move(selector)) {} + + template < + class Value, + class Source, + class ValueDecayed = typename std::decay<Value>::type, + class Key = invoke_result_t<Selector, Value>, + class KeyDecayed = typename std::decay<Key>::type> + class Generator + : public GenImpl< + Group<KeyDecayed, ValueDecayed>&&, + Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> { + Source source_; + Selector selector_; + + public: + Generator(Source source, Selector selector) + : source_(std::move(source)), selector_(std::move(selector)) {} + + typedef Group<KeyDecayed, ValueDecayed> GroupType; + + template <class Handler> + bool apply(Handler&& handler) const { + Optional<KeyDecayed> key = none; + typename GroupType::VectorType values; + + bool result = source_.apply([&](Value value) mutable { + KeyDecayed newKey = selector_(value); + + // start the first group + if (!key.hasValue()) { + key.emplace(newKey); + } + + if (key == newKey) { + // grow the current group + values.push_back(std::forward<Value>(value)); + } else { + // flush the current group + GroupType group(key.value(), std::move(values)); + if (!handler(std::move(group))) { + return false; + } + + // start a new group + key.emplace(newKey); + values.clear(); + values.push_back(std::forward<Value>(value)); + } + return true; + }); + + if (!result) { + return false; + } + + if (!key.hasValue()) { + return true; + } + + // flush the final group + GroupType group(key.value(), std::move(values)); + return handler(std::move(group)); + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), selector_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), selector_); + } +}; + +/* + * TypeAssertion - For verifying the exact type of the value produced by a + * generator. Useful for testing and debugging, and acts as a no-op at runtime. + * Pass-through at runtime. Used through the 'assert_type<>()' factory method + * like so: + * + * auto c = from(vector) | assert_type<int&>() | sum; + * + */ +template <class Expected> +class TypeAssertion : public Operator<TypeAssertion<Expected>> { + public: + template <class Source, class Value> + const Source& compose(const GenImpl<Value, Source>& source) const { + static_assert( + std::is_same<Expected, Value>::value, "assert_type() check failed"); + return source.self(); + } + + template <class Source, class Value> + Source&& compose(GenImpl<Value, Source>&& source) const { + static_assert( + std::is_same<Expected, Value>::value, "assert_type() check failed"); + return std::move(source.self()); + } +}; + +/** + * Distinct - For filtering duplicates out of a sequence. A selector may be + * provided to generate a key to uniquify for each value. + * + * This type is usually used through the 'distinct' helper function, like: + * + * auto closest = from(results) + * | distinctBy([](Item& i) { + * return i.target; + * }) + * | take(10); + */ +template <class Selector> +class Distinct : public Operator<Distinct<Selector>> { + Selector selector_; + + public: + Distinct() = default; + + explicit Distinct(Selector selector) : selector_(std::move(selector)) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + Selector selector_; + + typedef typename std::decay<Value>::type StorageType; + + // selector_ cannot be passed an rvalue or it would end up passing the husk + // of a value to the downstream operators. + typedef const StorageType& ParamType; + + typedef invoke_result_t<Selector, ParamType> KeyType; + typedef typename std::decay<KeyType>::type KeyStorageType; + + public: + Generator(Source source, Selector selector) + : source_(std::move(source)), selector_(std::move(selector)) {} + + template <class Body> + void foreach(Body&& body) const { + folly::F14FastSet<KeyStorageType> keysSeen; + source_.foreach([&](Value value) { + if (keysSeen.insert(selector_(ParamType(value))).second) { + body(std::forward<Value>(value)); + } + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + folly::F14FastSet<KeyStorageType> keysSeen; + return source_.apply([&](Value value) -> bool { + if (keysSeen.insert(selector_(ParamType(value))).second) { + return handler(std::forward<Value>(value)); + } + return true; + }); + } + + // While running distinct on an infinite sequence might produce a + // conceptually finite sequence, it will take infinite time + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), selector_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), selector_); + } +}; + +/** + * Composer - Helper class for adapting pipelines into functors. Primarily used + * for 'mapOp'. + */ +template <class Operators> +class Composer { + Operators op_; + + public: + explicit Composer(Operators op) : op_(std::move(op)) {} + + template < + class Source, + class Ret = + decltype(std::declval<Operators>().compose(std::declval<Source>()))> + Ret operator()(Source&& source) const { + return op_.compose(std::forward<Source>(source)); + } +}; + +/** + * Batch - For producing fixed-size batches of each value from a source. + * + * This type is usually used through the 'batch' helper function: + * + * auto batchSums + * = seq(1, 10) + * | batch(3) + * | map([](const std::vector<int>& batch) { + * return from(batch) | sum; + * }) + * | as<vector>(); + */ +class Batch : public Operator<Batch> { + size_t batchSize_; + + public: + explicit Batch(size_t batchSize) : batchSize_(batchSize) { + if (batchSize_ == 0) { + throw std::invalid_argument("Batch size must be non-zero!"); + } + } + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type, + class VectorType = std::vector<StorageType>> + class Generator : public GenImpl< + VectorType&, + Generator<Value, Source, StorageType, VectorType>> { + Source source_; + size_t batchSize_; + + public: + explicit Generator(Source source, size_t batchSize) + : source_(std::move(source)), batchSize_(batchSize) {} + + template <class Handler> + bool apply(Handler&& handler) const { + VectorType batch_; + batch_.reserve(batchSize_); + bool shouldContinue = source_.apply([&](Value value) -> bool { + batch_.push_back(std::forward<Value>(value)); + if (batch_.size() == batchSize_) { + bool needMore = handler(batch_); + batch_.clear(); + return needMore; + } + // Always need more if the handler is not called. + return true; + }); + // Flush everything, if and only if `handler` hasn't returned false. + if (shouldContinue && !batch_.empty()) { + shouldContinue = handler(batch_); + batch_.clear(); + } + return shouldContinue; + } + + // Taking n-tuples of an infinite source is still infinite + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), batchSize_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), batchSize_); + } +}; + +/** + * Window - For overlapping the lifetimes of pipeline values, especially with + * Futures. + * + * This type is usually used through the 'window' helper function: + * + * auto responses + * = byLine(STDIN) + * | map(makeRequestFuture) + * | window(1000) + * | map(waitFuture) + * | as<vector>(); + */ +class Window : public Operator<Window> { + size_t windowSize_; + + public: + explicit Window(size_t windowSize) : windowSize_(windowSize) { + if (windowSize_ == 0) { + throw std::invalid_argument("Window size must be non-zero!"); + } + } + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type> + class Generator + : public GenImpl<StorageType&&, Generator<Value, Source, StorageType>> { + Source source_; + size_t windowSize_; + + public: + explicit Generator(Source source, size_t windowSize) + : source_(std::move(source)), windowSize_(windowSize) {} + + template <class Handler> + bool apply(Handler&& handler) const { + std::vector<StorageType> buffer; + buffer.reserve(windowSize_); + size_t readIndex = 0; + bool shouldContinue = source_.apply([&](Value value) -> bool { + if (buffer.size() < windowSize_) { + buffer.push_back(std::forward<Value>(value)); + } else { + StorageType& entry = buffer[readIndex++]; + if (readIndex == windowSize_) { + readIndex = 0; + } + if (!handler(std::move(entry))) { + return false; + } + entry = std::forward<Value>(value); + } + return true; + }); + if (!shouldContinue) { + return false; + } + if (buffer.size() < windowSize_) { + for (StorageType& entry : buffer) { + if (!handler(std::move(entry))) { + return false; + } + } + } else { + for (size_t i = readIndex;;) { + StorageType& entry = buffer[i++]; + if (!handler(std::move(entry))) { + return false; + } + if (i == windowSize_) { + i = 0; + } + if (i == readIndex) { + break; + } + } + } + return true; + } + + // Taking n-tuples of an infinite source is still infinite + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), windowSize_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), windowSize_); + } +}; + +/** + * Concat - For flattening generators of generators. + * + * This type is usually used through the 'concat' static value, like: + * + * auto edges = + * from(nodes) + * | map([](Node& x) { + * return from(x.neighbors) + * | map([&](Node& y) { + * return Edge(x, y); + * }); + * }) + * | concat + * | as<std::set>(); + */ +class Concat : public Operator<Concat> { + public: + Concat() = default; + + template < + class Inner, + class Source, + class InnerValue = typename std::decay<Inner>::type::ValueType> + class Generator + : public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> { + Source source_; + + public: + explicit Generator(Source source) : source_(std::move(source)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Inner inner) -> bool { + return inner.apply(std::forward<Handler>(handler)); + }); + } + + template <class Body> + void foreach(Body&& body) const { + source_.foreach( + [&](Inner inner) { inner.foreach(std::forward<Body>(body)); }); + } + + // Resulting concatenation is only finite if both Source and Inner are also + // finite. In one sence, if dosn't make sence to call concat when the Inner + // generator is infinite (you could just call first), so we could also just + // static_assert if the inner is infinite. Taking the less restrictive + // approch for now. + static constexpr bool infinite = + Source::infinite || std::decay<Inner>::type::infinite; + }; + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self())); + } + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self()); + } +}; + +/** + * RangeConcat - For flattening generators of iterables. + * + * This type is usually used through the 'rconcat' static value, like: + * + * map<int, vector<int>> adjacency; + * auto sinks = + * from(adjacency) + * | get<1>() + * | rconcat() + * | as<std::set>(); + */ +class RangeConcat : public Operator<RangeConcat> { + public: + RangeConcat() = default; + + template < + class Range, + class Source, + class InnerValue = typename ValueTypeOfRange<Range>::RefType> + class Generator + : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> { + Source source_; + + public: + explicit Generator(Source source) : source_(std::move(source)) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach([&](Range range) { + for (auto& value : range) { + body(value); + } + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Range range) -> bool { + for (auto& value : range) { + if (!handler(value)) { + return false; + } + } + return true; + }); + } + + // This is similar to concat, except that the inner iterables all are finite + // so the only thing that matters is that the source is infinite. + static constexpr bool infinite = Source::infinite; + }; + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self())); + } + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self()); + } +}; + +/** + * GuardImpl - For handling exceptions from downstream computation. Requires the + * type of exception to catch, and handler function to invoke in the event of + * the exception. Note that the handler may: + * 1) return true to continue processing the sequence + * 2) return false to end the sequence immediately + * 3) throw, to pass the exception to the next catch + * The handler must match the signature 'bool(Exception&, Value)'. + * + * This type is used through the `guard` helper, like so: + * + * auto indexes + * = byLine(STDIN_FILENO) + * | guard<std::runtime_error>([](std::runtime_error& e, + * StringPiece sp) { + * LOG(ERROR) << sp << ": " << e.str(); + * return true; // continue processing subsequent lines + * }) + * | eachTo<int>() + * | as<vector>(); + * + * KNOWN ISSUE: This only guards pipelines through operators which do not + * retain resulting values. Exceptions thrown after operators like pmap, order, + * batch, cannot be caught from here. + **/ +template <class Exception, class ErrorHandler> +class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> { + ErrorHandler handler_; + + public: + explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + ErrorHandler handler_; + + public: + explicit Generator(Source source, ErrorHandler handler) + : source_(std::move(source)), handler_(std::move(handler)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) -> bool { + try { + handler(std::forward<Value>(value)); + return true; + } catch (Exception& e) { + return handler_(e, std::forward<Value>(value)); + } + }); + } + + // Just passes value though, length unaffected + static constexpr bool infinite = Source::infinite; + }; + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), handler_); + } + + template <class Value, class Source, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), handler_); + } +}; + +/** + * Dereference - For dereferencing a sequence of pointers while filtering out + * null pointers. + * + * This type is usually used through the 'dereference' static value, like: + * + * auto refs = from(ptrs) | dereference; + */ +class Dereference : public Operator<Dereference> { + public: + Dereference() = default; + + template < + class Value, + class Source, + class Result = decltype(*std::declval<Value>())> + class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { + Source source_; + + public: + explicit Generator(Source source) : source_(std::move(source)) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach([&](Value value) { + if (value) { + return body(*std::forward<Value>(value)); + } + }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) -> bool { + if (value) { + return handler(*std::forward<Value>(value)); + } + return true; + }); + } + + // Just passes value though, length unaffected + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self())); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self()); + } +}; + +/** + * Indirect - For producing a sequence of the addresses of the values in the + * input. + * + * This type is usually used through the 'indirect' static value, like: + * + * auto ptrs = from(refs) | indirect; + */ +class Indirect : public Operator<Indirect> { + public: + Indirect() = default; + + template < + class Value, + class Source, + class Result = typename std::remove_reference<Value>::type*> + class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { + Source source_; + static_assert( + !std::is_rvalue_reference<Value>::value, + "Cannot use indirect on an rvalue"); + + public: + explicit Generator(Source source) : source_(std::move(source)) {} + + template <class Body> + void foreach(Body&& body) const { + source_.foreach( + [&](Value value) { return body(&std::forward<Value>(value)); }); + } + + template <class Handler> + bool apply(Handler&& handler) const { + return source_.apply([&](Value value) -> bool { + return handler(&std::forward<Value>(value)); + }); + } + + // Just passes value though, length unaffected + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self())); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self()); + } +}; + +/** + * Cycle - For repeating a sequence forever. + * + * This type is usually used through the 'cycle' static value, like: + * + * auto tests + * = from(samples) + * | cycle + * | take(100); + * + * or in the finite case: + * + * auto thrice = g | cycle(3); + */ +template <bool forever> +class Cycle : public Operator<Cycle<forever>> { + off_t limit_; // not used if forever == true + public: + Cycle() = default; + + explicit Cycle(off_t limit) : limit_(limit) { + static_assert( + !forever, + "Cycle limit constructor should not be used when forever == true."); + } + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + off_t limit_; + + public: + explicit Generator(Source source, off_t limit) + : source_(std::move(source)), limit_(limit) {} + + template <class Handler> + bool apply(Handler&& handler) const { + bool cont; + auto handler2 = [&](Value value) { + cont = handler(std::forward<Value>(value)); + return cont; + }; + // Becomes an infinte loop if forever == true + for (off_t count = 0; (forever || count != limit_); ++count) { + cont = false; + source_.apply(handler2); + if (!cont) { + return false; + } + } + return true; + } + + // This is the hardest one to infer. If we are simply doing a finite cycle, + // then (gen | cycle(n)) is infinite if and only if gen is infinite. + // However, if we are doing an infinite cycle, (gen | cycle) is infinite + // unless gen is empty. However, we will always mark (gen | cycle) as + // infinite, because patterns such as (gen | cycle | count) can either take + // on exactly one value, or infinite loop. + static constexpr bool infinite = forever || Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), limit_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), limit_); + } + + /** + * Convenience function for finite cycles used like: + * + * auto tripled = gen | cycle(3); + */ + Cycle<false> operator()(off_t limit) const { + return Cycle<false>(limit); + } +}; + +/* + ******************************* Sinks ***************************************** + */ + +/** + * FoldLeft - Left-associative functional fold. For producing an aggregate value + * from a seed and a folder function. Useful for custom aggregators on a + * sequence. + * + * This type is primarily used through the 'foldl' helper method, like: + * + * double movingAverage = from(values) + * | foldl(0.0, [](double avg, double sample) { + * return sample * 0.1 + avg * 0.9; + * }); + */ +template <class Seed, class Fold> +class FoldLeft : public Operator<FoldLeft<Seed, Fold>> { + Seed seed_; + Fold fold_; + + public: + FoldLeft() = default; + FoldLeft(Seed seed, Fold fold) + : seed_(std::move(seed)), fold_(std::move(fold)) {} + + template <class Source, class Value> + Seed compose(const GenImpl<Value, Source>& source) const { + static_assert(!Source::infinite, "Cannot foldl infinite source"); + Seed accum = seed_; + source | [&](Value v) { + accum = fold_(std::move(accum), std::forward<Value>(v)); + }; + return accum; + } +}; + +/** + * First - For finding the first value in a sequence. + * + * This type is primarily used through the 'first' static value, like: + * + * int firstThreeDigitPrime = seq(100) | filter(isPrime) | first; + */ +class First : public Operator<First> { + public: + First() = default; + + template < + class Source, + class Value, + class StorageType = typename std::decay<Value>::type> + Optional<StorageType> compose(const GenImpl<Value, Source>& source) const { + Optional<StorageType> accum; + source | [&](Value v) -> bool { + accum = std::forward<Value>(v); + return false; + }; + return accum; + } +}; + +/** + * IsEmpty - a helper class for isEmpty and notEmpty + * + * Essentially returns 'result' if the source is empty. Note that this cannot be + * called on an infinite source, because then there is only one possible return + * value. + * + * + * Used primarily through 'isEmpty' and 'notEmpty' static values + * + * bool hasPrimes = g | filter(prime) | notEmpty; + * bool lacksEvens = g | filter(even) | isEmpty; + * + * Also used in the implementation of 'any' and 'all' + */ +template <bool emptyResult> +class IsEmpty : public Operator<IsEmpty<emptyResult>> { + public: + IsEmpty() = default; + + template <class Source, class Value> + bool compose(const GenImpl<Value, Source>& source) const { + static_assert( + !Source::infinite, + "Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on " + "infinite source. 'all' and 'isEmpty' will either return " + "false or hang. 'any' or 'notEmpty' will either return true " + "or hang."); + bool ans = emptyResult; + source | [&](Value /* v */) -> bool { + ans = !emptyResult; + return false; + }; + return ans; + } +}; + +/** + * Reduce - Functional reduce, for recursively combining values from a source + * using a reducer function until there is only one item left. Useful for + * combining values when an empty sequence doesn't make sense. + * + * This type is primarily used through the 'reduce' helper method, like: + * + * sring longest = from(names) + * | reduce([](string&& best, string& current) { + * return best.size() >= current.size() ? best : current; + * }); + */ +template <class Reducer> +class Reduce : public Operator<Reduce<Reducer>> { + Reducer reducer_; + + public: + Reduce() = default; + explicit Reduce(Reducer reducer) : reducer_(std::move(reducer)) {} + + template < + class Source, + class Value, + class StorageType = typename std::decay<Value>::type> + Optional<StorageType> compose(const GenImpl<Value, Source>& source) const { + static_assert(!Source::infinite, "Cannot reduce infinite source"); + Optional<StorageType> accum; + source | [&](Value v) { + if (auto target = accum.get_pointer()) { + *target = reducer_(std::move(*target), std::forward<Value>(v)); + } else { + accum = std::forward<Value>(v); + } + }; + return accum; + } +}; + +/** + * Count - for simply counting the items in a collection. + * + * This type is usually used through its singleton, 'count': + * + * auto shortPrimes = seq(1, 100) | filter(isPrime) | count; + */ +class Count : public Operator<Count> { + public: + Count() = default; + + template <class Source, class Value> + size_t compose(const GenImpl<Value, Source>& source) const { + static_assert(!Source::infinite, "Cannot count infinite source"); + return foldl( + size_t(0), [](size_t accum, Value /* v */) { return accum + 1; }) + .compose(source); + } +}; + +/** + * Sum - For simply summing up all the values from a source. + * + * This type is usually used through its singleton, 'sum': + * + * auto gaussSum = seq(1, 100) | sum; + */ +class Sum : public Operator<Sum> { + public: + Sum() = default; + + template < + class Source, + class Value, + class StorageType = typename std::decay<Value>::type> + StorageType compose(const GenImpl<Value, Source>& source) const { + static_assert(!Source::infinite, "Cannot sum infinite source"); + return foldl( + StorageType(0), + [](StorageType&& accum, Value v) { + return std::move(accum) + std::forward<Value>(v); + }) + .compose(source); + } +}; + +/** + * Contains - For testing whether a value matching the given value is contained + * in a sequence. + * + * This type should be used through the 'contains' helper method, like: + * + * bool contained = seq(1, 10) | map(square) | contains(49); + */ +template <class Needle> +class Contains : public Operator<Contains<Needle>> { + Needle needle_; + + public: + explicit Contains(Needle needle) : needle_(std::move(needle)) {} + + template < + class Source, + class Value, + class StorageType = typename std::decay<Value>::type> + bool compose(const GenImpl<Value, Source>& source) const { + static_assert( + !Source::infinite, + "Calling contains on an infinite source might cause " + "an infinite loop."); + return !(source | [this](Value value) { + return !(needle_ == std::forward<Value>(value)); + }); + } +}; + +/** + * Min - For a value which minimizes a key, where the key is determined by a + * given selector, and compared by given comparer. + * + * This type is usually used through the singletone 'min' or through the helper + * functions 'minBy' and 'maxBy'. + * + * auto oldest = from(people) + * | minBy([](Person& p) { + * return p.dateOfBirth; + * }); + */ +template <class Selector, class Comparer> +class Min : public Operator<Min<Selector, Comparer>> { + Selector selector_; + Comparer comparer_; + + template <typename T> + const T& asConst(const T& t) const { + return t; + } + + public: + Min() = default; + + explicit Min(Selector selector) : selector_(std::move(selector)) {} + + Min(Selector selector, Comparer comparer) + : selector_(std::move(selector)), comparer_(std::move(comparer)) {} + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type, + class Key = typename std::decay<invoke_result_t<Selector, Value>>::type> + Optional<StorageType> compose(const GenImpl<Value, Source>& source) const { + static_assert( + !Source::infinite, + "Calling min or max on an infinite source will cause " + "an infinite loop."); + Optional<StorageType> min; + Optional<Key> minKey; + source | [&](Value v) { + Key key = selector_(asConst(v)); // so that selector_ cannot mutate v + if (auto lastKey = minKey.get_pointer()) { + if (!comparer_(key, *lastKey)) { + return; + } + } + minKey = std::move(key); + min = std::forward<Value>(v); + }; + return min; + } +}; + +/** + * Append - For collecting values from a source into a given output container + * by appending. + * + * This type is usually used through the helper function 'appendTo', like: + * + * vector<int64_t> ids; + * from(results) | map([](Person& p) { return p.id }) + * | appendTo(ids); + */ +template <class Collection> +class Append : public Operator<Append<Collection>> { + Collection* collection_; + + public: + explicit Append(Collection* collection) : collection_(collection) {} + + template <class Value, class Source> + Collection& compose(const GenImpl<Value, Source>& source) const { + static_assert(!Source::infinite, "Cannot appendTo with infinite source"); + source | [&](Value v) { + collection_->insert(collection_->end(), std::forward<Value>(v)); + }; + return *collection_; + } +}; + +/** + * Collect - For collecting values from a source in a collection of the desired + * type. + * + * This type is usually used through the helper function 'as', like: + * + * std::string upper = from(stringPiece) + * | map(&toupper) + * | as<std::string>(); + */ +template <class Collection> +class Collect : public Operator<Collect<Collection>> { + public: + Collect() = default; + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type> + Collection compose(const GenImpl<Value, Source>& source) const { + static_assert( + !Source::infinite, "Cannot convert infinite source to object with as."); + Collection collection; + source | [&](Value v) { + collection.insert(collection.end(), std::forward<Value>(v)); + }; + return collection; + } +}; + +/** + * CollectTemplate - For collecting values from a source in a collection + * constructed using the specified template type. Given the type of values + * produced by the given generator, the collection type will be: + * Container<Value, Allocator<Value>> + * + * The allocator defaults to std::allocator, so this may be used for the STL + * containers by simply using operators like 'as<set>', 'as<deque>', + * 'as<vector>'. 'as', here is the helper method which is the usual means of + * constructing this operator. + * + * Example: + * + * set<string> uniqueNames = from(names) | as<set>(); + */ +template < + template <class, class> class Container, + template <class> class Allocator> +class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> { + public: + CollectTemplate() = default; + + template < + class Value, + class Source, + class StorageType = typename std::decay<Value>::type, + class Collection = Container<StorageType, Allocator<StorageType>>> + Collection compose(const GenImpl<Value, Source>& source) const { + static_assert( + !Source::infinite, "Cannot convert infinite source to object with as."); + Collection collection; + source | [&](Value v) { + collection.insert(collection.end(), std::forward<Value>(v)); + }; + return collection; + } +}; + +/** + * UnwrapOr - For unwrapping folly::Optional values, or providing the given + * fallback value. Usually used through the 'unwrapOr' helper like so: + * + * auto best = from(scores) | max | unwrapOr(-1); + * + * Note that the fallback value needn't match the value in the Optional it is + * unwrapping. If mis-matched types are supported, the common type of the two is + * returned by value. If the types match, a reference (T&& > T& > const T&) is + * returned. + */ +template <class T> +class UnwrapOr { + public: + explicit UnwrapOr(T&& value) : value_(std::move(value)) {} + explicit UnwrapOr(const T& value) : value_(value) {} + + T& value() { + return value_; + } + const T& value() const { + return value_; + } + + private: + T value_; +}; + +template <class T> +T&& operator|(Optional<T>&& opt, UnwrapOr<T>&& fallback) { + if (T* p = opt.get_pointer()) { + return std::move(*p); + } + return std::move(fallback.value()); +} + +template <class T> +T& operator|(Optional<T>& opt, UnwrapOr<T>& fallback) { + if (T* p = opt.get_pointer()) { + return *p; + } + return fallback.value(); +} + +template <class T> +const T& operator|(const Optional<T>& opt, const UnwrapOr<T>& fallback) { + if (const T* p = opt.get_pointer()) { + return *p; + } + return fallback.value(); +} + +// Mixed type unwrapping always returns values, moving where possible +template < + class T, + class U, + class R = typename std::enable_if< + !std::is_same<T, U>::value, + typename std::common_type<T, U>::type>::type> +R operator|(Optional<T>&& opt, UnwrapOr<U>&& fallback) { + if (T* p = opt.get_pointer()) { + return std::move(*p); + } + return std::move(fallback.value()); +} + +template < + class T, + class U, + class R = typename std::enable_if< + !std::is_same<T, U>::value, + typename std::common_type<T, U>::type>::type> +R operator|(const Optional<T>& opt, UnwrapOr<U>&& fallback) { + if (const T* p = opt.get_pointer()) { + return *p; + } + return std::move(fallback.value()); +} + +template < + class T, + class U, + class R = typename std::enable_if< + !std::is_same<T, U>::value, + typename std::common_type<T, U>::type>::type> +R operator|(Optional<T>&& opt, const UnwrapOr<U>& fallback) { + if (T* p = opt.get_pointer()) { + return std::move(*p); + } + return fallback.value(); +} + +template < + class T, + class U, + class R = typename std::enable_if< + !std::is_same<T, U>::value, + typename std::common_type<T, U>::type>::type> +R operator|(const Optional<T>& opt, const UnwrapOr<U>& fallback) { + if (const T* p = opt.get_pointer()) { + return *p; + } + return fallback.value(); +} + +/** + * Unwrap - For unwrapping folly::Optional values in a folly::gen style. Usually + * used through the 'unwrap' instace like so: + * + * auto best = from(scores) | max | unwrap; // may throw + */ +class Unwrap {}; + +template <class T> +T&& operator|(Optional<T>&& opt, const Unwrap&) { + return std::move(opt.value()); +} + +template <class T> +T& operator|(Optional<T>& opt, const Unwrap&) { + return opt.value(); +} + +template <class T> +const T& operator|(const Optional<T>& opt, const Unwrap&) { + return opt.value(); +} + +#if FOLLY_USE_RANGEV3 +template <class RangeV3, class Value> +class RangeV3Source + : public gen::GenImpl<Value, RangeV3Source<RangeV3, Value>> { + mutable RangeV3 r_; // mutable since some ranges are not const-iteratable + + public: + explicit RangeV3Source(RangeV3 const& r) : r_(r) {} + + template <class Body> + void foreach(Body&& body) const { + for (auto const& value : r_) { + body(value); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + for (auto const& value : r_) { + if (!handler(value)) { + return false; + } + } + return true; + } + + static constexpr bool infinite = false; +}; + +template <class RangeV3, class Value> +class RangeV3CopySource + : public gen::GenImpl<Value, RangeV3CopySource<RangeV3, Value>> { + mutable RangeV3 r_; // mutable since some ranges are not const-iteratable + + public: + explicit RangeV3CopySource(RangeV3&& r) : r_(std::move(r)) {} + + template <class Body> + void foreach(Body&& body) const { + for (auto const& value : r_) { + body(value); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + for (auto const& value : r_) { + if (!handler(value)) { + return false; + } + } + return true; + } + + static constexpr bool infinite = false; +}; + +struct from_container_fn { + template <typename Container> + friend auto operator|(Container&& c, from_container_fn) { + return gen::from(std::forward<Container>(c)); + } +}; + +struct from_rangev3_fn { + template <typename Range> + friend auto operator|(Range&& r, from_rangev3_fn) { + using DecayedRange = std::decay_t<Range>; + using DecayedValue = std::decay_t<decltype(*r.begin())>; + return RangeV3Source<DecayedRange, DecayedValue>(r); + } +}; + +struct from_rangev3_copy_fn { + template <typename Range> + friend auto operator|(Range&& r, from_rangev3_copy_fn) { + using RangeDecay = std::decay_t<Range>; + using Value = std::decay_t<decltype(*r.begin())>; + return RangeV3CopySource<RangeDecay, Value>(std::move(r)); + } +}; +#endif // FOLLY_USE_RANGEV3 +} // namespace detail + +#if FOLLY_USE_RANGEV3 +/* + ****************************************************************************** + * Pipe fittings between a container/range-v3 and a folly::gen. + * Example: vec | gen::from_container | folly::gen::filter(...); + * Example: vec | ranges::views::filter(...) | gen::from_rangev3 | gen::xxx; + ****************************************************************************** + */ +constexpr detail::from_container_fn from_container; +constexpr detail::from_rangev3_fn from_rangev3; +constexpr detail::from_rangev3_copy_fn from_rangev3_copy; + +template <typename Range> +auto from_rangev3_call(Range&& r) { + using Value = std::decay_t<decltype(*r.begin())>; + return detail::RangeV3Source<Range, Value>(r); +} + +// it is safe to pipe an rvalue into a range-v3 view if the rest of the pipeline +// will finish its traversal within the current full-expr, a condition provided +// by folly::gen. +template <typename Range> +auto rangev3_will_be_consumed(Range&& r) { + // intentionally use `r` instead of `std::forward<Range>(r)`; see above. + // range-v3 ranges copy in O(1) so it is appropriate. + return ranges::views::all(r); +} +#endif // FOLLY_USE_RANGEV3 + +/** + * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper. + **/ +template <class Value> +class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { + class WrapperBase { + public: + virtual ~WrapperBase() noexcept {} + virtual bool apply(const std::function<bool(Value)>& handler) const = 0; + virtual void foreach(const std::function<void(Value)>& body) const = 0; + virtual std::unique_ptr<const WrapperBase> clone() const = 0; + }; + + template <class Wrapped> + class WrapperImpl : public WrapperBase { + Wrapped wrapped_; + + public: + explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {} + + bool apply(const std::function<bool(Value)>& handler) const override { + return wrapped_.apply(handler); + } + + void foreach(const std::function<void(Value)>& body) const override { + wrapped_.foreach(body); + } + + std::unique_ptr<const WrapperBase> clone() const override { + return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_)); + } + }; + + std::unique_ptr<const WrapperBase> wrapper_; + + public: + template <class Self> + /* implicit */ VirtualGen(Self source) + : wrapper_(new WrapperImpl<Self>(std::move(source))) {} + + VirtualGen(VirtualGen&& source) noexcept + : wrapper_(std::move(source.wrapper_)) {} + + VirtualGen(const VirtualGen& source) : wrapper_(source.wrapper_->clone()) {} + + VirtualGen& operator=(const VirtualGen& source) { + wrapper_.reset(source.wrapper_->clone()); + return *this; + } + + VirtualGen& operator=(VirtualGen&& source) noexcept { + wrapper_ = std::move(source.wrapper_); + return *this; + } + + bool apply(const std::function<bool(Value)>& handler) const { + return wrapper_->apply(handler); + } + + void foreach(const std::function<void(Value)>& body) const { + wrapper_->foreach(body); + } +}; + +/** + * non-template operators, statically defined to avoid the need for anything but + * the header. + */ +constexpr detail::Sum sum{}; + +constexpr detail::Count count{}; + +constexpr detail::First first{}; + +constexpr detail::IsEmpty<true> isEmpty{}; + +constexpr detail::IsEmpty<false> notEmpty{}; + +constexpr detail::Min<Identity, Less> min{}; + +constexpr detail::Min<Identity, Greater> max{}; + +constexpr detail::Order<Identity> order{}; + +constexpr detail::Distinct<Identity> distinct{}; + +constexpr detail::Map<Move> move{}; + +constexpr detail::Concat concat{}; + +constexpr detail::RangeConcat rconcat{}; + +constexpr detail::Cycle<true> cycle{}; + +constexpr detail::Dereference dereference{}; + +constexpr detail::Indirect indirect{}; + +constexpr detail::Unwrap unwrap{}; + +template <class Number> +inline detail::Take take(Number count) { + if (count < 0) { + throw std::invalid_argument("Negative value passed to take()"); + } + return detail::Take(static_cast<size_t>(count)); +} + +inline detail::Stride stride(size_t s) { + return detail::Stride(s); +} + +template <class Random = std::default_random_engine> +inline detail::Sample<Random> sample(size_t count, Random rng = Random()) { + return detail::Sample<Random>(count, std::move(rng)); +} + +inline detail::Skip skip(size_t count) { + return detail::Skip(count); +} + +inline detail::Batch batch(size_t batchSize) { + return detail::Batch(batchSize); +} + +inline detail::Window window(size_t windowSize) { + return detail::Window(windowSize); +} + +} // namespace gen +} // namespace folly + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/gen/Base.h b/ios/Pods/Flipper-Folly/folly/gen/Base.h new file mode 100644 index 000000000..45680653c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Base.h @@ -0,0 +1,852 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_BASE_H_ + +#include <algorithm> +#include <functional> +#include <memory> +#include <random> +#include <type_traits> +#include <utility> +#include <vector> + +#include <folly/Conv.h> +#include <folly/Optional.h> +#include <folly/Range.h> +#include <folly/Utility.h> +#include <folly/gen/Core.h> + +/** + * Generator-based Sequence Comprehensions in C++, akin to C#'s LINQ + * @author Tom Jackson <tjackson@fb.com> + * + * This library makes it possible to write declarative comprehensions for + * processing sequences of values efficiently in C++. The operators should be + * familiar to those with experience in functional programming, and the + * performance will be virtually identical to the equivalent, boilerplate C++ + * implementations. + * + * Generator objects may be created from either an stl-like container (anything + * supporting begin() and end()), from sequences of values, or from another + * generator (see below). To create a generator that pulls values from a vector, + * for example, one could write: + * + * vector<string> names { "Jack", "Jill", "Sara", "Tom" }; + * auto gen = from(names); + * + * Generators are composed by building new generators out of old ones through + * the use of operators. These are reminiscent of shell pipelines, and afford + * similar composition. Lambda functions are used liberally to describe how to + * handle individual values: + * + * auto lengths = gen + * | mapped([](const fbstring& name) { return name.size(); }); + * + * Generators are lazy; they don't actually perform any work until they need to. + * As an example, the 'lengths' generator (above) won't actually invoke the + * provided lambda until values are needed: + * + * auto lengthVector = lengths | as<std::vector>(); + * auto totalLength = lengths | sum; + * + * 'auto' is useful in here because the actual types of the generators objects + * are usually complicated and implementation-sensitive. + * + * If a simpler type is desired (for returning, as an example), VirtualGen<T> + * may be used to wrap the generator in a polymorphic wrapper: + * + * VirtualGen<float> powersOfE() { + * return seq(1) | mapped(&expf); + * } + * + * To learn more about this library, including the use of infinite generators, + * see the examples in the comments, or the docs (coming soon). + */ + +namespace folly { +namespace gen { + +class Less { + public: + template <class First, class Second> + auto operator()(const First& first, const Second& second) const + -> decltype(first < second) { + return first < second; + } +}; + +class Greater { + public: + template <class First, class Second> + auto operator()(const First& first, const Second& second) const + -> decltype(first > second) { + return first > second; + } +}; + +template <int n> +class Get { + public: + template <class Value> + auto operator()(Value&& value) const + -> decltype(std::get<n>(std::forward<Value>(value))) { + return std::get<n>(std::forward<Value>(value)); + } +}; + +template <class Class, class Result> +class MemberFunction { + public: + typedef Result (Class::*MemberPtr)(); + + private: + MemberPtr member_; + + public: + explicit MemberFunction(MemberPtr member) : member_(member) {} + + Result operator()(Class&& x) const { + return (x.*member_)(); + } + + Result operator()(Class& x) const { + return (x.*member_)(); + } + + Result operator()(Class* x) const { + return (x->*member_)(); + } +}; + +template <class Class, class Result> +class ConstMemberFunction { + public: + typedef Result (Class::*MemberPtr)() const; + + private: + MemberPtr member_; + + public: + explicit ConstMemberFunction(MemberPtr member) : member_(member) {} + + Result operator()(const Class& x) const { + return (x.*member_)(); + } + + Result operator()(const Class* x) const { + return (x->*member_)(); + } +}; + +template <class Class, class FieldType> +class Field { + public: + typedef FieldType Class::*FieldPtr; + + private: + FieldPtr field_; + + public: + explicit Field(FieldPtr field) : field_(field) {} + + const FieldType& operator()(const Class& x) const { + return x.*field_; + } + + const FieldType& operator()(const Class* x) const { + return x->*field_; + } + + FieldType& operator()(Class& x) const { + return x.*field_; + } + + FieldType& operator()(Class* x) const { + return x->*field_; + } + + FieldType&& operator()(Class&& x) const { + return std::move(x.*field_); + } +}; + +class Move { + public: + template <class Value> + auto operator()(Value&& value) const + -> decltype(std::move(std::forward<Value>(value))) { + return std::move(std::forward<Value>(value)); + } +}; + +/** + * Class and helper function for negating a boolean Predicate + */ +template <class Predicate> +class Negate { + Predicate pred_; + + public: + Negate() = default; + + explicit Negate(Predicate pred) : pred_(std::move(pred)) {} + + template <class Arg> + bool operator()(Arg&& arg) const { + return !pred_(std::forward<Arg>(arg)); + } +}; +template <class Predicate> +Negate<Predicate> negate(Predicate pred) { + return Negate<Predicate>(std::move(pred)); +} + +template <class Dest> +class Cast { + public: + template <class Value> + Dest operator()(Value&& value) const { + return Dest(std::forward<Value>(value)); + } +}; + +template <class Dest> +class To { + public: + template <class Value> + Dest operator()(Value&& value) const { + return ::folly::to<Dest>(std::forward<Value>(value)); + } +}; + +template <class Dest> +class TryTo { + public: + template <class Value> + Expected<Dest, ConversionCode> operator()(Value&& value) const { + return ::folly::tryTo<Dest>(std::forward<Value>(value)); + } +}; + +// Specialization to allow String->StringPiece conversion +template <> +class To<StringPiece> { + public: + StringPiece operator()(StringPiece src) const { + return src; + } +}; + +template <class Key, class Value> +class Group; + +namespace detail { + +template <class Self> +struct FBounded; + +/* + * Type Traits + */ +template <class Container> +struct ValueTypeOfRange { + public: + using RefType = decltype(*std::begin(std::declval<Container&>())); + using StorageType = typename std::decay<RefType>::type; +}; + +/* + * Sources + */ +template < + class Container, + class Value = typename ValueTypeOfRange<Container>::RefType> +class ReferencedSource; + +template < + class Value, + class Container = std::vector<typename std::decay<Value>::type>> +class CopiedSource; + +template <class Value, class SequenceImpl> +class Sequence; + +template <class Value> +class RangeImpl; + +template <class Value, class Distance> +class RangeWithStepImpl; + +template <class Value> +class SeqImpl; + +template <class Value, class Distance> +class SeqWithStepImpl; + +template <class Value> +class InfiniteImpl; + +template <class Value, class Source> +class Yield; + +template <class Value> +class Empty; + +template <class Value> +class SingleReference; + +template <class Value> +class SingleCopy; + +/* + * Operators + */ +template <class Predicate> +class Map; + +template <class Predicate> +class Filter; + +template <class Predicate> +class Until; + +class Take; + +class Stride; + +template <class Rand> +class Sample; + +class Skip; + +template <class Visitor> +class Visit; + +template <class Selector, class Comparer = Less> +class Order; + +template <class Selector> +class GroupBy; + +template <class Selector> +class GroupByAdjacent; + +template <class Selector> +class Distinct; + +template <class Operators> +class Composer; + +template <class Expected> +class TypeAssertion; + +class Concat; + +class RangeConcat; + +template <bool forever> +class Cycle; + +class Batch; + +class Window; + +class Dereference; + +class Indirect; + +/* + * Sinks + */ +template <class Seed, class Fold> +class FoldLeft; + +class First; + +template <bool result> +class IsEmpty; + +template <class Reducer> +class Reduce; + +class Sum; + +template <class Selector, class Comparer> +class Min; + +template <class Container> +class Collect; + +template < + template <class, class> class Collection = std::vector, + template <class> class Allocator = std::allocator> +class CollectTemplate; + +template <class Collection> +class Append; + +template <class Value> +struct GeneratorBuilder; + +template <class Needle> +class Contains; + +template <class Exception, class ErrorHandler> +class GuardImpl; + +template <class T> +class UnwrapOr; + +class Unwrap; + +} // namespace detail + +/** + * Polymorphic wrapper + **/ +template <class Value> +class VirtualGen; + +/* + * Source Factories + */ +template < + class Container, + class From = detail::ReferencedSource<const Container>> +From fromConst(const Container& source) { + return From(&source); +} + +template <class Container, class From = detail::ReferencedSource<Container>> +From from(Container& source) { + return From(&source); +} + +template < + class Container, + class Value = typename detail::ValueTypeOfRange<Container>::StorageType, + class CopyOf = detail::CopiedSource<Value>> +CopyOf fromCopy(Container&& source) { + return CopyOf(std::forward<Container>(source)); +} + +template <class Value, class From = detail::CopiedSource<Value>> +From from(std::initializer_list<Value> source) { + return From(source); +} + +template < + class Container, + class From = + detail::CopiedSource<typename Container::value_type, Container>> +From from(Container&& source) { + return From(std::move(source)); +} + +template < + class Value, + class Impl = detail::RangeImpl<Value>, + class Gen = detail::Sequence<Value, Impl>> +Gen range(Value begin, Value end) { + return Gen{std::move(begin), Impl{std::move(end)}}; +} + +template < + class Value, + class Distance, + class Impl = detail::RangeWithStepImpl<Value, Distance>, + class Gen = detail::Sequence<Value, Impl>> +Gen range(Value begin, Value end, Distance step) { + return Gen{std::move(begin), Impl{std::move(end), std::move(step)}}; +} + +template < + class Value, + class Impl = detail::SeqImpl<Value>, + class Gen = detail::Sequence<Value, Impl>> +Gen seq(Value first, Value last) { + return Gen{std::move(first), Impl{std::move(last)}}; +} + +template < + class Value, + class Distance, + class Impl = detail::SeqWithStepImpl<Value, Distance>, + class Gen = detail::Sequence<Value, Impl>> +Gen seq(Value first, Value last, Distance step) { + return Gen{std::move(first), Impl{std::move(last), std::move(step)}}; +} + +template < + class Value, + class Impl = detail::InfiniteImpl<Value>, + class Gen = detail::Sequence<Value, Impl>> +Gen seq(Value first) { + return Gen{std::move(first), Impl{}}; +} + +template <class Value, class Source, class Yield = detail::Yield<Value, Source>> +Yield generator(Source&& source) { + return Yield(std::forward<Source>(source)); +} + +/* + * Create inline generator, used like: + * + * auto gen = GENERATOR(int) { yield(1); yield(2); }; + */ +#define GENERATOR(TYPE) \ + ::folly::gen::detail::GeneratorBuilder<TYPE>() + [=](auto&& yield) + +/* + * empty() - for producing empty sequences. + */ +template <class Value> +detail::Empty<Value> empty() { + return {}; +} + +template < + class Value, + class Just = typename std::conditional< + std::is_reference<Value>::value, + detail::SingleReference<typename std::remove_reference<Value>::type>, + detail::SingleCopy<Value>>::type> +Just just(Value&& value) { + return Just(std::forward<Value>(value)); +} + +/* + * Operator Factories + */ +template <class Predicate, class Map = detail::Map<Predicate>> +Map mapped(Predicate pred = Predicate()) { + return Map(std::move(pred)); +} + +template <class Predicate, class Map = detail::Map<Predicate>> +Map map(Predicate pred = Predicate()) { + return Map(std::move(pred)); +} + +/** + * mapOp - Given a generator of generators, maps the application of the given + * operator on to each inner gen. Especially useful in aggregating nested data + * structures: + * + * chunked(samples, 256) + * | mapOp(filter(sampleTest) | count) + * | sum; + */ +template <class Operator, class Map = detail::Map<detail::Composer<Operator>>> +Map mapOp(Operator op) { + return Map(detail::Composer<Operator>(std::move(op))); +} + +/* + * member(...) - For extracting a member from each value. + * + * vector<string> strings = ...; + * auto sizes = from(strings) | member(&string::size); + * + * If a member is const overridden (like 'front()'), pass template parameter + * 'Const' to select the const version, or 'Mutable' to select the non-const + * version: + * + * auto heads = from(strings) | member<Const>(&string::front); + */ +enum MemberType { + Const, + Mutable, +}; + +/** + * These exist because MSVC has problems with expression SFINAE in templates + * assignment and comparisons don't work properly without being pulled out + * of the template declaration + */ +template <MemberType Constness> +struct ExprIsConst { + enum { + value = Constness == Const, + }; +}; + +template <MemberType Constness> +struct ExprIsMutable { + enum { + value = Constness == Mutable, + }; +}; + +template < + MemberType Constness = Const, + class Class, + class Return, + class Mem = ConstMemberFunction<Class, Return>, + class Map = detail::Map<Mem>> +typename std::enable_if<ExprIsConst<Constness>::value, Map>::type member( + Return (Class::*member)() const) { + return Map(Mem(member)); +} + +template < + MemberType Constness = Mutable, + class Class, + class Return, + class Mem = MemberFunction<Class, Return>, + class Map = detail::Map<Mem>> +typename std::enable_if<ExprIsMutable<Constness>::value, Map>::type member( + Return (Class::*member)()) { + return Map(Mem(member)); +} + +/* + * field(...) - For extracting a field from each value. + * + * vector<Item> items = ...; + * auto names = from(items) | field(&Item::name); + * + * Note that if the values of the generator are rvalues, any non-reference + * fields will be rvalues as well. As an example, the code below does not copy + * any strings, only moves them: + * + * auto namesVector = from(items) + * | move + * | field(&Item::name) + * | as<vector>(); + */ +template < + class Class, + class FieldType, + class Field = Field<Class, FieldType>, + class Map = detail::Map<Field>> +Map field(FieldType Class::*field) { + return Map(Field(field)); +} + +template <class Predicate = Identity, class Filter = detail::Filter<Predicate>> +Filter filter(Predicate pred = Predicate()) { + return Filter(std::move(pred)); +} + +template <class Visitor = Ignore, class Visit = detail::Visit<Visitor>> +Visit visit(Visitor visitor = Visitor()) { + return Visit(std::move(visitor)); +} + +template <class Predicate = Identity, class Until = detail::Until<Predicate>> +Until until(Predicate pred = Predicate()) { + return Until(std::move(pred)); +} + +template < + class Predicate = Identity, + class TakeWhile = detail::Until<Negate<Predicate>>> +TakeWhile takeWhile(Predicate pred = Predicate()) { + return TakeWhile(Negate<Predicate>(std::move(pred))); +} + +template < + class Selector = Identity, + class Comparer = Less, + class Order = detail::Order<Selector, Comparer>> +Order orderBy(Selector selector = Selector(), Comparer comparer = Comparer()) { + return Order(std::move(selector), std::move(comparer)); +} + +template < + class Selector = Identity, + class Order = detail::Order<Selector, Greater>> +Order orderByDescending(Selector selector = Selector()) { + return Order(std::move(selector)); +} + +template <class Selector = Identity, class GroupBy = detail::GroupBy<Selector>> +GroupBy groupBy(Selector selector = Selector()) { + return GroupBy(std::move(selector)); +} + +template < + class Selector = Identity, + class GroupByAdjacent = detail::GroupByAdjacent<Selector>> +GroupByAdjacent groupByAdjacent(Selector selector = Selector()) { + return GroupByAdjacent(std::move(selector)); +} + +template < + class Selector = Identity, + class Distinct = detail::Distinct<Selector>> +Distinct distinctBy(Selector selector = Selector()) { + return Distinct(std::move(selector)); +} + +template <int n, class Get = detail::Map<Get<n>>> +Get get() { + return Get(); +} + +// construct Dest from each value +template <class Dest, class Cast = detail::Map<Cast<Dest>>> +Cast eachAs() { + return Cast(); +} + +// call folly::to on each value +template <class Dest, class EachTo = detail::Map<To<Dest>>> +EachTo eachTo() { + return EachTo(); +} + +// call folly::tryTo on each value +template <class Dest, class EachTryTo = detail::Map<TryTo<Dest>>> +EachTryTo eachTryTo() { + return EachTryTo(); +} + +template <class Value> +detail::TypeAssertion<Value> assert_type() { + return {}; +} + +/* + * Sink Factories + */ + +/** + * any() - For determining if any value in a sequence satisfies a predicate. + * + * The following is an example for checking if any computer is broken: + * + * bool schrepIsMad = from(computers) | any(isBroken); + * + * (because everyone knows Schrep hates broken computers). + * + * Note that if no predicate is provided, 'any()' checks if any of the values + * are true when cased to bool. To check if any of the scores are nonZero: + * + * bool somebodyScored = from(scores) | any(); + * + * Note: Passing an empty sequence through 'any()' will always return false. In + * fact, 'any()' is equivilent to the composition of 'filter()' and 'notEmpty'. + * + * from(source) | any(pred) == from(source) | filter(pred) | notEmpty + */ + +template < + class Predicate = Identity, + class Filter = detail::Filter<Predicate>, + class NotEmpty = detail::IsEmpty<false>, + class Composed = detail::Composed<Filter, NotEmpty>> +Composed any(Predicate pred = Predicate()) { + return Composed(Filter(std::move(pred)), NotEmpty()); +} + +/** + * all() - For determining whether all values in a sequence satisfy a predicate. + * + * The following is an example for checking if all members of a team are cool: + * + * bool isAwesomeTeam = from(team) | all(isCool); + * + * Note that if no predicate is provided, 'all()'' checks if all of the values + * are true when cased to bool. + * The following makes sure none of 'pointers' are nullptr: + * + * bool allNonNull = from(pointers) | all(); + * + * Note: Passing an empty sequence through 'all()' will always return true. In + * fact, 'all()' is equivilent to the composition of 'filter()' with the + * reversed predicate and 'isEmpty'. + * + * from(source) | all(pred) == from(source) | filter(negate(pred)) | isEmpty + */ +template < + class Predicate = Identity, + class Filter = detail::Filter<Negate<Predicate>>, + class IsEmpty = detail::IsEmpty<true>, + class Composed = detail::Composed<Filter, IsEmpty>> +Composed all(Predicate pred = Predicate()) { + return Composed(Filter(std::move(negate(pred))), IsEmpty()); +} + +template <class Seed, class Fold, class FoldLeft = detail::FoldLeft<Seed, Fold>> +FoldLeft foldl(Seed seed = Seed(), Fold fold = Fold()) { + return FoldLeft(std::move(seed), std::move(fold)); +} + +template <class Reducer, class Reduce = detail::Reduce<Reducer>> +Reduce reduce(Reducer reducer = Reducer()) { + return Reduce(std::move(reducer)); +} + +template <class Selector = Identity, class Min = detail::Min<Selector, Less>> +Min minBy(Selector selector = Selector()) { + return Min(std::move(selector)); +} + +template <class Selector, class MaxBy = detail::Min<Selector, Greater>> +MaxBy maxBy(Selector selector = Selector()) { + return MaxBy(std::move(selector)); +} + +template <class Collection, class Collect = detail::Collect<Collection>> +Collect as() { + return Collect(); +} + +template < + template <class, class> class Container = std::vector, + template <class> class Allocator = std::allocator, + class Collect = detail::CollectTemplate<Container, Allocator>> +Collect as() { + return Collect(); +} + +template <class Collection, class Append = detail::Append<Collection>> +Append appendTo(Collection& collection) { + return Append(&collection); +} + +template < + class Needle, + class Contains = detail::Contains<typename std::decay<Needle>::type>> +Contains contains(Needle&& needle) { + return Contains(std::forward<Needle>(needle)); +} + +template < + class Exception, + class ErrorHandler, + class GuardImpl = + detail::GuardImpl<Exception, typename std::decay<ErrorHandler>::type>> +GuardImpl guard(ErrorHandler&& handler) { + return GuardImpl(std::forward<ErrorHandler>(handler)); +} + +template < + class Fallback, + class UnwrapOr = detail::UnwrapOr<typename std::decay<Fallback>::type>> +UnwrapOr unwrapOr(Fallback&& fallback) { + return UnwrapOr(std::forward<Fallback>(fallback)); +} + +} // namespace gen +} // namespace folly + +#include <folly/gen/Base-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/Combine-inl.h b/ios/Pods/Flipper-Folly/folly/gen/Combine-inl.h new file mode 100644 index 000000000..b69279b94 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Combine-inl.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_COMBINE_H_ +#error This file may only be included from folly/gen/Combine.h +#endif + +#include <iterator> +#include <system_error> +#include <tuple> +#include <type_traits> + +namespace folly { +namespace gen { +namespace detail { + +/** + * Interleave + * + * Alternate values from a sequence with values from a sequence container. + * Stops once we run out of values from either source. + */ +template <class Container> +class Interleave : public Operator<Interleave<Container>> { + // see comment about copies in CopiedSource + const std::shared_ptr<Container> container_; + + public: + explicit Interleave(Container container) + : container_(new Container(std::move(container))) {} + + template <class Value, class Source> + class Generator : public GenImpl<Value, Generator<Value, Source>> { + Source source_; + const std::shared_ptr<Container> container_; + + public: + explicit Generator( + Source source, + const std::shared_ptr<Container> container) + : source_(std::move(source)), container_(container) {} + + template <class Handler> + bool apply(Handler&& handler) const { + auto iter = container_->begin(); + return source_.apply([&](Value value) -> bool { + if (iter == container_->end()) { + return false; + } + if (!handler(std::forward<Value>(value))) { + return false; + } + if (!handler(std::move(*iter))) { + return false; + } + iter++; + return true; + }); + } + }; + + template <class Value2, class Source, class Gen = Generator<Value2, Source>> + Gen compose(GenImpl<Value2, Source>&& source) const { + return Gen(std::move(source.self()), container_); + } + + template <class Value2, class Source, class Gen = Generator<Value2, Source>> + Gen compose(const GenImpl<Value2, Source>& source) const { + return Gen(source.self(), container_); + } +}; + +/** + * Zip + * + * Combine inputs from Source with values from a sequence container by merging + * them into a tuple. + * + */ +template <class Container> +class Zip : public Operator<Zip<Container>> { + // see comment about copies in CopiedSource + const std::shared_ptr<Container> container_; + + public: + explicit Zip(Container container) + : container_(new Container(std::move(container))) {} + + template < + class Value, + class Source, + class Result = std::tuple< + typename std::decay<Value>::type, + typename std::decay<typename Container::value_type>::type>> + class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { + Source source_; + const std::shared_ptr<Container> container_; + + public: + explicit Generator( + Source source, + const std::shared_ptr<Container> container) + : source_(std::move(source)), container_(container) {} + + template <class Handler> + bool apply(Handler&& handler) const { + auto iter = container_->begin(); + return (source_.apply([&](Value value) -> bool { + if (iter == container_->end()) { + return false; + } + if (!handler(std::make_tuple( + std::forward<Value>(value), std::move(*iter)))) { + return false; + } + ++iter; + return true; + })); + } + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), container_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), container_); + } +}; + +template <class... Types1, class... Types2> +auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2) + -> std::tuple<Types1..., Types2...> { + return std::tuple_cat(std::move(t1), std::move(t2)); +} + +template <class... Types1, class Type2> +auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) -> decltype( + std::tuple_cat(std::move(t1), std::make_tuple(std::forward<Type2>(t2)))) { + return std::tuple_cat( + std::move(t1), std::make_tuple(std::forward<Type2>(t2))); +} + +template <class Type1, class... Types2> +auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) -> decltype( + std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)), std::move(t2))) { + return std::tuple_cat( + std::make_tuple(std::forward<Type1>(t1)), std::move(t2)); +} + +template <class Type1, class Type2> +auto add_to_tuple(Type1&& t1, Type2&& t2) -> decltype( + std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2))) { + return std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2)); +} + +// Merges a 2-tuple into a single tuple (get<0> could already be a tuple) +class MergeTuples { + public: + template <class Tuple> + auto operator()(Tuple&& value) const -> decltype(add_to_tuple( + std::get<0>(std::forward<Tuple>(value)), + std::get<1>(std::forward<Tuple>(value)))) { + static_assert( + std::tuple_size<typename std::remove_reference<Tuple>::type>::value == + 2, + "Can only merge tuples of size 2"); + return add_to_tuple( + std::get<0>(std::forward<Tuple>(value)), + std::get<1>(std::forward<Tuple>(value))); + } +}; + +} // namespace detail + +// TODO(mcurtiss): support zip() for N>1 operands. +template < + class Source, + class Zip = detail::Zip<typename std::decay<Source>::type>> +Zip zip(Source&& source) { + return Zip(std::forward<Source>(source)); +} + +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/Combine.h b/ios/Pods/Flipper-Folly/folly/gen/Combine.h new file mode 100644 index 000000000..38c90412a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Combine.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_COMBINE_H_ + +#include <folly/gen/Base.h> + +namespace folly { +namespace gen { +namespace detail { + +template <class Container> +class Interleave; + +template <class Container> +class Zip; + +} // namespace detail + +template < + class Source2, + class Source2Decayed = typename std::decay<Source2>::type, + class Interleave = detail::Interleave<Source2Decayed>> +Interleave interleave(Source2&& source2) { + return Interleave(std::forward<Source2>(source2)); +} + +} // namespace gen +} // namespace folly + +#include <folly/gen/Combine-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/Core-inl.h b/ios/Pods/Flipper-Folly/folly/gen/Core-inl.h new file mode 100644 index 000000000..79eb1811e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Core-inl.h @@ -0,0 +1,378 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_CORE_H_ +#error This file may only be included from folly/gen/Core.h +#endif + +#include <type_traits> +#include <utility> + +#include <folly/Portability.h> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { +namespace gen { + +/** + * IsCompatibleSignature - Trait type for testing whether a given Functor + * matches an expected signature. + * + * Usage: + * IsCompatibleSignature<FunctorType, bool(int, float)>::value + */ +template <class Candidate, class Expected> +class IsCompatibleSignature { + static constexpr bool value = false; +}; + +template <class Candidate, class ExpectedReturn, class... ArgTypes> +class IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> { + template < + class F, + class ActualReturn = + decltype(std::declval<F>()(std::declval<ArgTypes>()...)), + bool good = std::is_same<ExpectedReturn, ActualReturn>::value> + static constexpr bool testArgs(int*) { + return good; + } + + template <class F> + static constexpr bool testArgs(...) { + return false; + } + + public: + static constexpr bool value = testArgs<Candidate>(nullptr); +}; + +/** + * FBounded - Helper type for the curiously recurring template pattern, used + * heavily here to enable inlining and obviate virtual functions + */ +template <class Self> +struct FBounded { + const Self& self() const { + return *static_cast<const Self*>(this); + } + + Self& self() { + return *static_cast<Self*>(this); + } +}; + +/** + * Operator - Core abstraction of an operation which may be applied to a + * generator. All operators implement a method compose(), which takes a + * generator and produces an output generator. + */ +template <class Self> +class Operator : public FBounded<Self> { + public: + /** + * compose() - Must be implemented by child class to compose a new Generator + * out of a given generator. This function left intentionally unimplemented. + */ + template <class Source, class Value, class ResultGen = void> + ResultGen compose(const GenImpl<Value, Source>& source) const; + + protected: + Operator() = default; + Operator(Operator&&) noexcept = default; + Operator(const Operator&) = default; + Operator& operator=(Operator&&) noexcept = default; + Operator& operator=(const Operator&) = default; +}; + +/** + * operator|() - For composing two operators without binding it to a + * particular generator. + */ +template < + class Left, + class Right, + class Composed = detail::Composed<Left, Right>> +Composed operator|(const Operator<Left>& left, const Operator<Right>& right) { + return Composed(left.self(), right.self()); +} + +template < + class Left, + class Right, + class Composed = detail::Composed<Left, Right>> +Composed operator|(const Operator<Left>& left, Operator<Right>&& right) { + return Composed(left.self(), std::move(right.self())); +} + +template < + class Left, + class Right, + class Composed = detail::Composed<Left, Right>> +Composed operator|(Operator<Left>&& left, const Operator<Right>& right) { + return Composed(std::move(left.self()), right.self()); +} + +template < + class Left, + class Right, + class Composed = detail::Composed<Left, Right>> +Composed operator|(Operator<Left>&& left, Operator<Right>&& right) { + return Composed(std::move(left.self()), std::move(right.self())); +} + +/** + * GenImpl - Core abstraction of a generator, an object which produces values by + * passing them to a given handler lambda. All generator implementations must + * implement apply(). foreach() may also be implemented to special case the + * condition where the entire sequence is consumed. + */ +template <class Value, class Self> +class GenImpl : public FBounded<Self> { + protected: + // To prevent slicing + GenImpl() = default; + GenImpl(GenImpl&&) = default; + GenImpl(const GenImpl&) = default; + GenImpl& operator=(GenImpl&&) = default; + GenImpl& operator=(const GenImpl&) = default; + + public: + typedef Value ValueType; + typedef typename std::decay<Value>::type StorageType; + + /** + * apply() - Send all values produced by this generator to given handler until + * the handler returns false. Returns false if and only if the handler passed + * in returns false. Note: It should return true even if it completes (without + * the handler returning false), as 'Chain' uses the return value of apply to + * determine if it should process the second object in its chain. + */ + template <class Handler> + bool apply(Handler&& handler) const; + + /** + * foreach() - Send all values produced by this generator to given lambda. + */ + template <class Body> + void foreach(Body&& body) const { + this->self().apply([&](Value value) -> bool { + static_assert(!infinite, "Cannot call foreach on infinite GenImpl"); + body(std::forward<Value>(value)); + return true; + }); + } + + // Child classes should override if the sequence generated is *definitely* + // infinite. 'infinite' may be false_type for some infinite sequences + // (due the the Halting Problem). + // + // In general, almost all sources are finite (only seq(n) produces an infinite + // source), almost all operators keep the finiteness of the source (only cycle + // makes an infinite generator from a finite one, only until and take make a + // finite generator from an infinite one, and concat needs both the inner and + // outer generators to be finite to make a finite one), and most sinks + // cannot accept and infinite generators (first being the expection). + static constexpr bool infinite = false; +}; + +template < + class LeftValue, + class Left, + class RightValue, + class Right, + class Chain = detail::Chain<LeftValue, Left, Right>> +Chain operator+( + const GenImpl<LeftValue, Left>& left, + const GenImpl<RightValue, Right>& right) { + static_assert( + std::is_same<LeftValue, RightValue>::value, + "Generators may ony be combined if Values are the exact same type."); + return Chain(left.self(), right.self()); +} + +template < + class LeftValue, + class Left, + class RightValue, + class Right, + class Chain = detail::Chain<LeftValue, Left, Right>> +Chain operator+( + const GenImpl<LeftValue, Left>& left, + GenImpl<RightValue, Right>&& right) { + static_assert( + std::is_same<LeftValue, RightValue>::value, + "Generators may ony be combined if Values are the exact same type."); + return Chain(left.self(), std::move(right.self())); +} + +template < + class LeftValue, + class Left, + class RightValue, + class Right, + class Chain = detail::Chain<LeftValue, Left, Right>> +Chain operator+( + GenImpl<LeftValue, Left>&& left, + const GenImpl<RightValue, Right>& right) { + static_assert( + std::is_same<LeftValue, RightValue>::value, + "Generators may ony be combined if Values are the exact same type."); + return Chain(std::move(left.self()), right.self()); +} + +template < + class LeftValue, + class Left, + class RightValue, + class Right, + class Chain = detail::Chain<LeftValue, Left, Right>> +Chain operator+( + GenImpl<LeftValue, Left>&& left, + GenImpl<RightValue, Right>&& right) { + static_assert( + std::is_same<LeftValue, RightValue>::value, + "Generators may ony be combined if Values are the exact same type."); + return Chain(std::move(left.self()), std::move(right.self())); +} + +/** + * operator|() which enables foreach-like usage: + * gen | [](Value v) -> void {...}; + */ +template <class Value, class Gen, class Handler> +typename std::enable_if< + IsCompatibleSignature<Handler, void(Value)>::value>::type +operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) { + static_assert( + !Gen::infinite, "Cannot pull all values from an infinite sequence."); + gen.self().foreach(std::forward<Handler>(handler)); +} + +/** + * operator|() which enables foreach-like usage with 'break' support: + * gen | [](Value v) -> bool { return shouldContinue(); }; + */ +template <class Value, class Gen, class Handler> +typename std:: + enable_if<IsCompatibleSignature<Handler, bool(Value)>::value, bool>::type + operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) { + return gen.self().apply(std::forward<Handler>(handler)); +} + +/** + * operator|() for composing generators with operators, similar to boosts' range + * adaptors: + * gen | map(square) | sum + */ +template <class Value, class Gen, class Op> +auto operator|(const GenImpl<Value, Gen>& gen, const Operator<Op>& op) + -> decltype(op.self().compose(gen.self())) { + return op.self().compose(gen.self()); +} + +template <class Value, class Gen, class Op> +auto operator|(GenImpl<Value, Gen>&& gen, const Operator<Op>& op) + -> decltype(op.self().compose(std::move(gen.self()))) { + return op.self().compose(std::move(gen.self())); +} + +namespace detail { + +/** + * Composed - For building up a pipeline of operations to perform, absent any + * particular source generator. Useful for building up custom pipelines. + * + * This type is usually used by just piping two operators together: + * + * auto valuesOf = filter([](Optional<int>& o) { return o.hasValue(); }) + * | map([](Optional<int>& o) -> int& { return o.value(); }); + * + * auto valuesIncluded = from(optionals) | valuesOf | as<vector>(); + */ +template <class First, class Second> +class Composed : public Operator<Composed<First, Second>> { + First first_; + Second second_; + + public: + Composed() = default; + + Composed(First first, Second second) + : first_(std::move(first)), second_(std::move(second)) {} + + template < + class Source, + class Value, + class FirstRet = + decltype(std::declval<First>().compose(std::declval<Source>())), + class SecondRet = + decltype(std::declval<Second>().compose(std::declval<FirstRet>()))> + SecondRet compose(const GenImpl<Value, Source>& source) const { + return second_.compose(first_.compose(source.self())); + } + + template < + class Source, + class Value, + class FirstRet = + decltype(std::declval<First>().compose(std::declval<Source>())), + class SecondRet = + decltype(std::declval<Second>().compose(std::declval<FirstRet>()))> + SecondRet compose(GenImpl<Value, Source>&& source) const { + return second_.compose(first_.compose(std::move(source.self()))); + } +}; + +/** + * Chain - For concatenating the values produced by two Generators. + * + * This type is primarily used through using '+' to combine generators, like: + * + * auto nums = seq(1, 10) + seq(20, 30); + * int total = nums | sum; + */ +template <class Value, class First, class Second> +class Chain : public GenImpl<Value, Chain<Value, First, Second>> { + First first_; + Second second_; + + public: + explicit Chain(First first, Second second) + : first_(std::move(first)), second_(std::move(second)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + return first_.apply(std::forward<Handler>(handler)) && + second_.apply(std::forward<Handler>(handler)); + } + + template <class Body> + void foreach(Body&& body) const { + first_.foreach(std::forward<Body>(body)); + second_.foreach(std::forward<Body>(body)); + } + + static constexpr bool infinite = First::infinite || Second::infinite; +}; + +} // namespace detail +} // namespace gen +} // namespace folly + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/gen/Core.h b/ios/Pods/Flipper-Folly/folly/gen/Core.h new file mode 100644 index 000000000..d5317c347 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Core.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_CORE_H_ + +namespace folly { +namespace gen { + +template <class Value, class Self> +class GenImpl; + +template <class Self> +class Operator; + +namespace detail { + +template <class Self> +struct FBounded; + +template <class First, class Second> +class Composed; + +template <class Value, class First, class Second> +class Chain; + +} // namespace detail +} // namespace gen +} // namespace folly + +#include <folly/gen/Core-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/File-inl.h b/ios/Pods/Flipper-Folly/folly/gen/File-inl.h new file mode 100644 index 000000000..43f171e16 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/File-inl.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_FILE_H_ +#error This file may only be included from folly/gen/File.h +#endif + +#include <system_error> + +#include <folly/gen/String.h> + +namespace folly { +namespace gen { +namespace detail { + +class FileReader : public GenImpl<ByteRange, FileReader> { + public: + FileReader(File file, std::unique_ptr<IOBuf> buffer) + : file_(std::move(file)), buffer_(std::move(buffer)) { + buffer_->clear(); + } + + template <class Body> + bool apply(Body&& body) const { + for (;;) { + ssize_t n; + do { + n = ::read(file_.fd(), buffer_->writableTail(), buffer_->capacity()); + } while (n == -1 && errno == EINTR); + if (n == -1) { + throw std::system_error(errno, std::system_category(), "read failed"); + } + if (n == 0) { + return true; + } + if (!body(ByteRange(buffer_->tail(), size_t(n)))) { + return false; + } + } + } + + // Technically, there could be infinite files (e.g. /dev/random), but people + // who open those can do so at their own risk. + static constexpr bool infinite = false; + + private: + File file_; + std::unique_ptr<IOBuf> buffer_; +}; + +class FileWriter : public Operator<FileWriter> { + public: + FileWriter(File file, std::unique_ptr<IOBuf> buffer) + : file_(std::move(file)), buffer_(std::move(buffer)) { + if (buffer_) { + buffer_->clear(); + } + } + + template <class Source, class Value> + void compose(const GenImpl<Value, Source>& source) const { + auto fn = [&](ByteRange v) { + if (!this->buffer_ || v.size() >= this->buffer_->capacity()) { + this->flushBuffer(); + this->write(v); + } else { + if (v.size() > this->buffer_->tailroom()) { + this->flushBuffer(); + } + memcpy(this->buffer_->writableTail(), v.data(), v.size()); + this->buffer_->append(v.size()); + } + }; + + // Iterate + source.foreach(std::move(fn)); + + flushBuffer(); + file_.close(); + } + + private: + void write(ByteRange v) const { + ssize_t n; + while (!v.empty()) { + do { + n = ::write(file_.fd(), v.data(), v.size()); + } while (n == -1 && errno == EINTR); + if (n == -1) { + throw std::system_error( + errno, std::system_category(), "write() failed"); + } + v.advance(size_t(n)); + } + } + + void flushBuffer() const { + if (buffer_ && buffer_->length() != 0) { + write(ByteRange(buffer_->data(), buffer_->length())); + buffer_->clear(); + } + } + + mutable File file_; + std::unique_ptr<IOBuf> buffer_; +}; + +inline auto byLineImpl(File file, char delim, bool keepDelimiter) { + // clang-format off + return fromFile(std::move(file)) + | eachAs<StringPiece>() + | resplit(delim, keepDelimiter); + // clang-format on +} + +} // namespace detail + +/** + * Generator which reads lines from a file. + * Note: This produces StringPieces which reference temporary strings which are + * only valid during iteration. + */ +inline auto byLineFull(File file, char delim = '\n') { + return detail::byLineImpl(std::move(file), delim, true); +} + +inline auto byLineFull(int fd, char delim = '\n') { + return byLineFull(File(fd), delim); +} + +inline auto byLineFull(const char* f, char delim = '\n') { + return byLineFull(File(f), delim); +} + +inline auto byLine(File file, char delim = '\n') { + return detail::byLineImpl(std::move(file), delim, false); +} + +inline auto byLine(int fd, char delim = '\n') { + return byLine(File(fd), delim); +} + +inline auto byLine(const char* f, char delim = '\n') { + return byLine(File(f), delim); +} + +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/File.h b/ios/Pods/Flipper-Folly/folly/gen/File.h new file mode 100644 index 000000000..5beb92dd9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/File.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_FILE_H_ + +#include <folly/File.h> +#include <folly/gen/Base.h> +#include <folly/io/IOBuf.h> + +namespace folly { +namespace gen { + +namespace detail { +class FileReader; +class FileWriter; +} // namespace detail + +/** + * Generator that reads from a file with a buffer of the given size. + * Reads must be buffered (the generator interface expects the generator + * to hold each value). + */ +template <class S = detail::FileReader> +S fromFile(File file, size_t bufferSize = 4096) { + return S(std::move(file), IOBuf::create(bufferSize)); +} + +/** + * Generator that reads from a file using a given buffer. + */ +template <class S = detail::FileReader> +S fromFile(File file, std::unique_ptr<IOBuf> buffer) { + return S(std::move(file), std::move(buffer)); +} + +/** + * Sink that writes to a file with a buffer of the given size. + * If bufferSize is 0, writes will be unbuffered. + */ +template <class S = detail::FileWriter> +S toFile(File file, size_t bufferSize = 4096) { + return S(std::move(file), bufferSize ? nullptr : IOBuf::create(bufferSize)); +} + +/** + * Sink that writes to a file using a given buffer. + * If the buffer is nullptr, writes will be unbuffered. + */ +template <class S = detail::FileWriter> +S toFile(File file, std::unique_ptr<IOBuf> buffer) { + return S(std::move(file), std::move(buffer)); +} +} // namespace gen +} // namespace folly + +#include <folly/gen/File-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/IStream.h b/ios/Pods/Flipper-Folly/folly/gen/IStream.h new file mode 100644 index 000000000..6941e9a7f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/IStream.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <istream> +#include <string> + +#include <folly/gen/Core.h> + +namespace folly { +namespace gen { +namespace detail { + +/** + * Generates lines by calling std::getline() on a given istream. + */ +class IStreamByLine : public GenImpl<std::string&&, IStreamByLine> { + public: + IStreamByLine(std::istream& in) : in_(in) {} + + template <class Body> + bool apply(Body&& body) const { + for (std::string line; std::getline(in_, line);) { + if (!body(std::move(line))) { + return false; + } + } + return true; + } + + // Technically, there could be infinite files (e.g. /dev/random), but people + // who open those can do so at their own risk. + static constexpr bool infinite = false; + + private: + std::istream& in_; +}; + +} // namespace detail + +inline detail::IStreamByLine byLine(std::istream& in) { + return detail::IStreamByLine(in); +} + +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/Parallel-inl.h b/ios/Pods/Flipper-Folly/folly/gen/Parallel-inl.h new file mode 100644 index 000000000..801e33637 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Parallel-inl.h @@ -0,0 +1,438 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_PARALLEL_H_ +#error This file may only be included from folly/gen/Parallel.h +#endif + +#include <folly/MPMCQueue.h> +#include <folly/ScopeGuard.h> +#include <folly/experimental/EventCount.h> +#include <atomic> +#include <thread> +#include <vector> + +namespace folly { +namespace gen { +namespace detail { + +template <typename T> +class ClosableMPMCQueue { + MPMCQueue<T> queue_; + std::atomic<size_t> producers_{0}; + std::atomic<size_t> consumers_{0}; + folly::EventCount wakeProducer_; + folly::EventCount wakeConsumer_; + + public: + explicit ClosableMPMCQueue(size_t capacity) : queue_(capacity) {} + + ~ClosableMPMCQueue() { + CHECK(!producers()); + CHECK(!consumers()); + } + + void openProducer() { + ++producers_; + } + void openConsumer() { + ++consumers_; + } + + void closeInputProducer() { + size_t producers = producers_--; + CHECK(producers); + if (producers == 1) { // last producer + wakeConsumer_.notifyAll(); + } + } + + void closeOutputConsumer() { + size_t consumers = consumers_--; + CHECK(consumers); + if (consumers == 1) { // last consumer + wakeProducer_.notifyAll(); + } + } + + size_t producers() const { + return producers_.load(std::memory_order_acquire); + } + + size_t consumers() const { + return consumers_.load(std::memory_order_acquire); + } + + template <typename... Args> + bool writeUnlessFull(Args&&... args) noexcept { + if (queue_.write(std::forward<Args>(args)...)) { + // wake consumers to pick up new value + wakeConsumer_.notify(); + return true; + } + return false; + } + + template <typename... Args> + bool writeUnlessClosed(Args&&... args) { + // write if there's room + if (!queue_.writeIfNotFull(std::forward<Args>(args)...)) { + while (true) { + auto key = wakeProducer_.prepareWait(); + // if write fails, check if there are still consumers listening + if (!consumers()) { + // no consumers left; bail out + wakeProducer_.cancelWait(); + return false; + } + if (queue_.writeIfNotFull(std::forward<Args>(args)...)) { + wakeProducer_.cancelWait(); + break; + } + wakeProducer_.wait(key); + } + } + // wake consumers to pick up new value + wakeConsumer_.notify(); + return true; + } + + bool readUnlessEmpty(T& out) { + if (queue_.read(out)) { + // wake producers to fill empty space + wakeProducer_.notify(); + return true; + } + return false; + } + + bool readUnlessClosed(T& out) { + if (!queue_.readIfNotEmpty(out)) { + while (true) { + auto key = wakeConsumer_.prepareWait(); + if (queue_.readIfNotEmpty(out)) { + wakeConsumer_.cancelWait(); + break; + } + if (!producers()) { + wakeConsumer_.cancelWait(); + // wake producers to fill empty space + wakeProducer_.notify(); + return false; + } + wakeConsumer_.wait(key); + } + } + // wake writers blocked by full queue + wakeProducer_.notify(); + return true; + } +}; + +template <class Sink> +class Sub : public Operator<Sub<Sink>> { + Sink sink_; + + public: + explicit Sub(Sink sink) : sink_(sink) {} + + template < + class Value, + class Source, + class Result = + decltype(std::declval<Sink>().compose(std::declval<Source>())), + class Just = SingleCopy<typename std::decay<Result>::type>> + Just compose(const GenImpl<Value, Source>& source) const { + return Just(source | sink_); + } +}; + +template <class Ops> +class Parallel : public Operator<Parallel<Ops>> { + Ops ops_; + size_t threads_; + + public: + Parallel(Ops ops, size_t threads) : ops_(std::move(ops)), threads_(threads) {} + + template < + class Input, + class Source, + class InputDecayed = typename std::decay<Input>::type, + class Composed = + decltype(std::declval<Ops>().compose(Empty<InputDecayed&&>())), + class Output = typename Composed::ValueType, + class OutputDecayed = typename std::decay<Output>::type> + class Generator : public GenImpl< + OutputDecayed&&, + Generator< + Input, + Source, + InputDecayed, + Composed, + Output, + OutputDecayed>> { + Source source_; + Ops ops_; + size_t threads_; + + using InQueue = ClosableMPMCQueue<InputDecayed>; + using OutQueue = ClosableMPMCQueue<OutputDecayed>; + + class Puller : public GenImpl<InputDecayed&&, Puller> { + InQueue* queue_; + + public: + explicit Puller(InQueue* queue) : queue_(queue) {} + + template <class Handler> + bool apply(Handler&& handler) const { + InputDecayed input; + while (queue_->readUnlessClosed(input)) { + if (!handler(std::move(input))) { + return false; + } + } + return true; + } + + template <class Body> + void foreach(Body&& body) const { + InputDecayed input; + while (queue_->readUnlessClosed(input)) { + body(std::move(input)); + } + } + }; + + template <bool all = false> + class Pusher : public Operator<Pusher<all>> { + OutQueue* queue_; + + public: + explicit Pusher(OutQueue* queue) : queue_(queue) {} + + template <class Value, class InnerSource> + void compose(const GenImpl<Value, InnerSource>& source) const { + if (all) { + source.self().foreach([&](Value value) { + queue_->writeUnlessClosed(std::forward<Value>(value)); + }); + } else { + source.self().apply([&](Value value) { + return queue_->writeUnlessClosed(std::forward<Value>(value)); + }); + } + } + }; + + template <bool all = false> + class Executor { + InQueue inQueue_; + OutQueue outQueue_; + Puller puller_; + Pusher<all> pusher_; + std::vector<std::thread> workers_; + const Ops* ops_; + + void work() { + puller_ | *ops_ | pusher_; + } + + public: + Executor(size_t threads, const Ops* ops) + : inQueue_(threads * 4), + outQueue_(threads * 4), + puller_(&inQueue_), + pusher_(&outQueue_), + ops_(ops) { + inQueue_.openProducer(); + outQueue_.openConsumer(); + for (size_t t = 0; t < threads; ++t) { + inQueue_.openConsumer(); + outQueue_.openProducer(); + workers_.emplace_back([this] { + SCOPE_EXIT { + inQueue_.closeOutputConsumer(); + outQueue_.closeInputProducer(); + }; + this->work(); + }); + } + } + + ~Executor() { + if (inQueue_.producers()) { + inQueue_.closeInputProducer(); + } + if (outQueue_.consumers()) { + outQueue_.closeOutputConsumer(); + } + while (!workers_.empty()) { + workers_.back().join(); + workers_.pop_back(); + } + CHECK(!inQueue_.consumers()); + CHECK(!outQueue_.producers()); + } + + void closeInputProducer() { + inQueue_.closeInputProducer(); + } + + void closeOutputConsumer() { + outQueue_.closeOutputConsumer(); + } + + bool writeUnlessClosed(Input&& input) { + return inQueue_.writeUnlessClosed(std::forward<Input>(input)); + } + + bool writeUnlessFull(Input&& input) { + return inQueue_.writeUnlessFull(std::forward<Input>(input)); + } + + bool readUnlessClosed(OutputDecayed& output) { + return outQueue_.readUnlessClosed(output); + } + + bool readUnlessEmpty(OutputDecayed& output) { + return outQueue_.readUnlessEmpty(output); + } + }; + + public: + Generator(Source source, Ops ops, size_t threads) + : source_(std::move(source)), + ops_(std::move(ops)), + threads_( + threads + ? threads + : size_t(std::max<long>(1, sysconf(_SC_NPROCESSORS_CONF)))) {} + + template <class Handler> + bool apply(Handler&& handler) const { + Executor<false> executor(threads_, &ops_); + bool more = true; + source_.apply([&](Input input) { + if (executor.writeUnlessFull(std::forward<Input>(input))) { + return true; + } + OutputDecayed output; + while (executor.readUnlessEmpty(output)) { + if (!handler(std::move(output))) { + more = false; + return false; + } + } + if (!executor.writeUnlessClosed(std::forward<Input>(input))) { + return false; + } + return true; + }); + executor.closeInputProducer(); + + if (more) { + OutputDecayed output; + while (executor.readUnlessClosed(output)) { + if (!handler(std::move(output))) { + more = false; + break; + } + } + } + executor.closeOutputConsumer(); + + return more; + } + + template <class Body> + void foreach(Body&& body) const { + Executor<true> executor(threads_, &ops_); + source_.foreach([&](Input input) { + if (executor.writeUnlessFull(std::forward<Input>(input))) { + return; + } + OutputDecayed output; + while (executor.readUnlessEmpty(output)) { + body(std::move(output)); + } + CHECK(executor.writeUnlessClosed(std::forward<Input>(input))); + }); + executor.closeInputProducer(); + + OutputDecayed output; + while (executor.readUnlessClosed(output)) { + body(std::move(output)); + } + executor.closeOutputConsumer(); + } + }; + + template <class Value, class Source> + Generator<Value, Source> compose(const GenImpl<Value, Source>& source) const { + return Generator<Value, Source>(source.self(), ops_, threads_); + } + + template <class Value, class Source> + Generator<Value, Source> compose(GenImpl<Value, Source>&& source) const { + return Generator<Value, Source>(std::move(source.self()), ops_, threads_); + } +}; + +/** + * ChunkedRangeSource - For slicing up ranges into a sequence of chunks given a + * maximum chunk size. + * + * Usually used through the 'chunked' helper, like: + * + * int n + * = chunked(values) + * | parallel // each thread processes a chunk + * | concat // but can still process values one at a time + * | filter(isPrime) + * | atomic_count; + */ +template <class Iterator> +class ChunkedRangeSource + : public GenImpl<RangeSource<Iterator>&&, ChunkedRangeSource<Iterator>> { + int chunkSize_; + Range<Iterator> range_; + + public: + ChunkedRangeSource() = default; + ChunkedRangeSource(int chunkSize, Range<Iterator> range) + : chunkSize_(chunkSize), range_(std::move(range)) {} + + template <class Handler> + bool apply(Handler&& handler) const { + auto remaining = range_; + while (!remaining.empty()) { + auto chunk = remaining.subpiece(0, chunkSize_); + remaining.advance(chunk.size()); + auto gen = RangeSource<Iterator>(chunk); + if (!handler(std::move(gen))) { + return false; + } + } + return true; + } +}; + +} // namespace detail + +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/Parallel.h b/ios/Pods/Flipper-Folly/folly/gen/Parallel.h new file mode 100644 index 000000000..cc9904955 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/Parallel.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_PARALLEL_H_ + +#include <mutex> + +#include <folly/gen/Base.h> + +namespace folly { +namespace gen { +namespace detail { + +template <class Ops> +class Parallel; + +template <class Sink> +class Sub; + +template <class Iterator> +class ChunkedRangeSource; + +} // namespace detail + +/** + * chunked() - For producing values from a container in slices. + * + * Especially for use with 'parallel()', chunked can be used to process values + * from a persistent container in chunks larger than one value at a time. The + * values produced are generators for slices of the input container. */ +template < + class Container, + class Iterator = typename Container::const_iterator, + class Chunked = detail::ChunkedRangeSource<Iterator>> +Chunked chunked(const Container& container, int chunkSize = 256) { + return Chunked(chunkSize, folly::range(container.begin(), container.end())); +} + +template < + class Container, + class Iterator = typename Container::iterator, + class Chunked = detail::ChunkedRangeSource<Iterator>> +Chunked chunked(Container& container, int chunkSize = 256) { + return Chunked(chunkSize, folly::range(container.begin(), container.end())); +} + +/** + * parallel - A parallelization operator. + * + * 'parallel(ops)' can be used with any generator to process a segment + * of the pipeline in parallel. Multiple threads are used to apply the + * operations ('ops') to the input sequence, with the resulting sequence + * interleaved to be processed on the client thread. + * + * auto scoredResults + * = from(ids) + * | parallel(map(fetchObj) | filter(isValid) | map(scoreObj)) + * | as<vector>(); + * + * Operators specified for parallel execution must yield sequences, not just + * individual values. If a sink function such as 'count' is desired, it must be + * wrapped in 'sub' to produce a subcount, since any such aggregation must be + * re-aggregated. + * + * auto matches + * = from(docs) + * | parallel(filter(expensiveTest) | sub(count)) + * | sum; + * + * Here, each thread counts its portion of the result, then the sub-counts are + * summed up to produce the total count. + */ +template <class Ops, class Parallel = detail::Parallel<Ops>> +Parallel parallel(Ops ops, size_t threads = 0) { + return Parallel(std::move(ops), threads); +} + +/** + * sub - For sub-summarization of a sequence. + * + * 'sub' can be used to apply a sink function to a generator, but wrap the + * single value in another generator. Note that the sink is eagerly evaluated on + * the input sequence. + * + * auto sum = from(list) | sub(count) | first; + * + * This is primarily used with 'parallel', as noted above. + */ +template <class Sink, class Sub = detail::Sub<Sink>> +Sub sub(Sink sink) { + return Sub(std::move(sink)); +} +} // namespace gen +} // namespace folly + +#include <folly/gen/Parallel-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/ParallelMap-inl.h b/ios/Pods/Flipper-Folly/folly/gen/ParallelMap-inl.h new file mode 100644 index 000000000..144c01f59 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/ParallelMap-inl.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_PARALLELMAP_H_ +#error This file may only be included from folly/gen/ParallelMap.h +#endif + +#include <atomic> +#include <cassert> +#include <exception> +#include <thread> +#include <type_traits> +#include <utility> +#include <vector> + +#include <folly/Expected.h> +#include <folly/MPMCPipeline.h> +#include <folly/experimental/EventCount.h> +#include <folly/functional/Invoke.h> + +namespace folly { +namespace gen { +namespace detail { + +/** + * PMap - Map in parallel (using threads). For producing a sequence of + * values by passing each value from a source collection through a + * predicate while running the predicate in parallel in different + * threads. + * + * This type is usually used through the 'pmap' helper function: + * + * auto squares = seq(1, 10) | pmap(fibonacci, 4) | sum; + */ +template <class Predicate> +class PMap : public Operator<PMap<Predicate>> { + Predicate pred_; + size_t nThreads_; + + public: + PMap() = default; + + PMap(Predicate pred, size_t nThreads) + : pred_(std::move(pred)), nThreads_(nThreads) {} + + template < + class Value, + class Source, + class Input = typename std::decay<Value>::type, + class Output = + typename std::decay<invoke_result_t<Predicate, Value>>::type> + class Generator + : public GenImpl<Output, Generator<Value, Source, Input, Output>> { + Source source_; + Predicate pred_; + const size_t nThreads_; + + using Result = folly::Expected<Output, std::exception_ptr>; + class ExecutionPipeline { + std::vector<std::thread> workers_; + std::atomic<bool> done_{false}; + const Predicate& pred_; + using Pipeline = MPMCPipeline<Input, Result>; + Pipeline pipeline_; + EventCount wake_; + + public: + ExecutionPipeline(const Predicate& pred, size_t nThreads) + : pred_(pred), pipeline_(nThreads, nThreads) { + workers_.reserve(nThreads); + for (size_t i = 0; i < nThreads; i++) { + workers_.push_back(std::thread([this] { this->predApplier(); })); + } + } + + ~ExecutionPipeline() { + assert(pipeline_.sizeGuess() == 0); + assert(done_.load()); + for (auto& w : workers_) { + w.join(); + } + } + + void stop() { + // prevent workers from consuming more than we produce. + done_.store(true, std::memory_order_release); + wake_.notifyAll(); + } + + bool write(Value&& value) { + bool wrote = pipeline_.write(std::forward<Value>(value)); + if (wrote) { + wake_.notify(); + } + return wrote; + } + + void blockingWrite(Value&& value) { + pipeline_.blockingWrite(std::forward<Value>(value)); + wake_.notify(); + } + + bool read(Result& result) { + return pipeline_.read(result); + } + + void blockingRead(Result& result) { + pipeline_.blockingRead(result); + } + + private: + void predApplier() { + // Each thread takes a value from the pipeline_, runs the + // predicate and enqueues the result. The pipeline preserves + // ordering. NOTE: don't use blockingReadStage<0> to read from + // the pipeline_ as there may not be any: end-of-data is signaled + // separately using done_/wake_. + Input in; + for (;;) { + auto key = wake_.prepareWait(); + + typename Pipeline::template Ticket<0> ticket; + if (pipeline_.template readStage<0>(ticket, in)) { + wake_.cancelWait(); + try { + Output out = pred_(std::move(in)); + pipeline_.template blockingWriteStage<0>(ticket, std::move(out)); + } catch (...) { + pipeline_.template blockingWriteStage<0>( + ticket, makeUnexpected(std::current_exception())); + } + continue; + } + + if (done_.load(std::memory_order_acquire)) { + wake_.cancelWait(); + break; + } + + // Not done_, but no items in the queue. + wake_.wait(key); + } + } + }; + + static Output&& getOutput(Result& result) { + if (result.hasError()) { + std::rethrow_exception(std::move(result).error()); + } + return std::move(result).value(); + } + + public: + Generator(Source source, const Predicate& pred, size_t nThreads) + : source_(std::move(source)), + pred_(pred), + nThreads_(nThreads ? nThreads : sysconf(_SC_NPROCESSORS_ONLN)) {} + + template <class Body> + void foreach(Body&& body) const { + ExecutionPipeline pipeline(pred_, nThreads_); + + size_t wrote = 0; + size_t read = 0; + source_.foreach([&](Value value) { + if (pipeline.write(std::forward<Value>(value))) { + // input queue not yet full, saturate it before we process + // anything downstream + ++wrote; + return; + } + + // input queue full; drain ready items from the queue + Result result; + while (pipeline.read(result)) { + ++read; + body(getOutput(result)); + } + + // write the value we were going to write before we made room. + pipeline.blockingWrite(std::forward<Value>(value)); + ++wrote; + }); + + pipeline.stop(); + + // flush the output queue + while (read < wrote) { + Result result; + pipeline.blockingRead(result); + ++read; + body(getOutput(result)); + } + } + + template <class Handler> + bool apply(Handler&& handler) const { + ExecutionPipeline pipeline(pred_, nThreads_); + + size_t wrote = 0; + size_t read = 0; + bool more = true; + source_.apply([&](Value value) { + if (pipeline.write(std::forward<Value>(value))) { + // input queue not yet full, saturate it before we process + // anything downstream + ++wrote; + return true; + } + + // input queue full; drain ready items from the queue + Result result; + while (pipeline.read(result)) { + ++read; + if (!handler(getOutput(result))) { + more = false; + return false; + } + } + + // write the value we were going to write before we made room. + pipeline.blockingWrite(std::forward<Value>(value)); + ++wrote; + return true; + }); + + pipeline.stop(); + + // flush the output queue + while (read < wrote) { + Result result; + pipeline.blockingRead(result); + ++read; + if (more && !handler(getOutput(result))) { + more = false; + } + } + return more; + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), pred_, nThreads_); + } + + template <class Source, class Value, class Gen = Generator<Value, Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), pred_, nThreads_); + } +}; +} // namespace detail +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/ParallelMap.h b/ios/Pods/Flipper-Folly/folly/gen/ParallelMap.h new file mode 100644 index 000000000..3b1bc3b42 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/ParallelMap.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_PARALLELMAP_H_ + +#include <folly/gen/Core.h> + +namespace folly { +namespace gen { + +namespace detail { + +template <class Predicate> +class PMap; + +} // namespace detail + +/** + * Run `pred` in parallel in nThreads. Results are returned in the + * same order in which they were retrieved from the source generator + * (similar to map). + * + * NOTE: Only `pred` is run from separate threads; the source + * generator and the rest of the pipeline is executed in the + * caller thread. + */ +template <class Predicate, class PMap = detail::PMap<Predicate>> +PMap pmap(Predicate pred = Predicate(), size_t nThreads = 0) { + return PMap(std::move(pred), nThreads); +} +} // namespace gen +} // namespace folly + +#include <folly/gen/ParallelMap-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/gen/String-inl.h b/ios/Pods/Flipper-Folly/folly/gen/String-inl.h new file mode 100644 index 000000000..86a174b08 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/String-inl.h @@ -0,0 +1,405 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_GEN_STRING_H_ +#error This file may only be included from folly/gen/String.h +#endif + +#include <folly/Conv.h> +#include <folly/Portability.h> +#include <folly/String.h> + +namespace folly { +namespace gen { +namespace detail { + +/** + * Finds the first occurrence of delimiter in "in", advances "in" past the + * delimiter. Populates "prefix" with the consumed bytes, including the + * delimiter. + * + * Returns the number of trailing bytes of "prefix" that make up the + * delimiter, or 0 if the delimiter was not found. + */ +inline size_t +splitPrefix(StringPiece& in, StringPiece& prefix, char delimiter) { + size_t found = in.find(delimiter); + if (found != StringPiece::npos) { + ++found; + prefix.assign(in.data(), in.data() + found); + in.advance(found); + return 1; + } + prefix.clear(); + return 0; +} + +/** + * As above, but supports multibyte delimiters. + */ +inline size_t +splitPrefix(StringPiece& in, StringPiece& prefix, StringPiece delimiter) { + auto found = in.find(delimiter); + if (found != StringPiece::npos) { + found += delimiter.size(); + prefix.assign(in.data(), in.data() + found); + in.advance(found); + return delimiter.size(); + } + prefix.clear(); + return 0; +} + +/** + * As above, but splits by any of the EOL terms: \r, \n, or \r\n. + */ +inline size_t splitPrefix(StringPiece& in, StringPiece& prefix, MixedNewlines) { + const auto kCRLF = "\r\n"; + const size_t kLenCRLF = 2; + + auto p = in.find_first_of(kCRLF); + if (p != std::string::npos) { + const auto in_start = in.data(); + size_t delim_len = 1; + in.advance(p); + // Either remove an MS-DOS CR-LF 2-byte newline, or eat 1 byte at a time. + if (in.removePrefix(kCRLF)) { + delim_len = kLenCRLF; + } else { + in.advance(delim_len); + } + prefix.assign(in_start, in.data()); + return delim_len; + } + prefix.clear(); + return 0; +} + +inline const char* ch(const unsigned char* p) { + return reinterpret_cast<const char*>(p); +} + +// Chop s into pieces of at most maxLength, feed them to cb +template <class Callback> +bool consumeFixedSizeChunks(Callback& cb, StringPiece& s, uint64_t maxLength) { + while (!s.empty()) { + auto num_to_add = s.size(); + if (maxLength) { + num_to_add = std::min<uint64_t>(num_to_add, maxLength); + } + if (!cb(StringPiece(s.begin(), num_to_add))) { + return false; + } + s.advance(num_to_add); + } + return true; +} + +// Consumes all of buffer, plus n chars from s. +template <class Callback> +bool consumeBufferPlus(Callback& cb, IOBuf& buf, StringPiece& s, uint64_t n) { + buf.reserve(0, n); + memcpy(buf.writableTail(), s.data(), n); + buf.append(n); + s.advance(n); + if (!cb(StringPiece(detail::ch(buf.data()), buf.length()))) { + return false; + } + buf.clear(); + return true; +} + +} // namespace detail + +template <class Callback> +bool StreamSplitter<Callback>::flush() { + CHECK(maxLength_ == 0 || buffer_.length() < maxLength_); + if (!pieceCb_(StringPiece(detail::ch(buffer_.data()), buffer_.length()))) { + return false; + } + // We are ready to handle another stream now. + buffer_.clear(); + return true; +} + +template <class Callback> +bool StreamSplitter<Callback>::operator()(StringPiece in) { + StringPiece prefix; + // NB This code assumes a 1-byte delimiter. It's not too hard to support + // multibyte delimiters, just remember that maxLength_ chunks can end up + // falling in the middle of a delimiter. + bool found = detail::splitPrefix(in, prefix, delimiter_); + if (buffer_.length() != 0) { + if (found) { + uint64_t num_to_add = prefix.size(); + if (maxLength_) { + CHECK(buffer_.length() < maxLength_); + // Consume as much of prefix as possible without exceeding maxLength_ + num_to_add = std::min(maxLength_ - buffer_.length(), num_to_add); + } + + // Append part of the prefix to the buffer, and send it to the callback + if (!detail::consumeBufferPlus(pieceCb_, buffer_, prefix, num_to_add)) { + return false; + } + + if (!detail::consumeFixedSizeChunks(pieceCb_, prefix, maxLength_)) { + return false; + } + + found = detail::splitPrefix(in, prefix, delimiter_); + // Post-conditions: + // - we consumed all of buffer_ and all of the first prefix. + // - found, in, and prefix reflect the second delimiter_ search + } else if (maxLength_ && buffer_.length() + in.size() >= maxLength_) { + // Send all of buffer_, plus a bit of in, to the callback + if (!detail::consumeBufferPlus( + pieceCb_, buffer_, in, maxLength_ - buffer_.length())) { + return false; + } + // Post-conditions: + // - we consumed all of buffer, and the minimal # of bytes from in + // - found is false + } // Otherwise: found is false & we cannot invoke the callback this turn + } + // Post-condition: buffer_ is nonempty only if found is false **and** + // len(buffer + in) < maxLength_. + + // Send lines to callback directly from input (no buffer) + while (found) { // Buffer guaranteed to be empty + if (!detail::consumeFixedSizeChunks(pieceCb_, prefix, maxLength_)) { + return false; + } + found = detail::splitPrefix(in, prefix, delimiter_); + } + + // No more delimiters left; consume 'in' until it is shorter than maxLength_ + if (maxLength_) { + while (in.size() >= maxLength_) { // Buffer is guaranteed to be empty + if (!pieceCb_(StringPiece(in.begin(), maxLength_))) { + return false; + } + in.advance(maxLength_); + } + } + + if (!in.empty()) { // Buffer may be nonempty + // Incomplete line left, append to buffer + buffer_.reserve(0, in.size()); + memcpy(buffer_.writableTail(), in.data(), in.size()); + buffer_.append(in.size()); + } + CHECK(maxLength_ == 0 || buffer_.length() < maxLength_); + return true; +} + +namespace detail { + +class StringResplitter : public Operator<StringResplitter> { + char delimiter_; + bool keepDelimiter_; + + public: + explicit StringResplitter(char delimiter, bool keepDelimiter = false) + : delimiter_(delimiter), keepDelimiter_(keepDelimiter) {} + + template <class Source> + class Generator : public GenImpl<StringPiece, Generator<Source>> { + Source source_; + char delimiter_; + bool keepDelimiter_; + + public: + Generator(Source source, char delimiter, bool keepDelimiter) + : source_(std::move(source)), + delimiter_(delimiter), + keepDelimiter_(keepDelimiter) {} + + template <class Body> + bool apply(Body&& body) const { + auto splitter = + streamSplitter(this->delimiter_, [this, &body](StringPiece s) { + // The stream ended with a delimiter; our contract is to swallow + // the final empty piece. + if (s.empty()) { + return true; + } + if (s.back() != this->delimiter_) { + return body(s); + } + if (!keepDelimiter_) { + s.pop_back(); // Remove the 1-character delimiter + } + return body(s); + }); + if (!source_.apply(splitter)) { + return false; + } + return splitter.flush(); + } + + static constexpr bool infinite = Source::infinite; + }; + + template <class Source, class Value, class Gen = Generator<Source>> + Gen compose(GenImpl<Value, Source>&& source) const { + return Gen(std::move(source.self()), delimiter_, keepDelimiter_); + } + + template <class Source, class Value, class Gen = Generator<Source>> + Gen compose(const GenImpl<Value, Source>& source) const { + return Gen(source.self(), delimiter_, keepDelimiter_); + } +}; + +template <class DelimiterType = char> +class SplitStringSource + : public GenImpl<StringPiece, SplitStringSource<DelimiterType>> { + StringPiece source_; + DelimiterType delimiter_; + + public: + SplitStringSource(const StringPiece source, DelimiterType delimiter) + : source_(source), delimiter_(std::move(delimiter)) {} + + template <class Body> + bool apply(Body&& body) const { + StringPiece rest(source_); + StringPiece prefix; + while (size_t delim_len = splitPrefix(rest, prefix, this->delimiter_)) { + prefix.subtract(delim_len); // Remove the delimiter + if (!body(prefix)) { + return false; + } + } + if (!rest.empty()) { + if (!body(rest)) { + return false; + } + } + return true; + } +}; + +/** + * Unsplit - For joining tokens from a generator into a string. This is + * the inverse of `split` above. + * + * This type is primarily used through the 'unsplit' function. + */ +template <class Delimiter, class Output> +class Unsplit : public Operator<Unsplit<Delimiter, Output>> { + Delimiter delimiter_; + + public: + explicit Unsplit(const Delimiter& delimiter) : delimiter_(delimiter) {} + + template <class Source, class Value> + Output compose(const GenImpl<Value, Source>& source) const { + Output outputBuffer; + UnsplitBuffer<Delimiter, Output> unsplitter(delimiter_, &outputBuffer); + unsplitter.compose(source); + return outputBuffer; + } +}; + +/** + * UnsplitBuffer - For joining tokens from a generator into a string, + * and inserting them into a custom buffer. + * + * This type is primarily used through the 'unsplit' function. + */ +template <class Delimiter, class OutputBuffer> +class UnsplitBuffer : public Operator<UnsplitBuffer<Delimiter, OutputBuffer>> { + Delimiter delimiter_; + OutputBuffer* outputBuffer_; + + public: + UnsplitBuffer(const Delimiter& delimiter, OutputBuffer* outputBuffer) + : delimiter_(delimiter), outputBuffer_(outputBuffer) { + CHECK(outputBuffer); + } + + template <class Source, class Value> + void compose(const GenImpl<Value, Source>& source) const { + // If the output buffer is empty, we skip inserting the delimiter for the + // first element. + bool skipDelim = outputBuffer_->empty(); + source | [&](Value v) { + if (skipDelim) { + skipDelim = false; + toAppend(std::forward<Value>(v), outputBuffer_); + } else { + toAppend(delimiter_, std::forward<Value>(v), outputBuffer_); + } + }; + } +}; + +/** + * Hack for static for-like constructs + */ +template <class Target, class = void> +inline Target passthrough(Target target) { + return target; +} + +FOLLY_PUSH_WARNING +#ifdef __clang__ +// Clang isn't happy with eatField() hack below. +#pragma GCC diagnostic ignored "-Wreturn-stack-address" +#endif // __clang__ + +/** + * ParseToTuple - For splitting a record and immediatlely converting it to a + * target tuple type. Primary used through the 'eachToTuple' helper, like so: + * + * auto config + * = split("1:a 2:b", ' ') + * | eachToTuple<int, string>() + * | as<vector<tuple<int, string>>>(); + * + */ +template <class TargetContainer, class Delimiter, class... Targets> +class SplitTo { + Delimiter delimiter_; + + public: + explicit SplitTo(Delimiter delimiter) : delimiter_(delimiter) {} + + TargetContainer operator()(StringPiece line) const { + int i = 0; + StringPiece fields[sizeof...(Targets)]; + // HACK(tjackson): Used for referencing fields[] corresponding to variadic + // template parameters. + auto eatField = [&]() -> StringPiece& { return fields[i++]; }; + if (!split( + delimiter_, + line, + detail::passthrough<StringPiece&, Targets>(eatField())...)) { + throw std::runtime_error("field count mismatch"); + } + i = 0; + return TargetContainer(To<Targets>()(eatField())...); + } +}; + +FOLLY_POP_WARNING + +} // namespace detail + +} // namespace gen +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/gen/String.h b/ios/Pods/Flipper-Folly/folly/gen/String.h new file mode 100644 index 000000000..b358ad2a1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/gen/String.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_GEN_STRING_H_ + +#include <folly/Range.h> +#include <folly/gen/Base.h> +#include <folly/io/IOBuf.h> + +namespace folly { +namespace gen { + +namespace detail { +class StringResplitter; + +template <class Delimiter> +class SplitStringSource; + +template <class Delimiter, class Output> +class Unsplit; + +template <class Delimiter, class OutputBuffer> +class UnsplitBuffer; + +template <class TargetContainer, class Delimiter, class... Targets> +class SplitTo; + +} // namespace detail + +/** + * Split the output from a generator into StringPiece "lines" delimited by + * the given delimiter. Delimters are NOT included in the output. + * + * resplit() behaves as if the input strings were concatenated into one long + * string and then split. + * + * Equivalently, you can use StreamSplitter outside of a folly::gen setting. + */ +// make this a template so we don't require StringResplitter to be complete +// until use +template <class S = detail::StringResplitter> +S resplit(char delimiter, bool keepDelimiter = false) { + return S(delimiter, keepDelimiter); +} + +template <class S = detail::SplitStringSource<char>> +S split(const StringPiece source, char delimiter) { + return S(source, delimiter); +} + +template <class S = detail::SplitStringSource<StringPiece>> +S split(StringPiece source, StringPiece delimiter) { + return S(source, delimiter); +} + +/** + * EOL terms ("\r", "\n", or "\r\n"). + */ +class MixedNewlines {}; + +/** + * Split by EOL ("\r", "\n", or "\r\n"). + * @see split(). + */ +template <class S = detail::SplitStringSource<MixedNewlines>> +S lines(StringPiece source) { + return S(source, MixedNewlines{}); +} + +/* + * Joins a sequence of tokens into a string, with the chosen delimiter. + * + * E.G. + * fbstring result = split("a,b,c", ",") | unsplit(","); + * assert(result == "a,b,c"); + * + * std::string result = split("a,b,c", ",") | unsplit<std::string>(" "); + * assert(result == "a b c"); + */ + +// NOTE: The template arguments are reversed to allow the user to cleanly +// specify the output type while still inferring the type of the delimiter. +template < + class Output = folly::fbstring, + class Delimiter, + class Unsplit = detail::Unsplit<Delimiter, Output>> +Unsplit unsplit(const Delimiter& delimiter) { + return Unsplit(delimiter); +} + +template < + class Output = folly::fbstring, + class Unsplit = detail::Unsplit<fbstring, Output>> +Unsplit unsplit(const char* delimiter) { + return Unsplit(delimiter); +} + +/* + * Joins a sequence of tokens into a string, appending them to the output + * buffer. If the output buffer is empty, an initial delimiter will not be + * inserted at the start. + * + * E.G. + * std::string buffer; + * split("a,b,c", ",") | unsplit(",", &buffer); + * assert(buffer == "a,b,c"); + * + * std::string anotherBuffer("initial"); + * split("a,b,c", ",") | unsplit(",", &anotherbuffer); + * assert(anotherBuffer == "initial,a,b,c"); + */ +template < + class Delimiter, + class OutputBuffer, + class UnsplitBuffer = detail::UnsplitBuffer<Delimiter, OutputBuffer>> +UnsplitBuffer unsplit(Delimiter delimiter, OutputBuffer* outputBuffer) { + return UnsplitBuffer(delimiter, outputBuffer); +} + +template < + class OutputBuffer, + class UnsplitBuffer = detail::UnsplitBuffer<fbstring, OutputBuffer>> +UnsplitBuffer unsplit(const char* delimiter, OutputBuffer* outputBuffer) { + return UnsplitBuffer(delimiter, outputBuffer); +} + +template <class... Targets> +detail::Map<detail::SplitTo<std::tuple<Targets...>, char, Targets...>> +eachToTuple(char delim) { + return detail::Map<detail::SplitTo<std::tuple<Targets...>, char, Targets...>>( + detail::SplitTo<std::tuple<Targets...>, char, Targets...>(delim)); +} + +template <class... Targets> +detail::Map<detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>> +eachToTuple(StringPiece delim) { + return detail::Map< + detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>>( + detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>( + to<fbstring>(delim))); +} + +template <class First, class Second> +detail::Map<detail::SplitTo<std::pair<First, Second>, char, First, Second>> +eachToPair(char delim) { + return detail::Map< + detail::SplitTo<std::pair<First, Second>, char, First, Second>>( + detail::SplitTo<std::pair<First, Second>, char, First, Second>(delim)); +} + +template <class First, class Second> +detail::Map<detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>> +eachToPair(StringPiece delim) { + return detail::Map< + detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>>( + detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>( + to<fbstring>(delim))); +} + +/** + * Outputs exactly the same bytes as the input stream, in different chunks. + * A chunk boundary occurs after each delimiter, or, if maxLength is + * non-zero, after maxLength bytes, whichever comes first. Your callback + * can return false to stop consuming the stream at any time. + * + * The splitter buffers the last incomplete chunk, so you must call flush() + * to consume the piece of the stream after the final delimiter. This piece + * may be empty. After a flush(), the splitter can be re-used for a new + * stream. + * + * operator() and flush() return false iff your callback returns false. The + * internal buffer is not flushed, so reusing such a splitter will have + * indeterminate results. Same goes if your callback throws. Feel free to + * fix these corner cases if needed. + * + * Tips: + * - Create via streamSplitter() to take advantage of template deduction. + * - If your callback needs an end-of-stream signal, test for "no + * trailing delimiter **and** shorter than maxLength". + * - You can fine-tune the initial capacity of the internal IOBuf. + */ +template <class Callback> +class StreamSplitter { + public: + StreamSplitter( + char delimiter, + Callback&& pieceCb, + uint64_t maxLength = 0, + uint64_t initialCapacity = 0) + : buffer_(IOBuf::CREATE, initialCapacity), + delimiter_(delimiter), + maxLength_(maxLength), + pieceCb_(std::move(pieceCb)) {} + + /** + * Consume any incomplete last line (may be empty). Do this before + * destroying the StreamSplitter, or you will fail to consume part of the + * input. + * + * After flush() you may proceed to consume the next stream via (). + * + * Returns false if the callback wants no more data, true otherwise. + * A return value of false means that this splitter must no longer be used. + */ + bool flush(); + + /** + * Consume another piece of the input stream. + * + * Returns false only if your callback refuses to consume more data by + * returning false (true otherwise). A return value of false means that + * this splitter must no longer be used. + */ + bool operator()(StringPiece in); + + private: + // Holds the current "incomplete" chunk so that chunks can span calls to () + IOBuf buffer_; + char delimiter_; + uint64_t maxLength_; // The callback never gets more chars than this + Callback pieceCb_; +}; + +template <class Callback> // Helper to enable template deduction +StreamSplitter<Callback> +streamSplitter(char delimiter, Callback&& pieceCb, uint64_t capacity = 0) { + return StreamSplitter<Callback>(delimiter, std::move(pieceCb), capacity); +} + +} // namespace gen +} // namespace folly + +#include <folly/gen/String-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/hash/Checksum.cpp b/ios/Pods/Flipper-Folly/folly/hash/Checksum.cpp new file mode 100644 index 000000000..c42f95bf8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/Checksum.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/hash/Checksum.h> +#include <boost/crc.hpp> +#include <folly/CpuId.h> +#include <folly/hash/detail/ChecksumDetail.h> +#include <algorithm> +#include <stdexcept> + +#if FOLLY_SSE_PREREQ(4, 2) +#include <emmintrin.h> +#include <nmmintrin.h> +#endif + +namespace folly { + +namespace detail { + +uint32_t +crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum); +#if FOLLY_SSE_PREREQ(4, 2) + +uint32_t +crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum); + +// Fast SIMD implementation of CRC-32 for x86 with pclmul +uint32_t +crc32_hw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + uint32_t sum = startingChecksum; + size_t offset = 0; + + // Process unaligned bytes + if ((uintptr_t)data & 15) { + size_t limit = std::min(nbytes, -(uintptr_t)data & 15); + sum = crc32_sw(data, limit, sum); + offset += limit; + nbytes -= limit; + } + + if (nbytes >= 16) { + sum = crc32_hw_aligned(sum, (const __m128i*)(data + offset), nbytes / 16); + offset += nbytes & ~15; + nbytes &= 15; + } + + // Remaining unaligned bytes + return crc32_sw(data + offset, nbytes, sum); +} + +bool crc32c_hw_supported() { + static folly::CpuId id; + return id.sse42(); +} + +bool crc32_hw_supported() { + static folly::CpuId id; + return id.sse42(); +} + +#else + +uint32_t crc32_hw( + const uint8_t* /* data */, + size_t /* nbytes */, + uint32_t /* startingChecksum */) { + throw std::runtime_error("crc32_hw is not implemented on this platform"); +} + +bool crc32c_hw_supported() { + return false; +} + +bool crc32_hw_supported() { + return false; +} +#endif + +template <uint32_t CRC_POLYNOMIAL> +uint32_t crc_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + // Reverse the bits in the starting checksum so they'll be in the + // right internal format for Boost's CRC engine. + // O(1)-time, branchless bit reversal algorithm from + // http://graphics.stanford.edu/~seander/bithacks.html + startingChecksum = ((startingChecksum >> 1) & 0x55555555) | + ((startingChecksum & 0x55555555) << 1); + startingChecksum = ((startingChecksum >> 2) & 0x33333333) | + ((startingChecksum & 0x33333333) << 2); + startingChecksum = ((startingChecksum >> 4) & 0x0f0f0f0f) | + ((startingChecksum & 0x0f0f0f0f) << 4); + startingChecksum = ((startingChecksum >> 8) & 0x00ff00ff) | + ((startingChecksum & 0x00ff00ff) << 8); + startingChecksum = (startingChecksum >> 16) | (startingChecksum << 16); + + boost::crc_optimal<32, CRC_POLYNOMIAL, ~0U, 0, true, true> sum( + startingChecksum); + sum.process_bytes(data, nbytes); + return sum.checksum(); +} + +uint32_t +crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + constexpr uint32_t CRC32C_POLYNOMIAL = 0x1EDC6F41; + return crc_sw<CRC32C_POLYNOMIAL>(data, nbytes, startingChecksum); +} + +uint32_t +crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + constexpr uint32_t CRC32_POLYNOMIAL = 0x04C11DB7; + return crc_sw<CRC32_POLYNOMIAL>(data, nbytes, startingChecksum); +} + +} // namespace detail + +uint32_t crc32c(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + if (detail::crc32c_hw_supported()) { + return detail::crc32c_hw(data, nbytes, startingChecksum); + } else { + return detail::crc32c_sw(data, nbytes, startingChecksum); + } +} + +uint32_t crc32(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + if (detail::crc32_hw_supported()) { + return detail::crc32_hw(data, nbytes, startingChecksum); + } else { + return detail::crc32_sw(data, nbytes, startingChecksum); + } +} + +uint32_t +crc32_type(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) { + return ~crc32(data, nbytes, startingChecksum); +} + +uint32_t crc32_combine(uint32_t crc1, uint32_t crc2, size_t crc2len) { + // Append up to 32 bits of zeroes in the normal way + uint8_t data[4] = {0, 0, 0, 0}; + auto len = crc2len & 3; + if (len) { + crc1 = crc32(data, len, crc1); + } + + if (detail::crc32_hw_supported()) { + return detail::crc32_combine_hw(crc1, crc2, crc2len); + } else { + return detail::crc32_combine_sw(crc1, crc2, crc2len); + } +} + +uint32_t crc32c_combine(uint32_t crc1, uint32_t crc2, size_t crc2len) { + // Append up to 32 bits of zeroes in the normal way + uint8_t data[4] = {0, 0, 0, 0}; + auto len = crc2len & 3; + if (len) { + crc1 = crc32c(data, len, crc1); + } + + if (detail::crc32_hw_supported()) { + return detail::crc32c_combine_hw(crc1, crc2, crc2len - len); + } else { + return detail::crc32c_combine_sw(crc1, crc2, crc2len - len); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/Checksum.h b/ios/Pods/Flipper-Folly/folly/hash/Checksum.h new file mode 100644 index 000000000..c0f96b9af --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/Checksum.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdint.h> +#include <cstddef> + +/* + * Checksum functions + */ + +namespace folly { + +/** + * Compute the CRC-32C checksum of a buffer, using a hardware-accelerated + * implementation if available or a portable software implementation as + * a default. + * + * @note CRC-32C is different from CRC-32; CRC-32C starts with a different + * polynomial and thus yields different results for the same input + * than a traditional CRC-32. + */ +uint32_t +crc32c(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/** + * Compute the CRC-32 checksum of a buffer, using a hardware-accelerated + * implementation if available or a portable software implementation as + * a default. + */ +uint32_t +crc32(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/** + * Compute the CRC-32 checksum of a buffer, using a hardware-accelerated + * implementation if available or a portable software implementation as + * a default. + * + * @note compared to crc32(), crc32_type() uses a different set of default + * parameters to match the results returned by boost::crc_32_type and + * php's built-in crc32 implementation + */ +uint32_t +crc32_type(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/** + * Given two checksums, combine them in to one checksum. + * + * Example: + * len1 len2 + * Given a buffer [ checksum 1 | checksum 2 ] + * such that the first buffer's crc is checksum1 and has length len1, + * and the remainder of the buffer's crc is checksum2 and len 2, + * a total checksum over the whole buffer can be made by: + * + * crc32_combine(checksum1, checksum 2, len2); // len1 not needed. + * + * Note that this is equivalent to: + * + * crc32(buffer2, len2, crc32(buffer1, len1)); + * + * However, this allows calculating the checksums in parallel + * or calculating checksum 2 before checksum 1. + * + * Additionally, this is also equivalent, but much slower: + * crc2 = crc32(buffer2, len2, 0); + * crc1 = crc32(buffer1, len1, 0); + * combined = crc2 ^ crc32(buffer_of_all_zeros, len2, crc1); + * + * crc32[c]_combine is roughly ~10x faster than either of the other + * above two examples. + */ +uint32_t crc32_combine(uint32_t crc1, uint32_t crc2, size_t crc2len); + +/* crc32c_combine is the same as crc32_combine, but uses the crc32c + polynomial */ +uint32_t crc32c_combine(uint32_t crc1, uint32_t crc2, size_t crc2len); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/FarmHash.h b/ios/Pods/Flipper-Folly/folly/hash/FarmHash.h new file mode 100644 index 000000000..7f733dc99 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/FarmHash.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/external/farmhash/farmhash.h> + +namespace folly { +namespace hash { +namespace farmhash { + +// The values returned by Hash, Hash32, and Hash64 are only guaranteed to +// be the same within the same process. Fingerpring32 and Fingerprint64 +// are fixed algorithms that always give the same result. + +// std::size_t Hash(char const*, std::size_t) +using external::farmhash::Hash; + +// uint32_t Hash32(char const*, std::size_t) +using external::farmhash::Hash32; + +// uint64_t Hash64(char const*, std::size_t) +using external::farmhash::Hash64; + +// uint32_t Fingerprint32(char const*, std::size_t) +using external::farmhash::Fingerprint32; + +// uint64_t Fingerprint64(char const*, std::size_t) +using external::farmhash::Fingerprint64; + +} // namespace farmhash +} // namespace hash +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/Hash.h b/ios/Pods/Flipper-Folly/folly/hash/Hash.h new file mode 100644 index 000000000..a8a50e8e8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/Hash.h @@ -0,0 +1,791 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> +#include <cstring> +#include <limits> +#include <memory> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/functional/ApplyTuple.h> +#include <folly/hash/SpookyHashV1.h> +#include <folly/hash/SpookyHashV2.h> +#include <folly/lang/Bits.h> + +/* + * Various hashing functions. + */ + +namespace folly { +namespace hash { + +// This is the Hash128to64 function from Google's cityhash (available +// under the MIT License). We use it to reduce multiple 64 bit hashes +// into a single hash. +FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER("unsigned-integer-overflow") +inline uint64_t hash_128_to_64( + const uint64_t upper, + const uint64_t lower) noexcept { + // Murmur-inspired hashing. + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (lower ^ upper) * kMul; + a ^= (a >> 47); + uint64_t b = (upper ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +////////////////////////////////////////////////////////////////////// + +/* + * Thomas Wang 64 bit mix hash function + */ + +FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER("unsigned-integer-overflow") +inline uint64_t twang_mix64(uint64_t key) noexcept { + key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1; + key = key ^ (key >> 24); + key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8) + key = key ^ (key >> 14); + key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4) + key = key ^ (key >> 28); + key = key + (key << 31); // key *= 1 + (1 << 31) + return key; +} + +/* + * Inverse of twang_mix64 + * + * Note that twang_unmix64 is significantly slower than twang_mix64. + */ + +inline uint64_t twang_unmix64(uint64_t key) noexcept { + // See the comments in jenkins_rev_unmix32 for an explanation as to how this + // was generated + key *= 4611686016279904257U; + key ^= (key >> 28) ^ (key >> 56); + key *= 14933078535860113213U; + key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56); + key *= 15244667743933553977U; + key ^= (key >> 24) ^ (key >> 48); + key = (key + 1) * 9223367638806167551U; + return key; +} + +/* + * Thomas Wang downscaling hash function + */ + +inline uint32_t twang_32from64(uint64_t key) noexcept { + key = (~key) + (key << 18); + key = key ^ (key >> 31); + key = key * 21; + key = key ^ (key >> 11); + key = key + (key << 6); + key = key ^ (key >> 22); + return (uint32_t)key; +} + +/* + * Robert Jenkins' reversible 32 bit mix hash function + */ + +inline uint32_t jenkins_rev_mix32(uint32_t key) noexcept { + key += (key << 12); // key *= (1 + (1 << 12)) + key ^= (key >> 22); + key += (key << 4); // key *= (1 + (1 << 4)) + key ^= (key >> 9); + key += (key << 10); // key *= (1 + (1 << 10)) + key ^= (key >> 2); + // key *= (1 + (1 << 7)) * (1 + (1 << 12)) + key += (key << 7); + key += (key << 12); + return key; +} + +/* + * Inverse of jenkins_rev_mix32 + * + * Note that jenkinks_rev_unmix32 is significantly slower than + * jenkins_rev_mix32. + */ + +inline uint32_t jenkins_rev_unmix32(uint32_t key) noexcept { + // These are the modular multiplicative inverses (in Z_2^32) of the + // multiplication factors in jenkins_rev_mix32, in reverse order. They were + // computed using the Extended Euclidean algorithm, see + // http://en.wikipedia.org/wiki/Modular_multiplicative_inverse + key *= 2364026753U; + + // The inverse of a ^= (a >> n) is + // b = a + // for (int i = n; i < 32; i += n) { + // b ^= (a >> i); + // } + key ^= (key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^ (key >> 10) ^ + (key >> 12) ^ (key >> 14) ^ (key >> 16) ^ (key >> 18) ^ (key >> 20) ^ + (key >> 22) ^ (key >> 24) ^ (key >> 26) ^ (key >> 28) ^ (key >> 30); + key *= 3222273025U; + key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27); + key *= 4042322161U; + key ^= (key >> 22); + key *= 16773121U; + return key; +} + +/* + * Fowler / Noll / Vo (FNV) Hash + * http://www.isthe.com/chongo/tech/comp/fnv/ + */ + +const uint32_t FNV_32_HASH_START = 2166136261UL; +const uint64_t FNV_64_HASH_START = 14695981039346656037ULL; +const uint64_t FNVA_64_HASH_START = 14695981039346656037ULL; + +inline uint32_t fnv32( + const char* buf, + uint32_t hash = FNV_32_HASH_START) noexcept { + // forcing signed char, since other platforms can use unsigned + const signed char* s = reinterpret_cast<const signed char*>(buf); + + for (; *s; ++s) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= *s; + } + return hash; +} + +inline uint32_t fnv32_buf( + const void* buf, + size_t n, + uint32_t hash = FNV_32_HASH_START) noexcept { + // forcing signed char, since other platforms can use unsigned + const signed char* char_buf = reinterpret_cast<const signed char*>(buf); + + for (size_t i = 0; i < n; ++i) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= char_buf[i]; + } + + return hash; +} + +inline uint32_t fnv32( + const std::string& str, + uint32_t hash = FNV_32_HASH_START) noexcept { + return fnv32_buf(str.data(), str.size(), hash); +} + +inline uint64_t fnv64( + const char* buf, + uint64_t hash = FNV_64_HASH_START) noexcept { + // forcing signed char, since other platforms can use unsigned + const signed char* s = reinterpret_cast<const signed char*>(buf); + + for (; *s; ++s) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= *s; + } + return hash; +} + +inline uint64_t fnv64_buf( + const void* buf, + size_t n, + uint64_t hash = FNV_64_HASH_START) noexcept { + // forcing signed char, since other platforms can use unsigned + const signed char* char_buf = reinterpret_cast<const signed char*>(buf); + + for (size_t i = 0; i < n; ++i) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= char_buf[i]; + } + return hash; +} + +inline uint64_t fnv64( + const std::string& str, + uint64_t hash = FNV_64_HASH_START) noexcept { + return fnv64_buf(str.data(), str.size(), hash); +} + +inline uint64_t fnva64_buf( + const void* buf, + size_t n, + uint64_t hash = FNVA_64_HASH_START) noexcept { + const uint8_t* char_buf = reinterpret_cast<const uint8_t*>(buf); + + for (size_t i = 0; i < n; ++i) { + hash ^= char_buf[i]; + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + } + return hash; +} + +inline uint64_t fnva64( + const std::string& str, + uint64_t hash = FNVA_64_HASH_START) noexcept { + return fnva64_buf(str.data(), str.size(), hash); +} + +/* + * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html + */ + +#define get16bits(d) folly::loadUnaligned<uint16_t>(d) + +inline uint32_t hsieh_hash32_buf(const void* buf, size_t len) noexcept { + // forcing signed char, since other platforms can use unsigned + const unsigned char* s = reinterpret_cast<const unsigned char*>(buf); + uint32_t hash = static_cast<uint32_t>(len); + uint32_t tmp; + size_t rem; + + if (len <= 0 || buf == nullptr) { + return 0; + } + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (; len > 0; len--) { + hash += get16bits(s); + tmp = (get16bits(s + 2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2 * sizeof(uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: + hash += get16bits(s); + hash ^= hash << 16; + hash ^= s[sizeof(uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: + hash += get16bits(s); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: + hash += *s; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +#undef get16bits + +inline uint32_t hsieh_hash32(const char* s) noexcept { + return hsieh_hash32_buf(s, std::strlen(s)); +} + +inline uint32_t hsieh_hash32_str(const std::string& str) noexcept { + return hsieh_hash32_buf(str.data(), str.size()); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace hash + +namespace detail { + +template <typename I> +struct integral_hasher { + using folly_is_avalanching = + bool_constant<(sizeof(I) >= 8 || sizeof(size_t) == 4)>; + + size_t operator()(I const& i) const noexcept { + static_assert(sizeof(I) <= 16, "Input type is too wide"); + /* constexpr */ if (sizeof(I) <= 4) { + auto const i32 = static_cast<int32_t>(i); // impl accident: sign-extends + auto const u32 = static_cast<uint32_t>(i32); + return static_cast<size_t>(hash::jenkins_rev_mix32(u32)); + } else if (sizeof(I) <= 8) { + auto const u64 = static_cast<uint64_t>(i); + return static_cast<size_t>(hash::twang_mix64(u64)); + } else { + auto const u = to_unsigned(i); + auto const hi = static_cast<uint64_t>(u >> sizeof(I) * 4); + auto const lo = static_cast<uint64_t>(u); + return hash::hash_128_to_64(hi, lo); + } + } +}; + +template <typename F> +struct float_hasher { + using folly_is_avalanching = std::true_type; + + size_t operator()(F const& f) const noexcept { + static_assert(sizeof(F) <= 8, "Input type is too wide"); + + if (f == F{}) { // Ensure 0 and -0 get the same hash. + return 0; + } + + uint64_t u64 = 0; + memcpy(&u64, &f, sizeof(F)); + return static_cast<size_t>(hash::twang_mix64(u64)); + } +}; + +} // namespace detail + +template <class Key, class Enable = void> +struct hasher; + +struct Hash { + template <class T> + size_t operator()(const T& v) const noexcept(noexcept(hasher<T>()(v))) { + return hasher<T>()(v); + } + + template <class T, class... Ts> + size_t operator()(const T& t, const Ts&... ts) const { + return hash::hash_128_to_64((*this)(t), (*this)(ts...)); + } + + size_t operator()() const noexcept { + return 0; + } +}; + +// IsAvalanchingHasher<H, K> extends std::integral_constant<bool, V>. +// V will be true if it is known that when a hasher of type H computes +// the hash of a key of type K, any subset of B bits from the resulting +// hash value is usable in a context that can tolerate a collision rate +// of about 1/2^B. (Input bits lost implicitly converting between K and +// the argument of H::operator() are not considered here; K is separate +// to handle the case of generic hashers like folly::Hash). +// +// If std::hash<T> or folly::hasher<T> is specialized for a new type T and +// the implementation avalanches input entropy across all of the bits of a +// std::size_t result, the specialization should be marked as avalanching. +// This can be done either by adding a member type folly_is_avalanching +// to the functor H that contains a constexpr bool value of true, or by +// specializing IsAvalanchingHasher<H, K>. The member type mechanism is +// more convenient, but specializing IsAvalanchingHasher may be required +// if a hasher is polymorphic on the key type or if its definition cannot +// be modified. +// +// The standard's definition of hash quality is based on the chance hash +// collisions using the entire hash value. No requirement is made that +// this property holds for subsets of the bits. In addition, hashed keys +// in real-world workloads are not chosen uniformly from the entire domain +// of keys, which can further increase the collision rate for a subset +// of bits. For example, std::hash<uint64_t> in libstdc++-v3 and libc++ +// is the identity function. This hash function has no collisions when +// considering hash values in their entirety, but for real-world workloads +// the high bits are likely to always be zero. +// +// Some hash functions provide a stronger guarantee -- the standard's +// collision property is also preserved for subsets of the output bits and +// for sub-domains of keys. Another way to say this is that each bit of +// the hash value contains entropy from the entire input, changes to the +// input avalanche across all of the bits of the output. The distinction +// is useful when mapping the hash value onto a smaller space efficiently +// (such as when implementing a hash table). +template <typename Hasher, typename Key> +struct IsAvalanchingHasher; + +namespace detail { +template <typename Hasher, typename Void = void> +struct IsAvalanchingHasherFromMemberType : std::false_type {}; + +template <typename Hasher> +struct IsAvalanchingHasherFromMemberType< + Hasher, + void_t<typename Hasher::folly_is_avalanching>> + : bool_constant<Hasher::folly_is_avalanching::value> {}; +} // namespace detail + +template <typename Hasher, typename Key> +struct IsAvalanchingHasher : detail::IsAvalanchingHasherFromMemberType<Hasher> { +}; + +// It's ugly to put this here, but folly::transparent isn't hash specific +// so it seems even more ugly to put this near its declaration +template <typename H, typename K> +struct IsAvalanchingHasher<transparent<H>, K> : IsAvalanchingHasher<H, K> {}; + +template <typename K> +struct IsAvalanchingHasher<Hash, K> : IsAvalanchingHasher<hasher<K>, K> {}; + +template <> +struct hasher<bool> { + using folly_is_avalanching = std::true_type; + + size_t operator()(bool key) const noexcept { + // Make sure that all the output bits depend on the input. + return key ? std::numeric_limits<size_t>::max() : 0; + } +}; +template <typename K> +struct IsAvalanchingHasher<hasher<bool>, K> : std::true_type {}; + +template <> +struct hasher<unsigned long long> + : detail::integral_hasher<unsigned long long> {}; + +template <> +struct hasher<signed long long> : detail::integral_hasher<signed long long> {}; + +template <> +struct hasher<unsigned long> : detail::integral_hasher<unsigned long> {}; + +template <> +struct hasher<signed long> : detail::integral_hasher<signed long> {}; + +template <> +struct hasher<unsigned int> : detail::integral_hasher<unsigned int> {}; + +template <> +struct hasher<signed int> : detail::integral_hasher<signed int> {}; + +template <> +struct hasher<unsigned short> : detail::integral_hasher<unsigned short> {}; + +template <> +struct hasher<signed short> : detail::integral_hasher<signed short> {}; + +template <> +struct hasher<unsigned char> : detail::integral_hasher<unsigned char> {}; + +template <> +struct hasher<signed char> : detail::integral_hasher<signed char> {}; + +template <> // char is a different type from both signed char and unsigned char +struct hasher<char> : detail::integral_hasher<char> {}; + +#if FOLLY_HAVE_INT128_T +template <> +struct hasher<signed __int128> : detail::integral_hasher<signed __int128> {}; + +template <> +struct hasher<unsigned __int128> : detail::integral_hasher<unsigned __int128> { +}; +#endif + +template <> +struct hasher<float> : detail::float_hasher<float> {}; + +template <> +struct hasher<double> : detail::float_hasher<double> {}; + +template <> +struct hasher<std::string> { + using folly_is_avalanching = std::true_type; + + size_t operator()(const std::string& key) const { + return static_cast<size_t>( + hash::SpookyHashV2::Hash64(key.data(), key.size(), 0)); + } +}; +template <typename K> +struct IsAvalanchingHasher<hasher<std::string>, K> : std::true_type {}; + +template <typename T> +struct hasher<T, std::enable_if_t<std::is_enum<T>::value>> { + size_t operator()(T key) const noexcept { + return Hash()(to_underlying(key)); + } +}; + +template <typename T, typename K> +struct IsAvalanchingHasher< + hasher<T, std::enable_if_t<std::is_enum<T>::value>>, + K> : IsAvalanchingHasher<hasher<std::underlying_type_t<T>>, K> {}; + +template <typename T1, typename T2> +struct hasher<std::pair<T1, T2>> { + using folly_is_avalanching = std::true_type; + + size_t operator()(const std::pair<T1, T2>& key) const { + return Hash()(key.first, key.second); + } +}; + +template <typename... Ts> +struct hasher<std::tuple<Ts...>> { + size_t operator()(const std::tuple<Ts...>& key) const { + return apply(Hash(), key); + } +}; + +template <typename T> +struct hasher<T*> { + using folly_is_avalanching = hasher<std::uintptr_t>::folly_is_avalanching; + + size_t operator()(T* key) const { + return Hash()(bit_cast<std::uintptr_t>(key)); + } +}; + +template <typename T> +struct hasher<std::unique_ptr<T>> { + using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching; + + size_t operator()(const std::unique_ptr<T>& key) const { + return Hash()(key.get()); + } +}; + +template <typename T> +struct hasher<std::shared_ptr<T>> { + using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching; + + size_t operator()(const std::shared_ptr<T>& key) const { + return Hash()(key.get()); + } +}; + +// combiner for multi-arg tuple also mixes bits +template <typename T, typename K> +struct IsAvalanchingHasher<hasher<std::tuple<T>>, K> + : IsAvalanchingHasher<hasher<T>, K> {}; +template <typename T1, typename T2, typename... Ts, typename K> +struct IsAvalanchingHasher<hasher<std::tuple<T1, T2, Ts...>>, K> + : std::true_type {}; + +namespace hash { +// Simply uses std::hash to hash. Note that std::hash is not guaranteed +// to be a very good hash function; provided std::hash doesn't collide on +// the individual inputs, you are fine, but that won't be true for, say, +// strings or pairs +class StdHasher { + public: + // The standard requires all explicit and partial specializations of std::hash + // supplied by either the standard library or by users to be default + // constructible. + template <typename T> + size_t operator()(const T& t) const noexcept(noexcept(std::hash<T>()(t))) { + return std::hash<T>()(t); + } +}; + +// This is a general-purpose way to create a single hash from multiple +// hashable objects. hash_combine_generic takes a class Hasher implementing +// hash<T>; hash_combine uses a default hasher StdHasher that uses std::hash. +// hash_combine_generic hashes each argument and combines those hashes in +// an order-dependent way to yield a new hash; hash_range does so (also in an +// order-dependent way) for items in the range [first, last); +// commutative_hash_combine_* hashes values but combines them in an +// order-independent way to yield a new hash. + +template <class Hash, class Value> +uint64_t commutative_hash_combine_value_generic( + uint64_t seed, + Hash const& hasher, + Value const& value) { + auto const x = hasher(value); + auto const y = IsAvalanchingHasher<Hash, Value>::value ? x : twang_mix64(x); + // Commutative accumulator taken from this paper: + // https://www.preprints.org/manuscript/201710.0192/v1/download + return 3860031 + (seed + y) * 2779 + (seed * y * 2); +} + +// hash_range combines hashes of items in the range [first, last) in an +// __order-dependent__ fashion. To hash an unordered container (e.g., +// folly::dynamic, hash tables like std::unordered_map), use +// commutative_hash_combine_range instead, which combines hashes of items +// independent of ordering. +template < + class Iter, + class Hash = std::hash<typename std::iterator_traits<Iter>::value_type>> +uint64_t +hash_range(Iter begin, Iter end, uint64_t hash = 0, Hash hasher = Hash()) { + for (; begin != end; ++begin) { + hash = hash_128_to_64(hash, hasher(*begin)); + } + return hash; +} + +template <class Hash, class Iter> +uint64_t commutative_hash_combine_range_generic( + uint64_t seed, + Hash const& hasher, + Iter first, + Iter last) { + while (first != last) { + seed = commutative_hash_combine_value_generic(seed, hasher, *first++); + } + return seed; +} + +template <class Iter> +uint64_t commutative_hash_combine_range(Iter first, Iter last) { + return commutative_hash_combine_range_generic(0, Hash{}, first, last); +} + +namespace detail { +using c_array_size_t = size_t[]; +} // namespace detail + +// Never used, but gcc demands it. +template <class Hasher> +inline size_t hash_combine_generic(const Hasher&) noexcept { + return 0; +} + +template <class Hasher, typename T, typename... Ts> +size_t hash_combine_generic( + const Hasher& h, + const T& t, + const Ts&... ts) noexcept(noexcept(detail::c_array_size_t{h(t), + h(ts)...})) { + size_t seed = h(t); + if (sizeof...(ts) == 0) { + return seed; + } + size_t remainder = hash_combine_generic(h, ts...); + if /* constexpr */ (sizeof(size_t) == sizeof(uint32_t)) { + return twang_32from64((uint64_t(seed) << 32) | remainder); + } else { + return static_cast<size_t>(hash_128_to_64(seed, remainder)); + } +} + +template <typename Hash, typename... Value> +uint64_t commutative_hash_combine_generic( + uint64_t seed, + Hash const& hasher, + Value const&... value) { + // variadic foreach: + uint64_t _[] = { + 0, seed = commutative_hash_combine_value_generic(seed, hasher, value)...}; + (void)_; + return seed; +} + +template <typename T, typename... Ts> +size_t hash_combine(const T& t, const Ts&... ts) noexcept( + noexcept(hash_combine_generic(StdHasher{}, t, ts...))) { + return hash_combine_generic(StdHasher{}, t, ts...); +} + +template <typename... Value> +uint64_t commutative_hash_combine(Value const&... value) { + return commutative_hash_combine_generic(0, Hash{}, value...); +} +} // namespace hash + +// recursion +template <size_t index, typename... Ts> +struct TupleHasher { + size_t operator()(std::tuple<Ts...> const& key) const { + return hash::hash_combine( + TupleHasher<index - 1, Ts...>()(key), std::get<index>(key)); + } +}; + +// base +template <typename... Ts> +struct TupleHasher<0, Ts...> { + size_t operator()(std::tuple<Ts...> const& key) const { + // we could do std::hash here directly, but hash_combine hides all the + // ugly templating implicitly + return hash::hash_combine(std::get<0>(key)); + } +}; + +} // namespace folly + +// Custom hash functions. +namespace std { +#if FOLLY_SUPPLY_MISSING_INT128_TRAITS +template <> +struct hash<__int128> : folly::detail::integral_hasher<__int128> {}; + +template <> +struct hash<unsigned __int128> + : folly::detail::integral_hasher<unsigned __int128> {}; +#endif + +// Hash function for pairs. Requires default hash functions for both +// items in the pair. +template <typename T1, typename T2> +struct hash<std::pair<T1, T2>> { + using folly_is_avalanching = std::true_type; + + size_t operator()(const std::pair<T1, T2>& x) const { + return folly::hash::hash_combine(x.first, x.second); + } +}; + +// Hash function for tuples. Requires default hash functions for all types. +template <typename... Ts> +struct hash<std::tuple<Ts...>> { + private: + using FirstT = std::decay_t<std::tuple_element_t<0, std::tuple<Ts..., bool>>>; + + public: + using folly_is_avalanching = folly::bool_constant<( + sizeof...(Ts) != 1 || + folly::IsAvalanchingHasher<std::hash<FirstT>, FirstT>::value)>; + + size_t operator()(std::tuple<Ts...> const& key) const { + folly::TupleHasher< + sizeof...(Ts) - 1, // start index + Ts...> + hasher; + + return hasher(key); + } +}; +} // namespace std + +namespace folly { + +// std::hash<std::string> is avalanching on libstdc++-v3 (code checked), +// libc++ (code checked), and MSVC (based on online information). +// std::hash for float and double on libstdc++-v3 are avalanching, +// but they are not on libc++. std::hash for integral types is not +// avalanching for libstdc++-v3 or libc++. We're conservative here and +// just mark std::string as avalanching. std::string_view will also be +// so, once it exists. +template <typename... Args, typename K> +struct IsAvalanchingHasher<std::hash<std::basic_string<Args...>>, K> + : std::true_type {}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.cpp b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.cpp new file mode 100644 index 000000000..4a6c8e84f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Spooky Hash +// A 128-bit noncryptographic hash, for checksums and table lookup +// By Bob Jenkins. Public domain. +// Oct 31 2010: published framework, disclaimer ShortHash isn't right +// Nov 7 2010: disabled ShortHash +// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again +// April 10 2012: buffer overflow on platforms without unaligned reads +// July 12 2012: was passing out variables in final to in/out in short +// July 30 2012: I reintroduced the buffer overflow + +#include <folly/hash/SpookyHashV1.h> + +#include <folly/CppAttributes.h> + +#include <cstring> + +#define ALLOW_UNALIGNED_READS 1 + +namespace folly { +namespace hash { + +// clang-format off + +// +// short hash ... it could be used on any message, +// but it's used by Spooky just for short messages. +// +void SpookyHashV1::Short( + const void* message, + size_t length, + uint64_t *hash1, + uint64_t *hash2) +{ + uint64_t buf[2*sc_numVars]; + union + { + const uint8_t *p8; + uint32_t *p32; + uint64_t *p64; + size_t i; + } u; + + u.p8 = (const uint8_t *)message; + + if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) + { + memcpy(buf, message, length); + u.p64 = buf; + } + + size_t remainder = length%32; + uint64_t a=*hash1; + uint64_t b=*hash2; + uint64_t c=sc_const; + uint64_t d=sc_const; + + if (length > 15) + { + const uint64_t *end = u.p64 + (length/32)*4; + + // handle all complete sets of 32 bytes + for (; u.p64 < end; u.p64 += 4) + { + c += u.p64[0]; + d += u.p64[1]; + ShortMix(a,b,c,d); + a += u.p64[2]; + b += u.p64[3]; + } + + //Handle the case of 16+ remaining bytes. + if (remainder >= 16) + { + c += u.p64[0]; + d += u.p64[1]; + ShortMix(a,b,c,d); + u.p64 += 2; + remainder -= 16; + } + } + + // Handle the last 0..15 bytes, and its length + d = ((uint64_t)length) << 56; + switch (remainder) + { + case 15: + d += ((uint64_t)u.p8[14]) << 48; + FOLLY_FALLTHROUGH; + case 14: + d += ((uint64_t)u.p8[13]) << 40; + FOLLY_FALLTHROUGH; + case 13: + d += ((uint64_t)u.p8[12]) << 32; + FOLLY_FALLTHROUGH; + case 12: + d += u.p32[2]; + c += u.p64[0]; + break; + case 11: + d += ((uint64_t)u.p8[10]) << 16; + FOLLY_FALLTHROUGH; + case 10: + d += ((uint64_t)u.p8[9]) << 8; + FOLLY_FALLTHROUGH; + case 9: + d += (uint64_t)u.p8[8]; + FOLLY_FALLTHROUGH; + case 8: + c += u.p64[0]; + break; + case 7: + c += ((uint64_t)u.p8[6]) << 48; + FOLLY_FALLTHROUGH; + case 6: + c += ((uint64_t)u.p8[5]) << 40; + FOLLY_FALLTHROUGH; + case 5: + c += ((uint64_t)u.p8[4]) << 32; + FOLLY_FALLTHROUGH; + case 4: + c += u.p32[0]; + break; + case 3: + c += ((uint64_t)u.p8[2]) << 16; + FOLLY_FALLTHROUGH; + case 2: + c += ((uint64_t)u.p8[1]) << 8; + FOLLY_FALLTHROUGH; + case 1: + c += (uint64_t)u.p8[0]; + break; + case 0: + c += sc_const; + d += sc_const; + } + ShortEnd(a,b,c,d); + *hash1 = a; + *hash2 = b; +} + + + + +// do the whole hash in one call +void SpookyHashV1::Hash128( + const void *message, + size_t length, + uint64_t *hash1, + uint64_t *hash2) +{ + if (length < sc_bufSize) + { + Short(message, length, hash1, hash2); + return; + } + + uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; + uint64_t buf[sc_numVars]; + uint64_t *end; + union + { + const uint8_t *p8; + uint64_t *p64; + size_t i; + } u; + size_t remainder; + + h0=h3=h6=h9 = *hash1; + h1=h4=h7=h10 = *hash2; + h2=h5=h8=h11 = sc_const; + + u.p8 = (const uint8_t *)message; + end = u.p64 + (length/sc_blockSize)*sc_numVars; + + // handle all whole sc_blockSize blocks of bytes + if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0)) + { + while (u.p64 < end) + { + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + else + { + while (u.p64 < end) + { + memcpy(buf, u.p64, sc_blockSize); + Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + + // handle the last partial block of sc_blockSize bytes + remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); + memcpy(buf, end, remainder); + memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); + ((uint8_t*)buf)[sc_blockSize - 1] = uint8_t(remainder); + Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + + // do some final mixing + End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + *hash1 = h0; + *hash2 = h1; +} + + + +// init spooky state +void SpookyHashV1::Init(uint64_t seed1, uint64_t seed2) +{ + m_length = 0; + m_remainder = 0; + m_state[0] = seed1; + m_state[1] = seed2; +} + + +// add a message fragment to the state +void SpookyHashV1::Update(const void *message, size_t length) +{ + uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; + size_t newLength = length + m_remainder; + uint8_t remainder; + union + { + const uint8_t *p8; + uint64_t *p64; + size_t i; + } u; + const uint64_t *end; + + // Is this message fragment too short? If it is, stuff it away. + if (newLength < sc_bufSize) + { + memcpy(&((uint8_t *)m_data)[m_remainder], message, length); + m_length = length + m_length; + m_remainder = (uint8_t)newLength; + return; + } + + // init the variables + if (m_length < sc_bufSize) + { + h0=h3=h6=h9 = m_state[0]; + h1=h4=h7=h10 = m_state[1]; + h2=h5=h8=h11 = sc_const; + } + else + { + h0 = m_state[0]; + h1 = m_state[1]; + h2 = m_state[2]; + h3 = m_state[3]; + h4 = m_state[4]; + h5 = m_state[5]; + h6 = m_state[6]; + h7 = m_state[7]; + h8 = m_state[8]; + h9 = m_state[9]; + h10 = m_state[10]; + h11 = m_state[11]; + } + m_length = length + m_length; + + // if we've got anything stuffed away, use it now + if (m_remainder) + { + uint8_t prefix = sc_bufSize-m_remainder; + memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix); + u.p64 = m_data; + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p8 = ((const uint8_t *)message) + prefix; + length -= prefix; + } + else + { + u.p8 = (const uint8_t *)message; + } + + // handle all whole blocks of sc_blockSize bytes + end = u.p64 + (length/sc_blockSize)*sc_numVars; + remainder = (uint8_t)(length-((const uint8_t *)end-u.p8)); + if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0) + { + while (u.p64 < end) + { + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + else + { + while (u.p64 < end) + { + memcpy(m_data, u.p8, sc_blockSize); + Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + + // stuff away the last few bytes + m_remainder = remainder; + memcpy(m_data, end, remainder); + + // stuff away the variables + m_state[0] = h0; + m_state[1] = h1; + m_state[2] = h2; + m_state[3] = h3; + m_state[4] = h4; + m_state[5] = h5; + m_state[6] = h6; + m_state[7] = h7; + m_state[8] = h8; + m_state[9] = h9; + m_state[10] = h10; + m_state[11] = h11; +} + + +// report the hash for the concatenation of all message fragments so far +void SpookyHashV1::Final(uint64_t *hash1, uint64_t *hash2) +{ + // init the variables + if (m_length < sc_bufSize) + { + *hash1 = m_state[0]; + *hash2 = m_state[1]; + Short( m_data, m_length, hash1, hash2); + return; + } + + auto data = (const uint64_t *)m_data; + uint8_t remainder = m_remainder; + + uint64_t h0 = m_state[0]; + uint64_t h1 = m_state[1]; + uint64_t h2 = m_state[2]; + uint64_t h3 = m_state[3]; + uint64_t h4 = m_state[4]; + uint64_t h5 = m_state[5]; + uint64_t h6 = m_state[6]; + uint64_t h7 = m_state[7]; + uint64_t h8 = m_state[8]; + uint64_t h9 = m_state[9]; + uint64_t h10 = m_state[10]; + uint64_t h11 = m_state[11]; + + if (remainder >= sc_blockSize) + { + // m_data can contain two blocks; handle any whole first block + Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + data += sc_numVars; + remainder -= sc_blockSize; + } + + // mix in the last partial block, and the length mod sc_blockSize + memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder)); + + ((uint8_t *)data)[sc_blockSize-1] = remainder; + Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + + // do some final mixing + End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + + *hash1 = h0; + *hash2 = h1; +} + +// clang-format on + +} // namespace hash +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.h b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.h new file mode 100644 index 000000000..fe052cc75 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV1.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 is version 1 of SpookyHash, incompatible with version 2. +// +// SpookyHash: a 128-bit noncryptographic hash function +// By Bob Jenkins, public domain +// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right +// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right +// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas +// Feb 2 2012: production, same bits as beta +// Feb 5 2012: adjusted definitions of uint* to be more portable +// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. +// +// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. +// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. +// +// This was developed for and tested on 64-bit x86-compatible processors. +// It assumes the processor is little-endian. There is a macro +// controlling whether unaligned reads are allowed (by default they are). +// This should be an equally good hash on big-endian machines, but it will +// compute different results on them than on little-endian machines. +// +// Google's CityHash has similar specs to SpookyHash, and CityHash is faster +// on some platforms. MD4 and MD5 also have similar specs, but they are orders +// of magnitude slower. CRCs are two or more times slower, but unlike +// SpookyHash, they have nice math for combining the CRCs of pieces to form +// the CRCs of wholes. There are also cryptographic hashes, but those are even +// slower than MD5. +// + +#pragma once + +#include <cstddef> +#include <cstdint> + +namespace folly { +namespace hash { + +// clang-format off + +class SpookyHashV1 +{ +public: + // + // SpookyHash: hash a single message in one call, produce 128-bit output + // + static void Hash128( + const void *message, // message to hash + size_t length, // length of message in bytes + uint64_t *hash1, // in/out: in seed 1, out hash value 1 + uint64_t *hash2); // in/out: in seed 2, out hash value 2 + + // + // Hash64: hash a single message in one call, return 64-bit output + // + static uint64_t Hash64( + const void *message, // message to hash + size_t length, // length of message in bytes + uint64_t seed) // seed + { + uint64_t hash1 = seed; + Hash128(message, length, &hash1, &seed); + return hash1; + } + + // + // Hash32: hash a single message in one call, produce 32-bit output + // + static uint32_t Hash32( + const void *message, // message to hash + size_t length, // length of message in bytes + uint32_t seed) // seed + { + uint64_t hash1 = seed, hash2 = seed; + Hash128(message, length, &hash1, &hash2); + return (uint32_t)hash1; + } + + // + // Init: initialize the context of a SpookyHash + // + void Init( + uint64_t seed1, // any 64-bit value will do, including 0 + uint64_t seed2); // different seeds produce independent hashes + + // + // Update: add a piece of a message to a SpookyHash state + // + void Update( + const void *message, // message fragment + size_t length); // length of message fragment in bytes + + + // + // Final: compute the hash for the current SpookyHash state + // + // This does not modify the state; you can keep updating it afterward + // + // The result is the same as if SpookyHash() had been called with + // all the pieces concatenated into one message. + // + void Final( + uint64_t *hash1, // out only: first 64 bits of hash value. + uint64_t *hash2); // out only: second 64 bits of hash value. + + // + // left rotate a 64-bit value by k bytes + // + static inline uint64_t Rot64(uint64_t x, int k) + { + return (x << k) | (x >> (64 - k)); + } + + // + // This is used if the input is 96 bytes long or longer. + // + // The internal state is fully overwritten every 96 bytes. + // Every input bit appears to cause at least 128 bits of entropy + // before 96 other bytes are combined, when run forward or backward + // For every input bit, + // Two inputs differing in just that input bit + // Where "differ" means xor or subtraction + // And the base value is random + // When run forward or backwards one Mix + // I tried 3 pairs of each; they all differed by at least 212 bits. + // + static inline void Mix( + const uint64_t *data, + uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3, + uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7, + uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11) + { + s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1; + s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2; + s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3; + s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4; + s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5; + s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6; + s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7; + s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8; + s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9; + s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10; + s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11; + s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0; + } + + // + // Mix all 12 inputs together so that h0, h1 are a hash of them all. + // + // For two inputs differing in just the input bits + // Where "differ" means xor or subtraction + // And the base value is random, or a counting value starting at that bit + // The final result will have each bit of h0, h1 flip + // For every input bit, + // with probability 50 +- .3% + // For every pair of input bits, + // with probability 50 +- 3% + // + // This does not rely on the last Mix() call having already mixed some. + // Two iterations was almost good enough for a 64-bit result, but a + // 128-bit result is reported, so End() does three iterations. + // + static inline void EndPartial( + uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, + uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, + uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) + { + h11+= h1; h2 ^= h11; h1 = Rot64(h1,44); + h0 += h2; h3 ^= h0; h2 = Rot64(h2,15); + h1 += h3; h4 ^= h1; h3 = Rot64(h3,34); + h2 += h4; h5 ^= h2; h4 = Rot64(h4,21); + h3 += h5; h6 ^= h3; h5 = Rot64(h5,38); + h4 += h6; h7 ^= h4; h6 = Rot64(h6,33); + h5 += h7; h8 ^= h5; h7 = Rot64(h7,10); + h6 += h8; h9 ^= h6; h8 = Rot64(h8,13); + h7 += h9; h10^= h7; h9 = Rot64(h9,38); + h8 += h10; h11^= h8; h10= Rot64(h10,53); + h9 += h11; h0 ^= h9; h11= Rot64(h11,42); + h10+= h0; h1 ^= h10; h0 = Rot64(h0,54); + } + + static inline void End( + uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, + uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, + uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) + { + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + } + + // + // The goal is for each bit of the input to expand into 128 bits of + // apparent entropy before it is fully overwritten. + // n trials both set and cleared at least m bits of h0 h1 h2 h3 + // n: 2 m: 29 + // n: 3 m: 46 + // n: 4 m: 57 + // n: 5 m: 107 + // n: 6 m: 146 + // n: 7 m: 152 + // when run forwards or backwards + // for all 1-bit and 2-bit diffs + // with diffs defined by either xor or subtraction + // with a base of all zeros plus a counter, or plus another bit, or random + // + static inline void ShortMix(uint64_t &h0, uint64_t &h1, + uint64_t &h2, uint64_t &h3) + { + h2 = Rot64(h2,50); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,52); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,30); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,41); h1 += h2; h3 ^= h1; + h2 = Rot64(h2,54); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,48); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,38); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,37); h1 += h2; h3 ^= h1; + h2 = Rot64(h2,62); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,34); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,5); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,36); h1 += h2; h3 ^= h1; + } + + // + // Mix all 4 inputs together so that h0, h1 are a hash of them all. + // + // For two inputs differing in just the input bits + // Where "differ" means xor or subtraction + // And the base value is random, or a counting value starting at that bit + // The final result will have each bit of h0, h1 flip + // For every input bit, + // with probability 50 +- .3% (it is probably better than that) + // For every pair of input bits, + // with probability 50 +- .75% (the worst case is approximately that) + // + static inline void ShortEnd(uint64_t &h0, uint64_t &h1, + uint64_t &h2, uint64_t &h3) + { + h3 ^= h2; h2 = Rot64(h2,15); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,52); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,26); h1 += h0; + h2 ^= h1; h1 = Rot64(h1,51); h2 += h1; + h3 ^= h2; h2 = Rot64(h2,28); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,9); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,47); h1 += h0; + h2 ^= h1; h1 = Rot64(h1,54); h2 += h1; + h3 ^= h2; h2 = Rot64(h2,32); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,25); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,63); h1 += h0; + } + +private: + + // + // Short is used for messages under 192 bytes in length + // Short has a low startup cost, the normal mode is good for long + // keys, the cost crossover is at about 192 bytes. The two modes were + // held to the same quality bar. + // + static void Short( + const void *message, // message (byte array, not necessarily aligned) + size_t length, // length of message (in bytes) + uint64_t *hash1, // in/out: in the seed, out the hash value + uint64_t *hash2); // in/out: in the seed, out the hash value + + // number of uint64_t's in internal state + static const size_t sc_numVars = 12; + + // size of the internal state + static const size_t sc_blockSize = sc_numVars*8; + + // size of buffer of unhashed data, in bytes + static const size_t sc_bufSize = 2*sc_blockSize; + + // + // sc_const: a constant which: + // * is not zero + // * is odd + // * is a not-very-regular mix of 1's and 0's + // * does not need any other special mathematical properties + // + static const uint64_t sc_const = 0xdeadbeefdeadbeefULL; + + uint64_t m_data[2*sc_numVars]; // unhashed data, for partial messages + uint64_t m_state[sc_numVars]; // internal state of the hash + size_t m_length; // total length of the input so far + uint8_t m_remainder; // length of unhashed data stashed in m_data +}; + +// clang-format on + +} // namespace hash +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.cpp b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.cpp new file mode 100644 index 000000000..05322c71a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Spooky Hash +// A 128-bit noncryptographic hash, for checksums and table lookup +// By Bob Jenkins. Public domain. +// Oct 31 2010: published framework, disclaimer ShortHash isn't right +// Nov 7 2010: disabled ShortHash +// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again +// April 10 2012: buffer overflow on platforms without unaligned reads +// July 12 2012: was passing out variables in final to in/out in short +// July 30 2012: I reintroduced the buffer overflow +// August 5 2012: SpookyV2: d = should be d += in short hash, and remove +// extra mix from long hash + +#include <folly/hash/SpookyHashV2.h> + +#include <folly/CppAttributes.h> +#include <folly/Portability.h> + +#include <cstring> + +namespace folly { +namespace hash { + +// clang-format off + +// +// short hash ... it could be used on any message, +// but it's used by Spooky just for short messages. +// +void SpookyHashV2::Short( + const void *message, + size_t length, + uint64_t *hash1, + uint64_t *hash2) +{ + uint64_t buf[2*sc_numVars]; + union + { + const uint8_t *p8; + uint32_t *p32; + uint64_t *p64; + size_t i; + } u; + + u.p8 = (const uint8_t *)message; + + if (!kHasUnalignedAccess && (u.i & 0x7)) + { + memcpy(buf, message, length); + u.p64 = buf; + } + + size_t remainder = length%32; + uint64_t a=*hash1; + uint64_t b=*hash2; + uint64_t c=sc_const; + uint64_t d=sc_const; + + if (length > 15) + { + const uint64_t *end = u.p64 + (length/32)*4; + + // handle all complete sets of 32 bytes + for (; u.p64 < end; u.p64 += 4) + { + c += u.p64[0]; + d += u.p64[1]; + ShortMix(a,b,c,d); + a += u.p64[2]; + b += u.p64[3]; + } + + //Handle the case of 16+ remaining bytes. + if (remainder >= 16) + { + c += u.p64[0]; + d += u.p64[1]; + ShortMix(a,b,c,d); + u.p64 += 2; + remainder -= 16; + } + } + + // Handle the last 0..15 bytes, and its length + d += ((uint64_t)length) << 56; + switch (remainder) + { + case 15: + d += ((uint64_t)u.p8[14]) << 48; + FOLLY_FALLTHROUGH; + case 14: + d += ((uint64_t)u.p8[13]) << 40; + FOLLY_FALLTHROUGH; + case 13: + d += ((uint64_t)u.p8[12]) << 32; + FOLLY_FALLTHROUGH; + case 12: + d += u.p32[2]; + c += u.p64[0]; + break; + case 11: + d += ((uint64_t)u.p8[10]) << 16; + FOLLY_FALLTHROUGH; + case 10: + d += ((uint64_t)u.p8[9]) << 8; + FOLLY_FALLTHROUGH; + case 9: + d += (uint64_t)u.p8[8]; + FOLLY_FALLTHROUGH; + case 8: + c += u.p64[0]; + break; + case 7: + c += ((uint64_t)u.p8[6]) << 48; + FOLLY_FALLTHROUGH; + case 6: + c += ((uint64_t)u.p8[5]) << 40; + FOLLY_FALLTHROUGH; + case 5: + c += ((uint64_t)u.p8[4]) << 32; + FOLLY_FALLTHROUGH; + case 4: + c += u.p32[0]; + break; + case 3: + c += ((uint64_t)u.p8[2]) << 16; + FOLLY_FALLTHROUGH; + case 2: + c += ((uint64_t)u.p8[1]) << 8; + FOLLY_FALLTHROUGH; + case 1: + c += (uint64_t)u.p8[0]; + break; + case 0: + c += sc_const; + d += sc_const; + } + ShortEnd(a,b,c,d); + *hash1 = a; + *hash2 = b; +} + + + + +// do the whole hash in one call +void SpookyHashV2::Hash128( + const void *message, + size_t length, + uint64_t *hash1, + uint64_t *hash2) +{ + if (length < sc_bufSize) + { + Short(message, length, hash1, hash2); + return; + } + + uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; + uint64_t buf[sc_numVars]; + uint64_t *end; + union + { + const uint8_t *p8; + uint64_t *p64; + size_t i; + } u; + size_t remainder; + + h0=h3=h6=h9 = *hash1; + h1=h4=h7=h10 = *hash2; + h2=h5=h8=h11 = sc_const; + + u.p8 = (const uint8_t *)message; + end = u.p64 + (length/sc_blockSize)*sc_numVars; + + // handle all whole sc_blockSize blocks of bytes + if (kHasUnalignedAccess || ((u.i & 0x7) == 0)) + { + while (u.p64 < end) + { + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + else + { + while (u.p64 < end) + { + memcpy(buf, u.p64, sc_blockSize); + Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + + // handle the last partial block of sc_blockSize bytes + remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); + memcpy(buf, end, remainder); + memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); + ((uint8_t*)buf)[sc_blockSize - 1] = uint8_t(remainder); + + // do some final mixing + End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + *hash1 = h0; + *hash2 = h1; +} + + + +// init spooky state +void SpookyHashV2::Init(uint64_t seed1, uint64_t seed2) +{ + m_length = 0; + m_remainder = 0; + m_state[0] = seed1; + m_state[1] = seed2; +} + + +// add a message fragment to the state +void SpookyHashV2::Update(const void *message, size_t length) +{ + uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; + size_t newLength = length + m_remainder; + uint8_t remainder; + union + { + const uint8_t *p8; + uint64_t *p64; + size_t i; + } u; + const uint64_t *end; + + // Is this message fragment too short? If it is, stuff it away. + if (newLength < sc_bufSize) + { + memcpy(&((uint8_t *)m_data)[m_remainder], message, length); + m_length = length + m_length; + m_remainder = (uint8_t)newLength; + return; + } + + // init the variables + if (m_length < sc_bufSize) + { + h0=h3=h6=h9 = m_state[0]; + h1=h4=h7=h10 = m_state[1]; + h2=h5=h8=h11 = sc_const; + } + else + { + h0 = m_state[0]; + h1 = m_state[1]; + h2 = m_state[2]; + h3 = m_state[3]; + h4 = m_state[4]; + h5 = m_state[5]; + h6 = m_state[6]; + h7 = m_state[7]; + h8 = m_state[8]; + h9 = m_state[9]; + h10 = m_state[10]; + h11 = m_state[11]; + } + m_length = length + m_length; + + // if we've got anything stuffed away, use it now + if (m_remainder) + { + uint8_t prefix = sc_bufSize-m_remainder; + memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix); + u.p64 = m_data; + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p8 = ((const uint8_t *)message) + prefix; + length -= prefix; + } + else + { + u.p8 = (const uint8_t *)message; + } + + // handle all whole blocks of sc_blockSize bytes + end = u.p64 + (length/sc_blockSize)*sc_numVars; + remainder = (uint8_t)(length-((const uint8_t *)end-u.p8)); + if (kHasUnalignedAccess || (u.i & 0x7) == 0) + { + while (u.p64 < end) + { + Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + else + { + while (u.p64 < end) + { + memcpy(m_data, u.p8, sc_blockSize); + Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + u.p64 += sc_numVars; + } + } + + // stuff away the last few bytes + m_remainder = remainder; + memcpy(m_data, end, remainder); + + // stuff away the variables + m_state[0] = h0; + m_state[1] = h1; + m_state[2] = h2; + m_state[3] = h3; + m_state[4] = h4; + m_state[5] = h5; + m_state[6] = h6; + m_state[7] = h7; + m_state[8] = h8; + m_state[9] = h9; + m_state[10] = h10; + m_state[11] = h11; +} + + +// report the hash for the concatenation of all message fragments so far +void SpookyHashV2::Final(uint64_t *hash1, uint64_t *hash2) const +{ + // init the variables + if (m_length < sc_bufSize) + { + *hash1 = m_state[0]; + *hash2 = m_state[1]; + Short( m_data, m_length, hash1, hash2); + return; + } + + uint64_t buf[2*sc_numVars]; + memcpy(buf, m_data, sizeof(buf)); + uint64_t *data = buf; + uint8_t remainder = m_remainder; + + uint64_t h0 = m_state[0]; + uint64_t h1 = m_state[1]; + uint64_t h2 = m_state[2]; + uint64_t h3 = m_state[3]; + uint64_t h4 = m_state[4]; + uint64_t h5 = m_state[5]; + uint64_t h6 = m_state[6]; + uint64_t h7 = m_state[7]; + uint64_t h8 = m_state[8]; + uint64_t h9 = m_state[9]; + uint64_t h10 = m_state[10]; + uint64_t h11 = m_state[11]; + + if (remainder >= sc_blockSize) + { + // m_data can contain two blocks; handle any whole first block + Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + data += sc_numVars; + remainder -= sc_blockSize; + } + + // mix in the last partial block, and the length mod sc_blockSize + memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder)); + + ((uint8_t *)data)[sc_blockSize-1] = remainder; + + // do some final mixing + End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + + *hash1 = h0; + *hash2 = h1; +} + +// clang-format on + +} // namespace hash +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.h b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.h new file mode 100644 index 000000000..8e9be8c5b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/SpookyHashV2.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 is version 2 of SpookyHash, incompatible with version 1. +// +// SpookyHash: a 128-bit noncryptographic hash function +// By Bob Jenkins, public domain +// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right +// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right +// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas +// Feb 2 2012: production, same bits as beta +// Feb 5 2012: adjusted definitions of uint* to be more portable +// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. +// August 5 2012: SpookyV2 (different results) +// +// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. +// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. +// +// This was developed for and tested on 64-bit x86-compatible processors. +// It assumes the processor is little-endian. There is a macro +// controlling whether unaligned reads are allowed (by default they are). +// This should be an equally good hash on big-endian machines, but it will +// compute different results on them than on little-endian machines. +// +// Google's CityHash has similar specs to SpookyHash, and CityHash is faster +// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders +// of magnitude slower. CRCs are two or more times slower, but unlike +// SpookyHash, they have nice math for combining the CRCs of pieces to form +// the CRCs of wholes. There are also cryptographic hashes, but those are even +// slower than MD5. +// + +#pragma once + +#include <cstddef> +#include <cstdint> + +namespace folly { +namespace hash { + +// clang-format off + +class SpookyHashV2 +{ +public: + // + // SpookyHash: hash a single message in one call, produce 128-bit output + // + static void Hash128( + const void *message, // message to hash + size_t length, // length of message in bytes + uint64_t *hash1, // in/out: in seed 1, out hash value 1 + uint64_t *hash2); // in/out: in seed 2, out hash value 2 + + // + // Hash64: hash a single message in one call, return 64-bit output + // + static uint64_t Hash64( + const void *message, // message to hash + size_t length, // length of message in bytes + uint64_t seed) // seed + { + uint64_t hash1 = seed; + Hash128(message, length, &hash1, &seed); + return hash1; + } + + // + // Hash32: hash a single message in one call, produce 32-bit output + // + static uint32_t Hash32( + const void *message, // message to hash + size_t length, // length of message in bytes + uint32_t seed) // seed + { + uint64_t hash1 = seed, hash2 = seed; + Hash128(message, length, &hash1, &hash2); + return (uint32_t)hash1; + } + + // + // Init: initialize the context of a SpookyHash + // + void Init( + uint64_t seed1, // any 64-bit value will do, including 0 + uint64_t seed2); // different seeds produce independent hashes + + // + // Update: add a piece of a message to a SpookyHash state + // + void Update( + const void *message, // message fragment + size_t length); // length of message fragment in bytes + + + // + // Final: compute the hash for the current SpookyHash state + // + // This does not modify the state; you can keep updating it afterward + // + // The result is the same as if SpookyHash() had been called with + // all the pieces concatenated into one message. + // + void Final( + uint64_t *hash1, // out only: first 64 bits of hash value. + uint64_t *hash2) const; // out only: second 64 bits of hash value. + + // + // left rotate a 64-bit value by k bytes + // + static inline uint64_t Rot64(uint64_t x, int k) + { + return (x << k) | (x >> (64 - k)); + } + + // + // This is used if the input is 96 bytes long or longer. + // + // The internal state is fully overwritten every 96 bytes. + // Every input bit appears to cause at least 128 bits of entropy + // before 96 other bytes are combined, when run forward or backward + // For every input bit, + // Two inputs differing in just that input bit + // Where "differ" means xor or subtraction + // And the base value is random + // When run forward or backwards one Mix + // I tried 3 pairs of each; they all differed by at least 212 bits. + // + static inline void Mix( + const uint64_t *data, + uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3, + uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7, + uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11) + { + s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1; + s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2; + s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3; + s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4; + s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5; + s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6; + s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7; + s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8; + s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9; + s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10; + s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11; + s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0; + } + + // + // Mix all 12 inputs together so that h0, h1 are a hash of them all. + // + // For two inputs differing in just the input bits + // Where "differ" means xor or subtraction + // And the base value is random, or a counting value starting at that bit + // The final result will have each bit of h0, h1 flip + // For every input bit, + // with probability 50 +- .3% + // For every pair of input bits, + // with probability 50 +- 3% + // + // This does not rely on the last Mix() call having already mixed some. + // Two iterations was almost good enough for a 64-bit result, but a + // 128-bit result is reported, so End() does three iterations. + // + static inline void EndPartial( + uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, + uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, + uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) + { + h11+= h1; h2 ^= h11; h1 = Rot64(h1,44); + h0 += h2; h3 ^= h0; h2 = Rot64(h2,15); + h1 += h3; h4 ^= h1; h3 = Rot64(h3,34); + h2 += h4; h5 ^= h2; h4 = Rot64(h4,21); + h3 += h5; h6 ^= h3; h5 = Rot64(h5,38); + h4 += h6; h7 ^= h4; h6 = Rot64(h6,33); + h5 += h7; h8 ^= h5; h7 = Rot64(h7,10); + h6 += h8; h9 ^= h6; h8 = Rot64(h8,13); + h7 += h9; h10^= h7; h9 = Rot64(h9,38); + h8 += h10; h11^= h8; h10= Rot64(h10,53); + h9 += h11; h0 ^= h9; h11= Rot64(h11,42); + h10+= h0; h1 ^= h10; h0 = Rot64(h0,54); + } + + static inline void End( + const uint64_t *data, + uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, + uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, + uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) + { + h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; + h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; + h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); + } + + // + // The goal is for each bit of the input to expand into 128 bits of + // apparent entropy before it is fully overwritten. + // n trials both set and cleared at least m bits of h0 h1 h2 h3 + // n: 2 m: 29 + // n: 3 m: 46 + // n: 4 m: 57 + // n: 5 m: 107 + // n: 6 m: 146 + // n: 7 m: 152 + // when run forwards or backwards + // for all 1-bit and 2-bit diffs + // with diffs defined by either xor or subtraction + // with a base of all zeros plus a counter, or plus another bit, or random + // + static inline void ShortMix(uint64_t &h0, uint64_t &h1, + uint64_t &h2, uint64_t &h3) + { + h2 = Rot64(h2,50); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,52); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,30); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,41); h1 += h2; h3 ^= h1; + h2 = Rot64(h2,54); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,48); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,38); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,37); h1 += h2; h3 ^= h1; + h2 = Rot64(h2,62); h2 += h3; h0 ^= h2; + h3 = Rot64(h3,34); h3 += h0; h1 ^= h3; + h0 = Rot64(h0,5); h0 += h1; h2 ^= h0; + h1 = Rot64(h1,36); h1 += h2; h3 ^= h1; + } + + // + // Mix all 4 inputs together so that h0, h1 are a hash of them all. + // + // For two inputs differing in just the input bits + // Where "differ" means xor or subtraction + // And the base value is random, or a counting value starting at that bit + // The final result will have each bit of h0, h1 flip + // For every input bit, + // with probability 50 +- .3% (it is probably better than that) + // For every pair of input bits, + // with probability 50 +- .75% (the worst case is approximately that) + // + static inline void ShortEnd(uint64_t &h0, uint64_t &h1, + uint64_t &h2, uint64_t &h3) + { + h3 ^= h2; h2 = Rot64(h2,15); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,52); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,26); h1 += h0; + h2 ^= h1; h1 = Rot64(h1,51); h2 += h1; + h3 ^= h2; h2 = Rot64(h2,28); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,9); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,47); h1 += h0; + h2 ^= h1; h1 = Rot64(h1,54); h2 += h1; + h3 ^= h2; h2 = Rot64(h2,32); h3 += h2; + h0 ^= h3; h3 = Rot64(h3,25); h0 += h3; + h1 ^= h0; h0 = Rot64(h0,63); h1 += h0; + } + +private: + + // + // Short is used for messages under 192 bytes in length + // Short has a low startup cost, the normal mode is good for long + // keys, the cost crossover is at about 192 bytes. The two modes were + // held to the same quality bar. + // + static void Short( + const void *message, // message (byte array, not necessarily aligned) + size_t length, // length of message (in bytes) + uint64_t *hash1, // in/out: in the seed, out the hash value + uint64_t *hash2); // in/out: in the seed, out the hash value + + // number of uint64_t's in internal state + static constexpr size_t sc_numVars = 12; + + // size of the internal state + static constexpr size_t sc_blockSize = sc_numVars*8; + + // size of buffer of unhashed data, in bytes + static constexpr size_t sc_bufSize = 2*sc_blockSize; + + // + // sc_const: a constant which: + // * is not zero + // * is odd + // * is a not-very-regular mix of 1's and 0's + // * does not need any other special mathematical properties + // + static constexpr uint64_t sc_const = 0xdeadbeefdeadbeefULL; + + uint64_t m_data[2*sc_numVars]; // unhashed data, for partial messages + uint64_t m_state[sc_numVars]; // internal state of the hash + size_t m_length; // total length of the input so far + uint8_t m_remainder; // length of unhashed data stashed in m_data +}; + +// clang-format on + +} // namespace hash +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/hash/detail/ChecksumDetail.h b/ios/Pods/Flipper-Folly/folly/hash/detail/ChecksumDetail.h new file mode 100644 index 000000000..a1c0f07e8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/hash/detail/ChecksumDetail.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +#if FOLLY_SSE_PREREQ(4, 2) +#include <immintrin.h> +#endif + +#include <stdint.h> +#include <cstddef> + +namespace folly { +namespace detail { + +/** + * Compute a CRC-32C checksum of a buffer using a hardware-accelerated + * implementation. + * + * @note This function is exposed to support special cases where the + * calling code is absolutely certain it ought to invoke a hardware- + * accelerated CRC-32C implementation - unit tests, for example. For + * all other scenarios, please call crc32c() and let it pick an + * implementation based on the capabilities of the underlying CPU. + */ +uint32_t +crc32c_hw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/** + * Check whether a hardware-accelerated CRC-32C implementation is + * supported on the current CPU. + */ +bool crc32c_hw_supported(); + +/** + * Compute a CRC-32C checksum of a buffer using a portable, + * software-only implementation. + * + * @note This function is exposed to support special cases where the + * calling code is absolutely certain it wants to use the software + * implementation instead of the hardware-accelerated code - unit + * tests, for example. For all other scenarios, please call crc32c() + * and let it pick an implementation based on the capabilities of + * the underlying CPU. + */ +uint32_t +crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/** + * Compute a CRC-32 checksum of a buffer using a hardware-accelerated + * implementation. + * + * @note This function is exposed to support special cases where the + * calling code is absolutely certain it ought to invoke a hardware- + * accelerated CRC-32 implementation - unit tests, for example. For + * all other scenarios, please call crc32() and let it pick an + * implementation based on the capabilities of the underlying CPU. + */ +uint32_t +crc32_hw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +#if FOLLY_SSE_PREREQ(4, 2) +uint32_t +crc32_hw_aligned(uint32_t remainder, const __m128i* p, size_t vec_count); +#endif + +/** + * Check whether a hardware-accelerated CRC-32 implementation is + * supported on the current CPU. + */ +bool crc32_hw_supported(); + +/** + * Compute a CRC-32 checksum of a buffer using a portable, + * software-only implementation. + * + * @note This function is exposed to support special cases where the + * calling code is absolutely certain it wants to use the software + * implementation instead of the hardware-accelerated code - unit + * tests, for example. For all other scenarios, please call crc32() + * and let it pick an implementation based on the capabilities of + * the underlying CPU. + */ +uint32_t +crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U); + +/* See Checksum.h for details. + * + * crc2len *must* be a power of two >= 4. + */ +uint32_t crc32_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len); +uint32_t crc32_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len); +uint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len); +uint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len); + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/init/Init.h b/ios/Pods/Flipper-Folly/folly/init/Init.h new file mode 100644 index 000000000..6316ac490 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/init/Init.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <folly/CPortability.h> +#include <bitset> + +namespace folly { +class InitOptions { + public: + InitOptions() noexcept; + + bool remove_flags{true}; + + // mask of all fatal (default handler of terminating the process) signals for + // which `init()` will install handler that print stack traces and invokes + // previously established handler (or terminate if there were none). + // Signals that are not in `symbolizer::kAllFatalSignals` will be ignored + // if passed here + // Defaults to all signal in `symbolizer::kAllFatalSignals` + std::bitset<64> fatal_signals; + + InitOptions& removeFlags(bool remove) { + remove_flags = remove; + return *this; + } + + InitOptions& fatalSignals(unsigned long val) { + fatal_signals = val; + return *this; + } +}; + +/* + * Calls common init functions in the necessary order + * Among other things, this ensures that folly::Singletons are initialized + * correctly and installs signal handlers for a superior debugging experience. + * It also initializes gflags and glog. + * + * @param argc, argv arguments to your main + * @param removeFlags if true, will update argc,argv to remove recognized + * gflags passed on the command line + * @param options options + */ + +void init(int* argc, char*** argv, bool removeFlags = true); + +void init(int* argc, char*** argv, InitOptions options); + +/* + * An RAII object to be constructed at the beginning of main() and destructed + * implicitly at the end of main(). + * + * The constructor performs the same setup as folly::init(), including + * initializing singletons managed by folly::Singleton. + * + * The destructor destroys all singletons managed by folly::Singleton, yielding + * better shutdown behavior when performed at the end of main(). In particular, + * this guarantees that all singletons managed by folly::Singleton are destroyed + * before all Meyers singletons are destroyed. + */ +class Init { + public: + // Force ctor & dtor out of line for better stack traces even with LTO. + FOLLY_NOINLINE Init(int* argc, char*** argv, bool removeFlags = true); + + FOLLY_NOINLINE Init(int* argc, char*** argv, InitOptions options); + + FOLLY_NOINLINE ~Init(); + + Init(Init const&) = delete; + Init(Init&&) = delete; + Init& operator=(Init const&) = delete; + Init& operator=(Init&&) = delete; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/init/Phase.h b/ios/Pods/Flipper-Folly/folly/init/Phase.h new file mode 100644 index 000000000..88154603e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/init/Phase.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +namespace folly { + +/// Process phases for programs that use Folly: +/// - Init: Not all globals may have been initialized. +/// - Regular: All globals have been initialized and have not +/// been destroyed. +/// - Exit: Some globals may have been destroyed. + +/// Process phases +enum class ProcessPhase { + Init = 0, + Regular = 1, + Exit = 2, +}; + +/// Start Regular phase and register handler to set Exit phase. +/// To be called exactly once in each program that uses Folly. +/// Ideally, it is to be called from folly::init(), which in turn +/// is to be called by every program that uses Folly. +void set_process_phases(); + +/// Get the current process phase. +ProcessPhase get_process_phase() noexcept; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/Cursor-inl.h b/ios/Pods/Flipper-Folly/folly/io/Cursor-inl.h new file mode 100644 index 000000000..f7e5f46fd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/Cursor-inl.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { +namespace io { +namespace detail { + +/* + * Helper classes for use with CursorBase::readWhile() + */ +class CursorStringAppender { + public: + void append(ByteRange bytes) { + str_.append(reinterpret_cast<char const*>(bytes.data()), bytes.size()); + } + std::string extractString() { + return std::move(str_); + } + + private: + std::string str_; +}; + +class CursorNoopAppender { + public: + void append(ByteRange) {} +}; + +template <class Derived, class BufType> +std::string CursorBase<Derived, BufType>::readTerminatedString( + char termChar, + size_t maxLength) { + size_t bytesRead{0}; + auto keepReading = [&bytesRead, termChar, maxLength](uint8_t byte) { + if (byte == termChar) { + return false; + } + ++bytesRead; + if (bytesRead >= maxLength) { + throw std::length_error("string overflow"); + } + return true; + }; + + auto result = readWhile(keepReading); + // skip over the terminator character + if (isAtEnd()) { + throw_exception<std::out_of_range>("terminator not found"); + } + skip(1); + + return result; +} + +template <class Derived, class BufType> +template <typename Predicate> +std::string CursorBase<Derived, BufType>::readWhile( + const Predicate& predicate) { + CursorStringAppender s; + readWhile(predicate, s); + return s.extractString(); +} + +template <class Derived, class BufType> +template <typename Predicate, typename Output> +void CursorBase<Derived, BufType>::readWhile( + const Predicate& predicate, + Output& out) { + while (true) { + auto peeked = peekBytes(); + if (peeked.empty()) { + return; + } + for (size_t idx = 0; idx < peeked.size(); ++idx) { + if (!predicate(peeked[idx])) { + peeked.reset(peeked.data(), idx); + out.append(peeked); + skip(idx); + return; + } + } + out.append(peeked); + skip(peeked.size()); + } +} + +template <class Derived, class BufType> +template <typename Predicate> +void CursorBase<Derived, BufType>::skipWhile(const Predicate& predicate) { + CursorNoopAppender appender; + readWhile(predicate, appender); +} +} // namespace detail +} // namespace io +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/Cursor.cpp b/ios/Pods/Flipper-Folly/folly/io/Cursor.cpp new file mode 100644 index 000000000..d0503ae28 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/Cursor.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/Cursor.h> + +#include <cstdio> + +#include <folly/ScopeGuard.h> + +namespace folly { +namespace io { + +void Appender::printf(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +void Appender::vprintf(const char* fmt, va_list ap) { + // Make a copy of ap in case we need to retry. + // We use ap on the first attempt, so it always gets advanced + // passed the used arguments. We'll only use apCopy if we need to retry. + va_list apCopy; + va_copy(apCopy, ap); + SCOPE_EXIT { + va_end(apCopy); + }; + + // First try writing into our available data space. + int ret = + vsnprintf(reinterpret_cast<char*>(writableData()), length(), fmt, ap); + if (ret < 0) { + throw std::runtime_error("error formatting printf() data"); + } + auto len = size_t(ret); + // vsnprintf() returns the number of characters that would be printed, + // not including the terminating nul. + if (len < length()) { + // All of the data was successfully written. + append(len); + return; + } + + // There wasn't enough room for the data. + // Allocate more room, and then retry. + ensure(len + 1); + ret = + vsnprintf(reinterpret_cast<char*>(writableData()), length(), fmt, apCopy); + if (ret < 0) { + throw std::runtime_error("error formatting printf() data"); + } + len = size_t(ret); + if (len >= length()) { + // This shouldn't ever happen. + throw std::runtime_error( + "unexpectedly out of buffer space on second " + "vsnprintf() attmept"); + } + append(len); +} +} // namespace io +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/Cursor.h b/ios/Pods/Flipper-Folly/folly/io/Cursor.h new file mode 100644 index 000000000..d58e5ba9a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/Cursor.h @@ -0,0 +1,1291 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <cstdarg> +#include <cstring> +#include <memory> +#include <stdexcept> +#include <type_traits> + +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/io/IOBuf.h> +#include <folly/io/IOBufQueue.h> +#include <folly/lang/Bits.h> +#include <folly/lang/Exception.h> + +/** + * Cursor class for fast iteration over IOBuf chains. + * + * Cursor - Read-only access + * + * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain + * RWUnshareCursor - Read-write access, calls unshare on write (COW) + * Appender - Write access, assumes private access to IOBuf chain + * + * Note that RW cursors write in the preallocated part of buffers (that is, + * between the buffer's data() and tail()), while Appenders append to the end + * of the buffer (between the buffer's tail() and bufferEnd()). Appenders + * automatically adjust the buffer pointers, so you may only use one + * Appender with a buffer chain; for this reason, Appenders assume private + * access to the buffer (you need to call unshare() yourself if necessary). + **/ +namespace folly { +namespace io { + +namespace detail { + +template <class Derived, class BufType> +class CursorBase { + // Make all the templated classes friends for copy constructor. + template <class D, typename B> + friend class CursorBase; + + public: + explicit CursorBase(BufType* buf) : crtBuf_(buf), buffer_(buf) { + if (crtBuf_) { + crtPos_ = crtBegin_ = crtBuf_->data(); + crtEnd_ = crtBuf_->tail(); + } + } + + CursorBase(BufType* buf, size_t len) : crtBuf_(buf), buffer_(buf) { + if (crtBuf_) { + crtPos_ = crtBegin_ = crtBuf_->data(); + crtEnd_ = crtBuf_->tail(); + if (crtPos_ + len < crtEnd_) { + crtEnd_ = crtPos_ + len; + } + remainingLen_ = len - (crtEnd_ - crtPos_); + } + } + + /** + * Copy constructor. + * + * This also allows constructing a CursorBase from other derived types. + * For instance, this allows constructing a Cursor from an RWPrivateCursor. + */ + template <class OtherDerived, class OtherBuf> + explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor) + : crtBuf_(cursor.crtBuf_), + buffer_(cursor.buffer_), + crtBegin_(cursor.crtBegin_), + crtEnd_(cursor.crtEnd_), + crtPos_(cursor.crtPos_), + absolutePos_(cursor.absolutePos_), + remainingLen_(cursor.remainingLen_) {} + + template <class OtherDerived, class OtherBuf> + explicit CursorBase( + const CursorBase<OtherDerived, OtherBuf>& cursor, + size_t len) + : crtBuf_(cursor.crtBuf_), + buffer_(cursor.buffer_), + crtBegin_(cursor.crtBegin_), + crtEnd_(cursor.crtEnd_), + crtPos_(cursor.crtPos_), + absolutePos_(cursor.absolutePos_) { + if (cursor.isBounded() && len > cursor.remainingLen_ + cursor.length()) { + throw_exception<std::out_of_range>("underflow"); + } + if (crtPos_ + len < crtEnd_) { + crtEnd_ = crtPos_ + len; + } + remainingLen_ = len - (crtEnd_ - crtPos_); + } + + /** + * Reset cursor to point to a new buffer. + */ + void reset(BufType* buf) { + crtBuf_ = buf; + buffer_ = buf; + absolutePos_ = 0; + remainingLen_ = std::numeric_limits<size_t>::max(); + if (crtBuf_) { + crtPos_ = crtBegin_ = crtBuf_->data(); + crtEnd_ = crtBuf_->tail(); + } + } + + /** + * Get the current Cursor position relative to the head of IOBuf chain. + */ + size_t getCurrentPosition() const { + dcheckIntegrity(); + return (crtPos_ - crtBegin_) + absolutePos_; + } + + const uint8_t* data() const { + dcheckIntegrity(); + return crtPos_; + } + + /** + * Return the remaining space available in the current IOBuf. + * + * May return 0 if the cursor is at the end of an IOBuf. Use peekBytes() + * instead if you want to avoid this. peekBytes() will advance to the next + * non-empty IOBuf (up to the end of the chain) if the cursor is currently + * pointing at the end of a buffer. + */ + size_t length() const { + dcheckIntegrity(); + return crtEnd_ - crtPos_; + } + + /** + * Return the space available until the end of the entire IOBuf chain. + * For bounded Cursors, return the available space until the boundary. + */ + size_t totalLength() const { + size_t len = 0; + const IOBuf* buf = crtBuf_->next(); + while (buf != buffer_ && len < remainingLen_) { + len += buf->length(); + buf = buf->next(); + } + return std::min(len, remainingLen_) + length(); + } + + /** + * Return true if the cursor could advance the specified number of bytes + * from its current position. + * This is useful for applications that want to do checked reads instead of + * catching exceptions and is more efficient than using totalLength as it + * walks the minimal set of buffers in the chain to determine the result. + */ + bool canAdvance(size_t amount) const { + if (isBounded() && amount > remainingLen_ + length()) { + return false; + } + const IOBuf* nextBuf = crtBuf_; + size_t available = length(); + do { + if (available >= amount) { + return true; + } + amount -= available; + nextBuf = nextBuf->next(); + available = nextBuf->length(); + } while (nextBuf != buffer_); + return false; + } + + /* + * Return true if the cursor is at the end of the entire IOBuf chain. + */ + bool isAtEnd() const { + dcheckIntegrity(); + // Check for the simple cases first. + if (crtPos_ != crtEnd_) { + return false; + } + if (crtBuf_ == buffer_->prev()) { + return true; + } + if (isBounded() && remainingLen_ == 0) { + return true; + } + // We are at the end of a buffer, but it isn't the last buffer. + // We might still be at the end if the remaining buffers in the chain are + // empty. + const IOBuf* buf = crtBuf_->next(); + while (buf != buffer_) { + if (buf->length() > 0) { + return false; + } + buf = buf->next(); + } + return true; + } + + /** + * Advances the cursor to the end of the entire IOBuf chain. + */ + void advanceToEnd() { + // Simple case, we're already in the last IOBuf. + if (crtBuf_ == buffer_->prev()) { + crtPos_ = crtEnd_; + return; + } + + auto* nextBuf = crtBuf_->next(); + while (nextBuf != buffer_) { + if (isBounded() && remainingLen_ == 0) { + crtPos_ = crtEnd_; + return; + } + absolutePos_ += crtEnd_ - crtBegin_; + + crtBuf_ = nextBuf; + nextBuf = crtBuf_->next(); + crtBegin_ = crtBuf_->data(); + crtEnd_ = crtBuf_->tail(); + if (isBounded()) { + if (crtBegin_ + remainingLen_ < crtEnd_) { + crtEnd_ = crtBegin_ + remainingLen_; + } + remainingLen_ -= crtEnd_ - crtBegin_; + } + crtPos_ = crtEnd_; + derived().advanceDone(); + } + } + + Derived& operator+=(size_t offset) { + Derived* p = static_cast<Derived*>(this); + p->skip(offset); + return *p; + } + Derived operator+(size_t offset) const { + Derived other(*this); + other.skip(offset); + return other; + } + + Derived& operator-=(size_t offset) { + Derived* p = static_cast<Derived*>(this); + p->retreat(offset); + return *p; + } + Derived operator-(size_t offset) const { + Derived other(*this); + other.retreat(offset); + return other; + } + + /** + * Compare cursors for equality/inequality. + * + * Two cursors are equal if they are pointing to the same location in the + * same IOBuf chain. + */ + bool operator==(const Derived& other) const { + const IOBuf* crtBuf = crtBuf_; + auto crtPos = crtPos_; + // We can be pointing to the end of a buffer chunk, find first non-empty. + while (crtPos == crtBuf->tail() && crtBuf != buffer_->prev()) { + crtBuf = crtBuf->next(); + crtPos = crtBuf->data(); + } + + const IOBuf* crtBufOther = other.crtBuf_; + auto crtPosOther = other.crtPos_; + // We can be pointing to the end of a buffer chunk, find first non-empty. + while (crtPosOther == crtBufOther->tail() && + crtBufOther != other.buffer_->prev()) { + crtBufOther = crtBufOther->next(); + crtPosOther = crtBufOther->data(); + } + return (crtPos == crtPosOther) && (crtBuf == crtBufOther); + } + bool operator!=(const Derived& other) const { + return !operator==(other); + } + + template <class T> + typename std::enable_if<std::is_arithmetic<T>::value, bool>::type tryRead( + T& val) { + if (LIKELY(crtPos_ + sizeof(T) <= crtEnd_)) { + val = loadUnaligned<T>(data()); + crtPos_ += sizeof(T); + return true; + } + return pullAtMostSlow(&val, sizeof(T)) == sizeof(T); + } + + template <class T> + bool tryReadBE(T& val) { + const bool result = tryRead(val); + val = Endian::big(val); + return result; + } + + template <class T> + bool tryReadLE(T& val) { + const bool result = tryRead(val); + val = Endian::little(val); + return result; + } + + template <class T> + T read() { + if (LIKELY(crtPos_ + sizeof(T) <= crtEnd_)) { + T val = loadUnaligned<T>(data()); + crtPos_ += sizeof(T); + return val; + } else { + return readSlow<T>(); + } + } + + template <class T> + T readBE() { + return Endian::big(read<T>()); + } + + template <class T> + T readLE() { + return Endian::little(read<T>()); + } + + /** + * Read a fixed-length string. + * + * The std::string-based APIs should probably be avoided unless you + * ultimately want the data to live in an std::string. You're better off + * using the pull() APIs to copy into a raw buffer otherwise. + */ + std::string readFixedString(size_t len) { + std::string str; + str.reserve(len); + if (LIKELY(length() >= len)) { + str.append(reinterpret_cast<const char*>(data()), len); + crtPos_ += len; + } else { + readFixedStringSlow(&str, len); + } + return str; + } + + /** + * Read a string consisting of bytes until the given terminator character is + * seen. Raises an std::length_error if maxLength bytes have been processed + * before the terminator is seen. + * + * See comments in readFixedString() about when it's appropriate to use this + * vs. using pull(). + */ + std::string readTerminatedString( + char termChar = '\0', + size_t maxLength = std::numeric_limits<size_t>::max()); + + /* + * Read all bytes until the specified predicate returns true. + * + * The predicate will be called on each byte in turn, until it returns false + * or until the end of the IOBuf chain is reached. + * + * Returns the result as a string. + */ + template <typename Predicate> + std::string readWhile(const Predicate& predicate); + + /* + * Read all bytes until the specified predicate returns true. + * + * This is a more generic version of readWhile() takes an arbitrary Output + * object, and calls Output::append() with each chunk of matching data. + */ + template <typename Predicate, typename Output> + void readWhile(const Predicate& predicate, Output& out); + + /* + * Skip all bytes until the specified predicate returns true. + * + * The predicate will be called on each byte in turn, until it returns false + * or until the end of the IOBuf chain is reached. + */ + template <typename Predicate> + void skipWhile(const Predicate& predicate); + + size_t skipAtMost(size_t len) { + dcheckIntegrity(); + if (LIKELY(crtPos_ + len < crtEnd_)) { + crtPos_ += len; + return len; + } + return skipAtMostSlow(len); + } + + void skip(size_t len) { + dcheckIntegrity(); + if (LIKELY(crtPos_ + len < crtEnd_)) { + crtPos_ += len; + } else { + skipSlow(len); + } + } + + /** + * Skip bytes in the current IOBuf without advancing to the next one. + * Precondition: length() >= len + */ + void skipNoAdvance(size_t len) { + DCHECK_LE(len, length()); + crtPos_ += len; + } + + size_t retreatAtMost(size_t len) { + dcheckIntegrity(); + if (len <= static_cast<size_t>(crtPos_ - crtBegin_)) { + crtPos_ -= len; + return len; + } + return retreatAtMostSlow(len); + } + + void retreat(size_t len) { + dcheckIntegrity(); + if (len <= static_cast<size_t>(crtPos_ - crtBegin_)) { + crtPos_ -= len; + } else { + retreatSlow(len); + } + } + + size_t pullAtMost(void* buf, size_t len) { + dcheckIntegrity(); + // Fast path: it all fits in one buffer. + if (LIKELY(crtPos_ + len <= crtEnd_)) { + memcpy(buf, data(), len); + crtPos_ += len; + return len; + } + return pullAtMostSlow(buf, len); + } + + void pull(void* buf, size_t len) { + if (UNLIKELY(len == 0)) { + return; + } + dcheckIntegrity(); + if (LIKELY(crtPos_ + len <= crtEnd_)) { + memcpy(buf, data(), len); + crtPos_ += len; + } else { + pullSlow(buf, len); + } + } + + /** + * Return the available data in the current buffer. + * If you want to gather more data from the chain into a contiguous region + * (for hopefully zero-copy access), use gather() before peekBytes(). + */ + ByteRange peekBytes() { + // Ensure that we're pointing to valid data + size_t available = length(); + while (UNLIKELY(available == 0 && tryAdvanceBuffer())) { + available = length(); + } + return ByteRange{data(), available}; + } + + /** + * Alternate version of peekBytes() that returns a std::pair + * instead of a ByteRange. (This method pre-dates ByteRange.) + * + * This function will eventually be deprecated. + */ + std::pair<const uint8_t*, size_t> peek() { + auto bytes = peekBytes(); + return std::make_pair(bytes.data(), bytes.size()); + } + + void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) { + if (UNLIKELY(cloneAtMost(buf, len) != len)) { + throw_exception<std::out_of_range>("underflow"); + } + } + + void clone(folly::IOBuf& buf, size_t len) { + if (UNLIKELY(cloneAtMost(buf, len) != len)) { + throw_exception<std::out_of_range>("underflow"); + } + } + + size_t cloneAtMost(folly::IOBuf& buf, size_t len) { + // We might be at the end of buffer. + advanceBufferIfEmpty(); + + std::unique_ptr<folly::IOBuf> tmp; + size_t copied = 0; + for (int loopCount = 0; true; ++loopCount) { + // Fast path: it all fits in one buffer. + size_t available = length(); + if (LIKELY(available >= len)) { + if (loopCount == 0) { + crtBuf_->cloneOneInto(buf); + buf.trimStart(crtPos_ - crtBegin_); + buf.trimEnd(buf.length() - len); + } else { + tmp = crtBuf_->cloneOne(); + tmp->trimStart(crtPos_ - crtBegin_); + tmp->trimEnd(tmp->length() - len); + buf.prependChain(std::move(tmp)); + } + + crtPos_ += len; + advanceBufferIfEmpty(); + return copied + len; + } + + if (loopCount == 0) { + crtBuf_->cloneOneInto(buf); + buf.trimStart(crtPos_ - crtBegin_); + } else { + tmp = crtBuf_->cloneOne(); + tmp->trimStart(crtPos_ - crtBegin_); + buf.prependChain(std::move(tmp)); + } + + copied += available; + if (UNLIKELY(!tryAdvanceBuffer())) { + return copied; + } + len -= available; + } + } + + size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) { + if (!buf) { + buf = std::make_unique<folly::IOBuf>(); + } + return cloneAtMost(*buf, len); + } + + /** + * Return the distance between two cursors. + */ + size_t operator-(const CursorBase& other) const { + BufType* otherBuf = other.crtBuf_; + size_t len = 0; + + if (otherBuf != crtBuf_) { + if (other.remainingLen_ == 0) { + len += otherBuf->tail() - other.crtPos_; + } else { + len += other.crtEnd_ - other.crtPos_; + } + + for (otherBuf = otherBuf->next(); + otherBuf != crtBuf_ && otherBuf != other.buffer_; + otherBuf = otherBuf->next()) { + len += otherBuf->length(); + } + + if (otherBuf == other.buffer_) { + throw_exception<std::out_of_range>("wrap-around"); + } + + len += crtPos_ - crtBegin_; + } else { + if (crtPos_ < other.crtPos_) { + throw_exception<std::out_of_range>("underflow"); + } + + len += crtPos_ - other.crtPos_; + } + + return len; + } + + /** + * Return the distance from the given IOBuf to the this cursor. + */ + size_t operator-(const BufType* buf) const { + size_t len = 0; + + const BufType* curBuf = buf; + while (curBuf != crtBuf_) { + len += curBuf->length(); + curBuf = curBuf->next(); + if (curBuf == buf || curBuf == buffer_) { + throw_exception<std::out_of_range>("wrap-around"); + } + } + + len += crtPos_ - crtBegin_; + return len; + } + + bool isBounded() const { + return remainingLen_ != std::numeric_limits<size_t>::max(); + } + + protected: + void dcheckIntegrity() const { + DCHECK(crtBegin_ <= crtPos_ && crtPos_ <= crtEnd_); + DCHECK(crtBuf_ == nullptr || crtBegin_ == crtBuf_->data()); + DCHECK( + crtBuf_ == nullptr || + (std::size_t)(crtEnd_ - crtBegin_) <= crtBuf_->length()); + } + + ~CursorBase() {} + + BufType* head() { + return buffer_; + } + + bool tryAdvanceBuffer() { + BufType* nextBuf = crtBuf_->next(); + if (UNLIKELY(nextBuf == buffer_) || remainingLen_ == 0) { + crtPos_ = crtEnd_; + return false; + } + + absolutePos_ += crtEnd_ - crtBegin_; + crtBuf_ = nextBuf; + crtPos_ = crtBegin_ = crtBuf_->data(); + crtEnd_ = crtBuf_->tail(); + if (isBounded()) { + if (crtPos_ + remainingLen_ < crtEnd_) { + crtEnd_ = crtPos_ + remainingLen_; + } + remainingLen_ -= crtEnd_ - crtPos_; + } + derived().advanceDone(); + return true; + } + + bool tryRetreatBuffer() { + if (UNLIKELY(crtBuf_ == buffer_)) { + crtPos_ = crtBegin_; + return false; + } + if (isBounded()) { + remainingLen_ += crtEnd_ - crtBegin_; + } + crtBuf_ = crtBuf_->prev(); + crtBegin_ = crtBuf_->data(); + crtPos_ = crtEnd_ = crtBuf_->tail(); + absolutePos_ -= crtEnd_ - crtBegin_; + derived().advanceDone(); + return true; + } + + void advanceBufferIfEmpty() { + dcheckIntegrity(); + if (crtPos_ == crtEnd_) { + tryAdvanceBuffer(); + } + } + + BufType* crtBuf_; + BufType* buffer_; + const uint8_t* crtBegin_{nullptr}; + const uint8_t* crtEnd_{nullptr}; + const uint8_t* crtPos_{nullptr}; + size_t absolutePos_{0}; + // For bounded Cursor, remainingLen_ is the remaining number of data bytes + // in subsequent IOBufs in the chain. For unbounded Cursor, remainingLen_ + // is set to the max of size_t + size_t remainingLen_{std::numeric_limits<size_t>::max()}; + + private: + Derived& derived() { + return static_cast<Derived&>(*this); + } + + Derived const& derived() const { + return static_cast<const Derived&>(*this); + } + + template <class T> + FOLLY_NOINLINE T readSlow() { + T val; + pullSlow(&val, sizeof(T)); + return val; + } + + void readFixedStringSlow(std::string* str, size_t len) { + for (size_t available; (available = length()) < len;) { + str->append(reinterpret_cast<const char*>(data()), available); + if (UNLIKELY(!tryAdvanceBuffer())) { + throw_exception<std::out_of_range>("string underflow"); + } + len -= available; + } + str->append(reinterpret_cast<const char*>(data()), len); + crtPos_ += len; + advanceBufferIfEmpty(); + } + + size_t pullAtMostSlow(void* buf, size_t len) { + // If the length of this buffer is 0 try advancing it. + // Otherwise on the first iteration of the following loop memcpy is called + // with a null source pointer. + if (UNLIKELY(length() == 0 && !tryAdvanceBuffer())) { + return 0; + } + uint8_t* p = reinterpret_cast<uint8_t*>(buf); + size_t copied = 0; + for (size_t available; (available = length()) < len;) { + memcpy(p, data(), available); + copied += available; + if (UNLIKELY(!tryAdvanceBuffer())) { + return copied; + } + p += available; + len -= available; + } + memcpy(p, data(), len); + crtPos_ += len; + advanceBufferIfEmpty(); + return copied + len; + } + + void pullSlow(void* buf, size_t len) { + if (UNLIKELY(pullAtMostSlow(buf, len) != len)) { + throw_exception<std::out_of_range>("underflow"); + } + } + + size_t skipAtMostSlow(size_t len) { + size_t skipped = 0; + for (size_t available; (available = length()) < len;) { + skipped += available; + if (UNLIKELY(!tryAdvanceBuffer())) { + return skipped; + } + len -= available; + } + crtPos_ += len; + advanceBufferIfEmpty(); + return skipped + len; + } + + void skipSlow(size_t len) { + if (UNLIKELY(skipAtMostSlow(len) != len)) { + throw_exception<std::out_of_range>("underflow"); + } + } + + size_t retreatAtMostSlow(size_t len) { + size_t retreated = 0; + for (size_t available; (available = crtPos_ - crtBegin_) < len;) { + retreated += available; + if (UNLIKELY(!tryRetreatBuffer())) { + return retreated; + } + len -= available; + } + crtPos_ -= len; + return retreated + len; + } + + void retreatSlow(size_t len) { + if (UNLIKELY(retreatAtMostSlow(len) != len)) { + throw_exception<std::out_of_range>("underflow"); + } + } + + void advanceDone() {} +}; + +} // namespace detail + +class Cursor : public detail::CursorBase<Cursor, const IOBuf> { + public: + explicit Cursor(const IOBuf* buf) + : detail::CursorBase<Cursor, const IOBuf>(buf) {} + + explicit Cursor(const IOBuf* buf, size_t len) + : detail::CursorBase<Cursor, const IOBuf>(buf, len) {} + + template <class OtherDerived, class OtherBuf> + explicit Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor) + : detail::CursorBase<Cursor, const IOBuf>(cursor) {} + + template <class OtherDerived, class OtherBuf> + Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor, size_t len) + : detail::CursorBase<Cursor, const IOBuf>(cursor, len) {} +}; + +namespace detail { + +template <class Derived> +class Writable { + public: + template <class T> + typename std::enable_if<std::is_arithmetic<T>::value>::type write(T value) { + const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value); + Derived* d = static_cast<Derived*>(this); + d->push(u8, sizeof(T)); + } + + template <class T> + void writeBE(T value) { + Derived* d = static_cast<Derived*>(this); + d->write(Endian::big(value)); + } + + template <class T> + void writeLE(T value) { + Derived* d = static_cast<Derived*>(this); + d->write(Endian::little(value)); + } + + void push(const uint8_t* buf, size_t len) { + Derived* d = static_cast<Derived*>(this); + if (d->pushAtMost(buf, len) != len) { + throw_exception<std::out_of_range>("overflow"); + } + } + + void push(ByteRange buf) { + if (this->pushAtMost(buf) != buf.size()) { + throw_exception<std::out_of_range>("overflow"); + } + } + + size_t pushAtMost(ByteRange buf) { + Derived* d = static_cast<Derived*>(this); + return d->pushAtMost(buf.data(), buf.size()); + } + + /** + * push len bytes of data from input cursor, data could be in an IOBuf chain. + * If input cursor contains less than len bytes, or this cursor has less than + * len bytes writable space, an out_of_range exception will be thrown. + */ + void push(Cursor cursor, size_t len) { + if (this->pushAtMost(cursor, len) != len) { + throw_exception<std::out_of_range>("overflow"); + } + } + + size_t pushAtMost(Cursor cursor, size_t len) { + size_t written = 0; + for (;;) { + auto currentBuffer = cursor.peekBytes(); + const uint8_t* crtData = currentBuffer.data(); + size_t available = currentBuffer.size(); + if (available == 0) { + // end of buffer chain + return written; + } + // all data is in current buffer + if (available >= len) { + this->push(crtData, len); + cursor.skip(len); + return written + len; + } + + // write the whole current IOBuf + this->push(crtData, available); + cursor.skip(available); + written += available; + len -= available; + } + } +}; + +} // namespace detail + +enum class CursorAccess { PRIVATE, UNSHARE }; + +template <CursorAccess access> +class RWCursor : public detail::CursorBase<RWCursor<access>, IOBuf>, + public detail::Writable<RWCursor<access>> { + friend class detail::CursorBase<RWCursor<access>, IOBuf>; + + public: + explicit RWCursor(IOBuf* buf) + : detail::CursorBase<RWCursor<access>, IOBuf>(buf), maybeShared_(true) {} + + template <class OtherDerived, class OtherBuf> + explicit RWCursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor) + : detail::CursorBase<RWCursor<access>, IOBuf>(cursor), + maybeShared_(true) { + CHECK(!cursor.isBounded()) + << "Creating RWCursor from bounded Cursor is not allowed"; + } + /** + * Gather at least n bytes contiguously into the current buffer, + * by coalescing subsequent buffers from the chain as necessary. + */ + void gather(size_t n) { + // Forbid attempts to gather beyond the end of this IOBuf chain. + // Otherwise we could try to coalesce the head of the chain and end up + // accidentally freeing it, invalidating the pointer owned by external + // code. + // + // If crtBuf_ == head() then IOBuf::gather() will perform all necessary + // checking. We only have to perform an explicit check here when calling + // gather() on a non-head element. + if (this->crtBuf_ != this->head() && this->totalLength() < n) { + throw std::overflow_error("cannot gather() past the end of the chain"); + } + size_t offset = this->crtPos_ - this->crtBegin_; + this->crtBuf_->gather(offset + n); + this->crtBegin_ = this->crtBuf_->data(); + this->crtEnd_ = this->crtBuf_->tail(); + this->crtPos_ = this->crtBegin_ + offset; + } + void gatherAtMost(size_t n) { + this->dcheckIntegrity(); + size_t size = std::min(n, this->totalLength()); + size_t offset = this->crtPos_ - this->crtBegin_; + this->crtBuf_->gather(offset + size); + this->crtBegin_ = this->crtBuf_->data(); + this->crtEnd_ = this->crtBuf_->tail(); + this->crtPos_ = this->crtBegin_ + offset; + } + + using detail::Writable<RWCursor<access>>::pushAtMost; + size_t pushAtMost(const uint8_t* buf, size_t len) { + // We have to explicitly check for an input length of 0. + // We support buf being nullptr in this case, but we need to avoid calling + // memcpy() with a null source pointer, since that is undefined behavior + // even if the length is 0. + if (len == 0) { + return 0; + } + + size_t copied = 0; + for (;;) { + // Fast path: the current buffer is big enough. + size_t available = this->length(); + if (LIKELY(available >= len)) { + if (access == CursorAccess::UNSHARE) { + maybeUnshare(); + } + memcpy(writableData(), buf, len); + this->crtPos_ += len; + return copied + len; + } + + if (access == CursorAccess::UNSHARE) { + maybeUnshare(); + } + memcpy(writableData(), buf, available); + copied += available; + if (UNLIKELY(!this->tryAdvanceBuffer())) { + return copied; + } + buf += available; + len -= available; + } + } + + void insert(std::unique_ptr<folly::IOBuf> buf) { + this->dcheckIntegrity(); + this->absolutePos_ += buf->computeChainDataLength(); + if (this->crtPos_ == this->crtBegin_ && this->crtBuf_ != this->buffer_) { + // Can just prepend + this->crtBuf_->prependChain(std::move(buf)); + } else { + IOBuf* nextBuf; + std::unique_ptr<folly::IOBuf> remaining; + if (this->crtPos_ != this->crtEnd_) { + // Need to split current IOBuf in two. + remaining = this->crtBuf_->cloneOne(); + remaining->trimStart(this->crtPos_ - this->crtBegin_); + nextBuf = remaining.get(); + buf->prependChain(std::move(remaining)); + } else { + // Can just append + nextBuf = this->crtBuf_->next(); + } + this->crtBuf_->trimEnd(this->length()); + this->absolutePos_ += this->crtPos_ - this->crtBegin_; + this->crtBuf_->appendChain(std::move(buf)); + + if (nextBuf == this->buffer_) { + // We've just appended to the end of the buffer, so advance to the end. + this->crtBuf_ = this->buffer_->prev(); + this->crtBegin_ = this->crtBuf_->data(); + this->crtPos_ = this->crtEnd_ = this->crtBuf_->tail(); + // This has already been accounted for, so remove it. + this->absolutePos_ -= this->crtEnd_ - this->crtBegin_; + } else { + // Jump past the new links + this->crtBuf_ = nextBuf; + this->crtPos_ = this->crtBegin_ = this->crtBuf_->data(); + this->crtEnd_ = this->crtBuf_->tail(); + } + } + } + + uint8_t* writableData() { + this->dcheckIntegrity(); + return this->crtBuf_->writableData() + (this->crtPos_ - this->crtBegin_); + } + + private: + void maybeUnshare() { + if (UNLIKELY(maybeShared_)) { + size_t offset = this->crtPos_ - this->crtBegin_; + this->crtBuf_->unshareOne(); + this->crtBegin_ = this->crtBuf_->data(); + this->crtEnd_ = this->crtBuf_->tail(); + this->crtPos_ = this->crtBegin_ + offset; + maybeShared_ = false; + } + } + + void advanceDone() { + maybeShared_ = true; + } + + bool maybeShared_; +}; + +typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor; +typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor; + +/** + * Append to the end of a buffer chain, growing the chain (by allocating new + * buffers) in increments of at least growth bytes every time. Won't grow + * (and push() and ensure() will throw) if growth == 0. + * + * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead + * of chaining. + */ +class Appender : public detail::Writable<Appender> { + public: + Appender(IOBuf* buf, std::size_t growth) + : buffer_(buf), crtBuf_(buf->prev()), growth_(growth) {} + + uint8_t* writableData() { + return crtBuf_->writableTail(); + } + + size_t length() const { + return crtBuf_->tailroom(); + } + + /** + * Mark n bytes (must be <= length()) as appended, as per the + * IOBuf::append() method. + */ + void append(size_t n) { + crtBuf_->append(n); + } + + /** + * Ensure at least n contiguous bytes available to write. + * Postcondition: length() >= n. + */ + void ensure(std::size_t n) { + if (LIKELY(length() >= n)) { + return; + } + + // Waste the rest of the current buffer and allocate a new one. + // Don't make it too small, either. + if (growth_ == 0) { + throw_exception<std::out_of_range>("can't grow buffer chain"); + } + + n = std::max(n, growth_); + buffer_->prependChain(IOBuf::create(n)); + crtBuf_ = buffer_->prev(); + } + + using detail::Writable<Appender>::pushAtMost; + size_t pushAtMost(const uint8_t* buf, size_t len) { + // We have to explicitly check for an input length of 0. + // We support buf being nullptr in this case, but we need to avoid calling + // memcpy() with a null source pointer, since that is undefined behavior + // even if the length is 0. + if (len == 0) { + return 0; + } + + // If the length of this buffer is 0 try growing it. + // Otherwise on the first iteration of the following loop memcpy is called + // with a null source pointer. + if (UNLIKELY(length() == 0 && !tryGrowChain())) { + return 0; + } + + size_t copied = 0; + for (;;) { + // Fast path: it all fits in one buffer. + size_t available = length(); + if (LIKELY(available >= len)) { + memcpy(writableData(), buf, len); + append(len); + return copied + len; + } + + memcpy(writableData(), buf, available); + append(available); + copied += available; + if (UNLIKELY(!tryGrowChain())) { + return copied; + } + buf += available; + len -= available; + } + } + + /* + * Append to the end of this buffer, using a printf() style + * format specifier. + * + * Note that folly/Format.h provides nicer and more type-safe mechanisms + * for formatting strings, which should generally be preferred over + * printf-style formatting. Appender objects can be used directly as an + * output argument for Formatter objects. For example: + * + * Appender app(&iobuf); + * format("{} {}", "hello", "world")(app); + * + * However, printf-style strings are still needed when dealing with existing + * third-party code in some cases. + * + * This will always add a nul-terminating character after the end + * of the output. However, the buffer data length will only be updated to + * include the data itself. The nul terminator will be the first byte in the + * buffer tailroom. + * + * This method may throw exceptions on error. + */ + void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...) + FOLLY_PRINTF_FORMAT_ATTR(2, 3); + + void vprintf(const char* fmt, va_list ap); + + /* + * Calling an Appender object with a StringPiece will append the string + * piece. This allows Appender objects to be used directly with + * Formatter. + */ + void operator()(StringPiece sp) { + push(ByteRange(sp)); + } + + private: + bool tryGrowChain() { + assert(crtBuf_->next() == buffer_); + if (growth_ == 0) { + return false; + } + + buffer_->prependChain(IOBuf::create(growth_)); + crtBuf_ = buffer_->prev(); + return true; + } + + IOBuf* buffer_; + IOBuf* crtBuf_; + std::size_t growth_; +}; + +class QueueAppender : public detail::Writable<QueueAppender> { + public: + /** + * Create an Appender that writes to a IOBufQueue. When we allocate + * space in the queue, we grow no more than growth bytes at once + * (unless you call ensure() with a bigger value yourself). + */ + QueueAppender(IOBufQueue* queue, std::size_t growth) + : queueCache_(queue), growth_(growth) {} + + void reset(IOBufQueue* queue, std::size_t growth) { + queueCache_.reset(queue); + growth_ = growth; + } + + uint8_t* writableData() { + return queueCache_.writableData(); + } + + size_t length() { + return queueCache_.length(); + } + + void append(size_t n) { + queueCache_.append(n); + } + + // Ensure at least n contiguous; can go above growth_, throws if + // not enough room. + void ensure(size_t n) { + if (length() < n) { + ensureSlow(n); + } + } + + template <class T> + typename std::enable_if<std::is_arithmetic<T>::value>::type write(T value) { + // We can't fail. + if (length() >= sizeof(T)) { + storeUnaligned(queueCache_.writableData(), value); + queueCache_.appendUnsafe(sizeof(T)); + } else { + writeSlow<T>(value); + } + } + + using detail::Writable<QueueAppender>::pushAtMost; + size_t pushAtMost(const uint8_t* buf, size_t len) { + // Fill the current buffer + const size_t copyLength = std::min(len, length()); + if (copyLength != 0) { + memcpy(writableData(), buf, copyLength); + queueCache_.appendUnsafe(copyLength); + buf += copyLength; + } + size_t remaining = len - copyLength; + // Allocate more buffers as necessary + while (remaining != 0) { + auto p = queueCache_.queue()->preallocate( + std::min(remaining, growth_), growth_, remaining); + memcpy(p.first, buf, p.second); + queueCache_.queue()->postallocate(p.second); + buf += p.second; + remaining -= p.second; + } + return len; + } + + void insert(std::unique_ptr<folly::IOBuf> buf) { + if (buf) { + queueCache_.queue()->append(std::move(buf), true); + } + } + + void insert(const folly::IOBuf& buf) { + queueCache_.queue()->append(buf, true); + } + + private: + folly::IOBufQueue::WritableRangeCache queueCache_{nullptr}; + size_t growth_{0}; + + FOLLY_NOINLINE void ensureSlow(size_t n) { + queueCache_.queue()->preallocate(n, growth_); + queueCache_.fillCache(); + } + + template <class T> + typename std::enable_if<std::is_arithmetic<T>::value>::type FOLLY_NOINLINE + writeSlow(T value) { + queueCache_.queue()->preallocate(sizeof(T), growth_); + queueCache_.fillCache(); + + storeUnaligned(queueCache_.writableData(), value); + queueCache_.appendUnsafe(sizeof(T)); + } +}; + +} // namespace io +} // namespace folly + +#include <folly/io/Cursor-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.cpp b/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.cpp new file mode 100644 index 000000000..fc6414df2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/GlobalShutdownSocketSet.h> + +#include <folly/Singleton.h> + +namespace folly { + +namespace { +struct PrivateTag {}; +} // namespace + +static Singleton<ShutdownSocketSet, PrivateTag> singleton; + +std::shared_ptr<ShutdownSocketSet> tryGetShutdownSocketSet() { + return singleton.try_get(); +} +ReadMostlySharedPtr<ShutdownSocketSet> tryGetShutdownSocketSetFast() { + return singleton.try_get_fast(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h b/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h new file mode 100644 index 000000000..80318cb87 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/experimental/ReadMostlySharedPtr.h> +#include <folly/io/ShutdownSocketSet.h> + +namespace folly { + +std::shared_ptr<ShutdownSocketSet> tryGetShutdownSocketSet(); +ReadMostlySharedPtr<ShutdownSocketSet> tryGetShutdownSocketSetFast(); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/IOBuf.cpp b/ios/Pods/Flipper-Folly/folly/io/IOBuf.cpp new file mode 100644 index 000000000..6e19b4d48 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/IOBuf.cpp @@ -0,0 +1,1271 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#include <folly/io/IOBuf.h> + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <stdexcept> + +#include <folly/Conv.h> +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/ScopeGuard.h> +#include <folly/hash/SpookyHashV2.h> +#include <folly/io/Cursor.h> +#include <folly/lang/Align.h> +#include <folly/lang/Exception.h> +#include <folly/memory/Malloc.h> + +using std::unique_ptr; + +namespace { + +enum : uint16_t { + kHeapMagic = 0xa5a5, + // This memory segment contains an IOBuf that is still in use + kIOBufInUse = 0x01, + // This memory segment contains buffer data that is still in use + kDataInUse = 0x02, + // This memory segment contains a SharedInfo that is still in use + kSharedInfoInUse = 0x04, +}; + +enum : std::size_t { + // When create() is called for buffers less than kDefaultCombinedBufSize, + // we allocate a single combined memory segment for the IOBuf and the data + // together. See the comments for createCombined()/createSeparate() for more + // details. + // + // (The size of 1k is largely just a guess here. We could could probably do + // benchmarks of real applications to see if adjusting this number makes a + // difference. Callers that know their exact use case can also explicitly + // call createCombined() or createSeparate().) + kDefaultCombinedBufSize = 1024 +}; + +// Helper function for IOBuf::takeOwnership() +// The user's free function is not allowed to throw. +// (We are already in the middle of throwing an exception, so +// we cannot let this exception go unhandled.) +void takeOwnershipError( + bool freeOnError, + void* buf, + folly::IOBuf::FreeFunction freeFn, + void* userData) noexcept { + if (!freeOnError) { + return; + } + if (!freeFn) { + free(buf); + return; + } + freeFn(buf, userData); +} + +} // namespace + +namespace folly { + +// use free for size >= 4GB +// since we can store only 32 bits in the size var +struct IOBuf::HeapPrefix { + HeapPrefix(uint16_t flg, size_t sz) + : magic(kHeapMagic), + flags(flg), + size((sz == ((size_t)(uint32_t)sz)) ? static_cast<uint32_t>(sz) : 0) {} + ~HeapPrefix() { + // Reset magic to 0 on destruction. This is solely for debugging purposes + // to help catch bugs where someone tries to use HeapStorage after it has + // been deleted. + magic = 0; + } + + uint16_t magic; + std::atomic<uint16_t> flags; + uint32_t size; +}; + +struct IOBuf::HeapStorage { + HeapPrefix prefix; + // The IOBuf is last in the HeapStorage object. + // This way operator new will work even if allocating a subclass of IOBuf + // that requires more space. + folly::IOBuf buf; +}; + +struct IOBuf::HeapFullStorage { + // Make sure jemalloc allocates from the 64-byte class. Putting this here + // because HeapStorage is private so it can't be at namespace level. + static_assert(sizeof(HeapStorage) <= 64, "IOBuf may not grow over 56 bytes!"); + + HeapStorage hs; + SharedInfo shared; + folly::max_align_t align; +}; + +IOBuf::SharedInfo::SharedInfo() + : freeFn(nullptr), userData(nullptr), useHeapFullStorage(false) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +IOBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg, bool hfs) + : freeFn(fn), userData(arg), useHeapFullStorage(hfs) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +void IOBuf::SharedInfo::invokeAndDeleteEachObserver( + SharedInfoObserverEntryBase* observerListHead, + ObserverCb cb) noexcept { + if (observerListHead && cb) { + // break the chain + observerListHead->prev->next = nullptr; + auto entry = observerListHead; + while (entry) { + auto tmp = entry->next; + cb(*entry); + delete entry; + entry = tmp; + } + } +} + +void IOBuf::SharedInfo::releaseStorage(SharedInfo* info) noexcept { + if (info->useHeapFullStorage) { + auto storageAddr = + reinterpret_cast<uint8_t*>(info) - offsetof(HeapFullStorage, shared); + auto storage = reinterpret_cast<HeapFullStorage*>(storageAddr); + info->~SharedInfo(); + IOBuf::releaseStorage(&storage->hs, kSharedInfoInUse); + } +} + +void* IOBuf::operator new(size_t size) { + size_t fullSize = offsetof(HeapStorage, buf) + size; + auto storage = static_cast<HeapStorage*>(checkedMalloc(fullSize)); + + new (&storage->prefix) HeapPrefix(kIOBufInUse, fullSize); + return &(storage->buf); +} + +void* IOBuf::operator new(size_t /* size */, void* ptr) { + return ptr; +} + +void IOBuf::operator delete(void* ptr) { + auto storageAddr = static_cast<uint8_t*>(ptr) - offsetof(HeapStorage, buf); + auto storage = reinterpret_cast<HeapStorage*>(storageAddr); + releaseStorage(storage, kIOBufInUse); +} + +void IOBuf::operator delete(void* /* ptr */, void* /* placement */) { + // Provide matching operator for `IOBuf::new` to avoid MSVC compilation + // warning (C4291) about memory leak when exception is thrown in the + // constructor. +} + +void IOBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) noexcept { + CHECK_EQ(storage->prefix.magic, static_cast<uint16_t>(kHeapMagic)); + + // Use relaxed memory order here. If we are unlucky and happen to get + // out-of-date data the compare_exchange_weak() call below will catch + // it and load new data with memory_order_acq_rel. + auto flags = storage->prefix.flags.load(std::memory_order_acquire); + DCHECK_EQ((flags & freeFlags), freeFlags); + + while (true) { + auto newFlags = uint16_t(flags & ~freeFlags); + if (newFlags == 0) { + // save the size + size_t size = storage->prefix.size; + // The storage space is now unused. Free it. + storage->prefix.HeapPrefix::~HeapPrefix(); + if (FOLLY_LIKELY(size)) { + sizedFree(storage, size); + } else { + free(storage); + } + return; + } + + // This storage segment still contains portions that are in use. + // Just clear the flags specified in freeFlags for now. + auto ret = storage->prefix.flags.compare_exchange_weak( + flags, newFlags, std::memory_order_acq_rel); + if (ret) { + // We successfully updated the flags. + return; + } + + // We failed to update the flags. Some other thread probably updated them + // and cleared some of the other bits. Continue around the loop to see if + // we are the last user now, or if we need to try updating the flags again. + } +} + +void IOBuf::freeInternalBuf(void* /* buf */, void* userData) noexcept { + auto storage = static_cast<HeapStorage*>(userData); + releaseStorage(storage, kDataInUse); +} + +IOBuf::IOBuf(CreateOp, std::size_t capacity) + : next_(this), + prev_(this), + data_(nullptr), + length_(0), + flagsAndSharedInfo_(0) { + SharedInfo* info; + allocExtBuffer(capacity, &buf_, &info, &capacity_); + setSharedInfo(info); + data_ = buf_; +} + +IOBuf::IOBuf( + CopyBufferOp /* op */, + const void* buf, + std::size_t size, + std::size_t headroom, + std::size_t minTailroom) + : IOBuf(CREATE, headroom + size + minTailroom) { + advance(headroom); + if (size > 0) { + assert(buf != nullptr); + memcpy(writableData(), buf, size); + append(size); + } +} + +IOBuf::IOBuf( + CopyBufferOp op, + ByteRange br, + std::size_t headroom, + std::size_t minTailroom) + : IOBuf(op, br.data(), br.size(), headroom, minTailroom) {} + +unique_ptr<IOBuf> IOBuf::create(std::size_t capacity) { + // For smaller-sized buffers, allocate the IOBuf, SharedInfo, and the buffer + // all with a single allocation. + // + // We don't do this for larger buffers since it can be wasteful if the user + // needs to reallocate the buffer but keeps using the same IOBuf object. + // In this case we can't free the data space until the IOBuf is also + // destroyed. Callers can explicitly call createCombined() or + // createSeparate() if they know their use case better, and know if they are + // likely to reallocate the buffer later. + if (capacity <= kDefaultCombinedBufSize) { + return createCombined(capacity); + } + return createSeparate(capacity); +} + +unique_ptr<IOBuf> IOBuf::createCombined(std::size_t capacity) { + // To save a memory allocation, allocate space for the IOBuf object, the + // SharedInfo struct, and the data itself all with a single call to malloc(). + size_t requiredStorage = offsetof(HeapFullStorage, align) + capacity; + size_t mallocSize = goodMallocSize(requiredStorage); + auto storage = static_cast<HeapFullStorage*>(checkedMalloc(mallocSize)); + + new (&storage->hs.prefix) HeapPrefix(kIOBufInUse | kDataInUse, mallocSize); + new (&storage->shared) SharedInfo(freeInternalBuf, storage); + + auto bufAddr = reinterpret_cast<uint8_t*>(&storage->align); + uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize; + auto actualCapacity = size_t(storageEnd - bufAddr); + unique_ptr<IOBuf> ret(new (&storage->hs.buf) IOBuf( + InternalConstructor(), + packFlagsAndSharedInfo(0, &storage->shared), + bufAddr, + actualCapacity, + bufAddr, + 0)); + return ret; +} + +unique_ptr<IOBuf> IOBuf::createSeparate(std::size_t capacity) { + return std::make_unique<IOBuf>(CREATE, capacity); +} + +unique_ptr<IOBuf> IOBuf::createChain( + size_t totalCapacity, + std::size_t maxBufCapacity) { + unique_ptr<IOBuf> out = + create(std::min(totalCapacity, size_t(maxBufCapacity))); + size_t allocatedCapacity = out->capacity(); + + while (allocatedCapacity < totalCapacity) { + unique_ptr<IOBuf> newBuf = create( + std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity))); + allocatedCapacity += newBuf->capacity(); + out->prependChain(std::move(newBuf)); + } + + return out; +} + +size_t IOBuf::goodSize(size_t minCapacity, CombinedOption combined) { + if (combined == CombinedOption::DEFAULT) { + combined = minCapacity <= kDefaultCombinedBufSize + ? CombinedOption::COMBINED + : CombinedOption::SEPARATE; + } + size_t overhead; + if (combined == CombinedOption::COMBINED) { + overhead = offsetof(HeapFullStorage, align); + } else { + // Pad minCapacity to a multiple of 8 + minCapacity = (minCapacity + 7) & ~7; + overhead = sizeof(SharedInfo); + } + size_t goodSize = folly::goodMallocSize(minCapacity + overhead); + return goodSize - overhead; +} + +IOBuf::IOBuf( + TakeOwnershipOp, + void* buf, + std::size_t capacity, + std::size_t length, + FreeFunction freeFn, + void* userData, + bool freeOnError) + : next_(this), + prev_(this), + data_(static_cast<uint8_t*>(buf)), + buf_(static_cast<uint8_t*>(buf)), + length_(length), + capacity_(capacity), + flagsAndSharedInfo_( + packFlagsAndSharedInfo(kFlagFreeSharedInfo, nullptr)) { + // do not allow only user data without a freeFn + // since we use that for folly::sizedFree + DCHECK(!userData || (userData && freeFn)); + + auto rollback = makeGuard([&] { // + takeOwnershipError(freeOnError, buf, freeFn, userData); + }); + setSharedInfo(new SharedInfo(freeFn, userData)); + rollback.dismiss(); +} + +unique_ptr<IOBuf> IOBuf::takeOwnership( + void* buf, + std::size_t capacity, + std::size_t length, + FreeFunction freeFn, + void* userData, + bool freeOnError) { + // do not allow only user data without a freeFn + // since we use that for folly::sizedFree + DCHECK(!userData || (userData && freeFn)); + + HeapFullStorage* storage = nullptr; + auto rollback = makeGuard([&] { + if (storage) { + free(storage); + } + takeOwnershipError(freeOnError, buf, freeFn, userData); + }); + + size_t requiredStorage = sizeof(HeapFullStorage); + size_t mallocSize = goodMallocSize(requiredStorage); + storage = static_cast<HeapFullStorage*>(checkedMalloc(mallocSize)); + + new (&storage->hs.prefix) + HeapPrefix(kIOBufInUse | kSharedInfoInUse, mallocSize); + new (&storage->shared) + SharedInfo(freeFn, userData, true /*useHeapFullStorage*/); + + auto result = unique_ptr<IOBuf>(new (&storage->hs.buf) IOBuf( + InternalConstructor(), + packFlagsAndSharedInfo(0, &storage->shared), + static_cast<uint8_t*>(buf), + capacity, + static_cast<uint8_t*>(buf), + length)); + + rollback.dismiss(); + + return result; +} + +IOBuf::IOBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept + : IOBuf( + InternalConstructor(), + 0, + // We cast away the const-ness of the buffer here. + // This is okay since IOBuf users must use unshare() to create a copy + // of this buffer before writing to the buffer. + static_cast<uint8_t*>(const_cast<void*>(buf)), + capacity, + static_cast<uint8_t*>(const_cast<void*>(buf)), + capacity) {} + +IOBuf::IOBuf(WrapBufferOp op, ByteRange br) noexcept + : IOBuf(op, br.data(), br.size()) {} + +unique_ptr<IOBuf> IOBuf::wrapBuffer(const void* buf, std::size_t capacity) { + return std::make_unique<IOBuf>(WRAP_BUFFER, buf, capacity); +} + +IOBuf IOBuf::wrapBufferAsValue(const void* buf, std::size_t capacity) noexcept { + return IOBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity); +} + +IOBuf::IOBuf() noexcept = default; + +IOBuf::IOBuf(IOBuf&& other) noexcept + : data_(other.data_), + buf_(other.buf_), + length_(other.length_), + capacity_(other.capacity_), + flagsAndSharedInfo_(other.flagsAndSharedInfo_) { + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flagsAndSharedInfo_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } + + // Sanity check to make sure that other is in a valid state to be destroyed. + DCHECK_EQ(other.prev_, &other); + DCHECK_EQ(other.next_, &other); +} + +IOBuf::IOBuf(const IOBuf& other) { + *this = other.cloneAsValue(); +} + +IOBuf::IOBuf( + InternalConstructor, + uintptr_t flagsAndSharedInfo, + uint8_t* buf, + std::size_t capacity, + uint8_t* data, + std::size_t length) noexcept + : next_(this), + prev_(this), + data_(data), + buf_(buf), + length_(length), + capacity_(capacity), + flagsAndSharedInfo_(flagsAndSharedInfo) { + assert(data >= buf); + assert(data + length <= buf + capacity); +} + +IOBuf::~IOBuf() { + // Destroying an IOBuf destroys the entire chain. + // Users of IOBuf should only explicitly delete the head of any chain. + // The other elements in the chain will be automatically destroyed. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + decrementRefcount(); +} + +IOBuf& IOBuf::operator=(IOBuf&& other) noexcept { + if (this == &other) { + return *this; + } + + // If we are part of a chain, delete the rest of the chain. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + // Decrement our refcount on the current buffer + decrementRefcount(); + + // Take ownership of the other buffer's data + data_ = other.data_; + buf_ = other.buf_; + length_ = other.length_; + capacity_ = other.capacity_; + flagsAndSharedInfo_ = other.flagsAndSharedInfo_; + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flagsAndSharedInfo_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } + + // Sanity check to make sure that other is in a valid state to be destroyed. + DCHECK_EQ(other.prev_, &other); + DCHECK_EQ(other.next_, &other); + + return *this; +} + +IOBuf& IOBuf::operator=(const IOBuf& other) { + if (this != &other) { + *this = IOBuf(other); + } + return *this; +} + +bool IOBuf::empty() const { + const IOBuf* current = this; + do { + if (current->length() != 0) { + return false; + } + current = current->next_; + } while (current != this); + return true; +} + +size_t IOBuf::countChainElements() const { + size_t numElements = 1; + for (IOBuf* current = next_; current != this; current = current->next_) { + ++numElements; + } + return numElements; +} + +std::size_t IOBuf::computeChainDataLength() const { + std::size_t fullLength = length_; + for (IOBuf* current = next_; current != this; current = current->next_) { + fullLength += current->length_; + } + return fullLength; +} + +void IOBuf::prependChain(unique_ptr<IOBuf>&& iobuf) { + // Take ownership of the specified IOBuf + IOBuf* other = iobuf.release(); + + // Remember the pointer to the tail of the other chain + IOBuf* otherTail = other->prev_; + + // Hook up prev_->next_ to point at the start of the other chain, + // and other->prev_ to point at prev_ + prev_->next_ = other; + other->prev_ = prev_; + + // Hook up otherTail->next_ to point at us, + // and prev_ to point back at otherTail, + otherTail->next_ = this; + prev_ = otherTail; +} + +unique_ptr<IOBuf> IOBuf::clone() const { + return std::make_unique<IOBuf>(cloneAsValue()); +} + +unique_ptr<IOBuf> IOBuf::cloneOne() const { + return std::make_unique<IOBuf>(cloneOneAsValue()); +} + +unique_ptr<IOBuf> IOBuf::cloneCoalesced() const { + return std::make_unique<IOBuf>(cloneCoalescedAsValue()); +} + +unique_ptr<IOBuf> IOBuf::cloneCoalescedWithHeadroomTailroom( + std::size_t newHeadroom, + std::size_t newTailroom) const { + return std::make_unique<IOBuf>( + cloneCoalescedAsValueWithHeadroomTailroom(newHeadroom, newTailroom)); +} + +IOBuf IOBuf::cloneAsValue() const { + auto tmp = cloneOneAsValue(); + + for (IOBuf* current = next_; current != this; current = current->next_) { + tmp.prependChain(current->cloneOne()); + } + + return tmp; +} + +IOBuf IOBuf::cloneOneAsValue() const { + if (SharedInfo* info = sharedInfo()) { + info->refcount.fetch_add(1, std::memory_order_acq_rel); + } + return IOBuf( + InternalConstructor(), + flagsAndSharedInfo_, + buf_, + capacity_, + data_, + length_); +} + +IOBuf IOBuf::cloneCoalescedAsValue() const { + const std::size_t newHeadroom = headroom(); + const std::size_t newTailroom = prev()->tailroom(); + return cloneCoalescedAsValueWithHeadroomTailroom(newHeadroom, newTailroom); +} + +IOBuf IOBuf::cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t newHeadroom, + std::size_t newTailroom) const { + if (!isChained() && newHeadroom <= headroom() && newTailroom <= tailroom()) { + return cloneOneAsValue(); + } + // Coalesce into newBuf + const std::size_t newLength = computeChainDataLength(); + const std::size_t newCapacity = newLength + newHeadroom + newTailroom; + IOBuf newBuf{CREATE, newCapacity}; + newBuf.advance(newHeadroom); + + auto current = this; + do { + if (current->length() > 0) { + DCHECK_NOTNULL(current->data()); + DCHECK_LE(current->length(), newBuf.tailroom()); + memcpy(newBuf.writableTail(), current->data(), current->length()); + newBuf.append(current->length()); + } + current = current->next(); + } while (current != this); + + DCHECK_EQ(newLength, newBuf.length()); + DCHECK_EQ(newHeadroom, newBuf.headroom()); + DCHECK_LE(newTailroom, newBuf.tailroom()); + + return newBuf; +} + +void IOBuf::unshareOneSlow() { + // Allocate a new buffer for the data + uint8_t* buf; + SharedInfo* sharedInfo; + std::size_t actualCapacity; + allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity); + + // Copy the data + // Maintain the same amount of headroom. Since we maintained the same + // minimum capacity we also maintain at least the same amount of tailroom. + std::size_t headlen = headroom(); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(buf + headlen, data_, length_); + } + + // Release our reference on the old buffer + decrementRefcount(); + // Make sure flags are all cleared. + setFlagsAndSharedInfo(0, sharedInfo); + + // Update the buffer pointers to point to the new buffer + data_ = buf + headlen; + buf_ = buf; +} + +void IOBuf::unshareChained() { + // unshareChained() should only be called if we are part of a chain of + // multiple IOBufs. The caller should have already verified this. + assert(isChained()); + + IOBuf* current = this; + while (true) { + if (current->isSharedOne()) { + // we have to unshare + break; + } + + current = current->next_; + if (current == this) { + // None of the IOBufs in the chain are shared, + // so return without doing anything + return; + } + } + + // We have to unshare. Let coalesceSlow() do the work. + coalesceSlow(); +} + +void IOBuf::markExternallyShared() { + IOBuf* current = this; + do { + current->markExternallySharedOne(); + current = current->next_; + } while (current != this); +} + +void IOBuf::makeManagedChained() { + assert(isChained()); + + IOBuf* current = this; + while (true) { + current->makeManagedOne(); + current = current->next_; + if (current == this) { + break; + } + } +} + +void IOBuf::coalesceSlow() { + // coalesceSlow() should only be called if we are part of a chain of multiple + // IOBufs. The caller should have already verified this. + DCHECK(isChained()); + + // Compute the length of the entire chain + std::size_t newLength = 0; + IOBuf* end = this; + do { + newLength += end->length_; + end = end->next_; + } while (end != this); + + coalesceAndReallocate(newLength, end); + // We should be only element left in the chain now + DCHECK(!isChained()); +} + +void IOBuf::coalesceSlow(size_t maxLength) { + // coalesceSlow() should only be called if we are part of a chain of multiple + // IOBufs. The caller should have already verified this. + DCHECK(isChained()); + DCHECK_LT(length_, maxLength); + + // Compute the length of the entire chain + std::size_t newLength = 0; + IOBuf* end = this; + while (true) { + newLength += end->length_; + end = end->next_; + if (newLength >= maxLength) { + break; + } + if (end == this) { + throw_exception<std::overflow_error>( + "attempted to coalesce more data than " + "available"); + } + } + + coalesceAndReallocate(newLength, end); + // We should have the requested length now + DCHECK_GE(length_, maxLength); +} + +void IOBuf::coalesceAndReallocate( + size_t newHeadroom, + size_t newLength, + IOBuf* end, + size_t newTailroom) { + std::size_t newCapacity = newLength + newHeadroom + newTailroom; + + // Allocate space for the coalesced buffer. + // We always convert to an external buffer, even if we happened to be an + // internal buffer before. + uint8_t* newBuf; + SharedInfo* newInfo; + std::size_t actualCapacity; + allocExtBuffer(newCapacity, &newBuf, &newInfo, &actualCapacity); + + // Copy the data into the new buffer + uint8_t* newData = newBuf + newHeadroom; + uint8_t* p = newData; + IOBuf* current = this; + size_t remaining = newLength; + do { + if (current->length_ > 0) { + assert(current->length_ <= remaining); + assert(current->data_ != nullptr); + remaining -= current->length_; + memcpy(p, current->data_, current->length_); + p += current->length_; + } + current = current->next_; + } while (current != end); + assert(remaining == 0); + + // Point at the new buffer + decrementRefcount(); + + // Make sure flags are all cleared. + setFlagsAndSharedInfo(0, newInfo); + + capacity_ = actualCapacity; + buf_ = newBuf; + data_ = newData; + length_ = newLength; + + // Separate from the rest of our chain. + // Since we don't store the unique_ptr returned by separateChain(), + // this will immediately delete the returned subchain. + if (isChained()) { + (void)separateChain(next_, current->prev_); + } +} + +void IOBuf::decrementRefcount() noexcept { + // Externally owned buffers don't have a SharedInfo object and aren't managed + // by the reference count + SharedInfo* info = sharedInfo(); + if (!info) { + return; + } + + // Avoid doing atomic decrement if the refcount is 1. + // This is safe, because it means that we're the last reference and destroying + // the object. Anything trying to copy it is already undefined behavior. + if (info->refcount.load(std::memory_order_acquire) > 1) { + // Decrement the refcount + uint32_t newcnt = info->refcount.fetch_sub(1, std::memory_order_acq_rel); + // Note that fetch_sub() returns the value before we decremented. + // If it is 1, we were the only remaining user; if it is greater there are + // still other users. + if (newcnt > 1) { + return; + } + } + + // save the useHeapFullStorage flag here since + // freeExtBuffer can delete the sharedInfo() + bool useHeapFullStorage = info->useHeapFullStorage; + + // We were the last user. Free the buffer + freeExtBuffer(); + + // Free the SharedInfo if it was allocated separately. + // + // This is only used by takeOwnership(). + // + // To avoid this special case handling in decrementRefcount(), we could have + // takeOwnership() set a custom freeFn() that calls the user's free function + // then frees the SharedInfo object. (This would require that + // takeOwnership() store the user's free function with its allocated + // SharedInfo object.) However, handling this specially with a flag seems + // like it shouldn't be problematic. + if (flags() & kFlagFreeSharedInfo) { + delete info; + } else { + if (useHeapFullStorage) { + SharedInfo::releaseStorage(info); + } + } +} + +void IOBuf::reserveSlow(std::size_t minHeadroom, std::size_t minTailroom) { + size_t newCapacity = (size_t)length_ + minHeadroom + minTailroom; + DCHECK_LT(newCapacity, UINT32_MAX); + + // reserveSlow() is dangerous if anyone else is sharing the buffer, as we may + // reallocate and free the original buffer. It should only ever be called if + // we are the only user of the buffer. + DCHECK(!isSharedOne()); + + // We'll need to reallocate the buffer. + // There are a few options. + // - If we have enough total room, move the data around in the buffer + // and adjust the data_ pointer. + // - If we're using an internal buffer, we'll switch to an external + // buffer with enough headroom and tailroom. + // - If we have enough headroom (headroom() >= minHeadroom) but not too much + // (so we don't waste memory), we can try one of two things, depending on + // whether we use jemalloc or not: + // - If using jemalloc, we can try to expand in place, avoiding a memcpy() + // - If not using jemalloc and we don't have too much to copy, + // we'll use realloc() (note that realloc might have to copy + // headroom + data + tailroom, see smartRealloc in folly/memory/Malloc.h) + // - Otherwise, bite the bullet and reallocate. + if (headroom() + tailroom() >= minHeadroom + minTailroom) { + uint8_t* newData = writableBuffer() + minHeadroom; + memmove(newData, data_, length_); + data_ = newData; + return; + } + + size_t newAllocatedCapacity = 0; + uint8_t* newBuffer = nullptr; + std::size_t newHeadroom = 0; + std::size_t oldHeadroom = headroom(); + + // If we have a buffer allocated with malloc and we just need more tailroom, + // try to use realloc()/xallocx() to grow the buffer in place. + SharedInfo* info = sharedInfo(); + bool useHeapFullStorage = info && info->useHeapFullStorage; + if (info && (info->freeFn == nullptr) && length_ != 0 && + oldHeadroom >= minHeadroom) { + size_t headSlack = oldHeadroom - minHeadroom; + newAllocatedCapacity = goodExtBufferSize(newCapacity + headSlack); + if (usingJEMalloc()) { + // We assume that tailroom is more useful and more important than + // headroom (not least because realloc / xallocx allow us to grow the + // buffer at the tail, but not at the head) So, if we have more headroom + // than we need, we consider that "wasted". We arbitrarily define "too + // much" headroom to be 25% of the capacity. + if (headSlack * 4 <= newCapacity) { + size_t allocatedCapacity = capacity() + sizeof(SharedInfo); + void* p = buf_; + if (allocatedCapacity >= jemallocMinInPlaceExpandable) { + if (xallocx(p, newAllocatedCapacity, 0, 0) == newAllocatedCapacity) { + newBuffer = static_cast<uint8_t*>(p); + newHeadroom = oldHeadroom; + // update the userData + info->userData = reinterpret_cast<void*>(newAllocatedCapacity); + } + // if xallocx failed, do nothing, fall back to malloc/memcpy/free + } + } + } else { // Not using jemalloc + size_t copySlack = capacity() - length_; + if (copySlack * 2 <= length_) { + void* p = realloc(buf_, newAllocatedCapacity); + if (UNLIKELY(p == nullptr)) { + throw_exception<std::bad_alloc>(); + } + newBuffer = static_cast<uint8_t*>(p); + newHeadroom = oldHeadroom; + } + } + } + + // None of the previous reallocation strategies worked (or we're using + // an internal buffer). malloc/copy/free. + if (newBuffer == nullptr) { + newAllocatedCapacity = goodExtBufferSize(newCapacity); + newBuffer = static_cast<uint8_t*>(checkedMalloc(newAllocatedCapacity)); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(newBuffer + minHeadroom, data_, length_); + } + if (sharedInfo()) { + freeExtBuffer(); + } + newHeadroom = minHeadroom; + } + + std::size_t cap; + initExtBuffer(newBuffer, newAllocatedCapacity, &info, &cap); + + if (flags() & kFlagFreeSharedInfo) { + delete sharedInfo(); + } else { + if (useHeapFullStorage) { + SharedInfo::releaseStorage(sharedInfo()); + } + } + + setFlagsAndSharedInfo(0, info); + capacity_ = cap; + buf_ = newBuffer; + data_ = newBuffer + newHeadroom; + // length_ is unchanged +} + +// The user's free function should never throw. Otherwise we might throw from +// the IOBuf destructor. Other code paths like coalesce() also assume that +// decrementRefcount() cannot throw. +void IOBuf::freeExtBuffer() noexcept { + SharedInfo* info = sharedInfo(); + DCHECK(info); + + // save the observerListHead + // since the SharedInfo can be freed + auto observerListHead = info->observerListHead; + info->observerListHead = nullptr; + + if (info->freeFn) { + info->freeFn(buf_, info->userData); + } else { + // this will invoke free if info->userData is 0 + size_t size = reinterpret_cast<size_t>(info->userData); + if (size) { + folly::sizedFree(buf_, size); + } else { + free(buf_); + } + } + SharedInfo::invokeAndDeleteEachObserver( + observerListHead, [](auto& entry) { entry.afterFreeExtBuffer(); }); +} + +void IOBuf::allocExtBuffer( + std::size_t minCapacity, + uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + size_t mallocSize = goodExtBufferSize(minCapacity); + auto buf = static_cast<uint8_t*>(checkedMalloc(mallocSize)); + initExtBuffer(buf, mallocSize, infoReturn, capacityReturn); + + // the userData and the freeFn are nullptr here + // just store the mallocSize in userData + (*infoReturn)->userData = reinterpret_cast<void*>(mallocSize); + + *bufReturn = buf; +} + +size_t IOBuf::goodExtBufferSize(std::size_t minCapacity) { + // Determine how much space we should allocate. We'll store the SharedInfo + // for the external buffer just after the buffer itself. (We store it just + // after the buffer rather than just before so that the code can still just + // use free(buf_) to free the buffer.) + size_t minSize = static_cast<size_t>(minCapacity) + sizeof(SharedInfo); + // Add room for padding so that the SharedInfo will be aligned on an 8-byte + // boundary. + minSize = (minSize + 7) & ~7; + + // Use goodMallocSize() to bump up the capacity to a decent size to request + // from malloc, so we can use all of the space that malloc will probably give + // us anyway. + return goodMallocSize(minSize); +} + +void IOBuf::initExtBuffer( + uint8_t* buf, + size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + // Find the SharedInfo storage at the end of the buffer + // and construct the SharedInfo. + uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo); + auto sharedInfo = new (infoStart) SharedInfo; + + *capacityReturn = std::size_t(infoStart - buf); + *infoReturn = sharedInfo; +} + +fbstring IOBuf::moveToFbString() { + // we need to save useHeapFullStorage and the observerListHead since + // sharedInfo() may not be valid after fbstring str + bool useHeapFullStorage = false; + SharedInfoObserverEntryBase* observerListHead = nullptr; + // malloc-allocated buffers are just fine, everything else needs + // to be turned into one. + if (!sharedInfo() || // user owned, not ours to give up + sharedInfo()->freeFn || // not malloc()-ed + headroom() != 0 || // malloc()-ed block doesn't start at beginning + tailroom() == 0 || // no room for NUL terminator + isShared() || // shared + isChained()) { // chained + // We might as well get rid of all head and tailroom if we're going + // to reallocate; we need 1 byte for NUL terminator. + coalesceAndReallocate(0, computeChainDataLength(), this, 1); + } else { + auto info = sharedInfo(); + if (info) { + // if we do not call coalesceAndReallocate + // we might need to call SharedInfo::releaseStorage() + // and/or SharedInfo::invokeAndDeleteEachObserver() + useHeapFullStorage = info->useHeapFullStorage; + // save the observerListHead + // the coalesceAndReallocate path will call + // decrementRefcount and freeExtBuffer if needed + // so the observer lis notification is needed here + observerListHead = info->observerListHead; + info->observerListHead = nullptr; + } + } + + // Ensure NUL terminated + *writableTail() = 0; + fbstring str( + reinterpret_cast<char*>(writableData()), + length(), + capacity(), + AcquireMallocatedString()); + + SharedInfo::invokeAndDeleteEachObserver( + observerListHead, [](auto& entry) { entry.afterReleaseExtBuffer(); }); + + if (flags() & kFlagFreeSharedInfo) { + delete sharedInfo(); + } else { + if (useHeapFullStorage) { + SharedInfo::releaseStorage(sharedInfo()); + } + } + + // Reset to a state where we can be deleted cleanly + flagsAndSharedInfo_ = 0; + buf_ = nullptr; + clear(); + return str; +} + +IOBuf::Iterator IOBuf::cbegin() const { + return Iterator(this, this); +} + +IOBuf::Iterator IOBuf::cend() const { + return Iterator(nullptr, nullptr); +} + +folly::fbvector<struct iovec> IOBuf::getIov() const { + folly::fbvector<struct iovec> iov; + iov.reserve(countChainElements()); + appendToIov(&iov); + return iov; +} + +void IOBuf::appendToIov(folly::fbvector<struct iovec>* iov) const { + IOBuf const* p = this; + do { + // some code can get confused by empty iovs, so skip them + if (p->length() > 0) { + iov->push_back({(void*)p->data(), folly::to<size_t>(p->length())}); + } + p = p->next(); + } while (p != this); +} + +unique_ptr<IOBuf> IOBuf::wrapIov(const iovec* vec, size_t count) { + unique_ptr<IOBuf> result = nullptr; + for (size_t i = 0; i < count; ++i) { + size_t len = vec[i].iov_len; + void* data = vec[i].iov_base; + if (len > 0) { + auto buf = wrapBuffer(data, len); + if (!result) { + result = std::move(buf); + } else { + result->prependChain(std::move(buf)); + } + } + } + if (UNLIKELY(result == nullptr)) { + return create(0); + } + return result; +} + +std::unique_ptr<IOBuf> IOBuf::takeOwnershipIov( + const iovec* vec, + size_t count, + FreeFunction freeFn, + void* userData, + bool freeOnError) { + unique_ptr<IOBuf> result = nullptr; + for (size_t i = 0; i < count; ++i) { + size_t len = vec[i].iov_len; + void* data = vec[i].iov_base; + if (len > 0) { + auto buf = takeOwnership(data, len, freeFn, userData, freeOnError); + if (!result) { + result = std::move(buf); + } else { + result->prependChain(std::move(buf)); + } + } + } + if (UNLIKELY(result == nullptr)) { + return create(0); + } + return result; +} + +IOBuf::FillIovResult IOBuf::fillIov(struct iovec* iov, size_t len) const { + IOBuf const* p = this; + size_t i = 0; + size_t totalBytes = 0; + while (i < len) { + // some code can get confused by empty iovs, so skip them + if (p->length() > 0) { + iov[i].iov_base = const_cast<uint8_t*>(p->data()); + iov[i].iov_len = p->length(); + totalBytes += p->length(); + i++; + } + p = p->next(); + if (p == this) { + return {i, totalBytes}; + } + } + return {0, 0}; +} + +uint32_t IOBuf::approximateShareCountOne() const { + if (UNLIKELY(!sharedInfo())) { + return 1U; + } + return sharedInfo()->refcount.load(std::memory_order_acquire); +} + +size_t IOBufHash::operator()(const IOBuf& buf) const noexcept { + folly::hash::SpookyHashV2 hasher; + hasher.Init(0, 0); + io::Cursor cursor(&buf); + for (;;) { + auto b = cursor.peekBytes(); + if (b.empty()) { + break; + } + hasher.Update(b.data(), b.size()); + cursor.skip(b.size()); + } + uint64_t h1; + uint64_t h2; + hasher.Final(&h1, &h2); + return static_cast<std::size_t>(h1); +} + +ordering IOBufCompare::impl(const IOBuf& a, const IOBuf& b) const noexcept { + io::Cursor ca(&a); + io::Cursor cb(&b); + for (;;) { + auto ba = ca.peekBytes(); + auto bb = cb.peekBytes(); + if (ba.empty() || bb.empty()) { + return to_ordering(int(bb.empty()) - int(ba.empty())); + } + const size_t n = std::min(ba.size(), bb.size()); + DCHECK_GT(n, 0u); + const ordering r = to_ordering(std::memcmp(ba.data(), bb.data(), n)); + if (r != ordering::eq) { + return r; + } + // Cursor::skip() may throw if n is too large, but n is not too large here + ca.skip(n); + cb.skip(n); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/IOBuf.h b/ios/Pods/Flipper-Folly/folly/io/IOBuf.h new file mode 100644 index 000000000..7021ae12c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/IOBuf.h @@ -0,0 +1,1800 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <glog/logging.h> +#include <atomic> +#include <cassert> +#include <cinttypes> +#include <cstddef> +#include <cstring> +#include <iterator> +#include <limits> +#include <memory> +#include <type_traits> + +#include <folly/FBString.h> +#include <folly/FBVector.h> +#include <folly/Function.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/detail/Iterators.h> +#include <folly/lang/Ordering.h> +#include <folly/portability/SysUio.h> +#include <folly/synchronization/MicroSpinLock.h> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +/** + * An IOBuf is a pointer to a buffer of data. + * + * IOBuf objects are intended to be used primarily for networking code, and are + * modelled somewhat after FreeBSD's mbuf data structure, and Linux's sk_buff + * structure. + * + * IOBuf objects facilitate zero-copy network programming, by allowing multiple + * IOBuf objects to point to the same underlying buffer of data, using a + * reference count to track when the buffer is no longer needed and can be + * freed. + * + * + * Data Layout + * ----------- + * + * The IOBuf itself is a small object containing a pointer to the buffer and + * information about which segment of the buffer contains valid data. + * + * The data layout looks like this: + * + * +-------+ + * | IOBuf | + * +-------+ + * / + * | + * v + * +------------+--------------------+-----------+ + * | headroom | data | tailroom | + * +------------+--------------------+-----------+ + * ^ ^ ^ ^ + * buffer() data() tail() bufferEnd() + * + * The length() method returns the length of the valid data; capacity() + * returns the entire capacity of the buffer (from buffer() to bufferEnd()). + * The headroom() and tailroom() methods return the amount of unused capacity + * available before and after the data. + * + * + * Buffer Sharing + * -------------- + * + * The buffer itself is reference counted, and multiple IOBuf objects may point + * to the same buffer. Each IOBuf may point to a different section of valid + * data within the underlying buffer. For example, if multiple protocol + * requests are read from the network into a single buffer, a separate IOBuf + * may be created for each request, all sharing the same underlying buffer. + * + * In other words, when multiple IOBufs share the same underlying buffer, the + * data() and tail() methods on each IOBuf may point to a different segment of + * the data. However, the buffer() and bufferEnd() methods will point to the + * same location for all IOBufs sharing the same underlying buffer. + * + * +-----------+ +---------+ + * | IOBuf 1 | | IOBuf 2 | + * +-----------+ +---------+ + * | | _____/ | + * data | tail |/ data | tail + * v v v + * +-------------------------------------+ + * | | | | | + * +-------------------------------------+ + * + * If you only read data from an IOBuf, you don't need to worry about other + * IOBuf objects possibly sharing the same underlying buffer. However, if you + * ever write to the buffer you need to first ensure that no other IOBufs point + * to the same buffer. The unshare() method may be used to ensure that you + * have an unshared buffer. + * + * + * IOBuf Chains + * ------------ + * + * IOBuf objects also contain pointers to next and previous IOBuf objects. + * This can be used to represent a single logical piece of data that is stored + * in non-contiguous chunks in separate buffers. + * + * A single IOBuf object can only belong to one chain at a time. + * + * IOBuf chains are always circular. The "prev" pointer in the head of the + * chain points to the tail of the chain. However, it is up to the user to + * decide which IOBuf is the head. Internally the IOBuf code does not care + * which element is the head. + * + * The lifetime of all IOBufs in the chain are linked: when one element in the + * chain is deleted, all other chained elements are also deleted. Conceptually + * it is simplest to treat this as if the head of the chain owns all other + * IOBufs in the chain. When you delete the head of the chain, it will delete + * the other elements as well. For this reason, prependChain() and + * appendChain() take ownership of the new elements being added to this chain. + * + * When the coalesce() method is used to coalesce an entire IOBuf chain into a + * single IOBuf, all other IOBufs in the chain are eliminated and automatically + * deleted. The unshare() method may coalesce the chain; if it does it will + * similarly delete all IOBufs eliminated from the chain. + * + * As discussed in the following section, it is up to the user to maintain a + * lock around the entire IOBuf chain if multiple threads need to access the + * chain. IOBuf does not provide any internal locking. + * + * + * Synchronization + * --------------- + * + * When used in multithread programs, a single IOBuf object should only be used + * in a single thread at a time. If a caller uses a single IOBuf across + * multiple threads the caller is responsible for using an external lock to + * synchronize access to the IOBuf. + * + * Two separate IOBuf objects may be accessed concurrently in separate threads + * without locking, even if they point to the same underlying buffer. The + * buffer reference count is always accessed atomically, and no other + * operations should affect other IOBufs that point to the same data segment. + * The caller is responsible for using unshare() to ensure that the data buffer + * is not shared by other IOBufs before writing to it, and this ensures that + * the data itself is not modified in one thread while also being accessed from + * another thread. + * + * For IOBuf chains, no two IOBufs in the same chain should be accessed + * simultaneously in separate threads. The caller must maintain a lock around + * the entire chain if the chain, or individual IOBufs in the chain, may be + * accessed by multiple threads. + * + * + * IOBuf Object Allocation + * ----------------------- + * + * IOBuf objects themselves exist separately from the data buffer they point + * to. Therefore one must also consider how to allocate and manage the IOBuf + * objects. + * + * It is more common to allocate IOBuf objects on the heap, using the create(), + * takeOwnership(), or wrapBuffer() factory functions. The clone()/cloneOne() + * functions also return new heap-allocated IOBufs. The createCombined() + * function allocates the IOBuf object and data storage space together, in a + * single memory allocation. This can improve performance, particularly if you + * know that the data buffer and the IOBuf itself will have similar lifetimes. + * + * That said, it is also possible to allocate IOBufs on the stack or inline + * inside another object as well. This is useful for cases where the IOBuf is + * short-lived, or when the overhead of allocating the IOBuf on the heap is + * undesirable. + * + * However, note that stack-allocated IOBufs may only be used as the head of a + * chain (or standalone as the only IOBuf in a chain). All non-head members of + * an IOBuf chain must be heap allocated. (All functions to add nodes to a + * chain require a std::unique_ptr<IOBuf>, which enforces this requirement.) + * + * Copying IOBufs is only meaningful for the head of a chain. The entire chain + * is cloned; the IOBufs will become shared, and the old and new IOBufs will + * refer to the same underlying memory. + * + * IOBuf Sharing + * ------------- + * + * The IOBuf class manages sharing of the underlying buffer that it points to, + * maintaining a reference count if multiple IOBufs are pointing at the same + * buffer. + * + * However, it is the callers responsibility to manage sharing and ownership of + * IOBuf objects themselves. The IOBuf structure does not provide room for an + * intrusive refcount on the IOBuf object itself, only the underlying data + * buffer is reference counted. If users want to share the same IOBuf object + * between multiple parts of the code, they are responsible for managing this + * sharing on their own. (For example, by using a shared_ptr. Alternatively, + * users always have the option of using clone() to create a second IOBuf that + * points to the same underlying buffer.) + */ +namespace detail { +// Is T a unique_ptr<> to a standard-layout type? +template <typename T> +struct IsUniquePtrToSL : std::false_type {}; +template <typename T, typename D> +struct IsUniquePtrToSL<std::unique_ptr<T, D>> : std::is_standard_layout<T> {}; +} // namespace detail + +class IOBuf { + public: + class Iterator; + + enum CreateOp { CREATE }; + enum WrapBufferOp { WRAP_BUFFER }; + enum TakeOwnershipOp { TAKE_OWNERSHIP }; + enum CopyBufferOp { COPY_BUFFER }; + + enum class CombinedOption { DEFAULT, COMBINED, SEPARATE }; + + typedef ByteRange value_type; + typedef Iterator iterator; + typedef Iterator const_iterator; + + typedef void (*FreeFunction)(void* buf, void* userData); + + /** + * Allocate a new IOBuf object with the requested capacity. + * + * Returns a new IOBuf object that must be (eventually) deleted by the + * caller. The returned IOBuf may actually have slightly more capacity than + * requested. + * + * The data pointer will initially point to the start of the newly allocated + * buffer, and will have a data length of 0. + * + * Throws std::bad_alloc on error. + */ + static std::unique_ptr<IOBuf> create(std::size_t capacity); + IOBuf(CreateOp, std::size_t capacity); + + /** + * Create a new IOBuf, using a single memory allocation to allocate space + * for both the IOBuf object and the data storage space. + * + * This saves one memory allocation. However, it can be wasteful if you + * later need to grow the buffer using reserve(). If the buffer needs to be + * reallocated, the space originally allocated will not be freed() until the + * IOBuf object itself is also freed. (It can also be slightly wasteful in + * some cases where you clone this IOBuf and then free the original IOBuf.) + */ + static std::unique_ptr<IOBuf> createCombined(std::size_t capacity); + + /** + * Create a new IOBuf, using separate memory allocations for the IOBuf object + * for the IOBuf and the data storage space. + * + * This requires two memory allocations, but saves space in the long run + * if you know that you will need to reallocate the data buffer later. + */ + static std::unique_ptr<IOBuf> createSeparate(std::size_t capacity); + + /** + * Allocate a new IOBuf chain with the requested total capacity, allocating + * no more than maxBufCapacity to each buffer. + */ + static std::unique_ptr<IOBuf> createChain( + size_t totalCapacity, + std::size_t maxBufCapacity); + + /** + * Uses folly::goodMallocSize() to figure out what the largest capacity would + * be that would trigger the same underlying allocation size as would be + * triggered by the given capacity. + * + * Note that IOBufs do this up-sizing for you: they will round up to the full + * allocation size and make that capacity available to you without your using + * this function. This just lets you introspect into that process, so you can + * for example figure out whether a given IOBuf can be usefully compacted. + */ + static size_t goodSize( + size_t minCapacity, + CombinedOption combined = CombinedOption::DEFAULT); + + /** + * Create a new IOBuf pointing to an existing data buffer. + * + * The new IOBuffer will assume ownership of the buffer, and free it by + * calling the specified FreeFunction when the last IOBuf pointing to this + * buffer is destroyed. The function will be called with a pointer to the + * buffer as the first argument, and the supplied userData value as the + * second argument. The free function must never throw exceptions. + * + * If no FreeFunction is specified, the buffer will be freed using free() + * which will result in undefined behavior if the memory was allocated + * using 'new'. + * + * The IOBuf data pointer will initially point to the start of the buffer, + * + * In the first version of this function, the length of data is unspecified + * and is initialized to the capacity of the buffer + * + * In the second version, the user specifies the valid length of data + * in the buffer + * + * On error, std::bad_alloc will be thrown. If freeOnError is true (the + * default) the buffer will be freed before throwing the error. + */ + static std::unique_ptr<IOBuf> takeOwnership( + void* buf, + std::size_t capacity, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true) { + return takeOwnership( + buf, capacity, capacity, freeFn, userData, freeOnError); + } + IOBuf( + TakeOwnershipOp op, + void* buf, + std::size_t capacity, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true) + : IOBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {} + + static std::unique_ptr<IOBuf> takeOwnership( + void* buf, + std::size_t capacity, + std::size_t length, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + IOBuf( + TakeOwnershipOp, + void* buf, + std::size_t capacity, + std::size_t length, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + /** + * Create a new IOBuf pointing to an existing data buffer made up of + * count objects of a given standard-layout type. + * + * This is dangerous -- it is essentially equivalent to doing + * reinterpret_cast<unsigned char*> on your data -- but it's often useful + * for serialization / deserialization. + * + * The new IOBuffer will assume ownership of the buffer, and free it + * appropriately (by calling the UniquePtr's custom deleter, or by calling + * delete or delete[] appropriately if there is no custom deleter) + * when the buffer is destroyed. The custom deleter, if any, must never + * throw exceptions. + * + * The IOBuf data pointer will initially point to the start of the buffer, + * and the length will be the full capacity of the buffer (count * + * sizeof(T)). + * + * On error, std::bad_alloc will be thrown, and the buffer will be freed + * before throwing the error. + */ + template <class UniquePtr> + static typename std::enable_if< + detail::IsUniquePtrToSL<UniquePtr>::value, + std::unique_ptr<IOBuf>>::type + takeOwnership(UniquePtr&& buf, size_t count = 1); + + /** + * Create a new IOBuf object that points to an existing user-owned buffer. + * + * This should only be used when the caller knows the lifetime of the IOBuf + * object ahead of time and can ensure that all IOBuf objects that will point + * to this buffer will be destroyed before the buffer itself is destroyed. + * + * This buffer will not be freed automatically when the last IOBuf + * referencing it is destroyed. It is the caller's responsibility to free + * the buffer after the last IOBuf has been destroyed. + * + * The IOBuf data pointer will initially point to the start of the buffer, + * and the length will be the full capacity of the buffer. + * + * An IOBuf created using wrapBuffer() will always be reported as shared. + * unshare() may be used to create a writable copy of the buffer. + * + * On error, std::bad_alloc will be thrown. + */ + static std::unique_ptr<IOBuf> wrapBuffer( + const void* buf, + std::size_t capacity); + static std::unique_ptr<IOBuf> wrapBuffer(ByteRange br) { + return wrapBuffer(br.data(), br.size()); + } + + /** + * Similar to wrapBuffer(), but returns IOBuf by value rather than + * heap-allocating it. + */ + static IOBuf wrapBufferAsValue( + const void* buf, + std::size_t capacity) noexcept; + static IOBuf wrapBufferAsValue(ByteRange br) noexcept { + return wrapBufferAsValue(br.data(), br.size()); + } + + IOBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept; + IOBuf(WrapBufferOp op, ByteRange br) noexcept; + + /** + * Convenience function to create a new IOBuf object that copies data from a + * user-supplied buffer, optionally allocating a given amount of + * headroom and tailroom. + */ + static std::unique_ptr<IOBuf> copyBuffer( + const void* buf, + std::size_t size, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + static std::unique_ptr<IOBuf> copyBuffer( + ByteRange br, + std::size_t headroom = 0, + std::size_t minTailroom = 0) { + return copyBuffer(br.data(), br.size(), headroom, minTailroom); + } + IOBuf( + CopyBufferOp op, + const void* buf, + std::size_t size, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + IOBuf( + CopyBufferOp op, + ByteRange br, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + + /** + * Convenience function to create a new IOBuf object that copies data from a + * user-supplied string, optionally allocating a given amount of + * headroom and tailroom. + * + * Beware when attempting to invoke this function with a constant string + * literal and a headroom argument: you will likely end up invoking the + * version of copyBuffer() above. IOBuf::copyBuffer("hello", 3) will treat + * the first argument as a const void*, and will invoke the version of + * copyBuffer() above, with the size argument of 3. + */ + static std::unique_ptr<IOBuf> copyBuffer( + const std::string& buf, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + IOBuf( + CopyBufferOp op, + const std::string& buf, + std::size_t headroom = 0, + std::size_t minTailroom = 0) + : IOBuf(op, buf.data(), buf.size(), headroom, minTailroom) {} + + /** + * A version of copyBuffer() that returns a null pointer if the input string + * is empty. + */ + static std::unique_ptr<IOBuf> maybeCopyBuffer( + const std::string& buf, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + + /** + * Convenience function to free a chain of IOBufs held by a unique_ptr. + */ + static void destroy(std::unique_ptr<IOBuf>&& data) { + auto destroyer = std::move(data); + } + + /** + * Destroy this IOBuf. + * + * Deleting an IOBuf will automatically destroy all IOBufs in the chain. + * (See the comments above regarding the ownership model of IOBuf chains. + * All subsequent IOBufs in the chain are considered to be owned by the head + * of the chain. Users should only explicitly delete the head of a chain.) + * + * When each individual IOBuf is destroyed, it will release its reference + * count on the underlying buffer. If it was the last user of the buffer, + * the buffer will be freed. + */ + ~IOBuf(); + + /** + * Check whether the chain is empty (i.e., whether the IOBufs in the + * chain have a total data length of zero). + * + * This method is semantically equivalent to + * i->computeChainDataLength()==0 + * but may run faster because it can short-circuit as soon as it + * encounters a buffer with length()!=0 + */ + bool empty() const; + + /** + * Get the pointer to the start of the data. + */ + const uint8_t* data() const { + return data_; + } + + /** + * Get a writable pointer to the start of the data. + * + * The caller is responsible for calling unshare() first to ensure that it is + * actually safe to write to the buffer. + */ + uint8_t* writableData() { + return data_; + } + + /** + * Get the pointer to the end of the data. + */ + const uint8_t* tail() const { + return data_ + length_; + } + + /** + * Get a writable pointer to the end of the data. + * + * The caller is responsible for calling unshare() first to ensure that it is + * actually safe to write to the buffer. + */ + uint8_t* writableTail() { + return data_ + length_; + } + + /** + * Get the length of the data for this individual IOBuf in the chain. See + * computeChainDataLength() for the sum of data length for the full chain. + */ + std::size_t length() const { + return length_; + } + + /** + * Get the amount of head room. + * + * Returns the number of bytes in the buffer before the start of the data. + */ + std::size_t headroom() const { + return std::size_t(data_ - buffer()); + } + + /** + * Get the amount of tail room. + * + * Returns the number of bytes in the buffer after the end of the data. + */ + std::size_t tailroom() const { + return std::size_t(bufferEnd() - tail()); + } + + /** + * Get the pointer to the start of the buffer. + * + * Note that this is the pointer to the very beginning of the usable buffer, + * not the start of valid data within the buffer. Use the data() method to + * get a pointer to the start of the data within the buffer. + */ + const uint8_t* buffer() const { + return buf_; + } + + /** + * Get a writable pointer to the start of the buffer. + * + * The caller is responsible for calling unshare() first to ensure that it is + * actually safe to write to the buffer. + */ + uint8_t* writableBuffer() { + return buf_; + } + + /** + * Get the pointer to the end of the buffer. + * + * Note that this is the pointer to the very end of the usable buffer, + * not the end of valid data within the buffer. Use the tail() method to + * get a pointer to the end of the data within the buffer. + */ + const uint8_t* bufferEnd() const { + return buf_ + capacity_; + } + + /** + * Get the total size of the buffer. + * + * This returns the total usable length of the buffer. Use the length() + * method to get the length of the actual valid data in this IOBuf. + */ + std::size_t capacity() const { + return capacity_; + } + + /** + * Get a pointer to the next IOBuf in this chain. + */ + IOBuf* next() { + return next_; + } + const IOBuf* next() const { + return next_; + } + + /** + * Get a pointer to the previous IOBuf in this chain. + */ + IOBuf* prev() { + return prev_; + } + const IOBuf* prev() const { + return prev_; + } + + /** + * Shift the data forwards in the buffer. + * + * This shifts the data pointer forwards in the buffer to increase the + * headroom. This is commonly used to increase the headroom in a newly + * allocated buffer. + * + * The caller is responsible for ensuring that there is sufficient + * tailroom in the buffer before calling advance(). + * + * If there is a non-zero data length, advance() will use memmove() to shift + * the data forwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other IOBufs + * that may be sharing the same underlying buffer. + */ + void advance(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= tailroom()); + + if (length_ > 0) { + memmove(data_ + amount, data_, length_); + } + data_ += amount; + } + + /** + * Shift the data backwards in the buffer. + * + * The caller is responsible for ensuring that there is sufficient headroom + * in the buffer before calling retreat(). + * + * If there is a non-zero data length, retreat() will use memmove() to shift + * the data backwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other IOBufs + * that may be sharing the same underlying buffer. + */ + void retreat(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= headroom()); + + if (length_ > 0) { + memmove(data_ - amount, data_, length_); + } + data_ -= amount; + } + + /** + * Adjust the data pointer to include more valid data at the beginning. + * + * This moves the data pointer backwards to include more of the available + * buffer. The caller is responsible for ensuring that there is sufficient + * headroom for the new data. The caller is also responsible for populating + * this section with valid data. + * + * This does not modify any actual data in the buffer. + */ + void prepend(std::size_t amount) { + DCHECK_LE(amount, headroom()); + data_ -= amount; + length_ += amount; + } + + /** + * Adjust the tail pointer to include more valid data at the end. + * + * This moves the tail pointer forwards to include more of the available + * buffer. The caller is responsible for ensuring that there is sufficient + * tailroom for the new data. The caller is also responsible for populating + * this section with valid data. + * + * This does not modify any actual data in the buffer. + */ + void append(std::size_t amount) { + DCHECK_LE(amount, tailroom()); + length_ += amount; + } + + /** + * Adjust the data pointer forwards to include less valid data. + * + * This moves the data pointer forwards so that the first amount bytes are no + * longer considered valid data. The caller is responsible for ensuring that + * amount is less than or equal to the actual data length. + * + * This does not modify any actual data in the buffer. + */ + void trimStart(std::size_t amount) { + DCHECK_LE(amount, length_); + data_ += amount; + length_ -= amount; + } + + /** + * Adjust the tail pointer backwards to include less valid data. + * + * This moves the tail pointer backwards so that the last amount bytes are no + * longer considered valid data. The caller is responsible for ensuring that + * amount is less than or equal to the actual data length. + * + * This does not modify any actual data in the buffer. + */ + void trimEnd(std::size_t amount) { + DCHECK_LE(amount, length_); + length_ -= amount; + } + + /** + * Clear the buffer. + * + * Postcondition: headroom() == 0, length() == 0, tailroom() == capacity() + */ + void clear() { + data_ = writableBuffer(); + length_ = 0; + } + + /** + * Ensure that this buffer has at least minHeadroom headroom bytes and at + * least minTailroom tailroom bytes. The buffer must be writable + * (you must call unshare() before this, if necessary). + * + * Postcondition: headroom() >= minHeadroom, tailroom() >= minTailroom, + * the data (between data() and data() + length()) is preserved. + */ + void reserve(std::size_t minHeadroom, std::size_t minTailroom) { + // Maybe we don't need to do anything. + if (headroom() >= minHeadroom && tailroom() >= minTailroom) { + return; + } + // If the buffer is empty but we have enough total room (head + tail), + // move the data_ pointer around. + if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) { + data_ = writableBuffer() + minHeadroom; + return; + } + // Bah, we have to do actual work. + reserveSlow(minHeadroom, minTailroom); + } + + /** + * Return true if this IOBuf is part of a chain of multiple IOBufs, or false + * if this is the only IOBuf in its chain. + */ + bool isChained() const { + assert((next_ == this) == (prev_ == this)); + return next_ != this; + } + + /** + * Get the number of IOBufs in this chain. + * + * Beware that this method has to walk the entire chain. + * Use isChained() if you just want to check if this IOBuf is part of a chain + * or not. + */ + size_t countChainElements() const; + + /** + * Get the length of all the data in this IOBuf chain. + * + * Beware that this method has to walk the entire chain. + */ + std::size_t computeChainDataLength() const; + + /** + * Insert another IOBuf chain immediately before this IOBuf. + * + * For example, if there are two IOBuf chains (A, B, C) and (D, E, F), + * and B->prependChain(D) is called, the (D, E, F) chain will be subsumed + * and become part of the chain starting at A, which will now look like + * (A, D, E, F, B, C) + * + * Note that since IOBuf chains are circular, head->prependChain(other) can + * be used to append the other chain at the very end of the chain pointed to + * by head. For example, if there are two IOBuf chains (A, B, C) and + * (D, E, F), and A->prependChain(D) is called, the chain starting at A will + * now consist of (A, B, C, D, E, F) + * + * The elements in the specified IOBuf chain will become part of this chain, + * and will be owned by the head of this chain. When this chain is + * destroyed, all elements in the supplied chain will also be destroyed. + * + * For this reason, appendChain() only accepts an rvalue-reference to a + * unique_ptr(), to make it clear that it is taking ownership of the supplied + * chain. If you have a raw pointer, you can pass in a new temporary + * unique_ptr around the raw pointer. If you have an existing, + * non-temporary unique_ptr, you must call std::move(ptr) to make it clear + * that you are destroying the original pointer. + */ + void prependChain(std::unique_ptr<IOBuf>&& iobuf); + + /** + * Append another IOBuf chain immediately after this IOBuf. + * + * For example, if there are two IOBuf chains (A, B, C) and (D, E, F), + * and B->appendChain(D) is called, the (D, E, F) chain will be subsumed + * and become part of the chain starting at A, which will now look like + * (A, B, D, E, F, C) + * + * The elements in the specified IOBuf chain will become part of this chain, + * and will be owned by the head of this chain. When this chain is + * destroyed, all elements in the supplied chain will also be destroyed. + * + * For this reason, appendChain() only accepts an rvalue-reference to a + * unique_ptr(), to make it clear that it is taking ownership of the supplied + * chain. If you have a raw pointer, you can pass in a new temporary + * unique_ptr around the raw pointer. If you have an existing, + * non-temporary unique_ptr, you must call std::move(ptr) to make it clear + * that you are destroying the original pointer. + */ + void appendChain(std::unique_ptr<IOBuf>&& iobuf) { + // Just use prependChain() on the next element in our chain + next_->prependChain(std::move(iobuf)); + } + + /** + * Remove this IOBuf from its current chain. + * + * Since ownership of all elements an IOBuf chain is normally maintained by + * the head of the chain, unlink() transfers ownership of this IOBuf from the + * chain and gives it to the caller. A new unique_ptr to the IOBuf is + * returned to the caller. The caller must store the returned unique_ptr (or + * call release() on it) to take ownership, otherwise the IOBuf will be + * immediately destroyed. + * + * Since unlink transfers ownership of the IOBuf to the caller, be careful + * not to call unlink() on the head of a chain if you already maintain + * ownership on the head of the chain via other means. The pop() method + * is a better choice for that situation. + */ + std::unique_ptr<IOBuf> unlink() { + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr<IOBuf>(this); + } + + /** + * Remove this IOBuf from its current chain and return a unique_ptr to + * the IOBuf that formerly followed it in the chain. + */ + std::unique_ptr<IOBuf> pop() { + IOBuf* next = next_; + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr<IOBuf>((next == this) ? nullptr : next); + } + + /** + * Remove a subchain from this chain. + * + * Remove the subchain starting at head and ending at tail from this chain. + * + * Returns a unique_ptr pointing to head. (In other words, ownership of the + * head of the subchain is transferred to the caller.) If the caller ignores + * the return value and lets the unique_ptr be destroyed, the subchain will + * be immediately destroyed. + * + * The subchain referenced by the specified head and tail must be part of the + * same chain as the current IOBuf, but must not contain the current IOBuf. + * However, the specified head and tail may be equal to each other (i.e., + * they may be a subchain of length 1). + */ + std::unique_ptr<IOBuf> separateChain(IOBuf* head, IOBuf* tail) { + assert(head != this); + assert(tail != this); + + head->prev_->next_ = tail->next_; + tail->next_->prev_ = head->prev_; + + head->prev_ = tail; + tail->next_ = head; + + return std::unique_ptr<IOBuf>(head); + } + + /** + * Return true if at least one of the IOBufs in this chain are shared, + * or false if all of the IOBufs point to unique buffers. + * + * Use isSharedOne() to only check this IOBuf rather than the entire chain. + */ + bool isShared() const { + const IOBuf* current = this; + while (true) { + if (current->isSharedOne()) { + return true; + } + current = current->next_; + if (current == this) { + return false; + } + } + } + + /** + * + * Returns the SharedInfo::userData if sharedInfo() + * is not nullptr, nullptr otherwise + * + **/ + void* getUserData() const noexcept { + SharedInfo* info = sharedInfo(); + return info ? info->userData : nullptr; + } + + /** + * + * Returns free function if sharedInfo() is not nullputr, nullptr otherwise + * + **/ + FreeFunction getFreeFn() const noexcept { + SharedInfo* info = sharedInfo(); + return info ? info->freeFn : nullptr; + } + + template <typename Observer> + bool appendSharedInfoObserver(Observer&& observer) { + SharedInfo* info = sharedInfo(); + if (!info) { + return false; + } + + auto* entry = + new SharedInfoObserverEntry<Observer>(std::forward<Observer>(observer)); + std::lock_guard<MicroSpinLock> guard(info->observerListLock); + if (!info->observerListHead) { + info->observerListHead = entry; + } else { + // prepend + entry->next = info->observerListHead; + entry->prev = info->observerListHead->prev; + info->observerListHead->prev->next = entry; + info->observerListHead->prev = entry; + } + + return true; + } + + /** + * Return true if all IOBufs in this chain are managed by the usual + * refcounting mechanism (and so the lifetime of the underlying memory + * can be extended by clone()). + */ + bool isManaged() const { + const IOBuf* current = this; + while (true) { + if (!current->isManagedOne()) { + return false; + } + current = current->next_; + if (current == this) { + return true; + } + } + } + + /** + * Return true if this IOBuf is managed by the usual refcounting mechanism + * (and so the lifetime of the underlying memory can be extended by + * cloneOne()). + */ + bool isManagedOne() const noexcept { + return sharedInfo(); + } + + /** + * For most of the use-cases where it seems like a good idea to call this + * function, what you really want is `isSharedOne()`. + * + * If this IOBuf is managed by the usual refcounting mechanism (ie + * `isManagedOne()` returns `true`): this returns the reference count as it + * was when recently observed by this thread. + * + * If this IOBuf is *not* managed by the usual refcounting mechanism then the + * result of this function is not defined. + * + * This only checks the current IOBuf, and not other IOBufs in the chain. + */ + uint32_t approximateShareCountOne() const; + + /** + * Return true if other IOBufs are also pointing to the buffer used by this + * IOBuf, and false otherwise. + * + * If this IOBuf points at a buffer owned by another (non-IOBuf) part of the + * code (i.e., if the IOBuf was created using wrapBuffer(), or was cloned + * from such an IOBuf), it is always considered shared. + * + * This only checks the current IOBuf, and not other IOBufs in the chain. + */ + bool isSharedOne() const noexcept { + // If this is a user-owned buffer, it is always considered shared + if (UNLIKELY(!sharedInfo())) { + return true; + } + + if (UNLIKELY(sharedInfo()->externallyShared)) { + return true; + } + + return sharedInfo()->refcount.load(std::memory_order_acquire) > 1; + } + + /** + * Ensure that this IOBuf has a unique buffer that is not shared by other + * IOBufs. + * + * unshare() operates on an entire chain of IOBuf objects. If the chain is + * shared, it may also coalesce the chain when making it unique. If the + * chain is coalesced, subsequent IOBuf objects in the current chain will be + * automatically deleted. + * + * Note that buffers owned by other (non-IOBuf) users are automatically + * considered shared. + * + * Throws std::bad_alloc on error. On error the IOBuf chain will be + * unmodified. + * + * Currently unshare may also throw std::overflow_error if it tries to + * coalesce. (TODO: In the future it would be nice if unshare() were smart + * enough not to coalesce the entire buffer if the data is too large. + * However, in practice this seems unlikely to become an issue.) + */ + void unshare() { + if (isChained()) { + unshareChained(); + } else { + unshareOne(); + } + } + + /** + * Ensure that this IOBuf has a unique buffer that is not shared by other + * IOBufs. + * + * unshareOne() operates on a single IOBuf object. This IOBuf will have a + * unique buffer after unshareOne() returns, but other IOBufs in the chain + * may still be shared after unshareOne() returns. + * + * Throws std::bad_alloc on error. On error the IOBuf will be unmodified. + */ + void unshareOne() { + if (isSharedOne()) { + unshareOneSlow(); + } + } + + /** + * Mark the underlying buffers in this chain as shared with external memory + * management mechanism. This will make isShared() always returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an IOBuf, before it has been shared with other threads. + */ + void markExternallyShared(); + + /** + * Mark the underlying buffer that this IOBuf refers to as shared with + * external memory management mechanism. This will make isSharedOne() always + * returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an IOBuf, before it has been shared with other threads. + */ + void markExternallySharedOne() { + SharedInfo* info = sharedInfo(); + if (info) { + info->externallyShared = true; + } + } + + /** + * Ensure that the memory that IOBufs in this chain refer to will continue to + * be allocated for as long as the IOBufs of the chain (or any clone()s + * created from this point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManaged() { + if (isChained()) { + makeManagedChained(); + } else { + makeManagedOne(); + } + } + + /** + * Ensure that the memory that this IOBuf refers to will continue to be + * allocated for as long as this IOBuf (or any clone()s created from this + * point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManagedOne() { + if (!isManagedOne()) { + // We can call the internal function directly; unmanaged implies shared. + unshareOneSlow(); + } + } + + /** + * Coalesce this IOBuf chain into a single buffer. + * + * This method moves all of the data in this IOBuf chain into a single + * contiguous buffer, if it is not already in one buffer. After coalesce() + * returns, this IOBuf will be a chain of length one. Other IOBufs in the + * chain will be automatically deleted. + * + * After coalescing, the IOBuf will have at least as much headroom as the + * first IOBuf in the chain, and at least as much tailroom as the last IOBuf + * in the chain. + * + * Throws std::bad_alloc on error. On error the IOBuf chain will be + * unmodified. + * + * Returns ByteRange that points to the data IOBuf stores. + */ + ByteRange coalesce() { + const std::size_t newHeadroom = headroom(); + const std::size_t newTailroom = prev()->tailroom(); + return coalesceWithHeadroomTailroom(newHeadroom, newTailroom); + } + + /** + * This is similar to the coalesce() method, except this allows to set a + * headroom and tailroom after coalescing. + * + * Returns ByteRange that points to the data IOBuf stores. + */ + ByteRange coalesceWithHeadroomTailroom( + std::size_t newHeadroom, + std::size_t newTailroom) { + if (isChained()) { + coalesceAndReallocate( + newHeadroom, computeChainDataLength(), this, newTailroom); + } + return ByteRange(data_, length_); + } + + /** + * Ensure that this chain has at least maxLength bytes available as a + * contiguous memory range. + * + * This method coalesces whole buffers in the chain into this buffer as + * necessary until this buffer's length() is at least maxLength. + * + * After coalescing, the IOBuf will have at least as much headroom as the + * first IOBuf in the chain, and at least as much tailroom as the last IOBuf + * that was coalesced. + * + * Throws std::bad_alloc or std::overflow_error on error. On error the IOBuf + * chain will be unmodified. Throws std::overflow_error if maxLength is + * longer than the total chain length. + * + * Upon return, either enough of the chain was coalesced into a contiguous + * region, or the entire chain was coalesced. That is, + * length() >= maxLength || !isChained() is true. + */ + void gather(std::size_t maxLength) { + if (!isChained() || length_ >= maxLength) { + return; + } + coalesceSlow(maxLength); + } + + /** + * Return a new IOBuf chain sharing the same data as this chain. + * + * The new IOBuf chain will normally point to the same underlying data + * buffers as the original chain. (The one exception to this is if some of + * the IOBufs in this chain contain small internal data buffers which cannot + * be shared.) + */ + std::unique_ptr<IOBuf> clone() const; + + /** + * Similar to clone(). But returns IOBuf by value rather than heap-allocating + * it. + */ + IOBuf cloneAsValue() const; + + /** + * Return a new IOBuf with the same data as this IOBuf. + * + * The new IOBuf returned will not be part of a chain (even if this IOBuf is + * part of a larger chain). + */ + std::unique_ptr<IOBuf> cloneOne() const; + + /** + * Similar to cloneOne(). But returns IOBuf by value rather than + * heap-allocating it. + */ + IOBuf cloneOneAsValue() const; + + /** + * Return a new unchained IOBuf that may share the same data as this chain. + * + * If the IOBuf chain is not chained then the new IOBuf will point to the same + * underlying data buffer as the original chain. Otherwise, it will clone and + * coalesce the IOBuf chain. + * + * The new IOBuf will have at least as much headroom as the first IOBuf in the + * chain, and at least as much tailroom as the last IOBuf in the chain. + * + * Throws std::bad_alloc on error. + */ + std::unique_ptr<IOBuf> cloneCoalesced() const; + + /** + * This is similar to the cloneCoalesced() method, except this allows to set a + * headroom and tailroom for the new IOBuf. + */ + std::unique_ptr<IOBuf> cloneCoalescedWithHeadroomTailroom( + std::size_t newHeadroom, + std::size_t newTailroom) const; + + /** + * Similar to cloneCoalesced(). But returns IOBuf by value rather than + * heap-allocating it. + */ + IOBuf cloneCoalescedAsValue() const; + + /** + * This is similar to the cloneCoalescedAsValue() method, except this allows + * to set a headroom and tailroom for the new IOBuf. + */ + IOBuf cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t newHeadroom, + std::size_t newTailroom) const; + + /** + * Similar to Clone(). But use other as the head node. Other nodes in the + * chain (if any) will be allocted on heap. + */ + void cloneInto(IOBuf& other) const { + other = cloneAsValue(); + } + + /** + * Similar to CloneOne(). But to fill an existing IOBuf instead of a new + * IOBuf. + */ + void cloneOneInto(IOBuf& other) const { + other = cloneOneAsValue(); + } + + /** + * Return an iovector suitable for e.g. writev() + * + * auto iov = buf->getIov(); + * auto xfer = writev(fd, iov.data(), iov.size()); + * + * Naturally, the returned iovector is invalid if you modify the buffer + * chain. + */ + folly::fbvector<struct iovec> getIov() const; + + /** + * Update an existing iovec array with the IOBuf data. + * + * New iovecs will be appended to the existing vector; anything already + * present in the vector will be left unchanged. + * + * Naturally, the returned iovec data will be invalid if you modify the + * buffer chain. + */ + void appendToIov(folly::fbvector<struct iovec>* iov) const; + + struct FillIovResult { + // How many iovecs were filled (or 0 on error). + size_t numIovecs; + // The total length of filled iovecs (or 0 on error). + size_t totalLength; + }; + + /** + * Fill an iovec array with the IOBuf data. + * + * Returns a struct with two fields: the number of iovec filled, and total + * size of the iovecs filled. If there are more buffer than iovec, returns 0 + * in both fields. + * This version is suitable to use with stack iovec arrays. + * + * Naturally, the filled iovec data will be invalid if you modify the + * buffer chain. + */ + FillIovResult fillIov(struct iovec* iov, size_t len) const; + + /** + * A helper that wraps a number of iovecs into an IOBuf chain. If count == 0, + * then a zero length buf is returned. This function never returns nullptr. + */ + static std::unique_ptr<IOBuf> wrapIov(const iovec* vec, size_t count); + + /** + * A helper that takes ownerships a number of iovecs into an IOBuf chain. If + * count == 0, then a zero length buf is returned. This function never + * returns nullptr. + */ + static std::unique_ptr<IOBuf> takeOwnershipIov( + const iovec* vec, + size_t count, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + /* + * Overridden operator new and delete. + * These perform specialized memory management to help support + * createCombined(), which allocates IOBuf objects together with the buffer + * data. + */ + void* operator new(size_t size); + void* operator new(size_t size, void* ptr); + void operator delete(void* ptr); + void operator delete(void* ptr, void* placement); + + /** + * Destructively convert this IOBuf to a fbstring efficiently. + * We rely on fbstring's AcquireMallocatedString constructor to + * transfer memory. + */ + fbstring moveToFbString(); + + /** + * Iteration support: a chain of IOBufs may be iterated through using + * STL-style iterators over const ByteRanges. Iterators are only invalidated + * if the IOBuf that they currently point to is removed. + */ + Iterator cbegin() const; + Iterator cend() const; + Iterator begin() const; + Iterator end() const; + + /** + * Allocate a new null buffer. + * + * This can be used to allocate an empty IOBuf on the stack. It will have no + * space allocated for it. This is generally useful only to later use move + * assignment to fill out the IOBuf. + */ + IOBuf() noexcept; + + /** + * Move constructor and assignment operator. + * + * In general, you should only ever move the head of an IOBuf chain. + * Internal nodes in an IOBuf chain are owned by the head of the chain, and + * should not be moved from. (Technically, nothing prevents you from moving + * a non-head node, but the moved-to node will replace the moved-from node in + * the chain. This has implications for ownership, since non-head nodes are + * owned by the chain head. You are then responsible for relinquishing + * ownership of the moved-to node, and manually deleting the moved-from + * node.) + * + * With the move assignment operator, the destination of the move should be + * the head of an IOBuf chain or a solitary IOBuf not part of a chain. If + * the move destination is part of a chain, all other IOBufs in the chain + * will be deleted. + */ + IOBuf(IOBuf&& other) noexcept; + IOBuf& operator=(IOBuf&& other) noexcept; + + IOBuf(const IOBuf& other); + IOBuf& operator=(const IOBuf& other); + + private: + enum FlagsEnum : uintptr_t { + // Adding any more flags would not work on 32-bit architectures, + // as these flags are stashed in the least significant 2 bits of a + // max-align-aligned pointer. + kFlagFreeSharedInfo = 0x1, + kFlagMask = (1 << 2 /* least significant bits */) - 1, + }; + + struct SharedInfoObserverEntryBase { + SharedInfoObserverEntryBase* prev{this}; + SharedInfoObserverEntryBase* next{this}; + + virtual ~SharedInfoObserverEntryBase() = default; + + virtual void afterFreeExtBuffer() const noexcept = 0; + virtual void afterReleaseExtBuffer() const noexcept = 0; + }; + + template <typename Observer> + struct SharedInfoObserverEntry : SharedInfoObserverEntryBase { + std::decay_t<Observer> observer; + + explicit SharedInfoObserverEntry(Observer&& obs) noexcept( + noexcept(Observer(std::forward<Observer>(obs)))) + : observer(std::forward<Observer>(obs)) {} + + void afterFreeExtBuffer() const noexcept final { + observer.afterFreeExtBuffer(); + } + + void afterReleaseExtBuffer() const noexcept final { + observer.afterReleaseExtBuffer(); + } + }; + + struct SharedInfo { + SharedInfo(); + SharedInfo(FreeFunction fn, void* arg, bool hfs = false); + + static void releaseStorage(SharedInfo* info) noexcept; + + using ObserverCb = folly::FunctionRef<void(SharedInfoObserverEntryBase&)>; + static void invokeAndDeleteEachObserver( + SharedInfoObserverEntryBase* observerListHead, + ObserverCb cb) noexcept; + + // A pointer to a function to call to free the buffer when the refcount + // hits 0. If this is null, free() will be used instead. + FreeFunction freeFn; + void* userData; + SharedInfoObserverEntryBase* observerListHead{nullptr}; + std::atomic<uint32_t> refcount; + bool externallyShared{false}; + bool useHeapFullStorage{false}; + MicroSpinLock observerListLock{0}; + }; + // Helper structs for use by operator new and delete + struct HeapPrefix; + struct HeapStorage; + struct HeapFullStorage; + + /** + * Create a new IOBuf pointing to an external buffer. + * + * The caller is responsible for holding a reference count for this new + * IOBuf. The IOBuf constructor does not automatically increment the + * reference count. + */ + struct InternalConstructor {}; // avoid conflicts + IOBuf( + InternalConstructor, + uintptr_t flagsAndSharedInfo, + uint8_t* buf, + std::size_t capacity, + uint8_t* data, + std::size_t length) noexcept; + + void unshareOneSlow(); + void unshareChained(); + void makeManagedChained(); + void coalesceSlow(); + void coalesceSlow(size_t maxLength); + // newLength must be the entire length of the buffers between this and + // end (no truncation) + void coalesceAndReallocate( + size_t newHeadroom, + size_t newLength, + IOBuf* end, + size_t newTailroom); + void coalesceAndReallocate(size_t newLength, IOBuf* end) { + coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom()); + } + void decrementRefcount() noexcept; + void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom); + void freeExtBuffer() noexcept; + + static size_t goodExtBufferSize(std::size_t minCapacity); + static void initExtBuffer( + uint8_t* buf, + size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void allocExtBuffer( + std::size_t minCapacity, + uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void releaseStorage(HeapStorage* storage, uint16_t freeFlags) noexcept; + static void freeInternalBuf(void* buf, void* userData) noexcept; + + /* + * Member variables + */ + + /* + * Links to the next and the previous IOBuf in this chain. + * + * The chain is circularly linked (the last element in the chain points back + * at the head), and next_ and prev_ can never be null. If this IOBuf is the + * only element in the chain, next_ and prev_ will both point to this. + */ + IOBuf* next_{this}; + IOBuf* prev_{this}; + + /* + * A pointer to the start of the data referenced by this IOBuf, and the + * length of the data. + * + * This may refer to any subsection of the actual buffer capacity. + */ + uint8_t* data_{nullptr}; + uint8_t* buf_{nullptr}; + std::size_t length_{0}; + std::size_t capacity_{0}; + + // Pack flags in least significant 2 bits, sharedInfo in the rest + uintptr_t flagsAndSharedInfo_{0}; + + static inline uintptr_t packFlagsAndSharedInfo( + uintptr_t flags, + SharedInfo* info) noexcept { + uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); + DCHECK_EQ(flags & ~kFlagMask, 0u); + DCHECK_EQ(uinfo & kFlagMask, 0u); + return flags | uinfo; + } + + inline SharedInfo* sharedInfo() const noexcept { + return reinterpret_cast<SharedInfo*>(flagsAndSharedInfo_ & ~kFlagMask); + } + + inline void setSharedInfo(SharedInfo* info) noexcept { + uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); + DCHECK_EQ(uinfo & kFlagMask, 0u); + flagsAndSharedInfo_ = (flagsAndSharedInfo_ & kFlagMask) | uinfo; + } + + inline uintptr_t flags() const noexcept { + return flagsAndSharedInfo_ & kFlagMask; + } + + // flags_ are changed from const methods + inline void setFlags(uintptr_t flags) noexcept { + DCHECK_EQ(flags & ~kFlagMask, 0u); + flagsAndSharedInfo_ |= flags; + } + + inline void clearFlags(uintptr_t flags) noexcept { + DCHECK_EQ(flags & ~kFlagMask, 0u); + flagsAndSharedInfo_ &= ~flags; + } + + inline void setFlagsAndSharedInfo( + uintptr_t flags, + SharedInfo* info) noexcept { + flagsAndSharedInfo_ = packFlagsAndSharedInfo(flags, info); + } + + struct DeleterBase { + virtual ~DeleterBase() {} + virtual void dispose(void* p) noexcept = 0; + }; + + template <class UniquePtr> + struct UniquePtrDeleter : public DeleterBase { + typedef typename UniquePtr::pointer Pointer; + typedef typename UniquePtr::deleter_type Deleter; + + explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {} + void dispose(void* p) noexcept override { + deleter_(static_cast<Pointer>(p)); + delete this; + } + + private: + Deleter deleter_; + }; + + static void freeUniquePtrBuffer(void* ptr, void* userData) noexcept { + static_cast<DeleterBase*>(userData)->dispose(ptr); + } +}; + +/** + * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2. + */ +struct IOBufHash { + size_t operator()(const IOBuf& buf) const noexcept; + size_t operator()(const std::unique_ptr<IOBuf>& buf) const noexcept { + return operator()(buf.get()); + } + size_t operator()(const IOBuf* buf) const noexcept { + return buf ? (*this)(*buf) : 0; + } +}; + +/** + * Ordering for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufCompare { + ordering operator()(const IOBuf& a, const IOBuf& b) const { + return &a == &b ? ordering::eq : impl(a, b); + } + ordering operator()( + const std::unique_ptr<IOBuf>& a, + const std::unique_ptr<IOBuf>& b) const { + return operator()(a.get(), b.get()); + } + ordering operator()(const IOBuf* a, const IOBuf* b) const { + // clang-format off + return + !a && !b ? ordering::eq : + !a && b ? ordering::lt : + a && !b ? ordering::gt : + operator()(*a, *b); + // clang-format on + } + + private: + ordering impl(IOBuf const& a, IOBuf const& b) const noexcept; +}; + +/** + * Equality predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufEqualTo : compare_equal_to<IOBufCompare> {}; + +/** + * Inequality predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufNotEqualTo : compare_not_equal_to<IOBufCompare> {}; + +/** + * Less predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufLess : compare_less<IOBufCompare> {}; + +/** + * At-most predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufLessEqual : compare_less_equal<IOBufCompare> {}; + +/** + * Greater predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufGreater : compare_greater<IOBufCompare> {}; + +/** + * At-least predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufGreaterEqual : compare_greater_equal<IOBufCompare> {}; + +template <class UniquePtr> +typename std::enable_if< + detail::IsUniquePtrToSL<UniquePtr>::value, + std::unique_ptr<IOBuf>>::type +IOBuf::takeOwnership(UniquePtr&& buf, size_t count) { + size_t size = count * sizeof(typename UniquePtr::element_type); + auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter()); + return takeOwnership( + buf.release(), size, &IOBuf::freeUniquePtrBuffer, deleter); +} + +inline std::unique_ptr<IOBuf> IOBuf::copyBuffer( + const void* data, + std::size_t size, + std::size_t headroom, + std::size_t minTailroom) { + std::size_t capacity = headroom + size + minTailroom; + std::unique_ptr<IOBuf> buf = create(capacity); + buf->advance(headroom); + if (size != 0) { + memcpy(buf->writableData(), data, size); + } + buf->append(size); + return buf; +} + +inline std::unique_ptr<IOBuf> IOBuf::copyBuffer( + const std::string& buf, + std::size_t headroom, + std::size_t minTailroom) { + return copyBuffer(buf.data(), buf.size(), headroom, minTailroom); +} + +inline std::unique_ptr<IOBuf> IOBuf::maybeCopyBuffer( + const std::string& buf, + std::size_t headroom, + std::size_t minTailroom) { + if (buf.empty()) { + return nullptr; + } + return copyBuffer(buf.data(), buf.size(), headroom, minTailroom); +} + +class IOBuf::Iterator : public detail::IteratorFacade< + IOBuf::Iterator, + ByteRange const, + std::forward_iterator_tag> { + public: + // Note that IOBufs are stored as a circular list without a guard node, + // so pos == end is ambiguous (it may mean "begin" or "end"). To solve + // the ambiguity (at the cost of one extra comparison in the "increment" + // code path), we define end iterators as having pos_ == end_ == nullptr + // and we only allow forward iteration. + explicit Iterator(const IOBuf* pos, const IOBuf* end) : pos_(pos), end_(end) { + // Sadly, we must return by const reference, not by value. + if (pos_) { + setVal(); + } + } + + Iterator() {} + + Iterator(Iterator const& rhs) : Iterator(rhs.pos_, rhs.end_) {} + + Iterator& operator=(Iterator const& rhs) { + pos_ = rhs.pos_; + end_ = rhs.end_; + if (pos_) { + setVal(); + } + return *this; + } + + const ByteRange& dereference() const { + return val_; + } + + bool equal(const Iterator& other) const { + // We must compare end_ in addition to pos_, because forward traversal + // requires that if two iterators are equal (a == b) and dereferenceable, + // then ++a == ++b. + return pos_ == other.pos_ && end_ == other.end_; + } + + void increment() { + pos_ = pos_->next(); + adjustForEnd(); + } + + private: + void setVal() { + val_ = ByteRange(pos_->data(), pos_->tail()); + } + + void adjustForEnd() { + if (pos_ == end_) { + pos_ = end_ = nullptr; + val_ = ByteRange(); + } else { + setVal(); + } + } + + const IOBuf* pos_{nullptr}; + const IOBuf* end_{nullptr}; + ByteRange val_; +}; + +inline IOBuf::Iterator IOBuf::begin() const { + return cbegin(); +} +inline IOBuf::Iterator IOBuf::end() const { + return cend(); +} + +} // namespace folly + +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.cpp b/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.cpp new file mode 100644 index 000000000..76ecfb2b3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/IOBufQueue.h> + +#include <cstring> + +#include <stdexcept> + +using std::make_pair; +using std::pair; +using std::unique_ptr; + +namespace { + +using folly::IOBuf; + +const size_t MIN_ALLOC_SIZE = 2000; +const size_t MAX_ALLOC_SIZE = 8000; + +/** + * Convenience function to append chain src to chain dst. + */ +void appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) { + if (dst == nullptr) { + dst = std::move(src); + } else { + IOBuf* tail = dst->prev(); + if (pack) { + // Copy up to kMaxPackCopy bytes if we can free buffers; this helps + // reduce wastage (the tail's tailroom and the head's headroom) when + // joining two IOBufQueues together. + size_t copyRemaining = folly::IOBufQueue::kMaxPackCopy; + std::size_t n; + while (src && (n = src->length()) <= copyRemaining && + n <= tail->tailroom()) { + if (n > 0) { + memcpy(tail->writableTail(), src->data(), n); + tail->append(n); + copyRemaining -= n; + } + src = src->pop(); + } + } + if (src) { + tail->appendChain(std::move(src)); + } + } +} + +} // namespace + +namespace folly { + +IOBufQueue::IOBufQueue(const Options& options) + : options_(options), cachePtr_(&localCache_) { + localCache_.attached = true; +} + +IOBufQueue::~IOBufQueue() { + clearWritableRangeCache(); +} + +IOBufQueue::IOBufQueue(IOBufQueue&& other) noexcept + : options_(other.options_), cachePtr_(&localCache_) { + other.clearWritableRangeCache(); + head_ = std::move(other.head_); + chainLength_ = other.chainLength_; + + tailStart_ = other.tailStart_; + localCache_.cachedRange = other.localCache_.cachedRange; + localCache_.attached = true; + + other.chainLength_ = 0; + other.tailStart_ = nullptr; + other.localCache_.cachedRange = {nullptr, nullptr}; +} + +IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) { + if (&other != this) { + other.clearWritableRangeCache(); + clearWritableRangeCache(); + + options_ = other.options_; + head_ = std::move(other.head_); + chainLength_ = other.chainLength_; + + tailStart_ = other.tailStart_; + localCache_.cachedRange = other.localCache_.cachedRange; + localCache_.attached = true; + + other.chainLength_ = 0; + other.tailStart_ = nullptr; + other.localCache_.cachedRange = {nullptr, nullptr}; + } + return *this; +} + +std::pair<void*, std::size_t> IOBufQueue::headroom() { + // Note, headroom is independent from the tail, so we don't need to flush the + // cache. + if (head_) { + return std::make_pair(head_->writableBuffer(), head_->headroom()); + } else { + return std::make_pair(nullptr, 0); + } +} + +void IOBufQueue::markPrepended(std::size_t n) { + if (n == 0) { + return; + } + // Note, headroom is independent from the tail, so we don't need to flush the + // cache. + assert(head_); + head_->prepend(n); + chainLength_ += n; +} + +void IOBufQueue::prepend(const void* buf, std::size_t n) { + // We're not touching the tail, so we don't need to flush the cache. + auto hroom = head_->headroom(); + if (!head_ || hroom < n) { + throw std::overflow_error("Not enough room to prepend"); + } + memcpy(head_->writableBuffer() + hroom - n, buf, n); + head_->prepend(n); + chainLength_ += n; +} + +void IOBufQueue::append(unique_ptr<IOBuf>&& buf, bool pack) { + if (!buf) { + return; + } + auto guard = updateGuard(); + if (options_.cacheChainLength) { + chainLength_ += buf->computeChainDataLength(); + } + appendToChain(head_, std::move(buf), pack); +} + +void IOBufQueue::append(const folly::IOBuf& buf, bool pack) { + if (!head_ || !pack) { + append(buf.clone(), pack); + return; + } + + auto guard = updateGuard(); + if (options_.cacheChainLength) { + chainLength_ += buf.computeChainDataLength(); + } + + size_t copyRemaining = kMaxPackCopy; + std::size_t n; + const folly::IOBuf* src = &buf; + folly::IOBuf* tail = head_->prev(); + while ((n = src->length()) <= copyRemaining && n <= tail->tailroom()) { + if (n > 0) { + memcpy(tail->writableTail(), src->data(), n); + tail->append(n); + copyRemaining -= n; + } + src = src->next(); + + // Consumed full input. + if (src == &buf) { + return; + } + } + + // Clone the rest. + do { + head_->prependChain(src->cloneOne()); + src = src->next(); + } while (src != &buf); +} + +void IOBufQueue::append(IOBufQueue& other, bool pack) { + if (!other.head_) { + return; + } + // We're going to chain other, thus we need to grab both guards. + auto otherGuard = other.updateGuard(); + auto guard = updateGuard(); + if (options_.cacheChainLength) { + if (other.options_.cacheChainLength) { + chainLength_ += other.chainLength_; + } else { + chainLength_ += other.head_->computeChainDataLength(); + } + } + appendToChain(head_, std::move(other.head_), pack); + other.chainLength_ = 0; +} + +void IOBufQueue::append(const void* buf, size_t len) { + auto guard = updateGuard(); + auto src = static_cast<const uint8_t*>(buf); + while (len != 0) { + if ((head_ == nullptr) || head_->prev()->isSharedOne() || + (head_->prev()->tailroom() == 0)) { + appendToChain( + head_, + IOBuf::create( + std::max(MIN_ALLOC_SIZE, std::min(len, MAX_ALLOC_SIZE))), + false); + } + IOBuf* last = head_->prev(); + std::size_t copyLen = std::min(len, (size_t)last->tailroom()); + memcpy(last->writableTail(), src, copyLen); + src += copyLen; + last->append(copyLen); + chainLength_ += copyLen; + len -= copyLen; + } +} + +void IOBufQueue::wrapBuffer( + const void* buf, + size_t len, + std::size_t blockSize) { + auto src = static_cast<const uint8_t*>(buf); + while (len != 0) { + size_t n = std::min(len, size_t(blockSize)); + append(IOBuf::wrapBuffer(src, n)); + src += n; + len -= n; + } +} + +pair<void*, std::size_t> IOBufQueue::preallocateSlow( + std::size_t min, + std::size_t newAllocationSize, + std::size_t max) { + // Avoid grabbing update guard, since we're manually setting the cache ptrs. + flushCache(); + // Allocate a new buffer of the requested max size. + unique_ptr<IOBuf> newBuf(IOBuf::create(std::max(min, newAllocationSize))); + + tailStart_ = newBuf->writableTail(); + cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>( + tailStart_, tailStart_ + newBuf->tailroom()); + appendToChain(head_, std::move(newBuf), false); + return make_pair(writableTail(), std::min<std::size_t>(max, tailroom())); +} + +unique_ptr<IOBuf> IOBufQueue::split(size_t n, bool throwOnUnderflow) { + auto guard = updateGuard(); + unique_ptr<IOBuf> result; + while (n != 0) { + if (head_ == nullptr) { + if (throwOnUnderflow) { + throw std::underflow_error( + "Attempt to remove more bytes than are present in IOBufQueue"); + } else { + break; + } + } else if (head_->length() <= n) { + n -= head_->length(); + chainLength_ -= head_->length(); + unique_ptr<IOBuf> remainder = head_->pop(); + appendToChain(result, std::move(head_), false); + head_ = std::move(remainder); + } else { + unique_ptr<IOBuf> clone = head_->cloneOne(); + clone->trimEnd(clone->length() - n); + appendToChain(result, std::move(clone), false); + head_->trimStart(n); + chainLength_ -= n; + break; + } + } + if (UNLIKELY(result == nullptr)) { + return IOBuf::create(0); + } + return result; +} + +void IOBufQueue::trimStart(size_t amount) { + auto trimmed = trimStartAtMost(amount); + if (trimmed != amount) { + throw std::underflow_error( + "Attempt to trim more bytes than are present in IOBufQueue"); + } +} + +size_t IOBufQueue::trimStartAtMost(size_t amount) { + auto guard = updateGuard(); + auto original = amount; + while (amount > 0) { + if (!head_) { + break; + } + if (head_->length() > amount) { + head_->trimStart(amount); + chainLength_ -= amount; + amount = 0; + break; + } + amount -= head_->length(); + chainLength_ -= head_->length(); + head_ = head_->pop(); + } + return original - amount; +} + +void IOBufQueue::trimEnd(size_t amount) { + auto trimmed = trimEndAtMost(amount); + if (trimmed != amount) { + throw std::underflow_error( + "Attempt to trim more bytes than are present in IOBufQueue"); + } +} + +size_t IOBufQueue::trimEndAtMost(size_t amount) { + auto guard = updateGuard(); + auto original = amount; + while (amount > 0) { + if (!head_) { + break; + } + if (head_->prev()->length() > amount) { + head_->prev()->trimEnd(amount); + chainLength_ -= amount; + amount = 0; + break; + } + amount -= head_->prev()->length(); + chainLength_ -= head_->prev()->length(); + + if (head_->isChained()) { + head_->prev()->unlink(); + } else { + head_.reset(); + } + } + return original - amount; +} + +std::unique_ptr<folly::IOBuf> IOBufQueue::pop_front() { + auto guard = updateGuard(); + if (!head_) { + return nullptr; + } + chainLength_ -= head_->length(); + std::unique_ptr<folly::IOBuf> retBuf = std::move(head_); + head_ = retBuf->pop(); + return retBuf; +} + +void IOBufQueue::clear() { + if (!head_) { + return; + } + auto guard = updateGuard(); + IOBuf* buf = head_.get(); + do { + buf->clear(); + buf = buf->next(); + } while (buf != head_.get()); + chainLength_ = 0; +} + +void IOBufQueue::appendToString(std::string& out) const { + if (!head_) { + return; + } + auto len = options_.cacheChainLength + ? chainLength_ + (cachePtr_->cachedRange.first - tailStart_) + : head_->computeChainDataLength() + + (cachePtr_->cachedRange.first - tailStart_); + out.reserve(out.size() + len); + + for (auto range : *head_) { + out.append(reinterpret_cast<const char*>(range.data()), range.size()); + } + + if (tailStart_ != cachePtr_->cachedRange.first) { + out.append( + reinterpret_cast<const char*>(tailStart_), + cachePtr_->cachedRange.first - tailStart_); + } +} + +void IOBufQueue::gather(std::size_t maxLength) { + auto guard = updateGuard(); + if (head_ != nullptr) { + head_->gather(maxLength); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.h b/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.h new file mode 100644 index 000000000..f057e88db --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/IOBufQueue.h @@ -0,0 +1,656 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/ScopeGuard.h> +#include <folly/io/IOBuf.h> + +#include <stdexcept> +#include <string> + +namespace folly { + +/** + * An IOBufQueue encapsulates a chain of IOBufs and provides + * convenience functions to append data to the back of the chain + * and remove data from the front. + * + * You may also prepend data into the headroom of the first buffer in the + * chain, if any. + */ +class IOBufQueue { + private: + /** + * This guard should be taken by any method that intends to do any changes + * to in data_ (e.g. appending to it). + * + * It flushes the writable tail cache and refills it on destruction. + */ + auto updateGuard() { + flushCache(); + return folly::makeGuard([this] { updateWritableTailCache(); }); + } + + struct WritableRangeCacheData { + std::pair<uint8_t*, uint8_t*> cachedRange; + bool attached{false}; + + WritableRangeCacheData() = default; + + WritableRangeCacheData(WritableRangeCacheData&& other) + : cachedRange(other.cachedRange), attached(other.attached) { + other.cachedRange = {nullptr, nullptr}; + other.attached = false; + } + WritableRangeCacheData& operator=(WritableRangeCacheData&& other) { + cachedRange = other.cachedRange; + attached = other.attached; + + other.cachedRange = {nullptr, nullptr}; + other.attached = false; + + return *this; + } + + WritableRangeCacheData(const WritableRangeCacheData&) = delete; + WritableRangeCacheData& operator=(const WritableRangeCacheData&) = delete; + }; + + public: + struct Options { + Options() : cacheChainLength(false) {} + bool cacheChainLength; + }; + + /** + * Commonly used Options, currently the only possible value other than + * the default. + */ + static Options cacheChainLength() { + Options options; + options.cacheChainLength = true; + return options; + } + + /** + * WritableRangeCache represents a cache of current writable tail and provides + * cheap and simple interface to append to it that avoids paying the cost of + * preallocate/postallocate pair (i.e. indirections and checks). + * + * The cache is flushed on destruction/copy/move and on non-const accesses to + * the underlying IOBufQueue. + * + * Note: there can be only one active cache for a given IOBufQueue, i.e. when + * you fill a cache object it automatically invalidates other + * cache (if any). + */ + class WritableRangeCache { + public: + explicit WritableRangeCache(folly::IOBufQueue* q = nullptr) : queue_(q) { + if (queue_) { + fillCache(); + } + } + + /** + * Move constructor/assignment can move the cached range, but must update + * the reference in IOBufQueue. + */ + WritableRangeCache(WritableRangeCache&& other) + : data_(std::move(other.data_)), queue_(other.queue_) { + if (data_.attached) { + queue_->updateCacheRef(data_); + } + } + WritableRangeCache& operator=(WritableRangeCache&& other) { + if (data_.attached) { + queue_->clearWritableRangeCache(); + } + + data_ = std::move(other.data_); + queue_ = other.queue_; + + if (data_.attached) { + queue_->updateCacheRef(data_); + } + + return *this; + } + + /** + * Copy constructor/assignment cannot copy the cached range. + */ + WritableRangeCache(const WritableRangeCache& other) + : queue_(other.queue_) {} + WritableRangeCache& operator=(const WritableRangeCache& other) { + if (data_.attached) { + queue_->clearWritableRangeCache(); + } + + queue_ = other.queue_; + + return *this; + } + + ~WritableRangeCache() { + if (data_.attached) { + queue_->clearWritableRangeCache(); + } + } + + /** + * Reset the underlying IOBufQueue, will flush current cache if present. + */ + void reset(IOBufQueue* q) { + if (data_.attached) { + queue_->clearWritableRangeCache(); + } + + queue_ = q; + + if (queue_) { + fillCache(); + } + } + + /** + * Get a pointer to the underlying IOBufQueue object. + */ + IOBufQueue* queue() { + return queue_; + } + + /** + * Return a pointer to the start of cached writable tail. + * + * Note: doesn't populate cache. + */ + uint8_t* writableData() { + dcheckIntegrity(); + return data_.cachedRange.first; + } + + /** + * Return a length of cached writable tail. + * + * Note: doesn't populate cache. + */ + size_t length() { + dcheckIntegrity(); + return data_.cachedRange.second - data_.cachedRange.first; + } + + /** + * Mark n bytes as occupied (e.g. postallocate). + */ + void append(size_t n) { + dcheckIntegrity(); + // This can happen only if somebody is misusing the interface. + // E.g. calling append after touching IOBufQueue or without checking + // the length(). + if (LIKELY(data_.cachedRange.first != nullptr)) { + DCHECK_LE(n, length()); + data_.cachedRange.first += n; + } else { + appendSlow(n); + } + } + + /** + * Same as append(n), but avoids checking if there is a cache. + * The caller must guarantee that the cache is set (e.g. the caller just + * called fillCache or checked that it's not empty). + */ + void appendUnsafe(size_t n) { + data_.cachedRange.first += n; + } + + /** + * Fill the cache of writable tail from the underlying IOBufQueue. + */ + void fillCache() { + queue_->fillWritableRangeCache(data_); + } + + private: + WritableRangeCacheData data_; + IOBufQueue* queue_; + + FOLLY_NOINLINE void appendSlow(size_t n) { + queue_->postallocate(n); + } + + void dcheckIntegrity() { + // Tail start should always be less than tail end. + DCHECK_LE( + (void*)data_.cachedRange.first, (void*)data_.cachedRange.second); + DCHECK( + data_.cachedRange.first != nullptr || + data_.cachedRange.second == nullptr); + + // Cached range should be always empty if the cache is not attached. + DCHECK( + data_.attached || + (data_.cachedRange.first == nullptr && + data_.cachedRange.second == nullptr)); + + // We cannot be in attached state if the queue_ is not set. + DCHECK(queue_ != nullptr || !data_.attached); + + // If we're attached and the cache is not empty, then it should coincide + // with the tail buffer. + DCHECK( + !data_.attached || data_.cachedRange.first == nullptr || + (queue_->head_ != nullptr && + data_.cachedRange.first >= queue_->head_->prev()->writableTail() && + data_.cachedRange.second == + queue_->head_->prev()->writableTail() + + queue_->head_->prev()->tailroom())); + } + }; + + explicit IOBufQueue(const Options& options = Options()); + ~IOBufQueue(); + + /** + * Return a space to prepend bytes and the amount of headroom available. + */ + std::pair<void*, std::size_t> headroom(); + + /** + * Indicate that n bytes from the headroom have been used. + */ + void markPrepended(std::size_t n); + + /** + * Prepend an existing range; throws std::overflow_error if not enough + * room. + */ + void prepend(const void* buf, std::size_t n); + + /** + * Add a buffer or buffer chain to the end of this queue. The + * queue takes ownership of buf. + * + * If pack is true, we try to reduce wastage at the end of this queue + * by copying some data from the first buffers in the buf chain (and + * releasing the buffers), if possible. If pack is false, we leave + * the chain topology unchanged. + */ + void append(std::unique_ptr<folly::IOBuf>&& buf, bool pack = false); + void append(const folly::IOBuf& buf, bool pack = false); + + /** + * Add a queue to the end of this queue. The queue takes ownership of + * all buffers from the other queue. + */ + void append(IOBufQueue& other, bool pack = false); + void append(IOBufQueue&& other, bool pack = false) { + append(other, pack); // call lvalue reference overload, above + } + + /** + * Copy len bytes, starting at buf, to the end of this queue. + * The caller retains ownership of the source data. + */ + void append(const void* buf, size_t len); + + /** + * Copy a string to the end of this queue. + * The caller retains ownership of the source data. + */ + void append(StringPiece sp) { + append(sp.data(), sp.size()); + } + + /** + * Append a chain of IOBuf objects that point to consecutive regions + * within buf. + * + * Just like IOBuf::wrapBuffer, this should only be used when the caller + * knows ahead of time and can ensure that all IOBuf objects that will point + * to this buffer will be destroyed before the buffer itself is destroyed; + * all other caveats from wrapBuffer also apply. + * + * Every buffer except for the last will wrap exactly blockSize bytes. + * Importantly, this method may be used to wrap buffers larger than 4GB. + */ + void wrapBuffer( + const void* buf, + size_t len, + std::size_t blockSize = (1U << 31)); // default block size: 2GB + + /** + * Obtain a writable block of contiguous bytes at the end of this + * queue, allocating more space if necessary. The amount of space + * reserved will be at least min. If min contiguous space is not + * available at the end of the queue, and IOBuf with size newAllocationSize + * is appended to the chain and returned. The actual available space + * may be larger than newAllocationSize, but will be truncated to max, + * if specified. + * + * If the caller subsequently writes anything into the returned space, + * it must call the postallocate() method. + * + * @return The starting address of the block and the length in bytes. + * + * @note The point of the preallocate()/postallocate() mechanism is + * to support I/O APIs such as Thrift's TAsyncSocket::ReadCallback + * that request a buffer from the application and then, in a later + * callback, tell the application how much of the buffer they've + * filled with data. + */ + std::pair<void*, std::size_t> preallocate( + std::size_t min, + std::size_t newAllocationSize, + std::size_t max = std::numeric_limits<std::size_t>::max()) { + dcheckCacheIntegrity(); + + if (LIKELY(writableTail() != nullptr && tailroom() >= min)) { + return std::make_pair( + writableTail(), std::min<std::size_t>(max, tailroom())); + } + + return preallocateSlow(min, newAllocationSize, max); + } + + /** + * Tell the queue that the caller has written data into the first n + * bytes provided by the previous preallocate() call. + * + * @note n should be less than or equal to the size returned by + * preallocate(). If n is zero, the caller may skip the call + * to postallocate(). If n is nonzero, the caller must not + * invoke any other non-const methods on this IOBufQueue between + * the call to preallocate and the call to postallocate(). + */ + void postallocate(std::size_t n) { + dcheckCacheIntegrity(); + DCHECK_LE( + (void*)(cachePtr_->cachedRange.first + n), + (void*)cachePtr_->cachedRange.second); + cachePtr_->cachedRange.first += n; + } + + /** + * Obtain a writable block of n contiguous bytes, allocating more space + * if necessary, and mark it as used. The caller can fill it later. + */ + void* allocate(std::size_t n) { + void* p = preallocate(n, n).first; + postallocate(n); + return p; + } + + void* writableTail() const { + dcheckCacheIntegrity(); + return cachePtr_->cachedRange.first; + } + + size_t tailroom() const { + dcheckCacheIntegrity(); + return cachePtr_->cachedRange.second - cachePtr_->cachedRange.first; + } + + /** + * Split off the first n bytes of the queue into a separate IOBuf chain, + * and transfer ownership of the new chain to the caller. The IOBufQueue + * retains ownership of everything after the split point. + * + * @warning If the split point lies in the middle of some IOBuf within + * the chain, this function may, as an implementation detail, + * clone that IOBuf. + * + * @throws std::underflow_error if n exceeds the number of bytes + * in the queue. + */ + std::unique_ptr<folly::IOBuf> split(size_t n) { + return split(n, true); + } + + /** + * Similar to split, but will return the entire queue instead of throwing + * if n exceeds the number of bytes in the queue. + */ + std::unique_ptr<folly::IOBuf> splitAtMost(size_t n) { + return split(n, false); + } + + /** + * Similar to IOBuf::trimStart, but works on the whole queue. Will + * pop off buffers that have been completely trimmed. + */ + void trimStart(size_t amount); + + /** + * Similar to trimStart, but will trim at most amount bytes and returns + * the number of bytes trimmed. + */ + size_t trimStartAtMost(size_t amount); + + /** + * Similar to IOBuf::trimEnd, but works on the whole queue. Will + * pop off buffers that have been completely trimmed. + */ + void trimEnd(size_t amount); + + /** + * Similar to trimEnd, but will trim at most amount bytes and returns + * the number of bytes trimmed. + */ + size_t trimEndAtMost(size_t amount); + + /** + * Transfer ownership of the queue's entire IOBuf chain to the caller. + */ + std::unique_ptr<folly::IOBuf> move() { + auto guard = updateGuard(); + std::unique_ptr<folly::IOBuf> res = std::move(head_); + chainLength_ = 0; + return res; + } + + /** + * Access the front IOBuf. + * + * Note: caller will see the current state of the chain, but may not see + * future updates immediately, due to the presence of a tail cache. + * Note: the caller may potentially clone the chain, thus marking all buffers + * as shared. We may still continue writing to the tail of the last + * IOBuf without checking if it's shared, but this is fine, since the + * cloned IOBufs won't reference that data. + */ + const folly::IOBuf* front() const { + flushCache(); + return head_.get(); + } + + /** + * returns the first IOBuf in the chain and removes it from the chain + * + * @return first IOBuf in the chain or nullptr if none. + */ + std::unique_ptr<folly::IOBuf> pop_front(); + + /** + * Total chain length, only valid if cacheLength was specified in the + * constructor. + */ + size_t chainLength() const { + if (UNLIKELY(!options_.cacheChainLength)) { + throw std::invalid_argument("IOBufQueue: chain length not cached"); + } + dcheckCacheIntegrity(); + return chainLength_ + (cachePtr_->cachedRange.first - tailStart_); + } + + /** + * Returns true iff the IOBuf chain length is 0. + */ + bool empty() const { + dcheckCacheIntegrity(); + return !head_ || + (head_->empty() && cachePtr_->cachedRange.first == tailStart_); + } + + const Options& options() const { + return options_; + } + + /** + * Clear the queue. Note that this does not release the buffers, it + * just sets their length to zero; useful if you want to reuse the + * same queue without reallocating. + */ + void clear(); + + /** + * Append the queue to a std::string. Non-destructive. + */ + void appendToString(std::string& out) const; + + /** + * Calls IOBuf::gather() on the head of the queue, if it exists. + */ + void gather(std::size_t maxLength); + + /** Movable */ + IOBufQueue(IOBufQueue&&) noexcept; + IOBufQueue& operator=(IOBufQueue&&); + + static constexpr size_t kMaxPackCopy = 4096; + + private: + std::unique_ptr<folly::IOBuf> split(size_t n, bool throwOnUnderflow); + + static const size_t kChainLengthNotCached = (size_t)-1; + /** Not copyable */ + IOBufQueue(const IOBufQueue&) = delete; + IOBufQueue& operator=(const IOBufQueue&) = delete; + + Options options_; + + // NOTE that chainLength_ is still updated even if !options_.cacheChainLength + // because doing it unchecked in postallocate() is faster (no (mis)predicted + // branch) + mutable size_t chainLength_{0}; + /** + * Everything that has been appended but not yet discarded or moved out + * Note: anything that needs to operate on a tail should either call + * flushCache() or grab updateGuard() (it will flush the cache itself). + */ + std::unique_ptr<folly::IOBuf> head_; + + mutable uint8_t* tailStart_{nullptr}; + WritableRangeCacheData* cachePtr_{nullptr}; + WritableRangeCacheData localCache_; + + void dcheckCacheIntegrity() const { + // Tail start should always be less than tail end. + DCHECK_LE((void*)tailStart_, (void*)cachePtr_->cachedRange.first); + DCHECK_LE( + (void*)cachePtr_->cachedRange.first, + (void*)cachePtr_->cachedRange.second); + DCHECK( + cachePtr_->cachedRange.first != nullptr || + cachePtr_->cachedRange.second == nullptr); + + // There is always an attached cache instance. + DCHECK(cachePtr_->attached); + + // Either cache is empty or it coincides with the tail. + DCHECK( + cachePtr_->cachedRange.first == nullptr || + (head_ != nullptr && tailStart_ == head_->prev()->writableTail() && + tailStart_ <= cachePtr_->cachedRange.first && + cachePtr_->cachedRange.first >= head_->prev()->writableTail() && + cachePtr_->cachedRange.second == + head_->prev()->writableTail() + head_->prev()->tailroom())); + } + + /** + * Populate dest with writable tail range cache. + */ + void fillWritableRangeCache(WritableRangeCacheData& dest) { + dcheckCacheIntegrity(); + if (cachePtr_ != &dest) { + dest = std::move(*cachePtr_); + cachePtr_ = &dest; + } + } + + /** + * Clear current writable tail cache and reset it to localCache_ + */ + void clearWritableRangeCache() { + flushCache(); + + if (cachePtr_ != &localCache_) { + localCache_ = std::move(*cachePtr_); + cachePtr_ = &localCache_; + } + + DCHECK(cachePtr_ == &localCache_ && localCache_.attached); + } + + /** + * Commit any pending changes to the tail of the queue. + */ + void flushCache() const { + dcheckCacheIntegrity(); + + if (tailStart_ != cachePtr_->cachedRange.first) { + auto buf = head_->prev(); + DCHECK_EQ( + (void*)(buf->writableTail() + buf->tailroom()), + (void*)cachePtr_->cachedRange.second); + auto len = cachePtr_->cachedRange.first - tailStart_; + buf->append(len); + chainLength_ += len; + tailStart_ += len; + } + } + + // For WritableRangeCache move assignment/construction. + void updateCacheRef(WritableRangeCacheData& newRef) { + cachePtr_ = &newRef; + } + + /** + * Update cached writable tail range. Called by updateGuard() + */ + void updateWritableTailCache() { + if (LIKELY(head_ != nullptr)) { + IOBuf* buf = head_->prev(); + if (LIKELY(!buf->isSharedOne())) { + tailStart_ = buf->writableTail(); + cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>( + tailStart_, tailStart_ + buf->tailroom()); + return; + } + } + tailStart_ = nullptr; + cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>(); + } + + std::pair<void*, std::size_t> preallocateSlow( + std::size_t min, + std::size_t newAllocationSize, + std::size_t max); +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/RecordIO-inl.h b/ios/Pods/Flipper-Folly/folly/io/RecordIO-inl.h new file mode 100644 index 000000000..c88ce06fb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/RecordIO-inl.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_IO_RECORDIO_H_ +#error This file may only be included from folly/io/RecordIO.h +#endif + +#include <folly/detail/Iterators.h> +#include <folly/hash/SpookyHashV2.h> + +namespace folly { + +class RecordIOReader::Iterator : public detail::IteratorFacade< + RecordIOReader::Iterator, + const std::pair<ByteRange, off_t>, + std::forward_iterator_tag> { + friend class detail:: + IteratorFacade<Iterator, value_type, std::forward_iterator_tag>; + friend class RecordIOReader; + + public: + Iterator() = default; + + private: + Iterator(ByteRange range, uint32_t fileId, off_t pos); + + reference dereference() const { + return recordAndPos_; + } + bool equal(const Iterator& other) const { + return range_ == other.range_; + } + void increment() { + size_t skip = recordio_helpers::headerSize() + recordAndPos_.first.size(); + recordAndPos_.second += off_t(skip); + range_.advance(skip); + advanceToValid(); + } + + void advanceToValid(); + ByteRange range_; + uint32_t fileId_ = 0; + // stored as a pair so we can return by reference in dereference() + std::pair<ByteRange, off_t> recordAndPos_; +}; + +inline auto RecordIOReader::cbegin() const -> Iterator { + return seek(0); +} +inline auto RecordIOReader::begin() const -> Iterator { + return cbegin(); +} +inline auto RecordIOReader::cend() const -> Iterator { + return seek(off_t(-1)); +} +inline auto RecordIOReader::end() const -> Iterator { + return cend(); +} +inline auto RecordIOReader::seek(off_t pos) const -> Iterator { + return Iterator(map_.range(), fileId_, pos); +} + +namespace recordio_helpers { + +namespace recordio_detail { + +FOLLY_PACK_PUSH +struct Header { + // First 4 bytes of SHA1("zuck"), big-endian + // Any values will do, except that the sequence must not have a + // repeated prefix (that is, if we see kMagic, we know that the next + // occurrence must start at least 4 bytes later) + static constexpr uint32_t kMagic = 0xeac313a1; + uint32_t magic; + uint8_t version; // backwards incompatible version, currently 0 + uint8_t hashFunction; // 0 = SpookyHashV2 + uint16_t flags; // reserved (must be 0) + uint32_t fileId; // unique file ID + uint32_t dataLength; + std::size_t dataHash; + uint32_t headerHash; // must be last +} FOLLY_PACK_ATTR; +FOLLY_PACK_POP + +static_assert( + offsetof(Header, headerHash) + sizeof(Header::headerHash) == sizeof(Header), + "invalid header layout"); + +} // namespace recordio_detail + +constexpr size_t headerSize() { + return sizeof(recordio_detail::Header); +} + +inline RecordInfo findRecord(ByteRange range, uint32_t fileId) { + return findRecord(range, range, fileId); +} + +} // namespace recordio_helpers + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/RecordIO.cpp b/ios/Pods/Flipper-Folly/folly/io/RecordIO.cpp new file mode 100644 index 000000000..4ab84ea7a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/RecordIO.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/RecordIO.h> + +#include <sys/types.h> + +#include <folly/Exception.h> +#include <folly/FileUtil.h> +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/String.h> +#include <folly/portability/Unistd.h> + +namespace folly { + +using namespace recordio_helpers; + +RecordIOWriter::RecordIOWriter(File file, uint32_t fileId) + : file_(std::move(file)), + fileId_(fileId), + writeLock_(file_, std::defer_lock), + filePos_(0) { + if (!writeLock_.try_lock()) { + throw std::runtime_error("RecordIOWriter: file locked by another process"); + } + + struct stat st; + checkUnixError(fstat(file_.fd(), &st), "fstat() failed"); + + filePos_ = st.st_size; +} + +void RecordIOWriter::write(std::unique_ptr<IOBuf> buf) { + size_t totalLength = prependHeader(buf, fileId_); + if (totalLength == 0) { + return; // nothing to do + } + + DCHECK_EQ(buf->computeChainDataLength(), totalLength); + + // We're going to write. Reserve space for ourselves. + off_t pos = filePos_.fetch_add(off_t(totalLength)); + +#if FOLLY_HAVE_PWRITEV + auto iov = buf->getIov(); + ssize_t bytes = pwritevFull(file_.fd(), iov.data(), iov.size(), pos); +#else + buf->unshare(); + buf->coalesce(); + ssize_t bytes = pwriteFull(file_.fd(), buf->data(), buf->length(), pos); +#endif + + checkUnixError(bytes, "pwrite() failed"); + DCHECK_EQ(size_t(bytes), totalLength); +} + +RecordIOReader::RecordIOReader(File file, uint32_t fileId) + : map_(std::move(file)), fileId_(fileId) {} + +RecordIOReader::Iterator::Iterator(ByteRange range, uint32_t fileId, off_t pos) + : range_(range), fileId_(fileId), recordAndPos_(ByteRange(), 0) { + if (size_t(pos) >= range_.size()) { + // Note that this branch can execute if pos is negative as well. + recordAndPos_.second = off_t(-1); + range_.clear(); + } else { + recordAndPos_.second = pos; + range_.advance(size_t(pos)); + advanceToValid(); + } +} + +void RecordIOReader::Iterator::advanceToValid() { + ByteRange record = findRecord(range_, fileId_).record; + if (record.empty()) { + recordAndPos_ = std::make_pair(ByteRange(), off_t(-1)); + range_.clear(); // at end + } else { + auto skipped = size_t(record.begin() - range_.begin()); + DCHECK_GE(skipped, headerSize()); + skipped -= headerSize(); + range_.advance(skipped); + recordAndPos_.first = record; + recordAndPos_.second += off_t(skipped); + } +} + +namespace recordio_helpers { + +using recordio_detail::Header; + +namespace { + +constexpr uint32_t kHashSeed = 0xdeadbeef; // for mcurtiss + +uint32_t headerHash(const Header& header) { + return hash::SpookyHashV2::Hash32( + &header, offsetof(Header, headerHash), kHashSeed); +} + +std::pair<size_t, std::size_t> dataLengthAndHash(const IOBuf* buf) { + size_t len = 0; + hash::SpookyHashV2 hasher; + hasher.Init(kHashSeed, kHashSeed); + for (auto br : *buf) { + len += br.size(); + hasher.Update(br.data(), br.size()); + } + uint64_t hash1; + uint64_t hash2; + hasher.Final(&hash1, &hash2); + if (len + headerSize() >= std::numeric_limits<uint32_t>::max()) { + throw std::invalid_argument("Record length must fit in 32 bits"); + } + return std::make_pair(len, static_cast<std::size_t>(hash1)); +} + +std::size_t dataHash(ByteRange range) { + return hash::SpookyHashV2::Hash64(range.data(), range.size(), kHashSeed); +} + +} // namespace + +size_t prependHeader(std::unique_ptr<IOBuf>& buf, uint32_t fileId) { + if (fileId == 0) { + throw std::invalid_argument("invalid file id"); + } + auto lengthAndHash = dataLengthAndHash(buf.get()); + if (lengthAndHash.first == 0) { + return 0; // empty, nothing to do, no zero-length records + } + + // Prepend to the first buffer in the chain if we have room, otherwise + // prepend a new buffer. + if (buf->headroom() >= headerSize()) { + buf->unshareOne(); + buf->prepend(headerSize()); + } else { + auto b = IOBuf::create(headerSize()); + b->append(headerSize()); + b->appendChain(std::move(buf)); + buf = std::move(b); + } + auto header = reinterpret_cast<Header*>(buf->writableData()); + memset(header, 0, sizeof(Header)); + header->magic = Header::kMagic; + header->fileId = fileId; + header->dataLength = uint32_t(lengthAndHash.first); + header->dataHash = lengthAndHash.second; + header->headerHash = headerHash(*header); + + return lengthAndHash.first + headerSize(); +} + +bool validateRecordHeader(ByteRange range, uint32_t fileId) { + if (range.size() < headerSize()) { // records may not be empty + return false; + } + auto header = reinterpret_cast<const Header*>(range.begin()); + if (header->magic != Header::kMagic || header->version != 0 || + header->hashFunction != 0 || header->flags != 0 || + (fileId != 0 && header->fileId != fileId)) { + return false; + } + if (headerHash(*header) != header->headerHash) { + return false; + } + return true; +} + +RecordInfo validateRecordData(ByteRange range) { + if (range.size() <= headerSize()) { // records may not be empty + return {0, {}}; + } + auto header = reinterpret_cast<const Header*>(range.begin()); + range.advance(sizeof(Header)); + if (header->dataLength > range.size()) { + return {0, {}}; + } + range.reset(range.begin(), header->dataLength); + if (dataHash(range) != header->dataHash) { + return {0, {}}; + } + return {header->fileId, range}; +} + +RecordInfo validateRecord(ByteRange range, uint32_t fileId) { + if (!validateRecordHeader(range, fileId)) { + return {0, {}}; + } + return validateRecordData(range); +} + +RecordInfo +findRecord(ByteRange searchRange, ByteRange wholeRange, uint32_t fileId) { + static const uint32_t magic = Header::kMagic; + static const ByteRange magicRange( + reinterpret_cast<const uint8_t*>(&magic), sizeof(magic)); + + DCHECK_GE(searchRange.begin(), wholeRange.begin()); + DCHECK_LE(searchRange.end(), wholeRange.end()); + + const uint8_t* start = searchRange.begin(); + const uint8_t* end = + std::min(searchRange.end(), wholeRange.end() - sizeof(Header)); + // end-1: the last place where a Header could start + while (start < end) { + auto p = ByteRange(start, end + sizeof(magic)).find(magicRange); + if (p == ByteRange::npos) { + break; + } + + start += p; + auto r = validateRecord(ByteRange(start, wholeRange.end()), fileId); + if (!r.record.empty()) { + return r; + } + + // No repeated prefix in magic, so we can do better than start++ + start += sizeof(magic); + } + + return {0, {}}; +} + +} // namespace recordio_helpers + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/RecordIO.h b/ios/Pods/Flipper-Folly/folly/io/RecordIO.h new file mode 100644 index 000000000..b72c4657a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/RecordIO.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * RecordIO: self-synchronizing stream of variable length records + * + * RecordIO gives you the ability to write a stream of variable length records + * and read them later even in the face of data corruption -- randomly inserted + * or deleted chunks of the file, or modified data. When reading, you may lose + * corrupted records, but the stream will resynchronize automatically. + */ + +#pragma once +#define FOLLY_IO_RECORDIO_H_ + +#include <atomic> +#include <memory> +#include <mutex> + +#include <folly/File.h> +#include <folly/Range.h> +#include <folly/io/IOBuf.h> +#include <folly/system/MemoryMapping.h> + +namespace folly { + +/** + * Class to write a stream of RecordIO records to a file. + * + * RecordIOWriter is thread-safe + */ +class RecordIOWriter { + public: + /** + * Create a RecordIOWriter around a file; will append to the end of + * file if it exists. + * + * Each file must have a non-zero file id, which is embedded in all + * record headers. Readers will only return records with the requested + * file id (or, if the reader is created with fileId=0 in the constructor, + * the reader will return all records). File ids are only used to allow + * resynchronization if you store RecordIO records (with headers) inside + * other RecordIO records (for example, if a record consists of a fragment + * from another RecordIO file). If you're not planning to do that, + * the defaults are fine. + */ + explicit RecordIOWriter(File file, uint32_t fileId = 1); + + /** + * Write a record. We will use at most headerSize() bytes of headroom, + * you might want to arrange that before copying your data into it. + */ + void write(std::unique_ptr<IOBuf> buf); + + /** + * Return the position in the file where the next byte will be written. + * Conservative, as stuff can be written at any time from another thread. + */ + off_t filePos() const { + return filePos_; + } + + private: + File file_; + uint32_t fileId_; + std::unique_lock<File> writeLock_; + std::atomic<off_t> filePos_; +}; + +/** + * Class to read from a RecordIO file. Will skip invalid records. + */ +class RecordIOReader { + public: + class Iterator; + + /** + * RecordIOReader is iterable, returning pairs of ByteRange (record content) + * and position in file where the record (including header) begins. + * Note that the position includes the header, that is, it can be passed back + * to seek(). + */ + typedef Iterator iterator; + typedef Iterator const_iterator; + typedef std::pair<ByteRange, off_t> value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + + /** + * A record reader with a fileId of 0 will return all records. + * A record reader with a non-zero fileId will only return records where + * the fileId matches. + */ + explicit RecordIOReader(File file, uint32_t fileId = 0); + + Iterator cbegin() const; + Iterator begin() const; + Iterator cend() const; + Iterator end() const; + + /** + * Create an iterator to the first valid record after pos. + */ + Iterator seek(off_t pos) const; + + private: + MemoryMapping map_; + uint32_t fileId_; +}; + +namespace recordio_helpers { + +// We're exposing the guts of the RecordIO implementation for two reasons: +// 1. It makes unit testing easier, and +// 2. It allows you to build different RecordIO readers / writers that use +// different storage systems underneath (not standard files) + +/** + * Header size. + */ +constexpr size_t headerSize(); // defined in RecordIO-inl.h + +/** + * Write a header in the buffer. We will prepend the header to the front + * of the chain. Do not write the buffer if empty (we don't allow empty + * records). Returns the total length, including header (0 if empty) + * (same as buf->computeChainDataLength(), but likely faster) + * + * The fileId should be unique per stream and allows you to have RecordIO + * headers stored inside the data (for example, have an entire RecordIO + * file stored as a record inside another RecordIO file). The fileId may + * not be 0. + */ +size_t prependHeader(std::unique_ptr<IOBuf>& buf, uint32_t fileId = 1); + +/** + * Search for the first valid record that begins in searchRange (which must be + * a subrange of wholeRange). Returns the record data (not the header) if + * found, ByteRange() otherwise. + * + * The fileId may be 0, in which case we'll return the first valid record for + * *any* fileId, or non-zero, in which case we'll only look for records with + * the requested fileId. + */ +struct RecordInfo { + uint32_t fileId; + ByteRange record; +}; +RecordInfo +findRecord(ByteRange searchRange, ByteRange wholeRange, uint32_t fileId); + +/** + * Search for the first valid record in range. + */ +RecordInfo findRecord(ByteRange range, uint32_t fileId); + +/** + * Check if the Record Header is valid at the beginning of range. + * Useful to check the validity of the header before building the entire record + * in IOBuf. If the record is from storage device (e.g. flash) then, it + * is better to make sure that the header is valid before reading the data + * from the storage device. + * Returns true if valid, false otherwise. + */ +bool validateRecordHeader(ByteRange range, uint32_t fileId); + +/** + * Check if there Record Data is valid (to be used after validating the header + * separately) + * Returns the record data (not the header) if the record data is valid, + * ByteRange() otherwise. + */ +RecordInfo validateRecordData(ByteRange range); + +/** + * Check if there is a valid record at the beginning of range. This validates + * both record header and data and Returns the + * record data (not the header) if the record is valid, ByteRange() otherwise. + */ +RecordInfo validateRecord(ByteRange range, uint32_t fileId); + +} // namespace recordio_helpers + +} // namespace folly + +#include <folly/io/RecordIO-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.cpp b/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.cpp new file mode 100644 index 000000000..60f8b0b8f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/ShutdownSocketSet.h> + +#include <chrono> +#include <thread> + +#include <glog/logging.h> + +#include <folly/FileUtil.h> +#include <folly/net/NetOps.h> +#include <folly/portability/Sockets.h> + +namespace folly { + +ShutdownSocketSet::ShutdownSocketSet(int maxFd) + : maxFd_(maxFd), + data_(static_cast<std::atomic<uint8_t>*>( + folly::checkedCalloc(size_t(maxFd), sizeof(std::atomic<uint8_t>)))), + nullFile_("/dev/null", O_RDWR) {} + +void ShutdownSocketSet::add(NetworkSocket fd) { + // Silently ignore any fds >= maxFd_, very unlikely + DCHECK_NE(fd, NetworkSocket()); + if (fd.data >= maxFd_) { + return; + } + + auto& sref = data_[size_t(fd.data)]; + uint8_t prevState = FREE; + CHECK(sref.compare_exchange_strong( + prevState, IN_USE, std::memory_order_relaxed)) + << "Invalid prev state for fd " << fd << ": " << int(prevState); +} + +void ShutdownSocketSet::remove(NetworkSocket fd) { + DCHECK_NE(fd, NetworkSocket()); + if (fd.data >= maxFd_) { + return; + } + + auto& sref = data_[size_t(fd.data)]; + uint8_t prevState = 0; + + prevState = sref.load(std::memory_order_relaxed); + do { + switch (prevState) { + case IN_SHUTDOWN: + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + prevState = sref.load(std::memory_order_relaxed); + continue; + case FREE: + LOG(FATAL) << "Invalid prev state for fd " << fd << ": " + << int(prevState); + } + } while ( + !sref.compare_exchange_weak(prevState, FREE, std::memory_order_relaxed)); +} + +int ShutdownSocketSet::close(NetworkSocket fd) { + DCHECK_NE(fd, NetworkSocket()); + if (fd.data >= maxFd_) { + return folly::closeNoInt(fd); + } + + auto& sref = data_[size_t(fd.data)]; + uint8_t prevState = sref.load(std::memory_order_relaxed); + uint8_t newState = 0; + + do { + switch (prevState) { + case IN_USE: + case SHUT_DOWN: + newState = FREE; + break; + case IN_SHUTDOWN: + newState = MUST_CLOSE; + break; + default: + LOG(FATAL) << "Invalid prev state for fd " << fd << ": " + << int(prevState); + } + } while (!sref.compare_exchange_weak( + prevState, newState, std::memory_order_relaxed)); + + return newState == FREE ? folly::closeNoInt(fd) : 0; +} + +void ShutdownSocketSet::shutdown(NetworkSocket fd, bool abortive) { + DCHECK_NE(fd, NetworkSocket()); + if (fd.data >= maxFd_) { + doShutdown(fd, abortive); + return; + } + + auto& sref = data_[size_t(fd.data)]; + uint8_t prevState = IN_USE; + if (!sref.compare_exchange_strong( + prevState, IN_SHUTDOWN, std::memory_order_relaxed)) { + return; + } + + doShutdown(fd, abortive); + + prevState = IN_SHUTDOWN; + if (sref.compare_exchange_strong( + prevState, SHUT_DOWN, std::memory_order_relaxed)) { + return; + } + + CHECK_EQ(prevState, MUST_CLOSE) + << "Invalid prev state for fd " << fd << ": " << int(prevState); + + folly::closeNoInt(fd); // ignore errors, nothing to do + + CHECK( + sref.compare_exchange_strong(prevState, FREE, std::memory_order_relaxed)) + << "Invalid prev state for fd " << fd << ": " << int(prevState); +} + +void ShutdownSocketSet::shutdownAll(bool abortive) { + for (int i = 0; i < maxFd_; ++i) { + auto& sref = data_[size_t(i)]; + if (sref.load(std::memory_order_relaxed) == IN_USE) { + shutdown( + NetworkSocket(static_cast<NetworkSocket::native_handle_type>(i)), + abortive); + } + } +} + +void ShutdownSocketSet::doShutdown(NetworkSocket fd, bool abortive) { + // shutdown() the socket first, to awaken any threads blocked on the fd + // (subsequent IO will fail because it's been shutdown); close()ing the + // socket does not wake up blockers, see + // http://stackoverflow.com/a/3624545/1736339 + folly::shutdownNoInt(fd, SHUT_RDWR); + + // If abortive shutdown is desired, we'll set the SO_LINGER option on + // the socket with a timeout of 0; this will cause RST to be sent on + // close. + if (abortive) { + struct linger l = {1, 0}; + if (netops::setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) { + // Probably not a socket, ignore. + return; + } + } + + // Note that this can be ignored without breaking anything due to the + // fact the file descriptor will eventually be closed, so on Windows + // all of the socket resources will just stick around longer than they + // do on posix-like systems. +#ifndef _WIN32 + // We can't close() the socket, as that would be dangerous; a new file + // could be opened and get the same file descriptor, and then code assuming + // the old fd would do IO in the wrong place. We'll (atomically) dup2 + // /dev/null onto the fd instead. + folly::dup2NoInt(nullFile_.fd(), fd.toFd()); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.h b/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.h new file mode 100644 index 000000000..80bfe3cd3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/ShutdownSocketSet.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstdlib> +#include <memory> + +#include <folly/File.h> +#include <folly/net/NetworkSocket.h> + +namespace folly { + +/** + * Set of sockets that allows immediate, take-no-prisoners abort. + */ +class ShutdownSocketSet { + public: + /** + * Create a socket set that can handle file descriptors < maxFd. + * The default value (256Ki) is high enough for just about all + * applications, even if you increased the number of file descriptors + * on your system. + */ + explicit ShutdownSocketSet(int maxFd = 1 << 18); + + ShutdownSocketSet(const ShutdownSocketSet&) = delete; + ShutdownSocketSet& operator=(const ShutdownSocketSet&) = delete; + + /** + * Add an already open socket to the list of sockets managed by + * ShutdownSocketSet. You MUST close the socket by calling + * ShutdownSocketSet::close (which will, as a side effect, also handle EINTR + * properly) and not by calling close() on the file descriptor. + */ + void add(NetworkSocket fd); + + /** + * Remove a socket from the list of sockets managed by ShutdownSocketSet. + * Note that remove() might block! (which we lamely implement by + * sleep()-ing) in the extremely rare case that the fd is currently + * being shutdown(). + */ + void remove(NetworkSocket fd); + + /** + * Close a socket managed by ShutdownSocketSet. Returns the same return code + * as ::close() (and sets errno accordingly). + */ + int close(NetworkSocket fd); + + /** + * Shut down a socket. If abortive is true, we perform an abortive + * shutdown (send RST rather than FIN). Note that we might still end up + * sending FIN due to the rather interesting implementation. + * + * This is async-signal-safe and ignores errors. Obviously, subsequent + * read() and write() operations to the socket will fail. During normal + * operation, just call ::shutdown() on the socket. + */ + void shutdown(NetworkSocket fd, bool abortive = false); + + /** + * Immediate shutdown of all connections. This is a hard-hitting hammer; + * all reads and writes will return errors and no new connections will + * be accepted. + * + * To be used only in dire situations. We're using it from the failure + * signal handler to close all connections quickly, even though the server + * might take multiple seconds to finish crashing. + * + * The optional bool parameter indicates whether to set the active + * connections in to not linger. The effect of that includes RST packets + * being immediately sent to clients which will result + * in errors (and not normal EOF) on the client side. This also causes + * the local (ip, tcp port number) tuple to be reusable immediately, instead + * of having to wait the standard amount of time. For full details see + * the `shutdown` method of `ShutdownSocketSet` (incl. notes about the + * `abortive` parameter). + * + * This is async-signal-safe and ignores errors. + */ + void shutdownAll(bool abortive = false); + + private: + void doShutdown(NetworkSocket fd, bool abortive); + + // State transitions: + // add(): + // FREE -> IN_USE + // + // close(): + // IN_USE -> (::close()) -> FREE + // SHUT_DOWN -> (::close()) -> FREE + // IN_SHUTDOWN -> MUST_CLOSE + // (If the socket is currently being shut down, we must defer the + // ::close() until the shutdown completes) + // + // shutdown(): + // IN_USE -> IN_SHUTDOWN + // (::shutdown()) + // IN_SHUTDOWN -> SHUT_DOWN + // MUST_CLOSE -> (::close()) -> FREE + // + // State atomic operation memory orders: + // All atomic operations on per-socket states use std::memory_order_relaxed + // because there is no associated per-socket data guarded by the state and + // the states for different sockets are unrelated. If there were associated + // per-socket data, acquire and release orders would be desired; and if the + // states for different sockets were related, it could be that sequential + // consistent orders would be desired. + enum State : uint8_t { + FREE = 0, + IN_USE, + IN_SHUTDOWN, + SHUT_DOWN, + MUST_CLOSE, + }; + + struct Free { + template <class T> + void operator()(T* ptr) const { + ::free(ptr); + } + }; + + const int maxFd_; + std::unique_ptr<std::atomic<uint8_t>[], Free> data_; + folly::File nullFile_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.cpp b/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.cpp new file mode 100644 index 000000000..65f301bc2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/SocketOptionMap.h> +#include <folly/net/NetworkSocket.h> + +#include <errno.h> + +namespace folly { + +const SocketOptionMap emptySocketOptionMap; + +int SocketOptionKey::apply(NetworkSocket fd, int val) const { + return netops::setsockopt(fd, level, optname, &val, sizeof(val)); +} + +int applySocketOptions( + NetworkSocket fd, + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos) { + for (const auto& opt : options) { + if (opt.first.applyPos_ == pos) { + auto rv = opt.first.apply(fd, opt.second); + if (rv != 0) { + return errno; + } + } + } + return 0; +} + +SocketOptionMap validateSocketOptions( + const SocketOptionMap& options, + sa_family_t family, + SocketOptionKey::ApplyPos pos) { + SocketOptionMap validOptions; + for (const auto& option : options) { + if (pos != option.first.applyPos_) { + continue; + } + if ((family == AF_INET && option.first.level == IPPROTO_IP) || + (family == AF_INET6 && option.first.level == IPPROTO_IPV6) || + option.first.level == IPPROTO_UDP || option.first.level == SOL_SOCKET || + option.first.level == SOL_UDP) { + validOptions.insert(option); + } + } + return validOptions; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.h b/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.h new file mode 100644 index 000000000..87e4fe251 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/SocketOptionMap.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Sockets.h> + +#include <map> + +namespace folly { + +/** + * Uniquely identifies a handle to a socket option value. Each + * combination of level and option name corresponds to one socket + * option value. + */ +class SocketOptionKey { + public: + enum class ApplyPos { POST_BIND = 0, PRE_BIND = 1 }; + + bool operator<(const SocketOptionKey& other) const { + if (level == other.level) { + return optname < other.optname; + } + return level < other.level; + } + + int apply(NetworkSocket fd, int val) const; + + int level; + int optname; + ApplyPos applyPos_{ApplyPos::POST_BIND}; +}; + +// Maps from a socket option key to its value +using SocketOptionMap = std::map<SocketOptionKey, int>; + +extern const SocketOptionMap emptySocketOptionMap; + +int applySocketOptions( + NetworkSocket fd, + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos); + +SocketOptionMap validateSocketOptions( + const SocketOptionMap& options, + sa_family_t family, + SocketOptionKey::ApplyPos pos); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/TypedIOBuf.h b/ios/Pods/Flipper-Folly/folly/io/TypedIOBuf.h new file mode 100644 index 000000000..8ad6339ae --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/TypedIOBuf.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <iterator> +#include <type_traits> + +#include <folly/io/IOBuf.h> +#include <folly/memory/Malloc.h> + +namespace folly { + +/** + * Wrapper class to handle a IOBuf as a typed buffer (to a standard layout + * class). + * + * This class punts on alignment, and assumes that you know what you're doing. + * + * All methods are wrappers around the corresponding IOBuf methods. The + * TypedIOBuf object is stateless, so it's perfectly okay to access the + * underlying IOBuf in between TypedIOBuf method calls. + */ +template <class T> +class TypedIOBuf { + static_assert(std::is_standard_layout<T>::value, "must be standard layout"); + + public: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef uint32_t size_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + explicit TypedIOBuf(IOBuf* buf) : buf_(buf) {} + + IOBuf* ioBuf() { + return buf_; + } + const IOBuf* ioBuf() const { + return buf_; + } + + bool empty() const { + return buf_->empty(); + } + const T* data() const { + return cast(buf_->data()); + } + T* writableData() { + return cast(buf_->writableData()); + } + const T* tail() const { + return cast(buf_->tail()); + } + T* writableTail() { + return cast(buf_->writableTail()); + } + uint32_t length() const { + return sdiv(buf_->length()); + } + uint32_t size() const { + return length(); + } + + uint32_t headroom() const { + return sdiv(buf_->headroom()); + } + uint32_t tailroom() const { + return sdiv(buf_->tailroom()); + } + const T* buffer() const { + return cast(buf_->buffer()); + } + T* writableBuffer() { + return cast(buf_->writableBuffer()); + } + const T* bufferEnd() const { + return cast(buf_->bufferEnd()); + } + uint32_t capacity() const { + return sdiv(buf_->capacity()); + } + void advance(uint32_t n) { + buf_->advance(smul(n)); + } + void retreat(uint32_t n) { + buf_->retreat(smul(n)); + } + void prepend(uint32_t n) { + buf_->prepend(smul(n)); + } + void append(uint32_t n) { + buf_->append(smul(n)); + } + void trimStart(uint32_t n) { + buf_->trimStart(smul(n)); + } + void trimEnd(uint32_t n) { + buf_->trimEnd(smul(n)); + } + void clear() { + buf_->clear(); + } + void reserve(uint32_t minHeadroom, uint32_t minTailroom) { + buf_->reserve(smul(minHeadroom), smul(minTailroom)); + } + void reserve(uint32_t minTailroom) { + reserve(0, minTailroom); + } + + const T* cbegin() const { + return data(); + } + const T* cend() const { + return tail(); + } + const T* begin() const { + return cbegin(); + } + const T* end() const { + return cend(); + } + T* begin() { + return writableData(); + } + T* end() { + return writableTail(); + } + + const T& front() const { + assert(!empty()); + return *begin(); + } + T& front() { + assert(!empty()); + return *begin(); + } + const T& back() const { + assert(!empty()); + return end()[-1]; + } + T& back() { + assert(!empty()); + return end()[-1]; + } + + /** + * Simple wrapper to make it easier to treat this TypedIOBuf as an array of + * T. + */ + const T& operator[](ssize_t idx) const { + assert(idx >= 0 && idx < length()); + return data()[idx]; + } + + T& operator[](ssize_t idx) { + assert(idx >= 0 && idx < length()); + return writableData()[idx]; + } + + /** + * Append one element. + */ + void push(const T& data) { + push(&data, &data + 1); + } + void push_back(const T& data) { + push(data); + } + + /** + * Append multiple elements in a sequence; will call distance(). + */ + template <class IT> + void push(IT begin, IT end) { + uint32_t n = std::distance(begin, end); + if (usingJEMalloc()) { + // Rely on xallocx() and avoid exponential growth to limit + // amount of memory wasted. + reserve(headroom(), n); + } else if (tailroom() < n) { + reserve(headroom(), std::max(n, 3 + size() / 2)); + } + std::copy(begin, end, writableTail()); + append(n); + } + + // Movable + TypedIOBuf(TypedIOBuf&&) = default; + TypedIOBuf& operator=(TypedIOBuf&&) = default; + + private: + // Non-copyable + TypedIOBuf(const TypedIOBuf&) = delete; + TypedIOBuf& operator=(const TypedIOBuf&) = delete; + + // cast to T* + static T* cast(uint8_t* p) { + return reinterpret_cast<T*>(p); + } + static const T* cast(const uint8_t* p) { + return reinterpret_cast<const T*>(p); + } + // divide by size + static uint32_t sdiv(uint32_t n) { + return n / sizeof(T); + } + // multiply by size + static uint32_t smul(uint32_t n) { + // In debug mode, check for overflow + assert((uint64_t(n) * sizeof(T)) < (uint64_t(1) << 32)); + return n * sizeof(T); + } + + IOBuf* buf_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.cpp new file mode 100644 index 000000000..a3aafb4b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncPipe.h> + +#include <folly/FileUtil.h> +#include <folly/Utility.h> +#include <folly/detail/FileUtilDetail.h> +#include <folly/io/async/AsyncSocketException.h> + +using std::string; +using std::unique_ptr; + +namespace folly { + +AsyncPipeReader::~AsyncPipeReader() { + close(); +} + +void AsyncPipeReader::failRead(const AsyncSocketException& ex) { + VLOG(5) << "AsyncPipeReader(this=" << this << ", fd=" << fd_ + << "): failed while reading: " << ex.what(); + + DCHECK(readCallback_ != nullptr); + AsyncReader::ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readErr(ex); + close(); +} + +void AsyncPipeReader::close() { + unregisterHandler(); + if (fd_ != NetworkSocket()) { + changeHandlerFD(NetworkSocket()); + + if (closeCb_) { + closeCb_(fd_); + } else { + netops::close(fd_); + } + fd_ = NetworkSocket(); + } +} + +#ifdef _WIN32 +static int recv_internal(NetworkSocket s, void* buf, size_t count) { + auto r = netops::recv(s, buf, count, 0); + if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { + errno = EAGAIN; + } + return folly::to_narrow(r); +} +#endif + +void AsyncPipeReader::handlerReady(uint16_t events) noexcept { + DestructorGuard dg(this); + CHECK(events & EventHandler::READ); + + VLOG(5) << "AsyncPipeReader::handlerReady() this=" << this << ", fd=" << fd_; + assert(readCallback_ != nullptr); + + while (readCallback_) { + // - What API does callback support? + const auto movable = readCallback_->isBufferMovable(); // noexcept + + // Get the buffer to read into. + void* buf = nullptr; + size_t buflen = 0; + std::unique_ptr<IOBuf> ioBuf; + + if (movable) { + ioBuf = IOBuf::create(readCallback_->maxBufferSize()); + buf = ioBuf->writableBuffer(); + buflen = ioBuf->capacity(); + } else { + try { + readCallback_->getReadBuffer(&buf, &buflen); + } catch (const std::exception& ex) { + AsyncSocketException aex( + AsyncSocketException::BAD_ARGS, + string("ReadCallback::getReadBuffer() " + "threw exception: ") + + ex.what()); + failRead(aex); + return; + } catch (...) { + AsyncSocketException aex( + AsyncSocketException::BAD_ARGS, + string("ReadCallback::getReadBuffer() " + "threw non-exception type")); + failRead(aex); + return; + } + if (buf == nullptr || buflen == 0) { + AsyncSocketException aex( + AsyncSocketException::INVALID_STATE, + string("ReadCallback::getReadBuffer() " + "returned empty buffer")); + failRead(aex); + return; + } + } + + // Perform the read +#ifdef _WIN32 + // On Windows you can't call read on a socket, so call recv instead. + ssize_t bytesRead = + folly::fileutil_detail::wrapNoInt(recv_internal, fd_, buf, buflen); +#else + ssize_t bytesRead = folly::readNoInt(fd_.toFd(), buf, buflen); +#endif + + if (bytesRead > 0) { + if (movable) { + ioBuf->append(std::size_t(bytesRead)); + readCallback_->readBufferAvailable(std::move(ioBuf)); + } else { + readCallback_->readDataAvailable(size_t(bytesRead)); + } + // Fall through and continue around the loop if the read + // completely filled the available buffer. + // Note that readCallback_ may have been uninstalled or changed inside + // readDataAvailable(). + if (static_cast<size_t>(bytesRead) < buflen) { + return; + } + } else if (bytesRead < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // No more data to read right now. + return; + } else if (bytesRead < 0) { + AsyncSocketException ex( + AsyncSocketException::INVALID_STATE, "read failed", errno); + failRead(ex); + return; + } else { + assert(bytesRead == 0); + // EOF + + unregisterHandler(); + AsyncReader::ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readEOF(); + return; + } + // Max reads per loop? + } +} + +void AsyncPipeWriter::write( + unique_ptr<folly::IOBuf> buf, + AsyncWriter::WriteCallback* callback) { + if (closed()) { + if (callback) { + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, "attempt to write to closed pipe"); + callback->writeErr(0, ex); + } + return; + } + bool wasEmpty = (queue_.empty()); + folly::IOBufQueue iobq; + iobq.append(std::move(buf)); + std::pair<folly::IOBufQueue, AsyncWriter::WriteCallback*> p( + std::move(iobq), callback); + queue_.emplace_back(std::move(p)); + if (wasEmpty) { + handleWrite(); + } else { + CHECK(!queue_.empty()); + CHECK(isHandlerRegistered()); + } +} + +void AsyncPipeWriter::writeChain( + folly::AsyncWriter::WriteCallback* callback, + std::unique_ptr<folly::IOBuf>&& buf, + WriteFlags) { + write(std::move(buf), callback); +} + +void AsyncPipeWriter::closeOnEmpty() { + VLOG(5) << "close on empty"; + if (queue_.empty()) { + closeNow(); + } else { + closeOnEmpty_ = true; + CHECK(isHandlerRegistered()); + } +} + +void AsyncPipeWriter::closeNow() { + VLOG(5) << "close now"; + if (!queue_.empty()) { + failAllWrites(AsyncSocketException( + AsyncSocketException::NOT_OPEN, "closed with pending writes")); + } + if (fd_ != NetworkSocket()) { + unregisterHandler(); + changeHandlerFD(NetworkSocket()); + if (closeCb_) { + closeCb_(fd_); + } else { + netops::close(fd_); + } + fd_ = NetworkSocket(); + } +} + +void AsyncPipeWriter::failAllWrites(const AsyncSocketException& ex) { + DestructorGuard dg(this); + while (!queue_.empty()) { + // the first entry of the queue could have had a partial write, but needs to + // be tracked. + if (queue_.front().second) { + queue_.front().second->writeErr(0, ex); + } + queue_.pop_front(); + } +} + +void AsyncPipeWriter::handlerReady(uint16_t events) noexcept { + CHECK(events & EventHandler::WRITE); + + handleWrite(); +} + +#ifdef _WIN32 +static int send_internal(NetworkSocket s, const void* buf, size_t count) { + auto r = netops::send(s, buf, count, 0); + if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { + errno = EAGAIN; + } + return folly::to_narrow(r); +} +#endif + +void AsyncPipeWriter::handleWrite() { + DestructorGuard dg(this); + assert(!queue_.empty()); + do { + auto& front = queue_.front(); + folly::IOBufQueue& curQueue = front.first; + DCHECK(!curQueue.empty()); + // someday, support writev. The logic for partial writes is a bit complex + const IOBuf* head = curQueue.front(); + CHECK(head->length()); +#ifdef _WIN32 + // On Windows you can't call write on a socket. + ssize_t rc = folly::fileutil_detail::wrapNoInt( + send_internal, fd_, head->data(), head->length()); +#else + ssize_t rc = folly::writeNoInt(fd_.toFd(), head->data(), head->length()); +#endif + if (rc < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // pipe is full + VLOG(5) << "write blocked"; + registerHandler(EventHandler::WRITE); + return; + } else { + failAllWrites(AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, "write failed", errno)); + closeNow(); + return; + } + } else if (rc == 0) { + registerHandler(EventHandler::WRITE); + return; + } + curQueue.trimStart(size_t(rc)); + if (curQueue.empty()) { + auto cb = front.second; + queue_.pop_front(); + if (cb) { + cb->writeSuccess(); + } + } else { + VLOG(5) << "partial write blocked"; + } + } while (!queue_.empty()); + + if (closeOnEmpty_) { + closeNow(); + } else { + unregisterHandler(); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.h new file mode 100644 index 000000000..3939211c3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncPipe.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <list> +#include <system_error> + +#include <folly/io/IOBufQueue.h> +#include <folly/io/async/AsyncTransport.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/EventHandler.h> + +namespace folly { + +class AsyncSocketException; + +/** + * Read from a pipe in an async manner. + */ +class AsyncPipeReader : public EventHandler, + public AsyncReader, + public DelayedDestruction { + public: + typedef std:: + unique_ptr<AsyncPipeReader, folly::DelayedDestruction::Destructor> + UniquePtr; + + static UniquePtr newReader( + folly::EventBase* eventBase, + NetworkSocket pipeFd) { + return UniquePtr(new AsyncPipeReader(eventBase, pipeFd)); + } + + AsyncPipeReader(folly::EventBase* eventBase, NetworkSocket pipeFd) + : EventHandler(eventBase, pipeFd), fd_(pipeFd) {} + + /** + * Set the read callback and automatically install/uninstall the handler + * for events. + */ + void setReadCB(AsyncReader::ReadCallback* callback) override { + if (callback == readCallback_) { + return; + } + readCallback_ = callback; + if (readCallback_ && !isHandlerRegistered()) { + registerHandler(EventHandler::READ | EventHandler::PERSIST); + } else if (!readCallback_ && isHandlerRegistered()) { + unregisterHandler(); + } + } + + /** + * Get the read callback + */ + AsyncReader::ReadCallback* getReadCallback() const override { + return readCallback_; + } + + /** + * Set a special hook to close the socket (otherwise, will call close()) + */ + void setCloseCallback(std::function<void(NetworkSocket)> closeCb) { + closeCb_ = closeCb; + } + + private: + ~AsyncPipeReader() override; + + void handlerReady(uint16_t events) noexcept override; + void failRead(const AsyncSocketException& ex); + void close(); + + NetworkSocket fd_; + AsyncReader::ReadCallback* readCallback_{nullptr}; + std::function<void(NetworkSocket)> closeCb_; +}; + +/** + * Write to a pipe in an async manner. + */ +class AsyncPipeWriter : public EventHandler, + public AsyncWriter, + public DelayedDestruction { + public: + typedef std:: + unique_ptr<AsyncPipeWriter, folly::DelayedDestruction::Destructor> + UniquePtr; + + static UniquePtr newWriter( + folly::EventBase* eventBase, + NetworkSocket pipeFd) { + return UniquePtr(new AsyncPipeWriter(eventBase, pipeFd)); + } + + AsyncPipeWriter(folly::EventBase* eventBase, NetworkSocket pipeFd) + : EventHandler(eventBase, pipeFd), fd_(pipeFd) {} + + /** + * Asynchronously write the given iobuf to this pipe, and invoke the callback + * on success/error. + */ + void write( + std::unique_ptr<folly::IOBuf> buf, + AsyncWriter::WriteCallback* callback = nullptr); + + /** + * Set a special hook to close the socket (otherwise, will call close()) + */ + void setCloseCallback(std::function<void(NetworkSocket)> closeCb) { + closeCb_ = closeCb; + } + + /** + * Returns true if the pipe is closed + */ + bool closed() const { + return (fd_ == NetworkSocket() || closeOnEmpty_); + } + + /** + * Notify the pipe to close as soon as all pending writes complete + */ + void closeOnEmpty(); + + /** + * Close the pipe immediately, and fail all pending writes + */ + void closeNow(); + + /** + * Return true if there are currently writes pending (eg: the pipe is blocked + * for writing) + */ + bool hasPendingWrites() const { + return !queue_.empty(); + } + + // AsyncWriter methods + void write( + folly::AsyncWriter::WriteCallback* callback, + const void* buf, + size_t bytes, + WriteFlags flags = WriteFlags::NONE) override { + writeChain(callback, IOBuf::wrapBuffer(buf, bytes), flags); + } + void writev( + folly::AsyncWriter::WriteCallback*, + const iovec*, + size_t, + WriteFlags = WriteFlags::NONE) override { + throw std::runtime_error("writev is not supported. Please use writeChain."); + } + void writeChain( + folly::AsyncWriter::WriteCallback* callback, + std::unique_ptr<folly::IOBuf>&& buf, + WriteFlags flags = WriteFlags::NONE) override; + + private: + void handlerReady(uint16_t events) noexcept override; + void handleWrite(); + void failAllWrites(const AsyncSocketException& ex); + + NetworkSocket fd_; + std::list<std::pair<folly::IOBufQueue, AsyncWriter::WriteCallback*>> queue_; + bool closeOnEmpty_{false}; + std::function<void(NetworkSocket)> closeCb_; + + ~AsyncPipeWriter() override { + closeNow(); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.cpp new file mode 100644 index 000000000..b9e8d9b17 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.cpp @@ -0,0 +1,2078 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncSSLSocket.h> + +#include <folly/io/async/EventBase.h> +#include <folly/portability/Sockets.h> + +#include <fcntl.h> +#include <sys/types.h> +#include <cerrno> +#include <chrono> +#include <memory> +#include <utility> + +#include <folly/Format.h> +#include <folly/Indestructible.h> +#include <folly/SocketAddress.h> +#include <folly/SpinLock.h> +#include <folly/io/Cursor.h> +#include <folly/io/IOBuf.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/io/async/ssl/BasicTransportCertificate.h> +#include <folly/lang/Bits.h> +#include <folly/portability/OpenSSL.h> + +using std::shared_ptr; + +using folly::SpinLock; +using folly::io::Cursor; + +namespace { +using folly::AsyncSSLSocket; +using folly::SSLContext; +// For OpenSSL portability API +using namespace folly::ssl; +using folly::ssl::OpenSSLUtils; + +// We have one single dummy SSL context so that we can implement attach +// and detach methods in a thread safe fashion without modifying opnessl. +SSLContext* dummyCtx = nullptr; +SpinLock dummyCtxLock; + +// If given min write size is less than this, buffer will be allocated on +// stack, otherwise it is allocated on heap +const size_t MAX_STACK_BUF_SIZE = 2048; + +// This converts "illegal" shutdowns into ZERO_RETURN +inline bool zero_return(int error, int rc, int errno_copy) { + if (error == SSL_ERROR_ZERO_RETURN || (rc == 0 && errno_copy == 0)) { + return true; + } +#ifdef _WIN32 + // on windows underlying TCP socket may error with this code + // if the sending/receiving client crashes or is killed + if (error == SSL_ERROR_SYSCALL && errno_copy == WSAECONNRESET) { + return true; + } +#endif + return false; +} + +void setup_SSL_CTX(SSL_CTX* ctx) { +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode( + ctx, + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_RELEASE_BUFFERS); +#else + SSL_CTX_set_mode( + ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE); +#endif +// SSL_CTX_set_mode is a Macro +#ifdef SSL_MODE_WRITE_IOVEC + SSL_CTX_set_mode(ctx, SSL_CTX_get_mode(ctx) | SSL_MODE_WRITE_IOVEC); +#endif +} + +// Note: This is a Leaky Meyer's Singleton. The reason we can't use a non-leaky +// thing is because we will be setting this BIO_METHOD* inside BIOs owned by +// various SSL objects which may get callbacks even during teardown. We may +// eventually try to fix this +BIO_METHOD* getSSLBioMethod() { + static auto const instance = OpenSSLUtils::newSocketBioMethod().release(); + return instance; +} + +void* initsslBioMethod() { + auto sslBioMethod = getSSLBioMethod(); + // override the bwrite method for MSG_EOR support + OpenSSLUtils::setCustomBioWriteMethod(sslBioMethod, AsyncSSLSocket::bioWrite); + OpenSSLUtils::setCustomBioReadMethod(sslBioMethod, AsyncSSLSocket::bioRead); + + // Note that the sslBioMethod.type and sslBioMethod.name are not + // set here. openssl code seems to be checking ".type == BIO_TYPE_SOCKET" and + // then have specific handlings. The sslWriteBioWrite should be compatible + // with the one in openssl. + + // Return something here to enable AsyncSSLSocket to call this method using + // a function-scoped static. + return nullptr; +} + +} // namespace + +namespace folly { + +class AsyncSSLSocketConnector : public AsyncSocket::ConnectCallback, + public AsyncSSLSocket::HandshakeCB { + private: + AsyncSSLSocket* sslSocket_; + AsyncSSLSocket::ConnectCallback* callback_; + std::chrono::milliseconds timeout_; + std::chrono::steady_clock::time_point startTime_; + + public: + AsyncSSLSocketConnector( + AsyncSSLSocket* sslSocket, + AsyncSocket::ConnectCallback* callback, + std::chrono::milliseconds timeout) + : sslSocket_(sslSocket), + callback_(callback), + timeout_(timeout), + startTime_(std::chrono::steady_clock::now()) {} + + ~AsyncSSLSocketConnector() override = default; + + void preConnect(folly::NetworkSocket fd) override { + VLOG(7) << "client preConnect hook is invoked"; + if (callback_) { + callback_->preConnect(fd); + } + } + + void connectSuccess() noexcept override { + VLOG(7) << "client socket connected"; + + std::chrono::milliseconds timeoutLeft{0}; + if (timeout_ > std::chrono::milliseconds::zero()) { + auto curTime = std::chrono::steady_clock::now(); + + timeoutLeft = std::chrono::duration_cast<std::chrono::milliseconds>( + timeout_ - (curTime - startTime_)); + if (timeoutLeft <= std::chrono::milliseconds::zero()) { + AsyncSocketException ex( + AsyncSocketException::TIMED_OUT, + folly::sformat( + "SSL connect timed out after {}ms", timeout_.count())); + fail(ex); + delete this; + return; + } + } + sslSocket_->sslConn(this, timeoutLeft); + } + + void connectErr(const AsyncSocketException& ex) noexcept override { + VLOG(1) << "TCP connect failed: " << ex.what(); + fail(ex); + delete this; + } + + void handshakeSuc(AsyncSSLSocket* /* sock */) noexcept override { + VLOG(7) << "client handshake success"; + if (callback_) { + callback_->connectSuccess(); + } + delete this; + } + + void handshakeErr( + AsyncSSLSocket* /* socket */, + const AsyncSocketException& ex) noexcept override { + VLOG(1) << "client handshakeErr: " << ex.what(); + fail(ex); + delete this; + } + + void fail(const AsyncSocketException& ex) { + // fail is a noop if called twice + if (callback_) { + AsyncSSLSocket::ConnectCallback* cb = callback_; + callback_ = nullptr; + + cb->connectErr(ex); + sslSocket_->closeNow(); + // closeNow can call handshakeErr if it hasn't been called already. + // So this may have been deleted, no member variable access beyond this + // point + // Note that closeNow may invoke writeError callbacks if the socket had + // write data pending connection completion. + } + } +}; + +/** + * Create a client AsyncSSLSocket + */ +AsyncSSLSocket::AsyncSSLSocket( + shared_ptr<SSLContext> ctx, + EventBase* evb, + bool deferSecurityNegotiation) + : AsyncSocket(evb), + ctx_(std::move(ctx)), + handshakeTimeout_(this, evb), + connectionTimeout_(this, evb) { + init(); + if (deferSecurityNegotiation) { + sslState_ = STATE_UNENCRYPTED; + } +} + +/** + * Create a server/client AsyncSSLSocket + */ +AsyncSSLSocket::AsyncSSLSocket( + shared_ptr<SSLContext> ctx, + EventBase* evb, + NetworkSocket fd, + bool server, + bool deferSecurityNegotiation) + : AsyncSocket(evb, fd), + server_(server), + ctx_(std::move(ctx)), + handshakeTimeout_(this, evb), + connectionTimeout_(this, evb) { + noTransparentTls_ = true; + init(); + if (server) { + SSL_CTX_set_info_callback( + ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback); + } + if (deferSecurityNegotiation) { + sslState_ = STATE_UNENCRYPTED; + } +} + +AsyncSSLSocket::AsyncSSLSocket( + shared_ptr<SSLContext> ctx, + AsyncSocket::UniquePtr oldAsyncSocket, + bool server, + bool deferSecurityNegotiation) + : AsyncSocket(std::move(oldAsyncSocket)), + server_(server), + ctx_(std::move(ctx)), + handshakeTimeout_(this, AsyncSocket::getEventBase()), + connectionTimeout_(this, AsyncSocket::getEventBase()) { + noTransparentTls_ = true; + init(); + if (server) { + SSL_CTX_set_info_callback( + ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback); + } + if (deferSecurityNegotiation) { + sslState_ = STATE_UNENCRYPTED; + } +} + +#if FOLLY_OPENSSL_HAS_SNI +/** + * Create a client AsyncSSLSocket and allow tlsext_hostname + * to be sent in Client Hello. + */ +AsyncSSLSocket::AsyncSSLSocket( + const shared_ptr<SSLContext>& ctx, + EventBase* evb, + const std::string& serverName, + bool deferSecurityNegotiation) + : AsyncSSLSocket(ctx, evb, deferSecurityNegotiation) { + tlsextHostname_ = serverName; +} + +/** + * Create a client AsyncSSLSocket from an already connected fd + * and allow tlsext_hostname to be sent in Client Hello. + */ +AsyncSSLSocket::AsyncSSLSocket( + const shared_ptr<SSLContext>& ctx, + EventBase* evb, + NetworkSocket fd, + const std::string& serverName, + bool deferSecurityNegotiation) + : AsyncSSLSocket(ctx, evb, fd, false, deferSecurityNegotiation) { + tlsextHostname_ = serverName; +} +#endif // FOLLY_OPENSSL_HAS_SNI + +AsyncSSLSocket::~AsyncSSLSocket() { + VLOG(3) << "actual destruction of AsyncSSLSocket(this=" << this + << ", evb=" << eventBase_ << ", fd=" << fd_ + << ", state=" << int(state_) << ", sslState=" << sslState_ + << ", events=" << eventFlags_ << ")"; +} + +void AsyncSSLSocket::init() { + // Do this here to ensure we initialize this once before any use of + // AsyncSSLSocket instances and not as part of library load. + static const auto sslBioMethodInitializer = initsslBioMethod(); + (void)sslBioMethodInitializer; + + setup_SSL_CTX(ctx_->getSSLCtx()); +} + +void AsyncSSLSocket::closeNow() { + // Close the SSL connection. + if (ssl_ != nullptr && fd_ != NetworkSocket() && !waitingOnAccept_) { + int rc = SSL_shutdown(ssl_.get()); + if (rc == 0) { + rc = SSL_shutdown(ssl_.get()); + } + if (rc < 0) { + ERR_clear_error(); + } + } + + if (sslSession_ != nullptr) { + SSL_SESSION_free(sslSession_); + sslSession_ = nullptr; + } + + sslState_ = STATE_CLOSED; + + if (handshakeTimeout_.isScheduled()) { + handshakeTimeout_.cancelTimeout(); + } + + DestructorGuard dg(this); + + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::END_OF_FILE, "SSL connection closed locally"); + invokeHandshakeErr(*ex); + + // Close the socket. + AsyncSocket::closeNow(); +} + +void AsyncSSLSocket::shutdownWrite() { + // SSL sockets do not support half-shutdown, so just perform a full shutdown. + // + // (Performing a full shutdown here is more desirable than doing nothing at + // all. The purpose of shutdownWrite() is normally to notify the other end + // of the connection that no more data will be sent. If we do nothing, the + // other end will never know that no more data is coming, and this may result + // in protocol deadlock.) + close(); +} + +void AsyncSSLSocket::shutdownWriteNow() { + closeNow(); +} + +bool AsyncSSLSocket::good() const { + return ( + AsyncSocket::good() && + (sslState_ == STATE_ACCEPTING || sslState_ == STATE_CONNECTING || + sslState_ == STATE_ESTABLISHED || sslState_ == STATE_UNENCRYPTED || + sslState_ == STATE_UNINIT)); +} + +// The AsyncTransportWrapper definition of 'good' states that the transport is +// ready to perform reads and writes, so sslState_ == UNINIT must report !good. +// connecting can be true when the sslState_ == UNINIT because the AsyncSocket +// is connected but we haven't initiated the call to SSL_connect. +bool AsyncSSLSocket::connecting() const { + return ( + !server_ && + (AsyncSocket::connecting() || + (AsyncSocket::good() && + (sslState_ == STATE_UNINIT || sslState_ == STATE_CONNECTING)))); +} + +std::string AsyncSSLSocket::getApplicationProtocol() const noexcept { + const unsigned char* protoName = nullptr; + unsigned protoLength; + if (getSelectedNextProtocolNoThrow(&protoName, &protoLength)) { + return std::string(reinterpret_cast<const char*>(protoName), protoLength); + } + return ""; +} + +void AsyncSSLSocket::setEorTracking(bool track) { + if (isEorTrackingEnabled() != track) { + AsyncSocket::setEorTracking(track); + appEorByteNo_ = 0; + appEorByteWriteFlags_ = {}; + minEorRawByteNo_ = 0; + } +} + +size_t AsyncSSLSocket::getRawBytesWritten() const { + // The bio(s) in the write path are in a chain + // each bio flushes to the next and finally written into the socket + // to get the rawBytesWritten on the socket, + // get the write bytes of the last bio + BIO* b; + if (!ssl_ || !(b = SSL_get_wbio(ssl_.get()))) { + return 0; + } + BIO* next = BIO_next(b); + while (next != nullptr) { + b = next; + next = BIO_next(b); + } + + return BIO_number_written(b); +} + +size_t AsyncSSLSocket::getRawBytesReceived() const { + BIO* b; + if (!ssl_ || !(b = SSL_get_rbio(ssl_.get()))) { + return 0; + } + + return BIO_number_read(b); +} + +void AsyncSSLSocket::invalidState(HandshakeCB* callback) { + LOG(ERROR) << "AsyncSSLSocket(this=" << this << ", fd=" << fd_ + << ", state=" << int(state_) << ", sslState=" << sslState_ << ", " + << "events=" << eventFlags_ << ", server=" << short(server_) + << "): " + << "sslAccept/Connect() called in invalid " + << "state, handshake callback " << handshakeCallback_ + << ", new callback " << callback; + assert(!handshakeTimeout_.isScheduled()); + sslState_ = STATE_ERROR; + + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INVALID_STATE, + "sslAccept() called with socket in invalid state"); + + handshakeEndTime_ = std::chrono::steady_clock::now(); + if (callback) { + callback->handshakeErr(this, *ex); + } + + failHandshake(__func__, *ex); +} + +void AsyncSSLSocket::sslAccept( + HandshakeCB* callback, + std::chrono::milliseconds timeout, + const SSLContext::SSLVerifyPeerEnum& verifyPeer) { + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + verifyPeer_ = verifyPeer; + + // Make sure we're in the uninitialized state + if (!server_ || + (sslState_ != STATE_UNINIT && sslState_ != STATE_UNENCRYPTED) || + handshakeCallback_ != nullptr) { + return invalidState(callback); + } + + // Cache local and remote socket addresses to keep them available + // after socket file descriptor is closed. + if (cacheAddrOnFailure_) { + cacheAddresses(); + } + + handshakeStartTime_ = std::chrono::steady_clock::now(); + // Make end time at least >= start time. + handshakeEndTime_ = handshakeStartTime_; + + sslState_ = STATE_ACCEPTING; + handshakeCallback_ = callback; + + if (timeout > std::chrono::milliseconds::zero()) { + handshakeTimeout_.scheduleTimeout(timeout); + } + + /* register for a read operation (waiting for CLIENT HELLO) */ + updateEventRegistration(EventHandler::READ, EventHandler::WRITE); + + checkForImmediateRead(); +} + +void AsyncSSLSocket::attachSSLContext(const std::shared_ptr<SSLContext>& ctx) { + // Check to ensure we are in client mode. Changing a server's ssl + // context doesn't make sense since clients of that server would likely + // become confused when the server's context changes. + DCHECK(!server_); + DCHECK(!ctx_); + DCHECK(ctx); + DCHECK(ctx->getSSLCtx()); + ctx_ = ctx; + + // It's possible this could be attached before ssl_ is set up + if (!ssl_) { + return; + } + + // In order to call attachSSLContext, detachSSLContext must have been + // previously called. + // We need to update the initial_ctx if necessary + // The 'initial_ctx' inside an SSL* points to the context that it was created + // with, which is also where session callbacks and servername callbacks + // happen. + // When we switch to a different SSL_CTX, we want to update the initial_ctx as + // well so that any callbacks don't go to a different object + // NOTE: this will only work if we have access to ssl_ internals, so it may + // not work on + // OpenSSL version >= 1.1.0 + auto sslCtx = ctx->getSSLCtx(); + OpenSSLUtils::setSSLInitialCtx(ssl_.get(), sslCtx); + // Detach sets the socket's context to the dummy context. Thus we must acquire + // this lock. + SpinLockGuard guard(dummyCtxLock); + SSL_set_SSL_CTX(ssl_.get(), sslCtx); +} + +void AsyncSSLSocket::detachSSLContext() { + DCHECK(ctx_); + ctx_.reset(); + // It's possible for this to be called before ssl_ has been + // set up + if (!ssl_) { + return; + } + // The 'initial_ctx' inside an SSL* points to the context that it was created + // with, which is also where session callbacks and servername callbacks + // happen. + // Detach the initial_ctx as well. It will be reattached in attachSSLContext + // it is used for session info. + // NOTE: this will only work if we have access to ssl_ internals, so it may + // not work on + // OpenSSL version >= 1.1.0 + SSL_CTX* initialCtx = OpenSSLUtils::getSSLInitialCtx(ssl_.get()); + if (initialCtx) { + SSL_CTX_free(initialCtx); + OpenSSLUtils::setSSLInitialCtx(ssl_.get(), nullptr); + } + + SpinLockGuard guard(dummyCtxLock); + if (nullptr == dummyCtx) { + // We need to lazily initialize the dummy context so we don't + // accidentally override any programmatic settings to openssl + dummyCtx = new SSLContext; + } + // We must remove this socket's references to its context right now + // since this socket could get passed to any thread. If the context has + // had its locking disabled, just doing a set in attachSSLContext() + // would not be thread safe. + SSL_set_SSL_CTX(ssl_.get(), dummyCtx->getSSLCtx()); +} + +#if FOLLY_OPENSSL_HAS_SNI +void AsyncSSLSocket::switchServerSSLContext( + const std::shared_ptr<SSLContext>& handshakeCtx) { + CHECK(server_); + if (sslState_ != STATE_ACCEPTING) { + // We log it here and allow the switch. + // It should not affect our re-negotiation support (which + // is not supported now). + VLOG(6) << "fd=" << getNetworkSocket() + << " renegotation detected when switching SSL_CTX"; + } + + setup_SSL_CTX(handshakeCtx->getSSLCtx()); + SSL_CTX_set_info_callback( + handshakeCtx->getSSLCtx(), AsyncSSLSocket::sslInfoCallback); + handshakeCtx_ = handshakeCtx; + SSL_set_SSL_CTX(ssl_.get(), handshakeCtx->getSSLCtx()); +} + +bool AsyncSSLSocket::isServerNameMatch() const { + CHECK(!server_); + + if (!ssl_) { + return false; + } + + SSL_SESSION* ss = SSL_get_session(ssl_.get()); + if (!ss) { + return false; + } + + auto tlsextHostname = SSL_SESSION_get0_hostname(ss); + return (tlsextHostname && !tlsextHostname_.compare(tlsextHostname)); +} + +void AsyncSSLSocket::setServerName(std::string serverName) noexcept { + tlsextHostname_ = std::move(serverName); +} + +#endif // FOLLY_OPENSSL_HAS_SNI + +void AsyncSSLSocket::timeoutExpired( + std::chrono::milliseconds timeout) noexcept { + if (state_ == StateEnum::ESTABLISHED && sslState_ == STATE_ASYNC_PENDING) { + sslState_ = STATE_ERROR; + // We are expecting a callback in restartSSLAccept. The cache lookup + // and rsa-call necessarily have pointers to this ssl socket, so delay + // the cleanup until he calls us back. + } else if (state_ == StateEnum::CONNECTING) { + assert(sslState_ == STATE_CONNECTING); + DestructorGuard dg(this); + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::TIMED_OUT, + "Fallback connect timed out during TFO"); + failHandshake(__func__, *ex); + } else { + assert( + state_ == StateEnum::ESTABLISHED && + (sslState_ == STATE_CONNECTING || sslState_ == STATE_ACCEPTING)); + DestructorGuard dg(this); + AsyncSocketException ex( + AsyncSocketException::TIMED_OUT, + folly::sformat( + "SSL {} timed out after {}ms", + (sslState_ == STATE_CONNECTING) ? "connect" : "accept", + timeout.count())); + failHandshake(__func__, ex); + } +} + +int AsyncSSLSocket::getSSLExDataIndex() { + static auto index = SSL_get_ex_new_index( + 0, (void*)"AsyncSSLSocket data index", nullptr, nullptr, nullptr); + return index; +} + +AsyncSSLSocket* AsyncSSLSocket::getFromSSL(const SSL* ssl) { + return static_cast<AsyncSSLSocket*>( + SSL_get_ex_data(ssl, getSSLExDataIndex())); +} + +void AsyncSSLSocket::failHandshake( + const char* /* fn */, + const AsyncSocketException& ex) { + startFail(); + if (handshakeTimeout_.isScheduled()) { + handshakeTimeout_.cancelTimeout(); + } + invokeHandshakeErr(ex); + finishFail(); +} + +void AsyncSSLSocket::invokeHandshakeErr(const AsyncSocketException& ex) { + handshakeEndTime_ = std::chrono::steady_clock::now(); + if (handshakeCallback_ != nullptr) { + HandshakeCB* callback = handshakeCallback_; + handshakeCallback_ = nullptr; + callback->handshakeErr(this, ex); + } +} + +void AsyncSSLSocket::invokeHandshakeCB() { + handshakeEndTime_ = std::chrono::steady_clock::now(); + if (handshakeTimeout_.isScheduled()) { + handshakeTimeout_.cancelTimeout(); + } + if (handshakeCallback_) { + HandshakeCB* callback = handshakeCallback_; + handshakeCallback_ = nullptr; + callback->handshakeSuc(this); + } +} + +void AsyncSSLSocket::connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + int timeout, + const SocketOptionMap& options, + const folly::SocketAddress& bindAddr) noexcept { + auto timeoutChrono = std::chrono::milliseconds(timeout); + connect(callback, address, timeoutChrono, timeoutChrono, options, bindAddr); +} + +void AsyncSSLSocket::connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + std::chrono::milliseconds connectTimeout, + std::chrono::milliseconds totalConnectTimeout, + const SocketOptionMap& options, + const folly::SocketAddress& bindAddr) noexcept { + assert(!server_); + assert(state_ == StateEnum::UNINIT); + assert(sslState_ == STATE_UNINIT || sslState_ == STATE_UNENCRYPTED); + noTransparentTls_ = true; + totalConnectTimeout_ = totalConnectTimeout; + if (sslState_ != STATE_UNENCRYPTED) { + allocatedConnectCallback_ = + new AsyncSSLSocketConnector(this, callback, totalConnectTimeout); + callback = allocatedConnectCallback_; + } + AsyncSocket::connect( + callback, address, int(connectTimeout.count()), options, bindAddr); +} + +void AsyncSSLSocket::cancelConnect() { + if (connectCallback_ && allocatedConnectCallback_) { + // Since the connect callback won't be called, clean it up. + delete allocatedConnectCallback_; + allocatedConnectCallback_ = nullptr; + connectCallback_ = nullptr; + } + AsyncSocket::cancelConnect(); +} + +bool AsyncSSLSocket::needsPeerVerification() const { + if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::USE_CTX) { + return ctx_->needsPeerVerification(); + } + return ( + verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY || + verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT); +} + +bool AsyncSSLSocket::applyVerificationOptions(const ssl::SSLUniquePtr& ssl) { + // apply the settings specified in verifyPeer_ + if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::USE_CTX) { + if (ctx_->needsPeerVerification()) { + if (ctx_->checkPeerName()) { +#if FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 + return false; +#else + std::string peerNameToVerify = !ctx_->peerFixedName().empty() + ? ctx_->peerFixedName() + : tlsextHostname_; + + X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get()); + if (!X509_VERIFY_PARAM_set1_host( + param, peerNameToVerify.c_str(), peerNameToVerify.length())) { + return false; + } +#endif // FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 + } + + SSL_set_verify( + ssl.get(), + ctx_->getVerificationMode(), + AsyncSSLSocket::sslVerifyCallback); + } + } else { + if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY || + verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT) { + SSL_set_verify( + ssl.get(), + SSLContext::getVerificationMode(verifyPeer_), + AsyncSSLSocket::sslVerifyCallback); + } + } + + return true; +} + +bool AsyncSSLSocket::setupSSLBio() { + auto sslBio = BIO_new(getSSLBioMethod()); + + if (!sslBio) { + return false; + } + + OpenSSLUtils::setBioAppData(sslBio, this); + OpenSSLUtils::setBioFd(sslBio, fd_, BIO_NOCLOSE); + SSL_set_bio(ssl_.get(), sslBio, sslBio); + return true; +} + +void AsyncSSLSocket::sslConn( + HandshakeCB* callback, + std::chrono::milliseconds timeout, + const SSLContext::SSLVerifyPeerEnum& verifyPeer) { + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + // Cache local and remote socket addresses to keep them available + // after socket file descriptor is closed. + if (cacheAddrOnFailure_) { + cacheAddresses(); + } + + verifyPeer_ = verifyPeer; + + // Make sure we're in the uninitialized state + if (server_ || + (sslState_ != STATE_UNINIT && sslState_ != STATE_UNENCRYPTED) || + handshakeCallback_ != nullptr) { + return invalidState(callback); + } + + sslState_ = STATE_CONNECTING; + handshakeCallback_ = callback; + + try { + ssl_.reset(ctx_->createSSL()); + } catch (std::exception& e) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, + "error calling SSLContext::createSSL()"); + LOG(ERROR) << "AsyncSSLSocket::sslConn(this=" << this << ", fd=" << fd_ + << "): " << e.what(); + return failHandshake(__func__, *ex); + } + + if (!setupSSLBio()) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, "error creating SSL bio"); + return failHandshake(__func__, *ex); + } + + if (!applyVerificationOptions(ssl_)) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, + "error applying the SSL verification options"); + return failHandshake(__func__, *ex); + } + + if (sslSession_ != nullptr) { + sessionResumptionAttempted_ = true; + SSL_set_session(ssl_.get(), sslSession_); + SSL_SESSION_free(sslSession_); + sslSession_ = nullptr; + } +#if FOLLY_OPENSSL_HAS_SNI + if (!tlsextHostname_.empty()) { + SSL_set_tlsext_host_name(ssl_.get(), tlsextHostname_.c_str()); + } +#endif + + SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this); + + handshakeConnectTimeout_ = timeout; + startSSLConnect(); +} + +// This could be called multiple times, during normal ssl connections +// and after TFO fallback. +void AsyncSSLSocket::startSSLConnect() { + handshakeStartTime_ = std::chrono::steady_clock::now(); + // Make end time at least >= start time. + handshakeEndTime_ = handshakeStartTime_; + if (handshakeConnectTimeout_ > std::chrono::milliseconds::zero()) { + handshakeTimeout_.scheduleTimeout(handshakeConnectTimeout_); + } + handleConnect(); +} + +SSL_SESSION* AsyncSSLSocket::getSSLSession() { + if (ssl_ != nullptr && sslState_ == STATE_ESTABLISHED) { + return SSL_get1_session(ssl_.get()); + } + + return sslSession_; +} + +const SSL* AsyncSSLSocket::getSSL() const { + return ssl_.get(); +} + +void AsyncSSLSocket::setSSLSession(SSL_SESSION* session, bool takeOwnership) { + if (sslSession_) { + SSL_SESSION_free(sslSession_); + } + sslSession_ = session; + if (!takeOwnership && session != nullptr) { + // Increment the reference count + // This API exists in BoringSSL and OpenSSL 1.1.0 + SSL_SESSION_up_ref(session); + } +} + +void AsyncSSLSocket::getSelectedNextProtocol( + const unsigned char** protoName, + unsigned* protoLen) const { + if (!getSelectedNextProtocolNoThrow(protoName, protoLen)) { + throw AsyncSocketException( + AsyncSocketException::NOT_SUPPORTED, "ALPN not supported"); + } +} + +bool AsyncSSLSocket::getSelectedNextProtocolNoThrow( + const unsigned char** protoName, + unsigned* protoLen) const { + *protoName = nullptr; + *protoLen = 0; +#if FOLLY_OPENSSL_HAS_ALPN + SSL_get0_alpn_selected(ssl_.get(), protoName, protoLen); + return true; +#else + return false; +#endif +} + +bool AsyncSSLSocket::getSSLSessionReused() const { + if (ssl_ != nullptr && sslState_ == STATE_ESTABLISHED) { + return SSL_session_reused(ssl_.get()); + } + return false; +} + +const char* AsyncSSLSocket::getNegotiatedCipherName() const { + return (ssl_ != nullptr) ? SSL_get_cipher_name(ssl_.get()) : nullptr; +} + +/* static */ +const char* AsyncSSLSocket::getSSLServerNameFromSSL(SSL* ssl) { + if (ssl == nullptr) { + return nullptr; + } +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); +#else + return nullptr; +#endif +} + +const char* AsyncSSLSocket::getSSLServerName() const { + if (clientHelloInfo_ && !clientHelloInfo_->clientHelloSNIHostname_.empty()) { + return clientHelloInfo_->clientHelloSNIHostname_.c_str(); + } +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + return getSSLServerNameFromSSL(ssl_.get()); +#else + throw AsyncSocketException( + AsyncSocketException::NOT_SUPPORTED, "SNI not supported"); +#endif +} + +const char* AsyncSSLSocket::getSSLServerNameNoThrow() const { + if (clientHelloInfo_ && !clientHelloInfo_->clientHelloSNIHostname_.empty()) { + return clientHelloInfo_->clientHelloSNIHostname_.c_str(); + } + return getSSLServerNameFromSSL(ssl_.get()); +} + +int AsyncSSLSocket::getSSLVersion() const { + return (ssl_ != nullptr) ? SSL_version(ssl_.get()) : 0; +} + +const char* AsyncSSLSocket::getSSLCertSigAlgName() const { + X509* cert = (ssl_ != nullptr) ? SSL_get_certificate(ssl_.get()) : nullptr; + if (cert) { + int nid = X509_get_signature_nid(cert); + return OBJ_nid2ln(nid); + } + return nullptr; +} + +int AsyncSSLSocket::getSSLCertSize() const { + int certSize = 0; + X509* cert = (ssl_ != nullptr) ? SSL_get_certificate(ssl_.get()) : nullptr; + if (cert) { + EVP_PKEY* key = X509_get_pubkey(cert); + certSize = EVP_PKEY_bits(key); + EVP_PKEY_free(key); + } + return certSize; +} + +const AsyncTransportCertificate* AsyncSSLSocket::getPeerCertificate() const { + if (peerCertData_) { + return peerCertData_.get(); + } + if (ssl_ != nullptr) { + auto peerX509 = SSL_get_peer_certificate(ssl_.get()); + if (peerX509) { + // already up ref'd + folly::ssl::X509UniquePtr peer(peerX509); + auto cn = OpenSSLUtils::getCommonName(peerX509); + peerCertData_ = std::make_unique<BasicTransportCertificate>( + std::move(cn), std::move(peer)); + } + } + return peerCertData_.get(); +} + +const AsyncTransportCertificate* AsyncSSLSocket::getSelfCertificate() const { + if (selfCertData_) { + return selfCertData_.get(); + } + if (ssl_ != nullptr) { + auto selfX509 = SSL_get_certificate(ssl_.get()); + if (selfX509) { + // need to upref + X509_up_ref(selfX509); + folly::ssl::X509UniquePtr peer(selfX509); + auto cn = OpenSSLUtils::getCommonName(selfX509); + selfCertData_ = std::make_unique<BasicTransportCertificate>( + std::move(cn), std::move(peer)); + } + } + return selfCertData_.get(); +} + +bool AsyncSSLSocket::willBlock( + int ret, + int* sslErrorOut, + unsigned long* errErrorOut) noexcept { + *errErrorOut = 0; + int error = *sslErrorOut = SSL_get_error(ssl_.get(), ret); + if (error == SSL_ERROR_WANT_READ) { + // Register for read event if not already. + updateEventRegistration(EventHandler::READ, EventHandler::WRITE); + return true; + } + if (error == SSL_ERROR_WANT_WRITE) { + VLOG(3) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_ << "): " + << "SSL_ERROR_WANT_WRITE"; + // Register for write event if not already. + updateEventRegistration(EventHandler::WRITE, EventHandler::READ); + return true; + } + if ((false +#ifdef SSL_ERROR_WANT_ASYNC // OpenSSL 1.1.0 Async API + || error == SSL_ERROR_WANT_ASYNC +#endif + )) { + // An asynchronous request has been kicked off. On completion, it will + // invoke a callback to re-call handleAccept + sslState_ = STATE_ASYNC_PENDING; + + // Unregister for all events while blocked here + updateEventRegistration( + EventHandler::NONE, EventHandler::READ | EventHandler::WRITE); + +#ifdef SSL_ERROR_WANT_ASYNC + if (error == SSL_ERROR_WANT_ASYNC) { + size_t numfds; + if (SSL_get_all_async_fds(ssl_.get(), nullptr, &numfds) <= 0) { + VLOG(4) << "SSL_ERROR_WANT_ASYNC but no async FDs set!"; + return false; + } + if (numfds != 1) { + VLOG(4) << "SSL_ERROR_WANT_ASYNC expected exactly 1 async fd, got " + << numfds; + return false; + } + OSSL_ASYNC_FD ofd; // This should just be an int in POSIX + if (SSL_get_all_async_fds(ssl_.get(), &ofd, &numfds) <= 0) { + VLOG(4) << "SSL_ERROR_WANT_ASYNC cant get async fd"; + return false; + } + + // On POSIX systems, OSSL_ASYNC_FD is type int, but on win32 + // it has type HANDLE. + // Our NetworkSocket::native_handle_type is type SOCKET on + // win32, which means that we need to explicitly construct + // a native handle type to pass to the constructor. + auto native_handle = NetworkSocket::native_handle_type(ofd); + + auto asyncPipeReader = + AsyncPipeReader::newReader(eventBase_, NetworkSocket(native_handle)); + auto asyncPipeReaderPtr = asyncPipeReader.get(); + if (!asyncOperationFinishCallback_) { + asyncOperationFinishCallback_.reset( + new DefaultOpenSSLAsyncFinishCallback( + std::move(asyncPipeReader), this, DestructorGuard(this))); + } + asyncPipeReaderPtr->setReadCB(asyncOperationFinishCallback_.get()); + } +#endif + + // The timeout (if set) keeps running here + return true; + } else { + unsigned long lastError = *errErrorOut = ERR_get_error(); + VLOG(6) << "AsyncSSLSocket(fd=" << fd_ << ", " + << "state=" << state_ << ", " + << "sslState=" << sslState_ << ", " + << "events=" << std::hex << eventFlags_ << "): " + << "SSL error: " << error << ", " + << "errno: " << errno << ", " + << "ret: " << ret << ", " + << "read: " << BIO_number_read(SSL_get_rbio(ssl_.get())) << ", " + << "written: " << BIO_number_written(SSL_get_wbio(ssl_.get())) + << ", " + << "func: " << ERR_func_error_string(lastError) << ", " + << "reason: " << ERR_reason_error_string(lastError); + return false; + } +} + +void AsyncSSLSocket::checkForImmediateRead() noexcept { + // openssl may have buffered data that it read from the socket already. + // In this case we have to process it immediately, rather than waiting for + // the socket to become readable again. + if (ssl_ != nullptr && SSL_pending(ssl_.get()) > 0) { + AsyncSocket::handleRead(); + } else { + AsyncSocket::checkForImmediateRead(); + } +} + +void AsyncSSLSocket::restartSSLAccept() { + VLOG(3) << "AsyncSSLSocket::restartSSLAccept() this=" << this + << ", fd=" << fd_ << ", state=" << int(state_) << ", " + << "sslState=" << sslState_ << ", events=" << eventFlags_; + DestructorGuard dg(this); + assert( + sslState_ == STATE_ASYNC_PENDING || sslState_ == STATE_ERROR || + sslState_ == STATE_CLOSED); + if (sslState_ == STATE_CLOSED) { + // I sure hope whoever closed this socket didn't delete it already, + // but this is not strictly speaking an error + return; + } + if (sslState_ == STATE_ERROR) { + // go straight to fail if timeout expired during lookup + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::TIMED_OUT, "SSL accept timed out"); + failHandshake(__func__, *ex); + return; + } + sslState_ = STATE_ACCEPTING; + this->handleAccept(); +} + +void AsyncSSLSocket::handleAccept() noexcept { + VLOG(3) << "AsyncSSLSocket::handleAccept() this=" << this << ", fd=" << fd_ + << ", state=" << int(state_) << ", " + << "sslState=" << sslState_ << ", events=" << eventFlags_; + assert(server_); + assert(state_ == StateEnum::ESTABLISHED && sslState_ == STATE_ACCEPTING); + if (!ssl_) { + /* lazily create the SSL structure */ + try { + ssl_.reset(ctx_->createSSL()); + } catch (std::exception& e) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, + "error calling SSLContext::createSSL()"); + LOG(ERROR) << "AsyncSSLSocket::handleAccept(this=" << this + << ", fd=" << fd_ << "): " << e.what(); + return failHandshake(__func__, *ex); + } + + if (!setupSSLBio()) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, "error creating write bio"); + return failHandshake(__func__, *ex); + } + + SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this); + + if (!applyVerificationOptions(ssl_)) { + sslState_ = STATE_ERROR; + static const Indestructible<AsyncSocketException> ex( + AsyncSocketException::INTERNAL_ERROR, + "error applying the SSL verification options"); + return failHandshake(__func__, *ex); + } + } + + if (server_ && parseClientHello_) { + SSL_set_msg_callback( + ssl_.get(), &AsyncSSLSocket::clientHelloParsingCallback); + SSL_set_msg_callback_arg(ssl_.get(), this); + } + + DCHECK(ctx_->sslAcceptRunner()); + updateEventRegistration( + EventHandler::NONE, EventHandler::READ | EventHandler::WRITE); + DelayedDestruction::DestructorGuard dg(this); + ctx_->sslAcceptRunner()->run( + [this, dg]() { + waitingOnAccept_ = true; + return SSL_accept(ssl_.get()); + }, + [this, dg](int ret) { + waitingOnAccept_ = false; + handleReturnFromSSLAccept(ret); + }); +} + +void AsyncSSLSocket::handleReturnFromSSLAccept(int ret) { + if (sslState_ != STATE_ACCEPTING) { + return; + } + + if (ret <= 0) { + VLOG(3) << "SSL_accept returned: " << ret; + int sslError; + unsigned long errError; + int errnoCopy = errno; + if (willBlock(ret, &sslError, &errError)) { + return; + } else { + sslState_ = STATE_ERROR; + SSLException ex(sslError, errError, ret, errnoCopy); + return failHandshake(__func__, ex); + } + } + + handshakeComplete_ = true; + updateEventRegistration(0, EventHandler::READ | EventHandler::WRITE); + + // Move into STATE_ESTABLISHED in the normal case that we are in + // STATE_ACCEPTING. + sslState_ = STATE_ESTABLISHED; + + VLOG(3) << "AsyncSSLSocket " << this << ": fd " << fd_ + << " successfully accepted; state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_; + + // Remember the EventBase we are attached to, before we start invoking any + // callbacks (since the callbacks may call detachEventBase()). + EventBase* originalEventBase = eventBase_; + + // Call the accept callback. + invokeHandshakeCB(); + + // Note that the accept callback may have changed our state. + // (set or unset the read callback, called write(), closed the socket, etc.) + // The following code needs to handle these situations correctly. + // + // If the socket has been closed, readCallback_ and writeReqHead_ will + // always be nullptr, so that will prevent us from trying to read or write. + // + // The main thing to check for is if eventBase_ is still originalEventBase. + // If not, we have been detached from this event base, so we shouldn't + // perform any more operations. + if (eventBase_ != originalEventBase) { + return; + } + + AsyncSocket::handleInitialReadWrite(); +} + +void AsyncSSLSocket::handleConnect() noexcept { + VLOG(3) << "AsyncSSLSocket::handleConnect() this=" << this << ", fd=" << fd_ + << ", state=" << int(state_) << ", " + << "sslState=" << sslState_ << ", events=" << eventFlags_; + assert(!server_); + if (state_ < StateEnum::ESTABLISHED) { + return AsyncSocket::handleConnect(); + } + + assert( + (state_ == StateEnum::FAST_OPEN || state_ == StateEnum::ESTABLISHED) && + sslState_ == STATE_CONNECTING); + assert(ssl_); + + auto originalState = state_; + int ret = SSL_connect(ssl_.get()); + if (ret <= 0) { + int sslError; + unsigned long errError; + int errnoCopy = errno; + if (willBlock(ret, &sslError, &errError)) { + // We fell back to connecting state due to TFO + if (state_ == StateEnum::CONNECTING) { + DCHECK_EQ(StateEnum::FAST_OPEN, originalState); + if (handshakeTimeout_.isScheduled()) { + handshakeTimeout_.cancelTimeout(); + } + } + return; + } else { + sslState_ = STATE_ERROR; + SSLException ex(sslError, errError, ret, errnoCopy); + return failHandshake(__func__, ex); + } + } + + handshakeComplete_ = true; + updateEventRegistration(0, EventHandler::READ | EventHandler::WRITE); + + // Move into STATE_ESTABLISHED in the normal case that we are in + // STATE_CONNECTING. + sslState_ = STATE_ESTABLISHED; + + VLOG(3) << "AsyncSSLSocket " << this << ": " + << "fd " << fd_ << " successfully connected; " + << "state=" << int(state_) << ", sslState=" << sslState_ + << ", events=" << eventFlags_; + + // Remember the EventBase we are attached to, before we start invoking any + // callbacks (since the callbacks may call detachEventBase()). + EventBase* originalEventBase = eventBase_; + + // Call the handshake callback. + invokeHandshakeCB(); + + // Note that the connect callback may have changed our state. + // (set or unset the read callback, called write(), closed the socket, etc.) + // The following code needs to handle these situations correctly. + // + // If the socket has been closed, readCallback_ and writeReqHead_ will + // always be nullptr, so that will prevent us from trying to read or write. + // + // The main thing to check for is if eventBase_ is still originalEventBase. + // If not, we have been detached from this event base, so we shouldn't + // perform any more operations. + if (eventBase_ != originalEventBase) { + return; + } + + AsyncSocket::handleInitialReadWrite(); +} + +void AsyncSSLSocket::invokeConnectErr(const AsyncSocketException& ex) { + connectionTimeout_.cancelTimeout(); + AsyncSocket::invokeConnectErr(ex); + if (sslState_ == SSLStateEnum::STATE_CONNECTING) { + if (handshakeTimeout_.isScheduled()) { + handshakeTimeout_.cancelTimeout(); + } + // If we fell back to connecting state during TFO and the connection + // failed, it would be an SSL failure as well. + invokeHandshakeErr(ex); + } +} + +void AsyncSSLSocket::invokeConnectSuccess() { + connectionTimeout_.cancelTimeout(); + if (sslState_ == SSLStateEnum::STATE_CONNECTING) { + assert(tfoAttempted_); + // If we failed TFO, we'd fall back to trying to connect the socket, + // to setup things like timeouts. + startSSLConnect(); + } + // still invoke the base class since it re-sets the connect time. + AsyncSocket::invokeConnectSuccess(); +} + +void AsyncSSLSocket::scheduleConnectTimeout() { + if (sslState_ == SSLStateEnum::STATE_CONNECTING) { + // We fell back from TFO, and need to set the timeouts. + // We will not have a connect callback in this case, thus if the timer + // expires we would have no-one to notify. + // Thus we should reset even the connect timers to point to the handshake + // timeouts. + assert(connectCallback_ == nullptr); + // We use a different connect timeout here than the handshake timeout, so + // that we can disambiguate the 2 timers. + if (connectTimeout_.count() > 0) { + if (!connectionTimeout_.scheduleTimeout(connectTimeout_)) { + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to schedule AsyncSSLSocket connect timeout")); + } + } + return; + } + AsyncSocket::scheduleConnectTimeout(); +} + +void AsyncSSLSocket::handleRead() noexcept { + VLOG(5) << "AsyncSSLSocket::handleRead() this=" << this << ", fd=" << fd_ + << ", state=" << int(state_) << ", " + << "sslState=" << sslState_ << ", events=" << eventFlags_; + if (state_ < StateEnum::ESTABLISHED) { + return AsyncSocket::handleRead(); + } + + if (sslState_ == STATE_ACCEPTING) { + assert(server_); + handleAccept(); + return; + } else if (sslState_ == STATE_CONNECTING) { + assert(!server_); + handleConnect(); + return; + } + + // Normal read + AsyncSocket::handleRead(); +} + +AsyncSocket::ReadResult +AsyncSSLSocket::performRead(void** buf, size_t* buflen, size_t* offset) { + VLOG(4) << "AsyncSSLSocket::performRead() this=" << this << ", buf=" << *buf + << ", buflen=" << *buflen; + + if (sslState_ == STATE_UNENCRYPTED) { + return AsyncSocket::performRead(buf, buflen, offset); + } + + int numToRead = 0; + if (*buflen > std::numeric_limits<int>::max()) { + numToRead = std::numeric_limits<int>::max(); + VLOG(4) << "Clamping SSL_read to " << numToRead; + } else { + numToRead = int(*buflen); + } + int bytes = SSL_read(ssl_.get(), *buf, numToRead); + + if (server_ && renegotiateAttempted_) { + LOG(ERROR) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslstate=" << sslState_ << ", events=" << eventFlags_ + << "): client intitiated SSL renegotiation not permitted"; + return ReadResult( + READ_ERROR, + std::make_unique<SSLException>(SSLError::CLIENT_RENEGOTIATION)); + } + if (bytes <= 0) { + int error = SSL_get_error(ssl_.get(), bytes); + if (error == SSL_ERROR_WANT_READ) { + // The caller will register for read event if not already. + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return ReadResult(READ_BLOCKING); + } else { + return ReadResult(READ_ERROR); + } + } else if (error == SSL_ERROR_WANT_WRITE) { + // TODO: Even though we are attempting to read data, SSL_read() may + // need to write data if renegotiation is being performed. We currently + // don't support this and just fail the read. + LOG(ERROR) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_ + << "): unsupported SSL renegotiation during read"; + return ReadResult( + READ_ERROR, + std::make_unique<SSLException>(SSLError::INVALID_RENEGOTIATION)); + } else { + if (zero_return(error, bytes, errno)) { + return ReadResult(bytes); + } + auto errError = ERR_get_error(); + VLOG(6) << "AsyncSSLSocket(fd=" << fd_ << ", " + << "state=" << state_ << ", " + << "sslState=" << sslState_ << ", " + << "events=" << std::hex << eventFlags_ << "): " + << "bytes: " << bytes << ", " + << "error: " << error << ", " + << "errno: " << errno << ", " + << "func: " << ERR_func_error_string(errError) << ", " + << "reason: " << ERR_reason_error_string(errError); + return ReadResult( + READ_ERROR, + std::make_unique<SSLException>(error, errError, bytes, errno)); + } + } else { + appBytesReceived_ += bytes; + return ReadResult(bytes); + } +} + +void AsyncSSLSocket::handleWrite() noexcept { + VLOG(5) << "AsyncSSLSocket::handleWrite() this=" << this << ", fd=" << fd_ + << ", state=" << int(state_) << ", " + << "sslState=" << sslState_ << ", events=" << eventFlags_; + if (state_ < StateEnum::ESTABLISHED) { + return AsyncSocket::handleWrite(); + } + + if (sslState_ == STATE_ACCEPTING) { + assert(server_); + handleAccept(); + return; + } + + if (sslState_ == STATE_CONNECTING) { + assert(!server_); + handleConnect(); + return; + } + + // Normal write + AsyncSocket::handleWrite(); +} + +AsyncSocket::WriteResult AsyncSSLSocket::interpretSSLError(int rc, int error) { + if (error == SSL_ERROR_WANT_READ) { + // Even though we are attempting to write data, SSL_write() may + // need to read data if renegotiation is being performed. We currently + // don't support this and just fail the write. + LOG(ERROR) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_ + << "): " + << "unsupported SSL renegotiation during write"; + return WriteResult( + WRITE_ERROR, + std::make_unique<SSLException>(SSLError::INVALID_RENEGOTIATION)); + } else { + auto errError = ERR_get_error(); + VLOG(3) << "ERROR: AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_ << "): " + << "SSL error: " << error << ", errno: " << errno + << ", func: " << ERR_func_error_string(errError) + << ", reason: " << ERR_reason_error_string(errError); + return WriteResult( + WRITE_ERROR, + std::make_unique<SSLException>(error, errError, rc, errno)); + } +} + +AsyncSocket::WriteResult AsyncSSLSocket::performWrite( + const iovec* vec, + uint32_t count, + WriteFlags flags, + uint32_t* countWritten, + uint32_t* partialWritten) { + if (sslState_ == STATE_UNENCRYPTED) { + return AsyncSocket::performWrite( + vec, count, flags, countWritten, partialWritten); + } + if (sslState_ != STATE_ESTABLISHED) { + LOG(ERROR) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_) + << ", sslState=" << sslState_ << ", events=" << eventFlags_ + << "): " + << "TODO: AsyncSSLSocket currently does not support calling " + << "write() before the handshake has fully completed"; + return WriteResult( + WRITE_ERROR, std::make_unique<SSLException>(SSLError::EARLY_WRITE)); + } + + // Declare a buffer used to hold small write requests. It could point to a + // memory block either on stack or on heap. If it is on heap, we release it + // manually when scope exits + char* combinedBuf{nullptr}; + SCOPE_EXIT { + // Note, always keep this check consistent with what we do below + if (combinedBuf != nullptr && minWriteSize_ > MAX_STACK_BUF_SIZE) { + delete[] combinedBuf; + } + }; + + *countWritten = 0; + *partialWritten = 0; + ssize_t totalWritten = 0; + size_t bytesStolenFromNextBuffer = 0; + for (uint32_t i = 0; i < count; i++) { + const iovec* v = vec + i; + size_t offset = bytesStolenFromNextBuffer; + bytesStolenFromNextBuffer = 0; + size_t len = v->iov_len - offset; + const void* buf; + if (len == 0) { + (*countWritten)++; + continue; + } + buf = ((const char*)v->iov_base) + offset; + + ssize_t bytes; + uint32_t buffersStolen = 0; + auto sslWriteBuf = buf; + if ((len < minWriteSize_) && ((i + 1) < count)) { + // Combine this buffer with part or all of the next buffers in + // order to avoid really small-grained calls to SSL_write(). + // Each call to SSL_write() produces a separate record in + // the egress SSL stream, and we've found that some low-end + // mobile clients can't handle receiving an HTTP response + // header and the first part of the response body in two + // separate SSL records (even if those two records are in + // the same TCP packet). + + if (combinedBuf == nullptr) { + if (minWriteSize_ > MAX_STACK_BUF_SIZE) { + // Allocate the buffer on heap + combinedBuf = new char[minWriteSize_]; + } else { + // Allocate the buffer on stack + combinedBuf = (char*)alloca(minWriteSize_); + } + } + assert(combinedBuf != nullptr); + sslWriteBuf = combinedBuf; + + memcpy(combinedBuf, buf, len); + do { + // INVARIANT: i + buffersStolen == complete chunks serialized + uint32_t nextIndex = i + buffersStolen + 1; + bytesStolenFromNextBuffer = + std::min(vec[nextIndex].iov_len, minWriteSize_ - len); + if (bytesStolenFromNextBuffer > 0) { + assert(vec[nextIndex].iov_base != nullptr); + ::memcpy( + combinedBuf + len, + vec[nextIndex].iov_base, + bytesStolenFromNextBuffer); + } + len += bytesStolenFromNextBuffer; + if (bytesStolenFromNextBuffer < vec[nextIndex].iov_len) { + // couldn't steal the whole buffer + break; + } else { + bytesStolenFromNextBuffer = 0; + buffersStolen++; + } + } while ((i + buffersStolen + 1) < count && (len < minWriteSize_)); + } + + // Advance any empty buffers immediately after. + if (bytesStolenFromNextBuffer == 0) { + while ((i + buffersStolen + 1) < count && + vec[i + buffersStolen + 1].iov_len == 0) { + buffersStolen++; + } + } + + // cork the current write if the original flags included CORK or if there + // are remaining iovec to write + corkCurrentWrite_ = + isSet(flags, WriteFlags::CORK) || (i + buffersStolen + 1 < count); + + // track the EoR if: + // (1) there are write flags that require EoR tracking (EOR / TIMESTAMP_TX) + // (2) if the buffer includes the EOR byte + appEorByteWriteFlags_ = flags & kEorRelevantWriteFlags; + bool trackEor = appEorByteWriteFlags_ != folly::WriteFlags::NONE && + (i + buffersStolen + 1 == count); + bytes = eorAwareSSLWrite(ssl_, sslWriteBuf, int(len), trackEor); + + if (bytes <= 0) { + int error = SSL_get_error(ssl_.get(), int(bytes)); + if (error == SSL_ERROR_WANT_WRITE) { + // The caller will register for write event if not already. + *partialWritten = uint32_t(offset); + return WriteResult(totalWritten); + } + return interpretSSLError(int(bytes), error); + } + + totalWritten += bytes; + + if (bytes == (ssize_t)len) { + // The full iovec is written. + (*countWritten) += 1 + buffersStolen; + i += buffersStolen; + // continue + } else { + bytes += offset; // adjust bytes to account for all of v + while (bytes >= (ssize_t)v->iov_len) { + // We combined this buf with part or all of the next one, and + // we managed to write all of this buf but not all of the bytes + // from the next one that we'd hoped to write. + bytes -= v->iov_len; + (*countWritten)++; + v = &(vec[++i]); + } + *partialWritten = uint32_t(bytes); + return WriteResult(totalWritten); + } + } + + return WriteResult(totalWritten); +} + +int AsyncSSLSocket::eorAwareSSLWrite( + const ssl::SSLUniquePtr& ssl, + const void* buf, + int n, + bool eor) { + if (eor && isEorTrackingEnabled()) { + if (appEorByteNo_) { + // cannot track for more than one app byte EOR + CHECK(appEorByteNo_ == appBytesWritten_ + n); + } else { + appEorByteNo_ = appBytesWritten_ + n; + } + + // 1. It is fine to keep updating minEorRawByteNo_. + // 2. It is _min_ in the sense that SSL record will add some overhead. + minEorRawByteNo_ = getRawBytesWritten() + n; + } + + n = sslWriteImpl(ssl.get(), buf, n); + if (n > 0) { + appBytesWritten_ += n; + if (appEorByteNo_) { + if (getRawBytesWritten() >= minEorRawByteNo_) { + minEorRawByteNo_ = 0; + } + if (appBytesWritten_ == appEorByteNo_) { + appEorByteNo_ = 0; + appEorByteWriteFlags_ = {}; + } else { + CHECK(appBytesWritten_ < appEorByteNo_); + } + } + } + return n; +} + +void AsyncSSLSocket::sslInfoCallback(const SSL* ssl, int where, int ret) { + AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl); + if (sslSocket->handshakeComplete_ && (where & SSL_CB_HANDSHAKE_START)) { + sslSocket->renegotiateAttempted_ = true; + } + if (sslSocket->handshakeComplete_ && (where & SSL_CB_WRITE_ALERT)) { + const char* desc = SSL_alert_desc_string(ret); + if (desc && strcmp(desc, "NR") == 0) { + sslSocket->renegotiateAttempted_ = true; + } + } + if (where & SSL_CB_READ_ALERT) { + const char* type = SSL_alert_type_string(ret); + if (type) { + const char* desc = SSL_alert_desc_string(ret); + sslSocket->alertsReceived_.emplace_back( + *type, StringPiece(desc, std::strlen(desc))); + } + } +} + +int AsyncSSLSocket::bioWrite(BIO* b, const char* in, int inl) { + struct msghdr msg; + struct iovec iov; + AsyncSSLSocket* tsslSock; + + iov.iov_base = const_cast<char*>(in); + iov.iov_len = size_t(inl); + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + auto appData = OpenSSLUtils::getBioAppData(b); + CHECK(appData); + + tsslSock = reinterpret_cast<AsyncSSLSocket*>(appData); + CHECK(tsslSock); + + WriteFlags flags = WriteFlags::NONE; + if (tsslSock->isEorTrackingEnabled() && tsslSock->minEorRawByteNo_ && + tsslSock->minEorRawByteNo_ <= BIO_number_written(b) + inl) { + flags |= tsslSock->appEorByteWriteFlags_; + } + + if (tsslSock->corkCurrentWrite_) { + flags |= WriteFlags::CORK; + } + + int msg_flags = tsslSock->getSendMsgParamsCB()->getFlags( + flags, false /*zeroCopyEnabled*/); + msg.msg_controllen = + tsslSock->getSendMsgParamsCB()->getAncillaryDataSize(flags); + CHECK_GE( + AsyncSocket::SendMsgParamsCallback::maxAncillaryDataSize, + msg.msg_controllen); + if (msg.msg_controllen != 0) { + msg.msg_control = reinterpret_cast<char*>(alloca(msg.msg_controllen)); + tsslSock->getSendMsgParamsCB()->getAncillaryData(flags, msg.msg_control); + } + + auto result = + tsslSock->sendSocketMessage(OpenSSLUtils::getBioFd(b), &msg, msg_flags); + BIO_clear_retry_flags(b); + if (!result.exception && result.writeReturn <= 0) { + if (OpenSSLUtils::getBioShouldRetryWrite(int(result.writeReturn))) { + BIO_set_retry_write(b); + } + } + return int(result.writeReturn); +} + +int AsyncSSLSocket::bioRead(BIO* b, char* out, int outl) { + if (!out) { + return 0; + } + BIO_clear_retry_flags(b); + + auto appData = OpenSSLUtils::getBioAppData(b); + CHECK(appData); + auto sslSock = reinterpret_cast<AsyncSSLSocket*>(appData); + + if (sslSock->preReceivedData_ && !sslSock->preReceivedData_->empty()) { + VLOG(5) << "AsyncSSLSocket::bioRead() this=" << sslSock + << ", reading pre-received data"; + + Cursor cursor(sslSock->preReceivedData_.get()); + auto len = cursor.pullAtMost(out, outl); + + IOBufQueue queue; + queue.append(std::move(sslSock->preReceivedData_)); + queue.trimStart(len); + sslSock->preReceivedData_ = queue.move(); + return static_cast<int>(len); + } else { + auto result = int(netops::recv(OpenSSLUtils::getBioFd(b), out, outl, 0)); + if (result <= 0 && OpenSSLUtils::getBioShouldRetryWrite(result)) { + BIO_set_retry_read(b); + } + return result; + } +} + +int AsyncSSLSocket::sslVerifyCallback( + int preverifyOk, + X509_STORE_CTX* x509Ctx) { + SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data( + x509Ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + AsyncSSLSocket* self = AsyncSSLSocket::getFromSSL(ssl); + + VLOG(3) << "AsyncSSLSocket::sslVerifyCallback() this=" << self << ", " + << "fd=" << self->fd_ << ", preverifyOk=" << preverifyOk; + + return (self->handshakeCallback_) + ? self->handshakeCallback_->handshakeVer(self, preverifyOk, x509Ctx) + : preverifyOk; +} + +void AsyncSSLSocket::enableClientHelloParsing() { + parseClientHello_ = true; + clientHelloInfo_ = std::make_unique<ssl::ClientHelloInfo>(); +} + +void AsyncSSLSocket::resetClientHelloParsing(SSL* ssl) { + SSL_set_msg_callback(ssl, nullptr); + SSL_set_msg_callback_arg(ssl, nullptr); + clientHelloInfo_->clientHelloBuf_.clear(); +} + +void AsyncSSLSocket::clientHelloParsingCallback( + int written, + int /* version */, + int contentType, + const void* buf, + size_t len, + SSL* ssl, + void* arg) { + auto sock = static_cast<AsyncSSLSocket*>(arg); + if (written != 0) { + sock->resetClientHelloParsing(ssl); + return; + } + if (contentType != SSL3_RT_HANDSHAKE) { + return; + } + if (len == 0) { + return; + } + + auto& clientHelloBuf = sock->clientHelloInfo_->clientHelloBuf_; + clientHelloBuf.append(IOBuf::wrapBuffer(buf, len)); + try { + Cursor cursor(clientHelloBuf.front()); + if (cursor.read<uint8_t>() != SSL3_MT_CLIENT_HELLO) { + sock->resetClientHelloParsing(ssl); + return; + } + + if (cursor.totalLength() < 3) { + clientHelloBuf.trimEnd(len); + clientHelloBuf.append(IOBuf::copyBuffer(buf, len)); + return; + } + + uint32_t messageLength = cursor.read<uint8_t>(); + messageLength <<= 8; + messageLength |= cursor.read<uint8_t>(); + messageLength <<= 8; + messageLength |= cursor.read<uint8_t>(); + if (cursor.totalLength() < messageLength) { + clientHelloBuf.trimEnd(len); + clientHelloBuf.append(IOBuf::copyBuffer(buf, len)); + return; + } + + sock->clientHelloInfo_->clientHelloMajorVersion_ = cursor.read<uint8_t>(); + sock->clientHelloInfo_->clientHelloMinorVersion_ = cursor.read<uint8_t>(); + + cursor.skip(4); // gmt_unix_time + cursor.skip(28); // random_bytes + + cursor.skip(cursor.read<uint8_t>()); // session_id + + auto cipherSuitesLength = cursor.readBE<uint16_t>(); + for (int i = 0; i < cipherSuitesLength; i += 2) { + sock->clientHelloInfo_->clientHelloCipherSuites_.push_back( + cursor.readBE<uint16_t>()); + } + + auto compressionMethodsLength = cursor.read<uint8_t>(); + for (int i = 0; i < compressionMethodsLength; ++i) { + sock->clientHelloInfo_->clientHelloCompressionMethods_.push_back( + cursor.readBE<uint8_t>()); + } + + if (cursor.totalLength() > 0) { + auto extensionsLength = cursor.readBE<uint16_t>(); + while (extensionsLength) { + auto extensionType = + static_cast<ssl::TLSExtension>(cursor.readBE<uint16_t>()); + sock->clientHelloInfo_->clientHelloExtensions_.push_back(extensionType); + extensionsLength -= 2; + auto extensionDataLength = cursor.readBE<uint16_t>(); + extensionsLength -= 2; + extensionsLength -= extensionDataLength; + + if (extensionType == ssl::TLSExtension::SIGNATURE_ALGORITHMS) { + cursor.skip(2); + extensionDataLength -= 2; + while (extensionDataLength) { + auto hashAlg = + static_cast<ssl::HashAlgorithm>(cursor.readBE<uint8_t>()); + auto sigAlg = + static_cast<ssl::SignatureAlgorithm>(cursor.readBE<uint8_t>()); + extensionDataLength -= 2; + sock->clientHelloInfo_->clientHelloSigAlgs_.emplace_back( + hashAlg, sigAlg); + } + } else if (extensionType == ssl::TLSExtension::SUPPORTED_VERSIONS) { + cursor.skip(1); + extensionDataLength -= 1; + while (extensionDataLength) { + sock->clientHelloInfo_->clientHelloSupportedVersions_.push_back( + cursor.readBE<uint16_t>()); + extensionDataLength -= 2; + } + } else if (extensionType == ssl::TLSExtension::SERVER_NAME) { + cursor.skip(2); + extensionDataLength -= 2; + while (extensionDataLength) { + static_assert( + std::is_same< + typename std::underlying_type<ssl::NameType>::type, + uint8_t>::value, + "unexpected underlying type"); + + auto typ = static_cast<ssl::NameType>(cursor.readBE<uint8_t>()); + auto nameLength = cursor.readBE<uint16_t>(); + + if (typ == NameType::HOST_NAME && + sock->clientHelloInfo_->clientHelloSNIHostname_.empty() && + cursor.canAdvance(nameLength)) { + sock->clientHelloInfo_->clientHelloSNIHostname_ = + cursor.readFixedString(nameLength); + } else { + // Must attempt to skip |nameLength| in order to keep cursor + // in sync. If the remaining buffer length is smaller than + // nameLength, this will throw. + cursor.skip(nameLength); + } + extensionDataLength -= + sizeof(typ) + sizeof(nameLength) + nameLength; + } + } else { + cursor.skip(extensionDataLength); + } + } + } + } catch (std::out_of_range&) { + // we'll use what we found and cleanup below. + VLOG(4) << "AsyncSSLSocket::clientHelloParsingCallback(): " + << "buffer finished unexpectedly." + << " AsyncSSLSocket socket=" << sock; + } + + sock->resetClientHelloParsing(ssl); +} + +void AsyncSSLSocket::getSSLClientCiphers( + std::string& clientCiphers, + bool convertToString) const { + std::string ciphers; + + if (!parseClientHello_ || + clientHelloInfo_->clientHelloCipherSuites_.empty()) { + clientCiphers = ""; + return; + } + + bool first = true; + for (auto originalCipherCode : clientHelloInfo_->clientHelloCipherSuites_) { + if (first) { + first = false; + } else { + ciphers += ":"; + } + + bool nameFound = convertToString; + + if (convertToString) { + const auto& name = OpenSSLUtils::getCipherName(originalCipherCode); + if (name.empty()) { + nameFound = false; + } else { + ciphers += name; + } + } + + if (!nameFound) { + folly::hexlify( + std::array<uint8_t, 2>{ + {static_cast<uint8_t>((originalCipherCode >> 8) & 0xffL), + static_cast<uint8_t>(originalCipherCode & 0x00ffL)}}, + ciphers, + /* append to ciphers = */ true); + } + } + + clientCiphers = std::move(ciphers); +} + +std::string AsyncSSLSocket::getSSLClientComprMethods() const { + if (!parseClientHello_) { + return ""; + } + return folly::join(":", clientHelloInfo_->clientHelloCompressionMethods_); +} + +std::string AsyncSSLSocket::getSSLClientExts() const { + if (!parseClientHello_) { + return ""; + } + return folly::join(":", clientHelloInfo_->clientHelloExtensions_); +} + +std::string AsyncSSLSocket::getSSLClientSigAlgs() const { + if (!parseClientHello_) { + return ""; + } + + std::string sigAlgs; + sigAlgs.reserve(clientHelloInfo_->clientHelloSigAlgs_.size() * 4); + for (size_t i = 0; i < clientHelloInfo_->clientHelloSigAlgs_.size(); i++) { + if (i) { + sigAlgs.push_back(':'); + } + sigAlgs.append( + folly::to<std::string>(clientHelloInfo_->clientHelloSigAlgs_[i].first)); + sigAlgs.push_back(','); + sigAlgs.append(folly::to<std::string>( + clientHelloInfo_->clientHelloSigAlgs_[i].second)); + } + + return sigAlgs; +} + +std::string AsyncSSLSocket::getSSLClientSupportedVersions() const { + if (!parseClientHello_) { + return ""; + } + return folly::join(":", clientHelloInfo_->clientHelloSupportedVersions_); +} + +std::string AsyncSSLSocket::getSSLAlertsReceived() const { + std::string ret; + + for (const auto& alert : alertsReceived_) { + if (!ret.empty()) { + ret.append(","); + } + ret.append(folly::to<std::string>(alert.first, ": ", alert.second)); + } + + return ret; +} + +void AsyncSSLSocket::setSSLCertVerificationAlert(std::string alert) { + sslVerificationAlert_ = std::move(alert); +} + +std::string AsyncSSLSocket::getSSLCertVerificationAlert() const { + return sslVerificationAlert_; +} + +void AsyncSSLSocket::getSSLSharedCiphers(std::string& sharedCiphers) const { + char ciphersBuffer[1024]; + ciphersBuffer[0] = '\0'; + SSL_get_shared_ciphers(ssl_.get(), ciphersBuffer, sizeof(ciphersBuffer) - 1); + sharedCiphers = ciphersBuffer; +} + +void AsyncSSLSocket::getSSLServerCiphers(std::string& serverCiphers) const { + serverCiphers = SSL_get_cipher_list(ssl_.get(), 0); + int i = 1; + const char* cipher; + while ((cipher = SSL_get_cipher_list(ssl_.get(), i)) != nullptr) { + serverCiphers.append(":"); + serverCiphers.append(cipher); + i++; + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.h new file mode 100644 index 000000000..d1ad299cd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSSLSocket.h @@ -0,0 +1,977 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <iomanip> + +#include <folly/Optional.h> +#include <folly/String.h> +#include <folly/io/Cursor.h> +#include <folly/io/IOBuf.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/io/async/AsyncPipe.h> +#include <folly/io/async/AsyncSocket.h> +#include <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/SSLContext.h> +#include <folly/io/async/TimeoutManager.h> +#include <folly/io/async/ssl/OpenSSLUtils.h> +#include <folly/io/async/ssl/SSLErrors.h> +#include <folly/io/async/ssl/TLSDefinitions.h> +#include <folly/lang/Bits.h> +#include <folly/portability/OpenSSL.h> +#include <folly/portability/Sockets.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { + +class AsyncSSLSocketConnector; + +/** + * A class for performing asynchronous I/O on an SSL connection. + * + * AsyncSSLSocket allows users to asynchronously wait for data on an + * SSL connection, and to asynchronously send data. + * + * The APIs for reading and writing are intentionally asymmetric. + * Waiting for data to read is a persistent API: a callback is + * installed, and is notified whenever new data is available. It + * continues to be notified of new events until it is uninstalled. + * + * AsyncSSLSocket does not provide read timeout functionality, + * because it typically cannot determine when the timeout should be + * active. Generally, a timeout should only be enabled when + * processing is blocked waiting on data from the remote endpoint. + * For server connections, the timeout should not be active if the + * server is currently processing one or more outstanding requests for + * this connection. For client connections, the timeout should not be + * active if there are no requests pending on the connection. + * Additionally, if a client has multiple pending requests, it will + * ususally want a separate timeout for each request, rather than a + * single read timeout. + * + * The write API is fairly intuitive: a user can request to send a + * block of data, and a callback will be informed once the entire + * block has been transferred to the kernel, or on error. + * AsyncSSLSocket does provide a send timeout, since most callers + * want to give up if the remote end stops responding and no further + * progress can be made sending the data. + */ +class AsyncSSLSocket : public virtual AsyncSocket { + public: + typedef std::unique_ptr<AsyncSSLSocket, Destructor> UniquePtr; + using X509_deleter = folly::static_function_deleter<X509, &X509_free>; + + class HandshakeCB { + public: + virtual ~HandshakeCB() = default; + + /** + * handshakeVer() is invoked during handshaking to give the + * application chance to validate it's peer's certificate. + * + * Note that OpenSSL performs only rudimentary internal + * consistency verification checks by itself. Any other validation + * like whether or not the certificate was issued by a trusted CA. + * The default implementation of this callback mimics what what + * OpenSSL does internally if SSL_VERIFY_PEER is set with no + * verification callback. + * + * See the passages on verify_callback in SSL_CTX_set_verify(3) + * for more details. + */ + virtual bool handshakeVer( + AsyncSSLSocket* /*sock*/, + bool preverifyOk, + X509_STORE_CTX* /*ctx*/) noexcept { + return preverifyOk; + } + + /** + * handshakeSuc() is called when a new SSL connection is + * established, i.e., after SSL_accept/connect() returns successfully. + * + * The HandshakeCB will be uninstalled before handshakeSuc() + * is called. + * + * @param sock SSL socket on which the handshake was initiated + */ + virtual void handshakeSuc(AsyncSSLSocket* sock) noexcept = 0; + + /** + * handshakeErr() is called if an error occurs while + * establishing the SSL connection. + * + * The HandshakeCB will be uninstalled before handshakeErr() + * is called. + * + * @param sock SSL socket on which the handshake was initiated + * @param ex An exception representing the error. + */ + virtual void handshakeErr( + AsyncSSLSocket* sock, + const AsyncSocketException& ex) noexcept = 0; + }; + + class Timeout : public AsyncTimeout { + public: + Timeout(AsyncSSLSocket* sslSocket, EventBase* eventBase) + : AsyncTimeout(eventBase), sslSocket_(sslSocket) {} + + bool scheduleTimeout(TimeoutManager::timeout_type timeout) { + timeout_ = timeout; + return AsyncTimeout::scheduleTimeout(timeout); + } + + bool scheduleTimeout(uint32_t timeoutMs) { + return scheduleTimeout(std::chrono::milliseconds{timeoutMs}); + } + + TimeoutManager::timeout_type getTimeout() { + return timeout_; + } + + void timeoutExpired() noexcept override { + sslSocket_->timeoutExpired(timeout_); + } + + private: + AsyncSSLSocket* sslSocket_; + TimeoutManager::timeout_type timeout_; + }; + + /** + * A class to wait for asynchronous operations with OpenSSL 1.1.0 + */ + class DefaultOpenSSLAsyncFinishCallback : public ReadCallback { + public: + DefaultOpenSSLAsyncFinishCallback( + AsyncPipeReader::UniquePtr reader, + AsyncSSLSocket* sslSocket, + DestructorGuard dg) + : pipeReader_(std::move(reader)), + sslSocket_(sslSocket), + dg_(std::move(dg)) {} + + ~DefaultOpenSSLAsyncFinishCallback() { + pipeReader_->setReadCB(nullptr); + sslSocket_->setAsyncOperationFinishCallback(nullptr); + } + + void readDataAvailable(size_t len) noexcept override { + CHECK_EQ(len, 1); + sslSocket_->restartSSLAccept(); + pipeReader_->setReadCB(nullptr); + sslSocket_->setAsyncOperationFinishCallback(nullptr); + } + + void getReadBuffer(void** bufReturn, size_t* lenReturn) noexcept override { + *bufReturn = &byte_; + *lenReturn = 1; + } + + void readEOF() noexcept override {} + + void readErr(const folly::AsyncSocketException&) noexcept override {} + + private: + uint8_t byte_{0}; + AsyncPipeReader::UniquePtr pipeReader_; + AsyncSSLSocket* sslSocket_{nullptr}; + DestructorGuard dg_; + }; + + /** + * Create a client AsyncSSLSocket + */ + AsyncSSLSocket( + std::shared_ptr<folly::SSLContext> ctx, + EventBase* evb, + bool deferSecurityNegotiation = false); + + /** + * Create a server/client AsyncSSLSocket from an already connected + * socket file descriptor. + * + * Note that while AsyncSSLSocket enables TCP_NODELAY for sockets it creates + * when connecting, it does not change the socket options when given an + * existing file descriptor. If callers want TCP_NODELAY enabled when using + * this version of the constructor, they need to explicitly call + * setNoDelay(true) after the constructor returns. + * + * @param ctx SSL context for this connection. + * @param evb EventBase that will manage this socket. + * @param fd File descriptor to take over (should be a connected socket). + * @param server Is socket in server mode? + * @param deferSecurityNegotiation + * unencrypted data can be sent before sslConn/Accept + */ + AsyncSSLSocket( + std::shared_ptr<folly::SSLContext> ctx, + EventBase* evb, + NetworkSocket fd, + bool server = true, + bool deferSecurityNegotiation = false); + + /** + * Create a server/client AsyncSSLSocket from an already connected + * AsyncSocket. + */ + AsyncSSLSocket( + std::shared_ptr<folly::SSLContext> ctx, + AsyncSocket::UniquePtr oldAsyncSocket, + bool server = true, + bool deferSecurityNegotiation = false); + + /** + * Helper function to create a server/client shared_ptr<AsyncSSLSocket>. + */ + static std::shared_ptr<AsyncSSLSocket> newSocket( + const std::shared_ptr<folly::SSLContext>& ctx, + EventBase* evb, + NetworkSocket fd, + bool server = true, + bool deferSecurityNegotiation = false) { + return std::shared_ptr<AsyncSSLSocket>( + new AsyncSSLSocket(ctx, evb, fd, server, deferSecurityNegotiation), + Destructor()); + } + + /** + * Helper function to create a client shared_ptr<AsyncSSLSocket>. + */ + static std::shared_ptr<AsyncSSLSocket> newSocket( + const std::shared_ptr<folly::SSLContext>& ctx, + EventBase* evb, + bool deferSecurityNegotiation = false) { + return std::shared_ptr<AsyncSSLSocket>( + new AsyncSSLSocket(ctx, evb, deferSecurityNegotiation), Destructor()); + } + +#if FOLLY_OPENSSL_HAS_SNI + /** + * Create a client AsyncSSLSocket with tlsext_servername in + * the Client Hello message. + */ + AsyncSSLSocket( + const std::shared_ptr<folly::SSLContext>& ctx, + EventBase* evb, + const std::string& serverName, + bool deferSecurityNegotiation = false); + + /** + * Create a client AsyncSSLSocket from an already connected + * socket file descriptor. + * + * Note that while AsyncSSLSocket enables TCP_NODELAY for sockets it creates + * when connecting, it does not change the socket options when given an + * existing file descriptor. If callers want TCP_NODELAY enabled when using + * this version of the constructor, they need to explicitly call + * setNoDelay(true) after the constructor returns. + * + * @param ctx SSL context for this connection. + * @param evb EventBase that will manage this socket. + * @param fd File descriptor to take over (should be a connected socket). + * @param serverName tlsext_hostname that will be sent in ClientHello. + */ + AsyncSSLSocket( + const std::shared_ptr<folly::SSLContext>& ctx, + EventBase* evb, + NetworkSocket fd, + const std::string& serverName, + bool deferSecurityNegotiation = false); + + static std::shared_ptr<AsyncSSLSocket> newSocket( + const std::shared_ptr<folly::SSLContext>& ctx, + EventBase* evb, + const std::string& serverName, + bool deferSecurityNegotiation = false) { + return std::shared_ptr<AsyncSSLSocket>( + new AsyncSSLSocket(ctx, evb, serverName, deferSecurityNegotiation), + Destructor()); + } +#endif // FOLLY_OPENSSL_HAS_SNI + + /** + * TODO: implement support for SSL renegotiation. + * + * This involves proper handling of the SSL_ERROR_WANT_READ/WRITE + * code as a result of SSL_write/read(), instead of returning an + * error. In that case, the READ/WRITE event should be registered, + * and a flag (e.g., writeBlockedOnRead) should be set to indiciate + * the condition. In the next invocation of read/write callback, if + * the flag is on, performWrite()/performRead() should be called in + * addition to the normal call to performRead()/performWrite(), and + * the flag should be reset. + */ + + // Inherit AsyncTransportWrapper methods from AsyncSocket except the + // following. + // See the documentation in AsyncTransport.h + // TODO: implement graceful shutdown in close() + // TODO: implement detachSSL() that returns the SSL connection + void closeNow() override; + void shutdownWrite() override; + void shutdownWriteNow() override; + bool good() const override; + bool connecting() const override; + std::string getApplicationProtocol() const noexcept override; + + std::string getSecurityProtocol() const override { + if (sslState_ == STATE_UNENCRYPTED) { + return ""; + } + return "TLS"; + } + + void setEorTracking(bool track) override; + size_t getRawBytesWritten() const override; + size_t getRawBytesReceived() const override; + void enableClientHelloParsing(); + + /** + * Accept an SSL connection on the socket. + * + * The callback will be invoked and uninstalled when an SSL + * connection has been established on the underlying socket. + * The value of verifyPeer determines the client verification method. + * By default, its set to use the value in the underlying context + * + * @param callback callback object to invoke on success/failure + * @param timeout timeout for this function in milliseconds, or 0 for no + * timeout + * @param verifyPeer SSLVerifyPeerEnum uses the options specified in the + * context by default, can be set explcitly to override the + * method in the context + */ + virtual void sslAccept( + HandshakeCB* callback, + std::chrono::milliseconds timeout = std::chrono::milliseconds::zero(), + const folly::SSLContext::SSLVerifyPeerEnum& verifyPeer = + folly::SSLContext::SSLVerifyPeerEnum::USE_CTX); + + /** + * Invoke SSL accept following an asynchronous session cache lookup + */ + void restartSSLAccept(); + + /** + * Connect to the given address, invoking callback when complete or on error + * + * Note timeout applies to TCP + SSL connection time + */ + void connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + int timeout = 0, + const SocketOptionMap& options = emptySocketOptionMap, + const folly::SocketAddress& bindAddr = anyAddress()) noexcept override; + + /** + * A variant of connect that allows the caller to specify + * the timeout for the regular connect and the ssl connect + * separately. + * connectTimeout is specified as the time to establish a TCP + * connection. + * totalConnectTimeout defines the + * time it takes from starting the TCP connection to the time + * the ssl connection is established. The reason the timeout is + * defined this way is because user's rarely need to specify the SSL + * timeout independently of the connect timeout. It allows us to + * bound the time for a connect and SSL connection in + * a finer grained manner than if timeout was just defined + * independently for SSL. + */ + virtual void connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + std::chrono::milliseconds connectTimeout, + std::chrono::milliseconds totalConnectTimeout, + const SocketOptionMap& options = emptySocketOptionMap, + const folly::SocketAddress& bindAddr = anyAddress()) noexcept; + + using AsyncSocket::connect; + + /** + * If a connect request is in-flight, cancels it and closes the socket + * immediately. Otherwise, this is a no-op. + * + * This does not invoke any connection related callbacks. Call this to + * prevent any connect callback while cleaning up, etc. + */ + void cancelConnect() override; + + /** + * Initiate an SSL connection on the socket + * The callback will be invoked and uninstalled when an SSL connection + * has been establshed on the underlying socket. + * The verification option verifyPeer is applied if it's passed explicitly. + * If it's not, the options in SSLContext set on the underlying SSLContext + * are applied. + * + * @param callback callback object to invoke on success/failure + * @param timeout timeout for this function in milliseconds, or 0 for no + * timeout + * @param verifyPeer SSLVerifyPeerEnum uses the options specified in the + * context by default, can be set explcitly to override the + * method in the context. If verification is turned on sets + * SSL_VERIFY_PEER and invokes + * HandshakeCB::handshakeVer(). + */ + virtual void sslConn( + HandshakeCB* callback, + std::chrono::milliseconds timeout = std::chrono::milliseconds::zero(), + const folly::SSLContext::SSLVerifyPeerEnum& verifyPeer = + folly::SSLContext::SSLVerifyPeerEnum::USE_CTX); + + enum SSLStateEnum { + STATE_UNINIT, + STATE_UNENCRYPTED, + STATE_ACCEPTING, + STATE_ASYNC_PENDING, + STATE_CONNECTING, + STATE_ESTABLISHED, + STATE_REMOTE_CLOSED, /// remote end closed; we can still write + STATE_CLOSING, ///< close() called, but waiting on writes to complete + /// close() called with pending writes, before connect() has completed + STATE_CONNECTING_CLOSING, + STATE_CLOSED, + STATE_ERROR + }; + + SSLStateEnum getSSLState() const { + return sslState_; + } + + /** + * Get a handle to the negotiated SSL session. This increments the session + * refcount and must be deallocated by the caller. + */ + SSL_SESSION* getSSLSession(); + + /** + * Get a handle to the SSL struct. + */ + const SSL* getSSL() const; + + /** + * Set the SSL session to be used during sslConn. AsyncSSLSocket will + * hold a reference to the session until it is destroyed or released by the + * underlying SSL structure. + * + * @param takeOwnership if true, AsyncSSLSocket will assume the caller's + * reference count to session. + */ + void setSSLSession(SSL_SESSION* session, bool takeOwnership = false); + + /** + * Get the name of the protocol selected by the client during + * Application Layer Protocol Negotiation (ALPN) + * + * Throw an exception if openssl does not support NPN + * + * @param protoName Name of the protocol (not guaranteed to be + * null terminated); will be set to nullptr if + * the client did not negotiate a protocol. + * Note: the AsyncSSLSocket retains ownership + * of this string. + * @param protoNameLen Length of the name. + * @param protoType Whether this was an NPN or ALPN negotiation + */ + virtual void getSelectedNextProtocol( + const unsigned char** protoName, + unsigned* protoLen) const; + + /** + * Get the name of the protocol selected by the client during + * Next Protocol Negotiation (NPN) or Application Layer Protocol Negotiation + * (ALPN) + * + * @param protoName Name of the protocol (not guaranteed to be + * null terminated); will be set to nullptr if + * the client did not negotiate a protocol. + * Note: the AsyncSSLSocket retains ownership + * of this string. + * @param protoNameLen Length of the name. + * @param protoType Whether this was an NPN or ALPN negotiation + * @return false if openssl does not support NPN + */ + virtual bool getSelectedNextProtocolNoThrow( + const unsigned char** protoName, + unsigned* protoLen) const; + + /** + * Determine if the session specified during setSSLSession was reused + * or if the server rejected it and issued a new session. + */ + virtual bool getSSLSessionReused() const; + + /** + * true if the session was resumed using session ID + */ + bool sessionIDResumed() const { + return sessionIDResumed_; + } + + void setSessionIDResumed(bool resumed) { + sessionIDResumed_ = resumed; + } + + /** + * Get the negociated cipher name for this SSL connection. + * Returns the cipher used or the constant value "NONE" when no SSL session + * has been established. + */ + virtual const char* getNegotiatedCipherName() const; + + /** + * Get the server name for this SSL connection. Returns the SNI sent in the + * ClientHello, if enableClientHelloParsing() was called. + * + * Returns the server name used or the constant value "NONE" when no SSL + * session has been established. + * If openssl has no SNI support, throw AsyncSocketException. + */ + const char* getSSLServerName() const; + + /** + * Get the server name for this SSL connection. + * Returns the server name used or the constant value "NONE" when no SSL + * session has been established. + * If openssl has no SNI support, return "NONE" + */ + const char* getSSLServerNameNoThrow() const; + + /** + * Get the SSL version for this connection. + * Possible return values are SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, + * with hexa representations 0x200, 0x300, 0x301, + * or 0 if no SSL session has been established. + */ + int getSSLVersion() const; + + /** + * Get the signature algorithm used in the cert that is used for this + * connection. + */ + const char* getSSLCertSigAlgName() const; + + /** + * Get the certificate size used for this SSL connection. + */ + int getSSLCertSize() const; + + void attachEventBase(EventBase* eventBase) override { + AsyncSocket::attachEventBase(eventBase); + handshakeTimeout_.attachEventBase(eventBase); + connectionTimeout_.attachEventBase(eventBase); + } + + void detachEventBase() override { + AsyncSocket::detachEventBase(); + handshakeTimeout_.detachEventBase(); + connectionTimeout_.detachEventBase(); + } + + bool isDetachable() const override { + return AsyncSocket::isDetachable() && !handshakeTimeout_.isScheduled(); + } + + virtual void attachTimeoutManager(TimeoutManager* manager) { + handshakeTimeout_.attachTimeoutManager(manager); + } + + virtual void detachTimeoutManager() { + handshakeTimeout_.detachTimeoutManager(); + } + +#if OPENSSL_VERSION_NUMBER >= 0x009080bfL + /** + * This function will set the SSL context for this socket to the + * argument. This should only be used on client SSL Sockets that have + * already called detachSSLContext(); + */ + void attachSSLContext(const std::shared_ptr<folly::SSLContext>& ctx); + + /** + * Detaches the SSL context for this socket. + */ + void detachSSLContext(); +#endif + + /** + * Returns the original folly::SSLContext associated with this socket. + * + * Suitable for use in AsyncSSLSocket constructor to construct a new + * AsyncSSLSocket using an existing socket's context. + * + * switchServerSSLContext() does not affect this return value. + */ + const std::shared_ptr<folly::SSLContext>& getSSLContext() const { + return ctx_; + } + +#if FOLLY_OPENSSL_HAS_SNI + /** + * Switch the SSLContext to continue the SSL handshake. + * It can only be used in server mode. + */ + void switchServerSSLContext( + const std::shared_ptr<folly::SSLContext>& handshakeCtx); + + /** + * Did server recognize/support the tlsext_hostname in Client Hello? + * It can only be used in client mode. + * + * @return true - tlsext_hostname is matched by the server + * false - tlsext_hostname is not matched or + * is not supported by server + */ + bool isServerNameMatch() const; + + /** + * Set the SNI hostname that we'll advertise to the server in the + * ClientHello message. + */ + void setServerName(std::string serverName) noexcept; +#endif // FOLLY_OPENSSL_HAS_SNI + + void timeoutExpired(std::chrono::milliseconds timeout) noexcept; + + /** + * Get the list of supported ciphers sent by the client in the client's + * preference order. + */ + void getSSLClientCiphers( + std::string& clientCiphers, + bool convertToString = true) const; + + /** + * Get the list of compression methods sent by the client in TLS Hello. + */ + std::string getSSLClientComprMethods() const; + + /** + * Get the list of TLS extensions sent by the client in the TLS Hello. + */ + std::string getSSLClientExts() const; + + std::string getSSLClientSigAlgs() const; + + /** + * Get the list of versions in the supported versions extension (used to + * negotiate TLS 1.3). + */ + std::string getSSLClientSupportedVersions() const; + + std::string getSSLAlertsReceived() const; + + /* + * Save an optional alert message generated during certificate verify + */ + void setSSLCertVerificationAlert(std::string alert); + + std::string getSSLCertVerificationAlert() const; + + /** + * Get the list of shared ciphers between the server and the client. + * Works well for only SSLv2, not so good for SSLv3 or TLSv1. + */ + void getSSLSharedCiphers(std::string& sharedCiphers) const; + + /** + * Get the list of ciphers supported by the server in the server's + * preference order. + */ + void getSSLServerCiphers(std::string& serverCiphers) const; + + /** + * Method to check if peer verfication is set. + * + * @return true if peer verification is required. + */ + bool needsPeerVerification() const; + + static int getSSLExDataIndex(); + static AsyncSSLSocket* getFromSSL(const SSL* ssl); + static int bioWrite(BIO* b, const char* in, int inl); + static int bioRead(BIO* b, char* out, int outl); + void resetClientHelloParsing(SSL* ssl); + static void clientHelloParsingCallback( + int written, + int version, + int contentType, + const void* buf, + size_t len, + SSL* ssl, + void* arg); + static const char* getSSLServerNameFromSSL(SSL* ssl); + + // For unit-tests + ssl::ClientHelloInfo* getClientHelloInfo() const { + return clientHelloInfo_.get(); + } + + /** + * Returns the time taken to complete a handshake. + */ + virtual std::chrono::nanoseconds getHandshakeTime() const { + return handshakeEndTime_ - handshakeStartTime_; + } + + void setMinWriteSize(size_t minWriteSize) { + minWriteSize_ = minWriteSize; + } + + size_t getMinWriteSize() const { + return minWriteSize_; + } + + const AsyncTransportCertificate* getPeerCertificate() const override; + const AsyncTransportCertificate* getSelfCertificate() const override; + + /** + * Force AsyncSSLSocket object to cache local and peer socket addresses. + * If called with "true" before connect() this function forces full local + * and remote socket addresses to be cached in the socket object and available + * through getLocalAddress()/getPeerAddress() methods even after the socket is + * closed. + */ + void forceCacheAddrOnFailure(bool force) { + cacheAddrOnFailure_ = force; + } + + const std::string& getSessionKey() const { + return sessionKey_; + } + + void setSessionKey(std::string sessionKey) { + sessionKey_ = std::move(sessionKey); + } + + void setCertCacheHit(bool hit) { + certCacheHit_ = hit; + } + + bool getCertCacheHit() const { + return certCacheHit_; + } + + bool sessionResumptionAttempted() const { + return sessionResumptionAttempted_; + } + + /** + * If the SSL socket was used to connect as well + * as establish an SSL connection, this gives the total + * timeout for the connect + SSL connection that was + * set. + */ + std::chrono::milliseconds getTotalConnectTimeout() const { + return totalConnectTimeout_; + } + + // This can be called for OpenSSL 1.1.0 async operation finishes + void setAsyncOperationFinishCallback(std::unique_ptr<ReadCallback> cb) { + asyncOperationFinishCallback_ = std::move(cb); + } + + private: + /** + * Handle the return from invoking SSL_accept + */ + void handleReturnFromSSLAccept(int ret); + + void init(); + + // Need to clean this up during a cancel if callback hasn't fired yet. + AsyncSSLSocketConnector* allocatedConnectCallback_; + + protected: + /** + * Protected destructor. + * + * Users of AsyncSSLSocket must never delete it directly. Instead, invoke + * destroy() instead. (See the documentation in DelayedDestruction.h for + * more details.) + */ + ~AsyncSSLSocket() override; + + // Inherit event notification methods from AsyncSocket except + // the following. + void handleRead() noexcept override; + void handleWrite() noexcept override; + void handleAccept() noexcept; + void handleConnect() noexcept override; + + void invalidState(HandshakeCB* callback); + bool + willBlock(int ret, int* sslErrorOut, unsigned long* errErrorOut) noexcept; + + void checkForImmediateRead() noexcept override; + // AsyncSocket calls this at the wrong time for SSL + void handleInitialReadWrite() noexcept override {} + + WriteResult interpretSSLError(int rc, int error); + ReadResult performRead(void** buf, size_t* buflen, size_t* offset) override; + WriteResult performWrite( + const iovec* vec, + uint32_t count, + WriteFlags flags, + uint32_t* countWritten, + uint32_t* partialWritten) override; + + ssize_t performWriteIovec( + const iovec* vec, + uint32_t count, + WriteFlags flags, + uint32_t* countWritten, + uint32_t* partialWritten); + + // This virtual wrapper around SSL_write exists solely for testing/mockability + virtual int sslWriteImpl(SSL* ssl, const void* buf, int n) { + return SSL_write(ssl, buf, n); + } + + /** + * Apply verification options passed to sslConn/sslAccept or those set + * in the underlying SSLContext object. + * + * @param ssl pointer to the SSL object on which verification options will be + * applied. If verifyPeer_ was explicitly set either via sslConn/sslAccept, + * those options override the settings in the underlying SSLContext. + */ + bool applyVerificationOptions(const ssl::SSLUniquePtr& ssl); + + /** + * Sets up SSL with a custom write bio which intercepts all writes. + * + * @return true, if succeeds and false if there is an error creating the bio. + */ + bool setupSSLBio(); + + /** + * A SSL_write wrapper that understand EOR + * + * @param ssl: SSL pointer + * @param buf: Buffer to be written + * @param n: Number of bytes to be written + * @param eor: Does the last byte (buf[n-1]) have the app-last-byte? + * @return: The number of app bytes successfully written to the socket + */ + int eorAwareSSLWrite( + const ssl::SSLUniquePtr& ssl, + const void* buf, + int n, + bool eor); + + // Inherit error handling methods from AsyncSocket, plus the following. + void failHandshake(const char* fn, const AsyncSocketException& ex); + + void invokeHandshakeErr(const AsyncSocketException& ex); + void invokeHandshakeCB(); + + void invokeConnectErr(const AsyncSocketException& ex) override; + void invokeConnectSuccess() override; + void scheduleConnectTimeout() override; + + void startSSLConnect(); + + static void sslInfoCallback(const SSL* ssl, int where, int ret); + + // Whether the current write to the socket should use MSG_MORE. + bool corkCurrentWrite_{false}; + // SSL related members. + bool server_{false}; + // Used to prevent client-initiated renegotiation. Note that AsyncSSLSocket + // doesn't fully support renegotiation, so we could just fail all attempts + // to enforce this. Once it is supported, we should make it an option + // to disable client-initiated renegotiation. + bool handshakeComplete_{false}; + bool renegotiateAttempted_{false}; + SSLStateEnum sslState_{STATE_UNINIT}; + std::shared_ptr<folly::SSLContext> ctx_; + // Callback for SSL_accept() or SSL_connect() + HandshakeCB* handshakeCallback_{nullptr}; + ssl::SSLUniquePtr ssl_; + SSL_SESSION* sslSession_{nullptr}; + Timeout handshakeTimeout_; + Timeout connectionTimeout_; + + // The app byte num that we are tracking for EOR. + // + // Only one app EOR byte can be tracked. + // See appEorByteWriteFlags_ for details. + size_t appEorByteNo_{0}; + + // The WriteFlags to pass for the app byte num that is tracked for EOR. + // + // When openssl is about to send appEorByteNo_, these flags will be passed to + // the application via the getAncillaryData callback. The application can then + // generate a control message containing socket timestamping flags or other + // commands that will be included when the corresponding buffer is passed to + // the kernel via sendmsg(). + // + // See AsyncSSLSocket::bioWrite (which overrides OpenSSL biowrite). + WriteFlags appEorByteWriteFlags_{}; + + // Try to avoid calling SSL_write() for buffers smaller than this. + // It doesn't take effect when it is 0. + size_t minWriteSize_{1500}; + + // When openssl is about to sendmsg() across the minEorRawBytesNo_, + // it will trigger logic to include an application defined control message. + // + // See appEorByteWriteFlags_ for details. + size_t minEorRawByteNo_{0}; +#if FOLLY_OPENSSL_HAS_SNI + std::shared_ptr<folly::SSLContext> handshakeCtx_; + std::string tlsextHostname_; +#endif + + // a key that can be used for caching the established session + std::string sessionKey_; + + folly::SSLContext::SSLVerifyPeerEnum verifyPeer_{ + folly::SSLContext::SSLVerifyPeerEnum::USE_CTX}; + + // Callback for SSL_CTX_set_verify() + static int sslVerifyCallback(int preverifyOk, X509_STORE_CTX* ctx); + + bool parseClientHello_{false}; + bool cacheAddrOnFailure_{false}; + bool certCacheHit_{false}; + std::unique_ptr<ssl::ClientHelloInfo> clientHelloInfo_; + std::vector<std::pair<char, StringPiece>> alertsReceived_; + + // Time taken to complete the ssl handshake. + std::chrono::steady_clock::time_point handshakeStartTime_; + std::chrono::steady_clock::time_point handshakeEndTime_; + std::chrono::milliseconds handshakeConnectTimeout_{0}; + std::chrono::milliseconds totalConnectTimeout_{0}; + + std::string sslVerificationAlert_; + + bool sessionResumptionAttempted_{false}; + // whether the SSL session was resumed using session ID or not + bool sessionIDResumed_{false}; + // This can be called for OpenSSL 1.1.0 async operation finishes + std::unique_ptr<ReadCallback> asyncOperationFinishCallback_; + // Whether this socket is currently waiting on SSL_accept + bool waitingOnAccept_{false}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.cpp new file mode 100644 index 000000000..82105e06d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.cpp @@ -0,0 +1,1187 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include <folly/io/async/AsyncServerSocket.h> + +#include <folly/FileUtil.h> +#include <folly/Portability.h> +#include <folly/SocketAddress.h> +#include <folly/String.h> +#include <folly/detail/SocketFastOpen.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/NotificationQueue.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Unistd.h> + +#include <sys/types.h> +#include <cerrno> +#include <cstring> + +namespace folly { + +#ifndef TCP_SAVE_SYN +#define TCP_SAVE_SYN 27 +#endif + +#ifndef TCP_SAVED_SYN +#define TCP_SAVED_SYN 28 +#endif + +static constexpr bool msgErrQueueSupported = +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + true; +#else + false; +#endif // FOLLY_HAVE_MSG_ERRQUEUE + +const uint32_t AsyncServerSocket::kDefaultMaxAcceptAtOnce; +const uint32_t AsyncServerSocket::kDefaultCallbackAcceptAtOnce; +const uint32_t AsyncServerSocket::kDefaultMaxMessagesInQueue; + +void AsyncServerSocket::RemoteAcceptor::start( + EventBase* eventBase, + uint32_t maxAtOnce, + uint32_t maxInQueue) { + setMaxReadAtOnce(maxAtOnce); + queue_.setMaxQueueSize(maxInQueue); + + eventBase->runInEventBaseThread([=]() { + callback_->acceptStarted(); + this->startConsuming(eventBase, &queue_); + }); +} + +void AsyncServerSocket::RemoteAcceptor::stop( + EventBase* eventBase, + AcceptCallback* callback) { + eventBase->runInEventBaseThread([=]() { + callback->acceptStopped(); + delete this; + }); +} + +void AsyncServerSocket::RemoteAcceptor::messageAvailable( + QueueMessage&& msg) noexcept { + switch (msg.type) { + case MessageType::MSG_NEW_CONN: { + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDequeuedByAcceptorCallback( + msg.fd, msg.address); + } + callback_->connectionAccepted(msg.fd, msg.address); + break; + } + case MessageType::MSG_ERROR: { + std::runtime_error ex(msg.msg); + callback_->acceptError(ex); + break; + } + default: { + LOG(ERROR) << "invalid accept notification message type " + << int(msg.type); + std::runtime_error ex( + "received invalid accept notification message type"); + callback_->acceptError(ex); + } + } +} + +/* + * AsyncServerSocket::BackoffTimeout + */ +class AsyncServerSocket::BackoffTimeout : public AsyncTimeout { + public: + // Disallow copy, move, and default constructors. + BackoffTimeout(BackoffTimeout&&) = delete; + explicit BackoffTimeout(AsyncServerSocket* socket) + : AsyncTimeout(socket->getEventBase()), socket_(socket) {} + + void timeoutExpired() noexcept override { + socket_->backoffTimeoutExpired(); + } + + private: + AsyncServerSocket* socket_; +}; + +/* + * AsyncServerSocket methods + */ + +AsyncServerSocket::AsyncServerSocket(EventBase* eventBase) + : eventBase_(eventBase), + accepting_(false), + maxAcceptAtOnce_(kDefaultMaxAcceptAtOnce), + maxNumMsgsInQueue_(kDefaultMaxMessagesInQueue), + acceptRateAdjustSpeed_(0), + acceptRate_(1), + lastAccepTimestamp_(std::chrono::steady_clock::now()), + numDroppedConnections_(0), + callbackIndex_(0), + backoffTimeout_(nullptr), + callbacks_(), + keepAliveEnabled_(true), + closeOnExec_(true) { + disableTransparentTls(); +} + +void AsyncServerSocket::setShutdownSocketSet( + const std::weak_ptr<ShutdownSocketSet>& wNewSS) { + const auto newSS = wNewSS.lock(); + const auto shutdownSocketSet = wShutdownSocketSet_.lock(); + + if (shutdownSocketSet == newSS) { + return; + } + + if (shutdownSocketSet) { + for (auto& h : sockets_) { + shutdownSocketSet->remove(h.socket_); + } + } + + if (newSS) { + for (auto& h : sockets_) { + newSS->add(h.socket_); + } + } + + wShutdownSocketSet_ = wNewSS; +} + +AsyncServerSocket::~AsyncServerSocket() { + assert(callbacks_.empty()); +} + +int AsyncServerSocket::stopAccepting(int shutdownFlags) { + int result = 0; + for (auto& handler : sockets_) { + VLOG(10) << "AsyncServerSocket::stopAccepting " << this << handler.socket_; + } + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // When destroy is called, unregister and close the socket immediately. + accepting_ = false; + + // Close the sockets in reverse order as they were opened to avoid + // the condition where another process concurrently tries to open + // the same port, succeed to bind the first socket but fails on the + // second because it hasn't been closed yet. + for (; !sockets_.empty(); sockets_.pop_back()) { + auto& handler = sockets_.back(); + handler.unregisterHandler(); + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->close(handler.socket_); + } else if (shutdownFlags >= 0) { + result = shutdownNoInt(handler.socket_, shutdownFlags); + pendingCloseSockets_.push_back(handler.socket_); + } else { + closeNoInt(handler.socket_); + } + } + + // Destroy the backoff timout. This will cancel it if it is running. + delete backoffTimeout_; + backoffTimeout_ = nullptr; + + // Close all of the callback queues to notify them that they are being + // destroyed. No one should access the AsyncServerSocket any more once + // destroy() is called. However, clear out callbacks_ before invoking the + // accept callbacks just in case. This will potentially help us detect the + // bug if one of the callbacks calls addAcceptCallback() or + // removeAcceptCallback(). + std::vector<CallbackInfo> callbacksCopy; + callbacks_.swap(callbacksCopy); + for (const auto& callback : callbacksCopy) { + // consumer may not be set if we are running in primary event base + if (callback.consumer) { + DCHECK(callback.eventBase); + callback.consumer->stop(callback.eventBase, callback.callback); + } else { + DCHECK(callback.callback); + callback.callback->acceptStopped(); + } + } + + return result; +} + +void AsyncServerSocket::destroy() { + stopAccepting(); + for (auto s : pendingCloseSockets_) { + closeNoInt(s); + } + // Then call DelayedDestruction::destroy() to take care of + // whether or not we need immediate or delayed destruction + DelayedDestruction::destroy(); +} + +void AsyncServerSocket::attachEventBase(EventBase* eventBase) { + assert(eventBase_ == nullptr); + eventBase->dcheckIsInEventBaseThread(); + + eventBase_ = eventBase; + for (auto& handler : sockets_) { + handler.attachEventBase(eventBase); + } +} + +void AsyncServerSocket::detachEventBase() { + assert(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + assert(!accepting_); + + eventBase_ = nullptr; + for (auto& handler : sockets_) { + handler.detachEventBase(); + } +} + +void AsyncServerSocket::useExistingSockets( + const std::vector<NetworkSocket>& fds) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + if (!sockets_.empty()) { + throw std::invalid_argument( + "cannot call useExistingSocket() on a " + "AsyncServerSocket that already has a socket"); + } + + for (auto fd : fds) { + // Set addressFamily_ from this socket. + // Note that the socket may not have been bound yet, but + // setFromLocalAddress() will still work and get the correct address family. + // We will update addressFamily_ again anyway if bind() is called later. + SocketAddress address; + address.setFromLocalAddress(fd); + +#if defined(__linux__) + if (noTransparentTls_) { + // Ignore return value, errors are ok + netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + setupSocket(fd, address.getFamily()); + sockets_.emplace_back(eventBase_, fd, this, address.getFamily()); + sockets_.back().changeHandlerFD(fd); + } +} + +void AsyncServerSocket::useExistingSocket(NetworkSocket fd) { + useExistingSockets({fd}); +} + +void AsyncServerSocket::bindSocket( + NetworkSocket fd, + const SocketAddress& address, + bool isExistingSocket) { + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + auto saddr = reinterpret_cast<sockaddr*>(&addrStorage); + + if (netops::bind(fd, saddr, address.getActualSize()) != 0) { + if (errno != EINPROGRESS) { + // Get a copy of errno so that it is not overwritten by subsequent calls. + auto errnoCopy = errno; + if (!isExistingSocket) { + closeNoInt(fd); + } + folly::throwSystemErrorExplicit( + errnoCopy, + "failed to bind to async server socket: " + address.describe()); + } + } + +#if defined(__linux__) + if (noTransparentTls_) { + // Ignore return value, errors are ok + netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + // If we just created this socket, update the EventHandler and set socket_ + if (!isExistingSocket) { + sockets_.emplace_back(eventBase_, fd, this, address.getFamily()); + } +} + +bool AsyncServerSocket::setZeroCopy(bool enable) { + if (msgErrQueueSupported) { + // save the enable flag here + zeroCopyVal_ = enable; + int val = enable ? 1 : 0; + size_t num = 0; + for (auto& s : sockets_) { + int ret = netops::setsockopt( + s.socket_, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)); + + num += (0 == ret) ? 1 : 0; + } + + return num != 0; + } + + return false; +} + +void AsyncServerSocket::bind(const SocketAddress& address) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // useExistingSocket() may have been called to initialize socket_ already. + // However, in the normal case we need to create a new socket now. + // Don't set socket_ yet, so that socket_ will remain uninitialized if an + // error occurs. + NetworkSocket fd; + if (sockets_.empty()) { + fd = createSocket(address.getFamily()); + } else if (sockets_.size() == 1) { + if (address.getFamily() != sockets_[0].addressFamily_) { + throw std::invalid_argument( + "Attempted to bind address to socket with " + "different address family"); + } + fd = sockets_[0].socket_; + } else { + throw std::invalid_argument("Attempted to bind to multiple fds"); + } + + bindSocket(fd, address, !sockets_.empty()); +} + +void AsyncServerSocket::bind( + const std::vector<IPAddress>& ipAddresses, + uint16_t port) { + if (ipAddresses.empty()) { + throw std::invalid_argument("No ip addresses were provided"); + } + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + for (const IPAddress& ipAddress : ipAddresses) { + SocketAddress address(ipAddress.toFullyQualified(), port); + auto fd = createSocket(address.getFamily()); + + bindSocket(fd, address, false); + } + if (sockets_.empty()) { + throw std::runtime_error( + "did not bind any async server socket for port and addresses"); + } +} + +void AsyncServerSocket::bind(uint16_t port) { + struct addrinfo hints, *res0; + char sport[sizeof("65536")]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + snprintf(sport, sizeof(sport), "%u", port); + + // On Windows the value we need to pass to bind to all available + // addresses is an empty string. Everywhere else, it's nullptr. + constexpr const char* kWildcardNode = kIsWindows ? "" : nullptr; + if (getaddrinfo(kWildcardNode, sport, &hints, &res0)) { + throw std::invalid_argument( + "Attempted to bind address to socket with " + "bad getaddrinfo"); + } + + SCOPE_EXIT { + freeaddrinfo(res0); + }; + + auto setupAddress = [&](struct addrinfo* res) { + auto s = netops::socket(res->ai_family, res->ai_socktype, res->ai_protocol); + // IPv6/IPv4 may not be supported by the kernel + if (s == NetworkSocket() && errno == EAFNOSUPPORT) { + return; + } + CHECK_NE(s, NetworkSocket()); + + try { + setupSocket(s, res->ai_family); + } catch (...) { + closeNoInt(s); + throw; + } + + if (res->ai_family == AF_INET6) { + int v6only = 1; + CHECK( + 0 == + netops::setsockopt( + s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))); + } + + // Bind to the socket + if (netops::bind(s, res->ai_addr, socklen_t(res->ai_addrlen)) != 0) { + folly::throwSystemError( + errno, + "failed to bind to async server socket for port ", + SocketAddress::getPortFrom(res->ai_addr), + " family ", + SocketAddress::getFamilyNameFrom(res->ai_addr, "<unknown>")); + } + +#if defined(__linux__) + if (noTransparentTls_) { + // Ignore return value, errors are ok + netops::setsockopt(s, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + SocketAddress address; + address.setFromLocalAddress(s); + + sockets_.emplace_back(eventBase_, s, this, address.getFamily()); + }; + + const int kNumTries = 25; + for (int tries = 1; true; tries++) { + // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo + // should return IPv6 first and then IPv4 addresses, but glibc's + // getaddrinfo(nullptr) with AI_PASSIVE returns: + // - 0.0.0.0 (IPv4-only) + // - :: (IPv6+IPv4) in this order + // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981 + for (struct addrinfo* res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_INET6) { + setupAddress(res); + } + } + + // If port == 0, then we should try to bind to the same port on ipv4 and + // ipv6. So if we did bind to ipv6, figure out that port and use it. + if (sockets_.size() == 1 && port == 0) { + SocketAddress address; + address.setFromLocalAddress(sockets_.back().socket_); + snprintf(sport, sizeof(sport), "%u", address.getPort()); + freeaddrinfo(res0); + CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); + } + + try { + for (struct addrinfo* res = res0; res; res = res->ai_next) { + if (res->ai_family != AF_INET6) { + setupAddress(res); + } + } + } catch (const std::system_error&) { + // If we can't bind to the same port on ipv4 as ipv6 when using + // port=0 then we will retry again before giving up after + // kNumTries attempts. We do this by closing the sockets that + // were opened, then restarting from scratch. + if (port == 0 && !sockets_.empty() && tries != kNumTries) { + for (const auto& socket : sockets_) { + if (socket.socket_ == NetworkSocket()) { + continue; + } else if ( + const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->close(socket.socket_); + } else { + closeNoInt(socket.socket_); + } + } + sockets_.clear(); + snprintf(sport, sizeof(sport), "%u", port); + freeaddrinfo(res0); + CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); + continue; + } + + throw; + } + + break; + } + + if (sockets_.empty()) { + throw std::runtime_error("did not bind any async server socket for port"); + } +} + +void AsyncServerSocket::listen(int backlog) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // Start listening + for (auto& handler : sockets_) { + if (netops::listen(handler.socket_, backlog) == -1) { + folly::throwSystemError(errno, "failed to listen on async server socket"); + } + } +} + +void AsyncServerSocket::getAddress(SocketAddress* addressReturn) const { + CHECK(!sockets_.empty()); + VLOG_IF(2, sockets_.size() > 1) + << "Warning: getAddress() called and multiple addresses available (" + << sockets_.size() << "). Returning only the first one."; + + addressReturn->setFromLocalAddress(sockets_[0].socket_); +} + +std::vector<SocketAddress> AsyncServerSocket::getAddresses() const { + CHECK(!sockets_.empty()); + auto tsaVec = std::vector<SocketAddress>(sockets_.size()); + auto tsaIter = tsaVec.begin(); + for (const auto& socket : sockets_) { + (tsaIter++)->setFromLocalAddress(socket.socket_); + }; + return tsaVec; +} + +void AsyncServerSocket::addAcceptCallback( + AcceptCallback* callback, + EventBase* eventBase, + uint32_t maxAtOnce) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // If this is the first accept callback and we are supposed to be accepting, + // start accepting once the callback is installed. + bool runStartAccepting = accepting_ && callbacks_.empty(); + + callbacks_.emplace_back(callback, eventBase); + + SCOPE_SUCCESS { + // If this is the first accept callback and we are supposed to be accepting, + // start accepting. + if (runStartAccepting) { + startAccepting(); + } + }; + + if (!eventBase) { + // Run in AsyncServerSocket's eventbase; notify that we are + // starting to accept connections + callback->acceptStarted(); + return; + } + + // Start the remote acceptor. + // + // It would be nice if we could avoid starting the remote acceptor if + // eventBase == eventBase_. However, that would cause issues if + // detachEventBase() and attachEventBase() were ever used to change the + // primary EventBase for the server socket. Therefore we require the caller + // to specify a nullptr EventBase if they want to ensure that the callback is + // always invoked in the primary EventBase, and to be able to invoke that + // callback more efficiently without having to use a notification queue. + RemoteAcceptor* acceptor = nullptr; + try { + acceptor = new RemoteAcceptor(callback, connectionEventCallback_); + acceptor->start(eventBase, maxAtOnce, maxNumMsgsInQueue_); + } catch (...) { + callbacks_.pop_back(); + delete acceptor; + throw; + } + callbacks_.back().consumer = acceptor; +} + +void AsyncServerSocket::removeAcceptCallback( + AcceptCallback* callback, + EventBase* eventBase) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // Find the matching AcceptCallback. + // We just do a simple linear search; we don't expect removeAcceptCallback() + // to be called frequently, and we expect there to only be a small number of + // callbacks anyway. + auto it = callbacks_.begin(); + uint32_t n = 0; + while (true) { + if (it == callbacks_.end()) { + throw std::runtime_error( + "AsyncServerSocket::removeAcceptCallback(): " + "accept callback not found"); + } + if (it->callback == callback && + (it->eventBase == eventBase || eventBase == nullptr)) { + break; + } + ++it; + ++n; + } + + // Remove this callback from callbacks_. + // + // Do this before invoking the acceptStopped() callback, in case + // acceptStopped() invokes one of our methods that examines callbacks_. + // + // Save a copy of the CallbackInfo first. + CallbackInfo info(*it); + callbacks_.erase(it); + if (n < callbackIndex_) { + // We removed an element before callbackIndex_. Move callbackIndex_ back + // one step, since things after n have been shifted back by 1. + --callbackIndex_; + } else { + // We removed something at or after callbackIndex_. + // If we removed the last element and callbackIndex_ was pointing at it, + // we need to reset callbackIndex_ to 0. + if (callbackIndex_ >= callbacks_.size()) { + callbackIndex_ = 0; + } + } + + if (info.consumer) { + // consumer could be nullptr is we run callbacks in primary event + // base + DCHECK(info.eventBase); + info.consumer->stop(info.eventBase, info.callback); + } else { + // callback invoked in the primary event base, just call directly + DCHECK(info.callback); + callback->acceptStopped(); + } + + // If we are supposed to be accepting but the last accept callback + // was removed, unregister for events until a callback is added. + if (accepting_ && callbacks_.empty()) { + for (auto& handler : sockets_) { + handler.unregisterHandler(); + } + } +} + +void AsyncServerSocket::startAccepting() { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + accepting_ = true; + if (callbacks_.empty()) { + // We can't actually begin accepting if no callbacks are defined. + // Wait until a callback is added to start accepting. + return; + } + + for (auto& handler : sockets_) { + if (!handler.registerHandler(EventHandler::READ | EventHandler::PERSIST)) { + throw std::runtime_error("failed to register for accept events"); + } + } +} + +void AsyncServerSocket::pauseAccepting() { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + accepting_ = false; + for (auto& handler : sockets_) { + handler.unregisterHandler(); + } + + // If we were in the accept backoff state, disable the backoff timeout + if (backoffTimeout_) { + backoffTimeout_->cancelTimeout(); + } +} + +NetworkSocket AsyncServerSocket::createSocket(int family) { + auto fd = netops::socket(family, SOCK_STREAM, 0); + if (fd == NetworkSocket()) { + folly::throwSystemError(errno, "error creating async server socket"); + } + + try { + setupSocket(fd, family); + } catch (...) { + closeNoInt(fd); + throw; + } + return fd; +} + +/** + * Enable/Disable TOS reflection for the server socket + * If enabled, the 'accepted' connections will reflect the + * TOS derived from the client's connect request + */ +void AsyncServerSocket::setTosReflect(bool enable) { + if (!kIsLinux || !enable) { + tosReflect_ = false; + return; + } + + for (auto& handler : sockets_) { + if (handler.socket_ == NetworkSocket()) { + continue; + } + + int val = (enable) ? 1 : 0; + int ret = netops::setsockopt( + handler.socket_, IPPROTO_TCP, TCP_SAVE_SYN, &val, sizeof(val)); + + if (ret == 0) { + VLOG(10) << "Enabled SYN save for socket " << handler.socket_; + } else { + folly::throwSystemError(errno, "failed to enable TOS reflect"); + } + } + tosReflect_ = true; +} + +void AsyncServerSocket::setupSocket(NetworkSocket fd, int family) { + // Put the socket in non-blocking mode + if (netops::set_socket_non_blocking(fd) != 0) { + folly::throwSystemError(errno, "failed to put socket in non-blocking mode"); + } + + // Set reuseaddr to avoid 2MSL delay on server restart + int one = 1; + if (netops::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != + 0) { + auto errnoCopy = errno; + // This isn't a fatal error; just log an error message and continue + LOG(ERROR) << "failed to set SO_REUSEADDR on async server socket " + << errnoCopy; + } + + // Set reuseport to support multiple accept threads + int zero = 0; + if (reusePortEnabled_ && + netops::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) != + 0) { + auto errnoCopy = errno; + LOG(ERROR) << "failed to set SO_REUSEPORT on async server socket " + << errnoStr(errnoCopy); +#ifdef WIN32 + folly::throwSystemErrorExplicit( + errnoCopy, "failed to set SO_REUSEPORT on async server socket"); +#else + SocketAddress address; + address.setFromLocalAddress(fd); + folly::throwSystemErrorExplicit( + errnoCopy, + "failed to set SO_REUSEPORT on async server socket: " + + address.describe()); +#endif + } + + // Set keepalive as desired + if (netops::setsockopt( + fd, + SOL_SOCKET, + SO_KEEPALIVE, + (keepAliveEnabled_) ? &one : &zero, + sizeof(int)) != 0) { + auto errnoCopy = errno; + LOG(ERROR) << "failed to set SO_KEEPALIVE on async server socket: " + << errnoStr(errnoCopy); + } + + // Setup FD_CLOEXEC flag + if (closeOnExec_ && (-1 == netops::set_socket_close_on_exec(fd))) { + auto errnoCopy = errno; + LOG(ERROR) << "failed to set FD_CLOEXEC on async server socket: " + << errnoStr(errnoCopy); + } + + // Set TCP nodelay if available, MAC OS X Hack + // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html +#ifndef TCP_NOPUSH + if (family != AF_UNIX) { + if (netops::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != + 0) { + auto errnoCopy = errno; + // This isn't a fatal error; just log an error message and continue + LOG(ERROR) << "failed to set TCP_NODELAY on async server socket: " + << errnoStr(errnoCopy); + } + } +#else + (void)family; // to avoid unused parameter warning +#endif + +#if FOLLY_ALLOW_TFO + if (tfo_ && detail::tfo_enable(fd, tfoMaxQueueSize_) != 0) { + auto errnoCopy = errno; + // This isn't a fatal error; just log an error message and continue + LOG(WARNING) << "failed to set TCP_FASTOPEN on async server socket: " + << folly::errnoStr(errnoCopy); + } +#endif + + if (zeroCopyVal_) { + int val = 1; + int ret = + netops::setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)); + if (ret) { + auto errnoCopy = errno; + LOG(WARNING) << "failed to set SO_ZEROCOPY on async server socket: " + << folly::errnoStr(errnoCopy); + } + } + + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->add(fd); + } +} + +void AsyncServerSocket::handlerReady( + uint16_t /* events */, + NetworkSocket fd, + sa_family_t addressFamily) noexcept { + assert(!callbacks_.empty()); + DestructorGuard dg(this); + + // Only accept up to maxAcceptAtOnce_ connections at a time, + // to avoid starving other I/O handlers using this EventBase. + for (uint32_t n = 0; n < maxAcceptAtOnce_; ++n) { + SocketAddress address; + + sockaddr_storage addrStorage = {}; + socklen_t addrLen = sizeof(addrStorage); + auto saddr = reinterpret_cast<sockaddr*>(&addrStorage); + + // In some cases, accept() doesn't seem to update these correctly. + saddr->sa_family = addressFamily; + if (addressFamily == AF_UNIX) { + addrLen = sizeof(struct sockaddr_un); + } + + // Accept a new client socket +#if FOLLY_HAVE_ACCEPT4 + auto clientSocket = NetworkSocket::fromFd( + accept4(fd.toFd(), saddr, &addrLen, SOCK_NONBLOCK)); +#else + auto clientSocket = netops::accept(fd, saddr, &addrLen); +#endif + + address.setFromSockaddr(saddr, addrLen); + + if (clientSocket != NetworkSocket() && connectionEventCallback_) { + connectionEventCallback_->onConnectionAccepted(clientSocket, address); + } + + // Connection accepted, get the SYN packet from the client if + // TOS reflect is enabled + if (kIsLinux && clientSocket != NetworkSocket() && tosReflect_) { + std::array<uint32_t, 64> buffer; + socklen_t len = sizeof(buffer); + int ret = netops::getsockopt( + clientSocket, IPPROTO_TCP, TCP_SAVED_SYN, &buffer, &len); + + if (ret == 0) { + uint32_t tosWord = folly::Endian::big(buffer[0]); + if (addressFamily == AF_INET6) { + tosWord = (tosWord & 0x0FC00000) >> 20; + // Set the TOS on the return socket only if it is non-zero + if (tosWord) { + ret = netops::setsockopt( + clientSocket, + IPPROTO_IPV6, + IPV6_TCLASS, + &tosWord, + sizeof(tosWord)); + } + } else if (addressFamily == AF_INET) { + tosWord = (tosWord & 0x00FC0000) >> 16; + if (tosWord) { + ret = netops::setsockopt( + clientSocket, IPPROTO_IP, IP_TOS, &tosWord, sizeof(tosWord)); + } + } + + if (ret != 0) { + LOG(ERROR) << "Unable to set TOS for accepted socket " + << clientSocket; + } + } else { + LOG(ERROR) << "Unable to get SYN packet for accepted socket " + << clientSocket; + } + } + + std::chrono::time_point<std::chrono::steady_clock> nowMs = + std::chrono::steady_clock::now(); + auto timeSinceLastAccept = std::max<int64_t>( + 0, + nowMs.time_since_epoch().count() - + lastAccepTimestamp_.time_since_epoch().count()); + lastAccepTimestamp_ = nowMs; + if (acceptRate_ < 1) { + acceptRate_ *= 1 + acceptRateAdjustSpeed_ * timeSinceLastAccept; + if (acceptRate_ >= 1) { + acceptRate_ = 1; + } else if (rand() > acceptRate_ * RAND_MAX) { + ++numDroppedConnections_; + if (clientSocket != NetworkSocket()) { + closeNoInt(clientSocket); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped( + clientSocket, address); + } + } + continue; + } + } + + if (clientSocket == NetworkSocket()) { + if (errno == EAGAIN) { + // No more sockets to accept right now. + // Check for this code first, since it's the most common. + return; + } else if (errno == EMFILE || errno == ENFILE) { + // We're out of file descriptors. Perhaps we're accepting connections + // too quickly. Pause accepting briefly to back off and give the server + // a chance to recover. + LOG(ERROR) << "accept failed: out of file descriptors; entering accept " + "back-off state"; + enterBackoff(); + + // Dispatch the error message + dispatchError("accept() failed", errno); + } else { + dispatchError("accept() failed", errno); + } + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionAcceptError(errno); + } + return; + } + +#if !FOLLY_HAVE_ACCEPT4 + // Explicitly set the new connection to non-blocking mode + if (netops::set_socket_non_blocking(clientSocket) != 0) { + closeNoInt(clientSocket); + dispatchError( + "failed to set accepted socket to non-blocking mode", errno); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped(clientSocket, address); + } + return; + } +#endif + + // Inform the callback about the new connection + dispatchSocket(clientSocket, std::move(address)); + + // If we aren't accepting any more, break out of the loop + if (!accepting_ || callbacks_.empty()) { + break; + } + } +} + +void AsyncServerSocket::dispatchSocket( + NetworkSocket socket, + SocketAddress&& address) { + uint32_t startingIndex = callbackIndex_; + + // Short circuit if the callback is in the primary EventBase thread + + CallbackInfo* info = nextCallback(); + if (info->eventBase == nullptr || info->eventBase == this->eventBase_) { + info->callback->connectionAccepted(socket, address); + return; + } + + const SocketAddress addr(address); + // Create a message to send over the notification queue + QueueMessage msg; + msg.type = MessageType::MSG_NEW_CONN; + msg.address = std::move(address); + msg.fd = socket; + + // Loop until we find a free queue to write to + while (true) { + if (info->consumer->getQueue()->tryPutMessageNoThrow(std::move(msg))) { + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionEnqueuedForAcceptorCallback( + socket, addr); + } + // Success! return. + return; + } + + // We couldn't add to queue. Fall through to below + + ++numDroppedConnections_; + if (acceptRateAdjustSpeed_ > 0) { + // aggressively decrease accept rate when in trouble + static const double kAcceptRateDecreaseSpeed = 0.1; + acceptRate_ *= 1 - kAcceptRateDecreaseSpeed; + } + + if (callbackIndex_ == startingIndex) { + // The notification queue was full + // We can't really do anything at this point other than close the socket. + // + // This should only happen if a user's service is behaving extremely + // badly and none of the EventBase threads are looping fast enough to + // process the incoming connections. If the service is overloaded, it + // should use pauseAccepting() to temporarily back off accepting new + // connections, before they reach the point where their threads can't + // even accept new messages. + LOG_EVERY_N(ERROR, 100) << "failed to dispatch newly accepted socket:" + << " all accept callback queues are full"; + closeNoInt(socket); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped(socket, addr); + } + return; + } + + info = nextCallback(); + } +} + +void AsyncServerSocket::dispatchError(const char* msgstr, int errnoValue) { + uint32_t startingIndex = callbackIndex_; + CallbackInfo* info = nextCallback(); + + // Create a message to send over the notification queue + QueueMessage msg; + msg.type = MessageType::MSG_ERROR; + msg.err = errnoValue; + msg.msg = msgstr; + + while (true) { + // Short circuit if the callback is in the primary EventBase thread + if (info->eventBase == nullptr || info->eventBase == this->eventBase_) { + std::runtime_error ex( + std::string(msgstr) + folly::to<std::string>(errnoValue)); + info->callback->acceptError(ex); + return; + } + + if (info->consumer->getQueue()->tryPutMessageNoThrow(std::move(msg))) { + return; + } + // Fall through and try another callback + + if (callbackIndex_ == startingIndex) { + // The notification queues for all of the callbacks were full. + // We can't really do anything at this point. + LOG_EVERY_N(ERROR, 100) + << "failed to dispatch accept error: all accept" + << " callback queues are full: error msg: " << msg.msg << ": " + << errnoValue; + return; + } + info = nextCallback(); + } +} + +void AsyncServerSocket::enterBackoff() { + // If this is the first time we have entered the backoff state, + // allocate backoffTimeout_. + if (backoffTimeout_ == nullptr) { + try { + backoffTimeout_ = new BackoffTimeout(this); + } catch (const std::bad_alloc&) { + // Man, we couldn't even allocate the timer to re-enable accepts. + // We must be in pretty bad shape. Don't pause accepting for now, + // since we won't be able to re-enable ourselves later. + LOG(ERROR) << "failed to allocate AsyncServerSocket backoff" + << " timer; unable to temporarly pause accepting"; + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffError(); + } + return; + } + } + + // For now, we simply pause accepting for 1 second. + // + // We could add some smarter backoff calculation here in the future. (e.g., + // start sleeping for longer if we keep hitting the backoff frequently.) + // Typically the user needs to figure out why the server is overloaded and + // fix it in some other way, though. The backoff timer is just a simple + // mechanism to try and give the connection processing code a little bit of + // breathing room to catch up, and to avoid just spinning and failing to + // accept over and over again. + const uint32_t timeoutMS = 1000; + if (!backoffTimeout_->scheduleTimeout(timeoutMS)) { + LOG(ERROR) << "failed to schedule AsyncServerSocket backoff timer;" + << "unable to temporarly pause accepting"; + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffError(); + } + return; + } + + // The backoff timer is scheduled to re-enable accepts. + // Go ahead and disable accepts for now. We leave accepting_ set to true, + // since that tracks the desired state requested by the user. + for (auto& handler : sockets_) { + handler.unregisterHandler(); + } + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffStarted(); + } +} + +void AsyncServerSocket::backoffTimeoutExpired() { + // accepting_ should still be true. + // If pauseAccepting() was called while in the backoff state it will cancel + // the backoff timeout. + assert(accepting_); + // We can't be detached from the EventBase without being paused + assert(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + + // If all of the callbacks were removed, we shouldn't re-enable accepts + if (callbacks_.empty()) { + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffEnded(); + } + return; + } + + // Register the handler. + for (auto& handler : sockets_) { + if (!handler.registerHandler(EventHandler::READ | EventHandler::PERSIST)) { + // We're hosed. We could just re-schedule backoffTimeout_ to + // re-try again after a little bit. However, we don't want to + // loop retrying forever if we can't re-enable accepts. Just + // abort the entire program in this state; things are really bad + // and restarting the entire server is probably the best remedy. + LOG(ERROR) + << "failed to re-enable AsyncServerSocket accepts after backoff; " + << "crashing now"; + abort(); + } + } + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffEnded(); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.h new file mode 100644 index 000000000..8d741a1a9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncServerSocket.h @@ -0,0 +1,901 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/SocketAddress.h> +#include <folly/String.h> +#include <folly/io/ShutdownSocketSet.h> +#include <folly/io/async/AsyncSocketBase.h> +#include <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/EventHandler.h> +#include <folly/io/async/NotificationQueue.h> +#include <folly/net/NetOps.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Sockets.h> + +#include <limits.h> +#include <stddef.h> +#include <exception> +#include <memory> +#include <vector> + +// Due to the way kernel headers are included, this may or may not be defined. +// Number pulled from 3.10 kernel headers. +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +#if defined __linux__ && !defined SO_NO_TRANSPARENT_TLS +#define SO_NO_TRANSPARENT_TLS 200 +#endif + +namespace folly { + +/** + * A listening socket that asynchronously informs a callback whenever a new + * connection has been accepted. + * + * Unlike most async interfaces that always invoke their callback in the same + * EventBase thread, AsyncServerSocket is unusual in that it can distribute + * the callbacks across multiple EventBase threads. + * + * This supports a common use case for network servers to distribute incoming + * connections across a number of EventBase threads. (Servers typically run + * with one EventBase thread per CPU.) + * + * Despite being able to invoke callbacks in multiple EventBase threads, + * AsyncServerSocket still has one "primary" EventBase. Operations that + * modify the AsyncServerSocket state may only be performed from the primary + * EventBase thread. + */ +class AsyncServerSocket : public DelayedDestruction, public AsyncSocketBase { + public: + typedef std::unique_ptr<AsyncServerSocket, Destructor> UniquePtr; + // Disallow copy, move, and default construction. + AsyncServerSocket(AsyncServerSocket&&) = delete; + + /** + * A callback interface to get notified of client socket events. + * + * The ConnectionEventCallback implementations need to be thread-safe as the + * callbacks may be called from different threads. + */ + class ConnectionEventCallback { + public: + virtual ~ConnectionEventCallback() = default; + + /** + * onConnectionAccepted() is called right after a client connection + * is accepted using the system accept()/accept4() APIs. + */ + virtual void onConnectionAccepted( + const NetworkSocket socket, + const SocketAddress& addr) noexcept = 0; + + /** + * onConnectionAcceptError() is called when an error occurred accepting + * a connection. + */ + virtual void onConnectionAcceptError(const int err) noexcept = 0; + + /** + * onConnectionDropped() is called when a connection is dropped, + * probably because of some error encountered. + */ + virtual void onConnectionDropped( + const NetworkSocket socket, + const SocketAddress& addr) noexcept = 0; + + /** + * onConnectionEnqueuedForAcceptorCallback() is called when the + * connection is successfully enqueued for an AcceptCallback to pick up. + */ + virtual void onConnectionEnqueuedForAcceptorCallback( + const NetworkSocket socket, + const SocketAddress& addr) noexcept = 0; + + /** + * onConnectionDequeuedByAcceptorCallback() is called when the + * connection is successfully dequeued by an AcceptCallback. + */ + virtual void onConnectionDequeuedByAcceptorCallback( + const NetworkSocket socket, + const SocketAddress& addr) noexcept = 0; + + /** + * onBackoffStarted is called when the socket has successfully started + * backing off accepting new client sockets. + */ + virtual void onBackoffStarted() noexcept = 0; + + /** + * onBackoffEnded is called when the backoff period has ended and the socket + * has successfully resumed accepting new connections if there is any + * AcceptCallback registered. + */ + virtual void onBackoffEnded() noexcept = 0; + + /** + * onBackoffError is called when there is an error entering backoff + */ + virtual void onBackoffError() noexcept = 0; + }; + + class AcceptCallback { + public: + virtual ~AcceptCallback() = default; + + /** + * connectionAccepted() is called whenever a new client connection is + * received. + * + * The AcceptCallback will remain installed after connectionAccepted() + * returns. + * + * @param fd The newly accepted client socket. The AcceptCallback + * assumes ownership of this socket, and is responsible + * for closing it when done. The newly accepted file + * descriptor will have already been put into + * non-blocking mode. + * @param clientAddr A reference to a SocketAddress struct containing the + * client's address. This struct is only guaranteed to + * remain valid until connectionAccepted() returns. + */ + virtual void connectionAccepted( + NetworkSocket fd, + const SocketAddress& clientAddr) noexcept = 0; + + /** + * acceptError() is called if an error occurs while accepting. + * + * The AcceptCallback will remain installed even after an accept error, + * as the errors are typically somewhat transient, such as being out of + * file descriptors. The server socket must be explicitly stopped if you + * wish to stop accepting after an error. + * + * @param ex An exception representing the error. + */ + virtual void acceptError(const std::exception& ex) noexcept = 0; + + /** + * acceptStarted() will be called in the callback's EventBase thread + * after this callback has been added to the AsyncServerSocket. + * + * acceptStarted() will be called before any calls to connectionAccepted() + * or acceptError() are made on this callback. + * + * acceptStarted() makes it easier for callbacks to perform initialization + * inside the callback thread. (The call to addAcceptCallback() must + * always be made from the AsyncServerSocket's primary EventBase thread. + * acceptStarted() provides a hook that will always be invoked in the + * callback's thread.) + * + * Note that the call to acceptStarted() is made once the callback is + * added, regardless of whether or not the AsyncServerSocket is actually + * accepting at the moment. acceptStarted() will be called even if the + * AsyncServerSocket is paused when the callback is added (including if + * the initial call to startAccepting() on the AsyncServerSocket has not + * been made yet). + */ + virtual void acceptStarted() noexcept {} + + /** + * acceptStopped() will be called when this AcceptCallback is removed from + * the AsyncServerSocket, or when the AsyncServerSocket is destroyed, + * whichever occurs first. + * + * No more calls to connectionAccepted() or acceptError() will be made + * after acceptStopped() is invoked. + */ + virtual void acceptStopped() noexcept {} + }; + + static const uint32_t kDefaultMaxAcceptAtOnce = 30; + static const uint32_t kDefaultCallbackAcceptAtOnce = 5; + static const uint32_t kDefaultMaxMessagesInQueue = 1024; + /** + * Create a new AsyncServerSocket with the specified EventBase. + * + * @param eventBase The EventBase to use for driving the asynchronous I/O. + * If this parameter is nullptr, attachEventBase() must be + * called before this socket can begin accepting + * connections. + */ + explicit AsyncServerSocket(EventBase* eventBase = nullptr); + + /** + * Helper function to create a shared_ptr<AsyncServerSocket>. + * + * This passes in the correct destructor object, since AsyncServerSocket's + * destructor is protected and cannot be invoked directly. + */ + static std::shared_ptr<AsyncServerSocket> newSocket( + EventBase* evb = nullptr) { + return std::shared_ptr<AsyncServerSocket>( + new AsyncServerSocket(evb), Destructor()); + } + + void setShutdownSocketSet(const std::weak_ptr<ShutdownSocketSet>& wNewSS); + + /** + * Destroy the socket. + * + * AsyncServerSocket::destroy() must be called to destroy the socket. + * The normal destructor is private, and should not be invoked directly. + * This prevents callers from deleting a AsyncServerSocket while it is + * invoking a callback. + * + * destroy() must be invoked from the socket's primary EventBase thread. + * + * If there are AcceptCallbacks still installed when destroy() is called, + * acceptStopped() will be called on these callbacks to notify them that + * accepting has stopped. Accept callbacks being driven by other EventBase + * threads may continue to receive new accept callbacks for a brief period of + * time after destroy() returns. They will not receive any more callback + * invocations once acceptStopped() is invoked. + */ + void destroy() override; + + /** + * Attach this AsyncServerSocket to its primary EventBase. + * + * This may only be called if the AsyncServerSocket is not already attached + * to a EventBase. The AsyncServerSocket must be attached to a EventBase + * before it can begin accepting connections. + */ + void attachEventBase(EventBase* eventBase); + + /** + * Detach the AsyncServerSocket from its primary EventBase. + * + * detachEventBase() may only be called if the AsyncServerSocket is not + * currently accepting connections. + */ + void detachEventBase(); + + /** + * Get the EventBase used by this socket. + */ + EventBase* getEventBase() const override { + return eventBase_; + } + + /** + * Create a AsyncServerSocket from an existing socket file descriptor. + * + * useExistingSocket() will cause the AsyncServerSocket to take ownership of + * the specified file descriptor, and use it to listen for new connections. + * The AsyncServerSocket will close the file descriptor when it is + * destroyed. + * + * useExistingSocket() must be called before bind() or listen(). + * + * The supplied file descriptor will automatically be put into non-blocking + * mode. The caller may have already directly called bind() and possibly + * listen on the file descriptor. If so the caller should skip calling the + * corresponding AsyncServerSocket::bind() and listen() methods. + * + * On error a AsyncSocketException will be thrown and the caller will retain + * ownership of the file descriptor. + */ + void useExistingSocket(NetworkSocket fd); + void useExistingSockets(const std::vector<NetworkSocket>& fds); + + /** + * Return the underlying file descriptor + */ + std::vector<NetworkSocket> getNetworkSockets() const { + std::vector<NetworkSocket> sockets; + for (auto& handler : sockets_) { + sockets.push_back(handler.socket_); + } + return sockets; + } + + /** + * Backwards compatible getSocket, warns if > 1 socket + */ + NetworkSocket getNetworkSocket() const { + if (sockets_.size() > 1) { + VLOG(2) << "Warning: getSocket can return multiple fds, " + << "but getSockets was not called, so only returning the first"; + } + if (sockets_.size() == 0) { + return NetworkSocket(); + } else { + return sockets_[0].socket_; + } + } + + /* enable zerocopy support for the server sockets - the s = accept sockets + * inherit it + */ + bool setZeroCopy(bool enable); + + /** + * Bind to the specified address. + * + * This must be called from the primary EventBase thread. + * + * Throws AsyncSocketException on error. + */ + virtual void bind(const SocketAddress& address); + + /** + * Bind to the specified port for the specified addresses. + * + * This must be called from the primary EventBase thread. + * + * Throws AsyncSocketException on error. + */ + virtual void bind(const std::vector<IPAddress>& ipAddresses, uint16_t port); + + /** + * Bind to the specified port. + * + * This must be called from the primary EventBase thread. + * + * Throws AsyncSocketException on error. + */ + virtual void bind(uint16_t port); + + /** + * Get the local address to which the socket is bound. + * + * Throws AsyncSocketException on error. + */ + void getAddress(SocketAddress* addressReturn) const override; + + /** + * Get the local address to which the socket is bound. + * + * Throws AsyncSocketException on error. + */ + SocketAddress getAddress() const { + SocketAddress ret; + getAddress(&ret); + return ret; + } + + /** + * Get all the local addresses to which the socket is bound. + * + * Throws AsyncSocketException on error. + */ + std::vector<SocketAddress> getAddresses() const; + + /** + * Begin listening for connections. + * + * This calls ::listen() with the specified backlog. + * + * Once listen() is invoked the socket will actually be open so that remote + * clients may establish connections. (Clients that attempt to connect + * before listen() is called will receive a connection refused error.) + * + * At least one callback must be set and startAccepting() must be called to + * actually begin notifying the accept callbacks of newly accepted + * connections. The backlog parameter controls how many connections the + * kernel will accept and buffer internally while the accept callbacks are + * paused (or if accepting is enabled but the callbacks cannot keep up). + * + * bind() must be called before calling listen(). + * listen() must be called from the primary EventBase thread. + * + * Throws AsyncSocketException on error. + */ + virtual void listen(int backlog); + + /** + * Add an AcceptCallback. + * + * When a new socket is accepted, one of the AcceptCallbacks will be invoked + * with the new socket. The AcceptCallbacks are invoked in a round-robin + * fashion. This allows the accepted sockets to be distributed among a pool + * of threads, each running its own EventBase object. This is a common model, + * since most asynchronous-style servers typically run one EventBase thread + * per CPU. + * + * The EventBase object associated with each AcceptCallback must be running + * its loop. If the EventBase loop is not running, sockets will still be + * scheduled for the callback, but the callback cannot actually get invoked + * until the loop runs. + * + * This method must be invoked from the AsyncServerSocket's primary + * EventBase thread. + * + * Note that startAccepting() must be called on the AsyncServerSocket to + * cause it to actually start accepting sockets once callbacks have been + * installed. + * + * @param callback The callback to invoke. + * @param eventBase The EventBase to use to invoke the callback. This + * parameter may be nullptr, in which case the callback will be invoked in + * the AsyncServerSocket's primary EventBase. + * @param maxAtOnce The maximum number of connections to accept in this + * callback on a single iteration of the event base loop. + * This only takes effect when eventBase is non-nullptr. + * When using a nullptr eventBase for the callback, the + * setMaxAcceptAtOnce() method controls how many + * connections the main event base will accept at once. + */ + virtual void addAcceptCallback( + AcceptCallback* callback, + EventBase* eventBase, + uint32_t maxAtOnce = kDefaultCallbackAcceptAtOnce); + + /** + * Remove an AcceptCallback. + * + * This allows a single AcceptCallback to be removed from the round-robin + * pool. + * + * This method must be invoked from the AsyncServerSocket's primary + * EventBase thread. Use EventBase::runInEventBaseThread() to schedule the + * operation in the correct EventBase if your code is not in the server + * socket's primary EventBase. + * + * Given that the accept callback is being driven by a different EventBase, + * the AcceptCallback may continue to be invoked for a short period of time + * after removeAcceptCallback() returns in this thread. Once the other + * EventBase thread receives the notification to stop, it will call + * acceptStopped() on the callback to inform it that it is fully stopped and + * will not receive any new sockets. + * + * If the last accept callback is removed while the socket is accepting, + * the socket will implicitly pause accepting. If a callback is later added, + * it will resume accepting immediately, without requiring startAccepting() + * to be invoked. + * + * @param callback The callback to uninstall. + * @param eventBase The EventBase associated with this callback. This must + * be the same EventBase that was used when the callback was installed + * with addAcceptCallback(). + */ + void removeAcceptCallback(AcceptCallback* callback, EventBase* eventBase); + + /** + * Begin accepting connctions on this socket. + * + * bind() and listen() must be called before calling startAccepting(). + * + * When a AsyncServerSocket is initially created, it will not begin + * accepting connections until at least one callback has been added and + * startAccepting() has been called. startAccepting() can also be used to + * resume accepting connections after a call to pauseAccepting(). + * + * If startAccepting() is called when there are no accept callbacks + * installed, the socket will not actually begin accepting until an accept + * callback is added. + * + * This method may only be called from the primary EventBase thread. + */ + virtual void startAccepting(); + + /** + * Pause accepting connections. + * + * startAccepting() may be called to resume accepting. + * + * This method may only be called from the primary EventBase thread. + * If there are AcceptCallbacks being driven by other EventBase threads they + * may continue to receive callbacks for a short period of time after + * pauseAccepting() returns. + * + * Unlike removeAcceptCallback() or destroy(), acceptStopped() will not be + * called on the AcceptCallback objects simply due to a temporary pause. If + * the server socket is later destroyed while paused, acceptStopped() will be + * called all of the installed AcceptCallbacks. + */ + void pauseAccepting(); + + /** + * Shutdown the listen socket and notify all callbacks that accept has + * stopped, but don't close the socket. This invokes shutdown(2) with the + * supplied argument. Passing -1 will close the socket now. Otherwise, the + * close will be delayed until this object is destroyed. + * + * Only use this if you have reason to pass special flags to shutdown. + * Otherwise just destroy the socket. + * + * This method has no effect when a ShutdownSocketSet option is used. + * + * Returns the result of shutdown on sockets_[n-1] + */ + int stopAccepting(int shutdownFlags = -1); + + /** + * Get the maximum number of connections that will be accepted each time + * around the event loop. + */ + uint32_t getMaxAcceptAtOnce() const { + return maxAcceptAtOnce_; + } + + /** + * Set the maximum number of connections that will be accepted each time + * around the event loop. + * + * This provides a very coarse-grained way of controlling how fast the + * AsyncServerSocket will accept connections. If you find that when your + * server is overloaded AsyncServerSocket accepts connections more quickly + * than your code can process them, you can try lowering this number so that + * fewer connections will be accepted each event loop iteration. + * + * For more explicit control over the accept rate, you can also use + * pauseAccepting() to temporarily pause accepting when your server is + * overloaded, and then use startAccepting() later to resume accepting. + */ + void setMaxAcceptAtOnce(uint32_t numConns) { + maxAcceptAtOnce_ = numConns; + } + + /** + * Get the maximum number of unprocessed messages which a NotificationQueue + * can hold. + */ + uint32_t getMaxNumMessagesInQueue() const { + return maxNumMsgsInQueue_; + } + + /** + * Set the maximum number of unprocessed messages in NotificationQueue. + * No new message will be sent to that NotificationQueue if there are more + * than such number of unprocessed messages in that queue. + * + * Only works if called before addAcceptCallback. + */ + void setMaxNumMessagesInQueue(uint32_t num) { + maxNumMsgsInQueue_ = num; + } + + /** + * Get the speed of adjusting connection accept rate. + */ + double getAcceptRateAdjustSpeed() const { + return acceptRateAdjustSpeed_; + } + + /** + * Set the speed of adjusting connection accept rate. + */ + void setAcceptRateAdjustSpeed(double speed) { + acceptRateAdjustSpeed_ = speed; + } + + /** + * Enable/Disable TOS reflection for the server socket + */ + void setTosReflect(bool enable); + + bool getTosReflect() { + return tosReflect_; + } + + /** + * Get the number of connections dropped by the AsyncServerSocket + */ + std::size_t getNumDroppedConnections() const { + return numDroppedConnections_; + } + + /** + * Get the current number of unprocessed messages in NotificationQueue. + * + * This method must be invoked from the AsyncServerSocket's primary + * EventBase thread. Use EventBase::runInEventBaseThread() to schedule the + * operation in the correct EventBase if your code is not in the server + * socket's primary EventBase. + */ + int64_t getNumPendingMessagesInQueue() const { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + int64_t numMsgs = 0; + for (const auto& callback : callbacks_) { + if (callback.consumer) { + numMsgs += callback.consumer->getQueue()->size(); + } + } + return numMsgs; + } + + /** + * Set whether or not SO_KEEPALIVE should be enabled on the server socket + * (and thus on all subsequently-accepted connections). By default, keepalive + * is enabled. + * + * Note that TCP keepalive usually only kicks in after the connection has + * been idle for several hours. Applications should almost always have their + * own, shorter idle timeout. + */ + void setKeepAliveEnabled(bool enabled) { + keepAliveEnabled_ = enabled; + + for (auto& handler : sockets_) { + if (handler.socket_ == NetworkSocket()) { + continue; + } + + int val = (enabled) ? 1 : 0; + if (netops::setsockopt( + handler.socket_, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != + 0) { + LOG(ERROR) << "failed to set SO_KEEPALIVE on async server socket: %s" + << errnoStr(errno); + } + } + } + + /** + * Get whether or not SO_KEEPALIVE is enabled on the server socket. + */ + bool getKeepAliveEnabled() const { + return keepAliveEnabled_; + } + + /** + * Set whether or not SO_REUSEPORT should be enabled on the server socket, + * allowing multiple binds to the same port + */ + void setReusePortEnabled(bool enabled) { + reusePortEnabled_ = enabled; + + for (auto& handler : sockets_) { + if (handler.socket_ == NetworkSocket()) { + continue; + } + + int val = (enabled) ? 1 : 0; + if (netops::setsockopt( + handler.socket_, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != + 0) { + auto errnoCopy = errno; + LOG(ERROR) << "failed to set SO_REUSEPORT on async server socket " + << errnoCopy; + folly::throwSystemErrorExplicit( + errnoCopy, "failed to set SO_REUSEPORT on async server socket"); + } + } + } + + /** + * Get whether or not SO_REUSEPORT is enabled on the server socket. + */ + bool getReusePortEnabled_() const { + return reusePortEnabled_; + } + + /** + * Set whether or not the socket should close during exec() (FD_CLOEXEC). By + * default, this is enabled + */ + void setCloseOnExec(bool closeOnExec) { + closeOnExec_ = closeOnExec; + } + + /** + * Get whether or not FD_CLOEXEC is enabled on the server socket. + */ + bool getCloseOnExec() const { + return closeOnExec_; + } + + /** + * Tries to enable TFO if the machine supports it. + */ + void setTFOEnabled(bool enabled, uint32_t maxTFOQueueSize) { + tfo_ = enabled; + tfoMaxQueueSize_ = maxTFOQueueSize; + } + + /** + * Do not attempt the transparent TLS handshake + */ + void disableTransparentTls() { + noTransparentTls_ = true; + } + + /** + * Get whether or not the socket is accepting new connections + */ + bool getAccepting() const { + return accepting_; + } + + /** + * Set the ConnectionEventCallback + */ + void setConnectionEventCallback( + ConnectionEventCallback* const connectionEventCallback) { + connectionEventCallback_ = connectionEventCallback; + } + + /** + * Get the ConnectionEventCallback + */ + ConnectionEventCallback* getConnectionEventCallback() const { + return connectionEventCallback_; + } + + protected: + /** + * Protected destructor. + * + * Invoke destroy() instead to destroy the AsyncServerSocket. + */ + ~AsyncServerSocket() override; + + private: + enum class MessageType { MSG_NEW_CONN = 0, MSG_ERROR = 1 }; + + struct QueueMessage { + MessageType type; + NetworkSocket fd; + int err; + SocketAddress address; + std::string msg; + }; + + /** + * A class to receive notifications to invoke AcceptCallback objects + * in other EventBase threads. + * + * A RemoteAcceptor object is created for each AcceptCallback that + * is installed in a separate EventBase thread. The RemoteAcceptor + * receives notification of new sockets via a NotificationQueue, + * and then invokes the AcceptCallback. + */ + class RemoteAcceptor : private NotificationQueue<QueueMessage>::Consumer { + public: + explicit RemoteAcceptor( + AcceptCallback* callback, + ConnectionEventCallback* connectionEventCallback) + : callback_(callback), + connectionEventCallback_(connectionEventCallback) {} + + ~RemoteAcceptor() override = default; + + void start(EventBase* eventBase, uint32_t maxAtOnce, uint32_t maxInQueue); + void stop(EventBase* eventBase, AcceptCallback* callback); + + void messageAvailable(QueueMessage&& msg) noexcept override; + + NotificationQueue<QueueMessage>* getQueue() { + return &queue_; + } + + private: + AcceptCallback* callback_; + ConnectionEventCallback* connectionEventCallback_; + + NotificationQueue<QueueMessage> queue_; + }; + + /** + * A struct to keep track of the callbacks associated with this server + * socket. + */ + struct CallbackInfo { + CallbackInfo(AcceptCallback* cb, EventBase* evb) + : callback(cb), eventBase(evb), consumer(nullptr) {} + + AcceptCallback* callback; + EventBase* eventBase; + + RemoteAcceptor* consumer; + }; + + class BackoffTimeout; + + virtual void + handlerReady(uint16_t events, NetworkSocket fd, sa_family_t family) noexcept; + + NetworkSocket createSocket(int family); + void setupSocket(NetworkSocket fd, int family); + void bindSocket( + NetworkSocket fd, + const SocketAddress& address, + bool isExistingSocket); + void dispatchSocket(NetworkSocket socket, SocketAddress&& address); + void dispatchError(const char* msg, int errnoValue); + void enterBackoff(); + void backoffTimeoutExpired(); + + CallbackInfo* nextCallback() { + CallbackInfo* info = &callbacks_[callbackIndex_]; + + ++callbackIndex_; + if (callbackIndex_ >= callbacks_.size()) { + callbackIndex_ = 0; + } + + return info; + } + + struct ServerEventHandler : public EventHandler { + ServerEventHandler( + EventBase* eventBase, + NetworkSocket socket, + AsyncServerSocket* parent, + sa_family_t addressFamily) + : EventHandler(eventBase, socket), + eventBase_(eventBase), + socket_(socket), + parent_(parent), + addressFamily_(addressFamily) {} + + ServerEventHandler(const ServerEventHandler& other) + : EventHandler(other.eventBase_, other.socket_), + eventBase_(other.eventBase_), + socket_(other.socket_), + parent_(other.parent_), + addressFamily_(other.addressFamily_) {} + + ServerEventHandler& operator=(const ServerEventHandler& other) { + if (this != &other) { + eventBase_ = other.eventBase_; + socket_ = other.socket_; + parent_ = other.parent_; + addressFamily_ = other.addressFamily_; + + detachEventBase(); + attachEventBase(other.eventBase_); + changeHandlerFD(other.socket_); + } + return *this; + } + + // Inherited from EventHandler + void handlerReady(uint16_t events) noexcept override { + parent_->handlerReady(events, socket_, addressFamily_); + } + + EventBase* eventBase_; + NetworkSocket socket_; + AsyncServerSocket* parent_; + sa_family_t addressFamily_; + }; + + EventBase* eventBase_; + std::vector<ServerEventHandler> sockets_; + std::vector<NetworkSocket> pendingCloseSockets_; + bool accepting_; + uint32_t maxAcceptAtOnce_; + uint32_t maxNumMsgsInQueue_; + double acceptRateAdjustSpeed_; // 0 to disable auto adjust + double acceptRate_; + std::chrono::time_point<std::chrono::steady_clock> lastAccepTimestamp_; + std::size_t numDroppedConnections_; + uint32_t callbackIndex_; + BackoffTimeout* backoffTimeout_; + std::vector<CallbackInfo> callbacks_; + bool keepAliveEnabled_; + bool reusePortEnabled_{false}; + bool closeOnExec_; + bool tfo_{false}; + bool noTransparentTls_{false}; + uint32_t tfoMaxQueueSize_{0}; + std::weak_ptr<ShutdownSocketSet> wShutdownSocketSet_; + ConnectionEventCallback* connectionEventCallback_{nullptr}; + bool tosReflect_{false}; + bool zeroCopyVal_{false}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.cpp new file mode 100644 index 000000000..8ca810cf9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncSignalHandler.h> + +#include <folly/io/async/EventBase.h> + +#include <folly/Conv.h> + +using std::make_pair; +using std::pair; +using std::string; + +namespace folly { + +AsyncSignalHandler::AsyncSignalHandler(EventBase* eventBase) + : eventBase_(eventBase) {} + +AsyncSignalHandler::~AsyncSignalHandler() { + // Unregister any outstanding events + for (auto& signalEvent : signalEvents_) { + signalEvent.second->eb_event_del(); + } +} + +void AsyncSignalHandler::attachEventBase(EventBase* eventBase) { + assert(eventBase_ == nullptr); + assert(signalEvents_.empty()); + eventBase_ = eventBase; +} + +void AsyncSignalHandler::detachEventBase() { + assert(eventBase_ != nullptr); + assert(signalEvents_.empty()); + eventBase_ = nullptr; +} + +void AsyncSignalHandler::registerSignalHandler(int signum) { + pair<SignalEventMap::iterator, bool> ret = signalEvents_.insert( + make_pair(signum, std::make_unique<EventBaseEvent>())); + if (!ret.second) { + // This signal has already been registered + throw std::runtime_error( + folly::to<string>("handler already registered for signal ", signum)); + } + + EventBaseEvent* ev = ret.first->second.get(); + try { + ev->eb_signal_set(signum, libeventCallback, this); + if (ev->eb_event_base_set(eventBase_) != 0) { + throw std::runtime_error(folly::to<string>( + "error initializing event handler for signal ", signum)); + } + + if (ev->eb_event_add(nullptr) != 0) { + throw std::runtime_error( + folly::to<string>("error adding event handler for signal ", signum)); + } + } catch (...) { + signalEvents_.erase(ret.first); + throw; + } +} + +void AsyncSignalHandler::unregisterSignalHandler(int signum) { + auto it = signalEvents_.find(signum); + if (it == signalEvents_.end()) { + throw std::runtime_error(folly::to<string>( + "unable to unregister handler for signal ", + signum, + ": signal not registered")); + } + + it->second->eb_event_del(); + signalEvents_.erase(it); +} + +void AsyncSignalHandler::libeventCallback( + libevent_fd_t signum, + short /* events */, + void* arg) { + auto handler = static_cast<AsyncSignalHandler*>(arg); + handler->signalReceived(int(signum)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.h new file mode 100644 index 000000000..f50f04a74 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSignalHandler.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/EventBase.h> +#include <folly/portability/Event.h> +#include <map> + +namespace folly { + +/** + * A handler to receive notification about POSIX signals. + * + * AsyncSignalHandler allows code to process signals from within a EventBase + * loop. + * + * Standard signal handlers interrupt execution of the main thread, and + * are run while the main thread is paused. As a result, great care must be + * taken to avoid race conditions if the signal handler has to access or modify + * any data used by the main thread. + * + * AsyncSignalHandler solves this problem by running the AsyncSignalHandler + * callback in normal thread of execution, as a EventBase callback. + * + * AsyncSignalHandler may only be used in a single thread. It will only + * process signals received by the thread where the AsyncSignalHandler is + * registered. It is the user's responsibility to ensure that signals are + * delivered to the desired thread in multi-threaded programs. + */ +class AsyncSignalHandler { + public: + /** + * Create a new AsyncSignalHandler. + */ + explicit AsyncSignalHandler(EventBase* eventBase); + virtual ~AsyncSignalHandler(); + + /** + * Attach this AsyncSignalHandler to an EventBase. + * + * This should only be called if the AsyncSignalHandler is not currently + * registered for any signals and is not currently attached to an existing + * EventBase. + */ + void attachEventBase(EventBase* eventBase); + + /** + * Detach this AsyncSignalHandler from its EventBase. + * + * This should only be called if the AsyncSignalHandler is not currently + * registered for any signals. + */ + void detachEventBase(); + + /** + * Get the EventBase used by this AsyncSignalHandler. + */ + EventBase* getEventBase() const { + return eventBase_; + } + + /** + * Register to receive callbacks about the specified signal. + * + * Once the handler has been registered for a particular signal, + * signalReceived() will be called each time this thread receives this + * signal. + * + * Throws if an error occurs or if this handler is already + * registered for this signal. + */ + void registerSignalHandler(int signum); + + /** + * Unregister for callbacks about the specified signal. + * + * Throws if an error occurs, or if this signal was not registered. + */ + void unregisterSignalHandler(int signum); + + /** + * signalReceived() will called to indicate that the specified signal has + * been received. + * + * signalReceived() will always be invoked from the EventBase loop (i.e., + * after the main POSIX signal handler has returned control to the EventBase + * thread). + */ + virtual void signalReceived(int signum) noexcept = 0; + + private: + // we cannot copy the EventBaseEvent instances + // so we need to store ptrs to them + // Also some backends store ptrs to the EventBaseEvent instances + using SignalEventMap = std::map<int, std::unique_ptr<EventBaseEvent>>; + + // Forbidden copy constructor and assignment operator + AsyncSignalHandler(AsyncSignalHandler const&); + AsyncSignalHandler& operator=(AsyncSignalHandler const&); + + static void libeventCallback(libevent_fd_t signum, short events, void* arg); + + EventBase* eventBase_{nullptr}; + SignalEventMap signalEvents_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.cpp new file mode 100644 index 000000000..5626a10f8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.cpp @@ -0,0 +1,2935 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncSocket.h> + +#include <folly/ExceptionWrapper.h> +#include <folly/Format.h> +#include <folly/Portability.h> +#include <folly/SocketAddress.h> +#include <folly/String.h> +#include <folly/io/Cursor.h> +#include <folly/io/IOBuf.h> +#include <folly/io/IOBufQueue.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/SysUio.h> +#include <folly/portability/Unistd.h> + +#include <boost/preprocessor/control/if.hpp> +#include <sys/types.h> +#include <cerrno> +#include <climits> +#include <sstream> +#include <thread> + +#if defined(__linux__) +#include <linux/sockios.h> +#include <sys/ioctl.h> +#endif + +#if FOLLY_HAVE_VLA +#define FOLLY_HAVE_VLA_01 1 +#else +#define FOLLY_HAVE_VLA_01 0 +#endif + +using std::string; +using std::unique_ptr; + +namespace fsp = folly::portability::sockets; + +namespace folly { + +static constexpr bool msgErrQueueSupported = +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + true; +#else + false; +#endif // FOLLY_HAVE_MSG_ERRQUEUE + +const AsyncSocketException socketClosedLocallyEx( + AsyncSocketException::END_OF_FILE, + "socket closed locally"); +const AsyncSocketException socketShutdownForWritesEx( + AsyncSocketException::END_OF_FILE, + "socket shutdown for writes"); + +// TODO: It might help performance to provide a version of BytesWriteRequest +// that users could derive from, so we can avoid the extra allocation for each +// call to write()/writev(). +// +// We would need the version for external users where they provide the iovec +// storage space, and only our internal version would allocate it at the end of +// the WriteRequest. + +/* The default WriteRequest implementation, used for write(), writev() and + * writeChain() + * + * A new BytesWriteRequest operation is allocated on the heap for all write + * operations that cannot be completed immediately. + */ +class AsyncSocket::BytesWriteRequest : public AsyncSocket::WriteRequest { + public: + static BytesWriteRequest* newRequest( + AsyncSocket* socket, + WriteCallback* callback, + const iovec* ops, + uint32_t opCount, + uint32_t partialWritten, + uint32_t bytesWritten, + unique_ptr<IOBuf>&& ioBuf, + WriteFlags flags) { + assert(opCount > 0); + // Since we put a variable size iovec array at the end + // of each BytesWriteRequest, we have to manually allocate the memory. + void* buf = + malloc(sizeof(BytesWriteRequest) + (opCount * sizeof(struct iovec))); + if (buf == nullptr) { + throw std::bad_alloc(); + } + + return new (buf) BytesWriteRequest( + socket, + callback, + ops, + opCount, + partialWritten, + bytesWritten, + std::move(ioBuf), + flags); + } + + void destroy() override { + this->~BytesWriteRequest(); + free(this); + } + + WriteResult performWrite() override { + WriteFlags writeFlags = flags_; + if (getNext() != nullptr) { + writeFlags |= WriteFlags::CORK; + } + + socket_->adjustZeroCopyFlags(writeFlags); + + auto writeResult = socket_->performWrite( + getOps(), getOpCount(), writeFlags, &opsWritten_, &partialBytes_); + bytesWritten_ = writeResult.writeReturn > 0 ? writeResult.writeReturn : 0; + if (bytesWritten_) { + if (socket_->isZeroCopyRequest(writeFlags)) { + if (isComplete()) { + socket_->addZeroCopyBuf(std::move(ioBuf_)); + } else { + socket_->addZeroCopyBuf(ioBuf_.get()); + } + } else { + // this happens if at least one of the prev requests were sent + // with zero copy but not the last one + if (isComplete() && socket_->getZeroCopy() && + socket_->containsZeroCopyBuf(ioBuf_.get())) { + socket_->setZeroCopyBuf(std::move(ioBuf_)); + } + } + } + return writeResult; + } + + bool isComplete() override { + return opsWritten_ == getOpCount(); + } + + void consume() override { + // Advance opIndex_ forward by opsWritten_ + opIndex_ += opsWritten_; + assert(opIndex_ < opCount_); + + if (!socket_->isZeroCopyRequest(flags_)) { + // If we've finished writing any IOBufs, release them + if (ioBuf_) { + for (uint32_t i = opsWritten_; i != 0; --i) { + assert(ioBuf_); + ioBuf_ = ioBuf_->pop(); + } + } + } + + // Move partialBytes_ forward into the current iovec buffer + struct iovec* currentOp = writeOps_ + opIndex_; + assert((partialBytes_ < currentOp->iov_len) || (currentOp->iov_len == 0)); + currentOp->iov_base = + reinterpret_cast<uint8_t*>(currentOp->iov_base) + partialBytes_; + currentOp->iov_len -= partialBytes_; + + // Increment the totalBytesWritten_ count by bytesWritten_; + assert(bytesWritten_ >= 0); + totalBytesWritten_ += uint32_t(bytesWritten_); + } + + private: + BytesWriteRequest( + AsyncSocket* socket, + WriteCallback* callback, + const struct iovec* ops, + uint32_t opCount, + uint32_t partialBytes, + uint32_t bytesWritten, + unique_ptr<IOBuf>&& ioBuf, + WriteFlags flags) + : AsyncSocket::WriteRequest(socket, callback), + opCount_(opCount), + opIndex_(0), + flags_(flags), + ioBuf_(std::move(ioBuf)), + opsWritten_(0), + partialBytes_(partialBytes), + bytesWritten_(bytesWritten) { + memcpy(writeOps_, ops, sizeof(*ops) * opCount_); + } + + // private destructor, to ensure callers use destroy() + ~BytesWriteRequest() override = default; + + const struct iovec* getOps() const { + assert(opCount_ > opIndex_); + return writeOps_ + opIndex_; + } + + uint32_t getOpCount() const { + assert(opCount_ > opIndex_); + return opCount_ - opIndex_; + } + + uint32_t opCount_; ///< number of entries in writeOps_ + uint32_t opIndex_; ///< current index into writeOps_ + WriteFlags flags_; ///< set for WriteFlags + unique_ptr<IOBuf> ioBuf_; ///< underlying IOBuf, or nullptr if N/A + + // for consume(), how much we wrote on the last write + uint32_t opsWritten_; ///< complete ops written + uint32_t partialBytes_; ///< partial bytes of incomplete op written + ssize_t bytesWritten_; ///< bytes written altogether + + struct iovec writeOps_[]; ///< write operation(s) list +}; + +int AsyncSocket::SendMsgParamsCallback::getDefaultFlags( + folly::WriteFlags flags, + bool zeroCopyEnabled) noexcept { + int msg_flags = MSG_DONTWAIT; + +#ifdef MSG_NOSIGNAL // Linux-only + msg_flags |= MSG_NOSIGNAL; +#ifdef MSG_MORE + if (isSet(flags, WriteFlags::CORK)) { + // MSG_MORE tells the kernel we have more data to send, so wait for us to + // give it the rest of the data rather than immediately sending a partial + // frame, even when TCP_NODELAY is enabled. + msg_flags |= MSG_MORE; + } +#endif // MSG_MORE +#endif // MSG_NOSIGNAL + if (isSet(flags, WriteFlags::EOR)) { + // marks that this is the last byte of a record (response) + msg_flags |= MSG_EOR; + } + + if (zeroCopyEnabled && isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY)) { + msg_flags |= MSG_ZEROCOPY; + } + + return msg_flags; +} + +namespace { +AsyncSocket::SendMsgParamsCallback defaultSendMsgParamsCallback; + +// Based on flags, signal the transparent handler to disable certain functions +void disableTransparentFunctions( + NetworkSocket fd, + bool noTransparentTls, + bool noTSocks) { + (void)fd; + (void)noTransparentTls; + (void)noTSocks; +#if defined(__linux__) + if (noTransparentTls) { + // Ignore return value, errors are ok + VLOG(5) << "Disabling TTLS for fd " << fd; + netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } + if (noTSocks) { + VLOG(5) << "Disabling TSOCKS for fd " << fd; + // Ignore return value, errors are ok + netops::setsockopt(fd, SOL_SOCKET, SO_NO_TSOCKS, nullptr, 0); + } +#endif +} + +} // namespace + +AsyncSocket::AsyncSocket() + : eventBase_(nullptr), + writeTimeout_(this, nullptr), + ioHandler_(this, nullptr), + immediateReadHandler_(this) { + VLOG(5) << "new AsyncSocket()"; + init(); +} + +AsyncSocket::AsyncSocket(EventBase* evb) + : eventBase_(evb), + writeTimeout_(this, evb), + ioHandler_(this, evb), + immediateReadHandler_(this) { + VLOG(5) << "new AsyncSocket(" << this << ", evb=" << evb << ")"; + init(); +} + +AsyncSocket::AsyncSocket( + EventBase* evb, + const folly::SocketAddress& address, + uint32_t connectTimeout, + bool useZeroCopy) + : AsyncSocket(evb) { + setZeroCopy(useZeroCopy); + connect(nullptr, address, connectTimeout); +} + +AsyncSocket::AsyncSocket( + EventBase* evb, + const std::string& ip, + uint16_t port, + uint32_t connectTimeout, + bool useZeroCopy) + : AsyncSocket(evb) { + setZeroCopy(useZeroCopy); + connect(nullptr, ip, port, connectTimeout); +} + +AsyncSocket::AsyncSocket( + EventBase* evb, + NetworkSocket fd, + uint32_t zeroCopyBufId) + : zeroCopyBufId_(zeroCopyBufId), + eventBase_(evb), + writeTimeout_(this, evb), + ioHandler_(this, evb, fd), + immediateReadHandler_(this) { + VLOG(5) << "new AsyncSocket(" << this << ", evb=" << evb << ", fd=" << fd + << ", zeroCopyBufId=" << zeroCopyBufId << ")"; + init(); + fd_ = fd; + disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_); + setCloseOnExec(); + state_ = StateEnum::ESTABLISHED; +} + +AsyncSocket::AsyncSocket(AsyncSocket::UniquePtr oldAsyncSocket) + : AsyncSocket( + oldAsyncSocket->getEventBase(), + oldAsyncSocket->detachNetworkSocket(), + oldAsyncSocket->getZeroCopyBufId()) { + preReceivedData_ = std::move(oldAsyncSocket->preReceivedData_); +} + +// init() method, since constructor forwarding isn't supported in most +// compilers yet. +void AsyncSocket::init() { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + shutdownFlags_ = 0; + state_ = StateEnum::UNINIT; + eventFlags_ = EventHandler::NONE; + fd_ = NetworkSocket(); + sendTimeout_ = 0; + maxReadsPerEvent_ = 16; + connectCallback_ = nullptr; + errMessageCallback_ = nullptr; + readCallback_ = nullptr; + writeReqHead_ = nullptr; + writeReqTail_ = nullptr; + wShutdownSocketSet_.reset(); + appBytesWritten_ = 0; + appBytesReceived_ = 0; + totalAppBytesScheduledForWrite_ = 0; + sendMsgParamCallback_ = &defaultSendMsgParamsCallback; +} + +AsyncSocket::~AsyncSocket() { + VLOG(7) << "actual destruction of AsyncSocket(this=" << this + << ", evb=" << eventBase_ << ", fd=" << fd_ << ", state=" << state_ + << ")"; +} + +void AsyncSocket::destroy() { + VLOG(5) << "AsyncSocket::destroy(this=" << this << ", evb=" << eventBase_ + << ", fd=" << fd_ << ", state=" << state_; + // When destroy is called, close the socket immediately + closeNow(); + + // Then call DelayedDestruction::destroy() to take care of + // whether or not we need immediate or delayed destruction + DelayedDestruction::destroy(); +} + +NetworkSocket AsyncSocket::detachNetworkSocket() { + VLOG(6) << "AsyncSocket::detachFd(this=" << this << ", fd=" << fd_ + << ", evb=" << eventBase_ << ", state=" << state_ + << ", events=" << std::hex << eventFlags_ << ")"; + // Extract the fd, and set fd_ to -1 first, so closeNow() won't + // actually close the descriptor. + if (const auto socketSet = wShutdownSocketSet_.lock()) { + socketSet->remove(fd_); + } + auto fd = fd_; + fd_ = NetworkSocket(); + // Call closeNow() to invoke all pending callbacks with an error. + closeNow(); + // Update the EventHandler to stop using this fd. + // This can only be done after closeNow() unregisters the handler. + ioHandler_.changeHandlerFD(NetworkSocket()); + return fd; +} + +const folly::SocketAddress& AsyncSocket::anyAddress() { + static const folly::SocketAddress anyAddress = + folly::SocketAddress("0.0.0.0", 0); + return anyAddress; +} + +void AsyncSocket::setShutdownSocketSet( + const std::weak_ptr<ShutdownSocketSet>& wNewSS) { + const auto newSS = wNewSS.lock(); + const auto shutdownSocketSet = wShutdownSocketSet_.lock(); + + if (newSS == shutdownSocketSet) { + return; + } + + if (shutdownSocketSet && fd_ != NetworkSocket()) { + shutdownSocketSet->remove(fd_); + } + + if (newSS && fd_ != NetworkSocket()) { + newSS->add(fd_); + } + + wShutdownSocketSet_ = wNewSS; +} + +void AsyncSocket::setCloseOnExec() { + int rv = netops::set_socket_close_on_exec(fd_); + if (rv != 0) { + auto errnoCopy = errno; + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to set close-on-exec flag"), + errnoCopy); + } +} + +void AsyncSocket::connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + int timeout, + const SocketOptionMap& options, + const folly::SocketAddress& bindAddr) noexcept { + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + addr_ = address; + + // Make sure we're in the uninitialized state + if (state_ != StateEnum::UNINIT) { + return invalidState(callback); + } + + connectTimeout_ = std::chrono::milliseconds(timeout); + connectStartTime_ = std::chrono::steady_clock::now(); + // Make connect end time at least >= connectStartTime. + connectEndTime_ = connectStartTime_; + + assert(fd_ == NetworkSocket()); + state_ = StateEnum::CONNECTING; + connectCallback_ = callback; + + sockaddr_storage addrStorage; + auto saddr = reinterpret_cast<sockaddr*>(&addrStorage); + + try { + // Create the socket + // Technically the first parameter should actually be a protocol family + // constant (PF_xxx) rather than an address family (AF_xxx), but the + // distinction is mainly just historical. In pretty much all + // implementations the PF_foo and AF_foo constants are identical. + fd_ = netops::socket(address.getFamily(), SOCK_STREAM, 0); + if (fd_ == NetworkSocket()) { + auto errnoCopy = errno; + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to create socket"), + errnoCopy); + } + disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_); + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->add(fd_); + } + ioHandler_.changeHandlerFD(fd_); + + setCloseOnExec(); + + // Put the socket in non-blocking mode + int rv = netops::set_socket_non_blocking(fd_); + if (rv == -1) { + auto errnoCopy = errno; + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to put socket in non-blocking mode"), + errnoCopy); + } + +#if !defined(MSG_NOSIGNAL) && defined(F_SETNOSIGPIPE) + // iOS and OS X don't support MSG_NOSIGNAL; set F_SETNOSIGPIPE instead + rv = fcntl(fd_.toFd(), F_SETNOSIGPIPE, 1); + if (rv == -1) { + auto errnoCopy = errno; + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + "failed to enable F_SETNOSIGPIPE on socket", + errnoCopy); + } +#endif + + // By default, turn on TCP_NODELAY + // If setNoDelay() fails, we continue anyway; this isn't a fatal error. + // setNoDelay() will log an error message if it fails. + // Also set the cached zeroCopyVal_ since it cannot be set earlier if the fd + // is not created + if (address.getFamily() != AF_UNIX) { + (void)setNoDelay(true); + setZeroCopy(zeroCopyVal_); + } + + // Apply the additional PRE_BIND options if any. + applyOptions(options, SocketOptionKey::ApplyPos::PRE_BIND); + + VLOG(5) << "AsyncSocket::connect(this=" << this << ", evb=" << eventBase_ + << ", fd=" << fd_ << ", host=" << address.describe().c_str(); + + // bind the socket + if (bindAddr != anyAddress()) { + int one = 1; + if (netops::setsockopt( + fd_, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + auto errnoCopy = errno; + doClose(); + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to setsockopt prior to bind on " + bindAddr.describe(), + errnoCopy); + } + + bindAddr.getAddress(&addrStorage); + + if (netops::bind(fd_, saddr, bindAddr.getActualSize()) != 0) { + auto errnoCopy = errno; + doClose(); + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to bind to async socket: " + bindAddr.describe(), + errnoCopy); + } + } + + // Apply the additional POST_BIND options if any. + applyOptions(options, SocketOptionKey::ApplyPos::POST_BIND); + + // Call preConnect hook if any. + if (connectCallback_) { + connectCallback_->preConnect(fd_); + } + + // Perform the connect() + address.getAddress(&addrStorage); + + if (tfoEnabled_) { + state_ = StateEnum::FAST_OPEN; + tfoAttempted_ = true; + } else { + if (socketConnect(saddr, addr_.getActualSize()) < 0) { + return; + } + } + + // If we're still here the connect() succeeded immediately. + // Fall through to call the callback outside of this try...catch block + } catch (const AsyncSocketException& ex) { + return failConnect(__func__, ex); + } catch (const std::exception& ex) { + // shouldn't happen, but handle it just in case + VLOG(4) << "AsyncSocket::connect(this=" << this << ", fd=" << fd_ + << "): unexpected " << typeid(ex).name() + << " exception: " << ex.what(); + AsyncSocketException tex( + AsyncSocketException::INTERNAL_ERROR, + withAddr(string("unexpected exception: ") + ex.what())); + return failConnect(__func__, tex); + } + + // The connection succeeded immediately + // The read callback may not have been set yet, and no writes may be pending + // yet, so we don't have to register for any events at the moment. + VLOG(8) << "AsyncSocket::connect succeeded immediately; this=" << this; + assert(errMessageCallback_ == nullptr); + assert(readCallback_ == nullptr); + assert(writeReqHead_ == nullptr); + if (state_ != StateEnum::FAST_OPEN) { + state_ = StateEnum::ESTABLISHED; + } + invokeConnectSuccess(); +} + +int AsyncSocket::socketConnect(const struct sockaddr* saddr, socklen_t len) { + int rv = netops::connect(fd_, saddr, len); + if (rv < 0) { + auto errnoCopy = errno; + if (errnoCopy == EINPROGRESS) { + scheduleConnectTimeout(); + registerForConnectEvents(); + } else { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "connect failed (immediately)", + errnoCopy); + } + } + return rv; +} + +void AsyncSocket::scheduleConnectTimeout() { + // Connection in progress. + auto timeout = connectTimeout_.count(); + if (timeout > 0) { + // Start a timer in case the connection takes too long. + if (!writeTimeout_.scheduleTimeout(uint32_t(timeout))) { + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to schedule AsyncSocket connect timeout")); + } + } +} + +void AsyncSocket::registerForConnectEvents() { + // Register for write events, so we'll + // be notified when the connection finishes/fails. + // Note that we don't register for a persistent event here. + assert(eventFlags_ == EventHandler::NONE); + eventFlags_ = EventHandler::WRITE; + if (!ioHandler_.registerHandler(eventFlags_)) { + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to register AsyncSocket connect handler")); + } +} + +void AsyncSocket::connect( + ConnectCallback* callback, + const string& ip, + uint16_t port, + int timeout, + const SocketOptionMap& options) noexcept { + DestructorGuard dg(this); + try { + connectCallback_ = callback; + connect(callback, folly::SocketAddress(ip, port), timeout, options); + } catch (const std::exception& ex) { + AsyncSocketException tex(AsyncSocketException::INTERNAL_ERROR, ex.what()); + return failConnect(__func__, tex); + } +} + +void AsyncSocket::cancelConnect() { + connectCallback_ = nullptr; + if (state_ == StateEnum::CONNECTING || state_ == StateEnum::FAST_OPEN) { + closeNow(); + } +} + +void AsyncSocket::setSendTimeout(uint32_t milliseconds) { + sendTimeout_ = milliseconds; + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // If we are currently pending on write requests, immediately update + // writeTimeout_ with the new value. + if ((eventFlags_ & EventHandler::WRITE) && + (state_ != StateEnum::CONNECTING && state_ != StateEnum::FAST_OPEN)) { + assert(state_ == StateEnum::ESTABLISHED); + assert((shutdownFlags_ & SHUT_WRITE) == 0); + if (sendTimeout_ > 0) { + if (!writeTimeout_.scheduleTimeout(sendTimeout_)) { + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to reschedule send timeout in setSendTimeout")); + return failWrite(__func__, ex); + } + } else { + writeTimeout_.cancelTimeout(); + } + } +} + +void AsyncSocket::setErrMessageCB(ErrMessageCallback* callback) { + VLOG(6) << "AsyncSocket::setErrMessageCB() this=" << this << ", fd=" << fd_ + << ", callback=" << callback << ", state=" << state_; + + // In the latest stable kernel 4.14.3 as of 2017-12-04, unix domain + // socket does not support MSG_ERRQUEUE. So recvmsg(MSG_ERRQUEUE) + // will read application data from unix doamin socket as error + // message, which breaks the message flow in application. Feel free + // to remove the next code block if MSG_ERRQUEUE is added for unix + // domain socket in the future. + if (callback != nullptr) { + cacheLocalAddress(); + if (localAddr_.getFamily() == AF_UNIX) { + LOG(ERROR) << "Failed to set ErrMessageCallback=" << callback + << " for Unix Doamin Socket where MSG_ERRQUEUE is unsupported," + << " fd=" << fd_; + return; + } + } + + // Short circuit if callback is the same as the existing errMessageCallback_. + if (callback == errMessageCallback_) { + return; + } + + if (!msgErrQueueSupported) { + // Per-socket error message queue is not supported on this platform. + return invalidState(callback); + } + + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + if (callback == nullptr) { + // We should be able to reset the callback regardless of the + // socket state. It's important to have a reliable callback + // cancellation mechanism. + errMessageCallback_ = callback; + return; + } + + switch ((StateEnum)state_) { + case StateEnum::CONNECTING: + case StateEnum::FAST_OPEN: + case StateEnum::ESTABLISHED: { + errMessageCallback_ = callback; + return; + } + case StateEnum::CLOSED: + case StateEnum::ERROR: + // We should never reach here. SHUT_READ should always be set + // if we are in STATE_CLOSED or STATE_ERROR. + assert(false); + return invalidState(callback); + case StateEnum::UNINIT: + // We do not allow setReadCallback() to be called before we start + // connecting. + return invalidState(callback); + } + + // We don't put a default case in the switch statement, so that the compiler + // will warn us to update the switch statement if a new state is added. + return invalidState(callback); +} + +AsyncSocket::ErrMessageCallback* AsyncSocket::getErrMessageCallback() const { + return errMessageCallback_; +} + +void AsyncSocket::setSendMsgParamCB(SendMsgParamsCallback* callback) { + sendMsgParamCallback_ = callback; +} + +AsyncSocket::SendMsgParamsCallback* AsyncSocket::getSendMsgParamsCB() const { + return sendMsgParamCallback_; +} + +void AsyncSocket::setReadCB(ReadCallback* callback) { + VLOG(6) << "AsyncSocket::setReadCallback() this=" << this << ", fd=" << fd_ + << ", callback=" << callback << ", state=" << state_; + + // Short circuit if callback is the same as the existing readCallback_. + // + // Note that this is needed for proper functioning during some cleanup cases. + // During cleanup we allow setReadCallback(nullptr) to be called even if the + // read callback is already unset and we have been detached from an event + // base. This check prevents us from asserting + // eventBase_->isInEventBaseThread() when eventBase_ is nullptr. + if (callback == readCallback_) { + return; + } + + /* We are removing a read callback */ + if (callback == nullptr && immediateReadHandler_.isLoopCallbackScheduled()) { + immediateReadHandler_.cancelLoopCallback(); + } + + if (shutdownFlags_ & SHUT_READ) { + // Reads have already been shut down on this socket. + // + // Allow setReadCallback(nullptr) to be called in this case, but don't + // allow a new callback to be set. + // + // For example, setReadCallback(nullptr) can happen after an error if we + // invoke some other error callback before invoking readError(). The other + // error callback that is invoked first may go ahead and clear the read + // callback before we get a chance to invoke readError(). + if (callback != nullptr) { + return invalidState(callback); + } + assert((eventFlags_ & EventHandler::READ) == 0); + readCallback_ = nullptr; + return; + } + + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + switch ((StateEnum)state_) { + case StateEnum::CONNECTING: + case StateEnum::FAST_OPEN: + // For convenience, we allow the read callback to be set while we are + // still connecting. We just store the callback for now. Once the + // connection completes we'll register for read events. + readCallback_ = callback; + return; + case StateEnum::ESTABLISHED: { + readCallback_ = callback; + uint16_t oldFlags = eventFlags_; + if (readCallback_) { + eventFlags_ |= EventHandler::READ; + } else { + eventFlags_ &= ~EventHandler::READ; + } + + // Update our registration if our flags have changed + if (eventFlags_ != oldFlags) { + // We intentionally ignore the return value here. + // updateEventRegistration() will move us into the error state if it + // fails, and we don't need to do anything else here afterwards. + (void)updateEventRegistration(); + } + + if (readCallback_) { + checkForImmediateRead(); + } + return; + } + case StateEnum::CLOSED: + case StateEnum::ERROR: + // We should never reach here. SHUT_READ should always be set + // if we are in STATE_CLOSED or STATE_ERROR. + assert(false); + return invalidState(callback); + case StateEnum::UNINIT: + // We do not allow setReadCallback() to be called before we start + // connecting. + return invalidState(callback); + } + + // We don't put a default case in the switch statement, so that the compiler + // will warn us to update the switch statement if a new state is added. + return invalidState(callback); +} + +AsyncSocket::ReadCallback* AsyncSocket::getReadCallback() const { + return readCallback_; +} + +bool AsyncSocket::setZeroCopy(bool enable) { + if (msgErrQueueSupported) { + zeroCopyVal_ = enable; + + if (fd_ == NetworkSocket()) { + return false; + } + + int val = enable ? 1 : 0; + int ret = + netops::setsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)); + + // if enable == false, set zeroCopyEnabled_ = false regardless + // if SO_ZEROCOPY is set or not + if (!enable) { + zeroCopyEnabled_ = enable; + return true; + } + + /* if the setsockopt failed, try to see if the socket inherited the flag + * since we cannot set SO_ZEROCOPY on a socket s = accept + */ + if (ret) { + val = 0; + socklen_t optlen = sizeof(val); + ret = netops::getsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, &optlen); + + if (!ret) { + enable = val != 0; + } + } + + if (!ret) { + zeroCopyEnabled_ = enable; + + return true; + } + } + + return false; +} + +void AsyncSocket::setZeroCopyEnableFunc(AsyncWriter::ZeroCopyEnableFunc func) { + zeroCopyEnableFunc_ = func; +} + +void AsyncSocket::setZeroCopyReenableThreshold(size_t threshold) { + zeroCopyReenableThreshold_ = threshold; +} + +bool AsyncSocket::isZeroCopyRequest(WriteFlags flags) { + return (zeroCopyEnabled_ && isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY)); +} + +void AsyncSocket::adjustZeroCopyFlags(folly::WriteFlags& flags) { + if (!zeroCopyEnabled_) { + // if the zeroCopyReenableCounter_ is > 0 + // we try to dec and if we reach 0 + // we set zeroCopyEnabled_ to true + if (zeroCopyReenableCounter_) { + if (0 == --zeroCopyReenableCounter_) { + zeroCopyEnabled_ = true; + return; + } + } + flags = unSet(flags, folly::WriteFlags::WRITE_MSG_ZEROCOPY); + } +} + +void AsyncSocket::addZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf) { + uint32_t id = getNextZeroCopyBufId(); + folly::IOBuf* ptr = buf.get(); + + idZeroCopyBufPtrMap_[id] = ptr; + auto& p = idZeroCopyBufInfoMap_[ptr]; + p.count_++; + CHECK(p.buf_.get() == nullptr); + p.buf_ = std::move(buf); +} + +void AsyncSocket::addZeroCopyBuf(folly::IOBuf* ptr) { + uint32_t id = getNextZeroCopyBufId(); + idZeroCopyBufPtrMap_[id] = ptr; + + idZeroCopyBufInfoMap_[ptr].count_++; +} + +void AsyncSocket::releaseZeroCopyBuf(uint32_t id) { + auto iter = idZeroCopyBufPtrMap_.find(id); + CHECK(iter != idZeroCopyBufPtrMap_.end()); + auto ptr = iter->second; + auto iter1 = idZeroCopyBufInfoMap_.find(ptr); + CHECK(iter1 != idZeroCopyBufInfoMap_.end()); + if (0 == --iter1->second.count_) { + idZeroCopyBufInfoMap_.erase(iter1); + } + + idZeroCopyBufPtrMap_.erase(iter); +} + +void AsyncSocket::setZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf) { + folly::IOBuf* ptr = buf.get(); + auto& p = idZeroCopyBufInfoMap_[ptr]; + CHECK(p.buf_.get() == nullptr); + + p.buf_ = std::move(buf); +} + +bool AsyncSocket::containsZeroCopyBuf(folly::IOBuf* ptr) { + return (idZeroCopyBufInfoMap_.find(ptr) != idZeroCopyBufInfoMap_.end()); +} + +bool AsyncSocket::isZeroCopyMsg(const cmsghdr& cmsg) const { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + if ((cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) || + (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR)) { + auto serr = + reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg)); + return ( + (serr->ee_errno == 0) && (serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY)); + } +#endif + (void)cmsg; + return false; +} + +void AsyncSocket::processZeroCopyMsg(const cmsghdr& cmsg) { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + auto serr = + reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg)); + uint32_t hi = serr->ee_data; + uint32_t lo = serr->ee_info; + // disable zero copy if the buffer was actually copied + if ((serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED) && zeroCopyEnabled_) { + VLOG(2) << "AsyncSocket::processZeroCopyMsg(): setting " + << "zeroCopyEnabled_ = false due to SO_EE_CODE_ZEROCOPY_COPIED " + << "on " << fd_; + zeroCopyEnabled_ = false; + } + + for (uint32_t i = lo; i <= hi; i++) { + releaseZeroCopyBuf(i); + } +#else + (void)cmsg; +#endif +} + +void AsyncSocket::write( + WriteCallback* callback, + const void* buf, + size_t bytes, + WriteFlags flags) { + iovec op; + op.iov_base = const_cast<void*>(buf); + op.iov_len = bytes; + writeImpl(callback, &op, 1, unique_ptr<IOBuf>(), bytes, flags); +} + +void AsyncSocket::writev( + WriteCallback* callback, + const iovec* vec, + size_t count, + WriteFlags flags) { + size_t totalBytes = 0; + for (size_t i = 0; i < count; ++i) { + totalBytes += vec[i].iov_len; + } + writeImpl(callback, vec, count, unique_ptr<IOBuf>(), totalBytes, flags); +} + +void AsyncSocket::writeChain( + WriteCallback* callback, + unique_ptr<IOBuf>&& buf, + WriteFlags flags) { + adjustZeroCopyFlags(flags); + + // adjustZeroCopyFlags can set zeroCopyEnabled_ to true + if (zeroCopyEnabled_ && !isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY) && + zeroCopyEnableFunc_ && zeroCopyEnableFunc_(buf)) { + flags |= WriteFlags::WRITE_MSG_ZEROCOPY; + } + + constexpr size_t kSmallSizeMax = 64; + size_t count = buf->countChainElements(); + if (count <= kSmallSizeMax) { + // suppress "warning: variable length array 'vec' is used [-Wvla]" + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wvla") + iovec vec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)]; + FOLLY_POP_WARNING + + writeChainImpl(callback, vec, count, std::move(buf), flags); + } else { + std::unique_ptr<iovec[]> vec(new iovec[count]); + writeChainImpl(callback, vec.get(), count, std::move(buf), flags); + } +} + +void AsyncSocket::writeChainImpl( + WriteCallback* callback, + iovec* vec, + size_t count, + unique_ptr<IOBuf>&& buf, + WriteFlags flags) { + auto res = buf->fillIov(vec, count); + writeImpl( + callback, vec, res.numIovecs, std::move(buf), res.totalLength, flags); +} + +void AsyncSocket::writeImpl( + WriteCallback* callback, + const iovec* vec, + size_t count, + unique_ptr<IOBuf>&& buf, + size_t totalBytes, + WriteFlags flags) { + VLOG(6) << "AsyncSocket::writev() this=" << this << ", fd=" << fd_ + << ", callback=" << callback << ", count=" << count + << ", state=" << state_; + DestructorGuard dg(this); + unique_ptr<IOBuf> ioBuf(std::move(buf)); + eventBase_->dcheckIsInEventBaseThread(); + + totalAppBytesScheduledForWrite_ += totalBytes; + + if (shutdownFlags_ & (SHUT_WRITE | SHUT_WRITE_PENDING)) { + // No new writes may be performed after the write side of the socket has + // been shutdown. + // + // We could just call callback->writeError() here to fail just this write. + // However, fail hard and use invalidState() to fail all outstanding + // callbacks and move the socket into the error state. There's most likely + // a bug in the caller's code, so we abort everything rather than trying to + // proceed as best we can. + return invalidState(callback); + } + + uint32_t countWritten = 0; + uint32_t partialWritten = 0; + ssize_t bytesWritten = 0; + bool mustRegister = false; + if ((state_ == StateEnum::ESTABLISHED || state_ == StateEnum::FAST_OPEN) && + !connecting()) { + if (writeReqHead_ == nullptr) { + // If we are established and there are no other writes pending, + // we can attempt to perform the write immediately. + assert(writeReqTail_ == nullptr); + assert((eventFlags_ & EventHandler::WRITE) == 0); + + auto writeResult = performWrite( + vec, uint32_t(count), flags, &countWritten, &partialWritten); + bytesWritten = writeResult.writeReturn; + if (bytesWritten < 0) { + auto errnoCopy = errno; + if (writeResult.exception) { + return failWrite(__func__, callback, 0, *writeResult.exception); + } + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("writev failed"), + errnoCopy); + return failWrite(__func__, callback, 0, ex); + } else if (countWritten == count) { + // done, add the whole buffer + if (countWritten && isZeroCopyRequest(flags)) { + addZeroCopyBuf(std::move(ioBuf)); + } + // We successfully wrote everything. + // Invoke the callback and return. + if (callback) { + callback->writeSuccess(); + } + return; + } else { // continue writing the next writeReq + // add just the ptr + if (bytesWritten && isZeroCopyRequest(flags)) { + addZeroCopyBuf(ioBuf.get()); + } + } + if (!connecting()) { + // Writes might put the socket back into connecting state + // if TFO is enabled, and using TFO fails. + // This means that write timeouts would not be active, however + // connect timeouts would affect this stage. + mustRegister = true; + } + } + } else if (!connecting()) { + // Invalid state for writing + return invalidState(callback); + } + + // Create a new WriteRequest to add to the queue + WriteRequest* req; + try { + req = BytesWriteRequest::newRequest( + this, + callback, + vec + countWritten, + uint32_t(count - countWritten), + partialWritten, + uint32_t(bytesWritten), + std::move(ioBuf), + flags); + } catch (const std::exception& ex) { + // we mainly expect to catch std::bad_alloc here + AsyncSocketException tex( + AsyncSocketException::INTERNAL_ERROR, + withAddr(string("failed to append new WriteRequest: ") + ex.what())); + return failWrite(__func__, callback, size_t(bytesWritten), tex); + } + req->consume(); + if (writeReqTail_ == nullptr) { + assert(writeReqHead_ == nullptr); + writeReqHead_ = writeReqTail_ = req; + } else { + writeReqTail_->append(req); + writeReqTail_ = req; + } + + if (bufferCallback_) { + bufferCallback_->onEgressBuffered(); + } + + // Register for write events if are established and not currently + // waiting on write events + if (mustRegister) { + assert(state_ == StateEnum::ESTABLISHED); + assert((eventFlags_ & EventHandler::WRITE) == 0); + if (!updateEventRegistration(EventHandler::WRITE, 0)) { + assert(state_ == StateEnum::ERROR); + return; + } + if (sendTimeout_ > 0) { + // Schedule a timeout to fire if the write takes too long. + if (!writeTimeout_.scheduleTimeout(sendTimeout_)) { + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to schedule send timeout")); + return failWrite(__func__, ex); + } + } + } +} + +void AsyncSocket::writeRequest(WriteRequest* req) { + if (writeReqTail_ == nullptr) { + assert(writeReqHead_ == nullptr); + writeReqHead_ = writeReqTail_ = req; + req->start(); + } else { + writeReqTail_->append(req); + writeReqTail_ = req; + } +} + +void AsyncSocket::close() { + VLOG(5) << "AsyncSocket::close(): this=" << this << ", fd_=" << fd_ + << ", state=" << state_ << ", shutdownFlags=" << std::hex + << (int)shutdownFlags_; + + // close() is only different from closeNow() when there are pending writes + // that need to drain before we can close. In all other cases, just call + // closeNow(). + // + // Note that writeReqHead_ can be non-nullptr even in STATE_CLOSED or + // STATE_ERROR if close() is invoked while a previous closeNow() or failure + // is still running. (e.g., If there are multiple pending writes, and we + // call writeError() on the first one, it may call close(). In this case we + // will already be in STATE_CLOSED or STATE_ERROR, but the remaining pending + // writes will still be in the queue.) + // + // We only need to drain pending writes if we are still in STATE_CONNECTING + // or STATE_ESTABLISHED + if ((writeReqHead_ == nullptr) || + !(state_ == StateEnum::CONNECTING || state_ == StateEnum::ESTABLISHED)) { + closeNow(); + return; + } + + // Declare a DestructorGuard to ensure that the AsyncSocket cannot be + // destroyed until close() returns. + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + // Since there are write requests pending, we have to set the + // SHUT_WRITE_PENDING flag, and wait to perform the real close until the + // connect finishes and we finish writing these requests. + // + // Set SHUT_READ to indicate that reads are shut down, and set the + // SHUT_WRITE_PENDING flag to mark that we want to shutdown once the + // pending writes complete. + shutdownFlags_ |= (SHUT_READ | SHUT_WRITE_PENDING); + + // If a read callback is set, invoke readEOF() immediately to inform it that + // the socket has been closed and no more data can be read. + if (readCallback_) { + // Disable reads if they are enabled + if (!updateEventRegistration(0, EventHandler::READ)) { + // We're now in the error state; callbacks have been cleaned up + assert(state_ == StateEnum::ERROR); + assert(readCallback_ == nullptr); + } else { + ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readEOF(); + } + } +} + +void AsyncSocket::closeNow() { + VLOG(5) << "AsyncSocket::closeNow(): this=" << this << ", fd_=" << fd_ + << ", state=" << state_ << ", shutdownFlags=" << std::hex + << (int)shutdownFlags_; + DestructorGuard dg(this); + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + switch (state_) { + case StateEnum::ESTABLISHED: + case StateEnum::CONNECTING: + case StateEnum::FAST_OPEN: { + shutdownFlags_ |= (SHUT_READ | SHUT_WRITE); + state_ = StateEnum::CLOSED; + + // If the write timeout was set, cancel it. + writeTimeout_.cancelTimeout(); + + // If we are registered for I/O events, unregister. + if (eventFlags_ != EventHandler::NONE) { + eventFlags_ = EventHandler::NONE; + if (!updateEventRegistration()) { + // We will have been moved into the error state. + assert(state_ == StateEnum::ERROR); + return; + } + } + + if (immediateReadHandler_.isLoopCallbackScheduled()) { + immediateReadHandler_.cancelLoopCallback(); + } + + if (fd_ != NetworkSocket()) { + ioHandler_.changeHandlerFD(NetworkSocket()); + doClose(); + } + + invokeConnectErr(socketClosedLocallyEx); + + failAllWrites(socketClosedLocallyEx); + + if (readCallback_) { + ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readEOF(); + } + return; + } + case StateEnum::CLOSED: + // Do nothing. It's possible that we are being called recursively + // from inside a callback that we invoked inside another call to close() + // that is still running. + return; + case StateEnum::ERROR: + // Do nothing. The error handling code has performed (or is performing) + // cleanup. + return; + case StateEnum::UNINIT: + assert(eventFlags_ == EventHandler::NONE); + assert(connectCallback_ == nullptr); + assert(readCallback_ == nullptr); + assert(writeReqHead_ == nullptr); + shutdownFlags_ |= (SHUT_READ | SHUT_WRITE); + state_ = StateEnum::CLOSED; + return; + } + + LOG(DFATAL) << "AsyncSocket::closeNow() (this=" << this << ", fd=" << fd_ + << ") called in unknown state " << state_; +} + +void AsyncSocket::closeWithReset() { + // Enable SO_LINGER, with the linger timeout set to 0. + // This will trigger a TCP reset when we close the socket. + if (fd_ != NetworkSocket()) { + struct linger optLinger = {1, 0}; + if (setSockOpt(SOL_SOCKET, SO_LINGER, &optLinger) != 0) { + VLOG(2) << "AsyncSocket::closeWithReset(): error setting SO_LINGER " + << "on " << fd_ << ": errno=" << errno; + } + } + + // Then let closeNow() take care of the rest + closeNow(); +} + +void AsyncSocket::shutdownWrite() { + VLOG(5) << "AsyncSocket::shutdownWrite(): this=" << this << ", fd=" << fd_ + << ", state=" << state_ << ", shutdownFlags=" << std::hex + << (int)shutdownFlags_; + + // If there are no pending writes, shutdownWrite() is identical to + // shutdownWriteNow(). + if (writeReqHead_ == nullptr) { + shutdownWriteNow(); + return; + } + + eventBase_->dcheckIsInEventBaseThread(); + + // There are pending writes. Set SHUT_WRITE_PENDING so that the actual + // shutdown will be performed once all writes complete. + shutdownFlags_ |= SHUT_WRITE_PENDING; +} + +void AsyncSocket::shutdownWriteNow() { + VLOG(5) << "AsyncSocket::shutdownWriteNow(): this=" << this << ", fd=" << fd_ + << ", state=" << state_ << ", shutdownFlags=" << std::hex + << (int)shutdownFlags_; + + if (shutdownFlags_ & SHUT_WRITE) { + // Writes are already shutdown; nothing else to do. + return; + } + + // If SHUT_READ is already set, just call closeNow() to completely + // close the socket. This can happen if close() was called with writes + // pending, and then shutdownWriteNow() is called before all pending writes + // complete. + if (shutdownFlags_ & SHUT_READ) { + closeNow(); + return; + } + + DestructorGuard dg(this); + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + switch (static_cast<StateEnum>(state_)) { + case StateEnum::ESTABLISHED: { + shutdownFlags_ |= SHUT_WRITE; + + // If the write timeout was set, cancel it. + writeTimeout_.cancelTimeout(); + + // If we are registered for write events, unregister. + if (!updateEventRegistration(0, EventHandler::WRITE)) { + // We will have been moved into the error state. + assert(state_ == StateEnum::ERROR); + return; + } + + // Shutdown writes on the file descriptor + netops::shutdown(fd_, SHUT_WR); + + // Immediately fail all write requests + failAllWrites(socketShutdownForWritesEx); + return; + } + case StateEnum::CONNECTING: { + // Set the SHUT_WRITE_PENDING flag. + // When the connection completes, it will check this flag, + // shutdown the write half of the socket, and then set SHUT_WRITE. + shutdownFlags_ |= SHUT_WRITE_PENDING; + + // Immediately fail all write requests + failAllWrites(socketShutdownForWritesEx); + return; + } + case StateEnum::UNINIT: + // Callers normally shouldn't call shutdownWriteNow() before the socket + // even starts connecting. Nonetheless, go ahead and set + // SHUT_WRITE_PENDING. Once the socket eventually connects it will + // immediately shut down the write side of the socket. + shutdownFlags_ |= SHUT_WRITE_PENDING; + return; + case StateEnum::FAST_OPEN: + // In fast open state we haven't call connected yet, and if we shutdown + // the writes, we will never try to call connect, so shut everything down + shutdownFlags_ |= SHUT_WRITE; + // Immediately fail all write requests + failAllWrites(socketShutdownForWritesEx); + return; + case StateEnum::CLOSED: + case StateEnum::ERROR: + // We should never get here. SHUT_WRITE should always be set + // in STATE_CLOSED and STATE_ERROR. + VLOG(4) << "AsyncSocket::shutdownWriteNow() (this=" << this + << ", fd=" << fd_ << ") in unexpected state " << state_ + << " with SHUT_WRITE not set (" << std::hex << (int)shutdownFlags_ + << ")"; + assert(false); + return; + } + + LOG(DFATAL) << "AsyncSocket::shutdownWriteNow() (this=" << this + << ", fd=" << fd_ << ") called in unknown state " << state_; +} + +bool AsyncSocket::readable() const { + if (fd_ == NetworkSocket()) { + return false; + } + netops::PollDescriptor fds[1]; + fds[0].fd = fd_; + fds[0].events = POLLIN; + fds[0].revents = 0; + int rc = netops::poll(fds, 1, 0); + return rc == 1; +} + +bool AsyncSocket::writable() const { + if (fd_ == NetworkSocket()) { + return false; + } + netops::PollDescriptor fds[1]; + fds[0].fd = fd_; + fds[0].events = POLLOUT; + fds[0].revents = 0; + int rc = netops::poll(fds, 1, 0); + return rc == 1; +} + +bool AsyncSocket::isPending() const { + return ioHandler_.isPending(); +} + +bool AsyncSocket::hangup() const { + if (fd_ == NetworkSocket()) { + // sanity check, no one should ask for hangup if we are not connected. + assert(false); + return false; + } +#ifdef POLLRDHUP // Linux-only + netops::PollDescriptor fds[1]; + fds[0].fd = fd_; + fds[0].events = POLLRDHUP | POLLHUP; + fds[0].revents = 0; + netops::poll(fds, 1, 0); + return (fds[0].revents & (POLLRDHUP | POLLHUP)) != 0; +#else + return false; +#endif +} + +bool AsyncSocket::good() const { + return ( + (state_ == StateEnum::CONNECTING || state_ == StateEnum::FAST_OPEN || + state_ == StateEnum::ESTABLISHED) && + (shutdownFlags_ == 0) && (eventBase_ != nullptr)); +} + +bool AsyncSocket::error() const { + return (state_ == StateEnum::ERROR); +} + +void AsyncSocket::attachEventBase(EventBase* eventBase) { + VLOG(5) << "AsyncSocket::attachEventBase(this=" << this << ", fd=" << fd_ + << ", old evb=" << eventBase_ << ", new evb=" << eventBase + << ", state=" << state_ << ", events=" << std::hex << eventFlags_ + << ")"; + assert(eventBase_ == nullptr); + eventBase->dcheckIsInEventBaseThread(); + + eventBase_ = eventBase; + ioHandler_.attachEventBase(eventBase); + + updateEventRegistration(); + + writeTimeout_.attachEventBase(eventBase); + if (evbChangeCb_) { + evbChangeCb_->evbAttached(this); + } +} + +void AsyncSocket::detachEventBase() { + VLOG(5) << "AsyncSocket::detachEventBase(this=" << this << ", fd=" << fd_ + << ", old evb=" << eventBase_ << ", state=" << state_ + << ", events=" << std::hex << eventFlags_ << ")"; + assert(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + + eventBase_ = nullptr; + + ioHandler_.unregisterHandler(); + + ioHandler_.detachEventBase(); + writeTimeout_.detachEventBase(); + if (evbChangeCb_) { + evbChangeCb_->evbDetached(this); + } +} + +bool AsyncSocket::isDetachable() const { + DCHECK(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + + return !writeTimeout_.isScheduled(); +} + +void AsyncSocket::cacheAddresses() { + if (fd_ != NetworkSocket()) { + try { + cacheLocalAddress(); + cachePeerAddress(); + } catch (const std::system_error& e) { + if (e.code() != std::error_code(ENOTCONN, std::system_category())) { + VLOG(2) << "Error caching addresses: " << e.code().value() << ", " + << e.code().message(); + } + } + } +} + +void AsyncSocket::cacheLocalAddress() const { + if (!localAddr_.isInitialized()) { + localAddr_.setFromLocalAddress(fd_); + } +} + +void AsyncSocket::cachePeerAddress() const { + if (!addr_.isInitialized()) { + addr_.setFromPeerAddress(fd_); + } +} + +void AsyncSocket::applyOptions( + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos) { + auto result = applySocketOptions(fd_, options, pos); + if (result != 0) { + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to set socket option"), + result); + } +} + +bool AsyncSocket::isZeroCopyWriteInProgress() const noexcept { + eventBase_->dcheckIsInEventBaseThread(); + return (!idZeroCopyBufPtrMap_.empty()); +} + +void AsyncSocket::getLocalAddress(folly::SocketAddress* address) const { + cacheLocalAddress(); + *address = localAddr_; +} + +void AsyncSocket::getPeerAddress(folly::SocketAddress* address) const { + cachePeerAddress(); + *address = addr_; +} + +bool AsyncSocket::getTFOSucceded() const { + return detail::tfo_succeeded(fd_); +} + +int AsyncSocket::setNoDelay(bool noDelay) { + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setNoDelay() called on non-open socket " << this + << "(state=" << state_ << ")"; + return EINVAL; + } + + int value = noDelay ? 1 : 0; + if (netops::setsockopt( + fd_, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to update TCP_NODELAY option on AsyncSocket " << this + << " (fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +} + +int AsyncSocket::setCongestionFlavor(const std::string& cname) { +#ifndef TCP_CONGESTION +#define TCP_CONGESTION 13 +#endif + + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setCongestionFlavor() called on non-open " + << "socket " << this << "(state=" << state_ << ")"; + return EINVAL; + } + + if (netops::setsockopt( + fd_, + IPPROTO_TCP, + TCP_CONGESTION, + cname.c_str(), + socklen_t(cname.length() + 1)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to update TCP_CONGESTION option on AsyncSocket " << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +} + +int AsyncSocket::setQuickAck(bool quickack) { + (void)quickack; + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setQuickAck() called on non-open socket " << this + << "(state=" << state_ << ")"; + return EINVAL; + } + +#ifdef TCP_QUICKACK // Linux-only + int value = quickack ? 1 : 0; + if (netops::setsockopt( + fd_, IPPROTO_TCP, TCP_QUICKACK, &value, sizeof(value)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to update TCP_QUICKACK option on AsyncSocket" << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +#else + return ENOSYS; +#endif +} + +int AsyncSocket::setSendBufSize(size_t bufsize) { + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setSendBufSize() called on non-open socket " + << this << "(state=" << state_ << ")"; + return EINVAL; + } + + if (netops::setsockopt( + fd_, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to update SO_SNDBUF option on AsyncSocket" << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +} + +int AsyncSocket::setRecvBufSize(size_t bufsize) { + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setRecvBufSize() called on non-open socket " + << this << "(state=" << state_ << ")"; + return EINVAL; + } + + if (netops::setsockopt( + fd_, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to update SO_RCVBUF option on AsyncSocket" << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +} + +#if defined(__linux__) +size_t AsyncSocket::getSendBufInUse() const { + if (fd_ == NetworkSocket()) { + std::stringstream issueString; + issueString << "AsyncSocket::getSendBufInUse() called on non-open socket " + << this << "(state=" << state_ << ")"; + VLOG(4) << issueString.str(); + throw std::logic_error(issueString.str()); + } + + size_t returnValue = 0; + if (-1 == ::ioctl(fd_.toFd(), SIOCOUTQ, &returnValue)) { + int errnoCopy = errno; + std::stringstream issueString; + issueString << "Failed to get the tx used bytes on Socket: " << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + VLOG(2) << issueString.str(); + throw std::logic_error(issueString.str()); + } + + return returnValue; +} + +size_t AsyncSocket::getRecvBufInUse() const { + if (fd_ == NetworkSocket()) { + std::stringstream issueString; + issueString << "AsyncSocket::getRecvBufInUse() called on non-open socket " + << this << "(state=" << state_ << ")"; + VLOG(4) << issueString.str(); + throw std::logic_error(issueString.str()); + } + + size_t returnValue = 0; + if (-1 == ::ioctl(fd_.toFd(), SIOCINQ, &returnValue)) { + std::stringstream issueString; + int errnoCopy = errno; + issueString << "Failed to get the rx used bytes on Socket: " << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + VLOG(2) << issueString.str(); + throw std::logic_error(issueString.str()); + } + + return returnValue; +} +#endif + +int AsyncSocket::setTCPProfile(int profd) { + if (fd_ == NetworkSocket()) { + VLOG(4) << "AsyncSocket::setTCPProfile() called on non-open socket " << this + << "(state=" << state_ << ")"; + return EINVAL; + } + + if (netops::setsockopt( + fd_, SOL_SOCKET, SO_SET_NAMESPACE, &profd, sizeof(int)) != 0) { + int errnoCopy = errno; + VLOG(2) << "failed to set socket namespace option on AsyncSocket" << this + << "(fd=" << fd_ << ", state=" << state_ + << "): " << errnoStr(errnoCopy); + return errnoCopy; + } + + return 0; +} + +void AsyncSocket::ioReady(uint16_t events) noexcept { + VLOG(7) << "AsyncSocket::ioRead() this=" << this << ", fd=" << fd_ + << ", events=" << std::hex << events << ", state=" << state_; + DestructorGuard dg(this); + assert(events & EventHandler::READ_WRITE); + eventBase_->dcheckIsInEventBaseThread(); + + auto relevantEvents = uint16_t(events & EventHandler::READ_WRITE); + EventBase* originalEventBase = eventBase_; + // If we got there it means that either EventHandler::READ or + // EventHandler::WRITE is set. Any of these flags can + // indicate that there are messages available in the socket + // error message queue. + // Return if we handle any error messages - this is to avoid + // unnecessary read/write calls + if (handleErrMessages()) { + return; + } + + // Return now if handleErrMessages() detached us from our EventBase + if (eventBase_ != originalEventBase) { + return; + } + + if (relevantEvents == EventHandler::READ) { + handleRead(); + } else if (relevantEvents == EventHandler::WRITE) { + handleWrite(); + } else if (relevantEvents == EventHandler::READ_WRITE) { + // If both read and write events are ready, process writes first. + handleWrite(); + + // Return now if handleWrite() detached us from our EventBase + if (eventBase_ != originalEventBase) { + return; + } + + // Only call handleRead() if a read callback is still installed. + // (It's possible that the read callback was uninstalled during + // handleWrite().) + if (readCallback_) { + handleRead(); + } + } else { + VLOG(4) << "AsyncSocket::ioRead() called with unexpected events " + << std::hex << events << "(this=" << this << ")"; + abort(); + } +} + +AsyncSocket::ReadResult +AsyncSocket::performRead(void** buf, size_t* buflen, size_t* /* offset */) { + VLOG(5) << "AsyncSocket::performRead() this=" << this << ", buf=" << *buf + << ", buflen=" << *buflen; + + if (preReceivedData_ && !preReceivedData_->empty()) { + VLOG(5) << "AsyncSocket::performRead() this=" << this + << ", reading pre-received data"; + + io::Cursor cursor(preReceivedData_.get()); + auto len = cursor.pullAtMost(*buf, *buflen); + + IOBufQueue queue; + queue.append(std::move(preReceivedData_)); + queue.trimStart(len); + preReceivedData_ = queue.move(); + + appBytesReceived_ += len; + return ReadResult(len); + } + + ssize_t bytes = netops::recv(fd_, *buf, *buflen, MSG_DONTWAIT); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No more data to read right now. + return ReadResult(READ_BLOCKING); + } else { + return ReadResult(READ_ERROR); + } + } else { + appBytesReceived_ += bytes; + return ReadResult(bytes); + } +} + +void AsyncSocket::prepareReadBuffer(void** buf, size_t* buflen) { + // no matter what, buffer should be preapared for non-ssl socket + CHECK(readCallback_); + readCallback_->getReadBuffer(buf, buflen); +} + +size_t AsyncSocket::handleErrMessages() noexcept { + // This method has non-empty implementation only for platforms + // supporting per-socket error queues. + VLOG(5) << "AsyncSocket::handleErrMessages() this=" << this << ", fd=" << fd_ + << ", state=" << state_; + if (errMessageCallback_ == nullptr && idZeroCopyBufPtrMap_.empty()) { + VLOG(7) << "AsyncSocket::handleErrMessages(): " + << "no callback installed - exiting."; + return 0; + } + +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + uint8_t ctrl[1024]; + unsigned char data; + struct msghdr msg; + iovec entry; + + entry.iov_base = &data; + entry.iov_len = sizeof(data); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + msg.msg_flags = 0; + + int ret; + size_t num = 0; + // the socket may be closed by errMessage callback, so check on each iteration + while (fd_ != NetworkSocket()) { + ret = netops::recvmsg(fd_, &msg, MSG_ERRQUEUE); + VLOG(5) << "AsyncSocket::handleErrMessages(): recvmsg returned " << ret; + + if (ret < 0) { + if (errno != EAGAIN) { + auto errnoCopy = errno; + LOG(ERROR) << "::recvmsg exited with code " << ret + << ", errno: " << errnoCopy << ", fd: " << fd_; + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("recvmsg() failed"), + errnoCopy); + failErrMessageRead(__func__, ex); + } + + return num; + } + + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg != nullptr && cmsg->cmsg_len != 0; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + ++num; + if (isZeroCopyMsg(*cmsg)) { + processZeroCopyMsg(*cmsg); + } else { + if (errMessageCallback_) { + errMessageCallback_->errMessage(*cmsg); + } + } + } + } + return num; +#else + return 0; +#endif // FOLLY_HAVE_MSG_ERRQUEUE +} + +bool AsyncSocket::processZeroCopyWriteInProgress() noexcept { + eventBase_->dcheckIsInEventBaseThread(); + if (idZeroCopyBufPtrMap_.empty()) { + return true; + } + + handleErrMessages(); + + return idZeroCopyBufPtrMap_.empty(); +} + +void AsyncSocket::handleRead() noexcept { + VLOG(5) << "AsyncSocket::handleRead() this=" << this << ", fd=" << fd_ + << ", state=" << state_; + assert(state_ == StateEnum::ESTABLISHED); + assert((shutdownFlags_ & SHUT_READ) == 0); + assert(readCallback_ != nullptr); + assert(eventFlags_ & EventHandler::READ); + + // Loop until: + // - a read attempt would block + // - readCallback_ is uninstalled + // - the number of loop iterations exceeds the optional maximum + // - this AsyncSocket is moved to another EventBase + // + // When we invoke readDataAvailable() it may uninstall the readCallback_, + // which is why need to check for it here. + // + // The last bullet point is slightly subtle. readDataAvailable() may also + // detach this socket from this EventBase. However, before + // readDataAvailable() returns another thread may pick it up, attach it to + // a different EventBase, and install another readCallback_. We need to + // exit immediately after readDataAvailable() returns if the eventBase_ has + // changed. (The caller must perform some sort of locking to transfer the + // AsyncSocket between threads properly. This will be sufficient to ensure + // that this thread sees the updated eventBase_ variable after + // readDataAvailable() returns.) + uint16_t numReads = 0; + EventBase* originalEventBase = eventBase_; + while (readCallback_ && eventBase_ == originalEventBase) { + // Get the buffer to read into. + void* buf = nullptr; + size_t buflen = 0, offset = 0; + try { + prepareReadBuffer(&buf, &buflen); + VLOG(5) << "prepareReadBuffer() buf=" << buf << ", buflen=" << buflen; + } catch (const AsyncSocketException& ex) { + return failRead(__func__, ex); + } catch (const std::exception& ex) { + AsyncSocketException tex( + AsyncSocketException::BAD_ARGS, + string("ReadCallback::getReadBuffer() " + "threw exception: ") + + ex.what()); + return failRead(__func__, tex); + } catch (...) { + AsyncSocketException ex( + AsyncSocketException::BAD_ARGS, + "ReadCallback::getReadBuffer() threw " + "non-exception type"); + return failRead(__func__, ex); + } + if (buf == nullptr || buflen == 0) { + AsyncSocketException ex( + AsyncSocketException::BAD_ARGS, + "ReadCallback::getReadBuffer() returned " + "empty buffer"); + return failRead(__func__, ex); + } + + // Perform the read + auto readResult = performRead(&buf, &buflen, &offset); + auto bytesRead = readResult.readReturn; + VLOG(4) << "this=" << this << ", AsyncSocket::handleRead() got " + << bytesRead << " bytes"; + if (bytesRead > 0) { + readCallback_->readDataAvailable(size_t(bytesRead)); + + // Fall through and continue around the loop if the read + // completely filled the available buffer. + // Note that readCallback_ may have been uninstalled or changed inside + // readDataAvailable(). + if (size_t(bytesRead) < buflen) { + return; + } + } else if (bytesRead == READ_BLOCKING) { + // No more data to read right now. + return; + } else if (bytesRead == READ_ERROR) { + readErr_ = READ_ERROR; + if (readResult.exception) { + return failRead(__func__, *readResult.exception); + } + auto errnoCopy = errno; + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("recv() failed"), + errnoCopy); + return failRead(__func__, ex); + } else { + assert(bytesRead == READ_EOF); + readErr_ = READ_EOF; + // EOF + shutdownFlags_ |= SHUT_READ; + if (!updateEventRegistration(0, EventHandler::READ)) { + // we've already been moved into STATE_ERROR + assert(state_ == StateEnum::ERROR); + assert(readCallback_ == nullptr); + return; + } + + ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readEOF(); + return; + } + if (maxReadsPerEvent_ && (++numReads >= maxReadsPerEvent_)) { + if (readCallback_ != nullptr) { + // We might still have data in the socket. + // (e.g. see comment in AsyncSSLSocket::checkForImmediateRead) + scheduleImmediateRead(); + } + return; + } + } +} + +/** + * This function attempts to write as much data as possible, until no more data + * can be written. + * + * - If it sends all available data, it unregisters for write events, and stops + * the writeTimeout_. + * + * - If not all of the data can be sent immediately, it reschedules + * writeTimeout_ (if a non-zero timeout is set), and ensures the handler is + * registered for write events. + */ +void AsyncSocket::handleWrite() noexcept { + VLOG(5) << "AsyncSocket::handleWrite() this=" << this << ", fd=" << fd_ + << ", state=" << state_; + DestructorGuard dg(this); + + if (state_ == StateEnum::CONNECTING) { + handleConnect(); + return; + } + + // Normal write + assert(state_ == StateEnum::ESTABLISHED); + assert((shutdownFlags_ & SHUT_WRITE) == 0); + assert(writeReqHead_ != nullptr); + + // Loop until we run out of write requests, + // or until this socket is moved to another EventBase. + // (See the comment in handleRead() explaining how this can happen.) + EventBase* originalEventBase = eventBase_; + while (writeReqHead_ != nullptr && eventBase_ == originalEventBase) { + auto writeResult = writeReqHead_->performWrite(); + if (writeResult.writeReturn < 0) { + if (writeResult.exception) { + return failWrite(__func__, *writeResult.exception); + } + auto errnoCopy = errno; + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("writev() failed"), + errnoCopy); + return failWrite(__func__, ex); + } else if (writeReqHead_->isComplete()) { + // We finished this request + WriteRequest* req = writeReqHead_; + writeReqHead_ = req->getNext(); + + if (writeReqHead_ == nullptr) { + writeReqTail_ = nullptr; + // This is the last write request. + // Unregister for write events and cancel the send timer + // before we invoke the callback. We have to update the state properly + // before calling the callback, since it may want to detach us from + // the EventBase. + if (eventFlags_ & EventHandler::WRITE) { + if (!updateEventRegistration(0, EventHandler::WRITE)) { + assert(state_ == StateEnum::ERROR); + return; + } + // Stop the send timeout + writeTimeout_.cancelTimeout(); + } + assert(!writeTimeout_.isScheduled()); + + // If SHUT_WRITE_PENDING is set, we should shutdown the socket after + // we finish sending the last write request. + // + // We have to do this before invoking writeSuccess(), since + // writeSuccess() may detach us from our EventBase. + if (shutdownFlags_ & SHUT_WRITE_PENDING) { + assert(connectCallback_ == nullptr); + shutdownFlags_ |= SHUT_WRITE; + + if (shutdownFlags_ & SHUT_READ) { + // Reads have already been shutdown. Fully close the socket and + // move to STATE_CLOSED. + // + // Note: This code currently moves us to STATE_CLOSED even if + // close() hasn't ever been called. This can occur if we have + // received EOF from the peer and shutdownWrite() has been called + // locally. Should we bother staying in STATE_ESTABLISHED in this + // case, until close() is actually called? I can't think of a + // reason why we would need to do so. No other operations besides + // calling close() or destroying the socket can be performed at + // this point. + assert(readCallback_ == nullptr); + state_ = StateEnum::CLOSED; + if (fd_ != NetworkSocket()) { + ioHandler_.changeHandlerFD(NetworkSocket()); + doClose(); + } + } else { + // Reads are still enabled, so we are only doing a half-shutdown + netops::shutdown(fd_, SHUT_WR); + } + } + } + + // Invoke the callback + WriteCallback* callback = req->getCallback(); + req->destroy(); + if (callback) { + callback->writeSuccess(); + } + // We'll continue around the loop, trying to write another request + } else { + // Partial write. + writeReqHead_->consume(); + if (bufferCallback_) { + bufferCallback_->onEgressBuffered(); + } + // Stop after a partial write; it's highly likely that a subsequent write + // attempt will just return EAGAIN. + // + // Ensure that we are registered for write events. + if ((eventFlags_ & EventHandler::WRITE) == 0) { + if (!updateEventRegistration(EventHandler::WRITE, 0)) { + assert(state_ == StateEnum::ERROR); + return; + } + } + + // Reschedule the send timeout, since we have made some write progress. + if (sendTimeout_ > 0) { + if (!writeTimeout_.scheduleTimeout(sendTimeout_)) { + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to reschedule write timeout")); + return failWrite(__func__, ex); + } + } + return; + } + } + if (!writeReqHead_ && bufferCallback_) { + bufferCallback_->onEgressBufferCleared(); + } +} + +void AsyncSocket::checkForImmediateRead() noexcept { + // We currently don't attempt to perform optimistic reads in AsyncSocket. + // (However, note that some subclasses do override this method.) + // + // Simply calling handleRead() here would be bad, as this would call + // readCallback_->getReadBuffer(), forcing the callback to allocate a read + // buffer even though no data may be available. This would waste lots of + // memory, since the buffer will sit around unused until the socket actually + // becomes readable. + // + // Checking if the socket is readable now also seems like it would probably + // be a pessimism. In most cases it probably wouldn't be readable, and we + // would just waste an extra system call. Even if it is readable, waiting to + // find out from libevent on the next event loop doesn't seem that bad. + // + // The exception to this is if we have pre-received data. In that case there + // is definitely data available immediately. + if (preReceivedData_ && !preReceivedData_->empty()) { + handleRead(); + } +} + +void AsyncSocket::handleInitialReadWrite() noexcept { + // Our callers should already be holding a DestructorGuard, but grab + // one here just to make sure, in case one of our calling code paths ever + // changes. + DestructorGuard dg(this); + // If we have a readCallback_, make sure we enable read events. We + // may already be registered for reads if connectSuccess() set + // the read calback. + if (readCallback_ && !(eventFlags_ & EventHandler::READ)) { + assert(state_ == StateEnum::ESTABLISHED); + assert((shutdownFlags_ & SHUT_READ) == 0); + if (!updateEventRegistration(EventHandler::READ, 0)) { + assert(state_ == StateEnum::ERROR); + return; + } + checkForImmediateRead(); + } else if (readCallback_ == nullptr) { + // Unregister for read events. + updateEventRegistration(0, EventHandler::READ); + } + + // If we have write requests pending, try to send them immediately. + // Since we just finished accepting, there is a very good chance that we can + // write without blocking. + // + // However, we only process them if EventHandler::WRITE is not already set, + // which means that we're already blocked on a write attempt. (This can + // happen if connectSuccess() called write() before returning.) + if (writeReqHead_ && !(eventFlags_ & EventHandler::WRITE)) { + // Call handleWrite() to perform write processing. + handleWrite(); + } else if (writeReqHead_ == nullptr) { + // Unregister for write event. + updateEventRegistration(0, EventHandler::WRITE); + } +} + +void AsyncSocket::handleConnect() noexcept { + VLOG(5) << "AsyncSocket::handleConnect() this=" << this << ", fd=" << fd_ + << ", state=" << state_; + assert(state_ == StateEnum::CONNECTING); + // SHUT_WRITE can never be set while we are still connecting; + // SHUT_WRITE_PENDING may be set, be we only set SHUT_WRITE once the connect + // finishes + assert((shutdownFlags_ & SHUT_WRITE) == 0); + + // In case we had a connect timeout, cancel the timeout + writeTimeout_.cancelTimeout(); + // We don't use a persistent registration when waiting on a connect event, + // so we have been automatically unregistered now. Update eventFlags_ to + // reflect reality. + assert(eventFlags_ == EventHandler::WRITE); + eventFlags_ = EventHandler::NONE; + + // Call getsockopt() to check if the connect succeeded + int error; + socklen_t len = sizeof(error); + int rv = netops::getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &len); + if (rv != 0) { + auto errnoCopy = errno; + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("error calling getsockopt() after connect"), + errnoCopy); + VLOG(4) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_ + << " host=" << addr_.describe() << ") exception:" << ex.what(); + return failConnect(__func__, ex); + } + + if (error != 0) { + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, "connect failed", error); + VLOG(2) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_ + << " host=" << addr_.describe() << ") exception: " << ex.what(); + return failConnect(__func__, ex); + } + + // Move into STATE_ESTABLISHED + state_ = StateEnum::ESTABLISHED; + + // If SHUT_WRITE_PENDING is set and we don't have any write requests to + // perform, immediately shutdown the write half of the socket. + if ((shutdownFlags_ & SHUT_WRITE_PENDING) && writeReqHead_ == nullptr) { + // SHUT_READ shouldn't be set. If close() is called on the socket while we + // are still connecting we just abort the connect rather than waiting for + // it to complete. + assert((shutdownFlags_ & SHUT_READ) == 0); + netops::shutdown(fd_, SHUT_WR); + shutdownFlags_ |= SHUT_WRITE; + } + + VLOG(7) << "AsyncSocket " << this << ": fd " << fd_ + << "successfully connected; state=" << state_; + + // Remember the EventBase we are attached to, before we start invoking any + // callbacks (since the callbacks may call detachEventBase()). + EventBase* originalEventBase = eventBase_; + + invokeConnectSuccess(); + // Note that the connect callback may have changed our state. + // (set or unset the read callback, called write(), closed the socket, etc.) + // The following code needs to handle these situations correctly. + // + // If the socket has been closed, readCallback_ and writeReqHead_ will + // always be nullptr, so that will prevent us from trying to read or write. + // + // The main thing to check for is if eventBase_ is still originalEventBase. + // If not, we have been detached from this event base, so we shouldn't + // perform any more operations. + if (eventBase_ != originalEventBase) { + return; + } + + handleInitialReadWrite(); +} + +void AsyncSocket::timeoutExpired() noexcept { + VLOG(7) << "AsyncSocket " << this << ", fd " << fd_ << ": timeout expired: " + << "state=" << state_ << ", events=" << std::hex << eventFlags_; + DestructorGuard dg(this); + eventBase_->dcheckIsInEventBaseThread(); + + if (state_ == StateEnum::CONNECTING) { + // connect() timed out + // Unregister for I/O events. + if (connectCallback_) { + AsyncSocketException ex( + AsyncSocketException::TIMED_OUT, + folly::sformat( + "connect timed out after {}ms", connectTimeout_.count())); + failConnect(__func__, ex); + } else { + // we faced a connect error without a connect callback, which could + // happen due to TFO. + AsyncSocketException ex( + AsyncSocketException::TIMED_OUT, "write timed out during connection"); + failWrite(__func__, ex); + } + } else { + // a normal write operation timed out + AsyncSocketException ex( + AsyncSocketException::TIMED_OUT, + folly::sformat("write timed out after {}ms", sendTimeout_)); + failWrite(__func__, ex); + } +} + +ssize_t +AsyncSocket::tfoSendMsg(NetworkSocket fd, struct msghdr* msg, int msg_flags) { + return detail::tfo_sendmsg(fd, msg, msg_flags); +} + +AsyncSocket::WriteResult AsyncSocket::sendSocketMessage( + NetworkSocket fd, + struct msghdr* msg, + int msg_flags) { + ssize_t totalWritten = 0; + if (state_ == StateEnum::FAST_OPEN) { + sockaddr_storage addr; + auto len = addr_.getAddress(&addr); + msg->msg_name = &addr; + msg->msg_namelen = len; + totalWritten = tfoSendMsg(fd_, msg, msg_flags); + if (totalWritten >= 0) { + tfoFinished_ = true; + state_ = StateEnum::ESTABLISHED; + // We schedule this asynchrously so that we don't end up + // invoking initial read or write while a write is in progress. + scheduleInitialReadWrite(); + } else if (errno == EINPROGRESS) { + VLOG(4) << "TFO falling back to connecting"; + // A normal sendmsg doesn't return EINPROGRESS, however + // TFO might fallback to connecting if there is no + // cookie. + state_ = StateEnum::CONNECTING; + try { + scheduleConnectTimeout(); + registerForConnectEvents(); + } catch (const AsyncSocketException& ex) { + return WriteResult( + WRITE_ERROR, std::make_unique<AsyncSocketException>(ex)); + } + // Let's fake it that no bytes were written and return an errno. + errno = EAGAIN; + totalWritten = -1; + } else if (errno == EOPNOTSUPP) { + // Try falling back to connecting. + VLOG(4) << "TFO not supported"; + state_ = StateEnum::CONNECTING; + try { + int ret = socketConnect((const sockaddr*)&addr, len); + if (ret == 0) { + // connect succeeded immediately + // Treat this like no data was written. + state_ = StateEnum::ESTABLISHED; + scheduleInitialReadWrite(); + } + // If there was no exception during connections, + // we would return that no bytes were written. + errno = EAGAIN; + totalWritten = -1; + } catch (const AsyncSocketException& ex) { + return WriteResult( + WRITE_ERROR, std::make_unique<AsyncSocketException>(ex)); + } + } else if (errno == EAGAIN) { + // Normally sendmsg would indicate that the write would block. + // However in the fast open case, it would indicate that sendmsg + // fell back to a connect. This is a return code from connect() + // instead, and is an error condition indicating no fds available. + return WriteResult( + WRITE_ERROR, + std::make_unique<AsyncSocketException>( + AsyncSocketException::UNKNOWN, "No more free local ports")); + } + } else { + totalWritten = netops::sendmsg(fd, msg, msg_flags); + } + return WriteResult(totalWritten); +} + +AsyncSocket::WriteResult AsyncSocket::performWrite( + const iovec* vec, + uint32_t count, + WriteFlags flags, + uint32_t* countWritten, + uint32_t* partialWritten) { + // We use sendmsg() instead of writev() so that we can pass in MSG_NOSIGNAL + // We correctly handle EPIPE errors, so we never want to receive SIGPIPE + // (since it may terminate the program if the main program doesn't explicitly + // ignore it). + struct msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = const_cast<iovec*>(vec); + msg.msg_iovlen = std::min<size_t>(count, kIovMax); + msg.msg_flags = 0; + msg.msg_controllen = sendMsgParamCallback_->getAncillaryDataSize(flags); + CHECK_GE( + AsyncSocket::SendMsgParamsCallback::maxAncillaryDataSize, + msg.msg_controllen); + + if (msg.msg_controllen != 0) { + msg.msg_control = reinterpret_cast<char*>(alloca(msg.msg_controllen)); + sendMsgParamCallback_->getAncillaryData(flags, msg.msg_control); + } else { + msg.msg_control = nullptr; + } + int msg_flags = sendMsgParamCallback_->getFlags(flags, zeroCopyEnabled_); + + auto writeResult = sendSocketMessage(fd_, &msg, msg_flags); + auto totalWritten = writeResult.writeReturn; + if (totalWritten < 0 && zeroCopyEnabled_ && errno == ENOBUFS) { + // workaround for running with zerocopy enabled but without a big enough + // memlock value - see ulimit -l + zeroCopyEnabled_ = false; + zeroCopyReenableCounter_ = zeroCopyReenableThreshold_; + msg_flags = sendMsgParamCallback_->getFlags(flags, zeroCopyEnabled_); + writeResult = sendSocketMessage(fd_, &msg, msg_flags); + totalWritten = writeResult.writeReturn; + } + if (totalWritten < 0) { + bool tryAgain = (errno == EAGAIN); +#ifdef __APPLE__ + // Apple has a bug where doing a second write on a socket which we + // have opened with TFO causes an ENOTCONN to be thrown. However the + // socket is really connected, so treat ENOTCONN as a EAGAIN until + // this bug is fixed. + tryAgain |= (errno == ENOTCONN); +#endif + + if (!writeResult.exception && tryAgain) { + // TCP buffer is full; we can't write any more data right now. + *countWritten = 0; + *partialWritten = 0; + return WriteResult(0); + } + // error + *countWritten = 0; + *partialWritten = 0; + return writeResult; + } + + appBytesWritten_ += totalWritten; + + uint32_t bytesWritten; + uint32_t n; + for (bytesWritten = uint32_t(totalWritten), n = 0; n < count; ++n) { + const iovec* v = vec + n; + if (v->iov_len > bytesWritten) { + // Partial write finished in the middle of this iovec + *countWritten = n; + *partialWritten = bytesWritten; + return WriteResult(totalWritten); + } + + bytesWritten -= uint32_t(v->iov_len); + } + + assert(bytesWritten == 0); + *countWritten = n; + *partialWritten = 0; + return WriteResult(totalWritten); +} + +/** + * Re-register the EventHandler after eventFlags_ has changed. + * + * If an error occurs, fail() is called to move the socket into the error state + * and call all currently installed callbacks. After an error, the + * AsyncSocket is completely unregistered. + * + * @return Returns true on success, or false on error. + */ +bool AsyncSocket::updateEventRegistration() { + VLOG(5) << "AsyncSocket::updateEventRegistration(this=" << this + << ", fd=" << fd_ << ", evb=" << eventBase_ << ", state=" << state_ + << ", events=" << std::hex << eventFlags_; + if (eventFlags_ == EventHandler::NONE) { + if (ioHandler_.isHandlerRegistered()) { + DCHECK(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + } + ioHandler_.unregisterHandler(); + return true; + } + + eventBase_->dcheckIsInEventBaseThread(); + + // Always register for persistent events, so we don't have to re-register + // after being called back. + if (!ioHandler_.registerHandler( + uint16_t(eventFlags_ | EventHandler::PERSIST))) { + eventFlags_ = EventHandler::NONE; // we're not registered after error + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("failed to update AsyncSocket event registration")); + fail("updateEventRegistration", ex); + return false; + } + + return true; +} + +bool AsyncSocket::updateEventRegistration(uint16_t enable, uint16_t disable) { + uint16_t oldFlags = eventFlags_; + eventFlags_ |= enable; + eventFlags_ &= ~disable; + if (eventFlags_ == oldFlags) { + return true; + } else { + return updateEventRegistration(); + } +} + +void AsyncSocket::startFail() { + // startFail() should only be called once + assert(state_ != StateEnum::ERROR); + assert(getDestructorGuardCount() > 0); + state_ = StateEnum::ERROR; + // Ensure that SHUT_READ and SHUT_WRITE are set, + // so all future attempts to read or write will be rejected + shutdownFlags_ |= (SHUT_READ | SHUT_WRITE); + + // Cancel any scheduled immediate read. + if (immediateReadHandler_.isLoopCallbackScheduled()) { + immediateReadHandler_.cancelLoopCallback(); + } + + if (eventFlags_ != EventHandler::NONE) { + eventFlags_ = EventHandler::NONE; + ioHandler_.unregisterHandler(); + } + writeTimeout_.cancelTimeout(); + + if (fd_ != NetworkSocket()) { + ioHandler_.changeHandlerFD(NetworkSocket()); + doClose(); + } +} + +void AsyncSocket::invokeAllErrors(const AsyncSocketException& ex) { + invokeConnectErr(ex); + failAllWrites(ex); + + if (readCallback_) { + ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readErr(ex); + } +} + +void AsyncSocket::finishFail() { + assert(state_ == StateEnum::ERROR); + assert(getDestructorGuardCount() > 0); + + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + withAddr("socket closing after error")); + invokeAllErrors(ex); +} + +void AsyncSocket::finishFail(const AsyncSocketException& ex) { + assert(state_ == StateEnum::ERROR); + assert(getDestructorGuardCount() > 0); + invokeAllErrors(ex); +} + +void AsyncSocket::fail(const char* fn, const AsyncSocketException& ex) { + VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed in " << fn << "(): " << ex.what(); + startFail(); + finishFail(); +} + +void AsyncSocket::failConnect(const char* fn, const AsyncSocketException& ex) { + VLOG(5) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed while connecting in " << fn << "(): " << ex.what(); + startFail(); + + invokeConnectErr(ex); + finishFail(ex); +} + +void AsyncSocket::failRead(const char* fn, const AsyncSocketException& ex) { + VLOG(5) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed while reading in " << fn << "(): " << ex.what(); + startFail(); + + if (readCallback_ != nullptr) { + ReadCallback* callback = readCallback_; + readCallback_ = nullptr; + callback->readErr(ex); + } + + finishFail(); +} + +void AsyncSocket::failErrMessageRead( + const char* fn, + const AsyncSocketException& ex) { + VLOG(5) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed while reading message in " << fn << "(): " << ex.what(); + startFail(); + + if (errMessageCallback_ != nullptr) { + ErrMessageCallback* callback = errMessageCallback_; + errMessageCallback_ = nullptr; + callback->errMessageError(ex); + } + + finishFail(); +} + +void AsyncSocket::failWrite(const char* fn, const AsyncSocketException& ex) { + VLOG(5) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed while writing in " << fn << "(): " << ex.what(); + startFail(); + + // Only invoke the first write callback, since the error occurred while + // writing this request. Let any other pending write callbacks be invoked in + // finishFail(). + if (writeReqHead_ != nullptr) { + WriteRequest* req = writeReqHead_; + writeReqHead_ = req->getNext(); + WriteCallback* callback = req->getCallback(); + uint32_t bytesWritten = req->getTotalBytesWritten(); + req->destroy(); + if (callback) { + callback->writeErr(bytesWritten, ex); + } + } + + finishFail(); +} + +void AsyncSocket::failWrite( + const char* fn, + WriteCallback* callback, + size_t bytesWritten, + const AsyncSocketException& ex) { + // This version of failWrite() is used when the failure occurs before + // we've added the callback to writeReqHead_. + VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << ", state=" << state_ << " host=" << addr_.describe() + << "): failed while writing in " << fn << "(): " << ex.what(); + if (closeOnFailedWrite_) { + startFail(); + } + + if (callback != nullptr) { + callback->writeErr(bytesWritten, ex); + } + + if (closeOnFailedWrite_) { + finishFail(); + } +} + +void AsyncSocket::failAllWrites(const AsyncSocketException& ex) { + // Invoke writeError() on all write callbacks. + // This is used when writes are forcibly shutdown with write requests + // pending, or when an error occurs with writes pending. + while (writeReqHead_ != nullptr) { + WriteRequest* req = writeReqHead_; + writeReqHead_ = req->getNext(); + WriteCallback* callback = req->getCallback(); + if (callback) { + callback->writeErr(req->getTotalBytesWritten(), ex); + } + req->destroy(); + } + + // All pending writes have failed - reset totalAppBytesScheduledForWrite_ + totalAppBytesScheduledForWrite_ = appBytesWritten_; +} + +void AsyncSocket::invalidState(ConnectCallback* callback) { + VLOG(5) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << "): connect() called in invalid state " << state_; + + /* + * The invalidState() methods don't use the normal failure mechanisms, + * since we don't know what state we are in. We don't want to call + * startFail()/finishFail() recursively if we are already in the middle of + * cleaning up. + */ + + AsyncSocketException ex( + AsyncSocketException::ALREADY_OPEN, + "connect() called with socket in invalid state"); + connectEndTime_ = std::chrono::steady_clock::now(); + if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) { + if (callback) { + callback->connectErr(ex); + } + } else { + // We can't use failConnect() here since connectCallback_ + // may already be set to another callback. Invoke this ConnectCallback + // here; any other connectCallback_ will be invoked in finishFail() + startFail(); + if (callback) { + callback->connectErr(ex); + } + finishFail(); + } +} + +void AsyncSocket::invalidState(ErrMessageCallback* callback) { + VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << "): setErrMessageCB(" << callback << ") called in invalid state " + << state_; + + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, + msgErrQueueSupported + ? "setErrMessageCB() called with socket in invalid state" + : "This platform does not support socket error message notifications"); + if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) { + if (callback) { + callback->errMessageError(ex); + } + } else { + startFail(); + if (callback) { + callback->errMessageError(ex); + } + finishFail(); + } +} + +void AsyncSocket::invokeConnectErr(const AsyncSocketException& ex) { + connectEndTime_ = std::chrono::steady_clock::now(); + if (connectCallback_) { + ConnectCallback* callback = connectCallback_; + connectCallback_ = nullptr; + callback->connectErr(ex); + } +} + +void AsyncSocket::invokeConnectSuccess() { + connectEndTime_ = std::chrono::steady_clock::now(); + if (connectCallback_) { + ConnectCallback* callback = connectCallback_; + connectCallback_ = nullptr; + callback->connectSuccess(); + } +} + +void AsyncSocket::invalidState(ReadCallback* callback) { + VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << "): setReadCallback(" << callback << ") called in invalid state " + << state_; + + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, + "setReadCallback() called with socket in " + "invalid state"); + if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) { + if (callback) { + callback->readErr(ex); + } + } else { + startFail(); + if (callback) { + callback->readErr(ex); + } + finishFail(); + } +} + +void AsyncSocket::invalidState(WriteCallback* callback) { + VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_ + << "): write() called in invalid state " << state_; + + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, + withAddr("write() called with socket in invalid state")); + if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) { + if (callback) { + callback->writeErr(0, ex); + } + } else { + startFail(); + if (callback) { + callback->writeErr(0, ex); + } + finishFail(); + } +} + +void AsyncSocket::doClose() { + if (fd_ == NetworkSocket()) { + return; + } + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->close(fd_); + } else { + netops::close(fd_); + } + fd_ = NetworkSocket(); + + // we also want to clear the zerocopy maps + // if the fd has been closed + idZeroCopyBufPtrMap_.clear(); + idZeroCopyBufInfoMap_.clear(); +} + +std::ostream& operator<<( + std::ostream& os, + const AsyncSocket::StateEnum& state) { + os << static_cast<int>(state); + return os; +} + +std::string AsyncSocket::withAddr(folly::StringPiece s) { + // Don't use addr_ directly because it may not be initialized + // e.g. if constructed from fd + folly::SocketAddress peer, local; + try { + getLocalAddress(&local); + } catch (...) { + // ignore + } + try { + getPeerAddress(&peer); + } catch (...) { + // ignore + } + + return folly::to<std::string>( + s, " (peer=", peer.describe(), ", local=", local.describe(), ")"); +} + +void AsyncSocket::setBufferCallback(BufferCallback* cb) { + bufferCallback_ = cb; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.h new file mode 100644 index 000000000..43c5eeb5d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocket.h @@ -0,0 +1,1343 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> +#include <folly/SocketAddress.h> +#include <folly/detail/SocketFastOpen.h> +#include <folly/io/IOBuf.h> +#include <folly/io/ShutdownSocketSet.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/io/async/AsyncSocketException.h> +#include <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/AsyncTransport.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/EventHandler.h> +#include <folly/portability/Sockets.h> + +#include <sys/types.h> + +#include <chrono> +#include <map> +#include <memory> + +namespace folly { + +/** + * A class for performing asynchronous I/O on a socket. + * + * AsyncSocket allows users to asynchronously wait for data on a socket, and + * to asynchronously send data. + * + * The APIs for reading and writing are intentionally asymmetric. Waiting for + * data to read is a persistent API: a callback is installed, and is notified + * whenever new data is available. It continues to be notified of new events + * until it is uninstalled. + * + * AsyncSocket does not provide read timeout functionality, because it + * typically cannot determine when the timeout should be active. Generally, a + * timeout should only be enabled when processing is blocked waiting on data + * from the remote endpoint. For server sockets, the timeout should not be + * active if the server is currently processing one or more outstanding + * requests for this socket. For client sockets, the timeout should not be + * active if there are no requests pending on the socket. Additionally, if a + * client has multiple pending requests, it will ususally want a separate + * timeout for each request, rather than a single read timeout. + * + * The write API is fairly intuitive: a user can request to send a block of + * data, and a callback will be informed once the entire block has been + * transferred to the kernel, or on error. AsyncSocket does provide a send + * timeout, since most callers want to give up if the remote end stops + * responding and no further progress can be made sending the data. + */ + +#if defined __linux__ && !defined SO_NO_TRANSPARENT_TLS +#define SO_NO_TRANSPARENT_TLS 200 +#endif + +#if defined __linux__ && !defined SO_NO_TSOCKS +#define SO_NO_TSOCKS 201 +#endif + +#ifdef _MSC_VER +// We do a dynamic_cast on this, in +// AsyncTransportWrapper::getUnderlyingTransport so be safe and +// force displacements for it. See: +// https://msdn.microsoft.com/en-us/library/7sf3txa8.aspx +#pragma vtordisp(push, 2) +#endif +class AsyncSocket : virtual public AsyncTransportWrapper { + public: + using UniquePtr = std::unique_ptr<AsyncSocket, Destructor>; + + class ConnectCallback { + public: + virtual ~ConnectCallback() = default; + + /** + * connectSuccess() will be invoked when the connection has been + * successfully established. + */ + virtual void connectSuccess() noexcept = 0; + + /** + * connectErr() will be invoked if the connection attempt fails. + * + * @param ex An exception describing the error that occurred. + */ + virtual void connectErr(const AsyncSocketException& ex) noexcept = 0; + + /** + * preConnect() will be invoked just before the actual connect happens, + * default is no-ops. + * + * @param fd An underneath created socket, use for connection. + * + */ + virtual void preConnect(NetworkSocket /*fd*/) {} + }; + + class EvbChangeCallback { + public: + virtual ~EvbChangeCallback() = default; + + // Called when the socket has been attached to a new EVB + // and is called from within that EVB thread + virtual void evbAttached(AsyncSocket* socket) = 0; + + // Called when the socket is detached from an EVB and + // is called from the EVB thread being detached + virtual void evbDetached(AsyncSocket* socket) = 0; + }; + + /** + * This interface is implemented only for platforms supporting + * per-socket error queues. + */ + class ErrMessageCallback { + public: + virtual ~ErrMessageCallback() = default; + + /** + * errMessage() will be invoked when kernel puts a message to + * the error queue associated with the socket. + * + * @param cmsg Reference to cmsghdr structure describing + * a message read from error queue associated + * with the socket. + */ + virtual void errMessage(const cmsghdr& cmsg) noexcept = 0; + + /** + * errMessageError() will be invoked if an error occurs reading a message + * from the socket error stream. + * + * @param ex An exception describing the error that occurred. + */ + virtual void errMessageError(const AsyncSocketException& ex) noexcept = 0; + }; + + class SendMsgParamsCallback { + public: + virtual ~SendMsgParamsCallback() = default; + + /** + * getFlags() will be invoked to retrieve the desired flags to be passed + * to ::sendmsg() system call. It is responsible for converting flags set in + * the passed folly::WriteFlags enum into a integer flag bitmask that can be + * passed to ::sendmsg. Some flags in folly::WriteFlags do not correspond to + * flags that can be passed to ::sendmsg and may instead be handled via + * getAncillaryData. + * + * This method was intentionally declared non-virtual, so there is no way to + * override it. Instead feel free to override getFlagsImpl(...) instead, and + * enjoy the convenience of defaultFlags passed there. + * + * @param flags Write flags requested for the given write operation + */ + int getFlags(folly::WriteFlags flags, bool zeroCopyEnabled) noexcept { + return getFlagsImpl(flags, getDefaultFlags(flags, zeroCopyEnabled)); + } + + /** + * getAncillaryData() will be invoked to initialize ancillary data + * buffer referred by "msg_control" field of msghdr structure passed to + * ::sendmsg() system call based on the flags set in the passed + * folly::WriteFlags enum. Some flags in folly::WriteFlags are not relevant + * during this process. The function assumes that the size of buffer + * is not smaller than the value returned by getAncillaryDataSize() method + * for the same combination of flags. + * + * @param flags Write flags requested for the given write operation + * @param data Pointer to ancillary data buffer to initialize. + */ + virtual void getAncillaryData( + folly::WriteFlags /*flags*/, + void* /*data*/) noexcept {} + + /** + * getAncillaryDataSize() will be invoked to retrieve the size of + * ancillary data buffer which should be passed to ::sendmsg() system call + * + * @param flags Write flags requested for the given write operation + */ + virtual uint32_t getAncillaryDataSize( + folly::WriteFlags /*flags*/) noexcept { + return 0; + } + + static const size_t maxAncillaryDataSize{0x5000}; + + private: + /** + * getFlagsImpl() will be invoked by getFlags(folly::WriteFlags flags) + * method to retrieve the flags to be passed to ::sendmsg() system call. + * SendMsgParamsCallback::getFlags() is calling this method, and returns + * its results directly to the caller in AsyncSocket. + * Classes inheriting from SendMsgParamsCallback are welcome to override + * this method to force SendMsgParamsCallback to return its own set + * of flags. + * + * @param flags Write flags requested for the given write operation + * @param defaultflags A set of message flags returned by getDefaultFlags() + * method for the given "flags" mask. + */ + virtual int getFlagsImpl(folly::WriteFlags /*flags*/, int defaultFlags) { + return defaultFlags; + } + + /** + * getDefaultFlags() will be invoked by getFlags(folly::WriteFlags flags) + * to retrieve the default set of flags, and pass them to getFlagsImpl(...) + * + * @param flags Write flags requested for the given write operation + */ + int getDefaultFlags(folly::WriteFlags flags, bool zeroCopyEnabled) noexcept; + }; + + explicit AsyncSocket(); + /** + * Create a new unconnected AsyncSocket. + * + * connect() must later be called on this socket to establish a connection. + */ + explicit AsyncSocket(EventBase* evb); + + void setShutdownSocketSet(const std::weak_ptr<ShutdownSocketSet>& wSS); + + /** + * Create a new AsyncSocket and begin the connection process. + * + * @param evb EventBase that will manage this socket. + * @param address The address to connect to. + * @param connectTimeout Optional timeout in milliseconds for the connection + * attempt. + * @param useZeroCopy Optional zerocopy socket mode + */ + AsyncSocket( + EventBase* evb, + const folly::SocketAddress& address, + uint32_t connectTimeout = 0, + bool useZeroCopy = false); + + /** + * Create a new AsyncSocket and begin the connection process. + * + * @param evb EventBase that will manage this socket. + * @param ip IP address to connect to (dotted-quad). + * @param port Destination port in host byte order. + * @param connectTimeout Optional timeout in milliseconds for the connection + * attempt. + * @param useZeroCopy Optional zerocopy socket mode + */ + AsyncSocket( + EventBase* evb, + const std::string& ip, + uint16_t port, + uint32_t connectTimeout = 0, + bool useZeroCopy = false); + + /** + * Create a AsyncSocket from an already connected socket file descriptor. + * + * Note that while AsyncSocket enables TCP_NODELAY for sockets it creates + * when connecting, it does not change the socket options when given an + * existing file descriptor. If callers want TCP_NODELAY enabled when using + * this version of the constructor, they need to explicitly call + * setNoDelay(true) after the constructor returns. + * + * @param evb EventBase that will manage this socket. + * @param fd File descriptor to take over (should be a connected socket). + * @param zeroCopyBufId Zerocopy buf id to start with. + */ + AsyncSocket(EventBase* evb, NetworkSocket fd, uint32_t zeroCopyBufId = 0); + + /** + * Create an AsyncSocket from a different, already connected AsyncSocket. + * + * Similar to AsyncSocket(evb, fd) when fd was previously owned by an + * AsyncSocket. + */ + explicit AsyncSocket(AsyncSocket::UniquePtr); + + /** + * Helper function to create a shared_ptr<AsyncSocket>. + * + * This passes in the correct destructor object, since AsyncSocket's + * destructor is protected and cannot be invoked directly. + */ + static std::shared_ptr<AsyncSocket> newSocket(EventBase* evb) { + return std::shared_ptr<AsyncSocket>(new AsyncSocket(evb), Destructor()); + } + + /** + * Helper function to create a shared_ptr<AsyncSocket>. + */ + static std::shared_ptr<AsyncSocket> newSocket( + EventBase* evb, + const folly::SocketAddress& address, + uint32_t connectTimeout = 0, + bool useZeroCopy = false) { + return std::shared_ptr<AsyncSocket>( + new AsyncSocket(evb, address, connectTimeout, useZeroCopy), + Destructor()); + } + + /** + * Helper function to create a shared_ptr<AsyncSocket>. + */ + static std::shared_ptr<AsyncSocket> newSocket( + EventBase* evb, + const std::string& ip, + uint16_t port, + uint32_t connectTimeout = 0, + bool useZeroCopy = false) { + return std::shared_ptr<AsyncSocket>( + new AsyncSocket(evb, ip, port, connectTimeout, useZeroCopy), + Destructor()); + } + + /** + * Helper function to create a shared_ptr<AsyncSocket>. + */ + static std::shared_ptr<AsyncSocket> newSocket( + EventBase* evb, + NetworkSocket fd) { + return std::shared_ptr<AsyncSocket>(new AsyncSocket(evb, fd), Destructor()); + } + + /** + * Destroy the socket. + * + * AsyncSocket::destroy() must be called to destroy the socket. + * The normal destructor is private, and should not be invoked directly. + * This prevents callers from deleting a AsyncSocket while it is invoking a + * callback. + */ + void destroy() override; + + /** + * Get the EventBase used by this socket. + */ + EventBase* getEventBase() const override { + return eventBase_; + } + + /** + * Get the network socket used by the AsyncSocket. + */ + virtual NetworkSocket getNetworkSocket() const { + return fd_; + } + + /** + * Extract the file descriptor from the AsyncSocket. + * + * This will immediately cause any installed callbacks to be invoked with an + * error. The AsyncSocket may no longer be used after the file descriptor + * has been extracted. + * + * This method should be used with care as the resulting fd is not guaranteed + * to perfectly reflect the state of the AsyncSocket (security state, + * pre-received data, etc.). + * + * Returns the file descriptor. The caller assumes ownership of the + * descriptor, and it will not be closed when the AsyncSocket is destroyed. + */ + virtual NetworkSocket detachNetworkSocket(); + + static const folly::SocketAddress& anyAddress(); + + /** + * Initiate a connection. + * + * @param callback The callback to inform when the connection attempt + * completes. + * @param address The address to connect to. + * @param timeout A timeout value, in milliseconds. If the connection + * does not succeed within this period, + * callback->connectError() will be invoked. + */ + virtual void connect( + ConnectCallback* callback, + const folly::SocketAddress& address, + int timeout = 0, + const SocketOptionMap& options = emptySocketOptionMap, + const folly::SocketAddress& bindAddr = anyAddress()) noexcept; + + void connect( + ConnectCallback* callback, + const std::string& ip, + uint16_t port, + int timeout = 0, + const SocketOptionMap& options = emptySocketOptionMap) noexcept; + + /** + * If a connect request is in-flight, cancels it and closes the socket + * immediately. Otherwise, this is a no-op. + * + * This does not invoke any connection related callbacks. Call this to + * prevent any connect callback while cleaning up, etc. + */ + virtual void cancelConnect(); + + /** + * Set the send timeout. + * + * If write requests do not make any progress for more than the specified + * number of milliseconds, fail all pending writes and close the socket. + * + * If write requests are currently pending when setSendTimeout() is called, + * the timeout interval is immediately restarted using the new value. + * + * (See the comments for AsyncSocket for an explanation of why AsyncSocket + * provides setSendTimeout() but not setRecvTimeout().) + * + * @param milliseconds The timeout duration, in milliseconds. If 0, no + * timeout will be used. + */ + void setSendTimeout(uint32_t milliseconds) override; + + /** + * Get the send timeout. + * + * @return Returns the current send timeout, in milliseconds. A return value + * of 0 indicates that no timeout is set. + */ + uint32_t getSendTimeout() const override { + return sendTimeout_; + } + + /** + * Set the maximum number of reads to execute from the underlying + * socket each time the EventBase detects that new ingress data is + * available. The default is unlimited, but callers can use this method + * to limit the amount of data read from the socket per event loop + * iteration. + * + * @param maxReads Maximum number of reads per data-available event; + * a value of zero means unlimited. + */ + void setMaxReadsPerEvent(uint16_t maxReads) { + maxReadsPerEvent_ = maxReads; + } + + /** + * Get the maximum number of reads this object will execute from + * the underlying socket each time the EventBase detects that new + * ingress data is available. + * + * @returns Maximum number of reads per data-available event; a value + * of zero means unlimited. + */ + uint16_t getMaxReadsPerEvent() const { + return maxReadsPerEvent_; + } + + /** + * Set a pointer to ErrMessageCallback implementation which will be + * receiving notifications for messages posted to the error queue + * associated with the socket. + * ErrMessageCallback is implemented only for platforms with + * per-socket error message queus support (recvmsg() system call must + * ) + * + */ + virtual void setErrMessageCB(ErrMessageCallback* callback); + + /** + * Get a pointer to ErrMessageCallback implementation currently + * registered with this socket. + * + */ + virtual ErrMessageCallback* getErrMessageCallback() const; + + /** + * Set a pointer to SendMsgParamsCallback implementation which + * will be used to form ::sendmsg() system call parameters + * + */ + virtual void setSendMsgParamCB(SendMsgParamsCallback* callback); + + /** + * Get a pointer to SendMsgParamsCallback implementation currently + * registered with this socket. + * + */ + virtual SendMsgParamsCallback* getSendMsgParamsCB() const; + + // Read and write methods + void setReadCB(ReadCallback* callback) override; + ReadCallback* getReadCallback() const override; + + bool setZeroCopy(bool enable) override; + bool getZeroCopy() const override { + return zeroCopyEnabled_; + } + + uint32_t getZeroCopyBufId() const { + return zeroCopyBufId_; + } + + size_t getZeroCopyReenableThreshold() const { + return zeroCopyReenableThreshold_; + } + + void setZeroCopyEnableFunc(AsyncWriter::ZeroCopyEnableFunc func) override; + + void setZeroCopyReenableThreshold(size_t threshold); + + void write( + WriteCallback* callback, + const void* buf, + size_t bytes, + WriteFlags flags = WriteFlags::NONE) override; + void writev( + WriteCallback* callback, + const iovec* vec, + size_t count, + WriteFlags flags = WriteFlags::NONE) override; + void writeChain( + WriteCallback* callback, + std::unique_ptr<folly::IOBuf>&& buf, + WriteFlags flags = WriteFlags::NONE) override; + + class WriteRequest; + virtual void writeRequest(WriteRequest* req); + void writeRequestReady() { + handleWrite(); + } + + // Methods inherited from AsyncTransport + void close() override; + void closeNow() override; + void closeWithReset() override; + void shutdownWrite() override; + void shutdownWriteNow() override; + + bool readable() const override; + bool writable() const override; + bool isPending() const override; + virtual bool hangup() const; + bool good() const override; + bool error() const override; + void attachEventBase(EventBase* eventBase) override; + void detachEventBase() override; + bool isDetachable() const override; + + void getLocalAddress(folly::SocketAddress* address) const override; + void getPeerAddress(folly::SocketAddress* address) const override; + + bool isEorTrackingEnabled() const override { + return trackEor_; + } + + void setEorTracking(bool track) override { + trackEor_ = track; + } + + bool connecting() const override { + return (state_ == StateEnum::CONNECTING); + } + + virtual bool isClosedByPeer() const { + return ( + state_ == StateEnum::CLOSED && + (readErr_ == READ_EOF || readErr_ == READ_ERROR)); + } + + virtual bool isClosedBySelf() const { + return ( + state_ == StateEnum::CLOSED && + (readErr_ != READ_EOF && readErr_ != READ_ERROR)); + } + + size_t getAppBytesWritten() const override { + return appBytesWritten_; + } + + size_t getRawBytesWritten() const override { + return getAppBytesWritten(); + } + + size_t getAppBytesReceived() const override { + return appBytesReceived_; + } + + size_t getRawBytesReceived() const override { + return getAppBytesReceived(); + } + + size_t getAppBytesBuffered() const override { + return totalAppBytesScheduledForWrite_ - appBytesWritten_; + } + size_t getRawBytesBuffered() const override { + return getAppBytesBuffered(); + } + + std::chrono::nanoseconds getConnectTime() const { + return connectEndTime_ - connectStartTime_; + } + + std::chrono::milliseconds getConnectTimeout() const { + return connectTimeout_; + } + + std::chrono::steady_clock::time_point getConnectStartTime() const { + return connectStartTime_; + } + + std::chrono::steady_clock::time_point getConnectEndTime() const { + return connectEndTime_; + } + + bool getTFOAttempted() const { + return tfoAttempted_; + } + + /** + * Returns whether or not the attempt to use TFO + * finished successfully. This does not necessarily + * mean TFO worked, just that trying to use TFO + * succeeded. + */ + bool getTFOFinished() const { + return tfoFinished_; + } + + /** + * Returns whether or not TFO attempt succeded on this + * connection. + * For servers this is pretty straightforward API and can + * be invoked right after the connection is accepted. This API + * will perform one syscall. + * This API is a bit tricky to use for clients, since clients + * only know this for sure after the SYN-ACK is returned. So it's + * appropriate to call this only after the first application + * data is read from the socket when the caller knows that + * the SYN has been ACKed by the server. + */ + bool getTFOSucceded() const; + + // Methods controlling socket options + + /** + * Force writes to be transmitted immediately. + * + * This controls the TCP_NODELAY socket option. When enabled, TCP segments + * are sent as soon as possible, even if it is not a full frame of data. + * When disabled, the data may be buffered briefly to try and wait for a full + * frame of data. + * + * By default, TCP_NODELAY is enabled for AsyncSocket objects. + * + * This method will fail if the socket is not currently open. + * + * @return Returns 0 if the TCP_NODELAY flag was successfully updated, + * or a non-zero errno value on error. + */ + int setNoDelay(bool noDelay); + + /** + * Set the FD_CLOEXEC flag so that the socket will be closed if the program + * later forks and execs. + */ + void setCloseOnExec(); + + /* + * Set the Flavor of Congestion Control to be used for this Socket + * Please check '/lib/modules/<kernel>/kernel/net/ipv4' for tcp_*.ko + * first to make sure the module is available for plugging in + * Alternatively you can choose from net.ipv4.tcp_allowed_congestion_control + */ + int setCongestionFlavor(const std::string& cname); + + /* + * Forces ACKs to be sent immediately + * + * @return Returns 0 if the TCP_QUICKACK flag was successfully updated, + * or a non-zero errno value on error. + */ + int setQuickAck(bool quickack); + + /** + * Set the send bufsize + */ + int setSendBufSize(size_t bufsize); + + /** + * Set the recv bufsize + */ + int setRecvBufSize(size_t bufsize); + +#if defined(__linux__) + /** + * @brief This method is used to get the number of bytes that are currently + * stored in the TCP send/tx buffer + * + * @return the number of bytes in the send/tx buffer or folly::none if there + * was a problem + */ + size_t getSendBufInUse() const; + + /** + * @brief This method is used to get the number of bytes that are currently + * stored in the TCP receive/rx buffer + * + * @return the number of bytes in the receive/rx buffer or folly::none if + * there was a problem + */ + size_t getRecvBufInUse() const; +#endif + +/** + * Sets a specific tcp personality + * Available only on kernels 3.2 and greater + */ +#define SO_SET_NAMESPACE 41 + int setTCPProfile(int profd); + + /** + * Generic API for reading a socket option. + * + * @param level same as the "level" parameter in getsockopt(). + * @param optname same as the "optname" parameter in getsockopt(). + * @param optval pointer to the variable in which the option value should + * be returned. + * @param optlen value-result argument, initially containing the size of + * the buffer pointed to by optval, and modified on return + * to indicate the actual size of the value returned. + * @return same as the return value of getsockopt(). + */ + template <typename T> + int getSockOpt(int level, int optname, T* optval, socklen_t* optlen) { + return netops::getsockopt(fd_, level, optname, (void*)optval, optlen); + } + + /** + * Generic API for setting a socket option. + * + * @param level same as the "level" parameter in getsockopt(). + * @param optname same as the "optname" parameter in getsockopt(). + * @param optval the option value to set. + * @return same as the return value of setsockopt(). + */ + template <typename T> + int setSockOpt(int level, int optname, const T* optval) { + return netops::setsockopt(fd_, level, optname, optval, sizeof(T)); + } + + /** + * Virtual method for reading a socket option returning integer + * value, which is the most typical case. Convenient for overriding + * and mocking. + * + * @param level same as the "level" parameter in getsockopt(). + * @param optname same as the "optname" parameter in getsockopt(). + * @param optval same as "optval" parameter in getsockopt(). + * @param optlen same as "optlen" parameter in getsockopt(). + * @return same as the return value of getsockopt(). + */ + virtual int + getSockOptVirtual(int level, int optname, void* optval, socklen_t* optlen) { + return netops::getsockopt(fd_, level, optname, optval, optlen); + } + + /** + * Virtual method for setting a socket option accepting integer + * value, which is the most typical case. Convenient for overriding + * and mocking. + * + * @param level same as the "level" parameter in setsockopt(). + * @param optname same as the "optname" parameter in setsockopt(). + * @param optval same as "optval" parameter in setsockopt(). + * @param optlen same as "optlen" parameter in setsockopt(). + * @return same as the return value of setsockopt(). + */ + virtual int setSockOptVirtual( + int level, + int optname, + void const* optval, + socklen_t optlen) { + return netops::setsockopt(fd_, level, optname, optval, optlen); + } + + /** + * Set pre-received data, to be returned to read callback before any data + * from the socket. + */ + virtual void setPreReceivedData(std::unique_ptr<IOBuf> data) { + if (preReceivedData_) { + preReceivedData_->prependChain(std::move(data)); + } else { + preReceivedData_ = std::move(data); + } + } + + /** + * Enables TFO behavior on the AsyncSocket if FOLLY_ALLOW_TFO + * is set. + */ + void enableTFO() { + // No-op if folly does not allow tfo +#if FOLLY_ALLOW_TFO + tfoEnabled_ = true; +#endif + } + + void disableTransparentTls() { + noTransparentTls_ = true; + } + + void disableTSocks() { + noTSocks_ = true; + } + + enum class StateEnum : uint8_t { + UNINIT, + CONNECTING, + ESTABLISHED, + CLOSED, + ERROR, + FAST_OPEN, + }; + + void setBufferCallback(BufferCallback* cb); + + // Callers should set this prior to connecting the socket for the safest + // behavior. + void setEvbChangedCallback(std::unique_ptr<EvbChangeCallback> cb) { + evbChangeCb_ = std::move(cb); + } + + /** + * Attempt to cache the current local and peer addresses (if not already + * cached) so that they are available from getPeerAddress() and + * getLocalAddress() even after the socket is closed. + */ + void cacheAddresses(); + + /** + * Returns true if there is any zero copy write in progress + * Needs to be called from within the socket's EVB thread + */ + bool isZeroCopyWriteInProgress() const noexcept; + + /** + * Tries to process the msg error queue + * And returns true if there are no more zero copy writes in progress + */ + bool processZeroCopyWriteInProgress() noexcept; + + void setPeerCertificate( + std::unique_ptr<const AsyncTransportCertificate> cert) { + peerCertData_ = std::move(cert); + } + const AsyncTransportCertificate* getPeerCertificate() const override { + return peerCertData_.get(); + } + + void setSelfCertificate( + std::unique_ptr<const AsyncTransportCertificate> cert) { + selfCertData_ = std::move(cert); + } + + const AsyncTransportCertificate* getSelfCertificate() const override { + return selfCertData_.get(); + } + + /** + * Whether socket should be closed on write failure (true by default). + */ + void setCloseOnFailedWrite(bool closeOnFailedWrite) { + closeOnFailedWrite_ = closeOnFailedWrite; + } + + /** + * writeReturn is the total number of bytes written, or WRITE_ERROR on error. + * If no data has been written, 0 is returned. + * exception is a more specific exception that cause a write error. + * Not all writes have exceptions associated with them thus writeReturn + * should be checked to determine whether the operation resulted in an error. + */ + struct WriteResult { + explicit WriteResult(ssize_t ret) : writeReturn(ret) {} + + WriteResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e) + : writeReturn(ret), exception(std::move(e)) {} + + ssize_t writeReturn; + std::unique_ptr<const AsyncSocketException> exception; + }; + + /** + * readReturn is the number of bytes read, or READ_EOF on EOF, or + * READ_ERROR on error, or READ_BLOCKING if the operation will + * block. + * exception is a more specific exception that may have caused a read error. + * Not all read errors have exceptions associated with them thus readReturn + * should be checked to determine whether the operation resulted in an error. + */ + struct ReadResult { + explicit ReadResult(ssize_t ret) : readReturn(ret) {} + + ReadResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e) + : readReturn(ret), exception(std::move(e)) {} + + ssize_t readReturn; + std::unique_ptr<const AsyncSocketException> exception; + }; + + /** + * A WriteRequest object tracks information about a pending write operation. + */ + class WriteRequest { + public: + WriteRequest(AsyncSocket* socket, WriteCallback* callback) + : socket_(socket), callback_(callback) {} + + virtual void start() {} + + virtual void destroy() = 0; + + virtual WriteResult performWrite() = 0; + + virtual void consume() = 0; + + virtual bool isComplete() = 0; + + WriteRequest* getNext() const { + return next_; + } + + WriteCallback* getCallback() const { + return callback_; + } + + uint32_t getTotalBytesWritten() const { + return totalBytesWritten_; + } + + void append(WriteRequest* next) { + assert(next_ == nullptr); + next_ = next; + } + + void fail(const char* fn, const AsyncSocketException& ex) { + socket_->failWrite(fn, ex); + } + + void bytesWritten(size_t count) { + totalBytesWritten_ += uint32_t(count); + socket_->appBytesWritten_ += count; + } + + protected: + // protected destructor, to ensure callers use destroy() + virtual ~WriteRequest() {} + + AsyncSocket* socket_; ///< parent socket + WriteRequest* next_{nullptr}; ///< pointer to next WriteRequest + WriteCallback* callback_; ///< completion callback + uint32_t totalBytesWritten_{0}; ///< total bytes written + }; + + protected: + enum ReadResultEnum { + READ_EOF = 0, + READ_ERROR = -1, + READ_BLOCKING = -2, + READ_NO_ERROR = -3, + }; + + enum WriteResultEnum { + WRITE_ERROR = -1, + }; + + /** + * Protected destructor. + * + * Users of AsyncSocket must never delete it directly. Instead, invoke + * destroy() instead. (See the documentation in DelayedDestruction.h for + * more details.) + */ + ~AsyncSocket() override; + + friend std::ostream& operator<<(std::ostream& os, const StateEnum& state); + + enum ShutdownFlags { + /// shutdownWrite() called, but we are still waiting on writes to drain + SHUT_WRITE_PENDING = 0x01, + /// writes have been completely shut down + SHUT_WRITE = 0x02, + /** + * Reads have been shutdown. + * + * At the moment we don't distinguish between remote read shutdown + * (received EOF from the remote end) and local read shutdown. We can + * only receive EOF when a read callback is set, and we immediately inform + * it of the EOF. Therefore there doesn't seem to be any reason to have a + * separate state of "received EOF but the local side may still want to + * read". + * + * We also don't currently provide any API for only shutting down the read + * side of a socket. (This is a no-op as far as TCP is concerned, anyway.) + */ + SHUT_READ = 0x04, + }; + + class BytesWriteRequest; + + class WriteTimeout : public AsyncTimeout { + public: + WriteTimeout(AsyncSocket* socket, EventBase* eventBase) + : AsyncTimeout(eventBase), socket_(socket) {} + + void timeoutExpired() noexcept override { + socket_->timeoutExpired(); + } + + private: + AsyncSocket* socket_; + }; + + class IoHandler : public EventHandler { + public: + IoHandler(AsyncSocket* socket, EventBase* eventBase) + : EventHandler(eventBase, NetworkSocket()), socket_(socket) {} + IoHandler(AsyncSocket* socket, EventBase* eventBase, NetworkSocket fd) + : EventHandler(eventBase, fd), socket_(socket) {} + + void handlerReady(uint16_t events) noexcept override { + socket_->ioReady(events); + } + + private: + AsyncSocket* socket_; + }; + + void init(); + + class ImmediateReadCB : public folly::EventBase::LoopCallback { + public: + explicit ImmediateReadCB(AsyncSocket* socket) : socket_(socket) {} + void runLoopCallback() noexcept override { + DestructorGuard dg(socket_); + socket_->checkForImmediateRead(); + } + + private: + AsyncSocket* socket_; + }; + + /** + * Schedule checkForImmediateRead to be executed in the next loop + * iteration. + */ + void scheduleImmediateRead() noexcept { + if (good()) { + eventBase_->runInLoop(&immediateReadHandler_); + } + } + + /** + * Schedule handleInitalReadWrite to run in the next iteration. + */ + void scheduleInitialReadWrite() noexcept { + if (good()) { + DestructorGuard dg(this); + eventBase_->runInLoop([this, dg] { + if (good()) { + handleInitialReadWrite(); + } + }); + } + } + + // event notification methods + void ioReady(uint16_t events) noexcept; + virtual void checkForImmediateRead() noexcept; + virtual void handleInitialReadWrite() noexcept; + virtual void prepareReadBuffer(void** buf, size_t* buflen); + virtual size_t handleErrMessages() noexcept; + virtual void handleRead() noexcept; + virtual void handleWrite() noexcept; + virtual void handleConnect() noexcept; + void timeoutExpired() noexcept; + + /** + * Attempt to read from the socket. + * + * @param buf The buffer to read data into. + * @param buflen The length of the buffer. + * + * @return Returns a read result. See read result for details. + */ + virtual ReadResult performRead(void** buf, size_t* buflen, size_t* offset); + + /** + * Populate an iovec array from an IOBuf and attempt to write it. + * + * @param callback Write completion/error callback. + * @param vec Target iovec array; caller retains ownership. + * @param count Number of IOBufs to write, beginning at start of buf. + * @param buf Chain of iovecs. + * @param flags set of flags for the underlying write calls, like cork + */ + void writeChainImpl( + WriteCallback* callback, + iovec* vec, + size_t count, + std::unique_ptr<folly::IOBuf>&& buf, + WriteFlags flags); + + /** + * Write as much data as possible to the socket without blocking, + * and queue up any leftover data to send when the socket can + * handle writes again. + * + * @param callback The callback to invoke when the write is completed. + * @param vec Array of buffers to write; this method will make a + * copy of the vector (but not the buffers themselves) + * if the write has to be completed asynchronously. + * @param count Number of elements in vec. + * @param buf The IOBuf that manages the buffers referenced by + * vec, or a pointer to nullptr if the buffers are not + * associated with an IOBuf. Note that ownership of + * the IOBuf is transferred here; upon completion of + * the write, the AsyncSocket deletes the IOBuf. + * @param totalBytes The total number of bytes to be written. + * @param flags Set of write flags. + */ + void writeImpl( + WriteCallback* callback, + const iovec* vec, + size_t count, + std::unique_ptr<folly::IOBuf>&& buf, + size_t totalBytes, + WriteFlags flags = WriteFlags::NONE); + + /** + * Attempt to write to the socket. + * + * @param vec The iovec array pointing to the buffers to write. + * @param count The length of the iovec array. + * @param flags Set of write flags. + * @param countWritten On return, the value pointed to by this parameter + * will contain the number of iovec entries that were + * fully written. + * @param partialWritten On return, the value pointed to by this parameter + * will contain the number of bytes written in the + * partially written iovec entry. + * + * @return Returns a WriteResult. See WriteResult for more details. + */ + virtual WriteResult performWrite( + const iovec* vec, + uint32_t count, + WriteFlags flags, + uint32_t* countWritten, + uint32_t* partialWritten); + + /** + * Sends the message over the socket using sendmsg + * + * @param msg Message to send + * @param msg_flags Flags to pass to sendmsg + */ + AsyncSocket::WriteResult + sendSocketMessage(NetworkSocket fd, struct msghdr* msg, int msg_flags); + + virtual ssize_t + tfoSendMsg(NetworkSocket fd, struct msghdr* msg, int msg_flags); + + int socketConnect(const struct sockaddr* addr, socklen_t len); + + virtual void scheduleConnectTimeout(); + void registerForConnectEvents(); + + bool updateEventRegistration(); + + /** + * Update event registration. + * + * @param enable Flags of events to enable. Set it to 0 if no events + * need to be enabled in this call. + * @param disable Flags of events + * to disable. Set it to 0 if no events need to be disabled in this + * call. + * + * @return true iff the update is successful. + */ + bool updateEventRegistration(uint16_t enable, uint16_t disable); + + // Actually close the file descriptor and set it to -1 so we don't + // accidentally close it again. + void doClose(); + + // error handling methods + void startFail(); + void finishFail(); + void finishFail(const AsyncSocketException& ex); + void invokeAllErrors(const AsyncSocketException& ex); + void fail(const char* fn, const AsyncSocketException& ex); + void failConnect(const char* fn, const AsyncSocketException& ex); + void failRead(const char* fn, const AsyncSocketException& ex); + void failErrMessageRead(const char* fn, const AsyncSocketException& ex); + void failWrite( + const char* fn, + WriteCallback* callback, + size_t bytesWritten, + const AsyncSocketException& ex); + void failWrite(const char* fn, const AsyncSocketException& ex); + void failAllWrites(const AsyncSocketException& ex); + virtual void invokeConnectErr(const AsyncSocketException& ex); + virtual void invokeConnectSuccess(); + void invalidState(ConnectCallback* callback); + void invalidState(ErrMessageCallback* callback); + void invalidState(ReadCallback* callback); + void invalidState(WriteCallback* callback); + + std::string withAddr(folly::StringPiece s); + + void cacheLocalAddress() const; + void cachePeerAddress() const; + + void applyOptions( + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos); + + bool isZeroCopyRequest(WriteFlags flags); + + bool isZeroCopyMsg(const cmsghdr& cmsg) const; + void processZeroCopyMsg(const cmsghdr& cmsg); + + uint32_t getNextZeroCopyBufId() { + return zeroCopyBufId_++; + } + void adjustZeroCopyFlags(folly::WriteFlags& flags); + void addZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf); + void addZeroCopyBuf(folly::IOBuf* ptr); + void setZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf); + bool containsZeroCopyBuf(folly::IOBuf* ptr); + void releaseZeroCopyBuf(uint32_t id); + + AsyncWriter::ZeroCopyEnableFunc zeroCopyEnableFunc_; + + // a folly::IOBuf can be used in multiple partial requests + // there is a that maps a buffer id to a raw folly::IOBuf ptr + // and another one that adds a ref count for a folly::IOBuf that is either + // the original ptr or nullptr + uint32_t zeroCopyBufId_{0}; + + struct IOBufInfo { + uint32_t count_{0}; + std::unique_ptr<folly::IOBuf> buf_; + }; + + std::unordered_map<uint32_t, folly::IOBuf*> idZeroCopyBufPtrMap_; + std::unordered_map<folly::IOBuf*, IOBufInfo> idZeroCopyBufInfoMap_; + + StateEnum state_; ///< StateEnum describing current state + uint8_t shutdownFlags_; ///< Shutdown state (ShutdownFlags) + uint16_t eventFlags_; ///< EventBase::HandlerFlags settings + NetworkSocket fd_; ///< The socket file descriptor + mutable folly::SocketAddress addr_; ///< The address we tried to connect to + mutable folly::SocketAddress localAddr_; + ///< The address we are connecting from + uint32_t sendTimeout_; ///< The send timeout, in milliseconds + uint16_t maxReadsPerEvent_; ///< Max reads per event loop iteration + + int8_t readErr_{READ_NO_ERROR}; ///< The read error encountered, if any + + EventBase* eventBase_; ///< The EventBase + WriteTimeout writeTimeout_; ///< A timeout for connect and write + IoHandler ioHandler_; ///< A EventHandler to monitor the fd + ImmediateReadCB immediateReadHandler_; ///< LoopCallback for checking read + + ConnectCallback* connectCallback_; ///< ConnectCallback + ErrMessageCallback* errMessageCallback_; ///< TimestampCallback + SendMsgParamsCallback* ///< Callback for retrieving + sendMsgParamCallback_; ///< ::sendmsg() parameters + ReadCallback* readCallback_; ///< ReadCallback + WriteRequest* writeReqHead_; ///< Chain of WriteRequests + WriteRequest* writeReqTail_; ///< End of WriteRequest chain + std::weak_ptr<ShutdownSocketSet> wShutdownSocketSet_; + size_t appBytesReceived_; ///< Num of bytes received from socket + size_t appBytesWritten_; ///< Num of bytes written to socket + // The total num of bytes passed to AsyncSocket's write functions. It doesn't + // include failed writes, but it does include buffered writes. + size_t totalAppBytesScheduledForWrite_; + + // Pre-received data, to be returned to read callback before any data from the + // socket. + std::unique_ptr<IOBuf> preReceivedData_; + + std::chrono::steady_clock::time_point connectStartTime_; + std::chrono::steady_clock::time_point connectEndTime_; + + std::chrono::milliseconds connectTimeout_{0}; + + std::unique_ptr<EvbChangeCallback> evbChangeCb_{nullptr}; + + BufferCallback* bufferCallback_{nullptr}; + bool tfoEnabled_{false}; + bool tfoAttempted_{false}; + bool tfoFinished_{false}; + bool noTransparentTls_{false}; + bool noTSocks_{false}; + // Whether to track EOR or not. + bool trackEor_{false}; + bool zeroCopyEnabled_{false}; + bool zeroCopyVal_{false}; + // zerocopy re-enable logic + size_t zeroCopyReenableThreshold_{0}; + size_t zeroCopyReenableCounter_{0}; + + // subclasses may cache these on first call to get + mutable std::unique_ptr<const AsyncTransportCertificate> peerCertData_{ + nullptr}; + mutable std::unique_ptr<const AsyncTransportCertificate> selfCertData_{ + nullptr}; + + bool closeOnFailedWrite_{true}; +}; +#ifdef _MSC_VER +#pragma vtordisp(pop) +#endif + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketBase.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketBase.h new file mode 100644 index 000000000..faac4e4c6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketBase.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/SocketAddress.h> +#include <folly/io/async/EventBase.h> + +namespace folly { + +class AsyncSocketBase { + public: + virtual EventBase* getEventBase() const = 0; + virtual ~AsyncSocketBase() = default; + virtual void getAddress(SocketAddress*) const = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.cpp new file mode 100644 index 000000000..28890d397 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncSocketException.h> + +#include <folly/Format.h> +#include <folly/String.h> + +namespace folly { + +/* static */ StringPiece AsyncSocketException::getExceptionTypeString( + AsyncSocketExceptionType type) { + switch (type) { + case UNKNOWN: + return "Unknown async socket exception"; + case NOT_OPEN: + return "Socket not open"; + case ALREADY_OPEN: + return "Socket already open"; + case TIMED_OUT: + return "Timed out"; + case END_OF_FILE: + return "End of file"; + case INTERRUPTED: + return "Interrupted"; + case BAD_ARGS: + return "Invalid arguments"; + case CORRUPTED_DATA: + return "Corrupted Data"; + case INTERNAL_ERROR: + return "Internal error"; + case NOT_SUPPORTED: + return "Not supported"; + case INVALID_STATE: + return "Invalid state"; + case SSL_ERROR: + return "SSL error"; + case COULD_NOT_BIND: + return "Could not bind"; + case NETWORK_ERROR: + return "Network error"; + case EARLY_DATA_REJECTED: + return "Early data rejected"; + default: + return "(Invalid exception type)"; + } +} + +/* static */ std::string AsyncSocketException::getMessage( + AsyncSocketExceptionType type, + const std::string& message, + int errnoCopy) { + if (errnoCopy != 0) { + return sformat( + "AsyncSocketException: {}, type = {}, errno = {} ({})", + message, + getExceptionTypeString(type), + errnoCopy, + errnoStr(errnoCopy)); + } else { + return sformat( + "AsyncSocketException: {}, type = {}", + message, + getExceptionTypeString(type)); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.h new file mode 100644 index 000000000..d99081146 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncSocketException.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdexcept> +#include <string> + +#include <folly/CPortability.h> +#include <folly/Range.h> + +namespace folly { + +class FOLLY_EXPORT AsyncSocketException : public std::runtime_error { + public: + enum AsyncSocketExceptionType { + UNKNOWN = 0, + NOT_OPEN = 1, + ALREADY_OPEN = 2, + TIMED_OUT = 3, + END_OF_FILE = 4, + INTERRUPTED = 5, + BAD_ARGS = 6, + CORRUPTED_DATA = 7, + INTERNAL_ERROR = 8, + NOT_SUPPORTED = 9, + INVALID_STATE = 10, + SSL_ERROR = 12, + COULD_NOT_BIND = 13, + // SASL_HANDSHAKE_TIMEOUT = 14, // no longer used + NETWORK_ERROR = 15, + EARLY_DATA_REJECTED = 16, + }; + + AsyncSocketException( + AsyncSocketExceptionType type, + const std::string& message, + int errnoCopy = 0) + : std::runtime_error(getMessage(type, message, errnoCopy)), + type_(type), + errno_(errnoCopy) {} + + AsyncSocketExceptionType getType() const noexcept { + return type_; + } + + int getErrno() const noexcept { + return errno_; + } + + protected: + /** get the string of exception type */ + static folly::StringPiece getExceptionTypeString( + AsyncSocketExceptionType type); + + /** Return a message based on the input. */ + static std::string getMessage( + AsyncSocketExceptionType type, + const std::string& message, + int errnoCopy); + + /** Error code */ + AsyncSocketExceptionType type_; + + /** A copy of the errno. */ + int errno_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.cpp new file mode 100644 index 000000000..f3c72f472 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/EventUtil.h> +#include <folly/io/async/Request.h> +#include <folly/net/NetworkSocket.h> + +#include <glog/logging.h> +#include <cassert> + +namespace folly { + +AsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager) + : timeoutManager_(timeoutManager) { + event_.eb_event_set( + NetworkSocket::invalid_handle_value, + EV_TIMEOUT, + &AsyncTimeout::libeventCallback, + this); + event_.eb_ev_base(nullptr); + timeoutManager_->attachTimeoutManager( + this, TimeoutManager::InternalEnum::NORMAL); +} + +AsyncTimeout::AsyncTimeout(EventBase* eventBase) : timeoutManager_(eventBase) { + event_.eb_event_set( + NetworkSocket::invalid_handle_value, + EV_TIMEOUT, + &AsyncTimeout::libeventCallback, + this); + event_.eb_ev_base(nullptr); + if (eventBase) { + timeoutManager_->attachTimeoutManager( + this, TimeoutManager::InternalEnum::NORMAL); + } +} + +AsyncTimeout::AsyncTimeout( + TimeoutManager* timeoutManager, + InternalEnum internal) + : timeoutManager_(timeoutManager) { + event_.eb_event_set( + NetworkSocket::invalid_handle_value, + EV_TIMEOUT, + &AsyncTimeout::libeventCallback, + this); + event_.eb_ev_base(nullptr); + timeoutManager_->attachTimeoutManager(this, internal); +} + +AsyncTimeout::AsyncTimeout(EventBase* eventBase, InternalEnum internal) + : timeoutManager_(eventBase) { + event_.eb_event_set( + NetworkSocket::invalid_handle_value, + EV_TIMEOUT, + &AsyncTimeout::libeventCallback, + this); + event_.eb_ev_base(nullptr); + timeoutManager_->attachTimeoutManager(this, internal); +} + +AsyncTimeout::AsyncTimeout() : timeoutManager_(nullptr) { + event_.eb_event_set( + NetworkSocket::invalid_handle_value, + EV_TIMEOUT, + &AsyncTimeout::libeventCallback, + this); + event_.eb_ev_base(nullptr); +} + +AsyncTimeout::~AsyncTimeout() { + cancelTimeout(); +} + +bool AsyncTimeout::scheduleTimeout(TimeoutManager::timeout_type timeout) { + assert(timeoutManager_ != nullptr); + context_ = RequestContext::saveContext(); + return timeoutManager_->scheduleTimeout(this, timeout); +} + +bool AsyncTimeout::scheduleTimeoutHighRes( + TimeoutManager::timeout_type_high_res timeout) { + assert(timeoutManager_ != nullptr); + context_ = RequestContext::saveContext(); + return timeoutManager_->scheduleTimeoutHighRes(this, timeout); +} + +bool AsyncTimeout::scheduleTimeout(uint32_t milliseconds) { + return scheduleTimeout(TimeoutManager::timeout_type(milliseconds)); +} + +void AsyncTimeout::cancelTimeout() { + if (isScheduled()) { + timeoutManager_->cancelTimeout(this); + context_.reset(); + } +} + +bool AsyncTimeout::isScheduled() const { + return event_.isEventRegistered(); +} + +void AsyncTimeout::attachTimeoutManager( + TimeoutManager* timeoutManager, + InternalEnum internal) { + // This also implies no timeout is scheduled. + assert(timeoutManager_ == nullptr); + assert(timeoutManager->isInTimeoutManagerThread()); + timeoutManager_ = timeoutManager; + + timeoutManager_->attachTimeoutManager(this, internal); +} + +void AsyncTimeout::attachEventBase( + EventBase* eventBase, + InternalEnum internal) { + attachTimeoutManager(eventBase, internal); +} + +void AsyncTimeout::detachTimeoutManager() { + // Only allow the event base to be changed if the timeout is not + // currently installed. + if (isScheduled()) { + // Programmer bug. Abort the program. + LOG(FATAL) << "detachEventBase() called on scheduled timeout; aborting"; + } + + if (timeoutManager_) { + timeoutManager_->detachTimeoutManager(this); + timeoutManager_ = nullptr; + } +} + +void AsyncTimeout::detachEventBase() { + detachTimeoutManager(); +} + +void AsyncTimeout::libeventCallback(libevent_fd_t fd, short events, void* arg) { + auto timeout = reinterpret_cast<AsyncTimeout*>(arg); + assert(fd == NetworkSocket::invalid_handle_value); + assert(events == EV_TIMEOUT); + // prevent unused variable warnings + (void)fd; + (void)events; + + // double check that ev_flags gets reset when the timeout is not running + assert( + (event_ref_flags(timeout->event_.getEvent()) & ~EVLIST_INTERNAL) == + EVLIST_INIT); + + // this can't possibly fire if timeout->eventBase_ is nullptr + timeout->timeoutManager_->bumpHandlingTime(); + + RequestContextScopeGuard rctx(timeout->context_); + + timeout->timeoutExpired(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.h new file mode 100644 index 000000000..d038cf7cb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTimeout.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/EventBaseBackendBase.h> +#include <folly/io/async/TimeoutManager.h> + +#include <folly/portability/Event.h> + +#include <memory> +#include <utility> + +namespace folly { + +class EventBase; +class RequestContext; + +/** + * AsyncTimeout is used to asynchronously wait for a timeout to occur. + */ +class AsyncTimeout { + public: + typedef TimeoutManager::InternalEnum InternalEnum; + + /** + * Create a new AsyncTimeout object, driven by the specified TimeoutManager. + */ + explicit AsyncTimeout(TimeoutManager* timeoutManager); + explicit AsyncTimeout(EventBase* eventBase); + + /** + * Create a new internal AsyncTimeout object. + * + * Internal timeouts are like regular timeouts, but will not stop the + * TimeoutManager loop from exiting if the only remaining events are internal + * timeouts. + * + * This is useful for implementing fallback timeouts to abort the + * TimeoutManager loop if the other events have not been processed within a + * specified time period: if the event loop takes too long the timeout will + * fire and can stop the event loop. However, if all other events complete, + * the event loop will exit even though the internal timeout is still + * installed. + */ + AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal); + AsyncTimeout(EventBase* eventBase, InternalEnum internal); + + /** + * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager. + * + * attachEventBase() must be called prior to scheduling the timeout. + */ + AsyncTimeout(); + + AsyncTimeout(const AsyncTimeout&) = delete; + AsyncTimeout& operator=(const AsyncTimeout&) = delete; + + /** + * AsyncTimeout destructor. + * + * The timeout will be automatically cancelled if it is running. + */ + virtual ~AsyncTimeout(); + + /** + * timeoutExpired() is invoked when the timeout period has expired. + */ + virtual void timeoutExpired() noexcept = 0; + + /** + * Schedule the timeout to fire in the specified number of milliseconds. + * + * After the specified number of milliseconds has elapsed, timeoutExpired() + * will be invoked by the TimeoutManager's main loop. + * + * If the timeout is already running, it will be rescheduled with the + * new timeout value. + * + * @param milliseconds The timeout duration, in milliseconds. + * + * @return Returns true if the timeout was successfully scheduled, + * and false if an error occurred. After an error, the timeout is + * always unscheduled, even if scheduleTimeout() was just + * rescheduling an existing timeout. + */ + bool scheduleTimeout(uint32_t milliseconds); + bool scheduleTimeout(TimeoutManager::timeout_type timeout); + bool scheduleTimeoutHighRes(TimeoutManager::timeout_type_high_res timeout); + + /** + * Cancel the timeout, if it is running. + */ + void cancelTimeout(); + + /** + * Returns true if the timeout is currently scheduled. + */ + bool isScheduled() const; + + /** + * Attach the timeout to a TimeoutManager. + * + * This may only be called if the timeout is not currently attached to a + * TimeoutManager (either by using the default constructor, or by calling + * detachTimeoutManager()). + * + * This method must be invoked in the TimeoutManager's thread. + * + * The internal parameter specifies if this timeout should be treated as an + * internal event. TimeoutManager::loop() will return when there are no more + * non-internal events remaining. + */ + void attachTimeoutManager( + TimeoutManager* timeoutManager, + InternalEnum internal = InternalEnum::NORMAL); + void attachEventBase( + EventBase* eventBase, + InternalEnum internal = InternalEnum::NORMAL); + + /** + * Detach the timeout from its TimeoutManager. + * + * This may only be called when the timeout is not running. + * Once detached, the timeout may not be scheduled again until it is + * re-attached to a EventBase by calling attachEventBase(). + * + * This method must be called from the current TimeoutManager's thread. + */ + void detachTimeoutManager(); + void detachEventBase(); + + const TimeoutManager* getTimeoutManager() { + return timeoutManager_; + } + + /** + * Returns the internal handle to the event + */ + EventBaseBackendBase::Event* getEvent() { + return &event_; + } + + /** + * Convenience function that wraps a function object as + * an AsyncTimeout instance and returns the wrapper. + * + * Specially useful when using lambdas as AsyncTimeout + * observers. + * + * Example: + * + * void foo(TimeoutManager &manager) { + * std::atomic_bool done = false; + * + * auto observer = AsyncTimeout::make(manager, [&] { + * std::cout << "hello, world!" << std::endl; + * done = true; + * }); + * + * observer->scheduleTimeout(std::chrono::seconds(5)); + * + * while (!done); // busy wait + * } + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + template <typename TCallback> + static std::unique_ptr<AsyncTimeout> make( + TimeoutManager& manager, + TCallback&& callback); + + /** + * Convenience function that wraps a function object as + * an AsyncTimeout instance and returns the wrapper + * after scheduling it using the given TimeoutManager. + * + * This is equivalent to calling `make_async_timeout` + * followed by a `scheduleTimeout` on the resulting + * wrapper. + * + * Specially useful when using lambdas as AsyncTimeout + * observers. + * + * Example: + * + * void foo(TimeoutManager &manager) { + * std::atomic_bool done = false; + * + * auto observer = AsyncTimeout::schedule( + * std::chrono::seconds(5), manager, [&] { + * std::cout << "hello, world!" << std::endl; + * done = true; + * } + * ); + * + * while (!done); // busy wait + * } + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + template <typename TCallback> + static std::unique_ptr<AsyncTimeout> schedule( + TimeoutManager::timeout_type timeout, + TimeoutManager& manager, + TCallback&& callback); + + private: + static void libeventCallback(libevent_fd_t fd, short events, void* arg); + + EventBaseBackendBase::Event event_; + + /* + * Store a pointer to the TimeoutManager. We only use this + * for some assert() statements, to make sure that AsyncTimeout is always + * used from the correct thread. + */ + TimeoutManager* timeoutManager_; + + // Save the request context for when the timeout fires. + std::shared_ptr<RequestContext> context_; +}; + +namespace detail { + +/** + * Wraps a function object as an AsyncTimeout instance. + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ +template <typename TCallback> +struct async_timeout_wrapper : public AsyncTimeout { + template <typename UCallback> + async_timeout_wrapper(TimeoutManager* manager, UCallback&& callback) + : AsyncTimeout(manager), callback_(std::forward<UCallback>(callback)) {} + + void timeoutExpired() noexcept override { + static_assert( + noexcept(std::declval<TCallback>()()), + "callback must be declared noexcept, e.g.: `[]() noexcept {}`"); + callback_(); + } + + private: + TCallback callback_; +}; + +} // namespace detail + +template <typename TCallback> +std::unique_ptr<AsyncTimeout> AsyncTimeout::make( + TimeoutManager& manager, + TCallback&& callback) { + return std::unique_ptr<AsyncTimeout>( + new detail::async_timeout_wrapper<typename std::decay<TCallback>::type>( + std::addressof(manager), std::forward<TCallback>(callback))); +} + +template <typename TCallback> +std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule( + TimeoutManager::timeout_type timeout, + TimeoutManager& manager, + TCallback&& callback) { + auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback)); + wrapper->scheduleTimeout(timeout); + return wrapper; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransport.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransport.h new file mode 100644 index 000000000..e4457f014 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransport.h @@ -0,0 +1,795 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/io/IOBuf.h> +#include <folly/io/async/AsyncSocketBase.h> +#include <folly/io/async/AsyncTransportCertificate.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/EventBase.h> +#include <folly/portability/OpenSSL.h> +#include <folly/portability/SysUio.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { + +class AsyncSocketException; +class EventBase; +class SocketAddress; + +/* + * flags given by the application for write* calls + */ +enum class WriteFlags : uint32_t { + NONE = 0x00, + /* + * Whether to delay the output until a subsequent non-corked write. + * (Note: may not be supported in all subclasses or on all platforms.) + */ + CORK = 0x01, + /* + * Used to request timestamping when entire buffer ACKed by remote endpoint. + * + * How timestamping is performed is implementation specific and may rely on + * software or hardware timestamps + */ + EOR = 0x02, + /* + * this indicates that only the write side of socket should be shutdown + */ + WRITE_SHUTDOWN = 0x04, + /* + * use msg zerocopy if allowed + */ + WRITE_MSG_ZEROCOPY = 0x08, + /* + * Used to request timestamping when entire buffer transmitted by the NIC. + * + * How timestamping is performed is implementation specific and may rely on + * software or hardware timestamps + */ + TIMESTAMP_TX = 0x10, +}; + +/* + * union operator + */ +constexpr WriteFlags operator|(WriteFlags a, WriteFlags b) { + return static_cast<WriteFlags>( + static_cast<uint32_t>(a) | static_cast<uint32_t>(b)); +} + +/* + * compound assignment union operator + */ +constexpr WriteFlags& operator|=(WriteFlags& a, WriteFlags b) { + a = a | b; + return a; +} + +/* + * intersection operator + */ +constexpr WriteFlags operator&(WriteFlags a, WriteFlags b) { + return static_cast<WriteFlags>( + static_cast<uint32_t>(a) & static_cast<uint32_t>(b)); +} + +/* + * compound assignment intersection operator + */ +constexpr WriteFlags& operator&=(WriteFlags& a, WriteFlags b) { + a = a & b; + return a; +} + +/* + * exclusion parameter + */ +constexpr WriteFlags operator~(WriteFlags a) { + return static_cast<WriteFlags>(~static_cast<uint32_t>(a)); +} + +/* + * unset operator + */ +constexpr WriteFlags unSet(WriteFlags a, WriteFlags b) { + return a & ~b; +} + +/* + * inclusion operator + */ +constexpr bool isSet(WriteFlags a, WriteFlags b) { + return (a & b) == b; +} + +/** + * Write flags that are specifically for the final write call of a buffer. + * + * In some cases, buffers passed to send may be coalesced or split by the socket + * write handling logic. For instance, a buffer passed to AsyncSSLSocket may be + * split across multiple TLS records (and therefore multiple calls to write). + * + * When a buffer is split up, these flags will only be applied for the final + * call to write for that buffer. + */ +constexpr WriteFlags kEorRelevantWriteFlags = + WriteFlags::EOR | WriteFlags::TIMESTAMP_TX; + +/** + * AsyncTransport defines an asynchronous API for streaming I/O. + * + * This class provides an API to for asynchronously waiting for data + * on a streaming transport, and for asynchronously sending data. + * + * The APIs for reading and writing are intentionally asymmetric. Waiting for + * data to read is a persistent API: a callback is installed, and is notified + * whenever new data is available. It continues to be notified of new events + * until it is uninstalled. + * + * AsyncTransport does not provide read timeout functionality, because it + * typically cannot determine when the timeout should be active. Generally, a + * timeout should only be enabled when processing is blocked waiting on data + * from the remote endpoint. For server-side applications, the timeout should + * not be active if the server is currently processing one or more outstanding + * requests on this transport. For client-side applications, the timeout + * should not be active if there are no requests pending on the transport. + * Additionally, if a client has multiple pending requests, it will ususally + * want a separate timeout for each request, rather than a single read timeout. + * + * The write API is fairly intuitive: a user can request to send a block of + * data, and a callback will be informed once the entire block has been + * transferred to the kernel, or on error. AsyncTransport does provide a send + * timeout, since most callers want to give up if the remote end stops + * responding and no further progress can be made sending the data. + */ +class AsyncTransport : public DelayedDestruction, public AsyncSocketBase { + public: + typedef std::unique_ptr<AsyncTransport, Destructor> UniquePtr; + + /** + * Close the transport. + * + * This gracefully closes the transport, waiting for all pending write + * requests to complete before actually closing the underlying transport. + * + * If a read callback is set, readEOF() will be called immediately. If there + * are outstanding write requests, the close will be delayed until all + * remaining writes have completed. No new writes may be started after + * close() has been called. + */ + virtual void close() = 0; + + /** + * Close the transport immediately. + * + * This closes the transport immediately, dropping any outstanding data + * waiting to be written. + * + * If a read callback is set, readEOF() will be called immediately. + * If there are outstanding write requests, these requests will be aborted + * and writeError() will be invoked immediately on all outstanding write + * callbacks. + */ + virtual void closeNow() = 0; + + /** + * Reset the transport immediately. + * + * This closes the transport immediately, sending a reset to the remote peer + * if possible to indicate abnormal shutdown. + * + * Note that not all subclasses implement this reset functionality: some + * subclasses may treat reset() the same as closeNow(). Subclasses that use + * TCP transports should terminate the connection with a TCP reset. + */ + virtual void closeWithReset() { + closeNow(); + } + + /** + * Perform a half-shutdown of the write side of the transport. + * + * The caller should not make any more calls to write() or writev() after + * shutdownWrite() is called. Any future write attempts will fail + * immediately. + * + * Not all transport types support half-shutdown. If the underlying + * transport does not support half-shutdown, it will fully shutdown both the + * read and write sides of the transport. (Fully shutting down the socket is + * better than doing nothing at all, since the caller may rely on the + * shutdownWrite() call to notify the other end of the connection that no + * more data can be read.) + * + * If there is pending data still waiting to be written on the transport, + * the actual shutdown will be delayed until the pending data has been + * written. + * + * Note: There is no corresponding shutdownRead() equivalent. Simply + * uninstall the read callback if you wish to stop reading. (On TCP sockets + * at least, shutting down the read side of the socket is a no-op anyway.) + */ + virtual void shutdownWrite() = 0; + + /** + * Perform a half-shutdown of the write side of the transport. + * + * shutdownWriteNow() is identical to shutdownWrite(), except that it + * immediately performs the shutdown, rather than waiting for pending writes + * to complete. Any pending write requests will be immediately failed when + * shutdownWriteNow() is called. + */ + virtual void shutdownWriteNow() = 0; + + /** + * Determine if transport is open and ready to read or write. + * + * Note that this function returns false on EOF; you must also call error() + * to distinguish between an EOF and an error. + * + * @return true iff the transport is open and ready, false otherwise. + */ + virtual bool good() const = 0; + + /** + * Determine if the transport is readable or not. + * + * @return true iff the transport is readable, false otherwise. + */ + virtual bool readable() const = 0; + + /** + * Determine if the transport is writable or not. + * + * @return true iff the transport is writable, false otherwise. + */ + virtual bool writable() const { + // By default return good() - leave it to implementers to override. + return good(); + } + + /** + * Determine if the there is pending data on the transport. + * + * @return true iff the if the there is pending data, false otherwise. + */ + virtual bool isPending() const { + return readable(); + } + + /** + * Determine if transport is connected to the endpoint + * + * @return false iff the transport is connected, otherwise true + */ + virtual bool connecting() const = 0; + + /** + * Determine if an error has occurred with this transport. + * + * @return true iff an error has occurred (not EOF). + */ + virtual bool error() const = 0; + + /** + * Attach the transport to a EventBase. + * + * This may only be called if the transport is not currently attached to a + * EventBase (by an earlier call to detachEventBase()). + * + * This method must be invoked in the EventBase's thread. + */ + virtual void attachEventBase(EventBase* eventBase) = 0; + + /** + * Detach the transport from its EventBase. + * + * This may only be called when the transport is idle and has no reads or + * writes pending. Once detached, the transport may not be used again until + * it is re-attached to a EventBase by calling attachEventBase(). + * + * This method must be called from the current EventBase's thread. + */ + virtual void detachEventBase() = 0; + + /** + * Determine if the transport can be detached. + * + * This method must be called from the current EventBase's thread. + */ + virtual bool isDetachable() const = 0; + + /** + * Set the send timeout. + * + * If write requests do not make any progress for more than the specified + * number of milliseconds, fail all pending writes and close the transport. + * + * If write requests are currently pending when setSendTimeout() is called, + * the timeout interval is immediately restarted using the new value. + * + * @param milliseconds The timeout duration, in milliseconds. If 0, no + * timeout will be used. + */ + virtual void setSendTimeout(uint32_t milliseconds) = 0; + + /** + * Get the send timeout. + * + * @return Returns the current send timeout, in milliseconds. A return value + * of 0 indicates that no timeout is set. + */ + virtual uint32_t getSendTimeout() const = 0; + + /** + * Get the address of the local endpoint of this transport. + * + * This function may throw AsyncSocketException on error. + * + * @param address The local address will be stored in the specified + * SocketAddress. + */ + virtual void getLocalAddress(SocketAddress* address) const = 0; + + /** + * Get the address of the remote endpoint to which this transport is + * connected. + * + * This function may throw AsyncSocketException on error. + * + * @return Return the local address + */ + SocketAddress getLocalAddress() const { + SocketAddress addr; + getLocalAddress(&addr); + return addr; + } + + void getAddress(SocketAddress* address) const override { + getLocalAddress(address); + } + + /** + * Get the address of the remote endpoint to which this transport is + * connected. + * + * This function may throw AsyncSocketException on error. + * + * @param address The remote endpoint's address will be stored in the + * specified SocketAddress. + */ + virtual void getPeerAddress(SocketAddress* address) const = 0; + + /** + * Get the address of the remote endpoint to which this transport is + * connected. + * + * This function may throw AsyncSocketException on error. + * + * @return Return the remote endpoint's address + */ + SocketAddress getPeerAddress() const { + SocketAddress addr; + getPeerAddress(&addr); + return addr; + } + + /** + * Get the peer certificate information if any + */ + virtual const AsyncTransportCertificate* getPeerCertificate() const { + return nullptr; + } + + /** + * Get the certificate information of this transport, if any + */ + virtual const AsyncTransportCertificate* getSelfCertificate() const { + return nullptr; + } + + /** + * Return the application protocol being used by the underlying transport + * protocol. This is useful for transports which are used to tunnel other + * protocols. + */ + virtual std::string getApplicationProtocol() const noexcept { + return ""; + } + + /** + * Returns the name of the security protocol being used. + */ + virtual std::string getSecurityProtocol() const { + return ""; + } + + /** + * @return True iff end of record tracking is enabled + */ + virtual bool isEorTrackingEnabled() const = 0; + + virtual void setEorTracking(bool track) = 0; + + virtual size_t getAppBytesWritten() const = 0; + virtual size_t getRawBytesWritten() const = 0; + virtual size_t getAppBytesReceived() const = 0; + virtual size_t getRawBytesReceived() const = 0; + + /** + * Calculates the total number of bytes that are currently buffered in the + * transport to be written later. + */ + virtual size_t getAppBytesBuffered() const { + return 0; + } + virtual size_t getRawBytesBuffered() const { + return 0; + } + + /** + * Callback class to signal changes in the transport's internal buffers. + */ + class BufferCallback { + public: + virtual ~BufferCallback() = default; + + /** + * onEgressBuffered() will be invoked when there's a partial write and it + * is necessary to buffer the remaining data. + */ + virtual void onEgressBuffered() = 0; + + /** + * onEgressBufferCleared() will be invoked when whatever was buffered is + * written, or when it errors out. + */ + virtual void onEgressBufferCleared() = 0; + }; + + /** + * Callback class to signal when a transport that did not have replay + * protection gains replay protection. This is needed for 0-RTT security + * protocols. + */ + class ReplaySafetyCallback { + public: + virtual ~ReplaySafetyCallback() = default; + + /** + * Called when the transport becomes replay safe. + */ + virtual void onReplaySafe() = 0; + }; + + /** + * False if the transport does not have replay protection, but will in the + * future. + */ + virtual bool isReplaySafe() const { + return true; + } + + /** + * Set the ReplaySafeCallback on this transport. + * + * This should only be called if isReplaySafe() returns false. + */ + virtual void setReplaySafetyCallback(ReplaySafetyCallback* callback) { + if (callback) { + CHECK(false) << "setReplaySafetyCallback() not supported"; + } + } + + protected: + ~AsyncTransport() override = default; +}; + +class AsyncReader { + public: + class ReadCallback { + public: + virtual ~ReadCallback() = default; + + /** + * When data becomes available, getReadBuffer() will be invoked to get the + * buffer into which data should be read. + * + * This method allows the ReadCallback to delay buffer allocation until + * data becomes available. This allows applications to manage large + * numbers of idle connections, without having to maintain a separate read + * buffer for each idle connection. + * + * It is possible that in some cases, getReadBuffer() may be called + * multiple times before readDataAvailable() is invoked. In this case, the + * data will be written to the buffer returned from the most recent call to + * readDataAvailable(). If the previous calls to readDataAvailable() + * returned different buffers, the ReadCallback is responsible for ensuring + * that they are not leaked. + * + * If getReadBuffer() throws an exception, returns a nullptr buffer, or + * returns a 0 length, the ReadCallback will be uninstalled and its + * readError() method will be invoked. + * + * getReadBuffer() is not allowed to change the transport state before it + * returns. (For example, it should never uninstall the read callback, or + * set a different read callback.) + * + * @param bufReturn getReadBuffer() should update *bufReturn to contain the + * address of the read buffer. This parameter will never + * be nullptr. + * @param lenReturn getReadBuffer() should update *lenReturn to contain the + * maximum number of bytes that may be written to the read + * buffer. This parameter will never be nullptr. + */ + virtual void getReadBuffer(void** bufReturn, size_t* lenReturn) = 0; + + /** + * readDataAvailable() will be invoked when data has been successfully read + * into the buffer returned by the last call to getReadBuffer(). + * + * The read callback remains installed after readDataAvailable() returns. + * It must be explicitly uninstalled to stop receiving read events. + * getReadBuffer() will be called at least once before each call to + * readDataAvailable(). getReadBuffer() will also be called before any + * call to readEOF(). + * + * @param len The number of bytes placed in the buffer. + */ + + virtual void readDataAvailable(size_t len) noexcept = 0; + + /** + * When data becomes available, isBufferMovable() will be invoked to figure + * out which API will be used, readBufferAvailable() or + * readDataAvailable(). If isBufferMovable() returns true, that means + * ReadCallback supports the IOBuf ownership transfer and + * readBufferAvailable() will be used. Otherwise, not. + + * By default, isBufferMovable() always return false. If + * readBufferAvailable() is implemented and to be invoked, You should + * overwrite isBufferMovable() and return true in the inherited class. + * + * This method allows the AsyncSocket/AsyncSSLSocket do buffer allocation by + * itself until data becomes available. Compared with the pre/post buffer + * allocation in getReadBuffer()/readDataAvailabe(), readBufferAvailable() + * has two advantages. First, this can avoid memcpy. E.g., in + * AsyncSSLSocket, the decrypted data was copied from the openssl internal + * buffer to the readbuf buffer. With the buffer ownership transfer, the + * internal buffer can be directly "moved" to ReadCallback. Second, the + * memory allocation can be more precise. The reason is + * AsyncSocket/AsyncSSLSocket can allocate the memory of precise size + * because they have more context about the available data than + * ReadCallback. Think about the getReadBuffer() pre-allocate 4072 bytes + * buffer, but the available data is always 16KB (max OpenSSL record size). + */ + + virtual bool isBufferMovable() noexcept { + return false; + } + + /** + * Suggested buffer size, allocated for read operations, + * if callback is movable and supports folly::IOBuf + */ + + virtual size_t maxBufferSize() const { + return 64 * 1024; // 64K + } + + /** + * readBufferAvailable() will be invoked when data has been successfully + * read. + * + * Note that only either readBufferAvailable() or readDataAvailable() will + * be invoked according to the return value of isBufferMovable(). The timing + * and aftereffect of readBufferAvailable() are the same as + * readDataAvailable() + * + * @param readBuf The unique pointer of read buffer. + */ + + virtual void readBufferAvailable( + std::unique_ptr<IOBuf> /*readBuf*/) noexcept {} + + /** + * readEOF() will be invoked when the transport is closed. + * + * The read callback will be automatically uninstalled immediately before + * readEOF() is invoked. + */ + virtual void readEOF() noexcept = 0; + + /** + * readError() will be invoked if an error occurs reading from the + * transport. + * + * The read callback will be automatically uninstalled immediately before + * readError() is invoked. + * + * @param ex An exception describing the error that occurred. + */ + virtual void readErr(const AsyncSocketException& ex) noexcept = 0; + }; + + // Read methods that aren't part of AsyncTransport. + virtual void setReadCB(ReadCallback* callback) = 0; + virtual ReadCallback* getReadCallback() const = 0; + + protected: + virtual ~AsyncReader() = default; +}; + +class AsyncWriter { + public: + class WriteCallback { + public: + virtual ~WriteCallback() = default; + + /** + * writeSuccess() will be invoked when all of the data has been + * successfully written. + * + * Note that this mainly signals that the buffer containing the data to + * write is no longer needed and may be freed or re-used. It does not + * guarantee that the data has been fully transmitted to the remote + * endpoint. For example, on socket-based transports, writeSuccess() only + * indicates that the data has been given to the kernel for eventual + * transmission. + */ + virtual void writeSuccess() noexcept = 0; + + /** + * writeError() will be invoked if an error occurs writing the data. + * + * @param bytesWritten The number of bytes that were successfull + * @param ex An exception describing the error that occurred. + */ + virtual void writeErr( + size_t bytesWritten, + const AsyncSocketException& ex) noexcept = 0; + }; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void write( + WriteCallback* callback, + const void* buf, + size_t bytes, + WriteFlags flags = WriteFlags::NONE) = 0; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void writev( + WriteCallback* callback, + const iovec* vec, + size_t count, + WriteFlags flags = WriteFlags::NONE) = 0; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void writeChain( + WriteCallback* callback, + std::unique_ptr<IOBuf>&& buf, + WriteFlags flags = WriteFlags::NONE) = 0; + + /** zero copy related + * */ + virtual bool setZeroCopy(bool /*enable*/) { + return false; + } + + virtual bool getZeroCopy() const { + return false; + } + + using ZeroCopyEnableFunc = + std::function<bool(const std::unique_ptr<folly::IOBuf>& buf)>; + + virtual void setZeroCopyEnableFunc(ZeroCopyEnableFunc /*func*/) {} + + protected: + virtual ~AsyncWriter() = default; +}; + +// Transitional intermediate interface. This is deprecated. +// Wrapper around folly::AsyncTransport, that includes read/write callbacks +class AsyncTransportWrapper : virtual public AsyncTransport, + virtual public AsyncReader, + virtual public AsyncWriter { + public: + using UniquePtr = std::unique_ptr<AsyncTransportWrapper, Destructor>; + + // Alias for inherited members from AsyncReader and AsyncWriter + // to keep compatibility. + using ReadCallback = AsyncReader::ReadCallback; + using WriteCallback = AsyncWriter::WriteCallback; + void setReadCB(ReadCallback* callback) override = 0; + ReadCallback* getReadCallback() const override = 0; + void write( + WriteCallback* callback, + const void* buf, + size_t bytes, + WriteFlags flags = WriteFlags::NONE) override = 0; + void writev( + WriteCallback* callback, + const iovec* vec, + size_t count, + WriteFlags flags = WriteFlags::NONE) override = 0; + void writeChain( + WriteCallback* callback, + std::unique_ptr<IOBuf>&& buf, + WriteFlags flags = WriteFlags::NONE) override = 0; + /** + * The transport wrapper may wrap another transport. This returns the + * transport that is wrapped. It returns nullptr if there is no wrapped + * transport. + */ + virtual const AsyncTransportWrapper* getWrappedTransport() const { + return nullptr; + } + + /** + * In many cases when we need to set socket properties or otherwise access the + * underlying transport from a wrapped transport. This method allows access to + * the derived classes of the underlying transport. + */ + template <class T> + const T* getUnderlyingTransport() const { + const AsyncTransportWrapper* current = this; + while (current) { + auto sock = dynamic_cast<const T*>(current); + if (sock) { + return sock; + } + current = current->getWrappedTransport(); + } + return nullptr; + } + + template <class T> + T* getUnderlyingTransport() { + return const_cast<T*>(static_cast<const AsyncTransportWrapper*>(this) + ->getUnderlyingTransport<T>()); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h new file mode 100644 index 000000000..f296c7c9c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { + +/** + * Generic interface applications may implement to convey self or peer + * certificate related information. + */ +class AsyncTransportCertificate { + public: + virtual ~AsyncTransportCertificate() = default; + + /** + * Returns the identity this certificate conveys. + * + * An identity is an opaque string that may be used by the application for + * authentication or authorization purposes. The exact structure and + * semantics of the identity string are determined by concrete + * implementations of AsyncTransport. + */ + virtual std::string getIdentity() const = 0; + + /** + * Returns an X509 structure associated with this Certificate. This may be + * null. + */ + virtual folly::ssl::X509UniquePtr getX509() const = 0; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h new file mode 100644 index 000000000..0c3b56eb8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Memory.h> +#include <folly/io/IOBufQueue.h> +#include <folly/io/async/AsyncUDPSocket.h> +#include <folly/io/async/EventBase.h> + +namespace folly { + +/** + * UDP server socket + * + * It wraps a UDP socket waiting for packets and distributes them among + * a set of event loops in round robin fashion. + * + * NOTE: At the moment it is designed to work with single packet protocols + * in mind. We distribute incoming packets among all the listeners in + * round-robin fashion. So, any protocol that expects to send/recv + * more than 1 packet will not work because they will end up with + * different event base to process. + */ +class AsyncUDPServerSocket : private AsyncUDPSocket::ReadCallback, + public AsyncSocketBase { + public: + class Callback { + public: + using OnDataAvailableParams = + AsyncUDPSocket::ReadCallback::OnDataAvailableParams; + /** + * Invoked when we start reading data from socket. It is invoked in + * each acceptors/listeners event base thread. + */ + virtual void onListenStarted() noexcept = 0; + + /** + * Invoked when the server socket is closed. It is invoked in each + * acceptors/listeners event base thread. + */ + virtual void onListenStopped() noexcept = 0; + + /** + * Invoked when the server socket is paused. It is invoked in each + * acceptors/listeners event base thread. + */ + virtual void onListenPaused() noexcept {} + + /** + * Invoked when the server socket is resumed. It is invoked in each + * acceptors/listeners event base thread. + */ + virtual void onListenResumed() noexcept {} + + /** + * Invoked when a new packet is received + */ + virtual void onDataAvailable( + std::shared_ptr<AsyncUDPSocket> socket, + const folly::SocketAddress& addr, + std::unique_ptr<folly::IOBuf> buf, + bool truncated, + OnDataAvailableParams) noexcept = 0; + + virtual ~Callback() = default; + }; + + enum class DispatchMechanism { RoundRobin, ClientAddressHash }; + + /** + * Create a new UDP server socket + * + * Note about packet size - We allocate buffer of packetSize_ size to read. + * If packet are larger than this value, as per UDP protocol, remaining data + * is dropped and you get `truncated = true` in onDataAvailable callback + */ + explicit AsyncUDPServerSocket( + EventBase* evb, + size_t sz = 1500, + DispatchMechanism dm = DispatchMechanism::RoundRobin) + : evb_(evb), packetSize_(sz), dispatchMechanism_(dm), nextListener_(0) {} + + ~AsyncUDPServerSocket() override { + if (socket_) { + close(); + } + } + + void bind( + const folly::SocketAddress& addy, + const SocketOptionMap& options = emptySocketOptionMap) { + CHECK(!socket_); + + socket_ = std::make_shared<AsyncUDPSocket>(evb_); + socket_->setReusePort(reusePort_); + socket_->setReuseAddr(reuseAddr_); + socket_->applyOptions( + validateSocketOptions( + options, addy.getFamily(), SocketOptionKey::ApplyPos::PRE_BIND), + SocketOptionKey::ApplyPos::PRE_BIND); + socket_->bind(addy); + socket_->applyOptions( + validateSocketOptions( + options, addy.getFamily(), SocketOptionKey::ApplyPos::POST_BIND), + SocketOptionKey::ApplyPos::POST_BIND); + } + + void setReusePort(bool reusePort) { + reusePort_ = reusePort; + } + + void setReuseAddr(bool reuseAddr) { + reuseAddr_ = reuseAddr; + } + + folly::SocketAddress address() const { + CHECK(socket_); + return socket_->address(); + } + + void getAddress(SocketAddress* a) const override { + *a = address(); + } + + /** + * Add a listener to the round robin list + */ + void addListener(EventBase* evb, Callback* callback) { + listeners_.emplace_back(evb, callback); + } + + void listen() { + CHECK(socket_) << "Need to bind before listening"; + + for (auto& listener : listeners_) { + auto callback = listener.second; + + listener.first->runInEventBaseThread( + [callback]() mutable { callback->onListenStarted(); }); + } + + socket_->resumeRead(this); + } + + NetworkSocket getNetworkSocket() const { + CHECK(socket_) << "Need to bind before getting Network Socket"; + return socket_->getNetworkSocket(); + } + + const std::shared_ptr<AsyncUDPSocket>& getSocket() const { + return socket_; + } + + void close() { + CHECK(socket_) << "Need to bind before closing"; + socket_->close(); + socket_.reset(); + } + + EventBase* getEventBase() const override { + return evb_; + } + + /** + * Indicates if the current socket is accepting. + */ + bool isAccepting() const { + return socket_->isReading(); + } + + /** + * Pauses accepting datagrams on the underlying socket. + */ + void pauseAccepting() { + socket_->pauseRead(); + for (auto& listener : listeners_) { + auto callback = listener.second; + + listener.first->runInEventBaseThread( + [callback]() mutable { callback->onListenPaused(); }); + } + } + + /** + * Starts accepting datagrams once again. + */ + void resumeAccepting() { + socket_->resumeRead(this); + for (auto& listener : listeners_) { + auto callback = listener.second; + + listener.first->runInEventBaseThread( + [callback]() mutable { callback->onListenResumed(); }); + } + } + + private: + // AsyncUDPSocket::ReadCallback + void getReadBuffer(void** buf, size_t* len) noexcept override { + std::tie(*buf, *len) = buf_.preallocate(packetSize_, packetSize_); + } + + void onDataAvailable( + const folly::SocketAddress& clientAddress, + size_t len, + bool truncated, + OnDataAvailableParams params) noexcept override { + buf_.postallocate(len); + auto data = buf_.split(len); + + if (listeners_.empty()) { + LOG(WARNING) << "UDP server socket dropping packet, " + << "no listener registered"; + return; + } + + uint32_t listenerId = 0; + uint64_t client_hash_lo = 0; + switch (dispatchMechanism_) { + case DispatchMechanism::ClientAddressHash: + // Hash base on clientAddress. + // 1. This logic is samilar to: clientAddress.hash() % listeners_.size() + // But runs faster as it use multiply and shift instead of division. + // 2. Only use the lower 32 bit from the address hash result for faster + // computation. + client_hash_lo = static_cast<uint32_t>(clientAddress.hash()); + listenerId = (client_hash_lo * listeners_.size()) >> 32; + break; + case DispatchMechanism::RoundRobin: // round robin is default. + default: + if (nextListener_ >= listeners_.size()) { + nextListener_ = 0; + } + listenerId = nextListener_; + ++nextListener_; + break; + } + + auto callback = listeners_[listenerId].second; + + // Schedule it in the listener's eventbase + // XXX: Speed this up + auto f = [socket = socket_, + client = clientAddress, + callback, + data = std::move(data), + truncated, + params]() mutable { + callback->onDataAvailable( + socket, client, std::move(data), truncated, params); + }; + + listeners_[listenerId].first->runInEventBaseThread(std::move(f)); + } + + void onReadError(const AsyncSocketException& ex) noexcept override { + LOG(ERROR) << ex.what(); + + // Lets register to continue listening for packets + socket_->resumeRead(this); + } + + void onReadClosed() noexcept override { + for (auto& listener : listeners_) { + auto callback = listener.second; + + listener.first->runInEventBaseThread( + [callback]() mutable { callback->onListenStopped(); }); + } + } + + EventBase* const evb_; + const size_t packetSize_; + + std::shared_ptr<AsyncUDPSocket> socket_; + + // List of listener to distribute packets among + typedef std::pair<EventBase*, Callback*> Listener; + std::vector<Listener> listeners_; + + DispatchMechanism dispatchMechanism_; + + // Next listener to send packet to + uint32_t nextListener_; + + // Temporary buffer for data + folly::IOBufQueue buf_; + + bool reusePort_{false}; + bool reuseAddr_{false}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.cpp b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.cpp new file mode 100644 index 000000000..1576204c0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.cpp @@ -0,0 +1,873 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/AsyncUDPSocket.h> + +#include <folly/Likely.h> +#include <folly/Utility.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/io/async/EventBase.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Unistd.h> + +#include <boost/preprocessor/control/if.hpp> +#include <cerrno> + +// Due to the way kernel headers are included, this may or may not be defined. +// Number pulled from 3.10 kernel headers. +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +#if FOLLY_HAVE_VLA +#define FOLLY_HAVE_VLA_01 1 +#else +#define FOLLY_HAVE_VLA_01 0 +#endif + +namespace fsp = folly::portability::sockets; + +namespace folly { + +AsyncUDPSocket::AsyncUDPSocket(EventBase* evb) + : EventHandler(CHECK_NOTNULL(evb)), + readCallback_(nullptr), + eventBase_(evb), + fd_() { + evb->dcheckIsInEventBaseThread(); +} + +AsyncUDPSocket::~AsyncUDPSocket() { + if (fd_ != NetworkSocket()) { + close(); + } +} + +void AsyncUDPSocket::bind(const folly::SocketAddress& address) { + NetworkSocket socket = netops::socket( + address.getFamily(), + SOCK_DGRAM, + address.getFamily() != AF_UNIX ? IPPROTO_UDP : 0); + if (socket == NetworkSocket()) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "error creating async udp socket", + errno); + } + + auto g = folly::makeGuard([&] { netops::close(socket); }); + + // put the socket in non-blocking mode + int ret = netops::set_socket_non_blocking(socket); + if (ret != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to put socket in non-blocking mode", + errno); + } + + if (reuseAddr_) { + // put the socket in reuse mode + int value = 1; + if (netops::setsockopt( + socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to put socket in reuse mode", + errno); + } + } + + if (reusePort_) { + // put the socket in port reuse mode + int value = 1; + if (netops::setsockopt( + socket, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to put socket in reuse_port mode", + errno); + } + } + + if (busyPollUs_ > 0) { +#ifdef SO_BUSY_POLL + // Set busy_poll time in microseconds on the socket. + // It sets how long socket will be in busy_poll mode when no event occurs. + int value = busyPollUs_; + if (netops::setsockopt( + socket, SOL_SOCKET, SO_BUSY_POLL, &value, sizeof(value)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to set SO_BUSY_POLL on the socket", + errno); + } +#else /* SO_BUSY_POLL is not supported*/ + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, "SO_BUSY_POLL is not supported", errno); +#endif + } + + if (rcvBuf_ > 0) { + // Set the size of the buffer for the received messages in rx_queues. + int value = rcvBuf_; + if (netops::setsockopt( + socket, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to set SO_RCVBUF on the socket", + errno); + } + } + + if (sndBuf_ > 0) { + // Set the size of the buffer for the sent messages in tx_queues. + int value = sndBuf_; + if (netops::setsockopt( + socket, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to set SO_SNDBUF on the socket", + errno); + } + } + + // If we're using IPv6, make sure we don't accept V4-mapped connections + if (address.getFamily() == AF_INET6) { + int flag = 1; + if (netops::setsockopt( + socket, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, "Failed to set IPV6_V6ONLY", errno); + } + } + + // bind to the address + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + auto& saddr = reinterpret_cast<sockaddr&>(addrStorage); + if (netops::bind(socket, &saddr, address.getActualSize()) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "failed to bind the async udp socket for:" + address.describe(), + errno); + } + + // success + g.dismiss(); + fd_ = socket; + ownership_ = FDOwnership::OWNS; + + // attach to EventHandler + EventHandler::changeHandlerFD(fd_); + + if (address.getFamily() == AF_UNIX || address.getPort() != 0) { + localAddress_ = address; + } else { + localAddress_.setFromLocalAddress(fd_); + } +} + +void AsyncUDPSocket::dontFragment(bool df) { + (void)df; // to avoid potential unused variable warning +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) && \ + defined(IP_PMTUDISC_WANT) + if (address().getFamily() == AF_INET) { + int v4 = df ? IP_PMTUDISC_DO : IP_PMTUDISC_WANT; + if (netops::setsockopt(fd_, IPPROTO_IP, IP_MTU_DISCOVER, &v4, sizeof(v4))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "Failed to set DF with IP_MTU_DISCOVER", + errno); + } + } +#endif +#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) && \ + defined(IPV6_PMTUDISC_WANT) + if (address().getFamily() == AF_INET6) { + int v6 = df ? IPV6_PMTUDISC_DO : IPV6_PMTUDISC_WANT; + if (netops::setsockopt( + fd_, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &v6, sizeof(v6))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "Failed to set DF with IPV6_MTU_DISCOVER", + errno); + } + } +#endif +} + +void AsyncUDPSocket::setDFAndTurnOffPMTU() { +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_PROBE) + if (address().getFamily() == AF_INET) { + int v4 = IP_PMTUDISC_PROBE; + if (folly::netops::setsockopt( + fd_, IPPROTO_IP, IP_MTU_DISCOVER, &v4, sizeof(v4))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "Failed to set PMTUDISC_PROBE with IP_MTU_DISCOVER", + errno); + } + } + +#endif +#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_PROBE) + if (address().getFamily() == AF_INET6) { + int v6 = IPV6_PMTUDISC_PROBE; + if (folly::netops::setsockopt( + fd_, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &v6, sizeof(v6))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, + "Failed to set PMTUDISC_PROBE with IPV6_MTU_DISCOVER", + errno); + } + } +#endif +} + +void AsyncUDPSocket::setErrMessageCallback( + ErrMessageCallback* errMessageCallback) { + errMessageCallback_ = errMessageCallback; + int err = (errMessageCallback_ != nullptr); +#if defined(IP_RECVERR) + if (address().getFamily() == AF_INET && + netops::setsockopt(fd_, IPPROTO_IP, IP_RECVERR, &err, sizeof(err))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, "Failed to set IP_RECVERR", errno); + } +#endif +#if defined(IPV6_RECVERR) + if (address().getFamily() == AF_INET6 && + netops::setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVERR, &err, sizeof(err))) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, "Failed to set IPV6_RECVERR", errno); + } +#endif + (void)err; +} + +void AsyncUDPSocket::setFD(NetworkSocket fd, FDOwnership ownership) { + CHECK_EQ(NetworkSocket(), fd_) << "Already bound to another FD"; + + fd_ = fd; + ownership_ = ownership; + + EventHandler::changeHandlerFD(fd_); + localAddress_.setFromLocalAddress(fd_); +} + +ssize_t AsyncUDPSocket::writeGSO( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>& buf, + int gso) { + // UDP's typical MTU size is 1500, so high number of buffers + // really do not make sense. Optimize for buffer chains with + // buffers less than 16, which is the highest I can think of + // for a real use case. + iovec vec[16]; + size_t iovec_len = buf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; + if (UNLIKELY(iovec_len == 0)) { + buf->coalesce(); + vec[0].iov_base = const_cast<uint8_t*>(buf->data()); + vec[0].iov_len = buf->length(); + iovec_len = 1; + } + + return writev(address, vec, iovec_len, gso); +} + +ssize_t AsyncUDPSocket::write( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>& buf) { + return writeGSO(address, buf, 0); +} + +ssize_t AsyncUDPSocket::writev( + const folly::SocketAddress& address, + const struct iovec* vec, + size_t iovec_len, + int gso) { + CHECK_NE(NetworkSocket(), fd_) << "Socket not yet bound"; + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + + struct msghdr msg; + if (!connected_) { + msg.msg_name = reinterpret_cast<void*>(&addrStorage); + msg.msg_namelen = address.getActualSize(); + } else { + if (connectedAddress_ != address) { + errno = ENOTSUP; + return -1; + } + msg.msg_name = nullptr; + msg.msg_namelen = 0; + } + msg.msg_iov = const_cast<struct iovec*>(vec); + msg.msg_iovlen = iovec_len; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + if (gso > 0) { + char control[CMSG_SPACE(sizeof(uint16_t))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + auto gso_len = static_cast<uint16_t>(gso); + memcpy(CMSG_DATA(cm), &gso_len, sizeof(gso_len)); + + return sendmsg(fd_, &msg, 0); + } +#else + CHECK_LT(gso, 1) << "GSO not supported"; +#endif + + return sendmsg(fd_, &msg, 0); +} + +ssize_t AsyncUDPSocket::writev( + const folly::SocketAddress& address, + const struct iovec* vec, + size_t iovec_len) { + return writev(address, vec, iovec_len, 0); +} + +/** + * Send the data in buffers to destination. Returns the return code from + * ::sendmmsg. + */ +int AsyncUDPSocket::writem( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count) { + return writemGSO(address, bufs, count, nullptr); +} + +int AsyncUDPSocket::writemGSO( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + const int* gso) { + int ret; + constexpr size_t kSmallSizeMax = 8; + char* gsoControl = nullptr; +#ifndef FOLLY_HAVE_MSG_ERRQUEUE + CHECK(!gso) << "GSO not supported"; +#endif + if (count <= kSmallSizeMax) { + // suppress "warning: variable length array 'vec' is used [-Wvla]" + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wvla") + mmsghdr vec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)]; +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + // we will allocate this on the stack anyway even if we do not use it + char control + [(BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)) * + (CMSG_SPACE(sizeof(uint16_t)))]; + + if (gso) { + gsoControl = control; + } +#endif + FOLLY_POP_WARNING + ret = writeImpl(address, bufs, count, vec, gso, gsoControl); + } else { + std::unique_ptr<mmsghdr[]> vec(new mmsghdr[count]); +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + std::unique_ptr<char[]> control( + gso ? (new char[count * (CMSG_SPACE(sizeof(uint16_t)))]) : nullptr); + if (gso) { + gsoControl = control.get(); + } +#endif + ret = writeImpl(address, bufs, count, vec.get(), gso, gsoControl); + } + + return ret; +} + +void AsyncUDPSocket::fillMsgVec( + sockaddr_storage* addr, + socklen_t addr_len, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + struct mmsghdr* msgvec, + struct iovec* iov, + size_t iov_count, + const int* gso, + char* gsoControl) { + size_t remaining = iov_count; + + size_t iov_pos = 0; + for (size_t i = 0; i < count; i++) { + // we can use remaining here to avoid calling countChainElements() again + auto ret = bufs[i]->fillIov(&iov[iov_pos], remaining); + size_t iovec_len = ret.numIovecs; + remaining -= iovec_len; + auto& msg = msgvec[i].msg_hdr; + msg.msg_name = reinterpret_cast<void*>(addr); + msg.msg_namelen = addr_len; + msg.msg_iov = &iov[iov_pos]; + msg.msg_iovlen = iovec_len; +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + if (gso && gso[i] > 0) { + msg.msg_control = &gsoControl[i * CMSG_SPACE(sizeof(uint16_t))]; + msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + + struct cmsghdr* cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + auto gso_len = static_cast<uint16_t>(gso[i]); + memcpy(CMSG_DATA(cm), &gso_len, sizeof(gso_len)); + } else { + msg.msg_control = nullptr; + msg.msg_controllen = 0; + } +#else + (void)gso; + (void)gsoControl; + msg.msg_control = nullptr; + msg.msg_controllen = 0; +#endif + msg.msg_flags = 0; + + msgvec[i].msg_len = 0; + + iov_pos += iovec_len; + } +} + +int AsyncUDPSocket::writeImpl( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + struct mmsghdr* msgvec, + const int* gso, + char* gsoControl) { + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + + size_t iov_count = 0; + for (size_t i = 0; i < count; i++) { + iov_count += bufs[i]->countChainElements(); + } + + int ret; + constexpr size_t kSmallSizeMax = 16; + if (iov_count <= kSmallSizeMax) { + // suppress "warning: variable length array 'vec' is used [-Wvla]" + FOLLY_PUSH_WARNING + FOLLY_GNU_DISABLE_WARNING("-Wvla") + iovec iov[BOOST_PP_IF(FOLLY_HAVE_VLA_01, iov_count, kSmallSizeMax)]; + FOLLY_POP_WARNING + fillMsgVec( + &addrStorage, + folly::to_narrow(address.getActualSize()), + bufs, + count, + msgvec, + iov, + iov_count, + gso, + gsoControl); + ret = sendmmsg(fd_, msgvec, count, 0); + } else { + std::unique_ptr<iovec[]> iov(new iovec[iov_count]); + fillMsgVec( + &addrStorage, + folly::to_narrow(address.getActualSize()), + bufs, + count, + msgvec, + iov.get(), + iov_count, + gso, + gsoControl); + ret = sendmmsg(fd_, msgvec, count, 0); + } + + return ret; +} + +ssize_t AsyncUDPSocket::recvmsg(struct msghdr* msg, int flags) { + return netops::recvmsg(fd_, msg, flags); +} + +int AsyncUDPSocket::recvmmsg( + struct mmsghdr* msgvec, + unsigned int vlen, + unsigned int flags, + struct timespec* timeout) { + return netops::recvmmsg(fd_, msgvec, vlen, flags, timeout); +} + +void AsyncUDPSocket::resumeRead(ReadCallback* cob) { + CHECK(!readCallback_) << "Another read callback already installed"; + CHECK_NE(NetworkSocket(), fd_) + << "UDP server socket not yet bind to an address"; + + readCallback_ = CHECK_NOTNULL(cob); + if (!updateRegistration()) { + AsyncSocketException ex( + AsyncSocketException::NOT_OPEN, "failed to register for accept events"); + + readCallback_ = nullptr; + cob->onReadError(ex); + return; + } +} + +void AsyncUDPSocket::pauseRead() { + // It is ok to pause an already paused socket + readCallback_ = nullptr; + updateRegistration(); +} + +void AsyncUDPSocket::close() { + eventBase_->dcheckIsInEventBaseThread(); + + if (readCallback_) { + auto cob = readCallback_; + readCallback_ = nullptr; + + cob->onReadClosed(); + } + + // Unregister any events we are registered for + unregisterHandler(); + + if (fd_ != NetworkSocket() && ownership_ == FDOwnership::OWNS) { + netops::close(fd_); + } + + fd_ = NetworkSocket(); +} + +void AsyncUDPSocket::handlerReady(uint16_t events) noexcept { + if (events & EventHandler::READ) { + DCHECK(readCallback_); + handleRead(); + } +} + +size_t AsyncUDPSocket::handleErrMessages() noexcept { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + if (errMessageCallback_ == nullptr) { + return 0; + } + uint8_t ctrl[1024]; + unsigned char data; + struct msghdr msg; + iovec entry; + + entry.iov_base = &data; + entry.iov_len = sizeof(data); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + msg.msg_flags = 0; + + int ret; + size_t num = 0; + while (fd_ != NetworkSocket()) { + ret = netops::recvmsg(fd_, &msg, MSG_ERRQUEUE); + VLOG(5) << "AsyncSocket::handleErrMessages(): recvmsg returned " << ret; + + if (ret < 0) { + if (errno != EAGAIN) { + auto errnoCopy = errno; + LOG(ERROR) << "::recvmsg exited with code " << ret + << ", errno: " << errnoCopy; + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, + "recvmsg() failed", + errnoCopy); + failErrMessageRead(ex); + } + return num; + } + + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg != nullptr && cmsg->cmsg_len != 0; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + ++num; + errMessageCallback_->errMessage(*cmsg); + if (fd_ == NetworkSocket()) { + // once the socket is closed there is no use for more read errors. + return num; + } + } + } + return num; +#else + return 0; +#endif +} + +void AsyncUDPSocket::failErrMessageRead(const AsyncSocketException& ex) { + if (errMessageCallback_ != nullptr) { + ErrMessageCallback* callback = errMessageCallback_; + errMessageCallback_ = nullptr; + callback->errMessageError(ex); + } +} + +int AsyncUDPSocket::connect(const folly::SocketAddress& address) { + CHECK_NE(NetworkSocket(), fd_) << "Socket not yet bound"; + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + int ret = netops::connect( + fd_, reinterpret_cast<sockaddr*>(&addrStorage), address.getActualSize()); + if (ret == 0) { + connected_ = true; + connectedAddress_ = address; + } + return ret; +} + +void AsyncUDPSocket::handleRead() noexcept { + void* buf{nullptr}; + size_t len{0}; + + if (handleErrMessages()) { + return; + } + + if (fd_ == NetworkSocket()) { + // The socket may have been closed by the error callbacks. + return; + } + if (readCallback_->shouldOnlyNotify()) { + return readCallback_->onNotifyDataAvailable(*this); + } + + readCallback_->getReadBuffer(&buf, &len); + if (buf == nullptr || len == 0) { + AsyncSocketException ex( + AsyncSocketException::BAD_ARGS, + "AsyncUDPSocket::getReadBuffer() returned empty buffer"); + + auto cob = readCallback_; + readCallback_ = nullptr; + + cob->onReadError(ex); + updateRegistration(); + return; + } + + struct sockaddr_storage addrStorage; + socklen_t addrLen = sizeof(addrStorage); + memset(&addrStorage, 0, size_t(addrLen)); + auto rawAddr = reinterpret_cast<sockaddr*>(&addrStorage); + rawAddr->sa_family = localAddress_.getFamily(); + + ssize_t bytesRead; + ReadCallback::OnDataAvailableParams params; + +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + if (gro_.has_value() && (gro_ != 0)) { + char control[CMSG_SPACE(sizeof(uint16_t))] = {}; + struct msghdr msg = {}; + struct iovec iov = {}; + struct cmsghdr* cmsg; + uint16_t* grosizeptr; + + iov.iov_base = buf; + iov.iov_len = len; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_name = rawAddr; + msg.msg_namelen = addrLen; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + bytesRead = netops::recvmsg(fd_, &msg, MSG_TRUNC); + + if (bytesRead >= 0) { + addrLen = msg.msg_namelen; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) { + grosizeptr = (uint16_t*)CMSG_DATA(cmsg); + params.gro_ = *grosizeptr; + break; + } + } + } + } else { + bytesRead = netops::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen); + } +#else + bytesRead = netops::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen); +#endif + if (bytesRead >= 0) { + clientAddress_.setFromSockaddr(rawAddr, addrLen); + + if (bytesRead > 0) { + bool truncated = false; + if ((size_t)bytesRead > len) { + truncated = true; + bytesRead = ssize_t(len); + } + + readCallback_->onDataAvailable( + clientAddress_, size_t(bytesRead), truncated, params); + } + } else { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No data could be read without blocking the socket + return; + } + + AsyncSocketException ex( + AsyncSocketException::INTERNAL_ERROR, "::recvfrom() failed", errno); + + // In case of UDP we can continue reading from the socket + // even if the current request fails. We notify the user + // so that he can do some logging/stats collection if he wants. + auto cob = readCallback_; + readCallback_ = nullptr; + + cob->onReadError(ex); + updateRegistration(); + } +} + +bool AsyncUDPSocket::updateRegistration() noexcept { + uint16_t flags = NONE; + + if (readCallback_) { + flags |= READ; + } + + return registerHandler(uint16_t(flags | PERSIST)); +} + +bool AsyncUDPSocket::setGSO(int val) { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + int ret = netops::setsockopt(fd_, SOL_UDP, UDP_SEGMENT, &val, sizeof(val)); + + gso_ = ret ? -1 : val; + + return !ret; +#else + (void)val; + return false; +#endif +} + +int AsyncUDPSocket::getGSO() { + // check if we can return the cached value + if (FOLLY_UNLIKELY(!gso_.has_value())) { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + int gso = -1; + socklen_t optlen = sizeof(gso); + if (!netops::getsockopt(fd_, SOL_UDP, UDP_SEGMENT, &gso, &optlen)) { + gso_ = gso; + } else { + gso_ = -1; + } +#else + gso_ = -1; +#endif + } + + return gso_.value(); +} + +bool AsyncUDPSocket::setGRO(bool bVal) { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + int val = bVal ? 1 : 0; + int ret = netops::setsockopt(fd_, SOL_UDP, UDP_GRO, &val, sizeof(val)); + + gro_ = ret ? -1 : val; + + return !ret; +#else + (void)bVal; + return false; +#endif +} + +int AsyncUDPSocket::getGRO() { + // check if we can return the cached value + if (FOLLY_UNLIKELY(!gro_.has_value())) { +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + int gro = -1; + socklen_t optlen = sizeof(gro); + if (!netops::getsockopt(fd_, SOL_UDP, UDP_GRO, &gro, &optlen)) { + gro_ = gro; + } else { + gro_ = -1; + } +#else + gro_ = -1; +#endif + } + + return gro_.value(); +} + +void AsyncUDPSocket::setTrafficClass(int tclass) { + if (netops::setsockopt( + fd_, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(int)) != 0) { + throw AsyncSocketException( + AsyncSocketException::NOT_OPEN, "Failed to set IPV6_TCLASS", errno); + } +} + +void AsyncUDPSocket::applyOptions( + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos) { + auto result = applySocketOptions(fd_, options, pos); + if (result != 0) { + throw AsyncSocketException( + AsyncSocketException::INTERNAL_ERROR, + "failed to set socket option", + result); + } +} + +void AsyncUDPSocket::detachEventBase() { + DCHECK(eventBase_ && eventBase_->isInEventBaseThread()); + registerHandler(uint16_t(NONE)); + eventBase_ = nullptr; + EventHandler::detachEventBase(); +} + +void AsyncUDPSocket::attachEventBase(folly::EventBase* evb) { + DCHECK(!eventBase_); + DCHECK(evb && evb->isInEventBaseThread()); + eventBase_ = evb; + EventHandler::attachEventBase(evb); + updateRegistration(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.h b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.h new file mode 100644 index 000000000..44748c5dd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.h @@ -0,0 +1,447 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/ScopeGuard.h> +#include <folly/SocketAddress.h> +#include <folly/io/IOBuf.h> +#include <folly/io/SocketOptionMap.h> +#include <folly/io/async/AsyncSocketBase.h> +#include <folly/io/async/AsyncSocketException.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/EventHandler.h> +#include <folly/net/NetOps.h> +#include <folly/net/NetworkSocket.h> + +namespace folly { + +/** + * UDP socket + */ +class AsyncUDPSocket : public EventHandler { + public: + enum class FDOwnership { OWNS, SHARED }; + + class ReadCallback { + public: + struct OnDataAvailableParams { + int gro_ = -1; + }; + /** + * Invoked when the socket becomes readable and we want buffer + * to write to. + * + * NOTE: From socket we will end up reading at most `len` bytes + * and if there were more bytes in datagram, we will end up + * dropping them. + */ + virtual void getReadBuffer(void** buf, size_t* len) noexcept = 0; + + /** + * Invoked when a new datagram is available on the socket. `len` + * is the number of bytes read and `truncated` is true if we had + * to drop few bytes because of running out of buffer space. + * OnDataAvailableParams::gro is the GRO segment size + */ + virtual void onDataAvailable( + const folly::SocketAddress& client, + size_t len, + bool truncated, + OnDataAvailableParams params) noexcept = 0; + + /** + * Notifies when data is available. This is only invoked when + * shouldNotifyOnly() returns true. + */ + virtual void onNotifyDataAvailable(AsyncUDPSocket&) noexcept {} + + /** + * Returns whether or not the read callback should only notify + * but not call getReadBuffer. + * If shouldNotifyOnly() returns true, AsyncUDPSocket will invoke + * onNotifyDataAvailable() instead of getReadBuffer(). + * If shouldNotifyOnly() returns false, AsyncUDPSocket will invoke + * getReadBuffer() and onDataAvailable(). + */ + virtual bool shouldOnlyNotify() { + return false; + } + + /** + * Invoked when there is an error reading from the socket. + * + * NOTE: Since UDP is connectionless, you can still read from the socket. + * But you have to re-register readCallback yourself after + * onReadError. + */ + virtual void onReadError(const AsyncSocketException& ex) noexcept = 0; + + /** + * Invoked when socket is closed and a read callback is registered. + */ + virtual void onReadClosed() noexcept = 0; + + virtual ~ReadCallback() = default; + }; + + class ErrMessageCallback { + public: + virtual ~ErrMessageCallback() = default; + + /** + * errMessage() will be invoked when kernel puts a message to + * the error queue associated with the socket. + * + * @param cmsg Reference to cmsghdr structure describing + * a message read from error queue associated + * with the socket. + */ + virtual void errMessage(const cmsghdr& cmsg) noexcept = 0; + + /** + * errMessageError() will be invoked if an error occurs reading a message + * from the socket error stream. + * + * @param ex An exception describing the error that occurred. + */ + virtual void errMessageError(const AsyncSocketException& ex) noexcept = 0; + }; + + /** + * Create a new UDP socket that will run in the + * given eventbase + */ + explicit AsyncUDPSocket(EventBase* evb); + ~AsyncUDPSocket() override; + + /** + * Returns the address server is listening on + */ + virtual const folly::SocketAddress& address() const { + CHECK_NE(NetworkSocket(), fd_) << "Server not yet bound to an address"; + return localAddress_; + } + + /** + * Bind the socket to the following address. If port is not + * set in the `address` an ephemeral port is chosen and you can + * use `address()` method above to get it after this method successfully + * returns. + */ + virtual void bind(const folly::SocketAddress& address); + + /** + * Use an already bound file descriptor. You can either transfer ownership + * of this FD by using ownership = FDOwnership::OWNS or share it using + * FDOwnership::SHARED. In case FD is shared, it will not be `close`d in + * destructor. + */ + virtual void setFD(NetworkSocket fd, FDOwnership ownership); + + /** + * Send the data in buffer to destination. Returns the return code from + * ::sendmsg. + */ + virtual ssize_t write( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>& buf); + + /** + * Send the data in buffers to destination. Returns the return code from + * ::sendmmsg. + * bufs is an array of std::unique_ptr<folly::IOBuf> + * of size num + */ + virtual int writem( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count); + + /** + * Send the data in buffer to destination. Returns the return code from + * ::sendmsg. + * gso is the generic segmentation offload value + * writeGSO will return -1 if + * buf->computeChainDataLength() <= gso + * Before calling writeGSO with a positive value + * verify GSO is supported on this platform by calling getGSO + */ + virtual ssize_t writeGSO( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>& buf, + int gso); + + /** + * Send the data in buffers to destination. Returns the return code from + * ::sendmmsg. + * bufs is an array of std::unique_ptr<folly::IOBuf> + * of size num + * gso is an array with the generic segmentation offload values or nullptr + * Before calling writeGSO with a positive value + * verify GSO is supported on this platform by calling getGSO + */ + virtual int writemGSO( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + const int* gso); + + /** + * Send data in iovec to destination. Returns the return code from sendmsg. + */ + virtual ssize_t writev( + const folly::SocketAddress& address, + const struct iovec* vec, + size_t iovec_len, + int gso); + + virtual ssize_t writev( + const folly::SocketAddress& address, + const struct iovec* vec, + size_t iovec_len); + + virtual ssize_t recvmsg(struct msghdr* msg, int flags); + + virtual int recvmmsg( + struct mmsghdr* msgvec, + unsigned int vlen, + unsigned int flags, + struct timespec* timeout); + + /** + * Start reading datagrams + */ + virtual void resumeRead(ReadCallback* cob); + + /** + * Pause reading datagrams + */ + virtual void pauseRead(); + + /** + * Stop listening on the socket. + */ + virtual void close(); + + /** + * Get internal FD used by this socket + */ + virtual NetworkSocket getNetworkSocket() const { + CHECK_NE(NetworkSocket(), fd_) << "Need to bind before getting FD out"; + return fd_; + } + + /** + * Set reuse port mode to call bind() on the same address multiple times + */ + virtual void setReusePort(bool reusePort) { + reusePort_ = reusePort; + } + + /** + * Set SO_REUSEADDR flag on the socket. Default is OFF. + */ + virtual void setReuseAddr(bool reuseAddr) { + reuseAddr_ = reuseAddr; + } + + /** + * Set SO_RCVBUF option on the socket, if not zero. Default is zero. + */ + virtual void setRcvBuf(int rcvBuf) { + rcvBuf_ = rcvBuf; + } + + /** + * Set SO_SNDBUG option on the socket, if not zero. Default is zero. + */ + virtual void setSndBuf(int sndBuf) { + sndBuf_ = sndBuf; + } + + /** + * Set SO_BUSY_POLL option on the socket, if not zero. Default is zero. + * Caution! The feature is not available on Apple's systems. + */ + virtual void setBusyPoll(int busyPollUs) { + busyPollUs_ = busyPollUs; + } + + EventBase* getEventBase() const { + return eventBase_; + } + + /** + * Enable or disable fragmentation on the socket. + * + * On Linux, this sets IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_DO when enabled, + * and to IP(V6)_PMTUDISC_WANT when disabled. IP(V6)_PMTUDISC_WANT will use + * per-route setting to set DF bit. It may be more desirable to use + * IP(V6)_PMTUDISC_PROBE as opposed to IP(V6)_PMTUDISC_DO for apps that has + * its own PMTU Discovery mechanism. + * Note this doesn't work on Apple. + */ + virtual void dontFragment(bool df); + + /** + * Set Dont-Fragment (DF) but ignore Path MTU. + * + * On Linux, this sets IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE. + * This essentially sets DF but ignores Path MTU for this socket. + * This may be desirable for apps that has its own PMTU Discovery mechanism. + * See http://man7.org/linux/man-pages/man7/ip.7.html for more info. + */ + virtual void setDFAndTurnOffPMTU(); + + /** + * Callback for receiving errors on the UDP sockets + */ + virtual void setErrMessageCallback(ErrMessageCallback* errMessageCallback); + + /** + * Connects the UDP socket to a remote destination address provided in + * address. This can speed up UDP writes on linux because it will cache flow + * state on connects. + * Using connect has many quirks, and you should be aware of them before using + * this API: + * 1. This must only be called after binding the socket. + * 2. Normally UDP can use the 2 tuple (src ip, src port) to steer packets + * sent by the peer to the socket, however after connecting the socket, only + * packets destined to the destination address specified in connect() will be + * forwarded and others will be dropped. If the server can send a packet + * from a different destination port / IP then you probably do not want to use + * this API. + * 3. It can be called repeatedly on either the client or server however it's + * normally only useful on the client and not server. + * + * Returns the result of calling the connect syscall. + */ + virtual int connect(const folly::SocketAddress& address); + + virtual bool isBound() const { + return fd_ != NetworkSocket(); + } + + virtual bool isReading() const { + return readCallback_ != nullptr; + } + + virtual void detachEventBase(); + + virtual void attachEventBase(folly::EventBase* evb); + + // generic segmentation offload get/set + // negative return value means GSO is not available + int getGSO(); + + bool setGSO(int val); + + // generic receive offload get/set + // negative return value means GRO is not available + int getGRO(); + + bool setGRO(bool bVal); + + void setTrafficClass(int tclass); + + void applyOptions( + const SocketOptionMap& options, + SocketOptionKey::ApplyPos pos); + + protected: + virtual ssize_t + sendmsg(NetworkSocket socket, const struct msghdr* message, int flags) { + return netops::sendmsg(socket, message, flags); + } + + virtual int sendmmsg( + NetworkSocket socket, + struct mmsghdr* msgvec, + unsigned int vlen, + int flags) { + return netops::sendmmsg(socket, msgvec, vlen, flags); + } + + void fillMsgVec( + sockaddr_storage* addr, + socklen_t addr_len, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + struct mmsghdr* msgvec, + struct iovec* iov, + size_t iov_count, + const int* gso, + char* gsoControl); + + virtual int writeImpl( + const folly::SocketAddress& address, + const std::unique_ptr<folly::IOBuf>* bufs, + size_t count, + struct mmsghdr* msgvec, + const int* gso, + char* gsoControl); + + size_t handleErrMessages() noexcept; + + void failErrMessageRead(const AsyncSocketException& ex); + + // Non-null only when we are reading + ReadCallback* readCallback_; + + private: + AsyncUDPSocket(const AsyncUDPSocket&) = delete; + AsyncUDPSocket& operator=(const AsyncUDPSocket&) = delete; + + // EventHandler + void handlerReady(uint16_t events) noexcept override; + + void handleRead() noexcept; + bool updateRegistration() noexcept; + + EventBase* eventBase_; + folly::SocketAddress localAddress_; + + NetworkSocket fd_; + FDOwnership ownership_; + + // Temp space to receive client address + folly::SocketAddress clientAddress_; + + // If the socket is connected. + folly::SocketAddress connectedAddress_; + bool connected_{false}; + + bool reuseAddr_{false}; + bool reusePort_{false}; + int rcvBuf_{0}; + int sndBuf_{0}; + int busyPollUs_{0}; + + // generic segmentation offload value, if available + // See https://lwn.net/Articles/188489/ for more details + folly::Optional<int> gso_; + + // generic receive offload value, if available + // See https://lwn.net/Articles/770978/ for more details + folly::Optional<int> gro_; + + ErrMessageCallback* errMessageCallback_{nullptr}; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h b/ios/Pods/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h new file mode 100644 index 000000000..fa802283d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/AsyncTransport.h> + +namespace folly { + +/** + * Convenience class so that AsyncTransportWrapper can be decorated without + * having to redefine every single method. + */ +template <class T> +class DecoratedAsyncTransportWrapper : public folly::AsyncTransportWrapper { + public: + explicit DecoratedAsyncTransportWrapper(typename T::UniquePtr transport) + : transport_(std::move(transport)) {} + + const AsyncTransportWrapper* getWrappedTransport() const override { + return transport_.get(); + } + + // folly::AsyncTransportWrapper + ReadCallback* getReadCallback() const override { + return transport_->getReadCallback(); + } + + void setReadCB( + folly::AsyncTransportWrapper::ReadCallback* callback) override { + transport_->setReadCB(callback); + } + + void write( + folly::AsyncTransportWrapper::WriteCallback* callback, + const void* buf, + size_t bytes, + folly::WriteFlags flags = folly::WriteFlags::NONE) override { + transport_->write(callback, buf, bytes, flags); + } + + void writeChain( + folly::AsyncTransportWrapper::WriteCallback* callback, + std::unique_ptr<folly::IOBuf>&& buf, + folly::WriteFlags flags = folly::WriteFlags::NONE) override { + transport_->writeChain(callback, std::move(buf), flags); + } + + void writev( + folly::AsyncTransportWrapper::WriteCallback* callback, + const iovec* vec, + size_t bytes, + folly::WriteFlags flags = folly::WriteFlags::NONE) override { + transport_->writev(callback, vec, bytes, flags); + } + + // folly::AsyncSocketBase + folly::EventBase* getEventBase() const override { + return transport_->getEventBase(); + } + + // folly::AsyncTransport + void attachEventBase(folly::EventBase* eventBase) override { + transport_->attachEventBase(eventBase); + } + + void close() override { + transport_->close(); + } + + void closeNow() override { + transport_->closeNow(); + } + + void closeWithReset() override { + transport_->closeWithReset(); + + // This will likely result in 2 closeNow() calls on the decorated transport, + // but otherwise it is very easy to miss the derived class's closeNow(). + closeNow(); + } + + bool connecting() const override { + return transport_->connecting(); + } + + void detachEventBase() override { + transport_->detachEventBase(); + } + + bool error() const override { + return transport_->error(); + } + + size_t getAppBytesReceived() const override { + return transport_->getAppBytesReceived(); + } + + size_t getAppBytesWritten() const override { + return transport_->getAppBytesWritten(); + } + + void getLocalAddress(folly::SocketAddress* address) const override { + return transport_->getLocalAddress(address); + } + + void getPeerAddress(folly::SocketAddress* address) const override { + return transport_->getPeerAddress(address); + } + + size_t getRawBytesReceived() const override { + return transport_->getRawBytesReceived(); + } + + size_t getRawBytesWritten() const override { + return transport_->getRawBytesWritten(); + } + + uint32_t getSendTimeout() const override { + return transport_->getSendTimeout(); + } + + bool good() const override { + return transport_->good(); + } + + bool isDetachable() const override { + return transport_->isDetachable(); + } + + bool isEorTrackingEnabled() const override { + return transport_->isEorTrackingEnabled(); + } + + bool readable() const override { + return transport_->readable(); + } + + bool writable() const override { + return transport_->writable(); + } + + void setEorTracking(bool track) override { + return transport_->setEorTracking(track); + } + + void setSendTimeout(uint32_t timeoutInMs) override { + transport_->setSendTimeout(timeoutInMs); + } + + void shutdownWrite() override { + transport_->shutdownWrite(); + } + + void shutdownWriteNow() override { + transport_->shutdownWriteNow(); + } + + std::string getApplicationProtocol() const noexcept override { + return transport_->getApplicationProtocol(); + } + + std::string getSecurityProtocol() const override { + return transport_->getSecurityProtocol(); + } + + bool isReplaySafe() const override { + return transport_->isReplaySafe(); + } + + void setReplaySafetyCallback( + folly::AsyncTransport::ReplaySafetyCallback* callback) override { + transport_->setReplaySafetyCallback(callback); + } + + const AsyncTransportCertificate* getPeerCertificate() const override { + return transport_->getPeerCertificate(); + } + + const AsyncTransportCertificate* getSelfCertificate() const override { + return transport_->getSelfCertificate(); + } + + protected: + ~DecoratedAsyncTransportWrapper() override {} + + typename T::UniquePtr transport_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestruction.h b/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestruction.h new file mode 100644 index 000000000..3f7feba8d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestruction.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/DelayedDestructionBase.h> + +namespace folly { + +/** + * DelayedDestruction is a helper class to ensure objects are not deleted + * while they still have functions executing in a higher stack frame. + * + * This is useful for objects that invoke callback functions, to ensure that a + * callback does not destroy the calling object. + * + * Classes needing this functionality should: + * - derive from DelayedDestruction + * - make their destructor private or protected, so it cannot be called + * directly + * - create a DestructorGuard object on the stack in each public method that + * may invoke a callback + * + * DelayedDestruction does not perform any locking. It is intended to be used + * only from a single thread. + */ +class DelayedDestruction : public DelayedDestructionBase { + public: + /** + * destroy() requests destruction of the object. + * + * This method will destroy the object after it has no more functions running + * higher up on the stack. (i.e., No more DestructorGuard objects exist for + * this object.) This method must be used instead of the destructor. + */ + virtual void destroy() { + // If guardCount_ is not 0, just set destroyPending_ to delay + // actual destruction. + if (getDestructorGuardCount() != 0) { + destroyPending_ = true; + } else { + onDelayedDestroy(false); + } + } + + /** + * Helper class to allow DelayedDestruction classes to be used with + * std::shared_ptr. + * + * This class can be specified as the destructor argument when creating the + * shared_ptr, and it will destroy the guarded class properly when all + * shared_ptr references are released. + */ + class Destructor { + public: + void operator()(DelayedDestruction* dd) const { + dd->destroy(); + } + }; + + bool getDestroyPending() const { + return destroyPending_; + } + + protected: + /** + * Protected destructor. + * + * Making this protected ensures that users cannot delete DelayedDestruction + * objects directly, and that everyone must use destroy() instead. + * Subclasses of DelayedDestruction must also define their destructors as + * protected or private in order for this to work. + * + * This also means that DelayedDestruction objects cannot be created + * directly on the stack; they must always be dynamically allocated on the + * heap. + * + * In order to use a DelayedDestruction object with a shared_ptr, create the + * shared_ptr using a DelayedDestruction::Destructor as the second argument + * to the shared_ptr constructor. + */ + ~DelayedDestruction() override = default; + + DelayedDestruction() : destroyPending_(false) {} + + private: + /** + * destroyPending_ is set to true if destoy() is called while guardCount_ is + * non-zero. It is set to false before the object is deleted. + * + * If destroyPending_ is true, the object will be destroyed the next time + * guardCount_ drops to 0. + */ + bool destroyPending_; + + void onDelayedDestroy(bool delayed) override { + // check if it is ok to destroy now + if (delayed && !destroyPending_) { + return; + } + destroyPending_ = false; + delete this; + } +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestructionBase.h b/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestructionBase.h new file mode 100644 index 000000000..0eb9149f3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/DelayedDestructionBase.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <assert.h> +#include <folly/Portability.h> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <type_traits> +#include <utility> + +namespace folly { + +/** + * DelayedDestructionBase is a helper class to ensure objects are not deleted + * while they still have functions executing in a higher stack frame. + * + * This is useful for objects that invoke callback functions, to ensure that a + * callback does not destroy the calling object. + * + * Classes needing this functionality should: + * - derive from DelayedDestructionBase directly + * - implement onDelayedDestroy which'll be called before the object is + * going to be destructed + * - create a DestructorGuard object on the stack in each public method that + * may invoke a callback + * + * DelayedDestructionBase does not perform any locking. It is intended to be + * used only from a single thread. + */ +class DelayedDestructionBase { + public: + DelayedDestructionBase(const DelayedDestructionBase&) = delete; + DelayedDestructionBase& operator=(const DelayedDestructionBase&) = delete; + + virtual ~DelayedDestructionBase() = default; + + /** + * Classes should create a DestructorGuard object on the stack in any + * function that may invoke callback functions. + * + * The DestructorGuard prevents the guarded class from being destroyed while + * it exists. Without this, the callback function could delete the guarded + * object, causing problems when the callback function returns and the + * guarded object's method resumes execution. + */ + class FOLLY_NODISCARD DestructorGuard { + public: + explicit DestructorGuard(DelayedDestructionBase* dd) : dd_(dd) { + if (dd_ != nullptr) { + ++dd_->guardCount_; + assert(dd_->guardCount_ > 0); // check for wrapping + } + } + + DestructorGuard(const DestructorGuard& dg) : DestructorGuard(dg.dd_) {} + + DestructorGuard(DestructorGuard&& dg) noexcept + : dd_(std::exchange(dg.dd_, nullptr)) {} + + DestructorGuard& operator=(DestructorGuard dg) noexcept { + std::swap(dd_, dg.dd_); + return *this; + } + + DestructorGuard& operator=(DelayedDestructionBase* dd) { + *this = DestructorGuard(dd); + return *this; + } + + ~DestructorGuard() { + if (dd_ != nullptr) { + assert(dd_->guardCount_ > 0); + --dd_->guardCount_; + if (dd_->guardCount_ == 0) { + dd_->onDelayedDestroy(true); + } + } + } + + DelayedDestructionBase* get() const { + return dd_; + } + + explicit operator bool() const { + return dd_ != nullptr; + } + + private: + DelayedDestructionBase* dd_; + }; + + /** + * This smart pointer is a convenient way to manage a concrete + * DelayedDestructorBase child. It can replace the equivalent raw pointer and + * provide automatic memory management. + */ + template <typename AliasType> + class IntrusivePtr : private DestructorGuard { + template <typename CopyAliasType> + friend class IntrusivePtr; + + public: + template <typename... Args> + static IntrusivePtr<AliasType> make(Args&&... args) { + return {new AliasType(std::forward<Args>(args)...)}; + } + + IntrusivePtr() = default; + IntrusivePtr(const IntrusivePtr&) = default; + IntrusivePtr(IntrusivePtr&&) noexcept = default; + + template < + typename CopyAliasType, + typename = typename std::enable_if< + std::is_convertible<CopyAliasType*, AliasType*>::value>::type> + IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy) + : DestructorGuard(copy) {} + + template < + typename CopyAliasType, + typename = typename std::enable_if< + std::is_convertible<CopyAliasType*, AliasType*>::value>::type> + IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy) + : DestructorGuard(std::move(copy)) {} + + explicit IntrusivePtr(AliasType* dd) : DestructorGuard(dd) {} + + // Copying from a unique_ptr is safe because if the upcast to + // DelayedDestructionBase works, then the instance is already using + // intrusive ref-counting. + template < + typename CopyAliasType, + typename Deleter, + typename = typename std::enable_if< + std::is_convertible<CopyAliasType*, AliasType*>::value>::type> + explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy) + : DestructorGuard(copy.get()) {} + + IntrusivePtr& operator=(const IntrusivePtr&) = default; + IntrusivePtr& operator=(IntrusivePtr&&) noexcept = default; + + template < + typename CopyAliasType, + typename = typename std::enable_if< + std::is_convertible<CopyAliasType*, AliasType*>::value>::type> + IntrusivePtr& operator=(IntrusivePtr<CopyAliasType> copy) noexcept { + DestructorGuard::operator=(copy); + return *this; + } + + IntrusivePtr& operator=(AliasType* dd) { + DestructorGuard::operator=(dd); + return *this; + } + + void reset(AliasType* dd = nullptr) { + *this = dd; + } + + AliasType* get() const { + return static_cast<AliasType*>(DestructorGuard::get()); + } + + AliasType& operator*() const { + return *get(); + } + + AliasType* operator->() const { + return get(); + } + + explicit operator bool() const { + return DestructorGuard::operator bool(); + } + }; + + protected: + DelayedDestructionBase() : guardCount_(0) {} + + /** + * Get the number of DestructorGuards currently protecting this object. + * + * This is primarily intended for debugging purposes, such as asserting + * that an object has at least 1 guard. + */ + uint32_t getDestructorGuardCount() const { + return guardCount_; + } + + /** + * Implement onDelayedDestroy in subclasses. + * onDelayedDestroy() is invoked when the object is potentially being + * destroyed. + * + * @param delayed This parameter is true if destruction was delayed because + * of a DestructorGuard object, or false if onDelayedDestroy() + * is being called directly from the destructor. + */ + virtual void onDelayedDestroy(bool delayed) = 0; + + private: + /** + * guardCount_ is incremented by DestructorGuard, to indicate that one of + * the DelayedDestructionBase object's methods is currently running. + * + * If the destructor is called while guardCount_ is non-zero, destruction + * will be delayed until guardCount_ drops to 0. This allows + * DelayedDestructionBase objects to invoke callbacks without having to worry + * about being deleted before the callback returns. + */ + uint32_t guardCount_; +}; + +inline bool operator==( + const DelayedDestructionBase::DestructorGuard& left, + const DelayedDestructionBase::DestructorGuard& right) { + return left.get() == right.get(); +} +inline bool operator!=( + const DelayedDestructionBase::DestructorGuard& left, + const DelayedDestructionBase::DestructorGuard& right) { + return left.get() != right.get(); +} +inline bool operator==( + const DelayedDestructionBase::DestructorGuard& left, + std::nullptr_t) { + return left.get() == nullptr; +} +inline bool operator==( + std::nullptr_t, + const DelayedDestructionBase::DestructorGuard& right) { + return nullptr == right.get(); +} +inline bool operator!=( + const DelayedDestructionBase::DestructorGuard& left, + std::nullptr_t) { + return left.get() != nullptr; +} +inline bool operator!=( + std::nullptr_t, + const DelayedDestructionBase::DestructorGuard& right) { + return nullptr != right.get(); +} + +template <typename LeftAliasType, typename RightAliasType> +inline bool operator==( + const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left, + const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) { + return left.get() == right.get(); +} +template <typename LeftAliasType, typename RightAliasType> +inline bool operator!=( + const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left, + const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) { + return left.get() != right.get(); +} +template <typename LeftAliasType> +inline bool operator==( + const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left, + std::nullptr_t) { + return left.get() == nullptr; +} +template <typename RightAliasType> +inline bool operator==( + std::nullptr_t, + const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) { + return nullptr == right.get(); +} +template <typename LeftAliasType> +inline bool operator!=( + const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left, + std::nullptr_t) { + return left.get() != nullptr; +} +template <typename RightAliasType> +inline bool operator!=( + std::nullptr_t, + const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) { + return nullptr != right.get(); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/DestructorCheck.h b/ios/Pods/Flipper-Folly/folly/io/async/DestructorCheck.h new file mode 100644 index 000000000..c0a05b51c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/DestructorCheck.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +/** + * DestructorCheck is a helper class that helps to detect if a tracked object + * was deleted. + * This is useful for objects that request callbacks from other components. + * + * Classes needing this functionality should: + * - derive from DestructorCheck + * + * Callback context can be extended with an instance of DestructorCheck::Safety + * object initialized with a reference to the object dereferenced from the + * callback. Once the callback is invoked, it can use this safety object to + * check if the object was not deallocated yet before dereferencing it. + * + * DestructorCheck does not perform any locking. It is intended to be used + * only from a single thread. + * + * Example: + * + * class AsyncFoo : public DestructorCheck { + * public: + * ~AsyncFoo(); + * // awesome async code with circuitous deletion paths + * void async1(); + * void async2(); + * }; + * + * righteousFunc(AsyncFoo& f) { + * DestructorCheck::Safety safety(f); + * + * f.async1(); // might have deleted f, oh noes + * if (!safety.destroyed()) { + * // phew, still there + * f.async2(); + * } + * } + */ + +class DestructorCheck { + public: + virtual ~DestructorCheck() { + rootGuard_.setAllDestroyed(); + } + + class Safety; + + class ForwardLink { + // These methods are mostly private because an outside caller could violate + // the integrity of the linked list. + private: + void setAllDestroyed() { + for (auto guard = next_; guard; guard = guard->next_) { + guard->setDestroyed(); + } + } + + // This is used to maintain the double-linked list. An intrusive list does + // not require any heap allocations, like a standard container would. This + // isolation of next_ in its own class means that the DestructorCheck can + // easily hold a next_ pointer without needing to hold a prev_ pointer. + // DestructorCheck never needs a prev_ pointer because it is the head node + // and this is a special list where the head never moves and never has a + // previous node. + Safety* next_{nullptr}; + + friend class DestructorCheck; + friend class Safety; + }; + + // See above example for usage + class Safety : public ForwardLink { + public: + explicit Safety(DestructorCheck& destructorCheck) { + // Insert this node at the head of the list. + prev_ = &destructorCheck.rootGuard_; + next_ = prev_->next_; + if (next_ != nullptr) { + next_->prev_ = this; + } + prev_->next_ = this; + } + + ~Safety() { + if (!destroyed()) { + // Remove this node from the list. + prev_->next_ = next_; + if (next_ != nullptr) { + next_->prev_ = prev_; + } + } + } + + Safety(const Safety&) = delete; + Safety(Safety&& goner) = delete; + Safety& operator=(const Safety&) = delete; + Safety& operator=(Safety&&) = delete; + + bool destroyed() const { + return prev_ == nullptr; + } + + private: + void setDestroyed() { + prev_ = nullptr; + } + + // This field is used to maintain the double-linked list. If the root has + // been destroyed then the field is set to the nullptr sentinel value. + ForwardLink* prev_; + + friend class ForwardLink; + }; + + private: + ForwardLink rootGuard_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBase.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventBase.cpp new file mode 100644 index 000000000..0eb9678db --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBase.cpp @@ -0,0 +1,908 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include <folly/io/async/EventBase.h> + +#include <fcntl.h> + +#include <memory> +#include <mutex> +#include <thread> + +#include <folly/Memory.h> +#include <folly/String.h> +#include <folly/io/async/EventBaseBackendBase.h> +#include <folly/io/async/NotificationQueue.h> +#include <folly/io/async/VirtualEventBase.h> +#include <folly/portability/Unistd.h> +#include <folly/synchronization/Baton.h> +#include <folly/system/ThreadName.h> + +namespace { +constexpr folly::StringPiece executorName = "EventBase"; + +class EventBaseBackend : public folly::EventBaseBackendBase { + public: + EventBaseBackend(); + explicit EventBaseBackend(event_base* evb); + ~EventBaseBackend() override; + + event_base* getEventBase() override { + return evb_; + } + + int eb_event_base_loop(int flags) override; + int eb_event_base_loopbreak() override; + + int eb_event_add(Event& event, const struct timeval* timeout) override; + int eb_event_del(EventBaseBackendBase::Event& event) override; + + private: + event_base* evb_; +}; + +// The interface used to libevent is not thread-safe. Calls to +// event_init() and event_base_free() directly modify an internal +// global 'current_base', so a mutex is required to protect this. +// +// event_init() should only ever be called once. Subsequent calls +// should be made to event_base_new(). We can recognise that +// event_init() has already been called by simply inspecting current_base. +std::mutex libevent_mutex_; + +EventBaseBackend::EventBaseBackend() { + struct event ev; + { + std::lock_guard<std::mutex> lock(libevent_mutex_); + + // The value 'current_base' (libevent 1) or + // 'event_global_current_base_' (libevent 2) is filled in by event_set(), + // allowing examination of its value without an explicit reference here. + // If ev.ev_base is nullptr, then event_init() must be called, otherwise + // call event_base_new(). + ::event_set(&ev, 0, 0, nullptr, nullptr); + if (!ev.ev_base) { + evb_ = event_init(); + } + } + + if (ev.ev_base) { + evb_ = ::event_base_new(); + } + + if (UNLIKELY(evb_ == nullptr)) { + LOG(ERROR) << "EventBase(): Failed to init event base."; + folly::throwSystemError("error in EventBaseBackend::EventBaseBackend()"); + } +} + +EventBaseBackend::EventBaseBackend(event_base* evb) : evb_(evb) { + if (UNLIKELY(evb_ == nullptr)) { + LOG(ERROR) << "EventBase(): Pass nullptr as event base."; + throw std::invalid_argument("EventBase(): event base cannot be nullptr"); + } +} + +int EventBaseBackend::eb_event_base_loop(int flags) { + return event_base_loop(evb_, flags); +} + +int EventBaseBackend::eb_event_base_loopbreak() { + return event_base_loopbreak(evb_); +} + +int EventBaseBackend::eb_event_add( + Event& event, + const struct timeval* timeout) { + return event_add(event.getEvent(), timeout); +} + +int EventBaseBackend::eb_event_del(EventBaseBackendBase::Event& event) { + return event_del(event.getEvent()); +} + +EventBaseBackend::~EventBaseBackend() { + std::lock_guard<std::mutex> lock(libevent_mutex_); + event_base_free(evb_); +} + +} // namespace + +namespace folly { +/* + * EventBase::FunctionRunner + */ + +class EventBase::FunctionRunner + : public NotificationQueue<EventBase::Func>::Consumer { + public: + void messageAvailable(Func&& msg) noexcept override { + msg(); + } +}; + +/* + * EventBase methods + */ + +EventBase::EventBase(bool enableTimeMeasurement) + : runOnceCallbacks_(nullptr), + stop_(false), + loopThread_(), + queue_(nullptr), + fnRunner_(nullptr), + maxLatency_(0), + avgLoopTime_(std::chrono::seconds(2)), + maxLatencyLoopTime_(avgLoopTime_), + enableTimeMeasurement_(enableTimeMeasurement), + nextLoopCnt_( + std::size_t(-40)) // Early wrap-around so bugs will manifest soon + , + latestLoopCnt_(nextLoopCnt_), + startWork_(), + observer_(nullptr), + observerSampleCount_(0), + executionObserver_(nullptr) { + evb_ = getDefaultBackend(); + + VLOG(5) << "EventBase(): Created."; + initNotificationQueue(); +} + +// takes ownership of the event_base +EventBase::EventBase(event_base* evb, bool enableTimeMeasurement) + : EventBase( + std::make_unique<EventBaseBackend>(evb), + enableTimeMeasurement) {} + +// takes ownership of the backend +EventBase::EventBase( + std::unique_ptr<EventBaseBackendBase>&& evb, + bool enableTimeMeasurement) + : runOnceCallbacks_(nullptr), + stop_(false), + loopThread_(), + queue_(nullptr), + fnRunner_(nullptr), + maxLatency_(0), + avgLoopTime_(std::chrono::seconds(2)), + maxLatencyLoopTime_(avgLoopTime_), + enableTimeMeasurement_(enableTimeMeasurement), + nextLoopCnt_( + std::size_t(-40)) // Early wrap-around so bugs will manifest soon + , + latestLoopCnt_(nextLoopCnt_), + startWork_(), + observer_(nullptr), + observerSampleCount_(0), + executionObserver_(nullptr) { + evb_ = evb ? std::move(evb) : getDefaultBackend(); + initNotificationQueue(); +} + +EventBase::~EventBase() { + std::future<void> virtualEventBaseDestroyFuture; + if (virtualEventBase_) { + virtualEventBaseDestroyFuture = virtualEventBase_->destroy(); + } + + // Keep looping until all keep-alive handles are released. Each keep-alive + // handle signals that some external code will still schedule some work on + // this EventBase (so it's not safe to destroy it). + while (loopKeepAliveCount() > 0) { + applyLoopKeepAlive(); + loopOnce(); + } + + if (virtualEventBaseDestroyFuture.valid()) { + virtualEventBaseDestroyFuture.get(); + } + + // Call all destruction callbacks, before we start cleaning up our state. + while (!onDestructionCallbacks_.rlock()->empty()) { + OnDestructionCallback::List callbacks; + onDestructionCallbacks_.swap(callbacks); + while (!callbacks.empty()) { + auto& callback = callbacks.front(); + callbacks.pop_front(); + callback.runCallback(); + } + } + + clearCobTimeouts(); + + DCHECK_EQ(0u, runBeforeLoopCallbacks_.size()); + + (void)runLoopCallbacks(); + + if (!fnRunner_->consumeUntilDrained()) { + LOG(ERROR) << "~EventBase(): Unable to drain notification queue"; + } + + // Stop consumer before deleting NotificationQueue + fnRunner_->stopConsuming(); + evb_.reset(); + + for (auto storage : localStorageToDtor_) { + storage->onEventBaseDestruction(*this); + } + + VLOG(5) << "EventBase(): Destroyed."; +} + +std::unique_ptr<EventBaseBackendBase> EventBase::getDefaultBackend() { + return std::make_unique<EventBaseBackend>(); +} + +size_t EventBase::getNotificationQueueSize() const { + return queue_->size(); +} + +void EventBase::setMaxReadAtOnce(uint32_t maxAtOnce) { + fnRunner_->setMaxReadAtOnce(maxAtOnce); +} + +void EventBase::checkIsInEventBaseThread() const { + auto evbTid = loopThread_.load(std::memory_order_relaxed); + if (evbTid == std::thread::id()) { + return; + } + + // Using getThreadName(evbTid) instead of name_ will work also if + // the thread name is set outside of EventBase (and name_ is empty). + auto curTid = std::this_thread::get_id(); + CHECK_EQ(evbTid, curTid) + << "This logic must be executed in the event base thread. " + << "Event base thread name: \"" + << folly::getThreadName(evbTid).value_or("") + << "\", current thread name: \"" + << folly::getThreadName(curTid).value_or("") << "\""; +} + +// Set smoothing coefficient for loop load average; input is # of milliseconds +// for exp(-1) decay. +void EventBase::setLoadAvgMsec(std::chrono::milliseconds ms) { + assert(enableTimeMeasurement_); + std::chrono::microseconds us = std::chrono::milliseconds(ms); + if (ms > std::chrono::milliseconds::zero()) { + maxLatencyLoopTime_.setTimeInterval(us); + avgLoopTime_.setTimeInterval(us); + } else { + LOG(ERROR) << "non-positive arg to setLoadAvgMsec()"; + } +} + +void EventBase::resetLoadAvg(double value) { + assert(enableTimeMeasurement_); + avgLoopTime_.reset(value); + maxLatencyLoopTime_.reset(value); +} + +static std::chrono::milliseconds getTimeDelta( + std::chrono::steady_clock::time_point* prev) { + auto result = std::chrono::steady_clock::now() - *prev; + *prev = std::chrono::steady_clock::now(); + + return std::chrono::duration_cast<std::chrono::milliseconds>(result); +} + +void EventBase::waitUntilRunning() { + while (!isRunning()) { + std::this_thread::yield(); + } +} + +// enters the event_base loop -- will only exit when forced to +bool EventBase::loop() { + auto guard = folly::makeBlockingDisallowedGuard(executorName); + return loopBody(); +} + +bool EventBase::loopIgnoreKeepAlive() { + if (loopKeepAliveActive_) { + // Make sure NotificationQueue is not counted as one of the readers + // (otherwise loopBody won't return until terminateLoopSoon is called). + fnRunner_->stopConsuming(); + fnRunner_->startConsumingInternal(this, queue_.get()); + loopKeepAliveActive_ = false; + } + return loopBody(0, true); +} + +bool EventBase::loopOnce(int flags) { + return loopBody(flags | EVLOOP_ONCE); +} + +bool EventBase::loopBody(int flags, bool ignoreKeepAlive) { + VLOG(5) << "EventBase(): Starting loop."; + + const char* message = + "Your code just tried to loop over an event base from inside another " + "event base loop. Since libevent is not reentrant, this leads to " + "undefined behavior in opt builds. Please fix immediately. For the " + "common case of an inner function that needs to do some synchronous " + "computation on an event-base, replace getEventBase() by a new, " + "stack-allocated EvenBase."; + + LOG_IF(DFATAL, invokingLoop_) << message; + + invokingLoop_ = true; + SCOPE_EXIT { + invokingLoop_ = false; + }; + + int res = 0; + bool ranLoopCallbacks; + bool blocking = !(flags & EVLOOP_NONBLOCK); + bool once = (flags & EVLOOP_ONCE); + + // time-measurement variables. + std::chrono::steady_clock::time_point prev; + std::chrono::steady_clock::time_point idleStart = {}; + std::chrono::microseconds busy; + std::chrono::microseconds idle; + + auto const prevLoopThread = loopThread_.exchange( + std::this_thread::get_id(), std::memory_order_relaxed); + CHECK_EQ(std::thread::id(), prevLoopThread) + << "Driving an EventBase in one thread (" << std::this_thread::get_id() + << ") while it is already being driven in another thread (" + << prevLoopThread << ") is forbidden."; + + if (!name_.empty()) { + setThreadName(name_); + } + + if (enableTimeMeasurement_) { + prev = std::chrono::steady_clock::now(); + idleStart = prev; + } + + while (!stop_.load(std::memory_order_relaxed)) { + if (!ignoreKeepAlive) { + applyLoopKeepAlive(); + } + ++nextLoopCnt_; + + // Run the before loop callbacks + LoopCallbackList callbacks; + callbacks.swap(runBeforeLoopCallbacks_); + + while (!callbacks.empty()) { + auto* item = &callbacks.front(); + callbacks.pop_front(); + item->runLoopCallback(); + } + + // nobody can add loop callbacks from within this thread if + // we don't have to handle anything to start with... + if (blocking && loopCallbacks_.empty()) { + res = evb_->eb_event_base_loop(EVLOOP_ONCE); + } else { + res = evb_->eb_event_base_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + } + + ranLoopCallbacks = runLoopCallbacks(); + + if (enableTimeMeasurement_) { + auto now = std::chrono::steady_clock::now(); + busy = std::chrono::duration_cast<std::chrono::microseconds>( + now - startWork_); + idle = std::chrono::duration_cast<std::chrono::microseconds>( + startWork_ - idleStart); + auto loop_time = busy + idle; + + avgLoopTime_.addSample(loop_time, busy); + maxLatencyLoopTime_.addSample(loop_time, busy); + + if (observer_) { + if (observerSampleCount_++ == observer_->getSampleRate()) { + observerSampleCount_ = 0; + observer_->loopSample(busy.count(), idle.count()); + } + } + + VLOG(11) << "EventBase " << this << " did not timeout " + << " loop time guess: " << loop_time.count() + << " idle time: " << idle.count() + << " busy time: " << busy.count() + << " avgLoopTime: " << avgLoopTime_.get() + << " maxLatencyLoopTime: " << maxLatencyLoopTime_.get() + << " maxLatency_: " << maxLatency_.count() << "us" + << " notificationQueueSize: " << getNotificationQueueSize() + << " nothingHandledYet(): " << nothingHandledYet(); + + // see if our average loop time has exceeded our limit + if ((maxLatency_ > std::chrono::microseconds::zero()) && + (maxLatencyLoopTime_.get() > double(maxLatency_.count()))) { + maxLatencyCob_(); + // back off temporarily -- don't keep spamming maxLatencyCob_ + // if we're only a bit over the limit + maxLatencyLoopTime_.dampen(0.9); + } + + // Our loop run did real work; reset the idle timer + idleStart = now; + } else { + VLOG(11) << "EventBase " << this << " did not timeout"; + } + + // Event loop indicated that there were no more events (NotificationQueue + // was registered as an internal event and there were no other registered + // events). + if (res != 0) { + // Since Notification Queue is marked 'internal' some events may not have + // run. Run them manually if so, and continue looping. + // + if (getNotificationQueueSize() > 0) { + fnRunner_->handlerReady(0); + } else if (!ranLoopCallbacks) { + // If there were no more events and we also didn't have any loop + // callbacks to run, there is nothing left to do. + break; + } + } + + if (enableTimeMeasurement_) { + VLOG(11) << "EventBase " << this + << " loop time: " << getTimeDelta(&prev).count(); + } + + if (once) { + break; + } + } + // Reset stop_ so loop() can be called again + stop_.store(false, std::memory_order_relaxed); + + if (res < 0) { + LOG(ERROR) << "EventBase: -- error in event loop, res = " << res; + return false; + } else if (res == 1) { + VLOG(5) << "EventBase: ran out of events (exiting loop)!"; + } else if (res > 1) { + LOG(ERROR) << "EventBase: unknown event loop result = " << res; + return false; + } + + loopThread_.store({}, std::memory_order_release); + + VLOG(5) << "EventBase(): Done with loop."; + return true; +} + +ssize_t EventBase::loopKeepAliveCount() { + if (loopKeepAliveCountAtomic_.load(std::memory_order_relaxed)) { + loopKeepAliveCount_ += + loopKeepAliveCountAtomic_.exchange(0, std::memory_order_relaxed); + } + DCHECK_GE(loopKeepAliveCount_, 0); + + return loopKeepAliveCount_; +} + +void EventBase::applyLoopKeepAlive() { + auto keepAliveCount = loopKeepAliveCount(); + // Make sure default VirtualEventBase won't hold EventBase::loop() forever. + if (auto virtualEventBase = tryGetVirtualEventBase()) { + if (virtualEventBase->keepAliveCount() == 1) { + --keepAliveCount; + } + } + + if (loopKeepAliveActive_ && keepAliveCount == 0) { + // Restore the notification queue internal flag + fnRunner_->stopConsuming(); + fnRunner_->startConsumingInternal(this, queue_.get()); + loopKeepAliveActive_ = false; + } else if (!loopKeepAliveActive_ && keepAliveCount > 0) { + // Update the notification queue event to treat it as a normal + // (non-internal) event. The notification queue event always remains + // installed, and the main loop won't exit with it installed. + fnRunner_->stopConsuming(); + fnRunner_->startConsuming(this, queue_.get()); + loopKeepAliveActive_ = true; + } +} + +void EventBase::loopForever() { + bool ret; + { + SCOPE_EXIT { + applyLoopKeepAlive(); + }; + // Make sure notification queue events are treated as normal events. + // We can't use loopKeepAlive() here since LoopKeepAlive token can only be + // released inside a loop. + ++loopKeepAliveCount_; + SCOPE_EXIT { + --loopKeepAliveCount_; + }; + ret = loop(); + } + + if (!ret) { + folly::throwSystemError("error in EventBase::loopForever()"); + } +} + +void EventBase::bumpHandlingTime() { + if (!enableTimeMeasurement_) { + return; + } + + VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ + << " (loop) latest " << latestLoopCnt_ << " next " << nextLoopCnt_; + if (nothingHandledYet()) { + latestLoopCnt_ = nextLoopCnt_; + // set the time + startWork_ = std::chrono::steady_clock::now(); + + VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ + << " (loop) startWork_ " << startWork_.time_since_epoch().count(); + } +} + +void EventBase::terminateLoopSoon() { + VLOG(5) << "EventBase(): Received terminateLoopSoon() command."; + + // Set stop to true, so the event loop will know to exit. + stop_.store(true, std::memory_order_relaxed); + + // If terminateLoopSoon() is called from another thread, + // the EventBase thread might be stuck waiting for events. + // In this case, it won't wake up and notice that stop_ is set until it + // receives another event. Send an empty frame to the notification queue + // so that the event loop will wake up even if there are no other events. + try { + queue_->putMessage([&] { evb_->eb_event_base_loopbreak(); }); + } catch (...) { + // putMessage() can only fail when the queue is draining in ~EventBase. + } +} + +void EventBase::runInLoop(LoopCallback* callback, bool thisIteration) { + dcheckIsInEventBaseThread(); + callback->cancelLoopCallback(); + callback->context_ = RequestContext::saveContext(); + if (runOnceCallbacks_ != nullptr && thisIteration) { + runOnceCallbacks_->push_back(*callback); + } else { + loopCallbacks_.push_back(*callback); + } +} + +void EventBase::runInLoop(Func cob, bool thisIteration) { + dcheckIsInEventBaseThread(); + auto wrapper = new FunctionLoopCallback(std::move(cob)); + wrapper->context_ = RequestContext::saveContext(); + if (runOnceCallbacks_ != nullptr && thisIteration) { + runOnceCallbacks_->push_back(*wrapper); + } else { + loopCallbacks_.push_back(*wrapper); + } +} + +void EventBase::runOnDestruction(OnDestructionCallback& callback) { + callback.schedule( + [this](auto& cb) { onDestructionCallbacks_.wlock()->push_back(cb); }, + [this](auto& cb) { + onDestructionCallbacks_.withWLock( + [&](auto& list) { list.erase(list.iterator_to(cb)); }); + }); +} + +void EventBase::runOnDestruction(Func f) { + auto* callback = new FunctionOnDestructionCallback(std::move(f)); + runOnDestruction(*callback); +} + +void EventBase::runBeforeLoop(LoopCallback* callback) { + dcheckIsInEventBaseThread(); + callback->cancelLoopCallback(); + runBeforeLoopCallbacks_.push_back(*callback); +} + +void EventBase::runInEventBaseThread(Func fn) noexcept { + // Send the message. + // It will be received by the FunctionRunner in the EventBase's thread. + + // We try not to schedule nullptr callbacks + if (!fn) { + DLOG(FATAL) << "EventBase " << this + << ": Scheduling nullptr callbacks is not allowed"; + return; + } + + // Short-circuit if we are already in our event base + if (inRunningEventBaseThread()) { + runInLoop(std::move(fn)); + return; + } + + queue_->putMessage(std::move(fn)); +} + +void EventBase::runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept { + // Send the message. + // It will be received by the FunctionRunner in the EventBase's thread. + + // We try not to schedule nullptr callbacks + if (!fn) { + LOG(DFATAL) << "EventBase " << this + << ": Scheduling nullptr callbacks is not allowed"; + return; + } + + queue_->putMessage(std::move(fn)); +} + +void EventBase::runInEventBaseThreadAndWait(Func fn) noexcept { + if (inRunningEventBaseThread()) { + LOG(DFATAL) << "EventBase " << this << ": Waiting in the event loop is not " + << "allowed"; + return; + } + + Baton<> ready; + runInEventBaseThread([&ready, fn = std::move(fn)]() mutable { + SCOPE_EXIT { + ready.post(); + }; + // A trick to force the stored functor to be executed and then destructed + // before posting the baton and waking the waiting thread. + copy(std::move(fn))(); + }); + ready.wait(folly::Baton<>::wait_options().logging_enabled(false)); +} + +void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept { + if (isInEventBaseThread()) { + fn(); + } else { + runInEventBaseThreadAndWait(std::move(fn)); + } +} + +bool EventBase::runLoopCallbacks() { + bumpHandlingTime(); + if (!loopCallbacks_.empty()) { + // Swap the loopCallbacks_ list with a temporary list on our stack. + // This way we will only run callbacks scheduled at the time + // runLoopCallbacks() was invoked. + // + // If any of these callbacks in turn call runInLoop() to schedule more + // callbacks, those new callbacks won't be run until the next iteration + // around the event loop. This prevents runInLoop() callbacks from being + // able to start file descriptor and timeout based events. + LoopCallbackList currentCallbacks; + currentCallbacks.swap(loopCallbacks_); + runOnceCallbacks_ = ¤tCallbacks; + + while (!currentCallbacks.empty()) { + LoopCallback* callback = ¤tCallbacks.front(); + currentCallbacks.pop_front(); + folly::RequestContextScopeGuard rctx(std::move(callback->context_)); + callback->runLoopCallback(); + } + + runOnceCallbacks_ = nullptr; + return true; + } + return false; +} + +void EventBase::initNotificationQueue() { + // Infinite size queue + queue_ = std::make_unique<NotificationQueue<Func>>(); + + // We allocate fnRunner_ separately, rather than declaring it directly + // as a member of EventBase solely so that we don't need to include + // NotificationQueue.h from EventBase.h + fnRunner_ = std::make_unique<FunctionRunner>(); + + // Mark this as an internal event, so event_base_loop() will return if + // there are no other events besides this one installed. + // + // Most callers don't care about the internal notification queue used by + // EventBase. The queue is always installed, so if we did count the queue as + // an active event, loop() would never exit with no more events to process. + // Users can use loopForever() if they do care about the notification queue. + // (This is useful for EventBase threads that do nothing but process + // runInEventBaseThread() notifications.) + fnRunner_->startConsumingInternal(this, queue_.get()); +} + +void EventBase::SmoothLoopTime::setTimeInterval( + std::chrono::microseconds timeInterval) { + expCoeff_ = -1.0 / timeInterval.count(); + VLOG(11) << "expCoeff_ " << expCoeff_ << " " << __PRETTY_FUNCTION__; +} + +void EventBase::SmoothLoopTime::reset(double value) { + value_ = value; +} + +void EventBase::SmoothLoopTime::addSample( + std::chrono::microseconds total, + std::chrono::microseconds busy) { + if ((buffer_time_ + total) > buffer_interval_ && buffer_cnt_ > 0) { + // See https://en.wikipedia.org/wiki/Exponential_smoothing for + // more info on this calculation. + double coeff = exp(buffer_time_.count() * expCoeff_); + value_ = + value_ * coeff + (1.0 - coeff) * (busy_buffer_.count() / buffer_cnt_); + buffer_time_ = std::chrono::microseconds{0}; + busy_buffer_ = std::chrono::microseconds{0}; + buffer_cnt_ = 0; + } + buffer_time_ += total; + busy_buffer_ += busy; + buffer_cnt_++; +} + +bool EventBase::nothingHandledYet() const noexcept { + VLOG(11) << "latest " << latestLoopCnt_ << " next " << nextLoopCnt_; + return (nextLoopCnt_ != latestLoopCnt_); +} + +void EventBase::attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) { + auto* ev = obj->getEvent(); + assert(ev->eb_ev_base() == nullptr); + + ev->eb_event_base_set(this); + if (internal == AsyncTimeout::InternalEnum::INTERNAL) { + // Set the EVLIST_INTERNAL flag + event_ref_flags(ev->getEvent()) |= EVLIST_INTERNAL; + } +} + +void EventBase::detachTimeoutManager(AsyncTimeout* obj) { + cancelTimeout(obj); + auto* ev = obj->getEvent(); + ev->eb_ev_base(nullptr); +} + +bool EventBase::scheduleTimeout( + AsyncTimeout* obj, + TimeoutManager::timeout_type timeout) { + dcheckIsInEventBaseThread(); + // Set up the timeval and add the event + struct timeval tv; + tv.tv_sec = long(timeout.count() / 1000LL); + tv.tv_usec = long((timeout.count() % 1000LL) * 1000LL); + + auto* ev = obj->getEvent(); + + DCHECK(ev->eb_ev_base()); + + if (ev->eb_event_add(&tv) < 0) { + LOG(ERROR) << "EventBase: failed to schedule timeout: " << errnoStr(errno); + return false; + } + + return true; +} + +void EventBase::cancelTimeout(AsyncTimeout* obj) { + dcheckIsInEventBaseThread(); + auto* ev = obj->getEvent(); + if (ev->isEventRegistered()) { + ev->eb_event_del(); + } +} + +void EventBase::setName(const std::string& name) { + dcheckIsInEventBaseThread(); + name_ = name; + + if (isRunning()) { + setThreadName(loopThread_.load(std::memory_order_relaxed), name_); + } +} + +const std::string& EventBase::getName() { + dcheckIsInEventBaseThread(); + return name_; +} + +void EventBase::scheduleAt(Func&& fn, TimePoint const& timeout) { + auto duration = timeout - now(); + timer().scheduleTimeoutFn( + std::move(fn), + std::chrono::duration_cast<std::chrono::milliseconds>(duration)); +} + +event_base* EventBase::getLibeventBase() const { + return evb_ ? (evb_->getEventBase()) : nullptr; +} + +const char* EventBase::getLibeventVersion() { + return event_get_version(); +} +const char* EventBase::getLibeventMethod() { + return event_get_method(); +} + +VirtualEventBase& EventBase::getVirtualEventBase() { + folly::call_once(virtualEventBaseInitFlag_, [&] { + virtualEventBase_ = std::make_unique<VirtualEventBase>(*this); + }); + + return *virtualEventBase_; +} + +VirtualEventBase* EventBase::tryGetVirtualEventBase() { + if (folly::test_once(virtualEventBaseInitFlag_)) { + return virtualEventBase_.get(); + } + return nullptr; +} + +EventBase* EventBase::getEventBase() { + return this; +} + +EventBase::OnDestructionCallback::~OnDestructionCallback() { + if (*scheduled_.rlock()) { + LOG(FATAL) + << "OnDestructionCallback must be canceled if needed prior to destruction"; + } +} + +void EventBase::OnDestructionCallback::runCallback() noexcept { + scheduled_.withWLock([&](bool& scheduled) { + CHECK(scheduled); + scheduled = false; + + // run can only be called by EventBase and VirtualEventBase, and it's called + // after the callback has been popped off the list. + eraser_ = nullptr; + + // Note that the exclusive lock on shared state is held while the callback + // runs. This ensures concurrent callers to cancel() block until the + // callback finishes. + onEventBaseDestruction(); + }); +} + +void EventBase::OnDestructionCallback::schedule( + FunctionRef<void(OnDestructionCallback&)> linker, + Function<void(OnDestructionCallback&)> eraser) { + eraser_ = std::move(eraser); + scheduled_.withWLock([](bool& scheduled) { scheduled = true; }); + linker(*this); +} + +bool EventBase::OnDestructionCallback::cancel() { + return scheduled_.withWLock([this](bool& scheduled) { + const bool wasScheduled = std::exchange(scheduled, false); + if (wasScheduled) { + auto eraser = std::move(eraser_); + CHECK(eraser); + eraser(*this); + } + return wasScheduled; + }); +} + +constexpr std::chrono::milliseconds EventBase::SmoothLoopTime::buffer_interval_; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBase.h b/ios/Pods/Flipper-Folly/folly/io/async/EventBase.h new file mode 100644 index 000000000..8c412a844 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBase.h @@ -0,0 +1,962 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cerrno> +#include <cmath> +#include <cstdlib> +#include <functional> +#include <list> +#include <memory> +#include <queue> +#include <set> +#include <stack> +#include <unordered_map> +#include <unordered_set> +#include <utility> + +#include <boost/intrusive/list.hpp> +#include <glog/logging.h> + +#include <folly/Executor.h> +#include <folly/Function.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/Synchronized.h> +#include <folly/executors/DrivableExecutor.h> +#include <folly/executors/IOExecutor.h> +#include <folly/executors/ScheduledExecutor.h> +#include <folly/executors/SequencedExecutor.h> +#include <folly/experimental/ExecutionObserver.h> +#include <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/HHWheelTimer.h> +#include <folly/io/async/Request.h> +#include <folly/io/async/TimeoutManager.h> +#include <folly/portability/Event.h> +#include <folly/synchronization/CallOnce.h> + +namespace folly { +class EventBaseBackendBase; + +using Cob = Func; // defined in folly/Executor.h +template <typename MessageT> +class NotificationQueue; + +namespace detail { +class EventBaseLocalBase; + +class EventBaseLocalBaseBase { + public: + virtual void onEventBaseDestruction(EventBase& evb) = 0; + virtual ~EventBaseLocalBaseBase() = default; +}; +} // namespace detail +template <typename T> +class EventBaseLocal; + +class EventBaseObserver { + public: + virtual ~EventBaseObserver() = default; + + virtual uint32_t getSampleRate() const = 0; + + virtual void loopSample(int64_t busyTime, int64_t idleTime) = 0; +}; + +// Helper class that sets and retrieves the EventBase associated with a given +// request via RequestContext. See Request.h for that mechanism. +class RequestEventBase : public RequestData { + public: + static EventBase* get() { + auto data = dynamic_cast<RequestEventBase*>( + RequestContext::get()->getContextData(kContextDataName)); + if (!data) { + return nullptr; + } + return data->eb_; + } + + static void set(EventBase* eb) { + RequestContext::get()->setContextData( + kContextDataName, + std::unique_ptr<RequestEventBase>(new RequestEventBase(eb))); + } + + bool hasCallback() override { + return false; + } + + private: + explicit RequestEventBase(EventBase* eb) : eb_(eb) {} + EventBase* eb_; + static constexpr const char* kContextDataName{"EventBase"}; +}; + +class VirtualEventBase; + +/** + * This class is a wrapper for all asynchronous I/O processing functionality + * + * EventBase provides a main loop that notifies EventHandler callback objects + * when I/O is ready on a file descriptor, and notifies AsyncTimeout objects + * when a specified timeout has expired. More complex, higher-level callback + * mechanisms can then be built on top of EventHandler and AsyncTimeout. + * + * A EventBase object can only drive an event loop for a single thread. To + * take advantage of multiple CPU cores, most asynchronous I/O servers have one + * thread per CPU, and use a separate EventBase for each thread. + * + * In general, most EventBase methods may only be called from the thread + * running the EventBase's loop. There are a few exceptions to this rule, for + * methods that are explicitly intended to allow communication with a + * EventBase from other threads. When it is safe to call a method from + * another thread it is explicitly listed in the method comments. + */ +class EventBase : public TimeoutManager, + public DrivableExecutor, + public IOExecutor, + public SequencedExecutor, + public ScheduledExecutor { + public: + friend class ScopedEventBaseThread; + + using Func = folly::Function<void()>; + + /** + * A callback interface to use with runInLoop() + * + * Derive from this class if you need to delay some code execution until the + * next iteration of the event loop. This allows you to schedule code to be + * invoked from the top-level of the loop, after your immediate callers have + * returned. + * + * If a LoopCallback object is destroyed while it is scheduled to be run in + * the next loop iteration, it will automatically be cancelled. + */ + class LoopCallback + : public boost::intrusive::list_base_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>> { + public: + virtual ~LoopCallback() = default; + + virtual void runLoopCallback() noexcept = 0; + void cancelLoopCallback() { + context_.reset(); + unlink(); + } + + bool isLoopCallbackScheduled() const { + return is_linked(); + } + + private: + typedef boost::intrusive:: + list<LoopCallback, boost::intrusive::constant_time_size<false>> + List; + + // EventBase needs access to LoopCallbackList (and therefore to hook_) + friend class EventBase; + friend class VirtualEventBase; + std::shared_ptr<RequestContext> context_; + }; + + class FunctionLoopCallback : public LoopCallback { + public: + explicit FunctionLoopCallback(Func&& function) + : function_(std::move(function)) {} + + void runLoopCallback() noexcept override { + function_(); + delete this; + } + + private: + Func function_; + }; + + // Like FunctionLoopCallback, but saves one allocation. Use with caution. + // + // The caller is responsible for maintaining the lifetime of this callback + // until after the point at which the contained function is called. + class StackFunctionLoopCallback : public LoopCallback { + public: + explicit StackFunctionLoopCallback(Func&& function) + : function_(std::move(function)) {} + void runLoopCallback() noexcept override { + Func(std::move(function_))(); + } + + private: + Func function_; + }; + + // Base class for user callbacks to be run during EventBase destruction. As + // with LoopCallback, users may inherit from this class and provide a concrete + // implementation of onEventBaseDestruction(). (Alternatively, users may use + // the convenience method EventBase::runOnDestruction(Function<void()> f) to + // schedule a function f to be run on EventBase destruction.) + // + // The only thread-safety guarantees of OnDestructionCallback are as follows: + // - Users may call runOnDestruction() from any thread, provided the caller + // is the only user of the callback, i.e., the callback is not already + // scheduled and there are no concurrent calls to schedule or cancel the + // callback. + // - Users may safely cancel() from any thread. Multiple calls to cancel() + // may execute concurrently. The only caveat is that it is not safe to + // call cancel() within the onEventBaseDestruction() callback. + class OnDestructionCallback { + public: + OnDestructionCallback() = default; + OnDestructionCallback(OnDestructionCallback&&) = default; + OnDestructionCallback& operator=(OnDestructionCallback&&) = default; + virtual ~OnDestructionCallback(); + + // Attempt to cancel the callback. If the callback is running or has already + // finished running, cancellation will fail. If the callback is running when + // cancel() is called, cancel() will block until the callback completes. + bool cancel(); + + // Callback to be invoked during ~EventBase() + virtual void onEventBaseDestruction() noexcept = 0; + + private: + boost::intrusive::list_member_hook< + boost::intrusive::link_mode<boost::intrusive::normal_link>> + listHook_; + Function<void(OnDestructionCallback&)> eraser_; + Synchronized<bool> scheduled_{in_place, false}; + + using List = boost::intrusive::list< + OnDestructionCallback, + boost::intrusive::member_hook< + OnDestructionCallback, + decltype(listHook_), + &OnDestructionCallback::listHook_>>; + + void schedule( + FunctionRef<void(OnDestructionCallback&)> linker, + Function<void(OnDestructionCallback&)> eraser); + + friend class EventBase; + friend class VirtualEventBase; + + protected: + virtual void runCallback() noexcept; + }; + + class FunctionOnDestructionCallback : public OnDestructionCallback { + public: + explicit FunctionOnDestructionCallback(Function<void()> f) + : f_(std::move(f)) {} + + void onEventBaseDestruction() noexcept final { + f_(); + } + + protected: + void runCallback() noexcept override { + OnDestructionCallback::runCallback(); + delete this; + } + + private: + Function<void()> f_; + }; + + /** + * Create a new EventBase object. + * + * Same as EventBase(true), which constructs an EventBase that measures time, + * except that this also allows the timer granularity to be specified + */ + + explicit EventBase(std::chrono::milliseconds tickInterval) : EventBase(true) { + intervalDuration_ = tickInterval; + } + + /** + * Create a new EventBase object. + * + * Same as EventBase(true), which constructs an EventBase that measures time. + */ + EventBase() : EventBase(true) {} + + /** + * Create a new EventBase object. + * + * @param enableTimeMeasurement Informs whether this event base should measure + * time. Disabling it would likely improve + * performance, but will disable some features + * that relies on time-measurement, including: + * observer, max latency and avg loop time. + */ + explicit EventBase(bool enableTimeMeasurement); + + EventBase(const EventBase&) = delete; + EventBase& operator=(const EventBase&) = delete; + + /** + * Create a new EventBase object that will use the specified libevent + * event_base object to drive the event loop. + * + * The EventBase will take ownership of this event_base, and will call + * event_base_free(evb) when the EventBase is destroyed. + * + * @param enableTimeMeasurement Informs whether this event base should measure + * time. Disabling it would likely improve + * performance, but will disable some features + * that relies on time-measurement, including: + * observer, max latency and avg loop time. + */ + explicit EventBase(event_base* evb, bool enableTimeMeasurement = true); + explicit EventBase( + std::unique_ptr<EventBaseBackendBase>&& evb, + bool enableTimeMeasurement = true); + ~EventBase() override; + + /** + * Runs the event loop. + * + * loop() will loop waiting for I/O or timeouts and invoking EventHandler + * and AsyncTimeout callbacks as their events become ready. loop() will + * only return when there are no more events remaining to process, or after + * terminateLoopSoon() has been called. + * + * loop() may be called again to restart event processing after a previous + * call to loop() or loopForever() has returned. + * + * Returns true if the loop completed normally (if it processed all + * outstanding requests, or if terminateLoopSoon() was called). If an error + * occurs waiting for events, false will be returned. + */ + bool loop(); + + /** + * Same as loop(), but doesn't wait for all keep-alive tokens to be released. + */ + [[deprecated("This should only be used in legacy unit tests")]] bool + loopIgnoreKeepAlive(); + + /** + * Wait for some events to become active, run them, then return. + * + * When EVLOOP_NONBLOCK is set in flags, the loop won't block if there + * are not any events to process. + * + * This is useful for callers that want to run the loop manually. + * + * Returns the same result as loop(). + */ + bool loopOnce(int flags = 0); + + /** + * Runs the event loop. + * + * loopForever() behaves like loop(), except that it keeps running even if + * when there are no more user-supplied EventHandlers or AsyncTimeouts + * registered. It will only return after terminateLoopSoon() has been + * called. + * + * This is useful for callers that want to wait for other threads to call + * runInEventBaseThread(), even when there are no other scheduled events. + * + * loopForever() may be called again to restart event processing after a + * previous call to loop() or loopForever() has returned. + * + * Throws a std::system_error if an error occurs. + */ + void loopForever(); + + /** + * Causes the event loop to exit soon. + * + * This will cause an existing call to loop() or loopForever() to stop event + * processing and return, even if there are still events remaining to be + * processed. + * + * It is safe to call terminateLoopSoon() from another thread to cause loop() + * to wake up and return in the EventBase loop thread. terminateLoopSoon() + * may also be called from the loop thread itself (for example, a + * EventHandler or AsyncTimeout callback may call terminateLoopSoon() to + * cause the loop to exit after the callback returns.) If the loop is not + * running, this will cause the next call to loop to terminate soon after + * starting. If a loop runs out of work (and so terminates on its own) + * concurrently with a call to terminateLoopSoon(), this may cause a race + * condition. + * + * Note that the caller is responsible for ensuring that cleanup of all event + * callbacks occurs properly. Since terminateLoopSoon() causes the loop to + * exit even when there are pending events present, there may be remaining + * callbacks present waiting to be invoked. If the loop is later restarted + * pending events will continue to be processed normally, however if the + * EventBase is destroyed after calling terminateLoopSoon() it is the + * caller's responsibility to ensure that cleanup happens properly even if + * some outstanding events are never processed. + */ + void terminateLoopSoon(); + + /** + * Adds the given callback to a queue of things run after the current pass + * through the event loop completes. Note that if this callback calls + * runInLoop() the new callback won't be called until the main event loop + * has gone through a cycle. + * + * This method may only be called from the EventBase's thread. This + * essentially allows an event handler to schedule an additional callback to + * be invoked after it returns. + * + * Use runInEventBaseThread() to schedule functions from another thread. + * + * The thisIteration parameter makes this callback run in this loop + * iteration, instead of the next one, even if called from a + * runInLoop callback (normal io callbacks that call runInLoop will + * always run in this iteration). This was originally added to + * support detachEventBase, as a user callback may have called + * terminateLoopSoon(), but we want to make sure we detach. Also, + * detachEventBase almost always must be called from the base event + * loop to ensure the stack is unwound, since most users of + * EventBase are not thread safe. + * + * Ideally we would not need thisIteration, and instead just use + * runInLoop with loop() (instead of terminateLoopSoon). + */ + void runInLoop(LoopCallback* callback, bool thisIteration = false); + + /** + * Convenience function to call runInLoop() with a folly::Function. + * + * This creates a LoopCallback object to wrap the folly::Function, and invoke + * the folly::Function when the loop callback fires. This is slightly more + * expensive than defining your own LoopCallback, but more convenient in + * areas that aren't too performance sensitive. + * + * This method may only be called from the EventBase's thread. This + * essentially allows an event handler to schedule an additional callback to + * be invoked after it returns. + * + * Use runInEventBaseThread() to schedule functions from another thread. + */ + void runInLoop(Func c, bool thisIteration = false); + + /** + * Adds the given callback to a queue of things run before destruction + * of current EventBase. + * + * This allows users of EventBase that run in it, but don't control it, to be + * notified before EventBase gets destructed. + * + * Note: will be called from the thread that invoked EventBase destructor, + * before the final run of loop callbacks. + */ + void runOnDestruction(OnDestructionCallback& callback); + + /** + * Convenience function that allows users to pass in a Function<void()> to be + * run on EventBase destruction. + */ + void runOnDestruction(Func f); + + /** + * Adds a callback that will run immediately *before* the event loop. + * This is very similar to runInLoop(), but will not cause the loop to break: + * For example, this callback could be used to get loop times. + */ + void runBeforeLoop(LoopCallback* callback); + + /** + * Run the specified function in the EventBase's thread. + * + * This method is thread-safe, and may be called from another thread. + * + * If runInEventBaseThread() is called when the EventBase loop is not + * running, the function call will be delayed until the next time the loop is + * started. + * + * If the loop is terminated (and never later restarted) before it has a + * chance to run the requested function, the function will be run upon the + * EventBase's destruction. + * + * If two calls to runInEventBaseThread() are made from the same thread, the + * functions will always be run in the order that they were scheduled. + * Ordering between functions scheduled from separate threads is not + * guaranteed. + * + * @param fn The function to run. The function must not throw any + * exceptions. + * @param arg An argument to pass to the function. + */ + template <typename T> + void runInEventBaseThread(void (*fn)(T*), T* arg) noexcept; + + /** + * Run the specified function in the EventBase's thread + * + * This version of runInEventBaseThread() takes a folly::Function object. + * Note that this may be less efficient than the version that takes a plain + * function pointer and void* argument, if moving the function is expensive + * (e.g., if it wraps a lambda which captures some values with expensive move + * constructors). + * + * If the loop is terminated (and never later restarted) before it has a + * chance to run the requested function, the function will be run upon the + * EventBase's destruction. + * + * The function must not throw any exceptions. + */ + void runInEventBaseThread(Func fn) noexcept; + + /** + * Run the specified function in the EventBase's thread. + * + * This method is thread-safe, and may be called from another thread. + * + * If runInEventBaseThreadAlwaysEnqueue() is called when the EventBase loop is + * not running, the function call will be delayed until the next time the loop + * is started. + * + * If the loop is terminated (and never later restarted) before it has a + * chance to run the requested function, the function will be run upon the + * EventBase's destruction. + * + * If two calls to runInEventBaseThreadAlwaysEnqueue() are made from the same + * thread, the functions will always be run in the order that they were + * scheduled. Ordering between functions scheduled from separate threads is + * not guaranteed. If a call is made from the EventBase thread, the function + * will not be executed inline and will be queued to the same queue as if the + * call would have been made from a different thread + * + * @param fn The function to run. The function must not throw any + * exceptions. + * @param arg An argument to pass to the function. + */ + template <typename T> + void runInEventBaseThreadAlwaysEnqueue(void (*fn)(T*), T* arg) noexcept; + + /** + * Run the specified function in the EventBase's thread + * + * This version of runInEventBaseThreadAlwaysEnqueue() takes a folly::Function + * object. Note that this may be less efficient than the version that takes a + * plain function pointer and void* argument, if moving the function is + * expensive (e.g., if it wraps a lambda which captures some values with + * expensive move constructors). + * + * If the loop is terminated (and never later restarted) before it has a + * chance to run the requested function, the function will be run upon the + * EventBase's destruction. + * + * The function must not throw any exceptions. + */ + void runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept; + + /* + * Like runInEventBaseThread, but the caller waits for the callback to be + * executed. + */ + template <typename T> + void runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept; + + /* + * Like runInEventBaseThread, but the caller waits for the callback to be + * executed. + */ + void runInEventBaseThreadAndWait(Func fn) noexcept; + + /* + * Like runInEventBaseThreadAndWait, except if the caller is already in the + * event base thread, the functor is simply run inline. + */ + template <typename T> + void runImmediatelyOrRunInEventBaseThreadAndWait( + void (*fn)(T*), + T* arg) noexcept; + + /* + * Like runInEventBaseThreadAndWait, except if the caller is already in the + * event base thread, the functor is simply run inline. + */ + void runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept; + + /** + * Set the maximum desired latency in us and provide a callback which will be + * called when that latency is exceeded. + * OBS: This functionality depends on time-measurement. + */ + void setMaxLatency(std::chrono::microseconds maxLatency, Func maxLatencyCob) { + assert(enableTimeMeasurement_); + maxLatency_ = maxLatency; + maxLatencyCob_ = std::move(maxLatencyCob); + } + + /** + * Set smoothing coefficient for loop load average; # of milliseconds + * for exp(-1) (1/2.71828...) decay. + */ + void setLoadAvgMsec(std::chrono::milliseconds ms); + + /** + * reset the load average to a desired value + */ + void resetLoadAvg(double value = 0.0); + + /** + * Get the average loop time in microseconds (an exponentially-smoothed ave) + */ + double getAvgLoopTime() const { + assert(enableTimeMeasurement_); + return avgLoopTime_.get(); + } + + /** + * check if the event base loop is running. + */ + bool isRunning() const { + return loopThread_.load(std::memory_order_relaxed) != std::thread::id(); + } + + /** + * wait until the event loop starts (after starting the event loop thread). + */ + void waitUntilRunning(); + + size_t getNotificationQueueSize() const; + + void setMaxReadAtOnce(uint32_t maxAtOnce); + + /** + * Verify that current thread is the EventBase thread, if the EventBase is + * running. + */ + bool isInEventBaseThread() const { + auto tid = loopThread_.load(std::memory_order_relaxed); + return tid == std::thread::id() || tid == std::this_thread::get_id(); + } + + bool inRunningEventBaseThread() const { + return loopThread_.load(std::memory_order_relaxed) == + std::this_thread::get_id(); + } + + /** + * Equivalent to CHECK(isInEventBaseThread()) (and assert/DCHECK for + * dcheckIsInEventBaseThread), but it prints more information on + * failure. + */ + void checkIsInEventBaseThread() const; + void dcheckIsInEventBaseThread() const { + if (kIsDebug) { + checkIsInEventBaseThread(); + } + } + + HHWheelTimer& timer() { + if (!wheelTimer_) { + wheelTimer_ = HHWheelTimer::newTimer(this, intervalDuration_); + } + return *wheelTimer_.get(); + } + + EventBaseBackendBase* getBackend() { + return evb_.get(); + } + // --------- interface to underlying libevent base ------------ + // Avoid using these functions if possible. These functions are not + // guaranteed to always be present if we ever provide alternative EventBase + // implementations that do not use libevent internally. + event_base* getLibeventBase() const; + + static const char* getLibeventVersion(); + static const char* getLibeventMethod(); + + /** + * only EventHandler/AsyncTimeout subclasses and ourselves should + * ever call this. + * + * This is used to mark the beginning of a new loop cycle by the + * first handler fired within that cycle. + * + */ + void bumpHandlingTime() final; + + class SmoothLoopTime { + public: + explicit SmoothLoopTime(std::chrono::microseconds timeInterval) + : expCoeff_(-1.0 / timeInterval.count()), value_(0.0) { + VLOG(11) << "expCoeff_ " << expCoeff_ << " " << __PRETTY_FUNCTION__; + } + + void setTimeInterval(std::chrono::microseconds timeInterval); + void reset(double value = 0.0); + + void addSample( + std::chrono::microseconds total, + std::chrono::microseconds busy); + + double get() const { + // Add the outstanding buffered times linearly, to avoid + // expensive exponentiation + auto lcoeff = buffer_time_.count() * -expCoeff_; + return value_ * (1.0 - lcoeff) + lcoeff * busy_buffer_.count(); + } + + void dampen(double factor) { + value_ *= factor; + } + + private: + double expCoeff_; + double value_; + std::chrono::microseconds buffer_time_{0}; + std::chrono::microseconds busy_buffer_{0}; + std::size_t buffer_cnt_{0}; + static constexpr std::chrono::milliseconds buffer_interval_{10}; + }; + + void setObserver(const std::shared_ptr<EventBaseObserver>& observer) { + assert(enableTimeMeasurement_); + observer_ = observer; + } + + const std::shared_ptr<EventBaseObserver>& getObserver() { + return observer_; + } + + /** + * Setup execution observation/instrumentation for every EventHandler + * executed in this EventBase. + * + * @param executionObserver EventHandle's execution observer. + */ + void setExecutionObserver(ExecutionObserver* observer) { + executionObserver_ = observer; + } + + /** + * Gets the execution observer associated with this EventBase. + */ + ExecutionObserver* getExecutionObserver() { + return executionObserver_; + } + + /** + * Set the name of the thread that runs this event base. + */ + void setName(const std::string& name); + + /** + * Returns the name of the thread that runs this event base. + */ + const std::string& getName(); + + /// Implements the Executor interface + void add(Cob fn) override { + runInEventBaseThread(std::move(fn)); + } + + /// Implements the DrivableExecutor interface + void drive() override { + ++loopKeepAliveCount_; + SCOPE_EXIT { + --loopKeepAliveCount_; + }; + loopOnce(); + } + + // Implements the ScheduledExecutor interface + void scheduleAt(Func&& fn, TimePoint const& timeout) override; + + // TimeoutManager + void attachTimeoutManager( + AsyncTimeout* obj, + TimeoutManager::InternalEnum internal) final; + + void detachTimeoutManager(AsyncTimeout* obj) final; + + bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout) + final; + + void cancelTimeout(AsyncTimeout* obj) final; + + bool isInTimeoutManagerThread() final { + return isInEventBaseThread(); + } + + // Returns a VirtualEventBase attached to this EventBase. Can be used to + // pass to APIs which expect VirtualEventBase. This VirtualEventBase will be + // destroyed together with the EventBase. + // + // Any number of VirtualEventBases instances may be independently constructed, + // which are backed by this EventBase. This method should be only used if you + // don't need to manage the life time of the VirtualEventBase used. + folly::VirtualEventBase& getVirtualEventBase(); + + /// Implements the IOExecutor interface + EventBase* getEventBase() override; + + static std::unique_ptr<EventBaseBackendBase> getDefaultBackend(); + + protected: + bool keepAliveAcquire() override { + if (inRunningEventBaseThread()) { + loopKeepAliveCount_++; + } else { + loopKeepAliveCountAtomic_.fetch_add(1, std::memory_order_relaxed); + } + return true; + } + + void keepAliveRelease() override { + if (!inRunningEventBaseThread()) { + return add([this] { loopKeepAliveCount_--; }); + } + loopKeepAliveCount_--; + } + + private: + folly::VirtualEventBase* tryGetVirtualEventBase(); + + void applyLoopKeepAlive(); + + ssize_t loopKeepAliveCount(); + + /* + * Helper function that tells us whether we have already handled + * some event/timeout/callback in this loop iteration. + */ + bool nothingHandledYet() const noexcept; + + typedef LoopCallback::List LoopCallbackList; + class FunctionRunner; + + bool loopBody(int flags = 0, bool ignoreKeepAlive = false); + + // executes any callbacks queued by runInLoop(); returns false if none found + bool runLoopCallbacks(); + + void initNotificationQueue(); + + // Tick granularity to wheelTimer_ + std::chrono::milliseconds intervalDuration_{ + HHWheelTimer::DEFAULT_TICK_INTERVAL}; + // should only be accessed through public getter + HHWheelTimer::UniquePtr wheelTimer_; + + LoopCallbackList loopCallbacks_; + LoopCallbackList runBeforeLoopCallbacks_; + Synchronized<OnDestructionCallback::List> onDestructionCallbacks_; + + // This will be null most of the time, but point to currentCallbacks + // if we are in the middle of running loop callbacks, such that + // runInLoop(..., true) will always run in the current loop + // iteration. + LoopCallbackList* runOnceCallbacks_; + + // stop_ is set by terminateLoopSoon() and is used by the main loop + // to determine if it should exit + std::atomic<bool> stop_; + + // The ID of the thread running the main loop. + // std::thread::id{} if loop is not running. + std::atomic<std::thread::id> loopThread_; + + // A notification queue for runInEventBaseThread() to use + // to send function requests to the EventBase thread. + std::unique_ptr<NotificationQueue<Func>> queue_; + std::unique_ptr<FunctionRunner> fnRunner_; + ssize_t loopKeepAliveCount_{0}; + std::atomic<ssize_t> loopKeepAliveCountAtomic_{0}; + bool loopKeepAliveActive_{false}; + + // limit for latency in microseconds (0 disables) + std::chrono::microseconds maxLatency_; + + // exponentially-smoothed average loop time for latency-limiting + SmoothLoopTime avgLoopTime_; + + // smoothed loop time used to invoke latency callbacks; differs from + // avgLoopTime_ in that it's scaled down after triggering a callback + // to reduce spamminess + SmoothLoopTime maxLatencyLoopTime_; + + // callback called when latency limit is exceeded + Func maxLatencyCob_; + + // Enables/disables time measurements in loopBody(). if disabled, the + // following functionality that relies on time-measurement, will not + // be supported: avg loop time, observer and max latency. + const bool enableTimeMeasurement_; + + // Wrap-around loop counter to detect beginning of each loop + std::size_t nextLoopCnt_; + std::size_t latestLoopCnt_; + std::chrono::steady_clock::time_point startWork_; + // Prevent undefined behavior from invoking event_base_loop() reentrantly. + // This is needed since many projects use libevent-1.4, which lacks commit + // b557b175c00dc462c1fce25f6e7dd67121d2c001 from + // https://github.com/libevent/libevent/. + bool invokingLoop_{false}; + + // Observer to export counters + std::shared_ptr<EventBaseObserver> observer_; + uint32_t observerSampleCount_; + + // EventHandler's execution observer. + ExecutionObserver* executionObserver_; + + // Name of the thread running this EventBase + std::string name_; + + // see EventBaseLocal + friend class detail::EventBaseLocalBase; + template <typename T> + friend class EventBaseLocal; + std::unordered_map<std::size_t, std::shared_ptr<void>> localStorage_; + std::unordered_set<detail::EventBaseLocalBaseBase*> localStorageToDtor_; + + folly::once_flag virtualEventBaseInitFlag_; + std::unique_ptr<VirtualEventBase> virtualEventBase_; + + // pointer to underlying backend class doing the heavy lifting + std::unique_ptr<EventBaseBackendBase> evb_; +}; + +template <typename T> +void EventBase::runInEventBaseThread(void (*fn)(T*), T* arg) noexcept { + return runInEventBaseThread([=] { fn(arg); }); +} + +template <typename T> +void EventBase::runInEventBaseThreadAlwaysEnqueue( + void (*fn)(T*), + T* arg) noexcept { + return runInEventBaseThreadAlwaysEnqueue([=] { fn(arg); }); +} + +template <typename T> +void EventBase::runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept { + return runInEventBaseThreadAndWait([=] { fn(arg); }); +} + +template <typename T> +void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait( + void (*fn)(T*), + T* arg) noexcept { + return runImmediatelyOrRunInEventBaseThreadAndWait([=] { fn(arg); }); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.cpp new file mode 100644 index 000000000..bd857f88c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/EventBaseBackendBase.h> +#include <folly/io/async/EventBase.h> + +namespace folly { +void EventBaseEvent::eb_ev_base(EventBase* evb) { + evb_ = evb; + event_.ev_base = evb ? evb->getLibeventBase() : nullptr; +} + +int EventBaseEvent::eb_event_base_set(EventBase* evb) { + evb_ = evb; + auto* base = evb_ ? (evb_->getLibeventBase()) : nullptr; + if (base) { + return ::event_base_set(base, &event_); + } + + return 0; +} + +int EventBaseEvent::eb_event_add(const struct timeval* timeout) { + auto* backend = evb_ ? (evb_->getBackend()) : nullptr; + if (backend) { + return backend->eb_event_add(*this, timeout); + } + + return -1; +} + +int EventBaseEvent::eb_event_del() { + auto* backend = evb_ ? (evb_->getBackend()) : nullptr; + if (backend) { + return backend->eb_event_del(*this); + } + + return -1; +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.h b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.h new file mode 100644 index 000000000..3d423d885 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseBackendBase.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +#include <folly/io/async/EventUtil.h> +#include <folly/portability/Event.h> + +namespace folly { +class EventBase; + +class EventBaseEvent { + public: + EventBaseEvent() = default; + ~EventBaseEvent() { + if (userData_ && freeFn_) { + freeFn_(userData_); + } + } + + EventBaseEvent(const EventBaseEvent&) = delete; + EventBaseEvent& operator=(const EventBaseEvent&) = delete; + + typedef void (*FreeFunction)(void* userData); + + const struct event* getEvent() const { + return &event_; + } + + struct event* getEvent() { + return &event_; + } + + bool isEventRegistered() const { + return EventUtil::isEventRegistered(&event_); + } + + libevent_fd_t eb_ev_fd() const { + return event_.ev_fd; + } + + short eb_ev_events() const { + return event_.ev_events; + } + + int eb_ev_res() const { + return event_.ev_res; + } + + void* getUserData() { + return userData_; + } + + void setUserData(void* userData) { + userData_ = userData; + } + + void setUserData(void* userData, FreeFunction freeFn) { + userData_ = userData; + freeFn_ = freeFn; + } + + void eb_event_set( + libevent_fd_t fd, + short events, + void (*callback)(libevent_fd_t, short, void*), + void* arg) { + event_set(&event_, fd, events, callback, arg); + } + + void eb_signal_set( + int signum, + void (*callback)(libevent_fd_t, short, void*), + void* arg) { + event_set(&event_, signum, EV_SIGNAL | EV_PERSIST, callback, arg); + } + + void eb_timer_set(void (*callback)(libevent_fd_t, short, void*), void* arg) { + event_set(&event_, -1, 0, callback, arg); + } + + void eb_ev_base(EventBase* evb); + EventBase* eb_ev_base() const { + return evb_; + } + + int eb_event_base_set(EventBase* evb); + + int eb_event_add(const struct timeval* timeout); + + int eb_event_del(); + + protected: + struct event event_; + EventBase* evb_{nullptr}; + void* userData_{nullptr}; + FreeFunction freeFn_{nullptr}; +}; + +class EventBaseBackendBase { + public: + using Event = EventBaseEvent; + using FactoryFunc = + std::function<std::unique_ptr<folly::EventBaseBackendBase>()>; + + EventBaseBackendBase() = default; + virtual ~EventBaseBackendBase() = default; + + EventBaseBackendBase(const EventBaseBackendBase&) = delete; + EventBaseBackendBase& operator=(const EventBaseBackendBase&) = delete; + + virtual event_base* getEventBase() = 0; + virtual int eb_event_base_loop(int flags) = 0; + virtual int eb_event_base_loopbreak() = 0; + + virtual int eb_event_add(Event& event, const struct timeval* timeout) = 0; + virtual int eb_event_del(Event& event) = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.cpp new file mode 100644 index 000000000..015005e94 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/EventBaseLocal.h> +#include <folly/MapUtil.h> +#include <atomic> +#include <thread> + +namespace folly { +namespace detail { + +EventBaseLocalBase::~EventBaseLocalBase() { + auto locked = eventBases_.rlock(); + for (auto* evb : *locked) { + evb->runInEventBaseThread([this, evb, key = key_] { + evb->localStorage_.erase(key); + evb->localStorageToDtor_.erase(this); + }); + } +} + +void* EventBaseLocalBase::getVoid(EventBase& evb) { + evb.dcheckIsInEventBaseThread(); + + return folly::get_default(evb.localStorage_, key_, {}).get(); +} + +void EventBaseLocalBase::erase(EventBase& evb) { + evb.dcheckIsInEventBaseThread(); + + evb.localStorage_.erase(key_); + evb.localStorageToDtor_.erase(this); + + eventBases_.wlock()->erase(&evb); +} + +void EventBaseLocalBase::onEventBaseDestruction(EventBase& evb) { + evb.dcheckIsInEventBaseThread(); + + eventBases_.wlock()->erase(&evb); +} + +void EventBaseLocalBase::setVoid(EventBase& evb, std::shared_ptr<void>&& ptr) { + evb.dcheckIsInEventBaseThread(); + + auto alreadyExists = evb.localStorage_.find(key_) != evb.localStorage_.end(); + + evb.localStorage_.emplace(key_, std::move(ptr)); + + if (!alreadyExists) { + eventBases_.wlock()->insert(&evb); + evb.localStorageToDtor_.insert(this); + } +} + +std::atomic<std::size_t> EventBaseLocalBase::keyCounter_{0}; +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.h b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.h new file mode 100644 index 000000000..10170ac3b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseLocal.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Synchronized.h> +#include <folly/io/async/EventBase.h> +#include <memory> +#include <mutex> +#include <unordered_set> +#include <utility> + +namespace folly { + +namespace detail { + +class EventBaseLocalBase : public EventBaseLocalBaseBase { + public: + EventBaseLocalBase() {} + EventBaseLocalBase(const EventBaseLocalBase&) = delete; + EventBaseLocalBase& operator=(const EventBaseLocalBase&) = delete; + ~EventBaseLocalBase() override; + void erase(EventBase& evb); + void onEventBaseDestruction(EventBase& evb) override; + + protected: + void setVoid(EventBase& evb, std::shared_ptr<void>&& ptr); + void* getVoid(EventBase& evb); + + folly::Synchronized<std::unordered_set<EventBase*>> eventBases_; + static std::atomic<std::size_t> keyCounter_; + std::size_t key_{keyCounter_++}; +}; + +} // namespace detail + +/** + * A storage abstraction for data that should be tied to an EventBase. + * + * struct Foo { Foo(int a, int b); }; + * EventBaseLocal<Foo> myFoo; + * ... + * EventBase evb; + * myFoo.set(evb, new Foo(1, 2)); + * myFoo.set(evb, 1, 2); + * Foo* foo = myFoo.get(evb); + * myFoo.erase(evb); + * Foo& foo = myFoo.getOrCreate(evb, 1, 2); // ctor + * Foo& foo = myFoo.getOrCreate(evb, 1, 2); // no ctor + * myFoo.erase(evb); + * Foo& foo = myFoo.getOrCreateFn(evb, [] () { return new Foo(3, 4); }) + * + * The objects will be deleted when the EventBaseLocal or the EventBase is + * destructed (whichever comes first). All methods must be called from the + * EventBase thread. + * + * The user is responsible for throwing away invalid references/ptrs returned + * by the get() method after set/erase is called. If shared ownership is + * needed, use a EventBaseLocal<shared_ptr<...>>. + */ +template <typename T> +class EventBaseLocal : public detail::EventBaseLocalBase { + public: + EventBaseLocal() : EventBaseLocalBase() {} + + T* get(EventBase& evb) { + return static_cast<T*>(getVoid(evb)); + } + + void emplace(EventBase& evb, T* ptr) { + std::shared_ptr<T> smartPtr(ptr); + setVoid(evb, std::move(smartPtr)); + } + + template <typename... Args> + void emplace(EventBase& evb, Args&&... args) { + auto smartPtr = std::make_shared<T>(std::forward<Args>(args)...); + setVoid(evb, smartPtr); + } + + template <typename... Args> + T& getOrCreate(EventBase& evb, Args&&... args) { + if (auto ptr = getVoid(evb)) { + return *static_cast<T*>(ptr); + } + auto smartPtr = std::make_shared<T>(std::forward<Args>(args)...); + auto& ref = *smartPtr; + setVoid(evb, std::move(smartPtr)); + return ref; + } + + template <typename Func> + T& getOrCreateFn(EventBase& evb, Func fn) { + // If this looks like it's copy/pasted from above, that's because it is. + // gcc has a bug (fixed in 4.9) that doesn't allow capturing variadic + // params in a lambda. + if (auto ptr = getVoid(evb)) { + return *static_cast<T*>(ptr); + } + std::shared_ptr<T> smartPtr(fn()); + auto& ref = *smartPtr; + setVoid(evb, std::move(smartPtr)); + return ref; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.cpp new file mode 100644 index 000000000..1950c8009 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/EventBaseManager.h> + +namespace folly { + +std::atomic<EventBaseManager*> globalManager(nullptr); + +EventBaseManager* EventBaseManager::get() { + EventBaseManager* mgr = globalManager; + if (mgr) { + return mgr; + } + + auto new_mgr = new EventBaseManager; + bool exchanged = globalManager.compare_exchange_strong(mgr, new_mgr); + if (!exchanged) { + delete new_mgr; + return mgr; + } else { + return new_mgr; + } +} + +/* + * EventBaseManager methods + */ + +void EventBaseManager::setEventBase(EventBase* eventBase, bool takeOwnership) { + EventBaseInfo* info = localStore_.get(); + if (info != nullptr) { + throw std::runtime_error( + "EventBaseManager: cannot set a new EventBase " + "for this thread when one already exists"); + } + + info = new EventBaseInfo(eventBase, takeOwnership); + localStore_.reset(info); + this->trackEventBase(eventBase); +} + +void EventBaseManager::clearEventBase() { + EventBaseInfo* info = localStore_.get(); + if (info != nullptr) { + this->untrackEventBase(info->eventBase); + this->localStore_.reset(nullptr); + } +} + +// XXX should this really be "const"? +EventBase* EventBaseManager::getEventBase() const { + // have one? + auto* info = localStore_.get(); + if (!info) { + info = func_ ? new EventBaseInfo(func_()) : new EventBaseInfo(); + localStore_.reset(info); + + if (observer_) { + info->eventBase->setObserver(observer_); + } + + // start tracking the event base + // XXX + // note: ugly cast because this does something mutable + // even though this method is defined as "const". + // Simply removing the const causes trouble all over fbcode; + // lots of services build a const EventBaseManager and errors + // abound when we make this non-const. + (const_cast<EventBaseManager*>(this))->trackEventBase(info->eventBase); + } + + return info->eventBase; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.h b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.h new file mode 100644 index 000000000..4fe8765e0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseManager.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <list> +#include <set> + +#include <folly/ThreadLocal.h> +#include <folly/io/async/EventBase.h> + +namespace folly { + +/** + * Manager for per-thread EventBase objects. + * This class will find or create a EventBase for the current + * thread, associated with thread-specific storage for that thread. + * Although a typical application will generally only have one + * EventBaseManager, there is no restriction on multiple instances; + * the EventBases belong to one instance are isolated from those of + * another. + */ +class EventBaseManager { + public: + // XXX Constructing a EventBaseManager directly is DEPRECATED and not + // encouraged. You should instead use the global singleton if possible. + EventBaseManager() {} + + explicit EventBaseManager(folly::EventBaseBackendBase::FactoryFunc func) + : func_(func) {} + + ~EventBaseManager() {} + + explicit EventBaseManager(const std::shared_ptr<EventBaseObserver>& observer) + : observer_(observer) {} + + /** + * Get the global EventBaseManager for this program. Ideally all users + * of EventBaseManager go through this interface and do not construct + * EventBaseManager directly. + */ + static EventBaseManager* get(); + + /** + * Get the EventBase for this thread, or create one if none exists yet. + * + * If no EventBase exists for this thread yet, a new one will be created and + * returned. May throw std::bad_alloc if allocation fails. + */ + EventBase* getEventBase() const; + + /** + * Get the EventBase for this thread. + * + * Returns nullptr if no EventBase has been created for this thread yet. + */ + EventBase* getExistingEventBase() const { + EventBaseInfo* info = localStore_.get(); + if (info == nullptr) { + return nullptr; + } + return info->eventBase; + } + + /** + * Set the EventBase to be used by this thread. + * + * This may only be called if no EventBase has been defined for this thread + * yet. If a EventBase is already defined for this thread, a + * std::runtime_error is thrown. std::bad_alloc may also be thrown if + * allocation fails while setting the EventBase. + * + * This should typically be invoked by the code that will call loop() on the + * EventBase, to make sure the EventBaseManager points to the correct + * EventBase that is actually running in this thread. + */ + void setEventBase(EventBase* eventBase, bool takeOwnership); + + /** + * Clear the EventBase for this thread. + * + * This can be used if the code driving the EventBase loop() has finished + * the loop and new events should no longer be added to the EventBase. + */ + void clearEventBase(); + + /** + * Gives the caller all references to all assigned EventBase instances at + * this moment in time. Locks a mutex so that these EventBase set cannot + * be changed, and also the caller can rely on no instances being destructed. + */ + template <typename FunctionType> + void withEventBaseSet(const FunctionType& runnable) { + // grab the mutex for the caller + std::lock_guard<std::mutex> g(*&eventBaseSetMutex_); + // give them only a const set to work with + const std::set<EventBase*>& constSet = eventBaseSet_; + runnable(constSet); + } + + private: + struct EventBaseInfo { + EventBaseInfo(EventBase* evb, bool owned) : eventBase(evb), owned_(owned) {} + explicit EventBaseInfo(std::unique_ptr<EventBaseBackendBase>&& evb) + : eventBase(new EventBase(std::move(evb))), owned_(true) {} + EventBaseInfo() : eventBase(new EventBase), owned_(true) {} + + EventBase* eventBase; + bool owned_; + ~EventBaseInfo() { + if (owned_) { + delete eventBase; + } + } + }; + + // Forbidden copy constructor and assignment opererator + EventBaseManager(EventBaseManager const&); + EventBaseManager& operator=(EventBaseManager const&); + + void trackEventBase(EventBase* evb) { + std::lock_guard<std::mutex> g(*&eventBaseSetMutex_); + eventBaseSet_.insert(evb); + } + + void untrackEventBase(EventBase* evb) { + std::lock_guard<std::mutex> g(*&eventBaseSetMutex_); + eventBaseSet_.erase(evb); + } + + folly::EventBaseBackendBase::FactoryFunc func_; + + mutable folly::ThreadLocalPtr<EventBaseInfo> localStore_; + + // set of "active" EventBase instances + // (also see the mutex "eventBaseSetMutex_" below + // which governs access to this). + mutable std::set<EventBase*> eventBaseSet_; + + // a mutex to use as a guard for the above set + std::mutex eventBaseSetMutex_; + + std::shared_ptr<folly::EventBaseObserver> observer_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.cpp new file mode 100644 index 000000000..4b4fabdd8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/EventBaseThread.h> + +#include <folly/Memory.h> +#include <folly/io/async/ScopedEventBaseThread.h> + +namespace folly { + +EventBaseThread::EventBaseThread() : EventBaseThread(true) {} + +EventBaseThread::EventBaseThread( + bool autostart, + EventBaseManager* ebm, + folly::StringPiece threadName) + : ebm_(ebm) { + if (autostart) { + start(threadName); + } +} + +EventBaseThread::EventBaseThread( + bool autostart, + std::unique_ptr<EventBaseBackendBase>&& evb, + EventBaseManager* ebm, + folly::StringPiece threadName) + : ebm_(ebm), evb_(std::move(evb)) { + if (autostart) { + start(threadName); + } +} + +EventBaseThread::EventBaseThread(EventBaseManager* ebm) + : EventBaseThread(true, ebm) {} + +EventBaseThread::~EventBaseThread() = default; + +EventBaseThread::EventBaseThread(EventBaseThread&&) noexcept = default; +EventBaseThread& EventBaseThread::operator=(EventBaseThread&&) noexcept = + default; + +EventBase* EventBaseThread::getEventBase() const { + return th_ ? th_->getEventBase() : nullptr; +} + +bool EventBaseThread::running() const { + return !!th_; +} + +void EventBaseThread::start(folly::StringPiece threadName) { + if (th_) { + return; + } + th_ = std::make_unique<ScopedEventBaseThread>( + std::move(evb_), ebm_, threadName); +} + +void EventBaseThread::stop() { + th_ = nullptr; +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.h b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.h new file mode 100644 index 000000000..77e4d3735 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventBaseThread.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Range.h> +#include <memory> + +namespace folly { + +class EventBase; +class EventBaseBackendBase; +class EventBaseManager; +class ScopedEventBaseThread; + +class EventBaseThread { + public: + EventBaseThread(); + explicit EventBaseThread( + bool autostart, + EventBaseManager* ebm = nullptr, + folly::StringPiece threadName = folly::StringPiece()); + EventBaseThread( + bool autostart, + std::unique_ptr<EventBaseBackendBase>&& evb, + EventBaseManager* ebm = nullptr, + folly::StringPiece threadName = folly::StringPiece()); + explicit EventBaseThread(EventBaseManager* ebm); + ~EventBaseThread(); + + EventBaseThread(EventBaseThread const&) = delete; + EventBaseThread& operator=(EventBaseThread const&) = delete; + EventBaseThread(EventBaseThread&&) noexcept; + EventBaseThread& operator=(EventBaseThread&&) noexcept; + + EventBase* getEventBase() const; + + bool running() const; + void start(folly::StringPiece threadName = folly::StringPiece()); + void stop(); + + private: + EventBaseManager* ebm_; + std::unique_ptr<EventBaseBackendBase> evb_; + std::unique_ptr<ScopedEventBaseThread> th_; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventFDWrapper.h b/ios/Pods/Flipper-Folly/folly/io/async/EventFDWrapper.h new file mode 100644 index 000000000..2c483a926 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventFDWrapper.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Work around the lack of <sys/eventfd.h> on glibc 2.5.1 which we still + * need to support, sigh. + */ + +#pragma once + +#include <folly/portability/Config.h> + +#if defined(__GLIBC__) && !defined(__APPLE__) +#if __GLIBC_PREREQ(2, 9) +#define FOLLY_GLIBC_2_9 +#endif +#endif + +// <sys/eventfd.h> doesn't exist on older glibc versions +#ifdef FOLLY_GLIBC_2_9 +#include <sys/eventfd.h> +#else /* !def FOLLY_GLIBC_2_9 */ + +#include <fcntl.h> +#include <sys/syscall.h> +#include <unistd.h> + +// Use existing __NR_eventfd2 if already defined +// Values from the Linux kernel source: +// arch/x86/include/asm/unistd_{32,64}.h +#ifndef __NR_eventfd2 +#if FOLLY_X64 +/* nolint */ +#define __NR_eventfd2 290 +#elif defined(__i386__) +/* nolint */ +#define __NR_eventfd2 328 +#else +#error "Can't define __NR_eventfd2 for your architecture." +#endif +#endif + +enum { + EFD_SEMAPHORE = 1, +#define EFD_SEMAPHORE EFD_SEMAPHORE + EFD_CLOEXEC = 02000000, +#define EFD_CLOEXEC EFD_CLOEXEC + EFD_NONBLOCK = 04000 +#define EFD_NONBLOCK EFD_NONBLOCK +}; + +// http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html +// Use the eventfd2 system call, as in glibc 2.9+ +// (requires kernel 2.6.30+) +#define eventfd(initval, flags) syscall(__NR_eventfd2, (initval), (flags)) + +#endif /* !(defined(__GLIBC__) && __GLIBC_PREREQ(2, 9)) */ diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.cpp b/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.cpp new file mode 100644 index 000000000..a09a2bc07 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/EventHandler.h> +#include <folly/String.h> +#include <folly/io/async/EventBase.h> + +#include <cassert> + +namespace folly { + +EventHandler::EventHandler(EventBase* eventBase, NetworkSocket fd) { + event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this); + if (eventBase != nullptr) { + setEventBase(eventBase); + } else { + // Callers must set the EventBase and fd before using this timeout. + // Set event_->ev_base to nullptr to ensure that this happens. + // (otherwise libevent will initialize it to the "default" event_base) + event_.eb_ev_base(nullptr); + eventBase_ = nullptr; + } +} + +EventHandler::~EventHandler() { + unregisterHandler(); +} + +bool EventHandler::registerImpl(uint16_t events, bool internal) { + assert(event_.eb_ev_base() != nullptr); + + // We have to unregister the event before we can change the event flags + if (isHandlerRegistered()) { + // If the new events are the same are the same as the already registered + // flags, we don't have to do anything. Just return. + auto flags = folly::event_ref_flags(event_.getEvent()); + if (events == event_.eb_ev_events() && + static_cast<bool>(flags & EVLIST_INTERNAL) == internal) { + return true; + } + + event_.eb_event_del(); + } + + // Update the event flags + // Unfortunately, event_set() resets the event_base, so we have to remember + // it before hand, then pass it back into event_base_set() afterwards + auto* evb = event_.eb_ev_base(); + event_.eb_event_set( + event_.eb_ev_fd(), short(events), &EventHandler::libeventCallback, this); + event_.eb_event_base_set(evb); + + // Set EVLIST_INTERNAL if this is an internal event + if (internal) { + folly::event_ref_flags(event_.getEvent()) |= EVLIST_INTERNAL; + } + + // Add the event. + // + // Although libevent allows events to wait on both I/O and a timeout, + // we intentionally don't allow an EventHandler to also use a timeout. + // Callers must maintain a separate AsyncTimeout object if they want a + // timeout. + // + // Otherwise, it is difficult to handle persistent events properly. (The I/O + // event and timeout may both fire together the same time around the event + // loop. Normally we would want to inform the caller of the I/O event first, + // then the timeout. However, it is difficult to do this properly since the + // I/O callback could delete the EventHandler.) Additionally, if a caller + // uses the same struct event for both I/O and timeout, and they just want to + // reschedule the timeout, libevent currently makes an epoll_ctl() call even + // if the I/O event flags haven't changed. Using a separate event struct is + // therefore slightly more efficient in this case (although it does take up + // more space). + if (event_.eb_event_add(nullptr) < 0) { + LOG(ERROR) << "EventBase: failed to register event handler for fd " + << event_.eb_ev_fd() << ": " << errnoStr(errno); + // Call event_del() to make sure the event is completely uninstalled + event_.eb_event_del(); + return false; + } + + return true; +} + +void EventHandler::unregisterHandler() { + if (isHandlerRegistered()) { + event_.eb_event_del(); + } +} + +void EventHandler::attachEventBase(EventBase* eventBase) { + // attachEventBase() may only be called on detached handlers + assert(event_.eb_ev_base() == nullptr); + assert(!isHandlerRegistered()); + // This must be invoked from the EventBase's thread + eventBase->dcheckIsInEventBaseThread(); + + setEventBase(eventBase); +} + +void EventHandler::detachEventBase() { + ensureNotRegistered(__func__); + event_.eb_ev_base(nullptr); +} + +void EventHandler::changeHandlerFD(NetworkSocket fd) { + ensureNotRegistered(__func__); + // event_set() resets event_base.ev_base, so manually restore it afterwards + auto* evb = event_.eb_ev_base(); + event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this); + event_.eb_ev_base( + evb); // don't use event_base_set(), since evb may be nullptr +} + +void EventHandler::initHandler(EventBase* eventBase, NetworkSocket fd) { + ensureNotRegistered(__func__); + event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this); + setEventBase(eventBase); +} + +void EventHandler::ensureNotRegistered(const char* fn) { + // Neither the EventBase nor file descriptor may be changed while the + // handler is registered. Treat it as a programmer bug and abort the program + // if this requirement is violated. + if (isHandlerRegistered()) { + LOG(ERROR) << fn << " called on registered handler; aborting"; + abort(); + } +} + +void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) { + auto handler = reinterpret_cast<EventHandler*>(arg); + assert(fd == handler->event_.eb_ev_fd()); + (void)fd; // prevent unused variable warnings + + auto observer = handler->eventBase_->getExecutionObserver(); + if (observer) { + observer->starting(reinterpret_cast<uintptr_t>(handler)); + } + + // this can't possibly fire if handler->eventBase_ is nullptr + handler->eventBase_->bumpHandlingTime(); + + handler->handlerReady(uint16_t(events)); + + if (observer) { + observer->stopped(reinterpret_cast<uintptr_t>(handler)); + } +} + +void EventHandler::setEventBase(EventBase* eventBase) { + event_.eb_event_base_set(eventBase); + eventBase_ = eventBase; +} + +bool EventHandler::isPending() const { + if (folly::event_ref_flags(event_.getEvent()) & EVLIST_ACTIVE) { + if (event_.eb_ev_res() & EV_READ) { + return true; + } + } + return false; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.h b/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.h new file mode 100644 index 000000000..e5f6a017b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventHandler.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> + +#include <glog/logging.h> + +#include <folly/io/async/EventBaseBackendBase.h> +#include <folly/io/async/EventUtil.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/Event.h> + +namespace folly { + +class EventBase; + +/** + * The EventHandler class is used to asynchronously wait for events on a file + * descriptor. + * + * Users that wish to wait on I/O events should derive from EventHandler and + * implement the handlerReady() method. + */ +class EventHandler { + public: + enum EventFlags { + NONE = 0, + READ = EV_READ, + WRITE = EV_WRITE, + READ_WRITE = (READ | WRITE), + PERSIST = EV_PERSIST, +// Temporary flag until EPOLLPRI is upstream on libevent. +#ifdef EV_PRI + PRI = EV_PRI, +#endif + }; + + /** + * Create a new EventHandler object. + * + * @param eventBase The EventBase to use to drive this event handler. + * This may be nullptr, in which case the EventBase must be + * set separately using initHandler() or attachEventBase() + * before the handler can be registered. + * @param fd The file descriptor that this EventHandler will + * monitor. This may be -1, in which case the file + * descriptor must be set separately using initHandler() or + * changeHandlerFD() before the handler can be registered. + */ + explicit EventHandler( + EventBase* eventBase = nullptr, + NetworkSocket fd = NetworkSocket()); + + EventHandler(const EventHandler&) = delete; + EventHandler& operator=(const EventHandler&) = delete; + + /** + * EventHandler destructor. + * + * The event will be automatically unregistered if it is still registered. + */ + virtual ~EventHandler(); + + /** + * handlerReady() is invoked when the handler is ready. + * + * @param events A bitset indicating the events that are ready. + */ + virtual void handlerReady(uint16_t events) noexcept = 0; + + /** + * Register the handler. + * + * If the handler is already registered, the registration will be updated + * to wait on the new set of events. + * + * @param events A bitset specifying the events to monitor. + * If the PERSIST bit is set, the handler will remain + * registered even after handlerReady() is called. + * + * @return Returns true if the handler was successfully registered, + * or false if an error occurred. After an error, the handler is + * always unregistered, even if it was already registered prior to + * this call to registerHandler(). + */ + bool registerHandler(uint16_t events) { + return registerImpl(events, false); + } + + /** + * Unregister the handler, if it is registered. + */ + void unregisterHandler(); + + /** + * Returns true if the handler is currently registered. + */ + bool isHandlerRegistered() const { + return event_.isEventRegistered(); + } + + /** + * Attach the handler to a EventBase. + * + * This may only be called if the handler is not currently attached to a + * EventBase (either by using the default constructor, or by calling + * detachEventBase()). + * + * This method must be invoked in the EventBase's thread. + */ + void attachEventBase(EventBase* eventBase); + + /** + * Detach the handler from its EventBase. + * + * This may only be called when the handler is not currently registered. + * Once detached, the handler may not be registered again until it is + * re-attached to a EventBase by calling attachEventBase(). + * + * This method must be called from the current EventBase's thread. + */ + void detachEventBase(); + + /** + * Change the file descriptor that this handler is associated with. + * + * This may only be called when the handler is not currently registered. + */ + void changeHandlerFD(NetworkSocket fd); + + /** + * Attach the handler to a EventBase, and change the file descriptor. + * + * This method may only be called if the handler is not currently attached to + * a EventBase. This is primarily intended to be used to initialize + * EventHandler objects created using the default constructor. + */ + void initHandler(EventBase* eventBase, NetworkSocket fd); + + /** + * Return the set of events that we're currently registered for. + */ + uint16_t getRegisteredEvents() const { + return (isHandlerRegistered()) ? (uint16_t)(event_.eb_ev_events()) : 0u; + } + + /** + * Register the handler as an internal event. + * + * This event will not count as an active event for determining if the + * EventBase loop has more events to process. The EventBase loop runs + * only as long as there are active EventHandlers, however "internal" event + * handlers are not counted. Therefore this event handler will not prevent + * EventBase loop from exiting with no more work to do if there are no other + * non-internal event handlers registered. + * + * This is intended to be used only in very rare cases by the internal + * EventBase code. This API is not guaranteed to remain stable or portable + * in the future. + */ + bool registerInternalHandler(uint16_t events) { + return registerImpl(events, true); + } + + bool isPending() const; + + private: + bool registerImpl(uint16_t events, bool internal); + void ensureNotRegistered(const char* fn); + + void setEventBase(EventBase* eventBase); + + static void libeventCallback(libevent_fd_t fd, short events, void* arg); + + EventBaseBackendBase::Event event_; + EventBase* eventBase_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/EventUtil.h b/ios/Pods/Flipper-Folly/folly/io/async/EventUtil.h new file mode 100644 index 000000000..f4b820368 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/EventUtil.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> + +#include <folly/portability/Event.h> + +namespace folly { + +#if LIBEVENT_VERSION_NUMBER <= 0x02010101 +#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_##name +#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) ev_##name +#else +#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_evcallback.evcb_##name +#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) \ + ev_evcallback.evcb_cb_union.evcb_##name +#endif +#define FOLLY_LIBEVENT_DEF_ACCESSORS(name) \ + inline auto event_ref_##name(struct event* ev) \ + ->decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) { \ + return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name)); \ + } \ + inline auto event_ref_##name(struct event const* ev) \ + ->decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) { \ + return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name)); \ + } \ + // + +#define FOLLY_LIBEVENT_DEF_ACCESSORS2(name) \ + inline auto event_ref_##name(struct event* ev) \ + ->decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \ + return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name)); \ + } \ + inline auto event_ref_##name(struct event const* ev) \ + ->decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \ + return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name)); \ + } \ + // + +FOLLY_LIBEVENT_DEF_ACCESSORS(arg) +FOLLY_LIBEVENT_DEF_ACCESSORS(flags) +// evcb_callback is inside a union{...} evcb_cb_union +FOLLY_LIBEVENT_DEF_ACCESSORS2(callback) + +#undef FOLLY_LIBEVENT_COMPAT_PLUCK +#undef FOLLY_LIBEVENT_DEF_ACCESSORS + +/** + * low-level libevent utility functions + */ +class EventUtil { + public: + static bool isEventRegistered(const struct event* ev) { + // If any of these flags are set, the event is registered. + enum { + EVLIST_REGISTERED = + (EVLIST_INSERTED | EVLIST_ACTIVE | EVLIST_TIMEOUT | EVLIST_SIGNAL) + }; + return (event_ref_flags(ev) & EVLIST_REGISTERED); + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h new file mode 100644 index 000000000..7f77f7439 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include <chrono> + +namespace folly { +template <class Duration> +class HHWheelTimerBase; +using HHWheelTimer = HHWheelTimerBase<std::chrono::milliseconds>; +using HHWheelTimerHighRes = HHWheelTimerBase<std::chrono::microseconds>; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.cpp b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.cpp new file mode 100644 index 000000000..20bd1ef67 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/HHWheelTimer.h> + +#include <cassert> + +#include <folly/Memory.h> +#include <folly/Optional.h> +#include <folly/ScopeGuard.h> +#include <folly/container/BitIterator.h> +#include <folly/io/async/Request.h> +#include <folly/lang/Bits.h> + +namespace folly { +/** + * We want to select the default interval carefully. + * An interval of 10ms will give us 10ms * WHEEL_SIZE^WHEEL_BUCKETS + * for the largest timeout possible, or about 497 days. + * + * For a lower bound, we want a reasonable limit on local IO, 10ms + * seems short enough + * + * A shorter interval also has CPU implications, less than 1ms might + * start showing up in cpu perf. Also, it might not be possible to set + * tick interval less than 10ms on older kernels. + */ + +/* + * For high res timers: + * An interval of 200usec will give us 200usec * WHEEL_SIZE^WHEEL_BUCKETS + * for the largest timeout possible, or about 9 days. + */ + +template <class Duration> +int HHWheelTimerBase<Duration>::DEFAULT_TICK_INTERVAL = + detail::HHWheelTimerDurationConst<Duration>::DEFAULT_TICK_INTERVAL; + +template <class Duration> +HHWheelTimerBase<Duration>::Callback::Callback() = default; + +template <class Duration> +HHWheelTimerBase<Duration>::Callback::~Callback() { + if (isScheduled()) { + cancelTimeout(); + } +} + +template <class Duration> +void HHWheelTimerBase<Duration>::Callback::setScheduled( + HHWheelTimerBase* wheel, + std::chrono::steady_clock::time_point deadline) { + assert(wheel_ == nullptr); + assert(expiration_ == decltype(expiration_){}); + + wheel_ = wheel; + expiration_ = deadline; +} + +template <class Duration> +void HHWheelTimerBase<Duration>::Callback::cancelTimeoutImpl() { + if (--wheel_->count_ <= 0) { + assert(wheel_->count_ == 0); + wheel_->AsyncTimeout::cancelTimeout(); + } + unlink(); + if ((-1 != bucket_) && (wheel_->buckets_[0][bucket_].empty())) { + auto bi = makeBitIterator(wheel_->bitmap_.begin()); + *(bi + bucket_) = false; + } + + wheel_ = nullptr; + expiration_ = {}; +} + +template <class Duration> +HHWheelTimerBase<Duration>::HHWheelTimerBase( + folly::TimeoutManager* timeoutMananger, + Duration intervalDuration, + AsyncTimeout::InternalEnum internal, + Duration defaultTimeoutDuration) + : AsyncTimeout(timeoutMananger, internal), + interval_(intervalDuration), + defaultTimeout_(defaultTimeoutDuration), + expireTick_(1), + count_(0), + startTime_(getCurTime()), + processingCallbacksGuard_(nullptr) { + bitmap_.fill(0); +} + +template <class Duration> +HHWheelTimerBase<Duration>::~HHWheelTimerBase() { + // Ensure this gets done, but right before destruction finishes. + auto destructionPublisherGuard = folly::makeGuard([&] { + // Inform the subscriber that this instance is doomed. + if (processingCallbacksGuard_) { + *processingCallbacksGuard_ = true; + } + }); + cancelAll(); +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleTimeoutImpl( + Callback* callback, + int64_t dueTick, + int64_t nextTickToProcess, + int64_t nextTick) { + int64_t diff = dueTick - nextTickToProcess; + CallbackList* list; + + auto bi = makeBitIterator(bitmap_.begin()); + + if (diff < 0) { + list = &buckets_[0][nextTick & WHEEL_MASK]; + *(bi + (nextTick & WHEEL_MASK)) = true; + callback->bucket_ = nextTick & WHEEL_MASK; + } else if (diff < WHEEL_SIZE) { + list = &buckets_[0][dueTick & WHEEL_MASK]; + *(bi + (dueTick & WHEEL_MASK)) = true; + callback->bucket_ = dueTick & WHEEL_MASK; + } else if (diff < 1 << (2 * WHEEL_BITS)) { + list = &buckets_[1][(dueTick >> WHEEL_BITS) & WHEEL_MASK]; + } else if (diff < 1 << (3 * WHEEL_BITS)) { + list = &buckets_[2][(dueTick >> 2 * WHEEL_BITS) & WHEEL_MASK]; + } else { + /* in largest slot */ + if (diff > LARGEST_SLOT) { + diff = LARGEST_SLOT; + dueTick = diff + nextTickToProcess; + } + list = &buckets_[3][(dueTick >> 3 * WHEEL_BITS) & WHEEL_MASK]; + } + list->push_back(*callback); +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleTimeout( + Callback* callback, + Duration timeout) { + // Make sure that the timeout is not negative. + timeout = std::max(timeout, Duration::zero()); + // Cancel the callback if it happens to be scheduled already. + callback->cancelTimeout(); + callback->requestContext_ = RequestContext::saveContext(); + + count_++; + + auto now = getCurTime(); + auto nextTick = calcNextTick(now); + callback->setScheduled(this, now + timeout); + + // There are three possible scenarios: + // - we are currently inside of HHWheelTimerBase<Duration>::timeoutExpired. + // In this case, + // we need to use its last tick as a base for computations + // - HHWheelTimerBase tick timeout is already scheduled. In this case, + // we need to use its scheduled tick as a base. + // - none of the above are true. In this case, it's safe to use the nextTick + // as a base. + int64_t baseTick = nextTick; + if (processingCallbacksGuard_ || isScheduled()) { + baseTick = std::min(expireTick_, nextTick); + } + int64_t ticks = timeToWheelTicks(timeout); + int64_t due = ticks + nextTick; + scheduleTimeoutImpl(callback, due, baseTick, nextTick); + + /* If we're calling callbacks, timer will be reset after all + * callbacks are called. + */ + if (!processingCallbacksGuard_) { + // Check if we need to reschedule the timer. + // If the wheel timeout is already scheduled, then we need to reschedule + // only if our due is earlier than the current scheduled one. + // If it's not scheduled, we need to schedule it either for the first tick + // of next wheel epoch or our due tick, whichever is earlier. + if (!isScheduled() && !inSameEpoch(nextTick - 1, due)) { + scheduleNextTimeout(nextTick, WHEEL_SIZE - ((nextTick - 1) & WHEEL_MASK)); + } else if (!isScheduled() || due < expireTick_) { + scheduleNextTimeout(nextTick, ticks + 1); + } + } +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleTimeout(Callback* callback) { + CHECK(Duration(-1) != defaultTimeout_) + << "Default timeout was not initialized"; + scheduleTimeout(callback, defaultTimeout_); +} + +template <class Duration> +bool HHWheelTimerBase<Duration>::cascadeTimers( + int bucket, + int tick, + const std::chrono::steady_clock::time_point curTime) { + CallbackList cbs; + cbs.swap(buckets_[bucket][tick]); + auto nextTick = calcNextTick(curTime); + while (!cbs.empty()) { + auto* cb = &cbs.front(); + cbs.pop_front(); + scheduleTimeoutImpl( + cb, + nextTick + timeToWheelTicks(cb->getTimeRemaining(curTime)), + expireTick_, + nextTick); + } + + // If tick is zero, timeoutExpired will cascade the next bucket. + return tick == 0; +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleTimeoutInternal(Duration timeout) { + this->AsyncTimeout::scheduleTimeout(timeout); +} + +template <class Duration> +void HHWheelTimerBase<Duration>::timeoutExpired() noexcept { + auto curTime = getCurTime(); + auto nextTick = calcNextTick(curTime); + + // If the last smart pointer for "this" is reset inside the callback's + // timeoutExpired(), then the guard will detect that it is time to bail from + // this method. + auto isDestroyed = false; + // If scheduleTimeout is called from a callback in this function, it may + // cause inconsistencies in the state of this object. As such, we need + // to treat these calls slightly differently. + CHECK(!processingCallbacksGuard_); + processingCallbacksGuard_ = &isDestroyed; + auto reEntryGuard = folly::makeGuard([&] { + if (!isDestroyed) { + processingCallbacksGuard_ = nullptr; + } + }); + + // timeoutExpired() can only be invoked directly from the event base loop. + // It should never be invoked recursively. + // + while (expireTick_ < nextTick) { + int idx = expireTick_ & WHEEL_MASK; + + if (idx == 0) { + // Cascade timers + if (cascadeTimers(1, (expireTick_ >> WHEEL_BITS) & WHEEL_MASK, curTime) && + cascadeTimers( + 2, (expireTick_ >> (2 * WHEEL_BITS)) & WHEEL_MASK, curTime)) { + cascadeTimers( + 3, (expireTick_ >> (3 * WHEEL_BITS)) & WHEEL_MASK, curTime); + } + } + + auto bi = makeBitIterator(bitmap_.begin()); + *(bi + idx) = false; + + expireTick_++; + CallbackList* cbs = &buckets_[0][idx]; + while (!cbs->empty()) { + auto* cb = &cbs->front(); + cbs->pop_front(); + timeoutsToRunNow_.push_back(*cb); + } + } + + while (!timeoutsToRunNow_.empty()) { + auto* cb = &timeoutsToRunNow_.front(); + timeoutsToRunNow_.pop_front(); + count_--; + cb->wheel_ = nullptr; + cb->expiration_ = {}; + RequestContextScopeGuard rctx(cb->requestContext_); + cb->timeoutExpired(); + if (isDestroyed) { + // The HHWheelTimerBase itself has been destroyed. The other callbacks + // will have been cancelled from the destructor. Bail before causing + // damage. + return; + } + } + + // We don't need to schedule a new timeout if there're nothing in the wheel. + if (count_ > 0) { + scheduleNextTimeout(expireTick_); + } +} + +template <class Duration> +size_t HHWheelTimerBase<Duration>::cancelAll() { + size_t count = 0; + + if (count_ != 0) { + const std::size_t numElements = WHEEL_BUCKETS * WHEEL_SIZE; + auto maxBuckets = std::min(numElements, count_); + auto buckets = std::make_unique<CallbackList[]>(maxBuckets); + size_t countBuckets = 0; + for (auto& tick : buckets_) { + for (auto& bucket : tick) { + if (bucket.empty()) { + continue; + } + count += bucket.size(); + std::swap(bucket, buckets[countBuckets++]); + if (count >= count_) { + break; + } + } + } + + for (size_t i = 0; i < countBuckets; ++i) { + cancelTimeoutsFromList(buckets[i]); + } + // Swap the list to prevent potential recursion if cancelAll is called by + // one of the callbacks. + CallbackList timeoutsToRunNow; + timeoutsToRunNow.swap(timeoutsToRunNow_); + count += cancelTimeoutsFromList(timeoutsToRunNow); + } + + return count; +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleNextTimeout(int64_t nextTick) { + int64_t tick = 1; + + if (nextTick & WHEEL_MASK) { + auto bi = makeBitIterator(bitmap_.begin()); + auto bi_end = makeBitIterator(bitmap_.end()); + auto it = folly::findFirstSet(bi + (nextTick & WHEEL_MASK), bi_end); + if (it == bi_end) { + tick = WHEEL_SIZE - ((nextTick - 1) & WHEEL_MASK); + } else { + tick = std::distance(bi + (nextTick & WHEEL_MASK), it) + 1; + } + } + + scheduleNextTimeout(nextTick, tick); +} + +template <class Duration> +void HHWheelTimerBase<Duration>::scheduleNextTimeout( + int64_t nextTick, + int64_t ticks) { + scheduleTimeoutInternal(interval_ * ticks); + expireTick_ = ticks + nextTick - 1; +} + +template <class Duration> +size_t HHWheelTimerBase<Duration>::cancelTimeoutsFromList( + CallbackList& timeouts) { + size_t count = 0; + while (!timeouts.empty()) { + ++count; + auto& cb = timeouts.front(); + cb.cancelTimeout(); + cb.callbackCanceled(); + } + return count; +} + +template <class Duration> +int64_t HHWheelTimerBase<Duration>::calcNextTick() { + return calcNextTick(getCurTime()); +} + +template <class Duration> +int64_t HHWheelTimerBase<Duration>::calcNextTick( + std::chrono::steady_clock::time_point curTime) { + return (curTime - startTime_) / interval_; +} + +// std::chrono::microseconds +template <> +void HHWheelTimerBase<std::chrono::microseconds>::scheduleTimeoutInternal( + std::chrono::microseconds timeout) { + this->AsyncTimeout::scheduleTimeoutHighRes(timeout); +} + +// std::chrono::milliseconds +template class HHWheelTimerBase<std::chrono::milliseconds>; + +// std::chrono::microseconds +template class HHWheelTimerBase<std::chrono::microseconds>; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.h b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.h new file mode 100644 index 000000000..ed9149f33 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/HHWheelTimer.h @@ -0,0 +1,376 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> +#include <folly/io/async/AsyncTimeout.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/HHWheelTimer-fwd.h> + +#include <boost/intrusive/list.hpp> +#include <glog/logging.h> + +#include <array> +#include <chrono> +#include <cstddef> +#include <memory> + +namespace folly { + +namespace detail { +template <class Duration> +struct HHWheelTimerDurationConst; + +template <> +struct HHWheelTimerDurationConst<std::chrono::milliseconds> { + static constexpr int DEFAULT_TICK_INTERVAL = 10; +}; + +template <> +struct HHWheelTimerDurationConst<std::chrono::microseconds> { + static constexpr int DEFAULT_TICK_INTERVAL = 200; +}; +} // namespace detail + +/** + * Hashed Hierarchical Wheel Timer + * + * We model timers as the number of ticks until the next + * due event. We allow 32-bits of space to track this + * due interval, and break that into 4 regions of 8 bits. + * Each region indexes into a bucket of 256 lists. + * + * Bucket 0 represents those events that are due the soonest. + * Each tick causes us to look at the next list in a bucket. + * The 0th list in a bucket is special; it means that it is time to + * flush the timers from the next higher bucket and schedule them + * into a different bucket. + * + * This technique results in a very cheap mechanism for + * maintaining time and timers. + * + * Unlike the original timer wheel paper, this implementation does + * *not* tick constantly, and instead calculates the exact next wakeup + * time. + */ +template <class Duration> +class HHWheelTimerBase : private folly::AsyncTimeout, + public folly::DelayedDestruction { + public: + using UniquePtr = std::unique_ptr<HHWheelTimerBase, Destructor>; + using SharedPtr = std::shared_ptr<HHWheelTimerBase>; + + template <typename... Args> + static UniquePtr newTimer(Args&&... args) { + return UniquePtr(new HHWheelTimerBase(std::forward<Args>(args)...)); + } + + /** + * A callback to be notified when a timeout has expired. + */ + class Callback + : public boost::intrusive::list_base_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>> { + public: + Callback(); + virtual ~Callback(); + + /** + * timeoutExpired() is invoked when the timeout has expired. + */ + virtual void timeoutExpired() noexcept = 0; + + /// This callback was canceled. The default implementation is to just + /// proxy to `timeoutExpired` but if you care about the difference between + /// the timeout finishing or being canceled you can override this. + virtual void callbackCanceled() noexcept { + timeoutExpired(); + } + + /** + * Cancel the timeout, if it is running. + * + * If the timeout is not scheduled, cancelTimeout() does nothing. + */ + void cancelTimeout() { + if (wheel_ == nullptr) { + // We're not scheduled, so there's nothing to do. + return; + } + cancelTimeoutImpl(); + } + + /** + * Return true if this timeout is currently scheduled, and false otherwise. + */ + bool isScheduled() const { + return wheel_ != nullptr; + } + + /** + * Get the time remaining until this timeout expires. Return 0 if this + * timeout is not scheduled or expired. Otherwise, return expiration + * time minus current time. + */ + Duration getTimeRemaining() const { + return getTimeRemaining(std::chrono::steady_clock::now()); + } + + private: + // Get the time remaining until this timeout expires + Duration getTimeRemaining(std::chrono::steady_clock::time_point now) const { + if (now >= expiration_) { + return Duration(0); + } + return std::chrono::duration_cast<Duration>(expiration_ - now); + } + + void setScheduled( + HHWheelTimerBase* wheel, + std::chrono::steady_clock::time_point deadline); + void cancelTimeoutImpl(); + + HHWheelTimerBase* wheel_{nullptr}; + std::chrono::steady_clock::time_point expiration_{}; + int bucket_{-1}; + + typedef boost::intrusive:: + list<Callback, boost::intrusive::constant_time_size<false>> + List; + + std::shared_ptr<RequestContext> requestContext_; + + // Give HHWheelTimerBase direct access to our members so it can take care + // of scheduling/cancelling. + friend class HHWheelTimerBase; + }; + + /** + * Create a new HHWheelTimerBase with the specified interval and the + * default timeout value set. + * + * Objects created using this version of constructor can be used + * to schedule both variable interval timeouts using + * scheduleTimeout(callback, timeout) method, and default + * interval timeouts using scheduleTimeout(callback) method. + */ + static int DEFAULT_TICK_INTERVAL; + explicit HHWheelTimerBase( + folly::TimeoutManager* timeoutMananger, + Duration intervalDuration = Duration(DEFAULT_TICK_INTERVAL), + AsyncTimeout::InternalEnum internal = AsyncTimeout::InternalEnum::NORMAL, + Duration defaultTimeoutDuration = Duration(-1)); + + /** + * Cancel all outstanding timeouts + * + * @returns the number of timeouts that were cancelled. + */ + size_t cancelAll(); + + /** + * Get the tick interval for this HHWheelTimerBase. + * + * Returns the tick interval in milliseconds. + */ + Duration getTickInterval() const { + return interval_; + } + + /** + * Get the default timeout interval for this HHWheelTimerBase. + * + * Returns the timeout interval in milliseconds. + */ + Duration getDefaultTimeout() const { + return defaultTimeout_; + } + + /** + * Set the default timeout interval for this HHWheelTimerBase. + */ + void setDefaultTimeout(Duration timeout) { + defaultTimeout_ = timeout; + } + + /** + * Schedule the specified Callback to be invoked after the + * specified timeout interval. + * + * If the callback is already scheduled, this cancels the existing timeout + * before scheduling the new timeout. + */ + void scheduleTimeout(Callback* callback, Duration timeout); + + /** + * Schedule the specified Callback to be invoked after the + * default timeout interval. + * + * If the callback is already scheduled, this cancels the existing timeout + * before scheduling the new timeout. + * + * This method uses CHECK() to make sure that the default timeout was + * specified on the object initialization. + */ + void scheduleTimeout(Callback* callback); + + template <class F> + void scheduleTimeoutFn(F fn, Duration timeout) { + struct Wrapper : Callback { + Wrapper(F f) : fn_(std::move(f)) {} + void timeoutExpired() noexcept override { + try { + fn_(); + } catch (std::exception const& e) { + LOG(ERROR) << "HHWheelTimerBase timeout callback threw an exception: " + << e.what(); + } catch (...) { + LOG(ERROR) + << "HHWheelTimerBase timeout callback threw a non-exception."; + } + delete this; + } + F fn_; + }; + Wrapper* w = new Wrapper(std::move(fn)); + scheduleTimeout(w, timeout); + } + + /** + * Return the number of currently pending timeouts + */ + std::size_t count() const { + return count_; + } + + bool isDetachable() const { + return !folly::AsyncTimeout::isScheduled(); + } + + using folly::AsyncTimeout::attachEventBase; + using folly::AsyncTimeout::detachEventBase; + using folly::AsyncTimeout::getTimeoutManager; + + protected: + /** + * Protected destructor. + * + * Use destroy() instead. See the comments in DelayedDestruction for more + * details. + */ + ~HHWheelTimerBase() override; + + private: + // Forbidden copy constructor and assignment operator + HHWheelTimerBase(HHWheelTimerBase const&) = delete; + HHWheelTimerBase& operator=(HHWheelTimerBase const&) = delete; + + // Methods inherited from AsyncTimeout + void timeoutExpired() noexcept override; + + Duration interval_; + Duration defaultTimeout_; + + static constexpr int WHEEL_BUCKETS = 4; + static constexpr int WHEEL_BITS = 8; + static constexpr unsigned int WHEEL_SIZE = (1 << WHEEL_BITS); + static constexpr unsigned int WHEEL_MASK = (WHEEL_SIZE - 1); + static constexpr uint32_t LARGEST_SLOT = 0xffffffffUL; + + typedef typename Callback::List CallbackList; + CallbackList buckets_[WHEEL_BUCKETS][WHEEL_SIZE]; + std::array<std::size_t, (WHEEL_SIZE / sizeof(std::size_t)) / 8> bitmap_; + + int64_t timeToWheelTicks(Duration t) { + return t.count() / interval_.count(); + } + + bool cascadeTimers( + int bucket, + int tick, + std::chrono::steady_clock::time_point curTime); + void scheduleTimeoutInternal(Duration timeout); + + int64_t expireTick_; + std::size_t count_; + std::chrono::steady_clock::time_point startTime_; + + int64_t calcNextTick(); + int64_t calcNextTick(std::chrono::steady_clock::time_point curTime); + + static bool inSameEpoch(int64_t tickA, int64_t tickB) { + return (tickA >> WHEEL_BITS) == (tickB >> WHEEL_BITS); + } + + /** + * Schedule a given timeout by putting it into the appropriate bucket of the + * wheel. + * + * @param callback Callback to fire after `timeout` + * @param dueTick Tick at which the timer is due. + * @param nextTickToProcess next tick that was not processed by the timer + * yet. Can be less than nextTick if we're lagging. + * @param nextTick next tick based on the actual time + */ + void scheduleTimeoutImpl( + Callback* callback, + int64_t dueTick, + int64_t nextTickToProcess, + int64_t nextTick); + + /** + * Compute next required wheel tick to fire and schedule the timeout for that + * tick. + * + * @param nextTick next tick based on the actual time + */ + void scheduleNextTimeout(int64_t nextTick); + + /** + * Schedule next wheel timeout in a fixed number of wheel ticks. + * + * @param nextTick next tick based on the actual time + * @param ticks number of ticks in which the timer should fire + */ + void scheduleNextTimeout(int64_t nextTick, int64_t ticks); + + size_t cancelTimeoutsFromList(CallbackList& timeouts); + + bool* processingCallbacksGuard_; + // Timeouts that we're about to run. They're already extracted from their + // corresponding buckets, so we need this list for the `cancelAll` to be able + // to cancel them. + CallbackList timeoutsToRunNow_; + + std::chrono::steady_clock::time_point getCurTime() { + return std::chrono::steady_clock::now(); + } +}; + +// std::chrono::milliseconds +using HHWheelTimer = HHWheelTimerBase<std::chrono::milliseconds>; +extern template class HHWheelTimerBase<std::chrono::milliseconds>; + +// std::chrono::microseconds +template <> +void HHWheelTimerBase<std::chrono::microseconds>::scheduleTimeoutInternal( + std::chrono::microseconds timeout); + +using HHWheelTimerHighRes = HHWheelTimerBase<std::chrono::microseconds>; +extern template class HHWheelTimerBase<std::chrono::microseconds>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/NotificationQueue.h b/ios/Pods/Flipper-Folly/folly/io/async/NotificationQueue.h new file mode 100644 index 000000000..242e960f1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/NotificationQueue.h @@ -0,0 +1,917 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/types.h> + +#include <algorithm> +#include <iterator> +#include <memory> +#include <stdexcept> +#include <utility> + +#include <boost/intrusive/slist.hpp> +#include <folly/Exception.h> +#include <folly/FileUtil.h> +#include <folly/Likely.h> +#include <folly/ScopeGuard.h> +#include <folly/SpinLock.h> +#include <folly/io/async/DelayedDestruction.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/EventHandler.h> +#include <folly/io/async/Request.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Unistd.h> + +#include <glog/logging.h> + +#if defined(__linux__) && !defined(__ANDROID__) +#define FOLLY_HAVE_EVENTFD +#include <folly/io/async/EventFDWrapper.h> +#endif + +namespace folly { + +/** + * A producer-consumer queue for passing messages between EventBase threads. + * + * Messages can be added to the queue from any thread. Multiple consumers may + * listen to the queue from multiple EventBase threads. + * + * A NotificationQueue may not be destroyed while there are still consumers + * registered to receive events from the queue. It is the user's + * responsibility to ensure that all consumers are unregistered before the + * queue is destroyed. + * + * MessageT should be MoveConstructible (i.e., must support either a move + * constructor or a copy constructor, or both). Ideally it's move constructor + * (or copy constructor if no move constructor is provided) should never throw + * exceptions. If the constructor may throw, the consumers could end up + * spinning trying to move a message off the queue and failing, and then + * retrying. + */ +template <typename MessageT> +class NotificationQueue { + struct Node : public boost::intrusive::slist_base_hook< + boost::intrusive::cache_last<true>> { + template <typename MessageTT> + Node(MessageTT&& msg, std::shared_ptr<RequestContext> ctx) + : msg_(std::forward<MessageTT>(msg)), ctx_(std::move(ctx)) {} + MessageT msg_; + std::shared_ptr<RequestContext> ctx_; + }; + + public: + /** + * A callback interface for consuming messages from the queue as they arrive. + */ + class Consumer : public DelayedDestruction, private EventHandler { + public: + enum : uint16_t { kDefaultMaxReadAtOnce = 10 }; + + Consumer() + : queue_(nullptr), + destroyedFlagPtr_(nullptr), + maxReadAtOnce_(kDefaultMaxReadAtOnce) {} + + // create a consumer in-place, without the need to build new class + template <typename TCallback> + static std::unique_ptr<Consumer, DelayedDestruction::Destructor> make( + TCallback&& callback); + + /** + * messageAvailable() will be invoked whenever a new + * message is available from the pipe. + */ + virtual void messageAvailable(MessageT&& message) noexcept = 0; + + /** + * Begin consuming messages from the specified queue. + * + * messageAvailable() will be called whenever a message is available. This + * consumer will continue to consume messages until stopConsuming() is + * called. + * + * A Consumer may only consume messages from a single NotificationQueue at + * a time. startConsuming() should not be called if this consumer is + * already consuming. + */ + void startConsuming(EventBase* eventBase, NotificationQueue* queue) { + init(eventBase, queue); + registerHandler(READ | PERSIST); + } + + /** + * Same as above but registers this event handler as internal so that it + * doesn't count towards the pending reader count for the IOLoop. + */ + void startConsumingInternal( + EventBase* eventBase, + NotificationQueue* queue) { + init(eventBase, queue); + registerInternalHandler(READ | PERSIST); + } + + /** + * Stop consuming messages. + * + * startConsuming() may be called again to resume consumption of messages + * at a later point in time. + */ + void stopConsuming(); + + /** + * Consume messages off the queue until it is empty. No messages may be + * added to the queue while it is draining, so that the process is bounded. + * To that end, putMessage/tryPutMessage will throw an std::runtime_error, + * and tryPutMessageNoThrow will return false. + * + * @returns true if the queue was drained, false otherwise. In practice, + * this will only fail if someone else is already draining the queue. + */ + bool consumeUntilDrained(size_t* numConsumed = nullptr) noexcept; + + /** + * Get the NotificationQueue that this consumer is currently consuming + * messages from. Returns nullptr if the consumer is not currently + * consuming events from any queue. + */ + NotificationQueue* getCurrentQueue() const { + return queue_; + } + + /** + * Set a limit on how many messages this consumer will read each iteration + * around the event loop. + * + * This helps rate-limit how much work the Consumer will do each event loop + * iteration, to prevent it from starving other event handlers. + * + * A limit of 0 means no limit will be enforced. If unset, the limit + * defaults to kDefaultMaxReadAtOnce (defined to 10 above). + */ + void setMaxReadAtOnce(uint32_t maxAtOnce) { + maxReadAtOnce_ = maxAtOnce; + } + uint32_t getMaxReadAtOnce() const { + return maxReadAtOnce_; + } + + EventBase* getEventBase() { + return base_; + } + + void handlerReady(uint16_t events) noexcept override; + + protected: + void destroy() override; + + ~Consumer() override {} + + private: + /** + * Consume messages off the the queue until + * - the queue is empty (1), or + * - until the consumer is destroyed, or + * - until the consumer is uninstalled, or + * - an exception is thrown in the course of dequeueing, or + * - unless isDrain is true, until the maxReadAtOnce_ limit is hit + * + * (1) Well, maybe. See logic/comments around "wasEmpty" in implementation. + */ + void consumeMessages(bool isDrain, size_t* numConsumed = nullptr) noexcept; + + void setActive(bool active, bool shouldLock = false) { + if (!queue_) { + active_ = active; + return; + } + if (shouldLock) { + queue_->spinlock_.lock(); + } + if (!active_ && active) { + ++queue_->numActiveConsumers_; + } else if (active_ && !active) { + --queue_->numActiveConsumers_; + } + active_ = active; + if (shouldLock) { + queue_->spinlock_.unlock(); + } + } + void init(EventBase* eventBase, NotificationQueue* queue); + + NotificationQueue* queue_; + bool* destroyedFlagPtr_; + uint32_t maxReadAtOnce_; + EventBase* base_; + bool active_{false}; + }; + + class SimpleConsumer { + public: + explicit SimpleConsumer(NotificationQueue& queue) : queue_(queue) { + ++queue_.numConsumers_; + } + + ~SimpleConsumer() { + --queue_.numConsumers_; + } + + int getFd() const { + return queue_.eventfd_ >= 0 ? queue_.eventfd_ : queue_.pipeFds_[0]; + } + + template <typename F> + void consumeUntilDrained(F&& foreach); + + private: + NotificationQueue& queue_; + }; + + enum class FdType { + PIPE, +#ifdef FOLLY_HAVE_EVENTFD + EVENTFD, +#endif + }; + + /** + * Create a new NotificationQueue. + * + * If the maxSize parameter is specified, this sets the maximum queue size + * that will be enforced by tryPutMessage(). (This size is advisory, and may + * be exceeded if producers explicitly use putMessage() instead of + * tryPutMessage().) + * + * The fdType parameter determines the type of file descriptor used + * internally to signal message availability. The default (eventfd) is + * preferable for performance and because it won't fail when the queue gets + * too long. It is not available on on older and non-linux kernels, however. + * In this case the code will fall back to using a pipe, the parameter is + * mostly for testing purposes. + */ + explicit NotificationQueue( + uint32_t maxSize = 0, +#ifdef FOLLY_HAVE_EVENTFD + FdType fdType = FdType::EVENTFD) +#else + FdType fdType = FdType::PIPE) +#endif + : eventfd_(-1), + pipeFds_{-1, -1}, + advisoryMaxQueueSize_(maxSize), + pid_(pid_t(getpid())) { + +#ifdef FOLLY_HAVE_EVENTFD + if (fdType == FdType::EVENTFD) { + eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (eventfd_ == -1) { + if (errno == ENOSYS || errno == EINVAL) { + // eventfd not availalble + LOG(ERROR) << "failed to create eventfd for NotificationQueue: " + << errno << ", falling back to pipe mode (is your kernel " + << "> 2.6.30?)"; + fdType = FdType::PIPE; + } else { + // some other error + folly::throwSystemError( + "Failed to create eventfd for " + "NotificationQueue", + errno); + } + } + } +#endif + if (fdType == FdType::PIPE) { + if (pipe(pipeFds_)) { + folly::throwSystemError( + "Failed to create pipe for NotificationQueue", errno); + } + try { + // put both ends of the pipe into non-blocking mode + if (fcntl(pipeFds_[0], F_SETFL, O_RDONLY | O_NONBLOCK) != 0) { + folly::throwSystemError( + "failed to put NotificationQueue pipe read " + "endpoint into non-blocking mode", + errno); + } + if (fcntl(pipeFds_[1], F_SETFL, O_WRONLY | O_NONBLOCK) != 0) { + folly::throwSystemError( + "failed to put NotificationQueue pipe write " + "endpoint into non-blocking mode", + errno); + } + } catch (...) { + ::close(pipeFds_[0]); + ::close(pipeFds_[1]); + throw; + } + } + } + + ~NotificationQueue() { + std::unique_ptr<Node> data; + while (!queue_.empty()) { + data.reset(&queue_.front()); + queue_.pop_front(); + } + if (eventfd_ >= 0) { + ::close(eventfd_); + eventfd_ = -1; + } + if (pipeFds_[0] >= 0) { + ::close(pipeFds_[0]); + pipeFds_[0] = -1; + } + if (pipeFds_[1] >= 0) { + ::close(pipeFds_[1]); + pipeFds_[1] = -1; + } + } + + /** + * Set the advisory maximum queue size. + * + * This maximum queue size affects calls to tryPutMessage(). Message + * producers can still use the putMessage() call to unconditionally put a + * message on the queue, ignoring the configured maximum queue size. This + * can cause the queue size to exceed the configured maximum. + */ + void setMaxQueueSize(uint32_t max) { + advisoryMaxQueueSize_ = max; + } + + /** + * Attempt to put a message on the queue if the queue is not already full. + * + * If the queue is full, a std::overflow_error will be thrown. The + * setMaxQueueSize() function controls the maximum queue size. + * + * If the queue is currently draining, an std::runtime_error will be thrown. + * + * This method may contend briefly on a spinlock if many threads are + * concurrently accessing the queue, but for all intents and purposes it will + * immediately place the message on the queue and return. + * + * tryPutMessage() may throw std::bad_alloc if memory allocation fails, and + * may throw any other exception thrown by the MessageT move/copy + * constructor. + */ + template <typename MessageTT> + void tryPutMessage(MessageTT&& message) { + putMessageImpl(std::forward<MessageTT>(message), advisoryMaxQueueSize_); + } + + /** + * No-throw versions of the above. Instead returns true on success, false on + * failure. + * + * Only std::overflow_error (the common exception case) and std::runtime_error + * (which indicates that the queue is being drained) are prevented from being + * thrown. User code must still catch std::bad_alloc errors. + */ + template <typename MessageTT> + bool tryPutMessageNoThrow(MessageTT&& message) { + return putMessageImpl( + std::forward<MessageTT>(message), advisoryMaxQueueSize_, false); + } + + /** + * Unconditionally put a message on the queue. + * + * This method is like tryPutMessage(), but ignores the maximum queue size + * and always puts the message on the queue, even if the maximum queue size + * would be exceeded. + * + * putMessage() may throw + * - std::bad_alloc if memory allocation fails, and may + * - std::runtime_error if the queue is currently draining + * - any other exception thrown by the MessageT move/copy constructor. + */ + template <typename MessageTT> + void putMessage(MessageTT&& message) { + putMessageImpl(std::forward<MessageTT>(message), 0); + } + + /** + * Put several messages on the queue. + */ + template <typename InputIteratorT> + void putMessages(InputIteratorT first, InputIteratorT last) { + typedef typename std::iterator_traits<InputIteratorT>::iterator_category + IterCategory; + putMessagesImpl(first, last, IterCategory()); + } + + /** + * Try to immediately pull a message off of the queue, without blocking. + * + * If a message is immediately available, the result parameter will be + * updated to contain the message contents and true will be returned. + * + * If no message is available, false will be returned and result will be left + * unmodified. + */ + bool tryConsume(MessageT& result) { + SCOPE_EXIT { + syncSignalAndQueue(); + }; + + checkPid(); + std::unique_ptr<Node> data; + + { + folly::SpinLockGuard g(spinlock_); + + if (UNLIKELY(queue_.empty())) { + return false; + } + + data.reset(&queue_.front()); + queue_.pop_front(); + } + + result = std::move(data->msg_); + RequestContext::setContext(std::move(data->ctx_)); + + return true; + } + + size_t size() const { + folly::SpinLockGuard g(spinlock_); + return queue_.size(); + } + + /** + * Check that the NotificationQueue is being used from the correct process. + * + * If you create a NotificationQueue in one process, then fork, and try to + * send messages to the queue from the child process, you're going to have a + * bad time. Unfortunately users have (accidentally) run into this. + * + * Because we use an eventfd/pipe, the child process can actually signal the + * parent process that an event is ready. However, it can't put anything on + * the parent's queue, so the parent wakes up and finds an empty queue. This + * check ensures that we catch the problem in the misbehaving child process + * code, and crash before signalling the parent process. + */ + void checkPid() const { + CHECK_EQ(pid_, pid_t(getpid())); + } + + private: + // Forbidden copy constructor and assignment operator + NotificationQueue(NotificationQueue const&) = delete; + NotificationQueue& operator=(NotificationQueue const&) = delete; + + inline bool checkQueueSize(size_t maxSize, bool throws = true) const { + DCHECK(0 == spinlock_.try_lock()); + if (maxSize > 0 && queue_.size() >= maxSize) { + if (throws) { + throw std::overflow_error( + "unable to add message to NotificationQueue: " + "queue is full"); + } + return false; + } + return true; + } + + inline bool checkDraining(bool throws = true) { + if (UNLIKELY(draining_ && throws)) { + throw std::runtime_error("queue is draining, cannot add message"); + } + return draining_; + } + + void ensureSignalLocked() const { + // semantics: empty fd == empty queue <=> !signal_ + if (signal_) { + return; + } + + ssize_t bytes_written = 0; + size_t bytes_expected = 0; + + do { + if (eventfd_ >= 0) { + // eventfd(2) dictates that we must write a 64-bit integer + uint64_t signal = 1; + bytes_expected = sizeof(signal); + bytes_written = ::write(eventfd_, &signal, bytes_expected); + } else { + uint8_t signal = 1; + bytes_expected = sizeof(signal); + bytes_written = ::write(pipeFds_[1], &signal, bytes_expected); + } + } while (bytes_written == -1 && errno == EINTR); + + if (bytes_written == ssize_t(bytes_expected)) { + signal_ = true; + } else { + folly::throwSystemError( + "failed to signal NotificationQueue after " + "write", + errno); + } + } + + void drainSignalsLocked() { + ssize_t bytes_read = 0; + if (eventfd_ > 0) { + uint64_t message; + bytes_read = readNoInt(eventfd_, &message, sizeof(message)); + CHECK(bytes_read != -1 || errno == EAGAIN); + } else { + // There should only be one byte in the pipe. To avoid potential leaks we + // still drain. + uint8_t message[32]; + ssize_t result; + while ((result = readNoInt(pipeFds_[0], &message, sizeof(message))) != + -1) { + bytes_read += result; + } + CHECK(result == -1 && errno == EAGAIN); + LOG_IF(ERROR, bytes_read > 1) + << "[NotificationQueue] Unexpected state while draining pipe: bytes_read=" + << bytes_read << " bytes, expected <= 1"; + } + LOG_IF(ERROR, (signal_ && bytes_read == 0) || (!signal_ && bytes_read > 0)) + << "[NotificationQueue] Unexpected state while draining signals: signal_=" + << signal_ << " bytes_read=" << bytes_read; + + signal_ = false; + } + + void ensureSignal() const { + folly::SpinLockGuard g(spinlock_); + ensureSignalLocked(); + } + + void syncSignalAndQueue() { + folly::SpinLockGuard g(spinlock_); + + if (queue_.empty()) { + drainSignalsLocked(); + } else { + ensureSignalLocked(); + } + } + + template <typename MessageTT> + bool putMessageImpl(MessageTT&& message, size_t maxSize, bool throws = true) { + checkPid(); + bool signal = false; + { + auto data = std::make_unique<Node>( + std::forward<MessageTT>(message), RequestContext::saveContext()); + folly::SpinLockGuard g(spinlock_); + if (checkDraining(throws) || !checkQueueSize(maxSize, throws)) { + return false; + } + // We only need to signal an event if not all consumers are + // awake. + if (numActiveConsumers_ < numConsumers_) { + signal = true; + } + queue_.push_back(*data.release()); + if (signal) { + ensureSignalLocked(); + } + } + return true; + } + + template <typename InputIteratorT> + void putMessagesImpl( + InputIteratorT first, + InputIteratorT last, + std::input_iterator_tag) { + checkPid(); + bool signal = false; + boost::intrusive::slist<Node, boost::intrusive::cache_last<true>> q; + try { + while (first != last) { + auto data = std::make_unique<Node>( + std::move(*first), RequestContext::saveContext()); + q.push_back(*data.release()); + ++first; + } + folly::SpinLockGuard g(spinlock_); + checkDraining(); + queue_.splice(queue_.end(), q); + if (numActiveConsumers_ < numConsumers_) { + signal = true; + } + if (signal) { + ensureSignalLocked(); + } + } catch (...) { + std::unique_ptr<Node> data; + while (!q.empty()) { + data.reset(&q.front()); + q.pop_front(); + } + throw; + } + } + + mutable folly::SpinLock spinlock_; + mutable bool signal_{false}; + int eventfd_; + int pipeFds_[2]; // to fallback to on older/non-linux systems + uint32_t advisoryMaxQueueSize_; + pid_t pid_; + boost::intrusive::slist<Node, boost::intrusive::cache_last<true>> queue_; + int numConsumers_{0}; + std::atomic<int> numActiveConsumers_{0}; + bool draining_{false}; +}; + +template <typename MessageT> +void NotificationQueue<MessageT>::Consumer::destroy() { + // If we are in the middle of a call to handlerReady(), destroyedFlagPtr_ + // will be non-nullptr. Mark the value that it points to, so that + // handlerReady() will know the callback is destroyed, and that it cannot + // access any member variables anymore. + if (destroyedFlagPtr_) { + *destroyedFlagPtr_ = true; + } + stopConsuming(); + DelayedDestruction::destroy(); +} + +template <typename MessageT> +void NotificationQueue<MessageT>::Consumer::handlerReady( + uint16_t /*events*/) noexcept { + consumeMessages(false); +} + +template <typename MessageT> +void NotificationQueue<MessageT>::Consumer::consumeMessages( + bool isDrain, + size_t* numConsumed) noexcept { + DestructorGuard dg(this); + uint32_t numProcessed = 0; + setActive(true); + SCOPE_EXIT { + if (queue_) { + queue_->syncSignalAndQueue(); + } + }; + SCOPE_EXIT { + setActive(false, /* shouldLock = */ true); + }; + SCOPE_EXIT { + if (numConsumed != nullptr) { + *numConsumed = numProcessed; + } + }; + while (true) { + // Now pop the message off of the queue. + // + // We have to manually acquire and release the spinlock here, rather than + // using SpinLockHolder since the MessageT has to be constructed while + // holding the spinlock and available after we release it. SpinLockHolder + // unfortunately doesn't provide a release() method. (We can't construct + // MessageT first since we have no guarantee that MessageT has a default + // constructor. + queue_->spinlock_.lock(); + bool locked = true; + + try { + if (UNLIKELY(queue_->queue_.empty())) { + // If there is no message, we've reached the end of the queue, return. + setActive(false); + queue_->spinlock_.unlock(); + return; + } + + // Pull a message off the queue. + std::unique_ptr<Node> data; + data.reset(&queue_->queue_.front()); + queue_->queue_.pop_front(); + + // Check to see if the queue is empty now. + // We use this as an optimization to see if we should bother trying to + // loop again and read another message after invoking this callback. + bool wasEmpty = queue_->queue_.empty(); + if (wasEmpty) { + setActive(false); + } + + // Now unlock the spinlock before we invoke the callback. + queue_->spinlock_.unlock(); + RequestContextScopeGuard rctx(std::move(data->ctx_)); + + locked = false; + + // Call the callback + bool callbackDestroyed = false; + CHECK(destroyedFlagPtr_ == nullptr); + destroyedFlagPtr_ = &callbackDestroyed; + messageAvailable(std::move(data->msg_)); + destroyedFlagPtr_ = nullptr; + + // If the callback was destroyed before it returned, we are done + if (callbackDestroyed) { + return; + } + + // If the callback is no longer installed, we are done. + if (queue_ == nullptr) { + return; + } + + // If we have hit maxReadAtOnce_, we are done. + ++numProcessed; + if (!isDrain && maxReadAtOnce_ > 0 && numProcessed >= maxReadAtOnce_) { + return; + } + + // If the queue was empty before we invoked the callback, it's probable + // that it is still empty now. Just go ahead and return, rather than + // looping again and trying to re-read from the eventfd. (If a new + // message had in fact arrived while we were invoking the callback, we + // will simply be woken up the next time around the event loop and will + // process the message then.) + if (wasEmpty) { + return; + } + } catch (const std::exception&) { + // This catch block is really just to handle the case where the MessageT + // constructor throws. The messageAvailable() callback itself is + // declared as noexcept and should never throw. + // + // If the MessageT constructor does throw we try to handle it as best as + // we can, but we can't work miracles. We will just ignore the error for + // now and return. The next time around the event loop we will end up + // trying to read the message again. If MessageT continues to throw we + // will never make forward progress and will keep trying each time around + // the event loop. + if (locked) { + // Unlock the spinlock. + queue_->spinlock_.unlock(); + } + + return; + } + } +} + +template <typename MessageT> +void NotificationQueue<MessageT>::Consumer::init( + EventBase* eventBase, + NotificationQueue* queue) { + eventBase->dcheckIsInEventBaseThread(); + assert(queue_ == nullptr); + assert(!isHandlerRegistered()); + queue->checkPid(); + + base_ = eventBase; + + queue_ = queue; + + { + folly::SpinLockGuard g(queue_->spinlock_); + queue_->numConsumers_++; + } + queue_->ensureSignal(); + + if (queue_->eventfd_ >= 0) { + initHandler(eventBase, folly::NetworkSocket::fromFd(queue_->eventfd_)); + } else { + initHandler(eventBase, folly::NetworkSocket::fromFd(queue_->pipeFds_[0])); + } +} + +template <typename MessageT> +void NotificationQueue<MessageT>::Consumer::stopConsuming() { + if (queue_ == nullptr) { + assert(!isHandlerRegistered()); + return; + } + + { + folly::SpinLockGuard g(queue_->spinlock_); + queue_->numConsumers_--; + setActive(false); + } + + assert(isHandlerRegistered()); + unregisterHandler(); + detachEventBase(); + queue_ = nullptr; +} + +template <typename MessageT> +bool NotificationQueue<MessageT>::Consumer::consumeUntilDrained( + size_t* numConsumed) noexcept { + DestructorGuard dg(this); + { + folly::SpinLockGuard g(queue_->spinlock_); + if (queue_->draining_) { + return false; + } + queue_->draining_ = true; + } + consumeMessages(true, numConsumed); + { + folly::SpinLockGuard g(queue_->spinlock_); + queue_->draining_ = false; + } + return true; +} + +template <typename MessageT> +template <typename F> +void NotificationQueue<MessageT>::SimpleConsumer::consumeUntilDrained( + F&& foreach) { + SCOPE_EXIT { + queue_.syncSignalAndQueue(); + }; + + queue_.checkPid(); + + while (true) { + std::unique_ptr<Node> data; + { + folly::SpinLockGuard g(queue_.spinlock_); + + if (UNLIKELY(queue_.queue_.empty())) { + return; + } + + data.reset(&queue_.queue_.front()); + queue_.queue_.pop_front(); + } + + RequestContextScopeGuard rctx(std::move(data->ctx_)); + foreach(std::move(data->msg_)); + // Make sure message destructor is called with the correct RequestContext. + data.reset(); + } +} + +/** + * Creates a NotificationQueue::Consumer wrapping a function object + * Modeled after AsyncTimeout::make + * + */ + +namespace detail { + +template <typename MessageT, typename TCallback> +struct notification_queue_consumer_wrapper + : public NotificationQueue<MessageT>::Consumer { + template <typename UCallback> + explicit notification_queue_consumer_wrapper(UCallback&& callback) + : callback_(std::forward<UCallback>(callback)) {} + + // we are being stricter here and requiring noexcept for callback + void messageAvailable(MessageT&& message) noexcept override { + static_assert( + noexcept(std::declval<TCallback>()(std::forward<MessageT>(message))), + "callback must be declared noexcept, e.g.: `[]() noexcept {}`"); + + callback_(std::forward<MessageT>(message)); + } + + private: + TCallback callback_; +}; + +} // namespace detail + +template <typename MessageT> +template <typename TCallback> +std::unique_ptr< + typename NotificationQueue<MessageT>::Consumer, + DelayedDestruction::Destructor> +NotificationQueue<MessageT>::Consumer::make(TCallback&& callback) { + return std::unique_ptr< + NotificationQueue<MessageT>::Consumer, + DelayedDestruction::Destructor>( + new detail::notification_queue_consumer_wrapper< + MessageT, + typename std::decay<TCallback>::type>( + std::forward<TCallback>(callback))); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.cpp b/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.cpp new file mode 100644 index 000000000..5a12d8b57 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/PasswordInFile.h> + +#include <folly/FileUtil.h> + +using namespace std; + +namespace folly { + +PasswordInFile::PasswordInFile(const string& file) : fileName_(file) { + readFile(file.c_str(), password_); + auto p = password_.find('\0'); + if (p != std::string::npos) { + password_.erase(p); + } +} + +PasswordInFile::~PasswordInFile() { + OPENSSL_cleanse((char*)password_.data(), password_.length()); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.h b/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.h new file mode 100644 index 000000000..be0a372e6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/PasswordInFile.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/SSLContext.h> // PasswordCollector + +namespace folly { + +class PasswordInFile : public PasswordCollector { + public: + explicit PasswordInFile(const std::string& file); + ~PasswordInFile() override; + + void getPassword(std::string& password, int /* size */) const override { + password = password_; + } + + const char* getPasswordStr() const { + return password_.c_str(); + } + + const std::string& describe() const override { + return fileName_; + } + + protected: + std::string fileName_; + std::string password_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/Request.cpp b/ios/Pods/Flipper-Folly/folly/io/async/Request.cpp new file mode 100644 index 000000000..39f4d368e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/Request.cpp @@ -0,0 +1,851 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/Request.h> +#include <folly/experimental/SingleWriterFixedHashMap.h> +#include <folly/synchronization/Hazptr.h> +#include <folly/tracing/StaticTracepoint.h> + +#include <glog/logging.h> + +#include <folly/MapUtil.h> +#include <folly/SingletonThreadLocal.h> + +DEFINE_bool( + folly_reqctx_use_hazptr, + true, + "RequestContext implementation using hazard pointers"); + +namespace folly { + +namespace { +using SingletonT = + SingletonThreadLocal<RequestContext::StaticContext, RequestContext>; +} + +RequestToken::RequestToken(const std::string& str) { + auto& cache = getCache(); + { + auto c = cache.rlock(); + auto res = c->find(str); + if (res != c->end()) { + token_ = res->second; + return; + } + } + auto c = cache.wlock(); + auto res = c->find(str); + if (res != c->end()) { + token_ = res->second; + return; + } + static uint32_t nextToken{1}; + + token_ = nextToken++; + (*c)[str] = token_; +} + +std::string RequestToken::getDebugString() const { + auto& cache = getCache(); + auto c = cache.rlock(); + for (auto& v : *c) { + if (v.second == token_) { + return v.first; + } + } + throw std::logic_error("Could not find debug string in RequestToken"); +} + +Synchronized<F14FastMap<std::string, uint32_t>>& RequestToken::getCache() { + static Indestructible<Synchronized<F14FastMap<std::string, uint32_t>>> cache; + return *cache; +} + +void RequestData::acquireRef() { + auto rc = keepAliveCounter_.fetch_add( + kClearCount + kDeleteCount, std::memory_order_relaxed); + DCHECK_GE(rc, 0); +} + +void RequestData::releaseRefClearOnly() { + auto rc = + keepAliveCounter_.fetch_sub(kClearCount, std::memory_order_acq_rel) - + kClearCount; + DCHECK_GT(rc, 0); + if (rc < kClearCount) { + this->onClear(); + } +} + +void RequestData::releaseRefDeleteOnly() { + auto rc = + keepAliveCounter_.fetch_sub(kDeleteCount, std::memory_order_acq_rel) - + kDeleteCount; + DCHECK_GE(rc, 0); + if (rc == 0) { + delete this; + } +} + +void RequestData::releaseRefClearDelete() { + auto rc = keepAliveCounter_.fetch_sub( + kClearCount + kDeleteCount, std::memory_order_acq_rel) - + (kClearCount + kDeleteCount); + DCHECK_GE(rc, 0); + if (rc < kClearCount) { + this->onClear(); + } + if (rc == 0) { + delete this; + } +} + +void RequestData::DestructPtr::operator()(RequestData* ptr) { + if (ptr) { + auto keepAliveCounter = + ptr->keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel); + // Note: this is the value before decrement, hence == 1 check + DCHECK(keepAliveCounter > 0); + if (keepAliveCounter == 1) { + ptr->onClear(); + delete ptr; + } + } +} + +/* static */ RequestData::SharedPtr RequestData::constructPtr( + RequestData* ptr) { + if (ptr) { + auto keepAliveCounter = + ptr->keepAliveCounter_.fetch_add(1, std::memory_order_relaxed); + DCHECK(keepAliveCounter >= 0); + } + return SharedPtr(ptr); +} + +// The Combined struct keeps the two structures for context data +// and callbacks together, so that readers can protect consistent +// versions of the two structures together using hazard pointers. +struct RequestContext::StateHazptr::Combined : hazptr_obj_base<Combined> { + static constexpr size_t kInitialCapacity = 4; + static constexpr size_t kSlackReciprocal = 4; // unused >= 1/4 capacity + + // This must be optimized for lookup, its hot path is getContextData + // Efficiency of copying the container also matters in setShallowCopyContext + SingleWriterFixedHashMap<RequestToken, RequestData*> requestData_; + // This must be optimized for iteration, its hot path is setContext + SingleWriterFixedHashMap<RequestData*, bool> callbackData_; + // Hash map to keep track of Clear and Delete counts. The presence + // of a key indicates holding a Delete count for the request data + // (i.e., delete when the Delete count goes to zero). A value of + // true indicates holding a Clear counts for the request data (i.e., + // call onClear() when the Clear count goes to zero). + F14FastMap<RequestData*, bool> refs_; + + Combined() + : requestData_(kInitialCapacity), callbackData_(kInitialCapacity) {} + + Combined(const Combined& o) + : Combined(o.requestData_.capacity(), o.callbackData_.capacity(), o) {} + + Combined(size_t dataCapacity, size_t callbackCapacity, const Combined& o) + : requestData_(dataCapacity, o.requestData_), + callbackData_(callbackCapacity, o.callbackData_) {} + + Combined(Combined&&) = delete; + Combined& operator=(const Combined&) = delete; + Combined& operator=(Combined&&) = delete; + + ~Combined() { + releaseDataRefs(); + } + + /* acquireDataRefs - Called at most once per Combined instance. */ + void acquireDataRefs() { + for (auto it = requestData_.begin(); it != requestData_.end(); ++it) { + auto p = it.value(); + if (p) { + refs_.insert({p, true}); + p->acquireRef(); + } + } + } + + /* releaseDataRefs - Called only once from ~Combined */ + void releaseDataRefs() { + for (auto pair : refs_) { + if (pair.second) { + pair.first->releaseRefClearDelete(); + } else { + pair.first->releaseRefDeleteOnly(); + } + } + } + + /* needExpand */ + bool needExpand() { + return needExpandRequestData() || needExpandCallbackData(); + } + + /* needExpandRequestData */ + bool needExpandRequestData() { + return kSlackReciprocal * (requestData_.available() - 1) < + requestData_.capacity(); + } + + /* needExpandCallbackData */ + bool needExpandCallbackData() { + return kSlackReciprocal * (callbackData_.available() - 1) < + callbackData_.capacity(); + } +}; // Combined + +RequestContext::StateHazptr::StateHazptr() = default; + +RequestContext::StateHazptr::StateHazptr(const StateHazptr& o) { + Combined* oc = o.combined(); + if (oc) { + auto p = new Combined(*oc); + p->acquireDataRefs(); + setCombined(p); + } +} + +RequestContext::StateHazptr::~StateHazptr() { + cohort_.shutdown_and_reclaim(); + auto p = combined(); + if (p) { + delete p; + } +} + +FOLLY_ALWAYS_INLINE +RequestContext::StateHazptr::Combined* RequestContext::StateHazptr::combined() + const { + return combined_.load(std::memory_order_acquire); +} + +RequestContext::StateHazptr::Combined* +RequestContext::StateHazptr::ensureCombined() { + auto c = combined(); + if (!c) { + c = new Combined; + setCombined(c); + } + return c; +} + +void RequestContext::StateHazptr::setCombined(Combined* p) { + p->set_cohort_tag(&cohort_); + combined_.store(p, std::memory_order_release); +} + +bool RequestContext::StateHazptr::doSetContextData( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe) { + SetContextDataResult result; + if (safe) { + result = doSetContextDataHelper(token, data, behaviour, safe); + } else { + std::lock_guard<std::mutex> g(mutex_); + result = doSetContextDataHelper(token, data, behaviour, safe); + } + if (result.unexpected) { + LOG_FIRST_N(WARNING, 1) + << "Calling RequestContext::setContextData for " + << token.getDebugString() << " but it is already set"; + } + if (result.replaced) { + result.replaced->retire(); // Retire to hazptr library + } + return result.changed; +} + +RequestContext::StateHazptr::SetContextDataResult +RequestContext::StateHazptr::doSetContextDataHelper( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe) { + bool unexpected = false; + Combined* cur = ensureCombined(); + Combined* replaced = nullptr; + auto it = cur->requestData_.find(token); + bool found = it != cur->requestData_.end(); + if (found) { + if (behaviour == DoSetBehaviour::SET_IF_ABSENT) { + return {false /* no changes made */, + false /* nothing unexpected */, + nullptr /* combined not replaced */}; + } + RequestData* oldData = it.value(); + if (oldData) { + // Always erase non-null old data (and run its onUnset callback, + // if any). Non-null old data will always be overwritten either + // by the new data (if behavior is OVERWRITE) or by nullptr (if + // behavior is SET). + Combined* newCombined = eraseOldData(cur, token, oldData, safe); + if (newCombined) { + replaced = cur; + cur = newCombined; + } + } + if (behaviour == DoSetBehaviour::SET) { + // The expected behavior for SET when found is to reset the + // pointer and warn, without updating to the new data. + if (oldData) { + cur->requestData_.insert(token, nullptr); + } + unexpected = true; + } else { + DCHECK(behaviour == DoSetBehaviour::OVERWRITE); + } + } + if (!unexpected) { + // Replace combined if needed, call onSet if any, insert new data. + Combined* newCombined = insertNewData(cur, token, data, found); + if (newCombined) { + replaced = cur; + cur = newCombined; + } + } + if (replaced) { + // Now the new Combined is consistent. Safe to publish. + setCombined(cur); + } + return {true, /* changes were made */ + unexpected, + replaced}; +} + +RequestContext::StateHazptr::Combined* FOLLY_NULLABLE +RequestContext::StateHazptr::eraseOldData( + RequestContext::StateHazptr::Combined* cur, + const RequestToken& token, + RequestData* olddata, + bool safe) { + Combined* newCombined = nullptr; + // Call onUnset, if any. + if (olddata->hasCallback()) { + olddata->onUnset(); + bool erased = cur->callbackData_.erase(olddata); + DCHECK(erased); + } + if (safe) { + // If the caller guarantees thread-safety, then erase the + // entry in the current version. + cur->requestData_.erase(token); + cur->refs_.erase(olddata); + olddata->releaseRefClearDelete(); + } else { + // If there may be concurrent readers, then copy-on-erase. + // Update the data reference counts to account for the + // existence of the new copy. + newCombined = new Combined(*cur); + newCombined->requestData_.erase(token); + newCombined->acquireDataRefs(); + } + return newCombined; +} + +RequestContext::StateHazptr::Combined* FOLLY_NULLABLE +RequestContext::StateHazptr::insertNewData( + RequestContext::StateHazptr::Combined* cur, + const RequestToken& token, + std::unique_ptr<RequestData>& data, + bool found) { + Combined* newCombined = nullptr; + // Update value to point to the new data. + if (!found && cur->needExpand()) { + // Replace the current Combined with an expanded one + newCombined = expand(cur); + cur = newCombined; + cur->acquireDataRefs(); + } + if (data && data->hasCallback()) { + // If data has callback, insert in callback structure, call onSet + cur->callbackData_.insert(data.get(), true); + data->onSet(); + } + if (data) { + cur->refs_.insert({data.get(), true}); + data->acquireRef(); + } + cur->requestData_.insert(token, data.release()); + return newCombined; +} + +FOLLY_ALWAYS_INLINE +bool RequestContext::StateHazptr::hasContextData( + const RequestToken& token) const { + hazptr_local<1> h; + Combined* combined = h[0].get_protected(combined_); + return combined ? combined->requestData_.contains(token) : false; +} + +FOLLY_ALWAYS_INLINE +RequestData* FOLLY_NULLABLE +RequestContext::StateHazptr::getContextData(const RequestToken& token) { + hazptr_local<1> h; + Combined* combined = h[0].get_protected(combined_); + if (!combined) { + return nullptr; + } + auto& reqData = combined->requestData_; + auto it = reqData.find(token); + return it == reqData.end() ? nullptr : it.value(); +} + +FOLLY_ALWAYS_INLINE +const RequestData* FOLLY_NULLABLE +RequestContext::StateHazptr::getContextData(const RequestToken& token) const { + hazptr_local<1> h; + Combined* combined = h[0].get_protected(combined_); + if (!combined) { + return nullptr; + } + auto& reqData = combined->requestData_; + auto it = reqData.find(token); + return it == reqData.end() ? nullptr : it.value(); +} + +FOLLY_ALWAYS_INLINE +void RequestContext::StateHazptr::onSet() { + // Don't use hazptr_local because callback may use hazptr + hazptr_holder<> h; + Combined* combined = h.get_protected(combined_); + if (!combined) { + return; + } + auto& cb = combined->callbackData_; + for (auto it = cb.begin(); it != cb.end(); ++it) { + it.key()->onSet(); + } +} + +FOLLY_ALWAYS_INLINE +void RequestContext::StateHazptr::onUnset() { + // Don't use hazptr_local because callback may use hazptr + hazptr_holder<> h; + Combined* combined = h.get_protected(combined_); + if (!combined) { + return; + } + auto& cb = combined->callbackData_; + for (auto it = cb.begin(); it != cb.end(); ++it) { + it.key()->onUnset(); + } +} + +void RequestContext::StateHazptr::clearContextData(const RequestToken& token) { + RequestData* data; + Combined* replaced = nullptr; + { // Lock mutex_ + std::lock_guard<std::mutex> g(mutex_); + Combined* cur = combined(); + if (!cur) { + return; + } + auto it = cur->requestData_.find(token); + if (it == cur->requestData_.end()) { + return; + } + data = it.value(); + if (!data) { + cur->requestData_.erase(token); + return; + } + if (data->hasCallback()) { + data->onUnset(); + cur->callbackData_.erase(data); + } + replaced = cur; + cur = new Combined(*replaced); + cur->requestData_.erase(token); + cur->acquireDataRefs(); + setCombined(cur); + } // Unlock mutex_ + DCHECK(data); + data->releaseRefClearOnly(); + replaced->refs_[data] = false; // Clear reference already released + DCHECK(replaced); + replaced->retire(); +} + +RequestContext::StateHazptr::Combined* RequestContext::StateHazptr::expand( + RequestContext::StateHazptr::Combined* c) { + size_t dataCapacity = c->requestData_.capacity(); + if (c->needExpandRequestData()) { + dataCapacity *= 2; + } + size_t callbackCapacity = c->callbackData_.capacity(); + if (c->needExpandCallbackData()) { + callbackCapacity *= 2; + } + return new Combined(dataCapacity, callbackCapacity, *c); +} + +RequestContext::RequestContext() + : useHazptr_(FLAGS_folly_reqctx_use_hazptr), + rootId_(reinterpret_cast<intptr_t>(this)) {} + +RequestContext::RequestContext(intptr_t rootid) + : useHazptr_(FLAGS_folly_reqctx_use_hazptr), rootId_(rootid) {} + +RequestContext::RequestContext(const RequestContext& ctx, intptr_t rootid, Tag) + : RequestContext(ctx) { + rootId_ = rootid; +} + +RequestContext::RequestContext(const RequestContext& ctx, Tag) + : RequestContext(ctx) {} + +/* static */ std::shared_ptr<RequestContext> RequestContext::copyAsRoot( + const RequestContext& ctx, + intptr_t rootid) { + return std::make_shared<RequestContext>(ctx, rootid, Tag{}); +} + +/* static */ std::shared_ptr<RequestContext> RequestContext::copyAsChild( + const RequestContext& ctx) { + return std::make_shared<RequestContext>(ctx, Tag{}); +} + +bool RequestContext::doSetContextDataLock( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour) { + auto wlock = state_.wlock(); + auto& state = *wlock; + + auto it = state.requestData_.find(token); + if (it != state.requestData_.end()) { + if (behaviour == DoSetBehaviour::SET_IF_ABSENT) { + return false; + } + if (it->second) { + if (it->second->hasCallback()) { + it->second->onUnset(); + state.callbackData_.erase(it->second.get()); + } + it->second.reset(nullptr); + } + if (behaviour == DoSetBehaviour::SET) { + LOG_FIRST_N(WARNING, 1) + << "Calling RequestContext::setContextData for " + << token.getDebugString() << " but it is already set"; + return true; + } + DCHECK(behaviour == DoSetBehaviour::OVERWRITE); + } + + if (data && data->hasCallback()) { + state.callbackData_.insert(data.get()); + data->onSet(); + } + auto ptr = RequestData::constructPtr(data.release()); + if (it != state.requestData_.end()) { + it->second = std::move(ptr); + } else { + state.requestData_.insert(std::make_pair(token, std::move(ptr))); + } + return true; +} + +void RequestContext::setContextData( + const RequestToken& token, + std::unique_ptr<RequestData> data) { + if (useHazptr()) { + stateHazptr_.doSetContextData(token, data, DoSetBehaviour::SET, false); + return; + } + doSetContextDataLock(token, data, DoSetBehaviour::SET); +} + +bool RequestContext::setContextDataIfAbsent( + const RequestToken& token, + std::unique_ptr<RequestData> data) { + if (useHazptr()) { + return stateHazptr_.doSetContextData( + token, data, DoSetBehaviour::SET_IF_ABSENT, false); + } + return doSetContextDataLock(token, data, DoSetBehaviour::SET_IF_ABSENT); +} + +void RequestContext::overwriteContextDataLock( + const RequestToken& token, + std::unique_ptr<RequestData> data) { + doSetContextDataLock(token, data, DoSetBehaviour::OVERWRITE); +} + +void RequestContext::overwriteContextDataHazptr( + const RequestToken& token, + std::unique_ptr<RequestData> data, + bool safe) { + stateHazptr_.doSetContextData(token, data, DoSetBehaviour::OVERWRITE, safe); +} + +bool RequestContext::hasContextData(const RequestToken& val) const { + if (useHazptr()) { + return stateHazptr_.hasContextData(val); + } + return state_.rlock()->requestData_.count(val); +} + +RequestData* FOLLY_NULLABLE +RequestContext::getContextData(const RequestToken& val) { + if (useHazptr()) { + return stateHazptr_.getContextData(val); + } + const RequestData::SharedPtr dflt{nullptr}; + return get_ref_default(state_.rlock()->requestData_, val, dflt).get(); +} + +const RequestData* FOLLY_NULLABLE +RequestContext::getContextData(const RequestToken& val) const { + if (useHazptr()) { + return stateHazptr_.getContextData(val); + } + const RequestData::SharedPtr dflt{nullptr}; + return get_ref_default(state_.rlock()->requestData_, val, dflt).get(); +} + +void RequestContext::onSet() { + if (useHazptr()) { + stateHazptr_.onSet(); + return; + } + auto rlock = state_.rlock(); + for (const auto& data : rlock->callbackData_) { + data->onSet(); + } +} + +void RequestContext::onUnset() { + if (useHazptr()) { + stateHazptr_.onUnset(); + return; + } + auto rlock = state_.rlock(); + for (const auto& data : rlock->callbackData_) { + data->onUnset(); + } +} + +void RequestContext::clearContextData(const RequestToken& val) { + if (useHazptr()) { + stateHazptr_.clearContextData(val); + return; + } + RequestData::SharedPtr requestData; + // Delete the RequestData after giving up the wlock just in case one of the + // RequestData destructors will try to grab the lock again. + { + auto ulock = state_.ulock(); + // Need non-const iterators to use under write lock. + auto& state = ulock.asNonConstUnsafe(); + auto it = state.requestData_.find(val); + if (it == state.requestData_.end()) { + return; + } + + auto wlock = ulock.moveFromUpgradeToWrite(); + if (it->second && it->second->hasCallback()) { + it->second->onUnset(); + wlock->callbackData_.erase(it->second.get()); + } + + requestData = std::move(it->second); + wlock->requestData_.erase(it); + } +} + +namespace { +// Execute functor exec for all RequestData in data, which are not in other +// Similar to std::set_difference but avoid intermediate data structure +template <typename TData, typename TExec> +void exec_set_difference(const TData& data, const TData& other, TExec&& exec) { + auto diter = data.begin(); + auto dend = data.end(); + auto oiter = other.begin(); + auto oend = other.end(); + while (diter != dend) { + // Order of "if" optimizes for the 2 common cases: + // 1) empty other, switching to default context + // 2) identical other, switching to similar context with same callbacks + if (oiter == oend) { + exec(*diter); + ++diter; + } else if (*diter == *oiter) { + ++diter; + ++oiter; + } else if (*diter < *oiter) { + exec(*diter); + ++diter; + } else { + ++oiter; + } + } +} +} // namespace + +/* static */ std::shared_ptr<RequestContext> RequestContext::setContext( + std::shared_ptr<RequestContext> const& newCtx) { + return setContext(copy(newCtx)); +} + +/* static */ std::shared_ptr<RequestContext> RequestContext::setContext( + std::shared_ptr<RequestContext>&& newCtx_) { + auto newCtx = std::move(newCtx_); // enforce that it is really moved-from + + auto& staticCtx = getStaticContext(); + if (newCtx == staticCtx.first) { + return newCtx; + } + + FOLLY_SDT( + folly, + request_context_switch_before, + staticCtx.first.get(), + newCtx.get()); + + if ((newCtx.get() && newCtx->useHazptr()) || + (staticCtx.first.get() && staticCtx.first->useHazptr())) { + DCHECK(!newCtx.get() || newCtx->useHazptr()); + DCHECK(!staticCtx.first.get() || staticCtx.first->useHazptr()); + return RequestContext::setContextHazptr(newCtx, staticCtx); + } else { + return RequestContext::setContextLock(newCtx, staticCtx); + } +} + +FOLLY_ALWAYS_INLINE +/* static */ std::shared_ptr<RequestContext> RequestContext::setContextLock( + std::shared_ptr<RequestContext>& newCtx, + StaticContext& staticCtx) { + auto curCtx = staticCtx; + if (newCtx && curCtx.first) { + // Only call set/unset for all request data that differs + auto ret = folly::acquireLocked( + as_const(newCtx->state_), as_const(curCtx.first->state_)); + auto& newLock = std::get<0>(ret); + auto& curLock = std::get<1>(ret); + auto& newData = newLock->callbackData_; + auto& curData = curLock->callbackData_; + exec_set_difference( + curData, newData, [](RequestData* data) { data->onUnset(); }); + staticCtx.first = newCtx; + staticCtx.second = newCtx->rootId_; + exec_set_difference( + newData, curData, [](RequestData* data) { data->onSet(); }); + } else { + if (curCtx.first) { + curCtx.first->onUnset(); + } + staticCtx.first = newCtx; + if (newCtx) { + staticCtx.second = newCtx->rootId_; + newCtx->onSet(); + } else { + staticCtx.second = 0; + } + } + return curCtx.first; +} + +FOLLY_ALWAYS_INLINE +/* static */ std::shared_ptr<RequestContext> RequestContext::setContextHazptr( + std::shared_ptr<RequestContext>& newCtx, + StaticContext& staticCtx) { + auto curCtx = std::move(staticCtx); + bool checkCur = curCtx.first && curCtx.first->stateHazptr_.combined(); + bool checkNew = newCtx && newCtx->stateHazptr_.combined(); + if (checkCur && checkNew) { + hazptr_array<2> h; + auto curc = h[0].get_protected(curCtx.first->stateHazptr_.combined_); + auto newc = h[1].get_protected(newCtx->stateHazptr_.combined_); + auto& curcb = curc->callbackData_; + auto& newcb = newc->callbackData_; + for (auto it = curcb.begin(); it != curcb.end(); ++it) { + DCHECK(it.key()); + auto data = it.key(); + if (!newcb.contains(data)) { + data->onUnset(); + } + } + staticCtx.first = std::move(newCtx); + staticCtx.second = staticCtx.first->rootId_; + for (auto it = newcb.begin(); it != newcb.end(); ++it) { + DCHECK(it.key()); + auto data = it.key(); + if (!curcb.contains(data)) { + data->onSet(); + } + } + } else { + if (curCtx.first) { + curCtx.first->stateHazptr_.onUnset(); + } + staticCtx.first = std::move(newCtx); + if (staticCtx.first) { + staticCtx.first->stateHazptr_.onSet(); + staticCtx.second = staticCtx.first->rootId_; + } else { + staticCtx.second = 0; + } + } + return curCtx.first; +} + +RequestContext::StaticContext& RequestContext::getStaticContext() { + return SingletonT::get(); +} + +/* static */ std::vector<RequestContext::RootIdInfo> +RequestContext::getRootIdsFromAllThreads() { + std::vector<RootIdInfo> result; + auto accessor = SingletonT::accessAllThreads(); + for (auto it = accessor.begin(); it != accessor.end(); ++it) { + result.push_back({it->second, it.getThreadId(), it.getOSThreadId()}); + } + return result; +} + +/* static */ std::shared_ptr<RequestContext> +RequestContext::setShallowCopyContext() { + auto& parent = getStaticContext().first; + auto child = parent ? RequestContext::copyAsChild(*parent) + : std::make_shared<RequestContext>(); + if (!parent) { + child->rootId_ = 0; + } + // Do not use setContext to avoid global set/unset + // Also rootId does not change so do not bother setting it. + std::swap(child, parent); + return child; +} + +RequestContext* RequestContext::get() { + auto& context = getStaticContext().first; + if (!context) { + static RequestContext defaultContext(0); + return std::addressof(defaultContext); + } + return context.get(); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/Request.h b/ios/Pods/Flipper-Folly/folly/io/async/Request.h new file mode 100644 index 000000000..e3277dce1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/Request.h @@ -0,0 +1,533 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Synchronized.h> +#include <folly/container/F14Map.h> +#include <folly/portability/GFlags.h> +#include <folly/sorted_vector_types.h> +#include <folly/synchronization/Hazptr.h> + +#include <atomic> +#include <memory> +#include <mutex> +#include <string> + +DECLARE_bool(folly_reqctx_use_hazptr); + +namespace folly { + +/* + * A token to be used to fetch data from RequestContext. + * Generally you will want this to be a static, created only once using a + * string, and then only copied. The string constructor is expensive. + */ +class RequestToken { + public: + RequestToken() = default; + explicit RequestToken(const std::string& str); + + bool operator==(const RequestToken& other) const { + return token_ == other.token_; + } + + // Slow, use only for debug log messages. + std::string getDebugString() const; + + friend struct std::hash<folly::RequestToken>; + + private: + static Synchronized<F14FastMap<std::string, uint32_t>>& getCache(); + + uint32_t token_; +}; + +} // namespace folly + +namespace std { +template <> +struct hash<folly::RequestToken> { + size_t operator()(const folly::RequestToken& token) const { + return hash<uint32_t>()(token.token_); + } +}; +} // namespace std + +namespace folly { + +// - A runtime flag GFLAGS_reqctx_use_hazptr determines the +// implementation of RequestContext. +// - The flag false implementation uses sequential data structures +// protected by a read-write lock. +// - The flag true implementation uses single-writer multi-readers +// data structures protected by hazard pointers for readers and a +// lock for writers. +// - Each RequestContext instances contains a bool member useHazptr_ +// (readable by a public member function useHazptr()) that indicates +// the implementation of the instance depending on the value of the +// GFLAG at instance construction time.. + +// Some request context that follows an async request through a process +// Everything in the context must be thread safe + +class RequestData { + public: + virtual ~RequestData() = default; + + // Avoid calling RequestContext::setContextData, setContextDataIfAbsent, or + // clearContextData from these callbacks. Doing so will cause deadlock. We + // could fix these deadlocks, but only at significant performance penalty, so + // just don't do it! + + // hasCallback() applies only to onSet() and onUnset(). + // onClear() is always executed exactly once. + virtual bool hasCallback() = 0; + // Callback executed when setting RequestContext. Make sure your RequestData + // instance overrides the hasCallback method to return true otherwise + // the callback will not be executed + virtual void onSet() {} + // Callback executed when unsetting RequestContext. Make sure your RequestData + // instance overrides the hasCallback method to return true otherwise + // the callback will not be executed + virtual void onUnset() {} + // Callback executed exactly once upon the release of the last + // reference to the request data (as a result of either a call to + // clearContextData or the destruction of a request context that + // contains a reference to the data). It can be overridden in + // derived classes. There may be concurrent executions of onSet() + // and onUnset() with that of onClear(). + virtual void onClear() {} + // For debugging + int refCount() { + return keepAliveCounter_.load(std::memory_order_acquire); + } + + private: + // Start shallow copy implementation details: + // For efficiency, RequestContext provides a raw ptr interface. + // To support shallow copy, we need a shared ptr. + // To keep it as safe as possible (even if a raw ptr is passed back), + // the counter lives directly in RequestData. + + friend class RequestContext; + + static constexpr int kDeleteCount = 0x1; + static constexpr int kClearCount = 0x1000; + + // Reference-counting functions used by the hazptr-based implementation. + // Increment the reference count + void acquireRef(); + // Decrement the reference count. Clear only if last. + void releaseRefClearOnly(); + // Decrement the reference count. Delete only if last. + void releaseRefDeleteOnly(); + // Decrement the reference count. Clear and delete if last. + void releaseRefClearDelete(); + + // Unique ptr with custom destructor, decrement the counter + // and only free if 0 + struct DestructPtr { + void operator()(RequestData* ptr); + }; + struct SharedPtr : public std::unique_ptr<RequestData, DestructPtr> { + SharedPtr() = default; + using std::unique_ptr<RequestData, DestructPtr>::unique_ptr; + SharedPtr(const SharedPtr& other) : SharedPtr(constructPtr(other.get())) {} + SharedPtr& operator=(const SharedPtr& other) { + return operator=(constructPtr(other.get())); + } + SharedPtr(SharedPtr&&) = default; + SharedPtr& operator=(SharedPtr&&) = default; + }; + + // Initialize the pseudo-shared ptr, increment the counter + static SharedPtr constructPtr(RequestData* ptr); + + std::atomic<int> keepAliveCounter_{0}; + // End shallow copy +}; + +// If you do not call create() to create a unique request context, +// this default request context will always be returned, and is never +// copied between threads. +class RequestContext { + public: + RequestContext(); + RequestContext(RequestContext&& ctx) = delete; + RequestContext& operator=(const RequestContext&) = delete; + RequestContext& operator=(RequestContext&&) = delete; + + // copy ctor is disabled, use copyAsRoot/copyAsChild instead. + static std::shared_ptr<RequestContext> copyAsRoot( + const RequestContext& ctx, + intptr_t rootid); + static std::shared_ptr<RequestContext> copyAsChild(const RequestContext& ctx); + + // Create a unique request context for this request. + // It will be passed between queues / threads (where implemented), + // so it should be valid for the lifetime of the request. + static void create() { + setContext(std::make_shared<RequestContext>()); + } + + // Get the current context. + static RequestContext* get(); + + intptr_t getRootId() const { + return rootId_; + } + + struct RootIdInfo { + intptr_t id; + std::thread::id tid; + uint64_t tidOS; + }; + static std::vector<RootIdInfo> getRootIdsFromAllThreads(); + + // The following APIs are used to add, remove and access RequestData instance + // in the RequestContext instance, normally used for per-RequestContext + // tracking or callback on set and unset. These APIs are Thread-safe. + // These APIs are performance sensitive, so please ask if you need help + // profiling any use of these APIs. + + // Add RequestData instance "data" to this RequestContext instance, with + // string identifier "val". If the same string identifier has already been + // used, will print a warning message for the first time, clear the existing + // RequestData instance for "val", and **not** add "data". + void setContextData( + const RequestToken& token, + std::unique_ptr<RequestData> data); + void setContextData( + const std::string& val, + std::unique_ptr<RequestData> data) { + setContextData(RequestToken(val), std::move(data)); + } + + // Add RequestData instance "data" to this RequestContext instance, with + // string identifier "val". If the same string identifier has already been + // used, return false and do nothing. Otherwise add "data" and return true. + bool setContextDataIfAbsent( + const RequestToken& token, + std::unique_ptr<RequestData> data); + bool setContextDataIfAbsent( + const std::string& val, + std::unique_ptr<RequestData> data) { + return setContextDataIfAbsent(RequestToken(val), std::move(data)); + } + + // Remove the RequestData instance with string identifier "val", if it exists. + void clearContextData(const RequestToken& val); + void clearContextData(const std::string& val) { + clearContextData(RequestToken(val)); + } + + // Returns true if and only if the RequestData instance with string identifier + // "val" exists in this RequestContext instnace. + bool hasContextData(const RequestToken& val) const; + bool hasContextData(const std::string& val) const { + return hasContextData(RequestToken(val)); + } + + // Get (constant) raw pointer of the RequestData instance with string + // identifier "val" if it exists, otherwise returns null pointer. + RequestData* getContextData(const RequestToken& val); + const RequestData* getContextData(const RequestToken& val) const; + RequestData* getContextData(const std::string& val) { + return getContextData(RequestToken(val)); + } + const RequestData* getContextData(const std::string& val) const { + return getContextData(RequestToken(val)); + } + + void onSet(); + void onUnset(); + + // useHazptr + FOLLY_ALWAYS_INLINE bool useHazptr() const { + return useHazptr_; + } + + // The following API is used to pass the context through queues / threads. + // saveContext is called to get a shared_ptr to the context, and + // setContext is used to reset it on the other side of the queue. + // + // Whenever possible, use RequestContextScopeGuard instead of setContext + // to make sure that RequestContext is reset to the original value when + // we exit the scope. + // + // A shared_ptr is used, because many request may fan out across + // multiple threads, or do post-send processing, etc. + static std::shared_ptr<RequestContext> setContext( + std::shared_ptr<RequestContext> const& ctx); + static std::shared_ptr<RequestContext> setContext( + std::shared_ptr<RequestContext>&& newCtx_); + + static std::shared_ptr<RequestContext> saveContext() { + return getStaticContext().first; + } + + private: + struct Tag {}; + RequestContext(const RequestContext& ctx) = default; + + public: + RequestContext(const RequestContext& ctx, intptr_t rootid, Tag tag); + RequestContext(const RequestContext& ctx, Tag tag); + explicit RequestContext(intptr_t rootId); + using StaticContext = std::pair<std::shared_ptr<RequestContext>, intptr_t>; + + private: + static StaticContext& getStaticContext(); + + static std::shared_ptr<RequestContext> setContextLock( + std::shared_ptr<RequestContext>& newCtx, + StaticContext& staticCtx); + static std::shared_ptr<RequestContext> setContextHazptr( + std::shared_ptr<RequestContext>& newCtx, + StaticContext& staticCtx); + + // Start shallow copy guard implementation details: + // All methods are private to encourage proper use + friend struct ShallowCopyRequestContextScopeGuard; + + // This sets a shallow copy of the current context as current, + // then return the previous context (so it can be reset later). + static std::shared_ptr<RequestContext> setShallowCopyContext(); + + // Similar to setContextData, except it overwrites the data + // if already set (instead of warn + reset ptr). + void overwriteContextDataLock( + const RequestToken& token, + std::unique_ptr<RequestData> data); + void overwriteContextDataLock( + const std::string& val, + std::unique_ptr<RequestData> data) { + overwriteContextDataLock(RequestToken(val), std::move(data)); + } + // End shallow copy guard + + // For functions with a parameter safe, if safe is true then the + // caller guarantees that there are no concurrent readers or writers + // accessing the structure. + void overwriteContextDataHazptr( + const RequestToken& token, + std::unique_ptr<RequestData> data, + bool safe = false); + void overwriteContextDataHazptr( + const std::string& val, + std::unique_ptr<RequestData> data, + bool safe = false) { + overwriteContextDataHazptr(RequestToken(val), std::move(data), safe); + } + + enum class DoSetBehaviour { + SET, + SET_IF_ABSENT, + OVERWRITE, + }; + + bool doSetContextDataLock( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour); + bool doSetContextDataLock( + const std::string& val, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour) { + return doSetContextDataLock(RequestToken(val), data, behaviour); + } + + bool doSetContextDataHazptr( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe = false); + bool doSetContextDataHazptr( + const std::string& val, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe = false) { + return doSetContextDataHazptr(RequestToken(val), data, behaviour, safe); + } + + // State immplementation with sequential data structures protected by a + // read-write locks. + struct State { + // This must be optimized for lookup, its hot path is getContextData + // Efficiency of copying the container also matters in setShallowCopyContext + F14FastMap<RequestToken, RequestData::SharedPtr> requestData_; + // This must be optimized for iteration, its hot path is setContext + // We also use the fact that it's ordered to efficiently compute + // the difference with previous context + sorted_vector_set<RequestData*> callbackData_; + }; + folly::Synchronized<State> state_; + + // State implementation with single-writer multi-reader data + // structures protected by hazard pointers for readers and a lock + // for writers. + struct StateHazptr { + // Hazard pointer-protected combined structure for request data + // and callbacks. + struct Combined; + hazptr_obj_cohort<> cohort_; // For destruction order + std::atomic<Combined*> combined_{nullptr}; + std::mutex mutex_; + + StateHazptr(); + StateHazptr(const StateHazptr& o); + StateHazptr(StateHazptr&&) = delete; + StateHazptr& operator=(const StateHazptr&) = delete; + StateHazptr& operator=(StateHazptr&&) = delete; + ~StateHazptr(); + + private: + friend class RequestContext; + + struct SetContextDataResult { + bool changed; // Changes were made + bool unexpected; // Update was unexpected + Combined* replaced; // The combined structure was replaced + }; + + Combined* combined() const; + Combined* ensureCombined(); // Lazy allocation if needed + void setCombined(Combined* p); + Combined* expand(Combined* combined); + bool doSetContextData( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe); + bool hasContextData(const RequestToken& token) const; + RequestData* getContextData(const RequestToken& token); + const RequestData* getContextData(const RequestToken& token) const; + void onSet(); + void onUnset(); + void clearContextData(const RequestToken& token); + SetContextDataResult doSetContextDataHelper( + const RequestToken& token, + std::unique_ptr<RequestData>& data, + DoSetBehaviour behaviour, + bool safe); + Combined* eraseOldData( + Combined* cur, + const RequestToken& token, + RequestData* oldData, + bool safe); + Combined* insertNewData( + Combined* cur, + const RequestToken& token, + std::unique_ptr<RequestData>& data, + bool found); + }; // StateHazptr + StateHazptr stateHazptr_; + bool useHazptr_; + // Shallow copies keep a note of the root context + intptr_t rootId_; +}; + +/** + * Note: you probably want to use ShallowCopyRequestContextScopeGuard + * This resets all other RequestData for the duration of the scope! + */ +class RequestContextScopeGuard { + private: + std::shared_ptr<RequestContext> prev_; + + public: + RequestContextScopeGuard(const RequestContextScopeGuard&) = delete; + RequestContextScopeGuard& operator=(const RequestContextScopeGuard&) = delete; + RequestContextScopeGuard(RequestContextScopeGuard&&) = delete; + RequestContextScopeGuard& operator=(RequestContextScopeGuard&&) = delete; + + // Create a new RequestContext and reset to the original value when + // this goes out of scope. + RequestContextScopeGuard() : prev_(RequestContext::saveContext()) { + RequestContext::create(); + } + + // Set a RequestContext that was previously captured by saveContext(). It will + // be automatically reset to the original value when this goes out of scope. + explicit RequestContextScopeGuard(std::shared_ptr<RequestContext> const& ctx) + : prev_(RequestContext::setContext(ctx)) {} + explicit RequestContextScopeGuard(std::shared_ptr<RequestContext>&& ctx) + : prev_(RequestContext::setContext(std::move(ctx))) {} + + ~RequestContextScopeGuard() { + RequestContext::setContext(std::move(prev_)); + } +}; + +/** + * This guard maintains all the RequestData pointers of the parent. + * This allows to overwrite a specific RequestData pointer for the + * scope's duration, without breaking others. + * + * Only modified pointers will have their set/onset methods called + */ +struct ShallowCopyRequestContextScopeGuard { + ShallowCopyRequestContextScopeGuard() + : prev_(RequestContext::setShallowCopyContext()) {} + + /** + * Shallow copy then overwrite one specific RequestData + * + * Helper constructor which is a more efficient equivalent to + * "clearRequestData" then "setRequestData" after the guard. + */ + ShallowCopyRequestContextScopeGuard( + const RequestToken& token, + std::unique_ptr<RequestData> data) + : ShallowCopyRequestContextScopeGuard() { + auto ctx = RequestContext::get(); + if (ctx->useHazptr()) { + ctx->overwriteContextDataHazptr(token, std::move(data), true); + } else { + ctx->overwriteContextDataLock(token, std::move(data)); + } + } + ShallowCopyRequestContextScopeGuard( + const std::string& val, + std::unique_ptr<RequestData> data) + : ShallowCopyRequestContextScopeGuard() { + auto ctx = RequestContext::get(); + if (ctx->useHazptr()) { + ctx->overwriteContextDataHazptr(val, std::move(data), true); + } else { + ctx->overwriteContextDataLock(val, std::move(data)); + } + } + + ~ShallowCopyRequestContextScopeGuard() { + RequestContext::setContext(std::move(prev_)); + } + + ShallowCopyRequestContextScopeGuard( + const ShallowCopyRequestContextScopeGuard&) = delete; + ShallowCopyRequestContextScopeGuard& operator=( + const ShallowCopyRequestContextScopeGuard&) = delete; + ShallowCopyRequestContextScopeGuard(ShallowCopyRequestContextScopeGuard&&) = + delete; + ShallowCopyRequestContextScopeGuard& operator=( + ShallowCopyRequestContextScopeGuard&&) = delete; + + private: + std::shared_ptr<RequestContext> prev_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.cpp b/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.cpp new file mode 100644 index 000000000..0f89523fd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/SSLContext.h> + +#include <folly/Format.h> +#include <folly/Memory.h> +#include <folly/Random.h> +#include <folly/SharedMutex.h> +#include <folly/SpinLock.h> +#include <folly/ssl/Init.h> +#include <folly/system/ThreadId.h> + +// --------------------------------------------------------------------- +// SSLContext implementation +// --------------------------------------------------------------------- +namespace folly { +// +// For OpenSSL portability API + +// SSLContext implementation +SSLContext::SSLContext(SSLVersion version) { + folly::ssl::init(); + + ctx_ = SSL_CTX_new(SSLv23_method()); + if (ctx_ == nullptr) { + throw std::runtime_error("SSL_CTX_new: " + getErrors()); + } + + int opt = 0; + switch (version) { + case TLSv1: + opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + break; + case SSLv3: + opt = SSL_OP_NO_SSLv2; + break; + case TLSv1_2: + opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | + SSL_OP_NO_TLSv1_1; + break; + case SSLv2: + default: + // do nothing + break; + } + + // Disable TLS 1.3 by default, for now, if this version of OpenSSL + // supports it. There are some semantic differences (e.g. assumptions + // on getSession() returning a resumable session, SSL_CTX_set_ciphersuites, + // etc.) + // +#if FOLLY_OPENSSL_IS_110 + SSL_CTX_set_max_proto_version(ctx_, TLS1_2_VERSION); +#endif + + int newOpt = SSL_CTX_set_options(ctx_, opt); + DCHECK((newOpt & opt) == opt); + + SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY); + + checkPeerName_ = false; + + SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION); + + sslAcceptRunner_ = std::make_unique<SSLAcceptRunner>(); + +#if FOLLY_OPENSSL_HAS_SNI + SSL_CTX_set_tlsext_servername_callback(ctx_, baseServerNameOpenSSLCallback); + SSL_CTX_set_tlsext_servername_arg(ctx_, this); +#endif +} + +SSLContext::~SSLContext() { + if (ctx_ != nullptr) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + +#if FOLLY_OPENSSL_HAS_ALPN + deleteNextProtocolsStrings(); +#endif +} + +void SSLContext::ciphers(const std::string& ciphers) { + setCiphersOrThrow(ciphers); +} + +void SSLContext::setClientECCurvesList( + const std::vector<std::string>& ecCurves) { + if (ecCurves.empty()) { + return; + } +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + std::string ecCurvesList; + join(":", ecCurves, ecCurvesList); + int rc = SSL_CTX_set1_curves_list(ctx_, ecCurvesList.c_str()); + if (rc == 0) { + throw std::runtime_error("SSL_CTX_set1_curves_list " + getErrors()); + } +#endif +} + +void SSLContext::setServerECCurve(const std::string& curveName) { +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH) + EC_KEY* ecdh = nullptr; + int nid; + + /* + * Elliptic-Curve Diffie-Hellman parameters are either "named curves" + * from RFC 4492 section 5.1.1, or explicitly described curves over + * binary fields. OpenSSL only supports the "named curves", which provide + * maximum interoperability. + */ + + nid = OBJ_sn2nid(curveName.c_str()); + if (nid == 0) { + LOG(FATAL) << "Unknown curve name:" << curveName.c_str(); + } + ecdh = EC_KEY_new_by_curve_name(nid); + if (ecdh == nullptr) { + LOG(FATAL) << "Unable to create curve:" << curveName.c_str(); + } + + SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + EC_KEY_free(ecdh); +#else + throw std::runtime_error("Elliptic curve encryption not allowed"); +#endif +} + +SSLContext::SSLContext(SSL_CTX* ctx) : ctx_(ctx) { + if (SSL_CTX_up_ref(ctx) == 0) { + throw std::runtime_error("Failed to increment SSL_CTX refcount"); + } +} + +void SSLContext::setX509VerifyParam( + const ssl::X509VerifyParam& x509VerifyParam) { + if (!x509VerifyParam) { + return; + } + if (SSL_CTX_set1_param(ctx_, x509VerifyParam.get()) != 1) { + throw std::runtime_error("SSL_CTX_set1_param " + getErrors()); + } +} + +void SSLContext::setCiphersOrThrow(const std::string& ciphers) { + int rc = SSL_CTX_set_cipher_list(ctx_, ciphers.c_str()); + if (rc == 0) { + throw std::runtime_error("SSL_CTX_set_cipher_list: " + getErrors()); + } + providedCiphersString_ = ciphers; +} + +void SSLContext::setVerificationOption( + const SSLContext::SSLVerifyPeerEnum& verifyPeer) { + CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX); // dont recurse + verifyPeer_ = verifyPeer; +} + +int SSLContext::getVerificationMode( + const SSLContext::SSLVerifyPeerEnum& verifyPeer) { + CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX); + int mode = SSL_VERIFY_NONE; + switch (verifyPeer) { + // case SSLVerifyPeerEnum::USE_CTX: // can't happen + // break; + + case SSLVerifyPeerEnum::VERIFY: + mode = SSL_VERIFY_PEER; + break; + + case SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT: + mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + + case SSLVerifyPeerEnum::NO_VERIFY: + mode = SSL_VERIFY_NONE; + break; + case SSLVerifyPeerEnum::USE_CTX: + default: + break; + } + return mode; +} + +int SSLContext::getVerificationMode() { + return getVerificationMode(verifyPeer_); +} + +void SSLContext::authenticate( + bool checkPeerCert, + bool checkPeerName, + const std::string& peerName) { + int mode; + if (checkPeerCert) { + mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | + SSL_VERIFY_CLIENT_ONCE; + checkPeerName_ = checkPeerName; + peerFixedName_ = peerName; + } else { + mode = SSL_VERIFY_NONE; + checkPeerName_ = false; // can't check name without cert! + peerFixedName_.clear(); + } + SSL_CTX_set_verify(ctx_, mode, nullptr); +} + +void SSLContext::loadCertificate(const char* path, const char* format) { + if (path == nullptr || format == nullptr) { + throw std::invalid_argument( + "loadCertificateChain: either <path> or <format> is nullptr"); + } + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_certificate_chain_file(ctx_, path) != 1) { + int errnoCopy = errno; + std::string reason("SSL_CTX_use_certificate_chain_file: "); + reason.append(path); + reason.append(": "); + reason.append(getErrors(errnoCopy)); + throw std::runtime_error(reason); + } + } else { + throw std::runtime_error( + "Unsupported certificate format: " + std::string(format)); + } +} + +void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) { + if (cert.data() == nullptr) { + throw std::invalid_argument("loadCertificate: <cert> is nullptr"); + } + + ssl::BioUniquePtr bio(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + throw std::runtime_error("BIO_new: " + getErrors()); + } + + int written = BIO_write(bio.get(), cert.data(), int(cert.size())); + if (written <= 0 || static_cast<unsigned>(written) != cert.size()) { + throw std::runtime_error("BIO_write: " + getErrors()); + } + + ssl::X509UniquePtr x509( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (x509 == nullptr) { + throw std::runtime_error("PEM_read_bio_X509: " + getErrors()); + } + + if (SSL_CTX_use_certificate(ctx_, x509.get()) == 0) { + throw std::runtime_error("SSL_CTX_use_certificate: " + getErrors()); + } + + // Any further X509 PEM blocks are treated as additional certificates in + // the certificate chain. + constexpr size_t kMaxCertChain = 64; + + for (size_t i = 0; i < kMaxCertChain; i++) { + x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (x509 == nullptr) { + ERR_clear_error(); + return; + } + + if (SSL_CTX_add1_chain_cert(ctx_, x509.get()) == 0) { + throw std::runtime_error("SSL_CTX_add0_chain_cert: " + getErrors()); + } + } + + throw std::runtime_error( + "loadCertificateFromBufferPEM(): Too many certificates in chain"); +} + +void SSLContext::loadPrivateKey(const char* path, const char* format) { + if (path == nullptr || format == nullptr) { + throw std::invalid_argument( + "loadPrivateKey: either <path> or <format> is nullptr"); + } + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_PrivateKey_file(ctx_, path, SSL_FILETYPE_PEM) == 0) { + throw std::runtime_error("SSL_CTX_use_PrivateKey_file: " + getErrors()); + } + } else { + throw std::runtime_error( + "Unsupported private key format: " + std::string(format)); + } +} + +void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) { + if (pkey.data() == nullptr) { + throw std::invalid_argument("loadPrivateKey: <pkey> is nullptr"); + } + + ssl::BioUniquePtr bio(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + throw std::runtime_error("BIO_new: " + getErrors()); + } + + int written = BIO_write(bio.get(), pkey.data(), int(pkey.size())); + if (written <= 0 || static_cast<unsigned>(written) != pkey.size()) { + throw std::runtime_error("BIO_write: " + getErrors()); + } + + ssl::EvpPkeyUniquePtr key( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (key == nullptr) { + throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors()); + } + + if (SSL_CTX_use_PrivateKey(ctx_, key.get()) == 0) { + throw std::runtime_error("SSL_CTX_use_PrivateKey: " + getErrors()); + } +} + +void SSLContext::loadCertKeyPairFromBufferPEM( + folly::StringPiece cert, + folly::StringPiece pkey) { + loadCertificateFromBufferPEM(cert); + loadPrivateKeyFromBufferPEM(pkey); + if (!isCertKeyPairValid()) { + throw std::runtime_error("SSL certificate and private key do not match"); + } +} + +void SSLContext::loadCertKeyPairFromFiles( + const char* certPath, + const char* keyPath, + const char* certFormat, + const char* keyFormat) { + loadCertificate(certPath, certFormat); + loadPrivateKey(keyPath, keyFormat); + if (!isCertKeyPairValid()) { + throw std::runtime_error("SSL certificate and private key do not match"); + } +} + +bool SSLContext::isCertKeyPairValid() const { + return SSL_CTX_check_private_key(ctx_) == 1; +} + +void SSLContext::loadTrustedCertificates(const char* path) { + if (path == nullptr) { + throw std::invalid_argument("loadTrustedCertificates: <path> is nullptr"); + } + if (SSL_CTX_load_verify_locations(ctx_, path, nullptr) == 0) { + throw std::runtime_error("SSL_CTX_load_verify_locations: " + getErrors()); + } + ERR_clear_error(); +} + +void SSLContext::loadTrustedCertificates(X509_STORE* store) { + SSL_CTX_set_cert_store(ctx_, store); +} + +void SSLContext::loadClientCAList(const char* path) { + auto clientCAs = SSL_load_client_CA_file(path); + if (clientCAs == nullptr) { + LOG(ERROR) << "Unable to load ca file: " << path << " " << getErrors(); + return; + } + SSL_CTX_set_client_CA_list(ctx_, clientCAs); +} + +void SSLContext::passwordCollector( + std::shared_ptr<PasswordCollector> collector) { + if (collector == nullptr) { + LOG(ERROR) << "passwordCollector: ignore invalid password collector"; + return; + } + collector_ = collector; + SSL_CTX_set_default_passwd_cb(ctx_, passwordCallback); + SSL_CTX_set_default_passwd_cb_userdata(ctx_, this); +} + +#if FOLLY_OPENSSL_HAS_SNI + +void SSLContext::setServerNameCallback(const ServerNameCallback& cb) { + serverNameCb_ = cb; +} + +void SSLContext::addClientHelloCallback(const ClientHelloCallback& cb) { + clientHelloCbs_.push_back(cb); +} + +int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) { + auto context = (SSLContext*)data; + + if (context == nullptr) { + return SSL_TLSEXT_ERR_NOACK; + } + + for (auto& cb : context->clientHelloCbs_) { + // Generic callbacks to happen after we receive the Client Hello. + // For example, we use one to switch which cipher we use depending + // on the user's TLS version. Because the primary purpose of + // baseServerNameOpenSSLCallback is for SNI support, and these callbacks + // are side-uses, we ignore any possible failures other than just logging + // them. + cb(ssl); + } + + if (!context->serverNameCb_) { + return SSL_TLSEXT_ERR_NOACK; + } + + ServerNameCallbackResult ret = context->serverNameCb_(ssl); + switch (ret) { + case SERVER_NAME_FOUND: + return SSL_TLSEXT_ERR_OK; + case SERVER_NAME_NOT_FOUND: + return SSL_TLSEXT_ERR_NOACK; + case SERVER_NAME_NOT_FOUND_ALERT_FATAL: + *al = TLS1_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + default: + CHECK(false); + } + + return SSL_TLSEXT_ERR_NOACK; +} +#endif // FOLLY_OPENSSL_HAS_SNI + +#if FOLLY_OPENSSL_HAS_ALPN +int SSLContext::alpnSelectCallback( + SSL* /* ssl */, + const unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* data) { + auto context = (SSLContext*)data; + CHECK(context); + if (context->advertisedNextProtocols_.empty()) { + *out = nullptr; + *outlen = 0; + } else { + auto i = context->pickNextProtocols(); + const auto& item = context->advertisedNextProtocols_[i]; + if (SSL_select_next_proto( + (unsigned char**)out, + outlen, + item.protocols, + item.length, + in, + inlen) != OPENSSL_NPN_NEGOTIATED) { + return SSL_TLSEXT_ERR_NOACK; + } + } + return SSL_TLSEXT_ERR_OK; +} + +bool SSLContext::setAdvertisedNextProtocols( + const std::list<std::string>& protocols) { + return setRandomizedAdvertisedNextProtocols({{1, protocols}}); +} + +bool SSLContext::setRandomizedAdvertisedNextProtocols( + const std::list<NextProtocolsItem>& items) { + unsetNextProtocols(); + if (items.empty()) { + return false; + } + int total_weight = 0; + for (const auto& item : items) { + if (item.protocols.empty()) { + continue; + } + AdvertisedNextProtocolsItem advertised_item; + advertised_item.length = 0; + for (const auto& proto : item.protocols) { + ++advertised_item.length; + auto protoLength = proto.length(); + if (protoLength >= 256) { + deleteNextProtocolsStrings(); + return false; + } + advertised_item.length += unsigned(protoLength); + } + advertised_item.protocols = new unsigned char[advertised_item.length]; + if (!advertised_item.protocols) { + throw std::runtime_error("alloc failure"); + } + unsigned char* dst = advertised_item.protocols; + for (auto& proto : item.protocols) { + auto protoLength = uint8_t(proto.length()); + *dst++ = (unsigned char)protoLength; + memcpy(dst, proto.data(), protoLength); + dst += protoLength; + } + total_weight += item.weight; + advertisedNextProtocols_.push_back(advertised_item); + advertisedNextProtocolWeights_.push_back(item.weight); + } + if (total_weight == 0) { + deleteNextProtocolsStrings(); + return false; + } + nextProtocolDistribution_ = std::discrete_distribution<>( + advertisedNextProtocolWeights_.begin(), + advertisedNextProtocolWeights_.end()); + SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this); + // Client cannot really use randomized alpn + // Note that this function reverses the typical return value convention + // of openssl and returns 0 on success. + return SSL_CTX_set_alpn_protos( + ctx_, + advertisedNextProtocols_[0].protocols, + advertisedNextProtocols_[0].length) == 0; +} + +void SSLContext::deleteNextProtocolsStrings() { + for (auto protocols : advertisedNextProtocols_) { + delete[] protocols.protocols; + } + advertisedNextProtocols_.clear(); + advertisedNextProtocolWeights_.clear(); +} + +void SSLContext::unsetNextProtocols() { + deleteNextProtocolsStrings(); + SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr); + SSL_CTX_set_alpn_protos(ctx_, nullptr, 0); + // clear the error stack here since openssl internals sometimes add a + // malloc failure when doing a memdup of NULL, 0.. + ERR_clear_error(); +} + +size_t SSLContext::pickNextProtocols() { + CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols"; + auto rng = ThreadLocalPRNG(); + return size_t(nextProtocolDistribution_(rng)); +} + +#endif // FOLLY_OPENSSL_HAS_ALPN + +SSL* SSLContext::createSSL() const { + SSL* ssl = SSL_new(ctx_); + if (ssl == nullptr) { + throw std::runtime_error("SSL_new: " + getErrors()); + } + return ssl; +} + +void SSLContext::setSessionCacheContext(const std::string& context) { + SSL_CTX_set_session_id_context( + ctx_, + reinterpret_cast<const unsigned char*>(context.data()), + std::min<unsigned int>( + static_cast<unsigned int>(context.length()), SSL_MAX_SID_CTX_LENGTH)); +} + +/** + * Match a name with a pattern. The pattern may include wildcard. A single + * wildcard "*" can match up to one component in the domain name. + * + * @param host Host name, typically the name of the remote host + * @param pattern Name retrieved from certificate + * @param size Size of "pattern" + * @return True, if "host" matches "pattern". False otherwise. + */ +bool SSLContext::matchName(const char* host, const char* pattern, int size) { + bool match = false; + int i = 0, j = 0; + while (i < size && host[j] != '\0') { + if (toupper(pattern[i]) == toupper(host[j])) { + i++; + j++; + continue; + } + if (pattern[i] == '*') { + while (host[j] != '.' && host[j] != '\0') { + j++; + } + i++; + continue; + } + break; + } + if (i == size && host[j] == '\0') { + match = true; + } + return match; +} + +int SSLContext::passwordCallback(char* password, int size, int, void* data) { + auto context = (SSLContext*)data; + if (context == nullptr || context->passwordCollector() == nullptr) { + return 0; + } + std::string userPassword; + // call user defined password collector to get password + context->passwordCollector()->getPassword(userPassword, size); + auto const length = std::min(userPassword.size(), size_t(size)); + std::memcpy(password, userPassword.data(), length); + return int(length); +} + +#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) +void SSLContext::enableFalseStart() { + SSL_CTX_set_mode(ctx_, SSL_MODE_HANDSHAKE_CUTTHROUGH); +} +#endif + +void SSLContext::initializeOpenSSL() { + folly::ssl::init(); +} + +void SSLContext::setOptions(long options) { + long newOpt = SSL_CTX_set_options(ctx_, options); + if ((newOpt & options) != options) { + throw std::runtime_error("SSL_CTX_set_options failed"); + } +} + +std::string SSLContext::getErrors(int errnoCopy) { + std::string errors; + unsigned long errorCode; + char message[256]; + + errors.reserve(512); + while ((errorCode = ERR_get_error()) != 0) { + if (!errors.empty()) { + errors += "; "; + } + const char* reason = ERR_reason_error_string(errorCode); + if (reason == nullptr) { + snprintf(message, sizeof(message) - 1, "SSL error # %08lX", errorCode); + reason = message; + } + errors += reason; + } + if (errors.empty()) { + errors = "error code: " + folly::to<std::string>(errnoCopy); + } + return errors; +} + +void SSLContext::enableTLS13() { +#if FOLLY_OPENSSL_IS_110 + SSL_CTX_set_max_proto_version(ctx_, 0); +#endif +} + +std::ostream& operator<<(std::ostream& os, const PasswordCollector& collector) { + os << collector.describe(); + return os; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.h b/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.h new file mode 100644 index 000000000..9b180c090 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/SSLContext.h @@ -0,0 +1,644 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <random> +#include <string> +#include <vector> + +#include <glog/logging.h> + +#include <folly/Function.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/String.h> +#include <folly/io/async/ssl/OpenSSLUtils.h> +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/OpenSSLLockTypes.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { + +/** + * Override the default password collector. + */ +class PasswordCollector { + public: + virtual ~PasswordCollector() = default; + /** + * Interface for customizing how to collect private key password. + * + * By default, OpenSSL prints a prompt on screen and request for password + * while loading private key. To implement a custom password collector, + * implement this interface and register it with SSLContext. + * + * @param password Pass collected password back to OpenSSL + * @param size Maximum length of password including nullptr character + */ + virtual void getPassword(std::string& password, int size) const = 0; + + /** + * Return a description of this collector for logging purposes + */ + virtual const std::string& describe() const = 0; +}; + +/** + * Run SSL_accept via a runner + */ +class SSLAcceptRunner { + public: + virtual ~SSLAcceptRunner() = default; + + /** + * This is expected to run the first function and provide its return + * value to the second function. This can be used to run the SSL_accept + * in different contexts. + */ + virtual void run(Function<int()> acceptFunc, Function<void(int)> finallyFunc) + const { + finallyFunc(acceptFunc()); + } +}; + +/** + * Wrap OpenSSL SSL_CTX into a class. + */ +class SSLContext { + public: + enum SSLVersion { + SSLv2, + SSLv3, + TLSv1, // support TLS 1.0+ + TLSv1_2, // support for only TLS 1.2+ + }; + + /** + * Defines the way that peers are verified. + **/ + enum SSLVerifyPeerEnum { + // Used by AsyncSSLSocket to delegate to the SSLContext's setting + USE_CTX, + // For server side - request a client certificate and verify the + // certificate if it is sent. Does not fail if the client does not present + // a certificate. + // For client side - validates the server certificate or fails. + VERIFY, + // For server side - same as VERIFY but will fail if no certificate + // is sent. + // For client side - same as VERIFY. + VERIFY_REQ_CLIENT_CERT, + // No verification is done for both server and client side. + NO_VERIFY + }; + + struct NextProtocolsItem { + NextProtocolsItem(int wt, const std::list<std::string>& ptcls) + : weight(wt), protocols(ptcls) {} + int weight; + std::list<std::string> protocols; + }; + + // Function that selects a client protocol given the server's list + using ClientProtocolFilterCallback = bool (*)( + unsigned char**, + unsigned int*, + const unsigned char*, + unsigned int); + + /** + * Convenience function to call getErrors() with the current errno value. + * + * Make sure that you only call this when there was no intervening operation + * since the last OpenSSL error that may have changed the current errno value. + */ + static std::string getErrors() { + return getErrors(errno); + } + + /** + * Constructor. + * + * @param version The lowest or oldest SSL version to support. + */ + explicit SSLContext(SSLVersion version = TLSv1); + /** + * Constructor that helps ease migrations by directly wrapping a provided + * SSL_CTX* + */ + explicit SSLContext(SSL_CTX* ctx); + virtual ~SSLContext(); + + /** + * Set default ciphers to be used in SSL handshake process. + * + * @param ciphers A list of ciphers to use for TLSv1.0 + */ + virtual void ciphers(const std::string& ciphers); + + /** + * Low-level method that attempts to set the provided ciphers on the + * SSL_CTX object, and throws if something goes wrong. + */ + virtual void setCiphersOrThrow(const std::string& ciphers); + + /** + * Set default ciphers to be used in SSL handshake process. + */ + + template <typename Iterator> + void setCipherList(Iterator ibegin, Iterator iend) { + if (ibegin != iend) { + std::string opensslCipherList; + folly::join(":", ibegin, iend, opensslCipherList); + setCiphersOrThrow(opensslCipherList); + } + } + + template <typename Container> + void setCipherList(const Container& cipherList) { + using namespace std; + setCipherList(begin(cipherList), end(cipherList)); + } + + template <typename Value> + void setCipherList(const std::initializer_list<Value>& cipherList) { + setCipherList(cipherList.begin(), cipherList.end()); + } + + /** + * Sets the signature algorithms to be used during SSL negotiation + * for TLS1.2+. + */ + + template <typename Iterator> + void setSignatureAlgorithms(Iterator ibegin, Iterator iend) { + if (ibegin != iend) { +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + std::string opensslSigAlgsList; + join(":", ibegin, iend, opensslSigAlgsList); + if (!SSL_CTX_set1_sigalgs_list(ctx_, opensslSigAlgsList.c_str())) { + throw std::runtime_error("SSL_CTX_set1_sigalgs_list " + getErrors()); + } +#endif + } + } + + template <typename Container> + void setSignatureAlgorithms(const Container& sigalgs) { + using namespace std; + setSignatureAlgorithms(begin(sigalgs), end(sigalgs)); + } + + template <typename Value> + void setSignatureAlgorithms(const std::initializer_list<Value>& sigalgs) { + setSignatureAlgorithms(sigalgs.begin(), sigalgs.end()); + } + + /** + * Sets the list of EC curves supported by the client. + * + * @param ecCurves A list of ec curves, eg: P-256 + */ + void setClientECCurvesList(const std::vector<std::string>& ecCurves); + + /** + * Method to add support for a specific elliptic curve encryption algorithm. + * + * @param curveName: The name of the ec curve to support, eg: prime256v1. + */ + void setServerECCurve(const std::string& curveName); + + /** + * Sets an x509 verification param on the context. + */ + void setX509VerifyParam(const ssl::X509VerifyParam& x509VerifyParam); + + /** + * Method to set verification option in the context object. + * + * @param verifyPeer SSLVerifyPeerEnum indicating the verification + * method to use. + */ + virtual void setVerificationOption(const SSLVerifyPeerEnum& verifyPeer); + + /** + * Method to check if peer verfication is set. + * + * @return true if peer verification is required. + * + */ + virtual bool needsPeerVerification() { + return ( + verifyPeer_ == SSLVerifyPeerEnum::VERIFY || + verifyPeer_ == SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT); + } + + /** + * Method to fetch Verification mode for a SSLVerifyPeerEnum. + * verifyPeer cannot be SSLVerifyPeerEnum::USE_CTX since there is no + * context. + * + * @param verifyPeer SSLVerifyPeerEnum for which the flags need to + * to be returned + * + * @return mode flags that can be used with SSL_set_verify + */ + static int getVerificationMode(const SSLVerifyPeerEnum& verifyPeer); + + /** + * Method to fetch Verification mode determined by the options + * set using setVerificationOption. + * + * @return mode flags that can be used with SSL_set_verify + */ + virtual int getVerificationMode(); + + /** + * Enable/Disable authentication. Peer name validation can only be done + * if checkPeerCert is true. + * + * @param checkPeerCert If true, require peer to present valid certificate + * @param checkPeerName If true, validate that the certificate common name + * or alternate name(s) of peer matches the hostname + * used to connect. + * @param peerName If non-empty, validate that the certificate common + * name of peer matches the given string (altername + * name(s) are not used in this case). + */ + virtual void authenticate( + bool checkPeerCert, + bool checkPeerName, + const std::string& peerName = std::string()); + /** + * Loads a certificate chain stored on disk to be sent to the peer during + * TLS connection establishment. + * + * @param path Path to the certificate file + * @param format Certificate file format + */ + virtual void loadCertificate(const char* path, const char* format = "PEM"); + /** + * Loads a PEM formatted certificate chain from memory to be sent to the peer + * during TLS connection establishment. + * + * @param cert A PEM formatted certificate + */ + virtual void loadCertificateFromBufferPEM(folly::StringPiece cert); + + /** + * Load private key. + * + * @param path Path to the private key file + * @param format Private key file format + */ + virtual void loadPrivateKey(const char* path, const char* format = "PEM"); + /** + * Load private key from memory. + * + * @param pkey A PEM formatted key + */ + virtual void loadPrivateKeyFromBufferPEM(folly::StringPiece pkey); + + /** + * Load cert and key from PEM buffers. Guaranteed to throw if cert and + * private key mismatch so no need to call isCertKeyPairValid. + * + * @param cert A PEM formatted certificate + * @param pkey A PEM formatted key + */ + virtual void loadCertKeyPairFromBufferPEM( + folly::StringPiece cert, + folly::StringPiece pkey); + + /** + * Load cert and key from files. Guaranteed to throw if cert and key mismatch. + * Equivalent to calling loadCertificate() and loadPrivateKey(). + * + * @param certPath Path to the certificate file + * @param keyPath Path to the private key file + * @param certFormat Certificate file format + * @param keyFormat Private key file format + */ + virtual void loadCertKeyPairFromFiles( + const char* certPath, + const char* keyPath, + const char* certFormat = "PEM", + const char* keyFormat = "PEM"); + + /** + * Call after both cert and key are loaded to check if cert matches key. + * Must call if private key is loaded before loading the cert. + * No need to call if cert is loaded first before private key. + * @return true if matches, or false if mismatch. + */ + virtual bool isCertKeyPairValid() const; + + /** + * Load trusted certificates from specified file. + * + * @param path Path to trusted certificate file + */ + virtual void loadTrustedCertificates(const char* path); + /** + * Load trusted certificates from specified X509 certificate store. + * + * @param store X509 certificate store. + */ + virtual void loadTrustedCertificates(X509_STORE* store); + /** + * Load a client CA list for validating clients + */ + virtual void loadClientCAList(const char* path); + /** + * Override default OpenSSL password collector. + * + * @param collector Instance of user defined password collector + */ + virtual void passwordCollector(std::shared_ptr<PasswordCollector> collector); + /** + * Obtain password collector. + * + * @return User defined password collector + */ + virtual std::shared_ptr<PasswordCollector> passwordCollector() { + return collector_; + } +#if FOLLY_OPENSSL_HAS_SNI + /** + * Provide SNI support + */ + enum ServerNameCallbackResult { + SERVER_NAME_FOUND, + SERVER_NAME_NOT_FOUND, + SERVER_NAME_NOT_FOUND_ALERT_FATAL, + }; + /** + * Callback function from openssl to give the application a + * chance to check the tlsext_hostname just right after parsing + * the Client Hello or Server Hello message. + * + * It is for the server to switch the SSL to another SSL_CTX + * to continue the handshake. (i.e. Server Name Indication, SNI, in RFC6066). + * + * If the ServerNameCallback returns: + * SERVER_NAME_FOUND: + * server: Send a tlsext_hostname in the Server Hello + * client: No-effect + * SERVER_NAME_NOT_FOUND: + * server: Does not send a tlsext_hostname in Server Hello + * and continue the handshake. + * client: No-effect + * SERVER_NAME_NOT_FOUND_ALERT_FATAL: + * server and client: Send fatal TLS1_AD_UNRECOGNIZED_NAME alert to + * the peer. + * + * Quote from RFC 6066: + * "... + * If the server understood the ClientHello extension but + * does not recognize the server name, the server SHOULD take one of two + * actions: either abort the handshake by sending a fatal-level + * unrecognized_name(112) alert or continue the handshake. It is NOT + * RECOMMENDED to send a warning-level unrecognized_name(112) alert, + * because the client's behavior in response to warning-level alerts is + * unpredictable. + * ..." + */ + + /** + * Set the ServerNameCallback + */ + typedef std::function<ServerNameCallbackResult(SSL* ssl)> ServerNameCallback; + virtual void setServerNameCallback(const ServerNameCallback& cb); + + /** + * Generic callbacks that are run after we get the Client Hello (right + * before we run the ServerNameCallback) + */ + typedef std::function<void(SSL* ssl)> ClientHelloCallback; + virtual void addClientHelloCallback(const ClientHelloCallback& cb); +#endif // FOLLY_OPENSSL_HAS_SNI + + /** + * Create an SSL object from this context. + */ + SSL* createSSL() const; + + /** + * Sets the namespace to use for sessions created from this context. + */ + void setSessionCacheContext(const std::string& context); + + /** + * Set the options on the SSL_CTX object. + */ + void setOptions(long options); + +#if FOLLY_OPENSSL_HAS_ALPN + /** + * Set the list of protocols that this SSL context supports. In client + * mode, this is the list of protocols that will be advertised for Application + * Layer Protocol Negotiation (ALPN). In server mode, the first protocol + * advertised by the client that is also on this list is chosen. + * Invoking this function with a list of length zero causes ALPN to be + * disabled. + * + * @param protocols List of protocol names. This method makes a copy, + * so the caller needn't keep the list in scope after + * the call completes. The list must have at least + * one element to enable ALPN. Each element must have + * a string length < 256. + * @return true if ALPN has been activated. False if ALPN is disabled. + */ + bool setAdvertisedNextProtocols(const std::list<std::string>& protocols); + /** + * Set weighted list of lists of protocols that this SSL context supports. + * In server mode, each element of the list contains a list of protocols that + * could be advertised for Application Layer Protocol Negotiation (ALPN). + * The list of protocols that will be advertised to a client is selected + * randomly, based on weights of elements. Client mode doesn't support + * randomized ALPN, so this list should contain only 1 element. The first + * protocol advertised by the client that is also on the list of protocols + * of this element is chosen. Invoking this function with a list of length + * zero causes ALPN to be disabled. + * + * @param items List of NextProtocolsItems, Each item contains a list of + * protocol names and weight. After the call of this fucntion + * each non-empty list of protocols will be advertised with + * probability weight/sum_of_weights. This method makes a copy, + * so the caller needn't keep the list in scope after the call + * completes. The list must have at least one element with + * non-zero weight and non-empty protocols list to enable NPN. + * Each name of the protocol must have a string length < 256. + * @return true if ALPN has been activated. False if ALPN is disabled. + */ + bool setRandomizedAdvertisedNextProtocols( + const std::list<NextProtocolsItem>& items); + + /** + * Disables ALPN on this SSL context. + */ + void unsetNextProtocols(); + void deleteNextProtocolsStrings(); +#endif // FOLLY_OPENSSL_HAS_ALPN + + /** + * Gets the underlying SSL_CTX for advanced usage + */ + SSL_CTX* getSSLCtx() const { + return ctx_; + } + + /** + * Examine OpenSSL's error stack, and return a string description of the + * errors. + * + * This operation removes the errors from OpenSSL's error stack. + */ + static std::string getErrors(int errnoCopy); + + bool checkPeerName() { + return checkPeerName_; + } + std::string peerFixedName() { + return peerFixedName_; + } + +#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) + /** + * Enable TLS false start, saving a roundtrip for full handshakes. Will only + * be used if the server uses NPN or ALPN, and a strong forward-secure cipher + * is negotiated. + */ + void enableFalseStart(); +#endif + + /** + * Sets the runner used for SSL_accept. If none is given, the accept will be + * done directly. + */ + void sslAcceptRunner(std::unique_ptr<SSLAcceptRunner> runner) { + if (nullptr == runner) { + LOG(ERROR) << "Ignore invalid runner"; + return; + } + sslAcceptRunner_ = std::move(runner); + } + + const SSLAcceptRunner* sslAcceptRunner() { + return sslAcceptRunner_.get(); + } + + /** + * Helper to match a hostname versus a pattern. + */ + static bool matchName(const char* host, const char* pattern, int size); + + /** + * Temporary. Will be removed after TLS1.3 is enabled by default. + * Function to enable TLS1.3 in OpenSSL versions that support it. + * Used to migrate users to TLS1.3 piecemeal. + */ + void enableTLS13(); + + [[deprecated("Use folly::ssl::init")]] static void initializeOpenSSL(); + + protected: + SSL_CTX* ctx_; + + private: + SSLVerifyPeerEnum verifyPeer_{SSLVerifyPeerEnum::NO_VERIFY}; + + bool checkPeerName_; + std::string peerFixedName_; + std::shared_ptr<PasswordCollector> collector_; +#if FOLLY_OPENSSL_HAS_SNI + ServerNameCallback serverNameCb_; + std::vector<ClientHelloCallback> clientHelloCbs_; +#endif + + ClientProtocolFilterCallback clientProtoFilter_{nullptr}; + + static bool initialized_; + + std::unique_ptr<SSLAcceptRunner> sslAcceptRunner_; + +#if FOLLY_OPENSSL_HAS_ALPN + + struct AdvertisedNextProtocolsItem { + unsigned char* protocols; + unsigned length; + }; + + /** + * Wire-format list of advertised protocols for use in NPN. + */ + std::vector<AdvertisedNextProtocolsItem> advertisedNextProtocols_; + std::vector<int> advertisedNextProtocolWeights_; + std::discrete_distribution<int> nextProtocolDistribution_; + + static int advertisedNextProtocolCallback( + SSL* ssl, + const unsigned char** out, + unsigned int* outlen, + void* data); + + static int alpnSelectCallback( + SSL* ssl, + const unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* data); + + size_t pickNextProtocols(); + +#endif // FOLLY_OPENSSL_HAS_ALPN + + static int passwordCallback(char* password, int size, int, void* data); + +#if FOLLY_OPENSSL_HAS_SNI + /** + * The function that will be called directly from openssl + * in order for the application to get the tlsext_hostname just after + * parsing the Client Hello or Server Hello message. It will then call + * the serverNameCb_ function object. Hence, it is sort of a + * wrapper/proxy between serverNameCb_ and openssl. + * + * The openssl's primary intention is for SNI support, but we also use it + * generically for performing logic after the Client Hello comes in. + */ + static int baseServerNameOpenSSLCallback( + SSL* ssl, + int* al /* alert (return value) */, + void* data); +#endif + + std::string providedCiphersString_; +}; + +typedef std::shared_ptr<SSLContext> SSLContextPtr; + +std::ostream& operator<<( + std::ostream& os, + const folly::PasswordCollector& collector); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.cpp b/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.cpp new file mode 100644 index 000000000..c1074e841 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/SSLOptions.h> +#include <folly/Format.h> +#include <glog/logging.h> + +namespace folly { +namespace ssl { + +namespace ssl_options_detail { +void logDfatal(std::exception const& e) { + LOG(DFATAL) << exceptionStr(e); +} +} // namespace ssl_options_detail + +void SSLCommonOptions::setClientOptions(SSLContext& ctx) { +#ifdef SSL_MODE_HANDSHAKE_CUTTHROUGH + ctx.enableFalseStart(); +#endif + + X509VerifyParam param(X509_VERIFY_PARAM_new()); + X509_VERIFY_PARAM_set_flags(param.get(), X509_V_FLAG_X509_STRICT); + try { + ctx.setX509VerifyParam(param); + } catch (std::runtime_error const& e) { + LOG(DFATAL) << exceptionStr(e); + } + + try { + ctx.setClientECCurvesList({"P-256", "P-384"}); + } catch (std::runtime_error const& e) { + LOG(DFATAL) << exceptionStr(e); + } + + setCipherSuites<SSLCommonOptions>(ctx); + setSignatureAlgorithms<SSLCommonOptions>(ctx); +} + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.h b/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.h new file mode 100644 index 000000000..b60ecde58 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/SSLOptions.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/container/Array.h> +#include <folly/io/async/SSLContext.h> + +namespace folly { +namespace ssl { + +namespace ssl_options_detail { +void logDfatal(std::exception const&); +} // namespace ssl_options_detail + +struct SSLCommonOptions { + /** + * The cipher list recommended for this options configuration. + */ + static constexpr auto ciphers() { + return folly::make_array( + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES256-SHA", + "ECDHE-RSA-AES256-SHA", + "ECDHE-ECDSA-AES128-SHA", + "ECDHE-RSA-AES128-SHA", + "ECDHE-RSA-AES256-SHA384", + "AES128-GCM-SHA256", + "AES256-SHA", + "AES128-SHA"); + } + + /** + * The list of signature algorithms recommended for this options + * configuration. + */ + static constexpr auto sigalgs() { + return folly::make_array( + "RSA+SHA512", + "ECDSA+SHA512", + "RSA+SHA384", + "ECDSA+SHA384", + "RSA+SHA256", + "ECDSA+SHA256", + "RSA+SHA1", + "ECDSA+SHA1"); + } + + /** + * Set common parameters on a client SSL context, for example, + * ciphers, signature algorithms, verification options, and client EC curves. + * @param ctx The SSL Context to which to apply the options. + */ + static void setClientOptions(SSLContext& ctx); +}; + +/** + * Recommended SSL options for server-side scenario. + */ +struct SSLServerOptions { + /** + * The list of ciphers recommended for server use. + */ + static constexpr auto ciphers() { + return folly::make_array( + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES128-SHA", + "ECDHE-ECDSA-AES256-SHA", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES128-SHA", + "ECDHE-RSA-AES256-SHA", + "AES128-GCM-SHA256", + "AES256-GCM-SHA384", + "AES128-SHA", + "AES256-SHA"); + } +}; + +/** + * Set the cipher suite of ctx to that in TSSLOptions, and print any runtime + * error it catches. + * @param ctx The SSLContext to apply the desired SSL options to. + */ +template <typename TSSLOptions> +void setCipherSuites(SSLContext& ctx) { + try { + ctx.setCipherList(TSSLOptions::ciphers()); + } catch (std::runtime_error const& e) { + ssl_options_detail::logDfatal(e); + } +} + +/** + * Set the cipher suite of ctx to the passed in cipherList, + * and print any runtime error it catches. + * @param ctx The SSLContext to apply the desired SSL options to. + * @param cipherList the list of ciphersuites to set + */ +template <typename Container> +void setCipherSuites(SSLContext& ctx, const Container& cipherList) { + try { + ctx.setCipherList(cipherList); + } catch (std::runtime_error const& e) { + ssl_options_detail::logDfatal(e); + } +} + +/** + * Set the signature algorithm list of ctx to that in TSSLOptions, and print + * any runtime errors it catche. + * @param ctx The SSLContext to apply the desired SSL options to. + */ +template <typename TSSLOptions> +void setSignatureAlgorithms(SSLContext& ctx) { + try { + ctx.setSignatureAlgorithms(TSSLOptions::sigalgs()); + } catch (std::runtime_error const& e) { + ssl_options_detail::logDfatal(e); + } +} + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.cpp b/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.cpp new file mode 100644 index 000000000..056f7105d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/ScopedEventBaseThread.h> + +#include <thread> + +#include <folly/Function.h> +#include <folly/Range.h> +#include <folly/io/async/EventBaseManager.h> +#include <folly/system/ThreadName.h> + +using namespace std; + +namespace folly { + +static void run( + EventBaseManager* ebm, + EventBase* eb, + folly::Baton<>* stop, + const StringPiece& name) { + if (!name.empty()) { + folly::setThreadName(name); + } + + ebm->setEventBase(eb, false); + eb->loopForever(); + + // must destruct in io thread for on-destruction callbacks + eb->runOnDestruction([=] { ebm->clearEventBase(); }); + // wait until terminateLoopSoon() is complete + stop->wait(folly::Baton<>::wait_options().logging_enabled(false)); + eb->~EventBase(); +} + +ScopedEventBaseThread::ScopedEventBaseThread() + : ScopedEventBaseThread(nullptr, "") {} + +ScopedEventBaseThread::ScopedEventBaseThread(StringPiece name) + : ScopedEventBaseThread(nullptr, name) {} + +ScopedEventBaseThread::ScopedEventBaseThread(EventBaseManager* ebm) + : ScopedEventBaseThread(ebm, "") {} + +ScopedEventBaseThread::ScopedEventBaseThread( + EventBaseManager* ebm, + StringPiece name) + : ScopedEventBaseThread( + std::unique_ptr<EventBaseBackendBase>(), + ebm, + name) {} + +ScopedEventBaseThread::ScopedEventBaseThread( + std::unique_ptr<EventBaseBackendBase>&& backend, + EventBaseManager* ebm, + StringPiece name) + : ebm_(ebm ? ebm : EventBaseManager::get()) { + new (&eb_) EventBase(std::move(backend)); + th_ = thread(run, ebm_, &eb_, &stop_, name); + eb_.waitUntilRunning(); +} + +ScopedEventBaseThread::~ScopedEventBaseThread() { + eb_.terminateLoopSoon(); + stop_.post(); + th_.join(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h b/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h new file mode 100644 index 000000000..5091fdb1f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <thread> + +#include <folly/io/async/EventBase.h> +#include <folly/synchronization/Baton.h> + +namespace folly { + +class EventBaseManager; +template <class Iter> +class Range; +typedef Range<const char*> StringPiece; + +/** + * A helper class to start a new thread running a EventBase loop. + * + * The new thread will be started by the ScopedEventBaseThread constructor. + * When the ScopedEventBaseThread object is destroyed, the thread will be + * stopped. + */ +class ScopedEventBaseThread : public IOExecutor, public SequencedExecutor { + public: + ScopedEventBaseThread(); + explicit ScopedEventBaseThread(StringPiece name); + explicit ScopedEventBaseThread(EventBaseManager* ebm); + explicit ScopedEventBaseThread(EventBaseManager* ebm, StringPiece name); + explicit ScopedEventBaseThread( + std::unique_ptr<EventBaseBackendBase>&& backend, + EventBaseManager* ebm, + StringPiece name); + ~ScopedEventBaseThread(); + + EventBase* getEventBase() const { + return &eb_; + } + + EventBase* getEventBase() override { + return &eb_; + } + + std::thread::id getThreadId() const { + return th_.get_id(); + } + + void add(Func func) override { + getEventBase()->add(std::move(func)); + } + + protected: + bool keepAliveAcquire() override { + return getEventBase()->keepAliveAcquire(); + } + + void keepAliveRelease() override { + getEventBase()->keepAliveRelease(); + } + + private: + ScopedEventBaseThread(ScopedEventBaseThread&& other) = delete; + ScopedEventBaseThread& operator=(ScopedEventBaseThread&& other) = delete; + + ScopedEventBaseThread(const ScopedEventBaseThread& other) = delete; + ScopedEventBaseThread& operator=(const ScopedEventBaseThread& other) = delete; + + EventBaseManager* ebm_; + union { + mutable EventBase eb_; + }; + std::thread th_; + folly::Baton<> stop_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.cpp b/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.cpp new file mode 100644 index 000000000..6edfb5404 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/TimeoutManager.h> + +#include <boost/intrusive/list.hpp> + +#include <folly/Chrono.h> +#include <folly/Exception.h> +#include <folly/Memory.h> +#include <folly/io/async/AsyncTimeout.h> + +#include <glog/logging.h> + +namespace folly { + +struct TimeoutManager::CobTimeouts { + // small object used as a callback arg with enough info to execute the + // appropriate client-provided Cob + class CobTimeout : public AsyncTimeout { + public: + CobTimeout(TimeoutManager* timeoutManager, Func cob, InternalEnum internal) + : AsyncTimeout(timeoutManager, internal), cob_(std::move(cob)) {} + + void timeoutExpired() noexcept override { + // For now, we just swallow any exceptions that the callback threw. + try { + cob_(); + } catch (const std::exception& ex) { + LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw " + << typeid(ex).name() << " exception: " << ex.what(); + } catch (...) { + LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw " + << "non-exception type"; + } + + // The CobTimeout object was allocated on the heap by runAfterDelay(), + // so delete it now that the it has fired. + delete this; + } + + private: + Func cob_; + + public: + using ListHook = boost::intrusive::list_member_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>>; + ListHook hook; + using List = boost::intrusive::list< + CobTimeout, + boost::intrusive::member_hook<CobTimeout, ListHook, &CobTimeout::hook>, + boost::intrusive::constant_time_size<false>>; + }; + + CobTimeout::List list; +}; + +TimeoutManager::TimeoutManager() + : cobTimeouts_(std::make_unique<CobTimeouts>()) {} + +bool TimeoutManager::scheduleTimeoutHighRes( + AsyncTimeout* obj, + timeout_type_high_res timeout) { + timeout_type timeout_ms = folly::chrono::ceil<timeout_type>(timeout); + return scheduleTimeout(obj, timeout_ms); +} + +void TimeoutManager::runAfterDelay( + Func cob, + uint32_t milliseconds, + InternalEnum internal) { + if (!tryRunAfterDelay(std::move(cob), milliseconds, internal)) { + folly::throwSystemError( + "error in TimeoutManager::runAfterDelay(), failed to schedule timeout"); + } +} + +bool TimeoutManager::tryRunAfterDelay( + Func cob, + uint32_t milliseconds, + InternalEnum internal) { + if (!cobTimeouts_) { + return false; + } + + auto timeout = + std::make_unique<CobTimeouts::CobTimeout>(this, std::move(cob), internal); + if (!timeout->scheduleTimeout(milliseconds)) { + return false; + } + cobTimeouts_->list.push_back(*timeout.release()); + return true; +} + +void TimeoutManager::clearCobTimeouts() { + if (!cobTimeouts_) { + return; + } + + // Delete any unfired callback objects, so that we don't leak memory + // Note that we don't fire them. + while (!cobTimeouts_->list.empty()) { + auto* timeout = &cobTimeouts_->list.front(); + delete timeout; + } +} + +TimeoutManager::~TimeoutManager() { + clearCobTimeouts(); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.h b/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.h new file mode 100644 index 000000000..b5a646ef5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/TimeoutManager.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <cstdint> + +#include <folly/Function.h> +#include <folly/Optional.h> + +namespace folly { + +class AsyncTimeout; + +/** + * Base interface to be implemented by all classes expecting to manage + * timeouts. AsyncTimeout will use implementations of this interface + * to schedule/cancel timeouts. + */ +class TimeoutManager { + public: + typedef std::chrono::milliseconds timeout_type; + typedef std::chrono::microseconds timeout_type_high_res; + + using Func = folly::Function<void()>; + + enum class InternalEnum { INTERNAL, NORMAL }; + + TimeoutManager(); + + virtual ~TimeoutManager(); + + /** + * Attaches/detaches TimeoutManager to AsyncTimeout + */ + virtual void attachTimeoutManager( + AsyncTimeout* obj, + InternalEnum internal) = 0; + virtual void detachTimeoutManager(AsyncTimeout* obj) = 0; + + /** + * Schedules AsyncTimeout to fire after `timeout` milliseconds + */ + virtual bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) = 0; + + /** + * Schedules AsyncTimeout to fire after `timeout` microseconds + */ + virtual bool scheduleTimeoutHighRes( + AsyncTimeout* obj, + timeout_type_high_res timeout); + + /** + * Cancels the AsyncTimeout, if scheduled + */ + virtual void cancelTimeout(AsyncTimeout* obj) = 0; + + /** + * This is used to mark the beginning of a new loop cycle by the + * first handler fired within that cycle. + */ + virtual void bumpHandlingTime() = 0; + + /** + * Helper method to know whether we are running in the timeout manager + * thread + */ + virtual bool isInTimeoutManagerThread() = 0; + + /** + * Runs the given Cob at some time after the specified number of + * milliseconds. (No guarantees exactly when.) + * + * Throws a std::system_error if an error occurs. + */ + void runAfterDelay( + Func cob, + uint32_t milliseconds, + InternalEnum internal = InternalEnum::NORMAL); + + /** + * @see tryRunAfterDelay for more details + * + * @return true iff the cob was successfully registered. + */ + bool tryRunAfterDelay( + Func cob, + uint32_t milliseconds, + InternalEnum internal = InternalEnum::NORMAL); + + protected: + void clearCobTimeouts(); + + private: + struct CobTimeouts; + std::unique_ptr<CobTimeouts> cobTimeouts_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.cpp b/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.cpp new file mode 100644 index 000000000..2b9c4c41a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/VirtualEventBase.h> + +namespace folly { + +VirtualEventBase::VirtualEventBase(EventBase& evb) + : evb_(getKeepAliveToken(evb)) {} + +std::future<void> VirtualEventBase::destroy() { + evb_->runInEventBaseThread([this] { loopKeepAlive_.reset(); }); + + return std::move(destroyFuture_); +} + +void VirtualEventBase::destroyImpl() { + try { + { + // After destroyPromise_ is posted this object may be destroyed, so make + // sure we release EventBase's keep-alive token before that. + SCOPE_EXIT { + evb_.reset(); + }; + + clearCobTimeouts(); + + while (!onDestructionCallbacks_.rlock()->empty()) { + // To avoid potential deadlock, do not hold the mutex while invoking + // user-supplied callbacks. + EventBase::OnDestructionCallback::List callbacks; + onDestructionCallbacks_.swap(callbacks); + while (!callbacks.empty()) { + auto& callback = callbacks.front(); + callbacks.pop_front(); + callback.runCallback(); + } + } + } + + destroyPromise_.set_value(); + } catch (...) { + destroyPromise_.set_exception(std::current_exception()); + } +} + +VirtualEventBase::~VirtualEventBase() { + if (!destroyFuture_.valid()) { + return; + } + CHECK(!evb_->inRunningEventBaseThread()); + destroy().get(); +} + +void VirtualEventBase::runOnDestruction( + EventBase::OnDestructionCallback& callback) { + callback.schedule( + [this](auto& cb) { onDestructionCallbacks_.wlock()->push_back(cb); }, + [this](auto& cb) { + onDestructionCallbacks_.withWLock( + [&](auto& list) { list.erase(list.iterator_to(cb)); }); + }); +} + +void VirtualEventBase::runOnDestruction(Func f) { + auto* callback = new EventBase::FunctionOnDestructionCallback(std::move(f)); + runOnDestruction(*callback); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.h b/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.h new file mode 100644 index 000000000..72078b053 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/VirtualEventBase.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <future> + +#include <folly/Executor.h> +#include <folly/Function.h> +#include <folly/Synchronized.h> +#include <folly/io/async/EventBase.h> +#include <folly/synchronization/Baton.h> + +namespace folly { + +/** + * VirtualEventBase implements a light-weight view onto existing EventBase. + * + * Multiple VirtualEventBases can be backed by a single EventBase. Similarly + * to EventBase, VirtualEventBase implements loopKeepAlive() functionality, + * which allows callbacks holding KeepAlive token to keep EventBase looping + * until they are complete. + * + * VirtualEventBase destructor blocks until all its KeepAliveTokens are released + * and all tasks scheduled through it are complete. EventBase destructor also + * blocks until all VirtualEventBases backed by it are released. + */ +class VirtualEventBase : public folly::Executor, public folly::TimeoutManager { + public: + explicit VirtualEventBase(EventBase& evb); + + VirtualEventBase(const VirtualEventBase&) = delete; + VirtualEventBase& operator=(const VirtualEventBase&) = delete; + + ~VirtualEventBase() override; + + EventBase& getEventBase() { + return *evb_; + } + + /** + * Adds the given callback to a queue of things run before destruction + * of current VirtualEventBase. + * + * This allows users of VirtualEventBase that run in it, but don't control it, + * to be notified before VirtualEventBase gets destructed. + * + * Note: this will be called from the loop of the EventBase, backing this + * VirtualEventBase + */ + void runOnDestruction(EventBase::OnDestructionCallback& callback); + void runOnDestruction(Func f); + + /** + * VirtualEventBase destructor blocks until all tasks scheduled through its + * runInEventBaseThread are complete. + * + * @see EventBase::runInEventBaseThread + */ + template <typename F> + void runInEventBaseThread(F&& f) noexcept { + // KeepAlive token has to be released in the EventBase thread. If + // runInEventBaseThread() fails, we can't extract the KeepAlive token + // from the callback to properly release it. + evb_->runInEventBaseThread([keepAliveToken = getKeepAliveToken(this), + f = std::forward<F>(f)]() mutable { f(); }); + } + + HHWheelTimer& timer() { + return evb_->timer(); + } + + void attachTimeoutManager( + AsyncTimeout* obj, + TimeoutManager::InternalEnum internal) override { + evb_->attachTimeoutManager(obj, internal); + } + + void detachTimeoutManager(AsyncTimeout* obj) override { + evb_->detachTimeoutManager(obj); + } + + bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout) + override { + return evb_->scheduleTimeout(obj, timeout); + } + + void cancelTimeout(AsyncTimeout* obj) override { + evb_->cancelTimeout(obj); + } + + void bumpHandlingTime() override { + evb_->bumpHandlingTime(); + } + + bool isInTimeoutManagerThread() override { + return evb_->isInTimeoutManagerThread(); + } + + /** + * @see runInEventBaseThread + */ + void add(folly::Func f) override { + runInEventBaseThread(std::move(f)); + } + + bool inRunningEventBaseThread() const { + return evb_->inRunningEventBaseThread(); + } + + protected: + bool keepAliveAcquire() override { + if (evb_->inRunningEventBaseThread()) { + DCHECK(loopKeepAliveCount_ + loopKeepAliveCountAtomic_.load() > 0); + + ++loopKeepAliveCount_; + } else { + ++loopKeepAliveCountAtomic_; + } + return true; + } + + void keepAliveReleaseEvb() { + if (loopKeepAliveCountAtomic_.load()) { + loopKeepAliveCount_ += loopKeepAliveCountAtomic_.exchange(0); + } + DCHECK(loopKeepAliveCount_ > 0); + if (--loopKeepAliveCount_ == 0) { + destroyImpl(); + } + } + + void keepAliveRelease() override { + if (!evb_->inRunningEventBaseThread()) { + evb_->add([=] { keepAliveReleaseEvb(); }); + return; + } + + keepAliveReleaseEvb(); + } + + private: + friend class EventBase; + + ssize_t keepAliveCount() { + if (loopKeepAliveCountAtomic_.load()) { + loopKeepAliveCount_ += loopKeepAliveCountAtomic_.exchange(0); + } + return loopKeepAliveCount_; + } + + std::future<void> destroy(); + void destroyImpl(); + + using LoopCallbackList = EventBase::LoopCallback::List; + + KeepAlive<EventBase> evb_; + + ssize_t loopKeepAliveCount_{1}; + std::atomic<ssize_t> loopKeepAliveCountAtomic_{0}; + std::promise<void> destroyPromise_; + std::future<void> destroyFuture_{destroyPromise_.get_future()}; + KeepAlive<VirtualEventBase> loopKeepAlive_{ + makeKeepAlive<VirtualEventBase>(this)}; + + Synchronized<EventBase::OnDestructionCallback::List> onDestructionCallbacks_; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h b/ios/Pods/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h new file mode 100644 index 000000000..ed94b2051 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/IOBuf.h> +#include <folly/io/async/AsyncTransport.h> +#include <folly/io/async/DecoratedAsyncTransportWrapper.h> + +namespace folly { + +/** + * Helper class that redirects write() and writev() calls to writeChain(). + */ +template <class T> +class WriteChainAsyncTransportWrapper + : public DecoratedAsyncTransportWrapper<T> { + public: + using DecoratedAsyncTransportWrapper<T>::DecoratedAsyncTransportWrapper; + + void write( + folly::AsyncTransportWrapper::WriteCallback* callback, + const void* buf, + size_t bytes, + folly::WriteFlags flags = folly::WriteFlags::NONE) override { + auto ioBuf = folly::IOBuf::wrapBuffer(buf, bytes); + writeChain(callback, std::move(ioBuf), flags); + } + + void writev( + folly::AsyncTransportWrapper::WriteCallback* callback, + const iovec* vec, + size_t count, + folly::WriteFlags flags = folly::WriteFlags::NONE) override { + auto writeBuffer = folly::IOBuf::wrapIov(vec, count); + writeChain(callback, std::move(writeBuffer), flags); + } + + /** + * It only makes sense to use this class if you override writeChain, so force + * derived classes to do that. + */ + void writeChain( + folly::AsyncTransportWrapper::WriteCallback* callback, + std::unique_ptr<folly::IOBuf>&& buf, + folly::WriteFlags flags = folly::WriteFlags::NONE) override = 0; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h b/ios/Pods/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h new file mode 100644 index 000000000..06a1efc50 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/AsyncTransportCertificate.h> +#include <memory> + +namespace folly { +namespace ssl { + +class BasicTransportCertificate : public folly::AsyncTransportCertificate { + public: + // Create a basic transport cert from an existing one. Returns nullptr + // if cert is null. + static std::unique_ptr<BasicTransportCertificate> create( + const folly::AsyncTransportCertificate* cert) { + if (!cert) { + return nullptr; + } + return std::make_unique<BasicTransportCertificate>( + cert->getIdentity(), cert->getX509()); + } + + BasicTransportCertificate( + std::string identity, + folly::ssl::X509UniquePtr x509) + : identity_(std::move(identity)), x509_(std::move(x509)) {} + + std::string getIdentity() const override { + return identity_; + } + + folly::ssl::X509UniquePtr getX509() const override { + if (!x509_) { + return nullptr; + } + auto x509raw = x509_.get(); + X509_up_ref(x509raw); + return folly::ssl::X509UniquePtr(x509raw); + } + + private: + std::string identity_; + folly::ssl::X509UniquePtr x509_; +}; + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.cpp b/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.cpp new file mode 100644 index 000000000..9f4f4bafc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/ssl/OpenSSLUtils.h> + +#include <glog/logging.h> + +#include <unordered_map> + +#include <folly/ScopeGuard.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Unistd.h> +#include <folly/ssl/Init.h> + +namespace { +#ifdef OPENSSL_IS_BORINGSSL +// BoringSSL doesn't (as of May 2016) export the equivalent +// of BIO_sock_should_retry, so this is one way around it :( +static int boringssl_bio_fd_should_retry(int err); +#endif + +} // namespace + +namespace folly { +namespace ssl { + +bool OpenSSLUtils::getTLSMasterKey( + const SSL_SESSION* session, + MutableByteRange keyOut) { +#if FOLLY_OPENSSL_IS_110 + auto size = SSL_SESSION_get_master_key(session, nullptr, 0); + if (size == keyOut.size()) { + return SSL_SESSION_get_master_key(session, keyOut.begin(), keyOut.size()); + } +#else + (void)session; + (void)keyOut; +#endif + return false; +} + +bool OpenSSLUtils::getTLSClientRandom( + const SSL* ssl, + MutableByteRange randomOut) { +#if FOLLY_OPENSSL_IS_110 + auto size = SSL_get_client_random(ssl, nullptr, 0); + if (size == randomOut.size()) { + return SSL_get_client_random(ssl, randomOut.begin(), randomOut.size()); + } +#else + (void)ssl; + (void)randomOut; +#endif + return false; +} + +bool OpenSSLUtils::getPeerAddressFromX509StoreCtx( + X509_STORE_CTX* ctx, + sockaddr_storage* addrStorage, + socklen_t* addrLen) { + // Grab the ssl idx and then the ssl object so that we can get the peer + // name to compare against the ips in the subjectAltName + auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx(); + auto ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, sslIdx)); + int fd = SSL_get_fd(ssl); + if (fd < 0) { + LOG(ERROR) << "Inexplicably couldn't get fd from SSL"; + return false; + } + + *addrLen = sizeof(*addrStorage); + if (getpeername(fd, reinterpret_cast<sockaddr*>(addrStorage), addrLen) != 0) { + PLOG(ERROR) << "Unable to get peer name"; + return false; + } + CHECK(*addrLen <= sizeof(*addrStorage)); + return true; +} + +bool OpenSSLUtils::validatePeerCertNames( + X509* cert, + const sockaddr* addr, + socklen_t /* addrLen */) { + // Try to extract the names within the SAN extension from the certificate + auto altNames = reinterpret_cast<STACK_OF(GENERAL_NAME)*>( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + SCOPE_EXIT { + if (altNames != nullptr) { + sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free); + } + }; + if (altNames == nullptr) { + LOG(WARNING) << "No subjectAltName provided and we only support ip auth"; + return false; + } + + const sockaddr_in* addr4 = nullptr; + const sockaddr_in6* addr6 = nullptr; + if (addr != nullptr) { + if (addr->sa_family == AF_INET) { + addr4 = reinterpret_cast<const sockaddr_in*>(addr); + } else if (addr->sa_family == AF_INET6) { + addr6 = reinterpret_cast<const sockaddr_in6*>(addr); + } else { + LOG(FATAL) << "Unsupported sockaddr family: " << addr->sa_family; + } + } + + for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) { + auto name = sk_GENERAL_NAME_value(altNames, i); + if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) { + // Extra const-ness for paranoia + unsigned char const* const rawIpStr = name->d.iPAddress->data; + auto const rawIpLen = size_t(name->d.iPAddress->length); + + if (rawIpLen == 4 && addr4 != nullptr) { + if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) { + return true; + } + } else if (rawIpLen == 16 && addr6 != nullptr) { + if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) { + return true; + } + } else if (rawIpLen != 4 && rawIpLen != 16) { + LOG(WARNING) << "Unexpected IP length: " << rawIpLen; + } + } + } + + LOG(WARNING) << "Unable to match client cert against alt name ip"; + return false; +} + +static std::unordered_map<uint16_t, std::string> getOpenSSLCipherNames() { + folly::ssl::init(); + std::unordered_map<uint16_t, std::string> ret; + SSL_CTX* ctx = nullptr; + SSL* ssl = nullptr; + + const SSL_METHOD* meth = TLS_server_method(); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + OpenSSL_add_ssl_algorithms(); +#endif + + if ((ctx = SSL_CTX_new(meth)) == nullptr) { + return ret; + } + SCOPE_EXIT { + SSL_CTX_free(ctx); + }; + + if ((ssl = SSL_new(ctx)) == nullptr) { + return ret; + } + SCOPE_EXIT { + SSL_free(ssl); + }; + + STACK_OF(SSL_CIPHER)* sk = SSL_get_ciphers(ssl); + for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + const SSL_CIPHER* c = sk_SSL_CIPHER_value(sk, i); + unsigned long id = SSL_CIPHER_get_id(c); + // OpenSSL 1.0.2 and prior does weird things such as stuff the SSL/TLS + // version into the top 16 bits. Let's ignore those for now. This is + // BoringSSL compatible (their id can be cast as uint16_t) + uint16_t cipherCode = id & 0xffffL; + ret[cipherCode] = SSL_CIPHER_get_name(c); + } + return ret; +} + +const std::string& OpenSSLUtils::getCipherName(uint16_t cipherCode) { + // Having this in a hash map saves the binary search inside OpenSSL + static std::unordered_map<uint16_t, std::string> cipherCodeToName( + getOpenSSLCipherNames()); + + const auto& iter = cipherCodeToName.find(cipherCode); + if (iter != cipherCodeToName.end()) { + return iter->second; + } else { + static std::string empty(""); + return empty; + } +} + +void OpenSSLUtils::setSSLInitialCtx(SSL* ssl, SSL_CTX* ctx) { + (void)ssl; + (void)ctx; +#if !FOLLY_OPENSSL_IS_110 && !defined(OPENSSL_NO_TLSEXT) + if (ssl) { + if (ctx) { + SSL_CTX_up_ref(ctx); + } + ssl->initial_ctx = ctx; + } +#endif +} + +SSL_CTX* OpenSSLUtils::getSSLInitialCtx(SSL* ssl) { + (void)ssl; +#if !FOLLY_OPENSSL_IS_110 && !defined(OPENSSL_NO_TLSEXT) + if (ssl) { + return ssl->initial_ctx; + } +#endif + return nullptr; +} + +BioMethodUniquePtr OpenSSLUtils::newSocketBioMethod() { + BIO_METHOD* newmeth = nullptr; +#if FOLLY_OPENSSL_IS_110 + if (!(newmeth = BIO_meth_new(BIO_TYPE_SOCKET, "socket_bio_method"))) { + return nullptr; + } + auto meth = const_cast<BIO_METHOD*>(BIO_s_socket()); + BIO_meth_set_create(newmeth, BIO_meth_get_create(meth)); + BIO_meth_set_destroy(newmeth, BIO_meth_get_destroy(meth)); + BIO_meth_set_ctrl(newmeth, BIO_meth_get_ctrl(meth)); + BIO_meth_set_callback_ctrl(newmeth, BIO_meth_get_callback_ctrl(meth)); + BIO_meth_set_read(newmeth, BIO_meth_get_read(meth)); + BIO_meth_set_write(newmeth, BIO_meth_get_write(meth)); + BIO_meth_set_gets(newmeth, BIO_meth_get_gets(meth)); + BIO_meth_set_puts(newmeth, BIO_meth_get_puts(meth)); +#else + if (!(newmeth = (BIO_METHOD*)OPENSSL_malloc(sizeof(BIO_METHOD)))) { + return nullptr; + } + memcpy(newmeth, BIO_s_socket(), sizeof(BIO_METHOD)); +#endif + + return BioMethodUniquePtr(newmeth); +} + +bool OpenSSLUtils::setCustomBioReadMethod( + BIO_METHOD* bioMeth, + int (*meth)(BIO*, char*, int)) { + bool ret = false; + ret = (BIO_meth_set_read(bioMeth, meth) == 1); + return ret; +} + +bool OpenSSLUtils::setCustomBioWriteMethod( + BIO_METHOD* bioMeth, + int (*meth)(BIO*, const char*, int)) { + bool ret = false; + ret = (BIO_meth_set_write(bioMeth, meth) == 1); + return ret; +} + +int OpenSSLUtils::getBioShouldRetryWrite(int r) { + int ret = 0; +#ifdef OPENSSL_IS_BORINGSSL + ret = boringssl_bio_fd_should_retry(r); +#else + ret = BIO_sock_should_retry(r); +#endif + return ret; +} + +void OpenSSLUtils::setBioAppData(BIO* b, void* ptr) { +#ifdef OPENSSL_IS_BORINGSSL + BIO_set_callback_arg(b, static_cast<char*>(ptr)); +#else + BIO_set_app_data(b, ptr); +#endif +} + +void* OpenSSLUtils::getBioAppData(BIO* b) { +#ifdef OPENSSL_IS_BORINGSSL + return BIO_get_callback_arg(b); +#else + return BIO_get_app_data(b); +#endif +} + +NetworkSocket OpenSSLUtils::getBioFd(BIO* b) { + auto ret = BIO_get_fd(b, nullptr); +#ifdef _WIN32 + return NetworkSocket((SOCKET)ret); +#else + return NetworkSocket(ret); +#endif +} + +void OpenSSLUtils::setBioFd(BIO* b, NetworkSocket fd, int flags) { +#ifdef _WIN32 + // Internally OpenSSL uses this as an int for reasons completely + // beyond any form of sanity, so we do the cast ourselves to avoid + // the warnings that would be generated. + int sock = int(fd.data); +#else + int sock = fd.toFd(); +#endif + BIO_set_fd(b, sock, flags); +} + +std::string OpenSSLUtils::getCommonName(X509* x509) { + if (x509 == nullptr) { + return ""; + } + X509_NAME* subject = X509_get_subject_name(x509); + std::string cn; + cn.resize(ub_common_name); + X509_NAME_get_text_by_NID( + subject, NID_commonName, const_cast<char*>(cn.data()), ub_common_name); + return cn; +} + +} // namespace ssl +} // namespace folly + +namespace { +#ifdef OPENSSL_IS_BORINGSSL + +static int boringssl_bio_fd_non_fatal_error(int err) { + if ( +#ifdef EWOULDBLOCK + err == EWOULDBLOCK || +#endif +#ifdef WSAEWOULDBLOCK + err == WSAEWOULDBLOCK || +#endif +#ifdef ENOTCONN + err == ENOTCONN || +#endif +#ifdef EINTR + err == EINTR || +#endif +#ifdef EAGAIN + err == EAGAIN || +#endif +#ifdef EPROTO + err == EPROTO || +#endif +#ifdef EINPROGRESS + err == EINPROGRESS || +#endif +#ifdef EALREADY + err == EALREADY || +#endif + 0) { + return 1; + } + return 0; +} + +#if defined(OPENSSL_WINDOWS) + +int boringssl_bio_fd_should_retry(int i) { + if (i == -1) { + return boringssl_bio_fd_non_fatal_error((int)GetLastError()); + } + return 0; +} + +#else // !OPENSSL_WINDOWS + +int boringssl_bio_fd_should_retry(int i) { + if (i == -1) { + return boringssl_bio_fd_non_fatal_error(errno); + } + return 0; +} +#endif // OPENSSL_WINDOWS + +#endif // OEPNSSL_IS_BORINGSSL + +} // namespace diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h b/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h new file mode 100644 index 000000000..d4fd3fd64 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Range.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/OpenSSL.h> +#include <folly/portability/Sockets.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { +namespace ssl { + +class OpenSSLUtils { + public: + /* + * Get the TLS Session Master Key used to generate the TLS key material + * + * @param session ssl session + * @param keyOut destination for the master key, the buffer must be at least + * 48 bytes + * @return true if the master key is available (>= TLS1) and the output buffer + * large enough + */ + static bool getTLSMasterKey( + const SSL_SESSION* session, + MutableByteRange keyOut); + + /* + * Get the TLS Client Random used to generate the TLS key material + * + * @param ssl + * @param randomOut destination for the client random, the buffer must be at + * least 32 bytes + * @return true if the client random is available (>= TLS1) and the output + * buffer large enough + */ + static bool getTLSClientRandom(const SSL* ssl, MutableByteRange randomOut); + + /** + * Validate that the peer certificate's common name or subject alt names + * match what we expect. Currently this only checks for IPs within + * subject alt names but it could easily be expanded to check common name + * and hostnames as well. + * + * @param cert X509* peer certificate + * @param addr sockaddr object containing sockaddr to verify + * @param addrLen length of sockaddr as returned by getpeername or accept + * @return true iff a subject altname IP matches addr + */ + // TODO(agartrell): Add support for things like common name when + // necessary. + static bool + validatePeerCertNames(X509* cert, const sockaddr* addr, socklen_t addrLen); + + /** + * Get the peer socket address from an X509_STORE_CTX*. Unlike the + * accept, getsockname, getpeername, etc family of operations, addrLen's + * initial value is ignored and reset. + * + * @param ctx Context from which to retrieve peer sockaddr + * @param addrStorage out param for address + * @param addrLen out param for length of address + * @return true on success, false on failure + */ + static bool getPeerAddressFromX509StoreCtx( + X509_STORE_CTX* ctx, + sockaddr_storage* addrStorage, + socklen_t* addrLen); + + /** + * Get a stringified cipher name (e.g., ECDHE-ECDSA-CHACHA20-POLY1305) given + * the 2-byte code (e.g., 0xcca9) for the cipher. The name conversion only + * works for the ciphers built into the linked OpenSSL library + * + * @param cipherCode A 16-bit IANA cipher code (machine endianness) + * @return Cipher name, or empty if the code is not found + */ + static const std::string& getCipherName(uint16_t cipherCode); + + /** + * Set the 'initial_ctx' SSL_CTX* inside an SSL. The initial_ctx is used to + * point to the SSL_CTX on which servername callback and session callbacks, + * as well as session caching stats are set. If we want to enforce SSL_CTX + * thread-based ownership (e.g., thread-local SSL_CTX) in the application, we + * need to also set/reset the initial_ctx when we call SSL_set_SSL_CTX. + * + * @param ssl SSL pointer + * @param ctx SSL_CTX pointer + * @return Cipher name, or empty if the code is not found + */ + static void setSSLInitialCtx(SSL* ssl, SSL_CTX* ctx); + static SSL_CTX* getSSLInitialCtx(SSL* ssl); + + /** + * Get the common name out of a cert. Return empty if x509 is null. + */ + static std::string getCommonName(X509* x509); + + /** + * Wrappers for BIO operations that may be different across different + * versions/flavors of OpenSSL (including forks like BoringSSL) + */ + static BioMethodUniquePtr newSocketBioMethod(); + static bool setCustomBioReadMethod( + BIO_METHOD* bioMeth, + int (*meth)(BIO*, char*, int)); + static bool setCustomBioWriteMethod( + BIO_METHOD* bioMeth, + int (*meth)(BIO*, const char*, int)); + static int getBioShouldRetryWrite(int ret); + static void setBioAppData(BIO* b, void* ptr); + static void* getBioAppData(BIO* b); + static NetworkSocket getBioFd(BIO* b); + static void setBioFd(BIO* b, NetworkSocket fd, int flags); +}; + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.cpp b/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.cpp new file mode 100644 index 000000000..439c17805 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/io/async/ssl/SSLErrors.h> + +#include <folly/Range.h> +#include <folly/portability/OpenSSL.h> + +using namespace folly; + +namespace { + +std::string decodeOpenSSLError( + int sslError, + unsigned long errError, + int sslOperationReturnValue) { + if (sslError == SSL_ERROR_SYSCALL && errError == 0) { + if (sslOperationReturnValue == 0) { + return "Connection EOF"; + } else { + // In this case errno is set, AsyncSocketException will add it. + return "Network error"; + } + } else if (sslError == SSL_ERROR_ZERO_RETURN) { + // This signifies a TLS closure alert. + return "SSL connection closed normally"; + } else { + std::array<char, 256> buf; + ERR_error_string_n(errError, buf.data(), buf.size()); + // OpenSSL will null terminate the string. + return std::string(buf.data()); + } +} + +StringPiece getSSLErrorString(SSLError error) { + StringPiece ret; + switch (error) { + case SSLError::CLIENT_RENEGOTIATION: + ret = "Client tried to renegotiate with server"; + break; + case SSLError::INVALID_RENEGOTIATION: + ret = "Attempt to start renegotiation, but unsupported"; + break; + case SSLError::EARLY_WRITE: + ret = "Attempt to write before SSL connection established"; + break; + case SSLError::SSL_ERROR: + ret = "SSL error"; + break; + case SSLError::NETWORK_ERROR: + ret = "Network error"; + break; + case SSLError::EOF_ERROR: + ret = "SSL connection closed normally"; + break; + } + return ret; +} + +AsyncSocketException::AsyncSocketExceptionType exTypefromSSLErrInfo( + int sslErr, + unsigned long errError, + int sslOperationReturnValue) { + if (sslErr == SSL_ERROR_ZERO_RETURN) { + return AsyncSocketException::END_OF_FILE; + } else if (sslErr == SSL_ERROR_SYSCALL) { + if (errError == 0 && sslOperationReturnValue == 0) { + return AsyncSocketException::END_OF_FILE; + } else { + return AsyncSocketException::NETWORK_ERROR; + } + } else { + // Assume an actual SSL error + return AsyncSocketException::SSL_ERROR; + } +} + +AsyncSocketException::AsyncSocketExceptionType exTypefromSSLErr(SSLError err) { + switch (err) { + case SSLError::EOF_ERROR: + return AsyncSocketException::END_OF_FILE; + case SSLError::NETWORK_ERROR: + return AsyncSocketException::NETWORK_ERROR; + case SSLError::CLIENT_RENEGOTIATION: + case SSLError::INVALID_RENEGOTIATION: + case SSLError::EARLY_WRITE: + case SSLError::SSL_ERROR: + default: + // everything else is a SSL_ERROR + return AsyncSocketException::SSL_ERROR; + } +} +} // namespace + +namespace folly { + +SSLException::SSLException( + int sslErr, + unsigned long errError, + int sslOperationReturnValue, + int errno_copy) + : AsyncSocketException( + exTypefromSSLErrInfo(sslErr, errError, sslOperationReturnValue), + decodeOpenSSLError(sslErr, errError, sslOperationReturnValue), + sslErr == SSL_ERROR_SYSCALL ? errno_copy : 0) { + if (sslErr == SSL_ERROR_ZERO_RETURN) { + sslError = SSLError::EOF_ERROR; + } else if (sslErr == SSL_ERROR_SYSCALL) { + sslError = SSLError::NETWORK_ERROR; + } else { + // Conservatively assume that this is an SSL error + sslError = SSLError::SSL_ERROR; + } +} + +SSLException::SSLException(SSLError error) + : AsyncSocketException( + exTypefromSSLErr(error), + getSSLErrorString(error).str(), + 0), + sslError(error) {} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.h b/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.h new file mode 100644 index 000000000..be21c57b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/SSLErrors.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/async/AsyncSocketException.h> + +namespace folly { + +enum class SSLError { + CLIENT_RENEGOTIATION, // A client tried to renegotiate with this server + INVALID_RENEGOTIATION, // We attempted to start a renegotiation. + EARLY_WRITE, // Wrote before SSL connection established. + SSL_ERROR, // An error related to SSL + NETWORK_ERROR, // An error related to the network. + EOF_ERROR, // The peer terminated the connection correctly. +}; + +class SSLException : public folly::AsyncSocketException { + public: + SSLException( + int sslError, + unsigned long errError, + int sslOperationReturnValue, + int errno_copy); + + explicit SSLException(SSLError error); + + SSLError getSSLError() const { + return sslError; + } + + private: + SSLError sslError; +}; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h b/ios/Pods/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h new file mode 100644 index 000000000..1f478df1f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/io/Cursor.h> +#include <folly/io/IOBuf.h> +#include <map> +#include <vector> + +namespace folly { +namespace ssl { + +// http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml +enum class TLSExtension : uint16_t { + SERVER_NAME = 0, + MAX_FRAGMENT_LENGTH = 1, + CLIENT_CERTIFICATE_URL = 2, + TRUSTED_CA_KEYS = 3, + TRUNCATED_HMAC = 4, + STATUS_REQUEST = 5, + USER_MAPPING = 6, + CLIENT_AUTHZ = 7, + SERVER_AUTHZ = 8, + CERT_TYPE = 9, + SUPPORTED_GROUPS = 10, + EC_POINT_FORMATS = 11, + SRP = 12, + SIGNATURE_ALGORITHMS = 13, + USE_SRTP = 14, + HEARTBEAT = 15, + APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, + STATUS_REQUEST_V2 = 17, + SIGNED_CERTIFICATE_TIMESTAMP = 18, + CLIENT_CERTIFICATE_TYPE = 19, + SERVER_CERTIFICATE_TYPE = 20, + PADDING = 21, + ENCRYPT_THEN_MAC = 22, + EXTENDED_MASTER_SECRET = 23, + SESSION_TICKET = 35, + SUPPORTED_VERSIONS = 43, + // Facebook-specific, not IANA assigned yet + TLS_CACHED_INFO_FB = 60001, + // End Facebook-specific + RENEGOTIATION_INFO = 65281 +}; + +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 +enum class HashAlgorithm : uint8_t { + NONE = 0, + MD5 = 1, + SHA1 = 2, + SHA224 = 3, + SHA256 = 4, + SHA384 = 5, + SHA512 = 6 +}; + +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 +enum class SignatureAlgorithm : uint8_t { + ANONYMOUS = 0, + RSA = 1, + DSA = 2, + ECDSA = 3 +}; + +enum class NameType : uint8_t { + HOST_NAME = 0, +}; + +struct ClientHelloInfo { + folly::IOBufQueue clientHelloBuf_; + uint8_t clientHelloMajorVersion_; + uint8_t clientHelloMinorVersion_; + std::vector<uint16_t> clientHelloCipherSuites_; + std::vector<uint8_t> clientHelloCompressionMethods_; + std::vector<TLSExtension> clientHelloExtensions_; + std::vector<std::pair<HashAlgorithm, SignatureAlgorithm>> clientHelloSigAlgs_; + std::vector<uint16_t> clientHelloSupportedVersions_; + + // Technically, the TLS spec allows for multiple ServerNames to be sent (as + // long as each ServerName has a distinct type). In practice, the only one + // we really care about is HOST_NAME. + std::string clientHelloSNIHostname_; +}; + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json.cpp b/ios/Pods/Flipper-Folly/folly/json.cpp new file mode 100644 index 000000000..f7893e95a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json.cpp @@ -0,0 +1,1027 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/json.h> + +#include <algorithm> +#include <functional> +#include <iterator> +#include <type_traits> + +#include <boost/algorithm/string.hpp> +#include <glog/logging.h> + +#include <folly/Conv.h> +#include <folly/Portability.h> +#include <folly/Range.h> +#include <folly/String.h> +#include <folly/Unicode.h> +#include <folly/Utility.h> +#include <folly/lang/Bits.h> +#include <folly/portability/Constexpr.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +namespace json { + +namespace { + +parse_error make_parse_error( + unsigned int line, + std::string const& context, + std::string const& expected) { + return parse_error(to<std::string>( + "json parse error on line ", + line, + !context.empty() ? to<std::string>(" near `", context, '\'') : "", + ": ", + expected)); +} + +struct Printer { + explicit Printer( + std::string& out, + unsigned* indentLevel, + serialization_opts const* opts) + : out_(out), indentLevel_(indentLevel), opts_(*opts) {} + + void operator()(dynamic const& v) const { + switch (v.type()) { + case dynamic::DOUBLE: + if (!opts_.allow_nan_inf && + (std::isnan(v.asDouble()) || std::isinf(v.asDouble()))) { + throw json::parse_error( + "folly::toJson: JSON object value was a " + "NaN or INF"); + } + toAppend( + v.asDouble(), &out_, opts_.double_mode, opts_.double_num_digits); + break; + case dynamic::INT64: { + auto intval = v.asInt(); + if (opts_.javascript_safe) { + // Use folly::to to check that this integer can be represented + // as a double without loss of precision. + intval = int64_t(to<double>(intval)); + } + toAppend(intval, &out_); + break; + } + case dynamic::BOOL: + out_ += v.asBool() ? "true" : "false"; + break; + case dynamic::NULLT: + out_ += "null"; + break; + case dynamic::STRING: + escapeString(v.asString(), out_, opts_); + break; + case dynamic::OBJECT: + printObject(v); + break; + case dynamic::ARRAY: + printArray(v); + break; + default: + CHECK(0) << "Bad type " << v.type(); + } + } + + private: + void printKV(const std::pair<const dynamic, dynamic>& p) const { + if (!opts_.allow_non_string_keys && !p.first.isString()) { + throw json::parse_error( + "folly::toJson: JSON object key was not a " + "string"); + } + (*this)(p.first); + mapColon(); + (*this)(p.second); + } + + template <typename Iterator> + void printKVPairs(Iterator begin, Iterator end) const { + printKV(*begin); + for (++begin; begin != end; ++begin) { + out_ += ','; + newline(); + printKV(*begin); + } + } + + void printObject(dynamic const& o) const { + if (o.empty()) { + out_ += "{}"; + return; + } + + out_ += '{'; + indent(); + newline(); + if (opts_.sort_keys || opts_.sort_keys_by) { + using ref = std::reference_wrapper<decltype(o.items())::value_type const>; + auto sort_keys_by = [&](auto begin, auto end, const auto& comp) { + std::sort(begin, end, [&](ref a, ref b) { + // Only compare keys. No ordering among identical keys. + return comp(a.get().first, b.get().first); + }); + }; + std::vector<ref> refs(o.items().begin(), o.items().end()); + if (opts_.sort_keys_by) { + sort_keys_by(refs.begin(), refs.end(), opts_.sort_keys_by); + } else { + sort_keys_by(refs.begin(), refs.end(), std::less<>()); + } + printKVPairs(refs.cbegin(), refs.cend()); + } else { + printKVPairs(o.items().begin(), o.items().end()); + } + outdent(); + newline(); + out_ += '}'; + } + + void printArray(dynamic const& a) const { + if (a.empty()) { + out_ += "[]"; + return; + } + + out_ += '['; + indent(); + newline(); + (*this)(a[0]); + for (auto& val : range(std::next(a.begin()), a.end())) { + out_ += ','; + newline(); + (*this)(val); + } + outdent(); + newline(); + out_ += ']'; + } + + private: + void outdent() const { + if (indentLevel_) { + --*indentLevel_; + } + } + + void indent() const { + if (indentLevel_) { + ++*indentLevel_; + } + } + + void newline() const { + if (indentLevel_) { + out_ += to<std::string>('\n', std::string(*indentLevel_ * 2, ' ')); + } + } + + void mapColon() const { + out_ += indentLevel_ ? ": " : ":"; + } + + private: + std::string& out_; + unsigned* const indentLevel_; + serialization_opts const& opts_; +}; + +////////////////////////////////////////////////////////////////////// + +// Wraps our input buffer with some helper functions. +struct Input { + explicit Input(StringPiece range, json::serialization_opts const* opts) + : range_(range), opts_(*opts), lineNum_(0) { + storeCurrent(); + } + + Input(Input const&) = delete; + Input& operator=(Input const&) = delete; + + char const* begin() const { + return range_.begin(); + } + + unsigned getLineNum() const { + return lineNum_; + } + + // Parse ahead for as long as the supplied predicate is satisfied, + // returning a range of what was skipped. + template <class Predicate> + StringPiece skipWhile(const Predicate& p) { + std::size_t skipped = 0; + for (; skipped < range_.size(); ++skipped) { + if (!p(range_[skipped])) { + break; + } + if (range_[skipped] == '\n') { + ++lineNum_; + } + } + auto ret = range_.subpiece(0, skipped); + range_.advance(skipped); + storeCurrent(); + return ret; + } + + StringPiece skipDigits() { + return skipWhile([](char c) { return c >= '0' && c <= '9'; }); + } + + StringPiece skipMinusAndDigits() { + bool firstChar = true; + return skipWhile([&firstChar](char c) { + bool result = (c >= '0' && c <= '9') || (firstChar && c == '-'); + firstChar = false; + return result; + }); + } + + void skipWhitespace() { + unsigned index = 0; + while (true) { + while (index < range_.size() && range_[index] == ' ') { + index++; + } + if (index < range_.size()) { + if (range_[index] == '\n') { + index++; + ++lineNum_; + continue; + } + if (range_[index] == '\t' || range_[index] == '\r') { + index++; + continue; + } + } + break; + } + range_.advance(index); + storeCurrent(); + } + + void expect(char c) { + if (**this != c) { + throw json::make_parse_error( + lineNum_, context(), to<std::string>("expected '", c, '\'')); + } + ++*this; + } + + std::size_t size() const { + return range_.size(); + } + + int operator*() const { + return current_; + } + + void operator++() { + range_.pop_front(); + storeCurrent(); + } + + template <class T> + T extract() { + try { + return to<T>(&range_); + } catch (std::exception const& e) { + error(e.what()); + } + } + + bool consume(StringPiece str) { + if (boost::starts_with(range_, str)) { + range_.advance(str.size()); + storeCurrent(); + return true; + } + return false; + } + + std::string context() const { + return range_.subpiece(0, 16 /* arbitrary */).toString(); + } + + dynamic error(char const* what) const { + throw json::make_parse_error(lineNum_, context(), what); + } + + json::serialization_opts const& getOpts() { + return opts_; + } + + void incrementRecursionLevel() { + if (currentRecursionLevel_ > opts_.recursion_limit) { + error("recursion limit exceeded"); + } + currentRecursionLevel_++; + } + + void decrementRecursionLevel() { + currentRecursionLevel_--; + } + + private: + void storeCurrent() { + current_ = range_.empty() ? EOF : range_.front(); + } + + private: + StringPiece range_; + json::serialization_opts const& opts_; + unsigned lineNum_; + int current_; + unsigned int currentRecursionLevel_{0}; +}; + +class RecursionGuard { + public: + explicit RecursionGuard(Input& in) : in_(in) { + in_.incrementRecursionLevel(); + } + + ~RecursionGuard() { + in_.decrementRecursionLevel(); + } + + private: + Input& in_; +}; + +dynamic parseValue(Input& in, json::metadata_map* map); +std::string parseString(Input& in); +dynamic parseNumber(Input& in); + +template <class K> +void parseObjectKeyValue( + Input& in, + dynamic& ret, + K&& key, + json::metadata_map* map) { + auto keyLineNumber = in.getLineNum(); + in.skipWhitespace(); + in.expect(':'); + in.skipWhitespace(); + K tmp; + if (map) { + tmp = K(key); + } + auto valueLineNumber = in.getLineNum(); + ret.insert(std::forward<K>(key), parseValue(in, map)); + if (map) { + auto val = ret.get_ptr(tmp); + // We just inserted it, so it should be there! + DCHECK(val != nullptr); + map->emplace( + val, json::parse_metadata{{{keyLineNumber}}, {{valueLineNumber}}}); + } +} + +dynamic parseObject(Input& in, json::metadata_map* map) { + DCHECK_EQ(*in, '{'); + ++in; + + dynamic ret = dynamic::object; + + in.skipWhitespace(); + if (*in == '}') { + ++in; + return ret; + } + + for (;;) { + if (in.getOpts().allow_trailing_comma && *in == '}') { + break; + } + if (*in == '\"') { // string + auto key = parseString(in); + parseObjectKeyValue(in, ret, std::move(key), map); + } else if (!in.getOpts().allow_non_string_keys) { + in.error("expected string for object key name"); + } else { + auto key = parseValue(in, map); + parseObjectKeyValue(in, ret, std::move(key), map); + } + + in.skipWhitespace(); + if (*in != ',') { + break; + } + ++in; + in.skipWhitespace(); + } + in.expect('}'); + + return ret; +} + +dynamic parseArray(Input& in, json::metadata_map* map) { + DCHECK_EQ(*in, '['); + ++in; + + dynamic ret = dynamic::array; + + in.skipWhitespace(); + if (*in == ']') { + ++in; + return ret; + } + + std::vector<uint32_t> lineNumbers; + for (;;) { + if (in.getOpts().allow_trailing_comma && *in == ']') { + break; + } + ret.push_back(parseValue(in, map)); + if (map) { + lineNumbers.push_back(in.getLineNum()); + } + in.skipWhitespace(); + if (*in != ',') { + break; + } + ++in; + in.skipWhitespace(); + } + if (map) { + for (size_t i = 0; i < ret.size(); i++) { + map->emplace(&ret[i], json::parse_metadata{{{0}}, {{lineNumbers[i]}}}); + } + } + in.expect(']'); + + return ret; +} + +dynamic parseNumber(Input& in) { + bool const negative = (*in == '-'); + if (negative && in.consume("-Infinity")) { + if (in.getOpts().parse_numbers_as_strings) { + return "-Infinity"; + } else { + return -std::numeric_limits<double>::infinity(); + } + } + + auto integral = in.skipMinusAndDigits(); + if (negative && integral.size() < 2) { + in.error("expected digits after `-'"); + } + + auto const wasE = *in == 'e' || *in == 'E'; + + constexpr const char* maxInt = "9223372036854775807"; + constexpr const char* minInt = "-9223372036854775808"; + constexpr auto maxIntLen = constexpr_strlen(maxInt); + constexpr auto minIntLen = constexpr_strlen(minInt); + + if (*in != '.' && !wasE && in.getOpts().parse_numbers_as_strings) { + return integral; + } + + if (*in != '.' && !wasE) { + if (LIKELY(!in.getOpts().double_fallback || integral.size() < maxIntLen) || + (!negative && integral.size() == maxIntLen && integral <= maxInt) || + (negative && integral.size() == minIntLen && integral <= minInt)) { + auto val = to<int64_t>(integral); + in.skipWhitespace(); + return val; + } else { + auto val = to<double>(integral); + in.skipWhitespace(); + return val; + } + } + + auto end = !wasE ? (++in, in.skipDigits().end()) : in.begin(); + if (*in == 'e' || *in == 'E') { + ++in; + if (*in == '+' || *in == '-') { + ++in; + } + auto expPart = in.skipDigits(); + end = expPart.end(); + } + auto fullNum = range(integral.begin(), end); + if (in.getOpts().parse_numbers_as_strings) { + return fullNum; + } + auto val = to<double>(fullNum); + return val; +} + +std::string decodeUnicodeEscape(Input& in) { + auto hexVal = [&](int c) -> uint16_t { + // clang-format off + return uint16_t( + c >= '0' && c <= '9' ? c - '0' : + c >= 'a' && c <= 'f' ? c - 'a' + 10 : + c >= 'A' && c <= 'F' ? c - 'A' + 10 : + (in.error("invalid hex digit"), 0)); + // clang-format on + }; + + auto readHex = [&]() -> uint16_t { + if (in.size() < 4) { + in.error("expected 4 hex digits"); + } + + auto ret = uint16_t(hexVal(*in) * 4096); + ++in; + ret += hexVal(*in) * 256; + ++in; + ret += hexVal(*in) * 16; + ++in; + ret += hexVal(*in); + ++in; + return ret; + }; + + /* + * If the value encoded is in the surrogate pair range, we need to + * make sure there is another escape that we can use also. + */ + uint32_t codePoint = readHex(); + if (codePoint >= 0xd800 && codePoint <= 0xdbff) { + if (!in.consume("\\u")) { + in.error( + "expected another unicode escape for second half of " + "surrogate pair"); + } + uint16_t second = readHex(); + if (second >= 0xdc00 && second <= 0xdfff) { + codePoint = 0x10000 + ((codePoint & 0x3ff) << 10) + (second & 0x3ff); + } else { + in.error("second character in surrogate pair is invalid"); + } + } else if (codePoint >= 0xdc00 && codePoint <= 0xdfff) { + in.error("invalid unicode code point (in range [0xdc00,0xdfff])"); + } + + return codePointToUtf8(codePoint); +} + +std::string parseString(Input& in) { + DCHECK_EQ(*in, '\"'); + ++in; + + std::string ret; + for (;;) { + auto range = in.skipWhile([](char c) { return c != '\"' && c != '\\'; }); + ret.append(range.begin(), range.end()); + + if (*in == '\"') { + ++in; + break; + } + if (*in == '\\') { + ++in; + switch (*in) { + // clang-format off + case '\"': ret.push_back('\"'); ++in; break; + case '\\': ret.push_back('\\'); ++in; break; + case '/': ret.push_back('/'); ++in; break; + case 'b': ret.push_back('\b'); ++in; break; + case 'f': ret.push_back('\f'); ++in; break; + case 'n': ret.push_back('\n'); ++in; break; + case 'r': ret.push_back('\r'); ++in; break; + case 't': ret.push_back('\t'); ++in; break; + case 'u': ++in; ret += decodeUnicodeEscape(in); break; + // clang-format on + default: + in.error( + to<std::string>("unknown escape ", *in, " in string").c_str()); + } + continue; + } + if (*in == EOF) { + in.error("unterminated string"); + } + if (!*in) { + /* + * Apparently we're actually supposed to ban all control + * characters from strings. This seems unnecessarily + * restrictive, so we're only banning zero bytes. (Since the + * string is presumed to be UTF-8 encoded it's fine to just + * check this way.) + */ + in.error("null byte in string"); + } + + ret.push_back(char(*in)); + ++in; + } + + return ret; +} + +dynamic parseValue(Input& in, json::metadata_map* map) { + RecursionGuard guard(in); + + in.skipWhitespace(); + // clang-format off + return + *in == '[' ? parseArray(in, map) : + *in == '{' ? parseObject(in, map) : + *in == '\"' ? parseString(in) : + (*in == '-' || (*in >= '0' && *in <= '9')) ? parseNumber(in) : + in.consume("true") ? true : + in.consume("false") ? false : + in.consume("null") ? nullptr : + in.consume("Infinity") ? + (in.getOpts().parse_numbers_as_strings ? (dynamic)"Infinity" : + (dynamic)std::numeric_limits<double>::infinity()) : + in.consume("NaN") ? + (in.getOpts().parse_numbers_as_strings ? (dynamic)"NaN" : + (dynamic)std::numeric_limits<double>::quiet_NaN()) : + in.error("expected json value"); + // clang-format on +} + +} // namespace + +////////////////////////////////////////////////////////////////////// + +std::array<uint64_t, 2> buildExtraAsciiToEscapeBitmap(StringPiece chars) { + std::array<uint64_t, 2> escapes{{0, 0}}; + for (auto b : ByteRange(chars)) { + if (b >= 0x20 && b < 0x80) { + escapes[b / 64] |= uint64_t(1) << (b % 64); + } + } + return escapes; +} + +std::string serialize(dynamic const& dyn, serialization_opts const& opts) { + std::string ret; + unsigned indentLevel = 0; + Printer p(ret, opts.pretty_formatting ? &indentLevel : nullptr, &opts); + p(dyn); + return ret; +} + +// Fast path to determine the longest prefix that can be left +// unescaped in a string of sizeof(T) bytes packed in an integer of +// type T. +template <bool EnableExtraAsciiEscapes, class T> +size_t firstEscapableInWord(T s, const serialization_opts& opts) { + static_assert(std::is_unsigned<T>::value, "Unsigned integer required"); + static constexpr T kOnes = ~T() / 255; // 0x...0101 + static constexpr T kMsbs = kOnes * 0x80; // 0x...8080 + + // Sets the MSB of bytes < b. Precondition: b < 128. + auto isLess = [](T w, uint8_t b) { + // A byte is < b iff subtracting b underflows, so we check that + // the MSB wasn't set before and it's set after the subtraction. + return (w - kOnes * b) & ~w & kMsbs; + }; + + auto isChar = [&](uint8_t c) { + // A byte is == c iff it is 0 if xored with c. + return isLess(s ^ (kOnes * c), 1); + }; + + // The following masks have the MSB set for each byte of the word + // that satisfies the corresponding condition. + auto isHigh = s & kMsbs; // >= 128 + auto isLow = isLess(s, 0x20); // <= 0x1f + auto needsEscape = isHigh | isLow | isChar('\\') | isChar('"'); + + if /* constexpr */ (EnableExtraAsciiEscapes) { + // Deal with optional bitmap for unicode escapes. Escapes can optionally be + // set for ascii characters 32 - 127, so the inner loop may run up to 96 + // times. However, for the case where 0 or a handful of bits are set, + // looping will be minimal through use of findFirstSet. + for (size_t i = 0; i < opts.extra_ascii_to_escape_bitmap.size(); ++i) { + const auto offset = i * 64; + // Clear first 32 characters if this is the first index, since those are + // always escaped. + auto bitmap = opts.extra_ascii_to_escape_bitmap[i] & + (i == 0 ? uint64_t(-1) << 32 : ~0UL); + while (bitmap) { + auto bit = folly::findFirstSet(bitmap); + needsEscape |= isChar(static_cast<uint8_t>(offset + bit - 1)); + bitmap &= bitmap - 1; + } + } + } + + if (!needsEscape) { + return sizeof(T); + } + + if (folly::kIsLittleEndian) { + return folly::findFirstSet(needsEscape) / 8 - 1; + } else { + return sizeof(T) - folly::findLastSet(needsEscape) / 8; + } +} + +// Escape a string so that it is legal to print it in JSON text. +template <bool EnableExtraAsciiEscapes> +void escapeStringImpl( + StringPiece input, + std::string& out, + const serialization_opts& opts) { + auto hexDigit = [](uint8_t c) -> char { + return c < 10 ? c + '0' : c - 10 + 'a'; + }; + + out.push_back('\"'); + + auto* p = reinterpret_cast<const unsigned char*>(input.begin()); + auto* q = reinterpret_cast<const unsigned char*>(input.begin()); + auto* e = reinterpret_cast<const unsigned char*>(input.end()); + + while (p < e) { + // Find the longest prefix that does not need escaping, and copy + // it literally into the output string. + auto firstEsc = p; + while (firstEsc < e) { + auto avail = to_unsigned(e - firstEsc); + uint64_t word = 0; + if (avail >= 8) { + word = folly::loadUnaligned<uint64_t>(firstEsc); + } else { + word = folly::partialLoadUnaligned<uint64_t>(firstEsc, avail); + } + auto prefix = firstEscapableInWord<EnableExtraAsciiEscapes>(word, opts); + DCHECK_LE(prefix, avail); + firstEsc += prefix; + if (prefix < 8) { + break; + } + } + if (firstEsc > p) { + out.append(reinterpret_cast<const char*>(p), firstEsc - p); + p = firstEsc; + // We can't be in the middle of a multibyte sequence, so we can reset q. + q = p; + if (p == e) { + break; + } + } + + // Handle the next byte that may need escaping. + + // Since non-ascii encoding inherently does utf8 validation + // we explicitly validate utf8 only if non-ascii encoding is disabled. + if ((opts.validate_utf8 || opts.skip_invalid_utf8) && + !opts.encode_non_ascii) { + // To achieve better spatial and temporal coherence + // we do utf8 validation progressively along with the + // string-escaping instead of two separate passes. + + // As the encoding progresses, q will stay at or ahead of p. + CHECK_GE(q, p); + + // As p catches up with q, move q forward. + if (q == p) { + // calling utf8_decode has the side effect of + // checking that utf8 encodings are valid + char32_t v = utf8ToCodePoint(q, e, opts.skip_invalid_utf8); + if (opts.skip_invalid_utf8 && v == U'\ufffd') { + out.append(reinterpret_cast<const char*>(u8"\ufffd")); + p = q; + continue; + } + } + } + + auto encodeUnicode = opts.encode_non_ascii && (*p & 0x80); + if /* constexpr */ (EnableExtraAsciiEscapes) { + encodeUnicode = encodeUnicode || + (*p >= 0x20 && *p < 0x80 && + (opts.extra_ascii_to_escape_bitmap[*p / 64] & + (uint64_t(1) << (*p % 64)))); + } + + if (encodeUnicode) { + // note that this if condition captures utf8 chars + // with value > 127, so size > 1 byte (or they are whitelisted for + // Unicode encoding). + // NOTE: char32_t / char16_t are both unsigned. + char32_t cp = utf8ToCodePoint(p, e, opts.skip_invalid_utf8); + auto writeHex = [&](char16_t v) { + char buf[] = "\\u\0\0\0\0"; + buf[2] = hexDigit((v >> 12) & 0x0f); + buf[3] = hexDigit((v >> 8) & 0x0f); + buf[4] = hexDigit((v >> 4) & 0x0f); + buf[5] = hexDigit(v & 0x0f); + out.append(buf, 6); + }; + // From the ECMA-404 The JSON Data Interchange Syntax 2nd Edition Dec 2017 + if (cp < 0x10000u) { + // If the code point is in the Basic Multilingual Plane (U+0000 through + // U+FFFF), then it may be represented as a six-character sequence: + // a reverse solidus, followed by the lowercase letter u, followed by + // four hexadecimal digits that encode the code point. + writeHex(static_cast<char16_t>(cp)); + } else { + // To escape a code point that is not in the Basic Multilingual Plane, + // the character may be represented as a twelve-character sequence, + // encoding the UTF-16 surrogate pair corresponding to the code point. + writeHex(static_cast<char16_t>( + 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu))); + writeHex(static_cast<char16_t>(0xdc00u + ((cp - 0x10000u) & 0x3ffu))); + } + } else if (*p == '\\' || *p == '\"') { + char buf[] = "\\\0"; + buf[1] = char(*p++); + out.append(buf, 2); + } else if (*p <= 0x1f) { + switch (*p) { + // clang-format off + case '\b': out.append("\\b"); p++; break; + case '\f': out.append("\\f"); p++; break; + case '\n': out.append("\\n"); p++; break; + case '\r': out.append("\\r"); p++; break; + case '\t': out.append("\\t"); p++; break; + // clang-format on + default: + // Note that this if condition captures non readable chars + // with value < 32, so size = 1 byte (e.g control chars). + char buf[] = "\\u00\0\0"; + buf[4] = hexDigit(uint8_t((*p & 0xf0) >> 4)); + buf[5] = hexDigit(uint8_t(*p & 0xf)); + out.append(buf, 6); + p++; + } + } else { + out.push_back(char(*p++)); + } + } + + out.push_back('\"'); +} + +void escapeString( + StringPiece input, + std::string& out, + const serialization_opts& opts) { + if (FOLLY_UNLIKELY( + opts.extra_ascii_to_escape_bitmap[0] || + opts.extra_ascii_to_escape_bitmap[1])) { + escapeStringImpl<true>(input, out, opts); + } else { + escapeStringImpl<false>(input, out, opts); + } +} + +std::string stripComments(StringPiece jsonC) { + std::string result; + enum class State { + None, + InString, + InlineComment, + LineComment + } state = State::None; + + for (size_t i = 0; i < jsonC.size(); ++i) { + auto s = jsonC.subpiece(i); + switch (state) { + case State::None: + if (s.startsWith("/*")) { + state = State::InlineComment; + ++i; + continue; + } else if (s.startsWith("//")) { + state = State::LineComment; + ++i; + continue; + } else if (s[0] == '\"') { + state = State::InString; + } + result.push_back(s[0]); + break; + case State::InString: + if (s[0] == '\\') { + if (UNLIKELY(s.size() == 1)) { + throw std::logic_error("Invalid JSONC: string is not terminated"); + } + result.push_back(s[0]); + result.push_back(s[1]); + ++i; + continue; + } else if (s[0] == '\"') { + state = State::None; + } + result.push_back(s[0]); + break; + case State::InlineComment: + if (s.startsWith("*/")) { + state = State::None; + ++i; + } + break; + case State::LineComment: + if (s[0] == '\n') { + // skip the line break. It doesn't matter. + state = State::None; + } + break; + default: + throw std::logic_error("Unknown comment state"); + } + } + return result; +} + +} // namespace json + +////////////////////////////////////////////////////////////////////// + +dynamic parseJsonWithMetadata(StringPiece range, json::metadata_map* map) { + return parseJsonWithMetadata(range, json::serialization_opts(), map); +} + +dynamic parseJsonWithMetadata( + StringPiece range, + json::serialization_opts const& opts, + json::metadata_map* map) { + json::Input in(range, &opts); + + uint32_t n = in.getLineNum(); + auto ret = parseValue(in, map); + if (map) { + map->emplace(&ret, json::parse_metadata{{{0}}, {{n}}}); + } + + in.skipWhitespace(); + if (in.size() && *in != '\0') { + in.error("parsing didn't consume all input"); + } + return ret; +} + +dynamic parseJson(StringPiece range) { + return parseJson(range, json::serialization_opts()); +} + +dynamic parseJson(StringPiece range, json::serialization_opts const& opts) { + json::Input in(range, &opts); + + auto ret = parseValue(in, nullptr); + in.skipWhitespace(); + if (in.size() && *in != '\0') { + in.error("parsing didn't consume all input"); + } + return ret; +} + +std::string toJson(dynamic const& dyn) { + return json::serialize(dyn, json::serialization_opts()); +} + +std::string toPrettyJson(dynamic const& dyn) { + json::serialization_opts opts; + opts.pretty_formatting = true; + opts.sort_keys = true; + return json::serialize(dyn, opts); +} + +////////////////////////////////////////////////////////////////////// +// dynamic::print_as_pseudo_json() is implemented here for header +// ordering reasons (most of the dynamic implementation is in +// dynamic-inl.h, which we don't want to include json.h). + +void dynamic::print_as_pseudo_json(std::ostream& out) const { + json::serialization_opts opts; + opts.allow_non_string_keys = true; + opts.allow_nan_inf = true; + out << json::serialize(*this, opts); +} + +void PrintTo(const dynamic& dyn, std::ostream* os) { + json::serialization_opts opts; + opts.allow_nan_inf = true; + opts.allow_non_string_keys = true; + opts.pretty_formatting = true; + opts.sort_keys = true; + *os << json::serialize(dyn, opts); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json.h b/ios/Pods/Flipper-Folly/folly/json.h new file mode 100644 index 000000000..264552cea --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Serialize and deserialize folly::dynamic values as JSON. + * + * Before you use this you should probably understand the basic + * concepts in the JSON type system: + * + * Value : String | Bool | Null | Object | Array | Number + * String : UTF-8 sequence + * Object : (String, Value) pairs, with unique String keys + * Array : ordered list of Values + * Null : null + * Bool : true | false + * Number : (representation unspecified) + * + * ... That's about it. For more information see http://json.org or + * look up RFC 4627. + * + * If your dynamic has anything illegal with regard to this type + * system, the serializer will throw. + * + * @author Jordan DeLong <delong.j@fb.com> + */ + +#pragma once + +#include <iosfwd> +#include <string> + +#include <folly/Function.h> +#include <folly/Range.h> +#include <folly/dynamic.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +namespace json { + +struct serialization_opts { + explicit serialization_opts() + : allow_non_string_keys(false), + javascript_safe(false), + pretty_formatting(false), + encode_non_ascii(false), + validate_utf8(false), + allow_trailing_comma(false), + sort_keys(false), + skip_invalid_utf8(false), + allow_nan_inf(false), + double_mode(double_conversion::DoubleToStringConverter::SHORTEST), + double_num_digits(0), // ignored when mode is SHORTEST + double_fallback(false), + parse_numbers_as_strings(false), + recursion_limit(100), + extra_ascii_to_escape_bitmap{{0, 0}} {} + + // If true, keys in an object can be non-strings. (In strict + // JSON, object keys must be strings.) This is used by dynamic's + // operator<<. + bool allow_non_string_keys; + + /* + * If true, refuse to serialize 64-bit numbers that cannot be + * precisely represented by fit a double---instead, throws an + * exception if the document contains this. + */ + bool javascript_safe; + + // If true, the serialized json will contain space and newlines to + // try to be minimally "pretty". + bool pretty_formatting; + + // If true, non-ASCII utf8 characters would be encoded as \uXXXX: + // - if the code point is in [U+0000..U+FFFF] => encode as a single \uXXXX + // - if the code point is > U+FFFF => encode as 2 UTF-16 surrogate pairs. + bool encode_non_ascii; + + // Check that strings are valid utf8 + bool validate_utf8; + + // Allow trailing comma in lists of values / items + bool allow_trailing_comma; + + // Sort keys of all objects before printing out (potentially slow) + // using dynamic::operator<. + // Has no effect if sort_keys_by is set. + bool sort_keys; + + // Sort keys of all objects before printing out (potentially slow) + // using the provided less functor. + Function<bool(dynamic const&, dynamic const&) const> sort_keys_by; + + // Replace invalid utf8 characters with U+FFFD and continue + bool skip_invalid_utf8; + + // true to allow NaN or INF values + bool allow_nan_inf; + + // Options for how to print floating point values. See Conv.h + // toAppend implementation for floating point for more info + double_conversion::DoubleToStringConverter::DtoaMode double_mode; + unsigned int double_num_digits; + + // Fallback to double when a value that looks like integer is too big to + // fit in an int64_t. Can result in loss a of precision. + bool double_fallback; + + // Do not parse numbers. Instead, store them as strings and leave the + // conversion up to the user. + bool parse_numbers_as_strings; + + // Recursion limit when parsing. + unsigned int recursion_limit; + + // Bitmap representing ASCII characters to escape with unicode + // representations. The least significant bit of the first in the pair is + // ASCII value 0; the most significant bit of the second in the pair is ASCII + // value 127. Some specific characters in this range are always escaped + // regardless of the bitmask - namely characters less than 0x20, \, and ". + std::array<uint64_t, 2> extra_ascii_to_escape_bitmap; +}; + +/* + * Generates a bitmap with bits set for each of the ASCII characters provided + * for use in the serialization_opts extra_ascii_to_escape_bitmap option. If any + * characters are not valid ASCII, they are ignored. + */ +std::array<uint64_t, 2> buildExtraAsciiToEscapeBitmap(StringPiece chars); + +/* + * Main JSON serialization routine taking folly::dynamic parameters. + * For the most common use cases there are simpler functions in the + * main folly namespace below. + */ +std::string serialize(dynamic const&, serialization_opts const&); + +/* + * Escape a string so that it is legal to print it in JSON text and + * append the result to out. + */ + +void escapeString( + StringPiece input, + std::string& out, + const serialization_opts& opts); + +/* + * Strip all C99-like comments (i.e. // and / * ... * /) + */ +std::string stripComments(StringPiece jsonC); + +class FOLLY_EXPORT parse_error : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +// may be extened in future to include offset, col, etc. +struct parse_location { + uint32_t line{}; // 0-indexed +}; + +// may be extended in future to include end location +struct parse_range { + parse_location begin; +}; + +struct parse_metadata { + parse_range key_range; + parse_range value_range; +}; + +using metadata_map = std::unordered_map<dynamic const*, parse_metadata>; + +} // namespace json + +////////////////////////////////////////////////////////////////////// + +/* + * Parse a json blob out of a range and produce a dynamic representing + * it. + */ +dynamic parseJson(StringPiece, json::serialization_opts const&); +dynamic parseJson(StringPiece); + +dynamic parseJsonWithMetadata(StringPiece range, json::metadata_map* map); +dynamic parseJsonWithMetadata( + StringPiece range, + json::serialization_opts const& opts, + json::metadata_map* map); + +/* + * Serialize a dynamic into a json string. + */ +std::string toJson(dynamic const&); + +/* + * Same as the above, except format the json with some minimal + * indentation. + */ +std::string toPrettyJson(dynamic const&); + +/* + * Printer for GTest. + * Uppercase name to fill GTest's API, which calls this method through ADL. + */ +void PrintTo(const dynamic&, std::ostream*); +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json_patch.cpp b/ios/Pods/Flipper-Folly/folly/json_patch.cpp new file mode 100644 index 000000000..11d791eee --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json_patch.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/json_patch.h> + +#include <glog/logging.h> + +#include <folly/container/Enumerate.h> + +namespace { +using folly::StringPiece; +// JSON patch operation names +constexpr StringPiece kOperationTest = "test"; +constexpr StringPiece kOperationRemove = "remove"; +constexpr StringPiece kOperationAdd = "add"; +constexpr StringPiece kOperationReplace = "replace"; +constexpr StringPiece kOperationMove = "move"; +constexpr StringPiece kOperationCopy = "copy"; +// field tags in JSON patch +constexpr StringPiece kOpTag = "op"; +constexpr StringPiece kValueTag = "value"; +constexpr StringPiece kPathTag = "path"; +constexpr StringPiece kFromTag = "from"; +} // namespace + +namespace folly { + +// static +Expected<json_patch, json_patch::parse_error> json_patch::try_parse( + dynamic const& obj) noexcept { + using err_code = parse_error_code; + + json_patch patch; + if (!obj.isArray()) { + return makeUnexpected(parse_error{err_code::invalid_shape, &obj}); + } + for (auto const& elem : obj) { + if (!elem.isObject()) { + return makeUnexpected(parse_error{err_code::invalid_shape, &elem}); + } + auto const* op_ptr = elem.get_ptr(kOpTag); + if (!op_ptr) { + return makeUnexpected(parse_error{err_code::missing_op, &elem}); + } + if (!op_ptr->isString()) { + return makeUnexpected(parse_error{err_code::malformed_op, &elem}); + } + auto const op_str = op_ptr->asString(); + patch_operation op; + + // extract 'from' attribute + { + auto const* from_ptr = elem.get_ptr(kFromTag); + if (from_ptr) { + if (!from_ptr->isString()) { + return makeUnexpected(parse_error{err_code::invalid_shape, &elem}); + } + auto json_ptr = json_pointer::try_parse(from_ptr->asString()); + if (!json_ptr.hasValue()) { + return makeUnexpected( + parse_error{err_code::malformed_from_attr, &elem}); + } + op.from = json_ptr.value(); + } + } + + // extract 'path' attribute + { + auto const* path_ptr = elem.get_ptr(kPathTag); + if (!path_ptr) { + return makeUnexpected(parse_error{err_code::missing_path_attr, &elem}); + } + if (!path_ptr->isString()) { + return makeUnexpected( + parse_error{err_code::malformed_path_attr, &elem}); + } + auto const json_ptr = json_pointer::try_parse(path_ptr->asString()); + if (!json_ptr.hasValue()) { + return makeUnexpected( + parse_error{err_code::malformed_path_attr, &elem}); + } + op.path = json_ptr.value(); + } + + // extract 'value' attribute + { + auto const* val_ptr = elem.get_ptr(kValueTag); + if (val_ptr) { + op.value = *val_ptr; + } + } + + // check mandatory attributes - different per operation + // NOTE: per RFC, the surplus attributes (e.g. 'from' with 'add') + // should be simply ignored + + using op_code = patch_operation_code; + + if (op_str == kOperationTest) { + if (!op.value) { + return makeUnexpected(parse_error{err_code::missing_value_attr, &elem}); + } + op.op_code = op_code::test; + } else if (op_str == kOperationRemove) { + op.op_code = op_code::remove; + } else if (op_str == kOperationAdd) { + if (!op.value) { + return makeUnexpected(parse_error{err_code::missing_value_attr, &elem}); + } + op.op_code = op_code::add; + } else if (op_str == kOperationReplace) { + if (!op.value) { + return makeUnexpected(parse_error{err_code::missing_value_attr, &elem}); + } + op.op_code = op_code::replace; + } else if (op_str == kOperationMove) { + if (!op.from) { + return makeUnexpected(parse_error{err_code::missing_from_attr, &elem}); + } + // is from a proper prefix to path? + if (op.from->is_prefix_of(op.path)) { + return makeUnexpected( + parse_error{err_code::overlapping_pointers, &elem}); + } + op.op_code = op_code::move; + } else if (op_str == kOperationCopy) { + if (!op.from) { + return makeUnexpected(parse_error{err_code::missing_from_attr, &elem}); + } + op.op_code = op_code::copy; + } + + if (op.op_code != op_code::invalid) { + patch.ops_.emplace_back(std::move(op)); + } else { + return makeUnexpected(parse_error{err_code::unknown_op, &elem}); + } + } + return patch; +} + +std::vector<json_patch::patch_operation> const& json_patch::ops() const { + return ops_; +} + +namespace { +// clang-format off +Expected<Unit, json_patch::patch_application_error_code> +// clang-format on +do_remove(dynamic::resolved_json_pointer<dynamic>& ptr) { + using error_code = json_patch::patch_application_error_code; + + if (!ptr.hasValue()) { + return folly::makeUnexpected(error_code::path_not_found); + } + + auto parent = ptr->parent; + + if (!parent) { + return folly::makeUnexpected(error_code::other); + } + + if (parent->isObject()) { + parent->erase(ptr->parent_key); + return unit; + } + + if (parent->isArray()) { + parent->erase(parent->begin() + ptr->parent_index); + return unit; + } + + return folly::makeUnexpected(error_code::other); +} + +// clang-format off +Expected<Unit, json_patch::patch_application_error_code> +// clang-format on +do_add( + dynamic::resolved_json_pointer<dynamic>& ptr, + const dynamic& value, + const std::string& last_token) { + using app_err_code = json_patch::patch_application_error_code; + using res_err_code = dynamic::json_pointer_resolution_error_code; + + // element found: see if parent is object or array + if (ptr.hasValue()) { + // root element, or key in object - replace (per RFC) + if (ptr->parent == nullptr || ptr->parent->isObject()) { + *ptr->value = value; + } + // valid index in array: insert at index and shift right + if (ptr->parent && ptr->parent->isArray()) { + ptr->parent->insert(ptr->parent->begin() + ptr->parent_index, value); + } + } else { + // see if we can add value, based on pointer resolution state + switch (ptr.error().error_code) { + // key not found. can only happen in object - add new key-value + case res_err_code::key_not_found: { + DCHECK(ptr.error().context->isObject()); + ptr.error().context->insert(last_token, value); + break; + } + // special '-' index in array - do append operation + case res_err_code::append_requested: { + DCHECK(ptr.error().context->isArray()); + ptr.error().context->push_back(value); + break; + } + case res_err_code::other: + case res_err_code::index_out_of_bounds: + case res_err_code::index_not_numeric: + case res_err_code::index_has_leading_zero: + case res_err_code::element_not_object_or_array: + case res_err_code::json_pointer_out_of_bounds: + default: + return folly::makeUnexpected(app_err_code::other); + } + } + return unit; +} +} // namespace + +// clang-format off +Expected<Unit, json_patch::patch_application_error> +// clang-format on +json_patch::apply(dynamic& obj) { + using op_code = patch_operation_code; + using error_code = patch_application_error_code; + using error = patch_application_error; + + for (auto it : enumerate(ops_)) { + auto const index = it.index; + auto const& op = *it; + auto resolved_path = obj.try_get_ptr(op.path); + + switch (op.op_code) { + case op_code::test: + if (!resolved_path.hasValue()) { + return folly::makeUnexpected( + error{error_code::path_not_found, index}); + } + if (*resolved_path->value != *op.value) { + return folly::makeUnexpected(error{error_code::test_failed, index}); + } + break; + case op_code::remove: { + auto ret = do_remove(resolved_path); + if (ret.hasError()) { + return makeUnexpected(error{ret.error(), index}); + } + break; + } + case op_code::add: { + DCHECK(op.value.has_value()); + auto ret = do_add(resolved_path, *op.value, op.path.tokens().back()); + if (ret.hasError()) { + return makeUnexpected(error{ret.error(), index}); + } + break; + } + case op_code::replace: { + if (resolved_path.hasValue()) { + *resolved_path->value = *op.value; + } else { + return folly::makeUnexpected( + error{error_code::path_not_found, index}); + } + break; + } + case op_code::move: { + DCHECK(op.from.has_value()); + auto resolved_from = obj.try_get_ptr(*op.from); + if (!resolved_from.hasValue()) { + return makeUnexpected(error{error_code::from_not_found, index}); + } + { + auto ret = do_add( + resolved_path, *resolved_from->value, op.path.tokens().back()); + if (ret.hasError()) { + return makeUnexpected(error{ret.error(), index}); + } + } + { + auto ret = do_remove(resolved_from); + if (ret.hasError()) { + return makeUnexpected(error{ret.error(), index}); + } + } + break; + } + case op_code::copy: { + DCHECK(op.from.has_value()); + auto const resolved_from = obj.try_get_ptr(*op.from); + if (!resolved_from.hasValue()) { + return makeUnexpected(error{error_code::from_not_found, index}); + } + { + DCHECK(!op.path.tokens().empty()); + auto ret = do_add( + resolved_path, *resolved_from->value, op.path.tokens().back()); + if (ret.hasError()) { + return makeUnexpected(error{ret.error(), index}); + } + } + break; + } + case op_code::invalid: { + DCHECK(false); + return makeUnexpected(error{error_code::other, index}); + } + } + } + return unit; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json_patch.h b/ios/Pods/Flipper-Folly/folly/json_patch.h new file mode 100644 index 000000000..655423809 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json_patch.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Expected.h> +#include <folly/Optional.h> +#include <folly/dynamic.h> +#include <folly/json_pointer.h> + +namespace folly { + +/* + * json_patch + * + * As described in RFC 6902 "JSON Patch". + * + * Implements parsing. Application over data structures must be + * implemented separately. + */ +class json_patch { + public: + enum class parse_error_code : uint8_t { + undefined, + invalid_shape, + missing_op, + unknown_op, + malformed_op, + missing_path_attr, + malformed_path_attr, + missing_from_attr, + malformed_from_attr, + missing_value_attr, + overlapping_pointers, + }; + + /* + * If parsing JSON patch object fails we return err code along with + * pointer to part of JSON document that we could not parse + */ + struct parse_error { + // one of the above error codes + parse_error_code error_code{parse_error_code::undefined}; + // pointer to object that caused the error + dynamic const* obj{}; + }; + + enum class patch_operation_code : uint8_t { + invalid = 0, + test, + remove, + add, + replace, + move, + copy, + }; + + /* + * Single JSON patch operation. Argument may vary based on op type + */ + struct patch_operation { + patch_operation_code op_code{patch_operation_code::invalid}; + json_pointer path; + Optional<json_pointer> from; + Optional<dynamic> value; + friend bool operator==( + patch_operation const& lhs, + patch_operation const& rhs) { + return lhs.op_code == rhs.op_code && lhs.path == rhs.path && + lhs.from == rhs.from && lhs.value == rhs.value; + } + friend bool operator!=( + patch_operation const& lhs, + patch_operation const& rhs) { + return !(lhs == rhs); + } + }; + + json_patch() = default; + ~json_patch() = default; + + static folly::Expected<json_patch, parse_error> try_parse( + dynamic const& obj) noexcept; + + std::vector<patch_operation> const& ops() const; + + enum class patch_application_error_code : uint8_t { + other, + // "from" pointer did not resolve + from_not_found, + // "path" pointer did not resolve + path_not_found, + // "test" condition failed + test_failed, + }; + + struct patch_application_error { + patch_application_error_code error_code{}; + // index of the patch element (in array) that caused error + size_t index{}; + }; + + /* + * Mutate supplied object in accordance with patch operations. Leaves + * object in partially modified state if one of the operations fails. + */ + Expected<Unit, patch_application_error> apply(folly::dynamic& obj); + + private: + std::vector<patch_operation> ops_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json_pointer.cpp b/ios/Pods/Flipper-Folly/folly/json_pointer.cpp new file mode 100644 index 000000000..312ab48fb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json_pointer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/json_pointer.h> + +#include <folly/String.h> + +namespace folly { + +// static, public +Expected<json_pointer, json_pointer::parse_error> json_pointer::try_parse( + StringPiece const str) { + // pointer describes complete document + if (str.empty()) { + return json_pointer{}; + } + + if (str.at(0) != '/') { + return makeUnexpected(parse_error::invalid_first_character); + } + + std::vector<std::string> tokens; + splitTo<std::string>("/", str, std::inserter(tokens, tokens.begin())); + tokens.erase(tokens.begin()); + + for (auto& token : tokens) { + if (!unescape(token)) { + return makeUnexpected(parse_error::invalid_escape_sequence); + } + } + + return json_pointer(std::move(tokens)); +} + +// static, public +json_pointer json_pointer::parse(StringPiece const str) { + auto res = try_parse(str); + if (res.hasValue()) { + return std::move(res.value()); + } + switch (res.error()) { + case parse_error::invalid_first_character: + throw json_pointer::parse_exception( + "non-empty JSON pointer string does not start with '/'"); + case parse_error::invalid_escape_sequence: + throw json_pointer::parse_exception( + "Invalid escape sequence in JSON pointer string"); + default: + assume_unreachable(); + } +} + +bool json_pointer::is_prefix_of(json_pointer const& other) const noexcept { + auto const& other_tokens = other.tokens(); + if (tokens_.size() > other_tokens.size()) { + return false; + } + auto const other_begin = other_tokens.cbegin(); + auto const other_end = other_tokens.cbegin() + tokens_.size(); + return std::equal(tokens_.cbegin(), tokens_.cend(), other_begin, other_end); +} + +std::vector<std::string> const& json_pointer::tokens() const { + return tokens_; +} + +// private +json_pointer::json_pointer(std::vector<std::string> tokens) noexcept + : tokens_{std::move(tokens)} {} + +// private, static +bool json_pointer::unescape(std::string& str) { + char const* end = &str[str.size()]; + char* out = &str.front(); + char const* decode = out; + while (decode < end) { + if (*decode != '~') { + *out++ = *decode++; + continue; + } + if (decode + 1 == end) { + return false; + } + switch (decode[1]) { + case '1': + *out++ = '/'; + break; + case '0': + *out++ = '~'; + break; + default: + return false; + } + decode += 2; + } + str.resize(out - &str.front()); + return true; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/json_pointer.h b/ios/Pods/Flipper-Folly/folly/json_pointer.h new file mode 100644 index 000000000..ad7061360 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/json_pointer.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <string> +#include <vector> + +#include <folly/Expected.h> +#include <folly/Range.h> + +namespace folly { + +/* + * json_pointer + * + * As described in RFC 6901 "JSON Pointer". + * + * Implements parsing. Traversal using the pointer over data structures must be + * implemented separately. + */ +class json_pointer { + public: + enum class parse_error { + invalid_first_character, + invalid_escape_sequence, + }; + + class parse_exception : public std::runtime_error { + using std::runtime_error::runtime_error; + }; + + json_pointer() = default; + ~json_pointer() = default; + + /* + * Parse string into vector of unescaped tokens. + * Non-throwing and throwing versions. + */ + static Expected<json_pointer, parse_error> try_parse(StringPiece const str); + + static json_pointer parse(StringPiece const str); + + /* + * Return true if this pointer is proper to prefix to another pointer + */ + bool is_prefix_of(json_pointer const& other) const noexcept; + + /* + * Get access to the parsed tokens for applications that want to traverse + * the pointer. + */ + std::vector<std::string> const& tokens() const; + + friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) { + return lhs.tokens_ == rhs.tokens_; + } + + friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) { + return lhs.tokens_ != rhs.tokens_; + } + + private: + explicit json_pointer(std::vector<std::string>) noexcept; + + /* + * Unescape the specified escape sequences, returns false if incorrect + */ + static bool unescape(std::string&); + + std::vector<std::string> tokens_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Align.h b/ios/Pods/Flipper-Folly/folly/lang/Align.h new file mode 100644 index 000000000..f7cee3cef --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Align.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <cstdint> + +#include <folly/Portability.h> + +namespace folly { + +// has_extended_alignment +// +// True if it may be presumed that the platform has static extended alignment; +// false if it may not be so presumed, even when the platform might actually +// have it. Static extended alignment refers to extended alignment of objects +// with automatic, static, or thread storage. Whether the there is support for +// dynamic extended alignment is a property of the allocator which is used for +// each given dynamic allocation. +// +// Currently, very heuristical - only non-mobile 64-bit linux gets the extended +// alignment treatment. Theoretically, this could be tuned better. +constexpr bool has_extended_alignment = + kIsLinux && sizeof(void*) >= sizeof(std::uint64_t); + +namespace detail { + +// Implemented this way because of a bug in Clang for ARMv7, which gives the +// wrong result for `alignof` a `union` with a field of each scalar type. +template <typename... Ts> +struct max_align_t_ { + static constexpr std::size_t value() { + std::size_t const values[] = {0u, alignof(Ts)...}; + std::size_t r = 0u; + for (auto const v : values) { + r = r < v ? v : r; + } + return r; + } +}; +using max_align_v_ = max_align_t_< + long double, + double, + float, + long long int, + long int, + int, + short int, + bool, + char, + char16_t, + char32_t, + wchar_t, + void*, + std::max_align_t>; + +} // namespace detail + +// max_align_v is the alignment of max_align_t. +// +// max_align_t is a type which is aligned at least as strictly as the +// most-aligned basic type (see the specification of std::max_align_t). This +// implementation exists because 32-bit iOS platforms have a broken +// std::max_align_t (see below). +// +// You should refer to this as `::folly::max_align_t` in portable code, even if +// you have `using namespace folly;` because C11 defines a global namespace +// `max_align_t` type. +// +// To be certain, we consider every non-void fundamental type specified by the +// standard. On most platforms `long double` would be enough, but iOS 32-bit +// has an 8-byte aligned `double` and `long long int` and a 4-byte aligned +// `long double`. +// +// So far we've covered locals and other non-allocated storage, but we also need +// confidence that allocated storage from `malloc`, `new`, etc will also be +// suitable for objects with this alignment requirement. +// +// Apple document that their implementation of malloc will issue 16-byte +// granularity chunks for small allocations (large allocations are page-size +// granularity and page-aligned). We think that allocated storage will be +// suitable for these objects based on the following assumptions: +// +// 1. 16-byte granularity also means 16-byte aligned. +// 2. `new` and other allocators follow the `malloc` rules. +// +// We also have some anecdotal evidence: we don't see lots of misaligned-storage +// crashes on 32-bit iOS apps that use `double`. +// +// Apple's allocation reference: http://bit.ly/malloc-small +constexpr std::size_t max_align_v = detail::max_align_v_::value(); +struct alignas(max_align_v) max_align_t {}; + +// Memory locations within the same cache line are subject to destructive +// interference, also known as false sharing, which is when concurrent +// accesses to these different memory locations from different cores, where at +// least one of the concurrent accesses is or involves a store operation, +// induce contention and harm performance. +// +// Microbenchmarks indicate that pairs of cache lines also see destructive +// interference under heavy use of atomic operations, as observed for atomic +// increment on Sandy Bridge. +// +// We assume a cache line size of 64, so we use a cache line pair size of 128 +// to avoid destructive interference. +// +// mimic: std::hardware_destructive_interference_size, C++17 +constexpr std::size_t hardware_destructive_interference_size = + (kIsArchArm || kIsArchS390X) ? 64 : 128; +static_assert(hardware_destructive_interference_size >= max_align_v, "math?"); + +// Memory locations within the same cache line are subject to constructive +// interference, also known as true sharing, which is when accesses to some +// memory locations induce all memory locations within the same cache line to +// be cached, benefiting subsequent accesses to different memory locations +// within the same cache line and heping performance. +// +// mimic: std::hardware_constructive_interference_size, C++17 +constexpr std::size_t hardware_constructive_interference_size = 64; +static_assert(hardware_constructive_interference_size >= max_align_v, "math?"); + +// A value corresponding to hardware_constructive_interference_size but which +// may be used with alignas, since hardware_constructive_interference_size may +// be too large on some platforms to be used with alignas. +constexpr std::size_t cacheline_align_v = has_extended_alignment + ? hardware_constructive_interference_size + : max_align_v; +struct alignas(cacheline_align_v) cacheline_align_t {}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Aligned.h b/ios/Pods/Flipper-Folly/folly/lang/Aligned.h new file mode 100644 index 000000000..5d079f0a3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Aligned.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <type_traits> +#include <utility> + +#include <folly/Utility.h> +#include <folly/lang/Align.h> + +namespace folly { + +template <typename T, std::size_t Align> +class aligned { + static_assert(!(Align & (Align - 1)), "alignment not a power of two"); + static_assert(alignof(T) <= Align, "alignment too small"); + + public: + using alignment = index_constant<Align>; + using value_type = T; + + aligned() = default; + aligned(aligned const&) = default; + aligned(aligned&&) = default; + template < + typename S = T, + std::enable_if_t<std::is_copy_constructible<S>::value, int> = 0> + aligned(T const& value) noexcept(std::is_nothrow_copy_constructible<T>::value) + : value_(value) {} + template < + typename S = T, + std::enable_if_t<std::is_move_constructible<S>::value, int> = 0> + aligned(T&& value) noexcept(std::is_nothrow_move_constructible<T>::value) + : value_(static_cast<T&&>(value)) {} + template < + typename... A, + std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0> + explicit aligned(in_place_t, A&&... a) noexcept( + std::is_nothrow_constructible<T, A...>::value) + : value_(static_cast<A&&>(a)...) {} + + aligned& operator=(aligned const&) = default; + aligned& operator=(aligned&&) = default; + template < + typename S = T, + std::enable_if_t<std::is_copy_assignable<S>::value, int> = 0> + aligned& operator=(T const& value) noexcept( + std::is_nothrow_copy_assignable<T>::value) { + value_ = value; + return *this; + } + template < + typename S = T, + std::enable_if_t<std::is_move_assignable<S>::value, int> = 0> + aligned& operator=(T&& value) noexcept( + std::is_nothrow_move_assignable<T>::value) { + value_ = std::move(value); + return *this; + } + + T* get() noexcept { + return &value_; + } + T const* get() const noexcept { + return &value_; + } + T* operator->() noexcept { + return &value_; + } + T const* operator->() const noexcept { + return &value_; + } + T& operator*() noexcept { + return value_; + } + T const& operator*() const noexcept { + return value_; + } + + private: + alignas(Align) T value_; +}; + +template <typename T> +using cacheline_aligned = aligned< + T, + (cacheline_align_v < alignof(T) ? alignof(T) : cacheline_align_v)>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Assume-inl.h b/ios/Pods/Flipper-Folly/folly/lang/Assume-inl.h new file mode 100644 index 000000000..b99a34c84 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Assume-inl.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdlib> + +#include <folly/Portability.h> + +namespace folly { + +namespace detail { + +extern void assume_check(bool cond); + +} // namespace detail + +FOLLY_ALWAYS_INLINE void assume(bool cond) { + if (kIsDebug) { + detail::assume_check(cond); + } else { +#if defined(__clang__) // Must go first because Clang also defines __GNUC__. + __builtin_assume(cond); +#elif defined(__GNUC__) + if (!cond) { + __builtin_unreachable(); + } +#elif defined(_MSC_VER) + __assume(cond); +#else + // Do nothing. +#endif + } +} + +[[noreturn]] FOLLY_ALWAYS_INLINE void assume_unreachable() { + assume(false); + // Do a bit more to get the compiler to understand + // that this function really will never return. +#if defined(__GNUC__) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(0); +#else + // Well, it's better than nothing. + std::abort(); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Assume.cpp b/ios/Pods/Flipper-Folly/folly/lang/Assume.cpp new file mode 100644 index 000000000..07de169d4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Assume.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/lang/Assume.h> + +#include <glog/logging.h> + +namespace folly { + +namespace detail { + +void assume_check(bool cond) { + CHECK(cond) << "compiler-hint assumption fails at runtime"; +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Assume.h b/ios/Pods/Flipper-Folly/folly/lang/Assume.h new file mode 100644 index 000000000..6a7a44484 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Assume.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +namespace folly { + +/** + * assume*() functions can be used to fine-tune optimizations or suppress + * warnings when certain conditions are provably true, but the compiler is not + * able to prove them. + * + * This is different from assertions: an assertion will place an explicit check + * that the condition is true, and abort the program if the condition is not + * verified. Calling assume*() with a condition that is not true at runtime + * is undefined behavior: for example, it may or may not result in a crash, + * silently corrupt memory, or jump to a random code path. + * + * These functions should only be used on conditions that are provable internal + * logic invariants; they cannot be used safely if the condition depends on + * external inputs or data. To detect unexpected conditions that *can* happen, + * an assertion or exception should be used. + */ + +/** + * assume(cond) informs the compiler that cond can be assumed true. If cond is + * not true at runtime the behavior is undefined. + * + * The typical use case is to allow the compiler exploit data structure + * invariants that can trigger better optimizations, for example to eliminate + * unnecessary bounds checks in a called function. It is recommended to check + * the generated code or run microbenchmarks to assess whether it is actually + * effective. + * + * The semantics are similar to clang's __builtin_assume(), but intentionally + * implemented as a function to force the evaluation of its argument, contrary + * to the builtin, which cannot used with expressions that have side-effects. + */ +FOLLY_ALWAYS_INLINE void assume(bool cond); + +/** + * assume_unreachable() informs the compiler that the statement is not reachable + * at runtime. It is undefined behavior if the statement is actually reached. + * + * Common use cases are to suppress a warning when the compiler cannot prove + * that the end of a non-void function is not reachable, or to optimize the + * evaluation of switch/case statements when all the possible values are + * provably enumerated. + */ +[[noreturn]] FOLLY_ALWAYS_INLINE void assume_unreachable(); + +} // namespace folly + +#include <folly/lang/Assume-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/lang/Bits.h b/ios/Pods/Flipper-Folly/folly/lang/Bits.h new file mode 100644 index 000000000..481701d40 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Bits.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Various low-level, bit-manipulation routines. + * + * findFirstSet(x) [constexpr] + * find first (least significant) bit set in a value of an integral type, + * 1-based (like ffs()). 0 = no bits are set (x == 0) + * + * findLastSet(x) [constexpr] + * find last (most significant) bit set in a value of an integral type, + * 1-based. 0 = no bits are set (x == 0) + * for x != 0, findLastSet(x) == 1 + floor(log2(x)) + * + * extractFirstSet(x) [constexpr] + * extract first (least significant) bit set in a value of an integral + * type, 0 = no bits are set (x == 0) + * + * nextPowTwo(x) [constexpr] + * Finds the next power of two >= x. + * + * isPowTwo(x) [constexpr] + * return true iff x is a power of two + * + * popcount(x) + * return the number of 1 bits in x + * + * Endian + * convert between native, big, and little endian representation + * Endian::big(x) big <-> native + * Endian::little(x) little <-> native + * Endian::swap(x) big <-> little + * + * @author Tudor Bosman (tudorb@fb.com) + */ + +#pragma once + +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstring> +#include <limits> +#include <type_traits> + +#include <folly/ConstexprMath.h> +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/lang/Assume.h> +#include <folly/portability/Builtins.h> + +#if __has_include(<bit>) +#include <bit> +#endif + +namespace folly { + +#if __cpp_lib_bit_cast + +using std::bit_cast; + +#else + +// mimic: std::bit_cast, C++20 +template < + typename To, + typename From, + std::enable_if_t< + sizeof(From) == sizeof(To) && is_trivially_copyable<To>::value && + is_trivially_copyable<From>::value, + int> = 0> +To bit_cast(const From& src) noexcept { + aligned_storage_for_t<To> storage; + std::memcpy(&storage, &src, sizeof(From)); + return reinterpret_cast<To&>(storage); +} + +#endif + +namespace detail { +template <typename Dst, typename Src> +constexpr std::make_signed_t<Dst> bits_to_signed(Src const s) { + static_assert(std::is_signed<Dst>::value, "unsigned type"); + return to_signed(static_cast<std::make_unsigned_t<Dst>>(to_unsigned(s))); +} +template <typename Dst, typename Src> +constexpr std::make_unsigned_t<Dst> bits_to_unsigned(Src const s) { + static_assert(std::is_unsigned<Dst>::value, "signed type"); + return static_cast<Dst>(to_unsigned(s)); +} +} // namespace detail + +/// findFirstSet +/// +/// Return the 1-based index of the least significant bit which is set. +/// For x > 0, the exponent in the largest power of two which does not divide x. +template <typename T> +inline constexpr unsigned int findFirstSet(T const v) { + using S0 = int; + using S1 = long int; + using S2 = long long int; + using detail::bits_to_signed; + static_assert(sizeof(T) <= sizeof(S2), "over-sized type"); + static_assert(std::is_integral<T>::value, "non-integral type"); + static_assert(!std::is_same<T, bool>::value, "bool type"); + + // clang-format off + return static_cast<unsigned int>( + sizeof(T) <= sizeof(S0) ? __builtin_ffs(bits_to_signed<S0>(v)) : + sizeof(T) <= sizeof(S1) ? __builtin_ffsl(bits_to_signed<S1>(v)) : + sizeof(T) <= sizeof(S2) ? __builtin_ffsll(bits_to_signed<S2>(v)) : + 0); + // clang-format on +} + +/// findLastSet +/// +/// Return the 1-based index of the most significant bit which is set. +/// For x > 0, findLastSet(x) == 1 + floor(log2(x)). +template <typename T> +inline constexpr unsigned int findLastSet(T const v) { + using U0 = unsigned int; + using U1 = unsigned long int; + using U2 = unsigned long long int; + using detail::bits_to_unsigned; + static_assert(sizeof(T) <= sizeof(U2), "over-sized type"); + static_assert(std::is_integral<T>::value, "non-integral type"); + static_assert(!std::is_same<T, bool>::value, "bool type"); + + // If X is a power of two X - Y = 1 + ((X - 1) ^ Y). Doing this transformation + // allows GCC to remove its own xor that it adds to implement clz using bsr. + // clang-format off + using size = index_constant<constexpr_max(sizeof(T), sizeof(U0))>; + return v ? 1u + static_cast<unsigned int>((8u * size{} - 1u) ^ ( + sizeof(T) <= sizeof(U0) ? __builtin_clz(bits_to_unsigned<U0>(v)) : + sizeof(T) <= sizeof(U1) ? __builtin_clzl(bits_to_unsigned<U1>(v)) : + sizeof(T) <= sizeof(U2) ? __builtin_clzll(bits_to_unsigned<U2>(v)) : + 0)) : 0u; + // clang-format on +} + +/// extractFirstSet +/// +/// Return a value where all the bits but the least significant are cleared. +template <typename T> +inline constexpr T extractFirstSet(T const v) { + static_assert(std::is_integral<T>::value, "non-integral type"); + static_assert(std::is_unsigned<T>::value, "signed type"); + static_assert(!std::is_same<T, bool>::value, "bool type"); + + return v & -v; +} + +/// popcount +/// +/// Returns the number of bits which are set. +template <typename T> +inline constexpr unsigned int popcount(T const v) { + using U0 = unsigned int; + using U1 = unsigned long int; + using U2 = unsigned long long int; + using detail::bits_to_unsigned; + static_assert(sizeof(T) <= sizeof(U2), "over-sized type"); + static_assert(std::is_integral<T>::value, "non-integral type"); + static_assert(!std::is_same<T, bool>::value, "bool type"); + + // clang-format off + return static_cast<unsigned int>( + sizeof(T) <= sizeof(U0) ? __builtin_popcount(bits_to_unsigned<U0>(v)) : + sizeof(T) <= sizeof(U1) ? __builtin_popcountl(bits_to_unsigned<U1>(v)) : + sizeof(T) <= sizeof(U2) ? __builtin_popcountll(bits_to_unsigned<U2>(v)) : + 0); + // clang-format on +} + +template <class T> +inline constexpr T nextPowTwo(T const v) { + static_assert(std::is_unsigned<T>::value, "signed type"); + return v ? (T(1) << findLastSet(v - 1)) : T(1); +} + +template <class T> +inline constexpr T prevPowTwo(T const v) { + static_assert(std::is_unsigned<T>::value, "signed type"); + return v ? (T(1) << (findLastSet(v) - 1)) : T(0); +} + +template <class T> +inline constexpr bool isPowTwo(T const v) { + static_assert(std::is_integral<T>::value, "non-integral type"); + static_assert(std::is_unsigned<T>::value, "signed type"); + static_assert(!std::is_same<T, bool>::value, "bool type"); + return (v != 0) && !(v & (v - 1)); +} + +/** + * Endianness detection and manipulation primitives. + */ +namespace detail { + +template <size_t Size> +struct uint_types_by_size; + +#define FB_GEN(sz, fn) \ + static inline uint##sz##_t byteswap_gen(uint##sz##_t v) { \ + return fn(v); \ + } \ + template <> \ + struct uint_types_by_size<sz / 8> { \ + using type = uint##sz##_t; \ + }; + +FB_GEN(8, uint8_t) +#ifdef _MSC_VER +FB_GEN(64, _byteswap_uint64) +FB_GEN(32, _byteswap_ulong) +FB_GEN(16, _byteswap_ushort) +#else +FB_GEN(64, __builtin_bswap64) +FB_GEN(32, __builtin_bswap32) +FB_GEN(16, __builtin_bswap16) +#endif + +#undef FB_GEN + +template <class T> +struct EndianInt { + static_assert( + (std::is_integral<T>::value && !std::is_same<T, bool>::value) || + std::is_floating_point<T>::value, + "template type parameter must be non-bool integral or floating point"); + static T swap(T x) { + // we implement this with bit_cast because that is defined behavior in C++ + // we rely on compilers to optimize away the bit_cast calls + constexpr auto s = sizeof(T); + using B = typename uint_types_by_size<s>::type; + return bit_cast<T>(byteswap_gen(bit_cast<B>(x))); + } + static T big(T x) { + return kIsLittleEndian ? EndianInt::swap(x) : x; + } + static T little(T x) { + return kIsBigEndian ? EndianInt::swap(x) : x; + } +}; + +} // namespace detail + +// big* convert between native and big-endian representations +// little* convert between native and little-endian representations +// swap* convert between big-endian and little-endian representations +// +// ntohs, htons == big16 +// ntohl, htonl == big32 +#define FB_GEN1(fn, t, sz) \ + static t fn##sz(t x) { \ + return fn<t>(x); \ + } + +#define FB_GEN2(t, sz) \ + FB_GEN1(swap, t, sz) \ + FB_GEN1(big, t, sz) \ + FB_GEN1(little, t, sz) + +#define FB_GEN(sz) \ + FB_GEN2(uint##sz##_t, sz) \ + FB_GEN2(int##sz##_t, sz) + +class Endian { + public: + enum class Order : uint8_t { + LITTLE, + BIG, + }; + + static constexpr Order order = kIsLittleEndian ? Order::LITTLE : Order::BIG; + + template <class T> + static T swap(T x) { + return folly::detail::EndianInt<T>::swap(x); + } + template <class T> + static T big(T x) { + return folly::detail::EndianInt<T>::big(x); + } + template <class T> + static T little(T x) { + return folly::detail::EndianInt<T>::little(x); + } + +#if !defined(__ANDROID__) + FB_GEN(64) + FB_GEN(32) + FB_GEN(16) + FB_GEN(8) +#endif +}; + +#undef FB_GEN +#undef FB_GEN2 +#undef FB_GEN1 + +template <class T, class Enable = void> +struct Unaligned; + +/** + * Representation of an unaligned value of a POD type. + */ +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wpacked") +FOLLY_PACK_PUSH +template <class T> +struct Unaligned<T, typename std::enable_if<std::is_pod<T>::value>::type> { + Unaligned() = default; // uninitialized + /* implicit */ Unaligned(T v) : value(v) {} + T value; +} FOLLY_PACK_ATTR; +FOLLY_PACK_POP +FOLLY_POP_WARNING + +/** + * Read an unaligned value of type T and return it. + */ +template <class T> +inline T loadUnaligned(const void* p) { + static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size"); + static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment"); + if (kHasUnalignedAccess) { + return static_cast<const Unaligned<T>*>(p)->value; + } else { + T value; + memcpy(&value, p, sizeof(T)); + return value; + } +} + +/** + * Read l bytes into the low bits of a value of an unsigned integral + * type T, where l < sizeof(T). + * + * This is intended as a complement to loadUnaligned to read the tail + * of a buffer when it is processed one word at a time. + */ +template <class T> +inline T partialLoadUnaligned(const void* p, size_t l) { + static_assert( + std::is_integral<T>::value && std::is_unsigned<T>::value && + sizeof(T) <= 8, + "Invalid type"); + assume(l < sizeof(T)); + + auto cp = static_cast<const char*>(p); + T value = 0; + if (!kHasUnalignedAccess || !kIsLittleEndian) { + // Unsupported, use memcpy. + memcpy(&value, cp, l); + return value; + } + + auto avail = l; + if (l & 4) { + avail -= 4; + value = static_cast<T>(loadUnaligned<uint32_t>(cp + avail)) << (avail * 8); + } + if (l & 2) { + avail -= 2; + value |= static_cast<T>(loadUnaligned<uint16_t>(cp + avail)) << (avail * 8); + } + if (l & 1) { + value |= loadUnaligned<uint8_t>(cp); + } + return value; +} + +/** + * Write an unaligned value of type T. + */ +template <class T> +inline void storeUnaligned(void* p, T value) { + static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size"); + static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment"); + if (kHasUnalignedAccess) { + // Prior to C++14, the spec says that a placement new like this + // is required to check that p is not nullptr, and to do nothing + // if p is a nullptr. By assuming it's not a nullptr, we get a + // nice loud segfault in optimized builds if p is nullptr, rather + // than just silently doing nothing. + assume(p != nullptr); + new (p) Unaligned<T>(value); + } else { + memcpy(p, &value, sizeof(T)); + } +} + +template <typename T> +T bitReverse(T n) { + auto m = static_cast<typename std::make_unsigned<T>::type>(n); + m = ((m & 0xAAAAAAAAAAAAAAAA) >> 1) | ((m & 0x5555555555555555) << 1); + m = ((m & 0xCCCCCCCCCCCCCCCC) >> 2) | ((m & 0x3333333333333333) << 2); + m = ((m & 0xF0F0F0F0F0F0F0F0) >> 4) | ((m & 0x0F0F0F0F0F0F0F0F) << 4); + return static_cast<T>(Endian::swap(m)); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/CString.cpp b/ios/Pods/Flipper-Folly/folly/lang/CString.cpp new file mode 100644 index 000000000..ed78deeae --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/CString.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/lang/CString.h> + +#include <algorithm> + +namespace folly { + +std::size_t +strlcpy(char* const dest, char const* const src, std::size_t const size) { + std::size_t const len = std::strlen(src); + if (size != 0) { + std::size_t const n = std::min(len, size - 1); // always null terminate! + std::memcpy(dest, src, n); + dest[n] = '\0'; + } + return len; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/CString.h b/ios/Pods/Flipper-Folly/folly/lang/CString.h new file mode 100644 index 000000000..ced6aaead --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/CString.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <cstring> + +namespace folly { + +// strlcpy +// +// mimic: strlcpy, libbsd +std::size_t strlcpy(char* dest, char const* src, std::size_t size); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Cast.h b/ios/Pods/Flipper-Folly/folly/lang/Cast.h new file mode 100644 index 000000000..709f17ef1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Cast.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/lang/SafeAssert.h> + +namespace folly { + +// down_cast +// +// Unchecked polymorphic down-cast using static_cast. Only works for pairs of +// types where the cvref-unqualified source type is polymorphic and a base of +// the target type. The target type, which is passed as an explicit template +// param, must be cvref-unqualified. The return type is the target type with +// the same cvref-qualifiers or cvptr-qualifiers as the source type. +// +// Checked with an assertion in debug builds. +template <typename T, typename S> +FOLLY_ERASE like_t<S, T>* down_cast(S* ptr) noexcept { + using Q = std::remove_cv_t<S>; + static_assert(std::is_polymorphic<Q>::value, "not polymorphic"); + static_assert(std::is_base_of<Q, T>::value, "not down-castable"); + using R = like_t<S, T>; + FOLLY_SAFE_DCHECK(dynamic_cast<R*>(ptr), "not a runtime down-cast"); + return static_cast<R*>(ptr); +} +template <typename T, typename S> +FOLLY_ERASE like_t<S&&, T> down_cast(S&& ref) noexcept { + return static_cast<like_t<S&&, T>>(*down_cast<T>(std::addressof(ref))); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/CheckedMath.h b/ios/Pods/Flipper-Folly/folly/lang/CheckedMath.h new file mode 100644 index 000000000..e474a3382 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/CheckedMath.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <limits> +#include <type_traits> + +#include <folly/Likely.h> + +namespace folly { +template <typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>> +bool checked_add(T* result, T a, T b) { + assert(result != nullptr); + if (FOLLY_LIKELY(a < std::numeric_limits<T>::max() - b)) { + *result = a + b; + return true; + } else { + *result = {}; + return false; + } +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/CustomizationPoint.h b/ios/Pods/Flipper-Folly/folly/lang/CustomizationPoint.h new file mode 100644 index 000000000..79ce3f560 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/CustomizationPoint.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/lang/StaticConst.h> + +// FOLLY_DEFINE_CPO +// +// Helper for portably defining customization-point objects (CPOs). +// +// The customization-point object must be placed in a nested namespace to avoid +// potential conflicts with customizations defined as friend-functions of types +// defined in the same namespace as the CPO. +// +// In C++17 and later the object may be defined using 'inline constexpr' to +// avoid ODR issues. However, prior to that a helper template is required to +// ensure that there is only a single instance of the CPO created and then a +// named reference in an anonymous namespace is required to avoid duplicate +// symbol definitions. +#if FOLLY_HAS_INLINE_VARIABLES +#define FOLLY_DEFINE_CPO(Type, Name) \ + namespace folly_cpo__ { \ + inline constexpr Type Name{}; \ + } \ + using namespace folly_cpo__; +#else +#define FOLLY_DEFINE_CPO(Type, Name) \ + namespace { \ + constexpr auto& Name = ::folly::StaticConst<Type>::value; \ + } +#endif diff --git a/ios/Pods/Flipper-Folly/folly/lang/Exception.h b/ios/Pods/Flipper-Folly/folly/lang/Exception.h new file mode 100644 index 000000000..e1cdfd067 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Exception.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <exception> +#include <type_traits> +#include <utility> + +#include <folly/CPortability.h> +#include <folly/CppAttributes.h> +#include <folly/Portability.h> + +namespace folly { + +/// throw_exception +/// +/// Throw an exception if exceptions are enabled, or terminate if compiled with +/// -fno-exceptions. +template <typename Ex> +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception(Ex&& ex) { +#if FOLLY_HAS_EXCEPTIONS + throw static_cast<Ex&&>(ex); +#else + (void)ex; + std::terminate(); +#endif +} + +/// terminate_with +/// +/// Terminates as if by forwarding to throw_exception but in a noexcept context. +template <typename Ex> +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with(Ex&& ex) noexcept { + throw_exception(static_cast<Ex&&>(ex)); +} + +// clang-format off +namespace detail { +template <typename T> +FOLLY_ERASE T&& to_exception_arg_(T&& t) { + return static_cast<T&&>(t); +} +template <std::size_t N> +FOLLY_ERASE char const* to_exception_arg_( + char const (&array)[N]) { + return static_cast<char const*>(array); +} +template <typename Ex, typename... Args> +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args&&... args) { + throw_exception(Ex(static_cast<Args&&>(args)...)); +} +template <typename Ex, typename... Args> +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with_( + Args&&... args) noexcept { + throw_exception(Ex(static_cast<Args&&>(args)...)); +} +} // namespace detail +// clang-format on + +/// throw_exception +/// +/// Construct and throw an exception if exceptions are enabled, or terminate if +/// compiled with -fno-exceptions. +/// +/// Converts any arguments of type `char const[N]` to `char const*`. +template <typename Ex, typename... Args> +[[noreturn]] FOLLY_ERASE void throw_exception(Args&&... args) { + detail::throw_exception_<Ex>( + detail::to_exception_arg_(static_cast<Args&&>(args))...); +} + +/// terminate_with +/// +/// Terminates as if by forwarding to throw_exception but in a noexcept context. +// clang-format off +template <typename Ex, typename... Args> +[[noreturn]] FOLLY_ERASE void +terminate_with(Args&&... args) noexcept { + detail::terminate_with_<Ex>( + detail::to_exception_arg_(static_cast<Args&&>(args))...); +} +// clang-format on + +/// invoke_cold +/// +/// Invoke the provided function with the provided arguments. +/// +/// Usage note: +/// Passing extra values as arguments rather than capturing them allows smaller +/// inlined native at the call-site. +/// +/// Example: +/// +/// if (i < 0) { +/// invoke_cold( +/// [](int j) { +/// std::string ret = doStepA(); +/// doStepB(ret); +/// doStepC(ret); +/// }, +/// i); +/// } +template <typename F, typename... A> +FOLLY_NOINLINE FOLLY_COLD auto invoke_cold(F&& f, A&&... a) + -> decltype(static_cast<F&&>(f)(static_cast<A&&>(a)...)) { + return static_cast<F&&>(f)(static_cast<A&&>(a)...); +} + +/// invoke_noreturn_cold +/// +/// Invoke the provided function with the provided arguments. If the invocation +/// returns, terminate. +/// +/// May be used with throw_exception in cases where construction of the object +/// to be thrown requires more than just invoking its constructor with a given +/// sequence of arguments passed by reference - for example, if a string message +/// must be computed before being passed to the constructor of the object to be +/// thrown. +/// +/// Usage note: +/// Passing extra values as arguments rather than capturing them allows smaller +/// inlined native code at the call-site. +/// +/// Example: +/// +/// if (i < 0) { +/// invoke_noreturn_cold( +/// [](int j) { +/// throw_exceptions(runtime_error(to<string>("invalid: ", j))); +/// }, +/// i); +/// } +template <typename F, typename... A> +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void invoke_noreturn_cold( + F&& f, + A&&... a) { + static_cast<F&&>(f)(static_cast<A&&>(a)...); + std::terminate(); +} + +/// catch_exception +/// +/// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), +/// catches a thrown exception e of type E and invokes c, forwarding e and any +/// trailing arguments. +/// +/// Usage note: +/// As a general rule, pass Ex const& rather than unqualified Ex as the explicit +/// template argument E. The catch statement catches E without qualifiers so +/// if E is Ex then that translates to catch (Ex), but if E is Ex const& then +/// that translates to catch (Ex const&). +/// +/// Usage note: +/// Passing extra values as arguments rather than capturing them allows smaller +/// inlined native code at the call-site. +/// +/// Example: +/// +/// int input = // ... +/// int def = 45; +/// auto result = catch_exception<std::runtime_error const&>( +/// [=] { +/// if (input < 0) throw std::runtime_error("foo"); +/// return input; +/// }, +/// [](auto&& e, int num) { return num; }, +/// def); +/// assert(result == input < 0 ? def : input); +template <typename E, typename Try, typename Catch, typename... CatchA> +FOLLY_ERASE_TRYCATCH auto catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> + typename std::common_type< + decltype(static_cast<Try&&>(t)()), + decltype(static_cast<Catch&&>( + c)(std::declval<E>(), static_cast<CatchA&&>(a)...))>::type { +#if FOLLY_HAS_EXCEPTIONS + try { + return static_cast<Try&&>(t)(); + } catch (E e) { + return invoke_cold(static_cast<Catch&&>(c), e, static_cast<CatchA&&>(a)...); + } +#else + [](auto&&...) {}(c, a...); // ignore + return static_cast<Try&&>(t)(); +#endif +} + +/// catch_exception +/// +/// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), +/// catches a thrown exception of any type and invokes c, forwarding any +/// trailing arguments. +// +/// Usage note: +/// Passing extra values as arguments rather than capturing them allows smaller +/// inlined native code at the call-site. +/// +/// Example: +/// +/// int input = // ... +/// int def = 45; +/// auto result = catch_exception( +/// [=] { +/// if (input < 0) throw 11; +/// return input; +/// }, +/// [](int num) { return num; }, +/// def); +/// assert(result == input < 0 ? def : input); +template <typename Try, typename Catch, typename... CatchA> +FOLLY_ERASE_TRYCATCH auto catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> + typename std::common_type< + decltype(static_cast<Try&&>(t)()), + decltype(static_cast<Catch&&>(c)(static_cast<CatchA&&>(a)...))>::type { +#if FOLLY_HAS_EXCEPTIONS + try { + return static_cast<Try&&>(t)(); + } catch (...) { + return invoke_cold(static_cast<Catch&&>(c), static_cast<CatchA&&>(a)...); + } +#else + [](auto&&...) {}(c, a...); // ignore + return static_cast<Try&&>(t)(); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Launder.h b/ios/Pods/Flipper-Folly/folly/lang/Launder.h new file mode 100644 index 000000000..738305669 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Launder.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <new> + +#include <folly/CPortability.h> +#include <folly/Portability.h> + +/*** + * include or backport: + * * std::launder + */ + +// Note: libc++ 6+ adds std::launder but does not define __cpp_lib_launder +#if __cpp_lib_launder >= 201606 || (_MSC_VER && _HAS_LAUNDER) || \ + ((_LIBCPP_VERSION >= (__ANDROID__ ? 7000 : 6000)) && \ + __cplusplus >= 201703L) + +namespace folly { + +/* using override */ using std::launder; + +} // namespace folly + +#else + +namespace folly { + +/** + * Approximate backport from C++17 of std::launder. It should be `constexpr` + * but that can't be done without specific support from the compiler. + */ +template <typename T> +FOLLY_NODISCARD inline T* launder(T* in) noexcept { +#if FOLLY_HAS_BUILTIN(__builtin_launder) || __GNUC__ >= 7 + // The builtin has no unwanted side-effects. + return __builtin_launder(in); +#elif __GNUC__ + // This inline assembler block declares that `in` is an input and an output, + // so the compiler has to assume that it has been changed inside the block. + __asm__("" : "+r"(in)); + return in; +#elif defined(_WIN32) + // MSVC does not currently have optimizations around const members of structs. + // _ReadWriteBarrier() will prevent compiler reordering memory accesses. + _ReadWriteBarrier(); + return in; +#else + static_assert( + false, "folly::launder is not implemented for this environment"); +#endif +} + +/* The standard explicitly forbids laundering these */ +void launder(void*) = delete; +void launder(void const*) = delete; +void launder(void volatile*) = delete; +void launder(void const volatile*) = delete; +template <typename T, typename... Args> +void launder(T (*)(Args...)) = delete; +} // namespace folly + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/lang/Ordering.h b/ios/Pods/Flipper-Folly/folly/lang/Ordering.h new file mode 100644 index 000000000..f21a0e2fb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Ordering.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +enum class ordering : int { lt = -1, eq = 0, gt = 1 }; + +template <typename T> +constexpr ordering to_ordering(T c) { + return ordering(int(c < T(0)) * -1 + int(c > T(0))); +} + +namespace detail { + +template <typename C, ordering o, bool ne> +struct cmp_pred : private C { + using C::C; + + template <typename A, typename B> + constexpr bool operator()(A&& a, B&& b) const { + return ne ^ (C::operator()(static_cast<A&&>(a), static_cast<B&&>(b)) == o); + } +}; + +} // namespace detail + +template <typename C> +struct compare_equal_to : detail::cmp_pred<C, ordering::eq, 0> { + using detail::cmp_pred<C, ordering::eq, 0>::cmp_pred; +}; + +template <typename C> +struct compare_not_equal_to : detail::cmp_pred<C, ordering::eq, 1> { + using detail::cmp_pred<C, ordering::eq, 1>::cmp_pred; +}; + +template <typename C> +struct compare_less : detail::cmp_pred<C, ordering::lt, 0> { + using detail::cmp_pred<C, ordering::lt, 0>::cmp_pred; +}; + +template <typename C> +struct compare_less_equal : detail::cmp_pred<C, ordering::gt, 1> { + using detail::cmp_pred<C, ordering::gt, 1>::cmp_pred; +}; + +template <typename C> +struct compare_greater : detail::cmp_pred<C, ordering::gt, 0> { + using detail::cmp_pred<C, ordering::gt, 0>::cmp_pred; +}; + +template <typename C> +struct compare_greater_equal : detail::cmp_pred<C, ordering::lt, 1> { + using detail::cmp_pred<C, ordering::lt, 1>::cmp_pred; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/Pretty.h b/ios/Pods/Flipper-Folly/folly/lang/Pretty.h new file mode 100644 index 000000000..35ca3d5b0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/Pretty.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstddef> +#include <type_traits> + +#include <folly/Portability.h> + +namespace folly { + +namespace detail { + +template <std::size_t S> +struct pretty_carray { + char data[S]; +}; + +template <std::size_t S> +static constexpr pretty_carray<S> pretty_carray_from(char const (&in)[S]) { + pretty_carray<S> out{}; + for (std::size_t i = 0; i < S; ++i) { + out.data[i] = in[i]; + } + return out; +} + +struct pretty_info { + std::size_t b; + std::size_t e; +}; + +template <typename To, std::size_t S> +static constexpr To pretty_info_to(pretty_info info, char const (&name)[S]) { + return To(name + info.b, info.e - info.b); +} + +template <std::size_t S> +static constexpr std::size_t pretty_lfind( + char const (&haystack)[S], + char const needle) { + for (std::size_t i = 0; i < S - 1; ++i) { + if (haystack[i] == needle) { + return i; + } + } + return ~std::size_t(0); +} + +template <std::size_t S> +static constexpr std::size_t pretty_rfind( + char const (&haystack)[S], + char const needle) { + for (std::size_t i = S; i != 0; --i) { + if (haystack[i - 1] == needle) { + return i - 1; + } + } + return ~std::size_t(0); +} + +struct pretty_tag_msc {}; +struct pretty_tag_gcc {}; + +using pretty_default_tag = std::conditional_t< // + kMscVer && !kIsClang, + pretty_tag_msc, + pretty_tag_gcc>; + +template <typename T> +static constexpr auto pretty_raw(pretty_tag_msc) { +#if defined(_MSC_VER) + return pretty_carray_from(__FUNCSIG__); +#endif +} + +template <typename T> +static constexpr auto pretty_raw(pretty_tag_gcc) { +#if defined(__GNUC__) || defined(__clang__) + return pretty_carray_from(__PRETTY_FUNCTION__); +#endif +} + +template <std::size_t S> +static constexpr pretty_info pretty_parse( + pretty_tag_msc, + char const (&name)[S]) { + // void __cdecl folly::detail::pretty_raw<{...}>( + // folly::detail::pretty_tag_msc) + auto const la = pretty_lfind(name, '<'); + auto const rp = pretty_rfind(name, '>'); + return pretty_info{la + 1, rp}; +} + +template <std::size_t S> +static constexpr pretty_info pretty_parse( + pretty_tag_gcc, + char const (&name)[S]) { + // void folly::detail::pretty_raw( + // folly::detail::pretty_tag_gcc) [T = {...}] + auto const eq = pretty_lfind(name, '='); + auto const br = pretty_rfind(name, ']'); + return pretty_info{eq + 2, br}; +} + +template <typename T, typename Tag> +struct pretty_name_zarray { + static constexpr auto raw_() { + constexpr auto const raw_ = pretty_raw<T>(Tag{}); + return raw_; + } + static constexpr auto raw = raw_(); // indirection b/c of gcc-5.3 ice, gh#1105 + static constexpr auto info = pretty_parse(Tag{}, raw.data); + static constexpr auto size = info.e - info.b; + static constexpr auto zarray_() { + pretty_carray<size + 1> data{}; + for (std::size_t i = 0; i < size; ++i) { + data.data[i] = raw.data[info.b + i]; + } + data.data[size] = 0; + return data; + } + static constexpr pretty_carray<size + 1> zarray = zarray_(); +}; + +template <typename T, typename Tag> +constexpr pretty_carray<pretty_name_zarray<T, Tag>::size + 1> + pretty_name_zarray<T, Tag>::zarray; + +} // namespace detail + +// pretty_name +// +// Returns a statically-allocated C string containing the pretty name of T. +// +// The pretty name of a type varies by compiler, may include tokens which +// would not be present in the type name as it is spelled in the source code +// or as it would be symbolized, and may not include tokens which would be +// present in the type name as it would be symbolized. +template <typename T> +constexpr char const* pretty_name() { + return detail::pretty_name_zarray<T, detail::pretty_default_tag>::zarray.data; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/PropagateConst.h b/ios/Pods/Flipper-Folly/folly/lang/PropagateConst.h new file mode 100644 index 000000000..a284df9ad --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/PropagateConst.h @@ -0,0 +1,432 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <functional> +#include <type_traits> +#include <utility> + +#include <folly/Traits.h> +#include <folly/Utility.h> + +namespace folly { + +template <typename Pointer> +class propagate_const; + +template <typename Pointer> +constexpr Pointer& get_underlying(propagate_const<Pointer>& obj) { + return obj.pointer_; +} + +template <typename Pointer> +constexpr Pointer const& get_underlying(propagate_const<Pointer> const& obj) { + return obj.pointer_; +} + +namespace detail { +template <class Pointer> +using is_propagate_const = is_instantiation_of<propagate_const, Pointer>; +template <typename T> +using is_decay_propagate_const = is_propagate_const<std::decay_t<T>>; + +namespace propagate_const_adl { +using std::swap; +template <typename T> +auto adl_swap(T& a, T& b) noexcept(noexcept(swap(a, b))) + -> decltype(swap(a, b)) { + swap(a, b); +} +} // namespace propagate_const_adl +} // namespace detail + +// mimic: std::experimental::propagate_const, C++ Library Fundamentals TS v2 +template <typename Pointer> +class propagate_const { + public: + using element_type = + std::remove_reference_t<decltype(*std::declval<Pointer&>())>; + + constexpr propagate_const() = default; + constexpr propagate_const(propagate_const&&) = default; + propagate_const(propagate_const const&) = delete; + + template < + typename OtherPointer, + std::enable_if_t< + std::is_constructible<Pointer, OtherPointer&&>::value && + !std::is_convertible<OtherPointer&&, Pointer>::value, + int> = 0> + constexpr explicit propagate_const(propagate_const<OtherPointer>&& other) + : pointer_(static_cast<OtherPointer&&>(other.pointer_)) {} + + template < + typename OtherPointer, + std::enable_if_t< + std::is_constructible<Pointer, OtherPointer&&>::value && + std::is_convertible<OtherPointer&&, Pointer>::value, + int> = 0> + constexpr propagate_const(propagate_const<OtherPointer>&& other) + : pointer_(static_cast<OtherPointer&&>(other.pointer_)) {} + + template < + typename OtherPointer, + std::enable_if_t< + !detail::is_decay_propagate_const<OtherPointer>::value && + std::is_constructible<Pointer, OtherPointer&&>::value && + !std::is_convertible<OtherPointer&&, Pointer>::value, + int> = 0> + constexpr explicit propagate_const(OtherPointer&& other) + : pointer_(static_cast<OtherPointer&&>(other)) {} + + template < + typename OtherPointer, + std::enable_if_t< + !detail::is_decay_propagate_const<OtherPointer>::value && + std::is_constructible<Pointer, OtherPointer&&>::value && + std::is_convertible<OtherPointer&&, Pointer>::value, + int> = 0> + constexpr propagate_const(OtherPointer&& other) + : pointer_(static_cast<OtherPointer&&>(other)) {} + + constexpr propagate_const& operator=(propagate_const&&) = default; + propagate_const& operator=(propagate_const const&) = delete; + + template < + typename OtherPointer, + typename = + std::enable_if_t<std::is_convertible<OtherPointer&&, Pointer>::value>> + constexpr propagate_const& operator=(propagate_const<OtherPointer>&& other) { + pointer_ = static_cast<OtherPointer&&>(other.pointer_); + } + + template < + typename OtherPointer, + typename = std::enable_if_t< + !detail::is_decay_propagate_const<OtherPointer>::value && + std::is_convertible<OtherPointer&&, Pointer>::value>> + constexpr propagate_const& operator=(OtherPointer&& other) { + pointer_ = static_cast<OtherPointer&&>(other); + return *this; + } + + constexpr void swap(propagate_const& other) noexcept( + noexcept(detail::propagate_const_adl::adl_swap( + std::declval<Pointer&>(), + other.pointer_))) { + detail::propagate_const_adl::adl_swap(pointer_, other.pointer_); + } + + constexpr element_type* get() { + return get_(pointer_); + } + + constexpr element_type const* get() const { + return get_(pointer_); + } + + constexpr explicit operator bool() const { + return static_cast<bool>(pointer_); + } + + constexpr element_type& operator*() { + return *get(); + } + + constexpr element_type const& operator*() const { + return *get(); + } + + constexpr element_type* operator->() { + return get(); + } + + constexpr element_type const* operator->() const { + return get(); + } + + template < + typename OtherPointer = Pointer, + typename = std::enable_if_t< + std::is_pointer<OtherPointer>::value || + std::is_convertible<OtherPointer, element_type*>::value>> + constexpr operator element_type*() { + return get(); + } + + template < + typename OtherPointer = Pointer, + typename = std::enable_if_t< + std::is_pointer<OtherPointer>::value || + std::is_convertible<OtherPointer, element_type const*>::value>> + constexpr operator element_type const*() const { + return get(); + } + + private: + friend Pointer& get_underlying<>(propagate_const&); + friend Pointer const& get_underlying<>(propagate_const const&); + template <typename OtherPointer> + friend class propagate_const; + + template <typename T> + constexpr static T* get_(T* t) { + return t; + } + template <typename T> + constexpr static auto get_(T& t) -> decltype(t.get()) { + return t.get(); + } + + Pointer pointer_; +}; + +template <typename Pointer> +constexpr void swap( + propagate_const<Pointer>& a, + propagate_const<Pointer>& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +template <typename Pointer> +constexpr bool operator==(propagate_const<Pointer> const& a, std::nullptr_t) { + return get_underlying(a) == nullptr; +} + +template <typename Pointer> +constexpr bool operator==(std::nullptr_t, propagate_const<Pointer> const& a) { + return nullptr == get_underlying(a); +} + +template <typename Pointer> +constexpr bool operator!=(propagate_const<Pointer> const& a, std::nullptr_t) { + return get_underlying(a) != nullptr; +} + +template <typename Pointer> +constexpr bool operator!=(std::nullptr_t, propagate_const<Pointer> const& a) { + return nullptr != get_underlying(a); +} + +template <typename Pointer> +constexpr bool operator==( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) == get_underlying(b); +} + +template <typename Pointer> +constexpr bool operator!=( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) != get_underlying(b); +} + +template <typename Pointer> +constexpr bool operator<( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) < get_underlying(b); +} + +template <typename Pointer> +constexpr bool operator<=( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) <= get_underlying(b); +} + +template <typename Pointer> +constexpr bool operator>( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) > get_underlying(b); +} + +template <typename Pointer> +constexpr bool operator>=( + propagate_const<Pointer> const& a, + propagate_const<Pointer> const& b) { + return get_underlying(a) >= get_underlying(b); +} + +// Note: contrary to the specification, the heterogeneous comparison operators +// only participate in overload resolution when the equivalent heterogeneous +// comparison operators on the underlying pointers, as returned by invocation +// of get_underlying, would also participate in overload resolution. + +template <typename Pointer, typename Other> +constexpr auto operator==(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) == b, false) { + return get_underlying(a) == b; +} + +template <typename Pointer, typename Other> +constexpr auto operator!=(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) != b, false) { + return get_underlying(a) != b; +} + +template <typename Pointer, typename Other> +constexpr auto operator<(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) < b, false) { + return get_underlying(a) < b; +} + +template <typename Pointer, typename Other> +constexpr auto operator<=(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) <= b, false) { + return get_underlying(a) <= b; +} + +template <typename Pointer, typename Other> +constexpr auto operator>(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) > b, false) { + return get_underlying(a) > b; +} + +template <typename Pointer, typename Other> +constexpr auto operator>=(propagate_const<Pointer> const& a, Other const& b) + -> decltype(get_underlying(a) >= b, false) { + return get_underlying(a) >= b; +} + +template <typename Other, typename Pointer> +constexpr auto operator==(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a == get_underlying(b), false) { + return a == get_underlying(b); +} + +template <typename Other, typename Pointer> +constexpr auto operator!=(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a != get_underlying(b), false) { + return a != get_underlying(b); +} + +template <typename Other, typename Pointer> +constexpr auto operator<(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a < get_underlying(b), false) { + return a < get_underlying(b); +} + +template <typename Other, typename Pointer> +constexpr auto operator<=(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a <= get_underlying(b), false) { + return a <= get_underlying(b); +} + +template <typename Other, typename Pointer> +constexpr auto operator>(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a > get_underlying(b), false) { + return a > get_underlying(b); +} + +template <typename Other, typename Pointer> +constexpr auto operator>=(Other const& a, propagate_const<Pointer> const& b) + -> decltype(a >= get_underlying(b), false) { + return a >= get_underlying(b); +} + +} // namespace folly + +namespace std { + +template <typename Pointer> +struct hash<folly::propagate_const<Pointer>> : private hash<Pointer> { + using hash<Pointer>::hash; + + size_t operator()(folly::propagate_const<Pointer> const& obj) const { + return hash<Pointer>::operator()(folly::get_underlying(obj)); + } +}; + +template <typename Pointer> +struct equal_to<folly::propagate_const<Pointer>> : private equal_to<Pointer> { + using equal_to<Pointer>::equal_to; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return equal_to<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template <typename Pointer> +struct not_equal_to<folly::propagate_const<Pointer>> + : private not_equal_to<Pointer> { + using not_equal_to<Pointer>::not_equal_to; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return not_equal_to<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template <typename Pointer> +struct less<folly::propagate_const<Pointer>> : private less<Pointer> { + using less<Pointer>::less; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return less<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template <typename Pointer> +struct greater<folly::propagate_const<Pointer>> : private greater<Pointer> { + using greater<Pointer>::greater; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return greater<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template <typename Pointer> +struct less_equal<folly::propagate_const<Pointer>> + : private less_equal<Pointer> { + using less_equal<Pointer>::less_equal; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return less_equal<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template <typename Pointer> +struct greater_equal<folly::propagate_const<Pointer>> + : private greater_equal<Pointer> { + using greater_equal<Pointer>::greater_equal; + + constexpr bool operator()( + folly::propagate_const<Pointer> const& a, + folly::propagate_const<Pointer> const& b) { + return greater_equal<Pointer>::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/lang/RValueReferenceWrapper.h b/ios/Pods/Flipper-Folly/folly/lang/RValueReferenceWrapper.h new file mode 100644 index 000000000..33fbb9b98 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/RValueReferenceWrapper.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> +#include <memory> +#include <utility> + +namespace folly { + +/** + * Class template that wraps a reference to an rvalue. Similar to + * std::reference_wrapper but with three important differences: + * + * 1) folly::rvalue_reference_wrappers can only be moved, not copied; + * 2) the get() function and the conversion-to-T operator are destructive and + * not const, they invalidate the wrapper; + * 3) the constructor-from-T is explicit. + * + * These restrictions are designed to make it harder to accidentally create a + * a dangling rvalue reference, or to use an rvalue reference multiple times. + * (Using an rvalue reference typically implies invalidation of the target + * object, such as move-assignment to another object.) + * + * @seealso folly::rref + */ +template <class T> +class rvalue_reference_wrapper { + public: + using type = T; + + /** + * Default constructor. Creates an invalid reference. Must be move-assigned + * to in order to be come valid. + */ + rvalue_reference_wrapper() noexcept : ptr_(nullptr) {} + + /** + * Explicit constructor to make it harder to accidentally create a dangling + * reference to a temporary. + */ + explicit rvalue_reference_wrapper(T&& ref) noexcept + : ptr_(std::addressof(ref)) {} + + /** + * No construction from lvalue reference. Use std::move. + */ + explicit rvalue_reference_wrapper(T&) noexcept = delete; + + /** + * Destructive move construction. + */ + rvalue_reference_wrapper(rvalue_reference_wrapper<T>&& other) noexcept + : ptr_(other.ptr_) { + other.ptr_ = nullptr; + } + + /** + * Destructive move assignment. + */ + rvalue_reference_wrapper& operator=( + rvalue_reference_wrapper&& other) noexcept { + ptr_ = other.ptr_; + other.ptr_ = nullptr; + return *this; + } + + /** + * Implicit conversion to raw reference. Destructive. + */ + /* implicit */ operator T &&() && noexcept { + return static_cast<rvalue_reference_wrapper&&>(*this).get(); + } + + /** + * Explicit unwrap. Destructive. + */ + T&& get() && noexcept { + assert(valid()); + T& ref = *ptr_; + ptr_ = nullptr; + return static_cast<T&&>(ref); + } + + /** + * Calls the callable object to whom reference is stored. Only available if + * the wrapped reference points to a callable object. Destructive. + */ + template <class... Args> + decltype(auto) operator()(Args&&... args) && + noexcept(noexcept(std::declval<T>()(std::forward<Args>(args)...))) { + return static_cast<rvalue_reference_wrapper&&>(*this).get()( + std::forward<Args>(args)...); + } + + /** + * Check whether wrapped reference is valid. + */ + bool valid() const noexcept { + return ptr_ != nullptr; + } + + private: + // Disallow copy construction and copy assignment, to make it harder to + // accidentally use an rvalue reference multiple times. + rvalue_reference_wrapper(const rvalue_reference_wrapper&) = delete; + rvalue_reference_wrapper& operator=(const rvalue_reference_wrapper&) = delete; + + T* ptr_; +}; + +/** + * Create a folly::rvalue_reference_wrapper. Analogous to std::ref(). + * + * Warning: folly::rvalue_reference_wrappers are potentially dangerous, because + * they can easily be used to capture references to temporary values. Users must + * ensure that the target object outlives the reference wrapper. + * + * @example + * class Object {}; + * void f(Object&&); + * // BAD + * void g() { + * auto ref = folly::rref(Object{}); // create reference to temporary + * f(std::move(ref)); // pass dangling reference + * } + * // GOOD + * void h() { + * Object o; + * auto ref = folly::rref(std::move(o)); + * f(std::move(ref)); + * } + */ +template <typename T> +rvalue_reference_wrapper<T> rref(T&& value) noexcept { + return rvalue_reference_wrapper<T>(std::move(value)); +} +template <typename T> +rvalue_reference_wrapper<T> rref(T&) noexcept = delete; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.cpp b/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.cpp new file mode 100644 index 000000000..edc1f50ce --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/lang/SafeAssert.h> + +#include <algorithm> + +#include <folly/Conv.h> +#include <folly/FileUtil.h> + +namespace folly { +namespace detail { + +namespace { + +// script (centos): +// +// for e in $( +// cat /usr/include/asm*/errno*.h | awk '{print $2}' | grep -P '^E' | sort +// ) ; do +// echo "#if defined($e)" +// echo " FOLLY_DETAIL_ERROR($e)," +// echo "#endif" +// done + +#define FOLLY_DETAIL_ERROR(name) \ + { name, #name } +constexpr std::pair<int, const char*> errors[] = { +#if defined(E2BIG) + FOLLY_DETAIL_ERROR(E2BIG), +#endif +#if defined(EACCES) + FOLLY_DETAIL_ERROR(EACCES), +#endif +#if defined(EADDRINUSE) + FOLLY_DETAIL_ERROR(EADDRINUSE), +#endif +#if defined(EADDRNOTAVAIL) + FOLLY_DETAIL_ERROR(EADDRNOTAVAIL), +#endif +#if defined(EADV) + FOLLY_DETAIL_ERROR(EADV), +#endif +#if defined(EAFNOSUPPORT) + FOLLY_DETAIL_ERROR(EAFNOSUPPORT), +#endif +#if defined(EAGAIN) + FOLLY_DETAIL_ERROR(EAGAIN), +#endif +#if defined(EALREADY) + FOLLY_DETAIL_ERROR(EALREADY), +#endif +#if defined(EBADE) + FOLLY_DETAIL_ERROR(EBADE), +#endif +#if defined(EBADF) + FOLLY_DETAIL_ERROR(EBADF), +#endif +#if defined(EBADFD) + FOLLY_DETAIL_ERROR(EBADFD), +#endif +#if defined(EBADMSG) + FOLLY_DETAIL_ERROR(EBADMSG), +#endif +#if defined(EBADR) + FOLLY_DETAIL_ERROR(EBADR), +#endif +#if defined(EBADRQC) + FOLLY_DETAIL_ERROR(EBADRQC), +#endif +#if defined(EBADSLT) + FOLLY_DETAIL_ERROR(EBADSLT), +#endif +#if defined(EBFONT) + FOLLY_DETAIL_ERROR(EBFONT), +#endif +#if defined(EBUSY) + FOLLY_DETAIL_ERROR(EBUSY), +#endif +#if defined(ECANCELED) + FOLLY_DETAIL_ERROR(ECANCELED), +#endif +#if defined(ECHILD) + FOLLY_DETAIL_ERROR(ECHILD), +#endif +#if defined(ECHRNG) + FOLLY_DETAIL_ERROR(ECHRNG), +#endif +#if defined(ECOMM) + FOLLY_DETAIL_ERROR(ECOMM), +#endif +#if defined(ECONNABORTED) + FOLLY_DETAIL_ERROR(ECONNABORTED), +#endif +#if defined(ECONNREFUSED) + FOLLY_DETAIL_ERROR(ECONNREFUSED), +#endif +#if defined(ECONNRESET) + FOLLY_DETAIL_ERROR(ECONNRESET), +#endif +#if defined(EDEADLK) + FOLLY_DETAIL_ERROR(EDEADLK), +#endif +#if defined(EDEADLOCK) + FOLLY_DETAIL_ERROR(EDEADLOCK), +#endif +#if defined(EDESTADDRREQ) + FOLLY_DETAIL_ERROR(EDESTADDRREQ), +#endif +#if defined(EDOM) + FOLLY_DETAIL_ERROR(EDOM), +#endif +#if defined(EDOTDOT) + FOLLY_DETAIL_ERROR(EDOTDOT), +#endif +#if defined(EDQUOT) + FOLLY_DETAIL_ERROR(EDQUOT), +#endif +#if defined(EEXIST) + FOLLY_DETAIL_ERROR(EEXIST), +#endif +#if defined(EFAULT) + FOLLY_DETAIL_ERROR(EFAULT), +#endif +#if defined(EFBIG) + FOLLY_DETAIL_ERROR(EFBIG), +#endif +#if defined(EHOSTDOWN) + FOLLY_DETAIL_ERROR(EHOSTDOWN), +#endif +#if defined(EHOSTUNREACH) + FOLLY_DETAIL_ERROR(EHOSTUNREACH), +#endif +#if defined(EHWPOISON) + FOLLY_DETAIL_ERROR(EHWPOISON), +#endif +#if defined(EIDRM) + FOLLY_DETAIL_ERROR(EIDRM), +#endif +#if defined(EILSEQ) + FOLLY_DETAIL_ERROR(EILSEQ), +#endif +#if defined(EINPROGRESS) + FOLLY_DETAIL_ERROR(EINPROGRESS), +#endif +#if defined(EINTR) + FOLLY_DETAIL_ERROR(EINTR), +#endif +#if defined(EINVAL) + FOLLY_DETAIL_ERROR(EINVAL), +#endif +#if defined(EIO) + FOLLY_DETAIL_ERROR(EIO), +#endif +#if defined(EISCONN) + FOLLY_DETAIL_ERROR(EISCONN), +#endif +#if defined(EISDIR) + FOLLY_DETAIL_ERROR(EISDIR), +#endif +#if defined(EISNAM) + FOLLY_DETAIL_ERROR(EISNAM), +#endif +#if defined(EKEYEXPIRED) + FOLLY_DETAIL_ERROR(EKEYEXPIRED), +#endif +#if defined(EKEYREJECTED) + FOLLY_DETAIL_ERROR(EKEYREJECTED), +#endif +#if defined(EKEYREVOKED) + FOLLY_DETAIL_ERROR(EKEYREVOKED), +#endif +#if defined(EL2HLT) + FOLLY_DETAIL_ERROR(EL2HLT), +#endif +#if defined(EL2NSYNC) + FOLLY_DETAIL_ERROR(EL2NSYNC), +#endif +#if defined(EL3HLT) + FOLLY_DETAIL_ERROR(EL3HLT), +#endif +#if defined(EL3RST) + FOLLY_DETAIL_ERROR(EL3RST), +#endif +#if defined(ELIBACC) + FOLLY_DETAIL_ERROR(ELIBACC), +#endif +#if defined(ELIBBAD) + FOLLY_DETAIL_ERROR(ELIBBAD), +#endif +#if defined(ELIBEXEC) + FOLLY_DETAIL_ERROR(ELIBEXEC), +#endif +#if defined(ELIBMAX) + FOLLY_DETAIL_ERROR(ELIBMAX), +#endif +#if defined(ELIBSCN) + FOLLY_DETAIL_ERROR(ELIBSCN), +#endif +#if defined(ELNRNG) + FOLLY_DETAIL_ERROR(ELNRNG), +#endif +#if defined(ELOOP) + FOLLY_DETAIL_ERROR(ELOOP), +#endif +#if defined(EMEDIUMTYPE) + FOLLY_DETAIL_ERROR(EMEDIUMTYPE), +#endif +#if defined(EMFILE) + FOLLY_DETAIL_ERROR(EMFILE), +#endif +#if defined(EMLINK) + FOLLY_DETAIL_ERROR(EMLINK), +#endif +#if defined(EMSGSIZE) + FOLLY_DETAIL_ERROR(EMSGSIZE), +#endif +#if defined(EMULTIHOP) + FOLLY_DETAIL_ERROR(EMULTIHOP), +#endif +#if defined(ENAMETOOLONG) + FOLLY_DETAIL_ERROR(ENAMETOOLONG), +#endif +#if defined(ENAVAIL) + FOLLY_DETAIL_ERROR(ENAVAIL), +#endif +#if defined(ENETDOWN) + FOLLY_DETAIL_ERROR(ENETDOWN), +#endif +#if defined(ENETRESET) + FOLLY_DETAIL_ERROR(ENETRESET), +#endif +#if defined(ENETUNREACH) + FOLLY_DETAIL_ERROR(ENETUNREACH), +#endif +#if defined(ENFILE) + FOLLY_DETAIL_ERROR(ENFILE), +#endif +#if defined(ENOANO) + FOLLY_DETAIL_ERROR(ENOANO), +#endif +#if defined(ENOBUFS) + FOLLY_DETAIL_ERROR(ENOBUFS), +#endif +#if defined(ENOCSI) + FOLLY_DETAIL_ERROR(ENOCSI), +#endif +#if defined(ENODATA) + FOLLY_DETAIL_ERROR(ENODATA), +#endif +#if defined(ENODEV) + FOLLY_DETAIL_ERROR(ENODEV), +#endif +#if defined(ENOENT) + FOLLY_DETAIL_ERROR(ENOENT), +#endif +#if defined(ENOEXEC) + FOLLY_DETAIL_ERROR(ENOEXEC), +#endif +#if defined(ENOKEY) + FOLLY_DETAIL_ERROR(ENOKEY), +#endif +#if defined(ENOLCK) + FOLLY_DETAIL_ERROR(ENOLCK), +#endif +#if defined(ENOLINK) + FOLLY_DETAIL_ERROR(ENOLINK), +#endif +#if defined(ENOMEDIUM) + FOLLY_DETAIL_ERROR(ENOMEDIUM), +#endif +#if defined(ENOMEM) + FOLLY_DETAIL_ERROR(ENOMEM), +#endif +#if defined(ENOMSG) + FOLLY_DETAIL_ERROR(ENOMSG), +#endif +#if defined(ENONET) + FOLLY_DETAIL_ERROR(ENONET), +#endif +#if defined(ENOPKG) + FOLLY_DETAIL_ERROR(ENOPKG), +#endif +#if defined(ENOPROTOOPT) + FOLLY_DETAIL_ERROR(ENOPROTOOPT), +#endif +#if defined(ENOSPC) + FOLLY_DETAIL_ERROR(ENOSPC), +#endif +#if defined(ENOSR) + FOLLY_DETAIL_ERROR(ENOSR), +#endif +#if defined(ENOSTR) + FOLLY_DETAIL_ERROR(ENOSTR), +#endif +#if defined(ENOSYS) + FOLLY_DETAIL_ERROR(ENOSYS), +#endif +#if defined(ENOTBLK) + FOLLY_DETAIL_ERROR(ENOTBLK), +#endif +#if defined(ENOTCONN) + FOLLY_DETAIL_ERROR(ENOTCONN), +#endif +#if defined(ENOTDIR) + FOLLY_DETAIL_ERROR(ENOTDIR), +#endif +#if defined(ENOTEMPTY) + FOLLY_DETAIL_ERROR(ENOTEMPTY), +#endif +#if defined(ENOTNAM) + FOLLY_DETAIL_ERROR(ENOTNAM), +#endif +#if defined(ENOTRECOVERABLE) + FOLLY_DETAIL_ERROR(ENOTRECOVERABLE), +#endif +#if defined(ENOTSOCK) + FOLLY_DETAIL_ERROR(ENOTSOCK), +#endif +#if defined(ENOTTY) + FOLLY_DETAIL_ERROR(ENOTTY), +#endif +#if defined(ENOTUNIQ) + FOLLY_DETAIL_ERROR(ENOTUNIQ), +#endif +#if defined(ENXIO) + FOLLY_DETAIL_ERROR(ENXIO), +#endif +#if defined(EOPNOTSUPP) + FOLLY_DETAIL_ERROR(EOPNOTSUPP), +#endif +#if defined(EOVERFLOW) + FOLLY_DETAIL_ERROR(EOVERFLOW), +#endif +#if defined(EOWNERDEAD) + FOLLY_DETAIL_ERROR(EOWNERDEAD), +#endif +#if defined(EPERM) + FOLLY_DETAIL_ERROR(EPERM), +#endif +#if defined(EPFNOSUPPORT) + FOLLY_DETAIL_ERROR(EPFNOSUPPORT), +#endif +#if defined(EPIPE) + FOLLY_DETAIL_ERROR(EPIPE), +#endif +#if defined(EPROTO) + FOLLY_DETAIL_ERROR(EPROTO), +#endif +#if defined(EPROTONOSUPPORT) + FOLLY_DETAIL_ERROR(EPROTONOSUPPORT), +#endif +#if defined(EPROTOTYPE) + FOLLY_DETAIL_ERROR(EPROTOTYPE), +#endif +#if defined(ERANGE) + FOLLY_DETAIL_ERROR(ERANGE), +#endif +#if defined(EREMCHG) + FOLLY_DETAIL_ERROR(EREMCHG), +#endif +#if defined(EREMOTE) + FOLLY_DETAIL_ERROR(EREMOTE), +#endif +#if defined(EREMOTEIO) + FOLLY_DETAIL_ERROR(EREMOTEIO), +#endif +#if defined(ERESTART) + FOLLY_DETAIL_ERROR(ERESTART), +#endif +#if defined(ERFKILL) + FOLLY_DETAIL_ERROR(ERFKILL), +#endif +#if defined(EROFS) + FOLLY_DETAIL_ERROR(EROFS), +#endif +#if defined(ESHUTDOWN) + FOLLY_DETAIL_ERROR(ESHUTDOWN), +#endif +#if defined(ESOCKTNOSUPPORT) + FOLLY_DETAIL_ERROR(ESOCKTNOSUPPORT), +#endif +#if defined(ESPIPE) + FOLLY_DETAIL_ERROR(ESPIPE), +#endif +#if defined(ESRCH) + FOLLY_DETAIL_ERROR(ESRCH), +#endif +#if defined(ESRMNT) + FOLLY_DETAIL_ERROR(ESRMNT), +#endif +#if defined(ESTALE) + FOLLY_DETAIL_ERROR(ESTALE), +#endif +#if defined(ESTRPIPE) + FOLLY_DETAIL_ERROR(ESTRPIPE), +#endif +#if defined(ETIME) + FOLLY_DETAIL_ERROR(ETIME), +#endif +#if defined(ETIMEDOUT) + FOLLY_DETAIL_ERROR(ETIMEDOUT), +#endif +#if defined(ETOOMANYREFS) + FOLLY_DETAIL_ERROR(ETOOMANYREFS), +#endif +#if defined(ETXTBSY) + FOLLY_DETAIL_ERROR(ETXTBSY), +#endif +#if defined(EUCLEAN) + FOLLY_DETAIL_ERROR(EUCLEAN), +#endif +#if defined(EUNATCH) + FOLLY_DETAIL_ERROR(EUNATCH), +#endif +#if defined(EUSERS) + FOLLY_DETAIL_ERROR(EUSERS), +#endif +#if defined(EWOULDBLOCK) + FOLLY_DETAIL_ERROR(EWOULDBLOCK), +#endif +#if defined(EXDEV) + FOLLY_DETAIL_ERROR(EXDEV), +#endif +#if defined(EXFULL) + FOLLY_DETAIL_ERROR(EXFULL), +#endif +}; +#undef FOLLY_DETAIL_ERROR + +void writeStderr(const char* s, size_t len) { + writeFull(STDERR_FILENO, s, len); +} +void writeStderr(const char* s) { + writeStderr(s, strlen(s)); +} + +} // namespace + +void assertionFailure( + const char* expr, + const char* msg, + const char* file, + unsigned int line, + const char* function, + int error) { + writeStderr("\n\nAssertion failure: "); + writeStderr(expr + 1, strlen(expr) - 2); + writeStderr("\nMessage: "); + writeStderr(msg); + writeStderr("\nFile: "); + writeStderr(file); + writeStderr("\nLine: "); + char buf[20]; + uint32_t n = uint64ToBufferUnsafe(line, buf); + writeFull(STDERR_FILENO, buf, n); + writeStderr("\nFunction: "); + writeStderr(function); + if (error) { + // if errno is set, print the number and the symbolic constant + // the symbolic constant is necessary since actual numbers may vary + // for simplicity, do not attempt to mimic strerror printing descriptions + writeStderr("\nError: "); + n = uint64ToBufferUnsafe(error, buf); + writeFull(STDERR_FILENO, buf, n); + writeStderr(" ("); + // the list is not required to be sorted; but the program is about to die + auto const pred = [=](auto const e) { return e.first == error; }; + auto const it = std::find_if(std::begin(errors), std::end(errors), pred); + writeStderr(it != std::end(errors) ? it->second : "<unknown>"); + writeStderr(")"); + } + writeStderr("\n"); + fsyncNoInt(STDERR_FILENO); + abort(); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.h b/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.h new file mode 100644 index 000000000..ee1859983 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/SafeAssert.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cerrno> + +#include <folly/Portability.h> +#include <folly/Preprocessor.h> + +#define FOLLY_SAFE_CHECK_IMPL(expr, expr_s, msg, error) \ + ((expr) ? static_cast<void>(0) \ + : ::folly::detail::assertionFailure( \ + FOLLY_PP_STRINGIZE(expr_s), \ + (msg), \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, \ + error)) + +/** + * Verify that the expression is true. If not, prints an error message + * (containing msg) to stderr and abort()s. Just like CHECK(), but only + * logs to stderr and only does async-signal-safe calls. + */ +#define FOLLY_SAFE_CHECK(expr, msg) \ + FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), 0) + +/** + * In debug mode, verify that the expression is true. Otherwise, do nothing + * (do not even evaluate expr). Just like DCHECK(), but only logs to stderr and + * only does async-signal-safe calls. + */ +#define FOLLY_SAFE_DCHECK(expr, msg) \ + FOLLY_SAFE_CHECK_IMPL(!::folly::kIsDebug || (expr), (expr), (msg), 0) + +/** + * Like FOLLY_SAFE_CHECK, but also prints errno. + */ +#define FOLLY_SAFE_PCHECK(expr, msg) \ + FOLLY_SAFE_CHECK_IMPL((expr), (expr), (msg), errno) + +namespace folly { +namespace detail { + +[[noreturn]] void assertionFailure( + const char* expr, + const char* msg, + const char* file, + unsigned int line, + const char* function, + int error); +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/StaticConst.h b/ios/Pods/Flipper-Folly/folly/lang/StaticConst.h new file mode 100644 index 000000000..9479dfe2b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/StaticConst.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +// StaticConst +// +// A template for defining ODR-usable constexpr instances. Safe from ODR +// violations and initialization-order problems. + +template <typename T> +struct StaticConst { + static constexpr T value{}; +}; + +template <typename T> +constexpr T StaticConst<T>::value; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/TypeInfo.h b/ios/Pods/Flipper-Folly/folly/lang/TypeInfo.h new file mode 100644 index 000000000..daf7c47f6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/TypeInfo.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <typeinfo> + +#include <folly/Portability.h> + +// FOLLY_TYPE_INFO_OF +// +// Returns &typeid(...) if RTTI is available, nullptr otherwise. In either +// case, has type std::type_info const*. +#if FOLLY_HAS_RTTI +#define FOLLY_TYPE_INFO_OF(...) (&typeid(__VA_ARGS__)) +#else +#define FOLLY_TYPE_INFO_OF(...) (static_cast<std::type_info const*>(nullptr)) +#endif + +namespace folly { + +// type_info_of +// +// Returns &typeid(T) if RTTI is available, nullptr otherwise. +// +// This overload works on the static type of the template parameter. +template <typename T> +FOLLY_ALWAYS_INLINE static std::type_info const* type_info_of() { + return FOLLY_TYPE_INFO_OF(T); +} + +// type_info_of +// +// Returns &typeid(t) if RTTI is available, nullptr otherwise. +// +// This overload works on the dynamic type of the non-template parameter. +template <typename T> +FOLLY_ALWAYS_INLINE static std::type_info const* type_info_of(T const& t) { + return FOLLY_TYPE_INFO_OF(t); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/lang/UncaughtExceptions.h b/ios/Pods/Flipper-Folly/folly/lang/UncaughtExceptions.h new file mode 100644 index 000000000..a00c2575f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/lang/UncaughtExceptions.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <exception> + +#if !defined(FOLLY_FORCE_EXCEPTION_COUNT_USE_STD) && defined(__GNUG__) +#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS +namespace __cxxabiv1 { +// forward declaration (originally defined in unwind-cxx.h from from libstdc++) +struct __cxa_eh_globals; +// declared in cxxabi.h from libstdc++-v3 +#if !defined(__FreeBSD__) +extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; +#else +// Signature mismatch with FreeBSD case +extern "C" __cxa_eh_globals* __cxa_get_globals(void); +#endif +} // namespace __cxxabiv1 +#elif defined(FOLLY_FORCE_EXCEPTION_COUNT_USE_STD) || defined(_MSC_VER) +#define FOLLY_EXCEPTION_COUNT_USE_STD +#else +// Raise an error when trying to use this on unsupported platforms. +#error "Unsupported platform, don't include this header." +#endif + +namespace folly { + +/** + * Returns the number of uncaught exceptions. + * + * This function is based on Evgeny Panasyuk's implementation from here: + * http://fburl.com/15190026 + */ +inline int uncaught_exceptions() noexcept { +#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) + // __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h). + // The offset below returns __cxa_eh_globals::uncaughtExceptions. + return *(reinterpret_cast<unsigned int*>( + static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + + sizeof(void*))); +#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) + // _getptd() returns a _tiddata* (defined in mtdll.h). + // The offset below returns _tiddata::_ProcessingThrow. + return *(reinterpret_cast<int*>( + static_cast<char*>(static_cast<void*>(_getptd())) + sizeof(void*) * 28 + + 0x4 * 8)); +#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD) + return std::uncaught_exceptions(); +#endif +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/Arena-inl.h b/ios/Pods/Flipper-Folly/folly/memory/Arena-inl.h new file mode 100644 index 000000000..06cccdecc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/Arena-inl.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_ARENA_H_ +#error This file may only be included from Arena.h +#endif + +// Implementation of Arena.h functions + +namespace folly { + +template <class Alloc> +std::pair<typename Arena<Alloc>::Block*, size_t> +Arena<Alloc>::Block::allocate(Alloc& alloc, size_t size, bool allowSlack) { + size_t allocSize = sizeof(Block) + size; + if (allowSlack) { + allocSize = ArenaAllocatorTraits<Alloc>::goodSize(alloc, allocSize); + } + + void* mem = std::allocator_traits<Alloc>::allocate(alloc, allocSize); + return std::make_pair(new (mem) Block(), allocSize - sizeof(Block)); +} + +template <class Alloc> +void Arena<Alloc>::Block::deallocate(Alloc& alloc) { + this->~Block(); + std::allocator_traits<Alloc>::deallocate(alloc, this, 1); +} + +template <class Alloc> +void* Arena<Alloc>::allocateSlow(size_t size) { + std::pair<Block*, size_t> p; + char* start; + + size_t allocSize; + if (!checked_add(&allocSize, std::max(size, minBlockSize()), sizeof(Block))) { + throw_exception<std::bad_alloc>(); + } + if (sizeLimit_ != kNoSizeLimit && + allocSize > sizeLimit_ - totalAllocatedSize_) { + throw_exception(std::bad_alloc()); + } + + if (size > minBlockSize()) { + // Allocate a large block for this chunk only, put it at the back of the + // list so it doesn't get used for small allocations; don't change ptr_ + // and end_, let them point into a normal block (or none, if they're + // null) + p = Block::allocate(alloc(), size, false); + start = p.first->start(); + blocks_.push_back(*p.first); + } else { + // Allocate a normal sized block and carve out size bytes from it + p = Block::allocate(alloc(), minBlockSize(), true); + start = p.first->start(); + blocks_.push_front(*p.first); + ptr_ = start + size; + end_ = start + p.second; + } + + assert(p.second >= size); + totalAllocatedSize_ += p.second + sizeof(Block); + return start; +} + +template <class Alloc> +void Arena<Alloc>::merge(Arena<Alloc>&& other) { + blocks_.splice_after(blocks_.before_begin(), other.blocks_); + other.blocks_.clear(); + other.ptr_ = other.end_ = nullptr; + totalAllocatedSize_ += other.totalAllocatedSize_; + other.totalAllocatedSize_ = 0; +} + +template <class Alloc> +Arena<Alloc>::~Arena() { + auto disposer = [this](Block* b) { b->deallocate(this->alloc()); }; + while (!blocks_.empty()) { + blocks_.pop_front_and_dispose(disposer); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/Arena.h b/ios/Pods/Flipper-Folly/folly/memory/Arena.h new file mode 100644 index 000000000..1eb830a39 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/Arena.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#define FOLLY_ARENA_H_ + +#include <cassert> +#include <limits> +#include <stdexcept> +#include <utility> + +#include <boost/intrusive/slist.hpp> + +#include <folly/Conv.h> +#include <folly/Likely.h> +#include <folly/Memory.h> +#include <folly/lang/Align.h> +#include <folly/lang/CheckedMath.h> +#include <folly/lang/Exception.h> +#include <folly/memory/Malloc.h> + +namespace folly { + +/** + * Simple arena: allocate memory which gets freed when the arena gets + * destroyed. + * + * The arena itself allocates memory using a custom allocator which conforms + * to the C++ concept Allocator. + * + * http://en.cppreference.com/w/cpp/concept/Allocator + * + * You may also specialize ArenaAllocatorTraits for your allocator type to + * provide: + * + * size_t goodSize(const Allocator& alloc, size_t size) const; + * Return a size (>= the provided size) that is considered "good" for your + * allocator (for example, if your allocator allocates memory in 4MB + * chunks, size should be rounded up to 4MB). The provided value is + * guaranteed to be rounded up to a multiple of the maximum alignment + * required on your system; the returned value must be also. + * + * An implementation that uses malloc() / free() is defined below, see SysArena. + */ +template <class Alloc> +struct ArenaAllocatorTraits; +template <class Alloc> +class Arena { + public: + explicit Arena( + const Alloc& alloc, + size_t minBlockSize = kDefaultMinBlockSize, + size_t sizeLimit = kNoSizeLimit, + size_t maxAlign = kDefaultMaxAlign) + : allocAndSize_(alloc, minBlockSize), + ptr_(nullptr), + end_(nullptr), + totalAllocatedSize_(0), + bytesUsed_(0), + sizeLimit_(sizeLimit), + maxAlign_(maxAlign) { + if ((maxAlign_ & (maxAlign_ - 1)) || maxAlign_ > alignof(Block)) { + throw_exception(std::invalid_argument( + folly::to<std::string>("Invalid maxAlign: ", maxAlign_))); + } + } + + ~Arena(); + + void* allocate(size_t size) { + size = roundUp(size); + bytesUsed_ += size; + + assert(ptr_ <= end_); + if (LIKELY((size_t)(end_ - ptr_) >= size)) { + // Fast path: there's enough room in the current block + char* r = ptr_; + ptr_ += size; + assert(isAligned(r)); + return r; + } + + // Not enough room in the current block + void* r = allocateSlow(size); + assert(isAligned(r)); + return r; + } + + void deallocate(void* /* p */, size_t = 0) { + // Deallocate? Never! + } + + // Transfer ownership of all memory allocated from "other" to "this". + void merge(Arena&& other); + + // Gets the total memory used by the arena + size_t totalSize() const { + return totalAllocatedSize_ + sizeof(Arena); + } + + // Gets the total number of "used" bytes, i.e. bytes that the arena users + // allocated via the calls to `allocate`. Doesn't include fragmentation, e.g. + // if block size is 4KB and you allocate 2 objects of 3KB in size, + // `bytesUsed()` will be 6KB, while `totalSize()` will be 8KB+. + size_t bytesUsed() const { + return bytesUsed_; + } + + // not copyable or movable + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + Arena(Arena&&) = delete; + Arena& operator=(Arena&&) = delete; + + private: + struct Block; + typedef boost::intrusive::slist_member_hook<boost::intrusive::tag<Arena>> + BlockLink; + + struct alignas(max_align_v) Block { + BlockLink link; + + // Allocate a block with at least size bytes of storage. + // If allowSlack is true, allocate more than size bytes if convenient + // (via ArenaAllocatorTraits::goodSize()) as we'll try to pack small + // allocations in this block. + static std::pair<Block*, size_t> + allocate(Alloc& alloc, size_t size, bool allowSlack); + void deallocate(Alloc& alloc); + + char* start() { + return reinterpret_cast<char*>(this + 1); + } + + private: + Block() = default; + ~Block() = default; + }; + + public: + static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof(Block); + static constexpr size_t kNoSizeLimit = 0; + static constexpr size_t kDefaultMaxAlign = alignof(Block); + static constexpr size_t kBlockOverhead = sizeof(Block); + + private: + bool isAligned(uintptr_t address) const { + return (address & (maxAlign_ - 1)) == 0; + } + bool isAligned(void* p) const { + return isAligned(reinterpret_cast<uintptr_t>(p)); + } + + // Round up size so it's properly aligned + size_t roundUp(size_t size) const { + auto maxAl = maxAlign_ - 1; + size_t realSize; + if (!checked_add<size_t>(&realSize, size, maxAl)) { + throw_exception<std::bad_alloc>(); + } + return realSize & ~maxAl; + } + + // cache_last<true> makes the list keep a pointer to the last element, so we + // have push_back() and constant time splice_after() + typedef boost::intrusive::slist< + Block, + boost::intrusive::member_hook<Block, BlockLink, &Block::link>, + boost::intrusive::constant_time_size<false>, + boost::intrusive::cache_last<true>> + BlockList; + + void* allocateSlow(size_t size); + + // Empty member optimization: package Alloc with a non-empty member + // in case Alloc is empty (as it is in the case of SysAllocator). + struct AllocAndSize : public Alloc { + explicit AllocAndSize(const Alloc& a, size_t s) + : Alloc(a), minBlockSize(s) {} + + size_t minBlockSize; + }; + + size_t minBlockSize() const { + return allocAndSize_.minBlockSize; + } + Alloc& alloc() { + return allocAndSize_; + } + const Alloc& alloc() const { + return allocAndSize_; + } + + AllocAndSize allocAndSize_; + BlockList blocks_; + char* ptr_; + char* end_; + size_t totalAllocatedSize_; + size_t bytesUsed_; + const size_t sizeLimit_; + const size_t maxAlign_; +}; + +template <class Alloc> +struct AllocatorHasTrivialDeallocate<Arena<Alloc>> : std::true_type {}; + +/** + * By default, don't pad the given size. + */ +template <class Alloc> +struct ArenaAllocatorTraits { + static size_t goodSize(const Alloc& /* alloc */, size_t size) { + return size; + } +}; + +template <> +struct ArenaAllocatorTraits<SysAllocator<void>> { + static size_t goodSize(const SysAllocator<void>& /* alloc */, size_t size) { + return goodMallocSize(size); + } +}; + +/** + * Arena that uses the system allocator (malloc / free) + */ +class SysArena : public Arena<SysAllocator<void>> { + public: + explicit SysArena( + size_t minBlockSize = kDefaultMinBlockSize, + size_t sizeLimit = kNoSizeLimit, + size_t maxAlign = kDefaultMaxAlign) + : Arena<SysAllocator<void>>({}, minBlockSize, sizeLimit, maxAlign) {} +}; + +template <> +struct AllocatorHasTrivialDeallocate<SysArena> : std::true_type {}; + +template <typename T, typename Alloc> +using ArenaAllocator = CxxAllocatorAdaptor<T, Arena<Alloc>>; + +template <typename T> +using SysArenaAllocator = ArenaAllocator<T, SysAllocator<void>>; + +} // namespace folly + +#include <folly/memory/Arena-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/memory/EnableSharedFromThis.h b/ios/Pods/Flipper-Folly/folly/memory/EnableSharedFromThis.h new file mode 100644 index 000000000..c8db6860a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/EnableSharedFromThis.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <memory> + +namespace folly { + +/* + * folly::enable_shared_from_this + * + * To be removed once C++17 becomes a minimum requirement for folly. + */ +#if __cplusplus >= 201700L || __cpp_lib_enable_shared_from_this >= 201603L + +// Guaranteed to have std::enable_shared_from_this::weak_from_this(). Prefer +// type alias over our own class. +/* using override */ using std::enable_shared_from_this; + +#else + +/** + * Extends std::enabled_shared_from_this. Offers weak_from_this() to pre-C++17 + * code. Use as drop-in replacement for std::enable_shared_from_this. + * + * C++14 has no direct means of creating a std::weak_ptr, one must always + * create a (temporary) std::shared_ptr first. C++17 adds weak_from_this() to + * std::enable_shared_from_this to avoid that overhead. Alas code that must + * compile under different language versions cannot call + * std::enable_shared_from_this::weak_from_this() directly. Hence this class. + * + * @example + * class MyClass : public folly::enable_shared_from_this<MyClass> {}; + * + * int main() { + * std::shared_ptr<MyClass> sp = std::make_shared<MyClass>(); + * std::weak_ptr<MyClass> wp = sp->weak_from_this(); + * } + */ +template <typename T> +class enable_shared_from_this : public std::enable_shared_from_this<T> { + public: + constexpr enable_shared_from_this() noexcept = default; + + std::weak_ptr<T> weak_from_this() noexcept { + return weak_from_this_<T>(this); + } + + std::weak_ptr<T const> weak_from_this() const noexcept { + return weak_from_this_<T>(this); + } + + private: + // Uses SFINAE to detect and call + // std::enable_shared_from_this<T>::weak_from_this() if available. Falls + // back to std::enable_shared_from_this<T>::shared_from_this() otherwise. + template <typename U> + auto weak_from_this_(std::enable_shared_from_this<U>* base_ptr) noexcept + -> decltype(base_ptr->weak_from_this()) { + return base_ptr->weak_from_this(); + } + + template <typename U> + auto weak_from_this_(std::enable_shared_from_this<U> const* base_ptr) const + noexcept -> decltype(base_ptr->weak_from_this()) { + return base_ptr->weak_from_this(); + } + + template <typename U> + std::weak_ptr<U> weak_from_this_(...) noexcept { + try { + return this->shared_from_this(); + } catch (std::bad_weak_ptr const&) { + // C++17 requires that weak_from_this() on an object not owned by a + // shared_ptr returns an empty weak_ptr. Sadly, in C++14, + // shared_from_this() on such an object is undefined behavior, and there + // is nothing we can do to detect and handle the situation in a portable + // manner. But in case a compiler is nice enough to implement C++17 + // semantics of shared_from_this() and throws a bad_weak_ptr, we catch it + // and return an empty weak_ptr. + return std::weak_ptr<U>{}; + } + } + + template <typename U> + std::weak_ptr<U const> weak_from_this_(...) const noexcept { + try { + return this->shared_from_this(); + } catch (std::bad_weak_ptr const&) { + return std::weak_ptr<U const>{}; + } + } +}; + +#endif + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.cpp b/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.cpp new file mode 100644 index 000000000..856820cb6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/memory/MallctlHelper.h> +#include <folly/Format.h> +#include <folly/String.h> + +#include <stdexcept> + +namespace folly { + +namespace detail { + +[[noreturn]] void handleMallctlError(const char* cmd, int err) { + assert(err != 0); + throw std::runtime_error( + sformat("mallctl {}: {} ({})", cmd, errnoStr(err), err)); +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.h b/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.h new file mode 100644 index 000000000..1d08b1eb8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/MallctlHelper.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Some helper functions for mallctl. + +#pragma once + +#include <folly/Likely.h> +#include <folly/memory/Malloc.h> + +#include <stdexcept> + +namespace folly { + +namespace detail { + +[[noreturn]] void handleMallctlError(const char* cmd, int err); + +template <typename T> +void mallctlHelper(const char* cmd, T* out, T* in) { + if (UNLIKELY(!usingJEMalloc())) { + throw std::logic_error("Calling mallctl when not using jemalloc."); + } + + size_t outLen = sizeof(T); + int err = mallctl(cmd, out, out ? &outLen : nullptr, in, in ? sizeof(T) : 0); + if (UNLIKELY(err != 0)) { + handleMallctlError(cmd, err); + } +} + +} // namespace detail + +template <typename T> +void mallctlRead(const char* cmd, T* out) { + detail::mallctlHelper(cmd, out, static_cast<T*>(nullptr)); +} + +template <typename T> +void mallctlWrite(const char* cmd, T in) { + detail::mallctlHelper(cmd, static_cast<T*>(nullptr), &in); +} + +template <typename T> +void mallctlReadWrite(const char* cmd, T* out, T in) { + detail::mallctlHelper(cmd, out, &in); +} + +inline void mallctlCall(const char* cmd) { + // Use <unsigned> rather than <void> to avoid sizeof(void). + mallctlRead<unsigned>(cmd, nullptr); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/Malloc.h b/ios/Pods/Flipper-Folly/folly/memory/Malloc.h new file mode 100644 index 000000000..bb8821545 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/Malloc.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Functions to provide smarter use of jemalloc, if jemalloc is being used. +// http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html + +#pragma once + +#include <folly/CPortability.h> +#include <folly/portability/Config.h> +#include <folly/portability/Malloc.h> + +/** + * Define various MALLOCX_* macros normally provided by jemalloc. We define + * them so that we don't have to include jemalloc.h, in case the program is + * built without jemalloc support. + */ +#if (defined(USE_JEMALLOC) || defined(FOLLY_USE_JEMALLOC)) && !FOLLY_SANITIZE +// We have JEMalloc, so use it. +#else +#ifndef MALLOCX_LG_ALIGN +#define MALLOCX_LG_ALIGN(la) (la) +#endif +#ifndef MALLOCX_ZERO +#define MALLOCX_ZERO (static_cast<int>(0x40)) +#endif +#endif + +#include <folly/lang/Exception.h> /* nolint */ +#include <folly/memory/detail/MallocImpl.h> /* nolint */ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +#include <atomic> +#include <new> + +// clang-format off + +namespace folly { + +#if defined(__GNUC__) +// This is for checked malloc-like functions (returns non-null pointer +// which cannot alias any outstanding pointer). +#define FOLLY_MALLOC_CHECKED_MALLOC \ + __attribute__((__returns_nonnull__, __malloc__)) +#else +#define FOLLY_MALLOC_CHECKED_MALLOC +#endif + +/** + * Determine if we are using jemalloc or not. + */ +#if defined(FOLLY_ASSUME_NO_JEMALLOC) || FOLLY_SANITIZE + inline bool usingJEMalloc() noexcept { + return false; + } +#elif defined(USE_JEMALLOC) && !FOLLY_SANITIZE + inline bool usingJEMalloc() noexcept { + return true; + } +#else +FOLLY_NOINLINE inline bool usingJEMalloc() noexcept { + // Checking for rallocx != nullptr is not sufficient; we may be in a + // dlopen()ed module that depends on libjemalloc, so rallocx is resolved, but + // the main program might be using a different memory allocator. + // How do we determine that we're using jemalloc? In the hackiest + // way possible. We allocate memory using malloc() and see if the + // per-thread counter of allocated memory increases. This makes me + // feel dirty inside. Also note that this requires jemalloc to have + // been compiled with --enable-stats. + static const bool result = []() noexcept { + // Some platforms (*cough* OSX *cough*) require weak symbol checks to be + // in the form if (mallctl != nullptr). Not if (mallctl) or if (!mallctl) + // (!!). http://goo.gl/xpmctm + if (mallocx == nullptr || rallocx == nullptr || xallocx == nullptr || + sallocx == nullptr || dallocx == nullptr || sdallocx == nullptr || + nallocx == nullptr || mallctl == nullptr || + mallctlnametomib == nullptr || mallctlbymib == nullptr) { + return false; + } + + // "volatile" because gcc optimizes out the reads from *counter, because + // it "knows" malloc doesn't modify global state... + /* nolint */ volatile uint64_t* counter; + size_t counterLen = sizeof(uint64_t*); + + if (mallctl( + "thread.allocatedp", + static_cast<void*>(&counter), + &counterLen, + nullptr, + 0) != 0) { + return false; + } + + if (counterLen != sizeof(uint64_t*)) { + return false; + } + + uint64_t origAllocated = *counter; + + static void* volatile ptr = malloc(1); + if (!ptr) { + // wtf, failing to allocate 1 byte + return false; + } + + free(ptr); + + return (origAllocated != *counter); + } + (); + + return result; +} +#endif + +inline bool getTCMallocNumericProperty(const char* name, size_t* out) noexcept { + return MallocExtension_Internal_GetNumericProperty(name, strlen(name), out); +} + +#if defined(FOLLY_ASSUME_NO_TCMALLOC) || FOLLY_SANITIZE + inline bool usingTCMalloc() noexcept { + return false; + } +#elif defined(USE_TCMALLOC) && !FOLLY_SANITIZE + inline bool usingTCMalloc() noexcept { + return true; + } +#else +FOLLY_NOINLINE inline bool usingTCMalloc() noexcept { + static const bool result = []() noexcept { + // Some platforms (*cough* OSX *cough*) require weak symbol checks to be + // in the form if (mallctl != nullptr). Not if (mallctl) or if (!mallctl) + // (!!). http://goo.gl/xpmctm + if (MallocExtension_Internal_GetNumericProperty == nullptr || + sdallocx == nullptr || nallocx == nullptr) { + return false; + } + static const char kAllocBytes[] = "generic.current_allocated_bytes"; + + size_t before_bytes = 0; + getTCMallocNumericProperty(kAllocBytes, &before_bytes); + + static void* volatile ptr = malloc(1); + if (!ptr) { + // wtf, failing to allocate 1 byte + return false; + } + + size_t after_bytes = 0; + getTCMallocNumericProperty(kAllocBytes, &after_bytes); + + free(ptr); + + return (before_bytes != after_bytes); + } + (); + + return result; +} +#endif + +FOLLY_NOINLINE inline bool canSdallocx() noexcept { + static bool rv = usingJEMalloc() || usingTCMalloc(); + return rv; +} + +FOLLY_NOINLINE inline bool canNallocx() noexcept { + static bool rv = usingJEMalloc() || usingTCMalloc(); + return rv; +} + +inline size_t goodMallocSize(size_t minSize) noexcept { + if (minSize == 0) { + return 0; + } + + if (!canNallocx()) { + // No nallocx - no smarts + return minSize; + } + + // nallocx returns 0 if minSize can't succeed, but 0 is not actually + // a goodMallocSize if you want minSize + auto rv = nallocx(minSize, 0); + return rv ? rv : minSize; +} + +// We always request "good" sizes for allocation, so jemalloc can +// never grow in place small blocks; they're already occupied to the +// brim. Blocks larger than or equal to 4096 bytes can in fact be +// expanded in place, and this constant reflects that. +static const size_t jemallocMinInPlaceExpandable = 4096; + +/** + * Trivial wrappers around malloc, calloc, realloc that check for allocation + * failure and throw std::bad_alloc in that case. + */ +inline void* checkedMalloc(size_t size) { + void* p = malloc(size); + if (!p) { + throw_exception<std::bad_alloc>(); + } + return p; +} + +inline void* checkedCalloc(size_t n, size_t size) { + void* p = calloc(n, size); + if (!p) { + throw_exception<std::bad_alloc>(); + } + return p; +} + +inline void* checkedRealloc(void* ptr, size_t size) { + void* p = realloc(ptr, size); + if (!p) { + throw_exception<std::bad_alloc>(); + } + return p; +} + +inline void sizedFree(void* ptr, size_t size) { + if (canSdallocx()) { + sdallocx(ptr, size, 0); + } else { + free(ptr); + } +} + +/** + * This function tries to reallocate a buffer of which only the first + * currentSize bytes are used. The problem with using realloc is that + * if currentSize is relatively small _and_ if realloc decides it + * needs to move the memory chunk to a new buffer, then realloc ends + * up copying data that is not used. It's generally not a win to try + * to hook in to realloc() behavior to avoid copies - at least in + * jemalloc, realloc() almost always ends up doing a copy, because + * there is little fragmentation / slack space to take advantage of. + */ +FOLLY_MALLOC_CHECKED_MALLOC FOLLY_NOINLINE inline void* smartRealloc( + void* p, + const size_t currentSize, + const size_t currentCapacity, + const size_t newCapacity) { + assert(p); + assert(currentSize <= currentCapacity && + currentCapacity < newCapacity); + + auto const slack = currentCapacity - currentSize; + if (slack * 2 > currentSize) { + // Too much slack, malloc-copy-free cycle: + auto const result = checkedMalloc(newCapacity); + std::memcpy(result, p, currentSize); + free(p); + return result; + } + // If there's not too much slack, we realloc in hope of coalescing + return checkedRealloc(p, newCapacity); +} + +} // namespace folly + +// clang-format on diff --git a/ios/Pods/Flipper-Folly/folly/memory/MemoryResource.h b/ios/Pods/Flipper-Folly/folly/memory/MemoryResource.h new file mode 100644 index 000000000..30abd66c6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/MemoryResource.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#if defined(__has_include) && __cplusplus >= 201703L + +#if __has_include(<memory_resource>) + +#define FOLLY_HAS_MEMORY_RESOURCE 1 +#include <memory_resource> // @manual +namespace folly { +namespace detail { +namespace std_pmr = ::std::pmr; +} // namespace detail +} // namespace folly + +// Ignore experimental/memory_resource for libc++ so that all programs +// don't need to explicitly link the c++experimental lib +#elif !defined(_LIBCPP_VERSION) && __has_include(<experimental/memory_resource>) + +#define FOLLY_HAS_MEMORY_RESOURCE 1 +#include <experimental/memory_resource> // @manual +namespace folly { +namespace detail { +namespace std_pmr = ::std::experimental::pmr; +} // namespace detail +} // namespace folly + +#else + +#define FOLLY_HAS_MEMORY_RESOURCE 0 + +#endif + +#else // __has_include + +#define FOLLY_HAS_MEMORY_RESOURCE 0 + +#endif // __has_include diff --git a/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.cpp b/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.cpp new file mode 100644 index 000000000..d4105016b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/memory/ReentrantAllocator.h> + +#include <new> +#include <utility> + +#include <folly/lang/Bits.h> +#include <folly/lang/SafeAssert.h> +#include <folly/portability/SysMman.h> + +namespace folly { + +namespace { + +max_align_t dummy; // return value for zero-sized allocations + +void* reentrant_allocate(std::size_t const n) noexcept { + FOLLY_SAFE_CHECK(n, "zero-sized"); + auto const prot = PROT_READ | PROT_WRITE; + auto const flags = MAP_ANONYMOUS | MAP_PRIVATE; + auto const addr = ::mmap(nullptr, n, prot, flags, 0, 0); + FOLLY_SAFE_PCHECK(addr != MAP_FAILED, "mmap failed"); + return addr; +} + +void reentrant_deallocate(void* const p, std::size_t const n) noexcept { + FOLLY_SAFE_CHECK(p, "null-pointer"); + FOLLY_SAFE_CHECK(n, "zero-sized"); + auto const err = ::munmap(p, n); + FOLLY_SAFE_PCHECK(!err, "munmap failed"); +} + +} // namespace + +namespace detail { + +reentrant_allocator_base::reentrant_allocator_base( + reentrant_allocator_options const& options) noexcept { + meta_ = static_cast<meta_t*>(reentrant_allocate(sizeof(meta_t))); + ::new (meta_) meta_t(options); +} + +reentrant_allocator_base::reentrant_allocator_base( + reentrant_allocator_base const& that) noexcept { + meta_ = that.meta_; + meta_->refs.fetch_add(1, std::memory_order_relaxed); +} + +reentrant_allocator_base& reentrant_allocator_base::operator=( + reentrant_allocator_base const& that) noexcept { + if (this != &that) { + if (meta_->refs.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { + obliterate(); + } + meta_ = that.meta_; + meta_->refs.fetch_add(1, std::memory_order_relaxed); + } + return *this; +} + +reentrant_allocator_base::~reentrant_allocator_base() { + if (meta_->refs.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { + obliterate(); + } +} + +void* reentrant_allocator_base::allocate( + std::size_t const n, + std::size_t const a) noexcept { + if (!n) { + return &dummy; + } + // large requests are handled directly + if (n >= meta_->large_size) { + return reentrant_allocate(n); + } + auto const block_size = meta_->block_size; + // small requests are handled from the shared arena list: + // * if the list is empty or the list head has insufficient space, c/x a new + // list head, starting over on failure + // * then c/x the list head size to the new size, starting over on failure + while (true) { + // load head - non-const because used in c/x below + auto head = meta_->head.load(std::memory_order_acquire); + // load size - non-const because used in c/x below + // size is where the prev allocation ends, if any + auto size = head // + ? head->size.load(std::memory_order_acquire) + : block_size; + // offset is where the next allocation starts, and is aligned as a + auto const offset = (size + a - 1) & ~(a - 1); + // if insufficient space in current segment or no current segment at all + if (offset + n > block_size || !head) { + // mmap a new segment and try to c/x it in to be the segment list head + auto const newhead = static_cast<node_t*>(reentrant_allocate(block_size)); + ::new (newhead) node_t(head); + auto const exchanged = meta_->head.compare_exchange_weak( + head, newhead, std::memory_order_release, std::memory_order_relaxed); + if (!exchanged) { + // lost the race - munmap the new segment and start over + reentrant_deallocate(newhead, block_size); + continue; + } + head = newhead; + } + // compute the new size and try to c/x it in to be the head segment size + auto const newsize = offset + n; + auto const exchanged = head->size.compare_exchange_weak( + size, newsize, std::memory_order_release, std::memory_order_relaxed); + if (!exchanged) { + // lost the race - start over + continue; + } + return reinterpret_cast<char*>(head) + offset; + } +} + +void reentrant_allocator_base::deallocate( + void* const p, + std::size_t const n) noexcept { + if (p == &dummy) { + FOLLY_SAFE_CHECK(n == 0, "unexpected non-zero size"); + return; + } + if (!n || !p) { + return; + } + // large requests are handled directly + if (n >= meta_->large_size) { + reentrant_deallocate(p, n); + return; + } + // small requests are deferred to allocator destruction, so no-op here +} + +void reentrant_allocator_base::obliterate() noexcept { + auto head = meta_->head.load(std::memory_order_acquire); + while (head != nullptr) { + auto const prev = std::exchange(head, head->next); + reentrant_deallocate(prev, meta_->block_size); + } + reentrant_deallocate(meta_, sizeof(meta_)); + meta_ = nullptr; +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.h b/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.h new file mode 100644 index 000000000..0df290168 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/ReentrantAllocator.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstddef> +#include <type_traits> + +namespace folly { + +class reentrant_allocator_options { + public: + // block_size_lg + // + // The log2 of the block size, which is the size of the blocks from which + // small allocations are returned. + std::size_t block_size_lg() const noexcept { + return block_size_lg_; + } + reentrant_allocator_options& block_size_lg(std::size_t const value) noexcept { + block_size_lg_ = value; + return *this; + } + + // large_size_lg + // + // The log2 of the large size, which is the size starting at which + // allocations are considered large and are returned directly from mmap. + std::size_t large_size_lg() const noexcept { + return large_size_lg_; + } + reentrant_allocator_options& large_size_lg(std::size_t const value) noexcept { + large_size_lg_ = value; + return *this; + } + + private: + std::size_t block_size_lg_ = 16; + std::size_t large_size_lg_ = 12; +}; + +namespace detail { + +class reentrant_allocator_base { + public: + explicit reentrant_allocator_base( + reentrant_allocator_options const& options) noexcept; + reentrant_allocator_base(reentrant_allocator_base const& that) noexcept; + reentrant_allocator_base& operator=( + reentrant_allocator_base const& that) noexcept; + ~reentrant_allocator_base(); + + void* allocate(std::size_t n, std::size_t a) noexcept; + void deallocate(void* p, std::size_t n) noexcept; + + friend bool operator==( + reentrant_allocator_base const& a, + reentrant_allocator_base const& b) noexcept { + return a.meta_ == b.meta_; + } + friend bool operator!=( + reentrant_allocator_base const& a, + reentrant_allocator_base const& b) noexcept { + return a.meta_ != b.meta_; + } + + private: + // For small sizes, maintain a shared list of segments. Segments are each + // allocated via mmap, chained together into a list, and collectively + // refcounted. When the last copy of the allocator is destroyed, segments + // are deallocated all at once via munmap. Node is the header data + // structure prefixing each segment while Meta is the data structure + // representing shared ownership of the segment list. Serve allocations + // from the head segment if it exists and has space, otherwise mmap and + // chain a new segment and serve allocations from it. Serve deallocations + // by doing nothing at all. + struct node_t { + node_t* next = nullptr; + std::atomic<std::size_t> size{sizeof(node_t)}; + explicit node_t(node_t* next_) noexcept : next{next_} {} + }; + + // The shared state which all copies of the allocator share. + struct meta_t { + // Small allocations are served from block-sized segments. + std::size_t const block_size = 0; + // Large allocations are served directly. + std::size_t const large_size = 0; + // The refcount is atomic to permit some copies of the allocator to be + // destroyed concurrently with uses of other copies of the allocator. + // This lets an allocator be copied in a signal handler and the copy + // be destroyed outside the signal handler. + std::atomic<std::size_t> refs{1}; + // The segment list head. All small allocations happen via the head node + // if possible, or via a new head node otherwise. + std::atomic<node_t*> head{nullptr}; + + explicit meta_t(reentrant_allocator_options const& options) noexcept + : block_size{std::size_t(1) << options.block_size_lg()}, + large_size{std::size_t(1) << options.large_size_lg()} {} + }; + + // Deduplicates code between dtor and copy-assignment. + void obliterate() noexcept; + + // The allocator has all state in the shared state, keeping only a pointer. + meta_t* meta_{nullptr}; +}; + +} // namespace detail + +// reentrant_allocator +// +// A reentrant mmap-based allocator. +// +// Safety: +// * multi-thread-safe +// * async-signal-safe +// +// The basic approach is in two parts: +// * For large sizes, serve allocations and deallocations directly via mmap and +// munmap and without any extra tracking. +// * For small sizes, serve allocations from a refcounted shared list of +// segments and defer deallocations to amortize calls to mmap and munmap - in +// other words, a shared arena list. +// +// Large allocations are aligned to page boundaries, even if the type's natural +// alignment is larger. +// +// Assumptions: +// * The mmap and munmap libc functions are async-signal-safe in practice even +// though POSIX does not require them to be. +// * The instances of std::atomic over size_t and pointer types are lock-free +// and operations on them are async-signal-safe. +template <typename T> +class reentrant_allocator : private detail::reentrant_allocator_base { + private: + template <typename> + friend class reentrant_allocator; + + using base = detail::reentrant_allocator_base; + + public: + using value_type = T; + + using base::base; + + template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0> + /* implicit */ reentrant_allocator( + reentrant_allocator<U> const& that) noexcept + : base{that} {} + + T* allocate(std::size_t n) { + return static_cast<T*>(base::allocate(n * sizeof(T), alignof(T))); + } + void deallocate(T* p, std::size_t n) { + base::deallocate(p, n * sizeof(T)); + } + + template <typename A, typename B> + friend bool operator==( + reentrant_allocator<A> const& a, + reentrant_allocator<B> const& b) noexcept; + template <typename A, typename B> + friend bool operator!=( + reentrant_allocator<A> const& a, + reentrant_allocator<B> const& b) noexcept; +}; + +template <typename A, typename B> +bool operator==( + reentrant_allocator<A> const& a, + reentrant_allocator<B> const& b) noexcept { + using base = detail::reentrant_allocator_base; + return static_cast<base const&>(a) == static_cast<base const&>(b); +} +template <typename A, typename B> +bool operator!=( + reentrant_allocator<A> const& a, + reentrant_allocator<B> const& b) noexcept { + using base = detail::reentrant_allocator_base; + return static_cast<base const&>(a) != static_cast<base const&>(b); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.cpp b/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.cpp new file mode 100644 index 000000000..972315704 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <mutex> +#include <unordered_set> + +#include <folly/memory/SanitizeLeak.h> + +namespace folly { +namespace detail { +namespace { +struct LeakedPtrs { + std::mutex mutex; + std::unordered_set<void const*> set; + + static LeakedPtrs& instance() { + static auto* ptrs = new LeakedPtrs(); + return *ptrs; + } +}; +} // namespace + +void annotate_object_leaked_impl(void const* ptr) { + if (ptr == nullptr) { + return; + } + auto& ptrs = LeakedPtrs::instance(); + std::lock_guard<std::mutex> lg(ptrs.mutex); + ptrs.set.insert(ptr); +} + +void annotate_object_collected_impl(void const* ptr) { + if (ptr == nullptr) { + return; + } + auto& ptrs = LeakedPtrs::instance(); + std::lock_guard<std::mutex> lg(ptrs.mutex); + ptrs.set.erase(ptr); +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.h b/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.h new file mode 100644 index 000000000..2d17286dd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/SanitizeLeak.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +namespace folly { + +namespace detail { +void annotate_object_leaked_impl(void const* ptr); +void annotate_object_collected_impl(void const* ptr); +} // namespace detail + +/** + * When the current compilation unit is being compiled with ASAN enabled this + * function will suppress an intentionally leaked pointer from the LSAN report. + * Otherwise, this function is an inlinable no-op. + * + * NOTE: This function will suppress LSAN leak reporting when the current + * compilation unit is being compiled with ASAN, independent of whether folly + * itself was compiled with ASAN enabled. + */ +FOLLY_ALWAYS_INLINE static void annotate_object_leaked(void const* ptr) { + if (kIsSanitizeAddress) { + detail::annotate_object_leaked_impl(static_cast<void const*>(ptr)); + } +} + +/** + * Annotate that an object previously passed to annotate_object_leaked() has + * been collected, so we should no longer suppress it from the LSAN report. + * This function is an inlinable no-op when ASAN is not enabled for the current + * compilation unit. + */ +FOLLY_ALWAYS_INLINE static void annotate_object_collected(void const* ptr) { + if (kIsSanitizeAddress) { + detail::annotate_object_collected_impl(static_cast<void const*>(ptr)); + } +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.cpp b/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.cpp new file mode 100644 index 000000000..ecf14dd64 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/memory/ThreadCachedArena.h> + +#include <memory> + +namespace folly { + +ThreadCachedArena::ThreadCachedArena(size_t minBlockSize, size_t maxAlign) + : minBlockSize_(minBlockSize), maxAlign_(maxAlign) {} + +SysArena* ThreadCachedArena::allocateThreadLocalArena() { + auto arena = new SysArena(minBlockSize_, SysArena::kNoSizeLimit, maxAlign_); + auto disposer = [this](SysArena* t, TLPDestructionMode mode) { + std::unique_ptr<SysArena> tp(t); // ensure it gets deleted + if (mode == TLPDestructionMode::THIS_THREAD) { + zombify(std::move(*t)); + } + }; + arena_.reset(arena, disposer); + return arena; +} + +void ThreadCachedArena::zombify(SysArena&& arena) { + zombies_.wlock()->merge(std::move(arena)); +} + +size_t ThreadCachedArena::totalSize() const { + size_t result = sizeof(ThreadCachedArena); + for (const auto& arena : arena_.accessAllThreads()) { + result += arena.totalSize(); + } + result += zombies_.rlock()->totalSize() - sizeof(SysArena); + return result; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.h b/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.h new file mode 100644 index 000000000..870194d96 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/ThreadCachedArena.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <type_traits> + +#include <folly/Likely.h> +#include <folly/Synchronized.h> +#include <folly/ThreadLocal.h> +#include <folly/memory/Arena.h> + +namespace folly { + +/** + * Thread-caching arena: allocate memory which gets freed when the arena gets + * destroyed. + * + * The arena itself allocates memory using malloc() in blocks of + * at least minBlockSize bytes. + * + * For speed, each thread gets its own Arena (see Arena.h); when threads + * exit, the Arena gets merged into a "zombie" Arena, which will be deallocated + * when the ThreadCachedArena object is destroyed. + */ +class ThreadCachedArena { + public: + explicit ThreadCachedArena( + size_t minBlockSize = SysArena::kDefaultMinBlockSize, + size_t maxAlign = SysArena::kDefaultMaxAlign); + + void* allocate(size_t size) { + SysArena* arena = arena_.get(); + if (UNLIKELY(!arena)) { + arena = allocateThreadLocalArena(); + } + + return arena->allocate(size); + } + + void deallocate(void* /* p */, size_t = 0) { + // Deallocate? Never! + } + + // Gets the total memory used by the arena + size_t totalSize() const; + + private: + struct ThreadLocalPtrTag {}; + + ThreadCachedArena(const ThreadCachedArena&) = delete; + ThreadCachedArena(ThreadCachedArena&&) = delete; + ThreadCachedArena& operator=(const ThreadCachedArena&) = delete; + ThreadCachedArena& operator=(ThreadCachedArena&&) = delete; + + SysArena* allocateThreadLocalArena(); + + // Zombify the blocks in arena, saving them for deallocation until + // the ThreadCachedArena is destroyed. + void zombify(SysArena&& arena); + + const size_t minBlockSize_; + const size_t maxAlign_; + + ThreadLocalPtr<SysArena, ThreadLocalPtrTag> arena_; // Per-thread arena. + + // Allocations from threads that are now dead. + Synchronized<SysArena> zombies_; +}; + +template <> +struct AllocatorHasTrivialDeallocate<ThreadCachedArena> : std::true_type {}; + +template <typename T> +using ThreadCachedArenaAllocator = CxxAllocatorAdaptor<T, ThreadCachedArena>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h b/ios/Pods/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h new file mode 100644 index 000000000..6f2908ffd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <string> +#include <type_traits> +#include <vector> + +namespace { +// This struct is different in every translation unit. We use template +// instantiations to define inline freestanding methods. Since the +// methods are inline it is fine to define them in multiple translation +// units, but the instantiation itself would be an ODR violation if it is +// present in the program more than once. By tagging the instantiations +// with this struct, we avoid ODR problems for the instantiation while +// allowing the resulting methods to be inline-able. If you think that +// seems hacky keep reading... +struct FollyMemoryDetailTranslationUnitTag {}; +} // namespace +namespace folly { +namespace detail { +template <typename T> +void unsafeStringSetLargerSize(std::basic_string<T>& s, std::size_t n); +template <typename T> +void unsafeVectorSetLargerSize(std::vector<T>& v, std::size_t n); +} // namespace detail + +/* + * This file provides helper functions resizeWithoutInitialization() + * that can resize std::basic_string or std::vector without constructing + * or initializing new elements. + * + * IMPORTANT: These functions can be unsafe if used improperly. If you + * don't write to an element with index >= oldSize and < newSize, reading + * the element can expose arbitrary memory contents to the world, including + * the contents of old strings. If you're lucky you'll get a segfault, + * because the kernel is only required to fault in new pages on write + * access. MSAN should be able to catch problems in the common case that + * the string or vector wasn't previously shrunk. + * + * Pay extra attention to your failure paths. For example, if you try + * to read directly into a caller-provided string, make sure to clear + * the string when you get an I/O error. + * + * You should only use this if you have profiling data from production + * that shows that this is not a premature optimization. This code is + * designed for retroactively optimizing code where touching every element + * twice (or touching never-used elements once) shows up in profiling, + * and where restructuring the code to use fixed-length arrays or IOBuf-s + * would be difficult. + * + * NOTE: Just because .resize() shows up in your profile (probably + * via one of the intrinsic memset implementations) doesn't mean that + * these functions will make your program faster. A lot of the cost + * of memset comes from cache misses, so avoiding the memset can mean + * that the cache miss cost just gets pushed to the following code. + * resizeWithoutInitialization can be a win when the contents are bigger + * than a cache level, because the second access isn't free in that case. + * It can be a win when the memory is already cached, so touching it + * doesn't help later code. It can also be a win if the final length + * of the string or vector isn't actually known, so the suffix will be + * chopped off with a second call to .resize(). + */ + +/** + * Like calling s.resize(n), but when growing the string does not + * initialize new elements. It is undefined behavior to read from + * any element added to the string by this method unless it has been + * written to by an operation that follows this call. + * + * Use the FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(T) macro to + * declare (and inline define) the internals required to call + * resizeWithoutInitialization for a std::basic_string<T>. + * See detailed description of a similar macro for std::vector<T> below. + * + * IMPORTANT: Read the warning at the top of this header file. + */ +template < + typename T, + typename = + typename std::enable_if<std::is_trivially_destructible<T>::value>::type> +inline void resizeWithoutInitialization( + std::basic_string<T>& s, + std::size_t n) { + if (n <= s.size()) { + s.resize(n); + } else { + // careful not to call reserve unless necessary, as it causes + // shrink_to_fit on many platforms + if (n > s.capacity()) { + s.reserve(n); + } + detail::unsafeStringSetLargerSize(s, n); + } +} + +/** + * Like calling v.resize(n), but when growing the vector does not construct + * or initialize new elements. It is undefined behavior to read from any + * element added to the vector by this method unless it has been written + * to by an operation that follows this call. + * + * Use the FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(T) macro to + * declare (and inline define) the internals required to call + * resizeWithoutInitialization for a std::vector<T>. This must + * be done exactly once in each translation unit that wants to call + * resizeWithoutInitialization(std::vector<T>&,size_t). char and unsigned + * char are provided by default. If you don't do this you will get linker + * errors about folly::detail::unsafeVectorSetLargerSize. Requiring that + * T be trivially_destructible is only an approximation of the property + * required of T. In fact what is required is that any random sequence of + * bytes may be safely reinterpreted as a T and passed to T's destructor. + * + * std::vector<bool> has specialized internals and is not supported. + * + * IMPORTANT: Read the warning at the top of this header file. + */ +template < + typename T, + typename = typename std::enable_if< + std::is_trivially_destructible<T>::value && + !std::is_same<T, bool>::value>::type> +void resizeWithoutInitialization(std::vector<T>& v, std::size_t n) { + if (n <= v.size()) { + v.resize(n); + } else { + if (n > v.capacity()) { + v.reserve(n); + } + detail::unsafeVectorSetLargerSize(v, n); + } +} + +namespace detail { + +// This machinery bridges template expansion and macro expansion +#define FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT_IMPL(TYPE) \ + namespace folly { \ + namespace detail { \ + void unsafeStringSetLargerSizeImpl(std::basic_string<TYPE>& s, std::size_t); \ + template <> \ + inline void unsafeStringSetLargerSize<TYPE>( \ + std::basic_string<TYPE> & s, \ + std::size_t n) { \ + unsafeStringSetLargerSizeImpl(s, n); \ + } \ + } \ + } + +#if defined(_LIBCPP_STRING) +// libc++ + +template <typename Tag, typename T, typename A, A Ptr__set_size> +struct MakeUnsafeStringSetLargerSize { + friend void unsafeStringSetLargerSizeImpl( + std::basic_string<T>& s, + std::size_t n) { + // s.__set_size(n); + (s.*Ptr__set_size)(n); + (&s[0])[n] = '\0'; + } +}; + +#define FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(TYPE) \ + template void std::basic_string<TYPE>::__set_size(std::size_t); \ + template struct folly::detail::MakeUnsafeStringSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE, \ + void (std::basic_string<TYPE>::*)(std::size_t), \ + &std::basic_string<TYPE>::__set_size>; \ + FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#elif defined(_GLIBCXX_STRING) && _GLIBCXX_USE_CXX11_ABI +// libstdc++ new implementation with SSO + +template <typename Tag, typename T, typename A, A Ptr_M_set_length> +struct MakeUnsafeStringSetLargerSize { + friend void unsafeStringSetLargerSizeImpl( + std::basic_string<T>& s, + std::size_t n) { + // s._M_set_length(n); + (s.*Ptr_M_set_length)(n); + } +}; + +#define FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(TYPE) \ + template void std::basic_string<TYPE>::_M_set_length(std::size_t); \ + template struct folly::detail::MakeUnsafeStringSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE, \ + void (std::basic_string<TYPE>::*)(std::size_t), \ + &std::basic_string<TYPE>::_M_set_length>; \ + FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#elif defined(_GLIBCXX_STRING) +// libstdc++ old implementation + +template < + typename Tag, + typename T, + typename A, + A Ptr_M_rep, + typename B, + B Ptr_M_set_length_and_sharable> +struct MakeUnsafeStringSetLargerSize { + friend void unsafeStringSetLargerSizeImpl( + std::basic_string<T>& s, + std::size_t n) { + // s._M_rep()->_M_set_length_and_sharable(n); + auto rep = (s.*Ptr_M_rep)(); + (rep->*Ptr_M_set_length_and_sharable)(n); + } +}; + +#define FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(TYPE) \ + template std::basic_string<TYPE>::_Rep* std::basic_string<TYPE>::_M_rep() \ + const; \ + template void std::basic_string<TYPE>::_Rep::_M_set_length_and_sharable( \ + std::size_t); \ + template struct folly::detail::MakeUnsafeStringSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE, \ + std::basic_string<TYPE>::_Rep* (std::basic_string<TYPE>::*)() const, \ + &std::basic_string<TYPE>::_M_rep, \ + void (std::basic_string<TYPE>::_Rep::*)(std::size_t), \ + &std::basic_string<TYPE>::_Rep::_M_set_length_and_sharable>; \ + FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#elif defined(_MSC_VER) +// MSVC + +template <typename Tag, typename T> +struct MakeUnsafeStringSetLargerSize { + friend void unsafeStringSetLargerSizeImpl( + std::basic_string<T>& s, + std::size_t n) { + s._Eos(n); + } +}; + +#define FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(TYPE) \ + template struct folly::detail::MakeUnsafeStringSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE>; \ + FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#else +#warning \ + "No implementation for resizeWithoutInitialization of std::basic_string" +#endif + +} // namespace detail +} // namespace folly + +#if defined(FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT) +FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(char) +FOLLY_DECLARE_STRING_RESIZE_WITHOUT_INIT(wchar_t) +#endif + +namespace folly { +namespace detail { + +// This machinery bridges template expansion and macro expansion +#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE) \ + namespace folly { \ + namespace detail { \ + void unsafeVectorSetLargerSizeImpl(std::vector<TYPE>& v, std::size_t); \ + template <> \ + inline void unsafeVectorSetLargerSize<TYPE>( \ + std::vector<TYPE> & v, \ + std::size_t n) { \ + unsafeVectorSetLargerSizeImpl(v, n); \ + } \ + } \ + } + +#if defined(_LIBCPP_VECTOR) +// libc++ + +template < + typename Tag, + typename T, + typename A, + A Ptr__end_, + typename B, + B Ptr__annotate_contiguous_container_> +struct MakeUnsafeVectorSetLargerSize { + friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) { + // v.__end_ += (n - v.size()); + using Base = std::__vector_base<T, std::allocator<T>>; + static_assert( + std::is_standard_layout<std::vector<T>>::value && + sizeof(std::vector<T>) == sizeof(Base), + "reinterpret_cast safety conditions not met"); + const auto old_size = v.size(); + reinterpret_cast<Base&>(v).*Ptr__end_ += (n - v.size()); + + // libc++ contiguous containers use special annotation functions that help + // the address sanitizer to detect improper memory accesses. When ASAN is + // enabled we need to call the appropriate annotation functions in order to + // stop ASAN from reporting false positives. When ASAN is disabled, the + // annotation function is a no-op. + (v.*Ptr__annotate_contiguous_container_)( + v.data(), + v.data() + v.capacity(), + v.data() + old_size, + v.data() + v.size()); + } +}; + +#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \ + template struct folly::detail::MakeUnsafeVectorSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE, \ + TYPE*(std::__vector_base<TYPE, std::allocator<TYPE>>::*), \ + &std::vector<TYPE>::__end_, \ + void (std::vector<TYPE>::*)( \ + const void*, const void*, const void*, const void*) const, \ + &std::vector<TYPE>::__annotate_contiguous_container>; \ + FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#elif defined(_GLIBCXX_VECTOR) +// libstdc++ + +template < + typename Tag, + typename T, + typename A, + A Ptr_M_impl, + typename B, + B Ptr_M_finish> +struct MakeUnsafeVectorSetLargerSize : std::vector<T> { + friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) { + // v._M_impl._M_finish += (n - v.size()); + (v.*Ptr_M_impl).*Ptr_M_finish += (n - v.size()); + } +}; + +#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \ + template struct folly::detail::MakeUnsafeVectorSetLargerSize< \ + FollyMemoryDetailTranslationUnitTag, \ + TYPE, \ + decltype(&std::vector<TYPE>::_M_impl), \ + &std::vector<TYPE>::_M_impl, \ + decltype(&std::vector<TYPE>::_Vector_impl::_M_finish), \ + &std::vector<TYPE>::_Vector_impl::_M_finish>; \ + FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#elif defined(_MSC_VER) +// MSVC + +#define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \ + extern inline void unsafeVectorSetLargerSizeImpl( \ + std::vector<TYPE>& v, std::size_t n) { \ + v._Mylast() += (n - v.size()); \ + } \ + FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE) + +#else +#warning "No implementation for resizeWithoutInitialization of std::vector" +#endif + +} // namespace detail +} // namespace folly + +#if defined(FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT) +FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(char) +FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(unsigned char) +#endif diff --git a/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.cpp b/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.cpp new file mode 100644 index 000000000..b49e51f8f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/memory/detail/MallocImpl.h> + +extern "C" { + +#ifdef _MSC_VER +// MSVC doesn't have weak symbols, so do some linker magic +// to emulate them. (the magic is in the header) +const char* mallocxWeak = nullptr; +const char* rallocxWeak = nullptr; +const char* xallocxWeak = nullptr; +const char* sallocxWeak = nullptr; +const char* dallocxWeak = nullptr; +const char* sdallocxWeak = nullptr; +const char* nallocxWeak = nullptr; +const char* mallctlWeak = nullptr; +const char* mallctlnametomibWeak = nullptr; +const char* mallctlbymibWeak = nullptr; +const char* MallocExtension_Internal_GetNumericPropertyWeak = nullptr; +#elif !FOLLY_HAVE_WEAK_SYMBOLS +void* (*mallocx)(size_t, int) = nullptr; +void* (*rallocx)(void*, size_t, int) = nullptr; +size_t (*xallocx)(void*, size_t, size_t, int) = nullptr; +size_t (*sallocx)(const void*, int) = nullptr; +void (*dallocx)(void*, int) = nullptr; +void (*sdallocx)(void*, size_t, int) = nullptr; +size_t (*nallocx)(size_t, int) = nullptr; +int (*mallctl)(const char*, void*, size_t*, void*, size_t) = nullptr; +int (*mallctlnametomib)(const char*, size_t*, size_t*) = nullptr; +int (*mallctlbymib)(const size_t*, size_t, void*, size_t*, void*, size_t) = + nullptr; +bool (*MallocExtension_Internal_GetNumericProperty)( + const char*, + size_t, + size_t*) = nullptr; +#endif +} diff --git a/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.h b/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.h new file mode 100644 index 000000000..ccdf0d117 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/memory/detail/MallocImpl.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdlib.h> + +#include <folly/Portability.h> + +extern "C" { + +#if FOLLY_HAVE_WEAK_SYMBOLS +#if !defined(__FreeBSD__) +void* mallocx(size_t, int) __attribute__((__nothrow__, __weak__)); +void* rallocx(void*, size_t, int) __attribute__((__nothrow__, __weak__)); +size_t xallocx(void*, size_t, size_t, int) + __attribute__((__nothrow__, __weak__)); +size_t sallocx(const void*, int) __attribute__((__nothrow__, __weak__)); +void dallocx(void*, int) __attribute__((__nothrow__, __weak__)); +void sdallocx(void*, size_t, int) __attribute__((__nothrow__, __weak__)); +size_t nallocx(size_t, int) __attribute__((__nothrow__, __weak__)); +int mallctl(const char*, void*, size_t*, void*, size_t) + __attribute__((__nothrow__, __weak__)); +int mallctlnametomib(const char*, size_t*, size_t*) + __attribute__((__nothrow__, __weak__)); +int mallctlbymib(const size_t*, size_t, void*, size_t*, void*, size_t) + __attribute__((__nothrow__, __weak__)); +bool MallocExtension_Internal_GetNumericProperty(const char*, size_t, size_t*) + __attribute__((__weak__)); +#endif +#else +extern void* (*mallocx)(size_t, int); +extern void* (*rallocx)(void*, size_t, int); +extern size_t (*xallocx)(void*, size_t, size_t, int); +extern size_t (*sallocx)(const void*, int); +extern void (*dallocx)(void*, int); +extern void (*sdallocx)(void*, size_t, int); +extern size_t (*nallocx)(size_t, int); +extern int (*mallctl)(const char*, void*, size_t*, void*, size_t); +extern int (*mallctlnametomib)(const char*, size_t*, size_t*); +extern int ( + *mallctlbymib)(const size_t*, size_t, void*, size_t*, void*, size_t); +extern bool ( + *MallocExtension_Internal_GetNumericProperty)(const char*, size_t, size_t*); +#ifdef _MSC_VER +// We emulate weak linkage for MSVC. The symbols we're +// aliasing to are hiding in MallocImpl.cpp +#if defined(_M_IX86) +#pragma comment(linker, "/alternatename:_mallocx=_mallocxWeak") +#pragma comment(linker, "/alternatename:_rallocx=_rallocxWeak") +#pragma comment(linker, "/alternatename:_xallocx=_xallocxWeak") +#pragma comment(linker, "/alternatename:_sallocx=_sallocxWeak") +#pragma comment(linker, "/alternatename:_dallocx=_dallocxWeak") +#pragma comment(linker, "/alternatename:_sdallocx=_sdallocxWeak") +#pragma comment(linker, "/alternatename:_nallocx=_nallocxWeak") +#pragma comment(linker, "/alternatename:_mallctl=_mallctlWeak") +#pragma comment( \ + linker, "/alternatename:_mallctlnametomib=_mallctlnametomibWeak") +#pragma comment(linker, "/alternatename:_mallctlbymib=_mallctlbymibWeak") +#pragma comment( \ + linker, \ + "/alternatename:_MallocExtension_Internal_GetNumericProperty=_MallocExtension_Internal_GetNumericPropertyWeak") +#else +#pragma comment(linker, "/alternatename:mallocx=mallocxWeak") +#pragma comment(linker, "/alternatename:rallocx=rallocxWeak") +#pragma comment(linker, "/alternatename:xallocx=xallocxWeak") +#pragma comment(linker, "/alternatename:sallocx=sallocxWeak") +#pragma comment(linker, "/alternatename:dallocx=dallocxWeak") +#pragma comment(linker, "/alternatename:sdallocx=sdallocxWeak") +#pragma comment(linker, "/alternatename:nallocx=nallocxWeak") +#pragma comment(linker, "/alternatename:mallctl=mallctlWeak") +#pragma comment(linker, "/alternatename:mallctlnametomib=mallctlnametomibWeak") +#pragma comment(linker, "/alternatename:mallctlbymib=mallctlbymibWeak") +#pragma comment( \ + linker, \ + "/alternatename:MallocExtension_Internal_GetNumericProperty=MallocExtension_Internal_GetNumericPropertyWeak") +#endif +#endif +#endif +} diff --git a/ios/Pods/Flipper-Folly/folly/net/NetOps.cpp b/ios/Pods/Flipper-Folly/folly/net/NetOps.cpp new file mode 100644 index 000000000..f1ad694ab --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/net/NetOps.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/net/NetOps.h> + +#include <fcntl.h> +#include <cerrno> + +#include <cstddef> + +#include <folly/Portability.h> +#include <folly/net/detail/SocketFileDescriptorMap.h> + +#ifdef _WIN32 +#include <event2/util.h> // @manual + +#include <MSWSock.h> // @manual + +#include <folly/ScopeGuard.h> +#endif + +namespace folly { +namespace netops { + +namespace { +#ifdef _WIN32 +// WSA has to be explicitly initialized. +static struct WinSockInit { + WinSockInit() { + WSADATA dat; + WSAStartup(MAKEWORD(2, 2), &dat); + } + ~WinSockInit() { + WSACleanup(); + } +} winsockInit; + +int translate_wsa_error(int wsaErr) { + switch (wsaErr) { + case WSAEWOULDBLOCK: + return EAGAIN; + default: + return wsaErr; + } +} +#endif + +template <class R, class F, class... Args> +static R wrapSocketFunction(F f, NetworkSocket s, Args... args) { + R ret = f(s.data, args...); +#ifdef _WIN32 + errno = translate_wsa_error(WSAGetLastError()); +#endif + return ret; +} +} // namespace + +NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen) { + return NetworkSocket(wrapSocketFunction<NetworkSocket::native_handle_type>( + ::accept, s, addr, addrlen)); +} + +int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) { + if (kIsWindows && name->sa_family == AF_UNIX) { + // Windows added support for AF_UNIX sockets, but didn't add + // support for autobind sockets, so detect requests for autobind + // sockets and treat them as invalid. (otherwise they don't trigger + // an error, but also don't actually work) + if (name->sa_data[0] == '\0') { + errno = EINVAL; + return -1; + } + } + return wrapSocketFunction<int>(::bind, s, name, namelen); +} + +int close(NetworkSocket s) { + return netops::detail::SocketFileDescriptorMap::close(s.data); +} + +int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) { + auto r = wrapSocketFunction<int>(::connect, s, name, namelen); +#ifdef _WIN32 + if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { + errno = EINPROGRESS; + } +#endif + return r; +} + +int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen) { + return wrapSocketFunction<int>(::getpeername, s, name, namelen); +} + +int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen) { + return wrapSocketFunction<int>(::getsockname, s, name, namelen); +} + +int getsockopt( + NetworkSocket s, + int level, + int optname, + void* optval, + socklen_t* optlen) { + auto ret = wrapSocketFunction<int>( + ::getsockopt, s, level, optname, (char*)optval, optlen); +#ifdef _WIN32 + if (optname == TCP_NODELAY && *optlen == 1) { + // Windows is weird about this value, and documents it as a + // BOOL (ie. int) but expects the variable to be bool (1-byte), + // so we get to adapt the interface to work that way. + *(int*)optval = *(uint8_t*)optval; + *optlen = sizeof(int); + } +#endif + return ret; +} + +int inet_aton(const char* cp, in_addr* inp) { + inp->s_addr = inet_addr(cp); + return inp->s_addr == INADDR_NONE ? 0 : 1; +} + +int listen(NetworkSocket s, int backlog) { + return wrapSocketFunction<int>(::listen, s, backlog); +} + +int poll(PollDescriptor fds[], nfds_t nfds, int timeout) { + // Make sure that PollDescriptor is byte-for-byte identical to pollfd, + // so we don't need extra allocations just for the safety of this shim. + static_assert( + alignof(PollDescriptor) == alignof(pollfd), + "PollDescriptor is misaligned"); + static_assert( + sizeof(PollDescriptor) == sizeof(pollfd), + "PollDescriptor is the wrong size"); + static_assert( + offsetof(PollDescriptor, fd) == offsetof(pollfd, fd), + "PollDescriptor.fd is at the wrong place"); + static_assert( + sizeof(decltype(PollDescriptor().fd)) == sizeof(decltype(pollfd().fd)), + "PollDescriptor.fd is the wrong size"); + static_assert( + offsetof(PollDescriptor, events) == offsetof(pollfd, events), + "PollDescriptor.events is at the wrong place"); + static_assert( + sizeof(decltype(PollDescriptor().events)) == + sizeof(decltype(pollfd().events)), + "PollDescriptor.events is the wrong size"); + static_assert( + offsetof(PollDescriptor, revents) == offsetof(pollfd, revents), + "PollDescriptor.revents is at the wrong place"); + static_assert( + sizeof(decltype(PollDescriptor().revents)) == + sizeof(decltype(pollfd().revents)), + "PollDescriptor.revents is the wrong size"); + + // Pun it through + auto files = reinterpret_cast<pollfd*>(reinterpret_cast<void*>(fds)); +#ifdef _WIN32 + return ::WSAPoll(files, (ULONG)nfds, timeout); +#else + return ::poll(files, nfds, timeout); +#endif +} + +ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) { +#ifdef _WIN32 + if ((flags & MSG_DONTWAIT) == MSG_DONTWAIT) { + flags &= ~MSG_DONTWAIT; + + u_long pendingRead = 0; + if (ioctlsocket(s.data, FIONREAD, &pendingRead)) { + errno = translate_wsa_error(WSAGetLastError()); + return -1; + } + + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(s.data, &readSet); + timeval timeout{0, 0}; + auto ret = select(1, &readSet, nullptr, nullptr, &timeout); + if (ret == 0) { + errno = EWOULDBLOCK; + return -1; + } + } + return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags); +#else + return wrapSocketFunction<ssize_t>(::recv, s, buf, len, flags); +#endif +} + +ssize_t recvfrom( + NetworkSocket s, + void* buf, + size_t len, + int flags, + sockaddr* from, + socklen_t* fromlen) { +#ifdef _WIN32 + if ((flags & MSG_TRUNC) == MSG_TRUNC) { + SOCKET h = s.data; + + WSABUF wBuf{}; + wBuf.buf = (CHAR*)buf; + wBuf.len = (ULONG)len; + WSAMSG wMsg{}; + wMsg.dwBufferCount = 1; + wMsg.lpBuffers = &wBuf; + wMsg.name = from; + if (fromlen != nullptr) { + wMsg.namelen = *fromlen; + } + + // WSARecvMsg is an extension, so we don't get + // the convenience of being able to call it directly, even though + // WSASendMsg is part of the normal API -_-... + LPFN_WSARECVMSG WSARecvMsg; + GUID WSARecgMsg_GUID = WSAID_WSARECVMSG; + DWORD recMsgBytes; + WSAIoctl( + h, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecgMsg_GUID, + sizeof(WSARecgMsg_GUID), + &WSARecvMsg, + sizeof(WSARecvMsg), + &recMsgBytes, + nullptr, + nullptr); + + DWORD bytesReceived; + int res = WSARecvMsg(h, &wMsg, &bytesReceived, nullptr, nullptr); + errno = translate_wsa_error(WSAGetLastError()); + if (res == 0) { + return bytesReceived; + } + if (fromlen != nullptr) { + *fromlen = wMsg.namelen; + } + if ((wMsg.dwFlags & MSG_TRUNC) == MSG_TRUNC) { + return wBuf.len + 1; + } + return -1; + } + return wrapSocketFunction<ssize_t>( + ::recvfrom, s, (char*)buf, (int)len, flags, from, fromlen); +#else + return wrapSocketFunction<ssize_t>( + ::recvfrom, s, buf, len, flags, from, fromlen); +#endif +} + +ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags) { +#ifdef _WIN32 + (void)flags; + SOCKET h = s.data; + + // Don't currently support the name translation. + if (message->msg_name != nullptr || message->msg_namelen != 0) { + return (ssize_t)-1; + } + WSAMSG msg; + msg.name = nullptr; + msg.namelen = 0; + msg.Control.buf = (CHAR*)message->msg_control; + msg.Control.len = (ULONG)message->msg_controllen; + msg.dwFlags = 0; + msg.dwBufferCount = (DWORD)message->msg_iovlen; + msg.lpBuffers = new WSABUF[message->msg_iovlen]; + SCOPE_EXIT { + delete[] msg.lpBuffers; + }; + for (size_t i = 0; i < message->msg_iovlen; i++) { + msg.lpBuffers[i].buf = (CHAR*)message->msg_iov[i].iov_base; + msg.lpBuffers[i].len = (ULONG)message->msg_iov[i].iov_len; + } + + // WSARecvMsg is an extension, so we don't get + // the convenience of being able to call it directly, even though + // WSASendMsg is part of the normal API -_-... + LPFN_WSARECVMSG WSARecvMsg; + GUID WSARecgMsg_GUID = WSAID_WSARECVMSG; + DWORD recMsgBytes; + WSAIoctl( + h, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecgMsg_GUID, + sizeof(WSARecgMsg_GUID), + &WSARecvMsg, + sizeof(WSARecvMsg), + &recMsgBytes, + nullptr, + nullptr); + + DWORD bytesReceived; + int res = WSARecvMsg(h, &msg, &bytesReceived, nullptr, nullptr); + errno = translate_wsa_error(WSAGetLastError()); + return res == 0 ? (ssize_t)bytesReceived : -1; +#else + return wrapSocketFunction<ssize_t>(::recvmsg, s, message, flags); +#endif +} + +int recvmmsg( + NetworkSocket s, + mmsghdr* msgvec, + unsigned int vlen, + unsigned int flags, + timespec* timeout) { +#if FOLLY_HAVE_RECVMMSG + return wrapSocketFunction<int>(::recvmmsg, s, msgvec, vlen, flags, timeout); +#else + // implement via recvmsg + for (unsigned int i = 0; i < vlen; i++) { + ssize_t ret = recvmsg(s, &msgvec[i].msg_hdr, flags); + // in case of an error + // we return the number of msgs received if > 0 + // or an error if no msg was sent + if (ret < 0) { + if (i) { + return static_cast<int>(i); + } + return static_cast<int>(ret); + } + } + return static_cast<int>(vlen); +#endif +} + +ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags) { +#ifdef _WIN32 + return wrapSocketFunction<ssize_t>( + ::send, s, (const char*)buf, (int)len, flags); +#else + return wrapSocketFunction<ssize_t>(::send, s, buf, len, flags); +#endif +} + +ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) { +#ifdef _WIN32 + (void)flags; + SOCKET h = socket.data; + + // Unfortunately, WSASendMsg requires the socket to have been opened + // as either SOCK_DGRAM or SOCK_RAW, but sendmsg has no such requirement, + // so we have to implement it based on send instead :( + ssize_t bytesSent = 0; + for (size_t i = 0; i < message->msg_iovlen; i++) { + int r = -1; + if (message->msg_name != nullptr) { + r = ::sendto( + h, + (const char*)message->msg_iov[i].iov_base, + (int)message->msg_iov[i].iov_len, + message->msg_flags, + (const sockaddr*)message->msg_name, + (int)message->msg_namelen); + } else { + r = ::send( + h, + (const char*)message->msg_iov[i].iov_base, + (int)message->msg_iov[i].iov_len, + message->msg_flags); + } + if (r == -1 || size_t(r) != message->msg_iov[i].iov_len) { + errno = translate_wsa_error(WSAGetLastError()); + if (WSAGetLastError() == WSAEWOULDBLOCK && bytesSent > 0) { + return bytesSent; + } + return -1; + } + bytesSent += r; + } + return bytesSent; +#else + return wrapSocketFunction<ssize_t>(::sendmsg, socket, message, flags); +#endif +} + +int sendmmsg( + NetworkSocket socket, + mmsghdr* msgvec, + unsigned int vlen, + int flags) { +#if FOLLY_HAVE_SENDMMSG + return wrapSocketFunction<int>(::sendmmsg, socket, msgvec, vlen, flags); +#else + // implement via sendmsg + for (unsigned int i = 0; i < vlen; i++) { + ssize_t ret = sendmsg(socket, &msgvec[i].msg_hdr, flags); + // in case of an error + // we return the number of msgs sent if > 0 + // or an error if no msg was sent + if (ret < 0) { + if (i) { + return static_cast<int>(i); + } + + return static_cast<int>(ret); + } + } + + return static_cast<int>(vlen); +#endif +} + +ssize_t sendto( + NetworkSocket s, + const void* buf, + size_t len, + int flags, + const sockaddr* to, + socklen_t tolen) { +#ifdef _WIN32 + return wrapSocketFunction<ssize_t>( + ::sendto, s, (const char*)buf, (int)len, flags, to, (int)tolen); +#else + return wrapSocketFunction<ssize_t>(::sendto, s, buf, len, flags, to, tolen); +#endif +} + +int setsockopt( + NetworkSocket s, + int level, + int optname, + const void* optval, + socklen_t optlen) { +#ifdef _WIN32 + if (optname == SO_REUSEADDR) { + // We don't have an equivelent to the Linux & OSX meaning of this + // on Windows, so ignore it. + return 0; + } else if (optname == SO_REUSEPORT) { + // Windows's SO_REUSEADDR option is closer to SO_REUSEPORT than + // it is to the Linux & OSX meaning of SO_REUSEADDR. + return -1; + } + return wrapSocketFunction<int>( + ::setsockopt, s, level, optname, (char*)optval, optlen); +#else + return wrapSocketFunction<int>( + ::setsockopt, s, level, optname, optval, optlen); +#endif +} + +int shutdown(NetworkSocket s, int how) { + return wrapSocketFunction<int>(::shutdown, s, how); +} + +NetworkSocket socket(int af, int type, int protocol) { + return NetworkSocket(::socket(af, type, protocol)); +} + +int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) { +#ifdef _WIN32 + if (domain != PF_UNIX || type != SOCK_STREAM || protocol != 0) { + return -1; + } + intptr_t pair[2]; + auto r = evutil_socketpair(AF_INET, type, protocol, pair); + if (r == -1) { + return r; + } + sv[0] = NetworkSocket(static_cast<SOCKET>(pair[0])); + sv[1] = NetworkSocket(static_cast<SOCKET>(pair[1])); + return r; +#else + int pair[2]; + auto r = ::socketpair(domain, type, protocol, pair); + if (r == -1) { + return r; + } + sv[0] = NetworkSocket(pair[0]); + sv[1] = NetworkSocket(pair[1]); + return r; +#endif +} + +int set_socket_non_blocking(NetworkSocket s) { +#ifdef _WIN32 + u_long nonBlockingEnabled = 1; + return ioctlsocket(s.data, FIONBIO, &nonBlockingEnabled); +#else + int flags = fcntl(s.data, F_GETFL, 0); + if (flags == -1) { + return -1; + } + return fcntl(s.data, F_SETFL, flags | O_NONBLOCK); +#endif +} + +int set_socket_close_on_exec(NetworkSocket s) { +#ifdef _WIN32 + if (SetHandleInformation((HANDLE)s.data, HANDLE_FLAG_INHERIT, 0)) { + return 0; + } + return -1; +#else + return fcntl(s.data, F_SETFD, FD_CLOEXEC); +#endif +} +} // namespace netops +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/net/NetOps.h b/ios/Pods/Flipper-Folly/folly/net/NetOps.h new file mode 100644 index 000000000..3072d38c1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/net/NetOps.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +#include <folly/Portability.h> +#include <folly/net/NetworkSocket.h> +#include <folly/portability/IOVec.h> +#include <folly/portability/SysTypes.h> +#include <folly/portability/Time.h> +#include <folly/portability/Windows.h> + +#ifndef _WIN32 +#include <netdb.h> +#include <poll.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/un.h> + +#ifdef MSG_ERRQUEUE +#define FOLLY_HAVE_MSG_ERRQUEUE 1 +/* for struct sock_extended_err*/ +#include <linux/errqueue.h> +#endif + +#ifndef SO_EE_ORIGIN_ZEROCOPY +#define SO_EE_ORIGIN_ZEROCOPY 5 +#endif + +#ifndef SO_EE_CODE_ZEROCOPY_COPIED +#define SO_EE_CODE_ZEROCOPY_COPIED 1 +#endif + +#ifndef SO_ZEROCOPY +#define SO_ZEROCOPY 60 +#endif + +#ifndef MSG_ZEROCOPY +#define MSG_ZEROCOPY 0x4000000 +#endif + +#ifndef SOL_UDP +#define SOL_UDP 17 +#endif + +#ifndef ETH_MAX_MTU +#define ETH_MAX_MTU 0xFFFFU +#endif + +#ifndef UDP_SEGMENT +#define UDP_SEGMENT 103 +#endif + +#ifndef UDP_GRO +#define UDP_GRO 104 +#endif + +#ifndef UDP_MAX_SEGMENTS +#define UDP_MAX_SEGMENTS (1 << 6UL) +#endif + +#if !defined(MSG_WAITFORONE) && !defined(__wasm32__) +struct mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; +#endif + +#ifndef IP_BIND_ADDRESS_NO_PORT +#define IP_BIND_ADDRESS_NO_PORT 24 +#endif + +#else +#include <WS2tcpip.h> // @manual + +using nfds_t = int; +using sa_family_t = ADDRESS_FAMILY; + +// these are not supported +#define SO_EE_ORIGIN_ZEROCOPY 0 +#define SO_ZEROCOPY 0 +#define MSG_ZEROCOPY 0x0 +#define SOL_UDP 0x0 +#define UDP_SEGMENT 0x0 +#define IP_BIND_ADDRESS_NO_PORT 0 + +// We don't actually support either of these flags +// currently. +#define MSG_DONTWAIT 0x1000 +#define MSG_EOR 0 +struct msghdr { + void* msg_name; + socklen_t msg_namelen; + struct iovec* msg_iov; + size_t msg_iovlen; + void* msg_control; + size_t msg_controllen; + int msg_flags; +}; + +struct mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; + +struct sockaddr_un { + sa_family_t sun_family; + char sun_path[108]; +}; + +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH + +// These are the same, but PF_LOCAL +// isn't defined by WinSock. +#define AF_LOCAL PF_UNIX +#define PF_LOCAL PF_UNIX + +// This isn't defined by Windows, and we need to +// distinguish it from SO_REUSEADDR +#define SO_REUSEPORT 0x7001 + +// Someone thought it would be a good idea +// to define a field via a macro... +#undef s_host +#endif + +namespace folly { +namespace netops { +// Poll descriptor is intended to be byte-for-byte identical to pollfd, +// except that it is typed as containing a NetworkSocket for sane interactions. +struct PollDescriptor { + NetworkSocket fd; + int16_t events; + int16_t revents; +}; + +NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen); +int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen); +int close(NetworkSocket s); +int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen); +int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen); +int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen); +int getsockopt( + NetworkSocket s, + int level, + int optname, + void* optval, + socklen_t* optlen); +int inet_aton(const char* cp, in_addr* inp); +int listen(NetworkSocket s, int backlog); +int poll(PollDescriptor fds[], nfds_t nfds, int timeout); +ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags); +ssize_t recvfrom( + NetworkSocket s, + void* buf, + size_t len, + int flags, + sockaddr* from, + socklen_t* fromlen); +ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags); +int recvmmsg( + NetworkSocket s, + mmsghdr* msgvec, + unsigned int vlen, + unsigned int flags, + timespec* timeout); +ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags); +ssize_t sendto( + NetworkSocket s, + const void* buf, + size_t len, + int flags, + const sockaddr* to, + socklen_t tolen); +ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags); +int sendmmsg( + NetworkSocket socket, + mmsghdr* msgvec, + unsigned int vlen, + int flags); +int setsockopt( + NetworkSocket s, + int level, + int optname, + const void* optval, + socklen_t optlen); +int shutdown(NetworkSocket s, int how); +NetworkSocket socket(int af, int type, int protocol); +int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]); + +// And now we diverge from the Posix way of doing things and just do things +// our own way. +int set_socket_non_blocking(NetworkSocket s); +int set_socket_close_on_exec(NetworkSocket s); +} // namespace netops +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/net/NetworkSocket.h b/ios/Pods/Flipper-Folly/folly/net/NetworkSocket.h new file mode 100644 index 000000000..62ef82050 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/net/NetworkSocket.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <ostream> + +#include <folly/net/detail/SocketFileDescriptorMap.h> +#include <folly/portability/Windows.h> + +namespace folly { +/** + * This is just a very thin wrapper around either a file descriptor or + * a SOCKET depending on platform, along with a couple of helper methods + * for explicitly converting to/from file descriptors, even on Windows. + */ +struct NetworkSocket { +#ifdef _WIN32 + using native_handle_type = SOCKET; + static constexpr native_handle_type invalid_handle_value = INVALID_SOCKET; +#else + using native_handle_type = int; + static constexpr native_handle_type invalid_handle_value = -1; +#endif + + native_handle_type data; + + constexpr NetworkSocket() : data(invalid_handle_value) {} + constexpr explicit NetworkSocket(native_handle_type d) : data(d) {} + + template <typename T> + static NetworkSocket fromFd(T) = delete; + static NetworkSocket fromFd(int fd) { + return NetworkSocket( + netops::detail::SocketFileDescriptorMap::fdToSocket(fd)); + } + + int toFd() const { + return netops::detail::SocketFileDescriptorMap::socketToFd(data); + } + + friend constexpr bool operator==( + const NetworkSocket& a, + const NetworkSocket& b) noexcept { + return a.data == b.data; + } + + friend constexpr bool operator!=( + const NetworkSocket& a, + const NetworkSocket& b) noexcept { + return !(a == b); + } +}; + +template <class CharT, class Traits> +inline std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, + const NetworkSocket& addr) { + os << "folly::NetworkSocket(" << addr.data << ")"; + return os; +} +} // namespace folly + +namespace std { +template <> +struct hash<folly::NetworkSocket> { + size_t operator()(const folly::NetworkSocket& s) const noexcept { + return std::hash<folly::NetworkSocket::native_handle_type>()(s.data); + } +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h b/ios/Pods/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h new file mode 100644 index 000000000..d6d8c22f6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/Windows.h> + +#ifndef _WIN32 +// This can't go via the portability header, because +// the portability header depends on us. +#include <unistd.h> +#endif + +namespace folly { +namespace netops { +namespace detail { +struct SocketFileDescriptorMap { +#ifdef _WIN32 + static int close(int fd) noexcept; + static int close(SOCKET sock) noexcept; + + static SOCKET fdToSocket(int fd) noexcept; + static int socketToFd(SOCKET sock) noexcept; +#else + static int close(int fd) noexcept { + return ::close(fd); + } + + static int fdToSocket(int fd) noexcept { + return fd; + } + static int socketToFd(int sock) noexcept { + return sock; + } +#endif +}; +} // namespace detail +} // namespace netops +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Asm.h b/ios/Pods/Flipper-Folly/folly/portability/Asm.h new file mode 100644 index 000000000..a0fd059c4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Asm.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +#include <cstdint> + +#ifdef _MSC_VER +#include <intrin.h> +#endif + +namespace folly { +inline void asm_volatile_memory() { +#if defined(__GNUC__) || defined(__clang__) + asm volatile("" : : : "memory"); +#elif defined(_MSC_VER) + ::_ReadWriteBarrier(); +#endif +} + +inline void asm_volatile_pause() { +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + ::_mm_pause(); +#elif defined(__i386__) || FOLLY_X64 || (__mips_isa_rev > 1) + asm volatile("pause"); +#elif FOLLY_AARCH64 || (defined(__arm__) && !(__ARM_ARCH < 7)) + asm volatile("yield"); +#elif FOLLY_PPC64 + asm volatile("or 27,27,27"); +#endif +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Atomic.h b/ios/Pods/Flipper-Folly/folly/portability/Atomic.h new file mode 100644 index 000000000..b5136c5fc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Atomic.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#ifdef _WIN32 + +#include <intrin.h> +#include <stdint.h> + +#include <folly/Portability.h> + +FOLLY_ALWAYS_INLINE +int64_t __sync_fetch_and_add(volatile int64_t* ptr, int64_t value) { + return _InterlockedExchangeAdd64(ptr, value); +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Builtins.cpp b/ios/Pods/Flipper-Folly/folly/portability/Builtins.cpp new file mode 100644 index 000000000..a0d3fd5a2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Builtins.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Builtins.h> + +#ifdef _WIN32 +#include <folly/portability/Windows.h> + +namespace folly { +namespace portability { +namespace detail { +void call_flush_instruction_cache_self_pid(void* begin, size_t size) { + FlushInstructionCache(GetCurrentProcess(), begin, size); +} +} // namespace detail +} // namespace portability +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Builtins.h b/ios/Pods/Flipper-Folly/folly/portability/Builtins.h new file mode 100644 index 000000000..971cb8819 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Builtins.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#if defined(_WIN32) && !defined(__clang__) +#include <assert.h> +#include <folly/Portability.h> +#include <intrin.h> +#include <stdint.h> + +namespace folly { +namespace portability { +namespace detail { +void call_flush_instruction_cache_self_pid(void* begin, size_t size); +} +} // namespace portability +} // namespace folly + +FOLLY_ALWAYS_INLINE void __builtin___clear_cache(char* begin, char* end) { + if (folly::kIsArchAmd64) { + // x86_64 doesn't require the instruction cache to be flushed after + // modification. + } else { + // Default to flushing it for everything else, such as ARM. + folly::portability::detail::call_flush_instruction_cache_self_pid( + static_cast<void*>(begin), static_cast<size_t>(end - begin)); + } +} + +#if !defined(_MSC_VER) || (_MSC_VER < 1923) +FOLLY_ALWAYS_INLINE int __builtin_clz(unsigned int x) { + unsigned long index; + return int(_BitScanReverse(&index, (unsigned long)x) ? 31 - index : 32); +} + +FOLLY_ALWAYS_INLINE int __builtin_clzl(unsigned long x) { + return __builtin_clz((unsigned int)x); +} + +#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) +FOLLY_ALWAYS_INLINE int __builtin_clzll(unsigned long long x) { + if (x == 0) { + return 64; + } + unsigned int msb = (unsigned int)(x >> 32); + unsigned int lsb = (unsigned int)x; + return (msb != 0) ? __builtin_clz(msb) : 32 + __builtin_clz(lsb); +} +#else +FOLLY_ALWAYS_INLINE int __builtin_clzll(unsigned long long x) { + unsigned long index; + return int(_BitScanReverse64(&index, x) ? 63 - index : 64); +} +#endif + +FOLLY_ALWAYS_INLINE int __builtin_ctz(unsigned int x) { + unsigned long index; + return int(_BitScanForward(&index, (unsigned long)x) ? index : 32); +} + +FOLLY_ALWAYS_INLINE int __builtin_ctzl(unsigned long x) { + return __builtin_ctz((unsigned int)x); +} + +#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) +FOLLY_ALWAYS_INLINE int __builtin_ctzll(unsigned long long x) { + unsigned long index; + unsigned int msb = (unsigned int)(x >> 32); + unsigned int lsb = (unsigned int)x; + if (lsb != 0) { + return (int)(_BitScanForward(&index, lsb) ? index : 64); + } else { + return (int)(_BitScanForward(&index, msb) ? index + 32 : 64); + } +} +#else +FOLLY_ALWAYS_INLINE int __builtin_ctzll(unsigned long long x) { + unsigned long index; + return int(_BitScanForward64(&index, x) ? index : 64); +} +#endif +#endif // !defined(_MSC_VER) || (_MSC_VER < 1923) + +FOLLY_ALWAYS_INLINE int __builtin_ffs(int x) { + unsigned long index; + return int(_BitScanForward(&index, (unsigned long)x) ? index + 1 : 0); +} + +FOLLY_ALWAYS_INLINE int __builtin_ffsl(long x) { + return __builtin_ffs(int(x)); +} + +#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) +FOLLY_ALWAYS_INLINE int __builtin_ffsll(long long x) { + int ctzll = __builtin_ctzll((unsigned long long)x); + return ctzll != 64 ? ctzll + 1 : 0; +} +#else +FOLLY_ALWAYS_INLINE int __builtin_ffsll(long long x) { + unsigned long index; + return int(_BitScanForward64(&index, (unsigned long long)x) ? index + 1 : 0); +} + +FOLLY_ALWAYS_INLINE int __builtin_popcount(unsigned int x) { + return int(__popcnt(x)); +} + +#if !defined(_MSC_VER) || (_MSC_VER < 1923) +FOLLY_ALWAYS_INLINE int __builtin_popcountl(unsigned long x) { + static_assert(sizeof(x) == 4, ""); + return int(__popcnt(x)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER < 1923) +#endif + +#if !defined(_MSC_VER) || (_MSC_VER < 1923) +#if defined(_M_IX86) +FOLLY_ALWAYS_INLINE int __builtin_popcountll(unsigned long long x) { + return int(__popcnt((unsigned int)(x >> 32))) + + int(__popcnt((unsigned int)x)); +} +#elif defined(_M_X64) +FOLLY_ALWAYS_INLINE int __builtin_popcountll(unsigned long long x) { + return int(__popcnt64(x)); +} +#endif +#endif // !defined(_MSC_VER) || (_MSC_VER < 1923) + +FOLLY_ALWAYS_INLINE void* __builtin_return_address(unsigned int frame) { + // I really hope frame is zero... + (void)frame; + assert(frame == 0); + return _ReturnAddress(); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Config.h b/ios/Pods/Flipper-Folly/folly/portability/Config.h new file mode 100644 index 000000000..8d500cc21 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Config.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FOLLY_NO_CONFIG +#include <folly/folly-config.h> +#endif + +#if __has_include(<features.h>) +#include <features.h> // @manual +#endif + +#if __has_include(<bits/c++config.h>) +#include <bits/c++config.h> // @manual +#endif + +#ifdef __ANDROID__ +#include <android/api-level.h> // @manual +#endif + +#ifdef __APPLE__ +#include <Availability.h> // @manual +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Constexpr.h b/ios/Pods/Flipper-Folly/folly/portability/Constexpr.h new file mode 100644 index 000000000..c88210236 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Constexpr.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/CPortability.h> + +#include <cstdint> +#include <cstring> +#include <type_traits> + +namespace folly { + +namespace detail { + +template <typename Char> +constexpr size_t constexpr_strlen_internal(const Char* s, size_t len) { + // clang-format off + return + *(s + 0) == Char(0) ? len + 0 : + *(s + 1) == Char(0) ? len + 1 : + *(s + 2) == Char(0) ? len + 2 : + *(s + 3) == Char(0) ? len + 3 : + *(s + 4) == Char(0) ? len + 4 : + *(s + 5) == Char(0) ? len + 5 : + *(s + 6) == Char(0) ? len + 6 : + *(s + 7) == Char(0) ? len + 7 : + constexpr_strlen_internal(s + 8, len + 8); + // clang-format on +} +static_assert( + constexpr_strlen_internal("123456789", 0) == 9, + "Someone appears to have broken constexpr_strlen..."); + +template <typename Char> +constexpr int constexpr_strcmp_internal(const Char* s1, const Char* s2) { + return (*s1 == '\0' || *s1 != *s2) + ? (static_cast<int>(*s1 - *s2)) + : constexpr_strcmp_internal(s1 + 1, s2 + 1); +} +} // namespace detail + +template <typename Char> +constexpr size_t constexpr_strlen(const Char* s) { + return detail::constexpr_strlen_internal(s, 0); +} + +template <> +constexpr size_t constexpr_strlen(const char* s) { +#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins) + // clang provides a constexpr builtin + return __builtin_strlen(s); +#elif defined(__GLIBCXX__) && !defined(__clang__) + // strlen() happens to already be constexpr under gcc + return std::strlen(s); +#else + return detail::constexpr_strlen_internal(s, 0); +#endif +} + +template <typename Char> +constexpr int constexpr_strcmp(const Char* s1, const Char* s2) { + return detail::constexpr_strcmp_internal(s1, s2); +} + +template <> +constexpr int constexpr_strcmp(const char* s1, const char* s2) { +#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins) + // clang provides a constexpr builtin + return __builtin_strcmp(s1, s2); +#elif defined(__GLIBCXX__) && !defined(__clang__) + // strcmp() happens to already be constexpr under gcc + return std::strcmp(s1, s2); +#else + return detail::constexpr_strcmp_internal(s1, s2); +#endif +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Dirent.cpp b/ios/Pods/Flipper-Folly/folly/portability/Dirent.cpp new file mode 100644 index 000000000..00005c31d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Dirent.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Dirent.h> + +#ifdef _WIN32 +#include <stdlib.h> +#include <string> + +#include <folly/portability/Windows.h> + +struct DIR { + dirent dir{}; + HANDLE searchHandle{INVALID_HANDLE_VALUE}; + int entriesRead{0}; + char currentName[MAX_PATH * 3]; + std::string pattern; + + int close() { + return FindClose(searchHandle) ? 0 : -1; + } + + DIR* open() { + wchar_t patternBuf[MAX_PATH + 3]; + size_t len; + + if (pattern.empty()) { + return nullptr; + } + + if (mbstowcs_s(&len, patternBuf, MAX_PATH, pattern.c_str(), MAX_PATH - 2)) { + return nullptr; + } + + // `len` includes the trailing NUL + if (len) { + len--; + } + if (len && patternBuf[len - 1] != '/' && patternBuf[len - 1] != '\\') { + patternBuf[len++] = '\\'; + } + patternBuf[len++] = '*'; + patternBuf[len] = 0; + + WIN32_FIND_DATAW fdata; + HANDLE h = FindFirstFileW(patternBuf, &fdata); + if (h == INVALID_HANDLE_VALUE) { + return nullptr; + } + + searchHandle = h; + dir.d_name = currentName; + if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) { + return nullptr; + } + + setEntryType(fdata.dwFileAttributes); + return this; + } + + dirent* nextDir() { + if (entriesRead) { + WIN32_FIND_DATAW fdata; + if (!FindNextFileW(searchHandle, &fdata)) { + return nullptr; + } + + if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) { + errno = EBADF; + return nullptr; + } + setEntryType(fdata.dwFileAttributes); + } + + entriesRead++; + return &dir; + } + + private: + void setEntryType(DWORD attr) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + dir.d_type = DT_DIR; + } else { + dir.d_type = DT_REG; + } + } +}; + +extern "C" { +int closedir(DIR* dir) { + auto ret = dir->close(); + delete dir; + return ret; +} + +DIR* opendir(const char* name) { + auto dir = new DIR(); + dir->pattern = name; + if (!dir->open()) { + delete dir; + return nullptr; + } + return dir; +} + +dirent* readdir(DIR* dir) { + return dir->nextDir(); +} + +int readdir_r(DIR* dir, dirent* buf, dirent** ent) { + if (!dir || !buf || !ent) { + return EBADF; + } + *ent = dir->nextDir(); + // Our normal readdir implementation is actually + // already reentrant, but we need to do this copy + // in case the caller expects buf to have the value. + if (*ent) { + *buf = dir->dir; + } + return 0; +} + +void rewinddir(DIR* dir) { + dir->close(); + dir->open(); +} +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Dirent.h b/ios/Pods/Flipper-Folly/folly/portability/Dirent.h new file mode 100644 index 000000000..9bb3fefab --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Dirent.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <dirent.h> +#else + +#define DT_UNKNOWN 0 +#define DT_DIR 1 +#define DT_REG 2 +#define DT_LNK 3 +struct dirent { + unsigned char d_type; + char* d_name; +}; + +struct DIR; + +extern "C" { +int closedir(DIR* dir); +DIR* opendir(const char* name); +dirent* readdir(DIR* dir); +int readdir_r(DIR* dir, dirent* buf, dirent** ent); +void rewinddir(DIR* dir); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Event.h b/ios/Pods/Flipper-Folly/folly/portability/Event.h new file mode 100644 index 000000000..9832e033b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Event.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#ifdef _MSC_VER +// This needs to be before the libevent include. +#include <folly/portability/Windows.h> +#endif + +#include <event.h> + +#ifdef _MSC_VER +#include <event2/event_compat.h> // @manual +#include <folly/portability/Fcntl.h> +#endif + +#include <folly/net/detail/SocketFileDescriptorMap.h> + +namespace folly { +using libevent_fd_t = decltype(event::ev_fd); +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Fcntl.cpp b/ios/Pods/Flipper-Folly/folly/portability/Fcntl.cpp new file mode 100644 index 000000000..e753c77a9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Fcntl.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Fcntl.h> + +#ifdef _WIN32 +#include <folly/portability/Sockets.h> +#include <folly/portability/SysStat.h> +#include <folly/portability/Windows.h> + +namespace folly { +namespace portability { +namespace fcntl { +int creat(char const* fn, int pm) { + return _creat(fn, pm); +} + +int fcntl(int fd, int cmd, ...) { + va_list args; + int res = -1; + va_start(args, cmd); + switch (cmd) { + case F_GETFD: { + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h != INVALID_HANDLE_VALUE) { + DWORD flags; + if (GetHandleInformation(h, &flags)) { + res = int(flags & HANDLE_FLAG_INHERIT); + } + } + break; + } + case F_SETFD: { + int flags = va_arg(args, int); + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h != INVALID_HANDLE_VALUE) { + if (SetHandleInformation( + h, HANDLE_FLAG_INHERIT, (DWORD)(flags & FD_CLOEXEC))) { + res = 0; + } + } + break; + } + case F_GETFL: { + // No idea how to get the IO blocking mode, so return 0. + res = 0; + break; + } + case F_SETFL: { + int flags = va_arg(args, int); + // If it's not a socket, it's probably a pipe. + if (folly::portability::sockets::is_fh_socket(fd)) { + SOCKET s = (SOCKET)_get_osfhandle(fd); + if (s != INVALID_SOCKET) { + u_long nonBlockingEnabled = (flags & O_NONBLOCK) ? 1 : 0; + res = ioctlsocket(s, FIONBIO, &nonBlockingEnabled); + } + } else { + HANDLE p = (HANDLE)_get_osfhandle(fd); + if (GetFileType(p) == FILE_TYPE_PIPE) { + DWORD newMode = PIPE_READMODE_BYTE; + newMode |= (flags & O_NONBLOCK) ? PIPE_NOWAIT : PIPE_WAIT; + if (SetNamedPipeHandleState(p, &newMode, nullptr, nullptr)) { + res = 0; + } + } + } + break; + } + } + va_end(args); + return res; +} + +int open(char const* fn, int of, int pm) { + int fh; + int realMode = _S_IREAD; + if ((of & _O_RDWR) == _O_RDWR) { + realMode = _S_IREAD | _S_IWRITE; + } else if ((of & _O_WRONLY) == _O_WRONLY) { + realMode = _S_IWRITE; + } else if ((of & _O_RDONLY) != _O_RDONLY) { + // One of these needs to be present, just fail if + // none are. + return -1; + } + if (!strcmp(fn, "/dev/null")) { + // Windows doesn't have a /dev/null, but it does have + // NUL, which achieves the same result. + fn = "NUL"; + } + errno_t res = _sopen_s(&fh, fn, of, _SH_DENYNO, realMode); + return res ? -1 : fh; +} + +int posix_fallocate(int fd, off_t offset, off_t len) { + // We'll pretend we always have enough space. We + // can't exactly pre-allocate on windows anyways. + return 0; +} +} // namespace fcntl +} // namespace portability +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Fcntl.h b/ios/Pods/Flipper-Folly/folly/portability/Fcntl.h new file mode 100644 index 000000000..8e968edf6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Fcntl.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <fcntl.h> + +#ifdef _WIN32 +#include <sys/types.h> + +#include <folly/portability/Windows.h> + +#include <folly/Portability.h> + +// I have no idea what the normal values for these are, +// and really don't care what they are. They're only used +// within fcntl, so it's not an issue. +#define FD_CLOEXEC HANDLE_FLAG_INHERIT +#define O_NONBLOCK 1 +#define O_CLOEXEC _O_NOINHERIT +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#ifdef HAVE_POSIX_FALLOCATE +#undef HAVE_POSIX_FALLOCATE +#endif +#define HAVE_POSIX_FALLOCATE 1 + +// See portability/Unistd.h for why these need to be in a namespace +// rather then extern "C". +namespace folly { +namespace portability { +namespace fcntl { +int creat(char const* fn, int pm); +int fcntl(int fd, int cmd, ...); +int posix_fallocate(int fd, off_t offset, off_t len); +int open(char const* fn, int of, int pm = 0); +} // namespace fcntl +} // namespace portability +} // namespace folly + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::fcntl; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/GFlags.h b/ios/Pods/Flipper-Folly/folly/portability/GFlags.h new file mode 100644 index 000000000..97df834ab --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/GFlags.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/Config.h> + +#if !FOLLY_HAVE_LIBGFLAGS +// glog/logging.h is dependent on this implementation detail +// being defined otherwise it undefines all of this -_-.... +// +// Also, this is deliberately expanded such that places using +// it directly break loudly. (C will break louder than C++, but oh well) +#define DECLARE_VARIABLE() \ + static_assert(false, "You shouldn't be using GFlags internals."); + +#define FOLLY_DECLARE_FLAG(_type, _shortType, _name) \ + namespace fL##_shortType { \ + extern _type FLAGS_##_name; \ + } \ + using fL##_shortType::FLAGS_##_name + +#define DECLARE_bool(_name) FOLLY_DECLARE_FLAG(bool, B, _name) +#define DECLARE_double(_name) FOLLY_DECLARE_FLAG(double, D, _name) +#define DECLARE_int32(_name) FOLLY_DECLARE_FLAG(int, I, _name) +#define DECLARE_int64(_name) FOLLY_DECLARE_FLAG(long long, I64, _name) +#define DECLARE_uint32(_name) FOLLY_DECLARE_FLAG(unsigned long, U32, _name) +#define DECLARE_uint64(_name) FOLLY_DECLARE_FLAG(unsigned long long, U64, _name) +#define DECLARE_string(_name) FOLLY_DECLARE_FLAG(std::string, S, _name) + +#define FOLLY_DEFINE_FLAG(_type, _shortType, _name, _default) \ + namespace fL##_shortType { \ + _type FLAGS_##_name = _default; \ + } \ + using fL##_shortType::FLAGS_##_name + +#define DEFINE_bool(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(bool, B, _name, _default) +#define DEFINE_double(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(double, D, _name, _default) +#define DEFINE_int32(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(int, I, _name, _default) +#define DEFINE_int64(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(long long, I64, _name, _default) +#define DEFINE_uint32(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(unsigned long, U32, _name, _default) +#define DEFINE_uint64(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(unsigned long long, U64, _name, _default) +#define DEFINE_string(_name, _default, _description) \ + FOLLY_DEFINE_FLAG(std::string, S, _name, _default) + +namespace google { +class FlagSaver {}; +} // namespace google + +#else +#include <gflags/gflags.h> +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/GMock.h b/ios/Pods/Flipper-Folly/folly/portability/GMock.h new file mode 100644 index 000000000..6147667a2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/GMock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// These _must_ be included before gmock.h, because +// it tries to include <io.h> on it's own, but we +// override all of those functions in Unistd.h :( +#include <folly/portability/Unistd.h> +#include <folly/portability/Windows.h> + +#include <folly/Portability.h> + +// Disable a couple of warnings due to GMock exporting classes +// that derive from stdlib classes which aren't explicitly exported. +FOLLY_PUSH_WARNING +FOLLY_MSVC_DISABLE_WARNING(4251) +FOLLY_MSVC_DISABLE_WARNING(4275) +#include <gmock/gmock.h> +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/portability/GTest.h b/ios/Pods/Flipper-Folly/folly/portability/GTest.h new file mode 100644 index 000000000..c834eb215 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/GTest.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// These _must_ be included before gtest.h, because +// it tries to include <io.h> on it's own, but we +// override all of those functions in Unistd.h :( +#include <folly/portability/Unistd.h> +#include <folly/portability/Windows.h> + +#include <folly/Portability.h> + +// Disable a couple of warnings due to GTest exporting classes +// that derive from stdlib classes which aren't explicitly exported. +FOLLY_PUSH_WARNING +FOLLY_MSVC_DISABLE_WARNING(4251) +FOLLY_MSVC_DISABLE_WARNING(4275) +#include <gtest/gtest.h> +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/portability/IOVec.h b/ios/Pods/Flipper-Folly/folly/portability/IOVec.h new file mode 100644 index 000000000..1e46c247f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/IOVec.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// This file only exists because without it there would be +// a circular dependency between SysUio.h, Sockets.h, and Unistd.h +#ifndef _WIN32 +#include <limits.h> +#include <sys/uio.h> +#else +#include <stdlib.h> + +#define UIO_MAXIOV 16 +#define IOV_MAX UIO_MAXIOV + +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Libgen.cpp b/ios/Pods/Flipper-Folly/folly/portability/Libgen.cpp new file mode 100644 index 000000000..9dfe6524f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Libgen.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Libgen.h> + +#include <cstring> + +namespace folly { +namespace portability { +static char mutableDot[] = {'.', '\0'}; +char* internal_dirname(char* path) { + if (path == nullptr || !strcmp(path, "")) { + return mutableDot; + } + if (!strcmp(path, "/") || !strcmp(path, "\\")) { + return path; + } + + size_t len = strlen(path); + if (path[len - 1] == '/' || path[len - 1] == '\\') { + path[len - 1] = '\0'; + } + + char* pos = strrchr(path, '/'); + if (strrchr(path, '\\') > pos) { + pos = strrchr(path, '\\'); + } + if (pos == nullptr) { + return mutableDot; + } + + if (pos == path) { + *(pos + 1) = '\0'; + } else { + *pos = '\0'; + } + return path; +} +} // namespace portability +} // namespace folly + +#ifdef _WIN32 +extern "C" char* dirname(char* path) { + return folly::portability::internal_dirname(path); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Libgen.h b/ios/Pods/Flipper-Folly/folly/portability/Libgen.h new file mode 100644 index 000000000..dd590f18b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Libgen.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { +namespace portability { +char* internal_dirname(char* path); +} // namespace portability +} // namespace folly + +#ifndef _WIN32 +#include <libgen.h> +#else +extern "C" char* dirname(char* path); +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Malloc.cpp b/ios/Pods/Flipper-Folly/folly/portability/Malloc.cpp new file mode 100644 index 000000000..9e7d81c3d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Malloc.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Malloc.h> + +#if !defined(USE_JEMALLOC) && !defined(FOLLY_USE_JEMALLOC) +#if defined(__APPLE__) && !defined(FOLLY_HAVE_MALLOC_USABLE_SIZE) +#include <malloc/malloc.h> // @manual + +extern "C" size_t malloc_usable_size(void* ptr) { + return malloc_size(ptr); +} +#elif defined(_WIN32) +extern "C" size_t malloc_usable_size(void* addr) { + return _msize(addr); +} +#endif +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Malloc.h b/ios/Pods/Flipper-Folly/folly/portability/Malloc.h new file mode 100644 index 000000000..16a95c5b1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Malloc.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdlib.h> + +#include <folly/CPortability.h> +#include <folly/portability/Config.h> + +#if (defined(USE_JEMALLOC) || defined(FOLLY_USE_JEMALLOC)) && !FOLLY_SANITIZE +#if defined(FOLLY_ASSUME_NO_JEMALLOC) +#error \ + "Both USE_JEMALLOC/FOLLY_USE_JEMALLOC and FOLLY_ASSUME_NO_JEMALLOC defined" +#endif +// JEMalloc provides it's own implementation of +// malloc_usable_size, and that's what we should be using. +#if defined(__FreeBSD__) +#include <malloc_np.h> // @manual +#else +#include <jemalloc/jemalloc.h> // @manual +#endif +#else +#if !defined(__FreeBSD__) +#if __has_include(<malloc.h>) +#include <malloc.h> +#endif +#endif + +#if defined(__APPLE__) && !defined(FOLLY_HAVE_MALLOC_USABLE_SIZE) +// MacOS doesn't have malloc_usable_size() +extern "C" size_t malloc_usable_size(void* ptr); +#elif defined(_WIN32) +extern "C" size_t malloc_usable_size(void* ptr); +#endif +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Math.h b/ios/Pods/Flipper-Folly/folly/portability/Math.h new file mode 100644 index 000000000..dfcaf03ed --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Math.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cmath> + +namespace folly { + +#if !defined(__ANDROID__) && !defined(__UCLIBC__) + +/** + * Most platforms hopefully provide std::nextafter, std::remainder. + */ + +/* using override */ using std::nextafter; +/* using override */ using std::remainder; + +#else // !__ANDROID__ && !__UCLIBC__ + +/** + * On Android and uclibc, std::nextafter or std::remainder isn't implemented. + * However, the C functions and compiler builtins are still provided. Using the + * GCC builtin is actually slightly faster, as they're constexpr and the use + * cases within folly are in constexpr context. + */ + +#if defined(__GNUC__) && !defined(__clang__) + +constexpr float nextafter(float x, float y) { + return __builtin_nextafterf(x, y); +} + +constexpr double nextafter(double x, double y) { + return __builtin_nextafter(x, y); +} + +constexpr long double nextafter(long double x, long double y) { + return __builtin_nextafterl(x, y); +} + +#else // __GNUC__ + +inline float nextafter(float x, float y) { + return ::nextafterf(x, y); +} + +inline double nextafter(double x, double y) { + return ::nextafter(x, y); +} + +inline long double nextafter(long double x, long double y) { + return ::nextafterl(x, y); +} + +#endif // __GNUC__ + +/** + * On uclibc, std::remainder isn't implemented. + * Implement it using builtin versions + */ +#ifdef __UCLIBC__ +constexpr float remainder(float x, float y) { + return __builtin_remainderf(x, y); +} + +constexpr double remainder(double x, double y) { + return __builtin_remainder(x, y); +} + +constexpr long double remainder(long double x, long double y) { + return __builtin_remainderl(x, y); +} +#endif // __UCLIBC__ + +#endif // !__ANDROID__ && !__UCLIBC__ +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Memory.h b/ios/Pods/Flipper-Folly/folly/portability/Memory.h new file mode 100644 index 000000000..8cd8ebc9f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Memory.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Memory.h> // @shim + +namespace folly { +namespace detail { +using folly::aligned_free; +using folly::aligned_malloc; +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.cpp b/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.cpp new file mode 100644 index 000000000..e5f80091f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/OpenSSL.h> +#include <folly/ssl/detail/OpenSSLThreading.h> + +#include <stdexcept> + +namespace folly { +namespace portability { +namespace ssl { + +#ifdef OPENSSL_IS_BORINGSSL +int SSL_CTX_set1_sigalgs_list(SSL_CTX*, const char*) { + return 1; // 0 implies error +} + +int TLS1_get_client_version(SSL* s) { + // Note that this isn't the client version, and the API to + // get this has been hidden. It may be found by parsing the + // ClientHello (there is a callback via the SSL_HANDSHAKE struct) + return s->version; +} +#endif + +#if FOLLY_OPENSSL_IS_100 +uint32_t SSL_CIPHER_get_id(const SSL_CIPHER* c) { + return c->id; +} + +int TLS1_get_client_version(const SSL* s) { + return (s->client_version >> 8) == TLS1_VERSION_MAJOR ? s->client_version : 0; +} +#endif + +#if FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 +int X509_get_signature_nid(X509* cert) { + return OBJ_obj2nid(cert->sig_alg->algorithm); +} +#endif + +#if FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 || FOLLY_OPENSSL_IS_102 +int SSL_CTX_up_ref(SSL_CTX* ctx) { + return CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX); +} + +int SSL_SESSION_up_ref(SSL_SESSION* session) { + return CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION); +} + +int X509_up_ref(X509* x) { + return CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); +} + +void X509_STORE_CTX_set0_verified_chain( + X509_STORE_CTX* ctx, + STACK_OF(X509) * sk) { + sk_X509_pop_free(ctx->chain, X509_free); + ctx->chain = sk; +} + +int X509_STORE_up_ref(X509_STORE* v) { + return CRYPTO_add(&v->references, 1, CRYPTO_LOCK_X509_STORE); +} + +int EVP_PKEY_up_ref(EVP_PKEY* evp) { + return CRYPTO_add(&evp->references, 1, CRYPTO_LOCK_EVP_PKEY); +} + +void RSA_get0_key( + const RSA* r, + const BIGNUM** n, + const BIGNUM** e, + const BIGNUM** d) { + if (n != nullptr) { + *n = r->n; + } + if (e != nullptr) { + *e = r->e; + } + if (d != nullptr) { + *d = r->d; + } +} + +RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_RSA) { + return nullptr; + } + return pkey->pkey.rsa; +} + +DSA* EVP_PKEY_get0_DSA(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_DSA) { + return nullptr; + } + return pkey->pkey.dsa; +} + +DH* EVP_PKEY_get0_DH(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_DH) { + return nullptr; + } + return pkey->pkey.dh; +} + +EC_KEY* EVP_PKEY_get0_EC_KEY(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_EC) { + return nullptr; + } + return pkey->pkey.ec; +} +#endif + +#if !FOLLY_OPENSSL_IS_110 +BIO_METHOD* BIO_meth_new(int type, const char* name) { + BIO_METHOD* method = (BIO_METHOD*)OPENSSL_malloc(sizeof(BIO_METHOD)); + if (method == nullptr) { + return nullptr; + } + memset(method, 0, sizeof(BIO_METHOD)); + method->type = type; + method->name = name; + return method; +} + +void BIO_meth_free(BIO_METHOD* biom) { + OPENSSL_free((void*)biom); +} + +int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int)) { + biom->bread = read; + return 1; +} + +int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int)) { + biom->bwrite = write; + return 1; +} + +int BIO_meth_set_puts(BIO_METHOD* biom, int (*bputs)(BIO*, const char*)) { + biom->bputs = bputs; + return 1; +} + +int BIO_meth_set_gets(BIO_METHOD* biom, int (*bgets)(BIO*, char*, int)) { + biom->bgets = bgets; + return 1; +} + +int BIO_meth_set_ctrl(BIO_METHOD* biom, long (*ctrl)(BIO*, int, long, void*)) { + biom->ctrl = ctrl; + return 1; +} + +int BIO_meth_set_create(BIO_METHOD* biom, int (*create)(BIO*)) { + biom->create = create; + return 1; +} + +int BIO_meth_set_destroy(BIO_METHOD* biom, int (*destroy)(BIO*)) { + biom->destroy = destroy; + return 1; +} + +void BIO_set_data(BIO* bio, void* ptr) { + bio->ptr = ptr; +} + +void* BIO_get_data(BIO* bio) { + return bio->ptr; +} + +void BIO_set_init(BIO* bio, int init) { + bio->init = init; +} + +void BIO_set_shutdown(BIO* bio, int shutdown) { + bio->shutdown = shutdown; +} + +const SSL_METHOD* TLS_server_method(void) { + return TLSv1_2_server_method(); +} + +const SSL_METHOD* TLS_client_method(void) { + return TLSv1_2_client_method(); +} + +const char* SSL_SESSION_get0_hostname(const SSL_SESSION* s) { + return s->tlsext_hostname; +} + +unsigned char* ASN1_STRING_get0_data(const ASN1_STRING* x) { + return ASN1_STRING_data((ASN1_STRING*)x); +} + +int SSL_SESSION_has_ticket(const SSL_SESSION* s) { + return (s->tlsext_ticklen > 0) ? 1 : 0; +} + +unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION* s) { + return s->tlsext_tick_lifetime_hint; +} + +// This is taken from OpenSSL 1.1.0 +int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g) { + /* If the fields p and g in d are nullptr, the corresponding input + * parameters MUST not be nullptr. q may remain nullptr. + */ + if (dh == nullptr || (dh->p == nullptr && p == nullptr) || + (dh->g == nullptr && g == nullptr)) { + return 0; + } + + if (p != nullptr) { + BN_free(dh->p); + dh->p = p; + } + if (q != nullptr) { + BN_free(dh->q); + dh->q = q; + } + if (g != nullptr) { + BN_free(dh->g); + dh->g = g; + } + + // In OpenSSL 1.1.0, DH_set0_pqg also sets + // dh->length = BN_num_bits(q) + // With OpenSSL 1.0.2, the output of openssl dhparam -C 2048 doesn't set + // the length field. So as far as the compat lib is concerned, this wrapper + // mimics the functionality of OpenSSL 1.0.2 + // Note: BoringSSL doesn't even have a length field anymore, just something + // called 'priv_length'. Let's not mess with that for now. + + return 1; +} + +void DH_get0_pqg( + const DH* dh, + const BIGNUM** p, + const BIGNUM** q, + const BIGNUM** g) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (p != nullptr) { + *p = dh->p; + } + if (q != nullptr) { + *q = dh->q; + } + if (g != nullptr) { + *g = dh->g; + } +} + +void DH_get0_key( + const DH* dh, + const BIGNUM** pub_key, + const BIGNUM** priv_key) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (pub_key != nullptr) { + *pub_key = dh->pub_key; + } + if (priv_key != nullptr) { + *priv_key = dh->priv_key; + } +} + +long DH_get_length(const DH* dh) { + return dh->length; +} + +int DH_set_length(DH* dh, long length) { + if (dh != nullptr) { + dh->length = length; + return 1; + } else { + return 0; + } +} + +void DSA_get0_pqg( + const DSA* dsa, + const BIGNUM** p, + const BIGNUM** q, + const BIGNUM** g) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (p != nullptr) { + *p = dsa->p; + } + if (q != nullptr) { + *q = dsa->q; + } + if (g != nullptr) { + *g = dsa->g; + } +} + +void DSA_get0_key( + const DSA* dsa, + const BIGNUM** pub_key, + const BIGNUM** priv_key) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (pub_key != nullptr) { + *pub_key = dsa->pub_key; + } + if (priv_key != nullptr) { + *priv_key = dsa->priv_key; + } +} + +STACK_OF(X509_OBJECT) * X509_STORE_get0_objects(X509_STORE* store) { + return store->objs; +} + +X509* X509_STORE_CTX_get0_cert(X509_STORE_CTX* ctx) { + return ctx->cert; +} + +STACK_OF(X509) * X509_STORE_CTX_get0_chain(X509_STORE_CTX* ctx) { + return X509_STORE_CTX_get_chain(ctx); +} + +STACK_OF(X509) * X509_STORE_CTX_get0_untrusted(X509_STORE_CTX* ctx) { + return ctx->untrusted; +} + +EVP_MD_CTX* EVP_MD_CTX_new() { + EVP_MD_CTX* ctx = (EVP_MD_CTX*)OPENSSL_malloc(sizeof(EVP_MD_CTX)); + if (!ctx) { + throw std::runtime_error("Cannot allocate EVP_MD_CTX"); + } + EVP_MD_CTX_init(ctx); + return ctx; +} + +void EVP_MD_CTX_free(EVP_MD_CTX* ctx) { + if (ctx) { + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +HMAC_CTX* HMAC_CTX_new() { + HMAC_CTX* ctx = (HMAC_CTX*)OPENSSL_malloc(sizeof(HMAC_CTX)); + if (!ctx) { + throw std::runtime_error("Cannot allocate HMAC_CTX"); + } + HMAC_CTX_init(ctx); + return ctx; +} + +void HMAC_CTX_free(HMAC_CTX* ctx) { + if (ctx) { + HMAC_CTX_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +bool RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + /** + * If the fields n and e in r are nullptr, the corresponding input parameters + * MUST be non-nullptr for n and e. d may be left NULL (in case only the + * public key is used). + */ + if ((r->n == nullptr && n == nullptr) || (r->e == nullptr && e == nullptr)) { + return false; + } + if (n != nullptr) { + BN_free(r->n); + r->n = n; + } + if (e != nullptr) { + BN_free(r->e); + r->e = e; + } + if (d != nullptr) { + BN_free(r->d); + r->d = d; + } + return true; +} + +void RSA_get0_factors(const RSA* r, const BIGNUM** p, const BIGNUM** q) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (p != nullptr) { + *p = r->p; + } + if (q != nullptr) { + *q = r->q; + } +} + +void RSA_get0_crt_params( + const RSA* r, + const BIGNUM** dmp1, + const BIGNUM** dmq1, + const BIGNUM** iqmp) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (dmp1 != nullptr) { + *dmp1 = r->dmp1; + } + if (dmq1 != nullptr) { + *dmq1 = r->dmq1; + } + if (iqmp != nullptr) { + *iqmp = r->iqmp; + } +} + +int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (r == nullptr || s == nullptr) { + return 0; + } + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void ECDSA_SIG_get0( + const ECDSA_SIG* sig, + const BIGNUM** pr, + const BIGNUM** ps) { + // Based off of https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes + if (pr != nullptr) { + *pr = sig->r; + } + if (ps != nullptr) { + *ps = sig->s; + } +} + +/** + * Compatibility shim for OpenSSL < 1.1.0. + * + * For now, options and settings are ignored. We implement the most common + * behavior, which is to add all digests, ciphers, and strings. + */ +int OPENSSL_init_ssl(uint64_t, const OPENSSL_INIT_SETTINGS*) { + // OpenSSL >= 1.1.0 handles initializing the library, adding digests & + // ciphers, loading strings. Additionally, OpenSSL >= 1.1.0 uses platform + // native threading & mutexes, which means that we should handle setting up + // the necessary threading initialization in the compat layer as well. + SSL_library_init(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + OpenSSL_add_all_algorithms(); + + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + // The caller should have used SSLContext::setLockTypes() prior to calling + // this function. + folly::ssl::detail::installThreadingLocks(); + return 1; +} + +void OPENSSL_cleanup() { + folly::ssl::detail::cleanupThreadingLocks(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + EVP_cleanup(); + ERR_clear_error(); +} + +const ASN1_INTEGER* X509_REVOKED_get0_serialNumber(const X509_REVOKED* r) { + return r->serialNumber; +} + +const ASN1_TIME* X509_REVOKED_get0_revocationDate(const X509_REVOKED* r) { + return r->revocationDate; +} + +uint32_t X509_get_extension_flags(X509* x) { + // Tells OpenSSL to load flags + X509_check_purpose(x, -1, -1); + return x->ex_flags; +} + +uint32_t X509_get_key_usage(X509* x) { + // Call get_extension_flags rather than accessing directly to force loading + // of flags + if ((X509_get_extension_flags(x) & EXFLAG_KUSAGE) == EXFLAG_KUSAGE) { + return x->ex_kusage; + } + return UINT32_MAX; +} + +uint32_t X509_get_extended_key_usage(X509* x) { + return x->ex_xkusage; +} + +int X509_OBJECT_get_type(const X509_OBJECT* obj) { + return obj->type; +} + +X509* X509_OBJECT_get0_X509(const X509_OBJECT* obj) { + if (obj == nullptr || obj->type != X509_LU_X509) { + return nullptr; + } + return obj->data.x509; +} + +const ASN1_TIME* X509_CRL_get0_lastUpdate(const X509_CRL* crl) { + return X509_CRL_get_lastUpdate(crl); +} + +const ASN1_TIME* X509_CRL_get0_nextUpdate(const X509_CRL* crl) { + return X509_CRL_get_nextUpdate(crl); +} + +const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x) { + return x->cert_info->signature; +} + +#endif // !FOLLY_OPENSSL_IS_110 +} // namespace ssl +} // namespace portability +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.h b/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.h new file mode 100644 index 000000000..b22b3c58f --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/OpenSSL.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +// This must come before the OpenSSL includes. +#include <folly/portability/Windows.h> + +#include <folly/Portability.h> + +#include <openssl/opensslv.h> + +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/crypto.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> +#include <openssl/ssl.h> +#include <openssl/tls1.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#ifndef OPENSSL_NO_EC +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#endif + +// BoringSSL doesn't have notion of versioning although it defines +// OPENSSL_VERSION_NUMBER to maintain compatibility. The following variables are +// intended to be specific to OpenSSL. +#if !defined(OPENSSL_IS_BORINGSSL) +#define FOLLY_OPENSSL_IS_100 \ + (OPENSSL_VERSION_NUMBER >= 0x10000003L && \ + OPENSSL_VERSION_NUMBER < 0x1000105fL) +#define FOLLY_OPENSSL_IS_101 \ + (OPENSSL_VERSION_NUMBER >= 0x1000105fL && \ + OPENSSL_VERSION_NUMBER < 0x1000200fL) +#define FOLLY_OPENSSL_IS_102 \ + (OPENSSL_VERSION_NUMBER >= 0x1000200fL && \ + OPENSSL_VERSION_NUMBER < 0x10100000L) +#define FOLLY_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#endif + +#if !defined(OPENSSL_IS_BORINGSSL) && !FOLLY_OPENSSL_IS_100 && \ + !FOLLY_OPENSSL_IS_101 && !FOLLY_OPENSSL_IS_102 && !FOLLY_OPENSSL_IS_110 +#warning Compiling with unsupported OpenSSL version +#endif + +// BoringSSL and OpenSSL 0.9.8f later with TLS extension support SNI. +#if defined(OPENSSL_IS_BORINGSSL) || \ + (OPENSSL_VERSION_NUMBER >= 0x00908070L && !defined(OPENSSL_NO_TLSEXT)) +#define FOLLY_OPENSSL_HAS_SNI 1 +#else +#define FOLLY_OPENSSL_HAS_SNI 0 +#endif + +// BoringSSL and OpenSSL 1.0.2 later with TLS extension support ALPN. +#if defined(OPENSSL_IS_BORINGSSL) || \ + (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)) +#define FOLLY_OPENSSL_HAS_ALPN 1 +#else +#define FOLLY_OPENSSL_HAS_ALPN 0 +#endif + +// OpenSSL 1.1.1 and above have TLS 1.3 support +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL +#define FOLLY_OPENSSL_HAS_TLS13 1 +#else +#define FOLLY_OPENSSL_HAS_TLS13 0 +#endif + +#if FOLLY_OPENSSL_IS_110 && \ + (!defined(OPENSSL_NO_CHACHA) || !defined(OPENSSL_NO_POLY1305)) +#define FOLLY_OPENSSL_HAS_CHACHA 1 +#else +#define FOLLY_OPENSSL_HAS_CHACHA 0 +#endif + +#if !FOLLY_OPENSSL_IS_110 +#define OPENSSL_VERSION SSLEAY_VERSION +#define OpenSSL_version SSLeay_version +#define OpenSSL_version_num SSLeay +#endif + +#if !FOLLY_OPENSSL_IS_110 +#define X509_get0_notAfter X509_get_notAfter +#define X509_get0_notBefore X509_get_notBefore +#endif + +// This attempts to "unify" the OpenSSL libcrypto/libssl APIs between +// OpenSSL 1.0.2, 1.1.0 (and some earlier versions) and BoringSSL. The general +// idea is to provide namespaced wrapper methods for versions which do not +// which already exist in BoringSSL and 1.1.0, but there are few APIs such as +// SSL_CTX_set1_sigalgs_list and so on which exist in 1.0.2 but were removed +// in BoringSSL +namespace folly { +namespace portability { +namespace ssl { + +#ifdef OPENSSL_IS_BORINGSSL +int SSL_CTX_set1_sigalgs_list(SSL_CTX* ctx, const char* sigalgs_list); +int TLS1_get_client_version(SSL* s); +#endif + +#if FOLLY_OPENSSL_IS_100 +uint32_t SSL_CIPHER_get_id(const SSL_CIPHER*); +int TLS1_get_client_version(const SSL*); +#endif + +#if FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 +int X509_get_signature_nid(X509* cert); +#endif + +#if FOLLY_OPENSSL_IS_100 || FOLLY_OPENSSL_IS_101 || FOLLY_OPENSSL_IS_102 +int SSL_CTX_up_ref(SSL_CTX* session); +int SSL_SESSION_up_ref(SSL_SESSION* session); +int X509_up_ref(X509* x); +int X509_STORE_up_ref(X509_STORE* v); +void X509_STORE_CTX_set0_verified_chain( + X509_STORE_CTX* ctx, + STACK_OF(X509) * sk); +int EVP_PKEY_up_ref(EVP_PKEY* evp); +void RSA_get0_key( + const RSA* r, + const BIGNUM** n, + const BIGNUM** e, + const BIGNUM** d); +RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey); +DSA* EVP_PKEY_get0_DSA(EVP_PKEY* pkey); +DH* EVP_PKEY_get0_DH(EVP_PKEY* pkey); +EC_KEY* EVP_PKEY_get0_EC_KEY(EVP_PKEY* pkey); +#endif + +#if !FOLLY_OPENSSL_IS_110 +BIO_METHOD* BIO_meth_new(int type, const char* name); +void BIO_meth_free(BIO_METHOD* biom); +int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int)); +int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int)); +int BIO_meth_set_puts(BIO_METHOD* biom, int (*bputs)(BIO*, const char*)); +int BIO_meth_set_gets(BIO_METHOD* biom, int (*bgets)(BIO*, char*, int)); +int BIO_meth_set_ctrl(BIO_METHOD* biom, long (*ctrl)(BIO*, int, long, void*)); +int BIO_meth_set_create(BIO_METHOD* biom, int (*create)(BIO*)); +int BIO_meth_set_destroy(BIO_METHOD* biom, int (*destroy)(BIO*)); + +void BIO_set_data(BIO* bio, void* ptr); +void* BIO_get_data(BIO* bio); +void BIO_set_init(BIO* bio, int init); +void BIO_set_shutdown(BIO* bio, int shutdown); + +const SSL_METHOD* TLS_server_method(void); +const SSL_METHOD* TLS_client_method(void); + +const char* SSL_SESSION_get0_hostname(const SSL_SESSION* s); +unsigned char* ASN1_STRING_get0_data(const ASN1_STRING* x); + +EVP_MD_CTX* EVP_MD_CTX_new(); +void EVP_MD_CTX_free(EVP_MD_CTX* ctx); + +HMAC_CTX* HMAC_CTX_new(); +void HMAC_CTX_free(HMAC_CTX* ctx); + +unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION* s); +int SSL_SESSION_has_ticket(const SSL_SESSION* s); +int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g); +void DH_get0_pqg( + const DH* dh, + const BIGNUM** p, + const BIGNUM** q, + const BIGNUM** g); +void DH_get0_key(const DH* dh, const BIGNUM** pub_key, const BIGNUM** priv_key); +long DH_get_length(const DH* dh); +int DH_set_length(DH* dh, long length); + +void DSA_get0_pqg( + const DSA* dsa, + const BIGNUM** p, + const BIGNUM** q, + const BIGNUM** g); +void DSA_get0_key( + const DSA* dsa, + const BIGNUM** pub_key, + const BIGNUM** priv_key); + +STACK_OF(X509_OBJECT) * X509_STORE_get0_objects(X509_STORE* store); + +X509* X509_STORE_CTX_get0_cert(X509_STORE_CTX* ctx); +STACK_OF(X509) * X509_STORE_CTX_get0_chain(X509_STORE_CTX* ctx); +STACK_OF(X509) * X509_STORE_CTX_get0_untrusted(X509_STORE_CTX* ctx); +bool RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d); +void RSA_get0_factors(const RSA* r, const BIGNUM** p, const BIGNUM** q); +void RSA_get0_crt_params( + const RSA* r, + const BIGNUM** dmp1, + const BIGNUM** dmq1, + const BIGNUM** iqmp); +int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s); +void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps); + +using OPENSSL_INIT_SETTINGS = void; +int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS* settings); +void OPENSSL_cleanup(); + +const ASN1_INTEGER* X509_REVOKED_get0_serialNumber(const X509_REVOKED* r); +const ASN1_TIME* X509_REVOKED_get0_revocationDate(const X509_REVOKED* r); + +uint32_t X509_get_extension_flags(X509* x); +uint32_t X509_get_key_usage(X509* x); +uint32_t X509_get_extended_key_usage(X509* x); + +int X509_OBJECT_get_type(const X509_OBJECT* obj); +X509* X509_OBJECT_get0_X509(const X509_OBJECT* obj); + +const ASN1_TIME* X509_CRL_get0_lastUpdate(const X509_CRL* crl); +const ASN1_TIME* X509_CRL_get0_nextUpdate(const X509_CRL* crl); + +const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x); + +#endif + +#if FOLLY_OPENSSL_IS_110 +// Note: this was a type and has been fixed upstream, so the next 1.1.0 +// minor version upgrade will need to remove this +#define OPENSSL_lh_new OPENSSL_LH_new + +// OpenSSL v1.1.0 removed support for SSLv2, and also removed the define that +// indicates it isn't supported. +#define OPENSSL_NO_SSL2 +#endif +} // namespace ssl +} // namespace portability +} // namespace folly + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::ssl; +FOLLY_POP_WARNING diff --git a/ios/Pods/Flipper-Folly/folly/portability/PThread.cpp b/ios/Pods/Flipper-Folly/folly/portability/PThread.cpp new file mode 100644 index 000000000..5448460ec --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/PThread.cpp @@ -0,0 +1,705 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/PThread.h> + +#if !FOLLY_HAVE_PTHREAD && defined(_WIN32) +#include <boost/thread/exceptions.hpp> +#include <boost/thread/tss.hpp> +#include <boost/version.hpp> + +#include <errno.h> + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <exception> +#include <limits> +#include <mutex> +#include <shared_mutex> +#include <thread> + +#include <folly/lang/Assume.h> +#include <folly/portability/Windows.h> + +namespace folly { +namespace portability { +namespace pthread { + +int pthread_attr_init(pthread_attr_t* attr) { + if (attr == nullptr) { + errno = EINVAL; + return -1; + } + attr->stackSize = 0; + attr->detached = false; + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t* attr, int state) { + if (attr == nullptr) { + errno = EINVAL; + return -1; + } + attr->detached = state == PTHREAD_CREATE_DETACHED ? true : false; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t* attr, size_t kb) { + if (attr == nullptr) { + errno = EINVAL; + return -1; + } + attr->stackSize = kb; + return 0; +} + +namespace pthread_detail { +pthread_t::~pthread_t() noexcept { + if (handle != INVALID_HANDLE_VALUE && !detached) { + CloseHandle(handle); + } +} +} // namespace pthread_detail + +int pthread_equal(pthread_t threadA, pthread_t threadB) { + if (threadA == threadB) { + return 1; + } + + // Note that, in the presence of detached threads, it is in theory possible + // for two different pthread_t handles to be compared as the same due to + // Windows HANDLE and Thread ID re-use. If you're doing anything useful with + // a detached thread, you're probably doing it wrong, but I felt like leaving + // this note here anyways. + if (threadA->handle == threadB->handle && + threadA->threadID == threadB->threadID) { + return 1; + } + return 0; +} + +namespace { +thread_local pthread_t current_thread_self; +struct pthread_startup_info { + pthread_t thread; + void* (*startupFunction)(void*); + void* startupArgument; +}; + +DWORD internal_pthread_thread_start(void* arg) { + // We are now in the new thread. + auto startupInfo = reinterpret_cast<pthread_startup_info*>(arg); + current_thread_self = startupInfo->thread; + auto ret = startupInfo->startupFunction(startupInfo->startupArgument); + if /* constexpr */ (sizeof(void*) != sizeof(DWORD)) { + auto tmp = reinterpret_cast<uintptr_t>(ret); + if (tmp > std::numeric_limits<DWORD>::max()) { + throw std::out_of_range( + "Exit code of the pthread is outside the range representable on Windows"); + } + } + + delete startupInfo; + return static_cast<DWORD>(reinterpret_cast<uintptr_t>(ret)); +} +} // namespace + +int pthread_create( + pthread_t* thread, + const pthread_attr_t* attr, + void* (*start_routine)(void*), + void* arg) { + if (thread == nullptr) { + errno = EINVAL; + return -1; + } + + size_t stackSize = attr != nullptr ? attr->stackSize : 0; + bool detach = attr != nullptr ? attr->detached : false; + + // Note that the start routine passed into pthread returns a void* and the + // windows API expects DWORD's, so we need to stub around that. + auto startupInfo = new pthread_startup_info(); + startupInfo->startupFunction = start_routine; + startupInfo->startupArgument = arg; + startupInfo->thread = std::make_shared<pthread_detail::pthread_t>(); + // We create the thread suspended so we can assign the handle and thread id + // in the pthread_t. + startupInfo->thread->handle = CreateThread( + nullptr, + stackSize, + internal_pthread_thread_start, + startupInfo, + CREATE_SUSPENDED, + &startupInfo->thread->threadID); + ResumeThread(startupInfo->thread->handle); + + if (detach) { + *thread = std::make_shared<pthread_detail::pthread_t>(); + (*thread)->detached = true; + (*thread)->handle = startupInfo->thread->handle; + (*thread)->threadID = startupInfo->thread->threadID; + } else { + *thread = startupInfo->thread; + } + return 0; +} + +pthread_t pthread_self() { + // Not possible to race :) + if (current_thread_self == nullptr) { + current_thread_self = std::make_shared<pthread_detail::pthread_t>(); + current_thread_self->threadID = GetCurrentThreadId(); + // The handle returned by GetCurrentThread is a pseudo-handle and needs to + // be swapped out for a real handle to be useful anywhere other than this + // thread. + DuplicateHandle( + GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + ¤t_thread_self->handle, + DUPLICATE_SAME_ACCESS, + TRUE, + 0); + } + + return current_thread_self; +} + +int pthread_join(pthread_t thread, void** exitCode) { + if (thread->detached) { + errno = EINVAL; + return -1; + } + + if (WaitForSingleObjectEx(thread->handle, INFINITE, FALSE) == WAIT_FAILED) { + return -1; + } + + if (exitCode != nullptr) { + DWORD e; + if (!GetExitCodeThread(thread->handle, &e)) { + return -1; + } + *exitCode = reinterpret_cast<void*>(static_cast<uintptr_t>(e)); + } + + return 0; +} + +HANDLE pthread_getw32threadhandle_np(pthread_t thread) { + return thread->handle; +} + +DWORD pthread_getw32threadid_np(pthread_t thread) { + return thread->threadID; +} + +int pthread_setschedparam( + pthread_t thread, + int policy, + const sched_param* param) { + if (thread->detached) { + errno = EINVAL; + return -1; + } + + auto newPrior = param->sched_priority; + if (newPrior > THREAD_PRIORITY_TIME_CRITICAL || + newPrior < THREAD_PRIORITY_IDLE) { + errno = EINVAL; + return -1; + } + if (GetPriorityClass(GetCurrentProcess()) != REALTIME_PRIORITY_CLASS) { + if (newPrior > THREAD_PRIORITY_IDLE && newPrior < THREAD_PRIORITY_LOWEST) { + // The values between IDLE and LOWEST are invalid unless the process is + // running as realtime. + newPrior = THREAD_PRIORITY_LOWEST; + } else if ( + newPrior < THREAD_PRIORITY_TIME_CRITICAL && + newPrior > THREAD_PRIORITY_HIGHEST) { + // Same as above. + newPrior = THREAD_PRIORITY_HIGHEST; + } + } + if (!SetThreadPriority(thread->handle, newPrior)) { + return -1; + } + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t* attr) { + if (attr == nullptr) { + return EINVAL; + } + + attr->type = PTHREAD_MUTEX_DEFAULT; + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) { + if (attr == nullptr) { + return EINVAL; + } + + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) { + if (attr == nullptr) { + return EINVAL; + } + + if (type != PTHREAD_MUTEX_DEFAULT && type != PTHREAD_MUTEX_RECURSIVE) { + return EINVAL; + } + + attr->type = type; + return 0; +} + +struct pthread_mutex_t_ { + private: + int type; + union { + std::timed_mutex timed_mtx; + std::recursive_timed_mutex recursive_timed_mtx; + }; + + public: + pthread_mutex_t_(int mutex_type) : type(mutex_type) { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + new (&timed_mtx) std::timed_mutex(); + break; + case PTHREAD_MUTEX_RECURSIVE: + new (&recursive_timed_mtx) std::recursive_timed_mutex(); + break; + } + } + + ~pthread_mutex_t_() noexcept { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + timed_mtx.~timed_mutex(); + break; + case PTHREAD_MUTEX_RECURSIVE: + recursive_timed_mtx.~recursive_timed_mutex(); + break; + } + } + + void lock() { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + timed_mtx.lock(); + break; + case PTHREAD_MUTEX_RECURSIVE: + recursive_timed_mtx.lock(); + break; + } + } + + bool try_lock() { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + return timed_mtx.try_lock(); + case PTHREAD_MUTEX_RECURSIVE: + return recursive_timed_mtx.try_lock(); + } + folly::assume_unreachable(); + } + + bool timed_try_lock(std::chrono::system_clock::time_point until) { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + return timed_mtx.try_lock_until(until); + case PTHREAD_MUTEX_RECURSIVE: + return recursive_timed_mtx.try_lock_until(until); + } + folly::assume_unreachable(); + } + + void unlock() { + switch (type) { + case PTHREAD_MUTEX_NORMAL: + timed_mtx.unlock(); + break; + case PTHREAD_MUTEX_RECURSIVE: + recursive_timed_mtx.unlock(); + break; + } + } + + void condition_wait(std::condition_variable_any& cond) { + switch (type) { + case PTHREAD_MUTEX_NORMAL: { + std::unique_lock<std::timed_mutex> lock(timed_mtx); + cond.wait(lock); + break; + } + case PTHREAD_MUTEX_RECURSIVE: { + std::unique_lock<std::recursive_timed_mutex> lock(recursive_timed_mtx); + cond.wait(lock); + break; + } + } + } + + bool condition_timed_wait( + std::condition_variable_any& cond, + std::chrono::system_clock::time_point until) { + switch (type) { + case PTHREAD_MUTEX_NORMAL: { + std::unique_lock<std::timed_mutex> lock(timed_mtx); + return cond.wait_until(lock, until) == std::cv_status::no_timeout; + } + case PTHREAD_MUTEX_RECURSIVE: { + std::unique_lock<std::recursive_timed_mutex> lock(recursive_timed_mtx); + return cond.wait_until(lock, until) == std::cv_status::no_timeout; + } + } + folly::assume_unreachable(); + } +}; + +int pthread_mutex_init( + pthread_mutex_t* mutex, + const pthread_mutexattr_t* attr) { + if (mutex == nullptr) { + return EINVAL; + } + + auto type = attr != nullptr ? attr->type : PTHREAD_MUTEX_DEFAULT; + auto ret = new pthread_mutex_t_(type); + *mutex = ret; + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t* mutex) { + if (mutex == nullptr) { + return EINVAL; + } + + delete *mutex; + *mutex = nullptr; + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t* mutex) { + if (mutex == nullptr) { + return EINVAL; + } + + // This implementation does not implement deadlock detection, as the + // STL mutexes we're wrapping don't either. + (*mutex)->lock(); + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t* mutex) { + if (mutex == nullptr) { + return EINVAL; + } + + if ((*mutex)->try_lock()) { + return 0; + } else { + return EBUSY; + } +} + +static std::chrono::system_clock::time_point timespec_to_time_point( + const timespec* t) { + using time_point = std::chrono::system_clock::time_point; + auto ns = + std::chrono::seconds(t->tv_sec) + std::chrono::nanoseconds(t->tv_nsec); + return time_point(std::chrono::duration_cast<time_point::duration>(ns)); +} + +int pthread_mutex_timedlock( + pthread_mutex_t* mutex, + const timespec* abs_timeout) { + if (mutex == nullptr || abs_timeout == nullptr) { + return EINVAL; + } + + auto time = timespec_to_time_point(abs_timeout); + if ((*mutex)->timed_try_lock(time)) { + return 0; + } else { + return ETIMEDOUT; + } +} + +int pthread_mutex_unlock(pthread_mutex_t* mutex) { + if (mutex == nullptr) { + return EINVAL; + } + + // This implementation allows other threads to unlock it, + // as the STL containers also do. + (*mutex)->unlock(); + return 0; +} + +struct pthread_rwlock_t_ { + std::shared_timed_mutex mtx; + std::atomic<bool> writing{false}; +}; + +int pthread_rwlock_init(pthread_rwlock_t* rwlock, const void* attr) { + if (attr != nullptr) { + return EINVAL; + } + if (rwlock == nullptr) { + return EINVAL; + } + + *rwlock = new pthread_rwlock_t_(); + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + delete *rwlock; + *rwlock = nullptr; + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + (*rwlock)->mtx.lock_shared(); + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + if ((*rwlock)->mtx.try_lock_shared()) { + return 0; + } else { + return EBUSY; + } +} + +int pthread_rwlock_timedrdlock( + pthread_rwlock_t* rwlock, + const timespec* abs_timeout) { + if (rwlock == nullptr) { + return EINVAL; + } + + auto time = timespec_to_time_point(abs_timeout); + if ((*rwlock)->mtx.try_lock_shared_until(time)) { + return 0; + } else { + return ETIMEDOUT; + } +} + +int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + (*rwlock)->mtx.lock(); + (*rwlock)->writing = true; + return 0; +} + +// Note: As far as I can tell, rwlock is technically supposed to +// be an upgradable lock, but we don't implement it that way. +int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + if ((*rwlock)->mtx.try_lock()) { + (*rwlock)->writing = true; + return 0; + } else { + return EBUSY; + } +} + +int pthread_rwlock_timedwrlock( + pthread_rwlock_t* rwlock, + const timespec* abs_timeout) { + if (rwlock == nullptr) { + return EINVAL; + } + + auto time = timespec_to_time_point(abs_timeout); + if ((*rwlock)->mtx.try_lock_until(time)) { + (*rwlock)->writing = true; + return 0; + } else { + return ETIMEDOUT; + } +} + +int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) { + if (rwlock == nullptr) { + return EINVAL; + } + + // Note: We don't have any checking to ensure we have actually + // locked things first, so you'll actually be in undefined behavior + // territory if you do attempt to unlock things you haven't locked. + if ((*rwlock)->writing) { + (*rwlock)->mtx.unlock(); + // If we fail, then another thread has already immediately acquired + // the write lock, so this should stay as true :) + bool dump = true; + (void)(*rwlock)->writing.compare_exchange_strong(dump, false); + } else { + (*rwlock)->mtx.unlock_shared(); + } + return 0; +} + +struct pthread_cond_t_ { + // pthread_mutex_t is backed by timed + // mutexes, so no basic condition variable for + // us :( + std::condition_variable_any cond; +}; + +int pthread_cond_init(pthread_cond_t* cond, const void* attr) { + if (attr != nullptr) { + return EINVAL; + } + if (cond == nullptr) { + return EINVAL; + } + + *cond = new pthread_cond_t_(); + return 0; +} + +int pthread_cond_destroy(pthread_cond_t* cond) { + if (cond == nullptr) { + return EINVAL; + } + + delete *cond; + *cond = nullptr; + return 0; +} + +int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) { + if (cond == nullptr || mutex == nullptr) { + return EINVAL; + } + + (*mutex)->condition_wait((*cond)->cond); + return 0; +} + +int pthread_cond_timedwait( + pthread_cond_t* cond, + pthread_mutex_t* mutex, + const timespec* abstime) { + if (cond == nullptr || mutex == nullptr || abstime == nullptr) { + return EINVAL; + } + + auto time = timespec_to_time_point(abstime); + if ((*mutex)->condition_timed_wait((*cond)->cond, time)) { + return 0; + } else { + return ETIMEDOUT; + } +} + +int pthread_cond_signal(pthread_cond_t* cond) { + if (cond == nullptr) { + return EINVAL; + } + + (*cond)->cond.notify_one(); + return 0; +} + +int pthread_cond_broadcast(pthread_cond_t* cond) { + if (cond == nullptr) { + return EINVAL; + } + + (*cond)->cond.notify_all(); + return 0; +} + +int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) { + try { + auto newKey = new boost::thread_specific_ptr<void>(destructor); + *key = newKey; + return 0; + } catch (boost::thread_resource_error) { + return -1; + } +} + +int pthread_key_delete(pthread_key_t key) { + try { + auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key); + delete realKey; + return 0; + } catch (boost::thread_resource_error) { + return -1; + } +} + +void* pthread_getspecific(pthread_key_t key) { + auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key); + // This can't throw as-per the documentation. + return realKey->get(); +} + +int pthread_setspecific(pthread_key_t key, const void* value) { + try { + auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key); + // We can't just call reset here because that would invoke the cleanup + // function, which we don't want to do. + boost::detail::set_tss_data( + realKey, +#if BOOST_VERSION >= 107000 + boost::detail::thread::cleanup_caller_t(), + boost::detail::thread::cleanup_func_t(), +#else + boost::shared_ptr<boost::detail::tss_cleanup_function>(), +#endif + const_cast<void*>(value), + false); + return 0; + } catch (boost::thread_resource_error) { + return -1; + } +} +} // namespace pthread +} // namespace portability +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/PThread.h b/ios/Pods/Flipper-Folly/folly/portability/PThread.h new file mode 100644 index 000000000..34247ddff --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/PThread.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/Config.h> + +#if !defined(_WIN32) + +#include <pthread.h> + +#if defined(__FreeBSD__) +#include <sys/thr.h> // @manual +#endif + +#elif !FOLLY_HAVE_PTHREAD + +#include <cstdint> +#include <memory> + +#include <folly/portability/Sched.h> +#include <folly/portability/Time.h> +#include <folly/portability/Windows.h> + +#include <folly/Portability.h> + +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 1 + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_RECURSIVE 1 +#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL + +#define _POSIX_TIMEOUTS 200112L + +namespace folly { +namespace portability { +namespace pthread { +struct pthread_attr_t { + size_t stackSize; + bool detached; +}; + +int pthread_attr_init(pthread_attr_t* attr); +int pthread_attr_setdetachstate(pthread_attr_t* attr, int state); +int pthread_attr_setstacksize(pthread_attr_t* attr, size_t kb); + +namespace pthread_detail { +struct pthread_t { + HANDLE handle{INVALID_HANDLE_VALUE}; + DWORD threadID{0}; + bool detached{false}; + + ~pthread_t() noexcept; +}; +} // namespace pthread_detail +using pthread_t = std::shared_ptr<pthread_detail::pthread_t>; + +int pthread_equal(pthread_t threadA, pthread_t threadB); +int pthread_create( + pthread_t* thread, + const pthread_attr_t* attr, + void* (*start_routine)(void*), + void* arg); +pthread_t pthread_self(); +int pthread_join(pthread_t thread, void** exitCode); + +HANDLE pthread_getw32threadhandle_np(pthread_t thread); +DWORD pthread_getw32threadid_np(pthread_t thread); + +int pthread_setschedparam( + pthread_t thread, + int policy, + const sched_param* param); + +struct pthread_mutexattr_t { + int type; +}; +int pthread_mutexattr_init(pthread_mutexattr_t* attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr); +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type); + +using pthread_mutex_t = struct pthread_mutex_t_*; +int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr); +int pthread_mutex_destroy(pthread_mutex_t* mutex); +int pthread_mutex_lock(pthread_mutex_t* mutex); +int pthread_mutex_trylock(pthread_mutex_t* mutex); +int pthread_mutex_unlock(pthread_mutex_t* mutex); +int pthread_mutex_timedlock( + pthread_mutex_t* mutex, + const timespec* abs_timeout); + +using pthread_rwlock_t = struct pthread_rwlock_t_*; +// Technically the second argument here is supposed to be a +// const pthread_rwlockattr_t* but we don support it, so we +// simply don't define pthread_rwlockattr_t at all to cause +// a build-break if anyone tries to use it. +int pthread_rwlock_init(pthread_rwlock_t* rwlock, const void* attr); +int pthread_rwlock_destroy(pthread_rwlock_t* rwlock); +int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_timedrdlock( + pthread_rwlock_t* rwlock, + const timespec* abs_timeout); +int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_timedwrlock( + pthread_rwlock_t* rwlock, + const timespec* abs_timeout); +int pthread_rwlock_unlock(pthread_rwlock_t* rwlock); + +using pthread_cond_t = struct pthread_cond_t_*; +// Once again, technically the second argument should be a +// pthread_condattr_t, but we don't implement it, so void* +// it is. +int pthread_cond_init(pthread_cond_t* cond, const void* attr); +int pthread_cond_destroy(pthread_cond_t* cond); +int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex); +int pthread_cond_timedwait( + pthread_cond_t* cond, + pthread_mutex_t* mutex, + const timespec* abstime); +int pthread_cond_signal(pthread_cond_t* cond); +int pthread_cond_broadcast(pthread_cond_t* cond); + +// In reality, this is boost::thread_specific_ptr*, but we're attempting +// to avoid introducing boost into a portability header. +using pthread_key_t = void*; + +int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)); +int pthread_key_delete(pthread_key_t key); +void* pthread_getspecific(pthread_key_t key); +int pthread_setspecific(pthread_key_t key, const void* value); +} // namespace pthread +} // namespace portability +} // namespace folly + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::pthread; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Sched.cpp b/ios/Pods/Flipper-Folly/folly/portability/Sched.cpp new file mode 100644 index 000000000..e5ff70bf9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Sched.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Sched.h> + +#ifdef _WIN32 +#include <thread> + +namespace folly { +namespace portability { +namespace sched { +int sched_yield() { + std::this_thread::yield(); + return 0; +} + +// There is only 1 scheduling policy on Windows +int sched_get_priority_min(int policy) { + return -15; +} + +int sched_get_priority_max(int policy) { + return 15; +} +} // namespace sched +} // namespace portability +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Sched.h b/ios/Pods/Flipper-Folly/folly/portability/Sched.h new file mode 100644 index 000000000..fe8504b0a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Sched.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <sched.h> +#else +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 + +#include <folly/Portability.h> + +namespace folly { +namespace portability { +namespace sched { +struct sched_param { + int sched_priority; +}; +int sched_yield(); +int sched_get_priority_min(int policy); +int sched_get_priority_max(int policy); +} // namespace sched +} // namespace portability +} // namespace folly + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::sched; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Semaphore.cpp b/ios/Pods/Flipper-Folly/folly/portability/Semaphore.cpp new file mode 100644 index 000000000..669f58165 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Semaphore.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Semaphore.h> + +#include <folly/portability/Windows.h> + +#include <cerrno> +#include <mutex> + +#ifdef _WIN32 +namespace folly::portability::semaphore { +struct sem_t_ { + std::mutex mtx{}; + HANDLE sema{INVALID_HANDLE_VALUE}; + int32_t value{0}; +}; + +int sem_init(sem_t* s, int shared, unsigned int value) { + // Don't support cross-process shared semaphores. + if (shared != 0) { + return -1; + } + auto sem = CreateSemaphoreA(nullptr, 0, SEM_VALUE_MAX, nullptr); + if (sem == 0) { + return -1; + } + auto ret = new sem_t_(); + ret->sema = sem; + ret->value = value; + *s = ret; + return 0; +} + +int sem_destroy(sem_t* s) { + if (!CloseHandle((*s)->sema)) { + return -1; + } + delete *s; + *s = nullptr; + return 0; +} + +int sem_post(sem_t* s) { + std::lock_guard<std::mutex> lock{(*s)->mtx}; + if ((*s)->value < SEM_VALUE_MAX) { + if (++(*s)->value <= 0 && !ReleaseSemaphore((*s)->sema, 1, nullptr)) { + --(*s)->value; + errno = EINVAL; + return -1; + } + } else { + errno = ERANGE; + return -1; + } + return 0; +} + +int sem_trywait(sem_t* s) { + std::lock_guard<std::mutex> lock{(*s)->mtx}; + if ((*s)->value > 0) { + (*s)->value--; + return 0; + } else { + errno = EAGAIN; + return -1; + } +} + +int sem_wait(sem_t* s) { + int32_t value = 0; + { + std::lock_guard<std::mutex> lock{(*s)->mtx}; + value = --(*s)->value; + } + + if (value < 0) { + if (WaitForSingleObject((*s)->sema, INFINITE) != WAIT_OBJECT_0) { + errno = EINVAL; + return -1; + } + } + return 0; +} +} // namespace folly::portability::semaphore +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Semaphore.h b/ios/Pods/Flipper-Folly/folly/portability/Semaphore.h new file mode 100644 index 000000000..369934d84 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Semaphore.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <semaphore.h> +#else +#include <limits.h> + +#include <folly/Portability.h> + +#define SEM_VALUE_MAX INT_MAX +namespace folly::portability::semaphore { +using sem_t = struct sem_t_*; +int sem_init(sem_t* s, int shared, unsigned int value); +int sem_destroy(sem_t* s); +int sem_post(sem_t* s); +int sem_trywait(sem_t* s); +int sem_wait(sem_t* s); +} // namespace folly::portability::semaphore + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::semaphore; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Sockets.cpp b/ios/Pods/Flipper-Folly/folly/portability/Sockets.cpp new file mode 100644 index 000000000..89e04ecb8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Sockets.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Sockets.h> + +#ifdef _MSC_VER + +#include <errno.h> +#include <fcntl.h> + +#include <MSWSock.h> // @manual + +#include <folly/ScopeGuard.h> +#include <folly/net/NetworkSocket.h> +#include <folly/net/detail/SocketFileDescriptorMap.h> + +namespace folly { +namespace portability { +namespace sockets { + +namespace { +int network_socket_to_fd(NetworkSocket sock) { + return socket_to_fd(sock.data); +} + +NetworkSocket fd_to_network_socket(int fd) { + return NetworkSocket(fd_to_socket(fd)); +} +} // namespace + +bool is_fh_socket(int fh) { + SOCKET h = fd_to_socket(fh); + constexpr long kDummyEvents = 0xABCDEF12; + WSANETWORKEVENTS e; + e.lNetworkEvents = kDummyEvents; + WSAEnumNetworkEvents(h, nullptr, &e); + return e.lNetworkEvents != kDummyEvents; +} + +SOCKET fd_to_socket(int fd) { + return netops::detail::SocketFileDescriptorMap::fdToSocket(fd); +} + +int socket_to_fd(SOCKET s) { + return netops::detail::SocketFileDescriptorMap::socketToFd(s); +} + +template <class R, class F, class... Args> +static R wrapSocketFunction(F f, int s, Args... args) { + NetworkSocket h = fd_to_network_socket(s); + return f(h, args...); +} + +int accept(int s, struct sockaddr* addr, socklen_t* addrlen) { + return network_socket_to_fd( + wrapSocketFunction<NetworkSocket>(netops::accept, s, addr, addrlen)); +} + +int bind(int s, const struct sockaddr* name, socklen_t namelen) { + return wrapSocketFunction<int>(netops::bind, s, name, namelen); +} + +int connect(int s, const struct sockaddr* name, socklen_t namelen) { + return wrapSocketFunction<int>(netops::connect, s, name, namelen); +} + +int getpeername(int s, struct sockaddr* name, socklen_t* namelen) { + return wrapSocketFunction<int>(netops::getpeername, s, name, namelen); +} + +int getsockname(int s, struct sockaddr* name, socklen_t* namelen) { + return wrapSocketFunction<int>(netops::getsockname, s, name, namelen); +} + +int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) { + return getsockopt(s, level, optname, (void*)optval, optlen); +} + +int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen) { + return wrapSocketFunction<int>( + netops::getsockopt, s, level, optname, optval, optlen); +} + +int inet_aton(const char* cp, struct in_addr* inp) { + return netops::inet_aton(cp, inp); +} + +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { + return ::inet_ntop(af, (char*)src, dst, size_t(size)); +} + +int listen(int s, int backlog) { + return wrapSocketFunction<int>(netops::listen, s, backlog); +} + +int poll(struct pollfd fds[], nfds_t nfds, int timeout) { + // NetOps already has the checks to ensure this is safe. + netops::PollDescriptor* desc = + reinterpret_cast<netops::PollDescriptor*>(reinterpret_cast<void*>(fds)); + for (nfds_t i = 0; i < nfds; ++i) { + desc[i].fd = fd_to_network_socket((int)desc[i].fd.data); + } + return netops::poll(desc, nfds, timeout); +} + +ssize_t recv(int s, void* buf, size_t len, int flags) { + return wrapSocketFunction<ssize_t>(netops::recv, s, buf, len, flags); +} + +ssize_t recv(int s, char* buf, int len, int flags) { + return recv(s, (void*)buf, (size_t)len, flags); +} + +ssize_t recv(int s, void* buf, int len, int flags) { + return recv(s, (void*)buf, (size_t)len, flags); +} + +ssize_t recvfrom( + int s, + void* buf, + size_t len, + int flags, + struct sockaddr* from, + socklen_t* fromlen) { + return wrapSocketFunction<ssize_t>( + netops::recvfrom, s, buf, len, flags, from, fromlen); +} + +ssize_t recvfrom( + int s, + char* buf, + int len, + int flags, + struct sockaddr* from, + socklen_t* fromlen) { + return recvfrom(s, (void*)buf, (size_t)len, flags, from, fromlen); +} + +ssize_t recvfrom( + int s, + void* buf, + int len, + int flags, + struct sockaddr* from, + socklen_t* fromlen) { + return recvfrom(s, (void*)buf, (size_t)len, flags, from, fromlen); +} + +ssize_t recvmsg(int s, struct msghdr* message, int flags) { + return wrapSocketFunction<ssize_t>(netops::recvmsg, s, message, flags); +} + +ssize_t send(int s, const void* buf, size_t len, int flags) { + return wrapSocketFunction<ssize_t>(netops::send, s, buf, len, flags); +} + +ssize_t send(int s, const char* buf, int len, int flags) { + return send(s, (const void*)buf, (size_t)len, flags); +} + +ssize_t send(int s, const void* buf, int len, int flags) { + return send(s, (const void*)buf, (size_t)len, flags); +} + +ssize_t sendmsg(int s, const struct msghdr* message, int flags) { + return wrapSocketFunction<ssize_t>(netops::sendmsg, s, message, flags); +} + +ssize_t sendto( + int s, + const void* buf, + size_t len, + int flags, + const sockaddr* to, + socklen_t tolen) { + return wrapSocketFunction<ssize_t>( + netops::sendto, s, buf, len, flags, to, tolen); +} + +ssize_t sendto( + int s, + const char* buf, + int len, + int flags, + const sockaddr* to, + socklen_t tolen) { + return sendto(s, (const void*)buf, (size_t)len, flags, to, tolen); +} + +ssize_t sendto( + int s, + const void* buf, + int len, + int flags, + const sockaddr* to, + socklen_t tolen) { + return sendto(s, buf, (size_t)len, flags, to, tolen); +} + +int setsockopt( + int s, + int level, + int optname, + const void* optval, + socklen_t optlen) { + return wrapSocketFunction<int>( + netops::setsockopt, s, level, optname, optval, optlen); +} + +int setsockopt( + int s, + int level, + int optname, + const char* optval, + socklen_t optlen) { + return setsockopt(s, level, optname, (const void*)optval, optlen); +} + +int shutdown(int s, int how) { + return wrapSocketFunction<int>(netops::shutdown, s, how); +} + +int socket(int af, int type, int protocol) { + return network_socket_to_fd(netops::socket(af, type, protocol)); +} + +int socketpair(int domain, int type, int protocol, int sv[2]) { + NetworkSocket pair[2]; + auto r = netops::socketpair(domain, type, protocol, pair); + if (r == -1) { + return r; + } + sv[0] = network_socket_to_fd(pair[0]); + sv[1] = network_socket_to_fd(pair[1]); + return 0; +} +} // namespace sockets +} // namespace portability +} // namespace folly +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Sockets.h b/ios/Pods/Flipper-Folly/folly/portability/Sockets.h new file mode 100644 index 000000000..3928812fd --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Sockets.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/net/NetOps.h> + +#include <folly/Portability.h> + +namespace folly { +namespace portability { +namespace sockets { +#ifndef _WIN32 +using ::accept; +using ::bind; +using ::connect; +using ::getpeername; +using ::getsockname; +using ::getsockopt; +using ::inet_ntop; +using ::listen; +using ::poll; +using ::recv; +using ::recvfrom; +using ::send; +using ::sendmsg; +using ::sendto; +using ::setsockopt; +using ::shutdown; +using ::socket; +#else +// Some Windows specific helper functions. +bool is_fh_socket(int fh); +SOCKET fd_to_socket(int fd); +int socket_to_fd(SOCKET s); +int translate_wsa_error(int wsaErr); + +// These aren't additional overloads, but rather other functions that +// are referenced that we need to wrap, or, in the case of inet_aton, +// implement. +int accept(int s, struct sockaddr* addr, socklen_t* addrlen); +int inet_aton(const char* cp, struct in_addr* inp); +int socketpair(int domain, int type, int protocol, int sv[2]); + +// Unless you have a case where you would normally have +// to reference the function as being explicitly in the +// global scope, then you shouldn't be calling these directly. +int bind(int s, const struct sockaddr* name, socklen_t namelen); +int connect(int s, const struct sockaddr* name, socklen_t namelen); +int getpeername(int s, struct sockaddr* name, socklen_t* namelen); +int getsockname(int s, struct sockaddr* name, socklen_t* namelen); +int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen); +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size); +int listen(int s, int backlog); +int poll(struct pollfd fds[], nfds_t nfds, int timeout); +ssize_t recv(int s, void* buf, size_t len, int flags); +ssize_t recvfrom( + int s, + void* buf, + size_t len, + int flags, + struct sockaddr* from, + socklen_t* fromlen); +ssize_t send(int s, const void* buf, size_t len, int flags); +ssize_t sendto( + int s, + const void* buf, + size_t len, + int flags, + const sockaddr* to, + socklen_t tolen); +ssize_t sendmsg(int socket, const struct msghdr* message, int flags); +int setsockopt( + int s, + int level, + int optname, + const void* optval, + socklen_t optlen); +int shutdown(int s, int how); + +// This is the only function that _must_ be referenced via the namespace +// because there is no difference in parameter types to overload +// on. +int socket(int af, int type, int protocol); + +// Windows needs a few extra overloads of some of the functions in order to +// resolve to our portability functions rather than the SOCKET accepting +// ones. +int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen); +ssize_t recv(int s, char* buf, int len, int flags); +ssize_t recv(int s, void* buf, int len, int flags); +ssize_t recvfrom( + int s, + char* buf, + int len, + int flags, + struct sockaddr* from, + socklen_t* fromlen); +ssize_t recvfrom( + int s, + void* buf, + int len, + int flags, + struct sockaddr* from, + socklen_t* fromlen); +ssize_t recvmsg(int s, struct msghdr* message, int fl); +ssize_t send(int s, const char* buf, int len, int flags); +ssize_t send(int s, const void* buf, int len, int flags); +ssize_t sendto( + int s, + const char* buf, + int len, + int flags, + const sockaddr* to, + socklen_t tolen); +ssize_t sendto( + int s, + const void* buf, + int len, + int flags, + const sockaddr* to, + socklen_t tolen); +int setsockopt( + int s, + int level, + int optname, + const char* optval, + socklen_t optlen); +#endif +} // namespace sockets +} // namespace portability +} // namespace folly + +#ifdef _WIN32 +// Add our helpers to the overload set. +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ +using namespace folly::portability::sockets; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Stdio.cpp b/ios/Pods/Flipper-Folly/folly/portability/Stdio.cpp new file mode 100644 index 000000000..378d65eaf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Stdio.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Stdio.h> + +#ifdef _WIN32 + +#include <cstdlib> + +#include <folly/ScopeGuard.h> +#include <folly/portability/Unistd.h> + +extern "C" { +int dprintf(int fd, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + SCOPE_EXIT { + va_end(args); + }; + + int ret = vsnprintf(nullptr, 0, fmt, args); + if (ret <= 0) { + return -1; + } + size_t len = size_t(ret); + char* buf = new char[len + 1]; + SCOPE_EXIT { + delete[] buf; + }; + if (size_t(vsnprintf(buf, len + 1, fmt, args)) == len && + write(fd, buf, len) == ssize_t(len)) { + return ret; + } + + return -1; +} + +int pclose(FILE* f) { + return _pclose(f); +} + +FILE* popen(const char* name, const char* mode) { + return _popen(name, mode); +} + +void setbuffer(FILE* f, char* buf, size_t size) { + setvbuf(f, buf, _IOFBF, size); +} + +int vasprintf(char** dest, const char* format, va_list ap) { + int len = vsnprintf(nullptr, 0, format, ap); + if (len <= 0) { + return -1; + } + char* buf = *dest = (char*)malloc(size_t(len + 1)); + if (vsnprintf(buf, size_t(len + 1), format, ap) == len) { + return len; + } + free(buf); + return -1; +} +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Stdio.h b/ios/Pods/Flipper-Folly/folly/portability/Stdio.h new file mode 100644 index 000000000..48b19aa85 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Stdio.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdio> + +#ifdef _WIN32 +#include <cstdarg> +#include <cstdint> + +extern "C" { +int dprintf(int fd, const char* fmt, ...); +int pclose(FILE* f); +FILE* popen(const char* name, const char* mode); +void setbuffer(FILE* f, char* buf, size_t size); +int vasprintf(char** dest, const char* format, va_list ap); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Stdlib.cpp b/ios/Pods/Flipper-Folly/folly/portability/Stdlib.cpp new file mode 100644 index 000000000..d89353899 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Stdlib.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Stdlib.h> + +#ifdef _WIN32 + +#include <cstring> + +#include <errno.h> + +#include <folly/portability/Fcntl.h> +#include <folly/portability/SysStat.h> +#include <folly/portability/Windows.h> + +extern "C" { +char* mktemp(char* tn) { + return _mktemp(tn); +} + +// While yes, this is for a directory, due to this being windows, +// a file and directory can't have the same name, resulting in this +// still working just fine. +char* mkdtemp(char* tn) { + char* ptr = nullptr; + auto len = strlen(tn); + int ret = 0; + do { + strcpy(tn + len - 6, "XXXXXX"); + ptr = mktemp(tn); + if (ptr == nullptr || *ptr == '\0') { + return nullptr; + } + ret = mkdir(ptr, 0700); + if (ret != 0 && errno != EEXIST) { + return nullptr; + } + } while (ret != 0); + return tn; +} + +int mkstemp(char* tn) { + char* ptr = nullptr; + auto len = strlen(tn); + int ret = 0; + do { + strcpy(tn + len - 6, "XXXXXX"); + ptr = mktemp(tn); + if (ptr == nullptr || *ptr == '\0') { + return -1; + } + ret = open(ptr, O_RDWR | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR); + if (ret == -1 && errno != EEXIST) { + return -1; + } + } while (ret == -1); + return ret; +} + +char* realpath(const char* path, char* resolved_path) { + // I sure hope the caller gave us _MAX_PATH space in the buffer.... + return _fullpath(resolved_path, path, _MAX_PATH); +} + +int setenv(const char* name, const char* value, int overwrite) { + if (overwrite == 0 && getenv(name) != nullptr) { + return 0; + } + + if (*value != '\0') { + auto e = _putenv_s(name, value); + if (e != 0) { + errno = e; + return -1; + } + return 0; + } + + // We are trying to set the value to an empty string, but + // _putenv_s deletes entries if the value is an empty string, + // and just calling SetEnvironmentVariableA doesn't update + // _environ, so we have to do these terrible things. + if (_putenv_s(name, " ") != 0) { + errno = EINVAL; + return -1; + } + + // Here lies the documentation we blatently ignore to make + // this work >_>... + *getenv(name) = '\0'; + // This would result in a double null termination, which + // normally signifies the end of the environment variable + // list, so we stick a completely empty environment variable + // into the list instead. + *(getenv(name) + 1) = '='; + + // If _wenviron is null, the wide environment has not been initialized + // yet, and we don't need to try to update it. + // We have to do this otherwise we'd be forcing the initialization and + // maintenance of the wide environment even though it's never actually + // used in most programs. + if (_wenviron != nullptr) { + wchar_t buf[_MAX_ENV + 1]; + size_t len; + if (mbstowcs_s(&len, buf, _MAX_ENV + 1, name, _MAX_ENV) != 0) { + errno = EINVAL; + return -1; + } + *_wgetenv(buf) = u'\0'; + *(_wgetenv(buf) + 1) = u'='; + } + + // And now, we have to update the outer environment to have + // a proper empty value. + if (!SetEnvironmentVariableA(name, value)) { + errno = EINVAL; + return -1; + } + return 0; +} + +int unsetenv(const char* name) { + if (_putenv_s(name, "") != 0) { + return -1; + } + return 0; +} +} + +#endif + +#if !__linux__ && !FOLLY_MOBILE && !defined(__wasm32__) + +#include <string> +#include <vector> + +extern "C" int clearenv() { + std::vector<std::string> data; + for (auto it = environ; it && *it; ++it) { + std::string entry(*it); + auto equalsPosition = entry.find('='); + if (equalsPosition == std::string::npos || equalsPosition == 0) { + // It's either a drive setting (if on Windows), or something clowny is + // going on in the environment. + continue; + } else { + data.emplace_back(entry.substr(0, equalsPosition)); + } + } + + for (auto s : data) { + if (unsetenv(s.c_str()) != 0) + return -1; + } + + return 0; +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Stdlib.h b/ios/Pods/Flipper-Folly/folly/portability/Stdlib.h new file mode 100644 index 000000000..bdf915413 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Stdlib.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdlib> + +#include <folly/portability/Config.h> + +#if defined(__APPLE__) +#if __has_include(<crt_externs.h>) +#include <crt_externs.h> // @manual +#endif +#endif + +extern "C" { +#ifdef _WIN32 +// These are technically supposed to be defined linux/limits.h and +// sys/param.h respectively, but Windows defines _MAX_PATH in stdlib.h, +// so, instead of creating two headers for a single define each, we put +// them here, where they are likely to already have been included in the +// code that needs them. +#define PATH_MAX _MAX_PATH +#define MAXPATHLEN _MAX_PATH + +char* mktemp(char* tn); +char* mkdtemp(char* tn); +int mkstemp(char* tn); +char* realpath(const char* path, char* resolved_path); +int setenv(const char* name, const char* value, int overwrite); +int unsetenv(const char* name); +#elif defined(__APPLE__) +// environ doesn't work well with dylibs, so use _NSGetEnviron instead. +#if !__has_include(<crt_externs.h>) +char*** _NSGetEnviron(void); +#endif +#define environ (*_NSGetEnviron()) +#endif + +#if defined(__FreeBSD__) +// Needed to resolve linkage +char** environ; +#endif + +#if !__linux__ && !FOLLY_MOBILE +int clearenv(); +#endif +} diff --git a/ios/Pods/Flipper-Folly/folly/portability/String.cpp b/ios/Pods/Flipper-Folly/folly/portability/String.cpp new file mode 100644 index 000000000..1e9f369e5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/String.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/String.h> + +#if !FOLLY_HAVE_MEMRCHR +extern "C" void* memrchr(const void* s, int c, size_t n) { + for (auto p = ((const char*)s) + n - 1; p >= (const char*)s; p--) { + if (*p == (char)c) { + return (void*)p; + } + } + return nullptr; +} +#endif + +#if defined(_WIN32) || defined(__FreeBSD__) +extern "C" char* strndup(const char* a, size_t len) { + auto neededLen = strlen(a); + if (neededLen > len) { + neededLen = len; + } + char* buf = (char*)malloc((neededLen + 1) * sizeof(char)); + if (!buf) { + return nullptr; + } + memcpy(buf, a, neededLen); + buf[neededLen] = '\0'; + return buf; +} +#endif + +#ifdef _WIN32 +extern "C" { +void bzero(void* s, size_t n) { + memset(s, 0, n); +} + +int strcasecmp(const char* a, const char* b) { + return _stricmp(a, b); +} + +int strncasecmp(const char* a, const char* b, size_t c) { + return _strnicmp(a, b, c); +} + +char* strtok_r(char* str, char const* delim, char** ctx) { + return strtok_s(str, delim, ctx); +} +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/String.h b/ios/Pods/Flipper-Folly/folly/portability/String.h new file mode 100644 index 000000000..966e09890 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/String.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdlib.h> +#include <string.h> + +#include <folly/portability/Config.h> + +#if !defined(_WIN32) +#include <strings.h> +#endif + +#if !FOLLY_HAVE_MEMRCHR +extern "C" void* memrchr(const void* s, int c, size_t n); +#endif + +#if defined(_WIN32) || defined(__FreeBSD__) +extern "C" char* strndup(const char* a, size_t len); +#endif + +#ifdef _WIN32 +extern "C" { +void bzero(void* s, size_t n); +int strcasecmp(const char* a, const char* b); +int strncasecmp(const char* a, const char* b, size_t c); +char* strtok_r(char* str, char const* delim, char** ctx); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysFile.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysFile.cpp new file mode 100644 index 000000000..9e02950cf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysFile.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysFile.h> + +#ifdef _WIN32 +#include <limits> + +#include <folly/portability/Windows.h> + +extern "C" int flock(int fd, int operation) { + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + + constexpr DWORD kMaxDWORD = std::numeric_limits<DWORD>::max(); + if (operation & LOCK_UN) { + if (!UnlockFile(h, 0, 0, kMaxDWORD, kMaxDWORD)) { + return -1; + } + } else { + DWORD flags = DWORD( + (operation & LOCK_NB ? LOCKFILE_FAIL_IMMEDIATELY : 0) | + (operation & LOCK_EX ? LOCKFILE_EXCLUSIVE_LOCK : 0)); + OVERLAPPED ov = {}; + if (!LockFileEx(h, flags, 0, kMaxDWORD, kMaxDWORD, &ov)) { + return -1; + } + } + return 0; +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysFile.h b/ios/Pods/Flipper-Folly/folly/portability/SysFile.h new file mode 100644 index 000000000..8bad9432b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysFile.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <sys/file.h> +#else +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_NB 4 +#define LOCK_UN 8 +extern "C" int flock(int fd, int operation); +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.cpp new file mode 100644 index 000000000..d19624452 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysMembarrier.h> + +#include <mutex> + +#include <folly/Portability.h> +#include <folly/portability/SysSyscall.h> +#include <folly/portability/Unistd.h> + +#if FOLLY_X64 && !FOLLY_MOBILE && defined(__linux__) +#define FOLLY_USE_SYS_MEMBARRIER 1 +#if !defined(__NR_membarrier) +#define __NR_membarrier 324 +#endif +#if __has_include(<linux/membarrier.h>) +#include <linux/membarrier.h> // @manual +#else +#define MEMBARRIER_CMD_QUERY 0 +#define MEMBARRIER_CMD_SHARED 1 +#endif +#endif + +namespace folly { +namespace detail { + +bool sysMembarrierAvailable() { + if (!kIsLinux) { + return false; + } + +#if FOLLY_USE_SYS_MEMBARRIER + auto r = syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, /* flags = */ 0); + if (r == -1) { + return false; + } + + return r & MEMBARRIER_CMD_SHARED; +#else + return false; +#endif +} + +int sysMembarrier() { +#if FOLLY_USE_SYS_MEMBARRIER + return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, /* flags = */ 0); +#else + return -1; +#endif +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.h b/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.h new file mode 100644 index 000000000..c77c00073 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysMembarrier.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { +namespace detail { + +int sysMembarrier(); +bool sysMembarrierAvailable(); +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysMman.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysMman.cpp new file mode 100644 index 000000000..11ffa7075 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysMman.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysMman.h> + +#ifdef _WIN32 + +#include <cassert> + +#include <folly/Portability.h> +#include <folly/portability/Windows.h> + +static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) { + if (prot == PROT_NONE) { + ret = PAGE_NOACCESS; + acc = 0; + } else if (prot == PROT_READ) { + ret = PAGE_READONLY; + acc = FILE_MAP_READ; + } else if (prot == PROT_EXEC) { + ret = PAGE_EXECUTE; + acc = FILE_MAP_EXECUTE; + } else if (prot == (PROT_READ | PROT_EXEC)) { + ret = PAGE_EXECUTE_READ; + acc = FILE_MAP_READ | FILE_MAP_EXECUTE; + } else if (prot == (PROT_READ | PROT_WRITE)) { + ret = PAGE_READWRITE; + acc = FILE_MAP_READ | FILE_MAP_WRITE; + } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) { + ret = PAGE_EXECUTE_READWRITE; + acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE; + } else { + return false; + } + return true; +} + +static size_t alignToAllocationGranularity(size_t s) { + static size_t granularity = [] { + static SYSTEM_INFO inf; + GetSystemInfo(&inf); + return inf.dwAllocationGranularity; + }(); + return (s + granularity - 1) / granularity * granularity; +} + +extern "C" { +int madvise(const void* /* addr */, size_t /* len */, int /* advise */) { + // We do nothing at all. + // Could probably implement dontneed via VirtualAlloc + // with the MEM_RESET and MEM_RESET_UNDO flags. + return 0; +} + +int mlock(const void* addr, size_t len) { + // For some strange reason, it's allowed to + // lock a nullptr as long as length is zero. + // VirtualLock doesn't allow it, so handle + // it specially. + if (addr == nullptr && len == 0) { + return 0; + } + if (!VirtualLock((void*)addr, len)) { + return -1; + } + return 0; +} + +namespace { +constexpr uint32_t kMMapLengthMagic = 0xFACEB00C; +struct MemMapDebugTrailer { + size_t length; + uint32_t magic; +}; +} // namespace + +void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) { + // Make sure it's something we support first. + + // No Anon shared. + if ((flags & (MAP_ANONYMOUS | MAP_SHARED)) == (MAP_ANONYMOUS | MAP_SHARED)) { + return MAP_FAILED; + } + // No private copy on write. + // If the map isn't writable, we can let it go through as + // whether changes to the underlying file are reflected in the map + // is defined to be unspecified by the standard. + if ((flags & MAP_PRIVATE) == MAP_PRIVATE && + (prot & PROT_WRITE) == PROT_WRITE && fd != -1) { + return MAP_FAILED; + } + // Map isn't anon, must be file backed. + if (!(flags & MAP_ANONYMOUS) && fd == -1) { + return MAP_FAILED; + } + + DWORD newProt; + DWORD accessFlags; + if (!mmap_to_page_protection(prot, newProt, accessFlags)) { + return MAP_FAILED; + } + + void* ret; + if (!(flags & MAP_ANONYMOUS) || (flags & MAP_SHARED)) { + HANDLE h = INVALID_HANDLE_VALUE; + if (!(flags & MAP_ANONYMOUS)) { + h = (HANDLE)_get_osfhandle(fd); + } + + HANDLE fmh = CreateFileMapping( + h, + nullptr, + newProt, + (DWORD)((length >> 32) & 0xFFFFFFFF), + (DWORD)(length & 0xFFFFFFFF), + nullptr); + if (fmh == nullptr) { + return MAP_FAILED; + } + ret = MapViewOfFileEx( + fmh, + accessFlags, + (DWORD)(0), // off_t is only 32-bit :( + (DWORD)(off & 0xFFFFFFFF), + 0, + addr); + if (ret == nullptr) { + ret = MAP_FAILED; + } + CloseHandle(fmh); + } else { + auto baseLength = length; + if (folly::kIsDebug) { + // In debug mode we keep track of the length to make + // sure you're only munmapping the entire thing if + // we're using VirtualAlloc. + length += sizeof(MemMapDebugTrailer); + } + + // VirtualAlloc rounds size down to a multiple + // of the system allocation granularity :( + length = alignToAllocationGranularity(length); + ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt); + if (ret == nullptr) { + return MAP_FAILED; + } + + if (folly::kIsDebug) { + auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength); + deb->length = baseLength; + deb->magic = kMMapLengthMagic; + } + } + + // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory + // Should also see about implementing MAP_NORESERVE + return ret; +} + +int mprotect(void* addr, size_t size, int prot) { + DWORD newProt; + DWORD access; + if (!mmap_to_page_protection(prot, newProt, access)) { + return -1; + } + + DWORD oldProt; + BOOL res = VirtualProtect(addr, size, newProt, &oldProt); + if (!res) { + return -1; + } + return 0; +} + +int munlock(const void* addr, size_t length) { + // See comment in mlock + if (addr == nullptr && length == 0) { + return 0; + } + if (!VirtualUnlock((void*)addr, length)) { + return -1; + } + return 0; +} + +int munmap(void* addr, size_t length) { + // Try to unmap it as a file, otherwise VirtualFree. + if (!UnmapViewOfFile(addr)) { + if (folly::kIsDebug) { + // We can't do partial unmapping with Windows, so + // assert that we aren't trying to do that if we're + // in debug mode. + MEMORY_BASIC_INFORMATION inf; + VirtualQuery(addr, &inf, sizeof(inf)); + assert(inf.AllocationBase == addr); + + auto deb = (MemMapDebugTrailer*)((char*)addr + length); + assert(deb->length == length); + assert(deb->magic == kMMapLengthMagic); + } + if (!VirtualFree(addr, 0, MEM_RELEASE)) { + return -1; + } + return 0; + } + return 0; +} +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysMman.h b/ios/Pods/Flipper-Folly/folly/portability/SysMman.h new file mode 100644 index 000000000..1b7c0a967 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysMman.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 + +#include <sys/mman.h> + +// MAP_ANONYMOUS is named MAP_ANON on OSX/BSD. +#if defined(__APPLE__) || defined(__FreeBSD__) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif +#endif + +#else + +#include <cstdint> + +#include <sys/types.h> + +#define MAP_ANONYMOUS 1 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_SHARED 2 +#define MAP_PRIVATE 4 +#define MAP_POPULATE 8 +#define MAP_NORESERVE 16 +#define MAP_FIXED 32 + +#define MAP_FAILED ((void*)-1) + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MADV_NORMAL 0 +#define MADV_DONTNEED 0 +#define MADV_SEQUENTIAL 0 + +extern "C" { +int madvise(const void* addr, size_t len, int advise); +int mlock(const void* addr, size_t len); +void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off); +int mprotect(void* addr, size_t size, int prot); +int munlock(const void* addr, size_t length); +int munmap(void* addr, size_t length); +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysResource.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysResource.cpp new file mode 100644 index 000000000..fbd12ef1e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysResource.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysResource.h> + +#include <cerrno> + +#ifdef _WIN32 +#include <folly/portability/Windows.h> + +extern "C" { +int getrlimit(int type, rlimit* dst) { + if (type == RLIMIT_STACK) { + NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); + dst->rlim_cur = (size_t)tib->StackBase - (size_t)tib->StackLimit; + dst->rlim_max = dst->rlim_cur; + return 0; + } + return -1; +} + +int getrusage(int /* who */, rusage* usage) { + // You get NOTHING! Good day to you sir. + ZeroMemory(usage, sizeof(rusage)); + return 0; +} + +int setrlimit(int /* type */, rlimit* /* src */) { + // Do nothing for setting them for now. + // We couldn't set the stack size at runtime even if we wanted to. + return 0; +} + +int getpriority(int which, int who) { + if (which != PRIO_PROCESS || who != 0) { + errno = EINVAL; + return -1; + } + + auto ret = GetPriorityClass(GetCurrentProcess()); + switch (ret) { + case 0: + errno = EACCES; + return -1; + case IDLE_PRIORITY_CLASS: + return 39; + case BELOW_NORMAL_PRIORITY_CLASS: + return 30; + case NORMAL_PRIORITY_CLASS: + return 20; + case ABOVE_NORMAL_PRIORITY_CLASS: + return 10; + case HIGH_PRIORITY_CLASS: + return 0; + case REALTIME_PRIORITY_CLASS: + // I'd return -1 if it weren't an error :( + // Realtime priority processes can't be set + // through these APIs because it's a terrible idea. + return 0; + default: + errno = EINVAL; + return -1; + } +} + +int setpriority(int which, int who, int value) { + if (which != PRIO_PROCESS || who != 0) { + errno = EINVAL; + return -1; + } + + auto newClass = [value] { + if (value >= 39) { + return IDLE_PRIORITY_CLASS; + } else if (value >= 30) { + return BELOW_NORMAL_PRIORITY_CLASS; + } else if (value >= 20) { + return NORMAL_PRIORITY_CLASS; + } else if (value >= 10) { + return ABOVE_NORMAL_PRIORITY_CLASS; + } else { + return HIGH_PRIORITY_CLASS; + } + }(); + + if (!SetPriorityClass(GetCurrentProcess(), newClass)) { + errno = EACCES; + return -1; + } + return 0; +} +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysResource.h b/ios/Pods/Flipper-Folly/folly/portability/SysResource.h new file mode 100644 index 000000000..9a5e4d1df --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysResource.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <sys/resource.h> +#else +#include <cstdint> + +#include <folly/portability/SysTime.h> + +#define PRIO_PROCESS 1 + +#define RLIMIT_CORE 0 +#define RLIMIT_NOFILE 0 +#define RLIMIT_DATA 0 +#define RLIMIT_STACK 3 +#define RLIM_INFINITY SIZE_MAX + +#define RUSAGE_SELF 0 +#define RUSAGE_CHILDREN 0 +#define RUSAGE_THREAD 0 + +using rlim_t = size_t; +struct rlimit { + rlim_t rlim_cur; + rlim_t rlim_max; +}; + +struct rusage { + timeval ru_utime; + timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +extern "C" { +int getrlimit(int type, rlimit* dst); +int getrusage(int who, rusage* usage); +int setrlimit(int type, rlimit* src); + +int getpriority(int which, int who); +int setpriority(int which, int who, int value); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysStat.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysStat.cpp new file mode 100644 index 000000000..fde705723 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysStat.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysStat.h> + +#ifdef _WIN32 +#include <folly/portability/Windows.h> + +extern "C" { +int chmod(char const* fn, int am) { + return _chmod(fn, am); +} + +int fchmod(int fd, mode_t mode) { + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + + FILE_ATTRIBUTE_TAG_INFO attr{}; + if (!GetFileInformationByHandleEx( + h, FileAttributeTagInfo, &attr, sizeof(attr))) { + return -1; + } + + if (mode & _S_IWRITE) { + attr.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; + } else { + attr.FileAttributes |= FILE_ATTRIBUTE_READONLY; + } + + if (!SetFileInformationByHandle( + h, FileAttributeTagInfo, &attr, sizeof(attr))) { + return -1; + } + + return 0; +} + +// Just return the result of a normal stat for now +int lstat(const char* path, struct stat* st) { + return stat(path, st); +} + +int mkdir(const char* fn, int /* mode */) { + return _mkdir(fn); +} + +int umask(int md) { + return _umask(md); +} +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysStat.h b/ios/Pods/Flipper-Folly/folly/portability/SysStat.h new file mode 100644 index 000000000..06c27f435 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysStat.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/stat.h> + +#ifdef _WIN32 +#include <folly/portability/SysTypes.h> + +// Windows gives weird names to these. +#define S_IXUSR 0 +#define S_IWUSR _S_IWRITE +#define S_IRUSR _S_IREAD +// No group/other permissions so default to user. +#define S_IXGRP S_IXUSR +#define S_IWGRP S_IWUSR +#define S_IRGRP S_IRUSR +#define S_IXOTH S_IXUSR +#define S_IWOTH S_IWUSR +#define S_IROTH S_IRUSR +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) + +#define S_ISDIR(mode) (((mode) & (_S_IFDIR)) == (_S_IFDIR) ? 1 : 0) + +// This isn't defined anywhere, so give a sane value. +#define MAXSYMLINKS 255 + +extern "C" { +int chmod(char const* fn, int am); +int fchmod(int fd, mode_t mode); +int lstat(const char* path, struct stat* st); +int mkdir(const char* fn, int mode); +int umask(int md); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysSyscall.h b/ios/Pods/Flipper-Folly/folly/portability/SysSyscall.h new file mode 100644 index 000000000..e7060fd4a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysSyscall.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <sys/syscall.h> + +#if defined(__APPLE__) +#define FOLLY_SYS_gettid SYS_thread_selfid +#elif defined(SYS_gettid) +#define FOLLY_SYS_gettid SYS_gettid +#else +#define FOLLY_SYS_gettid __NR_gettid +#endif +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysTime.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysTime.cpp new file mode 100644 index 000000000..55fc0601c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysTime.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysTime.h> + +#ifdef _WIN32 + +#include <cstdint> + +extern "C" { +int gettimeofday(timeval* tv, struct timezone*) { + constexpr auto posixWinFtOffset = 116444736000000000ULL; + + if (tv) { + FILETIME ft; + ULARGE_INTEGER lft; + GetSystemTimeAsFileTime(&ft); + // As per the docs of FILETIME, don't just do an indirect + // pointer cast, to avoid alignment faults. + lft.HighPart = ft.dwHighDateTime; + lft.LowPart = ft.dwLowDateTime; + uint64_t ns = lft.QuadPart; + tv->tv_usec = (long)((ns / 10ULL) % 1000000ULL); + tv->tv_sec = (long)((ns - posixWinFtOffset) / 10000000ULL); + } + + return 0; +} + +void timeradd(timeval* a, timeval* b, timeval* res) { + res->tv_sec = a->tv_sec + b->tv_sec; + res->tv_usec = a->tv_usec + b->tv_usec; + if (res->tv_usec >= 1000000) { + res->tv_sec++; + res->tv_usec -= 1000000; + } +} + +void timersub(timeval* a, timeval* b, timeval* res) { + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_usec = a->tv_usec - b->tv_usec; + if (res->tv_usec < 0) { + res->tv_sec--; + res->tv_usec += 1000000; + } +} +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysTime.h b/ios/Pods/Flipper-Folly/folly/portability/SysTime.h new file mode 100644 index 000000000..516aa7e9b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysTime.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <sys/time.h> +#else +// Someone decided this was a good place to define timeval..... +#include <folly/portability/Windows.h> +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +extern "C" { +// Note that this needs to explicitly be `struct timezone` due to the fact that +// the python 3 headers `#define timezone _timezone` on Windows. `_timezone` is +// a global field that contains information on the current timezone. By +// explicitly specifying that this is a `struct`, we ensure that it's treated as +// a type, regardless of what name that type actually is :) +// Note that this will break if `gettimeofday` ever becomes declared as anything +// other than `extern "C"`, as the mangled name would be dependent on whether +// python had been included before this header. +int gettimeofday(timeval* tv, struct timezone*); +void timeradd(timeval* a, timeval* b, timeval* res); +void timersub(timeval* a, timeval* b, timeval* res); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysTypes.h b/ios/Pods/Flipper-Folly/folly/portability/SysTypes.h new file mode 100644 index 000000000..42e0c381a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysTypes.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <sys/types.h> + +#ifdef _WIN32 +#include <basetsd.h> // @manual + +// This is a massive pain to have be an `int` due to the pthread implementation +// we support, but it's far more compatible with the rest of the windows world +// as an `int` than it would be as a `void*` +using pid_t = int; +// This isn't actually supposed to be defined here, but it's the most +// appropriate place without defining a portability header for stdint.h +// with just this single typedef. +using ssize_t = SSIZE_T; + +#ifndef HAVE_MODE_T +#define HAVE_MODE_T 1 +// The Windows headers don't define this anywhere, nor do any of the libs +// that Folly depends on, so define it here. +using mode_t = unsigned int; +#endif + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysUio.cpp b/ios/Pods/Flipper-Folly/folly/portability/SysUio.cpp new file mode 100644 index 000000000..e516b4395 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysUio.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/SysUio.h> + +#include <cerrno> +#include <cstdio> + +#include <folly/ScopeGuard.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/SysFile.h> +#include <folly/portability/Unistd.h> + +template <class F, class... Args> +static int wrapPositional(F f, int fd, off_t offset, Args... args) { + off_t origLoc = lseek(fd, 0, SEEK_CUR); + if (origLoc == off_t(-1)) { + return -1; + } + if (lseek(fd, offset, SEEK_SET) == off_t(-1)) { + return -1; + } + + int res = (int)f(fd, args...); + + int curErrNo = errno; + if (lseek(fd, origLoc, SEEK_SET) == off_t(-1)) { + if (res == -1) { + errno = curErrNo; + } + return -1; + } + errno = curErrNo; + + return res; +} + +#if !FOLLY_HAVE_PREADV +extern "C" ssize_t preadv(int fd, const iovec* iov, int count, off_t offset) { + return wrapPositional(readv, fd, offset, iov, count); +} +#endif + +#if !FOLLY_HAVE_PWRITEV +extern "C" ssize_t pwritev(int fd, const iovec* iov, int count, off_t offset) { + return wrapPositional(writev, fd, offset, iov, count); +} +#endif + +#ifdef _WIN32 +template <bool isRead> +static ssize_t doVecOperation(int fd, const iovec* iov, int count) { + if (!count) { + return 0; + } + if (count < 0 || count > folly::kIovMax) { + errno = EINVAL; + return -1; + } + + // We only need to worry about locking if the file descriptor is + // not a socket. We have no way of locking sockets :( + // The correct way to do this for sockets is via sendmsg/recvmsg, + // but this is good enough for now. + bool shouldLock = !folly::portability::sockets::is_fh_socket(fd); + if (shouldLock && lockf(fd, F_LOCK, 0) == -1) { + return -1; + } + SCOPE_EXIT { + if (shouldLock) { + lockf(fd, F_ULOCK, 0); + } + }; + + ssize_t bytesProcessed = 0; + int curIov = 0; + void* curBase = iov[0].iov_base; + size_t curLen = iov[0].iov_len; + while (curIov < count) { + ssize_t res = 0; + if (isRead) { + res = read(fd, curBase, (unsigned int)curLen); + if (res == 0 && curLen != 0) { + break; // End of File + } + } else { + res = write(fd, curBase, (unsigned int)curLen); + // Write of zero bytes is fine. + } + + if (res == -1) { + return -1; + } + + if (size_t(res) == curLen) { + curIov++; + if (curIov < count) { + curBase = iov[curIov].iov_base; + curLen = iov[curIov].iov_len; + } + } else { + curBase = (void*)((char*)curBase + res); + curLen -= res; + } + + if (bytesProcessed + res < 0) { + // Overflow + errno = EINVAL; + return -1; + } + bytesProcessed += res; + } + + return bytesProcessed; +} + +extern "C" ssize_t readv(int fd, const iovec* iov, int count) { + return doVecOperation<true>(fd, iov, count); +} + +extern "C" ssize_t writev(int fd, const iovec* iov, int count) { + return doVecOperation<false>(fd, iov, count); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/SysUio.h b/ios/Pods/Flipper-Folly/folly/portability/SysUio.h new file mode 100644 index 000000000..2c9a7ec85 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/SysUio.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/Config.h> +#include <folly/portability/IOVec.h> +#include <folly/portability/SysTypes.h> + +#if !FOLLY_HAVE_PREADV +extern "C" ssize_t preadv(int fd, const iovec* iov, int count, off_t offset); +#endif +#if !FOLLY_HAVE_PWRITEV +extern "C" ssize_t pwritev(int fd, const iovec* iov, int count, off_t offset); +#endif + +#ifdef _WIN32 +extern "C" ssize_t readv(int fd, const iovec* iov, int count); +extern "C" ssize_t writev(int fd, const iovec* iov, int count); +#endif + +namespace folly { +#ifdef IOV_MAX // not defined on Android +constexpr size_t kIovMax = IOV_MAX; +#else +constexpr size_t kIovMax = UIO_MAXIOV; +#endif +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/portability/Syslog.h b/ios/Pods/Flipper-Folly/folly/portability/Syslog.h new file mode 100644 index 000000000..b2d45ecd8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Syslog.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 +#include <syslog.h> +#else + +#define LOG_EMERG 1 +#define LOG_ALERT 1 +#define LOG_CRIT 1 +#define LOG_ERR 4 +#define LOG_WARNING 5 +#define LOG_NOTICE 6 +#define LOG_INFO 6 +#define LOG_DEBUG 6 + +extern "C" { +// Do nothing for the system log for now. +inline void openlog(const char*, int, int) {} +inline void closelog() {} +inline void syslog(int, const char*, ...) {} +} + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Time.cpp b/ios/Pods/Flipper-Folly/folly/portability/Time.cpp new file mode 100644 index 000000000..44863c5ba --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Time.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/portability/Time.h> + +#include <folly/CPortability.h> +#include <folly/Likely.h> + +#include <cassert> + +#include <chrono> + +template <typename _Rep, typename _Period> +static void duration_to_ts( + std::chrono::duration<_Rep, _Period> d, + struct timespec* ts) { + ts->tv_sec = + time_t(std::chrono::duration_cast<std::chrono::seconds>(d).count()); + ts->tv_nsec = long(std::chrono::duration_cast<std::chrono::nanoseconds>( + d % std::chrono::seconds(1)) + .count()); +} + +#if !FOLLY_HAVE_CLOCK_GETTIME || FOLLY_FORCE_CLOCK_GETTIME_DEFINITION +#if __MACH__ +#include <errno.h> +#include <mach/mach_init.h> // @manual +#include <mach/mach_port.h> // @manual +#include <mach/mach_time.h> // @manual +#include <mach/mach_types.h> // @manual +#include <mach/task.h> // @manual +#include <mach/thread_act.h> // @manual +#include <mach/vm_map.h> // @manual + +static std::chrono::nanoseconds time_value_to_ns(time_value_t t) { + return std::chrono::seconds(t.seconds) + + std::chrono::microseconds(t.microseconds); +} + +static int clock_process_cputime(struct timespec* ts) { + // Get CPU usage for live threads. + task_thread_times_info thread_times_info; + mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT; + kern_return_t kern_result = task_info( + mach_task_self(), + TASK_THREAD_TIMES_INFO, + (thread_info_t)&thread_times_info, + &thread_times_info_count); + if (UNLIKELY(kern_result != KERN_SUCCESS)) { + return -1; + } + + // Get CPU usage for terminated threads. + mach_task_basic_info task_basic_info; + mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT; + kern_result = task_info( + mach_task_self(), + MACH_TASK_BASIC_INFO, + (thread_info_t)&task_basic_info, + &task_basic_info_count); + if (UNLIKELY(kern_result != KERN_SUCCESS)) { + return -1; + } + + auto cputime = time_value_to_ns(thread_times_info.user_time) + + time_value_to_ns(thread_times_info.system_time) + + time_value_to_ns(task_basic_info.user_time) + + time_value_to_ns(task_basic_info.system_time); + duration_to_ts(cputime, ts); + return 0; +} + +static int clock_thread_cputime(struct timespec* ts) { + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t thread_info_data; + thread_act_t thread = mach_thread_self(); + kern_return_t kern_result = thread_info( + thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count); + mach_port_deallocate(mach_task_self(), thread); + if (UNLIKELY(kern_result != KERN_SUCCESS)) { + return -1; + } + auto cputime = time_value_to_ns(thread_info_data.system_time) + + time_value_to_ns(thread_info_data.user_time); + duration_to_ts(cputime, ts); + return 0; +} + +FOLLY_ATTR_WEAK int clock_gettime(clockid_t clk_id, struct timespec* ts) { + switch (clk_id) { + case CLOCK_REALTIME: { + auto now = std::chrono::system_clock::now().time_since_epoch(); + duration_to_ts(now, ts); + return 0; + } + case CLOCK_MONOTONIC: { + auto now = std::chrono::steady_clock::now().time_since_epoch(); + duration_to_ts(now, ts); + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: + return clock_process_cputime(ts); + case CLOCK_THREAD_CPUTIME_ID: + return clock_thread_cputime(ts); + default: + errno = EINVAL; + return -1; + } +} + +int clock_getres(clockid_t clk_id, struct timespec* ts) { + if (clk_id != CLOCK_MONOTONIC) { + return -1; + } + + static auto info = [] { + static mach_timebase_info_data_t info; + auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr; + assert(result); + return result; + }(); + + ts->tv_sec = 0; + ts->tv_nsec = info->numer / info->denom; + + return 0; +} +#elif defined(_WIN32) +#include <errno.h> +#include <locale.h> +#include <stdint.h> +#include <stdlib.h> + +#include <folly/portability/Windows.h> + +using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>; + +static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) { + ULARGE_INTEGER i; + i.HighPart = ft.dwHighDateTime; + i.LowPart = ft.dwLowDateTime; + + // FILETIMEs are in units of 100ns. + return unsigned_nanos(i.QuadPart * 100); +}; + +static LARGE_INTEGER performanceFrequency() { + static auto result = [] { + LARGE_INTEGER freq; + // On Windows XP or later, this will never fail. + BOOL res = QueryPerformanceFrequency(&freq); + assert(res); + return freq; + }(); + return result; +} + +extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) { + if (!res) { + errno = EFAULT; + return -1; + } + + static constexpr size_t kNsPerSec = 1000000000; + switch (clock_id) { + case CLOCK_REALTIME: { + constexpr auto perSec = double(std::chrono::system_clock::period::num) / + std::chrono::system_clock::period::den; + res->tv_sec = time_t(perSec); + res->tv_nsec = time_t(perSec * kNsPerSec); + return 0; + } + case CLOCK_MONOTONIC: { + constexpr auto perSec = double(std::chrono::steady_clock::period::num) / + std::chrono::steady_clock::period::den; + res->tv_sec = time_t(perSec); + res->tv_nsec = time_t(perSec * kNsPerSec); + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: { + DWORD adj, timeIncrement; + BOOL adjDisabled; + if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) { + errno = EINVAL; + return -1; + } + + res->tv_sec = 0; + res->tv_nsec = long(timeIncrement * 100); + return 0; + } + + default: + errno = EINVAL; + return -1; + } +} + +extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) { + if (!tp) { + errno = EFAULT; + return -1; + } + + const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int { + static constexpr unsigned_nanos one_sec{std::chrono::seconds(1)}; + tp->tv_sec = + time_t(std::chrono::duration_cast<std::chrono::seconds>(t).count()); + tp->tv_nsec = long((t % one_sec).count()); + return 0; + }; + + FILETIME createTime, exitTime, kernalTime, userTime; + switch (clock_id) { + case CLOCK_REALTIME: { + auto now = std::chrono::system_clock::now().time_since_epoch(); + duration_to_ts(now, tp); + return 0; + } + case CLOCK_MONOTONIC: { + auto now = std::chrono::steady_clock::now().time_since_epoch(); + duration_to_ts(now, tp); + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: { + if (!GetProcessTimes( + GetCurrentProcess(), + &createTime, + &exitTime, + &kernalTime, + &userTime)) { + errno = EINVAL; + return -1; + } + + return unanosToTimespec( + tp, + filetimeToUnsignedNanos(kernalTime) + + filetimeToUnsignedNanos(userTime)); + } + case CLOCK_THREAD_CPUTIME_ID: { + if (!GetThreadTimes( + GetCurrentThread(), + &createTime, + &exitTime, + &kernalTime, + &userTime)) { + errno = EINVAL; + return -1; + } + + return unanosToTimespec( + tp, + filetimeToUnsignedNanos(kernalTime) + + filetimeToUnsignedNanos(userTime)); + } + + default: + errno = EINVAL; + return -1; + } +} +#else +#error No clock_gettime(3) compatibility wrapper available for this platform. +#endif +#endif + +#ifdef _WIN32 +#include <iomanip> +#include <sstream> + +#include <folly/portability/Windows.h> + +extern "C" { +char* asctime_r(const tm* tm, char* buf) { + char tmpBuf[64]; + if (asctime_s(tmpBuf, tm)) { + return nullptr; + } + // Nothing we can do if the buff is to small :( + return strcpy(buf, tmpBuf); +} + +char* ctime_r(const time_t* t, char* buf) { + char tmpBuf[64]; + if (ctime_s(tmpBuf, 64, t)) { + return nullptr; + } + // Nothing we can do if the buff is to small :( + return strcpy(buf, tmpBuf); +} + +tm* gmtime_r(const time_t* t, tm* res) { + if (!gmtime_s(res, t)) { + return res; + } + return nullptr; +} + +tm* localtime_r(const time_t* t, tm* o) { + if (!localtime_s(o, t)) { + return o; + } + return nullptr; +} + +int nanosleep(const struct timespec* request, struct timespec* remain) { + Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000))); + if (remain != nullptr) { + remain->tv_nsec = 0; + remain->tv_sec = 0; + } + return 0; +} + +char* strptime( + const char* __restrict s, + const char* __restrict f, + struct tm* __restrict tm) { + // Isn't the C++ standard lib nice? std::get_time is defined such that its + // format parameters are the exact same as strptime. Of course, we have to + // create a string stream first, and imbue it with the current C locale, and + // we also have to make sure we return the right things if it fails, or + // if it succeeds, but this is still far simpler an implementation than any + // of the versions in any of the C standard libraries. + std::istringstream input(s); + input.imbue(std::locale(setlocale(LC_ALL, nullptr))); + input >> std::get_time(tm, f); + if (input.fail()) { + return nullptr; + } + return const_cast<char*>(s + input.tellg()); +} + +time_t timelocal(tm* tm) { + return mktime(tm); +} + +time_t timegm(tm* tm) { + return _mkgmtime(tm); +} +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Time.h b/ios/Pods/Flipper-Folly/folly/portability/Time.h new file mode 100644 index 000000000..d8127d106 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Time.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <stdint.h> +#include <time.h> + +#include <folly/portability/Config.h> + +// OSX is a pain. The XCode 8 SDK always declares clock_gettime +// even if the target OS version doesn't support it, so you get +// an error at runtime because it can't resolve the symbol. We +// solve that by pretending we have it here in the header and +// then enable our implementation on the source side so that +// gets linked in instead. +#if __MACH__ && \ + (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 || \ + __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) + +#ifdef FOLLY_HAVE_CLOCK_GETTIME +#undef FOLLY_HAVE_CLOCK_GETTIME +#endif + +#define FOLLY_HAVE_CLOCK_GETTIME 1 +#define FOLLY_FORCE_CLOCK_GETTIME_DEFINITION 1 + +#endif + +// These aren't generic implementations, so we can only declare them on +// platforms we support. +#if !FOLLY_HAVE_CLOCK_GETTIME && (defined(__MACH__) || defined(_WIN32)) +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 + +typedef uint8_t clockid_t; +extern "C" int clock_gettime(clockid_t clk_id, struct timespec* ts); +extern "C" int clock_getres(clockid_t clk_id, struct timespec* ts); +#endif + +#ifdef _WIN32 +#define TM_YEAR_BASE (1900) + +extern "C" { +char* asctime_r(const tm* tm, char* buf); +char* ctime_r(const time_t* t, char* buf); +tm* gmtime_r(const time_t* t, tm* res); +tm* localtime_r(const time_t* t, tm* o); +int nanosleep(const struct timespec* request, struct timespec* remain); +char* strptime( + const char* __restrict buf, + const char* __restrict fmt, + struct tm* __restrict tm); +time_t timelocal(tm* tm); +time_t timegm(tm* tm); +} +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Unistd.cpp b/ios/Pods/Flipper-Folly/folly/portability/Unistd.cpp new file mode 100644 index 000000000..af01b9d7d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Unistd.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// We need to prevent winnt.h from defining the core STATUS codes, +// otherwise they will conflict with what we're getting from ntstatus.h +#define UMDF_USING_NTSTATUS + +#include <folly/portability/Unistd.h> + +#ifdef _WIN32 + +#include <cstdio> + +#include <fcntl.h> + +#include <folly/net/detail/SocketFileDescriptorMap.h> +#include <folly/portability/Sockets.h> +#include <folly/portability/Windows.h> + +// Generic wrapper for the p* family of functions. +template <class F, class... Args> +static int wrapPositional(F f, int fd, off_t offset, Args... args) { + off_t origLoc = lseek(fd, 0, SEEK_CUR); + if (origLoc == (off_t)-1) { + return -1; + } + if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { + return -1; + } + + int res = (int)f(fd, args...); + + int curErrNo = errno; + if (lseek(fd, origLoc, SEEK_SET) == (off_t)-1) { + if (res == -1) { + errno = curErrNo; + } + return -1; + } + errno = curErrNo; + + return res; +} + +namespace folly { +namespace portability { +namespace unistd { +int access(char const* fn, int am) { + return _access(fn, am); +} + +int chdir(const char* path) { + return _chdir(path); +} + +int close(int fh) { + if (folly::portability::sockets::is_fh_socket(fh)) { + return netops::detail::SocketFileDescriptorMap::close(fh); + } + return _close(fh); +} + +int dup(int fh) { + return _dup(fh); +} + +int dup2(int fhs, int fhd) { + return _dup2(fhs, fhd); +} + +int fsync(int fd) { + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + if (!FlushFileBuffers(h)) { + return -1; + } + return 0; +} + +int ftruncate(int fd, off_t len) { + if (_lseek(fd, len, SEEK_SET) == -1) { + return -1; + } + + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + if (!SetEndOfFile(h)) { + return -1; + } + return 0; +} + +char* getcwd(char* buf, int sz) { + return _getcwd(buf, sz); +} + +int getdtablesize() { + return _getmaxstdio(); +} + +int getgid() { + return 1; +} + +pid_t getpid() { + return (pid_t)uint64_t(GetCurrentProcessId()); +} + +// No major need to implement this, and getting a non-potentially +// stale ID on windows is a bit involved. +pid_t getppid() { + return (pid_t)1; +} + +int getuid() { + return 1; +} + +int isatty(int fh) { + return _isatty(fh); +} + +int lockf(int fd, int cmd, off_t len) { + return _locking(fd, cmd, len); +} + +off_t lseek(int fh, off_t off, int orig) { + return _lseek(fh, off, orig); +} + +int rmdir(const char* path) { + return _rmdir(path); +} + +int pipe(int pth[2]) { + // We need to be able to listen to pipes with + // libevent, so they need to be actual sockets. + return socketpair(PF_UNIX, SOCK_STREAM, 0, pth); +} + +ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + return wrapPositional(_read, fd, offset, buf, (unsigned int)count); +} + +ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset) { + return wrapPositional(_write, fd, offset, buf, (unsigned int)count); +} + +ssize_t read(int fh, void* buf, size_t count) { + if (folly::portability::sockets::is_fh_socket(fh)) { + SOCKET s = (SOCKET)_get_osfhandle(fh); + if (s != INVALID_SOCKET) { + auto r = folly::portability::sockets::recv(fh, buf, count, 0); + if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { + errno = EAGAIN; + } + return r; + } + } + auto r = _read(fh, buf, static_cast<unsigned int>(count)); + if (r == -1 && GetLastError() == ERROR_NO_DATA) { + // This only happens if the file was non-blocking and + // no data was present. We have to translate the error + // to a form that the rest of the world is expecting. + errno = EAGAIN; + } + return r; +} + +ssize_t readlink(const char* path, char* buf, size_t buflen) { + if (!buflen) { + return -1; + } + + HANDLE h = CreateFileA( + path, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + + DWORD ret = + GetFinalPathNameByHandleA(h, buf, DWORD(buflen - 1), VOLUME_NAME_DOS); + if (ret >= buflen || ret >= MAX_PATH || !ret) { + CloseHandle(h); + return -1; + } + + CloseHandle(h); + buf[ret] = '\0'; + return ret; +} + +void* sbrk(intptr_t /* i */) { + return (void*)-1; +} + +unsigned int sleep(unsigned int seconds) { + Sleep((DWORD)(seconds * 1000)); + return 0; +} + +long sysconf(int tp) { + switch (tp) { + case _SC_PAGESIZE: { + SYSTEM_INFO inf; + GetSystemInfo(&inf); + return (long)inf.dwPageSize; + } + case _SC_NPROCESSORS_ONLN: { + SYSTEM_INFO inf; + GetSystemInfo(&inf); + return (long)inf.dwNumberOfProcessors; + } + default: + return -1L; + } +} + +int truncate(const char* path, off_t len) { + int fd = _open(path, O_WRONLY); + if (!fd) { + return -1; + } + if (ftruncate(fd, len)) { + _close(fd); + return -1; + } + return _close(fd) ? -1 : 0; +} + +int usleep(unsigned int ms) { + Sleep((DWORD)(ms / 1000)); + return 0; +} + +ssize_t write(int fh, void const* buf, size_t count) { + if (folly::portability::sockets::is_fh_socket(fh)) { + SOCKET s = (SOCKET)_get_osfhandle(fh); + if (s != INVALID_SOCKET) { + auto r = folly::portability::sockets::send(fh, buf, (size_t)count, 0); + if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { + errno = EAGAIN; + } + return r; + } + } + auto r = _write(fh, buf, static_cast<unsigned int>(count)); + if ((r > 0 && size_t(r) != count) || (r == -1 && errno == ENOSPC)) { + // Writing to a pipe with a full buffer doesn't generate + // any error type, unless it caused us to write exactly 0 + // bytes, so we have to see if we have a pipe first. We + // don't touch the errno for anything else. + HANDLE h = (HANDLE)_get_osfhandle(fh); + if (GetFileType(h) == FILE_TYPE_PIPE) { + DWORD state = 0; + if (GetNamedPipeHandleState( + h, &state, nullptr, nullptr, nullptr, nullptr, 0)) { + if ((state & PIPE_NOWAIT) == PIPE_NOWAIT) { + errno = EAGAIN; + return -1; + } + } + } + } + return r; +} +} // namespace unistd +} // namespace portability +} // namespace folly + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Unistd.h b/ios/Pods/Flipper-Folly/folly/portability/Unistd.h new file mode 100644 index 000000000..0253f6b6b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Unistd.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _WIN32 + +#include <unistd.h> + +#else + +#include <cstdint> + +#include <sys/locking.h> // @manual + +#include <folly/Portability.h> +#include <folly/portability/SysTypes.h> + +// This is different from the normal headers because there are a few cases, +// such as close(), where we need to override the definition of an existing +// function. To avoid conflicts at link time, everything here is in a namespace +// which is then used globally. + +#define _SC_PAGESIZE 1 +#define _SC_PAGE_SIZE _SC_PAGESIZE +#define _SC_NPROCESSORS_ONLN 2 +#define _SC_NPROCESSORS_CONF 2 + +// Windows doesn't define these, but these are the correct values +// for Windows. +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +// Windows is weird and doesn't actually defined these +// for the parameters to access, so we have to do it ourselves -_-... +#define F_OK 0 +#define X_OK F_OK +#define W_OK 2 +#define R_OK 4 +#define RW_OK 6 + +#define F_LOCK _LK_LOCK +#define F_ULOCK _LK_UNLCK + +namespace folly { +namespace portability { +namespace unistd { +int access(char const* fn, int am); +int chdir(const char* path); +int close(int fh); +int dup(int fh); +int dup2(int fhs, int fhd); +int fsync(int fd); +int ftruncate(int fd, off_t len); +char* getcwd(char* buf, int sz); +int getdtablesize(); +int getgid(); +pid_t getpid(); +pid_t getppid(); +int getuid(); +int isatty(int fh); +int lockf(int fd, int cmd, off_t len); +off_t lseek(int fh, off_t off, int orig); +ssize_t read(int fh, void* buf, size_t mcc); +int rmdir(const char* path); +int pipe(int pth[2]); +ssize_t pread(int fd, void* buf, size_t count, off_t offset); +ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset); +ssize_t readlink(const char* path, char* buf, size_t buflen); +void* sbrk(intptr_t i); +unsigned int sleep(unsigned int seconds); +long sysconf(int tp); +int truncate(const char* path, off_t len); +int usleep(unsigned int ms); +ssize_t write(int fh, void const* buf, size_t count); +} // namespace unistd +} // namespace portability +} // namespace folly + +FOLLY_PUSH_WARNING +FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene") +/* using override */ using namespace folly::portability::unistd; +FOLLY_POP_WARNING +#endif diff --git a/ios/Pods/Flipper-Folly/folly/portability/Windows.h b/ios/Pods/Flipper-Folly/folly/portability/Windows.h new file mode 100644 index 000000000..86fd0f998 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/portability/Windows.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// Only do anything if we are on windows. +#ifdef _WIN32 +// This header is intended to be used in-place of including <Windows.h>, +// <WinSock2.h>, <io.h> or <direct.h>. +// It includes all of them, and undefines certain names defined by them that +// are used in places in Folly. +// +// These have to be this way because we define our own versions +// of close(), because the normal Windows versions don't handle +// sockets at all. + +// There are some ordering issues internally in the SDK; we need to ensure +// stdio.h is included prior to including direct.h and io.h with internal names +// disabled to ensure all of the normal names get declared properly. +#include <stdio.h> + +#ifndef __STDC__ +/* nolint */ +#define __STDC__ 1 +#pragma push_macro("_CRT_DECLARE_NONSTDC_NAMES") +#ifdef _CRT_DECLARE_NONSTDC_NAMES +#undef _CRT_DECLARE_NONSTDC_NAMES +#endif +#pragma push_macro("_CRT_INTERNAL_NONSTDC_NAMES") +#undef _CRT_INTERNAL_NONSTDC_NAMES +#include <direct.h> // @manual nolint +#include <io.h> // @manual nolint +#undef __STDC__ +#pragma pop_macro("_CRT_INTERNAL_NONSTDC_NAMES") +#pragma pop_macro("_CRT_DECLARE_NONSTDC_NAMES") +#else +#include <direct.h> // @manual nolint +#include <io.h> // @manual nolint +#endif + +#if defined(min) || defined(max) +#error Windows.h needs to be included by this header, or else NOMINMAX needs \ + to be defined before including it yourself. +#endif + +// This is needed because, for some absurd reason, one of the windows headers +// tries to define "min" and "max" as macros, which messes up most uses of +// std::numeric_limits. +#ifndef NOMINMAX +#define NOMINMAX 1 +#endif + +#include <WinSock2.h> // @manual +#include <Windows.h> // @manual + +#ifdef CAL_GREGORIAN +#undef CAL_GREGORIAN +#endif + +// Defined in the GDI interface. +#ifdef ERROR +#undef ERROR +#endif + +// Defined in minwindef.h +#ifdef IN +#undef IN +#endif + +// Defined in winerror.h +#ifdef NO_ERROR +#undef NO_ERROR +#endif + +// Defined in minwindef.h +#ifdef OUT +#undef OUT +#endif + +// Defined in minwindef.h +#ifdef STRICT +#undef STRICT +#endif + +// Defined in Winbase.h +#ifdef Yield +#undef Yield +#endif + +#endif diff --git a/ios/Pods/Flipper-Folly/folly/small_vector.h b/ios/Pods/Flipper-Folly/folly/small_vector.h new file mode 100644 index 000000000..09374e500 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/small_vector.h @@ -0,0 +1,1321 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * For high-level documentation and usage examples see + * folly/docs/small_vector.md + * + * @author Jordan DeLong <delong.j@fb.com> + */ + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <cstring> +#include <iterator> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include <boost/mpl/count.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/eval_if.hpp> +#include <boost/mpl/filter_view.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/identity.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/placeholders.hpp> +#include <boost/mpl/size.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/operators.hpp> + +#include <folly/ConstexprMath.h> +#include <folly/FormatTraits.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> +#include <folly/lang/Assume.h> +#include <folly/lang/Exception.h> +#include <folly/memory/Malloc.h> +#include <folly/portability/Malloc.h> + +#if (FOLLY_X64 || FOLLY_PPC64) +#define FOLLY_SV_PACK_ATTR FOLLY_PACK_ATTR +#define FOLLY_SV_PACK_PUSH FOLLY_PACK_PUSH +#define FOLLY_SV_PACK_POP FOLLY_PACK_POP +#else +#define FOLLY_SV_PACK_ATTR +#define FOLLY_SV_PACK_PUSH +#define FOLLY_SV_PACK_POP +#endif + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +FOLLY_PUSH_WARNING +FOLLY_GNU_DISABLE_WARNING("-Wshadow") + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +namespace small_vector_policy { + +////////////////////////////////////////////////////////////////////// + +/* + * A flag which makes us refuse to use the heap at all. If we + * overflow the in situ capacity we throw an exception. + */ +struct NoHeap; + +////////////////////////////////////////////////////////////////////// + +} // namespace small_vector_policy + +////////////////////////////////////////////////////////////////////// + +template <class T, std::size_t M, class A, class B, class C> +class small_vector; + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +/* + * Move objects in memory to the right into some uninitialized memory, where + * the region overlaps. Then call create() for each hole in reverse order. + * + * This doesn't just use std::move_backward because move_backward only works + * if all the memory is initialized to type T already. + * + * The create function should return a reference type, to avoid + * extra copies and moves for non-trivial types. + */ +template <class T, class Create> +typename std::enable_if<!folly::is_trivially_copyable<T>::value>::type +moveObjectsRightAndCreate( + T* const first, + T* const lastConstructed, + T* const realLast, + Create&& create) { + if (lastConstructed == realLast) { + return; + } + + T* out = realLast; + T* in = lastConstructed; + { + auto rollback = makeGuard([&] { + // We want to make sure the same stuff is uninitialized memory + // if we exit via an exception (this is to make sure we provide + // the basic exception safety guarantee for insert functions). + if (out < lastConstructed) { + out = lastConstructed - 1; + } + for (auto it = out + 1; it != realLast; ++it) { + it->~T(); + } + }); + // Decrement the pointers only when it is known that the resulting pointer + // is within the boundaries of the object. Decrementing past the beginning + // of the object is UB. Note that this is asymmetric wrt forward iteration, + // as past-the-end pointers are explicitly allowed. + for (; in != first && out > lastConstructed;) { + // Out must be decremented before an exception can be thrown so that + // the rollback guard knows where to start. + --out; + new (out) T(std::move(*(--in))); + } + for (; in != first;) { + --out; + *out = std::move(*(--in)); + } + for (; out > lastConstructed;) { + --out; + new (out) T(create()); + } + for (; out != first;) { + --out; + *out = create(); + } + rollback.dismiss(); + } +} + +// Specialization for trivially copyable types. The call to +// std::move_backward here will just turn into a memmove. +// This must only be used with trivially copyable types because some of the +// memory may be uninitialized, and std::move_backward() won't work when it +// can't memmove(). +template <class T, class Create> +typename std::enable_if<folly::is_trivially_copyable<T>::value>::type +moveObjectsRightAndCreate( + T* const first, + T* const lastConstructed, + T* const realLast, + Create&& create) { + std::move_backward(first, lastConstructed, realLast); + T* const end = first - 1; + T* out = first + (realLast - lastConstructed) - 1; + for (; out != end; --out) { + *out = create(); + } +} + +/* + * Populate a region of memory using `op' to construct elements. If + * anything throws, undo what we did. + */ +template <class T, class Function> +void populateMemForward(T* mem, std::size_t n, Function const& op) { + std::size_t idx = 0; + { + auto rollback = makeGuard([&] { + for (std::size_t i = 0; i < idx; ++i) { + mem[i].~T(); + } + }); + for (size_t i = 0; i < n; ++i) { + op(&mem[idx]); + ++idx; + } + rollback.dismiss(); + } +} + +template <class SizeType, bool ShouldUseHeap> +struct IntegralSizePolicyBase { + typedef SizeType InternalSizeType; + + IntegralSizePolicyBase() : size_(0) {} + + protected: + static constexpr std::size_t policyMaxSize() { + return SizeType(~kExternMask); + } + + std::size_t doSize() const { + return size_ & ~kExternMask; + } + + std::size_t isExtern() const { + return kExternMask & size_; + } + + void setExtern(bool b) { + if (b) { + size_ |= kExternMask; + } else { + size_ &= ~kExternMask; + } + } + + void setSize(std::size_t sz) { + assert(sz <= policyMaxSize()); + size_ = (kExternMask & size_) | SizeType(sz); + } + + void swapSizePolicy(IntegralSizePolicyBase& o) { + std::swap(size_, o.size_); + } + + protected: + static bool constexpr kShouldUseHeap = ShouldUseHeap; + + private: + static SizeType constexpr kExternMask = + kShouldUseHeap ? SizeType(1) << (sizeof(SizeType) * 8 - 1) : 0; + + SizeType size_; +}; + +template <class SizeType, bool ShouldUseHeap> +struct IntegralSizePolicy; + +template <class SizeType> +struct IntegralSizePolicy<SizeType, true> + : public IntegralSizePolicyBase<SizeType, true> { + public: + /* + * Move a range to a range of uninitialized memory. Assumes the + * ranges don't overlap. + */ + template <class T> + typename std::enable_if<!folly::is_trivially_copyable<T>::value>::type + moveToUninitialized(T* first, T* last, T* out) { + std::size_t idx = 0; + { + auto rollback = makeGuard([&] { + // Even for callers trying to give the strong guarantee + // (e.g. push_back) it's ok to assume here that we don't have to + // move things back and that it was a copy constructor that + // threw: if someone throws from a move constructor the effects + // are unspecified. + for (std::size_t i = 0; i < idx; ++i) { + out[i].~T(); + } + }); + for (; first != last; ++first, ++idx) { + new (&out[idx]) T(std::move(*first)); + } + rollback.dismiss(); + } + } + + // Specialization for trivially copyable types. + template <class T> + typename std::enable_if<folly::is_trivially_copyable<T>::value>::type + moveToUninitialized(T* first, T* last, T* out) { + std::memmove( + static_cast<void*>(out), + static_cast<void const*>(first), + (last - first) * sizeof *first); + } + + /* + * Move a range to a range of uninitialized memory. Assumes the + * ranges don't overlap. Inserts an element at out + pos using + * emplaceFunc(). out will contain (end - begin) + 1 elements on success and + * none on failure. If emplaceFunc() throws [begin, end) is unmodified. + */ + template <class T, class EmplaceFunc> + void moveToUninitializedEmplace( + T* begin, + T* end, + T* out, + SizeType pos, + EmplaceFunc&& emplaceFunc) { + // Must be called first so that if it throws [begin, end) is unmodified. + // We have to support the strong exception guarantee for emplace_back(). + emplaceFunc(out + pos); + // move old elements to the left of the new one + { + auto rollback = makeGuard([&] { // + out[pos].~T(); + }); + this->moveToUninitialized(begin, begin + pos, out); + rollback.dismiss(); + } + // move old elements to the right of the new one + { + auto rollback = makeGuard([&] { + for (SizeType i = 0; i <= pos; ++i) { + out[i].~T(); + } + }); + if (begin + pos < end) { + this->moveToUninitialized(begin + pos, end, out + pos + 1); + } + rollback.dismiss(); + } + } +}; + +template <class SizeType> +struct IntegralSizePolicy<SizeType, false> + : public IntegralSizePolicyBase<SizeType, false> { + public: + template <class T> + void moveToUninitialized(T* /*first*/, T* /*last*/, T* /*out*/) { + assume_unreachable(); + } + template <class T, class EmplaceFunc> + void moveToUninitializedEmplace( + T* /* begin */, + T* /* end */, + T* /* out */, + SizeType /* pos */, + EmplaceFunc&& /* emplaceFunc */) { + assume_unreachable(); + } +}; + +/* + * If you're just trying to use this class, ignore everything about + * this next small_vector_base class thing. + * + * The purpose of this junk is to minimize sizeof(small_vector<>) + * and allow specifying the template parameters in whatever order is + * convenient for the user. There's a few extra steps here to try + * to keep the error messages at least semi-reasonable. + * + * Apologies for all the black magic. + */ +namespace mpl = boost::mpl; +template < + class Value, + std::size_t RequestedMaxInline, + class InPolicyA, + class InPolicyB, + class InPolicyC> +struct small_vector_base { + typedef mpl::vector<InPolicyA, InPolicyB, InPolicyC> PolicyList; + + /* + * Determine the size type + */ + typedef typename mpl::filter_view< + PolicyList, + std::is_integral<mpl::placeholders::_1>>::type Integrals; + typedef typename mpl::eval_if< + mpl::empty<Integrals>, + mpl::identity<std::size_t>, + mpl::front<Integrals>>::type SizeType; + + static_assert( + std::is_unsigned<SizeType>::value, + "Size type should be an unsigned integral type"); + static_assert( + mpl::size<Integrals>::value == 0 || mpl::size<Integrals>::value == 1, + "Multiple size types specified in small_vector<>"); + + /* + * Determine whether we should allow spilling to the heap or not. + */ + typedef typename mpl::count<PolicyList, small_vector_policy::NoHeap>::type + HasNoHeap; + + static_assert( + HasNoHeap::value == 0 || HasNoHeap::value == 1, + "Multiple copies of small_vector_policy::NoHeap " + "supplied; this is probably a mistake"); + + /* + * Make the real policy base classes. + */ + typedef IntegralSizePolicy<SizeType, !HasNoHeap::value> ActualSizePolicy; + + /* + * Now inherit from them all. This is done in such a convoluted + * way to make sure we get the empty base optimizaton on all these + * types to keep sizeof(small_vector<>) minimal. + */ + typedef boost::totally_ordered1< + small_vector<Value, RequestedMaxInline, InPolicyA, InPolicyB, InPolicyC>, + ActualSizePolicy> + type; +}; + +template <class T> +T* pointerFlagSet(T* p) { + return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(p) | 1); +} +template <class T> +bool pointerFlagGet(T* p) { + return reinterpret_cast<uintptr_t>(p) & 1; +} +template <class T> +T* pointerFlagClear(T* p) { + return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(p) & ~uintptr_t(1)); +} +inline void* shiftPointer(void* p, size_t sizeBytes) { + return static_cast<char*>(p) + sizeBytes; +} +} // namespace detail + +////////////////////////////////////////////////////////////////////// +FOLLY_SV_PACK_PUSH +template < + class Value, + std::size_t RequestedMaxInline = 1, + class PolicyA = void, + class PolicyB = void, + class PolicyC = void> +class small_vector : public detail::small_vector_base< + Value, + RequestedMaxInline, + PolicyA, + PolicyB, + PolicyC>::type { + typedef typename detail:: + small_vector_base<Value, RequestedMaxInline, PolicyA, PolicyB, PolicyC>:: + type BaseType; + typedef typename BaseType::InternalSizeType InternalSizeType; + + /* + * Figure out the max number of elements we should inline. (If + * the user asks for less inlined elements than we can fit unioned + * into our value_type*, we will inline more than they asked.) + */ + static constexpr std::size_t MaxInline{ + constexpr_max(sizeof(Value*) / sizeof(Value), RequestedMaxInline)}; + + public: + typedef std::size_t size_type; + typedef Value value_type; + typedef std::allocator<Value> allocator_type; + typedef value_type& reference; + typedef value_type const& const_reference; + typedef value_type* iterator; + typedef value_type* pointer; + typedef value_type const* const_iterator; + typedef value_type const* const_pointer; + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + small_vector() = default; + // Allocator is unused here. It is taken in for compatibility with std::vector + // interface, but it will be ignored. + small_vector(const std::allocator<Value>&) {} + + small_vector(small_vector const& o) { + auto n = o.size(); + makeSize(n); + { + auto rollback = makeGuard([&] { + if (this->isExtern()) { + u.freeHeap(); + } + }); + std::uninitialized_copy(o.begin(), o.end(), begin()); + rollback.dismiss(); + } + this->setSize(n); + } + + small_vector(small_vector&& o) noexcept( + std::is_nothrow_move_constructible<Value>::value) { + if (o.isExtern()) { + swap(o); + } else { + std::uninitialized_copy( + std::make_move_iterator(o.begin()), + std::make_move_iterator(o.end()), + begin()); + this->setSize(o.size()); + } + } + + small_vector(std::initializer_list<value_type> il) { + constructImpl(il.begin(), il.end(), std::false_type()); + } + + explicit small_vector(size_type n) { + doConstruct(n, [&](void* p) { new (p) value_type(); }); + } + + small_vector(size_type n, value_type const& t) { + doConstruct(n, [&](void* p) { new (p) value_type(t); }); + } + + template <class Arg> + explicit small_vector(Arg arg1, Arg arg2) { + // Forward using std::is_arithmetic to get to the proper + // implementation; this disambiguates between the iterators and + // (size_t, value_type) meaning for this constructor. + constructImpl(arg1, arg2, std::is_arithmetic<Arg>()); + } + + ~small_vector() { + for (auto& t : *this) { + (&t)->~value_type(); + } + if (this->isExtern()) { + u.freeHeap(); + } + } + + small_vector& operator=(small_vector const& o) { + if (FOLLY_LIKELY(this != &o)) { + assign(o.begin(), o.end()); + } + return *this; + } + + small_vector& operator=(small_vector&& o) { + // TODO: optimization: + // if both are internal, use move assignment where possible + if (FOLLY_LIKELY(this != &o)) { + clear(); + swap(o); + } + return *this; + } + + bool operator==(small_vector const& o) const { + return size() == o.size() && std::equal(begin(), end(), o.begin()); + } + + bool operator<(small_vector const& o) const { + return std::lexicographical_compare(begin(), end(), o.begin(), o.end()); + } + + static constexpr size_type max_size() { + return !BaseType::kShouldUseHeap ? static_cast<size_type>(MaxInline) + : BaseType::policyMaxSize(); + } + + allocator_type get_allocator() const { + return {}; + } + + size_type size() const { + return this->doSize(); + } + bool empty() const { + return !size(); + } + + iterator begin() { + return data(); + } + iterator end() { + return data() + size(); + } + const_iterator begin() const { + return data(); + } + const_iterator end() const { + return data() + size(); + } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + reverse_iterator rend() { + return reverse_iterator(begin()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crbegin() const { + return rbegin(); + } + const_reverse_iterator crend() const { + return rend(); + } + + /* + * Usually one of the simplest functions in a Container-like class + * but a bit more complex here. We have to handle all combinations + * of in-place vs. heap between this and o. + * + * Basic guarantee only. Provides the nothrow guarantee iff our + * value_type has a nothrow move or copy constructor. + */ + void swap(small_vector& o) { + using std::swap; // Allow ADL on swap for our value_type. + + if (this->isExtern() && o.isExtern()) { + this->swapSizePolicy(o); + + auto thisCapacity = this->capacity(); + auto oCapacity = o.capacity(); + + auto* tmp = u.pdata_.heap_; + u.pdata_.heap_ = o.u.pdata_.heap_; + o.u.pdata_.heap_ = tmp; + + this->setCapacity(oCapacity); + o.setCapacity(thisCapacity); + + return; + } + + if (!this->isExtern() && !o.isExtern()) { + auto& oldSmall = size() < o.size() ? *this : o; + auto& oldLarge = size() < o.size() ? o : *this; + + for (size_type i = 0; i < oldSmall.size(); ++i) { + swap(oldSmall[i], oldLarge[i]); + } + + size_type i = oldSmall.size(); + const size_type ci = i; + { + auto rollback = makeGuard([&] { + oldSmall.setSize(i); + for (; i < oldLarge.size(); ++i) { + oldLarge[i].~value_type(); + } + oldLarge.setSize(ci); + }); + for (; i < oldLarge.size(); ++i) { + auto addr = oldSmall.begin() + i; + new (addr) value_type(std::move(oldLarge[i])); + oldLarge[i].~value_type(); + } + rollback.dismiss(); + } + oldSmall.setSize(i); + oldLarge.setSize(ci); + return; + } + + // isExtern != o.isExtern() + auto& oldExtern = o.isExtern() ? o : *this; + auto& oldIntern = o.isExtern() ? *this : o; + + auto oldExternCapacity = oldExtern.capacity(); + auto oldExternHeap = oldExtern.u.pdata_.heap_; + + auto buff = oldExtern.u.buffer(); + size_type i = 0; + { + auto rollback = makeGuard([&] { + for (size_type kill = 0; kill < i; ++kill) { + buff[kill].~value_type(); + } + for (; i < oldIntern.size(); ++i) { + oldIntern[i].~value_type(); + } + oldIntern.setSize(0); + oldExtern.u.pdata_.heap_ = oldExternHeap; + oldExtern.setCapacity(oldExternCapacity); + }); + for (; i < oldIntern.size(); ++i) { + new (&buff[i]) value_type(std::move(oldIntern[i])); + oldIntern[i].~value_type(); + } + rollback.dismiss(); + } + oldIntern.u.pdata_.heap_ = oldExternHeap; + this->swapSizePolicy(o); + oldIntern.setCapacity(oldExternCapacity); + } + + void resize(size_type sz) { + if (sz < size()) { + erase(begin() + sz, end()); + return; + } + makeSize(sz); + detail::populateMemForward( + begin() + size(), sz - size(), [&](void* p) { new (p) value_type(); }); + this->setSize(sz); + } + + void resize(size_type sz, value_type const& v) { + if (sz < size()) { + erase(begin() + sz, end()); + return; + } + makeSize(sz); + detail::populateMemForward( + begin() + size(), sz - size(), [&](void* p) { new (p) value_type(v); }); + this->setSize(sz); + } + + value_type* data() noexcept { + return this->isExtern() ? u.heap() : u.buffer(); + } + + value_type const* data() const noexcept { + return this->isExtern() ? u.heap() : u.buffer(); + } + + template <class... Args> + iterator emplace(const_iterator p, Args&&... args) { + if (p == cend()) { + emplace_back(std::forward<Args>(args)...); + return end() - 1; + } + + /* + * We implement emplace at places other than at the back with a + * temporary for exception safety reasons. It is possible to + * avoid having to do this, but it becomes hard to maintain the + * basic exception safety guarantee (unless you respond to a copy + * constructor throwing by clearing the whole vector). + * + * The reason for this is that otherwise you have to destruct an + * element before constructing this one in its place---if the + * constructor throws, you either need a nothrow default + * constructor or a nothrow copy/move to get something back in the + * "gap", and the vector requirements don't guarantee we have any + * of these. Clearing the whole vector is a legal response in + * this situation, but it seems like this implementation is easy + * enough and probably better. + */ + return insert(p, value_type(std::forward<Args>(args)...)); + } + + void reserve(size_type sz) { + makeSize(sz); + } + + size_type capacity() const { + if (this->isExtern()) { + if (u.hasCapacity()) { + return u.getCapacity(); + } + return malloc_usable_size(u.pdata_.heap_) / sizeof(value_type); + } + return MaxInline; + } + + void shrink_to_fit() { + if (!this->isExtern()) { + return; + } + + small_vector tmp(begin(), end()); + tmp.swap(*this); + } + + template <class... Args> + reference emplace_back(Args&&... args) { + if (capacity() == size()) { + // Any of args may be references into the vector. + // When we are reallocating, we have to be careful to construct the new + // element before modifying the data in the old buffer. + makeSize( + size() + 1, + [&](void* p) { new (p) value_type(std::forward<Args>(args)...); }, + size()); + } else { + new (end()) value_type(std::forward<Args>(args)...); + } + this->setSize(size() + 1); + return back(); + } + + void push_back(value_type&& t) { + emplace_back(std::move(t)); + } + + void push_back(value_type const& t) { + emplace_back(t); + } + + void pop_back() { + erase(end() - 1); + } + + iterator insert(const_iterator constp, value_type&& t) { + iterator p = unconst(constp); + + if (p == end()) { + push_back(std::move(t)); + return end() - 1; + } + + auto offset = p - begin(); + + if (capacity() == size()) { + makeSize( + size() + 1, + [&t](void* ptr) { new (ptr) value_type(std::move(t)); }, + offset); + this->setSize(this->size() + 1); + } else { + detail::moveObjectsRightAndCreate( + data() + offset, + data() + size(), + data() + size() + 1, + [&]() mutable -> value_type&& { return std::move(t); }); + this->setSize(size() + 1); + } + return begin() + offset; + } + + iterator insert(const_iterator p, value_type const& t) { + // Make a copy and forward to the rvalue value_type&& overload + // above. + return insert(p, value_type(t)); + } + + iterator insert(const_iterator pos, size_type n, value_type const& val) { + auto offset = pos - begin(); + makeSize(size() + n); + detail::moveObjectsRightAndCreate( + data() + offset, + data() + size(), + data() + size() + n, + [&]() mutable -> value_type const& { return val; }); + this->setSize(size() + n); + return begin() + offset; + } + + template <class Arg> + iterator insert(const_iterator p, Arg arg1, Arg arg2) { + // Forward using std::is_arithmetic to get to the proper + // implementation; this disambiguates between the iterators and + // (size_t, value_type) meaning for this function. + return insertImpl(unconst(p), arg1, arg2, std::is_arithmetic<Arg>()); + } + + iterator insert(const_iterator p, std::initializer_list<value_type> il) { + return insert(p, il.begin(), il.end()); + } + + iterator erase(const_iterator q) { + std::move(unconst(q) + 1, end(), unconst(q)); + (data() + size() - 1)->~value_type(); + this->setSize(size() - 1); + return unconst(q); + } + + iterator erase(const_iterator q1, const_iterator q2) { + if (q1 == q2) { + return unconst(q1); + } + std::move(unconst(q2), end(), unconst(q1)); + for (auto it = (end() - std::distance(q1, q2)); it != end(); ++it) { + it->~value_type(); + } + this->setSize(size() - (q2 - q1)); + return unconst(q1); + } + + void clear() { + // Equivalent to erase(begin(), end()), but neither Clang or GCC are able to + // optimize away the abstraction. + for (auto it = begin(); it != end(); ++it) { + it->~value_type(); + } + this->setSize(0); + } + + template <class Arg> + void assign(Arg first, Arg last) { + clear(); + insert(end(), first, last); + } + + void assign(std::initializer_list<value_type> il) { + assign(il.begin(), il.end()); + } + + void assign(size_type n, const value_type& t) { + clear(); + insert(end(), n, t); + } + + reference front() { + assert(!empty()); + return *begin(); + } + reference back() { + assert(!empty()); + return *(end() - 1); + } + const_reference front() const { + assert(!empty()); + return *begin(); + } + const_reference back() const { + assert(!empty()); + return *(end() - 1); + } + + reference operator[](size_type i) { + assert(i < size()); + return *(begin() + i); + } + + const_reference operator[](size_type i) const { + assert(i < size()); + return *(begin() + i); + } + + reference at(size_type i) { + if (i >= size()) { + throw_exception<std::out_of_range>("index out of range"); + } + return (*this)[i]; + } + + const_reference at(size_type i) const { + if (i >= size()) { + throw_exception<std::out_of_range>("index out of range"); + } + return (*this)[i]; + } + + private: + static iterator unconst(const_iterator it) { + return const_cast<iterator>(it); + } + + // The std::false_type argument is part of disambiguating the + // iterator insert functions from integral types (see insert().) + template <class It> + iterator insertImpl(iterator pos, It first, It last, std::false_type) { + using categ = typename std::iterator_traits<It>::iterator_category; + using it_ref = typename std::iterator_traits<It>::reference; + if (std::is_same<categ, std::input_iterator_tag>::value) { + auto offset = pos - begin(); + while (first != last) { + pos = insert(pos, *first++); + ++pos; + } + return begin() + offset; + } + + auto const distance = std::distance(first, last); + auto const offset = pos - begin(); + assert(distance >= 0); + assert(offset >= 0); + makeSize(size() + distance); + detail::moveObjectsRightAndCreate( + data() + offset, + data() + size(), + data() + size() + distance, + [&, in = last]() mutable -> it_ref { return *--in; }); + this->setSize(size() + distance); + return begin() + offset; + } + + iterator + insertImpl(iterator pos, size_type n, const value_type& val, std::true_type) { + // The true_type means this should call the size_t,value_type + // overload. (See insert().) + return insert(pos, n, val); + } + + // The std::false_type argument came from std::is_arithmetic as part + // of disambiguating an overload (see the comment in the + // constructor). + template <class It> + void constructImpl(It first, It last, std::false_type) { + typedef typename std::iterator_traits<It>::iterator_category categ; + if (std::is_same<categ, std::input_iterator_tag>::value) { + // With iterators that only allow a single pass, we can't really + // do anything sane here. + while (first != last) { + emplace_back(*first++); + } + return; + } + + auto distance = std::distance(first, last); + makeSize(distance); + this->setSize(distance); + { + auto rollback = makeGuard([&] { + if (this->isExtern()) { + u.freeHeap(); + } + }); + detail::populateMemForward( + data(), distance, [&](void* p) { new (p) value_type(*first++); }); + rollback.dismiss(); + } + } + + template <typename InitFunc> + void doConstruct(size_type n, InitFunc&& func) { + makeSize(n); + this->setSize(n); + { + auto rollback = makeGuard([&] { + if (this->isExtern()) { + u.freeHeap(); + } + }); + detail::populateMemForward(data(), n, std::forward<InitFunc>(func)); + rollback.dismiss(); + } + } + + // The true_type means we should forward to the size_t,value_type + // overload. + void constructImpl(size_type n, value_type const& val, std::true_type) { + doConstruct(n, [&](void* p) { new (p) value_type(val); }); + } + + /* + * Compute the size after growth. + */ + size_type computeNewSize() const { + return std::min((3 * capacity()) / 2 + 1, max_size()); + } + + void makeSize(size_type newSize) { + makeSizeInternal(newSize, false, [](void*) { assume_unreachable(); }, 0); + } + + template <typename EmplaceFunc> + void makeSize(size_type newSize, EmplaceFunc&& emplaceFunc, size_type pos) { + assert(size() == capacity()); + makeSizeInternal( + newSize, true, std::forward<EmplaceFunc>(emplaceFunc), pos); + } + + /* + * Ensure we have a large enough memory region to be size `newSize'. + * Will move/copy elements if we are spilling to heap_ or needed to + * allocate a new region, but if resized in place doesn't initialize + * anything in the new region. In any case doesn't change size(). + * Supports insertion of new element during reallocation by given + * pointer to new element and position of new element. + * NOTE: If reallocation is not needed, insert must be false, + * because we only know how to emplace elements into new memory. + */ + template <typename EmplaceFunc> + void makeSizeInternal( + size_type newSize, + bool insert, + EmplaceFunc&& emplaceFunc, + size_type pos) { + if (newSize > max_size()) { + throw_exception<std::length_error>("max_size exceeded in small_vector"); + } + if (newSize <= capacity()) { + assert(!insert); + return; + } + + assert(this->kShouldUseHeap); + // This branch isn't needed for correctness, but allows the optimizer to + // skip generating code for the rest of this function in NoHeap + // small_vectors. + if (!this->kShouldUseHeap) { + return; + } + + newSize = std::max(newSize, computeNewSize()); + + const auto needBytes = newSize * sizeof(value_type); + // If the capacity isn't explicitly stored inline, but the heap + // allocation is grown to over some threshold, we should store + // a capacity at the front of the heap allocation. + const bool heapifyCapacity = + !kHasInlineCapacity && needBytes > kHeapifyCapacityThreshold; + const size_t allocationExtraBytes = + heapifyCapacity ? kHeapifyCapacitySize : 0; + const size_t goodAllocationSizeBytes = + goodMallocSize(needBytes + allocationExtraBytes); + const size_t newCapacity = + (goodAllocationSizeBytes - allocationExtraBytes) / sizeof(value_type); + // Make sure that the allocation request has a size computable from the + // capacity, instead of using goodAllocationSizeBytes, so that we can do + // sized deallocation. If goodMallocSize() gives us extra bytes that are not + // a multiple of the value size we cannot use them anyway. + const size_t sizeBytes = + newCapacity * sizeof(value_type) + allocationExtraBytes; + void* newh = checkedMalloc(sizeBytes); + // We expect newh to be at least 2-aligned, because we want to + // use its least significant bit as a flag. + assert(!detail::pointerFlagGet(newh)); + + value_type* newp = static_cast<value_type*>( + heapifyCapacity ? detail::shiftPointer(newh, kHeapifyCapacitySize) + : newh); + + { + auto rollback = makeGuard([&] { // + sizedFree(newh, sizeBytes); + }); + if (insert) { + // move and insert the new element + this->moveToUninitializedEmplace( + begin(), end(), newp, pos, std::forward<EmplaceFunc>(emplaceFunc)); + } else { + // move without inserting new element + this->moveToUninitialized(begin(), end(), newp); + } + rollback.dismiss(); + } + for (auto& val : *this) { + val.~value_type(); + } + + if (this->isExtern()) { + u.freeHeap(); + } + if (heapifyCapacity) { + u.pdata_.heap_ = detail::pointerFlagSet(newh); + } else { + u.pdata_.heap_ = newh; + } + this->setExtern(true); + this->setCapacity(newCapacity); + } + + /* + * This will set the capacity field, stored inline in the storage_ field + * if there is sufficient room to store it. + */ + void setCapacity(size_type newCapacity) { + assert(this->isExtern()); + if (u.hasCapacity()) { + assert(newCapacity < std::numeric_limits<InternalSizeType>::max()); + u.setCapacity(newCapacity); + } + } + + private: + struct HeapPtrWithCapacity { + void* heap_; + InternalSizeType capacity_; + + InternalSizeType getCapacity() const { + return capacity_; + } + void setCapacity(InternalSizeType c) { + capacity_ = c; + } + size_t allocationExtraBytes() const { + return 0; + } + } FOLLY_SV_PACK_ATTR; + + struct HeapPtr { + // Lower order bit of heap_ is used as flag to indicate whether capacity is + // stored at the front of the heap allocation. + void* heap_; + + InternalSizeType getCapacity() const { + assert(detail::pointerFlagGet(heap_)); + return *static_cast<InternalSizeType*>(detail::pointerFlagClear(heap_)); + } + void setCapacity(InternalSizeType c) { + *static_cast<InternalSizeType*>(detail::pointerFlagClear(heap_)) = c; + } + size_t allocationExtraBytes() const { + assert(detail::pointerFlagGet(heap_)); + return kHeapifyCapacitySize; + } + } FOLLY_SV_PACK_ATTR; + + typedef aligned_storage_for_t<value_type[MaxInline]> InlineStorageDataType; + + typedef typename std::conditional< + sizeof(value_type) * MaxInline != 0, + InlineStorageDataType, + void*>::type InlineStorageType; + + static bool constexpr kHasInlineCapacity = + sizeof(HeapPtrWithCapacity) < sizeof(InlineStorageType); + + // This value should we multiple of word size. + static size_t constexpr kHeapifyCapacitySize = sizeof( + typename std:: + aligned_storage<sizeof(InternalSizeType), alignof(value_type)>::type); + + // Threshold to control capacity heapifying. + static size_t constexpr kHeapifyCapacityThreshold = + 100 * kHeapifyCapacitySize; + + typedef typename std:: + conditional<kHasInlineCapacity, HeapPtrWithCapacity, HeapPtr>::type + PointerType; + + union Data { + explicit Data() { + pdata_.heap_ = nullptr; + } + + PointerType pdata_; + InlineStorageType storage_; + + value_type* buffer() noexcept { + void* vp = &storage_; + return static_cast<value_type*>(vp); + } + value_type const* buffer() const noexcept { + return const_cast<Data*>(this)->buffer(); + } + value_type* heap() noexcept { + if (kHasInlineCapacity || !detail::pointerFlagGet(pdata_.heap_)) { + return static_cast<value_type*>(pdata_.heap_); + } else { + return static_cast<value_type*>(detail::shiftPointer( + detail::pointerFlagClear(pdata_.heap_), kHeapifyCapacitySize)); + } + } + value_type const* heap() const noexcept { + return const_cast<Data*>(this)->heap(); + } + + bool hasCapacity() const { + return kHasInlineCapacity || detail::pointerFlagGet(pdata_.heap_); + } + InternalSizeType getCapacity() const { + return pdata_.getCapacity(); + } + void setCapacity(InternalSizeType c) { + pdata_.setCapacity(c); + } + + void freeHeap() { + auto vp = detail::pointerFlagClear(pdata_.heap_); + if (hasCapacity()) { + sizedFree( + vp, + pdata_.getCapacity() * sizeof(value_type) + + pdata_.allocationExtraBytes()); + } else { + free(vp); + } + } + } u; +}; +FOLLY_SV_PACK_POP + +////////////////////////////////////////////////////////////////////// + +// Basic guarantee only, or provides the nothrow guarantee iff T has a +// nothrow move or copy constructor. +template <class T, std::size_t MaxInline, class A, class B, class C> +void swap( + small_vector<T, MaxInline, A, B, C>& a, + small_vector<T, MaxInline, A, B, C>& b) { + a.swap(b); +} + +template <class T, std::size_t MaxInline, class A, class B, class C, class U> +void erase(small_vector<T, MaxInline, A, B, C>& v, U value) { + v.erase(std::remove(v.begin(), v.end(), value), v.end()); +} + +template < + class T, + std::size_t MaxInline, + class A, + class B, + class C, + class Predicate> +void erase_if(small_vector<T, MaxInline, A, B, C>& v, Predicate predicate) { + v.erase(std::remove_if(v.begin(), v.end(), predicate), v.end()); +} + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +// Format support. +template <class T, size_t M, class A, class B, class C> +struct IndexableTraits<small_vector<T, M, A, B, C>> + : public IndexableTraitsSeq<small_vector<T, M, A, B, C>> {}; + +} // namespace detail + +} // namespace folly + +FOLLY_POP_WARNING + +#undef FOLLY_SV_PACK_ATTR +#undef FOLLY_SV_PACK_PUSH +#undef FOLLY_SV_PACK_POP diff --git a/ios/Pods/Flipper-Folly/folly/sorted_vector_types.h b/ios/Pods/Flipper-Folly/folly/sorted_vector_types.h new file mode 100644 index 000000000..17bbddd8d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/sorted_vector_types.h @@ -0,0 +1,1326 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 header defines two classes that very nearly model + * AssociativeContainer (but not quite). These implement set-like and + * map-like behavior on top of a sorted vector, instead of using + * rb-trees like std::set and std::map. + * + * This is potentially useful in cases where the number of elements in + * the set or map is small, or when you want to avoid using more + * memory than necessary and insertions/deletions are much more rare + * than lookups (these classes have O(N) insertions/deletions). + * + * In the interest of using these in conditions where the goal is to + * minimize memory usage, they support a GrowthPolicy parameter, which + * is a class defining a single function called increase_capacity, + * which will be called whenever we are about to insert something: you + * can then decide to call reserve() based on the current capacity() + * and size() of the passed in vector-esque Container type. An + * example growth policy that grows one element at a time: + * + * struct OneAtATimePolicy { + * template <class Container> + * void increase_capacity(Container& c) { + * if (c.size() == c.capacity()) { + * c.reserve(c.size() + 1); + * } + * } + * }; + * + * typedef sorted_vector_set<int, + * std::less<int>, + * std::allocator<int>, + * OneAtATimePolicy> + * OneAtATimeIntSet; + * + * Important differences from std::set and std::map: + * - insert() and erase() invalidate iterators and references. + erase(iterator) returns an iterator pointing to the next valid element. + * - insert() and erase() are O(N) + * - our iterators model RandomAccessIterator + * - sorted_vector_map::value_type is pair<K,V>, not pair<const K,V>. + * (This is basically because we want to store the value_type in + * std::vector<>, which requires it to be Assignable.) + * - insert() single key variants, emplace(), and emplace_hint() only provide + * the strong exception guarantee (unchanged when exception is thrown) when + * std::is_nothrow_move_constructible<value_type>::value is true. + */ + +#pragma once + +#include <algorithm> +#include <cassert> +#include <initializer_list> +#include <iterator> +#include <memory> +#include <stdexcept> +#include <type_traits> +#include <utility> +#include <vector> + +#include <folly/ScopeGuard.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/lang/Exception.h> +#include <folly/memory/MemoryResource.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +template <typename, typename Compare, typename Key, typename T> +struct sorted_vector_enable_if_is_transparent {}; + +template <typename Compare, typename Key, typename T> +struct sorted_vector_enable_if_is_transparent< + void_t<typename Compare::is_transparent>, + Compare, + Key, + T> { + using type = T; +}; + +// This wrapper goes around a GrowthPolicy and provides iterator +// preservation semantics, but only if the growth policy is not the +// default (i.e. nothing). +template <class Policy> +struct growth_policy_wrapper : private Policy { + template <class Container, class Iterator> + Iterator increase_capacity(Container& c, Iterator desired_insertion) { + typedef typename Container::difference_type diff_t; + diff_t d = desired_insertion - c.begin(); + Policy::increase_capacity(c); + return c.begin() + d; + } +}; +template <> +struct growth_policy_wrapper<void> { + template <class Container, class Iterator> + Iterator increase_capacity(Container&, Iterator it) { + return it; + } +}; + +/* + * This helper returns the distance between two iterators if it is + * possible to figure it out without messing up the range + * (i.e. unless they are InputIterators). Otherwise this returns + * -1. + */ +template <class Iterator> +int distance_if_multipass(Iterator first, Iterator last) { + typedef typename std::iterator_traits<Iterator>::iterator_category categ; + if (std::is_same<categ, std::input_iterator_tag>::value) { + return -1; + } + return std::distance(first, last); +} + +template <class OurContainer, class Vector, class GrowthPolicy, class Value> +typename OurContainer::iterator insert_with_hint( + OurContainer& sorted, + Vector& cont, + typename OurContainer::const_iterator hint, + Value&& value, + GrowthPolicy& po) { + const typename OurContainer::value_compare& cmp(sorted.value_comp()); + if (hint == cont.end() || cmp(value, *hint)) { + if (hint == cont.begin() || cmp(*(hint - 1), value)) { + hint = po.increase_capacity(cont, hint); + return cont.insert(hint, std::forward<Value>(value)); + } else { + return sorted.insert(std::forward<Value>(value)).first; + } + } + + if (cmp(*hint, value)) { + if (hint + 1 == cont.end() || cmp(value, *(hint + 1))) { + hint = po.increase_capacity(cont, hint + 1); + return cont.insert(hint, std::forward<Value>(value)); + } else { + return sorted.insert(std::forward<Value>(value)).first; + } + } + + // Value and *hint did not compare, so they are equal keys. + return sorted.begin() + std::distance(sorted.cbegin(), hint); +} + +template <class OurContainer, class Vector, class InputIterator> +void bulk_insert( + OurContainer& sorted, + Vector& cont, + InputIterator first, + InputIterator last) { + // prevent deref of middle where middle == cont.end() + if (first == last) { + return; + } + + auto const& cmp(sorted.value_comp()); + + int const d = distance_if_multipass(first, last); + if (d != -1) { + cont.reserve(cont.size() + d); + } + auto const prev_size = cont.size(); + + std::copy(first, last, std::back_inserter(cont)); + auto const middle = cont.begin() + prev_size; + if (!std::is_sorted(middle, cont.end(), cmp)) { + std::sort(middle, cont.end(), cmp); + } + if (middle != cont.begin() && !cmp(*(middle - 1), *middle)) { + std::inplace_merge(cont.begin(), middle, cont.end(), cmp); + } + cont.erase( + std::unique( + cont.begin(), + cont.end(), + [&](typename OurContainer::value_type const& a, + typename OurContainer::value_type const& b) { + return !cmp(a, b) && !cmp(b, a); + }), + cont.end()); +} + +template <typename Container, typename Compare> +bool is_sorted_unique(Container const& container, Compare const& comp) { + if (container.empty()) { + return true; + } + auto const e = container.end(); + for (auto a = container.begin(), b = std::next(a); b != e; ++a, ++b) { + if (!comp(*a, *b)) { + return false; + } + } + return true; +} + +template <typename Container, typename Compare> +Container&& as_sorted_unique(Container&& container, Compare const& comp) { + std::sort(container.begin(), container.end(), comp); + container.erase( + std::unique( + container.begin(), + container.end(), + [&](auto const& a, auto const& b) { + return !comp(a, b) && !comp(b, a); + }), + container.end()); + return static_cast<Container&&>(container); +} +} // namespace detail + +////////////////////////////////////////////////////////////////////// + +/** + * A sorted_vector_set is a container similar to std::set<>, but + * implemented as a sorted array with std::vector<>. + * + * @param class T Data type to store + * @param class Compare Comparison function that imposes a + * strict weak ordering over instances of T + * @param class Allocator allocation policy + * @param class GrowthPolicy policy object to control growth + * + * @author Aditya Agarwal <aditya@fb.com> + * @author Akhil Wable <akhil@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ +template < + class T, + class Compare = std::less<T>, + class Allocator = std::allocator<T>, + class GrowthPolicy = void, + class Container = std::vector<T, Allocator>> +class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { + detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() { + return *this; + } + + template <typename K, typename V, typename C = Compare> + using if_is_transparent = + _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; + + struct EBO; + + public: + typedef T value_type; + typedef T key_type; + typedef Compare key_compare; + typedef Compare value_compare; + typedef Allocator allocator_type; + typedef Container container_type; + + typedef typename Container::pointer pointer; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + /* + * XXX: Our normal iterator ought to also be a constant iterator + * (cf. Defect Report 103 for std::set), but this is a bit more of a + * pain. + */ + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + typedef typename Container::difference_type difference_type; + typedef typename Container::size_type size_type; + typedef typename Container::reverse_iterator reverse_iterator; + typedef typename Container::const_reverse_iterator const_reverse_iterator; + + sorted_vector_set() : m_(Compare(), Allocator()) {} + + sorted_vector_set(const sorted_vector_set&) = default; + + sorted_vector_set(const sorted_vector_set& other, const Allocator& alloc) + : m_(other.m_, alloc) {} + + sorted_vector_set(sorted_vector_set&&) = default; + + sorted_vector_set(sorted_vector_set&& other, const Allocator& alloc) noexcept( + std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value) + : m_(std::move(other.m_), alloc) {} + + explicit sorted_vector_set(const Allocator& alloc) : m_(Compare(), alloc) {} + + explicit sorted_vector_set( + const Compare& comp, + const Allocator& alloc = Allocator()) + : m_(comp, alloc) {} + + template <class InputIterator> + sorted_vector_set( + InputIterator first, + InputIterator last, + const Compare& comp = Compare(), + const Allocator& alloc = Allocator()) + : m_(comp, alloc) { + // This is linear if [first, last) is already sorted (and if we + // can figure out the distance between the two iterators). + insert(first, last); + } + + template <class InputIterator> + sorted_vector_set( + InputIterator first, + InputIterator last, + const Allocator& alloc) + : m_(Compare(), alloc) { + // This is linear if [first, last) is already sorted (and if we + // can figure out the distance between the two iterators). + insert(first, last); + } + + /* implicit */ sorted_vector_set( + std::initializer_list<value_type> list, + const Compare& comp = Compare(), + const Allocator& alloc = Allocator()) + : m_(comp, alloc) { + insert(list.begin(), list.end()); + } + + sorted_vector_set( + std::initializer_list<value_type> list, + const Allocator& alloc) + : m_(Compare(), alloc) { + insert(list.begin(), list.end()); + } + + // Construct a sorted_vector_set by stealing the storage of a prefilled + // container. The container need not be sorted already. This supports + // bulk construction of sorted_vector_set with zero allocations, not counting + // those performed by the caller. (The iterator range constructor performs at + // least one allocation). + // + // Note that `sorted_vector_set(const Container& container)` is not provided, + // since the purpose of this constructor is to avoid an unnecessary copy. + explicit sorted_vector_set( + Container&& container, + const Compare& comp = Compare()) + : sorted_vector_set( + sorted_unique, + detail::as_sorted_unique(std::move(container), comp), + comp) {} + + // Construct a sorted_vector_set by stealing the storage of a prefilled + // container. Its elements must be sorted and unique, as sorted_unique_t + // hints. Supports bulk construction of sorted_vector_set with zero + // allocations, not counting those performed by the caller. (The iterator + // range constructor performs at least one allocation). + // + // Note that `sorted_vector_set(sorted_unique_t, const Container& container)` + // is not provided, since the purpose of this constructor is to avoid an extra + // copy. + sorted_vector_set( + sorted_unique_t, + Container&& container, + const Compare& comp = Compare()) noexcept(std:: + is_nothrow_constructible< + EBO, + const Compare&, + Container&&>::value) + : m_(comp, std::move(container)) { + assert(detail::is_sorted_unique(m_.cont_, value_comp())); + } + + Allocator get_allocator() const { + return m_.cont_.get_allocator(); + } + + sorted_vector_set& operator=(const sorted_vector_set& other) = default; + + sorted_vector_set& operator=(sorted_vector_set&& other) = default; + + sorted_vector_set& operator=(std::initializer_list<value_type> ilist) { + clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + key_compare key_comp() const { + return m_; + } + value_compare value_comp() const { + return m_; + } + + iterator begin() { + return m_.cont_.begin(); + } + iterator end() { + return m_.cont_.end(); + } + const_iterator cbegin() const { + return m_.cont_.cbegin(); + } + const_iterator begin() const { + return m_.cont_.begin(); + } + const_iterator cend() const { + return m_.cont_.cend(); + } + const_iterator end() const { + return m_.cont_.end(); + } + reverse_iterator rbegin() { + return m_.cont_.rbegin(); + } + reverse_iterator rend() { + return m_.cont_.rend(); + } + const_reverse_iterator rbegin() const { + return m_.cont_.rbegin(); + } + const_reverse_iterator rend() const { + return m_.cont_.rend(); + } + + void clear() { + return m_.cont_.clear(); + } + size_type size() const { + return m_.cont_.size(); + } + size_type max_size() const { + return m_.cont_.max_size(); + } + bool empty() const { + return m_.cont_.empty(); + } + void reserve(size_type s) { + return m_.cont_.reserve(s); + } + void shrink_to_fit() { + m_.cont_.shrink_to_fit(); + } + size_type capacity() const { + return m_.cont_.capacity(); + } + + std::pair<iterator, bool> insert(const value_type& value) { + iterator it = lower_bound(value); + if (it == end() || value_comp()(value, *it)) { + it = get_growth_policy().increase_capacity(m_.cont_, it); + return std::make_pair(m_.cont_.insert(it, value), true); + } + return std::make_pair(it, false); + } + + std::pair<iterator, bool> insert(value_type&& value) { + iterator it = lower_bound(value); + if (it == end() || value_comp()(value, *it)) { + it = get_growth_policy().increase_capacity(m_.cont_, it); + return std::make_pair(m_.cont_.insert(it, std::move(value)), true); + } + return std::make_pair(it, false); + } + + iterator insert(const_iterator hint, const value_type& value) { + return detail::insert_with_hint( + *this, m_.cont_, hint, value, get_growth_policy()); + } + + iterator insert(const_iterator hint, value_type&& value) { + return detail::insert_with_hint( + *this, m_.cont_, hint, std::move(value), get_growth_policy()); + } + + template <class InputIterator> + void insert(InputIterator first, InputIterator last) { + detail::bulk_insert(*this, m_.cont_, first, last); + } + + void insert(std::initializer_list<value_type> ilist) { + insert(ilist.begin(), ilist.end()); + } + + // emplace isn't better than insert for sorted_vector_set, but aids + // compatibility + template <typename... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; + value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); + auto a = get_allocator(); + std::allocator_traits<allocator_type>::construct( + a, p, std::forward<Args>(args)...); + auto g = makeGuard( + [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); + return insert(std::move(*p)); + } + + std::pair<iterator, bool> emplace(const value_type& value) { + return insert(value); + } + + std::pair<iterator, bool> emplace(value_type&& value) { + return insert(std::move(value)); + } + + // emplace_hint isn't better than insert for sorted_vector_set, but aids + // compatibility + template <typename... Args> + iterator emplace_hint(const_iterator hint, Args&&... args) { + std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; + value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); + auto a = get_allocator(); + std::allocator_traits<allocator_type>::construct( + a, p, std::forward<Args>(args)...); + auto g = makeGuard( + [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); + return insert(hint, std::move(*p)); + } + + iterator emplace_hint(const_iterator hint, const value_type& value) { + return insert(hint, value); + } + + iterator emplace_hint(const_iterator hint, value_type&& value) { + return insert(hint, std::move(value)); + } + + size_type erase(const key_type& key) { + iterator it = find(key); + if (it == end()) { + return 0; + } + m_.cont_.erase(it); + return 1; + } + + iterator erase(const_iterator it) { + return m_.cont_.erase(it); + } + + iterator erase(const_iterator first, const_iterator last) { + return m_.cont_.erase(first, last); + } + + iterator find(const key_type& key) { + return find(*this, key); + } + + const_iterator find(const key_type& key) const { + return find(*this, key); + } + + template <typename K> + if_is_transparent<K, iterator> find(const K& key) { + return find(*this, key); + } + + template <typename K> + if_is_transparent<K, const_iterator> find(const K& key) const { + return find(*this, key); + } + + size_type count(const key_type& key) const { + return find(key) == end() ? 0 : 1; + } + + template <typename K> + if_is_transparent<K, size_type> count(const K& key) const { + return find(key) == end() ? 0 : 1; + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, iterator> lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, const_iterator> lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, iterator> upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, const_iterator> upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + std::pair<iterator, iterator> equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, key_comp()); + } + + std::pair<const_iterator, const_iterator> equal_range( + const key_type& key) const { + return std::equal_range(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, std::pair<iterator, iterator>> equal_range( + const K& key) { + return std::equal_range(begin(), end(), key, key_comp()); + } + + template <typename K> + if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range( + const K& key) const { + return std::equal_range(begin(), end(), key, key_comp()); + } + + // Nothrow as long as swap() on the Compare type is nothrow. + void swap(sorted_vector_set& o) { + using std::swap; // Allow ADL for swap(); fall back to std::swap(). + Compare& a = m_; + Compare& b = o.m_; + swap(a, b); + m_.cont_.swap(o.m_.cont_); + } + + bool operator==(const sorted_vector_set& other) const { + return other.m_.cont_ == m_.cont_; + } + bool operator!=(const sorted_vector_set& other) const { + return !operator==(other); + } + + bool operator<(const sorted_vector_set& other) const { + return m_.cont_ < other.m_.cont_; + } + bool operator>(const sorted_vector_set& other) const { + return other < *this; + } + bool operator<=(const sorted_vector_set& other) const { + return !operator>(other); + } + bool operator>=(const sorted_vector_set& other) const { + return !operator<(other); + } + + const value_type* data() const noexcept { + return m_.cont_.data(); + } + + private: + /* + * This structure derives from the comparison object in order to + * make use of the empty base class optimization if our comparison + * functor is an empty class (usual case). + * + * Wrapping up this member like this is better than deriving from + * the Compare object ourselves (there are some perverse edge cases + * involving virtual functions). + * + * More info: http://www.cantrip.org/emptyopt.html + */ + struct EBO : Compare { + explicit EBO(const Compare& c, const Allocator& alloc) noexcept( + std::is_nothrow_default_constructible<Container>::value) + : Compare(c), cont_(alloc) {} + EBO(const EBO& other, const Allocator& alloc) + noexcept(std::is_nothrow_constructible< + Container, + const Container&, + const Allocator&>::value) + : Compare(static_cast<const Compare&>(other)), + cont_(other.cont_, alloc) {} + EBO(EBO&& other, const Allocator& alloc) + noexcept(std::is_nothrow_constructible< + Container, + Container&&, + const Allocator&>::value) + : Compare(static_cast<Compare&&>(other)), + cont_(std::move(other.cont_), alloc) {} + EBO(const Compare& c, Container&& cont) + noexcept(std::is_nothrow_move_constructible<Container>::value) + : Compare(c), cont_(std::move(cont)) {} + Container cont_; + } m_; + + template <typename Self> + using self_iterator_t = _t< + std::conditional<std::is_const<Self>::value, const_iterator, iterator>>; + + template <typename Self, typename K> + static self_iterator_t<Self> find(Self& self, K const& key) { + auto end = self.end(); + auto it = self.lower_bound(key); + if (it == end || !self.key_comp()(key, *it)) { + return it; + } + return end; + } +}; + +// Swap function that can be found using ADL. +template <class T, class C, class A, class G> +inline void swap( + sorted_vector_set<T, C, A, G>& a, + sorted_vector_set<T, C, A, G>& b) { + return a.swap(b); +} + +#if FOLLY_HAS_MEMORY_RESOURCE + +namespace pmr { + +template < + class T, + class Compare = std::less<T>, + class GrowthPolicy = void, + class Container = + std::vector<T, folly::detail::std_pmr::polymorphic_allocator<T>>> +using sorted_vector_set = folly::sorted_vector_set< + T, + Compare, + folly::detail::std_pmr::polymorphic_allocator<T>, + GrowthPolicy, + Container>; + +} // namespace pmr + +#endif + +////////////////////////////////////////////////////////////////////// + +/** + * A sorted_vector_map is similar to a sorted_vector_set but stores + * <key,value> pairs instead of single elements. + * + * @param class Key Key type + * @param class Value Value type + * @param class Compare Function that can compare key types and impose + * a strict weak ordering over them. + * @param class Allocator allocation policy + * @param class GrowthPolicy policy object to control growth + * + * @author Aditya Agarwal <aditya@fb.com> + * @author Akhil Wable <akhil@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ +template < + class Key, + class Value, + class Compare = std::less<Key>, + class Allocator = std::allocator<std::pair<Key, Value>>, + class GrowthPolicy = void, + class Container = std::vector<std::pair<Key, Value>, Allocator>> +class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { + detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() { + return *this; + } + + template <typename K, typename V, typename C = Compare> + using if_is_transparent = + _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; + + struct EBO; + + public: + typedef Key key_type; + typedef Value mapped_type; + typedef typename Container::value_type value_type; + typedef Compare key_compare; + typedef Allocator allocator_type; + typedef Container container_type; + + struct value_compare : private Compare { + bool operator()(const value_type& a, const value_type& b) const { + return Compare::operator()(a.first, b.first); + } + + protected: + friend class sorted_vector_map; + explicit value_compare(const Compare& c) : Compare(c) {} + }; + + typedef typename Container::pointer pointer; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + typedef typename Container::difference_type difference_type; + typedef typename Container::size_type size_type; + typedef typename Container::reverse_iterator reverse_iterator; + typedef typename Container::const_reverse_iterator const_reverse_iterator; + + sorted_vector_map() noexcept( + std::is_nothrow_constructible<EBO, value_compare, Allocator>::value) + : m_(value_compare(Compare()), Allocator()) {} + + sorted_vector_map(const sorted_vector_map&) = default; + + sorted_vector_map(const sorted_vector_map& other, const Allocator& alloc) + : m_(other.m_, alloc) {} + + sorted_vector_map(sorted_vector_map&&) = default; + + sorted_vector_map(sorted_vector_map&& other, const Allocator& alloc) noexcept( + std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value) + : m_(std::move(other.m_), alloc) {} + + explicit sorted_vector_map(const Allocator& alloc) + : m_(value_compare(Compare()), alloc) {} + + explicit sorted_vector_map( + const Compare& comp, + const Allocator& alloc = Allocator()) + : m_(value_compare(comp), alloc) {} + + template <class InputIterator> + explicit sorted_vector_map( + InputIterator first, + InputIterator last, + const Compare& comp = Compare(), + const Allocator& alloc = Allocator()) + : m_(value_compare(comp), alloc) { + insert(first, last); + } + + template <class InputIterator> + sorted_vector_map( + InputIterator first, + InputIterator last, + const Allocator& alloc) + : m_(value_compare(Compare()), alloc) { + insert(first, last); + } + + /* implicit */ sorted_vector_map( + std::initializer_list<value_type> list, + const Compare& comp = Compare(), + const Allocator& alloc = Allocator()) + : m_(value_compare(comp), alloc) { + insert(list.begin(), list.end()); + } + + sorted_vector_map( + std::initializer_list<value_type> list, + const Allocator& alloc) + : m_(value_compare(Compare()), alloc) { + insert(list.begin(), list.end()); + } + + // Construct a sorted_vector_map by stealing the storage of a prefilled + // container. The container need not be sorted already. This supports + // bulk construction of sorted_vector_map with zero allocations, not counting + // those performed by the caller. (The iterator range constructor performs at + // least one allocation). + // + // Note that `sorted_vector_map(const Container& container)` is not provided, + // since the purpose of this constructor is to avoid an unnecessary copy. + explicit sorted_vector_map( + Container&& container, + const Compare& comp = Compare()) + : sorted_vector_map( + sorted_unique, + detail::as_sorted_unique(std::move(container), value_compare(comp)), + comp) {} + + // Construct a sorted_vector_map by stealing the storage of a prefilled + // container. Its elements must be sorted and unique, as sorted_unique_t + // hints. Supports bulk construction of sorted_vector_map with zero + // allocations, not counting those performed by the caller. (The iterator + // range constructor performs at least one allocation). + // + // Note that `sorted_vector_map(sorted_unique_t, const Container& container)` + // is not provided, since the purpose of this constructor is to avoid an extra + // copy. + sorted_vector_map( + sorted_unique_t, + Container&& container, + const Compare& comp = Compare()) noexcept(std:: + is_nothrow_constructible< + EBO, + value_compare, + Container&&>::value) + : m_(value_compare(comp), std::move(container)) { + assert(std::is_sorted(m_.cont_.begin(), m_.cont_.end(), value_comp())); + assert(detail::is_sorted_unique(m_.cont_, value_comp())); + } + + Allocator get_allocator() const { + return m_.cont_.get_allocator(); + } + + sorted_vector_map& operator=(const sorted_vector_map& other) = default; + + sorted_vector_map& operator=(sorted_vector_map&& other) = default; + + sorted_vector_map& operator=(std::initializer_list<value_type> ilist) { + clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + key_compare key_comp() const { + return m_; + } + value_compare value_comp() const { + return m_; + } + + iterator begin() { + return m_.cont_.begin(); + } + iterator end() { + return m_.cont_.end(); + } + const_iterator cbegin() const { + return m_.cont_.cbegin(); + } + const_iterator begin() const { + return m_.cont_.begin(); + } + const_iterator cend() const { + return m_.cont_.cend(); + } + const_iterator end() const { + return m_.cont_.end(); + } + reverse_iterator rbegin() { + return m_.cont_.rbegin(); + } + reverse_iterator rend() { + return m_.cont_.rend(); + } + const_reverse_iterator rbegin() const { + return m_.cont_.rbegin(); + } + const_reverse_iterator rend() const { + return m_.cont_.rend(); + } + + void clear() { + return m_.cont_.clear(); + } + size_type size() const { + return m_.cont_.size(); + } + size_type max_size() const { + return m_.cont_.max_size(); + } + bool empty() const { + return m_.cont_.empty(); + } + void reserve(size_type s) { + return m_.cont_.reserve(s); + } + void shrink_to_fit() { + m_.cont_.shrink_to_fit(); + } + size_type capacity() const { + return m_.cont_.capacity(); + } + + std::pair<iterator, bool> insert(const value_type& value) { + iterator it = lower_bound(value.first); + if (it == end() || value_comp()(value, *it)) { + it = get_growth_policy().increase_capacity(m_.cont_, it); + return std::make_pair(m_.cont_.insert(it, value), true); + } + return std::make_pair(it, false); + } + + std::pair<iterator, bool> insert(value_type&& value) { + iterator it = lower_bound(value.first); + if (it == end() || value_comp()(value, *it)) { + it = get_growth_policy().increase_capacity(m_.cont_, it); + return std::make_pair(m_.cont_.insert(it, std::move(value)), true); + } + return std::make_pair(it, false); + } + + iterator insert(const_iterator hint, const value_type& value) { + return detail::insert_with_hint( + *this, m_.cont_, hint, value, get_growth_policy()); + } + + iterator insert(const_iterator hint, value_type&& value) { + return detail::insert_with_hint( + *this, m_.cont_, hint, std::move(value), get_growth_policy()); + } + + template <class InputIterator> + void insert(InputIterator first, InputIterator last) { + detail::bulk_insert(*this, m_.cont_, first, last); + } + + void insert(std::initializer_list<value_type> ilist) { + insert(ilist.begin(), ilist.end()); + } + + // emplace isn't better than insert for sorted_vector_map, but aids + // compatibility + template <typename... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; + value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); + auto a = get_allocator(); + std::allocator_traits<allocator_type>::construct( + a, p, std::forward<Args>(args)...); + auto g = makeGuard( + [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); + return insert(std::move(*p)); + } + + std::pair<iterator, bool> emplace(const value_type& value) { + return insert(value); + } + + std::pair<iterator, bool> emplace(value_type&& value) { + return insert(std::move(value)); + } + + // emplace_hint isn't better than insert for sorted_vector_set, but aids + // compatibility + template <typename... Args> + iterator emplace_hint(const_iterator hint, Args&&... args) { + std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; + value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); + auto a = get_allocator(); + std::allocator_traits<allocator_type>::construct( + a, p, std::forward<Args>(args)...); + auto g = makeGuard( + [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); + return insert(hint, std::move(*p)); + } + + iterator emplace_hint(const_iterator hint, const value_type& value) { + return insert(hint, value); + } + + iterator emplace_hint(const_iterator hint, value_type&& value) { + return insert(hint, std::move(value)); + } + + size_type erase(const key_type& key) { + iterator it = find(key); + if (it == end()) { + return 0; + } + m_.cont_.erase(it); + return 1; + } + + iterator erase(const_iterator it) { + return m_.cont_.erase(it); + } + + iterator erase(const_iterator first, const_iterator last) { + return m_.cont_.erase(first, last); + } + + iterator find(const key_type& key) { + return find(*this, key); + } + + const_iterator find(const key_type& key) const { + return find(*this, key); + } + + template <typename K> + if_is_transparent<K, iterator> find(const K& key) { + return find(*this, key); + } + + template <typename K> + if_is_transparent<K, const_iterator> find(const K& key) const { + return find(*this, key); + } + + mapped_type& at(const key_type& key) { + iterator it = find(key); + if (it != end()) { + return it->second; + } + throw_exception<std::out_of_range>("sorted_vector_map::at"); + } + + const mapped_type& at(const key_type& key) const { + const_iterator it = find(key); + if (it != end()) { + return it->second; + } + throw_exception<std::out_of_range>("sorted_vector_map::at"); + } + + size_type count(const key_type& key) const { + return find(key) == end() ? 0 : 1; + } + + template <typename K> + if_is_transparent<K, size_type> count(const K& key) const { + return find(key) == end() ? 0 : 1; + } + + iterator lower_bound(const key_type& key) { + return lower_bound(*this, key); + } + + const_iterator lower_bound(const key_type& key) const { + return lower_bound(*this, key); + } + + template <typename K> + if_is_transparent<K, iterator> lower_bound(const K& key) { + return lower_bound(*this, key); + } + + template <typename K> + if_is_transparent<K, const_iterator> lower_bound(const K& key) const { + return lower_bound(*this, key); + } + + iterator upper_bound(const key_type& key) { + return upper_bound(*this, key); + } + + const_iterator upper_bound(const key_type& key) const { + return upper_bound(*this, key); + } + + template <typename K> + if_is_transparent<K, iterator> upper_bound(const K& key) { + return upper_bound(*this, key); + } + + template <typename K> + if_is_transparent<K, const_iterator> upper_bound(const K& key) const { + return upper_bound(*this, key); + } + + std::pair<iterator, iterator> equal_range(const key_type& key) { + return equal_range(*this, key); + } + + std::pair<const_iterator, const_iterator> equal_range( + const key_type& key) const { + return equal_range(*this, key); + } + + template <typename K> + if_is_transparent<K, std::pair<iterator, iterator>> equal_range( + const K& key) { + return equal_range(*this, key); + } + + template <typename K> + if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range( + const K& key) const { + return equal_range(*this, key); + } + + // Nothrow as long as swap() on the Compare type is nothrow. + void swap(sorted_vector_map& o) { + using std::swap; // Allow ADL for swap(); fall back to std::swap(). + Compare& a = m_; + Compare& b = o.m_; + swap(a, b); + m_.cont_.swap(o.m_.cont_); + } + + mapped_type& operator[](const key_type& key) { + iterator it = lower_bound(key); + if (it == end() || key_comp()(key, it->first)) { + return insert(it, value_type(key, mapped_type()))->second; + } + return it->second; + } + + bool operator==(const sorted_vector_map& other) const { + return m_.cont_ == other.m_.cont_; + } + bool operator!=(const sorted_vector_map& other) const { + return !operator==(other); + } + + bool operator<(const sorted_vector_map& other) const { + return m_.cont_ < other.m_.cont_; + } + bool operator>(const sorted_vector_map& other) const { + return other < *this; + } + bool operator<=(const sorted_vector_map& other) const { + return !operator>(other); + } + bool operator>=(const sorted_vector_map& other) const { + return !operator<(other); + } + + const value_type* data() const noexcept { + return m_.cont_.data(); + } + + private: + // This is to get the empty base optimization; see the comment in + // sorted_vector_set. + struct EBO : value_compare { + explicit EBO(const value_compare& c, const Allocator& alloc) noexcept( + std::is_nothrow_default_constructible<Container>::value) + : value_compare(c), cont_(alloc) {} + EBO(const EBO& other, const Allocator& alloc) + noexcept(std::is_nothrow_constructible< + Container, + const Container&, + const Allocator&>::value) + : value_compare(static_cast<const value_compare&>(other)), + cont_(other.cont_, alloc) {} + EBO(EBO&& other, const Allocator& alloc) + noexcept(std::is_nothrow_constructible< + Container, + Container&&, + const Allocator&>::value) + : value_compare(static_cast<value_compare&&>(other)), + cont_(std::move(other.cont_), alloc) {} + EBO(const Compare& c, Container&& cont) + noexcept(std::is_nothrow_move_constructible<Container>::value) + : value_compare(c), cont_(std::move(cont)) {} + Container cont_; + } m_; + + template <typename Self> + using self_iterator_t = _t< + std::conditional<std::is_const<Self>::value, const_iterator, iterator>>; + + template <typename Self, typename K> + static self_iterator_t<Self> find(Self& self, K const& key) { + auto end = self.end(); + auto it = self.lower_bound(key); + if (it == end || !self.key_comp()(key, it->first)) { + return it; + } + return end; + } + + template <typename Self, typename K> + static self_iterator_t<Self> lower_bound(Self& self, K const& key) { + auto f = [c = self.key_comp()](value_type const& a, K const& b) { + return c(a.first, b); + }; + return std::lower_bound(self.begin(), self.end(), key, f); + } + + template <typename Self, typename K> + static self_iterator_t<Self> upper_bound(Self& self, K const& key) { + auto f = [c = self.key_comp()](K const& a, value_type const& b) { + return c(a, b.first); + }; + return std::upper_bound(self.begin(), self.end(), key, f); + } + + template <typename Self, typename K> + static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> equal_range( + Self& self, + K const& key) { + // Note: std::equal_range can't be passed a functor that takes + // argument types different from the iterator value_type, so we + // have to do this. + return {lower_bound(self, key), upper_bound(self, key)}; + } +}; + +// Swap function that can be found using ADL. +template <class K, class V, class C, class A, class G> +inline void swap( + sorted_vector_map<K, V, C, A, G>& a, + sorted_vector_map<K, V, C, A, G>& b) { + return a.swap(b); +} + +#if FOLLY_HAS_MEMORY_RESOURCE + +namespace pmr { + +template < + class Key, + class Value, + class Compare = std::less<Key>, + class GrowthPolicy = void, + class Container = std::vector< + std::pair<Key, Value>, + folly::detail::std_pmr::polymorphic_allocator<std::pair<Key, Value>>>> +using sorted_vector_map = folly::sorted_vector_map< + Key, + Value, + Compare, + folly::detail::std_pmr::polymorphic_allocator<std::pair<Key, Value>>, + GrowthPolicy, + Container>; + +} // namespace pmr + +#endif + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/Init.cpp b/ios/Pods/Flipper-Folly/folly/ssl/Init.cpp new file mode 100644 index 000000000..7baeae589 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/Init.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ssl/Init.h> + +#include <mutex> + +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/detail/OpenSSLThreading.h> +#include <glog/logging.h> + +namespace folly { +namespace ssl { + +namespace { +bool initialized_ = false; + +std::mutex& initMutex() { + static std::mutex m; + return m; +} + +void initializeOpenSSLLocked() { + if (initialized_) { + return; + } + if (OPENSSL_init_ssl(0, nullptr) != 1) { + // Fail early if we fail to initialize OpenSSL. Ignoring this means that + // future OpenSSL methods may segfault, since there is an implicit + // precondition that initialization properly initializes internal OpenSSL + // pointers to global resources. + throw std::runtime_error("Failed to initialize OpenSSL."); + } + + // OpenSSL implicitly seeds its RNG since 1.1.0, so this call is unnecessary + // and is only here for compatibility with older versions of OpenSSL. +#if !(FOLLY_OPENSSL_IS_110) + if (RAND_poll() != 1) { + // Similarly, if we fail to seed the RNG, future crypto operations + // may no longer be safe to use; fail fast and hard here. + throw std::runtime_error("Failed to initialize OpenSSL RNG."); + } +#endif + initialized_ = true; +} + +void cleanupOpenSSLLocked() { + if (!initialized_) { + return; + } + + OPENSSL_cleanup(); + initialized_ = false; +} +} // namespace + +void init() { + std::lock_guard<std::mutex> g(initMutex()); + initializeOpenSSLLocked(); +} + +void cleanup() { + std::lock_guard<std::mutex> g(initMutex()); + cleanupOpenSSLLocked(); +} + +void markInitialized() { + std::lock_guard<std::mutex> g(initMutex()); + initialized_ = true; +} + +void setLockTypesAndInit(LockTypeMapping inLockTypes) { + std::lock_guard<std::mutex> g(initMutex()); + CHECK(!initialized_) << "OpenSSL is already initialized"; + detail::setLockTypes(std::move(inLockTypes)); + initializeOpenSSLLocked(); +} + +void setLockTypes(LockTypeMapping inLockTypes) { + std::lock_guard<std::mutex> g(initMutex()); + if (initialized_) { + // We set the locks on initialization, so if we are already initialized + // this would have no affect. + LOG(INFO) << "Ignoring setSSLLockTypes after initialization"; + return; + } + detail::setLockTypes(std::move(inLockTypes)); +} + +bool isLockDisabled(int lockId) { + return detail::isSSLLockDisabled(lockId); +} + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/Init.h b/ios/Pods/Flipper-Folly/folly/ssl/Init.h new file mode 100644 index 000000000..4bda4e8a3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/Init.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <map> + +#include <folly/ssl/OpenSSLLockTypes.h> + +namespace folly { +namespace ssl { + +/** + * Initializes openssl. This should be invoked once, during the start of an + * application. Subsequent calls to this function are no-ops. + * + * For OpenSSL < 1.1.0, any lock types should be set with setLockTypes prior to + * the call to folly::ssl::init() + */ +void init(); + +/** + * Cleans up openssl. This should be invoked at most once during the lifetime + * of the application. OpenSSL >= 1.1.0 users do not need to manually invoke + * this method, as OpenSSL will automatically cleanup itself during the exit + * of the application. + */ +void cleanup(); + +/** + * Mark openssl as initialized without actually performing any initialization. + * Please use this only if you are using a library which requires that it must + * make its own calls to SSL_library_init() and related functions. + */ +void markInitialized(); + +/** + * Set preferences for how to treat locks in OpenSSL. This must be + * called before folly::ssl::init(), otherwise the defaults will be used. + * + * OpenSSL has a lock for each module rather than for each object or + * data that needs locking. Some locks protect only refcounts, and + * might be better as spinlocks rather than mutexes. Other locks + * may be totally unnecessary if the objects being protected are not + * shared between threads in the application. + * + * For a list of OpenSSL lock types, refer to crypto/crypto.h. + * + * By default, all locks are initialized as mutexes. OpenSSL's lock usage + * may change from version to version and you should know what you are doing + * before disabling any locks entirely. + * + * In newer versions of OpenSSL (>= 1.1.0), OpenSSL manages its own locks, + * and this function is a no-op. + * + * Example: if you don't share SSL sessions between threads in your + * application, you may be able to do this + * + * setSSLLockTypes({{ + * CRYPTO_LOCK_SSL_SESSION, + * SSLContext::SSLLockType::LOCK_NONE + * }}) + */ +void setLockTypes(LockTypeMapping inLockTypes); + +/** + * Set the lock types and initialize OpenSSL in an atomic fashion. This + * aborts if the library has already been initialized. + */ +void setLockTypesAndInit(LockTypeMapping lockTypes); + +bool isLockDisabled(int lockId); +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.cpp b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.cpp new file mode 100644 index 000000000..ea36a53b6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ssl/OpenSSLCertUtils.h> + +#include <folly/FileUtil.h> +#include <folly/ScopeGuard.h> +#include <folly/String.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { +namespace ssl { + +namespace { +std::string getOpenSSLErrorString(unsigned long err) { + std::array<char, 256> errBuff; + ERR_error_string_n(err, errBuff.data(), errBuff.size()); + return std::string(errBuff.data()); +} +} // namespace + +Optional<std::string> OpenSSLCertUtils::getCommonName(X509& x509) { + auto subject = X509_get_subject_name(&x509); + if (!subject) { + return none; + } + + auto cnLoc = X509_NAME_get_index_by_NID(subject, NID_commonName, -1); + if (cnLoc < 0) { + return none; + } + + auto cnEntry = X509_NAME_get_entry(subject, cnLoc); + if (!cnEntry) { + return none; + } + + auto cnAsn = X509_NAME_ENTRY_get_data(cnEntry); + if (!cnAsn) { + return none; + } + + auto cnData = reinterpret_cast<const char*>(ASN1_STRING_get0_data(cnAsn)); + auto cnLen = ASN1_STRING_length(cnAsn); + if (!cnData || cnLen <= 0) { + return none; + } + + return Optional<std::string>(std::string(cnData, cnLen)); +} + +std::vector<std::string> OpenSSLCertUtils::getSubjectAltNames(X509& x509) { + auto names = reinterpret_cast<STACK_OF(GENERAL_NAME)*>( + X509_get_ext_d2i(&x509, NID_subject_alt_name, nullptr, nullptr)); + if (!names) { + return {}; + } + SCOPE_EXIT { + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + }; + + std::vector<std::string> ret; + auto count = sk_GENERAL_NAME_num(names); + for (int i = 0; i < count; i++) { + auto genName = sk_GENERAL_NAME_value(names, i); + if (!genName || genName->type != GEN_DNS) { + continue; + } + auto nameData = reinterpret_cast<const char*>( + ASN1_STRING_get0_data(genName->d.dNSName)); + auto nameLen = ASN1_STRING_length(genName->d.dNSName); + if (!nameData || nameLen <= 0) { + continue; + } + ret.emplace_back(nameData, nameLen); + } + return ret; +} + +Optional<std::string> OpenSSLCertUtils::getSubject(X509& x509) { + auto subject = X509_get_subject_name(&x509); + if (!subject) { + return none; + } + + auto bio = BioUniquePtr(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + throw std::runtime_error("Cannot allocate bio"); + } + if (X509_NAME_print_ex(bio.get(), subject, 0, XN_FLAG_ONELINE) <= 0) { + return none; + } + + char* bioData = nullptr; + size_t bioLen = BIO_get_mem_data(bio.get(), &bioData); + return std::string(bioData, bioLen); +} + +Optional<std::string> OpenSSLCertUtils::getIssuer(X509& x509) { + auto issuer = X509_get_issuer_name(&x509); + if (!issuer) { + return none; + } + + auto bio = BioUniquePtr(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + throw std::runtime_error("Cannot allocate bio"); + } + + if (X509_NAME_print_ex(bio.get(), issuer, 0, XN_FLAG_ONELINE) <= 0) { + return none; + } + + char* bioData = nullptr; + size_t bioLen = BIO_get_mem_data(bio.get(), &bioData); + return std::string(bioData, bioLen); +} + +folly::Optional<std::string> OpenSSLCertUtils::toString(X509& x509) { + auto in = BioUniquePtr(BIO_new(BIO_s_mem())); + if (in == nullptr) { + throw std::runtime_error("Cannot allocate bio"); + } + + int flags = 0; + + flags |= X509_FLAG_NO_HEADER | /* A few bytes of cert and data */ + X509_FLAG_NO_PUBKEY | /* Public key */ + X509_FLAG_NO_AUX | /* Auxiliary info? */ + X509_FLAG_NO_SIGDUMP | /* Prints the signature */ + X509_FLAG_NO_SIGNAME; /* Signature algorithms */ + +#ifdef X509_FLAG_NO_IDS + flags |= X509_FLAG_NO_IDS; /* Issuer/subject IDs */ +#endif + + if (X509_print_ex(in.get(), &x509, XN_FLAG_ONELINE, flags) > 0) { + char* bioData = nullptr; + size_t bioLen = BIO_get_mem_data(in.get(), &bioData); + return std::string(bioData, bioLen); + } else { + return none; + } +} + +std::string OpenSSLCertUtils::getNotAfterTime(X509& x509) { + return getDateTimeStr(X509_get0_notAfter(&x509)); +} + +std::string OpenSSLCertUtils::getNotBeforeTime(X509& x509) { + return getDateTimeStr(X509_get0_notBefore(&x509)); +} + +std::chrono::system_clock::time_point OpenSSLCertUtils::asnTimeToTimepoint( + const ASN1_TIME* asnTime) { + int dSecs = 0; + int dDays = 0; + + auto epoch_time_t = std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point()); + folly::ssl::ASN1TimeUniquePtr epoch_asn(ASN1_TIME_set(nullptr, epoch_time_t)); + + if (!epoch_asn) { + throw std::runtime_error("failed to allocate epoch asn.1 time"); + } + + if (ASN1_TIME_diff(&dDays, &dSecs, epoch_asn.get(), asnTime) != 1) { + throw std::runtime_error("invalid asn.1 time"); + } + + return std::chrono::system_clock::time_point( + std::chrono::seconds(dSecs) + std::chrono::hours(24 * dDays)); +} + +std::string OpenSSLCertUtils::getDateTimeStr(const ASN1_TIME* time) { + if (!time) { + return ""; + } + + auto bio = BioUniquePtr(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + throw std::runtime_error("Cannot allocate bio"); + } + + if (ASN1_TIME_print(bio.get(), time) <= 0) { + throw std::runtime_error("Cannot print ASN1_TIME"); + } + + char* bioData = nullptr; + size_t bioLen = BIO_get_mem_data(bio.get(), &bioData); + return std::string(bioData, bioLen); +} + +X509UniquePtr OpenSSLCertUtils::derDecode(ByteRange range) { + auto begin = range.data(); + X509UniquePtr cert(d2i_X509(nullptr, &begin, range.size())); + if (!cert) { + throw std::runtime_error("could not read cert"); + } + return cert; +} + +std::unique_ptr<IOBuf> OpenSSLCertUtils::derEncode(X509& x509) { + auto len = i2d_X509(&x509, nullptr); + if (len < 0) { + throw std::runtime_error("Error computing length"); + } + auto buf = IOBuf::create(len); + auto dataPtr = buf->writableData(); + len = i2d_X509(&x509, &dataPtr); + if (len < 0) { + throw std::runtime_error("Error converting cert to DER"); + } + buf->append(len); + return buf; +} + +std::vector<X509UniquePtr> OpenSSLCertUtils::readCertsFromBuffer( + ByteRange range) { + BioUniquePtr b( + BIO_new_mem_buf(const_cast<unsigned char*>(range.data()), range.size())); + if (!b) { + throw std::runtime_error("failed to create BIO"); + } + std::vector<X509UniquePtr> certs; + ERR_clear_error(); + while (true) { + X509UniquePtr x509(PEM_read_bio_X509(b.get(), nullptr, nullptr, nullptr)); + if (x509) { + certs.push_back(std::move(x509)); + continue; + } + auto err = ERR_get_error(); + ERR_clear_error(); + if (BIO_eof(b.get()) && ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + // Reach end of buffer. + break; + } + throw std::runtime_error(folly::to<std::string>( + "Unable to parse cert ", + certs.size(), + ": ", + getOpenSSLErrorString(err))); + } + return certs; +} + +std::array<uint8_t, SHA_DIGEST_LENGTH> OpenSSLCertUtils::getDigestSha1( + X509& x509) { + unsigned int len; + std::array<uint8_t, SHA_DIGEST_LENGTH> md; + int rc = X509_digest(&x509, EVP_sha1(), md.data(), &len); + + if (rc <= 0) { + throw std::runtime_error("Could not calculate SHA1 digest for cert"); + } + return md; +} + +std::array<uint8_t, SHA256_DIGEST_LENGTH> OpenSSLCertUtils::getDigestSha256( + X509& x509) { + unsigned int len; + std::array<uint8_t, SHA256_DIGEST_LENGTH> md; + int rc = X509_digest(&x509, EVP_sha256(), md.data(), &len); + + if (rc <= 0) { + throw std::runtime_error("Could not calculate SHA256 digest for cert"); + } + return md; +} + +X509StoreUniquePtr OpenSSLCertUtils::readStoreFromFile(std::string caFile) { + std::string certData; + if (!folly::readFile(caFile.c_str(), certData)) { + throw std::runtime_error( + folly::to<std::string>("Could not read store file: ", caFile)); + } + return readStoreFromBuffer(folly::StringPiece(certData)); +} + +X509StoreUniquePtr OpenSSLCertUtils::readStoreFromBuffer(ByteRange certRange) { + auto certs = readCertsFromBuffer(certRange); + ERR_clear_error(); + folly::ssl::X509StoreUniquePtr store(X509_STORE_new()); + for (auto& caCert : certs) { + if (X509_STORE_add_cert(store.get(), caCert.get()) != 1) { + auto err = ERR_get_error(); + if (ERR_GET_LIB(err) != ERR_LIB_X509 || + ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + throw std::runtime_error(folly::to<std::string>( + "Could not insert CA certificate into store: ", + getOpenSSLErrorString(err))); + } + } + } + return store; +} +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h new file mode 100644 index 000000000..4ebc7c1f7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <string> +#include <vector> + +#include <folly/Optional.h> +#include <folly/io/IOBuf.h> +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { +namespace ssl { + +class OpenSSLCertUtils { + public: + // Note: non-const until OpenSSL 1.1.0 + static Optional<std::string> getCommonName(X509& x509); + + static std::vector<std::string> getSubjectAltNames(X509& x509); + + /* + * Return the subject name, if any, from the cert + * @param x509 Reference to an X509 + * @return a folly::Optional<std::string>, or folly::none + */ + static Optional<std::string> getSubject(X509& x509); + + /* + * Return the issuer name, if any, from the cert + * @param x509 Reference to an X509 + * @return a folly::Optional<std::string>, or folly::none + */ + static Optional<std::string> getIssuer(X509& x509); + + /* + * Get a string representation of the not-before time on the certificate + */ + static std::string getNotBeforeTime(X509& x509); + + /* + * Get a string representation of the not-after (expiration) time + */ + static std::string getNotAfterTime(X509& x509); + + /* + * Summarize the CN, Subject, Issuer, Validity, and extensions as a string + */ + static folly::Optional<std::string> toString(X509& x509); + + /** + * Decode the DER representation of an X509 certificate. + * + * Throws on error (if a valid certificate can't be decoded). + */ + static X509UniquePtr derDecode(ByteRange); + + /** + * Encode an X509 certificate in DER format. + * + * Throws on error. + */ + static std::unique_ptr<IOBuf> derEncode(X509&); + + /** + * Read certificates from memory and returns them as a vector of X509 + * pointers. Throw if there is any malformed cert or memory allocation + * problem. + * @param range Buffer to parse. + * @return A vector of X509 objects. + */ + static std::vector<X509UniquePtr> readCertsFromBuffer(ByteRange range); + + /** + * Return the output of the X509_digest for chosen message-digest algo + * NOTE: The returned digest will be in binary, and may need to be + * hex-encoded + */ + static std::array<uint8_t, SHA_DIGEST_LENGTH> getDigestSha1(X509& x509); + static std::array<uint8_t, SHA256_DIGEST_LENGTH> getDigestSha256(X509& x509); + + /** + * Read a store from a file. Throw if unable to read the file, memory + * allocation fails, or any cert can't be parsed or added to the store. + * @param caFile Path to the CA file. + * @return A X509 store that contains certs in the CA file. + */ + static X509StoreUniquePtr readStoreFromFile(std::string caFile); + + /** + * Read a store from a PEM buffer. Throw if memory allocation fails, or + * any cert can't be parsed or added to the store. + * @param range A buffer containing certs in PEM format. + * @return A X509 store that contains certs in the CA file. + */ + static X509StoreUniquePtr readStoreFromBuffer(ByteRange range); + + /** + * Converts an ASN1_TIME* into a system clock time point for use with other + * std::chrono classes. + */ + static std::chrono::system_clock::time_point asnTimeToTimepoint( + const ASN1_TIME* asnTime); + + private: + static std::string getDateTimeStr(const ASN1_TIME* time); +}; +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.cpp b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.cpp new file mode 100644 index 000000000..e0c68a3ff --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ssl/OpenSSLHash.h> + +#include <folly/Format.h> + +namespace folly { +namespace ssl { + +[[noreturn]] void OpenSSLHash::check_out_size_throw( + size_t size, + MutableByteRange out) { + throw std::invalid_argument(folly::sformat( + "expected out of size {} but was of size {}", size, out.size())); +} + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.h b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.h new file mode 100644 index 000000000..a23682ecf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLHash.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Range.h> +#include <folly/io/IOBuf.h> +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/OpenSSLPtrTypes.h> + +namespace folly { +namespace ssl { + +/// Warning: +/// These functions are not thread-safe unless you initialize OpenSSL. +class OpenSSLHash { + public: + class Digest { + public: + Digest() : ctx_(EVP_MD_CTX_new()) {} + + Digest(const Digest& other) { + ctx_ = EvpMdCtxUniquePtr(EVP_MD_CTX_new()); + if (other.md_ != nullptr) { + hash_init(other.md_); + check_libssl_result( + 1, EVP_MD_CTX_copy_ex(ctx_.get(), other.ctx_.get())); + } + } + + Digest& operator=(const Digest& other) { + this->~Digest(); + return *new (this) Digest(other); + } + + void hash_init(const EVP_MD* md) { + md_ = md; + check_libssl_result(1, EVP_DigestInit_ex(ctx_.get(), md, nullptr)); + } + void hash_update(ByteRange data) { + check_libssl_result( + 1, EVP_DigestUpdate(ctx_.get(), data.data(), data.size())); + } + void hash_update(const IOBuf& data) { + for (auto r : data) { + hash_update(r); + } + } + void hash_final(MutableByteRange out) { + const auto size = EVP_MD_size(md_); + check_out_size(size_t(size), out); + unsigned int len = 0; + check_libssl_result(1, EVP_DigestFinal_ex(ctx_.get(), out.data(), &len)); + check_libssl_result(size, int(len)); + md_ = nullptr; + } + + private: + const EVP_MD* md_ = nullptr; + EvpMdCtxUniquePtr ctx_{nullptr}; + }; + + static void hash(MutableByteRange out, const EVP_MD* md, ByteRange data) { + Digest hash; + hash.hash_init(md); + hash.hash_update(data); + hash.hash_final(out); + } + static void hash(MutableByteRange out, const EVP_MD* md, const IOBuf& data) { + Digest hash; + hash.hash_init(md); + hash.hash_update(data); + hash.hash_final(out); + } + static void sha1(MutableByteRange out, ByteRange data) { + hash(out, EVP_sha1(), data); + } + static void sha1(MutableByteRange out, const IOBuf& data) { + hash(out, EVP_sha1(), data); + } + static void sha256(MutableByteRange out, ByteRange data) { + hash(out, EVP_sha256(), data); + } + static void sha256(MutableByteRange out, const IOBuf& data) { + hash(out, EVP_sha256(), data); + } + + class Hmac { + public: + Hmac() : ctx_(HMAC_CTX_new()) {} + + void hash_init(const EVP_MD* md, ByteRange key) { + md_ = md; + check_libssl_result( + 1, + HMAC_Init_ex(ctx_.get(), key.data(), int(key.size()), md_, nullptr)); + } + void hash_update(ByteRange data) { + check_libssl_result(1, HMAC_Update(ctx_.get(), data.data(), data.size())); + } + void hash_update(const IOBuf& data) { + for (auto r : data) { + hash_update(r); + } + } + void hash_final(MutableByteRange out) { + const auto size = EVP_MD_size(md_); + check_out_size(size_t(size), out); + unsigned int len = 0; + check_libssl_result(1, HMAC_Final(ctx_.get(), out.data(), &len)); + check_libssl_result(size, int(len)); + md_ = nullptr; + } + + private: + const EVP_MD* md_ = nullptr; + HmacCtxUniquePtr ctx_{nullptr}; + }; + + static void + hmac(MutableByteRange out, const EVP_MD* md, ByteRange key, ByteRange data) { + Hmac hmac; + hmac.hash_init(md, key); + hmac.hash_update(data); + hmac.hash_final(out); + } + static void hmac( + MutableByteRange out, + const EVP_MD* md, + ByteRange key, + const IOBuf& data) { + Hmac hmac; + hmac.hash_init(md, key); + hmac.hash_update(data); + hmac.hash_final(out); + } + static void hmac_sha1(MutableByteRange out, ByteRange key, ByteRange data) { + hmac(out, EVP_sha1(), key, data); + } + static void + hmac_sha1(MutableByteRange out, ByteRange key, const IOBuf& data) { + hmac(out, EVP_sha1(), key, data); + } + static void hmac_sha256(MutableByteRange out, ByteRange key, ByteRange data) { + hmac(out, EVP_sha256(), key, data); + } + static void + hmac_sha256(MutableByteRange out, ByteRange key, const IOBuf& data) { + hmac(out, EVP_sha256(), key, data); + } + + private: + static inline void check_out_size(size_t size, MutableByteRange out) { + if (LIKELY(size == out.size())) { + return; + } + check_out_size_throw(size, out); + } + [[noreturn]] static void check_out_size_throw( + size_t size, + MutableByteRange out); + + static inline void check_libssl_result(int expected, int result) { + if (LIKELY(result == expected)) { + return; + } + throw_exception<std::runtime_error>("openssl crypto function failed"); + } +}; + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h similarity index 51% rename from ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h rename to ios/Pods/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h index dcb5429b9..bbe6a23be 100644 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h @@ -1,11 +1,11 @@ /* - * Copyright 2019 Google + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,18 +14,20 @@ * limitations under the License. */ -#import <Foundation/Foundation.h> +#pragma once -NS_ASSUME_NONNULL_BEGIN +#include <map> + +namespace folly { +namespace ssl { + +enum class LockType { MUTEX, SPINLOCK, SHAREDMUTEX, NONE }; /** - * A generic class to combine several handler blocks into a single block in a thread-safe manner + * Map between an OpenSSL lock (see constants in crypto/crypto.h) and the + * implementation of the lock */ -@interface FIRInstanceIDCombinedHandler<ResultType> : NSObject +using LockTypeMapping = std::map<int, LockType>; -- (void)addHandler:(void (^)(ResultType _Nullable result, NSError* _Nullable error))handler; -- (void (^)(ResultType _Nullable result, NSError* _Nullable error))combinedHandler; - -@end - -NS_ASSUME_NONNULL_END +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h new file mode 100644 index 000000000..4ce115707 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <glog/logging.h> + +#include <folly/Memory.h> +#include <folly/portability/OpenSSL.h> + +namespace folly { +namespace ssl { + +// helper which translates: +// FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(Foo, FOO, FOO_free); +// into +// using FooDeleter = folly::static_function_deleter<FOO, &FOO_free>; +// using FooUniquePtr = std::unique_ptr<FOO, FooDeleter>; +#define FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(alias, object, deleter) \ + using alias##Deleter = folly::static_function_deleter<object, &deleter>; \ + using alias##UniquePtr = std::unique_ptr<object, alias##Deleter> + +// ASN1 +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(ASN1Time, ASN1_TIME, ASN1_TIME_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + ASN1Ia5Str, + ASN1_IA5STRING, + ASN1_IA5STRING_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(ASN1Int, ASN1_INTEGER, ASN1_INTEGER_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(ASN1Obj, ASN1_OBJECT, ASN1_OBJECT_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(ASN1Str, ASN1_STRING, ASN1_STRING_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(ASN1Type, ASN1_TYPE, ASN1_TYPE_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + ASN1UTF8Str, + ASN1_UTF8STRING, + ASN1_UTF8STRING_free); + +// X509 +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509, X509, X509_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + X509Extension, + X509_EXTENSION, + X509_EXTENSION_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509Store, X509_STORE, X509_STORE_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + X509StoreCtx, + X509_STORE_CTX, + X509_STORE_CTX_free); +using X509VerifyParamDeleter = + folly::static_function_deleter<X509_VERIFY_PARAM, &X509_VERIFY_PARAM_free>; +using X509VerifyParam = + std::unique_ptr<X509_VERIFY_PARAM, X509VerifyParamDeleter>; + +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(GeneralName, GENERAL_NAME, GENERAL_NAME_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + GeneralNames, + GENERAL_NAMES, + GENERAL_NAMES_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + AccessDescription, + ACCESS_DESCRIPTION, + ACCESS_DESCRIPTION_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + AuthorityInfoAccess, + AUTHORITY_INFO_ACCESS, + AUTHORITY_INFO_ACCESS_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + DistPointName, + DIST_POINT_NAME, + DIST_POINT_NAME_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(DistPoint, DIST_POINT, DIST_POINT_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + CrlDistPoints, + CRL_DIST_POINTS, + CRL_DIST_POINTS_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509Crl, X509_CRL, X509_CRL_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509Name, X509_NAME, X509_NAME_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509Req, X509_REQ, X509_REQ_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(X509Revoked, X509_REVOKED, X509_REVOKED_free); + +// EVP +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EvpPkey, EVP_PKEY, EVP_PKEY_free); +using EvpPkeySharedPtr = std::shared_ptr<EVP_PKEY>; + +// No EVP_PKEY_CTX <= 0.9.8b +#if OPENSSL_VERSION_NUMBER >= 0x10000002L +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EvpPkeyCtx, EVP_PKEY_CTX, EVP_PKEY_CTX_free); +#else +struct EVP_PKEY_CTX; +#endif + +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EvpMdCtx, EVP_MD_CTX, EVP_MD_CTX_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE( + EvpCipherCtx, + EVP_CIPHER_CTX, + EVP_CIPHER_CTX_free); + +// HMAC +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(HmacCtx, HMAC_CTX, HMAC_CTX_free); + +// BIO +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(BioMethod, BIO_METHOD, BIO_meth_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(Bio, BIO, BIO_vfree); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(BioChain, BIO, BIO_free_all); +inline void BIO_free_fb(BIO* bio) { + CHECK_EQ(1, BIO_free(bio)); +} +using BioDeleterFb = folly::static_function_deleter<BIO, &BIO_free_fb>; +using BioUniquePtrFb = std::unique_ptr<BIO, BioDeleterFb>; + +// RSA and EC +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(Rsa, RSA, RSA_free); +#ifndef OPENSSL_NO_EC +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EcKey, EC_KEY, EC_KEY_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EcGroup, EC_GROUP, EC_GROUP_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EcPoint, EC_POINT, EC_POINT_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(EcdsaSig, ECDSA_SIG, ECDSA_SIG_free); +#endif + +// BIGNUMs +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(BIGNUM, BIGNUM, BN_clear_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(BNCtx, BN_CTX, BN_CTX_free); + +// SSL and SSL_CTX +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(SSL, SSL, SSL_free); +FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE(SSLSession, SSL_SESSION, SSL_SESSION_free); + +#undef FOLLY_SSL_DETAIL_DEFINE_PTR_TYPE +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h new file mode 100644 index 000000000..32d18f77d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Conv.h> +#include <folly/portability/OpenSSL.h> + +// This is used to find the OpenSSL version at runtime. Just returning +// OPENSSL_VERSION_NUMBER is insufficient as runtime version may be different +// from the compile-time version +namespace folly { +namespace ssl { +inline std::string getOpenSSLLongVersion() { +#ifdef OPENSSL_VERSION_TEXT + return OpenSSL_version(OPENSSL_VERSION); +#elif defined(OPENSSL_VERSION_NUMBER) + return folly::format("0x{:x}", OPENSSL_VERSION_NUMBER).str(); +#else + return ""; +#endif +} + +inline uint64_t getOpenSSLNumericVersion() { +#ifdef OPENSSL_VERSION_NUMBER + return OpenSSL_version_num(); +#else + return 0; +#endif +} +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/SSLSession.h b/ios/Pods/Flipper-Folly/folly/ssl/SSLSession.h new file mode 100644 index 000000000..44b834b86 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/SSLSession.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Memory.h> +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/detail/SSLSessionImpl.h> + +namespace folly { +namespace ssl { + +class SSLSession { + public: + // Holds and takes ownership of an SSL_SESSION object by incrementing refcount + explicit SSLSession(SSL_SESSION* session, bool takeOwnership = true) + : impl_( + std::make_unique<detail::SSLSessionImpl>(session, takeOwnership)) {} + + // Deserialize from a string + explicit SSLSession(const std::string& serializedSession) + : impl_(std::make_unique<detail::SSLSessionImpl>(serializedSession)) {} + + // Serialize to a string that is suitable to store in a persistent cache + std::string serialize() const { + return impl_->serialize(); + } + + // Get Session ID. Returns an empty container if session isn't set + std::string getSessionID() const { + return impl_->getSessionID(); + } + + // Get a const raw SSL_SESSION ptr without incrementing referecnce count + // (Warning: do not use) + const SSL_SESSION* getRawSSLSession() const { + return impl_->getRawSSLSession(); + } + + // Get raw SSL_SESSION pointer + // Warning: do not use unless you know what you're doing - caller needs to + // decrement refcount using SSL_SESSION_free or this will leak + SSL_SESSION* getRawSSLSessionDangerous() { + return impl_->getRawSSLSessionDangerous(); + } + + private: + std::unique_ptr<detail::SSLSessionImpl> impl_; +}; + +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.cpp b/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.cpp new file mode 100644 index 000000000..bbf43736a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ssl/detail/OpenSSLThreading.h> + +#include <memory> +#include <mutex> + +#include <folly/Portability.h> +#include <folly/SharedMutex.h> +#include <folly/SpinLock.h> + +#include <glog/logging.h> + +// We cannot directly use portability/openssl because it also depends on us. +// Therefore we directly use openssl includes. Order of includes is important +// here. See portability/openssl.h. +#include <folly/portability/Windows.h> +#include <openssl/crypto.h> + +#if !defined(OPENSSL_IS_BORINGSSL) +#define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#else +#define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (false) +#endif + +// OpenSSL requires us to provide the implementation of CRYPTO_dynlock_value +// so it must be done in the global namespace. +struct CRYPTO_dynlock_value { + std::mutex mutex; +}; + +namespace folly { +namespace ssl { +namespace detail { + +static std::map<int, LockType>& lockTypes() { + static auto lockTypesInst = new std::map<int, LockType>(); + return *lockTypesInst; +} + +void setLockTypes(std::map<int, LockType> inLockTypes) { +#if FOLLY_SSL_DETAIL_OPENSSL_IS_110 + VLOG(3) << "setLockTypes() is unsupported on OpenSSL >= 1.1.0. " + << "OpenSSL now uses platform native mutexes"; +#endif + + lockTypes() = inLockTypes; +} + +bool isSSLLockDisabled(int lockId) { + const auto& sslLocks = lockTypes(); + const auto it = sslLocks.find(lockId); + return it != sslLocks.end() && it->second == LockType::NONE; +} + +namespace { +struct SSLLock { + explicit SSLLock(LockType inLockType = LockType::MUTEX) + : lockType(inLockType) {} + + void lock(bool read) { + if (lockType == LockType::MUTEX) { + mutex.lock(); + } else if (lockType == LockType::SPINLOCK) { + spinLock.lock(); + } else if (lockType == LockType::SHAREDMUTEX) { + if (read) { + sharedMutex.lock_shared(); + } else { + sharedMutex.lock(); + } + } + // lockType == LOCK_NONE, no-op + } + + void unlock(bool read) { + if (lockType == LockType::MUTEX) { + mutex.unlock(); + } else if (lockType == LockType::SPINLOCK) { + spinLock.unlock(); + } else if (lockType == LockType::SHAREDMUTEX) { + if (read) { + sharedMutex.unlock_shared(); + } else { + sharedMutex.unlock(); + } + } + // lockType == LOCK_NONE, no-op + } + + LockType lockType; + folly::SpinLock spinLock{}; + std::mutex mutex; + SharedMutex sharedMutex; +}; +} // namespace + +// Statics are unsafe in environments that call exit(). +// If one thread calls exit() while another thread is +// references a member of SSLContext, bad things can happen. +// SSLContext runs in such environments. +// Instead of declaring a static member we "new" the static +// member so that it won't be destructed on exit(). +#if !FOLLY_SSL_DETAIL_OPENSSL_IS_110 +static std::unique_ptr<SSLLock[]>& locks() { + static auto locksInst = new std::unique_ptr<SSLLock[]>(); + return *locksInst; +} + +static void callbackLocking(int mode, int n, const char*, int) { + if (mode & CRYPTO_LOCK) { + locks()[size_t(n)].lock(mode & CRYPTO_READ); + } else { + locks()[size_t(n)].unlock(mode & CRYPTO_READ); + } +} + +static void callbackThreadID(CRYPTO_THREADID* id) { + return CRYPTO_THREADID_set_numeric(id, folly::getCurrentThreadID()); +} + +static CRYPTO_dynlock_value* dyn_create(const char*, int) { + return new CRYPTO_dynlock_value; +} + +static void +dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) { + if (lock != nullptr) { + if (mode & CRYPTO_LOCK) { + lock->mutex.lock(); + } else { + lock->mutex.unlock(); + } + } +} + +static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) { + delete lock; +} +#endif + +void installThreadingLocks() { +#if !FOLLY_SSL_DETAIL_OPENSSL_IS_110 + // static locking + locks() = std::make_unique<SSLLock[]>(size_t(CRYPTO_num_locks())); + for (auto it : lockTypes()) { + locks()[size_t(it.first)].lockType = it.second; + } + CRYPTO_THREADID_set_callback(callbackThreadID); + CRYPTO_set_locking_callback(callbackLocking); + // dynamic locking + CRYPTO_set_dynlock_create_callback(dyn_create); + CRYPTO_set_dynlock_lock_callback(dyn_lock); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy); +#endif +} + +void cleanupThreadingLocks() { +#if !FOLLY_SSL_DETAIL_OPENSSL_IS_110 + CRYPTO_THREADID_set_callback(nullptr); + CRYPTO_set_locking_callback(nullptr); + CRYPTO_set_dynlock_create_callback(nullptr); + CRYPTO_set_dynlock_lock_callback(nullptr); + CRYPTO_set_dynlock_destroy_callback(nullptr); + locks().reset(); +#endif +} + +} // namespace detail +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h b/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h new file mode 100644 index 000000000..bc2e35a60 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <map> + +#include <folly/ssl/OpenSSLLockTypes.h> + +namespace folly { +namespace ssl { +namespace detail { +bool isSSLLockDisabled(int lockId); +void setLockTypes(std::map<int, LockType> inLockTypes); +void installThreadingLocks(); +void cleanupThreadingLocks(); +} // namespace detail +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.cpp b/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.cpp new file mode 100644 index 000000000..e1a440fdf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/ssl/detail/SSLSessionImpl.h> + +#include <glog/logging.h> + +#include <folly/portability/OpenSSL.h> +#include <folly/ssl/OpenSSLVersionFinder.h> + +namespace folly { +namespace ssl { +namespace detail { + +// +// Wrapper OpenSSL 1.0.2 (and possibly 1.0.1) +// + +SSLSessionImpl::SSLSessionImpl(SSL_SESSION* session, bool takeOwnership) + : session_(session) { + if (session_ == nullptr) { + throw std::runtime_error("SSL_SESSION is null"); + } + // If we're not given ownership, we need to up the refcount so the SSL_SESSION + // object won't be freed while SSLSessionImpl is alive + if (!takeOwnership) { + upRef(); + } +} + +SSLSessionImpl::SSLSessionImpl(const std::string& serializedSession) { + auto sessionData = + reinterpret_cast<const unsigned char*>(serializedSession.data()); + auto longLen = long(serializedSession.length()); + if ((session_ = d2i_SSL_SESSION(nullptr, &sessionData, longLen)) == nullptr) { + throw std::runtime_error("Cannot deserialize SSLSession string"); + } +} + +SSLSessionImpl::~SSLSessionImpl() { + downRef(); +} + +std::string SSLSessionImpl::serialize() const { + std::string ret; + + // Get the length first, then we know how much space to allocate. + auto len = i2d_SSL_SESSION(session_, nullptr); + + if (len > 0) { + std::unique_ptr<unsigned char[]> uptr(new unsigned char[size_t(len)]); + auto p = uptr.get(); + auto written = i2d_SSL_SESSION(session_, &p); + if (written <= 0) { + VLOG(2) << "Could not serialize SSL_SESSION!"; + } else { + ret.assign(uptr.get(), uptr.get() + written); + } + } + return ret; +} + +std::string SSLSessionImpl::getSessionID() const { + std::string ret; + if (session_) { + const unsigned char* ptr = nullptr; + unsigned int len = 0; + ptr = SSL_SESSION_get_id(session_, &len); + ret.assign(ptr, ptr + len); + } + return ret; +} + +const SSL_SESSION* SSLSessionImpl::getRawSSLSession() const { + return const_cast<SSL_SESSION*>(session_); +} + +SSL_SESSION* SSLSessionImpl::getRawSSLSessionDangerous() { + upRef(); + return session_; +} + +void SSLSessionImpl::upRef() { + if (session_) { + SSL_SESSION_up_ref(session_); + } +} + +void SSLSessionImpl::downRef() { + if (session_) { + SSL_SESSION_free(session_); + } +} + +} // namespace detail +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h b/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h new file mode 100644 index 000000000..94394aa0d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Range.h> + +#include <folly/portability/OpenSSL.h> + +#include <string> + +namespace folly { +namespace ssl { +namespace detail { + +class SSLSessionImpl { + public: + explicit SSLSessionImpl(SSL_SESSION* session, bool takeOwnership = true); + explicit SSLSessionImpl(const std::string& serializedSession); + virtual ~SSLSessionImpl(); + std::string serialize() const; + std::string getSessionID() const; + const SSL_SESSION* getRawSSLSession() const; + SSL_SESSION* getRawSSLSessionDangerous(); + + private: + void upRef(); + void downRef(); + + SSL_SESSION* session_{nullptr}; +}; + +} // namespace detail +} // namespace ssl +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/stop_watch.h b/ios/Pods/Flipper-Folly/folly/stop_watch.h new file mode 100644 index 000000000..3556c70ca --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/stop_watch.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> +#include <stdexcept> +#include <utility> + +#include <folly/Chrono.h> +#include <folly/portability/Time.h> + +namespace folly { + +using monotonic_clock = std::chrono::steady_clock; + +/** + * Calculates the duration of time intervals. Prefer this over directly using + * monotonic clocks. It is very lightweight and provides convenient facilitles + * to avoid common pitfalls. + * + * There are two type aliases that should be preferred over instantiating this + * class directly: `coarse_stop_watch` and `stop_watch`. + * + * Arguments: + * - Clock: the monotonic clock to use when calculating time intervals + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example 1: + * + * coarse_stop_watch<std::chrono::seconds> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl; + * + * auto const ttl = 60_s; + * if (watch.elapsed(ttl)) { + * process_expiration(); + * } + * + * Example 2: + * + * struct run_every_n_seconds { + * using callback = std::function<void()>; + * run_every_n_seconds(std::chrono::seconds period, callback action) + * period_(period), + * action_(std::move(action)) + * { + * // watch_ is correctly initialized to the current time + * } + * + * void run() { + * while (true) { + * if (watch_.lap(period_)) { + * action_(); + * } + * std::this_thread::yield(); + * } + * } + * + * private: + * stop_watch<> watch_; + * std::chrono::seconds period_; + * callback action_; + * }; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ +template <typename Clock, typename Duration = typename Clock::duration> +struct custom_stop_watch { + using clock_type = Clock; + using duration = Duration; + using time_point = std::chrono::time_point<clock_type, duration>; + + static_assert( + std::ratio_less_equal< + typename clock_type::duration::period, + typename duration::period>::value, + "clock must be at least as precise as the requested duration"); + + static_assert( + Clock::is_steady, + "only monotonic clocks should be used to track time intervals"); + + /** + * Initializes the stop watch with the current time as its checkpoint. + * + * Example: + * + * stop_watch<> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + custom_stop_watch() : checkpoint_(clock_type::now()) {} + + /** + * Initializes the stop watch with the given time as its checkpoint. + * + * NOTE: this constructor should be seldomly used. It is only provided so + * that, in the rare occasions it is needed, one does not have to reimplement + * the `custom_stop_watch` class. + * + * Example: + * + * custom_stop_watch<monotonic_clock> watch(monotonic_clock::now()); + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + explicit custom_stop_watch(typename clock_type::time_point checkpoint) + : checkpoint_(std::move(checkpoint)) {} + + /** + * Updates the stop watch checkpoint to the current time. + * + * Example: + * + * struct some_resource { + * // ... + * + * void on_reloaded() { + * time_alive.reset(); + * } + * + * void report() { + * std::cout << "resource has been alive for " << time_alive.elapsed(); + * } + * + * private: + * stop_watch<> time_alive; + * }; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + void reset() { + checkpoint_ = clock_type::now(); + } + + /** + * Tells the elapsed time since the last update. + * + * The stop watch's checkpoint remains unchanged. + * + * Example: + * + * stop_watch<> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + duration elapsed() const { + return std::chrono::duration_cast<duration>( + clock_type::now() - checkpoint_); + } + + /** + * Tells whether the given duration has already elapsed since the last + * checkpoint. + * + * Example: + * + * auto const ttl = 60_s; + * stop_watch<> watch; + * + * do_something(); + * + * std::cout << "has the TTL expired? " std::boolalpha<< watch.elapsed(ttl); + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + template <typename UDuration> + bool elapsed(UDuration&& amount) const { + return clock_type::now() - checkpoint_ >= amount; + } + + /** + * Tells the elapsed time since the last update, and updates the checkpoint + * to the current time. + * + * Example: + * + * struct some_resource { + * // ... + * + * void on_reloaded() { + * auto const alive = time_alive.lap(); + * std::cout << "resource reloaded after being alive for " << alive; + * } + * + * private: + * stop_watch<> time_alive; + * }; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + duration lap() { + auto lastCheckpoint = checkpoint_; + + checkpoint_ = clock_type::now(); + + return std::chrono::duration_cast<duration>(checkpoint_ - lastCheckpoint); + } + + /** + * Tells whether the given duration has already elapsed since the last + * checkpoint. If so, update the checkpoint to the current time. If not, + * the checkpoint remains unchanged. + * + * Example: + * + * void run_every_n_seconds( + * std::chrono::seconds period, + * std::function<void()> action + * ) { + * for (stop_watch<> watch;; ) { + * if (watch.lap(period)) { + * action(); + * } + * std::this_thread::yield(); + * } + * } + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ + template <typename UDuration> + bool lap(UDuration&& amount) { + auto now = clock_type::now(); + + if (now - checkpoint_ < amount) { + return false; + } + + checkpoint_ = now; + return true; + } + + /** + * Returns the current checkpoint + */ + typename clock_type::time_point getCheckpoint() const { + return checkpoint_; + } + + private: + typename clock_type::time_point checkpoint_; +}; + +/** + * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as + * the time source. Refer to the documentation of `custom_stop_watch` for full + * documentation. + * + * Arguments: + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example: + * + * coarse_stop_watch<std::chrono::seconds> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ +template <typename Duration = folly::chrono::coarse_steady_clock::duration> +using coarse_stop_watch = + custom_stop_watch<folly::chrono::coarse_steady_clock, Duration>; + +/** + * A type alias for `custom_stop_watch` that uses a monotonic clock as the time + * source. Refer to the documentation of `custom_stop_watch` for full + * documentation. + * + * Arguments: + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example: + * + * stop_watch<std::chrono::seconds> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl; + * + * @author: Marcelo Juchem <marcelo@fb.com> + */ +template <typename Duration = std::chrono::steady_clock::duration> +using stop_watch = custom_stop_watch<std::chrono::steady_clock, Duration>; +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.cpp new file mode 100644 index 000000000..d8d9a482d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/AsymmetricMemoryBarrier.h> + +#include <folly/Exception.h> +#include <folly/Indestructible.h> +#include <folly/portability/SysMembarrier.h> +#include <folly/portability/SysMman.h> +#include <mutex> + +namespace folly { + +namespace { + +struct DummyPageCreator { + DummyPageCreator() { + get(); + } + + static void* get() { + static auto ptr = kIsLinux ? create() : nullptr; + return ptr; + } + + private: + static void* create() { + auto ptr = mmap(nullptr, 1, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + checkUnixError(reinterpret_cast<ssize_t>(ptr), "mmap"); + + // Optimistically try to lock the page so it stays resident. Could make + // the heavy barrier faster. + auto r = mlock(ptr, 1); + if (r != 0) { + // Do nothing. + } + + return ptr; + } +}; + +// Make sure dummy page is always initialized before shutdown. +DummyPageCreator dummyPageCreator; + +void mprotectMembarrier() { + auto dummyPage = dummyPageCreator.get(); + + // This function is required to be safe to call on shutdown, + // so we must leak the mutex. + static Indestructible<std::mutex> mprotectMutex; + std::lock_guard<std::mutex> lg(*mprotectMutex); + + int r = 0; + + // We want to downgrade the page while it is resident. To do that, it must + // first be upgraded and forced to be resident. + r = mprotect(dummyPage, 1, PROT_READ | PROT_WRITE); + checkUnixError(r, "mprotect"); + + // Force the page to be resident. If it is already resident, almost no-op. + *static_cast<char*>(dummyPage) = 0; + + // Downgrade the page. Forces a memory barrier in every core running any + // of the process's threads. On a sane platform. + r = mprotect(dummyPage, 1, PROT_READ); + checkUnixError(r, "mprotect"); +} +} // namespace + +void asymmetricHeavyBarrier(AMBFlags flags) { + if (kIsLinux) { + static const bool useSysMembarrier = detail::sysMembarrierAvailable(); + // sys_membarrier currently does not support EXPEDITED + if (useSysMembarrier && flags != AMBFlags::EXPEDITED) { + auto r = detail::sysMembarrier(); + checkUnixError(r, "membarrier"); + } else { + mprotectMembarrier(); + } + } else { + std::atomic_thread_fence(std::memory_order_seq_cst); + } +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h b/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h new file mode 100644 index 000000000..f61a213dc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +#include <folly/portability/Asm.h> + +namespace folly { + +enum class AMBFlags { + NORMAL, + EXPEDITED, +}; + +FOLLY_ALWAYS_INLINE void asymmetricLightBarrier() { + if (kIsLinux) { + asm_volatile_memory(); + } else { + std::atomic_thread_fence(std::memory_order_seq_cst); + } +} + +void asymmetricHeavyBarrier(AMBFlags flags = AMBFlags::EXPEDITED); +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h new file mode 100644 index 000000000..aa12dffb7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/detail/Futex.h> +#include <folly/synchronization/ParkingLot.h> + +#include <condition_variable> +#include <cstdint> + +namespace folly { +namespace detail { +namespace atomic_notification { +/** + * We use Futex<std::atomic> as the alias that has the lowest performance + * overhead with respect to atomic notifications. Assert that + * atomic_uint_fast_wait_t is the same as Futex<std::atomic> + */ +static_assert(std::is_same<atomic_uint_fast_wait_t, Futex<std::atomic>>{}, ""); + +/** + * Implementation and specializations for the atomic_wait() family of + * functions + */ +inline std::cv_status toCvStatus(FutexResult result) { + return (result == FutexResult::TIMEDOUT) ? std::cv_status::timeout + : std::cv_status::no_timeout; +} +inline std::cv_status toCvStatus(ParkResult result) { + return (result == ParkResult::Timeout) ? std::cv_status::timeout + : std::cv_status::no_timeout; +} + +// ParkingLot instantiation for futex management +extern ParkingLot<std::uint32_t> parkingLot; + +template <template <typename...> class Atom, typename... Args> +void atomic_wait_impl( + const Atom<std::uint32_t, Args...>* atomic, + std::uint32_t expected) { + futexWait(atomic, expected); + return; +} + +template <template <typename...> class Atom, typename Integer, typename... Args> +void atomic_wait_impl(const Atom<Integer, Args...>* atomic, Integer expected) { + static_assert(!std::is_same<Integer, std::uint32_t>{}, ""); + parkingLot.park( + atomic, -1, [&] { return atomic->load() == expected; }, [] {}); +} + +template < + template <typename...> class Atom, + typename... Args, + typename Clock, + typename Duration> +std::cv_status atomic_wait_until_impl( + const Atom<std::uint32_t, Args...>* atomic, + std::uint32_t expected, + const std::chrono::time_point<Clock, Duration>& deadline) { + return toCvStatus(futexWaitUntil(atomic, expected, deadline)); +} + +template < + template <typename...> class Atom, + typename Integer, + typename... Args, + typename Clock, + typename Duration> +std::cv_status atomic_wait_until_impl( + const Atom<Integer, Args...>* atomic, + Integer expected, + const std::chrono::time_point<Clock, Duration>& deadline) { + static_assert(!std::is_same<Integer, std::uint32_t>{}, ""); + return toCvStatus(parkingLot.park_until( + atomic, -1, [&] { return atomic->load() == expected; }, [] {}, deadline)); +} + +template <template <typename...> class Atom, typename... Args> +void atomic_notify_one_impl(const Atom<std::uint32_t, Args...>* atomic) { + futexWake(atomic, 1); + return; +} + +template <template <typename...> class Atom, typename Integer, typename... Args> +void atomic_notify_one_impl(const Atom<Integer, Args...>* atomic) { + static_assert(!std::is_same<Integer, std::uint32_t>{}, ""); + parkingLot.unpark(atomic, [&](const auto& data) { + FOLLY_SAFE_DCHECK(data == std::numeric_limits<std::uint32_t>::max(), ""); + return UnparkControl::RemoveBreak; + }); +} + +template <template <typename...> class Atom, typename... Args> +void atomic_notify_all_impl(const Atom<std::uint32_t, Args...>* atomic) { + futexWake(atomic); + return; +} + +template <template <typename...> class Atom, typename Integer, typename... Args> +void atomic_notify_all_impl(const Atom<Integer, Args...>* atomic) { + static_assert(!std::is_same<Integer, std::uint32_t>{}, ""); + parkingLot.unpark(atomic, [&](const auto& data) { + FOLLY_SAFE_DCHECK(data == std::numeric_limits<std::uint32_t>::max(), ""); + return UnparkControl::RemoveContinue; + }); +} +} // namespace atomic_notification +} // namespace detail + +template <typename Integer> +void atomic_wait(const std::atomic<Integer>* atomic, Integer expected) { + detail::atomic_notification::atomic_wait_impl(atomic, expected); +} + +template <typename Integer, typename Clock, typename Duration> +std::cv_status atomic_wait_until( + const std::atomic<Integer>* atomic, + Integer expected, + const std::chrono::time_point<Clock, Duration>& deadline) { + return detail::atomic_notification::atomic_wait_until_impl( + atomic, expected, deadline); +} + +template <typename Integer> +void atomic_notify_one(const std::atomic<Integer>* atomic) { + detail::atomic_notification::atomic_notify_one_impl(atomic); +} + +template <typename Integer> +void atomic_notify_all(const std::atomic<Integer>* atomic) { + detail::atomic_notification::atomic_notify_all_impl(atomic); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.cpp new file mode 100644 index 000000000..5cd3b2ea1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/AtomicNotification.h> + +#include <cstdint> + +namespace folly { +namespace detail { +namespace atomic_notification { + +// ParkingLot instance used for the atomic_wait() family of functions +// +// This has been defined as a static object (as opposed to allocated to avoid +// destruction order problems) because of possible uses coming from +// allocation-sensitive contexts. +ParkingLot<std::uint32_t> parkingLot; + +} // namespace atomic_notification +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.h new file mode 100644 index 000000000..9fc9dee17 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicNotification.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <condition_variable> + +namespace folly { + +/** + * The behavior of the atomic_wait() family of functions is semantically + * identical to futex(). Correspondingly, calling atomic_notify_one(), + * atomic_notify_all() is identical to futexWake() with 1 and + * std::numeric_limits<int>::max() respectively + * + * The difference here compared to the futex API above is that it works with + * all types of atomic widths. When a 32 bit atomic integer is used, the + * implementation falls back to using futex() if possible, and the + * compatibility implementation for non-linux systems otherwise. For all + * other integer widths, the compatibility implementation is used + * + * The templating of this API is changed from the standard in the following + * ways + * + * - At the time of writing, libstdc++'s implementation of std::atomic<> does + * not include the value_type alias. So we rely on the atomic type being a + * template class such that the first type is the underlying value type + * - The Atom parameter allows this API to be compatible with + * DeterministicSchedule testing. + * - atomic_wait_until() does not exist in the linked paper, the version here + * is identical to futexWaitUntil() and returns std::cv_status + */ +// mimic: std::atomic_wait, p1135r0 +template <typename Integer> +void atomic_wait(const std::atomic<Integer>* atomic, Integer expected); +template <typename Integer, typename Clock, typename Duration> +std::cv_status atomic_wait_until( + const std::atomic<Integer>* atomic, + Integer expected, + const std::chrono::time_point<Clock, Duration>& deadline); + +// mimic: std::atomic_notify_one, p1135r0 +template <typename Integer> +void atomic_notify_one(const std::atomic<Integer>* atomic); +// mimic: std::atomic_notify_all, p1135r0 +template <typename Integer> +void atomic_notify_all(const std::atomic<Integer>* atomic); + +// mimic: std::atomic_uint_fast_wait_t, p1135r0 +using atomic_uint_fast_wait_t = std::atomic<std::uint32_t>; + +} // namespace folly + +#include <folly/synchronization/AtomicNotification-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicRef.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicRef.h new file mode 100644 index 000000000..cbc911712 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicRef.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <type_traits> + +#include <folly/Traits.h> + +namespace folly { + +namespace detail { + +template <typename T> +struct atomic_ref_base { + static_assert(sizeof(T) == sizeof(std::atomic<T>), "size mismatch"); + static_assert(alignof(T) == alignof(std::atomic<T>), "alignment mismatch"); + static_assert(is_trivially_copyable_v<T>, "value not trivially-copyable"); + + explicit atomic_ref_base(T& ref) : ref_(ref) {} + atomic_ref_base(atomic_ref_base const&) = default; + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) + const noexcept { + return atomic().store(desired, order); + } + + T load(std::memory_order order = std::memory_order_seq_cst) const noexcept { + return atomic().load(order); + } + + std::atomic<T>& atomic() const noexcept { + return reinterpret_cast<std::atomic<T>&>(ref_); // ub dragons be here + } + + T& ref_; +}; + +template <typename T> +struct atomic_ref_integral_base : atomic_ref_base<T> { + using atomic_ref_base<T>::atomic_ref_base; + using atomic_ref_base<T>::atomic; + + T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) const + noexcept { + return atomic().fetch_add(arg, order); + } + + T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) const + noexcept { + return atomic().fetch_sub(arg, order); + } +}; + +template <typename T> +using atomic_ref_select = conditional_t< + std::is_integral<T>::value, + atomic_ref_integral_base<T>, + atomic_ref_base<T>>; + +} // namespace detail + +// atomic_ref +// +// A very partial backport of std::atomic_ref from C++20, limited for now to +// the common operations on counters for now. May become a complete backport +// in the future. +// +// Relies on the assumption that `T&` is reinterpretable as `std::atomic<T>&`. +// And that the required alignment for that reinterpretation is `alignof(T)`. +// When that is not the case, *kaboom*. +// +// mimic: std::atomic_ref, C++20 +template <typename T> +class atomic_ref : public detail::atomic_ref_select<T> { + private: + using base = detail::atomic_ref_select<T>; + + public: + using base::base; +}; + +struct make_atomic_ref_t { + template < + typename T, + std::enable_if_t< + is_trivially_copyable_v<T> && sizeof(T) == sizeof(std::atomic<T>) && + alignof(T) == alignof(std::atomic<T>), + int> = 0> + atomic_ref<T> operator()(T& ref) const { + return atomic_ref<T>{ref}; + } +}; + +FOLLY_INLINE_VARIABLE constexpr make_atomic_ref_t make_atomic_ref; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicStruct.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicStruct.h new file mode 100644 index 000000000..590d773bf --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicStruct.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <type_traits> + +#include <folly/ConstexprMath.h> +#include <folly/Traits.h> +#include <folly/synchronization/detail/AtomicUtils.h> + +namespace folly { + +namespace detail { +template <size_t> +struct AtomicStructRaw; +template <> +struct AtomicStructRaw<0> { + using type = uint8_t; +}; +template <> +struct AtomicStructRaw<1> { + using type = uint16_t; +}; +template <> +struct AtomicStructRaw<2> { + using type = uint32_t; +}; +template <> +struct AtomicStructRaw<3> { + using type = uint64_t; +}; +} // namespace detail + +/// AtomicStruct<T> work like C++ atomics, but can be used on any POD +/// type <= 8 bytes. +template <typename T, template <typename> class Atom = std::atomic> +class AtomicStruct { + private: + using Raw = _t<detail::AtomicStructRaw<constexpr_log2_ceil(sizeof(T))>>; + + static_assert(alignof(T) <= alignof(Raw), "underlying type is under-aligned"); + static_assert(sizeof(T) <= sizeof(Raw), "underlying type is under-sized"); + static_assert( + std::is_trivial<T>::value || is_trivially_copyable<T>::value, + "target type must be trivially copyable"); + + Atom<Raw> data; + + static Raw encode(T v) noexcept { + // we expect the compiler to optimize away the memcpy, but without + // it we would violate strict aliasing rules + Raw d = 0; + memcpy(&d, static_cast<void*>(&v), sizeof(T)); + return d; + } + + static T decode(Raw d) noexcept { + T v; + memcpy(static_cast<void*>(&v), &d, sizeof(T)); + return v; + } + + public: + AtomicStruct() = default; + ~AtomicStruct() = default; + AtomicStruct(AtomicStruct<T> const&) = delete; + AtomicStruct<T>& operator=(AtomicStruct<T> const&) = delete; + + constexpr /* implicit */ AtomicStruct(T v) noexcept : data(encode(v)) {} + + bool is_lock_free() const noexcept { + return data.is_lock_free(); + } + + bool compare_exchange_strong( + T& v0, + T v1, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_strong( + v0, v1, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_strong( + T& v0, + T v1, + std::memory_order success, + std::memory_order failure) noexcept { + Raw d0 = encode(v0); + bool rv = data.compare_exchange_strong(d0, encode(v1), success, failure); + if (!rv) { + v0 = decode(d0); + } + return rv; + } + + bool compare_exchange_weak( + T& v0, + T v1, + std::memory_order mo = std::memory_order_seq_cst) noexcept { + return compare_exchange_weak( + v0, v1, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_weak( + T& v0, + T v1, + std::memory_order success, + std::memory_order failure) noexcept { + Raw d0 = encode(v0); + bool rv = data.compare_exchange_weak(d0, encode(v1), success, failure); + if (!rv) { + v0 = decode(d0); + } + return rv; + } + + T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { + return decode(data.exchange(encode(v), mo)); + } + + /* implicit */ operator T() const noexcept { + return decode(data); + } + + T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept { + return decode(data.load(mo)); + } + + T operator=(T v) noexcept { + return decode(data = encode(v)); + } + + void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { + data.store(encode(v), mo); + } + + // std::atomic also provides volatile versions of all of the access + // methods. These are callable on volatile objects, and also can + // theoretically have different implementations than their non-volatile + // counterpart. If someone wants them here they can easily be added + // by duplicating the above code and the corresponding unit tests. +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h new file mode 100644 index 000000000..d3af439d7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h @@ -0,0 +1,328 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/Traits.h> + +#include <atomic> +#include <cassert> +#include <cstdint> +#include <tuple> +#include <type_traits> + +#ifdef _WIN32 +#include <intrin.h> +#endif + +namespace folly { + +namespace detail { + +constexpr std::memory_order atomic_compare_exchange_succ( + bool cond, + std::memory_order succ, + std::memory_order fail) { + constexpr auto const relaxed = std::memory_order_relaxed; + constexpr auto const release = std::memory_order_release; + constexpr auto const acq_rel = std::memory_order_acq_rel; + + assert(fail != release); + assert(fail != acq_rel); + + // Clang TSAN ignores the passed failure order and infers failure order from + // success order in atomic compare-exchange operations, which is broken for + // cases like success-release/failure-acquire, so return a success order with + // the failure order mixed in. + auto const bump = succ == release ? acq_rel : succ; + auto const high = fail < bump ? bump : fail; + return !cond || fail == relaxed ? succ : high; +} + +constexpr std::memory_order atomic_compare_exchange_succ( + std::memory_order succ, + std::memory_order fail) { + constexpr auto const cond = kIsSanitizeThread && kIsClang; + return atomic_compare_exchange_succ(cond, succ, fail); +} + +} // namespace detail + +template <typename T> +bool atomic_compare_exchange_weak_explicit( + std::atomic<T>* obj, + T* expected, + T desired, + std::memory_order succ, + std::memory_order fail) { + succ = detail::atomic_compare_exchange_succ(succ, fail); + return std::atomic_compare_exchange_weak_explicit( + obj, expected, desired, succ, fail); +} + +template <typename T> +bool atomic_compare_exchange_strong_explicit( + std::atomic<T>* obj, + T* expected, + T desired, + std::memory_order succ, + std::memory_order fail) { + succ = detail::atomic_compare_exchange_succ(succ, fail); + return std::atomic_compare_exchange_strong_explicit( + obj, expected, desired, succ, fail); +} + +namespace detail { + +// TODO: Remove the non-default implementations when both gcc and clang +// can recognize single bit set/reset patterns and compile them down to locked +// bts and btr instructions. +// +// Currently, at the time of writing it seems like gcc7 and greater can make +// this optimization and clang cannot - https://gcc.godbolt.org/z/Q83rxX + +template <typename Atomic> +bool atomic_fetch_set_default( + Atomic& atomic, + std::size_t bit, + std::memory_order order) { + using Integer = decltype(atomic.load()); + auto mask = Integer{0b1} << static_cast<Integer>(bit); + return (atomic.fetch_or(mask, order) & mask); +} + +template <typename Atomic> +bool atomic_fetch_reset_default( + Atomic& atomic, + std::size_t bit, + std::memory_order order) { + using Integer = decltype(atomic.load()); + auto mask = Integer{0b1} << static_cast<Integer>(bit); + return (atomic.fetch_and(static_cast<Integer>(~mask), order) & mask); +} + +/** + * A simple trait to determine if the given type is an instantiation of + * std::atomic + */ +template <typename T> +constexpr auto is_atomic = false; +template <typename Integer> +constexpr auto is_atomic<std::atomic<Integer>> = true; + +#if FOLLY_X64 + +#if defined(_MSC_VER) + +template <typename Integer> +inline bool atomic_fetch_set_x86( + std::atomic<Integer>& atomic, + std::size_t bit, + std::memory_order order) { + static_assert(alignof(std::atomic<Integer>) == alignof(Integer), ""); + static_assert(sizeof(std::atomic<Integer>) == sizeof(Integer), ""); + assert(atomic.is_lock_free()); + + if /* constexpr */ (sizeof(Integer) == 4) { + return _interlockedbittestandset( + reinterpret_cast<volatile long*>(&atomic), static_cast<long>(bit)); + } else if /* constexpr */ (sizeof(Integer) == 8) { + return _interlockedbittestandset64( + reinterpret_cast<volatile long long*>(&atomic), + static_cast<long long>(bit)); + } else { + assert(sizeof(Integer) != 4 && sizeof(Integer) != 8); + return atomic_fetch_set_default(atomic, bit, order); + } +} + +template <typename Atomic> +inline bool +atomic_fetch_set_x86(Atomic& atomic, std::size_t bit, std::memory_order order) { + static_assert(!std::is_same<Atomic, std::atomic<std::uint32_t>>{}, ""); + static_assert(!std::is_same<Atomic, std::atomic<std::uint64_t>>{}, ""); + return atomic_fetch_set_default(atomic, bit, order); +} + +template <typename Integer> +inline bool atomic_fetch_reset_x86( + std::atomic<Integer>& atomic, + std::size_t bit, + std::memory_order order) { + static_assert(alignof(std::atomic<Integer>) == alignof(Integer), ""); + static_assert(sizeof(std::atomic<Integer>) == sizeof(Integer), ""); + assert(atomic.is_lock_free()); + + if /* constexpr */ (sizeof(Integer) == 4) { + return _interlockedbittestandreset( + reinterpret_cast<volatile long*>(&atomic), static_cast<long>(bit)); + } else if /* constexpr */ (sizeof(Integer) == 8) { + return _interlockedbittestandreset64( + reinterpret_cast<volatile long long*>(&atomic), + static_cast<long long>(bit)); + } else { + assert(sizeof(Integer) != 4 && sizeof(Integer) != 8); + return atomic_fetch_reset_default(atomic, bit, order); + } +} + +template <typename Atomic> +inline bool +atomic_fetch_reset_x86(Atomic& atomic, std::size_t bit, std::memory_order mo) { + static_assert(!std::is_same<Atomic, std::atomic<std::uint32_t>>{}, ""); + static_assert(!std::is_same<Atomic, std::atomic<std::uint64_t>>{}, ""); + return atomic_fetch_reset_default(atomic, bit, mo); +} + +#else + +template <typename Integer> +inline bool atomic_fetch_set_x86( + std::atomic<Integer>& atomic, + std::size_t bit, + std::memory_order order) { + auto previous = false; + + if /* constexpr */ (sizeof(Integer) == 2) { + auto pointer = reinterpret_cast<std::uint16_t*>(&atomic); + asm volatile("lock; btsw %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint16_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else if /* constexpr */ (sizeof(Integer) == 4) { + auto pointer = reinterpret_cast<std::uint32_t*>(&atomic); + asm volatile("lock; btsl %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint32_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else if /* constexpr */ (sizeof(Integer) == 8) { + auto pointer = reinterpret_cast<std::uint64_t*>(&atomic); + asm volatile("lock; btsq %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint64_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else { + assert(sizeof(Integer) == 1); + return atomic_fetch_set_default(atomic, bit, order); + } + + return previous; +} + +template <typename Atomic> +inline bool +atomic_fetch_set_x86(Atomic& atomic, std::size_t bit, std::memory_order order) { + static_assert(!is_atomic<Atomic>, ""); + return atomic_fetch_set_default(atomic, bit, order); +} + +template <typename Integer> +inline bool atomic_fetch_reset_x86( + std::atomic<Integer>& atomic, + std::size_t bit, + std::memory_order order) { + auto previous = false; + + if /* constexpr */ (sizeof(Integer) == 2) { + auto pointer = reinterpret_cast<std::uint16_t*>(&atomic); + asm volatile("lock; btrw %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint16_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else if /* constexpr */ (sizeof(Integer) == 4) { + auto pointer = reinterpret_cast<std::uint32_t*>(&atomic); + asm volatile("lock; btrl %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint32_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else if /* constexpr */ (sizeof(Integer) == 8) { + auto pointer = reinterpret_cast<std::uint64_t*>(&atomic); + asm volatile("lock; btrq %1, (%2); setc %0" + : "=r"(previous) + : "ri"(static_cast<std::uint64_t>(bit)), "r"(pointer) + : "memory", "flags"); + } else { + assert(sizeof(Integer) == 1); + return atomic_fetch_reset_default(atomic, bit, order); + } + + return previous; +} + +template <typename Atomic> +bool atomic_fetch_reset_x86( + Atomic& atomic, + std::size_t bit, + std::memory_order order) { + static_assert(!is_atomic<Atomic>, ""); + return atomic_fetch_reset_default(atomic, bit, order); +} + +#endif + +#else + +template <typename Atomic> +bool atomic_fetch_set_x86(Atomic&, std::size_t, std::memory_order) noexcept { + // This should never be called on non x86_64 platforms. + std::terminate(); +} +template <typename Atomic> +bool atomic_fetch_reset_x86(Atomic&, std::size_t, std::memory_order) noexcept { + // This should never be called on non x86_64 platforms. + std::terminate(); +} + +#endif + +} // namespace detail + +template <typename Atomic> +bool atomic_fetch_set(Atomic& atomic, std::size_t bit, std::memory_order mo) { + using Integer = decltype(atomic.load()); + static_assert(std::is_unsigned<Integer>{}, ""); + static_assert(!std::is_const<Atomic>{}, ""); + assert(bit < (sizeof(Integer) * 8)); + + // do the optimized thing on x86 builds. Also, some versions of TSAN do not + // properly instrument the inline assembly, so avoid it when TSAN is enabled + if (folly::kIsArchAmd64 && !folly::kIsSanitizeThread) { + return detail::atomic_fetch_set_x86(atomic, bit, mo); + } else { + // otherwise default to the default implementation using fetch_or() + return detail::atomic_fetch_set_default(atomic, bit, mo); + } +} + +template <typename Atomic> +bool atomic_fetch_reset(Atomic& atomic, std::size_t bit, std::memory_order mo) { + using Integer = decltype(atomic.load()); + static_assert(std::is_unsigned<Integer>{}, ""); + static_assert(!std::is_const<Atomic>{}, ""); + assert(bit < (sizeof(Integer) * 8)); + + // do the optimized thing on x86 builds. Also, some versions of TSAN do not + // properly instrument the inline assembly, so avoid it when TSAN is enabled + if (folly::kIsArchAmd64 && !folly::kIsSanitizeThread) { + return detail::atomic_fetch_reset_x86(atomic, bit, mo); + } else { + // otherwise default to the default implementation using fetch_and() + return detail::atomic_fetch_reset_default(atomic, bit, mo); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil.h b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil.h new file mode 100644 index 000000000..91684560c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/AtomicUtil.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstdint> + +namespace folly { + +// atomic_compare_exchange_weak_explicit +// +// Fix TSAN bug in std::atomic_compare_exchange_weak_explicit. +// Workaround for https://github.com/google/sanitizers/issues/970. +// +// mimic: std::atomic_compare_exchange_weak +template <typename T> +bool atomic_compare_exchange_weak_explicit( + std::atomic<T>* obj, + T* expected, + T desired, + std::memory_order succ, + std::memory_order fail); + +// atomic_compare_exchange_strong_explicit +// +// Fix TSAN bug in std::atomic_compare_exchange_strong_explicit. +// Workaround for https://github.com/google/sanitizers/issues/970. +// +// mimic: std::atomic_compare_exchange_strong +template <typename T> +bool atomic_compare_exchange_strong_explicit( + std::atomic<T>* obj, + T* expected, + T desired, + std::memory_order succ, + std::memory_order fail); + +// atomic_fetch_set +// +// Sets the bit at the given index in the binary representation of the integer +// to 1. Returns the previous value of the bit, which is equivalent to whether +// that bit is unchanged. +// +// Equivalent to Atomic::fetch_or with a mask. For example, if the bit +// argument to this function is 1, the mask passed to the corresponding +// Atomic::fetch_or would be 0b1. +// +// Uses an optimized implementation when available, otherwise falling back to +// Atomic::fetch_or with mask. The optimization is currently available for +// std::atomic on x86, using the bts instruction. +template <typename Atomic> +bool atomic_fetch_set( + Atomic& atomic, + std::size_t bit, + std::memory_order order = std::memory_order_seq_cst); + +// atomic_fetch_reset +// +// Resets the bit at the given index in the binary representation of the +// integer to 0. Returns the previous value of the bit, which is equivalent to +// whether that bit is changed. +// +// Equivalent to Atomic::fetch_and with a mask. For example, if the bit +// argument to this function is 1, the mask passed to the corresponding +// Atomic::fetch_and would be ~0b1. +// +// Uses an optimized implementation when available, otherwise falling back to +// Atomic::fetch_and with mask. The optimization is currently available for +// std::atomic on x86, using the btr instruction. +template <typename Atomic> +bool atomic_fetch_reset( + Atomic& atomic, + std::size_t bit, + std::memory_order order = std::memory_order_seq_cst); + +} // namespace folly + +#include <folly/synchronization/AtomicUtil-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Baton.h b/ios/Pods/Flipper-Folly/folly/synchronization/Baton.h new file mode 100644 index 000000000..9c342d5cc --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Baton.h @@ -0,0 +1,348 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <atomic> +#include <thread> + +#include <folly/Likely.h> +#include <folly/detail/AsyncTrace.h> +#include <folly/detail/Futex.h> +#include <folly/detail/MemoryIdler.h> +#include <folly/portability/Asm.h> +#include <folly/synchronization/WaitOptions.h> +#include <folly/synchronization/detail/Spin.h> + +namespace folly { + +/// A Baton allows a thread to block once and be awoken. Captures a +/// single handoff, and during its lifecycle (from construction/reset +/// to destruction/reset) a baton must either be post()ed and wait()ed +/// exactly once each, or not at all. +/// +/// Baton includes no internal padding, and is only 4 bytes in size. +/// Any alignment or padding to avoid false sharing is up to the user. +/// +/// This is basically a stripped-down semaphore that supports only a +/// single call to sem_post and a single call to sem_wait. +/// +/// The non-blocking version (MayBlock == false) provides more speed +/// by using only load acquire and store release operations in the +/// critical path, at the cost of disallowing blocking. +/// +/// The current posix semaphore sem_t isn't too bad, but this provides +/// more a bit more speed, inlining, smaller size, a guarantee that +/// the implementation won't change, and compatibility with +/// DeterministicSchedule. By having a much more restrictive +/// lifecycle we can also add a bunch of assertions that can help to +/// catch race conditions ahead of time. +template <bool MayBlock = true, template <typename> class Atom = std::atomic> +class Baton { + public: + FOLLY_ALWAYS_INLINE static constexpr WaitOptions wait_options() { + return {}; + } + + constexpr Baton() noexcept : state_(INIT) {} + + Baton(Baton const&) = delete; + Baton& operator=(Baton const&) = delete; + + /// It is an error to destroy a Baton on which a thread is currently + /// wait()ing. In practice this means that the waiter usually takes + /// responsibility for destroying the Baton. + ~Baton() noexcept { + // The docblock for this function says that it can't be called when + // there is a concurrent waiter. We assume a strong version of this + // requirement in which the caller must _know_ that this is true, they + // are not allowed to be merely lucky. If two threads are involved, + // the destroying thread must actually have synchronized with the + // waiting thread after wait() returned. To convey causality the the + // waiting thread must have used release semantics and the destroying + // thread must have used acquire semantics for that communication, + // so we are guaranteed to see the post-wait() value of state_, + // which cannot be WAITING. + // + // Note that since we only care about a single memory location, + // the only two plausible memory orders here are relaxed and seq_cst. + assert(state_.load(std::memory_order_relaxed) != WAITING); + } + + FOLLY_ALWAYS_INLINE bool ready() const noexcept { + auto s = state_.load(std::memory_order_acquire); + assert(s == INIT || s == EARLY_DELIVERY); + return LIKELY(s == EARLY_DELIVERY); + } + + /// Equivalent to destroying the Baton and creating a new one. It is + /// a bug to call this while there is a waiting thread, so in practice + /// the waiter will be the one that resets the baton. + void reset() noexcept { + // See ~Baton for a discussion about why relaxed is okay here + assert(state_.load(std::memory_order_relaxed) != WAITING); + + // We use a similar argument to justify the use of a relaxed store + // here. Since both wait() and post() are required to be called + // only once per lifetime, no thread can actually call those methods + // correctly after a reset() unless it synchronizes with the thread + // that performed the reset(). If a post() or wait() on another thread + // didn't synchronize, then regardless of what operation we performed + // here there would be a race on proper use of the Baton's spec + // (although not on any particular load and store). Put another way, + // we don't need to synchronize here because anybody that might rely + // on such synchronization is required by the baton rules to perform + // an additional synchronization that has the desired effect anyway. + // + // There is actually a similar argument to be made about the + // constructor, in which the fenceless constructor initialization + // of state_ is piggybacked on whatever synchronization mechanism + // distributes knowledge of the Baton's existence + state_.store(INIT, std::memory_order_relaxed); + } + + /// Causes wait() to wake up. For each lifetime of a Baton (where a + /// lifetime starts at construction or reset() and ends at + /// destruction or reset()) there can be at most one call to post(), + /// in the single poster version. Any thread may call post(). + void post() noexcept { + if (!MayBlock) { + /// Spin-only version + /// + assert( + ((1 << state_.load(std::memory_order_relaxed)) & + ((1 << INIT) | (1 << EARLY_DELIVERY))) != 0); + state_.store(EARLY_DELIVERY, std::memory_order_release); + return; + } + + /// May-block versions + /// + uint32_t before = state_.load(std::memory_order_acquire); + + assert(before == INIT || before == WAITING || before == TIMED_OUT); + + if (before == INIT && + state_.compare_exchange_strong( + before, + EARLY_DELIVERY, + std::memory_order_release, + std::memory_order_relaxed)) { + return; + } + + assert(before == WAITING || before == TIMED_OUT); + + if (before == TIMED_OUT) { + return; + } + + assert(before == WAITING); + state_.store(LATE_DELIVERY, std::memory_order_release); + detail::futexWake(&state_, 1); + } + + /// Waits until post() has been called in the current Baton lifetime. + /// May be called at most once during a Baton lifetime (construction + /// |reset until destruction|reset). If post is called before wait in + /// the current lifetime then this method returns immediately. + /// + /// The restriction that there can be at most one wait() per lifetime + /// could be relaxed somewhat without any perf or size regressions, + /// but by making this condition very restrictive we can provide better + /// checking in debug builds. + FOLLY_ALWAYS_INLINE + void wait(const WaitOptions& opt = wait_options()) noexcept { + if (try_wait()) { + return; + } + + auto const deadline = std::chrono::steady_clock::time_point::max(); + tryWaitSlow(deadline, opt); + } + + /// Similar to wait, but doesn't block the thread if it hasn't been posted. + /// + /// try_wait has the following semantics: + /// - It is ok to call try_wait any number times on the same baton until + /// try_wait reports that the baton has been posted. + /// - It is ok to call timed_wait or wait on the same baton if try_wait + /// reports that baton hasn't been posted. + /// - If try_wait indicates that the baton has been posted, it is invalid to + /// call wait, try_wait or timed_wait on the same baton without resetting + /// + /// @return true if baton has been posted, false othewise + FOLLY_ALWAYS_INLINE bool try_wait() const noexcept { + return ready(); + } + + /// Similar to wait, but with a timeout. The thread is unblocked if the + /// timeout expires. + /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed + /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other + /// words, after try_wait_for the caller can't invoke + /// wait/try_wait/try_wait_for/try_wait_until + /// again on the same baton without resetting it. + /// + /// @param timeout Time until which the thread can block + /// @return true if the baton was posted to before timeout, + /// false otherwise + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool try_wait_for( + const std::chrono::duration<Rep, Period>& timeout, + const WaitOptions& opt = wait_options()) noexcept { + if (try_wait()) { + return true; + } + + auto const deadline = std::chrono::steady_clock::now() + timeout; + return tryWaitSlow(deadline, opt); + } + + /// Similar to wait, but with a deadline. The thread is unblocked if the + /// deadline expires. + /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed + /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other + /// words, after try_wait_until the caller can't invoke + /// wait/try_wait/try_wait_for/try_wait_until + /// again on the same baton without resetting it. + /// + /// @param deadline Time until which the thread can block + /// @return true if the baton was posted to before deadline, + /// false otherwise + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_wait_until( + const std::chrono::time_point<Clock, Duration>& deadline, + const WaitOptions& opt = wait_options()) noexcept { + if (try_wait()) { + return true; + } + + return tryWaitSlow(deadline, opt); + } + + /// Alias to try_wait_for. Deprecated. + template <typename Rep, typename Period> + FOLLY_ALWAYS_INLINE bool timed_wait( + const std::chrono::duration<Rep, Period>& timeout) noexcept { + return try_wait_for(timeout); + } + + /// Alias to try_wait_until. Deprecated. + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool timed_wait( + const std::chrono::time_point<Clock, Duration>& deadline) noexcept { + return try_wait_until(deadline); + } + + private: + enum State : uint32_t { + INIT = 0, + EARLY_DELIVERY = 1, + WAITING = 2, + LATE_DELIVERY = 3, + TIMED_OUT = 4, + }; + + template <typename Clock, typename Duration> + FOLLY_NOINLINE bool tryWaitSlow( + const std::chrono::time_point<Clock, Duration>& deadline, + const WaitOptions& opt) noexcept { + if (opt.logging_enabled()) { + folly::async_tracing::logBlockingOperation( + std::chrono::duration_cast<std::chrono::milliseconds>( + deadline - Clock::now())); + } + + switch (detail::spin_pause_until(deadline, opt, [=] { return ready(); })) { + case detail::spin_result::success: + return true; + case detail::spin_result::timeout: + return false; + case detail::spin_result::advance: + break; + } + + if (!MayBlock) { + switch (detail::spin_yield_until(deadline, [=] { return ready(); })) { + case detail::spin_result::success: + return true; + case detail::spin_result::timeout: + return false; + case detail::spin_result::advance: + break; + } + } + + // guess we have to block :( + uint32_t expected = INIT; + if (!state_.compare_exchange_strong( + expected, + WAITING, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + // CAS failed, last minute reprieve + assert(expected == EARLY_DELIVERY); + // TODO: move the acquire to the compare_exchange failure load after C++17 + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + + while (true) { + auto rv = detail::MemoryIdler::futexWaitUntil(state_, WAITING, deadline); + + // Awoken by the deadline passing. + if (rv == detail::FutexResult::TIMEDOUT) { + assert(deadline != (std::chrono::time_point<Clock, Duration>::max())); + state_.store(TIMED_OUT, std::memory_order_release); + return false; + } + + // Probably awoken by a matching wake event, but could also by awoken + // by an asynchronous signal or by a spurious wakeup. + // + // state_ is the truth even if FUTEX_WAIT reported a matching + // FUTEX_WAKE, since we aren't using type-stable storage and we + // don't guarantee reuse. The scenario goes like this: thread + // A's last touch of a Baton is a call to wake(), which stores + // LATE_DELIVERY and gets an unlucky context switch before delivering + // the corresponding futexWake. Thread B sees LATE_DELIVERY + // without consuming a futex event, because it calls futexWait + // with an expected value of WAITING and hence doesn't go to sleep. + // B returns, so the Baton's memory is reused and becomes another + // Baton (or a reuse of this one). B calls futexWait on the new + // Baton lifetime, then A wakes up and delivers a spurious futexWake + // to the same memory location. B's futexWait will then report a + // consumed wake event even though state_ is still WAITING. + // + // It would be possible to add an extra state_ dance to communicate + // that the futexWake has been sent so that we can be sure to consume + // it before returning, but that would be a perf and complexity hit. + uint32_t s = state_.load(std::memory_order_acquire); + assert(s == WAITING || s == LATE_DELIVERY); + if (s == LATE_DELIVERY) { + return true; + } + } + } + + detail::Futex<Atom> state_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/CallOnce.h b/ios/Pods/Flipper-Folly/folly/synchronization/CallOnce.h new file mode 100644 index 000000000..9e362aa4e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/CallOnce.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <mutex> +#include <utility> + +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/SharedMutex.h> +#include <folly/functional/Invoke.h> + +namespace folly { + +template <typename Mutex, template <typename> class Atom = std::atomic> +class basic_once_flag; + +// call_once +// +// Drop-in replacement for std::call_once. +// +// The libstdc++ implementation has two flaws: +// * it lacks a fast path, and +// * it deadlocks (in explicit violation of the standard) when invoked twice +// with a given flag, and the callable passed to the first invocation throws. +// +// This implementation corrects both flaws. +// +// The tradeoff is a slightly larger once_flag struct at 8 bytes, vs 4 bytes +// with libstdc++ on Linux/x64. +// +// Does not work with std::once_flag. +// +// mimic: std::call_once +template < + typename Mutex, + template <typename> class Atom, + typename F, + typename... Args> +FOLLY_ALWAYS_INLINE void +call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) { + flag.call_once(std::forward<F>(f), std::forward<Args>(args)...); +} + +// try_call_once +// +// Like call_once, but using a boolean return type to signal pass/fail rather +// than throwing exceptions. +// +// Returns true if any previous call to try_call_once with the same once_flag +// has returned true or if any previous call to call_once with the same +// once_flag has completed without throwing an exception or if the function +// passed as an argument returns true; otherwise returns false. +// +// Note: This has no parallel in the std::once_flag interface. +template < + typename Mutex, + template <typename> class Atom, + typename F, + typename... Args> +FOLLY_NODISCARD FOLLY_ALWAYS_INLINE bool try_call_once( + basic_once_flag<Mutex, Atom>& flag, + F&& f, + Args&&... args) noexcept { + static_assert(is_nothrow_invocable_v<F, Args...>, "must be noexcept"); + return flag.try_call_once(std::forward<F>(f), std::forward<Args>(args)...); +} + +// test_once +// +// Tests whether any invocation to call_once with the given flag has succeeded. +// +// May help with space usage in certain esoteric scenarios compared with caller +// code tracking a separate and possibly-padded bool. +// +// Note: This has no parallel in the std::once_flag interface. +template <typename Mutex, template <typename> class Atom> +FOLLY_ALWAYS_INLINE bool test_once( + basic_once_flag<Mutex, Atom> const& flag) noexcept { + return flag.called_.load(std::memory_order_acquire); +} + +// basic_once_flag +// +// The flag template to be used with call_once. Parameterizable by the mutex +// type and atomic template. The mutex type is required to mimic std::mutex and +// the atomic type is required to mimic std::atomic. +template <typename Mutex, template <typename> class Atom> +class basic_once_flag { + public: + constexpr basic_once_flag() noexcept = default; + basic_once_flag(const basic_once_flag&) = delete; + basic_once_flag& operator=(const basic_once_flag&) = delete; + + private: + template < + typename Mutex_, + template <typename> class Atom_, + typename F, + typename... Args> + friend void call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...); + + template <typename Mutex_, template <typename> class Atom_> + friend bool test_once(basic_once_flag<Mutex_, Atom_> const& flag) noexcept; + + template <typename F, typename... Args> + FOLLY_ALWAYS_INLINE void call_once(F&& f, Args&&... args) { + if (LIKELY(called_.load(std::memory_order_acquire))) { + return; + } + call_once_slow(std::forward<F>(f), std::forward<Args>(args)...); + } + + template <typename F, typename... Args> + FOLLY_NOINLINE void call_once_slow(F&& f, Args&&... args) { + std::lock_guard<Mutex> lock(mutex_); + if (called_.load(std::memory_order_relaxed)) { + return; + } + invoke(std::forward<F>(f), std::forward<Args>(args)...); + called_.store(true, std::memory_order_release); + } + + template < + typename Mutex_, + template <typename> class Atom_, + typename F, + typename... Args> + friend bool + try_call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...) noexcept; + + template <typename F, typename... Args> + FOLLY_ALWAYS_INLINE bool try_call_once(F&& f, Args&&... args) noexcept { + if (LIKELY(called_.load(std::memory_order_acquire))) { + return true; + } + return try_call_once_slow(std::forward<F>(f), std::forward<Args>(args)...); + } + + template <typename F, typename... Args> + FOLLY_NOINLINE bool try_call_once_slow(F&& f, Args&&... args) noexcept { + std::lock_guard<Mutex> lock(mutex_); + if (called_.load(std::memory_order_relaxed)) { + return true; + } + auto const pass = invoke(std::forward<F>(f), std::forward<Args>(args)...); + called_.store(pass, std::memory_order_release); + return pass; + } + + Atom<bool> called_{false}; + Mutex mutex_; +}; + +// once_flag +// +// The flag type to be used with call_once. An instance of basic_once_flag. +// +// Does not work with sd::call_once. +// +// mimic: std::once_flag +using once_flag = basic_once_flag<SharedMutex>; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h new file mode 100644 index 000000000..d23da411b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h @@ -0,0 +1,1729 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/DistributedMutex.h> + +#include <folly/ConstexprMath.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/ScopeGuard.h> +#include <folly/Utility.h> +#include <folly/chrono/Hardware.h> +#include <folly/detail/Futex.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Align.h> +#include <folly/lang/Bits.h> +#include <folly/portability/Asm.h> +#include <folly/synchronization/AtomicNotification.h> +#include <folly/synchronization/AtomicUtil.h> +#include <folly/synchronization/detail/InlineFunctionRef.h> +#include <folly/synchronization/detail/Sleeper.h> + +#include <glog/logging.h> + +#include <array> +#include <atomic> +#include <cstdint> +#include <limits> +#include <stdexcept> +#include <thread> +#include <utility> + +namespace folly { +namespace detail { +namespace distributed_mutex { +// kUnlocked is used to show unlocked state +// +// When locking threads encounter kUnlocked in the underlying storage, they +// can just acquire the lock without any further effort +constexpr auto kUnlocked = std::uintptr_t{0b0}; +// kLocked is used to show that the mutex is currently locked, and future +// attempts to lock the mutex should enqueue on the central storage +// +// Locking threads find this on central storage only when there is a +// contention chain that is undergoing wakeups, in every other case, a locker +// will either find kUnlocked or an arbitrary address with the kLocked bit set +constexpr auto kLocked = std::uintptr_t{0b1}; +// kTimedWaiter is set when there is at least one timed waiter on the mutex +// +// Timed waiters do not follow the sleeping strategy employed by regular, +// non-timed threads. They sleep on the central mutex atomic through an +// extended futex() interface that allows sleeping with the same semantics for +// non-standard integer widths +// +// When a regular non-timed thread unlocks or enqueues on the mutex, and sees +// a timed waiter, it takes ownership of all the timed waiters. The thread +// that has taken ownership of the timed waiter releases the timed waiters +// when it gets a chance at the critical section. At which point it issues a +// wakeup to single timed waiter, timed waiters always issue wake() calls to +// other timed waiters +constexpr auto kTimedWaiter = std::uintptr_t{0b10}; + +// kUninitialized means that the thread has just enqueued, and has not yet +// gotten to initializing itself with the address of its successor +// +// this becomes significant for threads that are trying to wake up the +// uninitialized thread, if they see that the thread is not yet initialized, +// they can do nothing but spin, and wait for the thread to get initialized +// +// This also plays a role in the functioning of flat combining as implemented +// in DistributedMutex. When a thread owning the lock goes through the +// contention chain to either unlock the mutex or combine critical sections +// from the other end. The presence of kUninitialized means that the +// combining thread is not able to make progress after this point. So we +// transfer the lock. +constexpr auto kUninitialized = std::uint32_t{0b0}; +// kWaiting will be set in the waiter's futex structs while they are spinning +// while waiting for the mutex +constexpr auto kWaiting = std::uint32_t{0b1}; +// kWake will be set by threads that are waking up waiters that have enqueued +constexpr auto kWake = std::uint32_t{0b10}; +// kSkipped will be set by a waker when they see that a waiter has been +// preempted away by the kernel, in this case the thread that got skipped will +// have to wake up and put itself back on the queue +constexpr auto kSkipped = std::uint32_t{0b11}; +// kAboutToWait will be set by a waiter that enqueues itself with the purpose +// of waiting on a futex +constexpr auto kAboutToWait = std::uint32_t{0b100}; +// kSleeping will be set by a waiter right before enqueueing on a futex. When +// a thread wants to wake up a waiter that has enqueued on a futex, it should +// set the futex to contain kWake +// +// a thread that is unlocking and wants to skip over a sleeping thread also +// calls futex_.exchange(kSleeping) on the sleeping thread's futex word. It +// does this to 1. detect whether the sleeping thread had actually gone to +// sleeping on the futex word so it can skip it, and 2. to synchronize with +// other non atomic writes in the sleeping thread's context (such as the write +// to track the next waiting thread). +// +// We reuse kSleeping instead of say using another constant kEarlyDelivery to +// avoid situations where a thread has to enter kernel mode due to calling +// futexWait() twice because of the presence of a waking thread. This +// situation can arise when an unlocking thread goes to skip over a sleeping +// thread, sees that the thread has slept and move on, but the sleeping thread +// had not yet entered futex(). This interleaving causes the thread calling +// futex() to return spuriously, as the futex word is not what it should be +constexpr auto kSleeping = std::uint32_t{0b101}; +// kCombined is set by the lock holder to let the waiter thread know that its +// combine request was successfully completed by the lock holder. A +// successful combine means that the thread requesting the combine operation +// does not need to unlock the mutex; in fact, doing so would be an error. +constexpr auto kCombined = std::uint32_t{0b111}; +// kCombineUninitialized is like kUninitialized but is set by a thread when it +// enqueues in hopes of getting its critical section combined with the lock +// holder +constexpr auto kCombineUninitialized = std::uint32_t{0b1000}; +// kCombineWaiting is set by a thread when it is ready to have its combine +// record fulfilled by the lock holder. In particular, this signals to the +// lock holder that the thread has set its next_ pointer in the contention +// chain +constexpr auto kCombineWaiting = std::uint32_t{0b1001}; +// kExceptionOccurred is set on the waiter futex when the remote task throws +// an exception. It is the caller's responsibility to retrieve the exception +// and rethrow it in their own context. Note that when the caller uses a +// noexcept function as their critical section, they can avoid checking for +// this value +// +// This allows us to avoid all cost of exceptions in the memory layout of the +// fast path (no errors) as exceptions are stored as an std::exception_ptr in +// the same union that stores the return value of the critical section. We +// also avoid all CPU overhead because the combiner uses a try-catch block +// without any additional branching to handle exceptions +constexpr auto kExceptionOccurred = std::uint32_t{0b1010}; + +// The number of spins that we are allowed to do before we resort to marking a +// thread as having slept +// +// This is just a magic number from benchmarks +constexpr auto kScheduledAwaySpinThreshold = std::chrono::nanoseconds{200}; +// The maximum number of spins before a thread starts yielding its processor +// in hopes of getting skipped +constexpr auto kMaxSpins = 4000; +// The maximum number of contention chains we can resolve with flat combining. +// After this number of contention chains, the mutex falls back to regular +// two-phased mutual exclusion to ensure that we don't starve the combiner +// thread +constexpr auto kMaxCombineIterations = 2; + +/** + * Write only data that is available to the thread that is waking up another. + * Only the waking thread is allowed to write to this, the thread to be woken + * is allowed to read from this after a wakeup has been issued + */ +template <template <typename> class Atomic> +class WakerMetadata { + public: + // This is the thread that initiated wakeups for the contention chain. + // There can only ever be one thread that initiates the wakeup for a + // chain in the spin only version of this mutex. When a thread that just + // woke up sees this as the next thread to wake up, it knows that it is the + // terminal node in the contention chain. This means that it was the one + // that took off the thread that had acquired the mutex off the centralized + // state. Therefore, the current thread is the last in its contention + // chain. It will fall back to centralized storage to pick up the next + // waiter or release the mutex + // + // When we move to a full sleeping implementation, this might need to change + // to a small_vector<> to account for failed wakeups, or we can put threads + // to sleep on the central futex, which is an easier implementation + // strategy. Although, since this is allocated on the stack, we can set a + // prohitively large threshold to avoid heap allocations, this strategy + // however, might cause increased cache misses on wakeup signalling + std::uintptr_t waker_{0}; + // the list of threads that the waker had previously seen to be sleeping on + // a futex(), + // + // this is given to the current thread as a means to pass on + // information. When the current thread goes to unlock the mutex and does + // not see contention, it should go and wake up the head of this list. If + // the current thread sees a contention chain on the mutex, it should pass + // on this list to the next thread that gets woken up + std::uintptr_t waiters_{0}; + // The futex that this waiter will sleep on + // + // how can we reuse futex_ from above for futex management? + Futex<Atomic> sleeper_{kUninitialized}; +}; + +/** + * Type of the type-erased callable that is used for combining from the lock + * holder's end. This has 48 bytes of inline storage that can be used to + * minimize cache misses when combining + */ +using CombineFunction = detail::InlineFunctionRef<void(), 48>; + +/** + * Waiter encapsulates the state required for waiting on the mutex, this + * contains potentially heavy state and is intended to be allocated on the + * stack as part of a lock() function call + * + * To ensure that synchronization does not cause unintended side effects on + * the rest of the thread stack (eg. metadata in lockImplementation(), or any + * other data in the user's thread), we aggresively pad this struct and use + * custom alignment internally to ensure that the relevant data fits within a + * single cacheline. The added alignment here also gives us some room to + * wiggle in the bottom few bits of the mutex, where we store extra metadata + */ +template <template <typename> class Atomic> +class Waiter { + public: + Waiter() {} + Waiter(Waiter&&) = delete; + Waiter(const Waiter&) = delete; + Waiter& operator=(Waiter&&) = delete; + Waiter& operator=(const Waiter&) = delete; + + void initialize(std::uint64_t futex, CombineFunction task) { + // we only initialize the function if we were actually given a non-null + // task, otherwise + if (task) { + DCHECK_EQ(futex, kCombineUninitialized); + new (&function_) CombineFunction{task}; + } else { + DCHECK((futex == kUninitialized) || (futex == kAboutToWait)); + new (&metadata_) WakerMetadata<Atomic>{}; + } + + // this pedantic store is needed to ensure that the waking thread + // synchronizes with the state in the waiter struct when it loads the + // value of the futex word + // + // on x86, this gets optimized away to just a regular store, it might be + // needed on platforms where explicit acquire-release barriers are + // required for synchronization + // + // note that we release here at the end of the constructor because + // construction is complete here, any thread that acquires this release + // will see a well constructed wait node + futex_.store(futex, std::memory_order_release); + } + + std::array<std::uint8_t, hardware_destructive_interference_size> padding1; + // the atomic that this thread will spin on while waiting for the mutex to + // be unlocked + alignas(hardware_destructive_interference_size) Atomic<std::uint64_t> futex_{ + kUninitialized}; + // The successor of this node. This will be the thread that had its address + // on the mutex previously + // + // We can do without making this atomic since the remote thread synchronizes + // on the futex variable above. If this were not atomic, the remote thread + // would only be allowed to read from it after the waiter has moved into the + // waiting state to avoid risk of a load racing with a write. However, it + // helps to make this atomic because we can use an unconditional load and make + // full use of the load buffer to coalesce both reads into a single clock + // cycle after the line arrives in the combiner core. This is a heavily + // contended line, so an RFO from the enqueueing thread is highly likely and + // has the potential to cause an immediate invalidation; blocking the combiner + // thread from making progress until the line is pulled back to read this + // value + // + // Further, making this atomic prevents the compiler from making an incorrect + // optimization where it does not load the value as written in the code, but + // rather dereferences it through a pointer whenever needed (since the value + // of the pointer to this is readily available on the stack). Doing this + // causes multiple invalidation requests from the enqueueing thread, blocking + // remote progress + // + // Note that we use relaxed loads and stores, so this should not have any + // additional overhead compared to a regular load on most architectures + std::atomic<std::uintptr_t> next_{0}; + // We use an anonymous union for the combined critical section request and + // the metadata that will be filled in from the leader's end. Only one is + // active at a time - if a leader decides to combine the requested critical + // section into its execution, it will not touch the metadata field. If a + // leader decides to migrate the lock to the waiter, it will not touch the + // function + // + // this allows us to transfer more state when combining a critical section + // and reduce the cache misses originating from executing an arbitrary + // lambda + // + // note that this is an anonymous union, not an unnamed union, the members + // leak into the surrounding scope + union { + // metadata for the waker + WakerMetadata<Atomic> metadata_; + // The critical section that can potentially be combined into the critical + // section of the locking thread + // + // This is kept as a FunctionRef because the original function is preserved + // until the lock_combine() function returns. A consequence of using + // FunctionRef here is that we don't need to do any allocations and can + // allow users to capture unbounded state into the critical section. Flat + // combining means that the user does not have access to the thread + // executing the critical section, so assumptions about thread local + // references can be invalidated. Being able to capture arbitrary state + // allows the user to do thread local accesses right before the critical + // section and pass them as state to the callable being referenced here + CombineFunction function_; + // The user is allowed to use a combined critical section that returns a + // value. This buffer is used to implement the value transfer to the + // waiting thread. We reuse the same union because this helps us combine + // one synchronization operation with a material value transfer. + // + // The waker thread needs to synchronize on this cacheline to issue a + // wakeup to the waiter, meaning that the entire line needs to be pulled + // into the remote core in exclusive mode. So we reuse the coherence + // operation to transfer the return value in addition to the + // synchronization signal. In the case that the user's data item is + // small, the data is transferred all inline as part of the same line, + // which pretty much arrives into the CPU cache in the same clock cycle or + // two after a read-for-ownership request. This gives us a high chance of + // coalescing the entire transitive store buffer together into one cache + // coherence operation from the waker's end. This allows us to make use + // of the CPU bus bandwidth which would have otherwise gone to waste. + // Benchmarks prove this theory under a wide range of contention, value + // sizes, NUMA interactions and processor models + // + // The current version of the Intel optimization manual confirms this + // theory somewhat as well in section 2.3.5.1 (Load and Store Operation + // Overview) + // + // When an instruction writes data to a memory location [...], the + // processor ensures that it has the line containing this memory location + // is in its L1d cache [...]. If the cache line is not there, it fetches + // from the next levels using a RFO request [...] RFO and storing the + // data happens after instruction retirement. Therefore, the store + // latency usually does not affect the store instruction itself + // + // This gives the user the ability to input up to 48 bytes into the + // combined critical section through an InlineFunctionRef and output 48 + // bytes from it basically without any cost. The type of the entity + // stored in the buffer has to be matched by the type erased callable that + // the caller has used. At this point, the caller is still in the + // template instantiation leading to the combine request, so it has + // knowledge of the return type and can apply the appropriate + // reinterpret_cast and launder operation to safely retrieve the data from + // this buffer + std::aligned_storage_t<48, 8> storage_; + }; + std::array<std::uint8_t, hardware_destructive_interference_size> padding2; +}; + +/** + * A template that helps us differentiate between the different ways to return + * a value from a combined critical section. A return value of type void + * cannot be stored anywhere, so we use specializations and pick the right one + * switched through std::conditional_t + * + * This is then used by CoalescedTask and its family of functions to implement + * efficient return value transfers to the waiting threads + */ +template <typename Func> +class RequestWithReturn { + public: + using F = Func; + using ReturnType = folly::invoke_result_t<const Func&>; + explicit RequestWithReturn(Func func) : func_{std::move(func)} {} + + /** + * We need to define the destructor here because C++ requires (with good + * reason) that a union with non-default destructor be explicitly destroyed + * from the surrounding class, as neither the runtime nor compiler have the + * knowledge of what to do with a union at the time of destruction + * + * Each request that has a valid return value set will have the value + * retrieved from the get() method, where the value is destroyed. So we + * don't need to destroy it here + */ + ~RequestWithReturn() {} + + /** + * This method can be used to return a value from the request. This returns + * the underlying value because return type of the function we were + * instantiated with is not void + */ + ReturnType get() && { + // when the return value has been processed, we destroy the value + // contained in this request. Using a scope_exit means that we don't have + // to worry about storing the value somewhere and causing potentially an + // extra move + // + // note that the invariant here is that this function is only called if the + // requesting thread had it's critical section combined, and the value_ + // member constructed through detach() + SCOPE_EXIT { + value_.~ReturnType(); + }; + return std::move(value_); + } + + // this contains a copy of the function the waiter had requested to be + // executed as a combined critical section + Func func_; + // this stores the return value used in the request, we use a union here to + // avoid laundering and allow return types that are not default + // constructible to be propagated through the execution of the critical + // section + // + // note that this is an anonymous union, the member leaks into the + // surrounding scope as a member variable + union { + ReturnType value_; + }; +}; + +template <typename Func> +class RequestWithoutReturn { + public: + using F = Func; + using ReturnType = void; + explicit RequestWithoutReturn(Func func) : func_{std::move(func)} {} + + /** + * In this version of the request class, get() returns nothing as there is + * no stored value + */ + void get() && {} + + // this contains a copy of the function the waiter had requested to be + // executed as a combined critical section + Func func_; +}; + +// we need to use std::integral_constant::value here as opposed to +// std::integral_constant::operator T() because MSVC errors out with the +// implicit conversion +template <typename Func> +using Request = std::conditional_t< + std::is_void<folly::invoke_result_t<const Func&>>::value, + RequestWithoutReturn<Func>, + RequestWithReturn<Func>>; + +/** + * A template that helps us to transform a callable returning a value to one + * that returns void so it can be type erased and passed on to the waker. If + * the return value is small enough, it gets coalesced into the wait struct + * for optimal data transfer. When it's not small enough to fit in the waiter + * storage buffer, we place it on it's own cacheline with isolation to prevent + * false-sharing with the on-stack metadata of the waiter thread + * + * This helps a combined critical section feel more normal in the case where + * the user wants to return a value, for example + * + * auto value = mutex_.lock_combine([&]() { + * return data_.value(); + * }); + * + * Without this, the user would typically create a dummy object that they + * would then assign to from within the lambda. With return value chaining, + * this pattern feels more natural + * + * Note that it is important to copy the entire callble into this class. + * Storing something like a reference instead is not desirable because it does + * not allow InlineFunctionRef to use inline storage to represent the user's + * callable without extra indirections + * + * We use std::conditional_t and switch to the right type of task with the + * CoalescedTask type alias + */ +template <typename Func, typename Waiter> +class TaskWithCoalesce { + public: + using ReturnType = folly::invoke_result_t<const Func&>; + using StorageType = folly::Unit; + explicit TaskWithCoalesce(Func func, Waiter& waiter) + : func_{std::move(func)}, waiter_{waiter} {} + + void operator()() const { + auto value = func_(); + new (&waiter_.storage_) ReturnType{std::move(value)}; + } + + private: + Func func_; + Waiter& waiter_; + + static_assert(!std::is_void<ReturnType>{}, ""); + static_assert(alignof(decltype(waiter_.storage_)) >= alignof(ReturnType), ""); + static_assert(sizeof(decltype(waiter_.storage_)) >= sizeof(ReturnType), ""); +}; + +template <typename Func, typename Waiter> +class TaskWithoutCoalesce { + public: + using ReturnType = void; + using StorageType = folly::Unit; + explicit TaskWithoutCoalesce(Func func, Waiter&) : func_{std::move(func)} {} + + void operator()() const { + func_(); + } + + private: + Func func_; +}; + +template <typename Func, typename Waiter> +class TaskWithBigReturnValue { + public: + // Using storage that is aligned on the cacheline boundary helps us avoid a + // situation where the data ends up being allocated on two separate + // cachelines. This would require the remote thread to pull in both lines + // to issue a write. + // + // We also isolate the storage by appending some padding to the end to + // ensure we avoid false-sharing with the metadata used while the waiter + // waits + using ReturnType = folly::invoke_result_t<const Func&>; + static const auto kReturnValueAlignment = folly::constexpr_max( + alignof(ReturnType), + folly::hardware_destructive_interference_size); + using StorageType = std::aligned_storage_t< + sizeof(std::aligned_storage_t<sizeof(ReturnType), kReturnValueAlignment>), + kReturnValueAlignment>; + + explicit TaskWithBigReturnValue(Func func, Waiter&) + : func_{std::move(func)} {} + + void operator()() const { + DCHECK(storage_); + auto value = func_(); + new (storage_) ReturnType{std::move(value)}; + } + + void attach(StorageType* storage) { + DCHECK(!storage_); + storage_ = storage; + } + + private: + Func func_; + StorageType* storage_{nullptr}; + + static_assert(!std::is_void<ReturnType>{}, ""); + static_assert(sizeof(Waiter::storage_) < sizeof(ReturnType), ""); +}; + +template <typename T, bool> +struct Sizeof_; +template <typename T> +struct Sizeof_<T, false> : index_constant<sizeof(T)> {}; +template <typename T> +struct Sizeof_<T, true> : index_constant<0> {}; +template <typename T> +struct Sizeof : Sizeof_<T, std::is_void<T>::value> {}; + +// we need to use std::integral_constant::value here as opposed to +// std::integral_constant::operator T() because MSVC errors out with the +// implicit conversion +template <typename Func, typename Waiter> +using CoalescedTask = std::conditional_t< + std::is_void<folly::invoke_result_t<const Func&>>::value, + TaskWithoutCoalesce<Func, Waiter>, + std::conditional_t< + Sizeof<folly::invoke_result_t<const Func&>>::value <= + sizeof(Waiter::storage_), + TaskWithCoalesce<Func, Waiter>, + TaskWithBigReturnValue<Func, Waiter>>>; + +/** + * Given a request and a wait node, coalesce them into a CoalescedTask that + * coalesces the return value into the wait node when invoked from a remote + * thread + * + * When given a null request through nullptr_t, coalesce() returns null as well + */ +template <typename Waiter> +std::nullptr_t coalesce(std::nullptr_t&, Waiter&) { + return nullptr; +} + +template < + typename Request, + typename Waiter, + typename Func = typename Request::F> +CoalescedTask<Func, Waiter> coalesce(Request& request, Waiter& waiter) { + static_assert(!std::is_same<Request, std::nullptr_t>{}, ""); + return CoalescedTask<Func, Waiter>{request.func_, waiter}; +} + +/** + * Given a task, create storage for the return value. When we get a type + * of CoalescedTask, this returns an instance of CoalescedTask::StorageType. + * std::nullptr_t otherwise + */ +inline std::nullptr_t makeReturnValueStorageFor(std::nullptr_t&) { + return {}; +} + +template < + typename CoalescedTask, + typename StorageType = typename CoalescedTask::StorageType> +StorageType makeReturnValueStorageFor(CoalescedTask&) { + return {}; +} + +/** + * Given a task and storage, attach them together if needed. This only helps + * when we have a task that returns a value bigger than can be coalesced. In + * that case, we need to attach the storage with the task so the return value + * can be transferred to this thread from the remote thread + */ +template <typename Task, typename Storage> +void attach(Task&, Storage&) { + static_assert( + std::is_same<Storage, std::nullptr_t>{} || + std::is_same<Storage, folly::Unit>{}, + ""); +} + +template < + typename R, + typename W, + typename StorageType = typename TaskWithBigReturnValue<R, W>::StorageType> +void attach(TaskWithBigReturnValue<R, W>& task, StorageType& storage) { + task.attach(&storage); +} + +template <typename Request, typename Waiter> +void throwIfExceptionOccurred(Request&, Waiter& waiter, bool exception) { + using Storage = decltype(waiter.storage_); + using F = typename Request::F; + static_assert(sizeof(Storage) >= sizeof(std::exception_ptr), ""); + static_assert(alignof(Storage) >= alignof(std::exception_ptr), ""); + + // we only need to check for an exception in the waiter struct if the passed + // callable is not noexcept + // + // we need to make another instance of the exception with automatic storage + // duration and destroy the exception held in the storage *before throwing* to + // avoid leaks. If we don't destroy the exception_ptr in storage, the + // refcount for the internal exception will never hit zero, thereby leaking + // memory + if (UNLIKELY(!folly::is_nothrow_invocable_v<const F&> && exception)) { + auto storage = &waiter.storage_; + auto exc = folly::launder(reinterpret_cast<std::exception_ptr*>(storage)); + auto copy = std::move(*exc); + exc->std::exception_ptr::~exception_ptr(); + std::rethrow_exception(std::move(copy)); + } +} + +/** + * Given a CoalescedTask, a wait node and a request. Detach the return value + * into the request from the wait node and task. + */ +template <typename Waiter> +void detach(std::nullptr_t&, Waiter&, bool exception, std::nullptr_t&) { + DCHECK(!exception); +} + +template <typename Waiter, typename F> +void detach( + RequestWithoutReturn<F>& request, + Waiter& waiter, + bool exception, + folly::Unit&) { + throwIfExceptionOccurred(request, waiter, exception); +} + +template <typename Waiter, typename F> +void detach( + RequestWithReturn<F>& request, + Waiter& waiter, + bool exception, + folly::Unit&) { + throwIfExceptionOccurred(request, waiter, exception); + + using ReturnType = typename RequestWithReturn<F>::ReturnType; + static_assert(!std::is_same<ReturnType, void>{}, ""); + static_assert(sizeof(waiter.storage_) >= sizeof(ReturnType), ""); + + auto& val = *folly::launder(reinterpret_cast<ReturnType*>(&waiter.storage_)); + new (&request.value_) ReturnType{std::move(val)}; + val.~ReturnType(); +} + +template <typename Waiter, typename F, typename Storage> +void detach( + RequestWithReturn<F>& request, + Waiter& waiter, + bool exception, + Storage& storage) { + throwIfExceptionOccurred(request, waiter, exception); + + using ReturnType = typename RequestWithReturn<F>::ReturnType; + static_assert(!std::is_same<ReturnType, void>{}, ""); + static_assert(sizeof(storage) >= sizeof(ReturnType), ""); + + auto& val = *folly::launder(reinterpret_cast<ReturnType*>(&storage)); + new (&request.value_) ReturnType{std::move(val)}; + val.~ReturnType(); +} + +/** + * Get the time since epoch in nanoseconds + * + * This is faster than std::chrono::steady_clock because it avoids a VDSO + * access to get the timestamp counter + * + * Note that the hardware timestamp counter on x86, like std::steady_clock is + * guaranteed to be monotonically increasing - + * https://c9x.me/x86/html/file_module_x86_id_278.html + */ +inline std::chrono::nanoseconds time() { + return std::chrono::nanoseconds{hardware_timestamp()}; +} + +/** + * Zero out the other bits used by the implementation and return just an + * address from a uintptr_t + */ +template <typename Type> +Type* extractPtr(std::uintptr_t from) { + // shift one bit off the end, to get all 1s followed by a single 0 + auto mask = std::numeric_limits<std::uintptr_t>::max(); + mask >>= 1; + mask <<= 1; + CHECK(!(mask & 0b1)); + + return folly::bit_cast<Type*>(from & mask); +} + +/** + * Strips the given nanoseconds into only the least significant 56 bits by + * moving the least significant 56 bits over by 8 zeroing out the bottom 8 + * bits to be used as a medium of information transfer for the thread wait + * nodes + */ +inline std::uint64_t strip(std::chrono::nanoseconds t) { + auto time = t.count(); + return static_cast<std::uint64_t>(time) << 8; +} + +/** + * Recover the timestamp value from an integer that has the timestamp encoded + * in it + */ +inline std::uint64_t recover(std::uint64_t from) { + return from >> 8; +} + +template <template <typename> class Atomic, bool TimePublishing> +class DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy { + public: + // DistributedMutexStateProxy is move constructible and assignable for + // convenience + DistributedMutexStateProxy(DistributedMutexStateProxy&& other) { + *this = std::move(other); + } + + DistributedMutexStateProxy& operator=(DistributedMutexStateProxy&& other) { + DCHECK(!(*this)) << "Cannot move into a valid DistributedMutexStateProxy"; + + next_ = std::exchange(other.next_, nullptr); + expected_ = std::exchange(other.expected_, 0); + timedWaiters_ = std::exchange(other.timedWaiters_, false); + combined_ = std::exchange(other.combined_, false); + waker_ = std::exchange(other.waker_, 0); + waiters_ = std::exchange(other.waiters_, nullptr); + ready_ = std::exchange(other.ready_, nullptr); + + return *this; + } + + // The proxy is valid when a mutex acquisition attempt was successful, + // lock() is guaranteed to return a valid proxy, try_lock() is not + explicit operator bool() const { + return expected_; + } + + // private: + // friend the mutex class, since that will be accessing state private to + // this class + friend class DistributedMutex<Atomic, TimePublishing>; + + DistributedMutexStateProxy( + Waiter<Atomic>* next, + std::uintptr_t expected, + bool timedWaiter = false, + bool combined = false, + std::uintptr_t waker = 0, + Waiter<Atomic>* waiters = nullptr, + Waiter<Atomic>* ready = nullptr) + : next_{next}, + expected_{expected}, + timedWaiters_{timedWaiter}, + combined_{combined}, + waker_{waker}, + waiters_{waiters}, + ready_{ready} {} + + // the next thread that is to be woken up, this being null at the time of + // unlock() shows that the current thread acquired the mutex without + // contention or it was the terminal thread in the queue of threads waking up + Waiter<Atomic>* next_{nullptr}; + // this is the value that the current thread should expect to find on + // unlock, and if this value is not there on unlock, the current thread + // should assume that other threads are enqueued waiting for the mutex + // + // note that if the mutex has the same state set at unlock time, and this is + // set to an address (and not say kLocked in the case of a terminal waker) + // then it must have been the case that no other thread had enqueued itself, + // since threads in the domain of this mutex do not share stack space + // + // if we want to support stack sharing, we can solve the problem by looping + // at lock time, and setting a variable that says whether we have acquired + // the lock or not perhaps + std::uintptr_t expected_{0}; + // a boolean that will be set when the mutex has timed waiters that the + // current thread is responsible for waking, in such a case, the current + // thread will issue an atomic_notify_one() call after unlocking the mutex + // + // note that a timed waiter will itself always have this flag set. This is + // done so we can avoid having to issue a atomic_notify_all() call (and + // subsequently a thundering herd) when waking up timed-wait threads + bool timedWaiters_{false}; + // a boolean that contains true if the state proxy is not meant to be passed + // to the unlock() function. This is set only when there is contention and + // a thread had asked for its critical section to be combined + bool combined_{false}; + // metadata passed along from the thread that woke this thread up + std::uintptr_t waker_{0}; + // the list of threads that are waiting on a futex + // + // the current threads is meant to wake up this list of waiters if it is + // able to commit an unlock() on the mutex without seeing a contention chain + Waiter<Atomic>* waiters_{nullptr}; + // after a thread has woken up from a futex() call, it will have the rest of + // the threads that it were waiting behind it in this list, a thread that + // unlocks has to wake up threads from this list if it has any, before it + // goes to sleep to prevent pathological unfairness + Waiter<Atomic>* ready_{nullptr}; +}; + +template <template <typename> class Atomic, bool TimePublishing> +DistributedMutex<Atomic, TimePublishing>::DistributedMutex() + : state_{kUnlocked} {} + +template <typename Waiter> +std::uint64_t publish( + std::uint64_t spins, + bool& shouldPublish, + std::chrono::nanoseconds& previous, + Waiter& waiter, + std::uint32_t waitMode) { + // time publishing has some overhead because it executes an atomic exchange on + // the futex word. If this line is in a remote thread (eg. the combiner), + // then each time we publish a timestamp, this thread has to submit an RFO to + // the remote core for the cacheline, blocking progress for both threads. + // + // the remote core uses a store in the fast path - why then does an RFO make a + // difference? The only educated guess we have here is that the added + // roundtrip delays draining of the store buffer, which essentially exerts + // backpressure on future stores, preventing parallelization + // + // if we have requested a combine, time publishing is less important as it + // only comes into play when the combiner has exhausted their max combine + // passes. So we defer time publishing to the point when the current thread + // gets preempted + auto current = time(); + if ((current - previous) >= kScheduledAwaySpinThreshold) { + shouldPublish = true; + } + previous = current; + + // if we have requested a combine, and this is the first iteration of the + // wait-loop, we publish a max timestamp to optimistically convey that we have + // not yet been preempted (the remote knows the meaning of max timestamps) + // + // then if we are under the maximum number of spins allowed before sleeping, + // we publish the exact timestamp, otherwise we publish the minimum possible + // timestamp to force the waking thread to skip us + auto now = ((waitMode == kCombineWaiting) && !spins) + ? decltype(time())::max() + : (spins < kMaxSpins) ? previous : decltype(time())::zero(); + + // the wait mode information is published in the bottom 8 bits of the futex + // word, the rest contains time information as computed above. Overflows are + // not really a correctness concern because time publishing is only a + // heuristic. This leaves us 56 bits of nanoseconds (2 years) before we hit + // two consecutive wraparounds, so the lack of bits to respresent time is + // neither a performance nor correctness concern + auto data = strip(now) | waitMode; + auto signal = (shouldPublish || !spins || (waitMode != kCombineWaiting)) + ? waiter.futex_.exchange(data, std::memory_order_acq_rel) + : waiter.futex_.load(std::memory_order_acquire); + return signal & std::numeric_limits<std::uint8_t>::max(); +} + +template <typename Waiter> +bool spin(Waiter& waiter, std::uint32_t& sig, std::uint32_t mode) { + auto spins = std::uint64_t{0}; + auto waitMode = (mode == kCombineUninitialized) ? kCombineWaiting : kWaiting; + auto previous = time(); + auto shouldPublish = false; + while (true) { + auto signal = publish(spins++, shouldPublish, previous, waiter, waitMode); + + // if we got skipped, make a note of it and return if we got a skipped + // signal or a signal to wake up + auto skipped = (signal == kSkipped); + auto combined = (signal == kCombined); + auto exceptionOccurred = (signal == kExceptionOccurred); + auto woken = (signal == kWake); + if (skipped || woken || combined || exceptionOccurred) { + sig = static_cast<std::uint32_t>(signal); + return !skipped; + } + + // if we are under the spin threshold, pause to allow the other + // hyperthread to run. If not, then sleep + if (spins < kMaxSpins) { + asm_volatile_pause(); + } else { + Sleeper::sleep(); + } + } +} + +template <typename Waiter> +void doFutexWake(Waiter* waiter) { + if (waiter) { + // We can use a simple store operation here and not worry about checking + // to see if the thread had actually started waiting on the futex, that is + // already done in tryWake() when a sleeping thread is collected + // + // We now do not know whether the waiter had already enqueued on the futex + // or whether it had just stored kSleeping in its futex and was about to + // call futexWait(). We treat both these scenarios the same + // + // the below can theoretically cause a problem if we set the + // wake signal and the waiter was in between setting kSleeping in its + // futex and enqueueing on the futex. In this case the waiter will just + // return from futexWait() immediately. This leaves the address that the + // waiter was using for futexWait() possibly dangling, and the thread that + // we woke in the exchange above might have used that address for some + // other object + // + // however, even if the thread had indeed woken up simply becasue of the + // above exchange(), the futexWake() below is not incorrect. It is not + // incorrect because futexWake() does not actually change the memory of + // the futex word. It just uses the address to do a lookup in the kernel + // futex table. And even if we call futexWake() on some other address, + // and that address was being used to wait on futex() that thread will + // protect itself from spurious wakeups, check the value in the futex word + // and enqueue itself back on the futex + // + // this dangilng pointer possibility is why we use a pointer to the futex + // word, and avoid dereferencing after the store() operation + auto sleeper = &waiter->metadata_.sleeper_; + sleeper->store(kWake, std::memory_order_release); + futexWake(sleeper, 1); + } +} + +template <typename Waiter> +bool doFutexWait(Waiter* waiter, Waiter*& next) { + // first we get ready to sleep by calling exchange() on the futex with a + // kSleeping value + DCHECK(waiter->futex_.load(std::memory_order_relaxed) == kAboutToWait); + + // note the semantics of using a futex here, when we exchange the sleeper_ + // with kSleeping, we are getting ready to sleep, but before sleeping we get + // ready to sleep, and we return from futexWait() when the value of + // sleeper_ might have changed. We can also wake up because of a spurious + // wakeup, so we always check against the value in sleeper_ after returning + // from futexWait(), if the value is not kWake, then we continue + auto pre = + waiter->metadata_.sleeper_.exchange(kSleeping, std::memory_order_acq_rel); + + // Seeing a kSleeping on a futex word before we set it ourselves means only + // one thing - an unlocking thread caught us before we went to futex(), and + // we now have the lock, so we abort + // + // if we were given an early delivery, we can return from this function with + // a true, meaning that we now have the lock + if (pre == kSleeping) { + return true; + } + + // if we reach here then were were not given an early delivery, and any + // thread that goes to wake us up will see a consistent view of the rest of + // the contention chain (since the next_ variable is set before the + // kSleeping exchange above) + while (pre != kWake) { + // before enqueueing on the futex, we wake any waiters that we were + // possibly responsible for + doFutexWake(std::exchange(next, nullptr)); + + // then we wait on the futex + // + // note that we have to protect ourselves against spurious wakeups here. + // Because the corresponding futexWake() above does not synchronize + // wakeups around the futex word. Because doing so would become + // inefficient + futexWait(&waiter->metadata_.sleeper_, kSleeping); + pre = waiter->metadata_.sleeper_.load(std::memory_order_acquire); + DCHECK((pre == kSleeping) || (pre == kWake)); + } + + // when coming out of a futex, we might have some other sleeping threads + // that we were supposed to wake up, assign that to the next pointer + DCHECK(next == nullptr); + next = extractPtr<Waiter>(waiter->next_.load(std::memory_order_relaxed)); + return false; +} + +template <typename Waiter> +bool wait(Waiter* waiter, std::uint32_t mode, Waiter*& next, uint32_t& signal) { + if (mode == kAboutToWait) { + return doFutexWait(waiter, next); + } + + return spin(*waiter, signal, mode); +} + +inline void recordTimedWaiterAndClearTimedBit( + bool& timedWaiter, + std::uintptr_t& previous) { + // the previous value in the mutex can never be kTimedWaiter, timed waiters + // always set (kTimedWaiter | kLocked) in the mutex word when they try and + // acquire the mutex + DCHECK(previous != kTimedWaiter); + + if (UNLIKELY(previous & kTimedWaiter)) { + // record whether there was a timed waiter in the previous mutex state, and + // clear the timed bit from the previous state + timedWaiter = true; + previous = previous & (~kTimedWaiter); + } +} + +template <typename Atomic> +void wakeTimedWaiters(Atomic* state, bool timedWaiters) { + if (UNLIKELY(timedWaiters)) { + atomic_notify_one(state); + } +} + +template <template <typename> class Atomic, bool TimePublishing> +template <typename Func> +auto DistributedMutex<Atomic, TimePublishing>::lock_combine(Func func) + -> folly::invoke_result_t<const Func&> { + // invoke the lock implementation function and check whether we came out of + // it with our task executed as a combined critical section. This usually + // happens when the mutex is contended. + // + // In the absence of contention, we just return from the try_lock() function + // with the lock acquired. So we need to invoke the task and unlock + // the mutex before returning + auto&& task = Request<Func>{func}; + auto&& state = lockImplementation(*this, state_, task); + if (!state.combined_) { + // to avoid having to play a return-value dance when the combinable + // returns void, we use a scope exit to perform the unlock after the + // function return has been processed + SCOPE_EXIT { + unlock(std::move(state)); + }; + return func(); + } + + // if we are here, that means we were able to get our request combined, we + // can return the value that was transferred to us + // + // each thread that enqueues as a part of a contention chain takes up the + // responsibility of any timed waiter that had come immediately before it, + // so we wake up timed waiters before exiting the lock function. Another + // strategy might be to add the timed waiter information to the metadata and + // let a single leader wake up a timed waiter for better concurrency. But + // this has proven not to be useful in benchmarks beyond a small 5% delta, + // so we avoid taking the complexity hit and branch to wake up timed waiters + // from each thread + wakeTimedWaiters(&state_, state.timedWaiters_); + return std::move(task).get(); +} + +template <template <typename> class Atomic, bool TimePublishing> +typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy +DistributedMutex<Atomic, TimePublishing>::lock() { + auto null = nullptr; + return lockImplementation(*this, state_, null); +} + +template <template <typename> class Atomic, bool TimePublishing> +template <typename Rep, typename Period, typename Func> +folly::Optional<invoke_result_t<Func&>> +DistributedMutex<Atomic, TimePublishing>::try_lock_combine_for( + const std::chrono::duration<Rep, Period>& duration, + Func func) { + auto state = try_lock_for(duration); + if (state) { + SCOPE_EXIT { + unlock(std::move(state)); + }; + return func(); + } + + return folly::none; +} + +template <template <typename> class Atomic, bool TimePublishing> +template <typename Clock, typename Duration, typename Func> +folly::Optional<invoke_result_t<Func&>> +DistributedMutex<Atomic, TimePublishing>::try_lock_combine_until( + const std::chrono::time_point<Clock, Duration>& deadline, + Func func) { + auto state = try_lock_until(deadline); + if (state) { + SCOPE_EXIT { + unlock(std::move(state)); + }; + return func(); + } + + return folly::none; +} + +template <typename Atomic, template <typename> class A, bool T> +auto tryLockNoLoad(Atomic& atomic, DistributedMutex<A, T>&) { + // Try and set the least significant bit of the centralized lock state to 1, + // if this succeeds, it must have been the case that we had a kUnlocked (or + // 0) in the central storage before, since that is the only case where a 0 + // can be found in the least significant bit + // + // If this fails, then it is a no-op + using Proxy = typename DistributedMutex<A, T>::DistributedMutexStateProxy; + auto previous = atomic_fetch_set(atomic, 0, std::memory_order_acquire); + if (!previous) { + return Proxy{nullptr, kLocked}; + } + + return Proxy{nullptr, 0}; +} + +template <template <typename> class Atomic, bool TimePublishing> +typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy +DistributedMutex<Atomic, TimePublishing>::try_lock() { + // The lock attempt below requires an expensive atomic fetch-and-mutate or + // an even more expensive atomic compare-and-swap loop depending on the + // platform. These operations require pulling the lock cacheline into the + // current core in exclusive mode and are therefore hard to parallelize + // + // This probabilistically avoids the expense by first checking whether the + // mutex is currently locked + if (state_.load(std::memory_order_relaxed) != kUnlocked) { + return DistributedMutexStateProxy{nullptr, 0}; + } + + return tryLockNoLoad(state_, *this); +} + +template < + template <typename> class Atomic, + bool TimePublishing, + typename State, + typename Request> +typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy +lockImplementation( + DistributedMutex<Atomic, TimePublishing>& mutex, + State& atomic, + Request& request) { + // first try and acquire the lock as a fast path, the underlying + // implementation is slightly faster than using std::atomic::exchange() as + // is used in this function. So we get a small perf boost in the + // uncontended case + // + // We only go through this fast path for the lock/unlock usage and avoid this + // for combined critical sections. This check adds unnecessary overhead in + // that case as it causes an extra cacheline bounce + constexpr auto combineRequested = !std::is_same<Request, std::nullptr_t>{}; + if (!combineRequested) { + if (auto state = tryLockNoLoad(atomic, mutex)) { + return state; + } + } + + auto previous = std::uintptr_t{0}; + auto waitMode = combineRequested ? kCombineUninitialized : kUninitialized; + auto nextWaitMode = kAboutToWait; + auto timedWaiter = false; + Waiter<Atomic>* nextSleeper = nullptr; + while (true) { + // construct the state needed to wait + // + // We can't use auto here because MSVC errors out due to a missing copy + // constructor + Waiter<Atomic> state{}; + auto&& task = coalesce(request, state); + auto&& storage = makeReturnValueStorageFor(task); + auto&& address = folly::bit_cast<std::uintptr_t>(&state); + attach(task, storage); + state.initialize(waitMode, std::move(task)); + DCHECK(!(address & 0b1)); + + // set the locked bit in the address we will be persisting in the mutex + address |= kLocked; + + // attempt to acquire the mutex, mutex acquisition is successful if the + // previous value is zeroed out + // + // we use memory_order_acq_rel here because we want the read-modify-write + // operation to be both acquire and release. Acquire becasue if this is a + // successful lock acquisition, we want to acquire state any other thread + // has released from a prior unlock. We want release semantics becasue + // other threads that read the address of this value should see the full + // well-initialized node we are going to wait on if the mutex acquisition + // was unsuccessful + previous = atomic.exchange(address, std::memory_order_acq_rel); + recordTimedWaiterAndClearTimedBit(timedWaiter, previous); + state.next_.store(previous, std::memory_order_relaxed); + if (previous == kUnlocked) { + return {/* next */ nullptr, + /* expected */ address, + /* timedWaiter */ timedWaiter, + /* combined */ false, + /* waker */ 0, + /* waiters */ nullptr, + /* ready */ nextSleeper}; + } + DCHECK(previous & kLocked); + + // wait until we get a signal from another thread, if this returns false, + // we got skipped and had probably been scheduled out, so try again + auto signal = kUninitialized; + if (!wait(&state, waitMode, nextSleeper, signal)) { + std::swap(waitMode, nextWaitMode); + continue; + } + + // at this point it is safe to access the other fields in the waiter state, + // since the thread that woke us up is gone and nobody will be touching this + // state again, note that this requires memory ordering, and this is why we + // use memory_order_acquire (among other reasons) in the above wait + // + // first we see if the value we took off the mutex state was the thread that + // initated the wakeups, if so, we are the terminal node of the current + // contention chain. If we are the terminal node, then we should expect to + // see a kLocked in the mutex state when we unlock, if we see that, we can + // commit the unlock to the centralized mutex state. If not, we need to + // continue wakeups + // + // a nice consequence of passing kLocked as the current address if we are + // the terminal node is that it naturally just works with the algorithm. If + // we get a contention chain when coming out of a contention chain, the tail + // of the new contention chain will have kLocked set as the previous, which, + // as it happens "just works", since we have now established a recursive + // relationship until broken + auto next = previous; + auto expected = address; + if (previous == state.metadata_.waker_) { + next = 0; + expected = kLocked; + } + + // if we were given a combine signal, detach the return value from the + // wait struct into the request, so the current thread can access it + // outside this function + auto combined = (signal == kCombined); + auto exceptionOccurred = (signal == kExceptionOccurred); + if (combined || exceptionOccurred) { + detach(request, state, exceptionOccurred, storage); + } + + // if we are just coming out of a futex call, then it means that the next + // waiter we are responsible for is also a waiter waiting on a futex, so + // we return that list in the list of ready threads. We wlil be waking up + // the ready threads on unlock no matter what + return {/* next */ extractPtr<Waiter<Atomic>>(next), + /* expected */ expected, + /* timedWaiter */ timedWaiter, + /* combined */ combineRequested && (combined || exceptionOccurred), + /* waker */ state.metadata_.waker_, + /* waiters */ extractPtr<Waiter<Atomic>>(state.metadata_.waiters_), + /* ready */ nextSleeper}; + } +} + +inline bool preempted(std::uint64_t value, std::chrono::nanoseconds now) { + auto currentTime = recover(strip(now)); + auto nodeTime = recover(value); + auto preempted = + (currentTime > nodeTime + kScheduledAwaySpinThreshold.count()) && + (nodeTime != recover(strip(std::chrono::nanoseconds::max()))); + + // we say that the thread has been preempted if its timestamp says so, and + // also if it is neither uninitialized nor skipped + DCHECK(value != kSkipped); + return (preempted) && (value != kUninitialized) && + (value != kCombineUninitialized); +} + +inline bool isSleeper(std::uintptr_t value) { + return (value == kAboutToWait); +} + +inline bool isInitialized(std::uintptr_t value) { + return (value != kUninitialized) && (value != kCombineUninitialized); +} + +inline bool isCombiner(std::uintptr_t value) { + auto mode = (value & 0xff); + return (mode == kCombineWaiting) || (mode == kCombineUninitialized); +} + +inline bool isWaitingCombiner(std::uintptr_t value) { + return (value & 0xff) == kCombineWaiting; +} + +template <typename Waiter> +CombineFunction loadTask(Waiter* current, std::uintptr_t value) { + // if we know that the waiter is a combiner of some sort, it is safe to read + // and copy the value of the function in the waiter struct, since we know + // that a waiter would have set it before enqueueing + if (isCombiner(value)) { + return current->function_; + } + + return nullptr; +} + +template <typename Waiter> +FOLLY_COLD void transferCurrentException(Waiter* waiter) { + DCHECK(std::current_exception()); + new (&waiter->storage_) std::exception_ptr{std::current_exception()}; + waiter->futex_.store(kExceptionOccurred, std::memory_order_release); +} + +template <template <typename> class Atomic> +FOLLY_ALWAYS_INLINE std::uintptr_t tryCombine( + Waiter<Atomic>* waiter, + std::uintptr_t value, + std::uintptr_t next, + std::uint64_t iteration, + std::chrono::nanoseconds now, + CombineFunction task) { + // if the waiter has asked for a combine operation, we should combine its + // critical section and move on to the next waiter + // + // the waiter is combinable if the following conditions are satisfied + // + // 1) the state in the futex word is not uninitialized (kUninitialized) + // 2) it has a valid combine function + // 3) we are not past the limit of the number of combines we can perform + // or the waiter thread been preempted. If the waiter gets preempted, + // its better to just execute their critical section before moving on. + // As they will have to re-queue themselves after preemption anyway, + // leading to further delays in critical section completion + // + // if all the above are satisfied, then we can combine the critical section. + // Note that if the waiter is in a combineable state, that means that it had + // finished its writes to both the task and the next_ value. And observing + // a waiting state also means that we have acquired the writes to the other + // members of the waiter struct, so it's fine to use those values here + if (isWaitingCombiner(value) && + (iteration <= kMaxCombineIterations || preempted(value, now))) { + try { + task(); + waiter->futex_.store(kCombined, std::memory_order_release); + } catch (...) { + transferCurrentException(waiter); + } + return next; + } + + return 0; +} + +template <typename Waiter> +FOLLY_ALWAYS_INLINE std::uintptr_t tryWake( + bool publishing, + Waiter* waiter, + std::uintptr_t value, + std::uintptr_t next, + std::uintptr_t waker, + Waiter*& sleepers, + std::uint64_t iteration, + CombineFunction task) { + // try and combine the waiter's request first, if that succeeds that means + // we have successfully executed their critical section and can move on to + // the rest of the chain + auto now = time(); + if (tryCombine(waiter, value, next, iteration, now, task)) { + return next; + } + + // first we see if we can wake the current thread that is spinning + if ((!publishing || !preempted(value, now)) && !isSleeper(value)) { + // the Metadata class should be trivially destructible as we use placement + // new to set the relevant metadata without calling any destructor. We + // need to use placement new because the class contains a futex, which is + // non-movable and non-copyable + using Metadata = std::decay_t<decltype(waiter->metadata_)>; + static_assert(std::is_trivially_destructible<Metadata>{}, ""); + + // we need release here because of the write to waker_ and also because we + // are unlocking the mutex, the thread we do the handoff to here should + // see the modified data + new (&waiter->metadata_) Metadata{waker, bit_cast<uintptr_t>(sleepers)}; + waiter->futex_.store(kWake, std::memory_order_release); + return 0; + } + + // if the thread is not a sleeper, and we were not able to catch it before + // preemption, we can just return a false, it is safe to read next_ because + // the thread was preempted. Preemption signals can only come after the + // thread has set the next_ pointer, since the timestamp writes only start + // occurring after that point + // + // if a thread was preempted it must have stored next_ in the waiter struct, + // as the store to futex_ that resets the value from kUninitialized happens + // after the write to next + CHECK(publishing); + if (!isSleeper(value)) { + // go on to the next one + // + // Also, we need a memory_order_release here to prevent missed wakeups. A + // missed wakeup here can happen when we see that a thread had been + // preempted and skip it. Then go on to release the lock, and then when + // the thread which got skipped does an exchange on the central storage, + // still sees the locked bit, and never gets woken up + // + // Can we relax this? + DCHECK(preempted(value, now)); + DCHECK(!isCombiner(value)); + next = waiter->next_.load(std::memory_order_relaxed); + waiter->futex_.store(kSkipped, std::memory_order_release); + return next; + } + + // if we are here the thread is a sleeper + // + // we attempt to catch the thread before it goes to futex(). If we are able + // to catch the thread before it sleeps on a futex, we are done, and don't + // need to go any further + // + // if we are not able to catch the thread before it goes to futex, we + // collect the current thread in the list of sleeping threads represented by + // sleepers, and return the next thread in the list and return false along + // with the previous next value + // + // it is safe to read the next_ pointer in the waiter struct if we were + // unable to catch the thread before it went to futex() because we use + // acquire-release ordering for the exchange operation below. And if we see + // that the thread was already sleeping, we have synchronized with the write + // to next_ in the context of the sleeping thread + // + // Also we need to set the value of waiters_ and waker_ in the thread before + // doing the exchange because we need to pass on the list of sleepers in the + // event that we were able to catch the thread before it went to futex(). + // If we were unable to catch the thread before it slept, these fields will + // be ignored when the thread wakes up anyway + DCHECK(isSleeper(value)); + waiter->metadata_.waker_ = waker; + waiter->metadata_.waiters_ = folly::bit_cast<std::uintptr_t>(sleepers); + auto pre = + waiter->metadata_.sleeper_.exchange(kSleeping, std::memory_order_acq_rel); + + // we were able to catch the thread before it went to sleep, return true + if (pre != kSleeping) { + return 0; + } + + // otherwise return false, with the value of next_, it is safe to read next + // because of the same logic as when a thread was preempted + // + // we also need to collect this sleeper in the list of sleepers being built + // up + next = waiter->next_.load(std::memory_order_relaxed); + auto head = folly::bit_cast<std::uintptr_t>(sleepers); + waiter->next_.store(head, std::memory_order_relaxed); + sleepers = waiter; + return next; +} + +template <typename Waiter> +bool wake( + bool publishing, + Waiter& waiter, + std::uintptr_t waker, + Waiter*& sleepers, + std::uint64_t iter) { + // loop till we find a node that is either at the end of the list (as + // specified by waker) or we find a node that is active (as specified by + // the last published timestamp of the node) + auto current = &waiter; + while (current) { + // it is important that we load the value of function and next_ after the + // initial acquire load. This is required because we need to synchronize + // with the construction of the waiter struct before reading from it + // + // the load from the next_ variable is an optimistic load that assumes + // that the waiting thread has probably gone to the waiting state. If the + // waiitng thread is in the waiting state (as revealed by the acquire load + // from the futex word), we will see a well formed next_ value because it + // happens-before the release store to the futex word. The atomic load from + // next_ is an optimization to avoid branching before loading and prevent + // the compiler from eliding the load altogether (and using a pointer + // dereference when needed) + auto value = current->futex_.load(std::memory_order_acquire); + auto next = current->next_.load(std::memory_order_relaxed); + auto task = loadTask(current, value); + next = + tryWake(publishing, current, value, next, waker, sleepers, iter, task); + + // if there is no next node, we have managed to wake someone up and have + // successfully migrated the lock to another thread + if (!next) { + return true; + } + + // we need to read the value of the next node in the list before skipping + // it, this is because after we skip it the node might wake up and enqueue + // itself, and thereby gain a new next node + CHECK(publishing); + current = (next == waker) ? nullptr : extractPtr<Waiter>(next); + } + + return false; +} + +template <typename Atomic, typename Proxy, typename Sleepers> +bool tryUnlockClean(Atomic& state, Proxy& proxy, Sleepers sleepers) { + auto expected = proxy.expected_; + while (true) { + if (state.compare_exchange_strong( + expected, + kUnlocked, + std::memory_order_release, + std::memory_order_relaxed)) { + // if we were able to commit an unlocked, we need to wake up the futex + // waiters, if any + doFutexWake(sleepers); + return true; + } + + // if we failed the compare_exchange_strong() above, we check to see if + // the failure was because of the presence of a timed waiter. If that + // was the case then we try one more time with the kTimedWaiter bit set + if (UNLIKELY(expected == (proxy.expected_ | kTimedWaiter))) { + proxy.timedWaiters_ = true; + continue; + } + + // otherwise break, we have a contention chain + return false; + } +} + +template <template <typename> class Atomic, bool Publish> +void DistributedMutex<Atomic, Publish>::unlock( + DistributedMutex::DistributedMutexStateProxy proxy) { + // we always wake up ready threads and timed waiters if we saw either + DCHECK(proxy) << "Invalid proxy passed to DistributedMutex::unlock()"; + DCHECK(!proxy.combined_) << "Cannot unlock mutex after a successful combine"; + SCOPE_EXIT { + doFutexWake(proxy.ready_); + wakeTimedWaiters(&state_, proxy.timedWaiters_); + }; + + // if there is a wait queue we are responsible for, try and start wakeups, + // don't bother with the mutex state + auto sleepers = proxy.waiters_; + if (proxy.next_) { + if (wake(Publish, *proxy.next_, proxy.waker_, sleepers, 0)) { + return; + } + + // At this point, if are in the if statement, we were not the terminal + // node of the wakeup chain. Terminal nodes have the next_ pointer set to + // null in lock() + // + // So we need to pretend we were the end of the contention chain. Coming + // out of a contention chain always has the kLocked state set in the + // mutex. Unless there is another contention chain lined up, which does + // not matter since we are the terminal node anyway + proxy.expected_ = kLocked; + } + + for (std::uint64_t i = 0; true; ++i) { + // otherwise, since we don't have anyone we need to wake up, we try and + // release the mutex just as is + // + // if this is successful, we can return, the unlock was successful, we have + // committed a nice kUnlocked to the central storage, yay + if (tryUnlockClean(state_, proxy, sleepers)) { + return; + } + + // here we have a contention chain built up on the mutex. We grab the + // wait queue and start executing wakeups. We leave a locked bit on the + // centralized storage and handoff control to the head of the queue + // + // we use memory_order_acq_rel here because we want to see the + // full well-initialized node that the other thread is waiting on + // + // If we are unable to wake the contention chain, it is possible that when + // we come back to looping here, a new contention chain will form. In + // that case we need to use kLocked as the waker_ value because the + // terminal node of the new chain will see kLocked in the central storage + auto head = state_.exchange(kLocked, std::memory_order_acq_rel); + recordTimedWaiterAndClearTimedBit(proxy.timedWaiters_, head); + auto next = extractPtr<Waiter<Atomic>>(head); + auto expected = std::exchange(proxy.expected_, kLocked); + DCHECK((head & kLocked) && (head != kLocked)) << "incorrect state " << head; + if (wake(Publish, *next, expected, sleepers, i)) { + break; + } + } +} + +template <typename Atomic, typename Deadline, typename MakeProxy> +auto timedLock(Atomic& state, Deadline deadline, MakeProxy proxy) { + while (true) { + // we put a bit on the central state to show that there is a timed waiter + // and go to sleep on the central state + // + // when this thread goes to unlock the mutex, it will expect a 0b1 in the + // mutex state (0b1, not 0b11), but then it will see that the value in the + // mutex state is 0b11 and not 0b1, meaning that there might have been + // another timed waiter. Even though there might not have been another + // timed waiter in the time being. This sort of missed wakeup is + // desirable for timed waiters; it helps avoid thundering herds of timed + // waiters. Because the mutex is packed in 8 bytes, and we need an + // address to be stored in those 8 bytes, we don't have much room to play + // with. The only other solution is to issue a futexWake(INT_MAX) to wake + // up all waiters when a clean unlock is committed, when a thread saw a + // timed waiter in the mutex previously. + // + // putting a 0b11 here works for a set of reasons that is a superset of + // the set of reasons that make it okay to put a kLocked (0b1) in the + // mutex state. Now that the thread has put (kTimedWaiter | kLocked) + // (0b11) in the mutex state and it expects a kLocked (0b1), there are two + // scenarios possible. The first being when there is no contention chain + // formation in the mutex from the time a timed waiter got a lock to + // unlock. In this case, the unlocker sees a 0b11 in the mutex state, + // adjusts to the presence of a timed waiter and cleanly unlocks with a + // kUnlocked (0b0). The second is when there is a contention chain. + // When a thread puts its address in the mutex and sees the timed bit, it + // records the presence of a timed waiter, and then pretends as if it + // hadn't seen the timed bit. So future contention chain releases, will + // terminate with a kLocked (0b1) and not a (kLocked | kTimedWaiter) + // (0b11). This just works naturally with the rest of the algorithm + // without incurring a perf hit for the regular non-timed case + // + // this strategy does however mean, that when threads try to acquire the + // mutex and all time out, there will be a wasteful syscall to issue wakeups + // to waiting threads. We don't do anything to try and minimize this + // + // we need to use a fetch_or() here because we need to convey two bits of + // information - 1, whether the mutex is locked or not, and 2, whether + // there is a timed waiter. The alternative here is to use the second bit + // to convey information only, we can use a fetch_set() on the second bit + // to make this faster, but that comes at the expense of requiring regular + // fast path lock attempts. Which use a single bit read-modify-write for + // better performance + auto data = kTimedWaiter | kLocked; + auto previous = state.fetch_or(data, std::memory_order_acquire); + if (!(previous & 0b1)) { + DCHECK(!previous); + return proxy(nullptr, kLocked, true); + } + + // wait on the futex until signalled, if we get a timeout, the try_lock + // fails + auto result = atomic_wait_until(&state, previous | data, deadline); + if (result == std::cv_status::timeout) { + return proxy(nullptr, std::uintptr_t{0}, false); + } + } +} + +template <template <typename> class Atomic, bool TimePublishing> +template <typename Clock, typename Duration> +typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy +DistributedMutex<Atomic, TimePublishing>::try_lock_until( + const std::chrono::time_point<Clock, Duration>& deadline) { + // fast path for the uncontended case + // + // we get the time after trying to acquire the mutex because in the + // uncontended case, the price of getting the time is about 1/3 of the + // actual mutex acquisition. So we only pay the price of that extra bit of + // latency when needed + // + // this is even higher when VDSO is involved on architectures that do not + // offer a direct interface to the timestamp counter + if (auto state = try_lock()) { + return state; + } + + // fall back to the timed locking algorithm + using Proxy = DistributedMutexStateProxy; + return timedLock(state_, deadline, [](auto... as) { return Proxy{as...}; }); +} + +template <template <typename> class Atomic, bool TimePublishing> +template <typename Rep, typename Period> +typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy +DistributedMutex<Atomic, TimePublishing>::try_lock_for( + const std::chrono::duration<Rep, Period>& duration) { + // fast path for the uncontended case. Reasoning for doing this here is the + // same as in try_lock_until() + if (auto state = try_lock()) { + return state; + } + + // fall back to the timed locking algorithm + using Proxy = DistributedMutexStateProxy; + auto deadline = std::chrono::steady_clock::now() + duration; + return timedLock(state_, deadline, [](auto... as) { return Proxy{as...}; }); +} +} // namespace distributed_mutex +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.cpp new file mode 100644 index 000000000..ef1630afb --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/DistributedMutex.h> + +namespace folly { +namespace detail { +namespace distributed_mutex { + +template class DistributedMutex<std::atomic, true>; + +} // namespace distributed_mutex +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.h b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.h new file mode 100644 index 000000000..f4edeca18 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutex.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> +#include <folly/functional/Invoke.h> + +#include <atomic> +#include <chrono> +#include <cstdint> + +namespace folly { +namespace detail { +namespace distributed_mutex { + +/** + * DistributedMutex is a small, exclusive-only mutex that distributes the + * bookkeeping required for mutual exclusion in the stacks of threads that are + * contending for it. It has a mode that can combine critical sections when + * the mutex experiences contention; this allows the implementation to elide + * several expensive coherence and synchronization operations to boost + * throughput, surpassing even atomic instructions in some cases. It has a + * smaller memory footprint than std::mutex, a similar level of fairness + * (better in some cases) and no dependencies on heap allocation. It is the + * same width as a single pointer (8 bytes on most platforms), where on the + * other hand, std::mutex and pthread_mutex_t are both 40 bytes. It is larger + * than some of the other smaller locks, but the wide majority of cases using + * the small locks are wasting the difference in alignment padding anyway + * + * Benchmark results are good - at the time of writing, in the contended case, + * for lock/unlock based critical sections, it is about 4-5x faster than the + * smaller locks and about ~2x faster than std::mutex. When used in + * combinable mode, it is much faster than the alternatives, going more than + * 10x faster than the small locks, about 6x faster than std::mutex, 2-3x + * faster than flat combining and even faster than std::atomic<> in some + * cases, allowing more work with higher throughput. In the uncontended case, + * it is a few cycles faster than folly::MicroLock but a bit slower than + * std::mutex. DistributedMutex is also resistent to tail latency pathalogies + * unlike many of the other mutexes in use, which sleep for large time + * quantums to reduce spin churn, this causes elevated latencies for threads + * that enter the sleep cycle. The tail latency of lock acquisition can go up + * to 10x lower because of a more deterministic scheduling algorithm that is + * managed almost entirely in userspace. Detailed results comparing the + * throughput and latencies of different mutex implementations and atomics are + * at the bottom of folly/synchronization/test/SmallLocksBenchmark.cpp + * + * Theoretically, write locks promote concurrency when the critical sections + * are small as most of the work is done outside the lock. And indeed, + * performant concurrent applications go through several pains to limit the + * amount of work they do while holding a lock. However, most times, the + * synchronization and scheduling overhead of a write lock in the critical + * path is so high, that after a certain point, making critical sections + * smaller does not actually increase the concurrency of the application and + * throughput plateaus. DistributedMutex moves this breaking point to the + * level of hardware atomic instructions, so applications keep getting + * concurrency even under very high contention. It does this by reducing + * cache misses and contention in userspace and in the kernel by making each + * thread wait on a thread local node and futex. When combined critical + * sections are used DistributedMutex leverages template metaprogramming to + * allow the mutex to make better synchronization decisions based on the + * layout of the input and output data. This allows threads to keep working + * only on their own cache lines without requiring cache coherence operations + * when a mutex experiences heavy contention + * + * Non-timed mutex acquisitions are scheduled through intrusive LIFO + * contention chains. Each thread starts by spinning for a short quantum and + * falls back to two phased sleeping. Enqueue operations are lock free and + * are piggybacked off mutex acquisition attempts. The LIFO behavior of a + * contention chain is good in the case where the mutex is held for a short + * amount of time, as the head of the chain is likely to not have slept on + * futex() after exhausting its spin quantum. This allow us to avoid + * unnecessary traversal and syscalls in the fast path with a higher + * probability. Even though the contention chains are LIFO, the mutex itself + * does not adhere to that scheduling policy globally. During contention, + * threads that fail to lock the mutex form a LIFO chain on the central mutex + * state, this chain is broken when a wakeup is scheduled, and future enqueue + * operations form a new chain. This makes the chains themselves LIFO, but + * preserves global fairness through a constant factor which is limited to the + * number of concurrent failed mutex acquisition attempts. This binds the + * last in first out behavior to the number of contending threads and helps + * prevent starvation and latency outliers + * + * This strategy of waking up wakers one by one in a queue does not scale well + * when the number of threads goes past the number of cores. At which point + * preemption causes elevated lock acquisition latencies. DistributedMutex + * implements a hardware timestamp publishing heuristic to detect and adapt to + * preemption. + * + * DistributedMutex does not have the typical mutex API - it does not satisfy + * the Lockable concept. It requires the user to maintain ephemeral bookkeeping + * and pass that bookkeeping around to unlock() calls. The API overhead, + * however, comes for free when you wrap this mutex for usage with + * std::unique_lock, which is the recommended usage (std::lock_guard, in + * optimized mode, has no performance benefit over std::unique_lock, so has been + * omitted). A benefit of this API is that it disallows incorrect usage where a + * thread unlocks a mutex that it does not own, thinking a mutex is functionally + * identical to a binary semaphore, which, unlike a mutex, is a suitable + * primitive for that usage + * + * Combined critical sections allow the implementation to elide several + * expensive operations during the lifetime of a critical section that cause + * slowdowns with regular lock/unlock based usage. DistributedMutex resolves + * contention through combining up to a constant factor of 2 contention chains + * to prevent issues with fairness and latency outliers, so we retain the + * fairness benefits of the lock/unlock implementation with no noticeable + * regression when switching between the lock methods. Despite the efficiency + * benefits, combined critical sections can only be used when the critical + * section does not depend on thread local state and does not introduce new + * dependencies between threads when the critical section gets combined. For + * example, locking or unlocking an unrelated mutex in a combined critical + * section might lead to unexpected results or even undefined behavior. This + * can happen if, for example, a different thread unlocks a mutex locked by + * the calling thread, leading to undefined behavior as the mutex might not + * allow locking and unlocking from unrelated threads (the posix and C++ + * standard disallow this usage for their mutexes) + * + * Timed locking through DistributedMutex is implemented through a centralized + * algorithm. The underlying contention-chains framework used in + * DistributedMutex is not abortable so we build abortability on the side. + * All waiters wait on the central mutex state, by setting and resetting bits + * within the pointer-length word. Since pointer length atomic integers are + * incompatible with futex(FUTEX_WAIT) on most systems, a non-standard + * implementation of futex() is used, where wait queues are managed in + * user-space (see p1135r0 and folly::ParkingLot for more) + */ +template < + template <typename> class Atomic = std::atomic, + bool TimePublishing = true> +class DistributedMutex { + public: + class DistributedMutexStateProxy; + + /** + * DistributedMutex is only default constructible, it can neither be moved + * nor copied + */ + DistributedMutex(); + DistributedMutex(DistributedMutex&&) = delete; + DistributedMutex(const DistributedMutex&) = delete; + DistributedMutex& operator=(DistributedMutex&&) = delete; + DistributedMutex& operator=(const DistributedMutex&) = delete; + + /** + * Acquires the mutex in exclusive mode + * + * This returns an ephemeral proxy that contains internal mutex state. This + * must be kept around for the duration of the critical section and passed + * subsequently to unlock() as an rvalue + * + * The proxy has no public API and is intended to be for internal usage only + * + * There are three notable cases where this method causes undefined + * behavior: + * + * - This is not a recursive mutex. Trying to acquire the mutex twice from + * the same thread without unlocking it results in undefined behavior + * - Thread, coroutine or fiber migrations from within a critical section + * are disallowed. This is because the implementation requires owning the + * stack frame through the execution of the critical section for both + * lock/unlock or combined critical sections. This also means that you + * cannot allow another thread, fiber or coroutine to unlock the mutex + * - This mutex cannot be used in a program compiled with segmented stacks, + * there is currently no way to detect the presence of segmented stacks + * at compile time or runtime, so we have no checks against this + */ + DistributedMutexStateProxy lock(); + + /** + * Unlocks the mutex + * + * The proxy returned by lock must be passed to unlock as an rvalue. No + * other option is possible here, since the proxy is only movable and not + * copyable + * + * It is undefined behavior to unlock from a thread that did not lock the + * mutex + */ + void unlock(DistributedMutexStateProxy); + + /** + * Try to acquire the mutex + * + * A non blocking version of the lock() function. The returned object is + * contextually convertible to bool. And has the value true when the mutex + * was successfully acquired, false otherwise + * + * This is allowed to return false spuriously, i.e. this is not guaranteed + * to return true even when the mutex is currently unlocked. In the event + * of a failed acquisition, this does not impose any memory ordering + * constraints for other threads + */ + DistributedMutexStateProxy try_lock(); + + /** + * Try to acquire the mutex, blocking for the given time + * + * Like try_lock(), this is allowed to fail spuriously and is not guaranteed + * to return false even when the mutex is currently unlocked. But only + * after the given time has elapsed + * + * try_lock_for() accepts a duration to block for, and try_lock_until() + * accepts an absolute wall clock time point + */ + template <typename Rep, typename Period> + DistributedMutexStateProxy try_lock_for( + const std::chrono::duration<Rep, Period>& duration); + + /** + * Try to acquire the lock, blocking until the given deadline + * + * Other than the difference in the meaning of the second argument, the + * semantics of this function are identical to try_lock_for() + */ + template <typename Clock, typename Duration> + DistributedMutexStateProxy try_lock_until( + const std::chrono::time_point<Clock, Duration>& deadline); + + /** + * Execute a task as a combined critical section + * + * Unlike traditional lock and unlock methods, lock_combine() enqueues the + * passed task for execution on any arbitrary thread. This allows the + * implementation to prevent cache line invalidations originating from + * expensive synchronization operations. The thread holding the lock is + * allowed to execute the task before unlocking, thereby forming a "combined + * critical section". + * + * This idea is inspired by Flat Combining. Flat Combining was introduced + * in the SPAA 2010 paper titled "Flat Combining and the + * Synchronization-Parallelism Tradeoff", by Danny Hendler, Itai Incze, Nir + * Shavit, and Moran Tzafrir - + * https://www.cs.bgu.ac.il/~hendlerd/papers/flat-combining.pdf. The + * implementation used here is significantly different from that described + * in the paper. The high-level goal of reducing the overhead of + * synchronization, however, is the same. + * + * Combined critical sections work best when kept simple. Since the + * critical section might be executed on any arbitrary thread, relying on + * things like thread local state or mutex locking and unlocking might cause + * incorrectness. Associativity is important. For example + * + * auto one = std::unique_lock{one_}; + * two_.lock_combine([&]() { + * if (bar()) { + * one.unlock(); + * } + * }); + * + * This has the potential to cause undefined behavior because mutexes are + * only meant to be acquired and released from the owning thread. Similar + * errors can arise from a combined critical section introducing implicit + * dependencies based on the state of the combining thread. For example + * + * // thread 1 + * auto one = std::unique_lock{one_}; + * auto two = std::unique_lock{two_}; + * + * // thread 2 + * two_.lock_combine([&]() { + * auto three = std::unique_lock{three_}; + * }); + * + * Here, because we used a combined critical section, we have introduced a + * dependency from one -> three that might not obvious to the reader + * + * This function is exception-safe. If the passed task throws an exception, + * it will be propagated to the caller, even if the task is running on + * another thread + * + * There are three notable cases where this method causes undefined + * behavior: + * + * - This is not a recursive mutex. Trying to acquire the mutex twice from + * the same thread without unlocking it results in undefined behavior + * - Thread, coroutine or fiber migrations from within a critical section + * are disallowed. This is because the implementation requires owning the + * stack frame through the execution of the critical section for both + * lock/unlock or combined critical sections. This also means that you + * cannot allow another thread, fiber or coroutine to unlock the mutex + * - This mutex cannot be used in a program compiled with segmented stacks, + * there is currently no way to detect the presence of segmented stacks + * at compile time or runtime, so we have no checks against this + */ + template <typename Task> + auto lock_combine(Task task) -> folly::invoke_result_t<const Task&>; + + /** + * Try to combine a task as a combined critical section untill the given time + * + * Like the other try_lock() mehtods, this is allowed to fail spuriously, + * and is not guaranteed to return true even when the mutex is currently + * unlocked. + * + * Note that this does not necessarily have the same performance + * characteristics as the non-timed version of the combine method. If + * performance is critical, use that one instead + */ + template <typename Rep, typename Period, typename Task> + folly::Optional<invoke_result_t<Task&>> try_lock_combine_for( + const std::chrono::duration<Rep, Period>& duration, + Task task); + + /** + * Try to combine a task as a combined critical section untill the given time + * + * Other than the difference in the meaning of the second argument, the + * semantics of this function are identical to try_lock_combine_for() + */ + template <typename Clock, typename Duration, typename Task> + folly::Optional<invoke_result_t<Task&>> try_lock_combine_until( + const std::chrono::time_point<Clock, Duration>& deadline, + Task task); + + private: + Atomic<std::uintptr_t> state_{0}; +}; + +} // namespace distributed_mutex +} // namespace detail + +/** + * Bring the default instantiation of DistributedMutex into the folly + * namespace without requiring any template arguments for public usage + */ +extern template class detail::distributed_mutex::DistributedMutex<>; +using DistributedMutex = detail::distributed_mutex::DistributedMutex<>; + +} // namespace folly + +#include <folly/synchronization/DistributedMutex-inl.h> +#include <folly/synchronization/DistributedMutexSpecializations.h> diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h new file mode 100644 index 000000000..2fb4d1419 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/DistributedMutex.h> +#include <folly/synchronization/detail/ProxyLockable.h> + +/** + * Specializations for DistributedMutex allow us to use it like a normal + * mutex. Even though it has a non-usual interface + */ +namespace std { +template <template <typename> class Atom, bool TimePublishing> +class unique_lock< + ::folly::detail::distributed_mutex::DistributedMutex<Atom, TimePublishing>> + : public ::folly::detail::ProxyLockableUniqueLock< + ::folly::detail::distributed_mutex:: + DistributedMutex<Atom, TimePublishing>> { + public: + using ::folly::detail::ProxyLockableUniqueLock< + ::folly::detail::distributed_mutex:: + DistributedMutex<Atom, TimePublishing>>::ProxyLockableUniqueLock; +}; + +template <template <typename> class Atom, bool TimePublishing> +class lock_guard< + ::folly::detail::distributed_mutex::DistributedMutex<Atom, TimePublishing>> + : public ::folly::detail::ProxyLockableLockGuard< + ::folly::detail::distributed_mutex:: + DistributedMutex<Atom, TimePublishing>> { + public: + using ::folly::detail::ProxyLockableLockGuard< + ::folly::detail::distributed_mutex:: + DistributedMutex<Atom, TimePublishing>>::ProxyLockableLockGuard; +}; +} // namespace std diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr-fwd.h b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr-fwd.h new file mode 100644 index 000000000..345964771 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr-fwd.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/portability/GFlags.h> + +#include <atomic> +#include <memory> + +/// +/// Forward declatations and implicit documentation of all hazptr +/// top-level classes, functions, macros, default values, and globals. +/// + +/** FOLYY_HAZPTR_THR_LOCAL */ +#if FOLLY_MOBILE +#define FOLLY_HAZPTR_THR_LOCAL false +#else +#define FOLLY_HAZPTR_THR_LOCAL true +#endif + +DECLARE_bool(folly_hazptr_use_executor); + +namespace folly { + +/// +/// Hazard pointer record. +/// Defined in HazptrRec.h +/// + +/** hazptr_rec */ +template <template <typename> class Atom = std::atomic> +class hazptr_rec; + +/// +/// Classes related to objects protected by hazard pointers. +/// Defined in HazptrObj.h +/// + +/** hazptr_obj */ +template <template <typename> class Atom = std::atomic> +class hazptr_obj; + +/** hazptr_obj_list */ +template <template <typename> class Atom = std::atomic> +class hazptr_obj_list; + +/** hazptr_obj_cohort */ +template <template <typename> class Atom = std::atomic> +class hazptr_obj_cohort; + +/** hazptr_obj_retired_list */ +template <template <typename> class Atom = std::atomic> +class hazptr_obj_retired_list; + +/** hazptr_deleter */ +template <typename T, typename D> +class hazptr_deleter; + +/** hazptr_obj_base */ +template < + typename T, + template <typename> class Atom = std::atomic, + typename D = std::default_delete<T>> +class hazptr_obj_base; + +/// +/// Classes related to link counted objects and automatic retirement. +/// Defined in HazptrLinked.h +/// + +/** hazptr_root */ +template <typename T, template <typename> class Atom = std::atomic> +class hazptr_root; + +/** hazptr_obj_linked */ +template <template <typename> class Atom = std::atomic> +class hazptr_obj_linked; + +/** hazptr_obj_base_linked */ +template < + typename T, + template <typename> class Atom = std::atomic, + typename Deleter = std::default_delete<T>> +class hazptr_obj_base_linked; + +/// +/// Classes and functions related to thread local structures. +/// Defined in HazptrThrLocal.h +/// + +/** hazptr_tc_entry */ +template <template <typename> class Atom = std::atomic> +class hazptr_tc_entry; + +/** hazptr_tc */ +template <template <typename> class Atom = std::atomic> +class hazptr_tc; + +/** hazptr_tc_tls */ +template <template <typename> class Atom = std::atomic> +hazptr_tc<Atom>& hazptr_tc_tls(); + +/** hazptr_priv */ +template <template <typename> class Atom = std::atomic> +class hazptr_priv; + +/** hazptr_priv_tls */ +template <template <typename> class Atom = std::atomic> +hazptr_priv<Atom>& hazptr_priv_tls(); + +/// +/// Hazard pointer domain +/// Defined in HazptrDomain.h +/// + +/** hazptr_domain */ +template <template <typename> class Atom = std::atomic> +class hazptr_domain; + +/** default_hazptr_domain */ +template <template <typename> class Atom = std::atomic> +hazptr_domain<Atom>& default_hazptr_domain(); + +/** hazptr_domain_push_list */ +template <template <typename> class Atom = std::atomic> +void hazptr_domain_push_list( + hazptr_obj_list<Atom>& l, + hazptr_domain<Atom>& domain = default_hazptr_domain<Atom>()) noexcept; + +/** hazptr_domain_push_retired */ +template <template <typename> class Atom = std::atomic> +void hazptr_domain_push_retired( + hazptr_obj_list<Atom>& l, + bool check = true, + hazptr_domain<Atom>& domain = default_hazptr_domain<Atom>()) noexcept; + +/** hazptr_retire */ +template < + template <typename> class Atom = std::atomic, + typename T, + typename D = std::default_delete<T>> +void hazptr_retire(T* obj, D reclaim = {}); + +/** hazptr_cleanup */ +template <template <typename> class Atom = std::atomic> +void hazptr_cleanup( + hazptr_domain<Atom>& domain = default_hazptr_domain<Atom>()) noexcept; + +/** Global default domain defined in Hazptr.cpp */ +extern hazptr_domain<std::atomic> default_domain; + +/// +/// Classes related to hazard pointer holders. +/// Defined in HazptrHolder.h +/// + +/** hazptr_holder */ +template <template <typename> class Atom = std::atomic> +class hazptr_holder; + +/** Free function swap of hazptr_holder-s */ +template <template <typename> class Atom = std::atomic> +void swap(hazptr_holder<Atom>&, hazptr_holder<Atom>&) noexcept; + +/** hazptr_array */ +template <uint8_t M = 1, template <typename> class Atom = std::atomic> +class hazptr_array; + +/** hazptr_local */ +template <uint8_t M = 1, template <typename> class Atom = std::atomic> +class hazptr_local; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.cpp new file mode 100644 index 000000000..ec93129e1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/Hazptr.h> + +#include <folly/portability/GFlags.h> + +#include <atomic> + +DEFINE_bool( + folly_hazptr_use_executor, + true, + "Use an executor for hazptr asynchronous reclamation"); + +namespace folly { + +FOLLY_STATIC_CTOR_PRIORITY_MAX hazptr_domain<std::atomic> default_domain; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.h b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.h new file mode 100644 index 000000000..c62f2407d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Hazptr.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> +#include <folly/synchronization/HazptrDomain.h> +#include <folly/synchronization/HazptrHolder.h> +#include <folly/synchronization/HazptrObj.h> +#include <folly/synchronization/HazptrObjLinked.h> +#include <folly/synchronization/HazptrRec.h> +#include <folly/synchronization/HazptrThrLocal.h> + +/// Hazard pointers is a safe reclamation method. It protects objects +/// from being reclaimed while being accessed by one or more threads, but +/// allows objects to be removed concurrently while being accessed. +/// +/// What is a Hazard Pointer? +/// ------------------------- +/// A hazard pointer is a single-writer multi-reader pointer that can +/// be owned by at most one thread at a time. To protect an object A +/// from being reclaimed while in use, a thread X sets one of its +/// owned hazard pointers, P, to the address of A. If P is set to &A +/// before A is removed (i.e., it becomes unreachable) then A will not be +/// reclaimed as long as P continues to hold the value &A. +/// +/// Why use hazard pointers? +/// ------------------------ +/// - Speed and scalability. +/// - Can be used while blocking. +/// +/// When not to use hazard pointers? +/// -------------------------------- +/// - When thread local data is not supported efficiently. +/// +/// Basic Interface +/// --------------- +/// - In the hazptr library, raw hazard pointers are not exposed to +/// users. Instead, each instance of the class hazptr_holder owns +/// and manages at most one hazard pointer. +/// - Typically classes of objects protected by hazard pointers are +/// derived from a class template hazptr_obj_base that provides a +/// member function retire(). When an object A is removed, +/// A.retire() is called to pass responsibility for reclaiming A to +/// the hazptr library. A will be reclaimed only after it is not +/// protected by hazard pointers. +/// - The essential components of the hazptr API are: +/// o hazptr_holder: Class that owns and manages a hazard pointer. +/// o get_protected: Mmember function of hazptr_holder. Protects +/// an object pointed to by an atomic source (if not null). +/// T* get_protected(const atomic<T*>& src); +/// o hazptr_obj_base<T>: Base class for protected objects. +/// o retire: Member function of hazptr_obj_base that automatically +/// reclaims the object when safe. +/// void retire(); +/// +/// Default Domain and Default Deleters +/// ----------------------------------- +/// - Most uses do not need to specify custom domains and custom +/// deleters, and by default use the default domain and default +/// deleters. +/// +/// Simple usage example +/// -------------------- +/// class Config : public hazptr_obj_base<Config> { +/// /* ... details ... */ +/// U get_config(V v); +/// }; +/// +/// std::atomic<Config*> config_; +/// +/// // Called frequently +/// U get_config(V v) { +/// hazptr_holder h; /* h owns a hazard pointer */ +/// Config* ptr = h.get_protected(config_); +/// /* safe to access *ptr as long as it is protected by h */ +/// return ptr->get_config(v); +/// /* h dtor resets and releases the owned hazard pointer, +/// *ptr will be no longer protected by this hazard pointer */ +/// } +/// +/// // called rarely +/// void update_config(Config* new_config) { +/// Config* ptr = config_.exchange(new_config); +/// ptr->retire() // Member function of hazptr_obj_base<Config> +/// } +/// +/// Optimized Holders +/// ----------------- +/// - The template hazptr_array<M> provides most of the functionality +/// of M hazptr_holder-s but with faster construction/destruction +/// (for M > 1), at the cost of restrictions (on move and swap). +/// - The template hazptr_local<M> provides greater speed even when +/// M=1 (~2 ns vs ~5 ns for construction/destruction) but it is +/// unsafe for the current thread to construct any other holder-type +/// objects (hazptr_holder, hazptr_array and other hazptr_local) +/// while the current instance exists. +/// - In the above example, if Config::get_config() and all of its +/// descendants are guaranteed not to use hazard pointers, then it +/// can be faster (by ~3 ns.) to use +/// hazptr_local<1> h; +/// Config* ptr = h[0].get_protected(config_); +/// than +/// hazptr_holder h; +/// Config* ptr = h.get_protected(config_); +/// +/// Memory Usage +/// ------------ +/// - The size of the metadata for the hazptr library is linear in the +/// number of threads using hazard pointers, assuming a constant +/// number of hazard pointers per thread, which is typical. +/// - The typical number of reclaimable but not yet reclaimed of +/// objects is linear in the number of hazard pointers, which +/// typically is linear in the number of threads using hazard +/// pointers. +/// +/// Protecting Linked Structures and Automatic Retirement +/// ----------------------------------------------------- +/// Hazard pointers provide link counting API to protect linked +/// structures. It is capable of automatic retirement of objects even +/// when the removal of objects is uncertain. It also supports +/// optimizations when links are known to be immutable. All the link +/// counting features incur no extra overhead for readers. +/// See HazptrObjLinked.h for more details. +/// +/// Alternative Safe Reclamation Methods +/// ------------------------------------ +/// - Locking (exclusive or shared): +/// o Pros: simple to reason about. +/// o Cons: serialization, high reader overhead, high contention, deadlock. +/// o When to use: When speed and contention are not critical, and +/// when deadlock avoidance is simple. +/// - Reference counting (atomic shared_ptr): +/// o Pros: automatic reclamation, thread-anonymous, independent of +/// support for thread local data, immune to deadlock. +/// o Cons: high reader (and writer) overhead, high reader (and +/// writer) contention. +/// o When to use: When thread local support is lacking and deadlock +/// can be a problem, or automatic reclamation is needed. +/// - Read-copy-update (RCU): +/// o Pros: simple, fast, scalable. +/// o Cons: sensitive to blocking +/// o When to use: When speed and scalability are important and +/// objects do not need to be protected while blocking. +/// +/// Hazard Pointers vs RCU +/// ---------------------- +/// - The differences between hazard pointers and RCU boil down to +/// that hazard pointers protect specific objects, whereas RCU +/// sections protect all protectable objects. +/// - Both have comparably low overheads for protection (i.e. reading +/// or traversal) in the order of low nanoseconds. +/// - Both support effectively perfect scalability of object +/// protection by read-only operations (barring other factors). +/// - Both rely on thread local data for performance. +/// - Hazard pointers can protect objects while blocking +/// indefinitely. Hazard pointers only prevent the reclamation of +/// the objects they are protecting. +/// - RCU sections do not allow indefinite blocking, because RCU +/// prevents the reclamation of all protectable objects, which +/// otherwise would lead to deadlock and/or running out of memory. +/// - Hazard pointers can support end-to-end lock-free operations, +/// including updates (provided lock-free allocator), regardless of +/// thread delays and scheduling constraints. +/// - RCU can support wait-free read operations, but reclamation of +/// unbounded objects can be delayed for as long as a single thread +/// is delayed. +/// - The number of unreclaimed objects is bounded when protected by +/// hazard pointers, but is unbounded when protected by RCU. +/// - RCU is simpler to use than hazard pointers (except for the +/// blocking and deadlock issues mentioned above). Hazard pointers +/// need to identify protected objects, whereas RCU does not need to +/// because it protects all protectable objects. +/// - Both can protect linked structures. Hazard pointers needs +/// additional link counting with low or moderate overhead for +/// update operations, and no overhead for readers. RCU protects +/// protects linked structures automatically, because it protects +/// everything. +/// +/// Differences from the Standard Proposal +/// -------------------------------------- +/// - The latest standard proposal is in wg21.link/p0566. +/// - This library's API differs from the standard proposal because: +/// (a) the standard proposal is changing based on committee +/// feedback, and (b) this library provides additional +/// fast-evolving features based on usage experience that do not +/// have corressponding proposed standard wording. +/// - The main differences are: +/// o This library uses an extra atomic template parameter for +/// testing and debugging. +/// o This library does not support a custom polymorphic allocator +/// (C++17) parameter for the hazptr_domain constructor, until +/// such support becomes widely available. +/// o The construction of empty and non-empty hazptr_holder-s are +/// reversed. This library will conform eventually. +/// o hazptr_holder member functions get_protected and reset are +/// called protect and reset_protected, respectively, in the +/// latest proposal. Will conform eventually. +/// o hazptr_array and hazptr_local are not part of the standard +/// proposal. +/// o Link counting support and protection of linked structures is +/// not part of the current standard proposal. diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrDomain.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrDomain.h new file mode 100644 index 000000000..97817e468 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrDomain.h @@ -0,0 +1,696 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> +#include <folly/synchronization/HazptrObj.h> +#include <folly/synchronization/HazptrRec.h> +#include <folly/synchronization/HazptrThrLocal.h> + +#include <folly/Memory.h> +#include <folly/Portability.h> +#include <folly/executors/QueuedImmediateExecutor.h> +#include <folly/synchronization/AsymmetricMemoryBarrier.h> + +#include <atomic> +#include <unordered_set> // for hash set in bulk_reclaim + +/// +/// Classes related to hazard pointer domains. +/// + +namespace folly { + +namespace detail { + +constexpr int hazptr_domain_rcount_threshold() { + return 1000; +} + +} // namespace detail + +/** + * hazptr_domain + * + * A domain manages a set of hazard pointers and a set of retired objects. + * + * Most user code need not specify any domains. + * + * Notes on destruction order, tagged objects, locking and deadlock + * avoidance: + * - Tagged objects support reclamation order guarantees. A call to + * cleanup_cohort_tag(tag) guarantees that all objects with the + * specified tag are reclaimed before the function returns. + * - Due to the strict order, access to the set of tagged objects + * needs synchronization and care must be taken to avoid deadlock. + * - There are two types of reclamation operations to consider: + * - Type A: A Type A reclamation operation is triggered by meeting + * some threshold. Reclaimed objects may have different + * tags. Hazard pointers are checked and only unprotected objects + * are reclaimed. This type is expected to be expensive but + * infrequent and the cost is amortized over a large number of + * reclaimed objects. This type is needed to guarantee an upper + * bound on unreclaimed reclaimable objects. + * - Type B: A Type B reclamation operation is triggered by a call + * to the function cleanup_cohort_tag for a specific tag. All + * objects with the specified tag must be reclaimed + * unconditionally before returning from such a function + * call. Hazard pointers are not checked. This type of reclamation + * operation is expected to be inexpensive and may be invoked more + * frequently than Type A. + * - Tagged retired objects are kept in a single list in the domain + * structure, named tagged_. + * - Both Type A and Type B of reclamation pop all the objects in + * tagged_ and sort them into two sets of reclaimable and + * unreclaimable objects. The objects in the reclaimable set are + * reclaimed and the objects in the unreclaimable set are pushed + * back in tagged_. + * - The tagged_ list is locked between popping all objects and + * pushing back unreclaimable objects, in order to guarantee that + * Type B operations do not miss any objects that match the + * specified tag. + * - A Type A operation cannot release the lock on the tagged_ list + * before reclaiming reclaimable objects, to prevent concurrent + * Type B operations from returning before the reclamation of + * objects with matching tags. + * - A Type B operation can release the lock on tagged_ before + * reclaiming objects because the set of reclaimable objects by + * Type B operations are disjoint. + * - The lock on the tagged_ list is re-entrant, to prevent deadlock + * when reclamation in a Type A operation requires a Type B + * reclamation operation to complete. + * - The implementation allows only one pattern of re-entrance: An + * inner Type B inside an outer Type A. + * - An inner Type B operation must have access and ability to modify + * the outer Type A operation's set of reclaimable objects and + * their children objects in order not to miss objects that match + * the specified tag. Hence, Type A operations use data members, + * unprotected_ and children_, to keep track of these objects + * between reclamation steps and to provide inner Type B operations + * access to these objects. + */ +template <template <typename> class Atom> +class hazptr_domain { + using Obj = hazptr_obj<Atom>; + using ObjList = hazptr_obj_list<Atom>; + using RetiredList = hazptr_obj_retired_list<Atom>; + using Set = std::unordered_set<const void*>; + using ExecFn = folly::Executor* (*)(); + + static constexpr int kThreshold = detail::hazptr_domain_rcount_threshold(); + static constexpr int kMultiplier = 2; + static constexpr uint64_t kSyncTimePeriod{2000000000}; // nanoseconds + static constexpr uintptr_t kTagBit = hazptr_obj<Atom>::kTagBit; + + static folly::Executor* get_default_executor() { + return &folly::QueuedImmediateExecutor::instance(); + } + + Atom<hazptr_rec<Atom>*> hazptrs_{nullptr}; + Atom<hazptr_obj<Atom>*> retired_{nullptr}; + Atom<uint64_t> sync_time_{0}; + /* Using signed int for rcount_ because it may transiently be negative. + Using signed int for all integer variables that may be involved in + calculations related to the value of rcount_. */ + Atom<int> hcount_{0}; + Atom<int> rcount_{0}; + Atom<uint16_t> num_bulk_reclaims_{0}; + bool shutdown_{false}; + RetiredList untagged_; + RetiredList tagged_; + Obj* unprotected_; // List of unprotected objects being reclaimed + ObjList children_; // Children of unprotected objects being reclaimed + Atom<uint64_t> tagged_sync_time_{0}; + Atom<uint64_t> untagged_sync_time_{0}; + Atom<ExecFn> exec_fn_{nullptr}; + Atom<int> exec_backlog_{0}; + + public: + /** Constructor */ + hazptr_domain() = default; + + /** Destructor */ + ~hazptr_domain() { + shutdown_ = true; + reclaim_all_objects(); + free_hazptr_recs(); + DCHECK(tagged_.empty()); + } + + hazptr_domain(const hazptr_domain&) = delete; + hazptr_domain(hazptr_domain&&) = delete; + hazptr_domain& operator=(const hazptr_domain&) = delete; + hazptr_domain& operator=(hazptr_domain&&) = delete; + + void set_executor(ExecFn exfn) { + exec_fn_.store(exfn, std::memory_order_release); + } + + void clear_executor() { + exec_fn_.store(nullptr, std::memory_order_release); + } + + /** retire - nonintrusive - allocates memory */ + template <typename T, typename D = std::default_delete<T>> + void retire(T* obj, D reclaim = {}) { + struct hazptr_retire_node : hazptr_obj<Atom> { + std::unique_ptr<T, D> obj_; + hazptr_retire_node(T* retireObj, D toReclaim) + : obj_{retireObj, std::move(toReclaim)} {} + }; + + auto node = new hazptr_retire_node(obj, std::move(reclaim)); + node->reclaim_ = [](hazptr_obj<Atom>* p, hazptr_obj_list<Atom>&) { + delete static_cast<hazptr_retire_node*>(p); + }; + hazptr_obj_list<Atom> l(node); + push_retired(l); + } + + /** cleanup */ + void cleanup() noexcept { + relaxed_cleanup(); + wait_for_zero_bulk_reclaims(); // wait for concurrent bulk_reclaim-s + } + + /** cleanup_cohort_tag */ + void cleanup_cohort_tag(const hazptr_obj_cohort<Atom>* cohort) noexcept { + auto tag = reinterpret_cast<uintptr_t>(cohort) + kTagBit; + auto obj = tagged_.pop_all(RetiredList::kAlsoLock); + ObjList match, nomatch; + list_match_tag(tag, obj, match, nomatch); + if (unprotected_) { // There must be ongoing do_reclamation + ObjList match2, nomatch2; + list_match_tag(tag, unprotected_, match2, nomatch2); + match.splice(match2); + unprotected_ = nomatch2.head(); + } + if (children_.head()) { + ObjList match2, nomatch2; + list_match_tag(tag, children_.head(), match2, nomatch2); + match.splice(match2); + children_ = std::move(nomatch2); + } + auto count = nomatch.count(); + nomatch.set_count(0); + tagged_.push_unlock(nomatch); + obj = match.head(); + reclaim_list_transitive(obj); + if (count >= threshold()) { + check_threshold_and_reclaim( + tagged_, RetiredList::kAlsoLock, tagged_sync_time_); + } + } + + void + list_match_tag(uintptr_t tag, Obj* obj, ObjList& match, ObjList& nomatch) { + list_match_condition( + obj, match, nomatch, [tag](Obj* o) { return o->cohort_tag() == tag; }); + } + + private: + using hazptr_rec_alloc = AlignedSysAllocator< + hazptr_rec<Atom>, + FixedAlign<alignof(hazptr_rec<Atom>)>>; + + friend void hazptr_domain_push_list<Atom>( + hazptr_obj_list<Atom>&, + hazptr_domain<Atom>&) noexcept; + friend void hazptr_domain_push_retired<Atom>( + hazptr_obj_list<Atom>&, + bool check, + hazptr_domain<Atom>&) noexcept; + friend class hazptr_holder<Atom>; + friend class hazptr_obj<Atom>; + friend class hazptr_obj_cohort<Atom>; +#if FOLLY_HAZPTR_THR_LOCAL + friend class hazptr_tc<Atom>; +#endif + + /** hprec_acquire */ + hazptr_rec<Atom>* hprec_acquire() { + auto rec = try_acquire_existing_hprec(); + return rec != nullptr ? rec : acquire_new_hprec(); + } + + /** hprec_release */ + void hprec_release(hazptr_rec<Atom>* hprec) noexcept { + hprec->release(); + } + + /** push_retired */ + void push_retired(hazptr_obj_list<Atom>& l, bool check = true) { + /*** Full fence ***/ asymmetricLightBarrier(); + while (true) { + auto r = retired(); + l.tail()->set_next(r); + if (retired_.compare_exchange_weak( + r, + l.head(), + std::memory_order_release, + std::memory_order_acquire)) { + break; + } + } + rcount_.fetch_add(l.count(), std::memory_order_release); + if (check) { + check_cleanup_and_reclaim(); + } + } + + /** push_list */ + void push_list(ObjList& l) { + if (l.empty()) { + return; + } + uintptr_t btag = l.head()->cohort_tag(); + bool tagged = ((btag & kTagBit) == kTagBit); + RetiredList& rlist = tagged ? tagged_ : untagged_; + Atom<uint64_t>& sync_time = + tagged ? tagged_sync_time_ : untagged_sync_time_; + /*** Full fence ***/ asymmetricLightBarrier(); + /* Only tagged lists need to be locked because tagging is used to + * guarantee the identification of all objects with a specific + * tag. Locking protects against concurrent hazptr_cleanup_tag() + * calls missing tagged objects. */ + bool lock = + tagged ? RetiredList::kMayBeLocked : RetiredList::kMayNotBeLocked; + rlist.push(l, lock); + check_threshold_and_reclaim(rlist, lock, sync_time); + } + + /** threshold */ + int threshold() { + auto thresh = kThreshold; + return std::max(thresh, kMultiplier * hcount()); + } + + /** check_threshold_and_reclaim */ + void check_threshold_and_reclaim( + RetiredList& rlist, + bool lock, + Atom<uint64_t>& sync_time) { + if (!(lock && rlist.check_lock()) && + (rlist.check_threshold_try_zero_count(threshold()) || + check_sync_time(sync_time))) { + if (std::is_same<Atom<int>, std::atomic<int>>{} && + this == &default_hazptr_domain<Atom>() && + FLAGS_folly_hazptr_use_executor) { + invoke_reclamation_in_executor(rlist, lock); + } else { + do_reclamation(rlist, lock); + } + } + } + + /** check_sync_time_and_reclaim **/ + void check_sync_time_and_reclaim() { + if (!tagged_.check_lock() && check_sync_time()) { + do_reclamation(tagged_, RetiredList::kAlsoLock); + do_reclamation(untagged_, RetiredList::kDontLock); + } + } + + /** do_reclamation */ + void do_reclamation(RetiredList& rlist, bool lock) { + auto obj = rlist.pop_all(lock == RetiredList::kAlsoLock); + if (!obj) { + if (lock) { + ObjList l; + rlist.push_unlock(l); + } + return; + } + /*** Full fence ***/ asymmetricHeavyBarrier(AMBFlags::EXPEDITED); + auto hprec = hazptrs_.load(std::memory_order_acquire); + /* Read hazard pointer values into private search structure */ + Set hs; + for (; hprec; hprec = hprec->next()) { + hs.insert(hprec->hazptr()); + } + /* Check objects against hazard pointer values */ + ObjList match, nomatch; + list_match_condition(obj, match, nomatch, [&](Obj* o) { + return hs.count(o->raw_ptr()) > 0; + }); + /* Reclaim unprotected objects and push back protected objects and + children of reclaimed objects */ + if (lock) { + unprotected_ = nomatch.head(); + DCHECK(children_.empty()); + reclaim_unprotected_safe(); + match.splice(children_); + rlist.push_unlock(match); + } else { + ObjList children; + reclaim_unprotected_unsafe(nomatch.head(), children); + match.splice(children); + rlist.push(match, false); + } + } + + /** lookup_and_reclaim */ + void lookup_and_reclaim(Obj* obj, const Set& hs, ObjList& keep) { + while (obj) { + auto next = obj->next(); + DCHECK_NE(obj, next); + if (hs.count(obj->raw_ptr()) == 0) { + (*(obj->reclaim()))(obj, keep); + } else { + keep.push(obj); + } + obj = next; + } + } + + /** list_match_condition */ + template <typename Cond> + void list_match_condition( + Obj* obj, + ObjList& match, + ObjList& nomatch, + const Cond& cond) { + while (obj) { + auto next = obj->next(); + DCHECK_NE(obj, next); + if (cond(obj)) { + match.push(obj); + } else { + nomatch.push(obj); + } + obj = next; + } + } + + /** reclaim_unprotected_safe */ + void reclaim_unprotected_safe() { + while (unprotected_) { + auto obj = unprotected_; + unprotected_ = obj->next(); + (*(obj->reclaim()))(obj, children_); + } + } + + /** reclaim_unprotected_unsafe */ + void reclaim_unprotected_unsafe(Obj* obj, ObjList& children) { + while (obj) { + auto next = obj->next(); + (*(obj->reclaim()))(obj, children); + obj = next; + } + } + + /** reclaim_unconditional */ + void reclaim_unconditional(Obj* head, ObjList& children) { + while (head) { + auto next = head->next(); + (*(head->reclaim()))(head, children); + head = next; + } + } + + hazptr_rec<Atom>* head() const noexcept { + return hazptrs_.load(std::memory_order_acquire); + } + + hazptr_obj<Atom>* retired() const noexcept { + return retired_.load(std::memory_order_acquire); + } + + int hcount() const noexcept { + return hcount_.load(std::memory_order_acquire); + } + + int rcount() const noexcept { + return rcount_.load(std::memory_order_acquire); + } + + bool reached_threshold(int rc, int hc) const noexcept { + return rc >= kThreshold && rc >= kMultiplier * hc; + } + + void reclaim_all_objects() { + auto head = retired_.exchange(nullptr); + reclaim_list_transitive(head); + head = untagged_.pop_all(RetiredList::kDontLock); + reclaim_list_transitive(head); + } + + void reclaim_list_transitive(Obj* head) { + while (head) { + ObjList children; + reclaim_unconditional(head, children); + head = children.head(); + } + } + + void free_hazptr_recs() { + /* Leak the hazard pointers for the default domain to avoid + destruction order issues with thread caches. */ + if (this == &default_hazptr_domain<Atom>()) { + return; + } + auto rec = head(); + while (rec) { + auto next = rec->next(); + DCHECK(!rec->active()); + rec->~hazptr_rec<Atom>(); + hazptr_rec_alloc{}.deallocate(rec, 1); + rec = next; + } + } + + void check_cleanup_and_reclaim() { + if (try_timed_cleanup()) { + return; + } + if (reached_threshold(rcount(), hcount())) { + try_bulk_reclaim(); + } + } + + void relaxed_cleanup() noexcept { +#if FOLLY_HAZPTR_THR_LOCAL + hazptr_obj<Atom>* h = nullptr; + hazptr_obj<Atom>* t = nullptr; + for (hazptr_priv<Atom>& priv : + hazptr_priv_singleton<Atom>::accessAllThreads()) { + priv.collect(h, t); + } + if (h) { + DCHECK(t); + hazptr_obj_list<Atom> l(h, t, 0); + push_retired(l); + } +#endif + rcount_.store(0, std::memory_order_release); + bulk_reclaim(true); + } + + void wait_for_zero_bulk_reclaims() { + while (num_bulk_reclaims_.load(std::memory_order_acquire) > 0) { + std::this_thread::yield(); + } + } + + void try_bulk_reclaim() { + auto hc = hcount(); + auto rc = rcount(); + if (!reached_threshold(rc, hc)) { + return; + } + rc = rcount_.exchange(0, std::memory_order_release); + if (!reached_threshold(rc, hc)) { + /* No need to add rc back to rcount_. At least one concurrent + try_bulk_reclaim will proceed to bulk_reclaim. */ + return; + } + bulk_reclaim(); + } + + void bulk_reclaim(bool transitive = false) { + num_bulk_reclaims_.fetch_add(1, std::memory_order_acquire); + while (true) { + auto obj = retired_.exchange(nullptr, std::memory_order_acquire); + /*** Full fence ***/ asymmetricHeavyBarrier(AMBFlags::EXPEDITED); + auto rec = hazptrs_.load(std::memory_order_acquire); + /* Part 1 - read hazard pointer values into private search structure */ + std::unordered_set<const void*> hashset; // TOTO: lock-free fixed hash set + for (; rec; rec = rec->next()) { + hashset.insert(rec->hazptr()); + } + /* Part 2 - for each retired object, reclaim if no match */ + if (bulk_lookup_and_reclaim(obj, hashset) || !transitive) { + break; + } + } + num_bulk_reclaims_.fetch_sub(1, std::memory_order_release); + } + + bool bulk_lookup_and_reclaim( + hazptr_obj<Atom>* obj, + const std::unordered_set<const void*>& hashset) { + hazptr_obj_list<Atom> children; + hazptr_obj_list<Atom> matched; + while (obj) { + auto next = obj->next(); + DCHECK_NE(obj, next); + if (hashset.count(obj->raw_ptr()) == 0) { + (*(obj->reclaim()))(obj, children); + } else { + matched.push(obj); + } + obj = next; + } +#if FOLLY_HAZPTR_THR_LOCAL + if (!shutdown_) { + hazptr_priv_tls<Atom>().push_all_to_domain(false); + } +#endif + bool done = ((children.count() == 0) && (retired() == nullptr)); + matched.splice(children); + if (matched.count() > 0) { + push_retired(matched, false /* don't call bulk_reclaim recursively */); + } + return done; + } + + bool check_sync_time(Atom<uint64_t>& sync_time) { + uint64_t time = std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + auto prevtime = sync_time.load(std::memory_order_relaxed); + if (time < prevtime || + !sync_time.compare_exchange_strong( + prevtime, time + kSyncTimePeriod, std::memory_order_relaxed)) { + return false; + } + return true; + } + + bool try_timed_cleanup() { + if (!check_sync_time(sync_time_)) { + return false; + } + relaxed_cleanup(); // calling regular cleanup may self deadlock + return true; + } + + hazptr_rec<Atom>* try_acquire_existing_hprec() { + auto rec = head(); + while (rec) { + auto next = rec->next(); + if (rec->try_acquire()) { + return rec; + } + rec = next; + } + return nullptr; + } + + hazptr_rec<Atom>* acquire_new_hprec() { + auto rec = hazptr_rec_alloc{}.allocate(1); + new (rec) hazptr_rec<Atom>(); + rec->set_active(); + rec->set_domain(this); + while (true) { + auto h = head(); + rec->set_next(h); + if (hazptrs_.compare_exchange_weak( + h, rec, std::memory_order_release, std::memory_order_acquire)) { + break; + } + } + hcount_.fetch_add(1); + return rec; + } + + void invoke_reclamation_in_executor(RetiredList& rlist, bool lock) { + auto fn = exec_fn_.load(std::memory_order_acquire); + auto ex = fn ? fn() : get_default_executor(); + auto backlog = exec_backlog_.fetch_add(1, std::memory_order_relaxed); + if (ex) { + ex->add([this, &rlist, lock] { + exec_backlog_.store(0, std::memory_order_relaxed); + do_reclamation(rlist, lock); + }); + } else { + LOG(INFO) << "Skip asynchronous reclamation by hazptr executor"; + } + if (backlog >= 10) { + LOG(WARNING) << backlog + << " request backlog for hazptr reclamation executora"; + } + } +}; // hazptr_domain + +/** + * Free functions related to hazptr domains + */ + +/** default_hazptr_domain: Returns reference to the default domain */ + +template <template <typename> class Atom> +struct hazptr_default_domain_helper { + static FOLLY_ALWAYS_INLINE hazptr_domain<Atom>& get() { + static hazptr_domain<Atom> domain; + return domain; + } +}; + +template <> +struct hazptr_default_domain_helper<std::atomic> { + static FOLLY_ALWAYS_INLINE hazptr_domain<std::atomic>& get() { + return default_domain; + } +}; + +template <template <typename> class Atom> +FOLLY_ALWAYS_INLINE hazptr_domain<Atom>& default_hazptr_domain() { + return hazptr_default_domain_helper<Atom>::get(); +} + +/** hazptr_domain_push_retired: push a list of retired objects into a domain */ +template <template <typename> class Atom> +void hazptr_domain_push_retired( + hazptr_obj_list<Atom>& l, + bool check, + hazptr_domain<Atom>& domain) noexcept { + domain.push_retired(l, check); +} + +/** hazptr_domain_push_list */ +template <template <typename> class Atom> +void hazptr_domain_push_list( + hazptr_obj_list<Atom>& l, + hazptr_domain<Atom>& domain) noexcept { + domain.push_list(l); +} + +/** hazptr_retire */ +template <template <typename> class Atom, typename T, typename D> +FOLLY_ALWAYS_INLINE void hazptr_retire(T* obj, D reclaim) { + default_hazptr_domain<Atom>().retire(obj, std::move(reclaim)); +} + +/** hazptr_cleanup: Reclaims all reclaimable objects retired to the domain */ +template <template <typename> class Atom> +void hazptr_cleanup(hazptr_domain<Atom>& domain) noexcept { + domain.cleanup(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrHolder.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrHolder.h new file mode 100644 index 000000000..d66e9cab0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrHolder.h @@ -0,0 +1,403 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Traits.h> + +#include <folly/synchronization/Hazptr-fwd.h> +#include <folly/synchronization/HazptrDomain.h> +#include <folly/synchronization/HazptrRec.h> +#include <folly/synchronization/HazptrThrLocal.h> + +#include <folly/synchronization/AsymmetricMemoryBarrier.h> + +namespace folly { + +/// +/// Classes related to hazard pointer holders. +/// + +/** + * hazptr_holder + * + * Class for automatic acquisition and release of hazard pointers, + * and interface for hazard pointer operations. + * + * Usage example: + * T* ptr; + * { + * hazptr_holder h; + * ptr = h.get_protected(src); + * // ... *ptr is protected ... + * h.reset(); + * // ... *ptr is not protected ... + * ptr = src.load(); + * while (!h.try_protect(ptr, src)) {} + * // ... *ptr is protected ... + * } + * // ... *ptr is not protected + */ +template <template <typename> class Atom> +class hazptr_holder { + hazptr_rec<Atom>* hprec_; + + public: + /** Constructor - automatically acquires a hazard pointer. */ + FOLLY_ALWAYS_INLINE explicit hazptr_holder( + hazptr_domain<Atom>& domain = default_hazptr_domain<Atom>()) { +#if FOLLY_HAZPTR_THR_LOCAL + if (LIKELY(&domain == &default_hazptr_domain<Atom>())) { + auto hprec = hazptr_tc_tls<Atom>().try_get(); + if (LIKELY(hprec != nullptr)) { + hprec_ = hprec; + return; + } + } +#endif + hprec_ = domain.hprec_acquire(); + } + + /** Empty constructor */ + FOLLY_ALWAYS_INLINE explicit hazptr_holder(std::nullptr_t) noexcept + : hprec_(nullptr) {} + + /** Move constructor */ + FOLLY_ALWAYS_INLINE hazptr_holder(hazptr_holder&& rhs) noexcept { + hprec_ = rhs.hprec_; + rhs.hprec_ = nullptr; + } + + hazptr_holder(const hazptr_holder&) = delete; + hazptr_holder& operator=(const hazptr_holder&) = delete; + + /** Destructor */ + FOLLY_ALWAYS_INLINE ~hazptr_holder() { + if (LIKELY(hprec_ != nullptr)) { + hprec_->reset_hazptr(); + auto domain = hprec_->domain(); +#if FOLLY_HAZPTR_THR_LOCAL + if (LIKELY(domain == &default_hazptr_domain<Atom>())) { + if (LIKELY(hazptr_tc_tls<Atom>().try_put(hprec_))) { + return; + } + } +#endif + domain->hprec_release(hprec_); + } + } + + /** Move operator */ + FOLLY_ALWAYS_INLINE hazptr_holder& operator=(hazptr_holder&& rhs) noexcept { + /* Self-move is a no-op. */ + if (LIKELY(this != &rhs)) { + this->~hazptr_holder(); + new (this) hazptr_holder(nullptr); + hprec_ = rhs.hprec_; + rhs.hprec_ = nullptr; + } + return *this; + } + + /** Hazard pointer operations */ + + /** try_protect */ + template <typename T> + FOLLY_ALWAYS_INLINE bool try_protect(T*& ptr, const Atom<T*>& src) noexcept { + return try_protect(ptr, src, [](T* t) { return t; }); + } + + template <typename T, typename Func> + FOLLY_ALWAYS_INLINE bool + try_protect(T*& ptr, const Atom<T*>& src, Func f) noexcept { + /* Filtering the protected pointer through function Func is useful + for stealing bits of the pointer word */ + auto p = ptr; + reset(f(p)); + /*** Full fence ***/ folly::asymmetricLightBarrier(); + ptr = src.load(std::memory_order_acquire); + if (UNLIKELY(p != ptr)) { + reset(); + return false; + } + return true; + } + + /** get_protected */ + template <typename T> + FOLLY_ALWAYS_INLINE T* get_protected(const Atom<T*>& src) noexcept { + return get_protected(src, [](T* t) { return t; }); + } + + template <typename T, typename Func> + FOLLY_ALWAYS_INLINE T* get_protected(const Atom<T*>& src, Func f) noexcept { + T* ptr = src.load(std::memory_order_relaxed); + while (!try_protect(ptr, src, f)) { + /* Keep trying */; + } + return ptr; + } + + /** reset */ + template <typename T> + FOLLY_ALWAYS_INLINE void reset(const T* ptr) noexcept { + auto p = static_cast<hazptr_obj<Atom>*>(const_cast<T*>(ptr)); + DCHECK(hprec_); // UB if *this is empty + hprec_->reset_hazptr(p); + } + + FOLLY_ALWAYS_INLINE void reset(std::nullptr_t = nullptr) noexcept { + DCHECK(hprec_); // UB if *this is empty + hprec_->reset_hazptr(); + } + + /* Swap ownership of hazard pointers between hazptr_holder-s. */ + /* Note: The owned hazard pointers remain unmodified during the swap + * and continue to protect the respective objects that they were + * protecting before the swap, if any. */ + FOLLY_ALWAYS_INLINE void swap(hazptr_holder<Atom>& rhs) noexcept { + std::swap(this->hprec_, rhs.hprec_); + } + + /** Returns a pointer to the owned hazptr_rec */ + FOLLY_ALWAYS_INLINE hazptr_rec<Atom>* hprec() const noexcept { + return hprec_; + } + + /** Set the pointer to the owned hazptr_rec */ + FOLLY_ALWAYS_INLINE void set_hprec(hazptr_rec<Atom>* hprec) noexcept { + hprec_ = hprec; + } +}; // hazptr_holder + +/** + * Free function swap of hazptr_holder-s. + */ +template <template <typename> class Atom> +FOLLY_ALWAYS_INLINE void swap( + hazptr_holder<Atom>& lhs, + hazptr_holder<Atom>& rhs) noexcept { + lhs.swap(rhs); +} + +/** + * Type used by hazptr_array and hazptr_local. + */ +template <template <typename> class Atom> +using aligned_hazptr_holder = aligned_storage_for_t<hazptr_holder<Atom>>; + +/** + * hazptr_array + * + * Optimized template for bulk construction and destruction of hazard + * pointers. + * + * WARNING: Do not move from or to individual hazptr_holder-s. + * Only move the whole hazptr_array. + * + * NOTE: It is allowed to swap an individual hazptr_holder that + * belongs to hazptr_array with (a) a hazptr_holder object, or (b) a + * hazptr_holder that is part of hazptr_array, under the conditions: + * (i) both hazptr_holder-s are either both empty or both nonempty + * and (ii) both belong to the same domain. + */ +template <uint8_t M, template <typename> class Atom> +class hazptr_array { + static_assert(M > 0, "M must be a positive integer."); + + aligned_hazptr_holder<Atom> raw_[M]; + bool empty_{false}; + + public: + /** Constructor */ + FOLLY_ALWAYS_INLINE hazptr_array() { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); +#if FOLLY_HAZPTR_THR_LOCAL + static_assert( + M <= hazptr_tc<Atom>::capacity(), + "M must be within the thread cache capacity."); + auto& tc = hazptr_tc_tls<Atom>(); + auto count = tc.count(); + if (UNLIKELY(M > count)) { + tc.fill(M - count); + count = M; + } + uint8_t offset = count - M; + for (uint8_t i = 0; i < M; ++i) { + auto hprec = tc[offset + i].get(); + DCHECK(hprec != nullptr); + new (&h[i]) hazptr_holder<Atom>(nullptr); + h[i].set_hprec(hprec); + } + tc.set_count(offset); +#else + for (uint8_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder<Atom>; + } +#endif + } + + /** Empty constructor */ + FOLLY_ALWAYS_INLINE hazptr_array(std::nullptr_t) noexcept { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); + for (uint8_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder<Atom>(nullptr); + } + empty_ = true; + } + + /** Move constructor */ + FOLLY_ALWAYS_INLINE hazptr_array(hazptr_array&& other) noexcept { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); + auto hother = reinterpret_cast<hazptr_holder<Atom>*>(&other.raw_); + for (uint8_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder<Atom>(std::move(hother[i])); + } + empty_ = other.empty_; + other.empty_ = true; + } + + hazptr_array(const hazptr_array&) = delete; + hazptr_array& operator=(const hazptr_array&) = delete; + + /** Destructor */ + FOLLY_ALWAYS_INLINE ~hazptr_array() { + if (empty_) { + return; + } + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); +#if FOLLY_HAZPTR_THR_LOCAL + auto& tc = hazptr_tc_tls<Atom>(); + auto count = tc.count(); + auto cap = hazptr_tc<Atom>::capacity(); + if (UNLIKELY((M + count) > cap)) { + tc.evict((M + count) - cap); + count = cap - M; + } + for (uint8_t i = 0; i < M; ++i) { + h[i].reset(); + tc[count + i].fill(h[i].hprec()); + new (&h[i]) hazptr_holder<Atom>(nullptr); + } + tc.set_count(count + M); +#else + for (uint8_t i = 0; i < M; ++i) { + h[i].~hazptr_holder(); + } +#endif + } + + /** Move operator */ + FOLLY_ALWAYS_INLINE hazptr_array& operator=(hazptr_array&& other) noexcept { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); + for (uint8_t i = 0; i < M; ++i) { + h[i] = std::move(other[i]); + } + empty_ = other.empty_; + other.empty_ = true; + return *this; + } + + /** [] operator */ + FOLLY_ALWAYS_INLINE hazptr_holder<Atom>& operator[](uint8_t i) noexcept { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); + DCHECK(i < M); + return h[i]; + } +}; // hazptr_array + +/** + * hazptr_local + * + * Optimized for construction and destruction of one or more + * hazptr_holder-s with local scope. + * + * WARNING 1: Do not move from or to individual hazptr_holder-s. + * + * WARNING 2: There can only be one hazptr_local active for the same + * thread at any time. This is not tracked and checked by the + * implementation (except in debug mode) because it would negate the + * performance gains of this class. + */ +template <uint8_t M, template <typename> class Atom> +class hazptr_local { + static_assert(M > 0, "M must be a positive integer."); + + aligned_hazptr_holder<Atom> raw_[M]; + + public: + /** Constructor */ + FOLLY_ALWAYS_INLINE hazptr_local() { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); +#if FOLLY_HAZPTR_THR_LOCAL + static_assert( + M <= hazptr_tc<Atom>::capacity(), + "M must be <= hazptr_tc::capacity()."); + auto& tc = hazptr_tc_tls<Atom>(); + auto count = tc.count(); + if (UNLIKELY(M > count)) { + tc.fill(M - count); + } + if (kIsDebug) { + DCHECK(!tc.local()); + tc.set_local(true); + } + for (uint8_t i = 0; i < M; ++i) { + auto hprec = tc[i].get(); + DCHECK(hprec != nullptr); + new (&h[i]) hazptr_holder<Atom>(nullptr); + h[i].set_hprec(hprec); + } +#else + for (uint8_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder<Atom>; + } +#endif + } + + hazptr_local(const hazptr_local&) = delete; + hazptr_local& operator=(const hazptr_local&) = delete; + hazptr_local(hazptr_local&&) = delete; + hazptr_local& operator=(hazptr_local&&) = delete; + + /** Destructor */ + FOLLY_ALWAYS_INLINE ~hazptr_local() { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); +#if FOLLY_HAZPTR_THR_LOCAL + if (kIsDebug) { + auto& tc = hazptr_tc_tls<Atom>(); + DCHECK(tc.local()); + tc.set_local(false); + } + for (uint8_t i = 0; i < M; ++i) { + h[i].reset(); + } +#else + for (uint8_t i = 0; i < M; ++i) { + h[i].~hazptr_holder(); + } +#endif + } + + /** [] operator */ + FOLLY_ALWAYS_INLINE hazptr_holder<Atom>& operator[](uint8_t i) noexcept { + auto h = reinterpret_cast<hazptr_holder<Atom>*>(&raw_); + DCHECK(i < M); + return h[i]; + } +}; // hazptr_local + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObj.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObj.h new file mode 100644 index 000000000..69a375208 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObj.h @@ -0,0 +1,553 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> +#include <folly/synchronization/detail/HazptrUtils.h> + +#include <folly/CPortability.h> +#include <folly/Portability.h> +#include <folly/concurrency/CacheLocality.h> + +#include <glog/logging.h> + +#include <atomic> +#include <memory> + +/// +/// Classes related to objects protected by hazard pointers. +/// + +namespace folly { + +/** + * hazptr_obj + * + * Private base class for objects protected by hazard pointers. + * + * Data members: + * - next_: link to next object in private singly linked lists. + * - reclaim_: reclamation function for this object. + * - cohort_tag_: A pointer to a cohort (a linked list where the + * object is to be pushed when retired). It can also be used as a + * tag (see below). See details below. + * + * Cohorts, Tags, Tagged Objects, and Untagged Objects: + * + * - Cohorts: Cohorts (instances of hazptr_obj_cohort) are sets of + * retired hazptr_obj-s. Cohorts are used to keep related objects + * together instead of being spread across thread local structures + * and/or mixed with unrelated objects. + * + * - Tags: A tag is a unique identifier used for fast identification + * of related objects. Tags are implemented as addresses of + * cohorts, with the lowest bit set (to save the space of separate + * cohort and tag data members and to differentiate from cohorts of + * untagged objects. + * + * - Tagged objects: Objects are tagged for fast identification. The + * primary use case is for guaranteeing the destruction of all + * objects with a certain tag (e.g., the destruction of all Key and + * Value objects that were part of a Folly ConcurrentHashMap + * instance). Member function set_cohort_tag makes an object tagged. + * + * - Untagged objects: Objects that do not need to be identified + * separately from unrelated objects are not tagged (to keep tagged + * objects uncluttered). Untagged objects may or may not be + * associated with cohorts. An example of untagged objects + * associated with cohorts are Segment-s of Folly UnboundedQueue. + * Although such objects do not need to be tagged, keeping them in + * cohorts helps avoid cases of a few missing objects delaying the + * reclamation of large numbers of link-counted objects. Objects + * are untagged either by default or after calling + * set_cohort_no_tag. + * + * - Thread Safety: Member functions set_cohort_tag and + * set_cohort_no_tag are not thread-safe. Thread safety must be + * ensured by the calling thread. + */ +template <template <typename> class Atom> +class hazptr_obj { + using ReclaimFnPtr = void (*)(hazptr_obj<Atom>*, hazptr_obj_list<Atom>&); + + template <template <typename> class> + friend class hazptr_domain; + template <typename, template <typename> class, typename> + friend class hazptr_obj_base; + template <typename, template <typename> class, typename> + friend class hazptr_obj_base_linked; + template <template <typename> class> + friend class hazptr_obj_list; + template <template <typename> class> + friend class hazptr_priv; + friend class hazptr_detail::linked_list<hazptr_obj<Atom>>; + friend class hazptr_detail::shared_head_only_list<hazptr_obj<Atom>, Atom>; + friend class hazptr_detail::shared_head_tail_list<hazptr_obj<Atom>, Atom>; + + static constexpr uintptr_t kTagBit = 1u; + + ReclaimFnPtr reclaim_; + hazptr_obj<Atom>* next_; + uintptr_t cohort_tag_; + + public: + /** Constructors */ + /* All constructors set next_ to this in order to catch misuse bugs + such as double retire. By default, objects are untagged and not + associated with a cohort. */ + + hazptr_obj() noexcept : next_(this), cohort_tag_(0) {} + + hazptr_obj(const hazptr_obj<Atom>& o) noexcept + : next_(this), cohort_tag_(o.cohort_tag_) {} + + hazptr_obj(hazptr_obj<Atom>&& o) noexcept + : next_(this), cohort_tag_(o.cohort_tag_) {} + + /** Copy operator */ + hazptr_obj<Atom>& operator=(const hazptr_obj<Atom>&) noexcept { + return *this; + } + + /** Move operator */ + hazptr_obj<Atom>& operator=(hazptr_obj<Atom>&&) noexcept { + return *this; + } + + /** cohort_tag */ + uintptr_t cohort_tag() { + return cohort_tag_; + } + + /** cohort */ + hazptr_obj_cohort<Atom>* cohort() { + uintptr_t btag = cohort_tag_; + btag -= btag & kTagBit; + return reinterpret_cast<hazptr_obj_cohort<Atom>*>(btag); + } + + /** tagged */ + bool tagged() { + return (cohort_tag_ & kTagBit) == kTagBit; + } + + /** set_cohort_tag: Set cohort and make object tagged. */ + void set_cohort_tag(hazptr_obj_cohort<Atom>* cohort) { + cohort_tag_ = reinterpret_cast<uintptr_t>(cohort) + kTagBit; + } + + /** set_cohort_no_tag: Set cohort and make object untagged. */ + void set_cohort_no_tag(hazptr_obj_cohort<Atom>* cohort) { + cohort_tag_ = reinterpret_cast<uintptr_t>(cohort); + } + + private: + friend class hazptr_domain<Atom>; + template <typename, template <typename> class, typename> + friend class hazptr_obj_base; + template <typename, template <typename> class, typename> + friend class hazptr_obj_base_refcounted; + friend class hazptr_obj_cohort<Atom>; + friend class hazptr_priv<Atom>; + + hazptr_obj<Atom>* next() const noexcept { + return next_; + } + + void set_next(hazptr_obj* obj) noexcept { + next_ = obj; + } + + ReclaimFnPtr reclaim() noexcept { + return reclaim_; + } + + const void* raw_ptr() const { + return this; + } + + void pre_retire_check() noexcept { + // Only for catching misuse bugs like double retire + if (next_ != this) { + pre_retire_check_fail(); + } + } + + void push_obj(hazptr_domain<Atom>& domain) { + auto coh = cohort(); + if (coh) { + DCHECK_EQ(&domain, &default_hazptr_domain<Atom>()); + coh->push_obj(this); + } else { + push_to_retired(domain); + } + } + + void push_to_retired(hazptr_domain<Atom>& domain) { +#if FOLLY_HAZPTR_THR_LOCAL + if (&domain == &default_hazptr_domain<Atom>() && !domain.shutdown_) { + hazptr_priv_tls<Atom>().push(this); + return; + } +#endif + hazptr_obj_list<Atom> l(this); + hazptr_domain_push_retired(l, true, domain); + } + + FOLLY_NOINLINE void pre_retire_check_fail() noexcept { + CHECK_EQ(next_, this); + } +}; // hazptr_obj + +/** + * hazptr_obj_list + * + * List of hazptr_obj-s. + */ +template <template <typename> class Atom> +class hazptr_obj_list { + using Obj = hazptr_obj<Atom>; + using List = hazptr_detail::linked_list<Obj>; + + List l_; + int count_; + + public: + hazptr_obj_list() noexcept : l_(nullptr, nullptr), count_(0) {} + + explicit hazptr_obj_list(Obj* obj) noexcept : l_(obj, obj), count_(1) { + obj->set_next(nullptr); + } + + explicit hazptr_obj_list(Obj* head, Obj* tail, int count) noexcept + : l_(head, tail), count_(count) {} + + Obj* head() const noexcept { + return l_.head(); + } + + Obj* tail() const noexcept { + return l_.tail(); + } + + int count() const noexcept { + return count_; + } + + void set_count(int val) { + count_ = val; + } + + bool empty() const noexcept { + return head() == nullptr; + } + + void push(Obj* obj) { + l_.push(obj); + ++count_; + } + + void splice(hazptr_obj_list<Atom>& l) { + if (l.count() == 0) { + return; + } + l_.splice(l.l_); + count_ += l.count(); + l.clear(); + } + + void clear() { + l_.clear(); + count_ = 0; + } +}; // hazptr_obj_list + +/** + * hazptr_obj_cohort + * + * List of retired objects. For objects to be retred to a cohort, + * either of the hazptr_obj member functions set_cohort_tag or + * set_cohort_no_tag needs to be called before the object is retired. + * + * See description of hazptr_obj for notes on cohorts, tags, and + * tageed and untagged objects. + * + * [Note: For now supports only the default domain.] + */ +template <template <typename> class Atom> +class hazptr_obj_cohort { + using Obj = hazptr_obj<Atom>; + using List = hazptr_detail::linked_list<Obj>; + using SharedList = hazptr_detail::shared_head_tail_list<Obj, Atom>; + + static constexpr int kThreshold = 20; + + SharedList l_; + Atom<int> count_; + bool active_; + Atom<bool> pushed_to_domain_tagged_; + + public: + /** Constructor */ + hazptr_obj_cohort() noexcept + : l_(), count_(0), active_(true), pushed_to_domain_tagged_{false} {} + + /** Not copyable or moveable */ + hazptr_obj_cohort(const hazptr_obj_cohort& o) = delete; + hazptr_obj_cohort(hazptr_obj_cohort&& o) = delete; + hazptr_obj_cohort& operator=(const hazptr_obj_cohort&& o) = delete; + hazptr_obj_cohort& operator=(hazptr_obj_cohort&& o) = delete; + + /** Destructor */ + ~hazptr_obj_cohort() { + if (active_) { + shutdown_and_reclaim(); + } + DCHECK(!active_); + DCHECK(l_.empty()); + } + + /** shutdown_and_reclaim */ + void shutdown_and_reclaim() { + DCHECK(active_); + active_ = false; + if (!l_.empty()) { + List l = l_.pop_all(); + clear_count(); + Obj* obj = l.head(); + reclaim_list(obj); + } + if (pushed_to_domain_tagged_.load(std::memory_order_relaxed)) { + default_hazptr_domain<Atom>().cleanup_cohort_tag(this); + } + DCHECK(l_.empty()); + } + + private: + friend class hazptr_obj<Atom>; + + int count() const noexcept { + return count_.load(std::memory_order_acquire); + } + + void clear_count() noexcept { + count_.store(0, std::memory_order_release); + } + + void inc_count() noexcept { + count_.fetch_add(1, std::memory_order_release); + } + + bool cas_count(int& expected, int newval) noexcept { + return count_.compare_exchange_weak( + expected, newval, std::memory_order_acq_rel, std::memory_order_acquire); + } + + /** push_obj */ + void push_obj(Obj* obj) { + if (active_) { + l_.push(obj); + inc_count(); + check_threshold_push(); + } else { + obj->set_next(nullptr); + reclaim_list(obj); + } + } + + /** reclaim_list */ + void reclaim_list(hazptr_obj<Atom>* obj) { + while (obj) { + hazptr_obj_list<Atom> children; + while (obj) { + Obj* next = obj->next(); + (*(obj->reclaim()))(obj, children); + obj = next; + } + obj = children.head(); + } + } + + /** check_threshold_push */ + void check_threshold_push() { + auto c = count(); + while (c >= kThreshold) { + if (cas_count(c, 0)) { + List ll = l_.pop_all(); + if (ll.head() && ll.head()->tagged()) { + pushed_to_domain_tagged_.store(true, std::memory_order_relaxed); + } + if (kIsDebug) { + Obj* p = ll.head(); + for (int i = 1; p; ++i, p = p->next()) { + DCHECK_EQ(reinterpret_cast<uintptr_t>(p) & 7, uintptr_t{0}) + << p << " " << i; + } + } + hazptr_obj_list<Atom> l(ll.head(), ll.tail(), c); + hazptr_domain_push_list<Atom>(l); + return; + } + } + } +}; // hazptr_obj_cohort + +/** + * hazptr_obj_retired_list + * + * Used for maintaining lists of retired objects in domain + * structure. Locked operations are used for lists of tagged + * objects. Unlocked operations are used for the untagged list. + */ +/** hazptr_obj_retired_list */ +template <template <typename> class Atom> +class hazptr_obj_retired_list { + using Obj = hazptr_obj<Atom>; + using List = hazptr_detail::linked_list<Obj>; + using RetiredList = hazptr_detail::shared_head_only_list<Obj, Atom>; + + alignas(hardware_destructive_interference_size) RetiredList retired_; + Atom<int> count_; + + public: + static constexpr bool kAlsoLock = RetiredList::kAlsoLock; + static constexpr bool kDontLock = RetiredList::kDontLock; + static constexpr bool kMayBeLocked = RetiredList::kMayBeLocked; + static constexpr bool kMayNotBeLocked = RetiredList::kMayNotBeLocked; + + public: + hazptr_obj_retired_list() noexcept : count_(0) {} + + void push(hazptr_obj_list<Atom>& l, bool may_be_locked) noexcept { + List ll(l.head(), l.tail()); + retired_.push(ll, may_be_locked); + add_count(l.count()); + } + + void push_unlock(hazptr_obj_list<Atom>& l) noexcept { + List ll(l.head(), l.tail()); + retired_.push_unlock(ll); + auto count = l.count(); + if (count) { + add_count(count); + } + } + + int count() const noexcept { + return count_.load(std::memory_order_acquire); + } + + bool empty() { + return retired_.empty(); + } + + bool check_threshold_try_zero_count(int thresh) { + auto oldval = count(); + while (oldval >= thresh) { + if (cas_count(oldval, 0)) { + return true; + } + } + return false; + } + + Obj* pop_all(bool lock) { + return retired_.pop_all(lock); + } + + bool check_lock() { + return retired_.check_lock(); + } + + private: + void add_count(int val) { + count_.fetch_add(val, std::memory_order_release); + } + + bool cas_count(int& expected, int newval) { + return count_.compare_exchange_weak( + expected, newval, std::memory_order_acq_rel, std::memory_order_acquire); + } +}; // hazptr_obj_retired_list + +/** + * hazptr_deleter + * + * For empty base optimization. + */ +template <typename T, typename D> +class hazptr_deleter { + D deleter_; + + public: + void set_deleter(D d = {}) { + deleter_ = std::move(d); + } + + void delete_obj(T* p) { + deleter_(p); + } +}; + +template <typename T> +class hazptr_deleter<T, std::default_delete<T>> { + public: + void set_deleter(std::default_delete<T> = {}) {} + + void delete_obj(T* p) { + delete p; + } +}; + +/** + * hazptr_obj_base + * + * Base template for objects protected by hazard pointers. + */ +template <typename T, template <typename> class Atom, typename D> +class hazptr_obj_base : public hazptr_obj<Atom>, public hazptr_deleter<T, D> { + public: + /* Retire a removed object and pass the responsibility for + * reclaiming it to the hazptr library */ + void retire( + D deleter = {}, + hazptr_domain<Atom>& domain = default_hazptr_domain<Atom>()) { + pre_retire(std::move(deleter)); + set_reclaim(); + this->push_obj(domain); // defined in hazptr_obj + } + + void retire(hazptr_domain<Atom>& domain) { + retire({}, domain); + } + + private: + void pre_retire(D deleter) { + this->pre_retire_check(); // defined in hazptr_obj + this->set_deleter(std::move(deleter)); + } + + void set_reclaim() { + this->reclaim_ = [](hazptr_obj<Atom>* p, hazptr_obj_list<Atom>&) { + auto hobp = static_cast<hazptr_obj_base<T, Atom, D>*>(p); + auto obj = static_cast<T*>(hobp); + hobp->delete_obj(obj); + }; + } +}; // hazptr_obj_base + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObjLinked.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObjLinked.h new file mode 100644 index 000000000..715dddab4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrObjLinked.h @@ -0,0 +1,327 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> +#include <folly/synchronization/HazptrObj.h> + +#include <glog/logging.h> + +#include <atomic> +#include <stack> + +/// +/// Classes related to link counted objects and automatic retirement. +/// + +namespace folly { + +/** + * hazptr_root + * + * Link to counted objects. When destroyed unlinks the linked object + * if any. + * + * Template parameter T must support a member function unlink(), + * inherited from hazptr_obj_base_linked. + * + * Use example: Bucket heads in ConcurrentHashMap. + */ +template <typename T, template <typename> class Atom> +class hazptr_root { + Atom<T*> link_; + + public: + explicit hazptr_root(T* p = nullptr) noexcept : link_(p) {} + + ~hazptr_root() { + auto p = link_.load(std::memory_order_relaxed); + if (p) { + p->unlink(); + } + } + + const Atom<T*>& operator()() const noexcept { + return link_; + } + + Atom<T*>& operator()() noexcept { + return link_; + } +}; // hazptr_root + +/** + * hazptr_obj_linked + * + * Base class template for link counted objects. + * Supports: + * - Protecting descendants of protected objects. + * - One-pass reclamation of long immutable chains of objects. + * - Automatic reclamation of acyclic structures. + * + * Two inbound link counts are maintained per object: + * - Link count: Represents the number of links from mutable paths. + * - Ref count: Represents the number of links from immutable paths. + * [Note: The ref count is one less than such links plus one if + * the object hasn't gone through matching with hazard pointers + * without finding a match. That is, a new object without inbound + * links has a ref count of 0 and an about-to-be-reclaimed object + * can be viewed to have a ref count of -1.] + * + * User code can increment the link and ref counts by calling + * acquire_link and acquire_ref or their variants that require the + * user to guarantee thread safety. There are no public functions to + * decrement the counts explicitly. Counts are decremented implicitly + * as described in hazptr_obj_base_linked. + */ +template <template <typename> class Atom> +class hazptr_obj_linked : public hazptr_obj<Atom> { + using Count = uint32_t; + + static constexpr Count kRef = 1u; + static constexpr Count kLink = 1u << 16; + static constexpr Count kRefMask = kLink - 1u; + static constexpr Count kLinkMask = ~kRefMask; + + Atom<Count> count_{0}; + + public: + void acquire_link() noexcept { + count_inc(kLink); + } + + void acquire_link_safe() noexcept { + count_inc_safe(kLink); + } + + void acquire_ref() noexcept { + count_inc(kRef); + } + + void acquire_ref_safe() noexcept { + count_inc_safe(kRef); + } + + private: + template <typename, template <typename> class, typename> + friend class hazptr_obj_base_linked; + + Count count() const noexcept { + return count_.load(std::memory_order_acquire); + } + + void count_set(Count val) noexcept { + count_.store(val, std::memory_order_release); + } + + void count_inc(Count add) noexcept { + auto oldval = count_.fetch_add(add, std::memory_order_acq_rel); + DCHECK_LT(oldval & kLinkMask, kLinkMask); + DCHECK_LT(oldval & kRefMask, kRefMask); + } + + void count_inc_safe(Count add) noexcept { + auto oldval = count(); + count_set(oldval + add); + DCHECK_LT(oldval & kLinkMask, kLinkMask); + DCHECK_LT(oldval & kRefMask, kRefMask); + } + + bool count_cas(Count& oldval, Count newval) noexcept { + return count_.compare_exchange_weak( + oldval, newval, std::memory_order_acq_rel, std::memory_order_acquire); + } + + bool release_link() noexcept { + auto sub = kLink; + auto oldval = count(); + while (true) { + DCHECK_GT(oldval & kLinkMask, 0u); + if (oldval == kLink) { + count_set(0u); + return true; + } + if (count_cas(oldval, oldval - sub)) { + return false; + } + } + } + + bool release_ref() noexcept { + auto sub = kRef; + auto oldval = count(); + while (true) { + if (oldval == 0u) { + if (kIsDebug) { + count_set(kRefMask); + } + return true; + } + DCHECK_GT(oldval & kRefMask, 0u); + if (count_cas(oldval, oldval - sub)) { + return false; + } + } + } + + bool downgrade_link() noexcept { + auto oldval = count(); + auto sub = kLink - kRef; + while (true) { + if (oldval == kLink) { + count_set(kRef); + return true; + } + if (count_cas(oldval, oldval - sub)) { + return (oldval & kLinkMask) == kLink; + } + } + } +}; // hazptr_obj_linked + +/** + * hazptr_obj_base_linked + * + * Base class template for link counted objects. + * + * Supports both *explicit* and *implicit* object retirement, depending + * on whether object removal is *certain* or *uncertain*. + * + * A derived object's removal is certain when it is always possible + * to reason based only on the local state of user code when an + * object is removed, i.e., becomes unreachable from static + * roots. Otherwise, removal is uncertain. + * + * For example, Removal in UnboundedQueue is certain, whereas removal + * is ConcurrentHashMap is uncertain. + * + * If removal is certain, user code can call retire() explicitly. + * Otherwise, user code should call unlink() whenever an inbound + * link to the object is changed. Calls to unlink() automatically + * retire the object when the link count is decremented to 0. [Note: + * A ref count greater than 0 does not delay retiring an object.] + * + * Derived type T must define a member function template + * template <typename S> + * void push_links(bool m, S& s) { + * if (m) { // m stands mutable links + * // for each outbound mutable pointer p call + * // s.push(p); + * } else { + * // for each outbound immutable pointer p call + * // s.push(p); + * } + * } + * + * T may have both, either, or none of the two types of outbound + * links. For example, UnboundedQueue Segment has an immutable + * link, and ConcurrentHashMap NodeT has a mutable link. + */ +template <typename T, template <typename> class Atom, typename D> +class hazptr_obj_base_linked : public hazptr_obj_linked<Atom>, + public hazptr_deleter<T, D> { + using Stack = std::stack<hazptr_obj_base_linked<T, Atom, D>*>; + + public: + void retire() { + this->pre_retire_check(); // defined in hazptr_obj + set_reclaim(); + auto& domain = default_hazptr_domain<Atom>(); + this->push_obj(domain); // defined in hazptr_obj + } + + /* unlink: Retire object if last link is released. */ + void unlink() { + if (this->release_link()) { // defined in hazptr_obj_linked + downgrade_retire_immutable_descendants(); + retire(); + } + } + + /* unlink_and_reclaim_unchecked: Reclaim object if the last link is + released, without checking hazard pointers. To be called only + when the object cannot possibly be protected by any hazard + pointers. */ + void unlink_and_reclaim_unchecked() { + if (this->release_link()) { // defined in hazptr_obj_linked + DCHECK_EQ(this->count(), 0u); + delete_self(); + } + } + + private: + void set_reclaim() noexcept { + this->reclaim_ = [](hazptr_obj<Atom>* p, hazptr_obj_list<Atom>& l) { + auto obj = static_cast<hazptr_obj_base_linked<T, Atom, D>*>(p); + if (obj->release_ref()) { // defined in hazptr_obj_linked + obj->release_delete_immutable_descendants(); + obj->release_retire_mutable_children(l); + obj->delete_self(); + } + }; + } + + void downgrade_retire_immutable_descendants() { + Stack s; + call_push_links(false, s); + while (!s.empty()) { + auto p = s.top(); + s.pop(); + if (p && p->downgrade_link()) { + p->call_push_links(false, s); + p->retire(); + } + } + } + + void release_delete_immutable_descendants() { + Stack s; + call_push_links(false, s); + while (!s.empty()) { + auto p = s.top(); + s.pop(); + if (p && p->release_ref()) { + p->call_push_links(false, s); + p->delete_self(); + } + } + } + + void release_retire_mutable_children(hazptr_obj_list<Atom>& l) { + Stack s; + call_push_links(true, s); + while (!s.empty()) { + auto p = s.top(); + s.pop(); + if (p->release_link()) { + p->pre_retire_check(); // defined in hazptr_obj + p->set_reclaim(); + l.push(p); // treated as if retired immediately + } + } + } + + void call_push_links(bool m, Stack& s) { + static_cast<T*>(this)->push_links(m, s); // to be defined in T + } + + void delete_self() { + this->delete_obj(static_cast<T*>(this)); // defined in hazptr_deleter + } +}; // hazptr_obj_base_linked + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrRec.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrRec.h new file mode 100644 index 000000000..80a8935ea --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrRec.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> + +#include <folly/concurrency/CacheLocality.h> + +#include <atomic> + +namespace folly { + +/** + * hazptr_rec: + * + * Contains the actual hazard pointer. + */ +template <template <typename> class Atom> +class alignas(hardware_destructive_interference_size) hazptr_rec { + Atom<const void*> hazptr_{nullptr}; // the hazard pointer + hazptr_domain<Atom>* domain_; + hazptr_rec* next_; + Atom<bool> active_{false}; + + friend class hazptr_domain<Atom>; + friend class hazptr_holder<Atom>; + friend class hazptr_tc_entry<Atom>; + + const void* hazptr() const noexcept { + return hazptr_.load(std::memory_order_acquire); + } + + FOLLY_ALWAYS_INLINE void reset_hazptr(const void* p = nullptr) noexcept { + hazptr_.store(p, std::memory_order_release); + } + + bool active() const noexcept { + return active_.load(std::memory_order_acquire); + } + + void set_active() noexcept { + active_.store(true, std::memory_order_relaxed); + } + + bool try_acquire() noexcept { + bool a = active(); + return !a && + active_.compare_exchange_strong( + a, true, std::memory_order_release, std::memory_order_relaxed); + } + + void release() noexcept { + active_.store(false, std::memory_order_release); + } + + hazptr_rec<Atom>* next() { + return next_; + } + + void set_next(hazptr_rec<Atom>* rec) { + next_ = rec; + } + + FOLLY_ALWAYS_INLINE hazptr_domain<Atom>* domain() { + return domain_; + } + + void set_domain(hazptr_domain<Atom>* dom) { + domain_ = dom; + } +}; // hazptr_rec + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThrLocal.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThrLocal.h new file mode 100644 index 000000000..1f0c2aa70 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThrLocal.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr-fwd.h> + +#if FOLLY_HAZPTR_THR_LOCAL + +#include <folly/synchronization/HazptrObj.h> +#include <folly/synchronization/HazptrRec.h> + +#include <folly/SingletonThreadLocal.h> + +#include <glog/logging.h> + +#include <atomic> + +/** + * Thread local classes and singletons + */ + +namespace folly { + +/** + * hazptr_tc_entry + * + * Thread cache entry. + */ +template <template <typename> class Atom> +class hazptr_tc_entry { + hazptr_rec<Atom>* hprec_; + + template <uint8_t, template <typename> class> + friend class hazptr_array; + template <uint8_t, template <typename> class> + friend class hazptr_local; + friend class hazptr_tc<Atom>; + + FOLLY_ALWAYS_INLINE void fill(hazptr_rec<Atom>* hprec) noexcept { + hprec_ = hprec; + } + + FOLLY_ALWAYS_INLINE hazptr_rec<Atom>* get() const noexcept { + return hprec_; + } + + void evict() { + hprec_->release(); + } +}; // hazptr_tc_entry + +/** + * hazptr_tc: + * + * Thread cache of hazptr_rec-s that belong to the default domain. + */ +template <template <typename> class Atom> +class hazptr_tc { + static constexpr uint8_t kCapacity = 9; + + hazptr_tc_entry<Atom> entry_[kCapacity]; + uint8_t count_{0}; + bool local_{false}; // for debug mode only + + public: + ~hazptr_tc() { + for (uint8_t i = 0; i < count(); ++i) { + entry_[i].evict(); + } + } + + static constexpr uint8_t capacity() noexcept { + return kCapacity; + } + + private: + template <uint8_t, template <typename> class> + friend class hazptr_array; + friend class hazptr_holder<Atom>; + template <uint8_t, template <typename> class> + friend class hazptr_local; + + FOLLY_ALWAYS_INLINE + hazptr_tc_entry<Atom>& operator[](uint8_t i) noexcept { + DCHECK(i <= capacity()); + return entry_[i]; + } + + FOLLY_ALWAYS_INLINE hazptr_rec<Atom>* try_get() noexcept { + if (LIKELY(count_ > 0)) { + auto hprec = entry_[--count_].get(); + return hprec; + } + return nullptr; + } + + FOLLY_ALWAYS_INLINE bool try_put(hazptr_rec<Atom>* hprec) noexcept { + if (LIKELY(count_ < capacity())) { + entry_[count_++].fill(hprec); + return true; + } + return false; + } + + FOLLY_ALWAYS_INLINE uint8_t count() const noexcept { + return count_; + } + + FOLLY_ALWAYS_INLINE void set_count(uint8_t val) noexcept { + count_ = val; + } + + FOLLY_NOINLINE void fill(uint8_t num) { + DCHECK_LE(count_ + num, capacity()); + auto& domain = default_hazptr_domain<Atom>(); + for (uint8_t i = 0; i < num; ++i) { + auto hprec = domain.hprec_acquire(); + entry_[count_++].fill(hprec); + } + } + + FOLLY_NOINLINE void evict(uint8_t num) { + DCHECK_GE(count_, num); + for (uint8_t i = 0; i < num; ++i) { + entry_[--count_].evict(); + } + } + + bool local() const noexcept { // for debugging only + return local_; + } + + void set_local(bool b) noexcept { // for debugging only + local_ = b; + } +}; // hazptr_tc + +struct hazptr_tc_tls_tag {}; +/** hazptr_tc_tls */ +template <template <typename> class Atom> +FOLLY_ALWAYS_INLINE hazptr_tc<Atom>& hazptr_tc_tls() { + return folly::SingletonThreadLocal<hazptr_tc<Atom>, hazptr_tc_tls_tag>::get(); +} + +/** + * hazptr_priv + * + * Per-thread list of retired objects to be pushed in bulk to domain. + */ +template <template <typename> class Atom> +class hazptr_priv { + static constexpr int kThreshold = 20; + + Atom<hazptr_obj<Atom>*> head_; + Atom<hazptr_obj<Atom>*> tail_; + int rcount_; + bool in_dtor_; + + public: + hazptr_priv() : head_(nullptr), tail_(nullptr), rcount_(0), in_dtor_(false) {} + + ~hazptr_priv() { + in_dtor_ = true; + if (!empty()) { + push_all_to_domain(false); + } + } + + private: + friend class hazptr_domain<Atom>; + friend class hazptr_obj<Atom>; + + bool empty() const noexcept { + return head() == nullptr; + } + + void push(hazptr_obj<Atom>* obj) { + DCHECK(!in_dtor_); + push_in_priv_list(obj); + } + + void push_in_priv_list(hazptr_obj<Atom>* obj) { + while (true) { + if (tail()) { + if (push_in_non_empty_list(obj)) { + break; + } + } else { + if (push_in_empty_list(obj)) { + break; + } + } + } + if (++rcount_ >= kThreshold) { + push_all_to_domain(true); + } + } + + void push_all_to_domain(bool check_to_reclaim) { + hazptr_obj<Atom>* h = nullptr; + hazptr_obj<Atom>* t = nullptr; + collect(h, t); + if (h) { + DCHECK(t); + hazptr_obj_list<Atom> l(h, t, rcount_); + hazptr_domain_push_retired<Atom>(l, check_to_reclaim); + rcount_ = 0; + } + } + + void collect( + hazptr_obj<Atom>*& colHead, + hazptr_obj<Atom>*& colTail) noexcept { + // This function doesn't change rcount_. + // The value rcount_ is accurate excluding the effects of calling collect(). + auto h = exchange_head(); + if (h) { + auto t = exchange_tail(); + DCHECK(t); + if (colTail) { + colTail->set_next(h); + } else { + colHead = h; + } + colTail = t; + } + } + + hazptr_obj<Atom>* head() const noexcept { + return head_.load(std::memory_order_acquire); + } + + hazptr_obj<Atom>* tail() const noexcept { + return tail_.load(std::memory_order_acquire); + } + + void set_head(hazptr_obj<Atom>* obj) noexcept { + head_.store(obj, std::memory_order_release); + } + + bool cas_head(hazptr_obj<Atom>* expected, hazptr_obj<Atom>* obj) noexcept { + return head_.compare_exchange_weak( + expected, obj, std::memory_order_acq_rel, std::memory_order_relaxed); + } + + bool cas_tail(hazptr_obj<Atom>* expected, hazptr_obj<Atom>* obj) noexcept { + return tail_.compare_exchange_weak( + expected, obj, std::memory_order_acq_rel, std::memory_order_relaxed); + } + + hazptr_obj<Atom>* exchange_head() noexcept { + return head_.exchange(nullptr, std::memory_order_acq_rel); + } + + hazptr_obj<Atom>* exchange_tail() noexcept { + return tail_.exchange(nullptr, std::memory_order_acq_rel); + } + + bool push_in_non_empty_list(hazptr_obj<Atom>* obj) noexcept { + auto h = head(); + if (h) { + obj->set_next(h); + if (cas_head(h, obj)) { + return true; + } + } + return false; + } + + bool push_in_empty_list(hazptr_obj<Atom>* obj) noexcept { + hazptr_obj<Atom>* t = nullptr; + obj->set_next(nullptr); + if (cas_tail(t, obj)) { + set_head(obj); + return true; + } + return false; + } +}; // hazptr_priv + +/** hazptr_priv_tls */ +struct HazptrTag {}; + +template <template <typename> class Atom> +using hazptr_priv_singleton = + folly::SingletonThreadLocal<hazptr_priv<Atom>, HazptrTag>; + +template <template <typename> class Atom> +FOLLY_ALWAYS_INLINE hazptr_priv<Atom>& hazptr_priv_tls() { + return hazptr_priv_singleton<Atom>::get(); +} + +} // namespace folly + +#endif // FOLLY_HAZPTR_THR_LOCAL diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.cpp new file mode 100644 index 000000000..9e7c43706 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/HazptrThreadPoolExecutor.h> + +#include <folly/Singleton.h> +#include <folly/executors/CPUThreadPoolExecutor.h> + +namespace { + +struct HazptrTPETag {}; +folly::Singleton<folly::CPUThreadPoolExecutor, HazptrTPETag> hazptr_tpe_([] { + return new folly::CPUThreadPoolExecutor(1); +}); + +folly::Executor* get_hazptr_tpe() { + auto ex = hazptr_tpe_.try_get(); + return ex ? ex.get() : nullptr; +} + +} // namespace + +namespace folly { + +void enable_hazptr_thread_pool_executor() { + if (FLAGS_folly_hazptr_use_executor) { + default_hazptr_domain().set_executor(&get_hazptr_tpe); + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h new file mode 100644 index 000000000..44031d13a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/synchronization/Hazptr.h> + +namespace folly { + +void enable_hazptr_thread_pool_executor(); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/LifoSem.h b/ios/Pods/Flipper-Folly/folly/synchronization/LifoSem.h new file mode 100644 index 000000000..8041433df --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/LifoSem.h @@ -0,0 +1,757 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <atomic> +#include <cstdint> +#include <cstring> +#include <memory> +#include <system_error> + +#include <folly/CPortability.h> +#include <folly/IndexedMemPool.h> +#include <folly/Likely.h> +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/detail/StaticSingletonManager.h> +#include <folly/lang/Aligned.h> +#include <folly/lang/SafeAssert.h> +#include <folly/synchronization/AtomicStruct.h> +#include <folly/synchronization/SaturatingSemaphore.h> + +namespace folly { + +template < + template <typename> class Atom = std::atomic, + class BatonType = SaturatingSemaphore<true, Atom>> +struct LifoSemImpl; + +/// LifoSem is a semaphore that wakes its waiters in a manner intended to +/// maximize performance rather than fairness. It should be preferred +/// to a mutex+condvar or POSIX sem_t solution when all of the waiters +/// are equivalent. It is faster than a condvar or sem_t, and it has a +/// shutdown state that might save you a lot of complexity when it comes +/// time to shut down your work pipelines. LifoSem is larger than sem_t, +/// but that is only because it uses padding and alignment to avoid +/// false sharing. +/// +/// LifoSem allows multi-post and multi-tryWait, and provides a shutdown +/// state that awakens all waiters. LifoSem is faster than sem_t because +/// it performs exact wakeups, so it often requires fewer system calls. +/// It provides all of the functionality of sem_t except for timed waiting. +/// It is called LifoSem because its wakeup policy is approximately LIFO, +/// rather than the usual FIFO. +/// +/// The core semaphore operations provided are: +/// +/// -- post() -- if there is a pending waiter, wake it up, otherwise +/// increment the value of the semaphore. If the value of the semaphore +/// is already 2^32-1, does nothing. Compare to sem_post(). +/// +/// -- post(n) -- equivalent to n calls to post(), but much more efficient. +/// sem_t has no equivalent to this method. +/// +/// -- bool tryWait() -- if the semaphore's value is positive, decrements it +/// and returns true, otherwise returns false. Compare to sem_trywait(). +/// +/// -- uint32_t tryWait(uint32_t n) -- attempts to decrement the semaphore's +/// value by n, returning the amount by which it actually was decremented +/// (a value from 0 to n inclusive). Not atomic. Equivalent to n calls +/// to tryWait(). sem_t has no equivalent to this method. +/// +/// -- wait() -- waits until tryWait() can succeed. Compare to sem_wait(). +/// +/// -- timed wait variants - will wait until timeout. Note when these +/// timeout, the current implementation takes a lock, blocking +/// concurrent pushes and pops. (If timed wait calls are +/// substantial, consider re-working this code to be lock-free). +/// +/// LifoSem also has the notion of a shutdown state, in which any calls +/// that would block (or are already blocked) throw ShutdownSemError. +/// Note the difference between a call to wait() and a call to wait() +/// that might block. In the former case tryWait() would succeed, and no +/// isShutdown() check is performed. In the latter case an exception is +/// thrown. This behavior allows a LifoSem controlling work distribution +/// to drain. If you want to immediately stop all waiting on shutdown, +/// you can just check isShutdown() yourself (preferrably wrapped in +/// an UNLIKELY). This fast-stop behavior is easy to add, but difficult +/// to remove if you want the draining behavior, which is why we have +/// chosen the former. +/// +/// All LifoSem operations except valueGuess() are guaranteed to be +/// linearizable. +typedef LifoSemImpl<> LifoSem; + +/// The exception thrown when wait()ing on an isShutdown() LifoSem +class FOLLY_EXPORT ShutdownSemError : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +namespace detail { + +// Internally, a LifoSem is either a value or a linked list of wait nodes. +// This union is captured in the LifoSemHead type, which holds either a +// value or an indexed pointer to the list. LifoSemHead itself is a value +// type, the head is a mutable atomic box containing a LifoSemHead value. +// Each wait node corresponds to exactly one waiter. Values can flow +// through the semaphore either by going into and out of the head's value, +// or by direct communication from a poster to a waiter. The former path +// is taken when there are no pending waiters, the latter otherwise. The +// general flow of a post is to try to increment the value or pop-and-post +// a wait node. Either of those have the effect of conveying one semaphore +// unit. Waiting is the opposite, either a decrement of the value or +// push-and-wait of a wait node. The generic LifoSemBase abstracts the +// actual mechanism by which a wait node's post->wait communication is +// performed, which is why we have LifoSemRawNode and LifoSemNode. + +/// LifoSemRawNode is the actual pooled storage that backs LifoSemNode +/// for user-specified Handoff types. This is done so that we can have +/// a large static IndexedMemPool of nodes, instead of per-type pools +template <template <typename> class Atom> +struct LifoSemRawNode { + aligned_storage_for_t<void*> raw; + + /// The IndexedMemPool index of the next node in this chain, or 0 + /// if none. This will be set to uint32_t(-1) if the node is being + /// posted due to a shutdown-induced wakeup + Atom<uint32_t> next{0}; + + bool isShutdownNotice() const { + return next.load(std::memory_order_relaxed) == uint32_t(-1); + } + void clearShutdownNotice() { + next.store(0, std::memory_order_relaxed); + } + void setShutdownNotice() { + next.store(uint32_t(-1), std::memory_order_relaxed); + } + + typedef folly::IndexedMemPool< + LifoSemRawNode<Atom>, + 32, + 200, + Atom, + IndexedMemPoolTraitsLazyRecycle<LifoSemRawNode<Atom>>> + Pool; + + /// Storage for all of the waiter nodes for LifoSem-s that use Atom + static Pool& pool() { + return detail::createGlobal<PoolImpl, void>(); + } + + private: + struct PoolImpl : Pool { + /// Raw node storage is preallocated in a contiguous memory segment, + /// but we use an anonymous mmap so the physical memory used (RSS) will + /// only reflect the maximum number of waiters that actually existed + /// concurrently. For blocked threads the max node count is limited by the + /// number of threads, so we can conservatively estimate that this will be + /// < 10k. For LifoEventSem, however, we could potentially have many more. + /// + /// On a 64-bit architecture each LifoSemRawNode takes 16 bytes. We make + /// the pool 1 million entries. + static constexpr size_t capacity = 1 << 20; + + PoolImpl() : Pool(static_cast<uint32_t>(capacity)) {} + }; +}; + +/// Handoff is a type not bigger than a void* that knows how to perform a +/// single post() -> wait() communication. It must have a post() method. +/// If it has a wait() method then LifoSemBase's wait() implementation +/// will work out of the box, otherwise you will need to specialize +/// LifoSemBase::wait accordingly. +template <typename Handoff, template <typename> class Atom> +struct LifoSemNode : public LifoSemRawNode<Atom> { + static_assert( + sizeof(Handoff) <= sizeof(LifoSemRawNode<Atom>::raw), + "Handoff too big for small-object optimization, use indirection"); + static_assert( + alignof(Handoff) <= alignof(decltype(LifoSemRawNode<Atom>::raw)), + "Handoff alignment constraint not satisfied"); + + template <typename... Args> + void init(Args&&... args) { + new (&this->raw) Handoff(std::forward<Args>(args)...); + } + + void destroy() { + handoff().~Handoff(); + if (kIsDebug) { + memset(&this->raw, 'F', sizeof(this->raw)); + } + } + + Handoff& handoff() { + return *static_cast<Handoff*>(static_cast<void*>(&this->raw)); + } + + const Handoff& handoff() const { + return *static_cast<const Handoff*>(static_cast<const void*>(&this->raw)); + } +}; + +template <typename Handoff, template <typename> class Atom> +struct LifoSemNodeRecycler { + void operator()(LifoSemNode<Handoff, Atom>* elem) const { + elem->destroy(); + auto idx = LifoSemRawNode<Atom>::pool().locateElem(elem); + LifoSemRawNode<Atom>::pool().recycleIndex(idx); + } +}; + +/// LifoSemHead is a 64-bit struct that holds a 32-bit value, some state +/// bits, and a sequence number used to avoid ABA problems in the lock-free +/// management of the LifoSem's wait lists. The value can either hold +/// an integral semaphore value (if there are no waiters) or a node index +/// (see IndexedMemPool) for the head of a list of wait nodes +class LifoSemHead { + // What we really want are bitfields: + // uint64_t data : 32; uint64_t isNodeIdx : 1; uint64_t seq : 31; + // Unfortunately g++ generates pretty bad code for this sometimes (I saw + // -O3 code from gcc 4.7.1 copying the bitfields one at a time instead of + // in bulk, for example). We can generate better code anyway by assuming + // that setters won't be given values that cause under/overflow, and + // putting the sequence at the end where its planned overflow doesn't + // need any masking. + // + // data == 0 (empty list) with isNodeIdx is conceptually the same + // as data == 0 (no unclaimed increments) with !isNodeIdx, we always + // convert the former into the latter to make the logic simpler. + enum { + IsNodeIdxShift = 32, + IsShutdownShift = 33, + IsLockedShift = 34, + SeqShift = 35, + }; + enum : uint64_t { + IsNodeIdxMask = uint64_t(1) << IsNodeIdxShift, + IsShutdownMask = uint64_t(1) << IsShutdownShift, + IsLockedMask = uint64_t(1) << IsLockedShift, + SeqIncr = uint64_t(1) << SeqShift, + SeqMask = ~(SeqIncr - 1), + }; + + public: + uint64_t bits; + + //////// getters + + inline uint32_t idx() const { + assert(isNodeIdx()); + assert(uint32_t(bits) != 0); + return uint32_t(bits); + } + inline uint32_t value() const { + assert(!isNodeIdx()); + return uint32_t(bits); + } + inline constexpr bool isNodeIdx() const { + return (bits & IsNodeIdxMask) != 0; + } + inline constexpr bool isShutdown() const { + return (bits & IsShutdownMask) != 0; + } + inline constexpr bool isLocked() const { + return (bits & IsLockedMask) != 0; + } + inline constexpr uint32_t seq() const { + return uint32_t(bits >> SeqShift); + } + + //////// setter-like things return a new struct + + /// This should only be used for initial construction, not for setting + /// the value, because it clears the sequence number + static inline constexpr LifoSemHead fresh(uint32_t value) { + return LifoSemHead{value}; + } + + /// Returns the LifoSemHead that results from popping a waiter node, + /// given the current waiter node's next ptr + inline LifoSemHead withPop(uint32_t idxNext) const { + assert(!isLocked()); + assert(isNodeIdx()); + if (idxNext == 0) { + // no isNodeIdx bit or data bits. Wraparound of seq bits is okay + return LifoSemHead{(bits & (SeqMask | IsShutdownMask)) + SeqIncr}; + } else { + // preserve sequence bits (incremented with wraparound okay) and + // isNodeIdx bit, replace all data bits + return LifoSemHead{(bits & (SeqMask | IsShutdownMask | IsNodeIdxMask)) + + SeqIncr + idxNext}; + } + } + + /// Returns the LifoSemHead that results from pushing a new waiter node + inline LifoSemHead withPush(uint32_t _idx) const { + assert(!isLocked()); + assert(isNodeIdx() || value() == 0); + assert(!isShutdown()); + assert(_idx != 0); + return LifoSemHead{(bits & SeqMask) | IsNodeIdxMask | _idx}; + } + + /// Returns the LifoSemHead with value increased by delta, with + /// saturation if the maximum value is reached + inline LifoSemHead withValueIncr(uint32_t delta) const { + assert(!isLocked()); + assert(!isNodeIdx()); + auto rv = LifoSemHead{bits + SeqIncr + delta}; + if (UNLIKELY(rv.isNodeIdx())) { + // value has overflowed into the isNodeIdx bit + rv = LifoSemHead{(rv.bits & ~IsNodeIdxMask) | (IsNodeIdxMask - 1)}; + } + return rv; + } + + /// Returns the LifoSemHead that results from decrementing the value + inline LifoSemHead withValueDecr(uint32_t delta) const { + assert(!isLocked()); + assert(delta > 0 && delta <= value()); + return LifoSemHead{bits + SeqIncr - delta}; + } + + /// Returns the LifoSemHead with the same state as the current node, + /// but with the shutdown bit set + inline LifoSemHead withShutdown() const { + return LifoSemHead{bits | IsShutdownMask}; + } + + // Returns LifoSemHead with lock bit set, but rest of bits unchanged. + inline LifoSemHead withLock() const { + assert(!isLocked()); + return LifoSemHead{bits | IsLockedMask}; + } + + // Returns LifoSemHead with lock bit unset, and updated seqno based + // on idx. + inline LifoSemHead withoutLock(uint32_t idxNext) const { + assert(isLocked()); + // We need to treat this as a pop, as we may change the list head. + return LifoSemHead{bits & ~IsLockedMask}.withPop(idxNext); + } + + inline constexpr bool operator==(const LifoSemHead& rhs) const { + return bits == rhs.bits; + } + inline constexpr bool operator!=(const LifoSemHead& rhs) const { + return !(*this == rhs); + } +}; + +/// LifoSemBase is the engine for several different types of LIFO +/// semaphore. LifoSemBase handles storage of positive semaphore values +/// and wait nodes, but the actual waiting and notification mechanism is +/// up to the client. +/// +/// The Handoff type is responsible for arranging one wakeup notification. +/// See LifoSemNode for more information on how to make your own. +template <typename Handoff, template <typename> class Atom = std::atomic> +struct LifoSemBase { + /// Constructor + constexpr explicit LifoSemBase(uint32_t initialValue = 0) + : head_(in_place, LifoSemHead::fresh(initialValue)) {} + + LifoSemBase(LifoSemBase const&) = delete; + LifoSemBase& operator=(LifoSemBase const&) = delete; + + /// Silently saturates if value is already 2^32-1 + bool post() { + auto idx = incrOrPop(1); + if (idx != 0) { + idxToNode(idx).handoff().post(); + return true; + } + return false; + } + + /// Equivalent to n calls to post(), except may be much more efficient. + /// At any point in time at which the semaphore's value would exceed + /// 2^32-1 if tracked with infinite precision, it may be silently + /// truncated to 2^32-1. This saturation is not guaranteed to be exact, + /// although it is guaranteed that overflow won't result in wrap-around. + /// There would be a substantial performance and complexity cost in + /// guaranteeing exact saturation (similar to the cost of maintaining + /// linearizability near the zero value, but without as much of + /// a benefit). + void post(uint32_t n) { + uint32_t idx; + while (n > 0 && (idx = incrOrPop(n)) != 0) { + // pop accounts for only 1 + idxToNode(idx).handoff().post(); + --n; + } + } + + /// Returns true iff shutdown() has been called + bool isShutdown() const { + return UNLIKELY(head_->load(std::memory_order_acquire).isShutdown()); + } + + /// Prevents blocking on this semaphore, causing all blocking wait() + /// calls to throw ShutdownSemError. Both currently blocked wait() and + /// future calls to wait() for which tryWait() would return false will + /// cause an exception. Calls to wait() for which the matching post() + /// has already occurred will proceed normally. + void shutdown() { + // first set the shutdown bit + auto h = head_->load(std::memory_order_acquire); + while (!h.isShutdown()) { + if (h.isLocked()) { + std::this_thread::yield(); + h = head_->load(std::memory_order_acquire); + continue; + } + + if (head_->compare_exchange_strong(h, h.withShutdown())) { + // success + h = h.withShutdown(); + break; + } + // compare_exchange_strong rereads h, retry + } + + // now wake up any waiters + while (h.isNodeIdx()) { + if (h.isLocked()) { + std::this_thread::yield(); + h = head_->load(std::memory_order_acquire); + continue; + } + auto& node = idxToNode(h.idx()); + auto repl = h.withPop(node.next.load(std::memory_order_relaxed)); + if (head_->compare_exchange_strong(h, repl)) { + // successful pop, wake up the waiter and move on. The next + // field is used to convey that this wakeup didn't consume a value + node.setShutdownNotice(); + node.handoff().post(); + h = repl; + } + } + } + + /// Returns true iff value was decremented + bool tryWait() { + uint32_t n = 1; + auto rv = decrOrPush(n, 0); + assert( + (rv == WaitResult::DECR && n == 0) || + (rv != WaitResult::DECR && n == 1)); + // SHUTDOWN is okay here, since we don't actually wait + return rv == WaitResult::DECR; + } + + /// Equivalent to (but may be much more efficient than) n calls to + /// tryWait(). Returns the total amount by which the semaphore's value + /// was decreased + uint32_t tryWait(uint32_t n) { + auto const orig = n; + while (n > 0) { +#ifndef NDEBUG + auto prev = n; +#endif + auto rv = decrOrPush(n, 0); + assert( + (rv == WaitResult::DECR && n < prev) || + (rv != WaitResult::DECR && n == prev)); + if (rv != WaitResult::DECR) { + break; + } + } + return orig - n; + } + + /// Blocks the current thread until there is a matching post or the + /// semaphore is shut down. Throws ShutdownSemError if the semaphore + /// has been shut down and this method would otherwise be blocking. + /// Note that wait() doesn't throw during shutdown if tryWait() would + /// return true + void wait() { + auto const deadline = std::chrono::steady_clock::time_point::max(); + auto res = try_wait_until(deadline); + FOLLY_SAFE_DCHECK(res, "infinity time has passed"); + } + + bool try_wait() { + return tryWait(); + } + + template <typename Rep, typename Period> + bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) { + return try_wait_until(timeout + std::chrono::steady_clock::now()); + } + + template <typename Clock, typename Duration> + bool try_wait_until( + const std::chrono::time_point<Clock, Duration>& deadline) { + // early check isn't required for correctness, but is an important + // perf win if we can avoid allocating and deallocating a node + if (tryWait()) { + return true; + } + + // allocateNode() won't compile unless Handoff has a default + // constructor + UniquePtr node = allocateNode(); + + auto rv = tryWaitOrPush(*node); + if (UNLIKELY(rv == WaitResult::SHUTDOWN)) { + assert(isShutdown()); + throw ShutdownSemError("wait() would block but semaphore is shut down"); + } + + if (rv == WaitResult::PUSH) { + if (!node->handoff().try_wait_until(deadline)) { + if (tryRemoveNode(*node)) { + return false; + } else { + // We could not remove our node. Return to waiting. + // + // This only happens if we lose a removal race with post(), + // so we are not likely to wait long. This is only + // necessary to ensure we don't return node's memory back to + // IndexedMemPool before post() has had a chance to post to + // handoff(). In a stronger memory reclamation scheme, such + // as hazptr or rcu, this would not be necessary. + node->handoff().wait(); + } + } + if (UNLIKELY(node->isShutdownNotice())) { + // this wait() didn't consume a value, it was triggered by shutdown + throw ShutdownSemError( + "blocking wait() interrupted by semaphore shutdown"); + } + + // node->handoff().wait() can't return until after the node has + // been popped and post()ed, so it is okay for the UniquePtr to + // recycle the node now + } + // else node wasn't pushed, so it is safe to recycle + return true; + } + + /// Returns a guess at the current value, designed for debugging. + /// If there are no concurrent posters or waiters then this will + /// be correct + uint32_t valueGuess() const { + // this is actually linearizable, but we don't promise that because + // we may want to add striping in the future to help under heavy + // contention + auto h = head_->load(std::memory_order_acquire); + return h.isNodeIdx() ? 0 : h.value(); + } + + protected: + enum class WaitResult { + PUSH, + DECR, + SHUTDOWN, + }; + + /// The type of a std::unique_ptr that will automatically return a + /// LifoSemNode to the appropriate IndexedMemPool + typedef std:: + unique_ptr<LifoSemNode<Handoff, Atom>, LifoSemNodeRecycler<Handoff, Atom>> + UniquePtr; + + /// Returns a node that can be passed to decrOrLink + template <typename... Args> + UniquePtr allocateNode(Args&&... args) { + auto idx = LifoSemRawNode<Atom>::pool().allocIndex(); + if (idx != 0) { + auto& node = idxToNode(idx); + node.clearShutdownNotice(); + try { + node.init(std::forward<Args>(args)...); + } catch (...) { + LifoSemRawNode<Atom>::pool().recycleIndex(idx); + throw; + } + return UniquePtr(&node); + } else { + return UniquePtr(); + } + } + + /// Returns DECR if the semaphore value was decremented (and waiterNode + /// was untouched), PUSH if a reference to the wait node was pushed, + /// or SHUTDOWN if decrement was not possible and push wasn't allowed + /// because isShutdown(). Ownership of the wait node remains the + /// responsibility of the caller, who must not release it until after + /// the node's Handoff has been posted. + WaitResult tryWaitOrPush(LifoSemNode<Handoff, Atom>& waiterNode) { + uint32_t n = 1; + return decrOrPush(n, nodeToIdx(waiterNode)); + } + + // Locks the list head (blocking concurrent pushes and pops) + // and attempts to remove this node. Returns true if node was + // found and removed, false if not found. + bool tryRemoveNode(const LifoSemNode<Handoff, Atom>& removenode) { + auto removeidx = nodeToIdx(removenode); + auto head = head_->load(std::memory_order_acquire); + // Try to lock the head. + while (true) { + if (head.isLocked()) { + std::this_thread::yield(); + head = head_->load(std::memory_order_acquire); + continue; + } + if (!head.isNodeIdx()) { + return false; + } + if (head_->compare_exchange_weak( + head, + head.withLock(), + std::memory_order_acquire, + std::memory_order_relaxed)) { + break; + } + } + // Update local var to what head_ is, for better assert() checking. + head = head.withLock(); + bool result = false; + auto idx = head.idx(); + if (idx == removeidx) { + // pop from head. Head seqno is updated. + head_->store( + head.withoutLock(removenode.next.load(std::memory_order_relaxed)), + std::memory_order_release); + return true; + } + auto node = &idxToNode(idx); + idx = node->next.load(std::memory_order_relaxed); + while (idx) { + if (idx == removeidx) { + // Pop from mid-list. + node->next.store( + removenode.next.load(std::memory_order_relaxed), + std::memory_order_relaxed); + result = true; + break; + } + node = &idxToNode(idx); + idx = node->next.load(std::memory_order_relaxed); + } + // Unlock and return result + head_->store(head.withoutLock(head.idx()), std::memory_order_release); + return result; + } + + private: + cacheline_aligned<folly::AtomicStruct<LifoSemHead, Atom>> head_; + + static LifoSemNode<Handoff, Atom>& idxToNode(uint32_t idx) { + auto raw = &LifoSemRawNode<Atom>::pool()[idx]; + return *static_cast<LifoSemNode<Handoff, Atom>*>(raw); + } + + static uint32_t nodeToIdx(const LifoSemNode<Handoff, Atom>& node) { + return LifoSemRawNode<Atom>::pool().locateElem(&node); + } + + /// Either increments by n and returns 0, or pops a node and returns it. + /// If n + the stripe's value overflows, then the stripe's value + /// saturates silently at 2^32-1 + uint32_t incrOrPop(uint32_t n) { + while (true) { + assert(n > 0); + + auto head = head_->load(std::memory_order_acquire); + if (head.isLocked()) { + std::this_thread::yield(); + continue; + } + if (head.isNodeIdx()) { + auto& node = idxToNode(head.idx()); + if (head_->compare_exchange_strong( + head, + head.withPop(node.next.load(std::memory_order_relaxed)))) { + // successful pop + return head.idx(); + } + } else { + auto after = head.withValueIncr(n); + if (head_->compare_exchange_strong(head, after)) { + // successful incr + return 0; + } + } + // retry + } + } + + /// Returns DECR if some amount was decremented, with that amount + /// subtracted from n. If n is 1 and this function returns DECR then n + /// must be 0 afterward. Returns PUSH if no value could be decremented + /// and idx was pushed, or if idx was zero and no push was performed but + /// a push would have been performed with a valid node. Returns SHUTDOWN + /// if the caller should have blocked but isShutdown(). If idx == 0, + /// may return PUSH even after isShutdown() or may return SHUTDOWN + WaitResult decrOrPush(uint32_t& n, uint32_t idx) { + assert(n > 0); + + while (true) { + auto head = head_->load(std::memory_order_acquire); + + if (head.isLocked()) { + std::this_thread::yield(); + continue; + } + + if (!head.isNodeIdx() && head.value() > 0) { + // decr + auto delta = std::min(n, head.value()); + if (head_->compare_exchange_strong(head, head.withValueDecr(delta))) { + n -= delta; + return WaitResult::DECR; + } + } else { + // push + if (idx == 0) { + return WaitResult::PUSH; + } + + if (UNLIKELY(head.isShutdown())) { + return WaitResult::SHUTDOWN; + } + + auto& node = idxToNode(idx); + node.next.store( + head.isNodeIdx() ? head.idx() : 0, std::memory_order_relaxed); + if (head_->compare_exchange_strong(head, head.withPush(idx))) { + // push succeeded + return WaitResult::PUSH; + } + } + } + // retry + } +}; + +} // namespace detail + +template <template <typename> class Atom, class BatonType> +struct LifoSemImpl : public detail::LifoSemBase<BatonType, Atom> { + constexpr explicit LifoSemImpl(uint32_t v = 0) + : detail::LifoSemBase<BatonType, Atom>(v) {} +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/MicroSpinLock.h b/ios/Pods/Flipper-Folly/folly/synchronization/MicroSpinLock.h new file mode 100644 index 000000000..b8f78589c --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/MicroSpinLock.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * N.B. You most likely do _not_ want to use MicroSpinLock or any + * other kind of spinlock. Consider MicroLock instead. + * + * In short, spinlocks in preemptive multi-tasking operating systems + * have serious problems and fast mutexes like std::mutex are almost + * certainly the better choice, because letting the OS scheduler put a + * thread to sleep is better for system responsiveness and throughput + * than wasting a timeslice repeatedly querying a lock held by a + * thread that's blocked, and you can't prevent userspace + * programs blocking. + * + * Spinlocks in an operating system kernel make much more sense than + * they do in userspace. + */ + +#pragma once + +/* + * @author Keith Adams <kma@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +#include <array> +#include <atomic> +#include <cassert> +#include <cstdint> +#include <mutex> +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/lang/Align.h> +#include <folly/synchronization/SanitizeThread.h> +#include <folly/synchronization/detail/Sleeper.h> + +namespace folly { + +/* + * A really, *really* small spinlock for fine-grained locking of lots + * of teeny-tiny data. + * + * Zero initializing these is guaranteed to be as good as calling + * init(), since the free state is guaranteed to be all-bits zero. + * + * This class should be kept a POD, so we can used it in other packed + * structs (gcc does not allow __attribute__((__packed__)) on structs that + * contain non-POD data). This means avoid adding a constructor, or + * making some members private, etc. + */ +struct MicroSpinLock { + enum { FREE = 0, LOCKED = 1 }; + // lock_ can't be std::atomic<> to preserve POD-ness. + uint8_t lock_; + + // Initialize this MSL. It is unnecessary to call this if you + // zero-initialize the MicroSpinLock. + void init() noexcept { + payload()->store(FREE); + } + + bool try_lock() noexcept { + bool ret = cas(FREE, LOCKED); + annotate_rwlock_try_acquired( + this, annotate_rwlock_level::wrlock, ret, __FILE__, __LINE__); + return ret; + } + + void lock() noexcept { + detail::Sleeper sleeper; + while (!cas(FREE, LOCKED)) { + do { + sleeper.wait(); + } while (payload()->load(std::memory_order_relaxed) == LOCKED); + } + assert(payload()->load() == LOCKED); + annotate_rwlock_acquired( + this, annotate_rwlock_level::wrlock, __FILE__, __LINE__); + } + + void unlock() noexcept { + assert(payload()->load() == LOCKED); + annotate_rwlock_released( + this, annotate_rwlock_level::wrlock, __FILE__, __LINE__); + payload()->store(FREE, std::memory_order_release); + } + + private: + std::atomic<uint8_t>* payload() noexcept { + return reinterpret_cast<std::atomic<uint8_t>*>(&this->lock_); + } + + bool cas(uint8_t compare, uint8_t newVal) noexcept { + return std::atomic_compare_exchange_strong_explicit( + payload(), + &compare, + newVal, + std::memory_order_acquire, + std::memory_order_relaxed); + } +}; +static_assert( + std::is_pod<MicroSpinLock>::value, + "MicroSpinLock must be kept a POD type."); + +////////////////////////////////////////////////////////////////////// + +/** + * Array of spinlocks where each one is padded to prevent false sharing. + * Useful for shard-based locking implementations in environments where + * contention is unlikely. + */ + +template <class T, size_t N> +struct alignas(max_align_v) SpinLockArray { + T& operator[](size_t i) noexcept { + return data_[i].lock; + } + + const T& operator[](size_t i) const noexcept { + return data_[i].lock; + } + + constexpr size_t size() const noexcept { + return N; + } + + private: + struct PaddedSpinLock { + PaddedSpinLock() : lock() {} + T lock; + char padding[hardware_destructive_interference_size - sizeof(T)]; + }; + static_assert( + sizeof(PaddedSpinLock) == hardware_destructive_interference_size, + "Invalid size of PaddedSpinLock"); + + // Check if T can theoretically cross a cache line. + static_assert( + max_align_v > 0 && + hardware_destructive_interference_size % max_align_v == 0 && + sizeof(T) <= max_align_v, + "T can cross cache line boundaries"); + + char padding_[hardware_destructive_interference_size]; + std::array<PaddedSpinLock, N> data_; +}; + +////////////////////////////////////////////////////////////////////// + +typedef std::lock_guard<MicroSpinLock> MSLGuard; + +////////////////////////////////////////////////////////////////////// + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.cpp new file mode 100644 index 000000000..e0ead9f0a --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/ParkingLot.h> + +#include <array> + +namespace folly { +namespace parking_lot_detail { + +Bucket& Bucket::bucketFor(uint64_t key) { + constexpr size_t const kNumBuckets = kIsMobile ? 256 : 4096; + + // Statically allocating this lets us use this in allocation-sensitive + // contexts. This relies on the assumption that std::mutex won't dynamically + // allocate memory, which we assume to be the case on Linux and iOS. + static Indestructible<std::array<Bucket, kNumBuckets>> gBuckets; + return (*gBuckets)[key % kNumBuckets]; +} + +std::atomic<uint64_t> idallocator{0}; + +} // namespace parking_lot_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.h b/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.h new file mode 100644 index 000000000..610cc2c19 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/ParkingLot.h @@ -0,0 +1,331 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <condition_variable> +#include <mutex> + +#include <folly/Hash.h> +#include <folly/Indestructible.h> +#include <folly/Portability.h> +#include <folly/Unit.h> +#include <folly/lang/SafeAssert.h> + +namespace folly { + +namespace parking_lot_detail { + +struct WaitNodeBase { + const uint64_t key_; + const uint64_t lotid_; + WaitNodeBase* next_{nullptr}; + WaitNodeBase* prev_{nullptr}; + + // tricky: hold both bucket and node mutex to write, either to read + bool signaled_; + std::mutex mutex_; + std::condition_variable cond_; + + WaitNodeBase(uint64_t key, uint64_t lotid) + : key_(key), lotid_(lotid), signaled_(false) {} + + template <typename Clock, typename Duration> + std::cv_status wait(std::chrono::time_point<Clock, Duration> deadline) { + std::cv_status status = std::cv_status::no_timeout; + std::unique_lock<std::mutex> nodeLock(mutex_); + while (!signaled_ && status != std::cv_status::timeout) { + if (deadline != std::chrono::time_point<Clock, Duration>::max()) { + status = cond_.wait_until(nodeLock, deadline); + } else { + cond_.wait(nodeLock); + } + } + return status; + } + + void wake() { + std::lock_guard<std::mutex> nodeLock(mutex_); + signaled_ = true; + cond_.notify_one(); + } + + bool signaled() { + return signaled_; + } +}; + +extern std::atomic<uint64_t> idallocator; + +// Our emulated futex uses 4096 lists of wait nodes. There are two levels +// of locking: a per-list mutex that controls access to the list and a +// per-node mutex, condvar, and bool that are used for the actual wakeups. +// The per-node mutex allows us to do precise wakeups without thundering +// herds. +struct Bucket { + std::mutex mutex_; + WaitNodeBase* head_; + WaitNodeBase* tail_; + std::atomic<uint64_t> count_; + + static Bucket& bucketFor(uint64_t key); + + void push_back(WaitNodeBase* node) { + if (tail_) { + FOLLY_SAFE_DCHECK(head_, ""); + node->prev_ = tail_; + tail_->next_ = node; + tail_ = node; + } else { + tail_ = node; + head_ = node; + } + } + + void erase(WaitNodeBase* node) { + FOLLY_SAFE_DCHECK(count_.load(std::memory_order_relaxed) >= 1, ""); + if (head_ == node && tail_ == node) { + FOLLY_SAFE_DCHECK(node->prev_ == nullptr, ""); + FOLLY_SAFE_DCHECK(node->next_ == nullptr, ""); + head_ = nullptr; + tail_ = nullptr; + } else if (head_ == node) { + FOLLY_SAFE_DCHECK(node->prev_ == nullptr, ""); + FOLLY_SAFE_DCHECK(node->next_, ""); + head_ = node->next_; + head_->prev_ = nullptr; + } else if (tail_ == node) { + FOLLY_SAFE_DCHECK(node->next_ == nullptr, ""); + FOLLY_SAFE_DCHECK(node->prev_, ""); + tail_ = node->prev_; + tail_->next_ = nullptr; + } else { + FOLLY_SAFE_DCHECK(node->next_, ""); + FOLLY_SAFE_DCHECK(node->prev_, ""); + node->next_->prev_ = node->prev_; + node->prev_->next_ = node->next_; + } + count_.fetch_sub(1, std::memory_order_relaxed); + } +}; + +} // namespace parking_lot_detail + +enum class UnparkControl { + RetainContinue, + RemoveContinue, + RetainBreak, + RemoveBreak, +}; + +enum class ParkResult { + Skip, + Unpark, + Timeout, +}; + +/* + * ParkingLot provides an interface that is similar to Linux's futex + * system call, but with additional functionality. It is implemented + * in a portable way on top of std::mutex and std::condition_variable. + * + * Additional reading: + * https://webkit.org/blog/6161/locking-in-webkit/ + * https://github.com/WebKit/webkit/blob/master/Source/WTF/wtf/ParkingLot.h + * https://locklessinc.com/articles/futex_cheat_sheet/ + * + * The main difference from futex is that park/unpark take lambdas, + * such that nearly anything can be done while holding the bucket + * lock. Unpark() lambda can also be used to wake up any number of + * waiters. + * + * ParkingLot is templated on the data type, however, all ParkingLot + * implementations are backed by a single static array of buckets to + * avoid large memory overhead. Lambdas will only ever be called on + * the specific ParkingLot's nodes. + */ +template <typename Data = Unit> +class ParkingLot { + const uint64_t lotid_; + ParkingLot(const ParkingLot&) = delete; + + struct WaitNode : public parking_lot_detail::WaitNodeBase { + const Data data_; + + template <typename D> + WaitNode(uint64_t key, uint64_t lotid, D&& data) + : WaitNodeBase(key, lotid), data_(std::forward<D>(data)) {} + }; + + public: + ParkingLot() : lotid_(parking_lot_detail::idallocator++) {} + + /* Park API + * + * Key is almost always the address of a variable. + * + * ToPark runs while holding the bucket lock: usually this + * is a check to see if we can sleep, by checking waiter bits. + * + * PreWait is usually used to implement condition variable like + * things, such that you can unlock the condition variable's lock at + * the appropriate time. + */ + template <typename Key, typename D, typename ToPark, typename PreWait> + ParkResult park(const Key key, D&& data, ToPark&& toPark, PreWait&& preWait) { + return park_until( + key, + std::forward<D>(data), + std::forward<ToPark>(toPark), + std::forward<PreWait>(preWait), + std::chrono::steady_clock::time_point::max()); + } + + template < + typename Key, + typename D, + typename ToPark, + typename PreWait, + typename Clock, + typename Duration> + ParkResult park_until( + const Key key, + D&& data, + ToPark&& toPark, + PreWait&& preWait, + std::chrono::time_point<Clock, Duration> deadline); + + template < + typename Key, + typename D, + typename ToPark, + typename PreWait, + typename Rep, + typename Period> + ParkResult park_for( + const Key key, + D&& data, + ToPark&& toPark, + PreWait&& preWait, + std::chrono::duration<Rep, Period>& timeout) { + return park_until( + key, + std::forward<D>(data), + std::forward<ToPark>(toPark), + std::forward<PreWait>(preWait), + timeout + std::chrono::steady_clock::now()); + } + + /* + * Unpark API + * + * Key is the same uniqueaddress used in park(), and is used as a + * hash key for lookup of waiters. + * + * Unparker is a function that is given the Data parameter, and + * returns an UnparkControl. The Remove* results will remove and + * wake the waiter, the Ignore/Stop results will not, while stopping + * or continuing iteration of the waiter list. + */ + template <typename Key, typename Unparker> + void unpark(const Key key, Unparker&& func); +}; + +template <typename Data> +template < + typename Key, + typename D, + typename ToPark, + typename PreWait, + typename Clock, + typename Duration> +ParkResult ParkingLot<Data>::park_until( + const Key bits, + D&& data, + ToPark&& toPark, + PreWait&& preWait, + std::chrono::time_point<Clock, Duration> deadline) { + auto key = hash::twang_mix64(uint64_t(bits)); + auto& bucket = parking_lot_detail::Bucket::bucketFor(key); + WaitNode node(key, lotid_, std::forward<D>(data)); + + { + // A: Must be seq_cst. Matches B. + bucket.count_.fetch_add(1, std::memory_order_seq_cst); + + std::unique_lock<std::mutex> bucketLock(bucket.mutex_); + + if (!std::forward<ToPark>(toPark)()) { + bucketLock.unlock(); + bucket.count_.fetch_sub(1, std::memory_order_relaxed); + return ParkResult::Skip; + } + + bucket.push_back(&node); + } // bucketLock scope + + std::forward<PreWait>(preWait)(); + + auto status = node.wait(deadline); + + if (status == std::cv_status::timeout) { + // it's not really a timeout until we unlink the unsignaled node + std::lock_guard<std::mutex> bucketLock(bucket.mutex_); + if (!node.signaled()) { + bucket.erase(&node); + return ParkResult::Timeout; + } + } + + return ParkResult::Unpark; +} + +template <typename Data> +template <typename Key, typename Func> +void ParkingLot<Data>::unpark(const Key bits, Func&& func) { + auto key = hash::twang_mix64(uint64_t(bits)); + auto& bucket = parking_lot_detail::Bucket::bucketFor(key); + // B: Must be seq_cst. Matches A. If true, A *must* see in seq_cst + // order any atomic updates in toPark() (and matching updates that + // happen before unpark is called) + if (bucket.count_.load(std::memory_order_seq_cst) == 0) { + return; + } + + std::lock_guard<std::mutex> bucketLock(bucket.mutex_); + + for (auto iter = bucket.head_; iter != nullptr;) { + auto node = static_cast<WaitNode*>(iter); + iter = iter->next_; + if (node->key_ == key && node->lotid_ == lotid_) { + auto result = std::forward<Func>(func)(node->data_); + if (result == UnparkControl::RemoveBreak || + result == UnparkControl::RemoveContinue) { + // we unlink, but waiter destroys the node + bucket.erase(node); + + node->wake(); + } + if (result == UnparkControl::RemoveBreak || + result == UnparkControl::RetainBreak) { + return; + } + } + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/PicoSpinLock.h b/ios/Pods/Flipper-Folly/folly/synchronization/PicoSpinLock.h new file mode 100644 index 000000000..9d0058992 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/PicoSpinLock.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * N.B. You most likely do _not_ want to use PicoSpinLock or any other + * kind of spinlock. Consider MicroLock instead. + * + * In short, spinlocks in preemptive multi-tasking operating systems + * have serious problems and fast mutexes like std::mutex are almost + * certainly the better choice, because letting the OS scheduler put a + * thread to sleep is better for system responsiveness and throughput + * than wasting a timeslice repeatedly querying a lock held by a + * thread that's blocked, and you can't prevent userspace + * programs blocking. + * + * Spinlocks in an operating system kernel make much more sense than + * they do in userspace. + */ + +#pragma once +#define FOLLY_PICO_SPIN_LOCK_H_ + +/* + * @author Keith Adams <kma@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +#include <array> +#include <atomic> +#include <cinttypes> +#include <cstdlib> +#include <mutex> +#include <type_traits> + +#include <glog/logging.h> + +#include <folly/Portability.h> +#include <folly/synchronization/AtomicUtil.h> +#include <folly/synchronization/SanitizeThread.h> +#include <folly/synchronization/detail/Sleeper.h> + +namespace folly { + +/* + * Spin lock on a single bit in an integral type. You can use this + * with 16, 32, or 64-bit integral types. + * + * This is useful if you want a small lock and already have an int + * with a bit in it that you aren't using. But note that it can't be + * as small as MicroSpinLock (1 byte), if you don't already have a + * convenient int with an unused bit lying around to put it on. + * + * To construct these, either use init() or zero initialize. We don't + * have a real constructor because we want this to be a POD type so we + * can put it into packed structs. + */ +template <class IntType, int Bit = sizeof(IntType) * 8 - 1> +struct PicoSpinLock { + // Internally we deal with the unsigned version of the type. + typedef typename std::make_unsigned<IntType>::type UIntType; + + static_assert( + std::is_integral<IntType>::value, + "PicoSpinLock needs an integral type"); + static_assert( + sizeof(IntType) == 2 || sizeof(IntType) == 4 || sizeof(IntType) == 8, + "PicoSpinLock can't work on integers smaller than 2 bytes"); + + public: + static const UIntType kLockBitMask_ = UIntType(1) << Bit; + mutable UIntType lock_; + + /* + * You must call this function before using this class, if you + * default constructed it. If you zero-initialized it you can + * assume the PicoSpinLock is in a valid unlocked state with + * getData() == 0. + * + * (This doesn't use a constructor because we want to be a POD.) + */ + void init(IntType initialValue = 0) { + CHECK(!(initialValue & kLockBitMask_)); + reinterpret_cast<std::atomic<UIntType>*>(&lock_)->store( + UIntType(initialValue), std::memory_order_release); + } + + /* + * Returns the value of the integer we using for our lock, except + * with the bit we are using as a lock cleared, regardless of + * whether the lock is held. + * + * It is 'safe' to call this without holding the lock. (As in: you + * get the same guarantees for simultaneous accesses to an integer + * as you normally get.) + */ + IntType getData() const { + auto res = reinterpret_cast<std::atomic<UIntType>*>(&lock_)->load( + std::memory_order_relaxed) & + ~kLockBitMask_; + return res; + } + + /* + * Set the value of the other bits in our integer. + * + * Don't use this when you aren't holding the lock, unless it can be + * guaranteed that no other threads may be trying to use this. + */ + void setData(IntType w) { + CHECK(!(w & kLockBitMask_)); + auto l = reinterpret_cast<std::atomic<UIntType>*>(&lock_); + l->store( + (l->load(std::memory_order_relaxed) & kLockBitMask_) | w, + std::memory_order_relaxed); + } + + /* + * Try to get the lock without blocking: returns whether or not we + * got it. + */ + bool try_lock() const { + auto ret = try_lock_internal(); + annotate_rwlock_try_acquired( + this, annotate_rwlock_level::wrlock, ret, __FILE__, __LINE__); + return ret; + } + + /* + * Block until we can acquire the lock. Uses Sleeper to wait. + */ + void lock() const { + detail::Sleeper sleeper; + while (!try_lock_internal()) { + sleeper.wait(); + } + annotate_rwlock_acquired( + this, annotate_rwlock_level::wrlock, __FILE__, __LINE__); + } + + /* + * Release the lock, without changing the value of the rest of the + * integer. + */ + void unlock() const { + annotate_rwlock_released( + this, annotate_rwlock_level::wrlock, __FILE__, __LINE__); + auto previous = atomic_fetch_reset( + *reinterpret_cast<std::atomic<UIntType>*>(&lock_), + Bit, + std::memory_order_release); + DCHECK(previous); + } + + private: + // called by lock/try_lock - this is not TSAN aware + bool try_lock_internal() const { + auto previous = atomic_fetch_set( + *reinterpret_cast<std::atomic<UIntType>*>(&lock_), + Bit, + std::memory_order_acquire); + return !previous; + } +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/RWSpinLock.h b/ios/Pods/Flipper-Folly/folly/synchronization/RWSpinLock.h new file mode 100644 index 000000000..4fdb36fb5 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/RWSpinLock.h @@ -0,0 +1,852 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * N.B. You most likely do _not_ want to use RWSpinLock or any other + * kind of spinlock. Use SharedMutex instead. + * + * In short, spinlocks in preemptive multi-tasking operating systems + * have serious problems and fast mutexes like SharedMutex are almost + * certainly the better choice, because letting the OS scheduler put a + * thread to sleep is better for system responsiveness and throughput + * than wasting a timeslice repeatedly querying a lock held by a + * thread that's blocked, and you can't prevent userspace + * programs blocking. + * + * Spinlocks in an operating system kernel make much more sense than + * they do in userspace. + * + * ------------------------------------------------------------------- + * + * Two Read-Write spin lock implementations. + * + * Ref: http://locklessinc.com/articles/locks + * + * Both locks here are faster than pthread_rwlock and have very low + * overhead (usually 20-30ns). They don't use any system mutexes and + * are very compact (4/8 bytes), so are suitable for per-instance + * based locking, particularly when contention is not expected. + * + * For a spinlock, RWSpinLock is a reasonable choice. (See the note + * about for why a spin lock is frequently a bad idea generally.) + * RWSpinLock has minimal overhead, and comparable contention + * performance when the number of competing threads is less than or + * equal to the number of logical CPUs. Even as the number of + * threads gets larger, RWSpinLock can still be very competitive in + * READ, although it is slower on WRITE, and also inherently unfair + * to writers. + * + * RWTicketSpinLock shows more balanced READ/WRITE performance. If + * your application really needs a lot more threads, and a + * higher-priority writer, prefer one of the RWTicketSpinLock locks. + * + * Caveats: + * + * RWTicketSpinLock locks can only be used with GCC on x86/x86-64 + * based systems. + * + * RWTicketSpinLock<32> only allows up to 2^8 - 1 concurrent + * readers and writers. + * + * RWTicketSpinLock<64> only allows up to 2^16 - 1 concurrent + * readers and writers. + * + * RWTicketSpinLock<..., true> (kFavorWriter = true, that is, strict + * writer priority) is NOT reentrant, even for lock_shared(). + * + * The lock will not grant any new shared (read) accesses while a thread + * attempting to acquire the lock in write mode is blocked. (That is, + * if the lock is held in shared mode by N threads, and a thread attempts + * to acquire it in write mode, no one else can acquire it in shared mode + * until these N threads release the lock and then the blocked thread + * acquires and releases the exclusive lock.) This also applies for + * attempts to reacquire the lock in shared mode by threads that already + * hold it in shared mode, making the lock non-reentrant. + * + * RWSpinLock handles 2^30 - 1 concurrent readers. + * + * @author Xin Liu <xliux@fb.com> + */ + +#pragma once + +/* +======================================================================== +Benchmark on (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz) 8 cores(16 HTs) +======================================================================== + +------------------------------------------------------------------------------ +1. Single thread benchmark (read/write lock + unlock overhead) +Benchmark Iters Total t t/iter iter/sec +------------------------------------------------------------------------------- +* BM_RWSpinLockRead 100000 1.786 ms 17.86 ns 53.4M ++30.5% BM_RWSpinLockWrite 100000 2.331 ms 23.31 ns 40.91M ++85.7% BM_RWTicketSpinLock32Read 100000 3.317 ms 33.17 ns 28.75M ++96.0% BM_RWTicketSpinLock32Write 100000 3.5 ms 35 ns 27.25M ++85.6% BM_RWTicketSpinLock64Read 100000 3.315 ms 33.15 ns 28.77M ++96.0% BM_RWTicketSpinLock64Write 100000 3.5 ms 35 ns 27.25M ++85.7% BM_RWTicketSpinLock32FavorWriterRead 100000 3.317 ms 33.17 ns 28.75M ++29.7% BM_RWTicketSpinLock32FavorWriterWrite 100000 2.316 ms 23.16 ns 41.18M ++85.3% BM_RWTicketSpinLock64FavorWriterRead 100000 3.309 ms 33.09 ns 28.82M ++30.2% BM_RWTicketSpinLock64FavorWriterWrite 100000 2.325 ms 23.25 ns 41.02M ++ 175% BM_PThreadRWMutexRead 100000 4.917 ms 49.17 ns 19.4M ++ 166% BM_PThreadRWMutexWrite 100000 4.757 ms 47.57 ns 20.05M + +------------------------------------------------------------------------------ +2. Contention Benchmark 90% read 10% write +Benchmark hits average min max sigma +------------------------------------------------------------------------------ +---------- 8 threads ------------ +RWSpinLock Write 142666 220ns 78ns 40.8us 269ns +RWSpinLock Read 1282297 222ns 80ns 37.7us 248ns +RWTicketSpinLock Write 85692 209ns 71ns 17.9us 252ns +RWTicketSpinLock Read 769571 215ns 78ns 33.4us 251ns +pthread_rwlock_t Write 84248 2.48us 99ns 269us 8.19us +pthread_rwlock_t Read 761646 933ns 101ns 374us 3.25us + +---------- 16 threads ------------ +RWSpinLock Write 124236 237ns 78ns 261us 801ns +RWSpinLock Read 1115807 236ns 78ns 2.27ms 2.17us +RWTicketSpinLock Write 81781 231ns 71ns 31.4us 351ns +RWTicketSpinLock Read 734518 238ns 78ns 73.6us 379ns +pthread_rwlock_t Write 83363 7.12us 99ns 785us 28.1us +pthread_rwlock_t Read 754978 2.18us 101ns 1.02ms 14.3us + +---------- 50 threads ------------ +RWSpinLock Write 131142 1.37us 82ns 7.53ms 68.2us +RWSpinLock Read 1181240 262ns 78ns 6.62ms 12.7us +RWTicketSpinLock Write 83045 397ns 73ns 7.01ms 31.5us +RWTicketSpinLock Read 744133 386ns 78ns 11ms 31.4us +pthread_rwlock_t Write 80849 112us 103ns 4.52ms 263us +pthread_rwlock_t Read 728698 24us 101ns 7.28ms 194us + +*/ + +#include <folly/Portability.h> +#include <folly/portability/Asm.h> + +#if defined(__GNUC__) && (defined(__i386) || FOLLY_X64 || defined(ARCH_K8)) +#define RW_SPINLOCK_USE_X86_INTRINSIC_ +#include <x86intrin.h> +#elif defined(_MSC_VER) && defined(FOLLY_X64) +#define RW_SPINLOCK_USE_X86_INTRINSIC_ +#elif FOLLY_AARCH64 +#define RW_SPINLOCK_USE_X86_INTRINSIC_ +#else +#undef RW_SPINLOCK_USE_X86_INTRINSIC_ +#endif + +// iOS doesn't define _mm_cvtsi64_si128 and friends +#if (FOLLY_SSE >= 2) && !FOLLY_MOBILE && FOLLY_X64 +#define RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ +#else +#undef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ +#endif + +#include <algorithm> +#include <atomic> +#include <thread> + +#include <folly/Likely.h> + +namespace folly { + +/* + * A simple, small (4-bytes), but unfair rwlock. Use it when you want + * a nice writer and don't expect a lot of write/read contention, or + * when you need small rwlocks since you are creating a large number + * of them. + * + * Note that the unfairness here is extreme: if the lock is + * continually accessed for read, writers will never get a chance. If + * the lock can be that highly contended this class is probably not an + * ideal choice anyway. + * + * It currently implements most of the Lockable, SharedLockable and + * UpgradeLockable concepts except the TimedLockable related locking/unlocking + * interfaces. + */ +class RWSpinLock { + enum : int32_t { READER = 4, UPGRADED = 2, WRITER = 1 }; + + public: + constexpr RWSpinLock() : bits_(0) {} + + RWSpinLock(RWSpinLock const&) = delete; + RWSpinLock& operator=(RWSpinLock const&) = delete; + + // Lockable Concept + void lock() { + uint_fast32_t count = 0; + while (!LIKELY(try_lock())) { + if (++count > 1000) { + std::this_thread::yield(); + } + } + } + + // Writer is responsible for clearing up both the UPGRADED and WRITER bits. + void unlock() { + static_assert(READER > WRITER + UPGRADED, "wrong bits!"); + bits_.fetch_and(~(WRITER | UPGRADED), std::memory_order_release); + } + + // SharedLockable Concept + void lock_shared() { + uint_fast32_t count = 0; + while (!LIKELY(try_lock_shared())) { + if (++count > 1000) { + std::this_thread::yield(); + } + } + } + + void unlock_shared() { + bits_.fetch_add(-READER, std::memory_order_release); + } + + // Downgrade the lock from writer status to reader status. + void unlock_and_lock_shared() { + bits_.fetch_add(READER, std::memory_order_acquire); + unlock(); + } + + // UpgradeLockable Concept + void lock_upgrade() { + uint_fast32_t count = 0; + while (!try_lock_upgrade()) { + if (++count > 1000) { + std::this_thread::yield(); + } + } + } + + void unlock_upgrade() { + bits_.fetch_add(-UPGRADED, std::memory_order_acq_rel); + } + + // unlock upgrade and try to acquire write lock + void unlock_upgrade_and_lock() { + int64_t count = 0; + while (!try_unlock_upgrade_and_lock()) { + if (++count > 1000) { + std::this_thread::yield(); + } + } + } + + // unlock upgrade and read lock atomically + void unlock_upgrade_and_lock_shared() { + bits_.fetch_add(READER - UPGRADED, std::memory_order_acq_rel); + } + + // write unlock and upgrade lock atomically + void unlock_and_lock_upgrade() { + // need to do it in two steps here -- as the UPGRADED bit might be OR-ed at + // the same time when other threads are trying do try_lock_upgrade(). + bits_.fetch_or(UPGRADED, std::memory_order_acquire); + bits_.fetch_add(-WRITER, std::memory_order_release); + } + + // Attempt to acquire writer permission. Return false if we didn't get it. + bool try_lock() { + int32_t expect = 0; + return bits_.compare_exchange_strong( + expect, WRITER, std::memory_order_acq_rel); + } + + // Try to get reader permission on the lock. This can fail if we + // find out someone is a writer or upgrader. + // Setting the UPGRADED bit would allow a writer-to-be to indicate + // its intention to write and block any new readers while waiting + // for existing readers to finish and release their read locks. This + // helps avoid starving writers (promoted from upgraders). + bool try_lock_shared() { + // fetch_add is considerably (100%) faster than compare_exchange, + // so here we are optimizing for the common (lock success) case. + int32_t value = bits_.fetch_add(READER, std::memory_order_acquire); + if (UNLIKELY(value & (WRITER | UPGRADED))) { + bits_.fetch_add(-READER, std::memory_order_release); + return false; + } + return true; + } + + // try to unlock upgrade and write lock atomically + bool try_unlock_upgrade_and_lock() { + int32_t expect = UPGRADED; + return bits_.compare_exchange_strong( + expect, WRITER, std::memory_order_acq_rel); + } + + // try to acquire an upgradable lock. + bool try_lock_upgrade() { + int32_t value = bits_.fetch_or(UPGRADED, std::memory_order_acquire); + + // Note: when failed, we cannot flip the UPGRADED bit back, + // as in this case there is either another upgrade lock or a write lock. + // If it's a write lock, the bit will get cleared up when that lock's done + // with unlock(). + return ((value & (UPGRADED | WRITER)) == 0); + } + + // mainly for debugging purposes. + int32_t bits() const { + return bits_.load(std::memory_order_acquire); + } + + class FOLLY_NODISCARD ReadHolder; + class FOLLY_NODISCARD UpgradedHolder; + class FOLLY_NODISCARD WriteHolder; + + class FOLLY_NODISCARD ReadHolder { + public: + explicit ReadHolder(RWSpinLock* lock) : lock_(lock) { + if (lock_) { + lock_->lock_shared(); + } + } + + explicit ReadHolder(RWSpinLock& lock) : lock_(&lock) { + lock_->lock_shared(); + } + + ReadHolder(ReadHolder&& other) noexcept : lock_(other.lock_) { + other.lock_ = nullptr; + } + + // down-grade + explicit ReadHolder(UpgradedHolder&& upgraded) : lock_(upgraded.lock_) { + upgraded.lock_ = nullptr; + if (lock_) { + lock_->unlock_upgrade_and_lock_shared(); + } + } + + explicit ReadHolder(WriteHolder&& writer) : lock_(writer.lock_) { + writer.lock_ = nullptr; + if (lock_) { + lock_->unlock_and_lock_shared(); + } + } + + ReadHolder& operator=(ReadHolder&& other) { + using std::swap; + swap(lock_, other.lock_); + return *this; + } + + ReadHolder(const ReadHolder& other) = delete; + ReadHolder& operator=(const ReadHolder& other) = delete; + + ~ReadHolder() { + if (lock_) { + lock_->unlock_shared(); + } + } + + void reset(RWSpinLock* lock = nullptr) { + if (lock == lock_) { + return; + } + if (lock_) { + lock_->unlock_shared(); + } + lock_ = lock; + if (lock_) { + lock_->lock_shared(); + } + } + + void swap(ReadHolder* other) { + std::swap(lock_, other->lock_); + } + + private: + friend class UpgradedHolder; + friend class WriteHolder; + RWSpinLock* lock_; + }; + + class FOLLY_NODISCARD UpgradedHolder { + public: + explicit UpgradedHolder(RWSpinLock* lock) : lock_(lock) { + if (lock_) { + lock_->lock_upgrade(); + } + } + + explicit UpgradedHolder(RWSpinLock& lock) : lock_(&lock) { + lock_->lock_upgrade(); + } + + explicit UpgradedHolder(WriteHolder&& writer) { + lock_ = writer.lock_; + writer.lock_ = nullptr; + if (lock_) { + lock_->unlock_and_lock_upgrade(); + } + } + + UpgradedHolder(UpgradedHolder&& other) noexcept : lock_(other.lock_) { + other.lock_ = nullptr; + } + + UpgradedHolder& operator=(UpgradedHolder&& other) { + using std::swap; + swap(lock_, other.lock_); + return *this; + } + + UpgradedHolder(const UpgradedHolder& other) = delete; + UpgradedHolder& operator=(const UpgradedHolder& other) = delete; + + ~UpgradedHolder() { + if (lock_) { + lock_->unlock_upgrade(); + } + } + + void reset(RWSpinLock* lock = nullptr) { + if (lock == lock_) { + return; + } + if (lock_) { + lock_->unlock_upgrade(); + } + lock_ = lock; + if (lock_) { + lock_->lock_upgrade(); + } + } + + void swap(UpgradedHolder* other) { + using std::swap; + swap(lock_, other->lock_); + } + + private: + friend class WriteHolder; + friend class ReadHolder; + RWSpinLock* lock_; + }; + + class FOLLY_NODISCARD WriteHolder { + public: + explicit WriteHolder(RWSpinLock* lock) : lock_(lock) { + if (lock_) { + lock_->lock(); + } + } + + explicit WriteHolder(RWSpinLock& lock) : lock_(&lock) { + lock_->lock(); + } + + // promoted from an upgrade lock holder + explicit WriteHolder(UpgradedHolder&& upgraded) { + lock_ = upgraded.lock_; + upgraded.lock_ = nullptr; + if (lock_) { + lock_->unlock_upgrade_and_lock(); + } + } + + WriteHolder(WriteHolder&& other) noexcept : lock_(other.lock_) { + other.lock_ = nullptr; + } + + WriteHolder& operator=(WriteHolder&& other) { + using std::swap; + swap(lock_, other.lock_); + return *this; + } + + WriteHolder(const WriteHolder& other) = delete; + WriteHolder& operator=(const WriteHolder& other) = delete; + + ~WriteHolder() { + if (lock_) { + lock_->unlock(); + } + } + + void reset(RWSpinLock* lock = nullptr) { + if (lock == lock_) { + return; + } + if (lock_) { + lock_->unlock(); + } + lock_ = lock; + if (lock_) { + lock_->lock(); + } + } + + void swap(WriteHolder* other) { + using std::swap; + swap(lock_, other->lock_); + } + + private: + friend class ReadHolder; + friend class UpgradedHolder; + RWSpinLock* lock_; + }; + + private: + std::atomic<int32_t> bits_; +}; + +#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_ +// A more balanced Read-Write spin lock implemented based on GCC intrinsics. + +namespace detail { +template <size_t kBitWidth> +struct RWTicketIntTrait { + static_assert( + kBitWidth == 32 || kBitWidth == 64, + "bit width has to be either 32 or 64 "); +}; + +template <> +struct RWTicketIntTrait<64> { + typedef uint64_t FullInt; + typedef uint32_t HalfInt; + typedef uint16_t QuarterInt; + +#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ + static __m128i make128(const uint16_t v[4]) { + return _mm_set_epi16( + 0, 0, 0, 0, short(v[3]), short(v[2]), short(v[1]), short(v[0])); + } + static inline __m128i fromInteger(uint64_t from) { + return _mm_cvtsi64_si128(int64_t(from)); + } + static inline uint64_t toInteger(__m128i in) { + return uint64_t(_mm_cvtsi128_si64(in)); + } + static inline uint64_t addParallel(__m128i in, __m128i kDelta) { + return toInteger(_mm_add_epi16(in, kDelta)); + } +#endif +}; + +template <> +struct RWTicketIntTrait<32> { + typedef uint32_t FullInt; + typedef uint16_t HalfInt; + typedef uint8_t QuarterInt; + +#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ + static __m128i make128(const uint8_t v[4]) { + // clang-format off + return _mm_set_epi8( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + char(v[3]), char(v[2]), char(v[1]), char(v[0])); + // clang-format on + } + static inline __m128i fromInteger(uint32_t from) { + return _mm_cvtsi32_si128(int32_t(from)); + } + static inline uint32_t toInteger(__m128i in) { + return uint32_t(_mm_cvtsi128_si32(in)); + } + static inline uint32_t addParallel(__m128i in, __m128i kDelta) { + return toInteger(_mm_add_epi8(in, kDelta)); + } +#endif +}; +} // namespace detail + +template <size_t kBitWidth, bool kFavorWriter = false> +class RWTicketSpinLockT { + typedef detail::RWTicketIntTrait<kBitWidth> IntTraitType; + typedef typename detail::RWTicketIntTrait<kBitWidth>::FullInt FullInt; + typedef typename detail::RWTicketIntTrait<kBitWidth>::HalfInt HalfInt; + typedef typename detail::RWTicketIntTrait<kBitWidth>::QuarterInt QuarterInt; + + union RWTicket { + constexpr RWTicket() : whole(0) {} + FullInt whole; + HalfInt readWrite; + __extension__ struct { + QuarterInt write; + QuarterInt read; + QuarterInt users; + }; + } ticket; + + private: // Some x64-specific utilities for atomic access to ticket. + template <class T> + static T load_acquire(T* addr) { + T t = *addr; // acquire barrier + asm_volatile_memory(); + return t; + } + + template <class T> + static void store_release(T* addr, T v) { + asm_volatile_memory(); + *addr = v; // release barrier + } + + public: + constexpr RWTicketSpinLockT() {} + + RWTicketSpinLockT(RWTicketSpinLockT const&) = delete; + RWTicketSpinLockT& operator=(RWTicketSpinLockT const&) = delete; + + void lock() { + if (kFavorWriter) { + writeLockAggressive(); + } else { + writeLockNice(); + } + } + + /* + * Both try_lock and try_lock_shared diverge in our implementation from the + * lock algorithm described in the link above. + * + * In the read case, it is undesirable that the readers could wait + * for another reader (before increasing ticket.read in the other + * implementation). Our approach gives up on + * first-come-first-serve, but our benchmarks showed improve + * performance for both readers and writers under heavily contended + * cases, particularly when the number of threads exceeds the number + * of logical CPUs. + * + * We have writeLockAggressive() using the original implementation + * for a writer, which gives some advantage to the writer over the + * readers---for that path it is guaranteed that the writer will + * acquire the lock after all the existing readers exit. + */ + bool try_lock() { + RWTicket t; + FullInt old = t.whole = load_acquire(&ticket.whole); + if (t.users != t.write) { + return false; + } + ++t.users; + return __sync_bool_compare_and_swap(&ticket.whole, old, t.whole); + } + + /* + * Call this if you want to prioritize writer to avoid starvation. + * Unlike writeLockNice, immediately acquires the write lock when + * the existing readers (arriving before the writer) finish their + * turns. + */ + void writeLockAggressive() { + // std::this_thread::yield() is needed here to avoid a pathology if the + // number of threads attempting concurrent writes is >= the number of real + // cores allocated to this process. This is less likely than the + // corresponding situation in lock_shared(), but we still want to + // avoid it + uint_fast32_t count = 0; + QuarterInt val = __sync_fetch_and_add(&ticket.users, 1); + while (val != load_acquire(&ticket.write)) { + asm_volatile_pause(); + if (UNLIKELY(++count > 1000)) { + std::this_thread::yield(); + } + } + } + + // Call this when the writer should be nicer to the readers. + void writeLockNice() { + // Here it doesn't cpu-relax the writer. + // + // This is because usually we have many more readers than the + // writers, so the writer has less chance to get the lock when + // there are a lot of competing readers. The aggressive spinning + // can help to avoid starving writers. + // + // We don't worry about std::this_thread::yield() here because the caller + // has already explicitly abandoned fairness. + while (!try_lock()) { + } + } + + // Atomically unlock the write-lock from writer and acquire the read-lock. + void unlock_and_lock_shared() { + QuarterInt val = __sync_fetch_and_add(&ticket.read, 1); + } + + // Release writer permission on the lock. + void unlock() { + RWTicket t; + t.whole = load_acquire(&ticket.whole); + +#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ + FullInt old = t.whole; + // SSE2 can reduce the lock and unlock overhead by 10% + static const QuarterInt kDeltaBuf[4] = {1, 1, 0, 0}; // write/read/user + static const __m128i kDelta = IntTraitType::make128(kDeltaBuf); + __m128i m = IntTraitType::fromInteger(old); + t.whole = IntTraitType::addParallel(m, kDelta); +#else + ++t.read; + ++t.write; +#endif + store_release(&ticket.readWrite, t.readWrite); + } + + void lock_shared() { + // std::this_thread::yield() is important here because we can't grab the + // shared lock if there is a pending writeLockAggressive, so we + // need to let threads that already have a shared lock complete + uint_fast32_t count = 0; + while (!LIKELY(try_lock_shared())) { + asm_volatile_pause(); + if (UNLIKELY((++count & 1023) == 0)) { + std::this_thread::yield(); + } + } + } + + bool try_lock_shared() { + RWTicket t, old; + old.whole = t.whole = load_acquire(&ticket.whole); + old.users = old.read; +#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_ + // SSE2 may reduce the total lock and unlock overhead by 10% + static const QuarterInt kDeltaBuf[4] = {0, 1, 1, 0}; // write/read/user + static const __m128i kDelta = IntTraitType::make128(kDeltaBuf); + __m128i m = IntTraitType::fromInteger(old.whole); + t.whole = IntTraitType::addParallel(m, kDelta); +#else + ++t.read; + ++t.users; +#endif + return __sync_bool_compare_and_swap(&ticket.whole, old.whole, t.whole); + } + + void unlock_shared() { + __sync_fetch_and_add(&ticket.write, 1); + } + + class FOLLY_NODISCARD WriteHolder; + + typedef RWTicketSpinLockT<kBitWidth, kFavorWriter> RWSpinLock; + class FOLLY_NODISCARD ReadHolder { + public: + ReadHolder(ReadHolder const&) = delete; + ReadHolder& operator=(ReadHolder const&) = delete; + + explicit ReadHolder(RWSpinLock* lock) : lock_(lock) { + if (lock_) { + lock_->lock_shared(); + } + } + + explicit ReadHolder(RWSpinLock& lock) : lock_(&lock) { + if (lock_) { + lock_->lock_shared(); + } + } + + // atomically unlock the write-lock from writer and acquire the read-lock + explicit ReadHolder(WriteHolder* writer) : lock_(nullptr) { + std::swap(this->lock_, writer->lock_); + if (lock_) { + lock_->unlock_and_lock_shared(); + } + } + + ~ReadHolder() { + if (lock_) { + lock_->unlock_shared(); + } + } + + void reset(RWSpinLock* lock = nullptr) { + if (lock_) { + lock_->unlock_shared(); + } + lock_ = lock; + if (lock_) { + lock_->lock_shared(); + } + } + + void swap(ReadHolder* other) { + std::swap(this->lock_, other->lock_); + } + + private: + RWSpinLock* lock_; + }; + + class FOLLY_NODISCARD WriteHolder { + public: + WriteHolder(WriteHolder const&) = delete; + WriteHolder& operator=(WriteHolder const&) = delete; + + explicit WriteHolder(RWSpinLock* lock) : lock_(lock) { + if (lock_) { + lock_->lock(); + } + } + explicit WriteHolder(RWSpinLock& lock) : lock_(&lock) { + if (lock_) { + lock_->lock(); + } + } + + ~WriteHolder() { + if (lock_) { + lock_->unlock(); + } + } + + void reset(RWSpinLock* lock = nullptr) { + if (lock == lock_) { + return; + } + if (lock_) { + lock_->unlock(); + } + lock_ = lock; + if (lock_) { + lock_->lock(); + } + } + + void swap(WriteHolder* other) { + std::swap(this->lock_, other->lock_); + } + + private: + friend class ReadHolder; + RWSpinLock* lock_; + }; +}; + +typedef RWTicketSpinLockT<32> RWTicketSpinLock32; +typedef RWTicketSpinLockT<64> RWTicketSpinLock64; + +#endif // RW_SPINLOCK_USE_X86_INTRINSIC_ + +} // namespace folly + +#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_ +#undef RW_SPINLOCK_USE_X86_INTRINSIC_ +#endif diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Rcu-inl.h b/ios/Pods/Flipper-Folly/folly/synchronization/Rcu-inl.h new file mode 100644 index 000000000..c43013632 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Rcu-inl.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/Function.h> +#include <folly/detail/AtFork.h> +#include <folly/detail/TurnSequencer.h> + +namespace folly { + +template <typename Tag> +bool rcu_domain<Tag>::singleton_ = false; + +template <typename Tag> +rcu_domain<Tag>::rcu_domain(Executor* executor) noexcept + : executor_(executor ? executor : &QueuedImmediateExecutor::instance()) { + // Please use a unique tag for each domain. + CHECK(!singleton_); + singleton_ = true; + + // Register fork handlers. Holding read locks across fork is not + // supported. Using read locks in other atfork handlers is not + // supported. Other atfork handlers launching new child threads + // that use read locks *is* supported. + detail::AtFork::registerHandler( + this, + [this]() { return syncMutex_.try_lock(); }, + [this]() { syncMutex_.unlock(); }, + [this]() { + counters_.resetAfterFork(); + syncMutex_.unlock(); + }); +} + +template <typename Tag> +rcu_domain<Tag>::~rcu_domain() { + detail::AtFork::unregisterHandler(this); +} + +template <typename Tag> +rcu_token<Tag> rcu_domain<Tag>::lock_shared() { + auto idx = version_.load(std::memory_order_acquire); + idx &= 1; + counters_.increment(idx); + + return rcu_token<Tag>(idx); +} + +template <typename Tag> +void rcu_domain<Tag>::unlock_shared(rcu_token<Tag>&& token) { + DCHECK(0 == token.epoch_ || 1 == token.epoch_); + counters_.decrement(token.epoch_); +} + +template <typename Tag> +template <typename T> +void rcu_domain<Tag>::call(T&& cbin) { + auto node = new list_node; + node->cb_ = [node, cb = std::forward<T>(cbin)]() { + cb(); + delete node; + }; + retire(node); +} + +template <typename Tag> +void rcu_domain<Tag>::retire(list_node* node) noexcept { + q_.push(node); + + // Note that it's likely we hold a read lock here, + // so we can only half_sync(false). half_sync(true) + // or a synchronize() call might block forever. + uint64_t time = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + auto syncTime = syncTime_.load(std::memory_order_relaxed); + if (time > syncTime + syncTimePeriod_ && + syncTime_.compare_exchange_strong( + syncTime, time, std::memory_order_relaxed)) { + list_head finished; + { + std::lock_guard<std::mutex> g(syncMutex_); + half_sync(false, finished); + } + // callbacks are called outside of syncMutex_ + finished.forEach( + [&](list_node* item) { executor_->add(std::move(item->cb_)); }); + } +} + +template <typename Tag> +void rcu_domain<Tag>::synchronize() noexcept { + auto curr = version_.load(std::memory_order_acquire); + // Target is two epochs away. + auto target = curr + 2; + while (true) { + // Try to assign ourselves to do the sync work. + // If someone else is already assigned, we can wait for + // the work to be finished by waiting on turn_. + auto work = work_.load(std::memory_order_acquire); + auto tmp = work; + if (work < target && work_.compare_exchange_strong(tmp, target)) { + list_head finished; + { + std::lock_guard<std::mutex> g(syncMutex_); + while (version_.load(std::memory_order_acquire) < target) { + half_sync(true, finished); + } + } + // callbacks are called outside of syncMutex_ + finished.forEach( + [&](list_node* node) { executor_->add(std::move(node->cb_)); }); + return; + } else { + if (version_.load(std::memory_order_acquire) >= target) { + return; + } + std::atomic<uint32_t> cutoff{100}; + // Wait for someone to finish the work. + turn_.tryWaitForTurn(work, cutoff, false); + } + } +} + +/* + * Not multithread safe, but it could be with proper version + * checking and stronger increment of version. See + * https://github.com/pramalhe/ConcurrencyFreaks/blob/master/papers/gracesharingurcu-2016.pdf + * + * This version, however, can go to sleep if there are outstanding + * readers, and does not spin or need rescheduling, unless blocking = false. + */ +template <typename Tag> +void rcu_domain<Tag>::half_sync(bool blocking, list_head& finished) { + uint64_t curr = version_.load(std::memory_order_acquire); + auto next = curr + 1; + + // Push all work to a queue for moving through two epochs. One + // version is not enough because of late readers of the version_ + // counter in lock_shared. + // + // Note that for a similar reason we can't swap out the q here, + // and instead drain it, so concurrent calls to call() are safe, + // and will wait for the next epoch. + q_.collect(queues_[0]); + + if (blocking) { + counters_.waitForZero(next & 1); + } else { + if (counters_.readFull(next & 1) != 0) { + return; + } + } + + // Run callbacks that have been through two epochs, and swap queues + // for those only through a single epoch. + finished.splice(queues_[1]); + queues_[1].splice(queues_[0]); + + version_.store(next, std::memory_order_release); + // Notify synchronous waiters in synchronize(). + turn_.completeTurn(curr); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.cpp new file mode 100644 index 000000000..49dc1951b --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/SanitizeThread.h> + +// abseil uses size_t for size params while other FB code and libraries use +// long, so it is helpful to keep these declarations out of widely-included +// header files. + +extern "C" FOLLY_ATTR_WEAK void +AnnotateRWLockCreate(const char* f, int l, const volatile void* addr); + +extern "C" FOLLY_ATTR_WEAK void +AnnotateRWLockCreateStatic(const char* f, int l, const volatile void* addr); + +extern "C" FOLLY_ATTR_WEAK void +AnnotateRWLockDestroy(const char* f, int l, const volatile void* addr); + +extern "C" FOLLY_ATTR_WEAK void +AnnotateRWLockAcquired(const char* f, int l, const volatile void* addr, long w); + +extern "C" FOLLY_ATTR_WEAK void +AnnotateRWLockReleased(const char* f, int l, const volatile void* addr, long w); + +extern "C" FOLLY_ATTR_WEAK void AnnotateBenignRaceSized( + const char* f, + int l, + const volatile void* addr, + long size, + const char* desc); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreReadsBegin(const char* f, int l); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreReadsEnd(const char* f, int l); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreWritesBegin(const char* f, int l); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreWritesEnd(const char* f, int l); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreSyncBegin(const char* f, int l); + +extern "C" FOLLY_ATTR_WEAK void AnnotateIgnoreSyncEnd(const char* f, int l); + +#ifdef _MSC_VER +#define FOLLY_SANITIZE_THREAD_CALL_HOOK(name, ...) [](...) {}(__VA_ARGS__) +#else +#define FOLLY_SANITIZE_THREAD_CALL_HOOK(name, ...) name(__VA_ARGS__) +#endif + +namespace folly { +namespace detail { + +void annotate_rwlock_create_impl( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateRWLockCreate, f, l, addr); + } +} + +void annotate_rwlock_create_static_impl( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateRWLockCreateStatic, f, l, addr); + } +} + +void annotate_rwlock_destroy_impl( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateRWLockDestroy, f, l, addr); + } +} + +void annotate_rwlock_acquired_impl( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK( + AnnotateRWLockAcquired, f, l, addr, static_cast<long>(w)); + } +} + +void annotate_rwlock_try_acquired_impl( + void const volatile* const addr, + annotate_rwlock_level const w, + bool const result, + char const* const f, + int const l) { + if (result) { + annotate_rwlock_acquired(addr, w, f, l); + } +} + +void annotate_rwlock_released_impl( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK( + AnnotateRWLockReleased, f, l, addr, static_cast<long>(w)); + } +} + +void annotate_benign_race_sized_impl( + const volatile void* addr, + long size, + const char* desc, + const char* f, + int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK( + AnnotateBenignRaceSized, f, l, addr, size, desc); + } +} + +void annotate_ignore_reads_begin_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreReadsBegin, f, l); + } +} + +void annotate_ignore_reads_end_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreReadsEnd, f, l); + } +} + +void annotate_ignore_writes_begin_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreWritesBegin, f, l); + } +} + +void annotate_ignore_writes_end_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreWritesEnd, f, l); + } +} + +void annotate_ignore_sync_begin_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreSyncBegin, f, l); + } +} + +void annotate_ignore_sync_end_impl(const char* f, int l) { + if (kIsSanitizeThread) { + FOLLY_SANITIZE_THREAD_CALL_HOOK(AnnotateIgnoreSyncEnd, f, l); + } +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.h b/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.h new file mode 100644 index 000000000..d1d116075 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/SanitizeThread.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> + +namespace folly { + +enum class annotate_rwlock_level : long { + rdlock = 0, + wrlock = 1, +}; + +namespace detail { + +void annotate_rwlock_create_impl( + void const volatile* const addr, + char const* const f, + int const l); + +void annotate_rwlock_create_static_impl( + void const volatile* const addr, + char const* const f, + int const l); + +void annotate_rwlock_destroy_impl( + void const volatile* const addr, + char const* const f, + int const l); + +void annotate_rwlock_acquired_impl( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l); + +void annotate_rwlock_released_impl( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l); + +void annotate_benign_race_sized_impl( + const volatile void* addr, + long size, + const char* desc, + const char* f, + int l); + +void annotate_ignore_reads_begin_impl(const char* f, int l); + +void annotate_ignore_reads_end_impl(const char* f, int l); + +void annotate_ignore_writes_begin_impl(const char* f, int l); + +void annotate_ignore_writes_end_impl(const char* f, int l); + +void annotate_ignore_sync_begin_impl(const char* f, int l); + +void annotate_ignore_sync_end_impl(const char* f, int l); + +} // namespace detail + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_create( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_rwlock_create_impl(addr, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_create_static( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_rwlock_create_static_impl(addr, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_destroy( + void const volatile* const addr, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_rwlock_destroy_impl(addr, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_acquired( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_rwlock_acquired_impl(addr, w, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_try_acquired( + void const volatile* const addr, + annotate_rwlock_level const w, + bool const result, + char const* const f, + int const l) { + if (result) { + annotate_rwlock_acquired(addr, w, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_rwlock_released( + void const volatile* const addr, + annotate_rwlock_level const w, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_rwlock_released_impl(addr, w, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_benign_race_sized( + void const volatile* const addr, + long const size, + char const* const desc, + char const* const f, + int const l) { + if (kIsSanitizeThread) { + detail::annotate_benign_race_sized_impl(addr, size, desc, f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_reads_begin( + const char* f, + int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_reads_begin_impl(f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_reads_end( + const char* f, + int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_reads_end_impl(f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_writes_begin( + const char* f, + int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_writes_begin_impl(f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_writes_end( + const char* f, + int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_writes_end_impl(f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_sync_begin( + const char* f, + int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_sync_begin_impl(f, l); + } +} + +FOLLY_ALWAYS_INLINE static void annotate_ignore_sync_end(const char* f, int l) { + if (kIsSanitizeThread) { + detail::annotate_ignore_sync_end_impl(f, l); + } +} + +class annotate_ignore_thread_sanitizer_guard { + public: + annotate_ignore_thread_sanitizer_guard(const char* file, int line) + : file_(file), line_(line) { + annotate_ignore_reads_begin(file_, line_); + annotate_ignore_writes_begin(file_, line_); + annotate_ignore_sync_begin(file_, line_); + } + + annotate_ignore_thread_sanitizer_guard( + const annotate_ignore_thread_sanitizer_guard&) = delete; + annotate_ignore_thread_sanitizer_guard& operator=( + const annotate_ignore_thread_sanitizer_guard&) = delete; + + ~annotate_ignore_thread_sanitizer_guard() { + annotate_ignore_reads_end(file_, line_); + annotate_ignore_writes_end(file_, line_); + annotate_ignore_sync_end(file_, line_); + } + + private: + const char* file_; + int line_; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h b/ios/Pods/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h new file mode 100644 index 000000000..7e299fa69 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Likely.h> +#include <folly/detail/Futex.h> +#include <folly/detail/MemoryIdler.h> +#include <folly/portability/Asm.h> +#include <folly/synchronization/WaitOptions.h> +#include <folly/synchronization/detail/Spin.h> + +#include <glog/logging.h> + +#include <atomic> + +namespace folly { + +/// SaturatingSemaphore is a flag that allows concurrent posting by +/// multiple posters and concurrent non-destructive waiting by +/// multiple waiters. +/// +/// A SaturatingSemaphore allows one or more waiter threads to check, +/// spin, or block, indefinitely or with timeout, for a flag to be set +/// by one or more poster threads. By setting the flag, posters +/// announce to waiters (that may be already waiting or will check +/// the flag in the future) that some condition is true. Posts to an +/// already set flag are idempotent. +/// +/// SaturatingSemaphore is called so because it behaves like a hybrid +/// binary/counted _semaphore_ with values zero and infinity, and +/// post() and wait() functions. It is called _saturating_ because one +/// post() is enough to set it to infinity and to satisfy any number +/// of wait()-s. Once set (to infinity) it remains unchanged by +/// subsequent post()-s and wait()-s, until it is reset() back to +/// zero. +/// +/// The implementation of SaturatingSemaphore is based on that of +/// Baton. It includes no internal padding, and is only 4 bytes in +/// size. Any alignment or padding to avoid false sharing is up to +/// the user. +/// SaturatingSemaphore differs from Baton as follows: +/// - Baton allows at most one call to post(); this allows any number +/// and concurrently. +/// - Baton allows at most one successful call to any wait variant; +/// this allows any number and concurrently. +/// +/// Template parameter: +/// - bool MayBlock: If false, waiting operations spin only. If +/// true, timed and wait operations may block; adds an atomic +/// instruction to the critical path of posters. +/// +/// Wait options: +/// WaitOptions contains optional per call setting for spin-max duration: +/// Calls to wait(), try_wait_until(), and try_wait_for() block only after the +/// passage of the spin-max period. The default spin-max duration is 10 usec. +/// The spin-max option is applicable only if MayBlock is true. +/// +/// Functions: +/// bool ready(): +/// Returns true if the flag is set by a call to post, otherwise false. +/// Equivalent to try_wait, but available on const receivers. +/// void reset(); +/// Clears the flag. +/// void post(); +/// Sets the flag and wakes all current waiters, i.e., causes all +/// concurrent calls to wait, try_wait_for, and try_wait_until to +/// return. +/// void wait( +/// WaitOptions opt = wait_options()); +/// Waits for the flag to be set by a call to post. +/// bool try_wait(); +/// Returns true if the flag is set by a call to post, otherwise false. +/// bool try_wait_until( +/// time_point& deadline, +/// WaitOptions& = wait_options()); +/// Returns true if the flag is set by a call to post before the +/// deadline, otherwise false. +/// bool try_wait_for( +/// duration&, +/// WaitOptions& = wait_options()); +/// Returns true if the flag is set by a call to post before the +/// expiration of the specified duration, otherwise false. +/// +/// Usage: +/// @code +/// SaturatingSemaphore</* MayBlock = */ true> f; +/// ASSERT_FALSE(f.try_wait()); +/// ASSERT_FALSE(f.try_wait_until( +/// std::chrono::steady_clock::now() + std::chrono::microseconds(1))); +/// ASSERT_FALSE(f.try_wait_until( +/// std::chrono::steady_clock::now() + std::chrono::microseconds(1), +/// f.wait_options().spin_max(std::chrono::microseconds(1)))); +/// f.post(); +/// f.post(); +/// f.wait(); +/// f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100))); +/// ASSERT_TRUE(f.try_wait()); +/// ASSERT_TRUE(f.try_wait_until( +/// std::chrono::steady_clock::now() + std::chrono::microseconds(1))); +/// f.wait(); +/// f.reset(); +/// ASSERT_FALSE(f.try_wait()); +/// @endcode + +template <bool MayBlock, template <typename> class Atom = std::atomic> +class SaturatingSemaphore { + detail::Futex<Atom> state_; + + enum State : uint32_t { + NOTREADY = 0, + READY = 1, + BLOCKED = 2, + }; + + public: + FOLLY_ALWAYS_INLINE static constexpr WaitOptions wait_options() { + return {}; + } + + /** constructor */ + constexpr SaturatingSemaphore() noexcept : state_(NOTREADY) {} + + /** destructor */ + ~SaturatingSemaphore() {} + + /** ready */ + FOLLY_ALWAYS_INLINE bool ready() const noexcept { + return state_.load(std::memory_order_acquire) == READY; + } + + /** reset */ + void reset() noexcept { + state_.store(NOTREADY, std::memory_order_relaxed); + } + + /** post */ + FOLLY_ALWAYS_INLINE void post() noexcept { + if (!MayBlock) { + state_.store(READY, std::memory_order_release); + } else { + postFastWaiterMayBlock(); + } + } + + /** wait */ + FOLLY_ALWAYS_INLINE + void wait(const WaitOptions& opt = wait_options()) noexcept { + try_wait_until(std::chrono::steady_clock::time_point::max(), opt); + } + + /** try_wait */ + FOLLY_ALWAYS_INLINE bool try_wait() noexcept { + return ready(); + } + + /** try_wait_until */ + template <typename Clock, typename Duration> + FOLLY_ALWAYS_INLINE bool try_wait_until( + const std::chrono::time_point<Clock, Duration>& deadline, + const WaitOptions& opt = wait_options()) noexcept { + if (LIKELY(try_wait())) { + return true; + } + return tryWaitSlow(deadline, opt); + } + + /** try_wait_for */ + template <class Rep, class Period> + FOLLY_ALWAYS_INLINE bool try_wait_for( + const std::chrono::duration<Rep, Period>& duration, + const WaitOptions& opt = wait_options()) noexcept { + if (LIKELY(try_wait())) { + return true; + } + auto deadline = std::chrono::steady_clock::now() + duration; + return tryWaitSlow(deadline, opt); + } + + private: + FOLLY_ALWAYS_INLINE void postFastWaiterMayBlock() noexcept { + uint32_t before = NOTREADY; + if (LIKELY(state_.compare_exchange_strong( + before, + READY, + std::memory_order_release, + std::memory_order_relaxed))) { + return; + } + postSlowWaiterMayBlock(before); + } + + void postSlowWaiterMayBlock(uint32_t before) noexcept; // defined below + + template <typename Clock, typename Duration> + bool tryWaitSlow( + const std::chrono::time_point<Clock, Duration>& deadline, + const WaitOptions& opt) noexcept; // defined below +}; + +/// +/// Member function definitioons +/// + +/** postSlowWaiterMayBlock */ +template <bool MayBlock, template <typename> class Atom> +FOLLY_NOINLINE void SaturatingSemaphore<MayBlock, Atom>::postSlowWaiterMayBlock( + uint32_t before) noexcept { + while (true) { + if (before == NOTREADY) { + if (state_.compare_exchange_strong( + before, + READY, + std::memory_order_release, + std::memory_order_relaxed)) { + return; + } + } + if (before == READY) { // Only if multiple posters + // The reason for not simply returning (without the following + // steps) is to prevent the following case: + // + // T1: T2: T3: + // local1.post(); local2.post(); global.wait(); + // global.post(); global.post(); global.reset(); + // seq_cst fence + // local1.try_wait() == true; + // local2.try_wait() == false; + // + // This following steps correspond to T2's global.post(), where + // global is already posted by T1. + // + // The following fence and load guarantee that T3 does not miss + // T2's prior stores, i.e., local2.post() in this example. + // + // The following case is prevented: + // + // Starting with local2 == NOTREADY and global == READY + // + // T2: T3: + // store READY to local2 // post store NOTREADY to global // reset + // seq_cst fenc seq_cst fence + // load READY from global // post load NOTREADY from local2 // try_wait + // + std::atomic_thread_fence(std::memory_order_seq_cst); + before = state_.load(std::memory_order_relaxed); + if (before == READY) { + return; + } + continue; + } + DCHECK_EQ(before, BLOCKED); + if (state_.compare_exchange_strong( + before, + READY, + std::memory_order_release, + std::memory_order_relaxed)) { + detail::futexWake(&state_); + return; + } + } +} + +/** tryWaitSlow */ +template <bool MayBlock, template <typename> class Atom> +template <typename Clock, typename Duration> +FOLLY_NOINLINE bool SaturatingSemaphore<MayBlock, Atom>::tryWaitSlow( + const std::chrono::time_point<Clock, Duration>& deadline, + const WaitOptions& opt) noexcept { + switch (detail::spin_pause_until(deadline, opt, [=] { return ready(); })) { + case detail::spin_result::success: + return true; + case detail::spin_result::timeout: + return false; + case detail::spin_result::advance: + break; + } + + if (!MayBlock) { + switch (detail::spin_yield_until(deadline, [=] { return ready(); })) { + case detail::spin_result::success: + return true; + case detail::spin_result::timeout: + return false; + case detail::spin_result::advance: + break; + } + } + + auto before = state_.load(std::memory_order_relaxed); + while (before == NOTREADY && + !state_.compare_exchange_strong( + before, + BLOCKED, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + if (before == READY) { + // TODO: move the acquire to the compare_exchange failure load after C++17 + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + } + + while (true) { + auto rv = detail::MemoryIdler::futexWaitUntil(state_, BLOCKED, deadline); + if (rv == detail::FutexResult::TIMEDOUT) { + assert(deadline != (std::chrono::time_point<Clock, Duration>::max())); + return false; + } + + if (ready()) { + return true; + } + } +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/SmallLocks.h b/ios/Pods/Flipper-Folly/folly/synchronization/SmallLocks.h new file mode 100644 index 000000000..a5c196b7d --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/SmallLocks.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/* + * This header defines a few very small mutex types. These are useful + * in highly memory-constrained environments where contention is + * unlikely. + * + * Note: these locks are for use when you aren't likely to contend on + * the critical section, or when the critical section is incredibly + * small. Given that, both of the locks defined in this header are + * inherently unfair: that is, the longer a thread is waiting, the + * longer it waits between attempts to acquire, so newer waiters are + * more likely to get the mutex. For the intended use-case this is + * fine. + * + * @author Keith Adams <kma@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +#include <folly/MicroLock.h> +#include <folly/synchronization/MicroSpinLock.h> + +#include <folly/Portability.h> +#if FOLLY_X64 || FOLLY_AARCH64 || FOLLY_PPC64 +#include <folly/synchronization/PicoSpinLock.h> +#endif diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Tearable.h b/ios/Pods/Flipper-Folly/folly/synchronization/Tearable.h new file mode 100644 index 000000000..f72a7a9e9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Tearable.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> +#include <cstring> +#include <type_traits> + +#include <folly/Traits.h> + +namespace folly { + +/** + * This class allows you to perform torn loads and stores on the bits of a + * trivially-copyable type T without triggering undefined behavior. You may + * encounter corrupt data, but should not encounter nasal demons. + * + * This class provides no atomicity or memory ordering. Loads and stores are + * expected often to be data races. Synchronization is expected to be provided + * externally, and this class is helpful in building higher-level optimistic + * concurrency tools in combination with externally-provided synchronization. + * + * To see why this is useful, consider the guarantees provided by + * std::atomic<T>. It ensures that every load returns a T that was stored in the + * atomic. If T is too large to be read/written with a single load/store + * instruction, std::atomic<T> falls back to locking to provide this guarantee. + * Users pay this cost even if they have some higher-level mechanism (an + * external lock, version numbers, other application-level reasoning) that makes + * them resilient to torn reads. Tearable<T> allows concurrent access without + * these costs. + * + * For types smaller than the processor word size, prefer std::atomic<T>. + */ +template <typename T> +class Tearable { + public: + // We memcpy the object representation, and the destructor would not know how + // to deal with an object state it doesn't understand. + static_assert( + is_trivially_copyable<T>::value, + "Tearable types must be trivially copyable."); + + Tearable() noexcept { + for (std::size_t i = 0; i < kNumDataWords; ++i) { + std::atomic_init(&data_[i], RawWord{}); + } + } + + Tearable(const T& val) : Tearable() { + store(val); + } + + // Note that while filling dst with invalid data should be fine, *doing + // anything* with the result may trigger undefined behavior unless you've + // verified that the data you read was consistent. + void load(T& dst) const { + RawWord newDst[kNumDataWords]; + + for (std::size_t i = 0; i < kNumDataWords; ++i) { + newDst[i] = data_[i].load(std::memory_order_relaxed); + } + std::memcpy(&dst, newDst, sizeof(T)); + } + + void store(const T& val) { + RawWord newData[kNumDataWords]; + std::memcpy(newData, &val, sizeof(T)); + + for (std::size_t i = 0; i < kNumDataWords; ++i) { + data_[i].store(newData[i], std::memory_order_relaxed); + } + } + + private: + // A union gets us memcpy-like copy semantics always. + union RawWord { + // "unsigned" here matters; we may read uninitialized values (in the + // trailing data word in write(), for instance). + unsigned char data alignas(void*)[sizeof(void*)]; + }; + const static std::size_t kNumDataWords = + (sizeof(T) + sizeof(RawWord) - 1) / sizeof(RawWord); + + std::atomic<RawWord> data_[kNumDataWords]; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/Utility.h b/ios/Pods/Flipper-Folly/folly/synchronization/Utility.h new file mode 100644 index 000000000..f72aec8f7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/Utility.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <mutex> + +namespace folly { + +/** + * Given a mutex, return a RAII lock type templated on the type of the mutex + * + * auto lck = folly::make_unique_lock(mutex); + * + * After C++17, this function will no longer be useful because constructor + * type deduction can be used for the same purpose, and is also shorter to + * type. Note that we prepend the function with "make_" for consistency with + * standard library functions like "make_tuple", "make_pair", etc + * + * auto lck = std::unique_lock{mutex}; + * + * Till we have C++17 and constructor type deduction this function offers + * the convenience of doing the same + * + * The tail of arguments after the mutex will be forwarded to the constructor + * of std::unique_lock + */ +template <typename Mutex, typename... Args> +FOLLY_NODISCARD std::unique_lock<Mutex> make_unique_lock( + Mutex& mutex, + Args&&... args) { + return std::unique_lock<Mutex>{mutex, std::forward<Args>(args)...}; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.cpp b/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.cpp new file mode 100644 index 000000000..0cb12b415 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/synchronization/WaitOptions.h> + +namespace folly { + +constexpr std::chrono::nanoseconds WaitOptions::Defaults::spin_max; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.h b/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.h new file mode 100644 index 000000000..e16cd47b9 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/WaitOptions.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <chrono> + +#include <folly/CPortability.h> + +namespace folly { + +/// WaitOptions +/// +/// Various synchronization primitives as well as various concurrent data +/// structures built using them have operations which might wait. This type +/// represents a set of options for controlling such waiting. +class WaitOptions { + public: + struct Defaults { + /// spin_max + /// + /// If multiple threads are actively using a synchronization primitive, + /// whether indirectly via a higher-level concurrent data structure or + /// directly, where the synchronization primitive has an operation which + /// waits and another operation which wakes the waiter, it is common for + /// wait and wake events to happen almost at the same time. In this state, + /// we lose big 50% of the time if the wait blocks immediately. + /// + /// We can improve our chances of being waked immediately, before blocking, + /// by spinning for a short duration, although we have to balance this + /// against the extra cpu utilization, latency reduction, power consumption, + /// and priority inversion effect if we end up blocking anyway. + /// + /// We use a default maximum of 2 usec of spinning. As partial consolation, + /// since spinning as implemented in folly uses the pause instruction where + /// available, we give a small speed boost to the colocated hyperthread. + /// + /// On circa-2013 devbox hardware, it costs about 7 usec to FUTEX_WAIT and + /// then be awoken. Spins on this hw take about 7 nsec, where all but 0.5 + /// nsec is the pause instruction. + static constexpr std::chrono::nanoseconds spin_max = + std::chrono::microseconds(2); + static constexpr bool logging_enabled = true; + }; + + constexpr std::chrono::nanoseconds spin_max() const { + return spin_max_; + } + constexpr WaitOptions& spin_max(std::chrono::nanoseconds dur) { + spin_max_ = dur; + return *this; + } + constexpr bool logging_enabled() const { + return logging_enabled_; + } + constexpr WaitOptions& logging_enabled(bool enable) { + logging_enabled_ = enable; + return *this; + } + + private: + std::chrono::nanoseconds spin_max_ = Defaults::spin_max; + bool logging_enabled_ = Defaults::logging_enabled; +}; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h new file mode 100644 index 000000000..cecb911d2 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +namespace folly { +namespace detail { + +inline std::memory_order default_failure_memory_order( + std::memory_order successMode) { + switch (successMode) { + case std::memory_order_acq_rel: + return std::memory_order_acquire; + case std::memory_order_release: + return std::memory_order_relaxed; + case std::memory_order_relaxed: + case std::memory_order_consume: + case std::memory_order_acquire: + case std::memory_order_seq_cst: + default: + return successMode; + } +} + +inline char const* memory_order_to_str(std::memory_order mo) { + switch (mo) { + case std::memory_order_relaxed: + return "relaxed"; + case std::memory_order_consume: + return "consume"; + case std::memory_order_acquire: + return "acquire"; + case std::memory_order_release: + return "release"; + case std::memory_order_acq_rel: + return "acq_rel"; + case std::memory_order_seq_cst: + return "seq_cst"; + } +} +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/Hardware.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Hardware.h new file mode 100644 index 000000000..b7ee62e56 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Hardware.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +#include <folly/Portability.h> + +namespace folly { + +// Valid status values returned from rtmBegin. +// kRtmDisabled is a new return value indicating that RTM support is unavailable +// on the platform or the compiler. +constexpr unsigned kRtmDisabled = static_cast<unsigned>(-2); +constexpr unsigned kRtmBeginStarted = static_cast<unsigned>(-1); + +// Valid abort status bits (when the status value is not kRtmBeginStarted or +// kRtmDisabled), defined as per the Intel RTM specifications: +// https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions. +constexpr unsigned kRtmAbortExplicit = 1; +constexpr unsigned kRtmAbortRetry = 2; +constexpr unsigned kRtmAbortConflict = 4; +constexpr unsigned kRtmAbortCapacity = 8; +constexpr unsigned kRtmAbortDebug = 16; +constexpr unsigned kRtmAbortNested = 32; + +// False if there is no need for a dynamic check to see if +// the current environment supports RTM +constexpr bool kRtmSupportEnabled = kIsArchAmd64; + +// Check on cpu support for tsx-rtm +extern bool rtmEnabled(); + +namespace detail { + +// Use func ptrs to access the txn functions to avoid txn aborts +// due to plt mapping. +extern std::atomic<unsigned (*)()> rtmBeginV; +extern std::atomic<void (*)()> rtmEndV; +extern std::atomic<bool (*)()> rtmTestV; +extern std::atomic<void (*)(uint8_t)> rtmAbortV; + +} // namespace detail + +inline unsigned rtmBegin() { + return detail::rtmBeginV.load(std::memory_order_relaxed)(); +} +inline void rtmEnd() { + return detail::rtmEndV.load(std::memory_order_relaxed)(); +} +inline bool rtmTest() { + return detail::rtmTestV.load(std::memory_order_relaxed)(); +} +inline void rtmAbort(uint8_t status) { + return detail::rtmAbortV.load(std::memory_order_relaxed)(status); +} +inline uint8_t rtmStatusToAbortCode(unsigned status) { + return status >> 24; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h new file mode 100644 index 000000000..17d768b93 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h @@ -0,0 +1,358 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Portability.h> +#include <folly/synchronization/detail/Sleeper.h> + +#include <glog/logging.h> + +#include <atomic> +#include <thread> + +/// Linked list class templates used in the hazard pointer library: +/// - linked_list: Sequential linked list that uses a pre-existing +/// members next() and set_next();. +/// - shared_head_tail_list: Thread-safe linked list that maintains +/// head and tail pointers. Supports push and pop_all. +/// - shared_head_only_list: Thread-safe linked lockable list that +/// maintains only a head pointer. Supports push and pop_all. + +namespace folly { +namespace hazptr_detail { + +/** + * linked_list + * + * Template parameter Node must support set_next + * + */ +template <typename Node> +class linked_list { + Node* head_; + Node* tail_; + + public: + linked_list() noexcept : head_(nullptr), tail_(nullptr) {} + + explicit linked_list(Node* head, Node* tail) noexcept + : head_(head), tail_(tail) {} + + Node* head() const noexcept { + return head_; + } + + Node* tail() const noexcept { + return tail_; + } + + bool empty() const noexcept { + return head() == nullptr; + } + + void push(Node* node) noexcept { + node->set_next(nullptr); + if (tail_) { + tail_->set_next(node); + } else { + head_ = node; + } + tail_ = node; + } + + void splice(linked_list& l) { + if (head() == nullptr) { + head_ = l.head(); + } else { + tail_->set_next(l.head()); + } + tail_ = l.tail(); + l.clear(); + } + + void clear() { + head_ = nullptr; + tail_ = nullptr; + } + +}; // linked_list + +/** + * shared_head_tail_list + * + * Maintains head and tail pointers. Supports push and pop all + * operations. Pop all operation is wait-free. + */ +template <typename Node, template <typename> class Atom = std::atomic> +class shared_head_tail_list { + Atom<Node*> head_; + Atom<Node*> tail_; + + public: + shared_head_tail_list() noexcept : head_(nullptr), tail_(nullptr) {} + + shared_head_tail_list(shared_head_tail_list&& o) noexcept { + head_.store(o.head(), std::memory_order_relaxed); + tail_.store(o.tail(), std::memory_order_relaxed); + o.head_.store(nullptr, std::memory_order_relaxed); + o.tail_.store(nullptr, std::memory_order_relaxed); + } + + shared_head_tail_list& operator=(shared_head_tail_list&& o) noexcept { + head_.store(o.head(), std::memory_order_relaxed); + tail_.store(o.tail(), std::memory_order_relaxed); + o.head_.store(nullptr, std::memory_order_relaxed); + o.tail_.store(nullptr, std::memory_order_relaxed); + return *this; + } + + ~shared_head_tail_list() { + DCHECK(head() == nullptr); + DCHECK(tail() == nullptr); + } + + void push(Node* node) noexcept { + bool done = false; + while (!done) { + if (tail()) { + done = push_in_non_empty_list(node); + } else { + done = push_in_empty_list(node); + } + } + } + + linked_list<Node> pop_all() noexcept { + auto h = exchange_head(); + auto t = (h != nullptr) ? exchange_tail() : nullptr; + return linked_list<Node>(h, t); + } + + bool empty() const noexcept { + return head() == nullptr; + } + + private: + Node* head() const noexcept { + return head_.load(std::memory_order_acquire); + } + + Node* tail() const noexcept { + return tail_.load(std::memory_order_acquire); + } + + void set_head(Node* node) noexcept { + head_.store(node, std::memory_order_release); + } + + bool cas_head(Node* expected, Node* node) noexcept { + return head_.compare_exchange_weak( + expected, node, std::memory_order_acq_rel, std::memory_order_relaxed); + } + + bool cas_tail(Node* expected, Node* node) noexcept { + return tail_.compare_exchange_weak( + expected, node, std::memory_order_acq_rel, std::memory_order_relaxed); + } + + Node* exchange_head() noexcept { + return head_.exchange(nullptr, std::memory_order_acq_rel); + } + + Node* exchange_tail() noexcept { + return tail_.exchange(nullptr, std::memory_order_acq_rel); + } + + bool push_in_non_empty_list(Node* node) noexcept { + auto h = head(); + if (h) { + node->set_next(h); // Node must support set_next + if (cas_head(h, node)) { + return true; + } + } + return false; + } + + bool push_in_empty_list(Node* node) noexcept { + Node* t = nullptr; + node->set_next(nullptr); // Node must support set_next + if (cas_tail(t, node)) { + set_head(node); + return true; + } + return false; + } +}; // shared_head_tail_list + +/** + * shared_head_only_list + * + * A shared singly linked list that maintains only a head pointer. It + * supports pop all and push list operations. Optionally the list may + * be locked for pop all operations. Pop all operations have locked + * and wait-free variants. Push operations are always lock-free. + * + * Not all combinations of operationsa are mutually operable. The + * following are valid combinations: + * - push(kMayBeLocked), pop_all(kAlsoLock), push_unlock + * - push(kMayNotBeLocked), pop_all(kDontLock) + * + * Locking is reentrant to prevent self deadlock. + */ +template <typename Node, template <typename> class Atom = std::atomic> +class shared_head_only_list { + Atom<uintptr_t> head_{0}; // lowest bit is a lock for pop all + Atom<std::thread::id> owner_{std::thread::id()}; + int reentrance_{0}; + + static constexpr uintptr_t kLockBit = 1u; + static constexpr uintptr_t kUnlocked = 0u; + + public: + static constexpr bool kAlsoLock = true; + static constexpr bool kDontLock = false; + static constexpr bool kMayBeLocked = true; + static constexpr bool kMayNotBeLocked = false; + + public: + void push(linked_list<Node>& l, bool may_be_locked) noexcept { + if (l.empty()) { + return; + } + auto oldval = head(); + while (true) { + auto newval = reinterpret_cast<uintptr_t>(l.head()); + auto ptrval = oldval; + auto lockbit = oldval & kLockBit; + if (may_be_locked == kMayBeLocked) { + ptrval -= lockbit; + newval += lockbit; + } else { + DCHECK_EQ(lockbit, kUnlocked); + } + auto ptr = reinterpret_cast<Node*>(ptrval); + l.tail()->set_next(ptr); // Node must support set_next + if (cas_head(oldval, newval)) { + break; + } + } + } + + Node* pop_all(bool lock) noexcept { + return lock == kAlsoLock ? pop_all_lock() : pop_all_no_lock(); + } + + void push_unlock(linked_list<Node>& l) noexcept { + DCHECK_EQ(owner(), std::this_thread::get_id()); + uintptr_t lockbit; + if (reentrance_ > 0) { + DCHECK_EQ(reentrance_, 1); + --reentrance_; + lockbit = kLockBit; + } else { + clear_owner(); + lockbit = kUnlocked; + } + DCHECK_EQ(reentrance_, 0); + while (true) { + auto oldval = head(); + DCHECK_EQ(oldval & kLockBit, kLockBit); // Should be already locked + auto ptrval = oldval - kLockBit; + auto ptr = reinterpret_cast<Node*>(ptrval); + auto t = l.tail(); + if (t) { + t->set_next(ptr); // Node must support set_next + } + auto newval = + (t == nullptr) ? ptrval : reinterpret_cast<uintptr_t>(l.head()); + newval += lockbit; + if (cas_head(oldval, newval)) { + break; + } + } + } + + bool check_lock() const noexcept { + return (head() & kLockBit) == kLockBit; + } + + bool empty() const noexcept { + return head() == 0u; + } + + private: + uintptr_t head() const noexcept { + return head_.load(std::memory_order_acquire); + } + + uintptr_t exchange_head() noexcept { + auto newval = reinterpret_cast<uintptr_t>(nullptr); + auto oldval = head_.exchange(newval, std::memory_order_acq_rel); + return oldval; + } + + bool cas_head(uintptr_t& oldval, uintptr_t newval) noexcept { + return head_.compare_exchange_weak( + oldval, newval, std::memory_order_acq_rel, std::memory_order_acquire); + } + + std::thread::id owner() { + return owner_.load(std::memory_order_relaxed); + } + + void set_owner() { + DCHECK(owner() == std::thread::id()); + owner_.store(std::this_thread::get_id(), std::memory_order_relaxed); + } + + void clear_owner() { + owner_.store(std::thread::id(), std::memory_order_relaxed); + } + + Node* pop_all_no_lock() noexcept { + auto oldval = exchange_head(); + DCHECK_EQ(oldval & kLockBit, kUnlocked); + return reinterpret_cast<Node*>(oldval); + } + + Node* pop_all_lock() noexcept { + folly::detail::Sleeper s; + while (true) { + auto oldval = head(); + auto lockbit = oldval & kLockBit; + std::thread::id tid = std::this_thread::get_id(); + if (lockbit == kUnlocked || owner() == tid) { + auto newval = reinterpret_cast<uintptr_t>(nullptr) + kLockBit; + if (cas_head(oldval, newval)) { + DCHECK_EQ(reentrance_, 0); + if (lockbit == kUnlocked) { + set_owner(); + } else { + ++reentrance_; + } + auto ptrval = oldval - lockbit; + return reinterpret_cast<Node*>(ptrval); + } + } + s.sleep(); + } + } +}; // shared_head_only_list + +} // namespace hazptr_detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h new file mode 100644 index 000000000..0b6e330c6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Function.h> +#include <folly/Traits.h> +#include <folly/Utility.h> +#include <folly/functional/Invoke.h> +#include <folly/lang/Launder.h> + +namespace folly { +namespace detail { + +/** + * InlineFunctionRef is similar to folly::FunctionRef but has the additional + * benefit of being able to store the function it was instantiated with inline + * in a buffer of the given capacity. Inline storage is only used if the + * function object and a pointer (for type-erasure) are small enough to fit in + * the templated size. If there is not enough in-situ capacity for the + * callable, this just stores a reference to the function object like + * FunctionRef. + * + * This helps give a perf boost in the case where the data gets separated from + * the point of invocation. If, for example, at the point of invocation, the + * InlineFunctionRef object is not cached, a remote memory/cache read might be + * required to invoke the original callable. Customizable inline storage + * helps tune storage so we can store a type-erased callable with better + * performance and locality. A real-life example of this might be a + * folly::FunctionRef with a function pointer. The folly::FunctionRef would + * point to the function pointer object in a remote location. This causes a + * double-indirection at the point of invocation, and if that memory is dirty, + * or not cached, it would cause additional cache misses. On the other hand + * with InlineFunctionRef, inline storage would store the value of the + * function pointer, avoiding the need to do a remote lookup to fetch the + * value of the function pointer. + * + * To prevent misuse, InlineFunctionRef disallows construction from an lvalue + * callable. This is to prevent usage where a user relies on the callable's + * state after invocation through InlineFunctionRef. This has the potential + * to copy the callable into inline storage when the callable is small, so we + * might not use the same function when invoking, but rather a copy of it. + * + * Also note that InlineFunctionRef will always invoke the const qualified + * version of the call operator for any callable that is passed. Regardless + * of whether it has a non-const version. This is done to enforce the logical + * constraint of function state being immutable. + * + * This class is always trivially-copyable (and therefore + * trivially-destructible), making it suitable for use in a union without + * requiring manual destruction. + */ +template <typename FunctionType, std::size_t Size> +class InlineFunctionRef; + +template <typename ReturnType, typename... Args, std::size_t Size> +class InlineFunctionRef<ReturnType(Args...), Size> { + template <typename Arg> + using CallArg = function::CallArg<Arg>; + + using Storage = + std::aligned_storage_t<Size - sizeof(uintptr_t), sizeof(uintptr_t)>; + using Call = ReturnType (*)(CallArg<Args>..., const Storage&); + + struct InSituTag {}; + struct RefTag {}; + + static_assert( + (Size % sizeof(uintptr_t)) == 0, + "Size has to be a multiple of sizeof(uintptr_t)"); + static_assert(Size >= 2 * sizeof(uintptr_t), "This doesn't work"); + static_assert(alignof(Call) == alignof(Storage), "Mismatching alignments"); + + // This defines a mode tag that is used in the construction of + // InlineFunctionRef to determine the storage and indirection method for the + // passed callable. + // + // This requires that the we pass in a type that is not ref-qualified. + template <typename Func> + using ConstructMode = std::conditional_t< + folly::is_trivially_copyable<Func>{} && + (sizeof(Func) <= sizeof(Storage)) && + (alignof(Func) <= alignof(Storage)), + InSituTag, + RefTag>; + + public: + /** + * InlineFunctionRef can be constructed from a nullptr, callable or another + * InlineFunctionRef with the same size. These are the constructors that + * don't take a callable. + * + * InlineFunctionRef is meant to be trivially copyable so we default the + * constructors and assignment operators. + */ + InlineFunctionRef(std::nullptr_t) : call_{nullptr} {} + InlineFunctionRef() : call_{nullptr} {} + InlineFunctionRef(const InlineFunctionRef& other) = default; + InlineFunctionRef(InlineFunctionRef&&) = default; + InlineFunctionRef& operator=(const InlineFunctionRef&) = default; + InlineFunctionRef& operator=(InlineFunctionRef&&) = default; + + /** + * Constructors from callables. + * + * If all of the following conditions are satisfied, then we store the + * callable in the inline storage: + * + * 1) The function has been passed as an rvalue, meaning that there is no + * use of the original in the user's code after it has been passed to + * us. + * 2) Size of the callable is less than the size of the inline storage + * buffer. + * 3) The callable is trivially constructible and destructible. + * + * If any one of the above conditions is not satisfied, we fall back to + * reference semantics and store the function as a pointer, and add a level + * of indirection through type erasure. + */ + template < + typename Func, + std::enable_if_t< + !std::is_same<std::decay_t<Func>, InlineFunctionRef>{} && + !std::is_reference<Func>{} && + folly::is_invocable_r_v<ReturnType, Func&&, Args&&...>>* = nullptr> + InlineFunctionRef(Func&& func) { + // We disallow construction from lvalues, so assert that this is not a + // reference type. When invoked with an lvalue, Func is a lvalue + // reference type, when invoked with an rvalue, Func is not ref-qualified. + static_assert( + !std::is_reference<Func>{}, + "InlineFunctionRef cannot be used with lvalues"); + static_assert(std::is_rvalue_reference<Func&&>{}, ""); + construct(ConstructMode<Func>{}, folly::as_const(func)); + } + + /** + * The call operator uses the function pointer and a reference to the + * storage to do the dispatch. The function pointer takes care of the + * appropriate casting. + */ + ReturnType operator()(Args... args) const { + return call_(static_cast<Args&&>(args)..., storage_); + } + + /** + * We have a function engaged if the call function points to anything other + * than null. + */ + operator bool() const noexcept { + return call_; + } + + private: + friend class InlineFunctionRefTest; + + /** + * Inline storage constructor implementation. + */ + template <typename Func> + void construct(InSituTag, Func& func) { + using Value = std::remove_reference_t<Func>; + + // Assert that the following two assumptions are valid + // 1) fit in the storage space we have and match alignments, and + // 2) be invocable in a const context, it does not make sense to copy a + // callable into inline storage if it makes state local + // modifications. + static_assert(alignof(Value) <= alignof(Storage), ""); + static_assert(is_invocable<const std::decay_t<Func>, Args&&...>{}, ""); + static_assert(folly::is_trivially_copyable<Value>{}, ""); + + new (&storage_) Value{func}; + call_ = &callInline<Value>; + } + + /** + * Ref storage constructor implementation. This is identical to + * folly::FunctionRef. + */ + template <typename Func> + void construct(RefTag, Func& func) { + // store a pointer to the function + using Pointer = std::add_pointer_t<std::remove_reference_t<Func>>; + new (&storage_) Pointer{&func}; + call_ = &callPointer<Pointer>; + } + + template <typename Func> + static ReturnType callInline(CallArg<Args>... args, const Storage& object) { + // The only type of pointer allowed is a function pointer, no other + // pointer types are invocable. + static_assert( + !std::is_pointer<Func>::value || + std::is_function<std::remove_pointer_t<Func>>::value, + ""); + return folly::invoke( + *folly::launder(reinterpret_cast<const Func*>(&object)), + static_cast<Args&&>(args)...); + } + + template <typename Func> + static ReturnType callPointer(CallArg<Args>... args, const Storage& object) { + // When the function we were instantiated with was not trivial, the given + // pointer points to a pointer, which pointers to the callable. So we + // cast to a pointer and then to the pointee. + static_assert(std::is_pointer<Func>::value, ""); + return folly::invoke( + **folly::launder(reinterpret_cast<const Func*>(&object)), + static_cast<Args&&>(args)...); + } + + Call call_; + Storage storage_{}; +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h new file mode 100644 index 000000000..16efcc4f6 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> +#include <folly/Portability.h> + +#include <cassert> +#include <memory> +#include <mutex> +#include <stdexcept> +#include <utility> + +namespace folly { +namespace detail { +namespace proxylockable_detail { +template <typename Bool> +void throwIfAlreadyLocked(Bool&& locked) { + if (kIsDebug && locked) { + throw std::system_error{ + std::make_error_code(std::errc::resource_deadlock_would_occur)}; + } +} + +template <typename Bool> +void throwIfNotLocked(Bool&& locked) { + if (kIsDebug && !locked) { + throw std::system_error{ + std::make_error_code(std::errc::operation_not_permitted)}; + } +} + +template <typename Bool> +void throwIfNoMutex(Bool&& mutex) { + if (kIsDebug && !mutex) { + throw std::system_error{ + std::make_error_code(std::errc::operation_not_permitted)}; + } +} +} // namespace proxylockable_detail + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::~ProxyLockableUniqueLock() { + if (owns_lock()) { + unlock(); + } +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + mutex_type& mutex) noexcept { + proxy_.emplace(mutex.lock()); + mutex_ = std::addressof(mutex); +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + ProxyLockableUniqueLock&& a) noexcept { + *this = std::move(a); +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>& ProxyLockableUniqueLock<Mutex>::operator=( + ProxyLockableUniqueLock&& other) noexcept { + proxy_ = std::move(other.proxy_); + mutex_ = std::exchange(other.mutex_, nullptr); + return *this; +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + mutex_type& mutex, + std::defer_lock_t) noexcept { + mutex_ = std::addressof(mutex); +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + mutex_type& mutex, + std::try_to_lock_t) { + mutex_ = std::addressof(mutex); + if (auto state = mutex.try_lock()) { + proxy_.emplace(std::move(state)); + } +} + +template <typename Mutex> +template <typename Rep, typename Period> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + mutex_type& mutex, + const std::chrono::duration<Rep, Period>& duration) { + mutex_ = std::addressof(mutex); + if (auto state = mutex.try_lock_for(duration)) { + proxy_.emplace(std::move(state)); + } +} + +template <typename Mutex> +template <typename Clock, typename Duration> +ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock( + mutex_type& mutex, + const std::chrono::time_point<Clock, Duration>& time) { + mutex_ = std::addressof(mutex); + if (auto state = mutex.try_lock_until(time)) { + proxy_.emplace(std::move(state)); + } +} + +template <typename Mutex> +void ProxyLockableUniqueLock<Mutex>::lock() { + proxylockable_detail::throwIfAlreadyLocked(proxy_); + proxylockable_detail::throwIfNoMutex(mutex_); + + proxy_.emplace(mutex_->lock()); +} + +template <typename Mutex> +void ProxyLockableUniqueLock<Mutex>::unlock() { + proxylockable_detail::throwIfNoMutex(mutex_); + proxylockable_detail::throwIfNotLocked(proxy_); + + mutex_->unlock(std::move(*proxy_)); + proxy_.reset(); +} + +template <typename Mutex> +bool ProxyLockableUniqueLock<Mutex>::try_lock() { + proxylockable_detail::throwIfNoMutex(mutex_); + proxylockable_detail::throwIfAlreadyLocked(proxy_); + + if (auto state = mutex_->try_lock()) { + proxy_.emplace(std::move(state)); + return true; + } + + return false; +} + +template <typename Mutex> +template <typename Rep, typename Period> +bool ProxyLockableUniqueLock<Mutex>::try_lock_for( + const std::chrono::duration<Rep, Period>& duration) { + proxylockable_detail::throwIfNoMutex(mutex_); + proxylockable_detail::throwIfAlreadyLocked(proxy_); + + if (auto state = mutex_->try_lock_for(duration)) { + proxy_.emplace(std::move(state)); + return true; + } + + return false; +} + +template <typename Mutex> +template <typename Clock, typename Duration> +bool ProxyLockableUniqueLock<Mutex>::try_lock_until( + const std::chrono::time_point<Clock, Duration>& time) { + proxylockable_detail::throwIfNoMutex(mutex_); + proxylockable_detail::throwIfAlreadyLocked(proxy_); + + if (auto state = mutex_->try_lock_until(time)) { + proxy_.emplace(std::move(state)); + return true; + } + + return false; +} + +template <typename Mutex> +void ProxyLockableUniqueLock<Mutex>::swap( + ProxyLockableUniqueLock& other) noexcept { + std::swap(mutex_, other.mutex_); + std::swap(proxy_, other.proxy_); +} + +template <typename Mutex> +typename ProxyLockableUniqueLock<Mutex>::mutex_type* +ProxyLockableUniqueLock<Mutex>::mutex() const noexcept { + return mutex_; +} + +template <typename Mutex> +typename ProxyLockableUniqueLock<Mutex>::proxy_type* +ProxyLockableUniqueLock<Mutex>::proxy() const noexcept { + return proxy_ ? std::addressof(proxy_.value()) : nullptr; +} + +template <typename Mutex> +bool ProxyLockableUniqueLock<Mutex>::owns_lock() const noexcept { + return proxy_.has_value(); +} + +template <typename Mutex> +ProxyLockableUniqueLock<Mutex>::operator bool() const noexcept { + return owns_lock(); +} + +template <typename Mutex> +ProxyLockableLockGuard<Mutex>::ProxyLockableLockGuard(mutex_type& mutex) + : ProxyLockableUniqueLock<Mutex>{mutex} {} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h new file mode 100644 index 000000000..83c1ad998 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Optional.h> + +#include <mutex> + +namespace folly { +namespace detail { + +/** + * ProxyLockable is a "concept" that is used usually for mutexes that don't + * return void, but rather a proxy object that contains data that should be + * passed to the unlock function. + * + * This is in contrast with the normal Lockable concept that imposes no + * requirement on the return type of lock(), and requires an unlock() with no + * parameters. Here we require that lock() returns non-void and that unlock() + * accepts the return type of lock() by value, rvalue-reference or + * const-reference + * + * Here we define two classes, that can be used by the top level to implement + * specializations for std::unique_lock and std::lock_guard. Both + * ProxyLockableUniqueLock and ProxyLockableLockGuard implement the entire + * interface of std::unique_lock and std::lock_guard respectively + */ +template <typename Mutex> +class ProxyLockableUniqueLock { + public: + using mutex_type = Mutex; + using proxy_type = std::decay_t<decltype(std::declval<mutex_type>().lock())>; + + /** + * Default constructor initializes the unique_lock to an empty state + */ + ProxyLockableUniqueLock() = default; + + /** + * Destructor releases the mutex if it is locked + */ + ~ProxyLockableUniqueLock(); + + /** + * Move constructor and move assignment operators take state from the other + * lock + */ + ProxyLockableUniqueLock(ProxyLockableUniqueLock&& other) noexcept; + ProxyLockableUniqueLock& operator=(ProxyLockableUniqueLock&&) noexcept; + + /** + * Locks the mutex, blocks until the mutex can be acquired. + * + * The mutex is guaranteed to be acquired after this function returns. + */ + ProxyLockableUniqueLock(mutex_type&) noexcept; + + /** + * Explicit locking constructors to control how the lock() method is called + * + * std::defer_lock_t causes the mutex to get tracked, but not locked + * std::try_to_lock_t causes try_lock() to be called. The current object is + * converts to true if the lock was successful + */ + ProxyLockableUniqueLock(mutex_type& mutex, std::defer_lock_t) noexcept; + ProxyLockableUniqueLock(mutex_type& mutex, std::try_to_lock_t); + + /** + * Timed locking constructors + */ + template <typename Rep, typename Period> + ProxyLockableUniqueLock( + mutex_type& mutex, + const std::chrono::duration<Rep, Period>& duration); + template <typename Clock, typename Duration> + ProxyLockableUniqueLock( + mutex_type& mutex, + const std::chrono::time_point<Clock, Duration>& time); + + /** + * Lock and unlock methods + * + * lock() and try_lock() throw if the mutex is already locked, or there is + * no mutex. unlock() throws if there is no mutex or if the mutex was not + * locked + */ + void lock(); + void unlock(); + bool try_lock(); + + /** + * Timed locking methods + * + * These throw if there was no mutex, or if the mutex was already locked + */ + template <typename Rep, typename Period> + bool try_lock_for(const std::chrono::duration<Rep, Period>& duration); + template <typename Clock, typename Duration> + bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time); + + /** + * Swap this unique lock with the other one + */ + void swap(ProxyLockableUniqueLock& other) noexcept; + + /** + * Returns true if the unique lock contains a lock and also has acquired an + * exclusive lock successfully + */ + bool owns_lock() const noexcept; + explicit operator bool() const noexcept; + + /** + * mutex() return a pointer to the mutex if there is a contained mutex and + * proxy() returns a pointer to the contained proxy if the mutex is locked + * + * If the unique lock was not constructed with a mutex, then mutex() returns + * nullptr. If the mutex is not locked, then proxy() returns nullptr + */ + mutex_type* mutex() const noexcept; + proxy_type* proxy() const noexcept; + + private: + friend class ProxyLockableTest; + + /** + * If the optional has a value, the mutex is locked, if it is empty, it is + * not + */ + mutable folly::Optional<proxy_type> proxy_{}; + mutex_type* mutex_{nullptr}; +}; + +template <typename Mutex> +class ProxyLockableLockGuard : private ProxyLockableUniqueLock<Mutex> { + public: + using mutex_type = Mutex; + + /** + * Constructor locks the mutex, and destructor unlocks + */ + ProxyLockableLockGuard(mutex_type& mutex); + ~ProxyLockableLockGuard() = default; + + /** + * This class is not movable or assignable + * + * For more complicated usecases, consider the UniqueLock variant, which + * provides more options + */ + ProxyLockableLockGuard(const ProxyLockableLockGuard&) = delete; + ProxyLockableLockGuard(ProxyLockableLockGuard&&) = delete; + ProxyLockableLockGuard& operator=(ProxyLockableLockGuard&&) = delete; + ProxyLockableLockGuard& operator=(const ProxyLockableLockGuard&) = delete; +}; + +} // namespace detail +} // namespace folly + +#include <folly/synchronization/detail/ProxyLockable-inl.h> diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/Sleeper.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Sleeper.h new file mode 100644 index 000000000..29df50585 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Sleeper.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +/* + * @author Keith Adams <kma@fb.com> + * @author Jordan DeLong <delong.j@fb.com> + */ + +#include <cstdint> + +#include <folly/portability/Asm.h> +#include <folly/portability/Time.h> + +namespace folly { + +////////////////////////////////////////////////////////////////////// + +namespace detail { + +/* + * A helper object for the contended case. Starts off with eager + * spinning, and falls back to sleeping for small quantums. + */ +class Sleeper { + static const uint32_t kMaxActiveSpin = 4000; + + uint32_t spinCount; + + public: + Sleeper() noexcept : spinCount(0) {} + + static void sleep() noexcept { + /* + * Always sleep 0.5ms, assuming this will make the kernel put + * us down for whatever its minimum timer resolution is (in + * linux this varies by kernel version from 1ms to 10ms). + */ + struct timespec ts = {0, 500000}; + nanosleep(&ts, nullptr); + } + + void wait() noexcept { + if (spinCount < kMaxActiveSpin) { + ++spinCount; + asm_volatile_pause(); + } else { + sleep(); + } + } +}; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/Spin.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Spin.h new file mode 100644 index 000000000..2c37869d4 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/Spin.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <algorithm> +#include <chrono> +#include <thread> + +#include <folly/portability/Asm.h> +#include <folly/synchronization/WaitOptions.h> + +namespace folly { +namespace detail { + +enum class spin_result { + success, // condition passed + timeout, // exceeded deadline + advance, // exceeded current wait-options component timeout +}; + +template <typename Clock, typename Duration, typename F> +spin_result spin_pause_until( + std::chrono::time_point<Clock, Duration> const& deadline, + WaitOptions const& opt, + F f) { + if (opt.spin_max() <= opt.spin_max().zero()) { + return spin_result::advance; + } + + auto tbegin = Clock::now(); + while (true) { + if (f()) { + return spin_result::success; + } + + auto const tnow = Clock::now(); + if (tnow >= deadline) { + return spin_result::timeout; + } + + // Backward time discontinuity in Clock? revise pre_block starting point + tbegin = std::min(tbegin, tnow); + if (tnow >= tbegin + opt.spin_max()) { + return spin_result::advance; + } + + // The pause instruction is the polite way to spin, but it doesn't + // actually affect correctness to omit it if we don't have it. Pausing + // donates the full capabilities of the current core to its other + // hyperthreads for a dozen cycles or so. + asm_volatile_pause(); + } +} + +template <typename Clock, typename Duration, typename F> +spin_result spin_yield_until( + std::chrono::time_point<Clock, Duration> const& deadline, + F f) { + while (true) { + if (f()) { + return spin_result::success; + } + + auto const max = std::chrono::time_point<Clock, Duration>::max(); + if (deadline != max && Clock::now() >= deadline) { + return spin_result::timeout; + } + + std::this_thread::yield(); + } +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h new file mode 100644 index 000000000..62c1e7ee1 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <folly/Function.h> +#include <folly/ThreadLocal.h> +#include <folly/synchronization/AsymmetricMemoryBarrier.h> + +// This is unlike folly::ThreadCachedInt in that the full value +// is never rounded up globally and cached, it only supports readFull. +// +// folly/experimental/TLRefCount is similar, but does not support a +// waitForZero, and is not reset-able. +// +// Note that the RCU implementation is completely abstracted from the +// counter implementation, a rseq implementation can be dropped in +// if the kernel supports it. + +namespace folly { + +namespace detail { + +template <typename Tag> +class ThreadCachedInts { + std::atomic<int64_t> orphan_inc_[2] = {}; + std::atomic<int64_t> orphan_dec_[2] = {}; + folly::detail::Futex<> waiting_{0}; + + class Integer { + public: + ThreadCachedInts* ints_; + constexpr Integer(ThreadCachedInts* ints) noexcept + : ints_(ints), inc_{}, dec_{}, cache_(ints->int_cache_) {} + std::atomic<int64_t> inc_[2]; + std::atomic<int64_t> dec_[2]; + Integer*& cache_; // reference to the cached ptr + ~Integer() noexcept { + // Increment counts must be set before decrement counts + ints_->orphan_inc_[0].fetch_add( + inc_[0].load(std::memory_order_relaxed), std::memory_order_relaxed); + ints_->orphan_inc_[1].fetch_add( + inc_[1].load(std::memory_order_relaxed), std::memory_order_relaxed); + folly::asymmetricLightBarrier(); // B + ints_->orphan_dec_[0].fetch_add( + dec_[0].load(std::memory_order_relaxed), std::memory_order_relaxed); + ints_->orphan_dec_[1].fetch_add( + dec_[1].load(std::memory_order_relaxed), std::memory_order_relaxed); + ints_->waiting_.store(0, std::memory_order_release); + detail::futexWake(&ints_->waiting_); + // reset the cache_ on destructor so we can handle the delete/recreate + cache_ = nullptr; + } + }; + folly::ThreadLocalPtr<Integer, Tag> cs_; + + // Cache the int pointer in a threadlocal. + static thread_local Integer* int_cache_; + + void init() { + auto ret = new Integer(this); + cs_.reset(ret); + int_cache_ = ret; + } + + public: + FOLLY_ALWAYS_INLINE void increment(uint8_t epoch) { + if (!int_cache_) { + init(); + } + + auto& c = int_cache_->inc_[epoch]; + auto val = c.load(std::memory_order_relaxed); + c.store(val + 1, std::memory_order_relaxed); + + folly::asymmetricLightBarrier(); // A + } + + FOLLY_ALWAYS_INLINE void decrement(uint8_t epoch) { + folly::asymmetricLightBarrier(); // B + if (!int_cache_) { + init(); + } + + auto& c = int_cache_->dec_[epoch]; + auto val = c.load(std::memory_order_relaxed); + c.store(val + 1, std::memory_order_relaxed); + + folly::asymmetricLightBarrier(); // C + if (waiting_.load(std::memory_order_acquire)) { + waiting_.store(0, std::memory_order_release); + detail::futexWake(&waiting_); + } + } + + int64_t readFull(uint8_t epoch) { + int64_t full = -orphan_dec_[epoch].load(std::memory_order_relaxed); + + // Matches A - ensure all threads have seen new value of version, + // *and* that we see current values of counters in readFull() + // + // Note that in lock_shared if a reader is currently between the + // version load and counter increment, they may update the wrong + // epoch. However, this is ok - they started concurrently *after* + // any callbacks that will run, and therefore it is safe to run + // the callbacks. + folly::asymmetricHeavyBarrier(); + for (auto& i : cs_.accessAllThreads()) { + full -= i.dec_[epoch].load(std::memory_order_relaxed); + } + + // Matches B - ensure that all increments are seen if decrements + // are seen. This is necessary because increment and decrement + // are allowed to happen on different threads. + folly::asymmetricHeavyBarrier(); + + auto accessor = cs_.accessAllThreads(); + for (auto& i : accessor) { + full += i.inc_[epoch].load(std::memory_order_relaxed); + } + + // orphan is read behind accessAllThreads lock + return full + orphan_inc_[epoch].load(std::memory_order_relaxed); + } + + void waitForZero(uint8_t phase) { + // Try reading before futex sleeping. + if (readFull(phase) == 0) { + return; + } + + while (true) { + waiting_.store(1, std::memory_order_release); + // Matches C. Ensure either decrement sees waiting_, + // or we see their decrement and can safely sleep. + folly::asymmetricHeavyBarrier(); + if (readFull(phase) == 0) { + break; + } + detail::futexWait(&waiting_, 1); + } + waiting_.store(0, std::memory_order_relaxed); + } + + // We are guaranteed to be called while StaticMeta lock is still + // held because of ordering in AtForkList. We can therefore safely + // touch orphan_ and clear out all counts. + void resetAfterFork() { + if (int_cache_) { + int_cache_->dec_[0].store(0, std::memory_order_relaxed); + int_cache_->dec_[1].store(0, std::memory_order_relaxed); + int_cache_->inc_[0].store(0, std::memory_order_relaxed); + int_cache_->inc_[1].store(0, std::memory_order_relaxed); + } + orphan_inc_[0].store(0, std::memory_order_relaxed); + orphan_inc_[1].store(0, std::memory_order_relaxed); + orphan_dec_[0].store(0, std::memory_order_relaxed); + orphan_dec_[1].store(0, std::memory_order_relaxed); + } +}; + +template <typename Tag> +thread_local typename detail::ThreadCachedInts<Tag>::Integer* + detail::ThreadCachedInts<Tag>::int_cache_ = nullptr; + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h new file mode 100644 index 000000000..34d3c1411 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <atomic> + +#include <folly/Function.h> +#include <folly/Synchronized.h> +#include <folly/ThreadLocal.h> +#include <glog/logging.h> + +namespace folly { + +namespace detail { + +// This is a thread-local cached, multi-producer single-consumer +// queue, similar to a concurrent version of std::list. +// +class ThreadCachedListsBase { + public: + struct Node { + folly::Function<void()> cb_; + Node* next_{nullptr}; + }; +}; + +template <typename Tag> +class ThreadCachedLists : public ThreadCachedListsBase { + public: + struct AtomicListHead { + std::atomic<Node*> tail_{nullptr}; + std::atomic<Node*> head_{nullptr}; + }; + + // Non-concurrent list, similar to std::list. + struct ListHead { + Node* head_{nullptr}; + Node* tail_{nullptr}; + + // Run func on each list node. + template <typename Func> + void forEach(Func func) { + auto node = tail_; + while (node != nullptr) { + auto next = node->next_; + func(node); + node = next; + } + } + + // Splice other in to this list. + // Afterwards, other is a valid empty listhead. + void splice(ListHead& other); + + void splice(AtomicListHead& other); + }; + + // Push a node on a thread-local list. Returns true if local list + // was pushed global. + void push(Node* node); + + // Collect all thread local lists to a single local list. + // This function is threadsafe with concurrent push()es, + // but only a single thread may call collect() at a time. + void collect(ListHead& list); + + private: + // Push list to the global list. + void pushGlobal(ListHead& list); + + folly::Synchronized<ListHead> ghead_; + + struct TLHead : public AtomicListHead { + ThreadCachedLists* parent_; + + public: + TLHead(ThreadCachedLists* parent) : parent_(parent) {} + + ~TLHead() { + parent_->ghead_.wlock()->splice(*this); + } + }; + + folly::ThreadLocalPtr<TLHead, Tag> lhead_; +}; + +// push() and splice() are optimistic w.r.t setting the list head: The +// first pusher cas's the list head, which functions as a lock until +// tail != null. The first pusher then sets tail_ = head_. +// +// splice() does the opposite: steals the tail_ via exchange, then +// unlocks the list again by setting head_ to null. +template <typename Tag> +void ThreadCachedLists<Tag>::push(Node* node) { + DCHECK(node->next_ == nullptr); + static thread_local TLHead* cache_{nullptr}; + + if (!cache_) { + auto l = lhead_.get(); + if (!l) { + lhead_.reset(new TLHead(this)); + l = lhead_.get(); + DCHECK(l); + } + cache_ = l; + } + + while (true) { + auto head = cache_->head_.load(std::memory_order_relaxed); + if (!head) { + node->next_ = nullptr; + if (cache_->head_.compare_exchange_weak(head, node)) { + cache_->tail_.store(node); + break; + } + } else { + auto tail = cache_->tail_.load(std::memory_order_relaxed); + if (tail) { + node->next_ = tail; + if (cache_->tail_.compare_exchange_weak(node->next_, node)) { + break; + } + } + } + } +} + +template <typename Tag> +void ThreadCachedLists<Tag>::collect(ListHead& list) { + auto acc = lhead_.accessAllThreads(); + + for (auto& thr : acc) { + list.splice(thr); + } + + list.splice(*ghead_.wlock()); +} + +template <typename Tag> +void ThreadCachedLists<Tag>::ListHead::splice(ListHead& other) { + if (other.head_ != nullptr) { + DCHECK(other.tail_ != nullptr); + } else { + DCHECK(other.tail_ == nullptr); + return; + } + + if (head_) { + DCHECK(tail_ != nullptr); + DCHECK(head_->next_ == nullptr); + head_->next_ = other.tail_; + head_ = other.head_; + } else { + DCHECK(head_ == nullptr); + head_ = other.head_; + tail_ = other.tail_; + } + + other.head_ = nullptr; + other.tail_ = nullptr; +} + +template <typename Tag> +void ThreadCachedLists<Tag>::ListHead::splice(AtomicListHead& list) { + ListHead local; + + auto tail = list.tail_.load(); + if (tail) { + local.tail_ = list.tail_.exchange(nullptr); + local.head_ = list.head_.exchange(nullptr); + splice(local); + } +} + +} // namespace detail +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.cpp b/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.cpp new file mode 100644 index 000000000..dec32aac8 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/system/HardwareConcurrency.h> + +#include <thread> + +#include <folly/portability/Sched.h> + +namespace folly { + +unsigned int hardware_concurrency() noexcept { +#if defined(__linux__) && !defined(__ANDROID__) + cpu_set_t cpuset; + if (!sched_getaffinity(0, sizeof(cpuset), &cpuset)) { + auto count = CPU_COUNT(&cpuset); + if (count != 0) { + return count; + } + } +#endif + + return std::thread::hardware_concurrency(); +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.h b/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.h new file mode 100644 index 000000000..a56bc36c7 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/HardwareConcurrency.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace folly { + +unsigned int hardware_concurrency() noexcept; + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.cpp b/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.cpp new file mode 100644 index 000000000..df7465f83 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/system/MemoryMapping.h> + +#include <algorithm> +#include <cerrno> +#include <utility> + +#include <glog/logging.h> + +#include <folly/Format.h> +#include <folly/Portability.h> +#include <folly/portability/GFlags.h> +#include <folly/portability/SysMman.h> +#include <folly/portability/SysSyscall.h> + +#ifdef __linux__ +#include <folly/experimental/io/HugePages.h> // @manual +#endif + +#include <fcntl.h> +#include <sys/types.h> +#include <system_error> + +static constexpr ssize_t kDefaultMlockChunkSize = !folly::kMscVer + // Linux implementations of unmap/mlock/munlock take a kernel + // semaphore and block other threads from doing other memory + // operations. Split the operations in chunks. + ? (1 << 20) // 1MB + // MSVC doesn't have this problem, and calling munmap many times + // with the same address is a bad idea with the windows implementation. + : (-1); + +DEFINE_int64( + mlock_chunk_size, + kDefaultMlockChunkSize, + "Maximum bytes to mlock/munlock/munmap at once " + "(will be rounded up to PAGESIZE). Ignored if negative."); + +namespace folly { + +namespace { + +enum mmap_flags : int { +#ifdef MAP_POPULATE + populate = MAP_POPULATE, +#else + populate = 0, +#endif +}; + +} // namespace + +MemoryMapping::MemoryMapping(MemoryMapping&& other) noexcept { + swap(other); +} + +MemoryMapping::MemoryMapping( + File file, + off_t offset, + off_t length, + Options options) + : file_(std::move(file)), options_(options) { + CHECK(file_); + init(offset, length); +} + +MemoryMapping::MemoryMapping( + const char* name, + off_t offset, + off_t length, + Options options) + : MemoryMapping( + File(name, options.writable ? O_RDWR : O_RDONLY), + offset, + length, + options) {} + +MemoryMapping::MemoryMapping( + int fd, + off_t offset, + off_t length, + Options options) + : MemoryMapping(File(fd), offset, length, options) {} + +MemoryMapping::MemoryMapping(AnonymousType, off_t length, Options options) + : options_(options) { + init(0, length); +} + +namespace { + +#ifdef __linux__ +void getDeviceOptions(dev_t device, off_t& pageSize, bool& autoExtend) { + auto ps = getHugePageSizeForDevice(device); + if (ps) { + pageSize = ps->size; + autoExtend = true; + } +} +#else +inline void getDeviceOptions(dev_t, off_t&, bool&) {} +#endif + +} // namespace + +void MemoryMapping::init(off_t offset, off_t length) { + const bool grow = options_.grow; + const bool anon = !file_; + CHECK(!(grow && anon)); + + off_t& pageSize = options_.pageSize; + + struct stat st; + + // On Linux, hugetlbfs file systems don't require ftruncate() to grow the + // file, and (on kernels before 2.6.24) don't even allow it. Also, the file + // size is always a multiple of the page size. + bool autoExtend = false; + + if (!anon) { + // Stat the file + CHECK_ERR(fstat(file_.fd(), &st)); + + if (pageSize == 0) { + getDeviceOptions(st.st_dev, pageSize, autoExtend); + } + } else { + DCHECK(!file_); + DCHECK_EQ(offset, 0); + CHECK_EQ(pageSize, 0); + CHECK_GE(length, 0); + } + + if (pageSize == 0) { + pageSize = off_t(sysconf(_SC_PAGESIZE)); + } + + CHECK_GT(pageSize, 0); + CHECK_EQ(pageSize & (pageSize - 1), 0); // power of two + CHECK_GE(offset, 0); + + // Round down the start of the mapped region + off_t skipStart = offset % pageSize; + offset -= skipStart; + + mapLength_ = length; + if (mapLength_ != -1) { + mapLength_ += skipStart; + + // Round up the end of the mapped region + mapLength_ = (mapLength_ + pageSize - 1) / pageSize * pageSize; + } + + off_t remaining = anon ? length : st.st_size - offset; + + if (mapLength_ == -1) { + length = mapLength_ = remaining; + } else { + if (length > remaining) { + if (grow) { + if (!autoExtend) { + PCHECK(0 == ftruncate(file_.fd(), offset + length)) + << "ftruncate() failed, couldn't grow file to " + << offset + length; + remaining = length; + } else { + // Extend mapping to multiple of page size, don't use ftruncate + remaining = mapLength_; + } + } else { + length = remaining; + } + } + if (mapLength_ > remaining) { + mapLength_ = remaining; + } + } + + if (length == 0) { + mapLength_ = 0; + mapStart_ = nullptr; + } else { + int flags = options_.shared ? MAP_SHARED : MAP_PRIVATE; + if (anon) { + flags |= MAP_ANONYMOUS; + } + if (options_.prefault) { + flags |= mmap_flags::populate; + } + + // The standard doesn't actually require PROT_NONE to be zero... + int prot = PROT_NONE; + if (options_.readable || options_.writable) { + prot = + ((options_.readable ? PROT_READ : 0) | + (options_.writable ? PROT_WRITE : 0)); + } + + auto start = static_cast<unsigned char*>(mmap( + options_.address, size_t(mapLength_), prot, flags, file_.fd(), offset)); + PCHECK(start != MAP_FAILED) + << " offset=" << offset << " length=" << mapLength_; + mapStart_ = start; + data_.reset(start + skipStart, size_t(length)); + } +} + +namespace { + +off_t memOpChunkSize(off_t length, off_t pageSize) { + off_t chunkSize = length; + if (FLAGS_mlock_chunk_size <= 0) { + return chunkSize; + } + + chunkSize = off_t(FLAGS_mlock_chunk_size); + off_t r = chunkSize % pageSize; + if (r) { + chunkSize += (pageSize - r); + } + return chunkSize; +} + +/** + * Run @op in chunks over the buffer @mem of @bufSize length. + * + * Return: + * - success: true + amountSucceeded == bufSize (op success on whole buffer) + * - failure: false + amountSucceeded == nr bytes on which op succeeded. + */ +template <typename Op> +bool memOpInChunks( + Op op, + void* mem, + size_t bufSize, + off_t pageSize, + size_t& amountSucceeded) { + // Linux' unmap/mlock/munlock take a kernel semaphore and block other threads + // from doing other memory operations. If the size of the buffer is big the + // semaphore can be down for seconds (for benchmarks see + // http://kostja-osipov.livejournal.com/42963.html). Doing the operations in + // chunks breaks the locking into intervals and lets other threads do memory + // operations of their own. + + auto chunkSize = size_t(memOpChunkSize(off_t(bufSize), pageSize)); + + auto addr = static_cast<char*>(mem); + amountSucceeded = 0; + + while (amountSucceeded < bufSize) { + size_t size = std::min(chunkSize, bufSize - amountSucceeded); + if (op(addr + amountSucceeded, size) != 0) { + return false; + } + amountSucceeded += size; + } + + return true; +} + +} // namespace + +int mlock2wrapper( + const void* addr, + size_t len, + MemoryMapping::LockFlags flags) { + int intFlags = 0; + if (flags.lockOnFault) { + // MLOCK_ONFAULT, only available in non-portable headers. + intFlags |= 0x01; + } + +#if defined(__GLIBC__) && !defined(__APPLE__) +#if __GLIBC_PREREQ(2, 27) + return mlock2(addr, len, intFlags); +#elif defined(SYS_mlock2) + // SYS_mlock2 is defined in Linux headers since 4.4 + return syscall(SYS_mlock2, addr, len, intFlags); +#else // !__GLIBC_PREREQ(2, 27) && !defined(SYS_mlock2) + errno = ENOSYS; + return -1; +#endif +#else // !defined(__GLIBC__) || defined(__APPLE__) + errno = ENOSYS; + return -1; +#endif +} + +bool MemoryMapping::mlock(LockMode mode, LockFlags flags) { + size_t amountSucceeded = 0; + locked_ = memOpInChunks( + [flags](void* addr, size_t len) -> int { + // If no flags are set, mlock2() behaves exactly the same as + // mlock(). Prefer the portable variant. + return flags == LockFlags{} ? ::mlock(addr, len) + : mlock2wrapper(addr, len, flags); + }, + mapStart_, + size_t(mapLength_), + options_.pageSize, + amountSucceeded); + if (locked_) { + return true; + } + + auto msg = + folly::format("mlock({}) failed at {}", mapLength_, amountSucceeded); + if (mode == LockMode::TRY_LOCK && errno == EPERM) { + PLOG(WARNING) << msg; + } else if (mode == LockMode::TRY_LOCK && errno == ENOMEM) { + VLOG(1) << msg; + } else { + PLOG(FATAL) << msg; + } + + // only part of the buffer was mlocked, unlock it back + if (!memOpInChunks( + ::munlock, + mapStart_, + amountSucceeded, + options_.pageSize, + amountSucceeded)) { + PLOG(WARNING) << "munlock()"; + } + + return false; +} + +void MemoryMapping::munlock(bool dontneed) { + if (!locked_) { + return; + } + + size_t amountSucceeded = 0; + if (!memOpInChunks( + ::munlock, + mapStart_, + size_t(mapLength_), + options_.pageSize, + amountSucceeded)) { + PLOG(WARNING) << "munlock()"; + } + if (mapLength_ && dontneed && + ::madvise(mapStart_, size_t(mapLength_), MADV_DONTNEED)) { + PLOG(WARNING) << "madvise()"; + } + locked_ = false; +} + +void MemoryMapping::hintLinearScan() { + advise(MADV_SEQUENTIAL); +} + +MemoryMapping::~MemoryMapping() { + if (mapLength_) { + size_t amountSucceeded = 0; + if (!memOpInChunks( + ::munmap, + mapStart_, + size_t(mapLength_), + options_.pageSize, + amountSucceeded)) { + PLOG(FATAL) << folly::format( + "munmap({}) failed at {}", mapLength_, amountSucceeded); + } + } +} + +void MemoryMapping::advise(int advice) const { + advise(advice, 0, size_t(mapLength_)); +} + +void MemoryMapping::advise(int advice, size_t offset, size_t length) const { + CHECK_LE(offset + length, size_t(mapLength_)) + << " offset: " << offset << " length: " << length + << " mapLength_: " << mapLength_; + + // Include the entire start page: round down to page boundary. + const auto offMisalign = offset % options_.pageSize; + offset -= offMisalign; + length += offMisalign; + + // Round the last page down to page boundary. + if (offset + length != size_t(mapLength_)) { + length -= length % options_.pageSize; + } + + if (length == 0) { + return; + } + + char* mapStart = static_cast<char*>(mapStart_) + offset; + PLOG_IF(WARNING, ::madvise(mapStart, length, advice)) << "madvise"; +} + +MemoryMapping& MemoryMapping::operator=(MemoryMapping&& other) { + swap(other); + return *this; +} + +void MemoryMapping::swap(MemoryMapping& other) noexcept { + using std::swap; + swap(this->file_, other.file_); + swap(this->mapStart_, other.mapStart_); + swap(this->mapLength_, other.mapLength_); + swap(this->options_, other.options_); + swap(this->locked_, other.locked_); + swap(this->data_, other.data_); +} + +void swap(MemoryMapping& a, MemoryMapping& b) noexcept { + a.swap(b); +} + +void alignedForwardMemcpy(void* dst, const void* src, size_t size) { + assert(reinterpret_cast<uintptr_t>(src) % alignof(unsigned long) == 0); + assert(reinterpret_cast<uintptr_t>(dst) % alignof(unsigned long) == 0); + + auto srcl = static_cast<const unsigned long*>(src); + auto dstl = static_cast<unsigned long*>(dst); + + while (size >= sizeof(unsigned long)) { + *dstl++ = *srcl++; + size -= sizeof(unsigned long); + } + + auto srcc = reinterpret_cast<const unsigned char*>(srcl); + auto dstc = reinterpret_cast<unsigned char*>(dstl); + + while (size != 0) { + *dstc++ = *srcc++; + --size; + } +} + +void mmapFileCopy(const char* src, const char* dest, mode_t mode) { + MemoryMapping srcMap(src); + srcMap.hintLinearScan(); + + MemoryMapping destMap( + File(dest, O_RDWR | O_CREAT | O_TRUNC, mode), + 0, + off_t(srcMap.range().size()), + MemoryMapping::writable()); + + alignedForwardMemcpy( + destMap.writableRange().data(), + srcMap.range().data(), + srcMap.range().size()); +} + +bool MemoryMapping::LockFlags::operator==(const LockFlags& other) const { + return lockOnFault == other.lockOnFault; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.h b/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.h new file mode 100644 index 000000000..f2f143d4e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/MemoryMapping.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cassert> + +#include <folly/File.h> +#include <folly/Range.h> + +namespace folly { + +/** + * Maps files in memory (read-only). + * + * @author Tudor Bosman (tudorb@fb.com) + */ +class MemoryMapping { + public: + /** + * Lock the pages in memory? + * TRY_LOCK = try to lock, log warning if permission denied + * MUST_LOCK = lock, fail assertion if permission denied. + */ + enum class LockMode { + TRY_LOCK, + MUST_LOCK, + }; + + struct LockFlags { + LockFlags() {} + + bool operator==(const LockFlags& other) const; + + /** + * Instead of locking all the pages in the mapping before the call returns, + * only lock those that are currently resident and mark the others to be + * locked at the time they're populated by their first page fault. + * + * Uses mlock2(flags=MLOCK_ONFAULT). Requires Linux >= 4.4. + */ + bool lockOnFault = false; + }; + + /** + * Map a portion of the file indicated by filename in memory, causing SIGABRT + * on error. + * + * By default, map the whole file. length=-1: map from offset to EOF. + * Unlike the mmap() system call, offset and length don't need to be + * page-aligned. length is clipped to the end of the file if it's too large. + * + * The mapping will be destroyed (and the memory pointed-to by data() will + * likely become inaccessible) when the MemoryMapping object is destroyed. + */ + struct Options { + Options() {} + + // Convenience methods; return *this for chaining. + Options& setPageSize(off_t v) { + pageSize = v; + return *this; + } + Options& setShared(bool v) { + shared = v; + return *this; + } + Options& setPrefault(bool v) { + prefault = v; + return *this; + } + Options& setReadable(bool v) { + readable = v; + return *this; + } + Options& setWritable(bool v) { + writable = v; + return *this; + } + Options& setGrow(bool v) { + grow = v; + return *this; + } + + // Page size. 0 = use appropriate page size. + // (On Linux, we use a huge page size if the file is on a hugetlbfs + // file system, and the default page size otherwise) + off_t pageSize = 0; + + // If shared (default), the memory mapping is shared with other processes + // mapping the same file (or children); if not shared (private), each + // process has its own mapping. Changes in writable, private mappings are + // not reflected to the underlying file. See the discussion of + // MAP_PRIVATE vs MAP_SHARED in the mmap(2) manual page. + bool shared = true; + + // Populate page tables; subsequent accesses should not be blocked + // by page faults. This is a hint, as it may not be supported. + bool prefault = false; + + // Map the pages readable. Note that mapping pages without read permissions + // is not universally supported (not supported on hugetlbfs on Linux, for + // example) + bool readable = true; + + // Map the pages writable. + bool writable = false; + + // When mapping a file in writable mode, grow the file to the requested + // length (using ftruncate()) before mapping; if false, truncate the + // mapping to the actual file size instead. + bool grow = false; + + // Fix map at this address, if not nullptr. Must be aligned to a multiple + // of the appropriate page size. + void* address = nullptr; + }; + + // Options to emulate the old WritableMemoryMapping: readable and writable, + // allow growing the file if mapping past EOF. + static Options writable() { + return Options().setWritable(true).setGrow(true); + } + + enum AnonymousType { + kAnonymous, + }; + + /** + * Create an anonymous mapping. + */ + MemoryMapping(AnonymousType, off_t length, Options options = Options()); + + explicit MemoryMapping( + File file, + off_t offset = 0, + off_t length = -1, + Options options = Options()); + + explicit MemoryMapping( + const char* name, + off_t offset = 0, + off_t length = -1, + Options options = Options()); + + explicit MemoryMapping( + int fd, + off_t offset = 0, + off_t length = -1, + Options options = Options()); + + MemoryMapping(const MemoryMapping&) = delete; + MemoryMapping(MemoryMapping&&) noexcept; + + ~MemoryMapping(); + + MemoryMapping& operator=(const MemoryMapping&) = delete; + MemoryMapping& operator=(MemoryMapping&&); + + void swap(MemoryMapping& other) noexcept; + + /** + * Lock the pages in memory + */ + bool mlock(LockMode mode, LockFlags flags = {}); + + /** + * Unlock the pages. + * If dontneed is true, the kernel is instructed to release these pages + * (per madvise(MADV_DONTNEED)). + */ + void munlock(bool dontneed = false); + + /** + * Hint that these pages will be scanned linearly. + * madvise(MADV_SEQUENTIAL) + */ + void hintLinearScan(); + + /** + * Advise the kernel about memory access. + */ + void advise(int advice) const; + void advise(int advice, size_t offset, size_t length) const; + + /** + * A bitwise cast of the mapped bytes as range of values. Only intended for + * use with POD or in-place usable types. + */ + template <class T> + Range<const T*> asRange() const { + size_t count = data_.size() / sizeof(T); + return Range<const T*>( + static_cast<const T*>(static_cast<const void*>(data_.data())), count); + } + + /** + * A range of bytes mapped by this mapping. + */ + ByteRange range() const { + return data_; + } + + /** + * A bitwise cast of the mapped bytes as range of mutable values. Only + * intended for use with POD or in-place usable types. + */ + template <class T> + Range<T*> asWritableRange() const { + assert(options_.writable); // you'll segfault anyway... + size_t count = data_.size() / sizeof(T); + return Range<T*>(static_cast<T*>(static_cast<void*>(data_.data())), count); + } + + /** + * A range of mutable bytes mapped by this mapping. + */ + MutableByteRange writableRange() const { + assert(options_.writable); // you'll segfault anyway... + return data_; + } + + /** + * Return the memory area where the file was mapped. + * Deprecated; use range() instead. + */ + StringPiece data() const { + return asRange<const char>(); + } + + bool mlocked() const { + return locked_; + } + + int fd() const { + return file_.fd(); + } + + private: + MemoryMapping(); + + enum InitFlags { + kGrow = 1 << 0, + kAnon = 1 << 1, + }; + void init(off_t offset, off_t length); + + File file_; + void* mapStart_ = nullptr; + off_t mapLength_ = 0; + Options options_; + bool locked_ = false; + MutableByteRange data_; +}; + +void swap(MemoryMapping&, MemoryMapping&) noexcept; + +/** + * A special case of memcpy() that always copies memory forwards. + * (libc's memcpy() is allowed to copy memory backwards, and will do so + * when using SSSE3 instructions). + * + * Assumes src and dest are aligned to alignof(unsigned long). + * + * Useful when copying from/to memory mappings after hintLinearScan(); + * copying backwards renders any prefetching useless (even harmful). + */ +void alignedForwardMemcpy(void* dst, const void* src, size_t size); + +/** + * Copy a file using mmap(). Overwrites dest. + */ +void mmapFileCopy(const char* src, const char* dest, mode_t mode = 0666); + +/** + * mlock2 is Linux-only and exists since Linux 4.4 + * On Linux pre-4.4 and other platforms fail with ENOSYS. + * glibc added the mlock2 wrapper in 2.27 + * https://lists.gnu.org/archive/html/info-gnu/2018-02/msg00000.html + */ +int mlock2wrapper(const void* addr, size_t len, MemoryMapping::LockFlags flags); + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/Shell.cpp b/ios/Pods/Flipper-Folly/folly/system/Shell.cpp new file mode 100644 index 000000000..3db30b318 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/Shell.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/system/Shell.h> + +namespace folly { + +std::string shellQuote(StringPiece argument) { + std::string quoted = "'"; + for (auto c : argument) { + if (c == '\'') { + quoted += "'\\''"; + } else { + quoted += c; + } + } + return quoted + "'"; +} + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/Shell.h b/ios/Pods/Flipper-Folly/folly/system/Shell.h new file mode 100644 index 000000000..0d0886f18 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/Shell.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * `Shell` provides a collection of functions to use with `Subprocess` that make + * it easier to safely run processes in a unix shell. + * + * Note: use this rarely and carefully. By default you should use `Subprocess` + * with a vector of arguments. + */ + +#pragma once + +#include <string> +#include <vector> + +#include <folly/Conv.h> +#include <folly/Format.h> +#include <folly/Range.h> + +namespace folly { + +/** + * Quotes an argument to make it suitable for use as shell command arguments. + */ +std::string shellQuote(StringPiece argument); + +namespace detail { +template <typename... Arguments> +std::vector<std::string> shellify( + StringPiece format, + Arguments&&... arguments) { + auto command = sformat( + format, + shellQuote(to<std::string>(std::forward<Arguments>(arguments)))...); + return {"/bin/sh", "-c", command}; +} + +struct ShellCmdFormat { + StringPiece format; + template <typename... Arguments> + std::vector<std::string> operator()(Arguments&&... arguments) const { + return ::folly::detail::shellify( + format, std::forward<Arguments>(arguments)...); + } +}; + +} // namespace detail + +inline namespace literals { +inline namespace shell_literals { +constexpr detail::ShellCmdFormat operator"" _shellify( + char const* name, + std::size_t length) { + return {folly::StringPiece(name, length)}; +} +} // namespace shell_literals +} // namespace literals + +/** + * Create argument array for `Subprocess()` for a process running in a + * shell. + * + * The shell to use is always going to be `/bin/sh`. + * + * This is deprecated in favour of the user-defined-literal `_shellify` + * from namespace `folly::shell_literals` because that requires that the format + * string is a compile-time constant which can be inspected during code reviews + */ +// clang-format off +template <typename... Arguments> +[[deprecated( + "Use `\"command {} {} ...\"_shellify(argument1, argument2 ...)` from " + "namespace `folly::literals::shell_literals`")]] +std::vector<std::string> shellify( + StringPiece format, + Arguments&&... arguments) { + return detail::shellify(format, std::forward<Arguments>(arguments)...); +} +// clang-format on + +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/ThreadId.h b/ios/Pods/Flipper-Folly/folly/system/ThreadId.h new file mode 100644 index 000000000..67533612e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/ThreadId.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <cstdint> + +#include <folly/portability/PThread.h> +#include <folly/portability/SysSyscall.h> +#include <folly/portability/Unistd.h> +#include <folly/portability/Windows.h> + +namespace folly { + +/** + * Get a process-specific identifier for the current thread. + * + * The return value will uniquely identify the thread within the current + * process. + * + * Note that the return value does not necessarily correspond to an operating + * system thread ID. The return value is also only unique within the current + * process: getCurrentThreadID() may return the same value for two concurrently + * running threads in separate processes. + * + * The thread ID may be reused once the thread it corresponds to has been + * joined. + */ +inline uint64_t getCurrentThreadID() { +#if __APPLE__ + return uint64_t(pthread_mach_thread_np(pthread_self())); +#elif defined(_WIN32) + return uint64_t(GetCurrentThreadId()); +#else + return uint64_t(pthread_self()); +#endif +} + +/** + * Get the operating-system level thread ID for the current thread. + * + * The returned value will uniquely identify this thread on the system. + * + * This makes it more suitable for logging or displaying in user interfaces + * than the result of getCurrentThreadID(). + * + * There are some potential caveats about this API, however: + * + * - In theory there is no guarantee that application threads map one-to-one to + * kernel threads. An application threading implementation could potentially + * share one OS thread across multiple application threads, and/or it could + * potentially move application threads between different OS threads over + * time. However, in practice all of the platforms we currently support have + * a one-to-one mapping between userspace threads and operating system + * threads. + * + * - This API may also be slightly slower than getCurrentThreadID() on some + * platforms. This API may require a system call, where getCurrentThreadID() + * may only need to read thread-local memory. + * + * On Linux the returned value is a pid_t, and can be used in contexts + * requiring a thread pid_t. + * + * The thread ID may be reused once the thread it corresponds to has been + * joined. + */ +inline uint64_t getOSThreadID() { +#if __APPLE__ + uint64_t tid; + pthread_threadid_np(nullptr, &tid); + return tid; +#elif defined(_WIN32) + return uint64_t(GetCurrentThreadId()); +#elif defined(__FreeBSD__) + long tid; + thr_self(&tid); + return uint64_t(tid); +#else + return uint64_t(syscall(FOLLY_SYS_gettid)); +#endif +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/ThreadName.cpp b/ios/Pods/Flipper-Folly/folly/system/ThreadName.cpp new file mode 100644 index 000000000..2eeb0fa11 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/ThreadName.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <folly/system/ThreadName.h> + +#include <type_traits> + +#include <folly/Portability.h> +#include <folly/Traits.h> +#include <folly/portability/PThread.h> +#include <folly/portability/Windows.h> + +// Android only, prctl is only used when pthread_setname_np +// and pthread_getname_np are not avilable. +#if defined(__linux__) +#define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME 1 +#else +#define FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME 0 +#endif + +#if FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME +#include <sys/prctl.h> +#endif + +// This looks a bit weird, but it's necessary to avoid +// having an undefined compiler function called. +#if defined(__GLIBC__) && !defined(__APPLE__) && !defined(__ANDROID__) +#if __GLIBC_PREREQ(2, 12) +// has pthread_setname_np(pthread_t, const char*) (2 params) +#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1 +#else +#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 0 +#endif +// pthread_setname_np was introduced in Android NDK version 9 +#elif defined(__ANDROID__) && __ANDROID_API__ >= 9 +#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 1 +#else +#define FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME 0 +#endif + +#if defined(__APPLE__) +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 +// macOS 10.6+ has pthread_setname_np(const char*) (1 param) +#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1 +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200 +// iOS 3.2+ has pthread_setname_np(const char*) (1 param) +#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 1 +#else +#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 0 +#endif +#else +#define FOLLY_HAS_PTHREAD_SETNAME_NP_NAME 0 +#endif // defined(__APPLE__) + +namespace folly { + +namespace { + +#if FOLLY_HAVE_PTHREAD && !defined(_WIN32) +pthread_t stdTidToPthreadId(std::thread::id tid) { + static_assert( + std::is_same<pthread_t, std::thread::native_handle_type>::value, + "This assumes that the native handle type is pthread_t"); + static_assert( + sizeof(std::thread::native_handle_type) == sizeof(std::thread::id), + "This assumes std::thread::id is a thin wrapper around " + "std::thread::native_handle_type, but that doesn't appear to be true."); + // In most implementations, std::thread::id is a thin wrapper around + // std::thread::native_handle_type, which means we can do unsafe things to + // extract it. + pthread_t id; + std::memcpy(&id, &tid, sizeof(id)); + return id; +} +#endif + +} // namespace + +bool canSetCurrentThreadName() { +#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \ + FOLLY_HAS_PTHREAD_SETNAME_NP_NAME || FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME || \ + defined(_WIN32) + return true; +#else + return false; +#endif +} + +bool canSetOtherThreadName() { +#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || defined(_WIN32) + return true; +#else + return false; +#endif +} + +static constexpr size_t kMaxThreadNameLength = 16; + +Optional<std::string> getThreadName(std::thread::id id) { +#if ( \ + FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \ + FOLLY_HAS_PTHREAD_SETNAME_NP_NAME) && \ + !defined(__ANDROID__) + // Android NDK does not yet support pthread_getname_np. + std::array<char, kMaxThreadNameLength> buf; + if (id != std::thread::id() && + pthread_getname_np(stdTidToPthreadId(id), buf.data(), buf.size()) == 0) { + return std::string(buf.data()); + } +#elif FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME + std::array<char, kMaxThreadNameLength> buf; + if (id == std::this_thread::get_id() && + prctl(PR_GET_NAME, buf.data(), 0L, 0L, 0L) == 0) { + return std::string(buf.data()); + } +#endif + (void)id; + return none; +} // namespace folly + +Optional<std::string> getCurrentThreadName() { + return getThreadName(std::this_thread::get_id()); +} + +bool setThreadName(std::thread::id tid, StringPiece name) { + auto trimmedName = name.subpiece(0, kMaxThreadNameLength - 1).str(); +#ifdef _WIN32 + static_assert( + sizeof(unsigned int) == sizeof(std::thread::id), + "This assumes std::thread::id is a thin wrapper around " + "the thread id as an unsigned int, but that doesn't appear to be true."); + +// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx +#pragma pack(push, 8) + struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags; // Reserved for future use; must be zero + }; + union TNIUnion { + THREADNAME_INFO tni; + ULONG_PTR upArray[4]; + }; +#pragma pack(pop) + + static constexpr DWORD kMSVCException = 0x406D1388; + + // std::thread::id is a thin wrapper around an integral thread id, + // so just extract the ID. + unsigned int id; + std::memcpy(&id, &tid, sizeof(id)); + + TNIUnion tniUnion = {0x1000, trimmedName.data(), id, 0}; + // This has to be in a separate stack frame from trimmedName, which requires + // C++ object destruction semantics. + return [&]() { + __try { + RaiseException(kMSVCException, 0, 4, tniUnion.upArray); + } __except ( + GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION + : EXCEPTION_EXECUTE_HANDLER) { + // Swallow the exception when a debugger isn't attached. + } + return true; + }(); +#else + name = name.subpiece(0, kMaxThreadNameLength - 1); + char buf[kMaxThreadNameLength] = {}; + std::memcpy(buf, name.data(), name.size()); + auto id = stdTidToPthreadId(tid); +#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME + return 0 == pthread_setname_np(id, buf); +#elif FOLLY_HAS_PTHREAD_SETNAME_NP_NAME + // Since macOS 10.6 and iOS 3.2 it is possible for a thread + // to set its own name using pthread, but + // not that of some other thread. + if (pthread_equal(pthread_self(), id)) { + return 0 == pthread_setname_np(buf); + } +#elif FOLLY_DETAIL_HAS_PRCTL_PR_SET_NAME + // for Android prctl is used instead of pthread_setname_np + // if Android NDK version is older than API level 9. + if (pthread_equal(pthread_self(), id)) { + return 0 == prctl(PR_SET_NAME, buf, 0L, 0L, 0L); + } +#endif + + (void)id; + return false; +#endif +} + +bool setThreadName(pthread_t pid, StringPiece name) { +#ifdef _WIN32 + static_assert( + sizeof(unsigned int) == sizeof(std::thread::id), + "This assumes std::thread::id is a thin wrapper around " + "the thread id as an unsigned int, but that doesn't appear to be true."); + + // std::thread::id is a thin wrapper around an integral thread id, + // so just stick the ID in. + unsigned int tid = pthread_getw32threadid_np(pid); + std::thread::id id; + std::memcpy(&id, &tid, sizeof(id)); + return setThreadName(id, name); +#else + static_assert( + std::is_same<pthread_t, std::thread::native_handle_type>::value, + "This assumes that the native handle type is pthread_t"); + static_assert( + sizeof(std::thread::native_handle_type) == sizeof(std::thread::id), + "This assumes std::thread::id is a thin wrapper around " + "std::thread::native_handle_type, but that doesn't appear to be true."); + // In most implementations, std::thread::id is a thin wrapper around + // std::thread::native_handle_type, which means we can do unsafe things to + // extract it. + std::thread::id id; + std::memcpy(static_cast<void*>(&id), &pid, sizeof(id)); + return setThreadName(id, name); +#endif +} + +bool setThreadName(StringPiece name) { + return setThreadName(std::this_thread::get_id(), name); +} +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/system/ThreadName.h b/ios/Pods/Flipper-Folly/folly/system/ThreadName.h new file mode 100644 index 000000000..360299881 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/system/ThreadName.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include <string> +#include <thread> + +#include <folly/Optional.h> +#include <folly/Range.h> +#include <folly/portability/Config.h> +#include <folly/portability/PThread.h> + +namespace folly { + +/** + * This returns true if the current platform supports setting the name of the + * current thread. + */ +bool canSetCurrentThreadName(); + +/** + * This returns true if the current platform supports setting the name of + * threads other than the one currently executing. + */ +bool canSetOtherThreadName(); + +/** + * Get the name of the given thread, or nothing if an error occurs + * or the functionality is not available. + */ +Optional<std::string> getThreadName(std::thread::id tid); + +/** + * Equivalent to getThreadName(std::this_thread::get_id()); + */ +Optional<std::string> getCurrentThreadName(); + +/** + * Set the name of the given thread. + * Returns false on failure, if an error occurs or the functionality + * is not available. + */ +bool setThreadName(std::thread::id tid, StringPiece name); +bool setThreadName(pthread_t pid, StringPiece name); + +/** + * Equivalent to setThreadName(std::this_thread::get_id(), name); + */ +bool setThreadName(StringPiece name); +} // namespace folly diff --git a/ios/Pods/Flipper-Folly/folly/tracing/ScopedTraceSection.h b/ios/Pods/Flipper-Folly/folly/tracing/ScopedTraceSection.h new file mode 100644 index 000000000..9f8aab23e --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/tracing/ScopedTraceSection.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 macro enables FbSystrace usage in production for fb4a. When + * FOLLY_SCOPED_TRACE_SECTION_HEADER is defined then a trace section is started + * and later automatically terminated at the close of the scope it is called in. + * In all other cases no action is taken. + */ + +#pragma once + +#if defined(FOLLY_SCOPED_TRACE_SECTION_HEADER) +#include FOLLY_SCOPED_TRACE_SECTION_HEADER +#else +#define FOLLY_SCOPED_TRACE_SECTION(arg, ...) \ + do { \ + } while (0) +#endif diff --git a/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h b/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h new file mode 100644 index 000000000..033809cb3 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// clang-format off +#include <cstddef> + +// Default constraint for the probe arguments as operands. +#ifndef FOLLY_SDT_ARG_CONSTRAINT +#define FOLLY_SDT_ARG_CONSTRAINT "nor" +#endif + +// Instruction to emit for the probe. +#define FOLLY_SDT_NOP nop + +// Note section properties. +#define FOLLY_SDT_NOTE_NAME "stapsdt" +#define FOLLY_SDT_NOTE_TYPE 3 + +// Semaphore variables are put in this section +#define FOLLY_SDT_SEMAPHORE_SECTION ".probes" + +// Size of address depending on platform. +#ifdef __LP64__ +#define FOLLY_SDT_ASM_ADDR .8byte +#else +#define FOLLY_SDT_ASM_ADDR .4byte +#endif + +// Assembler helper Macros. +#define FOLLY_SDT_S(x) #x +#define FOLLY_SDT_ASM_1(x) FOLLY_SDT_S(x) "\n" +#define FOLLY_SDT_ASM_2(a, b) FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "\n" +#define FOLLY_SDT_ASM_3(a, b, c) FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "," \ + FOLLY_SDT_S(c) "\n" +#define FOLLY_SDT_ASM_STRING(x) FOLLY_SDT_ASM_1(.asciz FOLLY_SDT_S(x)) + +// Helper to determine the size of an argument. +#define FOLLY_SDT_IS_ARRAY_POINTER(x) ((__builtin_classify_type(x) == 14) || \ + (__builtin_classify_type(x) == 5)) +#define FOLLY_SDT_ARGSIZE(x) (FOLLY_SDT_IS_ARRAY_POINTER(x) \ + ? sizeof(void*) \ + : sizeof(x)) + +// Format of each probe arguments as operand. +// Size of the arugment tagged with FOLLY_SDT_Sn, with "n" constraint. +// Value of the argument tagged with FOLLY_SDT_An, with configured constraint. +#define FOLLY_SDT_ARG(n, x) \ + [FOLLY_SDT_S##n] "n" ((size_t)FOLLY_SDT_ARGSIZE(x)), \ + [FOLLY_SDT_A##n] FOLLY_SDT_ARG_CONSTRAINT (x) + +// Templates to append arguments as operands. +#define FOLLY_SDT_OPERANDS_0() [__sdt_dummy] "g" (0) +#define FOLLY_SDT_OPERANDS_1(_1) FOLLY_SDT_ARG(1, _1) +#define FOLLY_SDT_OPERANDS_2(_1, _2) \ + FOLLY_SDT_OPERANDS_1(_1), FOLLY_SDT_ARG(2, _2) +#define FOLLY_SDT_OPERANDS_3(_1, _2, _3) \ + FOLLY_SDT_OPERANDS_2(_1, _2), FOLLY_SDT_ARG(3, _3) +#define FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4) \ + FOLLY_SDT_OPERANDS_3(_1, _2, _3), FOLLY_SDT_ARG(4, _4) +#define FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5) \ + FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4), FOLLY_SDT_ARG(5, _5) +#define FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6) \ + FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5), FOLLY_SDT_ARG(6, _6) +#define FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7) \ + FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6), FOLLY_SDT_ARG(7, _7) +#define FOLLY_SDT_OPERANDS_8(_1, _2, _3, _4, _5, _6, _7, _8) \ + FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7), FOLLY_SDT_ARG(8, _8) +#define FOLLY_SDT_OPERANDS_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \ + FOLLY_SDT_OPERANDS_8(_1, _2, _3, _4, _5, _6, _7, _8), FOLLY_SDT_ARG(9, _9) + +// Templates to reference the arguments from operands in note section. +#define FOLLY_SDT_ARGFMT(no) %n[FOLLY_SDT_S##no]@%[FOLLY_SDT_A##no] +#define FOLLY_SDT_ARG_TEMPLATE_0 /*No arguments*/ +#define FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(1) +#define FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(2) +#define FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARGFMT(3) +#define FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARGFMT(4) +#define FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARGFMT(5) +#define FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARGFMT(6) +#define FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARGFMT(7) +#define FOLLY_SDT_ARG_TEMPLATE_8 FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARGFMT(8) +#define FOLLY_SDT_ARG_TEMPLATE_9 FOLLY_SDT_ARG_TEMPLATE_8 FOLLY_SDT_ARGFMT(9) + +// Semaphore define, declare and probe note format + +#define FOLLY_SDT_SEMAPHORE(provider, name) \ + folly_sdt_semaphore_##provider##_##name + +#define FOLLY_SDT_DEFINE_SEMAPHORE(provider, name) \ + extern "C" { \ + volatile unsigned short FOLLY_SDT_SEMAPHORE(provider, name) \ + __attribute__((section(FOLLY_SDT_SEMAPHORE_SECTION), used)) = 0; \ + } + +#define FOLLY_SDT_DECLARE_SEMAPHORE(provider, name) \ + extern "C" volatile unsigned short FOLLY_SDT_SEMAPHORE(provider, name) + +#define FOLLY_SDT_SEMAPHORE_NOTE_0(provider, name) \ + FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*No Semaphore*/ \ + +#define FOLLY_SDT_SEMAPHORE_NOTE_1(provider, name) \ + FOLLY_SDT_ASM_1(FOLLY_SDT_ASM_ADDR FOLLY_SDT_SEMAPHORE(provider, name)) + +// Structure of note section for the probe. +#define FOLLY_SDT_NOTE_CONTENT(provider, name, has_semaphore, arg_template) \ + FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP) \ + FOLLY_SDT_ASM_3( .pushsection .note.stapsdt,"","note") \ + FOLLY_SDT_ASM_1( .balign 4) \ + FOLLY_SDT_ASM_3( .4byte 992f-991f, 994f-993f, FOLLY_SDT_NOTE_TYPE) \ + FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME) \ + FOLLY_SDT_ASM_1(992: .balign 4) \ + FOLLY_SDT_ASM_1(993: FOLLY_SDT_ASM_ADDR 990b) \ + FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*Reserved for Base Address*/ \ + FOLLY_SDT_SEMAPHORE_NOTE_##has_semaphore(provider, name) \ + FOLLY_SDT_ASM_STRING(provider) \ + FOLLY_SDT_ASM_STRING(name) \ + FOLLY_SDT_ASM_STRING(arg_template) \ + FOLLY_SDT_ASM_1(994: .balign 4) \ + FOLLY_SDT_ASM_1( .popsection) + +// Main probe Macro. +#define FOLLY_SDT_PROBE(provider, name, has_semaphore, n, arglist) \ + __asm__ __volatile__ ( \ + FOLLY_SDT_NOTE_CONTENT( \ + provider, name, has_semaphore, FOLLY_SDT_ARG_TEMPLATE_##n) \ + :: FOLLY_SDT_OPERANDS_##n arglist \ + ) \ + +// Helper Macros to handle variadic arguments. +#define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N +#define FOLLY_SDT_NARG(...) \ + FOLLY_SDT_NARG_(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define FOLLY_SDT_PROBE_N(provider, name, has_semaphore, N, ...) \ + FOLLY_SDT_PROBE(provider, name, has_semaphore, N, (__VA_ARGS__)) diff --git a/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint.h b/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint.h new file mode 100644 index 000000000..858b7dbc0 --- /dev/null +++ b/ios/Pods/Flipper-Folly/folly/tracing/StaticTracepoint.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) && \ + !FOLLY_DISABLE_SDT + +#include <folly/tracing/StaticTracepoint-ELFx86.h> + +#define FOLLY_SDT(provider, name, ...) \ + FOLLY_SDT_PROBE_N( \ + provider, name, 0, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__) +// Use FOLLY_SDT_DEFINE_SEMAPHORE(provider, name) to define the semaphore +// as global variable before using the FOLLY_SDT_WITH_SEMAPHORE macro +#define FOLLY_SDT_WITH_SEMAPHORE(provider, name, ...) \ + FOLLY_SDT_PROBE_N( \ + provider, name, 1, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__) +#define FOLLY_SDT_IS_ENABLED(provider, name) \ + (FOLLY_SDT_SEMAPHORE(provider, name) > 0) + +#else + +#define FOLLY_SDT(provider, name, ...) \ + do { \ + } while (0) +#define FOLLY_SDT_WITH_SEMAPHORE(provider, name, ...) \ + do { \ + } while (0) +#define FOLLY_SDT_IS_ENABLED(provider, name) (false) +#define FOLLY_SDT_DEFINE_SEMAPHORE(provider, name) +#define FOLLY_SDT_DECLARE_SEMAPHORE(provider, name) +#endif diff --git a/ios/Pods/Flipper-Glog/COPYING b/ios/Pods/Flipper-Glog/COPYING new file mode 100644 index 000000000..38396b580 --- /dev/null +++ b/ios/Pods/Flipper-Glog/COPYING @@ -0,0 +1,65 @@ +Copyright (c) 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +A function gettimeofday in utilities.cc is based on + +http://www.google.com/codesearch/p?hl=en#dR3YEbitojA/COPYING&q=GetSystemTimeAsFileTime%20license:bsd + +The license of this code is: + +Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ios/Pods/Flipper-Glog/README b/ios/Pods/Flipper-Glog/README new file mode 100644 index 000000000..77efd3750 --- /dev/null +++ b/ios/Pods/Flipper-Glog/README @@ -0,0 +1,5 @@ +This repository contains a C++ implementation of the Google logging +module. Documentation for the implementation is in doc/. + +See INSTALL for (generic) installation instructions for C++: basically + ./configure && make && make install diff --git a/ios/Pods/Flipper-Glog/README.windows b/ios/Pods/Flipper-Glog/README.windows new file mode 100644 index 000000000..dbeef321b --- /dev/null +++ b/ios/Pods/Flipper-Glog/README.windows @@ -0,0 +1,26 @@ +This project has begun being ported to Windows. A working solution +file exists in this directory: + google-glog.sln + +You can load this solution file into VC++ 9.0 (Visual Studio +2008). You may also be able to use this solution file with older +Visual Studios by converting the solution file. + +Note that stack tracing and some unittests are not ported +yet. + +You can also link glog code in statically -- see the example project +libglog_static and logging_unittest_static, which does this. For this +to work, you'll need to add "/D GOOGLE_GLOG_DLL_DECL=" to the compile +line of every glog's .cc file. + +I have little experience with Windows programming, so there may be +better ways to set this up than I've done! If you run across any +problems, please post to the google-glog Google Group, or report +them on the google-glog Google Code site: + http://groups.google.com/group/google-glog + https://github.com/google/glog/issues + +-- Shinichiro Hamaji + +Last modified: 23 January 2009 diff --git a/ios/Pods/Flipper-Glog/src/base/commandlineflags.h b/ios/Pods/Flipper-Glog/src/base/commandlineflags.h new file mode 100644 index 000000000..c8d508902 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/base/commandlineflags.h @@ -0,0 +1,133 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// This file is a compatibility layer that defines Google's version of +// command line flags that are used for configuration. +// +// We put flags into their own namespace. It is purposefully +// named in an opaque way that people should have trouble typing +// directly. The idea is that DEFINE puts the flag in the weird +// namespace, and DECLARE imports the flag from there into the +// current namespace. The net result is to force people to use +// DECLARE to get access to a flag, rather than saying +// extern bool FLAGS_logtostderr; +// or some such instead. We want this so we can put extra +// functionality (like sanity-checking) in DECLARE if we want, +// and make sure it is picked up everywhere. +// +// We also put the type of the variable in the namespace, so that +// people can't DECLARE_int32 something that they DEFINE_bool'd +// elsewhere. +#ifndef BASE_COMMANDLINEFLAGS_H__ +#define BASE_COMMANDLINEFLAGS_H__ + +#include "config.h" +#include <string> +#include <string.h> // for memchr +#include <stdlib.h> // for getenv + +#ifdef HAVE_LIB_GFLAGS + +#include <gflags/gflags.h> + +#else + +#include "glog/logging.h" + +#define DECLARE_VARIABLE(type, shorttype, name, tn) \ + namespace fL##shorttype { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name +#define DEFINE_VARIABLE(type, shorttype, name, value, meaning, tn) \ + namespace fL##shorttype { \ + GOOGLE_GLOG_DLL_DECL type FLAGS_##name(value); \ + char FLAGS_no##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, B, name, bool) +#define DEFINE_bool(name, value, meaning) \ + DEFINE_VARIABLE(bool, B, name, value, meaning, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(GOOGLE_NAMESPACE::int32, I, name, int32) +#define DEFINE_int32(name, value, meaning) \ + DEFINE_VARIABLE(GOOGLE_NAMESPACE::int32, I, name, value, meaning, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace fLS { \ + extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name; \ + } \ + using fLS::FLAGS_##name +#define DEFINE_string(name, value, meaning) \ + namespace fLS { \ + std::string FLAGS_##name##_buf(value); \ + GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name = FLAGS_##name##_buf; \ + char FLAGS_no##name; \ + } \ + using fLS::FLAGS_##name + +#endif // HAVE_LIB_GFLAGS + +// Define GLOG_DEFINE_* using DEFINE_* . By using these macros, we +// have GLOG_* environ variables even if we have gflags installed. +// +// If both an environment variable and a flag are specified, the value +// specified by a flag wins. E.g., if GLOG_v=0 and --v=1, the +// verbosity will be 1, not 0. + +#define GLOG_DEFINE_bool(name, value, meaning) \ + DEFINE_bool(name, EnvToBool("GLOG_" #name, value), meaning) + +#define GLOG_DEFINE_int32(name, value, meaning) \ + DEFINE_int32(name, EnvToInt("GLOG_" #name, value), meaning) + +#define GLOG_DEFINE_string(name, value, meaning) \ + DEFINE_string(name, EnvToString("GLOG_" #name, value), meaning) + +// These macros (could be functions, but I don't want to bother with a .cc +// file), make it easier to initialize flags from the environment. + +#define EnvToString(envname, dflt) \ + (!getenv(envname) ? (dflt) : getenv(envname)) + +#define EnvToBool(envname, dflt) \ + (!getenv(envname) ? (dflt) : memchr("tTyY1\0", getenv(envname)[0], 6) != NULL) + +#define EnvToInt(envname, dflt) \ + (!getenv(envname) ? (dflt) : strtol(getenv(envname), NULL, 10)) + +#endif // BASE_COMMANDLINEFLAGS_H__ diff --git a/ios/Pods/Flipper-Glog/src/base/googleinit.h b/ios/Pods/Flipper-Glog/src/base/googleinit.h new file mode 100644 index 000000000..5a8b515cd --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/base/googleinit.h @@ -0,0 +1,51 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Jacob Hoffman-Andrews + +#ifndef _GOOGLEINIT_H +#define _GOOGLEINIT_H + +class GoogleInitializer { + public: + typedef void (*void_function)(void); + GoogleInitializer(const char*, void_function f) { + f(); + } +}; + +#define REGISTER_MODULE_INITIALIZER(name, body) \ + namespace { \ + static void google_init_module_##name () { body; } \ + GoogleInitializer google_initializer_module_##name(#name, \ + google_init_module_##name); \ + } + +#endif /* _GOOGLEINIT_H */ diff --git a/ios/Pods/Flipper-Glog/src/base/mutex.h b/ios/Pods/Flipper-Glog/src/base/mutex.h new file mode 100644 index 000000000..ced2b9950 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/base/mutex.h @@ -0,0 +1,333 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --- +// Author: Craig Silverstein. +// +// A simple mutex wrapper, supporting locks and read-write locks. +// You should assume the locks are *not* re-entrant. +// +// To use: you should define the following macros in your configure.ac: +// ACX_PTHREAD +// AC_RWLOCK +// The latter is defined in ../autoconf. +// +// This class is meant to be internal-only and should be wrapped by an +// internal namespace. Before you use this module, please give the +// name of your internal namespace for this module. Or, if you want +// to expose it, you'll want to move it to the Google namespace. We +// cannot put this class in global namespace because there can be some +// problems when we have multiple versions of Mutex in each shared object. +// +// NOTE: by default, we have #ifdef'ed out the TryLock() method. +// This is for two reasons: +// 1) TryLock() under Windows is a bit annoying (it requires a +// #define to be defined very early). +// 2) TryLock() is broken for NO_THREADS mode, at least in NDEBUG +// mode. +// If you need TryLock(), and either these two caveats are not a +// problem for you, or you're willing to work around them, then +// feel free to #define GMUTEX_TRYLOCK, or to remove the #ifdefs +// in the code below. +// +// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy: +// http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html +// Because of that, we might as well use windows locks for +// cygwin. They seem to be more reliable than the cygwin pthreads layer. +// +// TRICKY IMPLEMENTATION NOTE: +// This class is designed to be safe to use during +// dynamic-initialization -- that is, by global constructors that are +// run before main() starts. The issue in this case is that +// dynamic-initialization happens in an unpredictable order, and it +// could be that someone else's dynamic initializer could call a +// function that tries to acquire this mutex -- but that all happens +// before this mutex's constructor has run. (This can happen even if +// the mutex and the function that uses the mutex are in the same .cc +// file.) Basically, because Mutex does non-trivial work in its +// constructor, it's not, in the naive implementation, safe to use +// before dynamic initialization has run on it. +// +// The solution used here is to pair the actual mutex primitive with a +// bool that is set to true when the mutex is dynamically initialized. +// (Before that it's false.) Then we modify all mutex routines to +// look at the bool, and not try to lock/unlock until the bool makes +// it to true (which happens after the Mutex constructor has run.) +// +// This works because before main() starts -- particularly, during +// dynamic initialization -- there are no threads, so a) it's ok that +// the mutex operations are a no-op, since we don't need locking then +// anyway; and b) we can be quite confident our bool won't change +// state between a call to Lock() and a call to Unlock() (that would +// require a global constructor in one translation unit to call Lock() +// and another global constructor in another translation unit to call +// Unlock() later, which is pretty perverse). +// +// That said, it's tricky, and can conceivably fail; it's safest to +// avoid trying to acquire a mutex in a global constructor, if you +// can. One way it can fail is that a really smart compiler might +// initialize the bool to true at static-initialization time (too +// early) rather than at dynamic-initialization time. To discourage +// that, we set is_safe_ to true in code (not the constructor +// colon-initializer) and set it to true via a function that always +// evaluates to true, but that the compiler can't know always +// evaluates to true. This should be good enough. + +#ifndef GOOGLE_MUTEX_H_ +#define GOOGLE_MUTEX_H_ + +#include "config.h" // to figure out pthreads support + +#if defined(NO_THREADS) + typedef int MutexType; // to keep a lock-count +#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN // We only need minimal includes +# endif +# ifdef GMUTEX_TRYLOCK + // We need Windows NT or later for TryEnterCriticalSection(). If you + // don't need that functionality, you can remove these _WIN32_WINNT + // lines, and change TryLock() to assert(0) or something. +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 +# endif +# endif +// To avoid macro definition of ERROR. +# ifndef NOGDI +# define NOGDI +# endif +// To avoid macro definition of min/max. +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include <windows.h> + typedef CRITICAL_SECTION MutexType; +#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) + // Needed for pthread_rwlock_*. If it causes problems, you could take it + // out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it + // *does* cause problems for FreeBSD, or MacOSX, but isn't needed + // for locking there.) +# ifdef __linux__ +# ifndef _XOPEN_SOURCE // Some other header might have already set it for us. +# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls +# endif +# endif +# include <pthread.h> + typedef pthread_rwlock_t MutexType; +#elif defined(HAVE_PTHREAD) +# include <pthread.h> + typedef pthread_mutex_t MutexType; +#else +# error Need to implement mutex.h for your architecture, or #define NO_THREADS +#endif + +// We need to include these header files after defining _XOPEN_SOURCE +// as they may define the _XOPEN_SOURCE macro. +#include <assert.h> +#include <stdlib.h> // for abort() + +#define MUTEX_NAMESPACE glog_internal_namespace_ + +namespace MUTEX_NAMESPACE { + +class Mutex { + public: + // Create a Mutex that is not held by anybody. This constructor is + // typically used for Mutexes allocated on the heap or the stack. + // See below for a recommendation for constructing global Mutex + // objects. + inline Mutex(); + + // Destructor + inline ~Mutex(); + + inline void Lock(); // Block if needed until free then acquire exclusively + inline void Unlock(); // Release a lock acquired via Lock() +#ifdef GMUTEX_TRYLOCK + inline bool TryLock(); // If free, Lock() and return true, else return false +#endif + // Note that on systems that don't support read-write locks, these may + // be implemented as synonyms to Lock() and Unlock(). So you can use + // these for efficiency, but don't use them anyplace where being able + // to do shared reads is necessary to avoid deadlock. + inline void ReaderLock(); // Block until free or shared then acquire a share + inline void ReaderUnlock(); // Release a read share of this Mutex + inline void WriterLock() { Lock(); } // Acquire an exclusive lock + inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock() + + // TODO(hamaji): Do nothing, implement correctly. + inline void AssertHeld() {} + + private: + MutexType mutex_; + // We want to make sure that the compiler sets is_safe_ to true only + // when we tell it to, and never makes assumptions is_safe_ is + // always true. volatile is the most reliable way to do that. + volatile bool is_safe_; + + inline void SetIsSafe() { is_safe_ = true; } + + // Catch the error of writing Mutex when intending MutexLock. + Mutex(Mutex* /*ignored*/) {} + // Disallow "evil" constructors + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// Now the implementation of Mutex for various systems +#if defined(NO_THREADS) + +// When we don't have threads, we can be either reading or writing, +// but not both. We can have lots of readers at once (in no-threads +// mode, that's most likely to happen in recursive function calls), +// but only one writer. We represent this by having mutex_ be -1 when +// writing and a number > 0 when reading (and 0 when no lock is held). +// +// In debug mode, we assert these invariants, while in non-debug mode +// we do nothing, for efficiency. That's why everything is in an +// assert. + +Mutex::Mutex() : mutex_(0) { } +Mutex::~Mutex() { assert(mutex_ == 0); } +void Mutex::Lock() { assert(--mutex_ == -1); } +void Mutex::Unlock() { assert(mutex_++ == -1); } +#ifdef GMUTEX_TRYLOCK +bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; } +#endif +void Mutex::ReaderLock() { assert(++mutex_ > 0); } +void Mutex::ReaderUnlock() { assert(mutex_-- > 0); } + +#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__) + +Mutex::Mutex() { InitializeCriticalSection(&mutex_); SetIsSafe(); } +Mutex::~Mutex() { DeleteCriticalSection(&mutex_); } +void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); } +void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); } +#ifdef GMUTEX_TRYLOCK +bool Mutex::TryLock() { return is_safe_ ? + TryEnterCriticalSection(&mutex_) != 0 : true; } +#endif +void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks +void Mutex::ReaderUnlock() { Unlock(); } + +#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) + +#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \ + if (is_safe_ && fncall(&mutex_) != 0) abort(); \ +} while (0) + +Mutex::Mutex() { + SetIsSafe(); + if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort(); +} +Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy); } +void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock); } +void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock); } +#ifdef GMUTEX_TRYLOCK +bool Mutex::TryLock() { return is_safe_ ? + pthread_rwlock_trywrlock(&mutex_) == 0 : + true; } +#endif +void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock); } +void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); } +#undef SAFE_PTHREAD + +#elif defined(HAVE_PTHREAD) + +#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \ + if (is_safe_ && fncall(&mutex_) != 0) abort(); \ +} while (0) + +Mutex::Mutex() { + SetIsSafe(); + if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort(); +} +Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy); } +void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); } +void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); } +#ifdef GMUTEX_TRYLOCK +bool Mutex::TryLock() { return is_safe_ ? + pthread_mutex_trylock(&mutex_) == 0 : true; } +#endif +void Mutex::ReaderLock() { Lock(); } +void Mutex::ReaderUnlock() { Unlock(); } +#undef SAFE_PTHREAD + +#endif + +// -------------------------------------------------------------------------- +// Some helper classes + +// MutexLock(mu) acquires mu when constructed and releases it when destroyed. +class MutexLock { + public: + explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); } + ~MutexLock() { mu_->Unlock(); } + private: + Mutex * const mu_; + // Disallow "evil" constructors + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +// ReaderMutexLock and WriterMutexLock do the same, for rwlocks +class ReaderMutexLock { + public: + explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); } + ~ReaderMutexLock() { mu_->ReaderUnlock(); } + private: + Mutex * const mu_; + // Disallow "evil" constructors + ReaderMutexLock(const ReaderMutexLock&); + void operator=(const ReaderMutexLock&); +}; + +class WriterMutexLock { + public: + explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); } + ~WriterMutexLock() { mu_->WriterUnlock(); } + private: + Mutex * const mu_; + // Disallow "evil" constructors + WriterMutexLock(const WriterMutexLock&); + void operator=(const WriterMutexLock&); +}; + +// Catch bug where variable name is omitted, e.g. MutexLock (&mu); +#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name) +#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name) +#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name) + +} // namespace MUTEX_NAMESPACE + +using namespace MUTEX_NAMESPACE; + +#undef MUTEX_NAMESPACE + +#endif /* #define GOOGLE_MUTEX_H__ */ diff --git a/ios/Pods/Flipper-Glog/src/config.h b/ios/Pods/Flipper-Glog/src/config.h new file mode 100644 index 000000000..79f142537 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/config.h @@ -0,0 +1,200 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* define if glog doesn't use RTTI */ +/* #undef DISABLE_RTTI */ + +/* Namespace for Google classes */ +#define GOOGLE_NAMESPACE google + +/* Define if you have the `dladdr' function */ +#define HAVE_DLADDR 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <execinfo.h> header file. */ +#define HAVE_EXECINFO_H 1 + +/* Define if you have the `fcntl' function */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the <glob.h> header file. */ +#define HAVE_GLOB_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the <libunwind.h> header file. */ +#define HAVE_LIBUNWIND_H 1 + +/* define if you have google gflags library */ +/* #undef HAVE_LIB_GFLAGS */ + +/* define if you have google gmock library */ +/* #undef HAVE_LIB_GMOCK */ + +/* define if you have google gtest library */ +/* #undef HAVE_LIB_GTEST */ + +/* define if you have libunwind */ +/* #undef HAVE_LIB_UNWIND */ + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* define if the compiler implements namespaces */ +#define HAVE_NAMESPACES 1 + +/* Define if you have the 'pread' function */ +#define HAVE_PREAD 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Define to 1 if you have the <pwd.h> header file. */ +#define HAVE_PWD_H 1 + +/* Define if you have the 'pwrite' function */ +#define HAVE_PWRITE 1 + +/* define if the compiler implements pthread_rwlock_* */ +#define HAVE_RWLOCK 1 + +/* Define if you have the 'sigaction' function */ +#define HAVE_SIGACTION 1 + +/* Define if you have the `sigaltstack' function */ +#define HAVE_SIGALTSTACK 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <syscall.h> header file. */ +/* #undef HAVE_SYSCALL_H */ + +/* Define to 1 if you have the <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/syscall.h> header file. */ +#define HAVE_SYS_SYSCALL_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/ucontext.h> header file. */ +#define HAVE_SYS_UCONTEXT_H 1 + +/* Define to 1 if you have the <sys/utsname.h> header file. */ +#define HAVE_SYS_UTSNAME_H 1 + +/* Define to 1 if you have the <ucontext.h> header file. */ +/* #undef HAVE_UCONTEXT_H */ + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <unwind.h> header file. */ +#define HAVE_UNWIND_H 1 + +/* define if the compiler supports using expression for operator */ +#define HAVE_USING_OPERATOR 1 + +/* define if your compiler has __attribute__ */ +#define HAVE___ATTRIBUTE__ 1 + +/* define if your compiler has __builtin_expect */ +#define HAVE___BUILTIN_EXPECT 1 + +/* define if your compiler has __sync_val_compare_and_swap */ +#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "glog" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "opensource@google.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "glog" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "glog 0.3.5" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "glog" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.3.5" + +/* How to access the PC from a struct ucontext */ +/* #undef PC_FROM_UCONTEXT */ + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 4 + +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* the namespace where STL code like vector<> is defined */ +#define STL_NAMESPACE std + +/* location of source code */ +#define TEST_SRC_DIR "." + +/* Version number of package */ +#define VERSION "0.3.5" + +/* Stops putting the code inside the Google namespace */ +#define _END_GOOGLE_NAMESPACE_ } + +/* Puts following code inside the Google namespace */ +#define _START_GOOGLE_NAMESPACE_ namespace google { +/* Add in so we have Apple Target Conditionals */ +#ifdef __APPLE__ +#include <TargetConditionals.h> +#include <Availability.h> +#endif +/* Special configuration for AppleTVOS */ +#if TARGET_OS_TV +#undef HAVE_SYSCALL_H +#undef HAVE_SYS_SYSCALL_H +#undef OS_MACOSX +#endif +/* Special configuration for ucontext */ +#undef HAVE_UCONTEXT_H +#undef PC_FROM_UCONTEXT +#if defined(__x86_64__) +#define PC_FROM_UCONTEXT uc_mcontext->__ss.__rip +#elif defined(__i386__) +#define PC_FROM_UCONTEXT uc_mcontext->__ss.__eip +#endif diff --git a/ios/Pods/Flipper-Glog/src/config.h.cmake.in b/ios/Pods/Flipper-Glog/src/config.h.cmake.in new file mode 100644 index 000000000..6635df12d --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/config.h.cmake.in @@ -0,0 +1,184 @@ +/* define if glog doesn't use RTTI */ +#cmakedefine DISABLE_RTTI + +/* Namespace for Google classes */ +#cmakedefine GOOGLE_NAMESPACE ${GOOGLE_NAMESPACE} + +/* Define if you have the `dladdr' function */ +#cmakedefine HAVE_DLADDR + +/* Define if you have the `snprintf' function */ +#cmakedefine HAVE_SNPRINTF + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#cmakedefine HAVE_DLFCN_H + +/* Define to 1 if you have the <execinfo.h> header file. */ +#cmakedefine HAVE_EXECINFO_H + +/* Define if you have the `fcntl' function */ +#cmakedefine HAVE_FCNTL + +/* Define to 1 if you have the <glob.h> header file. */ +#cmakedefine HAVE_GLOB_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H} + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#cmakedefine HAVE_LIBPTHREAD + +/* Define to 1 if you have the <libunwind.h> header file. */ +#cmakedefine HAVE_LIBUNWIND_H + +/* define if you have google gflags library */ +#cmakedefine HAVE_LIB_GFLAGS + +/* define if you have google gmock library */ +#cmakedefine HAVE_LIB_GMOCK + +/* define if you have google gtest library */ +#cmakedefine HAVE_LIB_GTEST + +/* define if you have libunwind */ +#cmakedefine HAVE_LIB_UNWIND + +/* Define to 1 if you have the <memory.h> header file. */ +#cmakedefine HAVE_MEMORY_H + +/* define to disable multithreading support. */ +#cmakedefine NO_THREADS + +/* define if the compiler implements namespaces */ +#cmakedefine HAVE_NAMESPACES + +/* Define if you have the 'pread' function */ +#cmakedefine HAVE_PREAD + +/* Define if you have POSIX threads libraries and header files. */ +#cmakedefine HAVE_PTHREAD + +/* Define to 1 if you have the <pwd.h> header file. */ +#cmakedefine HAVE_PWD_H + +/* Define if you have the 'pwrite' function */ +#cmakedefine HAVE_PWRITE + +/* define if the compiler implements pthread_rwlock_* */ +#cmakedefine HAVE_RWLOCK + +/* Define if you have the 'sigaction' function */ +#cmakedefine HAVE_SIGACTION + +/* Define if you have the `sigaltstack' function */ +#cmakedefine HAVE_SIGALTSTACK + +/* Define to 1 if you have the <stdint.h> header file. */ +#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H} + +/* Define to 1 if you have the <stdlib.h> header file. */ +#cmakedefine HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#cmakedefine HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#cmakedefine HAVE_STRING_H + +/* Define to 1 if you have the <syscall.h> header file. */ +#cmakedefine HAVE_SYSCALL_H + +/* Define to 1 if you have the <syslog.h> header file. */ +#cmakedefine HAVE_SYSLOG_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#cmakedefine HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/syscall.h> header file. */ +#cmakedefine HAVE_SYS_SYSCALL_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#cmakedefine HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H} + +/* Define to 1 if you have the <sys/ucontext.h> header file. */ +#cmakedefine HAVE_SYS_UCONTEXT_H + +/* Define to 1 if you have the <sys/utsname.h> header file. */ +#cmakedefine HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the <ucontext.h> header file. */ +#cmakedefine HAVE_UCONTEXT_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} + +/* Define to 1 if you have the <unwind.h> header file. */ +#cmakedefine HAVE_UNWIND_H ${HAVE_UNWIND_H} + +/* define if the compiler supports using expression for operator */ +#cmakedefine HAVE_USING_OPERATOR + +/* define if your compiler has __attribute__ */ +#cmakedefine HAVE___ATTRIBUTE__ + +/* define if your compiler has __builtin_expect */ +#cmakedefine HAVE___BUILTIN_EXPECT ${HAVE___BUILTIN_EXPECT} + +/* define if your compiler has __sync_val_compare_and_swap */ +#cmakedefine HAVE___SYNC_VAL_COMPARE_AND_SWAP + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#cmakedefine LT_OBJDIR + +/* Name of package */ +#cmakedefine PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#cmakedefine PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#cmakedefine PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#cmakedefine PACKAGE_URL + +/* Define to the version of this package. */ +#cmakedefine PACKAGE_VERSION + +/* How to access the PC from a struct ucontext */ +#cmakedefine PC_FROM_UCONTEXT + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#cmakedefine PTHREAD_CREATE_JOINABLE + +/* The size of `void *', as computed by sizeof. */ +#cmakedefine SIZEOF_VOID_P ${SIZEOF_VOID_P} + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS + +/* the namespace where STL code like vector<> is defined */ +#cmakedefine STL_NAMESPACE ${STL_NAMESPACE} + +/* location of source code */ +#cmakedefine TEST_SRC_DIR ${TEST_SRC_DIR} + +/* Version number of package */ +#cmakedefine VERSION + +/* Stops putting the code inside the Google namespace */ +#cmakedefine _END_GOOGLE_NAMESPACE_ ${_END_GOOGLE_NAMESPACE_} + +/* Puts following code inside the Google namespace */ +#cmakedefine _START_GOOGLE_NAMESPACE_ ${_START_GOOGLE_NAMESPACE_} diff --git a/ios/Pods/Flipper-Glog/src/config.h.in b/ios/Pods/Flipper-Glog/src/config.h.in new file mode 100644 index 000000000..8190f2397 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/config.h.in @@ -0,0 +1,180 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* define if glog doesn't use RTTI */ +#undef DISABLE_RTTI + +/* Namespace for Google classes */ +#undef GOOGLE_NAMESPACE + +/* Define if you have the `dladdr' function */ +#undef HAVE_DLADDR + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <execinfo.h> header file. */ +#undef HAVE_EXECINFO_H + +/* Define if you have the `fcntl' function */ +#undef HAVE_FCNTL + +/* Define to 1 if you have the <glob.h> header file. */ +#undef HAVE_GLOB_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the <libunwind.h> header file. */ +#undef HAVE_LIBUNWIND_H + +/* define if you have google gflags library */ +#undef HAVE_LIB_GFLAGS + +/* define if you have google gmock library */ +#undef HAVE_LIB_GMOCK + +/* define if you have google gtest library */ +#undef HAVE_LIB_GTEST + +/* define if you have libunwind */ +#undef HAVE_LIB_UNWIND + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* define if the compiler implements namespaces */ +#undef HAVE_NAMESPACES + +/* Define if you have the 'pread' function */ +#undef HAVE_PREAD + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the <pwd.h> header file. */ +#undef HAVE_PWD_H + +/* Define if you have the 'pwrite' function */ +#undef HAVE_PWRITE + +/* define if the compiler implements pthread_rwlock_* */ +#undef HAVE_RWLOCK + +/* Define if you have the 'sigaction' function */ +#undef HAVE_SIGACTION + +/* Define if you have the `sigaltstack' function */ +#undef HAVE_SIGALTSTACK + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <syscall.h> header file. */ +#undef HAVE_SYSCALL_H + +/* Define to 1 if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/syscall.h> header file. */ +#undef HAVE_SYS_SYSCALL_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/ucontext.h> header file. */ +#undef HAVE_SYS_UCONTEXT_H + +/* Define to 1 if you have the <sys/utsname.h> header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the <ucontext.h> header file. */ +#undef HAVE_UCONTEXT_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the <unwind.h> header file. */ +#undef HAVE_UNWIND_H + +/* define if the compiler supports using expression for operator */ +#undef HAVE_USING_OPERATOR + +/* define if your compiler has __attribute__ */ +#undef HAVE___ATTRIBUTE__ + +/* define if your compiler has __builtin_expect */ +#undef HAVE___BUILTIN_EXPECT + +/* define if your compiler has __sync_val_compare_and_swap */ +#undef HAVE___SYNC_VAL_COMPARE_AND_SWAP + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* How to access the PC from a struct ucontext */ +#undef PC_FROM_UCONTEXT + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* The size of `void *', as computed by sizeof. */ +#undef SIZEOF_VOID_P + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* the namespace where STL code like vector<> is defined */ +#undef STL_NAMESPACE + +/* location of source code */ +#undef TEST_SRC_DIR + +/* Version number of package */ +#undef VERSION + +/* Stops putting the code inside the Google namespace */ +#undef _END_GOOGLE_NAMESPACE_ + +/* Puts following code inside the Google namespace */ +#undef _START_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/config_for_unittests.h b/ios/Pods/Flipper-Glog/src/config_for_unittests.h new file mode 100644 index 000000000..13ea8eab7 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/config_for_unittests.h @@ -0,0 +1,66 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// All Rights Reserved. +// +// Author: Craig Silverstein +// Copied from google-perftools and modified by Shinichiro Hamaji +// +// This file is needed for windows -- unittests are not part of the +// glog dll, but still want to include config.h just like the +// dll does, so they can use internal tools and APIs for testing. +// +// The problem is that config.h declares GOOGLE_GLOG_DLL_DECL to be +// for exporting symbols, but the unittest needs to *import* symbols +// (since it's not the dll). +// +// The solution is to have this file, which is just like config.h but +// sets GOOGLE_GLOG_DLL_DECL to do a dllimport instead of a dllexport. +// +// The reason we need this extra GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS +// variable is in case people want to set GOOGLE_GLOG_DLL_DECL explicitly +// to something other than __declspec(dllexport). In that case, they +// may want to use something other than __declspec(dllimport) for the +// unittest case. For that, we allow folks to define both +// GOOGLE_GLOG_DLL_DECL and GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS explicitly. +// +// NOTE: This file is equivalent to config.h on non-windows systems, +// which never defined GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS and always +// define GOOGLE_GLOG_DLL_DECL to the empty string. + +#include "config.h" + +#undef GOOGLE_GLOG_DLL_DECL +#ifdef GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS +# define GOOGLE_GLOG_DLL_DECL GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS +#else +// if DLL_DECL_FOR_UNITTESTS isn't defined, use "" +# define GOOGLE_GLOG_DLL_DECL +#endif diff --git a/ios/Pods/Flipper-Glog/src/demangle.cc b/ios/Pods/Flipper-Glog/src/demangle.cc new file mode 100644 index 000000000..e858181a6 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/demangle.cc @@ -0,0 +1,1304 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Satoru Takabayashi +// +// For reference check out: +// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling +// +// Note that we only have partial C++0x support yet. + +#include <stdio.h> // for NULL +#include "demangle.h" + +_START_GOOGLE_NAMESPACE_ + +typedef struct { + const char *abbrev; + const char *real_name; +} AbbrevPair; + +// List of operators from Itanium C++ ABI. +static const AbbrevPair kOperatorList[] = { + { "nw", "new" }, + { "na", "new[]" }, + { "dl", "delete" }, + { "da", "delete[]" }, + { "ps", "+" }, + { "ng", "-" }, + { "ad", "&" }, + { "de", "*" }, + { "co", "~" }, + { "pl", "+" }, + { "mi", "-" }, + { "ml", "*" }, + { "dv", "/" }, + { "rm", "%" }, + { "an", "&" }, + { "or", "|" }, + { "eo", "^" }, + { "aS", "=" }, + { "pL", "+=" }, + { "mI", "-=" }, + { "mL", "*=" }, + { "dV", "/=" }, + { "rM", "%=" }, + { "aN", "&=" }, + { "oR", "|=" }, + { "eO", "^=" }, + { "ls", "<<" }, + { "rs", ">>" }, + { "lS", "<<=" }, + { "rS", ">>=" }, + { "eq", "==" }, + { "ne", "!=" }, + { "lt", "<" }, + { "gt", ">" }, + { "le", "<=" }, + { "ge", ">=" }, + { "nt", "!" }, + { "aa", "&&" }, + { "oo", "||" }, + { "pp", "++" }, + { "mm", "--" }, + { "cm", "," }, + { "pm", "->*" }, + { "pt", "->" }, + { "cl", "()" }, + { "ix", "[]" }, + { "qu", "?" }, + { "st", "sizeof" }, + { "sz", "sizeof" }, + { NULL, NULL }, +}; + +// List of builtin types from Itanium C++ ABI. +static const AbbrevPair kBuiltinTypeList[] = { + { "v", "void" }, + { "w", "wchar_t" }, + { "b", "bool" }, + { "c", "char" }, + { "a", "signed char" }, + { "h", "unsigned char" }, + { "s", "short" }, + { "t", "unsigned short" }, + { "i", "int" }, + { "j", "unsigned int" }, + { "l", "long" }, + { "m", "unsigned long" }, + { "x", "long long" }, + { "y", "unsigned long long" }, + { "n", "__int128" }, + { "o", "unsigned __int128" }, + { "f", "float" }, + { "d", "double" }, + { "e", "long double" }, + { "g", "__float128" }, + { "z", "ellipsis" }, + { NULL, NULL } +}; + +// List of substitutions Itanium C++ ABI. +static const AbbrevPair kSubstitutionList[] = { + { "St", "" }, + { "Sa", "allocator" }, + { "Sb", "basic_string" }, + // std::basic_string<char, std::char_traits<char>,std::allocator<char> > + { "Ss", "string"}, + // std::basic_istream<char, std::char_traits<char> > + { "Si", "istream" }, + // std::basic_ostream<char, std::char_traits<char> > + { "So", "ostream" }, + // std::basic_iostream<char, std::char_traits<char> > + { "Sd", "iostream" }, + { NULL, NULL } +}; + +// State needed for demangling. +typedef struct { + const char *mangled_cur; // Cursor of mangled name. + char *out_cur; // Cursor of output string. + const char *out_begin; // Beginning of output string. + const char *out_end; // End of output string. + const char *prev_name; // For constructors/destructors. + int prev_name_length; // For constructors/destructors. + short nest_level; // For nested names. + bool append; // Append flag. + bool overflowed; // True if output gets overflowed. +} State; + +// We don't use strlen() in libc since it's not guaranteed to be async +// signal safe. +static size_t StrLen(const char *str) { + size_t len = 0; + while (*str != '\0') { + ++str; + ++len; + } + return len; +} + +// Returns true if "str" has at least "n" characters remaining. +static bool AtLeastNumCharsRemaining(const char *str, int n) { + for (int i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } + } + return true; +} + +// Returns true if "str" has "prefix" as a prefix. +static bool StrPrefix(const char *str, const char *prefix) { + size_t i = 0; + while (str[i] != '\0' && prefix[i] != '\0' && + str[i] == prefix[i]) { + ++i; + } + return prefix[i] == '\0'; // Consumed everything in "prefix". +} + +static void InitState(State *state, const char *mangled, + char *out, int out_size) { + state->mangled_cur = mangled; + state->out_cur = out; + state->out_begin = out; + state->out_end = out + out_size; + state->prev_name = NULL; + state->prev_name_length = -1; + state->nest_level = -1; + state->append = true; + state->overflowed = false; +} + +// Returns true and advances "mangled_cur" if we find "one_char_token" +// at "mangled_cur" position. It is assumed that "one_char_token" does +// not contain '\0'. +static bool ParseOneCharToken(State *state, const char one_char_token) { + if (state->mangled_cur[0] == one_char_token) { + ++state->mangled_cur; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find "two_char_token" +// at "mangled_cur" position. It is assumed that "two_char_token" does +// not contain '\0'. +static bool ParseTwoCharToken(State *state, const char *two_char_token) { + if (state->mangled_cur[0] == two_char_token[0] && + state->mangled_cur[1] == two_char_token[1]) { + state->mangled_cur += 2; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find any character in +// "char_class" at "mangled_cur" position. +static bool ParseCharClass(State *state, const char *char_class) { + const char *p = char_class; + for (; *p != '\0'; ++p) { + if (state->mangled_cur[0] == *p) { + ++state->mangled_cur; + return true; + } + } + return false; +} + +// This function is used for handling an optional non-terminal. +static bool Optional(bool) { + return true; +} + +// This function is used for handling <non-terminal>+ syntax. +typedef bool (*ParseFunc)(State *); +static bool OneOrMore(ParseFunc parse_func, State *state) { + if (parse_func(state)) { + while (parse_func(state)) { + } + return true; + } + return false; +} + +// This function is used for handling <non-terminal>* syntax. The function +// always returns true and must be followed by a termination token or a +// terminating sequence not handled by parse_func (e.g. +// ParseOneCharToken(state, 'E')). +static bool ZeroOrMore(ParseFunc parse_func, State *state) { + while (parse_func(state)) { + } + return true; +} + +// Append "str" at "out_cur". If there is an overflow, "overflowed" +// is set to true for later use. The output string is ensured to +// always terminate with '\0' as long as there is no overflow. +static void Append(State *state, const char * const str, const int length) { + int i; + for (i = 0; i < length; ++i) { + if (state->out_cur + 1 < state->out_end) { // +1 for '\0' + *state->out_cur = str[i]; + ++state->out_cur; + } else { + state->overflowed = true; + break; + } + } + if (!state->overflowed) { + *state->out_cur = '\0'; // Terminate it with '\0' + } +} + +// We don't use equivalents in libc to avoid locale issues. +static bool IsLower(char c) { + return c >= 'a' && c <= 'z'; +} + +static bool IsAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool IsDigit(char c) { + return c >= '0' && c <= '9'; +} + +// Returns true if "str" is a function clone suffix. These suffixes are used +// by GCC 4.5.x and later versions to indicate functions which have been +// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as +// a function clone suffix. +static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { + // Consume a single .<alpha>+.<digit>+ sequence. + if (str[i] != '.' || !IsAlpha(str[i + 1])) { + return false; + } + i += 2; + while (IsAlpha(str[i])) { + ++i; + } + if (str[i] != '.' || !IsDigit(str[i + 1])) { + return false; + } + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + return true; // Consumed everything in "str". +} + +// Append "str" with some tweaks, iff "append" state is true. +// Returns true so that it can be placed in "if" conditions. +static void MaybeAppendWithLength(State *state, const char * const str, + const int length) { + if (state->append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" + // starts with '<' to avoid <<<. + if (str[0] == '<' && state->out_begin < state->out_cur && + state->out_cur[-1] == '<') { + Append(state, " ", 1); + } + // Remember the last identifier name for ctors/dtors. + if (IsAlpha(str[0]) || str[0] == '_') { + state->prev_name = state->out_cur; + state->prev_name_length = length; + } + Append(state, str, length); + } +} + +// A convenient wrapper arount MaybeAppendWithLength(). +static bool MaybeAppend(State *state, const char * const str) { + if (state->append) { + int length = StrLen(str); + MaybeAppendWithLength(state, str, length); + } + return true; +} + +// This function is used for handling nested names. +static bool EnterNestedName(State *state) { + state->nest_level = 0; + return true; +} + +// This function is used for handling nested names. +static bool LeaveNestedName(State *state, short prev_value) { + state->nest_level = prev_value; + return true; +} + +// Disable the append mode not to print function parameters, etc. +static bool DisableAppend(State *state) { + state->append = false; + return true; +} + +// Restore the append mode to the previous state. +static bool RestoreAppend(State *state, bool prev_value) { + state->append = prev_value; + return true; +} + +// Increase the nest level for nested names. +static void MaybeIncreaseNestLevel(State *state) { + if (state->nest_level > -1) { + ++state->nest_level; + } +} + +// Appends :: for nested names if necessary. +static void MaybeAppendSeparator(State *state) { + if (state->nest_level >= 1) { + MaybeAppend(state, "::"); + } +} + +// Cancel the last separator if necessary. +static void MaybeCancelLastSeparator(State *state) { + if (state->nest_level >= 1 && state->append && + state->out_begin <= state->out_cur - 2) { + state->out_cur -= 2; + *state->out_cur = '\0'; + } +} + +// Returns true if the identifier of the given length pointed to by +// "mangled_cur" is anonymous namespace. +static bool IdentifierIsAnonymousNamespace(State *state, int length) { + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > (int)sizeof(anon_prefix) - 1 && // Should be longer. + StrPrefix(state->mangled_cur, anon_prefix)); +} + +// Forward declarations of our parsing functions. +static bool ParseMangledName(State *state); +static bool ParseEncoding(State *state); +static bool ParseName(State *state); +static bool ParseUnscopedName(State *state); +static bool ParseUnscopedTemplateName(State *state); +static bool ParseNestedName(State *state); +static bool ParsePrefix(State *state); +static bool ParseUnqualifiedName(State *state); +static bool ParseSourceName(State *state); +static bool ParseLocalSourceName(State *state); +static bool ParseNumber(State *state, int *number_out); +static bool ParseFloatNumber(State *state); +static bool ParseSeqId(State *state); +static bool ParseIdentifier(State *state, int length); +static bool ParseOperatorName(State *state); +static bool ParseSpecialName(State *state); +static bool ParseCallOffset(State *state); +static bool ParseNVOffset(State *state); +static bool ParseVOffset(State *state); +static bool ParseCtorDtorName(State *state); +static bool ParseType(State *state); +static bool ParseCVQualifiers(State *state); +static bool ParseBuiltinType(State *state); +static bool ParseFunctionType(State *state); +static bool ParseBareFunctionType(State *state); +static bool ParseClassEnumType(State *state); +static bool ParseArrayType(State *state); +static bool ParsePointerToMemberType(State *state); +static bool ParseTemplateParam(State *state); +static bool ParseTemplateTemplateParam(State *state); +static bool ParseTemplateArgs(State *state); +static bool ParseTemplateArg(State *state); +static bool ParseExpression(State *state); +static bool ParseExprPrimary(State *state); +static bool ParseLocalName(State *state); +static bool ParseDiscriminator(State *state); +static bool ParseSubstitution(State *state); + +// Implementation note: the following code is a straightforward +// translation of the Itanium C++ ABI defined in BNF with a couple of +// exceptions. +// +// - Support GNU extensions not defined in the Itanium C++ ABI +// - <prefix> and <template-prefix> are combined to avoid infinite loop +// - Reorder patterns to shorten the code +// - Reorder patterns to give greedier functions precedence +// We'll mark "Less greedy than" for these cases in the code +// +// Each parsing function changes the state and returns true on +// success. Otherwise, don't change the state and returns false. To +// ensure that the state isn't changed in the latter case, we save the +// original state before we call more than one parsing functions +// consecutively with &&, and restore the state if unsuccessful. See +// ParseEncoding() as an example of this convention. We follow the +// convention throughout the code. +// +// Originally we tried to do demangling without following the full ABI +// syntax but it turned out we needed to follow the full syntax to +// parse complicated cases like nested template arguments. Note that +// implementing a full-fledged demangler isn't trivial (libiberty's +// cp-demangle.c has +4300 lines). +// +// Note that (foo) in <(foo) ...> is a modifier to be ignored. +// +// Reference: +// - Itanium C++ ABI +// <http://www.codesourcery.com/cxx-abi/abi.html#mangling> + +// <mangled-name> ::= _Z <encoding> +static bool ParseMangledName(State *state) { + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); +} + +// <encoding> ::= <(function) name> <bare-function-type> +// ::= <(data) name> +// ::= <special-name> +static bool ParseEncoding(State *state) { + State copy = *state; + if (ParseName(state) && ParseBareFunctionType(state)) { + return true; + } + *state = copy; + + if (ParseName(state) || ParseSpecialName(state)) { + return true; + } + return false; +} + +// <name> ::= <nested-name> +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> +// ::= <local-name> +static bool ParseName(State *state) { + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } + + State copy = *state; + if (ParseUnscopedTemplateName(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + // Less greedy than <unscoped-template-name> <template-args>. + if (ParseUnscopedName(state)) { + return true; + } + return false; +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> +static bool ParseUnscopedName(State *state) { + if (ParseUnqualifiedName(state)) { + return true; + } + + State copy = *state; + if (ParseTwoCharToken(state, "St") && + MaybeAppend(state, "std::") && + ParseUnqualifiedName(state)) { + return true; + } + *state = copy; + return false; +} + +// <unscoped-template-name> ::= <unscoped-name> +// ::= <substitution> +static bool ParseUnscopedTemplateName(State *state) { + return ParseUnscopedName(state) || ParseSubstitution(state); +} + +// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E +// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E +static bool ParseNestedName(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'N') && + EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && + ParsePrefix(state) && + LeaveNestedName(state, copy.nest_level) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// This part is tricky. If we literally translate them to code, we'll +// end up infinite loop. Hence we merge them to avoid the case. +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <substitution> +// ::= # empty +// <template-prefix> ::= <prefix> <(template) unqualified-name> +// ::= <template-param> +// ::= <substitution> +static bool ParsePrefix(State *state) { + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); + if (ParseTemplateParam(state) || + ParseSubstitution(state) || + ParseUnscopedName(state)) { + has_something = true; + MaybeIncreaseNestLevel(state); + continue; + } + MaybeCancelLastSeparator(state); + if (has_something && ParseTemplateArgs(state)) { + return ParsePrefix(state); + } else { + break; + } + } + return true; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <local-source-name> +static bool ParseUnqualifiedName(State *state) { + return (ParseOperatorName(state) || + ParseCtorDtorName(state) || + ParseSourceName(state) || + ParseLocalSourceName(state)); +} + +// <source-name> ::= <positive length number> <identifier> +static bool ParseSourceName(State *state) { + State copy = *state; + int length = -1; + if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + return true; + } + *state = copy; + return false; +} + +// <local-source-name> ::= L <source-name> [<discriminator>] +// +// References: +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +static bool ParseLocalSourceName(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + return false; +} + +// <number> ::= [n] <non-negative decimal integer> +// If "number_out" is non-null, then *number_out is set to the value of the +// parsed number on success. +static bool ParseNumber(State *state, int *number_out) { + int sign = 1; + if (ParseOneCharToken(state, 'n')) { + sign = -1; + } + const char *p = state->mangled_cur; + int number = 0; + for (;*p != '\0'; ++p) { + if (IsDigit(*p)) { + number = number * 10 + (*p - '0'); + } else { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + if (number_out != NULL) { + *number_out = number * sign; + } + return true; + } + return false; +} + +// Floating-point literals are encoded using a fixed-length lowercase +// hexadecimal string. +static bool ParseFloatNumber(State *state) { + const char *p = state->mangled_cur; + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + return true; + } + return false; +} + +// The <seq-id> is a sequence number in base 36, +// using digits and upper case letters +static bool ParseSeqId(State *state) { + const char *p = state->mangled_cur; + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { + break; + } + } + if (p != state->mangled_cur) { // Conversion succeeded. + state->mangled_cur = p; + return true; + } + return false; +} + +// <identifier> ::= <unqualified source code identifier> (of given length) +static bool ParseIdentifier(State *state, int length) { + if (length == -1 || + !AtLeastNumCharsRemaining(state->mangled_cur, length)) { + return false; + } + if (IdentifierIsAnonymousNamespace(state, length)) { + MaybeAppend(state, "(anonymous namespace)"); + } else { + MaybeAppendWithLength(state, state->mangled_cur, length); + } + state->mangled_cur += length; + return true; +} + +// <operator-name> ::= nw, and other two letters cases +// ::= cv <type> # (cast) +// ::= v <digit> <source-name> # vendor extended operator +static bool ParseOperatorName(State *state) { + if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { + return false; + } + // First check with "cv" (cast) case. + State copy = *state; + if (ParseTwoCharToken(state, "cv") && + MaybeAppend(state, "operator ") && + EnterNestedName(state) && + ParseType(state) && + LeaveNestedName(state, copy.nest_level)) { + return true; + } + *state = copy; + + // Then vendor extended operators. + if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && + ParseSourceName(state)) { + return true; + } + *state = copy; + + // Other operator names should start with a lower alphabet followed + // by a lower/upper alphabet. + if (!(IsLower(state->mangled_cur[0]) && + IsAlpha(state->mangled_cur[1]))) { + return false; + } + // We may want to perform a binary search if we really need speed. + const AbbrevPair *p; + for (p = kOperatorList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[0] && + state->mangled_cur[1] == p->abbrev[1]) { + MaybeAppend(state, "operator"); + if (IsLower(*p->real_name)) { // new, delete, etc. + MaybeAppend(state, " "); + } + MaybeAppend(state, p->real_name); + state->mangled_cur += 2; + return true; + } + } + return false; +} + +// <special-name> ::= TV <type> +// ::= TT <type> +// ::= TI <type> +// ::= TS <type> +// ::= Tc <call-offset> <call-offset> <(base) encoding> +// ::= GV <(object) name> +// ::= T <call-offset> <(base) encoding> +// G++ extensions: +// ::= TC <type> <(offset) number> _ <(base) type> +// ::= TF <type> +// ::= TJ <type> +// ::= GR <name> +// ::= GA <encoding> +// ::= Th <call-offset> <(base) encoding> +// ::= Tv <call-offset> <(base) encoding> +// +// Note: we don't care much about them since they don't appear in +// stack traces. The are special data. +static bool ParseSpecialName(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'T') && + ParseCharClass(state, "VTIS") && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "GV") && + ParseName(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && + ParseEncoding(state)) { + return true; + } + *state = copy; + + // G++ extensions + if (ParseTwoCharToken(state, "TC") && ParseType(state) && + ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && + DisableAppend(state) && + ParseType(state)) { + RestoreAppend(state, copy.append); + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + *state = copy; + return false; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +static bool ParseCallOffset(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'h') && + ParseNVOffset(state) && ParseOneCharToken(state, '_')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'v') && + ParseVOffset(state) && ParseOneCharToken(state, '_')) { + return true; + } + *state = copy; + + return false; +} + +// <nv-offset> ::= <(offset) number> +static bool ParseNVOffset(State *state) { + return ParseNumber(state, NULL); +} + +// <v-offset> ::= <(offset) number> _ <(virtual offset) number> +static bool ParseVOffset(State *state) { + State copy = *state; + if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && + ParseNumber(state, NULL)) { + return true; + } + *state = copy; + return false; +} + +// <ctor-dtor-name> ::= C1 | C2 | C3 +// ::= D0 | D1 | D2 +static bool ParseCtorDtorName(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'C') && + ParseCharClass(state, "123")) { + const char * const prev_name = state->prev_name; + const int prev_name_length = state->prev_name_length; + MaybeAppendWithLength(state, prev_name, prev_name_length); + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'D') && + ParseCharClass(state, "012")) { + const char * const prev_name = state->prev_name; + const int prev_name_length = state->prev_name_length; + MaybeAppend(state, "~"); + MaybeAppendWithLength(state, prev_name, prev_name_length); + return true; + } + *state = copy; + return false; +} + +// <type> ::= <CV-qualifiers> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= U <source-name> <type> # vendor extended type qualifier +// ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-template-param> <template-args> +// ::= <template-param> +// ::= <substitution> +// ::= Dp <type> # pack expansion of (C++0x) +// ::= Dt <expression> E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +// +static bool ParseType(State *state) { + // We should check CV-qualifers, and PRGC things first. + State copy = *state; + if (ParseCVQualifiers(state) && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseCharClass(state, "OPRCG") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && + ParseType(state)) { + return true; + } + *state = copy; + + if (ParseBuiltinType(state) || + ParseFunctionType(state) || + ParseClassEnumType(state) || + ParseArrayType(state) || + ParsePointerToMemberType(state) || + ParseSubstitution(state)) { + return true; + } + + if (ParseTemplateTemplateParam(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + // Less greedy than <template-template-param> <template-args>. + if (ParseTemplateParam(state)) { + return true; + } + + return false; +} + +// <CV-qualifiers> ::= [r] [V] [K] +// We don't allow empty <CV-qualifiers> to avoid infinite loop in +// ParseType(). +static bool ParseCVQualifiers(State *state) { + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); + num_cv_qualifiers += ParseOneCharToken(state, 'K'); + return num_cv_qualifiers > 0; +} + +// <builtin-type> ::= v, etc. +// ::= u <source-name> +static bool ParseBuiltinType(State *state) { + const AbbrevPair *p; + for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[0]) { + MaybeAppend(state, p->real_name); + ++state->mangled_cur; + return true; + } + } + + State copy = *state; + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { + return true; + } + *state = copy; + return false; +} + +// <function-type> ::= F [Y] <bare-function-type> E +static bool ParseFunctionType(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && + ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// <bare-function-type> ::= <(signature) type>+ +static bool ParseBareFunctionType(State *state) { + State copy = *state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "()"); + return true; + } + *state = copy; + return false; +} + +// <class-enum-type> ::= <name> +static bool ParseClassEnumType(State *state) { + return ParseName(state); +} + +// <array-type> ::= A <(positive dimension) number> _ <(element) type> +// ::= A [<(dimension) expression>] _ <(element) type> +static bool ParseArrayType(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + *state = copy; + return false; +} + +// <pointer-to-member-type> ::= M <(class) type> <(member) type> +static bool ParsePointerToMemberType(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && + ParseType(state)) { + return true; + } + *state = copy; + return false; +} + +// <template-param> ::= T_ +// ::= T <parameter-2 non-negative number> _ +static bool ParseTemplateParam(State *state) { + if (ParseTwoCharToken(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + + State copy = *state; + if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + *state = copy; + return false; +} + + +// <template-template-param> ::= <template-param> +// ::= <substitution> +static bool ParseTemplateTemplateParam(State *state) { + return (ParseTemplateParam(state) || + ParseSubstitution(state)); +} + +// <template-args> ::= I <template-arg>+ E +static bool ParseTemplateArgs(State *state) { + State copy = *state; + DisableAppend(state); + if (ParseOneCharToken(state, 'I') && + OneOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "<>"); + return true; + } + *state = copy; + return false; +} + +// <template-arg> ::= <type> +// ::= <expr-primary> +// ::= I <template-arg>* E # argument pack +// ::= X <expression> E +static bool ParseTemplateArg(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'I') && + ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseType(state) || + ParseExprPrimary(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + return false; +} + +// <expression> ::= <template-param> +// ::= <expr-primary> +// ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <trinary operator-name> <expression> <expression> +// <expression> +// ::= st <type> +// ::= sr <type> <unqualified-name> <template-args> +// ::= sr <type> <unqualified-name> +static bool ParseExpression(State *state) { + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } + + State copy = *state; + if (ParseOperatorName(state) && + ParseExpression(state) && + ParseExpression(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseOperatorName(state) && + ParseExpression(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseOperatorName(state) && + ParseExpression(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "st") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseType(state) && + ParseUnqualifiedName(state) && + ParseTemplateArgs(state)) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseType(state) && + ParseUnqualifiedName(state)) { + return true; + } + *state = copy; + return false; +} + +// <expr-primary> ::= L <type> <(value) number> E +// ::= L <type> <(value) float> E +// ::= L <mangled-name> E +// // A bug in g++'s C++ ABI version 2 (-fabi-version=2). +// ::= LZ <encoding> E +static bool ParseExprPrimary(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseNumber(state, NULL) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseFloatNumber(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + return false; +} + +// <local-name> := Z <(function) encoding> E <(entity) name> +// [<discriminator>] +// := Z <(function) encoding> E s [<discriminator>] +static bool ParseLocalName(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && + ParseName(state) && Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { + return true; + } + *state = copy; + return false; +} + +// <discriminator> := _ <(non-negative) number> +static bool ParseDiscriminator(State *state) { + State copy = *state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { + return true; + } + *state = copy; + return false; +} + +// <substitution> ::= S_ +// ::= S <seq-id> _ +// ::= St, etc. +static bool ParseSubstitution(State *state) { + if (ParseTwoCharToken(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + + State copy = *state; + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + *state = copy; + + // Expand abbreviations like "St" => "std". + if (ParseOneCharToken(state, 'S')) { + const AbbrevPair *p; + for (p = kSubstitutionList; p->abbrev != NULL; ++p) { + if (state->mangled_cur[0] == p->abbrev[1]) { + MaybeAppend(state, "std"); + if (p->real_name[0] != '\0') { + MaybeAppend(state, "::"); + MaybeAppend(state, p->real_name); + } + ++state->mangled_cur; + return true; + } + } + } + *state = copy; + return false; +} + +// Parse <mangled-name>, optionally followed by either a function-clone suffix +// or version suffix. Returns true only if all of "mangled_cur" was consumed. +static bool ParseTopLevelMangledName(State *state) { + if (ParseMangledName(state)) { + if (state->mangled_cur[0] != '\0') { + // Drop trailing function clone suffix, if any. + if (IsFunctionCloneSuffix(state->mangled_cur)) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (state->mangled_cur[0] == '@') { + MaybeAppend(state, state->mangled_cur); + return true; + } + return false; // Unconsumed suffix. + } + return true; + } + return false; +} + +// The demangler entry point. +bool Demangle(const char *mangled, char *out, int out_size) { + State state; + InitState(&state, mangled, out, out_size); + return ParseTopLevelMangledName(&state) && !state.overflowed; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/demangle.h b/ios/Pods/Flipper-Glog/src/demangle.h new file mode 100644 index 000000000..991b6ffcf --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/demangle.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Satoru Takabayashi +// +// An async-signal-safe and thread-safe demangler for Itanium C++ ABI +// (aka G++ V3 ABI). + +// The demangler is implemented to be used in async signal handlers to +// symbolize stack traces. We cannot use libstdc++'s +// abi::__cxa_demangle() in such signal handlers since it's not async +// signal safe (it uses malloc() internally). +// +// Note that this demangler doesn't support full demangling. More +// specifically, it doesn't print types of function parameters and +// types of template arguments. It just skips them. However, it's +// still very useful to extract basic information such as class, +// function, constructor, destructor, and operator names. +// +// See the implementation note in demangle.cc if you are interested. +// +// Example: +// +// | Mangled Name | The Demangler | abi::__cxa_demangle() +// |---------------|---------------|----------------------- +// | _Z1fv | f() | f() +// | _Z1fi | f() | f(int) +// | _Z3foo3bar | foo() | foo(bar) +// | _Z1fIiEvi | f<>() | void f<int>(int) +// | _ZN1N1fE | N::f | N::f +// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() +// | _Zrm1XS_" | operator%() | operator%(X, X) +// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() +// | _Z1fSs | f() | f(std::basic_string<char, +// | | | std::char_traits<char>, +// | | | std::allocator<char> >) +// +// See the unit test for more examples. +// +// Note: we might want to write demanglers for ABIs other than Itanium +// C++ ABI in the future. +// + +#ifndef BASE_DEMANGLE_H_ +#define BASE_DEMANGLE_H_ + +#include "config.h" +#include "glog/logging.h" + +_START_GOOGLE_NAMESPACE_ + +// Demangle "mangled". On success, return true and write the +// demangled symbol name to "out". Otherwise, return false. +// "out" is modified even if demangling is unsuccessful. +bool GOOGLE_GLOG_DLL_DECL Demangle(const char *mangled, char *out, int out_size); + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_DEMANGLE_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/log_severity.h b/ios/Pods/Flipper-Glog/src/glog/log_severity.h new file mode 100644 index 000000000..99945a426 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/log_severity.h @@ -0,0 +1,92 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BASE_LOG_SEVERITY_H__ +#define BASE_LOG_SEVERITY_H__ + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// Variables of type LogSeverity are widely taken to lie in the range +// [0, NUM_SEVERITIES-1]. Be careful to preserve this assumption if +// you ever need to change their values or add a new severity. +typedef int LogSeverity; + +const int GLOG_INFO = 0, GLOG_WARNING = 1, GLOG_ERROR = 2, GLOG_FATAL = 3, + NUM_SEVERITIES = 4; +#ifndef GLOG_NO_ABBREVIATED_SEVERITIES +# ifdef ERROR +# error ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h. See the document for detail. +# endif +const int INFO = GLOG_INFO, WARNING = GLOG_WARNING, + ERROR = GLOG_ERROR, FATAL = GLOG_FATAL; +#endif + +// DFATAL is FATAL in debug mode, ERROR in normal mode +#ifdef NDEBUG +#define DFATAL_LEVEL ERROR +#else +#define DFATAL_LEVEL FATAL +#endif + +extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES]; + +// NDEBUG usage helpers related to (RAW_)DCHECK: +// +// DEBUG_MODE is for small !NDEBUG uses like +// if (DEBUG_MODE) foo.CheckThatFoo(); +// instead of substantially more verbose +// #ifndef NDEBUG +// foo.CheckThatFoo(); +// #endif +// +// IF_DEBUG_MODE is for small !NDEBUG uses like +// IF_DEBUG_MODE( string error; ) +// DCHECK(Foo(&error)) << error; +// instead of substantially more verbose +// #ifndef NDEBUG +// string error; +// DCHECK(Foo(&error)) << error; +// #endif +// +#ifdef NDEBUG +enum { DEBUG_MODE = 0 }; +#define IF_DEBUG_MODE(x) +#else +enum { DEBUG_MODE = 1 }; +#define IF_DEBUG_MODE(x) x +#endif + +#endif // BASE_LOG_SEVERITY_H__ diff --git a/ios/Pods/Flipper-Glog/src/glog/logging.h b/ios/Pods/Flipper-Glog/src/glog/logging.h new file mode 100644 index 000000000..5abdacdb2 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/logging.h @@ -0,0 +1,1661 @@ +// Copyright (c) 1999, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney +// +// This file contains #include information about logging-related stuff. +// Pretty much everybody needs to #include this file so that they can +// log various happenings. +// +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include <errno.h> +#include <string.h> +#include <time.h> +#include <iosfwd> +#include <ostream> +#include <sstream> +#include <string> +#if 1 +# include <unistd.h> +#endif +#include <vector> + +#if defined(_MSC_VER) +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) +#define GLOG_MSVC_POP_WARNING() __pragma(warning(pop)) +#else +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) +#define GLOG_MSVC_POP_WARNING() +#endif + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at logging.h.in to see how they're calculated (based on your config). +#if 1 +#include <stdint.h> // the normal place uint16_t is defined +#endif +#if 1 +#include <sys/types.h> // the normal place u_int16_t is defined +#endif +#if 1 +#include <inttypes.h> // a third place for uint16_t or u_int16_t +#endif + +#if 0 +#include <gflags/gflags.h> +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +} + +// The global value of GOOGLE_STRIP_LOG. All the messages logged to +// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. +// If it can be determined at compile time that the message will not be +// printed, the statement will be compiled out. +// +// Example: to strip out all INFO and WARNING messages, use the value +// of 2 below. To make an exception for WARNING messages from a single +// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including +// base/logging.h +#ifndef GOOGLE_STRIP_LOG +#define GOOGLE_STRIP_LOG 0 +#endif + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +// +#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN +#if 1 +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_FALSE +#if 1 +#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_FALSE(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_TRUE +#if 1 +#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define GOOGLE_PREDICT_TRUE(x) x +#endif +#endif + + +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(<a particular severity level>). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector<string> errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a NULL pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special google::COUNTER value is used +// to identify which repetition is happening. +// +// You can also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurence is " << google::COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurence is " << google::COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// Note that messages of a given severity are logged not only in the +// logfile for that severity, but also in all logfiles of lower severity. +// E.g., a message of severity FATAL will be logged to the logfiles of +// severity FATAL, ERROR, WARNING, and INFO. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "<program name>.<hostname>.<user name>.log.<severity level>.", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// LOG LINE PREFIX FORMAT +// +// Log lines have this form: +// +// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +// +// where the fields are defined as follows: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// mm The month (zero padded; ie May is '05') +// dd The day (zero padded) +// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds +// threadid The space-padded thread ID as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog +// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 +// +// NOTE: although the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +#ifndef DECLARE_VARIABLE +#define MUST_UNDEF_GFLAGS_DECLARE_MACROS +#define DECLARE_VARIABLE(type, shorttype, name, tn) \ + namespace fL##shorttype { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, B, name, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(google::int32, I, name, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace fLS { \ + extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name; \ + } \ + using fLS::FLAGS_##name +#endif + +// Set whether log messages go to stderr instead of logfiles +DECLARE_bool(logtostderr); + +// Set whether log messages go to stderr in addition to logfiles. +DECLARE_bool(alsologtostderr); + +// Set color messages logged to stderr (if supported by terminal). +DECLARE_bool(colorlogtostderr); + +// Log messages at a level >= this flag are automatically sent to +// stderr in addition to log files. +DECLARE_int32(stderrthreshold); + +// Set whether the log prefix should be prepended to each line of output. +DECLARE_bool(log_prefix); + +// Log messages at a level <= this flag are buffered. +// Log messages at a higher level are flushed immediately. +DECLARE_int32(logbuflevel); + +// Sets the maximum number of seconds which logs may be buffered for. +DECLARE_int32(logbufsecs); + +// Log suppression level: messages logged at a lower level than this +// are suppressed. +DECLARE_int32(minloglevel); + +// If specified, logfiles are written into this directory instead of the +// default logging directory. +DECLARE_string(log_dir); + +// Set the log file mode. +DECLARE_int32(logfile_mode); + +// Sets the path of the directory into which to put additional links +// to the log files. +DECLARE_string(log_link); + +DECLARE_int32(v); // in vlog_is_on.cc + +// Sets the maximum log file size (in MB). +DECLARE_int32(max_log_size); + +// Sets whether to avoid logging to the disk if the disk is full. +DECLARE_bool(stop_logging_if_full_disk); + +#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef DECLARE_VARIABLE +#undef DECLARE_bool +#undef DECLARE_int32 +#undef DECLARE_string +#endif + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severtiy) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_INFO(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_INFO, message) +#else +#define COMPACT_GOOGLE_LOG_INFO google::NullStream() +#define LOG_TO_STRING_INFO(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_WARNING) +#define LOG_TO_STRING_WARNING(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_WARNING, message) +#else +#define COMPACT_GOOGLE_LOG_WARNING google::NullStream() +#define LOG_TO_STRING_WARNING(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ERROR) +#define LOG_TO_STRING_ERROR(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ERROR, message) +#else +#define COMPACT_GOOGLE_LOG_ERROR google::NullStream() +#define LOG_TO_STRING_ERROR(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_FATAL(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_FATAL, message) +#else +#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() +#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() +#endif + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON() 0 +#else +#define DCHECK_IS_ON() 1 +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#if !DCHECK_IS_ON() +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_DFATAL google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_FATAL) +#else +#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, &google::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +#define LOG_SYSRESULT(result) \ + if (FAILED(HRESULT_FROM_WIN32(result))) { \ + LPSTR message = NULL; \ + LPSTR msg = reinterpret_cast<LPSTR>(&message); \ + DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM, \ + 0, result, 0, msg, 100, NULL); \ + if (message_length > 0) { \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, 0, \ + &google::LogMessage::SendToLog).stream() \ + << reinterpret_cast<const char*>(message); \ + LocalFree(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() + +namespace google { + +// They need the definitions of integer types. +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); + +// Shutdown google's logging library. +GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging(); + +// Install a function which will be called after LOG(FATAL). +GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); + +class LogSink; // defined below + +// If a non-NULL sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +#define LOG_TO_SINK(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::GLOG_ ## severity, \ + static_cast<google::LogSink*>(sink), true).stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::GLOG_ ## severity, \ + static_cast<google::LogSink*>(sink), false).stream() + +// If a non-NULL string pointer is given, we write this message to that string. +// We then do normal LOG(severity) logging as well. +// This is useful for capturing messages and storing them somewhere more +// specific than the global log of the process. +// Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast<string*>(message)).stream() + +// If a non-NULL pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector<string> *outvec; +// The cast is to disambiguate NULL arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream() + +#define LOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by DCHECK_IS_ON(), so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); + } + std::string* str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template <class T> +inline const T& GetReferenceableValue(const T& t) { return t; } +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +} + +// Define global operator<< to declare using ::operator<<. +// This declaration will allow use to use CHECK macros for user +// defined classes which have operator<< (e.g., stl_logging.h). +inline std::ostream& operator<<( + std::ostream& out, const google::DummyClassToDefineOperator&) { + return out; +} + +namespace google { + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template <typename T> +inline void MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Overrides for char types provide readable values for unprintable +// characters. +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const signed char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v); + +// Build the error message string. Specify no inlining for code size. +template <typename T1, typename T2> +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) + __attribute__ ((noinline)); + +namespace base { +namespace internal { + +// If "s" is less than base_logging::INFO, returns base_logging::INFO. +// If "s" is greater than base_logging::FATAL, returns +// base_logging::ERROR. Otherwise, returns "s". +LogSeverity NormalizeSeverity(LogSeverity s); + +} // namespace internal + +// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX +// statement. See MakeCheckOpString for sample usage. Other +// approaches were considered: use of a template method (e.g., +// base::BuildCheckOpString(exprtext, base::Print<T1>, &v1, +// base::Print<T2>, &v2), however this approach has complications +// related to volatile arguments and function-pointer arguments). +class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder { + public: + // Inserts "exprtext" and " (" to the stream. + explicit CheckOpMessageBuilder(const char *exprtext); + // Deletes "stream_". + ~CheckOpMessageBuilder(); + // For inserting the first variable. + std::ostream* ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate " vs. "). + std::ostream* ForVar2(); + // Get the result (inserts the closing ")"). + std::string* NewString(); + + private: + std::ostringstream *stream_; +}; + +} // namespace base + +template <typename T1, typename T2> +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) { + base::CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template <typename T1, typename T2> \ + inline std::string* name##Impl(const T1& v1, const T2& v2, \ + const char* exprtext) { \ + if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \ + else return MakeCheckOpString(v1, v2, exprtext); \ + } \ + inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \ + return name##Impl<int, int>(v1, v2, exprtext); \ + } + +// We use the full name Check_EQ, Check_NE, etc. in case the file including +// base/logging.h provides its own #defines for the simpler names EQ, NE, etc. +// This happens if, for example, those are used as token names in a +// yacc grammar. +DEFINE_CHECK_OP_IMPL(Check_EQ, ==) // Compilation error with CHECK_EQ(NULL, x)? +DEFINE_CHECK_OP_IMPL(Check_NE, !=) // Use CHECK(x == NULL) instead. +DEFINE_CHECK_OP_IMPL(Check_LE, <=) +DEFINE_CHECK_OP_IMPL(Check_LT, < ) +DEFINE_CHECK_OP_IMPL(Check_GE, >=) +DEFINE_CHECK_OP_IMPL(Check_GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) +#elif DCHECK_IS_ON() +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statments by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// with other string implementations that get defined after this +// file is included). Save the current meaning now and use it +// in the macro. +typedef std::string _Check_string; +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::_Check_string* _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, \ + google::CheckOpString(_result)).stream() +#else +// In optimized mode, use CheckOpString to hint to compiler that +// the while condition is unlikely. +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::CheckOpString _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, DCHECK_IS_ON() + +#if GOOGLE_STRIP_LOG <= 3 +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) +#else +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// CHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) + +// Check that the input is non NULL. This very useful in constructor +// initializer lists. + +#define CHECK_NOTNULL(val) \ + google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_CHECK_STROP_IMPL(func, expected) \ + GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ + const char* s1, const char* s2, const char* names); +DECLARE_CHECK_STROP_IMPL(strcmp, true) +DECLARE_CHECK_STROP_IMPL(strcmp, false) +DECLARE_CHECK_STROP_IMPL(strcasecmp, true) +DECLARE_CHECK_STROP_IMPL(strcasecmp, false) +#undef DECLARE_CHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define CHECK_STROP(func, op, expected, s1, s2) \ + while (google::CheckOpString _result = \ + google::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) +#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2)+0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2)+(margin)); \ + CHECK_GE((val1), (val2)-(margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, counter, \ + &google::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ +PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (condition && \ + ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0; \ + if (LOG_OCCURRENCES <= n) \ + ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +namespace glog_internal_namespace_ { +template <bool> +struct CompileAssert { +}; +struct CrashReason; + +// Returns true if FailureSignalHandler is installed. +bool IsFailureSignalHandlerInstalled(); +} // namespace glog_internal_namespace_ + +#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \ + typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +#define LOG_EVERY_N(severity, n) \ + GOOGLE_GLOG_COMPILE_ASSERT(google::GLOG_ ## severity < \ + google::NUM_SEVERITIES, \ + INVALID_REQUESTED_LOG_SEVERITY); \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +enum PRIVATE_Counter {COUNTER}; + +#ifdef GLOG_NO_ABBREVIATED_SEVERITIES +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR. +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +#define SYSLOG_0 SYSLOG_ERROR +#define LOG_TO_STRING_0 LOG_TO_STRING_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity GLOG_0 = GLOG_ERROR; +#else +// Users may include windows.h after logging.h without +// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN. +// For this case, we cannot detect if ERROR is defined before users +// actually use ERROR. Let's make an undefined symbol to warn users. +# define GLOG_ERROR_MSG ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail +# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG +# define SYSLOG_0 GLOG_ERROR_MSG +# define LOG_TO_STRING_0 GLOG_ERROR_MSG +# define GLOG_0 GLOG_ERROR_MSG +#endif + +// Plus some debug-logging macros that get compiled to nothing for production + +#if DCHECK_IS_ON() + +#define DLOG(severity) LOG(severity) +#define DVLOG(verboselevel) VLOG(verboselevel) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +#define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. executed if DCHECK_IS_ON(). +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +#define DCHECK_NOTNULL(val) CHECK_NOTNULL(val) +#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // !DCHECK_IS_ON() + +#define DLOG(severity) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DVLOG(verboselevel) \ + (true || !VLOG_IS_ON(verboselevel)) ?\ + (void) 0 : google::LogMessageVoidify() & LOG(INFO) + +#define DLOG_IF(severity, condition) \ + (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_EVERY_N(severity, n) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF_EVERY_N(severity, condition, n) \ + (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +// MSVC warning C4127: conditional expression is constant +#define DCHECK(condition) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK(condition) + +#define DCHECK_EQ(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2) + +#define DCHECK_NE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2) + +#define DCHECK_LE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2) + +#define DCHECK_LT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2) + +#define DCHECK_GE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2) + +#define DCHECK_GT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2) + +// You may see warnings in release mode if you don't use the return +// value of DCHECK_NOTNULL. Please just use DCHECK for such cases. +#define DCHECK_NOTNULL(val) (val) + +#define DCHECK_STREQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2) + +#define DCHECK_STRCASEEQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2) + +#define DCHECK_STRNE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2) + +#define DCHECK_STRCASENE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2) + +#endif // DCHECK_IS_ON() + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +namespace base_logging { + +// LogMessage::LogStream is a std::ostream backed by this streambuf. +// This class ignores overflow and leaves two bytes at the end of the +// buffer to allow for a '\n' and '\0'. +class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf { + public: + // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\n'. + LogStreamBuf(char *buf, int len) { + setp(buf, buf + len - 2); + } + // This effectively ignores overflow. + virtual int_type overflow(int_type ch) { + return ch; + } + + // Legacy public ostrstream method. + size_t pcount() const { return pptr() - pbase(); } + char* pbase() const { return std::streambuf::pbase(); } +}; + +} // namespace base_logging + +// +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GOOGLE_GLOG_DLL_DECL LogMessage { +public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. +#ifdef _MSC_VER +# pragma warning(disable: 4275) +#endif + class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream { +#ifdef _MSC_VER +# pragma warning(default: 4275) +#endif + public: + LogStream(char *buf, int len, int ctr) + : std::ostream(NULL), + streambuf_(buf, len), + ctr_(ctr), + self_(this) { + rdbuf(&streambuf_); + } + + int ctr() const { return ctr_; } + void set_ctr(int ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + // Legacy std::streambuf methods. + size_t pcount() const { return streambuf_.pcount(); } + char* pbase() const { return streambuf_.pbase(); } + char* str() const { return pbase(); } + + private: + LogStream(const LogStream&); + LogStream& operator=(const LogStream&); + base_logging::LogStreamBuf streambuf_; + int ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream *self_; // Consistency check hack + }; + +public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector<string> pointer + // for storing the messages (if the pointer is not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector<std::string>* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not NULL). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, const CheckOpString& result); + + ~LogMessage(); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // Theses should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + static void __attribute__ ((noreturn)) Fail(); + + std::ostream& stream(); + + int preserved_errno() const; + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + + struct LogMessageData; + +private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); + + // Counts of messages sent at each priority: + static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex + + // We keep the data in a separate struct so that each instance of + // LogMessage uses less stack space. + LogMessageData* allocated_; + LogMessageData* data_; + + friend class LogDestination; + + LogMessage(const LogMessage&); + void operator=(const LogMessage&); +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line); + LogMessageFatal(const char* file, int line, const CheckOpString& result); + __attribute__ ((noreturn)) ~LogMessageFatal(); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const severity, std::string const &msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) google::LogMessage(__FILE__, __LINE__, severity).stream() + +// Check if it's compiled in C++11 mode. +// +// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least +// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 +// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is +// defined according to the language version in effect thereafter. +// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite +// reasonably good C++11 support, so we set LANG_CXX for it and +// newer versions (_MSC_VER >= 1900). +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1900)) +// Helper for CHECK_NOTNULL(). +// +// In C++11, all cases can be handled by a single function. Since the value +// category of the argument is preserved (also for rvalue references), +// member initializer lists like the one below will compile correctly: +// +// Foo() +// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {} +template <typename T> +T CheckNotNull(const char* file, int line, const char* names, T&& t) { + if (t == nullptr) { + LogMessageFatal(file, line, new std::string(names)); + } + return std::forward<T>(t); +} + +#else + +// A small helper for CHECK_NOTNULL(). +template <typename T> +T* CheckNotNull(const char *file, int line, const char *names, T* t) { + if (t == NULL) { + LogMessageFatal(file, line, new std::string(names)); + } + return t; +} +#endif + +// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This +// only works if ostream is a LogStream. If the ostream is not a +// LogStream you'll get an assert saying as much at runtime. +GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, + const PRIVATE_Counter&); + + +// Derived class for PLOG*() above. +class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { + public: + + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-safe. +GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GOOGLE_GLOG_DLL_DECL LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) = 0; + + // Redefine this to implement waiting for + // the sink's logging logic to complete. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // Returns the normal text output of the log message. + // Can be useful to implement send(). + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); +GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( + const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a commma-separated +// list of addressess. Thread-safe. +GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, + const char *subject, const char *body); + +GOOGLE_GLOG_DLL_DECL const std::vector<std::string>& GetLoggingDirectories(); + +// For tests only: Clear the internal [cached] list of logging directories to +// force a refresh the next time GetLoggingDirectories is called. +// Thread-hostile. +void TestOnly_ClearLoggingDirectoriesList(); + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLogginDirectories(). +// Thread-safe. +GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( + std::vector<std::string>* list); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, + int64 limit, int64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GOOGLE_GLOG_DLL_DECL Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // appropriate by the higher level logging facility. For example, + // textual log messages already contain timestamps, and the + // file:linenumber header. + virtual void Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) = 0; + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); + +} + +// glibc has traditionally implemented two incompatible versions of +// strerror_r(). There is a poorly defined convention for picking the +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +// DEPRECATED: Use StrError(int) instead. +GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); + +// A thread-safe replacement for strerror(). Returns a string describing the +// given POSIX error code. +GOOGLE_GLOG_DLL_DECL std::string StrError(int err); + +// A class for which we define operator<<, which does nothing. +class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream(const char* /*file*/, int /*line*/, + const CheckOpString& /*result*/) : + LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream &stream() { return *this; } + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[2]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template<class T> +inline NullStream& operator<<(NullStream &str, const T &) { return str; } + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { + public: + NullStreamFatal() { } + NullStreamFatal(const char* file, int line, const CheckOpString& result) : + NullStream(file, line, result) { } + __attribute__ ((noreturn)) ~NullStreamFatal() throw () { _exit(1); } +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( + void (*writer)(const char* data, int size)); + +} + +#endif // _LOGGING_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/logging.h.in b/ios/Pods/Flipper-Glog/src/glog/logging.h.in new file mode 100644 index 000000000..d6cc865ab --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/logging.h.in @@ -0,0 +1,1661 @@ +// Copyright (c) 1999, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney +// +// This file contains #include information about logging-related stuff. +// Pretty much everybody needs to #include this file so that they can +// log various happenings. +// +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include <errno.h> +#include <string.h> +#include <time.h> +#include <iosfwd> +#include <ostream> +#include <sstream> +#include <string> +#if @ac_cv_have_unistd_h@ +# include <unistd.h> +#endif +#include <vector> + +#if defined(_MSC_VER) +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) +#define GLOG_MSVC_POP_WARNING() __pragma(warning(pop)) +#else +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) +#define GLOG_MSVC_POP_WARNING() +#endif + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at logging.h.in to see how they're calculated (based on your config). +#if @ac_cv_have_stdint_h@ +#include <stdint.h> // the normal place uint16_t is defined +#endif +#if @ac_cv_have_systypes_h@ +#include <sys/types.h> // the normal place u_int16_t is defined +#endif +#if @ac_cv_have_inttypes_h@ +#include <inttypes.h> // a third place for uint16_t or u_int16_t +#endif + +#if @ac_cv_have_libgflags@ +#include <gflags/gflags.h> +#endif + +@ac_google_start_namespace@ + +#if @ac_cv_have_uint16_t@ // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif @ac_cv_have_u_int16_t@ // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif @ac_cv_have___uint16@ // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +@ac_google_end_namespace@ + +// The global value of GOOGLE_STRIP_LOG. All the messages logged to +// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. +// If it can be determined at compile time that the message will not be +// printed, the statement will be compiled out. +// +// Example: to strip out all INFO and WARNING messages, use the value +// of 2 below. To make an exception for WARNING messages from a single +// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including +// base/logging.h +#ifndef GOOGLE_STRIP_LOG +#define GOOGLE_STRIP_LOG 0 +#endif + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +// +#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN +#if @ac_cv_have___builtin_expect@ +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_FALSE +#if @ac_cv_have___builtin_expect@ +#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_FALSE(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_TRUE +#if @ac_cv_have___builtin_expect@ +#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define GOOGLE_PREDICT_TRUE(x) x +#endif +#endif + + +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(<a particular severity level>). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector<string> errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a NULL pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special google::COUNTER value is used +// to identify which repetition is happening. +// +// You can also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurence is " << google::COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurence is " << google::COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// Note that messages of a given severity are logged not only in the +// logfile for that severity, but also in all logfiles of lower severity. +// E.g., a message of severity FATAL will be logged to the logfiles of +// severity FATAL, ERROR, WARNING, and INFO. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "<program name>.<hostname>.<user name>.log.<severity level>.", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// LOG LINE PREFIX FORMAT +// +// Log lines have this form: +// +// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +// +// where the fields are defined as follows: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// mm The month (zero padded; ie May is '05') +// dd The day (zero padded) +// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds +// threadid The space-padded thread ID as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog +// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 +// +// NOTE: although the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +#ifndef DECLARE_VARIABLE +#define MUST_UNDEF_GFLAGS_DECLARE_MACROS +#define DECLARE_VARIABLE(type, shorttype, name, tn) \ + namespace fL##shorttype { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, B, name, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(@ac_google_namespace@::int32, I, name, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace fLS { \ + extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name; \ + } \ + using fLS::FLAGS_##name +#endif + +// Set whether log messages go to stderr instead of logfiles +DECLARE_bool(logtostderr); + +// Set whether log messages go to stderr in addition to logfiles. +DECLARE_bool(alsologtostderr); + +// Set color messages logged to stderr (if supported by terminal). +DECLARE_bool(colorlogtostderr); + +// Log messages at a level >= this flag are automatically sent to +// stderr in addition to log files. +DECLARE_int32(stderrthreshold); + +// Set whether the log prefix should be prepended to each line of output. +DECLARE_bool(log_prefix); + +// Log messages at a level <= this flag are buffered. +// Log messages at a higher level are flushed immediately. +DECLARE_int32(logbuflevel); + +// Sets the maximum number of seconds which logs may be buffered for. +DECLARE_int32(logbufsecs); + +// Log suppression level: messages logged at a lower level than this +// are suppressed. +DECLARE_int32(minloglevel); + +// If specified, logfiles are written into this directory instead of the +// default logging directory. +DECLARE_string(log_dir); + +// Set the log file mode. +DECLARE_int32(logfile_mode); + +// Sets the path of the directory into which to put additional links +// to the log files. +DECLARE_string(log_link); + +DECLARE_int32(v); // in vlog_is_on.cc + +// Sets the maximum log file size (in MB). +DECLARE_int32(max_log_size); + +// Sets whether to avoid logging to the disk if the disk is full. +DECLARE_bool(stop_logging_if_full_disk); + +#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef DECLARE_VARIABLE +#undef DECLARE_bool +#undef DECLARE_int32 +#undef DECLARE_string +#endif + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severtiy) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_INFO(message) @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, message) +#else +#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::NullStream() +#define LOG_TO_STRING_INFO(message) @ac_google_namespace@::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +#define COMPACT_GOOGLE_LOG_WARNING @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING) +#define LOG_TO_STRING_WARNING(message) @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, message) +#else +#define COMPACT_GOOGLE_LOG_WARNING @ac_google_namespace@::NullStream() +#define LOG_TO_STRING_WARNING(message) @ac_google_namespace@::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +#define COMPACT_GOOGLE_LOG_ERROR @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR) +#define LOG_TO_STRING_ERROR(message) @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, message) +#else +#define COMPACT_GOOGLE_LOG_ERROR @ac_google_namespace@::NullStream() +#define LOG_TO_STRING_ERROR(message) @ac_google_namespace@::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_FATAL @ac_google_namespace@::LogMessageFatal( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_FATAL(message) @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, message) +#else +#define COMPACT_GOOGLE_LOG_FATAL @ac_google_namespace@::NullStreamFatal() +#define LOG_TO_STRING_FATAL(message) @ac_google_namespace@::NullStreamFatal() +#endif + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON() 0 +#else +#define DCHECK_IS_ON() 1 +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#if !DCHECK_IS_ON() +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_DFATAL @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL) +#else +#define COMPACT_GOOGLE_LOG_DFATAL @ac_google_namespace@::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, counter, &@ac_google_namespace@::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, counter, \ + &@ac_google_namespace@::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, counter, \ + &@ac_google_namespace@::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, counter, \ + &@ac_google_namespace@::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, counter, \ + &@ac_google_namespace@::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, counter, \ + &@ac_google_namespace@::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, counter, \ + &@ac_google_namespace@::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, counter, \ + &@ac_google_namespace@::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::DFATAL_LEVEL, counter, \ + &@ac_google_namespace@::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::DFATAL_LEVEL, counter, \ + &@ac_google_namespace@::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +#define LOG_SYSRESULT(result) \ + if (FAILED(HRESULT_FROM_WIN32(result))) { \ + LPSTR message = NULL; \ + LPSTR msg = reinterpret_cast<LPSTR>(&message); \ + DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM, \ + 0, result, 0, msg, 100, NULL); \ + if (message_length > 0) { \ + @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, 0, \ + &@ac_google_namespace@::LogMessage::SendToLog).stream() \ + << reinterpret_cast<const char*>(message); \ + LocalFree(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() + +@ac_google_start_namespace@ + +// They need the definitions of integer types. +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); + +// Shutdown google's logging library. +GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging(); + +// Install a function which will be called after LOG(FATAL). +GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); + +class LogSink; // defined below + +// If a non-NULL sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +#define LOG_TO_SINK(sink, severity) \ + @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, \ + @ac_google_namespace@::GLOG_ ## severity, \ + static_cast<@ac_google_namespace@::LogSink*>(sink), true).stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, \ + @ac_google_namespace@::GLOG_ ## severity, \ + static_cast<@ac_google_namespace@::LogSink*>(sink), false).stream() + +// If a non-NULL string pointer is given, we write this message to that string. +// We then do normal LOG(severity) logging as well. +// This is useful for capturing messages and storing them somewhere more +// specific than the global log of the process. +// Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast<string*>(message)).stream() + +// If a non-NULL pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector<string> *outvec; +// The cast is to disambiguate NULL arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream() + +#define LOG_IF(severity, condition) \ + !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by DCHECK_IS_ON(), so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); + } + std::string* str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template <class T> +inline const T& GetReferenceableValue(const T& t) { return t; } +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +@ac_google_end_namespace@ + +// Define global operator<< to declare using ::operator<<. +// This declaration will allow use to use CHECK macros for user +// defined classes which have operator<< (e.g., stl_logging.h). +inline std::ostream& operator<<( + std::ostream& out, const google::DummyClassToDefineOperator&) { + return out; +} + +@ac_google_start_namespace@ + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template <typename T> +inline void MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Overrides for char types provide readable values for unprintable +// characters. +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const signed char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v); + +// Build the error message string. Specify no inlining for code size. +template <typename T1, typename T2> +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) + @ac_cv___attribute___noinline@; + +namespace base { +namespace internal { + +// If "s" is less than base_logging::INFO, returns base_logging::INFO. +// If "s" is greater than base_logging::FATAL, returns +// base_logging::ERROR. Otherwise, returns "s". +LogSeverity NormalizeSeverity(LogSeverity s); + +} // namespace internal + +// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX +// statement. See MakeCheckOpString for sample usage. Other +// approaches were considered: use of a template method (e.g., +// base::BuildCheckOpString(exprtext, base::Print<T1>, &v1, +// base::Print<T2>, &v2), however this approach has complications +// related to volatile arguments and function-pointer arguments). +class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder { + public: + // Inserts "exprtext" and " (" to the stream. + explicit CheckOpMessageBuilder(const char *exprtext); + // Deletes "stream_". + ~CheckOpMessageBuilder(); + // For inserting the first variable. + std::ostream* ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate " vs. "). + std::ostream* ForVar2(); + // Get the result (inserts the closing ")"). + std::string* NewString(); + + private: + std::ostringstream *stream_; +}; + +} // namespace base + +template <typename T1, typename T2> +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) { + base::CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template <typename T1, typename T2> \ + inline std::string* name##Impl(const T1& v1, const T2& v2, \ + const char* exprtext) { \ + if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \ + else return MakeCheckOpString(v1, v2, exprtext); \ + } \ + inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \ + return name##Impl<int, int>(v1, v2, exprtext); \ + } + +// We use the full name Check_EQ, Check_NE, etc. in case the file including +// base/logging.h provides its own #defines for the simpler names EQ, NE, etc. +// This happens if, for example, those are used as token names in a +// yacc grammar. +DEFINE_CHECK_OP_IMPL(Check_EQ, ==) // Compilation error with CHECK_EQ(NULL, x)? +DEFINE_CHECK_OP_IMPL(Check_NE, !=) // Use CHECK(x == NULL) instead. +DEFINE_CHECK_OP_IMPL(Check_LE, <=) +DEFINE_CHECK_OP_IMPL(Check_LT, < ) +DEFINE_CHECK_OP_IMPL(Check_GE, >=) +DEFINE_CHECK_OP_IMPL(Check_GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) +#elif DCHECK_IS_ON() +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statments by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// with other string implementations that get defined after this +// file is included). Save the current meaning now and use it +// in the macro. +typedef std::string _Check_string; +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (@ac_google_namespace@::_Check_string* _result = \ + @ac_google_namespace@::Check##name##Impl( \ + @ac_google_namespace@::GetReferenceableValue(val1), \ + @ac_google_namespace@::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, \ + @ac_google_namespace@::CheckOpString(_result)).stream() +#else +// In optimized mode, use CheckOpString to hint to compiler that +// the while condition is unlikely. +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (@ac_google_namespace@::CheckOpString _result = \ + @ac_google_namespace@::Check##name##Impl( \ + @ac_google_namespace@::GetReferenceableValue(val1), \ + @ac_google_namespace@::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, DCHECK_IS_ON() + +#if GOOGLE_STRIP_LOG <= 3 +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, @ac_google_namespace@::LogMessageFatal) +#else +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, @ac_google_namespace@::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// CHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) + +// Check that the input is non NULL. This very useful in constructor +// initializer lists. + +#define CHECK_NOTNULL(val) \ + @ac_google_namespace@::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_CHECK_STROP_IMPL(func, expected) \ + GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ + const char* s1, const char* s2, const char* names); +DECLARE_CHECK_STROP_IMPL(strcmp, true) +DECLARE_CHECK_STROP_IMPL(strcmp, false) +DECLARE_CHECK_STROP_IMPL(strcasecmp, true) +DECLARE_CHECK_STROP_IMPL(strcasecmp, false) +#undef DECLARE_CHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define CHECK_STROP(func, op, expected, s1, s2) \ + while (@ac_google_namespace@::CheckOpString _result = \ + @ac_google_namespace@::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) +#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2)+0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2)+(margin)); \ + CHECK_GE((val1), (val2)-(margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + @ac_google_namespace@::ErrnoLogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, counter, \ + &@ac_google_namespace@::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ +PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (condition && \ + ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + @ac_google_namespace@::ErrnoLogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0; \ + if (LOG_OCCURRENCES <= n) \ + ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + @ac_google_namespace@::LogMessage( \ + __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +namespace glog_internal_namespace_ { +template <bool> +struct CompileAssert { +}; +struct CrashReason; + +// Returns true if FailureSignalHandler is installed. +bool IsFailureSignalHandlerInstalled(); +} // namespace glog_internal_namespace_ + +#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \ + typedef @ac_google_namespace@::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +#define LOG_EVERY_N(severity, n) \ + GOOGLE_GLOG_COMPILE_ASSERT(@ac_google_namespace@::GLOG_ ## severity < \ + @ac_google_namespace@::NUM_SEVERITIES, \ + INVALID_REQUESTED_LOG_SEVERITY); \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), @ac_google_namespace@::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +enum PRIVATE_Counter {COUNTER}; + +#ifdef GLOG_NO_ABBREVIATED_SEVERITIES +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR. +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +#define SYSLOG_0 SYSLOG_ERROR +#define LOG_TO_STRING_0 LOG_TO_STRING_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity GLOG_0 = GLOG_ERROR; +#else +// Users may include windows.h after logging.h without +// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN. +// For this case, we cannot detect if ERROR is defined before users +// actually use ERROR. Let's make an undefined symbol to warn users. +# define GLOG_ERROR_MSG ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail +# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG +# define SYSLOG_0 GLOG_ERROR_MSG +# define LOG_TO_STRING_0 GLOG_ERROR_MSG +# define GLOG_0 GLOG_ERROR_MSG +#endif + +// Plus some debug-logging macros that get compiled to nothing for production + +#if DCHECK_IS_ON() + +#define DLOG(severity) LOG(severity) +#define DVLOG(verboselevel) VLOG(verboselevel) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +#define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. executed if DCHECK_IS_ON(). +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +#define DCHECK_NOTNULL(val) CHECK_NOTNULL(val) +#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // !DCHECK_IS_ON() + +#define DLOG(severity) \ + true ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity) + +#define DVLOG(verboselevel) \ + (true || !VLOG_IS_ON(verboselevel)) ?\ + (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(INFO) + +#define DLOG_IF(severity, condition) \ + (true || !(condition)) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity) + +#define DLOG_EVERY_N(severity, n) \ + true ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF_EVERY_N(severity, condition, n) \ + (true || !(condition))? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +// MSVC warning C4127: conditional expression is constant +#define DCHECK(condition) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK(condition) + +#define DCHECK_EQ(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2) + +#define DCHECK_NE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2) + +#define DCHECK_LE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2) + +#define DCHECK_LT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2) + +#define DCHECK_GE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2) + +#define DCHECK_GT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2) + +// You may see warnings in release mode if you don't use the return +// value of DCHECK_NOTNULL. Please just use DCHECK for such cases. +#define DCHECK_NOTNULL(val) (val) + +#define DCHECK_STREQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2) + +#define DCHECK_STRCASEEQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2) + +#define DCHECK_STRNE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2) + +#define DCHECK_STRCASENE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2) + +#endif // DCHECK_IS_ON() + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +namespace base_logging { + +// LogMessage::LogStream is a std::ostream backed by this streambuf. +// This class ignores overflow and leaves two bytes at the end of the +// buffer to allow for a '\n' and '\0'. +class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf { + public: + // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\n'. + LogStreamBuf(char *buf, int len) { + setp(buf, buf + len - 2); + } + // This effectively ignores overflow. + virtual int_type overflow(int_type ch) { + return ch; + } + + // Legacy public ostrstream method. + size_t pcount() const { return pptr() - pbase(); } + char* pbase() const { return std::streambuf::pbase(); } +}; + +} // namespace base_logging + +// +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GOOGLE_GLOG_DLL_DECL LogMessage { +public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. +#ifdef _MSC_VER +# pragma warning(disable: 4275) +#endif + class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream { +#ifdef _MSC_VER +# pragma warning(default: 4275) +#endif + public: + LogStream(char *buf, int len, int ctr) + : std::ostream(NULL), + streambuf_(buf, len), + ctr_(ctr), + self_(this) { + rdbuf(&streambuf_); + } + + int ctr() const { return ctr_; } + void set_ctr(int ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + // Legacy std::streambuf methods. + size_t pcount() const { return streambuf_.pcount(); } + char* pbase() const { return streambuf_.pbase(); } + char* str() const { return pbase(); } + + private: + LogStream(const LogStream&); + LogStream& operator=(const LogStream&); + base_logging::LogStreamBuf streambuf_; + int ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream *self_; // Consistency check hack + }; + +public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector<string> pointer + // for storing the messages (if the pointer is not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector<std::string>* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not NULL). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, const CheckOpString& result); + + ~LogMessage(); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // Theses should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + static void @ac_cv___attribute___noreturn@ Fail(); + + std::ostream& stream(); + + int preserved_errno() const; + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + + struct LogMessageData; + +private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); + + // Counts of messages sent at each priority: + static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex + + // We keep the data in a separate struct so that each instance of + // LogMessage uses less stack space. + LogMessageData* allocated_; + LogMessageData* data_; + + friend class LogDestination; + + LogMessage(const LogMessage&); + void operator=(const LogMessage&); +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line); + LogMessageFatal(const char* file, int line, const CheckOpString& result); + @ac_cv___attribute___noreturn@ ~LogMessageFatal(); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const severity, std::string const &msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) @ac_google_namespace@::LogMessage(__FILE__, __LINE__, severity).stream() + +// Check if it's compiled in C++11 mode. +// +// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least +// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 +// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is +// defined according to the language version in effect thereafter. +// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite +// reasonably good C++11 support, so we set LANG_CXX for it and +// newer versions (_MSC_VER >= 1900). +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1900)) +// Helper for CHECK_NOTNULL(). +// +// In C++11, all cases can be handled by a single function. Since the value +// category of the argument is preserved (also for rvalue references), +// member initializer lists like the one below will compile correctly: +// +// Foo() +// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {} +template <typename T> +T CheckNotNull(const char* file, int line, const char* names, T&& t) { + if (t == nullptr) { + LogMessageFatal(file, line, new std::string(names)); + } + return std::forward<T>(t); +} + +#else + +// A small helper for CHECK_NOTNULL(). +template <typename T> +T* CheckNotNull(const char *file, int line, const char *names, T* t) { + if (t == NULL) { + LogMessageFatal(file, line, new std::string(names)); + } + return t; +} +#endif + +// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This +// only works if ostream is a LogStream. If the ostream is not a +// LogStream you'll get an assert saying as much at runtime. +GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, + const PRIVATE_Counter&); + + +// Derived class for PLOG*() above. +class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { + public: + + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-safe. +GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GOOGLE_GLOG_DLL_DECL LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) = 0; + + // Redefine this to implement waiting for + // the sink's logging logic to complete. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // Returns the normal text output of the log message. + // Can be useful to implement send(). + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); +GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( + const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a commma-separated +// list of addressess. Thread-safe. +GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, + const char *subject, const char *body); + +GOOGLE_GLOG_DLL_DECL const std::vector<std::string>& GetLoggingDirectories(); + +// For tests only: Clear the internal [cached] list of logging directories to +// force a refresh the next time GetLoggingDirectories is called. +// Thread-hostile. +void TestOnly_ClearLoggingDirectoriesList(); + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLogginDirectories(). +// Thread-safe. +GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( + std::vector<std::string>* list); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, + int64 limit, int64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GOOGLE_GLOG_DLL_DECL Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // appropriate by the higher level logging facility. For example, + // textual log messages already contain timestamps, and the + // file:linenumber header. + virtual void Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) = 0; + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); + +} + +// glibc has traditionally implemented two incompatible versions of +// strerror_r(). There is a poorly defined convention for picking the +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +// DEPRECATED: Use StrError(int) instead. +GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); + +// A thread-safe replacement for strerror(). Returns a string describing the +// given POSIX error code. +GOOGLE_GLOG_DLL_DECL std::string StrError(int err); + +// A class for which we define operator<<, which does nothing. +class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream(const char* /*file*/, int /*line*/, + const CheckOpString& /*result*/) : + LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream &stream() { return *this; } + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[2]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template<class T> +inline NullStream& operator<<(NullStream &str, const T &) { return str; } + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { + public: + NullStreamFatal() { } + NullStreamFatal(const char* file, int line, const CheckOpString& result) : + NullStream(file, line, result) { } + @ac_cv___attribute___noreturn@ ~NullStreamFatal() throw () { _exit(1); } +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( + void (*writer)(const char* data, int size)); + +@ac_google_end_namespace@ + +#endif // _LOGGING_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/raw_logging.h b/ios/Pods/Flipper-Glog/src/glog/raw_logging.h new file mode 100644 index 000000000..65278f628 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/raw_logging.h @@ -0,0 +1,185 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Maxim Lifantsev +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation and synchronization code. + +#ifndef BASE_RAW_LOGGING_H_ +#define BASE_RAW_LOGGING_H_ + +#include <time.h> + +namespace google { + +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// This is similar to LOG(severity) << format... and VLOG(level) << format.., +// but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is desiged to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// RAW_VLOG(3, "status is %i", status); +// These will print an almost standard log lines like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file +// I0821 211317 file.cc:142] RAW: status is 20 +#define RAW_LOG(severity, ...) \ + do { \ + switch (google::GLOG_ ## severity) { \ + case 0: \ + RAW_LOG_INFO(__VA_ARGS__); \ + break; \ + case 1: \ + RAW_LOG_WARNING(__VA_ARGS__); \ + break; \ + case 2: \ + RAW_LOG_ERROR(__VA_ARGS__); \ + break; \ + case 3: \ + RAW_LOG_FATAL(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +// The following STRIP_LOG testing is performed in the header file so that it's +// possible to completely compile out the logging code and the log messages. +#if STRIP_LOG == 0 +#define RAW_VLOG(verboselevel, ...) \ + do { \ + if (VLOG_IS_ON(verboselevel)) { \ + RAW_LOG_INFO(__VA_ARGS__); \ + } \ + } while (0) +#else +#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG == 0 +#define RAW_LOG_INFO(...) google::RawLog__(google::GLOG_INFO, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG <= 1 +#define RAW_LOG_WARNING(...) google::RawLog__(google::GLOG_WARNING, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 1 + +#if STRIP_LOG <= 2 +#define RAW_LOG_ERROR(...) google::RawLog__(google::GLOG_ERROR, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 2 + +#if STRIP_LOG <= 3 +#define RAW_LOG_FATAL(...) google::RawLog__(google::GLOG_FATAL, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_FATAL(...) \ + do { \ + google::RawLogStub__(0, __VA_ARGS__); \ + exit(1); \ + } while (0) +#endif // STRIP_LOG <= 3 + +// Similar to CHECK(condition) << message, +// but for low-level modules: we use only RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define RAW_CHECK(condition, message) \ + do { \ + if (!(condition)) { \ + RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// Debug versions of RAW_LOG and RAW_CHECK +#ifndef NDEBUG + +#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) + +#else // NDEBUG + +#define RAW_DLOG(severity, ...) \ + while (false) \ + RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) \ + while (false) \ + RAW_CHECK(condition, message) + +#endif // NDEBUG + +// Stub log function used to work around for unused variable warnings when +// building with STRIP_LOG > 0. +static inline void RawLogStub__(int /* ignored */, ...) { +} + +// Helper function to implement RAW_LOG and RAW_VLOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity, + const char* file, + int line, + const char* format, ...) + __attribute__((__format__ (__printf__, 4, 5))); + +// Hack to propagate time information into this module so that +// this module does not have to directly call localtime_r(), +// which could allocate memory. +GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs); + +} + +#endif // BASE_RAW_LOGGING_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/raw_logging.h.in b/ios/Pods/Flipper-Glog/src/glog/raw_logging.h.in new file mode 100644 index 000000000..fa1705765 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/raw_logging.h.in @@ -0,0 +1,185 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Maxim Lifantsev +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation and synchronization code. + +#ifndef BASE_RAW_LOGGING_H_ +#define BASE_RAW_LOGGING_H_ + +#include <time.h> + +@ac_google_start_namespace@ + +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// This is similar to LOG(severity) << format... and VLOG(level) << format.., +// but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is desiged to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// RAW_VLOG(3, "status is %i", status); +// These will print an almost standard log lines like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file +// I0821 211317 file.cc:142] RAW: status is 20 +#define RAW_LOG(severity, ...) \ + do { \ + switch (@ac_google_namespace@::GLOG_ ## severity) { \ + case 0: \ + RAW_LOG_INFO(__VA_ARGS__); \ + break; \ + case 1: \ + RAW_LOG_WARNING(__VA_ARGS__); \ + break; \ + case 2: \ + RAW_LOG_ERROR(__VA_ARGS__); \ + break; \ + case 3: \ + RAW_LOG_FATAL(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +// The following STRIP_LOG testing is performed in the header file so that it's +// possible to completely compile out the logging code and the log messages. +#if STRIP_LOG == 0 +#define RAW_VLOG(verboselevel, ...) \ + do { \ + if (VLOG_IS_ON(verboselevel)) { \ + RAW_LOG_INFO(__VA_ARGS__); \ + } \ + } while (0) +#else +#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG == 0 +#define RAW_LOG_INFO(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_INFO, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_INFO(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG <= 1 +#define RAW_LOG_WARNING(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_WARNING, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_WARNING(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 1 + +#if STRIP_LOG <= 2 +#define RAW_LOG_ERROR(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_ERROR, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_ERROR(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 2 + +#if STRIP_LOG <= 3 +#define RAW_LOG_FATAL(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_FATAL, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_FATAL(...) \ + do { \ + @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__); \ + exit(1); \ + } while (0) +#endif // STRIP_LOG <= 3 + +// Similar to CHECK(condition) << message, +// but for low-level modules: we use only RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define RAW_CHECK(condition, message) \ + do { \ + if (!(condition)) { \ + RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// Debug versions of RAW_LOG and RAW_CHECK +#ifndef NDEBUG + +#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) + +#else // NDEBUG + +#define RAW_DLOG(severity, ...) \ + while (false) \ + RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) \ + while (false) \ + RAW_CHECK(condition, message) + +#endif // NDEBUG + +// Stub log function used to work around for unused variable warnings when +// building with STRIP_LOG > 0. +static inline void RawLogStub__(int /* ignored */, ...) { +} + +// Helper function to implement RAW_LOG and RAW_VLOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity, + const char* file, + int line, + const char* format, ...) + @ac_cv___attribute___printf_4_5@; + +// Hack to propagate time information into this module so that +// this module does not have to directly call localtime_r(), +// which could allocate memory. +GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs); + +@ac_google_end_namespace@ + +#endif // BASE_RAW_LOGGING_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/stl_logging.h b/ios/Pods/Flipper-Glog/src/glog/stl_logging.h new file mode 100644 index 000000000..40a15aa45 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/stl_logging.h @@ -0,0 +1,220 @@ +// Copyright (c) 2003, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Stream output operators for STL containers; to be used for logging *only*. +// Inclusion of this file lets you do: +// +// list<string> x; +// LOG(INFO) << "data: " << x; +// vector<int> v1, v2; +// CHECK_EQ(v1, v2); +// +// If you want to use this header file with hash maps or slist, you +// need to define macros before including this file: +// +// - GLOG_STL_LOGGING_FOR_UNORDERED - <unordered_map> and <unordered_set> +// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)> +// - GLOG_STL_LOGGING_FOR_EXT_HASH - <ext/hash_(map|set)> +// - GLOG_STL_LOGGING_FOR_EXT_SLIST - <ext/slist> +// + +#ifndef UTIL_GTL_STL_LOGGING_INL_H_ +#define UTIL_GTL_STL_LOGGING_INL_H_ + +#if !1 +# error We do not support stl_logging for this compiler +#endif + +#include <deque> +#include <list> +#include <map> +#include <ostream> +#include <set> +#include <utility> +#include <vector> + +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +# include <unordered_map> +# include <unordered_set> +#endif + +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +# include <tr1/unordered_map> +# include <tr1/unordered_set> +#endif + +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +# include <ext/hash_set> +# include <ext/hash_map> +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST +# include <ext/slist> +#endif + +// Forward declare these two, and define them after all the container streams +// operators so that we can recurse from pair -> container -> container -> pair +// properly. +template<class First, class Second> +std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p); + +namespace google { + +template<class Iter> +void PrintSequence(std::ostream& out, Iter begin, Iter end); + +} + +#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ +template<class T1, class T2> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2>& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_TWO_ARG_CONTAINER(std::vector) +OUTPUT_TWO_ARG_CONTAINER(std::deque) +OUTPUT_TWO_ARG_CONTAINER(std::list) +#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST +OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist) +#endif + +#undef OUTPUT_TWO_ARG_CONTAINER + +#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3>& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_THREE_ARG_CONTAINER(std::set) +OUTPUT_THREE_ARG_CONTAINER(std::multiset) + +#undef OUTPUT_THREE_ARG_CONTAINER + +#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3, class T4> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3, T4>& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_FOUR_ARG_CONTAINER(std::map) +OUTPUT_FOUR_ARG_CONTAINER(std::multimap) +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set) +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset) +#endif +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set) +OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset) +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set) +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset) +#endif + +#undef OUTPUT_FOUR_ARG_CONTAINER + +#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3, class T4, class T5> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3, T4, T5>& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map) +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap) +#endif +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map) +OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap) +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map) +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap) +#endif + +#undef OUTPUT_FIVE_ARG_CONTAINER + +template<class First, class Second> +inline std::ostream& operator<<(std::ostream& out, + const std::pair<First, Second>& p) { + out << '(' << p.first << ", " << p.second << ')'; + return out; +} + +namespace google { + +template<class Iter> +inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { + // Output at most 100 elements -- appropriate if used for logging. + for (int i = 0; begin != end && i < 100; ++i, ++begin) { + if (i > 0) out << ' '; + out << *begin; + } + if (begin != end) { + out << " ..."; + } +} + +} + +// Note that this is technically undefined behavior! We are adding things into +// the std namespace for a reason though -- we are providing new operations on +// types which are themselves defined with this namespace. Without this, these +// operator overloads cannot be found via ADL. If these definitions are not +// found via ADL, they must be #included before they're used, which requires +// this header to be included before apparently independent other headers. +// +// For example, base/logging.h defines various template functions to implement +// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails. +// It does so via the function template MakeCheckOpValueString: +// template<class T> +// void MakeCheckOpValueString(strstream* ss, const T& v) { +// (*ss) << v; +// } +// Because 'glog/logging.h' is included before 'glog/stl_logging.h', +// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only +// find these operator definitions via ADL. +// +// Even this solution has problems -- it may pull unintended operators into the +// namespace as well, allowing them to also be found via ADL, and creating code +// that only works with a particular order of includes. Long term, we need to +// move all of the *definitions* into namespace std, bet we need to ensure no +// one references them first. This lets us take that step. We cannot define them +// in both because that would create ambiguous overloads when both are found. +namespace std { using ::operator<<; } + +#endif // UTIL_GTL_STL_LOGGING_INL_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/stl_logging.h.in b/ios/Pods/Flipper-Glog/src/glog/stl_logging.h.in new file mode 100644 index 000000000..600945d2c --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/stl_logging.h.in @@ -0,0 +1,220 @@ +// Copyright (c) 2003, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Stream output operators for STL containers; to be used for logging *only*. +// Inclusion of this file lets you do: +// +// list<string> x; +// LOG(INFO) << "data: " << x; +// vector<int> v1, v2; +// CHECK_EQ(v1, v2); +// +// If you want to use this header file with hash maps or slist, you +// need to define macros before including this file: +// +// - GLOG_STL_LOGGING_FOR_UNORDERED - <unordered_map> and <unordered_set> +// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)> +// - GLOG_STL_LOGGING_FOR_EXT_HASH - <ext/hash_(map|set)> +// - GLOG_STL_LOGGING_FOR_EXT_SLIST - <ext/slist> +// + +#ifndef UTIL_GTL_STL_LOGGING_INL_H_ +#define UTIL_GTL_STL_LOGGING_INL_H_ + +#if !@ac_cv_cxx_using_operator@ +# error We do not support stl_logging for this compiler +#endif + +#include <deque> +#include <list> +#include <map> +#include <ostream> +#include <set> +#include <utility> +#include <vector> + +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +# include <unordered_map> +# include <unordered_set> +#endif + +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +# include <tr1/unordered_map> +# include <tr1/unordered_set> +#endif + +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +# include <ext/hash_set> +# include <ext/hash_map> +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST +# include <ext/slist> +#endif + +// Forward declare these two, and define them after all the container streams +// operators so that we can recurse from pair -> container -> container -> pair +// properly. +template<class First, class Second> +std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p); + +@ac_google_start_namespace@ + +template<class Iter> +void PrintSequence(std::ostream& out, Iter begin, Iter end); + +@ac_google_end_namespace@ + +#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ +template<class T1, class T2> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2>& seq) { \ + @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_TWO_ARG_CONTAINER(std::vector) +OUTPUT_TWO_ARG_CONTAINER(std::deque) +OUTPUT_TWO_ARG_CONTAINER(std::list) +#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST +OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist) +#endif + +#undef OUTPUT_TWO_ARG_CONTAINER + +#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3>& seq) { \ + @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_THREE_ARG_CONTAINER(std::set) +OUTPUT_THREE_ARG_CONTAINER(std::multiset) + +#undef OUTPUT_THREE_ARG_CONTAINER + +#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3, class T4> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3, T4>& seq) { \ + @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_FOUR_ARG_CONTAINER(std::map) +OUTPUT_FOUR_ARG_CONTAINER(std::multimap) +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set) +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset) +#endif +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set) +OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset) +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set) +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset) +#endif + +#undef OUTPUT_FOUR_ARG_CONTAINER + +#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ +template<class T1, class T2, class T3, class T4, class T5> \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence<T1, T2, T3, T4, T5>& seq) { \ + @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +#ifdef GLOG_STL_LOGGING_FOR_UNORDERED +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map) +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap) +#endif +#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED +OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map) +OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap) +#endif +#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map) +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap) +#endif + +#undef OUTPUT_FIVE_ARG_CONTAINER + +template<class First, class Second> +inline std::ostream& operator<<(std::ostream& out, + const std::pair<First, Second>& p) { + out << '(' << p.first << ", " << p.second << ')'; + return out; +} + +@ac_google_start_namespace@ + +template<class Iter> +inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { + // Output at most 100 elements -- appropriate if used for logging. + for (int i = 0; begin != end && i < 100; ++i, ++begin) { + if (i > 0) out << ' '; + out << *begin; + } + if (begin != end) { + out << " ..."; + } +} + +@ac_google_end_namespace@ + +// Note that this is technically undefined behavior! We are adding things into +// the std namespace for a reason though -- we are providing new operations on +// types which are themselves defined with this namespace. Without this, these +// operator overloads cannot be found via ADL. If these definitions are not +// found via ADL, they must be #included before they're used, which requires +// this header to be included before apparently independent other headers. +// +// For example, base/logging.h defines various template functions to implement +// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails. +// It does so via the function template MakeCheckOpValueString: +// template<class T> +// void MakeCheckOpValueString(strstream* ss, const T& v) { +// (*ss) << v; +// } +// Because 'glog/logging.h' is included before 'glog/stl_logging.h', +// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only +// find these operator definitions via ADL. +// +// Even this solution has problems -- it may pull unintended operators into the +// namespace as well, allowing them to also be found via ADL, and creating code +// that only works with a particular order of includes. Long term, we need to +// move all of the *definitions* into namespace std, bet we need to ensure no +// one references them first. This lets us take that step. We cannot define them +// in both because that would create ambiguous overloads when both are found. +namespace std { using ::operator<<; } + +#endif // UTIL_GTL_STL_LOGGING_INL_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h b/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h new file mode 100644 index 000000000..02b0b8670 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h @@ -0,0 +1,129 @@ +// Copyright (c) 1999, 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney and many others +// +// Defines the VLOG_IS_ON macro that controls the variable-verbosity +// conditional logging. +// +// It's used by VLOG and VLOG_IF in logging.h +// and by RAW_VLOG in raw_logging.h to trigger the logging. +// +// It can also be used directly e.g. like this: +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished e.g. via just VLOG(2) << ...; +// } +// +// The truth value that VLOG_IS_ON(level) returns is determined by +// the three verbosity level flags: +// --v=<n> Gives the default maximal active V-logging level; +// 0 is the default. +// Normally positive values are used for V-logging levels. +// --vmodule=<str> Gives the per-module maximal V-logging levels to override +// the value given by --v. +// E.g. "my_module=2,foo*=3" would change the logging level +// for all code in source files "my_module.*" and "foo*.*" +// ("-inl" suffixes are also disregarded for this matching). +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by overriding the per-module settings given via --vmodule flag. +// +// CAVEAT: --vmodule functionality is not available in non gcc compilers. +// + +#ifndef BASE_VLOG_IS_ON_H_ +#define BASE_VLOG_IS_ON_H_ + +#include "glog/log_severity.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +#if defined(__GNUC__) +// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. +// (Normally) the first time every VLOG_IS_ON(n) site is hit, +// we determine what variable will dynamically control logging at this site: +// it's either FLAGS_v or an appropriate internal variable +// matching the current source file that represents results of +// parsing of --vmodule flag and/or SetVLOGLevel calls. +#define VLOG_IS_ON(verboselevel) \ + __extension__ \ + ({ static google::int32* vlocal__ = &google::kLogSiteUninitialized; \ + google::int32 verbose_level__ = (verboselevel); \ + (*vlocal__ >= verbose_level__) && \ + ((vlocal__ != &google::kLogSiteUninitialized) || \ + (google::InitVLOG3__(&vlocal__, &FLAGS_v, \ + __FILE__, verbose_level__))); }) +#else +// GNU extensions not available, so we do not support --vmodule. +// Dynamic value of FLAGS_v always controls the logging level. +#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) +#endif + +// Set VLOG(_IS_ON) level for module_pattern to log_level. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// NOTE: To change the log level for VLOG(_IS_ON) sites +// that have already executed after/during InitGoogleLogging, +// one needs to supply the exact --vmodule pattern that applied to them. +// (If no --vmodule pattern applied to them +// the value of FLAGS_v will continue to control them.) +extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern, + int log_level); + +// Various declarations needed for VLOG_IS_ON above: ========================= + +// Special value used to indicate that a VLOG_IS_ON site has not been +// initialized. We make this a large value, so the common-case check +// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition +// passes in such cases and InitVLOG3__ is then triggered. +extern google::int32 kLogSiteUninitialized; + +// Helper routine which determines the logging info for a particalur VLOG site. +// site_flag is the address of the site-local pointer to the controlling +// verbosity level +// site_default is the default to use for *site_flag +// fname is the current source file name +// verbose_level is the argument to VLOG_IS_ON +// We will return the return value for VLOG_IS_ON +// and if possible set *site_flag appropriately. +extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__( + google::int32** site_flag, + google::int32* site_default, + const char* fname, + google::int32 verbose_level); + +#endif // BASE_VLOG_IS_ON_H_ diff --git a/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h.in b/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h.in new file mode 100644 index 000000000..3f4c4a32a --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/glog/vlog_is_on.h.in @@ -0,0 +1,129 @@ +// Copyright (c) 1999, 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney and many others +// +// Defines the VLOG_IS_ON macro that controls the variable-verbosity +// conditional logging. +// +// It's used by VLOG and VLOG_IF in logging.h +// and by RAW_VLOG in raw_logging.h to trigger the logging. +// +// It can also be used directly e.g. like this: +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished e.g. via just VLOG(2) << ...; +// } +// +// The truth value that VLOG_IS_ON(level) returns is determined by +// the three verbosity level flags: +// --v=<n> Gives the default maximal active V-logging level; +// 0 is the default. +// Normally positive values are used for V-logging levels. +// --vmodule=<str> Gives the per-module maximal V-logging levels to override +// the value given by --v. +// E.g. "my_module=2,foo*=3" would change the logging level +// for all code in source files "my_module.*" and "foo*.*" +// ("-inl" suffixes are also disregarded for this matching). +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by overriding the per-module settings given via --vmodule flag. +// +// CAVEAT: --vmodule functionality is not available in non gcc compilers. +// + +#ifndef BASE_VLOG_IS_ON_H_ +#define BASE_VLOG_IS_ON_H_ + +#include "glog/log_severity.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +#if defined(__GNUC__) +// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. +// (Normally) the first time every VLOG_IS_ON(n) site is hit, +// we determine what variable will dynamically control logging at this site: +// it's either FLAGS_v or an appropriate internal variable +// matching the current source file that represents results of +// parsing of --vmodule flag and/or SetVLOGLevel calls. +#define VLOG_IS_ON(verboselevel) \ + __extension__ \ + ({ static @ac_google_namespace@::int32* vlocal__ = &@ac_google_namespace@::kLogSiteUninitialized; \ + @ac_google_namespace@::int32 verbose_level__ = (verboselevel); \ + (*vlocal__ >= verbose_level__) && \ + ((vlocal__ != &@ac_google_namespace@::kLogSiteUninitialized) || \ + (@ac_google_namespace@::InitVLOG3__(&vlocal__, &FLAGS_v, \ + __FILE__, verbose_level__))); }) +#else +// GNU extensions not available, so we do not support --vmodule. +// Dynamic value of FLAGS_v always controls the logging level. +#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) +#endif + +// Set VLOG(_IS_ON) level for module_pattern to log_level. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// NOTE: To change the log level for VLOG(_IS_ON) sites +// that have already executed after/during InitGoogleLogging, +// one needs to supply the exact --vmodule pattern that applied to them. +// (If no --vmodule pattern applied to them +// the value of FLAGS_v will continue to control them.) +extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern, + int log_level); + +// Various declarations needed for VLOG_IS_ON above: ========================= + +// Special value used to indicate that a VLOG_IS_ON site has not been +// initialized. We make this a large value, so the common-case check +// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition +// passes in such cases and InitVLOG3__ is then triggered. +extern @ac_google_namespace@::int32 kLogSiteUninitialized; + +// Helper routine which determines the logging info for a particalur VLOG site. +// site_flag is the address of the site-local pointer to the controlling +// verbosity level +// site_default is the default to use for *site_flag +// fname is the current source file name +// verbose_level is the argument to VLOG_IS_ON +// We will return the return value for VLOG_IS_ON +// and if possible set *site_flag appropriately. +extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__( + @ac_google_namespace@::int32** site_flag, + @ac_google_namespace@::int32* site_default, + const char* fname, + @ac_google_namespace@::int32 verbose_level); + +#endif // BASE_VLOG_IS_ON_H_ diff --git a/ios/Pods/Flipper-Glog/src/googletest.h b/ios/Pods/Flipper-Glog/src/googletest.h new file mode 100644 index 000000000..b4677b276 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/googletest.h @@ -0,0 +1,596 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Shinichiro Hamaji +// (based on googletest: http://code.google.com/p/googletest/) + +#ifdef GOOGLETEST_H__ +#error You must not include this file twice. +#endif +#define GOOGLETEST_H__ + +#include "utilities.h" + +#include <ctype.h> +#include <setjmp.h> +#include <time.h> + +#include <map> +#include <sstream> +#include <string> +#include <vector> + +#include <stdio.h> +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "base/commandlineflags.h" + +using std::map; +using std::string; +using std::vector; + +_START_GOOGLE_NAMESPACE_ + +extern GOOGLE_GLOG_DLL_DECL void (*g_logging_fail_func)(); + +_END_GOOGLE_NAMESPACE_ + +#undef GOOGLE_GLOG_DLL_DECL +#define GOOGLE_GLOG_DLL_DECL + +static inline string GetTempDir() { +#ifndef OS_WINDOWS + return "/tmp"; +#else + char tmp[MAX_PATH]; + GetTempPathA(MAX_PATH, tmp); + return tmp; +#endif +} + +#if defined(OS_WINDOWS) && defined(_MSC_VER) && !defined(TEST_SRC_DIR) +// The test will run in glog/vsproject/<project name> +// (e.g., glog/vsproject/logging_unittest). +static const char TEST_SRC_DIR[] = "../.."; +#elif !defined(TEST_SRC_DIR) +# warning TEST_SRC_DIR should be defined in config.h +static const char TEST_SRC_DIR[] = "."; +#endif + +DEFINE_string(test_tmpdir, GetTempDir(), "Dir we use for temp files"); +DEFINE_string(test_srcdir, TEST_SRC_DIR, + "Source-dir root, needed to find glog_unittest_flagfile"); +DEFINE_bool(run_benchmark, false, "If true, run benchmarks"); +#ifdef NDEBUG +DEFINE_int32(benchmark_iters, 100000000, "Number of iterations per benchmark"); +#else +DEFINE_int32(benchmark_iters, 100000, "Number of iterations per benchmark"); +#endif + +#ifdef HAVE_LIB_GTEST +# include <gtest/gtest.h> +// Use our ASSERT_DEATH implementation. +# undef ASSERT_DEATH +# undef ASSERT_DEBUG_DEATH +using testing::InitGoogleTest; +#else + +_START_GOOGLE_NAMESPACE_ + +void InitGoogleTest(int*, char**) {} + +// The following is some bare-bones testing infrastructure + +#define EXPECT_TRUE(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "Check failed: %s\n", #cond); \ + exit(1); \ + } \ + } while (0) + +#define EXPECT_FALSE(cond) EXPECT_TRUE(!(cond)) + +#define EXPECT_OP(op, val1, val2) \ + do { \ + if (!((val1) op (val2))) { \ + fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2); \ + exit(1); \ + } \ + } while (0) + +#define EXPECT_EQ(val1, val2) EXPECT_OP(==, val1, val2) +#define EXPECT_NE(val1, val2) EXPECT_OP(!=, val1, val2) +#define EXPECT_GT(val1, val2) EXPECT_OP(>, val1, val2) +#define EXPECT_LT(val1, val2) EXPECT_OP(<, val1, val2) + +#define EXPECT_NAN(arg) \ + do { \ + if (!isnan(arg)) { \ + fprintf(stderr, "Check failed: isnan(%s)\n", #arg); \ + exit(1); \ + } \ + } while (0) + +#define EXPECT_INF(arg) \ + do { \ + if (!isinf(arg)) { \ + fprintf(stderr, "Check failed: isinf(%s)\n", #arg); \ + exit(1); \ + } \ + } while (0) + +#define EXPECT_DOUBLE_EQ(val1, val2) \ + do { \ + if (((val1) < (val2) - 0.001 || (val1) > (val2) + 0.001)) { \ + fprintf(stderr, "Check failed: %s == %s\n", #val1, #val2); \ + exit(1); \ + } \ + } while (0) + +#define EXPECT_STREQ(val1, val2) \ + do { \ + if (strcmp((val1), (val2)) != 0) { \ + fprintf(stderr, "Check failed: streq(%s, %s)\n", #val1, #val2); \ + exit(1); \ + } \ + } while (0) + +vector<void (*)()> g_testlist; // the tests to run + +#define TEST(a, b) \ + struct Test_##a##_##b { \ + Test_##a##_##b() { g_testlist.push_back(&Run); } \ + static void Run() { FlagSaver fs; RunTest(); } \ + static void RunTest(); \ + }; \ + static Test_##a##_##b g_test_##a##_##b; \ + void Test_##a##_##b::RunTest() + + +static inline int RUN_ALL_TESTS() { + vector<void (*)()>::const_iterator it; + for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { + (*it)(); + } + fprintf(stderr, "Passed %d tests\n\nPASS\n", (int)g_testlist.size()); + return 0; +} + +_END_GOOGLE_NAMESPACE_ + +#endif // ! HAVE_LIB_GTEST + +_START_GOOGLE_NAMESPACE_ + +static bool g_called_abort; +static jmp_buf g_jmp_buf; +static inline void CalledAbort() { + g_called_abort = true; + longjmp(g_jmp_buf, 1); +} + +#ifdef OS_WINDOWS +// TODO(hamaji): Death test somehow doesn't work in Windows. +#define ASSERT_DEATH(fn, msg) +#else +#define ASSERT_DEATH(fn, msg) \ + do { \ + g_called_abort = false; \ + /* in logging.cc */ \ + void (*original_logging_fail_func)() = g_logging_fail_func; \ + g_logging_fail_func = &CalledAbort; \ + if (!setjmp(g_jmp_buf)) fn; \ + /* set back to their default */ \ + g_logging_fail_func = original_logging_fail_func; \ + if (!g_called_abort) { \ + fprintf(stderr, "Function didn't die (%s): %s\n", msg, #fn); \ + exit(1); \ + } \ + } while (0) +#endif + +#ifdef NDEBUG +#define ASSERT_DEBUG_DEATH(fn, msg) +#else +#define ASSERT_DEBUG_DEATH(fn, msg) ASSERT_DEATH(fn, msg) +#endif // NDEBUG + +// Benchmark tools. + +#define BENCHMARK(n) static BenchmarkRegisterer __benchmark_ ## n (#n, &n); + +map<string, void (*)(int)> g_benchlist; // the benchmarks to run + +class BenchmarkRegisterer { + public: + BenchmarkRegisterer(const char* name, void (*function)(int iters)) { + EXPECT_TRUE(g_benchlist.insert(std::make_pair(name, function)).second); + } +}; + +static inline void RunSpecifiedBenchmarks() { + if (!FLAGS_run_benchmark) { + return; + } + + int iter_cnt = FLAGS_benchmark_iters; + puts("Benchmark\tTime(ns)\tIterations"); + for (map<string, void (*)(int)>::const_iterator iter = g_benchlist.begin(); + iter != g_benchlist.end(); + ++iter) { + clock_t start = clock(); + iter->second(iter_cnt); + double elapsed_ns = + ((double)clock() - start) / CLOCKS_PER_SEC * 1000*1000*1000; + printf("%s\t%8.2lf\t%10d\n", + iter->first.c_str(), elapsed_ns / iter_cnt, iter_cnt); + } + puts(""); +} + +// ---------------------------------------------------------------------- +// Golden file functions +// ---------------------------------------------------------------------- + +class CapturedStream { + public: + CapturedStream(int fd, const string & filename) : + fd_(fd), + uncaptured_fd_(-1), + filename_(filename) { + Capture(); + } + + ~CapturedStream() { + if (uncaptured_fd_ != -1) { + CHECK(close(uncaptured_fd_) != -1); + } + } + + // Start redirecting output to a file + void Capture() { + // Keep original stream for later + CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!"; + uncaptured_fd_ = dup(fd_); + CHECK(uncaptured_fd_ != -1); + + // Open file to save stream to + int cap_fd = open(filename_.c_str(), + O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR); + CHECK(cap_fd != -1); + + // Send stdout/stderr to this file + fflush(NULL); + CHECK(dup2(cap_fd, fd_) != -1); + CHECK(close(cap_fd) != -1); + } + + // Remove output redirection + void StopCapture() { + // Restore original stream + if (uncaptured_fd_ != -1) { + fflush(NULL); + CHECK(dup2(uncaptured_fd_, fd_) != -1); + } + } + + const string & filename() const { return filename_; } + + private: + int fd_; // file descriptor being captured + int uncaptured_fd_; // where the stream was originally being sent to + string filename_; // file where stream is being saved +}; +static CapturedStream * s_captured_streams[STDERR_FILENO+1]; +// Redirect a file descriptor to a file. +// fd - Should be STDOUT_FILENO or STDERR_FILENO +// filename - File where output should be stored +static inline void CaptureTestOutput(int fd, const string & filename) { + CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO)); + CHECK(s_captured_streams[fd] == NULL); + s_captured_streams[fd] = new CapturedStream(fd, filename); +} +static inline void CaptureTestStderr() { + CaptureTestOutput(STDERR_FILENO, FLAGS_test_tmpdir + "/captured.err"); +} +// Return the size (in bytes) of a file +static inline size_t GetFileSize(FILE * file) { + fseek(file, 0, SEEK_END); + return static_cast<size_t>(ftell(file)); +} +// Read the entire content of a file as a string +static inline string ReadEntireFile(FILE * file) { + const size_t file_size = GetFileSize(file); + char * const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keep reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const string content = string(buffer, buffer+bytes_read); + delete[] buffer; + + return content; +} +// Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when +// fd is STDERR_FILENO) as a string +static inline string GetCapturedTestOutput(int fd) { + CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO); + CapturedStream * const cap = s_captured_streams[fd]; + CHECK(cap) + << ": did you forget CaptureTestStdout() or CaptureTestStderr()?"; + + // Make sure everything is flushed. + cap->StopCapture(); + + // Read the captured file. + FILE * const file = fopen(cap->filename().c_str(), "r"); + const string content = ReadEntireFile(file); + fclose(file); + + delete cap; + s_captured_streams[fd] = NULL; + + return content; +} +// Get the captured stderr of a test as a string. +static inline string GetCapturedTestStderr() { + return GetCapturedTestOutput(STDERR_FILENO); +} + +// Check if the string is [IWEF](\d{4}|DATE) +static inline bool IsLoggingPrefix(const string& s) { + if (s.size() != 5) return false; + if (!strchr("IWEF", s[0])) return false; + for (int i = 1; i <= 4; ++i) { + if (!isdigit(s[i]) && s[i] != "DATE"[i-1]) return false; + } + return true; +} + +// Convert log output into normalized form. +// +// Example: +// I0102 030405 logging_unittest.cc:345] RAW: vlog -1 +// => IDATE TIME__ logging_unittest.cc:LINE] RAW: vlog -1 +static inline string MungeLine(const string& line) { + std::istringstream iss(line); + string before, logcode_date, time, thread_lineinfo; + iss >> logcode_date; + while (!IsLoggingPrefix(logcode_date)) { + before += " " + logcode_date; + if (!(iss >> logcode_date)) { + // We cannot find the header of log output. + return before; + } + } + if (!before.empty()) before += " "; + iss >> time; + iss >> thread_lineinfo; + CHECK(!thread_lineinfo.empty()); + if (thread_lineinfo[thread_lineinfo.size() - 1] != ']') { + // We found thread ID. + string tmp; + iss >> tmp; + CHECK(!tmp.empty()); + CHECK_EQ(']', tmp[tmp.size() - 1]); + thread_lineinfo = "THREADID " + tmp; + } + size_t index = thread_lineinfo.find(':'); + CHECK_NE(string::npos, index); + thread_lineinfo = thread_lineinfo.substr(0, index+1) + "LINE]"; + string rest; + std::getline(iss, rest); + return (before + logcode_date[0] + "DATE TIME__ " + thread_lineinfo + + MungeLine(rest)); +} + +static inline void StringReplace(string* str, + const string& oldsub, + const string& newsub) { + size_t pos = str->find(oldsub); + if (pos != string::npos) { + str->replace(pos, oldsub.size(), newsub.c_str()); + } +} + +static inline string Munge(const string& filename) { + FILE* fp = fopen(filename.c_str(), "rb"); + CHECK(fp != NULL) << filename << ": couldn't open"; + char buf[4096]; + string result; + while (fgets(buf, 4095, fp)) { + string line = MungeLine(buf); + char null_str[256]; + sprintf(null_str, "%p", static_cast<void*>(NULL)); + StringReplace(&line, "__NULLP__", null_str); + // Remove 0x prefix produced by %p. VC++ doesn't put the prefix. + StringReplace(&line, " 0x", " "); + + StringReplace(&line, "__SUCCESS__", StrError(0)); + StringReplace(&line, "__ENOENT__", StrError(ENOENT)); + StringReplace(&line, "__EINTR__", StrError(EINTR)); + StringReplace(&line, "__ENXIO__", StrError(ENXIO)); + StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC)); + result += line + "\n"; + } + fclose(fp); + return result; +} + +static inline void WriteToFile(const string& body, const string& file) { + FILE* fp = fopen(file.c_str(), "wb"); + fwrite(body.data(), 1, body.size(), fp); + fclose(fp); +} + +static inline bool MungeAndDiffTestStderr(const string& golden_filename) { + CapturedStream* cap = s_captured_streams[STDERR_FILENO]; + CHECK(cap) << ": did you forget CaptureTestStderr()?"; + + cap->StopCapture(); + + // Run munge + const string captured = Munge(cap->filename()); + const string golden = Munge(golden_filename); + if (captured != golden) { + fprintf(stderr, + "Test with golden file failed. We'll try to show the diff:\n"); + string munged_golden = golden_filename + ".munged"; + WriteToFile(golden, munged_golden); + string munged_captured = cap->filename() + ".munged"; + WriteToFile(captured, munged_captured); + string diffcmd("diff -u " + munged_golden + " " + munged_captured); + if (system(diffcmd.c_str()) != 0) { + fprintf(stderr, "diff command was failed.\n"); + } + unlink(munged_golden.c_str()); + unlink(munged_captured.c_str()); + return false; + } + LOG(INFO) << "Diff was successful"; + return true; +} + +// Save flags used from logging_unittest.cc. +#ifndef HAVE_LIB_GFLAGS +struct FlagSaver { + FlagSaver() + : v_(FLAGS_v), + stderrthreshold_(FLAGS_stderrthreshold), + logtostderr_(FLAGS_logtostderr), + alsologtostderr_(FLAGS_alsologtostderr) {} + ~FlagSaver() { + FLAGS_v = v_; + FLAGS_stderrthreshold = stderrthreshold_; + FLAGS_logtostderr = logtostderr_; + FLAGS_alsologtostderr = alsologtostderr_; + } + int v_; + int stderrthreshold_; + bool logtostderr_; + bool alsologtostderr_; +}; +#endif + +class Thread { + public: + virtual ~Thread() {} + + void SetJoinable(bool) {} +#if defined(OS_WINDOWS) || defined(OS_CYGWIN) + void Start() { + handle_ = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)&Thread::InvokeThread, + (LPVOID)this, + 0, + &th_); + CHECK(handle_) << "CreateThread"; + } + void Join() { + WaitForSingleObject(handle_, INFINITE); + } +#elif defined(HAVE_PTHREAD) + void Start() { + pthread_create(&th_, NULL, &Thread::InvokeThread, this); + } + void Join() { + pthread_join(th_, NULL); + } +#else +# error No thread implementation. +#endif + + protected: + virtual void Run() = 0; + + private: + static void* InvokeThread(void* self) { + ((Thread*)self)->Run(); + return NULL; + } + +#if defined(OS_WINDOWS) || defined(OS_CYGWIN) + HANDLE handle_; + DWORD th_; +#else + pthread_t th_; +#endif +}; + +static inline void SleepForMilliseconds(int t) { +#ifndef OS_WINDOWS + usleep(t * 1000); +#else + Sleep(t); +#endif +} + +// Add hook for operator new to ensure there are no memory allocation. + +void (*g_new_hook)() = NULL; + +_END_GOOGLE_NAMESPACE_ + +void* operator new(size_t size) throw(std::bad_alloc) { + if (GOOGLE_NAMESPACE::g_new_hook) { + GOOGLE_NAMESPACE::g_new_hook(); + } + return malloc(size); +} + +void* operator new[](size_t size) throw(std::bad_alloc) { + return ::operator new(size); +} + +void operator delete(void* p) throw() { + free(p); +} + +void operator delete[](void* p) throw() { + ::operator delete(p); +} diff --git a/ios/Pods/Flipper-Glog/src/logging.cc b/ios/Pods/Flipper-Glog/src/logging.cc new file mode 100644 index 000000000..0b5e6ee97 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/logging.cc @@ -0,0 +1,2088 @@ +// Copyright (c) 1999, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define _GNU_SOURCE 1 // needed for O_NOFOLLOW and pread()/pwrite() + +#include "utilities.h" + +#include <algorithm> +#include <assert.h> +#include <iomanip> +#include <string> +#ifdef HAVE_UNISTD_H +# include <unistd.h> // For _exit. +#endif +#include <climits> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_UTSNAME_H +# include <sys/utsname.h> // For uname. +#endif +#include <fcntl.h> +#include <cstdio> +#include <iostream> +#include <stdarg.h> +#include <stdlib.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif +#include <vector> +#include <errno.h> // for errno +#include <sstream> +#include "base/commandlineflags.h" // to get the program name +#include "glog/logging.h" +#include "glog/raw_logging.h" +#include "base/googleinit.h" + +#ifdef HAVE_STACKTRACE +# include "stacktrace.h" +#endif + +using std::string; +using std::vector; +using std::setw; +using std::setfill; +using std::hex; +using std::dec; +using std::min; +using std::ostream; +using std::ostringstream; + +using std::FILE; +using std::fwrite; +using std::fclose; +using std::fflush; +using std::fprintf; +using std::perror; + +#ifdef __QNX__ +using std::fdopen; +#endif + +#ifdef _WIN32 +#define fdopen _fdopen +#endif + +// There is no thread annotation support. +#define EXCLUSIVE_LOCKS_REQUIRED(mu) + +static bool BoolFromEnv(const char *varname, bool defval) { + const char* const valstr = getenv(varname); + if (!valstr) { + return defval; + } + return memchr("tTyY1\0", valstr[0], 6) != NULL; +} + +GLOG_DEFINE_bool(logtostderr, BoolFromEnv("GOOGLE_LOGTOSTDERR", false), + "log messages go to stderr instead of logfiles"); +GLOG_DEFINE_bool(alsologtostderr, BoolFromEnv("GOOGLE_ALSOLOGTOSTDERR", false), + "log messages go to stderr in addition to logfiles"); +GLOG_DEFINE_bool(colorlogtostderr, false, + "color messages logged to stderr (if supported by terminal)"); +#ifdef OS_LINUX +GLOG_DEFINE_bool(drop_log_memory, true, "Drop in-memory buffers of log contents. " + "Logs can grow very quickly and they are rarely read before they " + "need to be evicted from memory. Instead, drop them from memory " + "as soon as they are flushed to disk."); +_START_GOOGLE_NAMESPACE_ +namespace logging { +static const int64 kPageSize = getpagesize(); +} +_END_GOOGLE_NAMESPACE_ +#endif + +// By default, errors (including fatal errors) get logged to stderr as +// well as the file. +// +// The default is ERROR instead of FATAL so that users can see problems +// when they run a program without having to look in another file. +DEFINE_int32(stderrthreshold, + GOOGLE_NAMESPACE::GLOG_ERROR, + "log messages at or above this level are copied to stderr in " + "addition to logfiles. This flag obsoletes --alsologtostderr."); + +GLOG_DEFINE_string(alsologtoemail, "", + "log messages go to these email addresses " + "in addition to logfiles"); +GLOG_DEFINE_bool(log_prefix, true, + "Prepend the log prefix to the start of each log line"); +GLOG_DEFINE_int32(minloglevel, 0, "Messages logged at a lower level than this don't " + "actually get logged anywhere"); +GLOG_DEFINE_int32(logbuflevel, 0, + "Buffer log messages logged at this level or lower" + " (-1 means don't buffer; 0 means buffer INFO only;" + " ...)"); +GLOG_DEFINE_int32(logbufsecs, 30, + "Buffer log messages for at most this many seconds"); +GLOG_DEFINE_int32(logemaillevel, 999, + "Email log messages logged at this level or higher" + " (0 means email all; 3 means email FATAL only;" + " ...)"); +GLOG_DEFINE_string(logmailer, "/bin/mail", + "Mailer used to send logging email"); + +// Compute the default value for --log_dir +static const char* DefaultLogDir() { + const char* env; + env = getenv("GOOGLE_LOG_DIR"); + if (env != NULL && env[0] != '\0') { + return env; + } + env = getenv("TEST_TMPDIR"); + if (env != NULL && env[0] != '\0') { + return env; + } + return ""; +} + +GLOG_DEFINE_int32(logfile_mode, 0664, "Log file mode/permissions."); + +GLOG_DEFINE_string(log_dir, DefaultLogDir(), + "If specified, logfiles are written into this directory instead " + "of the default logging directory."); +GLOG_DEFINE_string(log_link, "", "Put additional links to the log " + "files in this directory"); + +GLOG_DEFINE_int32(max_log_size, 1800, + "approx. maximum log file size (in MB). A value of 0 will " + "be silently overridden to 1."); + +GLOG_DEFINE_bool(stop_logging_if_full_disk, false, + "Stop attempting to log to disk if the disk is full."); + +GLOG_DEFINE_string(log_backtrace_at, "", + "Emit a backtrace when logging at file:linenum."); + +// TODO(hamaji): consider windows +#define PATH_SEPARATOR '/' + +#ifndef HAVE_PREAD +#if defined(OS_WINDOWS) +#include <BaseTsd.h> +#define ssize_t SSIZE_T +#endif +static ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) + return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) + return -1; + ssize_t len = read(fd, buf, count); + if (len < 0) + return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) + return -1; + return len; +} +#endif // !HAVE_PREAD + +#ifndef HAVE_PWRITE +static ssize_t pwrite(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) + return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) + return -1; + ssize_t len = write(fd, buf, count); + if (len < 0) + return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) + return -1; + return len; +} +#endif // !HAVE_PWRITE + +static void GetHostName(string* hostname) { +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname buf; + if (0 != uname(&buf)) { + // ensure null termination on failure + *buf.nodename = '\0'; + } + *hostname = buf.nodename; +#elif defined(OS_WINDOWS) + char buf[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD len = MAX_COMPUTERNAME_LENGTH + 1; + if (GetComputerNameA(buf, &len)) { + *hostname = buf; + } else { + hostname->clear(); + } +#else +# warning There is no way to retrieve the host name. + *hostname = "(unknown)"; +#endif +} + +// Returns true iff terminal supports using colors in output. +static bool TerminalSupportsColor() { + bool term_supports_color = false; +#ifdef OS_WINDOWS + // on Windows TERM variable is usually not set, but the console does + // support colors. + term_supports_color = true; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = getenv("TERM"); + if (term != NULL && term[0] != '\0') { + term_supports_color = + !strcmp(term, "xterm") || + !strcmp(term, "xterm-color") || + !strcmp(term, "xterm-256color") || + !strcmp(term, "screen-256color") || + !strcmp(term, "screen") || + !strcmp(term, "linux") || + !strcmp(term, "cygwin"); + } +#endif + return term_supports_color; +} + +_START_GOOGLE_NAMESPACE_ + +enum GLogColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +static GLogColor SeverityToColor(LogSeverity severity) { + assert(severity >= 0 && severity < NUM_SEVERITIES); + GLogColor color = COLOR_DEFAULT; + switch (severity) { + case GLOG_INFO: + color = COLOR_DEFAULT; + break; + case GLOG_WARNING: + color = COLOR_YELLOW; + break; + case GLOG_ERROR: + case GLOG_FATAL: + color = COLOR_RED; + break; + default: + // should never get here. + assert(false); + } + return color; +} + +#ifdef OS_WINDOWS + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GLogColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. +const char* GetAnsiColorCode(GLogColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + case COLOR_DEFAULT: return ""; + }; + return NULL; // stop warning about return type. +} + +#endif // OS_WINDOWS + +// Safely get max_log_size, overriding to 1 if it somehow gets defined as 0 +static int32 MaxLogSize() { + return (FLAGS_max_log_size > 0 ? FLAGS_max_log_size : 1); +} + +// An arbitrary limit on the length of a single log message. This +// is so that streaming can be done more efficiently. +const size_t LogMessage::kMaxLogMessageLen = 30000; + +struct LogMessage::LogMessageData { + LogMessageData(); + + int preserved_errno_; // preserved errno + // Buffer space; contains complete message text. + char message_text_[LogMessage::kMaxLogMessageLen+1]; + LogStream stream_; + char severity_; // What level is this LogMessage logged at? + int line_; // line number where logging call is. + void (LogMessage::*send_method_)(); // Call this in destructor to send + union { // At most one of these is used: union to keep the size low. + LogSink* sink_; // NULL or sink to send message to + std::vector<std::string>* outvec_; // NULL or vector to push message onto + std::string* message_; // NULL or string to write message into + }; + time_t timestamp_; // Time of creation of LogMessage + struct ::tm tm_time_; // Time of creation of LogMessage + size_t num_prefix_chars_; // # of chars of prefix in this message + size_t num_chars_to_log_; // # of chars of msg to send to log + size_t num_chars_to_syslog_; // # of chars of msg to send to syslog + const char* basename_; // basename of file that called LOG + const char* fullname_; // fullname of file that called LOG + bool has_been_flushed_; // false => data has not been flushed + bool first_fatal_; // true => this was first fatal msg + + private: + LogMessageData(const LogMessageData&); + void operator=(const LogMessageData&); +}; + +// A mutex that allows only one thread to log at a time, to keep things from +// getting jumbled. Some other very uncommon logging operations (like +// changing the destination file for log messages of a given severity) also +// lock this mutex. Please be sure that anybody who might possibly need to +// lock it does so. +static Mutex log_mutex; + +// Number of messages sent at each severity. Under log_mutex. +int64 LogMessage::num_messages_[NUM_SEVERITIES] = {0, 0, 0, 0}; + +// Globally disable log writing (if disk is full) +static bool stop_writing = false; + +const char*const LogSeverityNames[NUM_SEVERITIES] = { + "INFO", "WARNING", "ERROR", "FATAL" +}; + +// Has the user called SetExitOnDFatal(true)? +static bool exit_on_dfatal = true; + +const char* GetLogSeverityName(LogSeverity severity) { + return LogSeverityNames[severity]; +} + +static bool SendEmailInternal(const char*dest, const char *subject, + const char*body, bool use_logging); + +base::Logger::~Logger() { +} + +namespace { + +// Encapsulates all file-system related state +class LogFileObject : public base::Logger { + public: + LogFileObject(LogSeverity severity, const char* base_filename); + ~LogFileObject(); + + virtual void Write(bool force_flush, // Should we force a flush here? + time_t timestamp, // Timestamp for this entry + const char* message, + int message_len); + + // Configuration options + void SetBasename(const char* basename); + void SetExtension(const char* ext); + void SetSymlinkBasename(const char* symlink_basename); + + // Normal flushing routine + virtual void Flush(); + + // It is the actual file length for the system loggers, + // i.e., INFO, ERROR, etc. + virtual uint32 LogSize() { + MutexLock l(&lock_); + return file_length_; + } + + // Internal flush routine. Exposed so that FlushLogFilesUnsafe() + // can avoid grabbing a lock. Usually Flush() calls it after + // acquiring lock_. + void FlushUnlocked(); + + private: + static const uint32 kRolloverAttemptFrequency = 0x20; + + Mutex lock_; + bool base_filename_selected_; + string base_filename_; + string symlink_basename_; + string filename_extension_; // option users can specify (eg to add port#) + FILE* file_; + LogSeverity severity_; + uint32 bytes_since_flush_; + uint32 file_length_; + unsigned int rollover_attempt_; + int64 next_flush_time_; // cycle count at which to flush log + + // Actually create a logfile using the value of base_filename_ and the + // supplied argument time_pid_string + // REQUIRES: lock_ is held + bool CreateLogfile(const string& time_pid_string); +}; + +} // namespace + +class LogDestination { + public: + friend class LogMessage; + friend void ReprintFatalMessage(); + friend base::Logger* base::GetLogger(LogSeverity); + friend void base::SetLogger(LogSeverity, base::Logger*); + + // These methods are just forwarded to by their global versions. + static void SetLogDestination(LogSeverity severity, + const char* base_filename); + static void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + static void AddLogSink(LogSink *destination); + static void RemoveLogSink(LogSink *destination); + static void SetLogFilenameExtension(const char* filename_extension); + static void SetStderrLogging(LogSeverity min_severity); + static void SetEmailLogging(LogSeverity min_severity, const char* addresses); + static void LogToStderr(); + // Flush all log files that are at least at the given severity level + static void FlushLogFiles(int min_severity); + static void FlushLogFilesUnsafe(int min_severity); + + // we set the maximum size of our packet to be 1400, the logic being + // to prevent fragmentation. + // Really this number is arbitrary. + static const int kNetworkBytes = 1400; + + static const string& hostname(); + static const bool& terminal_supports_color() { + return terminal_supports_color_; + } + + static void DeleteLogDestinations(); + + private: + LogDestination(LogSeverity severity, const char* base_filename); + ~LogDestination() { } + + // Take a log message of a particular severity and log it to stderr + // iff it's of a high enough severity to deserve it. + static void MaybeLogToStderr(LogSeverity severity, const char* message, + size_t len); + + // Take a log message of a particular severity and log it to email + // iff it's of a high enough severity to deserve it. + static void MaybeLogToEmail(LogSeverity severity, const char* message, + size_t len); + // Take a log message of a particular severity and log it to a file + // iff the base filename is not "" (which means "don't log to me") + static void MaybeLogToLogfile(LogSeverity severity, + time_t timestamp, + const char* message, size_t len); + // Take a log message of a particular severity and log it to the file + // for that severity and also for all files with severity less than + // this severity. + static void LogToAllLogfiles(LogSeverity severity, + time_t timestamp, + const char* message, size_t len); + + // Send logging info to all registered sinks. + static void LogToSinks(LogSeverity severity, + const char *full_filename, + const char *base_filename, + int line, + const struct ::tm* tm_time, + const char* message, + size_t message_len); + + // Wait for all registered sinks via WaitTillSent + // including the optional one in "data". + static void WaitForSinks(LogMessage::LogMessageData* data); + + static LogDestination* log_destination(LogSeverity severity); + + LogFileObject fileobject_; + base::Logger* logger_; // Either &fileobject_, or wrapper around it + + static LogDestination* log_destinations_[NUM_SEVERITIES]; + static LogSeverity email_logging_severity_; + static string addresses_; + static string hostname_; + static bool terminal_supports_color_; + + // arbitrary global logging destinations. + static vector<LogSink*>* sinks_; + + // Protects the vector sinks_, + // but not the LogSink objects its elements reference. + static Mutex sink_mutex_; + + // Disallow + LogDestination(const LogDestination&); + LogDestination& operator=(const LogDestination&); +}; + +// Errors do not get logged to email by default. +LogSeverity LogDestination::email_logging_severity_ = 99999; + +string LogDestination::addresses_; +string LogDestination::hostname_; + +vector<LogSink*>* LogDestination::sinks_ = NULL; +Mutex LogDestination::sink_mutex_; +bool LogDestination::terminal_supports_color_ = TerminalSupportsColor(); + +/* static */ +const string& LogDestination::hostname() { + if (hostname_.empty()) { + GetHostName(&hostname_); + if (hostname_.empty()) { + hostname_ = "(unknown)"; + } + } + return hostname_; +} + +LogDestination::LogDestination(LogSeverity severity, + const char* base_filename) + : fileobject_(severity, base_filename), + logger_(&fileobject_) { +} + +inline void LogDestination::FlushLogFilesUnsafe(int min_severity) { + // assume we have the log_mutex or we simply don't care + // about it + for (int i = min_severity; i < NUM_SEVERITIES; i++) { + LogDestination* log = log_destinations_[i]; + if (log != NULL) { + // Flush the base fileobject_ logger directly instead of going + // through any wrappers to reduce chance of deadlock. + log->fileobject_.FlushUnlocked(); + } + } +} + +inline void LogDestination::FlushLogFiles(int min_severity) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&log_mutex); + for (int i = min_severity; i < NUM_SEVERITIES; i++) { + LogDestination* log = log_destination(i); + if (log != NULL) { + log->logger_->Flush(); + } + } +} + +inline void LogDestination::SetLogDestination(LogSeverity severity, + const char* base_filename) { + assert(severity >= 0 && severity < NUM_SEVERITIES); + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&log_mutex); + log_destination(severity)->fileobject_.SetBasename(base_filename); +} + +inline void LogDestination::SetLogSymlink(LogSeverity severity, + const char* symlink_basename) { + CHECK_GE(severity, 0); + CHECK_LT(severity, NUM_SEVERITIES); + MutexLock l(&log_mutex); + log_destination(severity)->fileobject_.SetSymlinkBasename(symlink_basename); +} + +inline void LogDestination::AddLogSink(LogSink *destination) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&sink_mutex_); + if (!sinks_) sinks_ = new vector<LogSink*>; + sinks_->push_back(destination); +} + +inline void LogDestination::RemoveLogSink(LogSink *destination) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&sink_mutex_); + // This doesn't keep the sinks in order, but who cares? + if (sinks_) { + for (int i = sinks_->size() - 1; i >= 0; i--) { + if ((*sinks_)[i] == destination) { + (*sinks_)[i] = (*sinks_)[sinks_->size() - 1]; + sinks_->pop_back(); + break; + } + } + } +} + +inline void LogDestination::SetLogFilenameExtension(const char* ext) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&log_mutex); + for ( int severity = 0; severity < NUM_SEVERITIES; ++severity ) { + log_destination(severity)->fileobject_.SetExtension(ext); + } +} + +inline void LogDestination::SetStderrLogging(LogSeverity min_severity) { + assert(min_severity >= 0 && min_severity < NUM_SEVERITIES); + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&log_mutex); + FLAGS_stderrthreshold = min_severity; +} + +inline void LogDestination::LogToStderr() { + // *Don't* put this stuff in a mutex lock, since SetStderrLogging & + // SetLogDestination already do the locking! + SetStderrLogging(0); // thus everything is "also" logged to stderr + for ( int i = 0; i < NUM_SEVERITIES; ++i ) { + SetLogDestination(i, ""); // "" turns off logging to a logfile + } +} + +inline void LogDestination::SetEmailLogging(LogSeverity min_severity, + const char* addresses) { + assert(min_severity >= 0 && min_severity < NUM_SEVERITIES); + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + MutexLock l(&log_mutex); + LogDestination::email_logging_severity_ = min_severity; + LogDestination::addresses_ = addresses; +} + +static void ColoredWriteToStderr(LogSeverity severity, + const char* message, size_t len) { + const GLogColor color = + (LogDestination::terminal_supports_color() && FLAGS_colorlogtostderr) ? + SeverityToColor(severity) : COLOR_DEFAULT; + + // Avoid using cerr from this module since we may get called during + // exit code, and cerr may be partially or fully destroyed by then. + if (COLOR_DEFAULT == color) { + fwrite(message, len, 1, stderr); + return; + } +#ifdef OS_WINDOWS + const HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stderr_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stderr); + SetConsoleTextAttribute(stderr_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + fwrite(message, len, 1, stderr); + fflush(stderr); + // Restores the text color. + SetConsoleTextAttribute(stderr_handle, old_color_attrs); +#else + fprintf(stderr, "\033[0;3%sm", GetAnsiColorCode(color)); + fwrite(message, len, 1, stderr); + fprintf(stderr, "\033[m"); // Resets the terminal to default. +#endif // OS_WINDOWS +} + +static void WriteToStderr(const char* message, size_t len) { + // Avoid using cerr from this module since we may get called during + // exit code, and cerr may be partially or fully destroyed by then. + fwrite(message, len, 1, stderr); +} + +inline void LogDestination::MaybeLogToStderr(LogSeverity severity, + const char* message, size_t len) { + if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) { + ColoredWriteToStderr(severity, message, len); +#ifdef OS_WINDOWS + // On Windows, also output to the debugger + ::OutputDebugStringA(string(message,len).c_str()); +#endif + } +} + + +inline void LogDestination::MaybeLogToEmail(LogSeverity severity, + const char* message, size_t len) { + if (severity >= email_logging_severity_ || + severity >= FLAGS_logemaillevel) { + string to(FLAGS_alsologtoemail); + if (!addresses_.empty()) { + if (!to.empty()) { + to += ","; + } + to += addresses_; + } + const string subject(string("[LOG] ") + LogSeverityNames[severity] + ": " + + glog_internal_namespace_::ProgramInvocationShortName()); + string body(hostname()); + body += "\n\n"; + body.append(message, len); + + // should NOT use SendEmail(). The caller of this function holds the + // log_mutex and SendEmail() calls LOG/VLOG which will block trying to + // acquire the log_mutex object. Use SendEmailInternal() and set + // use_logging to false. + SendEmailInternal(to.c_str(), subject.c_str(), body.c_str(), false); + } +} + + +inline void LogDestination::MaybeLogToLogfile(LogSeverity severity, + time_t timestamp, + const char* message, + size_t len) { + const bool should_flush = severity > FLAGS_logbuflevel; + LogDestination* destination = log_destination(severity); + destination->logger_->Write(should_flush, timestamp, message, len); +} + +inline void LogDestination::LogToAllLogfiles(LogSeverity severity, + time_t timestamp, + const char* message, + size_t len) { + + if ( FLAGS_logtostderr ) { // global flag: never log to file + ColoredWriteToStderr(severity, message, len); + } else { + for (int i = severity; i >= 0; --i) + LogDestination::MaybeLogToLogfile(i, timestamp, message, len); + } +} + +inline void LogDestination::LogToSinks(LogSeverity severity, + const char *full_filename, + const char *base_filename, + int line, + const struct ::tm* tm_time, + const char* message, + size_t message_len) { + ReaderMutexLock l(&sink_mutex_); + if (sinks_) { + for (int i = sinks_->size() - 1; i >= 0; i--) { + (*sinks_)[i]->send(severity, full_filename, base_filename, + line, tm_time, message, message_len); + } + } +} + +inline void LogDestination::WaitForSinks(LogMessage::LogMessageData* data) { + ReaderMutexLock l(&sink_mutex_); + if (sinks_) { + for (int i = sinks_->size() - 1; i >= 0; i--) { + (*sinks_)[i]->WaitTillSent(); + } + } + const bool send_to_sink = + (data->send_method_ == &LogMessage::SendToSink) || + (data->send_method_ == &LogMessage::SendToSinkAndLog); + if (send_to_sink && data->sink_ != NULL) { + data->sink_->WaitTillSent(); + } +} + +LogDestination* LogDestination::log_destinations_[NUM_SEVERITIES]; + +inline LogDestination* LogDestination::log_destination(LogSeverity severity) { + assert(severity >=0 && severity < NUM_SEVERITIES); + if (!log_destinations_[severity]) { + log_destinations_[severity] = new LogDestination(severity, NULL); + } + return log_destinations_[severity]; +} + +void LogDestination::DeleteLogDestinations() { + for (int severity = 0; severity < NUM_SEVERITIES; ++severity) { + delete log_destinations_[severity]; + log_destinations_[severity] = NULL; + } + MutexLock l(&sink_mutex_); + delete sinks_; + sinks_ = NULL; +} + +namespace { + +LogFileObject::LogFileObject(LogSeverity severity, + const char* base_filename) + : base_filename_selected_(base_filename != NULL), + base_filename_((base_filename != NULL) ? base_filename : ""), + symlink_basename_(glog_internal_namespace_::ProgramInvocationShortName()), + filename_extension_(), + file_(NULL), + severity_(severity), + bytes_since_flush_(0), + file_length_(0), + rollover_attempt_(kRolloverAttemptFrequency-1), + next_flush_time_(0) { + assert(severity >= 0); + assert(severity < NUM_SEVERITIES); +} + +LogFileObject::~LogFileObject() { + MutexLock l(&lock_); + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } +} + +void LogFileObject::SetBasename(const char* basename) { + MutexLock l(&lock_); + base_filename_selected_ = true; + if (base_filename_ != basename) { + // Get rid of old log file since we are changing names + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + rollover_attempt_ = kRolloverAttemptFrequency-1; + } + base_filename_ = basename; + } +} + +void LogFileObject::SetExtension(const char* ext) { + MutexLock l(&lock_); + if (filename_extension_ != ext) { + // Get rid of old log file since we are changing names + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + rollover_attempt_ = kRolloverAttemptFrequency-1; + } + filename_extension_ = ext; + } +} + +void LogFileObject::SetSymlinkBasename(const char* symlink_basename) { + MutexLock l(&lock_); + symlink_basename_ = symlink_basename; +} + +void LogFileObject::Flush() { + MutexLock l(&lock_); + FlushUnlocked(); +} + +void LogFileObject::FlushUnlocked(){ + if (file_ != NULL) { + fflush(file_); + bytes_since_flush_ = 0; + } + // Figure out when we are due for another flush. + const int64 next = (FLAGS_logbufsecs + * static_cast<int64>(1000000)); // in usec + next_flush_time_ = CycleClock_Now() + UsecToCycles(next); +} + +bool LogFileObject::CreateLogfile(const string& time_pid_string) { + string string_filename = base_filename_+filename_extension_+ + time_pid_string; + const char* filename = string_filename.c_str(); + int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, FLAGS_logfile_mode); + if (fd == -1) return false; +#ifdef HAVE_FCNTL + // Mark the file close-on-exec. We don't really care if this fails + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + file_ = fdopen(fd, "a"); // Make a FILE*. + if (file_ == NULL) { // Man, we're screwed! + close(fd); + unlink(filename); // Erase the half-baked evidence: an unusable log file + return false; + } + + // We try to create a symlink called <program_name>.<severity>, + // which is easier to use. (Every time we create a new logfile, + // we destroy the old symlink and create a new one, so it always + // points to the latest logfile.) If it fails, we're sad but it's + // no error. + if (!symlink_basename_.empty()) { + // take directory from filename + const char* slash = strrchr(filename, PATH_SEPARATOR); + const string linkname = + symlink_basename_ + '.' + LogSeverityNames[severity_]; + string linkpath; + if ( slash ) linkpath = string(filename, slash-filename+1); // get dirname + linkpath += linkname; + unlink(linkpath.c_str()); // delete old one if it exists + +#if defined(OS_WINDOWS) + // TODO(hamaji): Create lnk file on Windows? +#elif defined(HAVE_UNISTD_H) + // We must have unistd.h. + // Make the symlink be relative (in the same dir) so that if the + // entire log directory gets relocated the link is still valid. + const char *linkdest = slash ? (slash + 1) : filename; + if (symlink(linkdest, linkpath.c_str()) != 0) { + // silently ignore failures + } + + // Make an additional link to the log file in a place specified by + // FLAGS_log_link, if indicated + if (!FLAGS_log_link.empty()) { + linkpath = FLAGS_log_link + "/" + linkname; + unlink(linkpath.c_str()); // delete old one if it exists + if (symlink(filename, linkpath.c_str()) != 0) { + // silently ignore failures + } + } +#endif + } + + return true; // Everything worked +} + +void LogFileObject::Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) { + MutexLock l(&lock_); + + // We don't log if the base_name_ is "" (which means "don't write") + if (base_filename_selected_ && base_filename_.empty()) { + return; + } + + if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() || + PidHasChanged()) { + if (file_ != NULL) fclose(file_); + file_ = NULL; + file_length_ = bytes_since_flush_ = 0; + rollover_attempt_ = kRolloverAttemptFrequency-1; + } + + // If there's no destination file, make one before outputting + if (file_ == NULL) { + // Try to rollover the log file every 32 log messages. The only time + // this could matter would be when we have trouble creating the log + // file. If that happens, we'll lose lots of log messages, of course! + if (++rollover_attempt_ != kRolloverAttemptFrequency) return; + rollover_attempt_ = 0; + + struct ::tm tm_time; + localtime_r(×tamp, &tm_time); + + // The logfile's filename will have the date/time & pid in it + ostringstream time_pid_stream; + time_pid_stream.fill('0'); + time_pid_stream << 1900+tm_time.tm_year + << setw(2) << 1+tm_time.tm_mon + << setw(2) << tm_time.tm_mday + << '-' + << setw(2) << tm_time.tm_hour + << setw(2) << tm_time.tm_min + << setw(2) << tm_time.tm_sec + << '.' + << GetMainThreadPid(); + const string& time_pid_string = time_pid_stream.str(); + + if (base_filename_selected_) { + if (!CreateLogfile(time_pid_string)) { + perror("Could not create log file"); + fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n", + time_pid_string.c_str()); + return; + } + } else { + // If no base filename for logs of this severity has been set, use a + // default base filename of + // "<program name>.<hostname>.<user name>.log.<severity level>.". So + // logfiles will have names like + // webserver.examplehost.root.log.INFO.19990817-150000.4354, where + // 19990817 is a date (1999 August 17), 150000 is a time (15:00:00), + // and 4354 is the pid of the logging process. The date & time reflect + // when the file was created for output. + // + // Where does the file get put? Successively try the directories + // "/tmp", and "." + string stripped_filename( + glog_internal_namespace_::ProgramInvocationShortName()); + string hostname; + GetHostName(&hostname); + + string uidname = MyUserName(); + // We should not call CHECK() here because this function can be + // called after holding on to log_mutex. We don't want to + // attempt to hold on to the same mutex, and get into a + // deadlock. Simply use a name like invalid-user. + if (uidname.empty()) uidname = "invalid-user"; + + stripped_filename = stripped_filename+'.'+hostname+'.' + +uidname+".log." + +LogSeverityNames[severity_]+'.'; + // We're going to (potentially) try to put logs in several different dirs + const vector<string> & log_dirs = GetLoggingDirectories(); + + // Go through the list of dirs, and try to create the log file in each + // until we succeed or run out of options + bool success = false; + for (vector<string>::const_iterator dir = log_dirs.begin(); + dir != log_dirs.end(); + ++dir) { + base_filename_ = *dir + "/" + stripped_filename; + if ( CreateLogfile(time_pid_string) ) { + success = true; + break; + } + } + // If we never succeeded, we have to give up + if ( success == false ) { + perror("Could not create logging file"); + fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!", + time_pid_string.c_str()); + return; + } + } + + // Write a header message into the log file + ostringstream file_header_stream; + file_header_stream.fill('0'); + file_header_stream << "Log file created at: " + << 1900+tm_time.tm_year << '/' + << setw(2) << 1+tm_time.tm_mon << '/' + << setw(2) << tm_time.tm_mday + << ' ' + << setw(2) << tm_time.tm_hour << ':' + << setw(2) << tm_time.tm_min << ':' + << setw(2) << tm_time.tm_sec << '\n' + << "Running on machine: " + << LogDestination::hostname() << '\n' + << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu " + << "threadid file:line] msg" << '\n'; + const string& file_header_string = file_header_stream.str(); + + const int header_len = file_header_string.size(); + fwrite(file_header_string.data(), 1, header_len, file_); + file_length_ += header_len; + bytes_since_flush_ += header_len; + } + + // Write to LOG file + if ( !stop_writing ) { + // fwrite() doesn't return an error when the disk is full, for + // messages that are less than 4096 bytes. When the disk is full, + // it returns the message length for messages that are less than + // 4096 bytes. fwrite() returns 4096 for message lengths that are + // greater than 4096, thereby indicating an error. + errno = 0; + fwrite(message, 1, message_len, file_); + if ( FLAGS_stop_logging_if_full_disk && + errno == ENOSPC ) { // disk full, stop writing to disk + stop_writing = true; // until the disk is + return; + } else { + file_length_ += message_len; + bytes_since_flush_ += message_len; + } + } else { + if ( CycleClock_Now() >= next_flush_time_ ) + stop_writing = false; // check to see if disk has free space. + return; // no need to flush + } + + // See important msgs *now*. Also, flush logs at least every 10^6 chars, + // or every "FLAGS_logbufsecs" seconds. + if ( force_flush || + (bytes_since_flush_ >= 1000000) || + (CycleClock_Now() >= next_flush_time_) ) { + FlushUnlocked(); +#ifdef OS_LINUX + if (FLAGS_drop_log_memory) { + if (file_length_ >= logging::kPageSize) { + // don't evict the most recent page + uint32 len = file_length_ & ~(logging::kPageSize - 1); + posix_fadvise(fileno(file_), 0, len, POSIX_FADV_DONTNEED); + } + } +#endif + } +} + +} // namespace + + +// Static log data space to avoid alloc failures in a LOG(FATAL) +// +// Since multiple threads may call LOG(FATAL), and we want to preserve +// the data from the first call, we allocate two sets of space. One +// for exclusive use by the first thread, and one for shared use by +// all other threads. +static Mutex fatal_msg_lock; +static CrashReason crash_reason; +static bool fatal_msg_exclusive = true; +static LogMessage::LogMessageData fatal_msg_data_exclusive; +static LogMessage::LogMessageData fatal_msg_data_shared; + +LogMessage::LogMessageData::LogMessageData() + : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) { +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + int ctr, void (LogMessage::*send_method)()) + : allocated_(NULL) { + Init(file, line, severity, send_method); + data_->stream_.set_ctr(ctr); +} + +LogMessage::LogMessage(const char* file, int line, + const CheckOpString& result) + : allocated_(NULL) { + Init(file, line, GLOG_FATAL, &LogMessage::SendToLog); + stream() << "Check failed: " << (*result.str_) << " "; +} + +LogMessage::LogMessage(const char* file, int line) + : allocated_(NULL) { + Init(file, line, GLOG_INFO, &LogMessage::SendToLog); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : allocated_(NULL) { + Init(file, line, severity, &LogMessage::SendToLog); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + LogSink* sink, bool also_send_to_log) + : allocated_(NULL) { + Init(file, line, severity, also_send_to_log ? &LogMessage::SendToSinkAndLog : + &LogMessage::SendToSink); + data_->sink_ = sink; // override Init()'s setting to NULL +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + vector<string> *outvec) + : allocated_(NULL) { + Init(file, line, severity, &LogMessage::SaveOrSendToLog); + data_->outvec_ = outvec; // override Init()'s setting to NULL +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + string *message) + : allocated_(NULL) { + Init(file, line, severity, &LogMessage::WriteToStringAndLog); + data_->message_ = message; // override Init()'s setting to NULL +} + +void LogMessage::Init(const char* file, + int line, + LogSeverity severity, + void (LogMessage::*send_method)()) { + allocated_ = NULL; + if (severity != GLOG_FATAL || !exit_on_dfatal) { + allocated_ = new LogMessageData(); + data_ = allocated_; + data_->first_fatal_ = false; + } else { + MutexLock l(&fatal_msg_lock); + if (fatal_msg_exclusive) { + fatal_msg_exclusive = false; + data_ = &fatal_msg_data_exclusive; + data_->first_fatal_ = true; + } else { + data_ = &fatal_msg_data_shared; + data_->first_fatal_ = false; + } + } + + stream().fill('0'); + data_->preserved_errno_ = errno; + data_->severity_ = severity; + data_->line_ = line; + data_->send_method_ = send_method; + data_->sink_ = NULL; + data_->outvec_ = NULL; + WallTime now = WallTime_Now(); + data_->timestamp_ = static_cast<time_t>(now); + localtime_r(&data_->timestamp_, &data_->tm_time_); + int usecs = static_cast<int>((now - data_->timestamp_) * 1000000); + RawLog__SetLastTime(data_->tm_time_, usecs); + + data_->num_chars_to_log_ = 0; + data_->num_chars_to_syslog_ = 0; + data_->basename_ = const_basename(file); + data_->fullname_ = file; + data_->has_been_flushed_ = false; + + // If specified, prepend a prefix to each line. For example: + // I1018 160715 f5d4fbb0 logging.cc:1153] + // (log level, GMT month, date, time, thread_id, file basename, line) + // We exclude the thread_id for the default thread. + if (FLAGS_log_prefix && (line != kNoLogPrefix)) { + stream() << LogSeverityNames[severity][0] + << setw(2) << 1+data_->tm_time_.tm_mon + << setw(2) << data_->tm_time_.tm_mday + << ' ' + << setw(2) << data_->tm_time_.tm_hour << ':' + << setw(2) << data_->tm_time_.tm_min << ':' + << setw(2) << data_->tm_time_.tm_sec << "." + << setw(6) << usecs + << ' ' + << setfill(' ') << setw(5) + << static_cast<unsigned int>(GetTID()) << setfill('0') + << ' ' + << data_->basename_ << ':' << data_->line_ << "] "; + } + data_->num_prefix_chars_ = data_->stream_.pcount(); + + if (!FLAGS_log_backtrace_at.empty()) { + char fileline[128]; + snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line); +#ifdef HAVE_STACKTRACE + if (!strcmp(FLAGS_log_backtrace_at.c_str(), fileline)) { + string stacktrace; + DumpStackTraceToString(&stacktrace); + stream() << " (stacktrace:\n" << stacktrace << ") "; + } +#endif + } +} + +LogMessage::~LogMessage() { + Flush(); + delete allocated_; +} + +int LogMessage::preserved_errno() const { + return data_->preserved_errno_; +} + +ostream& LogMessage::stream() { + return data_->stream_; +} + +// Flush buffered message, called by the destructor, or any other function +// that needs to synchronize the log. +void LogMessage::Flush() { + if (data_->has_been_flushed_ || data_->severity_ < FLAGS_minloglevel) + return; + + data_->num_chars_to_log_ = data_->stream_.pcount(); + data_->num_chars_to_syslog_ = + data_->num_chars_to_log_ - data_->num_prefix_chars_; + + // Do we need to add a \n to the end of this message? + bool append_newline = + (data_->message_text_[data_->num_chars_to_log_-1] != '\n'); + char original_final_char = '\0'; + + // If we do need to add a \n, we'll do it by violating the memory of the + // ostrstream buffer. This is quick, and we'll make sure to undo our + // modification before anything else is done with the ostrstream. It + // would be preferable not to do things this way, but it seems to be + // the best way to deal with this. + if (append_newline) { + original_final_char = data_->message_text_[data_->num_chars_to_log_]; + data_->message_text_[data_->num_chars_to_log_++] = '\n'; + } + + // Prevent any subtle race conditions by wrapping a mutex lock around + // the actual logging action per se. + { + MutexLock l(&log_mutex); + (this->*(data_->send_method_))(); + ++num_messages_[static_cast<int>(data_->severity_)]; + } + LogDestination::WaitForSinks(data_); + + if (append_newline) { + // Fix the ostrstream back how it was before we screwed with it. + // It's 99.44% certain that we don't need to worry about doing this. + data_->message_text_[data_->num_chars_to_log_-1] = original_final_char; + } + + // If errno was already set before we enter the logging call, we'll + // set it back to that value when we return from the logging call. + // It happens often that we log an error message after a syscall + // failure, which can potentially set the errno to some other + // values. We would like to preserve the original errno. + if (data_->preserved_errno_ != 0) { + errno = data_->preserved_errno_; + } + + // Note that this message is now safely logged. If we're asked to flush + // again, as a result of destruction, say, we'll do nothing on future calls. + data_->has_been_flushed_ = true; +} + +// Copy of first FATAL log message so that we can print it out again +// after all the stack traces. To preserve legacy behavior, we don't +// use fatal_msg_data_exclusive. +static time_t fatal_time; +static char fatal_message[256]; + +void ReprintFatalMessage() { + if (fatal_message[0]) { + const int n = strlen(fatal_message); + if (!FLAGS_logtostderr) { + // Also write to stderr (don't color to avoid terminal checks) + WriteToStderr(fatal_message, n); + } + LogDestination::LogToAllLogfiles(GLOG_ERROR, fatal_time, fatal_message, n); + } +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + static bool already_warned_before_initgoogle = false; + + log_mutex.AssertHeld(); + + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_-1] == '\n', ""); + + // Messages of a given severity get logged to lower severity logs, too + + if (!already_warned_before_initgoogle && !IsGoogleLoggingInitialized()) { + const char w[] = "WARNING: Logging before InitGoogleLogging() is " + "written to STDERR\n"; + WriteToStderr(w, strlen(w)); + already_warned_before_initgoogle = true; + } + + // global flag: never log to file if set. Also -- don't log to a + // file if we haven't parsed the command line flags to get the + // program name. + if (FLAGS_logtostderr || !IsGoogleLoggingInitialized()) { + ColoredWriteToStderr(data_->severity_, + data_->message_text_, data_->num_chars_to_log_); + + // this could be protected by a flag if necessary. + LogDestination::LogToSinks(data_->severity_, + data_->fullname_, data_->basename_, + data_->line_, &data_->tm_time_, + data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ - + data_->num_prefix_chars_ - 1)); + } else { + + // log this message to all log files of severity <= severity_ + LogDestination::LogToAllLogfiles(data_->severity_, data_->timestamp_, + data_->message_text_, + data_->num_chars_to_log_); + + LogDestination::MaybeLogToStderr(data_->severity_, data_->message_text_, + data_->num_chars_to_log_); + LogDestination::MaybeLogToEmail(data_->severity_, data_->message_text_, + data_->num_chars_to_log_); + LogDestination::LogToSinks(data_->severity_, + data_->fullname_, data_->basename_, + data_->line_, &data_->tm_time_, + data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ + - data_->num_prefix_chars_ - 1)); + // NOTE: -1 removes trailing \n + } + + // If we log a FATAL message, flush all the log destinations, then toss + // a signal for others to catch. We leave the logs in a state that + // someone else can use them (as long as they flush afterwards) + if (data_->severity_ == GLOG_FATAL && exit_on_dfatal) { + if (data_->first_fatal_) { + // Store crash information so that it is accessible from within signal + // handlers that may be invoked later. + RecordCrashReason(&crash_reason); + SetCrashReason(&crash_reason); + + // Store shortened fatal message for other logs and GWQ status + const int copy = min<int>(data_->num_chars_to_log_, + sizeof(fatal_message)-1); + memcpy(fatal_message, data_->message_text_, copy); + fatal_message[copy] = '\0'; + fatal_time = data_->timestamp_; + } + + if (!FLAGS_logtostderr) { + for (int i = 0; i < NUM_SEVERITIES; ++i) { + if ( LogDestination::log_destinations_[i] ) + LogDestination::log_destinations_[i]->logger_->Write(true, 0, "", 0); + } + } + + // release the lock that our caller (directly or indirectly) + // LogMessage::~LogMessage() grabbed so that signal handlers + // can use the logging facility. Alternately, we could add + // an entire unsafe logging interface to bypass locking + // for signal handlers but this seems simpler. + log_mutex.Unlock(); + LogDestination::WaitForSinks(data_); + + const char* message = "*** Check failure stack trace: ***\n"; + if (write(STDERR_FILENO, message, strlen(message)) < 0) { + // Ignore errors. + } + Fail(); + } +} + +void LogMessage::RecordCrashReason( + glog_internal_namespace_::CrashReason* reason) { + reason->filename = fatal_msg_data_exclusive.fullname_; + reason->line_number = fatal_msg_data_exclusive.line_; + reason->message = fatal_msg_data_exclusive.message_text_ + + fatal_msg_data_exclusive.num_prefix_chars_; +#ifdef HAVE_STACKTRACE + // Retrieve the stack trace, omitting the logging frames that got us here. + reason->depth = GetStackTrace(reason->stack, ARRAYSIZE(reason->stack), 4); +#else + reason->depth = 0; +#endif +} + +#ifdef HAVE___ATTRIBUTE__ +# define ATTRIBUTE_NORETURN __attribute__((noreturn)) +#else +# define ATTRIBUTE_NORETURN +#endif + +static void logging_fail() ATTRIBUTE_NORETURN; + +static void logging_fail() { +#if defined(_DEBUG) && defined(_MSC_VER) + // When debugging on windows, avoid the obnoxious dialog and make + // it possible to continue past a LOG(FATAL) in the debugger + __debugbreak(); +#else + abort(); +#endif +} + +typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN; + +GOOGLE_GLOG_DLL_DECL +logging_fail_func_t g_logging_fail_func = &logging_fail; + +void InstallFailureFunction(void (*fail_func)()) { + g_logging_fail_func = (logging_fail_func_t)fail_func; +} + +void LogMessage::Fail() { + g_logging_fail_func(); +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSink() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->sink_ != NULL) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_-1] == '\n', ""); + data_->sink_->send(data_->severity_, data_->fullname_, data_->basename_, + data_->line_, &data_->tm_time_, + data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ - + data_->num_prefix_chars_ - 1)); + } +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSinkAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + SendToSink(); + SendToLog(); +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SaveOrSendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->outvec_ != NULL) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_-1] == '\n', ""); + // Omit prefix of message and trailing newline when recording in outvec_. + const char *start = data_->message_text_ + data_->num_prefix_chars_; + int len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1; + data_->outvec_->push_back(string(start, len)); + } else { + SendToLog(); + } +} + +void LogMessage::WriteToStringAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->message_ != NULL) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_-1] == '\n', ""); + // Omit prefix of message and trailing newline when writing to message_. + const char *start = data_->message_text_ + data_->num_prefix_chars_; + int len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1; + data_->message_->assign(start, len); + } + SendToLog(); +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSyslogAndLog() { +#ifdef HAVE_SYSLOG_H + // Before any calls to syslog(), make a single call to openlog() + static bool openlog_already_called = false; + if (!openlog_already_called) { + openlog(glog_internal_namespace_::ProgramInvocationShortName(), + LOG_CONS | LOG_NDELAY | LOG_PID, + LOG_USER); + openlog_already_called = true; + } + + // This array maps Google severity levels to syslog levels + const int SEVERITY_TO_LEVEL[] = { LOG_INFO, LOG_WARNING, LOG_ERR, LOG_EMERG }; + syslog(LOG_USER | SEVERITY_TO_LEVEL[static_cast<int>(data_->severity_)], "%.*s", + int(data_->num_chars_to_syslog_), + data_->message_text_ + data_->num_prefix_chars_); + SendToLog(); +#else + LOG(ERROR) << "No syslog support: message=" << data_->message_text_; +#endif +} + +base::Logger* base::GetLogger(LogSeverity severity) { + MutexLock l(&log_mutex); + return LogDestination::log_destination(severity)->logger_; +} + +void base::SetLogger(LogSeverity severity, base::Logger* logger) { + MutexLock l(&log_mutex); + LogDestination::log_destination(severity)->logger_ = logger; +} + +// L < log_mutex. Acquires and releases mutex_. +int64 LogMessage::num_messages(int severity) { + MutexLock l(&log_mutex); + return num_messages_[severity]; +} + +// Output the COUNTER value. This is only valid if ostream is a +// LogStream. +ostream& operator<<(ostream &os, const PRIVATE_Counter&) { +#ifdef DISABLE_RTTI + LogMessage::LogStream *log = static_cast<LogMessage::LogStream*>(&os); +#else + LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&os); +#endif + CHECK(log && log == log->self()) + << "You must not use COUNTER with non-glog ostream"; + os << log->ctr(); + return os; +} + +ErrnoLogMessage::ErrnoLogMessage(const char* file, int line, + LogSeverity severity, int ctr, + void (LogMessage::*send_method)()) + : LogMessage(file, line, severity, ctr, send_method) { +} + +ErrnoLogMessage::~ErrnoLogMessage() { + // Don't access errno directly because it may have been altered + // while streaming the message. + stream() << ": " << StrError(preserved_errno()) << " [" + << preserved_errno() << "]"; +} + +void FlushLogFiles(LogSeverity min_severity) { + LogDestination::FlushLogFiles(min_severity); +} + +void FlushLogFilesUnsafe(LogSeverity min_severity) { + LogDestination::FlushLogFilesUnsafe(min_severity); +} + +void SetLogDestination(LogSeverity severity, const char* base_filename) { + LogDestination::SetLogDestination(severity, base_filename); +} + +void SetLogSymlink(LogSeverity severity, const char* symlink_basename) { + LogDestination::SetLogSymlink(severity, symlink_basename); +} + +LogSink::~LogSink() { +} + +void LogSink::WaitTillSent() { + // noop default +} + +string LogSink::ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) { + ostringstream stream(string(message, message_len)); + stream.fill('0'); + + // FIXME(jrvb): Updating this to use the correct value for usecs + // requires changing the signature for both this method and + // LogSink::send(). This change needs to be done in a separate CL + // so subclasses of LogSink can be updated at the same time. + int usecs = 0; + + stream << LogSeverityNames[severity][0] + << setw(2) << 1+tm_time->tm_mon + << setw(2) << tm_time->tm_mday + << ' ' + << setw(2) << tm_time->tm_hour << ':' + << setw(2) << tm_time->tm_min << ':' + << setw(2) << tm_time->tm_sec << '.' + << setw(6) << usecs + << ' ' + << setfill(' ') << setw(5) << GetTID() << setfill('0') + << ' ' + << file << ':' << line << "] "; + + stream << string(message, message_len); + return stream.str(); +} + +void AddLogSink(LogSink *destination) { + LogDestination::AddLogSink(destination); +} + +void RemoveLogSink(LogSink *destination) { + LogDestination::RemoveLogSink(destination); +} + +void SetLogFilenameExtension(const char* ext) { + LogDestination::SetLogFilenameExtension(ext); +} + +void SetStderrLogging(LogSeverity min_severity) { + LogDestination::SetStderrLogging(min_severity); +} + +void SetEmailLogging(LogSeverity min_severity, const char* addresses) { + LogDestination::SetEmailLogging(min_severity, addresses); +} + +void LogToStderr() { + LogDestination::LogToStderr(); +} + +namespace base { +namespace internal { + +bool GetExitOnDFatal() { + MutexLock l(&log_mutex); + return exit_on_dfatal; +} + +// Determines whether we exit the program for a LOG(DFATAL) message in +// debug mode. It does this by skipping the call to Fail/FailQuietly. +// This is intended for testing only. +// +// This can have some effects on LOG(FATAL) as well. Failure messages +// are always allocated (rather than sharing a buffer), the crash +// reason is not recorded, the "gwq" status message is not updated, +// and the stack trace is not recorded. The LOG(FATAL) *will* still +// exit the program. Since this function is used only in testing, +// these differences are acceptable. +void SetExitOnDFatal(bool value) { + MutexLock l(&log_mutex); + exit_on_dfatal = value; +} + +} // namespace internal +} // namespace base + +// use_logging controls whether the logging functions LOG/VLOG are used +// to log errors. It should be set to false when the caller holds the +// log_mutex. +static bool SendEmailInternal(const char*dest, const char *subject, + const char*body, bool use_logging) { + if (dest && *dest) { + if ( use_logging ) { + VLOG(1) << "Trying to send TITLE:" << subject + << " BODY:" << body << " to " << dest; + } else { + fprintf(stderr, "Trying to send TITLE: %s BODY: %s to %s\n", + subject, body, dest); + } + + string cmd = + FLAGS_logmailer + " -s\"" + subject + "\" " + dest; + FILE* pipe = popen(cmd.c_str(), "w"); + if (pipe != NULL) { + // Add the body if we have one + if (body) + fwrite(body, sizeof(char), strlen(body), pipe); + bool ok = pclose(pipe) != -1; + if ( !ok ) { + if ( use_logging ) { + LOG(ERROR) << "Problems sending mail to " << dest << ": " + << StrError(errno); + } else { + fprintf(stderr, "Problems sending mail to %s: %s\n", + dest, StrError(errno).c_str()); + } + } + return ok; + } else { + if ( use_logging ) { + LOG(ERROR) << "Unable to send mail to " << dest; + } else { + fprintf(stderr, "Unable to send mail to %s\n", dest); + } + } + } + return false; +} + +bool SendEmail(const char*dest, const char *subject, const char*body){ + return SendEmailInternal(dest, subject, body, true); +} + +static void GetTempDirectories(vector<string>* list) { + list->clear(); +#ifdef OS_WINDOWS + // On windows we'll try to find a directory in this order: + // C:/Documents & Settings/whomever/TEMP (or whatever GetTempPath() is) + // C:/TMP/ + // C:/TEMP/ + // C:/WINDOWS/ or C:/WINNT/ + // . + char tmp[MAX_PATH]; + if (GetTempPathA(MAX_PATH, tmp)) + list->push_back(tmp); + list->push_back("C:\\tmp\\"); + list->push_back("C:\\temp\\"); +#else + // Directories, in order of preference. If we find a dir that + // exists, we stop adding other less-preferred dirs + const char * candidates[] = { + // Non-null only during unittest/regtest + getenv("TEST_TMPDIR"), + + // Explicitly-supplied temp dirs + getenv("TMPDIR"), getenv("TMP"), + + // If all else fails + "/tmp", + }; + + for (size_t i = 0; i < ARRAYSIZE(candidates); i++) { + const char *d = candidates[i]; + if (!d) continue; // Empty env var + + // Make sure we don't surprise anyone who's expecting a '/' + string dstr = d; + if (dstr[dstr.size() - 1] != '/') { + dstr += "/"; + } + list->push_back(dstr); + + struct stat statbuf; + if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) { + // We found a dir that exists - we're done. + return; + } + } + +#endif +} + +static vector<string>* logging_directories_list; + +const vector<string>& GetLoggingDirectories() { + // Not strictly thread-safe but we're called early in InitGoogle(). + if (logging_directories_list == NULL) { + logging_directories_list = new vector<string>; + + if ( !FLAGS_log_dir.empty() ) { + // A dir was specified, we should use it + logging_directories_list->push_back(FLAGS_log_dir.c_str()); + } else { + GetTempDirectories(logging_directories_list); +#ifdef OS_WINDOWS + char tmp[MAX_PATH]; + if (GetWindowsDirectoryA(tmp, MAX_PATH)) + logging_directories_list->push_back(tmp); + logging_directories_list->push_back(".\\"); +#else + logging_directories_list->push_back("./"); +#endif + } + } + return *logging_directories_list; +} + +void TestOnly_ClearLoggingDirectoriesList() { + fprintf(stderr, "TestOnly_ClearLoggingDirectoriesList should only be " + "called from test code.\n"); + delete logging_directories_list; + logging_directories_list = NULL; +} + +void GetExistingTempDirectories(vector<string>* list) { + GetTempDirectories(list); + vector<string>::iterator i_dir = list->begin(); + while( i_dir != list->end() ) { + // zero arg to access means test for existence; no constant + // defined on windows + if ( access(i_dir->c_str(), 0) ) { + i_dir = list->erase(i_dir); + } else { + ++i_dir; + } + } +} + +void TruncateLogFile(const char *path, int64 limit, int64 keep) { +#ifdef HAVE_UNISTD_H + struct stat statbuf; + const int kCopyBlockSize = 8 << 10; + char copybuf[kCopyBlockSize]; + int64 read_offset, write_offset; + // Don't follow symlinks unless they're our own fd symlinks in /proc + int flags = O_RDWR; + // TODO(hamaji): Support other environments. +#ifdef OS_LINUX + const char *procfd_prefix = "/proc/self/fd/"; + if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW; +#endif + + int fd = open(path, flags); + if (fd == -1) { + if (errno == EFBIG) { + // The log file in question has got too big for us to open. The + // real fix for this would be to compile logging.cc (or probably + // all of base/...) with -D_FILE_OFFSET_BITS=64 but that's + // rather scary. + // Instead just truncate the file to something we can manage + if (truncate(path, 0) == -1) { + PLOG(ERROR) << "Unable to truncate " << path; + } else { + LOG(ERROR) << "Truncated " << path << " due to EFBIG error"; + } + } else { + PLOG(ERROR) << "Unable to open " << path; + } + return; + } + + if (fstat(fd, &statbuf) == -1) { + PLOG(ERROR) << "Unable to fstat()"; + goto out_close_fd; + } + + // See if the path refers to a regular file bigger than the + // specified limit + if (!S_ISREG(statbuf.st_mode)) goto out_close_fd; + if (statbuf.st_size <= limit) goto out_close_fd; + if (statbuf.st_size <= keep) goto out_close_fd; + + // This log file is too large - we need to truncate it + LOG(INFO) << "Truncating " << path << " to " << keep << " bytes"; + + // Copy the last "keep" bytes of the file to the beginning of the file + read_offset = statbuf.st_size - keep; + write_offset = 0; + int bytesin, bytesout; + while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) { + bytesout = pwrite(fd, copybuf, bytesin, write_offset); + if (bytesout == -1) { + PLOG(ERROR) << "Unable to write to " << path; + break; + } else if (bytesout != bytesin) { + LOG(ERROR) << "Expected to write " << bytesin << ", wrote " << bytesout; + } + read_offset += bytesin; + write_offset += bytesout; + } + if (bytesin == -1) PLOG(ERROR) << "Unable to read from " << path; + + // Truncate the remainder of the file. If someone else writes to the + // end of the file after our last read() above, we lose their latest + // data. Too bad ... + if (ftruncate(fd, write_offset) == -1) { + PLOG(ERROR) << "Unable to truncate " << path; + } + + out_close_fd: + close(fd); +#else + LOG(ERROR) << "No log truncation support."; +#endif +} + +void TruncateStdoutStderr() { +#ifdef HAVE_UNISTD_H + int64 limit = MaxLogSize() << 20; + int64 keep = 1 << 20; + TruncateLogFile("/proc/self/fd/1", limit, keep); + TruncateLogFile("/proc/self/fd/2", limit, keep); +#else + LOG(ERROR) << "No log truncation support."; +#endif +} + + +// Helper functions for string comparisons. +#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \ + string* Check##func##expected##Impl(const char* s1, const char* s2, \ + const char* names) { \ + bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \ + if (equal == expected) return NULL; \ + else { \ + ostringstream ss; \ + if (!s1) s1 = ""; \ + if (!s2) s2 = ""; \ + ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \ + return new string(ss.str()); \ + } \ + } +DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true) +DEFINE_CHECK_STROP_IMPL(CHECK_STRNE, strcmp, false) +DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true) +DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false) +#undef DEFINE_CHECK_STROP_IMPL + +int posix_strerror_r(int err, char *buf, size_t len) { + // Sanity check input parameters + if (buf == NULL || len <= 0) { + errno = EINVAL; + return -1; + } + + // Reset buf and errno, and try calling whatever version of strerror_r() + // is implemented by glibc + buf[0] = '\000'; + int old_errno = errno; + errno = 0; + char *rc = reinterpret_cast<char *>(strerror_r(err, buf, len)); + + // Both versions set errno on failure + if (errno) { + // Should already be there, but better safe than sorry + buf[0] = '\000'; + return -1; + } + errno = old_errno; + + // POSIX is vague about whether the string will be terminated, although + // is indirectly implies that typically ERANGE will be returned, instead + // of truncating the string. This is different from the GNU implementation. + // We play it safe by always terminating the string explicitly. + buf[len-1] = '\000'; + + // If the function succeeded, we can use its exit code to determine the + // semantics implemented by glibc + if (!rc) { + return 0; + } else { + // GNU semantics detected + if (rc == buf) { + return 0; + } else { + buf[0] = '\000'; +#if defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_OPENBSD) + if (reinterpret_cast<intptr_t>(rc) < sys_nerr) { + // This means an error on MacOSX or FreeBSD. + return -1; + } +#endif + strncat(buf, rc, len-1); + return 0; + } + } +} + +string StrError(int err) { + char buf[100]; + int rc = posix_strerror_r(err, buf, sizeof(buf)); + if ((rc < 0) || (buf[0] == '\000')) { + snprintf(buf, sizeof(buf), "Error number %d", err); + } + return buf; +} + +LogMessageFatal::LogMessageFatal(const char* file, int line) : + LogMessage(file, line, GLOG_FATAL) {} + +LogMessageFatal::LogMessageFatal(const char* file, int line, + const CheckOpString& result) : + LogMessage(file, line, result) {} + +LogMessageFatal::~LogMessageFatal() { + Flush(); + LogMessage::Fail(); +} + +namespace base { + +CheckOpMessageBuilder::CheckOpMessageBuilder(const char *exprtext) + : stream_(new ostringstream) { + *stream_ << exprtext << " ("; +} + +CheckOpMessageBuilder::~CheckOpMessageBuilder() { + delete stream_; +} + +ostream* CheckOpMessageBuilder::ForVar2() { + *stream_ << " vs. "; + return stream_; +} + +string* CheckOpMessageBuilder::NewString() { + *stream_ << ")"; + return new string(stream_->str()); +} + +} // namespace base + +template <> +void MakeCheckOpValueString(std::ostream* os, const char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "char value " << (short)v; + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const signed char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "signed char value " << (short)v; + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "unsigned char value " << (unsigned short)v; + } +} + +void InitGoogleLogging(const char* argv0) { + glog_internal_namespace_::InitGoogleLoggingUtilities(argv0); +} + +void ShutdownGoogleLogging() { + glog_internal_namespace_::ShutdownGoogleLoggingUtilities(); + LogDestination::DeleteLogDestinations(); + delete logging_directories_list; + logging_directories_list = NULL; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/mock-log.h b/ios/Pods/Flipper-Glog/src/mock-log.h new file mode 100644 index 000000000..30a0f74ef --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/mock-log.h @@ -0,0 +1,156 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Zhanyong Wan +// +// Defines the ScopedMockLog class (using Google C++ Mocking +// Framework), which is convenient for testing code that uses LOG(). + +#ifndef GLOG_SRC_MOCK_LOG_H_ +#define GLOG_SRC_MOCK_LOG_H_ + +// For GOOGLE_NAMESPACE. This must go first so we get _XOPEN_SOURCE. +#include "utilities.h" + +#include <string> + +#include <gmock/gmock.h> + +#include "glog/logging.h" + +_START_GOOGLE_NAMESPACE_ +namespace glog_testing { + +// A ScopedMockLog object intercepts LOG() messages issued during its +// lifespan. Using this together with Google C++ Mocking Framework, +// it's very easy to test how a piece of code calls LOG(). The +// typical usage: +// +// TEST(FooTest, LogsCorrectly) { +// ScopedMockLog log; +// +// // We expect the WARNING "Something bad!" exactly twice. +// EXPECT_CALL(log, Log(WARNING, _, "Something bad!")) +// .Times(2); +// +// // We allow foo.cc to call LOG(INFO) any number of times. +// EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _)) +// .Times(AnyNumber()); +// +// Foo(); // Exercises the code under test. +// } +class ScopedMockLog : public GOOGLE_NAMESPACE::LogSink { + public: + // When a ScopedMockLog object is constructed, it starts to + // intercept logs. + ScopedMockLog() { AddLogSink(this); } + + // When the object is destructed, it stops intercepting logs. + virtual ~ScopedMockLog() { RemoveLogSink(this); } + + // Implements the mock method: + // + // void Log(LogSeverity severity, const string& file_path, + // const string& message); + // + // The second argument to Send() is the full path of the source file + // in which the LOG() was issued. + // + // Note, that in a multi-threaded environment, all LOG() messages from a + // single thread will be handled in sequence, but that cannot be guaranteed + // for messages from different threads. In fact, if the same or multiple + // expectations are matched on two threads concurrently, their actions will + // be executed concurrently as well and may interleave. + MOCK_METHOD3(Log, void(GOOGLE_NAMESPACE::LogSeverity severity, + const std::string& file_path, + const std::string& message)); + + private: + // Implements the send() virtual function in class LogSink. + // Whenever a LOG() statement is executed, this function will be + // invoked with information presented in the LOG(). + // + // The method argument list is long and carries much information a + // test usually doesn't care about, so we trim the list before + // forwarding the call to Log(), which is much easier to use in + // tests. + // + // We still cannot call Log() directly, as it may invoke other LOG() + // messages, either due to Invoke, or due to an error logged in + // Google C++ Mocking Framework code, which would trigger a deadlock + // since a lock is held during send(). + // + // Hence, we save the message for WaitTillSent() which will be called after + // the lock on send() is released, and we'll call Log() inside + // WaitTillSent(). Since while a single send() call may be running at a + // time, multiple WaitTillSent() calls (along with the one send() call) may + // be running simultaneously, we ensure thread-safety of the exchange between + // send() and WaitTillSent(), and that for each message, LOG(), send(), + // WaitTillSent() and Log() are executed in the same thread. + virtual void send(GOOGLE_NAMESPACE::LogSeverity severity, + const char* full_filename, + const char* /*base_filename*/, int /*line*/, + const tm* /*tm_time*/, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + message_info_.severity = severity; + message_info_.file_path = full_filename; + message_info_.message = std::string(message, message_len); + } + + // Implements the WaitTillSent() virtual function in class LogSink. + // It will be executed after send() and after the global logging lock is + // released, so calls within it (or rather within the Log() method called + // within) may also issue LOG() statements. + // + // LOG(), send(), WaitTillSent() and Log() will occur in the same thread for + // a given log message. + virtual void WaitTillSent() { + // First, and very importantly, we save a copy of the message being + // processed before calling Log(), since Log() may indirectly call send() + // and WaitTillSent() in the same thread again. + MessageInfo message_info = message_info_; + Log(message_info.severity, message_info.file_path, message_info.message); + } + + // All relevant information about a logged message that needs to be passed + // from send() to WaitTillSent(). + struct MessageInfo { + GOOGLE_NAMESPACE::LogSeverity severity; + std::string file_path; + std::string message; + }; + MessageInfo message_info_; +}; + +} // namespace glog_testing +_END_GOOGLE_NAMESPACE_ + +#endif // GLOG_SRC_MOCK_LOG_H_ diff --git a/ios/Pods/Flipper-Glog/src/raw_logging.cc b/ios/Pods/Flipper-Glog/src/raw_logging.cc new file mode 100644 index 000000000..7a7409bbf --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/raw_logging.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Maxim Lifantsev +// +// logging_unittest.cc covers the functionality herein + +#include "utilities.h" + +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> // for close() and write() +#endif +#include <fcntl.h> // for open() +#include <time.h> +#include "config.h" +#include "glog/logging.h" // To pick up flag settings etc. +#include "glog/raw_logging.h" +#include "base/commandlineflags.h" + +#ifdef HAVE_STACKTRACE +# include "stacktrace.h" +#endif + +#if defined(HAVE_SYSCALL_H) +#include <syscall.h> // for syscall() +#elif defined(HAVE_SYS_SYSCALL_H) +#include <sys/syscall.h> // for syscall() +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H) +# define safe_write(fd, s, len) syscall(SYS_write, fd, s, len) +#else + // Not so safe, but what can you do? +# define safe_write(fd, s, len) write(fd, s, len) +#endif + +_START_GOOGLE_NAMESPACE_ + +// Data for RawLog__ below. We simply pick up the latest +// time data created by a normal log message to avoid calling +// localtime_r which can allocate memory. +static struct ::tm last_tm_time_for_raw_log; +static int last_usecs_for_raw_log; + +void RawLog__SetLastTime(const struct ::tm& t, int usecs) { + memcpy(&last_tm_time_for_raw_log, &t, sizeof(last_tm_time_for_raw_log)); + last_usecs_for_raw_log = usecs; +} + +// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths +// that invoke malloc() and getenv() that might acquire some locks. +// If this becomes a problem we should reimplement a subset of vsnprintf +// that does not need locks and malloc. + +// Helper for RawLog__ below. +// *DoRawLog writes to *buf of *size and move them past the written portion. +// It returns true iff there was no overflow or error. +static bool DoRawLog(char** buf, int* size, const char* format, ...) { + va_list ap; + va_start(ap, format); + int n = vsnprintf(*buf, *size, format, ap); + va_end(ap); + if (n < 0 || n > *size) return false; + *size -= n; + *buf += n; + return true; +} + +// Helper for RawLog__ below. +inline static bool VADoRawLog(char** buf, int* size, + const char* format, va_list ap) { + int n = vsnprintf(*buf, *size, format, ap); + if (n < 0 || n > *size) return false; + *size -= n; + *buf += n; + return true; +} + +static const int kLogBufSize = 3000; +static bool crashed = false; +static CrashReason crash_reason; +static char crash_buf[kLogBufSize + 1] = { 0 }; // Will end in '\0' + +void RawLog__(LogSeverity severity, const char* file, int line, + const char* format, ...) { + if (!(FLAGS_logtostderr || severity >= FLAGS_stderrthreshold || + FLAGS_alsologtostderr || !IsGoogleLoggingInitialized())) { + return; // this stderr log message is suppressed + } + // can't call localtime_r here: it can allocate + struct ::tm& t = last_tm_time_for_raw_log; + char buffer[kLogBufSize]; + char* buf = buffer; + int size = sizeof(buffer); + + // NOTE: this format should match the specification in base/logging.h + DoRawLog(&buf, &size, "%c%02d%02d %02d:%02d:%02d.%06d %5u %s:%d] RAW: ", + LogSeverityNames[severity][0], + 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, + last_usecs_for_raw_log, + static_cast<unsigned int>(GetTID()), + const_basename(const_cast<char *>(file)), line); + + // Record the position and size of the buffer after the prefix + const char* msg_start = buf; + const int msg_size = size; + + va_list ap; + va_start(ap, format); + bool no_chop = VADoRawLog(&buf, &size, format, ap); + va_end(ap); + if (no_chop) { + DoRawLog(&buf, &size, "\n"); + } else { + DoRawLog(&buf, &size, "RAW_LOG ERROR: The Message was too long!\n"); + } + // We make a raw syscall to write directly to the stderr file descriptor, + // avoiding FILE buffering (to avoid invoking malloc()), and bypassing + // libc (to side-step any libc interception). + // We write just once to avoid races with other invocations of RawLog__. + safe_write(STDERR_FILENO, buffer, strlen(buffer)); + if (severity == GLOG_FATAL) { + if (!sync_val_compare_and_swap(&crashed, false, true)) { + crash_reason.filename = file; + crash_reason.line_number = line; + memcpy(crash_buf, msg_start, msg_size); // Don't include prefix + crash_reason.message = crash_buf; +#ifdef HAVE_STACKTRACE + crash_reason.depth = + GetStackTrace(crash_reason.stack, ARRAYSIZE(crash_reason.stack), 1); +#else + crash_reason.depth = 0; +#endif + SetCrashReason(&crash_reason); + } + LogMessage::Fail(); // abort() + } +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/signalhandler.cc b/ios/Pods/Flipper-Glog/src/signalhandler.cc new file mode 100644 index 000000000..a7aef8b99 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/signalhandler.cc @@ -0,0 +1,375 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Satoru Takabayashi +// +// Implementation of InstallFailureSignalHandler(). + +#include "utilities.h" +#include "stacktrace.h" +#include "symbolize.h" +#include "glog/logging.h" + +#include <signal.h> +#include <time.h> +#ifdef HAVE_UCONTEXT_H +# include <ucontext.h> +#endif +#ifdef HAVE_SYS_UCONTEXT_H +# include <sys/ucontext.h> +#endif +#include <algorithm> + +_START_GOOGLE_NAMESPACE_ + +// TOOD(hamaji): Use signal instead of sigaction? +#ifdef HAVE_SIGACTION + +namespace { + +// We'll install the failure signal handler for these signals. We could +// use strsignal() to get signal names, but we don't use it to avoid +// introducing yet another #ifdef complication. +// +// The list should be synced with the comment in signalhandler.h. +const struct { + int number; + const char *name; +} kFailureSignals[] = { + { SIGSEGV, "SIGSEGV" }, + { SIGILL, "SIGILL" }, + { SIGFPE, "SIGFPE" }, + { SIGABRT, "SIGABRT" }, + { SIGBUS, "SIGBUS" }, + { SIGTERM, "SIGTERM" }, +}; + +// Returns the program counter from signal context, NULL if unknown. +void* GetPC(void* ucontext_in_void) { +#if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT) + if (ucontext_in_void != NULL) { + ucontext_t *context = reinterpret_cast<ucontext_t *>(ucontext_in_void); + return (void*)context->PC_FROM_UCONTEXT; + } +#endif + return NULL; +} + +// The class is used for formatting error messages. We don't use printf() +// as it's not async signal safe. +class MinimalFormatter { + public: + MinimalFormatter(char *buffer, int size) + : buffer_(buffer), + cursor_(buffer), + end_(buffer + size) { + } + + // Returns the number of bytes written in the buffer. + int num_bytes_written() const { return cursor_ - buffer_; } + + // Appends string from "str" and updates the internal cursor. + void AppendString(const char* str) { + int i = 0; + while (str[i] != '\0' && cursor_ + i < end_) { + cursor_[i] = str[i]; + ++i; + } + cursor_ += i; + } + + // Formats "number" in "radix" and updates the internal cursor. + // Lowercase letters are used for 'a' - 'z'. + void AppendUint64(uint64 number, int radix) { + int i = 0; + while (cursor_ + i < end_) { + const int tmp = number % radix; + number /= radix; + cursor_[i] = (tmp < 10 ? '0' + tmp : 'a' + tmp - 10); + ++i; + if (number == 0) { + break; + } + } + // Reverse the bytes written. + std::reverse(cursor_, cursor_ + i); + cursor_ += i; + } + + // Formats "number" as hexadecimal number, and updates the internal + // cursor. Padding will be added in front if needed. + void AppendHexWithPadding(uint64 number, int width) { + char* start = cursor_; + AppendString("0x"); + AppendUint64(number, 16); + // Move to right and add padding in front if needed. + if (cursor_ < start + width) { + const int64 delta = start + width - cursor_; + std::copy(start, cursor_, start + delta); + std::fill(start, start + delta, ' '); + cursor_ = start + width; + } + } + + private: + char *buffer_; + char *cursor_; + const char * const end_; +}; + +// Writes the given data with the size to the standard error. +void WriteToStderr(const char* data, int size) { + if (write(STDERR_FILENO, data, size) < 0) { + // Ignore errors. + } +} + +// The writer function can be changed by InstallFailureWriter(). +void (*g_failure_writer)(const char* data, int size) = WriteToStderr; + +// Dumps time information. We don't dump human-readable time information +// as localtime() is not guaranteed to be async signal safe. +void DumpTimeInfo() { + time_t time_in_sec = time(NULL); + char buf[256]; // Big enough for time info. + MinimalFormatter formatter(buf, sizeof(buf)); + formatter.AppendString("*** Aborted at "); + formatter.AppendUint64(time_in_sec, 10); + formatter.AppendString(" (unix time)"); + formatter.AppendString(" try \"date -d @"); + formatter.AppendUint64(time_in_sec, 10); + formatter.AppendString("\" if you are using GNU date ***\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +// Dumps information about the signal to STDERR. +void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { + // Get the signal name. + const char* signal_name = NULL; + for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { + if (signal_number == kFailureSignals[i].number) { + signal_name = kFailureSignals[i].name; + } + } + + char buf[256]; // Big enough for signal info. + MinimalFormatter formatter(buf, sizeof(buf)); + + formatter.AppendString("*** "); + if (signal_name) { + formatter.AppendString(signal_name); + } else { + // Use the signal number if the name is unknown. The signal name + // should be known, but just in case. + formatter.AppendString("Signal "); + formatter.AppendUint64(signal_number, 10); + } + formatter.AppendString(" (@0x"); + formatter.AppendUint64(reinterpret_cast<uintptr_t>(siginfo->si_addr), 16); + formatter.AppendString(")"); + formatter.AppendString(" received by PID "); + formatter.AppendUint64(getpid(), 10); + formatter.AppendString(" (TID 0x"); + // We assume pthread_t is an integral number or a pointer, rather + // than a complex struct. In some environments, pthread_self() + // returns an uint64 but in some other environments pthread_self() + // returns a pointer. Hence we use C-style cast here, rather than + // reinterpret/static_cast, to support both types of environments. + formatter.AppendUint64((uintptr_t)pthread_self(), 16); + formatter.AppendString(") "); + // Only linux has the PID of the signal sender in si_pid. +#ifdef OS_LINUX + formatter.AppendString("from PID "); + formatter.AppendUint64(siginfo->si_pid, 10); + formatter.AppendString("; "); +#endif + formatter.AppendString("stack trace: ***\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +// Dumps information about the stack frame to STDERR. +void DumpStackFrameInfo(const char* prefix, void* pc) { + // Get the symbol name. + const char *symbol = "(unknown)"; + char symbolized[1024]; // Big enough for a sane symbol. + // Symbolizes the previous address of pc because pc may be in the + // next function. + if (Symbolize(reinterpret_cast<char *>(pc) - 1, + symbolized, sizeof(symbolized))) { + symbol = symbolized; + } + + char buf[1024]; // Big enough for stack frame info. + MinimalFormatter formatter(buf, sizeof(buf)); + + formatter.AppendString(prefix); + formatter.AppendString("@ "); + const int width = 2 * sizeof(void*) + 2; // + 2 for "0x". + formatter.AppendHexWithPadding(reinterpret_cast<uintptr_t>(pc), width); + formatter.AppendString(" "); + formatter.AppendString(symbol); + formatter.AppendString("\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +// Invoke the default signal handler. +void InvokeDefaultSignalHandler(int signal_number) { + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_handler = SIG_DFL; + sigaction(signal_number, &sig_action, NULL); + kill(getpid(), signal_number); +} + +// This variable is used for protecting FailureSignalHandler() from +// dumping stuff while another thread is doing it. Our policy is to let +// the first thread dump stuff and let other threads wait. +// See also comments in FailureSignalHandler(). +static pthread_t* g_entered_thread_id_pointer = NULL; + +// Dumps signal and stack frame information, and invokes the default +// signal handler once our job is done. +void FailureSignalHandler(int signal_number, + siginfo_t *signal_info, + void *ucontext) { + // First check if we've already entered the function. We use an atomic + // compare and swap operation for platforms that support it. For other + // platforms, we use a naive method that could lead to a subtle race. + + // We assume pthread_self() is async signal safe, though it's not + // officially guaranteed. + pthread_t my_thread_id = pthread_self(); + // NOTE: We could simply use pthread_t rather than pthread_t* for this, + // if pthread_self() is guaranteed to return non-zero value for thread + // ids, but there is no such guarantee. We need to distinguish if the + // old value (value returned from __sync_val_compare_and_swap) is + // different from the original value (in this case NULL). + pthread_t* old_thread_id_pointer = + glog_internal_namespace_::sync_val_compare_and_swap( + &g_entered_thread_id_pointer, + static_cast<pthread_t*>(NULL), + &my_thread_id); + if (old_thread_id_pointer != NULL) { + // We've already entered the signal handler. What should we do? + if (pthread_equal(my_thread_id, *g_entered_thread_id_pointer)) { + // It looks the current thread is reentering the signal handler. + // Something must be going wrong (maybe we are reentering by another + // type of signal?). Kill ourself by the default signal handler. + InvokeDefaultSignalHandler(signal_number); + } + // Another thread is dumping stuff. Let's wait until that thread + // finishes the job and kills the process. + while (true) { + sleep(1); + } + } + // This is the first time we enter the signal handler. We are going to + // do some interesting stuff from here. + // TODO(satorux): We might want to set timeout here using alarm(), but + // mixing alarm() and sleep() can be a bad idea. + + // First dump time info. + DumpTimeInfo(); + + // Get the program counter from ucontext. + void *pc = GetPC(ucontext); + DumpStackFrameInfo("PC: ", pc); + +#ifdef HAVE_STACKTRACE + // Get the stack traces. + void *stack[32]; + // +1 to exclude this function. + const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1); + DumpSignalInfo(signal_number, signal_info); + // Dump the stack traces. + for (int i = 0; i < depth; ++i) { + DumpStackFrameInfo(" ", stack[i]); + } +#endif + + // *** TRANSITION *** + // + // BEFORE this point, all code must be async-termination-safe! + // (See WARNING above.) + // + // AFTER this point, we do unsafe things, like using LOG()! + // The process could be terminated or hung at any time. We try to + // do more useful things first and riskier things later. + + // Flush the logs before we do anything in case 'anything' + // causes problems. + FlushLogFilesUnsafe(0); + + // Kill ourself by the default signal handler. + InvokeDefaultSignalHandler(signal_number); +} + +} // namespace + +#endif // HAVE_SIGACTION + +namespace glog_internal_namespace_ { + +bool IsFailureSignalHandlerInstalled() { +#ifdef HAVE_SIGACTION + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sigaction(SIGABRT, NULL, &sig_action); + if (sig_action.sa_sigaction == &FailureSignalHandler) + return true; +#endif // HAVE_SIGACTION + return false; +} + +} // namespace glog_internal_namespace_ + +void InstallFailureSignalHandler() { +#ifdef HAVE_SIGACTION + // Build the sigaction struct. + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags |= SA_SIGINFO; + sig_action.sa_sigaction = &FailureSignalHandler; + + for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { + CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL)); + } +#endif // HAVE_SIGACTION +} + +void InstallFailureWriter(void (*writer)(const char* data, int size)) { +#ifdef HAVE_SIGACTION + g_failure_writer = writer; +#endif // HAVE_SIGACTION +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace.h b/ios/Pods/Flipper-Glog/src/stacktrace.h new file mode 100644 index 000000000..8c3e8fe8f --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace.h @@ -0,0 +1,60 @@ +// Copyright (c) 2000 - 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Routines to extract the current stack trace. These functions are +// thread-safe. + +#ifndef BASE_STACKTRACE_H_ +#define BASE_STACKTRACE_H_ + +#include "config.h" + +_START_GOOGLE_NAMESPACE_ + +// This is similar to the GetStackFrames routine, except that it returns +// the stack trace only, and not the stack frame sizes as well. +// Example: +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int depth = GetStackFrames(result, 10, 1); +// } +// +// This produces: +// result[0] foo +// result[1] main +// .... ... +// +// "result" must not be NULL. +extern int GetStackTrace(void** result, int max_depth, int skip_count); + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_STACKTRACE_H_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace_generic-inl.h b/ios/Pods/Flipper-Glog/src/stacktrace_generic-inl.h new file mode 100644 index 000000000..fad81d3e3 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace_generic-inl.h @@ -0,0 +1,59 @@ +// Copyright (c) 2000 - 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. +#include <execinfo.h> +#include <string.h> +#include "stacktrace.h" + +_START_GOOGLE_NAMESPACE_ + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + static const int kStackLength = 64; + void * stack[kStackLength]; + int size; + + size = backtrace(stack, kStackLength); + skip_count++; // we want to skip the current frame as well + int result_count = size - skip_count; + if (result_count < 0) + result_count = 0; + if (result_count > max_depth) + result_count = max_depth; + for (int i = 0; i < result_count; i++) + result[i] = stack[i + skip_count]; + + return result_count; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace_libunwind-inl.h b/ios/Pods/Flipper-Glog/src/stacktrace_libunwind-inl.h new file mode 100644 index 000000000..0dc14c650 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace_libunwind-inl.h @@ -0,0 +1,87 @@ +// Copyright (c) 2005 - 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Arun Sharma +// +// Produce stack trace using libunwind + +#include "utilities.h" + +extern "C" { +#define UNW_LOCAL_ONLY +#include <libunwind.h> +} +#include "glog/raw_logging.h" +#include "stacktrace.h" + +_START_GOOGLE_NAMESPACE_ + +// Sometimes, we can try to get a stack trace from within a stack +// trace, because libunwind can call mmap (maybe indirectly via an +// internal mmap based memory allocator), and that mmap gets trapped +// and causes a stack-trace request. If were to try to honor that +// recursive request, we'd end up with infinite recursion or deadlock. +// Luckily, it's safe to ignore those subsequent traces. In such +// cases, we return 0 to indicate the situation. +static bool g_now_entering = false; + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void *ip; + int n = 0; + unw_cursor_t cursor; + unw_context_t uc; + + if (sync_val_compare_and_swap(&g_now_entering, false, true)) { + return 0; + } + + unw_getcontext(&uc); + RAW_CHECK(unw_init_local(&cursor, &uc) >= 0, "unw_init_local failed"); + skip_count++; // Do not include the "GetStackTrace" frame + + while (n < max_depth) { + int ret = unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *) &ip); + if (ret < 0) + break; + if (skip_count > 0) { + skip_count--; + } else { + result[n++] = ip; + } + ret = unw_step(&cursor); + if (ret <= 0) + break; + } + + g_now_entering = false; + return n; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace_powerpc-inl.h b/ios/Pods/Flipper-Glog/src/stacktrace_powerpc-inl.h new file mode 100644 index 000000000..03b91089a --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace_powerpc-inl.h @@ -0,0 +1,130 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Craig Silverstein +// +// Produce stack trace. I'm guessing (hoping!) the code is much like +// for x86. For apple machines, at least, it seems to be; see +// http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 + +#include <stdio.h> +#include <stdint.h> // for uintptr_t +#include "stacktrace.h" + +_START_GOOGLE_NAMESPACE_ + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return NULL if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template<bool STRICT_UNWINDING> +static void **NextStackFrame(void **old_sp) { + void **new_sp = (void **) *old_sp; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return NULL; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return NULL; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL; + } + if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL; + return new_sp; +} + +// This ensures that GetStackTrace stes up the Link Register properly. +void StacktracePowerPCDummyFunction() __attribute__((noinline)); +void StacktracePowerPCDummyFunction() { __asm__ volatile(""); } + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void **sp; + // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a + // different asm syntax. I don't know quite the best way to discriminate + // systems using the old as from the new one; I've gone with __APPLE__. +#ifdef __APPLE__ + __asm__ volatile ("mr %0,r1" : "=r" (sp)); +#else + __asm__ volatile ("mr %0,1" : "=r" (sp)); +#endif + + // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack + // entry that holds the return address of the subroutine call (what + // instruction we run after our function finishes). This is the + // same as the stack-pointer of our parent routine, which is what we + // want here. While the compiler will always(?) set up LR for + // subroutine calls, it may not for leaf functions (such as this one). + // This routine forces the compiler (at least gcc) to push it anyway. + StacktracePowerPCDummyFunction(); + + // The LR save area is used by the callee, so the top entry is bogus. + skip_count++; + + int n = 0; + while (sp && n < max_depth) { + if (skip_count > 0) { + skip_count--; + } else { + // PowerPC has 3 main ABIs, which say where in the stack the + // Link Register is. For DARWIN and AIX (used by apple and + // linux ppc64), it's in sp[2]. For SYSV (used by linux ppc), + // it's in sp[1]. +#if defined(_CALL_AIX) || defined(_CALL_DARWIN) + result[n++] = *(sp+2); +#elif defined(_CALL_SYSV) + result[n++] = *(sp+1); +#elif defined(__APPLE__) || ((defined(__linux) || defined(__linux__)) && defined(__PPC64__)) + // This check is in case the compiler doesn't define _CALL_AIX/etc. + result[n++] = *(sp+2); +#elif defined(__linux) + // This check is in case the compiler doesn't define _CALL_SYSV. + result[n++] = *(sp+1); +#else +#error Need to specify the PPC ABI for your archiecture. +#endif + } + // Use strict unwinding rules. + sp = NextStackFrame<true>(sp); + } + return n; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace_x86-inl.h b/ios/Pods/Flipper-Glog/src/stacktrace_x86-inl.h new file mode 100644 index 000000000..cfd31f783 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace_x86-inl.h @@ -0,0 +1,139 @@ +// Copyright (c) 2000 - 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Produce stack trace + +#include <stdint.h> // for uintptr_t + +#include "utilities.h" // for OS_* macros + +#if !defined(OS_WINDOWS) +#include <unistd.h> +#include <sys/mman.h> +#endif + +#include <stdio.h> // for NULL +#include "stacktrace.h" + +_START_GOOGLE_NAMESPACE_ + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return NULL if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template<bool STRICT_UNWINDING> +static void **NextStackFrame(void **old_sp) { + void **new_sp = (void **) *old_sp; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return NULL; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return NULL; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL; + } + if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL; +#ifdef __i386__ + // On 64-bit machines, the stack pointer can be very close to + // 0xffffffff, so we explicitly check for a pointer into the + // last two pages in the address space + if ((uintptr_t)new_sp >= 0xffffe000) return NULL; +#endif +#if !defined(OS_WINDOWS) + if (!STRICT_UNWINDING) { + // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test + // on AMD-based machines with VDSO-enabled kernels. + // Make an extra sanity check to insure new_sp is readable. + // Note: NextStackFrame<false>() is only called while the program + // is already on its last leg, so it's ok to be slow here. + static int page_size = getpagesize(); + void *new_sp_aligned = (void *)((uintptr_t)new_sp & ~(page_size - 1)); + if (msync(new_sp_aligned, page_size, MS_ASYNC) == -1) + return NULL; + } +#endif + return new_sp; +} + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void **sp; +#ifdef __i386__ + // Stack frame format: + // sp[0] pointer to previous frame + // sp[1] caller address + // sp[2] first argument + // ... + sp = (void **)&result - 2; +#endif + +#ifdef __x86_64__ + // __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8 + unsigned long rbp; + // Move the value of the register %rbp into the local variable rbp. + // We need 'volatile' to prevent this instruction from getting moved + // around during optimization to before function prologue is done. + // An alternative way to achieve this + // would be (before this __asm__ instruction) to call Noop() defined as + // static void Noop() __attribute__ ((noinline)); // prevent inlining + // static void Noop() { asm(""); } // prevent optimizing-away + __asm__ volatile ("mov %%rbp, %0" : "=r" (rbp)); + // Arguments are passed in registers on x86-64, so we can't just + // offset from &result + sp = (void **) rbp; +#endif + + int n = 0; + while (sp && n < max_depth) { + if (*(sp+1) == (void *)0) { + // In 64-bit code, we often see a frame that + // points to itself and has a return address of 0. + break; + } + if (skip_count > 0) { + skip_count--; + } else { + result[n++] = *(sp+1); + } + // Use strict unwinding rules. + sp = NextStackFrame<true>(sp); + } + return n; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/stacktrace_x86_64-inl.h b/ios/Pods/Flipper-Glog/src/stacktrace_x86_64-inl.h new file mode 100644 index 000000000..f7d1dca85 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/stacktrace_x86_64-inl.h @@ -0,0 +1,105 @@ +// Copyright (c) 2005 - 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Arun Sharma +// +// Produce stack trace using libgcc + +extern "C" { +#include <stdlib.h> // for NULL +#include <unwind.h> // ABI defined unwinder +} +#include "stacktrace.h" + +_START_GOOGLE_NAMESPACE_ + +typedef struct { + void **result; + int max_depth; + int skip_count; + int count; +} trace_arg_t; + + +// Workaround for the malloc() in _Unwind_Backtrace() issue. +static _Unwind_Reason_Code nop_backtrace(struct _Unwind_Context *uc, void *opq) { + return _URC_NO_REASON; +} + + +// This code is not considered ready to run until +// static initializers run so that we are guaranteed +// that any malloc-related initialization is done. +static bool ready_to_run = false; +class StackTraceInit { + public: + StackTraceInit() { + // Extra call to force initialization + _Unwind_Backtrace(nop_backtrace, NULL); + ready_to_run = true; + } +}; + +static StackTraceInit module_initializer; // Force initialization + +static _Unwind_Reason_Code GetOneFrame(struct _Unwind_Context *uc, void *opq) { + trace_arg_t *targ = (trace_arg_t *) opq; + + if (targ->skip_count > 0) { + targ->skip_count--; + } else { + targ->result[targ->count++] = (void *) _Unwind_GetIP(uc); + } + + if (targ->count == targ->max_depth) + return _URC_END_OF_STACK; + + return _URC_NO_REASON; +} + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + if (!ready_to_run) + return 0; + + trace_arg_t targ; + + skip_count += 1; // Do not include the "GetStackTrace" frame + + targ.result = result; + targ.max_depth = max_depth; + targ.skip_count = skip_count; + targ.count = 0; + + _Unwind_Backtrace(GetOneFrame, &targ); + + return targ.count; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-Glog/src/symbolize.cc b/ios/Pods/Flipper-Glog/src/symbolize.cc new file mode 100644 index 000000000..f83c30973 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/symbolize.cc @@ -0,0 +1,869 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Satoru Takabayashi +// Stack-footprint reduction work done by Raksit Ashok +// +// Implementation note: +// +// We don't use heaps but only use stacks. We want to reduce the +// stack consumption so that the symbolizer can run on small stacks. +// +// Here are some numbers collected with GCC 4.1.0 on x86: +// - sizeof(Elf32_Sym) = 16 +// - sizeof(Elf32_Shdr) = 40 +// - sizeof(Elf64_Sym) = 24 +// - sizeof(Elf64_Shdr) = 64 +// +// This implementation is intended to be async-signal-safe but uses +// some functions which are not guaranteed to be so, such as memchr() +// and memmove(). We assume they are async-signal-safe. +// +// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE +// macro to add platform specific defines (e.g. OS_OPENBSD). + +#ifdef GLOG_BUILD_CONFIG_INCLUDE +#include GLOG_BUILD_CONFIG_INCLUDE +#endif // GLOG_BUILD_CONFIG_INCLUDE + +#include "utilities.h" + +#if defined(HAVE_SYMBOLIZE) + +#include <limits> + +#include "symbolize.h" +#include "demangle.h" + +_START_GOOGLE_NAMESPACE_ + +// We don't use assert() since it's not guaranteed to be +// async-signal-safe. Instead we define a minimal assertion +// macro. So far, we don't need pretty printing for __FILE__, etc. + +// A wrapper for abort() to make it callable in ? :. +static int AssertFail() { + abort(); + return 0; // Should not reach. +} + +#define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail()) + +static SymbolizeCallback g_symbolize_callback = NULL; +void InstallSymbolizeCallback(SymbolizeCallback callback) { + g_symbolize_callback = callback; +} + +static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = + NULL; +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback) { + g_symbolize_open_object_file_callback = callback; +} + +// This function wraps the Demangle function to provide an interface +// where the input symbol is demangled in-place. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { + char demangled[256]; // Big enough for sane demangled symbols. + if (Demangle(out, demangled, sizeof(demangled))) { + // Demangling succeeded. Copy to out if the space allows. + size_t len = strlen(demangled); + if (len + 1 <= (size_t)out_size) { // +1 for '\0'. + SAFE_ASSERT(len < sizeof(demangled)); + memmove(out, demangled, len + 1); + } + } +} + +_END_GOOGLE_NAMESPACE_ + +#if defined(__ELF__) + +#include <dlfcn.h> +#if defined(OS_OPENBSD) +#include <sys/exec_elf.h> +#else +#include <elf.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "symbolize.h" +#include "config.h" +#include "glog/raw_logging.h" + +// Re-runs fn until it doesn't cause EINTR. +#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) + +_START_GOOGLE_NAMESPACE_ + +// Read up to "count" bytes from file descriptor "fd" into the buffer +// starting at "buf" while handling short reads and EINTR. On +// success, return the number of bytes read. Otherwise, return -1. +static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) { + SAFE_ASSERT(fd >= 0); + SAFE_ASSERT(count <= std::numeric_limits<ssize_t>::max()); + char *buf0 = reinterpret_cast<char *>(buf); + ssize_t num_bytes = 0; + while (num_bytes < count) { + ssize_t len; + NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes)); + if (len < 0) { // There was an error other than EINTR. + return -1; + } + if (len == 0) { // Reached EOF. + break; + } + num_bytes += len; + } + SAFE_ASSERT(num_bytes <= count); + return num_bytes; +} + +// Read up to "count" bytes from "offset" in the file pointed by file +// descriptor "fd" into the buffer starting at "buf". On success, +// return the number of bytes read. Otherwise, return -1. +static ssize_t ReadFromOffset(const int fd, void *buf, + const size_t count, const off_t offset) { + off_t off = lseek(fd, offset, SEEK_SET); + if (off == (off_t)-1) { + return -1; + } + return ReadPersistent(fd, buf, count); +} + +// Try reading exactly "count" bytes from "offset" bytes in a file +// pointed by "fd" into the buffer starting at "buf" while handling +// short reads and EINTR. On success, return true. Otherwise, return +// false. +static bool ReadFromOffsetExact(const int fd, void *buf, + const size_t count, const off_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return len == count; +} + +// Returns elf_header.e_type if the file pointed by fd is an ELF binary. +static int FileGetElfType(const int fd) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return -1; + } + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { + return -1; + } + return elf_header.e_type; +} + +// Read the section headers in the given ELF binary, and if a section +// of the specified type is found, set the output to this section header +// and return true. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool +GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset, + ElfW(Word) type, ElfW(Shdr) *out) { + // Read at most 16 section headers at a time to save read calls. + ElfW(Shdr) buf[16]; + for (int i = 0; i < sh_num;) { + const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); + const ssize_t num_bytes_to_read = + (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf); + const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, + sh_offset + i * sizeof(buf[0])); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_headers_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0])); + for (int j = 0; j < num_headers_in_buf; ++j) { + if (buf[j].sh_type == type) { + *out = buf[j]; + return true; + } + } + i += num_headers_in_buf; + } + return false; +} + +// There is no particular reason to limit section name to 63 characters, +// but there has (as yet) been no need for anything longer either. +const int kMaxSectionNameLen = 64; + +// name_len should include terminating '\0'. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = (elf_header.e_shoff + + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + off_t section_header_offset = (elf_header.e_shoff + + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } + char header_name[kMaxSectionNameLen]; + if (sizeof(header_name) < name_len) { + RAW_LOG(WARNING, "Section name '%s' is too long (%" PRIuS "); " + "section will not be found (even if present).", name, name_len); + // No point in even trying. + return false; + } + off_t name_offset = shstrtab.sh_offset + out->sh_name; + ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); + if (n_read == -1) { + return false; + } else if (n_read != name_len) { + // Short read -- name could be at end of file. + continue; + } + if (memcmp(header_name, name, name_len) == 0) { + return true; + } + } + return false; +} + +// Read a symbol table and look for the symbol containing the +// pc. Iterate over symbols in a symbol table and look for the symbol +// containing "pc". On success, return true and write the symbol name +// to out. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool +FindSymbol(uint64_t pc, const int fd, char *out, int out_size, + uint64_t symbol_offset, const ElfW(Shdr) *strtab, + const ElfW(Shdr) *symtab) { + if (symtab == NULL) { + return false; + } + const int num_symbols = symtab->sh_size / symtab->sh_entsize; + for (int i = 0; i < num_symbols;) { + off_t offset = symtab->sh_offset + i * symtab->sh_entsize; + + // If we are reading Elf64_Sym's, we want to limit this array to + // 32 elements (to keep stack consumption low), otherwise we can + // have a 64 element Elf32_Sym array. +#if __WORDSIZE == 64 +#define NUM_SYMBOLS 32 +#else +#define NUM_SYMBOLS 64 +#endif + + // Read at most NUM_SYMBOLS symbols at once to save read() calls. + ElfW(Sym) buf[NUM_SYMBOLS]; + const ssize_t len = ReadFromOffset(fd, &buf, sizeof(buf), offset); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_symbols_in_buf <= sizeof(buf)/sizeof(buf[0])); + for (int j = 0; j < num_symbols_in_buf; ++j) { + const ElfW(Sym)& symbol = buf[j]; + uint64_t start_address = symbol.st_value; + start_address += symbol_offset; + uint64_t end_address = start_address + symbol.st_size; + if (symbol.st_value != 0 && // Skip null value symbols. + symbol.st_shndx != 0 && // Skip undefined symbols. + start_address <= pc && pc < end_address) { + ssize_t len1 = ReadFromOffset(fd, out, out_size, + strtab->sh_offset + symbol.st_name); + if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) { + return false; + } + return true; // Obtained the symbol name. + } + } + i += num_symbols_in_buf; + } + return false; +} + +// Get the symbol name of "pc" from the file pointed by "fd". Process +// both regular and dynamic symbol tables if necessary. On success, +// write the symbol name to "out" and return true. Otherwise, return +// false. +static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, + char *out, int out_size, + uint64_t map_base_address) { + // Read the ELF header. + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + uint64_t symbol_offset = 0; + if (elf_header.e_type == ET_DYN) { // DSO needs offset adjustment. + ElfW(Phdr) phdr; + // We need to find the PT_LOAD segment corresponding to the read-execute + // file mapping in order to correctly perform the offset adjustment. + for (unsigned i = 0; i != elf_header.e_phnum; ++i) { + if (!ReadFromOffsetExact(fd, &phdr, sizeof(phdr), + elf_header.e_phoff + i * sizeof(phdr))) + return false; + if (phdr.p_type == PT_LOAD && + (phdr.p_flags & (PF_R | PF_X)) == (PF_R | PF_X)) { + // Find the mapped address corresponding to virtual address zero. We do + // this by first adding p_offset. This gives us the mapped address of + // the start of the segment, or in other words the mapped address + // corresponding to the virtual address of the segment. (Note that this + // is distinct from the start address, as p_offset is not guaranteed to + // be page aligned.) We then subtract p_vaddr, which takes us to virtual + // address zero. + symbol_offset = map_base_address + phdr.p_offset - phdr.p_vaddr; + break; + } + } + if (symbol_offset == 0) + return false; + } + + ElfW(Shdr) symtab, strtab; + + // Consult a regular symbol table first. + if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_SYMTAB, &symtab)) { + if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, symbol_offset, + &strtab, &symtab)) { + return true; // Found the symbol in a regular symbol table. + } + } + + // If the symbol is not found, then consult a dynamic symbol table. + if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_DYNSYM, &symtab)) { + if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, symbol_offset, + &strtab, &symtab)) { + return true; // Found the symbol in a dynamic symbol table. + } + } + + return false; +} + +namespace { +// Thin wrapper around a file descriptor so that the file descriptor +// gets closed for sure. +struct FileDescriptor { + const int fd_; + explicit FileDescriptor(int fd) : fd_(fd) {} + ~FileDescriptor() { + if (fd_ >= 0) { + NO_INTR(close(fd_)); + } + } + int get() { return fd_; } + + private: + explicit FileDescriptor(const FileDescriptor&); + void operator=(const FileDescriptor&); +}; + +// Helper class for reading lines from file. +// +// Note: we don't use ProcMapsIterator since the object is big (it has +// a 5k array member) and uses async-unsafe functions such as sscanf() +// and snprintf(). +class LineReader { + public: + explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd), + buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) { + } + + // Read '\n'-terminated line from file. On success, modify "bol" + // and "eol", then return true. Otherwise, return false. + // + // Note: if the last line doesn't end with '\n', the line will be + // dropped. It's an intentional behavior to make the code simple. + bool ReadLine(const char **bol, const char **eol) { + if (BufferIsEmpty()) { // First time. + const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = buf_ + num_bytes; + bol_ = buf_; + } else { + bol_ = eol_ + 1; // Advance to the next line in the buffer. + SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". + if (!HasCompleteLine()) { + const int incomplete_line_length = eod_ - bol_; + // Move the trailing incomplete line to the beginning. + memmove(buf_, bol_, incomplete_line_length); + // Read text from file and append it. + char * const append_pos = buf_ + incomplete_line_length; + const int capacity_left = buf_len_ - incomplete_line_length; + const ssize_t num_bytes = ReadPersistent(fd_, append_pos, + capacity_left); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = append_pos + num_bytes; + bol_ = buf_; + } + } + eol_ = FindLineFeed(); + if (eol_ == NULL) { // '\n' not found. Malformed line. + return false; + } + *eol_ = '\0'; // Replace '\n' with '\0'. + + *bol = bol_; + *eol = eol_; + return true; + } + + // Beginning of line. + const char *bol() { + return bol_; + } + + // End of line. + const char *eol() { + return eol_; + } + + private: + explicit LineReader(const LineReader&); + void operator=(const LineReader&); + + char *FindLineFeed() { + return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); + } + + bool BufferIsEmpty() { + return buf_ == eod_; + } + + bool HasCompleteLine() { + return !BufferIsEmpty() && FindLineFeed() != NULL; + } + + const int fd_; + char * const buf_; + const int buf_len_; + char *bol_; + char *eol_; + const char *eod_; // End of data in "buf_". +}; +} // namespace + +// Place the hex number read from "start" into "*hex". The pointer to +// the first non-hex character or "end" is returned. +static char *GetHex(const char *start, const char *end, uint64_t *hex) { + *hex = 0; + const char *p; + for (p = start; p < end; ++p) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { + *hex = (*hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); + } else { // Encountered the first non-hex character. + break; + } + } + SAFE_ASSERT(p <= end); + return const_cast<char *>(p); +} + +// Searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). +static ATTRIBUTE_NOINLINE int +OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size) { + int object_fd; + + // Open /proc/self/maps. + int maps_fd; + NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); + FileDescriptor wrapped_maps_fd(maps_fd); + if (wrapped_maps_fd.get() < 0) { + return -1; + } + + // Iterate over maps and look for the map containing the pc. Then + // look into the symbol tables inside. + char buf[1024]; // Big enough for line of sane /proc/self/maps + int num_maps = 0; + LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); + while (true) { + num_maps++; + const char *cursor; + const char *eol; + if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. + return -1; + } + + // Start parsing line in /proc/self/maps. Here is an example: + // + // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat + // + // We want start address (08048000), end address (0804c000), flags + // (r-xp) and file name (/bin/cat). + + // Read start address. + cursor = GetHex(cursor, eol, &start_address); + if (cursor == eol || *cursor != '-') { + return -1; // Malformed line. + } + ++cursor; // Skip '-'. + + // Read end address. + uint64_t end_address; + cursor = GetHex(cursor, eol, &end_address); + if (cursor == eol || *cursor != ' ') { + return -1; // Malformed line. + } + ++cursor; // Skip ' '. + + // Check start and end addresses. + if (!(start_address <= pc && pc < end_address)) { + continue; // We skip this map. PC isn't in this map. + } + + // Read flags. Skip flags until we encounter a space or eol. + const char * const flags_start = cursor; + while (cursor < eol && *cursor != ' ') { + ++cursor; + } + // We expect at least four letters for flags (ex. "r-xp"). + if (cursor == eol || cursor < flags_start + 4) { + return -1; // Malformed line. + } + + // Check flags. We are only interested in "r*x" maps. + if (flags_start[0] != 'r' || flags_start[2] != 'x') { + continue; // We skip this map. + } + ++cursor; // Skip ' '. + + // Read file offset. + uint64_t file_offset; + cursor = GetHex(cursor, eol, &file_offset); + if (cursor == eol || *cursor != ' ') { + return -1; // Malformed line. + } + ++cursor; // Skip ' '. + + // Don't subtract 'start_address' from the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + base_address = ((num_maps == 1) ? 0U : start_address) - file_offset; + + // Skip to file name. "cursor" now points to dev. We need to + // skip at least two spaces for dev and inode. + int num_spaces = 0; + while (cursor < eol) { + if (*cursor == ' ') { + ++num_spaces; + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces + // is the beginning of the file name. + break; + } + ++cursor; + } + if (cursor == eol) { + return -1; // Malformed line. + } + + // Finally, "cursor" now points to file name of our interest. + NO_INTR(object_fd = open(cursor, O_RDONLY)); + if (object_fd < 0) { + // Failed to open object file. Copy the object file name to + // |out_file_name|. + strncpy(out_file_name, cursor, out_file_name_size); + // Making sure |out_file_name| is always null-terminated. + out_file_name[out_file_name_size - 1] = '\0'; + return -1; + } + return object_fd; + } +} + +// POSIX doesn't define any async-signal safe function for converting +// an integer to ASCII. We'll have to define our own version. +// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the +// conversion was successful or NULL otherwise. It never writes more than "sz" +// bytes. Output will be truncated as needed, and a NUL character is always +// appended. +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) + return NULL; + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return NULL; + } + + char *start = buf; + + uintptr_t j = i; + + // Handle negative numbers (only for base 10). + if (i < 0 && base == 10) { + j = -i; + + // Make sure we can write the '-' character. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + *start++ = '-'; + } + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char *ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[j % base]; + j /= base; + + if (padding > 0) + padding--; + } while (j > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +// Safely appends string |source| to string |dest|. Never writes past the +// buffer size |dest_size| and guarantees that |dest| is null-terminated. +void SafeAppendString(const char* source, char* dest, int dest_size) { + int dest_string_length = strlen(dest); + SAFE_ASSERT(dest_string_length < dest_size); + dest += dest_string_length; + dest_size -= dest_string_length; + strncpy(dest, source, dest_size); + // Making sure |dest| is always null-terminated. + dest[dest_size - 1] = '\0'; +} + +// Converts a 64-bit value into a hex string, and safely appends it to |dest|. +// Never writes past the buffer size |dest_size| and guarantees that |dest| is +// null-terminated. +void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) { + // 64-bit numbers in hex can have up to 16 digits. + char buf[17] = {'\0'}; + SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); +} + +// The implementation of our symbolization routine. If it +// successfully finds the symbol containing "pc" and obtains the +// symbol name, returns true and write the symbol name to "out". +// Otherwise, returns false. If Callback function is installed via +// InstallSymbolizeCallback(), the function is also called in this function, +// and "out" is used as its output. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, + int out_size) { + uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); + uint64_t start_address = 0; + uint64_t base_address = 0; + int object_fd = -1; + + if (out_size < 1) { + return false; + } + out[0] = '\0'; + SafeAppendString("(", out, out_size); + + if (g_symbolize_open_object_file_callback) { + object_fd = g_symbolize_open_object_file_callback(pc0, start_address, + base_address, out + 1, + out_size - 1); + } else { + object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address, + base_address, + out + 1, + out_size - 1); + } + + // Check whether a file name was returned. + if (object_fd < 0) { + if (out[1]) { + // The object file containing PC was determined successfully however the + // object file was not opened successfully. This is still considered + // success because the object file name and offset are known and tools + // like asan_symbolize.py can be used for the symbolization. + out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. + SafeAppendString("+0x", out, out_size); + SafeAppendHexNumber(pc0 - base_address, out, out_size); + SafeAppendString(")", out, out_size); + return true; + } + // Failed to determine the object file containing PC. Bail out. + return false; + } + FileDescriptor wrapped_object_fd(object_fd); + int elf_type = FileGetElfType(wrapped_object_fd.get()); + if (elf_type == -1) { + return false; + } + if (g_symbolize_callback) { + // Run the call back if it's installed. + // Note: relocation (and much of the rest of this code) will be + // wrong for prelinked shared libraries and PIE executables. + uint64 relocation = (elf_type == ET_DYN) ? start_address : 0; + int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), + pc, out, out_size, + relocation); + if (num_bytes_written > 0) { + out += num_bytes_written; + out_size -= num_bytes_written; + } + } + if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0, + out, out_size, base_address)) { + return false; + } + + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; +} + +_END_GOOGLE_NAMESPACE_ + +#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) + +#include <dlfcn.h> +#include <string.h> + +_START_GOOGLE_NAMESPACE_ + +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, + int out_size) { + Dl_info info; + if (dladdr(pc, &info)) { + if ((int)strlen(info.dli_sname) < out_size) { + strcpy(out, info.dli_sname); + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; + } + } + return false; +} + +_END_GOOGLE_NAMESPACE_ + +#else +# error BUG: HAVE_SYMBOLIZE was wrongly set +#endif + +_START_GOOGLE_NAMESPACE_ + +bool Symbolize(void *pc, char *out, int out_size) { + SAFE_ASSERT(out_size >= 0); + return SymbolizeAndDemangle(pc, out, out_size); +} + +_END_GOOGLE_NAMESPACE_ + +#else /* HAVE_SYMBOLIZE */ + +#include <assert.h> + +#include "config.h" + +_START_GOOGLE_NAMESPACE_ + +// TODO: Support other environments. +bool Symbolize(void *pc, char *out, int out_size) { + assert(0); + return false; +} + +_END_GOOGLE_NAMESPACE_ + +#endif diff --git a/ios/Pods/Flipper-Glog/src/symbolize.h b/ios/Pods/Flipper-Glog/src/symbolize.h new file mode 100644 index 000000000..f61718424 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/symbolize.h @@ -0,0 +1,155 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Satoru Takabayashi +// +// This library provides Symbolize() function that symbolizes program +// counters to their corresponding symbol names on linux platforms. +// This library has a minimal implementation of an ELF symbol table +// reader (i.e. it doesn't depend on libelf, etc.). +// +// The algorithm used in Symbolize() is as follows. +// +// 1. Go through a list of maps in /proc/self/maps and find the map +// containing the program counter. +// +// 2. Open the mapped file and find a regular symbol table inside. +// Iterate over symbols in the symbol table and look for the symbol +// containing the program counter. If such a symbol is found, +// obtain the symbol name, and demangle the symbol if possible. +// If the symbol isn't found in the regular symbol table (binary is +// stripped), try the same thing with a dynamic symbol table. +// +// Note that Symbolize() is originally implemented to be used in +// FailureSignalHandler() in base/google.cc. Hence it doesn't use +// malloc() and other unsafe operations. It should be both +// thread-safe and async-signal-safe. + +#ifndef BASE_SYMBOLIZE_H_ +#define BASE_SYMBOLIZE_H_ + +#include "utilities.h" +#include "config.h" +#include "glog/logging.h" + +#ifdef HAVE_SYMBOLIZE + +#if defined(__ELF__) // defined by gcc +#if defined(__OpenBSD__) +#include <sys/exec_elf.h> +#else +#include <elf.h> +#endif + +#if !defined(ANDROID) +#include <link.h> // For ElfW() macro. +#endif + +// For systems where SIZEOF_VOID_P is not defined, determine it +// based on __LP64__ (defined by gcc on 64-bit systems) +#if !defined(SIZEOF_VOID_P) +# if defined(__LP64__) +# define SIZEOF_VOID_P 8 +# else +# define SIZEOF_VOID_P 4 +# endif +#endif + +// If there is no ElfW macro, let's define it by ourself. +#ifndef ElfW +# if SIZEOF_VOID_P == 4 +# define ElfW(type) Elf32_##type +# elif SIZEOF_VOID_P == 8 +# define ElfW(type) Elf64_##type +# else +# error "Unknown sizeof(void *)" +# endif +#endif + +_START_GOOGLE_NAMESPACE_ + +// Gets the section header for the given name, if it exists. Returns true on +// success. Otherwise, returns false. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out); + +_END_GOOGLE_NAMESPACE_ + +#endif /* __ELF__ */ + +_START_GOOGLE_NAMESPACE_ + +// Restrictions on the callbacks that follow: +// - The callbacks must not use heaps but only use stacks. +// - The callbacks must be async-signal-safe. + +// Installs a callback function, which will be called right before a symbol name +// is printed. The callback is intended to be used for showing a file name and a +// line number preceding a symbol name. +// "fd" is a file descriptor of the object file containing the program +// counter "pc". The callback function should write output to "out" +// and return the size of the output written. On error, the callback +// function should return -1. +typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size, + uint64 relocation); +void InstallSymbolizeCallback(SymbolizeCallback callback); + +// Installs a callback function, which will be called instead of +// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected +// to searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). +typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc, + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size); +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback); + +_END_GOOGLE_NAMESPACE_ + +#endif + +_START_GOOGLE_NAMESPACE_ + +// Symbolizes a program counter. On success, returns true and write the +// symbol name to "out". The symbol name is demangled if possible +// (supports symbols generated by GCC 3.x or newer). Otherwise, +// returns false. +bool Symbolize(void *pc, char *out, int out_size); + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_SYMBOLIZE_H_ diff --git a/ios/Pods/Flipper-Glog/src/utilities.cc b/ios/Pods/Flipper-Glog/src/utilities.cc new file mode 100644 index 000000000..5c88e58d3 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/utilities.cc @@ -0,0 +1,352 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Shinichiro Hamaji + +#include "utilities.h" + +#include <stdio.h> +#include <stdlib.h> + +#include <signal.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#include <time.h> +#if defined(HAVE_SYSCALL_H) +#include <syscall.h> // for syscall() +#elif defined(HAVE_SYS_SYSCALL_H) +#include <sys/syscall.h> // for syscall() +#endif +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif + +#include "base/googleinit.h" + +using std::string; + +_START_GOOGLE_NAMESPACE_ + +static const char* g_program_invocation_short_name = NULL; +static pthread_t g_main_thread_id; + +_END_GOOGLE_NAMESPACE_ + +// The following APIs are all internal. +#ifdef HAVE_STACKTRACE + +#include "stacktrace.h" +#include "symbolize.h" +#include "base/commandlineflags.h" + +GLOG_DEFINE_bool(symbolize_stacktrace, true, + "Symbolize the stack trace in the tombstone"); + +_START_GOOGLE_NAMESPACE_ + +typedef void DebugWriter(const char*, void*); + +// The %p field width for printf() functions is two characters per byte. +// For some environments, add two extra bytes for the leading "0x". +static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); + +static void DebugWriteToStderr(const char* data, void *) { + // This one is signal-safe. + if (write(STDERR_FILENO, data, strlen(data)) < 0) { + // Ignore errors. + } +} + +void DebugWriteToString(const char* data, void *arg) { + reinterpret_cast<string*>(arg)->append(data); +} + +#ifdef HAVE_SYMBOLIZE +// Print a program counter and its symbol name. +static void DumpPCAndSymbol(DebugWriter *writerfn, void *arg, void *pc, + const char * const prefix) { + char tmp[1024]; + const char *symbol = "(unknown)"; + // Symbolizes the previous address of pc because pc may be in the + // next function. The overrun happens when the function ends with + // a call to a function annotated noreturn (e.g. CHECK). + if (Symbolize(reinterpret_cast<char *>(pc) - 1, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + snprintf(buf, sizeof(buf), "%s@ %*p %s\n", + prefix, kPrintfPointerFieldWidth, pc, symbol); + writerfn(buf, arg); +} +#endif + +static void DumpPC(DebugWriter *writerfn, void *arg, void *pc, + const char * const prefix) { + char buf[100]; + snprintf(buf, sizeof(buf), "%s@ %*p\n", + prefix, kPrintfPointerFieldWidth, pc); + writerfn(buf, arg); +} + +// Dump current stack trace as directed by writerfn +static void DumpStackTrace(int skip_count, DebugWriter *writerfn, void *arg) { + // Print stack trace + void* stack[32]; + int depth = GetStackTrace(stack, ARRAYSIZE(stack), skip_count+1); + for (int i = 0; i < depth; i++) { +#if defined(HAVE_SYMBOLIZE) + if (FLAGS_symbolize_stacktrace) { + DumpPCAndSymbol(writerfn, arg, stack[i], " "); + } else { + DumpPC(writerfn, arg, stack[i], " "); + } +#else + DumpPC(writerfn, arg, stack[i], " "); +#endif + } +} + +static void DumpStackTraceAndExit() { + DumpStackTrace(1, DebugWriteToStderr, NULL); + + // TOOD(hamaji): Use signal instead of sigaction? +#ifdef HAVE_SIGACTION + if (IsFailureSignalHandlerInstalled()) { + // Set the default signal handler for SIGABRT, to avoid invoking our + // own signal handler installed by InstallFailureSignalHandler(). + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_handler = SIG_DFL; + sigaction(SIGABRT, &sig_action, NULL); + } +#endif // HAVE_SIGACTION + + abort(); +} + +_END_GOOGLE_NAMESPACE_ + +#endif // HAVE_STACKTRACE + +_START_GOOGLE_NAMESPACE_ + +namespace glog_internal_namespace_ { + +const char* ProgramInvocationShortName() { + if (g_program_invocation_short_name != NULL) { + return g_program_invocation_short_name; + } else { + // TODO(hamaji): Use /proc/self/cmdline and so? + return "UNKNOWN"; + } +} + +bool IsGoogleLoggingInitialized() { + return g_program_invocation_short_name != NULL; +} + +bool is_default_thread() { + if (g_program_invocation_short_name == NULL) { + // InitGoogleLogging() not yet called, so unlikely to be in a different + // thread + return true; + } else { + return pthread_equal(pthread_self(), g_main_thread_id); + } +} + +#ifdef OS_WINDOWS +struct timeval { + long tv_sec, tv_usec; +}; + +// Based on: http://www.google.com/codesearch/p?hl=en#dR3YEbitojA/os_win32.c&q=GetSystemTimeAsFileTime%20license:bsd +// See COPYING for copyright information. +static int gettimeofday(struct timeval *tv, void* tz) { +#define EPOCHFILETIME (116444736000000000ULL) + FILETIME ft; + LARGE_INTEGER li; + uint64 tt; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + tt = (li.QuadPart - EPOCHFILETIME) / 10; + tv->tv_sec = tt / 1000000; + tv->tv_usec = tt % 1000000; + + return 0; +} +#endif + +int64 CycleClock_Now() { + // TODO(hamaji): temporary impementation - it might be too slow. + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast<int64>(tv.tv_sec) * 1000000 + tv.tv_usec; +} + +int64 UsecToCycles(int64 usec) { + return usec; +} + +WallTime WallTime_Now() { + // Now, cycle clock is retuning microseconds since the epoch. + return CycleClock_Now() * 0.000001; +} + +static int32 g_main_thread_pid = getpid(); +int32 GetMainThreadPid() { + return g_main_thread_pid; +} + +bool PidHasChanged() { + int32 pid = getpid(); + if (g_main_thread_pid == pid) { + return false; + } + g_main_thread_pid = pid; + return true; +} + +pid_t GetTID() { + // On Linux and MacOSX, we try to use gettid(). +#if defined OS_LINUX || defined OS_MACOSX +#ifndef __NR_gettid +#ifdef OS_MACOSX +#define __NR_gettid SYS_gettid +#elif ! defined __i386__ +#error "Must define __NR_gettid for non-x86 platforms" +#else +#define __NR_gettid 224 +#endif +#endif + static bool lacks_gettid = false; + if (!lacks_gettid) { + pid_t tid = syscall(__NR_gettid); + if (tid != -1) { + return tid; + } + // Technically, this variable has to be volatile, but there is a small + // performance penalty in accessing volatile variables and there should + // not be any serious adverse effect if a thread does not immediately see + // the value change to "true". + lacks_gettid = true; + } +#endif // OS_LINUX || OS_MACOSX + + // If gettid() could not be used, we use one of the following. +#if defined OS_LINUX + return getpid(); // Linux: getpid returns thread ID when gettid is absent +#elif defined OS_WINDOWS || defined OS_CYGWIN + return GetCurrentThreadId(); +#else + // If none of the techniques above worked, we use pthread_self(). + return (pid_t)(uintptr_t)pthread_self(); +#endif +} + +const char* const_basename(const char* filepath) { + const char* base = strrchr(filepath, '/'); +#ifdef OS_WINDOWS // Look for either path separator in Windows + if (!base) + base = strrchr(filepath, '\\'); +#endif + return base ? (base+1) : filepath; +} + +static string g_my_user_name; +const string& MyUserName() { + return g_my_user_name; +} +static void MyUserNameInitializer() { + // TODO(hamaji): Probably this is not portable. +#if defined(OS_WINDOWS) + const char* user = getenv("USERNAME"); +#else + const char* user = getenv("USER"); +#endif + if (user != NULL) { + g_my_user_name = user; + } else { + g_my_user_name = "invalid-user"; + } +} +REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer()); + +#ifdef HAVE_STACKTRACE +void DumpStackTraceToString(string* stacktrace) { + DumpStackTrace(1, DebugWriteToString, stacktrace); +} +#endif + +// We use an atomic operation to prevent problems with calling CrashReason +// from inside the Mutex implementation (potentially through RAW_CHECK). +static const CrashReason* g_reason = 0; + +void SetCrashReason(const CrashReason* r) { + sync_val_compare_and_swap(&g_reason, + reinterpret_cast<const CrashReason*>(0), + r); +} + +void InitGoogleLoggingUtilities(const char* argv0) { + CHECK(!IsGoogleLoggingInitialized()) + << "You called InitGoogleLogging() twice!"; + const char* slash = strrchr(argv0, '/'); +#ifdef OS_WINDOWS + if (!slash) slash = strrchr(argv0, '\\'); +#endif + g_program_invocation_short_name = slash ? slash + 1 : argv0; + g_main_thread_id = pthread_self(); + +#ifdef HAVE_STACKTRACE + InstallFailureFunction(&DumpStackTraceAndExit); +#endif +} + +void ShutdownGoogleLoggingUtilities() { + CHECK(IsGoogleLoggingInitialized()) + << "You called ShutdownGoogleLogging() without calling InitGoogleLogging() first!"; + g_program_invocation_short_name = NULL; +#ifdef HAVE_SYSLOG_H + closelog(); +#endif +} + +} // namespace glog_internal_namespace_ + +_END_GOOGLE_NAMESPACE_ + +// Make an implementation of stacktrace compiled. +#ifdef STACKTRACE_H +# include STACKTRACE_H +#endif diff --git a/ios/Pods/Flipper-Glog/src/utilities.h b/ios/Pods/Flipper-Glog/src/utilities.h new file mode 100644 index 000000000..5f79968ef --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/utilities.h @@ -0,0 +1,226 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Shinichiro Hamaji +// +// Define utilties for glog internal usage. + +#ifndef UTILITIES_H__ +#define UTILITIES_H__ + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define OS_WINDOWS +#elif defined(__CYGWIN__) || defined(__CYGWIN32__) +# define OS_CYGWIN +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define OS_LINUX +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +# define OS_MACOSX +#elif defined(__FreeBSD__) +# define OS_FREEBSD +#elif defined(__NetBSD__) +# define OS_NETBSD +#elif defined(__OpenBSD__) +# define OS_OPENBSD +#else +// TODO(hamaji): Add other platforms. +#endif + +// printf macros for size_t, in the style of inttypes.h +#ifdef _LP64 +#define __PRIS_PREFIX "z" +#else +#define __PRIS_PREFIX +#endif + +// Use these macros after a % in a printf format string +// to get correct 32/64 bit behavior, like this: +// size_t size = records.size(); +// printf("%"PRIuS"\n", size); + +#define PRIdS __PRIS_PREFIX "d" +#define PRIxS __PRIS_PREFIX "x" +#define PRIuS __PRIS_PREFIX "u" +#define PRIXS __PRIS_PREFIX "X" +#define PRIoS __PRIS_PREFIX "o" + +#include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE + +#include <string> + +#if defined(OS_WINDOWS) +# include "port.h" +#endif + +#include "config.h" +#include "glog/logging.h" + +// There are three different ways we can try to get the stack trace: +// +// 1) The libunwind library. This is still in development, and as a +// separate library adds a new dependency, but doesn't need a frame +// pointer. It also doesn't call malloc. +// +// 2) Our hand-coded stack-unwinder. This depends on a certain stack +// layout, which is used by gcc (and those systems using a +// gcc-compatible ABI) on x86 systems, at least since gcc 2.95. +// It uses the frame pointer to do its work. +// +// 3) The gdb unwinder -- also the one used by the c++ exception code. +// It's obviously well-tested, but has a fatal flaw: it can call +// malloc() from the unwinder. This is a problem because we're +// trying to use the unwinder to instrument malloc(). +// +// Note: if you add a new implementation here, make sure it works +// correctly when GetStackTrace() is called with max_depth == 0. +// Some code may do that. + +#if defined(HAVE_LIB_UNWIND) +# define STACKTRACE_H "stacktrace_libunwind-inl.h" +#elif !defined(NO_FRAME_POINTER) +# if defined(__i386__) && __GNUC__ >= 2 +# define STACKTRACE_H "stacktrace_x86-inl.h" +# elif defined(__x86_64__) && __GNUC__ >= 2 && HAVE_UNWIND_H +# define STACKTRACE_H "stacktrace_x86_64-inl.h" +# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2 +# define STACKTRACE_H "stacktrace_powerpc-inl.h" +# endif +#endif + +#if !defined(STACKTRACE_H) && defined(HAVE_EXECINFO_H) +# define STACKTRACE_H "stacktrace_generic-inl.h" +#endif + +#if defined(STACKTRACE_H) +# define HAVE_STACKTRACE +#endif + +// defined by gcc +#if defined(__ELF__) && defined(OS_LINUX) +# define HAVE_SYMBOLIZE +#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) +// Use dladdr to symbolize. +# define HAVE_SYMBOLIZE +#endif + +#ifndef ARRAYSIZE +// There is a better way, but this is good enough for our purpose. +# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) +#endif + +_START_GOOGLE_NAMESPACE_ + +namespace glog_internal_namespace_ { + +#ifdef HAVE___ATTRIBUTE__ +# define ATTRIBUTE_NOINLINE __attribute__ ((noinline)) +# define HAVE_ATTRIBUTE_NOINLINE +#else +# define ATTRIBUTE_NOINLINE +#endif + +const char* ProgramInvocationShortName(); + +bool IsGoogleLoggingInitialized(); + +bool is_default_thread(); + +int64 CycleClock_Now(); + +int64 UsecToCycles(int64 usec); + +typedef double WallTime; +WallTime WallTime_Now(); + +int32 GetMainThreadPid(); +bool PidHasChanged(); + +pid_t GetTID(); + +const std::string& MyUserName(); + +// Get the part of filepath after the last path separator. +// (Doesn't modify filepath, contrary to basename() in libgen.h.) +const char* const_basename(const char* filepath); + +// Wrapper of __sync_val_compare_and_swap. If the GCC extension isn't +// defined, we try the CPU specific logics (we only support x86 and +// x86_64 for now) first, then use a naive implementation, which has a +// race condition. +template<typename T> +inline T sync_val_compare_and_swap(T* ptr, T oldval, T newval) { +#if defined(HAVE___SYNC_VAL_COMPARE_AND_SWAP) + return __sync_val_compare_and_swap(ptr, oldval, newval); +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + T ret; + __asm__ __volatile__("lock; cmpxchg %1, (%2);" + :"=a"(ret) + // GCC may produces %sil or %dil for + // constraint "r", but some of apple's gas + // dosn't know the 8 bit registers. + // We use "q" to avoid these registers. + :"q"(newval), "q"(ptr), "a"(oldval) + :"memory", "cc"); + return ret; +#else + T ret = *ptr; + if (ret == oldval) { + *ptr = newval; + } + return ret; +#endif +} + +void DumpStackTraceToString(std::string* stacktrace); + +struct CrashReason { + CrashReason() : filename(0), line_number(0), message(0), depth(0) {} + + const char* filename; + int line_number; + const char* message; + + // We'll also store a bit of stack trace context at the time of crash as + // it may not be available later on. + void* stack[32]; + int depth; +}; + +void SetCrashReason(const CrashReason* r); + +void InitGoogleLoggingUtilities(const char* argv0); +void ShutdownGoogleLoggingUtilities(); + +} // namespace glog_internal_namespace_ + +_END_GOOGLE_NAMESPACE_ + +using namespace GOOGLE_NAMESPACE::glog_internal_namespace_; + +#endif // UTILITIES_H__ diff --git a/ios/Pods/Flipper-Glog/src/vlog_is_on.cc b/ios/Pods/Flipper-Glog/src/vlog_is_on.cc new file mode 100644 index 000000000..4c95583b6 --- /dev/null +++ b/ios/Pods/Flipper-Glog/src/vlog_is_on.cc @@ -0,0 +1,251 @@ +// Copyright (c) 1999, 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney and many others +// +// Broken out from logging.cc by Soren Lassen +// logging_unittest.cc covers the functionality herein + +#include "utilities.h" + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <cstdio> +#include <string> +#include "base/commandlineflags.h" +#include "glog/logging.h" +#include "glog/raw_logging.h" +#include "base/googleinit.h" + +// glog doesn't have annotation +#define ANNOTATE_BENIGN_RACE(address, description) + +using std::string; + +GLOG_DEFINE_int32(v, 0, "Show all VLOG(m) messages for m <= this." +" Overridable by --vmodule."); + +GLOG_DEFINE_string(vmodule, "", "per-module verbose level." +" Argument is a comma-separated list of <module name>=<log level>." +" <module name> is a glob pattern, matched against the filename base" +" (that is, name ignoring .cc/.h./-inl.h)." +" <log level> overrides any value given by --v."); + +_START_GOOGLE_NAMESPACE_ + +namespace glog_internal_namespace_ { + +// Implementation of fnmatch that does not need 0-termination +// of arguments and does not allocate any memory, +// but we only support "*" and "?" wildcards, not the "[...]" patterns. +// It's not a static function for the unittest. +GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern, + size_t patt_len, + const char* str, + size_t str_len) { + size_t p = 0; + size_t s = 0; + while (1) { + if (p == patt_len && s == str_len) return true; + if (p == patt_len) return false; + if (s == str_len) return p+1 == patt_len && pattern[p] == '*'; + if (pattern[p] == str[s] || pattern[p] == '?') { + p += 1; + s += 1; + continue; + } + if (pattern[p] == '*') { + if (p+1 == patt_len) return true; + do { + if (SafeFNMatch_(pattern+(p+1), patt_len-(p+1), str+s, str_len-s)) { + return true; + } + s += 1; + } while (s != str_len); + return false; + } + return false; + } +} + +} // namespace glog_internal_namespace_ + +using glog_internal_namespace_::SafeFNMatch_; + +int32 kLogSiteUninitialized = 1000; + +// List of per-module log levels from FLAGS_vmodule. +// Once created each element is never deleted/modified +// except for the vlog_level: other threads will read VModuleInfo blobs +// w/o locks and we'll store pointers to vlog_level at VLOG locations +// that will never go away. +// We can't use an STL struct here as we wouldn't know +// when it's safe to delete/update it: other threads need to use it w/o locks. +struct VModuleInfo { + string module_pattern; + mutable int32 vlog_level; // Conceptually this is an AtomicWord, but it's + // too much work to use AtomicWord type here + // w/o much actual benefit. + const VModuleInfo* next; +}; + +// This protects the following global variables. +static Mutex vmodule_lock; +// Pointer to head of the VModuleInfo list. +// It's a map from module pattern to logging level for those module(s). +static VModuleInfo* vmodule_list = 0; +// Boolean initialization flag. +static bool inited_vmodule = false; + +// L >= vmodule_lock. +static void VLOG2Initializer() { + vmodule_lock.AssertHeld(); + // Can now parse --vmodule flag and initialize mapping of module-specific + // logging levels. + inited_vmodule = false; + const char* vmodule = FLAGS_vmodule.c_str(); + const char* sep; + VModuleInfo* head = NULL; + VModuleInfo* tail = NULL; + while ((sep = strchr(vmodule, '=')) != NULL) { + string pattern(vmodule, sep - vmodule); + int module_level; + if (sscanf(sep, "=%d", &module_level) == 1) { + VModuleInfo* info = new VModuleInfo; + info->module_pattern = pattern; + info->vlog_level = module_level; + if (head) tail->next = info; + else head = info; + tail = info; + } + // Skip past this entry + vmodule = strchr(sep, ','); + if (vmodule == NULL) break; + vmodule++; // Skip past "," + } + if (head) { // Put them into the list at the head: + tail->next = vmodule_list; + vmodule_list = head; + } + inited_vmodule = true; +} + +// This can be called very early, so we use SpinLock and RAW_VLOG here. +int SetVLOGLevel(const char* module_pattern, int log_level) { + int result = FLAGS_v; + int const pattern_len = strlen(module_pattern); + bool found = false; + { + MutexLock l(&vmodule_lock); // protect whole read-modify-write + for (const VModuleInfo* info = vmodule_list; + info != NULL; info = info->next) { + if (info->module_pattern == module_pattern) { + if (!found) { + result = info->vlog_level; + found = true; + } + info->vlog_level = log_level; + } else if (!found && + SafeFNMatch_(info->module_pattern.c_str(), + info->module_pattern.size(), + module_pattern, pattern_len)) { + result = info->vlog_level; + found = true; + } + } + if (!found) { + VModuleInfo* info = new VModuleInfo; + info->module_pattern = module_pattern; + info->vlog_level = log_level; + info->next = vmodule_list; + vmodule_list = info; + } + } + RAW_VLOG(1, "Set VLOG level for \"%s\" to %d", module_pattern, log_level); + return result; +} + +// NOTE: Individual VLOG statements cache the integer log level pointers. +// NOTE: This function must not allocate memory or require any locks. +bool InitVLOG3__(int32** site_flag, int32* site_default, + const char* fname, int32 verbose_level) { + MutexLock l(&vmodule_lock); + bool read_vmodule_flag = inited_vmodule; + if (!read_vmodule_flag) { + VLOG2Initializer(); + } + + // protect the errno global in case someone writes: + // VLOG(..) << "The last error was " << strerror(errno) + int old_errno = errno; + + // site_default normally points to FLAGS_v + int32* site_flag_value = site_default; + + // Get basename for file + const char* base = strrchr(fname, '/'); + base = base ? (base+1) : fname; + const char* base_end = strchr(base, '.'); + size_t base_length = base_end ? size_t(base_end - base) : strlen(base); + + // Trim out trailing "-inl" if any + if (base_length >= 4 && (memcmp(base+base_length-4, "-inl", 4) == 0)) { + base_length -= 4; + } + + // TODO: Trim out _unittest suffix? Perhaps it is better to have + // the extra control and just leave it there. + + // find target in vector of modules, replace site_flag_value with + // a module-specific verbose level, if any. + for (const VModuleInfo* info = vmodule_list; + info != NULL; info = info->next) { + if (SafeFNMatch_(info->module_pattern.c_str(), info->module_pattern.size(), + base, base_length)) { + site_flag_value = &info->vlog_level; + // value at info->vlog_level is now what controls + // the VLOG at the caller site forever + break; + } + } + + // Cache the vlog value pointer if --vmodule flag has been parsed. + ANNOTATE_BENIGN_RACE(site_flag, + "*site_flag may be written by several threads," + " but the value will be the same"); + if (read_vmodule_flag) *site_flag = site_flag_value; + + // restore the errno in case something recoverable went wrong during + // the initialization of the VLOG mechanism (see above note "protect the..") + errno = old_errno; + return *site_flag_value >= verbose_level; +} + +_END_GOOGLE_NAMESPACE_ diff --git a/ios/Pods/Flipper-PeerTalk/LICENSE.txt b/ios/Pods/Flipper-PeerTalk/LICENSE.txt new file mode 100644 index 000000000..59ce36f9f --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2012 Rasmus Andersson <http://rsms.me/> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/ios/Pods/Flipper-PeerTalk/README.md b/ios/Pods/Flipper-PeerTalk/README.md new file mode 100644 index 000000000..84674c342 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/README.md @@ -0,0 +1,64 @@ +# peertalk + +PeerTalk is an iOS and Mac Cocoa library for communicating over USB. + + + ┌──────────────────────────────┐ + │ ┌──────────────────────────┐ │ + │ │ │ │ + ┌─────────┐ │ │ │ │ + │┌───────┐│ │ │ Hello │ │ + ││ ││ │ │ │ │ + ││ Hello ││ │ │ │ │ + ││ ││ │ │ │ │ + │└───────┘│ │ └──────────────────────────┘ │ + │ ⃝ │ \ ─────────────────────────── \ + └────╦────┘ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ + ║ ╔══════════■ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ + ╚═════════╝ \ ─────────────────────────── \ + - meep - └─────────────────────────────┘ + - beep - + + +#### Highlights + +1. Provides you with USB device attach/detach events and attached device's info + +2. Can connect to TCP services on supported attached devices (e.g. an iPhone), + bridging the communication over USB transport + +3. Offers a higher-level API (PTChannel and PTProtocol) for convenient + implementations. + +4. Tested and designed for libdispatch (aka Grand Central Dispatch). + +Grab the goods from [https://github.com/rsms/peertalk](https://github.com/rsms/peertalk) + + +### Usage in Apple App Store + +PeerTalk has successfully been released on both the iOS and OS X app store. + +A great example is [Duet Display](http://www.duetdisplay.com/) which is a fantastic piece of software allowing you to use your iDevice as an extra display for your Mac using the Lightning or 30-pin cable. + +Facebook's [Origami](http://facebook.github.io/origami/) uses PeerTalk for it's Origami Live iOS app (in fact, this is where PeerTalk was first used, back in 2012) + +This *probably* means that you can use PeerTalk for apps aiming at the App Store. + +## Getting started + +Suck down the code and open *peertalk.xcodeproj* in Xcode 4.3 or later on OS X 10.7 or later. + +1. Select the "peertalk" target and hit Cmd+U (Product → Test) and verify that the unit tests passed. + +2. Select the "Peertalk Example" target and hit Cmd+R (Product → Run). You should se a less than-pretty, standard window with some text saying it's ready. That's the OS X example app you're looking at. + +3. In Xcode, select the "Peertalk iOS Example" target for the iPhone Simulator, and hit Cmd+R (Product → Run). There should be some action going on now. Try sending some messages between the OS X app and the app running in the iPhone simulator. + +3. Connect your iOS device (iPhone, iPod or iPad) and kill the iPhone simulator and go back to Xcode. Select the "Peertalk iOS Example" target for your connected iOS device. Hit Cmd+R (Product → Run) to build and run the sample app on your device. + +It _should_ work. + +Demo video: [http://www.youtube.com/watch?v=kQPWy8N0mBg](http://www.youtube.com/watch?v=kQPWy8N0mBg) + +<iframe width="880" height="530" src="http://www.youtube.com/embed/kQPWy8N0mBg?hd=1&rel=0" frameborder="0" allowfullscreen></iframe> diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.h b/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.h new file mode 100644 index 000000000..e5cfdd95f --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.h @@ -0,0 +1,126 @@ +// +// Represents a communication channel between two endpoints talking the same +// PTProtocol. +// +#import <Foundation/Foundation.h> +#import <dispatch/dispatch.h> +#import <netinet/in.h> +#import <sys/socket.h> + +#import "PTProtocol.h" +#import "PTUSBHub.h" + +@class PTData, PTAddress; +@protocol PTChannelDelegate; + +@interface PTChannel : NSObject + +// Delegate +@property (strong) id<PTChannelDelegate> delegate; + +// Communication protocol. Must not be nil. +@property PTProtocol *protocol; + +// YES if this channel is a listening server +@property (readonly) BOOL isListening; + +// YES if this channel is a connected peer +@property (readonly) BOOL isConnected; + +// Arbitrary attachment. Note that if you set this, the object will grow by +// 8 bytes (64 bits). +@property (strong) id userInfo; + +// Create a new channel using the shared PTProtocol for the current dispatch +// queue, with *delegate*. ++ (PTChannel*)channelWithDelegate:(id<PTChannelDelegate>)delegate; + + +// Initialize a new frame channel, configuring it to use the calling queue's +// protocol instance (as returned by [PTProtocol sharedProtocolForQueue: +// dispatch_get_current_queue()]) +- (id)init; + +// Initialize a new frame channel with a specific protocol. +- (id)initWithProtocol:(PTProtocol*)protocol; + +// Initialize a new frame channel with a specific protocol and delegate. +- (id)initWithProtocol:(PTProtocol*)protocol delegate:(id<PTChannelDelegate>)delegate; + + +// Connect to a TCP port on a device connected over USB +- (void)connectToPort:(int)port overUSBHub:(PTUSBHub*)usbHub deviceID:(NSNumber*)deviceID callback:(void(^)(NSError *error))callback; + +// Connect to a TCP port at IPv4 address. Provided port must NOT be in network +// byte order. Provided in_addr_t must NOT be in network byte order. A value returned +// from inet_aton() will be in network byte order. You can use a value of inet_aton() +// as the address parameter here, but you must flip the byte order before passing the +// in_addr_t to this function. +- (void)connectToPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error, PTAddress *address))callback; + +// Listen for connections on port and address, effectively starting a socket +// server. Provided port must NOT be in network byte order. Provided in_addr_t +// must NOT be in network byte order. +// For this to make sense, you should provide a onAccept block handler +// or a delegate implementing ioFrameChannel:didAcceptConnection:. +- (void)listenOnPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error))callback; + +// Send a frame with an optional payload and optional callback. +// If *callback* is not NULL, the block is invoked when either an error occured +// or when the frame (and payload, if any) has been completely sent. +- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload callback:(void(^)(NSError *error))callback; + +// Lower-level method to assign a connected dispatch IO channel to this channel +- (BOOL)startReadingFromConnectedChannel:(dispatch_io_t)channel error:(__autoreleasing NSError**)error; + +// Close the channel, preventing further reading and writing. Any ongoing and +// queued reads and writes will be aborted. +- (void)close; + +// "graceful" close -- any ongoing and queued reads and writes will complete +// before the channel ends. +- (void)cancel; + +@end + + +// Wraps a mapped dispatch_data_t object. The memory pointed to by *data* is +// valid until *dispatchData* is deallocated (normally when the receiver is +// deallocated). +@interface PTData : NSObject +@property (readonly) dispatch_data_t dispatchData; +@property (readonly) void *data; +@property (readonly) size_t length; +@end + + +// Represents a peer's address +@interface PTAddress : NSObject +// For network addresses, this is the IP address in textual format +@property (readonly) NSString *name; +// For network addresses, this is the port number. Otherwise 0 (zero). +@property (readonly) NSInteger port; +@end + + +// Protocol for PTChannel delegates +@protocol PTChannelDelegate <NSObject> + +@required +// Invoked when a new frame has arrived on a channel. +- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData*)payload; + +@optional +// Invoked to accept an incoming frame on a channel. Reply NO ignore the +// incoming frame. If not implemented by the delegate, all frames are accepted. +- (BOOL)ioFrameChannel:(PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize; + +// Invoked when the channel closed. If it closed because of an error, *error* is +// a non-nil NSError object. +- (void)ioFrameChannel:(PTChannel*)channel didEndWithError:(NSError*)error; + +// For listening channels, this method is invoked when a new connection has been +// accepted. +- (void)ioFrameChannel:(PTChannel*)channel didAcceptConnection:(PTChannel*)otherChannel fromAddress:(PTAddress*)address; + +@end diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.m b/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.m new file mode 100644 index 000000000..1573c1125 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTChannel.m @@ -0,0 +1,634 @@ +#import "PTChannel.h" +#import "PTPrivate.h" + +#include <sys/ioctl.h> +#include <sys/un.h> +#include <err.h> +#include <fcntl.h> +#include <arpa/inet.h> +#import <objc/runtime.h> + +// Read member of sockaddr_in without knowing the family +#define PT_SOCKADDR_ACCESS(ss, member4, member6) \ + (((ss)->ss_family == AF_INET) ? ( \ + ((const struct sockaddr_in *)(ss))->member4 \ + ) : ( \ + ((const struct sockaddr_in6 *)(ss))->member6 \ + )) + +// Connection state (storage: uint8_t) +#define kConnStateNone 0 +#define kConnStateConnecting 1 +#define kConnStateConnected 2 +#define kConnStateListening 3 + +// Delegate support optimization (storage: uint8_t) +#define kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize 1 +#define kDelegateFlagImplements_ioFrameChannel_didEndWithError 2 +#define kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress 4 + + +#pragma mark - +// Note: We are careful about the size of this struct as each connected peer +// implies one allocation of this struct. +@interface PTChannel () { + dispatch_io_t dispatchObj_channel_; + dispatch_source_t dispatchObj_source_; + NSError *endError_; // 64 bit +@public // here be hacks + id<PTChannelDelegate> delegate_; // 64 bit + uint8_t delegateFlags_; // 8 bit +@private + uint8_t connState_; // 8 bit + //char padding_[6]; // 48 bit -- only if allocation speed is important +} +- (id)initWithProtocol:(PTProtocol*)protocol delegate:(id<PTChannelDelegate>)delegate; +- (BOOL)acceptIncomingConnection:(dispatch_fd_t)serverSocketFD; +@end +static const uint8_t kUserInfoKey; + +#pragma mark - +@interface PTData () +- (id)initWithMappedDispatchData:(dispatch_data_t)mappedContiguousData data:(void*)data length:(size_t)length; +@end + +#pragma mark - +@interface PTAddress () { + struct sockaddr_storage sockaddr_; +} +- (id)initWithSockaddr:(const struct sockaddr_storage*)addr; +@end + +#pragma mark - +@implementation PTChannel + +@synthesize protocol = protocol_; + + ++ (PTChannel*)channelWithDelegate:(id<PTChannelDelegate>)delegate { + return [[PTChannel alloc] initWithProtocol:[PTProtocol sharedProtocolForQueue:dispatch_get_main_queue()] delegate:delegate]; +} + + +- (id)initWithProtocol:(PTProtocol*)protocol delegate:(id<PTChannelDelegate>)delegate { + if (!(self = [super init])) return nil; + protocol_ = protocol; + self.delegate = delegate; + return self; +} + + +- (id)initWithProtocol:(PTProtocol*)protocol { + if (!(self = [super init])) return nil; + protocol_ = protocol; + return self; +} + + +- (id)init { + return [self initWithProtocol:[PTProtocol sharedProtocolForQueue:dispatch_get_main_queue()]]; +} + + +- (void)dealloc { +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchObj_channel_) dispatch_release(dispatchObj_channel_); + else if (dispatchObj_source_) dispatch_release(dispatchObj_source_); +#endif +} + + +- (BOOL)isConnected { + return connState_ == kConnStateConnecting || connState_ == kConnStateConnected; +} + + +- (BOOL)isListening { + return connState_ == kConnStateListening; +} + + +- (id)userInfo { + return objc_getAssociatedObject(self, (void*)&kUserInfoKey); +} + +- (void)setUserInfo:(id)userInfo { + objc_setAssociatedObject(self, (const void*)&kUserInfoKey, userInfo, OBJC_ASSOCIATION_RETAIN); +} + + +- (void)setConnState:(char)connState { + connState_ = connState; +} + + +- (void)setDispatchChannel:(dispatch_io_t)channel { + assert(connState_ == kConnStateConnecting || connState_ == kConnStateConnected || connState_ == kConnStateNone); + dispatch_io_t prevChannel = dispatchObj_channel_; + if (prevChannel != channel) { + dispatchObj_channel_ = channel; +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchObj_channel_) dispatch_retain(dispatchObj_channel_); + if (prevChannel) dispatch_release(prevChannel); +#endif + if (!dispatchObj_channel_ && !dispatchObj_source_) { + connState_ = kConnStateNone; + } + } +} + + +- (void)setDispatchSource:(dispatch_source_t)source { + assert(connState_ == kConnStateListening || connState_ == kConnStateNone); + dispatch_source_t prevSource = dispatchObj_source_; + if (prevSource != source) { + dispatchObj_source_ = source; +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchObj_source_) dispatch_retain(dispatchObj_source_); + if (prevSource) dispatch_release(prevSource); +#endif + if (!dispatchObj_channel_ && !dispatchObj_source_) { + connState_ = kConnStateNone; + } + } +} + + +- (id<PTChannelDelegate>)delegate { + return delegate_; +} + + +- (void)setDelegate:(id<PTChannelDelegate>)delegate { + delegate_ = delegate; + delegateFlags_ = 0; + if (!delegate_) { + return; + } + + if ([delegate respondsToSelector:@selector(ioFrameChannel:shouldAcceptFrameOfType:tag:payloadSize:)]) { + delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize; + } + + if (delegate_ && [delegate respondsToSelector:@selector(ioFrameChannel:didEndWithError:)]) { + delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_didEndWithError; + } + + if (delegate_ && [delegate respondsToSelector:@selector(ioFrameChannel:didAcceptConnection:fromAddress:)]) { + delegateFlags_ |= kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress; + } +} + + +//- (void)setFileDescriptor:(dispatch_fd_t)fd { +// [self setDispatchChannel:dispatch_io_create(DISPATCH_IO_STREAM, fd, protocol_.queue, ^(int error) { +// close(fd); +// })]; +//} + + +#pragma mark - Connecting + + +- (void)connectToPort:(int)port overUSBHub:(PTUSBHub*)usbHub deviceID:(NSNumber*)deviceID callback:(void(^)(NSError *error))callback { + assert(protocol_ != NULL); + if (connState_ != kConnStateNone) { + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil]); + return; + } + connState_ = kConnStateConnecting; + [usbHub connectToDevice:deviceID port:port onStart:^(NSError *err, dispatch_io_t dispatchChannel) { + NSError *error = err; + if (!error) { + [self startReadingFromConnectedChannel:dispatchChannel error:&error]; + } else { + connState_ = kConnStateNone; + } + if (callback) callback(error); + } onEnd:^(NSError *error) { + if (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) { + [delegate_ ioFrameChannel:self didEndWithError:error]; + } + endError_ = nil; + }]; +} + + +- (void)connectToPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error, PTAddress *address))callback { + assert(protocol_ != NULL); + if (connState_ != kConnStateNone) { + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil], nil); + return; + } + connState_ = kConnStateConnecting; + + int error = 0; + + // Create socket + dispatch_fd_t fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + perror("socket(AF_INET, SOCK_STREAM, 0) failed"); + error = errno; + if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil], nil); + return; + } + + // Connect socket + struct sockaddr_in addr; + bzero((char *)&addr, sizeof(addr)); + + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + //addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + //addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_addr.s_addr = htonl(address); + + // prevent SIGPIPE + int on = 1; + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + + // int socket, const struct sockaddr *address, socklen_t address_len + if (connect(fd, (const struct sockaddr *)&addr, addr.sin_len) == -1) { + //perror("connect"); + error = errno; + close(fd); + if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil); + return; + } + + // get actual address + //if (getsockname(fd, (struct sockaddr*)&addr, (socklen_t*)&addr.sin_len) == -1) { + // error = errno; + // close(fd); + // if (callback) callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil); + // return; + //} + + dispatch_io_t dispatchChannel = dispatch_io_create(DISPATCH_IO_STREAM, fd, protocol_.queue, ^(int error) { + close(fd); + if (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) { + NSError *err = error == 0 ? endError_ : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]; + [delegate_ ioFrameChannel:self didEndWithError:err]; + endError_ = nil; + } + }); + + if (!dispatchChannel) { + close(fd); + if (callback) callback([[NSError alloc] initWithDomain:@"PTError" code:0 userInfo:nil], nil); + return; + } + + // Success + NSError *err = nil; + PTAddress *ptAddr = [[PTAddress alloc] initWithSockaddr:(struct sockaddr_storage*)&addr]; + [self startReadingFromConnectedChannel:dispatchChannel error:&err]; + if (callback) callback(err, ptAddr); +} + + +#pragma mark - Listening and serving + + +- (void)listenOnPort:(in_port_t)port IPv4Address:(in_addr_t)address callback:(void(^)(NSError *error))callback { + assert(dispatchObj_source_ == nil); + + // Create socket + dispatch_fd_t fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); + return; + } + + // Connect socket + struct sockaddr_in addr; + bzero((char *)&addr, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + //addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + //addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_addr.s_addr = htonl(address); + + socklen_t socklen = sizeof(addr); + + int on = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + close(fd); + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); + return; + } + + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + close(fd); + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); + return; + } + + if (bind(fd, (struct sockaddr*)&addr, socklen) != 0) { + close(fd); + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); + return; + } + + if (listen(fd, 512) != 0) { + close(fd); + if (callback) callback([NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); + return; + } + + [self setDispatchSource:dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, protocol_.queue)]; + + dispatch_source_set_event_handler(dispatchObj_source_, ^{ + unsigned long nconns = dispatch_source_get_data(dispatchObj_source_); + while ([self acceptIncomingConnection:fd] && --nconns); + }); + + dispatch_source_set_cancel_handler(dispatchObj_source_, ^{ + // Captures *self*, effectively holding a reference to *self* until cancelled. + dispatchObj_source_ = nil; + close(fd); + if (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) { + [delegate_ ioFrameChannel:self didEndWithError:endError_]; + endError_ = nil; + } + }); + + dispatch_resume(dispatchObj_source_); + //NSLog(@"%@ opened on fd #%d", self, fd); + + connState_ = kConnStateListening; + if (callback) callback(nil); +} + + +- (BOOL)acceptIncomingConnection:(dispatch_fd_t)serverSocketFD { + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + dispatch_fd_t clientSocketFD = accept(serverSocketFD, (struct sockaddr*)&addr, &addrLen); + + if (clientSocketFD == -1) { + perror("accept()"); + return NO; + } + + // prevent SIGPIPE + int on = 1; + setsockopt(clientSocketFD, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + + if (fcntl(clientSocketFD, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl(.. O_NONBLOCK)"); + close(clientSocketFD); + return NO; + } + + if (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didAcceptConnection_fromAddress) { + PTChannel *peerChannel = [[PTChannel alloc] initWithProtocol:protocol_ delegate:delegate_]; + __block PTChannel *localChannelRef = self; + dispatch_io_t dispatchChannel = dispatch_io_create(DISPATCH_IO_STREAM, clientSocketFD, protocol_.queue, ^(int error) { + // Important note: This block captures *self*, thus a reference is held to + // *self* until the fd is truly closed. + localChannelRef = nil; + + close(clientSocketFD); + + if (peerChannel->delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_didEndWithError) { + NSError *err = error == 0 ? peerChannel->endError_ : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]; + [peerChannel->delegate_ ioFrameChannel:peerChannel didEndWithError:err]; + peerChannel->endError_ = nil; + } + }); + + [peerChannel setConnState:kConnStateConnected]; + [peerChannel setDispatchChannel:dispatchChannel]; + + assert(((struct sockaddr_storage*)&addr)->ss_len == addrLen); + PTAddress *address = [[PTAddress alloc] initWithSockaddr:(struct sockaddr_storage*)&addr]; + [delegate_ ioFrameChannel:self didAcceptConnection:peerChannel fromAddress:address]; + + NSError *err = nil; + if (![peerChannel startReadingFromConnectedChannel:dispatchChannel error:&err]) { + NSLog(@"startReadingFromConnectedChannel failed in accept: %@", err); + } + } else { + close(clientSocketFD); + } + return YES; +} + + +#pragma mark - Closing the channel + + +- (void)close { + if ((connState_ == kConnStateConnecting || connState_ == kConnStateConnected) && dispatchObj_channel_) { + dispatch_io_close(dispatchObj_channel_, DISPATCH_IO_STOP); + [self setDispatchChannel:NULL]; + } else if (connState_ == kConnStateListening && dispatchObj_source_) { + dispatch_source_cancel(dispatchObj_source_); + } +} + + +- (void)cancel { + if ((connState_ == kConnStateConnecting || connState_ == kConnStateConnected) && dispatchObj_channel_) { + dispatch_io_close(dispatchObj_channel_, 0); + [self setDispatchChannel:NULL]; + } else if (connState_ == kConnStateListening && dispatchObj_source_) { + dispatch_source_cancel(dispatchObj_source_); + } +} + + +#pragma mark - Reading + + +- (BOOL)startReadingFromConnectedChannel:(dispatch_io_t)channel error:(__autoreleasing NSError**)error { + if (connState_ != kConnStateNone && connState_ != kConnStateConnecting && connState_ != kConnStateConnected) { + if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil]; + return NO; + } + + if (dispatchObj_channel_ != channel) { + [self close]; + [self setDispatchChannel:channel]; + } + + connState_ = kConnStateConnected; + + // helper + BOOL(^handleError)(NSError*,BOOL) = ^BOOL(NSError *error, BOOL isEOS) { + if (error) { + //NSLog(@"Error while communicating: %@", error); + endError_ = error; + [self close]; + return YES; + } else if (isEOS) { + [self cancel]; + return YES; + } + return NO; + }; + + [protocol_ readFramesOverChannel:channel onFrame:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize, dispatch_block_t resumeReadingFrames) { + if (handleError(error, type == PTFrameTypeEndOfStream)) { + return; + } + + BOOL accepted = (channel == dispatchObj_channel_); + if (accepted && (delegateFlags_ & kDelegateFlagImplements_ioFrameChannel_shouldAcceptFrameOfType_tag_payloadSize)) { + accepted = [delegate_ ioFrameChannel:self shouldAcceptFrameOfType:type tag:tag payloadSize:payloadSize]; + } + + if (payloadSize == 0) { + if (accepted && delegate_) { + [delegate_ ioFrameChannel:self didReceiveFrameOfType:type tag:tag payload:nil]; + } else { + // simply ignore the frame + } + resumeReadingFrames(); + } else { + // has payload + if (!accepted) { + // Read and discard payload, ignoring frame + [protocol_ readAndDiscardDataOfSize:payloadSize overChannel:channel callback:^(NSError *error, BOOL endOfStream) { + if (!handleError(error, endOfStream)) { + resumeReadingFrames(); + } + }]; + } else { + [protocol_ readPayloadOfSize:payloadSize overChannel:channel callback:^(NSError *error, dispatch_data_t contiguousData, const uint8_t *buffer, size_t bufferSize) { + if (handleError(error, bufferSize == 0)) { + return; + } + + if (delegate_) { + PTData *payload = [[PTData alloc] initWithMappedDispatchData:contiguousData data:(void*)buffer length:bufferSize]; + [delegate_ ioFrameChannel:self didReceiveFrameOfType:type tag:tag payload:payload]; + } + + resumeReadingFrames(); + }]; + } + } + }]; + + return YES; +} + + +#pragma mark - Sending + +- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload callback:(void(^)(NSError *error))callback { + if (connState_ == kConnStateConnecting || connState_ == kConnStateConnected) { + [protocol_ sendFrameOfType:frameType tag:tag withPayload:payload overChannel:dispatchObj_channel_ callback:callback]; + } else if (callback) { + callback([NSError errorWithDomain:NSPOSIXErrorDomain code:EPERM userInfo:nil]); + } +} + +#pragma mark - NSObject + +- (NSString*)description { + id userInfo = objc_getAssociatedObject(self, (void*)&kUserInfoKey); + return [NSString stringWithFormat:@"<PTChannel: %p (%@)%s%@>", self, ( connState_ == kConnStateConnecting ? @"connecting" + : connState_ == kConnStateConnected ? @"connected" + : connState_ == kConnStateListening ? @"listening" + : @"closed"), + userInfo ? " " : "", userInfo ? userInfo : @""]; +} + + +@end + + +#pragma mark - +@implementation PTAddress + +- (id)initWithSockaddr:(const struct sockaddr_storage*)addr { + if (!(self = [super init])) return nil; + assert(addr); + memcpy((void*)&sockaddr_, (const void*)addr, addr->ss_len); + return self; +} + + +- (NSString*)name { + if (sockaddr_.ss_len) { + const void *sin_addr = NULL; + size_t bufsize = 0; + if (sockaddr_.ss_family == AF_INET6) { + bufsize = INET6_ADDRSTRLEN; + sin_addr = (const void *)&((const struct sockaddr_in6*)&sockaddr_)->sin6_addr; + } else { + bufsize = INET_ADDRSTRLEN; + sin_addr = (const void *)&((const struct sockaddr_in*)&sockaddr_)->sin_addr; + } + char *buf = CFAllocatorAllocate(kCFAllocatorDefault, bufsize+1, 0); + if (inet_ntop(sockaddr_.ss_family, sin_addr, buf, (unsigned int)bufsize-1) == NULL) { + CFAllocatorDeallocate(kCFAllocatorDefault, buf); + return nil; + } + return [[NSString alloc] initWithBytesNoCopy:(void*)buf length:strlen(buf) encoding:NSUTF8StringEncoding freeWhenDone:YES]; + } else { + return nil; + } +} + + +- (NSInteger)port { + if (sockaddr_.ss_len) { + return ntohs(PT_SOCKADDR_ACCESS(&sockaddr_, sin_port, sin6_port)); + } else { + return 0; + } +} + + +- (NSString*)description { + if (sockaddr_.ss_len) { + return [NSString stringWithFormat:@"%@:%u", self.name, (unsigned)self.port]; + } else { + return @"(?)"; + } +} + +@end + + +#pragma mark - +@implementation PTData + +@synthesize dispatchData = dispatchData_; +@synthesize data = data_; +@synthesize length = length_; + +- (id)initWithMappedDispatchData:(dispatch_data_t)mappedContiguousData data:(void*)data length:(size_t)length { + if (!(self = [super init])) return nil; + dispatchData_ = mappedContiguousData; +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchData_) dispatch_retain(dispatchData_); +#endif + data_ = data; + length_ = length; + return self; +} + +- (void)dealloc { +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchData_) dispatch_release(dispatchData_); +#endif + data_ = NULL; + length_ = 0; +} + +#pragma mark - NSObject + +- (NSString*)description { + return [NSString stringWithFormat:@"<PTData: %p (%zu bytes)>", self, length_]; +} + +@end + diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTPrivate.h b/ios/Pods/Flipper-PeerTalk/peertalk/PTPrivate.h new file mode 100644 index 000000000..7878ef69c --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTPrivate.h @@ -0,0 +1,14 @@ +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && (!defined(__IPHONE_6_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0)) || \ + (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (!defined(__MAC_10_8) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8)) +#define PT_DISPATCH_RETAIN_RELEASE 1 +#else +#define PT_DISPATCH_RETAIN_RELEASE 0 +#endif + +#if PT_DISPATCH_RETAIN_RELEASE +#define PT_PRECISE_LIFETIME +#define PT_PRECISE_LIFETIME_UNUSED +#else +#define PT_PRECISE_LIFETIME __attribute__((objc_precise_lifetime)) +#define PT_PRECISE_LIFETIME_UNUSED __attribute__((objc_precise_lifetime, unused)) +#endif diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.h b/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.h new file mode 100644 index 000000000..726904670 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.h @@ -0,0 +1,116 @@ +// +// A universal frame-based communication protocol which can be used to exchange +// arbitrary structured data. +// +// In short: +// +// - Each transmission is comprised by one fixed-size frame. +// - Each frame contains a protocol version number. +// - Each frame contains an application frame type. +// - Each frame can contain an identifying tag. +// - Each frame can have application-specific data of up to UINT32_MAX size. +// - Transactions style messaging can be modeled on top using frame tags. +// - Lightweight API on top of libdispatch (aka GCD) -- close to the metal. +// +#include <dispatch/dispatch.h> +#import <Foundation/Foundation.h> + +// Special frame tag that signifies "no tag". Your implementation should never +// create a reply for a frame with this tag. +static const uint32_t PTFrameNoTag = 0; + +// Special frame type that signifies that the stream has ended. +static const uint32_t PTFrameTypeEndOfStream = 0; + +// NSError domain +FOUNDATION_EXPORT NSString * const PTProtocolErrorDomain; + + +@interface PTProtocol : NSObject + +// Queue on which to run data processing blocks. +@property dispatch_queue_t queue; + +// Get the shared protocol object for *queue* ++ (PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue; + +// Initialize a new protocol object to use a specific queue. +- (id)initWithDispatchQueue:(dispatch_queue_t)queue; + +// Initialize a new protocol object to use the current calling queue. +- (id)init; + +#pragma mark Sending frames + +// Generate a new tag that is unique within this protocol object. +- (uint32_t)newTag; + +// Send a frame over *channel* with an optional payload and optional callback. +// If *callback* is not NULL, the block is invoked when either an error occured +// or when the frame (and payload, if any) has been completely sent. +- (void)sendFrameOfType:(uint32_t)frameType + tag:(uint32_t)tag + withPayload:(dispatch_data_t)payload + overChannel:(dispatch_io_t)channel + callback:(void(^)(NSError *error))callback; + +#pragma mark Receiving frames + +// Read frames over *channel* as they arrive. +// The onFrame handler is responsible for reading (or discarding) any payload +// and call *resumeReadingFrames* afterwards to resume reading frames. +// To stop reading frames, simply do not invoke *resumeReadingFrames*. +// When the stream ends, a frame of type PTFrameTypeEndOfStream is received. +- (void)readFramesOverChannel:(dispatch_io_t)channel + onFrame:(void(^)(NSError *error, + uint32_t type, + uint32_t tag, + uint32_t payloadSize, + dispatch_block_t resumeReadingFrames))onFrame; + +// Read a single frame over *channel*. A frame of type PTFrameTypeEndOfStream +// denotes the stream has ended. +- (void)readFrameOverChannel:(dispatch_io_t)channel + callback:(void(^)(NSError *error, + uint32_t frameType, + uint32_t frameTag, + uint32_t payloadSize))callback; + +#pragma mark Receiving frame payloads + +// Read a complete payload. It's the callers responsibility to make sure +// payloadSize is not too large since memory will be automatically allocated +// where only payloadSize is the limit. +// The returned dispatch_data_t object owns *buffer* and thus you need to call +// dispatch_retain on *contiguousData* if you plan to keep *buffer* around after +// returning from the callback. +- (void)readPayloadOfSize:(size_t)payloadSize + overChannel:(dispatch_io_t)channel + callback:(void(^)(NSError *error, + dispatch_data_t contiguousData, + const uint8_t *buffer, + size_t bufferSize))callback; + +// Discard data of *size* waiting on *channel*. *callback* can be NULL. +- (void)readAndDiscardDataOfSize:(size_t)size + overChannel:(dispatch_io_t)channel + callback:(void(^)(NSError *error, BOOL endOfStream))callback; + +@end + +@interface NSData (PTProtocol) +// Creates a new dispatch_data_t object which references the receiver and uses +// the receivers bytes as its backing data. The returned dispatch_data_t object +// holds a reference to the recevier. It's the callers responsibility to call +// dispatch_release on the returned object when done. +- (dispatch_data_t)createReferencingDispatchData; ++ (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data; +@end + +@interface NSDictionary (PTProtocol) +// See description of -[NSData(PTProtocol) createReferencingDispatchData] +- (dispatch_data_t)createReferencingDispatchData; + +// Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure. ++ (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data; +@end diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.m b/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.m new file mode 100644 index 000000000..78893755e --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTProtocol.m @@ -0,0 +1,424 @@ +#import "PTProtocol.h" +#import "PTPrivate.h" +#import <objc/runtime.h> + +static const uint32_t PTProtocolVersion1 = 1; + +NSString * const PTProtocolErrorDomain = @"PTProtocolError"; + +// This is what we send as the header for each frame. +typedef struct _PTFrame { + // The version of the frame and protocol. + uint32_t version; + + // Type of frame + uint32_t type; + + // Unless zero, a tag is retained in frames that are responses to previous + // frames. Applications can use this to build transactions or request-response + // logic. + uint32_t tag; + + // If payloadSize is larger than zero, *payloadSize* number of bytes are + // following, constituting application-specific data. + uint32_t payloadSize; + +} PTFrame; + + +@interface PTProtocol () { + uint32_t nextFrameTag_; + @public + dispatch_queue_t queue_; +} +- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload; +@end + + +static void _release_queue_local_protocol(void *objcobj) { + if (objcobj) { + PTProtocol *protocol = (__bridge_transfer id)objcobj; + protocol->queue_ = NULL; + } +} + + +@interface RQueueLocalIOFrameProtocol : PTProtocol +@end +@implementation RQueueLocalIOFrameProtocol +- (void)setQueue:(dispatch_queue_t)queue { +} +@end + + +@implementation PTProtocol + + ++ (PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue { + static const char currentQueueFrameProtocolKey; + //dispatch_queue_t queue = dispatch_get_current_queue(); + PTProtocol *currentQueueFrameProtocol = (__bridge PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey); + if (!currentQueueFrameProtocol) { + currentQueueFrameProtocol = [[RQueueLocalIOFrameProtocol alloc] initWithDispatchQueue:NULL]; + currentQueueFrameProtocol->queue_ = queue; // reference, no retain, since we would create cyclic references + dispatch_queue_set_specific(queue, ¤tQueueFrameProtocolKey, (__bridge_retained void*)currentQueueFrameProtocol, &_release_queue_local_protocol); + return (__bridge PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey); // to avoid race conds + } else { + return currentQueueFrameProtocol; + } +} + + +- (id)initWithDispatchQueue:(dispatch_queue_t)queue { + if (!(self = [super init])) return nil; + queue_ = queue; +#if PT_DISPATCH_RETAIN_RELEASE + if (queue_) dispatch_retain(queue_); +#endif + return self; +} + +- (id)init { + return [self initWithDispatchQueue:dispatch_get_main_queue()]; +} + +- (void)dealloc { + if (queue_) { +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(queue_); +#endif + } +} + +- (dispatch_queue_t)queue { + return queue_; +} + +- (void)setQueue:(dispatch_queue_t)queue { +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_queue_t prev_queue = queue_; + queue_ = queue; + if (queue_) dispatch_retain(queue_); + if (prev_queue) dispatch_release(prev_queue); +#else + queue_ = queue; +#endif +} + + +- (uint32_t)newTag { + return ++nextFrameTag_; +} + + +#pragma mark - +#pragma mark Creating frames + + +- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload { + PTFrame *frame = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(PTFrame), 0); + frame->version = htonl(PTProtocolVersion1); + frame->type = htonl(type); + frame->tag = htonl(frameTag); + + if (payload) { + size_t payloadSize = dispatch_data_get_size(payload); + assert(payloadSize <= UINT32_MAX); + frame->payloadSize = htonl((uint32_t)payloadSize); + } else { + frame->payloadSize = 0; + } + + dispatch_data_t frameData = dispatch_data_create((const void*)frame, sizeof(PTFrame), queue_, ^{ + CFAllocatorDeallocate(kCFAllocatorDefault, (void*)frame); + }); + + if (payload && frame->payloadSize != 0) { + // chain frame + payload + dispatch_data_t data = dispatch_data_create_concat(frameData, payload); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(frameData); +#endif + frameData = data; + } + + return frameData; +} + + +#pragma mark - +#pragma mark Sending frames + + +- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*))callback { + dispatch_data_t frame = [self createDispatchDataWithFrameOfType:frameType frameTag:tag payload:payload]; + dispatch_io_write(channel, 0, frame, queue_, ^(bool done, dispatch_data_t data, int _errno) { + if (done && callback) { + callback(_errno == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]); + } + }); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(frame); +#endif +} + + +#pragma mark - +#pragma mark Receiving frames + + +- (void)readFrameOverChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, uint32_t frameType, uint32_t frameTag, uint32_t payloadSize))callback { + __block dispatch_data_t allData = NULL; + + dispatch_io_read(channel, 0, sizeof(PTFrame), queue_, ^(bool done, dispatch_data_t data, int error) { + //NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error); + size_t dataSize = data ? dispatch_data_get_size(data) : 0; + + if (dataSize) { + if (!allData) { + allData = data; +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_retain(allData); +#endif + } else { +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_data_t allDataPrev = allData; + allData = dispatch_data_create_concat(allData, data); + dispatch_release(allDataPrev); +#else + allData = dispatch_data_create_concat(allData, data); +#endif + } + } + + if (done) { + if (error != 0) { + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], 0, 0, 0); + return; + } + + if (dataSize == 0) { + callback(nil, PTFrameTypeEndOfStream, 0, 0); + return; + } + + if (!allData || dispatch_data_get_size(allData) < sizeof(PTFrame)) { +#if PT_DISPATCH_RETAIN_RELEASE + if (allData) dispatch_release(allData); +#endif + callback([[NSError alloc] initWithDomain:PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0); + return; + } + + PTFrame *frame = NULL; + size_t size = 0; + + PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(allData, (const void **)&frame, &size); // precise lifetime guarantees bytes in frame will stay valid till the end of scope +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(allData); +#endif + if (!contiguousData) { + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], 0, 0, 0); + return; + } + + frame->version = ntohl(frame->version); + if (frame->version != PTProtocolVersion1) { + callback([[NSError alloc] initWithDomain:PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0); + } else { + frame->type = ntohl(frame->type); + frame->tag = ntohl(frame->tag); + frame->payloadSize = ntohl(frame->payloadSize); + callback(nil, frame->type, frame->tag, frame->payloadSize); + } + +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(contiguousData); +#endif + } + }); +} + + +- (void)readPayloadOfSize:(size_t)payloadSize overChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, dispatch_data_t contiguousData, const uint8_t *buffer, size_t bufferSize))callback { + __block dispatch_data_t allData = NULL; + dispatch_io_read(channel, 0, payloadSize, queue_, ^(bool done, dispatch_data_t data, int error) { + //NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error); + size_t dataSize = dispatch_data_get_size(data); + + if (dataSize) { + if (!allData) { + allData = data; +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_retain(allData); +#endif + } else { +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_data_t allDataPrev = allData; + allData = dispatch_data_create_concat(allData, data); + dispatch_release(allDataPrev); +#else + allData = dispatch_data_create_concat(allData, data); +#endif + } + } + + if (done) { + if (error != 0) { +#if PT_DISPATCH_RETAIN_RELEASE + if (allData) dispatch_release(allData); +#endif + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], NULL, NULL, 0); + return; + } + + if (dataSize == 0) { +#if PT_DISPATCH_RETAIN_RELEASE + if (allData) dispatch_release(allData); +#endif + callback(nil, NULL, NULL, 0); + return; + } + + uint8_t *buffer = NULL; + size_t bufferSize = 0; + PT_PRECISE_LIFETIME dispatch_data_t contiguousData = NULL; + + if (allData) { + contiguousData = dispatch_data_create_map(allData, (const void **)&buffer, &bufferSize); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(allData); allData = NULL; +#endif + if (!contiguousData) { + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], NULL, NULL, 0); + return; + } + } + + callback(nil, contiguousData, buffer, bufferSize); +#if PT_DISPATCH_RETAIN_RELEASE + if (contiguousData) dispatch_release(contiguousData); +#endif + } + }); +} + + +- (void)readAndDiscardDataOfSize:(size_t)size overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*, BOOL))callback { + dispatch_io_read(channel, 0, size, queue_, ^(bool done, dispatch_data_t data, int error) { + if (done && callback) { + size_t dataSize = data ? dispatch_data_get_size(data) : 0; + callback(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], dataSize == 0); + } + }); +} + + +- (void)readFramesOverChannel:(dispatch_io_t)channel onFrame:(void(^)(NSError*, uint32_t, uint32_t, uint32_t, dispatch_block_t))onFrame { + [self readFrameOverChannel:channel callback:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize) { + onFrame(error, type, tag, payloadSize, ^{ + if (type != PTFrameTypeEndOfStream) { + [self readFramesOverChannel:channel onFrame:onFrame]; + } + }); + }]; +} + + +@end + + +@interface _PTDispatchData : NSObject { + dispatch_data_t dispatchData_; +} +@end +@implementation _PTDispatchData +- (id)initWithDispatchData:(dispatch_data_t)dispatchData { + if (!(self = [super init])) return nil; + dispatchData_ = dispatchData; +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_retain(dispatchData_); +#endif + return self; +} +- (void)dealloc { +#if PT_DISPATCH_RETAIN_RELEASE + if (dispatchData_) dispatch_release(dispatchData_); +#endif +} +@end + +@implementation NSData (PTProtocol) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-getter-return-value" + +- (dispatch_data_t)createReferencingDispatchData { + // Note: The queue is used to submit the destructor. Since we only perform an + // atomic release of self, it doesn't really matter which queue is used, thus + // we use the current calling queue. + return dispatch_data_create((const void*)self.bytes, self.length, dispatch_get_main_queue(), ^{ + // trick to have the block capture the data, thus retain/releasing + [self length]; + }); +} + +#pragma clang diagnostic pop + ++ (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data { + if (!data) { + return nil; + } + uint8_t *buffer = NULL; + size_t bufferSize = 0; + PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize); + if (!contiguousData) { + return nil; + } + + _PTDispatchData *dispatchDataRef = [[_PTDispatchData alloc] initWithDispatchData:contiguousData]; + NSData *newData = [NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO]; +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(contiguousData); +#endif + static const bool kDispatchDataRefKey; + objc_setAssociatedObject(newData, (const void*)kDispatchDataRefKey, dispatchDataRef, OBJC_ASSOCIATION_RETAIN); + + return newData; +} + +@end + + +@implementation NSDictionary (PTProtocol) + +- (dispatch_data_t)createReferencingDispatchData { + NSError *error = nil; + NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error]; + if (!plistData) { + NSLog(@"Failed to serialize property list: %@", error); + return nil; + } else { + return [plistData createReferencingDispatchData]; + } +} + +// Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure. ++ (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data { + if (!data) { + return nil; + } + uint8_t *buffer = NULL; + size_t bufferSize = 0; + PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize); + if (!contiguousData) { + return nil; + } + NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:nil]; +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(contiguousData); +#endif + return dict; +} + +@end diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.h b/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.h new file mode 100644 index 000000000..89aaf6469 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.h @@ -0,0 +1,82 @@ +#include <dispatch/dispatch.h> +#import <Foundation/Foundation.h> + +// PTUSBDeviceDidAttachNotification +// Posted when a device has been attached. Also posted for each device that is +// already attached when the PTUSBHub starts listening. +// +// .userInfo = { +// DeviceID = 3; +// MessageType = Attached; +// Properties = { +// ConnectionSpeed = 480000000; +// ConnectionType = USB; +// DeviceID = 3; +// LocationID = 1234567890; +// ProductID = 1234; +// SerialNumber = 0123456789abcdef0123456789abcdef01234567; +// }; +// } +// +FOUNDATION_EXPORT NSString * const PTUSBDeviceDidAttachNotification; + +// PTUSBDeviceDidDetachNotification +// Posted when a device has been detached. +// +// .userInfo = { +// DeviceID = 3; +// MessageType = Detached; +// } +// +FOUNDATION_EXPORT NSString * const PTUSBDeviceDidDetachNotification; + +// NSError domain +FOUNDATION_EXPORT NSString * const PTUSBHubErrorDomain; + +// Error codes returned with NSError.code for NSError domain PTUSBHubErrorDomain +typedef enum { + PTUSBHubErrorBadDevice = 2, + PTUSBHubErrorConnectionRefused = 3, +} PTUSBHubError; + +@interface PTUSBHub : NSObject + +// Shared, implicitly opened hub. ++ (PTUSBHub*)sharedHub; + +// Connect to a TCP *port* on a device, while the actual transport is over USB. +// Upon success, *error* is nil and *channel* is a duplex I/O channel. +// You can retrieve the underlying file descriptor using +// dispatch_io_get_descriptor(channel). The dispatch_io_t channel behaves just +// like any stream type dispatch_io_t, making it possible to use the same logic +// for both USB bridged connections and e.g. ethernet-based connections. +// +// *onStart* is called either when a connection failed, in which case the error +// argument is non-nil, or when the connection was successfully established (the +// error argument is nil). Must not be NULL. +// +// *onEnd* is called when a connection was open and just did close. If the error +// argument is non-nil, the channel closed because of an error. Pass NULL for no +// callback. +// +- (void)connectToDevice:(NSNumber*)deviceID + port:(int)port + onStart:(void(^)(NSError *error, dispatch_io_t channel))onStart + onEnd:(void(^)(NSError *error))onEnd; + +// Start listening for devices. You only need to invoke this method on custom +// instances to start receiving notifications. The shared instance returned from +// +sharedHub is always in listening mode. +// +// *onStart* is called either when the system failed to start listening, in +// which case the error argument is non-nil, or when the receiver is listening. +// Pass NULL for no callback. +// +// *onEnd* is called when listening stopped. If the error argument is non-nil, +// listening stopped because of an error. Pass NULL for no callback. +// +- (void)listenOnQueue:(dispatch_queue_t)queue + onStart:(void(^)(NSError*))onStart + onEnd:(void(^)(NSError*))onEnd; + +@end diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.m b/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.m new file mode 100644 index 000000000..646899583 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/PTUSBHub.m @@ -0,0 +1,667 @@ +#import "PTUSBHub.h" +#import "PTPrivate.h" + +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/un.h> +#include <err.h> + +NSString * const PTUSBHubErrorDomain = @"PTUSBHubError"; + +typedef uint32_t USBMuxPacketType; +enum { + USBMuxPacketTypeResult = 1, + USBMuxPacketTypeConnect = 2, + USBMuxPacketTypeListen = 3, + USBMuxPacketTypeDeviceAdd = 4, + USBMuxPacketTypeDeviceRemove = 5, + // ? = 6, + // ? = 7, + USBMuxPacketTypePlistPayload = 8, +}; + +typedef uint32_t USBMuxPacketProtocol; +enum { + USBMuxPacketProtocolBinary = 0, + USBMuxPacketProtocolPlist = 1, +}; + +typedef uint32_t USBMuxReplyCode; +enum { + USBMuxReplyCodeOK = 0, + USBMuxReplyCodeBadCommand = 1, + USBMuxReplyCodeBadDevice = 2, + USBMuxReplyCodeConnectionRefused = 3, + // ? = 4, + // ? = 5, + USBMuxReplyCodeBadVersion = 6, +}; + + +typedef struct usbmux_packet { + uint32_t size; + USBMuxPacketProtocol protocol; + USBMuxPacketType type; + uint32_t tag; + char data[0]; +} __attribute__((__packed__)) usbmux_packet_t; + +static const uint32_t kUsbmuxPacketMaxPayloadSize = UINT32_MAX - (uint32_t)sizeof(usbmux_packet_t); + + +static uint32_t usbmux_packet_payload_size(usbmux_packet_t *upacket) { + return upacket->size - sizeof(usbmux_packet_t); +} + + +static void *usbmux_packet_payload(usbmux_packet_t *upacket) { + return (void*)upacket->data; +} + + +static void usbmux_packet_set_payload(usbmux_packet_t *upacket, + const void *payload, + uint32_t payloadLength) +{ + memcpy(usbmux_packet_payload(upacket), payload, payloadLength); +} + + +static usbmux_packet_t *usbmux_packet_alloc(uint32_t payloadSize) { + assert(payloadSize <= kUsbmuxPacketMaxPayloadSize); + uint32_t upacketSize = sizeof(usbmux_packet_t) + payloadSize; + usbmux_packet_t *upacket = CFAllocatorAllocate(kCFAllocatorDefault, upacketSize, 0); + memset(upacket, 0, sizeof(usbmux_packet_t)); + upacket->size = upacketSize; + return upacket; +} + + +static usbmux_packet_t *usbmux_packet_create(USBMuxPacketProtocol protocol, + USBMuxPacketType type, + uint32_t tag, + const void *payload, + uint32_t payloadSize) +{ + usbmux_packet_t *upacket = usbmux_packet_alloc(payloadSize); + if (!upacket) { + return NULL; + } + + upacket->protocol = protocol; + upacket->type = type; + upacket->tag = tag; + + if (payload && payloadSize) { + usbmux_packet_set_payload(upacket, payload, (uint32_t)payloadSize); + } + + return upacket; +} + + +static void usbmux_packet_free(usbmux_packet_t *upacket) { + CFAllocatorDeallocate(kCFAllocatorDefault, upacket); +} + + +NSString * const PTUSBDeviceDidAttachNotification = @"PTUSBDeviceDidAttachNotification"; +NSString * const PTUSBDeviceDidDetachNotification = @"PTUSBDeviceDidDetachNotification"; + +static NSString *kPlistPacketTypeListen = @"Listen"; +static NSString *kPlistPacketTypeConnect = @"Connect"; + + +// Represents a channel of communication between the host process and a remote +// (device) system. In practice, a PTUSBChannel is connected to a usbmuxd +// endpoint which is configured to either listen for device changes (the +// PTUSBHub's channel is usually configured as a device notification listener) or +// configured as a TCP bridge (e.g. channels returned from PTUSBHub's +// connectToDevice:port:callback:). You should not create channels yourself, but +// let PTUSBHub provide you with already configured channels. +@interface PTUSBChannel : NSObject { + dispatch_io_t channel_; + dispatch_queue_t queue_; + uint32_t nextPacketTag_; + NSMutableDictionary *responseQueue_; + BOOL autoReadPackets_; + BOOL isReadingPackets_; +} + +// The underlying dispatch I/O channel. This is handy if you want to handle your +// own I/O logic without PTUSBChannel. Remember to dispatch_retain() the channel +// if you plan on using it as it might be released from the PTUSBChannel at any +// point in time. +@property (readonly) dispatch_io_t dispatchChannel; + +// The underlying file descriptor. +@property (readonly) dispatch_fd_t fileDescriptor; + +// Send data +- (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback; +- (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback; + +// Read data +- (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback; + +// Close the channel, preventing further reads and writes, but letting currently +// queued reads and writes finish. +- (void)cancel; + +// Close the channel, preventing further reads and writes, immediately +// terminating any ongoing reads and writes. +- (void)stop; + +@end + + +@interface PTUSBChannel (Private) + ++ (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload; +- (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError *error))onEnd; +- (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback; +- (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error; +- (uint32_t)nextPacketTag; +- (void)sendPacketOfType:(USBMuxPacketType)type overProtocol:(USBMuxPacketProtocol)protocol tag:(uint32_t)tag payload:(NSData*)payload callback:(void(^)(NSError*))callback; +- (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError *error))callback; +- (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError *error, NSDictionary *responsePacket))callback; +- (void)scheduleReadPacketWithCallback:(void(^)(NSError *error, NSDictionary *packet, uint32_t packetTag))callback; +- (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler; +- (void)setNeedsReadingPacket; +@end + + +@interface PTUSBHub () { + PTUSBChannel *channel_; +} +- (void)handleBroadcastPacket:(NSDictionary*)packet; +@end + + +@implementation PTUSBHub + + ++ (PTUSBHub*)sharedHub { + static PTUSBHub *gSharedHub; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSharedHub = [PTUSBHub new]; + [gSharedHub listenOnQueue:dispatch_get_main_queue() onStart:^(NSError *error) { + if (error) { + NSLog(@"PTUSBHub failed to initialize: %@", error); + } + } onEnd:nil]; + }); + return gSharedHub; +} + + +- (id)init { + if (!(self = [super init])) return nil; + + return self; +} + + +- (void)listenOnQueue:(dispatch_queue_t)queue onStart:(void(^)(NSError*))onStart onEnd:(void(^)(NSError*))onEnd { + if (channel_) { + if (onStart) onStart(nil); + return; + } + channel_ = [PTUSBChannel new]; + NSError *error = nil; + if ([channel_ openOnQueue:queue error:&error onEnd:onEnd]) { + [channel_ listenWithBroadcastHandler:^(NSDictionary *packet) { [self handleBroadcastPacket:packet]; } callback:onStart]; + } else if (onStart) { + onStart(error); + } +} + + +- (void)connectToDevice:(NSNumber*)deviceID port:(int)port onStart:(void(^)(NSError*, dispatch_io_t))onStart onEnd:(void(^)(NSError*))onEnd { + PTUSBChannel *channel = [PTUSBChannel new]; + NSError *error = nil; + + if (![channel openOnQueue:dispatch_get_main_queue() error:&error onEnd:onEnd]) { + onStart(error, nil); + return; + } + + port = ((port<<8) & 0xFF00) | (port>>8); // limit + + NSDictionary *packet = [PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeConnect + payload:[NSDictionary dictionaryWithObjectsAndKeys: + deviceID, @"DeviceID", + [NSNumber numberWithInt:port], @"PortNumber", + nil]]; + + [channel sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) { + NSError *error = error_; + [channel errorFromPlistResponse:responsePacket error:&error]; + onStart(error, (error ? nil : channel.dispatchChannel) ); + }]; +} + + +- (void)handleBroadcastPacket:(NSDictionary*)packet { + NSString *messageType = [packet objectForKey:@"MessageType"]; + + if ([@"Attached" isEqualToString:messageType]) { + [[NSNotificationCenter defaultCenter] postNotificationName:PTUSBDeviceDidAttachNotification object:self userInfo:packet]; + } else if ([@"Detached" isEqualToString:messageType]) { + [[NSNotificationCenter defaultCenter] postNotificationName:PTUSBDeviceDidDetachNotification object:self userInfo:packet]; + } else { + NSLog(@"Warning: Unhandled broadcast message: %@", packet); + } +} + + +@end + +#pragma mark - + +@implementation PTUSBChannel + ++ (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload { + NSDictionary *packet = nil; + + static NSString *bundleName = nil; + static NSString *bundleVersion = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary; + if (infoDict) { + bundleName = [infoDict objectForKey:@"CFBundleName"]; + bundleVersion = [[infoDict objectForKey:@"CFBundleVersion"] description]; + } + }); + + if (bundleName) { + packet = [NSDictionary dictionaryWithObjectsAndKeys: + messageType, @"MessageType", + bundleName, @"ProgName", + bundleVersion, @"ClientVersionString", + nil]; + } else { + packet = [NSDictionary dictionaryWithObjectsAndKeys:messageType, @"MessageType", nil]; + } + + if (payload) { + NSMutableDictionary *mpacket = [NSMutableDictionary dictionaryWithDictionary:payload]; + [mpacket addEntriesFromDictionary:packet]; + packet = mpacket; + } + + return packet; +} + + +- (id)init { + if (!(self = [super init])) return nil; + + return self; +} + + +- (void)dealloc { + //NSLog(@"dealloc %@", self); + if (channel_) { +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(channel_); +#endif + channel_ = nil; + } +} + + +- (BOOL)valid { + return !!channel_; +} + + +- (dispatch_io_t)dispatchChannel { + return channel_; +} + + +- (dispatch_fd_t)fileDescriptor { + return dispatch_io_get_descriptor(channel_); +} + + +- (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError*))onEnd { + assert(queue != nil); + assert(channel_ == nil); + queue_ = queue; + + // Create socket + dispatch_fd_t fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + return NO; + } + + // prevent SIGPIPE + int on = 1; + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + + // Connect socket + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/var/run/usbmuxd"); + socklen_t socklen = sizeof(addr); + if (connect(fd, (struct sockaddr*)&addr, socklen) == -1) { + if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + return NO; + } + + channel_ = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue_, ^(int error) { + close(fd); + if (onEnd) { + onEnd(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]); + } + }); + + return YES; +} + + +- (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback { + autoReadPackets_ = YES; + [self scheduleReadPacketWithBroadcastHandler:broadcastHandler]; + + NSDictionary *packet = [PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeListen payload:nil]; + + [self sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) { + if (!callback) + return; + + NSError *error = error_; + [self errorFromPlistResponse:responsePacket error:&error]; + + callback(error); + }]; +} + + +- (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error { + if (!*error) { + NSNumber *n = [packet objectForKey:@"Number"]; + + if (!n) { + *error = [NSError errorWithDomain:PTUSBHubErrorDomain code:(n ? n.integerValue : 0) userInfo:nil]; + return NO; + } + + USBMuxReplyCode replyCode = (USBMuxReplyCode)n.integerValue; + if (replyCode != 0) { + NSString *errmessage = @"Unspecified error"; + switch (replyCode) { + case USBMuxReplyCodeBadCommand: errmessage = @"illegal command"; break; + case USBMuxReplyCodeBadDevice: errmessage = @"unknown device"; break; + case USBMuxReplyCodeConnectionRefused: errmessage = @"connection refused"; break; + case USBMuxReplyCodeBadVersion: errmessage = @"invalid version"; break; + default: break; + } + *error = [NSError errorWithDomain:PTUSBHubErrorDomain code:replyCode userInfo:[NSDictionary dictionaryWithObject:errmessage forKey:NSLocalizedDescriptionKey]]; + return NO; + } + } + return YES; +} + + +- (uint32_t)nextPacketTag { + return ++nextPacketTag_; +} + + +- (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError*, NSDictionary*))callback { + uint32_t tag = [self nextPacketTag]; + [self sendPacket:packet tag:tag callback:^(NSError *error) { + if (error) { + callback(error, nil); + return; + } + // TODO: timeout un-triggered callbacks in responseQueue_ + if (!responseQueue_) responseQueue_ = [NSMutableDictionary new]; + [responseQueue_ setObject:callback forKey:[NSNumber numberWithUnsignedInt:tag]]; + }]; + + // We are awaiting a response + [self setNeedsReadingPacket]; +} + + +- (void)setNeedsReadingPacket { + if (!isReadingPackets_) { + [self scheduleReadPacketWithBroadcastHandler:nil]; + } +} + + +- (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler { + assert(isReadingPackets_ == NO); + + [self scheduleReadPacketWithCallback:^(NSError *error, NSDictionary *packet, uint32_t packetTag) { + // Interpret the package we just received + if (packetTag == 0) { + // Broadcast message + //NSLog(@"Received broadcast: %@", packet); + if (broadcastHandler) broadcastHandler(packet); + } else if (responseQueue_) { + // Reply + NSNumber *key = [NSNumber numberWithUnsignedInt:packetTag]; + void(^requestCallback)(NSError*,NSDictionary*) = [responseQueue_ objectForKey:key]; + if (requestCallback) { + [responseQueue_ removeObjectForKey:key]; + requestCallback(error, packet); + } else { + NSLog(@"Warning: Ignoring reply packet for which there is no registered callback. Packet => %@", packet); + } + } + + // Schedule reading another incoming package + if (autoReadPackets_) { + [self scheduleReadPacketWithBroadcastHandler:broadcastHandler]; + } + }]; +} + + +- (void)scheduleReadPacketWithCallback:(void(^)(NSError*, NSDictionary*, uint32_t))callback { + static usbmux_packet_t ref_upacket; + isReadingPackets_ = YES; + + // Read the first `sizeof(ref_upacket.size)` bytes off the channel_ + dispatch_io_read(channel_, 0, sizeof(ref_upacket.size), queue_, ^(bool done, dispatch_data_t data, int error) { + //NSLog(@"dispatch_io_read 0,4: done=%d data=%p error=%d", done, data, error); + + if (!done) + return; + + if (error) { + isReadingPackets_ = NO; + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0); + return; + } + + // Read size of incoming usbmux_packet_t + uint32_t upacket_len = 0; + char *buffer = NULL; + size_t buffer_size = 0; + PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size); // objc_precise_lifetime guarantees 'map_data' isn't released before memcpy has a chance to do its thing + assert(buffer_size == sizeof(ref_upacket.size)); + assert(sizeof(upacket_len) == sizeof(ref_upacket.size)); + memcpy((void *)&(upacket_len), (const void *)buffer, buffer_size); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(map_data); +#endif + + // Allocate a new usbmux_packet_t for the expected size + uint32_t payloadLength = upacket_len - (uint32_t)sizeof(usbmux_packet_t); + usbmux_packet_t *upacket = usbmux_packet_alloc(payloadLength); + + // Read rest of the incoming usbmux_packet_t + off_t offset = sizeof(ref_upacket.size); + dispatch_io_read(channel_, offset, upacket->size - offset, queue_, ^(bool done, dispatch_data_t data, int error) { + //NSLog(@"dispatch_io_read X,Y: done=%d data=%p error=%d", done, data, error); + + if (!done) { + return; + } + + isReadingPackets_ = NO; + + if (error) { + callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0); + usbmux_packet_free(upacket); + return; + } + + if (upacket_len > kUsbmuxPacketMaxPayloadSize) { + callback( + [[NSError alloc] initWithDomain:PTUSBHubErrorDomain code:1 userInfo:@{ + NSLocalizedDescriptionKey:@"Received a packet that is too large"}], + nil, + 0 + ); + usbmux_packet_free(upacket); + return; + } + + // Copy read bytes onto our usbmux_packet_t + char *buffer = NULL; + size_t buffer_size = 0; + PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size); + assert(buffer_size == upacket->size - offset); + memcpy(((void *)(upacket))+offset, (const void *)buffer, buffer_size); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(map_data); +#endif + + // We only support plist protocol + if (upacket->protocol != USBMuxPacketProtocolPlist) { + callback([[NSError alloc] initWithDomain:PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package protocol" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag); + usbmux_packet_free(upacket); + return; + } + + // Only one type of packet in the plist protocol + if (upacket->type != USBMuxPacketTypePlistPayload) { + callback([[NSError alloc] initWithDomain:PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package type" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag); + usbmux_packet_free(upacket); + return; + } + + // Try to decode any payload as plist + NSError *err = nil; + NSDictionary *dict = nil; + if (usbmux_packet_payload_size(upacket)) { + dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:usbmux_packet_payload(upacket) length:usbmux_packet_payload_size(upacket) freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:&err]; + } + + // Invoke callback + callback(err, dict, upacket->tag); + usbmux_packet_free(upacket); + }); + }); +} + + +- (void)sendPacketOfType:(USBMuxPacketType)type + overProtocol:(USBMuxPacketProtocol)protocol + tag:(uint32_t)tag + payload:(NSData*)payload + callback:(void(^)(NSError*))callback +{ + assert(payload.length <= kUsbmuxPacketMaxPayloadSize); + usbmux_packet_t *upacket = usbmux_packet_create( + protocol, + type, + tag, + payload ? payload.bytes : nil, + (uint32_t)(payload ? payload.length : 0) + ); + dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, queue_, ^{ + // Free packet when data is freed + usbmux_packet_free(upacket); + }); + //NSData *data1 = [NSData dataWithBytesNoCopy:(void*)upacket length:upacket->size freeWhenDone:NO]; + //[data1 writeToFile:[NSString stringWithFormat:@"/Users/rsms/c-packet-%u.data", tag] atomically:NO]; + [self sendDispatchData:data callback:callback]; +} + + +- (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError*))callback { + NSError *error = nil; + // NSPropertyListBinaryFormat_v1_0 + NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packet format:NSPropertyListXMLFormat_v1_0 options:0 error:&error]; + if (!plistData) { + callback(error); + } else { + [self sendPacketOfType:USBMuxPacketTypePlistPayload overProtocol:USBMuxPacketProtocolPlist tag:tag payload:plistData callback:callback]; + } +} + + +- (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback { + off_t offset = 0; + dispatch_io_write(channel_, offset, data, queue_, ^(bool done, dispatch_data_t data, int _errno) { + //NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, error); + if (!done) + return; + if (callback) { + NSError *err = nil; + if (_errno) err = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]; + callback(err); + } + }); +#if PT_DISPATCH_RETAIN_RELEASE + dispatch_release(data); // Release our ref. A ref is still held by dispatch_io_write +#endif +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-getter-return-value" + +- (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback { + dispatch_data_t ddata = dispatch_data_create((const void*)data.bytes, data.length, queue_, ^{ + // trick to have the block capture and retain the data + [data length]; + }); + [self sendDispatchData:ddata callback:callback]; +} + +#pragma clang diagnostic pop + +- (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback { + dispatch_io_read(channel_, offset, length, queue_, ^(bool done, dispatch_data_t data, int _errno) { + if (!done) + return; + + NSError *error = nil; + if (_errno != 0) { + error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]; + } + + callback(error, data); + }); +} + + +- (void)cancel { + if (channel_) { + dispatch_io_close(channel_, 0); + } +} + + +- (void)stop { + if (channel_) { + dispatch_io_close(channel_, DISPATCH_IO_STOP); + } +} + +@end diff --git a/ios/Pods/Flipper-PeerTalk/peertalk/Peertalk.h b/ios/Pods/Flipper-PeerTalk/peertalk/Peertalk.h new file mode 100644 index 000000000..5541a7831 --- /dev/null +++ b/ios/Pods/Flipper-PeerTalk/peertalk/Peertalk.h @@ -0,0 +1,22 @@ +// +// Peertalk.h +// Peertalk +// +// Created by Marek Cirkos on 12/04/2016. +// +// + +#import <Foundation/Foundation.h> + +//! Project version number for Peertalk. +FOUNDATION_EXPORT double PeertalkVersionNumber; + +//! Project version string for Peertalk. +FOUNDATION_EXPORT const unsigned char PeertalkVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import <Peertalk/PublicHeader.h> + + +#import <Peertalk/PTChannel.h> +#import <Peertalk/PTProtocol.h> +#import <Peertalk/PTUSBHub.h> diff --git a/ios/Pods/FirebaseInstanceID/LICENSE b/ios/Pods/Flipper-RSocket/LICENSE similarity index 99% rename from ios/Pods/FirebaseInstanceID/LICENSE rename to ios/Pods/Flipper-RSocket/LICENSE index d64569567..989e2c59e 100644 --- a/ios/Pods/FirebaseInstanceID/LICENSE +++ b/ios/Pods/Flipper-RSocket/LICENSE @@ -1,5 +1,4 @@ - - Apache License +Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -199,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/ios/Pods/Flipper-RSocket/README.md b/ios/Pods/Flipper-RSocket/README.md new file mode 100644 index 000000000..1a5339e1c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/README.md @@ -0,0 +1,32 @@ +# rsocket-cpp + +C++ implementation of [RSocket](https://rsocket.io) + +<a href='https://travis-ci.org/rsocket/rsocket-cpp/builds'><img src='https://travis-ci.org/rsocket/rsocket-cpp.svg?branch=master'></a> +[![Coverage Status](https://coveralls.io/repos/github/rsocket/rsocket-cpp/badge.svg?branch=master)](https://coveralls.io/github/rsocket/rsocket-cpp?branch=master) + +# Dependencies + +Install `folly`: + +``` +brew install --HEAD folly +``` + +# Building and running tests + +After installing dependencies as above, you can build and run tests with: + +``` +# inside root ./rsocket-cpp +mkdir -p build +cd build +cmake -DCMAKE_BUILD_TYPE=DEBUG ../ +make -j +./tests +``` + +# License + +By contributing to rsocket-cpp, you agree that your contributions will be licensed +under the LICENSE file in the root directory of this source tree. diff --git a/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.cpp b/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.cpp new file mode 100644 index 000000000..870faef48 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.cpp @@ -0,0 +1,46 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/ColdResumeHandler.h" + +#include "yarpl/flowable/CancelingSubscriber.h" + +#include <folly/Conv.h> + +using namespace yarpl::flowable; + +namespace rsocket { + +std::string ColdResumeHandler::generateStreamToken( + const Payload&, + StreamId streamId, + StreamType) const { + return folly::to<std::string>(streamId); +} + +std::shared_ptr<Flowable<Payload>> +ColdResumeHandler::handleResponderResumeStream( + std::string /* streamToken */, + size_t /* publisherAllowance */) { + return Flowable<Payload>::error( + std::logic_error("ResumeHandler method not implemented")); +} + +std::shared_ptr<Subscriber<Payload>> +ColdResumeHandler::handleRequesterResumeStream( + std::string /* streamToken */, + size_t /* consumerAllowance */) { + return std::make_shared<CancelingSubscriber<Payload>>(); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.h b/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.h new file mode 100644 index 000000000..f4190e16f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/ColdResumeHandler.h @@ -0,0 +1,56 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/Flowable.h" + +#include "rsocket/Payload.h" +#include "rsocket/framing/FrameHeader.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +// This class has to be implemented by the client application for cold +// resumption. The default implementation will error/close the streams. +class ColdResumeHandler { + public: + virtual ~ColdResumeHandler() = default; + + // Generate an application-aware streamToken for the given stream parameters. + virtual std::string + generateStreamToken(const Payload&, StreamId streamId, StreamType) const; + + // This method will be called for each REQUEST_STREAM for which the + // application acted as a responder. The default action would be to return a + // Flowable which errors out immediately. + // The second parameter is the allowance which the application received + // before cold-start and hasn't been fulfilled yet. + virtual std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + handleResponderResumeStream( + std::string streamToken, + size_t publisherAllowance); + + // This method will be called for each REQUEST_STREAM for which the + // application acted as a requester. The default action would be to return a + // Subscriber which cancels the stream immediately after getting subscribed. + // The second parameter is the allowance which the application requested + // before cold-start and hasn't been fulfilled yet. + virtual std::shared_ptr<yarpl::flowable::Subscriber<rsocket::Payload>> + handleRequesterResumeStream( + std::string streamToken, + size_t consumerAllowance); +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/ConnectionAcceptor.h b/ios/Pods/Flipper-RSocket/rsocket/ConnectionAcceptor.h new file mode 100644 index 000000000..3e94a4416 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/ConnectionAcceptor.h @@ -0,0 +1,72 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Optional.h> + +#include "rsocket/DuplexConnection.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +using OnDuplexConnectionAccept = std::function< + void(std::unique_ptr<rsocket::DuplexConnection>, folly::EventBase&)>; + +/** + * Common interface for a server that accepts connections and turns them into + * DuplexConnection. + * + * This is primarily used with RSocket::createServer(ConnectionAcceptor) + * + * Built-in implementations can be found in rsocket/transports/, such as + * rsocket/transports/TcpConnectionAcceptor.h + */ +class ConnectionAcceptor { + public: + ConnectionAcceptor() = default; + virtual ~ConnectionAcceptor() = default; + + ConnectionAcceptor(const ConnectionAcceptor&) = delete; + ConnectionAcceptor(ConnectionAcceptor&&) = delete; + + ConnectionAcceptor& operator=(const ConnectionAcceptor&) = delete; + ConnectionAcceptor& operator=(ConnectionAcceptor&&) = delete; + + /** + * Allocate/start required resources (threads, sockets, etc) and begin + * listening for new connections. Must be synchronous. + * + * This can only be called once. + */ + virtual void start(OnDuplexConnectionAccept) = 0; + + /** + * Stop listening for new connections. + * + * This can only be called once. Must be called in or before + * the implementation's destructor. Must be synchronous. + */ + virtual void stop() = 0; + + /** + * Get the port the acceptor is listening on. Returns folly::none when the + * acceptor is not listening. + */ + virtual folly::Optional<uint16_t> listeningPort() const = 0; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/ConnectionFactory.h b/ios/Pods/Flipper-RSocket/rsocket/ConnectionFactory.h new file mode 100644 index 000000000..fd9605591 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/ConnectionFactory.h @@ -0,0 +1,66 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Function.h> +#include <folly/futures/Future.h> +#include "rsocket/DuplexConnection.h" +#include "rsocket/framing/ProtocolVersion.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +enum class ResumeStatus { NEW_SESSION, RESUMING }; + +/** + * Common interface for a client to create connections and turn them into + * DuplexConnections. + * + * This is primarily used with RSocket::createClient(ConnectionFactory) + * + * Built-in implementations can be found in rsocket/transports/, such as + * rsocket/transports/TcpConnectionFactory.h + */ +class ConnectionFactory { + public: + ConnectionFactory() = default; + virtual ~ConnectionFactory() = default; + ConnectionFactory(const ConnectionFactory&) = delete; // copy + ConnectionFactory(ConnectionFactory&&) = delete; // move + ConnectionFactory& operator=(const ConnectionFactory&) = delete; // copy + ConnectionFactory& operator=(ConnectionFactory&&) = delete; // move + + struct ConnectedDuplexConnection { + std::unique_ptr<rsocket::DuplexConnection> connection; + folly::EventBase& eventBase; + }; + + /** + * Connect to server defined by constructor of the implementing class. + * + * Every time this is called a new transport connection is made. This does not + * however mean it is a physical connection. An implementation could choose to + * multiplex many RSocket connections on a single transport. + * + * Resource creation depends on the particular implementation. + */ + virtual folly::Future<ConnectedDuplexConnection> connect( + ProtocolVersion, + ResumeStatus resume) = 0; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/DuplexConnection.h b/ios/Pods/Flipper-RSocket/rsocket/DuplexConnection.h new file mode 100644 index 000000000..7aaff2156 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/DuplexConnection.h @@ -0,0 +1,63 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <memory> + +#include <folly/io/IOBuf.h> + +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +/// Represents a connection of the underlying protocol, on top of which the +/// RSocket protocol is layered. The underlying protocol MUST provide an +/// ordered, guaranteed, bidirectional transport of frames. Moreover, frame +/// boundaries MUST be preserved. +/// +/// The frames exchanged through this interface are serialized, and lack the +/// optional frame length field. Presence of the field is determined by the +/// underlying protocol. If the protocol natively supports framing +/// (e.g. Aeron), the fileld MUST be omitted, otherwise (e.g. TCP) it must be +/// present. The RSocket implementation MUST NOT be provided with a frame that +/// contains the length field nor can it ever send such a frame. +/// +/// It can be assumed that both input and output will be closed by sending +/// appropriate terminal signals (according to ReactiveStreams specification) +/// before the connection is destroyed. +class DuplexConnection { + public: + using Subscriber = yarpl::flowable::Subscriber<std::unique_ptr<folly::IOBuf>>; + + virtual ~DuplexConnection() = default; + + /// Sets a Subscriber that will consume received frames (a reader). + /// + /// If setInput() has already been called, then calling setInput() again will + /// complete the previous subscriber. + virtual void setInput(std::shared_ptr<Subscriber>) = 0; + + /// Write a serialized frame to the connection. + /// + /// Does nothing if the underlying connection is closed. + virtual void send(std::unique_ptr<folly::IOBuf>) = 0; + + /// Whether the duplex connection respects frame boundaries. + virtual bool isFramed() const { + return false; + } +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/Payload.cpp b/ios/Pods/Flipper-RSocket/rsocket/Payload.cpp new file mode 100644 index 000000000..b4037d888 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/Payload.cpp @@ -0,0 +1,111 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/Payload.h" + +#include <folly/String.h> +#include <folly/io/Cursor.h> + +#include "rsocket/internal/Common.h" + +namespace rsocket { + +namespace { + +std::string moveIOBufToString(std::unique_ptr<folly::IOBuf> buf) { + return buf ? buf->moveToFbString().toStdString() : ""; +} + +std::string cloneIOBufToString(std::unique_ptr<folly::IOBuf> const& buf) { + return buf ? buf->cloneAsValue().moveToFbString().toStdString() : ""; +} + +} // namespace + +Payload::Payload( + std::unique_ptr<folly::IOBuf> d, + std::unique_ptr<folly::IOBuf> m) + : data{std::move(d)}, metadata{std::move(m)} {} + +Payload::Payload(folly::StringPiece d, folly::StringPiece m) + : data{folly::IOBuf::copyBuffer(d.data(), d.size())} { + if (!m.empty()) { + metadata = folly::IOBuf::copyBuffer(m.data(), m.size()); + } +} + +std::ostream& operator<<(std::ostream& os, const Payload& payload) { + return os << "Metadata(" + << (payload.metadata ? payload.metadata->computeChainDataLength() + : 0) + << "): " + << (payload.metadata ? "'" + humanify(payload.metadata) + "'" + : "<null>") + << ", Data(" + << (payload.data ? payload.data->computeChainDataLength() : 0) + << "): " + << (payload.data ? "'" + humanify(payload.data) + "'" : "<null>"); +} + +std::string Payload::moveDataToString() { + return moveIOBufToString(std::move(data)); +} + +std::string Payload::cloneDataToString() const { + return cloneIOBufToString(data); +} + +std::string Payload::moveMetadataToString() { + return moveIOBufToString(std::move(metadata)); +} + +std::string Payload::cloneMetadataToString() const { + return cloneIOBufToString(metadata); +} + +void Payload::clear() { + data.reset(); + metadata.reset(); +} + +Payload Payload::clone() const { + Payload out; + if (data) { + out.data = data->clone(); + } + if (metadata) { + out.metadata = metadata->clone(); + } + return out; +} + +ErrorWithPayload::ErrorWithPayload(Payload&& payload) + : payload(std::move(payload)) {} + +ErrorWithPayload::ErrorWithPayload(const ErrorWithPayload& oth) { + payload = oth.payload.clone(); +} + +ErrorWithPayload& ErrorWithPayload::operator=(const ErrorWithPayload& oth) { + payload = oth.payload.clone(); + return *this; +} + +std::ostream& operator<<( + std::ostream& os, + const ErrorWithPayload& errorWithPayload) { + return os << "rsocket::ErrorWithPayload: " << errorWithPayload.payload; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/Payload.h b/ios/Pods/Flipper-RSocket/rsocket/Payload.h new file mode 100644 index 000000000..c21587014 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/Payload.h @@ -0,0 +1,73 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/IOBuf.h> +#include <memory> +#include <string> + +namespace rsocket { + +/// The type of a read-only view on a binary buffer. +/// MUST manage the lifetime of the underlying buffer. +struct Payload { + Payload() = default; + + explicit Payload( + std::unique_ptr<folly::IOBuf> data, + std::unique_ptr<folly::IOBuf> metadata = std::unique_ptr<folly::IOBuf>()); + + explicit Payload( + folly::StringPiece data, + folly::StringPiece metadata = folly::StringPiece{}); + + explicit operator bool() const { + return data != nullptr || metadata != nullptr; + } + + std::string moveDataToString(); + std::string cloneDataToString() const; + + std::string moveMetadataToString(); + std::string cloneMetadataToString() const; + + void clear(); + + Payload clone() const; + + std::unique_ptr<folly::IOBuf> data; + std::unique_ptr<folly::IOBuf> metadata; +}; + +struct ErrorWithPayload : public std::exception { + explicit ErrorWithPayload(Payload&& payload); + + // folly::ExceptionWrapper requires exceptions to have copy constructors + ErrorWithPayload(const ErrorWithPayload& oth); + ErrorWithPayload& operator=(const ErrorWithPayload&); + ErrorWithPayload(ErrorWithPayload&&) = default; + ErrorWithPayload& operator=(ErrorWithPayload&&) = default; + + const char* what() const noexcept override { + return "ErrorWithPayload"; + } + + Payload payload; +}; + +std::ostream& operator<<(std::ostream& os, const Payload&); +std::ostream& operator<<(std::ostream& os, const ErrorWithPayload&); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/README.md b/ios/Pods/Flipper-RSocket/rsocket/README.md new file mode 100644 index 000000000..3b811aca9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/README.md @@ -0,0 +1,27 @@ +# rsocket-cpp + +C++ implementation of [RSocket](https://rsocket.io) + +<a href='https://travis-ci.org/rsocket/rsocket-cpp/builds'><img src='https://travis-ci.org/rsocket/rsocket-cpp.svg?branch=master'></a> +[![Coverage Status](https://coveralls.io/repos/github/rsocket/rsocket-cpp/badge.svg?branch=master)](https://coveralls.io/github/rsocket/rsocket-cpp?branch=master) + +# Dependencies + +Install `folly`: + +``` +brew install folly +``` + +# Building and running tests + +After installing dependencies as above, you can build and run tests with: + +``` +# inside root ./rsocket-cpp +mkdir -p build +cd build +cmake -DCMAKE_BUILD_TYPE=DEBUG ../ +make -j +./tests +``` diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocket.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocket.cpp new file mode 100644 index 000000000..e83c5ca71 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocket.cpp @@ -0,0 +1,140 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocket.h" + +namespace rsocket { + +folly::Future<std::unique_ptr<RSocketClient>> RSocket::createConnectedClient( + std::shared_ptr<ConnectionFactory> connectionFactory, + SetupParameters setupParameters, + std::shared_ptr<RSocketResponder> responder, + std::chrono::milliseconds keepaliveInterval, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + folly::EventBase* stateMachineEvb) { + CHECK(resumeManager) + << "provide ResumeManager::makeEmpty() instead of nullptr"; + auto protocolVersion = setupParameters.protocolVersion; + auto createRSC = + [connectionFactory, + setupParameters = std::move(setupParameters), + responder = std::move(responder), + keepaliveInterval, + stats = std::move(stats), + connectionEvents = std::move(connectionEvents), + resumeManager = std::move(resumeManager), + coldResumeHandler = std::move(coldResumeHandler), + stateMachineEvb]( + ConnectionFactory::ConnectedDuplexConnection connection) mutable { + VLOG(3) << "createConnectedClient received DuplexConnection"; + return RSocket::createClientFromConnection( + std::move(connection.connection), + connection.eventBase, + std::move(setupParameters), + std::move(connectionFactory), + std::move(responder), + keepaliveInterval, + std::move(stats), + std::move(connectionEvents), + std::move(resumeManager), + std::move(coldResumeHandler), + stateMachineEvb); + }; + + return connectionFactory->connect(protocolVersion, ResumeStatus::NEW_SESSION) + .thenValue( + [createRSC = std::move(createRSC)]( + ConnectionFactory::ConnectedDuplexConnection connection) mutable { + // fromConnection method must be called from the transport eventBase + // and since there is no guarantee that the Future returned from the + // connectionFactory::connect method is executed on the event base, + // we have to ensure it by using folly::via + auto transportEvb = &connection.eventBase; + return folly::via( + transportEvb, + [connection = std::move(connection), + createRSC = std::move(createRSC)]() mutable { + return createRSC(std::move(connection)); + }); + }); +} + +folly::Future<std::unique_ptr<RSocketClient>> RSocket::createResumedClient( + std::shared_ptr<ConnectionFactory> connectionFactory, + ResumeIdentificationToken token, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + std::shared_ptr<RSocketResponder> responder, + std::chrono::milliseconds keepaliveInterval, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + ProtocolVersion protocolVersion, + folly::EventBase* stateMachineEvb) { + auto* c = new RSocketClient( + std::move(connectionFactory), + std::move(protocolVersion), + std::move(token), + std::move(responder), + keepaliveInterval, + std::move(stats), + std::move(connectionEvents), + std::move(resumeManager), + std::move(coldResumeHandler), + stateMachineEvb); + + return c->resume().thenValue( + [client = std::unique_ptr<RSocketClient>(c)](auto&&) mutable { + return std::move(client); + }); +} + +std::unique_ptr<RSocketClient> RSocket::createClientFromConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& transportEvb, + SetupParameters params, + std::shared_ptr<ConnectionFactory> connectionFactory, + std::shared_ptr<RSocketResponder> responder, + std::chrono::milliseconds keepaliveInterval, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + folly::EventBase* stateMachineEvb) { + auto client = std::unique_ptr<RSocketClient>(new RSocketClient( + std::move(connectionFactory), + params.protocolVersion, + params.token, + std::move(responder), + keepaliveInterval, + std::move(stats), + std::move(connectionEvents), + std::move(resumeManager), + std::move(coldResumeHandler), + stateMachineEvb)); + client->fromConnection( + std::move(connection), transportEvb, std::move(params)); + return client; +} + +std::unique_ptr<RSocketServer> RSocket::createServer( + std::unique_ptr<ConnectionAcceptor> connectionAcceptor, + std::shared_ptr<RSocketStats> stats) { + return std::make_unique<RSocketServer>( + std::move(connectionAcceptor), std::move(stats)); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocket.h b/ios/Pods/Flipper-RSocket/rsocket/RSocket.h new file mode 100644 index 000000000..13f642830 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocket.h @@ -0,0 +1,86 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketClient.h" +#include "rsocket/RSocketServer.h" + +namespace rsocket { + +/** + * Main entry to creating RSocket clients and servers. + */ +class RSocket { + public: + // Creates a RSocketClient which is connected to the remoteside. + // keepaliveInterval of 0 will result in no keepAlives + static folly::Future<std::unique_ptr<RSocketClient>> createConnectedClient( + std::shared_ptr<ConnectionFactory>, + SetupParameters setupParameters = SetupParameters(), + std::shared_ptr<RSocketResponder> responder = + std::make_shared<RSocketResponder>(), + std::chrono::milliseconds keepaliveInterval = kDefaultKeepaliveInterval, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop(), + std::shared_ptr<RSocketConnectionEvents> connectionEvents = + std::shared_ptr<RSocketConnectionEvents>(), + std::shared_ptr<ResumeManager> resumeManager = ResumeManager::makeEmpty(), + std::shared_ptr<ColdResumeHandler> coldResumeHandler = + std::shared_ptr<ColdResumeHandler>(), + folly::EventBase* stateMachineEvb = nullptr); + + // Creates a RSocketClient which cold-resumes from the provided state + // keepaliveInterval of 0 will result in no keepAlives + static folly::Future<std::unique_ptr<RSocketClient>> createResumedClient( + std::shared_ptr<ConnectionFactory>, + ResumeIdentificationToken token, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + std::shared_ptr<RSocketResponder> responder = + std::make_shared<RSocketResponder>(), + std::chrono::milliseconds keepaliveInterval = kDefaultKeepaliveInterval, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop(), + std::shared_ptr<RSocketConnectionEvents> connectionEvents = + std::shared_ptr<RSocketConnectionEvents>(), + ProtocolVersion protocolVersion = ProtocolVersion::Latest, + folly::EventBase* stateMachineEvb = nullptr); + + // Creates a RSocketClient from an existing DuplexConnection. A keepalive + // interval of 0 will result in no keepalives. + static std::unique_ptr<RSocketClient> createClientFromConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& transportEvb, + SetupParameters setupParameters = SetupParameters(), + std::shared_ptr<ConnectionFactory> connectionFactory = nullptr, + std::shared_ptr<RSocketResponder> responder = + std::make_shared<RSocketResponder>(), + std::chrono::milliseconds keepaliveInterval = kDefaultKeepaliveInterval, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop(), + std::shared_ptr<RSocketConnectionEvents> connectionEvents = nullptr, + std::shared_ptr<ResumeManager> resumeManager = ResumeManager::makeEmpty(), + std::shared_ptr<ColdResumeHandler> coldResumeHandler = nullptr, + folly::EventBase* stateMachineEvb = nullptr); + + // A convenience function to create RSocketServer + static std::unique_ptr<RSocketServer> createServer( + std::unique_ptr<ConnectionAcceptor>, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + + RSocket() = delete; + RSocket(const RSocket&) = delete; + RSocket(RSocket&&) = delete; + RSocket& operator=(const RSocket&) = delete; + RSocket& operator=(RSocket&&) = delete; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.cpp new file mode 100644 index 000000000..7f10b3ead --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.cpp @@ -0,0 +1,242 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketClient.h" +#include "rsocket/RSocketRequester.h" +#include "rsocket/RSocketResponder.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/framing/FrameTransportImpl.h" +#include "rsocket/framing/FramedDuplexConnection.h" +#include "rsocket/framing/ScheduledFrameTransport.h" +#include "rsocket/internal/ClientResumeStatusCallback.h" +#include "rsocket/internal/KeepaliveTimer.h" + +namespace rsocket { + +RSocketClient::RSocketClient( + std::shared_ptr<ConnectionFactory> connectionFactory, + ProtocolVersion protocolVersion, + ResumeIdentificationToken token, + std::shared_ptr<RSocketResponder> responder, + std::chrono::milliseconds keepaliveInterval, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + folly::EventBase* stateMachineEvb) + : connectionFactory_(std::move(connectionFactory)), + responder_(std::move(responder)), + keepaliveInterval_(keepaliveInterval), + stats_(stats), + connectionEvents_(connectionEvents), + resumeManager_(resumeManager), + coldResumeHandler_(coldResumeHandler), + protocolVersion_(protocolVersion), + token_(std::move(token)), + evb_(stateMachineEvb) { + CHECK(resumeManager_) + << "provide ResumeManager::makeEmpty() instead of nullptr"; +} + +RSocketClient::~RSocketClient() { + VLOG(3) << "~RSocketClient .."; + + evb_->runImmediatelyOrRunInEventBaseThreadAndWait([sm = stateMachine_] { + auto exn = folly::make_exception_wrapper<std::runtime_error>( + "RSocketClient is closing"); + sm->close(std::move(exn), StreamCompletionSignal::CONNECTION_END); + }); +} + +const std::shared_ptr<RSocketRequester>& RSocketClient::getRequester() const { + return requester_; +} + +// Returns if this client is currently disconnected +bool RSocketClient::isDisconnected() const { + return stateMachine_->isDisconnected(); +} + +folly::Future<folly::Unit> RSocketClient::resume() { + CHECK(connectionFactory_) + << "The client was likely created without ConnectionFactory. Can't " + << "resume"; + + return connectionFactory_->connect(protocolVersion_, ResumeStatus::RESUMING) + .thenValue( + [this]( + ConnectionFactory::ConnectedDuplexConnection connection) mutable { + return resumeFromConnection(std::move(connection)); + }); +} + +folly::Future<folly::Unit> RSocketClient::resumeFromConnection( + ConnectionFactory::ConnectedDuplexConnection connection) { + VLOG(2) << "Resuming connection"; + + if (!evb_) { + // Cold-resumption. EventBase hasn't been explicitly set for SM by the + // application. Use the transport's eventBase. + evb_ = &connection.eventBase; + } + + class ResumeCallback : public ClientResumeStatusCallback { + public: + explicit ResumeCallback(folly::Promise<folly::Unit> promise) + : promise_(std::move(promise)) {} + + void onResumeOk() noexcept override { + promise_.setValue(); + } + + void onResumeError(folly::exception_wrapper ex) noexcept override { + promise_.setException(ex); + } + + private: + folly::Promise<folly::Unit> promise_; + }; + + folly::Promise<folly::Unit> promise; + auto future = promise.getFuture(); + + auto resumeCallback = std::make_unique<ResumeCallback>(std::move(promise)); + std::unique_ptr<DuplexConnection> framedConnection; + if (connection.connection->isFramed()) { + framedConnection = std::move(connection.connection); + } else { + framedConnection = std::make_unique<FramedDuplexConnection>( + std::move(connection.connection), protocolVersion_); + } + auto transport = + std::make_shared<FrameTransportImpl>(std::move(framedConnection)); + + std::shared_ptr<FrameTransport> ft; + if (evb_ != &connection.eventBase) { + // If the StateMachine EventBase is different from the transport + // EventBase, then use ScheduledFrameTransport and + // ScheduledFrameProcessor to ensure the RSocketStateMachine and + // Transport live on the desired EventBases + ft = std::make_shared<ScheduledFrameTransport>( + std::move(transport), + &connection.eventBase, /* Transport EventBase */ + evb_); /* StateMachine EventBase */ + } else { + ft = std::move(transport); + } + + evb_->runInEventBaseThread([this, + frameTransport = std::move(ft), + callback = std::move(resumeCallback)]() mutable { + if (!stateMachine_) { + createState(); + } + + stateMachine_->resumeClient( + token_, + std::move(frameTransport), + std::move(callback), + protocolVersion_); + }); + + return future; +} + +folly::Future<folly::Unit> RSocketClient::disconnect( + folly::exception_wrapper ew) { + if (!stateMachine_) { + return folly::makeFuture<folly::Unit>( + std::runtime_error{"RSocketClient must always have a state machine"}); + } + + auto work = [sm = stateMachine_, e = std::move(ew)]() mutable { + sm->disconnect(std::move(e)); + }; + + if (evb_->isInEventBaseThread()) { + VLOG(2) << "Running RSocketClient disconnect synchronously"; + work(); + return folly::unit; + } + + VLOG(2) << "Scheduling RSocketClient disconnect"; + return folly::via(evb_, work); +} + +void RSocketClient::fromConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& transportEvb, + SetupParameters params) { + if (!evb_) { + // If no EventBase is given for the stateMachine, then use the transport's + // EventBase to drive the stateMachine. + evb_ = &transportEvb; + } + createState(); + + std::unique_ptr<DuplexConnection> framed; + if (connection->isFramed()) { + framed = std::move(connection); + } else { + framed = std::make_unique<FramedDuplexConnection>( + std::move(connection), params.protocolVersion); + } + auto transport = std::make_shared<FrameTransportImpl>(std::move(framed)); + + if (evb_ == &transportEvb) { + stateMachine_->connectClient(std::move(transport), std::move(params)); + return; + } + + // If the StateMachine EventBase is different from the transport EventBase, + // then use ScheduledFrameTransport and ScheduledFrameProcessor to ensure the + // RSocketStateMachine and Transport live on the desired EventBases. + auto scheduledFT = std::make_shared<ScheduledFrameTransport>( + std::move(transport), &transportEvb, evb_); + evb_->runInEventBaseThread([stateMachine = stateMachine_, + scheduledFT = std::move(scheduledFT), + params = std::move(params)]() mutable { + stateMachine->connectClient(std::move(scheduledFT), std::move(params)); + }); +} + +void RSocketClient::createState() { + // Creation of state is permitted only once for each RSocketClient. + // When evb is removed from RSocketStateMachine, the state can be + // created in constructor + CHECK(!stateMachine_) << "A stateMachine has already been created"; + + if (!responder_) { + responder_ = std::make_shared<RSocketResponder>(); + } + + std::unique_ptr<KeepaliveTimer> keepaliveTimer{nullptr}; + if (keepaliveInterval_ > std::chrono::milliseconds(0)) { + keepaliveTimer = + std::make_unique<KeepaliveTimer>(keepaliveInterval_, *evb_); + } + + stateMachine_ = std::make_shared<RSocketStateMachine>( + std::move(responder_), + std::move(keepaliveTimer), + RSocketMode::CLIENT, + std::move(stats_), + std::move(connectionEvents_), + std::move(resumeManager_), + std::move(coldResumeHandler_)); + + requester_ = std::make_shared<RSocketRequester>(stateMachine_, *evb_); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.h new file mode 100644 index 000000000..070a3f6be --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketClient.h @@ -0,0 +1,120 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/futures/Future.h> + +#include "rsocket/ColdResumeHandler.h" +#include "rsocket/ConnectionFactory.h" +#include "rsocket/DuplexConnection.h" +#include "rsocket/RSocketConnectionEvents.h" +#include "rsocket/RSocketParameters.h" +#include "rsocket/RSocketRequester.h" +#include "rsocket/RSocketResponder.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/ResumeManager.h" + +namespace rsocket { + +class RSocket; + +/** + * API for connecting to an RSocket server. Created with RSocket class. + * This connects using a transport from the provided ConnectionFactory. + */ +class RSocketClient { + public: + ~RSocketClient(); + + RSocketClient(const RSocketClient&) = delete; + RSocketClient(RSocketClient&&) = delete; + RSocketClient& operator=(const RSocketClient&) = delete; + RSocketClient& operator=(RSocketClient&&) = delete; + + friend class RSocket; + + // Returns the RSocketRequester associated with the RSocketClient. + const std::shared_ptr<RSocketRequester>& getRequester() const; + + // Returns if this client is currently disconnected + bool isDisconnected() const; + + // Resumes the client's connection. If the client was previously connected + // this will attempt a warm-resumption. Otherwise this will attempt a + // cold-resumption. + // + // Uses the internal ConnectionFactory instance to re-connect. + folly::Future<folly::Unit> resume(); + + // Like resume(), but this doesn't use a ConnectionFactory and instead takes + // the connection and transport EventBase by argument. + // + // Prefer using resume() if possible. + folly::Future<folly::Unit> resumeFromConnection( + ConnectionFactory::ConnectedDuplexConnection); + + // Disconnect the underlying transport. + folly::Future<folly::Unit> disconnect(folly::exception_wrapper = {}); + + private: + // Private constructor. RSocket class should be used to create instances + // of RSocketClient. + RSocketClient( + std::shared_ptr<ConnectionFactory>, + ProtocolVersion protocolVersion, + ResumeIdentificationToken token, + std::shared_ptr<RSocketResponder> responder, + std::chrono::milliseconds keepaliveInterval, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler, + folly::EventBase* stateMachineEvb); + + // Create stateMachine with the given DuplexConnection + void fromConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& transportEvb, + SetupParameters setupParameters); + + // Creates RSocketStateMachine and RSocketRequester + void createState(); + + const std::shared_ptr<ConnectionFactory> connectionFactory_; + std::shared_ptr<RSocketResponder> responder_; + const std::chrono::milliseconds keepaliveInterval_; + std::shared_ptr<RSocketStats> stats_; + std::shared_ptr<RSocketConnectionEvents> connectionEvents_; + std::shared_ptr<ResumeManager> resumeManager_; + std::shared_ptr<ColdResumeHandler> coldResumeHandler_; + + std::shared_ptr<RSocketStateMachine> stateMachine_; + std::shared_ptr<RSocketRequester> requester_; + + const ProtocolVersion protocolVersion_; + const ResumeIdentificationToken token_; + + // Remember the StateMachine's evb (supplied through constructor). If no + // EventBase is provided, the underlying transport's EventBase will be used + // to drive the StateMachine. + // If an EventBase is provided for StateMachine and underlying Transport's + // EventBase is different from it, then we use Scheduled* classes to let the + // StateMachine and Transport live on different EventBases. + // It might happen that the StateMachine and Transport live on same + // EventBase, but the transport ends up being in different EventBase after + // resumption, and vice versa. + folly::EventBase* evb_{nullptr}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketConnectionEvents.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketConnectionEvents.h new file mode 100644 index 000000000..177a819d2 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketConnectionEvents.h @@ -0,0 +1,54 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +namespace folly { +class exception_wrapper; +} + +namespace rsocket { + +// The application should implement this interface to get called-back +// on network events. +class RSocketConnectionEvents { + public: + virtual ~RSocketConnectionEvents() = default; + + // This method gets called when the underlying transport is connected to the + // remote side. This does not necessarily mean that the RSocket connection + // will be successful. As an example, the transport might get reconnected + // for an existing RSocketStateMachine. But resumption at the RSocket layer + // might not succeed. + virtual void onConnected() {} + + // This gets called when the underlying transport has disconnected. This also + // means the RSocket connection is disconnected. + virtual void onDisconnected(const folly::exception_wrapper&) {} + + // This gets called when the RSocketStateMachine is closed. You cant use this + // RSocketStateMachine anymore. + virtual void onClosed(const folly::exception_wrapper&) {} + + // This gets called when no more frames can be sent over the RSocket streams. + // This typically happens immediately after onDisconnected(). The streams can + // be resumed after onStreamsResumed() event. + virtual void onStreamsPaused() {} + + // This gets called when the underlying transport has been successfully + // connected AND the connection can be resumed at the RSocket layer. This + // typically gets called after onConnected() + virtual void onStreamsResumed() {} +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketErrors.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketErrors.h new file mode 100644 index 000000000..e570e7532 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketErrors.h @@ -0,0 +1,133 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <stdexcept> +#include <string> + +namespace rsocket { + +/* + * Error Codes from + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#error-codes + */ +class RSocketError : public std::runtime_error { + public: + using std::runtime_error::runtime_error; + + /** + * Get the error code for inclusion in an RSocket ERROR frame as per + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#error-codes + * @return + */ + virtual int getErrorCode() const = 0; +}; + +/** + * Error Code: INVALID_SETUP 0x00000001 + */ +class InvalidSetupError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000001; + } + + const char* what() const noexcept override { + return "INVALID_SETUP"; + } +}; + +/** + * Error Code: UNSUPPORTED_SETUP 0x00000002 + */ +class UnsupportedSetupError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000002; + } + + const char* what() const noexcept override { + return "UNSUPPORTED_SETUP"; + } +}; + +/** + * Error Code: REJECTED_SETUP 0x00000003 + */ +class RejectedSetupError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000003; + } + + const char* what() const noexcept override { + return "REJECTED_SETUP"; + } +}; + +/** + * Error Code: REJECTED_RESUME 0x00000004 + */ +class RejectedResumeError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000004; + } + + const char* what() const noexcept override { + return "REJECTED_RESUME"; + } +}; + +/** + * Error Code: CONNECTION_ERROR 0x00000101 + */ +class ConnectionError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000101; + } + + const char* what() const noexcept override { + return "CONNECTION_ERROR"; + } +}; + +/** + * Error Code: CONNECTION_CLOSE 0x00000102 + */ +class ConnectionCloseError : public RSocketError { + public: + using RSocketError::RSocketError; + + int getErrorCode() const override { + return 0x00000102; + } + + const char* what() const noexcept override { + return "CONNECTION_CLOSE"; + } +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketException.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketException.h new file mode 100644 index 000000000..9dc9d61e7 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketException.h @@ -0,0 +1,36 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <stdexcept> + +namespace rsocket { + +class RSocketException : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +// Thrown when an ERROR frame with CONNECTION_ERROR or REJECTED_RESUME is +// received during resumption. +class ResumptionException : public RSocketException { + using RSocketException::RSocketException; +}; + +// Thrown when the resume operation was interrupted due to network. +// The application may try to resume again. +class ConnectionException : public RSocketException { + using RSocketException::RSocketException; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.cpp new file mode 100644 index 000000000..08f221e44 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketParameters.h" + +#include <folly/String.h> + +namespace rsocket { +std::ostream& operator<<( + std::ostream& os, + const SetupParameters& setupPayload) { + return os << "metadataMimeType: " << setupPayload.metadataMimeType + << " dataMimeType: " << setupPayload.dataMimeType + << " payload: " << setupPayload.payload + << " token: " << setupPayload.token + << " resumable: " << setupPayload.resumable; +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.h new file mode 100644 index 000000000..0605bcfcf --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketParameters.h @@ -0,0 +1,80 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <functional> +#include <iosfwd> +#include <string> +#include <vector> + +#include "rsocket/Payload.h" +#include "rsocket/framing/Frame.h" + +namespace rsocket { + +using OnRSocketResume = + std::function<bool(std::vector<StreamId>, std::vector<StreamId>)>; + +class RSocketParameters { + public: + RSocketParameters(bool resume, ProtocolVersion version) + : resumable{resume}, protocolVersion{std::move(version)} {} + + bool resumable; + ProtocolVersion protocolVersion; +}; + +class SetupParameters : public RSocketParameters { + public: + explicit SetupParameters( + std::string metadataMime = "text/plain", + std::string dataMime = "text/plain", + Payload buf = Payload(), + bool resume = false, + ResumeIdentificationToken resumeToken = + ResumeIdentificationToken::generateNew(), + ProtocolVersion version = ProtocolVersion::Latest) + : RSocketParameters(resume, version), + metadataMimeType(std::move(metadataMime)), + dataMimeType(std::move(dataMime)), + payload(std::move(buf)), + token(resumeToken) {} + + std::string metadataMimeType; + std::string dataMimeType; + Payload payload; + ResumeIdentificationToken token; +}; + +std::ostream& operator<<(std::ostream&, const SetupParameters&); + +class ResumeParameters : public RSocketParameters { + public: + ResumeParameters( + ResumeIdentificationToken resumeToken, + ResumePosition serverPos, + ResumePosition clientPos, + ProtocolVersion version) + : RSocketParameters(true, version), + token(std::move(resumeToken)), + serverPosition(serverPos), + clientPosition(clientPos) {} + + ResumeIdentificationToken token; + ResumePosition serverPosition; + ResumePosition clientPosition; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.cpp new file mode 100644 index 000000000..cf1799506 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.cpp @@ -0,0 +1,181 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketRequester.h" + +#include <folly/ExceptionWrapper.h> + +#include "rsocket/internal/ScheduledSingleObserver.h" +#include "rsocket/internal/ScheduledSubscriber.h" +#include "yarpl/Flowable.h" +#include "yarpl/single/SingleSubscriptions.h" + +using namespace folly; + +namespace rsocket { + +namespace { + +template <class Fn> +void runOnCorrectThread(folly::EventBase& evb, Fn fn) { + if (evb.isInEventBaseThread()) { + fn(); + } else { + evb.runInEventBaseThread(std::move(fn)); + } +} + +} // namespace + +RSocketRequester::RSocketRequester( + std::shared_ptr<RSocketStateMachine> srs, + EventBase& eventBase) + : stateMachine_{std::move(srs)}, eventBase_{&eventBase} {} + +RSocketRequester::~RSocketRequester() { + VLOG(1) << "Destroying RSocketRequester"; +} + +void RSocketRequester::closeSocket() { + eventBase_->runInEventBaseThread([stateMachine = std::move(stateMachine_)] { + VLOG(2) << "Closing RSocketStateMachine on EventBase"; + stateMachine->close({}, StreamCompletionSignal::SOCKET_CLOSED); + }); +} + +std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> +RSocketRequester::requestChannel( + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestStream) { + return requestChannel({}, false, std::move(requestStream)); +} + +std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> +RSocketRequester::requestChannel( + Payload request, + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestStream) { + return requestChannel(std::move(request), true, std::move(requestStream)); +} + +std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> +RSocketRequester::requestChannel( + Payload request, + bool hasInitialRequest, + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestStreamFlowable) { + CHECK(stateMachine_); + + return yarpl::flowable::internal::flowableFromSubscriber<Payload>( + [eb = eventBase_, + req = std::move(request), + hasInitialRequest, + requestStream = std::move(requestStreamFlowable), + srs = stateMachine_]( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + auto lambda = [eb, + r = req.clone(), + hasInitialRequest, + requestStream, + srs, + subs = std::move(subscriber)]() mutable { + auto scheduled = + std::make_shared<ScheduledSubscriptionSubscriber<Payload>>( + std::move(subs), *eb); + auto responseSink = srs->requestChannel( + std::move(r), hasInitialRequest, std::move(scheduled)); + // responseSink is wrapped with thread scheduling + // so all emissions happen on the right thread. + + // If we don't get a responseSink back, that means that + // the requesting peer wasn't connected (or similar error) + // and the Flowable it gets back will immediately call onError. + if (responseSink) { + auto scheduledResponse = + std::make_shared<ScheduledSubscriber<Payload>>( + std::move(responseSink), *eb); + requestStream->subscribe(std::move(scheduledResponse)); + } + }; + runOnCorrectThread(*eb, std::move(lambda)); + }); +} + +std::shared_ptr<yarpl::flowable::Flowable<Payload>> +RSocketRequester::requestStream(Payload request) { + CHECK(stateMachine_); + + return yarpl::flowable::internal::flowableFromSubscriber<Payload>( + [eb = eventBase_, req = std::move(request), srs = stateMachine_]( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + auto lambda = + [eb, r = req.clone(), srs, subs = std::move(subscriber)]() mutable { + auto scheduled = + std::make_shared<ScheduledSubscriptionSubscriber<Payload>>( + std::move(subs), *eb); + srs->requestStream(std::move(r), std::move(scheduled)); + }; + runOnCorrectThread(*eb, std::move(lambda)); + }); +} + +std::shared_ptr<yarpl::single::Single<rsocket::Payload>> +RSocketRequester::requestResponse(Payload request) { + CHECK(stateMachine_); + + return yarpl::single::Single<Payload>::create( + [eb = eventBase_, req = std::move(request), srs = stateMachine_]( + std::shared_ptr<yarpl::single::SingleObserver<Payload>> observer) { + auto lambda = [eb, + r = req.clone(), + srs, + obs = std::move(observer)]() mutable { + auto scheduled = + std::make_shared<ScheduledSubscriptionSingleObserver<Payload>>( + std::move(obs), *eb); + srs->requestResponse(std::move(r), std::move(scheduled)); + }; + runOnCorrectThread(*eb, std::move(lambda)); + }); +} + +std::shared_ptr<yarpl::single::Single<void>> RSocketRequester::fireAndForget( + rsocket::Payload request) { + CHECK(stateMachine_); + + return yarpl::single::Single<void>::create( + [eb = eventBase_, req = std::move(request), srs = stateMachine_]( + std::shared_ptr<yarpl::single::SingleObserverBase<void>> subscriber) { + auto lambda = + [r = req.clone(), srs, subs = std::move(subscriber)]() mutable { + // TODO: Pass in SingleSubscriber for underlying layers to call + // onSuccess/onError once put on network. + srs->fireAndForget(std::move(r)); + subs->onSubscribe(yarpl::single::SingleSubscriptions::empty()); + subs->onSuccess(); + }; + runOnCorrectThread(*eb, std::move(lambda)); + }); +} + +void RSocketRequester::metadataPush(std::unique_ptr<folly::IOBuf> metadata) { + CHECK(stateMachine_); + + runOnCorrectThread( + *eventBase_, [srs = stateMachine_, meta = std::move(metadata)]() mutable { + srs->metadataPush(std::move(meta)); + }); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.h new file mode 100644 index 000000000..a87d15955 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketRequester.h @@ -0,0 +1,131 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "yarpl/Flowable.h" +#include "yarpl/Single.h" + +#include "rsocket/Payload.h" +#include "rsocket/statemachine/RSocketStateMachine.h" + +namespace rsocket { + +/** + * Request APIs to submit requests on an RSocket connection. + * + * This is most commonly used by an RSocketClient, but due to the symmetric + * nature of RSocket, this can be used from server->client as well. + * + * For context within the overall RSocket protocol: + * + * - Client: The side initiating a connection. + * - Server: The side accepting connections from clients. + * - Connection: The instance of a transport session between client and server. + * - Requester: The side sending a request. + * A connection has at most 2 Requesters. One in each direction. + * - Responder: The side receiving a request. + * A connection has at most 2 Responders. One in each direction. + * + * See https://github.com/rsocket/rsocket/blob/master/Protocol.md#terminology + * for more information on how this fits into the RSocket protocol terminology. + */ +class RSocketRequester { + public: + RSocketRequester( + std::shared_ptr<rsocket::RSocketStateMachine> srs, + folly::EventBase& eventBase); + + virtual ~RSocketRequester(); // implementing for logging right now + + RSocketRequester(const RSocketRequester&) = delete; + RSocketRequester(RSocketRequester&&) = delete; + + RSocketRequester& operator=(const RSocketRequester&) = delete; + RSocketRequester& operator=(RSocketRequester&&) = delete; + + /** + * Send a single request and get a response stream. + * + * Interaction model details can be found at + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#request-stream + */ + virtual std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestStream(rsocket::Payload request); + + /** + * Start a channel (streams in both directions). + * + * Interaction model details can be found at + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#request-channel + */ + virtual std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestChannel( + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> requests); + + /** + * As requestStream function accepts an initial request, this version of + * requestChannel also accepts an initial request. + * @see requestChannel + * @see requestStream + */ + virtual std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestChannel( + Payload request, + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> requests); + + /** + * Send a single request and get a single response. + * + * Interaction model details can be found at + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#stream-sequences-request-response + */ + virtual std::shared_ptr<yarpl::single::Single<rsocket::Payload>> + requestResponse(rsocket::Payload request); + + /** + * Send a single Payload with no response. + * + * The returned Single<void> invokes onSuccess or onError + * based on client-side success or failure. Once the payload is + * sent to the network it is "forgotten" and the Single<void> will + * be finished with no further response indicating success + * or failure on the server. + * + * Interaction model details can be found at + * https://github.com/ReactiveSocket/reactivesocket/blob/master/Protocol.md#request-fire-n-forget + */ + virtual std::shared_ptr<yarpl::single::Single<void>> fireAndForget( + rsocket::Payload request); + + /** + * Send metadata without response. + */ + virtual void metadataPush(std::unique_ptr<folly::IOBuf> metadata); + + virtual void closeSocket(); + + protected: + virtual std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + requestChannel( + Payload request, + bool hasInitialRequest, + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> requests); + + std::shared_ptr<rsocket::RSocketStateMachine> stateMachine_; + folly::EventBase* eventBase_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.cpp new file mode 100644 index 000000000..892d2e12e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.cpp @@ -0,0 +1,200 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketResponder.h" + +#include <folly/io/async/EventBase.h> +#include <yarpl/flowable/CancelingSubscriber.h> + +namespace rsocket { + +using namespace yarpl::flowable; +using namespace yarpl::single; + +void RSocketResponderCore::handleRequestStream( + Payload, + StreamId, + std::shared_ptr<Subscriber<Payload>> response) noexcept { + response->onSubscribe(Subscription::create()); + response->onError(std::logic_error("handleRequestStream not implemented")); +} + +void RSocketResponderCore::handleRequestResponse( + Payload, + StreamId, + std::shared_ptr<SingleObserver<Payload>> responseObserver) noexcept { + responseObserver->onSubscribe(SingleSubscriptions::empty()); + responseObserver->onError( + std::logic_error("handleRequestResponse not implemented")); +} + +void RSocketResponderCore::handleFireAndForget(Payload, StreamId) { + // No default implementation, no error response to provide. +} + +void RSocketResponderCore::handleMetadataPush(std::unique_ptr<folly::IOBuf>) { + // No default implementation, no error response to provide. +} + +std::shared_ptr<Subscriber<Payload>> RSocketResponderCore::handleRequestChannel( + Payload, + StreamId, + std::shared_ptr<Subscriber<Payload>> response) noexcept { + response->onSubscribe(Subscription::create()); + response->onError(std::logic_error("handleRequestStream not implemented")); + + // cancel immediately + return std::make_shared<CancelingSubscriber<Payload>>(); +} + +std::shared_ptr<Single<Payload>> RSocketResponder::handleRequestResponse( + Payload, + StreamId) { + return Singles::error<Payload>( + std::logic_error("handleRequestResponse not implemented")); +} + +std::shared_ptr<Flowable<Payload>> RSocketResponder::handleRequestStream( + Payload, + StreamId) { + return Flowable<Payload>::error( + std::logic_error("handleRequestStream not implemented")); +} + +std::shared_ptr<Flowable<Payload>> RSocketResponder::handleRequestChannel( + Payload, + std::shared_ptr<Flowable<Payload>>, + StreamId) { + return Flowable<Payload>::error( + std::logic_error("handleRequestChannel not implemented")); +} + +void RSocketResponder::handleFireAndForget(Payload, StreamId) { + // No default implementation, no error response to provide. +} + +void RSocketResponder::handleMetadataPush(std::unique_ptr<folly::IOBuf>) { + // No default implementation, no error response to provide. +} + +/// Handles a new Channel requested by the other end. +std::shared_ptr<Subscriber<Payload>> +RSocketResponderAdapter::handleRequestChannel( + Payload request, + StreamId streamId, + std::shared_ptr<Subscriber<Payload>> response) noexcept { + class EagerSubscriberBridge : public Subscriber<Payload> { + public: + void onSubscribe( + std::shared_ptr<Subscription> subscription) noexcept override { + CHECK(!subscription_); + subscription_ = std::move(subscription); + if (inner_) { + inner_->onSubscribe(subscription_); + } + } + + void onNext(Payload element) noexcept override { + DCHECK(inner_); + inner_->onNext(std::move(element)); + } + + void onComplete() noexcept override { + if (auto inner = std::move(inner_)) { + inner->onComplete(); + subscription_.reset(); + } else { + completed_ = true; + } + } + + void onError(folly::exception_wrapper ex) noexcept override { + VLOG(3) << "handleRequestChannelCore::onError: " << ex.what(); + if (auto inner = std::move(inner_)) { + inner->onError(std::move(ex)); + subscription_.reset(); + } else { + error_ = std::move(ex); + } + } + + void subscribe(std::shared_ptr<Subscriber<Payload>> inner) { + CHECK(!inner_); // only one call to subscribe is supported + CHECK(inner); + + inner_ = std::move(inner); + if (subscription_) { + inner_->onSubscribe(subscription_); + // it's possible to get an error or completion before subscribe happens, + // delay sending it but send it when this class gets subscribed + if (completed_) { + onComplete(); + } else if (error_) { + onError(std::move(error_)); + } + } + } + + private: + std::shared_ptr<Subscriber<Payload>> inner_; + std::shared_ptr<Subscription> subscription_; + folly::exception_wrapper error_; + bool completed_{false}; + }; + + auto eagerSubscriber = std::make_shared<EagerSubscriberBridge>(); + auto flowable = inner_->handleRequestChannel( + std::move(request), + internal::flowableFromSubscriber<Payload>( + [eagerSubscriber](std::shared_ptr<Subscriber<Payload>> subscriber) { + eagerSubscriber->subscribe(subscriber); + }), + std::move(streamId)); + // bridge from the existing eager RequestHandler and old Subscriber type + // to the lazy Flowable and new Subscriber type + flowable->subscribe(std::move(response)); + return eagerSubscriber; +} + +/// Handles a new Stream requested by the other end. +void RSocketResponderAdapter::handleRequestStream( + Payload request, + StreamId streamId, + std::shared_ptr<Subscriber<Payload>> response) noexcept { + auto flowable = + inner_->handleRequestStream(std::move(request), std::move(streamId)); + flowable->subscribe(std::move(response)); +} + +/// Handles a new inbound RequestResponse requested by the other end. +void RSocketResponderAdapter::handleRequestResponse( + Payload request, + StreamId streamId, + std::shared_ptr<SingleObserver<Payload>> responseObserver) noexcept { + auto single = inner_->handleRequestResponse(std::move(request), streamId); + single->subscribe(std::move(responseObserver)); +} + +void RSocketResponderAdapter::handleFireAndForget( + Payload request, + StreamId streamId) { + inner_->handleFireAndForget(std::move(request), streamId); +} + +void RSocketResponderAdapter::handleMetadataPush( + std::unique_ptr<folly::IOBuf> buf) { + inner_->handleMetadataPush(std::move(buf)); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.h new file mode 100644 index 000000000..4fc17cc9c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketResponder.h @@ -0,0 +1,154 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/framing/FrameHeader.h" +#include "yarpl/Flowable.h" +#include "yarpl/Single.h" + +namespace rsocket { + +class RSocketResponderCore { + public: + virtual ~RSocketResponderCore() = default; + + virtual void handleFireAndForget(Payload request, StreamId streamId); + + virtual void handleMetadataPush(std::unique_ptr<folly::IOBuf> metadata); + + virtual std::shared_ptr<yarpl::flowable::Subscriber<Payload>> + handleRequestChannel( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) noexcept; + + virtual void handleRequestStream( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) noexcept; + + virtual void handleRequestResponse( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> + response) noexcept; +}; + +/** + * Responder APIs to handle requests on an RSocket connection. + * + * This is most commonly used by an RSocketServer, but due to the symmetric + * nature of RSocket, this can be used on the client as well. + * + * For context within the overall RSocket protocol: + * + * - Client: The side initiating a connection. + * - Server: The side accepting connections from clients. + * - Connection: The instance of a transport session between client and server. + * - Requester: The side sending a request. + * A connection has at most 2 Requesters. One in each direction. + * - Responder: The side receiving a request. + * A connection has at most 2 Responders. One in each direction. + * + * See https://github.com/rsocket/rsocket/blob/master/Protocol.md#terminology + * for more information on how this fits into the RSocket protocol terminology. + */ +class RSocketResponder { + public: + virtual ~RSocketResponder() = default; + + /** + * Called when a new `requestResponse` occurs from an RSocketRequester. + * + * Returns a Single with the response. + */ + virtual std::shared_ptr<yarpl::single::Single<Payload>> handleRequestResponse( + Payload request, + StreamId streamId); + + /** + * Called when a new `requestStream` occurs from an RSocketRequester. + * + * Returns a Flowable with the response stream. + */ + virtual std::shared_ptr<yarpl::flowable::Flowable<Payload>> + handleRequestStream(Payload request, StreamId streamId); + + /** + * Called when a new `requestChannel` occurs from an RSocketRequester. + * + * Returns a Flowable with the response stream. + */ + virtual std::shared_ptr<yarpl::flowable::Flowable<Payload>> + handleRequestChannel( + Payload request, + std::shared_ptr<yarpl::flowable::Flowable<Payload>> requestStream, + StreamId streamId); + + /** + * Called when a new `fireAndForget` occurs from an RSocketRequester. + * + * No response. + */ + virtual void handleFireAndForget( + rsocket::Payload request, + rsocket::StreamId streamId); + + /** + * Called when a new `metadataPush` occurs from an RSocketRequester. + * + * No response. + */ + virtual void handleMetadataPush(std::unique_ptr<folly::IOBuf> metadata); +}; + +class RSocketResponderAdapter : public RSocketResponderCore { + public: + explicit RSocketResponderAdapter(std::shared_ptr<RSocketResponder> inner) + : inner_(std::move(inner)) {} + virtual ~RSocketResponderAdapter() = default; + + /// Internal method for handling channel requests, not intended to be used by + /// application code. + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> handleRequestChannel( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> + response) noexcept override; + + /// Internal method for handling stream requests, not intended to be used + /// by application code. + void handleRequestStream( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> + response) noexcept override; + + /// Internal method for handling request-response requests, not intended to be + /// used by application code. + void handleRequestResponse( + Payload request, + StreamId streamId, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> + response) noexcept override; + + void handleFireAndForget(Payload request, StreamId streamId) override; + void handleMetadataPush(std::unique_ptr<folly::IOBuf> buf) override; + + private: + std::shared_ptr<RSocketResponder> inner_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.cpp new file mode 100644 index 000000000..d1da360ea --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.cpp @@ -0,0 +1,268 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketServer.h" +#include <folly/io/async/EventBaseManager.h> + +#include <rsocket/internal/ScheduledRSocketResponder.h> +#include "rsocket/RSocketErrors.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/framing/FramedDuplexConnection.h" +#include "rsocket/framing/ScheduledFrameTransport.h" +#include "rsocket/internal/ConnectionSet.h" +#include "rsocket/internal/WarmResumeManager.h" + +namespace rsocket { + +RSocketServer::RSocketServer( + std::unique_ptr<ConnectionAcceptor> connectionAcceptor, + std::shared_ptr<RSocketStats> stats) + : duplexConnectionAcceptor_(std::move(connectionAcceptor)), + setupResumeAcceptors_([] { + return new rsocket::SetupResumeAcceptor{ + folly::EventBaseManager::get()->getExistingEventBase()}; + }), + connectionSet_(std::make_unique<ConnectionSet>()), + stats_(std::move(stats)) {} + +RSocketServer::~RSocketServer() { + VLOG(3) << "~RSocketServer .."; + shutdownAndWait(); +} + +void RSocketServer::shutdownAndWait() { + if (isShutdown_) { + return; + } + + // Will stop forwarding connections from duplexConnectionAcceptor_ to + // setupResumeAcceptors_ + isShutdown_ = true; + + // Stop accepting new connections. + if (duplexConnectionAcceptor_) { + duplexConnectionAcceptor_->stop(); + } + + std::vector<folly::Future<folly::Unit>> closingFutures; + for (auto& acceptor : setupResumeAcceptors_.accessAllThreads()) { + // This call will queue up the cleanup on the eventBase. + closingFutures.push_back(acceptor.close()); + } + + folly::collectAllSemiFuture(closingFutures).get(); + + // Close off all outstanding connections. + connectionSet_->shutdownAndWait(); +} + +void RSocketServer::start( + std::shared_ptr<RSocketServiceHandler> serviceHandler) { + CHECK(duplexConnectionAcceptor_); // RSocketServer has to be initialized with + // the acceptor + + if (started) { + throw std::runtime_error("RSocketServer::start() already called."); + } + started = true; + + duplexConnectionAcceptor_->start( + [this, serviceHandler]( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& eventBase) { + acceptConnection(std::move(connection), eventBase, serviceHandler); + }); +} + +void RSocketServer::start(OnNewSetupFn onNewSetupFn) { + start(RSocketServiceHandler::create(std::move(onNewSetupFn))); +} + +void RSocketServer::startAndPark(OnNewSetupFn onNewSetupFn) { + startAndPark(RSocketServiceHandler::create(std::move(onNewSetupFn))); +} + +void RSocketServer::setSingleThreadedResponder() { + useScheduledResponder_ = false; +} + +void RSocketServer::acceptConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase&, + std::shared_ptr<RSocketServiceHandler> serviceHandler) { + stats_->serverConnectionAccepted(); + if (isShutdown_) { + // connection is getting out of scope and terminated + return; + } + + std::unique_ptr<DuplexConnection> framedConnection; + if (connection->isFramed()) { + framedConnection = std::move(connection); + } else { + framedConnection = std::make_unique<FramedDuplexConnection>( + std::move(connection), ProtocolVersion::Unknown); + } + + auto* acceptor = setupResumeAcceptors_.get(); + + VLOG(2) << "Going to accept duplex connection"; + + acceptor->accept( + std::move(framedConnection), + [serviceHandler, + weakConSet = std::weak_ptr<ConnectionSet>(connectionSet_), + scheduledResponder = useScheduledResponder_]( + std::unique_ptr<DuplexConnection> conn, + SetupParameters params) mutable { + if (auto connectionSet = weakConSet.lock()) { + RSocketServer::onRSocketSetup( + serviceHandler, + std::move(connectionSet), + scheduledResponder, + std::move(conn), + std::move(params)); + } + }, + std::bind( + &RSocketServer::onRSocketResume, + this, + serviceHandler, + std::placeholders::_1, + std::placeholders::_2)); +} + +void RSocketServer::onRSocketSetup( + std::shared_ptr<RSocketServiceHandler> serviceHandler, + std::shared_ptr<ConnectionSet> connectionSet, + bool scheduledResponder, + std::unique_ptr<DuplexConnection> connection, + SetupParameters setupParams) { + const auto eventBase = folly::EventBaseManager::get()->getExistingEventBase(); + VLOG(2) << "Received new setup payload on " << eventBase->getName(); + CHECK(eventBase); + auto result = serviceHandler->onNewSetup(setupParams); + if (result.hasError()) { + VLOG(3) << "Terminating SETUP attempt from client. " + << result.error().what(); + connection->send( + FrameSerializer::createFrameSerializer(setupParams.protocolVersion) + ->serializeOut(Frame_ERROR::rejectedSetup(result.error().what()))); + return; + } + auto connectionParams = std::move(result.value()); + if (!connectionParams.responder) { + LOG(ERROR) << "Received invalid Responder. Dropping connection"; + connection->send( + FrameSerializer::createFrameSerializer(setupParams.protocolVersion) + ->serializeOut(Frame_ERROR::rejectedSetup( + "Received invalid Responder from server"))); + return; + } + const auto rs = std::make_shared<RSocketStateMachine>( + scheduledResponder + ? std::make_shared<ScheduledRSocketResponder>( + std::move(connectionParams.responder), *eventBase) + : std::move(connectionParams.responder), + nullptr, + RSocketMode::SERVER, + connectionParams.stats, + std::move(connectionParams.connectionEvents), + setupParams.resumable + ? std::make_shared<WarmResumeManager>(connectionParams.stats) + : ResumeManager::makeEmpty(), + nullptr /* coldResumeHandler */); + + if (!connectionSet->insert(rs, eventBase)) { + VLOG(1) << "Server is closed, so ignore the connection"; + connection->send( + FrameSerializer::createFrameSerializer(setupParams.protocolVersion) + ->serializeOut(Frame_ERROR::rejectedSetup( + "Server ignores the connection attempt"))); + return; + } + rs->registerCloseCallback(connectionSet.get()); + + auto requester = std::make_shared<RSocketRequester>(rs, *eventBase); + auto serverState = std::shared_ptr<RSocketServerState>( + new RSocketServerState(*eventBase, rs, std::move(requester))); + serviceHandler->onNewRSocketState(std::move(serverState), setupParams.token); + rs->connectServer( + std::make_shared<FrameTransportImpl>(std::move(connection)), + std::move(setupParams)); +} + +void RSocketServer::onRSocketResume( + std::shared_ptr<RSocketServiceHandler> serviceHandler, + std::unique_ptr<DuplexConnection> connection, + ResumeParameters resumeParams) { + auto result = serviceHandler->onResume(resumeParams.token); + if (result.hasError()) { + stats_->resumeFailedNoState(); + VLOG(3) << "Terminating RESUME attempt from client. No ServerState found"; + connection->send( + FrameSerializer::createFrameSerializer(resumeParams.protocolVersion) + ->serializeOut(Frame_ERROR::rejectedSetup(result.error().what()))); + return; + } + const auto serverState = std::move(result.value()); + CHECK(serverState); + const auto eventBase = folly::EventBaseManager::get()->getExistingEventBase(); + VLOG(2) << "Resuming client on " << eventBase->getName(); + if (!serverState->eventBase_.isInEventBaseThread()) { + // If the resumed connection is on a different EventBase, then use + // ScheduledFrameTransport and ScheduledFrameProcessor to ensure the + // RSocketStateMachine continues to live on the same EventBase and the + // IO happens in the new EventBase + auto scheduledFT = std::make_shared<ScheduledFrameTransport>( + std::make_shared<FrameTransportImpl>(std::move(connection)), + eventBase, /* Transport EventBase */ + &serverState->eventBase_); /* StateMachine EventBase */ + serverState->eventBase_.runInEventBaseThread( + [serverState, + scheduledFT = std::move(scheduledFT), + resumeParams = std::move(resumeParams)]() mutable { + serverState->rSocketStateMachine_->resumeServer( + std::move(scheduledFT), resumeParams); + }); + } else { + // If the resumed connection is on the same EventBase, then the + // RSocketStateMachine and Transport can continue living in the same + // EventBase without any thread hopping between them. + serverState->rSocketStateMachine_->resumeServer( + std::make_shared<FrameTransportImpl>(std::move(connection)), + resumeParams); + } +} + +void RSocketServer::startAndPark( + std::shared_ptr<RSocketServiceHandler> serviceHandler) { + start(std::move(serviceHandler)); + waiting_.wait(); +} + +void RSocketServer::unpark() { + waiting_.post(); +} + +folly::Optional<uint16_t> RSocketServer::listeningPort() const { + return duplexConnectionAcceptor_ ? duplexConnectionAcceptor_->listeningPort() + : folly::none; +} + +size_t RSocketServer::getNumConnections() { + return connectionSet_ ? connectionSet_->size() : 0; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.h new file mode 100644 index 000000000..39dae66a3 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketServer.h @@ -0,0 +1,143 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <mutex> + +#include <folly/Synchronized.h> +#include <folly/ThreadLocal.h> +#include <folly/synchronization/Baton.h> + +#include "rsocket/ConnectionAcceptor.h" +#include "rsocket/RSocketParameters.h" +#include "rsocket/RSocketResponder.h" +#include "rsocket/RSocketServiceHandler.h" +#include "rsocket/internal/ConnectionSet.h" +#include "rsocket/internal/SetupResumeAcceptor.h" + +namespace rsocket { + +/** + * API for starting an RSocket server. Returned from RSocket::createServer. + * + * This listens for connections using a transport from the provided + * ConnectionAcceptor. + * + */ +class RSocketServer { + public: + explicit RSocketServer( + std::unique_ptr<ConnectionAcceptor>, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + ~RSocketServer(); + + RSocketServer(const RSocketServer&) = delete; + RSocketServer(RSocketServer&&) = delete; + RSocketServer& operator=(const RSocketServer&) = delete; + RSocketServer& operator=(RSocketServer&&) = delete; + + /** + * Start the ConnectionAcceptor and begin handling connections. + * + * This method blocks until the server has started. It returns if successful + * or throws an exception if failure occurs. + * + * This method assumes it will be called only once. + */ + void start(std::shared_ptr<RSocketServiceHandler> serviceHandler); + void start(OnNewSetupFn onNewSetupFn); + + /** + * Start the ConnectionAcceptor and begin handling connections. + * + * This method will block the calling thread as long as the server is running. + * It will throw an exception if a failure occurs on startup. + * + * The provided RSocketServiceHandler will be used to handle all connections + * to this server. If you wish to use different RSocketServiceHandler for + * each connection, then refer to acceptConnection() + * + * This method assumes it will be called only once. + */ + void startAndPark(std::shared_ptr<RSocketServiceHandler> serviceHandler); + void startAndPark(OnNewSetupFn onNewSetupFn); + + /** + * Unblock the server if it has called startAndPark(). Can only be called + * once. + */ + void unpark(); + + /** + * Accept RSocket connection over the provided DuplexConnection. The + * provided RSocketServiceHandler will be used to handle the connection. + */ + void acceptConnection( + std::unique_ptr<DuplexConnection> connection, + folly::EventBase& eventBase, + std::shared_ptr<RSocketServiceHandler> serviceHandler); + + void shutdownAndWait(); + + /** + * Gets the port the ConnectionAcceptor is listening on. Returns folly::none + * if this server is not listening on a port. + */ + folly::Optional<uint16_t> listeningPort() const; + + /** + * Use the same EventBase that is provided to acceptConnection function for + * internal operations. Don't schedule to another event base. + */ + void setSingleThreadedResponder(); + + /** + * Number of active connections to this server. + */ + size_t getNumConnections(); + + private: + static void onRSocketSetup( + std::shared_ptr<RSocketServiceHandler> serviceHandler, + std::shared_ptr<ConnectionSet> connectionSet, + bool scheduledResponder, + std::unique_ptr<DuplexConnection> connection, + rsocket::SetupParameters setupPayload); + void onRSocketResume( + std::shared_ptr<RSocketServiceHandler> serviceHandler, + std::unique_ptr<DuplexConnection> connection, + rsocket::ResumeParameters setupPayload); + + const std::unique_ptr<ConnectionAcceptor> duplexConnectionAcceptor_; + bool started{false}; + + class SetupResumeAcceptorTag {}; + folly::ThreadLocal<rsocket::SetupResumeAcceptor, SetupResumeAcceptorTag> + setupResumeAcceptors_; + + folly::Baton<> waiting_; + std::atomic<bool> isShutdown_{false}; + + std::shared_ptr<ConnectionSet> connectionSet_; + std::shared_ptr<RSocketStats> stats_; + + /** + * If this field is false, acceptConnection() function will assume that there + * will be a single thread for each connected client. The execution will not + * be scheduled to another event base. + */ + bool useScheduledResponder_{true}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketServerState.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketServerState.h new file mode 100644 index 000000000..c5d010dbb --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketServerState.h @@ -0,0 +1,56 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketRequester.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +class RSocketServerState { + public: + void close() { + eventBase_.runInEventBaseThread([sm = rSocketStateMachine_] { + sm->close({}, StreamCompletionSignal::SOCKET_CLOSED); + }); + } + + std::shared_ptr<RSocketRequester> getRequester() { + return rSocketRequester_; + } + + folly::EventBase* eventBase() { + return &eventBase_; + } + + friend class RSocketServer; + + private: + RSocketServerState( + folly::EventBase& eventBase, + std::shared_ptr<RSocketStateMachine> stateMachine, + std::shared_ptr<RSocketRequester> rSocketRequester) + : eventBase_(eventBase), + rSocketStateMachine_(stateMachine), + rSocketRequester_(rSocketRequester) {} + + folly::EventBase& eventBase_; + const std::shared_ptr<RSocketStateMachine> rSocketStateMachine_; + const std::shared_ptr<RSocketRequester> rSocketRequester_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.cpp new file mode 100644 index 000000000..8e3f8d341 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.cpp @@ -0,0 +1,55 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketServiceHandler.h" + +namespace rsocket { + +void RSocketServiceHandler::onNewRSocketState( + std::shared_ptr<RSocketServerState>, + ResumeIdentificationToken) {} + +folly::Expected<std::shared_ptr<RSocketServerState>, RSocketException> +RSocketServiceHandler::onResume(ResumeIdentificationToken) { + return folly::makeUnexpected(RSocketException("No ServerState")); +} + +bool RSocketServiceHandler::canResume( + const std::vector<StreamId>& /* cleanStreamIds */, + const std::vector<StreamId>& /* dirtyStreamIds */, + ResumeIdentificationToken) const { + return true; +} + +std::shared_ptr<RSocketServiceHandler> RSocketServiceHandler::create( + OnNewSetupFn onNewSetupFn) { + class ServiceHandler : public RSocketServiceHandler { + public: + explicit ServiceHandler(OnNewSetupFn fn) : onNewSetupFn_(std::move(fn)) {} + folly::Expected<RSocketConnectionParams, RSocketException> onNewSetup( + const SetupParameters& setupParameters) override { + try { + return RSocketConnectionParams(onNewSetupFn_(setupParameters)); + } catch (const std::exception& e) { + return folly::Unexpected<RSocketException>( + ConnectionException(e.what())); + } + } + + private: + OnNewSetupFn onNewSetupFn_; + }; + return std::make_shared<ServiceHandler>(std::move(onNewSetupFn)); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.h new file mode 100644 index 000000000..b67caa358 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketServiceHandler.h @@ -0,0 +1,110 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Expected.h> + +#include "rsocket/RSocketConnectionEvents.h" +#include "rsocket/RSocketException.h" +#include "rsocket/RSocketParameters.h" +#include "rsocket/RSocketResponder.h" +#include "rsocket/RSocketServerState.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +// This is a convenience function which is used to create a simplified +// RSocketServiceHandler. +using OnNewSetupFn = + folly::Function<std::shared_ptr<RSocketResponder>(const SetupParameters&)>; + +// This struct holds all the necessary information needed by the RSocketServer +// to initiate a connection with a client. +struct RSocketConnectionParams { + explicit RSocketConnectionParams( + std::shared_ptr<RSocketResponder> _responder, + std::shared_ptr<RSocketStats> _stats = RSocketStats::noop(), + std::shared_ptr<RSocketConnectionEvents> _connectionEvents = nullptr) + : responder(std::move(_responder)), + stats(std::move(_stats)), + connectionEvents(std::move(_connectionEvents)) {} + std::shared_ptr<RSocketResponder> responder; + std::shared_ptr<RSocketStats> stats; + std::shared_ptr<RSocketConnectionEvents> connectionEvents; +}; + +// This class has to be implemented by the application. The methods can be +// called from different threads and it is the application's responsibility to +// ensure thread-safety. +// +// A RSocketServiceHandler instance should be passed as parameter to the +// RSocketServer::start() methods. The passed instance will apply globally to +// all connections handled by the server. +// +// If the application wishes to preserve per connection information at the +// transport level (say HTTP Auth, TCP options) and use it at the application +// layer OR if the application wishes to have per-connection +// RSocketServiceHandler, then it should pass an instance of +// RSocketServiceHandler to the RSocketServer::acceptConnection() method. In +// this case it does not have to worry about thread safety. +// +class RSocketServiceHandler { + public: + RSocketServiceHandler() = default; + virtual ~RSocketServiceHandler() = default; + RSocketServiceHandler(RSocketServiceHandler&&) = default; // move + RSocketServiceHandler& operator=(RSocketServiceHandler&&) = default; // move + + // This method gets called for each client that connects to the server. The + // application has to implement this method. If the application does not + // want to accept this connection, it should return a RSocketException. If + // the application returns a RSocketException, then an ERROR frame with code + // REJECTED_SETUP is sent to the client. The exception message is sent as + // payload. + virtual folly::Expected<RSocketConnectionParams, RSocketException> onNewSetup( + const SetupParameters&) = 0; + + // This method gets called after some state is created for each client. The + // application should preserve the RSocketServerState if it wants to resume + // the connection later. + virtual void onNewRSocketState( + std::shared_ptr<RSocketServerState>, + ResumeIdentificationToken); + + // This method gets called when a client tries to resume. The application + // should return the corresponding RSocketServerState for the given resumeId + // if it wants to resume. The application should return a RSocketException + // if it does not want to resume. If the application returns a + // RSocketException, then an ERROR frame with code REJECTED_RESUME is sent to + // the client. The exception message is sent as payload. + virtual folly::Expected<std::shared_ptr<RSocketServerState>, RSocketException> + onResume(ResumeIdentificationToken); + + // This method gives a fine-grained control to the application during + // resumption (provided it agreed to resume in the onResume() call above). + // It tells the application what streams will be resumed in clean/dirty + // state. If the application is okay with resuming the given clean/dirty + // streams, it should return true. + virtual bool canResume( + const std::vector<StreamId>& /* cleanStreamIds */, + const std::vector<StreamId>& /* dirtyStreamIds */, + ResumeIdentificationToken) const; + + // Convenience constructor to create a simple RSocketServiceHandler. + static std::shared_ptr<RSocketServiceHandler> create( + OnNewSetupFn onNewSetupFn); +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.cpp b/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.cpp new file mode 100644 index 000000000..ee7bc6f70 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/RSocketStats.h" + +namespace rsocket { + +class NoopStats : public RSocketStats { + public: + NoopStats() = default; + NoopStats(const NoopStats&) = delete; // non construction-copyable + NoopStats& operator=(const NoopStats&) = delete; // non copyable + NoopStats& operator=(const NoopStats&&) = delete; // non movable + NoopStats(NoopStats&&) = delete; // non construction-movable + ~NoopStats() override = default; + + void socketCreated() override {} + void socketConnected() override {} + void socketDisconnected() override {} + void socketClosed(StreamCompletionSignal) override {} + + void serverConnectionAccepted() override {} + + void duplexConnectionCreated(const std::string&, rsocket::DuplexConnection*) + override {} + + void duplexConnectionClosed(const std::string&, rsocket::DuplexConnection*) + override {} + + void bytesWritten(size_t) override {} + void bytesRead(size_t) override {} + void frameWritten(FrameType) override {} + void frameRead(FrameType) override {} + void serverResume(folly::Optional<int64_t>, int64_t, int64_t, ResumeOutcome) + override {} + void resumeBufferChanged(int, int) override {} + void streamBufferChanged(int64_t, int64_t) override {} + + void resumeFailedNoState() override {} + + void keepaliveSent() override {} + void keepaliveReceived() override {} + + static std::shared_ptr<NoopStats> instance() { + static const auto singleton = std::make_shared<NoopStats>(); + return singleton; + } +}; + +std::shared_ptr<RSocketStats> RSocketStats::noop() { + return NoopStats::instance(); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.h b/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.h new file mode 100644 index 000000000..8e7480b91 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/RSocketStats.h @@ -0,0 +1,71 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Optional.h> +#include <cstdint> +#include <memory> +#include <string> + +#include "rsocket/framing/FrameType.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +class DuplexConnection; + +class RSocketStats { + public: + enum class ResumeOutcome { SUCCESS, FAILURE }; + + virtual ~RSocketStats() = default; + + static std::shared_ptr<RSocketStats> noop(); + + virtual void socketCreated() {} + virtual void socketConnected() {} + virtual void socketDisconnected() {} + virtual void socketClosed(StreamCompletionSignal /* signal */) {} + + virtual void serverConnectionAccepted() {} + + virtual void duplexConnectionCreated( + const std::string& /* type */, + DuplexConnection* /* connection */) {} + virtual void duplexConnectionClosed( + const std::string& /* type */, + DuplexConnection* /* connection */) {} + virtual void serverResume( + folly::Optional<int64_t> /* clientAvailable */, + int64_t /* serverAvailable */, + int64_t /* serverDelta */, + ResumeOutcome /* outcome */) {} + virtual void bytesWritten(size_t /* bytes */) {} + virtual void bytesRead(size_t /* bytes */) {} + virtual void frameWritten(FrameType /* frameType */) {} + virtual void frameRead(FrameType /* frameType */) {} + virtual void resumeBufferChanged( + int /* framesCountDelta */, + int /* dataSizeDelta */) {} + virtual void streamBufferChanged( + int64_t /* framesCountDelta */, + int64_t /* dataSizeDelta */) {} + virtual void resumeFailedNoState() {} + virtual void keepaliveSent() {} + virtual void keepaliveReceived() {} + virtual void unknownFrameReceived() { + } // TODO(lehecka): add to all implementations +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/ResumeManager.h b/ios/Pods/Flipper-RSocket/rsocket/ResumeManager.h new file mode 100644 index 000000000..198539916 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/ResumeManager.h @@ -0,0 +1,161 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Optional.h> +#include <unordered_map> +#include "rsocket/framing/Frame.h" +#include "rsocket/framing/FrameTransportImpl.h" + +namespace folly { +class IOBuf; +} + +namespace rsocket { + +// Struct to hold information relevant per stream. +struct StreamResumeInfo { + StreamResumeInfo() = delete; + StreamResumeInfo(StreamType sType, RequestOriginator req, std::string sToken) + : streamType(sType), requester(req), streamToken(sToken) {} + + // REQUEST_STREAM, REQUEST_CHANNEL or REQUEST_RESPONSE. We don't + // have to store any stream level information for FNF. + StreamType streamType; + + // Did the stream originate locally or remotely. + RequestOriginator requester; + + // Application defined string representation for the stream. + std::string streamToken; + + // Stores the allowance which the local side has received but hasn't + // fulfilled yet. Relevant for REQUEST_STREAM Responder and REQUEST_CHANNEL + size_t producerAllowance{0}; + + // Stores the allowance which has been sent to the remote side and has not + // been fulfilled yet. Relevant for REQUEST_STREAM Requester and + // REQUEST_CHANNEL + size_t consumerAllowance{0}; +}; + +using StreamResumeInfos = std::unordered_map<StreamId, StreamResumeInfo>; + +// Applications desiring to have cold-resumption should implement a +// ResumeManager interface. By default, an in-memory implementation of this +// interface (WarmResumeManager) will be used by RSocket. +// +// The API refers to the stored frames by "position". "position" is the byte +// count at frame boundaries. For example, if the ResumeManager has stored 3 +// 100-byte sent frames starting from byte count 150. Then, +// - isPositionAvailable would return true for the values [150, 250, 350]. +// - firstSentPosition() would return 150 +// - lastSentPosition() would return 350 +class ResumeManager { + public: + static std::shared_ptr<ResumeManager> makeEmpty(); + + virtual ~ResumeManager() {} + + // The following methods will be called for each frame which is being + // sent/received on the wire. The application should implement a way to + // store the sent and received frames in persistent storage. + virtual void trackReceivedFrame( + size_t frameLength, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) = 0; + + virtual void trackSentFrame( + const folly::IOBuf& serializedFrame, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) = 0; + + // We have received acknowledgement from the remote-side that it has frames + // up to "position". We can discard all frames before that. This + // information is periodically received from remote-side through KeepAlive + // frames. + virtual void resetUpToPosition(ResumePosition position) = 0; + + // The application should check its persistent storage and respond whether it + // has frames starting from "position" in send buffer. + virtual bool isPositionAvailable(ResumePosition position) const = 0; + + // The application should send frames starting from the "position" using the + // provided "transport". As an alternative, we could design the API such + // that we retrieve individual frames from the application and send them over + // wire. But that would mean application has random access to frames + // indexed by position. This API gives the flexibility to the application to + // store the frames in any way it wants (randomly accessed or sequentially + // accessed). + virtual void sendFramesFromPosition( + ResumePosition position, + FrameTransport& transport) const = 0; + + // This should return the first (oldest) available position in the send + // buffer. + virtual ResumePosition firstSentPosition() const = 0; + + // This should return the last (latest) available position in the send + // buffer. + virtual ResumePosition lastSentPosition() const = 0; + + // This should return the latest tracked position of frames received from + // remote side. + virtual ResumePosition impliedPosition() const = 0; + + // This gets called when a stream is opened (both local/remote streams) + virtual void onStreamOpen( + StreamId, + RequestOriginator, + std::string streamToken, + StreamType streamType) = 0; + + // This gets called when a stream is closed (both local/remote streams) + virtual void onStreamClosed(StreamId streamId) = 0; + + // Returns the cached stream information. + virtual const StreamResumeInfos& getStreamResumeInfos() const = 0; + + // Returns the largest used StreamId so far. + virtual StreamId getLargestUsedStreamId() const = 0; + + // Utility method to check frames which should be tracked for resumption. + virtual bool shouldTrackFrame(const FrameType frameType) const { + switch (frameType) { + case FrameType::REQUEST_CHANNEL: + case FrameType::REQUEST_STREAM: + case FrameType::REQUEST_RESPONSE: + case FrameType::REQUEST_FNF: + case FrameType::REQUEST_N: + case FrameType::CANCEL: + case FrameType::ERROR: + case FrameType::PAYLOAD: + return true; + case FrameType::RESERVED: + case FrameType::SETUP: + case FrameType::LEASE: + case FrameType::KEEPALIVE: + case FrameType::METADATA_PUSH: + case FrameType::RESUME: + case FrameType::RESUME_OK: + case FrameType::EXT: + default: + return false; + } + } +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesAsyncSocket.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesAsyncSocket.cpp new file mode 100644 index 000000000..74b7165ba --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesAsyncSocket.cpp @@ -0,0 +1,285 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 <folly/Benchmark.h> +#include <folly/io/IOBufQueue.h> +#include <folly/io/async/AsyncServerSocket.h> +#include <folly/io/async/AsyncTransport.h> +#include <folly/io/async/ScopedEventBaseThread.h> + +#define PORT (35437) + +// namespace { +// +// class TcpReader : public ::folly::AsyncTransportWrapper::ReadCallback { +// public: +// TcpReader( +// folly::AsyncTransportWrapper::UniquePtr&& socket, +// EventBase& eventBase, +// size_t loadSize, +// size_t recvBufferLength) +// : socket_(std::move(socket)), +// eventBase_(eventBase), +// loadSize_(loadSize), +// recvBufferLength_(recvBufferLength) { +// socket_->setReadCB(this); +// } +// +// private: +// void getReadBuffer(void** bufReturn, size_t* lenReturn) noexcept override { +// std::tie(*bufReturn, *lenReturn) = +// readBuffer_.preallocate(recvBufferLength_, recvBufferLength_); +// } +// +// void readDataAvailable(size_t len) noexcept override { +// readBuffer_.postallocate(len); +// auto readData = readBuffer_.split(len); +// +// receivedLength_ += readData->computeChainDataLength(); +// ++reads_; +// if (receivedLength_ >= loadSize_) { +// LOG(INFO) << "closing reader"; +// close(); +// } +// } +// +// void readBufferAvailable( +// std::unique_ptr<folly::IOBuf> readBuf) noexcept override { +// receivedLength_ += readBuf->computeChainDataLength(); +// ++reads_; +// if (receivedLength_ >= loadSize_) { +// LOG(INFO) << "closing reader"; +// close(); +// } +// } +// +// void readEOF() noexcept override { +// LOG(INFO) << "closing reader"; +// close(); +// } +// +// void readErr(const folly::AsyncSocketException& exn) noexcept override { +// LOG(ERROR) << exn.what(); +// close(); +// } +// +// bool isBufferMovable() noexcept override { +// return true; +// } +// +// void close() { +// if (socket_) { +// LOG(INFO) << "received " << receivedLength_ << " via " << reads_ +// << " reads"; +// auto socket = std::move(socket_); +// socket->close(); +// eventBase_.terminateLoopSoon(); +// delete this; +// } +// } +// +// folly::AsyncTransportWrapper::UniquePtr socket_; +// folly::IOBufQueue readBuffer_{folly::IOBufQueue::cacheChainLength()}; +// EventBase& eventBase_; +// const size_t loadSize_; +// const size_t recvBufferLength_; +// size_t receivedLength_{0}; +// int reads_{0}; +//}; +// +// class ServerAcceptCallback : public AsyncServerSocket::AcceptCallback { +// public: +// ServerAcceptCallback( +// EventBase& eventBase, +// size_t loadSize, +// size_t recvBufferLength) +// : eventBase_(eventBase), +// loadSize_(loadSize), +// recvBufferLength_(recvBufferLength) {} +// +// void connectionAccepted( +// int fd, +// const SocketAddress&) noexcept override { +// auto socket = +// folly::AsyncTransportWrapper::UniquePtr(new AsyncSocket(&eventBase_, +// fd)); +// +// new TcpReader( +// std::move(socket), eventBase_, loadSize_, recvBufferLength_); +// } +// +// void acceptError(const std::exception& ex) noexcept override { +// LOG(FATAL) << "acceptError" << ex.what() << std::endl; +// eventBase_.terminateLoopSoon(); +// } +// +// private: +// EventBase& eventBase_; +// const size_t loadSize_; +// const size_t recvBufferLength_; +//}; +// +// class TcpWriter : public ::folly::AsyncTransportWrapper::WriteCallback { +// public: +// ~TcpWriter() { +// LOG(INFO) << "writes=" << writes_ << " success=" << success_ << " errors=" +// << errors_; +// } +// +// void startWriting(AsyncSocket& socket, size_t loadSize, +// size_t messageSize) { +// size_t bytesSent{0}; +// +// while (!closed_ && bytesSent < loadSize) { +// auto data = IOBuf::copyBuffer(std::string(messageSize, 'a')); +// socket.writeChain(this, std::move(data)); +// ++writes_; +// bytesSent += messageSize; +// } +// LOG(INFO) << "wrote " << bytesSent << " closed=" << closed_; +// } +// +// private: +// void writeSuccess() noexcept override { +// ++success_; +// } +// +// void writeErr( +// size_t, +// const folly::AsyncSocketException& exn) noexcept override { +// LOG_EVERY_N(ERROR,10000) << "writeError: " << exn.what(); +// closed_ = true; +// ++errors_; +// } +// +// bool closed_{false}; +// int writes_{0}; +// int success_{0}; +// int errors_{0}; +//}; +// +// class ClientConnectCallback : public AsyncSocket::ConnectCallback { +// public: +// ClientConnectCallback(EventBase& eventBase, size_t loadSize, +// size_t msgLength) +// : eventBase_(eventBase), loadSize_(loadSize), msgLength_(msgLength) {} +// +// void connect() { +// eventBase_.runInEventBaseThread([this] { +// socket_.reset(new AsyncSocket(&eventBase_)); +// SocketAddress clientAaddr("::", PORT); +// socket_->connect(this, clientAaddr); +// }); +// } +// +// private: +// void connectSuccess() noexcept override { +// { +// TcpWriter writer; +// LOG(INFO) << "startWriting"; +// writer.startWriting(*socket_, loadSize_, msgLength_); +// LOG(INFO) << "endWriting"; +// socket_->close(); +// LOG(INFO) << "socket closed, deleting this"; +// } +// delete this; +// } +// +// void connectErr(const AsyncSocketException& ex) noexcept override { +// LOG(FATAL) << "connectErr: " << ex.what() << " " << ex.getType(); +// delete this; +// } +// +// AsyncTransportWrapper::UniquePtr socket_; +// EventBase& eventBase_; +// const size_t loadSize_; +// const size_t msgLength_; +//}; +//} + +static void BM_Baseline_AsyncSocket_SendReceive( + size_t /*loadSize*/, + size_t /*msgLength*/, + size_t /*recvLength*/) { + LOG_EVERY_N(INFO, 10000) << "TODO(lehecka): benchmark needs updating, " + << "it has memory corruption bugs"; + // EventBase serverEventBase; + // auto serverSocket = AsyncServerSocket::newSocket(&serverEventBase); + // + // ServerAcceptCallback serverCallback(serverEventBase, loadSize, + // recvLength); + // + // SocketAddress addr("::", PORT); + // + // serverSocket->setReusePortEnabled(true); + // serverSocket->bind(addr); + // serverSocket->addAcceptCallback(&serverCallback, &serverEventBase); + // serverSocket->listen(1); + // serverSocket->startAccepting(); + // + // ScopedEventBaseThread clientThread; + // auto* clientCallback = new ClientConnectCallback( + // *clientThread.getEventBase(), loadSize, msgLength); + // clientCallback->connect(); + // + // serverEventBase.loopForever(); +} + +BENCHMARK(BM_Baseline_AsyncSocket_Throughput_100MB_s40B_r1024B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 40; + constexpr size_t receiveSizeB = 1024; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_AsyncSocket_Throughput_100MB_s40B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 40; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_AsyncSocket_Throughput_100MB_s80B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 80; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_AsyncSocket_Throughput_100MB_s4096B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 4096; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} + +BENCHMARK(BM_Baseline_AsyncSocket_Latency_1M_msgs_32B, n) { + (void)n; + constexpr size_t messageSizeB = 32; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} +BENCHMARK(BM_Baseline_AsyncSocket_Latency_1M_msgs_128B, n) { + (void)n; + constexpr size_t messageSizeB = 128; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} +BENCHMARK(BM_Baseline_AsyncSocket_Latency_1M_msgs_4kB, n) { + (void)n; + constexpr size_t messageSizeB = 4096; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_AsyncSocket_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesTcp.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesTcp.cpp new file mode 100644 index 000000000..d9e22892d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/BaselinesTcp.cpp @@ -0,0 +1,183 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 <arpa/inet.h> +#include <folly/Benchmark.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#include <array> +#include <atomic> +#include <cstring> +#include <iostream> +#include <thread> + +#define MAX_MESSAGE_LENGTH (8 * 1024) +#define PORT (35437) + +static void BM_Baseline_TCP_SendReceive( + size_t loadSize, + size_t msgLength, + size_t recvLength) { + std::atomic<bool> accepting{false}; + std::atomic<bool> accepted{false}; + + std::thread t([&]() { + int serverSock = socket(AF_INET, SOCK_STREAM, 0); + int sock = -1; + struct sockaddr_in addr = {}; + socklen_t addrlen = sizeof(addr); + std::array<char, MAX_MESSAGE_LENGTH> message = {}; + + if (serverSock < 0) { + perror("acceptor socket"); + return; + } + + int enable = 1; + if (setsockopt( + serverSock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < + 0) { + perror("setsocketopt SO_REUSEADDR"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(PORT); + if (bind(serverSock, reinterpret_cast<struct sockaddr*>(&addr), addrlen) < + 0) { + perror("bind"); + return; + } + + if (listen(serverSock, 1) < 0) { + perror("listen"); + return; + } + + accepting.store(true); + + if ((sock = accept( + serverSock, reinterpret_cast<struct sockaddr*>(&addr), &addrlen)) < + 0) { + perror("accept"); + return; + } + + accepted.store(true); + + size_t sentBytes = 0; + while (sentBytes < loadSize) { + if (send(sock, message.data(), msgLength, 0) != + static_cast<ssize_t>(msgLength)) { + perror("send"); + return; + } + sentBytes += msgLength; + } + + close(sock); + close(serverSock); + }); + + while (!accepting) { + std::this_thread::yield(); + } + + const int sock = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in addr = {}; + const socklen_t addrlen = sizeof(addr); + std::array<char, MAX_MESSAGE_LENGTH> message = {}; + + if (sock < 0) { + perror("connector socket"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(PORT); + if (connect(sock, reinterpret_cast<struct sockaddr*>(&addr), addrlen) < 0) { + perror("connect"); + return; + } + + while (!accepted) { + std::this_thread::yield(); + } + + size_t receivedBytes = 0; + while (receivedBytes < loadSize) { + const ssize_t recved = recv(sock, message.data(), recvLength, 0); + + if (recved < 0) { + perror("recv"); + return; + } + + receivedBytes += recved; + } + + close(sock); + t.join(); +} + +BENCHMARK(BM_Baseline_TCP_Throughput_100MB_s40B_r1024B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 40; + constexpr size_t receiveSizeB = 1024; + BM_Baseline_TCP_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_TCP_Throughput_100MB_s40B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 40; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_TCP_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_TCP_Throughput_100MB_s80B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 80; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_TCP_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} +BENCHMARK(BM_Baseline_TCP_Throughput_100MB_s4096B_r4096B, n) { + (void)n; + constexpr size_t loadSizeB = 100 * 1024 * 1024; + constexpr size_t sendSizeB = 4096; + constexpr size_t receiveSizeB = 4096; + BM_Baseline_TCP_SendReceive(loadSizeB, sendSizeB, receiveSizeB); +} + +BENCHMARK(BM_Baseline_TCP_Latency_1M_msgs_32B, n) { + (void)n; + constexpr size_t messageSizeB = 32; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_TCP_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} +BENCHMARK(BM_Baseline_TCP_Latency_1M_msgs_128B, n) { + (void)n; + constexpr size_t messageSizeB = 128; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_TCP_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} +BENCHMARK(BM_Baseline_TCP_Latency_1M_msgs_4kB, n) { + (void)n; + constexpr size_t messageSizeB = 4096; + constexpr size_t loadSizeB = 1000000 * messageSizeB; + BM_Baseline_TCP_SendReceive(loadSizeB, messageSizeB, messageSizeB); +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Benchmarks.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Benchmarks.cpp new file mode 100644 index 000000000..69a2abc91 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Benchmarks.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 <folly/Benchmark.h> +#include <folly/init/Init.h> + +int main(int argc, char** argv) { + folly::init(&argc, &argv); + + FLAGS_logtostderr = true; + + LOG(INFO) << "Running benchmarks... (takes minutes)"; + folly::runBenchmarks(); + + return 0; +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/CMakeLists.txt b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..4d0c4f51c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library(fixture Fixture.cpp Fixture.h) +target_link_libraries(fixture ReactiveSocket Folly::folly) + +function(benchmark NAME FILE) + add_executable(${NAME} ${FILE} Benchmarks.cpp) + target_link_libraries( + ${NAME} + fixture + ReactiveSocket + Folly::follybenchmark + glog::glog + gflags) +endfunction() + +benchmark(baselines_tcp BaselinesTcp.cpp) +benchmark(baselines_async_socket BaselinesAsyncSocket.cpp) + +benchmark(fire-forget-throughput-tcp FireForgetThroughputTcp.cpp) +benchmark(req-response-throughput-tcp RequestResponseThroughputTcp.cpp) +benchmark(stream-throughput-tcp StreamThroughputTcp.cpp) + +benchmark(stream-throughput-mem StreamThroughputMemory.cpp) + +add_test(NAME RequestResponseThroughputTcpTest COMMAND req-response-throughput-tcp --items 100000) +add_test(NAME StreamThroughputTcpTest COMMAND stream-throughput-tcp --items 100000) +add_test(NAME FireForgetThroughputTcpTest COMMAND fire-forget-throughput-tcp --items 100000) + +#TODO(lehecka):enable test +#add_test(NAME StreamThroughputMemoryTest COMMAND stream-throughput-mem --items 100000) diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/FireForgetThroughputTcp.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/FireForgetThroughputTcp.cpp new file mode 100644 index 000000000..03f5a29f6 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/FireForgetThroughputTcp.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/benchmarks/Fixture.h" +#include "rsocket/benchmarks/Latch.h" + +#include <folly/Benchmark.h> +#include <folly/portability/GFlags.h> + +#include "rsocket/RSocket.h" + +using namespace rsocket; + +DEFINE_int32(server_threads, 8, "number of server threads to run"); +DEFINE_int32( + override_client_threads, + 0, + "control the number of client threads (defaults to the number of clients)"); +DEFINE_int32(clients, 10, "number of clients to run"); +DEFINE_int32(items, 1000000, "number of items to fire-and-forget, in total"); + +namespace { + +class Responder : public RSocketResponder { + public: + Responder(Latch& latch) : latch_{latch} {} + + void handleFireAndForget(Payload, StreamId) override { + latch_.post(); + } + + private: + Latch& latch_; +}; +} // namespace + +BENCHMARK(FireForgetThroughput, n) { + (void)n; + + Latch latch{static_cast<size_t>(FLAGS_items)}; + + std::unique_ptr<Fixture> fixture; + Fixture::Options opts; + + BENCHMARK_SUSPEND { + auto responder = std::make_shared<Responder>(latch); + + opts.serverThreads = FLAGS_server_threads; + opts.clients = FLAGS_clients; + if (FLAGS_override_client_threads > 0) { + opts.clientThreads = FLAGS_override_client_threads; + } + + fixture = std::make_unique<Fixture>(opts, std::move(responder)); + + LOG(INFO) << "Running:"; + LOG(INFO) << " Server with " << opts.serverThreads << " threads."; + LOG(INFO) << " " << opts.clients << " clients across " + << fixture->workers.size() << " threads."; + LOG(INFO) << " Running " << FLAGS_items << " requests in total."; + } + + for (int i = 0; i < FLAGS_items; ++i) { + for (auto& client : fixture->clients) { + client->getRequester() + ->fireAndForget(Payload("TcpFireAndForget")) + ->subscribe( + std::make_shared<yarpl::single::SingleObserverBase<void>>()); + } + } + + constexpr std::chrono::minutes timeout{5}; + if (!latch.timed_wait(timeout)) { + LOG(ERROR) << "Timed out!"; + } +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.cpp new file mode 100644 index 000000000..2a42fd222 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.cpp @@ -0,0 +1,62 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/benchmarks/Fixture.h" + +#include "rsocket/RSocket.h" +#include "rsocket/transports/tcp/TcpConnectionAcceptor.h" +#include "rsocket/transports/tcp/TcpConnectionFactory.h" + +namespace rsocket { + +namespace { + +std::shared_ptr<RSocketClient> makeClient( + folly::EventBase* eventBase, + folly::SocketAddress address) { + auto factory = + std::make_unique<TcpConnectionFactory>(*eventBase, std::move(address)); + return RSocket::createConnectedClient(std::move(factory)).get(); +} +} // namespace + +Fixture::Fixture( + Fixture::Options fixtureOpts, + std::shared_ptr<RSocketResponder> responder) + : options{std::move(fixtureOpts)} { + TcpConnectionAcceptor::Options opts; + opts.address = folly::SocketAddress{"0.0.0.0", 0}; + opts.threads = options.serverThreads; + + auto acceptor = std::make_unique<TcpConnectionAcceptor>(std::move(opts)); + server = std::make_unique<RSocketServer>(std::move(acceptor)); + server->start([responder](const SetupParameters&) { return responder; }); + + auto const numWorkers = + options.clientThreads ? *options.clientThreads : options.clients; + for (size_t i = 0; i < numWorkers; ++i) { + workers.push_back(std::make_unique<folly::ScopedEventBaseThread>( + "rsocket-client-thread")); + } + + const folly::SocketAddress actual{"127.0.0.1", *server->listeningPort()}; + + for (size_t i = 0; i < options.clients; ++i) { + auto worker = std::move(workers.front()); + workers.pop_front(); + clients.push_back(makeClient(worker->getEventBase(), actual)); + workers.push_back(std::move(worker)); + } +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.h b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.h new file mode 100644 index 000000000..a1b290f7d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Fixture.h @@ -0,0 +1,54 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketClient.h" +#include "rsocket/RSocketServer.h" + +#include <folly/Optional.h> +#include <folly/io/async/ScopedEventBaseThread.h> + +#include <deque> +#include <vector> + +namespace rsocket { + +/// Benchmarks fixture object that contains a server, along with a list of +/// clients and their worker threads. +/// +/// Uses TCP as the transport. +struct Fixture { + struct Options { + /// Number of threads the server will run. + size_t serverThreads{8}; + + /// Number of clients to run. + size_t clients{8}; + + /// Number of worker threads driving the clients. A default value means to + /// use one thread per client. + folly::Optional<size_t> clientThreads; + }; + + Fixture(Options, std::shared_ptr<RSocketResponder>); + + // State is public, have at it. + + std::unique_ptr<RSocketServer> server; + std::deque<std::unique_ptr<folly::ScopedEventBaseThread>> workers; + std::vector<std::shared_ptr<RSocketClient>> clients; + const Options options; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Latch.h b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Latch.h new file mode 100644 index 000000000..fc5422169 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Latch.h @@ -0,0 +1,43 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/synchronization/Baton.h> + +/// Simple implementation of a latch synchronization primitive, for testing. +class Latch { + public: + explicit Latch(size_t limit) : limit_{limit} {} + + void wait() { + baton_.wait(); + } + + bool timed_wait(std::chrono::milliseconds timeout) { + return baton_.timed_wait(timeout); + } + + void post() { + auto const old = count_.fetch_add(1); + if (old == limit_ - 1) { + baton_.post(); + } + } + + private: + folly::Baton<> baton_; + std::atomic<size_t> count_{0}; + const size_t limit_{0}; +}; diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/README.md b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/README.md new file mode 100644 index 000000000..2b3019a2e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/README.md @@ -0,0 +1,8 @@ +# Benchmarks + +Various benchmarks. + +- `Baselines`: TCP loopback baseline throughput and latency. +- `StreamThroughput`: Single stream throughput measured for various message lengths and messages/second. +- `RequestResponseLatency`: Latency of a single request/response measured in latency and requests/second. +- `RequestResponseThroughput`: Throughput of number of request/responses per second for various max number of outstanding requests as a time. diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/RequestResponseThroughputTcp.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/RequestResponseThroughputTcp.cpp new file mode 100644 index 000000000..aace80fd2 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/RequestResponseThroughputTcp.cpp @@ -0,0 +1,106 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/benchmarks/Fixture.h" +#include "rsocket/benchmarks/Latch.h" +#include "rsocket/benchmarks/Throughput.h" + +#include <folly/Benchmark.h> +#include <folly/init/Init.h> +#include <folly/portability/GFlags.h> + +#include "rsocket/RSocket.h" +#include "yarpl/Single.h" + +using namespace rsocket; + +constexpr size_t kMessageLen = 32; + +DEFINE_int32(server_threads, 8, "number of server threads to run"); +DEFINE_int32( + override_client_threads, + 0, + "control the number of client threads (defaults to the number of clients)"); +DEFINE_int32(clients, 10, "number of clients to run"); +DEFINE_int32( + items, + 1000000, + "number of request-response requests to send, in total"); + +namespace { + +class Observer : public yarpl::single::SingleObserverBase<Payload> { + public: + explicit Observer(Latch& latch) : latch_{latch} {} + + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription> + subscription) override { + yarpl::single::SingleObserverBase<Payload>::onSubscribe( + std::move(subscription)); + } + + void onSuccess(Payload) override { + latch_.post(); + yarpl::single::SingleObserverBase<Payload>::onSuccess({}); + } + + void onError(folly::exception_wrapper) override { + latch_.post(); + yarpl::single::SingleObserverBase<Payload>::onError({}); + } + + private: + Latch& latch_; +}; +} // namespace + +BENCHMARK(RequestResponseThroughput, n) { + (void)n; + + Latch latch{static_cast<size_t>(FLAGS_items)}; + + std::unique_ptr<Fixture> fixture; + Fixture::Options opts; + + BENCHMARK_SUSPEND { + auto responder = + std::make_shared<FixedResponder>(std::string(kMessageLen, 'a')); + + opts.serverThreads = FLAGS_server_threads; + opts.clients = FLAGS_clients; + if (FLAGS_override_client_threads > 0) { + opts.clientThreads = FLAGS_override_client_threads; + } + + fixture = std::make_unique<Fixture>(opts, std::move(responder)); + + LOG(INFO) << "Running:"; + LOG(INFO) << " Server with " << opts.serverThreads << " threads."; + LOG(INFO) << " " << opts.clients << " clients across " + << fixture->workers.size() << " threads."; + LOG(INFO) << " Running " << FLAGS_items << " requests in total"; + } + + for (int i = 0; i < FLAGS_items; ++i) { + auto& client = fixture->clients[i % opts.clients]; + client->getRequester() + ->requestResponse(Payload("RequestResponseTcp")) + ->subscribe(std::make_shared<Observer>(latch)); + } + + constexpr std::chrono::minutes timeout{5}; + if (!latch.timed_wait(timeout)) { + LOG(ERROR) << "Timed out!"; + } +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputMemory.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputMemory.cpp new file mode 100644 index 000000000..dc47bf4e6 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputMemory.cpp @@ -0,0 +1,187 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/benchmarks/Throughput.h" + +#include <folly/Benchmark.h> +#include <folly/Synchronized.h> +#include <folly/init/Init.h> +#include <folly/io/async/ScopedEventBaseThread.h> +#include <folly/portability/GFlags.h> +#include <folly/synchronization/Baton.h> + +#include "rsocket/RSocket.h" +#include "yarpl/Flowable.h" + +using namespace rsocket; + +constexpr size_t kMessageLen = 32; + +DEFINE_int32(items, 1000000, "number of items in stream"); + +namespace { + +/// State shared across the client and server DirectDuplexConnections. +struct State { + /// Whether one of the two connections has been destroyed. + folly::Synchronized<bool> destroyed; +}; + +/// DuplexConnection that talks to another DuplexConnection via memory. +class DirectDuplexConnection : public DuplexConnection { + public: + DirectDuplexConnection(std::shared_ptr<State> state, folly::EventBase& evb) + : state_{std::move(state)}, evb_{evb} {} + + ~DirectDuplexConnection() override { + *state_->destroyed.wlock() = true; + } + + // Tie two DirectDuplexConnections together so they can talk to each other. + void tie(DirectDuplexConnection* other) { + other_ = other; + other_->other_ = this; + } + + void setInput(std::shared_ptr<DuplexConnection::Subscriber> input) override { + input_ = std::move(input); + } + + void send(std::unique_ptr<folly::IOBuf> buf) override { + auto destroyed = state_->destroyed.rlock(); + if (*destroyed || !other_) { + return; + } + + other_->evb_.runInEventBaseThread( + [state = state_, other = other_, b = std::move(buf)]() mutable { + auto destroyed = state->destroyed.rlock(); + if (*destroyed) { + return; + } + + other->input_->onNext(std::move(b)); + }); + } + + private: + std::shared_ptr<State> state_; + folly::EventBase& evb_; + + DirectDuplexConnection* other_{nullptr}; + + std::shared_ptr<DuplexConnection::Subscriber> input_; +}; + +class Acceptor : public ConnectionAcceptor { + public: + explicit Acceptor(std::shared_ptr<State> state) : state_{std::move(state)} {} + + void setClientConnection(DirectDuplexConnection* connection) { + client_ = connection; + } + + void start(OnDuplexConnectionAccept onAccept) override { + worker_.getEventBase()->runInEventBaseThread( + [this, onAccept = std::move(onAccept)]() mutable { + auto server = std::make_unique<DirectDuplexConnection>( + std::move(state_), *worker_.getEventBase()); + server->tie(client_); + onAccept(std::move(server), *worker_.getEventBase()); + }); + } + + void stop() override {} + + folly::Optional<uint16_t> listeningPort() const override { + return folly::none; + } + + private: + std::shared_ptr<State> state_; + + DirectDuplexConnection* client_{nullptr}; + + folly::ScopedEventBaseThread worker_; +}; + +class Factory : public ConnectionFactory { + public: + Factory() { + auto state = std::make_shared<State>(); + + connection_ = std::make_unique<DirectDuplexConnection>( + state, *worker_.getEventBase()); + + auto acceptor = std::make_unique<Acceptor>(state); + acceptor_ = acceptor.get(); + + acceptor_->setClientConnection(connection_.get()); + + auto responder = + std::make_shared<FixedResponder>(std::string(kMessageLen, 'a')); + + server_ = std::make_unique<RSocketServer>(std::move(acceptor)); + server_->start([responder](const SetupParameters&) { return responder; }); + } + + folly::Future<ConnectedDuplexConnection> connect( + ProtocolVersion, + ResumeStatus /* unused */) override { + return folly::via(worker_.getEventBase(), [this] { + return ConnectedDuplexConnection{std::move(connection_), + *worker_.getEventBase()}; + }); + } + + private: + std::unique_ptr<DirectDuplexConnection> connection_; + + std::unique_ptr<rsocket::RSocketServer> server_; + Acceptor* acceptor_{nullptr}; + + folly::ScopedEventBaseThread worker_; +}; + +std::shared_ptr<RSocketClient> makeClient() { + auto factory = std::make_unique<Factory>(); + return RSocket::createConnectedClient(std::move(factory)).get(); +} +} // namespace + +BENCHMARK(StreamThroughput, n) { + (void)n; + + std::shared_ptr<RSocketClient> client; + std::shared_ptr<BoundedSubscriber> subscriber; + + folly::ScopedEventBaseThread worker; + + Latch latch{1}; + + BENCHMARK_SUSPEND { + LOG(INFO) << " Running with " << FLAGS_items << " items"; + + client = makeClient(); + } + + client->getRequester() + ->requestStream(Payload("InMemoryStream")) + ->subscribe(std::make_shared<BoundedSubscriber>(latch, FLAGS_items)); + + constexpr std::chrono::minutes timeout{5}; + if (!latch.timed_wait(timeout)) { + LOG(ERROR) << "Timed out!"; + } +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputTcp.cpp b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputTcp.cpp new file mode 100644 index 000000000..4f9c5e343 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/StreamThroughputTcp.cpp @@ -0,0 +1,77 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/benchmarks/Fixture.h" +#include "rsocket/benchmarks/Throughput.h" + +#include <folly/Benchmark.h> +#include <folly/portability/GFlags.h> +#include <folly/synchronization/Baton.h> + +#include "rsocket/RSocket.h" + +using namespace rsocket; + +constexpr size_t kMessageLen = 32; + +DEFINE_int32(server_threads, 8, "number of server threads to run"); +DEFINE_int32( + override_client_threads, + 0, + "control the number of client threads (defaults to the number of clients)"); +DEFINE_int32(clients, 10, "number of clients to run"); +DEFINE_int32(items, 1000000, "number of items in stream, per client"); +DEFINE_int32(streams, 1, "number of streams, per client"); + +BENCHMARK(StreamThroughput, n) { + (void)n; + + Latch latch{static_cast<size_t>(FLAGS_streams)}; + + std::unique_ptr<Fixture> fixture; + Fixture::Options opts; + + BENCHMARK_SUSPEND { + auto responder = + std::make_shared<FixedResponder>(std::string(kMessageLen, 'a')); + + opts.serverThreads = FLAGS_server_threads; + opts.clients = FLAGS_clients; + if (FLAGS_override_client_threads > 0) { + opts.clientThreads = FLAGS_override_client_threads; + } + + fixture = std::make_unique<Fixture>(opts, std::move(responder)); + + LOG(INFO) << "Running:"; + LOG(INFO) << " Server with " << opts.serverThreads << " threads."; + LOG(INFO) << " " << opts.clients << " clients across " + << fixture->workers.size() << " threads."; + LOG(INFO) << " Running " << FLAGS_streams << " streams of " << FLAGS_items + << " items each."; + } + + for (size_t i = 0; i < FLAGS_streams; ++i) { + for (auto& client : fixture->clients) { + client->getRequester() + ->requestStream(Payload("TcpStream")) + ->subscribe(std::make_shared<BoundedSubscriber>(latch, FLAGS_items)); + } + } + + constexpr std::chrono::minutes timeout{5}; + if (!latch.timed_wait(timeout)) { + LOG(ERROR) << "Timed out!"; + } +} diff --git a/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Throughput.h b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Throughput.h new file mode 100644 index 000000000..c5c215e99 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/benchmarks/Throughput.h @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketResponder.h" +#include "rsocket/benchmarks/Latch.h" + +namespace rsocket { + +/// Responder that always sends back a fixed message. +class FixedResponder : public RSocketResponder { + public: + explicit FixedResponder(const std::string& message) + : message_{folly::IOBuf::copyBuffer(message)} {} + + /// Infinitely streams back the message. + std::shared_ptr<yarpl::flowable::Flowable<Payload>> handleRequestStream( + Payload, + StreamId) override { + return yarpl::flowable::Flowable<Payload>::fromGenerator( + [msg = message_->clone()] { return Payload(msg->clone()); }); + } + + std::shared_ptr<yarpl::single::Single<Payload>> handleRequestResponse( + Payload, + StreamId) override { + return yarpl::single::Singles::fromGenerator<Payload>( + [msg = message_->clone()] { return Payload(msg->clone()); }); + } + + private: + std::unique_ptr<folly::IOBuf> message_; +}; + +/// Subscriber that requests N items and cancels the subscription once all of +/// them arrive. Signals a latch when it terminates. +class BoundedSubscriber : public yarpl::flowable::BaseSubscriber<Payload> { + public: + BoundedSubscriber(Latch& latch, size_t requested) + : latch_{latch}, requested_{requested} {} + + void onSubscribeImpl() override { + this->request(requested_); + } + + void onNextImpl(Payload) override { + if (received_.fetch_add(1) == requested_ - 1) { + DCHECK(!terminated_.exchange(true)); + latch_.post(); + + // After this cancel we could be destroyed. + this->cancel(); + } + } + + void onCompleteImpl() override { + if (!terminated_.exchange(true)) { + latch_.post(); + } + } + + void onErrorImpl(folly::exception_wrapper) override { + if (!terminated_.exchange(true)) { + latch_.post(); + } + } + + private: + Latch& latch_; + + std::atomic_bool terminated_{false}; + size_t requested_{0}; + std::atomic<size_t> received_{0}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/JsonRequestHandler.h b/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/JsonRequestHandler.h new file mode 100644 index 000000000..2bc0f45ad --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/JsonRequestHandler.h @@ -0,0 +1,26 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/RSocket.h" + +class JsonRequestResponder : public rsocket::RSocketResponder { + public: + /// Handles a new inbound Stream requested by the other end. + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + handleRequestStream(rsocket::Payload request, rsocket::StreamId streamId) + override; +}; diff --git a/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/TextRequestHandler.h b/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/TextRequestHandler.h new file mode 100644 index 000000000..7098b516e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/examples/conditional-request-handling/TextRequestHandler.h @@ -0,0 +1,26 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/RSocket.h" + +class TextRequestResponder : public rsocket::RSocketResponder { + public: + /// Handles a new inbound Stream requested by the other end. + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + handleRequestStream(rsocket::Payload request, rsocket::StreamId streamId) + override; +}; diff --git a/ios/Pods/Flipper-RSocket/rsocket/examples/util/ExampleSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/examples/util/ExampleSubscriber.h new file mode 100644 index 000000000..24a1caa23 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/examples/util/ExampleSubscriber.h @@ -0,0 +1,54 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <condition_variable> +#include <mutex> +#include "rsocket/Payload.h" + +#include "yarpl/Flowable.h" +#include "yarpl/flowable/Subscriber.h" + +/** + * Subscriber that logs all events. + * Request 5 items to begin with, then 3 more after each receipt of 3. + */ +namespace rsocket_example { +class ExampleSubscriber : public yarpl::flowable::Subscriber<rsocket::Payload> { + public: + ~ExampleSubscriber(); + ExampleSubscriber(int initialRequest, int numToTake); + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription> + subscription) noexcept override; + void onNext(rsocket::Payload) noexcept override; + void onComplete() noexcept override; + void onError(folly::exception_wrapper ex) noexcept override; + + void awaitTerminalEvent(); + + private: + int initialRequest_; + int thresholdForRequest_; + int numToTake_; + int requested_; + int received_; + std::shared_ptr<yarpl::flowable::Subscription> subscription_; + bool terminated_{false}; + std::mutex m_; + std::condition_variable terminalEventCV_; +}; +} // namespace rsocket_example diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.cpp new file mode 100644 index 000000000..6ee11c348 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.cpp @@ -0,0 +1,46 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/ErrorCode.h" + +#include <ostream> + +namespace rsocket { + +std::ostream& operator<<(std::ostream& os, ErrorCode errorCode) { + switch (errorCode) { + case ErrorCode::RESERVED: + return os << "RESERVED"; + case ErrorCode::INVALID_SETUP: + return os << "INVALID_SETUP"; + case ErrorCode::UNSUPPORTED_SETUP: + return os << "UNSUPPORTED_SETUP"; + case ErrorCode::REJECTED_SETUP: + return os << "REJECTED_SETUP"; + case ErrorCode::REJECTED_RESUME: + return os << "REJECTED_RESUME"; + case ErrorCode::CONNECTION_ERROR: + return os << "CONNECTION_ERROR"; + case ErrorCode::APPLICATION_ERROR: + return os << "APPLICATION_ERROR"; + case ErrorCode::REJECTED: + return os << "REJECTED"; + case ErrorCode::CANCELED: + return os << "CANCELED"; + case ErrorCode::INVALID: + return os << "INVALID"; + } + return os << "ErrorCode[" << static_cast<uint32_t>(errorCode) << "]"; +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.h b/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.h new file mode 100644 index 000000000..93f741aaa --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ErrorCode.h @@ -0,0 +1,55 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cstdint> +#include <iosfwd> + +namespace rsocket { + +enum class ErrorCode : uint32_t { + RESERVED = 0x00000000, + // The Setup frame is invalid for the server (it could be that the client is + // too recent for the old server). Stream ID MUST be 0. + INVALID_SETUP = 0x00000001, + // Some (or all) of the parameters specified by the client are unsupported by + // the server. Stream ID MUST be 0. + UNSUPPORTED_SETUP = 0x00000002, + // The server rejected the setup, it can specify the reason in the payload. + // Stream ID MUST be 0. + REJECTED_SETUP = 0x00000003, + // The server rejected the resume, it can specify the reason in the payload. + // Stream ID MUST be 0. + REJECTED_RESUME = 0x00000004, + // The connection is being terminated. Stream ID MUST be 0. + CONNECTION_ERROR = 0x00000101, + // Application layer logic generating a Reactive Streams onError event. + // Stream ID MUST be non-0. + APPLICATION_ERROR = 0x00000201, + // Despite being a valid request, the Responder decided to reject it. The + // Responder guarantees that it didn't process the request. The reason for the + // rejection is explained in the metadata section. Stream ID MUST be non-0. + REJECTED = 0x00000202, + // The responder canceled the request but potentially have started processing + // it (almost identical to REJECTED but doesn't garantee that no side-effect + // have been started). Stream ID MUST be non-0. + CANCELED = 0x00000203, + // The request is invalid. Stream ID MUST be non-0. + INVALID = 0x00000204, + // EXT = 0xFFFFFFFF, +}; + +std::ostream& operator<<(std::ostream&, ErrorCode); +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.cpp new file mode 100644 index 000000000..9b3d8cc53 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.cpp @@ -0,0 +1,197 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/Frame.h" + +#include <folly/Memory.h> +#include <folly/io/Cursor.h> +#include <map> +#include <sstream> + +#include "rsocket/RSocketParameters.h" + +namespace rsocket { + +namespace detail { + +FrameFlags getFlags(const Payload& p) { + return p.metadata ? FrameFlags::METADATA : FrameFlags::EMPTY_; +} + +void checkFlags(const Payload& p, FrameFlags flags) { + if (bool(p.metadata) != bool(flags & FrameFlags::METADATA)) { + throw std::invalid_argument{ + "Value of METADATA flag doesn't match payload metadata"}; + } +} + +} // namespace detail + +constexpr uint32_t Frame_LEASE::kMaxTtl; +constexpr uint32_t Frame_LEASE::kMaxNumRequests; +constexpr uint32_t Frame_SETUP::kMaxKeepaliveTime; +constexpr uint32_t Frame_SETUP::kMaxLifetime; + +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_Base& frame) { + return os << frame.header_ << "(" << frame.requestN_ << ", " + << frame.payload_; +} + +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_N& frame) { + return os << frame.header_ << "(" << frame.requestN_ << ")"; +} + +std::ostream& operator<<( + std::ostream& os, + const Frame_REQUEST_RESPONSE& frame) { + return os << frame.header_ << ", " << frame.payload_; +} + +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_FNF& frame) { + return os << frame.header_ << ", " << frame.payload_; +} + +std::ostream& operator<<(std::ostream& os, const Frame_METADATA_PUSH& frame) { + return os << frame.header_ << ", " + << (frame.metadata_ ? frame.metadata_->computeChainDataLength() + : 0); +} + +std::ostream& operator<<(std::ostream& os, const Frame_CANCEL& frame) { + return os << frame.header_; +} + +Frame_PAYLOAD Frame_PAYLOAD::complete(StreamId streamId) { + return Frame_PAYLOAD(streamId, FrameFlags::COMPLETE, Payload()); +} + +std::ostream& operator<<(std::ostream& os, const Frame_PAYLOAD& frame) { + return os << frame.header_ << ", " << frame.payload_; +} + +Frame_ERROR Frame_ERROR::invalidSetup(folly::StringPiece message) { + return connectionErr(ErrorCode::INVALID_SETUP, message); +} + +Frame_ERROR Frame_ERROR::unsupportedSetup(folly::StringPiece message) { + return connectionErr(ErrorCode::UNSUPPORTED_SETUP, message); +} + +Frame_ERROR Frame_ERROR::rejectedSetup(folly::StringPiece message) { + return connectionErr(ErrorCode::REJECTED_SETUP, message); +} + +Frame_ERROR Frame_ERROR::rejectedResume(folly::StringPiece message) { + return connectionErr(ErrorCode::REJECTED_RESUME, message); +} + +Frame_ERROR Frame_ERROR::connectionError(folly::StringPiece message) { + return connectionErr(ErrorCode::CONNECTION_ERROR, message); +} + +Frame_ERROR Frame_ERROR::applicationError( + StreamId stream, + folly::StringPiece message) { + return streamErr(ErrorCode::APPLICATION_ERROR, message, stream); +} + +Frame_ERROR Frame_ERROR::applicationError(StreamId stream, Payload&& payload) { + if (stream == 0) { + throw std::invalid_argument{"Can't make stream error for stream zero"}; + } + return Frame_ERROR(stream, ErrorCode::APPLICATION_ERROR, std::move(payload)); +} + +Frame_ERROR Frame_ERROR::rejected(StreamId stream, folly::StringPiece message) { + return streamErr(ErrorCode::REJECTED, message, stream); +} + +Frame_ERROR Frame_ERROR::canceled(StreamId stream, folly::StringPiece message) { + return streamErr(ErrorCode::CANCELED, message, stream); +} + +Frame_ERROR Frame_ERROR::invalid(StreamId stream, folly::StringPiece message) { + return streamErr(ErrorCode::INVALID, message, stream); +} + +Frame_ERROR Frame_ERROR::connectionErr( + ErrorCode err, + folly::StringPiece message) { + return Frame_ERROR{0, err, Payload{message}}; +} + +Frame_ERROR Frame_ERROR::streamErr( + ErrorCode err, + folly::StringPiece message, + StreamId stream) { + if (stream == 0) { + throw std::invalid_argument{"Can't make stream error for stream zero"}; + } + return Frame_ERROR{stream, err, Payload{message}}; +} + +std::ostream& operator<<(std::ostream& os, const Frame_ERROR& frame) { + return os << frame.header_ << ", " << frame.errorCode_ << ", " + << frame.payload_; +} + +std::ostream& operator<<(std::ostream& os, const Frame_KEEPALIVE& frame) { + return os << frame.header_ << "(<" + << (frame.data_ ? frame.data_->computeChainDataLength() : 0) + << ">)"; +} + +std::ostream& operator<<(std::ostream& os, const Frame_SETUP& frame) { + return os << frame.header_ << ", Version: " << frame.versionMajor_ << "." + << frame.versionMinor_ << ", " + << "Token: " << frame.token_ << ", " << frame.payload_; +} + +void Frame_SETUP::moveToSetupPayload(SetupParameters& setupPayload) { + setupPayload.metadataMimeType = std::move(metadataMimeType_); + setupPayload.dataMimeType = std::move(dataMimeType_); + setupPayload.payload = std::move(payload_); + setupPayload.token = std::move(token_); + setupPayload.resumable = !!(header_.flags & FrameFlags::RESUME_ENABLE); + setupPayload.protocolVersion = ProtocolVersion(versionMajor_, versionMinor_); +} + +std::ostream& operator<<(std::ostream& os, const Frame_LEASE& frame) { + return os << frame.header_ << ", (" + << (frame.metadata_ ? frame.metadata_->computeChainDataLength() : 0) + << ")"; +} + +std::ostream& operator<<(std::ostream& os, const Frame_RESUME& frame) { + return os << frame.header_ << ", (" + << "token " << frame.token_ << ", @server " + << frame.lastReceivedServerPosition_ << ", @client " + << frame.clientPosition_ << ")"; +} + +std::ostream& operator<<(std::ostream& os, const Frame_RESUME_OK& frame) { + return os << frame.header_ << ", (@" << frame.position_ << ")"; +} + +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_CHANNEL& frame) { + return os << frame.header_ << ", initialRequestN=" << frame.requestN_ << ", " + << frame.payload_; +} + +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_STREAM& frame) { + return os << frame.header_ << ", initialRequestN=" << frame.requestN_ << ", " + << frame.payload_; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.h b/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.h new file mode 100644 index 000000000..8de331f1a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/Frame.h @@ -0,0 +1,439 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <array> +#include <iosfwd> +#include <limits> + +#include <folly/io/IOBuf.h> +#include <folly/io/IOBufQueue.h> + +#include "rsocket/Payload.h" +#include "rsocket/framing/ErrorCode.h" +#include "rsocket/framing/FrameFlags.h" +#include "rsocket/framing/FrameHeader.h" +#include "rsocket/framing/FrameType.h" +#include "rsocket/framing/ProtocolVersion.h" +#include "rsocket/framing/ResumeIdentificationToken.h" + +namespace folly { +template <typename V> +class Optional; +namespace io { +class Cursor; +class QueueAppender; +} // namespace io +} // namespace folly + +namespace rsocket { + +namespace detail { + +FrameFlags getFlags(const Payload&); + +void checkFlags(const Payload&, FrameFlags); + +} // namespace detail + +using ResumePosition = int64_t; +constexpr ResumePosition kUnspecifiedResumePosition = -1; + +/// Frames do not form hierarchy, as we never perform type erasure on a frame. +/// We use inheritance only to save code duplication. +/// +/// Since frames are only meaningful for stream automata on both ends of a +/// stream, intermediate layers that are frame-type-agnostic pass around +/// serialized frame. + +class Frame_REQUEST_N { + public: + /* + * Maximum value for ReactiveSocket Subscription::request. + * Value is a signed int, however negative values are not allowed. + * + * n.b. this is less than size_t because of the Frame encoding restrictions. + */ + static constexpr int64_t kMaxRequestN = std::numeric_limits<int32_t>::max(); + + Frame_REQUEST_N() = default; + Frame_REQUEST_N(StreamId streamId, uint32_t requestN) + : header_(FrameType::REQUEST_N, FrameFlags::EMPTY_, streamId), + requestN_(requestN) { + DCHECK(requestN_ > 0); + DCHECK(requestN_ <= kMaxRequestN); + } + + FrameHeader header_; + uint32_t requestN_{}; +}; +std::ostream& operator<<(std::ostream&, const Frame_REQUEST_N&); + +class Frame_REQUEST_Base { + public: + Frame_REQUEST_Base() = default; + Frame_REQUEST_Base( + FrameType frameType, + StreamId streamId, + FrameFlags flags, + uint32_t requestN, + Payload payload) + : header_(frameType, flags | detail::getFlags(payload), streamId), + requestN_(requestN), + payload_(std::move(payload)) { + detail::checkFlags(payload_, header_.flags); + // TODO: DCHECK(requestN_ > 0); + DCHECK(requestN_ <= Frame_REQUEST_N::kMaxRequestN); + } + + /// For compatibility with other data-carrying frames. + Frame_REQUEST_Base( + FrameType frameType, + StreamId streamId, + FrameFlags flags, + Payload payload) + : Frame_REQUEST_Base(frameType, streamId, flags, 0, std::move(payload)) {} + + FrameHeader header_; + uint32_t requestN_{}; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_REQUEST_Base&); + +class Frame_REQUEST_STREAM : public Frame_REQUEST_Base { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::METADATA | FrameFlags::FOLLOWS; + + Frame_REQUEST_STREAM() = default; + Frame_REQUEST_STREAM( + StreamId streamId, + FrameFlags flags, + uint32_t requestN, + Payload payload) + : Frame_REQUEST_Base( + FrameType::REQUEST_STREAM, + streamId, + flags, + requestN, + std::move(payload)) {} + + /// For compatibility with other data-carrying frames. + Frame_REQUEST_STREAM(StreamId streamId, FrameFlags flags, Payload payload) + : Frame_REQUEST_STREAM( + streamId, + flags & AllowedFlags, + 0, + std::move(payload)) {} +}; +std::ostream& operator<<(std::ostream& os, const Frame_REQUEST_STREAM& frame); + +class Frame_REQUEST_CHANNEL : public Frame_REQUEST_Base { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::METADATA | FrameFlags::FOLLOWS | FrameFlags::COMPLETE; + + Frame_REQUEST_CHANNEL() = default; + Frame_REQUEST_CHANNEL( + StreamId streamId, + FrameFlags flags, + uint32_t requestN, + Payload payload) + : Frame_REQUEST_Base( + FrameType::REQUEST_CHANNEL, + streamId, + flags, + requestN, + std::move(payload)) {} + + /// For compatibility with other data-carrying frames. + Frame_REQUEST_CHANNEL(StreamId streamId, FrameFlags flags, Payload payload) + : Frame_REQUEST_CHANNEL( + streamId, + flags & AllowedFlags, + 0, + std::move(payload)) {} +}; +std::ostream& operator<<(std::ostream&, const Frame_REQUEST_CHANNEL&); + +class Frame_REQUEST_RESPONSE { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::METADATA | FrameFlags::FOLLOWS; + + Frame_REQUEST_RESPONSE() = default; + Frame_REQUEST_RESPONSE(StreamId streamId, FrameFlags flags, Payload payload) + : header_( + FrameType::REQUEST_RESPONSE, + (flags & AllowedFlags) | detail::getFlags(payload), + streamId), + payload_(std::move(payload)) { + detail::checkFlags(payload_, header_.flags); + } + + FrameHeader header_; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_REQUEST_RESPONSE&); + +class Frame_REQUEST_FNF { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::METADATA | FrameFlags::FOLLOWS; + + Frame_REQUEST_FNF() = default; + Frame_REQUEST_FNF(StreamId streamId, FrameFlags flags, Payload payload) + : header_( + FrameType::REQUEST_FNF, + (flags & AllowedFlags) | detail::getFlags(payload), + streamId), + payload_(std::move(payload)) { + detail::checkFlags(payload_, header_.flags); + } + + FrameHeader header_; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_REQUEST_FNF&); + +class Frame_METADATA_PUSH { + public: + Frame_METADATA_PUSH() {} + explicit Frame_METADATA_PUSH(std::unique_ptr<folly::IOBuf> metadata) + : header_(FrameType::METADATA_PUSH, FrameFlags::METADATA, 0), + metadata_(std::move(metadata)) { + CHECK(metadata_); + } + + FrameHeader header_; + std::unique_ptr<folly::IOBuf> metadata_; +}; +std::ostream& operator<<(std::ostream&, const Frame_METADATA_PUSH&); + +class Frame_CANCEL { + public: + Frame_CANCEL() = default; + explicit Frame_CANCEL(StreamId streamId) + : header_(FrameType::CANCEL, FrameFlags::EMPTY_, streamId) {} + + FrameHeader header_; +}; +std::ostream& operator<<(std::ostream&, const Frame_CANCEL&); + +class Frame_PAYLOAD { + public: + constexpr static const FrameFlags AllowedFlags = FrameFlags::METADATA | + FrameFlags::FOLLOWS | FrameFlags::COMPLETE | FrameFlags::NEXT; + + Frame_PAYLOAD() = default; + Frame_PAYLOAD(StreamId streamId, FrameFlags flags, Payload payload) + : header_( + FrameType::PAYLOAD, + (flags & AllowedFlags) | detail::getFlags(payload), + streamId), + payload_(std::move(payload)) { + detail::checkFlags(payload_, header_.flags); + } + + static Frame_PAYLOAD complete(StreamId streamId); + + FrameHeader header_; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_PAYLOAD&); + +class Frame_ERROR { + public: + constexpr static const FrameFlags AllowedFlags = FrameFlags::METADATA; + + Frame_ERROR() = default; + Frame_ERROR(StreamId streamId, ErrorCode errorCode, Payload payload) + : header_(FrameType::ERROR, detail::getFlags(payload), streamId), + errorCode_(errorCode), + payload_(std::move(payload)) {} + + // Connection errors. + static Frame_ERROR invalidSetup(folly::StringPiece); + static Frame_ERROR unsupportedSetup(folly::StringPiece); + static Frame_ERROR rejectedSetup(folly::StringPiece); + static Frame_ERROR rejectedResume(folly::StringPiece); + static Frame_ERROR connectionError(folly::StringPiece); + + // Stream errors. + static Frame_ERROR applicationError(StreamId, folly::StringPiece); + static Frame_ERROR applicationError(StreamId, Payload&&); + static Frame_ERROR rejected(StreamId, folly::StringPiece); + static Frame_ERROR canceled(StreamId, folly::StringPiece); + static Frame_ERROR invalid(StreamId, folly::StringPiece); + + private: + static Frame_ERROR connectionErr(ErrorCode, folly::StringPiece); + static Frame_ERROR streamErr(ErrorCode, folly::StringPiece, StreamId); + + public: + FrameHeader header_; + ErrorCode errorCode_{}; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_ERROR&); + +class Frame_KEEPALIVE { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::KEEPALIVE_RESPOND; + + Frame_KEEPALIVE() = default; + Frame_KEEPALIVE( + FrameFlags flags, + ResumePosition position, + std::unique_ptr<folly::IOBuf> data) + : header_(FrameType::KEEPALIVE, flags & AllowedFlags, 0), + position_(position), + data_(std::move(data)) {} + + FrameHeader header_; + ResumePosition position_{}; + std::unique_ptr<folly::IOBuf> data_; +}; +std::ostream& operator<<(std::ostream&, const Frame_KEEPALIVE&); + +class SetupParameters; + +class Frame_SETUP { + public: + constexpr static const FrameFlags AllowedFlags = + FrameFlags::METADATA | FrameFlags::RESUME_ENABLE | FrameFlags::LEASE; + + constexpr static const uint32_t kMaxKeepaliveTime = + std::numeric_limits<int32_t>::max(); + constexpr static const uint32_t kMaxLifetime = + std::numeric_limits<int32_t>::max(); + + Frame_SETUP() = default; + Frame_SETUP( + FrameFlags flags, + uint16_t versionMajor, + uint16_t versionMinor, + uint32_t keepaliveTime, + uint32_t maxLifetime, + const ResumeIdentificationToken& token, + std::string metadataMimeType, + std::string dataMimeType, + Payload payload) + : header_( + FrameType::SETUP, + (flags & AllowedFlags) | detail::getFlags(payload), + 0), + versionMajor_(versionMajor), + versionMinor_(versionMinor), + keepaliveTime_(keepaliveTime), + maxLifetime_(maxLifetime), + token_(token), + metadataMimeType_(metadataMimeType), + dataMimeType_(dataMimeType), + payload_(std::move(payload)) { + detail::checkFlags(payload_, header_.flags); + DCHECK(keepaliveTime_ > 0); + DCHECK(maxLifetime_ > 0); + DCHECK(keepaliveTime_ <= kMaxKeepaliveTime); + DCHECK(maxLifetime_ <= kMaxLifetime); + } + + void moveToSetupPayload(SetupParameters& setupPayload); + + FrameHeader header_; + uint16_t versionMajor_{}; + uint16_t versionMinor_{}; + uint32_t keepaliveTime_{}; + uint32_t maxLifetime_{}; + ResumeIdentificationToken token_; + std::string metadataMimeType_; + std::string dataMimeType_; + Payload payload_; +}; +std::ostream& operator<<(std::ostream&, const Frame_SETUP&); +/// @} + +class Frame_LEASE { + public: + constexpr static const FrameFlags AllowedFlags = FrameFlags::METADATA; + constexpr static const uint32_t kMaxTtl = std::numeric_limits<int32_t>::max(); + constexpr static const uint32_t kMaxNumRequests = + std::numeric_limits<int32_t>::max(); + + Frame_LEASE() = default; + Frame_LEASE( + uint32_t ttl, + uint32_t numberOfRequests, + std::unique_ptr<folly::IOBuf> metadata = std::unique_ptr<folly::IOBuf>()) + : header_( + FrameType::LEASE, + metadata ? FrameFlags::METADATA : FrameFlags::EMPTY_, + 0), + ttl_(ttl), + numberOfRequests_(numberOfRequests), + metadata_(std::move(metadata)) { + DCHECK(ttl_ > 0); + DCHECK(numberOfRequests_ > 0); + DCHECK(ttl_ <= kMaxTtl); + DCHECK(numberOfRequests_ <= kMaxNumRequests); + } + + FrameHeader header_; + uint32_t ttl_{}; + uint32_t numberOfRequests_{}; + std::unique_ptr<folly::IOBuf> metadata_; +}; +std::ostream& operator<<(std::ostream&, const Frame_LEASE&); +/// @} + +class Frame_RESUME { + public: + Frame_RESUME() = default; + Frame_RESUME( + const ResumeIdentificationToken& token, + ResumePosition lastReceivedServerPosition, + ResumePosition clientPosition, + ProtocolVersion protocolVersion) + : header_(FrameType::RESUME, FrameFlags::EMPTY_, 0), + versionMajor_(protocolVersion.major), + versionMinor_(protocolVersion.minor), + token_(token), + lastReceivedServerPosition_(lastReceivedServerPosition), + clientPosition_(clientPosition) {} + + FrameHeader header_; + uint16_t versionMajor_{}; + uint16_t versionMinor_{}; + ResumeIdentificationToken token_; + ResumePosition lastReceivedServerPosition_{}; + ResumePosition clientPosition_{}; +}; +std::ostream& operator<<(std::ostream&, const Frame_RESUME&); +/// @} + +class Frame_RESUME_OK { + public: + Frame_RESUME_OK() = default; + explicit Frame_RESUME_OK(ResumePosition position) + : header_(FrameType::RESUME_OK, FrameFlags::EMPTY_, 0), + position_(position) {} + + FrameHeader header_; + ResumePosition position_{}; +}; +std::ostream& operator<<(std::ostream&, const Frame_RESUME_OK&); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.cpp new file mode 100644 index 000000000..d95399aa1 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.cpp @@ -0,0 +1,25 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameFlags.h" + +#include <bitset> +#include <ostream> + +namespace rsocket { + +std::ostream& operator<<(std::ostream& os, FrameFlags flags) { + return os << std::bitset<16>{raw(flags)}; +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.h new file mode 100644 index 000000000..7ab7eacf7 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameFlags.h @@ -0,0 +1,75 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cstdint> +#include <iosfwd> + +namespace rsocket { +enum class FrameFlags : uint16_t { + // Note that win32 defines EMPTY and IGNORE so we use a trailing + // underscore to avoid a collision + EMPTY_ = 0x000, + IGNORE_ = 0x200, + METADATA = 0x100, + + // SETUP. + RESUME_ENABLE = 0x80, + LEASE = 0x40, + + // KEEPALIVE + KEEPALIVE_RESPOND = 0x80, + + // REQUEST_RESPONSE, REQUEST_FNF, REQUEST_STREAM, REQUEST_CHANNEL, PAYLOAD. + FOLLOWS = 0x80, + + // REQUEST_CHANNEL, PAYLOAD. + COMPLETE = 0x40, + + // PAYLOAD. + NEXT = 0x20, +}; + +constexpr uint16_t raw(FrameFlags flags) { + return static_cast<uint16_t>(flags); +} + +constexpr FrameFlags operator|(FrameFlags a, FrameFlags b) { + return static_cast<FrameFlags>(raw(a) | raw(b)); +} + +constexpr FrameFlags operator&(FrameFlags a, FrameFlags b) { + return static_cast<FrameFlags>(raw(a) & raw(b)); +} + +inline FrameFlags& operator|=(FrameFlags& a, FrameFlags b) { + return a = (a | b); +} + +inline FrameFlags& operator&=(FrameFlags& a, FrameFlags b) { + return a = (a & b); +} + +constexpr bool operator!(FrameFlags a) { + return !raw(a); +} + +constexpr FrameFlags operator~(FrameFlags a) { + return static_cast<FrameFlags>(~raw(a)); +} + +std::ostream& operator<<(std::ostream& ostr, FrameFlags a); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.cpp new file mode 100644 index 000000000..3ee16dfca --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.cpp @@ -0,0 +1,106 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameHeader.h" + +#include <map> +#include <ostream> +#include <vector> + +namespace rsocket { + +namespace { + +using FlagString = std::pair<FrameFlags, const char*>; + +constexpr std::array<FlagString, 1> kMetadata = { + {std::make_pair(FrameFlags::METADATA, "METADATA")}}; +constexpr std::array<FlagString, 1> kKeepaliveRespond = { + {std::make_pair(FrameFlags::KEEPALIVE_RESPOND, "KEEPALIVE_RESPOND")}}; +constexpr std::array<FlagString, 2> kMetadataFollows = { + {std::make_pair(FrameFlags::METADATA, "METADATA"), + std::make_pair(FrameFlags::FOLLOWS, "FOLLOWS")}}; +constexpr std::array<FlagString, 3> kMetadataResumeEnableLease = { + {std::make_pair(FrameFlags::METADATA, "METADATA"), + std::make_pair(FrameFlags::RESUME_ENABLE, "RESUME_ENABLE"), + std::make_pair(FrameFlags::LEASE, "LEASE")}}; +constexpr std::array<FlagString, 3> kMetadataFollowsComplete = { + {std::make_pair(FrameFlags::METADATA, "METADATA"), + std::make_pair(FrameFlags::FOLLOWS, "FOLLOWS"), + std::make_pair(FrameFlags::COMPLETE, "COMPLETE")}}; +constexpr std::array<FlagString, 4> kMetadataFollowsCompleteNext = { + {std::make_pair(FrameFlags::METADATA, "METADATA"), + std::make_pair(FrameFlags::FOLLOWS, "FOLLOWS"), + std::make_pair(FrameFlags::COMPLETE, "COMPLETE"), + std::make_pair(FrameFlags::NEXT, "NEXT")}}; + +template <size_t N> +constexpr auto toRange(const std::array<FlagString, N>& arr) { + return folly::Range<const FlagString*>{arr.data(), arr.size()}; +} + +// constexpr -- Old versions of C++ compiler doesn't support +// compound-statements in constexpr function (no switch statement) +folly::Range<const FlagString*> allowedFlags(FrameType type) { + switch (type) { + case FrameType::SETUP: + return toRange(kMetadataResumeEnableLease); + case FrameType::LEASE: + case FrameType::ERROR: + return toRange(kMetadata); + case FrameType::KEEPALIVE: + return toRange(kKeepaliveRespond); + case FrameType::REQUEST_RESPONSE: + case FrameType::REQUEST_FNF: + case FrameType::REQUEST_STREAM: + return toRange(kMetadataFollows); + case FrameType::REQUEST_CHANNEL: + return toRange(kMetadataFollowsComplete); + case FrameType::PAYLOAD: + return toRange(kMetadataFollowsCompleteNext); + default: + return {}; + } +} + +std::ostream& +writeFlags(std::ostream& os, FrameFlags frameFlags, FrameType frameType) { + FrameFlags foundFlags = FrameFlags::EMPTY_; + + std::string delimiter; + for (const auto& pair : allowedFlags(frameType)) { + if (!!(frameFlags & pair.first)) { + os << delimiter << pair.second; + delimiter = "|"; + foundFlags |= pair.first; + } + } + + if (foundFlags != frameFlags) { + os << frameFlags; + } else if (delimiter.empty()) { + os << "0x00"; + } + return os; +} + +} // namespace + +std::ostream& operator<<(std::ostream& os, const FrameHeader& header) { + os << header.type << "["; + return writeFlags(os, header.flags, header.type) + << ", " << header.streamId << "]"; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.h new file mode 100644 index 000000000..cb67c895b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameHeader.h @@ -0,0 +1,52 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <iosfwd> + +#include "rsocket/framing/FrameFlags.h" +#include "rsocket/framing/FrameType.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +/// Header that begins every RSocket frame. +class FrameHeader { + public: + FrameHeader() {} + + FrameHeader(FrameType ty, FrameFlags fflags, StreamId stream) + : type{ty}, flags{fflags}, streamId{stream} {} + + bool flagsComplete() const { + return !!(flags & FrameFlags::COMPLETE); + } + + bool flagsNext() const { + return !!(flags & FrameFlags::NEXT); + } + + bool flagsFollows() const { + return !!(flags & FrameFlags::FOLLOWS); + } + + FrameType type{FrameType::RESERVED}; + FrameFlags flags{FrameFlags::EMPTY_}; + StreamId streamId{0}; +}; + +std::ostream& operator<<(std::ostream&, const FrameHeader&); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameProcessor.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameProcessor.h new file mode 100644 index 000000000..70c5eae3e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameProcessor.h @@ -0,0 +1,30 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <folly/io/IOBuf.h> + +namespace rsocket { + +class FrameProcessor { + public: + virtual ~FrameProcessor() = default; + + virtual void processFrame(std::unique_ptr<folly::IOBuf>) = 0; + virtual void onTerminal(folly::exception_wrapper) = 0; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.cpp new file mode 100644 index 000000000..92904944b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameSerializer.h" +#include "rsocket/framing/FrameSerializer_v1_0.h" + +namespace rsocket { + +std::unique_ptr<FrameSerializer> FrameSerializer::createFrameSerializer( + const ProtocolVersion& protocolVersion) { + if (protocolVersion == FrameSerializerV1_0::Version) { + return std::make_unique<FrameSerializerV1_0>(); + } + + DCHECK(protocolVersion == ProtocolVersion::Unknown); + LOG_IF(ERROR, protocolVersion != ProtocolVersion::Unknown) + << "unknown protocol version " << protocolVersion; + return nullptr; +} + +std::unique_ptr<FrameSerializer> FrameSerializer::createAutodetectedSerializer( + const folly::IOBuf& firstFrame) { + auto detectedVersion = FrameSerializerV1_0::detectProtocolVersion(firstFrame); + return createFrameSerializer(detectedVersion); +} + +bool& FrameSerializer::preallocateFrameSizeField() { + return preallocateFrameSizeField_; +} + +folly::IOBufQueue FrameSerializer::createBufferQueue(size_t bufferSize) const { + const auto prependSize = + preallocateFrameSizeField_ ? frameLengthFieldSize() : 0; + auto buf = folly::IOBuf::createCombined(bufferSize + prependSize); + buf->advance(prependSize); + folly::IOBufQueue queue(folly::IOBufQueue::cacheChainLength()); + queue.append(std::move(buf)); + return queue; +} + +folly::Optional<StreamId> FrameSerializer::peekStreamId( + const ProtocolVersion& protocolVersion, + const folly::IOBuf& frame, + bool skipFrameLengthBytes) { + if (protocolVersion == FrameSerializerV1_0::Version) { + return FrameSerializerV1_0().peekStreamId(frame, skipFrameLengthBytes); + } + + auto* msg = "unknown protocol version"; + DCHECK(false) << msg; + return folly::none; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.h new file mode 100644 index 000000000..7ee0bafae --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer.h @@ -0,0 +1,115 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Optional.h> + +#include <memory> + +#include "rsocket/framing/Frame.h" + +namespace rsocket { + +// interface separating serialization/deserialization of ReactiveSocket frames +class FrameSerializer { + public: + virtual ~FrameSerializer() = default; + + virtual ProtocolVersion protocolVersion() const = 0; + + static std::unique_ptr<FrameSerializer> createFrameSerializer( + const ProtocolVersion& protocolVersion); + + static std::unique_ptr<FrameSerializer> createAutodetectedSerializer( + const folly::IOBuf& firstFrame); + + static folly::Optional<StreamId> peekStreamId( + const ProtocolVersion& protocolVersion, + const folly::IOBuf& frame, + bool skipFrameLengthBytes); + + virtual FrameType peekFrameType(const folly::IOBuf& in) const = 0; + virtual folly::Optional<StreamId> peekStreamId( + const folly::IOBuf& in, + bool skipFrameLengthBytes) const = 0; + + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_STREAM&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_CHANNEL&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_RESPONSE&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_FNF&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_N&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_METADATA_PUSH&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_CANCEL&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_PAYLOAD&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_ERROR&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_KEEPALIVE&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_SETUP&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_LEASE&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut(Frame_RESUME&&) const = 0; + virtual std::unique_ptr<folly::IOBuf> serializeOut( + Frame_RESUME_OK&&) const = 0; + + virtual bool deserializeFrom( + Frame_REQUEST_STREAM&, + std::unique_ptr<folly::IOBuf>) const = 0; + virtual bool deserializeFrom( + Frame_REQUEST_CHANNEL&, + std::unique_ptr<folly::IOBuf>) const = 0; + virtual bool deserializeFrom( + Frame_REQUEST_RESPONSE&, + std::unique_ptr<folly::IOBuf>) const = 0; + virtual bool deserializeFrom( + Frame_REQUEST_FNF&, + std::unique_ptr<folly::IOBuf>) const = 0; + virtual bool deserializeFrom(Frame_REQUEST_N&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom( + Frame_METADATA_PUSH&, + std::unique_ptr<folly::IOBuf>) const = 0; + virtual bool deserializeFrom(Frame_CANCEL&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_PAYLOAD&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_ERROR&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_KEEPALIVE&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_SETUP&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_LEASE&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_RESUME&, std::unique_ptr<folly::IOBuf>) + const = 0; + virtual bool deserializeFrom(Frame_RESUME_OK&, std::unique_ptr<folly::IOBuf>) + const = 0; + + virtual size_t frameLengthFieldSize() const = 0; + bool& preallocateFrameSizeField(); + + protected: + folly::IOBufQueue createBufferQueue(size_t bufferSize) const; + + private: + bool preallocateFrameSizeField_{false}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.cpp new file mode 100644 index 000000000..446246d11 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.cpp @@ -0,0 +1,690 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameSerializer_v1_0.h" + +#include <folly/io/Cursor.h> + +namespace rsocket { + +constexpr const ProtocolVersion FrameSerializerV1_0::Version; +constexpr const size_t FrameSerializerV1_0::kFrameHeaderSize; +constexpr const size_t FrameSerializerV1_0::kMinBytesNeededForAutodetection; + +namespace { +constexpr const uint32_t kMedatadaLengthSize = 3u; // bytes +constexpr const uint32_t kMaxMetadataLength = 0xFFFFFFu; // 24bit max value +} // namespace + +ProtocolVersion FrameSerializerV1_0::protocolVersion() const { + return Version; +} + +static FrameType deserializeFrameType(uint16_t frameType) { + if (frameType > static_cast<uint8_t>(FrameType::RESUME_OK) && + frameType != static_cast<uint8_t>(FrameType::EXT)) { + return FrameType::RESERVED; + } + return static_cast<FrameType>(frameType); +} + +static void serializeHeaderInto( + folly::io::QueueAppender& appender, + const FrameHeader& header) { + appender.writeBE<int32_t>(static_cast<int32_t>(header.streamId)); + + auto type = static_cast<uint8_t>(header.type); // 6 bit + auto flags = static_cast<uint16_t>(header.flags); // 10 bit + appender.write(static_cast<uint8_t>((type << 2) | (flags >> 8))); + appender.write(static_cast<uint8_t>(flags)); // lower 8 bits +} + +static void deserializeHeaderFrom(folly::io::Cursor& cur, FrameHeader& header) { + auto streamId = cur.readBE<int32_t>(); + if (streamId < 0) { + throw std::runtime_error("invalid stream id"); + } + header.streamId = static_cast<StreamId>(streamId); + uint16_t type = cur.readBE<uint8_t>(); // |Frame Type |I|M| + header.type = deserializeFrameType(type >> 2); + header.flags = + static_cast<FrameFlags>(((type & 0x3) << 8) | cur.readBE<uint8_t>()); +} + +static void serializeMetadataInto( + folly::io::QueueAppender& appender, + std::unique_ptr<folly::IOBuf> metadata) { + if (metadata == nullptr) { + return; + } + + // metadata length field not included in the medatadata length + uint32_t metadataLength = + static_cast<uint32_t>(metadata->computeChainDataLength()); + CHECK_LT(metadataLength, kMaxMetadataLength) + << "Metadata is too big to serialize"; + + appender.write(static_cast<uint8_t>(metadataLength >> 16)); // first byte + appender.write( + static_cast<uint8_t>((metadataLength >> 8) & 0xFF)); // second byte + appender.write(static_cast<uint8_t>(metadataLength & 0xFF)); // third byte + + appender.insert(std::move(metadata)); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::deserializeMetadataFrom( + folly::io::Cursor& cur, + FrameFlags flags) { + if (!(flags & FrameFlags::METADATA)) { + return nullptr; + } + + uint32_t metadataLength = 0; + metadataLength |= static_cast<uint32_t>(cur.read<uint8_t>() << 16); + metadataLength |= static_cast<uint32_t>(cur.read<uint8_t>() << 8); + metadataLength |= cur.read<uint8_t>(); + + CHECK_LE(metadataLength, kMaxMetadataLength) + << "Read out the 24-bit integer incorrectly somehow"; + + std::unique_ptr<folly::IOBuf> metadata; + cur.clone(metadata, metadataLength); + return metadata; +} + +static std::unique_ptr<folly::IOBuf> deserializeDataFrom( + folly::io::Cursor& cur) { + std::unique_ptr<folly::IOBuf> data; + auto totalLength = cur.totalLength(); + + if (totalLength > 0) { + cur.clone(data, totalLength); + } + return data; +} + +static Payload deserializePayloadFrom( + folly::io::Cursor& cur, + FrameFlags flags) { + auto metadata = FrameSerializerV1_0::deserializeMetadataFrom(cur, flags); + auto data = deserializeDataFrom(cur); + return Payload(std::move(data), std::move(metadata)); +} + +static void serializePayloadInto( + folly::io::QueueAppender& appender, + Payload&& payload) { + serializeMetadataInto(appender, std::move(payload.metadata)); + if (payload.data) { + appender.insert(std::move(payload.data)); + } +} + +static uint32_t payloadFramingSize(const Payload& payload) { + return (payload.metadata != nullptr ? kMedatadaLengthSize : 0); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOutInternal( + Frame_REQUEST_Base&& frame) const { + auto queue = createBufferQueue( + FrameSerializerV1_0::kFrameHeaderSize + sizeof(uint32_t) + + payloadFramingSize(frame.payload_)); + + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + + appender.writeBE<int32_t>(static_cast<int32_t>(frame.requestN_)); + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +static bool deserializeFromInternal( + Frame_REQUEST_Base& frame, + std::unique_ptr<folly::IOBuf> in) { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + + auto requestN = cur.readBE<int32_t>(); + // TODO(lehecka): requestN <= 0 + if (requestN < 0) { + throw std::runtime_error("invalid request N"); + } + frame.requestN_ = static_cast<uint32_t>(requestN); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +static size_t getResumeIdTokenFramingLength( + FrameFlags flags, + const ResumeIdentificationToken& token) { + return !!(flags & FrameFlags::RESUME_ENABLE) + ? sizeof(uint16_t) + token.data().size() + : 0; +} + +FrameType FrameSerializerV1_0::peekFrameType(const folly::IOBuf& in) const { + folly::io::Cursor cur(&in); + try { + cur.skip(sizeof(int32_t)); // streamId + uint8_t type = cur.readBE<uint8_t>(); // |Frame Type |I|M| + return deserializeFrameType(type >> 2); + } catch (...) { + return FrameType::RESERVED; + } +} + +folly::Optional<StreamId> FrameSerializerV1_0::peekStreamId( + const folly::IOBuf& in, + bool skipFrameLengthBytes) const { + folly::io::Cursor cur(&in); + try { + if (skipFrameLengthBytes) { + cur.skip(3); // skip 3 bytes for frame length + } + auto streamId = cur.readBE<int32_t>(); + if (streamId < 0) { + return folly::none; + } + return folly::make_optional(static_cast<StreamId>(streamId)); + } catch (...) { + return folly::none; + } +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_REQUEST_STREAM&& frame) const { + return serializeOutInternal(std::move(frame)); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_REQUEST_CHANNEL&& frame) const { + return serializeOutInternal(std::move(frame)); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_REQUEST_RESPONSE&& frame) const { + auto queue = + createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_REQUEST_FNF&& frame) const { + auto queue = + createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_REQUEST_N&& frame) const { + auto queue = createBufferQueue(kFrameHeaderSize + sizeof(uint32_t)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + appender.writeBE<int32_t>(static_cast<int32_t>(frame.requestN_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_METADATA_PUSH&& frame) const { + auto queue = createBufferQueue(kFrameHeaderSize); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + if (frame.metadata_) { + appender.insert(std::move(frame.metadata_)); + } + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_CANCEL&& frame) const { + auto queue = createBufferQueue(kFrameHeaderSize); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_PAYLOAD&& frame) const { + auto queue = + createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_ERROR&& frame) const { + auto queue = createBufferQueue( + kFrameHeaderSize + sizeof(uint32_t) + payloadFramingSize(frame.payload_)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + appender.writeBE(static_cast<uint32_t>(frame.errorCode_)); + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_KEEPALIVE&& frame) const { + auto queue = createBufferQueue(kFrameHeaderSize + sizeof(int64_t)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + appender.writeBE<int64_t>(static_cast<int64_t>(frame.position_)); + if (frame.data_) { + appender.insert(std::move(frame.data_)); + } + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_SETUP&& frame) const { + auto queue = createBufferQueue( + kFrameHeaderSize + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(int32_t) + + sizeof(int32_t) + + getResumeIdTokenFramingLength(frame.header_.flags, frame.token_) + + +sizeof(uint8_t) + frame.metadataMimeType_.length() + sizeof(uint8_t) + + frame.dataMimeType_.length() + payloadFramingSize(frame.payload_)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + + serializeHeaderInto(appender, frame.header_); + CHECK( + frame.versionMajor_ != ProtocolVersion::Unknown.major || + frame.versionMinor_ != ProtocolVersion::Unknown.minor); + appender.writeBE<uint16_t>(frame.versionMajor_); + appender.writeBE<uint16_t>(frame.versionMinor_); + appender.writeBE(static_cast<int32_t>(frame.keepaliveTime_)); + appender.writeBE(static_cast<int32_t>(frame.maxLifetime_)); + + if (!!(frame.header_.flags & FrameFlags::RESUME_ENABLE)) { + appender.writeBE<uint16_t>( + static_cast<uint16_t>(frame.token_.data().size())); + appender.push(frame.token_.data().data(), frame.token_.data().size()); + } + + CHECK( + frame.metadataMimeType_.length() <= std::numeric_limits<uint8_t>::max()); + appender.writeBE(static_cast<uint8_t>(frame.metadataMimeType_.length())); + appender.push( + reinterpret_cast<const uint8_t*>(frame.metadataMimeType_.data()), + frame.metadataMimeType_.length()); + + CHECK(frame.dataMimeType_.length() <= std::numeric_limits<uint8_t>::max()); + appender.writeBE(static_cast<uint8_t>(frame.dataMimeType_.length())); + appender.push( + reinterpret_cast<const uint8_t*>(frame.dataMimeType_.data()), + frame.dataMimeType_.length()); + + serializePayloadInto(appender, std::move(frame.payload_)); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_LEASE&& frame) const { + auto queue = + createBufferQueue(kFrameHeaderSize + sizeof(int32_t) + sizeof(int32_t)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + appender.writeBE(static_cast<int32_t>(frame.ttl_)); + appender.writeBE(static_cast<int32_t>(frame.numberOfRequests_)); + if (frame.metadata_) { + appender.insert(std::move(frame.metadata_)); + } + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_RESUME&& frame) const { + auto queue = createBufferQueue( + kFrameHeaderSize + sizeof(uint16_t) + sizeof(uint16_t) + + sizeof(uint16_t) + frame.token_.data().size() + sizeof(int32_t) + + sizeof(int32_t)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + + CHECK( + frame.versionMajor_ != ProtocolVersion::Unknown.major || + frame.versionMinor_ != ProtocolVersion::Unknown.minor); + appender.writeBE(static_cast<uint16_t>(frame.versionMajor_)); + appender.writeBE(static_cast<uint16_t>(frame.versionMinor_)); + + appender.writeBE<uint16_t>(static_cast<uint16_t>(frame.token_.data().size())); + appender.push(frame.token_.data().data(), frame.token_.data().size()); + + appender.writeBE<int64_t>(frame.lastReceivedServerPosition_); + appender.writeBE<int64_t>(frame.clientPosition_); + return queue.move(); +} + +std::unique_ptr<folly::IOBuf> FrameSerializerV1_0::serializeOut( + Frame_RESUME_OK&& frame) const { + auto queue = createBufferQueue(kFrameHeaderSize + sizeof(int64_t)); + folly::io::QueueAppender appender(&queue, /* do not grow */ 0); + serializeHeaderInto(appender, frame.header_); + appender.writeBE<int64_t>(frame.position_); + return queue.move(); +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_REQUEST_STREAM& frame, + std::unique_ptr<folly::IOBuf> in) const { + return deserializeFromInternal(frame, std::move(in)); +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_REQUEST_CHANNEL& frame, + std::unique_ptr<folly::IOBuf> in) const { + return deserializeFromInternal(frame, std::move(in)); +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_REQUEST_RESPONSE& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_REQUEST_FNF& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_REQUEST_N& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + auto requestN = cur.readBE<int32_t>(); + if (requestN <= 0) { + throw std::runtime_error("invalid request n"); + } + frame.requestN_ = static_cast<uint32_t>(requestN); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_METADATA_PUSH& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + // metadata takes the rest of the frame, just like data in other frames + // that's why we use deserializeDataFrom + frame.metadata_ = deserializeDataFrom(cur); + } catch (...) { + return false; + } + return frame.metadata_ != nullptr; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_CANCEL& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_PAYLOAD& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_ERROR& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + frame.errorCode_ = static_cast<ErrorCode>(cur.readBE<uint32_t>()); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_KEEPALIVE& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + auto position = cur.readBE<int64_t>(); + if (position < 0) { + throw std::runtime_error("invalid value for position"); + } + frame.position_ = static_cast<ResumePosition>(position); + frame.data_ = deserializeDataFrom(cur); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_SETUP& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + + frame.versionMajor_ = cur.readBE<uint16_t>(); + frame.versionMinor_ = cur.readBE<uint16_t>(); + + auto keepaliveTime = cur.readBE<int32_t>(); + if (keepaliveTime <= 0) { + throw std::runtime_error("invalid keepalive time"); + } + frame.keepaliveTime_ = static_cast<uint32_t>(keepaliveTime); + + auto maxLifetime = cur.readBE<int32_t>(); + if (maxLifetime <= 0) { + throw std::runtime_error("invalid maxLife time"); + } + frame.maxLifetime_ = static_cast<uint32_t>(maxLifetime); + + if (!!(frame.header_.flags & FrameFlags::RESUME_ENABLE)) { + auto resumeTokenSize = cur.readBE<uint16_t>(); + std::vector<uint8_t> data(resumeTokenSize); + cur.pull(data.data(), data.size()); + frame.token_.set(std::move(data)); + } else { + frame.token_ = ResumeIdentificationToken(); + } + + auto mdmtLen = cur.readBE<uint8_t>(); + frame.metadataMimeType_ = cur.readFixedString(mdmtLen); + + auto dmtLen = cur.readBE<uint8_t>(); + frame.dataMimeType_ = cur.readFixedString(dmtLen); + frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_LEASE& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + + auto ttl = cur.readBE<int32_t>(); + if (ttl <= 0) { + throw std::runtime_error("invalid ttl value"); + } + frame.ttl_ = static_cast<uint32_t>(ttl); + + auto numberOfRequests = cur.readBE<int32_t>(); + if (numberOfRequests <= 0) { + throw std::runtime_error("invalid numberOfRequests value"); + } + frame.numberOfRequests_ = static_cast<uint32_t>(numberOfRequests); + frame.metadata_ = deserializeDataFrom(cur); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_RESUME& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + frame.versionMajor_ = cur.readBE<uint16_t>(); + frame.versionMinor_ = cur.readBE<uint16_t>(); + + auto resumeTokenSize = cur.readBE<uint16_t>(); + std::vector<uint8_t> data(resumeTokenSize); + cur.pull(data.data(), data.size()); + frame.token_.set(std::move(data)); + + auto lastReceivedServerPosition = cur.readBE<int64_t>(); + if (lastReceivedServerPosition < 0) { + throw std::runtime_error("invalid value for lastReceivedServerPosition"); + } + frame.lastReceivedServerPosition_ = + static_cast<ResumePosition>(lastReceivedServerPosition); + + auto clientPosition = cur.readBE<int64_t>(); + if (clientPosition < 0) { + throw std::runtime_error("invalid value for clientPosition"); + } + frame.clientPosition_ = static_cast<ResumePosition>(clientPosition); + } catch (...) { + return false; + } + return true; +} + +bool FrameSerializerV1_0::deserializeFrom( + Frame_RESUME_OK& frame, + std::unique_ptr<folly::IOBuf> in) const { + folly::io::Cursor cur(in.get()); + try { + deserializeHeaderFrom(cur, frame.header_); + + auto position = cur.readBE<int64_t>(); + if (position < 0) { + throw std::runtime_error("invalid value for position"); + } + frame.position_ = static_cast<ResumePosition>(position); + } catch (...) { + return false; + } + return true; +} + +ProtocolVersion FrameSerializerV1_0::detectProtocolVersion( + const folly::IOBuf& firstFrame, + size_t skipBytes) { + // SETUP frame + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Stream ID = 0 | + // +-----------+-+-+-+-+-----------+-------------------------------+ + // |Frame Type |0|M|R|L| Flags | + // +-----------+-+-+-+-+-----------+-------------------------------+ + // | Major Version | Minor Version | + // +-------------------------------+-------------------------------+ + // ... + // +-------------------------------+-------------------------------+ + + // RESUME frame + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Stream ID = 0 | + // +-----------+-+-+---------------+-------------------------------+ + // |Frame Type |0|0| Flags | + // +-------------------------------+-------------------------------+ + // | Major Version | Minor Version | + // +-------------------------------+-------------------------------+ + // ... + // +-------------------------------+-------------------------------+ + + folly::io::Cursor cur(&firstFrame); + try { + cur.skip(skipBytes); + + auto streamId = cur.readBE<int32_t>(); + auto frameType = cur.readBE<uint8_t>() >> 2; + cur.skip(sizeof(uint8_t)); // flags + auto majorVersion = cur.readBE<uint16_t>(); + auto minorVersion = cur.readBE<uint16_t>(); + + constexpr static const auto kSETUP = 0x01; + constexpr static const auto kRESUME = 0x0D; + + VLOG(4) << "frameType=" << frameType << "streamId=" << streamId + << " majorVersion=" << majorVersion + << " minorVersion=" << minorVersion; + + if (streamId == 0 && (frameType == kSETUP || frameType == kRESUME) && + majorVersion == FrameSerializerV1_0::Version.major && + minorVersion == FrameSerializerV1_0::Version.minor) { + return FrameSerializerV1_0::Version; + } + } catch (...) { + } + return ProtocolVersion::Unknown; +} + +size_t FrameSerializerV1_0::frameLengthFieldSize() const { + return 3; // bytes +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h new file mode 100644 index 000000000..f636584dd --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h @@ -0,0 +1,97 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/framing/FrameSerializer.h" + +namespace rsocket { + +class FrameSerializerV1_0 : public FrameSerializer { + public: + constexpr static const ProtocolVersion Version = ProtocolVersion(1, 0); + constexpr static const size_t kFrameHeaderSize = 6; // bytes + constexpr static const size_t kMinBytesNeededForAutodetection = 10; // bytes + + ProtocolVersion protocolVersion() const override; + + static ProtocolVersion detectProtocolVersion( + const folly::IOBuf& firstFrame, + size_t skipBytes = 0); + + FrameType peekFrameType(const folly::IOBuf& in) const override; + folly::Optional<StreamId> peekStreamId( + const folly::IOBuf& in, + bool skipFrameLengthBytes) const override; + + std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_STREAM&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_CHANNEL&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_RESPONSE&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut( + Frame_REQUEST_FNF&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_REQUEST_N&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut( + Frame_METADATA_PUSH&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_CANCEL&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_PAYLOAD&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_ERROR&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_KEEPALIVE&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_SETUP&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_LEASE&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_RESUME&&) const override; + std::unique_ptr<folly::IOBuf> serializeOut(Frame_RESUME_OK&&) const override; + + bool deserializeFrom(Frame_REQUEST_STREAM&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_REQUEST_CHANNEL&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_REQUEST_RESPONSE&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_REQUEST_FNF&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_REQUEST_N&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_METADATA_PUSH&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_CANCEL&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_PAYLOAD&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_ERROR&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_KEEPALIVE&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_SETUP&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_LEASE&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_RESUME&, std::unique_ptr<folly::IOBuf>) + const override; + bool deserializeFrom(Frame_RESUME_OK&, std::unique_ptr<folly::IOBuf>) + const override; + + static std::unique_ptr<folly::IOBuf> deserializeMetadataFrom( + folly::io::Cursor& cur, + FrameFlags flags); + + private: + std::unique_ptr<folly::IOBuf> serializeOutInternal( + Frame_REQUEST_Base&& frame) const; + + size_t frameLengthFieldSize() const override; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransport.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransport.h new file mode 100644 index 000000000..6c5ed3ef1 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransport.h @@ -0,0 +1,38 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/IOBuf.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/framing/FrameProcessor.h" + +namespace rsocket { + +// Refer to FrameTransportImpl for documentation on the implementation +class FrameTransport { + public: + virtual ~FrameTransport() = default; + virtual void setFrameProcessor(std::shared_ptr<FrameProcessor>) = 0; + virtual void outputFrameOrDrop(std::unique_ptr<folly::IOBuf>) = 0; + virtual void close() = 0; + + // Just for observation purposes! + // TODO(T25011919): remove + virtual DuplexConnection* getConnection() = 0; + + virtual bool isConnectionFramed() const = 0; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.cpp new file mode 100644 index 000000000..8e49b9bac --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.cpp @@ -0,0 +1,136 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameTransportImpl.h" + +#include <folly/ExceptionWrapper.h> +#include <folly/io/IOBuf.h> +#include <glog/logging.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/framing/FrameProcessor.h" + +namespace rsocket { + +using namespace yarpl::flowable; + +FrameTransportImpl::FrameTransportImpl( + std::unique_ptr<DuplexConnection> connection) + : connection_(std::move(connection)) { + CHECK(connection_); +} + +FrameTransportImpl::~FrameTransportImpl() { + VLOG(1) << "~FrameTransport (" << this << ")"; +} + +void FrameTransportImpl::connect() { + CHECK(connection_); + + // The onSubscribe call on the previous line may have called the terminating + // signal which would call disconnect/close. + if (connection_) { + // This may call ::onSubscribe in-line, which calls ::request on the + // provided subscription, which might deliver frames in-line. It can also + // call onComplete which will call disconnect/close and reset the + // connection_ while still inside of the connection_::setInput method. We + // will create a hard reference for that case and keep the object alive + // until setInput method returns + auto connectionCopy = connection_; + connectionCopy->setInput(shared_from_this()); + } +} + +void FrameTransportImpl::setFrameProcessor( + std::shared_ptr<FrameProcessor> frameProcessor) { + frameProcessor_ = std::move(frameProcessor); + if (frameProcessor_) { + CHECK(!isClosed()); + connect(); + } +} + +void FrameTransportImpl::close() { + // Make sure we never try to call back into the processor. + frameProcessor_ = nullptr; + + if (!connection_) { + return; + } + connection_.reset(); + + if (auto subscription = std::move(connectionInputSub_)) { + subscription->cancel(); + } +} + +void FrameTransportImpl::onSubscribe( + std::shared_ptr<Subscription> subscription) { + if (!connection_) { + return; + } + + CHECK(!connectionInputSub_); + CHECK(frameProcessor_); + connectionInputSub_ = std::move(subscription); + connectionInputSub_->request(std::numeric_limits<int64_t>::max()); +} + +void FrameTransportImpl::onNext(std::unique_ptr<folly::IOBuf> frame) { + // Copy in case frame processing calls through to close(). + if (auto const processor = frameProcessor_) { + processor->processFrame(std::move(frame)); + } +} + +void FrameTransportImpl::terminateProcessor(folly::exception_wrapper ex) { + // This method can be executed multiple times while terminating. + + if (!frameProcessor_) { + // already terminated + return; + } + + if (auto conn_sub = std::move(connectionInputSub_)) { + conn_sub->cancel(); + } + + auto frameProcessor = std::move(frameProcessor_); + VLOG(3) << this << " terminating frame processor ex=" << ex.what(); + frameProcessor->onTerminal(std::move(ex)); +} + +void FrameTransportImpl::onComplete() { + VLOG(3) << "FrameTransport received onComplete"; + terminateProcessor(folly::exception_wrapper()); +} + +void FrameTransportImpl::onError(folly::exception_wrapper ex) { + VLOG(3) << "FrameTransport received onError: " << ex.what(); + terminateProcessor(std::move(ex)); +} + +void FrameTransportImpl::outputFrameOrDrop( + std::unique_ptr<folly::IOBuf> frame) { + if (connection_) { + connection_->send(std::move(frame)); + } +} + +bool FrameTransportImpl::isConnectionFramed() const { + CHECK(connection_); + return connection_->isFramed(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h new file mode 100644 index 000000000..36ce9b526 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h @@ -0,0 +1,77 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include "rsocket/DuplexConnection.h" +#include "rsocket/internal/Common.h" +#include "yarpl/flowable/Subscription.h" + +#include "rsocket/framing/FrameTransport.h" + +namespace rsocket { + +class FrameProcessor; + +class FrameTransportImpl + : public FrameTransport, + /// Registered as an input in the DuplexConnection. + public DuplexConnection::Subscriber, + public std::enable_shared_from_this<FrameTransportImpl> { + public: + explicit FrameTransportImpl(std::unique_ptr<DuplexConnection> connection); + ~FrameTransportImpl(); + + void setFrameProcessor(std::shared_ptr<FrameProcessor>) override; + + /// Writes the frame directly to output. If the connection was closed it will + /// drop the frame. + void outputFrameOrDrop(std::unique_ptr<folly::IOBuf>) override; + + /// Cancel the input and close the underlying connection. + void close() override; + + bool isClosed() const { + return !connection_; + } + + DuplexConnection* getConnection() override { + return connection_.get(); + } + + bool isConnectionFramed() const override; + + // Subscriber. + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>) override; + void onNext(std::unique_ptr<folly::IOBuf>) override; + void onComplete() override; + void onError(folly::exception_wrapper) override; + + private: + void connect(); + + /// Terminates the FrameProcessor. Will queue up the exception if no + /// processor is set, overwriting any previously queued exception. + void terminateProcessor(folly::exception_wrapper); + + std::shared_ptr<FrameProcessor> frameProcessor_; + std::shared_ptr<DuplexConnection> connection_; + + std::shared_ptr<DuplexConnection::Subscriber> connectionOutput_; + std::shared_ptr<yarpl::flowable::Subscription> connectionInputSub_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.cpp new file mode 100644 index 000000000..8fb4fd140 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FrameType.h" + +#include <ostream> + +#include <glog/logging.h> + +namespace rsocket { + +constexpr folly::StringPiece kUnknown{"UNKNOWN_FRAME_TYPE"}; + +folly::StringPiece toString(FrameType type) { + switch (type) { + case FrameType::RESERVED: + return "RESERVED"; + case FrameType::SETUP: + return "SETUP"; + case FrameType::LEASE: + return "LEASE"; + case FrameType::KEEPALIVE: + return "KEEPALIVE"; + case FrameType::REQUEST_RESPONSE: + return "REQUEST_RESPONSE"; + case FrameType::REQUEST_FNF: + return "REQUEST_FNF"; + case FrameType::REQUEST_STREAM: + return "REQUEST_STREAM"; + case FrameType::REQUEST_CHANNEL: + return "REQUEST_CHANNEL"; + case FrameType::REQUEST_N: + return "REQUEST_N"; + case FrameType::CANCEL: + return "CANCEL"; + case FrameType::PAYLOAD: + return "PAYLOAD"; + case FrameType::ERROR: + return "ERROR"; + case FrameType::METADATA_PUSH: + return "METADATA_PUSH"; + case FrameType::RESUME: + return "RESUME"; + case FrameType::RESUME_OK: + return "RESUME_OK"; + case FrameType::EXT: + return "EXT"; + default: + DLOG(FATAL) << "Unknown frame type"; + return kUnknown; + } +} + +std::ostream& operator<<(std::ostream& os, FrameType type) { + auto const str = toString(type); + if (str == kUnknown) { + return os << "Unknown FrameType[" << static_cast<int>(type) << "]"; + } + return os << str; +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.h new file mode 100644 index 000000000..726f9cd75 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FrameType.h @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cstdint> +#include <iosfwd> + +#include <folly/Range.h> + +namespace rsocket { + +enum class FrameType : uint8_t { + RESERVED = 0x00, + SETUP = 0x01, + LEASE = 0x02, + KEEPALIVE = 0x03, + REQUEST_RESPONSE = 0x04, + REQUEST_FNF = 0x05, + REQUEST_STREAM = 0x06, + REQUEST_CHANNEL = 0x07, + REQUEST_N = 0x08, + CANCEL = 0x09, + PAYLOAD = 0x0A, + ERROR = 0x0B, + METADATA_PUSH = 0x0C, + RESUME = 0x0D, + RESUME_OK = 0x0E, + EXT = 0x3F, +}; + +folly::StringPiece toString(FrameType); + +std::ostream& operator<<(std::ostream&, FrameType); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.cpp new file mode 100644 index 000000000..9dec14a76 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FramedDuplexConnection.h" +#include <folly/io/Cursor.h> +#include "rsocket/framing/FrameSerializer_v1_0.h" +#include "rsocket/framing/FramedReader.h" + +namespace rsocket { + +namespace { + +constexpr auto kMaxFrameLength = 0xFFFFFF; // 24bit max value + +template <typename TWriter> +void writeFrameLength( + TWriter& cur, + size_t frameLength, + size_t frameSizeFieldLength) { + DCHECK(frameSizeFieldLength > 0); + + // starting from the highest byte + // frameSizeFieldLength == 3 => shift = [16,8,0] + // frameSizeFieldLength == 4 => shift = [24,16,8,0] + auto shift = (frameSizeFieldLength - 1) * 8; + + while (frameSizeFieldLength--) { + const auto byte = (frameLength >> shift) & 0xFF; + cur.write(static_cast<uint8_t>(byte)); + shift -= 8; + } +} + +size_t getFrameSizeFieldLength(ProtocolVersion version) { + CHECK(version != ProtocolVersion::Unknown); + if (version < FrameSerializerV1_0::Version) { + return sizeof(int32_t); + } else { + return 3; // bytes + } +} + +std::unique_ptr<folly::IOBuf> prependSize( + ProtocolVersion version, + std::unique_ptr<folly::IOBuf> payload) { + CHECK(payload); + + const auto frameSizeFieldLength = getFrameSizeFieldLength(version); + const auto payloadLength = payload->computeChainDataLength(); + + CHECK_LE(payloadLength, kMaxFrameLength) + << "payloadLength: " << payloadLength + << " kMaxFrameLength: " << kMaxFrameLength; + + if (payload->headroom() >= frameSizeFieldLength) { + // move the data pointer back and write value to the payload + payload->prepend(frameSizeFieldLength); + folly::io::RWPrivateCursor cur(payload.get()); + writeFrameLength(cur, payloadLength, frameSizeFieldLength); + VLOG(4) << "writing frame length=" << payload->length() << std::endl + << hexDump(payload->clone()->moveToFbString()); + return payload; + } else { + auto newPayload = folly::IOBuf::createCombined(frameSizeFieldLength); + folly::io::Appender appender(newPayload.get(), /* do not grow */ 0); + writeFrameLength(appender, payloadLength, frameSizeFieldLength); + newPayload->appendChain(std::move(payload)); + VLOG(4) << "writing frame length=" << newPayload->computeChainDataLength() + << std::endl + << hexDump(newPayload->clone()->moveToFbString()); + return newPayload; + } +} + +} // namespace + +FramedDuplexConnection::~FramedDuplexConnection() {} + +FramedDuplexConnection::FramedDuplexConnection( + std::unique_ptr<DuplexConnection> connection, + ProtocolVersion protocolVersion) + : inner_(std::move(connection)), + protocolVersion_(std::make_shared<ProtocolVersion>(protocolVersion)) {} + +void FramedDuplexConnection::send(std::unique_ptr<folly::IOBuf> buf) { + if (!inner_) { + return; + } + + auto sized = prependSize(*protocolVersion_, std::move(buf)); + inner_->send(std::move(sized)); +} + +void FramedDuplexConnection::setInput( + std::shared_ptr<DuplexConnection::Subscriber> framesSink) { + if (!inputReader_) { + inputReader_ = std::make_shared<FramedReader>(protocolVersion_); + inner_->setInput(inputReader_); + } + inputReader_->setInput(std::move(framesSink)); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h new file mode 100644 index 000000000..2073266ea --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h @@ -0,0 +1,50 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/DuplexConnection.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +class FramedReader; +struct ProtocolVersion; + +class FramedDuplexConnection : public virtual DuplexConnection { + public: + FramedDuplexConnection( + std::unique_ptr<DuplexConnection> connection, + ProtocolVersion protocolVersion); + + ~FramedDuplexConnection(); + + void send(std::unique_ptr<folly::IOBuf>) override; + + void setInput(std::shared_ptr<DuplexConnection::Subscriber>) override; + + bool isFramed() const override { + return true; + } + + DuplexConnection* getConnection() { + return inner_.get(); + } + + private: + const std::unique_ptr<DuplexConnection> inner_; + std::shared_ptr<FramedReader> inputReader_; + const std::shared_ptr<ProtocolVersion> protocolVersion_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.cpp new file mode 100644 index 000000000..02edba694 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.cpp @@ -0,0 +1,217 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/FramedReader.h" + +#include <folly/io/Cursor.h> + +#include "rsocket/framing/FrameSerializer_v1_0.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +using namespace yarpl::flowable; + +namespace { + +constexpr size_t kFrameLengthFieldLengthV1_0 = 3; + +/// Get the byte size of the frame length field in an RSocket frame. +size_t frameSizeFieldLength(ProtocolVersion version) { + DCHECK_NE(version, ProtocolVersion::Unknown); + return kFrameLengthFieldLengthV1_0; +} + +/// Get the minimum size for a valid RSocket frame (including its frame length +/// field). +size_t minimalFrameLength(ProtocolVersion version) { + DCHECK_NE(version, ProtocolVersion::Unknown); + return FrameSerializerV1_0::kFrameHeaderSize; +} + +/// Compute the length of the entire frame (including its frame length field), +/// if given only its frame length field. +size_t frameSizeWithLengthField(ProtocolVersion version, size_t frameSize) { + return version < FrameSerializerV1_0::Version + ? frameSize + : frameSize + frameSizeFieldLength(version); +} + +/// Compute the length of the frame (excluding its frame length field), if given +/// only its frame length field. +size_t frameSizeWithoutLengthField(ProtocolVersion version, size_t frameSize) { + DCHECK_NE(version, ProtocolVersion::Unknown); + return version < FrameSerializerV1_0::Version + ? frameSize - frameSizeFieldLength(version) + : frameSize; +} +} // namespace + +size_t FramedReader::readFrameLength() const { + const auto fieldLength = frameSizeFieldLength(*version_); + DCHECK_GT(fieldLength, 0); + + folly::io::Cursor cur{payloadQueue_.front()}; + size_t frameLength = 0; + + // Reading of arbitrary-sized big-endian integer. + for (size_t i = 0; i < fieldLength; ++i) { + frameLength <<= 8; + frameLength |= cur.read<uint8_t>(); + } + + return frameLength; +} + +void FramedReader::onSubscribe(std::shared_ptr<Subscription> subscription) { + subscription_ = std::move(subscription); + subscription_->request(std::numeric_limits<int64_t>::max()); +} + +void FramedReader::onNext(std::unique_ptr<folly::IOBuf> payload) { + VLOG(4) << "incoming bytes length=" << payload->length() << '\n' + << hexDump(payload->clone()->moveToFbString()); + payloadQueue_.append(std::move(payload)); + parseFrames(); +} + +void FramedReader::parseFrames() { + if (dispatchingFrames_) { + return; + } + + // Delivering onNext can trigger termination and destroy this instance. + auto const self = shared_from_this(); + + dispatchingFrames_ = true; + + while (allowance_.canConsume(1) && inner_) { + if (!ensureOrAutodetectProtocolVersion()) { + // At this point we dont have enough bytes on the wire or we errored out. + break; + } + + auto const frameSizeFieldLen = frameSizeFieldLength(*version_); + if (payloadQueue_.chainLength() < frameSizeFieldLen) { + // We don't even have the next frame size value. + break; + } + + auto const nextFrameSize = readFrameLength(); + if (nextFrameSize < minimalFrameLength(*version_)) { + error("Invalid frame - Frame size smaller than minimum"); + break; + } + + if (payloadQueue_.chainLength() < + frameSizeWithLengthField(*version_, nextFrameSize)) { + // Need to accumulate more data. + break; + } + + payloadQueue_.trimStart(frameSizeFieldLen); + const auto payloadSize = + frameSizeWithoutLengthField(*version_, nextFrameSize); + + DCHECK_GT(payloadSize, 0) + << "folly::IOBufQueue::split(0) returns a nullptr, can't have that"; + auto nextFrame = payloadQueue_.split(payloadSize); + + CHECK(allowance_.tryConsume(1)); + + VLOG(4) << "parsed frame length=" << nextFrame->length() << '\n' + << hexDump(nextFrame->clone()->moveToFbString()); + inner_->onNext(std::move(nextFrame)); + } + + dispatchingFrames_ = false; +} + +void FramedReader::onComplete() { + payloadQueue_.move(); + auto subscription = std::move(subscription_); + if (auto subscriber = std::move(inner_)) { + // After this call the instance can be destroyed! + subscriber->onComplete(); + } +} + +void FramedReader::onError(folly::exception_wrapper ex) { + payloadQueue_.move(); + auto subscription = std::move(subscription_); + if (auto subscriber = std::move(inner_)) { + // After this call the instance can be destroyed! + subscriber->onError(std::move(ex)); + } +} + +void FramedReader::request(int64_t n) { + allowance_.add(n); + parseFrames(); +} + +void FramedReader::cancel() { + allowance_.consumeAll(); + inner_ = nullptr; +} + +void FramedReader::setInput( + std::shared_ptr<DuplexConnection::Subscriber> inner) { + CHECK(!inner_) + << "Must cancel original input to FramedReader before setting a new one"; + inner_ = std::move(inner); + inner_->onSubscribe(shared_from_this()); +} + +bool FramedReader::ensureOrAutodetectProtocolVersion() { + if (*version_ != ProtocolVersion::Unknown) { + return true; + } + + const auto minBytesNeeded = + FrameSerializerV1_0::kMinBytesNeededForAutodetection; + DCHECK_GT(minBytesNeeded, 0); + if (payloadQueue_.chainLength() < minBytesNeeded) { + return false; + } + + DCHECK_GT(minBytesNeeded, kFrameLengthFieldLengthV1_0); + + auto const& firstFrame = *payloadQueue_.front(); + + const auto detectedV1 = FrameSerializerV1_0::detectProtocolVersion( + firstFrame, kFrameLengthFieldLengthV1_0); + if (detectedV1 != ProtocolVersion::Unknown) { + *version_ = FrameSerializerV1_0::Version; + return true; + } + + error("Could not detect protocol version from framing"); + return false; +} + +void FramedReader::error(std::string errorMsg) { + VLOG(1) << "error: " << errorMsg; + + payloadQueue_.move(); + if (auto subscription = std::move(subscription_)) { + subscription->cancel(); + } + if (auto subscriber = std::move(inner_)) { + // After this call the instance can be destroyed! + subscriber->onError(std::runtime_error{std::move(errorMsg)}); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.h b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.h new file mode 100644 index 000000000..d0bc05a4f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/FramedReader.h @@ -0,0 +1,67 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/IOBufQueue.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/framing/ProtocolVersion.h" +#include "rsocket/internal/Allowance.h" +#include "yarpl/flowable/Subscription.h" + +namespace rsocket { + +class FramedReader : public DuplexConnection::Subscriber, + public yarpl::flowable::Subscription, + public std::enable_shared_from_this<FramedReader> { + public: + explicit FramedReader(std::shared_ptr<ProtocolVersion> version) + : version_{std::move(version)} {} + + /// Set the inner subscriber which will be getting full frame payloads. + void setInput(std::shared_ptr<DuplexConnection::Subscriber>); + + /// Cancel the subscription and error the inner subscriber. + void error(std::string); + + // Subscriber. + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>) override; + void onNext(std::unique_ptr<folly::IOBuf>) override; + void onComplete() override; + void onError(folly::exception_wrapper) override; + + // Subscription. + + void request(int64_t) override; + void cancel() override; + + private: + void parseFrames(); + bool ensureOrAutodetectProtocolVersion(); + + size_t readFrameLength() const; + + std::shared_ptr<yarpl::flowable::Subscription> subscription_; + std::shared_ptr<DuplexConnection::Subscriber> inner_; + + Allowance allowance_; + bool dispatchingFrames_{false}; + + folly::IOBufQueue payloadQueue_{folly::IOBufQueue::cacheChainLength()}; + const std::shared_ptr<ProtocolVersion> version_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.cpp new file mode 100644 index 000000000..0fb97763b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.cpp @@ -0,0 +1,204 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/Framer.h" +#include <folly/io/Cursor.h> +#include "rsocket/framing/FrameSerializer_v1_0.h" + +namespace rsocket { + +namespace { + +constexpr size_t kFrameLengthFieldLengthV1_0 = 3; +constexpr auto kMaxFrameLength = 0xFFFFFF; // 24bit max value + +template <typename TWriter> +void writeFrameLength( + TWriter& cur, + size_t frameLength, + size_t frameSizeFieldLength) { + DCHECK(frameSizeFieldLength > 0); + + // starting from the highest byte + // frameSizeFieldLength == 3 => shift = [16,8,0] + // frameSizeFieldLength == 4 => shift = [24,16,8,0] + auto shift = (frameSizeFieldLength - 1) * 8; + + while (frameSizeFieldLength--) { + const auto byte = (frameLength >> shift) & 0xFF; + cur.write(static_cast<uint8_t>(byte)); + shift -= 8; + } +} +} // namespace + +/// Get the byte size of the frame length field in an RSocket frame. +size_t Framer::frameSizeFieldLength() const { + DCHECK_NE(protocolVersion_, ProtocolVersion::Unknown); + if (protocolVersion_ < FrameSerializerV1_0::Version) { + return sizeof(int32_t); + } else { + return 3; // bytes + } +} + +/// Get the minimum size for a valid RSocket frame (including its frame length +/// field). +size_t Framer::minimalFrameLength() const { + DCHECK_NE(protocolVersion_, ProtocolVersion::Unknown); + return FrameSerializerV1_0::kFrameHeaderSize; +} + +/// Compute the length of the entire frame (including its frame length field), +/// if given only its frame length field. +size_t Framer::frameSizeWithLengthField(size_t frameSize) const { + return protocolVersion_ < FrameSerializerV1_0::Version + ? frameSize + : frameSize + frameSizeFieldLength(); +} + +/// Compute the length of the frame (excluding its frame length field), if given +/// only its frame length field. +size_t Framer::frameSizeWithoutLengthField(size_t frameSize) const { + DCHECK_NE(protocolVersion_, ProtocolVersion::Unknown); + return protocolVersion_ < FrameSerializerV1_0::Version + ? frameSize - frameSizeFieldLength() + : frameSize; +} + +size_t Framer::readFrameLength() const { + const auto fieldLength = frameSizeFieldLength(); + DCHECK_GT(fieldLength, 0); + + folly::io::Cursor cur{payloadQueue_.front()}; + size_t frameLength = 0; + + // Reading of arbitrary-sized big-endian integer. + for (size_t i = 0; i < fieldLength; ++i) { + frameLength <<= 8; + frameLength |= cur.read<uint8_t>(); + } + + return frameLength; +} + +void Framer::addFrameChunk(std::unique_ptr<folly::IOBuf> payload) { + payloadQueue_.append(std::move(payload)); + parseFrames(); +} + +void Framer::parseFrames() { + if (payloadQueue_.empty() || !ensureOrAutodetectProtocolVersion()) { + // At this point we dont have enough bytes on the wire or we errored out. + return; + } + + while (!payloadQueue_.empty()) { + auto const frameSizeFieldLen = frameSizeFieldLength(); + if (payloadQueue_.chainLength() < frameSizeFieldLen) { + // We don't even have the next frame size value. + break; + } + + auto const nextFrameSize = readFrameLength(); + if (nextFrameSize < minimalFrameLength()) { + error("Invalid frame - Frame size smaller than minimum"); + break; + } + + if (payloadQueue_.chainLength() < frameSizeWithLengthField(nextFrameSize)) { + // Need to accumulate more data. + break; + } + + auto payloadSize = frameSizeWithoutLengthField(nextFrameSize); + if (stripFrameLengthField_) { + payloadQueue_.trimStart(frameSizeFieldLen); + } else { + payloadSize += frameSizeFieldLen; + } + + DCHECK_GT(payloadSize, 0) + << "folly::IOBufQueue::split(0) returns a nullptr, can't have that"; + auto nextFrame = payloadQueue_.split(payloadSize); + onFrame(std::move(nextFrame)); + } +} + +bool Framer::ensureOrAutodetectProtocolVersion() { + if (protocolVersion_ != ProtocolVersion::Unknown) { + return true; + } + + const auto minBytesNeeded = + FrameSerializerV1_0::kMinBytesNeededForAutodetection; + DCHECK_GT(minBytesNeeded, 0); + if (payloadQueue_.chainLength() < minBytesNeeded) { + return false; + } + + DCHECK_GT(minBytesNeeded, kFrameLengthFieldLengthV1_0); + + auto const& firstFrame = *payloadQueue_.front(); + + const auto detectedV1 = FrameSerializerV1_0::detectProtocolVersion( + firstFrame, kFrameLengthFieldLengthV1_0); + if (detectedV1 != ProtocolVersion::Unknown) { + protocolVersion_ = FrameSerializerV1_0::Version; + return true; + } + + error("Could not detect protocol version from data"); + return false; +} + +std::unique_ptr<folly::IOBuf> Framer::prependSize( + std::unique_ptr<folly::IOBuf> payload) { + CHECK(payload); + + const auto frameSizeFieldLengthValue = frameSizeFieldLength(); + const auto payloadLength = payload->computeChainDataLength(); + + CHECK_LE(payloadLength, kMaxFrameLength) + << "payloadLength: " << payloadLength + << " kMaxFrameLength: " << kMaxFrameLength; + + if (payload->headroom() >= frameSizeFieldLengthValue) { + // move the data pointer back and write value to the payload + payload->prepend(frameSizeFieldLengthValue); + folly::io::RWPrivateCursor cur(payload.get()); + writeFrameLength(cur, payloadLength, frameSizeFieldLengthValue); + return payload; + } else { + auto newPayload = folly::IOBuf::createCombined(frameSizeFieldLengthValue); + folly::io::Appender appender(newPayload.get(), /* do not grow */ 0); + writeFrameLength(appender, payloadLength, frameSizeFieldLengthValue); + newPayload->appendChain(std::move(payload)); + return newPayload; + } +} + +StreamId Framer::peekStreamId( + const folly::IOBuf& frame, + bool skipFrameLengthBytes) const { + return FrameSerializer::peekStreamId( + protocolVersion_, frame, skipFrameLengthBytes) + .value(); +} + +std::unique_ptr<folly::IOBuf> Framer::drainPayloadQueue() { + return payloadQueue_.move(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.h b/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.h new file mode 100644 index 000000000..2ff740492 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/Framer.h @@ -0,0 +1,73 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/IOBufQueue.h> +#include "rsocket/framing/ProtocolVersion.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +/// +/// Frames class is used to parse individual rsocket frames from the stream of +/// incoming payload chunks. Every time a frame is parsed the onFrame method is +/// invoked. +/// Each rsocket frame is prepended with the frame length by +/// prependSize method. +/// +class Framer { + public: + Framer(ProtocolVersion protocolVersion, bool stripFrameLengthField) + : protocolVersion_{protocolVersion}, + stripFrameLengthField_{stripFrameLengthField} {} + virtual ~Framer() {} + + /// For processing incoming frame chunks + void addFrameChunk(std::unique_ptr<folly::IOBuf>); + + /// Prepends payload size to the beginning of he IOBuf based on the + /// set protocol version + std::unique_ptr<folly::IOBuf> prependSize( + std::unique_ptr<folly::IOBuf> payload); + + /// derived class can override this method to react to termination + virtual void error(const char*) = 0; + virtual void onFrame(std::unique_ptr<folly::IOBuf>) = 0; + + ProtocolVersion protocolVersion() const { + return protocolVersion_; + } + + StreamId peekStreamId(const folly::IOBuf& frame, bool) const; + + std::unique_ptr<folly::IOBuf> drainPayloadQueue(); + + private: + // to explicitly trigger parsing frames + void parseFrames(); + bool ensureOrAutodetectProtocolVersion(); + + size_t readFrameLength() const; + size_t frameSizeFieldLength() const; + size_t minimalFrameLength() const; + size_t frameSizeWithLengthField(size_t frameSize) const; + size_t frameSizeWithoutLengthField(size_t frameSize) const; + + folly::IOBufQueue payloadQueue_{folly::IOBufQueue::cacheChainLength()}; + ProtocolVersion protocolVersion_; + const bool stripFrameLengthField_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.cpp new file mode 100644 index 000000000..ee8f54c5f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.cpp @@ -0,0 +1,32 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/ProtocolVersion.h" + +#include <limits> +#include <ostream> + +namespace rsocket { + +const ProtocolVersion ProtocolVersion::Unknown = ProtocolVersion( + std::numeric_limits<uint16_t>::max(), + std::numeric_limits<uint16_t>::max()); + +const ProtocolVersion ProtocolVersion::Latest = ProtocolVersion(1, 0); + +std::ostream& operator<<(std::ostream& os, const ProtocolVersion& version) { + return os << version.major << "." << version.minor; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.h b/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.h new file mode 100644 index 000000000..3daf24dad --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ProtocolVersion.h @@ -0,0 +1,75 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cstdint> +#include <iosfwd> + +namespace rsocket { + +// Bug in GCC: https://bugzilla.redhat.com/show_bug.cgi?id=130601 +#pragma push_macro("major") +#pragma push_macro("minor") +#undef major +#undef minor + +struct ProtocolVersion { + uint16_t major{}; + uint16_t minor{}; + + constexpr ProtocolVersion() = default; + constexpr ProtocolVersion(uint16_t _major, uint16_t _minor) + : major(_major), minor(_minor) {} + + static const ProtocolVersion Unknown; + static const ProtocolVersion Latest; +}; + +#pragma pop_macro("major") +#pragma pop_macro("minor") + +std::ostream& operator<<(std::ostream&, const ProtocolVersion&); + +constexpr bool operator==( + const ProtocolVersion& left, + const ProtocolVersion& right) { + return left.major == right.major && left.minor == right.minor; +} + +constexpr bool operator!=( + const ProtocolVersion& left, + const ProtocolVersion& right) { + return !(left == right); +} + +constexpr bool operator<( + const ProtocolVersion& left, + const ProtocolVersion& right) { + return left != ProtocolVersion::Unknown && + right != ProtocolVersion::Unknown && + (left.major < right.major || + (left.major == right.major && left.minor < right.minor)); +} + +constexpr bool operator>( + const ProtocolVersion& left, + const ProtocolVersion& right) { + return left != ProtocolVersion::Unknown && + right != ProtocolVersion::Unknown && + (left.major > right.major || + (left.major == right.major && left.minor > right.minor)); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.cpp new file mode 100644 index 000000000..3af76ed9a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/ResumeIdentificationToken.h" + +#include <limits> +#include <ostream> +#include <sstream> +#include <stdexcept> + +#include <folly/Random.h> +#include <glog/logging.h> + +namespace rsocket { + +constexpr const char* kHexChars = "0123456789abcdef"; + +ResumeIdentificationToken::ResumeIdentificationToken() {} + +ResumeIdentificationToken::ResumeIdentificationToken(const std::string& token) { + const auto getNibble = [&token](size_t i) { + uint8_t nibble; + if (token[i] >= '0' && token[i] <= '9') { + nibble = token[i] - '0'; + } else if (token[i] >= 'a' && token[i] <= 'f') { + nibble = token[i] - 'a' + 10; + } else { + throw std::invalid_argument("ResumeToken not in right format: " + token); + } + return nibble; + }; + if (token.size() < 2 || token[0] != '0' || token[1] != 'x' || + (token.size() % 2) != 0) { + throw std::invalid_argument("ResumeToken not in right format: " + token); + } + size_t i = 2; + while (i < token.size()) { + const uint8_t firstNibble = getNibble(i++); + const uint8_t secondNibble = getNibble(i++); + bits_.push_back((firstNibble << 4) | secondNibble); + } +} + +ResumeIdentificationToken ResumeIdentificationToken::generateNew() { + constexpr size_t kSize = 16; + std::vector<uint8_t> data; + data.reserve(kSize); + for (size_t i = 0; i < kSize; i++) { + data.push_back(static_cast<uint8_t>(folly::Random::rand32())); + } + return ResumeIdentificationToken(std::move(data)); +} + +void ResumeIdentificationToken::set(std::vector<uint8_t> newBits) { + CHECK(newBits.size() <= std::numeric_limits<uint16_t>::max()); + bits_ = std::move(newBits); +} + +std::string ResumeIdentificationToken::str() const { + std::stringstream out; + out << *this; + return out.str(); +} + +std::ostream& operator<<( + std::ostream& out, + const ResumeIdentificationToken& token) { + out << "0x"; + for (const auto b : token.data()) { + out << kHexChars[(b & 0xF0) >> 4]; + out << kHexChars[b & 0x0F]; + } + return out; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h b/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h new file mode 100644 index 000000000..be276ec3e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h @@ -0,0 +1,65 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cstdint> +#include <iosfwd> +#include <string> +#include <vector> + +namespace rsocket { + +class ResumeIdentificationToken { + public: + /// Creates an empty token. + ResumeIdentificationToken(); + + // The string token and ::str() function should complement each other. The + // string representation should be of the format + // 0x44ab7cf01fd290b63140d01ee789cfb6 + explicit ResumeIdentificationToken(const std::string&); + + static ResumeIdentificationToken generateNew(); + + const std::vector<uint8_t>& data() const { + return bits_; + } + + void set(std::vector<uint8_t> newBits); + + bool operator==(const ResumeIdentificationToken& right) const { + return data() == right.data(); + } + + bool operator!=(const ResumeIdentificationToken& right) const { + return data() != right.data(); + } + + bool operator<(const ResumeIdentificationToken& right) const { + return data() < right.data(); + } + + std::string str() const; + + private: + explicit ResumeIdentificationToken(std::vector<uint8_t> bits) + : bits_(std::move(bits)) {} + + std::vector<uint8_t> bits_; +}; + +std::ostream& operator<<(std::ostream&, const ResumeIdentificationToken&); + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.cpp new file mode 100644 index 000000000..e1abeade9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.cpp @@ -0,0 +1,43 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/ScheduledFrameProcessor.h" + +namespace rsocket { + +ScheduledFrameProcessor::ScheduledFrameProcessor( + std::shared_ptr<FrameProcessor> processor, + folly::EventBase* evb) + : evb_{evb}, processor_{std::move(processor)} {} + +ScheduledFrameProcessor::~ScheduledFrameProcessor() = default; + +void ScheduledFrameProcessor::processFrame( + std::unique_ptr<folly::IOBuf> ioBuf) { + CHECK(processor_) << "Calling processFrame() after onTerminal()"; + + evb_->runInEventBaseThread( + [processor = processor_, buf = std::move(ioBuf)]() mutable { + processor->processFrame(std::move(buf)); + }); +} + +void ScheduledFrameProcessor::onTerminal(folly::exception_wrapper ew) { + evb_->runInEventBaseThread( + [e = std::move(ew), processor = std::move(processor_)]() mutable { + processor->onTerminal(std::move(e)); + }); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h new file mode 100644 index 000000000..e4546af79 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h @@ -0,0 +1,44 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "rsocket/framing/FrameProcessor.h" + +namespace rsocket { + +// This class is a wrapper around FrameProcessor which ensures all methods of +// FrameProcessor get executed in a particular EventBase. +// +// This is currently used in the server where the resumed Transport of the +// client is on a different EventBase compared to the EventBase on which the +// original RSocketStateMachine was constructed for the client. Here the +// transport uses this class to schedule events of the RSocketStateMachine +// (FrameProcessor) in the original EventBase. +class ScheduledFrameProcessor : public FrameProcessor { + public: + ScheduledFrameProcessor(std::shared_ptr<FrameProcessor>, folly::EventBase*); + ~ScheduledFrameProcessor(); + + void processFrame(std::unique_ptr<folly::IOBuf>) override; + void onTerminal(folly::exception_wrapper) override; + + private: + folly::EventBase* const evb_; + std::shared_ptr<FrameProcessor> processor_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.cpp b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.cpp new file mode 100644 index 000000000..88f715f16 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.cpp @@ -0,0 +1,58 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/framing/ScheduledFrameTransport.h" + +#include "rsocket/framing/ScheduledFrameProcessor.h" + +namespace rsocket { + +ScheduledFrameTransport::~ScheduledFrameTransport() = default; + +void ScheduledFrameTransport::setFrameProcessor( + std::shared_ptr<FrameProcessor> fp) { + CHECK(frameTransport_) << "Inner transport already closed"; + + transportEvb_->runInEventBaseThread([stateMachineEvb = stateMachineEvb_, + transport = frameTransport_, + fp = std::move(fp)]() mutable { + auto scheduledFP = std::make_shared<ScheduledFrameProcessor>( + std::move(fp), stateMachineEvb); + transport->setFrameProcessor(std::move(scheduledFP)); + }); +} + +void ScheduledFrameTransport::outputFrameOrDrop( + std::unique_ptr<folly::IOBuf> ioBuf) { + CHECK(frameTransport_) << "Inner transport already closed"; + + transportEvb_->runInEventBaseThread( + [transport = frameTransport_, buf = std::move(ioBuf)]() mutable { + transport->outputFrameOrDrop(std::move(buf)); + }); +} + +void ScheduledFrameTransport::close() { + CHECK(frameTransport_) << "Inner transport already closed"; + + transportEvb_->runInEventBaseThread( + [transport = std::move(frameTransport_)]() { transport->close(); }); +} + +bool ScheduledFrameTransport::isConnectionFramed() const { + CHECK(frameTransport_) << "Inner transport already closed"; + return frameTransport_->isConnectionFramed(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h new file mode 100644 index 000000000..cc53f9444 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h @@ -0,0 +1,63 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "rsocket/framing/FrameTransport.h" + +namespace rsocket { + +// This class is a wrapper around FrameTransport which ensures all methods of +// FrameTransport get executed in a particular EventBase. +// +// This is currently used in the server where the resumed Transport of the +// client is on a different EventBase compared to the EventBase on which the +// original RSocketStateMachine was constructed for the client. Here the +// RSocketStateMachine uses this class to schedule events of the Transport in +// the new EventBase. +class ScheduledFrameTransport : public FrameTransport { + public: + ScheduledFrameTransport( + std::shared_ptr<FrameTransport> frameTransport, + folly::EventBase* transportEvb, + folly::EventBase* stateMachineEvb) + : transportEvb_(transportEvb), + stateMachineEvb_(stateMachineEvb), + frameTransport_(std::move(frameTransport)) {} + + ~ScheduledFrameTransport(); + + void setFrameProcessor(std::shared_ptr<FrameProcessor>) override; + void outputFrameOrDrop(std::unique_ptr<folly::IOBuf>) override; + void close() override; + bool isConnectionFramed() const override; + + private: + DuplexConnection* getConnection() override { + DLOG(FATAL) + << "ScheduledFrameTransport doesn't support getConnection method, " + "because it can create safe usage issues when EventBase of the " + "transport and the RSocketClient is not the same."; + return nullptr; + } + + private: + folly::EventBase* const transportEvb_; + folly::EventBase* const stateMachineEvb_; + std::shared_ptr<FrameTransport> frameTransport_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/Allowance.h b/ios/Pods/Flipper-RSocket/rsocket/internal/Allowance.h new file mode 100644 index 000000000..059dd3c47 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/Allowance.h @@ -0,0 +1,85 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cassert> +#include <cstddef> +#include <limits> + +namespace rsocket { + +class Allowance { + public: + using ValueType = size_t; + + Allowance() = default; + + explicit Allowance(ValueType initialValue) : value_(initialValue) {} + + bool tryConsume(ValueType n) { + if (!canConsume(n)) { + return false; + } + value_ -= n; + return true; + } + + ValueType add(ValueType n) { + auto old_value = value_; + value_ += n; + if (old_value > value_) { + value_ = max(); + } + return old_value; + } + + bool canConsume(ValueType n) const { + return value_ >= n; + } + + ValueType consumeAll() { + return consumeUpTo(max()); + } + + ValueType consumeUpTo(ValueType limit) { + if (limit > value_) { + limit = value_; + } + value_ -= limit; + return limit; + } + + explicit operator bool() const { + return value_; + } + + ValueType get() const { + return value_; + } + + static ValueType max() { + return std::numeric_limits<ValueType>::max(); + } + + private: + static_assert( + !std::numeric_limits<ValueType>::is_signed, + "Allowance representation must be an unsigned type"); + static_assert( + std::numeric_limits<ValueType>::is_integer, + "Allowance representation must be an integer type"); + ValueType value_{0}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h new file mode 100644 index 000000000..abe20fc9d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h @@ -0,0 +1,33 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> + +#include "rsocket/RSocketException.h" + +namespace rsocket { +class ClientResumeStatusCallback { + public: + virtual ~ClientResumeStatusCallback() = default; + + // Called when a RESUME_OK frame is received during resuming operation + virtual void onResumeOk() noexcept = 0; + + // The exception could be one of ResumptionException or ConnectionException + virtual void onResumeError(folly::exception_wrapper ex) noexcept = 0; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/Common.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/Common.cpp new file mode 100644 index 000000000..afc0e31ec --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/Common.cpp @@ -0,0 +1,140 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/Common.h" + +#include <sstream> + +#include <folly/Random.h> +#include <folly/String.h> +#include <folly/io/IOBuf.h> +#include <algorithm> +#include <random> + +namespace rsocket { + +static const char* getTerminatingSignalErrorMessage(int terminatingSignal) { + switch (static_cast<StreamCompletionSignal>(terminatingSignal)) { + case StreamCompletionSignal::CONNECTION_END: + return "connection closed"; + case StreamCompletionSignal::CONNECTION_ERROR: + return "connection error"; + case StreamCompletionSignal::ERROR: + return "socket or stream error"; + case StreamCompletionSignal::APPLICATION_ERROR: + return "application error"; + case StreamCompletionSignal::SOCKET_CLOSED: + return "reactive socket closed"; + case StreamCompletionSignal::UNSUPPORTED_SETUP: + return "unsupported setup"; + case StreamCompletionSignal::REJECTED_SETUP: + return "rejected setup"; + case StreamCompletionSignal::INVALID_SETUP: + return "invalid setup"; + case StreamCompletionSignal::COMPLETE: + case StreamCompletionSignal::CANCEL: + DCHECK(false) << "throwing exception for graceful termination?"; + return "graceful termination"; + default: + return "stream interrupted"; + } +} + +folly::StringPiece toString(StreamType t) { + switch (t) { + case StreamType::REQUEST_RESPONSE: + return "REQUEST_RESPONSE"; + case StreamType::STREAM: + return "STREAM"; + case StreamType::CHANNEL: + return "CHANNEL"; + case StreamType::FNF: + return "FNF"; + default: + DCHECK(false); + return "(invalid StreamType)"; + } +} + +std::ostream& operator<<(std::ostream& os, StreamType t) { + return os << toString(t); +} + +std::ostream& operator<<(std::ostream& os, RSocketMode mode) { + switch (mode) { + case RSocketMode::CLIENT: + return os << "CLIENT"; + case RSocketMode::SERVER: + return os << "SERVER"; + } + DLOG(FATAL) << "Invalid RSocketMode"; + return os << "INVALID_RSOCKET_MODE"; +} + +std::string to_string(StreamCompletionSignal signal) { + switch (signal) { + case StreamCompletionSignal::COMPLETE: + return "COMPLETE"; + case StreamCompletionSignal::CANCEL: + return "CANCEL"; + case StreamCompletionSignal::ERROR: + return "ERROR"; + case StreamCompletionSignal::APPLICATION_ERROR: + return "APPLICATION_ERROR"; + case StreamCompletionSignal::INVALID_SETUP: + return "INVALID_SETUP"; + case StreamCompletionSignal::UNSUPPORTED_SETUP: + return "UNSUPPORTED_SETUP"; + case StreamCompletionSignal::REJECTED_SETUP: + return "REJECTED_SETUP"; + case StreamCompletionSignal::CONNECTION_ERROR: + return "CONNECTION_ERROR"; + case StreamCompletionSignal::CONNECTION_END: + return "CONNECTION_END"; + case StreamCompletionSignal::SOCKET_CLOSED: + return "SOCKET_CLOSED"; + } + // this should be never hit because the switch is over all cases + LOG(FATAL) << "unknown StreamCompletionSignal=" << static_cast<int>(signal); + return "<unknown StreamCompletionSignal>"; +} + +std::ostream& operator<<(std::ostream& os, StreamCompletionSignal signal) { + return os << to_string(signal); +} + +StreamInterruptedException::StreamInterruptedException(int _terminatingSignal) + : std::runtime_error(getTerminatingSignalErrorMessage(_terminatingSignal)), + terminatingSignal(_terminatingSignal) {} + +std::string humanify(std::unique_ptr<folly::IOBuf> const& buf) { + std::string ret; + size_t cursor = 0; + + for (const auto range : *buf) { + for (const unsigned char chr : range) { + if (cursor >= 20) + goto outer; + ret += chr; + cursor++; + } + } +outer: + + return folly::humanify(ret); +} +std::string hexDump(folly::StringPiece s) { + return folly::hexDump(s.data(), std::min<size_t>(0xFF, s.size())); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/Common.h b/ios/Pods/Flipper-RSocket/rsocket/internal/Common.h new file mode 100644 index 000000000..a096a5545 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/Common.h @@ -0,0 +1,95 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <chrono> +#include <cstdint> +#include <functional> +#include <memory> +#include <stdexcept> +#include <string> +#include <vector> + +// +// this file includes all PUBLIC common types. +// + +namespace folly { +class exception_wrapper; +class IOBuf; + +template <typename T> +class Range; +typedef Range<const char*> StringPiece; +} // namespace folly + +namespace rsocket { + +/// A unique identifier of a stream. +using StreamId = uint32_t; + +constexpr std::chrono::seconds kDefaultKeepaliveInterval{5}; + +constexpr int64_t kMaxRequestN = std::numeric_limits<int32_t>::max(); + +std::string humanify(std::unique_ptr<folly::IOBuf> const&); +std::string hexDump(folly::StringPiece s); + +/// Indicates the reason why the stream stateMachine received a terminal signal +/// from the connection. +enum class StreamCompletionSignal { + CANCEL, + COMPLETE, + APPLICATION_ERROR, + ERROR, + INVALID_SETUP, + UNSUPPORTED_SETUP, + REJECTED_SETUP, + CONNECTION_ERROR, + CONNECTION_END, + SOCKET_CLOSED, +}; + +enum class RSocketMode : uint8_t { SERVER, CLIENT }; + +std::ostream& operator<<(std::ostream&, RSocketMode); + +enum class StreamType { + REQUEST_RESPONSE, + STREAM, + CHANNEL, + FNF, +}; + +folly::StringPiece toString(StreamType); +std::ostream& operator<<(std::ostream&, StreamType); + +enum class RequestOriginator { + LOCAL, + REMOTE, +}; + +std::string to_string(StreamCompletionSignal); +std::ostream& operator<<(std::ostream&, StreamCompletionSignal); + +class StreamInterruptedException : public std::runtime_error { + public: + explicit StreamInterruptedException(int _terminatingSignal); + const int terminatingSignal; +}; + +class FrameSink; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.cpp new file mode 100644 index 000000000..0ed32db6a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.cpp @@ -0,0 +1,108 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/ConnectionSet.h" + +#include "rsocket/statemachine/RSocketStateMachine.h" + +#include <folly/io/async/EventBase.h> + +namespace rsocket { + +ConnectionSet::ConnectionSet() {} + +ConnectionSet::~ConnectionSet() { + if (!shutDown_) { + shutdownAndWait(); + } +} + +void ConnectionSet::shutdownAndWait() { + VLOG(1) << "Started ConnectionSet::shutdownAndWait"; + shutDown_ = true; + + SCOPE_EXIT { + VLOG(1) << "Finished ConnectionSet::shutdownAndWait"; + }; + + StateMachineMap map; + + // Move all the connections out of the synchronized map so we don't block + // while closing the state machines. + { + const auto locked = machines_.lock(); + if (locked->empty()) { + VLOG(2) << "No connections to close, early exit"; + return; + } + + targetRemoves_ = removes_ + locked->size(); + map.swap(*locked); + } + + VLOG(2) << "Need to close " << map.size() << " connections"; + + for (auto& kv : map) { + auto rsocket = std::move(kv.first); + auto evb = kv.second; + + const auto close = [rs = std::move(rsocket)] { + rs->close({}, StreamCompletionSignal::SOCKET_CLOSED); + }; + + // We could be closing on the same thread as the state machine. In that + // case, close the state machine inline, otherwise we hang. + if (evb->isInEventBaseThread()) { + VLOG(3) << "Closing connection inline"; + close(); + } else { + VLOG(3) << "Closing connection asynchronously"; + evb->runInEventBaseThread(close); + } + } + + VLOG(2) << "Waiting for connections to close"; + shutdownDone_.wait(); + VLOG(2) << "Connections have closed"; +} + +bool ConnectionSet::insert( + std::shared_ptr<RSocketStateMachine> machine, + folly::EventBase* evb) { + VLOG(4) << "insert(" << machine.get() << ", " << evb << ")"; + + if (shutDown_) { + return false; + } + machines_.lock()->emplace(std::move(machine), evb); + return true; +} + +void ConnectionSet::remove(RSocketStateMachine& machine) { + VLOG(4) << "remove(" << &machine << ")"; + + const auto locked = machines_.lock(); + auto const result = locked->erase(machine.shared_from_this()); + DCHECK_LE(result, 1); + + if (++removes_ == targetRemoves_) { + shutdownDone_.post(); + } +} + +size_t ConnectionSet::size() const { + return machines_.lock()->size(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.h new file mode 100644 index 000000000..b679b96f2 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ConnectionSet.h @@ -0,0 +1,60 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Synchronized.h> +#include <folly/synchronization/Baton.h> + +#include <memory> +#include <mutex> +#include <unordered_map> + +#include "rsocket/statemachine/RSocketStateMachine.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +/// Set of RSocketStateMachine objects. Stores them until they call +/// RSocketStateMachine::close(). +/// +/// Also tracks which EventBase is controlling each state machine so that they +/// can be closed on the correct thread. +class ConnectionSet : public RSocketStateMachine::CloseCallback { + public: + ConnectionSet(); + virtual ~ConnectionSet(); + + bool insert(std::shared_ptr<RSocketStateMachine>, folly::EventBase*); + void remove(RSocketStateMachine&) override; + + size_t size() const; + + void shutdownAndWait(); + + private: + using StateMachineMap = std:: + unordered_map<std::shared_ptr<RSocketStateMachine>, folly::EventBase*>; + + folly::Synchronized<StateMachineMap, std::mutex> machines_; + folly::Baton<> shutdownDone_; + size_t removes_{0}; + size_t targetRemoves_{0}; + std::atomic<bool> shutDown_{false}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.cpp new file mode 100644 index 000000000..6fdaa39d0 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/KeepaliveTimer.h" + +namespace rsocket { + +KeepaliveTimer::KeepaliveTimer( + std::chrono::milliseconds period, + folly::EventBase& eventBase) + : eventBase_(eventBase), + generation_(std::make_shared<uint32_t>(0)), + period_(period) {} + +KeepaliveTimer::~KeepaliveTimer() { + stop(); +} + +std::chrono::milliseconds KeepaliveTimer::keepaliveTime() const { + return period_; +} + +void KeepaliveTimer::schedule() { + const auto scheduledGeneration = *generation_; + const auto generation = generation_; + eventBase_.runAfterDelay( + [this, + wpConnection = std::weak_ptr<FrameSink>(connection_), + generation, + scheduledGeneration]() { + auto spConnection = wpConnection.lock(); + if (!spConnection) { + return; + } + if (*generation == scheduledGeneration) { + sendKeepalive(*spConnection); + } + }, + static_cast<uint32_t>(keepaliveTime().count())); +} + +void KeepaliveTimer::sendKeepalive(FrameSink& sink) { + if (pending_) { + stop(); + // TODO: we need to use max lifetime from the setup frame for this + sink.disconnectOrCloseWithError( + Frame_ERROR::connectionError("no response to keepalive")); + } else { + // this must happen before sendKeepalive as it can potentially result in + // stop() being called + pending_ = true; + sink.sendKeepalive(); + schedule(); + } +} + +// must be called from the same thread as start +void KeepaliveTimer::stop() { + *generation_ += 1; + pending_ = false; + connection_.reset(); +} + +// must be called from the same thread as stop +void KeepaliveTimer::start(const std::shared_ptr<FrameSink>& connection) { + connection_ = connection; + *generation_ += 1; + DCHECK(!pending_); + + schedule(); +} + +void KeepaliveTimer::keepaliveReceived() { + pending_ = false; +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h b/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h new file mode 100644 index 000000000..51bb6c3c2 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h @@ -0,0 +1,48 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "rsocket/statemachine/RSocketStateMachine.h" + +namespace rsocket { + +class KeepaliveTimer { + public: + KeepaliveTimer(std::chrono::milliseconds period, folly::EventBase& eventBase); + + ~KeepaliveTimer(); + + std::chrono::milliseconds keepaliveTime() const; + + void schedule(); + + void stop(); + + void start(const std::shared_ptr<FrameSink>& connection); + + void sendKeepalive(FrameSink& sink); + + void keepaliveReceived(); + + private: + std::shared_ptr<FrameSink> connection_; + folly::EventBase& eventBase_; + const std::shared_ptr<uint32_t> generation_; + const std::chrono::milliseconds period_; + std::atomic<bool> pending_{false}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.cpp new file mode 100644 index 000000000..d534657c8 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.cpp @@ -0,0 +1,88 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/ScheduledRSocketResponder.h" + +#include <folly/io/async/EventBase.h> + +#include "rsocket/internal/ScheduledSingleObserver.h" +#include "rsocket/internal/ScheduledSubscriber.h" + +namespace rsocket { + +ScheduledRSocketResponder::ScheduledRSocketResponder( + std::shared_ptr<RSocketResponder> inner, + folly::EventBase& eventBase) + : inner_(std::move(inner)), eventBase_(eventBase) {} + +std::shared_ptr<yarpl::single::Single<Payload>> +ScheduledRSocketResponder::handleRequestResponse( + Payload request, + StreamId streamId) { + auto innerFlowable = + inner_->handleRequestResponse(std::move(request), streamId); + return yarpl::single::Singles::create<Payload>( + [innerFlowable = std::move(innerFlowable), eventBase = &eventBase_]( + std::shared_ptr<yarpl::single::SingleObserver<Payload>> observer) { + innerFlowable->subscribe( + std::make_shared<ScheduledSingleObserver<Payload>>( + std::move(observer), *eventBase)); + }); +} + +std::shared_ptr<yarpl::flowable::Flowable<Payload>> +ScheduledRSocketResponder::handleRequestStream( + Payload request, + StreamId streamId) { + auto innerFlowable = + inner_->handleRequestStream(std::move(request), streamId); + return yarpl::flowable::internal::flowableFromSubscriber<Payload>( + [innerFlowable = std::move(innerFlowable), eventBase = &eventBase_]( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + innerFlowable->subscribe(std::make_shared<ScheduledSubscriber<Payload>>( + std::move(subscriber), *eventBase)); + }); +} + +std::shared_ptr<yarpl::flowable::Flowable<Payload>> +ScheduledRSocketResponder::handleRequestChannel( + Payload request, + std::shared_ptr<yarpl::flowable::Flowable<Payload>> requestStream, + StreamId streamId) { + auto requestStreamFlowable = + yarpl::flowable::internal::flowableFromSubscriber<Payload>( + [requestStream = std::move(requestStream), eventBase = &eventBase_]( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> + subscriber) { + requestStream->subscribe( + std::make_shared<ScheduledSubscriptionSubscriber<Payload>>( + std::move(subscriber), *eventBase)); + }); + auto innerFlowable = inner_->handleRequestChannel( + std::move(request), std::move(requestStreamFlowable), streamId); + return yarpl::flowable::internal::flowableFromSubscriber<Payload>( + [innerFlowable = std::move(innerFlowable), eventBase = &eventBase_]( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + innerFlowable->subscribe(std::make_shared<ScheduledSubscriber<Payload>>( + std::move(subscriber), *eventBase)); + }); +} + +void ScheduledRSocketResponder::handleFireAndForget( + Payload request, + StreamId streamId) { + inner_->handleFireAndForget(std::move(request), streamId); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h new file mode 100644 index 000000000..fe9039dcc --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h @@ -0,0 +1,55 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketResponder.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +// +// A decorated RSocketResponder object which schedules the calls from +// application code to RSocket on the provided EventBase +// +class ScheduledRSocketResponder : public RSocketResponder { + public: + ScheduledRSocketResponder( + std::shared_ptr<RSocketResponder> inner, + folly::EventBase& eventBase); + + std::shared_ptr<yarpl::single::Single<Payload>> handleRequestResponse( + Payload request, + StreamId streamId) override; + + std::shared_ptr<yarpl::flowable::Flowable<Payload>> handleRequestStream( + Payload request, + StreamId streamId) override; + + std::shared_ptr<yarpl::flowable::Flowable<Payload>> handleRequestChannel( + Payload request, + std::shared_ptr<yarpl::flowable::Flowable<Payload>> requestStream, + StreamId streamId) override; + + void handleFireAndForget(Payload request, StreamId streamId) override; + + private: + const std::shared_ptr<RSocketResponder> inner_; + folly::EventBase& eventBase_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h new file mode 100644 index 000000000..167b5458e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h @@ -0,0 +1,116 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "rsocket/internal/ScheduledSingleSubscription.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/Singles.h" + +namespace rsocket { + +// +// A decorated RSocketResponder object which schedules the calls from +// application code to RSocket on the provided EventBase +// This class should be used to wrap a SingleObserver returned to the +// application code so that calls to on{Subscribe,Success,Error} are +// scheduled on the right EventBase. +// +template <typename T> +class ScheduledSingleObserver : public yarpl::single::SingleObserver<T> { + public: + ScheduledSingleObserver( + std::shared_ptr<yarpl::single::SingleObserver<T>> observer, + folly::EventBase& eventBase) + : inner_(std::move(observer)), eventBase_(eventBase) {} + + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription> + subscription) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onSubscribe(std::move(subscription)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, subscription = std::move(subscription)] { + inner->onSubscribe(std::move(subscription)); + }); + } + } + + // No further calls to the subscription after this method is invoked. + void onSuccess(T value) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onSuccess(std::move(value)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, value = std::move(value)]() mutable { + inner->onSuccess(std::move(value)); + }); + } + } + + // No further calls to the subscription after this method is invoked. + void onError(folly::exception_wrapper ex) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onError(std::move(ex)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, ex = std::move(ex)]() mutable { + inner->onError(std::move(ex)); + }); + } + } + + private: + const std::shared_ptr<yarpl::single::SingleObserver<T>> inner_; + folly::EventBase& eventBase_; +}; + +// +// This class should be used to wrap a SingleObserver provided from the +// application code to the library. The library's Subscriber provided to the +// application code will be wrapped with a scheduled subscription to make the +// call to Subscription::cancel safe. +// +template <typename T> +class ScheduledSubscriptionSingleObserver + : public yarpl::single::SingleObserver<T> { + public: + ScheduledSubscriptionSingleObserver( + std::shared_ptr<yarpl::single::SingleObserver<T>> observer, + folly::EventBase& eventBase) + : inner_(std::move(observer)), eventBase_(eventBase) {} + + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription> + subscription) override { + inner_->onSubscribe(std::make_shared<ScheduledSingleSubscription>( + std::move(subscription), eventBase_)); + } + + // No further calls to the subscription after this method is invoked. + void onSuccess(T value) override { + inner_->onSuccess(std::move(value)); + } + + // No further calls to the subscription after this method is invoked. + void onError(folly::exception_wrapper ex) override { + inner_->onError(std::move(ex)); + } + + private: + const std::shared_ptr<yarpl::single::SingleObserver<T>> inner_; + folly::EventBase& eventBase_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.cpp new file mode 100644 index 000000000..b56f76c0d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/ScheduledSingleSubscription.h" + +#include <folly/io/async/EventBase.h> + +namespace rsocket { + +ScheduledSingleSubscription::ScheduledSingleSubscription( + std::shared_ptr<yarpl::single::SingleSubscription> inner, + folly::EventBase& eventBase) + : inner_(std::move(inner)), eventBase_(eventBase) {} + +void ScheduledSingleSubscription::cancel() { + if (eventBase_.isInEventBaseThread()) { + inner_->cancel(); + } else { + eventBase_.runInEventBaseThread([inner = inner_] { inner->cancel(); }); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h new file mode 100644 index 000000000..1d29412e4 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h @@ -0,0 +1,42 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/single/SingleSubscription.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +// +// A decorator of the SingleSubscription object which schedules the method calls +// on the provided EventBase +// +class ScheduledSingleSubscription : public yarpl::single::SingleSubscription { + public: + ScheduledSingleSubscription( + std::shared_ptr<yarpl::single::SingleSubscription> inner, + folly::EventBase& eventBase); + + void cancel() override; + + private: + const std::shared_ptr<yarpl::single::SingleSubscription> inner_; + folly::EventBase& eventBase_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h new file mode 100644 index 000000000..f73ee44f3 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h @@ -0,0 +1,132 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/internal/ScheduledSubscription.h" + +#include <folly/io/async/EventBase.h> + +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +// +// A decorator of the Subscriber object which schedules the method calls on the +// provided EventBase. +// This class should be used to wrap a Subscriber returned to the application +// code so that calls to on{Subscribe,Next,Complete,Error} are scheduled on the +// right EventBase. +// + +template <typename T> +class ScheduledSubscriber : public yarpl::flowable::Subscriber<T> { + public: + ScheduledSubscriber( + std::shared_ptr<yarpl::flowable::Subscriber<T>> inner, + folly::EventBase& eventBase) + : inner_(std::move(inner)), eventBase_(eventBase) {} + + void onSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> subscription) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onSubscribe(std::move(subscription)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, subscription = std::move(subscription)] { + inner->onSubscribe(std::move(subscription)); + }); + } + } + + // No further calls to the subscription after this method is invoked. + void onComplete() override { + if (eventBase_.isInEventBaseThread()) { + inner_->onComplete(); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_] { inner->onComplete(); }); + } + } + + void onError(folly::exception_wrapper ex) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onError(std::move(ex)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, ex = std::move(ex)]() mutable { + inner->onError(std::move(ex)); + }); + } + } + + void onNext(T value) override { + if (eventBase_.isInEventBaseThread()) { + inner_->onNext(std::move(value)); + } else { + eventBase_.runInEventBaseThread( + [inner = inner_, value = std::move(value)]() mutable { + inner->onNext(std::move(value)); + }); + } + } + + private: + const std::shared_ptr<yarpl::flowable::Subscriber<T>> inner_; + folly::EventBase& eventBase_; +}; + +// +// A decorator of a Subscriber object which schedules the method calls on the +// provided EventBase. +// This class is to wrap the Subscriber provided from the application code to +// the library. The Subscription passed to onSubscribe method needs to be +// wrapped in the ScheduledSubscription since the application code calls +// request and cancel from any thread. +// +template <typename T> +class ScheduledSubscriptionSubscriber : public yarpl::flowable::Subscriber<T> { + public: + ScheduledSubscriptionSubscriber( + std::shared_ptr<yarpl::flowable::Subscriber<T>> inner, + folly::EventBase& eventBase) + : inner_(std::move(inner)), eventBase_(eventBase) {} + + void onSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> sub) override { + auto scheduled = + std::make_shared<ScheduledSubscription>(std::move(sub), eventBase_); + inner_->onSubscribe(std::move(scheduled)); + } + + void onNext(T value) override { + inner_->onNext(std::move(value)); + } + + void onComplete() override { + auto inner = std::move(inner_); + inner->onComplete(); + } + + void onError(folly::exception_wrapper ew) override { + auto inner = std::move(inner_); + inner->onError(std::move(ew)); + } + + private: + std::shared_ptr<yarpl::flowable::Subscriber<T>> inner_; + folly::EventBase& eventBase_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.cpp new file mode 100644 index 000000000..a92687aa9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/ScheduledSubscription.h" + +namespace rsocket { + +ScheduledSubscription::ScheduledSubscription( + std::shared_ptr<yarpl::flowable::Subscription> inner, + folly::EventBase& eventBase) + : inner_{std::move(inner)}, eventBase_{eventBase} {} + +void ScheduledSubscription::request(int64_t n) { + if (eventBase_.isInEventBaseThread()) { + inner_->request(n); + } else { + eventBase_.runInEventBaseThread([inner = inner_, n] { inner->request(n); }); + } +} + +void ScheduledSubscription::cancel() { + if (eventBase_.isInEventBaseThread()) { + auto inner = std::move(inner_); + inner->cancel(); + } else { + eventBase_.runInEventBaseThread( + [inner = std::move(inner_)] { inner->cancel(); }); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h new file mode 100644 index 000000000..14c058cb4 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h @@ -0,0 +1,39 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "yarpl/flowable/Subscription.h" + +namespace rsocket { + +// A wrapper over Subscription that schedules all of the subscription's methods +// on an EventBase. +class ScheduledSubscription : public yarpl::flowable::Subscription { + public: + ScheduledSubscription( + std::shared_ptr<yarpl::flowable::Subscription>, + folly::EventBase&); + + void request(int64_t) override; + void cancel() override; + + private: + std::shared_ptr<yarpl::flowable::Subscription> inner_; + folly::EventBase& eventBase_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.cpp new file mode 100644 index 000000000..828e4dbdd --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.cpp @@ -0,0 +1,228 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/SetupResumeAcceptor.h" + +#include <folly/ExceptionWrapper.h> +#include <folly/io/async/EventBase.h> + +#include "rsocket/framing/Frame.h" +#include "rsocket/framing/FrameProcessor.h" +#include "rsocket/framing/FrameSerializer.h" + +namespace rsocket { + +/// Subscriber that owns a connection, sets itself as that connection's input, +/// and reads out a single frame before cancelling. +class SetupResumeAcceptor::OneFrameSubscriber final + : public yarpl::flowable::BaseSubscriber<std::unique_ptr<folly::IOBuf>> { + public: + OneFrameSubscriber( + SetupResumeAcceptor& acceptor, + std::unique_ptr<DuplexConnection> connection, + SetupResumeAcceptor::OnSetup onSetup, + SetupResumeAcceptor::OnResume onResume) + : acceptor_{acceptor}, + connection_{std::move(connection)}, + onSetup_{std::move(onSetup)}, + onResume_{std::move(onResume)} { + DCHECK(connection_); + DCHECK(onSetup_); + DCHECK(onResume_); + DCHECK(acceptor_.inOwnerThread()); + } + + void setInput() { + DCHECK(acceptor_.inOwnerThread()); + connection_->setInput(ref_from_this(this)); + } + + /// Shut down the DuplexConnection, breaking the cycle between it and this + /// subscriber. Expects the DuplexConnection's destructor to call + /// onComplete/onError on its input subscriber (this). + void close() { + auto self = ref_from_this(this); + connection_.reset(); + } + + void onSubscribeImpl() override { + DCHECK(acceptor_.inOwnerThread()); + this->request(1); + } + + void onNextImpl(std::unique_ptr<folly::IOBuf> buf) override { + DCHECK(connection_) << "OneFrameSubscriber received more than one frame"; + DCHECK(acceptor_.inOwnerThread()); + + this->cancel(); // calls onTerminateImpl + + acceptor_.processFrame( + std::move(connection_), + std::move(buf), + std::move(onSetup_), + std::move(onResume_)); + } + + void onCompleteImpl() override {} + void onErrorImpl(folly::exception_wrapper) override {} + + void onTerminateImpl() override { + DCHECK(acceptor_.inOwnerThread()); + acceptor_.remove(ref_from_this(this)); + } + + private: + SetupResumeAcceptor& acceptor_; + std::unique_ptr<DuplexConnection> connection_; + SetupResumeAcceptor::OnSetup onSetup_; + SetupResumeAcceptor::OnResume onResume_; +}; + +SetupResumeAcceptor::SetupResumeAcceptor(folly::EventBase* eventBase) + : eventBase_{eventBase} { + CHECK(eventBase_); +} + +SetupResumeAcceptor::~SetupResumeAcceptor() { + close().get(); +} + +void SetupResumeAcceptor::processFrame( + std::unique_ptr<DuplexConnection> connection, + std::unique_ptr<folly::IOBuf> buf, + SetupResumeAcceptor::OnSetup onSetup, + SetupResumeAcceptor::OnResume onResume) { + DCHECK(inOwnerThread()); + DCHECK(connection); + + if (closed_) { + return; + } + + const auto serializer = FrameSerializer::createAutodetectedSerializer(*buf); + if (!serializer) { + VLOG(2) << "Unable to detect protocol version"; + return; + } + + switch (serializer->peekFrameType(*buf)) { + case FrameType::SETUP: { + Frame_SETUP frame; + if (!serializer->deserializeFrom(frame, std::move(buf))) { + constexpr auto msg = "Cannot decode SETUP frame"; + auto err = serializer->serializeOut(Frame_ERROR::connectionError(msg)); + connection->send(std::move(err)); + break; + } + + VLOG(3) << "In: " << frame; + + SetupParameters params; + frame.moveToSetupPayload(params); + + if (serializer->protocolVersion() != params.protocolVersion) { + constexpr auto msg = "SETUP frame has invalid protocol version"; + auto err = serializer->serializeOut(Frame_ERROR::invalidSetup(msg)); + connection->send(std::move(err)); + break; + } + + onSetup(std::move(connection), std::move(params)); + break; + } + + case FrameType::RESUME: { + Frame_RESUME frame; + if (!serializer->deserializeFrom(frame, std::move(buf))) { + constexpr auto msg = "Cannot decode RESUME frame"; + auto err = serializer->serializeOut(Frame_ERROR::connectionError(msg)); + connection->send(std::move(err)); + break; + } + + VLOG(3) << "In: " << frame; + + ResumeParameters params( + std::move(frame.token_), + frame.lastReceivedServerPosition_, + frame.clientPosition_, + ProtocolVersion(frame.versionMajor_, frame.versionMinor_)); + + if (serializer->protocolVersion() != params.protocolVersion) { + constexpr auto msg = "RESUME frame has invalid protocol version"; + auto err = serializer->serializeOut(Frame_ERROR::rejectedResume(msg)); + connection->send(std::move(err)); + break; + } + + onResume(std::move(connection), std::move(params)); + break; + } + + default: { + constexpr auto msg = "Invalid frame, expected SETUP/RESUME"; + auto err = serializer->serializeOut(Frame_ERROR::connectionError(msg)); + connection->send(std::move(err)); + break; + } + } +} + +void SetupResumeAcceptor::accept( + std::unique_ptr<DuplexConnection> connection, + OnSetup onSetup, + OnResume onResume) { + DCHECK(inOwnerThread()); + + if (closed_) { + return; + } + + const auto subscriber = std::make_shared<OneFrameSubscriber>( + *this, std::move(connection), std::move(onSetup), std::move(onResume)); + connections_.insert(subscriber); + subscriber->setInput(); +} + +void SetupResumeAcceptor::remove( + const std::shared_ptr<SetupResumeAcceptor::OneFrameSubscriber>& + subscriber) { + DCHECK(inOwnerThread()); + connections_.erase(subscriber); +} + +folly::Future<folly::Unit> SetupResumeAcceptor::close() { + if (inOwnerThread()) { + closeAll(); + return folly::makeFuture(); + } + return folly::via(eventBase_, [this] { closeAll(); }); +} + +void SetupResumeAcceptor::closeAll() { + DCHECK(inOwnerThread()); + + closed_ = true; + + auto connections = std::move(connections_); + for (auto& connection : connections) { + connection->close(); + } +} + +bool SetupResumeAcceptor::inOwnerThread() const { + return eventBase_->isInEventBaseThread(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h b/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h new file mode 100644 index 000000000..7ae246e78 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h @@ -0,0 +1,90 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <memory> +#include <unordered_set> + +#include <folly/Function.h> +#include <folly/futures/Future.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/RSocketParameters.h" +#include "yarpl/Refcounted.h" + +namespace folly { +class EventBase; +class Executor; +class IOBuf; +class exception_wrapper; +} // namespace folly + +namespace rsocket { + +/// Acceptor of DuplexConnections that lets us decide whether the connection is +/// trying to setup a new connection or resume an existing one. +/// +/// An instance of this class must be tied to a specific thread, as the +/// SetupResumeAcceptor::accept() entry point is not thread-safe. +class SetupResumeAcceptor final { + public: + using OnSetup = folly::Function< + void(std::unique_ptr<DuplexConnection>, SetupParameters) noexcept>; + using OnResume = folly::Function< + void(std::unique_ptr<DuplexConnection>, ResumeParameters) noexcept>; + + explicit SetupResumeAcceptor(folly::EventBase*); + ~SetupResumeAcceptor(); + + /// Wait for and process the first frame on a DuplexConnection, calling the + /// appropriate callback when the frame is received. Not thread-safe. + void accept(std::unique_ptr<DuplexConnection>, OnSetup, OnResume); + + /// Close all open connections, and prevent new ones from being accepted. Can + /// be called from any thread, and also after the EventBase has been + /// destroyed, provided we know the ID of the owner thread. + folly::Future<folly::Unit> close(); + + private: + class OneFrameSubscriber; + + void processFrame( + std::unique_ptr<DuplexConnection>, + std::unique_ptr<folly::IOBuf>, + OnSetup, + OnResume); + + /// Remove a OneFrameSubscriber from the set. + void remove(const std::shared_ptr<OneFrameSubscriber>&); + + /// Close all open connections. + void closeAll(); + + /// Whether we're running in the thread that owns this object. If the ctor + /// specified an owner thread ID, then this will not access the EventBase + /// pointer. + /// + /// Useful if the EventBase has been destroyed but we still want to do some + /// work within the owner thread. + bool inOwnerThread() const; + + std::unordered_set<std::shared_ptr<OneFrameSubscriber>> connections_; + + bool closed_{false}; + + folly::EventBase* const eventBase_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/StackTraceUtils.h b/ios/Pods/Flipper-RSocket/rsocket/internal/StackTraceUtils.h new file mode 100644 index 000000000..b99d5b943 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/StackTraceUtils.h @@ -0,0 +1,29 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <string> + +namespace rsocket { + +std::string getStackTrace(); + +#ifndef REACTIVE_SOCKET_EXTERNAL_STACK_TRACE_UTILS +inline std::string getStackTrace() { + return ""; +} +#endif + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.cpp new file mode 100644 index 000000000..f745a9365 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "SwappableEventBase.h" + +namespace rsocket { + +bool SwappableEventBase::runInEventBaseThread(CbFunc cb) { + const std::lock_guard<std::mutex> l(hasSebDtored_->l_); + + if (this->isSwapping()) { + queued_.push_back(std::move(cb)); + return false; + } + + eb_->runInEventBaseThread( + [eb = eb_, cb_ = std::move(cb)]() mutable { return cb_(*eb); }); + + return true; +} + +void SwappableEventBase::setEventBase(folly::EventBase& newEb) { + const std::lock_guard<std::mutex> l(hasSebDtored_->l_); + + auto const alreadySwapping = this->isSwapping(); + nextEb_ = &newEb; + if (alreadySwapping) { + return; + } + + eb_->runInEventBaseThread([this, hasSebDtored = hasSebDtored_]() { + const std::lock_guard<std::mutex> lInner(hasSebDtored->l_); + if (hasSebDtored->destroyed_) { + // SEB was destroyed, any queued callbacks were appended to the old eb_ + return; + } + + eb_ = nextEb_; + nextEb_ = nullptr; + + // enqueue tasks that were being buffered while this was waiting + // for the previous EB to drain + for (auto& cb : queued_) { + eb_->runInEventBaseThread( + [cb = std::move(cb), eb = eb_]() mutable { return cb(*eb); }); + } + + queued_.clear(); + }); +} + +bool SwappableEventBase::isSwapping() const { + return nextEb_ != nullptr; +} + +SwappableEventBase::~SwappableEventBase() { + const std::lock_guard<std::mutex> l(hasSebDtored_->l_); + + hasSebDtored_->destroyed_ = true; + for (auto& cb : queued_) { + eb_->runInEventBaseThread( + [cb = std::move(cb), eb = eb_]() mutable { return cb(*eb); }); + } + queued_.clear(); +} + +} /* namespace rsocket */ diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.h b/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.h new file mode 100644 index 000000000..456eb67bf --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/SwappableEventBase.h @@ -0,0 +1,81 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Function.h> +#include <folly/io/async/EventBase.h> +#include <mutex> + +namespace rsocket { + +// SwappableEventBase provides an interface similar to EventBase, allowing +// an underlying EventBase to be changed, and to force callbacks to be +// executed in serial order regardless of which underlying EventBase they are +// enqueued on. +class SwappableEventBase final { + // std::mutex doesn't like being in a std::pair + struct MutexBoolPair { + // lock for synchronization on destroyed_, and all members of the parent SEB + std::mutex l_; + // has the SEB's destructor ran? + bool destroyed_{false}; + }; + + public: + using CbFunc = folly::Function<void(folly::EventBase&)>; + + explicit SwappableEventBase(folly::EventBase& eb) + : eb_(&eb), + nextEb_(nullptr), + hasSebDtored_(std::make_shared<MutexBoolPair>()) {} + + // Run or enqueue 'cb', in order with all prior calls to runInEventBaseThread + // If setEventBase has been called, and the prior EventBase is still + // processing tasks, runInEventBaseThread will queue tasks until the old EB's + // tasks have all completed. After that, SwappableEventBase will enqueue + // buffered tasks on the last EB set via setEventBase. + // + // Callbacks take a single parameter: the underlying EventBase + // that the callback is executing on. + bool runInEventBaseThread(CbFunc cb); + + // Sets the EventBase to enqueue callbacks on, once the current EventBase has + // drained + void setEventBase(folly::EventBase& newEb); + + // SwappableEventBase will enqueue tasks on the old eventbase if + // there are any pending by the time the SEB is destroyed + ~SwappableEventBase(); + + private: + folly::EventBase* eb_; + folly::EventBase* nextEb_; // also indicate if we're in the middle of a swap + + // is the SwappableEventBase waiting for the current EventBase to finish + // draining? + bool isSwapping() const; + + // shared data between the SwappableEventBase and anyone else holding + // a reference to the SEB (eg, swapping lambda in setEventBase) to avoid + // accessing a dangling pointer in the case where the SEB has already + // had its destructor run + mutable std::shared_ptr<MutexBoolPair> hasSebDtored_; + + // tasks enqueued with runInEventBaseThread while the SEB is waiting for + // the old EventBase* eb_ to drain + std::vector<CbFunc> queued_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.cpp b/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.cpp new file mode 100644 index 000000000..c67de86e9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.cpp @@ -0,0 +1,173 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/internal/WarmResumeManager.h" + +#include <algorithm> + +namespace rsocket { + +WarmResumeManager::~WarmResumeManager() { + clearFrames(lastSentPosition_); +} + +void WarmResumeManager::trackReceivedFrame( + size_t frameLength, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) { + if (shouldTrackFrame(frameType)) { + VLOG(6) << "Track received frame " << frameType << " StreamId: " << streamId + << " Allowance: " << consumerAllowance; + impliedPosition_ += frameLength; + } +} + +void WarmResumeManager::trackSentFrame( + const folly::IOBuf& serializedFrame, + FrameType frameType, + StreamId, + size_t consumerAllowance) { + if (shouldTrackFrame(frameType)) { + // TODO(tmont): this could be expensive, find a better way to get length + const auto frameDataLength = serializedFrame.computeChainDataLength(); + + VLOG(6) << "Track sent frame " << frameType + << " Allowance: " << consumerAllowance; + // If the frame is too huge, we don't cache it. + // We empty the entire cache instead. + if (frameDataLength > capacity_) { + resetUpToPosition(lastSentPosition_); + lastSentPosition_ += frameDataLength; + firstSentPosition_ += frameDataLength; + DCHECK(firstSentPosition_ == lastSentPosition_); + DCHECK(size_ == 0); + return; + } + + addFrame(serializedFrame, frameDataLength); + lastSentPosition_ += frameDataLength; + } +} + +void WarmResumeManager::resetUpToPosition(ResumePosition position) { + if (position <= firstSentPosition_) { + return; + } + + if (position > lastSentPosition_) { + position = lastSentPosition_; + } + + clearFrames(position); + + firstSentPosition_ = position; + DCHECK(frames_.empty() || frames_.front().first == firstSentPosition_); +} + +bool WarmResumeManager::isPositionAvailable(ResumePosition position) const { + return (lastSentPosition_ == position) || + std::binary_search( + frames_.begin(), + frames_.end(), + std::make_pair(position, std::unique_ptr<folly::IOBuf>()), + [](decltype(frames_.back()) pairA, + decltype(frames_.back()) pairB) { + return pairA.first < pairB.first; + }); +} + +void WarmResumeManager::addFrame( + const folly::IOBuf& frame, + size_t frameDataLength) { + size_ += frameDataLength; + while (size_ > capacity_) { + evictFrame(); + } + frames_.emplace_back(lastSentPosition_, frame.clone()); + stats_->resumeBufferChanged(1, static_cast<int>(frameDataLength)); +} + +void WarmResumeManager::evictFrame() { + DCHECK(!frames_.empty()); + + const auto position = frames_.size() > 1 ? std::next(frames_.begin())->first + : lastSentPosition_; + resetUpToPosition(position); +} + +void WarmResumeManager::clearFrames(ResumePosition position) { + if (frames_.empty()) { + return; + } + DCHECK(position <= lastSentPosition_); + DCHECK(position >= firstSentPosition_); + + const auto end = std::lower_bound( + frames_.begin(), + frames_.end(), + position, + [](decltype(frames_.back()) pair, ResumePosition pos) { + return pair.first < pos; + }); + DCHECK(end == frames_.end() || end->first >= firstSentPosition_); + const auto pos = end == frames_.end() ? position : end->first; + stats_->resumeBufferChanged( + -static_cast<int>(std::distance(frames_.begin(), end)), + -static_cast<int>(pos - firstSentPosition_)); + + frames_.erase(frames_.begin(), end); + size_ -= static_cast<decltype(size_)>(pos - firstSentPosition_); +} + +void WarmResumeManager::sendFramesFromPosition( + ResumePosition position, + FrameTransport& frameTransport) const { + DCHECK(isPositionAvailable(position)); + + if (position == lastSentPosition_) { + // idle resumption + return; + } + + auto found = std::lower_bound( + frames_.begin(), + frames_.end(), + position, + [](decltype(frames_.back()) pair, ResumePosition pos) { + return pair.first < pos; + }); + + DCHECK(found != frames_.end()); + DCHECK(found->first == position); + + while (found != frames_.end()) { + frameTransport.outputFrameOrDrop(found->second->clone()); + found++; + } +} + +std::shared_ptr<ResumeManager> ResumeManager::makeEmpty() { + class Empty : public WarmResumeManager { + public: + Empty() : WarmResumeManager(nullptr, 0) {} + bool shouldTrackFrame(FrameType) const override { + return false; + } + }; + + return std::make_shared<Empty>(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.h b/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.h new file mode 100644 index 000000000..b14969ca9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/internal/WarmResumeManager.h @@ -0,0 +1,116 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <deque> + +#include <folly/lang/Assume.h> + +#include "rsocket/RSocketStats.h" +#include "rsocket/ResumeManager.h" + +namespace folly { +class IOBuf; +} + +namespace rsocket { + +class RSocketStateMachine; +class FrameTransport; + +class WarmResumeManager : public ResumeManager { + public: + explicit WarmResumeManager( + std::shared_ptr<RSocketStats> stats, + size_t capacity = DEFAULT_CAPACITY) + : stats_(std::move(stats)), capacity_(capacity) {} + ~WarmResumeManager(); + + void trackReceivedFrame( + size_t frameLength, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) override; + + void trackSentFrame( + const folly::IOBuf& serializedFrame, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) override; + + void resetUpToPosition(ResumePosition position) override; + + bool isPositionAvailable(ResumePosition position) const override; + + void sendFramesFromPosition( + ResumePosition position, + FrameTransport& transport) const override; + + ResumePosition firstSentPosition() const override { + return firstSentPosition_; + } + + ResumePosition lastSentPosition() const override { + return lastSentPosition_; + } + + ResumePosition impliedPosition() const override { + return impliedPosition_; + } + + // No action to perform for WarmResumeManager + void onStreamOpen(StreamId, RequestOriginator, std::string, StreamType) + override {} + + // No action to perform for WarmResumeManager + void onStreamClosed(StreamId) override {} + + const StreamResumeInfos& getStreamResumeInfos() const override { + LOG(FATAL) << "Not Implemented for Warm Resumption"; + folly::assume_unreachable(); + } + + StreamId getLargestUsedStreamId() const override { + LOG(FATAL) << "Not Implemented for Warm Resumption"; + folly::assume_unreachable(); + } + + size_t size() const { + return size_; + } + + protected: + void addFrame(const folly::IOBuf&, size_t); + void evictFrame(); + + // Called before clearing cached frames to update stats. + void clearFrames(ResumePosition position); + + const std::shared_ptr<RSocketStats> stats_; + + // Start position of the send buffer queue + ResumePosition firstSentPosition_{0}; + // End position of the send buffer queue + ResumePosition lastSentPosition_{0}; + // Inferred position of the rcvd frames + ResumePosition impliedPosition_{0}; + + std::deque<std::pair<ResumePosition, std::unique_ptr<folly::IOBuf>>> frames_; + + constexpr static size_t DEFAULT_CAPACITY = 1024 * 1024; // 1MB + const size_t capacity_; + size_t size_{0}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.cpp new file mode 100644 index 000000000..9b01dca56 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.cpp @@ -0,0 +1,155 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/ChannelRequester.h" + +namespace rsocket { + +void ChannelRequester::onSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> subscription) { + CHECK(!requested_); + publisherSubscribe(std::move(subscription)); + + if (hasInitialRequest_) { + initStream(std::move(request_)); + } +} + +void ChannelRequester::onNext(Payload request) { + if (!requested_) { + initStream(std::move(request)); + return; + } + + if (!publisherClosed()) { + writePayload(std::move(request)); + } +} + +// TODO: consolidate code in onCompleteImpl, onErrorImpl, cancelImpl +void ChannelRequester::onComplete() { + if (!requested_) { + endStream(StreamCompletionSignal::CANCEL); + removeFromWriter(); + return; + } + if (!publisherClosed()) { + publisherComplete(); + writeComplete(); + tryCompleteChannel(); + } +} + +void ChannelRequester::onError(folly::exception_wrapper ex) { + if (!requested_) { + endStream(StreamCompletionSignal::CANCEL); + removeFromWriter(); + return; + } + if (!publisherClosed()) { + publisherComplete(); + endStream(StreamCompletionSignal::ERROR); + writeApplicationError(ex.get_exception()->what()); + tryCompleteChannel(); + } +} + +void ChannelRequester::request(int64_t n) { + if (!requested_) { + // The initial request has not been sent out yet, hence we must accumulate + // the unsynchronised allowance, portion of which will be sent out with + // the initial request frame, and the rest will be dispatched via + // ConsumerBase:request (ultimately by sending REQUEST_N frames). + initialResponseAllowance_.add(n); + return; + } + ConsumerBase::generateRequest(n); +} + +void ChannelRequester::cancel() { + if (!requested_) { + endStream(StreamCompletionSignal::CANCEL); + removeFromWriter(); + return; + } + cancelConsumer(); + writeCancel(); + tryCompleteChannel(); +} + +void ChannelRequester::handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) { + CHECK(requested_); + bool finalComplete = processFragmentedPayload( + std::move(payload), flagsNext, flagsComplete, flagsFollows); + + if (finalComplete) { + completeConsumer(); + tryCompleteChannel(); + } +} + +void ChannelRequester::handleRequestN(uint32_t n) { + CHECK(requested_); + PublisherBase::processRequestN(n); +} + +void ChannelRequester::handleError(folly::exception_wrapper ew) { + CHECK(requested_); + errorConsumer(std::move(ew)); + terminatePublisher(); +} + +void ChannelRequester::handleCancel() { + CHECK(requested_); + terminatePublisher(); + tryCompleteChannel(); +} + +void ChannelRequester::endStream(StreamCompletionSignal signal) { + terminatePublisher(); + ConsumerBase::endStream(signal); +} + +void ChannelRequester::initStream(Payload&& request) { + requested_ = true; + + const size_t initialN = initialResponseAllowance_.consumeUpTo(kMaxRequestN); + const size_t remainingN = initialResponseAllowance_.consumeAll(); + + // Send as much as possible with the initial request. + CHECK_GE(kMaxRequestN, initialN); + newStream( + StreamType::CHANNEL, static_cast<uint32_t>(initialN), std::move(request)); + // We must inform ConsumerBase about an implicit allowance we have + // requested from the remote end. + ConsumerBase::addImplicitAllowance(initialN); + // Pump the remaining allowance into the ConsumerBase _after_ sending the + // initial request. + if (remainingN) { + ConsumerBase::generateRequest(remainingN); + } +} + +void ChannelRequester::tryCompleteChannel() { + if (publisherClosed() && consumerClosed()) { + endStream(StreamCompletionSignal::COMPLETE); + removeFromWriter(); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h new file mode 100644 index 000000000..7c05b2028 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h @@ -0,0 +1,74 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/statemachine/ConsumerBase.h" +#include "rsocket/statemachine/PublisherBase.h" +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a Channel requester. +class ChannelRequester : public ConsumerBase, + public PublisherBase, + public yarpl::flowable::Subscriber<Payload> { + public: + ChannelRequester( + Payload request, + std::shared_ptr<StreamsWriter> writer, + StreamId streamId) + : ConsumerBase(std::move(writer), streamId), + PublisherBase(0 /*initialRequestN*/), + request_(std::move(request)), + hasInitialRequest_(true) {} + + ChannelRequester(std::shared_ptr<StreamsWriter> writer, StreamId streamId) + : ConsumerBase(std::move(writer), streamId), + PublisherBase(1 /*initialRequestN*/) {} + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>) override; + void onNext(Payload) override; + void onComplete() override; + void onError(folly::exception_wrapper) override; + + void request(int64_t) override; + void cancel() override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + void handleRequestN(uint32_t) override; + void handleError(folly::exception_wrapper) override; + void handleCancel() override; + + void endStream(StreamCompletionSignal) override; + + private: + void initStream(Payload&&); + void tryCompleteChannel(); + + /// An allowance accumulated before the stream is initialised. Remaining part + /// of the allowance is forwarded to the ConsumerBase. + Allowance initialResponseAllowance_; + + Payload request_; + bool requested_{false}; + bool hasInitialRequest_{false}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.cpp new file mode 100644 index 000000000..db366e70d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.cpp @@ -0,0 +1,122 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/ChannelResponder.h" + +namespace rsocket { + +void ChannelResponder::onSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> subscription) { + publisherSubscribe(std::move(subscription)); +} + +void ChannelResponder::onNext(Payload response) { + if (!publisherClosed()) { + writePayload(std::move(response)); + } +} + +void ChannelResponder::onComplete() { + if (!publisherClosed()) { + publisherComplete(); + writeComplete(); + tryCompleteChannel(); + } +} + +void ChannelResponder::onError(folly::exception_wrapper ex) { + if (!publisherClosed()) { + publisherComplete(); + endStream(StreamCompletionSignal::ERROR); + if (!ex.with_exception([this](rsocket::ErrorWithPayload& err) { + writeApplicationError(std::move(err.payload)); + })) { + writeApplicationError(ex.get_exception()->what()); + } + tryCompleteChannel(); + } +} + +void ChannelResponder::request(int64_t n) { + ConsumerBase::generateRequest(n); +} + +void ChannelResponder::cancel() { + cancelConsumer(); + writeCancel(); + tryCompleteChannel(); +} + +void ChannelResponder::handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) { + payloadFragments_.addPayload(std::move(payload), flagsNext, flagsComplete); + + if (flagsFollows) { + // there will be more fragments to come + return; + } + + bool finalFlagsComplete, finalFlagsNext; + Payload finalPayload; + + std::tie(finalPayload, finalFlagsNext, finalFlagsComplete) = + payloadFragments_.consumePayloadAndFlags(); + + if (newStream_) { + newStream_ = false; + auto channelOutputSubscriber = onNewStreamReady( + StreamType::CHANNEL, + std::move(finalPayload), + std::static_pointer_cast<ChannelResponder>(shared_from_this())); + subscribe(std::move(channelOutputSubscriber)); + } else { + processPayload(std::move(finalPayload), finalFlagsNext); + } + + if (finalFlagsComplete) { + completeConsumer(); + tryCompleteChannel(); + } +} + +void ChannelResponder::handleRequestN(uint32_t n) { + processRequestN(n); +} + +void ChannelResponder::handleError(folly::exception_wrapper ew) { + errorConsumer(std::move(ew)); + terminatePublisher(); +} + +void ChannelResponder::handleCancel() { + terminatePublisher(); + tryCompleteChannel(); +} + +void ChannelResponder::endStream(StreamCompletionSignal signal) { + terminatePublisher(); + ConsumerBase::endStream(signal); +} + +void ChannelResponder::tryCompleteChannel() { + if (publisherClosed() && consumerClosed()) { + endStream(StreamCompletionSignal::COMPLETE); + removeFromWriter(); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h new file mode 100644 index 000000000..c0e6de708 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h @@ -0,0 +1,61 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/statemachine/ConsumerBase.h" +#include "rsocket/statemachine/PublisherBase.h" +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a Channel responder. +class ChannelResponder : public ConsumerBase, + public PublisherBase, + public yarpl::flowable::Subscriber<Payload> { + public: + ChannelResponder( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId, + uint32_t initialRequestN) + : ConsumerBase(std::move(writer), streamId), + PublisherBase(initialRequestN) {} + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>) override; + void onNext(Payload) override; + void onComplete() override; + void onError(folly::exception_wrapper) override; + + void request(int64_t) override; + void cancel() override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + + void handleRequestN(uint32_t) override; + void handleError(folly::exception_wrapper) override; + void handleCancel() override; + + void endStream(StreamCompletionSignal) override; + + private: + void tryCompleteChannel(); + + bool newStream_{true}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.cpp new file mode 100644 index 000000000..21d1cedc5 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.cpp @@ -0,0 +1,151 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/ConsumerBase.h" + +#include <algorithm> + +#include <glog/logging.h> + +namespace rsocket { + +void ConsumerBase::subscribe( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + if (state_ == State::CLOSED) { + subscriber->onSubscribe(yarpl::flowable::Subscription::create()); + subscriber->onComplete(); + return; + } + + DCHECK(!consumingSubscriber_); + consumingSubscriber_ = std::move(subscriber); + consumingSubscriber_->onSubscribe(shared_from_this()); +} + +void ConsumerBase::cancelConsumer() { + state_ = State::CLOSED; + VLOG(5) << "ConsumerBase::cancelConsumer()"; + consumingSubscriber_ = nullptr; +} + +void ConsumerBase::addImplicitAllowance(size_t n) { + allowance_.add(n); + activeRequests_.add(n); +} + +void ConsumerBase::generateRequest(size_t n) { + allowance_.add(n); + pendingAllowance_.add(n); + sendRequests(); +} + +void ConsumerBase::endStream(StreamCompletionSignal signal) { + VLOG(5) << "ConsumerBase::endStream(" << signal << ")"; + state_ = State::CLOSED; + if (auto subscriber = std::move(consumingSubscriber_)) { + if (signal == StreamCompletionSignal::COMPLETE || + signal == StreamCompletionSignal::CANCEL) { // TODO: remove CANCEL + VLOG(5) << "Closing ConsumerBase subscriber with calling onComplete"; + subscriber->onComplete(); + } else { + VLOG(5) << "Closing ConsumerBase subscriber with calling onError"; + subscriber->onError(StreamInterruptedException(static_cast<int>(signal))); + } + } +} + +size_t ConsumerBase::getConsumerAllowance() const { + return allowance_.get(); +} + +void ConsumerBase::processPayload(Payload&& payload, bool onNext) { + if (!payload && !onNext) { + return; + } + + // Frames carrying application-level payloads are taken into account when + // figuring out flow control allowance. + if (!allowance_.tryConsume(1) || !activeRequests_.tryConsume(1)) { + handleFlowControlError(); + return; + } + + sendRequests(); + if (consumingSubscriber_) { + consumingSubscriber_->onNext(std::move(payload)); + } else { + LOG(ERROR) << "Consuming subscriber is missing, might be a race on " + << "cancel/onNext"; + } +} + +bool ConsumerBase::processFragmentedPayload( + Payload&& payload, + bool flagsNext, + bool flagsComplete, + bool flagsFollows) { + payloadFragments_.addPayload(std::move(payload), flagsNext, flagsComplete); + + if (flagsFollows) { + // there will be more fragments to come + return false; + } + + bool finalFlagsComplete, finalFlagsNext; + Payload finalPayload; + + std::tie(finalPayload, finalFlagsNext, finalFlagsComplete) = + payloadFragments_.consumePayloadAndFlags(); + processPayload(std::move(finalPayload), finalFlagsNext); + return finalFlagsComplete; +} + +void ConsumerBase::completeConsumer() { + state_ = State::CLOSED; + VLOG(5) << "ConsumerBase::completeConsumer()"; + if (auto subscriber = std::move(consumingSubscriber_)) { + subscriber->onComplete(); + } +} + +void ConsumerBase::errorConsumer(folly::exception_wrapper ew) { + state_ = State::CLOSED; + VLOG(5) << "ConsumerBase::errorConsumer()"; + if (auto subscriber = std::move(consumingSubscriber_)) { + subscriber->onError(std::move(ew)); + } +} + +void ConsumerBase::sendRequests() { + auto toSync = std::min<size_t>(pendingAllowance_.get(), kMaxRequestN); + auto actives = activeRequests_.get(); + if (actives <= toSync) { + toSync = pendingAllowance_.consumeUpTo(toSync); + if (toSync > 0) { + writeRequestN(static_cast<uint32_t>(toSync)); + activeRequests_.add(toSync); + } + } +} + +void ConsumerBase::handleFlowControlError() { + if (auto subscriber = std::move(consumingSubscriber_)) { + subscriber->onError(std::runtime_error("Surplus response")); + } + writeInvalidError("Flow control error"); + endStream(StreamCompletionSignal::ERROR); + removeFromWriter(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h new file mode 100644 index 000000000..773c1350c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/internal/Allowance.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/flowable/Subscription.h" + +namespace rsocket { + +/// A class that represents a flow-control-aware consumer of data. +class ConsumerBase : public StreamStateMachineBase, + public yarpl::flowable::Subscription, + public std::enable_shared_from_this<ConsumerBase> { + public: + using StreamStateMachineBase::StreamStateMachineBase; + + void subscribe(std::shared_ptr<yarpl::flowable::Subscriber<Payload>>); + + /// Adds implicit allowance. + /// + /// This portion of allowance will not be synced to the remote end, but will + /// count towards the limit of allowance the remote PublisherBase may use. + void addImplicitAllowance(size_t); + + void generateRequest(size_t); + + bool consumerClosed() const { + return state_ == State::CLOSED; + } + + size_t getConsumerAllowance() const override; + void endStream(StreamCompletionSignal) override; + + protected: + void processPayload(Payload&&, bool onNext); + + // returns true if the stream is completed + bool + processFragmentedPayload(Payload&&, bool next, bool complete, bool follows); + + void cancelConsumer(); + void completeConsumer(); + void errorConsumer(folly::exception_wrapper); + + private: + enum class State : uint8_t { + RESPONDING, + CLOSED, + }; + + void sendRequests(); + + void handleFlowControlError(); + + /// A Subscriber that will consume payloads. This is responsible for + /// delivering a terminal signal to the Subscriber once the stream ends. + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> consumingSubscriber_; + + /// A total, net allowance (requested less delivered) by this consumer. + Allowance allowance_; + /// An allowance that have yet to be synced to the other end by sending + /// REQUEST_N frames. + Allowance pendingAllowance_; + + /// The number of already requested payload count. Prevent excessive requestN + /// calls. + Allowance activeRequests_; + + State state_{State::RESPONDING}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.cpp new file mode 100644 index 000000000..2a15b87a6 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.cpp @@ -0,0 +1,45 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/FireAndForgetResponder.h" + +namespace rsocket { + +using namespace yarpl::flowable; + +void FireAndForgetResponder::handlePayload( + Payload&& payload, + bool /*flagsComplete*/, + bool /*flagsNext*/, + bool flagsFollows) { + payloadFragments_.addPayloadIgnoreFlags(std::move(payload)); + + if (flagsFollows) { + // there will be more fragments to come + return; + } + + Payload finalPayload = payloadFragments_.consumePayloadIgnoreFlags(); + onNewStreamReady( + StreamType::FNF, + std::move(finalPayload), + std::shared_ptr<Subscriber<Payload>>(nullptr)); + removeFromWriter(); +} + +void FireAndForgetResponder::handleCancel() { + removeFromWriter(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h new file mode 100644 index 000000000..bf9ad3397 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h @@ -0,0 +1,41 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleSubscription.h" + +namespace rsocket { + +/// Helper class for handling receiving fragmented payload +class FireAndForgetResponder : public StreamStateMachineBase { + public: + FireAndForgetResponder( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId) + : StreamStateMachineBase(std::move(writer), streamId) {} + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + + private: + void handleCancel() override; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.cpp new file mode 100644 index 000000000..867ae4255 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/PublisherBase.h" + +#include <glog/logging.h> + +namespace rsocket { + +PublisherBase::PublisherBase(uint32_t initialRequestN) + : initialRequestN_(initialRequestN) {} + +void PublisherBase::publisherSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> subscription) { + if (state_ == State::CLOSED) { + subscription->cancel(); + return; + } + DCHECK(!producingSubscription_); + producingSubscription_ = std::move(subscription); + if (initialRequestN_) { + producingSubscription_->request(initialRequestN_.consumeAll()); + } +} + +void PublisherBase::publisherComplete() { + state_ = State::CLOSED; + producingSubscription_ = nullptr; +} + +bool PublisherBase::publisherClosed() const { + return state_ == State::CLOSED; +} + +void PublisherBase::processRequestN(uint32_t requestN) { + if (requestN == 0 || state_ == State::CLOSED) { + return; + } + + // We might not have the subscription set yet as there can be REQUEST_N frames + // scheduled on the executor before onSubscribe method. + if (producingSubscription_) { + producingSubscription_->request(requestN); + } else { + initialRequestN_.add(requestN); + } +} + +void PublisherBase::terminatePublisher() { + state_ = State::CLOSED; + if (auto subscription = std::move(producingSubscription_)) { + subscription->cancel(); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.h new file mode 100644 index 000000000..b5df39909 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/PublisherBase.h @@ -0,0 +1,46 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/internal/Allowance.h" +#include "yarpl/flowable/Subscription.h" + +namespace rsocket { + +/// A class that represents a flow-control-aware producer of data. +class PublisherBase { + public: + explicit PublisherBase(uint32_t initialRequestN); + + void publisherSubscribe(std::shared_ptr<yarpl::flowable::Subscription>); + + void processRequestN(uint32_t); + void publisherComplete(); + + bool publisherClosed() const; + void terminatePublisher(); + + private: + enum class State : uint8_t { + RESPONDING, + CLOSED, + }; + + std::shared_ptr<yarpl::flowable::Subscription> producingSubscription_; + Allowance initialRequestN_; + State state_{State::RESPONDING}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.cpp new file mode 100644 index 000000000..4d914052a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.cpp @@ -0,0 +1,1236 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/RSocketStateMachine.h" + +#include <folly/ExceptionWrapper.h> +#include <folly/Format.h> +#include <folly/Optional.h> +#include <folly/String.h> +#include <folly/io/async/EventBaseManager.h> +#include <folly/lang/Assume.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/RSocketConnectionEvents.h" +#include "rsocket/RSocketParameters.h" +#include "rsocket/RSocketResponder.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/framing/Frame.h" +#include "rsocket/framing/FrameSerializer.h" +#include "rsocket/framing/FrameTransportImpl.h" +#include "rsocket/internal/ClientResumeStatusCallback.h" +#include "rsocket/internal/ScheduledSubscriber.h" +#include "rsocket/internal/WarmResumeManager.h" +#include "rsocket/statemachine/ChannelRequester.h" +#include "rsocket/statemachine/ChannelResponder.h" +#include "rsocket/statemachine/FireAndForgetResponder.h" +#include "rsocket/statemachine/RequestResponseRequester.h" +#include "rsocket/statemachine/RequestResponseResponder.h" +#include "rsocket/statemachine/StreamRequester.h" +#include "rsocket/statemachine/StreamResponder.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" + +#include "yarpl/flowable/Subscription.h" +#include "yarpl/single/SingleSubscriptions.h" + +namespace rsocket { + +namespace { + +void disconnectError( + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> subscriber) { + std::runtime_error exn{"RSocket connection is disconnected or closed"}; + subscriber->onSubscribe(yarpl::flowable::Subscription::create()); + subscriber->onError(std::move(exn)); +} + +void disconnectError( + std::shared_ptr<yarpl::single::SingleObserver<Payload>> observer) { + auto exn = folly::make_exception_wrapper<std::runtime_error>( + "RSocket connection is disconnected or closed"); + observer->onSubscribe(yarpl::single::SingleSubscriptions::empty()); + observer->onError(std::move(exn)); +} + +} // namespace + +RSocketStateMachine::RSocketStateMachine( + std::shared_ptr<RSocketResponder> requestResponder, + std::unique_ptr<KeepaliveTimer> keepaliveTimer, + RSocketMode mode, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler) + : RSocketStateMachine( + std::make_shared<RSocketResponderAdapter>( + std::move(requestResponder)), + std::move(keepaliveTimer), + mode, + std::move(stats), + std::move(connectionEvents), + std::move(resumeManager), + std::move(coldResumeHandler)) {} + +RSocketStateMachine::RSocketStateMachine( + std::shared_ptr<RSocketResponderCore> requestResponder, + std::unique_ptr<KeepaliveTimer> keepaliveTimer, + RSocketMode mode, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler) + : mode_{mode}, + stats_{stats ? stats : RSocketStats::noop()}, + // Streams initiated by a client MUST use odd-numbered and streams + // initiated by the server MUST use even-numbered stream identifiers + nextStreamId_(mode == RSocketMode::CLIENT ? 1 : 2), + resumeManager_(std::move(resumeManager)), + requestResponder_{std::move(requestResponder)}, + keepaliveTimer_{std::move(keepaliveTimer)}, + coldResumeHandler_{std::move(coldResumeHandler)}, + connectionEvents_{connectionEvents} { + CHECK(resumeManager_) + << "provide ResumeManager::makeEmpty() instead of nullptr"; + + // We deliberately do not "open" input or output to avoid having c'tor on the + // stack when processing any signals from the connection. See ::connect and + // ::onSubscribe. + + CHECK(requestResponder_); + + stats_->socketCreated(); + VLOG(2) << "Creating RSocketStateMachine"; +} + +RSocketStateMachine::~RSocketStateMachine() { + // this destructor can be called from a different thread because the stream + // automatons destroyed on different threads can be the last ones referencing + // this. + + VLOG(3) << "~RSocketStateMachine"; + // We rely on SubscriptionPtr and SubscriberPtr to dispatch appropriate + // terminal signals. + DCHECK(!resumeCallback_); + DCHECK(isDisconnected()); // the instance should be closed by via + // close method +} + +void RSocketStateMachine::setResumable(bool resumable) { + // We should set this flag before we are connected + DCHECK(isDisconnected()); + isResumable_ = resumable; +} + +void RSocketStateMachine::connectServer( + std::shared_ptr<FrameTransport> frameTransport, + const SetupParameters& setupParams) { + setResumable(setupParams.resumable); + setProtocolVersionOrThrow(setupParams.protocolVersion, frameTransport); + connect(std::move(frameTransport)); + sendPendingFrames(); +} + +bool RSocketStateMachine::resumeServer( + std::shared_ptr<FrameTransport> frameTransport, + const ResumeParameters& resumeParams) { + const folly::Optional<int64_t> clientAvailable = + (resumeParams.clientPosition == kUnspecifiedResumePosition) + ? folly::none + : folly::make_optional( + resumeManager_->impliedPosition() - resumeParams.clientPosition); + + const int64_t serverAvailable = + resumeManager_->lastSentPosition() - resumeManager_->firstSentPosition(); + const int64_t serverDelta = + resumeManager_->lastSentPosition() - resumeParams.serverPosition; + + if (frameTransport) { + stats_->socketDisconnected(); + } + closeFrameTransport( + std::runtime_error{"Connection being resumed, dropping old connection"}); + setProtocolVersionOrThrow(resumeParams.protocolVersion, frameTransport); + connect(std::move(frameTransport)); + + const auto result = resumeFromPositionOrClose( + resumeParams.serverPosition, resumeParams.clientPosition); + + stats_->serverResume( + clientAvailable, + serverAvailable, + serverDelta, + result ? RSocketStats::ResumeOutcome::SUCCESS + : RSocketStats::ResumeOutcome::FAILURE); + + return result; +} + +void RSocketStateMachine::connectClient( + std::shared_ptr<FrameTransport> transport, + SetupParameters params) { + auto const version = params.protocolVersion == ProtocolVersion::Unknown + ? ProtocolVersion::Latest + : params.protocolVersion; + + setProtocolVersionOrThrow(version, transport); + setResumable(params.resumable); + + Frame_SETUP frame( + (params.resumable ? FrameFlags::RESUME_ENABLE : FrameFlags::EMPTY_) | + (params.payload.metadata ? FrameFlags::METADATA : FrameFlags::EMPTY_), + version.major, + version.minor, + getKeepaliveTime(), + Frame_SETUP::kMaxLifetime, + std::move(params.token), + std::move(params.metadataMimeType), + std::move(params.dataMimeType), + std::move(params.payload)); + + // TODO: when the server returns back that it doesn't support resumability, we + // should retry without resumability + + VLOG(3) << "Out: " << frame; + + connect(std::move(transport)); + // making sure we send setup frame first + outputFrame(frameSerializer_->serializeOut(std::move(frame))); + // then the rest of the cached frames will be sent + sendPendingFrames(); +} + +void RSocketStateMachine::resumeClient( + ResumeIdentificationToken token, + std::shared_ptr<FrameTransport> transport, + std::unique_ptr<ClientResumeStatusCallback> resumeCallback, + ProtocolVersion version) { + // Cold-resumption. Set the serializer. + if (!frameSerializer_) { + CHECK(coldResumeHandler_); + coldResumeInProgress_ = true; + } + + setProtocolVersionOrThrow( + version == ProtocolVersion::Unknown ? ProtocolVersion::Latest : version, + transport); + + Frame_RESUME resumeFrame( + std::move(token), + resumeManager_->impliedPosition(), + resumeManager_->firstSentPosition(), + frameSerializer_->protocolVersion()); + VLOG(3) << "Out: " << resumeFrame; + + // Disconnect a previous client if there is one. + disconnect(std::runtime_error{"Resuming client on a different connection"}); + + setResumable(true); + reconnect(std::move(transport), std::move(resumeCallback)); + outputFrame(frameSerializer_->serializeOut(std::move(resumeFrame))); +} + +void RSocketStateMachine::connect(std::shared_ptr<FrameTransport> transport) { + VLOG(2) << "Connecting to transport " << transport.get(); + + CHECK(isDisconnected()); + CHECK(transport); + + // Keep a reference to the argument, make sure the instance survives until + // setFrameProcessor() returns. There can be terminating signals processed in + // that call which will nullify frameTransport_. + frameTransport_ = transport; + + CHECK(frameSerializer_); + frameSerializer_->preallocateFrameSizeField() = + transport->isConnectionFramed(); + + if (connectionEvents_) { + connectionEvents_->onConnected(); + } + + // Keep a reference to stats, as processing frames might close this instance. + auto const stats = stats_; + frameTransport_->setFrameProcessor(shared_from_this()); + stats->socketConnected(); +} + +void RSocketStateMachine::sendPendingFrames() { + DCHECK(!resumeCallback_); + + StreamsWriterImpl::sendPendingFrames(); + + // TODO: turn on only after setup frame was received + if (keepaliveTimer_) { + keepaliveTimer_->start(shared_from_this()); + } +} + +void RSocketStateMachine::disconnect(folly::exception_wrapper ex) { + VLOG(2) << "Disconnecting transport"; + if (isDisconnected()) { + return; + } + + if (connectionEvents_) { + connectionEvents_->onDisconnected(ex); + } + + closeFrameTransport(std::move(ex)); + + if (connectionEvents_) { + connectionEvents_->onStreamsPaused(); + } + + stats_->socketDisconnected(); +} + +void RSocketStateMachine::close( + folly::exception_wrapper ex, + StreamCompletionSignal signal) { + if (isClosed()) { + return; + } + + isClosed_ = true; + stats_->socketClosed(signal); + + VLOG(6) << "close"; + + if (auto resumeCallback = std::move(resumeCallback_)) { + resumeCallback->onResumeError( + ConnectionException(ex ? ex.get_exception()->what() : "RS closing")); + } + + closeStreams(signal); + closeFrameTransport(ex); + + if (auto connectionEvents = std::move(connectionEvents_)) { + connectionEvents->onClosed(std::move(ex)); + } + + if (closeCallback_) { + closeCallback_->remove(*this); + } +} + +void RSocketStateMachine::closeFrameTransport(folly::exception_wrapper ex) { + if (isDisconnected()) { + DCHECK(!resumeCallback_); + return; + } + + // Stop scheduling keepalives since the socket is now disconnected + if (keepaliveTimer_) { + keepaliveTimer_->stop(); + } + + if (auto resumeCallback = std::move(resumeCallback_)) { + resumeCallback->onResumeError(ConnectionException( + ex ? ex.get_exception()->what() : "connection closing")); + } + + // Echo the exception to the frameTransport only if the frameTransport started + // closing with error. Otherwise we sent some error frame over the wire and + // we are closing the transport cleanly. + if (frameTransport_) { + frameTransport_->close(); + frameTransport_ = nullptr; + } +} + +void RSocketStateMachine::disconnectOrCloseWithError(Frame_ERROR&& errorFrame) { + if (isResumable_) { + std::runtime_error exn{errorFrame.payload_.moveDataToString()}; + disconnect(std::move(exn)); + } else { + closeWithError(std::move(errorFrame)); + } +} + +void RSocketStateMachine::closeWithError(Frame_ERROR&& error) { + VLOG(3) << "closeWithError " + << error.payload_.data->cloneAsValue().moveToFbString(); + + StreamCompletionSignal signal; + switch (error.errorCode_) { + case ErrorCode::INVALID_SETUP: + signal = StreamCompletionSignal::INVALID_SETUP; + break; + case ErrorCode::UNSUPPORTED_SETUP: + signal = StreamCompletionSignal::UNSUPPORTED_SETUP; + break; + case ErrorCode::REJECTED_SETUP: + signal = StreamCompletionSignal::REJECTED_SETUP; + break; + + case ErrorCode::CONNECTION_ERROR: + // StreamCompletionSignal::CONNECTION_ERROR is reserved for + // frameTransport errors + // ErrorCode::CONNECTION_ERROR is a normal Frame_ERROR error code which has + // nothing to do with frameTransport + case ErrorCode::APPLICATION_ERROR: + case ErrorCode::REJECTED: + case ErrorCode::RESERVED: + case ErrorCode::CANCELED: + case ErrorCode::INVALID: + default: + signal = StreamCompletionSignal::ERROR; + } + + std::runtime_error exn{error.payload_.cloneDataToString()}; + if (frameSerializer_) { + outputFrameOrEnqueue(frameSerializer_->serializeOut(std::move(error))); + } + close(std::move(exn), signal); +} + +void RSocketStateMachine::reconnect( + std::shared_ptr<FrameTransport> newFrameTransport, + std::unique_ptr<ClientResumeStatusCallback> resumeCallback) { + CHECK(newFrameTransport); + CHECK(resumeCallback); + + CHECK(!resumeCallback_); + CHECK(isResumable_); + CHECK(mode_ == RSocketMode::CLIENT); + + // TODO: output frame buffer should not be written to the new connection until + // we receive resume ok + resumeCallback_ = std::move(resumeCallback); + connect(std::move(newFrameTransport)); +} + +void RSocketStateMachine::requestStream( + Payload request, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> responseSink) { + if (isDisconnected()) { + disconnectError(std::move(responseSink)); + return; + } + + auto const streamId = getNextStreamId(); + auto stateMachine = std::make_shared<StreamRequester>( + shared_from_this(), streamId, std::move(request)); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); + stateMachine->subscribe(std::move(responseSink)); +} + +std::shared_ptr<yarpl::flowable::Subscriber<Payload>> +RSocketStateMachine::requestChannel( + Payload request, + bool hasInitialRequest, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> responseSink) { + if (isDisconnected()) { + disconnectError(std::move(responseSink)); + return nullptr; + } + + auto const streamId = getNextStreamId(); + std::shared_ptr<ChannelRequester> stateMachine; + if (hasInitialRequest) { + stateMachine = std::make_shared<ChannelRequester>( + std::move(request), shared_from_this(), streamId); + } else { + stateMachine = + std::make_shared<ChannelRequester>(shared_from_this(), streamId); + } + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); + stateMachine->subscribe(std::move(responseSink)); + return stateMachine; +} + +void RSocketStateMachine::requestResponse( + Payload request, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> responseSink) { + if (isDisconnected()) { + disconnectError(std::move(responseSink)); + return; + } + + auto const streamId = getNextStreamId(); + auto stateMachine = std::make_shared<RequestResponseRequester>( + shared_from_this(), streamId, std::move(request)); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); + stateMachine->subscribe(std::move(responseSink)); +} + +void RSocketStateMachine::closeStreams(StreamCompletionSignal signal) { + while (!streams_.empty()) { + auto it = streams_.begin(); + auto streamStateMachine = std::move(it->second); + streams_.erase(it); + streamStateMachine->endStream(signal); + } +} + +void RSocketStateMachine::processFrame(std::unique_ptr<folly::IOBuf> frame) { + if (isClosed()) { + VLOG(4) << "StateMachine has been closed. Discarding incoming frame"; + return; + } + + if (!ensureOrAutodetectFrameSerializer(*frame)) { + constexpr auto msg = "Cannot detect protocol version"; + closeWithError(Frame_ERROR::connectionError(msg)); + return; + } + + const auto frameType = frameSerializer_->peekFrameType(*frame); + stats_->frameRead(frameType); + + const auto optStreamId = frameSerializer_->peekStreamId(*frame, false); + if (!optStreamId) { + constexpr auto msg = "Cannot decode stream ID"; + closeWithError(Frame_ERROR::connectionError(msg)); + return; + } + + const auto frameLength = frame->computeChainDataLength(); + const auto streamId = *optStreamId; + handleFrame(streamId, frameType, std::move(frame)); + resumeManager_->trackReceivedFrame( + frameLength, frameType, streamId, getConsumerAllowance(streamId)); +} + +void RSocketStateMachine::onTerminal(folly::exception_wrapper ex) { + if (isResumable_) { + disconnect(std::move(ex)); + return; + } + const auto termSignal = ex ? StreamCompletionSignal::CONNECTION_ERROR + : StreamCompletionSignal::CONNECTION_END; + close(std::move(ex), termSignal); +} + +void RSocketStateMachine::onKeepAliveFrame( + ResumePosition resumePosition, + std::unique_ptr<folly::IOBuf> data, + bool keepAliveRespond) { + resumeManager_->resetUpToPosition(resumePosition); + if (mode_ == RSocketMode::SERVER) { + if (keepAliveRespond) { + sendKeepalive(FrameFlags::EMPTY_, std::move(data)); + } else { + closeWithError(Frame_ERROR::connectionError("keepalive without flag")); + } + } else { + if (keepAliveRespond) { + closeWithError(Frame_ERROR::connectionError( + "client received keepalive with respond flag")); + } else if (keepaliveTimer_) { + keepaliveTimer_->keepaliveReceived(); + } + stats_->keepaliveReceived(); + } +} + +void RSocketStateMachine::onMetadataPushFrame( + std::unique_ptr<folly::IOBuf> metadata) { + requestResponder_->handleMetadataPush(std::move(metadata)); +} + +void RSocketStateMachine::onResumeOkFrame(ResumePosition resumePosition) { + if (!resumeCallback_) { + constexpr auto msg = "Received RESUME_OK while not resuming"; + closeWithError(Frame_ERROR::connectionError(msg)); + return; + } + + if (!resumeManager_->isPositionAvailable(resumePosition)) { + auto const msg = folly::sformat( + "Client cannot resume, server position {} is not available", + resumePosition); + closeWithError(Frame_ERROR::connectionError(msg)); + return; + } + + if (coldResumeInProgress_) { + setNextStreamId(resumeManager_->getLargestUsedStreamId()); + for (const auto& it : resumeManager_->getStreamResumeInfos()) { + const auto streamId = it.first; + const StreamResumeInfo& streamResumeInfo = it.second; + if (streamResumeInfo.requester == RequestOriginator::LOCAL && + streamResumeInfo.streamType == StreamType::STREAM) { + auto subscriber = coldResumeHandler_->handleRequesterResumeStream( + streamResumeInfo.streamToken, streamResumeInfo.consumerAllowance); + + auto stateMachine = std::make_shared<StreamRequester>( + shared_from_this(), streamId, Payload()); + // Set requested to true (since cold resumption) + stateMachine->setRequested(streamResumeInfo.consumerAllowance); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); + stateMachine->subscribe( + std::make_shared<ScheduledSubscriptionSubscriber<Payload>>( + std::move(subscriber), + *folly::EventBaseManager::get()->getEventBase())); + } + } + coldResumeInProgress_ = false; + } + + auto resumeCallback = std::move(resumeCallback_); + resumeCallback->onResumeOk(); + resumeFromPosition(resumePosition); +} + +void RSocketStateMachine::onErrorFrame( + StreamId streamId, + ErrorCode errorCode, + Payload payload) { + if (streamId != 0) { + if (!ensureNotInResumption()) { + return; + } + // we ignore messages for streams which don't exist + if (auto stateMachine = getStreamStateMachine(streamId)) { + if (errorCode != ErrorCode::APPLICATION_ERROR) { + // Encapsulate non-user errors with runtime_error, which is more + // suitable for LOGging. + stateMachine->handleError( + std::runtime_error(payload.moveDataToString())); + } else { + // Don't expose user errors + stateMachine->handleError(ErrorWithPayload(std::move(payload))); + } + } + } else { + // TODO: handle INVALID_SETUP, UNSUPPORTED_SETUP, REJECTED_SETUP + if ((errorCode == ErrorCode::CONNECTION_ERROR || + errorCode == ErrorCode::REJECTED_RESUME) && + resumeCallback_) { + auto resumeCallback = std::move(resumeCallback_); + resumeCallback->onResumeError( + ResumptionException(payload.cloneDataToString())); + // fall through + } + close( + std::runtime_error(payload.moveDataToString()), + StreamCompletionSignal::ERROR); + } +} + +void RSocketStateMachine::onSetupFrame() { + // this should be processed in SetupResumeAcceptor + onUnexpectedFrame(0); +} + +void RSocketStateMachine::onResumeFrame() { + // this should be processed in SetupResumeAcceptor + onUnexpectedFrame(0); +} + +void RSocketStateMachine::onReservedFrame() { + onUnexpectedFrame(0); +} + +void RSocketStateMachine::onLeaseFrame() { + onUnexpectedFrame(0); +} + +void RSocketStateMachine::onExtFrame() { + onUnexpectedFrame(0); +} + +void RSocketStateMachine::onUnexpectedFrame(StreamId streamId) { + auto&& msg = folly::sformat("Unexpected frame for stream {}", streamId); + closeWithError(Frame_ERROR::connectionError(msg)); +} + +void RSocketStateMachine::handleFrame( + StreamId streamId, + FrameType frameType, + std::unique_ptr<folly::IOBuf> payload) { + switch (frameType) { + case FrameType::KEEPALIVE: { + Frame_KEEPALIVE frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onKeepAliveFrame( + frame.position_, + std::move(frame.data_), + !!(frame.header_.flags & FrameFlags::KEEPALIVE_RESPOND)); + return; + } + case FrameType::METADATA_PUSH: { + Frame_METADATA_PUSH frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onMetadataPushFrame(std::move(frame.metadata_)); + return; + } + case FrameType::RESUME_OK: { + Frame_RESUME_OK frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onResumeOkFrame(frame.position_); + return; + } + case FrameType::ERROR: { + Frame_ERROR frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onErrorFrame(streamId, frame.errorCode_, std::move(frame.payload_)); + return; + } + case FrameType::SETUP: + onSetupFrame(); + return; + case FrameType::RESUME: + onResumeFrame(); + return; + case FrameType::RESERVED: + onReservedFrame(); + return; + case FrameType::LEASE: + onLeaseFrame(); + return; + case FrameType::REQUEST_N: { + Frame_REQUEST_N frameRequestN; + if (!deserializeFrameOrError(frameRequestN, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frameRequestN; + onRequestNFrame(streamId, frameRequestN.requestN_); + break; + } + case FrameType::CANCEL: { + VLOG(3) << mode_ << " In: " << Frame_CANCEL(streamId); + onCancelFrame(streamId); + break; + } + case FrameType::PAYLOAD: { + Frame_PAYLOAD framePayload; + if (!deserializeFrameOrError(framePayload, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << framePayload; + onPayloadFrame( + streamId, + std::move(framePayload.payload_), + framePayload.header_.flagsFollows(), + framePayload.header_.flagsComplete(), + framePayload.header_.flagsNext()); + break; + } + case FrameType::REQUEST_CHANNEL: { + Frame_REQUEST_CHANNEL frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onRequestChannelFrame( + streamId, + frame.requestN_, + std::move(frame.payload_), + frame.header_.flagsComplete(), + frame.header_.flagsNext(), + frame.header_.flagsFollows()); + break; + } + case FrameType::REQUEST_STREAM: { + Frame_REQUEST_STREAM frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onRequestStreamFrame( + streamId, + frame.requestN_, + std::move(frame.payload_), + frame.header_.flagsFollows()); + break; + } + case FrameType::REQUEST_RESPONSE: { + Frame_REQUEST_RESPONSE frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onRequestResponseFrame( + streamId, std::move(frame.payload_), frame.header_.flagsFollows()); + break; + } + case FrameType::REQUEST_FNF: { + Frame_REQUEST_FNF frame; + if (!deserializeFrameOrError(frame, std::move(payload))) { + return; + } + VLOG(3) << mode_ << " In: " << frame; + onFireAndForgetFrame( + streamId, std::move(frame.payload_), frame.header_.flagsFollows()); + break; + } + case FrameType::EXT: + onExtFrame(); + return; + + default: { + stats_->unknownFrameReceived(); + // per rsocket spec, we will ignore any other unknown frames + return; + } + } +} + +std::shared_ptr<StreamStateMachineBase> +RSocketStateMachine::getStreamStateMachine(StreamId streamId) { + const auto&& it = streams_.find(streamId); + if (it == streams_.end()) { + return nullptr; + } + // we are purposely making a copy of the reference here to avoid problems with + // lifetime of the stateMachine when a terminating signal is delivered which + // will cause the stateMachine to be destroyed while in one of its methods + return it->second; +} + +bool RSocketStateMachine::ensureNotInResumption() { + if (resumeCallback_) { + // during the time when we are resuming we are can't receive any other + // than connection level frames which drives the resumption + // TODO(lehecka): this assertion should be handled more elegantly using + // different state machine + constexpr auto msg = "Received stream frame while resuming"; + LOG(ERROR) << msg; + closeWithError(Frame_ERROR::connectionError(msg)); + return false; + } + return true; +} + +void RSocketStateMachine::onRequestNFrame( + StreamId streamId, + uint32_t requestN) { + if (!ensureNotInResumption()) { + return; + } + // we ignore messages for streams which don't exist + if (auto stateMachine = getStreamStateMachine(streamId)) { + stateMachine->handleRequestN(requestN); + } +} + +void RSocketStateMachine::onCancelFrame(StreamId streamId) { + if (!ensureNotInResumption()) { + return; + } + // we ignore messages for streams which don't exist + if (auto stateMachine = getStreamStateMachine(streamId)) { + stateMachine->handleCancel(); + } +} + +void RSocketStateMachine::onPayloadFrame( + StreamId streamId, + Payload payload, + bool flagsFollows, + bool flagsComplete, + bool flagsNext) { + if (!ensureNotInResumption()) { + return; + } + // we ignore messages for streams which don't exist + if (auto stateMachine = getStreamStateMachine(streamId)) { + stateMachine->handlePayload( + std::move(payload), flagsComplete, flagsNext, flagsFollows); + } +} + +void RSocketStateMachine::onRequestStreamFrame( + StreamId streamId, + uint32_t requestN, + Payload payload, + bool flagsFollows) { + if (!ensureNotInResumption() || !isNewStreamId(streamId)) { + return; + } + auto stateMachine = + std::make_shared<StreamResponder>(shared_from_this(), streamId, requestN); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); // ensured by calling isNewStreamId + stateMachine->handlePayload(std::move(payload), false, false, flagsFollows); +} + +void RSocketStateMachine::onRequestChannelFrame( + StreamId streamId, + uint32_t requestN, + Payload payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) { + if (!ensureNotInResumption() || !isNewStreamId(streamId)) { + return; + } + auto stateMachine = std::make_shared<ChannelResponder>( + shared_from_this(), streamId, requestN); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); // ensured by calling isNewStreamId + stateMachine->handlePayload( + std::move(payload), flagsComplete, flagsNext, flagsFollows); +} + +void RSocketStateMachine::onRequestResponseFrame( + StreamId streamId, + Payload payload, + bool flagsFollows) { + if (!ensureNotInResumption() || !isNewStreamId(streamId)) { + return; + } + auto stateMachine = + std::make_shared<RequestResponseResponder>(shared_from_this(), streamId); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); // ensured by calling isNewStreamId + stateMachine->handlePayload(std::move(payload), false, false, flagsFollows); +} + +void RSocketStateMachine::onFireAndForgetFrame( + StreamId streamId, + Payload payload, + bool flagsFollows) { + if (!ensureNotInResumption() || !isNewStreamId(streamId)) { + return; + } + auto stateMachine = + std::make_shared<FireAndForgetResponder>(shared_from_this(), streamId); + const auto result = streams_.emplace(streamId, stateMachine); + DCHECK(result.second); // ensured by calling isNewStreamId + stateMachine->handlePayload(std::move(payload), false, false, flagsFollows); +} + +bool RSocketStateMachine::isNewStreamId(StreamId streamId) { + if (frameSerializer_->protocolVersion() > ProtocolVersion{0, 0} && + !registerNewPeerStreamId(streamId)) { + return false; + } + return true; +} + +std::shared_ptr<yarpl::flowable::Subscriber<Payload>> +RSocketStateMachine::onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) { + if (coldResumeHandler_ && streamType != StreamType::FNF) { + auto streamToken = + coldResumeHandler_->generateStreamToken(payload, streamId, streamType); + resumeManager_->onStreamOpen( + streamId, RequestOriginator::REMOTE, streamToken, streamType); + } + + switch (streamType) { + case StreamType::CHANNEL: + return requestResponder_->handleRequestChannel( + std::move(payload), streamId, std::move(response)); + + case StreamType::STREAM: + requestResponder_->handleRequestStream( + std::move(payload), streamId, std::move(response)); + return nullptr; + + case StreamType::REQUEST_RESPONSE: + // the other overload method should be called + CHECK(false); + folly::assume_unreachable(); + + case StreamType::FNF: + requestResponder_->handleFireAndForget(std::move(payload), streamId); + return nullptr; + + default: + CHECK(false) << "unknown value: " << streamType; + folly::assume_unreachable(); + } +} + +void RSocketStateMachine::onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) { + CHECK(streamType == StreamType::REQUEST_RESPONSE); + + if (coldResumeHandler_) { + auto streamToken = + coldResumeHandler_->generateStreamToken(payload, streamId, streamType); + resumeManager_->onStreamOpen( + streamId, RequestOriginator::REMOTE, streamToken, streamType); + } + requestResponder_->handleRequestResponse( + std::move(payload), streamId, std::move(response)); +} + +void RSocketStateMachine::sendKeepalive(std::unique_ptr<folly::IOBuf> data) { + sendKeepalive(FrameFlags::KEEPALIVE_RESPOND, std::move(data)); +} + +void RSocketStateMachine::sendKeepalive( + FrameFlags flags, + std::unique_ptr<folly::IOBuf> data) { + Frame_KEEPALIVE pingFrame( + flags, resumeManager_->impliedPosition(), std::move(data)); + VLOG(3) << mode_ << " Out: " << pingFrame; + outputFrameOrEnqueue(frameSerializer_->serializeOut(std::move(pingFrame))); + stats_->keepaliveSent(); +} + +bool RSocketStateMachine::isPositionAvailable(ResumePosition position) const { + return resumeManager_->isPositionAvailable(position); +} + +bool RSocketStateMachine::resumeFromPositionOrClose( + ResumePosition serverPosition, + ResumePosition clientPosition) { + DCHECK(!resumeCallback_); + DCHECK(!isDisconnected()); + DCHECK(mode_ == RSocketMode::SERVER); + + const bool clientPositionExist = + (clientPosition == kUnspecifiedResumePosition) || + clientPosition <= resumeManager_->impliedPosition(); + + if (clientPositionExist && + resumeManager_->isPositionAvailable(serverPosition)) { + Frame_RESUME_OK resumeOkFrame{resumeManager_->impliedPosition()}; + VLOG(3) << "Out: " << resumeOkFrame; + frameTransport_->outputFrameOrDrop( + frameSerializer_->serializeOut(std::move(resumeOkFrame))); + resumeFromPosition(serverPosition); + return true; + } + + auto const msg = folly::to<std::string>( + "Cannot resume server, client lastServerPosition=", + serverPosition, + " firstClientPosition=", + clientPosition, + " is not available. Last reset position is ", + resumeManager_->firstSentPosition()); + + closeWithError(Frame_ERROR::connectionError(msg)); + return false; +} + +void RSocketStateMachine::resumeFromPosition(ResumePosition position) { + DCHECK(!resumeCallback_); + DCHECK(!isDisconnected()); + DCHECK(resumeManager_->isPositionAvailable(position)); + + if (connectionEvents_) { + connectionEvents_->onStreamsResumed(); + } + resumeManager_->sendFramesFromPosition(position, *frameTransport_); + + auto frames = consumePendingOutputFrames(); + for (auto& frame : frames) { + outputFrameOrEnqueue(std::move(frame)); + } + + if (!isDisconnected() && keepaliveTimer_) { + keepaliveTimer_->start(shared_from_this()); + } +} + +bool RSocketStateMachine::shouldQueue() { + // if we are resuming we cant send any frames until we receive RESUME_OK + return isDisconnected() || resumeCallback_; +} + +void RSocketStateMachine::fireAndForget(Payload request) { + auto const streamId = getNextStreamId(); + Frame_REQUEST_FNF frame{streamId, FrameFlags::EMPTY_, std::move(request)}; + outputFrameOrEnqueue(frameSerializer_->serializeOut(std::move(frame))); +} + +void RSocketStateMachine::metadataPush(std::unique_ptr<folly::IOBuf> metadata) { + Frame_METADATA_PUSH metadataPushFrame{std::move(metadata)}; + outputFrameOrEnqueue( + frameSerializer_->serializeOut(std::move(metadataPushFrame))); +} + +void RSocketStateMachine::outputFrame(std::unique_ptr<folly::IOBuf> frame) { + DCHECK(!isDisconnected()); + + const auto frameType = frameSerializer_->peekFrameType(*frame); + stats_->frameWritten(frameType); + + if (isResumable_) { + auto streamIdPtr = frameSerializer_->peekStreamId(*frame, false); + CHECK(streamIdPtr) << "Error in serialized frame."; + resumeManager_->trackSentFrame( + *frame, frameType, *streamIdPtr, getConsumerAllowance(*streamIdPtr)); + } + frameTransport_->outputFrameOrDrop(std::move(frame)); +} + +uint32_t RSocketStateMachine::getKeepaliveTime() const { + return keepaliveTimer_ + ? static_cast<uint32_t>(keepaliveTimer_->keepaliveTime().count()) + : Frame_SETUP::kMaxKeepaliveTime; +} + +bool RSocketStateMachine::isDisconnected() const { + return !frameTransport_; +} + +bool RSocketStateMachine::isClosed() const { + return isClosed_; +} + +void RSocketStateMachine::writeNewStream( + StreamId streamId, + StreamType streamType, + uint32_t initialRequestN, + Payload payload) { + if (coldResumeHandler_ && streamType != StreamType::FNF) { + const auto streamToken = + coldResumeHandler_->generateStreamToken(payload, streamId, streamType); + resumeManager_->onStreamOpen( + streamId, RequestOriginator::LOCAL, streamToken, streamType); + } + + StreamsWriterImpl::writeNewStream( + streamId, streamType, initialRequestN, std::move(payload)); +} + +void RSocketStateMachine::onStreamClosed(StreamId streamId) { + streams_.erase(streamId); + resumeManager_->onStreamClosed(streamId); +} + +bool RSocketStateMachine::ensureOrAutodetectFrameSerializer( + const folly::IOBuf& firstFrame) { + if (frameSerializer_) { + return true; + } + + if (mode_ != RSocketMode::SERVER) { + // this should never happen as clients are initized with FrameSerializer + // instance + DCHECK(false); + return false; + } + + auto serializer = FrameSerializer::createAutodetectedSerializer(firstFrame); + if (!serializer) { + LOG(ERROR) << "unable to detect protocol version"; + return false; + } + + VLOG(2) << "detected protocol version" << serializer->protocolVersion(); + frameSerializer_ = std::move(serializer); + frameSerializer_->preallocateFrameSizeField() = + frameTransport_ && frameTransport_->isConnectionFramed(); + + return true; +} + +size_t RSocketStateMachine::getConsumerAllowance(StreamId streamId) const { + auto const it = streams_.find(streamId); + return it != streams_.end() ? it->second->getConsumerAllowance() : 0; +} + +void RSocketStateMachine::registerCloseCallback( + RSocketStateMachine::CloseCallback* callback) { + closeCallback_ = callback; +} + +DuplexConnection* RSocketStateMachine::getConnection() { + return frameTransport_ ? frameTransport_->getConnection() : nullptr; +} + +void RSocketStateMachine::setProtocolVersionOrThrow( + ProtocolVersion version, + const std::shared_ptr<FrameTransport>& transport) { + CHECK(version != ProtocolVersion::Unknown); + + // TODO(lehecka): this is a temporary guard to make sure the transport is + // explicitly closed when exceptions are thrown. The right solution is to + // automatically close duplex connection in the destructor when unique_ptr + // is released + auto transportGuard = folly::makeGuard([&] { transport->close(); }); + + if (frameSerializer_) { + if (frameSerializer_->protocolVersion() != version) { + // serializer is not interchangeable, it would screw up resumability + throw std::runtime_error{"Protocol version mismatch"}; + } + } else { + auto frameSerializer = FrameSerializer::createFrameSerializer(version); + if (!frameSerializer) { + throw std::runtime_error{"Invalid protocol version"}; + } + + frameSerializer_ = std::move(frameSerializer); + frameSerializer_->preallocateFrameSizeField() = + frameTransport_ && frameTransport_->isConnectionFramed(); + } + + transportGuard.dismiss(); +} + +StreamId RSocketStateMachine::getNextStreamId() { + constexpr auto limit = + static_cast<uint32_t>(std::numeric_limits<int32_t>::max() - 2); + + auto const streamId = nextStreamId_; + if (streamId >= limit) { + throw std::runtime_error{"Ran out of stream IDs"}; + } + + CHECK_EQ(0, streams_.count(streamId)) + << "Next stream ID already exists in the streams map"; + + nextStreamId_ += 2; + return streamId; +} + +void RSocketStateMachine::setNextStreamId(StreamId streamId) { + nextStreamId_ = streamId + 2; +} + +bool RSocketStateMachine::registerNewPeerStreamId(StreamId streamId) { + DCHECK_NE(0, streamId); + if (nextStreamId_ % 2 == streamId % 2) { + // if this is an unknown stream to the socket and this socket is + // generating such stream ids, it is an incoming frame on the stream which + // no longer exist + return false; + } + if (streamId <= lastPeerStreamId_) { + // receiving frame for a stream which no longer exists + return false; + } + lastPeerStreamId_ = streamId; + return true; +} + +bool RSocketStateMachine::hasStreams() const { + return !streams_.empty(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h new file mode 100644 index 000000000..71deeb322 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h @@ -0,0 +1,349 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <deque> +#include <memory> + +#include "rsocket/ColdResumeHandler.h" +#include "rsocket/DuplexConnection.h" +#include "rsocket/Payload.h" +#include "rsocket/RSocketParameters.h" +#include "rsocket/ResumeManager.h" +#include "rsocket/framing/FrameProcessor.h" +#include "rsocket/framing/FrameSerializer.h" +#include "rsocket/internal/Common.h" +#include "rsocket/internal/KeepaliveTimer.h" +#include "rsocket/statemachine/StreamFragmentAccumulator.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "rsocket/statemachine/StreamsWriter.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/flowable/Subscription.h" +#include "yarpl/single/SingleObserver.h" + +namespace rsocket { + +class ClientResumeStatusCallback; +class DuplexConnection; +class FrameTransport; +class Frame_ERROR; +class KeepaliveTimer; +class RSocketConnectionEvents; +class RSocketParameters; +class RSocketResponder; +class RSocketResponderCore; +class RSocketStateMachine; +class RSocketStats; +class ResumeManager; +class RSocketStateMachineTest; + +class FrameSink { + public: + virtual ~FrameSink() = default; + + /// Terminates underlying connection sending the error frame + /// on the connection. + /// + /// This may synchronously deliver terminal signals to all + /// StreamAutomatonBase attached to this ConnectionAutomaton. + virtual void disconnectOrCloseWithError(Frame_ERROR&& error) = 0; + + virtual void sendKeepalive( + std::unique_ptr<folly::IOBuf> data = folly::IOBuf::create(0)) = 0; +}; + +/// Handles connection-level frames and (de)multiplexes streams. +/// +/// Instances of this class should be accessed and managed via shared_ptr, +/// instead of the pattern reflected in MemoryMixin and IntrusiveDeleter. +/// The reason why such a simple memory management story is possible lies in the +/// fact that there is no request(n)-based flow control between stream +/// automata and ConnectionAutomaton. +class RSocketStateMachine final + : public FrameSink, + public FrameProcessor, + public StreamsWriterImpl, + public std::enable_shared_from_this<RSocketStateMachine> { + public: + RSocketStateMachine( + std::shared_ptr<RSocketResponderCore> requestResponder, + std::unique_ptr<KeepaliveTimer> keepaliveTimer, + RSocketMode mode, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler); + + RSocketStateMachine( + std::shared_ptr<RSocketResponder> requestResponder, + std::unique_ptr<KeepaliveTimer> keepaliveTimer, + RSocketMode mode, + std::shared_ptr<RSocketStats> stats, + std::shared_ptr<RSocketConnectionEvents> connectionEvents, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> coldResumeHandler); + + ~RSocketStateMachine(); + + /// Create a new connection as a server. + void connectServer(std::shared_ptr<FrameTransport>, const SetupParameters&); + + /// Resume a connection as a server. + bool resumeServer(std::shared_ptr<FrameTransport>, const ResumeParameters&); + + /// Connect as a client. Sends a SETUP frame. + void connectClient(std::shared_ptr<FrameTransport>, SetupParameters); + + /// Resume a connection as a client. Sends a RESUME frame. + void resumeClient( + ResumeIdentificationToken, + std::shared_ptr<FrameTransport>, + std::unique_ptr<ClientResumeStatusCallback>, + ProtocolVersion); + + /// Disconnect the state machine's connection. Existing streams will stay + /// intact. + void disconnect(folly::exception_wrapper); + + /// Whether the connection has been disconnected or closed. + bool isDisconnected() const; + + /// Send an ERROR frame, and close the connection and all of its streams. + void closeWithError(Frame_ERROR&&); + + /// Disconnect the connection if it is resumable, otherwise send an ERROR + /// frame and close the connection and all of its streams. + void disconnectOrCloseWithError(Frame_ERROR&&) override; + + /// Close the connection and all of its streams. + void close(folly::exception_wrapper, StreamCompletionSignal); + + void requestStream( + Payload request, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> responseSink); + + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> requestChannel( + Payload request, + bool hasInitialRequest, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> responseSink); + + void requestResponse( + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> responseSink); + + /// Send a REQUEST_FNF frame. + void fireAndForget(Payload); + + /// Send a METADATA_PUSH frame. + void metadataPush(std::unique_ptr<folly::IOBuf>); + + /// Send a KEEPALIVE frame, with the RESPOND flag set. + void sendKeepalive(std::unique_ptr<folly::IOBuf>) override; + + class CloseCallback { + public: + virtual ~CloseCallback() = default; + virtual void remove(RSocketStateMachine&) = 0; + }; + + /// Register a callback to be called when the StateMachine is closed. + /// It will be used to inform the containers, i.e. ConnectionSet or + /// wangle::ConnectionManager, to don't store the StateMachine anymore. + void registerCloseCallback(CloseCallback* callback); + + DuplexConnection* getConnection(); + + // Has active requests? + bool hasStreams() const; + + private: + // connection scope signals + void onKeepAliveFrame( + ResumePosition resumePosition, + std::unique_ptr<folly::IOBuf> data, + bool keepAliveRespond); + void onMetadataPushFrame(std::unique_ptr<folly::IOBuf> metadata); + void onResumeOkFrame(ResumePosition resumePosition); + void onErrorFrame(StreamId streamId, ErrorCode errorCode, Payload payload); + + // stream scope signals + void onRequestNFrame(StreamId streamId, uint32_t requestN); + void onCancelFrame(StreamId streamId); + void onPayloadFrame( + StreamId streamId, + Payload payload, + bool flagsFollows, + bool flagsComplete, + bool flagsNext); + + void onRequestStreamFrame( + StreamId streamId, + uint32_t requestN, + Payload payload, + bool flagsFollows); + void onRequestChannelFrame( + StreamId streamId, + uint32_t requestN, + Payload payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows); + void + onRequestResponseFrame(StreamId streamId, Payload payload, bool flagsFollows); + void + onFireAndForgetFrame(StreamId streamId, Payload payload, bool flagsFollows); + void onSetupFrame(); + void onResumeFrame(); + void onReservedFrame(); + void onLeaseFrame(); + void onExtFrame(); + void onUnexpectedFrame(StreamId streamId); + + std::shared_ptr<StreamStateMachineBase> getStreamStateMachine( + StreamId streamId); + + void connect(std::shared_ptr<FrameTransport>); + + /// Terminate underlying connection and connect new connection + void reconnect( + std::shared_ptr<FrameTransport>, + std::unique_ptr<ClientResumeStatusCallback>); + + void setResumable(bool); + + bool resumeFromPositionOrClose( + ResumePosition serverPosition, + ResumePosition clientPosition); + + bool isPositionAvailable(ResumePosition) const; + + /// Whether the connection has been closed. + bool isClosed() const; + + uint32_t getKeepaliveTime() const; + + void sendPendingFrames() override; + + // Should buffer the frame if the state machine is disconnected or in the + // process of resuming. + bool shouldQueue() override; + RSocketStats& stats() override { + return *stats_; + } + + FrameSerializer& serializer() override { + return *frameSerializer_; + } + + template <typename TFrame> + bool deserializeFrameOrError( + TFrame& frame, + std::unique_ptr<folly::IOBuf> buf) { + if (frameSerializer_->deserializeFrom(frame, std::move(buf))) { + return true; + } + closeWithError(Frame_ERROR::connectionError("Invalid frame")); + return false; + } + + // FrameProcessor. + void processFrame(std::unique_ptr<folly::IOBuf>) override; + void onTerminal(folly::exception_wrapper) override; + + void handleFrame(StreamId, FrameType, std::unique_ptr<folly::IOBuf>); + + void closeStreams(StreamCompletionSignal); + void closeFrameTransport(folly::exception_wrapper); + + void sendKeepalive(FrameFlags, std::unique_ptr<folly::IOBuf>); + + void resumeFromPosition(ResumePosition); + void outputFrame(std::unique_ptr<folly::IOBuf>) override; + + void writeNewStream( + StreamId streamId, + StreamType streamType, + uint32_t initialRequestN, + Payload payload) override; + + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) override; + void onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) + override; + + void onStreamClosed(StreamId) override; + + bool ensureOrAutodetectFrameSerializer(const folly::IOBuf& firstFrame); + bool ensureNotInResumption(); + + size_t getConsumerAllowance(StreamId) const; + + void setProtocolVersionOrThrow( + ProtocolVersion version, + const std::shared_ptr<FrameTransport>& transport); + + bool isNewStreamId(StreamId streamId); + bool registerNewPeerStreamId(StreamId streamId); + StreamId getNextStreamId(); + + void setNextStreamId(StreamId streamId); + + /// Client/server mode this state machine is operating in. + const RSocketMode mode_; + + /// Whether the connection was initialized as resumable. + bool isResumable_{false}; + + /// Whether the connection has closed. + bool isClosed_{false}; + + /// Whether a cold resume is currently in progress. + bool coldResumeInProgress_{false}; + + std::shared_ptr<RSocketStats> stats_; + + /// Map of all individual stream state machines. + std::unordered_map<StreamId, std::shared_ptr<StreamStateMachineBase>> + streams_; + StreamId nextStreamId_; + StreamId lastPeerStreamId_{0}; + + // Manages all state needed for warm/cold resumption. + std::shared_ptr<ResumeManager> resumeManager_; + + const std::shared_ptr<RSocketResponderCore> requestResponder_; + std::shared_ptr<FrameTransport> frameTransport_; + std::unique_ptr<FrameSerializer> frameSerializer_; + + const std::unique_ptr<KeepaliveTimer> keepaliveTimer_; + + std::unique_ptr<ClientResumeStatusCallback> resumeCallback_; + std::shared_ptr<ColdResumeHandler> coldResumeHandler_; + + std::shared_ptr<RSocketConnectionEvents> connectionEvents_; + + CloseCallback* closeCallback_{nullptr}; + + friend class RSocketStateMachineTest; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.cpp new file mode 100644 index 000000000..2d39be17b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.cpp @@ -0,0 +1,135 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/RequestResponseRequester.h" + +#include "rsocket/internal/Common.h" +#include "rsocket/statemachine/RSocketStateMachine.h" + +namespace rsocket { + +void RequestResponseRequester::subscribe( + std::shared_ptr<yarpl::single::SingleObserver<Payload>> subscriber) { + DCHECK(state_ != State::CLOSED); + DCHECK(!consumingSubscriber_); + consumingSubscriber_ = std::move(subscriber); + consumingSubscriber_->onSubscribe(shared_from_this()); + + if (state_ == State::NEW) { + state_ = State::REQUESTED; + newStream(StreamType::REQUEST_RESPONSE, 1, std::move(initialPayload_)); + return; + } + + if (auto subscriber = std::move(consumingSubscriber_)) { + subscriber->onError(std::runtime_error("cannot request more than 1 item")); + } + removeFromWriter(); +} + +void RequestResponseRequester::cancel() noexcept { + consumingSubscriber_ = nullptr; + switch (state_) { + case State::NEW: + state_ = State::CLOSED; + removeFromWriter(); + break; + case State::REQUESTED: { + state_ = State::CLOSED; + writeCancel(); + removeFromWriter(); + } break; + case State::CLOSED: + break; + } + consumingSubscriber_.reset(); +} + +void RequestResponseRequester::endStream(StreamCompletionSignal signal) { + switch (state_) { + case State::NEW: + case State::REQUESTED: + // Spontaneous ::endStream signal means an error. + DCHECK(StreamCompletionSignal::COMPLETE != signal); + DCHECK(StreamCompletionSignal::CANCEL != signal); + state_ = State::CLOSED; + break; + case State::CLOSED: + break; + } + if (auto subscriber = std::move(consumingSubscriber_)) { + DCHECK(signal != StreamCompletionSignal::COMPLETE); + DCHECK(signal != StreamCompletionSignal::CANCEL); + subscriber->onError(StreamInterruptedException(static_cast<int>(signal))); + } +} + +void RequestResponseRequester::handleError(folly::exception_wrapper ew) { + switch (state_) { + case State::NEW: + // Cannot receive a frame before sending the initial request. + CHECK(false); + break; + case State::REQUESTED: + state_ = State::CLOSED; + if (auto subscriber = std::move(consumingSubscriber_)) { + subscriber->onError(std::move(ew)); + } + removeFromWriter(); + break; + case State::CLOSED: + break; + } +} + +void RequestResponseRequester::handlePayload( + Payload&& payload, + bool /*flagsComplete*/, + bool flagsNext, + bool flagsFollows) { + // (State::NEW) Cannot receive a frame before sending the initial request. + // (State::CLOSED) should not be receiving frames when closed + // if we fail here, we broke some internal invariant of the class + CHECK(state_ == State::REQUESTED); + + payloadFragments_.addPayload(std::move(payload), flagsNext, false); + + if (flagsFollows) { + // there will be more fragments to come + return; + } + + bool finalFlagsNext, finalFlagsComplete; + Payload finalPayload; + + std::tie(finalPayload, finalFlagsNext, finalFlagsComplete) = + payloadFragments_.consumePayloadAndFlags(); + + state_ = State::CLOSED; + + if (finalPayload || finalFlagsNext) { + consumingSubscriber_->onSuccess(std::move(finalPayload)); + consumingSubscriber_ = nullptr; + } else if (!finalFlagsComplete) { + writeInvalidError("Payload, NEXT or COMPLETE flag expected"); + endStream(StreamCompletionSignal::ERROR); + } + removeFromWriter(); +} + +size_t RequestResponseRequester::getConsumerAllowance() const { + return (state_ == State::REQUESTED) ? 1 : 0; +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h new file mode 100644 index 000000000..be17cf546 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h @@ -0,0 +1,70 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleSubscription.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a RequestResponse +/// requester +class RequestResponseRequester + : public StreamStateMachineBase, + public yarpl::single::SingleSubscription, + public std::enable_shared_from_this<RequestResponseRequester> { + public: + RequestResponseRequester( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId, + Payload payload) + : StreamStateMachineBase(std::move(writer), streamId), + initialPayload_(std::move(payload)) {} + + void subscribe( + std::shared_ptr<yarpl::single::SingleObserver<Payload>> subscriber); + + private: + void cancel() noexcept override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + void handleError(folly::exception_wrapper ew) override; + + void endStream(StreamCompletionSignal signal) override; + + size_t getConsumerAllowance() const override; + + /// State of the Subscription requester. + enum class State : uint8_t { + NEW, + REQUESTED, + CLOSED, + }; + + State state_{State::NEW}; + + /// The observer that will consume payloads. + std::shared_ptr<yarpl::single::SingleObserver<Payload>> consumingSubscriber_; + + /// Initial payload which has to be sent with 1st request. + Payload initialPayload_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.cpp new file mode 100644 index 000000000..ca51ff54b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.cpp @@ -0,0 +1,127 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/RequestResponseResponder.h" + +namespace rsocket { + +void RequestResponseResponder::onSubscribe( + std::shared_ptr<yarpl::single::SingleSubscription> subscription) { + DCHECK(State::NEW != state_); + if (state_ == State::CLOSED) { + subscription->cancel(); + return; + } + producingSubscription_ = std::move(subscription); +} + +void RequestResponseResponder::onSuccess(Payload response) { + DCHECK(State::NEW != state_); + if (!producingSubscription_) { + return; + } + + switch (state_) { + case State::RESPONDING: { + state_ = State::CLOSED; + writePayload(std::move(response), true /* complete */); + producingSubscription_ = nullptr; + removeFromWriter(); + break; + } + case State::CLOSED: + break; + + case State::NEW: + default: + // class is internally misused + CHECK(false); + } +} + +void RequestResponseResponder::onError(folly::exception_wrapper ex) { + DCHECK(State::NEW != state_); + producingSubscription_ = nullptr; + switch (state_) { + case State::RESPONDING: { + state_ = State::CLOSED; + if (!ex.with_exception([this](rsocket::ErrorWithPayload& err) { + writeApplicationError(std::move(err.payload)); + })) { + writeApplicationError(ex.get_exception()->what()); + } + removeFromWriter(); + } break; + case State::CLOSED: + break; + + case State::NEW: + default: + // class is internally misused + CHECK(false); + } +} + +void RequestResponseResponder::handleCancel() { + switch (state_) { + case State::RESPONDING: + state_ = State::CLOSED; + removeFromWriter(); + break; + case State::NEW: + case State::CLOSED: + break; + } +} + +void RequestResponseResponder::handlePayload( + Payload&& payload, + bool /*flagsComplete*/, + bool /*flagsNext*/, + bool flagsFollows) { + payloadFragments_.addPayloadIgnoreFlags(std::move(payload)); + + if (flagsFollows) { + // there will be more fragments to come + return; + } + + CHECK(state_ == State::NEW); + Payload finalPayload = payloadFragments_.consumePayloadIgnoreFlags(); + + state_ = State::RESPONDING; + onNewStreamReady( + StreamType::REQUEST_RESPONSE, + std::move(finalPayload), + shared_from_this()); +} + +void RequestResponseResponder::endStream(StreamCompletionSignal signal) { + switch (state_) { + case State::NEW: + case State::RESPONDING: + // Spontaneous ::endStream signal means an error. + DCHECK(StreamCompletionSignal::COMPLETE != signal); + DCHECK(StreamCompletionSignal::CANCEL != signal); + state_ = State::CLOSED; + break; + case State::CLOSED: + break; + } + if (auto subscription = std::move(producingSubscription_)) { + subscription->cancel(); + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h new file mode 100644 index 000000000..3e7a5e37b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h @@ -0,0 +1,61 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleSubscription.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a RequestResponse +/// responder +class RequestResponseResponder + : public StreamStateMachineBase, + public yarpl::single::SingleObserver<Payload>, + public std::enable_shared_from_this<RequestResponseResponder> { + public: + RequestResponseResponder( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId) + : StreamStateMachineBase(std::move(writer), streamId) {} + + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription>) override; + void onSuccess(Payload) override; + void onError(folly::exception_wrapper) override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + void handleCancel() override; + + void endStream(StreamCompletionSignal) override; + + private: + /// State of the Subscription responder. + enum class State : uint8_t { + NEW, + RESPONDING, + CLOSED, + }; + + std::shared_ptr<yarpl::single::SingleSubscription> producingSubscription_; + State state_{State::NEW}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.cpp new file mode 100644 index 000000000..07c7a3986 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/StreamFragmentAccumulator.h" + +namespace rsocket { + +StreamFragmentAccumulator::StreamFragmentAccumulator() + : flagsComplete(false), flagsNext(false) {} + +void StreamFragmentAccumulator::addPayloadIgnoreFlags(Payload p) { + if (p.metadata) { + if (!fragments.metadata) { + fragments.metadata = std::move(p.metadata); + } else { + fragments.metadata->prev()->appendChain(std::move(p.metadata)); + } + } + + if (p.data) { + if (!fragments.data) { + fragments.data = std::move(p.data); + } else { + fragments.data->prev()->appendChain(std::move(p.data)); + } + } +} + +void StreamFragmentAccumulator::addPayload( + Payload p, + bool next, + bool complete) { + flagsNext |= next; + flagsComplete |= complete; + addPayloadIgnoreFlags(std::move(p)); +} + +Payload StreamFragmentAccumulator::consumePayloadIgnoreFlags() { + flagsComplete = false; + flagsNext = false; + return std::move(fragments); +} + +std::tuple<Payload, bool, bool> +StreamFragmentAccumulator::consumePayloadAndFlags() { + auto ret = std::make_tuple( + std::move(fragments), bool(flagsNext), bool(flagsComplete)); + flagsComplete = false; + flagsNext = false; + return ret; +} + +} /* namespace rsocket */ diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h new file mode 100644 index 000000000..0ed5227d8 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h @@ -0,0 +1,41 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" + +namespace rsocket { + +class StreamFragmentAccumulator { + public: + StreamFragmentAccumulator(); + + void addPayloadIgnoreFlags(Payload p); + void addPayload(Payload p, bool next, bool complete); + + Payload consumePayloadIgnoreFlags(); + std::tuple<Payload, bool, bool> consumePayloadAndFlags(); + + bool anyFragments() const { + return fragments.data || fragments.metadata; + } + + private: + bool flagsComplete : 1; + bool flagsNext : 1; + Payload fragments; +}; + +} /* namespace rsocket */ diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.cpp new file mode 100644 index 000000000..52e407be9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/StreamRequester.h" + +namespace rsocket { + +void StreamRequester::setRequested(size_t n) { + VLOG(3) << "Setting allowance to " << n; + requested_ = true; + addImplicitAllowance(n); +} + +void StreamRequester::request(int64_t signedN) { + if (signedN <= 0 || consumerClosed()) { + return; + } + + const size_t n = signedN; + + if (requested_) { + generateRequest(n); + return; + } + + requested_ = true; + + // We must inform ConsumerBase about an implicit allowance we have requested + // from the remote end. + auto const initial = std::min<uint32_t>(n, kMaxRequestN); + addImplicitAllowance(initial); + newStream(StreamType::STREAM, initial, std::move(initialPayload_)); + + // Pump the remaining allowance into the ConsumerBase _after_ sending the + // initial request. + if (n > initial) { + generateRequest(n - initial); + } +} + +void StreamRequester::cancel() { + VLOG(5) << "StreamRequester::cancel(requested_=" << requested_ << ")"; + if (consumerClosed()) { + return; + } + cancelConsumer(); + if (requested_) { + writeCancel(); + } + removeFromWriter(); +} + +void StreamRequester::handlePayload( + Payload&& payload, + bool complete, + bool next, + bool follows) { + if (!requested_) { + handleError(std::runtime_error("Haven't sent REQUEST_STREAM yet")); + return; + } + bool finalComplete = + processFragmentedPayload(std::move(payload), next, complete, follows); + + if (finalComplete) { + completeConsumer(); + removeFromWriter(); + } +} + +void StreamRequester::handleError(folly::exception_wrapper ew) { + errorConsumer(std::move(ew)); + removeFromWriter(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.h new file mode 100644 index 000000000..696b81472 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamRequester.h @@ -0,0 +1,51 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/statemachine/ConsumerBase.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a Stream requester +class StreamRequester : public ConsumerBase { + public: + StreamRequester( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId, + Payload payload) + : ConsumerBase(std::move(writer), streamId), + initialPayload_(std::move(payload)) {} + + void setRequested(size_t); + + void request(int64_t) override; + void cancel() override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + void handleError(folly::exception_wrapper ew) override; + + private: + /// Payload to be sent with the first request. + Payload initialPayload_; + + /// Whether request() has been called. + bool requested_{false}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.cpp new file mode 100644 index 000000000..9dfa8a6a3 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.cpp @@ -0,0 +1,102 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/StreamResponder.h" + +namespace rsocket { + +void StreamResponder::onSubscribe( + std::shared_ptr<yarpl::flowable::Subscription> subscription) { + publisherSubscribe(std::move(subscription)); +} + +void StreamResponder::onNext(Payload response) { + if (publisherClosed()) { + return; + } + writePayload(std::move(response)); +} + +void StreamResponder::onComplete() { + if (publisherClosed()) { + return; + } + publisherComplete(); + writeComplete(); + removeFromWriter(); +} + +void StreamResponder::onError(folly::exception_wrapper ew) { + if (publisherClosed()) { + return; + } + publisherComplete(); + if (!ew.with_exception([this](rsocket::ErrorWithPayload& err) { + writeApplicationError(std::move(err.payload)); + })) { + writeApplicationError(ew.get_exception()->what()); + } + removeFromWriter(); +} + +void StreamResponder::handleRequestN(uint32_t n) { + processRequestN(n); +} + +void StreamResponder::handleError(folly::exception_wrapper) { + handleCancel(); +} + +void StreamResponder::handlePayload( + Payload&& payload, + bool /*flagsComplete*/, + bool /*flagsNext*/, + bool flagsFollows) { + payloadFragments_.addPayloadIgnoreFlags(std::move(payload)); + + if (flagsFollows) { + // there will be more fragments to come + return; + } + + Payload finalPayload = payloadFragments_.consumePayloadIgnoreFlags(); + + if (newStream_) { + newStream_ = false; + onNewStreamReady( + StreamType::STREAM, std::move(finalPayload), shared_from_this()); + } else { + // per rsocket spec, ignore unexpected frame (payload) if it makes no sense + // in the semantic of the stream + } +} + +void StreamResponder::handleCancel() { + if (publisherClosed()) { + return; + } + terminatePublisher(); + removeFromWriter(); +} + +void StreamResponder::endStream(StreamCompletionSignal signal) { + if (publisherClosed()) { + return; + } + terminatePublisher(); + writeApplicationError(to_string(signal)); + removeFromWriter(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.h new file mode 100644 index 000000000..09b445eda --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamResponder.h @@ -0,0 +1,56 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/statemachine/PublisherBase.h" +#include "rsocket/statemachine/StreamStateMachineBase.h" +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +/// Implementation of stream stateMachine that represents a Stream responder +class StreamResponder : public StreamStateMachineBase, + public PublisherBase, + public yarpl::flowable::Subscriber<Payload>, + public std::enable_shared_from_this<StreamResponder> { + public: + StreamResponder( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId, + uint32_t initialRequestN) + : StreamStateMachineBase(std::move(writer), streamId), + PublisherBase(initialRequestN) {} + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>) override; + void onNext(Payload) override; + void onComplete() override; + void onError(folly::exception_wrapper) override; + + void handlePayload( + Payload&& payload, + bool flagsComplete, + bool flagsNext, + bool flagsFollows) override; + void handleRequestN(uint32_t) override; + void handleError(folly::exception_wrapper) override; + void handleCancel() override; + + void endStream(StreamCompletionSignal) override; + + private: + bool newStream_{true}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.cpp new file mode 100644 index 000000000..f0988fff7 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.cpp @@ -0,0 +1,100 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/StreamStateMachineBase.h" +#include <folly/io/IOBuf.h> +#include "rsocket/statemachine/RSocketStateMachine.h" +#include "rsocket/statemachine/StreamsWriter.h" + +namespace rsocket { + +void StreamStateMachineBase::handleRequestN(uint32_t) { + VLOG(4) << "Unexpected handleRequestN"; +} + +void StreamStateMachineBase::handleError(folly::exception_wrapper) { + endStream(StreamCompletionSignal::ERROR); + removeFromWriter(); +} + +void StreamStateMachineBase::handleCancel() { + VLOG(4) << "Unexpected handleCancel"; +} + +size_t StreamStateMachineBase::getConsumerAllowance() const { + return 0; +} + +void StreamStateMachineBase::newStream( + StreamType streamType, + uint32_t initialRequestN, + Payload payload) { + writer_->writeNewStream( + streamId_, streamType, initialRequestN, std::move(payload)); +} + +void StreamStateMachineBase::writeRequestN(uint32_t n) { + writer_->writeRequestN(Frame_REQUEST_N{streamId_, n}); +} + +void StreamStateMachineBase::writeCancel() { + writer_->writeCancel(Frame_CANCEL{streamId_}); +} + +void StreamStateMachineBase::writePayload(Payload&& payload, bool complete) { + auto const flags = + FrameFlags::NEXT | (complete ? FrameFlags::COMPLETE : FrameFlags::EMPTY_); + Frame_PAYLOAD frame{streamId_, flags, std::move(payload)}; + writer_->writePayload(std::move(frame)); +} + +void StreamStateMachineBase::writeComplete() { + writer_->writePayload(Frame_PAYLOAD::complete(streamId_)); +} + +void StreamStateMachineBase::writeApplicationError(folly::StringPiece msg) { + writer_->writeError(Frame_ERROR::applicationError(streamId_, msg)); +} + +void StreamStateMachineBase::writeApplicationError(Payload&& payload) { + writer_->writeError( + Frame_ERROR::applicationError(streamId_, std::move(payload))); +} + +void StreamStateMachineBase::writeInvalidError(folly::StringPiece msg) { + writer_->writeError(Frame_ERROR::invalid(streamId_, msg)); +} + +void StreamStateMachineBase::removeFromWriter() { + writer_->onStreamClosed(streamId_); + // TODO: set writer_ to nullptr +} + +std::shared_ptr<yarpl::flowable::Subscriber<Payload>> +StreamStateMachineBase::onNewStreamReady( + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) { + return writer_->onNewStreamReady( + streamId_, streamType, std::move(payload), std::move(response)); +} + +void StreamStateMachineBase::onNewStreamReady( + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) { + writer_->onNewStreamReady( + streamId_, streamType, std::move(payload), std::move(response)); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h new file mode 100644 index 000000000..012c3b7fa --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h @@ -0,0 +1,105 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> + +#include "rsocket/framing/FrameHeader.h" +#include "rsocket/internal/Common.h" +#include "rsocket/statemachine/StreamFragmentAccumulator.h" +#include "yarpl/Flowable.h" +#include "yarpl/Single.h" + +namespace folly { +class IOBuf; +} + +namespace rsocket { + +class StreamsWriter; +struct Payload; + +/// A common base class of all state machines. +/// +/// The instances might be destroyed on a different thread than they were +/// created. +class StreamStateMachineBase { + public: + StreamStateMachineBase( + std::shared_ptr<StreamsWriter> writer, + StreamId streamId) + : writer_(std::move(writer)), streamId_(streamId) {} + virtual ~StreamStateMachineBase() = default; + + virtual void handlePayload( + Payload&& payload, + bool complete, + bool flagsNext, + bool flagsFollows) = 0; + virtual void handleRequestN(uint32_t n); + virtual void handleError(folly::exception_wrapper); + virtual void handleCancel(); + + virtual size_t getConsumerAllowance() const; + + /// Indicates a terminal signal from the connection. + /// + /// This signal corresponds to Subscriber::{onComplete,onError} and + /// Subscription::cancel. + /// Per ReactiveStreams specification: + /// 1. no other signal can be delivered during or after this one, + /// 2. "unsubscribe handshake" guarantees that the signal will be delivered + /// exactly once, even if the state machine initiated stream closure, + /// 3. per "unsubscribe handshake", the state machine must deliver + /// corresponding + /// terminal signal to the connection. + virtual void endStream(StreamCompletionSignal) {} + + protected: + void + newStream(StreamType streamType, uint32_t initialRequestN, Payload payload); + + void writeRequestN(uint32_t); + void writeCancel(); + + void writePayload(Payload&& payload, bool complete = false); + void writeComplete(); + void writeApplicationError(folly::StringPiece); + void writeApplicationError(Payload&& payload); + void writeInvalidError(folly::StringPiece); + + void removeFromWriter(); + + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> onNewStreamReady( + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response); + + void onNewStreamReady( + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response); + + /// A partially-owning pointer to the connection, the stream runs on. + /// It is declared as const to allow only ctor to initialize it for thread + /// safety of the dtor. + const std::shared_ptr<StreamsWriter> writer_; + StreamFragmentAccumulator payloadFragments_; + + private: + const StreamId streamId_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.cpp b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.cpp new file mode 100644 index 000000000..5e2279d70 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.cpp @@ -0,0 +1,197 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/statemachine/StreamsWriter.h" + +#include "rsocket/RSocketStats.h" +#include "rsocket/framing/FrameSerializer.h" + +namespace rsocket { + +void StreamsWriterImpl::outputFrameOrEnqueue( + std::unique_ptr<folly::IOBuf> frame) { + if (shouldQueue()) { + enqueuePendingOutputFrame(std::move(frame)); + } else { + outputFrame(std::move(frame)); + } +} + +void StreamsWriterImpl::sendPendingFrames() { + // We are free to try to send frames again. Not all frames might be sent if + // the connection breaks, the rest of them will queue up again. + auto frames = consumePendingOutputFrames(); + for (auto& frame : frames) { + outputFrameOrEnqueue(std::move(frame)); + } +} + +void StreamsWriterImpl::enqueuePendingOutputFrame( + std::unique_ptr<folly::IOBuf> frame) { + auto const length = frame->computeChainDataLength(); + stats().streamBufferChanged(1, static_cast<int64_t>(length)); + pendingSize_ += length; + pendingOutputFrames_.push_back(std::move(frame)); +} + +std::deque<std::unique_ptr<folly::IOBuf>> +StreamsWriterImpl::consumePendingOutputFrames() { + if (auto const numFrames = pendingOutputFrames_.size()) { + stats().streamBufferChanged( + -static_cast<int64_t>(numFrames), -static_cast<int64_t>(pendingSize_)); + pendingSize_ = 0; + } + return std::move(pendingOutputFrames_); +} + +void StreamsWriterImpl::writeNewStream( + StreamId streamId, + StreamType streamType, + uint32_t initialRequestN, + Payload payload) { + // for simplicity, require that sent buffers don't consist of chains + writeFragmented( + [&](Payload p, FrameFlags flags) { + switch (streamType) { + case StreamType::CHANNEL: + outputFrameOrEnqueue( + serializer().serializeOut(Frame_REQUEST_CHANNEL( + streamId, flags, initialRequestN, std::move(p)))); + break; + case StreamType::STREAM: + outputFrameOrEnqueue(serializer().serializeOut(Frame_REQUEST_STREAM( + streamId, flags, initialRequestN, std::move(p)))); + break; + case StreamType::REQUEST_RESPONSE: + outputFrameOrEnqueue(serializer().serializeOut( + Frame_REQUEST_RESPONSE(streamId, flags, std::move(p)))); + break; + case StreamType::FNF: + outputFrameOrEnqueue(serializer().serializeOut( + Frame_REQUEST_FNF(streamId, flags, std::move(p)))); + break; + default: + CHECK(false) << "invalid stream type " << toString(streamType); + } + }, + streamId, + FrameFlags::EMPTY_, + std::move(payload)); +} + +void StreamsWriterImpl::writeRequestN(Frame_REQUEST_N&& frame) { + outputFrameOrEnqueue(serializer().serializeOut(std::move(frame))); +} + +void StreamsWriterImpl::writeCancel(Frame_CANCEL&& frame) { + outputFrameOrEnqueue(serializer().serializeOut(std::move(frame))); +} + +void StreamsWriterImpl::writePayload(Frame_PAYLOAD&& f) { + Frame_PAYLOAD frame = std::move(f); + auto const streamId = frame.header_.streamId; + auto const initialFlags = frame.header_.flags; + + writeFragmented( + [this, streamId](Payload p, FrameFlags flags) { + outputFrameOrEnqueue(serializer().serializeOut( + Frame_PAYLOAD(streamId, flags, std::move(p)))); + }, + streamId, + initialFlags, + std::move(frame.payload_)); +} + +void StreamsWriterImpl::writeError(Frame_ERROR&& frame) { + // TODO: implement fragmentation for writeError as well + outputFrameOrEnqueue(serializer().serializeOut(std::move(frame))); +} + +// The max amount of user data transmitted per frame - eg the size +// of the data and metadata combined, plus the size of the frame header. +// This assumes that the frame header will never be more than 512 bytes in +// size. A CHECK in FrameTransportImpl enforces this. The idea is that +// 16M is so much larger than the ~500 bytes possibly wasted that it won't +// be noticeable (0.003% wasted at most) +constexpr size_t GENEROUS_MAX_FRAME_SIZE = 0xFFFFFF - 512; + +// writeFragmented takes a `payload` and splits it up into chunks which +// are sent as fragmented requests. The first fragmented payload is +// given to writeInitialFrame, which is expected to write the initial +// "REQUEST_" or "PAYLOAD" frame of a stream or response. writeFragmented +// then writes the rest of the frames as payloads. +// +// writeInitialFrame +// - called with the payload of the first frame to send, and any additional +// flags (eg, addFlags with FOLLOWS, if there are more frames to write) +// streamId +// - The stream ID to write additional fragments with +// addFlags +// - All flags that writeInitialFrame wants to write the first frame with, +// and all flags that subsequent fragmented payloads will be sent with +// payload +// - The unsplit payload to send, possibly in multiple fragments +template <typename WriteInitialFrame> +void StreamsWriterImpl::writeFragmented( + WriteInitialFrame writeInitialFrame, + StreamId const streamId, + FrameFlags const addFlags, + Payload payload) { + folly::IOBufQueue metaQueue{folly::IOBufQueue::cacheChainLength()}; + folly::IOBufQueue dataQueue{folly::IOBufQueue::cacheChainLength()}; + + // have to keep track of "did the full payload even have a metadata", because + // the rsocket protocol makes a distinction between a zero-length metadata + // and a null metadata. + bool const haveNonNullMeta = !!payload.metadata; + metaQueue.append(std::move(payload.metadata)); + dataQueue.append(std::move(payload.data)); + + bool isFirstFrame = true; + + while (true) { + Payload sendme; + + // chew off some metadata (splitAtMost will never return a null pointer, + // safe to compute length on it always) + if (haveNonNullMeta) { + sendme.metadata = metaQueue.splitAtMost(GENEROUS_MAX_FRAME_SIZE); + DCHECK_GE( + GENEROUS_MAX_FRAME_SIZE, sendme.metadata->computeChainDataLength()); + } + sendme.data = dataQueue.splitAtMost( + GENEROUS_MAX_FRAME_SIZE - + (haveNonNullMeta ? sendme.metadata->computeChainDataLength() : 0)); + + auto const metaLeft = metaQueue.chainLength(); + auto const dataLeft = dataQueue.chainLength(); + auto const moreFragments = metaLeft || dataLeft; + auto const flags = + (moreFragments ? FrameFlags::FOLLOWS : FrameFlags::EMPTY_) | addFlags; + + if (isFirstFrame) { + isFirstFrame = false; + writeInitialFrame(std::move(sendme), flags); + } else { + outputFrameOrEnqueue(serializer().serializeOut( + Frame_PAYLOAD(streamId, flags, std::move(sendme)))); + } + + if (!moreFragments) { + break; + } + } +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h new file mode 100644 index 000000000..7ecf1da87 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h @@ -0,0 +1,107 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <deque> + +#include <yarpl/Flowable.h> +#include <yarpl/Single.h> +#include "rsocket/Payload.h" +#include "rsocket/framing/Frame.h" +#include "rsocket/framing/FrameType.h" +#include "rsocket/internal/Common.h" + +namespace rsocket { + +class RSocketStats; +class FrameSerializer; + +/// The interface for writing stream related frames on the wire. +class StreamsWriter { + public: + virtual ~StreamsWriter() = default; + + virtual void writeNewStream( + StreamId streamId, + StreamType streamType, + uint32_t initialRequestN, + Payload payload) = 0; + + virtual void writeRequestN(Frame_REQUEST_N&&) = 0; + virtual void writeCancel(Frame_CANCEL&&) = 0; + + virtual void writePayload(Frame_PAYLOAD&&) = 0; + virtual void writeError(Frame_ERROR&&) = 0; + + virtual void onStreamClosed(StreamId) = 0; + + virtual std::shared_ptr<yarpl::flowable::Subscriber<Payload>> + onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) = 0; + virtual void onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) = 0; +}; + +class StreamsWriterImpl : public StreamsWriter { + public: + void writeNewStream( + StreamId streamId, + StreamType streamType, + uint32_t initialRequestN, + Payload payload) override; + + void writeRequestN(Frame_REQUEST_N&&) override; + void writeCancel(Frame_CANCEL&&) override; + + void writePayload(Frame_PAYLOAD&&) override; + + // TODO: writeFragmentedError + void writeError(Frame_ERROR&&) override; + + protected: + // note: onStreamClosed() method is also still pure + virtual void outputFrame(std::unique_ptr<folly::IOBuf>) = 0; + virtual FrameSerializer& serializer() = 0; + virtual RSocketStats& stats() = 0; + virtual bool shouldQueue() = 0; + + template <typename WriteInitialFrame> + void writeFragmented( + WriteInitialFrame, + StreamId const, + FrameFlags const, + Payload payload); + + /// Send a frame to the output, or queue it if shouldQueue() + virtual void sendPendingFrames(); + void outputFrameOrEnqueue(std::unique_ptr<folly::IOBuf>); + void enqueuePendingOutputFrame(std::unique_ptr<folly::IOBuf> frame); + std::deque<std::unique_ptr<folly::IOBuf>> consumePendingOutputFrames(); + + private: + /// A queue of frames that are slated to be sent out. + std::deque<std::unique_ptr<folly::IOBuf>> pendingOutputFrames_; + + /// The byte size of all pending output frames. + size_t pendingSize_{0}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/BaseSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/BaseSubscriber.h new file mode 100644 index 000000000..fdda649ff --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/BaseSubscriber.h @@ -0,0 +1,64 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <condition_variable> +#include <mutex> +#include <vector> +#include "rsocket/Payload.h" +#include "yarpl/Refcounted.h" + +namespace rsocket { +namespace tck { + +class BaseSubscriber { + public: + virtual void request(int n) = 0; + virtual void cancel() = 0; + void awaitTerminalEvent(); + void awaitAtLeast(int numItems); + void awaitNoEvents(int waitTime); + void assertNoErrors(); + void assertError(); + void assertValues( + const std::vector<std::pair<std::string, std::string>>& values); + void assertValueCount(size_t valueCount); + void assertReceivedAtLeast(size_t valueCount); + void assertCompleted(); + void assertNotCompleted(); + void assertCanceled(); + + protected: + std::atomic<bool> canceled_{false}; + + //////////////////////////////////////////////////////////////////////////// + mutable std::mutex + mutex_; // all variables below has to be protected with the mutex + + std::vector<std::pair<std::string, std::string>> values_; + std::condition_variable valuesCV_; + std::atomic<int> valuesCount_{0}; + + std::vector<folly::exception_wrapper> errors_; + + std::condition_variable terminatedCV_; + std::atomic<bool> completed_{false}; // by onComplete + std::atomic<bool> errored_{false}; // by onError + //////////////////////////////////////////////////////////////////////////// +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/FlowableSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/FlowableSubscriber.h new file mode 100644 index 000000000..3de091023 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/FlowableSubscriber.h @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/tck-test/BaseSubscriber.h" + +#include "yarpl/Flowable.h" + +namespace rsocket { +namespace tck { + +class FlowableSubscriber : public BaseSubscriber, + public yarpl::flowable::Subscriber<Payload> { + public: + explicit FlowableSubscriber(int initialRequestN = 0); + + // Inherited from BaseSubscriber + void request(int n) override; + void cancel() override; + + protected: + // Inherited from flowable::Subscriber + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription> + subscription) noexcept override; + void onNext(Payload element) noexcept override; + void onComplete() noexcept override; + void onError(folly::exception_wrapper ex) noexcept override; + + private: + std::shared_ptr<yarpl::flowable::Subscription> subscription_; + int initialRequestN_{0}; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/MarbleProcessor.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/MarbleProcessor.h new file mode 100644 index 000000000..77217b63b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/MarbleProcessor.h @@ -0,0 +1,50 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <map> +#include "rsocket/Payload.h" +#include "yarpl/Flowable.h" +#include "yarpl/Single.h" + +namespace rsocket { +namespace tck { + +class MarbleProcessor { + public: + explicit MarbleProcessor(const std::string /* marble */); + + void run( + yarpl::flowable::Subscriber<rsocket::Payload>& subscriber, + int64_t requested); + + void run(std::shared_ptr<yarpl::single::SingleObserver<rsocket::Payload>> + subscriber); + + private: + std::string marble_; + + // Stores a mapping from marble character to Payload (data, metadata) + std::map<std::string, std::pair<std::string, std::string>> argMap_; + + // Keeps an account of how many messages can be sent. This could be done + // with Allowance + std::atomic<size_t> canSend_{0}; + + size_t index_{0}; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/SingleSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/SingleSubscriber.h new file mode 100644 index 000000000..8b8b8556f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/SingleSubscriber.h @@ -0,0 +1,43 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/tck-test/BaseSubscriber.h" + +#include "yarpl/Single.h" + +namespace rsocket { +namespace tck { + +class SingleSubscriber : public BaseSubscriber, + public yarpl::single::SingleObserver<Payload> { + public: + // Inherited from BaseSubscriber + void request(int n) override; + void cancel() override; + + protected: + // Inherited from flowable::Subscriber + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription> + subscription) noexcept override; + void onSuccess(Payload element) noexcept override; + void onError(folly::exception_wrapper ex) noexcept override; + + private: + std::shared_ptr<yarpl::single::SingleSubscription> subscription_; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestFileParser.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestFileParser.h new file mode 100644 index 000000000..7830934fb --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestFileParser.h @@ -0,0 +1,42 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <fstream> + +#include "rsocket/tck-test/TestSuite.h" + +namespace rsocket { +namespace tck { + +class TestFileParser { + public: + explicit TestFileParser(const std::string& fileName); + + TestSuite parse(); + + private: + void parseCommand(const std::string& command); + void addCurrentTest(); + + std::ifstream input_; + int currentLine_; + + TestSuite testSuite_; + Test currentTest_; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestInterpreter.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestInterpreter.h new file mode 100644 index 000000000..d57fb76c6 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestInterpreter.h @@ -0,0 +1,83 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <map> + +#include <folly/SocketAddress.h> +#include <folly/io/async/ScopedEventBaseThread.h> +#include "rsocket/Payload.h" +#include "rsocket/RSocket.h" +#include "rsocket/RSocketRequester.h" + +#include "rsocket/tck-test/BaseSubscriber.h" +#include "rsocket/tck-test/TestSuite.h" + +namespace folly { +class EventBase; +} + +namespace rsocket { + +class ReactiveSocket; + +namespace tck { + +class SubscribeCommand; +class RequestCommand; +class AwaitCommand; +class CancelCommand; +class AssertCommand; +class ResumeCommand; +class DisconnectCommand; + +class TestInterpreter { + class TestClient { + public: + explicit TestClient(std::shared_ptr<RSocketClient> c) + : client(std::move(c)) { + auto rs = client->getRequester(); + requester = std::move(rs); + } + std::shared_ptr<RSocketClient> client; + std::shared_ptr<RSocketRequester> requester; + }; + + public: + TestInterpreter(const Test& test, folly::SocketAddress address); + + bool run(); + + private: + void handleSubscribe(const SubscribeCommand& command); + void handleRequest(const RequestCommand& command); + void handleAwait(const AwaitCommand& command); + void handleCancel(const CancelCommand& command); + void handleAssert(const AssertCommand& command); + void handleDisconnect(const DisconnectCommand& command); + void handleResume(const ResumeCommand& command); + + std::shared_ptr<BaseSubscriber> getSubscriber(const std::string& id); + + folly::ScopedEventBaseThread worker_; + folly::SocketAddress address_; + const Test& test_; + std::map<std::string, std::string> interactionIdToType_; + std::map<std::string, std::shared_ptr<BaseSubscriber>> testSubscribers_; + std::map<std::string, std::shared_ptr<TestClient>> testClient_; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestSuite.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestSuite.h new file mode 100644 index 000000000..f705e0d13 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TestSuite.h @@ -0,0 +1,96 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <string> +#include <vector> + +namespace rsocket { +namespace tck { + +class TestCommand { + public: + explicit TestCommand(std::vector<std::string> params) + : params_(std::move(params)) {} + + const std::string& name() const { + return params_[1]; + } + + template <typename T> + T as() const { + return T(*this); + } + + const std::vector<std::string>& params() const { + return params_; + } + + bool valid() const; + + private: + std::vector<std::string> params_; +}; + +class Test { + public: + const std::string& name() const { + return name_; + } + + void setName(const std::string& name) { + name_ = name; + } + + bool resumption() const { + return resumption_; + } + + void setResumption(bool resumption) { + resumption_ = resumption; + } + + void addCommand(TestCommand command); + + const std::vector<TestCommand>& commands() const { + return commands_; + } + + bool empty() const { + return commands_.empty(); + } + + private: + std::string name_; + bool resumption_{false}; + std::vector<TestCommand> commands_; +}; + +class TestSuite { + public: + void addTest(Test test) { + tests_.push_back(std::move(test)); + } + + const std::vector<Test>& tests() const { + return tests_; + } + + private: + std::vector<Test> tests_; +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/tck-test/TypedCommands.h b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TypedCommands.h new file mode 100644 index 000000000..d32b144ac --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/tck-test/TypedCommands.h @@ -0,0 +1,183 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Conv.h> +#include <folly/String.h> + +#include "rsocket/tck-test/TestSuite.h" + +namespace rsocket { +namespace tck { + +class TypedTestCommand { + public: + explicit TypedTestCommand(const TestCommand& command) : command_(command) {} + + const std::string& clientId() const { + return command_.params().at(0); + } + + protected: + const TestCommand& command_; +}; + +class SubscribeCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; + + const std::string& type() const { + return command_.params().at(2); + } + + bool isRequestResponseType() const { + return type() == "rr"; + } + bool isRequestStreamType() const { + return type() == "rs"; + } + bool isFireAndForgetType() const { + return type() == "fnf"; + } + const std::string& id() const { + return command_.params().at(3); + } + const std::string& payloadData() const { + return command_.params().at(4); + } + const std::string& payloadMetadata() const { + return command_.params().at(5); + } +}; + +class RequestCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; + + int n() const { + return folly::to<int>(command_.params().at(2)); + } + const std::string& id() const { + return command_.params().at(3); + } +}; + +class CancelCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; + + const std::string& id() const { + return command_.params().at(2); + } +}; + +class ResumeCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; +}; + +class DisconnectCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; +}; + +class AwaitCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; + + const std::string& type() const { + return command_.params().at(2); + } + bool isTerminalType() const { + return type() == "terminal"; + } + bool isAtLeastType() const { + return type() == "atLeast"; + } + bool isNoEventsType() const { + return type() == "no_events"; + } + const std::string& id() const { + return command_.params().at(3); + } + int numElements() const { + return folly::to<int>(command_.params().at(4)); + } + int waitTime() const { + return folly::to<int>(command_.params().at(4)); + } +}; + +class AssertCommand : public TypedTestCommand { + public: + using TypedTestCommand::TypedTestCommand; + + const std::string& assertion() const { + return command_.params().at(2); + } + bool isNoErrorAssert() const { + return assertion() == "no_error"; + } + bool isErrorAssert() const { + return assertion() == "error"; + } + bool isReceivedAssert() const { + return assertion() == "received"; + } + bool isReceivedNAssert() const { + return assertion() == "received_n"; + } + bool isReceivedAtLeastAssert() const { + return assertion() == "received_at_least"; + } + bool isCompletedAssert() const { + return assertion() == "completed"; + } + bool isNotCompletedAssert() const { + return assertion() == "no_completed"; + } + bool isCanceledAssert() const { + return assertion() == "canceled"; + } + const std::string& id() const { + return command_.params().at(3); + } + + std::vector<std::pair<std::string, std::string>> values() const { + const auto& valuesStr = command_.params().at(4); + std::vector<std::string> items; + folly::split("&&", valuesStr, items); + + std::vector<std::string> components; + std::vector<std::pair<std::string, std::string>> values; + for (const auto& item : items) { + components.clear(); + folly::split(",", item, components); + if (components.size() == 2) { + values.emplace_back(std::make_pair(components[0], components[1])); + } else { + LOG(ERROR) << "wrong item in values string: " << item; + } + } + return values; + } + + size_t valueCount() const { + return folly::to<size_t>(command_.params().at(4)); + } +}; + +} // namespace tck +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/RSocketTests.h b/ios/Pods/Flipper-RSocket/rsocket/test/RSocketTests.h new file mode 100644 index 000000000..9e6edfae2 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/RSocketTests.h @@ -0,0 +1,178 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <utility> + +#include "rsocket/RSocket.h" + +#include "rsocket/transports/tcp/TcpConnectionFactory.h" + +namespace rsocket { +namespace tests { +namespace client_server { + +class RSocketStatsFlowControl : public RSocketStats { +public: + void frameWritten(FrameType frameType) { + if (frameType == FrameType::REQUEST_N) { + ++writeRequestN_; + } + } + + void frameRead(FrameType frameType) { + if (frameType == FrameType::REQUEST_N) { + ++readRequestN_; + } + } + +public: + int writeRequestN_{0}; + int readRequestN_{0}; +}; + +std::unique_ptr<TcpConnectionFactory> getConnFactory( + folly::EventBase* eventBase, + uint16_t port); + +std::unique_ptr<RSocketServer> makeServer( + std::shared_ptr<rsocket::RSocketResponder> responder, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + +std::unique_ptr<RSocketServer> makeResumableServer( + std::shared_ptr<RSocketServiceHandler> serviceHandler); + +std::unique_ptr<RSocketClient> makeClient( + folly::EventBase* eventBase, + uint16_t port, + folly::EventBase* stateMachineEvb = nullptr, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + +std::unique_ptr<RSocketClient> makeDisconnectedClient( + folly::EventBase* eventBase); + +folly::Future<std::unique_ptr<RSocketClient>> makeClientAsync( + folly::EventBase* eventBase, + uint16_t port, + folly::EventBase* stateMachineEvb = nullptr, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + +std::unique_ptr<RSocketClient> makeWarmResumableClient( + folly::EventBase* eventBase, + uint16_t port, + std::shared_ptr<RSocketConnectionEvents> connectionEvents = nullptr, + folly::EventBase* stateMachineEvb = nullptr); + +std::unique_ptr<RSocketClient> makeColdResumableClient( + folly::EventBase* eventBase, + uint16_t port, + ResumeIdentificationToken token, + std::shared_ptr<ResumeManager> resumeManager, + std::shared_ptr<ColdResumeHandler> resumeHandler, + folly::EventBase* stateMachineEvb = nullptr); + +} // namespace client_server + +struct RSocketPayloadUtils { + // ~30 megabytes, for metadata+data + static constexpr size_t LargeRequestSize = 15 * 1024 * 1024; + static std::string makeLongString(size_t size, std::string pattern) { + while (pattern.size() < size) { + pattern += pattern; + } + return pattern; + } + + // Builds up an IOBuf consisting of chunks with the following sizes, and then + // the rest tacked on the end in one big iobuf chunk + static std::unique_ptr<folly::IOBuf> buildIOBufFromString( + std::vector<size_t> const& sizes, + std::string const& from) { + folly::IOBufQueue bufQueue{folly::IOBufQueue::cacheChainLength()}; + size_t fromCursor = 0; + size_t remaining = from.size(); + for (auto size : sizes) { + if (remaining == 0) + break; + if (size > remaining) { + size = remaining; + } + + bufQueue.append( + folly::IOBuf::copyBuffer(from.c_str() + fromCursor, size)); + + fromCursor += size; + remaining -= size; + } + + if (remaining) { + bufQueue.append( + folly::IOBuf::copyBuffer(from.c_str() + fromCursor, remaining)); + } + + CHECK_EQ(bufQueue.chainLength(), from.size()); + + auto ret = bufQueue.move(); + int numChainElems = 1; + auto currentChainElem = ret.get()->next(); + while (currentChainElem != ret.get()) { + numChainElems++; + currentChainElem = currentChainElem->next(); + } + CHECK_GE(numChainElems, sizes.size()); + + // verify that the returned buffer has identical data + auto str = ret->cloneAsValue().moveToFbString().toStdString(); + CHECK_EQ(str.size(), from.size()); + CHECK(str == from); + + return ret; + } + + static void checkSameStrings( + std::string const& got, + std::string const& expect, + std::string const& context) { + CHECK_EQ(got.size(), expect.size()) + << "Got mismatched size " << context << " string (" << got.size() + << " vs " << expect.size() << ")"; + CHECK(got == expect) << context << " mismatch between got and expected"; + } + + static void checkSameStrings( + std::unique_ptr<folly::IOBuf> const& got, + std::string const& expect, + std::string const& context) { + CHECK_EQ(got->computeChainDataLength(), expect.size()) + << "Mismatched size " << context << ", got " + << got->computeChainDataLength() << " vs expect " << expect.size(); + + size_t expect_cursor = 0; + + for (auto range : *got) { + for (auto got_chr : range) { + // perform redundant check to avoid gtest's CHECK overhead + if (got_chr != expect[expect_cursor]) { + CHECK_EQ(got_chr, expect[expect_cursor]) + << "mismatch at byte " << expect_cursor; + } + expect_cursor++; + } + } + } +}; + +} // namespace tests +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloServiceHandler.h b/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloServiceHandler.h new file mode 100644 index 000000000..55cf86d53 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloServiceHandler.h @@ -0,0 +1,50 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <map> +#include "rsocket/RSocketServiceHandler.h" + +namespace rsocket { +namespace tests { + +// A minimal RSocketServiceHandler which supports resumption. + +class HelloServiceHandler : public RSocketServiceHandler { + public: + explicit HelloServiceHandler( + std::shared_ptr<RSocketConnectionEvents> connEvents = nullptr) + : connectionEvents_(connEvents) {} + + folly::Expected<RSocketConnectionParams, RSocketException> onNewSetup( + const SetupParameters&) override; + + void onNewRSocketState( + std::shared_ptr<RSocketServerState> state, + ResumeIdentificationToken token) override; + + folly::Expected<std::shared_ptr<RSocketServerState>, RSocketException> + onResume(ResumeIdentificationToken token) override; + + private: + std::shared_ptr<RSocketConnectionEvents> connectionEvents_; + folly::Synchronized< + std::map<ResumeIdentificationToken, std::shared_ptr<RSocketServerState>>, + std::mutex> + store_; +}; + +} // namespace tests +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloStreamRequestHandler.h b/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloStreamRequestHandler.h new file mode 100644 index 000000000..3aa48fb08 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/handlers/HelloStreamRequestHandler.h @@ -0,0 +1,31 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/RSocketResponder.h" +#include "yarpl/Flowable.h" + +namespace rsocket { +namespace tests { + +class HelloStreamRequestHandler : public RSocketResponder { + public: + /// Handles a new inbound Stream requested by the other end. + std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>> + handleRequestStream(rsocket::Payload request, rsocket::StreamId streamId) + override; +}; +} // namespace tests +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/ColdResumeManager.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/ColdResumeManager.h new file mode 100644 index 000000000..17cee3afb --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/ColdResumeManager.h @@ -0,0 +1,76 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/internal/WarmResumeManager.h" + +namespace folly { +class IOBuf; +} + +namespace rsocket { + +class RSocketStateMachine; +class FrameTransport; + +// In-memory ResumeManager for cold-resumption (for prototyping and +// testing purposes) +class ColdResumeManager : public WarmResumeManager { + public: + // If inputFile is provided, the ColdResumeManager will read state from the + // file, else it will start with a clean state. + // The constructor will throw if there is an error reading from the inputFile. + ColdResumeManager( + std::shared_ptr<RSocketStats> stats, + std::string inputFile = ""); + + void trackReceivedFrame( + size_t frameLength, + FrameType frameType, + StreamId streamId, + size_t consumerAllowance) override; + + void trackSentFrame( + const folly::IOBuf& serializedFrame, + FrameType frameType, + StreamId streamIdPtr, + size_t consumerAllowance) override; + + void onStreamOpen( + StreamId, + RequestOriginator, + std::string streamToken, + StreamType) override; + + void onStreamClosed(StreamId streamId) override; + + const StreamResumeInfos& getStreamResumeInfos() const override { + return streamResumeInfos_; + } + + StreamId getLargestUsedStreamId() const override { + return largestUsedStreamId_; + } + + // Persist resumption state to outputFile. Will throw if write fails. + void persistState(std::string outputFile); + + private: + StreamResumeInfos streamResumeInfos_; + + // Largest used StreamId so far. + StreamId largestUsedStreamId_{0}; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/GenericRequestResponseHandler.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/GenericRequestResponseHandler.h new file mode 100644 index 000000000..f3f79b6d1 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/GenericRequestResponseHandler.h @@ -0,0 +1,117 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/Single.h" + +#include "folly/ExceptionWrapper.h" + +namespace rsocket { +namespace tests { + +using StringPair = std::pair<std::string, std::string>; + +struct ResponseImpl { + enum class Type { PAYLOAD, EXCEPTION }; + + StringPair p; + folly::exception_wrapper e; + Type type; + + explicit ResponseImpl(StringPair const& p) : p(p), type(Type::PAYLOAD) {} + explicit ResponseImpl(folly::exception_wrapper e) + : e(std::move(e)), type(Type::EXCEPTION) {} + + ~ResponseImpl() {} +}; + +using Response = std::unique_ptr<ResponseImpl>; + +// Type that maps a request (data/metadata) to a response +// (data/metadata or exception) +using HandlerFunc = folly::Function<Response(StringPair const&)>; + +struct GenericRequestResponseHandler : public rsocket::RSocketResponder { + explicit GenericRequestResponseHandler(HandlerFunc&& func) + : handler_(std::make_unique<HandlerFunc>(std::move(func))) {} + + std::shared_ptr<yarpl::single::Single<Payload>> handleRequestResponse( + Payload request, + StreamId) override { + auto ioBufChainToString = [](std::unique_ptr<folly::IOBuf> buf) { + folly::IOBufQueue queue; + queue.append(std::move(buf)); + + std::string ret; + while (auto elem = queue.pop_front()) { + auto part = elem->moveToFbString(); + ret += part.toStdString(); + } + + return ret; + }; + + std::string data = ioBufChainToString(std::move(request.data)); + std::string meta = ioBufChainToString(std::move(request.metadata)); + + StringPair req(data, meta); + Response resp = (*handler_)(req); + + return yarpl::single::Single<Payload>::create( + [resp = std::move(resp), this](auto subscriber) { + subscriber->onSubscribe(yarpl::single::SingleSubscriptions::empty()); + + if (resp->type == ResponseImpl::Type::PAYLOAD) { + subscriber->onSuccess(Payload(resp->p.first, resp->p.second)); + } else if (resp->type == ResponseImpl::Type::EXCEPTION) { + subscriber->onError(resp->e); + } else { + throw std::runtime_error("unknown response type"); + } + }); + } + + ~GenericRequestResponseHandler() {} + + private: + std::unique_ptr<HandlerFunc> handler_; +}; + +inline Response payload_response(StringPair const& sp) { + return std::make_unique<ResponseImpl>(sp); +} + +inline Response payload_response(std::string const& a, std::string const& b) { + return payload_response({a, b}); +} + +template <typename T> +Response error_response(T const& err) { + return std::make_unique<ResponseImpl>(err); +} + +inline StringPair payload_to_stringpair(Payload p) { + return StringPair(p.moveDataToString(), p.moveMetadataToString()); +} +} // namespace tests +} // namespace rsocket + +namespace std { +inline ostream& operator<<( + std::ostream& os, + rsocket::tests::StringPair const& payload) { + return os << "('" << payload.first << "', '" << payload.second << "')"; +} +} // namespace std diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockDuplexConnection.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockDuplexConnection.h new file mode 100644 index 000000000..a64c64436 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockDuplexConnection.h @@ -0,0 +1,54 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <gmock/gmock.h> + +#include "rsocket/DuplexConnection.h" +#include "yarpl/test_utils/Mocks.h" + +namespace rsocket { + +class MockDuplexConnection : public DuplexConnection { + public: + using Subscriber = DuplexConnection::Subscriber; + + MockDuplexConnection() {} + + /// Creates a DuplexConnection that always runs `in` on the input subscriber. + template <class InputFn> + MockDuplexConnection(InputFn in) { + EXPECT_CALL(*this, setInput_(testing::_)) + .WillRepeatedly(testing::Invoke(std::move(in))); + } + + // DuplexConnection. + + void setInput(std::shared_ptr<Subscriber> in) override { + setInput_(std::move(in)); + } + + void send(std::unique_ptr<folly::IOBuf> buf) override { + send_(buf); + } + + // Mocks. + + MOCK_METHOD1(setInput_, void(std::shared_ptr<Subscriber>)); + MOCK_METHOD1(send_, void(std::unique_ptr<folly::IOBuf>&)); + MOCK_CONST_METHOD0(isFramed, bool()); +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockFrameProcessor.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockFrameProcessor.h new file mode 100644 index 000000000..385a143f1 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockFrameProcessor.h @@ -0,0 +1,40 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <gmock/gmock.h> + +#include <folly/ExceptionWrapper.h> +#include <folly/io/IOBuf.h> + +#include "rsocket/framing/FrameProcessor.h" + +namespace rsocket { + +class MockFrameProcessor : public FrameProcessor { + public: + void processFrame(std::unique_ptr<folly::IOBuf> buf) override { + processFrame_(buf); + } + + void onTerminal(folly::exception_wrapper ew) override { + onTerminal_(std::move(ew)); + } + + MOCK_METHOD1(processFrame_, void(std::unique_ptr<folly::IOBuf>&)); + MOCK_METHOD1(onTerminal_, void(folly::exception_wrapper)); +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStats.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStats.h new file mode 100644 index 000000000..c1707299f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStats.h @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <memory> + +#include <gmock/gmock.h> + +#include "rsocket/Payload.h" +#include "rsocket/RSocketStats.h" +#include "rsocket/transports/tcp/TcpDuplexConnection.h" + +namespace rsocket { + +class MockStats : public RSocketStats { + public: + MOCK_METHOD0(socketCreated, void()); + MOCK_METHOD1(socketClosed, void(StreamCompletionSignal)); + MOCK_METHOD0(socketDisconnected, void()); + + MOCK_METHOD2( + duplexConnectionCreated, + void(const std::string&, rsocket::DuplexConnection*)); + MOCK_METHOD2( + duplexConnectionClosed, + void(const std::string&, rsocket::DuplexConnection*)); + + MOCK_METHOD1(bytesWritten, void(size_t)); + MOCK_METHOD1(bytesRead, void(size_t)); + MOCK_METHOD1(frameWritten, void(FrameType)); + MOCK_METHOD1(frameRead, void(FrameType)); + MOCK_METHOD2(resumeBufferChanged, void(int, int)); + MOCK_METHOD2(streamBufferChanged, void(int64_t, int64_t)); +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStreamsWriter.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStreamsWriter.h new file mode 100644 index 000000000..4d6593c48 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/MockStreamsWriter.h @@ -0,0 +1,152 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <gmock/gmock.h> + +#include "rsocket/RSocketStats.h" +#include "rsocket/framing/FrameSerializer_v1_0.h" +#include "rsocket/statemachine/StreamsWriter.h" + +namespace rsocket { + +class MockStreamsWriterImpl : public StreamsWriterImpl { + public: + MOCK_METHOD1(onStreamClosed, void(StreamId)); + MOCK_METHOD1(outputFrame_, void(folly::IOBuf*)); + MOCK_METHOD0(shouldQueue, bool()); + + MockStreamsWriterImpl() { + using namespace testing; + ON_CALL(*this, shouldQueue()).WillByDefault(Invoke([this]() { + return this->shouldQueue_; + })); + } + + void outputFrame(std::unique_ptr<folly::IOBuf> buf) override { + outputFrame_(buf.get()); + } + + FrameSerializer& serializer() override { + return frameSerializer; + } + + RSocketStats& stats() override { + return *stats_; + } + + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) override { + // ignoring... + return nullptr; + } + + void onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) + override { + // ignoring... + } + + using StreamsWriterImpl::sendPendingFrames; + + bool shouldQueue_{false}; + std::shared_ptr<RSocketStats> stats_ = RSocketStats::noop(); + FrameSerializerV1_0 frameSerializer; +}; + +class MockStreamsWriter : public StreamsWriter { + public: + MOCK_METHOD4(writeNewStream_, void(StreamId, StreamType, uint32_t, Payload&)); + MOCK_METHOD1(writeRequestN_, void(rsocket::Frame_REQUEST_N)); + MOCK_METHOD1(writeCancel_, void(rsocket::Frame_CANCEL)); + MOCK_METHOD1(writePayload_, void(rsocket::Frame_PAYLOAD&)); + MOCK_METHOD1(writeError_, void(rsocket::Frame_ERROR&)); + MOCK_METHOD1(onStreamClosed, void(rsocket::StreamId)); + + // Delegate the Mock calls to the implementation in StreamsWriterImpl. + MockStreamsWriterImpl& delegateToImpl() { + delegateToImpl_ = true; + using namespace testing; + ON_CALL(*this, onStreamClosed(_)) + .WillByDefault(Invoke(&impl_, &StreamsWriter::onStreamClosed)); + return impl_; + } + + void writeNewStream(StreamId id, StreamType type, uint32_t i, Payload p) + override { + writeNewStream_(id, type, i, p); + if (delegateToImpl_) { + impl_.writeNewStream(id, type, i, std::move(p)); + } + } + + void writeRequestN(rsocket::Frame_REQUEST_N&& request) override { + if (delegateToImpl_) { + impl_.writeRequestN(std::move(request)); + } + writeRequestN_(request); + } + + void writeCancel(rsocket::Frame_CANCEL&& cancel) override { + writeCancel_(cancel); + if (delegateToImpl_) { + impl_.writeCancel(std::move(cancel)); + } + } + + void writePayload(rsocket::Frame_PAYLOAD&& payload) override { + writePayload_(payload); + if (delegateToImpl_) { + impl_.writePayload(std::move(payload)); + } + } + + void writeError(rsocket::Frame_ERROR&& error) override { + writeError_(error); + if (delegateToImpl_) { + impl_.writeError(std::move(error)); + } + } + + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::flowable::Subscriber<Payload>> response) override { + // ignoring... + return nullptr; + } + + void onNewStreamReady( + StreamId streamId, + StreamType streamType, + Payload payload, + std::shared_ptr<yarpl::single::SingleObserver<Payload>> response) + override { + // ignoring... + } + + protected: + MockStreamsWriterImpl impl_; + bool delegateToImpl_{false}; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/PrintSubscriber.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/PrintSubscriber.h new file mode 100644 index 000000000..5a392c1dd --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/PrintSubscriber.h @@ -0,0 +1,31 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/Payload.h" +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { +class PrintSubscriber : public yarpl::flowable::Subscriber<Payload> { + public: + ~PrintSubscriber(); + + void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription> + subscription) noexcept override; + void onNext(Payload element) noexcept override; + void onComplete() noexcept override; + void onError(folly::exception_wrapper ex) noexcept override; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/StatsPrinter.h b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/StatsPrinter.h new file mode 100644 index 000000000..afe2fa8a0 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/test_utils/StatsPrinter.h @@ -0,0 +1,45 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "rsocket/DuplexConnection.h" +#include "rsocket/RSocketStats.h" + +namespace rsocket { +class StatsPrinter : public RSocketStats { + public: + void socketCreated() override; + void socketClosed(StreamCompletionSignal signal) override; + void socketDisconnected() override; + + void duplexConnectionCreated( + const std::string& type, + rsocket::DuplexConnection* connection) override; + void duplexConnectionClosed( + const std::string& type, + rsocket::DuplexConnection* connection) override; + + void bytesWritten(size_t bytes) override; + void bytesRead(size_t bytes) override; + void frameWritten(FrameType frameType) override; + void frameRead(FrameType frameType) override; + void resumeBufferChanged(int framesCountDelta, int dataSizeDelta) override; + void streamBufferChanged(int64_t framesCountDelta, int64_t dataSizeDelta) + override; + + void keepaliveSent() override; + void keepaliveReceived() override; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/test/transport/DuplexConnectionTest.h b/ios/Pods/Flipper-RSocket/rsocket/test/transport/DuplexConnectionTest.h new file mode 100644 index 000000000..c370975e9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/test/transport/DuplexConnectionTest.h @@ -0,0 +1,43 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/EventBase.h> + +#include "rsocket/DuplexConnection.h" + +namespace rsocket { +namespace tests { + +void makeMultipleSetInputGetOutputCalls( + std::unique_ptr<rsocket::DuplexConnection> serverConnection, + folly::EventBase* serverEvb, + std::unique_ptr<rsocket::DuplexConnection> clientConnection, + folly::EventBase* clientEvb); + +void verifyInputAndOutputIsUntied( + std::unique_ptr<rsocket::DuplexConnection> serverConnection, + folly::EventBase* serverEvb, + std::unique_ptr<rsocket::DuplexConnection> clientConnection, + folly::EventBase* clientEvb); + +void verifyClosingInputAndOutputDoesntCloseConnection( + std::unique_ptr<rsocket::DuplexConnection> serverConnection, + folly::EventBase* serverEvb, + std::unique_ptr<rsocket::DuplexConnection> clientConnection, + folly::EventBase* clientEvb); + +} // namespace tests +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/RSocketTransport.h b/ios/Pods/Flipper-RSocket/rsocket/transports/RSocketTransport.h new file mode 100644 index 000000000..d86a4669a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/RSocketTransport.h @@ -0,0 +1,49 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +namespace rsocket { +class RSocketTransportHandler { + public: + virtual ~RSocketTransportHandler() = default; + + // connection scope signals + virtual void onKeepAlive( + ResumePosition resumePosition, + std::unique_ptr<folly::IOBuf> data, + bool keepAliveRespond) = 0; + virtual void onMetadataPush(std::unique_ptr<folly::IOBuf> metadata) = 0; + virtual void onResumeOk(ResumePosition resumePosition); + virtual void onError(ErrorCode errorCode, Payload payload) = 0; + + // stream scope signals + virtual void onStreamRequestN(StreamId streamId, uint32_t requestN) = 0; + virtual void onStreamCancel(StreamId streamId) = 0; + virtual void onStreamError(StreamId streamId, Payload payload) = 0; + virtual void onStreamPayload( + StreamId streamId, + Payload payload, + bool flagsFollows, + bool flagsComplete, + bool flagsNext) = 0; +}; + +class RSocketTransport { + public: + virtual ~RSocketTransport() = default; + + // TODO: +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.cpp b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.cpp new file mode 100644 index 000000000..0c1c41b80 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.cpp @@ -0,0 +1,128 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/transports/tcp/TcpConnectionAcceptor.h" + +#include <folly/Format.h> +#include <folly/futures/Future.h> +#include <folly/io/async/AsyncSocket.h> +#include <folly/io/async/EventBaseManager.h> + +#include "rsocket/transports/tcp/TcpDuplexConnection.h" + +namespace rsocket { + +class TcpConnectionAcceptor::SocketCallback + : public folly::AsyncServerSocket::AcceptCallback { + public: + explicit SocketCallback(OnDuplexConnectionAccept& onAccept) + : thread_{folly::sformat("rstcp-acceptor")}, onAccept_{onAccept} {} + + void connectionAccepted( + folly::NetworkSocket fdNetworkSocket, + const folly::SocketAddress& address) noexcept override { + int fd = fdNetworkSocket.toFd(); + + VLOG(2) << "Accepting TCP connection from " << address << " on FD " << fd; + + folly::AsyncTransportWrapper::UniquePtr socket( + new folly::AsyncSocket(eventBase(), folly::NetworkSocket::fromFd(fd))); + + auto connection = std::make_unique<TcpDuplexConnection>(std::move(socket)); + onAccept_(std::move(connection), *eventBase()); + } + + void acceptError(const std::exception& ex) noexcept override { + VLOG(2) << "TCP error: " << ex.what(); + } + + folly::EventBase* eventBase() const { + return thread_.getEventBase(); + } + + private: + /// The thread running this callback. + folly::ScopedEventBaseThread thread_; + + /// Reference to the ConnectionAcceptor's callback. + OnDuplexConnectionAccept& onAccept_; +}; + +TcpConnectionAcceptor::TcpConnectionAcceptor(Options options) + : options_(std::move(options)) {} + +TcpConnectionAcceptor::~TcpConnectionAcceptor() { + if (serverThread_) { + stop(); + serverThread_.reset(); + } +} + +void TcpConnectionAcceptor::start(OnDuplexConnectionAccept onAccept) { + if (onAccept_ != nullptr) { + throw std::runtime_error("TcpConnectionAcceptor::start() already called"); + } + + onAccept_ = std::move(onAccept); + serverThread_ = + std::make_unique<folly::ScopedEventBaseThread>("rstcp-listener"); + + callbacks_.reserve(options_.threads); + for (size_t i = 0; i < options_.threads; ++i) { + callbacks_.push_back(std::make_unique<SocketCallback>(onAccept_)); + } + + VLOG(1) << "Starting TCP listener on port " << options_.address.getPort() + << " with " << options_.threads << " request threads"; + + serverSocket_.reset( + new folly::AsyncServerSocket(serverThread_->getEventBase())); + + // The AsyncServerSocket needs to be accessed from the listener thread only. + // This will propagate out any exceptions the listener throws. + folly::via( + serverThread_->getEventBase(), + [this] { + serverSocket_->bind(options_.address); + + for (auto const& callback : callbacks_) { + serverSocket_->addAcceptCallback( + callback.get(), callback->eventBase()); + } + + serverSocket_->listen(options_.backlog); + serverSocket_->startAccepting(); + + for (const auto& i : serverSocket_->getAddresses()) { + VLOG(1) << "Listening on " << i.describe(); + } + }) + .get(); +} + +void TcpConnectionAcceptor::stop() { + VLOG(1) << "Shutting down TCP listener"; + + serverThread_->getEventBase()->runInEventBaseThreadAndWait( + [serverSocket = std::move(serverSocket_)]() {}); +} + +folly::Optional<uint16_t> TcpConnectionAcceptor::listeningPort() const { + if (!serverSocket_) { + return folly::none; + } + return serverSocket_->getAddress().getPort(); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h new file mode 100644 index 000000000..5d922d06e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h @@ -0,0 +1,82 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/io/async/AsyncServerSocket.h> +#include <folly/io/async/ScopedEventBaseThread.h> + +#include "rsocket/ConnectionAcceptor.h" + +namespace rsocket { + +/** + * TCP implementation of ConnectionAcceptor for use with RSocket::createServer + * + * Construction of this does nothing. The `start` method kicks off work. + */ +class TcpConnectionAcceptor : public ConnectionAcceptor { + public: + struct Options { + /// Address to listen on + folly::SocketAddress address{"::", 8080}; + + /// Number of worker threads processing requests. + size_t threads{2}; + + /// Number of connections to buffer before accept handlers process them. + int backlog{10}; + }; + + explicit TcpConnectionAcceptor(Options); + ~TcpConnectionAcceptor(); + + // ConnectionAcceptor overrides. + + /** + * Bind an AsyncServerSocket and start accepting TCP connections. + */ + void start(OnDuplexConnectionAccept) override; + + /** + * Shutdown the AsyncServerSocket and associated listener thread. + */ + void stop() override; + + /** + * Get the port being listened on. + */ + folly::Optional<uint16_t> listeningPort() const override; + + private: + class SocketCallback; + + /// Options this acceptor has been configured with. + const Options options_; + + /// The thread driving the AsyncServerSocket. + std::unique_ptr<folly::ScopedEventBaseThread> serverThread_; + + /// Function to run when a connection is accepted. + OnDuplexConnectionAccept onAccept_; + + /// The callbacks handling accepted connections. Each has its own worker + /// thread. + std::vector<std::unique_ptr<SocketCallback>> callbacks_; + + /// The socket listening for new connections. + folly::AsyncServerSocket::UniquePtr serverSocket_; +}; + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.cpp b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.cpp new file mode 100644 index 000000000..b970cd756 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.cpp @@ -0,0 +1,123 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/transports/tcp/TcpConnectionFactory.h" + +#include <folly/io/async/AsyncSSLSocket.h> +#include <folly/io/async/AsyncSocket.h> +#include <folly/io/async/AsyncTransport.h> +#include <folly/io/async/EventBaseManager.h> +#include <glog/logging.h> + +#include "rsocket/transports/tcp/TcpDuplexConnection.h" + +namespace rsocket { + +namespace { + +class ConnectCallback : public folly::AsyncSocket::ConnectCallback { + public: + ConnectCallback( + folly::SocketAddress address, + const std::shared_ptr<folly::SSLContext>& sslContext, + folly::Promise<ConnectionFactory::ConnectedDuplexConnection> + connectPromise) + : address_(address), connectPromise_(std::move(connectPromise)) { + VLOG(2) << "Constructing ConnectCallback"; + + // Set up by ScopedEventBaseThread. + auto evb = folly::EventBaseManager::get()->getExistingEventBase(); + DCHECK(evb); + + if (sslContext) { +#if !FOLLY_OPENSSL_HAS_ALPN + // setAdvertisedNextProtocols() is unavailable +#error ALPN is required for rsockets. \ + Your version of OpenSSL is likely too old. +#else + VLOG(3) << "Starting SSL socket"; + sslContext->setAdvertisedNextProtocols({"rs"}); +#endif + socket_.reset(new folly::AsyncSSLSocket(sslContext, evb)); + } else { + VLOG(3) << "Starting socket"; + socket_.reset(new folly::AsyncSocket(evb)); + } + + VLOG(3) << "Attempting connection to " << address_; + + socket_->connect(this, address_); + } + + ~ConnectCallback() override { + VLOG(2) << "Destroying ConnectCallback"; + } + + void connectSuccess() noexcept override { + std::unique_ptr<ConnectCallback> deleter(this); + VLOG(4) << "connectSuccess() on " << address_; + + auto connection = TcpConnectionFactory::createDuplexConnectionFromSocket( + std::move(socket_), RSocketStats::noop()); + auto evb = folly::EventBaseManager::get()->getExistingEventBase(); + CHECK(evb); + connectPromise_.setValue(ConnectionFactory::ConnectedDuplexConnection{ + std::move(connection), *evb}); + } + + void connectErr(const folly::AsyncSocketException& ex) noexcept override { + std::unique_ptr<ConnectCallback> deleter(this); + VLOG(4) << "connectErr(" << ex.what() << ") on " << address_; + connectPromise_.setException(ex); + } + + private: + const folly::SocketAddress address_; + folly::AsyncSocket::UniquePtr socket_; + folly::Promise<ConnectionFactory::ConnectedDuplexConnection> connectPromise_; +}; + +} // namespace + +TcpConnectionFactory::TcpConnectionFactory( + folly::EventBase& eventBase, + folly::SocketAddress address, + std::shared_ptr<folly::SSLContext> sslContext) + : eventBase_(&eventBase), + address_(std::move(address)), + sslContext_(std::move(sslContext)) {} + +TcpConnectionFactory::~TcpConnectionFactory() = default; + +folly::Future<ConnectionFactory::ConnectedDuplexConnection> +TcpConnectionFactory::connect(ProtocolVersion, ResumeStatus /* unused */) { + folly::Promise<ConnectionFactory::ConnectedDuplexConnection> connectPromise; + auto connectFuture = connectPromise.getFuture(); + + eventBase_->runInEventBaseThread( + [this, promise = std::move(connectPromise)]() mutable { + new ConnectCallback(address_, sslContext_, std::move(promise)); + }); + return connectFuture; +} + +std::unique_ptr<DuplexConnection> +TcpConnectionFactory::createDuplexConnectionFromSocket( + folly::AsyncTransportWrapper::UniquePtr socket, + std::shared_ptr<RSocketStats> stats) { + return std::make_unique<TcpDuplexConnection>( + std::move(socket), std::move(stats)); +} + +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h new file mode 100644 index 000000000..283b50eb5 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h @@ -0,0 +1,63 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/SocketAddress.h> +#include <folly/io/async/AsyncTransport.h> + +#include "rsocket/ConnectionFactory.h" +#include "rsocket/DuplexConnection.h" + +namespace folly { + +class SSLContext; +} + +namespace rsocket { + +class RSocketStats; + +/** + * TCP implementation of ConnectionFactory for use with RSocket::createClient(). + * + * Creation of this does nothing. The `start` method kicks off work. + */ +class TcpConnectionFactory : public ConnectionFactory { + public: + TcpConnectionFactory( + folly::EventBase& eventBase, + folly::SocketAddress address, + std::shared_ptr<folly::SSLContext> sslContext = nullptr); + virtual ~TcpConnectionFactory(); + + /** + * Connect to server defined in constructor. + * + * Each call to connect() creates a new AsyncSocket. + */ + folly::Future<ConnectedDuplexConnection> connect( + ProtocolVersion, + ResumeStatus resume) override; + + static std::unique_ptr<DuplexConnection> createDuplexConnectionFromSocket( + folly::AsyncTransportWrapper::UniquePtr socket, + std::shared_ptr<RSocketStats> stats = std::shared_ptr<RSocketStats>()); + + private: + folly::EventBase* eventBase_; + const folly::SocketAddress address_; + std::shared_ptr<folly::SSLContext> sslContext_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.cpp b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.cpp new file mode 100644 index 000000000..6a3f91a18 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.cpp @@ -0,0 +1,233 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "rsocket/transports/tcp/TcpDuplexConnection.h" + +#include <folly/ExceptionWrapper.h> +#include <folly/io/IOBufQueue.h> + +#include "rsocket/internal/Common.h" +#include "yarpl/flowable/Subscription.h" + +namespace rsocket { + +using namespace yarpl::flowable; + +class TcpReaderWriter : public folly::AsyncTransportWrapper::WriteCallback, + public folly::AsyncTransportWrapper::ReadCallback { + friend void intrusive_ptr_add_ref(TcpReaderWriter* x); + friend void intrusive_ptr_release(TcpReaderWriter* x); + + public: + explicit TcpReaderWriter( + folly::AsyncTransportWrapper::UniquePtr&& socket, + std::shared_ptr<RSocketStats> stats) + : socket_(std::move(socket)), stats_(std::move(stats)) {} + + ~TcpReaderWriter() override { + CHECK(isClosed()); + DCHECK(!inputSubscriber_); + } + + folly::AsyncTransportWrapper* getTransport() { + return socket_.get(); + } + + void setInput(std::shared_ptr<DuplexConnection::Subscriber> inputSubscriber) { + if (inputSubscriber && isClosed()) { + inputSubscriber->onComplete(); + return; + } + + if (!inputSubscriber) { + inputSubscriber_ = nullptr; + return; + } + + CHECK(!inputSubscriber_); + inputSubscriber_ = std::move(inputSubscriber); + + if (!socket_->getReadCallback()) { + // The AsyncSocket will hold a reference to this instance until it calls + // readEOF or readErr. + intrusive_ptr_add_ref(this); + socket_->setReadCB(this); + } + } + + void send(std::unique_ptr<folly::IOBuf> element) { + if (isClosed()) { + return; + } + + if (stats_) { + stats_->bytesWritten(element->computeChainDataLength()); + } + // now AsyncSocket will hold a reference to this instance as a writer until + // they call writeComplete or writeErr + intrusive_ptr_add_ref(this); + socket_->writeChain(this, std::move(element)); + } + + void close() { + if (auto socket = std::move(socket_)) { + socket->close(); + } + if (auto subscriber = std::move(inputSubscriber_)) { + subscriber->onComplete(); + } + } + + void closeErr(folly::exception_wrapper ew) { + if (auto socket = std::move(socket_)) { + socket->close(); + } + if (auto subscriber = std::move(inputSubscriber_)) { + subscriber->onError(std::move(ew)); + } + } + + private: + bool isClosed() const { + return !socket_; + } + + void writeSuccess() noexcept override { + intrusive_ptr_release(this); + } + + void writeErr( + size_t, + const folly::AsyncSocketException& exn) noexcept override { + closeErr(folly::exception_wrapper{std::make_exception_ptr(exn), exn}); + intrusive_ptr_release(this); + } + + void getReadBuffer(void** bufReturn, size_t* lenReturn) noexcept override { + std::tie(*bufReturn, *lenReturn) = readBuffer_.preallocate(4096, 4096); + } + + void readDataAvailable(size_t len) noexcept override { + readBuffer_.postallocate(len); + if (stats_) { + stats_->bytesRead(len); + } + + if (inputSubscriber_) { + readBufferAvailable(readBuffer_.split(len)); + } + } + + void readEOF() noexcept override { + close(); + intrusive_ptr_release(this); + } + + void readErr(const folly::AsyncSocketException& exn) noexcept override { + closeErr(folly::exception_wrapper{std::make_exception_ptr(exn), exn}); + intrusive_ptr_release(this); + } + + bool isBufferMovable() noexcept override { + return true; + } + + void readBufferAvailable( + std::unique_ptr<folly::IOBuf> readBuf) noexcept override { + CHECK(inputSubscriber_); + inputSubscriber_->onNext(std::move(readBuf)); + } + + folly::IOBufQueue readBuffer_{folly::IOBufQueue::cacheChainLength()}; + folly::AsyncTransportWrapper::UniquePtr socket_; + const std::shared_ptr<RSocketStats> stats_; + + std::shared_ptr<DuplexConnection::Subscriber> inputSubscriber_; + int refCount_{0}; +}; + +void intrusive_ptr_add_ref(TcpReaderWriter* x); +void intrusive_ptr_release(TcpReaderWriter* x); + +inline void intrusive_ptr_add_ref(TcpReaderWriter* x) { + ++x->refCount_; +} + +inline void intrusive_ptr_release(TcpReaderWriter* x) { + if (--x->refCount_ == 0) + delete x; +} + +namespace { + +class TcpInputSubscription : public Subscription { + public: + explicit TcpInputSubscription( + boost::intrusive_ptr<TcpReaderWriter> tcpReaderWriter) + : tcpReaderWriter_(std::move(tcpReaderWriter)) { + CHECK(tcpReaderWriter_); + } + + void request(int64_t n) noexcept override { + DCHECK(tcpReaderWriter_); + DCHECK_EQ(n, std::numeric_limits<int64_t>::max()) + << "TcpDuplexConnection doesnt support proper flow control"; + } + + void cancel() noexcept override { + tcpReaderWriter_->setInput(nullptr); + tcpReaderWriter_ = nullptr; + } + + private: + boost::intrusive_ptr<TcpReaderWriter> tcpReaderWriter_; +}; + +} // namespace + +TcpDuplexConnection::TcpDuplexConnection( + folly::AsyncTransportWrapper::UniquePtr&& socket, + std::shared_ptr<RSocketStats> stats) + : tcpReaderWriter_(new TcpReaderWriter(std::move(socket), stats)), + stats_(stats) { + if (stats_) { + stats_->duplexConnectionCreated("tcp", this); + } +} + +TcpDuplexConnection::~TcpDuplexConnection() { + if (stats_) { + stats_->duplexConnectionClosed("tcp", this); + } + tcpReaderWriter_->close(); +} + +folly::AsyncTransportWrapper* TcpDuplexConnection::getTransport() { + return tcpReaderWriter_ ? tcpReaderWriter_->getTransport() : nullptr; +} + +void TcpDuplexConnection::send(std::unique_ptr<folly::IOBuf> buf) { + if (tcpReaderWriter_) { + tcpReaderWriter_->send(std::move(buf)); + } +} + +void TcpDuplexConnection::setInput( + std::shared_ptr<DuplexConnection::Subscriber> inputSubscriber) { + // we don't care if the subscriber will call request synchronously + inputSubscriber->onSubscribe( + std::make_shared<TcpInputSubscription>(tcpReaderWriter_)); + tcpReaderWriter_->setInput(std::move(inputSubscriber)); +} +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h new file mode 100644 index 000000000..5bfa9adec --- /dev/null +++ b/ios/Pods/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include <folly/io/async/AsyncSocket.h> +#include <folly/io/async/AsyncTransport.h> + +#include "rsocket/DuplexConnection.h" +#include "rsocket/RSocketStats.h" +#include "yarpl/flowable/Subscriber.h" + +namespace rsocket { + +class TcpReaderWriter; + +class TcpDuplexConnection : public DuplexConnection { + public: + explicit TcpDuplexConnection( + folly::AsyncTransportWrapper::UniquePtr&& socket, + std::shared_ptr<RSocketStats> stats = RSocketStats::noop()); + ~TcpDuplexConnection(); + + void send(std::unique_ptr<folly::IOBuf>) override; + + void setInput(std::shared_ptr<DuplexConnection::Subscriber>) override; + + // Only to be used for observation purposes. + folly::AsyncTransportWrapper* getTransport(); + + private: + boost::intrusive_ptr<TcpReaderWriter> tcpReaderWriter_; + std::shared_ptr<RSocketStats> stats_; +}; +} // namespace rsocket diff --git a/ios/Pods/Flipper-RSocket/yarpl/Common.h b/ios/Pods/Flipper-RSocket/yarpl/Common.h new file mode 100644 index 000000000..be9d8287c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Common.h @@ -0,0 +1,69 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <memory> +#include <stdexcept> + +namespace yarpl { + +namespace observable { +template <typename T> +class Observable; +} // namespace observable + +namespace flowable { +template <typename T> +class Subscriber; + +// Exception thrown in case the downstream can't keep up. +class MissingBackpressureException : public std::runtime_error { + public: + MissingBackpressureException() + : std::runtime_error("BACK_PRESSURE: DROP (missing credits onNext)") {} +}; + +} // namespace flowable + +/** + *Strategy for backpressure when converting from Observable to Flowable. + */ +enum class BackpressureStrategy { + BUFFER, // Buffers all onNext values until the downstream consumes them. + DROP, // Drops the most recent onNext value if the downstream can't keep up. + ERROR, // Signals a MissingBackpressureException in case the downstream can't + // keep up. + LATEST, // Keeps only the latest onNext value, overwriting any previous value + // if the downstream can't keep up. + MISSING // OnNext events are written without any buffering or dropping. +}; + +template <typename T> +class IBackpressureStrategy { + public: + virtual ~IBackpressureStrategy() = default; + + virtual void init( + std::shared_ptr<observable::Observable<T>> upstream, + std::shared_ptr<flowable::Subscriber<T>> downstream) = 0; + + static std::shared_ptr<IBackpressureStrategy<T>> buffer(); + static std::shared_ptr<IBackpressureStrategy<T>> drop(); + static std::shared_ptr<IBackpressureStrategy<T>> error(); + static std::shared_ptr<IBackpressureStrategy<T>> latest(); + static std::shared_ptr<IBackpressureStrategy<T>> missing(); +}; + +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/Disposable.h b/ios/Pods/Flipper-RSocket/yarpl/Disposable.h new file mode 100644 index 000000000..6cc5d8264 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Disposable.h @@ -0,0 +1,42 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +namespace yarpl { + +/** + * Represents a disposable resource. + */ +class Disposable { + public: + Disposable() {} + virtual ~Disposable() = default; + Disposable(Disposable&&) = delete; + Disposable(const Disposable&) = delete; + Disposable& operator=(Disposable&&) = delete; + Disposable& operator=(const Disposable&) = delete; + + /** + * Dispose the resource, the operation should be idempotent. + */ + virtual void dispose() = 0; + + /** + * Returns true if this resource has been disposed. + * @return true if this resource has been disposed + */ + virtual bool isDisposed() = 0; +}; +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/Flowable.h b/ios/Pods/Flipper-RSocket/yarpl/Flowable.h new file mode 100644 index 000000000..34014ddd7 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Flowable.h @@ -0,0 +1,25 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +// include all the things a developer needs for using Flowable +#include "yarpl/flowable/Flowable.h" +#include "yarpl/flowable/Flowables.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/flowable/Subscription.h" + +/** + * // TODO add documentation + */ diff --git a/ios/Pods/Flipper-RSocket/yarpl/Observable.h b/ios/Pods/Flipper-RSocket/yarpl/Observable.h new file mode 100644 index 000000000..d115d5160 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Observable.h @@ -0,0 +1,25 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +// include all the things a developer needs for using Observable +#include "yarpl/observable/Observable.h" +#include "yarpl/observable/Observables.h" +#include "yarpl/observable/Observer.h" +#include "yarpl/observable/Subscription.h" + +/** + * // TODO add documentation + */ diff --git a/ios/Pods/Flipper-RSocket/yarpl/Refcounted.h b/ios/Pods/Flipper-RSocket/yarpl/Refcounted.h new file mode 100644 index 000000000..ac0a4950d --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Refcounted.h @@ -0,0 +1,85 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Synchronized.h> +#include <atomic> + +namespace yarpl { + +template <typename T> +struct AtomicReference { + folly::Synchronized<std::shared_ptr<T>, std::mutex> ref; + + AtomicReference() = default; + + AtomicReference(std::shared_ptr<T>&& r) { + *(ref.lock()) = std::move(r); + } +}; + +template <typename T> +std::shared_ptr<T> atomic_load(AtomicReference<T>* ar) { + return *(ar->ref.lock()); +} + +template <typename T> +std::shared_ptr<T> atomic_exchange( + AtomicReference<T>* ar, + std::shared_ptr<T> r) { + auto refptr = ar->ref.lock(); + auto old = std::move(*refptr); + *refptr = std::move(r); + return old; +} + +template <typename T> +std::shared_ptr<T> atomic_exchange(AtomicReference<T>* ar, std::nullptr_t) { + return atomic_exchange(ar, std::shared_ptr<T>()); +} + +template <typename T> +void atomic_store(AtomicReference<T>* ar, std::shared_ptr<T> r) { + *ar->ref.lock() = std::move(r); +} + +class enable_get_ref : public std::enable_shared_from_this<enable_get_ref> { + private: + virtual void dummy_internal_get_ref() {} + + protected: + // materialize a reference to 'this', but a type even further derived from + // Derived, because C++ doesn't have covariant return types on methods + template <typename As> + std::shared_ptr<As> ref_from_this(As* ptr) { + // at runtime, ensure that the most derived class can indeed be + // converted into an 'as' + (void)ptr; // silence 'unused parameter' errors in Release builds + return std::static_pointer_cast<As>(this->shared_from_this()); + } + + template <typename As> + std::shared_ptr<As> ref_from_this(As const* ptr) const { + // at runtime, ensure that the most derived class can indeed be + // converted into an 'as' + (void)ptr; // silence 'unused parameter' errors in Release builds + return std::static_pointer_cast<As const>(this->shared_from_this()); + } + + public: + virtual ~enable_get_ref() = default; +}; + +} /* namespace yarpl */ diff --git a/ios/Pods/Flipper-RSocket/yarpl/Single.h b/ios/Pods/Flipper-RSocket/yarpl/Single.h new file mode 100644 index 000000000..c5b737b5f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/Single.h @@ -0,0 +1,35 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/Refcounted.h" + +// include all the things a developer needs for using Single +#include "yarpl/single/Single.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleObservers.h" +#include "yarpl/single/SingleSubscriptions.h" +#include "yarpl/single/Singles.h" + +/** + * Create a single with code such as this: + * + * auto a = Single<int>::create([](std::shared_ptr<SingleObserver<int>> obs) { + * obs->onSubscribe(SingleSubscriptions::empty()); + * obs->onSuccess(1); + * }); + * + * // TODO add more documentation + */ diff --git a/ios/Pods/Flipper-RSocket/yarpl/examples/FlowableExamples.h b/ios/Pods/Flipper-RSocket/yarpl/examples/FlowableExamples.h new file mode 100644 index 000000000..675140b8b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/examples/FlowableExamples.h @@ -0,0 +1,24 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <iostream> +#include <memory> +#include <string> + +class FlowableExamples { + public: + static void run(); +}; diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h new file mode 100644 index 000000000..72d212c83 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h @@ -0,0 +1,165 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once +#include <folly/executors/GlobalExecutor.h> +#include <folly/experimental/coro/AsyncGenerator.h> +#include <folly/experimental/coro/Baton.h> +#include <folly/experimental/coro/Task.h> +#include <folly/experimental/coro/WithCancellation.h> +#include <memory> +#include "yarpl/flowable/Flowable.h" + +namespace yarpl { +namespace detail { +template <typename T> +class AsyncGeneratorShim { + public: + AsyncGeneratorShim( + folly::coro::AsyncGenerator<T&&>&& generator, + folly::SequencedExecutor* ex) + : generator_(std::move(generator)), + sharedState_(std::make_shared<SharedState>()) { + sharedState_->executor_ = folly::getKeepAliveToken(ex); + } + + void subscribe( + std::shared_ptr<yarpl::flowable::Subscriber<T>> subscriber) && { + class Subscription : public yarpl::flowable::Subscription { + public: + explicit Subscription(std::weak_ptr<SharedState> state) + : state_(std::move(state)) {} + + void request(int64_t n) override { + if (auto state = state_.lock()) { + state->executor_->add([n, state = std::move(state)]() { + if (state->requested_ == credits::kNoFlowControl || + n == credits::kNoFlowControl) { + state->requested_ = credits::kNoFlowControl; + } else { + state->requested_ += n; + } + state->baton_.post(); + }); + } + } + + void cancel() override { + if (auto state = state_.lock()) { + state->executor_->add([state = std::move(state)]() { + // requestCancellation will execute registered CancellationCallback + // inline, but CancellationCallback should be run in + // executor_ thread + state->cancelSource_.requestCancellation(); + state->baton_.post(); + }); + } + } + + private: + std::weak_ptr<SharedState> state_; + }; + sharedState_->executor_->add( + [keepAlive = sharedState_->executor_.copy(), + subscriber, + subscription = std::make_shared<Subscription>( + std::weak_ptr<SharedState>(sharedState_))]() mutable { + subscriber->onSubscribe(std::move(subscription)); + }); + auto executor = sharedState_->executor_.get(); + folly::coro::co_withCancellation( + sharedState_->cancelSource_.getToken(), + folly::coro::co_invoke( + [subscriber = std::move(subscriber), + self = std::move(*this)]() mutable -> folly::coro::Task<void> { + while (true) { + while (self.sharedState_->requested_ == 0 && + !self.sharedState_->cancelSource_ + .isCancellationRequested()) { + co_await self.sharedState_->baton_; + self.sharedState_->baton_.reset(); + } + + if (self.sharedState_->cancelSource_ + .isCancellationRequested()) { + self.sharedState_->executor_->add( + [subscriber = std::move(subscriber)]() { + // destory subscriber on executor_ thread + }); + co_return; + } + + folly::Try<T> value; + try { + auto item = co_await self.generator_.next(); + + if (item.has_value()) { + value.emplace(std::move(*item)); + } + } catch (const std::exception& ex) { + value.emplaceException(std::current_exception(), ex); + } catch (...) { + value.emplaceException(std::current_exception()); + } + + if (value.hasValue()) { + self.sharedState_->executor_->add( + [subscriber, + keepAlive = self.sharedState_->executor_.copy(), + value = std::move(value)]() mutable { + subscriber->onNext(std::move(value).value()); + }); + } else if (value.hasException()) { + self.sharedState_->executor_->add( + [subscriber = std::move(subscriber), + keepAlive = self.sharedState_->executor_.copy(), + value = std::move(value)]() mutable { + subscriber->onError(std::move(value).exception()); + }); + co_return; + } else { + self.sharedState_->executor_->add( + [subscriber = std::move(subscriber), + keepAlive = + self.sharedState_->executor_.copy()]() mutable { + subscriber->onComplete(); + }); + co_return; + } + + if (self.sharedState_->requested_ != credits::kNoFlowControl) { + self.sharedState_->requested_--; + } + } + })) + .scheduleOn(std::move(executor)) + .start(); + } + + private: + struct SharedState { + SharedState() = default; + explicit SharedState(folly::CancellationSource source) + : cancelSource_(std::move(source)) {} + folly::Executor::KeepAlive<folly::SequencedExecutor> executor_; + int64_t requested_{0}; + folly::coro::Baton baton_{0}; + folly::CancellationSource cancelSource_; + }; + + folly::coro::AsyncGenerator<T&&> generator_; + std::shared_ptr<SharedState> sharedState_; +}; +} // namespace detail + +template <typename T> +std::shared_ptr<yarpl::flowable::Flowable<T>> toFlowable( + folly::coro::AsyncGenerator<T&&> gen, + folly::SequencedExecutor* ex = folly::getEventBase()) { + return yarpl::flowable::internal::flowableFromSubscriber<T>( + [gen = std::move(gen), + ex](std::shared_ptr<yarpl::flowable::Subscriber<T>> subscriber) mutable { + detail::AsyncGeneratorShim<T>(std::move(gen), ex) + .subscribe(std::move(subscriber)); + }); +} +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h new file mode 100644 index 000000000..0933a6908 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/flowable/Subscriber.h" + +#include <stdexcept> + +namespace yarpl { +namespace flowable { + +/** + * A Subscriber that always cancels the subscription passed to it. + */ +template <typename T> +class CancelingSubscriber final : public BaseSubscriber<T> { + public: + void onSubscribeImpl() override { + this->cancel(); + } + + void onNextImpl(T) override { + throw std::logic_error{"CancelingSubscriber::onNext() can never be called"}; + } + void onCompleteImpl() override { + throw std::logic_error{ + "CancelingSubscriber::onComplete() can never be called"}; + } + void onErrorImpl(folly::exception_wrapper) override { + throw std::logic_error{ + "CancelingSubscriber::onError() can never be called"}; + } +}; +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/DeferFlowable.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/DeferFlowable.h new file mode 100644 index 000000000..b817c85f4 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/DeferFlowable.h @@ -0,0 +1,49 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/flowable/Flowable.h" + +namespace yarpl { +namespace flowable { +namespace details { + +template <typename T, typename FlowableFactory> +class DeferFlowable : public Flowable<T> { + static_assert( + std::is_same<std::decay_t<FlowableFactory>, FlowableFactory>::value, + "undecayed"); + + public: + template <typename F> + explicit DeferFlowable(F&& factory) : factory_(std::forward<F>(factory)) {} + + virtual void subscribe(std::shared_ptr<Subscriber<T>> subscriber) { + std::shared_ptr<Flowable<T>> flowable; + try { + flowable = factory_(); + } catch (const std::exception& ex) { + flowable = Flowable<T>::error(ex, std::current_exception()); + } + flowable->subscribe(std::move(subscriber)); + } + + private: + FlowableFactory factory_; +}; + +} // namespace details +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h new file mode 100644 index 000000000..5c5089551 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h @@ -0,0 +1,320 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <atomic> +#include <memory> +#include <utility> + +#include <folly/Conv.h> +#include <folly/ScopeGuard.h> + +#include "yarpl/flowable/Flowable.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { +namespace flowable { +namespace details { + +template <typename T> +class EmitterBase { + public: + virtual ~EmitterBase() = default; + + virtual std::tuple<int64_t, bool> emit( + std::shared_ptr<Subscriber<T>>, + int64_t) = 0; +}; + +/** + * Manager for a flowable subscription. + * + * This is synchronous: the emit calls are triggered within the context + * of a request(n) call. + */ +template <typename T> +class EmiterSubscription final : public Subscription, + public Subscriber<T>, + public yarpl::enable_get_ref { + constexpr static auto kCanceled = credits::kCanceled; + constexpr static auto kNoFlowControl = credits::kNoFlowControl; + + public: + EmiterSubscription( + std::shared_ptr<EmitterBase<T>> emitter, + std::shared_ptr<Subscriber<T>> subscriber) + : emitter_(std::move(emitter)), subscriber_(std::move(subscriber)) {} + + void init() { + subscriber_->onSubscribe(this->ref_from_this(this)); + } + + virtual ~EmiterSubscription() { + subscriber_.reset(); + } + + void request(int64_t delta) override { + while (true) { + auto current = requested_.load(std::memory_order_relaxed); + + if (current == kCanceled) { + // this can happen because there could be an async barrier between the + // subscriber and the subscription for instance while onComplete is + // being delivered (on effectively cancelled subscription) the + // subscriber can call request(n) + return; + } + + auto const total = credits::add(current, delta); + if (requested_.compare_exchange_strong(current, total)) { + break; + } + } + + process(); + } + + void cancel() override { + // if this is the first terminating signal to receive, we need to + // make sure we break the reference cycle between subscription and + // subscriber + auto previous = requested_.exchange(kCanceled, std::memory_order_relaxed); + if (previous != kCanceled) { + // this can happen because there could be an async barrier between the + // subscriber and the subscription for instance while onComplete is being + // delivered (on effectively cancelled subscription) the subscriber can + // call request(n) + process(); + } + } + + // Subscriber methods. + void onSubscribe(std::shared_ptr<Subscription>) override { + LOG(FATAL) << "Do not call this method"; + } + + void onNext(T value) override { +#ifndef NDEBUG + DCHECK(!hasFinished_) << "onComplete() or onError() already called"; +#endif + if (subscriber_) { + subscriber_->onNext(std::move(value)); + } else { + DCHECK(requested_.load(std::memory_order_relaxed) == kCanceled); + } + } + + void onComplete() override { +#ifndef NDEBUG + DCHECK(!hasFinished_) << "onComplete() or onError() already called"; + hasFinished_ = true; +#endif + if (subscriber_) { + subscriber_->onComplete(); + } else { + DCHECK(requested_.load(std::memory_order_relaxed) == kCanceled); + } + } + + void onError(folly::exception_wrapper error) override { +#ifndef NDEBUG + DCHECK(!hasFinished_) << "onComplete() or onError() already called"; + hasFinished_ = true; +#endif + if (subscriber_) { + subscriber_->onError(error); + } else { + DCHECK(requested_.load(std::memory_order_relaxed) == kCanceled); + } + } + + private: + // Processing loop. Note: this can delete `this` upon completion, + // error, or cancellation; thus, no fields should be accessed once + // this method returns. + // + // Thread-Safety: there is no guarantee as to which thread this is + // invoked on. However, there is a strong guarantee on cancel and + // request(n) calls: no more than one instance of either of these + // can be outstanding at any time. + void process() { + // Guards against re-entrancy in request(n) calls. + if (processing_.exchange(true)) { + return; + } + + auto guard = folly::makeGuard([this] { processing_ = false; }); + + // Keep a reference to ourselves here in case the emit() call + // frees all other references to 'this' + auto this_subscriber = this->ref_from_this(this); + + while (true) { + auto current = requested_.load(std::memory_order_relaxed); + + // Subscription was canceled, completed, or had an error. + if (current == kCanceled) { + guard.dismiss(); + release(); + return; + } + + // If no more items can be emitted now, wait for a request(n). + // See note above re: thread-safety. We are guaranteed that + // request(n) is not simultaneously invoked on another thread. + if (current <= 0) + return; + + int64_t emitted; + bool done; + + std::tie(emitted, done) = emitter_->emit(this_subscriber, current); + + while (true) { + current = requested_.load(std::memory_order_relaxed); + if (current == kCanceled) { + break; + } + int64_t updated; + // generally speaking updated will be number of credits lefted over + // after emitter_->emit(), so updated = current - emitted + // need to handle case where done = true and avoid doing arithmetic + // operation on kNoFlowControl + + // in asynchrnous emitter cases, might have emitted=kNoFlowControl + // this means that emitter will take the responsibility to send the + // whole conext and credits lefted over should be set to 0. + if (current == kNoFlowControl) { + updated = + done ? kCanceled : emitted == kNoFlowControl ? 0 : kNoFlowControl; + } else { + updated = done ? kCanceled : current - emitted; + } + if (requested_.compare_exchange_strong(current, updated)) { + break; + } + } + } + } + + void release() { + emitter_.reset(); + subscriber_.reset(); + } + + // The number of items that can be sent downstream. Each request(n) + // adds n; each onNext consumes 1. If this is MAX, flow-control is + // disabled: items sent downstream don't consume any longer. A MIN + // value represents cancellation. Other -ve values aren't permitted. + std::atomic_int_fast64_t requested_{0}; + +#ifndef NDEBUG + bool hasFinished_{false}; // onComplete or onError called +#endif + + // We don't want to recursively invoke process(); one loop should do. + std::atomic_bool processing_{false}; + + std::shared_ptr<EmitterBase<T>> emitter_; + std::shared_ptr<Subscriber<T>> subscriber_; +}; + +template <typename T> +class TrackingSubscriber : public Subscriber<T> { + public: + TrackingSubscriber( + Subscriber<T>& subscriber, + int64_t +#ifndef NDEBUG + requested +#endif + ) + : inner_(&subscriber) +#ifndef NDEBUG + , + requested_(requested) +#endif + { + } + + void onSubscribe(std::shared_ptr<Subscription> s) override { + inner_->onSubscribe(std::move(s)); + } + + void onComplete() override { + completed_ = true; + inner_->onComplete(); + } + + void onError(folly::exception_wrapper ex) override { + completed_ = true; + inner_->onError(std::move(ex)); + } + + void onNext(T value) override { +#ifndef NDEBUG + auto old = requested_; + DCHECK(old > credits::consume(requested_, 1)) + << "cannot emit more than requested"; +#endif + emitted_++; + inner_->onNext(std::move(value)); + } + + auto getResult() { + return std::make_tuple(emitted_, completed_); + } + + private: + int64_t emitted_{0}; + bool completed_{false}; + Subscriber<T>* inner_; +#ifndef NDEBUG + int64_t requested_; +#endif +}; + +template <typename T, typename Emitter> +class EmitterWrapper : public EmitterBase<T>, public Flowable<T> { + static_assert( + std::is_same<std::decay_t<Emitter>, Emitter>::value, + "undecayed"); + + public: + template <typename F> + explicit EmitterWrapper(F&& emitter) : emitter_(std::forward<F>(emitter)) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + auto ef = std::make_shared<EmiterSubscription<T>>( + this->ref_from_this(this), std::move(subscriber)); + ef->init(); + } + + std::tuple<int64_t, bool> emit( + std::shared_ptr<Subscriber<T>> subscriber, + int64_t requested) override { + TrackingSubscriber<T> trackingSubscriber(*subscriber, requested); + emitter_(trackingSubscriber, requested); + return trackingSubscriber.getResult(); + } + + private: + Emitter emitter_; +}; + +} // namespace details +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable.h new file mode 100644 index 000000000..9dff78b03 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable.h @@ -0,0 +1,749 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Executor.h> +#include <folly/functional/Invoke.h> +#include <folly/io/async/HHWheelTimer.h> +#include <glog/logging.h> +#include <memory> +#include "yarpl/Disposable.h" +#include "yarpl/Refcounted.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { + +class TimeoutException; +namespace detail { +class TimeoutExceptionGenerator; +} + +namespace flowable { + +template <typename T = void> +class Flowable; + +namespace details { + +template <typename T> +struct IsFlowable : std::false_type {}; + +template <typename R> +struct IsFlowable<std::shared_ptr<Flowable<R>>> : std::true_type { + using ElemType = R; +}; + +} // namespace details + +template <typename T> +class Flowable : public yarpl::enable_get_ref { + public: + virtual ~Flowable() = default; + + virtual void subscribe(std::shared_ptr<Subscriber<T>>) = 0; + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Next, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value>::type> + std::unique_ptr<Disposable> subscribe( + Next&& next, + int64_t batch = credits::kNoFlowControl) { + auto subscriber = + details::LambdaSubscriber<T>::create(std::forward<Next>(next), batch); + subscribe(subscriber); + return std::make_unique<details::BaseSubscriberDisposable<T>>( + std::move(subscriber)); + } + + /** + * Subscribe overload that accepts lambdas. + * + * Takes an optional batch size for request_n. Default is no flow control. + */ + template < + typename Next, + typename Error, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type> + std::unique_ptr<Disposable> subscribe( + Next&& next, + Error&& e, + int64_t batch = credits::kNoFlowControl) { + auto subscriber = details::LambdaSubscriber<T>::create( + std::forward<Next>(next), std::forward<Error>(e), batch); + subscribe(subscriber); + return std::make_unique<details::BaseSubscriberDisposable<T>>( + std::move(subscriber)); + } + + /** + * Subscribe overload that accepts lambdas. + * + * Takes an optional batch size for request_n. Default is no flow control. + */ + template < + typename Next, + typename Error, + typename Complete, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value && + folly::is_invocable<std::decay_t<Complete>&>::value>::type> + std::unique_ptr<Disposable> subscribe( + Next&& next, + Error&& e, + Complete&& complete, + int64_t batch = credits::kNoFlowControl) { + auto subscriber = details::LambdaSubscriber<T>::create( + std::forward<Next>(next), + std::forward<Error>(e), + std::forward<Complete>(complete), + batch); + subscribe(subscriber); + return std::make_unique<details::BaseSubscriberDisposable<T>>( + std::move(subscriber)); + } + + void subscribe() { + subscribe(Subscriber<T>::create()); + } + + // + // creator methods: + // + + // Creates Flowable which completes the subscriber right after it subscribes + static std::shared_ptr<Flowable<T>> empty(); + + // Creates Flowable which will never terminate the subscriber + static std::shared_ptr<Flowable<T>> never(); + + // Create Flowable which will imediatelly terminate the subscriber upon + // subscription with the provided error + static std::shared_ptr<Flowable<T>> error(folly::exception_wrapper ex); + + template <typename Ex> + static std::shared_ptr<Flowable<T>> error(Ex&) { + static_assert( + std::is_lvalue_reference<Ex>::value, + "use variant of error() method accepting also exception_ptr"); + } + + template <typename Ex> + static std::shared_ptr<Flowable<T>> error(Ex& ex, std::exception_ptr ptr) { + return Flowable<T>::error(folly::exception_wrapper(std::move(ptr), ex)); + } + + static std::shared_ptr<Flowable<T>> just(T value) { + auto lambda = [value = std::move(value)]( + Subscriber<T>& subscriber, int64_t requested) mutable { + DCHECK_GT(requested, 0); + subscriber.onNext(std::move(value)); + subscriber.onComplete(); + }; + + return Flowable<T>::create(std::move(lambda)); + } + + static std::shared_ptr<Flowable<T>> justN(std::initializer_list<T> list) { + auto lambda = [v = std::vector<T>(std::move(list)), i = size_t{0}]( + Subscriber<T>& subscriber, int64_t requested) mutable { + while (i < v.size() && requested-- > 0) { + subscriber.onNext(v[i++]); + } + + if (i == v.size()) { + // TODO T27302402: Even though having two subscriptions exist + // concurrently for Emitters is not possible still. At least it possible + // to resubscribe and consume the same values again. + i = 0; + subscriber.onComplete(); + } + }; + + return Flowable<T>::create(std::move(lambda)); + } + + // this will generate a flowable which can be subscribed to only once + static std::shared_ptr<Flowable<T>> justOnce(T value) { + auto lambda = [value = std::move(value), used = false]( + Subscriber<T>& subscriber, int64_t) mutable { + if (used) { + subscriber.onError( + std::runtime_error("justOnce value was already used")); + return; + } + + used = true; + // # requested should be > 0. Ignoring the actual parameter. + subscriber.onNext(std::move(value)); + subscriber.onComplete(); + }; + + return Flowable<T>::create(std::move(lambda)); + } + + template <typename TGenerator> + static std::shared_ptr<Flowable<T>> fromGenerator(TGenerator&& generator); + + /** + * The Defer operator waits until a subscriber subscribes to it, and then it + * generates a Flowabe with a FlowableFactory function. It + * does this afresh for each subscriber, so although each subscriber may + * think it is subscribing to the same Flowable, in fact each subscriber + * gets its own individual sequence. + */ + template < + typename FlowableFactory, + typename = typename std::enable_if<folly::is_invocable_r< + std::shared_ptr<Flowable<T>>, + std::decay_t<FlowableFactory>&>::value>::type> + static std::shared_ptr<Flowable<T>> defer(FlowableFactory&&); + + template < + typename Function, + typename ErrorFunction = + folly::Function<folly::exception_wrapper(folly::exception_wrapper&&)>, + typename R = typename folly::invoke_result_t<Function, T>, + typename = typename std::enable_if<folly::is_invocable_r< + folly::exception_wrapper, + std::decay_t<ErrorFunction>&, + folly::exception_wrapper&&>::value>::type> + std::shared_ptr<Flowable<R>> map( + Function&& function, + ErrorFunction&& errormapFunc = [](folly::exception_wrapper&& ew) { + return std::move(ew); + }); + + template < + typename Function, + typename R = typename details::IsFlowable< + typename folly::invoke_result_t<Function, T>>::ElemType> + std::shared_ptr<Flowable<R>> flatMap(Function&& func); + + template <typename Function> + std::shared_ptr<Flowable<T>> filter(Function&& function); + + template < + typename Function, + typename R = typename folly::invoke_result_t<Function, T, T>> + std::shared_ptr<Flowable<R>> reduce(Function&& function); + + std::shared_ptr<Flowable<T>> take(int64_t); + + std::shared_ptr<Flowable<T>> skip(int64_t); + + std::shared_ptr<Flowable<T>> ignoreElements(); + + /* + * To instruct a Flowable to do its work on a particular Executor. + * the onSubscribe, request and cancel methods will be scheduled on the + * provided executor + */ + std::shared_ptr<Flowable<T>> subscribeOn(folly::Executor&); + + std::shared_ptr<Flowable<T>> observeOn(folly::Executor&); + + std::shared_ptr<Flowable<T>> observeOn(folly::Executor::KeepAlive<>); + + std::shared_ptr<Flowable<T>> concatWith(std::shared_ptr<Flowable<T>>); + + template <typename... Args> + std::shared_ptr<Flowable<T>> concatWith( + std::shared_ptr<Flowable<T>> first, + Args... args) { + return concatWith(first)->concatWith(args...); + } + + template <typename... Args> + static std::shared_ptr<Flowable<T>> concat( + std::shared_ptr<Flowable<T>> first, + Args... args) { + return first->concatWith(args...); + } + + template <typename Q> + using enableWrapRef = + typename std::enable_if<details::IsFlowable<Q>::value, Q>::type; + + // Combines multiple Flowables so that they act like a + // single Flowable. The items + // emitted by the merged Flowables may interlieve. + template <typename Q = T> + enableWrapRef<Q> merge() { + return this->flatMap([](auto f) { return std::move(f); }); + } + + // function is invoked when onComplete occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Flowable<T>> doOnSubscribe(Function&& function); + + // function is invoked when onNext occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&, const T&>::value>::type> + std::shared_ptr<Flowable<T>> doOnNext(Function&& function); + + // function is invoked when onError occurs. + template < + typename Function, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<Function>&, + folly::exception_wrapper&>::value>::type> + std::shared_ptr<Flowable<T>> doOnError(Function&& function); + + // function is invoked when onComplete occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Flowable<T>> doOnComplete(Function&& function); + + // function is invoked when either onComplete or onError occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Flowable<T>> doOnTerminate(Function&& function); + + // the function is invoked for each of onNext, onCompleted, onError + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Flowable<T>> doOnEach(Function&& function); + + // function is invoked when request(n) is called. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&, int64_t>::value>::type> + std::shared_ptr<Flowable<T>> doOnRequest(Function&& function); + + // function is invoked when cancel is called. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Flowable<T>> doOnCancel(Function&& function); + + // the callbacks will be invoked of each of the signals + template < + typename OnNextFunc, + typename OnCompleteFunc, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnNextFunc>&, const T&>::value>:: + type, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnCompleteFunc>&>::value>::type> + std::shared_ptr<Flowable<T>> doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete); + + // the callbacks will be invoked of each of the signals + template < + typename OnNextFunc, + typename OnCompleteFunc, + typename OnErrorFunc, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnNextFunc>&, const T&>::value>:: + type, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnCompleteFunc>&>::value>::type, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<OnErrorFunc>&, + folly::exception_wrapper&>::value>::type> + std::shared_ptr<Flowable<T>> + doOn(OnNextFunc&& onNext, OnCompleteFunc&& onComplete, OnErrorFunc&& onError); + + template < + typename ExceptionGenerator = yarpl::detail::TimeoutExceptionGenerator> + std::shared_ptr<Flowable<T>> timeout( + folly::EventBase& timerEvb, + std::chrono::milliseconds timeout, + std::chrono::milliseconds initTimeout, + ExceptionGenerator&& exnGen = ExceptionGenerator()); + + template < + typename Emitter, + typename = typename std::enable_if<folly::is_invocable_r< + void, + std::decay_t<Emitter>&, + Subscriber<T>&, + int64_t>::value>::type> + static std::shared_ptr<Flowable<T>> create(Emitter&& emitter); + + template < + typename OnSubscribe, + typename = typename std::enable_if<folly::is_invocable< + OnSubscribe&&, + std::shared_ptr<Subscriber<T>>>::value>::type> + // TODO(lehecka): enable this warning once mobile code is clear + // [[deprecated( + // "Flowable<T>::fromPublisher is deprecated: Use PublishProcessor or " + // "contact rsocket team if you can't figure out what to replace it " + // "with")]] + static std::shared_ptr<Flowable<T>> fromPublisher(OnSubscribe&& function); +}; + +} // namespace flowable +} // namespace yarpl + +#include "yarpl/flowable/DeferFlowable.h" +#include "yarpl/flowable/EmitterFlowable.h" +#include "yarpl/flowable/FlowableOperator.h" + +namespace yarpl { +namespace flowable { + +template <typename T> +template <typename Emitter, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::create(Emitter&& emitter) { + return std::make_shared<details::EmitterWrapper<T, std::decay_t<Emitter>>>( + std::forward<Emitter>(emitter)); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::empty() { + class EmptyFlowable : public Flowable<T> { + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + subscriber->onSubscribe(Subscription::create()); + // does not wait for request(n) to complete + subscriber->onComplete(); + } + }; + return std::make_shared<EmptyFlowable>(); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::never() { + class NeverFlowable : public Flowable<T> { + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + subscriber->onSubscribe(Subscription::create()); + } + }; + return std::make_shared<NeverFlowable>(); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::error(folly::exception_wrapper ex) { + class ErrorFlowable : public Flowable<T> { + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + subscriber->onSubscribe(Subscription::create()); + // does not wait for request(n) to error + subscriber->onError(ex_); + } + folly::exception_wrapper ex_; + + public: + explicit ErrorFlowable(folly::exception_wrapper ew) : ex_(std::move(ew)) {} + }; + return std::make_shared<ErrorFlowable>(std::move(ex)); +} + +namespace internal { +template <typename T, typename OnSubscribe> +std::shared_ptr<Flowable<T>> flowableFromSubscriber(OnSubscribe&& function) { + return std::make_shared<FromPublisherOperator<T, std::decay_t<OnSubscribe>>>( + std::forward<OnSubscribe>(function)); +} +} // namespace internal + +// TODO(lehecka): remove +template <typename T> +template <typename OnSubscribe, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::fromPublisher( + OnSubscribe&& function) { + return internal::flowableFromSubscriber<T>( + std::forward<OnSubscribe>(function)); +} + +template <typename T> +template <typename TGenerator> +std::shared_ptr<Flowable<T>> Flowable<T>::fromGenerator( + TGenerator&& generator) { + auto lambda = [generator = std::forward<TGenerator>(generator)]( + Subscriber<T>& subscriber, int64_t requested) mutable { + try { + while (requested-- > 0) { + subscriber.onNext(generator()); + } + } catch (const std::exception& ex) { + subscriber.onError( + folly::exception_wrapper(std::current_exception(), ex)); + } catch (...) { + subscriber.onError(std::runtime_error( + "Flowable::fromGenerator() threw from Subscriber:onNext()")); + } + }; + return Flowable<T>::create(std::move(lambda)); +} // namespace flowable + +template <typename T> +template <typename FlowableFactory, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::defer(FlowableFactory&& factory) { + return std::make_shared< + details::DeferFlowable<T, std::decay_t<FlowableFactory>>>( + std::forward<FlowableFactory>(factory)); +} + +template <typename T> +template <typename Function, typename ErrorFunction, typename R, typename> +std::shared_ptr<Flowable<R>> Flowable<T>::map( + Function&& function, + ErrorFunction&& errorFunction) { + return std::make_shared< + MapOperator<T, R, std::decay_t<Function>, std::decay_t<ErrorFunction>>>( + this->ref_from_this(this), + std::forward<Function>(function), + std::forward<ErrorFunction>(errorFunction)); +} + +template <typename T> +template <typename Function> +std::shared_ptr<Flowable<T>> Flowable<T>::filter(Function&& function) { + return std::make_shared<FilterOperator<T, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +template <typename Function, typename R> +std::shared_ptr<Flowable<R>> Flowable<T>::reduce(Function&& function) { + return std::make_shared<ReduceOperator<T, R, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::take(int64_t limit) { + return std::make_shared<TakeOperator<T>>(this->ref_from_this(this), limit); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::skip(int64_t offset) { + return std::make_shared<SkipOperator<T>>(this->ref_from_this(this), offset); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::ignoreElements() { + return std::make_shared<IgnoreElementsOperator<T>>(this->ref_from_this(this)); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::subscribeOn( + folly::Executor& executor) { + return std::make_shared<SubscribeOnOperator<T>>( + this->ref_from_this(this), executor); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::observeOn(folly::Executor& executor) { + return observeOn(folly::getKeepAliveToken(executor)); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::observeOn( + folly::Executor::KeepAlive<> executor) { + return std::make_shared<yarpl::flowable::detail::ObserveOnOperator<T>>( + this->ref_from_this(this), std::move(executor)); +} + +template <typename T> +template <typename Function, typename R> +std::shared_ptr<Flowable<R>> Flowable<T>::flatMap(Function&& function) { + return std::make_shared<FlatMapOperator<T, R>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +std::shared_ptr<Flowable<T>> Flowable<T>::concatWith( + std::shared_ptr<Flowable<T>> next) { + return std::make_shared<details::ConcatWithOperator<T>>( + this->ref_from_this(this), std::move(next)); +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnSubscribe(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + std::forward<Function>(function), + [](const T&) {}, + [](const auto&) {}, + [] {}, + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnNext(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<Function>(function), + [](const auto&) {}, + [] {}, + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnError(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + std::forward<Function>(function), + [] {}, + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnComplete(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + [](const auto&) {}, + std::forward<Function>(function), + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnTerminate(Function&& function) { + auto sharedFunction = std::make_shared<std::decay_t<Function>>( + std::forward<Function>(function)); + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + [sharedFunction](const auto&) { (*sharedFunction)(); }, + [sharedFunction]() { (*sharedFunction)(); }, + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnEach(Function&& function) { + auto sharedFunction = std::make_shared<std::decay_t<Function>>( + std::forward<Function>(function)); + return details::createDoOperator( + ref_from_this(this), + [] {}, + [sharedFunction](const T&) { (*sharedFunction)(); }, + [sharedFunction](const auto&) { (*sharedFunction)(); }, + [sharedFunction]() { (*sharedFunction)(); }, + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename OnNextFunc, typename OnCompleteFunc, typename, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<OnNextFunc>(onNext), + [](const auto&) {}, + std::forward<OnCompleteFunc>(onComplete), + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template < + typename OnNextFunc, + typename OnCompleteFunc, + typename OnErrorFunc, + typename, + typename, + typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete, + OnErrorFunc&& onError) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<OnNextFunc>(onNext), + std::forward<OnErrorFunc>(onError), + std::forward<OnCompleteFunc>(onComplete), + [](const auto&) {}, // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnRequest(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, // onSubscribe + [](const auto&) {}, // onNext + [](const auto&) {}, // onError + [] {}, // onComplete + std::forward<Function>(function), // onRequest + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Flowable<T>> Flowable<T>::doOnCancel(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, // onSubscribe + [](const auto&) {}, // onNext + [](const auto&) {}, // onError + [] {}, // onComplete + [](const auto&) {}, // onRequest + std::forward<Function>(function)); // onCancel +} + +template <typename T> +template <typename ExceptionGenerator> +std::shared_ptr<Flowable<T>> Flowable<T>::timeout( + folly::EventBase& timerEvb, + std::chrono::milliseconds starvationTimeout, + std::chrono::milliseconds initTimeout, + ExceptionGenerator&& exnGen) { + return std::make_shared<details::TimeoutOperator<T, ExceptionGenerator>>( + ref_from_this(this), + timerEvb, + starvationTimeout, + initTimeout, + std::forward<ExceptionGenerator>(exnGen)); +} + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h new file mode 100644 index 000000000..56694146b --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h @@ -0,0 +1,189 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/flowable/FlowableOperator.h" + +namespace yarpl { +namespace flowable { +namespace details { + +template <typename T> +class ConcatWithOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + + public: + ConcatWithOperator( + std::shared_ptr<Flowable<T>> first, + std::shared_ptr<Flowable<T>> second) + : first_(std::move(first)), second_(std::move(second)) { + CHECK(first_); + CHECK(second_); + } + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + auto subscription = + std::make_shared<ConcatWithSubscription>(subscriber, first_, second_); + subscription->init(); + } + + private: + class ForwardSubscriber; + + // Downstream will always point to this subscription + class ConcatWithSubscription + : public yarpl::flowable::Subscription, + public std::enable_shared_from_this<ConcatWithSubscription> { + public: + ConcatWithSubscription( + std::shared_ptr<Subscriber<T>> subscriber, + std::shared_ptr<Flowable<T>> first, + std::shared_ptr<Flowable<T>> second) + : downSubscriber_(std::move(subscriber)), + first_(std::move(first)), + second_(std::move(second)) {} + + void init() { + upSubscriber_ = + std::make_shared<ForwardSubscriber>(this->shared_from_this()); + first_->subscribe(upSubscriber_); + downSubscriber_->onSubscribe(this->shared_from_this()); + } + + void request(int64_t n) override { + credits::add(&requested_, n); + if (!upSubscriber_) { + if (auto second = std::exchange(second_, nullptr)) { + upSubscriber_ = std::make_shared<ForwardSubscriber>( + this->shared_from_this(), requested_); + second->subscribe(upSubscriber_); + } + } else { + upSubscriber_->request(n); + } + } + + void cancel() override { + if (auto subscriber = std::move(upSubscriber_)) { + subscriber->cancel(); + } + first_.reset(); + second_.reset(); + downSubscriber_.reset(); + upSubscriber_.reset(); + } + + void onNext(T value) { + credits::consume(&requested_, 1); + downSubscriber_->onNext(std::move(value)); + } + + void onComplete() { + upSubscriber_.reset(); + if (auto first = std::move(first_)) { + if (requested_ > 0) { + if (auto second = std::exchange(second_, nullptr)) { + upSubscriber_ = std::make_shared<ForwardSubscriber>( + this->shared_from_this(), requested_); + // TODO - T28771728 + // Concat should not call 'subscribe' on onComplete + second->subscribe(upSubscriber_); + } + } + } else { + if (auto downSubscriber = std::exchange(downSubscriber_, nullptr)) { + downSubscriber->onComplete(); + } + upSubscriber_.reset(); + } + } + + void onError(folly::exception_wrapper ew) { + downSubscriber_->onError(std::move(ew)); + first_.reset(); + second_.reset(); + downSubscriber_.reset(); + upSubscriber_.reset(); + } + + private: + std::shared_ptr<Subscriber<T>> downSubscriber_; + std::shared_ptr<Flowable<T>> first_; + std::shared_ptr<Flowable<T>> second_; + std::shared_ptr<ForwardSubscriber> upSubscriber_; + std::atomic<int64_t> requested_{0}; + }; + + class ForwardSubscriber : public yarpl::flowable::Subscriber<T>, + public yarpl::flowable::Subscription { + public: + ForwardSubscriber( + std::shared_ptr<ConcatWithSubscription> concatWithSubscription, + uint32_t initialRequest = 0u) + : concatWithSubscription_(std::move(concatWithSubscription)), + initialRequest_(initialRequest) {} + + void request(int64_t n) override { + subscription_->request(n); + } + + void cancel() override { + if (auto subs = std::move(subscription_)) { + subs->cancel(); + } else { + canceled_ = true; + } + } + + void onSubscribe(std::shared_ptr<Subscription> subscription) override { + if (canceled_) { + subscription->cancel(); + return; + } + subscription_ = std::move(subscription); + if (auto req = std::exchange(initialRequest_, 0)) { + subscription_->request(req); + } + } + + void onComplete() override { + auto sub = std::exchange(concatWithSubscription_, nullptr); + sub->onComplete(); + } + + void onError(folly::exception_wrapper ew) override { + auto sub = std::exchange(concatWithSubscription_, nullptr); + sub->onError(std::move(ew)); + } + void onNext(T value) override { + concatWithSubscription_->onNext(std::move(value)); + } + + private: + std::shared_ptr<ConcatWithSubscription> concatWithSubscription_; + std::shared_ptr<flowable::Subscription> subscription_; + + uint32_t initialRequest_{0}; + bool canceled_{false}; + }; + + private: + const std::shared_ptr<Flowable<T>> first_; + const std::shared_ptr<Flowable<T>> second_; +}; + +} // namespace details +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h new file mode 100644 index 000000000..256a345ba --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h @@ -0,0 +1,190 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/flowable/FlowableOperator.h" + +namespace yarpl { +namespace flowable { +namespace details { + +template < + typename U, + typename OnSubscribeFunc, + typename OnNextFunc, + typename OnErrorFunc, + typename OnCompleteFunc, + typename OnRequestFunc, + typename OnCancelFunc> +class DoOperator : public FlowableOperator<U, U> { + using Super = FlowableOperator<U, U>; + static_assert( + std::is_same<std::decay_t<OnSubscribeFunc>, OnSubscribeFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnNextFunc>, OnNextFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnErrorFunc>, OnErrorFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnCompleteFunc>, OnCompleteFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnRequestFunc>, OnRequestFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnCancelFunc>, OnCancelFunc>::value, + "undecayed"); + + public: + template < + typename FSubscribe, + typename FNext, + typename FError, + typename FComplete, + typename FRequest, + typename FCancel> + DoOperator( + std::shared_ptr<Flowable<U>> upstream, + FSubscribe&& onSubscribeFunc, + FNext&& onNextFunc, + FError&& onErrorFunc, + FComplete&& onCompleteFunc, + FRequest&& onRequestFunc, + FCancel&& onCancelFunc) + : upstream_(std::move(upstream)), + onSubscribeFunc_(std::forward<FSubscribe>(onSubscribeFunc)), + onNextFunc_(std::forward<FNext>(onNextFunc)), + onErrorFunc_(std::forward<FError>(onErrorFunc)), + onCompleteFunc_(std::forward<FComplete>(onCompleteFunc)), + onRequestFunc_(std::forward<FRequest>(onRequestFunc)), + onCancelFunc_(std::forward<FCancel>(onCancelFunc)) {} + + void subscribe(std::shared_ptr<Subscriber<U>> subscriber) override { + auto subscription = std::make_shared<DoSubscription>( + this->ref_from_this(this), std::move(subscriber)); + upstream_->subscribe( + // Note: implicit cast to a reference to a subscriber. + subscription); + } + + private: + class DoSubscription : public Super::Subscription { + using SuperSub = typename Super::Subscription; + + public: + DoSubscription( + std::shared_ptr<DoOperator> flowable, + std::shared_ptr<Subscriber<U>> subscriber) + : SuperSub(std::move(subscriber)), flowable_(std::move(flowable)) {} + + void onSubscribeImpl() override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + flowable->onSubscribeFunc_(); + SuperSub::onSubscribeImpl(); + } + } + + void onNextImpl(U value) override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + const auto& valueRef = value; + flowable->onNextFunc_(valueRef); + SuperSub::subscriberOnNext(std::move(value)); + } + } + + void onErrorImpl(folly::exception_wrapper ex) override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + const auto& exRef = ex; + flowable->onErrorFunc_(exRef); + SuperSub::onErrorImpl(std::move(ex)); + } + } + + void onCompleteImpl() override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + flowable->onCompleteFunc_(); + SuperSub::onCompleteImpl(); + } + } + + void cancel() override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + flowable->onCancelFunc_(); + SuperSub::cancel(); + } + } + + void request(int64_t n) override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + flowable->onRequestFunc_(n); + SuperSub::request(n); + } + } + + void onTerminateImpl() override { + yarpl::atomic_exchange(&flowable_, nullptr); + SuperSub::onTerminateImpl(); + } + + private: + AtomicReference<DoOperator> flowable_; + }; + + std::shared_ptr<Flowable<U>> upstream_; + OnSubscribeFunc onSubscribeFunc_; + OnNextFunc onNextFunc_; + OnErrorFunc onErrorFunc_; + OnCompleteFunc onCompleteFunc_; + OnRequestFunc onRequestFunc_; + OnCancelFunc onCancelFunc_; +}; + +template < + typename U, + typename OnSubscribeFunc, + typename OnNextFunc, + typename OnErrorFunc, + typename OnCompleteFunc, + typename OnRequestFunc, + typename OnCancelFunc> +inline auto createDoOperator( + std::shared_ptr<Flowable<U>> upstream, + OnSubscribeFunc&& onSubscribeFunc, + OnNextFunc&& onNextFunc, + OnErrorFunc&& onErrorFunc, + OnCompleteFunc&& onCompleteFunc, + OnRequestFunc&& onRequestFunc, + OnCancelFunc&& onCancelFunc) { + return std::make_shared<DoOperator< + U, + std::decay_t<OnSubscribeFunc>, + std::decay_t<OnNextFunc>, + std::decay_t<OnErrorFunc>, + std::decay_t<OnCompleteFunc>, + std::decay_t<OnRequestFunc>, + std::decay_t<OnCancelFunc>>>( + std::move(upstream), + std::forward<OnSubscribeFunc>(onSubscribeFunc), + std::forward<OnNextFunc>(onNextFunc), + std::forward<OnErrorFunc>(onErrorFunc), + std::forward<OnCompleteFunc>(onCompleteFunc), + std::forward<OnRequestFunc>(onRequestFunc), + std::forward<OnCancelFunc>(onCancelFunc)); +} +} // namespace details +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h new file mode 100644 index 000000000..359540980 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h @@ -0,0 +1,124 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/flowable/Flowable.h" + +namespace yarpl { +namespace flowable { +namespace detail { + +template <typename T> +class ObserveOnOperatorSubscriber; + +template <typename T> +class ObserveOnOperatorSubscription : public yarpl::flowable::Subscription, + public yarpl::enable_get_ref { + public: + ObserveOnOperatorSubscription( + std::shared_ptr<ObserveOnOperatorSubscriber<T>> subscriber, + std::shared_ptr<Subscription> subscription) + : subscriber_(std::move(subscriber)), + subscription_(std::move(subscription)) {} + + // all requesting methods are called from 'executor_' in the + // associated subscriber + void cancel() override { + auto self = this->ref_from_this(this); + + if (auto subscriber = std::move(subscriber_)) { + subscriber->inner_ = nullptr; + } + + subscription_->cancel(); + } + + void request(int64_t n) override { + subscription_->request(n); + } + + private: + std::shared_ptr<ObserveOnOperatorSubscriber<T>> subscriber_; + std::shared_ptr<Subscription> subscription_; +}; + +template <typename T> +class ObserveOnOperatorSubscriber : public yarpl::flowable::Subscriber<T>, + public yarpl::enable_get_ref { + public: + ObserveOnOperatorSubscriber( + std::shared_ptr<Subscriber<T>> inner, + folly::Executor::KeepAlive<> executor) + : inner_(std::move(inner)), executor_(std::move(executor)) {} + + // all signaling methods are called from upstream EB + void onSubscribe(std::shared_ptr<Subscription> subscription) override { + executor_->add([self = this->ref_from_this(this), + s = std::move(subscription)]() mutable { + auto sub = std::make_shared<ObserveOnOperatorSubscription<T>>( + self, std::move(s)); + self->inner_->onSubscribe(std::move(sub)); + }); + } + void onNext(T next) override { + executor_->add( + [self = this->ref_from_this(this), n = std::move(next)]() mutable { + if (auto& inner = self->inner_) { + inner->onNext(std::move(n)); + } + }); + } + void onComplete() override { + executor_->add([self = this->ref_from_this(this)]() mutable { + if (auto inner = std::exchange(self->inner_, nullptr)) { + inner->onComplete(); + } + }); + } + void onError(folly::exception_wrapper err) override { + executor_->add( + [self = this->ref_from_this(this), e = std::move(err)]() mutable { + if (auto inner = std::exchange(self->inner_, nullptr)) { + inner->onError(std::move(e)); + } + }); + } + + private: + friend class ObserveOnOperatorSubscription<T>; + + std::shared_ptr<Subscriber<T>> inner_; + folly::Executor::KeepAlive<> executor_; +}; + +template <typename T> +class ObserveOnOperator : public yarpl::flowable::Flowable<T> { + public: + ObserveOnOperator( + std::shared_ptr<Flowable<T>> upstream, + folly::Executor::KeepAlive<> executor) + : upstream_(std::move(upstream)), executor_(std::move(executor)) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + upstream_->subscribe(std::make_shared<ObserveOnOperatorSubscriber<T>>( + std::move(subscriber), folly::getKeepAliveToken(executor_.get()))); + } + + std::shared_ptr<Flowable<T>> upstream_; + folly::Executor::KeepAlive<> executor_; +}; +} // namespace detail +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableOperator.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableOperator.h new file mode 100644 index 000000000..314ba7f2e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableOperator.h @@ -0,0 +1,1010 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cassert> +#include <mutex> +#include <utility> + +#include "yarpl/flowable/Flowable.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/flowable/Subscription.h" +#include "yarpl/utils/credits.h" + +#include <boost/intrusive/list.hpp> +#include <folly/Executor.h> +#include <folly/Synchronized.h> +#include <folly/functional/Invoke.h> +#include <folly/io/async/EventBase.h> + +namespace yarpl { +namespace flowable { + +/** + * Base (helper) class for operators. Operators are templated on two types: D + * (downstream) and U (upstream). Operators are created by method calls on an + * upstream Flowable, and are Flowables themselves. Multi-stage pipelines can + * be built: a Flowable heading a sequence of Operators. + */ +template <typename U, typename D> +class FlowableOperator : public Flowable<D> { + protected: + /// An Operator's subscription. + /// + /// When a pipeline chain is active, each Flowable has a corresponding + /// subscription. Except for the first one, the subscriptions are created + /// against Operators. Each operator subscription has two functions: as a + /// subscriber for the previous stage; as a subscription for the next one, the + /// user-supplied subscriber being the last of the pipeline stages. + class Subscription : public yarpl::flowable::Subscription, + public BaseSubscriber<U> { + protected: + explicit Subscription(std::shared_ptr<Subscriber<D>> subscriber) + : subscriber_(std::move(subscriber)) { + CHECK(yarpl::atomic_load(&subscriber_)); + } + + // Subscriber will be provided by the init(Subscriber) call + Subscription() {} + + virtual void init(std::shared_ptr<Subscriber<D>> subscriber) { + if (yarpl::atomic_load(&subscriber_)) { + subscriber->onSubscribe(yarpl::flowable::Subscription::create()); + subscriber->onError(std::runtime_error("already initialized")); + return; + } + subscriber_ = std::move(subscriber); + } + + void subscriberOnNext(D value) { + if (auto subscriber = yarpl::atomic_load(&subscriber_)) { + subscriber->onNext(std::move(value)); + } + } + + /// Terminates both ends of an operator normally. + void terminate() { + std::shared_ptr<Subscriber<D>> null; + auto subscriber = yarpl::atomic_exchange(&subscriber_, null); + BaseSubscriber<U>::cancel(); + if (subscriber) { + subscriber->onComplete(); + } + } + + /// Terminates both ends of an operator with an error. + void terminateErr(folly::exception_wrapper ew) { + std::shared_ptr<Subscriber<D>> null; + auto subscriber = yarpl::atomic_exchange(&subscriber_, null); + BaseSubscriber<U>::cancel(); + if (subscriber) { + subscriber->onError(std::move(ew)); + } + } + + // Subscription. + + void request(int64_t n) override { + BaseSubscriber<U>::request(n); + } + + void cancel() override { + std::shared_ptr<Subscriber<D>> null; + auto subscriber = yarpl::atomic_exchange(&subscriber_, null); + BaseSubscriber<U>::cancel(); + } + + // Subscriber. + + void onSubscribeImpl() override { + yarpl::atomic_load(&subscriber_)->onSubscribe(this->ref_from_this(this)); + } + + void onCompleteImpl() override { + std::shared_ptr<Subscriber<D>> null; + if (auto subscriber = yarpl::atomic_exchange(&subscriber_, null)) { + subscriber->onComplete(); + } + } + + void onErrorImpl(folly::exception_wrapper ew) override { + std::shared_ptr<Subscriber<D>> null; + if (auto subscriber = yarpl::atomic_exchange(&subscriber_, null)) { + subscriber->onError(std::move(ew)); + } + } + + private: + /// This subscription controls the life-cycle of the subscriber. The + /// subscriber is retained as long as calls on it can be made. (Note: the + /// subscriber in turn maintains a reference on this subscription object + /// until cancellation and/or completion.) + AtomicReference<Subscriber<D>> subscriber_; + }; +}; + +template <typename U, typename D, typename F, typename EF> +class MapOperator : public FlowableOperator<U, D> { + using Super = FlowableOperator<U, D>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(folly::is_invocable_r<D, F, U>::value, "not invocable"); + static_assert( + folly::is_invocable_r< + folly::exception_wrapper, + EF, + folly::exception_wrapper&&>::value, + "exception handler not invocable"); + + public: + template <typename Func, typename ErrorFunc> + MapOperator( + std::shared_ptr<Flowable<U>> upstream, + Func&& function, + ErrorFunc&& errFunction) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)), + errFunction_(std::move(errFunction)) {} + + void subscribe(std::shared_ptr<Subscriber<D>> subscriber) override { + upstream_->subscribe(std::make_shared<Subscription>( + this->ref_from_this(this), std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription( + std::shared_ptr<MapOperator> flowable, + std::shared_ptr<Subscriber<D>> subscriber) + : SuperSubscription(std::move(subscriber)), + flowable_(std::move(flowable)) {} + + void onNextImpl(U value) override { + try { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + this->subscriberOnNext(flowable->function_(std::move(value))); + } + } catch (const std::exception& exn) { + folly::exception_wrapper ew{std::current_exception(), exn}; + this->terminateErr(std::move(ew)); + } + } + + void onErrorImpl(folly::exception_wrapper ew) override { + try { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + SuperSubscription::onErrorImpl(flowable->errFunction_(std::move(ew))); + } + } catch (const std::exception& exn) { + this->terminateErr( + folly::exception_wrapper{std::current_exception(), exn}); + } + } + + void onTerminateImpl() override { + yarpl::atomic_exchange(&flowable_, nullptr); + SuperSubscription::onTerminateImpl(); + } + + private: + AtomicReference<MapOperator> flowable_; + }; + + std::shared_ptr<Flowable<U>> upstream_; + F function_; + EF errFunction_; +}; + +template <typename U, typename F> +class FilterOperator : public FlowableOperator<U, U> { + // for use in subclasses + using Super = FlowableOperator<U, U>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(folly::is_invocable_r<bool, F, U>::value, "not invocable"); + + public: + template <typename Func> + FilterOperator(std::shared_ptr<Flowable<U>> upstream, Func&& function) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)) {} + + void subscribe(std::shared_ptr<Subscriber<U>> subscriber) override { + upstream_->subscribe(std::make_shared<Subscription>( + this->ref_from_this(this), std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription( + std::shared_ptr<FilterOperator> flowable, + std::shared_ptr<Subscriber<U>> subscriber) + : SuperSubscription(std::move(subscriber)), + flowable_(std::move(flowable)) {} + + void onNextImpl(U value) override { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + if (flowable->function_(value)) { + SuperSubscription::subscriberOnNext(std::move(value)); + } else { + SuperSubscription::request(1); + } + } + } + + void onTerminateImpl() override { + yarpl::atomic_exchange(&flowable_, nullptr); + SuperSubscription::onTerminateImpl(); + } + + private: + AtomicReference<FilterOperator> flowable_; + }; + + std::shared_ptr<Flowable<U>> upstream_; + F function_; +}; + +template <typename U, typename D, typename F> +class ReduceOperator : public FlowableOperator<U, D> { + using Super = FlowableOperator<U, D>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(std::is_assignable<D&, U>::value, "not assignable"); + static_assert(folly::is_invocable_r<D, F, D, U>::value, "not invocable"); + + public: + template <typename Func> + ReduceOperator(std::shared_ptr<Flowable<U>> upstream, Func&& function) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)) {} + + void subscribe(std::shared_ptr<Subscriber<D>> subscriber) override { + upstream_->subscribe(std::make_shared<Subscription>( + this->ref_from_this(this), std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription( + std::shared_ptr<ReduceOperator> flowable, + std::shared_ptr<Subscriber<D>> subscriber) + : SuperSubscription(std::move(subscriber)), + flowable_(std::move(flowable)), + accInitialized_(false) {} + + void request(int64_t) override { + // Request all of the items. + SuperSubscription::request(credits::kNoFlowControl); + } + + void onNextImpl(U value) override { + if (accInitialized_) { + if (auto flowable = yarpl::atomic_load(&flowable_)) { + acc_ = flowable->function_(std::move(acc_), std::move(value)); + } + } else { + acc_ = std::move(value); + accInitialized_ = true; + } + } + + void onCompleteImpl() override { + if (accInitialized_) { + SuperSubscription::subscriberOnNext(std::move(acc_)); + } + SuperSubscription::onCompleteImpl(); + } + + void onTerminateImpl() override { + yarpl::atomic_exchange(&flowable_, nullptr); + SuperSubscription::onTerminateImpl(); + } + + private: + AtomicReference<ReduceOperator> flowable_; + bool accInitialized_; + D acc_; + }; + + std::shared_ptr<Flowable<U>> upstream_; + F function_; +}; + +template <typename T> +class TakeOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + + public: + TakeOperator(std::shared_ptr<Flowable<T>> upstream, int64_t limit) + : upstream_(std::move(upstream)), limit_(limit) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + upstream_->subscribe( + std::make_shared<Subscription>(limit_, std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription(int64_t limit, std::shared_ptr<Subscriber<T>> subscriber) + : SuperSubscription(std::move(subscriber)), limit_(limit) {} + + void onSubscribeImpl() override { + SuperSubscription::onSubscribeImpl(); + + if (limit_ <= 0) { + SuperSubscription::terminate(); + } + } + + void onNextImpl(T value) override { + if (limit_-- > 0) { + if (pending_ > 0) { + --pending_; + } + SuperSubscription::subscriberOnNext(std::move(value)); + if (limit_ == 0) { + SuperSubscription::terminate(); + } + } + } + + void request(int64_t delta) override { + delta = std::min(delta, limit_ - pending_); + if (delta > 0) { + pending_ += delta; + SuperSubscription::request(delta); + } + } + + private: + int64_t pending_{0}; + int64_t limit_; + }; + + std::shared_ptr<Flowable<T>> upstream_; + const int64_t limit_; +}; + +template <typename T> +class SkipOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + + public: + SkipOperator(std::shared_ptr<Flowable<T>> upstream, int64_t offset) + : upstream_(std::move(upstream)), offset_(offset) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + upstream_->subscribe( + std::make_shared<Subscription>(offset_, std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription(int64_t offset, std::shared_ptr<Subscriber<T>> subscriber) + : SuperSubscription(std::move(subscriber)), offset_(offset) {} + + void onNextImpl(T value) override { + if (offset_ > 0) { + --offset_; + } else { + SuperSubscription::subscriberOnNext(std::move(value)); + } + } + + void request(int64_t delta) override { + if (firstRequest_) { + firstRequest_ = false; + delta = credits::add(delta, offset_); + } + SuperSubscription::request(delta); + } + + private: + int64_t offset_; + bool firstRequest_{true}; + }; + + std::shared_ptr<Flowable<T>> upstream_; + const int64_t offset_; +}; + +template <typename T> +class IgnoreElementsOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + + public: + explicit IgnoreElementsOperator(std::shared_ptr<Flowable<T>> upstream) + : upstream_(std::move(upstream)) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + upstream_->subscribe(std::make_shared<Subscription>(std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription(std::shared_ptr<Subscriber<T>> subscriber) + : SuperSubscription(std::move(subscriber)) {} + + void onNextImpl(T) override {} + }; + + std::shared_ptr<Flowable<T>> upstream_; +}; + +template <typename T> +class SubscribeOnOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + + public: + SubscribeOnOperator( + std::shared_ptr<Flowable<T>> upstream, + folly::Executor& executor) + : upstream_(std::move(upstream)), executor_(executor) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + executor_.add([this, self = this->ref_from_this(this), subscriber] { + upstream_->subscribe( + std::make_shared<Subscription>(executor_, std::move(subscriber))); + }); + } + + private: + using SuperSubscription = typename Super::Subscription; + class Subscription : public SuperSubscription { + public: + Subscription( + folly::Executor& executor, + std::shared_ptr<Subscriber<T>> subscriber) + : SuperSubscription(std::move(subscriber)), executor_(executor) {} + + void request(int64_t delta) override { + executor_.add([delta, this, self = this->ref_from_this(this)] { + this->callSuperRequest(delta); + }); + } + + void cancel() override { + executor_.add([this, self = this->ref_from_this(this)] { + this->callSuperCancel(); + }); + } + + void onNextImpl(T value) override { + SuperSubscription::subscriberOnNext(std::move(value)); + } + + private: + // Trampoline to call superclass method; gcc bug 58972. + void callSuperRequest(int64_t delta) { + SuperSubscription::request(delta); + } + + // Trampoline to call superclass method; gcc bug 58972. + void callSuperCancel() { + SuperSubscription::cancel(); + } + + folly::Executor& executor_; + }; + + std::shared_ptr<Flowable<T>> upstream_; + folly::Executor& executor_; +}; + +template <typename T, typename OnSubscribe> +class FromPublisherOperator : public Flowable<T> { + static_assert( + std::is_same<std::decay_t<OnSubscribe>, OnSubscribe>::value, + "undecayed"); + + public: + template <typename F> + explicit FromPublisherOperator(F&& function) + : function_(std::forward<F>(function)) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + function_(std::move(subscriber)); + } + + private: + OnSubscribe function_; +}; + +template <typename T, typename R> +class FlatMapOperator : public FlowableOperator<T, R> { + using Super = FlowableOperator<T, R>; + + public: + FlatMapOperator( + std::shared_ptr<Flowable<T>> upstream, + folly::Function<std::shared_ptr<Flowable<R>>(T)> func) + : upstream_(std::move(upstream)), function_(std::move(func)) {} + + void subscribe(std::shared_ptr<Subscriber<R>> subscriber) override { + upstream_->subscribe(std::make_shared<FMSubscription>( + this->ref_from_this(this), std::move(subscriber))); + } + + private: + using SuperSubscription = typename Super::Subscription; + class FMSubscription : public SuperSubscription { + struct MappedStreamSubscriber; + + public: + FMSubscription( + std::shared_ptr<FlatMapOperator> flowable, + std::shared_ptr<Subscriber<R>> subscriber) + : SuperSubscription(std::move(subscriber)), + flowable_(std::move(flowable)) {} + + void onSubscribeImpl() final { + liveSubscribers_++; + SuperSubscription::onSubscribeImpl(); + } + + void onNextImpl(T value) final { + std::shared_ptr<Flowable<R>> mappedStream; + + try { + mappedStream = flowable_->function_(std::move(value)); + } catch (const std::exception& exn) { + folly::exception_wrapper ew{std::current_exception(), exn}; + { + std::lock_guard<std::mutex> g(onErrorExGuard_); + onErrorEx_ = ew; + } + // next iteration of drainLoop will cancel this subscriber as well + drainLoop(); + return; + } + + std::shared_ptr<MappedStreamSubscriber> mappedSubscriber = + std::make_shared<MappedStreamSubscriber>(this->ref_from_this(this)); + mappedSubscriber->fmReference_ = mappedSubscriber; + + { + // put into pendingValue queue because once the mappedSubscriber + // is subscribed to, it will request elements. We don't want the + // drainLoop to execute while it's on withoutValue, and request + // a second element before the first arrives. + auto l = lists.wlock(); + CHECK(!mappedSubscriber->is_linked()); + l->pendingValue.push_back(*mappedSubscriber.get()); + } + + liveSubscribers_++; + mappedStream->subscribe(mappedSubscriber); + drainLoop(); + } + + void drainImpl() { + // phase 1: clear out terminated subscribers + { + auto clearList = [](auto& list, SubscriberList& t) { + while (!list.empty()) { + auto& elem = list.front(); + auto r = elem.sync.wlock(); + r->freeze = true; + elem.unlink(); + t.push_back(elem); + } + }; + + SubscriberList clearTrash; + if (clearAllSubscribers_.load()) { + auto l = lists.wlock(); + clearList(l->withValue, clearTrash); + clearList(l->withoutValue, clearTrash); + clearList(l->pendingValue, clearTrash); + } + + // clear elements while no locks are held + while (!clearTrash.empty()) { + auto& elem = clearTrash.front(); + elem.unlink(); + elem.cancel(); + elem.fmReference_ = nullptr; + } + } + + // phase 2: check if the subscriber should terminate due to error + // or all subscribers completing + if (!calledDownstreamTerminate_) { + folly::exception_wrapper ex; + { + std::lock_guard<std::mutex> exg(onErrorExGuard_); + ex = std::move(onErrorEx_); + } + if (ex) { + calledDownstreamTerminate_ = true; + cancel(); + this->terminateErr(std::move(ex)); + } else if (liveSubscribers_ == 0) { + calledDownstreamTerminate_ = true; + this->terminate(); + } + } + + // phase 3: if the downstream has requested elements, pop values out of + // subscribers which have received a value and call downstream->onNext + while (requested_ != 0) { + R val; + + { + auto l = lists.wlock(); + if (l->withValue.empty()) { + break; + } + + requested_--; + auto& elem = l->withValue.front(); + elem.unlink(); + + { + auto r = elem.sync.wlock(); + CHECK(r->hasValue); + r->hasValue = false; + val = std::move(r->value); + l->withoutValue.push_back(elem); + } + } + + SuperSubscription::subscriberOnNext(std::move(val)); + } + + // phase 4: ask any upstream flowables which don't have pending + // requests for their next element kick off any more requests. + // Put subscribers which have terminated into the trash. + { + SubscriberList terminatedTrash; + + while (true) { + MappedStreamSubscriber* elem; + { + auto l = lists.wlock(); + if (l->withoutValue.empty()) { + break; + } + elem = &l->withoutValue.front(); + + auto r = elem->sync.wlock(); + CHECK(!r->hasValue) << "failed for elem=" << elem; // sanity + + elem->unlink(); + + // Subscribers might call onNext and then terminate; delay + // removing its liveSubscriber reference until we've delivered + // its element to the downstream subscriber and dropped its + // synchronized reference to `r`, as dropping the + // flatMapSubscription_ reference may invoke its destructor + if (r->isTerminated) { + r->freeze = true; + terminatedTrash.push_back(*elem); + continue; // skips the next elem->request(1) + } + + // else, the stream hasn't terminated, request another + // element + l->pendingValue.push_back(*elem); + } + elem->request(1); + } + + // phase 5: destroy any mapped subscribers which have terminated, + // enqueue another drain loop run if we do end up discarding any + // subscribers, as our live subscriber count may have gone to zero + if (!terminatedTrash.empty()) { + drainLoopMutex_++; + } + while (!terminatedTrash.empty()) { + auto& elem = terminatedTrash.front(); + CHECK(elem.sync.wlock()->isTerminated); + elem.unlink(); + elem.fmReference_ = nullptr; + liveSubscribers_--; + } + } + } + + // called from MappedStreamSubscriber, receives the R and the + // subscriber which generated the R + void drainLoop() { + auto self = this->ref_from_this(this); + if (drainLoopMutex_++ == 0) { + do { + drainImpl(); + } while (drainLoopMutex_-- != 1); + } + } + + void onMappedSubscriberNext(MappedStreamSubscriber* elem, R value) { + { + // `elem` may not be in a list, as it may have been canceled. Push it + // on the withValue list and let drainLoop clear it if that's the case. + auto l = lists.wlock(); + auto r = elem->sync.wlock(); + + if (r->freeze) { + return; + } + + CHECK(!r->hasValue) << "failed for elem=" << elem; + r->hasValue = true; + r->value = std::move(value); + + elem->unlink(); + l->withValue.push_back(*elem); + } + + drainLoop(); + } + void onMappedSubscriberTerminate(MappedStreamSubscriber* elem) { + { + auto r = elem->sync.wlock(); + + r->isTerminated = true; + if (r->onErrorEx) { + std::lock_guard<std::mutex> exg(onErrorExGuard_); + onErrorEx_ = std::move(r->onErrorEx); + } + + if (r->freeze) { + return; + } + } + + { + auto l = lists.wlock(); + auto r = elem->sync.wlock(); + + if (r->freeze) { + return; + } + + CHECK(elem->is_linked()); + elem->unlink(); + + if (r->hasValue) { + l->withValue.push_back(*elem); + } else { + liveSubscribers_--; + elem->fmReference_ = nullptr; + } + } + + drainLoop(); + } + + // onComplete/onError fall through to onTerminateImpl, which + // will call drainLoop and update the liveSubscribers_ count + void onCompleteImpl() final {} + void onErrorImpl(folly::exception_wrapper ex) final { + std::lock_guard<std::mutex> g(onErrorExGuard_); + onErrorEx_ = std::move(ex); + clearAllSubscribers_.store(true); + } + + void onTerminateImpl() final { + liveSubscribers_--; + drainLoop(); + flowable_.reset(); + } + + void request(int64_t n) override { + if ((n + requested_) < requested_) { + requested_ = std::numeric_limits<int64_t>::max(); + } else { + requested_ += n; + } + + if (n > 0) { + // TODO: make max parallelism configurable a-la RxJava 2.x's + // FlowableFlatMapOperator + SuperSubscription::request(std::numeric_limits<int64_t>::max()); + } + + drainLoop(); + } + + void cancel() override { + clearAllSubscribers_.store(true); + drainLoop(); + } + + private: + // buffers at most a single element of type R + struct MappedStreamSubscriber + : public BaseSubscriber<R>, + public boost::intrusive::list_base_hook< + boost::intrusive::link_mode<boost::intrusive::auto_unlink>> { + MappedStreamSubscriber(std::shared_ptr<FMSubscription> subscription) + : flatMapSubscription_(std::move(subscription)) {} + + void onSubscribeImpl() final { + auto fmsb = yarpl::atomic_load(&flatMapSubscription_); + if (!fmsb || fmsb->clearAllSubscribers_) { + BaseSubscriber<R>::cancel(); + return; + } +#ifndef NDEBUG + if (auto fms = yarpl::atomic_load(&flatMapSubscription_)) { + auto l = fms->lists.wlock(); + auto r = sync.wlock(); + if (!is_in_list(*this, l->pendingValue, l)) { + LOG(INFO) << "failed: this=" << this; + LOG(INFO) << "in list: "; + debug_is_in_list(*this, l); + DCHECK(r->freeze); + } else { + } + DCHECK(!r->hasValue); + } +#endif + + BaseSubscriber<R>::request(1); + } + + void onNextImpl(R value) final { + if (auto fms = yarpl::atomic_load(&flatMapSubscription_)) { + fms->onMappedSubscriberNext(this, std::move(value)); + } + } + + // noop + void onCompleteImpl() final {} + + void onErrorImpl(folly::exception_wrapper ex) final { + auto r = sync.wlock(); + r->onErrorEx = std::move(ex); + } + + void onTerminateImpl() override { + std::shared_ptr<FMSubscription> null; + if (auto fms = yarpl::atomic_exchange(&flatMapSubscription_, null)) { + fms->onMappedSubscriberTerminate(this); + } + } + + struct SyncData { + R value; + bool hasValue{false}; + bool isTerminated{false}; + bool freeze{false}; + folly::exception_wrapper onErrorEx{nullptr}; + }; + folly::Synchronized<SyncData> sync; + + // FMSubscription's 'reference' to this object. FMSubscription + // clears this reference when it drops the MappedStreamSubscriber + // from one of its atomic lists + std::shared_ptr<MappedStreamSubscriber> fmReference_{nullptr}; + + // this is both a Subscriber and a Subscription<T> + AtomicReference<FMSubscription> flatMapSubscription_{nullptr}; + }; + + // used to make sure only one thread at a time is calling subscriberOnNext + std::atomic<int64_t> drainLoopMutex_{0}; + + using SubscriberList = boost::intrusive::list< + MappedStreamSubscriber, + boost::intrusive::constant_time_size<false>>; + + struct Lists { + // subscribers with a ready R + SubscriberList withValue{}; + // subscribers that have requested 1 R, waiting for it to arrive via + // onNext + SubscriberList pendingValue{}; + // idle subscribers + SubscriberList withoutValue{}; + }; + + folly::Synchronized<Lists> lists; + + template <typename L> + static bool is_in_list( + MappedStreamSubscriber const& elem, + SubscriberList const& list, + L const& lists) { + return in_list_impl(elem, list, lists, true); + } + template <typename L> + static bool not_in_list( + MappedStreamSubscriber const& elem, + SubscriberList const& list, + L const& lists) { + return in_list_impl(elem, list, lists, false); + } + + template <typename L> + static bool in_list_impl( + MappedStreamSubscriber const& elem, + SubscriberList const& list, + L const& lists, + bool should) { + if (is_in_list(elem, list) != should) { +#ifndef NDEBUG + debug_is_in_list(elem, lists); +#else + (void)lists; +#endif + return false; + } + return true; + } + + template <typename L> + static void debug_is_in_list( + MappedStreamSubscriber const& elem, + L const& lists) { + LOG(INFO) << "in without: " << is_in_list(elem, lists->withoutValue); + LOG(INFO) << "in pending: " << is_in_list(elem, lists->pendingValue); + LOG(INFO) << "in withval: " << is_in_list(elem, lists->withValue); + } + + static bool is_in_list( + MappedStreamSubscriber const& elem, + SubscriberList const& list) { + bool found = false; + for (auto& e : list) { + if (&e == &elem) { + found = true; + break; + } + } + return found; + } + + std::shared_ptr<FlatMapOperator> flowable_; + + // got a terminating signal from the upstream flowable + // always modified in the protected drainImpl() + bool calledDownstreamTerminate_{false}; + + std::mutex onErrorExGuard_; + folly::exception_wrapper onErrorEx_{nullptr}; + + // clear all lists of + std::atomic<bool> clearAllSubscribers_{false}; + + std::atomic<int64_t> requested_{0}; + + // number of subscribers (FMSubscription + MappedStreamSubscriber) which + // have not received a terminating signal yet + std::atomic<int64_t> liveSubscribers_{0}; + }; + + std::shared_ptr<Flowable<T>> upstream_; + folly::Function<std::shared_ptr<Flowable<R>>(T)> function_; +}; + +} // namespace flowable +} // namespace yarpl + +#include "yarpl/flowable/FlowableConcatOperators.h" +#include "yarpl/flowable/FlowableDoOperator.h" +#include "yarpl/flowable/FlowableObserveOnOperator.h" +#include "yarpl/flowable/FlowableTimeoutOperator.h" diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h new file mode 100644 index 000000000..bb14ccc9c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h @@ -0,0 +1,162 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// IWYU pragma: private, include "yarpl/flowable/Flowable.h" + +#pragma once + +#include <stdexcept> + +#include "yarpl/flowable/FlowableOperator.h" + +namespace yarpl { +class TimeoutException : public std::runtime_error { + public: + TimeoutException() : std::runtime_error("yarpl::TimeoutException") {} +}; +namespace detail { +class TimeoutExceptionGenerator { + public: + TimeoutException operator()() const { + return {}; + } +}; +} // namespace detail + +namespace flowable { +namespace details { + +template <typename T, typename ExceptionGenerator> +class TimeoutOperator : public FlowableOperator<T, T> { + using Super = FlowableOperator<T, T>; + static_assert( + std::is_same<std::decay_t<ExceptionGenerator>, ExceptionGenerator>::value, + "undecayed"); + + public: + template <typename F> + TimeoutOperator( + std::shared_ptr<Flowable<T>> upstream, + folly::EventBase& timerEvb, + std::chrono::milliseconds timeout, + std::chrono::milliseconds initTimeout, + F&& exnGen) + : upstream_(std::move(upstream)), + timerEvb_(timerEvb), + timeout_(timeout), + initTimeout_(initTimeout), + exnGen_(std::forward<F>(exnGen)) {} + + void subscribe(std::shared_ptr<Subscriber<T>> subscriber) override { + auto subscription = std::make_shared<TimeoutSubscription>( + this->ref_from_this(this), + subscriber, + timerEvb_, + initTimeout_, + timeout_); + upstream_->subscribe(std::move(subscription)); + } + + protected: + class TimeoutSubscription : public Super::Subscription, + public folly::HHWheelTimer::Callback { + using SuperSub = typename Super::Subscription; + + public: + TimeoutSubscription( + std::shared_ptr<TimeoutOperator<T, ExceptionGenerator>> flowable, + std::shared_ptr<Subscriber<T>> subscriber, + folly::EventBase& timerEvb, + std::chrono::milliseconds initTimeout, + std::chrono::milliseconds timeout) + : Super::Subscription(std::move(subscriber)), + flowable_(std::move(flowable)), + timerEvb_(timerEvb), + initTimeout_(initTimeout), + timeout_(timeout) {} + + void onSubscribeImpl() override { + DCHECK(timerEvb_.isInEventBaseThread()); + if (initTimeout_.count() > 0) { + nextTime_ = std::chrono::steady_clock::now() + initTimeout_; + timerEvb_.timer().scheduleTimeout(this, initTimeout_); + } else { + nextTime_ = std::chrono::steady_clock::time_point::max(); + } + + SuperSub::onSubscribeImpl(); + } + + void onNextImpl(T value) override { + DCHECK(timerEvb_.isInEventBaseThread()); + if (flowable_) { + if (nextTime_ != std::chrono::steady_clock::time_point::max()) { + cancelTimeout(); // cancel timer before calling onNext + auto currentTime = std::chrono::steady_clock::now(); + if (currentTime > nextTime_) { + timeoutExpired(); + return; + } + nextTime_ = std::chrono::steady_clock::time_point::max(); + } + + SuperSub::subscriberOnNext(std::move(value)); + + if (timeout_.count() > 0) { + nextTime_ = std::chrono::steady_clock::now() + timeout_; + timerEvb_.timer().scheduleTimeout(this, timeout_); + } + } + } + + void onTerminateImpl() override { + DCHECK(timerEvb_.isInEventBaseThread()); + flowable_.reset(); + cancelTimeout(); + } + + void timeoutExpired() noexcept override { + if (auto flowable = std::exchange(flowable_, nullptr)) { + SuperSub::terminateErr([&]() -> folly::exception_wrapper { + try { + return flowable->exnGen_(); + } catch (...) { + return folly::make_exception_wrapper<std::nested_exception>(); + } + }()); + } + } + + void callbackCanceled() noexcept override { + // Do nothing.. + } + + private: + std::shared_ptr<TimeoutOperator<T, ExceptionGenerator>> flowable_; + folly::EventBase& timerEvb_; + std::chrono::milliseconds initTimeout_; + std::chrono::milliseconds timeout_; + std::chrono::steady_clock::time_point nextTime_; + }; + + std::shared_ptr<Flowable<T>> upstream_; + folly::EventBase& timerEvb_; + std::chrono::milliseconds timeout_; + std::chrono::milliseconds initTimeout_; + ExceptionGenerator exnGen_; +}; + +} // namespace details +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h new file mode 100644 index 000000000..e191ad7c3 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h @@ -0,0 +1,348 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Synchronized.h> +#include <deque> +#include "yarpl/Common.h" +#include "yarpl/Flowable.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { +namespace observable { +template <typename T> +class Observable; + +template <typename T> +class Observer; +} // namespace observable + +template <typename T> +class BackpressureStrategyBase : public IBackpressureStrategy<T>, + public flowable::Subscription, + public observable::Observer<T> { + protected: + // + // the following methods are to be overridden + // + virtual void onCreditsAvailable(int64_t /*credits*/) = 0; + virtual void onNextWithoutCredits(T /*t*/) = 0; + + public: + void init( + std::shared_ptr<observable::Observable<T>> observable, + std::shared_ptr<flowable::Subscriber<T>> subscriber) override { + observable_ = std::move(observable); + subscriberWeak_ = subscriber; + subscriber_ = subscriber; + subscriber->onSubscribe(this->ref_from_this(this)); + observable_->subscribe(this->ref_from_this(this)); + } + + BackpressureStrategyBase() = default; + BackpressureStrategyBase(BackpressureStrategyBase&&) = delete; + + BackpressureStrategyBase(const BackpressureStrategyBase&) = delete; + BackpressureStrategyBase& operator=(BackpressureStrategyBase&&) = delete; + BackpressureStrategyBase& operator=(const BackpressureStrategyBase&) = delete; + + // only for testing purposes + void setTestSubscriber(std::shared_ptr<flowable::Subscriber<T>> subscriber) { + subscriberWeak_ = subscriber; + subscriber_ = subscriber; + subscriber->onSubscribe(this->ref_from_this(this)); + } + + void request(int64_t n) override { + if (n <= 0) { + return; + } + auto r = credits::add(&requested_, n); + if (r <= 0) { + return; + } + + // it is possible that after calling subscribe or in onCreditsAvailable + // methods, there will be a stream of + // onNext calls which the processing chain might cancel. The cancel signal + // will remove all references to this class and we need to keep this + // instance around to finish this method + auto thisPtr = this->ref_from_this(this); + + if (r > 0) { + onCreditsAvailable(r); + } + } + + void cancel() override { + if (auto subscriber = subscriber_.exchange(nullptr)) { + observable::Observer<T>::unsubscribe(); + observable_.reset(); + } + } + + // Observer override + void onNext(T t) override { + if (subscriberWeak_.expired()) { + return; + } + if (requested_ > 0) { + downstreamOnNext(std::move(t)); + return; + } + onNextWithoutCredits(std::move(t)); + } + + // Observer override + void onComplete() override { + downstreamOnComplete(); + } + + // Observer override + void onError(folly::exception_wrapper ex) override { + downstreamOnError(std::move(ex)); + } + + virtual void downstreamOnNext(T t) { + credits::consume(&requested_, 1); + if (auto subscriber = subscriberWeak_.lock()) { + subscriber->onNext(std::move(t)); + } + } + + void downstreamOnComplete() { + if (auto subscriber = subscriber_.exchange(nullptr)) { + subscriber->onComplete(); + observable::Observer<T>::onComplete(); + observable_.reset(); + } + } + + void downstreamOnError(folly::exception_wrapper error) { + if (auto subscriber = subscriber_.exchange(nullptr)) { + subscriber->onError(std::move(error)); + observable::Observer<T>::onError(folly::exception_wrapper()); + observable_.reset(); + } + } + + void downstreamOnErrorAndCancel(folly::exception_wrapper error) { + if (auto subscriber = subscriber_.exchange(nullptr)) { + subscriber->onError(std::move(error)); + + observable_.reset(); + observable::Observer<T>::unsubscribe(); + } + } + + private: + std::shared_ptr<observable::Observable<T>> observable_; + folly::Synchronized<std::shared_ptr<flowable::Subscriber<T>>> subscriber_; + std::weak_ptr<flowable::Subscriber<T>> subscriberWeak_; + std::atomic<int64_t> requested_{0}; +}; + +template <typename T> +class DropBackpressureStrategy : public BackpressureStrategyBase<T> { + public: + void onCreditsAvailable(int64_t /*credits*/) override {} + void onNextWithoutCredits(T /*t*/) override { + // drop anything while we don't have credits + } +}; + +template <typename T> +class ErrorBackpressureStrategy : public BackpressureStrategyBase<T> { + using Super = BackpressureStrategyBase<T>; + + void onCreditsAvailable(int64_t /*credits*/) override {} + + void onNextWithoutCredits(T /*t*/) override { + Super::downstreamOnErrorAndCancel(flowable::MissingBackpressureException()); + } +}; + +template <typename T> +class BufferBackpressureStrategy : public BackpressureStrategyBase<T> { + public: + static constexpr size_t kNoLimit = 0; + + explicit BufferBackpressureStrategy(size_t bufferSizeLimit = kNoLimit) + : buffer_(folly::in_place, bufferSizeLimit) {} + + private: + using Super = BackpressureStrategyBase<T>; + + void onComplete() override { + if (!buffer_.rlock()->empty()) { + // we have buffered some items so we will defer delivering on complete for + // later + completed_ = true; + } else { + Super::onComplete(); + } + } + + void onNext(T t) override { + { + auto buffer = buffer_.wlock(); + if (!buffer->empty()) { + if (buffer->push(std::move(t))) { + return; + } + buffer.unlock(); + Super::downstreamOnErrorAndCancel( + flowable::MissingBackpressureException()); + return; + } + } + BackpressureStrategyBase<T>::onNext(std::move(t)); + } + + // + // onError signal is delivered immediately by design + // + + void onNextWithoutCredits(T t) override { + if (buffer_.wlock()->push(std::move(t))) { + return; + } + Super::downstreamOnErrorAndCancel(flowable::MissingBackpressureException()); + } + + void onCreditsAvailable(int64_t credits) override { + DCHECK(credits > 0); + auto lockedBuffer = buffer_.wlock(); + while (credits-- > 0 && !lockedBuffer->empty()) { + Super::downstreamOnNext(std::move(lockedBuffer->front())); + lockedBuffer->pop(); + } + + if (lockedBuffer->empty() && completed_) { + Super::onComplete(); + } + } + + struct Buffer { + public: + explicit Buffer(size_t sizeLimit) : sizeLimit_(sizeLimit) {} + + bool empty() const { + return buffer_.empty(); + } + + bool push(T&& value) { + if (sizeLimit_ != kNoLimit && buffer_.size() >= sizeLimit_) { + return false; + } + buffer_.push(std::move(value)); + return true; + } + + T& front() { + return buffer_.front(); + } + + void pop() { + buffer_.pop(); + } + + private: + const size_t sizeLimit_; + std::queue<T> buffer_; + }; + + folly::Synchronized<Buffer> buffer_; + std::atomic<bool> completed_{false}; +}; + +template <typename T> +class LatestBackpressureStrategy : public BackpressureStrategyBase<T> { + using Super = BackpressureStrategyBase<T>; + + void onComplete() override { + if (storesLatest_) { + // we have buffered an item so we will defer delivering on complete for + // later + completed_ = true; + } else { + Super::onComplete(); + } + } + + // + // onError signal is delivered immediately by design + // + + void onNextWithoutCredits(T t) override { + storesLatest_ = true; + *latest_.wlock() = std::move(t); + } + + void onCreditsAvailable(int64_t credits) override { + DCHECK(credits > 0); + if (storesLatest_) { + storesLatest_ = false; + Super::downstreamOnNext(std::move(*latest_.wlock())); + + if (completed_) { + Super::onComplete(); + } + } + } + + std::atomic<bool> storesLatest_{false}; + std::atomic<bool> completed_{false}; + folly::Synchronized<T> latest_; +}; + +template <typename T> +class MissingBackpressureStrategy : public BackpressureStrategyBase<T> { + using Super = BackpressureStrategyBase<T>; + + void onCreditsAvailable(int64_t /*credits*/) override {} + + void onNextWithoutCredits(T t) override { + // call onNext anyways (and potentially violating the protocol) + Super::downstreamOnNext(std::move(t)); + } +}; + +template <typename T> +std::shared_ptr<IBackpressureStrategy<T>> IBackpressureStrategy<T>::buffer() { + return std::make_shared<BufferBackpressureStrategy<T>>(); +} + +template <typename T> +std::shared_ptr<IBackpressureStrategy<T>> IBackpressureStrategy<T>::drop() { + return std::make_shared<DropBackpressureStrategy<T>>(); +} + +template <typename T> +std::shared_ptr<IBackpressureStrategy<T>> IBackpressureStrategy<T>::error() { + return std::make_shared<ErrorBackpressureStrategy<T>>(); +} + +template <typename T> +std::shared_ptr<IBackpressureStrategy<T>> IBackpressureStrategy<T>::latest() { + return std::make_shared<LatestBackpressureStrategy<T>>(); +} + +template <typename T> +std::shared_ptr<IBackpressureStrategy<T>> IBackpressureStrategy<T>::missing() { + return std::make_shared<MissingBackpressureStrategy<T>>(); +} + +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.cpp b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.cpp new file mode 100644 index 000000000..4b61540f5 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "yarpl/flowable/Flowables.h" + +namespace yarpl { +namespace flowable { + +std::shared_ptr<Flowable<int64_t>> Flowable<>::range( + int64_t start, + int64_t count) { + auto lambda = [start, count, i = start]( + Subscriber<int64_t>& subscriber, + int64_t requested) mutable { + int64_t end = start + count; + + while (i < end && requested-- > 0) { + subscriber.onNext(i++); + } + + if (i >= end) { + // TODO T27302402: Even though having two subscriptions exist concurrently + // for Emitters is not possible still. At least it possible to resubscribe + // and consume the same values again. + i = start; + subscriber.onComplete(); + } + }; + return Flowable<int64_t>::create(std::move(lambda)); +} + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.h new file mode 100644 index 000000000..56cb8c034 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Flowables.h @@ -0,0 +1,64 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Traits.h> +#include <folly/functional/Invoke.h> +#include <glog/logging.h> +#include <algorithm> +#include <limits> +#include <vector> +#include "yarpl/flowable/Flowable.h" + +namespace yarpl { +namespace flowable { + +template <> +class Flowable<void> { + public: + /** + * Emit the sequence of numbers [start, start + count). + */ + static std::shared_ptr<Flowable<int64_t>> range(int64_t start, int64_t count); + + template <typename T> + static std::shared_ptr<Flowable<T>> just(T&& value) { + return Flowable<folly::remove_cvref_t<T>>::just(std::forward<T>(value)); + } + + template <typename T> + static std::shared_ptr<Flowable<T>> justN(std::initializer_list<T> list) { + return Flowable<folly::remove_cvref_t<T>>::justN(std::move(list)); + } + + // this will generate a flowable which can be subscribed to only once + template <typename T> + static std::shared_ptr<Flowable<T>> justOnce(T&& value) { + return Flowable<folly::remove_cvref_t<T>>::justOnce(std::forward<T>(value)); + } + + template <typename T, typename... Args> + static std::shared_ptr<Flowable<T>> concat( + std::shared_ptr<Flowable<T>> first, + Args... args) { + return first->concatWith(args...); + } + + private: + Flowable() = delete; +}; + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/PublishProcessor.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/PublishProcessor.h new file mode 100644 index 000000000..8232c4a82 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/PublishProcessor.h @@ -0,0 +1,255 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Synchronized.h> +#include <vector> +#include "yarpl/Common.h" +#include "yarpl/flowable/Flowable.h" +#include "yarpl/observable/Observable.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { +namespace flowable { + +// Processor that multicasts all subsequently observed items to its current +// Subscribers. The processor does not coordinate backpressure for its +// subscribers and implements a weaker onSubscribe which calls requests +// kNoFlowControl from the incoming Subscriptions. This makes it possible to +// subscribe the PublishProcessor to multiple sources unlike the standard +// Subscriber contract. If subscribers are not able to keep up with the flow +// control, they are terminated with MissingBackpressureException. The +// implementation of onXXX() and subscribe() methods are technically thread-safe +// but non-serialized calls to them may lead to undefined state in the currently +// subscribed Subscribers. +template <typename T> +class PublishProcessor : public observable::Observable<T>, + public Subscriber<T> { + class PublisherSubscription; + using PublishersVector = std::vector<std::shared_ptr<PublisherSubscription>>; + + public: + static std::shared_ptr<PublishProcessor> create() { + return std::shared_ptr<PublishProcessor>(new PublishProcessor()); + } + + ~PublishProcessor() { + auto publishers = std::make_shared<const PublishersVector>(); + publishers_.swap(publishers); + + for (const auto& publisher : *publishers) { + publisher->terminate(); + } + } + + bool hasSubscribers() const { + return !publishers_.copy()->empty(); + } + + std::shared_ptr<observable::Subscription> subscribe( + std::shared_ptr<observable::Observer<T>> subscriber) override { + auto publisher = std::make_shared<PublisherSubscription>(subscriber, this); + // we have to call onSubscribe before adding it to the list of publishers + // because they might start emitting right away + subscriber->onSubscribe(publisher); + + if (publisher->isCancelled()) { + return publisher; + } + + auto publishers = tryAddPublisher(publisher); + + if (publishers == kCompletedPublishers()) { + publisher->onComplete(); + } else if (publishers == kErroredPublishers()) { + publisher->onError(std::runtime_error("ErroredPublisher")); + } + + return publisher; + } + + void onSubscribe(std::shared_ptr<Subscription> subscription) override { + auto publishers = publishers_.copy(); + if (publishers == kCompletedPublishers() || + publishers == kErroredPublishers()) { + subscription->cancel(); + return; + } + + subscription->request(credits::kNoFlowControl); + } + + void onNext(T value) override { + auto publishers = publishers_.copy(); + DCHECK(publishers != kCompletedPublishers()); + DCHECK(publishers != kErroredPublishers()); + + for (const auto& publisher : *publishers) { + publisher->onNext(value); + } + } + + void onError(folly::exception_wrapper ex) override { + auto publishers = kErroredPublishers(); + publishers_.swap(publishers); + DCHECK(publishers != kCompletedPublishers()); + DCHECK(publishers != kErroredPublishers()); + + for (const auto& publisher : *publishers) { + publisher->onError(ex); + } + } + + void onComplete() override { + auto publishers = kCompletedPublishers(); + publishers_.swap(publishers); + DCHECK(publishers != kCompletedPublishers()); + DCHECK(publishers != kErroredPublishers()); + + for (const auto& publisher : *publishers) { + publisher->onComplete(); + } + } + + private: + PublishProcessor() : publishers_{std::make_shared<PublishersVector>()} {} + + std::shared_ptr<const PublishersVector> tryAddPublisher( + std::shared_ptr<PublisherSubscription> subscriber) { + while (true) { + auto oldPublishers = publishers_.copy(); + if (oldPublishers == kCompletedPublishers() || + oldPublishers == kErroredPublishers()) { + return oldPublishers; + } + + auto newPublishers = std::make_shared<PublishersVector>(); + newPublishers->reserve(oldPublishers->size() + 1); + newPublishers->insert( + newPublishers->begin(), + oldPublishers->cbegin(), + oldPublishers->cend()); + newPublishers->push_back(subscriber); + + auto locked = publishers_.lock(); + if (*locked == oldPublishers) { + *locked = newPublishers; + return newPublishers; + } + // else the vector changed so we will have to do it again + } + } + + void removePublisher(PublisherSubscription* subscriber) { + while (true) { + auto oldPublishers = publishers_.copy(); + + auto removingItem = std::find_if( + oldPublishers->cbegin(), + oldPublishers->cend(), + [&](const auto& publisherPtr) { + return publisherPtr.get() == subscriber; + }); + + if (removingItem == oldPublishers->cend()) { + // not found anymore + return; + } + + auto newPublishers = std::make_shared<PublishersVector>(); + newPublishers->reserve(oldPublishers->size() - 1); + newPublishers->insert( + newPublishers->begin(), oldPublishers->cbegin(), removingItem); + newPublishers->insert( + newPublishers->end(), std::next(removingItem), oldPublishers->cend()); + + auto locked = publishers_.lock(); + if (*locked == oldPublishers) { + *locked = std::move(newPublishers); + return; + } + // else the vector changed so we will have to do it again + } + } + + class PublisherSubscription : public observable::Subscription { + public: + PublisherSubscription( + std::shared_ptr<observable::Observer<T>> subscriber, + PublishProcessor* processor) + : subscriber_(std::move(subscriber)), processor_(processor) {} + + // cancel may race with terminate(), but the + // PublishProcessor::removePublisher will take care of that the race with + // on{Next, Error, Complete} methods is allowed by the spec + void cancel() override { + subscriber_.reset(); + processor_->removePublisher(this); + } + + // terminate will never race with on{Next, Error, Complete} because they are + // all called from PublishProcessor and terminate is called only from dtor + void terminate() { + if (auto subscriber = std::exchange(subscriber_, nullptr)) { + subscriber->onError(std::runtime_error("PublishProcessor shutdown")); + } + } + + void onNext(T value) { + if (subscriber_) { + subscriber_->onNext(std::move(value)); + } + } + + // used internally, not an interface method + void onError(folly::exception_wrapper ex) { + if (auto subscriber = std::exchange(subscriber_, nullptr)) { + subscriber->onError(std::move(ex)); + } + } + + // used internally, not an interface method + void onComplete() { + if (auto subscriber = std::exchange(subscriber_, nullptr)) { + subscriber->onComplete(); + } + } + + bool isCancelled() const { + return !subscriber_; + } + + private: + std::shared_ptr<observable::Observer<T>> subscriber_; + PublishProcessor* processor_; + }; + + static const std::shared_ptr<const PublishersVector>& kCompletedPublishers() { + static std::shared_ptr<const PublishersVector> constant = + std::make_shared<const PublishersVector>(); + return constant; + } + + static const std::shared_ptr<const PublishersVector>& kErroredPublishers() { + static std::shared_ptr<const PublishersVector> constant = + std::make_shared<const PublishersVector>(); + return constant; + } + + folly::Synchronized<std::shared_ptr<const PublishersVector>, std::mutex> + publishers_; +}; +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscriber.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscriber.h new file mode 100644 index 000000000..d1dc3b525 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscriber.h @@ -0,0 +1,448 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <boost/noncopyable.hpp> +#include <folly/ExceptionWrapper.h> +#include <folly/functional/Invoke.h> +#include <glog/logging.h> +#include "yarpl/Disposable.h" +#include "yarpl/Refcounted.h" +#include "yarpl/flowable/Subscription.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { +namespace flowable { + +template <typename T> +class Subscriber : boost::noncopyable { + public: + virtual ~Subscriber() = default; + virtual void onSubscribe(std::shared_ptr<Subscription>) = 0; + virtual void onComplete() = 0; + virtual void onError(folly::exception_wrapper) = 0; + virtual void onNext(T) = 0; + + template < + typename Next, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value>::type> + static std::shared_ptr<Subscriber<T>> create( + Next&& next, + int64_t batch = credits::kNoFlowControl); + + template < + typename Next, + typename Error, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type> + static std::shared_ptr<Subscriber<T>> + create(Next&& next, Error&& error, int64_t batch = credits::kNoFlowControl); + + template < + typename Next, + typename Error, + typename Complete, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value && + folly::is_invocable<std::decay_t<Complete>&>::value>::type> + static std::shared_ptr<Subscriber<T>> create( + Next&& next, + Error&& error, + Complete&& complete, + int64_t batch = credits::kNoFlowControl); + + static std::shared_ptr<Subscriber<T>> create() { + class NullSubscriber : public Subscriber<T> { + void onSubscribe(std::shared_ptr<Subscription> s) override final { + s->request(credits::kNoFlowControl); + } + + void onNext(T) override final {} + void onComplete() override {} + void onError(folly::exception_wrapper) override {} + }; + return std::make_shared<NullSubscriber>(); + } +}; + +namespace details { + +template <typename T> +class BaseSubscriberDisposable; + +} // namespace details + +#define KEEP_REF_TO_THIS() \ + std::shared_ptr<BaseSubscriber> self; \ + if (keep_reference_to_this) { \ + self = this->ref_from_this(this); \ + } + +// T : Type of Flowable that this Subscriber operates on +// +// keep_reference_to_this : BaseSubscriber will keep a live reference to +// itself on the stack while in a signaling or requesting method, in case +// the derived class causes all other references to itself to be dropped. +// +// Classes that ensure that at least one reference will stay live can +// use `keep_reference_to_this = false` as an optimization to +// prevent an atomic inc/dec pair +template <typename T, bool keep_reference_to_this = true> +class BaseSubscriber : public Subscriber<T>, public yarpl::enable_get_ref { + public: + // Note: If any of the following methods is overridden in a subclass, the new + // methods SHOULD ensure that these are invoked as well. + void onSubscribe(std::shared_ptr<Subscription> subscription) final override { + CHECK(subscription); + CHECK(!yarpl::atomic_load(&subscription_)); + +#ifndef NDEBUG + DCHECK(!gotOnSubscribe_.exchange(true)) + << "Already subscribed to BaseSubscriber"; +#endif + + yarpl::atomic_store(&subscription_, std::move(subscription)); + KEEP_REF_TO_THIS(); + onSubscribeImpl(); + } + + // No further calls to the subscription after this method is invoked. + void onComplete() final override { +#ifndef NDEBUG + DCHECK(gotOnSubscribe_.load()) << "Not subscribed to BaseSubscriber"; + DCHECK(!gotTerminating_.exchange(true)) + << "Already got terminating signal method"; +#endif + + std::shared_ptr<Subscription> null; + if (auto sub = yarpl::atomic_exchange(&subscription_, null)) { + KEEP_REF_TO_THIS(); + onCompleteImpl(); + onTerminateImpl(); + } + } + + // No further calls to the subscription after this method is invoked. + void onError(folly::exception_wrapper e) final override { +#ifndef NDEBUG + DCHECK(gotOnSubscribe_.load()) << "Not subscribed to BaseSubscriber"; + DCHECK(!gotTerminating_.exchange(true)) + << "Already got terminating signal method"; +#endif + + std::shared_ptr<Subscription> null; + if (auto sub = yarpl::atomic_exchange(&subscription_, null)) { + KEEP_REF_TO_THIS(); + onErrorImpl(std::move(e)); + onTerminateImpl(); + } + } + + void onNext(T t) final override { +#ifndef NDEBUG + DCHECK(gotOnSubscribe_.load()) << "Not subscibed to BaseSubscriber"; + if (gotTerminating_.load()) { + VLOG(2) << "BaseSubscriber already got terminating signal method"; + } +#endif + + if (auto sub = yarpl::atomic_load(&subscription_)) { + KEEP_REF_TO_THIS(); + onNextImpl(std::move(t)); + } + } + + void cancel() { + std::shared_ptr<Subscription> null; + if (auto sub = yarpl::atomic_exchange(&subscription_, null)) { + KEEP_REF_TO_THIS(); + sub->cancel(); + onTerminateImpl(); + } +#ifndef NDEBUG + else { + VLOG(2) << "cancel() on BaseSubscriber with no subscription_"; + } +#endif + } + + void request(int64_t n) { + if (auto sub = yarpl::atomic_load(&subscription_)) { + KEEP_REF_TO_THIS(); + sub->request(n); + } +#ifndef NDEBUG + else { + VLOG(2) << "request() on BaseSubscriber with no subscription_"; + } +#endif + } + + protected: + virtual void onSubscribeImpl() = 0; + virtual void onCompleteImpl() = 0; + virtual void onNextImpl(T) = 0; + virtual void onErrorImpl(folly::exception_wrapper) = 0; + + virtual void onTerminateImpl() {} + + private: + bool isTerminated() { + return !yarpl::atomic_load(&subscription_); + } + + friend class ::yarpl::flowable::details::BaseSubscriberDisposable<T>; + + // keeps a reference alive to the subscription + AtomicReference<Subscription> subscription_; + +#ifndef NDEBUG + std::atomic<bool> gotOnSubscribe_{false}; + std::atomic<bool> gotTerminating_{false}; +#endif +}; + +namespace details { + +template <typename T> +class BaseSubscriberDisposable : public Disposable { + public: + BaseSubscriberDisposable(std::shared_ptr<BaseSubscriber<T>> subscriber) + : subscriber_(std::move(subscriber)) {} + + void dispose() override { + if (auto sub = yarpl::atomic_exchange(&subscriber_, nullptr)) { + sub->cancel(); + } + } + + bool isDisposed() override { + if (auto sub = yarpl::atomic_load(&subscriber_)) { + return sub->isTerminated(); + } else { + return true; + } + } + + private: + AtomicReference<BaseSubscriber<T>> subscriber_; +}; + +template <typename T> +class LambdaSubscriber : public BaseSubscriber<T> { + public: + template < + typename Next, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value>::type> + static std::shared_ptr<LambdaSubscriber<T>> create( + Next&& next, + int64_t batch = credits::kNoFlowControl); + + template < + typename Next, + typename Error, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type> + static std::shared_ptr<LambdaSubscriber<T>> + create(Next&& next, Error&& error, int64_t batch = credits::kNoFlowControl); + + template < + typename Next, + typename Error, + typename Complete, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value && + folly::is_invocable<std::decay_t<Complete>&>::value>::type> + static std::shared_ptr<LambdaSubscriber<T>> create( + Next&& next, + Error&& error, + Complete&& complete, + int64_t batch = credits::kNoFlowControl); +}; + +template <typename T, typename Next> +class Base : public LambdaSubscriber<T> { + static_assert(std::is_same<std::decay_t<Next>, Next>::value, "undecayed"); + + public: + template <typename FNext> + Base(FNext&& next, int64_t batch) + : next_(std::forward<FNext>(next)), batch_(batch), pending_(0) {} + + void onSubscribeImpl() override final { + pending_ = batch_; + this->request(batch_); + } + + void onNextImpl(T value) override final { + try { + next_(std::move(value)); + } catch (const std::exception& exn) { + this->cancel(); + auto ew = folly::exception_wrapper{std::current_exception(), exn}; + LOG(ERROR) << "'next' method should not throw: " << ew.what(); + onErrorImpl(ew); + return; + } + + if (--pending_ <= batch_ / 2) { + const auto delta = batch_ - pending_; + pending_ += delta; + this->request(delta); + } + } + + void onCompleteImpl() override {} + void onErrorImpl(folly::exception_wrapper) override {} + + private: + Next next_; + const int64_t batch_; + int64_t pending_; +}; + +template <typename T, typename Next, typename Error> +class WithError : public Base<T, Next> { + static_assert(std::is_same<std::decay_t<Error>, Error>::value, "undecayed"); + + public: + template <typename FNext, typename FError> + WithError(FNext&& next, FError&& error, int64_t batch) + : Base<T, Next>(std::forward<FNext>(next), batch), + error_(std::forward<FError>(error)) {} + + void onErrorImpl(folly::exception_wrapper error) override final { + try { + error_(std::move(error)); + } catch (const std::exception& exn) { + LOG(ERROR) << "'error' method should not throw: " << exn.what(); + } + } + + private: + Error error_; +}; + +template <typename T, typename Next, typename Error, typename Complete> +class WithErrorAndComplete : public WithError<T, Next, Error> { + static_assert( + std::is_same<std::decay_t<Complete>, Complete>::value, + "undecayed"); + + public: + template <typename FNext, typename FError, typename FComplete> + WithErrorAndComplete( + FNext&& next, + FError&& error, + FComplete&& complete, + int64_t batch) + : WithError<T, Next, Error>( + std::forward<FNext>(next), + std::forward<FError>(error), + batch), + complete_(std::forward<FComplete>(complete)) {} + + void onCompleteImpl() override final { + try { + complete_(); + } catch (const std::exception& exn) { + LOG(ERROR) << "'complete' method should not throw: " << exn.what(); + } + } + + private: + Complete complete_; +}; + +template <typename T> +template <typename Next, typename> +std::shared_ptr<LambdaSubscriber<T>> LambdaSubscriber<T>::create( + Next&& next, + int64_t batch) { + return std::make_shared<details::Base<T, std::decay_t<Next>>>( + std::forward<Next>(next), batch); +} + +template <typename T> +template <typename Next, typename Error, typename> +std::shared_ptr<LambdaSubscriber<T>> +LambdaSubscriber<T>::create(Next&& next, Error&& error, int64_t batch) { + return std::make_shared< + details::WithError<T, std::decay_t<Next>, std::decay_t<Error>>>( + std::forward<Next>(next), std::forward<Error>(error), batch); +} + +template <typename T> +template <typename Next, typename Error, typename Complete, typename> +std::shared_ptr<LambdaSubscriber<T>> LambdaSubscriber<T>::create( + Next&& next, + Error&& error, + Complete&& complete, + int64_t batch) { + return std::make_shared<details::WithErrorAndComplete< + T, + std::decay_t<Next>, + std::decay_t<Error>, + std::decay_t<Complete>>>( + std::forward<Next>(next), + std::forward<Error>(error), + std::forward<Complete>(complete), + batch); +} + +} // namespace details + +template <typename T> +template <typename Next, typename> +std::shared_ptr<Subscriber<T>> Subscriber<T>::create( + Next&& next, + int64_t batch) { + return details::LambdaSubscriber<T>::create(std::forward<Next>(next), batch); +} + +template <typename T> +template <typename Next, typename Error, typename> +std::shared_ptr<Subscriber<T>> +Subscriber<T>::create(Next&& next, Error&& error, int64_t batch) { + return details::LambdaSubscriber<T>::create( + std::forward<Next>(next), std::forward<Error>(error), batch); +} + +template <typename T> +template <typename Next, typename Error, typename Complete, typename> +std::shared_ptr<Subscriber<T>> Subscriber<T>::create( + Next&& next, + Error&& error, + Complete&& complete, + int64_t batch) { + return details::LambdaSubscriber<T>::create( + std::forward<Next>(next), + std::forward<Error>(error), + std::forward<Complete>(complete), + batch); +} + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.cpp b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.cpp new file mode 100644 index 000000000..a49e1c97c --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "yarpl/flowable/Subscription.h" + +namespace yarpl { +namespace flowable { + +std::shared_ptr<Subscription> Subscription::create() { + class NullSubscription : public Subscription { + void request(int64_t) override {} + void cancel() override {} + }; + return std::make_shared<NullSubscription>(); +} + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.h new file mode 100644 index 000000000..bc4c49bbe --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/Subscription.h @@ -0,0 +1,87 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/Refcounted.h" + +namespace yarpl { +namespace flowable { + +class Subscription { + public: + virtual ~Subscription() = default; + + virtual void request(int64_t n) = 0; + virtual void cancel() = 0; + + static std::shared_ptr<Subscription> create(); + + template <typename CancelFunc> + static std::shared_ptr<Subscription> create(CancelFunc&& onCancel); + + template <typename CancelFunc, typename RequestFunc> + static std::shared_ptr<Subscription> create( + CancelFunc&& onCancel, + RequestFunc&& onRequest); +}; + +namespace detail { + +template <typename CancelFunc, typename RequestFunc> +class CallbackSubscription : public Subscription { + static_assert( + std::is_same<std::decay_t<CancelFunc>, CancelFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<RequestFunc>, RequestFunc>::value, + "undecayed"); + + public: + template <typename FCancel, typename FRequest> + CallbackSubscription(FCancel&& onCancel, FRequest&& onRequest) + : onCancel_(std::forward<FCancel>(onCancel)), + onRequest_(std::forward<FRequest>(onRequest)) {} + + void request(int64_t n) override { + onRequest_(n); + } + void cancel() override { + onCancel_(); + } + + private: + CancelFunc onCancel_; + RequestFunc onRequest_; +}; +} // namespace detail + +template <typename CancelFunc, typename RequestFunc> +std::shared_ptr<Subscription> Subscription::create( + CancelFunc&& onCancel, + RequestFunc&& onRequest) { + return std::make_shared<detail::CallbackSubscription< + std::decay_t<CancelFunc>, + std::decay_t<RequestFunc>>>( + std::forward<CancelFunc>(onCancel), std::forward<RequestFunc>(onRequest)); +} + +template <typename CancelFunc> +std::shared_ptr<Subscription> Subscription::create(CancelFunc&& onCancel) { + return Subscription::create( + std::forward<CancelFunc>(onCancel), [](int64_t) {}); +} + +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/TestSubscriber.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/TestSubscriber.h new file mode 100644 index 000000000..127b7fd0f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/TestSubscriber.h @@ -0,0 +1,270 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <condition_variable> +#include <mutex> +#include <sstream> +#include <vector> + +#include "yarpl/flowable/Flowable.h" +#include "yarpl/flowable/Subscriber.h" +#include "yarpl/utils/credits.h" + +namespace yarpl { +namespace flowable { + +/** + * A utility class for unit testing or experimenting with Flowable. + * + * Example usage: + * + * auto flowable = ... + * auto ts = TestSubscriber<int>::create(); + * flowable->subscribe(to); + * ts->awaitTerminalEvent(); + * ts->assert... + */ +template <typename T> +class TestSubscriber : public BaseSubscriber<T>, + public yarpl::flowable::Subscription { + public: + static_assert( + std::is_copy_constructible<T>::value, + "Requires copyable types in case of a delegate subscriber"); + + constexpr static auto kCanceled = credits::kCanceled; + constexpr static auto kNoFlowControl = credits::kNoFlowControl; + + /** + * Create a TestSubscriber that will subscribe and store the value it + * receives. + */ + static std::shared_ptr<TestSubscriber<T>> create( + int64_t initial = kNoFlowControl) { + return std::make_shared<TestSubscriber<T>>(initial); + } + + /** + * Create a TestSubscriber that will delegate all on* method calls + * to the provided Subscriber. + * + * This will store the value it receives to allow assertions. + */ + static std::shared_ptr<TestSubscriber<T>> create( + std::shared_ptr<Subscriber<T>> delegate, + int64_t initial = kNoFlowControl) { + return std::make_shared<TestSubscriber<T>>(std::move(delegate), initial); + } + + explicit TestSubscriber(int64_t initial = kNoFlowControl) + : TestSubscriber(std::shared_ptr<Subscriber<T>>{}, initial) {} + + explicit TestSubscriber( + std::shared_ptr<Subscriber<T>> delegate, + int64_t initial = kNoFlowControl) + : delegate_(std::move(delegate)), initial_{initial} {} + + void onSubscribeImpl() override { + if (delegate_) { + delegate_->onSubscribe(this->ref_from_this(this)); + } + this->request(initial_); + } + + void onNextImpl(T t) override final { + manuallyPush(std::move(t)); + } + + void manuallyPush(T t) { + if (dropValues_) { + valueCount_++; + } else { + if (delegate_) { + values_.push_back(t); + delegate_->onNext(std::move(t)); + } else { + values_.push_back(std::move(t)); + } + } + + terminalEventCV_.notify_all(); + } + + void onCompleteImpl() override final { + if (delegate_) { + delegate_->onComplete(); + } + } + + void onErrorImpl(folly::exception_wrapper ex) override final { + if (delegate_) { + delegate_->onError(ex); + } + e_ = std::move(ex); + } + + void onTerminateImpl() override final { + std::unique_lock<std::mutex> lk(m_); + terminated_ = true; + terminalEventCV_.notify_all(); + } + + // flowable::Subscription methods + void request(int64_t n) override { + this->BaseSubscriber<T>::request(n); + } + void cancel() override { + this->BaseSubscriber<T>::cancel(); + } + + /** + * Block the current thread until either onSuccess or onError is called. + */ + void awaitTerminalEvent( + std::chrono::milliseconds ms = std::chrono::seconds{1}) { + // now block this thread + std::unique_lock<std::mutex> lk(m_); + // if shutdown gets implemented this would then be released by it + if (!terminalEventCV_.wait_for(lk, ms, [this] { return terminated_; })) { + throw std::runtime_error("timeout in awaitTerminalEvent"); + } + } + + void awaitValueCount( + int64_t n, + std::chrono::milliseconds ms = std::chrono::seconds{1}) { + // now block this thread + std::unique_lock<std::mutex> lk(m_); + + auto didTimeOut = terminalEventCV_.wait_for(lk, ms, [this, n] { + if (getValueCount() < n && terminated_) { + std::stringstream msg; + msg << "onComplete/onError called before valueCount() == n;\nvalueCount: " + << getValueCount() << " != " << n; + throw std::runtime_error(msg.str()); + } + return getValueCount() >= n; + }); + + if (!didTimeOut) { + throw std::runtime_error("timeout in awaitValueCount"); + }; + } + + void assertValueCount(size_t count) { + if (values_.size() != count) { + std::stringstream ss; + ss << "Value count " << values_.size() << " does not match " << count; + throw std::runtime_error(ss.str()); + } + } + + int64_t getValueCount() { + if (dropValues_) { + return valueCount_; + } else { + return values_.size(); + } + } + + std::vector<T>& values() { + return values_; + } + + const std::vector<T>& values() const { + return values_; + } + + bool isComplete() const { + return terminated_ && !e_; + } + + bool isError() const { + return terminated_ && e_; + } + + const folly::exception_wrapper& exceptionWrapper() const { + return e_; + } + + std::string getErrorMsg() const { + return e_ ? e_.get_exception()->what() : ""; + } + + void assertValueAt(int64_t index, T expected) { + if (index < getValueCount()) { + auto& v = values_[index]; + if (expected != v) { + std::stringstream ss; + ss << "Expected: " << expected << " Actual: " << v; + throw std::runtime_error(ss.str()); + } + } else { + std::stringstream ss; + ss << "Index " << index << " is larger than received values " + << values_.size(); + throw std::runtime_error(ss.str()); + } + } + + /** + * If an onComplete call was not received throw a runtime_error + */ + void assertSuccess() { + if (!terminated_) { + throw std::runtime_error("Did not receive terminal event."); + } + if (e_) { + throw std::runtime_error("Received onError instead of onSuccess"); + } + } + + /** + * If the onError exception_wrapper points to an error containing + * the given msg, complete successfully, otherwise throw a runtime_error + */ + void assertOnErrorMessage(std::string msg) { + if (!e_ || e_.get_exception()->what() != msg) { + std::stringstream ss; + ss << "Error is: '" << e_ << "' but expected: '" << msg << "'"; + throw std::runtime_error(ss.str()); + } + } + + folly::exception_wrapper getException() const { + return e_; + } + + void dropValues(bool drop) { + valueCount_ = getValueCount(); + dropValues_ = drop; + } + + private: + bool dropValues_{false}; + std::atomic<int> valueCount_{0}; + + std::shared_ptr<Subscriber<T>> delegate_; + std::vector<T> values_; + folly::exception_wrapper e_; + int64_t initial_{kNoFlowControl}; + bool terminated_{false}; + std::mutex m_; + std::condition_variable terminalEventCV_; + std::shared_ptr<Subscription> subscription_; +}; +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h b/ios/Pods/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h new file mode 100644 index 000000000..23d32627f --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h @@ -0,0 +1,258 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include <folly/Portability.h> +#if FOLLY_HAS_COROUTINES +#include <folly/experimental/coro/Baton.h> +#include <folly/experimental/coro/Invoke.h> +#include <folly/experimental/coro/Task.h> +#endif +#include <folly/executors/SerialExecutor.h> + +#include <thrift/lib/cpp2/async/ClientBufferedStream.h> +#include <thrift/lib/cpp2/async/ServerStream.h> +#include <yarpl/flowable/Flowable.h> + +namespace yarpl { +namespace flowable { +class ThriftStreamShim { + public: +#if FOLLY_HAS_COROUTINES + template <typename T> + static std::shared_ptr<yarpl::flowable::Flowable<T>> fromClientStream( + apache::thrift::ClientBufferedStream<T>&& stream, + folly::Executor::KeepAlive<> ex) { + struct SharedState { + SharedState( + apache::thrift::detail::ClientStreamBridge::ClientPtr streamBridge, + folly::Executor::KeepAlive<> ex) + : streamBridge_(std::move(streamBridge)), + ex_(folly::SerialExecutor::create(std::move(ex))) {} + apache::thrift::detail::ClientStreamBridge::Ptr streamBridge_; + folly::Executor::KeepAlive<folly::SequencedExecutor> ex_; + std::atomic<bool> canceled_{false}; + }; + + return yarpl::flowable::internal::flowableFromSubscriber<T>( + [state = + std::make_shared<SharedState>(std::move(stream.streamBridge_), ex), + decode = + stream.decode_](std::shared_ptr<yarpl::flowable::Subscriber<T>> + subscriber) mutable { + class Subscription : public yarpl::flowable::Subscription { + public: + explicit Subscription(std::weak_ptr<SharedState> state) + : state_(std::move(state)) {} + + void request(int64_t n) override { + CHECK(n != yarpl::credits::kNoFlowControl) + << "kNoFlowControl unsupported"; + + if (auto state = state_.lock()) { + state->ex_->add([n, state = std::move(state)]() { + state->streamBridge_->requestN(n); + }); + } + } + + void cancel() override { + if (auto state = state_.lock()) { + state->ex_->add([state = std::move(state)]() { + state->streamBridge_->cancel(); + state->canceled_ = true; + }); + } + } + + private: + std::weak_ptr<SharedState> state_; + }; + + state->ex_->add([keepAlive = state->ex_.copy(), + subscriber, + subscription = std::make_shared<Subscription>( + std::weak_ptr<SharedState>(state))]() mutable { + subscriber->onSubscribe(std::move(subscription)); + }); + + folly::coro::co_invoke( + [subscriber = std::move(subscriber), + state, + decode]() mutable -> folly::coro::Task<void> { + apache::thrift::detail::ClientStreamBridge::ClientQueue queue; + class ReadyCallback + : public apache::thrift::detail::ClientStreamConsumer { + public: + void consume() override { + baton.post(); + } + + void canceled() override { + baton.post(); + } + + folly::coro::Baton baton; + }; + + while (!state->canceled_) { + if (queue.empty()) { + ReadyCallback callback; + if (state->streamBridge_->wait(&callback)) { + co_await callback.baton; + } + queue = state->streamBridge_->getMessages(); + if (queue.empty()) { + // we've been cancelled + apache::thrift::detail::ClientStreamBridge::Ptr( + state->streamBridge_.release()); + break; + } + } + + { + auto& payload = queue.front(); + if (!payload.hasValue() && !payload.hasException()) { + state->ex_->add([subscriber = std::move(subscriber), + keepAlive = state->ex_.copy()] { + subscriber->onComplete(); + }); + break; + } + auto value = decode(std::move(payload)); + queue.pop(); + if (value.hasValue()) { + state->ex_->add([subscriber, + keepAlive = state->ex_.copy(), + value = std::move(value)]() mutable { + subscriber->onNext(std::move(value).value()); + }); + } else if (value.hasException()) { + state->ex_->add([subscriber = std::move(subscriber), + keepAlive = state->ex_.copy(), + value = std::move(value)]() mutable { + subscriber->onError(std::move(value).exception()); + }); + break; + } else { + LOG(FATAL) << "unreachable"; + } + } + } + }) + .scheduleOn(state->ex_) + .start(); + }); + } +#endif + + template <typename T> + static apache::thrift::ServerStream<T> toServerStream( + std::shared_ptr<Flowable<T>> flowable) { + class StreamServerCallbackAdaptor final + : public apache::thrift::StreamServerCallback, + public Subscriber<T> { + public: + explicit StreamServerCallbackAdaptor( + folly::Try<apache::thrift::StreamPayload> (*encode)(folly::Try<T>&&), + folly::EventBase* eb) + : encode_(encode), eb_(eb) {} + // StreamServerCallback implementation + bool onStreamRequestN(uint64_t tokens) override { + if (!subscription_) { + tokensBeforeSubscribe_ += tokens; + } else { + DCHECK_EQ(0, tokensBeforeSubscribe_); + subscription_->request(tokens); + } + return clientCallback_; + } + void onStreamCancel() override { + clientCallback_ = nullptr; + if (auto subscription = std::move(subscription_)) { + subscription->cancel(); + } + self_.reset(); + } + void resetClientCallback( + apache::thrift::StreamClientCallback& clientCallback) override { + clientCallback_ = &clientCallback; + } + + // Subscriber implementation + void onSubscribe(std::shared_ptr<Subscription> subscription) override { + eb_->add([this, subscription = std::move(subscription)]() mutable { + if (!clientCallback_) { + return subscription->cancel(); + } + + subscription_ = std::move(subscription); + if (auto tokens = std::exchange(tokensBeforeSubscribe_, 0)) { + subscription_->request(tokens); + } + }); + } + void onNext(T next) override { + eb_->add([this, next = std::move(next), s = self_]() mutable { + if (clientCallback_) { + std::ignore = + clientCallback_->onStreamNext(apache::thrift::StreamPayload{ + encode_(folly::Try<T>(std::move(next))).value().payload, + {}}); + } + }); + } + void onError(folly::exception_wrapper ew) override { + eb_->add([this, ew = std::move(ew), s = self_]() mutable { + if (clientCallback_) { + std::exchange(clientCallback_, nullptr) + ->onStreamError( + encode_(folly::Try<T>(std::move(ew))).exception()); + self_.reset(); + } + }); + } + void onComplete() override { + eb_->add([this, s = self_] { + if (clientCallback_) { + std::exchange(clientCallback_, nullptr)->onStreamComplete(); + self_.reset(); + } + }); + } + + void takeRef(std::shared_ptr<StreamServerCallbackAdaptor> self) { + self_ = std::move(self); + } + + private: + apache::thrift::StreamClientCallback* clientCallback_{nullptr}; + std::shared_ptr<Subscription> subscription_; + uint32_t tokensBeforeSubscribe_{0}; + folly::Try<apache::thrift::StreamPayload> (*encode_)(folly::Try<T>&&); + folly::EventBase* eb_; + std::shared_ptr<StreamServerCallbackAdaptor> self_; + }; + + return apache::thrift::ServerStream<T>( + [flowable = std::move(flowable)]( + folly::Executor::KeepAlive<>, + folly::Try<apache::thrift::StreamPayload> (*encode)( + folly::Try<T> &&)) mutable { + return [flowable = std::move(flowable), encode]( + apache::thrift::FirstResponsePayload&& payload, + apache::thrift::StreamClientCallback* callback, + folly::EventBase* clientEb) mutable { + auto stream = + std::make_shared<StreamServerCallbackAdaptor>(encode, clientEb); + stream->takeRef(stream); + stream->resetClientCallback(*callback); + std::ignore = callback->onFirstResponse( + std::move(payload), clientEb, stream.get()); + flowable->subscribe(std::move(stream)); + }; + }); + } +}; +} // namespace flowable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/DeferObservable.h b/ios/Pods/Flipper-RSocket/yarpl/observable/DeferObservable.h new file mode 100644 index 000000000..302aeecaa --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/DeferObservable.h @@ -0,0 +1,50 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/observable/Observable.h" + +namespace yarpl { +namespace observable { +namespace details { + +template <typename T, typename ObservableFactory> +class DeferObservable : public Observable<T> { + static_assert( + std::is_same<std::decay_t<ObservableFactory>, ObservableFactory>::value, + "undecayed"); + + public: + template <typename F> + explicit DeferObservable(F&& factory) : factory_(std::forward<F>(factory)) {} + + virtual std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) { + std::shared_ptr<Observable<T>> observable; + try { + observable = factory_(); + } catch (const std::exception& ex) { + observable = Observable<T>::error(ex, std::current_exception()); + } + return observable->subscribe(std::move(observer)); + } + + private: + ObservableFactory factory_; +}; + +} // namespace details +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Observable.h b/ios/Pods/Flipper-RSocket/yarpl/observable/Observable.h new file mode 100644 index 000000000..30729c360 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Observable.h @@ -0,0 +1,560 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/functional/Invoke.h> +#include <memory> +#include <type_traits> +#include <utility> + +#include "yarpl/Refcounted.h" +#include "yarpl/observable/Observer.h" +#include "yarpl/observable/Subscription.h" + +#include "yarpl/Common.h" +#include "yarpl/Flowable.h" +#include "yarpl/flowable/Flowable_FromObservable.h" + +namespace yarpl { + +namespace observable { + +template <typename T = void> +class Observable : public yarpl::enable_get_ref { + public: + static std::shared_ptr<Observable<T>> empty() { + auto lambda = [](std::shared_ptr<Observer<T>> observer) { + observer->onComplete(); + }; + return Observable<T>::create(std::move(lambda)); + } + + static std::shared_ptr<Observable<T>> error(folly::exception_wrapper ex) { + auto lambda = + [ex = std::move(ex)](std::shared_ptr<Observer<T>> observer) mutable { + observer->onError(std::move(ex)); + }; + return Observable<T>::create(std::move(lambda)); + } + + template <typename Ex> + static std::shared_ptr<Observable<T>> error(Ex&) { + static_assert( + std::is_lvalue_reference<Ex>::value, + "use variant of error() method accepting also exception_ptr"); + } + + template <typename Ex> + static std::shared_ptr<Observable<T>> error(Ex& ex, std::exception_ptr ptr) { + auto lambda = [ew = folly::exception_wrapper(std::move(ptr), ex)]( + std::shared_ptr<Observer<T>> observer) mutable { + observer->onError(std::move(ew)); + }; + return Observable<T>::create(std::move(lambda)); + } + + static std::shared_ptr<Observable<T>> just(T value) { + auto lambda = + [value = std::move(value)](std::shared_ptr<Observer<T>> observer) { + observer->onNext(value); + observer->onComplete(); + }; + + return Observable<T>::create(std::move(lambda)); + } + + /** + * The Defer operator waits until an observer subscribes to it, and then it + * generates an Observable with an ObservableFactory function. It + * does this afresh for each subscriber, so although each subscriber may + * think it is subscribing to the same Observable, in fact each subscriber + * gets its own individual sequence. + */ + template < + typename ObservableFactory, + typename = typename std::enable_if<folly::is_invocable_r< + std::shared_ptr<Observable<T>>, + std::decay_t<ObservableFactory>&>::value>::type> + static std::shared_ptr<Observable<T>> defer(ObservableFactory&&); + + static std::shared_ptr<Observable<T>> justN(std::initializer_list<T> list) { + auto lambda = [v = std::vector<T>(std::move(list))]( + std::shared_ptr<Observer<T>> observer) { + for (auto const& elem : v) { + observer->onNext(elem); + } + observer->onComplete(); + }; + + return Observable<T>::create(std::move(lambda)); + } + + // this will generate an observable which can be subscribed to only once + static std::shared_ptr<Observable<T>> justOnce(T value) { + auto lambda = [value = std::move(value), used = false]( + std::shared_ptr<Observer<T>> observer) mutable { + if (used) { + observer->onError( + std::runtime_error("justOnce value was already used")); + return; + } + + used = true; + observer->onNext(std::move(value)); + observer->onComplete(); + }; + + return Observable<T>::create(std::move(lambda)); + } + + template <typename OnSubscribe> + static std::shared_ptr<Observable<T>> create(OnSubscribe&&); + + template <typename OnSubscribe> + static std::shared_ptr<Observable<T>> createEx(OnSubscribe&&); + + virtual std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>>) = 0; + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Next, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value>::type> + std::shared_ptr<Subscription> subscribe(Next&& next) { + return subscribe(Observer<T>::create(std::forward<Next>(next))); + } + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Next, + typename Error, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type> + std::shared_ptr<Subscription> subscribe(Next&& next, Error&& error) { + return subscribe(Observer<T>::create( + std::forward<Next>(next), std::forward<Error>(error))); + } + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Next, + typename Error, + typename Complete, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Next>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value && + folly::is_invocable<std::decay_t<Complete>&>::value>::type> + std::shared_ptr<Subscription> + subscribe(Next&& next, Error&& error, Complete&& complete) { + return subscribe(Observer<T>::create( + std::forward<Next>(next), + std::forward<Error>(error), + std::forward<Complete>(complete))); + } + + std::shared_ptr<Subscription> subscribe() { + return subscribe(Observer<T>::create()); + } + + template < + typename Function, + typename R = typename folly::invoke_result_t<Function, T>> + std::shared_ptr<Observable<R>> map(Function&& function); + + template <typename Function> + std::shared_ptr<Observable<T>> filter(Function&& function); + + template < + typename Function, + typename R = typename folly::invoke_result_t<Function, T, T>> + std::shared_ptr<Observable<R>> reduce(Function&& function); + + std::shared_ptr<Observable<T>> take(int64_t); + + std::shared_ptr<Observable<T>> skip(int64_t); + + std::shared_ptr<Observable<T>> ignoreElements(); + + std::shared_ptr<Observable<T>> subscribeOn(folly::Executor&); + + std::shared_ptr<Observable<T>> concatWith(std::shared_ptr<Observable<T>>); + + template <typename... Args> + std::shared_ptr<Observable<T>> concatWith( + std::shared_ptr<Observable<T>> first, + Args... args) { + return concatWith(first)->concatWith(args...); + } + + template <typename... Args> + static std::shared_ptr<Observable<T>> concat( + std::shared_ptr<Observable<T>> first, + Args... args) { + return first->concatWith(args...); + } + + // function is invoked when onComplete occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Observable<T>> doOnSubscribe(Function&& function); + + // function is invoked when onNext occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&, const T&>::value>::type> + std::shared_ptr<Observable<T>> doOnNext(Function&& function); + + // function is invoked when onError occurs. + template < + typename Function, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<Function>&, + folly::exception_wrapper&>::value>::type> + std::shared_ptr<Observable<T>> doOnError(Function&& function); + + // function is invoked when onComplete occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Observable<T>> doOnComplete(Function&& function); + + // function is invoked when either onComplete or onError occurs. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Observable<T>> doOnTerminate(Function&& function); + + // the function is invoked for each of onNext, onCompleted, onError + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Observable<T>> doOnEach(Function&& function); + + // the callbacks will be invoked of each of the signals + template < + typename OnNextFunc, + typename OnCompleteFunc, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnNextFunc>&, const T&>::value>:: + type, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnCompleteFunc>&>::value>::type> + std::shared_ptr<Observable<T>> doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete); + + // the callbacks will be invoked of each of the signals + template < + typename OnNextFunc, + typename OnCompleteFunc, + typename OnErrorFunc, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnNextFunc>&, const T&>::value>:: + type, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<OnCompleteFunc>&>::value>::type, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<OnErrorFunc>&, + folly::exception_wrapper&>::value>::type> + std::shared_ptr<Observable<T>> + doOn(OnNextFunc&& onNext, OnCompleteFunc&& onComplete, OnErrorFunc&& onError); + + // function is invoked when cancel is called. + template < + typename Function, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Function>&>::value>::type> + std::shared_ptr<Observable<T>> doOnCancel(Function&& function); + + /** + * Convert from Observable to Flowable with a given BackpressureStrategy. + */ + auto toFlowable(BackpressureStrategy strategy); + + /** + * Convert from Observable to Flowable with a given BackpressureStrategy. + */ + auto toFlowable(std::shared_ptr<IBackpressureStrategy<T>> strategy); +}; +} // namespace observable +} // namespace yarpl + +#include "yarpl/observable/DeferObservable.h" +#include "yarpl/observable/ObservableOperator.h" + +namespace yarpl { +namespace observable { + +template <typename T> +template <typename OnSubscribe> +std::shared_ptr<Observable<T>> Observable<T>::create(OnSubscribe&& function) { + static_assert( + folly::is_invocable<OnSubscribe&&, std::shared_ptr<Observer<T>>>::value, + "OnSubscribe must have type `void(std::shared_ptr<Observer<T>>)`"); + + return createEx([func = std::forward<OnSubscribe>(function)]( + std::shared_ptr<Observer<T>> observer, + std::shared_ptr<Subscription>) mutable { + func(std::move(observer)); + }); +} + +template <typename T> +template <typename OnSubscribe> +std::shared_ptr<Observable<T>> Observable<T>::createEx(OnSubscribe&& function) { + static_assert( + folly::is_invocable< + OnSubscribe&&, + std::shared_ptr<Observer<T>>, + std::shared_ptr<Subscription>>::value, + "OnSubscribe must have type " + "`void(std::shared_ptr<Observer<T>>, std::shared_ptr<Subscription>)`"); + + return std::make_shared<FromPublisherOperator<T, std::decay_t<OnSubscribe>>>( + std::forward<OnSubscribe>(function)); +} + +template <typename T> +template <typename ObservableFactory, typename> +std::shared_ptr<Observable<T>> Observable<T>::defer( + ObservableFactory&& factory) { + return std::make_shared< + details::DeferObservable<T, std::decay_t<ObservableFactory>>>( + std::forward<ObservableFactory>(factory)); +} + +template <typename T> +template <typename Function, typename R> +std::shared_ptr<Observable<R>> Observable<T>::map(Function&& function) { + return std::make_shared<MapOperator<T, R, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +template <typename Function> +std::shared_ptr<Observable<T>> Observable<T>::filter(Function&& function) { + return std::make_shared<FilterOperator<T, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +template <typename Function, typename R> +std::shared_ptr<Observable<R>> Observable<T>::reduce(Function&& function) { + return std::make_shared<ReduceOperator<T, R, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +template <typename T> +std::shared_ptr<Observable<T>> Observable<T>::take(int64_t limit) { + return std::make_shared<TakeOperator<T>>(this->ref_from_this(this), limit); +} + +template <typename T> +std::shared_ptr<Observable<T>> Observable<T>::skip(int64_t offset) { + return std::make_shared<SkipOperator<T>>(this->ref_from_this(this), offset); +} + +template <typename T> +std::shared_ptr<Observable<T>> Observable<T>::ignoreElements() { + return std::make_shared<IgnoreElementsOperator<T>>(this->ref_from_this(this)); +} + +template <typename T> +std::shared_ptr<Observable<T>> Observable<T>::subscribeOn( + folly::Executor& executor) { + return std::make_shared<SubscribeOnOperator<T>>( + this->ref_from_this(this), executor); +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnSubscribe( + Function&& function) { + return details::createDoOperator( + ref_from_this(this), + std::forward<Function>(function), + [](const T&) {}, + [](const auto&) {}, + [] {}, + [] {}); // onCancel +} + +template <typename T> +std::shared_ptr<Observable<T>> Observable<T>::concatWith( + std::shared_ptr<Observable<T>> next) { + return std::make_shared<details::ConcatWithOperator<T>>( + this->ref_from_this(this), std::move(next)); +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnNext(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<Function>(function), + [](const auto&) {}, + [] {}, + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnError(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + std::forward<Function>(function), + [] {}, + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnComplete( + Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + [](const auto&) {}, + std::forward<Function>(function), + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnTerminate( + Function&& function) { + auto sharedFunction = std::make_shared<std::decay_t<Function>>( + std::forward<Function>(function)); + return details::createDoOperator( + ref_from_this(this), + [] {}, + [](const T&) {}, + [sharedFunction](const auto&) { (*sharedFunction)(); }, + [sharedFunction]() { (*sharedFunction)(); }, + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnEach(Function&& function) { + auto sharedFunction = std::make_shared<std::decay_t<Function>>( + std::forward<Function>(function)); + return details::createDoOperator( + ref_from_this(this), + [] {}, + [sharedFunction](const T&) { (*sharedFunction)(); }, + [sharedFunction](const auto&) { (*sharedFunction)(); }, + [sharedFunction]() { (*sharedFunction)(); }, + [] {}); // onCancel +} + +template <typename T> +template <typename OnNextFunc, typename OnCompleteFunc, typename, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<OnNextFunc>(onNext), + [](const auto&) {}, + std::forward<OnCompleteFunc>(onComplete), + [] {}); // onCancel +} + +template <typename T> +template < + typename OnNextFunc, + typename OnCompleteFunc, + typename OnErrorFunc, + typename, + typename, + typename> +std::shared_ptr<Observable<T>> Observable<T>::doOn( + OnNextFunc&& onNext, + OnCompleteFunc&& onComplete, + OnErrorFunc&& onError) { + return details::createDoOperator( + ref_from_this(this), + [] {}, + std::forward<OnNextFunc>(onNext), + std::forward<OnErrorFunc>(onError), + std::forward<OnCompleteFunc>(onComplete), + [] {}); // onCancel +} + +template <typename T> +template <typename Function, typename> +std::shared_ptr<Observable<T>> Observable<T>::doOnCancel(Function&& function) { + return details::createDoOperator( + ref_from_this(this), + [] {}, // onSubscribe + [](const auto&) {}, // onNext + [](const auto&) {}, // onError + [] {}, // onComplete + std::forward<Function>(function)); // onCancel +} + +template <typename T> +auto Observable<T>::toFlowable(BackpressureStrategy strategy) { + switch (strategy) { + case BackpressureStrategy::DROP: + return toFlowable(IBackpressureStrategy<T>::drop()); + case BackpressureStrategy::ERROR: + return toFlowable(IBackpressureStrategy<T>::error()); + case BackpressureStrategy::BUFFER: + return toFlowable(IBackpressureStrategy<T>::buffer()); + case BackpressureStrategy::LATEST: + return toFlowable(IBackpressureStrategy<T>::latest()); + case BackpressureStrategy::MISSING: + return toFlowable(IBackpressureStrategy<T>::missing()); + default: + CHECK(false); // unknown value for strategy + } +} + +template <typename T> +auto Observable<T>::toFlowable( + std::shared_ptr<IBackpressureStrategy<T>> strategy) { + return yarpl::flowable::internal::flowableFromSubscriber<T>( + [thisObservable = this->ref_from_this(this), + strategy = std::move(strategy)]( + std::shared_ptr<flowable::Subscriber<T>> subscriber) { + strategy->init(std::move(thisObservable), std::move(subscriber)); + }); +} + +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h new file mode 100644 index 000000000..4a3879a4e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h @@ -0,0 +1,154 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/observable/ObservableOperator.h" + +namespace yarpl { +namespace observable { +namespace details { + +template <typename T> +class ConcatWithOperator : public ObservableOperator<T, T> { + using Super = ObservableOperator<T, T>; + + public: + ConcatWithOperator( + std::shared_ptr<Observable<T>> first, + std::shared_ptr<Observable<T>> second) + : first_(std::move(first)), second_(std::move(second)) { + CHECK(first_); + CHECK(second_); + } + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = + std::make_shared<ConcatWithSubscription>(observer, first_, second_); + subscription->init(); + + return subscription; + } + + private: + class ForwardObserver; + + // Downstream will always point to this subscription + class ConcatWithSubscription + : public yarpl::observable::Subscription, + public std::enable_shared_from_this<ConcatWithSubscription> { + public: + ConcatWithSubscription( + std::shared_ptr<Observer<T>> observer, + std::shared_ptr<Observable<T>> first, + std::shared_ptr<Observable<T>> second) + : downObserver_(std::move(observer)), + first_(std::move(first)), + second_(std::move(second)) {} + + void init() { + upObserver_ = std::make_shared<ForwardObserver>(this->shared_from_this()); + downObserver_->onSubscribe(this->shared_from_this()); + if (upObserver_) { + first_->subscribe(upObserver_); + } + } + + void cancel() override { + if (auto observer = std::move(upObserver_)) { + observer->cancel(); + } + first_.reset(); + second_.reset(); + upObserver_.reset(); + downObserver_.reset(); + } + + void onNext(T value) { + downObserver_->onNext(std::move(value)); + } + + void onComplete() { + if (auto first = std::move(first_)) { + upObserver_ = + std::make_shared<ForwardObserver>(this->shared_from_this()); + second_->subscribe(upObserver_); + second_.reset(); + } else { + downObserver_->onComplete(); + downObserver_.reset(); + } + } + + void onError(folly::exception_wrapper ew) { + downObserver_->onError(std::move(ew)); + first_.reset(); + second_.reset(); + upObserver_.reset(); + downObserver_.reset(); + } + + private: + std::shared_ptr<Observer<T>> downObserver_; + std::shared_ptr<Observable<T>> first_; + std::shared_ptr<Observable<T>> second_; + std::shared_ptr<ForwardObserver> upObserver_; + }; + + class ForwardObserver : public yarpl::observable::Observer<T>, + public yarpl::observable::Subscription { + public: + ForwardObserver( + std::shared_ptr<ConcatWithSubscription> concatWithSubscription) + : concatWithSubscription_(std::move(concatWithSubscription)) {} + + void cancel() override { + if (auto subs = std::move(subscription_)) { + subs->cancel(); + } + } + + void onSubscribe(std::shared_ptr<Subscription> subscription) override { + // Don't forward the subscription to downstream observer + subscription_ = std::move(subscription); + } + + void onComplete() override { + concatWithSubscription_->onComplete(); + concatWithSubscription_.reset(); + } + + void onError(folly::exception_wrapper ew) override { + concatWithSubscription_->onError(std::move(ew)); + concatWithSubscription_.reset(); + } + + void onNext(T value) override { + concatWithSubscription_->onNext(std::move(value)); + } + + private: + std::shared_ptr<ConcatWithSubscription> concatWithSubscription_; + std::shared_ptr<observable::Subscription> subscription_; + }; + + private: + const std::shared_ptr<Observable<T>> first_; + const std::shared_ptr<Observable<T>> second_; +}; + +} // namespace details +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h new file mode 100644 index 000000000..66f655eaf --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h @@ -0,0 +1,159 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/observable/ObservableOperator.h" + +namespace yarpl { +namespace observable { +namespace details { + +template < + typename U, + typename OnSubscribeFunc, + typename OnNextFunc, + typename OnErrorFunc, + typename OnCompleteFunc, + typename OnCancelFunc> +class DoOperator : public ObservableOperator<U, U> { + using Super = ObservableOperator<U, U>; + static_assert( + std::is_same<std::decay_t<OnSubscribeFunc>, OnSubscribeFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnNextFunc>, OnNextFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnErrorFunc>, OnErrorFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnCompleteFunc>, OnCompleteFunc>::value, + "undecayed"); + static_assert( + std::is_same<std::decay_t<OnCancelFunc>, OnCancelFunc>::value, + "undecayed"); + + public: + template < + typename FSubscribe, + typename FNext, + typename FError, + typename FComplete, + typename FCancel> + DoOperator( + std::shared_ptr<Observable<U>> upstream, + FSubscribe&& onSubscribeFunc, + FNext&& onNextFunc, + FError&& onErrorFunc, + FComplete&& onCompleteFunc, + FCancel&& onCancelFunc) + : upstream_(std::move(upstream)), + onSubscribeFunc_(std::forward<FSubscribe>(onSubscribeFunc)), + onNextFunc_(std::forward<FNext>(onNextFunc)), + onErrorFunc_(std::forward<FError>(onErrorFunc)), + onCompleteFunc_(std::forward<FComplete>(onCompleteFunc)), + onCancelFunc_(std::forward<FCancel>(onCancelFunc)) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<U>> observer) override { + auto subscription = std::make_shared<DoSubscription>( + this->ref_from_this(this), std::move(observer)); + upstream_->subscribe( + // Note: implicit cast to a reference to a observer. + subscription); + return subscription; + } + + private: + class DoSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + DoSubscription( + std::shared_ptr<DoOperator> observable, + std::shared_ptr<Observer<U>> observer) + : SuperSub(std::move(observer)), observable_(std::move(observable)) {} + + void onSubscribe(std::shared_ptr<yarpl::observable::Subscription> + subscription) override { + observable_->onSubscribeFunc_(); + SuperSub::onSubscribe(std::move(subscription)); + } + + void onNext(U value) override { + const auto& valueRef = value; + observable_->onNextFunc_(valueRef); + SuperSub::observerOnNext(std::move(value)); + } + + void onError(folly::exception_wrapper ex) override { + const auto& exRef = ex; + observable_->onErrorFunc_(exRef); + SuperSub::onError(std::move(ex)); + } + + void onComplete() override { + observable_->onCompleteFunc_(); + SuperSub::onComplete(); + } + + void cancel() override { + observable_->onCancelFunc_(); + SuperSub::cancel(); + } + + private: + std::shared_ptr<DoOperator> observable_; + }; + + std::shared_ptr<Observable<U>> upstream_; + OnSubscribeFunc onSubscribeFunc_; + OnNextFunc onNextFunc_; + OnErrorFunc onErrorFunc_; + OnCompleteFunc onCompleteFunc_; + OnCancelFunc onCancelFunc_; +}; + +template < + typename U, + typename OnSubscribeFunc, + typename OnNextFunc, + typename OnErrorFunc, + typename OnCompleteFunc, + typename OnCancelFunc> +inline auto createDoOperator( + std::shared_ptr<Observable<U>> upstream, + OnSubscribeFunc&& onSubscribeFunc, + OnNextFunc&& onNextFunc, + OnErrorFunc&& onErrorFunc, + OnCompleteFunc&& onCompleteFunc, + OnCancelFunc&& onCancelFunc) { + return std::make_shared<DoOperator< + U, + std::decay_t<OnSubscribeFunc>, + std::decay_t<OnNextFunc>, + std::decay_t<OnErrorFunc>, + std::decay_t<OnCompleteFunc>, + std::decay_t<OnCancelFunc>>>( + std::move(upstream), + std::forward<OnSubscribeFunc>(onSubscribeFunc), + std::forward<OnNextFunc>(onNextFunc), + std::forward<OnErrorFunc>(onErrorFunc), + std::forward<OnCompleteFunc>(onCompleteFunc), + std::forward<OnCancelFunc>(onCancelFunc)); +} +} // namespace details +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableOperator.h b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableOperator.h new file mode 100644 index 000000000..451c6bd13 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableOperator.h @@ -0,0 +1,560 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <utility> + +#include <folly/functional/Invoke.h> + +#include "yarpl/Observable.h" +#include "yarpl/observable/Observer.h" +#include "yarpl/observable/Observable.h" + +namespace yarpl { +namespace observable { + +/** + * Base (helper) class for operators. Operators are templated on two types: + * D (downstream) and U (upstream). Operators are created by method calls on + * an upstream Observable, and are Observables themselves. Multi-stage + * pipelines + * can be built: a Observable heading a sequence of Operators. + */ +template <typename U, typename D> +class ObservableOperator : public Observable<D> { + protected: + /// An Operator's subscription. + /// + /// When a pipeline chain is active, each Observable has a corresponding + /// subscription. Except for the first one, the subscriptions are created + /// against Operators. Each operator subscription has two functions: as a + /// subscriber for the previous stage; as a subscription for the next one, + /// the user-supplied subscriber being the last of the pipeline stages. + class OperatorSubscription : public ::yarpl::observable::Subscription, + public Observer<U> { + protected: + explicit OperatorSubscription(std::shared_ptr<Observer<D>> observer) + : observer_(std::move(observer)) { + assert(observer_); + } + + void observerOnNext(D value) { + if (observer_) { + observer_->onNext(std::move(value)); + } + } + + /// Terminates both ends of an operator normally. + void terminate() { + terminateImpl(TerminateState::Both()); + } + + /// Terminates both ends of an operator with an error. + void terminateErr(folly::exception_wrapper ex) { + terminateImpl(TerminateState::Both(), std::move(ex)); + } + + // Subscription. + + void cancel() override { + Subscription::cancel(); + terminateImpl(TerminateState::Up()); + } + + // Observer. + + void onSubscribe(std::shared_ptr<yarpl::observable::Subscription> + subscription) override { + if (upstream_) { + DLOG(ERROR) << "attempt to subscribe twice"; + subscription->cancel(); + return; + } + upstream_ = std::move(subscription); + observer_->onSubscribe(this->ref_from_this(this)); + } + + void onComplete() override { + terminateImpl(TerminateState::Down()); + } + + void onError(folly::exception_wrapper ex) override { + terminateImpl(TerminateState::Down(), std::move(ex)); + } + + private: + struct TerminateState { + TerminateState(bool u, bool d) : up{u}, down{d} {} + + static TerminateState Down() { + return TerminateState{false, true}; + } + + static TerminateState Up() { + return TerminateState{true, false}; + } + + static TerminateState Both() { + return TerminateState{true, true}; + } + + const bool up{false}; + const bool down{false}; + }; + + bool isTerminated() const { + return !upstream_ && !observer_; + } + + /// Terminates an operator, sending cancel() and on{Complete,Error}() + /// signals as necessary. + void terminateImpl( + TerminateState state, + folly::exception_wrapper ex = folly::exception_wrapper{nullptr}) { + if (isTerminated()) { + return; + } + + if (auto upstream = std::move(upstream_)) { + if (state.up) { + upstream->cancel(); + } + } + + if (auto observer = std::move(observer_)) { + if (state.down) { + if (ex) { + observer->onError(std::move(ex)); + } else { + observer->onComplete(); + } + } + } + } + + /// This subscription controls the life-cycle of the observer. The + /// observer is retained as long as calls on it can be made. (Note: + /// the observer in turn maintains a reference on this subscription + /// object until cancellation and/or completion.) + std::shared_ptr<Observer<D>> observer_; + + /// In an active pipeline, cancel and (possibly modified) request(n) + /// calls should be forwarded upstream. Note that `this` is also a + /// observer for the upstream stage: thus, there are cycles; all of + /// the objects drop their references at cancel/complete. + // TODO(lehecka): this is extra field... base class has this member so + // remove it + std::shared_ptr<::yarpl::observable::Subscription> upstream_; + }; +}; + +template <typename U, typename D, typename F> +class MapOperator : public ObservableOperator<U, D> { + using Super = ObservableOperator<U, D>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(folly::is_invocable_r<D, F, U>::value, "not invocable"); + + public: + template <typename Func> + MapOperator(std::shared_ptr<Observable<U>> upstream, Func&& function) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<D>> observer) override { + auto subscription = std::make_shared<MapSubscription>( + this->ref_from_this(this), std::move(observer)); + upstream_->subscribe( + // Note: implicit cast to a reference to a observer. + subscription); + return subscription; + } + + private: + class MapSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + MapSubscription( + std::shared_ptr<MapOperator> observable, + std::shared_ptr<Observer<D>> observer) + : SuperSub(std::move(observer)), observable_(std::move(observable)) {} + + void onNext(U value) override { + try { + this->observerOnNext(observable_->function_(std::move(value))); + } catch (const std::exception& exn) { + folly::exception_wrapper ew{std::current_exception(), exn}; + this->terminateErr(std::move(ew)); + } + } + + private: + std::shared_ptr<MapOperator> observable_; + }; + + std::shared_ptr<Observable<U>> upstream_; + F function_; +}; + +template <typename U, typename F> +class FilterOperator : public ObservableOperator<U, U> { + using Super = ObservableOperator<U, U>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(folly::is_invocable_r<bool, F, U>::value, "not invocable"); + + public: + template <typename Func> + FilterOperator(std::shared_ptr<Observable<U>> upstream, Func&& function) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<U>> observer) override { + auto subscription = std::make_shared<FilterSubscription>( + this->ref_from_this(this), std::move(observer)); + upstream_->subscribe( + // Note: implicit cast to a reference to a observer. + subscription); + return subscription; + } + + private: + class FilterSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + FilterSubscription( + std::shared_ptr<FilterOperator> observable, + std::shared_ptr<Observer<U>> observer) + : SuperSub(std::move(observer)), observable_(std::move(observable)) {} + + void onNext(U value) override { + if (observable_->function_(value)) { + SuperSub::observerOnNext(std::move(value)); + } + } + + private: + std::shared_ptr<FilterOperator> observable_; + }; + + std::shared_ptr<Observable<U>> upstream_; + F function_; +}; + +template <typename U, typename D, typename F> +class ReduceOperator : public ObservableOperator<U, D> { + using Super = ObservableOperator<U, D>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(std::is_assignable<D&, U>::value, "not assignable"); + static_assert(folly::is_invocable_r<D, F, D, U>::value, "not invocable"); + + public: + template <typename Func> + ReduceOperator(std::shared_ptr<Observable<U>> upstream, Func&& function) + : upstream_(std::move(upstream)), + function_(std::forward<Func>(function)) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<D>> subscriber) override { + auto subscription = std::make_shared<ReduceSubscription>( + this->ref_from_this(this), std::move(subscriber)); + upstream_->subscribe( + // Note: implicit cast to a reference to a subscriber. + subscription); + return subscription; + } + + private: + class ReduceSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + ReduceSubscription( + std::shared_ptr<ReduceOperator> observable, + std::shared_ptr<Observer<D>> observer) + : SuperSub(std::move(observer)), + observable_(std::move(observable)), + accInitialized_(false) {} + + void onNext(U value) override { + if (accInitialized_) { + acc_ = observable_->function_(std::move(acc_), std::move(value)); + } else { + acc_ = std::move(value); + accInitialized_ = true; + } + } + + void onComplete() override { + if (accInitialized_) { + SuperSub::observerOnNext(std::move(acc_)); + } + SuperSub::onComplete(); + } + + private: + std::shared_ptr<ReduceOperator> observable_; + bool accInitialized_; + D acc_; + }; + + std::shared_ptr<Observable<U>> upstream_; + F function_; +}; + +template <typename T> +class TakeOperator : public ObservableOperator<T, T> { + using Super = ObservableOperator<T, T>; + + public: + TakeOperator(std::shared_ptr<Observable<T>> upstream, int64_t limit) + : upstream_(std::move(upstream)), limit_(limit) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = + std::make_shared<TakeSubscription>(limit_, std::move(observer)); + upstream_->subscribe(subscription); + return subscription; + } + + private: + class TakeSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + TakeSubscription(int64_t limit, std::shared_ptr<Observer<T>> observer) + : SuperSub(std::move(observer)), limit_(limit) {} + + void onSubscribe(std::shared_ptr<yarpl::observable::Subscription> + subscription) override { + SuperSub::onSubscribe(std::move(subscription)); + + if (limit_ <= 0) { + SuperSub::terminate(); + } + } + + void onNext(T value) override { + if (limit_-- > 0) { + SuperSub::observerOnNext(std::move(value)); + if (limit_ == 0) { + SuperSub::terminate(); + } + } + } + + private: + int64_t limit_; + }; + + std::shared_ptr<Observable<T>> upstream_; + const int64_t limit_; +}; + +template <typename T> +class SkipOperator : public ObservableOperator<T, T> { + using Super = ObservableOperator<T, T>; + + public: + SkipOperator(std::shared_ptr<Observable<T>> upstream, int64_t offset) + : upstream_(std::move(upstream)), offset_(offset) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = + std::make_shared<SkipSubscription>(offset_, std::move(observer)); + upstream_->subscribe(subscription); + return subscription; + } + + private: + class SkipSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + SkipSubscription(int64_t offset, std::shared_ptr<Observer<T>> observer) + : SuperSub(std::move(observer)), offset_(offset) {} + + void onNext(T value) override { + if (offset_ <= 0) { + SuperSub::observerOnNext(std::move(value)); + } else { + --offset_; + } + } + + private: + int64_t offset_; + }; + + std::shared_ptr<Observable<T>> upstream_; + const int64_t offset_; +}; + +template <typename T> +class IgnoreElementsOperator : public ObservableOperator<T, T> { + using Super = ObservableOperator<T, T>; + + public: + explicit IgnoreElementsOperator(std::shared_ptr<Observable<T>> upstream) + : upstream_(std::move(upstream)) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = + std::make_shared<IgnoreElementsSubscription>(std::move(observer)); + upstream_->subscribe(subscription); + return subscription; + } + + private: + class IgnoreElementsSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + IgnoreElementsSubscription(std::shared_ptr<Observer<T>> observer) + : SuperSub(std::move(observer)) {} + + void onNext(T) override {} + }; + + std::shared_ptr<Observable<T>> upstream_; +}; + +template <typename T> +class SubscribeOnOperator : public ObservableOperator<T, T> { + using Super = ObservableOperator<T, T>; + + public: + SubscribeOnOperator( + std::shared_ptr<Observable<T>> upstream, + folly::Executor& executor) + : upstream_(std::move(upstream)), executor_(executor) {} + + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = std::make_shared<SubscribeOnSubscription>( + executor_, std::move(observer)); + executor_.add([subscription, upstream = upstream_]() mutable { + upstream->subscribe(std::move(subscription)); + }); + return subscription; + } + + private: + class SubscribeOnSubscription : public Super::OperatorSubscription { + using SuperSub = typename Super::OperatorSubscription; + + public: + SubscribeOnSubscription( + folly::Executor& executor, + std::shared_ptr<Observer<T>> observer) + : SuperSub(std::move(observer)), executor_(executor) {} + + void cancel() override { + executor_.add([self = this->ref_from_this(this), this] { + this->callSuperCancel(); + }); + } + + void onNext(T value) override { + SuperSub::observerOnNext(std::move(value)); + } + + private: + // Trampoline to call superclass method; gcc bug 58972. + void callSuperCancel() { + SuperSub::cancel(); + } + + folly::Executor& executor_; + }; + + std::shared_ptr<Observable<T>> upstream_; + folly::Executor& executor_; +}; + +template <typename T, typename OnSubscribe> +class FromPublisherOperator : public Observable<T> { + static_assert( + std::is_same<std::decay_t<OnSubscribe>, OnSubscribe>::value, + "undecayed"); + + public: + template <typename F> + explicit FromPublisherOperator(F&& function) + : function_(std::forward<F>(function)) {} + + private: + class PublisherObserver : public Observer<T> { + public: + PublisherObserver( + std::shared_ptr<Observer<T>> inner, + std::shared_ptr<Subscription> subscription) + : inner_(std::move(inner)) { + Observer<T>::onSubscribe(std::move(subscription)); + } + + void onSubscribe(std::shared_ptr<Subscription>) override { + DLOG(ERROR) << "not allowed to call"; + CHECK(false); + } + + void onComplete() override { + if (auto inner = atomic_exchange(&inner_, nullptr)) { + inner->onComplete(); + } + Observer<T>::onComplete(); + } + + void onError(folly::exception_wrapper ex) override { + if (auto inner = atomic_exchange(&inner_, nullptr)) { + inner->onError(std::move(ex)); + } + Observer<T>::onError(folly::exception_wrapper()); + } + + void onNext(T t) override { + atomic_load(&inner_)->onNext(std::move(t)); + } + + private: + AtomicReference<Observer<T>> inner_; + }; + + public: + std::shared_ptr<Subscription> subscribe( + std::shared_ptr<Observer<T>> observer) override { + auto subscription = Subscription::create(); + observer->onSubscribe(subscription); + + if (!subscription->isCancelled()) { + function_(std::make_shared<PublisherObserver>( + std::move(observer), subscription), subscription); + } + return subscription; + } + + private: + OnSubscribe function_; +}; +} // namespace observable +} // namespace yarpl + +#include "yarpl/observable/ObservableConcatOperators.h" +#include "yarpl/observable/ObservableDoOperator.h" diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.cpp b/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.cpp new file mode 100644 index 000000000..6107938fe --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "yarpl/observable/Observables.h" + +namespace yarpl { +namespace observable { + +std::shared_ptr<Observable<int64_t>> Observable<>::range( + int64_t start, + int64_t count) { + auto lambda = [start, count](std::shared_ptr<Observer<int64_t>> observer) { + auto end = start + count; + for (int64_t i = start; i < end; ++i) { + observer->onNext(i); + } + observer->onComplete(); + }; + + return Observable<int64_t>::create(std::move(lambda)); +} +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.h b/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.h new file mode 100644 index 000000000..7c30c4bec --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Observables.h @@ -0,0 +1,57 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/functional/Invoke.h> +#include <limits> +#include "yarpl/observable/Observable.h" +#include "yarpl/observable/Subscription.h" + +namespace yarpl { +namespace observable { + +template <> +class Observable<void> { + public: + /** + * Emit the sequence of numbers [start, start + count). + */ + static std::shared_ptr<Observable<int64_t>> range( + int64_t start, + int64_t count); + + template <typename T> + static std::shared_ptr<Observable<T>> just(T&& value) { + return Observable<folly::remove_cvref_t<T>>::just(std::forward<T>(value)); + } + + template <typename T> + static std::shared_ptr<Observable<T>> justN(std::initializer_list<T> list) { + return Observable<folly::remove_cvref_t<T>>::justN(std::move(list)); + } + + // this will generate an observable which can be subscribed to only once + template <typename T> + static std::shared_ptr<Observable<T>> justOnce(T&& value) { + return Observable<folly::remove_cvref_t<T>>::justOnce( + std::forward<T>(value)); + } + + private: + Observable() = delete; +}; + +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Observer.h b/ios/Pods/Flipper-RSocket/yarpl/observable/Observer.h new file mode 100644 index 000000000..3e1e456b4 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Observer.h @@ -0,0 +1,226 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <folly/functional/Invoke.h> +#include <glog/logging.h> +#include "yarpl/Refcounted.h" +#include "yarpl/observable/Subscription.h" + +namespace yarpl { +namespace observable { + +template <typename T> +class Observer : public yarpl::enable_get_ref { + public: + // Note: If any of the following methods is overridden in a subclass, the new + // methods SHOULD ensure that these are invoked as well. + virtual void onSubscribe(std::shared_ptr<Subscription> subscription) { + DCHECK(subscription); + + if (subscription_) { + DLOG(ERROR) << "attempt to double subscribe"; + subscription->cancel(); + return; + } + + if (cancelled_) { + subscription->cancel(); + } + + subscription_ = std::move(subscription); + } + + // No further calls to the subscription after this method is invoked. + virtual void onComplete() { + DCHECK(subscription_) << "Calling onComplete() without a subscription"; + subscription_.reset(); + } + + // No further calls to the subscription after this method is invoked. + virtual void onError(folly::exception_wrapper) { + DCHECK(subscription_) << "Calling onError() without a subscription"; + subscription_.reset(); + } + + virtual void onNext(T) = 0; + + bool isUnsubscribed() const { + CHECK(subscription_); + return subscription_->isCancelled(); + } + + // Ability to add more subscription objects which will be notified when the + // subscription has been cancelled. + // Note that calling cancel on the tied subscription is not going to cancel + // this subscriber + void addSubscription(std::shared_ptr<Subscription> subscription) { + if (!subscription_) { + subscription->cancel(); + return; + } + subscription_->tieSubscription(std::move(subscription)); + } + + template <typename OnCancel> + void addSubscription(OnCancel onCancel) { + addSubscription(Subscription::create(std::move(onCancel))); + } + + bool isUnsubscribedOrTerminated() const { + return !subscription_ || subscription_->isCancelled(); + } + + protected: + void unsubscribe() { + if (subscription_) { + subscription_->cancel(); + } else { + cancelled_ = true; + } + } + + public: + template < + typename Next, + typename = + typename std::enable_if<folly::is_invocable<Next, T>::value>::type> + static std::shared_ptr<Observer<T>> create(Next next); + + template < + typename Next, + typename Error, + typename = + typename std::enable_if<folly::is_invocable<Next, T>::value>::type, + typename = typename std::enable_if< + folly::is_invocable<Error, folly::exception_wrapper>::value>::type> + static std::shared_ptr<Observer<T>> create(Next next, Error error); + + template < + typename Next, + typename Error, + typename Complete, + typename = + typename std::enable_if<folly::is_invocable<Next, T>::value>::type, + typename = typename std::enable_if< + folly::is_invocable<Error, folly::exception_wrapper>::value>::type, + typename = + typename std::enable_if<folly::is_invocable<Complete>::value>::type> + static std::shared_ptr<Observer<T>> + create(Next next, Error error, Complete complete); + + static std::shared_ptr<Observer<T>> create() { + class NullObserver : public Observer<T> { + public: + void onNext(T) {} + }; + return std::make_shared<NullObserver>(); + } + + private: + std::shared_ptr<Subscription> subscription_; + bool cancelled_{false}; +}; + +namespace details { + +template <typename T, typename Next> +class Base : public Observer<T> { + static_assert(std::is_same<std::decay_t<Next>, Next>::value, "undecayed"); + + public: + template <typename FNext> + explicit Base(FNext&& next) : next_(std::forward<FNext>(next)) {} + + void onNext(T value) override { + next_(std::move(value)); + } + + private: + Next next_; +}; + +template <typename T, typename Next, typename Error> +class WithError : public Base<T, Next> { + static_assert(std::is_same<std::decay_t<Error>, Error>::value, "undecayed"); + + public: + template <typename FNext, typename FError> + WithError(FNext&& next, FError&& error) + : Base<T, Next>(std::forward<FNext>(next)), + error_(std::forward<FError>(error)) {} + + void onError(folly::exception_wrapper error) override { + error_(std::move(error)); + } + + private: + Error error_; +}; + +template <typename T, typename Next, typename Error, typename Complete> +class WithErrorAndComplete : public WithError<T, Next, Error> { + static_assert( + std::is_same<std::decay_t<Complete>, Complete>::value, + "undecayed"); + + public: + template <typename FNext, typename FError, typename FComplete> + WithErrorAndComplete(FNext&& next, FError&& error, FComplete&& complete) + : WithError<T, Next, Error>( + std::forward<FNext>(next), + std::forward<FError>(error)), + complete_(std::move(complete)) {} + + void onComplete() override { + complete_(); + } + + private: + Complete complete_; +}; +} // namespace details + +template <typename T> +template <typename Next, typename> +std::shared_ptr<Observer<T>> Observer<T>::create(Next next) { + return std::make_shared<details::Base<T, Next>>(std::move(next)); +} + +template <typename T> +template <typename Next, typename Error, typename, typename> +std::shared_ptr<Observer<T>> Observer<T>::create(Next next, Error error) { + return std::make_shared<details::WithError<T, Next, Error>>( + std::move(next), std::move(error)); +} + +template <typename T> +template < + typename Next, + typename Error, + typename Complete, + typename, + typename, + typename> +std::shared_ptr<Observer<T>> +Observer<T>::create(Next next, Error error, Complete complete) { + return std::make_shared< + details::WithErrorAndComplete<T, Next, Error, Complete>>( + std::move(next), std::move(error), std::move(complete)); +} + +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.cpp b/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.cpp new file mode 100644 index 000000000..6a0abda1a --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.cpp @@ -0,0 +1,85 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "yarpl/observable/Subscription.h" +#include <glog/logging.h> +#include <atomic> +#include <iostream> + +namespace yarpl { +namespace observable { + +/** + * Implementation that allows checking if a Subscription is cancelled. + */ +void Subscription::cancel() { + cancelled_ = true; + // Lock must be obtained here and not in the range expression for it to + // apply to the loop body. + auto locked = tiedSubscriptions_.wlock(); + for (auto& subscription : *locked) { + subscription->cancel(); + } +} + +bool Subscription::isCancelled() const { + return cancelled_; +} + +void Subscription::tieSubscription(std::shared_ptr<Subscription> subscription) { + CHECK(subscription); + if (isCancelled()) { + subscription->cancel(); + } + tiedSubscriptions_.wlock()->push_back(std::move(subscription)); +} + +std::shared_ptr<Subscription> Subscription::create( + std::function<void()> onCancel) { + class CallbackSubscription : public Subscription { + public: + explicit CallbackSubscription(std::function<void()> onCancel) + : onCancel_(std::move(onCancel)) {} + + void cancel() override { + bool expected = false; + // mark cancelled 'true' and only if successful invoke 'onCancel()' + if (cancelled_.compare_exchange_strong(expected, true)) { + onCancel_(); + // Lock must be obtained here and not in the range expression for it to + // apply to the loop body. + auto locked = tiedSubscriptions_.wlock(); + for (auto& subscription : *locked) { + subscription->cancel(); + } + } + } + + private: + std::function<void()> onCancel_; + }; + return std::make_shared<CallbackSubscription>(std::move(onCancel)); +} + +std::shared_ptr<Subscription> Subscription::create( + std::atomic_bool& cancelled) { + return create([&cancelled]() { cancelled = true; }); +} + +std::shared_ptr<Subscription> Subscription::create() { + return std::make_shared<Subscription>(); +} + +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.h b/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.h new file mode 100644 index 000000000..38dc17792 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/Subscription.h @@ -0,0 +1,45 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Synchronized.h> +#include <vector> + +namespace yarpl { +namespace observable { + +class Subscription { + public: + virtual ~Subscription() = default; + virtual void cancel(); + bool isCancelled() const; + + // Adds ability to tie another subscription to this instance. + // Whenever *this subscription is cancelled then all tied subscriptions get + // cancelled as well + void tieSubscription(std::shared_ptr<Subscription> subscription); + + static std::shared_ptr<Subscription> create(std::function<void()> onCancel); + static std::shared_ptr<Subscription> create(std::atomic_bool& cancelled); + static std::shared_ptr<Subscription> create(); + + protected: + std::atomic<bool> cancelled_{false}; + folly::Synchronized<std::vector<std::shared_ptr<Subscription>>> + tiedSubscriptions_; +}; + +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/observable/TestObserver.h b/ios/Pods/Flipper-RSocket/yarpl/observable/TestObserver.h new file mode 100644 index 000000000..a4d290492 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/observable/TestObserver.h @@ -0,0 +1,258 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <condition_variable> +#include <mutex> +#include <sstream> +#include <vector> + +#include "yarpl/Observable.h" + +namespace yarpl { +namespace observable { + +/** + * A utility class for unit testing or experimenting with Observable. + * + * Example usage: + * + * auto observable = ... + * auto ts = std::make_shared<TestObserver<int>>(); + * observable->subscribe(ts->unique_observer()); + * ts->awaitTerminalEvent(); + * ts->assert... + * + * If you have an Observer impl with specific logic you want used, + * you can pass it into the TestObserver and the on* events will be + * delegated to your implementation. + * + * For example: + * + * auto ts = + * std::make_shared<TestObserver<int>>(std::make_unique<MyObserver>()); + * observable->subscribe(ts->unique_observer()); + * + * Now when 'observable' is subscribed to, the TestObserver behavior + * will be used, but 'MyObserver' on* methods will also be invoked. + * + * @tparam T + */ +template <typename T> +class TestObserver : public yarpl::observable::Observer<T>, + public std::enable_shared_from_this<TestObserver<T>> { + using Subscription = yarpl::observable::Subscription; + using Observer = yarpl::observable::Observer<T>; + + public: + TestObserver(); + explicit TestObserver(std::unique_ptr<Observer> delegate); + + void onSubscribe(std::shared_ptr<Subscription> s) override; + void onNext(T t) override; + void onComplete() override; + void onError(folly::exception_wrapper ex) override; + + /** + * Get a unique Observer<T> that can be passed into the Observable.subscribe + * method which requires a unique_ptr<Observer>. + * + * This decouples the lifetime of TestObserver from what is passed into + * the Observable.subscribe method so that the testing code can retain + * a reference to TestObserver to use it beyond the lifecycle + * of Observable.subscribe. + * + * @return + */ + std::unique_ptr<yarpl::observable::Observer<T>> unique_observer(); + + /** + * Block the current thread until either onComplete or onError is called. + */ + void awaitTerminalEvent( + std::chrono::milliseconds ms = std::chrono::seconds{1}); + + /** + * If the onNext values received does not match the given count, + * throw a runtime_error + */ + void assertValueCount(size_t count); + + /** + * The number of onNext values received. + * @return + */ + int64_t getValueCount(); + + /** + * Get a reference to a stored value at a given index position. + * + * The values are stored in the order received from onNext. + */ + T& getValueAt(size_t index); + + /** + * If the onError exception_wrapper points to an error containing + * the given msg, complete successfully, otherwise throw a runtime_error + */ + void assertOnErrorMessage(std::string msg); + + /** + * Submit Subscription->cancel(); + */ + void cancel(); + + bool isComplete() const { + return complete_; + } + + bool isError() const { + return error_; + } + + private: + std::unique_ptr<Observer> delegate_; + std::vector<T> values_; + folly::exception_wrapper e_; + bool terminated_{false}; + bool complete_{false}; + bool error_{false}; + std::mutex m_; + std::condition_variable terminalEventCV_; + std::shared_ptr<Subscription> subscription_; +}; + +template <typename T> +TestObserver<T>::TestObserver() : delegate_(nullptr){}; + +template <typename T> +TestObserver<T>::TestObserver(std::unique_ptr<Observer> delegate) + : delegate_(std::move(delegate)){}; + +template <typename T> +void TestObserver<T>::onSubscribe(std::shared_ptr<Subscription> s) { + subscription_ = s; + if (delegate_) { + delegate_->onSubscribe(s); + } +} + +template <typename T> +void TestObserver<T>::onNext(T t) { + if (delegate_) { + // std::cout << "TestObserver onNext& => copy then delegate" << + // std::endl; + values_.push_back(t); + delegate_->onNext(t); + } else { + // std::cout << "TestObserver onNext& => copy" << std::endl; + values_.push_back(t); + } +} + +template <typename T> +void TestObserver<T>::onComplete() { + if (delegate_) { + delegate_->onComplete(); + } + terminated_ = true; + complete_ = true; + terminalEventCV_.notify_all(); +} + +template <typename T> +void TestObserver<T>::onError(folly::exception_wrapper ex) { + if (delegate_) { + delegate_->onError(ex); + } + e_ = std::move(ex); + terminated_ = true; + error_ = true; + terminalEventCV_.notify_all(); +} + +template <typename T> +void TestObserver<T>::awaitTerminalEvent(std::chrono::milliseconds ms) { + // now block this thread + std::unique_lock<std::mutex> lk(m_); + // if shutdown gets implemented this would then be released by it + if (!terminalEventCV_.wait_for(lk, ms, [this] { return terminated_; })) { + throw std::runtime_error("timeout in awaitTerminalEvent"); + } +} + +template <typename T> +void TestObserver<T>::cancel() { + subscription_->cancel(); +} + +template <typename T> +std::unique_ptr<yarpl::observable::Observer<T>> +TestObserver<T>::unique_observer() { + class UObserver : public yarpl::observable::Observer<T> { + public: + UObserver(std::shared_ptr<TestObserver<T>> ts) : ts_(std::move(ts)) {} + + void onSubscribe(yarpl::observable::Subscription* s) override { + ts_->onSubscribe(s); + } + + void onNext(const T& t) override { + ts_->onNext(t); + } + + void onError(folly::exception_wrapper e) override { + ts_->onError(std::move(e)); + } + + void onComplete() override { + ts_->onComplete(); + } + + private: + std::shared_ptr<TestObserver<T>> ts_; + }; + + return std::make_unique<UObserver>(this->shared_from_this()); +} + +template <typename T> +void TestObserver<T>::assertValueCount(size_t count) { + if (values_.size() != count) { + std::stringstream ss; + ss << "Value count " << values_.size() << " does not match " << count; + throw std::runtime_error(ss.str()); + } +} +template <typename T> +int64_t TestObserver<T>::getValueCount() { + return values_.size(); +} + +template <typename T> +T& TestObserver<T>::getValueAt(size_t index) { + return values_[index]; +} + +template <typename T> +void TestObserver<T>::assertOnErrorMessage(std::string msg) { + if (!e_ || e_.get_exception()->what() != msg) { + std::stringstream ss; + ss << "Error is: " << e_ << " but expected: " << msg; + throw std::runtime_error(ss.str()); + } +} +} // namespace observable +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/Single.h b/ios/Pods/Flipper-RSocket/yarpl/single/Single.h new file mode 100644 index 000000000..1355e30ba --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/Single.h @@ -0,0 +1,175 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/functional/Invoke.h> +#include <folly/synchronization/Baton.h> + +#include "yarpl/Refcounted.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleObservers.h" +#include "yarpl/single/SingleSubscription.h" + +namespace yarpl { +namespace single { + +template <typename T> +class Single : public yarpl::enable_get_ref { + public: + virtual ~Single() = default; + + virtual void subscribe(std::shared_ptr<SingleObserver<T>>) = 0; + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Success, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Success>&, T>::value>::type> + void subscribe(Success&& next) { + subscribe(SingleObservers::create<T>(std::forward<Success>(next))); + } + + /** + * Subscribe overload that accepts lambdas. + */ + template < + typename Success, + typename Error, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Success>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type> + void subscribe(Success next, Error error) { + subscribe(SingleObservers::create<T>( + std::forward<Success>(next), std::forward<Error>(error))); + } + + /** + * Blocking subscribe that accepts lambdas. + * + * This blocks the current thread waiting on the response. + */ + template < + typename Success, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Success>&, T>::value>::type> + void subscribeBlocking(Success&& next) { + auto waiting_ = std::make_shared<folly::Baton<>>(); + subscribe( + SingleObservers::create<T>([next = std::forward(next), waiting_](T t) { + next(std::move(t)); + waiting_->post(); + })); + // TODO get errors and throw if one is received + waiting_->wait(); + } + + template < + typename OnSubscribe, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<OnSubscribe>&, + std::shared_ptr<SingleObserver<T>>>::value>::type> + static std::shared_ptr<Single<T>> create(OnSubscribe&&); + + template <typename Function> + auto map(Function&& function); +}; + +template <> +class Single<void> { + public: + virtual ~Single() = default; + + virtual void subscribe(std::shared_ptr<SingleObserverBase<void>>) = 0; + + /** + * Subscribe overload taking lambda for onSuccess that is called upon writing + * to the network. + */ + template < + typename Success, + typename = typename std::enable_if< + folly::is_invocable<std::decay_t<Success>&>::value>::type> + void subscribe(Success&& s) { + class SuccessSingleObserver : public SingleObserverBase<void> { + public: + explicit SuccessSingleObserver(Success&& success) + : success_{std::forward<Success>(success)} {} + + void onSubscribe( + std::shared_ptr<SingleSubscription> subscription) override { + SingleObserverBase<void>::onSubscribe(std::move(subscription)); + } + + void onSuccess() override { + success_(); + SingleObserverBase<void>::onSuccess(); + } + + // No further calls to the subscription after this method is invoked. + void onError(folly::exception_wrapper ex) override { + SingleObserverBase<void>::onError(std::move(ex)); + } + + private: + std::decay_t<Success> success_; + }; + + subscribe( + std::make_shared<SuccessSingleObserver>(std::forward<Success>(s))); + } + + template < + typename OnSubscribe, + typename = typename std::enable_if<folly::is_invocable< + std::decay_t<OnSubscribe>&, + std::shared_ptr<SingleObserverBase<void>>>::value>::type> + static auto create(OnSubscribe&&); +}; + +} // namespace single +} // namespace yarpl + +#include "yarpl/single/SingleOperator.h" + +namespace yarpl { +namespace single { + +template <typename T> +template <typename OnSubscribe, typename> +std::shared_ptr<Single<T>> Single<T>::create(OnSubscribe&& function) { + return std::make_shared<FromPublisherOperator<T, std::decay_t<OnSubscribe>>>( + std::forward<OnSubscribe>(function)); +} + +template <typename OnSubscribe, typename> +auto Single<void>::create(OnSubscribe&& function) { + return std::make_shared< + SingleVoidFromPublisherOperator<std::decay_t<OnSubscribe>>>( + std::forward<OnSubscribe>(function)); +} + +template <typename T> +template <typename Function> +auto Single<T>::map(Function&& function) { + using D = typename folly::invoke_result_t<Function, T>; + return std::make_shared<MapOperator<T, D, std::decay_t<Function>>>( + this->ref_from_this(this), std::forward<Function>(function)); +} + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleObserver.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleObserver.h new file mode 100644 index 000000000..8c74337c5 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleObserver.h @@ -0,0 +1,171 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/ExceptionWrapper.h> +#include <glog/logging.h> +#include "yarpl/single/SingleSubscription.h" + +namespace yarpl { +namespace single { + +template <typename T> +class SingleObserver { + public: + virtual ~SingleObserver() = default; + virtual void onSubscribe(std::shared_ptr<SingleSubscription>) = 0; + virtual void onSuccess(T) = 0; + virtual void onError(folly::exception_wrapper) = 0; + + template <typename Success> + static std::shared_ptr<SingleObserver<T>> create(Success&& success); + + template <typename Success, typename Error> + static std::shared_ptr<SingleObserver<T>> create( + Success&& success, + Error&& error); +}; + +template <typename T> +class SingleObserverBase : public SingleObserver<T> { + public: + // Note: If any of the following methods is overridden in a subclass, the new + // methods SHOULD ensure that these are invoked as well. + void onSubscribe(std::shared_ptr<SingleSubscription> subscription) override { + DCHECK(subscription); + + if (subscription_) { + subscription->cancel(); + return; + } + + subscription_ = std::move(subscription); + } + + void onSuccess(T) override { + DCHECK(subscription_) << "Calling onSuccess() without a subscription"; + subscription_.reset(); + } + + // No further calls to the subscription after this method is invoked. + void onError(folly::exception_wrapper) override { + DCHECK(subscription_) << "Calling onError() without a subscription"; + subscription_.reset(); + } + + protected: + SingleSubscription* subscription() { + return subscription_.operator->(); + } + + private: + std::shared_ptr<SingleSubscription> subscription_; +}; + +/// Specialization of SingleObserverBase<void>. +template <> +class SingleObserverBase<void> { + public: + virtual ~SingleObserverBase() = default; + + // Note: If any of the following methods is overridden in a subclass, the new + // methods SHOULD ensure that these are invoked as well. + virtual void onSubscribe(std::shared_ptr<SingleSubscription> subscription) { + DCHECK(subscription); + + if (subscription_) { + subscription->cancel(); + return; + } + + subscription_ = std::move(subscription); + } + + virtual void onSuccess() { + DCHECK(subscription_) << "Calling onSuccess() without a subscription"; + subscription_.reset(); + } + + // No further calls to the subscription after this method is invoked. + virtual void onError(folly::exception_wrapper) { + DCHECK(subscription_) << "Calling onError() without a subscription"; + subscription_.reset(); + } + + protected: + SingleSubscription* subscription() { + return subscription_.operator->(); + } + + private: + std::shared_ptr<SingleSubscription> subscription_; +}; + +template <typename T, typename Success, typename Error> +class SimpleSingleObserver : public SingleObserver<T> { + public: + SimpleSingleObserver(Success success, Error error) + : success_(std::move(success)), error_(std::move(error)) {} + + void onSubscribe(std::shared_ptr<SingleSubscription>) { + // throw away the subscription + } + + void onSuccess(T value) override { + success_(std::move(value)); + } + + void onError(folly::exception_wrapper ew) { + error_(std::move(ew)); + } + + Success success_; + Error error_; +}; + +template <typename T> +template <typename Success> +std::shared_ptr<SingleObserver<T>> SingleObserver<T>::create( + Success&& success) { + static_assert( + folly::is_invocable<Success, T>::value, + "Input `success` should be invocable with a parameter of `T`."); + return std::make_shared<SimpleSingleObserver< + T, + std::decay_t<Success>, + folly::Function<void(folly::exception_wrapper)>>>( + std::forward<Success>(success), [](folly::exception_wrapper) {}); +} + +template <typename T> +template <typename Success, typename Error> +std::shared_ptr<SingleObserver<T>> SingleObserver<T>::create( + Success&& success, + Error&& error) { + static_assert( + folly::is_invocable<Success, T>::value, + "Input `success` should be invocable with a parameter of `T`."); + static_assert( + folly::is_invocable<Error, folly::exception_wrapper>::value, + "Input `error` should be invocable with a parameter of " + "`folly::exception_wrapper`."); + + return std::make_shared< + SimpleSingleObserver<T, std::decay_t<Success>, std::decay_t<Error>>>( + std::forward<Success>(success), std::forward<Error>(error)); +} + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleObservers.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleObservers.h new file mode 100644 index 000000000..118b25fa9 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleObservers.h @@ -0,0 +1,107 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/single/SingleObserver.h" + +#include <folly/functional/Invoke.h> + +namespace yarpl { +namespace single { + +/// Helper methods for constructing subscriber instances from functions: +/// one or two functions (callables; can be lamda, for instance) +/// may be specified, corresponding to onNext, onError and onComplete +/// method bodies in the subscriber. +class SingleObservers { + private: + /// Defined if Success and Error are signature-compatible with + /// onSuccess and onError subscriber methods respectively. + template < + typename T, + typename Success, + typename Error = void (*)(folly::exception_wrapper)> + using EnableIfCompatible = typename std::enable_if< + folly::is_invocable<std::decay_t<Success>&, T>::value && + folly::is_invocable<std::decay_t<Error>&, folly::exception_wrapper>:: + value>::type; + + public: + template <typename T, typename Next, typename = EnableIfCompatible<T, Next>> + static auto create(Next&& next) { + return std::make_shared<Base<T, std::decay_t<Next>>>( + std::forward<Next>(next)); + } + + template < + typename T, + typename Success, + typename Error, + typename = EnableIfCompatible<T, Success, Error>> + static auto create(Success&& next, Error&& error) { + return std::make_shared< + WithError<T, std::decay_t<Success>, std::decay_t<Error>>>( + std::forward<Success>(next), std::forward<Error>(error)); + } + + template <typename T> + static auto create() { + return std::make_shared<SingleObserverBase<T>>(); + } + + private: + template <typename T, typename Next> + class Base : public SingleObserverBase<T> { + static_assert(std::is_same<std::decay_t<Next>, Next>::value, "undecayed"); + + public: + template <typename FNext> + explicit Base(FNext&& next) : next_(std::forward<FNext>(next)) {} + + void onSuccess(T value) override { + next_(std::move(value)); + // TODO how do we call the super to trigger release? + // SingleObserver<T>::onSuccess(value); + } + + private: + Next next_; + }; + + template <typename T, typename Success, typename Error> + class WithError : public Base<T, Success> { + static_assert(std::is_same<std::decay_t<Error>, Error>::value, "undecayed"); + + public: + template <typename FSuccess, typename FError> + WithError(FSuccess&& success, FError&& error) + : Base<T, Success>(std::forward<FSuccess>(success)), + error_(std::forward<FError>(error)) {} + + void onError(folly::exception_wrapper error) override { + error_(error); + // TODO do we call the super here to trigger release? + Base<T, Success>::onError(std::move(error)); + } + + private: + Error error_; + }; + + SingleObservers() = delete; +}; + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleOperator.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleOperator.h new file mode 100644 index 000000000..0b3e7392e --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleOperator.h @@ -0,0 +1,248 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <folly/Try.h> +#include <folly/functional/Invoke.h> + +#include <utility> + +#include "yarpl/single/Single.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleSubscriptions.h" + +namespace yarpl { +namespace single { +/** + * Base (helper) class for operators. Operators are templated on two types: + * D (downstream) and U (upstream). Operators are created by method calls on + * an upstream Single, and are Observables themselves. Multi-stage + * pipelines + * can be built: a Single heading a sequence of Operators. + */ +template <typename U, typename D> +class SingleOperator : public Single<D> { + public: + explicit SingleOperator(std::shared_ptr<Single<U>> upstream) + : upstream_(std::move(upstream)) {} + + protected: + /// + /// \brief An Operator's subscription. + /// + /// When a pipeline chain is active, each Single has a corresponding + /// subscription. Except for the first one, the subscriptions are created + /// against Operators. Each operator subscription has two functions: as a + /// observer for the previous stage; as a subscription for the next one, + /// the user-supplied observer being the last of the pipeline stages. + template <typename Operator> + class Subscription : public ::yarpl::single::SingleSubscription, + public SingleObserver<U>, + public yarpl::enable_get_ref { + protected: + Subscription( + std::shared_ptr<Operator> single, + std::shared_ptr<SingleObserver<D>> observer) + : single_(std::move(single)), observer_(std::move(observer)) {} + + ~Subscription() { + observer_.reset(); + } + + void observerOnSuccess(D value) { + terminateImpl(TerminateState::Down(), folly::Try<D>{std::move(value)}); + } + + void observerOnError(folly::exception_wrapper ew) { + terminateImpl(TerminateState::Down(), folly::Try<D>{std::move(ew)}); + } + + std::shared_ptr<Operator> getOperator() { + return single_; + } + + void terminateErr(folly::exception_wrapper ew) { + terminateImpl(TerminateState::Both(), std::move(ew)); + } + + // SingleSubscription. + + void cancel() override { + terminateImpl(TerminateState::Up(), folly::Try<D>{}); + } + + // Subscriber. + + void onSubscribe(std::shared_ptr<yarpl::single::SingleSubscription> + subscription) override { + upstream_ = std::move(subscription); + observer_->onSubscribe(this->ref_from_this(this)); + } + + void onError(folly::exception_wrapper ew) override { + terminateImpl(TerminateState::Down(), folly::Try<D>{std::move(ew)}); + } + + private: + struct TerminateState { + TerminateState(bool u, bool d) : up{u}, down{d} {} + + static TerminateState Down() { + return TerminateState{false, true}; + } + + static TerminateState Up() { + return TerminateState{true, false}; + } + + static TerminateState Both() { + return TerminateState{true, true}; + } + + const bool up{false}; + const bool down{false}; + }; + + bool isTerminated() const { + return !upstream_ && !observer_; + } + + void terminateImpl(TerminateState state, folly::Try<D> maybe) { + if (isTerminated()) { + return; + } + + if (auto upstream = std::move(upstream_)) { + if (state.up) { + upstream->cancel(); + } + } + + if (auto observer = std::move(observer_)) { + if (state.down) { + if (maybe.hasValue()) { + observer->onSuccess(std::move(maybe).value()); + } else { + observer->onError(std::move(maybe).exception()); + } + } + } + } + + /// The Single has the lambda, and other creation parameters. + std::shared_ptr<Operator> single_; + + /// This subscription controls the life-cycle of the observer. The + /// observer is retained as long as calls on it can be made. (Note: + /// the observer in turn maintains a reference on this subscription + /// object until cancellation and/or completion.) + std::shared_ptr<SingleObserver<D>> observer_; + + /// In an active pipeline, cancel and (possibly modified) request(n) + /// calls should be forwarded upstream. Note that `this` is also a + /// observer for the upstream stage: thus, there are cycles; all of + /// the objects drop their references at cancel/complete. + std::shared_ptr<yarpl::single::SingleSubscription> upstream_; + }; + + std::shared_ptr<Single<U>> upstream_; +}; + +template < + typename U, + typename D, + typename F> +class MapOperator : public SingleOperator<U, D> { + using ThisOperatorT = MapOperator<U, D, F>; + using Super = SingleOperator<U, D>; + using OperatorSubscription = + typename Super::template Subscription<ThisOperatorT>; + static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed"); + static_assert(folly::is_invocable_r<D, F, U>::value, "not invocable"); + + public: + template <typename Func> + MapOperator(std::shared_ptr<Single<U>> upstream, Func&& function) + : Super(std::move(upstream)), function_(std::forward<Func>(function)) {} + + void subscribe(std::shared_ptr<SingleObserver<D>> observer) override { + Super::upstream_->subscribe( + // Note: implicit cast to a reference to a observer. + std::make_shared<MapSubscription>( + this->ref_from_this(this), std::move(observer))); + } + + private: + class MapSubscription : public OperatorSubscription { + public: + MapSubscription( + std::shared_ptr<ThisOperatorT> single, + std::shared_ptr<SingleObserver<D>> observer) + : OperatorSubscription(std::move(single), std::move(observer)) {} + + void onSuccess(U value) override { + try { + auto map_operator = this->getOperator(); + this->observerOnSuccess(map_operator->function_(std::move(value))); + } catch (const std::exception& exn) { + folly::exception_wrapper ew{std::current_exception(), exn}; + this->observerOnError(std::move(ew)); + } + } + }; + + F function_; +}; + +template <typename T, typename OnSubscribe> +class FromPublisherOperator : public Single<T> { + static_assert( + std::is_same<std::decay_t<OnSubscribe>, OnSubscribe>::value, + "undecayed"); + + public: + template <typename F> + explicit FromPublisherOperator(F&& function) + : function_(std::forward<F>(function)) {} + + void subscribe(std::shared_ptr<SingleObserver<T>> observer) override { + function_(std::move(observer)); + } + + private: + OnSubscribe function_; +}; + +template <typename OnSubscribe> +class SingleVoidFromPublisherOperator : public Single<void> { + static_assert( + std::is_same<std::decay_t<OnSubscribe>, OnSubscribe>::value, + "undecayed"); + + public: + template <typename F> + explicit SingleVoidFromPublisherOperator(F&& function) + : function_(std::forward<F>(function)) {} + + void subscribe(std::shared_ptr<SingleObserverBase<void>> observer) override { + function_(std::move(observer)); + } + + private: + OnSubscribe function_; +}; + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscription.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscription.h new file mode 100644 index 000000000..ef898c1ba --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscription.h @@ -0,0 +1,32 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/Refcounted.h" + +namespace yarpl { +namespace single { + +class SingleSubscription { + public: + virtual ~SingleSubscription() = default; + virtual void cancel() = 0; + + protected: + SingleSubscription() {} +}; + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscriptions.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscriptions.h new file mode 100644 index 000000000..9ebfe4498 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleSubscriptions.h @@ -0,0 +1,140 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <atomic> +#include <functional> +#include <mutex> + +#include "yarpl/Refcounted.h" +#include "yarpl/single/SingleSubscription.h" + +namespace yarpl { +namespace single { + +/** + * Implementation that allows checking if a Subscription is cancelled. + */ +class AtomicBoolSingleSubscription : public SingleSubscription { + public: + void cancel() override { + cancelled_ = true; + } + bool isCancelled() const { + return cancelled_; + } + + private: + std::atomic_bool cancelled_{false}; +}; + +/** + * Implementation that gets a callback when cancellation occurs. + */ +class CallbackSingleSubscription : public SingleSubscription { + public: + explicit CallbackSingleSubscription(std::function<void()> onCancel) + : onCancel_(std::move(onCancel)) {} + void cancel() override { + bool expected = false; + // mark cancelled 'true' and only if successful invoke 'onCancel()' + if (cancelled_.compare_exchange_strong(expected, true)) { + onCancel_(); + } + } + bool isCancelled() const { + return cancelled_; + } + + private: + std::atomic_bool cancelled_{false}; + std::function<void()> onCancel_; +}; + +/** + * Implementation that can be cancelled with or without + * a delegate, and when the delegate exists (before or after cancel) + * it will be cancelled in a thread-safe manner. + */ +class DelegateSingleSubscription : public SingleSubscription { + public: + explicit DelegateSingleSubscription() {} + void cancel() override { + bool shouldCancelDelegate = false; + { + std::lock_guard<std::mutex> g(m_); + cancelled_ = true; + if (delegate_) { + shouldCancelDelegate = true; + } + } + // cancel without holding lock + if (shouldCancelDelegate) { + delegate_->cancel(); + } + } + bool isCancelled() const { + std::lock_guard<std::mutex> g(m_); + return cancelled_; + } + /** + * This can be called once. + */ + void setDelegate(std::shared_ptr<SingleSubscription> d) { + bool shouldCancelDelegate = false; + { + std::lock_guard<std::mutex> g(m_); + if (delegate_) { + throw std::runtime_error("Delegate already set. Only one permitted."); + } + delegate_ = std::move(d); + if (cancelled_) { + shouldCancelDelegate = true; + } + } + // cancel without holding lock + if (shouldCancelDelegate) { + delegate_->cancel(); + } + } + + private: + // all must be protected by a mutex + mutable std::mutex m_; + bool cancelled_{false}; + std::shared_ptr<SingleSubscription> delegate_; +}; + +class SingleSubscriptions { + public: + static std::shared_ptr<CallbackSingleSubscription> create( + std::function<void()> onCancel) { + return std::make_shared<CallbackSingleSubscription>(std::move(onCancel)); + } + static std::shared_ptr<CallbackSingleSubscription> create( + std::atomic_bool& cancelled) { + return create([&cancelled]() { cancelled = true; }); + } + static std::shared_ptr<SingleSubscription> empty() { + return std::make_shared<AtomicBoolSingleSubscription>(); + } + static std::shared_ptr<AtomicBoolSingleSubscription> + atomicBoolSubscription() { + return std::make_shared<AtomicBoolSingleSubscription>(); + } +}; + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/SingleTestObserver.h b/ios/Pods/Flipper-RSocket/yarpl/single/SingleTestObserver.h new file mode 100644 index 000000000..2557f5d10 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/SingleTestObserver.h @@ -0,0 +1,239 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <atomic> +#include <condition_variable> +#include <mutex> +#include <sstream> +#include <vector> + +#include "yarpl/single/Single.h" +#include "yarpl/single/SingleObserver.h" +#include "yarpl/single/SingleSubscriptions.h" + +namespace yarpl { +namespace single { + +/** + * A utility class for unit testing or experimenting with Single. + * + * Example usage: + * + * auto single = ... + * auto to = SingleTestObserver<int>::create(); + * single->subscribe(to); + * ts->awaitTerminalEvent(); + * ts->assert... + * + * If you have a SingleObserver impl with specific logic you want used, + * you can pass it into the SingleTestObserver and the on* events will be + * delegated to your implementation. + * + * For example: + * + * auto to = SingleTestObserver<int>::create(std::make_shared<MyObserver>()); + * single->subscribe(to); + * + * Now when 'single' is subscribed to, the SingleTestObserver behavior + * will be used, but 'MyObserver' on* methods will also be invoked. + * + * @tparam T + */ +template <typename T> +class SingleTestObserver : public yarpl::single::SingleObserver<T> { + public: + /** + * Create a SingleTestObserver that will subscribe and store the value it + * receives. + * + * @return + */ + static std::shared_ptr<SingleTestObserver<T>> create() { + return std::make_shared<SingleTestObserver<T>>(); + } + + /** + * Create a SingleTestObserver that will delegate all on* method calls + * to the provided SingleObserver. + * + * This will store the value it receives to allow assertions. + * @return + */ + static std::shared_ptr<SingleTestObserver<T>> create( + std::shared_ptr<SingleObserver<T>> delegate) { + return std::make_shared<SingleTestObserver<T>>(std::move(delegate)); + } + + SingleTestObserver() : delegate_(nullptr) {} + + // Note on thread safety => + // Generally an observer assumes single threaded emission + // but this class is intended for use in unit tests + // when it will generally receive events on one thread + // and then access them for verification/assertion + // on the unit test main thread. + + explicit SingleTestObserver(std::shared_ptr<SingleObserver<T>> delegate) + : delegate_(std::move(delegate)) {} + + void onSubscribe(std::shared_ptr<SingleSubscription> subscription) override { + if (delegate_) { + delegateSubscription_->setDelegate(subscription); // copy + delegate_->onSubscribe(std::move(subscription)); + } else { + delegateSubscription_->setDelegate(std::move(subscription)); + } + } + + void onSuccess(T t) override { + { + // take lock with local scope so we can emit without holding the lock + std::lock_guard<std::mutex> g(m_); + if (delegate_) { + value_ = t; // take copy + // do not emit here, but later without lock + } else { + value_ = std::move(t); + } + delegateSubscription_ = nullptr; + terminated_ = true; + } + // after lock is released we emit + if (delegate_) { + // Do NOT hold the mutex while emitting + delegate_->onSuccess(std::move(t)); + } + // then we notify that we're completed + terminalEventCV_.notify_all(); + } + + void onError(folly::exception_wrapper ex) override { + if (delegate_) { + // Do NOT hold the mutex while emitting + delegate_->onError(ex); + } + { + std::lock_guard<std::mutex> g(m_); + e_ = std::move(ex); + terminated_ = true; + } + terminalEventCV_.notify_all(); + } + + /** + * Block the current thread until either onSuccess or onError is called. + */ + void awaitTerminalEvent() { + // now block this thread + std::unique_lock<std::mutex> lk(m_); + // if shutdown gets implemented this would then be released by it + terminalEventCV_.wait(lk, [this] { return terminated_; }); + } + + /** + * Assert no onSuccess or onError events were received + */ + void assertNoTerminalEvent() { + std::lock_guard<std::mutex> g(m_); + if (terminated_) { + throw std::runtime_error("An unexpected terminal event was received."); + } + } + /** + * If an onSuccess call was not received throw a runtime_error + */ + void assertSuccess() { + std::lock_guard<std::mutex> g(m_); + if (!terminated_) { + throw std::runtime_error("Did not receive terminal event."); + } + if (e_) { + std::stringstream ss; + ss << "Received onError instead of onSuccess"; + ss << " (error was " << e_ << ")"; + throw std::runtime_error(ss.str()); + } + } + + void assertOnSuccessValue(T t) { + assertSuccess(); + std::lock_guard<std::mutex> g(m_); + if (value_ != t) { + std::stringstream ss; + ss << "value == " << value_ << ", but expected " << t; + throw std::runtime_error(ss.str()); + } + } + + /** + * Get a reference to the received value if onSuccess was called. + */ + T& getOnSuccessValue() { + std::lock_guard<std::mutex> g(m_); + return value_; + } + + /** + * Get the error received from onError if it was called. + */ + folly::exception_wrapper getError() { + std::lock_guard<std::mutex> g(m_); + if (!terminated_) { + throw std::logic_error{"Must call getError() on a terminated observer"}; + } + return e_; + } + + /** + * If the onError exception_wrapper points to an error containing + * the given msg, complete successfully, otherwise throw a runtime_error + */ + void assertOnErrorMessage(std::string msg) { + std::lock_guard<std::mutex> g(m_); + if (!e_ || e_.get_exception()->what() != msg) { + std::stringstream ss; + ss << "Error is: " << e_ << " but expected: " << msg; + throw std::runtime_error(ss.str()); + } + } + + folly::exception_wrapper getException() const { + return e_; + } + + /** + * Submit SingleSubscription->cancel(); + */ + void cancel() { + // do NOT hold a lock while invoking the normal signals + delegateSubscription_->cancel(); + } + + private: + std::mutex m_; + std::condition_variable terminalEventCV_; + std::shared_ptr<SingleObserver<T>> delegate_; + // The following variables must be protected by mutex m_ + T value_; + folly::exception_wrapper e_; + bool terminated_{false}; + // allows thread-safe cancellation against a delegate + // regardless of when it is received + std::shared_ptr<DelegateSingleSubscription> delegateSubscription_{ + std::make_shared<DelegateSingleSubscription>()}; +}; +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/single/Singles.h b/ios/Pods/Flipper-RSocket/yarpl/single/Singles.h new file mode 100644 index 000000000..b6fe896cb --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/single/Singles.h @@ -0,0 +1,83 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include "yarpl/single/Single.h" +#include "yarpl/single/SingleSubscriptions.h" + +#include <folly/functional/Invoke.h> + +namespace yarpl { +namespace single { + +class Singles { + public: + template <typename T> + static std::shared_ptr<Single<T>> just(const T& value) { + auto lambda = [value](std::shared_ptr<SingleObserver<T>> observer) { + observer->onSubscribe(SingleSubscriptions::empty()); + observer->onSuccess(value); + }; + + return Single<T>::create(std::move(lambda)); + } + + template < + typename T, + typename OnSubscribe, + typename = typename std::enable_if<folly::is_invocable< + OnSubscribe&&, + std::shared_ptr<SingleObserver<T>>>::value>::type> + static std::shared_ptr<Single<T>> create(OnSubscribe&& function) { + return std::make_shared< + FromPublisherOperator<T, std::decay_t<OnSubscribe>>>( + std::forward<OnSubscribe>(function)); + } + + template <typename T> + static std::shared_ptr<Single<T>> error(folly::exception_wrapper ex) { + auto lambda = + [e = std::move(ex)](std::shared_ptr<SingleObserver<T>> observer) { + observer->onSubscribe(SingleSubscriptions::empty()); + observer->onError(e); + }; + return Single<T>::create(std::move(lambda)); + } + + template <typename T, typename ExceptionType> + static std::shared_ptr<Single<T>> error(const ExceptionType& ex) { + auto lambda = [ex](std::shared_ptr<SingleObserver<T>> observer) { + observer->onSubscribe(SingleSubscriptions::empty()); + observer->onError(ex); + }; + return Single<T>::create(std::move(lambda)); + } + + template <typename T, typename TGenerator> + static std::shared_ptr<Single<T>> fromGenerator(TGenerator&& generator) { + auto lambda = [generator = std::forward<TGenerator>(generator)]( + std::shared_ptr<SingleObserver<T>> observer) mutable { + observer->onSubscribe(SingleSubscriptions::empty()); + observer->onSuccess(generator()); + }; + return Single<T>::create(std::move(lambda)); + } + + private: + Singles() = delete; +}; + +} // namespace single +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/test_utils/Mocks.h b/ios/Pods/Flipper-RSocket/yarpl/test_utils/Mocks.h new file mode 100644 index 000000000..8662fcdf1 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/test_utils/Mocks.h @@ -0,0 +1,170 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <cassert> +#include <chrono> +#include <condition_variable> +#include <exception> + +#include <folly/ExceptionWrapper.h> +#include <gmock/gmock.h> + +#include "yarpl/flowable/Flowable.h" + +namespace yarpl { +namespace mocks { + +/// GoogleMock-compatible Publisher implementation for fast prototyping. +/// UnmanagedMockPublisher's lifetime MUST be managed externally. +template <typename T> +class MockFlowable : public flowable::Flowable<T> { + public: + MOCK_METHOD1_T( + subscribe_, + void(std::shared_ptr<flowable::Subscriber<T>> subscriber)); + + void subscribe( + std::shared_ptr<flowable::Subscriber<T>> subscriber) noexcept override { + subscribe_(std::move(subscriber)); + } +}; + +/// GoogleMock-compatible Subscriber implementation for fast prototyping. +/// MockSubscriber MUST be heap-allocated, as it manages its own lifetime. +/// For the same reason putting mock instance in a smart pointer is a poor idea. +/// Can only be instanciated for CopyAssignable E type. +template <typename T> +class MockSubscriber : public flowable::Subscriber<T>, + public yarpl::enable_get_ref { + public: + MOCK_METHOD1( + onSubscribe_, + void(std::shared_ptr<flowable::Subscription> subscription)); + MOCK_METHOD1_T(onNext_, void(const T& value)); + MOCK_METHOD0(onComplete_, void()); + MOCK_METHOD1_T(onError_, void(folly::exception_wrapper ex)); + + explicit MockSubscriber(int64_t initial = std::numeric_limits<int64_t>::max()) + : initial_(initial) {} + + void onSubscribe( + std::shared_ptr<flowable::Subscription> subscription) override { + subscription_ = subscription; + auto this_ = this->ref_from_this(this); + onSubscribe_(subscription); + + if (initial_ > 0) { + subscription_->request(initial_); + } + } + + void onNext(T element) override { + auto this_ = this->ref_from_this(this); + onNext_(element); + + --waitedFrameCount_; + framesEventCV_.notify_one(); + } + + void onComplete() override { + auto this_ = this->ref_from_this(this); + onComplete_(); + subscription_.reset(); + terminated_ = true; + terminalEventCV_.notify_all(); + } + + void onError(folly::exception_wrapper ex) override { + auto this_ = this->ref_from_this(this); + onError_(std::move(ex)); + terminated_ = true; + terminalEventCV_.notify_all(); + } + + flowable::Subscription* subscription() const { + return subscription_.operator->(); + } + + /** + * Block the current thread until either onSuccess or onError is called. + */ + void awaitTerminalEvent( + std::chrono::milliseconds timeout = std::chrono::seconds(1)) { + // now block this thread + std::unique_lock<std::mutex> lk(m_); + // if shutdown gets implemented this would then be released by it + bool result = + terminalEventCV_.wait_for(lk, timeout, [this] { return terminated_; }); + EXPECT_TRUE(result) << "Timed out"; + } + + /** + * Block the current thread until onNext is called 'count' times. + */ + void awaitFrames( + uint64_t count, + std::chrono::milliseconds timeout = std::chrono::seconds(1)) { + waitedFrameCount_ += count; + std::unique_lock<std::mutex> lk(mFrame_); + if (waitedFrameCount_ > 0) { + bool result = framesEventCV_.wait_for( + lk, timeout, [this] { return waitedFrameCount_ <= 0; }); + EXPECT_TRUE(result) << "Timed out"; + } + } + + protected: + // As the 'subscription_' member in the parent class is private, + // we define it here again. + std::shared_ptr<flowable::Subscription> subscription_; + + int64_t initial_; + + bool terminated_{false}; + mutable std::mutex m_, mFrame_; + mutable std::condition_variable terminalEventCV_, framesEventCV_; + mutable std::atomic<int> waitedFrameCount_{0}; +}; + +/// GoogleMock-compatible Subscriber implementation for fast prototyping. +/// MockSubscriber MUST be heap-allocated, as it manages its own lifetime. +/// For the same reason putting mock instance in a smart pointer is a poor idea. +class MockSubscription : public flowable::Subscription { + public: + MOCK_METHOD1(request_, void(int64_t n)); + MOCK_METHOD0(cancel_, void()); + + void request(int64_t n) override { + request_(n); + } + + void cancel() override { + cancel_(); + } +}; +} // namespace mocks + +template <typename T, bool keep_reference_to_this = true> +class MockBaseSubscriber + : public flowable::BaseSubscriber<T, keep_reference_to_this> { + public: + MOCK_METHOD0_T(onSubscribeImpl, void()); + MOCK_METHOD1_T(onNextImpl, void(T)); + MOCK_METHOD0_T(onCompleteImpl, void()); + MOCK_METHOD1_T(onErrorImpl, void(folly::exception_wrapper)); +}; + +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/test_utils/Tuple.h b/ios/Pods/Flipper-RSocket/yarpl/test_utils/Tuple.h new file mode 100644 index 000000000..663e29637 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/test_utils/Tuple.h @@ -0,0 +1,58 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <atomic> +#include <iostream> + +namespace yarpl { + +struct Tuple { + const int a; + const int b; + + Tuple(const int _a, const int _b) : a(_a), b(_b) { + std::cout << "Tuple (" << a << ", " << b << ") constructed." << std::endl; + instanceCount++; + createdCount++; + } + + Tuple(const Tuple& t) : a(t.a), b(t.b) { + std::cout << "Tuple (" << a << ", " << b << ") copy-constructed." + << std::endl; + instanceCount++; + createdCount++; + } + + Tuple(Tuple&& t) noexcept : a(std::move(t.a)), b(std::move(t.b)) { + std::cout << "Tuple (" << a << ", " << b << ") move-constructed." + << std::endl; + instanceCount++; + createdCount++; + } + + ~Tuple() { + std::cout << "Tuple (" << a << ", " << b << ") destroyed." << std::endl; + std::cout << "Tuple destroyed!!" << std::endl; + instanceCount--; + destroyedCount++; + } + + static std::atomic<int> createdCount; + static std::atomic<int> destroyedCount; + static std::atomic<int> instanceCount; +}; + +} // namespace yarpl diff --git a/ios/Pods/Flipper-RSocket/yarpl/utils/credits.h b/ios/Pods/Flipper-RSocket/yarpl/utils/credits.h new file mode 100644 index 000000000..10063b728 --- /dev/null +++ b/ios/Pods/Flipper-RSocket/yarpl/utils/credits.h @@ -0,0 +1,100 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 + +#include <atomic> +#include <cstdint> +#include <limits> +#include <stdexcept> + +namespace yarpl { +namespace credits { + +constexpr int64_t kCanceled{std::numeric_limits<int64_t>::min()}; +constexpr int64_t kNoFlowControl{std::numeric_limits<int64_t>::max()}; + +/** + * Utility functions to help with handling request(int64_t n) since it can be + * called concurrently and must handle rollover. + * + * Since Flowable Subscription must have an int64_t per Subscription, we also + * leverage it for storing cancellation by setting to INT64_MIN so we don't have + * to also deal with a separate boolean field per Subscription. + * + * These functions are thread-safe and intended to deal with concurrent + * modification by all working off of std::atomic and using + * compare_exchange_strong. + */ + +/** + * Add the new value 'n' to the 'current' atomic<int64_t>. + * + * Caps the result at INT64_MAX. + * + * Adding a negative number does nothing. + * + * If 'current' is set to "cancelled" using the magic number INT64_MIN it will + * not be changed. + * + * Returns new value of credits. + */ +int64_t add(std::atomic<int64_t>*, int64_t); + +/** + * Version of add that works for non-atomic integers. + */ +int64_t add(int64_t, int64_t); + +/** + * Set 'current' to INT64_MIN as a magic number to represent "cancelled". + * + * Return true if this changed to cancelled, or false if it was already + * cancelled. + */ +bool cancel(std::atomic<int64_t>*); + +/** + * Consume (remove) credits from the 'current' atomic<int64_t>. + * + * This MUST only be used to remove credits after emitting a value via onNext. + * + * Returns new value of credits. + */ +int64_t consume(std::atomic<int64_t>*, int64_t); + +/** + * Try Consume (remove) credits from the 'current' atomic<int64_t>. + * + * Returns true if consuming the credit was successful. + */ +bool tryConsume(std::atomic<int64_t>*, int64_t); + +/** + * Version of consume that works for non-atomic integers. + */ +int64_t consume(int64_t&, int64_t); + +/** + * Whether the current value represents a "cancelled" subscription. + */ +bool isCancelled(std::atomic<int64_t>*); + +/** + * If the requested value is MAX so we can ignore flow control. + */ +bool isInfinite(std::atomic<int64_t>*); + +} // namespace credits +} // namespace yarpl diff --git a/ios/Pods/Flipper/LICENSE b/ios/Pods/Flipper/LICENSE new file mode 100644 index 000000000..b96dcb048 --- /dev/null +++ b/ios/Pods/Flipper/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/Pods/Flipper/README.md b/ios/Pods/Flipper/README.md new file mode 100644 index 000000000..47df05320 --- /dev/null +++ b/ios/Pods/Flipper/README.md @@ -0,0 +1,139 @@ +# Flipper [![Build Status](https://travis-ci.org/facebook/flipper.svg?branch=master)](https://travis-ci.org/facebook/flipper) [![Android Maven Badge](https://img.shields.io/maven-metadata/v/https/jcenter.bintray.com/com/facebook/flipper/flipper/maven-metadata.xml.svg?color=green&label=android)](https://bintray.com/facebook/maven/com.facebook.flipper%3Aflipper) [![iOS](https://img.shields.io/cocoapods/v/FlipperKit.svg?label=iOS&color=blue)](https://cocoapods.org/pods/Flipper) [![Greenkeeper badge](https://badges.greenkeeper.io/facebook/flipper.svg)](https://greenkeeper.io/) + +Flipper (formerly Sonar) is a platform for debugging mobile apps on iOS and Android. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API. + +![Flipper](/docs/assets/layout.png) + +## Table of Contents + +- [Mobile development](#mobile-development) +- [Extending Flipper](#extending-flipper) +- [Contributing to Flipper](#contributing-to-flipper) +- [In this repo](#in-this-repo) +- [Getting started](#getting-started) + - [Requirements](#requirements) +- [Building from Source](#building-from-source) + - [Desktop](#desktop) + - [Running from source](#running-from-source) + - [Building standalone application](#building-standalone-application) +- [iOS SDK + Sample App](#ios-sdk--sample-app) +- [Android SDK + Sample app](#android-sdk--sample-app) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) + +## Mobile development + +Flipper aims to be your number one companion for mobile app development on iOS and Android. Therefore, we provide a bunch of useful tools, including a log viewer, interactive layout inspector, and network inspector. + +## Extending Flipper + +Flipper is built as a platform. In addition to using the tools already included, you can create your own plugins to visualize and debug data from your mobile apps. Flipper takes care of sending data back and forth, calling functions, and listening for events on the mobile app. + +## Contributing to Flipper + +Both Flipper's desktop app and native mobile SDKs are open-source and MIT licensed. This enables you to see and understand how we are building plugins, and of course, join the community and help improve Flipper. We are excited to see what you will build on this platform. + +# In this repo + +This repository includes all parts of Flipper. This includes: + +* Flipper's desktop app built using [Electron](https://electronjs.org) (`/src`) +* native Flipper SDKs for iOS (`/iOS`) +* native Flipper SDKs for Android (`/android`) +* Plugins: + * Logs (`/src/device-plugins/logs`) + * Layout inspector (`/src/plugins/layout`) + * Network inspector (`/src/plugins/network`) + * Shared Preferences/NSUserDefaults inspector (`/src/plugins/shared_preferences`) +* website and documentation (`/website` / `/docs`) + +# Getting started + +Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started.html) to set up Flipper. + +## Requirements + +* node >= 8 +* yarn >= 1.5 +* iOS developer tools (for developing iOS plugins) +* Android SDK and adb + +# Building from Source + +## Desktop +### Running from source + +``` +git clone https://github.com/facebook/flipper.git +cd flipper +yarn +yarn start +``` + +NOTE: If you're on Windows, you need to use Yarn 1.5.1 until [this issue](https://github.com/yarnpkg/yarn/issues/6048) is resolved. + +### Building standalone application + +Provide either `--mac`, `--win`, `--linux` or any combination of them +to `yarn build` to build a release zip file for the given platform(s). E.g. + +``` +yarn build --mac --version $buildNumber +``` + +You can find the resulting artifact in the `dist/` folder. + +## iOS SDK + Sample App + +``` +cd iOS/Sample +rm -f Podfile.lock +pod install --repo-update +open Sample.xcworkspace +<Run app from xcode> +``` + +You can omit `--repo-update` to speed up the installation, but watch out as you may be building against outdated dependencies. + +## Android SDK + Sample app + +Start up an android emulator and run the following in the project root: +``` +./gradlew :sample:installDebug +``` + +## React Native SDK + Sample app + +``` +cd react-native/ReactNativeFlipperExample +yarn +yarn android +``` + +Note that the first 2 steps need to be done only once. + +Alternatively, the app can be started on `iOS` by running `yarn ios`. + +#### Troubleshooting + +Older yarn versions might show an error / hang with the message 'Waiting for the other yarn instance to finish'. If that happens, run the command `yarn` first separately in the directory `react-native/react-native-flipper`. + +## Documentation + +Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/docs). + +Our documentation is built with [Docusaurus](https://docusaurus.io/). You can build +it locally by running this: + +```bash +cd website +yarn +yarn start +``` + +## Contributing +See the [CONTRIBUTING](/CONTRIBUTING.md) file for how to help out. + +## License +Flipper is MIT licensed, as found in the [LICENSE](/LICENSE) file. diff --git a/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.cpp b/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.cpp new file mode 100644 index 000000000..1e81f721c --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "CertificateUtils.h" + +#include <fcntl.h> +#include <folly/portability/Fcntl.h> +#include <folly/portability/SysStat.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <cstring> + +void free( + EVP_PKEY* pKey, + X509_REQ* x509_req, + BIGNUM* bne, + BIO* privateKey, + BIO* csrBio); + +bool generateCertSigningRequest( + const char* appId, + const char* csrFile, + const char* privateKeyFile) { + int ret = 0; + BIGNUM* bne = NULL; + + int nVersion = 1; + int bits = 2048; + + // Using 65537 as exponent + unsigned long e = RSA_F4; + + X509_NAME* x509_name = NULL; + + const char* subjectCountry = "US"; + const char* subjectProvince = "CA"; + const char* subjectCity = "Menlo Park"; + const char* subjectOrganization = "Flipper"; + const char* subjectCommon = appId; + + X509_REQ* x509_req = X509_REQ_new(); + EVP_PKEY* pKey = EVP_PKEY_new(); + RSA* rsa = RSA_new(); + BIO* privateKey = NULL; + BIO* csrBio = NULL; + + EVP_PKEY_assign_RSA(pKey, rsa); + + // Generate rsa key + bne = BN_new(); + BN_set_flags(bne, BN_FLG_CONSTTIME); + ret = BN_set_word(bne, e); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = RSA_generate_key_ex(rsa, bits, bne, NULL); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + { + // Write private key to a file + int privateKeyFd = + open(privateKeyFile, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); + if (privateKeyFd < 0) { + free(pKey, x509_req, bne, privateKey, csrBio); + return -1; + } + FILE* privateKeyFp = fdopen(privateKeyFd, "w"); + if (privateKeyFp == NULL) { + free(pKey, x509_req, bne, privateKey, csrBio); + return -1; + } + privateKey = BIO_new_fp(privateKeyFp, BIO_CLOSE); + ret = + PEM_write_bio_RSAPrivateKey(privateKey, rsa, NULL, NULL, 0, NULL, NULL); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + } + + rsa = NULL; + + ret = BIO_flush(privateKey); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_REQ_set_version(x509_req, nVersion); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + x509_name = X509_REQ_get_subject_name(x509_req); + + ret = X509_NAME_add_entry_by_txt( + x509_name, + "C", + MBSTRING_ASC, + (const unsigned char*)subjectCountry, + -1, + -1, + 0); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_NAME_add_entry_by_txt( + x509_name, + "ST", + MBSTRING_ASC, + (const unsigned char*)subjectProvince, + -1, + -1, + 0); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_NAME_add_entry_by_txt( + x509_name, + "L", + MBSTRING_ASC, + (const unsigned char*)subjectCity, + -1, + -1, + 0); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_NAME_add_entry_by_txt( + x509_name, + "O", + MBSTRING_ASC, + (const unsigned char*)subjectOrganization, + -1, + -1, + 0); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_NAME_add_entry_by_txt( + x509_name, + "CN", + MBSTRING_ASC, + (const unsigned char*)subjectCommon, + -1, + -1, + 0); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_REQ_set_pubkey(x509_req, pKey); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + ret = X509_REQ_sign( + x509_req, pKey, EVP_sha256()); // returns x509_req->signature->length + if (ret <= 0) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + { + // Write CSR to a file + int csrFd = open(csrFile, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); + if (csrFd < 0) { + free(pKey, x509_req, bne, privateKey, csrBio); + return -1; + } + FILE* csrFp = fdopen(csrFd, "w"); + if (csrFp == NULL) { + free(pKey, x509_req, bne, privateKey, csrBio); + return -1; + } + csrBio = BIO_new_fp(csrFp, BIO_CLOSE); + ret = PEM_write_bio_X509_REQ(csrBio, x509_req); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + } + + ret = BIO_flush(csrBio); + if (ret != 1) { + free(pKey, x509_req, bne, privateKey, csrBio); + return ret; + } + + return (ret == 1); +} + +void free( + EVP_PKEY* pKey, + X509_REQ* x509_req, + BIGNUM* bne, + BIO* privateKey, + BIO* csrBio) { + BN_free(bne); + X509_REQ_free(x509_req); + EVP_PKEY_free(pKey); + BIO_free_all(privateKey); + BIO_free_all(csrBio); +} diff --git a/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.h b/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.h new file mode 100644 index 000000000..a982f3fdd --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/CertificateUtils.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef CertificateUtils_hpp +#define CertificateUtils_hpp + +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <stdio.h> + +bool generateCertSigningRequest( + const char* appId, + const char* csrFile, + const char* privateKeyFile); + +#endif /* CertificateUtils_hpp */ diff --git a/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.cpp b/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.cpp new file mode 100644 index 000000000..b1d448ab2 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ConnectionContextStore.h" +#include <folly/json.h> +#include <folly/portability/SysStat.h> +#include <stdio.h> +#include <fstream> +#include <iostream> +#include "CertificateUtils.h" +#include "Log.h" + +using namespace facebook::flipper; + +static constexpr auto CSR_FILE_NAME = "app.csr"; +static constexpr auto FLIPPER_CA_FILE_NAME = "sonarCA.crt"; +static constexpr auto CLIENT_CERT_FILE_NAME = "device.crt"; +static constexpr auto PRIVATE_KEY_FILE = "privateKey.pem"; +static constexpr auto CONNECTION_CONFIG_FILE = "connection_config.json"; + +bool fileExists(std::string fileName); +std::string loadStringFromFile(std::string fileName); +void writeStringToFile(std::string content, std::string fileName); + +ConnectionContextStore::ConnectionContextStore(DeviceData deviceData) + : deviceData_(deviceData) {} + +bool ConnectionContextStore::hasRequiredFiles() { + std::string caCert = + loadStringFromFile(absoluteFilePath(FLIPPER_CA_FILE_NAME)); + std::string clientCert = + loadStringFromFile(absoluteFilePath(CLIENT_CERT_FILE_NAME)); + std::string privateKey = + loadStringFromFile(absoluteFilePath(PRIVATE_KEY_FILE)); + + if (caCert == "" || clientCert == "" || privateKey == "") { + return false; + } + return true; +} + +std::string ConnectionContextStore::getCertificateSigningRequest() { + // Use in-memory CSR if already loaded + if (csr != "") { + return csr; + } + + // Attempt to load existing CSR from previous run of the app + csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); + if (csr != "") { + return csr; + } + + // Clean all state and generate a new one + resetState(); + bool success = generateCertSigningRequest( + deviceData_.appId.c_str(), + absoluteFilePath(CSR_FILE_NAME).c_str(), + absoluteFilePath(PRIVATE_KEY_FILE).c_str()); + if (!success) { + throw new std::runtime_error("Failed to generate CSR"); + } + csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); + + return csr; +} + +std::shared_ptr<SSLContext> ConnectionContextStore::getSSLContext() { + std::shared_ptr<folly::SSLContext> sslContext = + std::make_shared<folly::SSLContext>(); + sslContext->loadTrustedCertificates( + absoluteFilePath(FLIPPER_CA_FILE_NAME).c_str()); + sslContext->setVerificationOption( + folly::SSLContext::SSLVerifyPeerEnum::VERIFY); + sslContext->loadCertKeyPairFromFiles( + absoluteFilePath(CLIENT_CERT_FILE_NAME).c_str(), + absoluteFilePath(PRIVATE_KEY_FILE).c_str()); + sslContext->authenticate(true, false); + return sslContext; +} + +std::string ConnectionContextStore::getDeviceId() { + /* On android we can't reliably get the serial of the current device + So rely on our locally written config, which is provided by the + desktop app. + For backwards compatibility, when this isn't present, fall back to the + unreliable source. */ + try { + std::string config = + loadStringFromFile(absoluteFilePath(CONNECTION_CONFIG_FILE)); + auto maybeDeviceId = folly::parseJson(config)["deviceId"]; + return maybeDeviceId.isString() ? maybeDeviceId.getString() + : deviceData_.deviceId; + } catch (std::exception&) { + return deviceData_.deviceId; + } +} + +void ConnectionContextStore::storeConnectionConfig(folly::dynamic& config) { + std::string json = folly::toJson(config); + writeStringToFile(json, absoluteFilePath(CONNECTION_CONFIG_FILE)); +} + +std::string ConnectionContextStore::absoluteFilePath(const char* filename) { + return std::string(deviceData_.privateAppDirectory + "/sonar/" + filename); +} + +std::string ConnectionContextStore::getCertificateDirectoryPath() { + return absoluteFilePath(""); +} + +bool ConnectionContextStore::resetState() { + // Clear in-memory state + csr = ""; + + // Delete state from disk + std::string dirPath = absoluteFilePath(""); + struct stat info; + if (stat(dirPath.c_str(), &info) != 0) { + int ret = mkdir(dirPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); + return ret == 0; + } else if (info.st_mode & S_IFDIR) { + for (auto file : {CSR_FILE_NAME, + FLIPPER_CA_FILE_NAME, + CLIENT_CERT_FILE_NAME, + PRIVATE_KEY_FILE, + CONNECTION_CONFIG_FILE}) { + std::remove(absoluteFilePath(file).c_str()); + } + return true; + } else { + log("ERROR: Flipper path exists but is not a directory: " + dirPath); + return false; + } +} + +std::string loadStringFromFile(std::string fileName) { + if (!fileExists(fileName)) { + return ""; + } + std::stringstream buffer; + std::ifstream stream; + std::string line; + stream.open(fileName.c_str()); + if (!stream) { + log("ERROR: Unable to open ifstream: " + fileName); + return ""; + } + buffer << stream.rdbuf(); + std::string s = buffer.str(); + return s; +} + +void writeStringToFile(std::string content, std::string fileName) { + std::ofstream out(fileName); + out << content; +} + +bool fileExists(std::string fileName) { + struct stat buffer; + return stat(fileName.c_str(), &buffer) == 0; +} diff --git a/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.h b/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.h new file mode 100644 index 000000000..bad971481 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/ConnectionContextStore.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/dynamic.h> +#include <folly/io/async/SSLContext.h> +#include <string> +#include "FlipperInitConfig.h" + +using namespace folly; + +namespace facebook { +namespace flipper { + +class ConnectionContextStore { + public: + ConnectionContextStore(DeviceData deviceData); + bool hasRequiredFiles(); + std::string getCertificateSigningRequest(); + std::shared_ptr<SSLContext> getSSLContext(); + std::string getCertificateDirectoryPath(); + std::string getDeviceId(); + void storeConnectionConfig(folly::dynamic& config); + bool resetState(); + + private: + DeviceData deviceData_; + std::string csr = ""; + + std::string absoluteFilePath(const char* filename); +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FireAndForgetBasedFlipperResponder.h b/ios/Pods/Flipper/xplat/Flipper/FireAndForgetBasedFlipperResponder.h new file mode 100644 index 000000000..f06da8e5a --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FireAndForgetBasedFlipperResponder.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/json.h> +#include "FlipperConnectionManager.h" +#include "FlipperResponder.h" + +namespace facebook { +namespace flipper { + +/* Responder for responding to legacy flipper applications. + Originally, flipper desktop used fireAndForget for all messages, so calling + the SDK would send a fire and forget message, to which the SDK would respond + with another one, with an id field that flipper uses to map it to the + original request. This Responder should be used when such requests are + received. + */ +class FireAndForgetBasedFlipperResponder : public FlipperResponder { + public: + FireAndForgetBasedFlipperResponder( + FlipperConnectionManager* socket, + int64_t responseID) + : socket_(socket), responseID_(responseID), idValid_(true) {} + + FireAndForgetBasedFlipperResponder(FlipperConnectionManager* socket) + : socket_(socket), idValid_(false) {} + + void success(const folly::dynamic& response) override { + const folly::dynamic message = idValid_ + ? folly::dynamic::object("id", responseID_)("success", response) + : folly::dynamic::object("success", response); + socket_->sendMessage(message); + } + + void error(const folly::dynamic& response) override { + const folly::dynamic message = idValid_ + ? folly::dynamic::object("id", responseID_)("error", response) + : folly::dynamic::object("error", response); + socket_->sendMessage(message); + } + + bool hasId() const { + return idValid_; + } + + private: + FlipperConnectionManager* socket_; + int64_t responseID_; + bool idValid_; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp new file mode 100644 index 000000000..dc3325e54 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperClient.h" +#include <fstream> +#include <iostream> +#include <stdexcept> +#include <vector> +#include "ConnectionContextStore.h" +#include "FireAndForgetBasedFlipperResponder.h" +#include "FlipperConnectionImpl.h" +#include "FlipperConnectionManagerImpl.h" +#include "FlipperResponderImpl.h" +#include "FlipperState.h" +#include "FlipperStep.h" +#include "Log.h" +#if __ANDROID__ +#include "utils/CallstackHelper.h" +#endif +#if __APPLE__ +#include <execinfo.h> +#endif + +#if FB_SONARKIT_ENABLED + +namespace facebook { +namespace flipper { + +static FlipperClient* kInstance{nullptr}; + +using folly::dynamic; + +void FlipperClient::init(FlipperInitConfig config) { + auto state = std::make_shared<FlipperState>(); + auto context = std::make_shared<ConnectionContextStore>(config.deviceData); + kInstance = new FlipperClient( + std::make_unique<FlipperConnectionManagerImpl>( + std::move(config), state, context), + state); +} + +FlipperClient* FlipperClient::instance() { + return kInstance; +} + +void FlipperClient::setStateListener( + std::shared_ptr<FlipperStateUpdateListener> stateListener) { + performAndReportError([this, &stateListener]() { + log("Setting state listener"); + flipperState_->setUpdateListener(stateListener); + }); +} + +void FlipperClient::addPlugin(std::shared_ptr<FlipperPlugin> plugin) { + performAndReportError([this, plugin]() { + log("FlipperClient::addPlugin " + plugin->identifier()); + auto step = flipperState_->start("Add plugin " + plugin->identifier()); + + std::lock_guard<std::mutex> lock(mutex_); + if (plugins_.find(plugin->identifier()) != plugins_.end()) { + throw std::out_of_range( + "plugin " + plugin->identifier() + " already added."); + } + plugins_[plugin->identifier()] = plugin; + step->complete(); + if (connected_) { + refreshPlugins(); + if (plugin->runInBackground()) { + auto& conn = connections_[plugin->identifier()]; + conn = std::make_shared<FlipperConnectionImpl>( + socket_.get(), plugin->identifier()); + plugin->didConnect(conn); + } + } + }); +} + +void FlipperClient::removePlugin(std::shared_ptr<FlipperPlugin> plugin) { + performAndReportError([this, plugin]() { + log("FlipperClient::removePlugin " + plugin->identifier()); + + std::lock_guard<std::mutex> lock(mutex_); + if (plugins_.find(plugin->identifier()) == plugins_.end()) { + throw std::out_of_range("plugin " + plugin->identifier() + " not added."); + } + disconnect(plugin); + plugins_.erase(plugin->identifier()); + if (connected_) { + refreshPlugins(); + } + }); +} + +void FlipperClient::startBackgroundPlugins() { + std::cout << "Activating Background Plugins..." << std::endl; + for (std::map<std::string, std::shared_ptr<FlipperPlugin>>::iterator it = + plugins_.begin(); + it != plugins_.end(); + ++it) { + std::cout << it->first << std::endl; + if (it->second.get()->runInBackground()) { + try { + auto& conn = connections_[it->first]; + conn = + std::make_shared<FlipperConnectionImpl>(socket_.get(), it->first); + it->second.get()->didConnect(conn); + } catch (std::exception& e) { + log("Exception starting background plugin: " + it->first + ". " + + e.what()); + } + } + } +} + +std::shared_ptr<FlipperPlugin> FlipperClient::getPlugin( + const std::string& identifier) { + std::lock_guard<std::mutex> lock(mutex_); + if (plugins_.find(identifier) == plugins_.end()) { + return nullptr; + } + return plugins_.at(identifier); +} + +bool FlipperClient::hasPlugin(const std::string& identifier) { + std::lock_guard<std::mutex> lock(mutex_); + return plugins_.find(identifier) != plugins_.end(); +} + +void FlipperClient::disconnect(std::shared_ptr<FlipperPlugin> plugin) { + const auto& conn = connections_.find(plugin->identifier()); + if (conn != connections_.end()) { + connections_.erase(plugin->identifier()); + plugin->didDisconnect(); + } +} + +void FlipperClient::refreshPlugins() { + performAndReportError([this]() { + dynamic message = dynamic::object("method", "refreshPlugins"); + socket_->sendMessage(message); + }); +} + +void FlipperClient::onConnected() { + performAndReportError([this]() { + log("FlipperClient::onConnected"); + + std::lock_guard<std::mutex> lock(mutex_); + connected_ = true; + startBackgroundPlugins(); + }); +} + +void FlipperClient::onDisconnected() { + performAndReportError([this]() { + log("FlipperClient::onDisconnected"); + auto step = flipperState_->start("Trigger onDisconnected callbacks"); + std::lock_guard<std::mutex> lock(mutex_); + connected_ = false; + for (const auto& iter : plugins_) { + disconnect(iter.second); + } + step->complete(); + }); +} + +void FlipperClient::onMessageReceived( + const dynamic& message, + std::unique_ptr<FlipperResponder> uniqueResponder) { + // Convert to shared pointer so we can hold on to it while passing it to the + // plugin, and still use it to respond with an error if we catch an exception. + std::shared_ptr<FlipperResponder> responder = std::move(uniqueResponder); + try { + std::lock_guard<std::mutex> lock(mutex_); + const auto& method = message["method"]; + const auto& params = message.getDefault("params"); + + if (method == "getPlugins") { + dynamic identifiers = dynamic::array(); + for (const auto& elem : plugins_) { + identifiers.push_back(elem.first); + } + dynamic response = dynamic::object("plugins", identifiers); + responder->success(response); + return; + } + + if (method == "init") { + const auto identifier = params["plugin"].getString(); + if (plugins_.find(identifier) == plugins_.end()) { + std::string errorMessage = "Plugin " + identifier + + " not found for method " + method.getString(); + log(errorMessage); + responder->error(folly::dynamic::object("message", errorMessage)( + "name", "PluginNotFound")); + return; + } + const auto plugin = plugins_.at(identifier); + if (!plugin.get()->runInBackground()) { + auto& conn = connections_[plugin->identifier()]; + conn = std::make_shared<FlipperConnectionImpl>( + socket_.get(), plugin->identifier()); + plugin->didConnect(conn); + } + return; + } + + if (method == "deinit") { + const auto identifier = params["plugin"].getString(); + if (plugins_.find(identifier) == plugins_.end()) { + std::string errorMessage = "Plugin " + identifier + + " not found for method " + method.getString(); + log(errorMessage); + responder->error(folly::dynamic::object("message", errorMessage)( + "name", "PluginNotFound")); + return; + } + const auto plugin = plugins_.at(identifier); + if (!plugin.get()->runInBackground()) { + disconnect(plugin); + } + return; + } + + if (method == "execute") { + const auto identifier = params["api"].getString(); + if (connections_.find(identifier) == connections_.end()) { + std::string errorMessage = "Connection " + identifier + + " not found for method " + method.getString(); + log(errorMessage); + responder->error(folly::dynamic::object("message", errorMessage)( + "name", "ConnectionNotFound")); + return; + } + const auto& conn = connections_.at(params["api"].getString()); + conn->call( + params["method"].getString(), params.getDefault("params"), responder); + return; + } + + if (method == "isMethodSupported") { + const auto identifier = params["api"].getString(); + if (connections_.find(identifier) == connections_.end()) { + std::string errorMessage = "Connection " + identifier + + " not found for method " + method.getString(); + log(errorMessage); + responder->error(folly::dynamic::object("message", errorMessage)( + "name", "ConnectionNotFound")); + return; + } + const auto& conn = connections_.at(params["api"].getString()); + bool isSupported = conn->hasReceiver(params["method"].getString()); + responder->success(dynamic::object("isSupported", isSupported)); + return; + } + + dynamic response = + dynamic::object("message", "Received unknown method: " + method); + responder->error(response); + } catch (std::exception& e) { + log(std::string("Error: ") + e.what()); + if (responder) { + responder->error(dynamic::object("message", e.what())( + "stacktrace", callstack())("name", e.what())); + } + } catch (...) { + log("Unknown error suppressed in FlipperClient"); + if (responder) { + responder->error(dynamic::object( + "message", + "Unknown error during " + message["method"] + ". " + + folly::toJson(message))("stacktrace", callstack())( + "name", "Unknown")); + } + } +} + +std::string FlipperClient::callstack() { +#if __APPLE__ + // For some iOS apps, __Unwind_Backtrace symbol wasn't found in sandcastle + // builds, thus, for iOS apps, using backtrace c function. + void* callstack[2048]; + int frames = backtrace(callstack, 2048); + char** strs = backtrace_symbols(callstack, frames); + std::string output = ""; + for (int i = 0; i < frames; ++i) { + output.append(strs[i]); + output.append("\n"); + } + return output; +#elif __ANDROID__ + const size_t max = 2048; + void* buffer[max]; + std::ostringstream oss; + + dumpBacktrace(oss, buffer, captureBacktrace(buffer, max)); + std::string output = std::string(oss.str().c_str()); + return output; +#else + return ""; +#endif +} +void FlipperClient::performAndReportError(const std::function<void()>& func) { +#if FLIPPER_ENABLE_CRASH + // To debug the stack trace and an exception turn on the compiler flag + // FLIPPER_ENABLE_CRASH + func(); +#else + try { + func(); + } catch (std::exception& e) { + handleError(e); + } catch (std::exception* e) { + if (e) { + handleError(*e); + } + } catch (...) { + // Generic catch block for the exception of type not belonging to + // std::exception + log("Unknown error suppressed in FlipperClient"); + } +#endif +} + +void FlipperClient::handleError(std::exception& e) { + if (connected_) { + std::string callstack = this->callstack(); + dynamic message = dynamic::object( + "error", + dynamic::object("message", e.what())("stacktrace", callstack)( + "name", e.what())); + socket_->sendMessage(message); + } else { + log("Error: " + std::string(e.what())); + } +} + +std::string FlipperClient::getState() { + return flipperState_->getState(); +} + +std::vector<StateElement> FlipperClient::getStateElements() { + return flipperState_->getStateElements(); +} + +} // namespace flipper +} // namespace facebook + +#endif diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h new file mode 100644 index 000000000..1c107d6a6 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <map> +#include <mutex> +#include <vector> +#include "FlipperConnectionImpl.h" +#include "FlipperConnectionManager.h" +#include "FlipperInitConfig.h" +#include "FlipperPlugin.h" +#include "FlipperState.h" +#include "FlipperStep.h" + +namespace facebook { +namespace flipper { + +class FlipperClient : public FlipperConnectionManager::Callbacks { + public: + /** + Call before accessing instance with FlipperClient::instance(). This will set + up all the state needed to establish a Flipper connection. + */ + static void init(FlipperInitConfig config); + + /** + Standard accessor for the shared FlipperClient instance. This returns a + singleton instance to a shared FlipperClient. First call to this function + will create the shared FlipperClient. Must call + FlipperClient::initDeviceData() before first call to + FlipperClient::instance(). + */ + static FlipperClient* instance(); + + /** + Only public for testing + */ + FlipperClient( + std::unique_ptr<FlipperConnectionManager> socket, + std::shared_ptr<FlipperState> state) + : socket_(std::move(socket)), flipperState_(state) { + auto step = flipperState_->start("Create client"); + socket_->setCallbacks(this); + auto& conn = connections_["flipper-crash-report"]; + conn = std::make_shared<FlipperConnectionImpl>( + socket_.get(), "flipper-crash-report"); + step->complete(); + } + + void start() { + performAndReportError([this]() { + auto step = flipperState_->start("Start client"); + socket_->start(); + step->complete(); + }); + } + + void stop() { + performAndReportError([this]() { + auto step = flipperState_->start("Stop client"); + socket_->stop(); + step->complete(); + }); + } + + void onConnected() override; + + void onDisconnected() override; + + void onMessageReceived( + const folly::dynamic& message, + std::unique_ptr<FlipperResponder>) override; + + void addPlugin(std::shared_ptr<FlipperPlugin> plugin); + + void removePlugin(std::shared_ptr<FlipperPlugin> plugin); + + void refreshPlugins(); + + void setStateListener( + std::shared_ptr<FlipperStateUpdateListener> stateListener); + + std::shared_ptr<FlipperPlugin> getPlugin(const std::string& identifier); + + std::string getState(); + + std::vector<StateElement> getStateElements(); + + template <typename P> + std::shared_ptr<P> getPlugin(const std::string& identifier) { + return std::static_pointer_cast<P>(getPlugin(identifier)); + } + + bool hasPlugin(const std::string& identifier); + void performAndReportError(const std::function<void()>& func); + + private: + static FlipperClient* instance_; + bool connected_ = false; + std::unique_ptr<FlipperConnectionManager> socket_; + std::map<std::string, std::shared_ptr<FlipperPlugin>> plugins_; + std::map<std::string, std::shared_ptr<FlipperConnectionImpl>> connections_; + std::mutex mutex_; + std::shared_ptr<FlipperState> flipperState_; + + void disconnect(std::shared_ptr<FlipperPlugin> plugin); + void startBackgroundPlugins(); + std::string callstack(); + void handleError(std::exception& e); +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnection.h b/ios/Pods/Flipper/xplat/Flipper/FlipperConnection.h new file mode 100644 index 000000000..cbb4daa58 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/json.h> +#include <functional> +#include <string> +#include "FlipperResponder.h" + +namespace facebook { +namespace flipper { + +/** +Represents a connection between the Desktop and mobile plugins +with corresponding identifiers. +*/ +class FlipperConnection { + public: + using FlipperReceiver = std::function< + void(const folly::dynamic&, std::shared_ptr<FlipperResponder>)>; + + virtual ~FlipperConnection() {} + + /** + Invoke a method on the Flipper desktop plugin with with a matching identifier. + */ + virtual void send( + const std::string& method, + const folly::dynamic& params) = 0; + + /** + Report an error to the Flipper desktop app + */ + virtual void error( + const std::string& message, + const std::string& stacktrace) = 0; + + /** + Register a receiver to be notified of incoming calls of the given + method from the Flipper desktop plugin with a matching identifier. + */ + virtual void receive( + const std::string& method, + const FlipperReceiver& receiver) = 0; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionImpl.h b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionImpl.h new file mode 100644 index 000000000..adb0d1b7a --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionImpl.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <map> +#include <string> +#include "FlipperConnection.h" +#include "FlipperConnectionManager.h" +#include "Log.h" + +namespace facebook { +namespace flipper { + +class FlipperConnectionImpl : public FlipperConnection { + public: + FlipperConnectionImpl( + FlipperConnectionManager* socket, + const std::string& name) + : socket_(socket), name_(name) {} + + void call( + const std::string& method, + const folly::dynamic& params, + std::shared_ptr<FlipperResponder> responder) { + if (receivers_.find(method) == receivers_.end()) { + std::string errorMessage = "Receiver " + method + " not found."; + log("Error: " + errorMessage); + responder->error(folly::dynamic::object("message", errorMessage)); + return; + } + receivers_.at(method)(params, responder); + } + + void send(const std::string& method, const folly::dynamic& params) override { + folly::dynamic message = folly::dynamic::object("method", "execute")( + "params", + folly::dynamic::object("api", name_)("method", method)( + "params", params)); + socket_->sendMessage(message); + } + + void error(const std::string& message, const std::string& stacktrace) + override { + socket_->sendMessage(folly::dynamic::object( + "error", + folly::dynamic::object("message", message)("stacktrace", stacktrace))); + } + + void receive(const std::string& method, const FlipperReceiver& receiver) + override { + receivers_[method] = receiver; + } + + /** + Runtime check which receivers are supported for this app + */ + bool hasReceiver(const std::string& method) { + return receivers_.find(method) != receivers_.end(); + } + + private: + FlipperConnectionManager* socket_; + std::string name_; + std::map<std::string, FlipperReceiver> receivers_; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManager.h b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManager.h new file mode 100644 index 000000000..6c41cc943 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManager.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/json.h> +#include "FlipperResponder.h" + +namespace facebook { +namespace flipper { + +class FlipperConnectionManager { + public: + class Callbacks; + + public: + virtual ~FlipperConnectionManager(){}; + + /** + Establishes a connection to the ws server. + */ + virtual void start() = 0; + + /** + Closes an open connection to the ws server. + */ + virtual void stop() = 0; + + /** + True if there's an open connection. + This method may block if the connection is busy. + */ + virtual bool isOpen() const = 0; + + /** + Send message to the ws server. + */ + virtual void sendMessage(const folly::dynamic& message) = 0; + + /** + Handler for connection and message receipt from the ws server. + The callbacks should be set before a connection is established. + */ + virtual void setCallbacks(Callbacks* callbacks) = 0; + + /** + Called by ws server when a message has been received. + */ + virtual void onMessageReceived( + const folly::dynamic& message, + std::unique_ptr<FlipperResponder> responder) = 0; +}; + +class FlipperConnectionManager::Callbacks { + public: + virtual ~Callbacks(){}; + + virtual void onConnected() = 0; + + virtual void onDisconnected() = 0; + + virtual void onMessageReceived( + const folly::dynamic& message, + std::unique_ptr<FlipperResponder>) = 0; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp new file mode 100644 index 000000000..9cbcfb5e9 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp @@ -0,0 +1,393 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperConnectionManagerImpl.h" +#include <folly/String.h> +#include <folly/futures/Future.h> +#include <folly/io/async/AsyncSocketException.h> +#include <folly/io/async/SSLContext.h> +#include <folly/json.h> +#include <rsocket/Payload.h> +#include <rsocket/RSocket.h> +#include <rsocket/transports/tcp/TcpConnectionFactory.h> +#include <stdexcept> +#include <thread> +#include "ConnectionContextStore.h" +#include "FireAndForgetBasedFlipperResponder.h" +#include "FlipperRSocketResponder.h" +#include "FlipperResponderImpl.h" +#include "FlipperStep.h" +#include "Log.h" +#include "yarpl/Single.h" + +#define WRONG_THREAD_EXIT_MSG \ + "ERROR: Aborting flipper initialization because it's not running in the flipper thread." + +static constexpr int reconnectIntervalSeconds = 2; +static constexpr int connectionKeepaliveSeconds = 10; + +static constexpr int maxPayloadSize = 0xFFFFFF; + +// Not a public-facing version number. +// Used for compatibility checking with desktop flipper. +// To be bumped for every core platform interface change. +static constexpr int sdkVersion = 3; + +namespace facebook { +namespace flipper { + +class ConnectionEvents : public rsocket::RSocketConnectionEvents { + private: + FlipperConnectionManagerImpl* websocket_; + + public: + ConnectionEvents(FlipperConnectionManagerImpl* websocket) + : websocket_(websocket) {} + + void onConnected() { + websocket_->isOpen_ = true; + if (websocket_->connectionIsTrusted_) { + websocket_->callbacks_->onConnected(); + } + } + + void onDisconnected(const folly::exception_wrapper&) { + if (!websocket_->isOpen_) + return; + websocket_->isOpen_ = false; + if (websocket_->connectionIsTrusted_) { + websocket_->connectionIsTrusted_ = false; + websocket_->callbacks_->onDisconnected(); + } + websocket_->reconnect(); + } + + void onClosed(const folly::exception_wrapper& e) { + onDisconnected(e); + } +}; + +FlipperConnectionManagerImpl::FlipperConnectionManagerImpl( + FlipperInitConfig config, + std::shared_ptr<FlipperState> state, + std::shared_ptr<ConnectionContextStore> contextStore) + : deviceData_(config.deviceData), + flipperState_(state), + insecurePort(config.insecurePort), + securePort(config.securePort), + flipperEventBase_(config.callbackWorker), + connectionEventBase_(config.connectionWorker), + contextStore_(contextStore) { + CHECK_THROW(config.callbackWorker, std::invalid_argument); + CHECK_THROW(config.connectionWorker, std::invalid_argument); +} + +FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() { + stop(); +} + +void FlipperConnectionManagerImpl::start() { + if (isStarted_) { + log("Already started"); + return; + } + isStarted_ = true; + + auto step = flipperState_->start("Start connection thread"); + + folly::makeFuture() + .via(flipperEventBase_->getEventBase()) + .delayed(std::chrono::milliseconds(0)) + .thenValue([this, step](auto&&) { + step->complete(); + startSync(); + }); +} + +void FlipperConnectionManagerImpl::startSync() { + if (!isStarted_) { + log("Not started"); + return; + } + if (!isRunningInOwnThread()) { + log(WRONG_THREAD_EXIT_MSG); + return; + } + if (isOpen()) { + log("Already connected"); + return; + } + bool isClientSetupStep = isCertificateExchangeNeeded(); + auto step = flipperState_->start( + isClientSetupStep ? "Establish pre-setup connection" + : "Establish main connection"); + try { + if (isClientSetupStep) { + doCertificateExchange(); + } else { + connectSecurely(); + } + step->complete(); + } catch (const folly::AsyncSocketException& e) { + if (e.getType() == folly::AsyncSocketException::NOT_OPEN || + e.getType() == folly::AsyncSocketException::NETWORK_ERROR) { + // The expected code path when flipper desktop is not running. + // Don't count as a failed attempt, or it would invalidate the connection + // files for no reason. On iOS devices, we can always connect to the local + // port forwarding server even when it can't connect to flipper. In that + // case we get a Network error instead of a Port not open error, so we + // treat them the same. + step->fail( + "No route to flipper found. Is flipper desktop running? Retrying..."); + } else { + if (e.getType() == folly::AsyncSocketException::SSL_ERROR) { + auto message = std::string(e.what()) + + "\nMake sure the date and time of your device is up to date."; + log(message); + step->fail(message); + } else { + log(e.what()); + step->fail(e.what()); + } + failedConnectionAttempts_++; + } + reconnect(); + } catch (const std::exception& e) { + log(e.what()); + step->fail(e.what()); + failedConnectionAttempts_++; + reconnect(); + } +} + +void FlipperConnectionManagerImpl::doCertificateExchange() { + rsocket::SetupParameters parameters; + folly::SocketAddress address; + + parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object( + "os", deviceData_.os)("device", deviceData_.device)( + "app", deviceData_.app)("sdk_version", sdkVersion))); + address.setFromHostPort(deviceData_.host, insecurePort); + + auto connectingInsecurely = flipperState_->start("Connect insecurely"); + connectionIsTrusted_ = false; + client_ = + rsocket::RSocket::createConnectedClient( + std::make_unique<rsocket::TcpConnectionFactory>( + *connectionEventBase_->getEventBase(), std::move(address)), + std::move(parameters), + nullptr, + std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval + nullptr, // stats + std::make_shared<ConnectionEvents>(this)) + .get(); + connectingInsecurely->complete(); + + auto resettingState = flipperState_->start("Reset state"); + contextStore_->resetState(); + resettingState->complete(); + + requestSignedCertFromFlipper(); +} + +void FlipperConnectionManagerImpl::connectSecurely() { + rsocket::SetupParameters parameters; + folly::SocketAddress address; + + auto loadingDeviceId = flipperState_->start("Load Device Id"); + auto deviceId = contextStore_->getDeviceId(); + if (deviceId.compare("unknown")) { + loadingDeviceId->complete(); + } + + parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object( + "csr", contextStore_->getCertificateSigningRequest().c_str())( + "csr_path", contextStore_->getCertificateDirectoryPath().c_str())( + "os", deviceData_.os)("device", deviceData_.device)( + "device_id", deviceId)("app", deviceData_.app)( + "sdk_version", sdkVersion))); + address.setFromHostPort(deviceData_.host, securePort); + + std::shared_ptr<folly::SSLContext> sslContext = + contextStore_->getSSLContext(); + auto connectingSecurely = flipperState_->start("Connect securely"); + connectionIsTrusted_ = true; + client_ = + rsocket::RSocket::createConnectedClient( + std::make_unique<rsocket::TcpConnectionFactory>( + *connectionEventBase_->getEventBase(), + std::move(address), + std::move(sslContext)), + std::move(parameters), + std::make_shared<FlipperRSocketResponder>(this, connectionEventBase_), + std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval + nullptr, // stats + std::make_shared<ConnectionEvents>(this)) + .get(); + connectingSecurely->complete(); + failedConnectionAttempts_ = 0; +} + +void FlipperConnectionManagerImpl::reconnect() { + if (!isStarted_) { + log("Not started"); + return; + } + folly::makeFuture() + .via(flipperEventBase_->getEventBase()) + .delayed(std::chrono::seconds(reconnectIntervalSeconds)) + .thenValue([this](auto&&) { startSync(); }); +} + +void FlipperConnectionManagerImpl::stop() { + if (!isStarted_) { + log("Not started"); + return; + } + isStarted_ = false; + + if (client_) { + client_->disconnect(); + } + client_ = nullptr; +} + +bool FlipperConnectionManagerImpl::isOpen() const { + return isOpen_ && connectionIsTrusted_; +} + +void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) { + callbacks_ = callbacks; +} + +void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) { + flipperEventBase_->add([this, message]() { + try { + rsocket::Payload payload = toRSocketPayload(message); + if (client_) { + client_->getRequester() + ->fireAndForget(std::move(payload)) + ->subscribe([]() {}); + } + } catch (std::length_error& e) { + // Skip sending messages that are too large. + log(e.what()); + return; + } + }); +} + +void FlipperConnectionManagerImpl::onMessageReceived( + const folly::dynamic& message, + std::unique_ptr<FlipperResponder> responder) { + callbacks_->onMessageReceived(message, std::move(responder)); +} + +bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() { + if (failedConnectionAttempts_ >= 2) { + return true; + } + + auto step = flipperState_->start("Check required certificates are present"); + bool hasRequiredFiles = contextStore_->hasRequiredFiles(); + if (hasRequiredFiles) { + step->complete(); + } + return !hasRequiredFiles; +} + +void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() { + auto generatingCSR = flipperState_->start("Generate CSR"); + std::string csr = contextStore_->getCertificateSigningRequest(); + generatingCSR->complete(); + + folly::dynamic message = + folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())( + "destination", contextStore_->getCertificateDirectoryPath().c_str()); + auto gettingCert = flipperState_->start("Getting cert from desktop"); + + flipperEventBase_->add([this, message, gettingCert]() { + client_->getRequester() + ->requestResponse(rsocket::Payload(folly::toJson(message))) + ->subscribe( + [this, gettingCert](rsocket::Payload p) { + auto response = p.moveDataToString(); + if (!response.empty()) { + folly::dynamic config = folly::parseJson(response); + contextStore_->storeConnectionConfig(config); + } + gettingCert->complete(); + log("Certificate exchange complete."); + // Disconnect after message sending is complete. + // This will trigger a reconnect which should use the secure + // channel. + // TODO: Connect immediately, without waiting for reconnect + client_ = nullptr; + }, + [this, message, gettingCert](folly::exception_wrapper e) { + e.handle( + [&](rsocket::ErrorWithPayload& errorWithPayload) { + std::string errorMessage = + errorWithPayload.payload.moveDataToString(); + + if (errorMessage.compare("not implemented")) { + auto error = + "Desktop failed to provide certificates. Error from flipper desktop:\n" + + errorMessage; + log(error); + gettingCert->fail(error); + client_ = nullptr; + } else { + sendLegacyCertificateRequest(message); + } + }, + [e, gettingCert](...) { + gettingCert->fail(e.what().c_str()); + }); + }); + }); + failedConnectionAttempts_ = 0; +} + +void FlipperConnectionManagerImpl::sendLegacyCertificateRequest( + folly::dynamic message) { + // Desktop is using an old version of Flipper. + // Fall back to fireAndForget, instead of requestResponse. + auto sendingRequest = + flipperState_->start("Sending fallback certificate request"); + client_->getRequester() + ->fireAndForget(rsocket::Payload(folly::toJson(message))) + ->subscribe([this, sendingRequest]() { + sendingRequest->complete(); + folly::dynamic config = folly::dynamic::object(); + contextStore_->storeConnectionConfig(config); + client_ = nullptr; + }); +} + +bool FlipperConnectionManagerImpl::isRunningInOwnThread() { + return flipperEventBase_->isInEventBaseThread(); +} + +rsocket::Payload toRSocketPayload(dynamic data) { + std::string json = folly::toJson(data); + rsocket::Payload payload = rsocket::Payload(json); + auto payloadLength = payload.data->computeChainDataLength(); + if (payloadLength > maxPayloadSize) { + auto logMessage = + std::string( + "Error: Skipping sending message larger than max rsocket payload: ") + + json.substr(0, 100) + "..."; + log(logMessage); + throw std::length_error(logMessage); + } + + return payload; +} + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h new file mode 100644 index 000000000..9763748ce --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/Executor.h> +#include <folly/io/async/EventBase.h> +#include <rsocket/RSocket.h> +#include <mutex> +#include "FlipperConnectionManager.h" +#include "FlipperInitConfig.h" +#include "FlipperState.h" + +namespace facebook { +namespace flipper { + +class ConnectionEvents; +class ConnectionContextStore; +class FlipperRSocketResponder; + +rsocket::Payload toRSocketPayload(folly::dynamic data); + +class FlipperConnectionManagerImpl : public FlipperConnectionManager { + friend ConnectionEvents; + + public: + FlipperConnectionManagerImpl( + FlipperInitConfig config, + std::shared_ptr<FlipperState> state, + std::shared_ptr<ConnectionContextStore> contextStore); + + ~FlipperConnectionManagerImpl(); + + void start() override; + + void stop() override; + + bool isOpen() const override; + + void setCallbacks(Callbacks* callbacks) override; + + void sendMessage(const folly::dynamic& message) override; + + void onMessageReceived( + const folly::dynamic& message, + std::unique_ptr<FlipperResponder> responder) override; + + void reconnect(); + + private: + bool isOpen_ = false; + bool isStarted_ = false; + Callbacks* callbacks_; + DeviceData deviceData_; + std::shared_ptr<FlipperState> flipperState_; + int insecurePort; + int securePort; + + folly::EventBase* flipperEventBase_; + folly::EventBase* connectionEventBase_; + std::unique_ptr<rsocket::RSocketClient> client_; + bool connectionIsTrusted_; + int failedConnectionAttempts_ = 0; + std::shared_ptr<ConnectionContextStore> contextStore_; + + void startSync(); + void doCertificateExchange(); + void connectSecurely(); + bool isCertificateExchangeNeeded(); + void requestSignedCertFromFlipper(); + bool isRunningInOwnThread(); + void sendLegacyCertificateRequest(folly::dynamic message); + std::string getDeviceId(); +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperInitConfig.h b/ios/Pods/Flipper/xplat/Flipper/FlipperInitConfig.h new file mode 100644 index 000000000..378367a5d --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperInitConfig.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/io/async/EventBase.h> +#include <map> + +namespace facebook { +namespace flipper { + +struct DeviceData { + std::string host; + std::string os; + std::string device; + std::string deviceId; + std::string app; + std::string appId; + std::string privateAppDirectory; +}; + +struct FlipperInitConfig { + /** + Map of client specific configuration data such as app name, device name, etc. + */ + DeviceData deviceData; + + /** + EventBase on which client callbacks should be called. + */ + folly::EventBase* callbackWorker; + + /** + EventBase to be used to maintain the network connection. + */ + folly::EventBase* connectionWorker; + + int insecurePort = 8089; + int securePort = 8088; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperPlugin.h b/ios/Pods/Flipper/xplat/Flipper/FlipperPlugin.h new file mode 100644 index 000000000..5f96aea16 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperPlugin.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <string> +#include "FlipperConnection.h" + +namespace facebook { +namespace flipper { + +class FlipperPlugin { + public: + virtual ~FlipperPlugin() {} + + /** + The plugin's identifier. This should map to a javascript plugin + with the same identifier to ensure messages are sent correctly. + */ + virtual std::string identifier() const = 0; + + /** + Called when a connection has been established between this plugin + and the corresponding plugin on the Flipper desktop app. The provided + connection can be used to register method receivers as well as send + messages back to the desktop app. + */ + virtual void didConnect(std::shared_ptr<FlipperConnection> conn) = 0; + + /** + Called when a plugin has been disconnected and the FlipperConnection + provided in didConnect is no longer valid to use. + */ + virtual void didDisconnect() = 0; + + /** + Returns true if the plugin is meant to be run in background too, otherwise it + returns false. + */ + virtual bool runInBackground() { + return false; + } +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.cpp new file mode 100644 index 000000000..d5e03cd48 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperRSocketResponder.h" +#include <folly/json.h> +#include <rsocket/RSocket.h> +#include "FireAndForgetBasedFlipperResponder.h" +#include "FlipperConnectionManagerImpl.h" +#include "FlipperResponderImpl.h" +#include "Log.h" + +using folly::dynamic; + +namespace facebook { +namespace flipper { + +rsocket::Payload toRSocketPayload(dynamic data); + +void FlipperRSocketResponder::handleFireAndForget( + rsocket::Payload request, + rsocket::StreamId streamId) { + const auto payload = request.moveDataToString(); + std::unique_ptr<FireAndForgetBasedFlipperResponder> responder; + auto message = folly::parseJson(payload); + auto idItr = message.find("id"); + if (idItr == message.items().end()) { + responder = + std::make_unique<FireAndForgetBasedFlipperResponder>(websocket_); + } else { + responder = std::make_unique<FireAndForgetBasedFlipperResponder>( + websocket_, idItr->second.getInt()); + } + + websocket_->onMessageReceived( + folly::parseJson(payload), std::move(responder)); +} + +std::shared_ptr<yarpl::single::Single<rsocket::Payload>> +FlipperRSocketResponder::handleRequestResponse( + rsocket::Payload request, + rsocket::StreamId streamId) { + const auto requestString = request.moveDataToString(); + + auto dynamicSingle = yarpl::single::Single<folly::dynamic>::create( + [payload = std::move(requestString), this](auto observer) { + auto responder = std::make_unique<FlipperResponderImpl>(observer); + websocket_->onMessageReceived( + folly::parseJson(payload), std::move(responder)); + }); + + auto rsocketSingle = yarpl::single::Single<rsocket::Payload>::create( + [payload = std::move(requestString), dynamicSingle, this](auto observer) { + observer->onSubscribe(yarpl::single::SingleSubscriptions::empty()); + dynamicSingle->subscribe( + [observer, this](folly::dynamic d) { + eventBase_->runInEventBaseThread([observer, d]() { + try { + observer->onSuccess(toRSocketPayload(d)); + + } catch (std::exception& e) { + log(e.what()); + observer->onError(e); + } + }); + }, + [observer, this](folly::exception_wrapper e) { + eventBase_->runInEventBaseThread( + [observer, e]() { observer->onError(e); }); + }); + }); + + return rsocketSingle; +} + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.h b/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.h new file mode 100644 index 000000000..1b020a727 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperRSocketResponder.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include <rsocket/RSocketResponder.h> + +namespace facebook { +namespace flipper { + +class FlipperConnectionManager; + +class FlipperRSocketResponder : public rsocket::RSocketResponder { + private: + FlipperConnectionManager* websocket_; + folly::EventBase* eventBase_; + + public: + FlipperRSocketResponder( + FlipperConnectionManager* websocket, + folly::EventBase* eventBase) + : websocket_(websocket), eventBase_(eventBase){}; + + void handleFireAndForget( + rsocket::Payload request, + rsocket::StreamId streamId); + + std::shared_ptr<yarpl::single::Single<rsocket::Payload>> + handleRequestResponse(rsocket::Payload request, rsocket::StreamId streamId); +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperResponder.h b/ios/Pods/Flipper/xplat/Flipper/FlipperResponder.h new file mode 100644 index 000000000..f7ce36bf8 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperResponder.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/json.h> + +namespace facebook { +namespace flipper { + +/** + * FlipperResponder is used to asynchronously respond to messages + * received from the Flipper desktop app. + */ +class FlipperResponder { + public: + virtual ~FlipperResponder(){}; + + /** + * Deliver a successful response to the Flipper desktop app. + */ + virtual void success(const folly::dynamic& response) = 0; + + /** + * Inform the Flipper desktop app of an error in handling the request. + */ + virtual void error(const folly::dynamic& response) = 0; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperResponderImpl.h b/ios/Pods/Flipper/xplat/Flipper/FlipperResponderImpl.h new file mode 100644 index 000000000..8c35ab9c3 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperResponderImpl.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <folly/io/async/EventBase.h> +#include <folly/json.h> +#include <rsocket/RSocketResponder.h> +#include "FlipperConnectionManager.h" +#include "FlipperResponder.h" +#include "Log.h" + +namespace facebook { +namespace flipper { + +/* Responder to encapsulate yarpl observables and hide them from flipper core + + * plugins */ +class FlipperResponderImpl : public FlipperResponder { + public: + FlipperResponderImpl( + std::shared_ptr<yarpl::single::SingleObserver<folly::dynamic>> + downstreamObserver) + : downstreamObserver_(downstreamObserver) {} + + void success(const folly::dynamic& response) override { + const folly::dynamic message = folly::dynamic::object("success", response); + isCompleted = true; + downstreamObserver_->onSuccess(message); + } + + void error(const folly::dynamic& response) override { + const folly::dynamic message = folly::dynamic::object("error", response); + isCompleted = true; + downstreamObserver_->onSuccess(message); + } + + ~FlipperResponderImpl() { + if (!isCompleted) { + try { + downstreamObserver_->onSuccess( + folly::dynamic::object("success", folly::dynamic::object())); + } catch (std::exception& e) { + log(std::string( + "Exception occurred when responding in FlipperResponder: ") + + e.what()); + } catch (...) { + log("Exception occurred when responding in FlipperResponder"); + } + } + } + + private: + std::shared_ptr<yarpl::single::SingleObserver<folly::dynamic>> + downstreamObserver_; + bool isCompleted = false; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperState.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperState.cpp new file mode 100644 index 000000000..81426ce9c --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperState.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperState.h" +#include <vector> +#include "FlipperStateUpdateListener.h" +#include "FlipperStep.h" + +#if FLIPPER_DEBUG_LOG +#include "Log.h" +#endif + +using namespace facebook::flipper; + +/* Class responsible for collecting state updates and combining them into a + * view of the current state of the flipper client. */ + +FlipperState::FlipperState() : logs("") {} +void FlipperState::setUpdateListener( + std::shared_ptr<FlipperStateUpdateListener> listener) { + std::lock_guard<std::mutex> lock(mutex); + mListener = listener; +} + +void FlipperState::started(std::string step) { + std::shared_ptr<FlipperStateUpdateListener> localListener; + { + std::lock_guard<std::mutex> lock(mutex); +#if FLIPPER_DEBUG_LOG + log("[started] " + step); +#endif + if (stateMap.find(step) == stateMap.end()) { + insertOrder.push_back(step); + } + stateMap[step] = State::in_progress; + localListener = mListener; + } + // Need to drop the lock before issuing callback because the caller + // might call us back (and is responsible for their own locking). + if (localListener) { + localListener->onUpdate(); + } +} + +void FlipperState::success(std::string step) { + std::shared_ptr<FlipperStateUpdateListener> localListener; + { + std::lock_guard<std::mutex> lock(mutex); +#if FLIPPER_DEBUG_LOG + log("[finished] " + step); +#endif + logs = logs + "[Success] " + step + "\n"; + stateMap[step] = State::success; + localListener = mListener; + } + // Need to drop the lock before issuing callback because the caller + // might call us back (and is responsible for their own locking). + if (localListener) { + localListener->onUpdate(); + } +} + +void FlipperState::failed(std::string step, std::string errorMessage) { + std::shared_ptr<FlipperStateUpdateListener> localListener; + { + std::lock_guard<std::mutex> lock(mutex); + std::string message = "[Failed] " + step + ": " + errorMessage; +#if FLIPPER_DEBUG_LOG + log(message); +#endif + logs = logs + message + "\n"; + stateMap[step] = State::failed; + localListener = mListener; + } + // Need to drop the lock before issuing callback because the caller + // might call us back (and is responsible for their own locking). + if (localListener) { + localListener->onUpdate(); + } +} + +// TODO: Currently returns string, but should really provide a better +// representation of the current state so the UI can show it in a more intuitive +// way +std::string FlipperState::getState() { + std::lock_guard<std::mutex> lock(mutex); + return logs; +} + +std::vector<StateElement> FlipperState::getStateElements() { + std::lock_guard<std::mutex> lock(mutex); + std::vector<StateElement> v; + for (auto stepName : insertOrder) { + v.push_back(StateElement(stepName, stateMap[stepName])); + } + return v; +} + +std::shared_ptr<FlipperStep> FlipperState::start(std::string step_name) { + // started() acquires the lock and we don't access any of our members below, + // so we needn't take the lock. + started(step_name); + return std::make_shared<FlipperStep>(step_name, this); +} diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperState.h b/ios/Pods/Flipper/xplat/Flipper/FlipperState.h new file mode 100644 index 000000000..64e853e66 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperState.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <map> +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +class FlipperStep; +class FlipperStateUpdateListener; + +namespace facebook { +namespace flipper { + +enum State { success, in_progress, failed }; + +class StateElement { + public: + StateElement(std::string name, State state) : name_(name), state_(state){}; + std::string name_; + State state_; +}; + +} // namespace flipper +} // namespace facebook + +class FlipperState { + friend FlipperStep; + + public: + FlipperState(); + // Update listeners are responsible for their own + // synchronization. There is no guarantee about which thread they + // may be called on. + void setUpdateListener(std::shared_ptr<FlipperStateUpdateListener>); + std::string getState(); + std::vector<facebook::flipper::StateElement> getStateElements(); + + /* To record a state update, call start() with the name of the step to get a + FlipperStep object. Call complete on this to register it successful, + the absense of the completion call when it is destructed will register as a + step failure. */ + std::shared_ptr<FlipperStep> start(std::string step); + + private: + void success(std::string); + void failed(std::string, std::string); + void started(std::string); + + std::mutex mutex; // Protects all our member variables. + std::shared_ptr<FlipperStateUpdateListener> mListener = nullptr; + std::string logs; + std::vector<std::string> insertOrder; + std::map<std::string, facebook::flipper::State> stateMap; +}; diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperStateUpdateListener.h b/ios/Pods/Flipper/xplat/Flipper/FlipperStateUpdateListener.h new file mode 100644 index 000000000..3d3d5f64c --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperStateUpdateListener.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +class FlipperStateUpdateListener { + public: + virtual ~FlipperStateUpdateListener() = default; + virtual void onUpdate() = 0; +}; diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperStep.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperStep.cpp new file mode 100644 index 000000000..c5e7a8a7d --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperStep.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperStep.h" +#include "FlipperState.h" +#include "Log.h" + +using facebook::flipper::log; + +void FlipperStep::complete() { + isLogged = true; + state->success(name); +} + +void FlipperStep::fail(std::string message) { + isLogged = true; + state->failed(name, message); +} + +FlipperStep::FlipperStep(std::string step, FlipperState* s) { + state = s; + name = step; +} + +FlipperStep::~FlipperStep() { + if (!isLogged) { + try { + state->failed(name, ""); + } catch (std::exception& e) { + log(std::string("Exception occurred in FlipperStep destructor: ") + + e.what()); + } catch (...) { + log("Exception occurred in FlipperStep destructor"); + } + } +} diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperStep.h b/ios/Pods/Flipper/xplat/Flipper/FlipperStep.h new file mode 100644 index 000000000..72dd20ec0 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperStep.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <string> + +class FlipperState; + +class FlipperStep { + public: + /* Mark this step as completed successfully + * failing to call complete() will be registered as a failure + * when the destructor is executed. */ + void complete(); + + // Mark the step as failed, and provide a message. + void fail(std::string message); + + FlipperStep(std::string name, FlipperState* state); + ~FlipperStep(); + + private: + std::string name; + bool isLogged = false; + FlipperState* state; +}; diff --git a/ios/Pods/Flipper/xplat/Flipper/Log.cpp b/ios/Pods/Flipper/xplat/Flipper/Log.cpp new file mode 100644 index 000000000..5332918df --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/Log.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Log.h" + +#ifdef __ANDROID__ +#include <android/log.h> +#endif + +namespace facebook { +namespace flipper { + +void log(const std::string& message) { +#ifdef __ANDROID__ + __android_log_print( + ANDROID_LOG_INFO, "flipper", "flipper: %s", message.c_str()); +#else + printf("flipper: %s\n", message.c_str()); +#endif +} + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/Log.h b/ios/Pods/Flipper/xplat/Flipper/Log.h new file mode 100644 index 000000000..ce181ca66 --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/Log.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include <string> + +namespace facebook { +namespace flipper { + +void log(const std::string& message); + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/Flipper/xplat/Flipper/utils/CallstackHelper.h b/ios/Pods/Flipper/xplat/Flipper/utils/CallstackHelper.h new file mode 100644 index 000000000..149a62f6e --- /dev/null +++ b/ios/Pods/Flipper/xplat/Flipper/utils/CallstackHelper.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#include <dlfcn.h> +#include <unwind.h> +#include <iomanip> + +namespace facebook { +namespace flipper { +// TODO: T39093653, Replace the backtrace implementation with folly +// implementation. Didn't use the backtrace() c function as it was not found in +// NDK. +struct BacktraceState { + void** current; + void** end; +}; + +static _Unwind_Reason_Code unwindCallback( + struct _Unwind_Context* context, + void* arg) { + BacktraceState* state = static_cast<BacktraceState*>(arg); + uintptr_t pc = _Unwind_GetIP(context); + if (pc) { + if (state->current == state->end) { + return _URC_END_OF_STACK; + } else { + *state->current++ = reinterpret_cast<void*>(pc); + } + } + return _URC_NO_REASON; +} + +static size_t captureBacktrace(void** buffer, size_t max) { + BacktraceState state = {buffer, buffer + max}; + _Unwind_Backtrace(unwindCallback, &state); + + return state.current - buffer; +} + +static void dumpBacktrace(std::ostream& os, void** buffer, size_t count) { + for (size_t idx = 0; idx < count; ++idx) { + const void* addr = buffer[idx]; + const char* symbol = ""; + + Dl_info info; + if (dladdr(addr, &info) && info.dli_sname) { + symbol = info.dli_sname; + } + + os << " #" << std::setw(2) << idx << ": " << addr << " " << symbol + << "\n"; + } +} + +} // namespace flipper +} // namespace facebook + +#endif diff --git a/ios/Pods/FlipperKit/LICENSE b/ios/Pods/FlipperKit/LICENSE new file mode 100644 index 000000000..b96dcb048 --- /dev/null +++ b/ios/Pods/FlipperKit/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/Pods/FlipperKit/README.md b/ios/Pods/FlipperKit/README.md new file mode 100644 index 000000000..47df05320 --- /dev/null +++ b/ios/Pods/FlipperKit/README.md @@ -0,0 +1,139 @@ +# Flipper [![Build Status](https://travis-ci.org/facebook/flipper.svg?branch=master)](https://travis-ci.org/facebook/flipper) [![Android Maven Badge](https://img.shields.io/maven-metadata/v/https/jcenter.bintray.com/com/facebook/flipper/flipper/maven-metadata.xml.svg?color=green&label=android)](https://bintray.com/facebook/maven/com.facebook.flipper%3Aflipper) [![iOS](https://img.shields.io/cocoapods/v/FlipperKit.svg?label=iOS&color=blue)](https://cocoapods.org/pods/Flipper) [![Greenkeeper badge](https://badges.greenkeeper.io/facebook/flipper.svg)](https://greenkeeper.io/) + +Flipper (formerly Sonar) is a platform for debugging mobile apps on iOS and Android. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API. + +![Flipper](/docs/assets/layout.png) + +## Table of Contents + +- [Mobile development](#mobile-development) +- [Extending Flipper](#extending-flipper) +- [Contributing to Flipper](#contributing-to-flipper) +- [In this repo](#in-this-repo) +- [Getting started](#getting-started) + - [Requirements](#requirements) +- [Building from Source](#building-from-source) + - [Desktop](#desktop) + - [Running from source](#running-from-source) + - [Building standalone application](#building-standalone-application) +- [iOS SDK + Sample App](#ios-sdk--sample-app) +- [Android SDK + Sample app](#android-sdk--sample-app) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) + +## Mobile development + +Flipper aims to be your number one companion for mobile app development on iOS and Android. Therefore, we provide a bunch of useful tools, including a log viewer, interactive layout inspector, and network inspector. + +## Extending Flipper + +Flipper is built as a platform. In addition to using the tools already included, you can create your own plugins to visualize and debug data from your mobile apps. Flipper takes care of sending data back and forth, calling functions, and listening for events on the mobile app. + +## Contributing to Flipper + +Both Flipper's desktop app and native mobile SDKs are open-source and MIT licensed. This enables you to see and understand how we are building plugins, and of course, join the community and help improve Flipper. We are excited to see what you will build on this platform. + +# In this repo + +This repository includes all parts of Flipper. This includes: + +* Flipper's desktop app built using [Electron](https://electronjs.org) (`/src`) +* native Flipper SDKs for iOS (`/iOS`) +* native Flipper SDKs for Android (`/android`) +* Plugins: + * Logs (`/src/device-plugins/logs`) + * Layout inspector (`/src/plugins/layout`) + * Network inspector (`/src/plugins/network`) + * Shared Preferences/NSUserDefaults inspector (`/src/plugins/shared_preferences`) +* website and documentation (`/website` / `/docs`) + +# Getting started + +Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started.html) to set up Flipper. + +## Requirements + +* node >= 8 +* yarn >= 1.5 +* iOS developer tools (for developing iOS plugins) +* Android SDK and adb + +# Building from Source + +## Desktop +### Running from source + +``` +git clone https://github.com/facebook/flipper.git +cd flipper +yarn +yarn start +``` + +NOTE: If you're on Windows, you need to use Yarn 1.5.1 until [this issue](https://github.com/yarnpkg/yarn/issues/6048) is resolved. + +### Building standalone application + +Provide either `--mac`, `--win`, `--linux` or any combination of them +to `yarn build` to build a release zip file for the given platform(s). E.g. + +``` +yarn build --mac --version $buildNumber +``` + +You can find the resulting artifact in the `dist/` folder. + +## iOS SDK + Sample App + +``` +cd iOS/Sample +rm -f Podfile.lock +pod install --repo-update +open Sample.xcworkspace +<Run app from xcode> +``` + +You can omit `--repo-update` to speed up the installation, but watch out as you may be building against outdated dependencies. + +## Android SDK + Sample app + +Start up an android emulator and run the following in the project root: +``` +./gradlew :sample:installDebug +``` + +## React Native SDK + Sample app + +``` +cd react-native/ReactNativeFlipperExample +yarn +yarn android +``` + +Note that the first 2 steps need to be done only once. + +Alternatively, the app can be started on `iOS` by running `yarn ios`. + +#### Troubleshooting + +Older yarn versions might show an error / hang with the message 'Waiting for the other yarn instance to finish'. If that happens, run the command `yarn` first separately in the directory `react-native/react-native-flipper`. + +## Documentation + +Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/docs). + +Our documentation is built with [Docusaurus](https://docusaurus.io/). You can build +it locally by running this: + +```bash +cd website +yarn +yarn start +``` + +## Contributing +See the [CONTRIBUTING](/CONTRIBUTING.md) file for how to help out. + +## License +Flipper is MIT licensed, as found in the [LICENSE](/LICENSE) file. diff --git a/ios/Pods/FlipperKit/iOS/FBDefines/FBDefines.h b/ios/Pods/FlipperKit/iOS/FBDefines/FBDefines.h new file mode 100644 index 000000000..d0f6541ad --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FBDefines/FBDefines.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef FB_SK_MACROS_H +#define FB_SK_MACROS_H +#define FB_LINK_REQUIRE_(NAME) +#define FB_LINKABLE(NAME) +#define FB_LINK_REQUIRE_CATEGORY(NAME) +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.h b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.h new file mode 100644 index 000000000..511c57bfd --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Flipper/FlipperConnection.h> +#import <FlipperKit/FlipperConnection.h> + +/** +FlipperCppBridgingConnection is a simple ObjC wrapper around SonarConnection +that forwards messages to the underlying C++ connection. This class allows +pure Objective-C plugins to send messages to the underlying connection. +*/ +@interface FlipperCppBridgingConnection : NSObject<FlipperConnection> +- (instancetype)initWithCppConnection: + (std::shared_ptr<facebook::flipper::FlipperConnection>)conn; +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.mm b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.mm new file mode 100644 index 000000000..711d89ae4 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.mm @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FlipperCppBridgingConnection.h" + +#import <FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h> + +#import "FlipperCppBridgingResponder.h" + +@implementation FlipperCppBridgingConnection { + std::shared_ptr<facebook::flipper::FlipperConnection> conn_; +} + +- (instancetype)initWithCppConnection: + (std::shared_ptr<facebook::flipper::FlipperConnection>)conn { + if (self = [super init]) { + conn_ = conn; + } + return self; +} + +#pragma mark - SonarConnection + +- (void)send:(NSString*)method withParams:(NSDictionary*)params { + conn_->send( + [method UTF8String], + facebook::cxxutils::convertIdToFollyDynamic(params, true)); +} + +- (void)receive:(NSString*)method withBlock:(SonarReceiver)receiver { + const auto lambda = + [receiver]( + const folly::dynamic& message, + std::shared_ptr<facebook::flipper::FlipperResponder> responder) { + @autoreleasepool { + FlipperCppBridgingResponder* const objCResponder = + [[FlipperCppBridgingResponder alloc] + initWithCppResponder:responder]; + receiver( + facebook::cxxutils::convertFollyDynamicToId(message), + objCResponder); + } + }; + conn_->receive([method UTF8String], lambda); +} + +- (void)errorWithMessage:(NSString*)message stackTrace:(NSString*)stacktrace { + conn_->error([message UTF8String], [stacktrace UTF8String]); +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.h b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.h new file mode 100644 index 000000000..24fccecbf --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Flipper/FlipperResponder.h> +#import <FlipperKit/FlipperResponder.h> + +/** +SonarCppBridgingResponder is a simple ObjC wrapper around FlipperResponder +that forwards messages to the underlying C++ responder. This class allows +pure Objective-C plugins to send messages to the underlying responder. +*/ +@interface FlipperCppBridgingResponder : NSObject<FlipperResponder> +- (instancetype)initWithCppResponder: + (std::shared_ptr<facebook::flipper::FlipperResponder>)responder; +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.mm b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.mm new file mode 100644 index 000000000..0760aa1e6 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.mm @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FlipperCppBridgingResponder.h" + +#import <FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h> + +@implementation FlipperCppBridgingResponder { + std::shared_ptr<facebook::flipper::FlipperResponder> responder_; +} + +- (instancetype)initWithCppResponder: + (std::shared_ptr<facebook::flipper::FlipperResponder>)responder { + if (!responder) { + return nil; + } + + if (self = [super init]) { + responder_ = responder; + } + + return self; +} + +#pragma mark - FlipperResponder + +- (void)success:(NSDictionary*)response { + responder_->success( + facebook::cxxutils::convertIdToFollyDynamic(response, true)); +} + +- (void)error:(NSDictionary*)response { + responder_->error( + facebook::cxxutils::convertIdToFollyDynamic(response, true)); +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h new file mode 100644 index 000000000..fce252a48 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef __OBJC__ +#error This header can only be included in .mm (ObjC++) files +#endif + +#import <Flipper/FlipperPlugin.h> +#import <FlipperKit/CppBridge/FlipperCppBridgingConnection.h> +#import <FlipperKit/FlipperPlugin.h> + +namespace facebook { +namespace flipper { + +using ObjCPlugin = NSObject<FlipperPlugin>*; + +/** +SonarCppWrapperPlugin is a simple C++ wrapper around Objective-C Sonar plugins +that can be passed to SonarClient. This class allows developers to write pure +Objective-C plugins if they want. +*/ +class FlipperCppWrapperPlugin final : public facebook::flipper::FlipperPlugin { + public: + // Under ARC copying objCPlugin *does* increment its retain count + FlipperCppWrapperPlugin(ObjCPlugin objCPlugin) : _objCPlugin(objCPlugin) {} + + std::string identifier() const override { + return [[_objCPlugin identifier] UTF8String]; + } + + void didConnect( + std::shared_ptr<facebook::flipper::FlipperConnection> conn) override { + FlipperCppBridgingConnection* const bridgingConn = + [[FlipperCppBridgingConnection alloc] initWithCppConnection:conn]; + [_objCPlugin didConnect:bridgingConn]; + } + + void didDisconnect() override { + [_objCPlugin didDisconnect]; + } + + bool runInBackground() override { + if ([_objCPlugin respondsToSelector:@selector(runInBackground)]) { + return [_objCPlugin runInBackground]; + } + return false; + } + + ObjCPlugin getObjCPlugin() { + return _objCPlugin; + } + + private: + ObjCPlugin _objCPlugin; +}; + +} // namespace flipper +} // namespace facebook diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h new file mode 100644 index 000000000..c90f6c1c7 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#import <Foundation/Foundation.h> + +#include <folly/dynamic.h> + +namespace facebook { +namespace cxxutils { + +folly::dynamic convertIdToFollyDynamic(id json, bool nullifyNanAndInf = false); +id convertFollyDynamicToId(const folly::dynamic& dyn); + +} // namespace cxxutils +} // namespace facebook diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.mm b/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.mm new file mode 100644 index 000000000..addadf67f --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.mm @@ -0,0 +1,138 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FBCxxFollyDynamicConvert.h" + +#import <objc/runtime.h> + +namespace facebook { +namespace cxxutils { + +/* + * The implementation is taken from RCTFollyConvert(https://fburl.com/vzw8ql2q) + */ + +id convertFollyDynamicToId(const folly::dynamic& dyn) { + // I could imagine an implementation which avoids copies by wrapping the + // dynamic in a derived class of NSDictionary. We can do that if profiling + // implies it will help. + + switch (dyn.type()) { + case folly::dynamic::NULLT: + return (id)kCFNull; + case folly::dynamic::BOOL: + return dyn.getBool() ? @YES : @NO; + case folly::dynamic::INT64: + return @(dyn.getInt()); + case folly::dynamic::DOUBLE: + return @(dyn.getDouble()); + case folly::dynamic::STRING: + return [[NSString alloc] initWithBytes:dyn.c_str() + length:dyn.size() + encoding:NSUTF8StringEncoding]; + case folly::dynamic::ARRAY: { + NSMutableArray* array = + [[NSMutableArray alloc] initWithCapacity:dyn.size()]; + for (auto& elem : dyn) { + id obj = convertFollyDynamicToId(elem); + if (obj) { + [array addObject:obj]; + } + } + return array; + } + case folly::dynamic::OBJECT: { + NSMutableDictionary* dict = + [[NSMutableDictionary alloc] initWithCapacity:dyn.size()]; + for (auto& elem : dyn.items()) { + id obj = convertFollyDynamicToId(elem.second); + if (obj) { + dict[convertFollyDynamicToId(elem.first)] = obj; + } + } + return dict; + } + } +} + +folly::dynamic convertIdToFollyDynamic(id json, bool nullifyNanAndInf) { + if (json == nil || json == (id)kCFNull) { + return nullptr; + } else if ([json isKindOfClass:[NSNumber class]]) { + const char* objCType = [json objCType]; + switch (objCType[0]) { + // This is a c++ bool or C99 _Bool. On some platforms, BOOL is a bool. + case _C_BOOL: + return (bool)[json boolValue]; + case _C_CHR: + // On some platforms, objc BOOL is a signed char, but it + // might also be a small number. Use the same hack JSC uses + // to distinguish them: + // https://phabricator.intern.facebook.com/diffusion/FBS/browse/master/fbobjc/xplat/third-party/jsc/safari-600-1-4-17/JavaScriptCore/API/JSValue.mm;b8ee03916489f8b12143cd5c0bca546da5014fc9$901 + if ([json isKindOfClass:[@YES class]]) { + return (bool)[json boolValue]; + } else { + const auto value = [json longLongValue]; + if (nullifyNanAndInf && (isnan(value) || isinf(value))) { + return nullptr; + } + return value; + } + case _C_UCHR: + case _C_SHT: + case _C_USHT: + case _C_INT: + case _C_UINT: + case _C_LNG: + case _C_ULNG: + case _C_LNG_LNG: + case _C_ULNG_LNG: { + const auto value = [json longLongValue]; + if (nullifyNanAndInf && (isnan(value) || isinf(value))) { + return nullptr; + } + return value; + } + + case _C_FLT: + case _C_DBL: { + const auto value = [json doubleValue]; + if (nullifyNanAndInf && (isnan(value) || isinf(value))) { + return nullptr; + } + return value; + } + + // default: + // fall through + } + } else if ([json isKindOfClass:[NSString class]]) { + NSData* data = [json dataUsingEncoding:NSUTF8StringEncoding]; + return std::string(reinterpret_cast<const char*>(data.bytes), data.length); + } else if ([json isKindOfClass:[NSArray class]]) { + folly::dynamic array = folly::dynamic::array; + for (id element in json) { + array.push_back(convertIdToFollyDynamic(element, nullifyNanAndInf)); + } + return array; + } else if ([json isKindOfClass:[NSDictionary class]]) { + __block folly::dynamic object = folly::dynamic::object(); + + [json enumerateKeysAndObjectsUsingBlock:^( + NSString* key, NSString* value, __unused BOOL* stop) { + object.insert( + convertIdToFollyDynamic(key, nullifyNanAndInf), + convertIdToFollyDynamic(value, nullifyNanAndInf)); + }]; + + return object; + } + + return nil; +} +} +} diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h new file mode 100644 index 000000000..fd8d131a3 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +#define FBPFTrace(...) /*NSLog(__VA_ARGS__)*/ +#define FBPFLog(...) NSLog(__VA_ARGS__) + +enum { + FKPortForwardingFrameTypeOpenPipe = 201, + FKPortForwardingFrameTypeWriteToPipe = 202, + FKPortForwardingFrameTypeClosePipe = 203, +}; + +static dispatch_data_t NSDataToGCDData(NSData* data) { + __block NSData* retainedData = data; + return dispatch_data_create(data.bytes, data.length, nil, ^{ + retainedData = nil; + }); +} diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h new file mode 100644 index 000000000..764953cf7 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@interface FKPortForwardingServer : NSObject + +- (instancetype)init; + +- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port; +- (void)forwardConnectionsFromPort:(NSUInteger)port; +- (void)close; + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m new file mode 100644 index 000000000..05dbed9bc --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.m @@ -0,0 +1,232 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FKPortForwardingServer.h" + +#import <UIKit/UIKit.h> + +#import <CocoaAsyncSocket/GCDAsyncSocket.h> +#import <peertalk/PTChannel.h> + +#import "FKPortForwardingCommon.h" + +@interface FKPortForwardingServer ()< + PTChannelDelegate, + GCDAsyncSocketDelegate> { + __weak PTChannel* _serverChannel; + __weak PTChannel* _peerChannel; + + GCDAsyncSocket* _serverSocket; + NSMutableDictionary* _clientSockets; + UInt32 _lastClientSocketTag; + dispatch_queue_t _socketQueue; + PTProtocol* _protocol; +} + +@end + +@implementation FKPortForwardingServer + +- (instancetype)init { + if (self = [super init]) { + _socketQueue = + dispatch_queue_create("FKPortForwardingServer", DISPATCH_QUEUE_SERIAL); + _lastClientSocketTag = 0; + _clientSockets = [NSMutableDictionary dictionary]; + _protocol = [[PTProtocol alloc] initWithDispatchQueue:_socketQueue]; + } + return self; +} + +- (void)dealloc { + [self close]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)forwardConnectionsFromPort:(NSUInteger)port { + [self _forwardConnectionsFromPort:port reportError:YES]; + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification* note) { + [self _forwardConnectionsFromPort:port reportError:NO]; + }]; +} + +- (void)_forwardConnectionsFromPort:(NSUInteger)port + reportError:(BOOL)shouldReportError { + GCDAsyncSocket* serverSocket = + [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketQueue]; + NSError* listenError; + if ([serverSocket acceptOnPort:port error:&listenError]) { + _serverSocket = serverSocket; + } else { + if (shouldReportError) { + FBPFLog(@"Failed to listen: %@", listenError); + } + } +} + +- (void)listenForMultiplexingChannelOnPort:(NSUInteger)port { + [self _listenForMultiplexingChannelOnPort:port reportError:YES]; + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification* note) { + [self _listenForMultiplexingChannelOnPort:port reportError:NO]; + }]; +} + +- (void)_listenForMultiplexingChannelOnPort:(NSUInteger)port + reportError:(BOOL)shouldReportError { + PTChannel* channel = [[PTChannel alloc] initWithProtocol:_protocol + delegate:self]; + [channel + listenOnPort:port + IPv4Address:INADDR_LOOPBACK + callback:^(NSError* error) { + if (error) { + if (shouldReportError) { + FBPFLog( + @"Failed to listen on 127.0.0.1:%lu: %@", + (unsigned long)port, + error); + } + } else { + FBPFTrace(@"Listening on 127.0.0.1:%lu", (unsigned long)port); + self->_serverChannel = channel; + } + }]; +} + +- (void)close { + if (_serverChannel) { + [_serverChannel close]; + _serverChannel = nil; + } + [_serverSocket disconnect]; +} + +#pragma mark - PTChannelDelegate + +- (void)ioFrameChannel:(PTChannel*)channel + didAcceptConnection:(PTChannel*)otherChannel + fromAddress:(PTAddress*)address { + // Cancel any other connection. We are FIFO, so the last connection + // established will cancel any previous connection and "take its place". + if (_peerChannel) { + [_peerChannel cancel]; + } + + // Weak pointer to current connection. Connection objects live by themselves + // (owned by its parent dispatch queue) until they are closed. + _peerChannel = otherChannel; + _peerChannel.userInfo = address; + FBPFTrace(@"Connected to %@", address); +} + +- (void)ioFrameChannel:(PTChannel*)channel + didReceiveFrameOfType:(uint32_t)type + tag:(uint32_t)tag + payload:(PTData*)payload { + // NSLog(@"didReceiveFrameOfType: %u, %u, %@", type, tag, payload); + if (type == FKPortForwardingFrameTypeWriteToPipe) { + GCDAsyncSocket* sock = _clientSockets[@(tag)]; + [sock writeData:[NSData dataWithBytes:payload.data length:payload.length] + withTimeout:-1 + tag:0]; + FBPFTrace(@"channel -> socket (%d), %zu bytes", tag, payload.length); + } + + if (type == FKPortForwardingFrameTypeClosePipe) { + GCDAsyncSocket* sock = _clientSockets[@(tag)]; + [sock disconnectAfterWriting]; + } +} + +- (void)ioFrameChannel:(PTChannel*)channel didEndWithError:(NSError*)error { + for (GCDAsyncSocket* sock in [_clientSockets objectEnumerator]) { + [sock setDelegate:nil]; + [sock disconnect]; + } + [_clientSockets removeAllObjects]; + FBPFTrace(@"Disconnected from %@, error = %@", channel.userInfo, error); +} + +#pragma mark - GCDAsyncSocketDelegate + +- (void)socket:(GCDAsyncSocket*)sock + didAcceptNewSocket:(GCDAsyncSocket*)newSocket { + dispatch_block_t block = ^() { + if (!self->_peerChannel) { + [newSocket setDelegate:nil]; + [newSocket disconnect]; + } + + UInt32 tag = ++self->_lastClientSocketTag; + newSocket.userData = @(tag); + newSocket.delegate = self; + self->_clientSockets[@(tag)] = newSocket; + [self->_peerChannel + sendFrameOfType:FKPortForwardingFrameTypeOpenPipe + tag:self->_lastClientSocketTag + withPayload:nil + callback:^(NSError* error) { + FBPFTrace( + @"open socket (%d), error = %@", (unsigned int)tag, error); + [newSocket readDataWithTimeout:-1 tag:0]; + }]; + }; + + if (_peerChannel) { + block(); + } else { + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + _socketQueue, + block); + } +} + +- (void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)_ { + UInt32 tag = [[sock userData] unsignedIntValue]; + FBPFTrace( + @"Incoming data on socket (%d) - %lu bytes", + (unsigned int)tag, + (unsigned long)data.length); + [_peerChannel sendFrameOfType:FKPortForwardingFrameTypeWriteToPipe + tag:tag + withPayload:NSDataToGCDData(data) + callback:^(NSError* error) { + FBPFTrace( + @"socket (%d) -> channel %lu bytes, error = %@", + (unsigned int)tag, + (unsigned long)data.length, + error); + [sock readDataWithTimeout:-1 tag:_]; + }]; +} + +- (void)socketDidDisconnect:(GCDAsyncSocket*)sock withError:(NSError*)err { + UInt32 tag = [sock.userData unsignedIntValue]; + [_clientSockets removeObjectForKey:@(tag)]; + [_peerChannel + sendFrameOfType:FKPortForwardingFrameTypeClosePipe + tag:tag + withPayload:nil + callback:^(NSError* error) { + FBPFTrace( + @"socket (%d) disconnected, err = %@, peer error = %@", + (unsigned int)tag, + err, + error); + }]; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient+Testing.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient+Testing.h new file mode 100644 index 000000000..ec9126e8a --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient+Testing.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef __cplusplus +#error This header can only be included in .mm (ObjC++) files +#endif + +#import <Foundation/Foundation.h> + +#import <Flipper/FlipperClient.h> +#import <FlipperKit/FlipperClient.h> + +@interface FlipperClient (Testing) + +- (instancetype)initWithCppClient:(facebook::flipper::FlipperClient*)cppClient; + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.h new file mode 100644 index 000000000..65f52877c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#import <Foundation/Foundation.h> +#import "FlipperPlugin.h" +#import "FlipperStateUpdateListener.h" + +/** +Represents a connection between the Sonar desktop och client side. Manages the +lifecycle of attached plugin instances. +*/ +@interface FlipperClient : NSObject + +/** +The shared singleton FlipperClient instance. It is an error to call this on +non-debug builds to avoid leaking data. +*/ ++ (instancetype)sharedClient; + +/** +Register a plugin with the client. +*/ +- (void)addPlugin:(NSObject<FlipperPlugin>*)plugin; + +/** +Unregister a plugin with the client. +*/ +- (void)removePlugin:(NSObject<FlipperPlugin>*)plugin; + +/** +Retrieve the plugin with a given identifier which was previously registered with +this client. +*/ +- (NSObject<FlipperPlugin>*)pluginWithIdentifier:(NSString*)identifier; + +/** +Establish a connection to the Sonar desktop. +*/ +- (void)start; + +/** +Stop the connection to the Sonar desktop. +*/ +- (void)stop; + +/** +Get the log of state changes from the sonar client +*/ +- (NSString*)getState; + +/** + Get the current summarized state of the sonar client + */ +- (NSArray<NSDictionary*>*)getStateElements; + +/** +Subscribe a ViewController to state update change notifications +*/ +- (void)subscribeForUpdates:(id<FlipperStateUpdateListener>)controller; + +// initializers are disabled. You must use `+[FlipperClient sharedClient]` +// instance. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.mm b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.mm new file mode 100644 index 000000000..93624d33d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperClient.mm @@ -0,0 +1,199 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "FlipperClient.h" +#import <Flipper/FlipperClient.h> +#import <UIKit/UIKit.h> +#include <folly/io/async/EventBase.h> +#include <folly/io/async/ScopedEventBaseThread.h> +#import "FlipperClient+Testing.h" +#import "FlipperCppWrapperPlugin.h" +#import "SKEnvironmentVariables.h" +#include "SKStateUpdateCPPWrapper.h" + +#if !TARGET_OS_SIMULATOR +#import <FKPortForwarding/FKPortForwardingServer.h> +#endif + +using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin; + +@implementation FlipperClient { + facebook::flipper::FlipperClient* _cppClient; + folly::ScopedEventBaseThread sonarThread; + folly::ScopedEventBaseThread connectionThread; +#if !TARGET_OS_SIMULATOR + FKPortForwardingServer* _secureServer; + FKPortForwardingServer* _insecureServer; +#endif +} + ++ (instancetype)sharedClient { + static FlipperClient* sharedClient = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + try { + sharedClient = [[self alloc] init]; + } catch (const std::exception& e) { + // fail. + sharedClient = nil; + } + }); + return sharedClient; +} + +- (instancetype)init { + if (self = [super init]) { + UIDevice* device = [UIDevice currentDevice]; + NSString* deviceName = [device name]; + NSBundle* bundle = [NSBundle mainBundle]; + NSString* appName = + [bundle objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey]; + NSString* appId = [bundle bundleIdentifier]; + NSString* privateAppDirectory = NSSearchPathForDirectoriesInDomains( + NSApplicationSupportDirectory, NSUserDomainMask, YES)[0]; + + NSFileManager* manager = [NSFileManager defaultManager]; + + if ([manager fileExistsAtPath:privateAppDirectory isDirectory:NULL] == NO && + ![manager createDirectoryAtPath:privateAppDirectory + withIntermediateDirectories:YES + attributes:nil + error:nil]) { + return nil; + } + +#if TARGET_OS_SIMULATOR + deviceName = [NSString stringWithFormat:@"%@ %@", + [[UIDevice currentDevice] model], + @"Simulator"]; +#endif + + static const std::string UNKNOWN = std::string("unknown"); + try { + facebook::flipper::FlipperClient::init( + {{ + "localhost", + "iOS", + [deviceName UTF8String], + UNKNOWN, + [appName UTF8String] ?: UNKNOWN, + [appId UTF8String] ?: UNKNOWN, + [privateAppDirectory UTF8String], + }, + sonarThread.getEventBase(), + connectionThread.getEventBase(), + [SKEnvironmentVariables getInsecurePort], + [SKEnvironmentVariables getSecurePort]}); + _cppClient = facebook::flipper::FlipperClient::instance(); + } catch (const std::system_error& e) { + // Probably ran out of disk space. + return nil; + } + } + return self; +} + +- (void)refreshPlugins { + _cppClient->refreshPlugins(); +} + +- (void)addPlugin:(NSObject<FlipperPlugin>*)plugin { + _cppClient->addPlugin(std::make_shared<WrapperPlugin>(plugin)); +} + +- (void)removePlugin:(NSObject<FlipperPlugin>*)plugin { + _cppClient->removePlugin(std::make_shared<WrapperPlugin>(plugin)); +} + +- (NSObject<FlipperPlugin>*)pluginWithIdentifier:(NSString*)identifier { + auto cppPlugin = _cppClient->getPlugin([identifier UTF8String]); + if (auto wrapper = dynamic_cast<WrapperPlugin*>(cppPlugin.get())) { + return wrapper->getObjCPlugin(); + } + return nil; +} + +- (void)start { +#if !TARGET_OS_SIMULATOR + _secureServer = [FKPortForwardingServer new]; + [_secureServer forwardConnectionsFromPort:8088]; + [_secureServer listenForMultiplexingChannelOnPort:8078]; + _insecureServer = [FKPortForwardingServer new]; + [_insecureServer forwardConnectionsFromPort:8089]; + [_insecureServer listenForMultiplexingChannelOnPort:8079]; +#endif + _cppClient->start(); +} + +- (void)stop { + _cppClient->stop(); +#if !TARGET_OS_SIMULATOR + [_secureServer close]; + _secureServer = nil; + [_insecureServer close]; + _insecureServer = nil; +#endif +} + +- (NSString*)getState { + return @(_cppClient->getState().c_str()); +} + +- (NSArray*)getStateElements { + NSMutableArray<NSDictionary<NSString*, NSString*>*>* const array = + [NSMutableArray array]; + + for (facebook::flipper::StateElement element : + _cppClient->getStateElements()) { + facebook::flipper::State state = element.state_; + NSString* stateString; + switch (state) { + case facebook::flipper::in_progress: + stateString = @"⏳ "; + break; + + case facebook::flipper::success: + stateString = @"✅ "; + break; + + case facebook::flipper::failed: + stateString = @"❌ "; + break; + + default: + stateString = @"❓ "; + break; + } + [array addObject:@{ + @"name" : [NSString stringWithUTF8String:element.name_.c_str()], + @"state" : stateString + }]; + } + return array; +} + +- (void)subscribeForUpdates:(id<FlipperStateUpdateListener>)controller { + auto stateListener = std::make_shared<SKStateUpdateCPPWrapper>(controller); + _cppClient->setStateListener(stateListener); +} + +@end + +@implementation FlipperClient (Testing) + +- (instancetype)initWithCppClient:(facebook::flipper::FlipperClient*)cppClient { + if (self = [super init]) { + _cppClient = cppClient; + } + return self; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperConnection.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperConnection.h new file mode 100644 index 000000000..a4b90767c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperConnection.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@protocol FlipperResponder; +@protocol FlipperConnectionManager; + +typedef void (^SonarReceiver)(NSDictionary*, id<FlipperResponder>); + +/** +Represents a connection between the Desktop and mobile plugins with +corresponding identifiers. +*/ +@protocol FlipperConnection + +/** +Invoke a method on the Sonar desktop plugin with with a matching identifier. +*/ +- (void)send:(NSString*)method withParams:(NSDictionary*)params; + +/** +Register a receiver to be notified of incoming calls of the given method from +the Sonar desktop plugin with a matching identifier. +*/ +- (void)receive:(NSString*)method withBlock:(SonarReceiver)receiver; + +/** +Report an error to the Flipper desktop app +*/ +- (void)errorWithMessage:(NSString*)message stackTrace:(NSString*)stacktrace; + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.h new file mode 100644 index 000000000..176ca8a86 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#import <UIKit/UIKit.h> +#include "FlipperStateUpdateListener.h" + +@interface StateTableDataSource : NSObject<UITableViewDataSource> +@property(strong, nonatomic) NSArray<NSDictionary*>* elements; +@end + +@interface FlipperDiagnosticsViewController + : UIViewController<FlipperStateUpdateListener> +@property(strong, nonatomic) StateTableDataSource* tableDataSource; +@property(strong, nonatomic) UILabel* stateLabel; +@property(strong, nonatomic) UITableView* stateTable; +@property(strong, nonatomic) UIScrollView* scrollView; +@property(strong, nonatomic) UILabel* logLabel; + +- (void)onUpdate; +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.m b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.m new file mode 100644 index 000000000..ed3689069 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.m @@ -0,0 +1,128 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#import "FlipperDiagnosticsViewController.h" +#import "FlipperClient.h" + +#define STATE_VIEW_HEIGHT 300 + +static NSString* const kSKCellIdentifier = + @"FlipperDiagnosticStateTableStableCellIdentifier"; + +@implementation StateTableDataSource +- (instancetype)initWithElements:(NSArray<NSDictionary*>*)elements { + self = [super init]; + if (self) { + _elements = elements; + } + return self; +} + +- (nonnull UITableViewCell*)tableView:(nonnull UITableView*)tableView + cellForRowAtIndexPath:(nonnull NSIndexPath*)indexPath { + NSInteger row = indexPath.row; + + UITableViewCell* cell = + [tableView dequeueReusableCellWithIdentifier:kSKCellIdentifier + forIndexPath:indexPath]; + cell.textLabel.font = [UIFont fontWithName:@"Arial" size:10]; + cell.textLabel.text = [self.elements[row][@"state"] + stringByAppendingString:self.elements[row][@"name"]]; + return cell; +} + +- (NSInteger)tableView:(nonnull UITableView*)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.elements count]; +} + +@end + +@implementation FlipperDiagnosticsViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.scrollView = [[UIScrollView alloc] + initWithFrame:CGRectMake( + 0, + STATE_VIEW_HEIGHT, + self.view.frame.size.width, + self.view.frame.size.height - 100 - STATE_VIEW_HEIGHT)]; + self.logLabel = + [[UILabel alloc] initWithFrame:CGRectMake( + 0, + 0, + self.view.frame.size.width, + self.scrollView.frame.size.height)]; + self.logLabel.numberOfLines = 0; + self.logLabel.font = [UIFont fontWithName:@"Arial" size:10]; + [self.scrollView addSubview:self.logLabel]; + + self.stateTable = [[UITableView alloc] + initWithFrame:CGRectMake( + 0, 0, self.view.bounds.size.width, STATE_VIEW_HEIGHT)]; + [self.stateTable registerClass:[UITableViewCell class] + forCellReuseIdentifier:kSKCellIdentifier]; + self.stateTable.rowHeight = 14; + self.tableDataSource = [[StateTableDataSource alloc] + initWithElements:[[FlipperClient sharedClient] getStateElements]]; + self.stateTable.dataSource = self.tableDataSource; + + [self updateLogView]; + + [self.view addSubview:self.stateTable]; + [self.view addSubview:self.scrollView]; + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (void)onUpdate { + FlipperDiagnosticsViewController __weak* weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf updateStateTable]; + [weakSelf updateLogView]; + }); +} + +- (void)updateStateTable { + self.tableDataSource.elements = + [[FlipperClient sharedClient] getStateElements]; + [self.stateTable reloadData]; +} + +- (void)updateLogView { + NSString* state = [[FlipperClient sharedClient] getState]; + self.logLabel.text = state; + [self.logLabel sizeToFit]; + self.scrollView.contentSize = self.logLabel.frame.size; + + // Scroll to bottom + CGPoint bottomOffset = CGPointMake( + 0, + self.scrollView.contentSize.height - self.scrollView.bounds.size.height); + [self.scrollView setContentOffset:bottomOffset animated:YES]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + id<FlipperStateUpdateListener> weakSelf = self; + [[FlipperClient sharedClient] subscribeForUpdates:weakSelf]; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait; +} + +- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { + return UIInterfaceOrientationPortrait; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperPlugin.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperPlugin.h new file mode 100644 index 000000000..a331a71ff --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperPlugin.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> +#import "FlipperResponder.h" +#import "SKMacros.h" + +SK_EXTERN_C_BEGIN +void FlipperPerformBlockOnMainThread( + void (^block)(void), + id<FlipperResponder> responder); +SK_EXTERN_C_END + +@protocol FlipperConnection; + +@protocol FlipperPlugin + +/** +The plugin's identifier. This should map to a javascript plugin with the same +identifier to ensure messages are sent correctly. +*/ +- (NSString*)identifier; + +/** +Called when a connection has been established between this plugin and the +corresponding plugin on the Sonar desktop app. The provided connection can be +used to register method receivers as well as send messages back to the desktop +app. +*/ +- (void)didConnect:(id<FlipperConnection>)connection; + +/** +Called when a plugin has been disconnected and the SonarConnection provided in +didConnect is no longer valid to use. +*/ +- (void)didDisconnect; + +/** + Returns true if the plugin is meant to be run in background too, otherwise it + returns false. + */ +@optional +- (BOOL)runInBackground; + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperResponder.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperResponder.h new file mode 100644 index 000000000..7372e5618 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperResponder.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +/** +Acts as a hook for providing return values to remote called from Sonar desktop +plugins. +*/ +@protocol FlipperResponder + +/** +Respond with a successful return value. +*/ +- (void)success:(NSDictionary*)response; + +/** +Respond with an error. +*/ +- (void)error:(NSDictionary*)response; + +@end diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperStateUpdateListener.h b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperStateUpdateListener.h new file mode 100644 index 000000000..a4b524db4 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperStateUpdateListener.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +@protocol FlipperStateUpdateListener +- (void)onUpdate; +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperUtil.m b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperUtil.m new file mode 100644 index 000000000..4caa2d6fc --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/FlipperUtil.m @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> +#import "FlipperPlugin.h" +#import "FlipperResponder.h" + +void FlipperPerformBlockOnMainThread( + void (^block)(void), + id<FlipperResponder> responder) { + if ([NSThread isMainThread]) { + @try { + block(); + } @catch (NSException* e) { + [responder error:@{@"name" : e.name, @"message" : e.reason}]; + } @catch (...) { + [responder error:@{ + @"name" : @"Unknown", + @"message" : + @"Unknown error caught when processing operation on main thread" + }]; + } + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + block(); + } @catch (NSException* e) { + [responder error:@{@"name" : e.name, @"message" : e.reason}]; + } @catch (...) { + [responder error:@{ + @"name" : @"Unknown", + @"message" : + @"Unknown error caught when processing operation on main thread" + }]; + } + }); + } +} diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.h b/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.h new file mode 100644 index 000000000..901b9ec83 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +/* + * This class exists to retreive configuration values stored in environment + * variables. + */ +@interface SKEnvironmentVariables : NSObject ++ (int)getInsecurePort; ++ (int)getSecurePort; +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.m b/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.m new file mode 100644 index 000000000..0574ac59c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.m @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#import "SKEnvironmentVariables.h" + +static int const DEFAULT_INSECURE_PORT = 8089; +static int const DEFAULT_SECURE_PORT = 8088; + +@implementation SKEnvironmentVariables + ++ (int)getInsecurePort { + NSString* envVar = [self getFlipperPortsVariable]; + return [self extractIntFromPropValue:envVar + atIndex:0 + withDefault:DEFAULT_INSECURE_PORT]; +} ++ (int)getSecurePort { + NSString* envVar = [self getFlipperPortsVariable]; + return [self extractIntFromPropValue:envVar + atIndex:1 + withDefault:DEFAULT_SECURE_PORT]; +} ++ (int)extractIntFromPropValue:(NSString*)propValue + atIndex:(int)index + withDefault:(int)fallback { + NSArray<NSString*>* components = [propValue componentsSeparatedByString:@","]; + NSString* component = [components objectAtIndex:index]; + int envInt = [component intValue]; + return envInt > 0 ? envInt : fallback; +} ++ (NSString*)getFlipperPortsVariable { + NSString* value = NSProcessInfo.processInfo.environment[@"FLIPPER_PORTS"]; + return value; +} +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/SKMacros.h b/ios/Pods/FlipperKit/iOS/FlipperKit/SKMacros.h new file mode 100644 index 000000000..b37ec6d81 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/SKMacros.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef SKMACROS_H +#define SKMACROS_H + +#import <FBDefines/FBDefines.h> + +#ifdef __cplusplus +#define SK_EXTERN_C_BEGIN extern "C" { +#define SK_EXTERN_C_END } +#define SK_EXTERN_C extern "C" +#else +#define SK_EXTERN_C_BEGIN +#define SK_EXTERN_C_END +#define SK_EXTERN_C extern +#endif + +#define SKLog(...) NSLog(__VA_ARGS__) +#define SKTrace(...) /*NSLog(__VA_ARGS__)*/ + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.h b/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.h new file mode 100644 index 000000000..befc3cb1d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#include <Flipper/FlipperStateUpdateListener.h> +#import "FlipperStateUpdateListener.h" + +/* + * This class exists to bridge the gap between Objective C and C++. + * A SKStateUpdateCPPWrapper instance allows for wrapping an Objective-C object + * and passing it to the pure C++ SonarClient, so it can be triggered when + * updates occur. + */ +class SKStateUpdateCPPWrapper : public FlipperStateUpdateListener { + public: + SKStateUpdateCPPWrapper(id<FlipperStateUpdateListener> delegate_); + void onUpdate(); + + private: + __weak id<FlipperStateUpdateListener> delegate_; +}; + +#endif diff --git a/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.mm b/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.mm new file mode 100644 index 000000000..2acb7337e --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.mm @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef FB_SONARKIT_ENABLED + +#include "SKStateUpdateCPPWrapper.h" + +SKStateUpdateCPPWrapper::SKStateUpdateCPPWrapper( + id<FlipperStateUpdateListener> controller) { + delegate_ = controller; +} + +void SKStateUpdateCPPWrapper::onUpdate() { + if (!delegate_) { + return; + } + [delegate_ onUpdate]; +} + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h new file mode 100644 index 000000000..6cb5f6251 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <Foundation/Foundation.h> + +#import <FlipperKit/FlipperPlugin.h> + +#import "SKDescriptorMapper.h" +#import "SKInvalidation.h" +#import "SKTapListener.h" + +@interface FlipperKitLayoutPlugin + : NSObject<FlipperPlugin, SKInvalidationDelegate> + +- (instancetype)initWithRootNode:(id<NSObject>)rootNode + withDescriptorMapper:(SKDescriptorMapper*)mapper; + +- (instancetype)initWithRootNode:(id<NSObject>)rootNode + withTapListener:(id<SKTapListener>)tapListener + withDescriptorMapper:(SKDescriptorMapper*)mapper; + +@property(nonatomic, readonly, strong) SKDescriptorMapper* descriptorMapper; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm new file mode 100644 index 000000000..d64db0b0d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm @@ -0,0 +1,570 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "FlipperKitLayoutPlugin.h" + +#import <FlipperKit/FlipperClient.h> +#import <FlipperKit/FlipperConnection.h> +#import <FlipperKit/FlipperResponder.h> +#import <FlipperKit/SKMacros.h> +#import <mutex> +#import "SKDescriptorMapper.h" +#import "SKNodeDescriptor.h" +#import "SKSearchResultNode.h" +#import "SKTapListener.h" +#import "SKTapListenerImpl.h" + +@implementation FlipperKitLayoutPlugin { + NSMapTable<NSString*, id>* _trackedObjects; + NSString* _lastHighlightedNode; + NSMutableSet* _invalidObjects; + Boolean _invalidateMessageQueued; + NSDate* _lastInvalidateMessage; + std::mutex invalidObjectsMutex; + + id<NSObject> _rootNode; + id<SKTapListener> _tapListener; + + id<FlipperConnection> _connection; + + NSMutableSet* _registeredDelegates; +} + +- (instancetype)initWithRootNode:(id<NSObject>)rootNode + withDescriptorMapper:(SKDescriptorMapper*)mapper { + return [self initWithRootNode:rootNode + withTapListener:[SKTapListenerImpl new] + withDescriptorMapper:mapper]; +} + +- (instancetype)initWithRootNode:(id<NSObject>)rootNode + withTapListener:(id<SKTapListener>)tapListener + withDescriptorMapper:(SKDescriptorMapper*)mapper { + if (self = [super init]) { + _descriptorMapper = mapper; + _trackedObjects = [NSMapTable strongToWeakObjectsMapTable]; + _lastHighlightedNode = nil; + _invalidObjects = [NSMutableSet new]; + _invalidateMessageQueued = false; + _lastInvalidateMessage = [NSDate date]; + _rootNode = rootNode; + _tapListener = tapListener; + + _registeredDelegates = [NSMutableSet new]; + [SKInvalidation sharedInstance].delegate = self; + } + + return self; +} + +- (NSString*)identifier { + return @"Inspector"; +} + +- (void)didConnect:(id<FlipperConnection>)connection { + _connection = connection; + + if (!_rootNode) { + // TODO: T61384369 get rid off this if condition. + _rootNode = [UIApplication sharedApplication]; + } + + [SKInvalidation enableInvalidations]; + + // Run setup logic for each descriptor + for (SKNodeDescriptor* descriptor in _descriptorMapper.allDescriptors) { + [descriptor setUp]; + } + + // In order to avoid a retain cycle (Connection -> Block -> + // FlipperKitLayoutPlugin -> Connection ...) + __weak FlipperKitLayoutPlugin* weakSelf = self; + + [connection receive:@"getRoot" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallGetRoot:responder]; + }, + responder); + }]; + + [connection receive:@"getAllNodes" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallGetAllNodesWithResponder:responder]; + }, + responder); + }]; + + [connection receive:@"getNodes" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallGetNodes:params[@"ids"] + withResponder:responder]; + }, + responder); + }]; + + [connection receive:@"setData" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallSetData:params[@"id"] + withPath:params[@"path"] + toValue:params[@"value"] + withConnection:connection]; + }, + responder); + }]; + + [connection receive:@"setHighlighted" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallSetHighlighted:params[@"id"] + withResponder:responder]; + }, + responder); + }]; + + [connection receive:@"setSearchActive" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf + onCallSetSearchActive:[params[@"active"] boolValue] + withConnection:connection]; + }, + responder); + }]; + + [connection receive:@"isSearchActive" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallIsSearchActiveWithConnection:responder]; + }, + responder); + }]; + + [connection receive:@"isConsoleEnabled" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [responder success:@{@"isEnabled" : @NO}]; + }, + responder); + }]; + + [connection receive:@"getSearchResults" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + FlipperPerformBlockOnMainThread( + ^{ + [weakSelf onCallGetSearchResults:params[@"query"] + withResponder:responder]; + }, + responder); + }]; +} + +- (void)didDisconnect { + // Clear the last highlight if there is any + [self onCallSetHighlighted:nil withResponder:nil]; + // Disable search if it is active + [self onCallSetSearchActive:NO withConnection:nil]; +} + +- (void)onCallGetRoot:(id<FlipperResponder>)responder { + const auto rootNode = [self getNode:[self trackObject:_rootNode]]; + + [responder success:rootNode]; +} + +- (void)populateAllNodesFromNode:(nonnull NSString*)identifier + inDictionary: + (nonnull NSMutableDictionary<NSString*, NSDictionary*>*) + mutableDict { + NSDictionary* nodeDict = [self getNode:identifier]; + mutableDict[identifier] = nodeDict; + NSArray* arr = nodeDict[@"children"]; + for (NSString* childIdentifier in arr) { + [self populateAllNodesFromNode:childIdentifier inDictionary:mutableDict]; + } + return; +} + +- (void)populateAllNodesFromNode:(nonnull NSString*)identifier + inArray:(nonnull NSMutableArray<NSDictionary*>*) + mutableArray { + NSDictionary* nodeDict = [self getNode:identifier]; + if (nodeDict == nil) { + return; + } + [mutableArray addObject:nodeDict]; + NSArray* children = nodeDict[@"children"]; + for (NSString* childIdentifier in children) { + [self populateAllNodesFromNode:childIdentifier inArray:mutableArray]; + } +} + +- (void)onCallGetAllNodesWithResponder:(nonnull id<FlipperResponder>)responder { + NSMutableArray<NSDictionary*>* allNodes = @[].mutableCopy; + NSString* identifier = [self trackObject:_rootNode]; + NSDictionary* rootNode = [self getNode:identifier]; + if (!rootNode) { + return [responder error:@{ + @"error" : [NSString + stringWithFormat: + @"getNode returned nil for the rootNode %@, while getting all the nodes", + identifier] + }]; + } + [allNodes addObject:rootNode]; + NSMutableDictionary* allNodesDict = @{}.mutableCopy; + [self populateAllNodesFromNode:identifier inDictionary:allNodesDict]; + [responder success:@{ + @"allNodes" : @{@"rootElement" : identifier, @"elements" : allNodesDict} + }]; +} + +- (NSMutableArray*)getChildrenForNode:(id)node + withDescriptor:(SKNodeDescriptor*)descriptor { + NSMutableArray* children = [NSMutableArray new]; + for (NSUInteger i = 0; i < [descriptor childCountForNode:node]; i++) { + id childNode = [descriptor childForNode:node atIndex:i]; + + NSString* childIdentifier = [self trackObject:childNode]; + if (childIdentifier) { + [children addObject:childIdentifier]; + } + } + return children; +} + +- (void)onCallGetNodes:(NSArray<NSDictionary*>*)nodeIds + withResponder:(id<FlipperResponder>)responder { + NSMutableArray<NSDictionary*>* elements = [NSMutableArray new]; + + for (id nodeId in nodeIds) { + const auto node = [self getNode:nodeId]; + if (node == nil) { + continue; + } + [elements addObject:node]; + } + + [responder success:@{@"elements" : elements}]; +} + +- (void)onCallSetData:(NSString*)objectId + withPath:(NSArray<NSString*>*)path + toValue:(id<NSObject>)value + withConnection:(id<FlipperConnection>)connection { + id node = [_trackedObjects objectForKey:objectId]; + if (node == nil) { + SKLog(@"node is nil, trying to setData: \ + objectId: %@ \ + path: %@ \ + value: %@", objectId, path, value); + return; + } + + // Sonar sends nil/NSNull on some values when the text-field + // is empty, disregard these changes otherwise we'll crash. + if (value == nil || [value isKindOfClass:[NSNull class]]) { + return; + } + + SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[node class]]; + + NSString* dotJoinedPath = [path componentsJoinedByString:@"."]; + SKNodeUpdateData updateDataForPath = + [[descriptor dataMutationsForNode:node] objectForKey:dotJoinedPath]; + if (updateDataForPath != nil) { + const auto identifierForInvalidation = + [descriptor identifierForInvalidation:node]; + id nodeForInvalidation = + [_trackedObjects objectForKey:identifierForInvalidation]; + SKNodeDescriptor* descriptorForInvalidation = + [_descriptorMapper descriptorForClass:[nodeForInvalidation class]]; + updateDataForPath(value); + + NSMutableArray* nodesForInvalidation = [NSMutableArray new]; + [self populateAllNodesFromNode:[descriptorForInvalidation + identifierForNode:nodeForInvalidation] + inArray:nodesForInvalidation]; + [connection send:@"invalidateWithData" + withParams:@{@"nodes" : nodesForInvalidation}]; + } +} + +- (void)onCallGetSearchResults:(NSString*)query + withResponder:(id<FlipperResponder>)responder { + const auto alreadyAddedElements = [NSMutableSet<NSString*> new]; + SKSearchResultNode* matchTree = + [self searchForQuery:(NSString*)[query lowercaseString] + fromNode:(id)_rootNode + withElementsAlreadyAdded:alreadyAddedElements]; + + [responder success:@{ + @"results" : [matchTree toNSDictionary] ?: [NSNull null], + @"query" : query + }]; + return; +} + +- (void)onCallSetHighlighted:(NSString*)objectId + withResponder:(id<FlipperResponder>)responder { + if (_lastHighlightedNode != nil) { + id lastHighlightedObject = + [_trackedObjects objectForKey:_lastHighlightedNode]; + if (lastHighlightedObject == nil) { + [responder error:@{@"error" : @"unable to get last highlighted object"}]; + return; + } + + SKNodeDescriptor* descriptor = [self->_descriptorMapper + descriptorForClass:[lastHighlightedObject class]]; + [descriptor setHighlighted:NO forNode:lastHighlightedObject]; + + _lastHighlightedNode = nil; + } + + if (objectId == nil || [objectId isKindOfClass:[NSNull class]]) { + return; + } + + id object = [_trackedObjects objectForKey:objectId]; + if (object == nil) { + SKLog(@"tried to setHighlighted for untracked id, objectId: %@", objectId); + return; + } + + SKNodeDescriptor* descriptor = + [self->_descriptorMapper descriptorForClass:[object class]]; + [descriptor setHighlighted:YES forNode:object]; + + _lastHighlightedNode = objectId; +} + +- (void)onCallSetSearchActive:(BOOL)active + withConnection:(id<FlipperConnection>)connection { + if (active) { + [_tapListener mountWithFrame:[[UIScreen mainScreen] bounds]]; + __block id<NSObject> rootNode = _rootNode; + + [_tapListener listenForTapWithBlock:^(CGPoint touchPoint) { + SKTouch* touch = [[SKTouch alloc] + initWithTouchPoint:touchPoint + withRootNode:rootNode + withDescriptorMapper:self->_descriptorMapper + finishWithBlock:^(NSArray<NSString*>* path) { + [connection send:@"select" withParams:@{@"path" : path}]; + }]; + + SKNodeDescriptor* descriptor = + [self->_descriptorMapper descriptorForClass:[rootNode class]]; + [descriptor hitTest:touch forNode:rootNode]; + }]; + } else { + [_tapListener unmount]; + } +} + +- (void)onCallIsSearchActiveWithConnection:(id<FlipperResponder>)responder { + [responder success:@{@"isSearchActive" : @NO}]; +} + +- (void)invalidateNode:(id<NSObject>)node { + SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[node class]]; + if (descriptor == nil) { + return; + } + + NSString* nodeId = [descriptor identifierForNode:node]; + if (![_trackedObjects objectForKey:nodeId]) { + return; + } + [descriptor invalidateNode:node]; + + // Collect invalidate messages before sending in a batch + std::lock_guard<std::mutex> lock(invalidObjectsMutex); + [_invalidObjects addObject:nodeId]; + if (_invalidateMessageQueued) { + return; + } + _invalidateMessageQueued = true; + + if (_lastInvalidateMessage.timeIntervalSinceNow < -1) { + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC), + dispatch_get_main_queue(), + ^{ + [self reportInvalidatedObjects]; + }); + } +} + +- (void)reportInvalidatedObjects { + std::lock_guard<std::mutex> lock(invalidObjectsMutex); + NSMutableArray* nodes = [NSMutableArray new]; + for (NSString* nodeId in self->_invalidObjects) { + [nodes addObject:[NSDictionary dictionaryWithObject:nodeId forKey:@"id"]]; + } + [self->_connection send:@"invalidate" + withParams:[NSDictionary dictionaryWithObject:nodes + forKey:@"nodes"]]; + self->_lastInvalidateMessage = [NSDate date]; + self->_invalidObjects = [NSMutableSet new]; + self->_invalidateMessageQueued = false; + return; +} + +- (void)updateNodeReference:(id<NSObject>)node { + SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[node class]]; + if (descriptor == nil) { + return; + } + + NSString* nodeId = [descriptor identifierForNode:node]; + [_trackedObjects setObject:node forKey:nodeId]; +} + +- (SKSearchResultNode*)searchForQuery:(NSString*)query + fromNode:(id)node + withElementsAlreadyAdded:(NSMutableSet<NSString*>*)alreadyAdded { + SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[node class]]; + if (node == nil || descriptor == nil) { + return nil; + } + + NSMutableArray<SKSearchResultNode*>* childTrees = nil; + BOOL isMatch = [descriptor matchesQuery:query forNode:node]; + + NSString* nodeId = [self trackObject:node]; + + for (auto i = 0; i < [descriptor childCountForNode:node]; i++) { + id child = [descriptor childForNode:node atIndex:i]; + if (child) { + SKSearchResultNode* childTree = [self searchForQuery:query + fromNode:child + withElementsAlreadyAdded:alreadyAdded]; + if (childTree != nil) { + if (childTrees == nil) { + childTrees = [NSMutableArray new]; + } + [childTrees addObject:childTree]; + } + } + } + + if (isMatch || childTrees != nil) { + NSDictionary* element = [self getNode:nodeId]; + if (nodeId == nil || element == nil) { + return nil; + } + NSMutableArray<NSString*>* descriptorChildElements = + [element objectForKey:@"children"]; + NSMutableDictionary* newElement = [element mutableCopy]; + + NSMutableArray<NSString*>* childElementsToReturn = [NSMutableArray new]; + for (NSString* child in descriptorChildElements) { + if (![alreadyAdded containsObject:child]) { + [alreadyAdded addObject:child]; // todo add all at end + [childElementsToReturn addObject:child]; + } + } + [newElement setObject:childElementsToReturn forKey:@"children"]; + return [[SKSearchResultNode alloc] initWithNode:nodeId + asMatch:isMatch + withElement:newElement + andChildren:childTrees]; + } + return nil; +} + +- (NSDictionary*)getNode:(NSString*)nodeId { + id<NSObject> node = [_trackedObjects objectForKey:nodeId]; + if (node == nil) { + SKLog(@"node is nil, no tracked node found for nodeId: %@", nodeId); + return nil; + } + + SKNodeDescriptor* nodeDescriptor = + [_descriptorMapper descriptorForClass:[node class]]; + if (nodeDescriptor == nil) { + SKLog(@"No registered descriptor for class: %@", [node class]); + return nil; + } + + NSMutableArray* attributes = [NSMutableArray new]; + NSMutableDictionary* data = [NSMutableDictionary new]; + + const auto* nodeAttributes = [nodeDescriptor attributesForNode:node]; + for (const SKNamed<NSString*>* namedPair in nodeAttributes) { + const auto name = namedPair.name; + if (name) { + const NSDictionary* attribute = @{ + @"name" : name, + @"value" : namedPair.value ?: [NSNull null], + }; + [attributes addObject:attribute]; + } + } + + const auto* nodeData = [nodeDescriptor dataForNode:node]; + for (const SKNamed<NSDictionary*>* namedPair in nodeData) { + data[namedPair.name] = namedPair.value; + } + + NSMutableArray* children = [self getChildrenForNode:node + withDescriptor:nodeDescriptor]; + + NSDictionary* nodeDic = @{ + // We shouldn't get nil for id/name/decoration, but let's not crash if we + // do. + @"id" : [nodeDescriptor identifierForNode:node] ?: @"(unknown)", + @"name" : [nodeDescriptor nameForNode:node] ?: @"(unknown)", + @"children" : children, + @"attributes" : attributes, + @"data" : data, + @"decoration" : [nodeDescriptor decorationForNode:node] ?: @"(unknown)", + }; + + return nodeDic; +} + +- (NSString*)trackObject:(id)object { + const SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[object class]]; + NSString* objectIdentifier = [descriptor identifierForNode:object]; + + if (objectIdentifier == nil) { + return nil; + } + + [_trackedObjects setObject:object forKey:objectIdentifier]; + + return objectIdentifier; +} + +- (BOOL)runInBackground { + return true; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h new file mode 100644 index 000000000..637353b28 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@class SKNodeDescriptor; + +@interface SKDescriptorMapper : NSObject + +- (instancetype)initWithDefaults; + +- (SKNodeDescriptor*)descriptorForClass:(Class)cls; + +- (void)registerDescriptor:(SKNodeDescriptor*)descriptor forClass:(Class)cls; + +- (NSArray<SKNodeDescriptor*>*)allDescriptors; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.mm new file mode 100644 index 000000000..87eab1ef4 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.mm @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKDescriptorMapper.h" + +#import "SKApplicationDescriptor.h" +#import "SKButtonDescriptor.h" +#import "SKScrollViewDescriptor.h" +#import "SKViewControllerDescriptor.h" +#import "SKViewDescriptor.h" + +@implementation SKDescriptorMapper { + NSMutableDictionary<NSString*, SKNodeDescriptor*>* _descriptors; +} + +- (instancetype)initWithDefaults { + if (self = [super init]) { + _descriptors = [NSMutableDictionary new]; + + [self registerDescriptor:[[SKApplicationDescriptor alloc] + initWithDescriptorMapper:self] + forClass:[UIApplication class]]; + [self registerDescriptor:[[SKViewControllerDescriptor alloc] + initWithDescriptorMapper:self] + forClass:[UIViewController class]]; + [self registerDescriptor:[[SKScrollViewDescriptor alloc] + initWithDescriptorMapper:self] + forClass:[UIScrollView class]]; + [self registerDescriptor:[[SKButtonDescriptor alloc] + initWithDescriptorMapper:self] + forClass:[UIButton class]]; + [self registerDescriptor:[[SKViewDescriptor alloc] + initWithDescriptorMapper:self] + forClass:[UIView class]]; + } + + return self; +} + +- (SKNodeDescriptor*)descriptorForClass:(Class)cls { + SKNodeDescriptor* classDescriptor = nil; + + while (classDescriptor == nil && cls != nil) { + classDescriptor = [_descriptors objectForKey:NSStringFromClass(cls)]; + cls = [cls superclass]; + } + + return classDescriptor; +} + +- (void)registerDescriptor:(SKNodeDescriptor*)descriptor forClass:(Class)cls { + NSString* className = NSStringFromClass(cls); + _descriptors[className] = descriptor; +} + +- (NSArray<SKNodeDescriptor*>*)allDescriptors { + return [_descriptors allValues]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h new file mode 100644 index 000000000..9b26d599e --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@protocol SKInvalidationDelegate + +- (void)invalidateNode:(id<NSObject>)node; + +- (void)updateNodeReference:(id<NSObject>)node; + +@end + +@interface SKInvalidation : NSObject + ++ (instancetype)sharedInstance; + ++ (void)enableInvalidations; + +@property(nonatomic, weak) id<SKInvalidationDelegate> delegate; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.m new file mode 100644 index 000000000..05966f35c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.m @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <UIKit/UIKit.h> + +#import "SKInvalidation.h" +#import "UICollectionView+SKInvalidation.h" +#import "UIView+SKInvalidation.h" + +@implementation SKInvalidation + ++ (instancetype)sharedInstance { + static SKInvalidation* sInstance = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sInstance = [SKInvalidation new]; + }); + + return sInstance; +} + ++ (void)enableInvalidations { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [UIView enableInvalidation]; + [UICollectionView enableInvalidations]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(windowDidBecomeVisible:) + name:UIWindowDidBecomeVisibleNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(windowDidBecomeHidden:) + name:UIWindowDidBecomeHiddenNotification + object:nil]; + }); +} + ++ (void)windowDidBecomeVisible:(NSNotification*)notification { + [[SKInvalidation sharedInstance].delegate + invalidateNode:[notification.object nextResponder]]; +} + ++ (void)windowDidBecomeHidden:(NSNotification*)notification { + [[SKInvalidation sharedInstance].delegate + invalidateNode:[notification.object nextResponder]]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.h new file mode 100644 index 000000000..3d7139e14 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@interface SKNamed<__covariant T> : NSObject + ++ (instancetype)newWithName:(NSString*)name withValue:(T)value; + +@property(nonatomic, readonly) NSString* name; +@property(nonatomic, readonly) T value; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.mm new file mode 100644 index 000000000..ebf421209 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.mm @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKNamed.h" + +@implementation SKNamed + ++ (instancetype)newWithName:(NSString*)name withValue:(id)value { + return [[SKNamed alloc] initWithName:name withValue:value]; +} + +- (instancetype)initWithName:(NSString*)name withValue:(id)value { + if (self = [super init]) { + _name = name; + _value = value; + } + + return self; +} + +- (NSString*)description { + return [NSString stringWithFormat:@"%@: %@", _name, _value]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h new file mode 100644 index 000000000..c805b8c64 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKDescriptorMapper.h" +#import "SKNamed.h" +#import "SKTouch.h" + +typedef void (^SKNodeUpdateData)(id value); + +/** + A SKNodeDescriptor is an object which know how to expose an Object of type T + to SonarKitLayoutPlugin. This class is the extension point for + SonarKitLayoutPlugin and is how custom objects or data can be exposed to Sonar. + */ +@interface SKNodeDescriptor<__covariant T> : NSObject + +/** + If the descriptor class is dependent on some set-up, use this. + This is invoked once Sonar connects. + */ +- (void)setUp; + +/** + Initializes the node-descriptor with a SKDescriptorMapper which contains + mappings between Class -> SKNodeDescriptor<Class>. + */ +- (instancetype)initWithDescriptorMapper:(SKDescriptorMapper*)mapper; + +/** + Gets the node-descriptor registered for a specific class. + */ +- (SKNodeDescriptor*)descriptorForClass:(Class)cls; + +/** + A globally unique ID used to identify a node in the hierarchy. This is used + in the communication between SonarKitLayoutPlugin and the Sonar desktop + application in order to identify nodes. + */ +- (NSString*)identifierForNode:(T)node; + +/** + An ID which is equal between reflowing components is needed to get the + identifier of root node of a tree which need to be invalidated on + FlipperKitLayoutPlugin side. +*/ +- (NSString*)identifierForInvalidation:(T)node; + +/** + The name used to identify this node in the Sonar desktop application. This is + what will be visible in the hierarchy. + */ +- (NSString*)nameForNode:(T)node; + +/** + The number of children this node exposes in the layout hierarchy. + */ +- (NSUInteger)childCountForNode:(T)node; + +/** + Get the child for a specific node at a specified index. + */ +- (id)childForNode:(T)node atIndex:(NSUInteger)index; + +/** + Get the data to show for this node in the sidebar of the Sonar application. The + objects will be shown in order by SKNamed.name as their header. + */ +- (NSArray<SKNamed<NSDictionary*>*>*)dataForNode:(T)node; + +/** + Get the attributes for this node. Attributes will be showed in the Sonar + application right next to the name of the node. + */ +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(T)node; + +/** + A mapping of the path for a specific value, and a block responsible for + updating its corresponding value for a specific node. + + The paths (string) is dependent on what `dataForNode` returns (e.g + "SKNodeDescriptor.name"). + */ +- (NSDictionary<NSString*, SKNodeUpdateData>*)dataMutationsForNode:(T)node; + +/** + This is used in order to highlight any specific node which is currently + selected in the Sonar application. The plugin automatically takes care of + de-selecting the previously highlighted node. + */ +- (void)setHighlighted:(BOOL)highlighted forNode:(T)node; + +/** + Perform hit testing on the given node. Either continue the search in + one of the children of the node, or finish the hit testing on this + node. + */ +- (void)hitTest:(SKTouch*)point forNode:(T)node; + +/** + Invalidate a specific node. This is called once a node is removed or added + from or to the layout hierarchy. + */ +- (void)invalidateNode:(T)node; + +/** + The decoration for this node. Valid values are defined in the Sonar + applictation. + */ +- (NSString*)decorationForNode:(T)node; + +/** + Whether the node matches the given query. + Used for layout search. + */ +- (BOOL)matchesQuery:(NSString*)query forNode:(T)node; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.mm new file mode 100644 index 000000000..2fd413baa --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.mm @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKNodeDescriptor.h" +#import <FlipperKitLayoutTextSearchable/FKTextSearchable.h> + +@implementation SKNodeDescriptor { + SKDescriptorMapper* _mapper; +} + +- (void)setUp { +} + +- (instancetype)initWithDescriptorMapper:(SKDescriptorMapper*)mapper { + if (self = [super init]) { + _mapper = mapper; + } + return self; +} + +- (SKNodeDescriptor*)descriptorForClass:(Class)cls { + return [_mapper descriptorForClass:cls]; +} + +- (NSString*)identifierForNode:(id)node { + @throw [NSString + stringWithFormat:@"need to implement %@", NSStringFromSelector(_cmd)]; +} + +- (NSString*)identifierForInvalidation:(id)node { + return [self identifierForNode:node]; +} + +- (NSString*)nameForNode:(id)node { + return NSStringFromClass([node class]); +} + +- (NSUInteger)childCountForNode:(id)node { + @throw [NSString + stringWithFormat:@"need to implement %@", NSStringFromSelector(_cmd)]; +} + +- (id)childForNode:(id)node atIndex:(NSUInteger)index { + @throw [NSString + stringWithFormat:@"need to implement %@", NSStringFromSelector(_cmd)]; +} + +- (NSDictionary<NSString*, SKNodeUpdateData>*)dataMutationsForNode:(id)node { + return @{}; +} + +- (NSArray<SKNamed<NSDictionary*>*>*)dataForNode:(id)node { + return @[]; +} + +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(id)node { + return @[]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(id)node { +} + +- (void)hitTest:(SKTouch*)point forNode:(id)node { +} + +- (void)invalidateNode:(id)node { +} + +- (NSString*)decorationForNode:(id)node { + return @""; +} + +- (BOOL)matchesQuery:(NSString*)query forNode:(id)node { + NSString* name = [self nameForNode:node]; + NSString* text = nil; + if ([node conformsToProtocol:@protocol(FKTextSearchable)]) { + text = [node searchableText]; + } + return [self string:name contains:query] || + [self string:[self identifierForNode:node] contains:query] || + [self string:text contains:query]; +} + +- (BOOL)string:(NSString*)string contains:(NSString*)substring { + return string != nil && substring != nil && + [string rangeOfString:substring options:NSCaseInsensitiveSearch] + .location != NSNotFound; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.h new file mode 100644 index 000000000..78f7fef44 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +@protocol SKSonarValueCoder + ++ (instancetype)fromSonarValue:(id)sonarValue; + +- (NSDictionary<NSString*, id<NSObject>>*)sonarValue; + +@end + +class SKObject { + public: + SKObject(CGRect rect); + SKObject(CGSize size); + SKObject(CGPoint point); + SKObject(UIEdgeInsets insets); + SKObject(CGAffineTransform transform); + SKObject(id<SKSonarValueCoder> value); + SKObject(id value); + + operator id<NSObject>() const noexcept { + return _actual ?: [NSNull null]; + } + + protected: + id<NSObject> _actual; +}; + +class SKMutableObject : public SKObject { + public: + SKMutableObject(CGRect rect) : SKObject(rect) {} + SKMutableObject(CGSize size) : SKObject(size){}; + SKMutableObject(CGPoint point) : SKObject(point){}; + SKMutableObject(UIEdgeInsets insets) : SKObject(insets){}; + SKMutableObject(CGAffineTransform transform) : SKObject(transform){}; + SKMutableObject(id<SKSonarValueCoder> value) : SKObject(value){}; + SKMutableObject(id value) : SKObject(value){}; + + operator id<NSObject>() { + convertToMutable(); + return _actual; + } + + protected: + BOOL _convertedToMutable = NO; + void convertToMutable(); +}; diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.mm new file mode 100644 index 000000000..710137a41 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.mm @@ -0,0 +1,112 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKObject.h" + +SKObject::SKObject(CGRect rect) { + _actual = @{@"origin" : SKObject(rect.origin), @"size" : SKObject(rect.size)}; +} + +SKObject::SKObject(CGSize size) { + _actual = @{@"height" : @(size.height), @"width" : @(size.width)}; +} + +SKObject::SKObject(CGPoint point) { + _actual = @{@"x" : @(point.x), @"y" : @(point.y)}; +} + +SKObject::SKObject(UIEdgeInsets insets) { + _actual = @{ + @"top" : @(insets.top), + @"bottom" : @(insets.bottom), + @"left" : @(insets.left), + @"right" : @(insets.right), + }; +} + +SKObject::SKObject(CGAffineTransform transform) { + _actual = @{ + @"a" : @(transform.a), + @"b" : @(transform.b), + @"c" : @(transform.c), + @"d" : @(transform.d), + @"tx" : @(transform.tx), + @"ty" : @(transform.ty), + }; +} + +SKObject::SKObject(id<SKSonarValueCoder> value) : _actual([value sonarValue]) {} + +SKObject::SKObject(id value) : _actual(value) {} + +static NSString* _objectType(id<NSObject> object) { + if ([object isKindOfClass:[NSDictionary class]]) { + return (NSString*)((NSDictionary*)object)[@"__type__"]; + } + + return nil; +} + +static id<NSObject> _objectValue(id<NSObject> object) { + if ([object isKindOfClass:[NSDictionary class]]) { + return ((NSDictionary*)object)[@"value"]; + } + + return object; +} + +static NSDictionary<NSString*, id<NSObject>>* _SKValue( + id<NSObject> object, + BOOL isMutable) { + NSString* type = _objectType(object); + id<NSObject> value = _objectValue(object); + + return @{ + @"__type__" : (type != nil ? type : @"auto"), + @"__mutable__" : @(isMutable), + @"value" : (value != nil ? value : [NSNull null]), + }; +} + +static NSDictionary* _SKMutable( + const NSDictionary<NSString*, id<NSObject>>* skObject) { + NSMutableDictionary* mutableObject = [NSMutableDictionary new]; + for (NSString* key : skObject) { + id<NSObject> value = skObject[key]; + + if (_objectType(value) != nil) { + mutableObject[key] = _SKValue(value, YES); + } else if ([value isKindOfClass:[NSDictionary class]]) { + auto objectValue = (NSDictionary<NSString*, id<NSObject>>*)value; + mutableObject[key] = _SKMutable(objectValue); + } else { + mutableObject[key] = _SKValue(value, YES); + } + } + + return mutableObject; +} + +void SKMutableObject::convertToMutable() { + if (_convertedToMutable) { + return; + } + + if (_objectType(_actual) == nil && + [_actual isKindOfClass:[NSDictionary class]]) { + auto object = (const NSDictionary<NSString*, id<NSObject>>*)_actual; + _actual = _SKMutable(object); + } else { + _actual = _SKValue(_actual, YES); + } + + _convertedToMutable = YES; +} + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.h new file mode 100644 index 000000000..2e9abaa50 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef SKSearchResultNode_h +#define SKSearchResultNode_h + +#import <Foundation/Foundation.h> + +@interface SKSearchResultNode : NSObject + +@property(nonatomic, copy, readonly) NSString* nodeId; + +- (instancetype)initWithNode:(NSString*)nodeId + asMatch:(BOOL)isMatch + withElement:(NSDictionary*)element + andChildren:(NSArray<SKSearchResultNode*>*)children; + +- (NSDictionary*)toNSDictionary; + +@end +#endif /* SKSearchResultNode_h */ diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.m new file mode 100644 index 000000000..cdba61423 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.m @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "SKSearchResultNode.h" + +@implementation SKSearchResultNode { + NSString* _nodeId; + BOOL _isMatch; + NSDictionary* _element; + NSArray<SKSearchResultNode*>* _children; +} + +- (instancetype)initWithNode:(NSString*)nodeId + asMatch:(BOOL)isMatch + withElement:(NSDictionary*)element + andChildren:(NSArray<SKSearchResultNode*>*)children { + self = [super init]; + if (self) { + _nodeId = nodeId; + _isMatch = isMatch; + _element = element; + _children = children; + } + return self; +} + +- (NSDictionary*)toNSDictionary { + if (_element == nil) { + return nil; + } + NSMutableArray<NSDictionary*>* childArray; + if (_children) { + childArray = [NSMutableArray new]; + for (SKSearchResultNode* child in _children) { + NSDictionary* childDict = [child toNSDictionary]; + if (childDict) { + [childArray addObject:childDict]; + } + } + } else { + childArray = nil; + } + return @{ + @"id" : _nodeId, + @"isMatch" : @(_isMatch), + @"element" : _element, + @"children" : childArray ?: [NSNull null] + }; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListener.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListener.h new file mode 100644 index 000000000..a136a5029 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListener.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +typedef void (^SKTapReceiver)(CGPoint touchPoint); + +@protocol SKTapListener + +@property(nonatomic, readonly) BOOL isMounted; + +- (void)mountWithFrame:(CGRect)frame; + +- (void)unmount; + +- (void)listenForTapWithBlock:(SKTapReceiver)receiver; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.h new file mode 100644 index 000000000..0dd285efa --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "SKTapListener.h" + +@interface SKTapListenerImpl + : NSObject<SKTapListener, UIGestureRecognizerDelegate> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.m new file mode 100644 index 000000000..18e92e36d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.m @@ -0,0 +1,92 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKTapListenerImpl.h" + +#import "SKHiddenWindow.h" + +#import <FlipperKitHighlightOverlay/SKHighlightOverlay.h> + +@implementation SKTapListenerImpl { + NSMutableArray<SKTapReceiver>* _receiversWaitingForInput; + UITapGestureRecognizer* _gestureRecognizer; + + SKHiddenWindow* _overlayWindow; +} + +@synthesize isMounted = _isMounted; + +- (instancetype)init { + if (self = [super init]) { + _receiversWaitingForInput = [NSMutableArray new]; + + _gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:nil]; + _gestureRecognizer.delegate = self; + + _isMounted = NO; + + _overlayWindow = [SKHiddenWindow new]; + _overlayWindow.hidden = YES; + _overlayWindow.windowLevel = UIWindowLevelAlert; + _overlayWindow.backgroundColor = [SKHighlightOverlay overlayColor]; + + [_overlayWindow addGestureRecognizer:_gestureRecognizer]; + } + + return self; +} + +- (void)mountWithFrame:(CGRect)frame { + if (_isMounted) { + return; + } + + [_overlayWindow setFrame:frame]; + [_overlayWindow makeKeyAndVisible]; + _overlayWindow.hidden = NO; + [[UIApplication sharedApplication].delegate.window addSubview:_overlayWindow]; + _isMounted = YES; +} + +- (void)unmount { + if (!_isMounted) { + return; + } + + [_receiversWaitingForInput removeAllObjects]; + [_overlayWindow removeFromSuperview]; + _overlayWindow.hidden = YES; + _isMounted = NO; +} + +- (void)listenForTapWithBlock:(SKTapReceiver)receiver { + [_receiversWaitingForInput addObject:receiver]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldReceiveTouch:(UITouch*)touch { + if ([_receiversWaitingForInput count] == 0) { + return YES; + } + + CGPoint touchPoint = [touch locationInView:_overlayWindow]; + + for (SKTapReceiver recv in _receiversWaitingForInput) { + recv(touchPoint); + } + + [_receiversWaitingForInput removeAllObjects]; + + return NO; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.h new file mode 100644 index 000000000..8b3b77f84 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKDescriptorMapper.h" + +typedef void (^SKTouchFinishDelegate)(NSArray<NSString*>* path); + +@interface SKTouch : NSObject + +- (instancetype)initWithTouchPoint:(CGPoint)touchPoint + withRootNode:(id<NSObject>)node + withDescriptorMapper:(SKDescriptorMapper*)mapper + finishWithBlock:(SKTouchFinishDelegate)d; + +- (void)continueWithChildIndex:(NSUInteger)childIndex + withOffset:(CGPoint)offset; + +- (void)finish; + +- (BOOL)containedIn:(CGRect)bounds; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.m new file mode 100644 index 000000000..53efa3222 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.m @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKTouch.h" +#import "SKNodeDescriptor.h" + +@implementation SKTouch { + SKTouchFinishDelegate _onFinish; + NSMutableArray<NSString*>* _path; + + CGPoint _currentTouchPoint; + id<NSObject> _currentNode; + + SKDescriptorMapper* _descriptorMapper; +} + +- (instancetype)initWithTouchPoint:(CGPoint)touchPoint + withRootNode:(id<NSObject>)node + withDescriptorMapper:(SKDescriptorMapper*)mapper + finishWithBlock:(SKTouchFinishDelegate)finishBlock { + if (self = [super init]) { + _onFinish = finishBlock; + _currentTouchPoint = touchPoint; + _currentNode = node; + _descriptorMapper = mapper; + _path = [NSMutableArray new]; + } + + return self; +} + +- (void)continueWithChildIndex:(NSUInteger)childIndex + withOffset:(CGPoint)offset { + _currentTouchPoint.x -= offset.x; + _currentTouchPoint.y -= offset.y; + + SKNodeDescriptor* descriptor = + [_descriptorMapper descriptorForClass:[_currentNode class]]; + _currentNode = [descriptor childForNode:_currentNode atIndex:childIndex]; + + descriptor = [_descriptorMapper descriptorForClass:[_currentNode class]]; + [_path addObject:[descriptor identifierForNode:_currentNode]]; + + [descriptor hitTest:self forNode:_currentNode]; +} + +- (void)finish { + _onFinish(_path); +} + +- (BOOL)containedIn:(CGRect)bounds { + return CGRectContainsPoint(bounds, _currentTouchPoint); +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h new file mode 100644 index 000000000..c000864ff --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import <FlipperKit/SKMacros.h> + +FB_LINK_REQUIRE_CATEGORY(UICollectionView_SKInvalidation) +@interface UICollectionView (SKInvalidation) + ++ (void)enableInvalidations; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.mm new file mode 100644 index 000000000..4c39ccb62 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.mm @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UICollectionView+SKInvalidation.h" + +#import "SKInvalidation.h" +#import "SKSwizzle.h" + +FB_LINKABLE(UICollectionView_SKInvalidation) +@implementation UICollectionView (SKInvalidation) + ++ (void)enableInvalidations { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + swizzleMethods( + [self class], + @selector(cellForItemAtIndexPath:), + @selector(swizzle_cellForItemAtIndexPath:)); + }); +} + +- (UICollectionViewCell*)swizzle_cellForItemAtIndexPath: + (NSIndexPath*)indexPath { + dispatch_async(dispatch_get_main_queue(), ^{ + [[SKInvalidation sharedInstance].delegate invalidateNode:self]; + }); + + return [self swizzle_cellForItemAtIndexPath:indexPath]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h new file mode 100644 index 000000000..ce237354c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import <FlipperKit/SKMacros.h> + +#import "SKObject.h" + +FB_LINK_REQUIRE_CATEGORY(UIColor_SonarValueCoder) +@interface UIColor (SonarValueCoder)<SKSonarValueCoder> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.mm new file mode 100644 index 000000000..de5fffaf2 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.mm @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIColor+SKSonarValueCoder.h" + +FB_LINKABLE(UIColor_SonarValueCoder) +@implementation UIColor (SonarValueCoder) + ++ (instancetype)fromSonarValue:(NSNumber*)sonarValue { + NSUInteger intColor = [sonarValue integerValue]; + + CGFloat r, g, b, a; + + b = CGFloat(intColor & 0xFF) / 255; + g = CGFloat((intColor >> 8) & 0xFF) / 255; + r = CGFloat((intColor >> 16) & 0xFF) / 255; + a = CGFloat((intColor >> 24) & 0xFF) / 255; + + return [[UIColor alloc] initWithRed:r green:g blue:b alpha:a]; +} + +- (NSDictionary<NSString*, id<NSObject>>*)sonarValue { + CGColorSpaceRef colorSpace = CGColorGetColorSpace([self CGColor]); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + + NSUInteger red, green, blue, alpha; + + switch (colorSpaceModel) { + case kCGColorSpaceModelUnknown: + case kCGColorSpaceModelRGB: { + CGFloat r, g, b, a; + [self getRed:&r green:&g blue:&b alpha:&a]; + + red = (NSUInteger)(r * 255) & 0xFF; + green = (NSUInteger)(g * 255) & 0xFF; + blue = (NSUInteger)(b * 255) & 0xFF; + alpha = (NSUInteger)(a * 255) & 0xFF; + } break; + + case kCGColorSpaceModelMonochrome: { + CGFloat a, w; + [self getWhite:&w alpha:&a]; + + red = green = blue = (NSUInteger)(w * 255) & 0xFF; + alpha = (NSUInteger)(a * 255) & 0xFF; + } break; + + default: + red = green = blue = alpha = 0; + } + + NSUInteger intColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + return + @{@"__type__" : @"color", @"__mutable__" : @NO, @"value" : @(intColor)}; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.h new file mode 100644 index 000000000..4697183ae --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <FlipperKit/SKMacros.h> + +FB_LINK_REQUIRE_CATEGORY(UIView_SKInvalidation) +@interface UIView (SKInvalidation) + ++ (void)enableInvalidation; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm new file mode 100644 index 000000000..3da4f961b --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm @@ -0,0 +1,66 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <UIKit/UIKit.h> +#import <objc/runtime.h> + +#import "SKInvalidation.h" +#import "SKSwizzle.h" +#import "UIView+SKInvalidation.h" + +FB_LINKABLE(UIView_SKInvalidation) +@implementation UIView (SKInvalidation) + ++ (void)enableInvalidation { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + swizzleMethods( + [self class], @selector(setHidden:), @selector(swizzle_setHidden:)); + swizzleMethods( + [self class], @selector(addSubview:), @selector(swizzle_addSubview:)); + swizzleMethods( + [self class], + @selector(removeFromSuperview), + @selector(swizzle_removeFromSuperview)); + }); +} + +- (void)swizzle_setHidden:(BOOL)hidden { + [self swizzle_setHidden:hidden]; + + id<SKInvalidationDelegate> delegate = + [SKInvalidation sharedInstance].delegate; + if (delegate != nil) { + [delegate invalidateNode:self.superview]; + } +} + +- (void)swizzle_addSubview:(UIView*)view { + [self swizzle_addSubview:view]; + + id<SKInvalidationDelegate> delegate = + [SKInvalidation sharedInstance].delegate; + if (delegate != nil) { + [delegate invalidateNode:view]; + } +} + +- (void)swizzle_removeFromSuperview { + id<SKInvalidationDelegate> delegate = + [SKInvalidation sharedInstance].delegate; + if (delegate != nil && self.superview != nil) { + [delegate invalidateNode:self.superview]; + } + + [self swizzle_removeFromSuperview]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.h new file mode 100644 index 000000000..3eb967d81 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKNodeDescriptor.h" + +@interface SKApplicationDescriptor : SKNodeDescriptor<UIApplication*> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.m new file mode 100644 index 000000000..354622cee --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.m @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKApplicationDescriptor.h" + +#import <objc/runtime.h> +#import "SKDescriptorMapper.h" +#import "SKHiddenWindow.h" + +@implementation SKApplicationDescriptor + +- (NSString*)identifierForNode:(UIApplication*)node { + return [NSString stringWithFormat:@"%p", node]; +} + +- (NSUInteger)childCountForNode:(UIApplication*)node { + return [[self visibleChildrenForNode:node] count]; +} + +- (id)childForNode:(UIApplication*)node atIndex:(NSUInteger)index { + return [self visibleChildrenForNode:node][index]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(UIApplication*)node { + SKNodeDescriptor* windowDescriptor = + [self descriptorForClass:[UIWindow class]]; + [windowDescriptor setHighlighted:highlighted forNode:[node keyWindow]]; +} + +- (void)hitTest:(SKTouch*)touch forNode:(UIApplication*)node { + for (NSInteger index = [self childCountForNode:node] - 1; index >= 0; + index--) { + UIWindow* child = [self childForNode:node atIndex:index]; + if (child.isHidden || child.alpha <= 0) { + continue; + } + + if ([touch containedIn:child.frame]) { + [touch continueWithChildIndex:index withOffset:child.frame.origin]; + return; + } + } + + [touch finish]; +} + +- (NSArray<UIWindow*>*)visibleChildrenForNode:(UIApplication*)node { + NSMutableArray<UIWindow*>* children = [NSMutableArray new]; + for (UIWindow* window in node.windows) { + if ([window isKindOfClass:[SKHiddenWindow class]] || + [window + isKindOfClass:objc_lookUpClass("FBAccessibilityOverlayWindow")] || + [window isKindOfClass:objc_lookUpClass("UITextEffectsWindow")] || + [window isKindOfClass:objc_lookUpClass("FBStatusBarTrackingWindow")]) { + continue; + } + [children addObject:window]; + } + return children; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.h new file mode 100644 index 000000000..0bc6b22e5 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKNodeDescriptor.h" + +@class UIButton; + +@interface SKButtonDescriptor : SKNodeDescriptor<UIButton*> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.mm new file mode 100644 index 000000000..63b6c5530 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.mm @@ -0,0 +1,97 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKButtonDescriptor.h" + +#import "SKDescriptorMapper.h" +#import "SKObject.h" +#import "UIColor+SKSonarValueCoder.h" + +@implementation SKButtonDescriptor + +- (NSString*)identifierForNode:(UIButton*)node { + return [NSString stringWithFormat:@"%p", node]; +} + +- (NSUInteger)childCountForNode:(UIButton*)node { + return 0; +} + +- (id)childForNode:(UIButton*)node atIndex:(NSUInteger)index { + return nil; +} + +- (NSArray<SKNamed<NSDictionary*>*>*)dataForNode:(UIButton*)node { + SKNodeDescriptor* viewDescriptor = [self descriptorForClass:[UIView class]]; + auto* viewData = [viewDescriptor dataForNode:node]; + + NSMutableArray* data = [NSMutableArray new]; + [data addObjectsFromArray:viewData]; + [data addObject:[SKNamed + newWithName:@"UIButton" + withValue:@{ + @"focused" : @(node.focused), + @"enabled" : SKMutableObject(@(node.enabled)), + @"highlighted" : SKMutableObject(@(node.highlighted)), + @"titleEdgeInsets" : SKObject(node.titleEdgeInsets), + @"titleLabel" : SKMutableObject( + node.titleLabel.attributedText.string + .stringByStandardizingPath), + @"currentTitleColor" : + SKMutableObject(node.currentTitleColor), + }]]; + return data; +} + +- (NSDictionary<NSString*, SKNodeUpdateData>*)dataMutationsForNode: + (UIButton*)node { + NSDictionary* buttonMutations = + @{@"UIButton.titleLabel" : ^(NSString* newValue){ + [node setTitle:newValue forState:node.state]; +} +, + @"UIButton.currentTitleColor": ^(NSNumber *newValue) { + [node setTitleColor: [UIColor fromSonarValue: newValue] forState: node.state]; + }, + @"UIButton.highlighted": ^(NSNumber *highlighted) { + [node setHighlighted: [highlighted boolValue]]; + }, + @"UIButton.enabled": ^(NSNumber *enabled) { + [node setEnabled: [enabled boolValue]]; + } +} +; + +SKNodeDescriptor* viewDescriptor = [self descriptorForClass:[UIView class]]; +NSDictionary* viewMutations = [viewDescriptor dataMutationsForNode:node]; + +NSMutableDictionary* mutations = [NSMutableDictionary new]; +[mutations addEntriesFromDictionary:buttonMutations]; +[mutations addEntriesFromDictionary:viewMutations]; + +return mutations; +} + +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor attributesForNode:node]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(UIButton*)node { + SKNodeDescriptor* viewDescriptor = [self descriptorForClass:[UIView class]]; + [viewDescriptor setHighlighted:highlighted forNode:node]; +} + +- (void)hitTest:(SKTouch*)touch forNode:(UIButton*)node { + [touch finish]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.h new file mode 100644 index 000000000..b9d029de0 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKNodeDescriptor.h" + +@interface SKScrollViewDescriptor : SKNodeDescriptor<UIScrollView*> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.m new file mode 100644 index 000000000..bd4f33924 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.m @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKScrollViewDescriptor.h" + +#import "SKDescriptorMapper.h" + +@implementation SKScrollViewDescriptor + +- (NSString*)identifierForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor identifierForNode:node]; +} + +- (NSUInteger)childCountForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor childCountForNode:node]; +} + +- (id)childForNode:(UIScrollView*)node atIndex:(NSUInteger)index { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor childForNode:node atIndex:index]; +} + +- (id)dataForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor dataForNode:node]; +} + +- (id)dataMutationsForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor dataMutationsForNode:node]; +} + +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + return [descriptor attributesForNode:node]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(UIScrollView*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + [descriptor setHighlighted:highlighted forNode:node]; +} + +- (void)hitTest:(SKTouch*)touch forNode:(UIScrollView*)node { + for (NSInteger index = [self childCountForNode:node] - 1; index >= 0; + index--) { + id<NSObject> childNode = [self childForNode:node atIndex:index]; + CGRect frame; + + if ([childNode isKindOfClass:[UIViewController class]]) { + UIViewController* child = (UIViewController*)childNode; + if (child.view.isHidden) { + continue; + } + + frame = child.view.frame; + } else { + UIView* child = (UIView*)childNode; + if (child.isHidden) { + continue; + } + + frame = child.frame; + } + + frame.origin.x -= node.contentOffset.x; + frame.origin.y -= node.contentOffset.y; + + if ([touch containedIn:frame]) { + [touch continueWithChildIndex:index withOffset:frame.origin]; + return; + } + } + + [touch finish]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.h new file mode 100644 index 000000000..2f8fd4f18 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +#import "SKNodeDescriptor.h" + +@interface SKViewControllerDescriptor : SKNodeDescriptor<UIViewController*> + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.m new file mode 100644 index 000000000..3cc042947 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKViewControllerDescriptor.h" + +#import "SKDescriptorMapper.h" + +@implementation SKViewControllerDescriptor + +- (NSString*)identifierForNode:(UIViewController*)node { + return [NSString stringWithFormat:@"%p", node]; +} + +- (NSUInteger)childCountForNode:(UIViewController*)node { + return 1; +} + +- (id)childForNode:(UIViewController*)node atIndex:(NSUInteger)index { + return node.view; +} + +- (void)setHighlightedForNode:(UIViewController*)node { +} + +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(UIViewController*)node { + return @[ [SKNamed newWithName:@"addr" + withValue:[NSString stringWithFormat:@"%p", node]] ]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(UIViewController*)node { + SKNodeDescriptor* descriptor = [self descriptorForClass:[UIView class]]; + [descriptor setHighlighted:highlighted forNode:node.view]; +} + +- (void)hitTest:(SKTouch*)touch forNode:(UIViewController*)node { + [touch continueWithChildIndex:0 withOffset:(CGPoint){0, 0}]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.h new file mode 100644 index 000000000..d3eab4f37 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <UIKit/UIKit.h> + +#import "SKNodeDescriptor.h" + +@interface SKViewDescriptor : SKNodeDescriptor<UIView*> + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm new file mode 100644 index 000000000..0a6bc759e --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm @@ -0,0 +1,661 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKViewDescriptor.h" + +#import <FlipperKitHighlightOverlay/SKHighlightOverlay.h> +#import <YogaKit/UIView+Yoga.h> +#import "SKDescriptorMapper.h" +#import "SKHiddenWindow.h" +#import "SKNamed.h" +#import "SKObject.h" +#import "SKYogaKitHelper.h" +#import "UIColor+SKSonarValueCoder.h" + +@implementation SKViewDescriptor + +static NSDictionary* YGDirectionEnumMap = nil; +static NSDictionary* YGFlexDirectionEnumMap = nil; +static NSDictionary* YGJustifyEnumMap = nil; +static NSDictionary* YGAlignEnumMap = nil; +static NSDictionary* YGPositionTypeEnumMap = nil; +static NSDictionary* YGWrapEnumMap = nil; +static NSDictionary* YGOverflowEnumMap = nil; +static NSDictionary* YGDisplayEnumMap = nil; +static NSDictionary* YGUnitEnumMap = nil; + +- (instancetype)initWithDescriptorMapper:(SKDescriptorMapper*)mapper { + if (self = [super initWithDescriptorMapper:mapper]) { + initEnumDictionaries(); + } + + return self; +} + +- (NSString*)identifierForNode:(UIView*)node { + return [NSString stringWithFormat:@"%p", node]; +} + +- (NSUInteger)childCountForNode:(UIView*)node { + return [[self validChildrenForNode:node] count]; +} + +- (id)childForNode:(UIView*)node atIndex:(NSUInteger)index { + return [[self validChildrenForNode:node] objectAtIndex:index]; +} + +- (NSArray*)validChildrenForNode:(UIView*)node { + NSMutableArray* validChildren = [NSMutableArray new]; + + // Use UIViewControllers for children which responds to a different + // viewController than their parent + for (UIView* child in node.subviews) { + BOOL responderIsUIViewController = + [child.nextResponder isKindOfClass:[UIViewController class]]; + + if (!child.isHidden) { + if (responderIsUIViewController && + child.nextResponder != node.nextResponder) { + [validChildren addObject:child.nextResponder]; + } else { + [validChildren addObject:child]; + } + } + } + + return validChildren; +} + +- (NSArray<SKNamed<NSDictionary*>*>*)dataForNode:(UIView*)node { + return [NSArray + arrayWithObjects: + [SKNamed + newWithName:@"UIView" + withValue:@{ + @"frame" : SKMutableObject(node.frame), + @"bounds" : SKObject(node.bounds), + @"center" : SKObject(node.center), + @"layoutMargins" : SKObject(node.layoutMargins), + @"clipsToBounds" : @(node.clipsToBounds), + @"alpha" : SKMutableObject(@(node.alpha)), + @"tag" : @(node.tag), + @"backgroundColor" : SKMutableObject(node.backgroundColor) + }], + [SKNamed + newWithName:@"CALayer" + withValue:@{ + @"shadowColor" : SKMutableObject( + [UIColor colorWithCGColor:node.layer.shadowColor]), + @"shadowOpacity" : + SKMutableObject(@(node.layer.shadowOpacity)), + @"shadowRadius" : SKMutableObject(@(node.layer.shadowRadius)), + @"shadowOffset" : SKMutableObject(node.layer.shadowOffset), + @"backgroundColor" : SKMutableObject( + [UIColor colorWithCGColor:node.layer.backgroundColor]), + @"borderColor" : SKMutableObject( + [UIColor colorWithCGColor:node.layer.borderColor]), + @"borderWidth" : SKMutableObject(@(node.layer.borderWidth)), + @"cornerRadius" : SKMutableObject(@(node.layer.cornerRadius)), + @"masksToBounds" : + SKMutableObject(@(node.layer.masksToBounds)), + }], + [SKNamed newWithName:@"Accessibility" + withValue:@{ + @"isAccessibilityElement" : + SKMutableObject(@(node.isAccessibilityElement)), + @"accessibilityLabel" : + SKMutableObject(node.accessibilityLabel ?: @""), + @"accessibilityIdentifier" : + SKMutableObject(node.accessibilityIdentifier ?: @""), + @"accessibilityValue" : + SKMutableObject(node.accessibilityValue ?: @""), + @"accessibilityHint" : + SKMutableObject(node.accessibilityHint ?: @""), + @"accessibilityTraits" : + AccessibilityTraitsDict(node.accessibilityTraits), + @"accessibilityViewIsModal" : + SKMutableObject(@(node.accessibilityViewIsModal)), + @"shouldGroupAccessibilityChildren" : SKMutableObject( + @(node.shouldGroupAccessibilityChildren)), + }], + !node.isYogaEnabled + ? nil + : [SKNamed + newWithName:@"YGLayout" + withValue:@{ + @"direction" : SKMutableObject( + YGDirectionEnumMap[@(node.yoga.direction)]), + @"justifyContent" : SKMutableObject( + YGJustifyEnumMap[@(node.yoga.justifyContent)]), + @"aligns" : @{ + @"alignContent" : SKMutableObject( + YGAlignEnumMap[@(node.yoga.alignContent)]), + @"alignItems" : SKMutableObject( + YGAlignEnumMap[@(node.yoga.alignItems)]), + @"alignSelf" : SKMutableObject( + YGAlignEnumMap[@(node.yoga.alignSelf)]), + }, + @"position" : @{ + @"type" : SKMutableObject( + YGPositionTypeEnumMap[@(node.yoga.position)]), + @"left" : SKYGValueObject(node.yoga.left), + @"top" : SKYGValueObject(node.yoga.top), + @"right" : SKYGValueObject(node.yoga.right), + @"bottom" : SKYGValueObject(node.yoga.bottom), + @"start" : SKYGValueObject(node.yoga.start), + @"end" : SKYGValueObject(node.yoga.end), + }, + @"overflow" : SKMutableObject( + YGOverflowEnumMap[@(node.yoga.overflow)]), + @"display" : SKMutableObject( + YGDisplayEnumMap[@(node.yoga.display)]), + @"flex" : @{ + @"flexDirection" : + SKMutableObject(YGFlexDirectionEnumMap[ + @(node.yoga.flexDirection)]), + @"flexWrap" : SKMutableObject( + YGWrapEnumMap[@(node.yoga.flexWrap)]), + @"flexGrow" : SKMutableObject(@(node.yoga.flexGrow)), + @"flexShrink" : + SKMutableObject(@(node.yoga.flexShrink)), + @"flexBasis" : SKYGValueObject(node.yoga.flexBasis), + }, + @"margin" : @{ + @"left" : SKYGValueObject(node.yoga.marginLeft), + @"top" : SKYGValueObject(node.yoga.marginTop), + @"right" : SKYGValueObject(node.yoga.marginRight), + @"bottom" : SKYGValueObject(node.yoga.marginBottom), + @"start" : SKYGValueObject(node.yoga.marginStart), + @"end" : SKYGValueObject(node.yoga.marginEnd), + @"horizontal" : + SKYGValueObject(node.yoga.marginHorizontal), + @"vertical" : + SKYGValueObject(node.yoga.marginVertical), + @"all" : SKYGValueObject(node.yoga.margin), + }, + @"padding" : @{ + @"left" : SKYGValueObject(node.yoga.paddingLeft), + @"top" : SKYGValueObject(node.yoga.paddingTop), + @"right" : SKYGValueObject(node.yoga.paddingRight), + @"bottom" : SKYGValueObject(node.yoga.paddingBottom), + @"start" : SKYGValueObject(node.yoga.paddingStart), + @"end" : SKYGValueObject(node.yoga.paddingEnd), + @"horizontal" : + SKYGValueObject(node.yoga.paddingHorizontal), + @"vertical" : + SKYGValueObject(node.yoga.paddingVertical), + @"all" : SKYGValueObject(node.yoga.padding), + }, + @"border" : @{ + @"leftWidth" : + SKMutableObject(@(node.yoga.borderLeftWidth)), + @"topWidth" : + SKMutableObject(@(node.yoga.borderTopWidth)), + @"rightWidth" : + SKMutableObject(@(node.yoga.borderRightWidth)), + @"bottomWidth" : + SKMutableObject(@(node.yoga.borderBottomWidth)), + @"startWidth" : + SKMutableObject(@(node.yoga.borderStartWidth)), + @"endWidth" : + SKMutableObject(@(node.yoga.borderEndWidth)), + @"all" : SKMutableObject(@(node.yoga.borderWidth)), + }, + @"dimensions" : @{ + @"width" : SKYGValueObject(node.yoga.width), + @"height" : SKYGValueObject(node.yoga.height), + @"minWidth" : SKYGValueObject(node.yoga.minWidth), + @"minHeight" : SKYGValueObject(node.yoga.minHeight), + @"maxWidth" : SKYGValueObject(node.yoga.maxWidth), + @"maxHeight" : SKYGValueObject(node.yoga.maxHeight), + }, + @"aspectRatio" : + SKMutableObject(@(node.yoga.aspectRatio)), + @"resolvedDirection" : SKObject( + YGDirectionEnumMap[@(node.yoga.resolvedDirection)]), + }], + nil]; +} + +- (NSDictionary<NSString*, SKNodeUpdateData>*)dataMutationsForNode: + (UIView*)node { + NSDictionary<NSString*, SKNodeUpdateData>* dataMutations = @{ + // UIView + @"UIView.alpha" : ^(NSNumber* value){ + node.alpha = [value floatValue]; +} +, + @"UIView.backgroundColor": ^(NSNumber *value) { + node.backgroundColor = [UIColor fromSonarValue: value]; + }, + @"UIView.frame.origin.y": ^(NSNumber *value) { + CGRect frame = node.frame; + frame.origin.y = [value floatValue]; + node.frame = frame; + }, + @"UIView.frame.origin.x": ^(NSNumber *value) { + CGRect frame = node.frame; + frame.origin.x = [value floatValue]; + node.frame = frame; + }, + @"UIView.frame.size.width": ^(NSNumber *value) { + CGRect frame = node.frame; + frame.size.width = [value floatValue]; + node.frame = frame; + }, + @"UIView.frame.size.height": ^(NSNumber *value) { + CGRect frame = node.frame; + frame.size.width = [value floatValue]; + node.frame = frame; + }, + // CALayer + @"CALayer.shadowColor": ^(NSNumber *value) { + node.layer.shadowColor = [UIColor fromSonarValue:value].CGColor; + }, + @"CALayer.shadowOpacity": ^(NSNumber *value) { + node.layer.shadowOpacity = [value floatValue]; + }, + @"CALayer.shadowRadius": ^(NSNumber *value) { + node.layer.shadowRadius = [value floatValue]; + }, + @"CALayer.shadowOffset.width": ^(NSNumber *value) { + CGSize offset = node.layer.shadowOffset; + offset.width = [value floatValue]; + node.layer.shadowOffset = offset; + }, + @"CALayer.shadowOffset.height": ^(NSNumber *value) { + CGSize offset = node.layer.shadowOffset; + offset.height = [value floatValue]; + node.layer.shadowOffset = offset; + }, + @"CALayer.backgroundColor": ^(NSNumber *value) { + node.layer.backgroundColor = [UIColor fromSonarValue:value].CGColor; + }, + @"CALayer.borderColor": ^(NSNumber *value) { + node.layer.borderColor = [UIColor fromSonarValue:value].CGColor; + }, + @"CALayer.borderWidth": ^(NSNumber *value) { + node.layer.borderWidth = [value floatValue]; + }, + @"CALayer.cornerRadius": ^(NSNumber *value) { + node.layer.cornerRadius = [value floatValue]; + }, + @"CALayer.masksToBounds": ^(NSNumber *value) { + node.layer.masksToBounds = [value boolValue]; + }, + // YGLayout + @"YGLayout.direction": APPLY_ENUM_TO_YOGA_PROPERTY(direction, YGDirection), + @"YGLayout.justifyContent": APPLY_ENUM_TO_YOGA_PROPERTY(justifyContent, YGJustify), + @"YGLayout.aligns.alignContent": APPLY_ENUM_TO_YOGA_PROPERTY(alignContent, YGAlign), + @"YGLayout.aligns.alignItems": APPLY_ENUM_TO_YOGA_PROPERTY(alignItems, YGAlign), + @"YGLayout.aligns.alignSelf": APPLY_ENUM_TO_YOGA_PROPERTY(alignSelf, YGAlign), + @"YGLayout.position.type": APPLY_ENUM_TO_YOGA_PROPERTY(position, YGPositionType), + @"YGLayout.position.left.value": APPLY_VALUE_TO_YGVALUE(left), + @"YGLayout.position.left.unit": APPLY_UNIT_TO_YGVALUE(left, YGUnit), + @"YGLayout.position.top.value": APPLY_VALUE_TO_YGVALUE(top), + @"YGLayout.position.top.unit": APPLY_UNIT_TO_YGVALUE(top, YGUnit), + @"YGLayout.position.right.value": APPLY_VALUE_TO_YGVALUE(right), + @"YGLayout.position.right.unit": APPLY_UNIT_TO_YGVALUE(right, YGUnit), + @"YGLayout.position.bottom.value": APPLY_VALUE_TO_YGVALUE(bottom), + @"YGLayout.position.bottom.unit": APPLY_UNIT_TO_YGVALUE(bottom, YGUnit), + @"YGLayout.position.start.value": APPLY_VALUE_TO_YGVALUE(start), + @"YGLayout.position.start.unit": APPLY_UNIT_TO_YGVALUE(start, YGUnit), + @"YGLayout.position.end.value": APPLY_VALUE_TO_YGVALUE(end), + @"YGLayout.position.end.unit": APPLY_UNIT_TO_YGVALUE(end, YGUnit), + @"YGLayout.overflow": APPLY_ENUM_TO_YOGA_PROPERTY(overflow, YGOverflow), + @"YGLayout.display": APPLY_ENUM_TO_YOGA_PROPERTY(display, YGDisplay), + @"YGLayout.flex.flexDirection": APPLY_ENUM_TO_YOGA_PROPERTY(flexDirection, YGFlexDirection), + @"YGLayout.flex.flexWrap": APPLY_ENUM_TO_YOGA_PROPERTY(flexWrap, YGWrap), + @"YGLayout.flex.flexGrow": ^(NSNumber *value) { + node.yoga.flexGrow = [value floatValue]; + }, + @"YGLayout.flex.flexShrink": ^(NSNumber *value) { + node.yoga.flexShrink = [value floatValue]; + }, + @"YGLayout.flex.flexBasis.value": APPLY_VALUE_TO_YGVALUE(flexBasis), + @"YGLayout.flex.flexBasis.unit": APPLY_UNIT_TO_YGVALUE(flexBasis, YGUnit), + @"YGLayout.margin.left.value": APPLY_VALUE_TO_YGVALUE(marginLeft), + @"YGLayout.margin.left.unit": APPLY_UNIT_TO_YGVALUE(marginLeft, YGUnit), + @"YGLayout.margin.top.value": APPLY_VALUE_TO_YGVALUE(marginTop), + @"YGLayout.margin.top.unit": APPLY_UNIT_TO_YGVALUE(marginTop, YGUnit), + @"YGLayout.margin.right.value": APPLY_VALUE_TO_YGVALUE(marginRight), + @"YGLayout.margin.right.unit": APPLY_UNIT_TO_YGVALUE(marginRight, YGUnit), + @"YGLayout.margin.bottom.value": APPLY_VALUE_TO_YGVALUE(marginBottom), + @"YGLayout.margin.bottom.unit": APPLY_UNIT_TO_YGVALUE(marginBottom, YGUnit), + @"YGLayout.margin.start.value": APPLY_VALUE_TO_YGVALUE(marginStart), + @"YGLayout.margin.start.unit": APPLY_UNIT_TO_YGVALUE(marginStart, YGUnit), + @"YGLayout.margin.end.value": APPLY_VALUE_TO_YGVALUE(marginEnd), + @"YGLayout.margin.end.unit": APPLY_UNIT_TO_YGVALUE(marginEnd, YGUnit), + @"YGLayout.margin.horizontal.value": APPLY_VALUE_TO_YGVALUE(marginHorizontal), + @"YGLayout.margin.horizontal.unit": APPLY_UNIT_TO_YGVALUE(marginHorizontal, YGUnit), + @"YGLayout.margin.vertical.value": APPLY_VALUE_TO_YGVALUE(marginVertical), + @"YGLayout.margin.vertical.unit": APPLY_UNIT_TO_YGVALUE(marginVertical, YGUnit), + @"YGLayout.margin.all.value": APPLY_VALUE_TO_YGVALUE(margin), + @"YGLayout.margin.all.unit": APPLY_UNIT_TO_YGVALUE(margin, YGUnit), + @"YGLayout.padding.left.value": APPLY_VALUE_TO_YGVALUE(paddingLeft), + @"YGLayout.padding.left.unit": APPLY_UNIT_TO_YGVALUE(paddingLeft, YGUnit), + @"YGLayout.padding.top.value": APPLY_VALUE_TO_YGVALUE(paddingTop), + @"YGLayout.padding.top.unit": APPLY_UNIT_TO_YGVALUE(paddingTop, YGUnit), + @"YGLayout.padding.right.value": APPLY_VALUE_TO_YGVALUE(paddingRight), + @"YGLayout.padding.right.unit": APPLY_UNIT_TO_YGVALUE(paddingRight, YGUnit), + @"YGLayout.padding.bottom.value": APPLY_VALUE_TO_YGVALUE(paddingBottom), + @"YGLayout.padding.bottom.unit": APPLY_UNIT_TO_YGVALUE(paddingBottom, YGUnit), + @"YGLayout.padding.start.value": APPLY_VALUE_TO_YGVALUE(paddingStart), + @"YGLayout.padding.start.unit": APPLY_UNIT_TO_YGVALUE(paddingStart, YGUnit), + @"YGLayout.padding.end.value": APPLY_VALUE_TO_YGVALUE(paddingEnd), + @"YGLayout.padding.end.unit": APPLY_UNIT_TO_YGVALUE(paddingEnd, YGUnit), + @"YGLayout.padding.horizontal.value": APPLY_VALUE_TO_YGVALUE(paddingHorizontal), + @"YGLayout.padding.horizontal.unit": APPLY_UNIT_TO_YGVALUE(paddingHorizontal, YGUnit), + @"YGLayout.padding.vertical.value": APPLY_VALUE_TO_YGVALUE(paddingVertical), + @"YGLayout.padding.vertical.unit": APPLY_UNIT_TO_YGVALUE(paddingVertical, YGUnit), + @"YGLayout.padding.all.value": APPLY_VALUE_TO_YGVALUE(padding), + @"YGLayout.padding.all.unit": APPLY_UNIT_TO_YGVALUE(padding, YGUnit), + @"YGLayout.border.leftWidth": ^(NSNumber *value) { + node.yoga.borderLeftWidth = [value floatValue]; + }, + @"YGLayout.border.topWidth": ^(NSNumber *value) { + node.yoga.borderTopWidth = [value floatValue]; + }, + @"YGLayout.border.rightWidth": ^(NSNumber *value) { + node.yoga.borderRightWidth = [value floatValue]; + }, + @"YGLayout.border.bottomWidth": ^(NSNumber *value) { + node.yoga.borderBottomWidth = [value floatValue]; + }, + @"YGLayout.border.startWidth": ^(NSNumber *value) { + node.yoga.borderStartWidth = [value floatValue]; + }, + @"YGLayout.border.endWidth": ^(NSNumber *value) { + node.yoga.borderEndWidth = [value floatValue]; + }, + @"YGLayout.border.all": ^(NSNumber *value) { + node.yoga.borderWidth = [value floatValue]; + }, + @"YGLayout.dimensions.width.value": APPLY_VALUE_TO_YGVALUE(width), + @"YGLayout.dimensions.width.unit": APPLY_UNIT_TO_YGVALUE(width, YGUnit), + @"YGLayout.dimensions.height.value": APPLY_VALUE_TO_YGVALUE(height), + @"YGLayout.dimensions.height.unit": APPLY_UNIT_TO_YGVALUE(height, YGUnit), + @"YGLayout.dimensions.minWidth.value": APPLY_VALUE_TO_YGVALUE(minWidth), + @"YGLayout.dimensions.minWidth.unit": APPLY_UNIT_TO_YGVALUE(minWidth, YGUnit), + @"YGLayout.dimensions.minHeight.value": APPLY_VALUE_TO_YGVALUE(minHeight), + @"YGLayout.dimensions.minHeight.unit": APPLY_UNIT_TO_YGVALUE(minHeight, YGUnit), + @"YGLayout.dimensions.maxWidth.value": APPLY_VALUE_TO_YGVALUE(maxWidth), + @"YGLayout.dimensions.maxWidth.unit": APPLY_UNIT_TO_YGVALUE(maxWidth, YGUnit), + @"YGLayout.dimensions.maxHeight.value": APPLY_VALUE_TO_YGVALUE(maxHeight), + @"YGLayout.dimensions.maxHeight.unit": APPLY_UNIT_TO_YGVALUE(maxHeight, YGUnit), + @"YGLayout.aspectRatio": ^(NSNumber *value) { + node.yoga.aspectRatio = [value floatValue]; + }, + // Accessibility + @"Accessibility.isAccessibilityElement": ^(NSNumber *value) { + node.isAccessibilityElement = [value boolValue]; + }, + @"Accessibility.accessibilityLabel": ^(NSString *value) { + node.accessibilityLabel = value; + }, + @"Accessibility.accessibilityValue": ^(NSString *value) { + node.accessibilityValue = value; + }, + @"Accessibility.accessibilityHint": ^(NSString *value) { + node.accessibilityHint = value; + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitButton": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitButton, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitLink": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitLink, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitHeader": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitHeader, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitSearchField": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitSearchField, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitImage": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitImage, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitSelected": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitSelected, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitPlaysSound": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitPlaysSound, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitKeyboardKey": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitKeyboardKey, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitStaticText": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitStaticText, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitSummaryElement": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitSummaryElement, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitNotEnabled": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitNotEnabled, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitUpdatesFrequently": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitUpdatesFrequently, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitStartsMediaSession": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitStartsMediaSession, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitAdjustable": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitAdjustable, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitAllowsDirectInteraction": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitAllowsDirectInteraction, [value boolValue]); + }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitCausesPageTurn": ^(NSNumber *value) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitCausesPageTurn, [value boolValue]); + }, + @"Accessibility.accessibilityViewIsModal": ^(NSNumber *value) { + node.accessibilityViewIsModal = [value boolValue]; + }, + @"Accessibility.shouldGroupAccessibilityChildren": ^(NSNumber *value) { + node.shouldGroupAccessibilityChildren = [value boolValue]; + }, +} +; +if (@available(iOS 10.0, *)) { + NSMutableDictionary<NSString*, SKNodeUpdateData>* latestDataMutations = + [dataMutations mutableCopy]; + latestDataMutations + [@"Accessibility.accessibilityTraits.UIAccessibilityTraitTabBar"] = + ^(NSNumber* value) { + node.accessibilityTraits = AccessibilityTraitsToggle( + node.accessibilityTraits, + UIAccessibilityTraitTabBar, + [value boolValue]); + }; + dataMutations = latestDataMutations; +} +return dataMutations; +} + +- (NSArray<SKNamed<NSString*>*>*)attributesForNode:(UIView*)node { + return @[ [SKNamed newWithName:@"addr" + withValue:[NSString stringWithFormat:@"%p", node]] ]; +} + +- (void)setHighlighted:(BOOL)highlighted forNode:(UIView*)node { + SKHighlightOverlay* overlay = [SKHighlightOverlay sharedInstance]; + if (highlighted == YES) { + [overlay mountInView:node withFrame:node.bounds]; + } else { + [overlay unmount]; + } +} + +- (void)hitTest:(SKTouch*)touch forNode:(UIView*)node { + for (NSInteger index = [self childCountForNode:node] - 1; index >= 0; + index--) { + id<NSObject> childNode = [self childForNode:node atIndex:index]; + UIView* viewForNode = nil; + + if ([childNode isKindOfClass:[UIViewController class]]) { + UIViewController* child = (UIViewController*)childNode; + viewForNode = child.view; + } else { + viewForNode = (UIView*)childNode; + } + + if (viewForNode.isHidden || viewForNode.alpha <= 0 || + [[viewForNode class] isEqual:[SKHiddenWindow class]]) { + /*SKHiddenWindow is the pink overlay which is added in window to capture + the gestures.*/ + continue; + } + + if ([touch containedIn:viewForNode.frame]) { + [touch continueWithChildIndex:index withOffset:viewForNode.frame.origin]; + return; + } + } + + [touch finish]; +} + +static void initEnumDictionaries() { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + YGDirectionEnumMap = @{ + @(YGDirectionInherit) : @"inherit", + @(YGDirectionLTR) : @"LTR", + @(YGDirectionRTL) : @"RTL", + }; + + YGFlexDirectionEnumMap = @{ + @(YGFlexDirectionColumn) : @"column", + @(YGFlexDirectionColumnReverse) : @"column-reverse", + @(YGFlexDirectionRow) : @"row", + @(YGFlexDirectionRowReverse) : @"row-reverse", + }; + + YGJustifyEnumMap = @{ + @(YGJustifyFlexStart) : @"flex-start", + @(YGJustifyCenter) : @"center", + @(YGJustifyFlexEnd) : @"flex-end", + @(YGJustifySpaceBetween) : @"space-between", + @(YGJustifySpaceAround) : @"space-around", + }; + + YGAlignEnumMap = @{ + @(YGAlignAuto) : @"auto", + @(YGAlignFlexStart) : @"flex-start", + @(YGAlignCenter) : @"end", + @(YGAlignFlexEnd) : @"flex-end", + @(YGAlignStretch) : @"stretch", + @(YGAlignBaseline) : @"baseline", + @(YGAlignSpaceBetween) : @"space-between", + @(YGAlignSpaceAround) : @"space-around", + }; + + YGPositionTypeEnumMap = @{ + @(YGPositionTypeRelative) : @"relative", + @(YGPositionTypeAbsolute) : @"absolute", + }; + + YGWrapEnumMap = @{ + @(YGWrapNoWrap) : @"no-wrap", + @(YGWrapWrap) : @"wrap", + @(YGWrapWrapReverse) : @"wrap-reverse", + }; + + YGOverflowEnumMap = @{ + @(YGOverflowVisible) : @"visible", + @(YGOverflowHidden) : @"hidden", + @(YGOverflowScroll) : @"scroll", + }; + + YGDisplayEnumMap = @{ + @(YGDisplayFlex) : @"flex", + @(YGDisplayNone) : @"none", + }; + + YGUnitEnumMap = @{ + @(YGUnitUndefined) : @"undefined", + @(YGUnitPoint) : @"point", + @(YGUnitPercent) : @"percent", + @(YGUnitAuto) : @"auto", + }; + }); +} + +static NSDictionary* SKYGValueObject(YGValue value) { + return @{ + @"value" : SKMutableObject(@(value.value)), + @"unit" : SKMutableObject(YGUnitEnumMap[@(value.unit)]), + }; +} + +/* + Takes the originalTraits, and set all bits from toggleTraits to the toggleValue + e.g. originalTraits = UIAccessibilityTraitButton | UIAccessibilityTraitSelected + toggleTraits = UIAccessibilityTraitImage + toggleValue = YES + return value = UIAccessibilityTraitButton | UIAccessibilityTraitSelected | + UIAccessibilityTraitImage + */ +static UIAccessibilityTraits AccessibilityTraitsToggle( + UIAccessibilityTraits originalTraits, + UIAccessibilityTraits toggleTraits, + BOOL toggleValue) { + // NEGATE all bits of toggleTraits from originalTraits and OR it against + // either toggleTraits or 0 (UIAccessibilityTraitNone) based on toggleValue + UIAccessibilityTraits bitsValue = + toggleValue ? toggleTraits : UIAccessibilityTraitNone; + return (originalTraits & ~(toggleTraits)) | bitsValue; +} + +static NSDictionary* AccessibilityTraitsDict( + UIAccessibilityTraits accessibilityTraits) { + NSMutableDictionary* traitsDict = [NSMutableDictionary new]; + [traitsDict addEntriesFromDictionary:@{ + @"UIAccessibilityTraitButton" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitButton))), + @"UIAccessibilityTraitLink" : + SKMutableObject(@(!!(accessibilityTraits & UIAccessibilityTraitLink))), + @"UIAccessibilityTraitHeader" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitHeader))), + @"UIAccessibilityTraitSearchField" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitSearchField))), + @"UIAccessibilityTraitImage" : + SKMutableObject(@(!!(accessibilityTraits & UIAccessibilityTraitImage))), + @"UIAccessibilityTraitSelected" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitSelected))), + @"UIAccessibilityTraitPlaysSound" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitPlaysSound))), + @"UIAccessibilityTraitKeyboardKey" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitKeyboardKey))), + @"UIAccessibilityTraitStaticText" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitStaticText))), + @"UIAccessibilityTraitSummaryElement" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitSummaryElement))), + @"UIAccessibilityTraitNotEnabled" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitNotEnabled))), + @"UIAccessibilityTraitUpdatesFrequently" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitUpdatesFrequently))), + @"UIAccessibilityTraitStartsMediaSession" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitStartsMediaSession))), + @"UIAccessibilityTraitAdjustable" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitAdjustable))), + @"UIAccessibilityTraitAllowsDirectInteraction" : SKMutableObject(@( + !!(accessibilityTraits & UIAccessibilityTraitAllowsDirectInteraction))), + @"UIAccessibilityTraitCausesPageTurn" : SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitCausesPageTurn))), + }]; + if (@available(iOS 10.0, *)) { + traitsDict[@"UIAccessibilityTraitTabBar"] = SKMutableObject( + @(!!(accessibilityTraits & UIAccessibilityTraitTabBar))); + } + return traitsDict; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.h new file mode 100644 index 000000000..877275dc1 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +@interface SKHiddenWindow : UIWindow +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.m new file mode 100644 index 000000000..6664ef7bf --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.m @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKHiddenWindow.h" + +@implementation SKHiddenWindow + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKObjectHash.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKObjectHash.h new file mode 100644 index 000000000..d8224ae28 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKObjectHash.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +/* + A hash function is needed in order to use NSObject classes + as keys in C++ STL + */ +class SKObjectHash { + public: + size_t operator()(const NSObject* x) const { + return (size_t)[x hash]; + } +}; diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.h new file mode 100644 index 000000000..a8d680182 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +void swizzleMethods(Class cls, SEL original, SEL swissled); diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.mm new file mode 100644 index 000000000..096bedc82 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.mm @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKSwizzle.h" + +#import <Foundation/Foundation.h> +#import <objc/runtime.h> + +void swizzleMethods(Class cls, SEL original, SEL swissled) { + Method originalMethod = class_getInstanceMethod(cls, original); + Method swissledMethod = class_getInstanceMethod(cls, swissled); + + BOOL didAddMethod = class_addMethod( + cls, + original, + method_getImplementation(swissledMethod), + method_getTypeEncoding(swissledMethod)); + + if (didAddMethod) { + class_replaceMethod( + cls, + swissled, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swissledMethod); + } +} + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKYogaKitHelper.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKYogaKitHelper.h new file mode 100644 index 000000000..0420ce03a --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKYogaKitHelper.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +#define APPLY_ENUM_TO_YOGA_PROPERTY(varName, enumName) \ + ^(NSString * newValue) { \ + NSNumber* varName = \ + [[enumName##EnumMap allKeysForObject:newValue] lastObject]; \ + if (varName == nil) { \ + return; \ + } \ + node.yoga.varName = (enumName)[varName unsignedIntegerValue]; \ + } + +#define APPLY_VALUE_TO_YGVALUE(varName) \ + ^(NSNumber * value) { \ + YGValue newValue = node.yoga.varName; \ + newValue.value = [value floatValue]; \ + node.yoga.varName = newValue; \ + } + +#define APPLY_UNIT_TO_YGVALUE(varName, enumName) \ + ^(NSString * value) { \ + NSNumber* varName = \ + [[enumName##EnumMap allKeysForObject:value] lastObject]; \ + if (varName == nil) { \ + return; \ + } \ + YGValue newValue = node.yoga.varName; \ + newValue.unit = (enumName)[varName unsignedIntegerValue]; \ + node.yoga.varName = newValue; \ + } diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutTextSearchable/FKTextSearchable.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutTextSearchable/FKTextSearchable.h new file mode 100644 index 000000000..817cb709b --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutTextSearchable/FKTextSearchable.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +@protocol FKTextSearchable<NSObject> + +- (NSString*)searchableText; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h new file mode 100644 index 000000000..7a0204789 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED +#import <Foundation/Foundation.h> + +#import <FlipperKit/FlipperPlugin.h> + +#import "SKBufferingPlugin.h" +#import "SKNetworkReporter.h" + +@interface FlipperKitNetworkPlugin + : SKBufferingPlugin<SKNetworkReporterDelegate> + +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter; +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter + queue:(dispatch_queue_t) + queue; // For test purposes + +@property(strong, nonatomic) id<SKNetworkAdapterDelegate> adapter; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm new file mode 100644 index 000000000..ab17894db --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm @@ -0,0 +1,122 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED +#import "FlipperKitNetworkPlugin.h" +#import <iostream> +#import <memory> +#import <vector> +#import "SKBufferingPlugin+CPPInitialization.h" +#import "SKDispatchQueue.h" +#import "SKNetworkReporter.h" +#import "SonarKitNetworkPlugin+CPPInitialization.h" + +@interface FlipperKitNetworkPlugin () + +@end + +@implementation FlipperKitNetworkPlugin + +- (void)setAdapter:(id<SKNetworkAdapterDelegate>)adapter { + _adapter = adapter; + _adapter.delegate = self; +} + +- (instancetype)init { + if (self = [super initWithQueue:dispatch_queue_create( + "com.sonarkit.network.buffer", + DISPATCH_QUEUE_SERIAL)]) { + } + return self; +} + +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter { + if (self = [super initWithQueue:dispatch_queue_create( + "com.sonarkit.network.buffer", + DISPATCH_QUEUE_SERIAL)]) { + adapter.delegate = self; + _adapter = adapter; + } + return self; +} + +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter + queue:(dispatch_queue_t)queue; +{ + if (self = [super initWithQueue:queue]) { + adapter.delegate = self; + _adapter = adapter; + } + return self; +} + +#pragma mark - SKNetworkReporterDelegate + +- (void)didObserveRequest:(SKRequestInfo*)request { + NSMutableArray<NSDictionary<NSString*, id>*>* headers = [NSMutableArray new]; + for (NSString* key in [request.request.allHTTPHeaderFields allKeys]) { + NSDictionary<NSString*, id>* header = + @{@"key" : key, @"value" : request.request.allHTTPHeaderFields[key]}; + [headers addObject:header]; + } + + NSString* body = request.body; + + [self send:@"newRequest" + sonarObject:@{ + @"id" : @(request.identifier), + @"timestamp" : @(request.timestamp), + @"method" : request.request.HTTPMethod ?: [NSNull null], + @"url" : [request.request.URL absoluteString] ?: [NSNull null], + @"headers" : headers, + @"data" : body ? body : [NSNull null] + }]; +} + +- (void)didObserveResponse:(SKResponseInfo*)response { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response.response; + + NSMutableArray<NSDictionary<NSString*, id>*>* headers = [NSMutableArray new]; + for (NSString* key in [httpResponse.allHeaderFields allKeys]) { + NSDictionary<NSString*, id>* header = + @{@"key" : key, @"value" : httpResponse.allHeaderFields[key]}; + [headers addObject:header]; + } + + NSString* body = response.body; + + [self send:@"newResponse" + sonarObject:@{ + @"id" : @(response.identifier), + @"timestamp" : @(response.timestamp), + @"status" : @(httpResponse.statusCode), + @"reason" : [NSHTTPURLResponse + localizedStringForStatusCode:httpResponse.statusCode] + ?: [NSNull null], + @"headers" : headers, + @"data" : body ? body : [NSNull null] + }]; +} + +@end + +@implementation FlipperKitNetworkPlugin (CPPInitialization) + +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter + dispatchQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>) + queue { + if (self = [super initWithDispatchQueue:queue]) { + adapter.delegate = self; + _adapter = adapter; + } + return self; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h new file mode 100644 index 000000000..5d5658e9f --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED +#pragma once + +#import <iostream> +#import <memory> +#import "SKBufferingPlugin.h" +#import "SKDispatchQueue.h" + +struct CachedEvent { + NSString* method; + NSDictionary<NSString*, id>* sonarObject; +}; + +@interface SKBufferingPlugin (CPPInitialization) + +- (instancetype)initWithVectorEventSize:(NSUInteger)size + connectionAccessQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>) + connectionAccessQueue; +- (instancetype)initWithDispatchQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>)queue; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h new file mode 100644 index 000000000..4747973de --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <Foundation/Foundation.h> + +#import <FlipperKit/FlipperPlugin.h> + +@interface SKBufferingPlugin : NSObject<FlipperPlugin> + +- (instancetype)initWithQueue:(dispatch_queue_t)queue; + +- (void)send:(NSString*)method + sonarObject:(NSDictionary<NSString*, id>*)sonarObject; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.mm new file mode 100644 index 000000000..ec7339d1c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.mm @@ -0,0 +1,111 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <vector> + +#import <FlipperKit/FlipperConnection.h> +#import "SKBufferingPlugin+CPPInitialization.h" +#import "SKBufferingPlugin.h" +#import "SKDispatchQueue.h" + +static const NSUInteger bufferSize = 500; + +@interface SKBufferingPlugin () + +@property(assign, nonatomic) std::vector<CachedEvent> ringBuffer; +@property(assign, nonatomic) std::shared_ptr<facebook::flipper::DispatchQueue> + connectionAccessQueue; +@property(strong, nonatomic) id<FlipperConnection> connection; + +@end + +@implementation SKBufferingPlugin +// { +// std::vector<CachedEvent> _ringBuffer; +// std::shared_ptr<facebook::flipper::DispatchQueue> _connectionAccessQueue; +// +// id<FlipperConnection> _connection; +// } + +- (instancetype)initWithQueue:(dispatch_queue_t)queue { + if (self = [super init]) { + _ringBuffer.reserve(bufferSize); + _connectionAccessQueue = + std::make_shared<facebook::flipper::GCDQueue>(queue); + } + return self; +} + +- (NSString*)identifier { + // Note: This must match with the javascript pulgin identifier!! + return @"Network"; +} + +- (void)didConnect:(id<FlipperConnection>)connection { + _connectionAccessQueue->async(^{ + self->_connection = connection; + [self sendBufferedEvents]; + }); +} + +- (void)didDisconnect { + _connectionAccessQueue->async(^{ + self->_connection = nil; + }); +} + +- (BOOL)runInBackground { + return YES; +} + +- (void)send:(NSString*)method + sonarObject:(NSDictionary<NSString*, id>*)sonarObject { + _connectionAccessQueue->async(^{ + if (self->_connection) { + [self->_connection send:method withParams:sonarObject]; + } else { + if (self->_ringBuffer.size() == bufferSize) { + return; + } + self->_ringBuffer.push_back( + {.method = method, .sonarObject = sonarObject}); + } + }); +} + +- (void)sendBufferedEvents { + NSAssert(_connection, @"connection object cannot be nil"); + for (const auto& event : _ringBuffer) { + [_connection send:event.method withParams:event.sonarObject]; + } + _ringBuffer.clear(); +} + +@end + +@implementation SKBufferingPlugin (CPPInitialization) + +- (instancetype)initWithVectorEventSize:(NSUInteger)size + connectionAccessQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>) + connectionAccessQueue { + if (self = [super init]) { + _ringBuffer.reserve(size); + _connectionAccessQueue = connectionAccessQueue; + } + return self; +} +- (instancetype)initWithDispatchQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>)queue { + return [self initWithVectorEventSize:bufferSize connectionAccessQueue:queue]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKDispatchQueue.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKDispatchQueue.h new file mode 100644 index 000000000..7f62cb774 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKDispatchQueue.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#pragma once + +#import <dispatch/dispatch.h> + +namespace facebook { +namespace flipper { +class DispatchQueue { + public: + virtual void async(dispatch_block_t block) = 0; + virtual ~DispatchQueue() {} +}; + +class GCDQueue : public DispatchQueue { + public: + GCDQueue(dispatch_queue_t underlyingQueue) + : _underlyingQueue(underlyingQueue) {} + + void async(dispatch_block_t block) override { + dispatch_async(_underlyingQueue, block); + } + + virtual ~GCDQueue() {} + + private: + dispatch_queue_t _underlyingQueue; +}; +} // namespace flipper +} // namespace facebook + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h new file mode 100644 index 000000000..3e4726e20 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> +#import "SKRequestInfo.h" +#import "SKResponseInfo.h" + +@protocol SKNetworkReporterDelegate + +- (void)didObserveRequest:(SKRequestInfo*)request; +- (void)didObserveResponse:(SKResponseInfo*)response; + +@end + +@protocol SKNetworkAdapterDelegate + +@property(weak, nonatomic) id<SKNetworkReporterDelegate> delegate; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h new file mode 100644 index 000000000..c7d4f4186 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@interface SKRequestInfo : NSObject +@property(assign, readwrite) int64_t identifier; +@property(assign, readwrite) uint64_t timestamp; +@property(strong, nonatomic) NSURLRequest* _Nullable request; +@property(strong, nonatomic) NSString* _Nullable body; + +- (instancetype _Nonnull)initWithIdentifier:(int64_t)identifier + timestamp:(uint64_t)timestamp + request:(NSURLRequest* _Nullable)request + data:(NSData* _Nullable)data; +- (void)setBodyFromData:(NSData* _Nullable)data; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.m new file mode 100644 index 000000000..06c7f4882 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.m @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "SKRequestInfo.h" + +@implementation SKRequestInfo +@synthesize identifier = _identifier; +@synthesize timestamp = _timestamp; +@synthesize request = _request; +@synthesize body = _body; + +- (instancetype)initWithIdentifier:(int64_t)identifier + timestamp:(uint64_t)timestamp + request:(NSURLRequest*)request + data:(NSData*)data { + if (self = [super init]) { + _identifier = identifier; + _timestamp = timestamp; + _request = request; + _body = data ? [data base64EncodedStringWithOptions:0] + : [request.HTTPBody base64EncodedStringWithOptions:0]; + } + return self; +} + +- (void)setBodyFromData:(NSData* _Nullable)data { + self.body = data ? [data base64EncodedStringWithOptions:0] + : [self.request.HTTPBody base64EncodedStringWithOptions:0]; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h new file mode 100644 index 000000000..c486650b8 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +@interface SKResponseInfo : NSObject + +@property(assign, readwrite) int64_t identifier; +@property(assign, readwrite) uint64_t timestamp; +@property(strong, nonatomic) NSURLResponse* _Nullable response; +@property(strong, nonatomic) NSString* _Nullable body; + +- (instancetype _Nonnull)initWithIndentifier:(int64_t)identifier + timestamp:(uint64_t)timestamp + response:(NSURLResponse* _Nullable)response + data:(NSData* _Nullable)data; +- (void)setBodyFromData:(NSData* _Nullable)data; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m new file mode 100644 index 000000000..1213dd16d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "SKResponseInfo.h" + +@implementation SKResponseInfo +@synthesize identifier = _identifier; +@synthesize timestamp = _timestamp; +@synthesize response = _response; +@synthesize body = _body; + +- (instancetype)initWithIndentifier:(int64_t)identifier + timestamp:(uint64_t)timestamp + response:(NSURLResponse*)response + data:(NSData*)data { + if (self = [super init]) { + _identifier = identifier; + _timestamp = timestamp; + _response = response; + _body = [SKResponseInfo shouldStripReponseBodyWithResponse:response] + ? nil + : [data base64EncodedStringWithOptions:0]; + } + return self; +} + ++ (BOOL)shouldStripReponseBodyWithResponse:(NSURLResponse*)response { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + NSString* contentType = httpResponse.allHeaderFields[@"content-type"]; + if (!contentType) { + return NO; + } + + return [contentType containsString:@"image/"] || + [contentType containsString:@"video/"] || + [contentType containsString:@"application/zip"]; +} + +- (void)setBodyFromData:(NSData* _Nullable)data { + self.body = [SKResponseInfo shouldStripReponseBodyWithResponse:self.response] + ? nil + : [data base64EncodedStringWithOptions:0]; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h new file mode 100644 index 000000000..40367688d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#pragma once +#import <memory> +#import "FlipperKitNetworkPlugin.h" +#import "SKDispatchQueue.h" + +@interface FlipperKitNetworkPlugin (CPPInitialization) +- (instancetype)initWithNetworkAdapter:(id<SKNetworkAdapterDelegate>)adapter + dispatchQueue: + (std::shared_ptr<facebook::flipper::DispatchQueue>) + queue; +@end +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.h new file mode 100755 index 000000000..d141f0042 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// +// FLEXNetworkObserver.h +// Derived from: +// +// PDAFNetworkDomainController.h +// PonyDebugger +// +// Created by Mike Lewis on 2/27/12. +// +// Licensed to Square, Inc. under one or more contributor license agreements. +// See the LICENSE file distributed with this work for the terms under +// which Square, Inc. licenses this file to you. +// + +#import <Foundation/Foundation.h> + +FOUNDATION_EXTERN NSString* const + kFLEXNetworkObserverEnabledStateChangedNotification; + +/// This class swizzles NSURLConnection and NSURLSession delegate methods to +/// observe events in the URL loading system. High level network events are sent +/// to the default FLEXNetworkRecorder instance which maintains the request +/// history and caches response bodies. +@interface FLEXNetworkObserver : NSObject + ++ (void)start; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.mm new file mode 100755 index 000000000..77c6cdc5b --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.mm @@ -0,0 +1,1684 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// +// FLEXNetworkObserver.m +// Derived from: +// +// PDAFNetworkDomainController.m +// PonyDebugger +// +// Created by Mike Lewis on 2/27/12. +// +// Licensed to Square, Inc. under one or more contributor license agreements. +// See the LICENSE file distributed with this work for the terms under +// which Square, Inc. licenses this file to you. +// + +#import "FLEXNetworkObserver.h" + +#import <objc/message.h> +#import <objc/runtime.h> + +#import <dispatch/queue.h> + +#import "FLEXNetworkRecorder.h" +#import "FLEXUtility.h" + +NSString* const kFLEXNetworkObserverEnabledStateChangedNotification = + @"kFLEXNetworkObserverEnabledStateChangedNotification"; +static NSString* const kFLEXNetworkObserverEnabledDefaultsKey = + @"com.flex.FLEXNetworkObserver.enableOnLaunch"; + +typedef void (^NSURLSessionAsyncCompletion)( + id fileURLOrData, + NSURLResponse* response, + NSError* error); + +@interface FLEXInternalRequestState : NSObject + +@property(nonatomic, copy) NSURLRequest* request; +@property(nonatomic, strong) NSMutableData* dataAccumulator; + +@end + +@implementation FLEXInternalRequestState + +@end + +@interface FLEXNetworkObserver (NSURLConnectionHelpers) + +- (void)connection:(NSURLConnection*)connection + willSendRequest:(NSURLRequest*)request + redirectResponse:(NSURLResponse*)response + delegate:(id<NSURLConnectionDelegate>)delegate; +- (void)connection:(NSURLConnection*)connection + didReceiveResponse:(NSURLResponse*)response + delegate:(id<NSURLConnectionDelegate>)delegate; + +- (void)connection:(NSURLConnection*)connection + didReceiveData:(NSData*)data + delegate:(id<NSURLConnectionDelegate>)delegate; + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection + delegate:(id<NSURLConnectionDelegate>)delegate; +- (void)connection:(NSURLConnection*)connection + didFailWithError:(NSError*)error + delegate:(id<NSURLConnectionDelegate>)delegate; + +- (void)connectionWillCancel:(NSURLConnection*)connection; + +@end + +@interface FLEXNetworkObserver (NSURLSessionTaskHelpers) + +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionTask*)task + willPerformHTTPRedirection:(NSHTTPURLResponse*)response + newRequest:(NSURLRequest*)request + completionHandler:(void (^)(NSURLRequest*))completionHandler + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didReceiveResponse:(NSURLResponse*)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition)) + completionHandler + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didReceiveData:(NSData*)data + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didBecomeDownloadTask:(NSURLSessionDownloadTask*)downloadTask + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionTask*)task + didCompleteWithError:(NSError*)error + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + downloadTask:(NSURLSessionDownloadTask*)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten + totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite + delegate:(id<NSURLSessionDelegate>)delegate; +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionDownloadTask*)downloadTask + didFinishDownloadingToURL:(NSURL*)location + data:(NSData*)data + delegate:(id<NSURLSessionDelegate>)delegate; + +- (void)URLSessionTaskWillResume:(NSURLSessionTask*)task; + +@end + +@interface FLEXNetworkObserver () + +@property(nonatomic, strong) + NSMutableDictionary<NSString*, FLEXInternalRequestState*>* + requestStatesForRequestIDs; +@property(nonatomic, strong) dispatch_queue_t queue; + +@end + +@implementation FLEXNetworkObserver + +#pragma mark - Public Methods + ++ (void)start { + [self injectIntoAllNSURLConnectionDelegateClasses]; +} + +#pragma mark - Statics + ++ (instancetype)sharedObserver { + static FLEXNetworkObserver* sharedObserver = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedObserver = [[[self class] alloc] init]; + }); + return sharedObserver; +} + ++ (NSString*)nextRequestID { + return [[NSUUID UUID] UUIDString]; +} + +#pragma mark Delegate Injection Convenience Methods + +/// All swizzled delegate methods should make use of this guard. +/// This will prevent duplicated sniffing when the original implementation calls +/// up to a superclass implementation which we've also swizzled. The superclass +/// implementation (and implementations in classes above that) will be executed +/// without inteference if called from the original implementation. ++ (void)sniffWithoutDuplicationForObject:(NSObject*)object + selector:(SEL)selector + sniffingBlock:(void (^)(void))sniffingBlock + originalImplementationBlock: + (void (^)(void))originalImplementationBlock { + // If we don't have an object to detect nested calls on, just run the original + // implmentation and bail. This case can happen if someone besides the URL + // loading system calls the delegate methods directly. See + // https://github.com/Flipboard/FLEX/issues/61 for an example. + if (!object) { + originalImplementationBlock(); + return; + } + + const void* key = selector; + + // Don't run the sniffing block if we're inside a nested call + if (!objc_getAssociatedObject(object, key)) { + sniffingBlock(); + } + + // Mark that we're calling through to the original so we can detect nested + // calls + objc_setAssociatedObject( + object, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + originalImplementationBlock(); + objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - Delegate Injection + ++ (void)injectIntoAllNSURLConnectionDelegateClasses { + // Only allow swizzling once. + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Swizzle any classes that implement one of these selectors. + const SEL selectors[] = { + @selector(connectionDidFinishLoading:), + @selector(connection:willSendRequest:redirectResponse:), + @selector(connection:didReceiveResponse:), + @selector(connection:didReceiveData:), + @selector(connection:didFailWithError:), + @selector + (URLSession: + task:willPerformHTTPRedirection:newRequest:completionHandler:), + @selector(URLSession:dataTask:didReceiveData:), + @selector(URLSession:dataTask:didReceiveResponse:completionHandler:), + @selector(URLSession:task:didCompleteWithError:), + @selector(URLSession:dataTask:didBecomeDownloadTask:), + @selector(URLSession: + downloadTask:didWriteData:totalBytesWritten + :totalBytesExpectedToWrite:), + @selector(URLSession:downloadTask:didFinishDownloadingToURL:) + }; + + const int numSelectors = sizeof(selectors) / sizeof(SEL); + + Class* classes = NULL; + int numClasses = objc_getClassList(NULL, 0); + + if (numClasses > 0) { + classes = (__unsafe_unretained Class*)malloc(sizeof(Class) * numClasses); + numClasses = objc_getClassList(classes, numClasses); + for (NSInteger classIndex = 0; classIndex < numClasses; ++classIndex) { + Class className = classes[classIndex]; + + if (className == [FLEXNetworkObserver class]) { + continue; + } + + // Use the runtime API rather than the methods on NSObject to avoid + // sending messages to classes we're not interested in swizzling. + // Otherwise we hit +initialize on all classes. NOTE: calling + // class_getInstanceMethod() DOES send +initialize to the class. That's + // why we iterate through the method list. + unsigned int methodCount = 0; + Method* methods = class_copyMethodList(className, &methodCount); + BOOL matchingSelectorFound = NO; + for (unsigned int methodIndex = 0; methodIndex < methodCount; + methodIndex++) { + for (int selectorIndex = 0; selectorIndex < numSelectors; + ++selectorIndex) { + if (method_getName(methods[methodIndex]) == + selectors[selectorIndex]) { + [self injectIntoDelegateClass:className]; + matchingSelectorFound = YES; + break; + } + } + if (matchingSelectorFound) { + break; + } + } + free(methods); + } + + free(classes); + } + + [self injectIntoNSURLConnectionCancel]; + [self injectIntoNSURLSessionTaskResume]; + + [self injectIntoNSURLConnectionAsynchronousClassMethod]; + [self injectIntoNSURLConnectionSynchronousClassMethod]; + + [self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods]; + [self injectIntoNSURLSessionAsyncUploadTaskMethods]; + }); +} + ++ (void)injectIntoDelegateClass:(Class)cls { + // Connections + [self injectWillSendRequestIntoDelegateClass:cls]; + [self injectDidReceiveDataIntoDelegateClass:cls]; + [self injectDidReceiveResponseIntoDelegateClass:cls]; + [self injectDidFinishLoadingIntoDelegateClass:cls]; + [self injectDidFailWithErrorIntoDelegateClass:cls]; + + // Sessions + [self injectTaskWillPerformHTTPRedirectionIntoDelegateClass:cls]; + [self injectTaskDidReceiveDataIntoDelegateClass:cls]; + [self injectTaskDidReceiveResponseIntoDelegateClass:cls]; + [self injectTaskDidCompleteWithErrorIntoDelegateClass:cls]; + [self injectRespondsToSelectorIntoDelegateClass:cls]; + + // Data tasks + [self injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:cls]; + + // Download tasks + [self injectDownloadTaskDidWriteDataIntoDelegateClass:cls]; + [self injectDownloadTaskDidFinishDownloadingIntoDelegateClass:cls]; +} + ++ (void)injectIntoNSURLConnectionCancel { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class className = [NSURLConnection class]; + SEL selector = @selector(cancel); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + Method originalCancel = class_getInstanceMethod(className, selector); + + void (^swizzleBlock)(NSURLConnection*) = ^(NSURLConnection* slf) { + [[FLEXNetworkObserver sharedObserver] connectionWillCancel:slf]; + ((void (*)(id, SEL))objc_msgSend)(slf, swizzledSelector); + }; + + IMP implementation = imp_implementationWithBlock(swizzleBlock); + class_addMethod( + className, + swizzledSelector, + implementation, + method_getTypeEncoding(originalCancel)); + Method newCancel = class_getInstanceMethod(className, swizzledSelector); + method_exchangeImplementations(originalCancel, newCancel); + }); +} + ++ (void)injectIntoNSURLSessionTaskResume { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // In iOS 7 resume lives in __NSCFLocalSessionTask + // In iOS 8 resume lives in NSURLSessionTask + // In iOS 9 resume lives in __NSCFURLSessionTask + Class className = Nil; + if (![[NSProcessInfo processInfo] + respondsToSelector:@selector(operatingSystemVersion)]) { + className = + NSClassFromString([@[ @"__", @"NSC", @"FLocalS", @"ession", @"Task" ] + componentsJoinedByString:@""]); + } else if ( + [[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 9) { + className = [NSURLSessionTask class]; + } else { + className = + NSClassFromString([@[ @"__", @"NSC", @"FURLS", @"ession", @"Task" ] + componentsJoinedByString:@""]); + } + SEL selector = @selector(resume); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Method originalResume = class_getInstanceMethod(className, selector); + + void (^swizzleBlock)(NSURLSessionTask*) = ^(NSURLSessionTask* slf) { + // iOS's internal HTTP parser finalization code is mysteriously not thread + // safe, invoke it asynchronously has a chance to cause a `double free` + // crash. This line below will ask for HTTPBody synchronously, make the + // HTTPParser parse the request and cache them in advance, After that the + // HTTPParser will be finalized, make sure other threads inspecting the + // request won't trigger a race to finalize the parser. + [slf.currentRequest HTTPBody]; + + [[FLEXNetworkObserver sharedObserver] URLSessionTaskWillResume:slf]; + ((void (*)(id, SEL))objc_msgSend)(slf, swizzledSelector); + }; + + IMP implementation = imp_implementationWithBlock(swizzleBlock); + class_addMethod( + className, + swizzledSelector, + implementation, + method_getTypeEncoding(originalResume)); + Method newResume = class_getInstanceMethod(className, swizzledSelector); + method_exchangeImplementations(originalResume, newResume); + }); +} + ++ (void)injectIntoNSURLConnectionAsynchronousClassMethod { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class className = objc_getMetaClass(class_getName([NSURLConnection class])); + SEL selector = @selector(sendAsynchronousRequest:queue:completionHandler:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + typedef void (^NSURLConnectionAsyncCompletion)( + NSURLResponse* response, NSData* data, NSError* connectionError); + + void (^asyncSwizzleBlock)( + Class, + NSURLRequest*, + NSOperationQueue*, + NSURLConnectionAsyncCompletion) = + ^(Class slf, + NSURLRequest* request, + NSOperationQueue* queue, + NSURLConnectionAsyncCompletion completion) { + NSString* requestID = [self nextRequestID]; + [[FLEXNetworkRecorder defaultRecorder] + recordRequestWillBeSentWithRequestID:requestID + request:request + redirectResponse:nil]; + NSString* mechanism = [self mechanismFromClassMethod:selector + onClass:className]; + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:mechanism + forRequestID:requestID]; + NSURLConnectionAsyncCompletion completionWrapper = ^( + NSURLResponse* response, NSData* data, NSError* connectionError) { + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:response]; + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:[data length]]; + if (connectionError) { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFailedWithRequestID:requestID + error:connectionError]; + } else { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFinishedWithRequestID:requestID + responseBody:data]; + } + + // Call through to the original completion handler + if (completion) { + completion(response, data, connectionError); + } + }; + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, request, queue, completionWrapper); + }; + + [FLEXUtility replaceImplementationOfKnownSelector:selector + onClass:className + withBlock:asyncSwizzleBlock + swizzledSelector:swizzledSelector]; + }); +} + ++ (void)injectIntoNSURLConnectionSynchronousClassMethod { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class className = objc_getMetaClass(class_getName([NSURLConnection class])); + SEL selector = @selector(sendSynchronousRequest:returningResponse:error:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + NSData* (^syncSwizzleBlock)( + Class, NSURLRequest*, NSURLResponse**, NSError**) = + ^NSData*( + Class slf, + NSURLRequest* request, + NSURLResponse** response, + NSError** error) { + NSData* data = nil; + NSString* requestID = [self nextRequestID]; + [[FLEXNetworkRecorder defaultRecorder] + recordRequestWillBeSentWithRequestID:requestID + request:request + redirectResponse:nil]; + NSString* mechanism = [self mechanismFromClassMethod:selector + onClass:className]; + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:mechanism + forRequestID:requestID]; + NSError* temporaryError = nil; + NSURLResponse* temporaryResponse = nil; + data = ((id(*)(id, SEL, id, NSURLResponse**, NSError**))objc_msgSend)( + slf, swizzledSelector, request, &temporaryResponse, &temporaryError); + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:temporaryResponse]; + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:[data length]]; + if (temporaryError) { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFailedWithRequestID:requestID + error:temporaryError]; + } else { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFinishedWithRequestID:requestID + responseBody:data]; + } + if (error) { + *error = temporaryError; + } + if (response) { + *response = temporaryResponse; + } + return data; + }; + + [FLEXUtility replaceImplementationOfKnownSelector:selector + onClass:className + withBlock:syncSwizzleBlock + swizzledSelector:swizzledSelector]; + }); +} + ++ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class className = [NSURLSession class]; + + // The method signatures here are close enough that we can use the same + // logic to inject into all of them. + const SEL selectors[] = { + @selector(dataTaskWithRequest:completionHandler:), + @selector(dataTaskWithURL:completionHandler:), + @selector(downloadTaskWithRequest:completionHandler:), + @selector(downloadTaskWithResumeData:completionHandler:), + @selector(downloadTaskWithURL:completionHandler:) + }; + + const int numSelectors = sizeof(selectors) / sizeof(SEL); + + for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) { + SEL selector = selectors[selectorIndex]; + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector + class:className]) { + // iOS 7 does not implement these methods on NSURLSession. We actually + // want to swizzle __NSCFURLSession, which we can get from the class of + // the shared session + className = [[NSURLSession sharedSession] class]; + } + + NSURLSessionTask* (^asyncDataOrDownloadSwizzleBlock)( + Class, id, NSURLSessionAsyncCompletion) = + ^NSURLSessionTask*( + Class slf, id argument, NSURLSessionAsyncCompletion completion) { + NSURLSessionTask* task = nil; + // If completion block was not provided sender expect to receive + // delegated methods or does not interested in callback at all. In this + // case we should just call original method implementation with nil + // completion block. + if (completion) { + NSString* requestID = [self nextRequestID]; + NSString* mechanism = [self mechanismFromClassMethod:selector + onClass:className]; + NSURLSessionAsyncCompletion completionWrapper = + [self asyncCompletionWrapperForRequestID:requestID + mechanism:mechanism + completion:completion]; + task = ((id(*)(id, SEL, id, id))objc_msgSend)( + slf, swizzledSelector, argument, completionWrapper); + [self setRequestID:requestID forConnectionOrTask:task]; + } else { + task = ((id(*)(id, SEL, id, id))objc_msgSend)( + slf, swizzledSelector, argument, completion); + } + return task; + }; + + [FLEXUtility + replaceImplementationOfKnownSelector:selector + onClass:className + withBlock:asyncDataOrDownloadSwizzleBlock + swizzledSelector:swizzledSelector]; + } + }); +} + ++ (void)injectIntoNSURLSessionAsyncUploadTaskMethods { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class className = [NSURLSession class]; + + // The method signatures here are close enough that we can use the same + // logic to inject into both of them. Note that they have 3 arguments, so we + // can't easily combine with the data and download method above. + const SEL selectors[] = { + @selector(uploadTaskWithRequest:fromData:completionHandler:), + @selector(uploadTaskWithRequest:fromFile:completionHandler:) + }; + + const int numSelectors = sizeof(selectors) / sizeof(SEL); + + for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) { + SEL selector = selectors[selectorIndex]; + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector + class:className]) { + // iOS 7 does not implement these methods on NSURLSession. We actually + // want to swizzle __NSCFURLSession, which we can get from the class of + // the shared session + className = [[NSURLSession sharedSession] class]; + } + + NSURLSessionUploadTask* (^asyncUploadTaskSwizzleBlock)( + Class, NSURLRequest*, id, NSURLSessionAsyncCompletion) = + ^NSURLSessionUploadTask*( + Class slf, + NSURLRequest* request, + id argument, + NSURLSessionAsyncCompletion completion) { + NSURLSessionUploadTask* task = nil; + if (completion) { + NSString* requestID = [self nextRequestID]; + NSString* mechanism = [self mechanismFromClassMethod:selector + onClass:className]; + NSURLSessionAsyncCompletion completionWrapper = + [self asyncCompletionWrapperForRequestID:requestID + mechanism:mechanism + completion:completion]; + task = ((id(*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, request, argument, completionWrapper); + [self setRequestID:requestID forConnectionOrTask:task]; + } else { + task = ((id(*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, request, argument, completion); + } + return task; + }; + + [FLEXUtility + replaceImplementationOfKnownSelector:selector + onClass:className + withBlock:asyncUploadTaskSwizzleBlock + swizzledSelector:swizzledSelector]; + } + }); +} + ++ (NSString*)mechanismFromClassMethod:(SEL)selector onClass:(Class)className { + return [NSString stringWithFormat:@"+[%@ %@]", + NSStringFromClass(className), + NSStringFromSelector(selector)]; +} + ++ (NSURLSessionAsyncCompletion) + asyncCompletionWrapperForRequestID:(NSString*)requestID + mechanism:(NSString*)mechanism + completion:(NSURLSessionAsyncCompletion)completion { + NSURLSessionAsyncCompletion completionWrapper = + ^(id fileURLOrData, NSURLResponse* response, NSError* error) { + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:mechanism + forRequestID:requestID]; + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:response]; + NSData* data = nil; + if ([fileURLOrData isKindOfClass:[NSURL class]]) { + data = [NSData dataWithContentsOfURL:fileURLOrData]; + } else if ([fileURLOrData isKindOfClass:[NSData class]]) { + data = fileURLOrData; + } + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:[data length]]; + if (error) { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFailedWithRequestID:requestID + error:error]; + } else { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFinishedWithRequestID:requestID + responseBody:data]; + } + + // Call through to the original completion handler + if (completion) { + completion(fileURLOrData, response, error); + } + }; + return completionWrapper; +} + ++ (void)injectWillSendRequestIntoDelegateClass:(Class)cls { + SEL selector = @selector(connection:willSendRequest:redirectResponse:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLConnectionDataDelegate); + if (!protocol) { + protocol = @protocol(NSURLConnectionDelegate); + } + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef NSURLRequest* (^NSURLConnectionWillSendRequestBlock)( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLRequest* request, + NSURLResponse* response); + + NSURLConnectionWillSendRequestBlock undefinedBlock = ^NSURLRequest*( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLRequest* request, + NSURLResponse* response) { + [[FLEXNetworkObserver sharedObserver] connection:connection + willSendRequest:request + redirectResponse:response + delegate:slf]; + return request; + }; + + NSURLConnectionWillSendRequestBlock implementationBlock = ^NSURLRequest*( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLRequest* request, + NSURLResponse* response) { + __block NSURLRequest* returnValue = nil; + [self sniffWithoutDuplicationForObject:connection + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, connection, request, response); + } + originalImplementationBlock:^{ + returnValue = ((id(*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, connection, request, response); + }]; + return returnValue; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDidReceiveResponseIntoDelegateClass:(Class)cls { + SEL selector = @selector(connection:didReceiveResponse:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLConnectionDataDelegate); + if (!protocol) { + protocol = @protocol(NSURLConnectionDelegate); + } + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLConnectionDidReceiveResponseBlock)( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLResponse* response); + + NSURLConnectionDidReceiveResponseBlock undefinedBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLResponse* response) { + [[FLEXNetworkObserver sharedObserver] connection:connection + didReceiveResponse:response + delegate:slf]; + }; + + NSURLConnectionDidReceiveResponseBlock implementationBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSURLResponse* response) { + [self sniffWithoutDuplicationForObject:connection + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, connection, response); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id))objc_msgSend)( + slf, swizzledSelector, connection, response); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDidReceiveDataIntoDelegateClass:(Class)cls { + SEL selector = @selector(connection:didReceiveData:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLConnectionDataDelegate); + if (!protocol) { + protocol = @protocol(NSURLConnectionDelegate); + } + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLConnectionDidReceiveDataBlock)( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSData* data); + + NSURLConnectionDidReceiveDataBlock undefinedBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSData* data) { + [[FLEXNetworkObserver sharedObserver] connection:connection + didReceiveData:data + delegate:slf]; + }; + + NSURLConnectionDidReceiveDataBlock implementationBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSData* data) { + [self sniffWithoutDuplicationForObject:connection + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, connection, data); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id))objc_msgSend)( + slf, swizzledSelector, connection, data); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDidFinishLoadingIntoDelegateClass:(Class)cls { + SEL selector = @selector(connectionDidFinishLoading:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLConnectionDataDelegate); + if (!protocol) { + protocol = @protocol(NSURLConnectionDelegate); + } + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLConnectionDidFinishLoadingBlock)( + id<NSURLConnectionDelegate> slf, NSURLConnection* connection); + + NSURLConnectionDidFinishLoadingBlock undefinedBlock = ^( + id<NSURLConnectionDelegate> slf, NSURLConnection* connection) { + [[FLEXNetworkObserver sharedObserver] connectionDidFinishLoading:connection + delegate:slf]; + }; + + NSURLConnectionDidFinishLoadingBlock implementationBlock = + ^(id<NSURLConnectionDelegate> slf, NSURLConnection* connection) { + [self sniffWithoutDuplicationForObject:connection + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, connection); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id))objc_msgSend)( + slf, swizzledSelector, connection); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDidFailWithErrorIntoDelegateClass:(Class)cls { + SEL selector = @selector(connection:didFailWithError:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLConnectionDelegate); + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLConnectionDidFailWithErrorBlock)( + id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSError* error); + + NSURLConnectionDidFailWithErrorBlock undefinedBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSError* error) { + [[FLEXNetworkObserver sharedObserver] connection:connection + didFailWithError:error + delegate:slf]; + }; + + NSURLConnectionDidFailWithErrorBlock implementationBlock = + ^(id<NSURLConnectionDelegate> slf, + NSURLConnection* connection, + NSError* error) { + [self sniffWithoutDuplicationForObject:connection + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, connection, error); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id))objc_msgSend)( + slf, swizzledSelector, connection, error); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectTaskWillPerformHTTPRedirectionIntoDelegateClass:(Class)cls { + SEL selector = @selector + (URLSession: + task:willPerformHTTPRedirection:newRequest:completionHandler:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionTaskDelegate); + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionWillPerformHTTPRedirectionBlock)( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSHTTPURLResponse* response, + NSURLRequest* newRequest, + void (^completionHandler)(NSURLRequest*)); + + NSURLSessionWillPerformHTTPRedirectionBlock undefinedBlock = + ^(id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSHTTPURLResponse* response, + NSURLRequest* newRequest, + void (^completionHandler)(NSURLRequest*)) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + task:task + willPerformHTTPRedirection:response + newRequest:newRequest + completionHandler:completionHandler + delegate:slf]; + completionHandler(newRequest); + }; + + NSURLSessionWillPerformHTTPRedirectionBlock implementationBlock = ^( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSHTTPURLResponse* response, + NSURLRequest* newRequest, + void (^completionHandler)(NSURLRequest*)) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + [[FLEXNetworkObserver sharedObserver] URLSession:session + task:task + willPerformHTTPRedirection:response + newRequest:newRequest + completionHandler:completionHandler + delegate:slf]; + } + originalImplementationBlock:^{ + ((id(*)( + id, SEL, id, id, id, id, void (^)(NSURLRequest*)))objc_msgSend)( + slf, + swizzledSelector, + session, + task, + response, + newRequest, + completionHandler); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectTaskDidReceiveDataIntoDelegateClass:(Class)cls { + SEL selector = @selector(URLSession:dataTask:didReceiveData:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionDataDelegate); + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionDidReceiveDataBlock)( + id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSData* data); + + NSURLSessionDidReceiveDataBlock undefinedBlock = + ^(id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSData* data) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + dataTask:dataTask + didReceiveData:data + delegate:slf]; + }; + + NSURLSessionDidReceiveDataBlock implementationBlock = + ^(id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSData* data) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, session, dataTask, data); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, session, dataTask, data); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:(Class)cls { + SEL selector = @selector(URLSession:dataTask:didBecomeDownloadTask:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionDataDelegate); + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionDidBecomeDownloadTaskBlock)( + id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLSessionDownloadTask* downloadTask); + + NSURLSessionDidBecomeDownloadTaskBlock undefinedBlock = + ^(id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLSessionDownloadTask* downloadTask) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + dataTask:dataTask + didBecomeDownloadTask:downloadTask + delegate:slf]; + }; + + NSURLSessionDidBecomeDownloadTaskBlock implementationBlock = + ^(id<NSURLSessionDataDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLSessionDownloadTask* downloadTask) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, session, dataTask, downloadTask); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, session, dataTask, downloadTask); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectTaskDidReceiveResponseIntoDelegateClass:(Class)cls { + SEL selector = @selector(URLSession: + dataTask:didReceiveResponse:completionHandler:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionDataDelegate); + + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionDidReceiveResponseBlock)( + id<NSURLSessionDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLResponse* response, + void (^completionHandler)(NSURLSessionResponseDisposition disposition)); + + NSURLSessionDidReceiveResponseBlock undefinedBlock = ^( + id<NSURLSessionDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLResponse* response, + void (^completionHandler)(NSURLSessionResponseDisposition disposition)) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + dataTask:dataTask + didReceiveResponse:response + completionHandler:completionHandler + delegate:slf]; + completionHandler(NSURLSessionResponseAllow); + }; + + NSURLSessionDidReceiveResponseBlock implementationBlock = ^( + id<NSURLSessionDelegate> slf, + NSURLSession* session, + NSURLSessionDataTask* dataTask, + NSURLResponse* response, + void (^completionHandler)(NSURLSessionResponseDisposition disposition)) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + [[FLEXNetworkObserver sharedObserver] URLSession:session + dataTask:dataTask + didReceiveResponse:response + completionHandler:completionHandler + delegate:slf]; + } + originalImplementationBlock:^{ + ((void (*)( + id, SEL, id, id, id, void (^)(NSURLSessionResponseDisposition))) + objc_msgSend)( + slf, + swizzledSelector, + session, + dataTask, + response, + completionHandler); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectTaskDidCompleteWithErrorIntoDelegateClass:(Class)cls { + SEL selector = @selector(URLSession:task:didCompleteWithError:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionTaskDelegate); + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionTaskDidCompleteWithErrorBlock)( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSError* error); + + NSURLSessionTaskDidCompleteWithErrorBlock undefinedBlock = + ^(id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSError* error) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + task:task + didCompleteWithError:error + delegate:slf]; + }; + + NSURLSessionTaskDidCompleteWithErrorBlock implementationBlock = + ^(id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionTask* task, + NSError* error) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, session, task, error); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, session, task, error); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + +// Used for overriding AFNetworking behavior ++ (void)injectRespondsToSelectorIntoDelegateClass:(Class)cls { + SEL selector = @selector(respondsToSelector:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + // Protocol *protocol = @protocol(NSURLSessionTaskDelegate); + Method method = class_getInstanceMethod(cls, selector); + struct objc_method_description methodDescription = + *method_getDescription(method); + + BOOL (^undefinedBlock)(id<NSURLSessionTaskDelegate>, SEL) = + ^(id slf, SEL sel) { + return YES; + }; + + BOOL (^implementationBlock)(id<NSURLSessionTaskDelegate>, SEL) = ^( + id<NSURLSessionTaskDelegate> slf, SEL sel) { + if (sel == + @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { + return undefinedBlock(slf, sel); + } + return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(slf, swizzledSelector, sel); + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDownloadTaskDidFinishDownloadingIntoDelegateClass:(Class)cls { + SEL selector = @selector(URLSession:downloadTask:didFinishDownloadingToURL:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionDownloadDelegate); + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionDownloadTaskDidFinishDownloadingBlock)( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + NSURL* location); + + NSURLSessionDownloadTaskDidFinishDownloadingBlock undefinedBlock = + ^(id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + NSURL* location) { + NSData* data = [NSData dataWithContentsOfFile:location.relativePath]; + [[FLEXNetworkObserver sharedObserver] URLSession:session + task:task + didFinishDownloadingToURL:location + data:data + delegate:slf]; + }; + + NSURLSessionDownloadTaskDidFinishDownloadingBlock implementationBlock = + ^(id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + NSURL* location) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + undefinedBlock(slf, session, task, location); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + slf, swizzledSelector, session, task, location); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + ++ (void)injectDownloadTaskDidWriteDataIntoDelegateClass:(Class)cls { + SEL selector = @selector( + URLSession: + downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:); + SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; + + Protocol* protocol = @protocol(NSURLSessionDownloadDelegate); + struct objc_method_description methodDescription = + protocol_getMethodDescription(protocol, selector, NO, YES); + + typedef void (^NSURLSessionDownloadTaskDidWriteDataBlock)( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + int64_t bytesWritten, + int64_t totalBytesWritten, + int64_t totalBytesExpectedToWrite); + + NSURLSessionDownloadTaskDidWriteDataBlock undefinedBlock = ^( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + int64_t bytesWritten, + int64_t totalBytesWritten, + int64_t totalBytesExpectedToWrite) { + [[FLEXNetworkObserver sharedObserver] URLSession:session + downloadTask:task + didWriteData:bytesWritten + totalBytesWritten:totalBytesWritten + totalBytesExpectedToWrite:totalBytesExpectedToWrite + delegate:slf]; + }; + + NSURLSessionDownloadTaskDidWriteDataBlock implementationBlock = ^( + id<NSURLSessionTaskDelegate> slf, + NSURLSession* session, + NSURLSessionDownloadTask* task, + int64_t bytesWritten, + int64_t totalBytesWritten, + int64_t totalBytesExpectedToWrite) { + [self sniffWithoutDuplicationForObject:session + selector:selector + sniffingBlock:^{ + undefinedBlock( + slf, + session, + task, + bytesWritten, + totalBytesWritten, + totalBytesExpectedToWrite); + } + originalImplementationBlock:^{ + ((void (*)(id, SEL, id, id, int64_t, int64_t, int64_t))objc_msgSend)( + slf, + swizzledSelector, + session, + task, + bytesWritten, + totalBytesWritten, + totalBytesExpectedToWrite); + }]; + }; + + [FLEXUtility replaceImplementationOfSelector:selector + withSelector:swizzledSelector + forClass:cls + withMethodDescription:methodDescription + implementationBlock:implementationBlock + undefinedBlock:undefinedBlock]; +} + +static char const* const kFLEXRequestIDKey = "kFLEXRequestIDKey"; + ++ (NSString*)requestIDForConnectionOrTask:(id)connectionOrTask { + NSString* requestID = + objc_getAssociatedObject(connectionOrTask, kFLEXRequestIDKey); + if (!requestID) { + requestID = [self nextRequestID]; + [self setRequestID:requestID forConnectionOrTask:connectionOrTask]; + } + return requestID; +} + ++ (void)setRequestID:(NSString*)requestID + forConnectionOrTask:(id)connectionOrTask { + if (connectionOrTask) { + objc_setAssociatedObject( + connectionOrTask, + kFLEXRequestIDKey, + requestID, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } +} + +#pragma mark - Initialization + +- (id)init { + self = [super init]; + if (self) { + self.requestStatesForRequestIDs = [NSMutableDictionary new]; + self.queue = dispatch_queue_create( + "com.flex.FLEXNetworkObserver", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +#pragma mark - Private Methods + +- (void)performBlock:(dispatch_block_t)block { + dispatch_async(_queue, block); +} + +- (FLEXInternalRequestState*)requestStateForRequestID:(NSString*)requestID { + FLEXInternalRequestState* requestState = + self.requestStatesForRequestIDs[requestID]; + if (!requestState) { + requestState = [FLEXInternalRequestState new]; + [self.requestStatesForRequestIDs setObject:requestState forKey:requestID]; + } + return requestState; +} + +- (void)removeRequestStateForRequestID:(NSString*)requestID { + [self.requestStatesForRequestIDs removeObjectForKey:requestID]; +} + +@end + +@implementation FLEXNetworkObserver (NSURLConnectionHelpers) + +- (void)connection:(NSURLConnection*)connection + willSendRequest:(NSURLRequest*)request + redirectResponse:(NSURLResponse*)response + delegate:(id<NSURLConnectionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:connection]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + requestState.request = request; + [[FLEXNetworkRecorder defaultRecorder] + recordRequestWillBeSentWithRequestID:requestID + request:request + redirectResponse:response]; + NSString* mechanism = [NSString + stringWithFormat:@"NSURLConnection (delegate: %@)", [delegate class]]; + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:mechanism + forRequestID:requestID]; + }]; +} + +- (void)connection:(NSURLConnection*)connection + didReceiveResponse:(NSURLResponse*)response + delegate:(id<NSURLConnectionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:connection]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + NSMutableData* dataAccumulator = nil; + if (response.expectedContentLength < 0) { + dataAccumulator = [NSMutableData new]; + } else { + dataAccumulator = [[NSMutableData alloc] + initWithCapacity:(NSUInteger)response.expectedContentLength]; + } + requestState.dataAccumulator = dataAccumulator; + + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:response]; + }]; +} + +- (void)connection:(NSURLConnection*)connection + didReceiveData:(NSData*)data + delegate:(id<NSURLConnectionDelegate>)delegate { + // Just to be safe since we're doing this async + data = [data copy]; + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:connection]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + [requestState.dataAccumulator appendData:data]; + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:data.length]; + }]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection + delegate:(id<NSURLConnectionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:connection]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFinishedWithRequestID:requestID + responseBody:requestState.dataAccumulator]; + [self removeRequestStateForRequestID:requestID]; + }]; +} + +- (void)connection:(NSURLConnection*)connection + didFailWithError:(NSError*)error + delegate:(id<NSURLConnectionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:connection]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + // Cancellations can occur prior to the willSendRequest:... NSURLConnection + // delegate call. These are pretty common and clutter up the logs. Only + // record the failure if the recorder already knows about the request + // through willSendRequest:... + if (requestState.request) { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFailedWithRequestID:requestID + error:error]; + } + + [self removeRequestStateForRequestID:requestID]; + }]; +} + +- (void)connectionWillCancel:(NSURLConnection*)connection { + [self performBlock:^{ + // Mimic the behavior of NSURLSession which is to create an error on + // cancellation. + NSDictionary<NSString*, id>* userInfo = + @{NSLocalizedDescriptionKey : @"cancelled"}; + NSError* error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + userInfo:userInfo]; + [self connection:connection didFailWithError:error delegate:nil]; + }]; +} + +@end + +@implementation FLEXNetworkObserver (NSURLSessionTaskHelpers) + +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionTask*)task + willPerformHTTPRedirection:(NSHTTPURLResponse*)response + newRequest:(NSURLRequest*)request + completionHandler:(void (^)(NSURLRequest*))completionHandler + delegate:(id<NSURLSessionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = [[self class] requestIDForConnectionOrTask:task]; + [[FLEXNetworkRecorder defaultRecorder] + recordRequestWillBeSentWithRequestID:requestID + request:request + redirectResponse:response]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didReceiveResponse:(NSURLResponse*)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition)) + completionHandler + delegate:(id<NSURLSessionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = [[self class] requestIDForConnectionOrTask:dataTask]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + NSMutableData* dataAccumulator = nil; + if (response.expectedContentLength < 0) { + dataAccumulator = [NSMutableData new]; + } else { + dataAccumulator = [[NSMutableData alloc] + initWithCapacity:(NSUInteger)response.expectedContentLength]; + } + requestState.dataAccumulator = dataAccumulator; + + NSString* requestMechanism = + [NSString stringWithFormat:@"NSURLSessionDataTask (delegate: %@)", + [delegate class]]; + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:requestMechanism + forRequestID:requestID]; + + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:response]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didBecomeDownloadTask:(NSURLSessionDownloadTask*)downloadTask + delegate:(id<NSURLSessionDelegate>)delegate { + [self performBlock:^{ + // By setting the request ID of the download task to match the data task, + // it can pick up where the data task left off. + NSString* requestID = [[self class] requestIDForConnectionOrTask:dataTask]; + [[self class] setRequestID:requestID forConnectionOrTask:downloadTask]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + dataTask:(NSURLSessionDataTask*)dataTask + didReceiveData:(NSData*)data + delegate:(id<NSURLSessionDelegate>)delegate { + // Just to be safe since we're doing this async + data = [data copy]; + [self performBlock:^{ + NSString* requestID = [[self class] requestIDForConnectionOrTask:dataTask]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + [requestState.dataAccumulator appendData:data]; + + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:data.length]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionTask*)task + didCompleteWithError:(NSError*)error + delegate:(id<NSURLSessionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = [[self class] requestIDForConnectionOrTask:task]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + if (error) { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFailedWithRequestID:requestID + error:error]; + } else { + [[FLEXNetworkRecorder defaultRecorder] + recordLoadingFinishedWithRequestID:requestID + responseBody:requestState.dataAccumulator]; + } + + [self removeRequestStateForRequestID:requestID]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + downloadTask:(NSURLSessionDownloadTask*)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten + totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite + delegate:(id<NSURLSessionDelegate>)delegate { + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:downloadTask]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + + if (!requestState.dataAccumulator) { + NSUInteger unsignedBytesExpectedToWrite = totalBytesExpectedToWrite > 0 + ? (NSUInteger)totalBytesExpectedToWrite + : 0; + requestState.dataAccumulator = + [[NSMutableData alloc] initWithCapacity:unsignedBytesExpectedToWrite]; + [[FLEXNetworkRecorder defaultRecorder] + recordResponseReceivedWithRequestID:requestID + response:downloadTask.response]; + + NSString* requestMechanism = + [NSString stringWithFormat:@"NSURLSessionDownloadTask (delegate: %@)", + [delegate class]]; + [[FLEXNetworkRecorder defaultRecorder] recordMechanism:requestMechanism + forRequestID:requestID]; + } + + [[FLEXNetworkRecorder defaultRecorder] + recordDataReceivedWithRequestID:requestID + dataLength:bytesWritten]; + }]; +} + +- (void)URLSession:(NSURLSession*)session + task:(NSURLSessionDownloadTask*)downloadTask + didFinishDownloadingToURL:(NSURL*)location + data:(NSData*)data + delegate:(id<NSURLSessionDelegate>)delegate { + data = [data copy]; + [self performBlock:^{ + NSString* requestID = + [[self class] requestIDForConnectionOrTask:downloadTask]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + [requestState.dataAccumulator appendData:data]; + }]; +} + +- (void)URLSessionTaskWillResume:(NSURLSessionTask*)task { + // Since resume can be called multiple times on the same task, only treat the + // first resume as the equivalent to connection:willSendRequest:... + [self performBlock:^{ + NSString* requestID = [[self class] requestIDForConnectionOrTask:task]; + FLEXInternalRequestState* requestState = + [self requestStateForRequestID:requestID]; + if (!requestState.request) { + requestState.request = task.currentRequest; + + [[FLEXNetworkRecorder defaultRecorder] + recordRequestWillBeSentWithRequestID:requestID + request:task.currentRequest + redirectResponse:nil]; + } + }]; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.h new file mode 100755 index 000000000..623b9b85d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +#import <FlipperKitNetworkPlugin/SKNetworkReporter.h> + +// Notifications posted when the record is updated +extern NSString* const kFLEXNetworkRecorderNewTransactionNotification; +extern NSString* const kFLEXNetworkRecorderTransactionUpdatedNotification; +extern NSString* const kFLEXNetworkRecorderUserInfoTransactionKey; +extern NSString* const kFLEXNetworkRecorderTransactionsClearedNotification; + +@class FLEXNetworkTransaction; + +@interface FLEXNetworkRecorder : NSObject + +/// In general, it only makes sense to have one recorder for the entire +/// application. ++ (instancetype)defaultRecorder; + +@property(nonatomic, weak) id<SKNetworkReporterDelegate> delegate; + +/// Defaults to 25 MB if never set. Values set here are presisted across +/// launches of the app. +@property(nonatomic, assign) NSUInteger responseCacheByteLimit; + +/// If NO, the recorder not cache will not cache response for content types with +/// an "image", "video", or "audio" prefix. +@property(nonatomic, assign) BOOL shouldCacheMediaResponses; + +@property(nonatomic, copy) NSArray<NSString*>* hostBlacklist; + +// Accessing recorded network activity + +/// Array of FLEXNetworkTransaction objects ordered by start time with the +/// newest first. +- (NSArray<FLEXNetworkTransaction*>*)networkTransactions; + +/// The full response data IFF it hasn't been purged due to memory pressure. +- (NSData*)cachedResponseBodyForTransaction: + (FLEXNetworkTransaction*)transaction; + +/// Dumps all network transactions and cached response bodies. +- (void)clearRecordedActivity; + +// Recording network activity + +/// Call when app is about to send HTTP request. +- (void)recordRequestWillBeSentWithRequestID:(NSString*)requestID + request:(NSURLRequest*)request + redirectResponse:(NSURLResponse*)redirectResponse; + +/// Call when HTTP response is available. +- (void)recordResponseReceivedWithRequestID:(NSString*)requestID + response:(NSURLResponse*)response; + +/// Call when data chunk is received over the network. +- (void)recordDataReceivedWithRequestID:(NSString*)requestID + dataLength:(int64_t)dataLength; + +/// Call when HTTP request has finished loading. +- (void)recordLoadingFinishedWithRequestID:(NSString*)requestID + responseBody:(NSData*)responseBody; + +/// Call when HTTP request has failed to load. +- (void)recordLoadingFailedWithRequestID:(NSString*)requestID + error:(NSError*)error; + +/// Call to set the request mechanism anytime after recordRequestWillBeSent... +/// has been called. This string can be set to anything useful about the API +/// used to make the request. +- (void)recordMechanism:(NSString*)mechanism forRequestID:(NSString*)requestID; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.mm new file mode 100755 index 000000000..512b5c0ad --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.mm @@ -0,0 +1,251 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FLEXNetworkRecorder.h" + +#import "FLEXNetworkTransaction.h" +#import "FLEXUtility.h" + +NSString* const kFLEXNetworkRecorderNewTransactionNotification = + @"kFLEXNetworkRecorderNewTransactionNotification"; +NSString* const kFLEXNetworkRecorderTransactionUpdatedNotification = + @"kFLEXNetworkRecorderTransactionUpdatedNotification"; +NSString* const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction"; +NSString* const kFLEXNetworkRecorderTransactionsClearedNotification = + @"kFLEXNetworkRecorderTransactionsClearedNotification"; + +NSString* const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = + @"com.flex.responseCacheLimit"; + +@interface FLEXNetworkRecorder () + +@property(nonatomic, strong) NSCache* responseCache; +@property(nonatomic, strong) + NSMutableArray<FLEXNetworkTransaction*>* orderedTransactions; +@property(nonatomic, strong) + NSMutableDictionary<NSString*, FLEXNetworkTransaction*>* + networkTransactionsForRequestIdentifiers; +@property(nonatomic, strong) dispatch_queue_t queue; +@property(nonatomic, strong) + NSMutableDictionary<NSString*, NSNumber*>* identifierDict; +@end + +@implementation FLEXNetworkRecorder + +- (instancetype)init { + self = [super init]; + if (self) { + _responseCache = [NSCache new]; + NSUInteger responseCacheLimit = [[[NSUserDefaults standardUserDefaults] + objectForKey:kFLEXNetworkRecorderResponseCacheLimitDefaultsKey] + unsignedIntegerValue]; + if (responseCacheLimit) { + [_responseCache setTotalCostLimit:responseCacheLimit]; + } else { + // Default to 25 MB max. The cache will purge earlier if there is memory + // pressure. + [_responseCache setTotalCostLimit:25 * 1024 * 1024]; + } + _orderedTransactions = [NSMutableArray array]; + _networkTransactionsForRequestIdentifiers = + [NSMutableDictionary dictionary]; + + // Serial queue used because we use mutable objects that are not thread safe + _queue = dispatch_queue_create( + "com.flex.FLEXNetworkRecorder", DISPATCH_QUEUE_SERIAL); + _identifierDict = [NSMutableDictionary dictionary]; + } + return self; +} + ++ (instancetype)defaultRecorder { + static FLEXNetworkRecorder* defaultRecorder = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultRecorder = [[[self class] alloc] init]; + }); + return defaultRecorder; +} + +#pragma mark - Public Data Access + +- (void)setDelegate:(id<SKNetworkReporterDelegate>)delegate { + _delegate = delegate; +} + +- (NSUInteger)responseCacheByteLimit { + return [self.responseCache totalCostLimit]; +} + +- (void)setResponseCacheByteLimit:(NSUInteger)responseCacheByteLimit { + [self.responseCache setTotalCostLimit:responseCacheByteLimit]; + [[NSUserDefaults standardUserDefaults] + setObject:@(responseCacheByteLimit) + forKey:kFLEXNetworkRecorderResponseCacheLimitDefaultsKey]; +} + +- (NSArray<FLEXNetworkTransaction*>*)networkTransactions { + __block NSArray<FLEXNetworkTransaction*>* transactions = nil; + dispatch_sync(self.queue, ^{ + transactions = [self.orderedTransactions copy]; + }); + return transactions; +} + +- (NSData*)cachedResponseBodyForTransaction: + (FLEXNetworkTransaction*)transaction { + return [self.responseCache objectForKey:transaction.requestID]; +} + +- (void)clearRecordedActivity { + dispatch_async(self.queue, ^{ + [self.responseCache removeAllObjects]; + [self.orderedTransactions removeAllObjects]; + [self.networkTransactionsForRequestIdentifiers removeAllObjects]; + }); +} + +#pragma mark - Network Events + +- (void)recordRequestWillBeSentWithRequestID:(NSString*)requestID + request:(NSURLRequest*)request + redirectResponse:(NSURLResponse*)redirectResponse { + if (![self.identifierDict objectForKey:requestID]) { + self.identifierDict[requestID] = [NSNumber random]; + } + NSDate* startDate = [NSDate date]; + + if (redirectResponse) { + [self recordResponseReceivedWithRequestID:requestID + response:redirectResponse]; + [self recordLoadingFinishedWithRequestID:requestID responseBody:nil]; + } + + dispatch_async(self.queue, ^{ + SKRequestInfo* info = [[SKRequestInfo alloc] + initWithIdentifier:self.identifierDict[requestID].longLongValue + timestamp:[NSDate timestamp] + request:request + data:request.HTTPBody]; + [self.delegate didObserveRequest:info]; + + FLEXNetworkTransaction* transaction = [FLEXNetworkTransaction new]; + transaction.requestID = requestID; + transaction.request = request; + transaction.startTime = startDate; + + [self.orderedTransactions insertObject:transaction atIndex:0]; + [self.networkTransactionsForRequestIdentifiers setObject:transaction + forKey:requestID]; + transaction.transactionState = FLEXNetworkTransactionStateAwaitingResponse; + }); +} + +/// Call when HTTP response is available. +- (void)recordResponseReceivedWithRequestID:(NSString*)requestID + response:(NSURLResponse*)response { + NSDate* responseDate = [NSDate date]; + + dispatch_async(self.queue, ^{ + FLEXNetworkTransaction* transaction = + self.networkTransactionsForRequestIdentifiers[requestID]; + if (!transaction) { + return; + } + transaction.response = response; + transaction.transactionState = FLEXNetworkTransactionStateReceivingData; + transaction.latency = + -[transaction.startTime timeIntervalSinceDate:responseDate]; + }); +} + +/// Call when data chunk is received over the network. +- (void)recordDataReceivedWithRequestID:(NSString*)requestID + dataLength:(int64_t)dataLength { + dispatch_async(self.queue, ^{ + FLEXNetworkTransaction* transaction = + self.networkTransactionsForRequestIdentifiers[requestID]; + if (!transaction) { + return; + } + transaction.receivedDataLength += dataLength; + }); +} + +/// Call when HTTP request has finished loading. +- (void)recordLoadingFinishedWithRequestID:(NSString*)requestID + responseBody:(NSData*)responseBody { + NSDate* finishedDate = [NSDate date]; + dispatch_async(self.queue, ^{ + FLEXNetworkTransaction* transaction = + self.networkTransactionsForRequestIdentifiers[requestID]; + if (!transaction) { + return; + } + transaction.transactionState = FLEXNetworkTransactionStateFinished; + transaction.duration = + -[transaction.startTime timeIntervalSinceDate:finishedDate]; + SKResponseInfo* responseInfo = [[SKResponseInfo alloc] + initWithIndentifier:self.identifierDict[requestID].longLongValue + timestamp:[NSDate timestamp] + response:transaction.response + data:responseBody]; + self.identifierDict[requestID] = nil; // Clear the entry + [self.delegate didObserveResponse:responseInfo]; + + BOOL shouldCache = [responseBody length] > 0; + if (!self.shouldCacheMediaResponses) { + NSArray<NSString*>* ignoredMIMETypePrefixes = + @[ @"audio", @"image", @"video" ]; + for (NSString* ignoredPrefix in ignoredMIMETypePrefixes) { + shouldCache = shouldCache && + ![transaction.response.MIMEType hasPrefix:ignoredPrefix]; + } + } + + if (shouldCache) { + [self.responseCache setObject:responseBody + forKey:requestID + cost:[responseBody length]]; + } + }); +} + +- (void)recordLoadingFailedWithRequestID:(NSString*)requestID + error:(NSError*)error { + dispatch_async(self.queue, ^{ + FLEXNetworkTransaction* transaction = + self.networkTransactionsForRequestIdentifiers[requestID]; + if (!transaction) { + return; + } + + SKResponseInfo* responseInfo = [[SKResponseInfo alloc] + initWithIndentifier:self.identifierDict[requestID].longLongValue + timestamp:[NSDate timestamp] + response:transaction.response + data:nil]; + self.identifierDict[requestID] = nil; // Clear the entry + [self.delegate didObserveResponse:responseInfo]; + transaction.transactionState = FLEXNetworkTransactionStateFailed; + transaction.duration = -[transaction.startTime timeIntervalSinceNow]; + transaction.error = error; + }); +} + +- (void)recordMechanism:(NSString*)mechanism forRequestID:(NSString*)requestID { + dispatch_async(self.queue, ^{ + FLEXNetworkTransaction* transaction = + self.networkTransactionsForRequestIdentifiers[requestID]; + if (!transaction) { + return; + } + transaction.requestMechanism = mechanism; + }); +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.h new file mode 100755 index 000000000..893a03bcf --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +#import "UIKit/UIKit.h" + +typedef NS_ENUM(NSInteger, FLEXNetworkTransactionState) { + FLEXNetworkTransactionStateUnstarted, + FLEXNetworkTransactionStateAwaitingResponse, + FLEXNetworkTransactionStateReceivingData, + FLEXNetworkTransactionStateFinished, + FLEXNetworkTransactionStateFailed +}; + +@interface FLEXNetworkTransaction : NSObject + +@property(nonatomic, copy) NSString* requestID; + +@property(nonatomic, strong) NSURLRequest* request; +@property(nonatomic, strong) NSURLResponse* response; +@property(nonatomic, copy) NSString* requestMechanism; +@property(nonatomic, assign) FLEXNetworkTransactionState transactionState; +@property(nonatomic, strong) NSError* error; + +@property(nonatomic, strong) NSDate* startTime; +@property(nonatomic, assign) NSTimeInterval latency; +@property(nonatomic, assign) NSTimeInterval duration; + +@property(nonatomic, assign) int64_t receivedDataLength; + +/// Only applicable for image downloads. A small thumbnail to preview the full +/// response. +@property(nonatomic, strong) UIImage* responseThumbnail; + +/// Populated lazily. Handles both normal HTTPBody data and HTTPBodyStreams. +@property(nonatomic, strong, readonly) NSData* cachedRequestBody; + ++ (NSString*)readableStringFromTransactionState: + (FLEXNetworkTransactionState)state; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.m new file mode 100755 index 000000000..747569d29 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FLEXNetworkTransaction.h" + +@interface FLEXNetworkTransaction () + +@property(nonatomic, strong, readwrite) NSData* cachedRequestBody; + +@end + +@implementation FLEXNetworkTransaction + +- (NSString*)description { + NSString* description = [super description]; + + description = + [description stringByAppendingFormat:@" id = %@;", self.requestID]; + description = + [description stringByAppendingFormat:@" url = %@;", self.request.URL]; + description = + [description stringByAppendingFormat:@" duration = %f;", self.duration]; + description = + [description stringByAppendingFormat:@" receivedDataLength = %lld", + self.receivedDataLength]; + + return description; +} + +- (NSData*)cachedRequestBody { + if (!_cachedRequestBody) { + if (self.request.HTTPBody != nil) { + _cachedRequestBody = self.request.HTTPBody; + } else if ([self.request.HTTPBodyStream + conformsToProtocol:@protocol(NSCopying)]) { + NSInputStream* bodyStream = [self.request.HTTPBodyStream copy]; + const NSUInteger bufferSize = 1024; + uint8_t buffer[bufferSize]; + NSMutableData* data = [NSMutableData data]; + [bodyStream open]; + NSInteger readBytes = 0; + do { + readBytes = [bodyStream read:buffer maxLength:bufferSize]; + [data appendBytes:buffer length:readBytes]; + } while (readBytes > 0); + [bodyStream close]; + _cachedRequestBody = data; + } + } + return _cachedRequestBody; +} + ++ (NSString*)readableStringFromTransactionState: + (FLEXNetworkTransactionState)state { + NSString* readableString = nil; + switch (state) { + case FLEXNetworkTransactionStateUnstarted: + readableString = @"Unstarted"; + break; + + case FLEXNetworkTransactionStateAwaitingResponse: + readableString = @"Awaiting Response"; + break; + + case FLEXNetworkTransactionStateReceivingData: + readableString = @"Receiving Data"; + break; + + case FLEXNetworkTransactionStateFinished: + readableString = @"Finished"; + break; + + case FLEXNetworkTransactionStateFailed: + readableString = @"Failed"; + break; + } + return readableString; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.h new file mode 100755 index 000000000..249f3e981 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Availability.h> +#import <AvailabilityInternal.h> +#import <objc/runtime.h> + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +#define FLEXFloor(x) \ + (floor([[UIScreen mainScreen] scale] * (x)) / [[UIScreen mainScreen] scale]) + +#define FLEX_AT_LEAST_IOS11_SDK \ + defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) + +@interface NSNumber (SonarUtility) + ++ (NSNumber*)random; + +@end + +@interface NSDate (SonarUtility) + ++ (uint64_t)timestamp; +@end + +@interface FLEXUtility : NSObject + +// Swizzling utilities + ++ (SEL)swizzledSelectorForSelector:(SEL)selector; ++ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector + class:(Class)cls; ++ (void)replaceImplementationOfKnownSelector:(SEL)originalSelector + onClass:(Class)className + withBlock:(id)block + swizzledSelector:(SEL)swizzledSelector; ++ (void)replaceImplementationOfSelector:(SEL)selector + withSelector:(SEL)swizzledSelector + forClass:(Class)cls + withMethodDescription: + (struct objc_method_description)methodDescription + implementationBlock:(id)implementationBlock + undefinedBlock:(id)undefinedBlock; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.mm new file mode 100755 index 000000000..ac31758ad --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.mm @@ -0,0 +1,131 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FLEXUtility.h" + +#include <assert.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#import <objc/runtime.h> +#import <zlib.h> + +#import <ImageIO/ImageIO.h> + +@implementation FLEXUtility + ++ (SEL)swizzledSelectorForSelector:(SEL)selector { + return NSSelectorFromString( + [NSString stringWithFormat:@"_flex_swizzle_%x_%@", + arc4random(), + NSStringFromSelector(selector)]); +} + ++ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector + class:(Class)cls { + if ([cls instancesRespondToSelector:selector]) { + unsigned int numMethods = 0; + Method* methods = class_copyMethodList(cls, &numMethods); + + BOOL implementsSelector = NO; + for (int index = 0; index < numMethods; index++) { + SEL methodSelector = method_getName(methods[index]); + if (selector == methodSelector) { + implementsSelector = YES; + break; + } + } + + free(methods); + + if (!implementsSelector) { + return YES; + } + } + + return NO; +} + ++ (void)replaceImplementationOfKnownSelector:(SEL)originalSelector + onClass:(Class)className + withBlock:(id)block + swizzledSelector:(SEL)swizzledSelector { + // This method is only intended for swizzling methods that are know to exist + // on the class. Bail if that isn't the case. + Method originalMethod = class_getInstanceMethod(className, originalSelector); + if (!originalMethod) { + return; + } + + IMP implementation = imp_implementationWithBlock(block); + class_addMethod( + className, + swizzledSelector, + implementation, + method_getTypeEncoding(originalMethod)); + Method newMethod = class_getInstanceMethod(className, swizzledSelector); + method_exchangeImplementations(originalMethod, newMethod); +} + ++ (void)replaceImplementationOfSelector:(SEL)selector + withSelector:(SEL)swizzledSelector + forClass:(Class)cls + withMethodDescription: + (struct objc_method_description)methodDescription + implementationBlock:(id)implementationBlock + undefinedBlock:(id)undefinedBlock { + if ([self instanceRespondsButDoesNotImplementSelector:selector class:cls]) { + return; + } + + IMP implementation = imp_implementationWithBlock((id)( + [cls instancesRespondToSelector:selector] ? implementationBlock + : undefinedBlock)); + + Method oldMethod = class_getInstanceMethod(cls, selector); + if (oldMethod) { + class_addMethod( + cls, swizzledSelector, implementation, methodDescription.types); + + Method newMethod = class_getInstanceMethod(cls, swizzledSelector); + + method_exchangeImplementations(oldMethod, newMethod); + } else { + class_addMethod(cls, selector, implementation, methodDescription.types); + } +} + +@end + +@implementation NSNumber (SonarUtility) + ++ (NSNumber*)random { + int64_t identifier; + arc4random_buf(&identifier, sizeof(int64_t)); + return @(identifier); +} + +@end + +@implementation NSDate (SonarUtility) + ++ (uint64_t)getTimeNanoseconds { + static struct mach_timebase_info tb_info = {0}; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + __unused int ret = mach_timebase_info(&tb_info); + assert(0 == ret); + }); + + return (mach_absolute_time() * tb_info.numer) / tb_info.denom; +} + ++ (uint64_t)timestamp { + const uint64_t nowNanoSeconds = [self getTimeNanoseconds]; + return nowNanoSeconds / 1000000; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h new file mode 100644 index 000000000..08c071d5d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <FlipperKitNetworkPlugin/SKNetworkReporter.h> +#import <Foundation/Foundation.h> + +@interface SKIOSNetworkAdapter : NSObject<SKNetworkAdapterDelegate> +- (instancetype)init NS_DESIGNATED_INITIALIZER; +@property(weak, nonatomic) id<SKNetworkReporterDelegate> delegate; + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.mm new file mode 100644 index 000000000..f48e11d26 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.mm @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKIOSNetworkAdapter.h" +#import "FLEXNetworkLib/FLEXNetworkObserver.h" +#import "FLEXNetworkLib/FLEXNetworkRecorder.h" + +@implementation SKIOSNetworkAdapter +@synthesize delegate = _delegate; +- (instancetype)init { + if (self = [super init]) { + _delegate = nil; + } + return self; +} + +- (void)setDelegate:(id<SKNetworkReporterDelegate>)delegate { + _delegate = delegate; + [FLEXNetworkObserver start]; + [FLEXNetworkRecorder defaultRecorder].delegate = _delegate; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.h new file mode 100644 index 000000000..aa0c1f8c0 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <UIKit/UIKit.h> + +@interface SKHighlightOverlay : NSObject + ++ (instancetype)sharedInstance; ++ (UIColor*)overlayColor; + +- (void)mountInView:(UIView*)view withFrame:(CGRect)frame; +- (void)unmount; + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.mm new file mode 100644 index 000000000..e9e7d77fe --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.mm @@ -0,0 +1,56 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "SKHighlightOverlay.h" + +@implementation SKHighlightOverlay { + CALayer* _overlayLayer; +} + ++ (instancetype)sharedInstance { + static SKHighlightOverlay* sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [self new]; + }); + return sharedInstance; +} + +- (instancetype)init { + if (self = [super init]) { + _overlayLayer = [CALayer layer]; + _overlayLayer.backgroundColor = [SKHighlightOverlay overlayColor].CGColor; + } + + return self; +} + +- (void)mountInView:(UIView*)view withFrame:(CGRect)frame { + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue + forKey:kCATransactionDisableActions]; + _overlayLayer.frame = frame; + [view.layer addSublayer:_overlayLayer]; + [CATransaction commit]; +} + +- (void)unmount { + [_overlayLayer removeFromSuperlayer]; +} + ++ (UIColor*)overlayColor { + return [UIColor colorWithRed:136.0 / 255.0 + green:117.0 / 255.0 + blue:197.0 / 255.0 + alpha:0.6]; +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.h new file mode 100644 index 000000000..9551ff5e6 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import <Foundation/Foundation.h> + +#import <FlipperKit/FlipperPlugin.h> + +@interface FlipperKitReactPlugin : NSObject<FlipperPlugin> + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.m new file mode 100644 index 000000000..f30446330 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.m @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "FlipperKitReactPlugin.h" + +#import <FlipperKit/FlipperClient.h> +#import <FlipperKit/FlipperConnection.h> +#import <FlipperKit/FlipperResponder.h> + +// This class is no longer needed, but kept here for backward compatibility +@implementation FlipperKitReactPlugin + +- (NSString*)identifier { + return @"React"; +} + +- (void)didConnect:(id<FlipperConnection>)connection { +} + +- (void)didDisconnect { +} + +@end + +#endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h new file mode 100644 index 000000000..0048f443c --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <FlipperKit/FlipperPlugin.h> +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface FKUserDefaultsPlugin : NSObject<FlipperPlugin> + +- (instancetype)initWithSuiteName:(nullable NSString*)suiteName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m new file mode 100644 index 000000000..ced17e17a --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m @@ -0,0 +1,132 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FKUserDefaultsPlugin.h" +#import <FlipperKit/FlipperConnection.h> +#import <FlipperKit/FlipperResponder.h> +#import "FKUserDefaultsSwizzleUtility.h" + +static NSString* const kStandardUserDefaultsName = @"Standard UserDefaults"; +static NSString* const kAppSuiteUserDefaultsName = @"App Suite UserDefaults"; + +@interface FKUserDefaultsPlugin () +@property(nonatomic, strong) id<FlipperConnection> flipperConnection; +@property(nonatomic, strong) NSUserDefaults* standardUserDefaults; +@property(nonatomic, strong) NSUserDefaults* appSuiteUserDefaults; +@property(nonatomic, copy) NSString* key; +@property(nonatomic, copy) NSString* suiteName; +@end + +@implementation FKUserDefaultsPlugin + +- (instancetype)init { + if (self = [super init]) { + _standardUserDefaults = [NSUserDefaults standardUserDefaults]; + __weak typeof(self) weakSelf = self; + [FKUserDefaultsSwizzleUtility + swizzleSelector:@selector(setObject:forKey:) + class:[NSUserDefaults class] + block:^(NSInvocation* _Nonnull invocation) { + __unsafe_unretained id firstArg = nil; + __unsafe_unretained id secondArg = nil; + [invocation getArgument:&firstArg atIndex:2]; + [invocation getArgument:&secondArg atIndex:3]; + [invocation invoke]; + [weakSelf userDefaults:([invocation.target + isKindOfClass:[NSUserDefaults + class]] + ? invocation.target + : nil) + changedWithValue:firstArg + key:secondArg]; + }]; + } + return self; +} + +- (instancetype)initWithSuiteName:(NSString*)suiteName { + if (self = [self init]) { + _suiteName = suiteName; + if (_suiteName) { + _appSuiteUserDefaults = + [[NSUserDefaults alloc] initWithSuiteName:_suiteName]; + } + } + return self; +} + +- (void)didConnect:(id<FlipperConnection>)connection { + self.flipperConnection = connection; + [connection receive:@"getAllSharedPreferences" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + NSMutableDictionary* userDefaults = [NSMutableDictionary new]; + userDefaults[kStandardUserDefaultsName] = + [self.standardUserDefaults dictionaryRepresentation]; + if (self.appSuiteUserDefaults) { + userDefaults[kAppSuiteUserDefaultsName] = + [self.appSuiteUserDefaults dictionaryRepresentation]; + } + [responder success:userDefaults]; + }]; + + [connection receive:@"setSharedPreference" + withBlock:^(NSDictionary* params, id<FlipperResponder> responder) { + NSUserDefaults* sharedPreferences = + [self sharedPreferencesForParams:params]; + NSString* preferenceName = params[@"preferenceName"]; + [sharedPreferences setObject:params[@"preferenceValue"] + forKey:preferenceName]; + [responder success:[sharedPreferences dictionaryRepresentation]]; + }]; +} + +- (void)didDisconnect { + self.flipperConnection = nil; +} + +- (NSString*)identifier { + return @"Preferences"; +} + +#pragma mark - Private methods + +- (NSUserDefaults*)sharedPreferencesForParams:(NSDictionary*)params { + NSString* const sharedPreferencesNameKey = @"sharedPreferencesName"; + if (![params[sharedPreferencesNameKey] isKindOfClass:[NSString class]]) { + return _standardUserDefaults; + } + + NSString* sharedPreferencesName = params[sharedPreferencesNameKey]; + return ( + [sharedPreferencesName isEqualToString:kAppSuiteUserDefaultsName] + ? _appSuiteUserDefaults + : _standardUserDefaults); +} + +- (void)userDefaults:(NSUserDefaults*)userDefaults + changedWithValue:(id)value + key:(NSString*)key { + NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] * 1000; + NSString* intervalStr = [NSString stringWithFormat:@"%f", interval]; + NSMutableDictionary* params = + [@{@"name" : key, @"time" : intervalStr} mutableCopy]; + + if (!value) { + [params setObject:@"YES" forKey:@"deleted"]; + } else { + [params setObject:value forKey:@"value"]; + } + + NSString* sharedPreferencesName = + (userDefaults == _standardUserDefaults ? kStandardUserDefaultsName + : kAppSuiteUserDefaultsName); + [params setObject:sharedPreferencesName forKey:@"preferences"]; + [self.flipperConnection send:@"sharedPreferencesChange" + withParams:[params copy]]; +} + +@end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h new file mode 100644 index 000000000..d52902bd7 --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface FKUserDefaultsSwizzleUtility : NSObject + ++ (void)swizzleSelector:(SEL)selector + class:(Class)aClass + block:(void (^)(NSInvocation* invocation))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m new file mode 100644 index 000000000..5c02fd02d --- /dev/null +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "FKUserDefaultsSwizzleUtility.h" +#import <objc/runtime.h> + +@interface FKUserDefaultsSwizzleUtility () +@property(nonatomic, strong) NSMutableSet* swizzledClasses; +@property(nonatomic, strong) NSMutableDictionary* swizzledBlocks; +@property(nonatomic) IMP forwardingIMP; +@end + +@implementation FKUserDefaultsSwizzleUtility + +- (instancetype)init { + if (self = [super init]) { + _swizzledClasses = [NSMutableSet set]; + _swizzledBlocks = [NSMutableDictionary dictionary]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + _forwardingIMP = class_getMethodImplementation( + [NSObject class], @selector(flipperKitThisMethodShouldNotExist)); +#pragma clang diagnostic pop + } + return self; +} + ++ (instancetype)sharedInstance { + static FKUserDefaultsSwizzleUtility* sharedInstance = nil; + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + ++ (void)swizzleSelector:(SEL)selector + class:(Class)aClass + block:(void (^)(NSInvocation* _Nonnull))block { + [[self sharedInstance] swizzleSelector:selector class:aClass block:block]; +} + +- (void)swizzleSelector:(SEL)selector + class:(Class)aClass + block:(void (^)(NSInvocation* _Nonnull))blk { + if (![self.swizzledClasses containsObject:aClass]) { + SEL fwdSel = @selector(forwardInvocation:); + Method m = class_getInstanceMethod(aClass, fwdSel); + __block IMP orig; + __weak typeof(self) weakSelf = self; + IMP imp = imp_implementationWithBlock(^(id this, NSInvocation* invocation) { + NSString* selStr = NSStringFromSelector([invocation selector]); + void (^block)(NSInvocation*) = weakSelf.swizzledBlocks[aClass][selStr]; + if (blk != nil) { + NSString* originalStr = + [@"comfacebookFlipperKit_" stringByAppendingString:selStr]; + [invocation setSelector:NSSelectorFromString(originalStr)]; + block(invocation); + } else { + ((void (*)(id, SEL, NSInvocation*))orig)(this, fwdSel, invocation); + } + }); + orig = method_setImplementation(m, imp); + [self.swizzledClasses addObject:aClass]; + } + NSMutableDictionary* classDict = self.swizzledBlocks[aClass]; + if (classDict == nil) { + classDict = [NSMutableDictionary dictionary]; + self.swizzledBlocks[(id)aClass] = classDict; + } + classDict[NSStringFromSelector(selector)] = blk; + Method m = class_getInstanceMethod(aClass, selector); + NSString* newSelStr = [@"comfacebookFlipperKit_" + stringByAppendingString:NSStringFromSelector(selector)]; + SEL newSel = NSSelectorFromString(newSelStr); + class_addMethod( + aClass, newSel, method_getImplementation(m), method_getTypeEncoding(m)); + method_setImplementation(m, self.forwardingIMP); +} +@end diff --git a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement index de27b250c..3408766ca 100755 Binary files a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement and b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement differ diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORConsoleLogger.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORConsoleLogger.m index 6df92a5ab..0f5170d02 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORConsoleLogger.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORConsoleLogger.m @@ -16,6 +16,8 @@ #import "GDTCORLibrary/Public/GDTCORConsoleLogger.h" +volatile NSInteger GDTCORConsoleLoggerLoggingLevel = GDTCORLoggingLevelErrors; + /** The console logger prefix. */ static NSString *kGDTCORConsoleLogger = @"[GoogleDataTransport]"; @@ -23,11 +25,28 @@ NSString *GDTCORMessageCodeEnumToString(GDTCORMessageCode code) { return [[NSString alloc] initWithFormat:@"I-GDTCOR%06ld", (long)code]; } -void GDTCORLog(GDTCORMessageCode code, NSString *format, ...) { +void GDTCORLog(GDTCORMessageCode code, GDTCORLoggingLevel logLevel, NSString *format, ...) { // Don't log anything in not debug builds. #if !NDEBUG - NSString *logFormat = [NSString stringWithFormat:@"%@[%@] %@", kGDTCORConsoleLogger, - GDTCORMessageCodeEnumToString(code), format]; + if (logLevel >= GDTCORConsoleLoggerLoggingLevel) { + NSString *logFormat = [NSString stringWithFormat:@"%@[%@] %@", kGDTCORConsoleLogger, + GDTCORMessageCodeEnumToString(code), format]; + va_list args; + va_start(args, format); + NSLogv(logFormat, args); + va_end(args); + } +#endif // !NDEBUG +} + +void GDTCORLogAssert( + BOOL wasFatal, NSString *_Nonnull file, NSInteger line, NSString *_Nullable format, ...) { +// Don't log anything in not debug builds. +#if !NDEBUG + GDTCORMessageCode code = wasFatal ? GDTCORMCEFatalAssertion : GDTCORMCEGeneralError; + NSString *logFormat = + [NSString stringWithFormat:@"%@[%@] (%@:%ld) : %@", kGDTCORConsoleLogger, + GDTCORMessageCodeEnumToString(code), file, (long)line, format]; va_list args; va_start(args, format); NSLogv(logFormat, args); diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORDataFuture.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORDataFuture.m index 33c3f1521..04903d472 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORDataFuture.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORDataFuture.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import <GoogleDataTransport/GDTCORDataFuture.h> +#import "GDTCORLibrary/Private/GDTCORDataFuture.h" @implementation GDTCORDataFuture @@ -32,7 +32,7 @@ - (NSUInteger)hash { // In reality, only one of these should be populated. - return [_fileURL hash] ^ [_originalData hash]; + return [_fileURL hash]; } #pragma mark - NSSecureCoding @@ -40,23 +40,18 @@ /** Coding key for _fileURL ivar. */ static NSString *kGDTCORDataFutureFileURLKey = @"GDTCORDataFutureFileURLKey"; -/** Coding key for _data ivar. */ -static NSString *kGDTCORDataFutureDataKey = @"GDTCORDataFutureDataKey"; - + (BOOL)supportsSecureCoding { return YES; } - (void)encodeWithCoder:(nonnull NSCoder *)aCoder { [aCoder encodeObject:_fileURL forKey:kGDTCORDataFutureFileURLKey]; - [aCoder encodeObject:_originalData forKey:kGDTCORDataFutureDataKey]; } - (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder { self = [self init]; if (self) { _fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kGDTCORDataFutureFileURLKey]; - _originalData = [aDecoder decodeObjectOfClass:[NSData class] forKey:kGDTCORDataFutureDataKey]; } return self; } diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCOREvent.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCOREvent.m index ed0c8e833..651431223 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCOREvent.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCOREvent.m @@ -14,16 +14,53 @@ * limitations under the License. */ -#import <GoogleDataTransport/GDTCOREvent.h> +#import "GDTCORLibrary/Public/GDTCOREvent.h" #import <GoogleDataTransport/GDTCORAssert.h> +#import <GoogleDataTransport/GDTCORClock.h> #import <GoogleDataTransport/GDTCORConsoleLogger.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> +#import <GoogleDataTransport/GDTCORPlatform.h> +#import "GDTCORLibrary/Private/GDTCORDataFuture.h" #import "GDTCORLibrary/Private/GDTCOREvent_Private.h" @implementation GDTCOREvent ++ (NSNumber *)nextEventID { + static unsigned long long nextEventID = 0; + static NSString *counterPath; + static dispatch_queue_t eventIDQueue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + eventIDQueue = dispatch_queue_create("com.google.GDTCOREventIDQueue", DISPATCH_QUEUE_SERIAL); + counterPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; + counterPath = [NSString stringWithFormat:@"%@/google-sdks-events/count", counterPath]; + NSError *error; + NSString *countText = [NSString stringWithContentsOfFile:counterPath + encoding:NSUTF8StringEncoding + error:&error]; + const char *countChars = [countText UTF8String]; + unsigned long long count = 0ULL; + if (countChars) { + count = strtoull([countText UTF8String], NULL, 10); + } + nextEventID = error ? 0 : count; + }); + + __block NSNumber *result; + dispatch_sync(eventIDQueue, ^{ + result = @(nextEventID); + nextEventID++; + NSError *error; + [[result stringValue] writeToFile:counterPath + atomically:YES + encoding:NSUTF8StringEncoding + error:&error]; + GDTCORAssert(error == nil, @"There was an error saving the new counter value to disk."); + }); + return result; +} + - (nullable instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target { GDTCORAssert(mappingID.length > 0, @"Please give a valid mapping ID"); GDTCORAssert(target > 0, @"A target cannot be negative or 0"); @@ -32,92 +69,189 @@ } self = [super init]; if (self) { + _eventID = [GDTCOREvent nextEventID]; _mappingID = mappingID; _target = target; _qosTier = GDTCOREventQosDefault; } - GDTCORLogDebug("Event %@ created. mappingID: %@ target:%ld qos:%ld", self, _mappingID, - (long)_target, (long)_qosTier); + GDTCORLogDebug(@"Event %@ created. mappingID: %@ target:%ld", self, mappingID, (long)target); return self; } - (instancetype)copy { GDTCOREvent *copy = [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target]; + copy->_eventID = _eventID; copy.dataObject = _dataObject; - copy.dataObjectTransportBytes = _dataObjectTransportBytes; copy.qosTier = _qosTier; copy.clockSnapshot = _clockSnapshot; - copy.customPrioritizationParams = _customPrioritizationParams; - GDTCORLogDebug("Copying event %@ to event %@", self, copy); + copy.customBytes = _customBytes; + copy->_GDTFilePath = _GDTFilePath; + GDTCORLogDebug(@"Copying event %@ to event %@", self, copy); return copy; } - (NSUInteger)hash { // This loses some precision, but it's probably fine. + NSUInteger eventIDHash = [_eventID hash]; NSUInteger mappingIDHash = [_mappingID hash]; NSUInteger timeHash = [_clockSnapshot hash]; - NSUInteger dataObjectTransportBytesHash = [_dataObjectTransportBytes hash]; - return mappingIDHash ^ _target ^ dataObjectTransportBytesHash ^ _qosTier ^ timeHash; + NSInteger dataObjectHash = [_dataObject hash]; + NSUInteger fileURL = [_GDTFilePath hash]; + + return eventIDHash ^ mappingIDHash ^ _target ^ _qosTier ^ timeHash ^ dataObjectHash ^ fileURL; } - (BOOL)isEqual:(id)object { return [self hash] == [object hash]; } +#pragma mark - Property overrides + - (void)setDataObject:(id<GDTCOREventDataObject>)dataObject { // If you're looking here because of a performance issue in -transportBytes slowing the assignment // of -dataObject, one way to address this is to add a queue to this class, // dispatch_(barrier_ if concurrent)async here, and implement the getter with a dispatch_sync. if (dataObject != _dataObject) { _dataObject = dataObject; - _dataObjectTransportBytes = [dataObject transportBytes]; } } -- (GDTCORStoredEvent *)storedEventWithDataFuture:(GDTCORDataFuture *)dataFuture { - return [[GDTCORStoredEvent alloc] initWithEvent:self dataFuture:dataFuture]; +- (NSURL *)fileURL { + if (!_GDTFilePath) { + _GDTFilePath = [NSString stringWithFormat:@"event-%lu", (unsigned long)self.hash]; + } + return [GDTCORRootDirectory() URLByAppendingPathComponent:_GDTFilePath]; +} + +#pragma mark - Private methods + +- (BOOL)writeToGDTPath:(NSString *)filePath error:(NSError **)error { + NSData *dataTransportBytes = [_dataObject transportBytes]; + if (dataTransportBytes == nil) { + _GDTFilePath = nil; + _dataObject = nil; + return NO; + } + NSURL *fileURL = [GDTCORRootDirectory() URLByAppendingPathComponent:filePath]; + BOOL writingSuccess = [dataTransportBytes writeToURL:fileURL + options:NSDataWritingAtomic + error:error]; + if (!writingSuccess) { + GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@", fileURL); + return NO; + } + _GDTFilePath = filePath; + _dataObject = nil; + return YES; } #pragma mark - NSSecureCoding and NSCoding Protocols +/** NSCoding key for eventID property. */ +static NSString *eventIDKey = @"_eventID"; + /** NSCoding key for mappingID property. */ static NSString *mappingIDKey = @"_mappingID"; /** NSCoding key for target property. */ static NSString *targetKey = @"_target"; -/** NSCoding key for dataObjectTransportBytes property. */ -static NSString *dataObjectTransportBytesKey = @"_dataObjectTransportBytesKey"; - /** NSCoding key for qosTier property. */ static NSString *qosTierKey = @"_qosTier"; /** NSCoding key for clockSnapshot property. */ static NSString *clockSnapshotKey = @"_clockSnapshot"; +/** NSCoding key for fileURL property. */ +static NSString *fileURLKey = @"_fileURL"; + +/** NSCoding key for GDTFilePath property. */ +static NSString *kGDTFilePathKey = @"_GDTFilePath"; + +/** NSCoding key for backwards compatibility of GDTCORStoredEvent mappingID property.*/ +static NSString *kStoredEventMappingIDKey = @"GDTCORStoredEventMappingIDKey"; + +/** NSCoding key for backwards compatibility of GDTCORStoredEvent target property.*/ +static NSString *kStoredEventTargetKey = @"GDTCORStoredEventTargetKey"; + +/** NSCoding key for backwards compatibility of GDTCORStoredEvent qosTier property.*/ +static NSString *kStoredEventQosTierKey = @"GDTCORStoredEventQosTierKey"; + +/** NSCoding key for backwards compatibility of GDTCORStoredEvent clockSnapshot property.*/ +static NSString *kStoredEventClockSnapshotKey = @"GDTCORStoredEventClockSnapshotKey"; + +/** NSCoding key for backwards compatibility of GDTCORStoredEvent dataFuture property.*/ +static NSString *kStoredEventDataFutureKey = @"GDTCORStoredEventDataFutureKey"; + +/** NSCoding key for customData property. */ +static NSString *kCustomDataKey = @"GDTCOREventCustomDataKey"; + + (BOOL)supportsSecureCoding { return YES; } - (id)initWithCoder:(NSCoder *)aDecoder { - NSString *mappingID = [aDecoder decodeObjectOfClass:[NSObject class] forKey:mappingIDKey]; + GDTCORDataFuture *dataFuture = [aDecoder decodeObjectOfClass:[GDTCORDataFuture class] + forKey:kStoredEventDataFutureKey]; + if (dataFuture) { + return [self initWithCoderForStoredEventBackwardCompatibility:aDecoder + fileURL:dataFuture.fileURL]; + } + NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:mappingIDKey]; NSInteger target = [aDecoder decodeIntegerForKey:targetKey]; self = [self initWithMappingID:mappingID target:target]; if (self) { - _dataObjectTransportBytes = [aDecoder decodeObjectOfClass:[NSData class] - forKey:dataObjectTransportBytesKey]; + _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey]; + if (_eventID == nil) { + _eventID = [GDTCOREvent nextEventID]; + } _qosTier = [aDecoder decodeIntegerForKey:qosTierKey]; _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:clockSnapshotKey]; + NSURL *fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:fileURLKey]; + if (fileURL) { + _GDTFilePath = [fileURL lastPathComponent]; + } else { + _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey]; + } + _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey]; + } + return self; +} + +- (id)initWithCoderForStoredEventBackwardCompatibility:(NSCoder *)aDecoder + fileURL:(NSURL *)fileURL { + NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class] + forKey:kStoredEventMappingIDKey]; + NSInteger target = [[aDecoder decodeObjectOfClass:[NSNumber class] + forKey:kStoredEventTargetKey] integerValue]; + self = [self initWithMappingID:mappingID target:target]; + if (self) { + _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey]; + if (_eventID == nil) { + _eventID = [GDTCOREvent nextEventID]; + } + _qosTier = [[aDecoder decodeObjectOfClass:[NSNumber class] + forKey:kStoredEventQosTierKey] integerValue]; + _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] + forKey:kStoredEventClockSnapshotKey]; + if (fileURL) { + _GDTFilePath = [fileURL lastPathComponent]; + } else { + _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey]; + } + _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_eventID forKey:eventIDKey]; [aCoder encodeObject:_mappingID forKey:mappingIDKey]; [aCoder encodeInteger:_target forKey:targetKey]; - [aCoder encodeObject:_dataObjectTransportBytes forKey:dataObjectTransportBytesKey]; [aCoder encodeInteger:_qosTier forKey:qosTierKey]; [aCoder encodeObject:_clockSnapshot forKey:clockSnapshotKey]; + [aCoder encodeObject:_GDTFilePath forKey:kGDTFilePathKey]; + [aCoder encodeObject:_customBytes forKey:kCustomDataKey]; } @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORFlatFileStorage.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORFlatFileStorage.m new file mode 100644 index 000000000..224fb5869 --- /dev/null +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORFlatFileStorage.m @@ -0,0 +1,342 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTCORLibrary/Private/GDTCORFlatFileStorage.h" + +#import <GoogleDataTransport/GDTCORAssert.h> +#import <GoogleDataTransport/GDTCORConsoleLogger.h> +#import <GoogleDataTransport/GDTCOREvent.h> +#import <GoogleDataTransport/GDTCORLifecycle.h> +#import <GoogleDataTransport/GDTCORPrioritizer.h> + +#import "GDTCORLibrary/Private/GDTCOREvent_Private.h" +#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" +#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h" + +@implementation GDTCORFlatFileStorage + ++ (void)load { + [[GDTCORRegistrar sharedInstance] registerStorage:[self sharedInstance] target:kGDTCORTargetCCT]; + [[GDTCORRegistrar sharedInstance] registerStorage:[self sharedInstance] target:kGDTCORTargetFLL]; + [[GDTCORRegistrar sharedInstance] registerStorage:[self sharedInstance] target:kGDTCORTargetCSH]; + + // Sets a global translation mapping to decode GDTCORStoredEvent objects encoded as instances of + // GDTCOREvent instead. Then we do the same thing with GDTCORStorage. This must be done in load + // because there are no direct references to this class and the NSCoding methods won't be called + // unless the class name is mapped early. + [NSKeyedUnarchiver setClass:[GDTCOREvent class] forClassName:@"GDTCORStoredEvent"]; + [NSKeyedUnarchiver setClass:[GDTCORFlatFileStorage class] forClassName:@"GDTCORStorage"]; +} + ++ (NSString *)archivePath { + static NSString *archivePath; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + archivePath = + [GDTCORRootDirectory() URLByAppendingPathComponent:@"GDTCORFlatFileStorageArchive"].path; + }); + return archivePath; +} + ++ (instancetype)sharedInstance { + static GDTCORFlatFileStorage *sharedStorage; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedStorage = [[GDTCORFlatFileStorage alloc] init]; + }); + return sharedStorage; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _storageQueue = + dispatch_queue_create("com.google.GDTCORFlatFileStorage", DISPATCH_QUEUE_SERIAL); + _targetToEventSet = [[NSMutableDictionary alloc] init]; + _storedEvents = [[NSMutableDictionary alloc] init]; + _uploadCoordinator = [GDTCORUploadCoordinator sharedInstance]; + } + return self; +} + +- (void)storeEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion { + GDTCORLogDebug(@"Saving event: %@", event); + if (event == nil) { + GDTCORLogDebug(@"%@", @"The event was nil, so it was not saved."); + return; + } + BOOL hadOriginalCompletion = completion != nil; + if (!completion) { + completion = ^(BOOL wasWritten, NSError *_Nullable error) { + GDTCORLogDebug(@"event %@ stored. success:%@ error:%@", event, wasWritten ? @"YES" : @"NO", + error); + }; + } + + __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; + bgID = [[GDTCORApplication sharedApplication] + beginBackgroundTaskWithName:@"GDTStorage" + expirationHandler:^{ + // End the background task if it's still valid. + [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + }]; + + dispatch_async(_storageQueue, ^{ + // Check that a backend implementation is available for this target. + NSInteger target = event.target; + + // Check that a prioritizer is available for this target. + id<GDTCORPrioritizer> prioritizer = + [GDTCORRegistrar sharedInstance].targetToPrioritizer[@(target)]; + GDTCORAssert(prioritizer, @"There's no prioritizer registered for the given target. Are you " + @"sure you've added the support library for the backend you need?"); + + // Write the transport bytes to disk, get a filename. + GDTCORAssert([event.dataObject transportBytes], + @"The event should have been serialized to bytes"); + NSError *error = nil; + NSURL *eventFile = [self saveEventBytesToDisk:event eventHash:event.hash error:&error]; + if (!eventFile || error) { + GDTCORLogError(GDTCORMCEFileWriteError, @"Event failed to save to disk: %@", error); + completion(NO, error); + return; + } else { + GDTCORLogDebug(@"Event saved to disk: %@", eventFile); + completion(YES, error); + } + + // Add event to tracking collections. + [self addEventToTrackingCollections:event]; + + // Have the prioritizer prioritize the event and save state if there was an onComplete block. + [prioritizer prioritizeEvent:event]; + if (hadOriginalCompletion && [prioritizer respondsToSelector:@selector(saveState)]) { + [prioritizer saveState]; + GDTCORLogDebug(@"Prioritizer %@ has saved state due to an event's onComplete block.", + prioritizer); + } + + // Check the QoS, if it's high priority, notify the target that it has a high priority event. + if (event.qosTier == GDTCOREventQoSFast) { + [self.uploadCoordinator forceUploadForTarget:target]; + } + + // Write state to disk if there was an onComplete block or if we're in the background. + if (hadOriginalCompletion || [[GDTCORApplication sharedApplication] isRunningInBackground]) { + if (hadOriginalCompletion) { + GDTCORLogDebug(@"%@", + @"Saving flat file storage state because a completion block was passed."); + } else { + GDTCORLogDebug( + @"%@", @"Saving flat file storage state because the app is running in the background"); + } + NSError *error; + GDTCOREncodeArchive(self, [GDTCORFlatFileStorage archivePath], &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCORFlatFileStorage to an archive failed: %@", error); + } + } + + // Cancel or end the associated background task if it's still valid. + [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + GDTCORLogDebug(@"Event %@ is stored. There are %ld events stored on disk", event, + (unsigned long)self->_storedEvents.count); + }); +} + +- (void)removeEvents:(NSSet<NSNumber *> *)eventIDs { + NSSet<NSNumber *> *eventsToRemove = [eventIDs copy]; + dispatch_async(_storageQueue, ^{ + for (NSNumber *eventID in eventsToRemove) { + // Remove from disk, first and foremost. + GDTCOREvent *event = self->_storedEvents[eventID]; + if (event) { + NSError *error; + if (event.fileURL) { + NSURL *fileURL = event.fileURL; + BOOL result = [[NSFileManager defaultManager] removeItemAtPath:fileURL.path error:&error]; + if (!result || error) { + GDTCORLogWarning(GDTCORMCWFileReadError, + @"There was an error removing an event file: %@", error); + } else { + GDTCORLogDebug(@"Removed event from disk: %@", fileURL); + } + } + + // Remove from the tracking collections. + [self.storedEvents removeObjectForKey:event.eventID]; + [self.targetToEventSet[@(event.target)] removeObject:event]; + } + } + }); +} + +#pragma mark - Private helper methods + +/** Saves the event's dataObject to a file using NSData mechanisms. + * + * @note This method should only be called from a method within a block on _storageQueue to maintain + * thread safety. + * + * @param event The event. + * @param eventHash The hash value of the event. + * @return The filename + */ +- (NSURL *)saveEventBytesToDisk:(GDTCOREvent *)event + eventHash:(NSUInteger)eventHash + error:(NSError **)error { + NSString *eventFileName = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash]; + NSError *writingError; + [event writeToGDTPath:eventFileName error:&writingError]; + if (writingError) { + GDTCORLogDebug(@"There was an error saving an event to disk: %@", writingError); + } + return event.fileURL; +} + +/** Adds the event to internal tracking collections. + * + * @note This method should only be called from a method within a block on _storageQueue to maintain + * thread safety. + * + * @param event The event to track. + */ +- (void)addEventToTrackingCollections:(GDTCOREvent *)event { + _storedEvents[event.eventID] = event; + NSNumber *target = @(event.target); + NSMutableSet<GDTCOREvent *> *events = self.targetToEventSet[target]; + events = events ? events : [[NSMutableSet alloc] init]; + [events addObject:event]; + _targetToEventSet[target] = events; +} + +#pragma mark - GDTCORLifecycleProtocol + +- (void)appWillForeground:(GDTCORApplication *)app { + dispatch_async(_storageQueue, ^{ + NSError *error; + GDTCORDecodeArchive([GDTCORFlatFileStorage class], [GDTCORFlatFileStorage archivePath], nil, + &error); + if (error) { + GDTCORLogDebug(@"Deserializing GDTCORFlatFileStorage from an archive failed: %@", error); + } + }); +} + +- (void)appWillBackground:(GDTCORApplication *)app { + dispatch_async(_storageQueue, ^{ + // Immediately request a background task to run until the end of the current queue of work, and + // cancel it once the work is done. + __block GDTCORBackgroundIdentifier bgID = + [app beginBackgroundTaskWithName:@"GDTStorage" + expirationHandler:^{ + [app endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + }]; + NSError *error; + GDTCOREncodeArchive(self, [GDTCORFlatFileStorage archivePath], &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCORFlatFileStorage to an archive failed: %@", error); + } else { + GDTCORLogDebug(@"Serialized GDTCORFlatFileStorage to %@", + [GDTCORFlatFileStorage archivePath]); + } + + // End the background task if it's still valid. + [app endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + }); +} + +- (void)appWillTerminate:(GDTCORApplication *)application { + dispatch_sync(_storageQueue, ^{ + NSError *error; + GDTCOREncodeArchive(self, [GDTCORFlatFileStorage archivePath], &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCORFlatFileStorage to an archive failed: %@", error); + } else { + GDTCORLogDebug(@"Serialized GDTCORFlatFileStorage to %@", + [GDTCORFlatFileStorage archivePath]); + } + }); +} + +#pragma mark - NSSecureCoding + +/** The NSKeyedCoder key for the storedEvents property. */ +static NSString *const kGDTCORFlatFileStorageStoredEventsKey = @"GDTCORStorageStoredEventsKey"; + +/** The NSKeyedCoder key for the targetToEventSet property. */ +static NSString *const kGDTCORFlatFileStorageTargetToEventSetKey = + @"GDTCORStorageTargetToEventSetKey"; + +/** The NSKeyedCoder key for the uploadCoordinator property. */ +static NSString *const kGDTCORFlatFileStorageUploadCoordinatorKey = + @"GDTCORStorageUploadCoordinatorKey"; + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + // Create the singleton and populate its ivars. + GDTCORFlatFileStorage *sharedInstance = [self.class sharedInstance]; + NSSet *classes = [NSSet setWithObjects:[NSMutableOrderedSet class], [NSMutableDictionary class], + [GDTCOREvent class], nil]; + id storedEvents = [aDecoder decodeObjectOfClasses:classes + forKey:kGDTCORFlatFileStorageStoredEventsKey]; + NSMutableDictionary<NSNumber *, GDTCOREvent *> *events = [[NSMutableDictionary alloc] init]; + if ([storedEvents isKindOfClass:[NSMutableOrderedSet class]]) { + [(NSMutableOrderedSet *)storedEvents + enumerateObjectsUsingBlock:^(GDTCOREvent *_Nonnull obj, NSUInteger idx, + BOOL *_Nonnull stop) { + events[obj.eventID] = obj; + }]; + } else if ([storedEvents isKindOfClass:[NSMutableDictionary class]]) { + events = (NSMutableDictionary *)storedEvents; + } + sharedInstance->_storedEvents = events; + classes = [NSSet + setWithObjects:[NSMutableDictionary class], [NSMutableSet class], [GDTCOREvent class], nil]; + sharedInstance->_targetToEventSet = + [aDecoder decodeObjectOfClasses:classes forKey:kGDTCORFlatFileStorageTargetToEventSetKey]; + sharedInstance->_uploadCoordinator = + [aDecoder decodeObjectOfClass:[GDTCORUploadCoordinator class] + forKey:kGDTCORFlatFileStorageUploadCoordinatorKey]; + return sharedInstance; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + GDTCORFlatFileStorage *sharedInstance = [self.class sharedInstance]; + NSMutableDictionary<NSNumber *, GDTCOREvent *> *storedEvents = sharedInstance->_storedEvents; + if (storedEvents) { + [aCoder encodeObject:storedEvents forKey:kGDTCORFlatFileStorageStoredEventsKey]; + } + NSMutableDictionary<NSNumber *, NSMutableSet<GDTCOREvent *> *> *targetToEventSet = + sharedInstance->_targetToEventSet; + if (targetToEventSet) { + [aCoder encodeObject:targetToEventSet forKey:kGDTCORFlatFileStorageTargetToEventSetKey]; + } + GDTCORUploadCoordinator *uploadCoordinator = sharedInstance->_uploadCoordinator; + if (uploadCoordinator) { + [aCoder encodeObject:uploadCoordinator forKey:kGDTCORFlatFileStorageUploadCoordinatorKey]; + } +} + +@end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORLifecycle.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORLifecycle.m index cd554ad6c..9228c346d 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORLifecycle.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORLifecycle.m @@ -20,7 +20,6 @@ #import <GoogleDataTransport/GDTCOREvent.h> #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" -#import "GDTCORLibrary/Private/GDTCORStorage_Private.h" #import "GDTCORLibrary/Private/GDTCORTransformer_Private.h" #import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h" @@ -72,19 +71,15 @@ - (void)applicationDidEnterBackground:(NSNotification *)notification { GDTCORApplication *application = [GDTCORApplication sharedApplication]; if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillBackground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORTransformer that the app is backgrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORTransformer that the app is backgrounding."); [[GDTCORTransformer sharedInstance] appWillBackground:application]; } - if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillBackground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORStorage that the app is backgrounding."); - [[GDTCORStorage sharedInstance] appWillBackground:application]; - } if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillBackground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORUploadCoordinator that the app is backgrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORUploadCoordinator that the app is backgrounding."); [[GDTCORUploadCoordinator sharedInstance] appWillBackground:application]; } if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillBackground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORRegistrar that the app is backgrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORRegistrar that the app is backgrounding."); [[GDTCORRegistrar sharedInstance] appWillBackground:application]; } } @@ -92,19 +87,15 @@ - (void)applicationWillEnterForeground:(NSNotification *)notification { GDTCORApplication *application = [GDTCORApplication sharedApplication]; if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillForeground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORTransformer that the app is foregrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORTransformer that the app is foregrounding."); [[GDTCORTransformer sharedInstance] appWillForeground:application]; } - if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillForeground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORStorage that the app is foregrounding."); - [[GDTCORStorage sharedInstance] appWillForeground:application]; - } if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillForeground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORUploadCoordinator that the app is foregrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORUploadCoordinator that the app is foregrounding."); [[GDTCORUploadCoordinator sharedInstance] appWillForeground:application]; } if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillForeground:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORRegistrar that the app is foregrounding."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORRegistrar that the app is foregrounding."); [[GDTCORRegistrar sharedInstance] appWillForeground:application]; } } @@ -112,19 +103,15 @@ - (void)applicationWillTerminate:(NSNotification *)notification { GDTCORApplication *application = [GDTCORApplication sharedApplication]; if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORTransformer that the app is terminating."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORTransformer that the app is terminating."); [[GDTCORTransformer sharedInstance] appWillTerminate:application]; } - if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORStorage that the app is terminating."); - [[GDTCORStorage sharedInstance] appWillTerminate:application]; - } if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORUploadCoordinator that the app is terminating."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORUploadCoordinator that the app is terminating."); [[GDTCORUploadCoordinator sharedInstance] appWillTerminate:application]; } if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) { - GDTCORLogDebug("%@", @"Signaling GDTCORRegistrar that the app is terminating."); + GDTCORLogDebug(@"%@", @"Signaling GDTCORRegistrar that the app is terminating."); [[GDTCORRegistrar sharedInstance] appWillTerminate:application]; } } diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORPlatform.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORPlatform.m index 778ced9c0..d0fea4316 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORPlatform.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORPlatform.m @@ -14,10 +14,11 @@ * limitations under the License. */ -#import <GoogleDataTransport/GDTCORPlatform.h> +#import "GDTCORLibrary/Public/GDTCORPlatform.h" #import <GoogleDataTransport/GDTCORAssert.h> #import <GoogleDataTransport/GDTCORConsoleLogger.h> +#import <GoogleDataTransport/GDTCORReachability.h> #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" @@ -39,15 +40,211 @@ NSString *const kGDTCORApplicationWillEnterForegroundNotification = NSString *const kGDTCORApplicationWillTerminateNotification = @"GDTCORApplicationWillTerminateNotification"; + +NSURL *GDTCORRootDirectory(void) { + static NSURL *GDTPath; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *cachePath = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; + GDTPath = + [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/google-sdks-events", cachePath]]; + GDTCORLogDebug(@"GDT's state will be saved to: %@", GDTPath); + if (![[NSFileManager defaultManager] fileExistsAtPath:GDTPath.path]) { + NSError *error; + [[NSFileManager defaultManager] createDirectoryAtPath:GDTPath.path + withIntermediateDirectories:YES + attributes:nil + error:&error]; + GDTCORAssert(error == nil, @"There was an error creating GDT's path"); + } + }); + return GDTPath; +} + +BOOL GDTCORReachabilityFlagsReachable(GDTCORNetworkReachabilityFlags flags) { #if !TARGET_OS_WATCH -BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { + BOOL reachable = + (flags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable; + BOOL connectionRequired = (flags & kSCNetworkReachabilityFlagsConnectionRequired) == + kSCNetworkReachabilityFlagsConnectionRequired; + return reachable && !connectionRequired; +#else + return (flags & kGDTCORNetworkReachabilityFlagsReachable) == + kGDTCORNetworkReachabilityFlagsReachable; +#endif +} + +BOOL GDTCORReachabilityFlagsContainWWAN(GDTCORNetworkReachabilityFlags flags) { #if TARGET_OS_IOS return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN; #else + // Assume network connection not WWAN on macOS, tvOS, watchOS. return NO; #endif // TARGET_OS_IOS } -#endif // !TARGET_OS_WATCH + +GDTCORNetworkType GDTCORNetworkTypeMessage() { +#if !TARGET_OS_WATCH + SCNetworkReachabilityFlags reachabilityFlags = [GDTCORReachability currentFlags]; + if ((reachabilityFlags & kSCNetworkReachabilityFlagsReachable) == + kSCNetworkReachabilityFlagsReachable) { + if (GDTCORReachabilityFlagsContainWWAN(reachabilityFlags)) { + return GDTCORNetworkTypeMobile; + } else { + return GDTCORNetworkTypeWIFI; + } + } +#endif + return GDTCORNetworkTypeUNKNOWN; +} + +GDTCORNetworkMobileSubtype GDTCORNetworkMobileSubTypeMessage() { +#if TARGET_OS_IOS + static NSDictionary<NSString *, NSNumber *> *CTRadioAccessTechnologyToNetworkSubTypeMessage; + static CTTelephonyNetworkInfo *networkInfo; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + CTRadioAccessTechnologyToNetworkSubTypeMessage = @{ + CTRadioAccessTechnologyGPRS : @(GDTCORNetworkMobileSubtypeGPRS), + CTRadioAccessTechnologyEdge : @(GDTCORNetworkMobileSubtypeEdge), + CTRadioAccessTechnologyWCDMA : @(GDTCORNetworkMobileSubtypeWCDMA), + CTRadioAccessTechnologyHSDPA : @(GDTCORNetworkMobileSubtypeHSDPA), + CTRadioAccessTechnologyHSUPA : @(GDTCORNetworkMobileSubtypeHSUPA), + CTRadioAccessTechnologyCDMA1x : @(GDTCORNetworkMobileSubtypeCDMA1x), + CTRadioAccessTechnologyCDMAEVDORev0 : @(GDTCORNetworkMobileSubtypeCDMAEVDORev0), + CTRadioAccessTechnologyCDMAEVDORevA : @(GDTCORNetworkMobileSubtypeCDMAEVDORevA), + CTRadioAccessTechnologyCDMAEVDORevB : @(GDTCORNetworkMobileSubtypeCDMAEVDORevB), + CTRadioAccessTechnologyeHRPD : @(GDTCORNetworkMobileSubtypeHRPD), + CTRadioAccessTechnologyLTE : @(GDTCORNetworkMobileSubtypeLTE), + }; + networkInfo = [[CTTelephonyNetworkInfo alloc] init]; + }); + NSString *networkCurrentRadioAccessTechnology; +#if TARGET_OS_MACCATALYST + NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict = + networkInfo.serviceCurrentRadioAccessTechnology; + if (networkCurrentRadioAccessTechnologyDict.count) { + networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0]; + } +#else // TARGET_OS_MACCATALYST +#if defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 + if (@available(iOS 12.0, *)) { + NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict = + networkInfo.serviceCurrentRadioAccessTechnology; + if (networkCurrentRadioAccessTechnologyDict.count) { + // In iOS 12, multiple radio technologies can be captured. We prefer not particular radio + // tech to another, so we'll just return the first value in the dictionary. + networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0]; + } + } else { +#else // defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 + networkCurrentRadioAccessTechnology = networkInfo.currentRadioAccessTechnology; +#endif // // defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 + } +#endif // TARGET_OS_MACCATALYST + if (networkCurrentRadioAccessTechnology) { + NSNumber *networkMobileSubtype = + CTRadioAccessTechnologyToNetworkSubTypeMessage[networkCurrentRadioAccessTechnology]; + return networkMobileSubtype.intValue; + } else { + return GDTCORNetworkMobileSubtypeUNKNOWN; + } +#else + return GDTCORNetworkMobileSubtypeUNKNOWN; +#endif +} + +NSData *_Nullable GDTCOREncodeArchive(id<NSSecureCoding> obj, + NSString *archivePath, + NSError *_Nullable *error) { + NSData *resultData; +#if (defined(__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || \ + (defined(__MAC_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300) || \ + (defined(__TVOS_11_0) && __TV_OS_VERSION_MAX_ALLOWED >= 110000) || \ + (defined(__WATCHOS_4_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= 040000) || \ + (defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST) + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4, *)) { + resultData = [NSKeyedArchiver archivedDataWithRootObject:obj + requiringSecureCoding:YES + error:error]; + if (*error) { + GDTCORLogDebug(@"Encoding an object failed: %@", *error); + return nil; + } + if (archivePath) { + BOOL result = [resultData writeToFile:archivePath options:NSDataWritingAtomic error:error]; + if (result == NO || *error) { + GDTCORLogDebug(@"Attempt to write archive failed: URL:%@ error:%@", archivePath, *error); + } else { + GDTCORLogDebug(@"Writing archive succeeded: %@", archivePath); + } + } + } else { +#endif + BOOL result = NO; + @try { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + resultData = [NSKeyedArchiver archivedDataWithRootObject:obj]; +#pragma clang diagnostic pop + if (archivePath) { + result = [resultData writeToFile:archivePath options:NSDataWritingAtomic error:error]; + if (result == NO || *error) { + GDTCORLogDebug(@"Attempt to write archive failed: URL:%@ error:%@", archivePath, *error); + } else { + GDTCORLogDebug(@"Writing archive succeeded: %@", archivePath); + } + } + } @catch (NSException *exception) { + NSString *errorString = + [NSString stringWithFormat:@"An exception was thrown during encoding: %@", exception]; + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1 + userInfo:@{NSLocalizedFailureReasonErrorKey : errorString}]; + } + GDTCORLogDebug(@"Attempt to write archive. successful:%@ URL:%@ error:%@", + result ? @"YES" : @"NO", archivePath, *error); + } + return resultData; +} + +id<NSSecureCoding> _Nullable GDTCORDecodeArchive(Class archiveClass, + NSString *_Nullable archivePath, + NSData *_Nullable archiveData, + NSError *_Nullable *error) { + id<NSSecureCoding> unarchivedObject = nil; +#if (defined(__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || \ + (defined(__MAC_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300) || \ + (defined(__TVOS_11_0) && __TV_OS_VERSION_MAX_ALLOWED >= 110000) || \ + (defined(__WATCHOS_4_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= 040000) || \ + (defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST) + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4, *)) { + NSData *data = archiveData ? archiveData : [NSData dataWithContentsOfFile:archivePath]; + if (data) { + unarchivedObject = [NSKeyedUnarchiver unarchivedObjectOfClass:archiveClass + fromData:data + error:error]; + } + } else { +#endif + @try { + NSData *archivedData = + archiveData ? archiveData : [NSData dataWithContentsOfFile:archivePath]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData]; +#pragma clang diagnostic pop + } @catch (NSException *exception) { + NSString *errorString = + [NSString stringWithFormat:@"An exception was thrown during encoding: %@", exception]; + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1 + userInfo:@{NSLocalizedFailureReasonErrorKey : errorString}]; + } + } + return unarchivedObject; +} @interface GDTCORApplication () /** @@ -60,11 +257,21 @@ BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { @implementation GDTCORApplication +#if TARGET_OS_WATCH +/** A dispatch queue on which all task semaphores will populate and remove from + * gBackgroundIdentifierToSemaphoreMap. + */ +static dispatch_queue_t gSemaphoreQueue; + +/** For mapping backgroundIdentifier to task semaphore. */ +static NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *gBackgroundIdentifierToSemaphoreMap; +#endif + + (void)load { GDTCORLogDebug( - "%@", @"GDT is initializing. Please note that if you quit the app via the " - "debugger and not through a lifecycle event, event data will remain on disk but " - "storage won't have a reference to them since the singleton wasn't saved to disk."); + @"%@", @"GDT is initializing. Please note that if you quit the app via the " + "debugger and not through a lifecycle event, event data will remain on disk but " + "storage won't have a reference to them since the singleton wasn't saved to disk."); #if TARGET_OS_IOS || TARGET_OS_TV // If this asserts, please file a bug at https://github.com/firebase/firebase-ios-sdk/issues. GDTCORFatalAssert( @@ -74,6 +281,21 @@ BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { [self sharedApplication]; } ++ (void)initialize { +#if TARGET_OS_WATCH + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSemaphoreQueue = dispatch_queue_create("com.google.GDTCORApplication", DISPATCH_QUEUE_SERIAL); + GDTCORLogDebug( + @"%@", + @"GDTCORApplication is initializing on watchOS, gSemaphoreQueue has been initialized."); + gBackgroundIdentifierToSemaphoreMap = [[NSMutableDictionary alloc] init]; + GDTCORLogDebug(@"%@", @"GDTCORApplication is initializing on watchOS, " + @"gBackgroundIdentifierToSemaphoreMap has been initialized."); + }); +#endif +} + + (nullable GDTCORApplication *)sharedApplication { static GDTCORApplication *application; static dispatch_once_t onceToken; @@ -125,72 +347,179 @@ BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { selector:@selector(macOSApplicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil]; -#endif // TARGET_OS_IOS || TARGET_OS_TV + +#elif TARGET_OS_WATCH + // TODO: Notification on watchOS platform is currently posted by strings which are frangible. + // TODO: Needs improvements here. + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(iOSApplicationDidEnterBackground:) + name:@"UIApplicationDidEnterBackgroundNotification" + object:nil]; + [notificationCenter addObserver:self + selector:@selector(iOSApplicationWillEnterForeground:) + name:@"UIApplicationWillEnterForegroundNotification" + object:nil]; + + // Adds observers for app extension on watchOS platform + [notificationCenter addObserver:self + selector:@selector(iOSApplicationDidEnterBackground:) + name:NSExtensionHostDidEnterBackgroundNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(iOSApplicationWillEnterForeground:) + name:NSExtensionHostWillEnterForegroundNotification + object:nil]; +#endif } return self; } +#if TARGET_OS_WATCH +/** Generates and maps a unique background identifier to the given semaphore. + * + * @param semaphore The semaphore to map. + * @return A unique GDTCORBackgroundIdentifier mapped to the given semaphore. + */ ++ (GDTCORBackgroundIdentifier)createAndMapBackgroundIdentifierToSemaphore: + (dispatch_semaphore_t)semaphore { + __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; + dispatch_queue_t queue = gSemaphoreQueue; + NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *map = gBackgroundIdentifierToSemaphoreMap; + if (queue && map) { + dispatch_sync(queue, ^{ + bgID = arc4random(); + NSNumber *bgIDNumber = @(bgID); + while (bgID == GDTCORBackgroundIdentifierInvalid || map[bgIDNumber]) { + bgID = arc4random(); + bgIDNumber = @(bgID); + } + map[bgIDNumber] = semaphore; + }); + } + return bgID; +} + +/** Returns the semaphore mapped to given bgID and removes the value from the map. + * + * @param bgID The unique NSUInteger as GDTCORBackgroundIdentifier. + * @return The semaphore mapped by given bgID. + */ ++ (dispatch_semaphore_t)semaphoreForBackgroundIdentifier:(GDTCORBackgroundIdentifier)bgID { + __block dispatch_semaphore_t semaphore; + dispatch_queue_t queue = gSemaphoreQueue; + NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *map = gBackgroundIdentifierToSemaphoreMap; + NSNumber *bgIDNumber = @(bgID); + if (queue && map) { + dispatch_sync(queue, ^{ + semaphore = map[bgIDNumber]; + [map removeObjectForKey:bgIDNumber]; + }); + } + return semaphore; +} +#endif + - (GDTCORBackgroundIdentifier)beginBackgroundTaskWithName:(NSString *)name expirationHandler:(void (^)(void))handler { - GDTCORBackgroundIdentifier bgID = - [[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithName:name - expirationHandler:handler]; + __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; +#if !TARGET_OS_WATCH + bgID = [[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithName:name + expirationHandler:handler]; #if !NDEBUG if (bgID != GDTCORBackgroundIdentifierInvalid) { - GDTCORLogDebug("Creating background task with name:%@ bgID:%ld", name, (long)bgID); + GDTCORLogDebug(@"Creating background task with name:%@ bgID:%ld", name, (long)bgID); } #endif // !NDEBUG +#elif TARGET_OS_WATCH + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + bgID = [GDTCORApplication createAndMapBackgroundIdentifierToSemaphore:semaphore]; + if (bgID != GDTCORBackgroundIdentifierInvalid) { + GDTCORLogDebug(@"Creating activity with name:%@ bgID:%ld on watchOS.", name, (long)bgID); + } + [[self sharedNSProcessInfoForBackgroundTask] + performExpiringActivityWithReason:name + usingBlock:^(BOOL expired) { + if (expired) { + if (handler) { + handler(); + } + dispatch_semaphore_signal(semaphore); + GDTCORLogDebug( + @"Activity with name:%@ bgID:%ld on watchOS is expiring.", + name, (long)bgID); + } else { + dispatch_semaphore_wait( + semaphore, + dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); + } + }]; +#endif return bgID; } - (void)endBackgroundTask:(GDTCORBackgroundIdentifier)bgID { +#if !TARGET_OS_WATCH if (bgID != GDTCORBackgroundIdentifierInvalid) { - GDTCORLogDebug("Ending background task with ID:%ld was successful", (long)bgID); + GDTCORLogDebug(@"Ending background task with ID:%ld was successful", (long)bgID); [[self sharedApplicationForBackgroundTask] endBackgroundTask:bgID]; return; } +#elif TARGET_OS_WATCH + if (bgID != GDTCORBackgroundIdentifierInvalid) { + dispatch_semaphore_t semaphore = [GDTCORApplication semaphoreForBackgroundIdentifier:bgID]; + GDTCORLogDebug(@"Ending activity with bgID:%ld on watchOS.", (long)bgID); + if (semaphore) { + dispatch_semaphore_signal(semaphore); + GDTCORLogDebug(@"Signaling semaphore with bgID:%ld on watchOS.", (long)bgID); + } else { + GDTCORLogDebug(@"Semaphore with bgID:%ld is nil on watchOS.", (long)bgID); + } + } +#endif // !TARGET_OS_WATCH } #pragma mark - App environment helpers - (BOOL)isAppExtension { -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]; return appExtension; -#elif TARGET_OS_OSX - return NO; -#endif } -/** Returns a UIApplication instance if on the appropriate platform. +/** Returns a UIApplication or NSProcessInfo instance if on the appropriate platform. * - * @return The shared UIApplication if on the appropriate platform. + * @return The shared UIApplication or NSProcessInfo if on the appropriate platform. */ #if TARGET_OS_IOS || TARGET_OS_TV - (nullable UIApplication *)sharedApplicationForBackgroundTask { +#elif TARGET_OS_WATCH +- (nullable NSProcessInfo *)sharedNSProcessInfoForBackgroundTask { #else - (nullable id)sharedApplicationForBackgroundTask { #endif - if ([self isAppExtension]) { - return nil; + id sharedInstance = nil; +#if TARGET_OS_IOS || TARGET_OS_TV + if (![self isAppExtension]) { + Class uiApplicationClass = NSClassFromString(@"UIApplication"); + if (uiApplicationClass && + [uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) { + sharedInstance = [uiApplicationClass sharedApplication]; + } } - id sharedApplication = nil; - Class uiApplicationClass = NSClassFromString(@"UIApplication"); - if (uiApplicationClass && - [uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) { - sharedApplication = [uiApplicationClass sharedApplication]; - } - return sharedApplication; +#elif TARGET_OS_WATCH + sharedInstance = [NSProcessInfo processInfo]; +#endif + return sharedInstance; } -#pragma mark - UIApplicationDelegate +#pragma mark - UIApplicationDelegate and WKExtensionDelegate -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - (void)iOSApplicationDidEnterBackground:(NSNotification *)notif { _isRunningInBackground = YES; NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter]; - GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is backgrounding."); + GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is backgrounding."); [notifCenter postNotificationName:kGDTCORApplicationDidEnterBackgroundNotification object:nil]; } @@ -198,13 +527,17 @@ BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { _isRunningInBackground = NO; NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter]; - GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is foregrounding."); + GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is foregrounding."); [notifCenter postNotificationName:kGDTCORApplicationWillEnterForegroundNotification object:nil]; } +#endif // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +#pragma mark - UIApplicationDelegate + +#if TARGET_OS_IOS || TARGET_OS_TV - (void)iOSApplicationWillTerminate:(NSNotification *)notif { NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter]; - GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is terminating."); + GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is terminating."); [notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil]; } #endif // TARGET_OS_IOS || TARGET_OS_TV @@ -214,7 +547,7 @@ BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) { #if TARGET_OS_OSX - (void)macOSApplicationWillTerminate:(NSNotification *)notif { NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter]; - GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is terminating."); + GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is terminating."); [notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil]; } #endif // TARGET_OS_OSX diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORReachability.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORReachability.m index 1c6555a1b..8e8bf38b2 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORReachability.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORReachability.m @@ -14,9 +14,8 @@ * limitations under the License. */ -#import "GDTCORLibrary/Private/GDTCORReachability.h" +#import "GDTCORLibrary/Public/GDTCORReachability.h" #import "GDTCORLibrary/Private/GDTCORReachability_Private.h" -#if !TARGET_OS_WATCH #import <GoogleDataTransport/GDTCORConsoleLogger.h> @@ -28,22 +27,22 @@ * @param flags The new flag values. * @param info Any data that might be passed in by the callback. */ -static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability, - SCNetworkReachabilityFlags flags, +static void GDTCORReachabilityCallback(GDTCORNetworkReachabilityRef reachability, + GDTCORNetworkReachabilityFlags flags, void *info); @implementation GDTCORReachability { /** The reachability object. */ - SCNetworkReachabilityRef _reachabilityRef; + GDTCORNetworkReachabilityRef _reachabilityRef; /** The queue on which callbacks and all work will occur. */ dispatch_queue_t _reachabilityQueue; /** Flags specified by reachability callbacks. */ - SCNetworkConnectionFlags _callbackFlags; + GDTCORNetworkReachabilityFlags _callbackFlags; } -+ (void)load { ++ (void)initialize { [self sharedInstance]; } @@ -56,18 +55,24 @@ static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability, return sharedInstance; } -+ (SCNetworkReachabilityFlags)currentFlags { - __block SCNetworkReachabilityFlags currentFlags; ++ (GDTCORNetworkReachabilityFlags)currentFlags { + __block GDTCORNetworkReachabilityFlags currentFlags; +#if !TARGET_OS_WATCH dispatch_sync([GDTCORReachability sharedInstance] -> _reachabilityQueue, ^{ GDTCORReachability *reachability = [GDTCORReachability sharedInstance]; - currentFlags = reachability->_flags ? reachability->_flags : reachability->_callbackFlags; - GDTCORLogDebug("Initial reachability flags determined: %d", currentFlags); + currentFlags = + reachability->_callbackFlags ? reachability->_callbackFlags : reachability->_flags; + GDTCORLogDebug(@"Initial reachability flags determined: %d", currentFlags); }); +#else + currentFlags = kGDTCORNetworkReachabilityFlagsReachable; +#endif return currentFlags; } - (instancetype)init { self = [super init]; +#if !TARGET_OS_WATCH if (self) { struct sockaddr_in zeroAddress; bzero(&zeroAddress, sizeof(zeroAddress)); @@ -92,15 +97,16 @@ static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability, dispatch_async(_reachabilityQueue, ^{ Boolean valid = SCNetworkReachabilityGetFlags(self->_reachabilityRef, &self->_flags); if (!valid) { - GDTCORLogDebug("%@", @"Determining reachability failed."); + GDTCORLogDebug(@"%@", @"Determining reachability failed."); self->_flags = 0; } }); } +#endif return self; } -- (void)setCallbackFlags:(SCNetworkReachabilityFlags)flags { +- (void)setCallbackFlags:(GDTCORNetworkReachabilityFlags)flags { if (_callbackFlags != flags) { self->_callbackFlags = flags; } @@ -108,11 +114,12 @@ static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability, @end -static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability, - SCNetworkReachabilityFlags flags, +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +static void GDTCORReachabilityCallback(GDTCORNetworkReachabilityRef reachability, + GDTCORNetworkReachabilityFlags flags, void *info) { - GDTCORLogDebug("Reachability changed, new flags: %d", flags); +#pragma clang diagnostic pop + GDTCORLogDebug(@"Reachability changed, new flags: %d", flags); [[GDTCORReachability sharedInstance] setCallbackFlags:flags]; } - -#endif diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORRegistrar.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORRegistrar.m index 1440376f1..536da66b3 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORRegistrar.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORRegistrar.m @@ -17,7 +17,7 @@ #import "GDTCORLibrary/Public/GDTCORRegistrar.h" #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" -#import <GoogleDataTransport/GDTCORConsoleLogger.h> +#import "GDTCORLibrary/Public/GDTCORConsoleLogger.h" @implementation GDTCORRegistrar { /** Backing ivar for targetToUploader property. */ @@ -25,6 +25,9 @@ /** Backing ivar for targetToPrioritizer property. */ NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *_targetToPrioritizer; + + /** Backing ivar for targetToStorage property. */ + NSMutableDictionary<NSNumber *, id<GDTCORStorageProtocol>> *_targetToStorage; } + (instancetype)sharedInstance { @@ -42,6 +45,7 @@ _registrarQueue = dispatch_queue_create("com.google.GDTCORRegistrar", DISPATCH_QUEUE_SERIAL); _targetToPrioritizer = [[NSMutableDictionary alloc] init]; _targetToUploader = [[NSMutableDictionary alloc] init]; + _targetToStorage = [[NSMutableDictionary alloc] init]; } return self; } @@ -51,18 +55,29 @@ dispatch_async(_registrarQueue, ^{ GDTCORRegistrar *strongSelf = weakSelf; if (strongSelf) { - GDTCORLogDebug("Registered an uploader: %@ for target:%ld", backend, (long)target); + GDTCORLogDebug(@"Registered an uploader: %@ for target:%ld", backend, (long)target); strongSelf->_targetToUploader[@(target)] = backend; } }); } +- (void)registerStorage:(id<GDTCORStorageProtocol>)storage target:(GDTCORTarget)target { + __weak GDTCORRegistrar *weakSelf = self; + dispatch_async(_registrarQueue, ^{ + GDTCORRegistrar *strongSelf = weakSelf; + if (strongSelf) { + GDTCORLogDebug(@"Registered storage: %@ for target:%ld", storage, (long)target); + strongSelf->_targetToStorage[@(target)] = storage; + } + }); +} + - (void)registerPrioritizer:(id<GDTCORPrioritizer>)prioritizer target:(GDTCORTarget)target { __weak GDTCORRegistrar *weakSelf = self; dispatch_async(_registrarQueue, ^{ GDTCORRegistrar *strongSelf = weakSelf; if (strongSelf) { - GDTCORLogDebug("Registered a prioritizer: %@ for target:%ld", prioritizer, (long)target); + GDTCORLogDebug(@"Registered a prioritizer: %@ for target:%ld", prioritizer, (long)target); strongSelf->_targetToPrioritizer[@(target)] = prioritizer; } }); @@ -92,51 +107,81 @@ return targetToPrioritizer; } +- (NSMutableDictionary<NSNumber *, id<GDTCORStorageProtocol>> *)targetToStorage { + __block NSMutableDictionary<NSNumber *, id<GDTCORStorageProtocol>> *targetToStorage; + __weak GDTCORRegistrar *weakSelf = self; + dispatch_sync(_registrarQueue, ^{ + GDTCORRegistrar *strongSelf = weakSelf; + if (strongSelf) { + targetToStorage = strongSelf->_targetToStorage; + } + }); + return targetToStorage; +} + #pragma mark - GDTCORLifecycleProtocol - (void)appWillBackground:(nonnull GDTCORApplication *)app { - dispatch_async(_registrarQueue, ^{ - for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) { - if ([uploader respondsToSelector:@selector(appWillBackground:)]) { - [uploader appWillBackground:app]; - } + NSArray<id<GDTCORUploader>> *uploaders = [self.targetToUploader allValues]; + for (id<GDTCORUploader> uploader in uploaders) { + if ([uploader respondsToSelector:@selector(appWillBackground:)]) { + [uploader appWillBackground:app]; } - for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) { - if ([prioritizer respondsToSelector:@selector(appWillBackground:)]) { - [prioritizer appWillBackground:app]; - } + } + NSArray<id<GDTCORPrioritizer>> *prioritizers = [self.targetToPrioritizer allValues]; + for (id<GDTCORPrioritizer> prioritizer in prioritizers) { + if ([prioritizer respondsToSelector:@selector(appWillBackground:)]) { + [prioritizer appWillBackground:app]; } - }); + } + NSArray<id<GDTCORStorageProtocol>> *storages = [self.targetToStorage allValues]; + for (id<GDTCORStorageProtocol> storage in storages) { + if ([storage respondsToSelector:@selector(appWillBackground:)]) { + [storage appWillBackground:app]; + } + } } - (void)appWillForeground:(nonnull GDTCORApplication *)app { - dispatch_async(_registrarQueue, ^{ - for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) { - if ([uploader respondsToSelector:@selector(appWillForeground:)]) { - [uploader appWillForeground:app]; - } + NSArray<id<GDTCORUploader>> *uploaders = [self.targetToUploader allValues]; + for (id<GDTCORUploader> uploader in uploaders) { + if ([uploader respondsToSelector:@selector(appWillForeground:)]) { + [uploader appWillForeground:app]; } - for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) { - if ([prioritizer respondsToSelector:@selector(appWillForeground:)]) { - [prioritizer appWillForeground:app]; - } + } + NSArray<id<GDTCORPrioritizer>> *prioritizers = [self.targetToPrioritizer allValues]; + for (id<GDTCORPrioritizer> prioritizer in prioritizers) { + if ([prioritizer respondsToSelector:@selector(appWillForeground:)]) { + [prioritizer appWillForeground:app]; } - }); + } + NSArray<id<GDTCORStorageProtocol>> *storages = [self.targetToStorage allValues]; + for (id<GDTCORStorageProtocol> storage in storages) { + if ([storage respondsToSelector:@selector(appWillForeground:)]) { + [storage appWillForeground:app]; + } + } } - (void)appWillTerminate:(nonnull GDTCORApplication *)app { - dispatch_sync(_registrarQueue, ^{ - for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) { - if ([uploader respondsToSelector:@selector(appWillTerminate:)]) { - [uploader appWillTerminate:app]; - } + NSArray<id<GDTCORUploader>> *uploaders = [self.targetToUploader allValues]; + for (id<GDTCORUploader> uploader in uploaders) { + if ([uploader respondsToSelector:@selector(appWillTerminate:)]) { + [uploader appWillTerminate:app]; } - for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) { - if ([prioritizer respondsToSelector:@selector(appWillTerminate:)]) { - [prioritizer appWillTerminate:app]; - } + } + NSArray<id<GDTCORPrioritizer>> *prioritizers = [self.targetToPrioritizer allValues]; + for (id<GDTCORPrioritizer> prioritizer in prioritizers) { + if ([prioritizer respondsToSelector:@selector(appWillTerminate:)]) { + [prioritizer appWillTerminate:app]; } - }); + } + NSArray<id<GDTCORStorageProtocol>> *storages = [self.targetToStorage allValues]; + for (id<GDTCORStorageProtocol> storage in storages) { + if ([storage respondsToSelector:@selector(appWillTerminate:)]) { + [storage appWillTerminate:app]; + } + } } @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStorage.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStorage.m deleted file mode 100644 index 23a5ee7ae..000000000 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStorage.m +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2018 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "GDTCORLibrary/Private/GDTCORStorage.h" -#import "GDTCORLibrary/Private/GDTCORStorage_Private.h" - -#import <GoogleDataTransport/GDTCORAssert.h> -#import <GoogleDataTransport/GDTCORConsoleLogger.h> -#import <GoogleDataTransport/GDTCORLifecycle.h> -#import <GoogleDataTransport/GDTCORPrioritizer.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> - -#import "GDTCORLibrary/Private/GDTCOREvent_Private.h" -#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" -#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h" - -/** Creates and/or returns a singleton NSString that is the shared storage path. - * - * @return The SDK event storage path. - */ -static NSString *GDTCORStoragePath() { - static NSString *storagePath; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSString *cachePath = - NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; - storagePath = [NSString stringWithFormat:@"%@/google-sdks-events", cachePath]; - GDTCORLogDebug("Events will be saved to %@", storagePath); - }); - return storagePath; -} - -@implementation GDTCORStorage - -+ (NSString *)archivePath { - static NSString *archivePath; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - archivePath = [GDTCORStoragePath() stringByAppendingPathComponent:@"GDTCORStorageArchive"]; - }); - return archivePath; -} - -+ (instancetype)sharedInstance { - static GDTCORStorage *sharedStorage; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedStorage = [[GDTCORStorage alloc] init]; - }); - return sharedStorage; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _storageQueue = dispatch_queue_create("com.google.GDTCORStorage", DISPATCH_QUEUE_SERIAL); - _targetToEventSet = [[NSMutableDictionary alloc] init]; - _storedEvents = [[NSMutableOrderedSet alloc] init]; - _uploadCoordinator = [GDTCORUploadCoordinator sharedInstance]; - } - return self; -} - -- (void)storeEvent:(GDTCOREvent *)event { - GDTCORLogDebug("Saving event: %@", event); - if (event == nil) { - GDTCORLogDebug("%@", @"The event was nil, so it was not saved."); - return; - } - - [self createEventDirectoryIfNotExists]; - - __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; - bgID = [[GDTCORApplication sharedApplication] - beginBackgroundTaskWithName:@"GDTStorage" - expirationHandler:^{ - // End the background task if it's still valid. - [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; - bgID = GDTCORBackgroundIdentifierInvalid; - }]; - - dispatch_async(_storageQueue, ^{ - // Check that a backend implementation is available for this target. - NSInteger target = event.target; - - // Check that a prioritizer is available for this target. - id<GDTCORPrioritizer> prioritizer = - [GDTCORRegistrar sharedInstance].targetToPrioritizer[@(target)]; - GDTCORAssert(prioritizer, @"There's no prioritizer registered for the given target. Are you " - @"sure you've added the support library for the backend you need?"); - - // Write the transport bytes to disk, get a filename. - GDTCORAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes"); - NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes - eventHash:event.hash]; - GDTCORLogDebug("Event saved to disk: %@", eventFile); - GDTCORDataFuture *dataFuture = [[GDTCORDataFuture alloc] initWithFileURL:eventFile]; - GDTCORStoredEvent *storedEvent = [event storedEventWithDataFuture:dataFuture]; - - // Add event to tracking collections. - [self addEventToTrackingCollections:storedEvent]; - - // Have the prioritizer prioritize the event. - [prioritizer prioritizeEvent:storedEvent]; - - // Check the QoS, if it's high priority, notify the target that it has a high priority event. - if (event.qosTier == GDTCOREventQoSFast) { - [self.uploadCoordinator forceUploadForTarget:target]; - } - - // Write state to disk if we're in the background. - if ([[GDTCORApplication sharedApplication] isRunningInBackground]) { - GDTCORLogDebug("%@", @"Saving storage state because the app is running in the background"); - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self - requiringSecureCoding:YES - error:&error]; - [data writeToFile:[GDTCORStorage archivePath] atomically:YES]; - } else { -#if !TARGET_OS_MACCATALYST && !TARGET_OS_WATCH - [NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]]; -#endif - } - } - - // Cancel or end the associated background task if it's still valid. - [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; - bgID = GDTCORBackgroundIdentifierInvalid; - GDTCORLogDebug("Event %@ is stored. There are %ld events stored on disk", event, - (unsigned long)self->_storedEvents.count); - }); -} - -- (void)removeEvents:(NSSet<GDTCORStoredEvent *> *)events { - NSSet<GDTCORStoredEvent *> *eventsToRemove = [events copy]; - dispatch_async(_storageQueue, ^{ - for (GDTCORStoredEvent *event in eventsToRemove) { - // Remove from disk, first and foremost. - NSError *error; - if (event.dataFuture.fileURL) { - NSURL *fileURL = event.dataFuture.fileURL; - [[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error]; - GDTCORAssert(error == nil, @"There was an error removing an event file: %@", error); - GDTCORLogDebug("Removed event from disk: %@", fileURL); - } - - // Remove from the tracking collections. - [self.storedEvents removeObject:event]; - [self.targetToEventSet[event.target] removeObject:event]; - } - }); -} - -#pragma mark - Private helper methods - -/** Creates the storage directory if it does not exist. */ -- (void)createEventDirectoryIfNotExists { - NSError *error; - BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:GDTCORStoragePath() - withIntermediateDirectories:YES - attributes:0 - error:&error]; - if (!result || error) { - GDTCORLogError(GDTCORMCEDirectoryCreationError, @"Error creating the directory: %@", error); - } -} - -/** Saves the event's dataObjectTransportBytes to a file using NSData mechanisms. - * - * @note This method should only be called from a method within a block on _storageQueue to maintain - * thread safety. - * - * @param transportBytes The transport bytes of the event. - * @param eventHash The hash value of the event. - * @return The filename - */ -- (NSURL *)saveEventBytesToDisk:(NSData *)transportBytes eventHash:(NSUInteger)eventHash { - NSString *storagePath = GDTCORStoragePath(); - NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash]; - NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]]; - - GDTCORAssert(![[NSFileManager defaultManager] fileExistsAtPath:eventFilePath.path], - @"An event shouldn't already exist at this path: %@", eventFilePath.path); - - BOOL writingSuccess = [transportBytes writeToURL:eventFilePath atomically:YES]; - if (!writingSuccess) { - GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@", - eventFilePath); - } - - return eventFilePath; -} - -/** Adds the event to internal tracking collections. - * - * @note This method should only be called from a method within a block on _storageQueue to maintain - * thread safety. - * - * @param event The event to track. - */ -- (void)addEventToTrackingCollections:(GDTCORStoredEvent *)event { - [_storedEvents addObject:event]; - NSMutableSet<GDTCORStoredEvent *> *events = self.targetToEventSet[event.target]; - events = events ? events : [[NSMutableSet alloc] init]; - [events addObject:event]; - _targetToEventSet[event.target] = events; -} - -#pragma mark - GDTCORLifecycleProtocol - -- (void)appWillForeground:(GDTCORApplication *)app { - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - NSData *data = [NSData dataWithContentsOfFile:[GDTCORStorage archivePath]]; - if (data) { - [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTCORStorage class] fromData:data error:&error]; - } - } else { -#if !TARGET_OS_MACCATALYST && !TARGET_OS_WATCH - [NSKeyedUnarchiver unarchiveObjectWithFile:[GDTCORStorage archivePath]]; -#endif - } -} - -- (void)appWillBackground:(GDTCORApplication *)app { - dispatch_async(_storageQueue, ^{ - // Immediately request a background task to run until the end of the current queue of work, and - // cancel it once the work is done. - __block GDTCORBackgroundIdentifier bgID = - [app beginBackgroundTaskWithName:@"GDTStorage" - expirationHandler:^{ - [app endBackgroundTask:bgID]; - bgID = GDTCORBackgroundIdentifierInvalid; - }]; - - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self - requiringSecureCoding:YES - error:&error]; - [data writeToFile:[GDTCORStorage archivePath] atomically:YES]; - } else { -#if !TARGET_OS_MACCATALYST && !TARGET_OS_WATCH - [NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]]; -#endif - } - - // End the background task if it's still valid. - [app endBackgroundTask:bgID]; - bgID = GDTCORBackgroundIdentifierInvalid; - }); -} - -- (void)appWillTerminate:(GDTCORApplication *)application { - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self - requiringSecureCoding:YES - error:&error]; - [data writeToFile:[GDTCORStorage archivePath] atomically:YES]; - } else { -#if !TARGET_OS_MACCATALYST && !TARGET_OS_WATCH - [NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]]; -#endif - } -} - -#pragma mark - NSSecureCoding - -/** The NSKeyedCoder key for the storedEvents property. */ -static NSString *const kGDTCORStorageStoredEventsKey = @"GDTCORStorageStoredEventsKey"; - -/** The NSKeyedCoder key for the targetToEventSet property. */ -static NSString *const kGDTCORStorageTargetToEventSetKey = @"GDTCORStorageTargetToEventSetKey"; - -/** The NSKeyedCoder key for the uploadCoordinator property. */ -static NSString *const kGDTCORStorageUploadCoordinatorKey = @"GDTCORStorageUploadCoordinatorKey"; - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - // Create the singleton and populate its ivars. - GDTCORStorage *sharedInstance = [self.class sharedInstance]; - dispatch_sync(sharedInstance.storageQueue, ^{ - NSSet *classes = - [NSSet setWithObjects:[NSMutableOrderedSet class], [GDTCORStoredEvent class], nil]; - sharedInstance->_storedEvents = [aDecoder decodeObjectOfClasses:classes - forKey:kGDTCORStorageStoredEventsKey]; - classes = [NSSet setWithObjects:[NSMutableDictionary class], [NSMutableSet class], - [GDTCORStoredEvent class], nil]; - sharedInstance->_targetToEventSet = - [aDecoder decodeObjectOfClasses:classes forKey:kGDTCORStorageTargetToEventSetKey]; - sharedInstance->_uploadCoordinator = - [aDecoder decodeObjectOfClass:[GDTCORUploadCoordinator class] - forKey:kGDTCORStorageUploadCoordinatorKey]; - }); - return sharedInstance; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - GDTCORStorage *sharedInstance = [self.class sharedInstance]; - NSMutableOrderedSet<GDTCORStoredEvent *> *storedEvents = sharedInstance->_storedEvents; - if (storedEvents) { - [aCoder encodeObject:storedEvents forKey:kGDTCORStorageStoredEventsKey]; - } - NSMutableDictionary<NSNumber *, NSMutableSet<GDTCORStoredEvent *> *> *targetToEventSet = - sharedInstance->_targetToEventSet; - if (targetToEventSet) { - [aCoder encodeObject:targetToEventSet forKey:kGDTCORStorageTargetToEventSetKey]; - } - GDTCORUploadCoordinator *uploadCoordinator = sharedInstance->_uploadCoordinator; - if (uploadCoordinator) { - [aCoder encodeObject:uploadCoordinator forKey:kGDTCORStorageUploadCoordinatorKey]; - } -} - -@end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStoredEvent.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStoredEvent.m deleted file mode 100644 index a24339472..000000000 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORStoredEvent.m +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <GoogleDataTransport/GDTCORStoredEvent.h> - -#import <GoogleDataTransport/GDTCORClock.h> - -#import "GDTCORLibrary/Private/GDTCORStorage_Private.h" - -@implementation GDTCORStoredEvent - -- (instancetype)initWithEvent:(GDTCOREvent *)event - dataFuture:(nonnull GDTCORDataFuture *)dataFuture { - self = [super init]; - if (self) { - _dataFuture = dataFuture; - _mappingID = event.mappingID; - _target = @(event.target); - _qosTier = event.qosTier; - _clockSnapshot = event.clockSnapshot; - _customPrioritizationParams = event.customPrioritizationParams; - } - return self; -} - -#pragma mark - NSSecureCoding - -/** Coding key for the dataFuture ivar. */ -static NSString *kDataFutureKey = @"GDTCORStoredEventDataFutureKey"; - -/** Coding key for mappingID ivar. */ -static NSString *kMappingIDKey = @"GDTCORStoredEventMappingIDKey"; - -/** Coding key for target ivar. */ -static NSString *kTargetKey = @"GDTCORStoredEventTargetKey"; - -/** Coding key for qosTier ivar. */ -static NSString *kQosTierKey = @"GDTCORStoredEventQosTierKey"; - -/** Coding key for clockSnapshot ivar. */ -static NSString *kClockSnapshotKey = @"GDTCORStoredEventClockSnapshotKey"; - -/** Coding key for customPrioritizationParams ivar. */ -static NSString *kCustomPrioritizationParamsKey = @"GDTCORStoredEventcustomPrioritizationParamsKey"; - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (void)encodeWithCoder:(nonnull NSCoder *)aCoder { - [aCoder encodeObject:_dataFuture forKey:kDataFutureKey]; - [aCoder encodeObject:_mappingID forKey:kMappingIDKey]; - [aCoder encodeObject:_target forKey:kTargetKey]; - [aCoder encodeObject:@(_qosTier) forKey:kQosTierKey]; - [aCoder encodeObject:_clockSnapshot forKey:kClockSnapshotKey]; - [aCoder encodeObject:_customPrioritizationParams forKey:kCustomPrioritizationParamsKey]; -} - -- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder { - self = [self init]; - if (self) { - _dataFuture = [aDecoder decodeObjectOfClass:[GDTCORDataFuture class] forKey:kDataFutureKey]; - _mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kMappingIDKey]; - _target = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kTargetKey]; - NSNumber *qosTier = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kQosTierKey]; - _qosTier = [qosTier intValue]; - _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:kClockSnapshotKey]; - _customPrioritizationParams = [aDecoder decodeObjectOfClass:[NSDictionary class] - forKey:kCustomPrioritizationParamsKey]; - } - return self; -} - -- (BOOL)isEqual:(GDTCORStoredEvent *)other { - return [self hash] == [other hash]; -} - -- (NSUInteger)hash { - return [_dataFuture hash] ^ [_mappingID hash] ^ [_target hash] ^ [_clockSnapshot hash] ^ _qosTier; -} - -@end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransformer.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransformer.m index f89204014..35f456e09 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransformer.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransformer.m @@ -22,8 +22,10 @@ #import <GoogleDataTransport/GDTCOREvent.h> #import <GoogleDataTransport/GDTCOREventTransformer.h> #import <GoogleDataTransport/GDTCORLifecycle.h> +#import <GoogleDataTransport/GDTCORStorageProtocol.h> -#import "GDTCORLibrary/Private/GDTCORStorage.h" +#import "GDTCORLibrary/Private/GDTCOREvent_Private.h" +#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" @implementation GDTCORTransformer @@ -41,14 +43,19 @@ if (self) { _eventWritingQueue = dispatch_queue_create("com.google.GDTCORTransformer", DISPATCH_QUEUE_SERIAL); - _storageInstance = [GDTCORStorage sharedInstance]; } return self; } - (void)transformEvent:(GDTCOREvent *)event - withTransformers:(NSArray<id<GDTCOREventTransformer>> *)transformers { + withTransformers:(NSArray<id<GDTCOREventTransformer>> *)transformers + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion { GDTCORAssert(event, @"You can't write a nil event"); + BOOL hadOriginalCompletion = completion != nil; + if (!completion) { + completion = ^(BOOL wasWritten, NSError *_Nullable error) { + }; + } __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; bgID = [[GDTCORApplication sharedApplication] @@ -61,18 +68,24 @@ GDTCOREvent *transformedEvent = event; for (id<GDTCOREventTransformer> transformer in transformers) { if ([transformer respondsToSelector:@selector(transform:)]) { - GDTCORLogDebug("Applying a transformer to event %@", event); + GDTCORLogDebug(@"Applying a transformer to event %@", event); transformedEvent = [transformer transform:transformedEvent]; if (!transformedEvent) { + completion(NO, nil); return; } } else { GDTCORLogError(GDTCORMCETransformerDoesntImplementTransform, @"Transformer doesn't implement transform: %@", transformer); + completion(NO, nil); return; } } - [self.storageInstance storeEvent:transformedEvent]; + + id<GDTCORStorageProtocol> storage = + [GDTCORRegistrar sharedInstance].targetToStorage[@(event.target)]; + + [storage storeEvent:transformedEvent onComplete:hadOriginalCompletion ? completion : nil]; // The work is done, cancel the background task if it's valid. [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransport.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransport.m index b095c8593..6c6af23fe 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransport.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORTransport.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import <GoogleDataTransport/GDTCORTransport.h> +#import "GDTCORLibrary/Public/GDTCORTransport.h" #import "GDTCORLibrary/Private/GDTCORTransport_Private.h" #import <GoogleDataTransport/GDTCORAssert.h> @@ -41,33 +41,52 @@ _target = target; _transformerInstance = [GDTCORTransformer sharedInstance]; } - GDTCORLogDebug("Transport object created. mappingID:%@ transformers:%@ target:%ld", _mappingID, - _transformers, (long)_target); + GDTCORLogDebug(@"Transport object created. mappingID:%@ transformers:%@ target:%ld", mappingID, + transformers, (long)target); return self; } +- (void)sendTelemetryEvent:(GDTCOREvent *)event + onComplete: + (void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion { + event.qosTier = GDTCOREventQoSTelemetry; + [self sendEvent:event onComplete:completion]; +} + +- (void)sendDataEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion { + GDTCORAssert(event.qosTier != GDTCOREventQoSTelemetry, @"Use -sendTelemetryEvent, please."); + [self sendEvent:event onComplete:completion]; +} + - (void)sendTelemetryEvent:(GDTCOREvent *)event { - // TODO: Determine if sending an event before registration is allowed. - GDTCORAssert(event, @"You can't send a nil event"); - GDTCOREvent *copiedEvent = [event copy]; - copiedEvent.qosTier = GDTCOREventQoSTelemetry; - copiedEvent.clockSnapshot = [GDTCORClock snapshot]; - [self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers]; - GDTCORLogDebug("Telemetry event sent: %@", event); + [self sendTelemetryEvent:event onComplete:nil]; } - (void)sendDataEvent:(GDTCOREvent *)event { - // TODO: Determine if sending an event before registration is allowed. - GDTCORAssert(event, @"You can't send a nil event"); - GDTCORAssert(event.qosTier != GDTCOREventQoSTelemetry, @"Use -sendTelemetryEvent, please."); - GDTCOREvent *copiedEvent = [event copy]; - copiedEvent.clockSnapshot = [GDTCORClock snapshot]; - [self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers]; - GDTCORLogDebug("Data event sent: %@", event); + [self sendDataEvent:event onComplete:nil]; } - (GDTCOREvent *)eventForTransport { return [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target]; } +#pragma mark - Private helper methods + +/** Sends the given event through the transport pipeline. + * + * @param event The event to send. + * @param completion A block that will be called when the event has been written or dropped. + */ +- (void)sendEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion { + // TODO: Determine if sending an event before registration is allowed. + GDTCORAssert(event, @"You can't send a nil event"); + GDTCOREvent *copiedEvent = [event copy]; + copiedEvent.clockSnapshot = [GDTCORClock snapshot]; + [self.transformerInstance transformEvent:copiedEvent + withTransformers:_transformers + onComplete:completion]; +} + @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadCoordinator.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadCoordinator.m index 0dbc1b48f..e2d2d2065 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadCoordinator.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadCoordinator.m @@ -19,10 +19,9 @@ #import <GoogleDataTransport/GDTCORAssert.h> #import <GoogleDataTransport/GDTCORClock.h> #import <GoogleDataTransport/GDTCORConsoleLogger.h> +#import <GoogleDataTransport/GDTCORReachability.h> -#import "GDTCORLibrary/Private/GDTCORReachability.h" #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" -#import "GDTCORLibrary/Private/GDTCORStorage.h" @implementation GDTCORUploadCoordinator @@ -51,24 +50,13 @@ - (void)forceUploadForTarget:(GDTCORTarget)target { dispatch_async(_coordinationQueue, ^{ - GDTCORLogDebug("Forcing an upload of target %ld", (long)target); + GDTCORLogDebug(@"Forcing an upload of target %ld", (long)target); GDTCORUploadConditions conditions = [self uploadConditions]; conditions |= GDTCORUploadConditionHighPriority; [self uploadTargets:@[ @(target) ] conditions:conditions]; }); } -#pragma mark - Property overrides - -// GDTCORStorage and GDTCORUploadCoordinator +sharedInstance methods call each other, so this breaks -// the loop. -- (GDTCORStorage *)storage { - if (!_storage) { - _storage = [GDTCORStorage sharedInstance]; - } - return _storage; -} - #pragma mark - Private helper methods /** Starts a timer that checks whether or not events can be uploaded at regular intervals. It will @@ -83,11 +71,11 @@ dispatch_source_set_event_handler(self->_timer, ^{ if (![[GDTCORApplication sharedApplication] isRunningInBackground]) { GDTCORUploadConditions conditions = [self uploadConditions]; - GDTCORLogDebug("%@", @"Upload timer fired"); + GDTCORLogDebug(@"%@", @"Upload timer fired"); [self uploadTargets:[self.registrar.targetToUploader allKeys] conditions:conditions]; } }); - GDTCORLogDebug("%@", @"Upload timer started"); + GDTCORLogDebug(@"%@", @"Upload timer started"); dispatch_resume(self->_timer); }); } @@ -112,54 +100,56 @@ for (NSNumber *target in targets) { // Don't trigger uploads for targets that have an in-flight package already. if (self->_targetToInFlightPackages[target]) { - GDTCORLogDebug("Target %@ will not upload, there's an upload in flight", target); + GDTCORLogDebug(@"Target %@ will not upload, there's an upload in flight", target); continue; } // Ask the uploader if they can upload and do so, if it can. id<GDTCORUploader> uploader = self.registrar.targetToUploader[target]; - if ([uploader readyToUploadWithConditions:conditions]) { + if ([uploader readyToUploadTarget:target.intValue conditions:conditions]) { id<GDTCORPrioritizer> prioritizer = self.registrar.targetToPrioritizer[target]; - GDTCORUploadPackage *package = [prioritizer uploadPackageWithConditions:conditions]; + GDTCORUploadPackage *package = [prioritizer uploadPackageWithTarget:target.intValue + conditions:conditions]; if (package.events.count) { self->_targetToInFlightPackages[target] = package; - GDTCORLogDebug("Package of %ld events is being handed over to an uploader", + GDTCORLogDebug(@"Package of %ld events is being handed over to an uploader", (long)package.events.count); [uploader uploadPackage:package]; } else { [package completeDelivery]; } } - GDTCORLogDebug("Target %@ is not ready to upload", target); + GDTCORLogDebug(@"Target %@ is not ready to upload", target); } }); } +/** Returns the registered storage for the given NSNumber wrapped GDTCORTarget. + * + * @param target The NSNumber wrapping of a GDTCORTarget to find the storage instance of. + * @return The storage instance for the given target. + */ +- (nullable id<GDTCORStorageProtocol>)storageForTarget:(NSNumber *)target { + id<GDTCORStorageProtocol> storage = [GDTCORRegistrar sharedInstance].targetToStorage[target]; + GDTCORAssert(storage, @"A storage must be registered for target %@", target); + return storage; +} + /** Returns the current upload conditions after making determinations about the network connection. * * @return The current upload conditions. */ - (GDTCORUploadConditions)uploadConditions { -#if TARGET_OS_WATCH - return GDTCORUploadConditionNoNetwork; -#else - SCNetworkReachabilityFlags currentFlags = [GDTCORReachability currentFlags]; - BOOL reachable = - (currentFlags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable; - BOOL connectionRequired = (currentFlags & kSCNetworkReachabilityFlagsConnectionRequired) == - kSCNetworkReachabilityFlagsConnectionRequired; - BOOL networkConnected = reachable && !connectionRequired; - + GDTCORNetworkReachabilityFlags currentFlags = [GDTCORReachability currentFlags]; + BOOL networkConnected = GDTCORReachabilityFlagsReachable(currentFlags); if (!networkConnected) { return GDTCORUploadConditionNoNetwork; } - BOOL isWWAN = GDTCORReachabilityFlagsContainWWAN(currentFlags); if (isWWAN) { return GDTCORUploadConditionMobileData; } else { return GDTCORUploadConditionWifiData; } -#endif } #pragma mark - NSSecureCoding support @@ -176,9 +166,10 @@ static NSString *const ktargetToInFlightPackagesKey = GDTCORUploadCoordinator *sharedCoordinator = [GDTCORUploadCoordinator sharedInstance]; dispatch_sync(sharedCoordinator->_coordinationQueue, ^{ @try { + NSSet *classes = + [NSSet setWithObjects:[NSMutableDictionary class], [GDTCORUploadPackage class], nil]; sharedCoordinator->_targetToInFlightPackages = - [aDecoder decodeObjectOfClass:[NSMutableDictionary class] - forKey:ktargetToInFlightPackagesKey]; + [aDecoder decodeObjectOfClasses:classes forKey:ktargetToInFlightPackagesKey]; } @catch (NSException *exception) { sharedCoordinator->_targetToInFlightPackages = [NSMutableDictionary dictionary]; @@ -200,13 +191,14 @@ static NSString *const ktargetToInFlightPackagesKey = #pragma mark - GDTCORLifecycleProtocol - (void)appWillForeground:(GDTCORApplication *)app { - // Not entirely thread-safe, but it should be fine. + // -startTimer is thread-safe. [self startTimer]; } - (void)appWillBackground:(GDTCORApplication *)app { - // Should be thread-safe. If it ends up not being, put this in a dispatch_sync. - [self stopTimer]; + dispatch_sync(_coordinationQueue, ^{ + [self stopTimer]; + }); } - (void)appWillTerminate:(GDTCORApplication *)application { @@ -229,6 +221,7 @@ static NSString *const ktargetToInFlightPackagesKey = if (targetToInFlightPackages) { [targetToInFlightPackages removeObjectForKey:targetNumber]; } + NSSet<GDTCOREvent *> *packageEvents = [package.events copy]; if (registrar) { id<GDTCORPrioritizer> prioritizer = registrar.targetToPrioritizer[targetNumber]; if (!prioritizer) { @@ -236,11 +229,20 @@ static NSString *const ktargetToInFlightPackagesKey = @"A prioritizer should be registered for this target: %@", targetNumber); } if ([prioritizer respondsToSelector:@selector(packageDelivered:successful:)]) { - [prioritizer packageDelivered:package successful:successful]; + [prioritizer packageDelivered:[package copy] successful:successful]; } } - if (package.events != nil) { - [self.storage removeEvents:package.events]; + if (successful && packageEvents.count) { + NSMutableSet *eventIDs = [[NSMutableSet alloc] init]; + for (GDTCOREvent *event in packageEvents) { + NSNumber *eventID = event.eventID; + if (eventID != nil) { + [eventIDs addObject:eventID]; + } else { + GDTCORLogDebug(@"An event was missing its ID: %@", event); + } + } + [[self storageForTarget:@(package.target)] removeEvents:eventIDs]; } }); } diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadPackage.m b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadPackage.m index 6ac5f4f47..38b7a7c59 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadPackage.m +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/GDTCORUploadPackage.m @@ -14,16 +14,39 @@ * limitations under the License. */ -#import <GoogleDataTransport/GDTCORUploadPackage.h> +#import "GDTCORLibrary/Public/GDTCORUploadPackage.h" #import <GoogleDataTransport/GDTCORClock.h> #import <GoogleDataTransport/GDTCORConsoleLogger.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> -#import "GDTCORLibrary/Private/GDTCORStorage_Private.h" +#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h" #import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h" #import "GDTCORLibrary/Private/GDTCORUploadPackage_Private.h" +/** A class that holds a weak reference to an upload package, for use by the package's NSTimer. */ +@interface GDTCORUploadPackageTimerHolder : NSObject + +/** The upload package. */ +@property(weak, nonatomic) GDTCORUploadPackage *package; + +@end + +@implementation GDTCORUploadPackageTimerHolder + +/** Calls checkIfPackageIsExpired on the package if non-nil. Invalidates if the package is nil. + * + * @param timer The timer instance calling this method. + */ +- (void)timerFired:(NSTimer *)timer { + if (_package) { + [_package checkIfPackageIsExpired]; + } else { + [timer invalidate]; + } +} + +@end + @implementation GDTCORUploadPackage { /** If YES, the package's -completeDelivery method has been called. */ BOOL _isDelivered; @@ -39,23 +62,36 @@ self = [super init]; if (self) { _target = target; - _storage = [GDTCORStorage sharedInstance]; + _storage = [GDTCORRegistrar sharedInstance].targetToStorage[@(target)]; _deliverByTime = [GDTCORClock clockSnapshotInTheFuture:180000]; _handler = [GDTCORUploadCoordinator sharedInstance]; + GDTCORUploadPackageTimerHolder *holder = [[GDTCORUploadPackageTimerHolder alloc] init]; + holder.package = self; _expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 - target:self - selector:@selector(checkIfPackageIsExpired:) + target:holder + selector:@selector(timerFired:) userInfo:nil repeats:YES]; } - GDTCORLogDebug("Upload package created %@", self); + GDTCORLogDebug(@"Upload package created %@", self); return self; } - (instancetype)copy { - GDTCORUploadPackage *newPackage = [[GDTCORUploadPackage alloc] initWithTarget:_target]; + GDTCORUploadPackage *newPackage = [[GDTCORUploadPackage alloc] init]; + newPackage->_target = _target; + newPackage->_storage = _storage; + newPackage->_deliverByTime = _deliverByTime; + newPackage->_handler = _handler; + GDTCORUploadPackageTimerHolder *holder = [[GDTCORUploadPackageTimerHolder alloc] init]; + holder.package = newPackage; + newPackage->_expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 + target:holder + selector:@selector(timerFired:) + userInfo:nil + repeats:YES]; newPackage->_events = [_events copy]; - GDTCORLogDebug("Copying UploadPackage %@ to %@", self, newPackage); + GDTCORLogDebug(@"Copying UploadPackage %@ to %@", self, newPackage); return newPackage; } @@ -71,43 +107,37 @@ [_expirationTimer invalidate]; } -- (void)setStorage:(GDTCORStorage *)storage { - if (storage != _storage) { - _storage = storage; - } -} - - (void)completeDelivery { if (_isDelivered) { GDTCORLogError(GDTCORMCEDeliverTwice, @"%@", @"It's an API violation to call -completeDelivery twice."); } _isDelivered = YES; + [_expirationTimer invalidate]; if (!_isHandled && _handler && [_handler respondsToSelector:@selector(packageDelivered:successful:)]) { - [_expirationTimer invalidate]; _isHandled = YES; - [_handler packageDelivered:self successful:YES]; + [_handler packageDelivered:[self copy] successful:YES]; } - GDTCORLogDebug("Upload package delivered: %@", self); + GDTCORLogDebug(@"Upload package delivered: %@", self); } - (void)retryDeliveryInTheFuture { + [_expirationTimer invalidate]; if (!_isHandled && _handler && [_handler respondsToSelector:@selector(packageDelivered:successful:)]) { - [_expirationTimer invalidate]; _isHandled = YES; - [_handler packageDelivered:self successful:NO]; + [_handler packageDelivered:[self copy] successful:NO]; } - GDTCORLogDebug("Upload package will retry in the future: %@", self); + GDTCORLogDebug(@"Upload package will retry in the future: %@", self); } -- (void)checkIfPackageIsExpired:(NSTimer *)timer { +- (void)checkIfPackageIsExpired { if ([[GDTCORClock snapshot] isAfter:_deliverByTime]) { + [_expirationTimer invalidate]; if (_handler && [_handler respondsToSelector:@selector(packageExpired:)]) { _isHandled = YES; - [_expirationTimer invalidate]; - GDTCORLogDebug("Upload package expired: %@", self); + GDTCORLogDebug(@"Upload package expired: %@", self); [_handler packageExpired:self]; } } @@ -143,10 +173,14 @@ static NSString *const kTargetKey = @"GDTCORUploadPackageTargetKey"; } - (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder { + // Sets a global translation mapping to decode GDTCORStoredEvent objects encoded as instances of + // GDTCOREvent instead. + [NSKeyedUnarchiver setClass:[GDTCOREvent class] forClassName:@"GDTCORStoredEvent"]; + GDTCORTarget target = [aDecoder decodeIntegerForKey:kTargetKey]; self = [self initWithTarget:target]; if (self) { - NSSet *classes = [NSSet setWithObjects:[NSSet class], [GDTCORStoredEvent class], nil]; + NSSet *classes = [NSSet setWithObjects:[NSSet class], [GDTCOREvent class], nil]; _events = [aDecoder decodeObjectOfClasses:classes forKey:kEventsKey]; _deliverByTime = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:kDeliverByTimeKey]; _isHandled = [aDecoder decodeBoolForKey:kIsHandledKey]; diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORDataFuture.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORDataFuture.h similarity index 81% rename from ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORDataFuture.h rename to ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORDataFuture.h index 07f428fc9..685cf2bac 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORDataFuture.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORDataFuture.h @@ -21,15 +21,9 @@ NS_ASSUME_NONNULL_BEGIN /** This class represents a future data object, determined at instantiation time. */ @interface GDTCORDataFuture : NSObject <NSSecureCoding> -/** The data, computed on-demand, depending on the initializer. */ -@property(nullable, readonly, nonatomic) NSData *data; - /** If not nil, this data future was instantiated with this file URL. */ @property(nullable, readonly, nonatomic) NSURL *fileURL; -/** If not nil, this data future was instantiated with this NSData instance. */ -@property(nullable, readonly, nonatomic) NSData *originalData; - /** Initializes an instance with the given the fileURL. * * @param fileURL The fileURL containing the data to return in -data. diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCOREvent_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCOREvent_Private.h index f7f8a28b7..485d6bb8e 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCOREvent_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCOREvent_Private.h @@ -22,8 +22,26 @@ NS_ASSUME_NONNULL_BEGIN @interface GDTCOREvent () -/** The serialized bytes of the event data object. */ -@property(nonatomic) NSData *dataObjectTransportBytes; +/** The unique ID of the event. This property is for testing only. */ +@property(nonatomic, readwrite) NSNumber *eventID; + +/** The GDT relative file path of the event. */ +@property(nullable, nonatomic, readonly) NSString *GDTFilePath; + +/** Writes [dataObject transportBytes] to the given URL, populates fileURL with the filename, then + * nils the dataObject property. This method should not be called twice on the same event. + * + * @param filePath The GDTCORRootDirectory-relative path that dataObject will be written to. + * @param error If populated, the error encountered during writing to disk. + * @return YES if writing dataObject to disk was successful, NO otherwise. + */ +- (BOOL)writeToGDTPath:(NSString *)filePath error:(NSError **)error; + +/** Generates incrementing event IDs, stored in a file in the app's cache. + * + * @return An event ID that is incremented based on a number in a file stored in the app cache. + */ ++ (NSNumber *)nextEventID; @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h similarity index 66% rename from ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage_Private.h rename to ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h index 24569fd46..837c3ea08 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h @@ -14,27 +14,39 @@ * limitations under the License. */ -#import "GDTCORLibrary/Private/GDTCORStorage.h" +#import <Foundation/Foundation.h> +#import <GoogleDataTransport/GDTCORLifecycle.h> +#import <GoogleDataTransport/GDTCORStorageProtocol.h> + +@class GDTCOREvent; @class GDTCORUploadCoordinator; NS_ASSUME_NONNULL_BEGIN -@interface GDTCORStorage () +/** Manages the storage of events. This class is thread-safe. */ +@interface GDTCORFlatFileStorage + : NSObject <NSSecureCoding, GDTCORStorageProtocol, GDTCORLifecycleProtocol> /** The queue on which all storage work will occur. */ @property(nonatomic) dispatch_queue_t storageQueue; /** A map of targets to a set of stored events. */ @property(nonatomic) - NSMutableDictionary<NSNumber *, NSMutableSet<GDTCORStoredEvent *> *> *targetToEventSet; + NSMutableDictionary<NSNumber *, NSMutableSet<GDTCOREvent *> *> *targetToEventSet; /** All the events that have been stored. */ -@property(readonly, nonatomic) NSMutableOrderedSet<GDTCORStoredEvent *> *storedEvents; +@property(readonly, nonatomic) NSMutableDictionary<NSNumber *, GDTCOREvent *> *storedEvents; /** The upload coordinator instance used by this storage instance. */ @property(nonatomic) GDTCORUploadCoordinator *uploadCoordinator; +/** Creates and/or returns the storage singleton. + * + * @return The storage singleton. + */ ++ (instancetype)sharedInstance; + /** Returns the path to the keyed archive of the singleton. This is where the singleton is saved * to disk during certain app lifecycle events. * diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability_Private.h index 5d3ddaf18..c5ca191a0 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability_Private.h @@ -14,14 +14,12 @@ * limitations under the License. */ -#import "GDTCORLibrary/Private/GDTCORReachability.h" +#import "GDTCORLibrary/Public/GDTCORReachability.h" @interface GDTCORReachability () -#if !TARGET_OS_WATCH /** Allows manually setting the flags for testing purposes. */ -@property(nonatomic, readwrite) SCNetworkReachabilityFlags flags; -#endif +@property(nonatomic, readwrite) GDTCORNetworkReachabilityFlags flags; /** Creates/returns the singleton instance of this class. * diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h index 074fc1148..8b298397a 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h @@ -30,6 +30,10 @@ NS_ASSUME_NONNULL_BEGIN @property(atomic, readonly) NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *targetToPrioritizer; +/** A map of targets to storage instances. */ +@property(atomic, readonly) + NSMutableDictionary<NSNumber *, id<GDTCORStorageProtocol>> *targetToStorage; + @end NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer.h index 9f4ec39ca..e2dbff126 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer.h @@ -45,9 +45,11 @@ NS_ASSUME_NONNULL_BEGIN * * @param event The event to apply transformers on. * @param transformers The list of transformers to apply. + * @param completion A block to run when an event was written to disk or dropped. */ - (void)transformEvent:(GDTCOREvent *)event - withTransformers:(nullable NSArray<id<GDTCOREventTransformer>> *)transformers; + withTransformers:(nullable NSArray<id<GDTCOREventTransformer>> *)transformers + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion; @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer_Private.h index fcdae34dd..4adcf93e0 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORTransformer_Private.h @@ -16,8 +16,6 @@ #import "GDTCORLibrary/Private/GDTCORTransformer.h" -@class GDTCORStorage; - NS_ASSUME_NONNULL_BEGIN @interface GDTCORTransformer () @@ -25,9 +23,6 @@ NS_ASSUME_NONNULL_BEGIN /** The queue on which all work will occur. */ @property(nonatomic) dispatch_queue_t eventWritingQueue; -/** The storage instance used to store events. Should only be used to inject a testing fake. */ -@property(nonatomic) GDTCORStorage *storageInstance; - @end NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h index b1d708cc4..b678ef258 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h @@ -22,7 +22,6 @@ #import "GDTCORLibrary/Private/GDTCORUploadPackage_Private.h" @class GDTCORClock; -@class GDTCORStorage; NS_ASSUME_NONNULL_BEGIN @@ -38,6 +37,8 @@ NS_ASSUME_NONNULL_BEGIN * @return The singleton instance of this class. */ + (instancetype)sharedInstance; + +/** The queue on which all upload coordination will occur. */ @property(nonatomic, readonly) dispatch_queue_t coordinationQueue; /** A timer that will causes regular checks for events to upload. */ @@ -53,9 +54,6 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, readonly) NSMutableDictionary<NSNumber *, GDTCORUploadPackage *> *targetToInFlightPackages; -/** The storage object the coordinator will use. Generally used for testing. */ -@property(nonatomic) GDTCORStorage *storage; - /** The registrar object the coordinator will use. Generally used for testing. */ @property(nonatomic) GDTCORRegistrar *registrar; diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadPackage_Private.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadPackage_Private.h index 1eb58d4c7..1ab643258 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadPackage_Private.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadPackage_Private.h @@ -16,14 +16,15 @@ #import <GoogleDataTransport/GDTCORUploadPackage.h> -@class GDTCORStorage; - @interface GDTCORUploadPackage () /** The storage object this upload package will use to resolve event hashes to files. */ -@property(nonatomic) GDTCORStorage *storage; +@property(nonatomic) id<GDTCORStorageProtocol> storage; /** A handler that will receive callbacks for certain events. */ @property(nonatomic) id<NSSecureCoding, GDTCORUploadPackageProtocol> handler; +/** Checks if the package is expired and calls -packageExpired: on the handler if necessary. */ +- (void)checkIfPackageIsExpired; + @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORAssert.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORAssert.h index 941e412e2..c0f9712ce 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORAssert.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORAssert.h @@ -18,6 +18,8 @@ #import <GoogleDataTransport/GDTCORConsoleLogger.h> +NS_ASSUME_NONNULL_BEGIN + /** A block type that could be run instead of normal assertion logging. No return type, no params. */ typedef void (^GDTCORAssertionBlock)(void); @@ -46,21 +48,20 @@ FOUNDATION_EXPORT GDTCORAssertionBlock _Nullable GDTCORAssertionBlockToRunInstea * * @param condition The condition you'd expect to be YES. */ -#define GDTCORAssert(condition, ...) \ - do { \ - if (__builtin_expect(!(condition), 0)) { \ - GDTCORAssertionBlock assertionBlock = GDTCORAssertionBlockToRunInstead(); \ - if (assertionBlock) { \ - assertionBlock(); \ - } else { \ - __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ - NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ - __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ - GDTCORLogError(GDTCORMCEGeneralError, @"Assertion failed (%@:%d): %s,", __assert_file__, \ - __LINE__, ##__VA_ARGS__); \ - __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ - } \ - } \ +#define GDTCORAssert(condition, format, ...) \ + do { \ + __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ + if (__builtin_expect(!(condition), 0)) { \ + GDTCORAssertionBlock assertionBlock = GDTCORAssertionBlockToRunInstead(); \ + if (assertionBlock) { \ + assertionBlock(); \ + } else { \ + NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ + __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ + GDTCORLogAssert(NO, __assert_file__, __LINE__, format, ##__VA_ARGS__); \ + __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ + } \ + } \ } while (0); /** Asserts by logging to the console and throwing an exception if NS_BLOCK_ASSERTIONS is not @@ -68,24 +69,27 @@ FOUNDATION_EXPORT GDTCORAssertionBlock _Nullable GDTCORAssertionBlockToRunInstea * * @param condition The condition you'd expect to be YES. */ -#define GDTCORFatalAssert(condition, ...) \ - do { \ - __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ - if (__builtin_expect(!(condition), 0)) { \ - NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ - __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ - GDTCORLogError(GDTCORMCEFatalAssertion, \ - @"Fatal assertion encountered, please open an issue at " \ - "https://github.com/firebase/firebase-ios-sdk/issues " \ - "(%@:%d): %s,", \ - __assert_file__, __LINE__, ##__VA_ARGS__); \ - [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \ - object:self \ - file:__assert_file__ \ - lineNumber:__LINE__ \ - description:@"%@", ##__VA_ARGS__]; \ - } \ - __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ +#define GDTCORFatalAssert(condition, format, ...) \ + do { \ + __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ + if (__builtin_expect(!(condition), 0)) { \ + GDTCORAssertionBlock assertionBlock = GDTCORAssertionBlockToRunInstead(); \ + if (assertionBlock) { \ + assertionBlock(); \ + } else { \ + NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ + __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ + GDTCORLogAssert(YES, __assert_file__, __LINE__, format, ##__VA_ARGS__); \ + [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \ + object:self \ + file:__assert_file__ \ + lineNumber:__LINE__ \ + description:format, ##__VA_ARGS__]; \ + __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ + } \ + } \ } while (0); #endif // defined(NS_BLOCK_ASSERTIONS) + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORConsoleLogger.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORConsoleLogger.h index c70706bf1..d306f1826 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORConsoleLogger.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORConsoleLogger.h @@ -16,18 +16,40 @@ #import <Foundation/Foundation.h> -// Set this to 1 to have the library print out as much as possible about what GDT is doing. -#define GDT_VERBOSE_LOGGING 0 +/** The current logging level. This value and higher will be printed. Declared as volatile to make + * getting and setting atomic. + */ +FOUNDATION_EXPORT volatile NSInteger GDTCORConsoleLoggerLoggingLevel; + +/** A list of logging levels that GDT supports. */ +typedef NS_ENUM(NSInteger, GDTCORLoggingLevel) { + + /** Causes all logs to be printed. */ + GDTCORLoggingLevelDebug = 1, + + /** Causes all non-debug logs to be printed. */ + GDTCORLoggingLevelVerbose = 2, + + /** Causes warnings and errors to be printed. */ + GDTCORLoggingLevelWarnings = 3, + + /** Causes errors to be printed. This is the default value. */ + GDTCORLoggingLevelErrors = 4 +}; /** A list of message codes to print in the logger that help to correspond printed messages with * code locations. * * Prefixes: + * - MCD => MessageCodeDebug * - MCW => MessageCodeWarning * - MCE => MessageCodeError */ typedef NS_ENUM(NSInteger, GDTCORMessageCode) { + /** For debug logs. */ + GDTCORMCDDebugLog = 0, + /** For warning messages concerning transportBytes: not being implemented by a data object. */ GDTCORMCWDataObjectMissingBytesImpl = 1, @@ -40,6 +62,12 @@ typedef NS_ENUM(NSInteger, GDTCORMessageCode) { /** For warning messages concerning a failed reachability call. */ GDTCORMCWReachabilityFailed = 4, + /** For warning messages concerning a database warning. */ + GDTCORMCWDatabaseWarning = 5, + + /** For warning messages concerning the reading of a event file. */ + GDTCORMCWFileReadError = 6, + /** For error messages concerning transform: not being implemented by an event transformer. */ GDTCORMCETransformerDoesntImplementTransform = 1000, @@ -64,12 +92,37 @@ typedef NS_ENUM(NSInteger, GDTCORMessageCode) { /** For fatal errors. Please go to https://github.com/firebase/firebase-ios-sdk/issues and open * an issue if you encounter an error with this code. */ - GDTCORMCEFatalAssertion = 1007 + GDTCORMCEFatalAssertion = 1007, + + /** For error messages concerning the reading of a event file. */ + GDTCORMCEFileReadError = 1008, + + /** For errors related to running sqlite. */ + GDTCORMCEDatabaseError = 1009, }; -/** */ +/** Prints the given code and format string to the console. + * + * @param code The message code describing the nature of the log. + * @param logLevel The log level of this log. + * @param format The format string. + */ FOUNDATION_EXPORT -void GDTCORLog(GDTCORMessageCode code, NSString *_Nonnull format, ...); +void GDTCORLog(GDTCORMessageCode code, GDTCORLoggingLevel logLevel, NSString *_Nonnull format, ...) + NS_FORMAT_FUNCTION(3, 4); + +/** Prints an assert log to the console. + * + * @param wasFatal Send YES if the assertion should be fatal, NO otherwise. + * @param file The file in which the failure occurred. + * @param line The line number of the failure. + * @param format The format string. + */ +FOUNDATION_EXPORT void GDTCORLogAssert(BOOL wasFatal, + NSString *_Nonnull file, + NSInteger line, + NSString *_Nullable format, + ...) NS_FORMAT_FUNCTION(4, 5); /** Returns the string that represents some message code. * @@ -78,17 +131,13 @@ void GDTCORLog(GDTCORMessageCode code, NSString *_Nonnull format, ...); */ FOUNDATION_EXPORT NSString *_Nonnull GDTCORMessageCodeEnumToString(GDTCORMessageCode code); +#define GDTCORLogDebug(MESSAGE_FORMAT, ...) \ + GDTCORLog(GDTCORMCDDebugLog, GDTCORLoggingLevelDebug, MESSAGE_FORMAT, __VA_ARGS__); + // A define to wrap GULLogWarning with slightly more convenient usage. #define GDTCORLogWarning(MESSAGE_CODE, MESSAGE_FORMAT, ...) \ - GDTCORLog(MESSAGE_CODE, MESSAGE_FORMAT, __VA_ARGS__); + GDTCORLog(MESSAGE_CODE, GDTCORLoggingLevelWarnings, MESSAGE_FORMAT, __VA_ARGS__); // A define to wrap GULLogError with slightly more convenient usage and a failing assert. #define GDTCORLogError(MESSAGE_CODE, MESSAGE_FORMAT, ...) \ - GDTCORLog(MESSAGE_CODE, MESSAGE_FORMAT, __VA_ARGS__); - -// A define to wrap NSLog for verbose console logs only useful for local debugging. -#if GDT_VERBOSE_LOGGING == 1 -#define GDTCORLogDebug(FORMAT, ...) NSLog(@"GDT: " FORMAT, __VA_ARGS__); -#else -#define GDTCORLogDebug(...) -#endif // GDT_VERBOSE_LOGGING == 1 + GDTCORLog(MESSAGE_CODE, GDTCORLoggingLevelErrors, MESSAGE_FORMAT, __VA_ARGS__); diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCOREvent.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCOREvent.h index 1ab55de8f..4d6ffce6f 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCOREvent.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCOREvent.h @@ -19,8 +19,6 @@ #import <GoogleDataTransport/GDTCOREventDataObject.h> @class GDTCORClock; -@class GDTCORDataFuture; -@class GDTCORStoredEvent; NS_ASSUME_NONNULL_BEGIN @@ -47,6 +45,9 @@ typedef NS_ENUM(NSInteger, GDTCOREventQoS) { @interface GDTCOREvent : NSObject <NSSecureCoding> +/** The unique ID of the event. */ +@property(nonatomic, readonly) NSNumber *eventID; + /** The mapping identifier, to allow backends to map the transport bytes to a proto. */ @property(readonly, nonatomic) NSString *mappingID; @@ -63,12 +64,13 @@ typedef NS_ENUM(NSInteger, GDTCOREventQoS) { /** The clock snapshot at the time of the event. */ @property(nonatomic) GDTCORClock *clockSnapshot; -/** A dictionary provided to aid prioritizers by allowing the passing of arbitrary data. It will be - * retained by a copy in -copy, but not used for -hash. - * - * @note Ensure that classes contained therein implement NSSecureCoding to prevent loss of data. +/** The resulting file URL when [dataObject -transportBytes] has been saved to disk.*/ +@property(nullable, readonly, nonatomic) NSURL *fileURL; + +/** Bytes that can be used by a prioritizer or uploader later on. It's the prioritizer or uploader's + * responsibility to serialize and deserialize these bytes. */ -@property(nullable, nonatomic) NSDictionary *customPrioritizationParams; +@property(nullable, nonatomic) NSData *customBytes; // Please use the designated initializer. - (instancetype)init NS_UNAVAILABLE; @@ -82,13 +84,6 @@ typedef NS_ENUM(NSInteger, GDTCOREventQoS) { - (nullable instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target NS_DESIGNATED_INITIALIZER; -/** Returns the GDTCORStoredEvent equivalent of self. - * - * @param dataFuture The data future representing the transport bytes of the original event. - * @return An equivalent GDTCORStoredEvent. - */ -- (GDTCORStoredEvent *)storedEventWithDataFuture:(GDTCORDataFuture *)dataFuture; - @end NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORLifecycle.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORLifecycle.h index 4d61a2131..a08a45694 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORLifecycle.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORLifecycle.h @@ -53,7 +53,7 @@ NS_ASSUME_NONNULL_BEGIN * * When backgrounding, the library doesn't stop processing events, it's just that several background * tasks will end up being created for every event that's sent, and the stateful objects of the - * library (GDTCORStorage and GDTCORUploadCoordinator singletons) will deserialize themselves from + * library (GDTCORStorage and GDTCORUploadCoordinator instances) will deserialize themselves from * and to disk before and after every operation, respectively. */ @interface GDTCORLifecycle : NSObject <GDTCORApplicationDelegate> diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPlatform.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPlatform.h index d72c2c90b..97bd08354 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPlatform.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPlatform.h @@ -19,12 +19,19 @@ #if !TARGET_OS_WATCH #import <SystemConfiguration/SystemConfiguration.h> #endif + #if TARGET_OS_IOS || TARGET_OS_TV #import <UIKit/UIKit.h> #elif TARGET_OS_OSX #import <AppKit/AppKit.h> +#elif TARGET_OS_WATCH +#import <WatchKit/WatchKit.h> #endif // TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS +#import <CoreTelephony/CTTelephonyNetworkInfo.h> +#endif + NS_ASSUME_NONNULL_BEGIN /** The GoogleDataTransport library version. */ @@ -39,14 +46,109 @@ FOUNDATION_EXPORT NSString *const kGDTCORApplicationWillEnterForegroundNotificat /** A notification sent out if the app is terminating. */ FOUNDATION_EXPORT NSString *const kGDTCORApplicationWillTerminateNotification; +/** The different possible network connection type. */ +typedef NS_ENUM(NSInteger, GDTCORNetworkType) { + GDTCORNetworkTypeUNKNOWN = 0, + GDTCORNetworkTypeWIFI = 1, + GDTCORNetworkTypeMobile = 2, +}; + +/** The different possible network connection mobile subtype. */ +typedef NS_ENUM(NSInteger, GDTCORNetworkMobileSubtype) { + GDTCORNetworkMobileSubtypeUNKNOWN = 0, + GDTCORNetworkMobileSubtypeGPRS = 1, + GDTCORNetworkMobileSubtypeEdge = 2, + GDTCORNetworkMobileSubtypeWCDMA = 3, + GDTCORNetworkMobileSubtypeHSDPA = 4, + GDTCORNetworkMobileSubtypeHSUPA = 5, + GDTCORNetworkMobileSubtypeCDMA1x = 6, + GDTCORNetworkMobileSubtypeCDMAEVDORev0 = 7, + GDTCORNetworkMobileSubtypeCDMAEVDORevA = 8, + GDTCORNetworkMobileSubtypeCDMAEVDORevB = 9, + GDTCORNetworkMobileSubtypeHRPD = 10, + GDTCORNetworkMobileSubtypeLTE = 11, +}; + #if !TARGET_OS_WATCH +/** Define SCNetworkReachabilityFlags as GDTCORNetworkReachabilityFlags on non-watchOS. */ +typedef SCNetworkReachabilityFlags GDTCORNetworkReachabilityFlags; + +/** Define SCNetworkReachabilityRef as GDTCORNetworkReachabilityRef on non-watchOS. */ +typedef SCNetworkReachabilityRef GDTCORNetworkReachabilityRef; + +#else +/** The different possible reachabilityFlags option on watchOS. */ +typedef NS_OPTIONS(uint32_t, GDTCORNetworkReachabilityFlags) { + kGDTCORNetworkReachabilityFlagsReachable = 1 << 1, + // TODO(doudounan): Add more options on watchOS if watchOS network connection information relative + // APIs available in the future. +}; + +/** Define a struct as GDTCORNetworkReachabilityRef on watchOS to store network connection + * information. */ +typedef struct { + // TODO(doudounan): Store network connection information on watchOS if watchOS network connection + // information relative APIs available in the future. +} GDTCORNetworkReachabilityRef; +#endif + +/** Returns a URL to the root directory under which all GDT-associated data must be saved. + * + * @return A URL to the root directory under which all GDT-associated data must be saved. + */ +NSURL *GDTCORRootDirectory(void); + +/** Compares flags with the reachable flag (on non-watchos with both reachable and + * connectionRequired flags), if available, and returns YES if network reachable. + * + * @param flags The set of reachability flags. + * @return YES if the network is reachable, NO otherwise. + */ +BOOL GDTCORReachabilityFlagsReachable(GDTCORNetworkReachabilityFlags flags); + /** Compares flags with the WWAN reachability flag, if available, and returns YES if present. * * @param flags The set of reachability flags. * @return YES if the WWAN flag is set, NO otherwise. */ -BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags); -#endif +BOOL GDTCORReachabilityFlagsContainWWAN(GDTCORNetworkReachabilityFlags flags); + +/** Generates an enum message GDTCORNetworkType representing network connection type. + * + * @return A GDTCORNetworkType representing network connection type. + */ +GDTCORNetworkType GDTCORNetworkTypeMessage(void); + +/** Generates an enum message GDTCORNetworkMobileSubtype representing network connection mobile + * subtype. + * + * @return A GDTCORNetworkMobileSubtype representing network connection mobile subtype. + */ +GDTCORNetworkMobileSubtype GDTCORNetworkMobileSubTypeMessage(void); + +/** Writes the given object to the given fileURL and populates the given error if it fails. + * + * @param obj The object to encode. + * @param filePath The path to write the object to. Can be nil if you just need the data. + * @param error The error to populate if something goes wrong. + * @return The data of the archive. If error is nil, it's been written to disk. + */ +NSData *_Nullable GDTCOREncodeArchive(id<NSSecureCoding> obj, + NSString *_Nullable filePath, + NSError *_Nullable *error); + +/** Decodes an object of the given class from the given archive path or data and populates the given + * error if it fails. + * + * @param archiveClass The class of the archive's root object. + * @param archivePath The path to the archived data. Don't use with the archiveData param. + * @param archiveData The data to decode. Don't use with the archivePath param. + * @param error The error to populate if something goes wrong. + */ +id<NSSecureCoding> _Nullable GDTCORDecodeArchive(Class archiveClass, + NSString *_Nullable archivePath, + NSData *_Nullable archiveData, + NSError *_Nullable *error); /** A typedef identify background identifiers. */ typedef volatile NSUInteger GDTCORBackgroundIdentifier; @@ -55,10 +157,14 @@ typedef volatile NSUInteger GDTCORBackgroundIdentifier; FOUNDATION_EXPORT const GDTCORBackgroundIdentifier GDTCORBackgroundIdentifierInvalid; #if TARGET_OS_IOS || TARGET_OS_TV -/** A protocol that wraps UIApplicationDelegate or NSObject protocol, depending on the platform. */ +/** A protocol that wraps UIApplicationDelegate, WKExtensionDelegate or NSObject protocol, depending + * on the platform. + */ @protocol GDTCORApplicationDelegate <UIApplicationDelegate> #elif TARGET_OS_OSX @protocol GDTCORApplicationDelegate <NSApplicationDelegate> +#elif TARGET_OS_WATCH +@protocol GDTCORApplicationDelegate <WKExtensionDelegate> #else @protocol GDTCORApplicationDelegate <NSObject> #endif // TARGET_OS_IOS || TARGET_OS_TV diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPrioritizer.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPrioritizer.h index 3c0c3c63d..d1f675494 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPrioritizer.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORPrioritizer.h @@ -16,11 +16,10 @@ #import <Foundation/Foundation.h> +#import <GoogleDataTransport/GDTCOREvent.h> #import <GoogleDataTransport/GDTCORLifecycle.h> #import <GoogleDataTransport/GDTCORUploadPackage.h> -@class GDTCORStoredEvent; - NS_ASSUME_NONNULL_BEGIN /** Options that define a set of upload conditions. This is used to help minimize end user data @@ -58,15 +57,22 @@ typedef NS_OPTIONS(NSInteger, GDTCORUploadConditions) { * * @param event The event to prioritize. */ -- (void)prioritizeEvent:(GDTCORStoredEvent *)event; +- (void)prioritizeEvent:(GDTCOREvent *)event; /** Returns a set of events to upload given a set of conditions. * + * @param target The target to create an upload package for. * @param conditions A bit mask specifying the current upload conditions. * @return An object to be used by the uploader to determine file URLs to upload with respect to the * current conditions. */ -- (GDTCORUploadPackage *)uploadPackageWithConditions:(GDTCORUploadConditions)conditions; +- (GDTCORUploadPackage *)uploadPackageWithTarget:(GDTCORTarget)target + conditions:(GDTCORUploadConditions)conditions; + +@optional + +/** Saves the state of the prioritizer. */ +- (void)saveState; @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORReachability.h similarity index 84% rename from ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability.h rename to ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORReachability.h index 7879b5983..a74e311be 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORReachability.h @@ -16,18 +16,15 @@ #import <Foundation/Foundation.h> -#if !TARGET_OS_WATCH -#import <SystemConfiguration/SCNetworkReachability.h> -#endif +#import <GoogleDataTransport/GDTCORPlatform.h> NS_ASSUME_NONNULL_BEGIN /** This class helps determine upload conditions by determining connectivity. */ @interface GDTCORReachability : NSObject -#if !TARGET_OS_WATCH + /** The current set flags indicating network conditions */ -+ (SCNetworkReachabilityFlags)currentFlags; -#endif ++ (GDTCORNetworkReachabilityFlags)currentFlags; @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORRegistrar.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORRegistrar.h index 0a8fbb0a9..63f82a726 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORRegistrar.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORRegistrar.h @@ -17,6 +17,7 @@ #import <Foundation/Foundation.h> #import <GoogleDataTransport/GDTCORPrioritizer.h> +#import <GoogleDataTransport/GDTCORStorageProtocol.h> #import <GoogleDataTransport/GDTCORTargets.h> #import <GoogleDataTransport/GDTCORUploader.h> @@ -38,6 +39,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)registerUploader:(id<GDTCORUploader>)backend target:(GDTCORTarget)target; +/** Registers a storage implementation with the GoogleDataTransport infrastructure. + * + * @param storage The storage instance to be associated with this uploader and target. + * @param target The target this backend object will be responsible for. + */ +- (void)registerStorage:(id<GDTCORStorageProtocol>)storage target:(GDTCORTarget)target; + /** Registers a event prioritizer implementation with the GoogleDataTransport infrastructure. * * @param prioritizer The prioritizer object to register. diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h similarity index 50% rename from ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage.h rename to ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h index 008b1d93c..99e9344ca 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Google + * Copyright 2020 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,31 +19,22 @@ #import <GoogleDataTransport/GDTCORLifecycle.h> @class GDTCOREvent; -@class GDTCORStoredEvent; NS_ASSUME_NONNULL_BEGIN -/** Manages the storage of events. This class is thread-safe. */ -@interface GDTCORStorage : NSObject <NSSecureCoding, GDTCORLifecycleProtocol> +/** Defines the interface a storage subsystem is expected to implement. */ +@protocol GDTCORStorageProtocol <NSObject, GDTCORLifecycleProtocol> -/** Creates and/or returns the storage singleton. +/** Stores an event and calls onComplete with a non-nil error if anything went wrong. * - * @return The storage singleton. + * @param event The event to store + * @param completion The completion block to call after an attempt to store the event has been made. */ -+ (instancetype)sharedInstance; +- (void)storeEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion; -/** Stores event.dataObjectTransportBytes into a shared on-device folder and tracks the event via - * a GDTCORStoredEvent instance. - * - * @param event The event to store. - */ -- (void)storeEvent:(GDTCOREvent *)event; - -/** Removes a set of events from storage specified by their hash. - * - * @param events The set of stored events to remove. - */ -- (void)removeEvents:(NSSet<GDTCORStoredEvent *> *)events; +/** Removes the events from storage. */ +- (void)removeEvents:(NSSet<NSNumber *> *)eventIDs; @end diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStoredEvent.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStoredEvent.h deleted file mode 100644 index 647b22088..000000000 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStoredEvent.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#import <Foundation/Foundation.h> - -#import <GoogleDataTransport/GDTCORDataFuture.h> -#import <GoogleDataTransport/GDTCOREvent.h> - -@class GDTCOREvent; - -NS_ASSUME_NONNULL_BEGIN - -@interface GDTCORStoredEvent : NSObject <NSSecureCoding> - -/** The data future representing the original event's transport bytes. */ -@property(readonly, nonatomic) GDTCORDataFuture *dataFuture; - -/** The mapping identifier, to allow backends to map the transport bytes to a proto. */ -@property(readonly, nonatomic) NSString *mappingID; - -/** The identifier for the backend this event will eventually be sent to. */ -@property(readonly, nonatomic) NSNumber *target; - -/** The quality of service tier this event belongs to. */ -@property(readonly, nonatomic) GDTCOREventQoS qosTier; - -/** The clock snapshot at the time of the event. */ -@property(readonly, nonatomic) GDTCORClock *clockSnapshot; - -/** A dictionary provided to aid prioritizers by allowing the passing of arbitrary data. - * - * @note Ensure that custom classes in this dict implement NSSecureCoding to prevent loss of data. - */ -@property(readonly, nullable, nonatomic) NSDictionary *customPrioritizationParams; - -/** Initializes a stored event with the given URL and event. - * - * @param event The event this stored event represents. - * @param dataFuture The dataFuture this event represents. - * @return An instance of this class. - */ -- (instancetype)initWithEvent:(GDTCOREvent *)event dataFuture:(GDTCORDataFuture *)dataFuture; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTargets.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTargets.h index ebd36d11b..5db8852e7 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTargets.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTargets.h @@ -29,4 +29,9 @@ typedef NS_ENUM(NSInteger, GDTCORTarget) { /** The FLL target. */ kGDTCORTargetFLL = 1001, + + /** The CSH target. The CSH target is a special-purpose backend. Please do not use it without + * permission. + */ + kGDTCORTargetCSH = 1002 }; diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTransport.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTransport.h index a95224036..445ff7285 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTransport.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORTransport.h @@ -40,6 +40,17 @@ NS_ASSUME_NONNULL_BEGIN (nullable NSArray<id<GDTCOREventTransformer>> *)transformers target:(NSInteger)target NS_DESIGNATED_INITIALIZER; +/** Copies and sends an internal telemetry event. Events sent using this API are lower in priority, + * and sometimes won't be sent on their own. + * + * @note This will convert the event's data object to data and release the original event. + * + * @param event The event to send. + * @param completion A block that will be called when the event has been written or dropped. + */ +- (void)sendTelemetryEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion; + /** Copies and sends an internal telemetry event. Events sent using this API are lower in priority, * and sometimes won't be sent on their own. * @@ -49,6 +60,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)sendTelemetryEvent:(GDTCOREvent *)event; +/** Copies and sends an SDK service data event. Events send using this API are higher in priority, + * and will cause a network request at some point in the relative near future. + * + * @note This will convert the event's data object to data and release the original event. + * + * @param event The event to send. + * @param completion A block that will be called when the event has been written or dropped. + */ +- (void)sendDataEvent:(GDTCOREvent *)event + onComplete:(void (^_Nullable)(BOOL wasWritten, NSError *_Nullable error))completion; + /** Copies and sends an SDK service data event. Events send using this API are higher in priority, * and will cause a network request at some point in the relative near future. * diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploadPackage.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploadPackage.h index 46a676b9d..4f1d9daf1 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploadPackage.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploadPackage.h @@ -15,11 +15,10 @@ */ #import <Foundation/Foundation.h> - #import <GoogleDataTransport/GDTCORTargets.h> @class GDTCORClock; -@class GDTCORStoredEvent; +@class GDTCOREvent; @class GDTCORUploadPackage; /** A protocol that allows a handler to respond to package lifecycle events. */ @@ -47,7 +46,7 @@ @interface GDTCORUploadPackage : NSObject <NSSecureCoding> /** The set of stored events in this upload package. */ -@property(nonatomic) NSSet<GDTCORStoredEvent *> *events; +@property(nonatomic) NSSet<GDTCOREvent *> *events; /** The expiration time. If [[GDTCORClock snapshot] isAfter:deliverByTime] this package has expired. * @@ -63,10 +62,7 @@ * @param target The target/destination of this package. * @return An instance of this class. */ -- (instancetype)initWithTarget:(GDTCORTarget)target NS_DESIGNATED_INITIALIZER; - -// Please use the designated initializer. -- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithTarget:(GDTCORTarget)target; /** Completes delivery of the package. * diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploader.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploader.h index a34f8b2ac..cadee4789 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploader.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORUploader.h @@ -31,10 +31,11 @@ NS_ASSUME_NONNULL_BEGIN /** Returns YES if the uploader can make an upload attempt, NO otherwise. * + * @param target The target being checked. * @param conditions The conditions that the upload attempt is likely to occur under. * @return YES if the uploader can make an upload attempt, NO otherwise. */ -- (BOOL)readyToUploadWithConditions:(GDTCORUploadConditions)conditions; +- (BOOL)readyToUploadTarget:(GDTCORTarget)target conditions:(GDTCORUploadConditions)conditions; /** Uploads events to the backend using this specific backend's chosen format. * diff --git a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport.h b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport.h index e46a385bf..a02451add 100644 --- a/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport.h +++ b/ios/Pods/GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport.h @@ -16,14 +16,12 @@ #import "GDTCORClock.h" #import "GDTCORConsoleLogger.h" -#import "GDTCORDataFuture.h" #import "GDTCOREvent.h" #import "GDTCOREventDataObject.h" #import "GDTCOREventTransformer.h" #import "GDTCORLifecycle.h" #import "GDTCORPrioritizer.h" #import "GDTCORRegistrar.h" -#import "GDTCORStoredEvent.h" #import "GDTCORTargets.h" #import "GDTCORTransport.h" #import "GDTCORUploadPackage.h" diff --git a/ios/Pods/GoogleDataTransport/README.md b/ios/Pods/GoogleDataTransport/README.md index 5097a89ae..1747cca7a 100644 --- a/ios/Pods/GoogleDataTransport/README.md +++ b/ios/Pods/GoogleDataTransport/README.md @@ -1,10 +1,24 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +83,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +134,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +149,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +180,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +191,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,32 +217,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst +### tvOS, macOS, watchOS and Catalyst Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). During app setup in the console, you may get to a step that mentions something like "Checking if the app -has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/Catalyst. +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. **It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Crashlytics' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -252,3 +272,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTCompressionHelper.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTCompressionHelper.m index 6ae115d3c..9e3745f4a 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTCompressionHelper.m +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTCompressionHelper.m @@ -45,8 +45,7 @@ int windowBits = 15 + 16; // Enable gzip header instead of zlib header. int retCode; - if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits, memLevel, - Z_DEFAULT_STRATEGY)) != Z_OK) { + if (deflateInit2(&strm, level, Z_DEFLATED, windowBits, memLevel, Z_DEFAULT_STRATEGY) != Z_OK) { return nil; } diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTNanopbHelpers.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTNanopbHelpers.m index 2afb823fb..4b63c6133 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTNanopbHelpers.m +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTNanopbHelpers.m @@ -22,13 +22,16 @@ #import <AppKit/AppKit.h> #endif // TARGET_OS_IOS || TARGET_OS_TV +#import <GoogleDataTransport/GDTCORClock.h> #import <GoogleDataTransport/GDTCORConsoleLogger.h> +#import <GoogleDataTransport/GDTCOREvent.h> +#import <GoogleDataTransport/GDTCORPlatform.h> #import <nanopb/pb.h> #import <nanopb/pb_decode.h> #import <nanopb/pb_encode.h> -#import "GDTCCTLibrary/Private/GDTCCTPrioritizer.h" +#import "GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h" #pragma mark - General purpose encoders @@ -38,10 +41,12 @@ pb_bytes_array_t *GDTCCTEncodeString(NSString *string) { } pb_bytes_array_t *GDTCCTEncodeData(NSData *data) { - pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); - memcpy(pbBytes->bytes, [data bytes], data.length); - pbBytes->size = (pb_size_t)data.length; - return pbBytes; + pb_bytes_array_t *pbBytesArray = calloc(1, PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); + if (pbBytesArray != NULL) { + [data getBytes:pbBytesArray->bytes length:data.length]; + pbBytesArray->size = (pb_size_t)data.length; + } + return pbBytesArray; } #pragma mark - CCT object constructors @@ -57,26 +62,29 @@ NSData *_Nullable GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batch // Encode a 2nd time to actually get the bytes from it. size_t bufferSize = sizestream.bytes_written; CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize); + CFDataSetLength(dataRef, bufferSize); pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize); if (!pb_encode(&ostream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) { GDTCORLogError(GDTCORMCEGeneralError, @"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream)); } - CFDataSetLength(dataRef, ostream.bytes_written); return CFBridgingRelease(dataRef); } gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest( - NSDictionary<NSString *, NSSet<GDTCORStoredEvent *> *> *logMappingIDToLogSet) { + NSDictionary<NSString *, NSSet<GDTCOREvent *> *> *logMappingIDToLogSet) { gdt_cct_BatchedLogRequest batchedLogRequest = gdt_cct_BatchedLogRequest_init_default; NSUInteger numberOfLogRequests = logMappingIDToLogSet.count; - gdt_cct_LogRequest *logRequests = malloc(sizeof(gdt_cct_LogRequest) * numberOfLogRequests); + gdt_cct_LogRequest *logRequests = calloc(numberOfLogRequests, sizeof(gdt_cct_LogRequest)); + if (logRequests == NULL) { + return batchedLogRequest; + } __block int i = 0; - [logMappingIDToLogSet enumerateKeysAndObjectsUsingBlock:^( - NSString *_Nonnull logMappingID, - NSSet<GDTCORStoredEvent *> *_Nonnull logSet, BOOL *_Nonnull stop) { + [logMappingIDToLogSet enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull logMappingID, + NSSet<GDTCOREvent *> *_Nonnull logSet, + BOOL *_Nonnull stop) { int32_t logSource = [logMappingID intValue]; gdt_cct_LogRequest logRequest = GDTCCTConstructLogRequest(logSource, logSet); logRequests[i] = logRequest; @@ -89,7 +97,7 @@ gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest( } gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, - NSSet<GDTCORStoredEvent *> *_Nonnull logSet) { + NSSet<GDTCOREvent *> *_Nonnull logSet) { if (logSet.count == 0) { GDTCORLogError(GDTCORMCEGeneralError, @"%@", @"An empty event set can't be serialized to proto."); @@ -101,9 +109,12 @@ gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, logRequest.has_log_source = 1; logRequest.client_info = GDTCCTConstructClientInfo(); logRequest.has_client_info = 1; - logRequest.log_event = malloc(sizeof(gdt_cct_LogEvent) * logSet.count); + logRequest.log_event = calloc(logSet.count, sizeof(gdt_cct_LogEvent)); + if (logRequest.log_event == NULL) { + return logRequest; + } int i = 0; - for (GDTCORStoredEvent *log in logSet) { + for (GDTCOREvent *log in logSet) { gdt_cct_LogEvent logEvent = GDTCCTConstructLogEvent(log); logRequest.log_event[i] = logEvent; i++; @@ -119,7 +130,7 @@ gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, return logRequest; } -gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTCORStoredEvent *event) { +gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTCOREvent *event) { gdt_cct_LogEvent logEvent = gdt_cct_LogEvent_init_default; logEvent.event_time_ms = event.clockSnapshot.timeMillis; logEvent.has_event_time_ms = 1; @@ -127,15 +138,25 @@ gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTCORStoredEvent *event) { logEvent.has_event_uptime_ms = 1; logEvent.timezone_offset_seconds = event.clockSnapshot.timezoneOffsetSeconds; logEvent.has_timezone_offset_seconds = 1; - // TODO: Read network_connection_info from the custom params dict. - + if (event.customBytes) { + NSData *networkConnectionInfoData = event.networkConnectionInfoData; + if (networkConnectionInfoData) { + [networkConnectionInfoData getBytes:&logEvent.network_connection_info + length:networkConnectionInfoData.length]; + logEvent.has_network_connection_info = 1; + } + } NSError *error; - NSData *extensionBytes = [NSData dataWithContentsOfURL:event.dataFuture.fileURL - options:0 - error:&error]; + NSData *extensionBytes; + if (event.fileURL) { + extensionBytes = [NSData dataWithContentsOfFile:event.fileURL.path options:0 error:&error]; + } else { + GDTCORLogError(GDTCORMCEFileReadError, @"%@", @"An event's fileURL property was nil."); + return logEvent; + } if (error) { - GDTCORLogError(GDTCORMCEGeneralError, - @"There was an error reading extension bytes from disk: %@", error); + GDTCORLogWarning(GDTCORMCWFileReadError, + @"There was an error reading extension bytes from disk: %@", error); return logEvent; } logEvent.source_extension = GDTCCTEncodeData(extensionBytes); // read bytes from the file. @@ -181,6 +202,57 @@ gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo() { return iOSClientInfo; } +NSData *GDTCCTConstructNetworkConnectionInfoData() { + gdt_cct_NetworkConnectionInfo networkConnectionInfo = gdt_cct_NetworkConnectionInfo_init_default; + NSInteger currentNetworkType = GDTCORNetworkTypeMessage(); + if (currentNetworkType) { + networkConnectionInfo.has_network_type = 1; + if (currentNetworkType == GDTCORNetworkTypeMobile) { + networkConnectionInfo.network_type = gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE; + networkConnectionInfo.mobile_subtype = GDTCCTNetworkConnectionInfoNetworkMobileSubtype(); + if (networkConnectionInfo.mobile_subtype != + gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE) { + networkConnectionInfo.has_mobile_subtype = 1; + } + } else { + networkConnectionInfo.network_type = gdt_cct_NetworkConnectionInfo_NetworkType_WIFI; + } + } + NSData *networkConnectionInfoData = [NSData dataWithBytes:&networkConnectionInfo + length:sizeof(networkConnectionInfo)]; + return networkConnectionInfoData; +} + +gdt_cct_NetworkConnectionInfo_MobileSubtype GDTCCTNetworkConnectionInfoNetworkMobileSubtype() { + NSNumber *networkMobileSubtypeMessage = @(GDTCORNetworkMobileSubTypeMessage()); + if (!networkMobileSubtypeMessage.intValue) { + return gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE; + } + static NSDictionary<NSNumber *, NSNumber *> *MessageToNetworkSubTypeMessage; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + MessageToNetworkSubTypeMessage = @{ + @(GDTCORNetworkMobileSubtypeGPRS) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_GPRS), + @(GDTCORNetworkMobileSubtypeEdge) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_EDGE), + @(GDTCORNetworkMobileSubtypeWCDMA) : + @(gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE), + @(GDTCORNetworkMobileSubtypeHSDPA) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_HSDPA), + @(GDTCORNetworkMobileSubtypeHSUPA) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_HSUPA), + @(GDTCORNetworkMobileSubtypeCDMA1x) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_CDMA), + @(GDTCORNetworkMobileSubtypeCDMAEVDORev0) : + @(gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_0), + @(GDTCORNetworkMobileSubtypeCDMAEVDORevA) : + @(gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_A), + @(GDTCORNetworkMobileSubtypeCDMAEVDORevB) : + @(gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_B), + @(GDTCORNetworkMobileSubtypeHRPD) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_EHRPD), + @(GDTCORNetworkMobileSubtypeLTE) : @(gdt_cct_NetworkConnectionInfo_MobileSubtype_LTE), + }; + }); + NSNumber *networkMobileSubtype = MessageToNetworkSubTypeMessage[networkMobileSubtypeMessage]; + return networkMobileSubtype.intValue; +} + #pragma mark - CCT Object decoders gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error) { diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTPrioritizer.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTPrioritizer.m index 0c6e879ba..224e34cbf 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTPrioritizer.m +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTPrioritizer.m @@ -18,17 +18,53 @@ #import <GoogleDataTransport/GDTCORConsoleLogger.h> #import <GoogleDataTransport/GDTCOREvent.h> +#import <GoogleDataTransport/GDTCORPlatform.h> #import <GoogleDataTransport/GDTCORRegistrar.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> #import <GoogleDataTransport/GDTCORTargets.h> +#import "GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h" +#import "GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h" + const static int64_t kMillisPerDay = 8.64e+7; +/** Creates and/or returns a singleton NSString that is the NSCoding file location. + * + * @return The NSCoding file path. + */ +static NSString *ArchivePath() { + static NSString *archivePath; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + archivePath = [GDTCORRootDirectory() URLByAppendingPathComponent:@"GDTCCTPrioritizer"].path; + }); + return archivePath; +} + +/** This class extension is for declaring private properties. */ +@interface GDTCCTPrioritizer () + +/** All CCT events that have been processed by this prioritizer. */ +@property(nonatomic) NSMutableSet<GDTCOREvent *> *CCTEvents; + +/** All FLL events that have been processed by this prioritizer. */ +@property(nonatomic) NSMutableSet<GDTCOREvent *> *FLLEvents; + +/** All CSH events that have been processed by this prioritizer. */ +@property(nonatomic) NSMutableSet<GDTCOREvent *> *CSHEvents; + +@end + @implementation GDTCCTPrioritizer + (void)load { GDTCCTPrioritizer *prioritizer = [GDTCCTPrioritizer sharedInstance]; [[GDTCORRegistrar sharedInstance] registerPrioritizer:prioritizer target:kGDTCORTargetCCT]; + [[GDTCORRegistrar sharedInstance] registerPrioritizer:prioritizer target:kGDTCORTargetFLL]; + [[GDTCORRegistrar sharedInstance] registerPrioritizer:prioritizer target:kGDTCORTargetCSH]; +} + ++ (BOOL)supportsSecureCoding { + return YES; } + (instancetype)sharedInstance { @@ -44,61 +80,72 @@ const static int64_t kMillisPerDay = 8.64e+7; self = [super init]; if (self) { _queue = dispatch_queue_create("com.google.GDTCCTPrioritizer", DISPATCH_QUEUE_SERIAL); - _events = [[NSMutableSet alloc] init]; + _CCTEvents = [[NSMutableSet alloc] init]; + _FLLEvents = [[NSMutableSet alloc] init]; + _CSHEvents = [[NSMutableSet alloc] init]; } return self; } +- (nullable NSSet *)eventsForTarget:(GDTCORTarget)target { + __block NSSet *events; + dispatch_sync(_queue, ^{ + switch (target) { + case kGDTCORTargetCCT: + events = [self->_CCTEvents copy]; + break; + + case kGDTCORTargetFLL: + events = [self->_FLLEvents copy]; + break; + + case kGDTCORTargetCSH: + events = [self->_CSHEvents copy]; + break; + + default: + break; + } + }); + return events; +} + #pragma mark - GDTCORPrioritizer Protocol -- (void)prioritizeEvent:(GDTCORStoredEvent *)event { +- (void)prioritizeEvent:(GDTCOREvent *)event { + if (event.needsNetworkConnectionInfoPopulated) { + event.networkConnectionInfoData = GDTCCTConstructNetworkConnectionInfoData(); + } dispatch_async(_queue, ^{ - [self.events addObject:event]; + switch (event.target) { + case kGDTCORTargetCCT: + [self.CCTEvents addObject:event]; + break; + + case kGDTCORTargetFLL: + [self.FLLEvents addObject:event]; + break; + + case kGDTCORTargetCSH: + [self.CSHEvents addObject:event]; + break; + + default: + GDTCORLogDebug(@"GDTCCTPrioritizer doesn't support target %ld", (long)event.target); + break; + } }); } -- (GDTCORUploadPackage *)uploadPackageWithConditions:(GDTCORUploadConditions)conditions { - GDTCORUploadPackage *package = [[GDTCORUploadPackage alloc] initWithTarget:kGDTCORTargetCCT]; +- (GDTCORUploadPackage *)uploadPackageWithTarget:(GDTCORTarget)target + conditions:(GDTCORUploadConditions)conditions { + GDTCORUploadPackage *package = [[GDTCORUploadPackage alloc] initWithTarget:target]; dispatch_sync(_queue, ^{ - NSSet<GDTCORStoredEvent *> *logEventsThatWillBeSent; - // A high priority event effectively flushes all events to be sent. - if ((conditions & GDTCORUploadConditionHighPriority) == GDTCORUploadConditionHighPriority) { - GDTCORLogDebug("%@", @"CCT: A high priority event is flushing all events."); - package.events = self.events; - GDTCORLogDebug("CCT: %lu events are in the upload package", - (unsigned long)package.events.count); - return; - } - - // If on wifi, upload logs that are ok to send on wifi. - if ((conditions & GDTCORUploadConditionWifiData) == GDTCORUploadConditionWifiData) { - logEventsThatWillBeSent = [self logEventsOkToSendOnWifi]; - GDTCORLogDebug("%@", @"CCT: events ok to send on wifi are being added to the upload package"); - } else { - logEventsThatWillBeSent = [self logEventsOkToSendOnMobileData]; - GDTCORLogDebug("%@", - @"CCT: events ok to send on mobile are being added to the upload package"); - } - - // If it's been > 24h since the last daily upload, upload logs with the daily QoS. - if (self.timeOfLastDailyUpload) { - int64_t millisSinceLastUpload = - [GDTCORClock snapshot].timeMillis - self.timeOfLastDailyUpload.timeMillis; - if (millisSinceLastUpload > kMillisPerDay) { - logEventsThatWillBeSent = - [logEventsThatWillBeSent setByAddingObjectsFromSet:[self logEventsOkToSendDaily]]; - GDTCORLogDebug("%@", @"CCT: events ok to send daily are being added to the upload package"); - } - } else { - self.timeOfLastDailyUpload = [GDTCORClock snapshot]; - logEventsThatWillBeSent = - [logEventsThatWillBeSent setByAddingObjectsFromSet:[self logEventsOkToSendDaily]]; - GDTCORLogDebug("%@", @"CCT: events ok to send daily are being added to the upload package"); - } - package.events = logEventsThatWillBeSent; + NSSet<GDTCOREvent *> *eventsThatWillBeSent = [self eventsForTarget:target + conditions:conditions]; + package.events = eventsThatWillBeSent; }); - GDTCORLogDebug("CCT: created an upload package with %ld events", - (unsigned long)package.events.count); + GDTCORLogDebug(@"CCT: %lu events are in the upload package", (unsigned long)package.events.count); return package; } @@ -119,6 +166,17 @@ typedef NS_ENUM(NSInteger, GDTCCTQoSTier) { GDTCCTQoSWifiOnly = 5, }; +- (void)saveState { + dispatch_sync(_queue, ^{ + NSError *error; + GDTCOREncodeArchive(self, ArchivePath(), &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCCTPrioritizer to an archive failed: %@", error); + } + }); + GDTCORLogDebug(@"GDTCCTPrioritizer saved state to %@ as requested by GDT.", ArchivePath()); +} + /** Converts a GDTCOREventQoS to a GDTCCTQoS tier. * * @param qosTier The GDTCOREventQoS value. @@ -143,16 +201,82 @@ NSNumber *GDTCCTQosTierFromGDTCOREventQosTier(GDTCOREventQoS qosTier) { } } +/** Constructs a set of events for upload to CCT, FLL, or CSH backends. These backends are + * request-proto and batching compatible, so they construct event batches the same way. + * + * @param conditions The set of conditions the upload package should be made under. + * @param target The target backend. + * @return A set of events for the target. + */ +- (NSSet<GDTCOREvent *> *)eventsForTarget:(GDTCORTarget)target + conditions:(GDTCORUploadConditions)conditions { + GDTCORClock __strong **timeOfLastDailyUpload = NULL; + NSSet<GDTCOREvent *> *eventsToFilter; + switch (target) { + case kGDTCORTargetCCT: + eventsToFilter = self.CCTEvents; + timeOfLastDailyUpload = &self->_CCTTimeOfLastDailyUpload; + break; + + case kGDTCORTargetFLL: + eventsToFilter = self.FLLEvents; + timeOfLastDailyUpload = &self->_FLLOfLastDailyUpload; + break; + + case kGDTCORTargetCSH: + // This backend doesn't batch and uploads all events as soon as possible without respect to + // any upload condition. + return self.CSHEvents; + break; + + default: + // Return an empty set. + return [[NSSet alloc] init]; + break; + } + + NSMutableSet<GDTCOREvent *> *eventsThatWillBeSent = [[NSMutableSet alloc] init]; + // A high priority event effectively flushes all events to be sent. + if ((conditions & GDTCORUploadConditionHighPriority) == GDTCORUploadConditionHighPriority) { + GDTCORLogDebug(@"%@", @"CCT: A high priority event is flushing all events."); + return eventsToFilter; + } + + // If on wifi, upload logs that are ok to send on wifi. + if ((conditions & GDTCORUploadConditionWifiData) == GDTCORUploadConditionWifiData) { + [eventsThatWillBeSent unionSet:[self logEventsOkToSendOnWifi:eventsToFilter]]; + GDTCORLogDebug(@"%@", @"CCT: events ok to send on wifi are being added to the upload package"); + } else { + [eventsThatWillBeSent unionSet:[self logEventsOkToSendOnMobileData:eventsToFilter]]; + GDTCORLogDebug(@"%@", + @"CCT: events ok to send on mobile are being added to the upload package"); + } + + // If it's been > 24h since the last daily upload, upload logs with the daily QoS. + if (*timeOfLastDailyUpload) { + int64_t millisSinceLastUpload = + [GDTCORClock snapshot].timeMillis - (*timeOfLastDailyUpload).timeMillis; + if (millisSinceLastUpload > kMillisPerDay) { + [eventsThatWillBeSent unionSet:[self logEventsOkToSendDaily:eventsToFilter]]; + GDTCORLogDebug(@"%@", @"CCT: events ok to send daily are being added to the upload package"); + } + } else { + *timeOfLastDailyUpload = [GDTCORClock snapshot]; + [eventsThatWillBeSent unionSet:[self logEventsOkToSendDaily:eventsToFilter]]; + GDTCORLogDebug(@"%@", @"CCT: events ok to send daily are being added to the upload package"); + } + return eventsThatWillBeSent; +} + /** Returns a set of logs that are ok to upload whilst on mobile data. * * @note This should be called from a thread safe method. * @return A set of logs that are ok to upload whilst on mobile data. */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendOnMobileData { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTCCTQoSDefault)]; - }]; +- (NSSet<GDTCOREvent *> *)logEventsOkToSendOnMobileData:(NSSet<GDTCOREvent *> *)events { + return [events objectsPassingTest:^BOOL(GDTCOREvent *_Nonnull event, BOOL *_Nonnull stop) { + return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTCCTQoSDefault)]; + }]; } /** Returns a set of logs that are ok to upload whilst on wifi. @@ -160,13 +284,12 @@ NSNumber *GDTCCTQosTierFromGDTCOREventQosTier(GDTCOREventQoS qosTier) { * @note This should be called from a thread safe method. * @return A set of logs that are ok to upload whilst on wifi. */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendOnWifi { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - NSNumber *qosTier = GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier); - return [qosTier isEqual:@(GDTCCTQoSDefault)] || [qosTier isEqual:@(GDTCCTQoSWifiOnly)] || - [qosTier isEqual:@(GDTCCTQoSDaily)]; - }]; +- (NSSet<GDTCOREvent *> *)logEventsOkToSendOnWifi:(NSSet<GDTCOREvent *> *)events { + return [events objectsPassingTest:^BOOL(GDTCOREvent *_Nonnull event, BOOL *_Nonnull stop) { + NSNumber *qosTier = GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier); + return [qosTier isEqual:@(GDTCCTQoSDefault)] || [qosTier isEqual:@(GDTCCTQoSWifiOnly)] || + [qosTier isEqual:@(GDTCCTQoSDaily)]; + }]; } /** Returns a set of logs that only should have a single upload attempt per day. @@ -174,20 +297,124 @@ NSNumber *GDTCCTQosTierFromGDTCOREventQosTier(GDTCOREventQoS qosTier) { * @note This should be called from a thread safe method. * @return A set of logs that are ok to upload only once per day. */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendDaily { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTCCTQoSDaily)]; - }]; +- (NSSet<GDTCOREvent *> *)logEventsOkToSendDaily:(NSSet<GDTCOREvent *> *)events { + return [events objectsPassingTest:^BOOL(GDTCOREvent *_Nonnull event, BOOL *_Nonnull stop) { + return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTCCTQoSDaily)]; + }]; +} + +#pragma mark - NSSecureCoding + +/** NSSecureCoding key for the CCTEvents property. */ +static NSString *const GDTCCTUploaderCCTEventsKey = @"GDTCCTUploaderCCTEventsKey"; + +/** NSSecureCoding key for the CCTEvents property. */ +static NSString *const GDTCCTUploaderFLLEventsKey = @"GDTCCTUploaderFLLEventsKey"; + +/** NSSecureCoding key for the CCTEvents property. */ +static NSString *const GDTCCTUploaderCSHEventsKey = @"GDTCCTUploaderCSHEventsKey"; + +- (instancetype)initWithCoder:(NSCoder *)coder { + GDTCCTPrioritizer *sharedInstance = [GDTCCTPrioritizer sharedInstance]; + if (sharedInstance) { + NSSet *classes = [NSSet setWithObjects:[NSMutableSet class], [GDTCOREvent class], nil]; + NSMutableSet *decodedCCTEvents = [coder decodeObjectOfClasses:classes + forKey:GDTCCTUploaderCCTEventsKey]; + if (decodedCCTEvents) { + sharedInstance->_CCTEvents = decodedCCTEvents; + } + NSMutableSet *decodedFLLEvents = [coder decodeObjectOfClasses:classes + forKey:GDTCCTUploaderFLLEventsKey]; + if (decodedFLLEvents) { + sharedInstance->_FLLEvents = decodedFLLEvents; + } + NSMutableSet *decodedCSHEvents = [coder decodeObjectOfClasses:classes + forKey:GDTCCTUploaderCSHEventsKey]; + if (decodedCSHEvents) { + sharedInstance->_CSHEvents = decodedCSHEvents; + } + } + return sharedInstance; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + GDTCCTPrioritizer *sharedInstance = [GDTCCTPrioritizer sharedInstance]; + if (!sharedInstance) { + return; + } + NSMutableSet<GDTCOREvent *> *CCTEvents = sharedInstance->_CCTEvents; + if (CCTEvents) { + [coder encodeObject:CCTEvents forKey:GDTCCTUploaderCCTEventsKey]; + } + NSMutableSet<GDTCOREvent *> *FLLEvents = sharedInstance->_FLLEvents; + if (FLLEvents) { + [coder encodeObject:FLLEvents forKey:GDTCCTUploaderFLLEventsKey]; + } + NSMutableSet<GDTCOREvent *> *CSHEvents = sharedInstance->_CSHEvents; + if (CSHEvents) { + [coder encodeObject:CSHEvents forKey:GDTCCTUploaderCSHEventsKey]; + } +} + +#pragma mark - GDTCORLifecycleProtocol + +- (void)appWillForeground:(GDTCORApplication *)app { + dispatch_async(_queue, ^{ + NSError *error; + GDTCORDecodeArchive([GDTCCTPrioritizer class], ArchivePath(), nil, &error); + if (error) { + GDTCORLogDebug(@"Deserializing GDTCCTPrioritizer from an archive failed: %@", error); + } + }); +} + +- (void)appWillBackground:(GDTCORApplication *)app { + dispatch_async(_queue, ^{ + // Immediately request a background task to run until the end of the current queue of work, and + // cancel it once the work is done. + __block GDTCORBackgroundIdentifier bgID = + [app beginBackgroundTaskWithName:@"GDTStorage" + expirationHandler:^{ + [app endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + }]; + NSError *error; + GDTCOREncodeArchive(self, ArchivePath(), &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCCTPrioritizer to an archive failed: %@", error); + } + + // End the background task if it's still valid. + [app endBackgroundTask:bgID]; + bgID = GDTCORBackgroundIdentifierInvalid; + }); +} + +- (void)appWillTerminate:(GDTCORApplication *)application { + dispatch_sync(_queue, ^{ + NSError *error; + GDTCOREncodeArchive(self, ArchivePath(), &error); + if (error) { + GDTCORLogDebug(@"Serializing GDTCCTPrioritizer to an archive failed: %@", error); + } + }); } #pragma mark - GDTCORUploadPackageProtocol - (void)packageDelivered:(GDTCORUploadPackage *)package successful:(BOOL)successful { + // If sending the package wasn't successful, we should keep track of these events. + if (!successful) { + return; + } + dispatch_async(_queue, ^{ - NSSet<GDTCORStoredEvent *> *events = [package.events copy]; - for (GDTCORStoredEvent *event in events) { - [self.events removeObject:event]; + NSSet<GDTCOREvent *> *events = [package.events copy]; + for (GDTCOREvent *event in events) { + // We don't know what collection the event was contained in, so attempt removal from all. + [self.CCTEvents removeObject:event]; + [self.FLLEvents removeObject:event]; + [self.CSHEvents removeObject:event]; } }); } diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTUploader.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTUploader.m index 532bf3b1c..f50c9bf53 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTUploader.m +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCCTUploader.m @@ -42,7 +42,7 @@ static NSString *const kGDTCCTSupportSDKVersion = @"UNKNOWN"; NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader.UploadComplete"; #endif // #if !NDEBUG -@interface GDTCCTUploader () +@interface GDTCCTUploader () <NSURLSessionDelegate> // Redeclared as readwrite. @property(nullable, nonatomic, readwrite) NSURLSessionUploadTask *currentTask; @@ -54,6 +54,8 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader + (void)load { GDTCCTUploader *uploader = [GDTCCTUploader sharedInstance]; [[GDTCORRegistrar sharedInstance] registerUploader:uploader target:kGDTCORTargetCCT]; + [[GDTCORRegistrar sharedInstance] registerUploader:uploader target:kGDTCORTargetFLL]; + [[GDTCORRegistrar sharedInstance] registerUploader:uploader target:kGDTCORTargetCSH]; } + (instancetype)sharedInstance { @@ -70,28 +72,105 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader if (self) { _uploaderQueue = dispatch_queue_create("com.google.GDTCCTUploader", DISPATCH_QUEUE_SERIAL); NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - _uploaderSession = [NSURLSession sessionWithConfiguration:config]; + _uploaderSession = [NSURLSession sessionWithConfiguration:config + delegate:self + delegateQueue:nil]; } return self; } -- (NSURL *)defaultServerURL { - static NSURL *defaultServerURL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // These strings should be interleaved to construct the real URL. This is just to (hopefully) - // fool github URL scanning bots. +/** + * + */ +- (nullable NSURL *)serverURLForTarget:(GDTCORTarget)target { + // These strings should be interleaved to construct the real URL. This is just to (hopefully) + // fool github URL scanning bots. + static NSURL *CCTServerURL; + static dispatch_once_t CCTOnceToken; + dispatch_once(&CCTOnceToken, ^{ const char *p1 = "hts/frbslgiggolai.o/0clgbth"; const char *p2 = "tp:/ieaeogn.ogepscmvc/o/ac"; - const char defaultURL[54] = { - p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4], p2[4], p1[5], - p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8], p1[9], p2[9], p1[10], p2[10], - p1[11], p2[11], p1[12], p2[12], p1[13], p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], - p2[16], p1[17], p2[17], p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], - p1[22], p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], '\0'}; - defaultServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:defaultURL]]; + const char URL[54] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4], + p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8], + p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13], + p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17], + p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22], + p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], '\0'}; + CCTServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]]; }); - return defaultServerURL; + + static NSURL *FLLServerURL; + static dispatch_once_t FLLOnceToken; + dispatch_once(&FLLOnceToken, ^{ + const char *p1 = "hts/frbslgigp.ogepscmv/ieo/eaybtho"; + const char *p2 = "tp:/ieaeogn-agolai.o/1frlglgc/aclg"; + const char URL[69] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4], + p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8], + p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13], + p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17], + p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22], + p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], p2[26], + p1[27], p2[27], p1[28], p2[28], p1[29], p2[29], p1[30], p2[30], p1[31], + p2[31], p1[32], p2[32], p1[33], p2[33], '\0'}; + FLLServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]]; + }); + + static NSURL *CSHServerURL; + static dispatch_once_t CSHOnceToken; + dispatch_once(&CSHOnceToken, ^{ + // These strings should be interleaved to construct the real URL. This is just to (hopefully) + // fool github URL scanning bots. + const char *p1 = "hts/cahyiseot-agolai.o/1frlglgc/aclg"; + const char *p2 = "tp:/rsltcrprsp.ogepscmv/ieo/eaybtho"; + const char URL[72] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4], + p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8], + p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13], + p2[13], p1[14], p2[14], p1[15], p2[15], p1[16], p2[16], p1[17], p2[17], + p1[18], p2[18], p1[19], p2[19], p1[20], p2[20], p1[21], p2[21], p1[22], + p2[22], p1[23], p2[23], p1[24], p2[24], p1[25], p2[25], p1[26], p2[26], + p1[27], p2[27], p1[28], p2[28], p1[29], p2[29], p1[30], p2[30], p1[31], + p2[31], p1[32], p2[32], p1[33], p2[33], p1[34], p2[34], p1[35], '\0'}; + CSHServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:URL]]; + }); + +#if !NDEBUG + if (_testServerURL) { + return _testServerURL; + } +#endif // !NDEBUG + + switch (target) { + case kGDTCORTargetCCT: + return CCTServerURL; + + case kGDTCORTargetFLL: + return FLLServerURL; + + case kGDTCORTargetCSH: + return CSHServerURL; + + default: + GDTCORLogDebug(@"GDTCCTUploader doesn't support target %ld", (long)target); + return nil; + break; + } +} + +- (NSString *)FLLAndCSHAPIKey { + static NSString *defaultServerKey; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // These strings should be interleaved to construct the real key. + const char *p1 = "AzSBG0honD6A-PxV5nBc"; + const char *p2 = "Iay44Iwtu2vV0AOrz1C"; + const char defaultKey[40] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], + p1[4], p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], + p1[8], p2[8], p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], + p1[12], p2[12], p1[13], p2[13], p1[14], p2[14], p1[15], p2[15], + p1[16], p2[16], p1[17], p2[17], p1[18], p2[18], p1[19], '\0'}; + defaultServerKey = [NSString stringWithUTF8String:defaultKey]; + }); + return defaultServerKey; } - (void)uploadPackage:(GDTCORUploadPackage *)package { @@ -100,11 +179,11 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader beginBackgroundTaskWithName:@"GDTCCTUploader-upload" expirationHandler:^{ if (bgID != GDTCORBackgroundIdentifierInvalid) { - // Cancel the current upload and complete delivery. + // Cancel the upload and complete delivery. [self.currentTask cancel]; [self.currentUploadPackage completeDelivery]; - // End the task. + // End the background task. [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; } }]; @@ -115,39 +194,61 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader @"An upload shouldn't be initiated with another in progress."); return; } - NSURL *serverURL = self.serverURL ? self.serverURL : [self defaultServerURL]; - + GDTCORTarget target = package.target; id completionHandler = ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { - GDTCORLogDebug("%@", @"CCT: request completed"); + GDTCORLogDebug(@"%@", @"CCT: request completed"); if (error) { GDTCORLogWarning(GDTCORMCWUploadFailed, @"There was an error uploading events: %@", error); } NSError *decodingError; + GDTCORClock *futureUploadTime; if (data) { gdt_cct_LogResponse logResponse = GDTCCTDecodeLogResponse(data, &decodingError); if (!decodingError && logResponse.has_next_request_wait_millis) { GDTCORLogDebug( - "CCT: The backend responded asking to not upload for %lld millis from now.", + @"CCT: The backend responded asking to not upload for %lld millis from now.", logResponse.next_request_wait_millis); - self->_nextUploadTime = + futureUploadTime = [GDTCORClock clockSnapshotInTheFuture:logResponse.next_request_wait_millis]; - } else { - GDTCORLogDebug("%@", @"CCT: The CCT backend response failed to parse, so the next " - @"request won't occur until 15 minutes from now"); - // 15 minutes from now. - self->_nextUploadTime = [GDTCORClock clockSnapshotInTheFuture:15 * 60 * 1000]; + } else if (decodingError) { + GDTCORLogDebug(@"There was a response decoding error: %@", decodingError); } pb_release(gdt_cct_LogResponse_fields, &logResponse); } + if (!futureUploadTime) { + GDTCORLogDebug(@"%@", @"CCT: The backend response failed to parse, so the next request " + @"won't occur until 15 minutes from now"); + // 15 minutes from now. + futureUploadTime = [GDTCORClock clockSnapshotInTheFuture:15 * 60 * 1000]; + } + switch (target) { + case kGDTCORTargetCCT: + self->_CCTNextUploadTime = futureUploadTime; + break; + + case kGDTCORTargetFLL: + // Falls through. + case kGDTCORTargetCSH: + self->_FLLNextUploadTime = futureUploadTime; + default: + break; + } + + // Only retry if one of these codes is returned, or there was an error. + if (error || ((NSHTTPURLResponse *)response).statusCode == 429 || + ((NSHTTPURLResponse *)response).statusCode == 503) { + [package retryDeliveryInTheFuture]; + } else { #if !NDEBUG - // Post a notification when in DEBUG mode to state how many packages were uploaded. Useful - // for validation during tests. - [[NSNotificationCenter defaultCenter] postNotificationName:GDTCCTUploadCompleteNotification - object:@(package.events.count)]; + // Post a notification when in DEBUG mode to state how many packages were uploaded. Useful + // for validation during tests. + [[NSNotificationCenter defaultCenter] postNotificationName:GDTCCTUploadCompleteNotification + object:@(package.events.count)]; #endif // #if !NDEBUG - GDTCORLogDebug("%@", @"CCT: package delivered"); - [package completeDelivery]; + GDTCORLogDebug(@"%@", @"CCT: package delivered"); + [package completeDelivery]; + } // End the background task if there was one. if (bgID != GDTCORBackgroundIdentifierInvalid) { @@ -163,46 +264,66 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader NSData *gzippedData = [GDTCCTCompressionHelper gzippedData:requestProtoData]; BOOL usingGzipData = gzippedData != nil && gzippedData.length < requestProtoData.length; NSData *dataToSend = usingGzipData ? gzippedData : requestProtoData; - NSURLRequest *request = [self constructRequestWithURL:serverURL data:dataToSend]; - GDTCORLogDebug("CCT: request created: %@", request); + NSURLRequest *request = [self constructRequestForTarget:target data:dataToSend]; + GDTCORLogDebug(@"CTT: request created: %@", request); self.currentTask = [self.uploaderSession uploadTaskWithRequest:request fromData:dataToSend completionHandler:completionHandler]; - GDTCORLogDebug("%@", @"CCT: The upload task is about to begin."); + GDTCORLogDebug(@"%@", @"CCT: The upload task is about to begin."); [self.currentTask resume]; }); } -- (BOOL)readyToUploadWithConditions:(GDTCORUploadConditions)conditions { +- (BOOL)readyToUploadTarget:(GDTCORTarget)target conditions:(GDTCORUploadConditions)conditions { __block BOOL result = NO; + NSSet *CSHEvents = [[GDTCCTPrioritizer sharedInstance] eventsForTarget:kGDTCORTargetCSH]; dispatch_sync(_uploaderQueue, ^{ + if (target == kGDTCORTargetCSH) { + result = CSHEvents.count > 0; + return; + } + if (self->_currentUploadPackage) { result = NO; - GDTCORLogDebug("%@", @"CCT: can't upload because a package is in flight"); + GDTCORLogDebug(@"%@", @"CCT: can't upload because a package is in flight"); return; } if (self->_currentTask) { result = NO; - GDTCORLogDebug("%@", @"CCT: can't upload because a task is in progress"); + GDTCORLogDebug(@"%@", @"CCT: can't upload because a task is in progress"); return; } if ((conditions & GDTCORUploadConditionHighPriority) == GDTCORUploadConditionHighPriority) { result = YES; - GDTCORLogDebug("%@", @"CCT: a high priority event is allowing an upload"); - return; - } else if (self->_nextUploadTime) { - result = [[GDTCORClock snapshot] isAfter:self->_nextUploadTime]; -#if !NDEBUG - if (result) { - GDTCORLogDebug("%@", @"CCT: can upload because the request wait time has transpired"); - } else { - GDTCORLogDebug("%@", @"CCT: can't upload because the backend asked to wait"); - } -#endif // !NDEBUG + GDTCORLogDebug(@"%@", @"CCT: a high priority event is allowing an upload"); return; } - GDTCORLogDebug("%@", @"CCT: can upload because nothing is preventing it"); + switch (target) { + case kGDTCORTargetCCT: + if (self->_CCTNextUploadTime) { + result = [[GDTCORClock snapshot] isAfter:self->_CCTNextUploadTime]; + } + break; + + case kGDTCORTargetFLL: + if (self->_FLLNextUploadTime) { + result = [[GDTCORClock snapshot] isAfter:self->_FLLNextUploadTime]; + } + break; + + default: + // The CSH backend should be handled above. + break; + } + if (result) { + GDTCORLogDebug(@"CCT: can upload to target %ld because the request wait time has transpired", + (long)target); + } else { + GDTCORLogDebug(@"CCT: can't upload to target %ld because the backend asked to wait", + (long)target); + } result = YES; + GDTCORLogDebug(@"CCT: can upload to target %ld because nothing is preventing it", (long)target); }); return result; } @@ -216,15 +337,14 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader */ - (nonnull NSData *)constructRequestProtoFromPackage:(GDTCORUploadPackage *)package { // Segment the log events by log type. - NSMutableDictionary<NSString *, NSMutableSet<GDTCORStoredEvent *> *> *logMappingIDToLogSet = + NSMutableDictionary<NSString *, NSMutableSet<GDTCOREvent *> *> *logMappingIDToLogSet = [[NSMutableDictionary alloc] init]; - [package.events - enumerateObjectsUsingBlock:^(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - NSMutableSet *logSet = logMappingIDToLogSet[event.mappingID]; - logSet = logSet ? logSet : [[NSMutableSet alloc] init]; - [logSet addObject:event]; - logMappingIDToLogSet[event.mappingID] = logSet; - }]; + [package.events enumerateObjectsUsingBlock:^(GDTCOREvent *_Nonnull event, BOOL *_Nonnull stop) { + NSMutableSet *logSet = logMappingIDToLogSet[event.mappingID]; + logSet = logSet ? logSet : [[NSMutableSet alloc] init]; + [logSet addObject:event]; + logMappingIDToLogSet[event.mappingID] = logSet; + }]; gdt_cct_BatchedLogRequest batchedLogRequest = GDTCCTConstructBatchedLogRequest(logMappingIDToLogSet); @@ -234,22 +354,44 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader return data ? data : [[NSData alloc] init]; } -/** Constructs a request to CCT given a URL and request body data. +/** Constructs a request to FLL given a URL and request body data. * - * @param URL The URL to send the request to. + * @param target The target backend to send the request to. * @param data The request body data. - * @return A new NSURLRequest ready to be sent to CCT. + * @return A new NSURLRequest ready to be sent to FLL. */ -- (NSURLRequest *)constructRequestWithURL:(NSURL *)URL data:(NSData *)data { - BOOL isGzipped = [GDTCCTCompressionHelper isGzipped:data]; +- (NSURLRequest *)constructRequestForTarget:(GDTCORTarget)target data:(NSData *)data { + NSURL *URL = [self serverURLForTarget:target]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; - [request setValue:@"application/x-protobuf" forHTTPHeaderField:@"Content-Type"]; - if (isGzipped) { + NSString *targetString; + switch (target) { + case kGDTCORTargetCCT: + targetString = @"cct"; + break; + + case kGDTCORTargetFLL: + targetString = @"fll"; + break; + + case kGDTCORTargetCSH: + targetString = @"csh"; + break; + + default: + targetString = @"unknown"; + break; + } + NSString *userAgent = + [NSString stringWithFormat:@"datatransport/%@ %@support/%@ apple/", kGDTCORVersion, + targetString, kGDTCCTSupportSDKVersion]; + if (target == kGDTCORTargetFLL || target == kGDTCORTargetCSH) { + [request setValue:[self FLLAndCSHAPIKey] forHTTPHeaderField:@"X-Goog-Api-Key"]; + } + if ([GDTCCTCompressionHelper isGzipped:data]) { [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; } + [request setValue:@"application/x-protobuf" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; - NSString *userAgent = [NSString stringWithFormat:@"datatransport/%@ cctsupport/%@ apple/", - kGDTCORVersion, kGDTCCTSupportSDKVersion]; [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; request.HTTPMethod = @"POST"; [request setHTTPBody:data]; @@ -275,4 +417,25 @@ NSNotificationName const GDTCCTUploadCompleteNotification = @"com.GDTCCTUploader }); } +#pragma mark - NSURLSessionDelegate + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *_Nullable))completionHandler { + if (!completionHandler) { + return; + } + if (response.statusCode == 302 || response.statusCode == 301) { + if ([request.URL isEqual:[self serverURLForTarget:kGDTCORTargetFLL]]) { + NSURLRequest *newRequest = [self constructRequestForTarget:kGDTCORTargetCCT + data:task.originalRequest.HTTPBody]; + completionHandler(newRequest); + } + } else { + completionHandler(request); + } +} + @end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCOREvent+NetworkConnectionInfo.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCOREvent+NetworkConnectionInfo.m new file mode 100644 index 000000000..4337f43c8 --- /dev/null +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTCOREvent+NetworkConnectionInfo.m @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h" + +#import <GoogleDataTransport/GDTCORConsoleLogger.h> + +NSString *const GDTCCTNeedsNetworkConnectionInfo = @"needs_network_connection_info"; + +NSString *const GDTCCTNetworkConnectionInfo = @"network_connection_info"; + +@implementation GDTCOREvent (CCTNetworkConnectionInfo) + +- (void)setNeedsNetworkConnectionInfoPopulated:(BOOL)needsNetworkConnectionInfoPopulated { + if (!needsNetworkConnectionInfoPopulated) { + self.customBytes = nil; + } else { + @try { + NSError *error; + self.customBytes = + [NSJSONSerialization dataWithJSONObject:@{GDTCCTNeedsNetworkConnectionInfo : @YES} + options:0 + error:&error]; + } @catch (NSException *exception) { + GDTCORLogDebug(@"Error when setting the event for needs_network_connection_info: %@", + exception); + } + } +} + +- (BOOL)needsNetworkConnectionInfoPopulated { + if (self.customBytes) { + @try { + NSError *error; + NSDictionary *bytesDict = [NSJSONSerialization JSONObjectWithData:self.customBytes + options:0 + error:&error]; + return bytesDict && !error && [bytesDict[GDTCCTNeedsNetworkConnectionInfo] boolValue]; + } @catch (NSException *exception) { + GDTCORLogDebug(@"Error when checking the event for needs_network_connection_info: %@", + exception); + } + } + return NO; +} + +- (void)setNetworkConnectionInfoData:(NSData *)networkConnectionInfoData { + @try { + NSError *error; + NSString *dataString = [networkConnectionInfoData base64EncodedStringWithOptions:0]; + if (dataString) { + self.customBytes = + [NSJSONSerialization dataWithJSONObject:@{GDTCCTNetworkConnectionInfo : dataString} + options:0 + error:&error]; + if (error) { + self.customBytes = nil; + GDTCORLogDebug(@"Error when setting an event's network_connection_info: %@", error); + } + } + } @catch (NSException *exception) { + GDTCORLogDebug(@"Error when setting an event's network_connection_info: %@", exception); + } +} + +- (nullable NSData *)networkConnectionInfoData { + if (self.customBytes) { + @try { + NSError *error; + NSDictionary *bytesDict = [NSJSONSerialization JSONObjectWithData:self.customBytes + options:0 + error:&error]; + NSString *base64Data = bytesDict[GDTCCTNetworkConnectionInfo]; + NSData *networkConnectionInfoData = [[NSData alloc] initWithBase64EncodedString:base64Data + options:0]; + if (error) { + GDTCORLogDebug(@"Error when getting an event's network_connection_info: %@", error); + return nil; + } else { + return networkConnectionInfoData; + } + } @catch (NSException *exception) { + GDTCORLogDebug(@"Error when getting an event's network_connection_info: %@", exception); + } + } + return nil; +} + +@end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLPrioritizer.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLPrioritizer.m deleted file mode 100644 index b52c10bd4..000000000 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLPrioritizer.m +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "GDTCCTLibrary/Private/GDTFLLPrioritizer.h" - -#import <GoogleDataTransport/GDTCORConsoleLogger.h> -#import <GoogleDataTransport/GDTCOREvent.h> -#import <GoogleDataTransport/GDTCORRegistrar.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> -#import <GoogleDataTransport/GDTCORTargets.h> - -const static int64_t kMillisPerDay = 8.64e+7; - -@implementation GDTFLLPrioritizer - -+ (void)load { - GDTFLLPrioritizer *prioritizer = [GDTFLLPrioritizer sharedInstance]; - [[GDTCORRegistrar sharedInstance] registerPrioritizer:prioritizer target:kGDTCORTargetFLL]; -} - -+ (instancetype)sharedInstance { - static GDTFLLPrioritizer *sharedInstance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[GDTFLLPrioritizer alloc] init]; - }); - return sharedInstance; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _queue = dispatch_queue_create("com.google.GDTFLLPrioritizer", DISPATCH_QUEUE_SERIAL); - _events = [[NSMutableSet alloc] init]; - } - return self; -} - -#pragma mark - GDTCORPrioritizer Protocol - -- (void)prioritizeEvent:(GDTCORStoredEvent *)event { - dispatch_async(_queue, ^{ - [self.events addObject:event]; - }); -} - -- (GDTCORUploadPackage *)uploadPackageWithConditions:(GDTCORUploadConditions)conditions { - GDTCORUploadPackage *package = [[GDTCORUploadPackage alloc] initWithTarget:kGDTCORTargetFLL]; - dispatch_sync(_queue, ^{ - NSSet<GDTCORStoredEvent *> *logEventsThatWillBeSent; - // A high priority event effectively flushes all events to be sent. - if ((conditions & GDTCORUploadConditionHighPriority) == GDTCORUploadConditionHighPriority) { - GDTCORLogDebug("%@", @"FLL: A high priority event is flushing all events."); - package.events = self.events; - GDTCORLogDebug("FLL: %lu events are in the upload package", - (unsigned long)package.events.count); - return; - } - - // If on wifi, upload logs that are ok to send on wifi. - if ((conditions & GDTCORUploadConditionWifiData) == GDTCORUploadConditionWifiData) { - logEventsThatWillBeSent = [self logEventsOkToSendOnWifi]; - GDTCORLogDebug("%@", @"FLL: events ok to send on wifi are being added to the upload package"); - } else { - logEventsThatWillBeSent = [self logEventsOkToSendOnMobileData]; - GDTCORLogDebug("%@", - @"FLL: events ok to send on mobile are being added to the upload package"); - } - - // If it's been > 24h since the last daily upload, upload logs with the daily QoS. - if (self.timeOfLastDailyUpload) { - int64_t millisSinceLastUpload = - [GDTCORClock snapshot].timeMillis - self.timeOfLastDailyUpload.timeMillis; - if (millisSinceLastUpload > kMillisPerDay) { - logEventsThatWillBeSent = - [logEventsThatWillBeSent setByAddingObjectsFromSet:[self logEventsOkToSendDaily]]; - GDTCORLogDebug("%@", @"FLL: events ok to send daily are being added to the upload package"); - } - } else { - self.timeOfLastDailyUpload = [GDTCORClock snapshot]; - logEventsThatWillBeSent = - [logEventsThatWillBeSent setByAddingObjectsFromSet:[self logEventsOkToSendDaily]]; - GDTCORLogDebug("%@", @"FLL: events ok to send daily are being added to the upload package"); - } - package.events = logEventsThatWillBeSent; - }); - GDTCORLogDebug("FLL: created an upload package with %ld events", - (unsigned long)package.events.count); - return package; -} - -#pragma mark - Private helper methods - -/** The different possible quality of service specifiers. High values indicate high priority. */ -typedef NS_ENUM(NSInteger, GDTFLLQoSTier) { - /** The QoS tier wasn't set, and won't ever be sent. */ - GDTFLLQoSDefault = 0, - - /** This event is internal telemetry data that should not be sent on its own if possible. */ - GDTFLLQoSTelemetry = 1, - - /** This event should be sent, but in a batch only roughly once per day. */ - GDTFLLQoSDaily = 2, - - /** This event should only be uploaded on wifi. */ - GDTFLLQoSWifiOnly = 5, -}; - -/** Converts a GDTCOREventQoS to a GDTFLLQoS tier. - * - * @param qosTier The GDTCOREventQoS value. - * @return A static NSNumber that represents the CCT QoS tier. - */ -FOUNDATION_STATIC_INLINE -NSNumber *GDTCCTQosTierFromGDTCOREventQosTier(GDTCOREventQoS qosTier) { - switch (qosTier) { - case GDTCOREventQoSWifiOnly: - return @(GDTFLLQoSWifiOnly); - break; - - case GDTCOREventQoSTelemetry: - // falls through. - case GDTCOREventQoSDaily: - return @(GDTFLLQoSDaily); - break; - - default: - return @(GDTFLLQoSDefault); - break; - } -} - -/** Returns a set of logs that are ok to upload whilst on mobile data. - * - * @note This should be called from a thread safe method. - * @return A set of logs that are ok to upload whilst on mobile data. - */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendOnMobileData { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTFLLQoSDefault)]; - }]; -} - -/** Returns a set of logs that are ok to upload whilst on wifi. - * - * @note This should be called from a thread safe method. - * @return A set of logs that are ok to upload whilst on wifi. - */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendOnWifi { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - NSNumber *qosTier = GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier); - return [qosTier isEqual:@(GDTFLLQoSDefault)] || [qosTier isEqual:@(GDTFLLQoSWifiOnly)] || - [qosTier isEqual:@(GDTFLLQoSDaily)]; - }]; -} - -/** Returns a set of logs that only should have a single upload attempt per day. - * - * @note This should be called from a thread safe method. - * @return A set of logs that are ok to upload only once per day. - */ -- (NSSet<GDTCORStoredEvent *> *)logEventsOkToSendDaily { - return [self.events - objectsPassingTest:^BOOL(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - return [GDTCCTQosTierFromGDTCOREventQosTier(event.qosTier) isEqual:@(GDTFLLQoSDaily)]; - }]; -} - -#pragma mark - GDTCORUploadPackageProtocol - -- (void)packageDelivered:(GDTCORUploadPackage *)package successful:(BOOL)successful { - dispatch_async(_queue, ^{ - NSSet<GDTCORStoredEvent *> *events = [package.events copy]; - for (GDTCORStoredEvent *event in events) { - [self.events removeObject:event]; - } - }); -} - -- (void)packageExpired:(GDTCORUploadPackage *)package { - [self packageDelivered:package successful:YES]; -} - -@end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLUploader.m b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLUploader.m deleted file mode 100644 index 6117d8878..000000000 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/GDTFLLUploader.m +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "GDTCCTLibrary/Private/GDTFLLUploader.h" - -#import <GoogleDataTransport/GDTCORConsoleLogger.h> -#import <GoogleDataTransport/GDTCORPlatform.h> -#import <GoogleDataTransport/GDTCORRegistrar.h> - -#import <nanopb/pb.h> -#import <nanopb/pb_decode.h> -#import <nanopb/pb_encode.h> - -#import "GDTCCTLibrary/Private/GDTCCTCompressionHelper.h" -#import "GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h" -#import "GDTCCTLibrary/Private/GDTFLLPrioritizer.h" - -#import "GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h" - -#ifdef GDTCCTSUPPORT_VERSION -#define STR(x) STR_EXPAND(x) -#define STR_EXPAND(x) #x -static NSString *const kGDTCCTSupportSDKVersion = @STR(GDTCCTSUPPORT_VERSION); -#else -static NSString *const kGDTCCTSupportSDKVersion = @"UNKNOWN"; -#endif // GDTCCTSUPPORT_VERSION - -#if !NDEBUG -NSNotificationName const GDTFLLUploadCompleteNotification = @"com.GDTFLLUploader.UploadComplete"; -#endif // #if !NDEBUG - -@interface GDTFLLUploader () <NSURLSessionDelegate> - -// Redeclared as readwrite. -@property(nullable, nonatomic, readwrite) NSURLSessionUploadTask *currentTask; - -@end - -@implementation GDTFLLUploader - -+ (void)load { - GDTFLLUploader *uploader = [GDTFLLUploader sharedInstance]; - [[GDTCORRegistrar sharedInstance] registerUploader:uploader target:kGDTCORTargetFLL]; -} - -+ (instancetype)sharedInstance { - static GDTFLLUploader *sharedInstance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[GDTFLLUploader alloc] init]; - }); - return sharedInstance; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _uploaderQueue = dispatch_queue_create("com.google.GDTFLLUploader", DISPATCH_QUEUE_SERIAL); - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - _uploaderSession = [NSURLSession sessionWithConfiguration:config - delegate:self - delegateQueue:nil]; - } - return self; -} - -- (NSURL *)defaultServerURL { - static NSURL *defaultServerURL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // These strings should be interleaved to construct the real URL. This is just to (hopefully) - // fool github URL scanning bots. - const char *p1 = "hts/frbslgigp.ogepscmv/ieo/eaybtho"; - const char *p2 = "tp:/ieaeogn-agolai.o/1frlglgc/aclg"; - const char defaultURL[69] = { - p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], p1[4], p2[4], - p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], p1[8], p2[8], p1[9], p2[9], - p1[10], p2[10], p1[11], p2[11], p1[12], p2[12], p1[13], p2[13], p1[14], p2[14], - p1[15], p2[15], p1[16], p2[16], p1[17], p2[17], p1[18], p2[18], p1[19], p2[19], - p1[20], p2[20], p1[21], p2[21], p1[22], p2[22], p1[23], p2[23], p1[24], p2[24], - p1[25], p2[25], p1[26], p2[26], p1[27], p2[27], p1[28], p2[28], p1[29], p2[29], - p1[30], p2[30], p1[31], p2[31], p1[32], p2[32], p1[33], p2[33], '\0'}; - defaultServerURL = [NSURL URLWithString:[NSString stringWithUTF8String:defaultURL]]; - }); - return defaultServerURL; -} - -- (NSString *)defaultAPIKey { - static NSString *defaultServerKey; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // These strings should be interleaved to construct the real key. - const char *p1 = "AzSBG0honD6A-PxV5nBc"; - const char *p2 = "Iay44Iwtu2vV0AOrz1C"; - const char defaultKey[40] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3], p2[3], - p1[4], p2[4], p1[5], p2[5], p1[6], p2[6], p1[7], p2[7], - p1[8], p2[8], p1[9], p2[9], p1[10], p2[10], p1[11], p2[11], - p1[12], p2[12], p1[13], p2[13], p1[14], p2[14], p1[15], p2[15], - p1[16], p2[16], p1[17], p2[17], p1[18], p2[18], p1[19], '\0'}; - defaultServerKey = [NSString stringWithUTF8String:defaultKey]; - }); - return defaultServerKey; -} - -- (void)uploadPackage:(GDTCORUploadPackage *)package { - __block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid; - bgID = [[GDTCORApplication sharedApplication] - beginBackgroundTaskWithName:@"GDTFLLUploader-upload" - expirationHandler:^{ - if (bgID != GDTCORBackgroundIdentifierInvalid) { - // Cancel the upload and complete delivery. - [self.currentTask cancel]; - [self.currentUploadPackage completeDelivery]; - - // End the background task. - [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; - } - }]; - - dispatch_async(_uploaderQueue, ^{ - if (self->_currentTask || self->_currentUploadPackage) { - GDTCORLogWarning(GDTCORMCWUploadFailed, @"%@", - @"An upload shouldn't be initiated with another in progress."); - return; - } - NSURL *serverURL = self.serverURL ? self.serverURL : [self defaultServerURL]; - - id completionHandler = ^(NSData *_Nullable data, NSURLResponse *_Nullable response, - NSError *_Nullable error) { - GDTCORLogDebug("%@", @"FLL: request completed"); - if (error) { - GDTCORLogWarning(GDTCORMCWUploadFailed, @"There was an error uploading events: %@", error); - } - NSError *decodingError; - if (data) { - gdt_cct_LogResponse logResponse = GDTCCTDecodeLogResponse(data, &decodingError); - if (!decodingError && logResponse.has_next_request_wait_millis) { - GDTCORLogDebug( - "FLL: The backend responded asking to not upload for %lld millis from now.", - logResponse.next_request_wait_millis); - self->_nextUploadTime = - [GDTCORClock clockSnapshotInTheFuture:logResponse.next_request_wait_millis]; - } else { - GDTCORLogDebug("%@", @"FLL: The CCT backend response failed to parse, so the next " - @"request won't occur until 15 minutes from now"); - // 15 minutes from now. - self->_nextUploadTime = [GDTCORClock clockSnapshotInTheFuture:15 * 60 * 1000]; - } - pb_release(gdt_cct_LogResponse_fields, &logResponse); - } - - // Only retry if one of these codes is returned. - if (((NSHTTPURLResponse *)response).statusCode == 429 || - ((NSHTTPURLResponse *)response).statusCode == 503) { - [package retryDeliveryInTheFuture]; - } else { -#if !NDEBUG - // Post a notification when in DEBUG mode to state how many packages were uploaded. Useful - // for validation during tests. - [[NSNotificationCenter defaultCenter] postNotificationName:GDTFLLUploadCompleteNotification - object:@(package.events.count)]; -#endif // #if !NDEBUG - GDTCORLogDebug("%@", @"FLL: package delivered"); - [package completeDelivery]; - } - - // End the background task if there was one. - if (bgID != GDTCORBackgroundIdentifierInvalid) { - [[GDTCORApplication sharedApplication] endBackgroundTask:bgID]; - bgID = GDTCORBackgroundIdentifierInvalid; - } - self.currentTask = nil; - self.currentUploadPackage = nil; - }; - self->_currentUploadPackage = package; - NSData *requestProtoData = - [self constructRequestProtoFromPackage:(GDTCORUploadPackage *)package]; - NSData *gzippedData = [GDTCCTCompressionHelper gzippedData:requestProtoData]; - BOOL usingGzipData = gzippedData != nil && gzippedData.length < requestProtoData.length; - NSData *dataToSend = usingGzipData ? gzippedData : requestProtoData; - NSURLRequest *request = [self constructRequestWithURL:serverURL data:dataToSend]; - GDTCORLogDebug("FLL: request created: %@", request); - self.currentTask = [self.uploaderSession uploadTaskWithRequest:request - fromData:dataToSend - completionHandler:completionHandler]; - GDTCORLogDebug("%@", @"FLL: The upload task is about to begin."); - [self.currentTask resume]; - }); -} - -- (BOOL)readyToUploadWithConditions:(GDTCORUploadConditions)conditions { - __block BOOL result = NO; - dispatch_sync(_uploaderQueue, ^{ - if (self->_currentUploadPackage) { - result = NO; - GDTCORLogDebug("%@", @"FLL: can't upload because a package is in flight"); - return; - } - if (self->_currentTask) { - result = NO; - GDTCORLogDebug("%@", @"FLL: can't upload because a task is in progress"); - return; - } - if ((conditions & GDTCORUploadConditionHighPriority) == GDTCORUploadConditionHighPriority) { - result = YES; - GDTCORLogDebug("%@", @"FLL: a high priority event is allowing an upload"); - return; - } else if (self->_nextUploadTime) { - result = [[GDTCORClock snapshot] isAfter:self->_nextUploadTime]; -#if !NDEBUG - if (result) { - GDTCORLogDebug("%@", @"FLL: can upload because the request wait time has transpired"); - } else { - GDTCORLogDebug("%@", @"FLL: can't upload because the backend asked to wait"); - } -#endif // !NDEBUG - return; - } - GDTCORLogDebug("%@", @"FLL: can upload because nothing is preventing it"); - result = YES; - }); - return result; -} - -#pragma mark - Private helper methods - -/** Constructs data given an upload package. - * - * @param package The upload package used to construct the request proto bytes. - * @return Proto bytes representing a gdt_cct_LogRequest object. - */ -- (nonnull NSData *)constructRequestProtoFromPackage:(GDTCORUploadPackage *)package { - // Segment the log events by log type. - NSMutableDictionary<NSString *, NSMutableSet<GDTCORStoredEvent *> *> *logMappingIDToLogSet = - [[NSMutableDictionary alloc] init]; - [package.events - enumerateObjectsUsingBlock:^(GDTCORStoredEvent *_Nonnull event, BOOL *_Nonnull stop) { - NSMutableSet *logSet = logMappingIDToLogSet[event.mappingID]; - logSet = logSet ? logSet : [[NSMutableSet alloc] init]; - [logSet addObject:event]; - logMappingIDToLogSet[event.mappingID] = logSet; - }]; - - gdt_cct_BatchedLogRequest batchedLogRequest = - GDTCCTConstructBatchedLogRequest(logMappingIDToLogSet); - - NSData *data = GDTCCTEncodeBatchedLogRequest(&batchedLogRequest); - pb_release(gdt_cct_BatchedLogRequest_fields, &batchedLogRequest); - return data ? data : [[NSData alloc] init]; -} - -/** Constructs a request to FLL given a URL and request body data. - * - * @param URL The URL to send the request to. - * @param data The request body data. - * @return A new NSURLRequest ready to be sent to FLL. - */ -- (NSURLRequest *)constructRequestWithURL:(NSURL *)URL data:(NSData *)data { - const UInt8 *bytes = (const UInt8 *)data.bytes; - // From https://en.wikipedia.org/wiki/Gzip, gzip's magic number is 1f 8b. - BOOL isGzipped = (data.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b); - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; - [request setValue:[self defaultAPIKey] forHTTPHeaderField:@"X-Goog-Api-Key"]; - [request setValue:@"application/x-protobuf" forHTTPHeaderField:@"Content-Type"]; - if (isGzipped) { - [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; - } - [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; - NSString *userAgent = [NSString stringWithFormat:@"datatransport/%@ fllsupport/%@ apple/", - kGDTCORVersion, kGDTCCTSupportSDKVersion]; - [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; - request.HTTPMethod = @"POST"; - [request setHTTPBody:data]; - return request; -} - -#pragma mark - GDTCORUploadPackageProtocol - -- (void)packageExpired:(GDTCORUploadPackage *)package { - dispatch_async(_uploaderQueue, ^{ - [self.currentTask cancel]; - self.currentTask = nil; - self.currentUploadPackage = nil; - }); -} - -#pragma mark - GDTCORLifecycleProtocol - -- (void)appWillTerminate:(GDTCORApplication *)application { - dispatch_sync(_uploaderQueue, ^{ - [self.currentTask cancel]; - [self.currentUploadPackage completeDelivery]; - }); -} - -#pragma mark - NSURLSessionDelegate - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task - willPerformHTTPRedirection:(NSHTTPURLResponse *)response - newRequest:(NSURLRequest *)request - completionHandler:(void (^)(NSURLRequest *_Nullable))completionHandler { - if (!completionHandler) { - return; - } - if (response.statusCode == 302 || response.statusCode == 301) { - NSURLRequest *newRequest = [self constructRequestWithURL:request.URL - data:task.originalRequest.HTTPBody]; - completionHandler(newRequest); - } else { - completionHandler(request); - } -} - -@end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h index 08081cc72..8051f0e25 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTNanopbHelpers.h @@ -16,7 +16,8 @@ #import <Foundation/Foundation.h> -#import <GoogleDataTransport/GDTCORStoredEvent.h> +#import <GoogleDataTransport/GDTCOREvent.h> +#import <GoogleDataTransport/GDTCORReachability.h> #import "GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h" @@ -26,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN /** Converts an NSString* to a pb_bytes_array_t*. * - * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * @note calloc is called in this method. Ensure that pb_release is called on this or the parent. * * @param string The string to convert. * @return A newly allocated array of bytes representing the UTF8 encoding of the string. @@ -35,7 +36,7 @@ pb_bytes_array_t *GDTCCTEncodeString(NSString *string); /** Converts an NSData to a pb_bytes_array_t*. * - * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * @note calloc is called in this method. Ensure that pb_release is called on this or the parent. * * @param data The data to convert. * @return A newly allocated array of bytes with [data bytes] copied into it. @@ -56,31 +57,31 @@ NSData *GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogReque /** Constructs a gdt_cct_BatchedLogRequest given sets of events segemented by mapping ID. * - * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * @note calloc is called in this method. Ensure that pb_release is called on this or the parent. * * @param logMappingIDToLogSet A map of mapping IDs to sets of events to convert into a batch. * @return A newly created gdt_cct_BatchedLogRequest. */ FOUNDATION_EXPORT gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest( - NSDictionary<NSString *, NSSet<GDTCORStoredEvent *> *> *logMappingIDToLogSet); + NSDictionary<NSString *, NSSet<GDTCOREvent *> *> *logMappingIDToLogSet); /** Constructs a log request given a log source and a set of events. * - * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * @note calloc is called in this method. Ensure that pb_release is called on this or the parent. * @param logSource The CCT log source to put into the log request. * @param logSet The set of events to send in this log request. */ FOUNDATION_EXPORT -gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, NSSet<GDTCORStoredEvent *> *logSet); +gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, NSSet<GDTCOREvent *> *logSet); -/** Constructs a gdt_cct_LogEvent given a GDTCORStoredEvent*. +/** Constructs a gdt_cct_LogEvent given a GDTCOREvent*. * - * @param event The GDTCORStoredEvent to convert. + * @param event The GDTCOREvent to convert. * @return The new gdt_cct_LogEvent object. */ FOUNDATION_EXPORT -gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTCORStoredEvent *event); +gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTCOREvent *event); /** Constructs a gdt_cct_ClientInfo representing the client device. * @@ -96,11 +97,26 @@ gdt_cct_ClientInfo GDTCCTConstructClientInfo(void); FOUNDATION_EXPORT gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo(void); +/** Constructs the data of a gdt_cct_NetworkConnectionInfo representing the client nework connection + * information. + * + * @return The data of a gdt_cct_NetworkConnectionInfo object. + */ +FOUNDATION_EXPORT +NSData *GDTCCTConstructNetworkConnectionInfoData(void); + +/** Return a gdt_cct_NetworkConnectionInfo_MobileSubtype representing the client + * + * @return The gdt_cct_NetworkConnectionInfo_MobileSubtype. + */ +FOUNDATION_EXPORT +gdt_cct_NetworkConnectionInfo_MobileSubtype GDTCCTNetworkConnectionInfoNetworkMobileSubtype(void); + #pragma mark - CCT object decoders /** Decodes a gdt_cct_LogResponse given proto bytes. * - * @note malloc is called in this method. Ensure that pb_release is called on the return value. + * @note calloc is called in this method. Ensure that pb_release is called on the return value. * * @param data The proto bytes of the gdt_cct_LogResponse. * @param error An error that will be populated if something went wrong during decoding. diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTPrioritizer.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTPrioritizer.h index 1908a31b8..7b7921b3b 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTPrioritizer.h +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTPrioritizer.h @@ -17,21 +17,23 @@ #import <Foundation/Foundation.h> #import <GoogleDataTransport/GDTCORClock.h> +#import <GoogleDataTransport/GDTCOREvent.h> #import <GoogleDataTransport/GDTCORPrioritizer.h> +#import <GoogleDataTransport/GDTCORTargets.h> NS_ASSUME_NONNULL_BEGIN /** Manages the prioritization of events from GoogleDataTransport. */ -@interface GDTCCTPrioritizer : NSObject <GDTCORPrioritizer> +@interface GDTCCTPrioritizer : NSObject <NSSecureCoding, GDTCORPrioritizer> /** The queue on which this prioritizer operates. */ @property(nonatomic) dispatch_queue_t queue; -/** All log events that have been processed by this prioritizer. */ -@property(nonatomic) NSMutableSet<GDTCORStoredEvent *> *events; +/** The most recent attempted upload of CCT daily uploaded logs. */ +@property(nonatomic) GDTCORClock *CCTTimeOfLastDailyUpload; -/** The most recent attempted upload of daily uploaded logs. */ -@property(nonatomic) GDTCORClock *timeOfLastDailyUpload; +/** The most recent attempted upload of FLL daily uploaded logs*/ +@property(nonatomic) GDTCORClock *FLLOfLastDailyUpload; /** Creates and/or returns the singleton instance of this class. * @@ -39,6 +41,13 @@ NS_ASSUME_NONNULL_BEGIN */ + (instancetype)sharedInstance; +/** Returns a set of events that have been prioritized for the given target. + * + * @param target The target to check. CCT, FLL, and CSH are currently supported by this class. + * @return The set of events prioritized so far. + */ +- (nullable NSSet *)eventsForTarget:(GDTCORTarget)target; + NS_ASSUME_NONNULL_END @end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTUploader.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTUploader.h index ba95a201b..cdc322d4e 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTUploader.h +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCCTUploader.h @@ -31,9 +31,6 @@ extern NSNotificationName const GDTCCTUploadCompleteNotification; /** The queue on which all CCT uploading will occur. */ @property(nonatomic, readonly) dispatch_queue_t uploaderQueue; -/** The server URL to upload to. Look at .m for the default value. */ -@property(nonatomic) NSURL *serverURL; - /** The URL session that will attempt upload. */ @property(nonatomic, readonly) NSURLSession *uploaderSession; @@ -43,8 +40,17 @@ extern NSNotificationName const GDTCCTUploadCompleteNotification; /** Current upload package. */ @property(nullable, nonatomic) GDTCORUploadPackage *currentUploadPackage; -/** The next upload time. */ -@property(nullable, nonatomic) GDTCORClock *nextUploadTime; +/** The next upload time for the CCT target. */ +@property(nullable, nonatomic) GDTCORClock *CCTNextUploadTime; + +/** The next upload time for the FLL target. */ +@property(nullable, nonatomic) GDTCORClock *FLLNextUploadTime; + +#if !NDEBUG +/** An upload URL used across all targets. For testing only. */ +@property(nullable, nonatomic) NSURL *testServerURL; + +#endif // !NDEBUG /** Creates and/or returns the singleton instance of this class. * diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h new file mode 100644 index 000000000..1c1512d50 --- /dev/null +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <GoogleDataTransport/GDTCOREvent.h> + +NS_ASSUME_NONNULL_BEGIN + +/** A string sets in customBytes as a key paired to @YES if current event needs to + * populate network connection info data, @NO otherwise. + */ +FOUNDATION_EXPORT NSString *const GDTCCTNeedsNetworkConnectionInfo; + +/** A string sets in customBytes as a key paired to the network connection info data + * of current event. + */ +FOUNDATION_EXPORT NSString *const GDTCCTNetworkConnectionInfo; + +/** A category that uses the customBytes property of a GDTCOREvent to store network connection info. + */ +@interface GDTCOREvent (CCTNetworkConnectionInfo) + +/** If YES, needs the network connection info field set during prioritization. + * @note Uses the GDTCOREvent customBytes property. + */ +@property(nonatomic) BOOL needsNetworkConnectionInfoPopulated; + +/** The network connection info as collected at the time of the event. + * @note Uses the GDTCOREvent customBytes property. + */ +@property(nullable, nonatomic) NSData *networkConnectionInfoData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLPrioritizer.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLPrioritizer.h deleted file mode 100644 index b7622f2ea..000000000 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLPrioritizer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -#import <GoogleDataTransport/GDTCORClock.h> -#import <GoogleDataTransport/GDTCORPrioritizer.h> - -NS_ASSUME_NONNULL_BEGIN - -/** Manages the prioritization of events from GoogleDataTransport. */ -@interface GDTFLLPrioritizer : NSObject <GDTCORPrioritizer> - -/** The queue on which this prioritizer operates. */ -@property(nonatomic) dispatch_queue_t queue; - -/** All log events that have been processed by this prioritizer. */ -@property(nonatomic) NSMutableSet<GDTCORStoredEvent *> *events; - -/** The most recent attempted upload of daily uploaded logs. */ -@property(nonatomic) GDTCORClock *timeOfLastDailyUpload; - -/** Creates and/or returns the singleton instance of this class. - * - * @return The singleton instance of this class. - */ -+ (instancetype)sharedInstance; - -NS_ASSUME_NONNULL_END - -@end diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLUploader.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLUploader.h deleted file mode 100644 index 7df08ab82..000000000 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLUploader.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2019 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -#import <GoogleDataTransport/GDTCORUploader.h> - -NS_ASSUME_NONNULL_BEGIN - -#if !NDEBUG -/** A notification fired when uploading is complete, detailing the number of events uploaded. */ -extern NSNotificationName const GDTFLLUploadCompleteNotification; -#endif // #if !NDEBUG - -/** Class capable of uploading events to the CCT backend. */ -@interface GDTFLLUploader : NSObject <GDTCORUploader> - -/** The queue on which all CCT uploading will occur. */ -@property(nonatomic, readonly) dispatch_queue_t uploaderQueue; - -/** The server URL to upload to. Look at .m for the default value. */ -@property(nonatomic) NSURL *serverURL; - -/** The URL session that will attempt upload. */ -@property(nonatomic, readonly) NSURLSession *uploaderSession; - -/** The current upload task. */ -@property(nullable, nonatomic, readonly) NSURLSessionUploadTask *currentTask; - -/** Current upload package. */ -@property(nullable, nonatomic) GDTCORUploadPackage *currentUploadPackage; - -/** The next upload time. */ -@property(nullable, nonatomic) GDTCORClock *nextUploadTime; - -/** Creates and/or returns the singleton instance of this class. - * - * @return The singleton instance of this class. - */ -+ (instancetype)sharedInstance; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.c b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.c index 95846e6d6..836031afb 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.c +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.c @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9.3 */ +/* Generated by nanopb-0.3.9.5 */ #include "cct.nanopb.h" diff --git a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h index a6d4cfb82..e9d9a12c9 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h +++ b/ios/Pods/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Protogen/nanopb/cct.nanopb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9.3 */ +/* Generated by nanopb-0.3.9.5 */ #ifndef PB_GDT_CCT_CCT_NANOPB_H_INCLUDED #define PB_GDT_CCT_CCT_NANOPB_H_INCLUDED diff --git a/ios/Pods/GoogleDataTransportCCTSupport/README.md b/ios/Pods/GoogleDataTransportCCTSupport/README.md index 5097a89ae..1747cca7a 100644 --- a/ios/Pods/GoogleDataTransportCCTSupport/README.md +++ b/ios/Pods/GoogleDataTransportCCTSupport/README.md @@ -1,10 +1,24 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +83,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +134,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +149,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +180,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +191,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,32 +217,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst +### tvOS, macOS, watchOS and Catalyst Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). During app setup in the console, you may get to a step that mentions something like "Checking if the app -has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/Catalyst. +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. **It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Crashlytics' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -252,3 +272,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/GULSecureCoding.m b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/GULSecureCoding.m index ebfb80845..25d5d1998 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/GULSecureCoding.m +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/GULSecureCoding.m @@ -23,7 +23,7 @@ NSString *const kGULSecureCodingError = @"GULSecureCodingError"; error:(NSError **)outError { id object; #if __has_builtin(__builtin_available) - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { object = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:data error:outError]; } else #endif // __has_builtin(__builtin_available) @@ -62,7 +62,7 @@ NSString *const kGULSecureCodingError = @"GULSecureCodingError"; + (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError { NSData *archiveData; #if __has_builtin(__builtin_available) - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { archiveData = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:outError]; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h similarity index 88% rename from ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.h rename to ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h index 5548e3e12..dc01a8368 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h @@ -21,7 +21,15 @@ NS_ASSUME_NONNULL_BEGIN /// The class provides a convenient abstraction on top of the iOS Keychain API to save data. -@interface FIRSecureStorage : NSObject +@interface GULKeychainStorage : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** Initializes the keychain storage with Keychain Service name. + * @param service A Keychain Service name that will be used to store and retrieve objects. See also + * `kSecAttrService`. + */ +- (instancetype)initWithService:(NSString *)service; /** * Get an object by key. diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h new file mode 100644 index 000000000..de4bef2fb --- /dev/null +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString *const kGULKeychainUtilsErrorDomain; + +/// Helper functions to access Keychain. +@interface GULKeychainUtils : NSObject + +/** Fetches a keychain item data matching to the provided query. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemCopyMatching` for + * details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns Data for the first Keychain Item matching the provided query or `nil` if there is not + * such an item (`outError` will be `nil` in this case) or an error occurred. + */ ++ (nullable NSData *)getItemWithQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError; + +/** Stores data to a Keychain Item matching to the provided query. An existing Keychain Item + * matching the query parameters will be updated or a new will be created. + * @param item A Keychain Item data to store. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemAdd` and + * `SecItemUpdate` for details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns `YES` when data was successfully stored, `NO` otherwise. + */ ++ (BOOL)setItem:(NSData *)item + withQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError; + +/** Removes a Keychain Item matching to the provided query. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemDelete` for + * details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns `YES` if the item was removed successfully or doesn't exist, `NO` otherwise. + */ ++ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.m b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainStorage.m similarity index 58% rename from ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.m rename to ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainStorage.m index 543e84815..3777724bd 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.m +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainStorage.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRSecureStorage.h" +#import <GoogleUtilities/GULKeychainStorage.h> #import <Security/Security.h> #if __has_include(<FBLPromises/FBLPromises.h>) @@ -23,32 +23,32 @@ #import "FBLPromises.h" #endif -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsKeychainUtils.h" +#import <GoogleUtilities/GULKeychainUtils.h> +#import <GoogleUtilities/GULSecureCoding.h> -@interface FIRSecureStorage () +@interface GULKeychainStorage () @property(nonatomic, readonly) dispatch_queue_t keychainQueue; @property(nonatomic, readonly) dispatch_queue_t inMemoryCacheQueue; @property(nonatomic, readonly) NSString *service; @property(nonatomic, readonly) NSCache<NSString *, id<NSSecureCoding>> *inMemoryCache; @end -@implementation FIRSecureStorage +@implementation GULKeychainStorage -- (instancetype)init { +- (instancetype)initWithService:(NSString *)service { NSCache *cache = [[NSCache alloc] init]; // Cache up to 5 installations. cache.countLimit = 5; - return [self initWithService:@"com.firebase.FIRInstallations.installations" cache:cache]; + return [self initWithService:service cache:cache]; } - (instancetype)initWithService:(NSString *)service cache:(NSCache *)cache { self = [super init]; if (self) { - _keychainQueue = dispatch_queue_create( - "com.firebase.FIRInstallations.FIRSecureStorage.Keychain", DISPATCH_QUEUE_SERIAL); - _inMemoryCacheQueue = dispatch_queue_create( - "com.firebase.FIRInstallations.FIRSecureStorage.InMemoryCache", DISPATCH_QUEUE_SERIAL); + _keychainQueue = + dispatch_queue_create("com.gul.KeychainStorage.Keychain", DISPATCH_QUEUE_SERIAL); + _inMemoryCacheQueue = + dispatch_queue_create("com.gul.KeychainStorage.InMemoryCache", DISPATCH_QUEUE_SERIAL); _service = [service copy]; _inMemoryCache = cache; } @@ -91,12 +91,12 @@ // Then store the object to the keychain. NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup]; NSError *error; - NSData *encodedObject = [self archiveDataForObject:object error:&error]; + NSData *encodedObject = [GULSecureCoding archivedDataWithRootObject:object error:&error]; if (!encodedObject) { return error; } - if (![FIRInstallationsKeychainUtils setItem:encodedObject withQuery:query error:&error]) { + if (![GULKeychainUtils setItem:encodedObject withQuery:query error:&error]) { return error; } @@ -115,7 +115,7 @@ NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup]; NSError *error; - if (![FIRInstallationsKeychainUtils removeItemWithQuery:query error:&error]) { + if (![GULKeychainUtils removeItemWithQuery:query error:&error]) { return error; } @@ -129,29 +129,28 @@ objectClass:(Class)objectClass accessGroup:(nullable NSString *)accessGroup { // Look for the object in the keychain. - return [FBLPromise onQueue:self.keychainQueue - do:^id { - NSDictionary *query = [self keychainQueryWithKey:key - accessGroup:accessGroup]; - NSError *error; - NSData *encodedObject = - [FIRInstallationsKeychainUtils getItemWithQuery:query error:&error]; + return [FBLPromise + onQueue:self.keychainQueue + do:^id { + NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup]; + NSError *error; + NSData *encodedObject = [GULKeychainUtils getItemWithQuery:query error:&error]; - if (error) { - return error; - } - if (!encodedObject) { - return nil; - } - id object = [self unarchivedObjectOfClass:objectClass - fromData:encodedObject - error:&error]; - if (error) { - return error; - } + if (error) { + return error; + } + if (!encodedObject) { + return nil; + } + id object = [GULSecureCoding unarchivedObjectOfClass:objectClass + fromData:encodedObject + error:&error]; + if (error) { + return error; + } - return object; - }] + return object; + }] .thenOn(self.inMemoryCacheQueue, ^id<NSSecureCoding> _Nullable(id<NSSecureCoding> _Nullable object) { // Save object to the in-memory cache if exists and return the object. @@ -190,66 +189,4 @@ return query; } -- (nullable NSData *)archiveDataForObject:(id<NSSecureCoding>)object error:(NSError **)outError { - NSData *archiveData; - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - archiveData = [NSKeyedArchiver archivedDataWithRootObject:object - requiringSecureCoding:YES - error:&error]; - if (error && outError) { - *outError = [FIRInstallationsErrorUtil keyedArchiverErrorWithError:error]; - } - } else { - @try { - NSMutableData *data = [NSMutableData data]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; -#pragma clang diagnostic pop - archiver.requiresSecureCoding = YES; - - [archiver encodeObject:object forKey:NSKeyedArchiveRootObjectKey]; - [archiver finishEncoding]; - - archiveData = [data copy]; - } @catch (NSException *exception) { - if (outError) { - *outError = [FIRInstallationsErrorUtil keyedArchiverErrorWithException:exception]; - } - } - } - - return archiveData; -} - -- (nullable id)unarchivedObjectOfClass:(Class)class - fromData:(NSData *)data - error:(NSError **)outError { - id object; - if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { - NSError *error; - object = [NSKeyedUnarchiver unarchivedObjectOfClass:class fromData:data error:&error]; - if (error && outError) { - *outError = [FIRInstallationsErrorUtil keyedArchiverErrorWithError:error]; - } - } else { - @try { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; -#pragma clang diagnostic pop - unarchiver.requiresSecureCoding = YES; - - object = [unarchiver decodeObjectOfClass:class forKey:NSKeyedArchiveRootObjectKey]; - } @catch (NSException *exception) { - if (outError) { - *outError = [FIRInstallationsErrorUtil keyedArchiverErrorWithException:exception]; - } - } - } - - return object; -} - @end diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.m b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainUtils.m similarity index 77% rename from ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.m rename to ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainUtils.m index 51da86a8a..673b6c4f9 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.m +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainUtils.m @@ -14,11 +14,11 @@ * limitations under the License. */ -#import "FIRInstallationsKeychainUtils.h" +#import <GoogleUtilities/GULKeychainUtils.h> -#import "FIRInstallationsErrorUtil.h" +NSString *const kGULKeychainUtilsErrorDomain = @"com.gul.keychain.ErrorDomain"; -@implementation FIRInstallationsKeychainUtils +@implementation GULKeychainUtils + (nullable NSData *)getItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError { @@ -45,8 +45,7 @@ } } else { if (outError) { - *outError = [FIRInstallationsErrorUtil keychainErrorWithFunction:@"SecItemCopyMatching" - status:status]; + *outError = [self keychainErrorWithFunction:@"SecItemCopyMatching" status:status]; } } return nil; @@ -82,7 +81,7 @@ NSString *function = existingItem ? @"SecItemUpdate" : @"SecItemAdd"; if (outError) { - *outError = [FIRInstallationsErrorUtil keychainErrorWithFunction:function status:status]; + *outError = [self keychainErrorWithFunction:function status:status]; } return NO; } @@ -98,10 +97,17 @@ } if (outError) { - *outError = [FIRInstallationsErrorUtil keychainErrorWithFunction:@"SecItemDelete" - status:status]; + *outError = [self keychainErrorWithFunction:@"SecItemDelete" status:status]; } return NO; } +#pragma mark - Errors + ++ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status { + NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status]; + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey : failureReason}; + return [NSError errorWithDomain:kGULKeychainUtilsErrorDomain code:0 userInfo:userInfo]; +} + @end diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m index 6442941d1..8b1ac6ab0 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m @@ -201,7 +201,11 @@ static BOOL HasEmbeddedMobileProvision() { } + (BOOL)isSimulator { -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_SIMULATOR + return YES; +#elif TARGET_OS_MACCATALYST + return NO; +#elif TARGET_OS_IOS || TARGET_OS_TV NSString *platform = [GULAppEnvironmentUtil deviceModel]; return [platform isEqual:@"x86_64"] || [platform isEqual:@"i386"]; #elif TARGET_OS_OSX diff --git a/ios/Pods/GoogleUtilities/README.md b/ios/Pods/GoogleUtilities/README.md index 5097a89ae..b04a27099 100644 --- a/ios/Pods/GoogleUtilities/README.md +++ b/ios/Pods/GoogleUtilities/README.md @@ -1,10 +1,19 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +# Firebase iOS Open Source Development + [![Actions Status][gh-auth-badge]][gh-actions] + [![Actions Status][gh-core-badge]][gh-actions] + [![Actions Status][gh-datatransport-badge]][gh-actions] + [![Actions Status][gh-dynamiclinks-badge]][gh-actions] + [![Actions Status][gh-firebasepod-badge]][gh-actions] + [![Actions Status][gh-firestore-badge]][gh-actions] + [![Actions Status][gh-interop-badge]][gh-actions] + [![Actions Status][gh-messaging-badge]][gh-actions] + [![Actions Status][gh-storage-badge]][gh-actions] + [![Actions Status][gh-symbolcollision-badge]][gh-actions] + [![Actions Status][gh-zip-badge]][gh-actions] + [![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +This repository contains all Firebase iOS SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,6 +78,10 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least @@ -116,8 +129,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +144,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,7 +175,7 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database @@ -173,7 +186,7 @@ To run the Database Integration tests, make your database authentication rules ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,32 +212,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst +### tvOS, macOS, watchOS and Catalyst Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). During app setup in the console, you may get to a step that mentions something like "Checking if the app -has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/Catalyst. +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. **It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Crashlytics' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -252,3 +267,16 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/Headers/Private/BugsnagReactNative/BugsnagPlugin.h b/ios/Pods/Headers/Private/BugsnagReactNative/BugsnagPlugin.h new file mode 120000 index 000000000..764dc5181 --- /dev/null +++ b/ios/Pods/Headers/Private/BugsnagReactNative/BugsnagPlugin.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncSocket.h b/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncSocket.h new file mode 120000 index 000000000..747af0852 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncSocket.h @@ -0,0 +1 @@ +../../../CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncUdpSocket.h b/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncUdpSocket.h new file mode 120000 index 000000000..0312a7010 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaAsyncSocket/GCDAsyncUdpSocket.h @@ -0,0 +1 @@ +../../../CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/evdns.h b/ios/Pods/Headers/Private/CocoaLibEvent/evdns.h new file mode 120000 index 000000000..79308f581 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/evdns.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evdns.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event.h b/ios/Pods/Headers/Private/CocoaLibEvent/event.h new file mode 120000 index 000000000..f0df150ba --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer.h new file mode 120000 index 000000000..47fa55c24 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/buffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer_compat.h new file mode 120000 index 000000000..932561ee4 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/buffer_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/buffer_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent.h new file mode 120000 index 000000000..ec2ad6fd9 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_compat.h new file mode 120000 index 000000000..ba6b1d368 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_ssl.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_ssl.h new file mode 120000 index 000000000..6cc5ad2be --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_ssl.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_ssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_struct.h new file mode 120000 index 000000000..7c5c4d0f8 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/bufferevent_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns.h new file mode 120000 index 000000000..0e50dbc28 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_compat.h new file mode 120000 index 000000000..801ab18f9 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_struct.h new file mode 120000 index 000000000..b68af8f1c --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/dns_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/event-config.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event-config.h new file mode 120000 index 000000000..0e5284539 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event-config.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event-config.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/event.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event.h new file mode 120000 index 000000000..e70176082 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_compat.h new file mode 120000 index 000000000..58bd2433e --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_struct.h new file mode 120000 index 000000000..b4b36c5c9 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/event_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/http.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http.h new file mode 120000 index 000000000..6857f0c07 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_compat.h new file mode 120000 index 000000000..169ba3d6e --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_struct.h new file mode 120000 index 000000000..51de61806 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/http_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/keyvalq_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/keyvalq_struct.h new file mode 120000 index 000000000..6b487cd2e --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/keyvalq_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/keyvalq_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/listener.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/listener.h new file mode 120000 index 000000000..9f0d36c2d --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/listener.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/listener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc.h new file mode 120000 index 000000000..15bdd9fc7 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_compat.h new file mode 120000 index 000000000..c3c4960c3 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_struct.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_struct.h new file mode 120000 index 000000000..e9ab6b44d --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/rpc_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag.h new file mode 120000 index 000000000..4faf06d83 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/tag.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag_compat.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag_compat.h new file mode 120000 index 000000000..6f727474c --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/tag_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/tag_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/thread.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/thread.h new file mode 120000 index 000000000..963efacbf --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/thread.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/thread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/util.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/util.h new file mode 120000 index 000000000..e2809bf9a --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/util.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/util.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/event2/visibility.h b/ios/Pods/Headers/Private/CocoaLibEvent/event2/visibility.h new file mode 120000 index 000000000..4fcd1f001 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/event2/visibility.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/visibility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/evhttp.h b/ios/Pods/Headers/Private/CocoaLibEvent/evhttp.h new file mode 120000 index 000000000..c4f8edbc5 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/evhttp.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evhttp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/evrpc.h b/ios/Pods/Headers/Private/CocoaLibEvent/evrpc.h new file mode 120000 index 000000000..3c47809a5 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/evrpc.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evrpc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/CocoaLibEvent/evutil.h b/ios/Pods/Headers/Private/CocoaLibEvent/evutil.h new file mode 120000 index 000000000..51d5519e8 --- /dev/null +++ b/ios/Pods/Headers/Private/CocoaLibEvent/evutil.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evutil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXAV/EXAudioRecordingPermissionRequester.h b/ios/Pods/Headers/Private/EXAV/EXAudioRecordingPermissionRequester.h new file mode 120000 index 000000000..5b8d1af04 --- /dev/null +++ b/ios/Pods/Headers/Private/EXAV/EXAudioRecordingPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-av/ios/EXAV/EXAudioRecordingPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderInterface.h b/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderInterface.h deleted file mode 120000 index e070a7851..000000000 --- a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderInterface.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/Interfaces/EXAppLoaderInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderProvider.h b/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderProvider.h deleted file mode 120000 index 8036fe182..000000000 --- a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppLoaderProvider.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/EXAppLoaderProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppRecordInterface.h b/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppRecordInterface.h deleted file mode 120000 index 9ed829b6b..000000000 --- a/ios/Pods/Headers/Private/EXAppLoaderProvider/EXAppRecordInterface.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/Interfaces/EXAppRecordInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXImageLoader/EXImageLoader.h b/ios/Pods/Headers/Private/EXImageLoader/EXImageLoader.h new file mode 120000 index 000000000..473e0322c --- /dev/null +++ b/ios/Pods/Headers/Private/EXImageLoader/EXImageLoader.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-image-loader/ios/EXImageLoader/EXImageLoader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXKeepAwake/EXKeepAwake.h b/ios/Pods/Headers/Private/EXKeepAwake/EXKeepAwake.h new file mode 120000 index 000000000..9eeda1fc7 --- /dev/null +++ b/ios/Pods/Headers/Private/EXKeepAwake/EXKeepAwake.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-keep-awake/ios/EXKeepAwake/EXKeepAwake.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXLocalAuthentication/EXLocalAuthentication.h b/ios/Pods/Headers/Private/EXLocalAuthentication/EXLocalAuthentication.h new file mode 120000 index 000000000..44a177344 --- /dev/null +++ b/ios/Pods/Headers/Private/EXLocalAuthentication/EXLocalAuthentication.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-local-authentication/ios/EXLocalAuthentication/EXLocalAuthentication.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXAudioRecordingPermissionRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXAudioRecordingPermissionRequester.h deleted file mode 120000 index d4ca7c2bc..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXAudioRecordingPermissionRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXAudioRecordingPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXCalendarRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXCalendarRequester.h deleted file mode 120000 index b0fe45f50..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXCalendarRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCalendarRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXCameraPermissionRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXCameraPermissionRequester.h deleted file mode 120000 index de574f490..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXCameraPermissionRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCameraPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXCameraRollRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXCameraRollRequester.h deleted file mode 120000 index d062f5f9c..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXCameraRollRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCameraRollRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXContactsRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXContactsRequester.h deleted file mode 120000 index 43bb947f3..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXContactsRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXContactsRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXLocationRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXLocationRequester.h deleted file mode 120000 index de4b28b28..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXLocationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXLocationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXRemindersRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXRemindersRequester.h deleted file mode 120000 index 04a73bfb1..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXRemindersRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXRemindersRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationPermissionRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationPermissionRequester.h new file mode 120000 index 000000000..b78040b03 --- /dev/null +++ b/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-permissions/ios/EXPermissions/Requesters/RemoteNotification/EXRemoteNotificationPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationRequester.h deleted file mode 120000 index 3f9869b58..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXRemoteNotificationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXRemoteNotificationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXSystemBrightnessRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXSystemBrightnessRequester.h deleted file mode 120000 index 9bf70ebc5..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXSystemBrightnessRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXSystemBrightnessRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationPermissionRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationPermissionRequester.h new file mode 120000 index 000000000..0c452886b --- /dev/null +++ b/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-permissions/ios/EXPermissions/Requesters/UserNotification/EXUserNotificationPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationRequester.h b/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationRequester.h deleted file mode 120000 index aab5405f0..000000000 --- a/ios/Pods/Headers/Private/EXPermissions/EXUserNotificationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXUserNotificationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstallations/FIRInstallationsKeychainUtils.h b/ios/Pods/Headers/Private/FirebaseInstallations/FIRInstallationsKeychainUtils.h deleted file mode 120000 index 9f5946a10..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstallations/FIRInstallationsKeychainUtils.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRInstallationsKeychainUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstallations/FIRSecureStorage.h b/ios/Pods/Headers/Private/FirebaseInstallations/FIRSecureStorage.h deleted file mode 120000 index 84762a97a..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstallations/FIRSecureStorage.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstallations/FirebaseInstallations/Source/Library/SecureStorage/FIRSecureStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRIMessageCode.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRIMessageCode.h deleted file mode 120000 index a9bf393c3..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRIMessageCode.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRIMessageCode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID+Private.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID+Private.h deleted file mode 120000 index 2b8d5e3f4..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID+Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h deleted file mode 120000 index 2a09cf0df..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Public/FIRInstanceID.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAPNSInfo.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAPNSInfo.h deleted file mode 120000 index 57a15c565..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAPNSInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAPNSInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthKeyChain.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthKeyChain.h deleted file mode 120000 index 63536e037..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthKeyChain.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthService.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthService.h deleted file mode 120000 index 66d2c18d7..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDAuthService.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDAuthService.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDBackupExcludedPlist.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDBackupExcludedPlist.h deleted file mode 120000 index be4834c1e..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDBackupExcludedPlist.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences+Internal.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences+Internal.h deleted file mode 120000 index 20b3c4b6b..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences+Internal.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h deleted file mode 120000 index 441846a98..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences_Private.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences_Private.h deleted file mode 120000 index f24e6ade0..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinPreferences_Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinPreferences_Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinService.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinService.h deleted file mode 120000 index 7ecde091f..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinService.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinService.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinStore.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinStore.h deleted file mode 120000 index c34c406c6..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCheckinStore.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCheckinStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCombinedHandler.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCombinedHandler.h deleted file mode 120000 index b7e07cafb..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDCombinedHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDConstants.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDConstants.h deleted file mode 120000 index 9dbb12e9c..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDConstants.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDConstants.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDDefines.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDDefines.h deleted file mode 120000 index 23b93078e..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDDefines.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDDefines.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDKeychain.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDKeychain.h deleted file mode 120000 index 25d182adf..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDKeychain.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDKeychain.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDLogger.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDLogger.h deleted file mode 120000 index f0b3b4aa9..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDLogger.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStore.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStore.h deleted file mode 120000 index 09c2baa47..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStore.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStringEncoding.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStringEncoding.h deleted file mode 120000 index 5ad4f905b..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDStringEncoding.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDStringEncoding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenDeleteOperation.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenDeleteOperation.h deleted file mode 120000 index 88ac35b8f..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenDeleteOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenFetchOperation.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenFetchOperation.h deleted file mode 120000 index 293fdcd89..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenFetchOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenInfo.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenInfo.h deleted file mode 120000 index 58095c6c2..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenManager.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenManager.h deleted file mode 120000 index 126f9ee14..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation+Private.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation+Private.h deleted file mode 120000 index 8e2b033df..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation+Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation.h deleted file mode 120000 index 41450eced..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenStore.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenStore.h deleted file mode 120000 index 31a07293a..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDTokenStore.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDTokenStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDURLQueryItem.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDURLQueryItem.h deleted file mode 120000 index f1eef9872..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDURLQueryItem.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDURLQueryItem.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDUtilities.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDUtilities.h deleted file mode 120000 index 47dcc92d7..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDUtilities.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDUtilities.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDVersionUtilities.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDVersionUtilities.h deleted file mode 120000 index 28c77eef5..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceIDVersionUtilities.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/FIRInstanceIDVersionUtilities.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID_Private.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID_Private.h deleted file mode 120000 index 3997968c8..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID_Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Private/FIRInstanceID_Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/FirebaseInstanceID.h b/ios/Pods/Headers/Private/FirebaseInstanceID/FirebaseInstanceID.h deleted file mode 120000 index fdf0d912e..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/FirebaseInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Public/FirebaseInstanceID.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FirebaseInstanceID/NSError+FIRInstanceID.h b/ios/Pods/Headers/Private/FirebaseInstanceID/NSError+FIRInstanceID.h deleted file mode 120000 index 60fa497e8..000000000 --- a/ios/Pods/Headers/Private/FirebaseInstanceID/NSError+FIRInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/NSError+FIRInstanceID.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h new file mode 120000 index 000000000..86f25220e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/bignum-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum.h new file mode 120000 index 000000000..52eb949cc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/bignum.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/bignum.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/cached-powers.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/cached-powers.h new file mode 120000 index 000000000..60ac107d2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/cached-powers.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/cached-powers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/diy-fp.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/diy-fp.h new file mode 120000 index 000000000..75887c71d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/diy-fp.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/diy-fp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/double-conversion.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/double-conversion.h new file mode 120000 index 000000000..b4f9e9fd6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/double-conversion.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/double-conversion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fast-dtoa.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fast-dtoa.h new file mode 120000 index 000000000..789a9bf3d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fast-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/fast-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h new file mode 120000 index 000000000..e78574950 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/fixed-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/ieee.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/ieee.h new file mode 120000 index 000000000..661051029 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/ieee.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/ieee.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/strtod.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/strtod.h new file mode 120000 index 000000000..54f180c3d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/strtod.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/strtod.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/utils.h b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/utils.h new file mode 120000 index 000000000..84aa4cc85 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-DoubleConversion/double-conversion/utils.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/utils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray-inl.h new file mode 120000 index 000000000..3dfb84d57 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashArray-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray.h new file mode 120000 index 000000000..4e0bbb685 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashArray.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashArray.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap-inl.h new file mode 120000 index 000000000..d26ec377c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashMap-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap.h new file mode 120000 index 000000000..9f2d31a32 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicHashMap.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h new file mode 120000 index 000000000..38326393b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicIntrusiveLinkedList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicLinkedList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicLinkedList.h new file mode 120000 index 000000000..b8cc03425 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicLinkedList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicLinkedList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicUnorderedMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicUnorderedMap.h new file mode 120000 index 000000000..6c6f46910 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/AtomicUnorderedMap.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicUnorderedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Benchmark.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Benchmark.h new file mode 120000 index 000000000..ca92b4785 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Benchmark.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Benchmark.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Bits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Bits.h new file mode 120000 index 000000000..a2c1e91eb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Bits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/CPortability.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/CPortability.h new file mode 120000 index 000000000..ff09af640 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/CPortability.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CPortability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken-inl.h new file mode 120000 index 000000000..c203959fd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CancellationToken-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken.h new file mode 120000 index 000000000..85f5be659 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/CancellationToken.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CancellationToken.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Chrono.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Chrono.h new file mode 120000 index 000000000..a13793257 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Chrono.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Chrono.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ClockGettimeWrappers.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ClockGettimeWrappers.h new file mode 120000 index 000000000..46c59f913 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ClockGettimeWrappers.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ClockGettimeWrappers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentBitSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentBitSet.h new file mode 120000 index 000000000..56d054436 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentBitSet.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentBitSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList-inl.h new file mode 120000 index 000000000..ea825d22f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentSkipList-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList.h new file mode 120000 index 000000000..0e4faf7d0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConcurrentSkipList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentSkipList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ConstexprMath.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConstexprMath.h new file mode 120000 index 000000000..837c3fbb6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ConstexprMath.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConstexprMath.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Conv.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Conv.h new file mode 120000 index 000000000..359e50e5b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Conv.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Conv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/CppAttributes.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/CppAttributes.h new file mode 120000 index 000000000..498f5d696 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/CppAttributes.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CppAttributes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/CpuId.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/CpuId.h new file mode 120000 index 000000000..802357b15 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/CpuId.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CpuId.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/DefaultKeepAliveExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/DefaultKeepAliveExecutor.h new file mode 120000 index 000000000..7982f82bc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/DefaultKeepAliveExecutor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DefaultKeepAliveExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Demangle.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Demangle.h new file mode 120000 index 000000000..84ea44f5f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Demangle.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Demangle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/DiscriminatedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/DiscriminatedPtr.h new file mode 120000 index 000000000..c26286890 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/DiscriminatedPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DiscriminatedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/DynamicConverter.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/DynamicConverter.h new file mode 120000 index 000000000..219d85b34 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/DynamicConverter.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DynamicConverter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Exception.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Exception.h new file mode 120000 index 000000000..7fc5dd0ec --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Exception.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Exception.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionString.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionString.h new file mode 120000 index 000000000..01679998b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper-inl.h new file mode 120000 index 000000000..34f080fde --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionWrapper-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper.h new file mode 120000 index 000000000..a3f711ce4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ExceptionWrapper.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Executor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Executor.h new file mode 120000 index 000000000..1719b8ff5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Executor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Executor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Expected.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Expected.h new file mode 120000 index 000000000..c24a83f3a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Expected.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Expected.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FBString.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FBString.h new file mode 120000 index 000000000..61ee3dbe0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FBString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FBString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FBVector.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FBVector.h new file mode 120000 index 000000000..270424bcc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FBVector.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FBVector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/File.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/File.h new file mode 120000 index 000000000..816be15d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/File.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/File.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FileUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FileUtil.h new file mode 120000 index 000000000..1198acb7d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FileUtil.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FileUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Fingerprint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Fingerprint.h new file mode 120000 index 000000000..bdd133571 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Fingerprint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Fingerprint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FixedString.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FixedString.h new file mode 120000 index 000000000..11f1efeff --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FixedString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FixedString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Format-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Format-inl.h new file mode 120000 index 000000000..b2e6873cc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Format-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Format-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Format.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Format.h new file mode 120000 index 000000000..76450d1ca --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Format.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Format.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatArg.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatArg.h new file mode 120000 index 000000000..54caf03b5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatArg.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FormatArg.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatTraits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatTraits.h new file mode 120000 index 000000000..6c5cfb2a8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/FormatTraits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FormatTraits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Function.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Function.h new file mode 120000 index 000000000..df6385768 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Function.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Function.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/GLog.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/GLog.h new file mode 120000 index 000000000..ad4494d99 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/GLog.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/GLog.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/GroupVarint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/GroupVarint.h new file mode 120000 index 000000000..b7f6163a5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/GroupVarint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/GroupVarint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Hash.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Hash.h new file mode 120000 index 000000000..bec889062 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Hash.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Hash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddress.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddress.h new file mode 120000 index 000000000..55d02498d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressException.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressException.h new file mode 120000 index 000000000..2653e47c5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressException.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV4.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV4.h new file mode 120000 index 000000000..7ae51e99c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV4.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressV4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV6.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV6.h new file mode 120000 index 000000000..24a48a575 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IPAddressV6.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressV6.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Indestructible.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Indestructible.h new file mode 120000 index 000000000..8092a0606 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Indestructible.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Indestructible.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IndexedMemPool.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IndexedMemPool.h new file mode 120000 index 000000000..6f24f6802 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IndexedMemPool.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IndexedMemPool.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/IntrusiveList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/IntrusiveList.h new file mode 120000 index 000000000..8c7c12ce3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/IntrusiveList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IntrusiveList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Lazy.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Lazy.h new file mode 120000 index 000000000..1a946884d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Lazy.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Lazy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Likely.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Likely.h new file mode 120000 index 000000000..60476602e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Likely.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Likely.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/LockTraits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/LockTraits.h new file mode 120000 index 000000000..2143a864f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/LockTraits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/LockTraits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCPipeline.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCPipeline.h new file mode 120000 index 000000000..b4ec9f063 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCPipeline.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MPMCPipeline.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCQueue.h new file mode 120000 index 000000000..951db2ce4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MPMCQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MacAddress.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MacAddress.h new file mode 120000 index 000000000..e39b2d426 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MacAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MacAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MapUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MapUtil.h new file mode 120000 index 000000000..99f992156 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MapUtil.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MapUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Math.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Math.h new file mode 120000 index 000000000..6ade47a33 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Math.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Math.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Memory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Memory.h new file mode 120000 index 000000000..160741115 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Memory.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Memory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroLock.h new file mode 120000 index 000000000..6636a6eaf --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MicroLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroSpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroSpinLock.h new file mode 120000 index 000000000..da757ff47 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MicroSpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MicroSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/MoveWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/MoveWrapper.h new file mode 120000 index 000000000..5b6c2c667 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/MoveWrapper.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MoveWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Optional.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Optional.h new file mode 120000 index 000000000..560db199a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Optional.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Optional.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Overload.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Overload.h new file mode 120000 index 000000000..b29defd5d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Overload.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Overload.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/PackedSyncPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/PackedSyncPtr.h new file mode 120000 index 000000000..b86c857dc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/PackedSyncPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/PackedSyncPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Padded.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Padded.h new file mode 120000 index 000000000..b3ca3f4a7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Padded.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Padded.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly-inl.h new file mode 120000 index 000000000..625771815 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Poly-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly.h new file mode 120000 index 000000000..073a154fe --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Poly.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Poly.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/PolyException.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/PolyException.h new file mode 120000 index 000000000..a923071e3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/PolyException.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/PolyException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Portability.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Portability.h new file mode 120000 index 000000000..2b9f0afca --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Portability.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Portability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Preprocessor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Preprocessor.h new file mode 120000 index 000000000..15bd95020 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Preprocessor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Preprocessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ProducerConsumerQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ProducerConsumerQueue.h new file mode 120000 index 000000000..a1ee75a64 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ProducerConsumerQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ProducerConsumerQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/RWSpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/RWSpinLock.h new file mode 120000 index 000000000..b78f9e6e8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/RWSpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/RWSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Random-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Random-inl.h new file mode 120000 index 000000000..ee0dbba11 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Random-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Random-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Random.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Random.h new file mode 120000 index 000000000..adefd8050 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Random.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Random.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Range.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Range.h new file mode 120000 index 000000000..3bfa432d9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Range.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Range.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Replaceable.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Replaceable.h new file mode 120000 index 000000000..67cf6113e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Replaceable.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Replaceable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ScopeGuard.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ScopeGuard.h new file mode 120000 index 000000000..9112fc651 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ScopeGuard.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ScopeGuard.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/SharedMutex.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/SharedMutex.h new file mode 120000 index 000000000..28337811c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/SharedMutex.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SharedMutex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton-inl.h new file mode 120000 index 000000000..bbf1da9cd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Singleton-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton.h new file mode 120000 index 000000000..eaa7fc447 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Singleton.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Singleton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/SingletonThreadLocal.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/SingletonThreadLocal.h new file mode 120000 index 000000000..146138cf6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/SingletonThreadLocal.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SingletonThreadLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/SocketAddress.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/SocketAddress.h new file mode 120000 index 000000000..f0a89f7ec --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/SocketAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SocketAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/SpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/SpinLock.h new file mode 120000 index 000000000..52a66ef95 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/SpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/String-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/String-inl.h new file mode 120000 index 000000000..699a0f69e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/String-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/String-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/String.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/String.h new file mode 120000 index 000000000..73d4249c1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/String.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Subprocess.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Subprocess.h new file mode 120000 index 000000000..950cd53d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Subprocess.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Subprocess.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Synchronized.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Synchronized.h new file mode 120000 index 000000000..06ffbcc44 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Synchronized.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Synchronized.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/SynchronizedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/SynchronizedPtr.h new file mode 120000 index 000000000..02236f64f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/SynchronizedPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SynchronizedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadCachedInt.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadCachedInt.h new file mode 120000 index 000000000..c42848910 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadCachedInt.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ThreadCachedInt.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadLocal.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadLocal.h new file mode 120000 index 000000000..ac21ca746 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ThreadLocal.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ThreadLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/TimeoutQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/TimeoutQueue.h new file mode 120000 index 000000000..6c0f3ac0e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/TimeoutQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/TimeoutQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/TokenBucket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/TokenBucket.h new file mode 120000 index 000000000..66a08797b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/TokenBucket.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/TokenBucket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Traits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Traits.h new file mode 120000 index 000000000..768244ec4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Traits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Traits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Try-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Try-inl.h new file mode 120000 index 000000000..b2a6668e1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Try-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Try-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Try.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Try.h new file mode 120000 index 000000000..b5c049719 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Try.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Try.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/UTF8String.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/UTF8String.h new file mode 120000 index 000000000..89cb3a59a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/UTF8String.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/UTF8String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Unicode.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Unicode.h new file mode 120000 index 000000000..8f6263d56 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Unicode.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Unicode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Unit.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Unit.h new file mode 120000 index 000000000..4448713c1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Unit.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Unit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri-inl.h new file mode 120000 index 000000000..b15ecbc22 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Uri-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri.h new file mode 120000 index 000000000..a3f58f808 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Uri.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Uri.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Utility.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Utility.h new file mode 120000 index 000000000..55394812f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Utility.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Utility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/Varint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/Varint.h new file mode 120000 index 000000000..e2df677a2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/Varint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Varint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/VirtualExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/VirtualExecutor.h new file mode 120000 index 000000000..9e1ffd04a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/VirtualExecutor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/VirtualExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Conv.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Conv.h new file mode 120000 index 000000000..675b15e47 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Conv.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/chrono/Conv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Hardware.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Hardware.h new file mode 120000 index 000000000..a993fb62d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/chrono/Hardware.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/chrono/Hardware.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h new file mode 120000 index 000000000..7d3273c45 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/AtomicSharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CacheLocality.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CacheLocality.h new file mode 120000 index 000000000..5b183b5f2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CacheLocality.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/CacheLocality.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h new file mode 120000 index 000000000..26f4880ac --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/ConcurrentHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h new file mode 120000 index 000000000..e7e22f6b5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h new file mode 120000 index 000000000..77250fb1b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h new file mode 120000 index 000000000..34b95c0f3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/UnboundedQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/UnboundedQueue.h new file mode 120000 index 000000000..37f466a8f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/concurrency/UnboundedQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/UnboundedQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Access.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Access.h new file mode 120000 index 000000000..be0025143 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Access.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Access.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Array.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Array.h new file mode 120000 index 000000000..4e8e5ac78 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Array.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Array.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/BitIterator.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/BitIterator.h new file mode 120000 index 000000000..ddc23381c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/BitIterator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/BitIterator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Enumerate.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Enumerate.h new file mode 120000 index 000000000..6956f7088 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Enumerate.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Enumerate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/EvictingCacheMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/EvictingCacheMap.h new file mode 120000 index 000000000..035695056 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/EvictingCacheMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/EvictingCacheMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map-fwd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map-fwd.h new file mode 120000 index 000000000..bfcdce152 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Map-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map.h new file mode 120000 index 000000000..c903d4a0d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Map.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Map.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set-fwd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set-fwd.h new file mode 120000 index 000000000..d9a9232f5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Set-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set.h new file mode 120000 index 000000000..79ef8ad54 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/F14Set.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Set.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach-inl.h new file mode 120000 index 000000000..01190ebf5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Foreach-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach.h new file mode 120000 index 000000000..f21d0a53c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Foreach.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Foreach.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h new file mode 120000 index 000000000..7623cb35e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess.h new file mode 120000 index 000000000..b7a51c2f5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/HeterogeneousAccess.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/HeterogeneousAccess.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Iterator.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Iterator.h new file mode 120000 index 000000000..68c982271 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Iterator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Iterator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Merge.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Merge.h new file mode 120000 index 000000000..38fe76960 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/Merge.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Merge.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/SparseByteSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/SparseByteSet.h new file mode 120000 index 000000000..59b048180 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/SparseByteSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/SparseByteSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/BitIteratorDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/BitIteratorDetail.h new file mode 120000 index 000000000..091e53115 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/BitIteratorDetail.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/BitIteratorDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Defaults.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Defaults.h new file mode 120000 index 000000000..c6c7107ca --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Defaults.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Defaults.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h new file mode 120000 index 000000000..3223e0324 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14MapFallback.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14MapFallback.h new file mode 120000 index 000000000..e6f1428a3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14MapFallback.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14MapFallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Mask.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Mask.h new file mode 120000 index 000000000..c23e8cf52 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Mask.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Mask.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Policy.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Policy.h new file mode 120000 index 000000000..ba46bfa3d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Policy.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Policy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14SetFallback.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14SetFallback.h new file mode 120000 index 000000000..0c878d9d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14SetFallback.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14SetFallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Table.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Table.h new file mode 120000 index 000000000..d7153a66d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/F14Table.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Table.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/Util.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/Util.h new file mode 120000 index 000000000..1efb2a9a3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/container/detail/Util.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/Util.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AsyncTrace.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AsyncTrace.h new file mode 120000 index 000000000..355d5ebb5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AsyncTrace.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AsyncTrace.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtFork.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtFork.h new file mode 120000 index 000000000..fe3b3e785 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtFork.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtFork.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicHashUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicHashUtils.h new file mode 120000 index 000000000..bb1f8b49f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicHashUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtomicHashUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h new file mode 120000 index 000000000..adad9b742 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Demangle.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Demangle.h new file mode 120000 index 000000000..d79647581 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Demangle.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Demangle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h new file mode 120000 index 000000000..59cac25ee --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FileUtilDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FileUtilDetail.h new file mode 120000 index 000000000..0e13a0bff --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FileUtilDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/FileUtilDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FingerprintPolynomial.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FingerprintPolynomial.h new file mode 120000 index 000000000..dec5e5ffe --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/FingerprintPolynomial.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/FingerprintPolynomial.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex-inl.h new file mode 120000 index 000000000..afc21a520 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Futex-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex.h new file mode 120000 index 000000000..d4e2148c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Futex.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Futex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/GroupVarintDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/GroupVarintDetail.h new file mode 120000 index 000000000..6495b47a1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/GroupVarintDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/GroupVarintDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddress.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddress.h new file mode 120000 index 000000000..31a8eb818 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddress.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/IPAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddressSource.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddressSource.h new file mode 120000 index 000000000..1121df078 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/IPAddressSource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/IPAddressSource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Iterators.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Iterators.h new file mode 120000 index 000000000..3a682cabf --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Iterators.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Iterators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MPMCPipelineDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MPMCPipelineDetail.h new file mode 120000 index 000000000..40bf59a9b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MPMCPipelineDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/MPMCPipelineDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MemoryIdler.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MemoryIdler.h new file mode 120000 index 000000000..119aafe09 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/MemoryIdler.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/MemoryIdler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/PolyDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/PolyDetail.h new file mode 120000 index 000000000..c955a4f2e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/PolyDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/PolyDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeCommon.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeCommon.h new file mode 120000 index 000000000..1f78cb8b0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeCommon.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/RangeCommon.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeSse42.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeSse42.h new file mode 120000 index 000000000..4f65acbed --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/RangeSse42.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/RangeSse42.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Singleton.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Singleton.h new file mode 120000 index 000000000..043bee1e9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Singleton.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Singleton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SingletonStackTrace.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SingletonStackTrace.h new file mode 120000 index 000000000..08502fa09 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SingletonStackTrace.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SingletonStackTrace.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SlowFingerprint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SlowFingerprint.h new file mode 120000 index 000000000..aa9454f81 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SlowFingerprint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SlowFingerprint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SocketFastOpen.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SocketFastOpen.h new file mode 120000 index 000000000..b0b1288f0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/SocketFastOpen.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SocketFastOpen.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Sse.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Sse.h new file mode 120000 index 000000000..00694df38 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/Sse.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Sse.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/StaticSingletonManager.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/StaticSingletonManager.h new file mode 120000 index 000000000..21c322088 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/StaticSingletonManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/StaticSingletonManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/ThreadLocalDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/ThreadLocalDetail.h new file mode 120000 index 000000000..f7baceba6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/ThreadLocalDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/ThreadLocalDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TurnSequencer.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TurnSequencer.h new file mode 120000 index 000000000..607497385 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TurnSequencer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/TurnSequencer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TypeList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TypeList.h new file mode 120000 index 000000000..f2b82b6b8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/TypeList.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/TypeList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/UniqueInstance.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/UniqueInstance.h new file mode 120000 index 000000000..3a9c94d24 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/detail/UniqueInstance.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/UniqueInstance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic-inl.h new file mode 120000 index 000000000..bfa10a1af --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/dynamic-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic.h new file mode 120000 index 000000000..57208baf1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/dynamic.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/dynamic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Async.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Async.h new file mode 120000 index 000000000..fa81a1a7a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Async.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/Async.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h new file mode 120000 index 000000000..c1c042619 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Codel.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Codel.h new file mode 120000 index 000000000..175f6b090 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/Codel.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/Codel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/DrivableExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/DrivableExecutor.h new file mode 120000 index 000000000..f6d28ff5d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/DrivableExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/DrivableExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h new file mode 120000 index 000000000..a1e708c2c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h new file mode 120000 index 000000000..f4d02c126 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority.h new file mode 120000 index 000000000..2c825b516 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ExecutorWithPriority.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ExecutorWithPriority.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FiberIOExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FiberIOExecutor.h new file mode 120000 index 000000000..747ccb890 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FiberIOExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/FiberIOExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FutureExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FutureExecutor.h new file mode 120000 index 000000000..3499c732a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/FutureExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/FutureExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalExecutor.h new file mode 120000 index 000000000..b04fcb830 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/GlobalExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalThreadPoolList.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalThreadPoolList.h new file mode 120000 index 000000000..4a590a461 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/GlobalThreadPoolList.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/GlobalThreadPoolList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOExecutor.h new file mode 120000 index 000000000..cefed6342 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOObjectCache.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOObjectCache.h new file mode 120000 index 000000000..dcfa4098b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOObjectCache.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOObjectCache.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h new file mode 120000 index 000000000..67e716f1a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/InlineExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/InlineExecutor.h new file mode 120000 index 000000000..99320e836 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/InlineExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/InlineExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ManualExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ManualExecutor.h new file mode 120000 index 000000000..bd6e0b980 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ManualExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ManualExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h new file mode 120000 index 000000000..01bc0e29d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/QueuedImmediateExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ScheduledExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ScheduledExecutor.h new file mode 120000 index 000000000..a2b442343 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ScheduledExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ScheduledExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SequencedExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SequencedExecutor.h new file mode 120000 index 000000000..9c0eb9b44 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SequencedExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SequencedExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SerialExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SerialExecutor.h new file mode 120000 index 000000000..917e441ac --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SerialExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SerialExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h new file mode 120000 index 000000000..d7ca17d26 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SoftRealTimeExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadPoolExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadPoolExecutor.h new file mode 120000 index 000000000..fe6b8f044 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadedExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadedExecutor.h new file mode 120000 index 000000000..40c41d7d0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/ThreadedExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ThreadedExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimedDrivableExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimedDrivableExecutor.h new file mode 120000 index 000000000..cb9290df9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimedDrivableExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/TimedDrivableExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h new file mode 120000 index 000000000..7d87a4b84 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h new file mode 120000 index 000000000..350ad9447 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/BlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h new file mode 120000 index 000000000..28c628da4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h new file mode 120000 index 000000000..406aafdac --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h new file mode 120000 index 000000000..95933f248 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h new file mode 120000 index 000000000..af062db5f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h new file mode 120000 index 000000000..15316c9ec --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h new file mode 120000 index 000000000..a546b458a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h new file mode 120000 index 000000000..be8064864 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h new file mode 120000 index 000000000..439a4e746 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h new file mode 120000 index 000000000..c65968565 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AutoTimer.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AutoTimer.h new file mode 120000 index 000000000..0b18b552f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/AutoTimer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/AutoTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/BitVectorCoding.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/BitVectorCoding.h new file mode 120000 index 000000000..39f266ab0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/BitVectorCoding.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/BitVectorCoding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Bits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Bits.h new file mode 120000 index 000000000..08f0c3cf3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Bits.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/CodingDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/CodingDetail.h new file mode 120000 index 000000000..c083a3095 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/CodingDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/CodingDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser-inl.h new file mode 120000 index 000000000..e5fae0e98 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/DynamicParser-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser.h new file mode 120000 index 000000000..ca3a70a45 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/DynamicParser.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/DynamicParser.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EliasFanoCoding.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EliasFanoCoding.h new file mode 120000 index 000000000..98d5f261f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EliasFanoCoding.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EliasFanoCoding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EnvUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EnvUtil.h new file mode 120000 index 000000000..9d180d8f7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EnvUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EnvUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EventCount.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EventCount.h new file mode 120000 index 000000000..3a4c39cc2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/EventCount.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EventCount.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ExecutionObserver.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ExecutionObserver.h new file mode 120000 index 000000000..59fc754fc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ExecutionObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ExecutionObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h new file mode 120000 index 000000000..72cc7456d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FunctionScheduler.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FunctionScheduler.h new file mode 120000 index 000000000..35974cd21 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FunctionScheduler.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FunctionScheduler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FutureDAG.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FutureDAG.h new file mode 120000 index 000000000..d7c1cc4eb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/FutureDAG.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FutureDAG.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Instructions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Instructions.h new file mode 120000 index 000000000..982a09b6f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Instructions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Instructions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JSONSchema.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JSONSchema.h new file mode 120000 index 000000000..728a215db --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JSONSchema.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JSONSchema.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h new file mode 120000 index 000000000..e73efefaa --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h new file mode 120000 index 000000000..dea7d214e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h new file mode 120000 index 000000000..2507702a4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/LockFreeRingBuffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/MasterPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/MasterPtr.h new file mode 120000 index 000000000..5615efe49 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/MasterPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/MasterPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/NestedCommandLineApp.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/NestedCommandLineApp.h new file mode 120000 index 000000000..e741e726e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/NestedCommandLineApp.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/NestedCommandLineApp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ProgramOptions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ProgramOptions.h new file mode 120000 index 000000000..35803d9af --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ProgramOptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ProgramOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h new file mode 120000 index 000000000..988d2f574 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet.h new file mode 120000 index 000000000..40cc2e33e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/QuotientMultiSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/QuotientMultiSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h new file mode 120000 index 000000000..c6131c105 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h new file mode 120000 index 000000000..3dd31c14c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h new file mode 120000 index 000000000..e440da868 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Select64.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Select64.h new file mode 120000 index 000000000..071f13260 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/Select64.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Select64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h new file mode 120000 index 000000000..b0cbb0a61 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h new file mode 120000 index 000000000..918b0189c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StampedPtr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StampedPtr.h new file mode 120000 index 000000000..4f26102de --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StampedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StampedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedCommon.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedCommon.h new file mode 120000 index 000000000..bd030e00c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedCommon.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedCommon.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedMap.h new file mode 120000 index 000000000..42fcf0364 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedSet.h new file mode 120000 index 000000000..c5ed6f064 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h new file mode 120000 index 000000000..dd94e73cc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h new file mode 120000 index 000000000..831fb5d6c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TLRefCount.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TLRefCount.h new file mode 120000 index 000000000..da5c820aa --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TLRefCount.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TLRefCount.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TestUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TestUtil.h new file mode 120000 index 000000000..b6584d17e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TestUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TestUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h new file mode 120000 index 000000000..c6965151d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h new file mode 120000 index 000000000..f223bd7f5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFD.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFD.h new file mode 120000 index 000000000..40e0435a5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFD.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TimerFD.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h new file mode 120000 index 000000000..d2fee4f6a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TupleOps.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TupleOps.h new file mode 120000 index 000000000..bca1e87ae --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/experimental/TupleOps.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TupleOps.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/ApplyTuple.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/ApplyTuple.h new file mode 120000 index 000000000..a6b82813a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/ApplyTuple.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/ApplyTuple.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Invoke.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Invoke.h new file mode 120000 index 000000000..85d8f478f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Invoke.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/Invoke.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Partial.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Partial.h new file mode 120000 index 000000000..5ad96ce1f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/functional/Partial.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/Partial.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Barrier.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Barrier.h new file mode 120000 index 000000000..c9ec9766e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Barrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Barrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-inl.h new file mode 120000 index 000000000..0d63de40a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-pre.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-pre.h new file mode 120000 index 000000000..528bba4ff --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future-pre.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future-pre.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future.h new file mode 120000 index 000000000..41b1ec54e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Future.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/FutureSplitter.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/FutureSplitter.h new file mode 120000 index 000000000..a301e43d3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/FutureSplitter.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/FutureSplitter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ManualTimekeeper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ManualTimekeeper.h new file mode 120000 index 000000000..adf22efe6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ManualTimekeeper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/ManualTimekeeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Portability.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Portability.h new file mode 120000 index 000000000..656f41316 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Portability.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Portability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise-inl.h new file mode 120000 index 000000000..92813dde7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Promise-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise.h new file mode 120000 index 000000000..34e7c3eaa --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Promise.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Promise.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Retrying.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Retrying.h new file mode 120000 index 000000000..f6348affb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/Retrying.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Retrying.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise-inl.h new file mode 120000 index 000000000..e8b93a51b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/SharedPromise-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise.h new file mode 120000 index 000000000..809bfde25 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/SharedPromise.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/SharedPromise.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h new file mode 120000 index 000000000..6701673e7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/WTCallback.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/WTCallback.h new file mode 120000 index 000000000..1b6031c5d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/WTCallback.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/WTCallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Core.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Core.h new file mode 120000 index 000000000..64926e693 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Core.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/futures/detail/Core.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Types.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Types.h new file mode 120000 index 000000000..3a8b41b45 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/futures/detail/Types.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/futures/detail/Types.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base-inl.h new file mode 120000 index 000000000..bd2e9dd20 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Base-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base.h new file mode 120000 index 000000000..35629e04b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Base.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Base.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine-inl.h new file mode 120000 index 000000000..70cec4176 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Combine-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine.h new file mode 120000 index 000000000..b59aa1b15 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Combine.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Combine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core-inl.h new file mode 120000 index 000000000..c0cec2d3b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Core-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core.h new file mode 120000 index 000000000..4f3feb5bb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Core.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Core.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File-inl.h new file mode 120000 index 000000000..7c8ce0209 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/File-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File.h new file mode 120000 index 000000000..79c251ed5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/File.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/File.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/IStream.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/IStream.h new file mode 120000 index 000000000..96dfefb43 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/IStream.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/IStream.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel-inl.h new file mode 120000 index 000000000..40b3bb6a0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Parallel-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel.h new file mode 120000 index 000000000..0a4d29269 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/Parallel.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Parallel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap-inl.h new file mode 120000 index 000000000..c6820e59b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/ParallelMap-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap.h new file mode 120000 index 000000000..16636ab8a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/ParallelMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/ParallelMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String-inl.h new file mode 120000 index 000000000..9dea9f6a0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/String-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String.h new file mode 120000 index 000000000..4b03250fe --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/gen/String.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Checksum.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Checksum.h new file mode 120000 index 000000000..772db087b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Checksum.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/Checksum.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/FarmHash.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/FarmHash.h new file mode 120000 index 000000000..afc7adc84 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/FarmHash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/FarmHash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Hash.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Hash.h new file mode 120000 index 000000000..a700b8cee --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/Hash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/Hash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV1.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV1.h new file mode 120000 index 000000000..396875c0a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV1.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/SpookyHashV1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV2.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV2.h new file mode 120000 index 000000000..60aaf2120 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/SpookyHashV2.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/SpookyHashV2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/detail/ChecksumDetail.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/detail/ChecksumDetail.h new file mode 120000 index 000000000..dc2ecacbd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/hash/detail/ChecksumDetail.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/hash/detail/ChecksumDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Init.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Init.h new file mode 120000 index 000000000..3401526f4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Init.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/init/Init.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Phase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Phase.h new file mode 120000 index 000000000..9c09d17d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/init/Phase.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/init/Phase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor-inl.h new file mode 120000 index 000000000..2f7830c54 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/Cursor-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor.h new file mode 120000 index 000000000..386f2af26 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/Cursor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/Cursor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h new file mode 120000 index 000000000..200bbca90 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/GlobalShutdownSocketSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBuf.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBuf.h new file mode 120000 index 000000000..577c3d93a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBuf.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/IOBuf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBufQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBufQueue.h new file mode 120000 index 000000000..0f4a52351 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/IOBufQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/IOBufQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO-inl.h new file mode 120000 index 000000000..093f30def --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/RecordIO-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO.h new file mode 120000 index 000000000..d2cf8f1ba --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/RecordIO.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/RecordIO.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/ShutdownSocketSet.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/ShutdownSocketSet.h new file mode 120000 index 000000000..11b990f92 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/ShutdownSocketSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/ShutdownSocketSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/SocketOptionMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/SocketOptionMap.h new file mode 120000 index 000000000..4ad54da56 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/SocketOptionMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/SocketOptionMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/TypedIOBuf.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/TypedIOBuf.h new file mode 120000 index 000000000..376af4a8e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/TypedIOBuf.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/TypedIOBuf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncPipe.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncPipe.h new file mode 120000 index 000000000..1d89e1377 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncPipe.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncPipe.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSSLSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSSLSocket.h new file mode 120000 index 000000000..6bd7d0e46 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSSLSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSSLSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncServerSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncServerSocket.h new file mode 120000 index 000000000..3c0a257a7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncServerSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncServerSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSignalHandler.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSignalHandler.h new file mode 120000 index 000000000..084536a07 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSignalHandler.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSignalHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocket.h new file mode 120000 index 000000000..a7f05a337 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketBase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketBase.h new file mode 120000 index 000000000..1a421b734 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocketBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketException.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketException.h new file mode 120000 index 000000000..3a6938497 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncSocketException.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocketException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTimeout.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTimeout.h new file mode 120000 index 000000000..29e15011a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTimeout.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTimeout.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransport.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransport.h new file mode 120000 index 000000000..04a53743e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransport.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h new file mode 120000 index 000000000..3b27a2c1b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTransportCertificate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h new file mode 120000 index 000000000..53af72c8d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPSocket.h new file mode 120000 index 000000000..a8adccc7b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/AsyncUDPSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncUDPSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h new file mode 120000 index 000000000..c2546b707 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestruction.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestruction.h new file mode 120000 index 000000000..fcc9de6d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestruction.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DelayedDestruction.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestructionBase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestructionBase.h new file mode 120000 index 000000000..4712379ce --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DelayedDestructionBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DelayedDestructionBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DestructorCheck.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DestructorCheck.h new file mode 120000 index 000000000..f9df0eb31 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/DestructorCheck.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DestructorCheck.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBase.h new file mode 120000 index 000000000..3e08454fd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseBackendBase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseBackendBase.h new file mode 120000 index 000000000..b80949fc4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseBackendBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseBackendBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseLocal.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseLocal.h new file mode 120000 index 000000000..e1f7d0bfd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseLocal.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseManager.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseManager.h new file mode 120000 index 000000000..680cb6aa0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseManager.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseThread.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseThread.h new file mode 120000 index 000000000..08a80d11b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventBaseThread.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventFDWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventFDWrapper.h new file mode 120000 index 000000000..ae722f8e4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventFDWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventFDWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventHandler.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventHandler.h new file mode 120000 index 000000000..b3f7c0e43 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventHandler.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventUtil.h new file mode 120000 index 000000000..9ae48e83e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/EventUtil.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h new file mode 120000 index 000000000..4a3c7d514 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer.h new file mode 120000 index 000000000..a8d0a066a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/HHWheelTimer.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/HHWheelTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/NotificationQueue.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/NotificationQueue.h new file mode 120000 index 000000000..95d3bc70d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/NotificationQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/NotificationQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/PasswordInFile.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/PasswordInFile.h new file mode 120000 index 000000000..d6e8031a5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/PasswordInFile.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/PasswordInFile.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/Request.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/Request.h new file mode 120000 index 000000000..eb81d77e6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/Request.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/Request.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLContext.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLContext.h new file mode 120000 index 000000000..c7b00d85b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLContext.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/SSLContext.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLOptions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLOptions.h new file mode 120000 index 000000000..e6e762013 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/SSLOptions.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/SSLOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h new file mode 120000 index 000000000..e50d592c8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/ScopedEventBaseThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/TimeoutManager.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/TimeoutManager.h new file mode 120000 index 000000000..9cd65aee7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/TimeoutManager.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/TimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/VirtualEventBase.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/VirtualEventBase.h new file mode 120000 index 000000000..261bd23fb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/VirtualEventBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/VirtualEventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h new file mode 120000 index 000000000..672b7bd0f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h new file mode 120000 index 000000000..47b083053 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h new file mode 120000 index 000000000..d40d967ab --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/SSLErrors.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/SSLErrors.h new file mode 120000 index 000000000..ba22d0266 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/SSLErrors.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/SSLErrors.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h new file mode 120000 index 000000000..bba637a09 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/json.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/json.h new file mode 120000 index 000000000..9a2868668 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/json.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/json_patch.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/json_patch.h new file mode 120000 index 000000000..7f2f82436 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/json_patch.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json_patch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/json_pointer.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/json_pointer.h new file mode 120000 index 000000000..731203398 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/json_pointer.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json_pointer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Align.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Align.h new file mode 120000 index 000000000..53e4b575e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Align.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Align.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Aligned.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Aligned.h new file mode 120000 index 000000000..077c72651 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Aligned.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Aligned.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume-inl.h new file mode 120000 index 000000000..4bd3795d0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Assume-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume.h new file mode 120000 index 000000000..8a6e8fe25 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Assume.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Assume.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Bits.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Bits.h new file mode 120000 index 000000000..9d7b9bebe --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Bits.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CString.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CString.h new file mode 120000 index 000000000..091a5272a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CString.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Cast.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Cast.h new file mode 120000 index 000000000..df4233d43 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Cast.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Cast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CheckedMath.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CheckedMath.h new file mode 120000 index 000000000..cf7a342a9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CheckedMath.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CheckedMath.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CustomizationPoint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CustomizationPoint.h new file mode 120000 index 000000000..6e91271e4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/CustomizationPoint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CustomizationPoint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Exception.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Exception.h new file mode 120000 index 000000000..472026d87 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Exception.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Exception.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Launder.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Launder.h new file mode 120000 index 000000000..47710cfa1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Launder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Launder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Ordering.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Ordering.h new file mode 120000 index 000000000..7d6e63f6d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Ordering.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Ordering.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Pretty.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Pretty.h new file mode 120000 index 000000000..729dae01a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/Pretty.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Pretty.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/PropagateConst.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/PropagateConst.h new file mode 120000 index 000000000..aadb9984a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/PropagateConst.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/PropagateConst.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/RValueReferenceWrapper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/RValueReferenceWrapper.h new file mode 120000 index 000000000..f17dcb20b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/RValueReferenceWrapper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/RValueReferenceWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/SafeAssert.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/SafeAssert.h new file mode 120000 index 000000000..603c325d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/SafeAssert.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/SafeAssert.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/StaticConst.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/StaticConst.h new file mode 120000 index 000000000..c27a60eb7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/StaticConst.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/StaticConst.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/TypeInfo.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/TypeInfo.h new file mode 120000 index 000000000..e629c053c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/TypeInfo.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/TypeInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/UncaughtExceptions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/UncaughtExceptions.h new file mode 120000 index 000000000..57c89967f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/lang/UncaughtExceptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/UncaughtExceptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena-inl.h new file mode 120000 index 000000000..abf127d8c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Arena-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena.h new file mode 120000 index 000000000..dabde886b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Arena.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Arena.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/EnableSharedFromThis.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/EnableSharedFromThis.h new file mode 120000 index 000000000..30d8334d3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/EnableSharedFromThis.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/EnableSharedFromThis.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MallctlHelper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MallctlHelper.h new file mode 120000 index 000000000..0ca5d27f6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MallctlHelper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/MallctlHelper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Malloc.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Malloc.h new file mode 120000 index 000000000..3c57e6dc8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/Malloc.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Malloc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MemoryResource.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MemoryResource.h new file mode 120000 index 000000000..c26326ad8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/MemoryResource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/MemoryResource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ReentrantAllocator.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ReentrantAllocator.h new file mode 120000 index 000000000..3e18c786e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ReentrantAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/ReentrantAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/SanitizeLeak.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/SanitizeLeak.h new file mode 120000 index 000000000..90f0135f1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/SanitizeLeak.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/SanitizeLeak.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ThreadCachedArena.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ThreadCachedArena.h new file mode 120000 index 000000000..2b1390045 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/ThreadCachedArena.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/ThreadCachedArena.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h new file mode 120000 index 000000000..be5534bad --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/UninitializedMemoryHacks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/detail/MallocImpl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/detail/MallocImpl.h new file mode 120000 index 000000000..0d0a717b9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/memory/detail/MallocImpl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/memory/detail/MallocImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetOps.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetOps.h new file mode 120000 index 000000000..833d8ace0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetOps.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/net/NetOps.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetworkSocket.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetworkSocket.h new file mode 120000 index 000000000..64ce96ef3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/NetworkSocket.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/net/NetworkSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h new file mode 120000 index 000000000..105dd9b4e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Asm.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Asm.h new file mode 120000 index 000000000..391894ab2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Asm.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Asm.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Atomic.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Atomic.h new file mode 120000 index 000000000..010a1c3fb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Atomic.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Atomic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Builtins.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Builtins.h new file mode 120000 index 000000000..bc4a8f945 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Builtins.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Builtins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Config.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Config.h new file mode 120000 index 000000000..48a4bf306 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Config.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Config.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Constexpr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Constexpr.h new file mode 120000 index 000000000..c03deb3b9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Constexpr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Constexpr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Dirent.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Dirent.h new file mode 120000 index 000000000..a1fbeb15d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Dirent.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Dirent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Event.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Event.h new file mode 120000 index 000000000..af978b7e4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Event.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Fcntl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Fcntl.h new file mode 120000 index 000000000..56d18421f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Fcntl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Fcntl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GFlags.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GFlags.h new file mode 120000 index 000000000..5687c8a43 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GFlags.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GFlags.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GMock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GMock.h new file mode 120000 index 000000000..84a31aaf2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GMock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GMock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GTest.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GTest.h new file mode 120000 index 000000000..36c9dab31 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/GTest.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GTest.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/IOVec.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/IOVec.h new file mode 120000 index 000000000..e5e91b5d4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/IOVec.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/IOVec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Libgen.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Libgen.h new file mode 120000 index 000000000..f670a5102 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Libgen.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Libgen.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Malloc.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Malloc.h new file mode 120000 index 000000000..79bd0d37f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Malloc.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Malloc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Math.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Math.h new file mode 120000 index 000000000..d85034c86 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Math.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Math.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Memory.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Memory.h new file mode 120000 index 000000000..25a594989 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Memory.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Memory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/OpenSSL.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/OpenSSL.h new file mode 120000 index 000000000..2bb054795 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/OpenSSL.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/OpenSSL.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/PThread.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/PThread.h new file mode 120000 index 000000000..d75eedb02 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/PThread.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/PThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sched.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sched.h new file mode 120000 index 000000000..902bfe780 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sched.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Sched.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Semaphore.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Semaphore.h new file mode 120000 index 000000000..5879b4095 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Semaphore.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Semaphore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sockets.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sockets.h new file mode 120000 index 000000000..7bfde99c1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Sockets.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Sockets.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdio.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdio.h new file mode 120000 index 000000000..f6eb9fc4f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdio.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Stdio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdlib.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdlib.h new file mode 120000 index 000000000..e3b0a0082 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Stdlib.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Stdlib.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/String.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/String.h new file mode 120000 index 000000000..881d0acaf --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/String.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysFile.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysFile.h new file mode 120000 index 000000000..cd65ea83b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysFile.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysFile.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMembarrier.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMembarrier.h new file mode 120000 index 000000000..648df3a2b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMembarrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysMembarrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMman.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMman.h new file mode 120000 index 000000000..fd557d483 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysMman.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysMman.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysResource.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysResource.h new file mode 120000 index 000000000..e7dafa71d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysResource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysResource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysStat.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysStat.h new file mode 120000 index 000000000..0beb62b93 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysStat.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysStat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysSyscall.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysSyscall.h new file mode 120000 index 000000000..25e5d7a3e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysSyscall.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysSyscall.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTime.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTime.h new file mode 120000 index 000000000..f42436d9a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTime.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysTime.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTypes.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTypes.h new file mode 120000 index 000000000..47f56217d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysUio.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysUio.h new file mode 120000 index 000000000..6ab5f8f2c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/SysUio.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysUio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Syslog.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Syslog.h new file mode 120000 index 000000000..b0755faf6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Syslog.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Syslog.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Time.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Time.h new file mode 120000 index 000000000..59ffa922a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Time.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Time.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Unistd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Unistd.h new file mode 120000 index 000000000..49ed325f9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Unistd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Unistd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Windows.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Windows.h new file mode 120000 index 000000000..a202022da --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/portability/Windows.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Windows.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/small_vector.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/small_vector.h new file mode 120000 index 000000000..92aae47bd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/small_vector.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/small_vector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/sorted_vector_types.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/sorted_vector_types.h new file mode 120000 index 000000000..8cb88a991 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/sorted_vector_types.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/sorted_vector_types.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/Init.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/Init.h new file mode 120000 index 000000000..36228fa06 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/Init.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/Init.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h new file mode 120000 index 000000000..508824e10 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLCertUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLHash.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLHash.h new file mode 120000 index 000000000..afa8f8d5f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLHash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLHash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h new file mode 120000 index 000000000..2721bdbf5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLLockTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h new file mode 120000 index 000000000..c9a6663f7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h new file mode 120000 index 000000000..bff5632f6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/SSLSession.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/SSLSession.h new file mode 120000 index 000000000..38910044c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/SSLSession.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/SSLSession.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h new file mode 120000 index 000000000..f5b171995 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h new file mode 120000 index 000000000..d77a751b4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/stop_watch.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/stop_watch.h new file mode 120000 index 000000000..682e6b8ac --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/stop_watch.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/stop_watch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h new file mode 120000 index 000000000..cbff7905d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h new file mode 120000 index 000000000..561ca9418 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicNotification-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification.h new file mode 120000 index 000000000..0d892928c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicNotification.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicRef.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicRef.h new file mode 120000 index 000000000..4a0220a66 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicRef.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicRef.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicStruct.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicStruct.h new file mode 120000 index 000000000..ddf96fce9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicStruct.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicStruct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h new file mode 120000 index 000000000..5b81da490 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicUtil-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil.h new file mode 120000 index 000000000..98110d18b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/AtomicUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Baton.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Baton.h new file mode 120000 index 000000000..b2d41f9e8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Baton.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Baton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/CallOnce.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/CallOnce.h new file mode 120000 index 000000000..d3cbcfde3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/CallOnce.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/CallOnce.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h new file mode 120000 index 000000000..5f76b1d6c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutex-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex.h new file mode 120000 index 000000000..d41bf0b15 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutex.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h new file mode 120000 index 000000000..de880064a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr-fwd.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr-fwd.h new file mode 120000 index 000000000..129e7b0f8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Hazptr-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr.h new file mode 120000 index 000000000..165c3d155 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Hazptr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Hazptr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrDomain.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrDomain.h new file mode 120000 index 000000000..e99412c43 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrDomain.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrDomain.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrHolder.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrHolder.h new file mode 120000 index 000000000..93a9a3fd6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrHolder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrHolder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObj.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObj.h new file mode 120000 index 000000000..e63361673 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObj.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrObj.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObjLinked.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObjLinked.h new file mode 120000 index 000000000..0f8edc854 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrObjLinked.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrObjLinked.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrRec.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrRec.h new file mode 120000 index 000000000..40c83412c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrRec.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrRec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThrLocal.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThrLocal.h new file mode 120000 index 000000000..7e4bb50ab --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThrLocal.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrThrLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h new file mode 120000 index 000000000..44f9d3734 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/LifoSem.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/LifoSem.h new file mode 120000 index 000000000..012ab53e9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/LifoSem.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/LifoSem.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/MicroSpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/MicroSpinLock.h new file mode 120000 index 000000000..0b24a7c35 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/MicroSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/MicroSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/ParkingLot.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/ParkingLot.h new file mode 120000 index 000000000..8b3d666c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/ParkingLot.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/ParkingLot.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/PicoSpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/PicoSpinLock.h new file mode 120000 index 000000000..8ea55b6a1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/PicoSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/PicoSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/RWSpinLock.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/RWSpinLock.h new file mode 120000 index 000000000..56a1a5edb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/RWSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/RWSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Rcu-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Rcu-inl.h new file mode 120000 index 000000000..ecc8a6c8d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Rcu-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Rcu-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SanitizeThread.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SanitizeThread.h new file mode 120000 index 000000000..84c2215e7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SanitizeThread.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SanitizeThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h new file mode 120000 index 000000000..8421d0a52 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SaturatingSemaphore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SmallLocks.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SmallLocks.h new file mode 120000 index 000000000..aa0c0537f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/SmallLocks.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SmallLocks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Tearable.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Tearable.h new file mode 120000 index 000000000..390ebbe81 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Tearable.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Tearable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Utility.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Utility.h new file mode 120000 index 000000000..05f2c3051 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/Utility.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Utility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/WaitOptions.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/WaitOptions.h new file mode 120000 index 000000000..f26bb0f46 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/WaitOptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/WaitOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h new file mode 120000 index 000000000..f76a38b37 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/AtomicUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Hardware.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Hardware.h new file mode 120000 index 000000000..a0021765c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Hardware.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Hardware.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h new file mode 120000 index 000000000..7bf231e91 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/HazptrUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h new file mode 120000 index 000000000..04061c642 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h new file mode 120000 index 000000000..040825d87 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h new file mode 120000 index 000000000..3781be5b7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ProxyLockable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Sleeper.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Sleeper.h new file mode 120000 index 000000000..40e9658de --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Sleeper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Sleeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Spin.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Spin.h new file mode 120000 index 000000000..e672626fb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/Spin.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Spin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h new file mode 120000 index 000000000..0a4e4de73 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h new file mode 120000 index 000000000..ffe9fd11c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/system/HardwareConcurrency.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/HardwareConcurrency.h new file mode 120000 index 000000000..0cb3b0d56 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/HardwareConcurrency.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/HardwareConcurrency.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/system/MemoryMapping.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/MemoryMapping.h new file mode 120000 index 000000000..6f642c4b1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/MemoryMapping.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/MemoryMapping.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/system/Shell.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/Shell.h new file mode 120000 index 000000000..ae6a254e8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/Shell.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/Shell.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadId.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadId.h new file mode 120000 index 000000000..05fbfd780 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadId.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/ThreadId.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadName.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadName.h new file mode 120000 index 000000000..3641dfe5e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/system/ThreadName.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/ThreadName.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/ScopedTraceSection.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/ScopedTraceSection.h new file mode 120000 index 000000000..3f5662d34 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/ScopedTraceSection.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/ScopedTraceSection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h new file mode 120000 index 000000000..2bac71b9a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint.h b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint.h new file mode 120000 index 000000000..8f5d47326 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Folly/folly/tracing/StaticTracepoint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/StaticTracepoint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Glog/glog/log_severity.h b/ios/Pods/Headers/Private/Flipper-Glog/glog/log_severity.h new file mode 120000 index 000000000..574678ddc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Glog/glog/log_severity.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/log_severity.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Glog/glog/logging.h b/ios/Pods/Headers/Private/Flipper-Glog/glog/logging.h new file mode 120000 index 000000000..79490eb17 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Glog/glog/logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Glog/glog/raw_logging.h b/ios/Pods/Headers/Private/Flipper-Glog/glog/raw_logging.h new file mode 120000 index 000000000..ab65c43a5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Glog/glog/raw_logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/raw_logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Glog/glog/stl_logging.h b/ios/Pods/Headers/Private/Flipper-Glog/glog/stl_logging.h new file mode 120000 index 000000000..4bcf9c098 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Glog/glog/stl_logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/stl_logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-Glog/glog/vlog_is_on.h b/ios/Pods/Headers/Private/Flipper-Glog/glog/vlog_is_on.h new file mode 120000 index 000000000..c2c66094a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-Glog/glog/vlog_is_on.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/vlog_is_on.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTChannel.h b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTChannel.h new file mode 120000 index 000000000..0e6de7989 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTChannel.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTChannel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTPrivate.h b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTPrivate.h new file mode 120000 index 000000000..c192ec9c9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTPrivate.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTPrivate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTProtocol.h b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTProtocol.h new file mode 120000 index 000000000..56721bfb1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTProtocol.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTUSBHub.h b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTUSBHub.h new file mode 120000 index 000000000..695340776 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/PTUSBHub.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTUSBHub.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/Peertalk.h b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/Peertalk.h new file mode 120000 index 000000000..c50168656 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-PeerTalk/peertalk/Peertalk.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/Peertalk.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ColdResumeHandler.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ColdResumeHandler.h new file mode 120000 index 000000000..09f8edcec --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ColdResumeHandler.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ColdResumeHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionAcceptor.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionAcceptor.h new file mode 120000 index 000000000..aeed980c4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionAcceptor.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ConnectionAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionFactory.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionFactory.h new file mode 120000 index 000000000..a8150eec0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ConnectionFactory.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ConnectionFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/DuplexConnection.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/DuplexConnection.h new file mode 120000 index 000000000..070c208ba --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/DuplexConnection.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/DuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/Payload.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/Payload.h new file mode 120000 index 000000000..c99772af2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/Payload.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/Payload.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocket.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocket.h new file mode 120000 index 000000000..88e7cfe7e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocket.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketClient.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketClient.h new file mode 120000 index 000000000..ba07d875c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketClient.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketConnectionEvents.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketConnectionEvents.h new file mode 120000 index 000000000..27cac8df5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketConnectionEvents.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketConnectionEvents.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketErrors.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketErrors.h new file mode 120000 index 000000000..d4fc8da03 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketErrors.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketErrors.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketException.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketException.h new file mode 120000 index 000000000..50e01404d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketException.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketParameters.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketParameters.h new file mode 120000 index 000000000..36051ec00 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketParameters.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketParameters.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketRequester.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketRequester.h new file mode 120000 index 000000000..4de6f033c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketRequester.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketResponder.h new file mode 120000 index 000000000..27e4ff8c3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketResponder.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServer.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServer.h new file mode 120000 index 000000000..b27c71985 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServer.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServerState.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServerState.h new file mode 120000 index 000000000..be8e42f71 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServerState.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServerState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServiceHandler.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServiceHandler.h new file mode 120000 index 000000000..01b4ab0c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketServiceHandler.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServiceHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketStats.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketStats.h new file mode 120000 index 000000000..e7332ac03 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/RSocketStats.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketStats.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ResumeManager.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ResumeManager.h new file mode 120000 index 000000000..f2b3b900b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/ResumeManager.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ResumeManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Fixture.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Fixture.h new file mode 120000 index 000000000..ca13cff3b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Fixture.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Fixture.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Latch.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Latch.h new file mode 120000 index 000000000..24aa48acd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Latch.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Latch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Throughput.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Throughput.h new file mode 120000 index 000000000..08e3d3ba5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/benchmarks/Throughput.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Throughput.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ErrorCode.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ErrorCode.h new file mode 120000 index 000000000..ad4379654 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ErrorCode.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ErrorCode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Frame.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Frame.h new file mode 120000 index 000000000..e5bb9aa90 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Frame.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/Frame.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameFlags.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameFlags.h new file mode 120000 index 000000000..1f0097e94 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameFlags.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameFlags.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameHeader.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameHeader.h new file mode 120000 index 000000000..c9c4983c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameHeader.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameHeader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameProcessor.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameProcessor.h new file mode 120000 index 000000000..a13776ef8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer.h new file mode 120000 index 000000000..ac0c8d743 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h new file mode 120000 index 000000000..af2df7620 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransport.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransport.h new file mode 120000 index 000000000..ca63b163f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h new file mode 120000 index 000000000..6d45f3489 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameTransportImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameType.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameType.h new file mode 120000 index 000000000..488816735 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FrameType.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameType.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h new file mode 120000 index 000000000..4d58eaafc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedReader.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedReader.h new file mode 120000 index 000000000..2eb3bcd47 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/FramedReader.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FramedReader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Framer.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Framer.h new file mode 120000 index 000000000..f676b5b64 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/Framer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/Framer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ProtocolVersion.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ProtocolVersion.h new file mode 120000 index 000000000..1a42ae5c2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ProtocolVersion.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ProtocolVersion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h new file mode 120000 index 000000000..9c7c36548 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h new file mode 120000 index 000000000..2d9e0c5f4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h new file mode 120000 index 000000000..9761f5e5b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Allowance.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Allowance.h new file mode 120000 index 000000000..5eac4c019 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Allowance.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/Allowance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h new file mode 120000 index 000000000..1620e1e83 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Common.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Common.h new file mode 120000 index 000000000..93f1229c1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/Common.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/Common.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ConnectionSet.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ConnectionSet.h new file mode 120000 index 000000000..01ef666e5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ConnectionSet.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ConnectionSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h new file mode 120000 index 000000000..62bee3c38 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/KeepaliveTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h new file mode 120000 index 000000000..702445954 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h new file mode 120000 index 000000000..982d15cca --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h new file mode 120000 index 000000000..b82b406f9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h new file mode 120000 index 000000000..cdfe1be27 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h new file mode 120000 index 000000000..cd677f0da --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSubscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h new file mode 120000 index 000000000..bb2f667d1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/StackTraceUtils.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/StackTraceUtils.h new file mode 120000 index 000000000..4ca0717a7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/StackTraceUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/StackTraceUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SwappableEventBase.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SwappableEventBase.h new file mode 120000 index 000000000..7254d4e7d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/SwappableEventBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/SwappableEventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/WarmResumeManager.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/WarmResumeManager.h new file mode 120000 index 000000000..587b3034a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/internal/WarmResumeManager.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/WarmResumeManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h new file mode 120000 index 000000000..ddb84d2af --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ChannelRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h new file mode 120000 index 000000000..2cd66c83d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ChannelResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h new file mode 120000 index 000000000..4d3d1dbb3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ConsumerBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h new file mode 120000 index 000000000..eff049f8e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/PublisherBase.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/PublisherBase.h new file mode 120000 index 000000000..55faa44bb --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/PublisherBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/PublisherBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h new file mode 120000 index 000000000..ece2d0cc1 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h new file mode 120000 index 000000000..4dacdf9e4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h new file mode 120000 index 000000000..8ca4f8eaf --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h new file mode 120000 index 000000000..c84f819b4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamRequester.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamRequester.h new file mode 120000 index 000000000..121dd21ae --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamResponder.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamResponder.h new file mode 120000 index 000000000..aa7cc0c01 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h new file mode 120000 index 000000000..d6ca91e45 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h new file mode 120000 index 000000000..a90d0f7b4 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamsWriter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/RSocketTransport.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/RSocketTransport.h new file mode 120000 index 000000000..638373dcc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/RSocketTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/transports/RSocketTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h new file mode 120000 index 000000000..6a9036d3e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h new file mode 120000 index 000000000..1933fe9ed --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h new file mode 120000 index 000000000..4124b6664 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h new file mode 120000 index 000000000..e0556193c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h new file mode 120000 index 000000000..82a0d2448 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/DeferFlowable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/DeferFlowable.h new file mode 120000 index 000000000..711b90a6b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/DeferFlowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/DeferFlowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h new file mode 120000 index 000000000..6b97631cc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/EmitterFlowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable.h new file mode 120000 index 000000000..97e649dc3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h new file mode 120000 index 000000000..dc60f88b9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h new file mode 120000 index 000000000..b53364dd7 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h new file mode 120000 index 000000000..b46328eba --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableOperator.h new file mode 120000 index 000000000..a776e4885 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h new file mode 120000 index 000000000..9320b08b9 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h new file mode 120000 index 000000000..76fb7eac5 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowables.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowables.h new file mode 120000 index 000000000..803b0ecb6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Flowables.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowables.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/PublishProcessor.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/PublishProcessor.h new file mode 120000 index 000000000..fada9dda6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/PublishProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/PublishProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscriber.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscriber.h new file mode 120000 index 000000000..e1abd24a3 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Subscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscription.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscription.h new file mode 120000 index 000000000..d5a1df665 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/Subscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Subscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/TestSubscriber.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/TestSubscriber.h new file mode 120000 index 000000000..63a7b2cc2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/TestSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/TestSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h new file mode 120000 index 000000000..a981c71c6 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/DeferObservable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/DeferObservable.h new file mode 120000 index 000000000..fb1eba682 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/DeferObservable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/DeferObservable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observable.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observable.h new file mode 120000 index 000000000..a681eaca8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h new file mode 120000 index 000000000..9eb2e09dc --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h new file mode 120000 index 000000000..d77604a4d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableDoOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableOperator.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableOperator.h new file mode 120000 index 000000000..6ab8464c2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/ObservableOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observables.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observables.h new file mode 120000 index 000000000..0d86053e8 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observables.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observables.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observer.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observer.h new file mode 120000 index 000000000..89f3bd4dd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Observer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Subscription.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Subscription.h new file mode 120000 index 000000000..53dfe923b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/Subscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Subscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/TestObserver.h b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/TestObserver.h new file mode 120000 index 000000000..6fbdd4400 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper-RSocket/yarpl/observable/TestObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/TestObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/CallstackHelper.h b/ios/Pods/Headers/Private/Flipper/CallstackHelper.h new file mode 120000 index 000000000..3577e9f83 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/CallstackHelper.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/utils/CallstackHelper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/CertificateUtils.h b/ios/Pods/Headers/Private/Flipper/CertificateUtils.h new file mode 120000 index 000000000..9ffc9c15e --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/CertificateUtils.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/CertificateUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/ConnectionContextStore.h b/ios/Pods/Headers/Private/Flipper/ConnectionContextStore.h new file mode 120000 index 000000000..4d1e2f56f --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/ConnectionContextStore.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/ConnectionContextStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FireAndForgetBasedFlipperResponder.h b/ios/Pods/Headers/Private/Flipper/FireAndForgetBasedFlipperResponder.h new file mode 120000 index 000000000..81f738362 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FireAndForgetBasedFlipperResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FireAndForgetBasedFlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperClient.h b/ios/Pods/Headers/Private/Flipper/FlipperClient.h new file mode 120000 index 000000000..4c8f6785a --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperClient.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperConnection.h b/ios/Pods/Headers/Private/Flipper/FlipperConnection.h new file mode 120000 index 000000000..32a2f084c --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperConnection.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperConnectionImpl.h b/ios/Pods/Headers/Private/Flipper/FlipperConnectionImpl.h new file mode 120000 index 000000000..190902d47 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperConnectionImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperConnectionManager.h b/ios/Pods/Headers/Private/Flipper/FlipperConnectionManager.h new file mode 120000 index 000000000..4aa8c7128 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperConnectionManager.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperConnectionManagerImpl.h b/ios/Pods/Headers/Private/Flipper/FlipperConnectionManagerImpl.h new file mode 120000 index 000000000..7007bf790 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperConnectionManagerImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperInitConfig.h b/ios/Pods/Headers/Private/Flipper/FlipperInitConfig.h new file mode 120000 index 000000000..9d9df0599 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperInitConfig.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperInitConfig.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperPlugin.h b/ios/Pods/Headers/Private/Flipper/FlipperPlugin.h new file mode 120000 index 000000000..9acf19344 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperPlugin.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperRSocketResponder.h b/ios/Pods/Headers/Private/Flipper/FlipperRSocketResponder.h new file mode 120000 index 000000000..44b7f360b --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperRSocketResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperRSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperResponder.h b/ios/Pods/Headers/Private/Flipper/FlipperResponder.h new file mode 120000 index 000000000..0d96a3850 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperResponderImpl.h b/ios/Pods/Headers/Private/Flipper/FlipperResponderImpl.h new file mode 120000 index 000000000..e3e9fbdce --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperResponderImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperResponderImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperState.h b/ios/Pods/Headers/Private/Flipper/FlipperState.h new file mode 120000 index 000000000..14ade198d --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperState.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperStateUpdateListener.h b/ios/Pods/Headers/Private/Flipper/FlipperStateUpdateListener.h new file mode 120000 index 000000000..1158774e2 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperStateUpdateListener.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperStateUpdateListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/FlipperStep.h b/ios/Pods/Headers/Private/Flipper/FlipperStep.h new file mode 120000 index 000000000..1a3e59fbd --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/FlipperStep.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperStep.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Flipper/Log.h b/ios/Pods/Headers/Private/Flipper/Log.h new file mode 120000 index 000000000..308b97af0 --- /dev/null +++ b/ios/Pods/Headers/Private/Flipper/Log.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/Log.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingConnection.h b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingConnection.h new file mode 120000 index 000000000..b054b95f4 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingConnection.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingResponder.h b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingResponder.h new file mode 120000 index 000000000..d5cb0e7bc --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppBridgingResponder.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h new file mode 120000 index 000000000..96c25fc90 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h b/ios/Pods/Headers/Private/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h new file mode 120000 index 000000000..0a40863ea --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/FBCxxFollyDynamicConvert/FBCxxFollyDynamicConvert.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FBDefines/FBDefines.h b/ios/Pods/Headers/Private/FlipperKit/FBDefines/FBDefines.h new file mode 120000 index 000000000..6f2c5bdd1 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FBDefines/FBDefines.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FBDefines/FBDefines.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h b/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h new file mode 120000 index 000000000..76e845d75 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingCommon.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingServer.h b/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingServer.h new file mode 120000 index 000000000..b647b33d4 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FKPortForwarding/FKPortForwardingServer.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FlipperKit/FKPortForwarding/FKPortForwardingServer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperClient+Testing.h b/ios/Pods/Headers/Private/FlipperKit/FlipperClient+Testing.h new file mode 120000 index 000000000..ccdd88865 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperClient+Testing.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperClient+Testing.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperClient.h b/ios/Pods/Headers/Private/FlipperKit/FlipperClient.h new file mode 120000 index 000000000..c980be15d --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperClient.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperConnection.h b/ios/Pods/Headers/Private/FlipperKit/FlipperConnection.h new file mode 120000 index 000000000..a07a33eb9 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperConnection.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingConnection.h b/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingConnection.h new file mode 120000 index 000000000..1691957e8 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingConnection.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingResponder.h b/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingResponder.h new file mode 120000 index 000000000..440166984 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperCppBridgingResponder.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppBridgingResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperCppWrapperPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperCppWrapperPlugin.h new file mode 120000 index 000000000..1786504df --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperCppWrapperPlugin.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/CppBridge/FlipperCppWrapperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperDiagnosticsViewController.h b/ios/Pods/Headers/Private/FlipperKit/FlipperDiagnosticsViewController.h new file mode 120000 index 000000000..104c20233 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperDiagnosticsViewController.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h new file mode 120000 index 000000000..28fad6fb5 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h new file mode 120000 index 000000000..ef46c9e89 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKApplicationDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKApplicationDescriptor.h new file mode 120000 index 000000000..2c13b3e84 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKApplicationDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKApplicationDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKButtonDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKButtonDescriptor.h new file mode 120000 index 000000000..c46060c8a --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKButtonDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKButtonDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h new file mode 120000 index 000000000..eaf79d764 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKHiddenWindow.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKHiddenWindow.h new file mode 120000 index 000000000..6e800546b --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKHiddenWindow.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKHiddenWindow.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h new file mode 120000 index 000000000..8255f2b64 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNamed.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNamed.h new file mode 120000 index 000000000..ed6d4ebed --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNamed.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNamed.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNodeDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNodeDescriptor.h new file mode 120000 index 000000000..a7b158835 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKNodeDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObject.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObject.h new file mode 120000 index 000000000..e03bbade9 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObject.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKObject.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObjectHash.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObjectHash.h new file mode 120000 index 000000000..96284ddaf --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKObjectHash.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKObjectHash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKScrollViewDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKScrollViewDescriptor.h new file mode 120000 index 000000000..e07e89a16 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKScrollViewDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKScrollViewDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSearchResultNode.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSearchResultNode.h new file mode 120000 index 000000000..2f4975256 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSearchResultNode.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKSearchResultNode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSwizzle.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSwizzle.h new file mode 120000 index 000000000..f9b8e9762 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKSwizzle.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKSwizzle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h new file mode 120000 index 000000000..cf740e903 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListenerImpl.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListenerImpl.h new file mode 120000 index 000000000..5ae157882 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTapListenerImpl.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListenerImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTouch.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTouch.h new file mode 120000 index 000000000..a9842f9e0 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKTouch.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTouch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewControllerDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewControllerDescriptor.h new file mode 120000 index 000000000..787e7ab8a --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewControllerDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewControllerDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewDescriptor.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewDescriptor.h new file mode 120000 index 000000000..13bda0f52 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKViewDescriptor.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKYogaKitHelper.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKYogaKitHelper.h new file mode 120000 index 000000000..189518d00 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/SKYogaKitHelper.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/utils/SKYogaKitHelper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h new file mode 120000 index 000000000..031b09853 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UICollectionView+SKInvalidation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h new file mode 120000 index 000000000..6c55f46bd --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIColor+SKSonarValueCoder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIView+SKInvalidation.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIView+SKInvalidation.h new file mode 120000 index 000000000..e30748112 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutPlugin/UIView+SKInvalidation.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h new file mode 120000 index 000000000..6844dd500 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutTextSearchable/FKTextSearchable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h new file mode 120000 index 000000000..665cdb2b8 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h new file mode 120000 index 000000000..ce8c8905f --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin+CPPInitialization.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h new file mode 120000 index 000000000..f6d72d0b7 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKDispatchQueue.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKDispatchQueue.h new file mode 120000 index 000000000..86c9cffc2 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKDispatchQueue.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKDispatchQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h new file mode 120000 index 000000000..b7dc55a0d --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h new file mode 120000 index 000000000..4892fd496 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h new file mode 120000 index 000000000..a00e359be --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h new file mode 120000 index 000000000..15a29220a --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SonarKitNetworkPlugin+CPPInitialization.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h new file mode 120000 index 000000000..bc679f939 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h new file mode 120000 index 000000000..afa451984 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h b/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h new file mode 120000 index 000000000..e98c414f6 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperPlugin.h b/ios/Pods/Headers/Private/FlipperKit/FlipperPlugin.h new file mode 120000 index 000000000..31938c93e --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperPlugin.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperResponder.h b/ios/Pods/Headers/Private/FlipperKit/FlipperResponder.h new file mode 120000 index 000000000..a95dd285c --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperResponder.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/FlipperStateUpdateListener.h b/ios/Pods/Headers/Private/FlipperKit/FlipperStateUpdateListener.h new file mode 120000 index 000000000..08c8ecc0a --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/FlipperStateUpdateListener.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperStateUpdateListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKEnvironmentVariables.h b/ios/Pods/Headers/Private/FlipperKit/SKEnvironmentVariables.h new file mode 120000 index 000000000..65b2e79e2 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKEnvironmentVariables.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/SKEnvironmentVariables.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkObserver.h b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkObserver.h new file mode 120000 index 000000000..50568d18f --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkObserver.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkRecorder.h b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkRecorder.h new file mode 120000 index 000000000..f8cdd8575 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkRecorder.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkRecorder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkTransaction.h b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkTransaction.h new file mode 120000 index 000000000..27cfa4b98 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXNetworkTransaction.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXNetworkTransaction.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXUtility.h b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXUtility.h new file mode 120000 index 000000000..55d9b69c2 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/FLEXUtility.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/FLEXNetworkLib/FLEXUtility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h new file mode 120000 index 000000000..bd37e7ab0 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKMacros.h b/ios/Pods/Headers/Private/FlipperKit/SKMacros.h new file mode 120000 index 000000000..fdd2403ec --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKMacros.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/SKMacros.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/FlipperKit/SKStateUpdateCPPWrapper.h b/ios/Pods/Headers/Private/FlipperKit/SKStateUpdateCPPWrapper.h new file mode 120000 index 000000000..a3c8df5b2 --- /dev/null +++ b/ios/Pods/Headers/Private/FlipperKit/SKStateUpdateCPPWrapper.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/SKStateUpdateCPPWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORDataFuture.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORDataFuture.h index 5f87ffd11..3d6fefe0c 120000 --- a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORDataFuture.h +++ b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORDataFuture.h @@ -1 +1 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORDataFuture.h \ No newline at end of file +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORDataFuture.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORFlatFileStorage.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORFlatFileStorage.h new file mode 120000 index 000000000..30b6e50f6 --- /dev/null +++ b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORFlatFileStorage.h @@ -0,0 +1 @@ +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORReachability.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORReachability.h index e21a76b6c..e71cfdfcb 120000 --- a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORReachability.h +++ b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORReachability.h @@ -1 +1 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORReachability.h \ No newline at end of file +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORReachability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage.h deleted file mode 120000 index 0aef1dab8..000000000 --- a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorageProtocol.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorageProtocol.h new file mode 120000 index 000000000..53c776834 --- /dev/null +++ b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorageProtocol.h @@ -0,0 +1 @@ +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage_Private.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage_Private.h deleted file mode 120000 index d0d7a06e7..000000000 --- a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStorage_Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Private/GDTCORStorage_Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStoredEvent.h b/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStoredEvent.h deleted file mode 120000 index 25f7c49ec..000000000 --- a/ios/Pods/Headers/Private/GoogleDataTransport/GDTCORStoredEvent.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStoredEvent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTCOREvent+NetworkConnectionInfo.h b/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTCOREvent+NetworkConnectionInfo.h new file mode 120000 index 000000000..ec1ad719c --- /dev/null +++ b/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTCOREvent+NetworkConnectionInfo.h @@ -0,0 +1 @@ +../../../GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTCOREvent+NetworkConnectionInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLPrioritizer.h b/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLPrioritizer.h deleted file mode 120000 index f12c39f00..000000000 --- a/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLPrioritizer.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLPrioritizer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLUploader.h b/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLUploader.h deleted file mode 120000 index 6942a20ab..000000000 --- a/ios/Pods/Headers/Private/GoogleDataTransportCCTSupport/GDTFLLUploader.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/GDTCCTLibrary/Private/GDTFLLUploader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainStorage.h b/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainStorage.h new file mode 120000 index 000000000..d0de71b6d --- /dev/null +++ b/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainStorage.h @@ -0,0 +1 @@ +../../../GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainUtils.h b/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainUtils.h new file mode 120000 index 000000000..d69319346 --- /dev/null +++ b/ios/Pods/Headers/Private/GoogleUtilities/GULKeychainUtils.h @@ -0,0 +1 @@ +../../../GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/aes.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/aes.h new file mode 120000 index 000000000..97a489fc7 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/aes.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/aes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1.h new file mode 120000 index 000000000..2ee6d1ec3 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1_mac.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1_mac.h new file mode 120000 index 000000000..29c1ceec5 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1_mac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1_mac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1t.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1t.h new file mode 120000 index 000000000..b9d44f5b3 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/asn1t.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1t.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bio.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bio.h new file mode 120000 index 000000000..d4098b335 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bio.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/bio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/blowfish.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/blowfish.h new file mode 120000 index 000000000..6995a7954 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/blowfish.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/blowfish.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bn.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bn.h new file mode 120000 index 000000000..9465a61e0 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/bn.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/bn.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/buffer.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/buffer.h new file mode 120000 index 000000000..68146764f --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/buffer.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/buffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/camellia.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/camellia.h new file mode 120000 index 000000000..b5cbfde87 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/camellia.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/camellia.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cast.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cast.h new file mode 120000 index 000000000..1423aca0a --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cast.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cmac.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cmac.h new file mode 120000 index 000000000..8b651f7e0 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cmac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cmac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cms.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cms.h new file mode 120000 index 000000000..718982c14 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/cms.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cms.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/comp.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/comp.h new file mode 120000 index 000000000..7ea4bce45 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/comp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/comp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf.h new file mode 120000 index 000000000..b3dcb0aec --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/conf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf_api.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf_api.h new file mode 120000 index 000000000..e94da2c0f --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/conf_api.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/conf_api.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/crypto.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/crypto.h new file mode 120000 index 000000000..b84d4f8a1 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/crypto.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/crypto.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des.h new file mode 120000 index 000000000..4261fd159 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/des.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des_old.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des_old.h new file mode 120000 index 000000000..5f94105a5 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/des_old.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/des_old.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dh.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dh.h new file mode 120000 index 000000000..0009abffe --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dh.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dh.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dsa.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dsa.h new file mode 120000 index 000000000..b79338d30 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dso.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dso.h new file mode 120000 index 000000000..3cd9024f8 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dso.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dso.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dtls1.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dtls1.h new file mode 120000 index 000000000..43acba1b5 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/dtls1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dtls1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/e_os2.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/e_os2.h new file mode 120000 index 000000000..a961ab409 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/e_os2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/e_os2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ebcdic.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ebcdic.h new file mode 120000 index 000000000..265725bb7 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ebcdic.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ebcdic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ec.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ec.h new file mode 120000 index 000000000..2c3c49d77 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ec.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdh.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdh.h new file mode 120000 index 000000000..25b079e5b --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdh.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ecdh.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdsa.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdsa.h new file mode 120000 index 000000000..517681e10 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ecdsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ecdsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/engine.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/engine.h new file mode 120000 index 000000000..c86dbf195 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/engine.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/engine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/err.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/err.h new file mode 120000 index 000000000..66aa10c6a --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/err.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/err.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/evp.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/evp.h new file mode 120000 index 000000000..8d7ddcb3e --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/evp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/evp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/hmac.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/hmac.h new file mode 120000 index 000000000..7d1dbab11 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/hmac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/hmac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/idea.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/idea.h new file mode 120000 index 000000000..d16a12c26 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/idea.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/idea.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/krb5_asn.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/krb5_asn.h new file mode 120000 index 000000000..428549eca --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/krb5_asn.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/krb5_asn.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/kssl.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/kssl.h new file mode 120000 index 000000000..547d88203 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/kssl.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/kssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/lhash.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/lhash.h new file mode 120000 index 000000000..4e809c962 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/lhash.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/lhash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md4.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md4.h new file mode 120000 index 000000000..eaec733aa --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md4.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/md4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md5.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md5.h new file mode 120000 index 000000000..40dce8faf --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/md5.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/md5.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/mdc2.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/mdc2.h new file mode 120000 index 000000000..939a5595f --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/mdc2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/mdc2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/modes.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/modes.h new file mode 120000 index 000000000..31852b890 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/modes.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/modes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/obj_mac.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/obj_mac.h new file mode 120000 index 000000000..a762cbb2f --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/obj_mac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/obj_mac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/objects.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/objects.h new file mode 120000 index 000000000..721325198 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/objects.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/objects.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ocsp.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ocsp.h new file mode 120000 index 000000000..194cc404d --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ocsp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ocsp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-arm64.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-arm64.h new file mode 120000 index 000000000..04000a53b --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-arm64.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-arm64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7.h new file mode 120000 index 000000000..831d559e4 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-armv7.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7s.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7s.h new file mode 120000 index 000000000..abcbeb242 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-armv7s.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-armv7s.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-i386.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-i386.h new file mode 120000 index 000000000..2e9b45560 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-i386.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-i386.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-x86_64.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-x86_64.h new file mode 120000 index 000000000..346d0b8cc --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf-x86_64.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-x86_64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf.h new file mode 120000 index 000000000..a65adfb03 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslconf.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslv.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslv.h new file mode 120000 index 000000000..cfa8c461c --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/opensslv.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ossl_typ.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ossl_typ.h new file mode 120000 index 000000000..69d1d043e --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ossl_typ.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ossl_typ.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem.h new file mode 120000 index 000000000..8ddc9fb45 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pem.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem2.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem2.h new file mode 120000 index 000000000..6b063c8da --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pem2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pem2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs12.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs12.h new file mode 120000 index 000000000..a7b581bf0 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs12.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pkcs12.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs7.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs7.h new file mode 120000 index 000000000..b84219bfe --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pkcs7.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pkcs7.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pqueue.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pqueue.h new file mode 120000 index 000000000..b3643e0cc --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/pqueue.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pqueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rand.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rand.h new file mode 120000 index 000000000..b1a56a7cd --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rand.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rand.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc2.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc2.h new file mode 120000 index 000000000..e95aa4cc8 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rc2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc4.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc4.h new file mode 120000 index 000000000..f10827e81 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rc4.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rc4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ripemd.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ripemd.h new file mode 120000 index 000000000..1f667944c --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ripemd.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ripemd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rsa.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rsa.h new file mode 120000 index 000000000..f96d767e9 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/rsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/safestack.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/safestack.h new file mode 120000 index 000000000..bbafcba72 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/safestack.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/safestack.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/seed.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/seed.h new file mode 120000 index 000000000..c39fda464 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/seed.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/seed.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/sha.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/sha.h new file mode 120000 index 000000000..61282c132 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/sha.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/sha.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/shim.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/shim.h new file mode 120000 index 000000000..af394b9d7 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/shim.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/shim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srp.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srp.h new file mode 120000 index 000000000..4fb9886e4 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/srp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srtp.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srtp.h new file mode 120000 index 000000000..9ae1fbaef --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/srtp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/srtp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl.h new file mode 120000 index 000000000..7e698749c --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl2.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl2.h new file mode 120000 index 000000000..ec3d83ec6 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl23.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl23.h new file mode 120000 index 000000000..8783362f3 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl23.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl23.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl3.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl3.h new file mode 120000 index 000000000..2c69510bf --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ssl3.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl3.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/stack.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/stack.h new file mode 120000 index 000000000..6659f6980 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/stack.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/stack.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/symhacks.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/symhacks.h new file mode 120000 index 000000000..246c6948e --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/symhacks.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/symhacks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/tls1.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/tls1.h new file mode 120000 index 000000000..ee638b811 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/tls1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/tls1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ts.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ts.h new file mode 120000 index 000000000..8b29cba94 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ts.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ts.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/txt_db.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/txt_db.h new file mode 120000 index 000000000..95acfe8f1 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/txt_db.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/txt_db.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui.h new file mode 120000 index 000000000..b2e967252 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ui.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui_compat.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui_compat.h new file mode 120000 index 000000000..e22819149 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/ui_compat.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ui_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/whrlpool.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/whrlpool.h new file mode 120000 index 000000000..95b2ef9b9 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/whrlpool.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/whrlpool.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509.h new file mode 120000 index 000000000..aefb78872 --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509_vfy.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509_vfy.h new file mode 120000 index 000000000..068dbae1f --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509_vfy.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509_vfy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509v3.h b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509v3.h new file mode 120000 index 000000000..fac96042d --- /dev/null +++ b/ios/Pods/Headers/Private/OpenSSL-Universal/openssl/x509v3.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509v3.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorage.h b/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorage.h new file mode 120000 index 000000000..c27b7a23e --- /dev/null +++ b/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorage.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/async-storage/ios/RNCAsyncStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorageDelegate.h b/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorageDelegate.h new file mode 120000 index 000000000..83daae9c5 --- /dev/null +++ b/ios/Pods/Headers/Private/RNCAsyncStorage/RNCAsyncStorageDelegate.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/async-storage/ios/RNCAsyncStorageDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/RNReanimated/REAUtils.h b/ios/Pods/Headers/Private/RNReanimated/REAUtils.h new file mode 120000 index 000000000..d3807079a --- /dev/null +++ b/ios/Pods/Headers/Private/RNReanimated/REAUtils.h @@ -0,0 +1 @@ +../../../../../node_modules/react-native-reanimated/ios/REAUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAccessibilityManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTAccessibilityManager.h index fc8b03b46..7e87bdd9c 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTAccessibilityManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAccessibilityManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAccessibilityManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAccessibilityManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTActionSheetManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTActionSheetManager.h index f16f03278..04b9961c8 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTActionSheetManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTActionSheetManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheetManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTActionSheetManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAlertManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTAlertManager.h index d48ca0d6b..fa0fb56db 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTAlertManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAlertManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAlertManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAlertManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAnimationPlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTAnimationPlugins.h new file mode 120000 index 000000000..1b4139671 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAnimationPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/NativeAnimation/RCTAnimationPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAppState.h b/ios/Pods/Headers/Private/React-Core/React/RCTAppState.h index 1e2dfd29b..d7f7500a5 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTAppState.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAppState.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAppState.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAppState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAppearance.h b/ios/Pods/Headers/Private/React-Core/React/RCTAppearance.h new file mode 120000 index 000000000..e89b140fe --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAppearance.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTAppearance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTAsyncLocalStorage.h b/ios/Pods/Headers/Private/React-Core/React/RCTAsyncLocalStorage.h index 16d86163b..56ac4219a 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTAsyncLocalStorage.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTAsyncLocalStorage.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAsyncLocalStorage.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAsyncLocalStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTClipboard.h b/ios/Pods/Headers/Private/React-Core/React/RCTClipboard.h index e90e62480..6224fa170 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTClipboard.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTClipboard.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTClipboard.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTClipboard.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTConstants.h b/ios/Pods/Headers/Private/React-Core/React/RCTConstants.h new file mode 120000 index 000000000..d00b686b8 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTConstants.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTConstants.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTDevMenu.h b/ios/Pods/Headers/Private/React-Core/React/RCTDevMenu.h index c265676ea..56e8e4f2c 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTDevMenu.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTDevMenu.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/DevSupport/RCTDevMenu.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDevMenu.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTDevSettings.h b/ios/Pods/Headers/Private/React-Core/React/RCTDevSettings.h index d139e6792..46fa8384b 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTDevSettings.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTDevSettings.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTDevSettings.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDevSettings.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTDeviceInfo.h b/ios/Pods/Headers/Private/React-Core/React/RCTDeviceInfo.h index c3a35d58c..f25f71850 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTDeviceInfo.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTDeviceInfo.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTDeviceInfo.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDeviceInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTFPSGraph.h b/ios/Pods/Headers/Private/React-Core/React/RCTFPSGraph.h index d653014cb..5ed8cabf6 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTFPSGraph.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTFPSGraph.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Profiler/RCTFPSGraph.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTFPSGraph.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTI18nManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTI18nManager.h index d3c6d62e0..38b8a201d 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTI18nManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTI18nManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTI18nManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTI18nManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImageEditingManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTImageEditingManager.h index ed704ac69..59fc21c1d 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTImageEditingManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImageEditingManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageEditingManager.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageEditingManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImageLoader.h b/ios/Pods/Headers/Private/React-Core/React/RCTImageLoader.h index 63f9cd794..c5f3ca662 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTImageLoader.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImageLoader.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageLoader.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageLoader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImageLoaderWithAttributionProtocol.h b/ios/Pods/Headers/Private/React-Core/React/RCTImageLoaderWithAttributionProtocol.h new file mode 120000 index 000000000..d0f46eae8 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImageLoaderWithAttributionProtocol.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImagePlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTImagePlugins.h new file mode 120000 index 000000000..7b0cc1dde --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImagePlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImagePlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImageStoreManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTImageStoreManager.h index ac7b53be7..1a11b286f 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTImageStoreManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImageStoreManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageStoreManager.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageStoreManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTImageURLLoaderWithAttribution.h b/ios/Pods/Headers/Private/React-Core/React/RCTImageURLLoaderWithAttribution.h new file mode 120000 index 000000000..2e0578785 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTImageURLLoaderWithAttribution.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImageURLLoaderWithAttribution.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTKeyboardObserver.h b/ios/Pods/Headers/Private/React-Core/React/RCTKeyboardObserver.h index 6e324b842..77f9767cc 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTKeyboardObserver.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTKeyboardObserver.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTKeyboardObserver.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTKeyboardObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTLinkingPlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTLinkingPlugins.h new file mode 120000 index 000000000..d90e57cd1 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTLinkingPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/LinkingIOS/RCTLinkingPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTLogBox.h b/ios/Pods/Headers/Private/React-Core/React/RCTLogBox.h new file mode 120000 index 000000000..1c62117bf --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTLogBox.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTLogBox.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTNetInfo.h b/ios/Pods/Headers/Private/React-Core/React/RCTNetInfo.h deleted file mode 120000 index 7a096e690..000000000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTNetInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/Libraries/Network/RCTNetInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTNetworkPlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTNetworkPlugins.h new file mode 120000 index 000000000..c927dfa33 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTNetworkPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Network/RCTNetworkPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTRedBox.h b/ios/Pods/Headers/Private/React-Core/React/RCTRedBox.h index a17cd2acf..39940a898 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTRedBox.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTRedBox.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTRedBox.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTRedBox.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTRedBoxSetEnabled.h b/ios/Pods/Headers/Private/React-Core/React/RCTRedBoxSetEnabled.h new file mode 120000 index 000000000..356daf6ff --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTRedBoxSetEnabled.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTRedBoxSetEnabled.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControl.h b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControl.h index a44be6e10..cb6d55dd1 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControl.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControl.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Views/RCTRefreshControl.h \ No newline at end of file +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControlManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControlManager.h index 8939ee0df..08c52d839 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControlManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshControlManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Views/RCTRefreshControlManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTRefreshableProtocol.h b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshableProtocol.h new file mode 120000 index 000000000..8646a6299 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTRefreshableProtocol.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshableProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTScrollEvent.h b/ios/Pods/Headers/Private/React-Core/React/RCTScrollEvent.h new file mode 120000 index 000000000..a0091281c --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTScrollEvent.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Views/ScrollView/RCTScrollEvent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTSettingsPlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTSettingsPlugins.h new file mode 120000 index 000000000..c03ab17e5 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTSettingsPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Settings/RCTSettingsPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTSourceCode.h b/ios/Pods/Headers/Private/React-Core/React/RCTSourceCode.h index 5d9c5d2af..e706c13ec 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTSourceCode.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTSourceCode.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTSourceCode.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTSourceCode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTStatusBarManager.h b/ios/Pods/Headers/Private/React-Core/React/RCTStatusBarManager.h index b112a8adc..bcc1757c4 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTStatusBarManager.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTStatusBarManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTStatusBarManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTStatusBarManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTTVNavigationEventEmitter.h b/ios/Pods/Headers/Private/React-Core/React/RCTTVNavigationEventEmitter.h new file mode 120000 index 000000000..83d07c0f6 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTTVNavigationEventEmitter.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTTVNavigationEventEmitter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTTiming.h b/ios/Pods/Headers/Private/React-Core/React/RCTTiming.h index 00772a3e6..af5ddad62 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTTiming.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTTiming.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTTiming.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTTiming.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTUtilsUIOverride.h b/ios/Pods/Headers/Private/React-Core/React/RCTUtilsUIOverride.h new file mode 120000 index 000000000..a5aaa954a --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTUtilsUIOverride.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTUtilsUIOverride.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTVibrationPlugins.h b/ios/Pods/Headers/Private/React-Core/React/RCTVibrationPlugins.h new file mode 120000 index 000000000..bae7f2445 --- /dev/null +++ b/ios/Pods/Headers/Private/React-Core/React/RCTVibrationPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Vibration/RCTVibrationPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketExecutor.h b/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketExecutor.h index 8737c8868..cbe306bec 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketExecutor.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketExecutor.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTWebSocketExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketModule.h b/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketModule.h index db623dff1..3e102ab26 120000 --- a/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketModule.h +++ b/ios/Pods/Headers/Private/React-Core/React/RCTWebSocketModule.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/WebSocket/RCTWebSocketModule.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTWebSocketModule.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/React-RCTBlob/RCTBlob/RCTBlobCollector.h b/ios/Pods/Headers/Private/React-RCTBlob/RCTBlob/RCTBlobCollector.h deleted file mode 120000 index 33df7e7ee..000000000 --- a/ios/Pods/Headers/Private/React-RCTBlob/RCTBlob/RCTBlobCollector.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/Libraries/Blob/RCTBlobCollector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/BridgeJSCallInvoker.h b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/BridgeJSCallInvoker.h index 5b8aac5df..08d71be32 120000 --- a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/BridgeJSCallInvoker.h +++ b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/BridgeJSCallInvoker.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/ReactCommon/jscallinvoker/ReactCommon/BridgeJSCallInvoker.h \ No newline at end of file +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/BridgeJSCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/CallInvoker.h b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/CallInvoker.h new file mode 120000 index 000000000..fb45cee52 --- /dev/null +++ b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/CallInvoker.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/CallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/JSCallInvoker.h b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/JSCallInvoker.h deleted file mode 120000 index 9fac1871a..000000000 --- a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/JSCallInvoker.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/ReactCommon/jscallinvoker/ReactCommon/JSCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h new file mode 120000 index 000000000..0720dccb3 --- /dev/null +++ b/ios/Pods/Headers/Private/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/MessageQueueThreadCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/ReactNativeART/ARTShadow.h b/ios/Pods/Headers/Private/ReactNativeART/ARTShadow.h new file mode 120000 index 000000000..054b91ce5 --- /dev/null +++ b/ios/Pods/Headers/Private/ReactNativeART/ARTShadow.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/art/ios/ARTShadow.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/Color+Interpolation.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/Color+Interpolation.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/Color+Interpolation.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/Color+Interpolation.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/LNAnimator.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNAnimator.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/LNAnimator.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNAnimator.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/LNInterpolable.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNInterpolable.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/LNInterpolable.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNInterpolable.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/LNInterpolation.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNInterpolation.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/LNInterpolation.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/LNInterpolation.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/NSValue+Interpolation.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/NSValue+Interpolation.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/NSValue+Interpolation.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/NSValue+Interpolation.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/RCTCustomInputController.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/RCTCustomInputController.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/RCTCustomInputController.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/RCTCustomInputController.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-input/RCTCustomKeyboardViewController.h b/ios/Pods/Headers/Private/ReactNativeKeyboardInput/RCTCustomKeyboardViewController.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-input/RCTCustomKeyboardViewController.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardInput/RCTCustomKeyboardViewController.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-tracking-view/KeyboardTrackingViewManager.h b/ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/KeyboardTrackingViewManager.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-tracking-view/KeyboardTrackingViewManager.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/KeyboardTrackingViewManager.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-tracking-view/ObservingInputAccessoryView.h b/ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/ObservingInputAccessoryView.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-tracking-view/ObservingInputAccessoryView.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/ObservingInputAccessoryView.h diff --git a/ios/Pods/Headers/Private/react-native-keyboard-tracking-view/UIResponder+FirstResponder.h b/ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/UIResponder+FirstResponder.h similarity index 100% rename from ios/Pods/Headers/Private/react-native-keyboard-tracking-view/UIResponder+FirstResponder.h rename to ios/Pods/Headers/Private/ReactNativeKeyboardTrackingView/UIResponder+FirstResponder.h diff --git a/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+RoundedCorners.h b/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+RoundedCorners.h deleted file mode 120000 index 18770a2b7..000000000 --- a/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+RoundedCorners.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/Private/NSBezierPath+RoundedCorners.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+SDRoundedCorners.h b/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+SDRoundedCorners.h new file mode 120000 index 000000000..2c6051aaa --- /dev/null +++ b/ios/Pods/Headers/Private/SDWebImage/NSBezierPath+SDRoundedCorners.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/Private/NSBezierPath+SDRoundedCorners.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/SDWebImage/UIColor+HexString.h b/ios/Pods/Headers/Private/SDWebImage/UIColor+HexString.h deleted file mode 120000 index 216a7580a..000000000 --- a/ios/Pods/Headers/Private/SDWebImage/UIColor+HexString.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/Private/UIColor+HexString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/SDWebImage/UIColor+SDHexString.h b/ios/Pods/Headers/Private/SDWebImage/UIColor+SDHexString.h new file mode 120000 index 000000000..2c443ec3e --- /dev/null +++ b/ios/Pods/Headers/Private/SDWebImage/UIColor+SDHexString.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/Private/UIColor+SDHexString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderInterface.h b/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderInterface.h new file mode 120000 index 000000000..0e04dcd08 --- /dev/null +++ b/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderInterface.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/Interfaces/UMAppLoaderInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderProvider.h b/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderProvider.h new file mode 120000 index 000000000..e05ce9f03 --- /dev/null +++ b/ios/Pods/Headers/Private/UMAppLoader/UMAppLoaderProvider.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/UMAppLoaderProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMAppLoader/UMAppRecordInterface.h b/ios/Pods/Headers/Private/UMAppLoader/UMAppRecordInterface.h new file mode 120000 index 000000000..8273e4ddb --- /dev/null +++ b/ios/Pods/Headers/Private/UMAppLoader/UMAppRecordInterface.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/Interfaces/UMAppRecordInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMCore/UMErrorCodes.h b/ios/Pods/Headers/Private/UMCore/UMErrorCodes.h new file mode 120000 index 000000000..ad5c734a4 --- /dev/null +++ b/ios/Pods/Headers/Private/UMCore/UMErrorCodes.h @@ -0,0 +1 @@ +../../../../../node_modules/@unimodules/core/ios/UMCore/UMErrorCodes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMPermissionsInterface/UMPermissionsMethodsDelegate.h b/ios/Pods/Headers/Private/UMPermissionsInterface/UMPermissionsMethodsDelegate.h new file mode 120000 index 000000000..5c0a00ac4 --- /dev/null +++ b/ios/Pods/Headers/Private/UMPermissionsInterface/UMPermissionsMethodsDelegate.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-permissions-interface/ios/UMPermissionsInterface/UMPermissionsMethodsDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h b/ios/Pods/Headers/Private/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h new file mode 120000 index 000000000..9f1d08845 --- /dev/null +++ b/ios/Pods/Headers/Private/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h @@ -0,0 +1 @@ +../../../../../node_modules/@unimodules/react-native-adapter/ios/UMReactNativeAdapter/UMModuleRegistryAdapter/UMModuleRegistryHolderReactModule.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Yoga/yoga/BitUtils.h b/ios/Pods/Headers/Private/Yoga/yoga/BitUtils.h new file mode 120000 index 000000000..202fc5aad --- /dev/null +++ b/ios/Pods/Headers/Private/Yoga/yoga/BitUtils.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/ReactCommon/yoga/yoga/BitUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Yoga/yoga/Bitfield.h b/ios/Pods/Headers/Private/Yoga/yoga/Bitfield.h deleted file mode 120000 index 494037b5f..000000000 --- a/ios/Pods/Headers/Private/Yoga/yoga/Bitfield.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/ReactCommon/yoga/yoga/Bitfield.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/YogaKit/UIView+Yoga.h b/ios/Pods/Headers/Private/YogaKit/UIView+Yoga.h new file mode 120000 index 000000000..611221e0e --- /dev/null +++ b/ios/Pods/Headers/Private/YogaKit/UIView+Yoga.h @@ -0,0 +1 @@ +../../../YogaKit/YogaKit/Source/UIView+Yoga.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/YogaKit/YGLayout+Private.h b/ios/Pods/Headers/Private/YogaKit/YGLayout+Private.h new file mode 120000 index 000000000..76fa33bcd --- /dev/null +++ b/ios/Pods/Headers/Private/YogaKit/YGLayout+Private.h @@ -0,0 +1 @@ +../../../YogaKit/YogaKit/Source/YGLayout+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/YogaKit/YGLayout.h b/ios/Pods/Headers/Private/YogaKit/YGLayout.h new file mode 120000 index 000000000..d58b91b9c --- /dev/null +++ b/ios/Pods/Headers/Private/YogaKit/YGLayout.h @@ -0,0 +1 @@ +../../../YogaKit/YogaKit/Source/YGLayout.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-notifications/RNNotificationCenterMulticast.h b/ios/Pods/Headers/Private/react-native-notifications/RNNotificationCenterMulticast.h new file mode 120000 index 000000000..3ed47c5e1 --- /dev/null +++ b/ios/Pods/Headers/Private/react-native-notifications/RNNotificationCenterMulticast.h @@ -0,0 +1 @@ +../../../../../node_modules/react-native-notifications/RNNotifications/RNNotificationCenterMulticast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-video/RCTVideo.h b/ios/Pods/Headers/Private/react-native-video/RCTVideo.h deleted file mode 120000 index ea6d68e02..000000000 --- a/ios/Pods/Headers/Private/react-native-video/RCTVideo.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-video/RCTVideoManager.h b/ios/Pods/Headers/Private/react-native-video/RCTVideoManager.h deleted file mode 120000 index 09465d815..000000000 --- a/ios/Pods/Headers/Private/react-native-video/RCTVideoManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewController.h b/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewController.h deleted file mode 120000 index 907439772..000000000 --- a/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewControllerDelegate.h b/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewControllerDelegate.h deleted file mode 120000 index 3ff8dff0e..000000000 --- a/ios/Pods/Headers/Private/react-native-video/RCTVideoPlayerViewControllerDelegate.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoPlayerViewControllerDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-video/UIView+FindUIViewController.h b/ios/Pods/Headers/Private/react-native-video/UIView+FindUIViewController.h deleted file mode 120000 index 0bf5e182c..000000000 --- a/ios/Pods/Headers/Private/react-native-video/UIView+FindUIViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/UIView+FindUIViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-webview/RNCWKProcessPoolManager.h b/ios/Pods/Headers/Private/react-native-webview/RNCWKProcessPoolManager.h index b9b4d0a98..462906351 120000 --- a/ios/Pods/Headers/Private/react-native-webview/RNCWKProcessPoolManager.h +++ b/ios/Pods/Headers/Private/react-native-webview/RNCWKProcessPoolManager.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWKProcessPoolManager.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWKProcessPoolManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-webview/RNCWebView.h b/ios/Pods/Headers/Private/react-native-webview/RNCWebView.h index c814fcebc..5168adbcc 120000 --- a/ios/Pods/Headers/Private/react-native-webview/RNCWebView.h +++ b/ios/Pods/Headers/Private/react-native-webview/RNCWebView.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWebView.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWebView.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/react-native-webview/RNCWebViewManager.h b/ios/Pods/Headers/Private/react-native-webview/RNCWebViewManager.h index c20dd096e..ef2014d2b 120000 --- a/ios/Pods/Headers/Private/react-native-webview/RNCWebViewManager.h +++ b/ios/Pods/Headers/Private/react-native-webview/RNCWebViewManager.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWebViewManager.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWebViewManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSGConnectivity.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSGConnectivity.h new file mode 120000 index 000000000..ba526a52e --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSGConnectivity.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BSGConnectivity.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSGOutOfMemoryWatchdog.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSGOutOfMemoryWatchdog.h new file mode 120000 index 000000000..2bbb04bfd --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSGOutOfMemoryWatchdog.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BSGOutOfMemoryWatchdog.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSGSerialization.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSGSerialization.h new file mode 120000 index 000000000..ff094d982 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSGSerialization.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BSGSerialization.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSArchSpecific.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSArchSpecific.h new file mode 120000 index 000000000..0691a2ef8 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSArchSpecific.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSArchSpecific.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace.h new file mode 120000 index 000000000..47b50f2d9 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSBacktrace.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace_Private.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace_Private.h new file mode 120000 index 000000000..ed66fd24a --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSBacktrace_Private.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSBacktrace_Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrash.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrash.h new file mode 120000 index 000000000..b2434789e --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrash.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashAdvanced.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashAdvanced.h new file mode 120000 index 000000000..ad8af7cd3 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashAdvanced.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashC.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashC.h new file mode 120000 index 000000000..b86ea33d4 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashC.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashCallCompletion.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashCallCompletion.h new file mode 120000 index 000000000..b57c08d5b --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashCallCompletion.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashContext.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashContext.h new file mode 120000 index 000000000..e4c04791a --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashContext.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashContext.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashDoctor.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashDoctor.h new file mode 120000 index 000000000..80cbf55aa --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashDoctor.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashDoctor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashIdentifier.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashIdentifier.h new file mode 120000 index 000000000..4ced5a652 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashIdentifier.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReport.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReport.h new file mode 120000 index 000000000..88348ea05 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReport.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFields.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFields.h new file mode 120000 index 000000000..3a355dc7f --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFields.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportFields.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilter.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilter.h new file mode 120000 index 000000000..92a2aef00 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilter.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilterCompletion.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilterCompletion.h new file mode 120000 index 000000000..afd65e741 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportFilterCompletion.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilterCompletion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportStore.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportStore.h new file mode 120000 index 000000000..c60c3dbae --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportStore.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportVersion.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportVersion.h new file mode 120000 index 000000000..5f3c4a7cc --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashReportVersion.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportVersion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry.h new file mode 120000 index 000000000..8893e0ceb --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_CPPException.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_CPPException.h new file mode 120000 index 000000000..1410087f4 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_CPPException.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_MachException.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_MachException.h new file mode 120000 index 000000000..c3b2eabb1 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_MachException.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_NSException.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_NSException.h new file mode 120000 index 000000000..f7f9c6ca6 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_NSException.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Private.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Private.h new file mode 120000 index 000000000..c19dbe4fc --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Private.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Signal.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Signal.h new file mode 120000 index 000000000..bfa29ab2d --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_Signal.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_User.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_User.h new file mode 120000 index 000000000..38776f884 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashSentry_User.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashState.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashState.h new file mode 120000 index 000000000..c1dbc9924 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashState.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashType.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashType.h new file mode 120000 index 000000000..5ef29e0cc --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSCrashType.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashType.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSDynamicLinker.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSDynamicLinker.h new file mode 120000 index 000000000..d648ff817 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSDynamicLinker.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSFileUtils.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSFileUtils.h new file mode 120000 index 000000000..aa81d07d8 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSFileUtils.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodec.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodec.h new file mode 120000 index 000000000..f137945c9 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodec.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSJSONCodec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodecObjC.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodecObjC.h new file mode 120000 index 000000000..9473418b3 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSJSONCodecObjC.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSJSONCodecObjC.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSLogger.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSLogger.h new file mode 120000 index 000000000..ede47600c --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSLogger.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMach.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMach.h new file mode 120000 index 000000000..707b63b3f --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMach.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMachApple.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMachApple.h new file mode 120000 index 000000000..c5aeaa3b6 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSMachApple.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachApple.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjC.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjC.h new file mode 120000 index 000000000..3f2435544 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjC.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSObjC.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjCApple.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjCApple.h new file mode 120000 index 000000000..d783942bd --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSObjCApple.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSObjCApple.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSignalInfo.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSignalInfo.h new file mode 120000 index 000000000..ed76913f7 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSignalInfo.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSSignalInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSingleton.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSingleton.h new file mode 120000 index 000000000..c345b6264 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSingleton.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSSingleton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSString.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSString.h new file mode 120000 index 000000000..cafd37769 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSString.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSysCtl.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSysCtl.h new file mode 120000 index 000000000..2b488e2c3 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSysCtl.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSSysCtl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemCapabilities.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemCapabilities.h new file mode 120000 index 000000000..d8318f87c --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemCapabilities.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSSystemCapabilities.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfo.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfo.h new file mode 120000 index 000000000..ec4fee2de --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfo.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfoC.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfoC.h new file mode 120000 index 000000000..2d24e1cff --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_KSSystemInfoC.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfoC.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BSG_RFC3339DateTool.h b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_RFC3339DateTool.h new file mode 120000 index 000000000..3182012d8 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BSG_RFC3339DateTool.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagApiClient.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagApiClient.h new file mode 120000 index 000000000..85e8b00af --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagApiClient.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCollections.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCollections.h new file mode 120000 index 000000000..3f6410e96 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCollections.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCollections.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCrashSentry.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCrashSentry.h new file mode 120000 index 000000000..8c40c0e97 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagCrashSentry.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashSentry.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagErrorReportApiClient.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagErrorReportApiClient.h new file mode 120000 index 000000000..8354707ae --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagErrorReportApiClient.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagErrorReportApiClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagFileStore.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagFileStore.h new file mode 120000 index 000000000..7c41db05d --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagFileStore.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagHandledState.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagHandledState.h new file mode 120000 index 000000000..c886975fc --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagHandledState.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagHandledState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKSCrashSysInfoParser.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKSCrashSysInfoParser.h new file mode 120000 index 000000000..36eab6f7c --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKSCrashSysInfoParser.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKSCrashSysInfoParser.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKeys.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKeys.h new file mode 120000 index 000000000..ba6ca62c1 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagKeys.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKeys.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagLogger.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagLogger.h new file mode 120000 index 000000000..9d008fdcb --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagLogger.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagNotifier.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagNotifier.h new file mode 120000 index 000000000..2c4b311f4 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagNotifier.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagPlugin.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagPlugin.h new file mode 120000 index 000000000..764dc5181 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagPlugin.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSession.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSession.h new file mode 120000 index 000000000..2ddf68710 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSession.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionFileStore.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionFileStore.h new file mode 120000 index 000000000..108e46a0a --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionFileStore.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionFileStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTracker.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTracker.h new file mode 120000 index 000000000..b407af823 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTracker.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingApiClient.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingApiClient.h new file mode 120000 index 000000000..49e3ae260 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingApiClient.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingApiClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingPayload.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingPayload.h new file mode 120000 index 000000000..8bdd9130c --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSessionTrackingPayload.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingPayload.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSink.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSink.h new file mode 120000 index 000000000..2edea8658 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagSink.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagUser.h b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagUser.h new file mode 120000 index 000000000..37960c13e --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/BugsnagUser.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/BugsnagUser.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/NSError+BSG_SimpleConstructor.h b/ios/Pods/Headers/Public/BugsnagReactNative/NSError+BSG_SimpleConstructor.h new file mode 120000 index 000000000..0db83f92c --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/NSError+BSG_SimpleConstructor.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/NSError+BSG_SimpleConstructor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/BugsnagReactNative/Private.h b/ios/Pods/Headers/Public/BugsnagReactNative/Private.h new file mode 120000 index 000000000..8f61e6806 --- /dev/null +++ b/ios/Pods/Headers/Public/BugsnagReactNative/Private.h @@ -0,0 +1 @@ +../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncSocket.h b/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncSocket.h new file mode 120000 index 000000000..747af0852 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncSocket.h @@ -0,0 +1 @@ +../../../CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncUdpSocket.h b/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncUdpSocket.h new file mode 120000 index 000000000..0312a7010 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaAsyncSocket/GCDAsyncUdpSocket.h @@ -0,0 +1 @@ +../../../CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/evdns.h b/ios/Pods/Headers/Public/CocoaLibEvent/evdns.h new file mode 120000 index 000000000..79308f581 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/evdns.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evdns.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event.h b/ios/Pods/Headers/Public/CocoaLibEvent/event.h new file mode 120000 index 000000000..f0df150ba --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer.h new file mode 120000 index 000000000..47fa55c24 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/buffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer_compat.h new file mode 120000 index 000000000..932561ee4 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/buffer_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/buffer_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent.h new file mode 120000 index 000000000..ec2ad6fd9 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_compat.h new file mode 120000 index 000000000..ba6b1d368 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_ssl.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_ssl.h new file mode 120000 index 000000000..6cc5ad2be --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_ssl.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_ssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_struct.h new file mode 120000 index 000000000..7c5c4d0f8 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/bufferevent_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/bufferevent_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns.h new file mode 120000 index 000000000..0e50dbc28 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_compat.h new file mode 120000 index 000000000..801ab18f9 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_struct.h new file mode 120000 index 000000000..b68af8f1c --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/dns_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/dns_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/event-config.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event-config.h new file mode 120000 index 000000000..0e5284539 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event-config.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event-config.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/event.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event.h new file mode 120000 index 000000000..e70176082 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_compat.h new file mode 120000 index 000000000..58bd2433e --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_struct.h new file mode 120000 index 000000000..b4b36c5c9 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/event_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/event_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/http.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http.h new file mode 120000 index 000000000..6857f0c07 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_compat.h new file mode 120000 index 000000000..169ba3d6e --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_struct.h new file mode 120000 index 000000000..51de61806 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/http_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/http_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/keyvalq_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/keyvalq_struct.h new file mode 120000 index 000000000..6b487cd2e --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/keyvalq_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/keyvalq_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/listener.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/listener.h new file mode 120000 index 000000000..9f0d36c2d --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/listener.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/listener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc.h new file mode 120000 index 000000000..15bdd9fc7 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_compat.h new file mode 120000 index 000000000..c3c4960c3 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_struct.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_struct.h new file mode 120000 index 000000000..e9ab6b44d --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/rpc_struct.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/rpc_struct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag.h new file mode 120000 index 000000000..4faf06d83 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/tag.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag_compat.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag_compat.h new file mode 120000 index 000000000..6f727474c --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/tag_compat.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/tag_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/thread.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/thread.h new file mode 120000 index 000000000..963efacbf --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/thread.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/thread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/util.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/util.h new file mode 120000 index 000000000..e2809bf9a --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/util.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/util.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/event2/visibility.h b/ios/Pods/Headers/Public/CocoaLibEvent/event2/visibility.h new file mode 120000 index 000000000..4fcd1f001 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/event2/visibility.h @@ -0,0 +1 @@ +../../../../CocoaLibEvent/src/event2/visibility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/evhttp.h b/ios/Pods/Headers/Public/CocoaLibEvent/evhttp.h new file mode 120000 index 000000000..c4f8edbc5 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/evhttp.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evhttp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/evrpc.h b/ios/Pods/Headers/Public/CocoaLibEvent/evrpc.h new file mode 120000 index 000000000..3c47809a5 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/evrpc.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evrpc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/CocoaLibEvent/evutil.h b/ios/Pods/Headers/Public/CocoaLibEvent/evutil.h new file mode 120000 index 000000000..51d5519e8 --- /dev/null +++ b/ios/Pods/Headers/Public/CocoaLibEvent/evutil.h @@ -0,0 +1 @@ +../../../CocoaLibEvent/src/evutil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXAV/EXAudioRecordingPermissionRequester.h b/ios/Pods/Headers/Public/EXAV/EXAudioRecordingPermissionRequester.h new file mode 120000 index 000000000..5b8d1af04 --- /dev/null +++ b/ios/Pods/Headers/Public/EXAV/EXAudioRecordingPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-av/ios/EXAV/EXAudioRecordingPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderInterface.h b/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderInterface.h deleted file mode 120000 index e070a7851..000000000 --- a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderInterface.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/Interfaces/EXAppLoaderInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderProvider.h b/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderProvider.h deleted file mode 120000 index 8036fe182..000000000 --- a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppLoaderProvider.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/EXAppLoaderProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppRecordInterface.h b/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppRecordInterface.h deleted file mode 120000 index 9ed829b6b..000000000 --- a/ios/Pods/Headers/Public/EXAppLoaderProvider/EXAppRecordInterface.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-app-loader-provider/ios/EXAppLoaderProvider/Interfaces/EXAppRecordInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXImageLoader/EXImageLoader.h b/ios/Pods/Headers/Public/EXImageLoader/EXImageLoader.h new file mode 120000 index 000000000..473e0322c --- /dev/null +++ b/ios/Pods/Headers/Public/EXImageLoader/EXImageLoader.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-image-loader/ios/EXImageLoader/EXImageLoader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXKeepAwake/EXKeepAwake.h b/ios/Pods/Headers/Public/EXKeepAwake/EXKeepAwake.h new file mode 120000 index 000000000..9eeda1fc7 --- /dev/null +++ b/ios/Pods/Headers/Public/EXKeepAwake/EXKeepAwake.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-keep-awake/ios/EXKeepAwake/EXKeepAwake.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXLocalAuthentication/EXLocalAuthentication.h b/ios/Pods/Headers/Public/EXLocalAuthentication/EXLocalAuthentication.h new file mode 120000 index 000000000..44a177344 --- /dev/null +++ b/ios/Pods/Headers/Public/EXLocalAuthentication/EXLocalAuthentication.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-local-authentication/ios/EXLocalAuthentication/EXLocalAuthentication.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXAudioRecordingPermissionRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXAudioRecordingPermissionRequester.h deleted file mode 120000 index d4ca7c2bc..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXAudioRecordingPermissionRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXAudioRecordingPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXCalendarRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXCalendarRequester.h deleted file mode 120000 index b0fe45f50..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXCalendarRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCalendarRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXCameraPermissionRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXCameraPermissionRequester.h deleted file mode 120000 index de574f490..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXCameraPermissionRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCameraPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXCameraRollRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXCameraRollRequester.h deleted file mode 120000 index d062f5f9c..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXCameraRollRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXCameraRollRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXContactsRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXContactsRequester.h deleted file mode 120000 index 43bb947f3..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXContactsRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXContactsRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXLocationRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXLocationRequester.h deleted file mode 120000 index de4b28b28..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXLocationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXLocationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXRemindersRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXRemindersRequester.h deleted file mode 120000 index 04a73bfb1..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXRemindersRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXRemindersRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationPermissionRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationPermissionRequester.h new file mode 120000 index 000000000..b78040b03 --- /dev/null +++ b/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-permissions/ios/EXPermissions/Requesters/RemoteNotification/EXRemoteNotificationPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationRequester.h deleted file mode 120000 index 3f9869b58..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXRemoteNotificationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXRemoteNotificationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXSystemBrightnessRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXSystemBrightnessRequester.h deleted file mode 120000 index 9bf70ebc5..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXSystemBrightnessRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXSystemBrightnessRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationPermissionRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationPermissionRequester.h new file mode 120000 index 000000000..0c452886b --- /dev/null +++ b/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationPermissionRequester.h @@ -0,0 +1 @@ +../../../../../node_modules/expo-permissions/ios/EXPermissions/Requesters/UserNotification/EXUserNotificationPermissionRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationRequester.h b/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationRequester.h deleted file mode 120000 index aab5405f0..000000000 --- a/ios/Pods/Headers/Public/EXPermissions/EXUserNotificationRequester.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/expo-permissions/ios/EXPermissions/EXUserNotificationRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h b/ios/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h deleted file mode 120000 index 2a09cf0df..000000000 --- a/ios/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Public/FIRInstanceID.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID.h b/ios/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID.h deleted file mode 120000 index fdf0d912e..000000000 --- a/ios/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -../../../FirebaseInstanceID/Firebase/InstanceID/Public/FirebaseInstanceID.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h new file mode 120000 index 000000000..86f25220e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/bignum-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum.h new file mode 120000 index 000000000..52eb949cc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/bignum.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/bignum.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/cached-powers.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/cached-powers.h new file mode 120000 index 000000000..60ac107d2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/cached-powers.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/cached-powers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/diy-fp.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/diy-fp.h new file mode 120000 index 000000000..75887c71d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/diy-fp.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/diy-fp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/double-conversion.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/double-conversion.h new file mode 120000 index 000000000..b4f9e9fd6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/double-conversion.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/double-conversion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fast-dtoa.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fast-dtoa.h new file mode 120000 index 000000000..789a9bf3d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fast-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/fast-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h new file mode 120000 index 000000000..e78574950 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/fixed-dtoa.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/fixed-dtoa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/ieee.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/ieee.h new file mode 120000 index 000000000..661051029 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/ieee.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/ieee.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/strtod.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/strtod.h new file mode 120000 index 000000000..54f180c3d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/strtod.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/strtod.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/utils.h b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/utils.h new file mode 120000 index 000000000..84aa4cc85 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-DoubleConversion/double-conversion/utils.h @@ -0,0 +1 @@ +../../../../Flipper-DoubleConversion/double-conversion/utils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray-inl.h new file mode 120000 index 000000000..3dfb84d57 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashArray-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray.h new file mode 120000 index 000000000..4e0bbb685 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashArray.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashArray.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap-inl.h new file mode 120000 index 000000000..d26ec377c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashMap-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap.h new file mode 120000 index 000000000..9f2d31a32 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicHashMap.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h new file mode 120000 index 000000000..38326393b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicIntrusiveLinkedList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicIntrusiveLinkedList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicLinkedList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicLinkedList.h new file mode 120000 index 000000000..b8cc03425 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicLinkedList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicLinkedList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicUnorderedMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicUnorderedMap.h new file mode 120000 index 000000000..6c6f46910 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/AtomicUnorderedMap.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/AtomicUnorderedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Benchmark.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Benchmark.h new file mode 120000 index 000000000..ca92b4785 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Benchmark.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Benchmark.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Bits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Bits.h new file mode 120000 index 000000000..a2c1e91eb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Bits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/CPortability.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/CPortability.h new file mode 120000 index 000000000..ff09af640 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/CPortability.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CPortability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken-inl.h new file mode 120000 index 000000000..c203959fd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CancellationToken-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken.h new file mode 120000 index 000000000..85f5be659 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/CancellationToken.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CancellationToken.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Chrono.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Chrono.h new file mode 120000 index 000000000..a13793257 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Chrono.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Chrono.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ClockGettimeWrappers.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ClockGettimeWrappers.h new file mode 120000 index 000000000..46c59f913 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ClockGettimeWrappers.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ClockGettimeWrappers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentBitSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentBitSet.h new file mode 120000 index 000000000..56d054436 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentBitSet.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentBitSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList-inl.h new file mode 120000 index 000000000..ea825d22f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentSkipList-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList.h new file mode 120000 index 000000000..0e4faf7d0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConcurrentSkipList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConcurrentSkipList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ConstexprMath.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConstexprMath.h new file mode 120000 index 000000000..837c3fbb6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ConstexprMath.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ConstexprMath.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Conv.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Conv.h new file mode 120000 index 000000000..359e50e5b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Conv.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Conv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/CppAttributes.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/CppAttributes.h new file mode 120000 index 000000000..498f5d696 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/CppAttributes.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CppAttributes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/CpuId.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/CpuId.h new file mode 120000 index 000000000..802357b15 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/CpuId.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/CpuId.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/DefaultKeepAliveExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/DefaultKeepAliveExecutor.h new file mode 120000 index 000000000..7982f82bc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/DefaultKeepAliveExecutor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DefaultKeepAliveExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Demangle.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Demangle.h new file mode 120000 index 000000000..84ea44f5f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Demangle.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Demangle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/DiscriminatedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/DiscriminatedPtr.h new file mode 120000 index 000000000..c26286890 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/DiscriminatedPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DiscriminatedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/DynamicConverter.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/DynamicConverter.h new file mode 120000 index 000000000..219d85b34 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/DynamicConverter.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/DynamicConverter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Exception.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Exception.h new file mode 120000 index 000000000..7fc5dd0ec --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Exception.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Exception.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionString.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionString.h new file mode 120000 index 000000000..01679998b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper-inl.h new file mode 120000 index 000000000..34f080fde --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionWrapper-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper.h new file mode 120000 index 000000000..a3f711ce4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ExceptionWrapper.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ExceptionWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Executor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Executor.h new file mode 120000 index 000000000..1719b8ff5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Executor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Executor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Expected.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Expected.h new file mode 120000 index 000000000..c24a83f3a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Expected.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Expected.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FBString.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FBString.h new file mode 120000 index 000000000..61ee3dbe0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FBString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FBString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FBVector.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FBVector.h new file mode 120000 index 000000000..270424bcc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FBVector.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FBVector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/File.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/File.h new file mode 120000 index 000000000..816be15d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/File.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/File.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FileUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FileUtil.h new file mode 120000 index 000000000..1198acb7d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FileUtil.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FileUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Fingerprint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Fingerprint.h new file mode 120000 index 000000000..bdd133571 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Fingerprint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Fingerprint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FixedString.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FixedString.h new file mode 120000 index 000000000..11f1efeff --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FixedString.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FixedString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Format-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Format-inl.h new file mode 120000 index 000000000..b2e6873cc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Format-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Format-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Format.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Format.h new file mode 120000 index 000000000..76450d1ca --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Format.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Format.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatArg.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatArg.h new file mode 120000 index 000000000..54caf03b5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatArg.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FormatArg.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatTraits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatTraits.h new file mode 120000 index 000000000..6c5cfb2a8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/FormatTraits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/FormatTraits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Function.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Function.h new file mode 120000 index 000000000..df6385768 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Function.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Function.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/GLog.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/GLog.h new file mode 120000 index 000000000..ad4494d99 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/GLog.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/GLog.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/GroupVarint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/GroupVarint.h new file mode 120000 index 000000000..b7f6163a5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/GroupVarint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/GroupVarint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Hash.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Hash.h new file mode 120000 index 000000000..bec889062 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Hash.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Hash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddress.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddress.h new file mode 120000 index 000000000..55d02498d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressException.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressException.h new file mode 120000 index 000000000..2653e47c5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressException.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV4.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV4.h new file mode 120000 index 000000000..7ae51e99c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV4.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressV4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV6.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV6.h new file mode 120000 index 000000000..24a48a575 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IPAddressV6.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IPAddressV6.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Indestructible.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Indestructible.h new file mode 120000 index 000000000..8092a0606 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Indestructible.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Indestructible.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IndexedMemPool.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IndexedMemPool.h new file mode 120000 index 000000000..6f24f6802 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IndexedMemPool.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IndexedMemPool.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/IntrusiveList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/IntrusiveList.h new file mode 120000 index 000000000..8c7c12ce3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/IntrusiveList.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/IntrusiveList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Lazy.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Lazy.h new file mode 120000 index 000000000..1a946884d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Lazy.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Lazy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Likely.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Likely.h new file mode 120000 index 000000000..60476602e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Likely.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Likely.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/LockTraits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/LockTraits.h new file mode 120000 index 000000000..2143a864f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/LockTraits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/LockTraits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCPipeline.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCPipeline.h new file mode 120000 index 000000000..b4ec9f063 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCPipeline.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MPMCPipeline.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCQueue.h new file mode 120000 index 000000000..951db2ce4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MPMCQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MacAddress.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MacAddress.h new file mode 120000 index 000000000..e39b2d426 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MacAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MacAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MapUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MapUtil.h new file mode 120000 index 000000000..99f992156 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MapUtil.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MapUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Math.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Math.h new file mode 120000 index 000000000..6ade47a33 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Math.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Math.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Memory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Memory.h new file mode 120000 index 000000000..160741115 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Memory.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Memory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroLock.h new file mode 120000 index 000000000..6636a6eaf --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MicroLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroSpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroSpinLock.h new file mode 120000 index 000000000..da757ff47 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MicroSpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MicroSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/MoveWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/MoveWrapper.h new file mode 120000 index 000000000..5b6c2c667 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/MoveWrapper.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/MoveWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Optional.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Optional.h new file mode 120000 index 000000000..560db199a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Optional.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Optional.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Overload.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Overload.h new file mode 120000 index 000000000..b29defd5d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Overload.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Overload.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/PackedSyncPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/PackedSyncPtr.h new file mode 120000 index 000000000..b86c857dc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/PackedSyncPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/PackedSyncPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Padded.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Padded.h new file mode 120000 index 000000000..b3ca3f4a7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Padded.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Padded.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly-inl.h new file mode 120000 index 000000000..625771815 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Poly-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly.h new file mode 120000 index 000000000..073a154fe --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Poly.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Poly.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/PolyException.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/PolyException.h new file mode 120000 index 000000000..a923071e3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/PolyException.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/PolyException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Portability.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Portability.h new file mode 120000 index 000000000..2b9f0afca --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Portability.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Portability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Preprocessor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Preprocessor.h new file mode 120000 index 000000000..15bd95020 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Preprocessor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Preprocessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ProducerConsumerQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ProducerConsumerQueue.h new file mode 120000 index 000000000..a1ee75a64 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ProducerConsumerQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ProducerConsumerQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/RWSpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/RWSpinLock.h new file mode 120000 index 000000000..b78f9e6e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/RWSpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/RWSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Random-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Random-inl.h new file mode 120000 index 000000000..ee0dbba11 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Random-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Random-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Random.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Random.h new file mode 120000 index 000000000..adefd8050 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Random.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Random.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Range.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Range.h new file mode 120000 index 000000000..3bfa432d9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Range.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Range.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Replaceable.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Replaceable.h new file mode 120000 index 000000000..67cf6113e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Replaceable.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Replaceable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ScopeGuard.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ScopeGuard.h new file mode 120000 index 000000000..9112fc651 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ScopeGuard.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ScopeGuard.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/SharedMutex.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/SharedMutex.h new file mode 120000 index 000000000..28337811c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/SharedMutex.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SharedMutex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton-inl.h new file mode 120000 index 000000000..bbf1da9cd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Singleton-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton.h new file mode 120000 index 000000000..eaa7fc447 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Singleton.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Singleton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/SingletonThreadLocal.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/SingletonThreadLocal.h new file mode 120000 index 000000000..146138cf6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/SingletonThreadLocal.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SingletonThreadLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/SocketAddress.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/SocketAddress.h new file mode 120000 index 000000000..f0a89f7ec --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/SocketAddress.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SocketAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/SpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/SpinLock.h new file mode 120000 index 000000000..52a66ef95 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/SpinLock.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/String-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/String-inl.h new file mode 120000 index 000000000..699a0f69e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/String-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/String-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/String.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/String.h new file mode 120000 index 000000000..73d4249c1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/String.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Subprocess.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Subprocess.h new file mode 120000 index 000000000..950cd53d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Subprocess.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Subprocess.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Synchronized.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Synchronized.h new file mode 120000 index 000000000..06ffbcc44 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Synchronized.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Synchronized.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/SynchronizedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/SynchronizedPtr.h new file mode 120000 index 000000000..02236f64f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/SynchronizedPtr.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/SynchronizedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadCachedInt.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadCachedInt.h new file mode 120000 index 000000000..c42848910 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadCachedInt.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ThreadCachedInt.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadLocal.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadLocal.h new file mode 120000 index 000000000..ac21ca746 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ThreadLocal.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/ThreadLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/TimeoutQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/TimeoutQueue.h new file mode 120000 index 000000000..6c0f3ac0e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/TimeoutQueue.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/TimeoutQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/TokenBucket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/TokenBucket.h new file mode 120000 index 000000000..66a08797b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/TokenBucket.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/TokenBucket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Traits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Traits.h new file mode 120000 index 000000000..768244ec4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Traits.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Traits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Try-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Try-inl.h new file mode 120000 index 000000000..b2a6668e1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Try-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Try-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Try.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Try.h new file mode 120000 index 000000000..b5c049719 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Try.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Try.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/UTF8String.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/UTF8String.h new file mode 120000 index 000000000..89cb3a59a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/UTF8String.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/UTF8String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Unicode.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Unicode.h new file mode 120000 index 000000000..8f6263d56 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Unicode.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Unicode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Unit.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Unit.h new file mode 120000 index 000000000..4448713c1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Unit.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Unit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri-inl.h new file mode 120000 index 000000000..b15ecbc22 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Uri-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri.h new file mode 120000 index 000000000..a3f58f808 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Uri.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Uri.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Utility.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Utility.h new file mode 120000 index 000000000..55394812f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Utility.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Utility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/Varint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/Varint.h new file mode 120000 index 000000000..e2df677a2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/Varint.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/Varint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/VirtualExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/VirtualExecutor.h new file mode 120000 index 000000000..9e1ffd04a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/VirtualExecutor.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/VirtualExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Conv.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Conv.h new file mode 120000 index 000000000..675b15e47 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Conv.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/chrono/Conv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Hardware.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Hardware.h new file mode 120000 index 000000000..a993fb62d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/chrono/Hardware.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/chrono/Hardware.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h new file mode 120000 index 000000000..7d3273c45 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/AtomicSharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/AtomicSharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CacheLocality.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CacheLocality.h new file mode 120000 index 000000000..5b183b5f2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CacheLocality.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/CacheLocality.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h new file mode 120000 index 000000000..26f4880ac --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/ConcurrentHashMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/ConcurrentHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h new file mode 120000 index 000000000..e7e22f6b5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/CoreCachedSharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h new file mode 120000 index 000000000..77250fb1b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/DynamicBoundedQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h new file mode 120000 index 000000000..34b95c0f3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/PriorityUnboundedQueueSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/UnboundedQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/UnboundedQueue.h new file mode 120000 index 000000000..37f466a8f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/concurrency/UnboundedQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/concurrency/UnboundedQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Access.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Access.h new file mode 120000 index 000000000..be0025143 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Access.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Access.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Array.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Array.h new file mode 120000 index 000000000..4e8e5ac78 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Array.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Array.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/BitIterator.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/BitIterator.h new file mode 120000 index 000000000..ddc23381c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/BitIterator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/BitIterator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Enumerate.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Enumerate.h new file mode 120000 index 000000000..6956f7088 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Enumerate.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Enumerate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/EvictingCacheMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/EvictingCacheMap.h new file mode 120000 index 000000000..035695056 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/EvictingCacheMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/EvictingCacheMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map-fwd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map-fwd.h new file mode 120000 index 000000000..bfcdce152 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Map-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map.h new file mode 120000 index 000000000..c903d4a0d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Map.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Map.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set-fwd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set-fwd.h new file mode 120000 index 000000000..d9a9232f5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Set-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set.h new file mode 120000 index 000000000..79ef8ad54 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/F14Set.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/F14Set.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach-inl.h new file mode 120000 index 000000000..01190ebf5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Foreach-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach.h new file mode 120000 index 000000000..f21d0a53c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Foreach.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Foreach.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h new file mode 120000 index 000000000..7623cb35e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/HeterogeneousAccess-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess.h new file mode 120000 index 000000000..b7a51c2f5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/HeterogeneousAccess.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/HeterogeneousAccess.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Iterator.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Iterator.h new file mode 120000 index 000000000..68c982271 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Iterator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Iterator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Merge.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Merge.h new file mode 120000 index 000000000..38fe76960 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/Merge.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/Merge.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/SparseByteSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/SparseByteSet.h new file mode 120000 index 000000000..59b048180 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/SparseByteSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/container/SparseByteSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/BitIteratorDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/BitIteratorDetail.h new file mode 120000 index 000000000..091e53115 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/BitIteratorDetail.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/BitIteratorDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Defaults.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Defaults.h new file mode 120000 index 000000000..c6c7107ca --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Defaults.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Defaults.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h new file mode 120000 index 000000000..3223e0324 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14IntrinsicsAvailability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14MapFallback.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14MapFallback.h new file mode 120000 index 000000000..e6f1428a3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14MapFallback.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14MapFallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Mask.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Mask.h new file mode 120000 index 000000000..c23e8cf52 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Mask.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Mask.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Policy.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Policy.h new file mode 120000 index 000000000..ba46bfa3d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Policy.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Policy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14SetFallback.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14SetFallback.h new file mode 120000 index 000000000..0c878d9d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14SetFallback.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14SetFallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Table.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Table.h new file mode 120000 index 000000000..d7153a66d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/F14Table.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/F14Table.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/Util.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/Util.h new file mode 120000 index 000000000..1efb2a9a3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/container/detail/Util.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/container/detail/Util.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AsyncTrace.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AsyncTrace.h new file mode 120000 index 000000000..355d5ebb5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AsyncTrace.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AsyncTrace.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtFork.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtFork.h new file mode 120000 index 000000000..fe3b3e785 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtFork.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtFork.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicHashUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicHashUtils.h new file mode 120000 index 000000000..bb1f8b49f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicHashUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtomicHashUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h new file mode 120000 index 000000000..adad9b742 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/AtomicUnorderedMapUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Demangle.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Demangle.h new file mode 120000 index 000000000..d79647581 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Demangle.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Demangle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h new file mode 120000 index 000000000..59cac25ee --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/DiscriminatedPtrDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FileUtilDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FileUtilDetail.h new file mode 120000 index 000000000..0e13a0bff --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FileUtilDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/FileUtilDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FingerprintPolynomial.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FingerprintPolynomial.h new file mode 120000 index 000000000..dec5e5ffe --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/FingerprintPolynomial.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/FingerprintPolynomial.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex-inl.h new file mode 120000 index 000000000..afc21a520 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Futex-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex.h new file mode 120000 index 000000000..d4e2148c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Futex.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Futex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/GroupVarintDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/GroupVarintDetail.h new file mode 120000 index 000000000..6495b47a1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/GroupVarintDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/GroupVarintDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddress.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddress.h new file mode 120000 index 000000000..31a8eb818 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddress.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/IPAddress.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddressSource.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddressSource.h new file mode 120000 index 000000000..1121df078 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/IPAddressSource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/IPAddressSource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Iterators.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Iterators.h new file mode 120000 index 000000000..3a682cabf --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Iterators.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Iterators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MPMCPipelineDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MPMCPipelineDetail.h new file mode 120000 index 000000000..40bf59a9b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MPMCPipelineDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/MPMCPipelineDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MemoryIdler.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MemoryIdler.h new file mode 120000 index 000000000..119aafe09 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/MemoryIdler.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/MemoryIdler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/PolyDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/PolyDetail.h new file mode 120000 index 000000000..c955a4f2e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/PolyDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/PolyDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeCommon.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeCommon.h new file mode 120000 index 000000000..1f78cb8b0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeCommon.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/RangeCommon.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeSse42.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeSse42.h new file mode 120000 index 000000000..4f65acbed --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/RangeSse42.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/RangeSse42.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Singleton.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Singleton.h new file mode 120000 index 000000000..043bee1e9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Singleton.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Singleton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SingletonStackTrace.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SingletonStackTrace.h new file mode 120000 index 000000000..08502fa09 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SingletonStackTrace.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SingletonStackTrace.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SlowFingerprint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SlowFingerprint.h new file mode 120000 index 000000000..aa9454f81 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SlowFingerprint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SlowFingerprint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SocketFastOpen.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SocketFastOpen.h new file mode 120000 index 000000000..b0b1288f0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/SocketFastOpen.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/SocketFastOpen.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Sse.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Sse.h new file mode 120000 index 000000000..00694df38 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/Sse.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/Sse.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/StaticSingletonManager.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/StaticSingletonManager.h new file mode 120000 index 000000000..21c322088 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/StaticSingletonManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/StaticSingletonManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/ThreadLocalDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/ThreadLocalDetail.h new file mode 120000 index 000000000..f7baceba6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/ThreadLocalDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/ThreadLocalDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TurnSequencer.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TurnSequencer.h new file mode 120000 index 000000000..607497385 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TurnSequencer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/TurnSequencer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TypeList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TypeList.h new file mode 120000 index 000000000..f2b82b6b8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/TypeList.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/TypeList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/UniqueInstance.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/UniqueInstance.h new file mode 120000 index 000000000..3a9c94d24 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/detail/UniqueInstance.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/detail/UniqueInstance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic-inl.h new file mode 120000 index 000000000..bfa10a1af --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic-inl.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/dynamic-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic.h new file mode 120000 index 000000000..57208baf1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/dynamic.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/dynamic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Async.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Async.h new file mode 120000 index 000000000..fa81a1a7a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Async.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/Async.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h new file mode 120000 index 000000000..c1c042619 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/CPUThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Codel.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Codel.h new file mode 120000 index 000000000..175f6b090 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/Codel.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/Codel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/DrivableExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/DrivableExecutor.h new file mode 120000 index 000000000..f6d28ff5d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/DrivableExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/DrivableExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h new file mode 120000 index 000000000..a1e708c2c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/EDFThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h new file mode 120000 index 000000000..f4d02c126 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ExecutorWithPriority-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority.h new file mode 120000 index 000000000..2c825b516 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ExecutorWithPriority.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ExecutorWithPriority.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FiberIOExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FiberIOExecutor.h new file mode 120000 index 000000000..747ccb890 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FiberIOExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/FiberIOExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FutureExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FutureExecutor.h new file mode 120000 index 000000000..3499c732a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/FutureExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/FutureExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalExecutor.h new file mode 120000 index 000000000..b04fcb830 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/GlobalExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalThreadPoolList.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalThreadPoolList.h new file mode 120000 index 000000000..4a590a461 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/GlobalThreadPoolList.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/GlobalThreadPoolList.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOExecutor.h new file mode 120000 index 000000000..cefed6342 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOObjectCache.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOObjectCache.h new file mode 120000 index 000000000..dcfa4098b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOObjectCache.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOObjectCache.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h new file mode 120000 index 000000000..67e716f1a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/IOThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/IOThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/InlineExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/InlineExecutor.h new file mode 120000 index 000000000..99320e836 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/InlineExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/InlineExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ManualExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ManualExecutor.h new file mode 120000 index 000000000..bd6e0b980 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ManualExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ManualExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h new file mode 120000 index 000000000..01bc0e29d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/QueuedImmediateExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/QueuedImmediateExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ScheduledExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ScheduledExecutor.h new file mode 120000 index 000000000..a2b442343 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ScheduledExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ScheduledExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SequencedExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SequencedExecutor.h new file mode 120000 index 000000000..9c0eb9b44 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SequencedExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SequencedExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SerialExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SerialExecutor.h new file mode 120000 index 000000000..917e441ac --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SerialExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SerialExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h new file mode 120000 index 000000000..d7ca17d26 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/SoftRealTimeExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/SoftRealTimeExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadPoolExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadPoolExecutor.h new file mode 120000 index 000000000..fe6b8f044 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadedExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadedExecutor.h new file mode 120000 index 000000000..40c41d7d0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/ThreadedExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/ThreadedExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimedDrivableExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimedDrivableExecutor.h new file mode 120000 index 000000000..cb9290df9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimedDrivableExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/TimedDrivableExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h new file mode 120000 index 000000000..7d87a4b84 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/executors/TimekeeperScheduledExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h new file mode 120000 index 000000000..350ad9447 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/BlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/BlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h new file mode 120000 index 000000000..28c628da4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/LifoSemMPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h new file mode 120000 index 000000000..406aafdac --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h new file mode 120000 index 000000000..95933f248 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h new file mode 120000 index 000000000..af062db5f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/task_queue/UnboundedBlockingQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h new file mode 120000 index 000000000..15316c9ec --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/InitThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h new file mode 120000 index 000000000..a546b458a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/NamedThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h new file mode 120000 index 000000000..be8064864 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/PriorityThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h new file mode 120000 index 000000000..439a4e746 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/executors/thread_factory/ThreadFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h new file mode 120000 index 000000000..c65968565 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/AtomicReadMostlyMainPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AutoTimer.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AutoTimer.h new file mode 120000 index 000000000..0b18b552f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/AutoTimer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/AutoTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/BitVectorCoding.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/BitVectorCoding.h new file mode 120000 index 000000000..39f266ab0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/BitVectorCoding.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/BitVectorCoding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Bits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Bits.h new file mode 120000 index 000000000..08f0c3cf3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Bits.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/CodingDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/CodingDetail.h new file mode 120000 index 000000000..c083a3095 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/CodingDetail.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/CodingDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser-inl.h new file mode 120000 index 000000000..e5fae0e98 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/DynamicParser-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser.h new file mode 120000 index 000000000..ca3a70a45 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/DynamicParser.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/DynamicParser.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EliasFanoCoding.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EliasFanoCoding.h new file mode 120000 index 000000000..98d5f261f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EliasFanoCoding.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EliasFanoCoding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EnvUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EnvUtil.h new file mode 120000 index 000000000..9d180d8f7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EnvUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EnvUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EventCount.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EventCount.h new file mode 120000 index 000000000..3a4c39cc2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/EventCount.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/EventCount.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ExecutionObserver.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ExecutionObserver.h new file mode 120000 index 000000000..59fc754fc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ExecutionObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ExecutionObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h new file mode 120000 index 000000000..72cc7456d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FlatCombiningPriorityQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FunctionScheduler.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FunctionScheduler.h new file mode 120000 index 000000000..35974cd21 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FunctionScheduler.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FunctionScheduler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FutureDAG.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FutureDAG.h new file mode 120000 index 000000000..d7c1cc4eb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/FutureDAG.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/FutureDAG.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Instructions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Instructions.h new file mode 120000 index 000000000..982a09b6f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Instructions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Instructions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JSONSchema.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JSONSchema.h new file mode 120000 index 000000000..728a215db --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JSONSchema.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JSONSchema.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h new file mode 120000 index 000000000..e73efefaa --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JemallocHugePageAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h new file mode 120000 index 000000000..dea7d214e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/JemallocNodumpAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h new file mode 120000 index 000000000..2507702a4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/LockFreeRingBuffer.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/LockFreeRingBuffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/MasterPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/MasterPtr.h new file mode 120000 index 000000000..5615efe49 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/MasterPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/MasterPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/NestedCommandLineApp.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/NestedCommandLineApp.h new file mode 120000 index 000000000..e741e726e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/NestedCommandLineApp.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/NestedCommandLineApp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ProgramOptions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ProgramOptions.h new file mode 120000 index 000000000..35803d9af --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ProgramOptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ProgramOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h new file mode 120000 index 000000000..988d2f574 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/QuotientMultiSet-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet.h new file mode 120000 index 000000000..40cc2e33e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/QuotientMultiSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/QuotientMultiSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h new file mode 120000 index 000000000..c6131c105 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ReadMostlySharedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h new file mode 120000 index 000000000..3dd31c14c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/RelaxedConcurrentPriorityQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h new file mode 120000 index 000000000..e440da868 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/STTimerFDTimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Select64.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Select64.h new file mode 120000 index 000000000..071f13260 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/Select64.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/Select64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h new file mode 120000 index 000000000..b0cbb0a61 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/SingleWriterFixedHashMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h new file mode 120000 index 000000000..918b0189c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/SingletonRelaxedCounter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StampedPtr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StampedPtr.h new file mode 120000 index 000000000..4f26102de --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StampedPtr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StampedPtr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedCommon.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedCommon.h new file mode 120000 index 000000000..bd030e00c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedCommon.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedCommon.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedMap.h new file mode 120000 index 000000000..42fcf0364 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedSet.h new file mode 120000 index 000000000..c5ed6f064 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h new file mode 120000 index 000000000..dd94e73cc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedUnorderedMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h new file mode 120000 index 000000000..831fb5d6c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/StringKeyedUnorderedSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TLRefCount.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TLRefCount.h new file mode 120000 index 000000000..da5c820aa --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TLRefCount.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TLRefCount.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TestUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TestUtil.h new file mode 120000 index 000000000..b6584d17e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TestUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TestUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h new file mode 120000 index 000000000..c6965151d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ThreadWheelTimekeeperHighRes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h new file mode 120000 index 000000000..f223bd7f5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/ThreadedRepeatingFunctionRunner.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFD.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFD.h new file mode 120000 index 000000000..40e0435a5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFD.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TimerFD.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h new file mode 120000 index 000000000..d2fee4f6a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TimerFDTimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TupleOps.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TupleOps.h new file mode 120000 index 000000000..bca1e87ae --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/experimental/TupleOps.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/experimental/TupleOps.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/ApplyTuple.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/ApplyTuple.h new file mode 120000 index 000000000..a6b82813a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/ApplyTuple.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/ApplyTuple.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Invoke.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Invoke.h new file mode 120000 index 000000000..85d8f478f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Invoke.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/Invoke.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Partial.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Partial.h new file mode 120000 index 000000000..5ad96ce1f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/functional/Partial.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/functional/Partial.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Barrier.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Barrier.h new file mode 120000 index 000000000..c9ec9766e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Barrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Barrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-inl.h new file mode 120000 index 000000000..0d63de40a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-pre.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-pre.h new file mode 120000 index 000000000..528bba4ff --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future-pre.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future-pre.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future.h new file mode 120000 index 000000000..41b1ec54e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Future.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Future.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/FutureSplitter.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/FutureSplitter.h new file mode 120000 index 000000000..a301e43d3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/FutureSplitter.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/FutureSplitter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ManualTimekeeper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ManualTimekeeper.h new file mode 120000 index 000000000..adf22efe6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ManualTimekeeper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/ManualTimekeeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Portability.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Portability.h new file mode 120000 index 000000000..656f41316 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Portability.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Portability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise-inl.h new file mode 120000 index 000000000..92813dde7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Promise-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise.h new file mode 120000 index 000000000..34e7c3eaa --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Promise.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Promise.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Retrying.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Retrying.h new file mode 120000 index 000000000..f6348affb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/Retrying.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/Retrying.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise-inl.h new file mode 120000 index 000000000..e8b93a51b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/SharedPromise-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise.h new file mode 120000 index 000000000..809bfde25 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/SharedPromise.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/SharedPromise.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h new file mode 120000 index 000000000..6701673e7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/ThreadWheelTimekeeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/WTCallback.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/WTCallback.h new file mode 120000 index 000000000..1b6031c5d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/WTCallback.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/futures/WTCallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Core.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Core.h new file mode 120000 index 000000000..64926e693 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Core.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/futures/detail/Core.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Types.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Types.h new file mode 120000 index 000000000..3a8b41b45 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/futures/detail/Types.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/futures/detail/Types.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base-inl.h new file mode 120000 index 000000000..bd2e9dd20 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Base-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base.h new file mode 120000 index 000000000..35629e04b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Base.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Base.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine-inl.h new file mode 120000 index 000000000..70cec4176 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Combine-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine.h new file mode 120000 index 000000000..b59aa1b15 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Combine.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Combine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core-inl.h new file mode 120000 index 000000000..c0cec2d3b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Core-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core.h new file mode 120000 index 000000000..4f3feb5bb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Core.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Core.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File-inl.h new file mode 120000 index 000000000..7c8ce0209 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/File-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File.h new file mode 120000 index 000000000..79c251ed5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/File.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/File.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/IStream.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/IStream.h new file mode 120000 index 000000000..96dfefb43 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/IStream.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/IStream.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel-inl.h new file mode 120000 index 000000000..40b3bb6a0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Parallel-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel.h new file mode 120000 index 000000000..0a4d29269 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/Parallel.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/Parallel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap-inl.h new file mode 120000 index 000000000..c6820e59b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/ParallelMap-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap.h new file mode 120000 index 000000000..16636ab8a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/ParallelMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/ParallelMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String-inl.h new file mode 120000 index 000000000..9dea9f6a0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/String-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String.h new file mode 120000 index 000000000..4b03250fe --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/gen/String.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/gen/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Checksum.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Checksum.h new file mode 120000 index 000000000..772db087b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Checksum.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/Checksum.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/FarmHash.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/FarmHash.h new file mode 120000 index 000000000..afc7adc84 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/FarmHash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/FarmHash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Hash.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Hash.h new file mode 120000 index 000000000..a700b8cee --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/Hash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/Hash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV1.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV1.h new file mode 120000 index 000000000..396875c0a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV1.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/SpookyHashV1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV2.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV2.h new file mode 120000 index 000000000..60aaf2120 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/SpookyHashV2.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/hash/SpookyHashV2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/detail/ChecksumDetail.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/detail/ChecksumDetail.h new file mode 120000 index 000000000..dc2ecacbd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/hash/detail/ChecksumDetail.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/hash/detail/ChecksumDetail.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Init.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Init.h new file mode 120000 index 000000000..3401526f4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Init.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/init/Init.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Phase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Phase.h new file mode 120000 index 000000000..9c09d17d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/init/Phase.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/init/Phase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor-inl.h new file mode 120000 index 000000000..2f7830c54 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/Cursor-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor.h new file mode 120000 index 000000000..386f2af26 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/Cursor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/Cursor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h new file mode 120000 index 000000000..200bbca90 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/GlobalShutdownSocketSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/GlobalShutdownSocketSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBuf.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBuf.h new file mode 120000 index 000000000..577c3d93a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBuf.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/IOBuf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBufQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBufQueue.h new file mode 120000 index 000000000..0f4a52351 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/IOBufQueue.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/IOBufQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO-inl.h new file mode 120000 index 000000000..093f30def --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/RecordIO-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO.h new file mode 120000 index 000000000..d2cf8f1ba --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/RecordIO.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/RecordIO.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/ShutdownSocketSet.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/ShutdownSocketSet.h new file mode 120000 index 000000000..11b990f92 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/ShutdownSocketSet.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/ShutdownSocketSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/SocketOptionMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/SocketOptionMap.h new file mode 120000 index 000000000..4ad54da56 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/SocketOptionMap.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/SocketOptionMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/TypedIOBuf.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/TypedIOBuf.h new file mode 120000 index 000000000..376af4a8e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/TypedIOBuf.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/io/TypedIOBuf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncPipe.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncPipe.h new file mode 120000 index 000000000..1d89e1377 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncPipe.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncPipe.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSSLSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSSLSocket.h new file mode 120000 index 000000000..6bd7d0e46 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSSLSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSSLSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncServerSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncServerSocket.h new file mode 120000 index 000000000..3c0a257a7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncServerSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncServerSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSignalHandler.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSignalHandler.h new file mode 120000 index 000000000..084536a07 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSignalHandler.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSignalHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocket.h new file mode 120000 index 000000000..a7f05a337 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketBase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketBase.h new file mode 120000 index 000000000..1a421b734 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocketBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketException.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketException.h new file mode 120000 index 000000000..3a6938497 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncSocketException.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncSocketException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTimeout.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTimeout.h new file mode 120000 index 000000000..29e15011a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTimeout.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTimeout.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransport.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransport.h new file mode 120000 index 000000000..04a53743e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransport.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h new file mode 120000 index 000000000..3b27a2c1b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncTransportCertificate.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncTransportCertificate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h new file mode 120000 index 000000000..53af72c8d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncUDPServerSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPSocket.h new file mode 120000 index 000000000..a8adccc7b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/AsyncUDPSocket.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/AsyncUDPSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h new file mode 120000 index 000000000..c2546b707 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DecoratedAsyncTransportWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestruction.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestruction.h new file mode 120000 index 000000000..fcc9de6d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestruction.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DelayedDestruction.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestructionBase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestructionBase.h new file mode 120000 index 000000000..4712379ce --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DelayedDestructionBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DelayedDestructionBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DestructorCheck.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DestructorCheck.h new file mode 120000 index 000000000..f9df0eb31 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/DestructorCheck.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/DestructorCheck.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBase.h new file mode 120000 index 000000000..3e08454fd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseBackendBase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseBackendBase.h new file mode 120000 index 000000000..b80949fc4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseBackendBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseBackendBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseLocal.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseLocal.h new file mode 120000 index 000000000..e1f7d0bfd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseLocal.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseManager.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseManager.h new file mode 120000 index 000000000..680cb6aa0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseManager.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseThread.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseThread.h new file mode 120000 index 000000000..08a80d11b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventBaseThread.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventBaseThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventFDWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventFDWrapper.h new file mode 120000 index 000000000..ae722f8e4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventFDWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventFDWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventHandler.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventHandler.h new file mode 120000 index 000000000..b3f7c0e43 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventHandler.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventUtil.h new file mode 120000 index 000000000..9ae48e83e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/EventUtil.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/EventUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h new file mode 120000 index 000000000..4a3c7d514 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/HHWheelTimer-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer.h new file mode 120000 index 000000000..a8d0a066a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/HHWheelTimer.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/HHWheelTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/NotificationQueue.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/NotificationQueue.h new file mode 120000 index 000000000..95d3bc70d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/NotificationQueue.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/NotificationQueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/PasswordInFile.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/PasswordInFile.h new file mode 120000 index 000000000..d6e8031a5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/PasswordInFile.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/PasswordInFile.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/Request.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/Request.h new file mode 120000 index 000000000..eb81d77e6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/Request.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/Request.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLContext.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLContext.h new file mode 120000 index 000000000..c7b00d85b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLContext.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/SSLContext.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLOptions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLOptions.h new file mode 120000 index 000000000..e6e762013 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/SSLOptions.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/SSLOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h new file mode 120000 index 000000000..e50d592c8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ScopedEventBaseThread.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/ScopedEventBaseThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/TimeoutManager.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/TimeoutManager.h new file mode 120000 index 000000000..9cd65aee7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/TimeoutManager.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/TimeoutManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/VirtualEventBase.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/VirtualEventBase.h new file mode 120000 index 000000000..261bd23fb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/VirtualEventBase.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/VirtualEventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h new file mode 120000 index 000000000..672b7bd0f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/io/async/WriteChainAsyncTransportWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h new file mode 120000 index 000000000..47b083053 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/BasicTransportCertificate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h new file mode 120000 index 000000000..d40d967ab --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/SSLErrors.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/SSLErrors.h new file mode 120000 index 000000000..ba22d0266 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/SSLErrors.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/SSLErrors.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h new file mode 120000 index 000000000..bba637a09 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h @@ -0,0 +1 @@ +../../../../../../../Flipper-Folly/folly/io/async/ssl/TLSDefinitions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/json.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/json.h new file mode 120000 index 000000000..9a2868668 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/json.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/json_patch.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/json_patch.h new file mode 120000 index 000000000..7f2f82436 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/json_patch.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json_patch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/json_pointer.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/json_pointer.h new file mode 120000 index 000000000..731203398 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/json_pointer.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/json_pointer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Align.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Align.h new file mode 120000 index 000000000..53e4b575e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Align.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Align.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Aligned.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Aligned.h new file mode 120000 index 000000000..077c72651 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Aligned.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Aligned.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume-inl.h new file mode 120000 index 000000000..4bd3795d0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Assume-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume.h new file mode 120000 index 000000000..8a6e8fe25 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Assume.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Assume.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Bits.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Bits.h new file mode 120000 index 000000000..9d7b9bebe --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Bits.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Bits.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CString.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CString.h new file mode 120000 index 000000000..091a5272a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CString.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CString.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Cast.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Cast.h new file mode 120000 index 000000000..df4233d43 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Cast.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Cast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CheckedMath.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CheckedMath.h new file mode 120000 index 000000000..cf7a342a9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CheckedMath.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CheckedMath.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CustomizationPoint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CustomizationPoint.h new file mode 120000 index 000000000..6e91271e4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/CustomizationPoint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/CustomizationPoint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Exception.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Exception.h new file mode 120000 index 000000000..472026d87 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Exception.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Exception.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Launder.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Launder.h new file mode 120000 index 000000000..47710cfa1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Launder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Launder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Ordering.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Ordering.h new file mode 120000 index 000000000..7d6e63f6d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Ordering.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Ordering.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Pretty.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Pretty.h new file mode 120000 index 000000000..729dae01a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/Pretty.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/Pretty.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/PropagateConst.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/PropagateConst.h new file mode 120000 index 000000000..aadb9984a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/PropagateConst.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/PropagateConst.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/RValueReferenceWrapper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/RValueReferenceWrapper.h new file mode 120000 index 000000000..f17dcb20b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/RValueReferenceWrapper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/RValueReferenceWrapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/SafeAssert.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/SafeAssert.h new file mode 120000 index 000000000..603c325d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/SafeAssert.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/SafeAssert.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/StaticConst.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/StaticConst.h new file mode 120000 index 000000000..c27a60eb7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/StaticConst.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/StaticConst.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/TypeInfo.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/TypeInfo.h new file mode 120000 index 000000000..e629c053c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/TypeInfo.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/TypeInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/UncaughtExceptions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/UncaughtExceptions.h new file mode 120000 index 000000000..57c89967f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/lang/UncaughtExceptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/lang/UncaughtExceptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena-inl.h new file mode 120000 index 000000000..abf127d8c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Arena-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena.h new file mode 120000 index 000000000..dabde886b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Arena.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Arena.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/EnableSharedFromThis.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/EnableSharedFromThis.h new file mode 120000 index 000000000..30d8334d3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/EnableSharedFromThis.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/EnableSharedFromThis.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MallctlHelper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MallctlHelper.h new file mode 120000 index 000000000..0ca5d27f6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MallctlHelper.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/MallctlHelper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Malloc.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Malloc.h new file mode 120000 index 000000000..3c57e6dc8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/Malloc.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/Malloc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MemoryResource.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MemoryResource.h new file mode 120000 index 000000000..c26326ad8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/MemoryResource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/MemoryResource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ReentrantAllocator.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ReentrantAllocator.h new file mode 120000 index 000000000..3e18c786e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ReentrantAllocator.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/ReentrantAllocator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/SanitizeLeak.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/SanitizeLeak.h new file mode 120000 index 000000000..90f0135f1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/SanitizeLeak.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/SanitizeLeak.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ThreadCachedArena.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ThreadCachedArena.h new file mode 120000 index 000000000..2b1390045 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/ThreadCachedArena.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/ThreadCachedArena.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h new file mode 120000 index 000000000..be5534bad --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/UninitializedMemoryHacks.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/memory/UninitializedMemoryHacks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/detail/MallocImpl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/detail/MallocImpl.h new file mode 120000 index 000000000..0d0a717b9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/memory/detail/MallocImpl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/memory/detail/MallocImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetOps.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetOps.h new file mode 120000 index 000000000..833d8ace0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetOps.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/net/NetOps.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetworkSocket.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetworkSocket.h new file mode 120000 index 000000000..64ce96ef3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/NetworkSocket.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/net/NetworkSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h new file mode 120000 index 000000000..105dd9b4e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/net/detail/SocketFileDescriptorMap.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Asm.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Asm.h new file mode 120000 index 000000000..391894ab2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Asm.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Asm.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Atomic.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Atomic.h new file mode 120000 index 000000000..010a1c3fb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Atomic.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Atomic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Builtins.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Builtins.h new file mode 120000 index 000000000..bc4a8f945 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Builtins.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Builtins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Config.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Config.h new file mode 120000 index 000000000..48a4bf306 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Config.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Config.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Constexpr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Constexpr.h new file mode 120000 index 000000000..c03deb3b9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Constexpr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Constexpr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Dirent.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Dirent.h new file mode 120000 index 000000000..a1fbeb15d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Dirent.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Dirent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Event.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Event.h new file mode 120000 index 000000000..af978b7e4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Event.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Event.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Fcntl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Fcntl.h new file mode 120000 index 000000000..56d18421f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Fcntl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Fcntl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GFlags.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GFlags.h new file mode 120000 index 000000000..5687c8a43 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GFlags.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GFlags.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GMock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GMock.h new file mode 120000 index 000000000..84a31aaf2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GMock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GMock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GTest.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GTest.h new file mode 120000 index 000000000..36c9dab31 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/GTest.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/GTest.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/IOVec.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/IOVec.h new file mode 120000 index 000000000..e5e91b5d4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/IOVec.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/IOVec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Libgen.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Libgen.h new file mode 120000 index 000000000..f670a5102 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Libgen.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Libgen.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Malloc.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Malloc.h new file mode 120000 index 000000000..79bd0d37f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Malloc.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Malloc.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Math.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Math.h new file mode 120000 index 000000000..d85034c86 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Math.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Math.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Memory.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Memory.h new file mode 120000 index 000000000..25a594989 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Memory.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Memory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/OpenSSL.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/OpenSSL.h new file mode 120000 index 000000000..2bb054795 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/OpenSSL.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/OpenSSL.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/PThread.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/PThread.h new file mode 120000 index 000000000..d75eedb02 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/PThread.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/PThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sched.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sched.h new file mode 120000 index 000000000..902bfe780 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sched.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Sched.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Semaphore.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Semaphore.h new file mode 120000 index 000000000..5879b4095 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Semaphore.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Semaphore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sockets.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sockets.h new file mode 120000 index 000000000..7bfde99c1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Sockets.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Sockets.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdio.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdio.h new file mode 120000 index 000000000..f6eb9fc4f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdio.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Stdio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdlib.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdlib.h new file mode 120000 index 000000000..e3b0a0082 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Stdlib.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Stdlib.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/String.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/String.h new file mode 120000 index 000000000..881d0acaf --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/String.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/String.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysFile.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysFile.h new file mode 120000 index 000000000..cd65ea83b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysFile.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysFile.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMembarrier.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMembarrier.h new file mode 120000 index 000000000..648df3a2b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMembarrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysMembarrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMman.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMman.h new file mode 120000 index 000000000..fd557d483 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysMman.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysMman.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysResource.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysResource.h new file mode 120000 index 000000000..e7dafa71d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysResource.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysResource.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysStat.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysStat.h new file mode 120000 index 000000000..0beb62b93 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysStat.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysStat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysSyscall.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysSyscall.h new file mode 120000 index 000000000..25e5d7a3e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysSyscall.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysSyscall.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTime.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTime.h new file mode 120000 index 000000000..f42436d9a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTime.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysTime.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTypes.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTypes.h new file mode 120000 index 000000000..47f56217d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysUio.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysUio.h new file mode 120000 index 000000000..6ab5f8f2c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/SysUio.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/SysUio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Syslog.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Syslog.h new file mode 120000 index 000000000..b0755faf6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Syslog.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Syslog.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Time.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Time.h new file mode 120000 index 000000000..59ffa922a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Time.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Time.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Unistd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Unistd.h new file mode 120000 index 000000000..49ed325f9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Unistd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Unistd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Windows.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Windows.h new file mode 120000 index 000000000..a202022da --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/portability/Windows.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/portability/Windows.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/small_vector.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/small_vector.h new file mode 120000 index 000000000..92aae47bd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/small_vector.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/small_vector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/sorted_vector_types.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/sorted_vector_types.h new file mode 120000 index 000000000..8cb88a991 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/sorted_vector_types.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/sorted_vector_types.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/Init.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/Init.h new file mode 120000 index 000000000..36228fa06 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/Init.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/Init.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h new file mode 120000 index 000000000..508824e10 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLCertUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLCertUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLHash.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLHash.h new file mode 120000 index 000000000..afa8f8d5f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLHash.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLHash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h new file mode 120000 index 000000000..2721bdbf5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLLockTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLLockTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h new file mode 120000 index 000000000..c9a6663f7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLPtrTypes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h new file mode 120000 index 000000000..bff5632f6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/OpenSSLVersionFinder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/SSLSession.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/SSLSession.h new file mode 120000 index 000000000..38910044c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/SSLSession.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/ssl/SSLSession.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h new file mode 120000 index 000000000..f5b171995 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/ssl/detail/OpenSSLThreading.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h new file mode 120000 index 000000000..d77a751b4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/ssl/detail/SSLSessionImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/stop_watch.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/stop_watch.h new file mode 120000 index 000000000..682e6b8ac --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/stop_watch.h @@ -0,0 +1 @@ +../../../../Flipper-Folly/folly/stop_watch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h new file mode 120000 index 000000000..cbff7905d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AsymmetricMemoryBarrier.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h new file mode 120000 index 000000000..561ca9418 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicNotification-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification.h new file mode 120000 index 000000000..0d892928c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicNotification.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicRef.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicRef.h new file mode 120000 index 000000000..4a0220a66 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicRef.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicRef.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicStruct.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicStruct.h new file mode 120000 index 000000000..ddf96fce9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicStruct.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicStruct.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h new file mode 120000 index 000000000..5b81da490 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicUtil-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil.h new file mode 120000 index 000000000..98110d18b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/AtomicUtil.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/AtomicUtil.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Baton.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Baton.h new file mode 120000 index 000000000..b2d41f9e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Baton.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Baton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/CallOnce.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/CallOnce.h new file mode 120000 index 000000000..d3cbcfde3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/CallOnce.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/CallOnce.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h new file mode 120000 index 000000000..5f76b1d6c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutex-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex.h new file mode 120000 index 000000000..d41bf0b15 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutex.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutex.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h new file mode 120000 index 000000000..de880064a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/DistributedMutexSpecializations.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr-fwd.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr-fwd.h new file mode 120000 index 000000000..129e7b0f8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr-fwd.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Hazptr-fwd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr.h new file mode 120000 index 000000000..165c3d155 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Hazptr.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Hazptr.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrDomain.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrDomain.h new file mode 120000 index 000000000..e99412c43 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrDomain.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrDomain.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrHolder.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrHolder.h new file mode 120000 index 000000000..93a9a3fd6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrHolder.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrHolder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObj.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObj.h new file mode 120000 index 000000000..e63361673 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObj.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrObj.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObjLinked.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObjLinked.h new file mode 120000 index 000000000..0f8edc854 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrObjLinked.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrObjLinked.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrRec.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrRec.h new file mode 120000 index 000000000..40c83412c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrRec.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrRec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThrLocal.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThrLocal.h new file mode 120000 index 000000000..7e4bb50ab --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThrLocal.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrThrLocal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h new file mode 120000 index 000000000..44f9d3734 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/HazptrThreadPoolExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/LifoSem.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/LifoSem.h new file mode 120000 index 000000000..012ab53e9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/LifoSem.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/LifoSem.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/MicroSpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/MicroSpinLock.h new file mode 120000 index 000000000..0b24a7c35 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/MicroSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/MicroSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/ParkingLot.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/ParkingLot.h new file mode 120000 index 000000000..8b3d666c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/ParkingLot.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/ParkingLot.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/PicoSpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/PicoSpinLock.h new file mode 120000 index 000000000..8ea55b6a1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/PicoSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/PicoSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/RWSpinLock.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/RWSpinLock.h new file mode 120000 index 000000000..56a1a5edb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/RWSpinLock.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/RWSpinLock.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Rcu-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Rcu-inl.h new file mode 120000 index 000000000..ecc8a6c8d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Rcu-inl.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Rcu-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SanitizeThread.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SanitizeThread.h new file mode 120000 index 000000000..84c2215e7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SanitizeThread.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SanitizeThread.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h new file mode 120000 index 000000000..8421d0a52 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SaturatingSemaphore.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SaturatingSemaphore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SmallLocks.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SmallLocks.h new file mode 120000 index 000000000..aa0c0537f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/SmallLocks.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/SmallLocks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Tearable.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Tearable.h new file mode 120000 index 000000000..390ebbe81 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Tearable.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Tearable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Utility.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Utility.h new file mode 120000 index 000000000..05f2c3051 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/Utility.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/Utility.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/WaitOptions.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/WaitOptions.h new file mode 120000 index 000000000..f26bb0f46 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/WaitOptions.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/synchronization/WaitOptions.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h new file mode 120000 index 000000000..f76a38b37 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/AtomicUtils.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/AtomicUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Hardware.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Hardware.h new file mode 120000 index 000000000..a0021765c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Hardware.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Hardware.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h new file mode 120000 index 000000000..7bf231e91 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/HazptrUtils.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/HazptrUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h new file mode 120000 index 000000000..04061c642 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/InlineFunctionRef.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h new file mode 120000 index 000000000..040825d87 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ProxyLockable-inl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h new file mode 120000 index 000000000..3781be5b7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ProxyLockable.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ProxyLockable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Sleeper.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Sleeper.h new file mode 120000 index 000000000..40e9658de --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Sleeper.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Sleeper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Spin.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Spin.h new file mode 120000 index 000000000..e672626fb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/Spin.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/Spin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h new file mode 120000 index 000000000..0a4e4de73 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ThreadCachedInts.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h new file mode 120000 index 000000000..ffe9fd11c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h @@ -0,0 +1 @@ +../../../../../../Flipper-Folly/folly/synchronization/detail/ThreadCachedLists.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/system/HardwareConcurrency.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/HardwareConcurrency.h new file mode 120000 index 000000000..0cb3b0d56 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/HardwareConcurrency.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/HardwareConcurrency.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/system/MemoryMapping.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/MemoryMapping.h new file mode 120000 index 000000000..6f642c4b1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/MemoryMapping.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/MemoryMapping.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/system/Shell.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/Shell.h new file mode 120000 index 000000000..ae6a254e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/Shell.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/Shell.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadId.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadId.h new file mode 120000 index 000000000..05fbfd780 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadId.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/ThreadId.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadName.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadName.h new file mode 120000 index 000000000..3641dfe5e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/system/ThreadName.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/system/ThreadName.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/ScopedTraceSection.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/ScopedTraceSection.h new file mode 120000 index 000000000..3f5662d34 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/ScopedTraceSection.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/ScopedTraceSection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h new file mode 120000 index 000000000..2bac71b9a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/StaticTracepoint-ELFx86.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint.h b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint.h new file mode 120000 index 000000000..8f5d47326 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Folly/folly/tracing/StaticTracepoint.h @@ -0,0 +1 @@ +../../../../../Flipper-Folly/folly/tracing/StaticTracepoint.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Glog/glog/log_severity.h b/ios/Pods/Headers/Public/Flipper-Glog/glog/log_severity.h new file mode 120000 index 000000000..574678ddc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Glog/glog/log_severity.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/log_severity.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Glog/glog/logging.h b/ios/Pods/Headers/Public/Flipper-Glog/glog/logging.h new file mode 120000 index 000000000..79490eb17 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Glog/glog/logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Glog/glog/raw_logging.h b/ios/Pods/Headers/Public/Flipper-Glog/glog/raw_logging.h new file mode 120000 index 000000000..ab65c43a5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Glog/glog/raw_logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/raw_logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Glog/glog/stl_logging.h b/ios/Pods/Headers/Public/Flipper-Glog/glog/stl_logging.h new file mode 120000 index 000000000..4bcf9c098 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Glog/glog/stl_logging.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/stl_logging.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-Glog/glog/vlog_is_on.h b/ios/Pods/Headers/Public/Flipper-Glog/glog/vlog_is_on.h new file mode 120000 index 000000000..c2c66094a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-Glog/glog/vlog_is_on.h @@ -0,0 +1 @@ +../../../../Flipper-Glog/src/glog/vlog_is_on.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTChannel.h b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTChannel.h new file mode 120000 index 000000000..0e6de7989 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTChannel.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTChannel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTPrivate.h b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTPrivate.h new file mode 120000 index 000000000..c192ec9c9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTPrivate.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTPrivate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTProtocol.h b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTProtocol.h new file mode 120000 index 000000000..56721bfb1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTProtocol.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTUSBHub.h b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTUSBHub.h new file mode 120000 index 000000000..695340776 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/PTUSBHub.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/PTUSBHub.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/Peertalk.h b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/Peertalk.h new file mode 120000 index 000000000..c50168656 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-PeerTalk/peertalk/Peertalk.h @@ -0,0 +1 @@ +../../../../Flipper-PeerTalk/peertalk/Peertalk.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ColdResumeHandler.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ColdResumeHandler.h new file mode 120000 index 000000000..09f8edcec --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ColdResumeHandler.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ColdResumeHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionAcceptor.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionAcceptor.h new file mode 120000 index 000000000..aeed980c4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionAcceptor.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ConnectionAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionFactory.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionFactory.h new file mode 120000 index 000000000..a8150eec0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ConnectionFactory.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ConnectionFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/DuplexConnection.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/DuplexConnection.h new file mode 120000 index 000000000..070c208ba --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/DuplexConnection.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/DuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/Payload.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/Payload.h new file mode 120000 index 000000000..c99772af2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/Payload.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/Payload.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocket.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocket.h new file mode 120000 index 000000000..88e7cfe7e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocket.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketClient.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketClient.h new file mode 120000 index 000000000..ba07d875c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketClient.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketConnectionEvents.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketConnectionEvents.h new file mode 120000 index 000000000..27cac8df5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketConnectionEvents.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketConnectionEvents.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketErrors.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketErrors.h new file mode 120000 index 000000000..d4fc8da03 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketErrors.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketErrors.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketException.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketException.h new file mode 120000 index 000000000..50e01404d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketException.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketException.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketParameters.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketParameters.h new file mode 120000 index 000000000..36051ec00 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketParameters.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketParameters.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketRequester.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketRequester.h new file mode 120000 index 000000000..4de6f033c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketRequester.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketResponder.h new file mode 120000 index 000000000..27e4ff8c3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketResponder.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServer.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServer.h new file mode 120000 index 000000000..b27c71985 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServer.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServerState.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServerState.h new file mode 120000 index 000000000..be8e42f71 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServerState.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServerState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServiceHandler.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServiceHandler.h new file mode 120000 index 000000000..01b4ab0c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketServiceHandler.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketServiceHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketStats.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketStats.h new file mode 120000 index 000000000..e7332ac03 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/RSocketStats.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/RSocketStats.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ResumeManager.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ResumeManager.h new file mode 120000 index 000000000..f2b3b900b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/ResumeManager.h @@ -0,0 +1 @@ +../../../../Flipper-RSocket/rsocket/ResumeManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Fixture.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Fixture.h new file mode 120000 index 000000000..ca13cff3b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Fixture.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Fixture.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Latch.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Latch.h new file mode 120000 index 000000000..24aa48acd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Latch.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Latch.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Throughput.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Throughput.h new file mode 120000 index 000000000..08e3d3ba5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/benchmarks/Throughput.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/benchmarks/Throughput.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ErrorCode.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ErrorCode.h new file mode 120000 index 000000000..ad4379654 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ErrorCode.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ErrorCode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Frame.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Frame.h new file mode 120000 index 000000000..e5bb9aa90 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Frame.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/Frame.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameFlags.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameFlags.h new file mode 120000 index 000000000..1f0097e94 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameFlags.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameFlags.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameHeader.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameHeader.h new file mode 120000 index 000000000..c9c4983c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameHeader.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameHeader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameProcessor.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameProcessor.h new file mode 120000 index 000000000..a13776ef8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer.h new file mode 120000 index 000000000..ac0c8d743 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h new file mode 120000 index 000000000..af2df7620 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameSerializer_v1_0.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransport.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransport.h new file mode 120000 index 000000000..ca63b163f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h new file mode 120000 index 000000000..6d45f3489 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameTransportImpl.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameTransportImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameType.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameType.h new file mode 120000 index 000000000..488816735 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FrameType.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FrameType.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h new file mode 120000 index 000000000..4d58eaafc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FramedDuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedReader.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedReader.h new file mode 120000 index 000000000..2eb3bcd47 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/FramedReader.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/FramedReader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Framer.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Framer.h new file mode 120000 index 000000000..f676b5b64 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/Framer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/Framer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ProtocolVersion.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ProtocolVersion.h new file mode 120000 index 000000000..1a42ae5c2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ProtocolVersion.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ProtocolVersion.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h new file mode 120000 index 000000000..9c7c36548 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ResumeIdentificationToken.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h new file mode 120000 index 000000000..2d9e0c5f4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ScheduledFrameProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h new file mode 120000 index 000000000..9761f5e5b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/framing/ScheduledFrameTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Allowance.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Allowance.h new file mode 120000 index 000000000..5eac4c019 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Allowance.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/Allowance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h new file mode 120000 index 000000000..1620e1e83 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ClientResumeStatusCallback.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Common.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Common.h new file mode 120000 index 000000000..93f1229c1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/Common.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/Common.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ConnectionSet.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ConnectionSet.h new file mode 120000 index 000000000..01ef666e5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ConnectionSet.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ConnectionSet.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h new file mode 120000 index 000000000..62bee3c38 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/KeepaliveTimer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/KeepaliveTimer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h new file mode 120000 index 000000000..702445954 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledRSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h new file mode 120000 index 000000000..982d15cca --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSingleObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h new file mode 120000 index 000000000..b82b406f9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSingleSubscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h new file mode 120000 index 000000000..cdfe1be27 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h new file mode 120000 index 000000000..cd677f0da --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/ScheduledSubscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/ScheduledSubscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h new file mode 120000 index 000000000..bb2f667d1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/SetupResumeAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/StackTraceUtils.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/StackTraceUtils.h new file mode 120000 index 000000000..4ca0717a7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/StackTraceUtils.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/StackTraceUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SwappableEventBase.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SwappableEventBase.h new file mode 120000 index 000000000..7254d4e7d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/SwappableEventBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/SwappableEventBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/WarmResumeManager.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/WarmResumeManager.h new file mode 120000 index 000000000..587b3034a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/internal/WarmResumeManager.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/internal/WarmResumeManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h new file mode 120000 index 000000000..ddb84d2af --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ChannelRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h new file mode 120000 index 000000000..2cd66c83d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ChannelResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ChannelResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h new file mode 120000 index 000000000..4d3d1dbb3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/ConsumerBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/ConsumerBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h new file mode 120000 index 000000000..eff049f8e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/FireAndForgetResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/PublisherBase.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/PublisherBase.h new file mode 120000 index 000000000..55faa44bb --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/PublisherBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/PublisherBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h new file mode 120000 index 000000000..ece2d0cc1 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RSocketStateMachine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h new file mode 120000 index 000000000..4dacdf9e4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RequestResponseRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h new file mode 120000 index 000000000..8ca4f8eaf --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/RequestResponseResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h new file mode 120000 index 000000000..c84f819b4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamFragmentAccumulator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamRequester.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamRequester.h new file mode 120000 index 000000000..121dd21ae --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamRequester.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamRequester.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamResponder.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamResponder.h new file mode 120000 index 000000000..aa7cc0c01 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamResponder.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h new file mode 120000 index 000000000..d6ca91e45 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamStateMachineBase.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h new file mode 120000 index 000000000..a90d0f7b4 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/statemachine/StreamsWriter.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/statemachine/StreamsWriter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/RSocketTransport.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/RSocketTransport.h new file mode 120000 index 000000000..638373dcc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/RSocketTransport.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/rsocket/transports/RSocketTransport.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h new file mode 120000 index 000000000..6a9036d3e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpConnectionAcceptor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h new file mode 120000 index 000000000..1933fe9ed --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpConnectionFactory.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h new file mode 120000 index 000000000..4124b6664 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h @@ -0,0 +1 @@ +../../../../../../Flipper-RSocket/rsocket/transports/tcp/TcpDuplexConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h new file mode 120000 index 000000000..e0556193c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/AsyncGeneratorShim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h new file mode 120000 index 000000000..82a0d2448 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/CancelingSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/DeferFlowable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/DeferFlowable.h new file mode 120000 index 000000000..711b90a6b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/DeferFlowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/DeferFlowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h new file mode 120000 index 000000000..6b97631cc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/EmitterFlowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/EmitterFlowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable.h new file mode 120000 index 000000000..97e649dc3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h new file mode 120000 index 000000000..dc60f88b9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableConcatOperators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h new file mode 120000 index 000000000..b53364dd7 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableDoOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h new file mode 120000 index 000000000..b46328eba --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableObserveOnOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableOperator.h new file mode 120000 index 000000000..a776e4885 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h new file mode 120000 index 000000000..9320b08b9 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/FlowableTimeoutOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h new file mode 120000 index 000000000..76fb7eac5 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowable_FromObservable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowables.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowables.h new file mode 120000 index 000000000..803b0ecb6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Flowables.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Flowables.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/PublishProcessor.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/PublishProcessor.h new file mode 120000 index 000000000..fada9dda6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/PublishProcessor.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/PublishProcessor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscriber.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscriber.h new file mode 120000 index 000000000..e1abd24a3 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Subscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscription.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscription.h new file mode 120000 index 000000000..d5a1df665 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/Subscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/Subscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/TestSubscriber.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/TestSubscriber.h new file mode 120000 index 000000000..63a7b2cc2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/TestSubscriber.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/TestSubscriber.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h new file mode 120000 index 000000000..a981c71c6 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/flowable/ThriftStreamShim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/DeferObservable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/DeferObservable.h new file mode 120000 index 000000000..fb1eba682 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/DeferObservable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/DeferObservable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observable.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observable.h new file mode 120000 index 000000000..a681eaca8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observable.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h new file mode 120000 index 000000000..9eb2e09dc --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableConcatOperators.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h new file mode 120000 index 000000000..d77604a4d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableDoOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableDoOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableOperator.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableOperator.h new file mode 120000 index 000000000..6ab8464c2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/ObservableOperator.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/ObservableOperator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observables.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observables.h new file mode 120000 index 000000000..0d86053e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observables.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observables.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observer.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observer.h new file mode 120000 index 000000000..89f3bd4dd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Observer.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Observer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Subscription.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Subscription.h new file mode 120000 index 000000000..53dfe923b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/Subscription.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/Subscription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/TestObserver.h b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/TestObserver.h new file mode 120000 index 000000000..6fbdd4400 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper-RSocket/yarpl/observable/TestObserver.h @@ -0,0 +1 @@ +../../../../../Flipper-RSocket/yarpl/observable/TestObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/CertificateUtils.h b/ios/Pods/Headers/Public/Flipper/CertificateUtils.h new file mode 120000 index 000000000..9ffc9c15e --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/CertificateUtils.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/CertificateUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/ConnectionContextStore.h b/ios/Pods/Headers/Public/Flipper/ConnectionContextStore.h new file mode 120000 index 000000000..4d1e2f56f --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/ConnectionContextStore.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/ConnectionContextStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FireAndForgetBasedFlipperResponder.h b/ios/Pods/Headers/Public/Flipper/FireAndForgetBasedFlipperResponder.h new file mode 120000 index 000000000..81f738362 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FireAndForgetBasedFlipperResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FireAndForgetBasedFlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperClient.h b/ios/Pods/Headers/Public/Flipper/FlipperClient.h new file mode 120000 index 000000000..4c8f6785a --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperClient.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperConnection.h b/ios/Pods/Headers/Public/Flipper/FlipperConnection.h new file mode 120000 index 000000000..32a2f084c --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperConnection.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperConnectionImpl.h b/ios/Pods/Headers/Public/Flipper/FlipperConnectionImpl.h new file mode 120000 index 000000000..190902d47 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperConnectionImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperConnectionManager.h b/ios/Pods/Headers/Public/Flipper/FlipperConnectionManager.h new file mode 120000 index 000000000..4aa8c7128 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperConnectionManager.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperConnectionManagerImpl.h b/ios/Pods/Headers/Public/Flipper/FlipperConnectionManagerImpl.h new file mode 120000 index 000000000..7007bf790 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperConnectionManagerImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperInitConfig.h b/ios/Pods/Headers/Public/Flipper/FlipperInitConfig.h new file mode 120000 index 000000000..9d9df0599 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperInitConfig.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperInitConfig.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperPlugin.h b/ios/Pods/Headers/Public/Flipper/FlipperPlugin.h new file mode 120000 index 000000000..9acf19344 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperPlugin.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperRSocketResponder.h b/ios/Pods/Headers/Public/Flipper/FlipperRSocketResponder.h new file mode 120000 index 000000000..44b7f360b --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperRSocketResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperRSocketResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperResponder.h b/ios/Pods/Headers/Public/Flipper/FlipperResponder.h new file mode 120000 index 000000000..0d96a3850 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperResponder.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperResponderImpl.h b/ios/Pods/Headers/Public/Flipper/FlipperResponderImpl.h new file mode 120000 index 000000000..e3e9fbdce --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperResponderImpl.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperResponderImpl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperState.h b/ios/Pods/Headers/Public/Flipper/FlipperState.h new file mode 120000 index 000000000..14ade198d --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperState.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperStateUpdateListener.h b/ios/Pods/Headers/Public/Flipper/FlipperStateUpdateListener.h new file mode 120000 index 000000000..1158774e2 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperStateUpdateListener.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperStateUpdateListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/FlipperStep.h b/ios/Pods/Headers/Public/Flipper/FlipperStep.h new file mode 120000 index 000000000..1a3e59fbd --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/FlipperStep.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/FlipperStep.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Flipper/Log.h b/ios/Pods/Headers/Public/Flipper/Log.h new file mode 120000 index 000000000..308b97af0 --- /dev/null +++ b/ios/Pods/Headers/Public/Flipper/Log.h @@ -0,0 +1 @@ +../../../Flipper/xplat/Flipper/Log.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FBDefines/FBDefines.h b/ios/Pods/Headers/Public/FlipperKit/FBDefines/FBDefines.h new file mode 120000 index 000000000..6f2c5bdd1 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FBDefines/FBDefines.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/FBDefines/FBDefines.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperClient.h b/ios/Pods/Headers/Public/FlipperKit/FlipperClient.h new file mode 120000 index 000000000..c980be15d --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperClient.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperConnection.h b/ios/Pods/Headers/Public/FlipperKit/FlipperConnection.h new file mode 120000 index 000000000..a07a33eb9 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperConnection.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperDiagnosticsViewController.h b/ios/Pods/Headers/Public/FlipperKit/FlipperDiagnosticsViewController.h new file mode 120000 index 000000000..104c20233 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperDiagnosticsViewController.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperDiagnosticsViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKit-umbrella.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKit-umbrella.h new file mode 120000 index 000000000..406177c1f --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKit-umbrella.h @@ -0,0 +1 @@ +../../../Target Support Files/FlipperKit/FlipperKit-umbrella.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKit.modulemap b/ios/Pods/Headers/Public/FlipperKit/FlipperKit.modulemap new file mode 120000 index 000000000..5961371f1 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKit.modulemap @@ -0,0 +1 @@ +../../../Target Support Files/FlipperKit/FlipperKit.modulemap \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h new file mode 120000 index 000000000..28fad6fb5 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitHighlightOverlay/SKHighlightOverlay.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitPluginUtils/FlipperKitHighlightOverlay/SKHighlightOverlay.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h new file mode 120000 index 000000000..ef46c9e89 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h new file mode 120000 index 000000000..eaf79d764 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKDescriptorMapper.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h new file mode 120000 index 000000000..8255f2b64 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKInvalidation.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h new file mode 120000 index 000000000..cf740e903 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutPlugin/SKTapListener.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKTapListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h new file mode 120000 index 000000000..6844dd500 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitLayoutTextSearchable/FKTextSearchable.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutTextSearchable/FKTextSearchable.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h new file mode 120000 index 000000000..665cdb2b8 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h new file mode 120000 index 000000000..f6d72d0b7 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKBufferingPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKBufferingPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h new file mode 120000 index 000000000..b7dc55a0d --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKNetworkReporter.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKNetworkReporter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h new file mode 120000 index 000000000..4892fd496 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKRequestInfo.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h new file mode 120000 index 000000000..a00e359be --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitNetworkPlugin/SKResponseInfo.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h new file mode 120000 index 000000000..bc679f939 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitReactPlugin/FlipperKitReactPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitReactPlugin/FlipperKitReactPlugin/FlipperKitReactPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h new file mode 120000 index 000000000..afa451984 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperPlugin.h b/ios/Pods/Headers/Public/FlipperKit/FlipperPlugin.h new file mode 120000 index 000000000..31938c93e --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperPlugin.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperPlugin.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperResponder.h b/ios/Pods/Headers/Public/FlipperKit/FlipperResponder.h new file mode 120000 index 000000000..a95dd285c --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperResponder.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperResponder.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/FlipperStateUpdateListener.h b/ios/Pods/Headers/Public/FlipperKit/FlipperStateUpdateListener.h new file mode 120000 index 000000000..08c8ecc0a --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/FlipperStateUpdateListener.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/FlipperStateUpdateListener.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h b/ios/Pods/Headers/Public/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h new file mode 120000 index 000000000..bd37e7ab0 --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h @@ -0,0 +1 @@ +../../../../FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/SKIOSNetworkAdapter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/FlipperKit/SKMacros.h b/ios/Pods/Headers/Public/FlipperKit/SKMacros.h new file mode 120000 index 000000000..fdd2403ec --- /dev/null +++ b/ios/Pods/Headers/Public/FlipperKit/SKMacros.h @@ -0,0 +1 @@ +../../../FlipperKit/iOS/FlipperKit/SKMacros.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORDataFuture.h b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORDataFuture.h deleted file mode 120000 index 5f87ffd11..000000000 --- a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORDataFuture.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORDataFuture.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORReachability.h b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORReachability.h new file mode 120000 index 000000000..e71cfdfcb --- /dev/null +++ b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORReachability.h @@ -0,0 +1 @@ +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORReachability.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStorageProtocol.h b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStorageProtocol.h new file mode 120000 index 000000000..53c776834 --- /dev/null +++ b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStorageProtocol.h @@ -0,0 +1 @@ +../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStorageProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStoredEvent.h b/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStoredEvent.h deleted file mode 120000 index 25f7c49ec..000000000 --- a/ios/Pods/Headers/Public/GoogleDataTransport/GDTCORStoredEvent.h +++ /dev/null @@ -1 +0,0 @@ -../../../GoogleDataTransport/GoogleDataTransport/GDTCORLibrary/Public/GDTCORStoredEvent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/aes.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/aes.h new file mode 120000 index 000000000..97a489fc7 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/aes.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/aes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1.h new file mode 120000 index 000000000..2ee6d1ec3 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1_mac.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1_mac.h new file mode 120000 index 000000000..29c1ceec5 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1_mac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1_mac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1t.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1t.h new file mode 120000 index 000000000..b9d44f5b3 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/asn1t.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/asn1t.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bio.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bio.h new file mode 120000 index 000000000..d4098b335 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bio.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/bio.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/blowfish.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/blowfish.h new file mode 120000 index 000000000..6995a7954 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/blowfish.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/blowfish.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bn.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bn.h new file mode 120000 index 000000000..9465a61e0 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/bn.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/bn.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/buffer.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/buffer.h new file mode 120000 index 000000000..68146764f --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/buffer.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/buffer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/camellia.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/camellia.h new file mode 120000 index 000000000..b5cbfde87 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/camellia.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/camellia.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cast.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cast.h new file mode 120000 index 000000000..1423aca0a --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cast.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cmac.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cmac.h new file mode 120000 index 000000000..8b651f7e0 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cmac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cmac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cms.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cms.h new file mode 120000 index 000000000..718982c14 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/cms.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/cms.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/comp.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/comp.h new file mode 120000 index 000000000..7ea4bce45 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/comp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/comp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf.h new file mode 120000 index 000000000..b3dcb0aec --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/conf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf_api.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf_api.h new file mode 120000 index 000000000..e94da2c0f --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/conf_api.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/conf_api.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/crypto.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/crypto.h new file mode 120000 index 000000000..b84d4f8a1 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/crypto.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/crypto.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des.h new file mode 120000 index 000000000..4261fd159 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/des.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des_old.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des_old.h new file mode 120000 index 000000000..5f94105a5 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/des_old.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/des_old.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dh.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dh.h new file mode 120000 index 000000000..0009abffe --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dh.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dh.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dsa.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dsa.h new file mode 120000 index 000000000..b79338d30 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dso.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dso.h new file mode 120000 index 000000000..3cd9024f8 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dso.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dso.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dtls1.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dtls1.h new file mode 120000 index 000000000..43acba1b5 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/dtls1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/dtls1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/e_os2.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/e_os2.h new file mode 120000 index 000000000..a961ab409 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/e_os2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/e_os2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ebcdic.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ebcdic.h new file mode 120000 index 000000000..265725bb7 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ebcdic.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ebcdic.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ec.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ec.h new file mode 120000 index 000000000..2c3c49d77 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ec.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ec.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdh.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdh.h new file mode 120000 index 000000000..25b079e5b --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdh.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ecdh.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdsa.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdsa.h new file mode 120000 index 000000000..517681e10 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ecdsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ecdsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/engine.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/engine.h new file mode 120000 index 000000000..c86dbf195 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/engine.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/engine.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/err.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/err.h new file mode 120000 index 000000000..66aa10c6a --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/err.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/err.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/evp.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/evp.h new file mode 120000 index 000000000..8d7ddcb3e --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/evp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/evp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/hmac.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/hmac.h new file mode 120000 index 000000000..7d1dbab11 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/hmac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/hmac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/idea.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/idea.h new file mode 120000 index 000000000..d16a12c26 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/idea.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/idea.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/krb5_asn.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/krb5_asn.h new file mode 120000 index 000000000..428549eca --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/krb5_asn.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/krb5_asn.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/kssl.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/kssl.h new file mode 120000 index 000000000..547d88203 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/kssl.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/kssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/lhash.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/lhash.h new file mode 120000 index 000000000..4e809c962 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/lhash.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/lhash.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md4.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md4.h new file mode 120000 index 000000000..eaec733aa --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md4.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/md4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md5.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md5.h new file mode 120000 index 000000000..40dce8faf --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/md5.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/md5.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/mdc2.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/mdc2.h new file mode 120000 index 000000000..939a5595f --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/mdc2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/mdc2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/modes.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/modes.h new file mode 120000 index 000000000..31852b890 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/modes.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/modes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/obj_mac.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/obj_mac.h new file mode 120000 index 000000000..a762cbb2f --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/obj_mac.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/obj_mac.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/objects.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/objects.h new file mode 120000 index 000000000..721325198 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/objects.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/objects.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ocsp.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ocsp.h new file mode 120000 index 000000000..194cc404d --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ocsp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ocsp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-arm64.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-arm64.h new file mode 120000 index 000000000..04000a53b --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-arm64.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-arm64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7.h new file mode 120000 index 000000000..831d559e4 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-armv7.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7s.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7s.h new file mode 120000 index 000000000..abcbeb242 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-armv7s.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-armv7s.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-i386.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-i386.h new file mode 120000 index 000000000..2e9b45560 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-i386.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-i386.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-x86_64.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-x86_64.h new file mode 120000 index 000000000..346d0b8cc --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf-x86_64.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf-x86_64.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf.h new file mode 120000 index 000000000..a65adfb03 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslconf.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslconf.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslv.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslv.h new file mode 120000 index 000000000..cfa8c461c --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/opensslv.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/opensslv.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ossl_typ.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ossl_typ.h new file mode 120000 index 000000000..69d1d043e --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ossl_typ.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ossl_typ.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem.h new file mode 120000 index 000000000..8ddc9fb45 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pem.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem2.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem2.h new file mode 120000 index 000000000..6b063c8da --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pem2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pem2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs12.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs12.h new file mode 120000 index 000000000..a7b581bf0 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs12.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pkcs12.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs7.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs7.h new file mode 120000 index 000000000..b84219bfe --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pkcs7.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pkcs7.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pqueue.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pqueue.h new file mode 120000 index 000000000..b3643e0cc --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/pqueue.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/pqueue.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rand.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rand.h new file mode 120000 index 000000000..b1a56a7cd --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rand.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rand.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc2.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc2.h new file mode 120000 index 000000000..e95aa4cc8 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rc2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc4.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc4.h new file mode 120000 index 000000000..f10827e81 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rc4.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rc4.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ripemd.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ripemd.h new file mode 120000 index 000000000..1f667944c --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ripemd.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ripemd.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rsa.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rsa.h new file mode 120000 index 000000000..f96d767e9 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/rsa.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/rsa.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/safestack.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/safestack.h new file mode 120000 index 000000000..bbafcba72 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/safestack.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/safestack.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/seed.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/seed.h new file mode 120000 index 000000000..c39fda464 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/seed.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/seed.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/sha.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/sha.h new file mode 120000 index 000000000..61282c132 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/sha.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/sha.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/shim.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/shim.h new file mode 120000 index 000000000..af394b9d7 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/shim.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/shim.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srp.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srp.h new file mode 120000 index 000000000..4fb9886e4 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/srp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srtp.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srtp.h new file mode 120000 index 000000000..9ae1fbaef --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/srtp.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/srtp.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl.h new file mode 120000 index 000000000..7e698749c --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl2.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl2.h new file mode 120000 index 000000000..ec3d83ec6 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl2.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl2.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl23.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl23.h new file mode 120000 index 000000000..8783362f3 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl23.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl23.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl3.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl3.h new file mode 120000 index 000000000..2c69510bf --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ssl3.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ssl3.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/stack.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/stack.h new file mode 120000 index 000000000..6659f6980 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/stack.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/stack.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/symhacks.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/symhacks.h new file mode 120000 index 000000000..246c6948e --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/symhacks.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/symhacks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/tls1.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/tls1.h new file mode 120000 index 000000000..ee638b811 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/tls1.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/tls1.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ts.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ts.h new file mode 120000 index 000000000..8b29cba94 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ts.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ts.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/txt_db.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/txt_db.h new file mode 120000 index 000000000..95acfe8f1 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/txt_db.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/txt_db.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui.h new file mode 120000 index 000000000..b2e967252 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ui.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui_compat.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui_compat.h new file mode 120000 index 000000000..e22819149 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/ui_compat.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/ui_compat.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/whrlpool.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/whrlpool.h new file mode 120000 index 000000000..95b2ef9b9 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/whrlpool.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/whrlpool.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509.h new file mode 120000 index 000000000..aefb78872 --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509_vfy.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509_vfy.h new file mode 120000 index 000000000..068dbae1f --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509_vfy.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509_vfy.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509v3.h b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509v3.h new file mode 120000 index 000000000..fac96042d --- /dev/null +++ b/ios/Pods/Headers/Public/OpenSSL-Universal/openssl/x509v3.h @@ -0,0 +1 @@ +../../../../OpenSSL-Universal/ios/include/openssl/x509v3.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorage.h b/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorage.h new file mode 120000 index 000000000..c27b7a23e --- /dev/null +++ b/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorage.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/async-storage/ios/RNCAsyncStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorageDelegate.h b/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorageDelegate.h new file mode 120000 index 000000000..83daae9c5 --- /dev/null +++ b/ios/Pods/Headers/Public/RNCAsyncStorage/RNCAsyncStorageDelegate.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/async-storage/ios/RNCAsyncStorageDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/RNReanimated/REAUtils.h b/ios/Pods/Headers/Public/RNReanimated/REAUtils.h new file mode 120000 index 000000000..d3807079a --- /dev/null +++ b/ios/Pods/Headers/Public/RNReanimated/REAUtils.h @@ -0,0 +1 @@ +../../../../../node_modules/react-native-reanimated/ios/REAUtils.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAccessibilityManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTAccessibilityManager.h index fc8b03b46..7e87bdd9c 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTAccessibilityManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAccessibilityManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAccessibilityManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAccessibilityManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTActionSheetManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTActionSheetManager.h index f16f03278..04b9961c8 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTActionSheetManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTActionSheetManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheetManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTActionSheetManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAlertManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTAlertManager.h index d48ca0d6b..fa0fb56db 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTAlertManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAlertManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAlertManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAlertManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAnimationPlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTAnimationPlugins.h new file mode 120000 index 000000000..1b4139671 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAnimationPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/NativeAnimation/RCTAnimationPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAppState.h b/ios/Pods/Headers/Public/React-Core/React/RCTAppState.h index 1e2dfd29b..d7f7500a5 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTAppState.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAppState.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAppState.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAppState.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAppearance.h b/ios/Pods/Headers/Public/React-Core/React/RCTAppearance.h new file mode 120000 index 000000000..e89b140fe --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAppearance.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTAppearance.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTAsyncLocalStorage.h b/ios/Pods/Headers/Public/React-Core/React/RCTAsyncLocalStorage.h index 16d86163b..56ac4219a 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTAsyncLocalStorage.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTAsyncLocalStorage.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTAsyncLocalStorage.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTAsyncLocalStorage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTClipboard.h b/ios/Pods/Headers/Public/React-Core/React/RCTClipboard.h index e90e62480..6224fa170 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTClipboard.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTClipboard.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTClipboard.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTClipboard.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTConstants.h b/ios/Pods/Headers/Public/React-Core/React/RCTConstants.h new file mode 120000 index 000000000..d00b686b8 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTConstants.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTConstants.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTDevMenu.h b/ios/Pods/Headers/Public/React-Core/React/RCTDevMenu.h index c265676ea..56e8e4f2c 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTDevMenu.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTDevMenu.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/DevSupport/RCTDevMenu.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDevMenu.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTDevSettings.h b/ios/Pods/Headers/Public/React-Core/React/RCTDevSettings.h index d139e6792..46fa8384b 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTDevSettings.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTDevSettings.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTDevSettings.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDevSettings.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTDeviceInfo.h b/ios/Pods/Headers/Public/React-Core/React/RCTDeviceInfo.h index c3a35d58c..f25f71850 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTDeviceInfo.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTDeviceInfo.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTDeviceInfo.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTDeviceInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTFPSGraph.h b/ios/Pods/Headers/Public/React-Core/React/RCTFPSGraph.h index d653014cb..5ed8cabf6 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTFPSGraph.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTFPSGraph.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Profiler/RCTFPSGraph.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTFPSGraph.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTI18nManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTI18nManager.h index d3c6d62e0..38b8a201d 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTI18nManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTI18nManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTI18nManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTI18nManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImageEditingManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTImageEditingManager.h index ed704ac69..59fc21c1d 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTImageEditingManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImageEditingManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageEditingManager.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageEditingManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImageLoader.h b/ios/Pods/Headers/Public/React-Core/React/RCTImageLoader.h index 63f9cd794..c5f3ca662 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTImageLoader.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImageLoader.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageLoader.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageLoader.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImageLoaderWithAttributionProtocol.h b/ios/Pods/Headers/Public/React-Core/React/RCTImageLoaderWithAttributionProtocol.h new file mode 120000 index 000000000..d0f46eae8 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImageLoaderWithAttributionProtocol.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImagePlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTImagePlugins.h new file mode 120000 index 000000000..7b0cc1dde --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImagePlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImagePlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImageStoreManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTImageStoreManager.h index ac7b53be7..1a11b286f 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTImageStoreManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImageStoreManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/CoreModules/RCTImageStoreManager.h \ No newline at end of file +../../../../../../node_modules/react-native/Libraries/Image/RCTImageStoreManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTImageURLLoaderWithAttribution.h b/ios/Pods/Headers/Public/React-Core/React/RCTImageURLLoaderWithAttribution.h new file mode 120000 index 000000000..2e0578785 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTImageURLLoaderWithAttribution.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Image/RCTImageURLLoaderWithAttribution.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTKeyboardObserver.h b/ios/Pods/Headers/Public/React-Core/React/RCTKeyboardObserver.h index 6e324b842..77f9767cc 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTKeyboardObserver.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTKeyboardObserver.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTKeyboardObserver.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTKeyboardObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTLinkingPlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTLinkingPlugins.h new file mode 120000 index 000000000..d90e57cd1 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTLinkingPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/LinkingIOS/RCTLinkingPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTLogBox.h b/ios/Pods/Headers/Public/React-Core/React/RCTLogBox.h new file mode 120000 index 000000000..1c62117bf --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTLogBox.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTLogBox.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTNetInfo.h b/ios/Pods/Headers/Public/React-Core/React/RCTNetInfo.h deleted file mode 120000 index 7a096e690..000000000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTNetInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/Libraries/Network/RCTNetInfo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTNetworkPlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTNetworkPlugins.h new file mode 120000 index 000000000..c927dfa33 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTNetworkPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Network/RCTNetworkPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTRedBox.h b/ios/Pods/Headers/Public/React-Core/React/RCTRedBox.h index a17cd2acf..39940a898 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTRedBox.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTRedBox.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTRedBox.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTRedBox.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTRedBoxSetEnabled.h b/ios/Pods/Headers/Public/React-Core/React/RCTRedBoxSetEnabled.h new file mode 120000 index 000000000..356daf6ff --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTRedBoxSetEnabled.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTRedBoxSetEnabled.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControl.h b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControl.h index a44be6e10..cb6d55dd1 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControl.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControl.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Views/RCTRefreshControl.h \ No newline at end of file +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControlManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControlManager.h index 8939ee0df..08c52d839 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControlManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshControlManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Views/RCTRefreshControlManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTRefreshableProtocol.h b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshableProtocol.h new file mode 120000 index 000000000..8646a6299 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTRefreshableProtocol.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Views/RefreshControl/RCTRefreshableProtocol.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTScrollEvent.h b/ios/Pods/Headers/Public/React-Core/React/RCTScrollEvent.h new file mode 120000 index 000000000..a0091281c --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTScrollEvent.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Views/ScrollView/RCTScrollEvent.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTSettingsPlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTSettingsPlugins.h new file mode 120000 index 000000000..c03ab17e5 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTSettingsPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Settings/RCTSettingsPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTSourceCode.h b/ios/Pods/Headers/Public/React-Core/React/RCTSourceCode.h index 5d9c5d2af..e706c13ec 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTSourceCode.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTSourceCode.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTSourceCode.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTSourceCode.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTStatusBarManager.h b/ios/Pods/Headers/Public/React-Core/React/RCTStatusBarManager.h index b112a8adc..bcc1757c4 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTStatusBarManager.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTStatusBarManager.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTStatusBarManager.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTStatusBarManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTTVNavigationEventEmitter.h b/ios/Pods/Headers/Public/React-Core/React/RCTTVNavigationEventEmitter.h new file mode 120000 index 000000000..83d07c0f6 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTTVNavigationEventEmitter.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/CoreModules/RCTTVNavigationEventEmitter.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTTiming.h b/ios/Pods/Headers/Public/React-Core/React/RCTTiming.h index 00772a3e6..af5ddad62 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTTiming.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTTiming.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/React/Modules/RCTTiming.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTTiming.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTUtilsUIOverride.h b/ios/Pods/Headers/Public/React-Core/React/RCTUtilsUIOverride.h new file mode 120000 index 000000000..a5aaa954a --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTUtilsUIOverride.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/React/Base/RCTUtilsUIOverride.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTVibrationPlugins.h b/ios/Pods/Headers/Public/React-Core/React/RCTVibrationPlugins.h new file mode 120000 index 000000000..bae7f2445 --- /dev/null +++ b/ios/Pods/Headers/Public/React-Core/React/RCTVibrationPlugins.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/Libraries/Vibration/RCTVibrationPlugins.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketExecutor.h b/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketExecutor.h index 8737c8868..cbe306bec 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketExecutor.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketExecutor.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTWebSocketExecutor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketModule.h b/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketModule.h index db623dff1..3e102ab26 120000 --- a/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketModule.h +++ b/ios/Pods/Headers/Public/React-Core/React/RCTWebSocketModule.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/Libraries/WebSocket/RCTWebSocketModule.h \ No newline at end of file +../../../../../../node_modules/react-native/React/CoreModules/RCTWebSocketModule.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/React-RCTBlob/RCTBlob/RCTBlobCollector.h b/ios/Pods/Headers/Public/React-RCTBlob/RCTBlob/RCTBlobCollector.h deleted file mode 120000 index 33df7e7ee..000000000 --- a/ios/Pods/Headers/Public/React-RCTBlob/RCTBlob/RCTBlobCollector.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/Libraries/Blob/RCTBlobCollector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/BridgeJSCallInvoker.h b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/BridgeJSCallInvoker.h index 5b8aac5df..08d71be32 120000 --- a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/BridgeJSCallInvoker.h +++ b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/BridgeJSCallInvoker.h @@ -1 +1 @@ -../../../../../../node_modules/react-native/ReactCommon/jscallinvoker/ReactCommon/BridgeJSCallInvoker.h \ No newline at end of file +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/BridgeJSCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/CallInvoker.h b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/CallInvoker.h new file mode 120000 index 000000000..fb45cee52 --- /dev/null +++ b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/CallInvoker.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/CallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/JSCallInvoker.h b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/JSCallInvoker.h deleted file mode 120000 index 9fac1871a..000000000 --- a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/JSCallInvoker.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../node_modules/react-native/ReactCommon/jscallinvoker/ReactCommon/JSCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h new file mode 120000 index 000000000..0720dccb3 --- /dev/null +++ b/ios/Pods/Headers/Public/ReactCommon/ReactCommon/MessageQueueThreadCallInvoker.h @@ -0,0 +1 @@ +../../../../../../node_modules/react-native/ReactCommon/callinvoker/ReactCommon/MessageQueueThreadCallInvoker.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/ReactNativeART/ARTShadow.h b/ios/Pods/Headers/Public/ReactNativeART/ARTShadow.h new file mode 120000 index 000000000..054b91ce5 --- /dev/null +++ b/ios/Pods/Headers/Public/ReactNativeART/ARTShadow.h @@ -0,0 +1 @@ +../../../../../node_modules/@react-native-community/art/ios/ARTShadow.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/Color+Interpolation.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/Color+Interpolation.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/Color+Interpolation.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/Color+Interpolation.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/LNAnimator.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNAnimator.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/LNAnimator.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNAnimator.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/LNInterpolable.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNInterpolable.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/LNInterpolable.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNInterpolable.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/LNInterpolation.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNInterpolation.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/LNInterpolation.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/LNInterpolation.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/NSValue+Interpolation.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/NSValue+Interpolation.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/NSValue+Interpolation.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/NSValue+Interpolation.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/RCTCustomInputController.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/RCTCustomInputController.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/RCTCustomInputController.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/RCTCustomInputController.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-input/RCTCustomKeyboardViewController.h b/ios/Pods/Headers/Public/ReactNativeKeyboardInput/RCTCustomKeyboardViewController.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-input/RCTCustomKeyboardViewController.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardInput/RCTCustomKeyboardViewController.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-tracking-view/KeyboardTrackingViewManager.h b/ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/KeyboardTrackingViewManager.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-tracking-view/KeyboardTrackingViewManager.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/KeyboardTrackingViewManager.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-tracking-view/ObservingInputAccessoryView.h b/ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/ObservingInputAccessoryView.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-tracking-view/ObservingInputAccessoryView.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/ObservingInputAccessoryView.h diff --git a/ios/Pods/Headers/Public/react-native-keyboard-tracking-view/UIResponder+FirstResponder.h b/ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/UIResponder+FirstResponder.h similarity index 100% rename from ios/Pods/Headers/Public/react-native-keyboard-tracking-view/UIResponder+FirstResponder.h rename to ios/Pods/Headers/Public/ReactNativeKeyboardTrackingView/UIResponder+FirstResponder.h diff --git a/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderInterface.h b/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderInterface.h new file mode 120000 index 000000000..0e04dcd08 --- /dev/null +++ b/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderInterface.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/Interfaces/UMAppLoaderInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderProvider.h b/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderProvider.h new file mode 120000 index 000000000..e05ce9f03 --- /dev/null +++ b/ios/Pods/Headers/Public/UMAppLoader/UMAppLoaderProvider.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/UMAppLoaderProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/UMAppLoader/UMAppRecordInterface.h b/ios/Pods/Headers/Public/UMAppLoader/UMAppRecordInterface.h new file mode 120000 index 000000000..8273e4ddb --- /dev/null +++ b/ios/Pods/Headers/Public/UMAppLoader/UMAppRecordInterface.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-app-loader/ios/UMAppLoader/Interfaces/UMAppRecordInterface.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/UMCore/UMErrorCodes.h b/ios/Pods/Headers/Public/UMCore/UMErrorCodes.h new file mode 120000 index 000000000..ad5c734a4 --- /dev/null +++ b/ios/Pods/Headers/Public/UMCore/UMErrorCodes.h @@ -0,0 +1 @@ +../../../../../node_modules/@unimodules/core/ios/UMCore/UMErrorCodes.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/UMPermissionsInterface/UMPermissionsMethodsDelegate.h b/ios/Pods/Headers/Public/UMPermissionsInterface/UMPermissionsMethodsDelegate.h new file mode 120000 index 000000000..5c0a00ac4 --- /dev/null +++ b/ios/Pods/Headers/Public/UMPermissionsInterface/UMPermissionsMethodsDelegate.h @@ -0,0 +1 @@ +../../../../../node_modules/unimodules-permissions-interface/ios/UMPermissionsInterface/UMPermissionsMethodsDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h b/ios/Pods/Headers/Public/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h new file mode 120000 index 000000000..9f1d08845 --- /dev/null +++ b/ios/Pods/Headers/Public/UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h @@ -0,0 +1 @@ +../../../../../node_modules/@unimodules/react-native-adapter/ios/UMReactNativeAdapter/UMModuleRegistryAdapter/UMModuleRegistryHolderReactModule.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Yoga/Yoga-umbrella.h b/ios/Pods/Headers/Public/Yoga/Yoga-umbrella.h new file mode 120000 index 000000000..3fbe042b1 --- /dev/null +++ b/ios/Pods/Headers/Public/Yoga/Yoga-umbrella.h @@ -0,0 +1 @@ +../../../Target Support Files/Yoga/Yoga-umbrella.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Yoga/Yoga.modulemap b/ios/Pods/Headers/Public/Yoga/Yoga.modulemap new file mode 120000 index 000000000..8f4cb80e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Yoga/Yoga.modulemap @@ -0,0 +1 @@ +../../../Target Support Files/Yoga/Yoga.modulemap \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YogaKit/UIView+Yoga.h b/ios/Pods/Headers/Public/YogaKit/UIView+Yoga.h new file mode 120000 index 000000000..611221e0e --- /dev/null +++ b/ios/Pods/Headers/Public/YogaKit/UIView+Yoga.h @@ -0,0 +1 @@ +../../../YogaKit/YogaKit/Source/UIView+Yoga.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YogaKit/YGLayout.h b/ios/Pods/Headers/Public/YogaKit/YGLayout.h new file mode 120000 index 000000000..d58b91b9c --- /dev/null +++ b/ios/Pods/Headers/Public/YogaKit/YGLayout.h @@ -0,0 +1 @@ +../../../YogaKit/YogaKit/Source/YGLayout.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YogaKit/YogaKit-umbrella.h b/ios/Pods/Headers/Public/YogaKit/YogaKit-umbrella.h new file mode 120000 index 000000000..57326b9c5 --- /dev/null +++ b/ios/Pods/Headers/Public/YogaKit/YogaKit-umbrella.h @@ -0,0 +1 @@ +../../../Target Support Files/YogaKit/YogaKit-umbrella.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YogaKit/YogaKit.modulemap b/ios/Pods/Headers/Public/YogaKit/YogaKit.modulemap new file mode 120000 index 000000000..e736ac002 --- /dev/null +++ b/ios/Pods/Headers/Public/YogaKit/YogaKit.modulemap @@ -0,0 +1 @@ +../../../Target Support Files/YogaKit/YogaKit.modulemap \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-notifications/RNNotificationCenterMulticast.h b/ios/Pods/Headers/Public/react-native-notifications/RNNotificationCenterMulticast.h new file mode 120000 index 000000000..3ed47c5e1 --- /dev/null +++ b/ios/Pods/Headers/Public/react-native-notifications/RNNotificationCenterMulticast.h @@ -0,0 +1 @@ +../../../../../node_modules/react-native-notifications/RNNotifications/RNNotificationCenterMulticast.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-video/RCTVideo.h b/ios/Pods/Headers/Public/react-native-video/RCTVideo.h deleted file mode 120000 index ea6d68e02..000000000 --- a/ios/Pods/Headers/Public/react-native-video/RCTVideo.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-video/RCTVideoManager.h b/ios/Pods/Headers/Public/react-native-video/RCTVideoManager.h deleted file mode 120000 index 09465d815..000000000 --- a/ios/Pods/Headers/Public/react-native-video/RCTVideoManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewController.h b/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewController.h deleted file mode 120000 index 907439772..000000000 --- a/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewControllerDelegate.h b/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewControllerDelegate.h deleted file mode 120000 index 3ff8dff0e..000000000 --- a/ios/Pods/Headers/Public/react-native-video/RCTVideoPlayerViewControllerDelegate.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/RCTVideoPlayerViewControllerDelegate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-video/UIView+FindUIViewController.h b/ios/Pods/Headers/Public/react-native-video/UIView+FindUIViewController.h deleted file mode 120000 index 0bf5e182c..000000000 --- a/ios/Pods/Headers/Public/react-native-video/UIView+FindUIViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../node_modules/react-native-video/ios/Video/UIView+FindUIViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-webview/RNCWKProcessPoolManager.h b/ios/Pods/Headers/Public/react-native-webview/RNCWKProcessPoolManager.h index b9b4d0a98..462906351 120000 --- a/ios/Pods/Headers/Public/react-native-webview/RNCWKProcessPoolManager.h +++ b/ios/Pods/Headers/Public/react-native-webview/RNCWKProcessPoolManager.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWKProcessPoolManager.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWKProcessPoolManager.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-webview/RNCWebView.h b/ios/Pods/Headers/Public/react-native-webview/RNCWebView.h index c814fcebc..5168adbcc 120000 --- a/ios/Pods/Headers/Public/react-native-webview/RNCWebView.h +++ b/ios/Pods/Headers/Public/react-native-webview/RNCWebView.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWebView.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWebView.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/react-native-webview/RNCWebViewManager.h b/ios/Pods/Headers/Public/react-native-webview/RNCWebViewManager.h index c20dd096e..ef2014d2b 120000 --- a/ios/Pods/Headers/Public/react-native-webview/RNCWebViewManager.h +++ b/ios/Pods/Headers/Public/react-native-webview/RNCWebViewManager.h @@ -1 +1 @@ -../../../../../node_modules/react-native-webview/ios/RNCWebViewManager.h \ No newline at end of file +../../../../../node_modules/react-native-webview/apple/RNCWebViewManager.h \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/GoogleSignIn.bundle/en.lproj/GoogleSignIn.strings b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/GoogleSignIn.bundle/en.lproj/GoogleSignIn.strings index 6b55b9b87..460ad64a2 100755 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/GoogleSignIn.bundle/en.lproj/GoogleSignIn.strings +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/GoogleSignIn.bundle/en.lproj/GoogleSignIn.strings @@ -4,18 +4,6 @@ /* Long form sign-in button text */ "Sign in with Google" = "Sign in with Google"; -/* The title of the promotional prompt to install the Google app. */ -"PromoTitle" = "Sign in with Google"; - -/* The body message of the promotional prompt to install the Google app. */ -"PromoMessage" = "Get the free Google app and sign in to apps with your Google Account. No need to remember passwords."; - -/* The cancel button on the promotional prompt to install the Google app. */ -"PromoActionCancel" = "Cancel"; - -/* The install button on the promotional prompt to install the Google app. */ -"PromoActionInstall" = "Get"; - /* The text for the button for user to acknowledge and dismiss a dialog. */ "OK" = "OK"; diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/InfoPlistUtil.h b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/InfoPlistUtil.h new file mode 100644 index 000000000..f7d5bb8f9 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/InfoPlistUtil.h @@ -0,0 +1,23 @@ +/* +* Copyright @ 2019-present 8x8, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#import <Foundation/Foundation.h> + +@interface InfoPlistUtil : NSObject + ++ (BOOL)containsRealServiceInfoPlistInBundle:(NSBundle *)bundle; + +@end diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet-Swift.h b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet-Swift.h index 1bf1bfcc1..0abc9c774 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet-Swift.h +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet-Swift.h @@ -1,4 +1,4 @@ -// Generated by Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7) +// Generated by Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgcc-compat" @@ -82,6 +82,11 @@ typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); #else # define SWIFT_NOESCAPE #endif +#if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +#else +# define SWIFT_RELEASES_ARGUMENT +#endif #if __has_attribute(warn_unused_result) # define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else @@ -227,7 +232,7 @@ SWIFT_CLASS("_TtC9JitsiMeet14JMCallKitProxy") /// Defaults to enabled, set to false when you don’t want to use CallKit. SWIFT_CLASS_PROPERTY(@property (nonatomic, class) BOOL enabled;) + (BOOL)enabled SWIFT_WARN_UNUSED_RESULT; -+ (void)setEnabled:(BOOL)newValue; ++ (void)setEnabled:(BOOL)value; + (void)configureProviderWithLocalizedName:(NSString * _Nonnull)localizedName ringtoneSound:(NSString * _Nullable)ringtoneSound iconTemplateImageData:(NSData * _Nullable)iconTemplateImageData; + (BOOL)isProviderConfigured SWIFT_WARN_UNUSED_RESULT; + (void)addListener:(id <JMCallKitListener> _Nonnull)listener; diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet.h b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet.h index b0123c6f4..8db09854d 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet.h +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Headers/JitsiMeet.h @@ -20,6 +20,7 @@ #import <JitsiMeet/JitsiMeetConferenceOptions.h> #import <JitsiMeet/JitsiMeetLogger.h> #import <JitsiMeet/JitsiMeetBaseLogHandler.h> +#import <JitsiMeet/InfoPlistUtil.h> @interface JitsiMeet : NSObject @@ -37,7 +38,6 @@ * List of domains used for universal linking. */ @property (copy, nonatomic, nullable) NSArray<NSString *> *universalLinkDomains; - /** * Default conference options used for all conferences. These options will be merged * with those passed to JitsiMeetView.join when joining a conference. @@ -65,4 +65,6 @@ - (JitsiMeetConferenceOptions *_Nonnull)getInitialConferenceOptions; +- (BOOL)isCrashReportingDisabled; + @end diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Info.plist b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Info.plist index 741240b21..adcb66089 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Info.plist and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Info.plist differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/JitsiMeet b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/JitsiMeet index a386beaa9..5c09324b3 100755 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/JitsiMeet and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/JitsiMeet differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo new file mode 100644 index 000000000..26010b723 Binary files /dev/null and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64.swiftsourceinfo b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64.swiftsourceinfo new file mode 100644 index 000000000..26010b723 Binary files /dev/null and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/Project/x86_64.swiftsourceinfo differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftmodule deleted file mode 100644 index 0cb2d01b6..000000000 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftmodule and /dev/null differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftdoc b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftdoc similarity index 95% rename from ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftdoc rename to ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftdoc index dfb9d20e8..8e5023661 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm.swiftdoc and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftdoc differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftinterface b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftinterface new file mode 100644 index 000000000..87de9daba --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftinterface @@ -0,0 +1,72 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) +// swift-module-flags: -target arm64-apple-ios11.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name JitsiMeet +import AVKit +import CallKit +import Foundation +@_exported import JitsiMeet +import Swift +public typealias AnimationCompletion = (Swift.Bool) -> Swift.Void +public protocol PiPViewCoordinatorDelegate : AnyObject { + func exitPictureInPicture() +} +public class PiPViewCoordinator { + public var dragBoundInsets: UIKit.UIEdgeInsets { + get + set + } + public enum Position { + case lowerRightCorner + case upperRightCorner + case lowerLeftCorner + case upperLeftCorner + public static func == (a: JitsiMeet.PiPViewCoordinator.Position, b: JitsiMeet.PiPViewCoordinator.Position) -> Swift.Bool + public var hashValue: Swift.Int { + get + } + public func hash(into hasher: inout Swift.Hasher) + } + public var initialPositionInSuperview: JitsiMeet.PiPViewCoordinator.Position + public var pipSizeRatio: CoreGraphics.CGFloat + weak public var delegate: JitsiMeet.PiPViewCoordinatorDelegate? + public init(withView view: UIKit.UIView) + public func configureAsStickyView(withParentView parentView: UIKit.UIView? = nil) + public func show(completion: JitsiMeet.AnimationCompletion? = nil) + public func hide(completion: JitsiMeet.AnimationCompletion? = nil) + public func enterPictureInPicture() + @objc public func exitPictureInPicture() + public func resetBounds(bounds: CoreGraphics.CGRect) + public func stopDragGesture() + open func configureExitPiPButton(target: Any, action: ObjectiveC.Selector) -> UIKit.UIButton + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc final public class JMCallKitProxy : ObjectiveC.NSObject { + @objc public static var enabled: Swift.Bool { + @objc get + @objc set + } + @objc public static func configureProvider(localizedName: Swift.String, ringtoneSound: Swift.String?, iconTemplateImageData: Foundation.Data?) + @objc public static func isProviderConfigured() -> Swift.Bool + @objc public static func addListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func removeListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func hasActiveCallForUUID(_ callUUID: Swift.String) -> Swift.Bool + @objc public static func reportNewIncomingCall(UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc public static func reportCallUpdate(with UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool) + @objc public static func reportCall(with UUID: Foundation.UUID, endedAt dateEnded: Foundation.Date?, reason endedReason: CallKit.CXCallEndedReason) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, startedConnectingAt dateStartedConnecting: Foundation.Date?) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, connectedAt dateConnected: Foundation.Date?) + @objc public static func request(_ transaction: CallKit.CXTransaction, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc deinit +} +@objc public protocol JMCallKitListener : ObjectiveC.NSObjectProtocol { + @objc optional func providerDidReset() + @objc optional func performAnswerCall(UUID: Foundation.UUID) + @objc optional func performEndCall(UUID: Foundation.UUID) + @objc optional func performSetMutedCall(UUID: Foundation.UUID, isMuted: Swift.Bool) + @objc optional func performStartCall(UUID: Foundation.UUID, isVideo: Swift.Bool) + @objc optional func providerDidActivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerDidDeactivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerTimedOutPerformingAction(action: CallKit.CXAction) +} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Equatable {} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Hashable {} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftmodule new file mode 100644 index 000000000..45f17a283 Binary files /dev/null and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64-apple-ios.swiftmodule differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftdoc b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftdoc index bbfe000c6..8e5023661 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftdoc and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftdoc differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftinterface b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftinterface new file mode 100644 index 000000000..87de9daba --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftinterface @@ -0,0 +1,72 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) +// swift-module-flags: -target arm64-apple-ios11.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name JitsiMeet +import AVKit +import CallKit +import Foundation +@_exported import JitsiMeet +import Swift +public typealias AnimationCompletion = (Swift.Bool) -> Swift.Void +public protocol PiPViewCoordinatorDelegate : AnyObject { + func exitPictureInPicture() +} +public class PiPViewCoordinator { + public var dragBoundInsets: UIKit.UIEdgeInsets { + get + set + } + public enum Position { + case lowerRightCorner + case upperRightCorner + case lowerLeftCorner + case upperLeftCorner + public static func == (a: JitsiMeet.PiPViewCoordinator.Position, b: JitsiMeet.PiPViewCoordinator.Position) -> Swift.Bool + public var hashValue: Swift.Int { + get + } + public func hash(into hasher: inout Swift.Hasher) + } + public var initialPositionInSuperview: JitsiMeet.PiPViewCoordinator.Position + public var pipSizeRatio: CoreGraphics.CGFloat + weak public var delegate: JitsiMeet.PiPViewCoordinatorDelegate? + public init(withView view: UIKit.UIView) + public func configureAsStickyView(withParentView parentView: UIKit.UIView? = nil) + public func show(completion: JitsiMeet.AnimationCompletion? = nil) + public func hide(completion: JitsiMeet.AnimationCompletion? = nil) + public func enterPictureInPicture() + @objc public func exitPictureInPicture() + public func resetBounds(bounds: CoreGraphics.CGRect) + public func stopDragGesture() + open func configureExitPiPButton(target: Any, action: ObjectiveC.Selector) -> UIKit.UIButton + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc final public class JMCallKitProxy : ObjectiveC.NSObject { + @objc public static var enabled: Swift.Bool { + @objc get + @objc set + } + @objc public static func configureProvider(localizedName: Swift.String, ringtoneSound: Swift.String?, iconTemplateImageData: Foundation.Data?) + @objc public static func isProviderConfigured() -> Swift.Bool + @objc public static func addListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func removeListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func hasActiveCallForUUID(_ callUUID: Swift.String) -> Swift.Bool + @objc public static func reportNewIncomingCall(UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc public static func reportCallUpdate(with UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool) + @objc public static func reportCall(with UUID: Foundation.UUID, endedAt dateEnded: Foundation.Date?, reason endedReason: CallKit.CXCallEndedReason) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, startedConnectingAt dateStartedConnecting: Foundation.Date?) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, connectedAt dateConnected: Foundation.Date?) + @objc public static func request(_ transaction: CallKit.CXTransaction, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc deinit +} +@objc public protocol JMCallKitListener : ObjectiveC.NSObjectProtocol { + @objc optional func providerDidReset() + @objc optional func performAnswerCall(UUID: Foundation.UUID) + @objc optional func performEndCall(UUID: Foundation.UUID) + @objc optional func performSetMutedCall(UUID: Foundation.UUID, isMuted: Swift.Bool) + @objc optional func performStartCall(UUID: Foundation.UUID, isVideo: Swift.Bool) + @objc optional func providerDidActivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerDidDeactivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerTimedOutPerformingAction(action: CallKit.CXAction) +} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Equatable {} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Hashable {} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftmodule index f20158cab..45f17a283 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftmodule and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/arm64.swiftmodule differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftmodule deleted file mode 100644 index 57b3b6ea3..000000000 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftmodule and /dev/null differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftdoc b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftdoc similarity index 88% rename from ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftdoc rename to ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftdoc index 439101190..dc266c8b5 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/i386.swiftdoc and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftdoc differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftinterface b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftinterface new file mode 100644 index 000000000..27ebba481 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftinterface @@ -0,0 +1,72 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) +// swift-module-flags: -target x86_64-apple-ios11.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name JitsiMeet +import AVKit +import CallKit +import Foundation +@_exported import JitsiMeet +import Swift +public typealias AnimationCompletion = (Swift.Bool) -> Swift.Void +public protocol PiPViewCoordinatorDelegate : AnyObject { + func exitPictureInPicture() +} +public class PiPViewCoordinator { + public var dragBoundInsets: UIKit.UIEdgeInsets { + get + set + } + public enum Position { + case lowerRightCorner + case upperRightCorner + case lowerLeftCorner + case upperLeftCorner + public static func == (a: JitsiMeet.PiPViewCoordinator.Position, b: JitsiMeet.PiPViewCoordinator.Position) -> Swift.Bool + public var hashValue: Swift.Int { + get + } + public func hash(into hasher: inout Swift.Hasher) + } + public var initialPositionInSuperview: JitsiMeet.PiPViewCoordinator.Position + public var pipSizeRatio: CoreGraphics.CGFloat + weak public var delegate: JitsiMeet.PiPViewCoordinatorDelegate? + public init(withView view: UIKit.UIView) + public func configureAsStickyView(withParentView parentView: UIKit.UIView? = nil) + public func show(completion: JitsiMeet.AnimationCompletion? = nil) + public func hide(completion: JitsiMeet.AnimationCompletion? = nil) + public func enterPictureInPicture() + @objc public func exitPictureInPicture() + public func resetBounds(bounds: CoreGraphics.CGRect) + public func stopDragGesture() + open func configureExitPiPButton(target: Any, action: ObjectiveC.Selector) -> UIKit.UIButton + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc final public class JMCallKitProxy : ObjectiveC.NSObject { + @objc public static var enabled: Swift.Bool { + @objc get + @objc set + } + @objc public static func configureProvider(localizedName: Swift.String, ringtoneSound: Swift.String?, iconTemplateImageData: Foundation.Data?) + @objc public static func isProviderConfigured() -> Swift.Bool + @objc public static func addListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func removeListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func hasActiveCallForUUID(_ callUUID: Swift.String) -> Swift.Bool + @objc public static func reportNewIncomingCall(UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc public static func reportCallUpdate(with UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool) + @objc public static func reportCall(with UUID: Foundation.UUID, endedAt dateEnded: Foundation.Date?, reason endedReason: CallKit.CXCallEndedReason) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, startedConnectingAt dateStartedConnecting: Foundation.Date?) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, connectedAt dateConnected: Foundation.Date?) + @objc public static func request(_ transaction: CallKit.CXTransaction, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc deinit +} +@objc public protocol JMCallKitListener : ObjectiveC.NSObjectProtocol { + @objc optional func providerDidReset() + @objc optional func performAnswerCall(UUID: Foundation.UUID) + @objc optional func performEndCall(UUID: Foundation.UUID) + @objc optional func performSetMutedCall(UUID: Foundation.UUID, isMuted: Swift.Bool) + @objc optional func performStartCall(UUID: Foundation.UUID, isVideo: Swift.Bool) + @objc optional func providerDidActivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerDidDeactivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerTimedOutPerformingAction(action: CallKit.CXAction) +} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Equatable {} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Hashable {} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftmodule new file mode 100644 index 000000000..39e068449 Binary files /dev/null and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64-apple-ios-simulator.swiftmodule differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftdoc b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftdoc index f12c63708..dc266c8b5 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftdoc and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftdoc differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftinterface b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftinterface new file mode 100644 index 000000000..27ebba481 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftinterface @@ -0,0 +1,72 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) +// swift-module-flags: -target x86_64-apple-ios11.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name JitsiMeet +import AVKit +import CallKit +import Foundation +@_exported import JitsiMeet +import Swift +public typealias AnimationCompletion = (Swift.Bool) -> Swift.Void +public protocol PiPViewCoordinatorDelegate : AnyObject { + func exitPictureInPicture() +} +public class PiPViewCoordinator { + public var dragBoundInsets: UIKit.UIEdgeInsets { + get + set + } + public enum Position { + case lowerRightCorner + case upperRightCorner + case lowerLeftCorner + case upperLeftCorner + public static func == (a: JitsiMeet.PiPViewCoordinator.Position, b: JitsiMeet.PiPViewCoordinator.Position) -> Swift.Bool + public var hashValue: Swift.Int { + get + } + public func hash(into hasher: inout Swift.Hasher) + } + public var initialPositionInSuperview: JitsiMeet.PiPViewCoordinator.Position + public var pipSizeRatio: CoreGraphics.CGFloat + weak public var delegate: JitsiMeet.PiPViewCoordinatorDelegate? + public init(withView view: UIKit.UIView) + public func configureAsStickyView(withParentView parentView: UIKit.UIView? = nil) + public func show(completion: JitsiMeet.AnimationCompletion? = nil) + public func hide(completion: JitsiMeet.AnimationCompletion? = nil) + public func enterPictureInPicture() + @objc public func exitPictureInPicture() + public func resetBounds(bounds: CoreGraphics.CGRect) + public func stopDragGesture() + open func configureExitPiPButton(target: Any, action: ObjectiveC.Selector) -> UIKit.UIButton + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc final public class JMCallKitProxy : ObjectiveC.NSObject { + @objc public static var enabled: Swift.Bool { + @objc get + @objc set + } + @objc public static func configureProvider(localizedName: Swift.String, ringtoneSound: Swift.String?, iconTemplateImageData: Foundation.Data?) + @objc public static func isProviderConfigured() -> Swift.Bool + @objc public static func addListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func removeListener(_ listener: JitsiMeet.JMCallKitListener) + @objc public static func hasActiveCallForUUID(_ callUUID: Swift.String) -> Swift.Bool + @objc public static func reportNewIncomingCall(UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc public static func reportCallUpdate(with UUID: Foundation.UUID, handle: Swift.String?, displayName: Swift.String?, hasVideo: Swift.Bool) + @objc public static func reportCall(with UUID: Foundation.UUID, endedAt dateEnded: Foundation.Date?, reason endedReason: CallKit.CXCallEndedReason) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, startedConnectingAt dateStartedConnecting: Foundation.Date?) + @objc public static func reportOutgoingCall(with UUID: Foundation.UUID, connectedAt dateConnected: Foundation.Date?) + @objc public static func request(_ transaction: CallKit.CXTransaction, completion: @escaping (Swift.Error?) -> Swift.Void) + @objc deinit +} +@objc public protocol JMCallKitListener : ObjectiveC.NSObjectProtocol { + @objc optional func providerDidReset() + @objc optional func performAnswerCall(UUID: Foundation.UUID) + @objc optional func performEndCall(UUID: Foundation.UUID) + @objc optional func performSetMutedCall(UUID: Foundation.UUID, isMuted: Swift.Bool) + @objc optional func performStartCall(UUID: Foundation.UUID, isVideo: Swift.Bool) + @objc optional func providerDidActivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerDidDeactivateAudioSession(session: AVFoundation.AVAudioSession) + @objc optional func providerTimedOutPerformingAction(action: CallKit.CXAction) +} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Equatable {} +extension JitsiMeet.PiPViewCoordinator.Position : Swift.Hashable {} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftmodule b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftmodule index 639af5677..39e068449 100644 Binary files a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftmodule and b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/Modules/JitsiMeet.swiftmodule/x86_64.swiftmodule differ diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-bg.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-bg.json index 6f2b2d780..555a28513 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-bg.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-bg.json @@ -1,27 +1,32 @@ { "en": "Английски", "af": "Африканс", - "az": "Азербайджански", "bg": "Български", + "ca": "Каталонски", "cs": "Чешки", "de": "Немски", "el": "Гръцки", + "enGB": "Английски (Великобритания)", "eo": "Есперанто", "es": "Испански", + "esUS": "Испански (Латинска Америка)", + "fi": "Фински", "fr": "Френски", + "frCA": "Френски (Канада)", + "hr": "Хърватски", + "hu": "Унгарски", "hy": "Арменски", "it": "Италиански", "ja": "Японски", "ko": "Корейски", - "nb": "Норвежки букмол", + "nl": "Нидерландски", "oc": "Окситански", "pl": "Полски", "ptBR": "Португалски (Бразилия)", "ru": "Руски", - "sk": "Словашки", - "sl": "Словенски", "sv": "Шведски", "tr": "Турски", "vi": "Виетнамски", - "zhCN": "Китайски (Китай)" + "zhCN": "Китайски (Китай)", + "zhTW": "Тайвански" } \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ca.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ca.json new file mode 100644 index 000000000..7b8e02384 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ca.json @@ -0,0 +1,34 @@ +{ + "en": "Anglès", + "af": "Afrikaans", + "bg": "Búlgar", + "ca": "Català", + "cs": "Txec", + "da": "Danès", + "de": "Alemany", + "el": "Grec", + "enGB": "Anglès (Regne Unit)", + "eo": "Esperanto", + "es": "Espanyol", + "esUS": "Espanyol (Amèrica llatina)", + "fi": "Finès", + "fr": "Francès", + "frCA": "Francès (Canadà)", + "hr": "Croat", + "hu": "Hongarès", + "hy": "Armeni", + "it": "Italià", + "ja": "Japonès", + "ko": "Coreà", + "nl": "Neerlandès", + "oc": "Occità", + "pl": "Polonès", + "ptBR": "Portuguès (Brasil)", + "ru": "Rus", + "sv": "Suec", + "tr": "Turc", + "vi": "Vietnamita", + "zhCN": "Xinès (Xina)", + "zhTW": "Xinès (Taiwan)", + "et": "Estonià" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-de.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-de.json index 13e1fc71c..d35a7652b 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-de.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-de.json @@ -1,18 +1,18 @@ { "en": "Englisch", - "af": "", - "az": "", + "af": "Afrikaans", + "az": "Aserbaidschanisch", "bg": "Bulgarisch", - "cs": "", + "cs": "Tschechisch", "de": "Deutsch", - "el": "", + "el": "Griechisch", "eo": "Esperanto", "es": "Spanisch", "fr": "Französisch", "hy": "Armenisch", "it": "Italienisch", - "ja": "", - "ko": "", + "ja": "Japanisch", + "ko": "Koreanisch", "nb": "Norwegisch (Bokmal)", "oc": "Okzitanisch", "pl": "Polnisch", @@ -22,6 +22,17 @@ "sl": "Slowenisch", "sv": "Schwedisch", "tr": "Türkisch", - "vi": "", - "zhCN": "Chinesisch (China)" -} \ No newline at end of file + "vi": "Vietnamesisch", + "zhCN": "Chinesisch (China)", + "zhTW": "Chinesisch (Taiwan)", + "nl": "Niederländisch", + "hu": "Ungarisch", + "hr": "Kroatisch", + "frCA": "Französisch (Kanada)", + "fi": "Finnisch", + "et": "Estnisch", + "esUS": "Spanisch (Lateinamerika)", + "enGB": "Englisch (Vereinigtes Königreich)", + "da": "Dänisch", + "ca": "Katalanisch" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-enGB.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-enGB.json index 89fb1ac95..f59913fa9 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-enGB.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-enGB.json @@ -1,27 +1,38 @@ { - "en": "", - "af": "", + "en": "English", + "af": "Afrikaans", "az": "", - "bg": "", - "cs": "", - "de": "", - "el": "", - "eo": "", - "es": "", - "fr": "", - "hy": "", - "it": "", - "ja": "", - "ko": "", + "bg": "Bulgarian", + "cs": "Czech", + "de": "German", + "el": "Greek", + "eo": "Esperanto", + "es": "Spanish", + "fr": "French", + "hy": "Armenian", + "it": "Italian", + "ja": "Japanese", + "ko": "Korean", "nb": "", - "oc": "", - "pl": "", - "ptBR": "", - "ru": "", + "oc": "Occitan", + "pl": "Polish", + "ptBR": "Portuguese (Brazil)", + "ru": "Russian", "sk": "", "sl": "", - "sv": "", - "tr": "", - "vi": "", - "zhCN": "" -} \ No newline at end of file + "sv": "Swedish", + "tr": "Turkish", + "vi": "Vietnamese", + "zhCN": "Chinese (China)", + "zhTW": "Chinese (Taiwan)", + "nl": "Dutch", + "hu": "Hungarian", + "hr": "Croatian", + "frCA": "French (Canadian)", + "fi": "Finnish", + "et": "Estonian", + "esUS": "Spanish (Latin America)", + "enGB": "English (United Kingdom)", + "da": "Danish", + "ca": "Catalan" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-eo.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-eo.json index 265118421..8b28ed3d7 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-eo.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-eo.json @@ -1,18 +1,18 @@ { "en": "Angla", - "af": "", - "az": "", + "af": "Afrikansa", + "az": "Azera", "bg": "Bulgara", - "cs": "", + "cs": "Ĉeĥa", "de": "Germana", - "el": "", + "el": "Greka", "eo": "Esperanto", "es": "Hispana", "fr": "Franca", "hy": "Armena", "it": "Itala", - "ja": "", - "ko": "", + "ja": "Japana", + "ko": "Korea", "nb": "Norvega (Bukmola)", "oc": "Okcitana", "pl": "Pola", @@ -22,6 +22,6 @@ "sl": "Slovena", "sv": "Sveda", "tr": "Turka", - "vi": "", + "vi": "Vjetnama", "zhCN": "Ĉina (Ĉinuja)" -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-es.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-es.json index cb1f05719..cdecbad87 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-es.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-es.json @@ -1,27 +1,36 @@ { "en": "Inglés", - "af": "Africano", - "az": "Azerbaijani", + "af": "Afrikáans", "bg": "Búlgaro", - "cs": "Czech", + "ca": "Catalán", + "cs": "Checo", "de": "Alemán", "el": "Griego", + "enGB": "Inglés (Reino Unido)", "eo": "Esperanto", "es": "Español", + "esUS": "Español (América Latina)", + "fi": "Finlandés", "fr": "Francés", + "frCA": "Francés (Canadiense)", + "he": "Hebreo", + "hr": "Croata", + "hu": "Húngaro", "hy": "Armenio", "it": "Italiano", - "ja": "Jopones", + "ja": "Japonés", "ko": "Coreano", - "nb": "Noruego (bokmal)", + "nl": "Holandés", "oc": "Occitano", "pl": "Polaco", "ptBR": "Portugués (Brasil)", "ru": "Ruso", "sk": "Eslovaco", - "sl": "Esloveno", "sv": "Sueco", "tr": "Turco", "vi": "Vietnamita", - "zhCN": "Chino (China)" -} \ No newline at end of file + "zhCN": "Chino (China)", + "zhTW": "Chino (Taiwán)", + "et": "Estonio", + "da": "Danés" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-esUS.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-esUS.json index 89fb1ac95..c9bc73bbf 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-esUS.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-esUS.json @@ -1,27 +1,34 @@ { - "en": "", - "af": "", - "az": "", - "bg": "", - "cs": "", - "de": "", - "el": "", - "eo": "", - "es": "", - "fr": "", - "hy": "", - "it": "", - "ja": "", - "ko": "", - "nb": "", - "oc": "", - "pl": "", - "ptBR": "", - "ru": "", - "sk": "", - "sl": "", - "sv": "", - "tr": "", - "vi": "", - "zhCN": "" -} \ No newline at end of file + "en": "Inglés", + "af": "Africano", + "bg": "Búlgaro", + "ca": "Catalán", + "cs": "Checo", + "de": "Alemán", + "el": "Griego", + "enGB": "Inglés", + "eo": "Esperanto", + "es": "Español", + "esUS": "Español (Latinoamérica)", + "fi": "Finlandés", + "fr": "Francés", + "frCA": "Francés (Canadiense)", + "he": "Hebreo", + "hr": "Croata", + "hu": "Húngaro", + "hy": "Armenio", + "it": "Italiano", + "ja": "Japonés", + "ko": "Coreano", + "nl": "Holandés", + "oc": "Occitano", + "pl": "Polaco", + "ptBR": "Portugués (Brasil)", + "ru": "Ruso", + "sk": "Eslovaco", + "sv": "Sueco", + "tr": "Turco", + "vi": "Vietnamita", + "zhCN": "Chino (China)", + "zhTW": "Chino (Taiwan)" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-et.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-et.json new file mode 100644 index 000000000..d87fbb7b2 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-et.json @@ -0,0 +1,33 @@ +{ + "en": "Inglise keel", + "af": "Afrikaani keel", + "bg": "Bulgaaria keel", + "ca": "Katalaani keel", + "cs": "Tšehhi keel", + "de": "Saksa keel", + "el": "Kreeka keel", + "enGB": "Inglise keel (Ühendkuningriik)", + "eo": "Esperanto keel", + "es": "Hispaania keel", + "esUS": "Hispaania keel (Ladina-Ameerika)", + "et": "Eesti keel", + "fi": "Soome keel", + "fr": "Prantsuse keel", + "frCA": "Prantsuse keel (Kanada)", + "hr": "Horvaadi keel", + "hu": "Ungari keel", + "hy": "Armeenia keel", + "it": "Itaalia keel", + "ja": "Jaapani keel", + "ko": "Korea keel", + "nl": "Hollandi keel", + "oc": "Oksitaani keel", + "pl": "Poola keel", + "ptBR": "Portigali keel (Brasiilia)", + "ru": "Vene keel", + "sv": "Roosi keel", + "tr": "Türgi keel", + "vi": "Vietnami keel", + "zhCN": "Hiina keel (Hiina)", + "zhTW": "Hiina keel (Tai)" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fi.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fi.json index 89fb1ac95..75c29e231 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fi.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fi.json @@ -1,27 +1,38 @@ { - "en": "", - "af": "", + "en": "englanti", + "af": "afrikaans", "az": "", - "bg": "", - "cs": "", - "de": "", - "el": "", - "eo": "", - "es": "", - "fr": "", - "hy": "", - "it": "", - "ja": "", - "ko": "", + "bg": "bulgaria", + "cs": "tšekki", + "de": "saksa", + "el": "kreikka", + "eo": "esperanto", + "es": "espanja", + "fr": "ranska", + "hy": "armenia", + "it": "italia", + "ja": "japani", + "ko": "korea", "nb": "", - "oc": "", - "pl": "", - "ptBR": "", - "ru": "", + "oc": "oksitaani", + "pl": "puola", + "ptBR": "portugali (Brasilia)", + "ru": "venäjä", "sk": "", "sl": "", - "sv": "", - "tr": "", - "vi": "", - "zhCN": "" -} \ No newline at end of file + "sv": "ruotsi", + "tr": "turkki", + "vi": "vietnam", + "zhCN": "kiina (Kiina)", + "zhTW": "kiina (Taiwan)", + "nl": "hollanti", + "hu": "unkari", + "hr": "kroaatti", + "frCA": "ranska (Kanada)", + "fi": "suomi", + "et": "viro", + "esUS": "espanja (Latinalainen Amerikka)", + "enGB": "englanti (Yhdistynyt kuningaskunta)", + "da": "tanska", + "ca": "katalaani" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fr.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fr.json index 0620b9d39..9624c7f4b 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fr.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-fr.json @@ -6,14 +6,15 @@ "cs": "Tchèque", "de": "Allemand", "el": "Grec", - "enGB": "Anglais (Royaume-Uni) ", + "enGB": "Anglais (Royaume-Uni)", "eo": "Espéranto", "es": "Espagnol", "esUS": "Espagnol (Amérique latine)", - "fi": "Finlandais", + "fi": "Finnois", "fr": "Français", - "frCA": "Français (Canadien)", + "frCA": "Français (Canada)", "hr": "Croate", + "hu": "Hongrois", "hy": "Arménien", "it": "Italien", "ja": "Japonais", @@ -23,9 +24,12 @@ "pl": "Polonais", "ptBR": "Portugais (Brésil)", "ru": "Russe", + "sk": "Slovaque", "sv": "Suédois", "tr": "Turc", "vi": "Vietnamien", "zhCN": "Chinois (Chine)", - "zhTW": "Chinois (Taiwan)" -} \ No newline at end of file + "zhTW": "Chinois (Taiwan)", + "et": "Estonien", + "da": "Danois" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-frCA.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-frCA.json index 89fb1ac95..f0a3960fd 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-frCA.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-frCA.json @@ -1,27 +1,35 @@ { - "en": "", - "af": "", - "az": "", - "bg": "", - "cs": "", - "de": "", - "el": "", - "eo": "", - "es": "", - "fr": "", - "hy": "", - "it": "", - "ja": "", - "ko": "", - "nb": "", - "oc": "", - "pl": "", - "ptBR": "", - "ru": "", - "sk": "", - "sl": "", - "sv": "", - "tr": "", - "vi": "", - "zhCN": "" -} \ No newline at end of file + "en": "Anglais", + "af": "Africain", + "bg": "Bulgare", + "ca": "Catalan", + "cs": "Tchèque", + "de": "Allemand", + "el": "Grec", + "enGB": "Anglais (Royaume-Uni)", + "eo": "Espéranto", + "es": "Espagnol", + "esUS": "Espagnol (Amérique latine)", + "fi": "Finnois", + "fr": "Français", + "frCA": "Français (Canadien)", + "hr": "Croate", + "hu": "Hongrois", + "hy": "Arménien", + "it": "Italien", + "ja": "Japonais", + "ko": "Coréen", + "nl": "Néerlandais", + "oc": "Occitan", + "pl": "Polonais", + "ptBR": "Portugais (Brésil)", + "ru": "Russe", + "sk": "Slovaque", + "sv": "Suédois", + "tr": "Turc", + "vi": "Vietnamien", + "zhCN": "Chinois (Chine)", + "zhTW": "Chinois (Taiwan)", + "et": "Estonien", + "da": "Danois" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-hu.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-hu.json new file mode 100644 index 000000000..d40d1dfb8 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-hu.json @@ -0,0 +1,35 @@ +{ + "en": "Angol", + "af": "Afrikaans", + "bg": "Bolgár", + "ca": "Katalán", + "cs": "Cseh", + "de": "Német", + "el": "Görög", + "enGB": "Angol (Egyesült Királyság)", + "eo": "Eszperantó", + "es": "Spanyol", + "esUS": "Spanyol (Latin-Amerika)", + "fi": "Finn", + "fr": "Francia", + "frCA": "Francia (kanadai)", + "hr": "Horvát", + "hu": "Magyar", + "hy": "Örmény", + "it": "Olasz", + "ja": "Japán", + "ko": "Koreai", + "nl": "Holland", + "oc": "Okszitán", + "pl": "Lengyel", + "ptBR": "Portugál (Brazil)", + "ru": "Orosz", + "sk": "Szlovákul", + "sv": "Svéd", + "tr": "Török", + "vi": "Vietnámi", + "zhCN": "Kínai (Kína)", + "zhTW": "Kínai (Tajvan)", + "et": "Észt", + "da": "Dán" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-it.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-it.json index 8f1a6bb3a..218e97a66 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-it.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-it.json @@ -1,6 +1,6 @@ { "en": "Inglese", - "af": "", + "af": "Afrikaans", "az": "Azero", "bg": "Bulgaro", "cs": "Ceco", @@ -23,5 +23,16 @@ "sv": "Svedese", "tr": "Turco", "vi": "Vietnamita", - "zhCN": "Cinese (Cina)" -} \ No newline at end of file + "zhCN": "Cinese (Cina)", + "enGB": "Inglese (Regno Unito)", + "da": "Danese", + "ca": "Catalano", + "zhTW": "", + "nl": "", + "hu": "", + "hr": "", + "frCA": "", + "fi": "", + "et": "", + "esUS": "" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-mn.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-mn.json new file mode 100644 index 000000000..de7fbe577 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-mn.json @@ -0,0 +1,35 @@ +{ + "en": "English", + "af": "Afrikaans", + "bg": "Bulgarian", + "ca": "Catalan", + "cs": "Czech", + "da": "Danish", + "de": "German", + "el": "Greek", + "enGB": "English (United Kingdom)", + "eo": "Esperanto", + "es": "Spanish", + "esUS": "Spanish (Latin America)", + "et": "Estonian", + "fi": "Finnish", + "fr": "French", + "frCA": "French (Canadian)", + "hr": "Croatian", + "hu": "Hungarian", + "hy": "Armenian", + "it": "Italian", + "ja": "Japanese", + "ko": "Korean", + "nl": "Dutch", + "oc": "Occitan", + "pl": "Polish", + "ptBR": "Portuguese (Brazil)", + "ru": "Russian", + "sv": "Swedish", + "tr": "Turkish", + "vi": "Vietnamese", + "zhCN": "Chinese (China)", + "zhTW": "Chinese (Taiwan)", + "mn": "Монгол" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-nl.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-nl.json index 89fb1ac95..a692dc92f 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-nl.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-nl.json @@ -1,27 +1,38 @@ { - "en": "", - "af": "", - "az": "", - "bg": "", - "cs": "", - "de": "", - "el": "", - "eo": "", - "es": "", - "fr": "", - "hy": "", - "it": "", - "ja": "", - "ko": "", - "nb": "", - "oc": "", - "pl": "", - "ptBR": "", - "ru": "", - "sk": "", - "sl": "", - "sv": "", - "tr": "", - "vi": "", - "zhCN": "" -} \ No newline at end of file + "en": "Engels", + "af": "Afrikaans", + "az": "Azerbeidzjaans", + "bg": "Bulgaars", + "ca": "Catalaans", + "cs": "Tsjechisch", + "da": "Deens", + "de": "Duits", + "enGB": "Engels (Verenigd Koninkrijk)", + "et": "Estlands", + "el": "Grieks", + "eo": "Esperanto", + "es": "Spaans", + "esUS": "Spaans (Latijns Amerika)", + "fi": "Fins", + "fr": "Frans", + "frCA": "Frans (Canadees)", + "hr": "Kroatisch", + "hu": "Hongaars", + "hy": "Armeens", + "it": "Italiaans", + "ja": "Japans", + "ko": "Koreaans", + "nb": "Noors (Bokmal)", + "nl": "Nederlands", + "oc": "Occitaans", + "pl": "Pools", + "ptBR": "Portugees (Brazilië)", + "ru": "Russisch", + "sk": "Slowaaks", + "sl": "Sloveens", + "sv": "Zweeds", + "tr": "Turks", + "vi": "Vietnamees", + "zhCN": "Chinees (China)", + "zhTW": "Chinees (Taiwan)" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-oc.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-oc.json index 40f2129e2..1c343a6d7 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-oc.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-oc.json @@ -2,23 +2,24 @@ "en": "Anglés", "af": "Afrikaans", "bg": "Bulgar", - "ca": "", + "ca": "Catalan", "cs": "Chèc", "de": "Aleman", "el": "Grèc", - "enGB": "", + "enGB": "Anglés (Reialme Unit)", "eo": "Esperanto", "es": "Castelhan", - "esUS": "", - "fi": "", + "esUS": "Espanhòl (America latina)", + "fi": "Finés", "fr": "Francés", - "frCA": "", - "hr": "", + "frCA": "Francés (Canadian)", + "hr": "Croat", + "hu": "Ongrés", "hy": "Armenian", "it": "Italian", "ja": "Japonés", "ko": "Corean", - "nl": "", + "nl": "Neerlandés", "oc": "Occitan", "pl": "Polonés", "ptBR": "Portugués (Brasil)", @@ -27,5 +28,7 @@ "tr": "Turc", "vi": "Vietnamian", "zhCN": "Chinés (China)", - "zhTW": "" -} \ No newline at end of file + "zhTW": "Chinés (Taiwan)", + "et": "Estonian", + "da": "Danés" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-pl.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-pl.json index 90a2bdbb8..20f20a443 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-pl.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-pl.json @@ -1,27 +1,33 @@ { - "en": "Anglik", - "af": "", - "az": "Azerski", - "bg": "Bułgarski", - "cs": "Czeski", - "de": "Niemiecki", - "el": "Grecki", - "eo": "Esperanto", - "es": "Hiszpański", - "fr": "Francuski", - "hy": "Ormiański", - "it": "Włoski", - "ja": "Japoński", - "ko": "Koreański", - "nb": "Norweski Bokmal", - "oc": "Oksytański", - "pl": "Polski", + "en": "angielski", + "af": "afrykanerski", + "bg": "bułgarski", + "ca": "kataloński", + "cs": "czeski", + "de": "niemiecki", + "el": "grecki", + "enGB": "angielski (Zjednoczone Królestwo)", + "eo": "esperanto", + "es": "hiszpański", + "esUS": "hiszpański (Ameryka Łacińska)", + "fi": "fiński", + "fr": "francuski", + "frCA": "francuski (kanadyjski)", + "hr": "chorwacki", + "hu": "węgierski", + "hy": "ormiański", + "it": "włoski", + "ja": "japoński", + "ko": "koreański", + "nl": "holenderski", + "oc": "oksytański", + "pl": "polski", "ptBR": "portugalski (brazylijski)", - "ru": "Rosyjski", - "sk": "Słowacki", - "sl": "Słoweński", - "sv": "Szwedzki", - "tr": "Turecki", - "vi": "Wietnamski", - "zhCN": "Chiński (Chiny)" + "ru": "rosyjski", + "sk": "słowacki", + "sv": "szwedzki", + "tr": "turecki", + "vi": "wietnamski", + "zhCN": "chiński (Chiny)", + "zhTW": "chiński (Tajwan)" } \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ptBR.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ptBR.json index 7eecf4cb5..5bdb5ca3a 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ptBR.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ptBR.json @@ -1,27 +1,32 @@ { "en": "Inglês", "af": "Africâner", - "az": "Azerbaijanês", "bg": "Búlgaro", + "ca": "Catalão", "cs": "Checo", "de": "Alemão", "el": "Grego", + "enGB": "Inglês (Reino Unido)", "eo": "Esperanto", "es": "Espanhol", + "esUS": "Espanhol (América Latina)", + "fi": "Finlandês", "fr": "Francês", + "frCA": "Francês (Canadá)", + "hr": "Croata", + "hu": "Húngaro", "hy": "Armênio", "it": "Italiano", "ja": "Japonês", "ko": "Coreano", - "nb": "Bokmal norueguês", + "nl": "Holandês", "oc": "Occitano", "pl": "Polonês", "ptBR": "Português (Brasil)", "ru": "Russo", - "sk": "Eslovaco", - "sl": "Esloveno", "sv": "Sueco", "tr": "Turco", "vi": "Vietnamita", - "zhCN": "Chinês (China)" + "zhCN": "Chinês (China)", + "zhTW": "Chinês (Taiwan)" } \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ru.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ru.json index 2c543aacf..0e7266fb1 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ru.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-ru.json @@ -1,24 +1,25 @@ { "en": "Английский", - "af": "", + "af": "Африкаанс", "bg": "Болгарский", - "ca": "", + "ca": "Каталонский", "cs": "Чешский", "de": "Немецкий", "el": "Греческий", - "enGB": "", + "enGB": "Английский (Великобритания)", "eo": "Эсперанто", "es": "Испанский", - "esUS": "", - "fi": "", + "esUS": "Испанский (Латинская Америка)", + "fi": "Финский", "fr": "Французский", - "frCA": "", - "hr": "", + "frCA": "Французский (канадский)", + "hr": "Хорватский", + "hu": "Венгерский", "hy": "Армянский", "it": "Итальянский", "ja": "Японский", "ko": "Корейский", - "nl": "", + "nl": "Голландский", "oc": "Окситанский", "pl": "Польский", "ptBR": "Португальский (Бразилия)", @@ -27,5 +28,7 @@ "tr": "Турецкий", "vi": "Вьетнамский", "zhCN": "Китайский (Китай)", - "zhTW": "" -} \ No newline at end of file + "zhTW": "Китайский (Тайвань)", + "et": "Эстонский", + "da": "Датский" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sc.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sc.json new file mode 100644 index 000000000..641d95721 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sc.json @@ -0,0 +1,34 @@ +{ + "en": "Inglesu", + "af": "Afrikaans", + "bg": "Bùlgaru", + "ca": "Catalanu", + "cs": "Tzecu", + "da": "Danesu", + "de": "Tedescu", + "el": "Gregu", + "enGB": "Inglesu (Rennu Unidu)", + "eo": "Esperanto", + "es": "Castillianu", + "esUS": "Castillianu (Amèrica de su Sud)", + "fi": "Finlandesu", + "fr": "Frantzesu", + "frCA": "Frantzesu (Canadesu)", + "hr": "Croatu", + "hu": "Ungheresu", + "hy": "Armenu", + "it": "Italianu", + "ja": "Giaponesu", + "ko": "Coreanu", + "nl": "Olandesu", + "oc": "Otzitanu", + "pl": "Polacu", + "ptBR": "Portughesu (Brasile)", + "ru": "Russu", + "sc": "Sardu", + "sv": "Isvedesu", + "tr": "Turcu", + "vi": "Vietnamita", + "zhCN": "Tzinesu (Tzina)", + "zhTW": "Tzinesu (Taiwan)" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sk.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sk.json new file mode 100644 index 000000000..59ac50b17 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sk.json @@ -0,0 +1,35 @@ +{ + "en": "Angličtina", + "af": "", + "bg": "Bulharština", + "ca": "Katalánština", + "cs": "Čeština", + "da": "Dánština", + "de": "Nemčina", + "el": "Gréčtina", + "enGB": "Angličtina (Spojené kráľovstvo)", + "eo": "Esperanto", + "es": "Španielčina", + "esUS": "Angličtina (Spojené štáty americké)", + "et": "Estónčina", + "fi": "Fínčina", + "fr": "Francúžtina", + "frCA": "Francúžtina (Kanada)", + "hr": "Chorvátčina", + "hu": "Maďarčina", + "hy": "Arménčina", + "it": "Taliančina", + "ja": "Japončina", + "ko": "Kórejčina", + "nl": "Holandčina", + "oc": "Okcitánština", + "pl": "Polština", + "ptBR": "Portugalčina", + "ru": "Ruština", + "sk": "Slovenčina", + "sv": "Švédčina", + "tr": "Turečtina", + "vi": "Vietnamčina", + "zhCN": "Čínština (Čína)", + "zhTW": "Čínština (Taiwan)" +} \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sv.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sv.json index 9908c438a..685ed2532 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sv.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-sv.json @@ -1,18 +1,18 @@ { "en": "Engelska", - "af": "", + "af": "Afrikaans", "az": "", "bg": "Bulgariska", - "cs": "", + "cs": "Tjeckiska", "de": "Tyska", - "el": "", + "el": "Grekiska", "eo": "Esperanto", "es": "Spanska", "fr": "Franska", "hy": "Armeniska", "it": "Italienska", - "ja": "", - "ko": "", + "ja": "Japanska", + "ko": "Koreanska", "nb": "Norska (Bokmål)", "oc": "Occitanska", "pl": "Polska", @@ -22,6 +22,17 @@ "sl": "Slovenska", "sv": "Svenska", "tr": "Turkiska", - "vi": "", - "zhCN": "Kinesiska (Kina)" -} \ No newline at end of file + "vi": "Vietnamesiska", + "zhCN": "Kinesiska (Kina)", + "zhTW": "Kinesiska (Taiwan)", + "nl": "Nederländska", + "hu": "Ungerska", + "hr": "Kroatiska", + "frCA": "Franska (Kanada)", + "fi": "Finska", + "et": "Estniska", + "esUS": "Spanska (Latinamerika)", + "enGB": "Engelska (Storbritannien)", + "da": "Danska", + "ca": "Katalanska" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-tr.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-tr.json new file mode 100644 index 000000000..f0fe0e529 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-tr.json @@ -0,0 +1,38 @@ +{ + "en": "İngilizce", + "af": "Afrikanca", + "az": "", + "bg": "Bulgarca", + "cs": "Çekçe", + "de": "Almanca", + "el": "Yunanca", + "eo": "Esperanto", + "es": "İspanyolca", + "fr": "Fransızca", + "hy": "Ermenice", + "it": "İtalyanca", + "ja": "Japonca", + "ko": "Korece", + "nb": "Norveççe Bokmal", + "oc": "Oksitan dili", + "pl": "Lehçe", + "ptBR": "Portekizce (Brezilya)", + "ru": "Rusça", + "sk": "Slovakça", + "sl": "Slovence", + "sv": "Isveççe", + "tr": "Türkçe", + "vi": "Vietnamca", + "zhCN": "Çince (Çin)", + "zhTW": "Çince (Tayvan)", + "nl": "Flemenkçe", + "hu": "Macarca", + "hr": "Hırvatça", + "frCA": "Fransızca (Kanada)", + "fi": "Fince", + "et": "Estonca", + "esUS": "İspanyolca (Latin Amerika)", + "enGB": "İngilizce (Birleşik Krallık)", + "da": "Danca", + "ca": "Katalanca" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhCN.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhCN.json index 25fb3740d..3c1639479 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhCN.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhCN.json @@ -1,27 +1,32 @@ { "en": "英语", "af": "南非荷兰语", - "az": "阿塞拜疆语", "bg": "保加利亚语", + "ca": "加泰罗尼亚语", "cs": "捷克语", "de": "德语", "el": "希腊语", + "enGB": "英语(英国)", "eo": "世界语", "es": "西班牙语", + "esUS": "西班牙语(拉丁美洲)", + "fi": "芬兰语", "fr": "法语", + "frCA": "法语(加拿大)", + "hr": "克罗地亚语", + "hu": "匈牙利语", "hy": "亚美尼亚语", "it": "意大利语", "ja": "日语", "ko": "韩语", - "nb": "挪威布克摩尔语", + "nl": "荷兰语", "oc": "欧西坦语", "pl": "波兰语", "ptBR": "葡萄牙语(巴西)", "ru": "俄语", - "sk": "斯洛伐克语", - "sl": "斯洛文尼亚语", "sv": "瑞典语", "tr": "土耳其语", "vi": "越南语", - "zhCN": "中文(中国)" + "zhCN": "中文(中国)", + "zhTW": "中文(台湾)" } \ No newline at end of file diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhTW.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhTW.json index 744c66fad..f8f684ae5 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhTW.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages-zhTW.json @@ -1,27 +1,34 @@ { - "en": "English", - "af": "", - "az": "Azerbaijani", - "bg": "Bulgarian", - "cs": "Czech", - "de": "German", - "el": "Greek", - "eo": "Esperanto", - "es": "Spanish", - "fr": "French", - "hy": "Armenian", - "it": "Italian", - "ja": "日本語", + "en": "英語", + "af": "南非荷蘭文", + "bg": "保加利亞文", + "ca": "卡達隆尼亞文", + "cs": "捷克文", + "de": "德文", + "el": "希臘文", + "enGB": "英文 (英國)", + "eo": "世界語", + "es": "西班牙文", + "esUS": "西班牙文 (拉丁美洲)", + "fi": "芬蘭文", + "fr": "法文", + "frCA": "法文 (加拿大)", + "hr": "克羅埃西亞文", + "hu": "匈牙利文", + "hy": "亞美尼亞文", + "it": "義大利文", + "ja": "日語", "ko": "韓文", - "nb": "Norwegian Bokmal", - "oc": "Occitan", - "pl": "Polish", - "ptBR": "Portuguese (Brazil)", - "ru": "Russian", - "sk": "Slovak", - "sl": "Slovenian", - "sv": "Swedish", - "tr": "Turkish", - "vi": "Vietnamese", - "zhCN": "中文 简体 (中国)" -} \ No newline at end of file + "nl": "荷蘭文", + "oc": "奧西坦文", + "pl": "波蘭文", + "ptBR": "葡萄牙文 (巴西)", + "ru": "俄文", + "sv": "瑞典文", + "tr": "土耳其文", + "vi": "越南文", + "zhCN": "中文 (中国,简体)", + "zhTW": "中文 (台灣,正體)", + "et": "愛沙尼亞文", + "da": "丹麥文" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages.json index 998cd4560..805a796ea 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/languages.json @@ -4,27 +4,38 @@ "bg": "Bulgarian", "ca": "Catalan", "cs": "Czech", + "da": "Danish", "de": "German", "el": "Greek", "enGB": "English (United Kingdom)", "eo": "Esperanto", "es": "Spanish", "esUS": "Spanish (Latin America)", + "et": "Estonian", + "eu": "Basque", "fi": "Finnish", "fr": "French", "frCA": "French (Canadian)", + "he": "Hebrew", "hr": "Croatian", + "hu": "Hungarian", "hy": "Armenian", + "id": "Indonesian", "it": "Italian", "ja": "Japanese", "ko": "Korean", + "lt": "Lithuanian", "nl": "Dutch", "oc": "Occitan", "pl": "Polish", "ptBR": "Portuguese (Brazil)", "ru": "Russian", + "sc": "Sardinian", + "sk": "Slovak", "sv": "Swedish", + "th": "Thailand", "tr": "Turkish", + "uk": "Ukrainian", "vi": "Vietnamese", "zhCN": "Chinese (China)", "zhTW": "Chinese (Taiwan)" diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-af.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-af.json index 439b29cd9..cf12f8603 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-af.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-af.json @@ -145,9 +145,9 @@ "cameraUnsupportedResolutionError": "Die kamera ondersteun nie die nodige videoresolusie nie.", "Cancel": "Kanselleer", "close": "Sluit", - "conferenceDisconnectMsg": "Kontroleer dalk die netwerkverbinding. Gaan oor {{seconds}} sekondes weer koppel...", + "conferenceDisconnectMsg": "Kontroleer dalk die netwerkverbinding. Gaan oor {{seconds}} sekondes weer koppel…", "conferenceDisconnectTitle": "Die verbinding is verbreek.", - "conferenceReloadMsg": "Ons probeer om dit reg te stel. Gaan herkoppel oor {{seconds}} sekondes...", + "conferenceReloadMsg": "Ons probeer om dit reg te stel. Gaan herkoppel oor {{seconds}} sekondes…", "conferenceReloadTitle": "Iets het ongelukkig skeefgeloop.", "confirm": "Bevestig", "confirmNo": "Nee", @@ -254,7 +254,7 @@ "userPassword": "gebruikerwagwoord", "WaitForHostMsg": "", "WaitForHostMsgWOk": "", - "WaitingForHost": "Wag tans vir die gasheer ...", + "WaitingForHost": "Wag tans vir die gasheer …", "Yes": "Ja", "yourEntireScreen": "U hele skerm" }, @@ -349,13 +349,13 @@ "errorLiveStreamNotEnabled": "Regstreekse stroom is nie geaktiveer op {{email}} nie. Aktiveer asb. regstreekse strome of meld aan met ’n rekening met regstreekse strome geaktiveer.", "expandedOff": "Die regstreekse stroom het gestop", "expandedOn": "Die vergadering word tans gestroom na YouTube.", - "expandedPending": "Die regstreekse stroom begin tans...", + "expandedPending": "Die regstreekse stroom begin tans…", "failedToStart": "Regstreekse stroom kon nie begin nie", "getStreamKeyManually": "", "invalidStreamKey": "", "off": "Regstreekse stroom het gestop", "on": "Regstreekse stroom", - "pending": "Begin tans regstreekse stroom...", + "pending": "Begin tans regstreekse stroom…", "serviceName": "Regstreekse stroomdiens", "signedInAs": "U is tans aangemeld as:", "signIn": "Meld aan met Google", @@ -433,22 +433,22 @@ "poweredby": "aangedryf deur", "presenceStatus": { "busy": "Besig", - "calling": "Bel tans...", + "calling": "Bel tans…", "connected": "Gekoppel", - "connecting": "Koppel tans...", + "connecting": "Koppel tans…", "connecting2": "Koppel tans*...", "disconnected": "Ontkoppeld", "expired": "Verval", "ignored": "Geïgnoreer", - "initializingCall": "Inisialiseer tans oproep...", + "initializingCall": "Inisialiseer tans oproep…", "invited": "Uitgenooi", "rejected": "Geweier", - "ringing": "Lui tans..." + "ringing": "Lui tans…" }, "profile": { "setDisplayNameLabel": "Stel u vertoonnaam", "setEmailInput": "Gee e-posadres", - "setEmailLabel": "Stel u gravatar-e-posadres", + "setEmailLabel": "Stel u Gravatar-e-posadres", "title": "Profiel" }, "recording": { @@ -460,14 +460,14 @@ "error": "Opname het misluk. Probeer gerus weer.", "expandedOff": "Opname het gestop", "expandedOn": "Die vergadering word tans opgeneem.", - "expandedPending": "Opname word begin...", + "expandedPending": "Opname word begin…", "failedToStart": "Kon nie begin opneem nie", "fileSharingdescription": "", "live": "", "loggedIn": "Aangemeld as {{name}}", "off": "Opname gestop", "on": "Neem tans op", - "pending": "Berei voor om vergadering op te neem...", + "pending": "Berei voor om vergadering op te neem…", "rec": "", "serviceDescription": "", "serviceName": "Opneemdiens", @@ -563,7 +563,7 @@ "recording": "Wissel opname", "remoteMute": "", "Settings": "Wissel instellings", - "sharedvideo": "Wissel Youtube-videodeling", + "sharedvideo": "Wissel YouTube-videodeling", "shareRoom": "Nooi iemand", "shareYourScreen": "Wissel skermdeling", "shortcuts": "Wissel kortpaaie", @@ -625,7 +625,7 @@ "failedToStart": "", "labelToolTip": "Die vergadering word getranskribeer", "off": "", - "pending": "Berei tans voor om die vergadering te transkribeer...", + "pending": "Berei tans voor om die vergadering te transkribeer…", "start": "", "stop": "", "tr": "" @@ -709,4 +709,4 @@ "terms": "Voorwaardes", "title": "Veilige en volledig gratis videokonferensies propvol funksionaliteit" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-bg.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-bg.json index 9bd2d3059..748569536 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-bg.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-bg.json @@ -4,57 +4,63 @@ "countryNotSupported": "Желаната дестинация не се поддържа.", "countryReminder": "Международно обаждане? Започнете номера с международният код!", "disabled": "Не можете да каните хора.", - "failedToAdd": "", - "footerText": "Изходящиите разговори не са разрешени.", - "loading": "Търсене на хора и телефонни номера.", + "failedToAdd": "Неуспешно добавяне на участници", + "footerText": "Изходящите разговори не са разрешени.", + "loading": "Търсене на хора и телефонни номера", "loadingNumber": "Валидиране на номера", "loadingPeople": "Търсене на хора", "noResults": "Няма резултати", "noValidNumbers": "Моля въведете телефонен номер", "searchNumbers": "Добавяне на номера", "searchPeople": "Търсене на хора", - "searchPeopleAndNumbers": "", - "telephone": "", - "title": "" + "searchPeopleAndNumbers": "Търсене на участници или добавяне с телефони номера", + "telephone": "Телефон: {{number}}", + "title": "Добавяне на участници в срещата" }, "audioDevices": { - "bluetooth": "", + "bluetooth": "Bluetooth", "headphones": "Слушалки", "phone": "Телефон", - "speaker": "Говорещ" + "speaker": "Говорещ", + "none": "Няма налични устройства за звук" }, "audioOnly": { - "audioOnly": "Само звук" + "audioOnly": "Нисък дебит" }, "calendarSync": { - "addMeetingURL": "", - "confirmAddLink": "", + "addMeetingURL": "Добавяне на връзка за среща", + "confirmAddLink": "Искате ли да добавите връзка към това събитие?", "error": { - "appConfiguration": "", - "generic": "", - "notSignedIn": "" + "appConfiguration": "Интеграцията с календара не е настроена.", + "generic": "Грешка, моля проверете настройката за календара или го обновете.", + "notSignedIn": "Грешка при идентификация за изтегляне на събития. Моля проверете настройките на календара и опитайте отново." }, - "join": "", - "joinTooltip": "", - "nextMeeting": "", - "noEvents": "", - "ongoingMeeting": "", - "permissionButton": "", - "permissionMessage": "", - "refresh": "", - "today": "" + "join": "Влизане", + "joinTooltip": "Влизане в срещата", + "nextMeeting": "следваща среща", + "noEvents": "Няма насрочени бъдещи събития.", + "ongoingMeeting": "настояща среща", + "permissionButton": "Отваряне на настройки", + "permissionMessage": "За показване на срещите ви е нужно позволение за ползване на календара.", + "refresh": "Обновяване на календара", + "today": "Днес" }, "chat": { - "error": "", - "messagebox": "", + "error": "Грешка: вашето съобщение не бе изпратено, пради: {{error}}", + "fieldPlaceHolder": "Въведете съобщението", + "messagebox": "Въведете съобщение", + "messageTo": "Лично съобщение до {{recipient}}", + "noMessagesMessage": "Все още няма съобщения в срещата. Започнете разговор тук!", "nickname": { "popover": "Избор на име", - "title": "" + "title": "Въведете име за да обменяте съобщения" }, - "title": "" + "privateNotice": "Лично съобщение до {{recipient}}", + "title": "Текстови съобщения", + "you": "вие" }, "connectingOverlay": { - "joiningRoom": "" + "joiningRoom": "Свързване с вашата среща..." }, "connection": { "ATTACHED": "Прикрепен", @@ -66,14 +72,18 @@ "DISCONNECTED": "Изключен", "DISCONNECTING": "Прекъсване на връзката", "ERROR": "Грешка", - "RECONNECTING": "Появи се проблем с мрежата. Връзваме се наново..." + "RECONNECTING": "Появи се проблем с мрежата. Връзваме се наново...", + "LOW_BANDWIDTH": "Виеото на {{displayName}} беше изключено поради слаба Интернет връзка", + "GOT_SESSION_ID": "Отваряне на сесията...Завърши", + "GET_SESSION_ID_ERROR": "Грешка при отваряне на сесията: {{code}}", + "FETCH_SESSION_ID": "Отваряне на сесия..." }, "connectionindicator": { "address": "Адрес:", "bandwidth": "Предполагаема скорост:", "bitrate": "Скорост:", - "bridgeCount": "", - "connectedTo": "", + "bridgeCount": "Брой сървъри: ", + "connectedTo": "Свързан към:", "framerate": "Кадри в секунда:", "less": "Скриване", "localaddress": "Локален адрес:", @@ -96,26 +106,25 @@ "resolution": "Резолюция:", "status": "Връзка:", "transport": "Транспорт:", - "transport_plural": "Транспорти:", - "turn": " (обръщане)" + "transport_plural": "Транспорти:" }, "dateUtils": { - "earlier": "", - "today": "", - "yesterday": "" + "earlier": "По-рано", + "today": "Днес", + "yesterday": "Вчера" }, "deepLinking": { - "appNotInstalled": "", - "description": "", - "descriptionWithoutWeb": "", - "downloadApp": "Сваляне не приложението", - "launchWebButton": "", - "openApp": "", - "title": "", - "tryAgainButton": "" + "appNotInstalled": "Имате нужда от мобилното приложение {{app}} за влизане в тази среща от телефона.", + "description": "Нищо не се случва? Опитахме се да заредим срещата в приложението {{app}}. Пробвайте отново или влезте чрез уеб приложението {{app}}.", + "descriptionWithoutWeb": "Нищо не се случва? Опитахме се да заредим срещата в приложението {{app}}.", + "downloadApp": "Свалете приложението", + "launchWebButton": "Заредете уеб страницата", + "openApp": "Продължете към приложението", + "title": "Зареждане на срещата в {{app}}...", + "tryAgainButton": "Пробвайте отново" }, - "\u0005deepLinking": {}, "defaultLink": "напр. {{url}}", + "defaultNickname": "напр. Иван Иванов", "deviceError": { "cameraError": "Камерата е недостъпна", "cameraPermission": "Грешка при получаване на разрешение за достъп до камерата", @@ -126,14 +135,14 @@ "noPermission": "Не е получено разрешение", "previewUnavailable": "Няма възможност за преглед", "selectADevice": "Изберете устройство", - "testAudio": "" + "testAudio": "Пусни пробен звук" }, "dialog": { "accessibilityLabel": { "liveStreaming": "Излъчване на живо" }, "allow": "Разрешаване", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Друг участник вече е споделил видео. Тази среща позволява само едно споделено видео.", "alreadySharedVideoTitle": "Разрешено е споделянето само на едно видео в даден момент", "applicationWindow": "Прозореца на програмата", "Back": "Назад", @@ -150,8 +159,8 @@ "conferenceDisconnectTitle": "Връзката се разпадна.", "conferenceReloadMsg": "Опитваме се да оправим нещата. Повторно свързване след {{seconds}} сек…", "conferenceReloadTitle": "За съжаление, нещо се обърка.", - "confirm": "", - "confirmNo": "", + "confirm": "Потвърждение", + "confirmNo": "Не", "confirmYes": "Да", "connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.", "connectErrorWithMsg": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията: {{msg}}", @@ -159,66 +168,66 @@ "contactSupport": "Връзка с отдела по поддръжка", "copy": "Копиране", "dismiss": "Отхвърляне", - "displayNameRequired": "", + "displayNameRequired": "Здравей! Как се казваш?", "done": "Готово", - "enterDisplayName": "", + "enterDisplayName": "Моля въведете вашето име", "error": "Грешка", "externalInstallationMsg": "Трябва да инсталирате разширението за споделяне на екрана.", "externalInstallationTitle": "Нужно е разширение", "goToStore": "Към магазина в Интернет", "gracefulShutdown": "Услугата временно не е достъпна поради профилактика. Моля опитайте по-късно.", "IamHost": "Аз съм домакина", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Грешна парола", "incorrectPassword": "Неправилно потребителско име или парола", "inlineInstallationMsg": "Трябва да инсталирате разширението за споделяне на екрана.", "inlineInstallExtension": "Инсталиране сега", "internalError": "Опа! Нещо се обърка. Възникна следната грешка: {{error}}", "internalErrorTitle": "Вътрешна грешка", - "kickMessage": "", - "kickParticipantButton": "", - "kickParticipantDialog": "", - "kickParticipantTitle": "", - "kickTitle": "", + "kickMessage": "Може да се свържете с {{participantDisplayName}} за повече подробности.", + "kickParticipantButton": "Изгони", + "kickParticipantDialog": "Сигурни ли сте че искате да изгоните участника?", + "kickParticipantTitle": "Изгонване на този участник?", + "kickTitle": "Ауч! {{participantDisplayName}} ви изгони от тази среща", "liveStreaming": "Излъчване на живо", - "liveStreamingDisabledForGuestTooltip": "", - "liveStreamingDisabledTooltip": "", + "liveStreamingDisabledForGuestTooltip": "Гостите не могат да стартират излъчване на живо.", + "liveStreamingDisabledTooltip": "Излъчването на живо е деактивирано.", "lockMessage": "Неуспешно заключване на конференцията.", - "lockRoom": "", + "lockRoom": "Добавяне $t(lockRoomPasswordUppercase) за срещата", "lockTitle": "Неуспешно заключване", "logoutQuestion": "Сигурни ли сте, че искате да излезете и да прекъснете конференцията?", "logoutTitle": "Изход", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "Лимитът за максимален брой участници бе достигнат. Капацитета на срещата е запълнен. Моля свържете се с организатора или опитайте по-късно!", + "maxUsersLimitReachedTitle": "Достигнат е лимита за максимален брой участници", "micConstraintFailedError": "Микрофонът Ви не покрива някои от изискванията.", "micNotFoundError": "Не е открит микрофон.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Пуснете микрофона си от системните настройки на компютъра ви", + "micNotSendingDataTitle": "Микрофона ви е спрян от системните настройки", "micPermissionDeniedError": "Не сте дали разрешение за използване на микрофона. Ще можете да се присъедините в беседата, но другите няма да Ви чуват. Използвайте бутона с камерата в адресната лента, за да оправите това.", - "micUnknownError": "Не възможен достъп до микрофона по неясна причина.", + "micUnknownError": "Невъзможен достъп до микрофона по неясна причина.", "muteParticipantBody": "Вие няма да можете да спрете заглушаването на участника, но той ще може да го направи по всяко време.", "muteParticipantButton": "Изключи микрофона", - "muteParticipantDialog": "", - "muteParticipantTitle": "", + "muteParticipantDialog": "Сигурни ли сте че искате да заглушите този участник? Няма да можете да пуснете обратно звука му, но участникът ще може да направи това сам.", + "muteParticipantTitle": "Спиране звука на участник?", "Ok": "Готово", - "passwordLabel": "", - "passwordNotSupported": "Задаването на парола за срещата не се поддържа.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "Тази среща е заключена. Моля въведете $t(lockRoomPassword) за да влезнете.", + "passwordNotSupported": "Задаването на $t(lockRoomPassword) за срещата не се поддържа.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) не се поддържа", + "passwordRequired": "Изисква се $t(lockRoomPassword)", "popupError": "Браузърът Ви блокира изскачащите прозорци от този уеб сайт. Моля, разрешете изскачащите прозорци от настройките за сигурност на браузъра си и след това опитайте отново.", "popupErrorTitle": "Блокиран изскачащ прозорец", "recording": "Запис", - "recordingDisabledForGuestTooltip": "", - "recordingDisabledTooltip": "", + "recordingDisabledForGuestTooltip": "Гостите не могат да стартират запис.", + "recordingDisabledTooltip": "Стартирането на запис е спряно.", "rejoinNow": "Повторно присъединяване сега", "remoteControlAllowedMessage": "{{user}} прие заявката Ви за отдалечено управление!", "remoteControlDeniedMessage": "{{user}} отказа заявката Ви за отдалечено управление!", - "remoteControlErrorMessage": "Възникна грешка при опита за искана на разрешение за отдалечено управление от {{user}}!", + "remoteControlErrorMessage": "Възникна грешка при опита за искане на разрешение за отдалечено управление от {{user}}!", "remoteControlRequestMessage": "Ще позволите ли на {{user}} да управлява отдалечено компютъра Ви?", "remoteControlShareScreenWarning": "Ако натиснете „Разрешаване“, ще споделите екрана си!", "remoteControlStopMessage": "Сесията за отдалечено управление приключи!", "remoteControlTitle": "Отдалечено управление на компютъра", "Remove": "Премахване", - "removePassword": "", + "removePassword": "Премахване на $t(lockRoomPassword)", "removeSharedVideoMsg": "Наистина ли искате да премахнете споделеното си видео?", "removeSharedVideoTitle": "Край на споделянето на видео", "reservationError": "Грешка в системата за резервации", @@ -226,100 +235,109 @@ "retry": "Повторен опит", "screenSharingFailedToInstall": "Опа! Разширението за споделяне на екрана не успя да се инсталира.", "screenSharingFailedToInstallTitle": "Разширението за споделяне на екрана не успя да се инсталира", - "screenSharingFirefoxPermissionDeniedError": "", - "screenSharingFirefoxPermissionDeniedTitle": "", + "screenSharingFirefoxPermissionDeniedError": "Нещо се обърка докато се опитвахме да споделим екрана. Моля уверете се че сте дали права за това. ", + "screenSharingFirefoxPermissionDeniedTitle": "Упс! Не успяхме да стартираме споделянето на екрана!", "screenSharingPermissionDeniedError": "Опа! Нещо се обърка с разрешенията на разширението за споделяне на екрана. Моля, презаредете и опитайте отново.", + "sendPrivateMessage": "Наскоро получихте лично съобщение. Искате да отговорите на това съобшение или да изпратите до всички?", + "sendPrivateMessageCancel": "Изпрати до всички", + "sendPrivateMessageOk": "Изпрати лично", + "sendPrivateMessageTitle": "Да се изпрати лично?", "serviceUnavailable": "Услугата не е налична", "sessTerminated": "Разговорът приключи", "Share": "Споделяне", "shareVideoLinkError": "Моля въведете правилна връзка към YouTube.", "shareVideoTitle": "Сподели видео", "shareYourScreen": "Споделяне на екрана", - "shareYourScreenDisabled": "", - "shareYourScreenDisabledForGuest": "", + "shareYourScreenDisabled": "Споделянето на екрана не се поддържа.", + "shareYourScreenDisabledForGuest": "Гостите не могат да споделят екрана.", "startLiveStreaming": "Започване на излъчване на живо", - "startRecording": "Край на записа", + "startRecording": "Стартиране на запис", "startRemoteControlErrorMessage": "Възникна грешка при опита за започване на сесията за отдалечено управление!", "stopLiveStreaming": "Спиране на излъчването на живо", "stopRecording": "Край на записа", "stopRecordingWarning": "Наистина ли искате да спрем записа?", "stopStreamingWarning": "Наистина ли искате да спрете излъчването на живо?", - "streamKey": "", + "streamKey": "Ключ за излъчване на живо", "Submit": "Изпращане", "thankYou": "Благодарим, че използвахте {{appName}}!", "token": "код за достъп", "tokenAuthFailed": "Съжаляваме, но не можете да се присъедините към този разговор.", "tokenAuthFailedTitle": "Неуспешна идентификация", - "transcribing": "", - "unlockRoom": "", + "transcribing": "Транскрипция", + "unlockRoom": "Премахване $t(lockRoomPassword) от срещата", "userPassword": "потребителска парола", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", + "WaitForHostMsg": "Конференцията <b>{{room}}</b> все още не е започнала. Ако сте домакинът тогава се идентифицирайте. В противен случай изчакайте докато домакинът пристигне.", + "WaitForHostMsgWOk": "Конференцията <b>{{room}}</b> все още не е започнала. Ако сте домакинът тогава натиснете бутона за да се идентифицирате. В противен случай изчакайте докато домакинът пристигне.", "WaitingForHost": "Чакаме домакина ...", "Yes": "Да", - "yourEntireScreen": "Целия екран" - }, - "\u0005dialog": { - "accessibilityLabel": {} + "yourEntireScreen": "Целия екран", + "screenSharingAudio": "Сподели и звука", + "muteEveryoneStartMuted": "Всички да влизат без звук", + "muteEveryoneSelf": "себе си", + "muteEveryoneTitle": "Заглуши всички?", + "muteEveryoneDialog": "Сигурни ли сте, че искате да заглушите всички? Няма да можете да пуснете звука им отново, но участниците ще могат да направят това сами.", + "muteEveryoneElseTitle": "Заглушете всички освен {{whom}}?", + "muteEveryoneElseDialog": "След като заглушите някой, няма да можете да пуснете обратно звука му, но участникът ще може да направи това сам." }, "dialOut": { "statusMessage": "в момента е {{status}}" }, + "documentSharing": { + "title": "Споделен документ" + }, "feedback": { "average": "Средно", "bad": "Лошо", - "detailsLabel": "", + "detailsLabel": "Разкажете ни повече.", "good": "Добра", - "rateExperience": "Моля, оценете качеството на срещата.", + "rateExperience": "Моля, оценете качеството на срещата", "veryBad": "Много лошо", "veryGood": "Много добра" }, - "\u0005feedback": {}, "incomingCall": { - "answer": "", - "audioCallTitle": "", + "answer": "Вдигни", + "audioCallTitle": "Входящ разговор", "decline": "Отхвърляне", - "productLabel": "", - "videoCallTitle": "" + "productLabel": "от Jitsi Meet", + "videoCallTitle": "Входящ видео разговор" }, "info": { - "accessibilityLabel": "", - "addPassword": "", - "cancelPassword": "", - "conferenceURL": "", - "country": "", - "dialANumber": "", - "dialInConferenceID": "", - "dialInNotSupported": "", - "dialInNumber": "", - "dialInSummaryError": "", - "dialInTollFree": "", - "genericError": "", - "inviteLiveStream": "", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", - "liveStreamURL": "Излъчване на живо", - "moreNumbers": "", - "noNumbers": "", + "accessibilityLabel": "Покажи информация", + "addPassword": "Добави $t(lockRoomPassword)", + "cancelPassword": "Премахни $t(lockRoomPassword)", + "conferenceURL": "Връзка:", + "country": "Страна", + "dialANumber": "За влизане в срещата, наберете един от изброените номера и въведете кода.", + "dialInConferenceID": "Код:", + "dialInNotSupported": "Съжаляваме, обаждането в момента не се поддържа.", + "dialInNumber": "Тел:", + "dialInSummaryError": "Проблем при достъпа на информация за опциите за влизане през телефон. Моля опитайте отново по-късно.", + "dialInTollFree": "Безплатен", + "genericError": "Упс, нещо се случи.", + "inviteLiveStream": "За да видите предаването на живо на срещата, използвйте тази връзка: {{url}}", + "invitePhone": "За влизане през телефон, използвайте: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Вижте още номера: {{url}}\n\n\nАко вече сте набрали от телефон в стаята, влезте без да е пуснат звука: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Поканени сте да се присъедините към среща.", + "inviteURLFirstPartPersonal": "{{name}} ви кани за среща.\n", + "inviteURLSecondPart": "\nВлезте в срещата:\n{{url}}\n", + "liveStreamURL": "Излъчване на живо:", + "moreNumbers": "Повече номера", + "noNumbers": "Няма номера за набиране.", "noPassword": "Няма", - "noRoom": "", - "numbers": "", - "password": "", + "noRoom": "Няма посочена стая за информация за номера за набиране.", + "numbers": "Номера", + "password": "$t(lockRoomPasswordUppercase):", "title": "Споделяне", - "tooltip": "", - "label": "" + "tooltip": "Споделете връзката и информацията за номера свързани със срещата", + "label": "Информация за срещата" }, - "\u0005info": {}, "inviteDialog": { - "alertText": "", + "alertText": "Не успях да поканя участниците.", "header": "Покани", "searchCallOnlyPlaceholder": "Въведете телефонен номер", - "searchPeopleOnlyPlaceholder": "", - "searchPlaceholder": "", - "send": "" + "searchPeopleOnlyPlaceholder": "Търсене на участници", + "searchPlaceholder": "Участник или телефонен номер", + "send": "Изпрати" }, "inlineDialogFailure": { "msg": "Имаше грешка.", @@ -329,181 +347,184 @@ }, "keyboardShortcuts": { "focusLocal": "Фокусиране върху Вашето видео", - "focusRemote": "Фокусиране върху видеото на друг участник", + "focusRemote": "Фокусирай видеото на друг участник", "fullScreen": "Влизане/излизане от режим на цял екран", "keyboardShortcuts": "Клавишни комбинации", - "localRecording": "", + "localRecording": "Показване или скриване на контролите за локален запис", "mute": "Спиране/пускане на микрофона", "pushToTalk": "Натиснете, за да говорите", "raiseHand": "Вдигнете или свалете ръка", "showSpeakerStats": "Показване на статистика за говорителя", "toggleChat": "Отваряне/скриване на текстовите съобщения", - "toggleFilmstrip": "", + "toggleFilmstrip": "Показване или скриване на видео миниатюрите", "toggleScreensharing": "Смяна между камера и споделен екран", - "toggleShortcuts": "", - "videoMute": "Пускане/спиране на камерата" + "toggleShortcuts": "Показване или скриване на клавишните комбинации", + "videoMute": "Пускане/спиране на камерата", + "videoQuality": "Управление на качество на обаждането" }, - "\u0005keyboardShortcuts": {}, "liveStreaming": { "busy": "Работим върху това да освободим ресурси за излъчване. Моля, опитайте отново след няколко минути.", - "busyTitle": "Всички излъчватели в момента са заети.", - "changeSignIn": "", - "choose": "", - "chooseCTA": "", - "enterStreamKey": "", + "busyTitle": "Всички излъчватели в момента са заети", + "changeSignIn": "Смяна на акаунти.", + "choose": "Изберете предаване на живо", + "chooseCTA": "Изберете опция за предаване. Влезли сте като {{email}}.", + "enterStreamKey": "Въведете ключа от YouTube за предаване на живо.", "error": "Излъчването на живо беше неуспешно. Моля, опитайте отново.", - "errorAPI": "", - "errorLiveStreamNotEnabled": "", - "expandedOff": "", - "expandedOn": "", - "expandedPending": "", + "errorAPI": "Изникна проблем с връзката към YouTube. Моля опитайте отново.", + "errorLiveStreamNotEnabled": "Предаването на живо не е пуснато за {{email}}. Моля активирайте го или сменете акаунта.", + "expandedOff": "Предаването на живо бе спряно", + "expandedOn": "Срещата се излъчва на живо в YouTube.", + "expandedPending": "Излъчването на живо се стартира...", "failedToStart": "Излъчването на живо не успя да започне", - "getStreamKeyManually": "", - "invalidStreamKey": "", + "getStreamKeyManually": "Не успяхме да открием никакво предаване на живо. Опитайте да вземете ключа за такова от YouTube.", + "invalidStreamKey": "Ключът за предаване на живо е грешен.", "off": "Край на излъчването на живо", + "offBy": "{{name}} спря излъчването на живо", "on": "Излъчване на живо", + "onBy": "{{name}} пусна излъчване на живо", "pending": "Започване на излъчването на живо…", - "serviceName": "", - "signedInAs": "", - "signIn": "", - "signInCTA": "", - "signOut": "", - "start": "Започване на излъчване на живо", - "streamIdHelp": "", - "unavailableTitle": "Излъчването на живо е недостъпно" + "serviceName": "Предаване на живо", + "signedInAs": "В момента сте влезли като:", + "signIn": "Влезте с Гугъл", + "signInCTA": "Влезте или въведете ключът за излъчване на живо от YouTube.", + "signOut": "Излизане", + "start": "Започни излъчване на живо", + "streamIdHelp": "Какво е това?", + "unavailableTitle": "Излъчването на живо е недостъпно", + "googlePrivacyPolicy": "Политика за поверителност на Google", + "youtubeTerms": "Условия за ползване на YouTube" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { - "off": "", - "on": "", - "unknown": "" + "off": "Изключено", + "on": "Включено", + "unknown": "Непознат" }, - "dialogTitle": "", - "duration": "", - "durationNA": "", - "encoding": "", - "label": "", - "labelToolTip": "", - "localRecording": "", + "dialogTitle": "Управление на локален запис", + "duration": "Продължителност", + "durationNA": "Няма", + "encoding": "Кодек", + "label": "Етикет", + "labelToolTip": "Локалния запис е включен", + "localRecording": "Локален запис", "me": "Аз", "messages": { - "engaged": "", - "finished": "", - "finishedModerator": "", - "notModerator": "" + "engaged": "Локалния запис е включен.", + "finished": "Записа на сесията {{token}} приключи. Моля изпратете записа на вашият домакин.", + "finishedModerator": "Сесията {{token}} за запис приключи. Локалният запис беше запазен. Моля поканете останалите участници да ви изпратят техните записи.", + "notModerator": "Нямате права да пускате спирате локален запис." }, "moderator": "Модератор", - "no": "", + "no": "Не", "participant": "Участник", - "participantStats": "", - "sessionToken": "", - "start": "Край на записа", - "stop": "Край на записа", + "participantStats": "Статистика на участник", + "sessionToken": "Тоукън за сесията", + "start": "Започни запис", + "stop": "Спри записа", "yes": "Да" }, - "\u0005localRecording": {}, "lockRoomPassword": "парола", "lockRoomPasswordUppercase": "Парола", "me": "аз", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", - "disconnected": "Връзка:", + "connectedOneMember": "{{name}} влезна в срещата", + "connectedThreePlusMembers": "{{name}} и още {{count}} влезнаха в срещата", + "connectedTwoMembers": "{{first}} и {{second}} влезнаха в срещата", + "disconnected": "Напусна срещата", "focus": "Конферентен фокус", - "focusFail": "{{component}} не е на раположения - следващ опит след {{ms}} секунди", + "focusFail": "{{component}} не е на раположение - следващ опит след {{ms}} секунди", "grantedTo": "Даване на роля модератор на {{to}}!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{name}} бе поканен", + "invitedThreePlusMembers": "{{name}} и още {{count}} бяха поканени", + "invitedTwoMembers": "{{first}} и {{second}} бяха поканени", + "kickParticipant": "{{kicked}} беше изгонен от {{kicker}}", "me": "Аз", "moderator": "Придобихте права на модератор!", "muted": "Започвате разговора без звук.", "mutedTitle": "Звукът ви е спрян!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "", + "mutedRemotelyTitle": "Микрофонът ви бе спрян от {{participantDisplayName}}!", + "mutedRemotelyDescription": "Винаги можете да пуснете микрофона си когато сте готови да говорите. Заглушете го отново за да не изпращате шум в срещата.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) е премахната от друг потребител", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) създадена от друг потребител", + "raisedHand": "{{name}} иска думата.", "somebody": "Някой", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "", - "suboptimalExperienceTitle": "", - "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "startSilentTitle": "Влязохте с опция да не чувате аудио!", + "startSilentDescription": "Влезте повторно за да пуснете звука", + "suboptimalBrowserWarning": "Опасяваме се, че няма да можете да се насладите на срещата. Работим по въпроса, междувременно използвайте някой от <a href='static/recommendedBrowsers.html' target='_blank'>напълно поддържаните браузъри</a>.", + "suboptimalExperienceTitle": "Внимание", + "unmute": "Пускане на микрофона", + "newDeviceCameraTitle": "Засечена е нова камера", + "newDeviceAudioTitle": "Ново аудио устройство е засечено", + "newDeviceAction": "Използвай" }, - "passwordSetRemotely": "", - "passwordDigitsOnly": "", + "passwordSetRemotely": "зададена от друг участник", + "passwordDigitsOnly": "До {{number}} цифри", "poweredby": "с подкрепата на", "presenceStatus": { - "busy": "", - "calling": "", + "busy": "Зает", + "calling": "Обаждане...", "connected": "Свързан", - "connecting": "Свързване", - "connecting2": "Свързване", + "connecting": "Свързване...", + "connecting2": "Свързване*...", "disconnected": "Изключен", - "expired": "", - "ignored": "", - "initializingCall": "", - "invited": "Покани", - "rejected": "", - "ringing": "" + "expired": "Изтекъл", + "ignored": "Пренебрегнат", + "initializingCall": "Свързване на обаждането...", + "invited": "Поканен", + "rejected": "Отхвърлен", + "ringing": "Звъни..." }, - "\u0005presenceStatus": {}, "profile": { "setDisplayNameLabel": "Задайте екранното си име", "setEmailInput": "Въведете е-поща", - "setEmailLabel": "Задайте е-пощата си в „gravatar“", + "setEmailLabel": "Задайте е-пощата си в „Gravatar“", "title": "Профил" }, + "raisedHand": "Иска думата", "recording": { - "authDropboxText": "", - "availableSpace": "", - "beta": "", + "authDropboxText": "Качете в Dropbox", + "availableSpace": "Налично място: {{spaceLeft}} MB (приблизително {{duration}} минути запис)", + "beta": "БЕТА", "busy": "Работим върху това да освободим ресурси за запис. Моля, опитайте отново след няколко минути.", "busyTitle": "Всички възможности за запис в момента са заети", "error": "Грешка при опит за запис. Моля опитайте отново.", - "expandedOff": "Записът спрян", - "expandedOn": "", - "expandedPending": "Записът започна", + "expandedOff": "Записът спря", + "expandedOn": "Срещата се записва в момента.", + "expandedPending": "Записът започва...", "failedToStart": "Неуспешен опит за записване", - "fileSharingdescription": "", - "live": "", - "loggedIn": "", + "fileSharingdescription": "Споделете записа с участниците в срещата", + "live": "На Живо", + "loggedIn": "Влезли сте като {{userName}}", "off": "Записът спрян", + "offBy": "{{name}} спря записът", "on": "Запис", - "pending": "", - "rec": "", - "serviceDescription": "", - "serviceName": "", - "signIn": "", - "signOut": "", - "unavailable": "", + "onBy": "{{name}} пусна запис", + "pending": "Стартира запис на срещата...", + "rec": "ЗАПИС", + "serviceDescription": "Записът ви ще се запише от специална записваща услуга", + "serviceName": "Записваща услуга", + "signIn": "Влизане", + "signOut": "Излизане", + "unavailable": "Упс! В момент {{serviceName}} е недостъпна. В момента се опитваме да решим проблема. Моля опитайте отново малко по-късно.", "unavailableTitle": "Записът е невъзможен" }, - "\u0005recording": {}, "sectionList": { - "pullToRefresh": "" + "pullToRefresh": "Издърпай за да се обнови" }, "settings": { "calendar": { - "about": "", - "disconnect": "Изключен", - "microsoftSignIn": "", - "signedIn": "", - "title": "" + "about": "Календарната интеграция на {{appName}} сигурно достъпва вашия календар за да покаже настъпващите събития.", + "disconnect": "Разкачи", + "microsoftSignIn": "Влез с Microsoft акаунт", + "signedIn": "В момента достъпва календара с {{email}}. Натиснете бутона Разкачи за да спрете достъпа.", + "title": "Календар" }, - "devices": "", + "devices": "Устройства", "followMe": "Всички ме следват", - "language": "", - "loggedIn": "", + "language": "Език", + "loggedIn": "Влезли сте като {{name}}", "moderator": "Модератор", - "more": "", + "more": "Повече", "name": "Име", "noDevice": "Няма", "selectAudioOutput": "Звуков изход", @@ -511,29 +532,32 @@ "selectMic": "Микрофон", "startAudioMuted": "Всички започват заглушени", "startVideoMuted": "Всички започват скрити", - "title": "Настройки" - }, - "\u0005settings": { - "calendar": {} + "title": "Настройки", + "speakers": "Говорители", + "microphones": "Микрофони" }, "settingsView": { - "alertOk": "", + "advanced": "Разширени", + "alertOk": "Потвърди", "alertTitle": "Внимание", - "alertURLText": "", - "buildInfoSection": "", - "conferenceSection": "", - "displayName": "", - "email": "", + "alertURLText": "Въведената връзка за сървър е невалидна", + "buildInfoSection": "Информация за програмата", + "conferenceSection": "Конференция", + "disableCallIntegration": "Декативирнае на интеграция с обажданията", + "disableP2P": "Деактивиране на опцията за пряка връзка (p2p)", + "displayName": "Име", + "email": "Поща", "header": "Настройки", "profileSection": "Профил", - "serverURL": "", - "startWithAudioMuted": "", - "startWithVideoMuted": "", - "version": "" + "serverURL": "Линк на сървъра", + "showAdvanced": "Показване на разширени настройки", + "startWithAudioMuted": "Започни със спрян звук", + "startWithVideoMuted": "Започни със спряно видео", + "version": "Версия" }, "share": { - "dialInfoText": "", - "mainText": "" + "dialInfoText": "\n\n=====\n\nИскате да наберете от телефона?\n\n{{defaultDialInNumber}}Използвайте този линк за повече номера\n{{dialInfoPageUrl}}", + "mainText": "Използвайте линка за да влезете в срещата:\n{{roomUrl}}" }, "speaker": "Говорещ", "speakerStats": { @@ -555,99 +579,113 @@ }, "toolbar": { "accessibilityLabel": { - "audioOnly": "", - "audioRoute": "", - "callQuality": "", - "cc": "", - "chat": "", - "document": "Отваряне/затваряне на споделен документ", - "feedback": "", - "fullScreen": "", - "hangup": "", + "audioOnly": "Пускане на режим само с звук", + "audioRoute": "Изберете устройство за звук", + "callQuality": "Промяна качеството на видеото", + "cc": "Пускане на субтитри", + "chat": "Активиране на прозорец за съобщения", + "document": "Показване на споделен документ", + "download": "Свалете приложението", + "feedback": "Отзиви", + "fullScreen": "Пускане/Спиране на изглед в цял екран", + "hangup": "Напускане на срещата", + "help": "Помощ", "invite": "Поканете участници", - "kick": "", - "localRecording": "", - "lockRoom": "", - "moreActions": "", - "moreActionsMenu": "", - "mute": "", - "pip": "", + "kick": "Изгони участник", + "localRecording": "Показване на контроли за локален запис", + "lockRoom": "Смяна парола на среща", + "moreActions": "Показване на меню с повече опции", + "moreActionsMenu": "Меню с повече опции", + "mute": "Пускане/спиране на видеото", + "pip": "Пускане на Картина-в-Картина", + "privateMessage": "Изпрати лично съобщение", "profile": "Редактиране на профила", - "raiseHand": "", - "recording": "", - "remoteMute": "", - "Settings": "", - "sharedvideo": "", - "shareRoom": "", - "shareYourScreen": "", - "shortcuts": "", - "show": "", - "speakerStats": "", - "tileView": "", - "toggleCamera": "", - "videomute": "", - "videoblur": "" + "raiseHand": "Смяна искане на думата", + "recording": "Пускане/спиране на запис", + "remoteMute": "Заглуши участник", + "Settings": "Промяна на настройки", + "sharedvideo": "Споделяне на YouTube видео", + "shareRoom": "Добавете някого", + "shareYourScreen": "Споделяне на екрана", + "shortcuts": "Бързи клавиши", + "show": "Покажи на главния екран", + "speakerStats": "Показване на статистики за участниците", + "tileView": "Превключване на изглед галерия", + "toggleCamera": "Пускане/спиране на камера", + "videomute": "Пускане/спиране на видеото", + "videoblur": "Пускане/спиране на замъгляване на видеото", + "toggleFilmstrip": "Превключи видео миниатюрите", + "muteEveryone": "Заглуши всички", + "moreOptions": "Покажи повече опции" }, "addPeople": "Добавяне на участници в разговора", - "audioOnlyOff": "", - "audioOnlyOn": "", - "audioRoute": "", + "audioOnlyOff": "Спиране режима с нисък трафик", + "audioOnlyOn": "Пускане режима с нисък трафик", + "audioRoute": "Изберете устройство за звук", "authenticate": "Идентификация", - "callQuality": "", + "callQuality": "Промяна качеството на видеото", "chat": "Отваряне/затваряне на текстовите съобщения", - "closeChat": "", - "documentClose": "Отваряне/затваряне на споделен документ", - "documentOpen": "Отваряне/затваряне на споделен документ", - "enterFullScreen": "", - "enterTileView": "", - "exitFullScreen": "", - "exitTileView": "", - "feedback": "", + "closeChat": "Затваряне на съобщенията", + "documentClose": "Затваряне на споделеният документ", + "documentOpen": "Отваряне на споделен документ", + "download": "Свалете приложението", + "enterFullScreen": "Вижте на цял екран", + "enterTileView": "Влизане в изглед галерия", + "exitFullScreen": "Изход от цял екран", + "exitTileView": "Спиране на изглед галерия", + "feedback": "Отзиви", "hangup": "Напускане", + "help": "Помощ", "invite": "Поканете участници", "login": "Влез", "logout": "Изход", - "lowerYourHand": "", - "moreActions": "", + "lowerYourHand": "Махни искането на думата", + "moreActions": "Още опции", "mute": "Спиране/пускане на микрофона", - "openChat": "", - "pip": "", + "noAudioSignalTitle": "Няма сигнал идващ от микрофона!", + "noAudioSignalDesc": "Ако не сте спрели звука на устройството от системните настройки, сменете с друго устройство.", + "noAudioSignalDescSuggestion": "Ако не сте спрели звука на устройството от системните настройки, използвайте някое от предложените устройства.", + "openChat": "Отвори съобщенията", + "pip": "Пусни Картина-в-Картина", + "privateMessage": "Изпрати лично съобщение", "profile": "Редактиране на профила", "raiseHand": "Вдигане/сваляне на ръка", - "raiseYourHand": "Вдигни ръка.", + "raiseYourHand": "Поискай думата", "Settings": "Настройки", "sharedvideo": "Пускане/спиране на споделянето на екрана", - "shareRoom": "", - "shortcuts": "", - "speakerStats": "Статистика на говорителя", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", - "stopSharedVideo": "", + "shareRoom": "Добавете някого", + "shortcuts": "Виж бързите клавиши", + "speakerStats": "Статистика за говорителите", + "startScreenSharing": "Започни споделяне на екрана", + "startSubtitles": "Пускане на субтитри", + "stopScreenSharing": "Спиране споделяне на екрана", + "stopSubtitles": "Спиране на субтитри", + "stopSharedVideo": "Спиране на YouTube видео", "talkWhileMutedPopup": "Опитвате се да говорите? В момента микрофонът Ви е заглушен.", - "tileViewToggle": "", - "toggleCamera": "", + "tileViewToggle": "Превключване на изглед галерия", + "toggleCamera": "Пускане/спиране на камера", "videomute": "Пускане/спиране на камерата", - "startvideoblur": "", - "stopvideoblur": "" - }, - "\u0005toolbar": { - "accessibilityLabel": {} + "startvideoblur": "Замъгли фона ми", + "stopvideoblur": "Спиране замъгляването на фона", + "noisyAudioInputDesc": "Изглежда доста шум идва от микрофона ви, заглушете го или сменете устройството.", + "noisyAudioInputTitle": "Изглежда е шумно около вас!", + "noAudioSignalDialInLinkDesc": "Номера за обаждане", + "noAudioSignalDialInDesc": "Може да влезнете чрез обаждане на:", + "muteEveryone": "Заглуши всички", + "moreOptions": "Повече опции" }, "transcribing": { - "ccButtonTooltip": "", - "error": "Грешка при опит за запис. Моля опитайте отново.", - "expandedLabel": "", - "failedToStart": "", - "labelToolTip": "", - "off": "", - "pending": "", - "start": "", - "stop": "", - "tr": "" + "ccButtonTooltip": "Пускане / Спиране на субтитри", + "error": "Грешка при опит за транскрибиране. Моля опитайте отново.", + "expandedLabel": "Транскрибирането е пуснато", + "failedToStart": "Транскрибирането не успя при пускане", + "labelToolTip": "Тази среща се транскрибира", + "off": "Транскрибирането спря", + "pending": "Стартира се транскрибиране на срещата...", + "start": "Започва показване на субтитри", + "stop": "Спира показване на субтитри", + "tr": "СУБ" }, - "\u0005transcribing": {}, "userMedia": { "androidGrantPermissions": "Изберете <b><i>Разрешаване</i></b>, когато браузърът Ви помоли за разрешение.", "chromeGrantPermissions": "Изберете <b><i>Разрешаване</i></b>, когато браузърът Ви помоли за разрешение.", @@ -661,31 +699,34 @@ "safariGrantPermissions": "Изберете <b><i>Добре</i></b>, когато браузърът Ви помоли за разрешение." }, "videoSIPGW": { - "busy": "", - "busyTitle": "", - "errorAlreadyInvited": "", - "errorInvite": "", - "errorInviteFailed": "", - "errorInviteFailedTitle": "", - "errorInviteTitle": "", - "pending": "" + "busy": "Работим по освобождаване на ресурси. Моля, опитайте след няколко минути.", + "busyTitle": "Услугата за стаи в момента е заета", + "errorAlreadyInvited": "{{displayName}} вече е поканен", + "errorInvite": "Конференцията не е стартирана. Моля, опитайте по-късно.", + "errorInviteFailed": "Работим по разрешаването на проблем. Моля, опитайте по-късно.", + "errorInviteFailedTitle": "Добавянето на {{displayName}} не успя", + "errorInviteTitle": "Грешка при добавяне на стая", + "pending": "{{displayName}} бе поканен" }, "videoStatus": { - "audioOnly": "", - "audioOnlyExpanded": "", - "callQuality": "", + "audioOnly": "АУДИО", + "audioOnlyExpanded": "Вие сте в режим на нисък трафик. В този режим ще получавате само аудио или споделени екрани.", + "callQuality": "Качество на видеото", "hd": "ВК", + "hdTooltip": "Гледате високо качество на видеото", "highDefinition": "Високо качество", - "labelTooiltipNoVideo": "", - "labelTooltipAudioOnly": "Включен е режим само със звук", + "labelTooiltipNoVideo": "Няма видео", + "labelTooltipAudioOnly": "Пуснат режим на нисък трафик", "ld": "НК", + "ldTooltip": "Виждате ниско качество на видеото", "lowDefinition": "Ниско качество", - "onlyAudioAvailable": "", - "onlyAudioSupported": "", + "onlyAudioAvailable": "Само аудио е налично", + "onlyAudioSupported": "Този браузър поддържа само аудио.", "p2pEnabled": "Вкл. директно свързване", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "В директна връзка, получаваното качество може да се сменя между високо и само аудио. Останалите настройки ще са достъпни когато връзката не е директна.", "recHighDefinitionOnly": "Ще се предпочита високо качество.", "sd": "СК", + "sdTooltip": "Гледате стандартно качество на видеото", "standardDefinition": "Стандартно качество" }, "videothumbnail": { @@ -693,38 +734,54 @@ "flip": "Огледално", "kick": "Изгони", "moderator": "Модератор", - "mute": "Учасника е с изключен микрофон", + "mute": "Участника е с изключен микрофон", "muted": "Изключен микрофон", "remoteControl": "Отдалечено управление", - "show": "", - "videomute": "" + "show": "Покажи на главния екран", + "videomute": "Участник е спрял камерата си", + "domuteOthers": "Заглушете всички останали" }, "welcomepage": { "accessibilityLabel": { - "join": "", + "join": "Натиснете за да влезете", "roomname": "Въведете име на стаята" }, - "appDescription": "", + "appDescription": "Хайде на видео разговор с целия екип! Всъщност, поканете всички свои познати! {{app}} е напълно защитено решение за видеоконференции със 100% отворен код, което може да ползвате по цял ден, всеки ден, безплатно - без да ви е нужна регистрация.", "audioVideoSwitch": { - "audio": "", - "video": "" + "audio": "Глас", + "video": "Видео" }, - "calendar": "", - "connectCalendarButton": "", - "connectCalendarText": "", - "enterRoomTitle": "", + "calendar": "Календар", + "connectCalendarButton": "Свържете вашия календар", + "connectCalendarText": "Свържете вашия календар за да видите срещите си в {{app}}. Добавяйки {{provider}} срещите в календара си ще можете да ги старирате с едно докосване.", + "enterRoomTitle": "Започни нова среща", + "roomNameAllowedChars": "Името на срещата не трябва да съдържа някой от символите: ?, &, :, ', \", %, #.", "go": "НАПРЕД", - "join": "ПРИСЪЕДИНЯВАНЕ", - "info": "", + "goSmall": "НАПРЕД", + "join": "Създай / Влез", + "info": "Информация", "privacy": "Поверителност", - "recentList": "", - "recentListDelete": "", - "recentListEmpty": "", - "reducedUIText": "", + "recentList": "Скорошни срещи", + "recentListDelete": "Изтрий", + "recentListEmpty": "Списъка с скорошни срещи е празен. След като участвате в някоя среща, ще я намерите тук.", + "reducedUIText": "Добре дошли в {{app}}!", "roomname": "Въведете име на стаята", - "roomnameHint": "", + "roomnameHint": "Въведете името или връзката на стаята в която искате да влезете. Също може да си измислите име. Само го споделете с някой, за да може и той да въведе същото име за да се срещнете.", "sendFeedback": "Изпращане на отзиви", "terms": "Условия", - "title": "" + "title": "Сигурна, с много възможности, и напълно безплатна платформа за видео конференции", + "getHelp": "Търсене на помощ" + }, + "helpView": { + "header": "Място за помощ" + }, + "lonelyMeetingExperience": { + "youAreAlone": "Вие сте сами в срещата", + "button": "Поканете участници" + }, + "chromeExtensionBanner": { + "dontShowAgain": "Не показвай повече", + "buttonText": "Инсталирайте разширението за Chrome", + "installExtensionText": "Инсталирайте разширенията за Google Calendar и Office 365" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ca.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ca.json new file mode 100644 index 000000000..ea9f51e86 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ca.json @@ -0,0 +1,787 @@ +{ + "addPeople": { + "add": "Convida", + "countryNotSupported": "Encara no és possible usar aquesta destinació.", + "countryReminder": "Truqueu des de fora els EUA? Assegureu-vos que comenceu b el codi de país!", + "disabled": "No podeu convidar-hi persones.", + "failedToAdd": "No s'han pogut afegir-hi participants", + "footerText": "La marcació està desactivada.", + "loading": "S'estan cercant persones i números de telèfon", + "loadingNumber": "S'està validant el número de telèfon", + "loadingPeople": "S'estan cercant les persones a convidar", + "noResults": "No s'ha trobat cap resultat coincident", + "noValidNumbers": "Introduïu un número de telèfon", + "searchNumbers": "Afegeix-hi números de telèfon", + "searchPeople": "Cerca-hi persones", + "searchPeopleAndNumbers": "Cerca persones o n'afegeix els números de telèfon", + "telephone": "Telèfon: {{number}}", + "title": "Convida persones a aquesta reunió" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Auriculars", + "phone": "Telèfon", + "speaker": "Altaveu", + "none": "No hi ha disponible cap aparell d'àudio" + }, + "audioOnly": { + "audioOnly": "Poc ample de banda" + }, + "calendarSync": { + "addMeetingURL": "Afegeix un enllaç de reunió", + "confirmAddLink": "Voleu afegir un enllaç Jitsi en aquest esdeveniment?", + "error": { + "appConfiguration": "La integració de l'agenda no està configurada correctament.", + "generic": "S'ha produït un error. Comproveu la configuració de l'agenda o intenteu d'actualitzar-la.", + "notSignedIn": "S'ha produït un error en l'autenticació per a visualitzar els esdeveniments del calendari. Reviseu la configuració de l'agenda i intenteu a iniciar la sessió més endavant." + }, + "join": "Afegeix-m'hi", + "joinTooltip": "Uniu-vos a la reunió", + "nextMeeting": "reunió següent", + "noEvents": "No hi ha cap esdeveniment previst a l'agenda.", + "ongoingMeeting": "reunió en curs", + "permissionButton": "Obre la configuració", + "permissionMessage": "Es requereix el permís d'agenda per a veure les reunions en l'aplicació.", + "refresh": "Actualitza l'agenda", + "today": "Avui" + }, + "chat": { + "error": "Error: no s'ha enviat el vostre missatge. Raó: {{error}}", + "fieldPlaceHolder": "Escriviu aquí el missatge", + "messagebox": "Escriviu un missatge", + "messageTo": "Missatge privat per a {{recipient}}", + "noMessagesMessage": "Encara no hi ha cap missatge en aquesta reunió. Comenceu una conversa aquí!", + "nickname": { + "popover": "Trieu un sobrenom", + "title": "Introduïu un sobrenom per a usar el xat" + }, + "privateNotice": "Missatge privat per a {{recipient}}", + "title": "Xat", + "you": "vós" + }, + "chromeExtensionBanner": { + "installExtensionText": "Instal·la l'extensió per a la integració amb Google Calendar i Office 365", + "buttonText": "Instal·la l'extensió de Chrome", + "dontShowAgain": "No m'ho mostris més" + }, + "connectingOverlay": { + "joiningRoom": "S'està connectat a la reunió…" + }, + "connection": { + "ATTACHED": "Adjunt", + "AUTHENTICATING": "S'està autenticant", + "AUTHFAIL": "L'autenticació ha fallat", + "CONNECTED": "Connectat", + "CONNECTING": "S'està connectant", + "CONNFAIL": "La connexió ha fallat", + "DISCONNECTED": "Desconnectat", + "DISCONNECTING": "S'està desconnectant", + "ERROR": "Error", + "FETCH_SESSION_ID": "S'està obtenint un identificador de sessió…", + "GET_SESSION_ID_ERROR": "S'ha produït un error en obtenir l'identificador de la sessió: {{code}}", + "GOT_SESSION_ID": "Obtenció d'identificador de sessió… Fet", + "LOW_BANDWIDTH": "S'ha apagat el vídeo de {{displayName}} per a estalviar ample de banda" + }, + "connectionindicator": { + "address": "Adreça:", + "bandwidth": "Ample de banda estimat:", + "bitrate": "Taxa de bits:", + "bridgeCount": "Nombre de servidors: ", + "connectedTo": "Connectat a:", + "e2e_rtt": "E2E RTT:", + "framerate": "Taxa de fotogrames:", + "less": "Menys informació", + "localaddress": "Adreça local:", + "localaddress_plural": "Adreces locals:", + "localport": "Port local:", + "localport_plural": "Ports locals:", + "more": "Més informació", + "packetloss": "Pèrdua de paquets:", + "quality": { + "good": "Bona", + "inactive": "Inactiva", + "lost": "Perduda", + "nonoptimal": "No òptima", + "poor": "Pobra" + }, + "remoteaddress": "Adreça remota:", + "remoteaddress_plural": "Adreces remotes:", + "remoteport": "Port remot:", + "remoteport_plural": "Ports remots:", + "resolution": "Resolució:", + "status": "Connexió:", + "transport": "Transport:", + "transport_plural": "Transports:" + }, + "dateUtils": { + "earlier": "Abans", + "today": "Avui", + "yesterday": "Ahir" + }, + "deepLinking": { + "appNotInstalled": "Us cal l'aplicació mòbil {{app}} per a unir-vos a aquesta reunió des del vostre telèfon.", + "description": "No ha passat res? Hem intentat iniciar la reunió en l'aplicació d'escriptori {{app}}. Torneu a intentar-ho en l'aplicació web {{app}}.", + "descriptionWithoutWeb": "No ha passat res? Hem intentat iniciar la reunió en l'aplicació d'escriptori {{app}}.", + "downloadApp": "Baixa l'aplicació", + "launchWebButton": "Inicia al web", + "openApp": "Continua en l'aplicació", + "title": "S'està iniciant la reunió en {{app}}…", + "tryAgainButton": "Torna-ho a intentar en l'escriptori" + }, + "defaultLink": "p. ex. {{url}}", + "defaultNickname": "p. ex. Pere Cullera", + "deviceError": { + "cameraError": "No s'ha pogut accedir a la càmera", + "cameraPermission": "S'ha produït un error en obtenir el permís de la càmera", + "microphoneError": "No s'ha pogut accedir al micròfon", + "microphonePermission": "S'ha produït un error en obtenir el permís del micròfon" + }, + "deviceSelection": { + "noPermission": "No s'ha concedit el permís", + "previewUnavailable": "La previsualització no és disponible", + "selectADevice": "Seleccioneu un aparell", + "testAudio": "Reprodueix un so de prova" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Transmissió en directe" + }, + "allow": "Permet", + "alreadySharedVideoMsg": "Un altre participant està compartint un vídeo. Aquesta conferència només permet compartir un vídeo a la vegada.", + "alreadySharedVideoTitle": "Només es permet un vídeo compartit a la vegada", + "applicationWindow": "Finestra de l'aplicació", + "Back": "Enrere", + "cameraConstraintFailedError": "La càmera no satisfà algun dels requeriments.", + "cameraNotFoundError": "No s'ha trobat cap càmera.", + "cameraNotSendingData": "No podem accedir a la càmera. Comproveu si alguna una aplicació l'està usant, seleccioneu un altre aparell en el menú de configuració o intenteu de recarregar l'aplicació.", + "cameraNotSendingDataTitle": "No s'ha pogut accedir a la càmera", + "cameraPermissionDeniedError": "No heu concedit permís per a usar la càmera. Encara podeu unir-vos a la conferència però els altres participants no us veuran. Useu el botó de la càmera en la barra d'adreces per a corregir això.", + "cameraUnknownError": "Per algun motiu desconegut, no es pot usar la càmera.", + "cameraUnsupportedResolutionError": "La vostra càmera no permet la resolució de vídeo requerida.", + "Cancel": "Cancel·la", + "close": "Tanca", + "conferenceDisconnectMsg": "Potser vulgueu comprovar la connexió a la xarxa. Es tornarà a connecta en {{seconds}} segons…", + "conferenceDisconnectTitle": "Esteu desconnectat.", + "conferenceReloadMsg": "Estem intentat de corregir-ho. Tornem a connectar en {{seconds}} segons…", + "conferenceReloadTitle": "Malauradament, alguna cosa no ha anat bé.", + "confirm": "Confirmo", + "confirmNo": "No", + "confirmYes": "Sí", + "connectError": "Vaja! Alguna cosa no ha anat bé i no podem connectar a la conferència.", + "connectErrorWithMsg": "Vaja! Alguna cosa no ha anat bé i no podem connectar a la conferència: {{msg}}", + "connecting": "S'està connectant", + "contactSupport": "Contacte amb l'assistència", + "copy": "Copia", + "dismiss": "Descarta", + "displayNameRequired": "Hola! Com us dieu?", + "done": "Fet", + "enterDisplayName": "Introduïu aquí el vostre nom", + "error": "Error", + "externalInstallationMsg": "Cal que instal·leu l'extensió de compartició d'escriptori.", + "externalInstallationTitle": "Es requereix una extensió", + "goToStore": "Aneu a la botiga d'aplicacions web", + "gracefulShutdown": "El nostre servei ara mateix és en manteniment. Torneu-ho a intentar més tard.", + "IamHost": "Sóc l'amfitrió", + "incorrectRoomLockPassword": "La contrasenya no és correcta", + "incorrectPassword": "El nom o la contrasenya no són correctes", + "inlineInstallationMsg": "Cal que instal·leu l'extensió de compartició de l'extensió.", + "inlineInstallExtension": "Instal·la-ho ara", + "internalError": "Vaja! Alguna cosa no ha anat bé. S'ha produït l'error següent: {{error}}", + "internalErrorTitle": "Error intern", + "kickMessage": "Per a més detalls, podeu contactar amb {{participantDisplayName}}.", + "kickParticipantButton": "Expulsa", + "kickParticipantDialog": "Esteu segur que voleu expulsar aquest participant?", + "kickParticipantTitle": "Voleu expulsar aquest participant?", + "kickTitle": "Ep! {{participantDisplayName}} us ha expulsat de la reunió", + "liveStreaming": "Transmissió en directe", + "liveStreamingDisabledForGuestTooltip": "Els convidats no poden iniciar la transmissió en directe.", + "liveStreamingDisabledTooltip": "No es pot iniciar la transmissió en directe.", + "lockMessage": "No s'ha pogut blocar la conferència.", + "lockRoom": "Afegeix la reunió $t(lockRoomPasswordUppercase)", + "lockTitle": "El blocatge ha fallat", + "logoutQuestion": "Esteu segur de voler tancar la sessió i aturar la conferència?", + "logoutTitle": "Tanca la sessió", + "maxUsersLimitReached": "S'ha assolit el nombre màxim de participants. La conferència és plena. Contacteu amb el propietari de la reunió o torneu-ho a intentar més tard!", + "maxUsersLimitReachedTitle": "S'ha assolit el límit màxim de participants", + "micConstraintFailedError": "La càmera no satisfà algun dels requeriments.", + "micNotFoundError": "No s'ha trobat cap micròfon.", + "micNotSendingData": "Aneu a la configuració de l'ordinador per a activar el micròfon i ajusteu-ne el nivell", + "micNotSendingDataTitle": "El micròfon està silenciat en la configuració del sistema", + "micPermissionDeniedError": "No heu concedit permís per a usar el micròfon. Encara podeu unir-vos a la conferència però els altres participants no us sentiran. Useu el botó de micròfon en la barra d'adreces per a corregir això.", + "micUnknownError": "No es pot usar el micròfon per alguna raó desconeguda.", + "muteEveryoneElseDialog": "Una vegada silenciats, no podreu activar-ne els micròfons, però podran activar-lo ells mateixos en qualsevol moment.", + "muteEveryoneElseTitle": "Voleu silenciar tothom excepte {{whom}}?", + "muteEveryoneDialog": "Esteu segur que voleu silenciar tothom? No podreu activar-los el micròfon, però ells mateixos podran activar el micròfon respectiu en qualsevol moment.", + "muteEveryoneTitle": "Voleu silenciar tothom?", + "muteEveryoneSelf": "vós mateix", + "muteEveryoneStartMuted": "Tothom comença en silenci a partir d'ara", + "muteParticipantBody": "No podreu activar-ne els micròfons, però ells podran activar-ne el seu en qualsevol moment.", + "muteParticipantButton": "Silencia", + "muteParticipantDialog": "Esteu segur que voleu silenciar aquest participant? No podreu activar-li el micròfon, però sí que podrà fer-ho ell mateix en qualsevol moment.", + "muteParticipantTitle": "Voleu silenciar aquest participant?", + "Ok": "D'acord", + "passwordLabel": "Un participant ha blocat la reunió. Introduïu una $t(lockRoomPassword) per a unirvos-hi.", + "passwordNotSupported": "No és possible definir una $t(lockRoomPassword).", + "passwordNotSupportedTitle": "No se suporta la $t(lockRoomPassword)", + "passwordRequired": "Es requereix una $t(lockRoomPassword)", + "popupError": "El vostre navegador bloca les finestres emergents d'aquest lloc. Habiliteu les finestres emergents a la configuració de seguretat del navegador i torneu-ho a intentar.", + "popupErrorTitle": "Finestres emergents blocades", + "recording": "Enregistrament", + "recordingDisabledForGuestTooltip": "Els convidats no poden iniciar enregistraments.", + "recordingDisabledTooltip": "No es pot enregistrar.", + "rejoinNow": "Torna a entrar ara", + "remoteControlAllowedMessage": "{{user}} ha acceptat la petició de control remot!", + "remoteControlDeniedMessage": "{{user}} ha rebutjat la petició de control remot!", + "remoteControlErrorMessage": "S'ha produït un error en intentar sol·licitar a {{user}} permisos de control remot!", + "remoteControlRequestMessage": "Voleu permetre a {{user}} de controlar remotament el vostre escriptori?", + "remoteControlShareScreenWarning": "Tingueu present que si pitgeu \"Permet\" compartireu la vostra pantalla!", + "remoteControlStopMessage": "La sessió de control remot ha finalitzat!", + "remoteControlTitle": "Control d'escriptori remot", + "Remove": "Suprimeix", + "removePassword": "Suprimeix la $t(lockRoomPassword)", + "removeSharedVideoMsg": "Esteu segur que voleu suprimir el vídeo compartit?", + "removeSharedVideoTitle": "Suprimeix aquest vídeo compartit", + "reservationError": "S'ha produït un error de reserva de sistema", + "reservationErrorMsg": "Codi d'error: {{code}}, missatge: {{msg}}", + "retry": "Torna a intentar-ho", + "screenSharingFailedToInstall": "Vaja! No s'ha pogut instal·lar l'extensió de compartició de pantalla.", + "screenSharingFailedToInstallTitle": "No s'ha pogut instal·lar l'extensió de compartició de pantalla", + "screenSharingFirefoxPermissionDeniedError": "Alguna cosa no ha anat bé en intentar compartir la pantalla. Assegureu-vos que heu donat el permís per a fer-ho. ", + "screenSharingFirefoxPermissionDeniedTitle": "Vaja! No hem pogut iniciar la compartició de pantalla!", + "screenSharingPermissionDeniedError": "Vaja! Alguna cosa no ha anat bé amb els permisos de l'extensió de compartició de pantalla. Torneu a carregar i intenteu-ho una altra vegada.", + "sendPrivateMessage": "Fa poc que heu rebut un missatge privat. Voleu respondre'l de forma privada, o voleu enviar el missatge al grup?", + "sendPrivateMessageCancel": "Envia'l al grup", + "sendPrivateMessageOk": "Envia'l en privat", + "sendPrivateMessageTitle": "Voleu enviar-lo en privat?", + "serviceUnavailable": "El servei no és disponible", + "sessTerminated": "La trucada ha finalitzat", + "Share": "Comparteix", + "shareVideoLinkError": "Proporcioneu un enllaç de YouTube correcte.", + "shareVideoTitle": "Comparteix un vídeo", + "shareYourScreen": "Comparteix la pantalla", + "shareYourScreenDisabled": "S'ha inhabilitat la compartició de pantalla.", + "shareYourScreenDisabledForGuest": "Els convidats no poden compartir la pantalla.", + "startLiveStreaming": "Inicia la transmissió en directe", + "startRecording": "Inicia l'enregistrament", + "startRemoteControlErrorMessage": "S'ha produït un error en intentar iniciar la sessió de control remot!", + "stopLiveStreaming": "Atura la transmissió en directe", + "stopRecording": "Atura l'enregistrament", + "stopRecordingWarning": "Esteu segur de voler aturar l'enregistrament?", + "stopStreamingWarning": "Esteu segur de voler aturar la transmissió en directe?", + "streamKey": "Clau de transmissió en directe", + "Submit": "Tramet", + "thankYou": "Gràcies per emprar {{appName}}!", + "token": "identificador", + "tokenAuthFailed": "No esteu autoritzat a unir-vos a aquesta trucada.", + "tokenAuthFailedTitle": "L'autenticació ha fallat", + "transcribing": "Transcripció", + "unlockRoom": "Suprimeix la $t(lockRoomPassword) de la reunió", + "userPassword": "contrasenya d'usuari", + "WaitForHostMsg": "La conferència <b>{{room}}</b> encara no ha començat. Si en sou l'amfitrió autentiqueu-vos. Altrament, espereu que arribi l'amfitrió.", + "WaitForHostMsgWOk": "La conferència <b>{{room}}</b> encara no ha començat. Si sou l'amfitrió aleshores pitgeu «D'acord» per a autenticar-vos. Altrament, espereu que arribi l'amfitrió.", + "WaitingForHost": "S'està esperant l'amfitrió…", + "Yes": "Sí", + "yourEntireScreen": "La pantalla sencera", + "screenSharingAudio": "Comparteix l'àudio" + }, + "dialOut": { + "statusMessage": "ara és {{status}}" + }, + "documentSharing": { + "title": "Document compartit" + }, + "feedback": { + "average": "Mitjana", + "bad": "Dolenta", + "detailsLabel": "Expliqueu-nos més sobre això.", + "good": "Bona", + "rateExperience": "Valoreu l'experiència de reunió", + "veryBad": "Molt dolenta", + "veryGood": "Molt bona" + }, + "incomingCall": { + "answer": "Resposta", + "audioCallTitle": "Trucada entrant", + "decline": "Rebutja", + "productLabel": "de Jitsi Meet", + "videoCallTitle": "Videotrucada entrant" + }, + "info": { + "accessibilityLabel": "Mostra la informació", + "addPassword": "Afegeix una $t(lockRoomPassword)", + "cancelPassword": "Cancel·la $t(lockRoomPassword)", + "conferenceURL": "Enllaç:", + "country": "País", + "dialANumber": "Per a unir-vos a la reunió, marqueu un d'aquests números i aleshores introduïu el PIN.", + "dialInConferenceID": "PIN:", + "dialInNotSupported": "El marcatge ara mateix no és permés.", + "dialInNumber": "Marcatge:", + "dialInSummaryError": "S'ha produït un error en obtenir la informació de marcatge. Torneu-ho a intentar més tard.", + "dialInTollFree": "Sense peatges", + "genericError": "Vaja, alguna cosa no ha anat bé.", + "inviteLiveStream": "Per a veure la transmissió en directe d'aquesta reunió, feu clic en aquest enllaç: {{url}}", + "invitePhone": "Per a unir-vos per telèfon, toqueu això: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Esteu cercant un número de marcatge diferent?\nVegeu els números de marcatge de la reunió: {{url}}\n\n\nSi també truqueu via un telèfon de reunió, uniu-vos-hi sense connectar l'àudio: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Us han convidat a unir-vos a una reunió.", + "inviteURLFirstPartPersonal": "{{name}} us convida a una reunió.\n", + "inviteURLSecondPart": "\nUniu-vos a la reunió:\n{{url}}\n", + "liveStreamURL": "Transmissió en directe:", + "moreNumbers": "Més números", + "noNumbers": "Sense números de marcatge.", + "noPassword": "Cap", + "noRoom": "No s'ha indicat cap sala a què marcar.", + "numbers": "Números de marcatge", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Comparteix", + "tooltip": "Comparteix l'enllaç i la informació de marcatge d'aquesta reunió", + "label": "Informació de la reunió" + }, + "inviteDialog": { + "alertText": "No s'ha pogut convidar alguns participants.", + "header": "Convida", + "searchCallOnlyPlaceholder": "Introduïu el número de telèfon", + "searchPeopleOnlyPlaceholder": "Cerca participants", + "searchPlaceholder": "Participant o número de telèfon", + "send": "Envia" + }, + "inlineDialogFailure": { + "msg": "Vam ensopegar una mica.", + "retry": "Torneu-ho a intentar", + "support": "Assistència", + "supportMsg": "Si segueix passant, feu-nos-ho saber" + }, + "keyboardShortcuts": { + "focusLocal": "Focus al vostre vídeo", + "focusRemote": "Focus en el vídeo d'una altra persona", + "fullScreen": "Entra o surt de la pantalla completa", + "keyboardShortcuts": "Dreceres de teclat", + "localRecording": "Mostra o amaga els controls d'enregistrament local", + "mute": "Silencia o activa el micròfon", + "pushToTalk": "Premeu per a parlar", + "raiseHand": "Aixeca o abaixa la mà", + "showSpeakerStats": "Mostra les estadístiques de l'interlocutor", + "toggleChat": "Obre o tanca el xat", + "toggleFilmstrip": "Mostra o amaga les miniatures de vídeo", + "toggleScreensharing": "Canvia entre la càmera i la compartició de pantalla", + "toggleShortcuts": "Mostra o amaga les dreceres de teclat", + "videoMute": "Inicia o atura la vostra càmara", + "videoQuality": "Gestiona la qualitat de la trucada" + }, + "liveStreaming": { + "busy": "Treballem per a alliberar recursos de transmissió. Torneu-ho a intentar en uns minuts.", + "busyTitle": "Ara mateix, tots els reproductors són ocupats", + "changeSignIn": "Canvia entre comptes.", + "choose": "Trieu una transmissió en directe", + "chooseCTA": "Trieu una opció de transmissió. Ara mateix teniu sessió iniciada com a {{email}}.", + "enterStreamKey": "Introduïu la clau YouTube de transmissió en directe aquí.", + "error": "La transmissió en directe ha fallat. Torneu-ho a intentar més tard.", + "errorAPI": "S'ha produït un error en accedir a les vostres emissions de Youtube. Torneu a iniciar sessió una altra vegada.", + "errorLiveStreamNotEnabled": "La transmissió en directe no està activada a ̣{{email}}. Activeu-la o inicieu sessió en un compte que tingui la transmissió en directe activada.", + "expandedOff": "S'ha aturat la transmissió en directe", + "expandedOn": "Ara mateix, la reunió s'està transmetent a Youtube.", + "expandedPending": "S'ha iniciat la transmissió en directe…", + "failedToStart": "No s'ha pogut iniciar la transmissió en directe", + "getStreamKeyManually": "No hem pogut obtenir cap transmissió en directe. Intenteu d'obtenir la clau de transmissió en directe del YouTube.", + "invalidStreamKey": "La clau de transmissió en directe pot ser incorrecta.", + "off": "S'ha aturat la transmissió en directe", + "offBy": "{{name}} ha aturat la transmissió en directe", + "on": "Transmissió en directe", + "onBy": "{{name}} ha iniciat la transmissió en directe", + "pending": "S'està iniciant la transmissió en directe…", + "serviceName": "Servei de transmissió en directe", + "signedInAs": "Teniu sessió iniciada com a:", + "signIn": "Inicia sessió amb Google", + "signInCTA": "Inicieu sessió o introduïu la clau de transmissió en directe de YouTube.", + "signOut": "Tanca la sessió", + "start": "Inicia la transmissió en directe", + "streamIdHelp": "Què és això?", + "unavailableTitle": "La transmissió en directe no és disponible", + "googlePrivacyPolicy": "Política de privadesa de Google", + "youtubeTerms": "Condicions de servei del Youtube" + }, + "localRecording": { + "clientState": { + "off": "Inactiu", + "on": "Actiu", + "unknown": "Desconegut" + }, + "dialogTitle": "Controls d'enregistrament local", + "duration": "Durada", + "durationNA": "N/D", + "encoding": "Codificació", + "label": "ENREG LOC", + "labelToolTip": "L'enregistrament local està funcionant", + "localRecording": "Enregistrament local", + "me": "Jo", + "messages": { + "engaged": "L'enregistrament local funciona.", + "finished": "La sessió d'enregistrament {{token}} ha finalitzat. Envieu el fitxer enregistrat al moderador.", + "finishedModerator": "La sessió d'enregistrament {{token}} ha finalitzat. L'enregistrament de la part local s'ha desat. Demaneu als altres participants que enviïn els seus enregistraments.", + "notModerator": "No sou el moderador. No podeu iniciar ni aturar un enregistrament local." + }, + "moderator": "Moderador", + "no": "No", + "participant": "Participant", + "participantStats": "Estadístiques del participant", + "sessionToken": "Identificador de la sessió", + "start": "Inicia l'enregistrament", + "stop": "Atura l'enregistrament", + "yes": "Sí" + }, + "lockRoomPassword": "contrasenya", + "lockRoomPasswordUppercase": "Contrasenya", + "me": "jo", + "notify": { + "connectedOneMember": "{{name}} s'ha unit a la reunió", + "connectedThreePlusMembers": "{{name}} i {{count}} persones més s'han unit a la reunió", + "connectedTwoMembers": "{{first}} i {{second}} s'han unit a la reunió", + "disconnected": "desconnectat", + "focus": "Focus de la conferència", + "focusFail": "{{component}} no és disponible, torneu a intentar en {{ms}} segons", + "grantedTo": "S'han concedit permisos de moderador a {{to}}!", + "invitedOneMember": "S'ha convidat {{name}}", + "invitedThreePlusMembers": "S'han convidat {{name}} i {{count}} participants més", + "invitedTwoMembers": "S'han convidat {{first}} i {{second}}", + "kickParticipant": "{{kicker}} ha expulsat {{kicked}}", + "me": "Jo", + "moderator": "S'han concedit permisos de moderador!", + "muted": "Heu iniciat una conversa silenciada.", + "mutedTitle": "Esteu silenciat!", + "mutedRemotelyTitle": "Heu estat silenciat per {{participantDisplayName}}!", + "mutedRemotelyDescription": "Sempre podeu activar el micròfon quan hàgiu de parlar. Torneu a silenciar-lo quan hàgiu acabat per a mantenir el soroll lluny de la reunió.", + "passwordRemovedRemotely": "Un altre participant ha suprimit $t(lockRoomPasswordUppercase)", + "passwordSetRemotely": "Un altre participant ha establert la $t(lockRoomPassword)", + "raisedHand": "{{name}} vol parlar.", + "somebody": "Algú", + "startSilentTitle": "Us hi heu unit sense cap sortida d'àudio!", + "startSilentDescription": "Torneu a entrar per a activar l'àudio", + "suboptimalBrowserWarning": "Ens sap greu que la vostra experiència de reunió aquí no serà gaire bona. Cerquem maneres per a millorar-la, però fins aleshores, proveu de fer servir algun dels <a href='static/recommendedBrowsers.html' target='_blank'>navegadors completament compatibles</a>.", + "suboptimalExperienceTitle": "Avís del navegador", + "unmute": "Activa el so", + "newDeviceCameraTitle": "S'ha detectat una càmera nova", + "newDeviceAudioTitle": "S'ha detectat un aparell d'àudio nou", + "newDeviceAction": "Usa" + }, + "passwordSetRemotely": "establerta per una altre participant", + "passwordDigitsOnly": "Fins a {{number}} dígits", + "poweredby": "funciona amb", + "presenceStatus": { + "busy": "Ocupat", + "calling": "S'està trucant…", + "connected": "Connectat", + "connecting": "Està connectant…", + "connecting2": "Està connectant*...", + "disconnected": "Desconnectat", + "expired": "Ha expirat", + "ignored": "Ignorat", + "initializingCall": "S'està inicialitzant la trucada...", + "invited": "Convidat", + "rejected": "Rebutjat", + "ringing": "Està sonat..." + }, + "profile": { + "setDisplayNameLabel": "Indiqueu el nom visible", + "setEmailInput": "Introduïu una adreça electrònica", + "setEmailLabel": "Indiqueu l'adreça electrònica de Gravatar", + "title": "Perfil" + }, + "raisedHand": "Vull parlar", + "recording": { + "authDropboxText": "Puja a Dropbox", + "availableSpace": "Espai disponible: {{spaceLeft}} MB (aproximadament {{duration}} minuts d'enregistrament)", + "beta": "BETA", + "busy": "Treballem per a alliberar recursos d'enregistrament. Torneu-ho a intentar en uns minuts.", + "busyTitle": "Tots els enregistradors estan ocupats", + "error": "L'enregistrament ha fallat. Torneu-ho a intentar més tard.", + "expandedOff": "S'ha aturat l'enregistrament", + "expandedOn": "S'està enregistrant la reunió.", + "expandedPending": "S'ha iniciat l'enregistrament...", + "failedToStart": "No s'ha pogut iniciar l'enregistrament", + "fileSharingdescription": "Comparteix l'enregistrament amb els participants de la reunió", + "live": "EN DIRECTE", + "loggedIn": "Sessió iniciada com a {{userName}}", + "off": "S'ha aturat l'enregistrament", + "offBy": "{{name}} ha aturat l'enregistrament", + "on": "Enregistrament", + "onBy": "{{name}} ha iniciat l'enregistrament", + "pending": "S'està preparant per a enregistrar la reunió...", + "rec": "ENREG", + "serviceDescription": "El servei d'enregistrament desarà el vostre enregistrament", + "serviceName": "Servei d'enregistrament", + "signIn": "Inicia la sessió", + "signOut": "Tanca la sessió", + "unavailable": "Vaja! El servei {{serviceName}} ara mateix no és disponible. Treballem per a resoldre el problema. Torneu-ho a intentar més tard.", + "unavailableTitle": "L'enregistrament no és disponible" + }, + "sectionList": { + "pullToRefresh": "Estireu per a actualitzar" + }, + "settings": { + "calendar": { + "about": "La integració de l'agenda {{appName}} s'usa per a accedir de forma segura a la vostra agenda perquè pugui llegir els esdeveniments propers.", + "disconnect": "Desconnectat", + "microsoftSignIn": "Inicia sessió amb Microsoft", + "signedIn": "Ara mateix s'està accedint els esdeveniments de l'agenda de {{email}}. Feu clic al botó «Desconnecta» per a deixar d'accedir als esdeveniments de l'agenda.", + "title": "Agenda" + }, + "devices": "Aparells", + "followMe": "Tothom em segueix", + "language": "Llengua", + "loggedIn": "Sessió iniciada com a {{name}}", + "moderator": "Moderador", + "more": "Més", + "name": "Nom", + "noDevice": "Cap", + "selectAudioOutput": "Sortida d'àudio", + "selectCamera": "Càmera", + "selectMic": "Micròfon", + "startAudioMuted": "Tothom comença silenciat", + "startVideoMuted": "Tothom comença amagat", + "title": "Configuració", + "speakers": "Altaveus", + "microphones": "Micròfons" + }, + "settingsView": { + "advanced": "Avançat", + "alertOk": "D'acord", + "alertTitle": "Avís", + "alertURLText": "L'URL introduït no és vàlid", + "buildInfoSection": "Informació de la construcció", + "conferenceSection": "Conferència", + "disableCallIntegration": "Desactiva la integració de trucades nativa", + "disableP2P": "Desactiva el mode d'igual a igual", + "displayName": "Nom visible", + "email": "Adreça electrònica", + "header": "Configuració", + "profileSection": "Perfil", + "serverURL": "URL del servidor", + "showAdvanced": "Mostra la configuració avançada", + "startWithAudioMuted": "Comença amb l'àudio silenciat", + "startWithVideoMuted": "Comença amb el vídeo desactivat", + "version": "Versió" + }, + "share": { + "dialInfoText": "\n\n=====\n\nNomés voleu marcar en el telèfon?\n\n{{defaultDialInNumber}}Feu clic en aquest enllaç per a veure el marcatge de números de telèfon per a aquesta reunió\n{{dialInfoPageUrl}}", + "mainText": "Feu clic a l'enllaç següent per a unir-vos a la reunió:\n{{roomUrl}}" + }, + "speaker": "Altaveu", + "speakerStats": { + "hours": "{{count}}h", + "minutes": "{{count}}m", + "name": "Nom", + "seconds": "{{count}}s", + "speakerStats": "Estadístiques de l'interlocutor", + "speakerTime": "Temps de l'interlocutor" + }, + "startupoverlay": { + "title": "{{app}} requereix usar el micròfon i la càmera.", + "policyText": " " + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Torna a entrar", + "text": "Premeu el botó <i>Torna a entrar</i> per a tornar a connectar.", + "title": "La videotrucada s'ha interromput perquè l'ordinador ha entrat en mode repòs." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Activa o desactiva només l'àudio", + "audioRoute": "Seleccioneu l'aparell de so", + "callQuality": "Gestiona la qualitat de la trucada", + "cc": "Activa o desactiva els subtítols", + "chat": "Activa o desactiva la finestra de xats", + "document": "Activa o desactiva el document compartit", + "download": "Baixeu les nostres aplicacions", + "feedback": "Deixa comentaris", + "fullScreen": "Activa o desactiva la pantalla completa", + "hangup": "Abandona la trucada", + "help": "Ajuda", + "invite": "Convida-hi persones", + "kick": "Expulsa el participant", + "localRecording": "Activa o desactiva les controls d'enregistrament local", + "lockRoom": "Activa o desactiva la contrasenya de la reunió", + "moreActions": "Activa o desactiva el menú d'accions addicionals", + "moreActionsMenu": "Menú d'accions addicionals", + "moreOptions": "Mostra més opcions", + "mute": "Activa o desactiva el silenci de l'àudio", + "muteEveryone": "Silencia tothom", + "pip": "Activa o desactiva el mode imatge en imatge", + "privateMessage": "Envia un missatge privat", + "profile": "Edita el perfil", + "raiseHand": "Aixeca o abaixa la mà", + "recording": "Activa o desactiva l'enregistrament", + "remoteMute": "Silencia el participant", + "Settings": "Activa o desactiva la configuració", + "sharedvideo": "Activa o desactiva la compartició de vídeo", + "shareRoom": "Convida-hi algú", + "shareYourScreen": "Activa o desactiva la compartició de pantalla", + "shortcuts": "Activa o desactiva les dreceres", + "show": "Mostra-ho en l'escena", + "speakerStats": "Activa o desactiva les estadístiques de l'interlocutor", + "tileView": "Activa o desactiva el mode mosaic", + "toggleCamera": "Activa o desactiva la càmera", + "videomute": "Activa o desactiva el vídeo", + "videoblur": "Activa o desactiva el difuminat", + "toggleFilmstrip": "Activa o desactiva la tira" + }, + "addPeople": "Afegeix persones a la trucada", + "audioOnlyOff": "Desactiva el mode de poc ample de banda", + "audioOnlyOn": "Activa el mode de poc ample de banda", + "audioRoute": "Seleccioneu l'aparell de so", + "authenticate": "Autentica", + "callQuality": "Gestiona la qualitat de la trucada", + "chat": "Obre o tanca el xat", + "closeChat": "Tanca el xat", + "documentClose": "Tanca el document compartit", + "documentOpen": "Obre el document compartit", + "download": "Baixeu les nostres aplicacions", + "enterFullScreen": "Mostra en pantalla completa", + "enterTileView": "Mostra en mode mosaic", + "exitFullScreen": "Surt de la pantalla completa", + "exitTileView": "Surt del mode mosaic", + "feedback": "Deixa comentaris", + "hangup": "Surt", + "help": "Ajuda", + "invite": "Convida-hi persones", + "login": "Inicia sessió", + "logout": "Tanca la sessió", + "lowerYourHand": "Abaixa la mà", + "moreActions": "Més accions", + "moreOptions": "Més opcions", + "mute": "Silencia o activa el so", + "muteEveryone": "Silencia tothom", + "noAudioSignalTitle": "No entra cap so pel vostre micròfon!", + "noAudioSignalDesc": "Si no l'heu silenciat intencionadament en la configuració del sistema o per maquinari, considereu canviar l'aparell.", + "noAudioSignalDescSuggestion": "Si no l'heu silenciat intencionadament en la configuració del sistema o per maquinari, considereu canviar a l'aparell suggerit.", + "noAudioSignalDialInDesc": "També podeu marcar usant:", + "noAudioSignalDialInLinkDesc": "Números de marcatge", + "noisyAudioInputTitle": "Sembla que el micròfon fa soroll!", + "noisyAudioInputDesc": "Sembla que el vostre micròfon fa soroll, considereu de silenciar-lo o canviar l'aparell.", + "openChat": "Obre el xat", + "pip": "Entra en el mode imatge en imatge", + "privateMessage": "Envia un missatge privat", + "profile": "Edita el perfil", + "raiseHand": "Aixeca o abaixa la mà", + "raiseYourHand": "Aixeca la mà", + "Settings": "Configuració", + "sharedvideo": "Comparteix un vídeo de YouTube", + "shareRoom": "Convida-hi algú", + "shortcuts": "Mostra les dreceres", + "speakerStats": "Estadístiques de l'interlocutor", + "startScreenSharing": "Atura la compartició de la pantalla", + "startSubtitles": "Inicia els subtítols", + "stopScreenSharing": "Atura la compartició de la pantalla", + "stopSubtitles": "Atura els subtítols", + "stopSharedVideo": "Atura el vídeo de YouTube", + "talkWhileMutedPopup": "Intenteu parlar? Esteu silenciat.", + "tileViewToggle": "Activa o desactiva el mode mosaic", + "toggleCamera": "Activa o desactiva la càmera", + "videomute": "Inicia o atura la càmera", + "startvideoblur": "Difumina el fons", + "stopvideoblur": "No difuminis el fons" + }, + "transcribing": { + "ccButtonTooltip": "Inicia o atura els subtítols", + "error": "La transcripció ha fallat. Torneu-ho a intentar més tard.", + "expandedLabel": "La transcripció està activada", + "failedToStart": "No s'ha pogut iniciar la transcripció", + "labelToolTip": "La reunió s'està transcrivint", + "off": "La transcripció s'ha aturat", + "pending": "S'està preparant per a enregistrar la reunió…", + "start": "Activa els subtítols", + "stop": "Desactiva els subtítols", + "tr": "TR" + }, + "userMedia": { + "androidGrantPermissions": "Seleccioneu <b><i>Permet</i></b> si el navegador us sol·licita permisos.", + "chromeGrantPermissions": "Seleccioneu <b><i>Permet</i></b> si el navegador us sol·licita permisos.", + "edgeGrantPermissions": "Seleccioneu <b><i>Sí</i></b> si el navegador us sol·licita permisos.", + "electronGrantPermissions": "Concediu permisos per a usar la càmera i el micròfon", + "firefoxGrantPermissions": "Seleccioneu <b><i>Comparteix l'apareix seleccionat</i></b> si el navegador us sol·licita permisos.", + "iexplorerGrantPermissions": "Seleccioneu <b><i>D'acord</i></b> si el navegador us sol·licita permisos.", + "nwjsGrantPermissions": "Concediu permisos per a usar la càmera i el micròfon", + "operaGrantPermissions": "Seleccioneu <b><i>Permet</i></b> si el navegador us sol·licita permisos.", + "react-nativeGrantPermissions": "Seleccioneu <b><i>Permet</i></b> si el navegador us sol·licita permisos.", + "safariGrantPermissions": "Seleccioneu <b><i>D'acord</i></b> si el navegador us sol·licita permisos." + }, + "videoSIPGW": { + "busy": "Treballem per a alliberar recursos. Torneu-ho a intentar en uns minuts.", + "busyTitle": "Ara mateix, el servei Room és ocupat", + "errorAlreadyInvited": "Ja s'ha convidat {{displayName}}", + "errorInvite": "Encara no s'ha establert la connexió. Torneu-ho a intentar més tard.", + "errorInviteFailed": "Treballem per a resoldre el problema. Torneu-ho a intentar més tard.", + "errorInviteFailedTitle": "No s'ha pogut convidar {{displayName}}", + "errorInviteTitle": "S'ha produït un error en la sala d'invitació", + "pending": "{{displayName}} us ha convidat" + }, + "videoStatus": { + "audioOnly": "AUD", + "audioOnlyExpanded": "Ara sou en el mode de poc ample de banda. En aquest mode només rebreu àudio i compartició de pantalla.", + "callQuality": "Qualitat de vídeo", + "hd": "HD", + "hdTooltip": "Vídeo en alta definició", + "highDefinition": "Alta definició", + "labelTooiltipNoVideo": "No hi ha vídeo", + "labelTooltipAudioOnly": "S'ha activat el mode de poc ample de banda", + "ld": "LD", + "ldTooltip": "Vídeo en baixa definició", + "lowDefinition": "Baixa definició", + "onlyAudioAvailable": "Només hi ha disponible l'àudio", + "onlyAudioSupported": "Només es permet àudio en aquest navegador.", + "p2pEnabled": "Mode d'igual a igual activat", + "p2pVideoQualityDescription": "En el mode d'igual a igual, la qualitat del vídeo rebut només es pot canviar entre alta i només àudio. La resta de configuracions no es compliran fins sortir del mode d'igual a igual.", + "recHighDefinitionOnly": "L'alta definició serà preferent.", + "sd": "SD", + "sdTooltip": "Vídeo en definició estàndard", + "standardDefinition": "Definició estàndard" + }, + "videothumbnail": { + "domute": "Silencia", + "domuteOthers": "Silencia a tots els altres participants", + "flip": "Capgira", + "kick": "Expulsa", + "moderator": "Moderador", + "mute": "El participant és silenciat", + "muted": "Silenciat", + "remoteControl": "Inicia o atura el control remot", + "show": "Mostra-ho en l'escena", + "videomute": "El participant ha aturat la càmera" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Toqueu per a unir-vos-hi", + "roomname": "Introduïu el nom de la sala" + }, + "appDescription": "Endavant, xat de vídeo amb tot l'equip. De fet, convideu tothom que conegueu. {{app}} és una solució de videoconferència de codi obert 100% completament xifrada que podeu utilitzar durant tot el dia, tots els dies, gratuïtament, sense necessitat de compte.", + "audioVideoSwitch": { + "audio": "Veu", + "video": "Vídeo" + }, + "calendar": "Agenda", + "connectCalendarButton": "Connecteu la vostra agenda", + "connectCalendarText": "Connecteu l'agenda per a veure totes les reunions en {{app}}. Més, afegiu les reunions {{provider}} a la vostra agenda i inicieu-les amb un clic.", + "enterRoomTitle": "Inicia una reunió nova", + "roomNameAllowedChars": "El nom de la reunió no hauria de contenir cap d'aquests caràcters: ?, &, :, ', \", % ni #.", + "go": "SOM-HI", + "goSmall": "SOM-HI", + "join": "CREA / ENTRA", + "info": "Informació", + "privacy": "Privadesa", + "recentList": "Recents", + "recentListDelete": "Suprimeix", + "recentListEmpty": "La llista de reunions recents ara mateix és buida. Feu xats amb el vostre equip i trobareu totes les reunions recents aquí.", + "reducedUIText": "Us donem la benvinguda a {{app}}!", + "roomname": "Introduïu el nom de la sala", + "roomnameHint": "Introduïu el nom o l'URL de la sala on voleu entrar. Podeu crear un nom, només cal que les persones amb qui us reuniu el coneguin i introdueixin el mateix nom.", + "sendFeedback": "Envia comentaris", + "terms": "Condicions", + "title": "Videoconferència segura, plena de funcionalitats i completament gratuïta i lliure", + "getHelp": "Ajuda" + }, + "lonelyMeetingExperience": { + "button": "Convideu altres persones", + "youAreAlone": "Sou l'únic participant de la reunió" + }, + "helpView": { + "header": "Centre d'ajuda" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-de.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-de.json index 5b415f798..42d385165 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-de.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-de.json @@ -20,46 +20,56 @@ "audioDevices": { "bluetooth": "Bluetooth", "headphones": "Kopfhörer", - "phone": "Telefon", - "speaker": "Sprecher" + "phone": "Hörer", + "speaker": "Lautsprecher", + "none": "Keine Audiogeräte verfügbar" }, "audioOnly": { - "audioOnly": "Nur Audio" + "audioOnly": "Geringe Bandbreite" }, "calendarSync": { "addMeetingURL": "Meeting-Link hinzufügen", - "confirmAddLink": "Wollen Sie einen Jitsi-Link zu dieser Veranstaltung hinzufügen?", + "confirmAddLink": "Möchten Sie einen Jitsi-Link zu diesem Termin hinzufügen?", "error": { "appConfiguration": "Kalenderintegration ist nicht richtig konfiguriert.", "generic": "Ein Fehler ist aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, den Kalender zu aktualisieren.", - "notSignedIn": "Ein Fehler ist während der Authentifizierung zur Anzeige von Kalendererveranstaltungen aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden." + "notSignedIn": "Ein Fehler ist während der Authentifizierung zur Anzeige von Kalenderterminen aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden." }, "join": "Teilnehmen", "joinTooltip": "Am Meeting teilnehmen", "nextMeeting": "Nächste Konferenz", - "noEvents": "Es sind keine bevorstehenden Veranstaltungen geplant.", + "noEvents": "Es gibt keine bevorstehenden Termine.", "ongoingMeeting": "Laufendes Meeting", "permissionButton": "Einstellungen öffnen", - "permissionMessage": "Die App benötigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen.", + "permissionMessage": "Die App benötigt Zugriff auf den Kalender, um Termine und Konferenzen anzuzeigen.", "refresh": "Kalender aktualisieren", "today": "Heute" }, - "\u0005calendarSync": {}, "chat": { - "error": "", + "error": "Fehler: Ihre Nachricht wurde nicht versendet. Grund: {{error}}", + "fieldPlaceHolder": "Geben Sie Ihre Nachricht hier ein", "messagebox": "Nachricht eingeben", + "messageTo": "Private Nachricht an {{recipient}}", + "noMessagesMessage": "Es gibt noch keine Nachricht in dieser Konferenz. Starten Sie hier eine Unterhaltung!", "nickname": { "popover": "Name", "title": "Geben Sie einen Alias zum Chatten ein" }, - "title": "Chatten" + "privateNotice": "Private Nachricht an {{recipient}}", + "title": "Chatten", + "you": "Sie" + }, + "chromeExtensionBanner": { + "installExtensionText": "Installieren Sie die Erweiterung für die Integration von Google Calendar und Office 365", + "buttonText": "Chrome-Erweiterung installieren", + "dontShowAgain": "Hinweis nicht mehr anzeigen" }, "connectingOverlay": { "joiningRoom": "Eine Verbindung zu Ihrem Meeting wird hergestellt…" }, "connection": { "ATTACHED": "Angehängt", - "AUTHENTICATING": "Anmeldung läuft", + "AUTHENTICATING": "Authentifizierung läuft", "AUTHFAIL": "Authentifizierung fehlgeschlagen", "CONNECTED": "Verbunden", "CONNECTING": "Verbindung wird hergestellt", @@ -67,7 +77,10 @@ "DISCONNECTED": "Getrennt", "DISCONNECTING": "Verbindung wird getrennt", "ERROR": "Fehler", - "RECONNECTING": "Es ist ein Netzwerkproblem aufgetreten. Verbinde..." + "FETCH_SESSION_ID": "Sitzungs-ID erhalten...", + "GET_SESSION_ID_ERROR": "Sitzungs-ID-Fehler erhalten: {{code}}", + "GOT_SESSION_ID": "Sitzungs-ID erhalten... Beendet", + "LOW_BANDWIDTH": "Video für {{displayName}} wurde ausgeschaltet, um Bandbreite einzusparen" }, "connectionindicator": { "address": "Adresse:", @@ -75,6 +88,7 @@ "bitrate": "Bitrate:", "bridgeCount": "Serverzahl: ", "connectedTo": "Verbunden mit:", + "e2e_rtt": "E2E RTT:", "framerate": "Bildwiederholrate:", "less": "Weniger anzeigen", "localaddress": "Lokale Adresse:", @@ -97,7 +111,7 @@ "resolution": "Auflösung:", "status": "Verbindung:", "transport": "Protokoll:", - "turn": " (TURN)" + "transport_plural": "Protokolle:" }, "dateUtils": { "earlier": "Früher", @@ -105,9 +119,9 @@ "yesterday": "Gestern" }, "deepLinking": { - "appNotInstalled": "Sie benötigen die {{app}} App um der Konferenz auf dem Smartphone beizutreten.", - "description": "Nichts passiert? Wir haben versucht die Konferenz in {{app}} zu öffnen. Versuchen Sie es erneut oder treten Sie der Konferenz in {{app}} im Web bei.", - "descriptionWithoutWeb": "", + "appNotInstalled": "Sie benötigen die „{{app}}“-App, um der Konferenz auf dem Smartphone beizutreten.", + "description": "Nichts passiert? Wir haben versucht, die Konferenz in {{app}} zu öffnen. Versuchen Sie es erneut oder treten Sie der Konferenz in {{app}} im Web bei.", + "descriptionWithoutWeb": "Ist nichts passiert? Wir haben versucht, Ihre Besprechung in der „{{app}}“-Desktop-App zu starten.", "downloadApp": "App herunterladen", "launchWebButton": "Im Web öffnen", "openApp": "In der App fortfahren", @@ -115,6 +129,7 @@ "tryAgainButton": "Erneut mit der nativen Applikation versuchen" }, "defaultLink": "Bsp.: {{url}}", + "defaultNickname": "Z.B. Jane Pink", "deviceError": { "cameraError": "Fehler beim Zugriff auf die Kamera", "cameraPermission": "Fehler beim Bezug der Kamera-Zugriffsberechtigungen", @@ -129,22 +144,22 @@ }, "dialog": { "accessibilityLabel": { - "liveStreaming": "Livestream:" + "liveStreaming": "Livestream" }, "allow": "Erlauben", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Ein anderer Teilnehmer gibt bereits ein Video weiter. Bei dieser Konferenz ist jeweils nur ein geteiltes Video möglich.", "alreadySharedVideoTitle": "Nur ein geteiltes Video gleichzeitig", "applicationWindow": "Anwendungsfenster", "Back": "Zurück", "cameraConstraintFailedError": "Ihre Kamera erfüllt die notwendigen Anforderungen nicht.", "cameraNotFoundError": "Kamera nicht gefunden.", - "cameraNotSendingData": "Die Kamera ist nicht verfügbar. Bitte prüfen ob eine andere Applikation die Kamera verwendet, eine andere Kamera vom Einstellungs-Menu auswählen oder die Applikation neu laden.", + "cameraNotSendingData": "Die Kamera ist nicht verfügbar. Bitte prüfen, ob eine andere Applikation die Kamera verwendet, eine andere Kamera vom Einstellungs-Menü auswählen oder die Applikation neu laden.", "cameraNotSendingDataTitle": "Zugriff auf Kamera nicht möglich", - "cameraPermissionDeniedError": "Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht sehen. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.", + "cameraPermissionDeniedError": "Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht sehen. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste, um die Berechtigungen zu erteilen.", "cameraUnknownError": "Die Kamera kann aus einem unbekannten Grund nicht verwendet werden.", "cameraUnsupportedResolutionError": "Die Kamera unterstützt die erforderliche Auflösung nicht.", "Cancel": "Abbrechen", - "close": "Schliessen", + "close": "Schließen", "conferenceDisconnectMsg": "Prüfen Sie allenfalls Ihre Netzwerkverbindung. Verbinde in {{seconds}} Sekunden...", "conferenceDisconnectTitle": "Ihre Verbindung ist getrennt worden.", "conferenceReloadMsg": "Wir versuchen das zu beheben. Verbinde in {{seconds}} Sekunden...", @@ -158,76 +173,86 @@ "contactSupport": "Support kontaktieren", "copy": "Kopieren", "dismiss": "OK", - "displayNameRequired": "", + "displayNameRequired": "Hallo! Wie ist Ihr Name?", "done": "Fertig", - "enterDisplayName": "", + "enterDisplayName": "Bitte geben Sie hier Ihren Namen ein", "error": "Fehler", - "externalInstallationMsg": "Die Bildschirmfreigabeerweiterung muss installiert werden.", + "externalInstallationMsg": "Die Bildschirmfreigabe-Erweiterung muss installiert werden.", "externalInstallationTitle": "Erweiterung erforderlich", "goToStore": "Zum Store", "gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.", "IamHost": "Ich bin der Organisator", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Falsches Passwort", "incorrectPassword": "Benutzername oder Passwort ungültig", - "inlineInstallationMsg": "Die Bildschirmfreigabeerweiterung muss installiert werden.", + "inlineInstallationMsg": "Die Bildschirmfreigabe-Erweiterung muss installiert werden.", "inlineInstallExtension": "Jetzt installieren", "internalError": "Oh! Es hat etwas nicht funktioniert. Der folgende Fehler ist aufgetreten: {{error}}", "internalErrorTitle": "Interner Fehler", - "kickMessage": "", + "kickMessage": "Sie können sich für mehr Details an {{participantDisplayName}} wenden.", "kickParticipantButton": "Entfernen", "kickParticipantDialog": "Wollen Sie diesen Teilnehmer wirklich entfernen?", - "kickParticipantTitle": "Teilnehmer stummschalten?", - "kickTitle": "", - "liveStreaming": "Live-Streaming", - "liveStreamingDisabledForGuestTooltip": "Gäste können kein Live-Streaming starten.", - "liveStreamingDisabledTooltip": "Starten des Live-Streams deaktiviert.", + "kickParticipantTitle": "Teilnehmer entfernen?", + "kickTitle": "Autsch! {{participantDisplayName}} hat Sie aus dem Meeting geworfen", + "liveStreaming": "Livestreaming", + "liveStreamingDisabledForGuestTooltip": "Gäste können kein Livestreaming starten.", + "liveStreamingDisabledTooltip": "Starten des Livestreams deaktiviert.", "lockMessage": "Die Konferenz konnte nicht gesperrt werden.", - "lockRoom": "", + "lockRoom": "Meeting-$t(lockRoomPasswordUppercase) hinzufügen", "lockTitle": "Sperren fehlgeschlagen", "logoutQuestion": "Sind Sie sicher, dass Sie sich abmelden und die Konferenz verlassen möchten?", "logoutTitle": "Abmelden", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "Das Limit für die maximale Teilnehmerzahl ist erreicht. Die Konferenz ist voll. Bitte wenden Sie sich an den Besitzer des Meetings oder versuchen Sie es später noch einmal!", + "maxUsersLimitReachedTitle": "Maximales Teilnehmerlimit erreicht", "micConstraintFailedError": "Ihr Mikrofon erfüllt die notwendigen Anforderungen nicht.", "micNotFoundError": "Mikrofon nicht gefunden.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", - "micPermissionDeniedError": "Die Berechtigung zur Verwendung des Mikrofons wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht hören. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.", + "micNotSendingData": "Gehen Sie zu den Einstellungen Ihres Computers, um die Stummschaltung Ihres Mikrofons aufzuheben und seinen Pegel einzustellen", + "micNotSendingDataTitle": "Ihr Mikrofon ist durch Ihre Systemeinstellungen stumm geschaltet", + "micPermissionDeniedError": "Die Berechtigung zur Verwendung des Mikrofons wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht hören. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste, um die Berechtigungen zu erteilen.", "micUnknownError": "Das Mikrofon kann aus einem unbekannten Grund nicht verwendet werden.", + "muteEveryoneElseDialog": "Einmal stummgeschaltet, können Sie deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.", + "muteEveryoneElseTitle": "Alle außer {{whom}} stummschaten?", + "muteEveryoneDialog": "Wollen Sie wirklich alle stummschalten? Sie können deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.", + "muteEveryoneTitle": "Alle stummschalten?", + "muteEveryoneSelf": "sich selbst", + "muteEveryoneStartMuted": "Alle beginnen von jetzt an stummgeschaltet", "muteParticipantBody": "Sie können die Stummschaltung anderer Teilnehmer nicht aufheben, aber ein Teilnehmer kann seine eigene Stummschaltung jederzeit beenden.", "muteParticipantButton": "Stummschalten", "muteParticipantDialog": "Wollen Sie diesen Teilnehmer wirklich stummschalten? Sie können die Stummschaltung nicht wieder aufheben, der Teilnehmer kann dies aber jederzeit selbst tun.", "muteParticipantTitle": "Teilnehmer stummschalten?", "Ok": "OK", - "passwordLabel": "", - "passwordNotSupported": "Setzen eines Konferenz-Passworts ist nicht unterstützt", - "passwordNotSupportedTitle": "", - "passwordRequired": "", - "popupError": "Ihr Browser blockiert Popups von dieser Website. Bitte aktivieren Sie Popups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.", - "popupErrorTitle": "Popup blockiert", + "passwordLabel": "Das Treffen wurde von einem Teilnehmer geslerrt. Bitte geben Sie die $t(lockRoomPassword) zu verbinden.", + "passwordNotSupported": "Das Festlegen von einem $t(lockRoomPassword) für das Meeting wird nicht unterstützt.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nicht unterstützt", + "passwordRequired": "$t(lockRoomPasswordUppercase) erforderlich", + "popupError": "Ihr Browser blockiert Pop-ups von dieser Website. Bitte aktivieren Sie Pop-ups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.", + "popupErrorTitle": "Pop-up blockiert", "recording": "Aufnahme", - "recordingDisabledForGuestTooltip": "Gäste können kein Aufzeichnungen starten.", + "recordingDisabledForGuestTooltip": "Gäste können keine Aufzeichnungen starten.", "recordingDisabledTooltip": "Start der Aufzeichnung deaktiviert.", "rejoinNow": "Jetzt erneut beitreten", - "remoteControlAllowedMessage": "{{user}} hat die Anfrage zur Fernsteuerung angenommen.", - "remoteControlDeniedMessage": "{{user}} hat die Anfrage zur Fernsteuerung verweigert.", - "remoteControlErrorMessage": "Beim Anfordern der Fernsteuerungsberechtigung von {{user}} ist ein Fehler aufgetreten.", - "remoteControlRequestMessage": "Möchten Sie {{user}} erlauben den Computer fernzusteuern?", - "remoteControlShareScreenWarning": "Achtung, wenn Sie die Anfrage genehmigen starten Sie die Bildschirmfreigabe!", - "remoteControlStopMessage": "Die Fernsteuerung wurde beendet.", + "remoteControlAllowedMessage": "{{user}} hat die Anfrage zur Fernsteuerung angenommen!", + "remoteControlDeniedMessage": "{{user}} hat die Anfrage zur Fernsteuerung verweigert!", + "remoteControlErrorMessage": "Beim Anfordern der Fernsteuerungsberechtigung von {{user}} ist ein Fehler aufgetreten!", + "remoteControlRequestMessage": "Möchten Sie {{user}} erlauben, den Computer fernzusteuern?", + "remoteControlShareScreenWarning": "Achtung, wenn Sie die Anfrage genehmigen, starten Sie die Bildschirmfreigabe!", + "remoteControlStopMessage": "Die Fernsteuerung wurde beendet!", "remoteControlTitle": "Fernsteuerung", "Remove": "Entfernen", - "removePassword": "", - "removeSharedVideoMsg": "Sind Sie sicher dass Sie das geteilte Video entfernen möchten?", + "removePassword": "$t(lockRoomPassword) entfernen", + "removeSharedVideoMsg": "Sind Sie sicher, dass Sie das geteilte Video entfernen möchten?", "removeSharedVideoTitle": "Freigegebenes Video entfernen", - "reservationError": "Fehler im Reservationssystem", + "reservationError": "Fehler im Reservierungssystem", "reservationErrorMsg": "Fehler, Nummer: {{code}}, Nachricht: {{msg}}", "retry": "Wiederholen", "screenSharingFailedToInstall": "Oh! Die Erweiterung für die Bildschirmfreigabe konnte nicht installiert werden.", "screenSharingFailedToInstallTitle": "Bildschirmfreigabe-Erweiterung konnte nicht installiert werden", - "screenSharingFirefoxPermissionDeniedError": "Die Bildschirmfreigabe ist leider fehlgeschlagen. Bitte stellen Sie sicher, dass die Berechtigung für die Bildschirmfreigabe im Browser erteilt wurde.", - "screenSharingFirefoxPermissionDeniedTitle": "Die Bildschirmfreigabe konnte nicht gestartet werden.", + "screenSharingFirefoxPermissionDeniedError": "Die Bildschirmfreigabe ist leider fehlgeschlagen. Bitte stellen Sie sicher, dass die Berechtigung für die Bildschirmfreigabe im Browser erteilt wurde. ", + "screenSharingFirefoxPermissionDeniedTitle": "Die Bildschirmfreigabe konnte nicht gestartet werden!", "screenSharingPermissionDeniedError": "Oh! Beim Anfordern der Bildschirmfreigabe-Berechtigungen hat etwas nicht funktioniert. Bitte aktualisieren und erneut versuchen.", + "sendPrivateMessage": "Sie haben kürzlich eine private Nachricht erhalten. Hatten Sie die Absicht, darauf privat zu antworten, oder wollen Sie Ihre Nachricht an die Gruppe senden?", + "sendPrivateMessageCancel": "An die Gruppe senden", + "sendPrivateMessageOk": "Privat antworten", + "sendPrivateMessageTitle": "Privat antworten?", "serviceUnavailable": "Dienst nicht verfügbar", "sessTerminated": "Konferenz beendet", "Share": "Teilen", @@ -238,34 +263,35 @@ "shareYourScreenDisabledForGuest": "Gäste können den Bildschirm nicht freigeben.", "startLiveStreaming": "Einen Livestream starten", "startRecording": "Aufnahme starten", - "startRemoteControlErrorMessage": "Beim Versuch die Fernsteuerung zu starten ist ein Fehler aufgetreten.", - "stopLiveStreaming": "Live-Streaming stoppen", + "startRemoteControlErrorMessage": "Beim Versuch, die Fernsteuerung zu starten, ist ein Fehler aufgetreten!", + "stopLiveStreaming": "Livestreaming stoppen", "stopRecording": "Aufnahme stoppen", - "stopRecordingWarning": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?", - "stopStreamingWarning": "Sind Sie sicher dass Sie das Live-Streaming stoppen möchten?", - "streamKey": "Name/Schlüssel für den Stream", + "stopRecordingWarning": "Sind Sie sicher, dass Sie die Aufnahme stoppen möchten?", + "stopStreamingWarning": "Sind Sie sicher, dass Sie das Livestreaming stoppen möchten?", + "streamKey": "Streamschlüssel", "Submit": "OK", "thankYou": "Danke für die Verwendung von {{appName}}!", "token": "Token", - "tokenAuthFailed": "Sie sind nicht berechtigt dieser Konferenz beizutreten.", + "tokenAuthFailed": "Sie sind nicht berechtigt, dieser Konferenz beizutreten.", "tokenAuthFailedTitle": "Authentifizierung fehlgeschlagen", "transcribing": "Wird transkribiert", - "unlockRoom": "", + "unlockRoom": "Meeting-$t(lockRoomPassword) entfernen", "userPassword": "Benutzerpasswort", - "WaitForHostMsg": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Wenn Sie der Veranstalter sind, authentifizieren Sie sich. Warten Sie andernfalls, bis der Veranstalter erscheint.", - "WaitForHostMsgWOk": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Wenn Sie der Veranstalter sind, drücken Sie zum Authentifizieren auf OK. Warten Sie andernfalls, bis der Veranstalter erscheint.", + "WaitForHostMsg": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Wenn Sie der Organisator sind, authentifizieren Sie sich. Warten Sie andernfalls, bis der Organisator erscheint.", + "WaitForHostMsgWOk": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Wenn Sie der Organisator sind, drücken Sie zum Authentifizieren auf OK. Warten Sie andernfalls, bis der Organisator erscheint.", "WaitingForHost": "Warten auf den Organisator...", "Yes": "Ja", - "yourEntireScreen": "Ganzer Bildschirm" - }, - "\u0005dialog": { - "accessibilityLabel": {} + "yourEntireScreen": "Ganzer Bildschirm", + "screenSharingAudio": "Audio austauschen" }, "dialOut": { "statusMessage": "ist jetzt {{status}}" }, + "documentSharing": { + "title": "Freigegebenes Dokument" + }, "feedback": { - "average": "Durschnittlich", + "average": "Durchschnittlich", "bad": "Schlecht", "detailsLabel": "Sagen Sie uns mehr dazu.", "good": "Gut", @@ -282,32 +308,32 @@ }, "info": { "accessibilityLabel": "Informationen anzeigen", - "addPassword": "", - "cancelPassword": "", + "addPassword": "$t(lockRoomPassword) hinzufügen", + "cancelPassword": "$t(lockRoomPassword) löschen", "conferenceURL": "Link:", "country": "Land", - "dialANumber": "Um am Metting teilzunehmen, müssen Sie eine dieser Nummern wählen und dann die PIN eingeben.", + "dialANumber": "Um am Meeting teilzunehmen, müssen Sie eine dieser Nummern wählen und dann die PIN eingeben.", "dialInConferenceID": "PIN:", "dialInNotSupported": "Entschuldigung, leider wird das Einwählen derzeit nicht unterstützt.", "dialInNumber": "Einwählen:", - "dialInSummaryError": "Fehler beim Abrufen der Einwählinformationen. Versuchen Sie es später erneut.", + "dialInSummaryError": "Fehler beim Abrufen der Einwahlinformationen. Versuchen Sie es später erneut.", "dialInTollFree": "Gebührenfrei", "genericError": "Es ist leider etwas schiefgegangen.", - "inviteLiveStream": "Klicken Sie auf {{url}} um den Livestream dieser Konferenz zu öffnen", - "invitePhone": "", - "invitePhoneAlternatives": "", + "inviteLiveStream": "Klicken Sie auf {{url}}, um den Livestream dieser Konferenz zu öffnen", + "invitePhone": "Wenn Sie stattdessen per Telefon beitreten möchten, tippen Sie hier: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Suche nach einer anderen Einwahlnummer?\nMeetings-Einwahlnummern sehen: {{url}}\n\n\nWenn Sie sich auch über ein Raumtelefon einwählen, nehmen Sie teil, ohne sich mit dem Ton zu verbinden: {{silentUrl}}", "inviteURLFirstPartGeneral": "Sie wurden zur Teilnahme an einem Meeting eingeladen.", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartPersonal": "{{name}} lädt Sie zu einem Meeting ein.\n", + "inviteURLSecondPart": "\nAm Meeting teilnehmen:\n{{url}}\n", "liveStreamURL": "Livestream:", "moreNumbers": "Weitere Telefonnummern", "noNumbers": "Keine Telefonnummern verfügbar.", - "noPassword": "Kein", - "noRoom": "Keine Konferenz für die Einwähl-Informationen angegeben.", - "numbers": "Einwählnummern", - "password": "", + "noPassword": "Keines", + "noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.", + "numbers": "Einwahlnummern", + "password": "$t(lockRoomPasswordUppercase):", "title": "Teilen", - "tooltip": "Freigabe-Link und Einwählinformationen für dieses Meeting", + "tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting", "label": "Meeting-Informationen" }, "inviteDialog": { @@ -322,54 +348,57 @@ "msg": "Es ist ein Fehler aufgetreten.", "retry": "Erneut versuchen", "support": "Support", - "supportMsg": "Wenn der Fehler erneut auftritt, bitte kontaktieren sie" + "supportMsg": "Wenn der Fehler erneut auftritt, bitte kontaktieren Sie" }, "keyboardShortcuts": { "focusLocal": "Lokales Video fokussieren", "focusRemote": "Auf das Video eines anderen Teilnehmers fokussieren", - "fullScreen": "Vollbildmodus aktivieren / deaktivieren", + "fullScreen": "Vollbildmodus aktivieren oder deaktivieren", "keyboardShortcuts": "Tastenkürzel", "localRecording": "Lokale Aufzeichnungssteuerelemente ein- oder ausblenden", "mute": "Stummschaltung aktivieren oder deaktivieren", - "pushToTalk": "Drücken um zu sprechen", + "pushToTalk": "Push-to-Talk (Sprechtaste)", "raiseHand": "Hand erheben", "showSpeakerStats": "Statistiken für Sprecher anzeigen", - "toggleChat": "Chat öffnen oder schliessen", + "toggleChat": "Chat öffnen oder schließen", "toggleFilmstrip": "Video-Miniaturansichten ein- oder ausblenden", "toggleScreensharing": "Zwischen Kamera und Bildschirmfreigabe wechseln", "toggleShortcuts": "Tastenkombinationen ein- oder ausblenden", - "videoMute": "Kamera starten oder stoppen" + "videoMute": "Kamera starten oder stoppen", + "videoQuality": "Anrufqualität verwalten" }, - "\u0005keyboardShortcuts": {}, "liveStreaming": { - "busy": "Es werden Resourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.", + "busy": "Es werden Ressourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.", "busyTitle": "Alle Streaming-Instanzen sind in Gebrauch", "changeSignIn": "Konten wechseln.", - "choose": "Live stream auswählen", + "choose": "Livestream auswählen", "chooseCTA": "Streaming-Option auswählen. Sie sind aktuell als {{email}} angemeldet.", - "enterStreamKey": "Name/Schlüssel für den YouTube Livestream hier eingeben.", - "error": "Das Live-Streaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.", - "errorAPI": "Beim abrufen der YouTube Livestreams ist ein Fehler aufgetreten. Bitte versuchen Sie sich erneut anzumelden.", - "errorLiveStreamNotEnabled": "Live-Streaming ist für {{email}} nicht aktiviert. Aktivieren Sie das Live-Streaming oder melden Sie sich bei einem Konto mit aktiviertem Live-Streaming an.", - "expandedOff": "Live-Streaming wurde angehalten", + "enterStreamKey": "Streamschlüssel für den YouTube-Livestream hier eingeben.", + "error": "Das Livestreaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.", + "errorAPI": "Beim Abrufen der YouTube-Livestreams ist ein Fehler aufgetreten. Bitte versuchen Sie, sich erneut anzumelden.", + "errorLiveStreamNotEnabled": "Livestreaming ist für {{email}} nicht aktiviert. Aktivieren Sie das Livestreaming oder melden Sie sich bei einem Konto mit aktiviertem Livestreaming an.", + "expandedOff": "Livestreaming wurde angehalten", "expandedOn": "Das Meeting wird momentan an YouTube gestreamt.", - "expandedPending": "Live-Streaming wird gestartet...", - "failedToStart": "Live-Streaming konnte nicht gestartet werden", - "getStreamKeyManually": "", - "invalidStreamKey": "Der Live-Stream-Schlüssel ist u. U. falsch.", - "off": "Live-Streaming gestoppt", - "on": "Live-Streaming", - "pending": "Live-Stream wird gestartet...", - "serviceName": "Live Streaming-Dienst", + "expandedPending": "Livestreaming wird gestartet...", + "failedToStart": "Livestreaming konnte nicht gestartet werden", + "getStreamKeyManually": "Wir waren nicht in der Lage, Livestreams abzurufen. Versuchen Sie, Ihren Livestream-Schlüssel von YouTube zu erhalten.", + "invalidStreamKey": "Der Livestream-Schlüssel ist u. U. falsch.", + "off": "Livestreaming gestoppt", + "offBy": "{{name}} stoppte das Livestreaming", + "on": "Livestreaming", + "onBy": "{{name}} startete das Livestreaming", + "pending": "Livestream wird gestartet...", + "serviceName": "Livestreaming-Dienst", "signedInAs": "Sie sind derzeit angemeldet als:", "signIn": "Mit Google anmelden", - "signInCTA": "Anmelden oder den Name/Schlüssel des YouTube Livestreams eingeben.", + "signInCTA": "Anmelden oder den Streamschlüssel des YouTube-Livestreams eingeben.", "signOut": "Abmelden", "start": "Einen Livestream starten", "streamIdHelp": "Was ist das?", - "unavailableTitle": "Live-Streaming nicht verfügbar" + "unavailableTitle": "Livestreaming nicht verfügbar", + "youtubeTerms": "YouTube-Nutzungsbedingungen", + "googlePrivacyPolicy": "Google-Datenschutzerklärung" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { "off": "Aus", @@ -379,15 +408,15 @@ "dialogTitle": "Lokale Aufzeichnungssteuerelemente", "duration": "Dauer", "durationNA": "N. v.", - "encoding": "Codierung", + "encoding": "Kodierung", "label": "LOR", "labelToolTip": "Lokale Aufzeichnung ist aktiviert", "localRecording": "Lokale Aufzeichnung", "me": "Ich", "messages": { - "engaged": "Lokale Aufzeichnung ist aktiviert", + "engaged": "Lokale Aufzeichnung ist aktiviert.", "finished": "Aufzeichnung der Sitzung {{token}} ist beendet. Senden Sie die aufgezeichnete Datei an den Moderator.", - "finishedModerator": "Aufzeichnung der Sitzung {{token}} ist beendet. Die Aufzeichnung der lokalen Verlaufs wurde gespeichert. Bitten Sie die anderen Teilnehmer, ihre Aufzeichnungen zu übermitteln.", + "finishedModerator": "Aufzeichnung der Sitzung {{token}} ist beendet. Die Aufzeichnung des lokalen Verlaufs wurde gespeichert. Bitten Sie die anderen Teilnehmer, ihre Aufzeichnungen zu übermitteln.", "notModerator": "Sie sind nicht der Moderator. Sie können die lokale Aufzeichnung nicht starten oder stoppen." }, "moderator": "Moderator", @@ -399,7 +428,6 @@ "stop": "Aufnahme stoppen", "yes": "Ja" }, - "\u0005localRecording": {}, "lockRoomPassword": "Passwort", "lockRoomPasswordUppercase": "Passwort", "me": "ich", @@ -410,38 +438,38 @@ "disconnected": "getrennt", "focus": "Konferenz-Organisator", "focusFail": "{{component}} ist im Moment nicht verfügbar - wiederholen in {{ms}} Sekunden", - "grantedTo": "Moderatorenrechte an {{to}} vergeben.", - "invitedOneMember": "{{displayName}} wurde eingeladen", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "grantedTo": "Moderatorenrechte an {{to}} vergeben!", + "invitedOneMember": "{{name}} wurde eingeladen", + "invitedThreePlusMembers": "{{name}} und {{count}} andere wurden eingeladen", + "invitedTwoMembers": "{{first}} und {{second}} wurden eingeladen", + "kickParticipant": "{{kicked}} wurde von {{kicker}} ausgewiesen", "me": "Ich", - "moderator": "Moderatorenrechte vergeben", + "moderator": "Moderatorenrechte vergeben!", "muted": "Der Konferenz wurde stumm beigetreten.", "mutedTitle": "Stummschaltung aktiv!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyTitle": "Sie wurden von {{participantDisplayName}} stummgeschaltet!", + "mutedRemotelyDescription": "Sie können jederzeit die Stummschaltung aufheben, wenn Sie bereit sind zu sprechen. Wenn Sie fertig sind, können Sie sich wieder stummschalten, um Geräusche vom Meeting fernzuhalten.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) von einem anderen Teilnehmer entfernt", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) von einem anderen Teilnehmer gesetzt", "raisedHand": "{{name}} möchte sprechen.", "somebody": "Jemand", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein grossartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"static/recommendedBrowsers.html\" target=\"_blank\">vollständig unterstützen Browser</a>.", + "startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!", + "startSilentDescription": "Treten Sie dem Meeting noch einmal bei, um Ihr Audio zu aktivieren", + "suboptimalBrowserWarning": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein großartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"static/recommendedBrowsers.html\" target=\"_blank\">vollständig unterstützen Browser</a>.", "suboptimalExperienceTitle": "Browserwarnung", - "unmute": "", + "unmute": "Stummschaltung aufheben", "newDeviceCameraTitle": "Neue Kamera erkannt", "newDeviceAudioTitle": "Neues Audiogerät erkannt", "newDeviceAction": "Verwenden" }, "passwordSetRemotely": "von einem anderen Teilnehmer gesetzt", - "passwordDigitsOnly": "", + "passwordDigitsOnly": "Bis zu {{number}} Ziffern", "poweredby": "Betrieben von", "presenceStatus": { "busy": "Beschäftigt", - "calling": "Wird angerufen…", + "calling": "Wird angerufen …", "connected": "Verbunden", - "connecting": "Verbindung wird hergestellt", + "connecting": "Verbindung wird hergestellt…", "connecting2": "Wird verbunden*…", "disconnected": "Getrennt", "expired": "Abgelaufen", @@ -449,20 +477,20 @@ "initializingCall": "Anruf wird initialisiert…", "invited": "Einladen", "rejected": "Abgelehnt", - "ringing": "Es klingelt…" + "ringing": "Es klingelt …" }, - "\u0005presenceStatus": {}, "profile": { "setDisplayNameLabel": "Anzeigename festlegen", "setEmailInput": "E-Mail eingeben", - "setEmailLabel": "E-Mail Adresse für Gravatar", + "setEmailLabel": "E-Mail-Adresse für Gravatar", "title": "Profil" }, + "raisedHand": "Ich möchte sprechen", "recording": { "authDropboxText": "In Dropbox hochladen", "availableSpace": "Verfügbarer Speicherplatz: {{spaceLeft}} MB (ca. {{duration}} Minuten Aufzeichnung)", "beta": "BETA", - "busy": "Es werden Resourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.", + "busy": "Es werden Ressourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.", "busyTitle": "Alle Aufnahme-Instanzen sind in Gebrauch", "error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.", "expandedOff": "Aufzeichnung wurde gestoppt", @@ -473,7 +501,9 @@ "live": "LIVE", "loggedIn": "Als {{userName}} angemeldet", "off": "Aufnahme gestoppt", + "offBy": "{{name}} stoppte die Aufnahme", "on": "Aufnahme", + "onBy": "{{name}} startete die Aufnahme", "pending": "Aufzeichnung des Meetings wird vorbereitet…", "rec": "AUFZ", "serviceDescription": "Ihre Aufzeichnung wird vom Aufzeichnungsdienst gespeichert", @@ -484,20 +514,21 @@ "unavailableTitle": "Aufnahme nicht verfügbar" }, "sectionList": { - "pullToRefresh": "Ziehen um zu aktualisieren" + "pullToRefresh": "Ziehen, um zu aktualisieren" }, "settings": { "calendar": { - "about": "Die Kalenderintegration von {{appName}} wird verwendet, um ein sicheres Zugreifen auf Ihren Kalender und Auslesen der bevorstehenden Veranstaltungen zu ermöglichen.", + "about": "Die Kalenderintegration von {{appName}} wird verwendet, um ein sicheres Zugreifen auf Ihren Kalender und Auslesen der bevorstehenden Termine zu ermöglichen.", "disconnect": "Getrennt", "microsoftSignIn": "Mit Microsoft anmelden", - "signedIn": "Momentan wird auf Kalenderveranstaltungen von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalenderveranstaltungen zu stoppen.", + "signedIn": "Momentan wird auf Kalendertermine von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalendertermine zu stoppen.", "title": "Kalender" }, "devices": "Geräte", "followMe": "Follow-me für alle Teilnehmer", "language": "Sprache", "loggedIn": "Als {{name}} angemeldet", + "microphones": "Mikrofon", "moderator": "Moderator", "more": "Mehr", "name": "Name", @@ -505,31 +536,33 @@ "selectAudioOutput": "Audioausgabe", "selectCamera": "Kamera", "selectMic": "Mikrofon", + "speakers": "Lautsprecher", "startAudioMuted": "Alle Teilnehmer treten stumm geschaltet bei", "startVideoMuted": "Alle Teilnehmer treten ohne Video bei", "title": "Einstellungen" }, - "\u0005settings": { - "calendar": {} - }, "settingsView": { + "advanced": "Erweitert", "alertOk": "OK", "alertTitle": "Warnung", - "alertURLText": "Die angegebene Server URL ist ungültig", + "alertURLText": "Die angegebene Server-URL ist ungültig", "buildInfoSection": "Build-Informationen", "conferenceSection": "Konferenz", + "disableCallIntegration": "Native Anrufintegration deaktivieren", + "disableP2P": "Ende-zu-Ende-Modus deaktivieren", "displayName": "Anzeigename", "email": "E-Mail", "header": "Einstellungen", "profileSection": "Profil", - "serverURL": "Server URL", + "serverURL": "Server-URL", + "showAdvanced": "Erweiterte Einstellungen anzeigen", "startWithAudioMuted": "Stumm beitreten", "startWithVideoMuted": "Ohne Video beitreten", "version": "Version" }, "share": { - "dialInfoText": "", - "mainText": "" + "dialInfoText": "\n\n=====\n\nWollen Sie sich nur auf Ihrem Telefon einwählen?\n\n{{defaultDialInNumber}}Klicken Sie auf diesen Link, um die eingewählten Telefonnummern für dieses Meeting zu sehen\n{{dialInfoPageUrl}}", + "mainText": "Klicken Sie auf den folgenden Link, um dem Meeting beizutreten:\n{{roomUrl}}" }, "speaker": "Sprecher", "speakerStats": { @@ -546,28 +579,33 @@ }, "suspendedoverlay": { "rejoinKeyTitle": "Erneut teilnehmen", - "text": "<i>Erneut teilnehmen</i> Schaltfläche betätigen um erneut zu verbinden.", - "title": "Die Konferenz wurde unterbrochen weil der Standbymodus aktiviert wurde." + "text": "„<i>Erneut teilnehmen</i>“-Schaltfläche betätigen, um erneut zu verbinden.", + "title": "Die Konferenz wurde unterbrochen, weil der Standby-Modus aktiviert wurde." }, "toolbar": { "accessibilityLabel": { - "audioOnly": "Nur Audio ein-/ausschalten", + "audioOnly": "„Nur Audio“ ein-/ausschalten", "audioRoute": "Audiogerät auswählen", "callQuality": "Qualitätseinstellungen", "cc": "Untertitel ein-/ausschalten", "chat": "Chatfenster ein-/ausblenden", - "document": "Geteiltes Dokument schliessen", - "feedback": "Feedback hinterlasen", - "fullScreen": "Vollbildschirm ein-/ausblenden", + "document": "Geteiltes Dokument schließen", + "download": "Unsere Apps herunterladen", + "feedback": "Feedback hinterlassen", + "fullScreen": "Vollbildmodus aktivieren/deaktivieren", "hangup": "Anruf beenden", + "help": "Hilfe", "invite": "Teilnehmer einladen", "kick": "Teilnehmer entfernen", "localRecording": "Lokale Aufzeichnungssteuerelemente ein-/ausschalten", "lockRoom": "Meeting-Passwort ein-/auschalten", "moreActions": "Menü „Weitere Aktionen“ ein-/ausschalten", "moreActionsMenu": "Menü „Weitere Aktionen“", + "moreOptions": "Menü „Weitere Optionen“", "mute": "„Audio stummschalten“ ein-/ausschalten", + "muteEveryone": "Alle stummschalten", "pip": "Bild-in-Bild-Modus ein-/ausschalten", + "privateMessage": "Private Nachricht senden", "profile": "Profil bearbeiten", "raiseHand": "„Melden“ ein-/ausschalten", "recording": "Aufzeichnung ein-/ausschalten", @@ -577,12 +615,12 @@ "shareRoom": "Person einladen", "shareYourScreen": "Bildschirmfreigabe ein-/ausschalten", "shortcuts": "Tastenkombinationen ein-/ausblenden", - "show": "", + "show": "Im Vordergrund anzeigen", "speakerStats": "Sprecherstatistik ein-/ausblenden", "tileView": "Kachelansicht ein-/ausschalten", - "toggleCamera": "Kamera ein-/ausschalten", + "toggleCamera": "Kamera wechseln", "videomute": "„Video stummschalten“ ein-/ausschalten", - "videoblur": "" + "videoblur": "Video-Unschärfe ein-/ausschalten" }, "addPeople": "Teilnehmer zur Konferenz hinzufügen", "audioOnlyOff": "Modus „Nur Audio“ deaktivieren", @@ -590,24 +628,36 @@ "audioRoute": "Audiogerät auswählen", "authenticate": "Anmelden", "callQuality": "Qualitätseinstellungen", - "chat": "Chat öffnen / schliessen", + "chat": "Chat öffnen / schließen", "closeChat": "Chat schließen", - "documentClose": "Geteiltes Dokument schliessen", + "documentClose": "Geteiltes Dokument schließen", "documentOpen": "Geteiltes Dokument öffnen", + "download": "Unsere Apps herunterladen", "enterFullScreen": "Vollbildmodus", "enterTileView": "Kachelansicht einschalten", "exitFullScreen": "Vollbildmodus verlassen", "exitTileView": "Kachelansicht ausschalten", - "feedback": "Feedback hinterlasen", + "feedback": "Feedback hinterlassen", "hangup": "Verlassen", + "help": "Hilfe", "invite": "Teilnehmer einladen", "login": "Anmelden", "logout": "Abmelden", "lowerYourHand": "Hand senken", "moreActions": "Weitere Einstellungen", + "moreOptions": "Weitere Optionen", "mute": "Stummschaltung aktivieren / deaktivieren", + "muteEveryone": "Alle stummschalten", + "noAudioSignalTitle": "Es kommt kein Input von Ihrem Mikrofon!", + "noAudioSignalDesc": "Wenn Sie das Gerät nicht absichtlich über die Systemeinstellungen oder die Hardware stumm geschaltet haben, sollten Sie einen Wechsel des Geräts in Erwägung ziehen.", + "noAudioSignalDescSuggestion": "Wenn Sie das Gerät nicht absichtlich über die Systemeinstellungen oder die Hardware stummgeschaltet haben, sollten Sie einen Wechsel auf das vorgeschlagene Gerät in Erwägung ziehen.", + "noAudioSignalDialInDesc": "Sie können sich auch über die Einwahlnummer einwählen:", + "noAudioSignalDialInLinkDesc": "Einwahlnummern", + "noisyAudioInputTitle": "Ihr Mikrofon scheint lärmintensiv zu sein!", + "noisyAudioInputDesc": "Es klingt, als ob Ihr Mikrofon Störgeräusche verursacht. Bitte überlegen Sie, ob Sie das Gerät stummschalten oder austauschen wollen.", "openChat": "Chat öffnen", "pip": "Bild-in-Bild-Modus einschalten", + "privateMessage": "Private Nachricht senden", "profile": "Profil bearbeiten", "raiseHand": "Hand erheben", "raiseYourHand": "Melden", @@ -620,16 +670,13 @@ "startSubtitles": "Untertitel einschalten", "stopScreenSharing": "Bildschirmfreigabe stoppen", "stopSubtitles": "Untertitel ausschalten", - "stopSharedVideo": "YouTube Video stoppen", - "talkWhileMutedPopup": "Versuchen sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.", + "stopSharedVideo": "YouTube-Video stoppen", + "talkWhileMutedPopup": "Versuchen Sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.", "tileViewToggle": "Kachelansicht ein-/ausschalten", - "toggleCamera": "Kamera ein-/ausschalten", + "toggleCamera": "Kamera wechseln", "videomute": "Kamera starten / stoppen", - "startvideoblur": "", - "stopvideoblur": "" - }, - "\u0005toolbar": { - "accessibilityLabel": {} + "startvideoblur": "Hintergrundunschärfe aktivieren", + "stopvideoblur": "Hintergrundunschärfe deaktivieren" }, "transcribing": { "ccButtonTooltip": "Untertitel ein-/ausschalten", @@ -643,18 +690,17 @@ "stop": "Anzeige der Untertitel stoppen", "tr": "TR" }, - "\u0005transcribing": {}, "userMedia": { - "androidGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b> wenn der Browser um Berechtigungen bittet.", - "chromeGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b> wenn der Browser um Berechtigungen bittet.", - "edgeGrantPermissions": "Wählen Sie <b><i>Ja</i></b> wenn der Browser um Berechtigungen bittet.", + "androidGrantPermissions": "Wählen Sie <b><i>Zulassen</i></b>, wenn der Browser um Berechtigungen bittet.", + "chromeGrantPermissions": "Wählen Sie <b><i>Zulassen</i></b>, wenn der Browser um Berechtigungen bittet.", + "edgeGrantPermissions": "Wählen Sie <b><i>Ja</i></b>, wenn der Browser um Berechtigungen bittet.", "electronGrantPermissions": "Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons erteilen", - "firefoxGrantPermissions": "Wählen Sie <b><i>Markiertes Gerät teilen</i></b> wenn der Browser um Berechtigungen bittet.", - "iexplorerGrantPermissions": "Wählen Sie <b><i>OK</i></b> wenn der Browser um Berechtigungen bittet.", + "firefoxGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b>, wenn der Browser um Berechtigungen bittet.", + "iexplorerGrantPermissions": "Wählen Sie <b><i>OK</i></b>, wenn der Browser um Berechtigungen bittet.", "nwjsGrantPermissions": "Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons erteilen", - "operaGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b> wenn der Browser um Berechtigungen bittet.", - "react-nativeGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b> wenn der Browser um Berechtigungen bittet.", - "safariGrantPermissions": "Wählen Sie <b><i>OK</i></b> wenn der Browser um Berechtigungen bittet." + "operaGrantPermissions": "Wählen Sie <b><i>Zulassen</i></b>, wenn der Browser um Berechtigungen bittet.", + "react-nativeGrantPermissions": "Wählen Sie <b><i>Erlauben</i></b>, wenn der Browser um Berechtigungen bittet.", + "safariGrantPermissions": "Wählen Sie <b><i>OK</i></b>, wenn der Browser um Berechtigungen bittet." }, "videoSIPGW": { "busy": "Es stehen keine freien Ressourcen zur Verfügung. Bitte versuchen Sie es später noch einmal.", @@ -669,58 +715,68 @@ "videoStatus": { "audioOnly": "AUD", "audioOnlyExpanded": "Sie befinden sich im Modus „Nur Audio“. Dieser Modus benötigt weniger Bandbreite, Sie sehen jedoch nicht die Videos der anderen.", - "callQuality": "", + "callQuality": "Videoqualität", "hd": "HD", + "hdTooltip": "Video wird in hoher Auflösung angezeigt", "highDefinition": "Hohe Auflösung", "labelTooiltipNoVideo": "Kein Video", - "labelTooltipAudioOnly": "Nur-Audio Modus aktiv", + "labelTooltipAudioOnly": "„Nur Audio“-Modus aktiv", "ld": "LD", + "ldTooltip": "Video wird in niedriger Auflösung angezeigt", "lowDefinition": "Niedrige Auflösung", "onlyAudioAvailable": "Nur Ton", "onlyAudioSupported": "In diesem Browser wird nur Audio unterstützt.", "p2pEnabled": "Ende-zu-Ende aktiviert", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "Im Ende-zu-Ende-Modus kann die empfangene Videoqualität nur zwischen „Hoch“ und „Nur Audio“ umgeschaltet werden. Andere Einstellungen werden erst beim Verlassen des Ende-zu-Ende-Modus berücksichtigt.", "recHighDefinitionOnly": "Hohe Qualität wird bevorzugt.", "sd": "SD", + "sdTooltip": "Video wird in Standardauflösung angezeigt", "standardDefinition": "Standardauflösung" }, "videothumbnail": { "domute": "Stummschalten", + "domuteOthers": "Alle anderen stummschalten", "flip": "Spiegeln", "kick": "Hinauswerfen", "moderator": "Moderator", "mute": "Teilnehmer ist stumm geschaltet", "muted": "Stummgeschaltet", "remoteControl": "Fernsteuerung", - "show": "", - "videomute": "" + "show": "Im Vordergrund anzeigen", + "videomute": "Teilnehmer hat die Kamera angehalten" }, "welcomepage": { "accessibilityLabel": { "join": "Zum Teilnehmen tippen", "roomname": "Konferenzname eingeben" }, - "appDescription": "Auf geht's! Beginne eine Videokonferenz mit dem ganzen Team. Oder eigentlich, lade alle ein die du kennst. {{app}} ist eine vollständig verschlüsselte, aus 100% Open-Source-Software bestehende Videokonferenzlösung die du den ganzen Tag kostenlos verwenden kannst — ohne Registrierung.", + "appDescription": "Auf geht's! Starten Sie eine Videokonferenz mit dem ganzen Team. Oder besser noch: Laden Sie alle ein, die Sie kennen. {{app}} ist eine vollständig verschlüsselte, aus 100 % Open-Source-Software bestehende Videokonferenzlösung, die Sie den ganzen Tag kostenlos verwenden können — ohne Registrierung.", "audioVideoSwitch": { - "audio": "Sprache", + "audio": "Audio", "video": "Video" }, "calendar": "Kalender", "connectCalendarButton": "Kalender verbinden", "connectCalendarText": "Verbinden Sie Ihren Kalender, um all Ihre Meetings in {{app}} anzuzeigen. Fügen Sie zudem {{provider}}-Meetings in Ihren Kalender ein und starten Sie sie mit nur einem Klick.", "enterRoomTitle": "Neues Meeting starten", + "roomNameAllowedChars": "Der Meeting-Name sollte keines der folgenden Zeichen enthalten: ?, &, :, ', \", %, #.", "go": "Los", + "goSmall": "Los", "join": "Beitreten", "info": "Informationen", - "privacy": "Privatsphäre", - "recentList": "Letzte\"", + "privacy": "Datenschutz", + "recentList": "Letzte", "recentListDelete": "Löschen", "recentListEmpty": "Die Liste „Letzte“ ist momentan leer. Chatten Sie mit Ihrem Team. Sie finden all Ihre letzten Meetings hier.", - "reducedUIText": "", + "reducedUIText": "Willkommen bei {{app}}!", "roomname": "Konferenzname eingeben", - "roomnameHint": "Name oder URL der Konferenz der Sie beitreten möchten. Sie können einen Namen erfinden, er muss nur den anderen Teilnehmern übermittelt werden damit sie der gleichen Konferenz beitreten.", - "sendFeedback": "Senden Sie uns Ihr Feedback", - "terms": "Bedingungen", + "roomnameHint": "Name oder URL der Konferenz, der Sie beitreten möchten. Sie können einen Namen erfinden, er muss nur den anderen Teilnehmern übermittelt werden, damit diese der gleichen Konferenz beitreten.", + "sendFeedback": "Feedback senden", + "terms": "AGB", "title": "Sichere, mit umfassenden Funktionen ausgestattete und vollkommen kostenlose Videokonferenzen" + }, + "lonelyMeetingExperience": { + "button": "Andere einladen", + "youAreAlone": "Nur Sie sind in diesem Meeting" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-enGB.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-enGB.json index f0d670ff6..92ac5b5a6 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-enGB.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-enGB.json @@ -21,7 +21,8 @@ "bluetooth": "Bluetooth", "headphones": "Headphones", "phone": "Phone", - "speaker": "Speaker" + "speaker": "Speaker", + "none": "" }, "audioOnly": { "audioOnly": "Audio only" @@ -51,10 +52,15 @@ "popover": "Choose a nickname", "title": "Enter a nickname to use chat" }, - "title": "Chat" + "title": "Chat", + "you": "", + "privateNotice": "", + "noMessagesMessage": "", + "messageTo": "", + "fieldPlaceHolder": "" }, "connectingOverlay": { - "joiningRoom": "Connecting you to your meeting..." + "joiningRoom": "Connecting you to your meeting…" }, "connection": { "ATTACHED": "Attached", @@ -66,7 +72,11 @@ "DISCONNECTED": "Disconnected", "DISCONNECTING": "Disconnecting", "ERROR": "Error", - "RECONNECTING": "A network problem occurred. Reconnecting..." + "RECONNECTING": "A network problem occurred. Reconnecting...", + "LOW_BANDWIDTH": "", + "GOT_SESSION_ID": "", + "GET_SESSION_ID_ERROR": "", + "FETCH_SESSION_ID": "" }, "connectionindicator": { "address": "Address:", @@ -97,7 +107,8 @@ "status": "Connection:", "transport": "Transport:", "transport_plural": "Transports:", - "turn": " (turn)" + "turn": " (turn)", + "e2e_rtt": "" }, "dateUtils": { "earlier": "Earlier", @@ -111,7 +122,7 @@ "downloadApp": "Download the app", "launchWebButton": "Launch in web", "openApp": "Continue to the app", - "title": "Launching your meeting in {{app}}...", + "title": "Launching your meeting in {{app}}…", "tryAgainButton": "Try again in desktop" }, "defaultLink": "e.g. {{url}}", @@ -145,9 +156,9 @@ "cameraUnsupportedResolutionError": "Your camera does not support required video resolution.", "Cancel": "Cancel", "close": "Close", - "conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in {{seconds}} sec...", + "conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in {{seconds}} sec…", "conferenceDisconnectTitle": "You have been disconnected.", - "conferenceReloadMsg": "We're trying to fix this. Reconnecting in {{seconds}} sec...", + "conferenceReloadMsg": "We're trying to fix this. Reconnecting in {{seconds}} sec…", "conferenceReloadTitle": "Unfortunately, something went wrong.", "confirm": "Confirm", "confirmNo": "No", @@ -254,9 +265,20 @@ "userPassword": "user password", "WaitForHostMsg": "The conference <b>{{room}}</b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.", "WaitForHostMsgWOk": "The conference <b>{{room}}</b> has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.", - "WaitingForHost": "Waiting for the host ...", + "WaitingForHost": "Waiting for the host …", "Yes": "Yes", - "yourEntireScreen": "Your entire screen" + "yourEntireScreen": "Your entire screen", + "sendPrivateMessageTitle": "", + "sendPrivateMessageOk": "", + "sendPrivateMessageCancel": "", + "sendPrivateMessage": "", + "screenSharingAudio": "", + "muteEveryoneStartMuted": "", + "muteEveryoneSelf": "", + "muteEveryoneTitle": "", + "muteEveryoneDialog": "", + "muteEveryoneElseTitle": "", + "muteEveryoneElseDialog": "" }, "dialOut": { "statusMessage": "is now {{status}}" @@ -291,7 +313,7 @@ "dialInTollFree": "Toll Free", "genericError": "Whoops, something went wrong.", "inviteLiveStream": "To view the live stream of this meeting, click this link: {{url}}", - "invitePhone": "One tap audio Dial In: {{number}},,{{conferenceID}}#", + "invitePhone": "One tap audio Dial In: {{number}},,{{conferenceID}}#\n", "invitePhoneAlternatives": "", "inviteURLFirstPartGeneral": "You are invited to join a meeting.", "inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n", @@ -328,14 +350,15 @@ "keyboardShortcuts": "Keyboard shortcuts", "localRecording": "Show or hide local recording controls", "mute": "Mute or unmute your microphone", - "pushToTalk": "Push to talk", + "pushToTalk": "Press to transmit", "raiseHand": "Raise or lower your hand", "showSpeakerStats": "Show speaker stats", "toggleChat": "Open or close the chat", "toggleFilmstrip": "Show or hide video thumbnails", "toggleScreensharing": "Switch between camera and screen sharing", "toggleShortcuts": "Show or hide keyboard shortcuts", - "videoMute": "Start or stop your camera" + "videoMute": "Start or stop your camera", + "videoQuality": "" }, "liveStreaming": { "busy": "We're working on freeing streaming resources. Please try again in a few minutes.", @@ -349,13 +372,13 @@ "errorLiveStreamNotEnabled": "Live Streaming is not enabled on {{email}}. Please enable live streaming or log into an account with live streaming enabled.", "expandedOff": "The live streaming has stopped", "expandedOn": "The meeting is currently being streamed to YouTube.", - "expandedPending": "The live streaming is being started...", + "expandedPending": "The live streaming is being started…", "failedToStart": "Live Streaming failed to start", "getStreamKeyManually": "We weren’t able to fetch any live streams. Try getting your live stream key from YouTube.", "invalidStreamKey": "Live stream key may be incorrect.", "off": "Live Streaming stopped", "on": "Live Streaming", - "pending": "Starting Live Stream...", + "pending": "Starting Live Stream…", "serviceName": "Live Streaming service", "signedInAs": "You are currently signed in as:", "signIn": "Sign in with Google", @@ -363,7 +386,11 @@ "signOut": "Sign out", "start": "Start a live stream", "streamIdHelp": "What's this?", - "unavailableTitle": "Live Streaming unavailable" + "unavailableTitle": "Live Streaming unavailable", + "onBy": "", + "offBy": "", + "googlePrivacyPolicy": "Google Privacy Policy", + "youtubeTerms": "YouTube terms of services" }, "localRecording": { "clientState": { @@ -414,41 +441,42 @@ "muted": "You have started the conversation muted.", "mutedTitle": "You're muted!", "mutedRemotelyTitle": "You have been muted by {{participantDisplayName}}!", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant", "raisedHand": "{{name}} would like to speak.", "somebody": "Somebody", - "startSilentTitle": "", - "startSilentDescription": "", + "startSilentTitle": "You joined with no audio output!", + "startSilentDescription": "Rejoin the meeting to enable audio", "suboptimalExperienceDescription": "Eer... we are afraid your experience with {{appName}} isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>.", "suboptimalExperienceTitle": "Browser Warning", - "unmute": "", + "unmute": "Unmute", "newDeviceCameraTitle": "New camera detected", "newDeviceAudioTitle": "New audio device detected", - "newDeviceAction": "Use" + "newDeviceAction": "Use", + "suboptimalBrowserWarning": "We are afraid your meeting experience isn't going to be that great here. We are looking for ways to improve this, but until then please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>." }, "passwordSetRemotely": "set by another member", "passwordDigitsOnly": "Up to {{number}} digits", "poweredby": "powered by", "presenceStatus": { "busy": "Busy", - "calling": "Calling...", + "calling": "Calling…", "connected": "Connected", - "connecting": "Connecting...", + "connecting": "Connecting…", "connecting2": "Connecting*...", "disconnected": "Disconnected", "expired": "Expired", "ignored": "Ignored", - "initializingCall": "Initialising Call...", + "initializingCall": "Initialising Call…", "invited": "Invited", "rejected": "Rejected", - "ringing": "Ringing..." + "ringing": "Ringing…" }, "profile": { "setDisplayNameLabel": "Set your display name", "setEmailInput": "Enter e-mail", - "setEmailLabel": "Set your gravatar email", + "setEmailLabel": "Set your Gravatar e-mail", "title": "Profile" }, "recording": { @@ -460,21 +488,23 @@ "error": "Recording failed. Please try again.", "expandedOff": "Recording has stopped", "expandedOn": "The meeting is currently being recorded.", - "expandedPending": "Recording is being started...", + "expandedPending": "Recording is being started…", "failedToStart": "Recording failed to start", "fileSharingdescription": "Share recording with meeting participants", "live": "LIVE", "loggedIn": "Logged in as {{userName}}", "off": "Recording stopped", "on": "Recording", - "pending": "Preparing to record the meeting...", + "pending": "Preparing to record the meeting…", "rec": "REC", "serviceDescription": "Your recording will be saved by the recording service", "serviceName": "Recording service", "signIn": "Sign in", "signOut": "Sign out", "unavailable": "Oops! The {{serviceName}} is currently unavailable. We're working on resolving the issue. Please try again later.", - "unavailableTitle": "Recording unavailable" + "unavailableTitle": "Recording unavailable", + "onBy": "{{name}} started the recording", + "offBy": "{{name}} stopped the recording" }, "sectionList": { "pullToRefresh": "Pull to refresh" @@ -500,7 +530,9 @@ "selectMic": "Microphone", "startAudioMuted": "Everyone starts muted", "startVideoMuted": "Everyone starts hidden", - "title": "Settings" + "title": "Settings", + "speakers": "Speakers", + "microphones": "Microphones" }, "settingsView": { "alertOk": "OK", @@ -515,7 +547,11 @@ "serverURL": "Server URL", "startWithAudioMuted": "Start with audio muted", "startWithVideoMuted": "Start with video muted", - "version": "Version" + "version": "Version", + "showAdvanced": "Show advanced settings", + "disableP2P": "Disable Peer-To-Peer mode", + "disableCallIntegration": "Disable native call integration", + "advanced": "Advanced" }, "share": { "dialInfoText": "\n\n=====\n\nJust want to dial in on your phone?\n\n{{defaultDialInNumber}}Click this link to see the dial in phone numbers for this meeting\n{{dialInfoPageUrl}}", @@ -563,16 +599,21 @@ "recording": "Toggle recording", "remoteMute": "Mute participant", "Settings": "Toggle settings", - "sharedvideo": "Toggle Youtube video sharing", + "sharedvideo": "Toggle YouTube video sharing", "shareRoom": "Invite someone", "shareYourScreen": "Toggle screenshare", "shortcuts": "Toggle shortcuts", - "show": "", + "show": "Show on stage", "speakerStats": "Toggle speaker statistics", "tileView": "Toggle tile view", "toggleCamera": "Toggle camera", "videomute": "Toggle mute video", - "videoblur": "" + "videoblur": "", + "privateMessage": "Send private message", + "muteEveryone": "Mute everyone", + "moreOptions": "Show more options", + "help": "Help", + "download": "Download our apps" }, "addPeople": "Add people to your call", "audioOnlyOff": "Disable audio only mode", @@ -625,7 +666,7 @@ "failedToStart": "Transcribing failed to start", "labelToolTip": "The meeting is being transcribed", "off": "Transcribing stopped", - "pending": "Preparing to transcribe the meeting...", + "pending": "Preparing to transcribe the meeting…", "start": "Start showing subtitles", "stop": "Stop showing subtitles", "tr": "TR" @@ -708,5 +749,15 @@ "sendFeedback": "Send feedback", "terms": "Terms", "title": "Secure, fully featured, and completely free video conferencing" - } -} \ No newline at end of file + }, + "documentSharing": { + "title": "" + }, + "defaultNickname": "", + "chromeExtensionBanner": { + "dontShowAgain": "", + "buttonText": "", + "installExtensionText": "" + }, + "raisedHand": "Would like to speak" +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-eo.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-eo.json index 6ec8e1b17..52859f900 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-eo.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-eo.json @@ -253,13 +253,10 @@ "userPassword": "uzantopasvorto", "WaitForHostMsg": "", "WaitForHostMsgWOk": "", - "WaitingForHost": "Atendanta la gastigan komputilon ...", + "WaitingForHost": "Atendanta la gastigan komputilon …", "Yes": "Jes", "yourEntireScreen": "Via tuta ekrano" }, - "\u0005dialog": { - "accessibilityLabel": {} - }, "dialOut": { "statusMessage": "nun estas {{status}}" }, @@ -272,7 +269,6 @@ "veryBad": "Tre malbona", "veryGood": "Tre bona" }, - "\u0005feedback": {}, "incomingCall": { "answer": "", "audioCallTitle": "", @@ -310,7 +306,6 @@ "tooltip": "", "label": "" }, - "\u0005info": {}, "inviteDialog": { "alertText": "", "header": "", @@ -341,7 +336,6 @@ "toggleShortcuts": "", "videoMute": "Ŝalti aŭ malŝalti vian kameraon" }, - "\u0005keyboardShortcuts": {}, "liveStreaming": { "busy": "Ni penas liberigi tujajn elsendilojn. Bonvolu reprovi post kelkaj minutoj.", "busyTitle": "Ĉiuj elsendiloj nun okupiĝas", @@ -360,7 +354,7 @@ "invalidStreamKey": "", "off": "Tuja elsendfluo finiĝis", "on": "Tuja Elsendfluo", - "pending": "Komencanta Tujan Elsendfluon...", + "pending": "Komencanta Tujan Elsendfluon…", "serviceName": "", "signedInAs": "", "signIn": "", @@ -370,7 +364,6 @@ "streamIdHelp": "", "unavailableTitle": "Tuja elsendfluo ne disponeblas" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { "off": "", @@ -400,7 +393,6 @@ "stop": "Fini registradon", "yes": "Jes" }, - "\u0005localRecording": {}, "lockRoomPassword": "Pasvorto", "lockRoomPasswordUppercase": "Pasvorto", "me": "mi", @@ -452,7 +444,6 @@ "rejected": "", "ringing": "" }, - "\u0005presenceStatus": {}, "profile": { "setDisplayNameLabel": "Agordi vian videblan nomon", "setEmailInput": "Enigu retpoŝtadreson", @@ -484,7 +475,6 @@ "unavailable": "", "unavailableTitle": "Registrado ne disponeblas" }, - "\u0005recording": {}, "sectionList": { "pullToRefresh": "" }, @@ -511,9 +501,6 @@ "startVideoMuted": "Ĉiuj komenciĝas kaŝitaj", "title": "Agordoj" }, - "\u0005settings": { - "calendar": {} - }, "settingsView": { "alertOk": "", "alertTitle": "Averto", @@ -630,9 +617,6 @@ "startvideoblur": "", "stopvideoblur": "" }, - "\u0005toolbar": { - "accessibilityLabel": {} - }, "transcribing": { "ccButtonTooltip": "", "error": "Registrado malsukcesis. Bonvolu provi denove.", @@ -645,7 +629,6 @@ "stop": "", "tr": "" }, - "\u0005transcribing": {}, "userMedia": { "androidGrantPermissions": "Elektu <b><i>Permesi</i></b> kiam via foliumilo petos permesojn.", "chromeGrantPermissions": "Elektu <b><i>Permesi</i></b> kiam via foliumilo petos permesojn.", @@ -725,4 +708,4 @@ "terms": "Kondiĉoj", "title": "" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-es.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-es.json index e312f95a8..24bad2dcf 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-es.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-es.json @@ -1,7 +1,7 @@ { "addPeople": { "add": "Invitar", - "countryNotSupported": "Aun no contamos con soporte a este destino.", + "countryNotSupported": "Aún no contamos con soporte a este destino.", "countryReminder": "¿Llamando fuera de los Estados Unidos? ¡Por favor, asegúrese de empezar con el código de país!", "disabled": "No puede invitar a otras personas.", "failedToAdd": "Error al agregar participantes", @@ -19,39 +19,50 @@ }, "audioDevices": { "bluetooth": "Bluetooth", - "headphones": "Audífonos", + "headphones": "Auriculares", "phone": "Teléfono", - "speaker": "Orador" + "speaker": "Altavoz", + "none": "No hay dispositivos de audio disponibles" }, "audioOnly": { - "audioOnly": "Solo audio" + "audioOnly": "Bajo consumo de Ancho de Banda" }, "calendarSync": { "addMeetingURL": "Agregar un vínculo a la reunión", "confirmAddLink": "¿Quiere añadir un enlace de Jitsi a este evento?", "error": { - "appConfiguration": "La integración del calendario no se está configurada correctamente", + "appConfiguration": "La integración del calendario no está configurada correctamente", "generic": "Se ha producido un error. Compruebe la configuración del calendario o pruebe a recargarlo", "notSignedIn": "Se ha producido un error de autenticación para ver los eventos del calendario. Compruebe la configuración del calendario e intente iniciar sesión de nuevo" }, - "join": "Unir", + "join": "Unirse", "joinTooltip": "Unirse a la reunión", "nextMeeting": "próxima reunión", "noEvents": "No hay eventos próximos programados.", - "ongoingMeeting": "reunión en proceso", + "ongoingMeeting": "reunión en curso", "permissionButton": "Abrir ajustes", "permissionMessage": "Los permisos al calendario son necesarios para ver sus reuniones en la aplicación.", "refresh": "Actualizar calendario", "today": "Hoy" }, "chat": { - "error": "Error: su mensaje \"{{originalText}}\" no fue enviado. Motivo: {{error}}", + "error": "Error: su mensaje no se envío. Motivo: {{error}}", + "fieldPlaceHolder": "Escriba su mensaje aquí", "messagebox": "Escriba un mensaje", + "messageTo": "Mensaje privado para {{recipient}}", + "noMessagesMessage": "No hay mensajes en la reunión. ¡Inicie una conversación!", "nickname": { "popover": "Seleccione un apodo", "title": "Introduzca un apodo para usar el chat" }, - "title": "Chat" + "privateNotice": "Mensaje privado para {{recipient}}", + "title": "Chat", + "you": "usted" + }, + "chromeExtensionBanner": { + "installExtensionText": "", + "buttonText": "", + "dontShowAgain": "" }, "connectingOverlay": { "joiningRoom": "Conectándose a su reunión…" @@ -66,15 +77,19 @@ "DISCONNECTED": "Desconectado", "DISCONNECTING": "Desconectando", "ERROR": "Error", - "RECONNECTING": "Ocurrió un problema en la red. Reconectando..." + "RECONNECTING": "Ocurrió un problema en la red. Reconectando...", + "LOW_BANDWIDTH": "", + "GOT_SESSION_ID": "", + "GET_SESSION_ID_ERROR": "", + "FETCH_SESSION_ID": "" }, "connectionindicator": { "address": "Dirección:", "bandwidth": "Ancho de banda estimado:", - "bitrate": "Tasa de bits:", + "bitrate": "Tasa de transferencia:", "bridgeCount": "Contador del servidor: ", "connectedTo": "Conectado a:", - "framerate": "Tasa de cuadros:", + "framerate": "Fotogramas por segundo:", "less": "Mostrar menos", "localaddress": "Dirección local:", "localaddress_plural": "Direcciones locales:", @@ -85,8 +100,8 @@ "quality": { "good": "Bueno", "inactive": "Inactivo", - "lost": "Perdida", - "nonoptimal": "No óptima", + "lost": "Perdido", + "nonoptimal": "No óptimo", "poor": "Pobre" }, "remoteaddress": "Dirección remota:", @@ -94,10 +109,9 @@ "remoteport": "Puerto remoto:", "remoteport_plural": "Puertos remotos:", "resolution": "Resolución:", - "status": "Conexión:", + "status": "Estado:", "transport": "Transporte:", - "transport_plural": "Transportes:", - "turn": " (turnar)" + "transport_plural": "Transportes:" }, "dateUtils": { "earlier": "Anterior", @@ -105,16 +119,17 @@ "yesterday": "Ayer" }, "deepLinking": { - "appNotInstalled": "Usted necesita la aplicación móvil {{app}} para unirse a esta reunión en su teléfono.", + "appNotInstalled": "Necesita la aplicación móvil {{app}} para unirse a esta reunión en su teléfono.", "description": "¿No pasó nada? Hemos intentado iniciar su reunión en la aplicación de escritorio {{app}}. intente de nuevo o inicie en la aplicación web {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "¿No pasó nada? Intentamos iniciar su reunión en la aplicación de escritorio {{app}}.", "downloadApp": "Descargar la app", "launchWebButton": "Iniciar en web", "openApp": "Continuar a la aplicación", - "title": "Iniciar su reunión en {{app}}...", + "title": "Iniciar su reunión en {{app}}…", "tryAgainButton": "Intentar de nuevo en el escritorio" }, "defaultLink": "ej. {{url}}", + "defaultNickname": "ej. Juana Rosas", "deviceError": { "cameraError": "Error al acceder a su cámara", "cameraPermission": "Error al obtener permiso de la cámara", @@ -132,7 +147,7 @@ "liveStreaming": "Transmisión en Vivo" }, "allow": "Permitir", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Otro participante esta compartiendo un video. Esta conferencia solo permite compartir un video a la vez.", "alreadySharedVideoTitle": "Solo se permite un video compartido a la vez", "applicationWindow": "Ventana de aplicación", "Back": "Anterior", @@ -145,9 +160,9 @@ "cameraUnsupportedResolutionError": "Su cámara no soporta la resolución de video.", "Cancel": "Cancelar", "close": "Cerrar", - "conferenceDisconnectMsg": "Es posible que desee comprobar la conexión de red. Reconectando en {{seconds}} segundos...", + "conferenceDisconnectMsg": "Es posible que desee comprobar la conexión de red. Reconectando en {{seconds}} segundos…", "conferenceDisconnectTitle": "Ha sido desconectado.", - "conferenceReloadMsg": "Estamos tratando de arreglar esto. Reconectando en {{seconds}} segundos...", + "conferenceReloadMsg": "Estamos tratando de arreglar esto. Reconectando en {{seconds}} segundos…", "conferenceReloadTitle": "Desafortunadamente, algo salió mal.", "confirm": "Confirmar", "confirmNo": "No", @@ -158,51 +173,57 @@ "contactSupport": "Contacte al soporte técnico", "copy": "Copiar", "dismiss": "Descartar", - "displayNameRequired": "", - "done": "Ninguno", - "enterDisplayName": "", + "displayNameRequired": "¡Hola! ¿Cuál es tu nombre?", + "done": "Listo", + "enterDisplayName": "Por favor ingresa tu nombre aquí", "error": "Error", "externalInstallationMsg": "Necesita instalar nuestra extensión para compartir escritorio.", "externalInstallationTitle": "Extensión requerida", "goToStore": "Ir al webstore", "gracefulShutdown": "Nuestro servicio se encuentra en mantenimiento. Por favor, intente más tarde.", "IamHost": "Yo soy el anfitrión", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Contraseña incorrecta", "incorrectPassword": "Nombre de usuario o contraseña incorrecta", "inlineInstallationMsg": "Necesita instalar nuestra extensión para compartir escritorio.", "inlineInstallExtension": "Instalar ahora", "internalError": "¡Oops! Algo salió mal. El siguiente error ocurrió: {{error}}", "internalErrorTitle": "Error interno", - "kickMessage": "", + "kickMessage": "Puede ponerse en contacto con {{participantDisplayName}} para obtener más detalles.", "kickParticipantButton": "Expulsar", "kickParticipantDialog": "¿Seguro que quiere expulsar a este participante?", - "kickParticipantTitle": "¿Silenciar a este participante?", - "kickTitle": "", + "kickParticipantTitle": "¿Expulsar a este participante?", + "kickTitle": "¡Ay! {{participantDisplayName}} te expulsó de la reunión", "liveStreaming": "Emisión en Directo", "liveStreamingDisabledForGuestTooltip": "Los invitados no pueden iniciar transmisiones en vivo.", "liveStreamingDisabledTooltip": "Iniciar transmisión en vivo deshabilitado.", "lockMessage": "No se pudo bloquear la conferencia.", - "lockRoom": "", + "lockRoom": "Agregar reunion $t(lockRoomPasswordUppercase)", "lockTitle": "El bloqueo falló", "logoutQuestion": "¿Está seguro que desea salir y detener la conferencia?", "logoutTitle": "Cerrar sesión", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "El límite máximo de participantes ha sido alcanzado. La conferencia está llena. Por favor contacta al organizador o intenta mas tarde.", + "maxUsersLimitReachedTitle": "Se ha alcanzado el límite máximo de participantes", "micConstraintFailedError": "El micrófono no satisface algunos de los requerimientos.", "micNotFoundError": "No se encontró el micrófono.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Vaya a la configuración de su computadora para activar el micrófono y ajustar su nivel", + "micNotSendingDataTitle": "Su micrófono está silenciado en la configuración de su sistema", "micPermissionDeniedError": "No ha otorgado permisos para usar su micrófono. Puede unirse a la conferencia, pero no lo podrán escuchar. Utilice el botón en la barra de dirección para solucionar esto.", - "micUnknownError": "No se puede usar su micrófono por motivos desconocidos.", + "micUnknownError": "No se puede usar el micrófono por motivos desconocidos.", + "muteEveryoneElseDialog": "Una vez silenciados, no podrás quitarles el modo silencio, pero ellos podrán hacerlo en cualquier momento.", + "muteEveryoneElseTitle": "¿Silenciar a todos los participantes excepto a {{whom}}?", + "muteEveryoneDialog": "¿Estás seguro de silenciar a todos los participantes? No podrás quitarles el modo en silencio, pero ellos podrán hacerlo en cualquier momento.", + "muteEveryoneTitle": "¿Silenciar a todos los participantes?", + "muteEveryoneSelf": "A ti mismo", + "muteEveryoneStartMuted": "Todos los participantes comienzan silenciados a partir de ahora", "muteParticipantBody": "No podrás quitarles el modo en silencio, pero ellos pueden quitárselo en cualquier momento.", - "muteParticipantButton": "Control de escritorio remoto", + "muteParticipantButton": "Silenciar", "muteParticipantDialog": "¿Seguro que quiere silenciar a este participante? No podrá revertir esta acción, pero el participante podrá hacerlo en cualquier momento", "muteParticipantTitle": "¿Silenciar a este participante?", "Ok": "Aceptar", - "passwordLabel": "", - "passwordNotSupported": "No se soporta establecer contraseña para una reunión.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "No se soporta $t(lockRoomPassword) en la reunión", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) no es compatible", + "passwordRequired": "$t(lockRoomPasswordUppercase) necesario", "popupError": "Su navegador está bloqueando las ventanas emergentes de este sitio. Habilite las ventanas emergentes en la configuración de seguridad de su navegador y vuelva a intentarlo.", "popupErrorTitle": "Ventana emergente bloqueada", "recording": "Grabando", @@ -217,7 +238,7 @@ "remoteControlStopMessage": "La sesión de control remoto ha finalizado!", "remoteControlTitle": "Control de escritorio remoto", "Remove": "Eliminar", - "removePassword": "", + "removePassword": "Eliminar $t(lockRoomPassword)", "removeSharedVideoMsg": "¿Está seguro que desea eliminar su vídeo compartido?", "removeSharedVideoTitle": "Eliminar video compartido", "reservationError": "Error del sistema de reservación", @@ -228,6 +249,10 @@ "screenSharingFirefoxPermissionDeniedError": "Algo salió mal mientras tratábamos de compartir la pantalla. Por favor, asegúrese de que nos ha dado permiso para hacerlo. ", "screenSharingFirefoxPermissionDeniedTitle": "OOPS! ¡ No pudimos empezar a compartir la pantalla!", "screenSharingPermissionDeniedError": "Oops! Algo salió mal con sus permisos de extensión para compartir pantalla. Por favor, vuelva a cargar e intente de nuevo.", + "sendPrivateMessage": "Acaba de recibir un mensaje privado. ¿Desea responder de forma privada?, ¿Desea responder al grupo?", + "sendPrivateMessageCancel": "Enviar al grupo", + "sendPrivateMessageOk": "Enviar privado", + "sendPrivateMessageTitle": "¿Enviar privado?", "serviceUnavailable": "Servicio no disponible", "sessTerminated": "Llamada terminada", "Share": "Compartir", @@ -250,16 +275,20 @@ "tokenAuthFailed": "Lo siento, usted no tiene permiso para unirse a este llamada.", "tokenAuthFailedTitle": "Falló la autenticación", "transcribing": "Transcribiendo", - "unlockRoom": "", + "unlockRoom": "Eliminar reunión $t(lockRoomPassword)", "userPassword": "contraseña del usuario", "WaitForHostMsg": "La conferencia <b> {{room}} </b> aún no ha comenzado. Si usted es el anfitrión, por favor autentíquese. De lo contrario, espere a que llegue el anfitrión.", "WaitForHostMsgWOk": "La conferencia <b> {{room}} </b> aún no ha comenzado. Si usted es el anfitrión, presione Ok para autenticar. De lo contrario, espere a que llegue el anfitrión.", - "WaitingForHost": "Esperando al anfitrión ...", + "WaitingForHost": "Esperando al anfitrión …", "Yes": "Sí", - "yourEntireScreen": "Su pantalla completa" + "yourEntireScreen": "Su pantalla completa", + "muteEveryoneDialog": "Silenciar a todos" }, "dialOut": { - "statusMessage": "esta {{status}}" + "statusMessage": "está {{status}}" + }, + "documentSharing": { + "title": "Compartir documento" }, "feedback": { "average": "Promedio", @@ -279,40 +308,40 @@ }, "info": { "accessibilityLabel": "Mostrar Información", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Agregar $t(lockRoomPassword)", + "cancelPassword": "Cancelar $t(lockRoomPassword)", "conferenceURL": "Enlace:", "country": "País", - "dialANumber": "Para unirse a su reunión, marque uno de estos números y luego ingrese el pin.", + "dialANumber": "Para unirse a la reunión, marque uno de estos números e introduzca el PIN", "dialInConferenceID": "PIN:", "dialInNotSupported": "Lo sentimos, actualmente no se admite la marcación.", "dialInNumber": "Marcar:", - "dialInSummaryError": "Error al obtener información de acceso telefónico ahora. Por favor, inténtelo de nuevo más tarde.", - "dialInTollFree": "Llamada gratuita", + "dialInSummaryError": "Se ha producido un error al capturar la información de marcación. Vuelva a intentarlo más tarde", + "dialInTollFree": "Número gratuito", "genericError": "Ups, algo salió mal.", "inviteLiveStream": "Marcado de un solo toque: {{number}},,{{conferenceID}}#", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "Usted está invitado a unirse a una reunión.", + "invitePhone": "Para unirse por teléfono, presione: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "¿Busca un número de marcación diferente?\nConsulte los números de marcación de la reunión: {{url}}\n\n\nSi está marcando a través del teléfono de otra reunión, únase sin conectarse al audio: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Le han invitado a unirse a una reunión", "inviteURLFirstPartPersonal": "{{name}} te esta invitando a una sesión.\n", - "inviteURLSecondPart": "", + "inviteURLSecondPart": "\nUnirse a la reunión:\n{{url}}\n", "liveStreamURL": "Transmisión en vivo:", "moreNumbers": "Más números", "noNumbers": "Sin números a marcar.", "noPassword": "Ninguno", "noRoom": "No se especificó la sala a marcar.", "numbers": "Números de marcado", - "password": "", + "password": "$t(lockRoomPasswordUppercase):", "title": "Compartir", - "tooltip": "Compartir el enlace y la información de acceso telefónico para esta reunión", - "label": "Información de la sesión" + "tooltip": "Compartir el enlace y acceso telefónico para esta reunión", + "label": "Información de la reunión" }, "inviteDialog": { - "alertText": "Error al invitar a algunos participantes ", + "alertText": "Se ha producido un error al invitar a algunos participantes", "header": "Invitar", "searchCallOnlyPlaceholder": "Introduzca número de télefono", - "searchPeopleOnlyPlaceholder": "Buscar participantes ", - "searchPlaceholder": "Participante o número de teléfono ", + "searchPeopleOnlyPlaceholder": "Buscar participantes", + "searchPlaceholder": "Participante o número de teléfono", "send": "Enviar" }, "inlineDialogFailure": { @@ -327,15 +356,16 @@ "fullScreen": "Ver o salir de pantalla completa", "keyboardShortcuts": "Atajos de teclado", "localRecording": "Mostrar u ocultar controles de grabación locales", - "mute": "Activar o desactivar micrófono", + "mute": "Activar o silenciar el micrófono", "pushToTalk": "Presione para hablar", "raiseHand": "Levantar o bajar la mano", - "showSpeakerStats": "Mostrar estadísticas del locutor", + "showSpeakerStats": "Estadísticas de participantes", "toggleChat": "Abrir o cerrar panel de chat", "toggleFilmstrip": "Mostrar/Ocultar miniaturas de video", "toggleScreensharing": "Cambiar entre cámara y compartir pantalla", "toggleShortcuts": "Mostrar/ocultar atajos del teclado", - "videoMute": "Activar o desactivar tu cámara" + "videoMute": "Activar o desactivar tu cámara", + "videoQuality": "Administrar la calidad de llamadas" }, "liveStreaming": { "busy": "Estamos trabajando para liberar recursos de transmisión. Por favor, inténtelo de nuevo en unos minutos.", @@ -349,13 +379,15 @@ "errorLiveStreamNotEnabled": "La transmisión en vivo no está activada en {{email}}. Por favor, active la transmisión en vivo o inicie sesión en una cuenta con transmisión en vivo activada.", "expandedOff": "La transmisión en vivo se ha detenido", "expandedOn": "La reunión se está transmitiendo a YouTube.", - "expandedPending": "La transmisión en vivo se está iniciando ...", + "expandedPending": "La transmisión en vivo se está iniciando …", "failedToStart": "La transmisión en vivo no se puso iniciar", "getStreamKeyManually": "No pudimos buscar ninguna transmisión en vivo. Trate de obtener su clave de transmisión en vivo de YouTube.", - "invalidStreamKey": "La clave de transmisión en vivo puede ser incorrecta.", + "invalidStreamKey": "Es posible que la clave de transmisión sea incorrecta", "off": "Transmisión en vivo detenida", + "offBy": "{{name}} transmisión en directo", "on": "Emisión en Directo", - "pending": "Iniciando Emisión en Directo...", + "onBy": "{{name}} transmisión en directo iniciada", + "pending": "Iniciando Emisión en Directo…", "serviceName": "Servicio de streaming en vivo", "signedInAs": "Actualmente está conectado como:", "signIn": "Iniciar sesión con Google", @@ -406,51 +438,52 @@ "focusFail": "{{component}} no disponible - reintentar en {{ms}} seg", "grantedTo": "¡Se otorgaron privilegios de moderador a {{to}}!", "invitedOneMember": "{{displayName}} ha sido invitado", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedThreePlusMembers": "{{name}} y otros {{count}} fueron invitados", + "invitedTwoMembers": "{{first}} y {{second}} fueron invitados", + "kickParticipant": "{{kicked}} desalojado por {{kicker}}", "me": "Yo", "moderator": "¡Se otorgaron privilegios de moderador!", "muted": "Has iniciado la conversación silenciado.", "mutedTitle": "¡Estás silenciado!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyTitle": "¡{{Nombre del participante}} te ha silenciado!", + "mutedRemotelyDescription": "Siempre puedes quitar el silencio cuando estés listo para hablar. Silencie cuando termine para mantener el ruido alejado de la reunión.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) eliminado por otro participante", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) establecido por otro participante", "raisedHand": "{{name}} quisiera hablar.", "somebody": "Alguien", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Eer... Al parecer su experiencia con {{appName}} no será tan buena aquí. Estamos buscando formas de mejorar esto pero hasta entonces, intente utilizar uno de los <a href='static/recommendedBrowsers.html' target='_blank'> navegadores compatibles</a>.", + "startSilentTitle": "¡Te uniste sin salida de audio!", + "startSilentDescription": "Vuelva a unirse a la reunión para habilitar el audio", + "suboptimalBrowserWarning": "Tememos que su experiencia en la reunión no sea tan buena. Estamos buscando formas de mejorar esto, pero hasta entonces intente utilizar uno de los <a href='static/recommendedBrowsers.html' target='_blank'>navegadores totalmente compatibles</a>.", "suboptimalExperienceTitle": "Advertencia del Explorador", - "unmute": "", - "newDeviceCameraTitle": "Nueva cámara detectada ", - "newDeviceAudioTitle": "Nuevo dispositivo de audio detectado ", + "unmute": "Dejar de silenciar", + "newDeviceCameraTitle": "Se ha detectado una nueva cámara", + "newDeviceAudioTitle": "Se ha detectado un nuevo dispositivo de audio", "newDeviceAction": "Usar" }, "passwordSetRemotely": "definido por otro participante", - "passwordDigitsOnly": "", + "passwordDigitsOnly": "Hasta {{number}} dígitos", "poweredby": "proporcionado por", "presenceStatus": { "busy": "Ocupado", - "calling": "Llamando...", + "calling": "Llamando…", "connected": "Conectado", "connecting": "Conectando…", "connecting2": "Conectando*…", "disconnected": "Desconectado", "expired": "Expirado", "ignored": "Ignorado", - "initializingCall": "Iniciando llamada...", + "initializingCall": "Iniciando llamada…", "invited": "Invitado", "rejected": "Rechazado", - "ringing": "Timbrando..." + "ringing": "Timbrando…" }, "profile": { "setDisplayNameLabel": "Establecer nombre a mostrar", "setEmailInput": "Introducir e-mail", - "setEmailLabel": "Establecer su gravatar", + "setEmailLabel": "Establecer su Gravatar", "title": "Perfil" }, + "raisedHand": "Desea hablar", "recording": { "authDropboxText": "Subir a Dropbox", "availableSpace": "Espacio disponible: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de grabación)", @@ -460,18 +493,20 @@ "error": "Falla de grabación. Vuelva a intentarlo.", "expandedOff": "Grabación detenida", "expandedOn": "La reunión está siendo grabada.", - "expandedPending": "La grabación se está inciando...", + "expandedPending": "La grabación se está inciando…", "failedToStart": "No se pudo iniciar la grabación", - "fileSharingdescription": "Compartir grabación con los participantes de la sesión ", + "fileSharingdescription": "Compartir la grabación con los participantes de la reunión", "live": "Directo", "loggedIn": "Sesión iniciada como {{userName}}", "off": "Grabación detenida", + "offBy": "{{name}} detuvo la grabación", "on": "Grabando", - "pending": "Preparando para grabar la reunión...", + "onBy": "{{name}} comenzó la grabación", + "pending": "Preparando para grabar la reunión…", "rec": "REC", - "serviceDescription": "Tu grabación sera guardada por el servicio de grabación ", + "serviceDescription": "El servicio de grabación guardará la grabación", "serviceName": "Servicio de grabación", - "signIn": "Entrar", + "signIn": "Iniciar sesión", "signOut": "Cerrar sesión", "unavailable": "Oops! El {{serviceName}} no está disponible actualmente. Estamos trabajando para resolver la situación. Por favor intente más tarde.", "unavailableTitle": "Grabación no disponible" @@ -498,37 +533,41 @@ "selectAudioOutput": "Salida de audio", "selectCamera": "Cámara", "selectMic": "Micrófono", - "startAudioMuted": "Todos inician en silencio", - "startVideoMuted": "Todos inician ocultos", + "startAudioMuted": "Todos inician silenciados", + "startVideoMuted": "Todos inician con cámara desactivada", "title": "Ajustes" }, "settingsView": { + "advanced": "Avanzado", "alertOk": "OK", "alertTitle": "Aviso", "alertURLText": "La dirección URL del servidor no es válida", - "buildInfoSection": "Generar información ", + "buildInfoSection": "Información de la compilación", "conferenceSection": "Conferencia", + "disableCallIntegration": "Deshabilitar la integración nativa de llamadas", + "disableP2P": "Deshabilitar el modo punto a punto", "displayName": "Nombre a mostrar", "email": "Email", "header": "Ajustes", "profileSection": "Perfil", "serverURL": "URL del servidor", + "showAdvanced": "Mostrar configuración avanzada", "startWithAudioMuted": "Inicio con audio en silencio", "startWithVideoMuted": "Iniciar con el vídeo en silencio", - "version": "Versión " + "version": "Versión" }, "share": { - "dialInfoText": "", - "mainText": "Pulse en el siguiente enlace para unirse a la reunión:\n{{roomUrl}}" + "dialInfoText": "\n\n=====\n\n¿Solo quieres marcar en tu teléfono?\n\n{{defaultDialInNumber}} Haga clic en este enlace para ver el marcado en los números de teléfono de esta reunión\n{{dialInfoPageUrl}}", + "mainText": "Presione en el siguiente enlace para unirse a la reunión:\n{{roomUrl}}" }, - "speaker": "Orador", + "speaker": "Participante", "speakerStats": { "hours": "{{count}}h", "minutes": "{{count}}m", "name": "Nombre", "seconds": "{{count}}s", - "speakerStats": "Estadísticas del locutor", - "speakerTime": "Tiempo del locutor" + "speakerStats": "Estadísticas de participantes", + "speakerTime": "Tiempo hablado" }, "startupoverlay": { "policyText": " ", @@ -543,89 +582,102 @@ "accessibilityLabel": { "audioOnly": "Alternar sólo audio", "audioRoute": "Seleccione el dispositivo de sonido", - "callQuality": "Administrar la calidad de llamadas", + "callQuality": "Administrar la calidad del video", "cc": "Alternar subtítulos", "chat": "Alternar ventana de chat", "document": "Alternar documento compartido", + "download": "Descarga nuestras aplicaciones", "feedback": "Dejar comentarios", "fullScreen": "Alternar pantalla completa", "hangup": "Dejar la llamada", + "help": "Ayuda", "invite": "Invitar personas", - "kick": "Expulsar participante ", + "kick": "Expulsar participante", "localRecording": "Alternar controles de grabación locales", - "lockRoom": "Cambiar contraseña de sesión ", + "lockRoom": "Activar o desactivar contraseña de la reunión", "moreActions": "Menú alternar más acciones", "moreActionsMenu": "Menú más acciones", "mute": "Alternar audio mudo", "pip": "Alternar modo de Picture-in-Picture", + "privateMessage": "Enviar mensaje privado", "profile": "Editar tu perfil", "raiseHand": "Levantar / Bajar tu mano", "recording": "Activar grabación", - "remoteMute": "Silenciar participante ", + "remoteMute": "Silenciar participante", "Settings": "Alternar configuración", "sharedvideo": "Alternar compartir un vídeo de YouTube", "shareRoom": "Invitar a alguien", "shareYourScreen": "Alternar compartir pantalla", "shortcuts": "Alternar accesos directos", - "show": "", + "show": "Mostrar en escena", "speakerStats": "Alternar estadísticas del orador", "tileView": "Alternar vista de mosaico", "toggleCamera": "Alternar cámara", "videomute": "Alternar silencio de video", - "videoblur": "" + "videoblur": "Alternar desenfoque de video" }, "addPeople": "Agregar personas a su llamada", - "audioOnlyOff": "Habilitar el modo de solo audio", - "audioOnlyOn": "Habilitar el modo de solo audio", + "audioOnlyOff": "Deshabilitar el modo de ancho de banda bajo", + "audioOnlyOn": "Habilitar el modo de ancho de banda bajo", "audioRoute": "Seleccione el dispositivo de sonido", "authenticate": "Autenticar", - "callQuality": "Administrar la calidad de llamadas", + "callQuality": "Administrar la calidad del video", "chat": "Abrir / cerrar sala de charla", - "closeChat": "Cerrar chat ", + "closeChat": "Cerrar chat", "documentClose": "Cerrar documento compartido", "documentOpen": "Abrir documento compartido", + "download": "Descarga nuestras aplicaciones", "enterFullScreen": "Ver pantalla completa", - "enterTileView": "Entrar en la vista de mosaico", + "enterTileView": "Entrar en vista de mosaico", "exitFullScreen": "Salir de pantalla completa", - "exitTileView": "Salir de la vista de mosaico", + "exitTileView": "Salir de vista de mosaico", "feedback": "Dejar comentarios", "hangup": "Salir", + "help": "Ayuda", "invite": "Invitar personas", "login": "Inicio de sesión", "logout": "Cerrar sesión", - "lowerYourHand": "Baja tu mano", + "lowerYourHand": "Bajar la mano", "moreActions": "Más acciones", - "mute": "Activar / Desactivar Silencio", + "moreOptions": "Más opciones", + "mute": "Activar o silenciar el micrófono", + "muteEveryone": "Silenciar a todos", + "noAudioSignalTitle": "¡No hay entrada proveniente de su micrófono!", + "noAudioSignalDesc": "Si no lo silenció a propósito desde la configuración del sistema o el dispositivo, considere cambiar el dispositivo.", + "noAudioSignalDescSuggestion": "Si no lo silenció a propósito desde la configuración del sistema o el dispositivo, considere usar el siguiente dispositivo:", + "noisyAudioInputTitle": "", + "noisyAudioInputDesc": "", "openChat": "Abrir chat", "pip": "Entra en el modo Picture-in-Picture", + "privateMessage": "Enviar mensaje privado", "profile": "Editar tu perfil", "raiseHand": "Levantar / Bajar tu mano", - "raiseYourHand": "Levanta tu mano", + "raiseYourHand": "Alzar la mano", "Settings": "Ajustes", "sharedvideo": "Compartir un vídeo de YouTube", "shareRoom": "Invitar a alguien", "shortcuts": "Ver accesos directos", "speakerStats": "Estadísticas del locutor", - "startScreenSharing": "Iniciar el uso compartido de pantalla", + "startScreenSharing": "Comenzar a compartir pantalla", "startSubtitles": "Iniciar subtítulos", - "stopScreenSharing": "Detener el uso compartido de pantalla ", - "stopSubtitles": "Detener subtítulos ", + "stopScreenSharing": "Dejar de compartir pantalla", + "stopSubtitles": "Detener subtítulos", "stopSharedVideo": "Detener vídeo de YouTube", - "talkWhileMutedPopup": "Tratas de hablar? Estás silenciado.", + "talkWhileMutedPopup": "¿Tratas de hablar? Estás silenciado.", "tileViewToggle": "Alternar vista de mosaico", "toggleCamera": "Alternar cámara", "videomute": "Iniciar / detener cámara", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Desenfocar mi fondo", + "stopvideoblur": "Desactivar desenfoque de fondo" }, "transcribing": { - "ccButtonTooltip": "Iniciar / Detener Subtítulos", + "ccButtonTooltip": "Iniciar / Detener subtítulos", "error": "La Transcripción falló. Por favor, inténtelo nuevamente.", "expandedLabel": "Transcripción encendida", "failedToStart": "No es posible iniciar la transcripción", "labelToolTip": "La reunión se esta transcribiendo", "off": "Transcripción detenida", - "pending": "Preparando la transcripción de la reunión...", + "pending": "Preparando la transcripción de la reunión…", "start": "Mostrar subtítulos", "stop": "Dejar de mostrar subtítulos", "tr": "TR" @@ -654,32 +706,36 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "Se encuentra en modalidad solo audio. Esta modalidad ahorra ancho de banda sin embargo no verá el video de otros.", - "callQuality": "", + "audioOnlyExpanded": "Estás en modo de ancho de banda bajo. En este modo, solo recibirá audio y pantalla compartida.", + "callQuality": "Calidad de video", "hd": "HD", + "hdTooltip": "Visualizando vídeo en alta definición", "highDefinition": "Alta definición", "labelTooiltipNoVideo": "No hay vídeo", - "labelTooltipAudioOnly": "Modo de sólo audio activado", + "labelTooltipAudioOnly": "Modo de ancho de banda bajo habilitado", "ld": "LD", + "ldTooltip": "Visualizando vídeo en baja definición", "lowDefinition": "Baja definición", "onlyAudioAvailable": "Solo hay audio disponible", "onlyAudioSupported": "Solo soportamos audio en este navegador.", "p2pEnabled": "Punto a Punto Activado", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "En el modo punto a punto, la calidad de video recibida solo se puede alternar entre calidad alta y solo audio. No se respetarán otras configuraciones hasta que se salga del modo punto a punto.", "recHighDefinitionOnly": "Preferirá alta definición.", "sd": "SD", + "sdTooltip": "Visualizando vídeo en definición estándar", "standardDefinition": "Definición estándar" }, "videothumbnail": { - "domute": "Control de escritorio remoto", + "domute": "Silenciar", + "domuteOthers": "Silenciar a todos", "flip": "Voltear", "kick": "Expulsar", "moderator": "Moderador", "mute": "Participante está silenciado", "muted": "Silenciado", "remoteControl": "Control remoto", - "show": "", - "videomute": "" + "show": "Mostrar en escena", + "videomute": "El participante ha detenido la cámara." }, "welcomepage": { "accessibilityLabel": { @@ -695,18 +751,24 @@ "connectCalendarButton": "Conecte su calendario", "connectCalendarText": "Conecte su calendario para ver todas sus reuniones en {{app}}. Plus, add {{provider}}reuniones a tu calendario e iniciarlas con un solo clic.", "enterRoomTitle": "Comenzar una reunión", + "roomNameAllowedChars": "El nombre de la reunión no debe contener ninguno de estos caracteres: ?, &, :, ', \", %, #.", "go": "IR", - "join": "UNIRSE", + "goSmall": "IR", + "join": "CREAR / UNIRSE", "info": "Información", "privacy": "Privacidad", "recentList": "Reciente", "recentListDelete": "Borrar", "recentListEmpty": "Su lista de recientes está actualmente vacía. Chatea con tu equipo y encontrarás todas tus reuniones aquí.", - "reducedUIText": "", + "reducedUIText": "¡Bienvenido a {{app}}!", "roomname": "Introduzca un nombre de sala", "roomnameHint": "Introduce el nombre o URL de la sala a la que quieres unirte. Puedes crear un nombre nuevo, sólo tienes que hacer llegar este nombre al resto de participantes para que puedan unirse a esta sala.", "sendFeedback": "Enviar comentarios", "terms": "Términos", - "title": "Seguro, lleno de funcionalidades y videoconferencias completamente gratuitas" + "title": "Videoconferencias seguras, con gran variedad de funcionalidades y completamente gratuitas" + }, + "lonelyMeetingExperience": { + "button": "Invitar a otros", + "youAreAlone": "Eres el único en la reunión" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-esUS.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-esUS.json index 70a1d969c..339dcfd12 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-esUS.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-esUS.json @@ -1,80 +1,95 @@ { "addPeople": { "add": "Invitar", - "countryNotSupported": "Todavía no admitimos este destino.", - "countryReminder": "¿Llamas fuera de los EE. UU.? Asegúrate de comenzar con el código de país.", - "disabled": "No puedes invitar personas.", - "failedToAdd": "", - "footerText": "La marcación externa está deshabilitada.", + "countryNotSupported": "Aun no contamos con soporte a este destino.", + "countryReminder": "¿Llamando fuera de los Estados Unidos? ¡Por favor, asegúrese de empezar con el código de país!", + "disabled": "No puede invitar a otras personas.", + "failedToAdd": "Error al agregar participantes", + "footerText": "La marcación está desactivada.", "loading": "Buscar personas y números de teléfono", - "loadingNumber": "Validar número de teléfono", - "loadingPeople": "Buscar personas para invitar", - "noResults": "No se encontraron resultados de búsqueda que coincidan", - "noValidNumbers": "Introduce un número de teléfono", - "searchNumbers": "Agregar números de teléfono", - "searchPeople": "Buscar personas", - "searchPeopleAndNumbers": "Buscar personas o agregar sus números de teléfono", + "loadingNumber": "Validando el número de teléfono", + "loadingPeople": "Buscando contactos a invitar", + "noResults": "No se encontraron coincidencias", + "noValidNumbers": "Por favor ingrese un número de teléfono", + "searchNumbers": "Agregar números telefónicos", + "searchPeople": "Búsqueda de personas", + "searchPeopleAndNumbers": "Buscar personas o añadir sus números de teléfono", "telephone": "Teléfono: {{number}}", - "title": "Invitar personas a esta reunión" + "title": "Invitar a otras personas a esta reunión" }, "audioDevices": { "bluetooth": "Bluetooth", "headphones": "Audífonos", "phone": "Teléfono", - "speaker": "Altavoz" + "speaker": "Altavoz", + "none": "No hay dispositivos de audio disponibles" }, "audioOnly": { - "audioOnly": "Solo audio" + "audioOnly": "Ancho de banda bajo" }, "calendarSync": { - "addMeetingURL": "Agregar un enlace de reunión", - "confirmAddLink": "¿Deseas agregar un enlace de Jitsi a este evento?", + "addMeetingURL": "Agregar un vínculo a la reunión", + "confirmAddLink": "¿Quiere añadir un enlace de Jitsi a este evento?", "error": { - "appConfiguration": "La integración del calendario no está correctamente configurada.", - "generic": "Se produjo un error. Comprueba la configuración del calendario o intenta actualizarlo.", - "notSignedIn": "Se produjo un error al autenticar para ver eventos de calendario. Comprueba la configuración del calendario e intenta volver a iniciar sesión." + "appConfiguration": "La integración del calendario no está configurada correctamente", + "generic": "Se ha producido un error. Compruebe la configuración del calendario o pruebe cargarlo nuevamente.", + "notSignedIn": "Se ha producido un error de autenticación para ver los eventos del calendario. Compruebe la configuración del calendario e intente iniciar sesión de nuevo" }, - "join": "Unirse", - "joinTooltip": "Unir a la reunión", - "nextMeeting": "reunión siguiente", - "noEvents": "No hay próximos eventos programados.", - "ongoingMeeting": "reunión en progreso", + "join": "Unir", + "joinTooltip": "Unirse a la reunión", + "nextMeeting": "próxima reunión", + "noEvents": "No hay eventos próximos programados.", + "ongoingMeeting": "reunión en proceso", "permissionButton": "Abrir configuración", - "permissionMessage": "Se requiere el permiso del calendario para ver las reuniones en la aplicación.", + "permissionMessage": "Los permisos al calendario son necesarios para ver sus reuniones en la aplicación.", "refresh": "Actualizar calendario", "today": "Hoy" }, "chat": { - "error": "Error: el mensaje \"{{originalText}}\" no se envió. Motivo: {{error}}", - "messagebox": "Escribir un mensaje", + "error": "Error: su mensaje no se envío. Motivo: {{error}}", + "fieldPlaceHolder": "Escriba su mensaje aquí", + "messagebox": "Escriba un mensaje", + "messageTo": "Mensaje privado para {{recipient}}", + "noMessagesMessage": "No hay mensajes en la reunión. ¡Inicie una conversación!", "nickname": { - "popover": "Elegir un apodo", + "popover": "Seleccione un apodo", "title": "Introducir un apodo para usar el chat" }, - "title": "Chat" + "privateNotice": "Mensaje privado para {{recipient}}", + "title": "Mensajes", + "you": "usted" + }, + "chromeExtensionBanner": { + "installExtensionText": "Instalar la extensión para Google Calendar y la integración con Office 365", + "buttonText": "Instalar extensión de Chrome", + "dontShowAgain": "No mostrar nuevamente" }, "connectingOverlay": { - "joiningRoom": "Conectándote a la reunión..." + "joiningRoom": "Conectando a su reunión…" }, "connection": { "ATTACHED": "Adjunto", - "AUTHENTICATING": "Autenticación", + "AUTHENTICATING": "Autenticando", "AUTHFAIL": "Error de autenticación", "CONNECTED": "Conectado", - "CONNECTING": "Conexión", + "CONNECTING": "Conectando", "CONNFAIL": "Error de conexión", "DISCONNECTED": "Desconectado", - "DISCONNECTING": "Desconexión", + "DISCONNECTING": "Desconectando", "ERROR": "Error", - "RECONNECTING": "Se produjo un problema de red. Reconectando..." + "RECONNECTING": "Ocurrió un problema en la red. Reconectando...", + "FETCH_SESSION_ID": "Obteniendo session-ID…", + "GET_SESSION_ID_ERROR": "Obtener session-id error: {{code}}", + "GOT_SESSION_ID": "Obteniendo session-ID… Listo", + "LOW_BANDWIDTH": "Video para {{displayName}} ha sido deshabilitado para economizar ancho de banda" }, "connectionindicator": { "address": "Dirección:", "bandwidth": "Ancho de banda estimado:", - "bitrate": "Velocidad de transferencia:", - "bridgeCount": "Recuento de servidor: ", + "bitrate": "Tasa de transferencia:", + "bridgeCount": "Contador del servidor: ", "connectedTo": "Conectado a:", - "framerate": "Velocidad de cuadro:", + "framerate": "Fotogramas por segundo:", "less": "Mostrar menos", "localaddress": "Dirección local:", "localaddress_plural": "Direcciones locales:", @@ -94,35 +109,35 @@ "remoteport": "Puerto remoto:", "remoteport_plural": "Puertos remotos:", "resolution": "Resolución:", - "status": "Conexión:", + "status": "Estado:", "transport": "Transporte:", - "transport_plural": "Transportes:", - "turn": " (activar/desactivar)" + "transport_plural": "Transportes:" }, "dateUtils": { - "earlier": "Más temprano", + "earlier": "Temprano", "today": "Hoy", "yesterday": "Ayer" }, "deepLinking": { "appNotInstalled": "Necesitas la aplicación móvil de {{app}} para unirte a esta reunión en el teléfono.", - "description": "¿No sucedió nada? Intentamos iniciar la reunión en la aplicación de escritorio de {{app}}. Vuelve a intentarlo o iníciala en la aplicación web de {{app}}.", - "descriptionWithoutWeb": "", + "description": "¿No pasó nada? Hemos intentado iniciar su reunión en la aplicación {{app}} de escritorio. intente de nuevo o inicie en la aplicación {{app}} vía web.", + "descriptionWithoutWeb": "¿No pasó nada? Intentamos iniciar su reunión en la aplicación {{app}} de escritorio.", "downloadApp": "Descargar la aplicación", "launchWebButton": "Iniciar en la Web", "openApp": "Continuar a la aplicación", - "title": "Iniciando la reunión en {{app}}...", - "tryAgainButton": "Volver a intentar en escritorio" + "title": "Iniciando la reunión en {{app}}…", + "tryAgainButton": "Intentar de nuevo en el escritorio" }, - "defaultLink": "por ejemplo, {{url}}", + "defaultLink": "p.ej. {{url}}", + "defaultNickname": "p. ej. Juan Pérez", "deviceError": { - "cameraError": "No se pudo acceder a la cámara", - "cameraPermission": "Error al obtener el permiso de la cámara", - "microphoneError": "No se pudo acceder al micrófono", - "microphonePermission": "Error al obtener el permiso del micrófono" + "cameraError": "Error al acceder a tu cámara", + "cameraPermission": "Error de permisos en la cámara", + "microphoneError": "Error al acceder a tu micrófono", + "microphonePermission": "Error al obtener permiso del micrófono" }, "deviceSelection": { - "noPermission": "No se otorgó permiso", + "noPermission": "Permiso no concedido", "previewUnavailable": "Vista previa no disponible", "selectADevice": "Seleccionar un dispositivo", "testAudio": "Reproducir un sonido de prueba" @@ -132,11 +147,11 @@ "liveStreaming": "Transmisión en vivo" }, "allow": "Permitir", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Otro participante ya está compartiendo un vídeo. Esta conferencia sólo permite compartir un vídeo a la vez.", "alreadySharedVideoTitle": "Solo se permite un video compartido por vez", "applicationWindow": "Ventana de aplicación", - "Back": "Volver", - "cameraConstraintFailedError": "La cámara no satisface algunas de las limitaciones requeridas.", + "Back": "Anterior", + "cameraConstraintFailedError": "Su cámara no satisface algunos de los requerimientos.", "cameraNotFoundError": "No se encontró la cámara.", "cameraNotSendingData": "No podemos acceder a la cámara. Comprueba si otra aplicación está usando este dispositivo, selecciona otro dispositivo del menú de configuración o intenta volver a cargar la aplicación.", "cameraNotSendingDataTitle": "No es posible acceder a la cámara", @@ -145,79 +160,85 @@ "cameraUnsupportedResolutionError": "La cámara no admite la resolución de video requerida.", "Cancel": "Cancelar", "close": "Cerrar", - "conferenceDisconnectMsg": "Se recomienda que compruebes la conexión de la red. Reconectando en {{seconds}} segundos...", - "conferenceDisconnectTitle": "Se te ha desconectado.", - "conferenceReloadMsg": "Estamos intentando solucionarlo. Reconectando en {{seconds}} segundos...", - "conferenceReloadTitle": "Lamentablemente, algo salió mal.", + "conferenceDisconnectMsg": "Se recomienda que compruebes la conexión de la red. Reconectando en {{seconds}} segundos…", + "conferenceDisconnectTitle": "Ha sido desconectado.", + "conferenceReloadMsg": "Estamos tratando de arreglar esto. Reconectando en {{seconds}} segundos…", + "conferenceReloadTitle": "Desafortunadamente, algo salió mal.", "confirm": "Confirmar", "confirmNo": "No", "confirmYes": "Sí", - "connectError": "¡Uy! Algo salió mal y no pudimos conectar con la conferencia.", - "connectErrorWithMsg": "¡Uy! Algo salió mal y no pudimos conectar con la conferencia: {{msg}}", - "connecting": "Conexión", - "contactSupport": "Contactar con soporte", + "connectError": "¡Oops! Algo salio mal y no fue posible conectarnos a la conferencia.", + "connectErrorWithMsg": "¡Oops! Algo salio mal y no fue posible conectarnos a la conferencia: {{msg}}", + "connecting": "Conectando", + "contactSupport": "Contacte a soporte técnico", "copy": "Copiar", "dismiss": "Descartar", - "displayNameRequired": "", + "displayNameRequired": "¡Hola! ¿Cuál es tu nombre?", "done": "Listo", - "enterDisplayName": "", + "enterDisplayName": "Por favor ingresa tu nombre aquí", "error": "Error", - "externalInstallationMsg": "Tienes que instalar nuestra extensión de uso compartido del escritorio.", - "externalInstallationTitle": "Se requiere extensión", + "externalInstallationMsg": "Necesita instalar nuestra extensión para compartir escritorio.", + "externalInstallationTitle": "Extensión requerida", "goToStore": "Ir a la tienda web", - "gracefulShutdown": "Nuestro servicio está actualmente interrumpido debido a tareas de mantenimiento. Vuelve a intentarlo más tarde.", + "gracefulShutdown": "Nuestro servicio se encuentra en mantenimiento. Por favor, intente más tarde.", "IamHost": "Soy el anfitrión", - "incorrectRoomLockPassword": "", - "incorrectPassword": "Nombre de usuario o contraseña incorrectos", - "inlineInstallationMsg": "Tienes que instalar nuestra extensión de uso compartido del escritorio.", + "incorrectRoomLockPassword": "Contraseña incorrecta", + "incorrectPassword": "Nombre de usuario o contraseña incorrecta", + "inlineInstallationMsg": "Necesita instalar nuestra extensión para compartir escritorio.", "inlineInstallExtension": "Instalar ahora", - "internalError": "¡Uy! Algo salió mal. Se produjo el siguiente error: {{error}}", + "internalError": "¡Oops! Algo salió mal. El siguiente error ocurrió: {{error}}", "internalErrorTitle": "Error interno", - "kickMessage": "", - "kickParticipantButton": "Echar", - "kickParticipantDialog": "¿Estás seguro de que deseas echar a este participante?", - "kickParticipantTitle": "", - "kickTitle": "", + "kickMessage": "Puede ponerse en contacto con {{participantDisplayName}} para obtener más detalles.", + "kickParticipantButton": "Expulsar", + "kickParticipantDialog": "¿Seguro que quiere expulsar a este participante?", + "kickParticipantTitle": "¿Sacar este participante?", + "kickTitle": "¡Ay! {{participantDisplayName}} te expulsó de la reunión", "liveStreaming": "Transmisión en vivo", "liveStreamingDisabledForGuestTooltip": "Los invitados no pueden iniciar la transmisión en vivo.", - "liveStreamingDisabledTooltip": "Inicio de transmisión en vivo deshabilitado.", + "liveStreamingDisabledTooltip": "Iniciar transmisión en vivo deshabilitado.", "lockMessage": "No se pudo bloquear la conferencia.", - "lockRoom": "", - "lockTitle": "Error de bloqueo", - "logoutQuestion": "¿Estás seguro de que deseas cerrar la sesión y detener la conferencia?", + "lockRoom": "Agregar $t(lockRoomPasswordUppercase) a la reunión", + "lockTitle": "El bloqueo falló", + "logoutQuestion": "¿Está seguro que desea salir y detener la conferencia?", "logoutTitle": "Cierre de sesión", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", - "micConstraintFailedError": "El micrófono no cumple algunas de las restricciones requeridas.", + "maxUsersLimitReached": "El límite máximo de participantes ha sido alcanzado. La conferencia está llena. Por favor contacta al organizador o intenta mas tarde.", + "maxUsersLimitReachedTitle": "Se ha alcanzado el límite máximo de participantes", + "micConstraintFailedError": "El micrófono no satisface algunos de los requerimientos.", "micNotFoundError": "No se encontró el micrófono.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", - "micPermissionDeniedError": "No has otorgado permiso para usar el micrófono. Puedes unirte a la conferencia de todos modos, pero los demás asistente no te podrán escuchar. Usa el botón de la cámara en la barra de direcciones para solucionarlo.", - "micUnknownError": "No se puede usar el micrófono por un motivo desconocido.", - "muteParticipantBody": "No podrás anular el silencio, pero él/ella podrá hacerlo en cualquier momento.", + "micNotSendingData": "Vaya a la configuración de su computadora para activar el micrófono y ajustar su nivel", + "micNotSendingDataTitle": "Su micrófono está silenciado en la configuración de su sistema", + "micPermissionDeniedError": "No ha otorgado permisos para usar su micrófono. Puede unirse a la conferencia, pero no lo podrán escuchar. Utilice el botón en la barra de dirección para solucionar esto.", + "micUnknownError": "No se puede usar su micrófono por motivos desconocidos.", + "muteEveryoneElseDialog": "Una vez silenciados, No podrás quitarles el modo en silencio, pero ellos pueden quitárselo en cualquier momento.", + "muteEveryoneElseTitle": "¿Silenciar a todos excepto a {{whom}}?", + "muteEveryoneDialog": "¿Está seguro que quiere silenciar a todos? No podrás quitarles el modo en silencio, pero ellos pueden quitárselo en cualquier momento.", + "muteEveryoneTitle": "¿Silenciar a todos?", + "muteEveryoneSelf": "Usted mismo", + "muteEveryoneStartMuted": "Todos inician silenciados desde ahora", + "muteParticipantBody": "No podrás quitarles el modo en silencio, pero ellos pueden quitárselo en cualquier momento.", "muteParticipantButton": "Silenciar", - "muteParticipantDialog": "¿Estás seguro de que deseas silenciar a este participante? No podrás anular el silencio, pero él/ella podrá hacerlo en cualquier momento.", - "muteParticipantTitle": "", + "muteParticipantDialog": "¿Seguro que quiere silenciar a este participante? No podrá revertir esta acción, pero el participante podrá hacerlo en cualquier momento", + "muteParticipantTitle": "Silenciar este participante?", "Ok": "Aceptar", - "passwordLabel": "", - "passwordNotSupported": "", - "passwordNotSupportedTitle": "", - "passwordRequired": "", - "popupError": "El navegador bloquea las ventanas emergentes de este sitio. Habilítalas en la configuración de seguridad del navegador y vuelve a intentarlo.", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "No se soporta $t(lockRoomPassword) en la reunión", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) no es compatible", + "passwordRequired": "$t(lockRoomPasswordUppercase) necesario", + "popupError": "Su navegador está bloqueando las ventanas emergentes de este sitio. Habilite las ventanas emergentes en la configuración de seguridad de su navegador y vuelva a intentarlo.", "popupErrorTitle": "Ventana emergente bloqueada", "recording": "Grabación", "recordingDisabledForGuestTooltip": "Los invitados no pueden iniciar grabaciones.", - "recordingDisabledTooltip": "Inicio de grabación deshabilitado.", - "rejoinNow": "Volver a unirse ahora", - "remoteControlAllowedMessage": "{{user}} aceptó tu solicitud de control remoto.", - "remoteControlDeniedMessage": "{{user}} rechazó tu solicitud de control remoto.", - "remoteControlErrorMessage": "Se produjo un error al intentar solicitar permisos de control remoto de {{user}}.", - "remoteControlRequestMessage": "¿Permites que {{user}} controle tu escritorio de manera remota?", - "remoteControlShareScreenWarning": "Ten en cuenta que si presionas \"Permitir\", compartirás tu pantalla.", - "remoteControlStopMessage": "La sesión de control remoto finalizó.", + "recordingDisabledTooltip": "Inicio de grabación desactivado.", + "rejoinNow": "Reunirse ahora", + "remoteControlAllowedMessage": "¡{{user}} ha aceptado tu solicitud de control remoto!", + "remoteControlDeniedMessage": "¡{{user}} ha rechazado tu solicitud de control remoto!", + "remoteControlErrorMessage": "¡Ha ocurrido un error tratando de solicitar permiso de control remoto de {{user}}!", + "remoteControlRequestMessage": "¿Permitirá que {{user}} controle remotamente su escritorio?", + "remoteControlShareScreenWarning": "¡Tenga en cuenta que si presiona \"Permitir\" usted compartirá su pantalla!", + "remoteControlStopMessage": "¡La sesión de control remoto ha finalizado!", "remoteControlTitle": "Control de escritorio remoto", "Remove": "Eliminar", - "removePassword": "", + "removePassword": "Eliminar $t(lockRoomPassword)", "removeSharedVideoMsg": "¿Estás seguro de que deseas eliminar el video compartido?", "removeSharedVideoTitle": "Eliminar video compartido", "reservationError": "Error del sistema de reservaciones", @@ -225,15 +246,19 @@ "retry": "Volver a intentar", "screenSharingFailedToInstall": "¡Uy! La extensión de uso compartido de pantalla no se pudo instalar.", "screenSharingFailedToInstallTitle": "La extensión de uso compartido de pantalla no se pudo instalar", - "screenSharingFirefoxPermissionDeniedError": "Algo salió mal cuando intentamos compartir tu pantalla. Asegúrate de habernos dado permiso para hacerlo. ", + "screenSharingFirefoxPermissionDeniedError": "Algo salió mal cuando intentamos compartir su pantalla. Asegúrese de habernos dado permiso para hacerlo.", "screenSharingFirefoxPermissionDeniedTitle": "¡Uy! No pudimos iniciar el uso compartido de la pantalla.", - "screenSharingPermissionDeniedError": "¡Uy! Algo salió mal con tus permisos de extensión de uso compartido de pantalla. Vuelve a cargar e inténtalo nuevamente.", + "screenSharingPermissionDeniedError": "¡Uy! Algo salió mal con sus permisos de extensión para compartido pantalla. Vuelva a cargar la página e intente de nuevo.", + "sendPrivateMessage": "Recientemente ha recibido un mensaje privado. ¿Pretendía responder a eso en privado, o quería enviar su mensaje al grupo?", + "sendPrivateMessageCancel": "Enviar al grupo", + "sendPrivateMessageOk": "Enviar en privado", + "sendPrivateMessageTitle": "¿Enviar en privado?", "serviceUnavailable": "Servicio no disponible", "sessTerminated": "Llamada finalizada", "Share": "Compartir", "shareVideoLinkError": "Proporciona un enlace de YouTube correcto.", "shareVideoTitle": "Compartir un video", - "shareYourScreen": "Compartir tu pantalla", + "shareYourScreen": "Compartir su pantalla", "shareYourScreenDisabled": "Uso compartido de pantalla deshabilitado.", "shareYourScreenDisabledForGuest": "Los invitados no pueden compartir la pantalla.", "startLiveStreaming": "Iniciar transmisión en vivo", @@ -250,23 +275,26 @@ "tokenAuthFailed": "Lo sentimos, no tienes permiso para unirte a esta llamada.", "tokenAuthFailedTitle": "Error de autenticación", "transcribing": "Transcripción", - "unlockRoom": "", + "unlockRoom": "Remover la $t(lockRoomPassword) de reunión", "userPassword": "contraseña de usuario", "WaitForHostMsg": "La conferencia <b>{{room}}</b> aún no ha comenzado. Si eres el anfitrión, inicia la autenticación. De lo contrario, espera a que llegue el anfitrión.", "WaitForHostMsgWOk": "La conferencia <b>{{room}}</b> aún no ha comenzado. Si eres el anfitrión, presiona Aceptar para autenticar. De lo contrario, espera a que llegue el anfitrión.", - "WaitingForHost": "Esperando al anfitrión...", + "WaitingForHost": "Esperando al anfitrión…", "Yes": "Sí", - "yourEntireScreen": "Toda tu pantalla" + "yourEntireScreen": "Toda su pantalla" }, "dialOut": { "statusMessage": "ahora está {{status}}" }, + "documentSharing": { + "title": "Documento Compartido" + }, "feedback": { "average": "Promedio", "bad": "Malo", "detailsLabel": "Cuéntanos más sobre eso.", "good": "Bueno", - "rateExperience": "Califica tu experiencia con la reunión", + "rateExperience": "Califique su experiencia en la reunión", "veryBad": "Muy malo", "veryGood": "Muy bueno" }, @@ -279,33 +307,33 @@ }, "info": { "accessibilityLabel": "Mostrar información", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Agregar la $t(lockRoomPassword)", + "cancelPassword": "Anular la $t(lockRoomPassword)", "conferenceURL": "Enlace:", "country": "País", "dialANumber": "Para unirte a la reunión, marca uno de estos números y, luego introduce el PIN.", "dialInConferenceID": "PIN:", - "dialInNotSupported": "Lo sentimos, la marcación interna actualmente no se admite.", - "dialInNumber": "Marcación interna:", - "dialInSummaryError": "Error al obtener información de marcación interna ahora. Vuelve a intentarlo más tarde.", + "dialInNotSupported": "Lo sentimos, la marcación desde afuera actualmente no se admite.", + "dialInNumber": "Marcación desde afuera:", + "dialInSummaryError": "Error al obtener información de marcación desde afuera. Vuelva a intentar más tarde.", "dialInTollFree": "Sin cargo", "genericError": "Epa, algo salió mal.", "inviteLiveStream": "Para ver la transmisión en vivo de esta reunión, haz clic en este enlace: {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", + "invitePhone": "Para unirse por teléfono, marque: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "¿Busca un número de acceso diferente?\nConsulte los números de acceso a las reuniones : {{url}}\n\n\nSi también marca a través de un teléfono externo, ingrese sin conectarse al audio: {{silentUrl}}", "inviteURLFirstPartGeneral": "Estás invitado a unirte a una reunión.", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartPersonal": "{{name}} lo invita a una reunión.\n", + "inviteURLSecondPart": "\nIngrese a la reunión :\n{{url}}\n", "liveStreamURL": "Transmisión en vivo:", "moreNumbers": "Más números", - "noNumbers": "Sin números de marcación interna.", + "noNumbers": "No hay números para marcación desde afuera.", "noPassword": "Ninguno", - "noRoom": "No se especificó ninguna sala para la marcación interna.", - "numbers": "Números de marcación interna", - "password": "", + "noRoom": "No se especificó ninguna sala para la marcación desde afuera.", + "numbers": "Números de marcación desde afuera", + "password": "$t(lockRoomPasswordUppercase):", "title": "Compartir", - "tooltip": "Compartir enlace e información de marcación interna para esta reunión", - "label": "Información de reunión" + "tooltip": "Compartir el enlace y acceso telefónico para esta reunión", + "label": "Información de la reunión" }, "inviteDialog": { "alertText": "No se pudieron invitar a algunos participantes.", @@ -322,7 +350,7 @@ "supportMsg": "Si esto sigue ocurriendo, contacta con" }, "keyboardShortcuts": { - "focusLocal": "Concentrar en tu video", + "focusLocal": "Concentrar en su video", "focusRemote": "Concentrar en el video de otra persona", "fullScreen": "Ver pantalla completa o salir de ella", "keyboardShortcuts": "Accesos directos del teclado", @@ -335,7 +363,8 @@ "toggleFilmstrip": "Mostrar u ocultar miniaturas de video", "toggleScreensharing": "Cambiar entre el uso compartido de pantalla y de cámara", "toggleShortcuts": "Mostrar u ocultar accesos directos del teclado", - "videoMute": "Iniciar o detener la cámara" + "videoMute": "Iniciar o detener la cámara", + "videoQuality": "Ajustar la calidad de la llamada" }, "liveStreaming": { "busy": "Estamos trabajando para liberar recursos de transmisión. Vuelve a intentarlo en unos minutos.", @@ -343,19 +372,21 @@ "changeSignIn": "Cambiar cuentas.", "choose": "Elegir una transmisión en vivo", "chooseCTA": "Elegir una opción de transmisión. Actualmente, la sesión está iniciada como {{email}}.", - "enterStreamKey": "Introduce tu clave de transmisión en vivo de YouTube aquí.", + "enterStreamKey": "Ingrese su clave de transmisión en vivo YouTube aquí.", "error": "Error de transmisión en vivo. Vuelve a intentarlo.", - "errorAPI": "Se produjo un error al acceder a tus difusiones de YouTube. Vuelve a intentar iniciar sesión.", + "errorAPI": "Se produjo un error al acceder a sus difusiones YouTube. Vuelva a intentar iniciar su sesión.", "errorLiveStreamNotEnabled": "La transmisión en vivo no está habilitada en {{email}}. Habilítala o inicia sesión en una cuenta con la transmisión en vivo habilitada.", "expandedOff": "La transmisión en vivo se ha detenido", "expandedOn": "La reunión se está transmitiendo actualmente a YouTube.", - "expandedPending": "La transmisión en vivo se está iniciando...", + "expandedPending": "La transmisión en vivo se está iniciando…", "failedToStart": "La transmisión en vivo no se pudo iniciar", "getStreamKeyManually": "No pudimos obtener ninguna transmisión en vivo. Intenta obtener la clave de transmisión en vivo de YouTube.", "invalidStreamKey": "Es posible que la clave de transmisión en vivo sea incorrecta.", "off": "Transmisión en vivo detenida", + "offBy": "{{name}} detuvo el streaming en directo", "on": "Transmisión en vivo", - "pending": "Iniciando transmisión en vivo...", + "onBy": "{{name}} inició el streaming en directo", + "pending": "Iniciando transmisión en vivo…", "serviceName": "Servicio de transmisión en vivo", "signedInAs": "Actualmente, la sesión está iniciada como:", "signIn": "Iniciar sesión con Google", @@ -394,8 +425,8 @@ "stop": "Detener grabación", "yes": "Sí" }, - "lockRoomPassword": "", - "lockRoomPasswordUppercase": "", + "lockRoomPassword": "clave", + "lockRoomPasswordUppercase": "Clave", "me": "yo", "notify": { "connectedOneMember": "{{name}} se unió a la reunión", @@ -405,52 +436,53 @@ "focus": "Enfoque de la conferencia", "focusFail": "{{component}} no disponible. Vuelve a intentar en {{ms}} segundos", "grantedTo": "Se otorgaron derechos de moderador a {{to}}.", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{name}} ha sido invitado", + "invitedThreePlusMembers": "{{name}} y {{count}} más han sido invitados", + "invitedTwoMembers": "{{first}} y {{second}} han sido invitados", + "kickParticipant": "{{kicker}} sacó a {{kicked}}", "me": "Yo", "moderator": "Derechos de moderador otorgados.", "muted": "Has iniciado la conversación con el silencio activado.", "mutedTitle": "Tienes el silencio activado.", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyTitle": "¡Su micrófono fue silenciado por {{participantDisplayName}}!", + "mutedRemotelyDescription": "Siempre puede reactivar sur micrófono cuando esté listo para hablar. Desactívelo de nuevo cuando termine para mantener el ruido al mínimo en la reunión.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) retirada por otro participante", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) agregada por otro participante", "raisedHand": "{{name}} desea hablar.", "somebody": "Alguien", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Bueno... Lamentamos que tu experiencia con {{appName}} no sea tan buena aquí. Estamos viendo la manera de mejorarlo, pero, hasta entonces, prueba con usar uno de los <a href='static/recommendedBrowsers.html' target='_blank'>navegadores totalmente compatibles</a>.", + "startSilentTitle": "¡Ingresó sin salida de audio!", + "startSilentDescription": "Vuelva a ingresar para activar el audio", + "suboptimalBrowserWarning": "Nos tememos que su experiencia de reunión no será tan buena aquí. Estamos buscando formas de mejorar esto, pero hasta entonces, por favor, intente utilizar uno de los <a href='static/recommendedBrowsers.html' target='_blank'>navegadores soportados</a>.", "suboptimalExperienceTitle": "Advertencia del navegador", - "unmute": "", + "unmute": "Reactivar el sonido", "newDeviceCameraTitle": "Se detectó una cámara nueva", "newDeviceAudioTitle": "Se detectó un dispositivo de audio nuevo", "newDeviceAction": "Usar" }, - "passwordSetRemotely": "", - "passwordDigitsOnly": "", + "passwordSetRemotely": "definida por otro participante", + "passwordDigitsOnly": "Hasta {{number]] cifras", "poweredby": "con tecnología de", "presenceStatus": { "busy": "Ocupado", - "calling": "Llamando...", + "calling": "Llamando…", "connected": "Conectado", - "connecting": "Conectando...", + "connecting": "Conectando…", "connecting2": "Conectando*...", "disconnected": "Desconectado", "expired": "Vencido", "ignored": "Omitido", - "initializingCall": "Inicializando llamada...", + "initializingCall": "Inicializando llamada…", "invited": "Invitado", "rejected": "Rechazado", - "ringing": "Sonando..." + "ringing": "Timbrando…" }, "profile": { - "setDisplayNameLabel": "Configurar tu nombre para mostrar", + "setDisplayNameLabel": "Configurar su nombre para mostrar", "setEmailInput": "Introducir correo electrónico", - "setEmailLabel": "Configurar tu correo electrónico de Gravatar", + "setEmailLabel": "Configurar su correo electrónico de Gravatar", "title": "Perfil" }, + "raisedHand": "Desea hablar", "recording": { "authDropboxText": "Cargar a Dropbox", "availableSpace": "Espacio disponible: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de grabación)", @@ -460,14 +492,16 @@ "error": "Error de grabación. Vuelve a intentarlo.", "expandedOff": "La grabación se ha detenido", "expandedOn": "La reunión se está grabando en este momento.", - "expandedPending": "La grabación se está iniciando...", + "expandedPending": "La grabación se está iniciando…", "failedToStart": "La grabación no se pudo iniciar", "fileSharingdescription": "Compartir grabación con participantes de la reunión", "live": "EN VIVO", "loggedIn": "Sesión iniciada como {{userName}}", "off": "Grabación detenida", + "offBy": "{{name}} paró la grabación", "on": "Grabación", - "pending": "Preparando para grabar la reunión...", + "onBy": "{{name}} inició la grabación", + "pending": "Preparando para grabar la reunión…", "rec": "REC", "serviceDescription": "El servicio de grabación guardará la grabación", "serviceName": "Servicio de grabación", @@ -498,41 +532,45 @@ "selectAudioOutput": "Salida de audio", "selectCamera": "Cámara", "selectMic": "Micrófono", - "startAudioMuted": "Todos comienzan con el silencio activado", - "startVideoMuted": "Todos comienzan ocultos", + "startAudioMuted": "Todos comienzan con silenciados", + "startVideoMuted": "Todos comienzan con cámara desactivada", "title": "Configuración" }, "settingsView": { + "advanced": "Avanzado", "alertOk": "Aceptar", "alertTitle": "Advertencia", "alertURLText": "La dirección URL de servidor introducida no es válida", "buildInfoSection": "Información de compilación", "conferenceSection": "Conferencia", + "disableCallIntegration": "Desactivar la integración nativa de llamadas", + "disableP2P": "Desactiver el modo \"Peer-To-Peer\"", "displayName": "Nombre para mostrar", "email": "Correo electrónico", "header": "Configuración", "profileSection": "Perfil", "serverURL": "Dirección URL del servidor", + "showAdvanced": "Mostrar la configuración avanzada", "startWithAudioMuted": "Iniciar con el audio en silencio", "startWithVideoMuted": "Iniciar con el video en silencio", "version": "Versión" }, "share": { - "dialInfoText": "", + "dialInfoText": "\n\n=====\n\n¿Desea llamar desde su teléfono?\n\n{{defaultDialInNumber}}La lista de números disponibles para la reunión está disponible aquí : \n{{dialInfoPageUrl}}", "mainText": "Haz clic en el enlace siguiente para unirte a la reunión:\n{{roomUrl}}" }, - "speaker": "Altavoz", + "speaker": "Participante", "speakerStats": { "hours": "{{count}} h", "minutes": "{{count}} min", "name": "Nombre", "seconds": "{{count}} s", - "speakerStats": "Estadísticas del altavoz", - "speakerTime": "Hora del altavoz" + "speakerStats": "Estadísticas de participantes", + "speakerTime": "Tiempo hablado" }, "startupoverlay": { "policyText": " ", - "title": "{{app}} tiene que usar el micrófono y la cámara." + "title": "{{app}} necesita usar el micrófono y la cámara." }, "suspendedoverlay": { "rejoinKeyTitle": "Volver a unirme", @@ -543,21 +581,26 @@ "accessibilityLabel": { "audioOnly": "Alternar solo audio", "audioRoute": "Seleccionar el dispositivo de sonido", - "callQuality": "", + "callQuality": "Ajustar la calidad vídeo", "cc": "Alternar subtítulos", "chat": "Alternar ventana de chat", "document": "Alternar documento compartido", + "download": "Descargar nuestras aplicaciones", "feedback": "Dejar comentario", "fullScreen": "Alternar pantalla completa", "hangup": "Dejar la llamada", + "help": "Ayuda", "invite": "Invitar personas", "kick": "Echar participante", "localRecording": "Alternar controles de grabación local", "lockRoom": "Alternar contraseña de reunión", "moreActions": "Alternar menú de más acciones", "moreActionsMenu": "Menú de más acciones", + "moreOptions": "Mostrar más opciones", "mute": "Alternar silenciar audio", + "muteEveryone": "Silenciar a todos", "pip": "Alternar modo de imagen en imagen", + "privateMessage": "Enviar un mensaje privado", "profile": "Editar el perfil", "raiseHand": "Alternar levantar la mano", "recording": "Alternar grabación", @@ -567,37 +610,49 @@ "shareRoom": "Invitar a alguien", "shareYourScreen": "Alternar uso compartido de pantalla", "shortcuts": "Alternar accesos directos", - "show": "", + "show": "Mostrar en primer plano", "speakerStats": "Alternar estadísticas del altavoz", "tileView": "Alternar vista de mosaico", "toggleCamera": "Alternar cámara", "videomute": "Alternar silenciar video", - "videoblur": "" + "videoblur": "Cambiar el desenfoque del fondo" }, "addPeople": "Agregar personas a la llamada", - "audioOnlyOff": "Deshabilitar modo de solo audio", - "audioOnlyOn": "Habilitar modo de solo audio", + "audioOnlyOff": "Desactivar el modo de ancho de banda bajo", + "audioOnlyOn": "Activar el modo de ancho de banda bajo", "audioRoute": "Seleccionar el dispositivo de sonido", "authenticate": "Autenticar", - "callQuality": "", + "callQuality": "Ajustar la calidad vídeo", "chat": "Abrir/cerrar chat", "closeChat": "Cerrar chat", "documentClose": "Cerrar documento compartido", "documentOpen": "Abrir documento compartido", + "download": "Descargar nuestras aplicaciones", "enterFullScreen": "Ver pantalla completa", "enterTileView": "Introducir vista de mosaico", "exitFullScreen": "Salir de pantalla completa", "exitTileView": "Salir de vista de mosaico", "feedback": "Dejar comentario", "hangup": "Dejar", + "help": "Ayuda", "invite": "Invitar personas", "login": "Inicio de sesión", "logout": "Cierre de sesión", "lowerYourHand": "Bajar la mano", "moreActions": "Más acciones", - "mute": "Silenciar/anular silencio", + "moreOptions": "Más opciones", + "mute": "Activar o silenciar el micrófono", + "muteEveryone": "Silenciar a todos", + "noAudioSignalTitle": "¡No hay ninguna entrada que provenga de su micrófono!", + "noAudioSignalDesc": "Si no lo silenció intencionalmente desde la configuración del sistema o el hardware, considere la posibilidad de cambiar el dispositivo.", + "noAudioSignalDescSuggestion": "Si no lo silenció intencionalmente desde la configuración del sistema o el hardware, considere utilizar el siguiente dispositivo:", + "noAudioSignalDialInDesc": "Usted puede además llamar usando:", + "noAudioSignalDialInLinkDesc": "Números de llamada", + "noisyAudioInputTitle": "Su micrófono parece estar ruidoso", + "noisyAudioInputDesc": "Parece que su micráfono está haciendo ruido, por favor considere silenciarlo o cambiar de dispositivo.", "openChat": "Abrir chat", "pip": "Introducir modo de imagen en imagen", + "privateMessage": "Enviar un mensaje privado", "profile": "Editar el perfil", "raiseHand": "Levantar/bajar la mano", "raiseYourHand": "Levantar la mano", @@ -605,7 +660,7 @@ "sharedvideo": "Compartir un video de YouTube", "shareRoom": "Invitar a alguien", "shortcuts": "Ver accesos directos", - "speakerStats": "Estadísticas del altavoz", + "speakerStats": "Estadísticas de participantes", "startScreenSharing": "Iniciar uso compartido de pantalla", "startSubtitles": "Iniciar subtítulos", "stopScreenSharing": "Detener uso compartido de pantalla", @@ -615,8 +670,8 @@ "tileViewToggle": "Alternar vista de mosaico", "toggleCamera": "Alternar cámara", "videomute": "Iniciar/detener cámara", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Desenfocar mi fondo", + "stopvideoblur": "Desactivar el desenfoque de fondo" }, "transcribing": { "ccButtonTooltip": "Iniciar/detener subtítulos", @@ -625,7 +680,7 @@ "failedToStart": "La transcripción no se pudo iniciar", "labelToolTip": "La reunión se está transcribiendo", "off": "Transcripción detenida", - "pending": "Preparando para transcribir la reunión...", + "pending": "Preparando para transcribir la reunión…", "start": "Comenzar a mostrar subtítulos", "stop": "Dejar de mostrar subtítulos", "tr": "TR" @@ -654,32 +709,36 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "Estás en modo de solo audio. Este modo ahorra ancho de banda, pero no podrás ver los videos de otras personas.", - "callQuality": "", + "audioOnlyExpanded": "Se encuentra en el modo de bajo ancho de banda. En este modo sólo recibirá audio y pantalla compartida.", + "callQuality": "Calidad Vídeo", "hd": "HD", + "hdTooltip": "Vista de video en alta definición", "highDefinition": "Alta definición", "labelTooiltipNoVideo": "Sin video", - "labelTooltipAudioOnly": "Modo de solo audio habilitado", + "labelTooltipAudioOnly": "Modo de bajo ancho de banda habilitado", "ld": "LD", + "ldTooltip": "Vista de video en baja definición", "lowDefinition": "Baja definición", "onlyAudioAvailable": "Modo de solo audio disponible", "onlyAudioSupported": "Solo admitimos audio en este navegador.", "p2pEnabled": "Punto a punto habilitado", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "En el modo \"peer to peer\", la calidad de vídeo recibida sólo se puede alternar entre alta y sólo audio. No se respetarán otros ajustes hasta que se salga del modo \"peer to peer\".", "recHighDefinitionOnly": "Preferiré alta definición.", "sd": "SD", + "sdTooltip": "Vista de video en definición estándar", "standardDefinition": "Definición estándar" }, "videothumbnail": { "domute": "Silenciar", + "domuteOthers": "Silenciar a todos", "flip": "Dar vuelta", "kick": "Echar", "moderator": "Moderador", - "mute": "", + "mute": "Se silenció el participante", "muted": "Silenciado", - "remoteControl": "Control remoto", - "show": "", - "videomute": "" + "remoteControl": "", + "show": "Mostrar en primer plano", + "videomute": "El participante paró su cámara" }, "welcomepage": { "accessibilityLabel": { @@ -693,20 +752,22 @@ }, "calendar": "Calendario", "connectCalendarButton": "Conectar el calendario", - "connectCalendarText": "", + "connectCalendarText": "Conecte su calendario para ver todas sus reuniones en {{app}}. Además, agregue reuniones {{provider}} a su calendario e inícielas con un solo clic.", "enterRoomTitle": "Iniciar una nueva reunión", + "roomNameAllowedChars": "El nombre de la reunión no debe contener ninguno de estos caracteres: ?, &, :, ', \", %, #.", "go": "IR", - "join": "UNIRSE", + "goSmall": "IR", + "join": "CREAR / INGRESAR", "info": "Información", "privacy": "Privacidad", "recentList": "Reciente", "recentListDelete": "Eliminar", - "recentListEmpty": "Tu lista reciente está actualmente vacía. Ten una sesión de chat con tu equipo y encontrarás todas tus reuniones recientes aquí.", - "reducedUIText": "", + "recentListEmpty": "Su lista reciente está actualmente vacía. Abra una sesión de chat con su equipo y encontrará todas sus reuniones recientes aquí.", + "reducedUIText": "¡Bienvenid@ a {{app}}!", "roomname": "Introducir nombre de sala", "roomnameHint": "Introduce el nombre o la dirección URL de la sala a la que deseas unirte. Puedes inventar un nombre, simplemente infórmaselo a las personas con las que te reunirás para que introduzcan el mismo nombre.", "sendFeedback": "Enviar comentario", "terms": "Términos", "title": "Conferencias en video seguras, con gran variedad de funciones y completamente gratuitas" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-et.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-et.json new file mode 100644 index 000000000..e3510b297 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-et.json @@ -0,0 +1,777 @@ +{ + "addPeople": { + "add": "Lisa", + "countryNotSupported": "Valitud riik ei ole toetatud.", + "countryReminder": "Veendu, et suunakood oleks lisatud.", + "disabled": "Uusi kontakte ei saa kõnesse lisada.", + "failedToAdd": "Uue kontakti kõnesse lisamine ebaõnnestus", + "footerText": "Numbri valimine on keelatud.", + "loading": "Kontaktide otsimine…", + "loadingNumber": "Telefoninumbri kontrollimine…", + "loadingPeople": "Kontaktide otsimine kõnesse lisamiseks…", + "noResults": "Vasteid ei leitud", + "noValidNumbers": "Sisesta telefoninumber", + "searchNumbers": "Lisa telefoninumber", + "searchPeople": "Kontaktide otsimine", + "searchPeopleAndNumbers": "Otsi kontakti või lisa telefoninumber", + "telephone": "Telefoninumber: {{number}}", + "title": "Lisa kontakte kõnesse" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Kõrvaklapid", + "phone": "Telefon", + "speaker": "Kõlar", + "none": "Heli vahendid pole kättesaadavad" + }, + "audioOnly": { + "audioOnly": "Ainult heli" + }, + "calendarSync": { + "addMeetingURL": "Lisa kõne link", + "confirmAddLink": "Kas soovid käesolevale kõnele lisada lingi?", + "error": { + "appConfiguration": "Ühendus kalendriga ei ole õigesti seadistatud.", + "generic": "Viga! Palun kontrolli kalendri seadistusi või värskenda kalendrit.", + "notSignedIn": "Viga kalendri autentimisel! Palun kontrolli kalendri seadistusi ja logi uuesti sisse." + }, + "join": "Liitu", + "joinTooltip": "Liitu kõnega", + "nextMeeting": "järgmine kõne", + "noEvents": "Uusi kõnesid pole planeeritud..", + "ongoingMeeting": "aktiivne kõne", + "permissionButton": "Ava seadistused", + "permissionMessage": "Planeeritud kõne nägemiseks on vajalik kalendri ligipääsuõigus.", + "refresh": "Värskenda kalendrit", + "today": "Täna" + }, + "chat": { + "error": "Viga: sõnumi \"{{originalText}}\" saatmine ebaõnnestus. Põhjus: {{error}}", + "fieldPlaceHolder": "Sisesta oma sõnum siia", + "messagebox": "Sisesta sõnum", + "messageTo": "Privaatsõnum kasutajale {{recipient}}", + "noMessagesMessage": "Kirjavahetust pole veel alustatud. Alusta kirjavahetust siin!", + "nickname": { + "popover": "Sisesta nimi", + "title": "Sisesta nimi, et kõnega alustada" + }, + "privateNotice": "Privaatsõnum kasutajale {{recipient}}", + "title": "Kõne", + "you": "you" + }, + "chromeExtensionBanner": { + "installExtensionText": "Paigalda Google Kalendri laiendus ja Office 365 integratsioon", + "buttonText": "Paigalda Chrome'i laiendus", + "dontShowAgain": "Ära rohkem näita" + }, + "connectingOverlay": { + "joiningRoom": "Kõnega ühendamine…" + }, + "connection": { + "ATTACHED": "Ühendatud", + "AUTHENTICATING": "Autentimine…", + "AUTHFAIL": "Autentimine ebaõnnestus", + "CONNECTED": "Ühendatud", + "CONNECTING": "Ühendamine…", + "CONNFAIL": "Ühendamine ebaõnnestus", + "DISCONNECTED": "Ühendus katkestatud", + "DISCONNECTING": "Ühenduse katkestamine…", + "ERROR": "Viga", + "FETCH_SESSION_ID": "Sessiooni-ID püüdmine…", + "GET_SESSION_ID_ERROR": "Sessiooni-ID püüdmisel tekkis viga: {{code}}", + "GOT_SESSION_ID": "Sessiooni-ID püüdmine… Tehtud", + "LOW_BANDWIDTH": "Kasutaja {{displayName}} video on ülekandekiiruse parandamiseks välja lülitatud" + }, + "connectionindicator": { + "address": "Aadress:", + "bandwidth": "Eeldatav ülekandekiirus:", + "bitrate": "Andmeedastuskiirus:", + "bridgeCount": "Serverite arv: ", + "connectedTo": "Ühendatud:", + "e2e_rtt": "E2E RTT:", + "framerate": "Ekraani eraldusvõime:", + "less": "Näita vähem", + "localaddress": "Lokaalne aadress:", + "localaddress_plural": "Lokaalsed aadressid:", + "localport": "Lokaalne port:", + "localport_plural": "Lokaalsed pordid:", + "more": "Näita rohkem", + "packetloss": "Andmepaketi kaotus:", + "quality": { + "good": "Hea", + "inactive": "Mitteaktiivne", + "lost": "Kaotatud", + "nonoptimal": "Mitteoptimaalne", + "poor": "Nõrk" + }, + "remoteaddress": "Kaugtöö aadress:", + "remoteaddress_plural": "Kaugtöö aadressid:", + "remoteport": "Kaugtöö port:", + "remoteport_plural": "Kaugtöö pordid:", + "resolution": "Resolutsioon:", + "status": "Ühendus:", + "transport": "Transport:", + "transport_plural": "Transpordid:" + }, + "dateUtils": { + "earlier": "Varem", + "today": "Täna", + "yesterday": "Eile" + }, + "deepLinking": { + "appNotInstalled": "Kõnega liitumiseks läbi mobiiltelefoni on vaja paigaldada {{app}} rakendus.", + "description": "Midagi ei juhtunud? Proovisime ühendust luua töölaua rakendusega. Proovi uuesti või käivita {{app}} rakendus.", + "descriptionWithoutWeb": "Midagi ei juhtunud? Televastuvõttu prooviti avada töölaua rakendusest {{app}}.", + "downloadApp": "Laadi rakendus alla", + "launchWebButton": "Käivita veebis", + "openApp": "Jätka töölaua rakendusega", + "title": "Kõne avamine rakenduses {{app}}…", + "tryAgainButton": "Proovi töölaua rakendusega uuesti " + }, + "defaultLink": "nt {{url}}", + "defaultNickname": "nt. Mari Maasikas", + "deviceError": { + "cameraError": "Ei saanud kaameraga ühendust", + "cameraPermission": "Puudub õigus kasutada kaamerat", + "microphoneError": "Ei saanud mikrofoniga ühendust", + "microphonePermission": "Puudub õigus kasutada mikrofoni" + }, + "deviceSelection": { + "noPermission": "Luba pole antud", + "previewUnavailable": "Eelvaade pole kättesaadav", + "selectADevice": "Vali seade", + "testAudio": "Mängi testimiseks heli" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Otseülekanne" + }, + "allow": "Luba", + "alreadySharedVideoMsg": "Teine kasutaja jagab videot. Selles kõnes on ainult ühe video jagamine korraga lubatud.", + "alreadySharedVideoTitle": "Korraga on lubatud ainult ühe video jagamine.", + "applicationWindow": "Rakenduse aken", + "Back": "Tagasi", + "cameraConstraintFailedError": "Kaamera ei vasta teatud piirangutele.", + "cameraNotFoundError": "Kaamerat ei leitud.", + "cameraNotSendingData": "Ei saa ühendust kaameraga. Kontrolli, kas vahend on mõne teise rakenduse poolt kasutusel, vali menüüst teine vahend või värskenda rakendust.", + "cameraNotSendingDataTitle": "Kaamera pole kättesaadav.", + "cameraPermissionDeniedError": "Kaamera kasutamine on keelatud. Kõnega on võimalik ühineda ilma kaamerata. Kaamera kasutamiseks vajuta aadressiribal kaamera nupule.", + "cameraUnknownError": "Kaamerat ei saa kasutada! Põhjus teadmata.", + "cameraUnsupportedResolutionError": "Kaamera ei toeta nõutud resolutsiooni.", + "Cancel": "Tühista", + "close": "Sulge", + "conferenceDisconnectMsg": "Kontrolli võrguühendust. Taasühendamine {{seconds}}…", + "conferenceDisconnectTitle": "Ühendus on katkenud.", + "conferenceReloadMsg": "Lahenduse otsime. Taasühendus {{seconds}} sek…", + "conferenceReloadTitle": "Midagi läks valesti!", + "confirm": "Kinnita", + "confirmNo": "Ei", + "confirmYes": "Kinnita", + "connectError": "Oih, midagi läks valesti! Kõnega ühendamine ebaõnnestus.", + "connectErrorWithMsg": "Oih, midagi läks valesti! Kõnega ühendamine ebaõnnestus. Põhjus: {{msg}}.", + "connecting": "Ühendamine.", + "contactSupport": "Võta ühendust kasutustoega", + "copy": "Kopeeri", + "dismiss": "Lõpeta", + "displayNameRequired": "Nimi on kohustuslik", + "done": "Valmis", + "enterDisplayName": "Sisesta nimi", + "error": "Viga", + "externalInstallationMsg": "Töölauale on vaja paigaldada jagamise laiendus.", + "externalInstallationTitle": "Laiendus on kohustuslik", + "goToStore": "Mine veebipoodi", + "gracefulShutdown": "Rakendus on hoolduseks ajutiselt maas. Proovi hiljem uuesti!", + "IamHost": "Mina olen võõrustaja", + "incorrectRoomLockPassword": "Vale parool", + "incorrectPassword": "Vale kasutajanimi või parool", + "inlineInstallationMsg": "Töölauale tuleb paigaldada jagamise laiendus.", + "inlineInstallExtension": "Paiglada kohe", + "internalError": "Oih, midagi läks valesti! Veateate: {{error}}.", + "internalErrorTitle": "Sisemine viga", + "kickMessage": "Oih, oled kõnest eemaldatud!", + "kickParticipantButton": "Eemalda kõnest", + "kickParticipantDialog": "Oled kindel, et tahad kasutaja kõnest eemaldada?", + "kickParticipantTitle": "Eemalda kasutaja kõnest?", + "kickTitle": "Kõnest eemaldatud", + "liveStreaming": "Otseülekanne", + "liveStreamingDisabledForGuestTooltip": "Külalised ei saa otseülekannet alustada.", + "liveStreamingDisabledTooltip": "Otseülekande alustamine on keelatud.", + "lockMessage": "Ebaõnnestunud lukustada kõnet.", + "lockRoom": "Lisa kõnele parool", + "lockTitle": "Lukustamine ebaõnnestus", + "logoutQuestion": "Oled kindel, et tahad kõne peatada ja välja logida?", + "logoutTitle": "Logi välja", + "maxUsersLimitReached": "Maksimaalne kõnes osalejate arv on täis. Võta ühendust kõne omanikuga või proovi hiljem uuesti!", + "maxUsersLimitReachedTitle": "Maksimaalne kõnes osalejate arv on täis", + "micConstraintFailedError": "Mikrofon ei vasta teatud piirangutele.", + "micNotFoundError": "Mikrofoni ei leitud.", + "micNotSendingData": "Ei saa ühendust mikrofoniga. Vali menüüst teine vahend või värskenda rakendust.", + "micNotSendingDataTitle": "Mikrofon pole kättesaadav.", + "micPermissionDeniedError": "Mikrofoni kasutamine on keelatud. Kõnega on võimalik ühineda, aga teised Sind ei kuule. Mikrofoni kasutamiseks vajuta aadressiribal mikrofoni nupule.", + "micUnknownError": "Mikrofoni ei saa kasutada. Põhjus teadmata.", + "muteEveryoneElseDialog": "Peale mikrofoni vaigistamist saab ainult kasutaja ise oma mikrofoni tagasi sisse lülitada.", + "muteEveryoneElseTitle": "Vaigista kõikide teiste mikrofonid, välja arvatud {{whom}}?", + "muteEveryoneDialog": "Oled kindel, et soovid kõikide teiste mikrofonid vaigistada? Neid saab tagasi sisse lülitada ainult kasutaja ise.", + "muteEveryoneTitle": "Vaigista kõik?", + "muteEveryoneSelf": "Sina ise", + "muteEveryoneStartMuted": "Edaspidi alustavad kõik välja lülitatud mikrofonidega", + "muteParticipantBody": "Teiste kasutajate mikrofoni ei saa sisse lülitada. Kasutaja saab ise otsutada, kas mikrofon on sees või väljas.", + "muteParticipantButton": "Lülita mikrofon välja", + "muteParticipantDialog": "Oled kindel, et soovid kasutaja mikrofoni välja lülitada? Seda saab ainult kasutaja ise sisse tagasi lülitada.", + "muteParticipantTitle": "Lülita kasutaja mikrofon välja?", + "Ok": "Jah", + "passwordLabel": "Parool", + "passwordNotSupported": "Kõnele ei saa parooli määrata.", + "passwordNotSupportedTitle": "Parooli lisamine pole toetatud", + "passwordRequired": "Parool on kohustuslik", + "popupError": "Modaalaknad on veebilehitsejas keelatud. Palun luba modaalakende kasutamine veebilehitseja turvalisuse seadistuses ning proovi uuesti.", + "popupErrorTitle": "Modaalaknad on keelatud", + "recording": "Salvetamine", + "recordingDisabledForGuestTooltip": "Külalised ei saa kõne salvestada.", + "recordingDisabledTooltip": "Kõne salvestamine on keelatud.", + "rejoinNow": "Ühine uuesti", + "remoteControlAllowedMessage": "{{user}} andis kaugjuhtimiseks loa!", + "remoteControlDeniedMessage": "{{user}} keelas kaugjuhtimise!", + "remoteControlErrorMessage": "Viga kaugjuhtimiseks loa küsimisel kasutajalt {{user}}!", + "remoteControlRequestMessage": "Kas lubad kasutajal {{user}} oma töölauda kaugjuhtida?", + "remoteControlShareScreenWarning": "Kui vajutad nupule \"Luba\", siis jagad oma ekraani.", + "remoteControlStopMessage": "Kaugjuhtimise sessioon on lõppenud!", + "remoteControlTitle": "Kaugjuhtimine", + "Remove": "Eemalda", + "removePassword": "Eemalda parool", + "removeSharedVideoMsg": "Oled kindel, et soovid oma jagatud video eemaldada?", + "removeSharedVideoTitle": "Eemalda jagatud video", + "reservationError": "Broneerimise süsteemi viga", + "reservationErrorMsg": "Veakood: {{code}}, sõnum: {{msg}}", + "retry": "Proovi uuesti", + "screenSharingFailedToInstall": "Oih, ekraanijagamise laienduse paigaldamine ebaõnnestus!", + "screenSharingFailedToInstallTitle": "Ekraanijagamise laienduse paigaldamine ebaõnnestus", + "screenSharingFirefoxPermissionDeniedError": "Ekraani jagamisega läks midagi valesti! Veendu, et oled ekraani jagamiseks loa andnud.", + "screenSharingFirefoxPermissionDeniedTitle": "Oih, ekraani jagamist ei saanud alustada!", + "screenSharingPermissionDeniedError": "Oih, midagi läks valesti ekraanijagamise laienduse õigustega! Värskenda ja proovi uuesti.", + "sendPrivateMessage": "Sulle saabus privaatsõnum. Kas soovid vastata privaatselt või avalikult?", + "sendPrivateMessageCancel": "Saada sõnum avalikult", + "sendPrivateMessageOk": "Saada sõnum privaatselt", + "sendPrivateMessageTitle": "Saada privaatselt?", + "serviceUnavailable": "Teenus pole kättesaadav", + "sessTerminated": "Kõne lõpetatud", + "Share": "Jaga", + "shareVideoLinkError": "Sisesta korrektne Youtube’i link.", + "shareVideoTitle": "Jaga videot", + "shareYourScreen": "Jaga ekraani", + "shareYourScreenDisabled": "Ekraani jagamine on keelatud.", + "shareYourScreenDisabledForGuest": "Külalised ei saa ekraani jagada.", + "startLiveStreaming": "Alusta otseülekannet", + "startRecording": "Alusta salvestamist", + "startRemoteControlErrorMessage": "Kaugjuhtimise sessiooni alustamisel tekkis viga!", + "stopLiveStreaming": "Lõpeta otseülekanne", + "stopRecording": "Lõpeta salvestamine", + "stopRecordingWarning": "Oled kindel, et soovid salvestamist lõpetada?", + "stopStreamingWarning": "Oled kindel, et soovid otseülekannet lõpetada?", + "streamKey": "Otseülekande võti", + "Submit": "Esita", + "thankYou": "Aitäh, et kasutasid rakendust {{appName}}!", + "token": "token", + "tokenAuthFailed": "Kahjuks ei ole kõnega ühinemine lubatud.", + "tokenAuthFailedTitle": "Autentimine ebaõnnestus", + "transcribing": "Transkribeerimine", + "unlockRoom": "Eemalda kõne parool", + "userPassword": "kasutaja parool", + "WaitForHostMsg": "Kõne <b>{{room}}</b> ei ole veel alanud. Autendi ennast, kui oled võõrustaja. Külalisena oota, kuni võõrustaja saabub.", + "WaitForHostMsgWOk": "Kõne <b>{{room}}</b> ei ole veel alanud. Kui oled võõrustaja, vajuta OK, et ennast autentida. Külalisena oota, kuni võõrustaja saabub.", + "WaitingForHost": "Võõrustaja ootamine…", + "Yes": "Jah", + "yourEntireScreen": "Täisekraan" + }, + "dialOut": { + "statusMessage": "on staatusega {{status}}" + }, + "documentSharing": { + "title": "Jagatud dokument" + }, + "feedback": { + "average": "Keskmine", + "bad": "Halb", + "detailsLabel": "Kirjelda täpsemalt.", + "good": "Hea", + "rateExperience": "Hinda kõne kvaliteeti", + "veryBad": "Väga halb", + "veryGood": "Väga hea" + }, + "incomingCall": { + "answer": "Vasta", + "audioCallTitle": "Sissetulev kõne", + "decline": "Lõpeta", + "productLabel": "Jitsi", + "videoCallTitle": "Sissetulev videokõne" + }, + "info": { + "accessibilityLabel": "Näita infot", + "addPassword": "Lisa parool", + "cancelPassword": "Tühista parool", + "conferenceURL": "Link:", + "country": "Riik", + "dialANumber": "Kõnega ühinemiseks vali number ja sisesta pin-kood.", + "dialInConferenceID": "PIN:", + "dialInNotSupported": "Oih, sissehelistamine ei ole toetatud!", + "dialInNumber": "Sissehelistamine:", + "dialInSummaryError": "Sissehelistamise info pole kättesaadav. Proovi hiljem uuesti!", + "dialInTollFree": "Maksuvaba", + "genericError": "Oih, midagi läks valesti!", + "inviteLiveStream": "Otseülekande nägemiseks vajuta lingile: {{url}}", + "invitePhone": "Üks klikk heliga sissehelistamiseks: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Otsid teist sissehelistamise numbrit?\nVaata sissehelistamise numbreid: {{url}}\n\n\nKui helistad läbi ruumi, ühine kõnega väljalülitatud mikrofoni režiimis: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Oled kõnesse kutsutud.", + "inviteURLFirstPartPersonal": "{{name}} kutsub Sind kõnesse.\n", + "inviteURLSecondPart": "\nÜhine kõnega:\n{{url}}\n", + "liveStreamURL": "Otseülekanne:", + "moreNumbers": "Rohkem numbreid", + "noNumbers": "Sissehelistamise numbrid puuduvad.", + "noPassword": "Andmed puuduvad", + "noRoom": "Sissehelistamise ruum pole täpsustatud.", + "numbers": "Sissehelistamise numbrid", + "password": "Parool:", + "title": "Jaga", + "tooltip": "Jaga linki ja helista, et kõnega ühineda", + "label": "Kõne info" + }, + "inviteDialog": { + "alertText": "Osade kasutajate kutsumine ebaõnnestus.", + "header": "Kutsu", + "searchCallOnlyPlaceholder": "Sisesta telefoninumber", + "searchPeopleOnlyPlaceholder": "Otsi kasutajaid", + "searchPlaceholder": "Kasutaja telefoninumber", + "send": "Saada" + }, + "inlineDialogFailure": { + "msg": "Midagi läks valesti.", + "retry": "Proovi uuesti", + "support": "Kasutajatugi", + "supportMsg": "Kui see juhtub uuesti, võta ühendust" + }, + "keyboardShortcuts": { + "focusLocal": "Keskendu videole", + "focusRemote": "Keskendu teise kasutaja videole", + "fullScreen": "Ava/sulge täisekraani vaade", + "keyboardShortcuts": "Klaviatuuri kiirvalikud", + "localRecording": "Näita/peida salvestamise võimalused", + "mute": "Lülita mikrofon sisse/välja", + "pushToTalk": "Vajuta, et rääkida", + "raiseHand": "Tõsta/langeta kätt", + "showSpeakerStats": "Näita kõnelejate statistikat", + "toggleChat": "Ava/sulge vestluse aken", + "toggleFilmstrip": "Näita/peida video eelvaade", + "toggleScreensharing": "Vaheta kaamera ja ekraanijagamise vahel", + "toggleShortcuts": "Näita/peida klaviatuuri kiirvalikud", + "videoMute": "Lülita kaamera sisse/välja", + "videoQuality": "Halda kõne kvaliteeti" + }, + "liveStreaming": { + "busy": "Toimub ülekande ressursi vabastamine. Proovi mõne minuti pärast uuesti.", + "busyTitle": "Kõik ülekandjad on hetkel hõivatud", + "changeSignIn": "Vaheta kontot.", + "choose": "Vali otseülekanne", + "chooseCTA": "Vali ülekande viis. Oled sisse logitud e-mailiga {{email}}.", + "enterStreamKey": "Sisesta siia oma YouTube’i ülekande võti.", + "error": "Otseülekanne ebaõnnestus. Proovi uuesti.", + "errorAPI": "YouTube’i kanaliga ühendumisel tekkis viga. Palun logi uuesti sisse.", + "errorLiveStreamNotEnabled": "Otseülekanne ei ole e-mailiga {{email}} sisse lülitatud. Luba kasutajaga otseülekanded või vaheta kontot.", + "expandedOff": "Otseülekanne on peatatud", + "expandedOn": "Kõnest tehakse otseülekanne YouTube’i.", + "expandedPending": "Otseülekanne algab…", + "failedToStart": "Otseülekandega alustamine ebaõnnestus.", + "getStreamKeyManually": "Ülekandjaid ei leitud. Leia YouTube’st otseülekande võti.", + "invalidStreamKey": "Otseülekande võti võib olla vale.", + "off": "Otseülekanne on peatatud", + "offBy": "{{name}} lõpetas otseülekande", + "on": "Otseülekanne", + "onBy": "{{name}} alustas otseülekandega", + "pending": "Otseülekande alustamine…", + "serviceName": "Otseülekande teenus", + "signedInAs": "Oled sisse logitud kasutajana:", + "signIn": "Google’ga sisselogimine", + "signInCTA": "Logi sisse või sisesta otseülekande võti YouTube’st.", + "signOut": "Logi välja", + "start": "Alusta otseülekannet.", + "streamIdHelp": "Mis see on?", + "unavailableTitle": "Otseülekanne pole kättesaadav." + }, + "localRecording": { + "clientState": { + "off": "Väljas", + "on": "Sees", + "unknown": "Teadmata" + }, + "dialogTitle": "Kohalikud salvestamise nupud", + "duration": "Kestvus", + "durationNA": "N/A", + "encoding": "Kodeerimine", + "label": "LOR", + "labelToolTip": "Kohalik salvestamine on alustatud", + "localRecording": "Kohalik salvestamine", + "me": "Mina", + "messages": { + "engaged": "Local recording engaged.", + "finished": "Salvestamise sessioon {{token}} on lõppenud. Palun saada salvestatud fail moderaatorile.", + "finishedModerator": "Salvestamise sessioon {{token}} on lõppenud ja salvestatud. Küsi teistelt kontaktidelt nende koopiaid.", + "notModerator": "Sa ei ole moderaator. Sa ei saa alustada ega lõpetada kohalikku salvestamist." + }, + "moderator": "Moderaator", + "no": "Ei", + "participant": "Osaleja", + "participantStats": "Osaleja andmed", + "sessionToken": "Sessiooni token", + "start": "Alusta salvestamist", + "stop": "Lõpeta salvestamine", + "yes": "Jah" + }, + "lockRoomPassword": "parool", + "lockRoomPasswordUppercase": "Parool", + "me": "mina", + "notify": { + "connectedOneMember": "{{name}} ühines kõnega", + "connectedThreePlusMembers": "{{name}} ja {{count}} teist kasutajat ühines kõnega", + "connectedTwoMembers": "{{first}} ja {{second}} ühinesid kõnega", + "disconnected": "lahti ühendatud", + "focus": "Konverentsi fookus", + "focusFail": "{{component}} pole kättesaadav - proovi uuesti {{ms}} sekundi pärast.", + "grantedTo": "Moderaatori õigused on antud kasutajale {{to}}!", + "invitedOneMember": "{{name}} on kutsutud", + "invitedThreePlusMembers": "{{name}} ja {{count}} teist kasutajat on kutsutud", + "invitedTwoMembers": "{{first}} ja {{second}} on kutsutud", + "kickParticipant": "{{kicked}} eemaldati kõnest kasutaja {{kicker}} poolt", + "me": "Mina", + "moderator": "Moderaatori õigused jagatud!", + "muted": "Alustasid kõnet väljalülitatud mikrofoniga.", + "mutedTitle": "Mikrofon on välja lülitatud!", + "mutedRemotelyTitle": "Sinu mikrofon lülitati välja kasutaja {{participantDisplayName}} poolt!", + "mutedRemotelyDescription": "Saad alati oma mikrofoni sisse lülitada, kui soovid rääkida. Lülita mikrofon peale rääkimist uuesti välja liigse müra ja kaja vältimiseks.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) eemaldatud teise kasutaja poolt", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) lisatud teise kasutaja poolt", + "raisedHand": "{{name}} soovib rääkida.", + "somebody": "Keegi", + "startSilentTitle": "Ühinesid ilma mikrofonita!", + "startSilentDescription": "Mikrofoni kasutamiseks ühine kõnega uuesti", + "suboptimalExperienceDescription": "Rakenduse {{appName}} parima kvaliteedi tagamiseks palun kasuta <a href='static/recommendedBrowsers.html' target='_blank'>ühte nendest veebilehitsejatest</a>.", + "suboptimalExperienceTitle": "Veebilehitseja hoiatus", + "unmute": "Lülita mikrofon sisse", + "newDeviceCameraTitle": "Leitud uus kaamera", + "newDeviceAudioTitle": "Leitud uus heliseadeldis", + "newDeviceAction": "Kasuta" + }, + "passwordSetRemotely": "määratud teise kasutaja poolt", + "passwordDigitsOnly": "Kuni {{number}} tähemärki", + "poweredby": "teieni toodud", + "presenceStatus": { + "busy": "Hõivatud", + "calling": "Helistamine…", + "connected": "Ühendatud", + "connecting": "Ühendamine…", + "connecting2": "Ühendamine*...", + "disconnected": "Lahti ühendatud", + "expired": "Aegunud", + "ignored": "Eiratud", + "initializingCall": "Kõne alustamine…", + "invited": "Kutsutud", + "rejected": "Tagasi lükatud", + "ringing": "Kutsumine…" + }, + "profile": { + "setDisplayNameLabel": "Sisesta nimi", + "setEmailInput": "Sisesta e-mail", + "setEmailLabel": "Sisesta Gravatar e-kirja aadress", + "title": "Profiil" + }, + "raisedHand": "Soovin rääkida", + "recording": { + "authDropboxText": "Lisa Dropbox’i", + "availableSpace": "Vaba ruum: {{spaceLeft}} MB (ca {{duration}} minutit salvestamist)", + "beta": "BETA", + "busy": "Salvestamise ressursi vabastamine… Proovi mõne minuti pärast uuesti.", + "busyTitle": "Kõik salvestajad on praegu kinni", + "error": "Salvestamine ebaõnnestus. Proovi uuesti.", + "expandedOff": "Salvestamine peatatud", + "expandedOn": "Kõne salvestatakse.", + "expandedPending": "Salvestamine on alustatud…", + "failedToStart": "Salvestamine ebaõnnestus", + "fileSharingdescription": "Jaga salvestust kõnelejatega", + "live": "Otse", + "loggedIn": "Sisseloginud kasutajana {{userName}}", + "off": "Salvestamine on lõpetatud", + "offBy": "{{name}} lõpetas salvestamise", + "on": "Salvestatakse", + "onBy": "{{name}} alustas salvestamist", + "pending": "Kõne salvestamise ettevalmistus…", + "rec": "REC", + "serviceDescription": "Salvestus toimub teenuse kaudu", + "serviceName": "Salvestamise teenus", + "signIn": "Logi sisse", + "signOut": "Logi välja", + "unavailable": "Oih! {{serviceName}} ei ole hetkel kättesaadav! Proovi hiljem uuesti.", + "unavailableTitle": "Salvestamine ei ole võimalik." + }, + "sectionList": { + "pullToRefresh": "Tõmba uuendamiseks" + }, + "settings": { + "calendar": { + "about": "Rakenduse {{appName}} kalender kasutab turvalist ühendust ning näeb eesolevaid kõnesid.", + "disconnect": "Ühenda lahti", + "microsoftSignIn": "Logi sisse Microsoft’i kontoga", + "signedIn": "Hetkel nähakse kasutaja {{email}} kalendrit. Kalendrikutsete peitmiseks vajuta “Ühenda lahti” nupule.", + "title": "Kalender" + }, + "devices": "Seaded", + "followMe": "Kõik jälgivad mind", + "language": "Keel", + "loggedIn": "Logitud sisse nimega: {{name}}", + "moderator": "Moderaator", + "more": "Rohkem", + "name": "Nimi", + "noDevice": "Andmed puuduvad", + "selectAudioOutput": "Heli väljund", + "selectCamera": "Kaamera", + "selectMic": "Mikrofon", + "startAudioMuted": "Kõik alustavad väljalülitatud mikrofoniga", + "startVideoMuted": "Kõik alustavad väljalülitatud kaameraga", + "title": "Seaded" + }, + "settingsView": { + "advanced": "Täpsem", + "alertOk": "OK", + "alertTitle": "Hoiatus", + "alertURLText": "Sisestatud link ei ole õige", + "buildInfoSection": "Versioon", + "conferenceSection": "Kõne", + "disableCallIntegration": "Lülita kohaliku kõne integratsioon välja", + "disableP2P": "Lülita otseühendus välja", + "displayName": "Kasutatav nimi", + "email": "E-mail", + "header": "Seaded", + "profileSection": "Profiil", + "serverURL": "Serveri link", + "showAdvanced": "Näita täpsemaid seadistusi", + "startWithAudioMuted": "Alusta väljalülitatud heliga", + "startWithVideoMuted": "Alusta väljalülitatud videoga", + "version": "Versioon" + }, + "share": { + "dialInfoText": "\n\n=====\n\nSoovid sisse helistada oma telefonilt?\n\n{{defaultDialInNumber}}Vajuta lingile, et näha telefoninumbreid sisse helistamiseks\n{{dialInfoPageUrl}}", + "mainText": "Vajuta lingile, et kõnega ühineda:\n{{roomUrl}}" + }, + "speaker": "Kõneleja", + "speakerStats": { + "hours": "{{count}}t", + "minutes": "{{count}}m", + "name": "Nimi", + "seconds": "{{count}}s", + "speakerStats": "Kõneleja andmed", + "speakerTime": "Kõnelemise aeg" + }, + "startupoverlay": { + "policyText": " ", + "title": "{{app}} vajab ligipääsu mikrofonile ja kaamerale." + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Ühine uuesti", + "text": "Vajuta <i>Ühine uuesti</i> nupule, et uuesti ühineda.", + "title": "Kõne katkestati, sest arvuti läks magama." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Kasuta ainult heli", + "audioRoute": "Vali heli vahend", + "callQuality": "Halda kõne kvaliteeti", + "cc": "Kasuta subtiitreid", + "chat": "Kasuta vestluse akent", + "document": "Kasuta jagatud dokumente", + "download": "Laadi rakendus alla", + "feedback": "Jäta tagasiside", + "fullScreen": "Kasuta täisekraani", + "hangup": "Lahku kõnest", + "help": "Abi", + "invite": "Kutsu inimesi", + "kick": "Eemalda kõneleja", + "localRecording": "Näita salvestamise paneeli", + "lockRoom": "Kasuta kõne parooli", + "moreActions": "Kasuta toimingute menüüd", + "moreActionsMenu": "Toimingute menüü", + "moreOptions": "Näita rohkem valikuid", + "mute": "Lülita heli välja", + "muteEveryone": "Vaigista kõikide mikrofonid", + "pip": "Kasuta pilt-pildis vaadet", + "privateMessage": "Saada privaatsõnum", + "profile": "Muuda profiili", + "raiseHand": "Käe tõstmine", + "recording": "Salvestamine", + "remoteMute": "Lülita kasutaja mikrofon välja", + "Settings": "Seadistused", + "sharedvideo": "Kasuta YouTube’i video jagamist", + "shareRoom": "Kutsu", + "shareYourScreen": "Jaga ekraani", + "shortcuts": "Kasuta kiirvalikuid", + "show": "Näita laval", + "speakerStats": "Kõnelejate statistika", + "tileView": "Paneelvaade", + "toggleCamera": "Kasuta kaamerat", + "videomute": "Video väljalülitamine", + "videoblur": "Video hägustamine" + }, + "addPeople": "Lisa kõnesse inimesi", + "audioOnlyOff": "Lülita “ainult heli” valik välja", + "audioOnlyOn": "Lülita “ainult heli” valik sisse", + "audioRoute": "Vali heli vahend", + "authenticate": "Autendi", + "callQuality": "Halda kõne kvaliteeti", + "chat": "Ava/sulge vestlus", + "closeChat": "Sulge vestlus", + "documentClose": "Sulge jagatud dokument", + "documentOpen": "Ava jagatud dokument", + "download": "Laadi rakendus alla", + "enterFullScreen": "Vaata täisekraanil", + "enterTileView": "Vaata paneelvaates", + "exitFullScreen": "Välju täisekraani vaatest", + "exitTileView": "Välju paneelvaatest", + "feedback": "Jäta tagasiside", + "hangup": "Lahku", + "help": "Abi", + "invite": "Kutsu", + "login": "Logi sisse", + "logout": "Logi välja", + "lowerYourHand": "Langeta kätt", + "moreActions": "Rohkem tegevusi", + "moreOptions": "Rohkem valikuid", + "mute": "Mikrofon sisse/välja", + "muteEveryone": "Vaigista kõikide mikrofonid", + "noAudioSignalTitle": "Mikrofon ei püüa sisendit kinni!", + "noAudioSignalDesc": "Kui Sa ei lülitanud mikrofoni seadistustest tahtlikult välja, kaalu seadme vahetamist.", + "noAudioSignalDescSuggestion": "Kui Sa ei lülitanud mikrofoni seadistustest tahtlikult välja, kaalu seadme vahetamist.", + "noAudioSignalDialInDesc": "Võid sisse helistada valides:", + "noAudioSignalDialInLinkDesc": "Sissehelistamise numbrid", + "noisyAudioInputTitle": "Mikrofonis on müra! Tundub, et läbi mikrofoni kostub palju helisid. Kaalu mikrofoni välja lülitamist või seadme vahetamist.", + "noisyAudioInputDesc": "Tundub, et läbi mikrofoni kostub palju helisid. Kaalu mikrofoni välja lülitamist või seadme vahetamist.", + "openChat": "Ava vestlus", + "pip": "Ava pilt-pildis vaade", + "privateMessage": "Saada privaatsõnum", + "profile": "Muuda profiili", + "raiseHand": "Tõsta/langeta kätt", + "raiseYourHand": "Tõsta kätt", + "Settings": "Seaded", + "sharedvideo": "Jaga YouTube’i videot", + "shareRoom": "Kutsu", + "shortcuts": "Vaata kiirvalikuid", + "speakerStats": "Kõneleja andmed", + "startScreenSharing": "Alust ekraani jagamist", + "startSubtitles": "Alusta subtiitrite näitamist", + "stopScreenSharing": "Lõpeta ekraani jagamine", + "stopSubtitles": "Lõpeta subtiitrite näitamine", + "stopSharedVideo": "Lõpeta YouTube’i video", + "talkWhileMutedPopup": "Soovid rääkida? Mikrofon on välja lülitatud.", + "tileViewToggle": "Näita paneelvaadet", + "toggleCamera": "Kasuta kaamerat", + "videomute": "Kaamera sisse/välja", + "startvideoblur": "Tausta hägustamine", + "stopvideoblur": "Lülita tausta hägustamine välja" + }, + "transcribing": { + "ccButtonTooltip": "Subtiitrid sisse/välja", + "error": "Transkribeerimine ebaõnnestus. Proovi uuesti.", + "expandedLabel": "Transkribeerimine on sisse lülitatud", + "failedToStart": "Transkribeerimise alustamine ebaõnnestus", + "labelToolTip": "Kõne transkribeeritakse", + "off": "Transkribeerimine peatatud", + "pending": "Transkribeerimise ettevalmistus…", + "start": "Alusta subtiitrite kuvamist", + "stop": "Lõpeta subtiitrite kuvamine", + "tr": "TR" + }, + "userMedia": { + "androidGrantPermissions": "Vali <b><i>Luba</i></b>, kui veebilehitseja küsib nõusolekut.", + "chromeGrantPermissions": "Vali <b><i>Luba</i></b>, kui veebilehitseja küsib nõusolekut.", + "edgeGrantPermissions": "Vali <b><i>Jah</i></b>, kui veebilehitseja küsib nõusolekut.", + "electronGrantPermissions": "Luba kasutada kaamerat ja mikrofoni", + "firefoxGrantPermissions": "Vali <b><i>Jaga valitud vahendit</i></b>, kui veebilehitseja küsib nõusolekut.", + "iexplorerGrantPermissions": "Vali <b><i>OK</i></b>, kui veebilehitseja küsib nõusolekut.", + "nwjsGrantPermissions": "Luba kasutada kaamerat ja mikrofoni", + "operaGrantPermissions": "Vali <b><i>Luba</i></b>, kui veebilehitseja küsib nõusolekut.", + "react-nativeGrantPermissions": "Vali <b><i>Luba</i></b>, kui veebilehitseja küsib nõusolekut.", + "safariGrantPermissions": "Vali <b><i>OK</i></b>, kui veebilehitseja küsib nõusolekut." + }, + "videoSIPGW": { + "busy": "Vabastatakse ressurssi… Proovi mõne minuti pärast uuesti.", + "busyTitle": "Ruumi teenus on hetkel hõivatud", + "errorAlreadyInvited": "{{displayName}} on juba kutsutud", + "errorInvite": "Ühendus ei ole veel saavutatud. Proovi hiljem uuesti.", + "errorInviteFailed": "Probleemi lahendatakse. Proovi hiljem uuesti.", + "errorInviteFailedTitle": "Kasutaja {{displayName}} kutsumine ebaõnnestus", + "errorInviteTitle": "Ruumi kutsumine ebaõnnestus", + "pending": "{{displayName}} on kutsutud" + }, + "videoStatus": { + "audioOnly": "AUD", + "audioOnlyExpanded": "Kasutad kõnes ainult heli. See hõivab ülekandekiirust vähem, aga video jagamine on välja lülitatud.", + "callQuality": "Kõne kvaliteet", + "hd": "HD", + "hdTooltip": "Video vaatamine kõrgkvaliteediga", + "highDefinition": "Kõrgresolutsioon", + "labelTooiltipNoVideo": "Video puudub", + "labelTooltipAudioOnly": "Valitud on “ainult heli” seadistus", + "ld": "LD", + "ldTooltip": "Video vaatamine madala kvaliteediga", + "lowDefinition": "Madal", + "onlyAudioAvailable": "Saab kasutada ainult heli", + "onlyAudioSupported": "Selles veebilehitsejas on toetatud ainult heli.", + "p2pEnabled": "Otseühendus on sisse lülitatud", + "p2pVideoQualityDescription": "Otseühenduse režiimis saab vastuvõetava kõne heli olla “ainult heli“, või kõrge. Teisi seadistusi ei saa valida.", + "recHighDefinitionOnly": "Eelistan kõrgresolutsiooni.", + "sd": "SD", + "sdTooltip": "Video vaatamine vaikekvaliteediga", + "standardDefinition": "Vaike resolutsioon" + }, + "videothumbnail": { + "domute": "Lülita mikrofon välja", + "domuteOthers": "Vaigista teiste mikrofonid", + "flip": "Pööra", + "kick": "Eemalda kõnest", + "moderator": "Moderaator", + "mute": "Kasutaja mikrofon välja lülitatud", + "muted": "Mikrofon välja lülitatud", + "remoteControl": "Kaugjuhtimine", + "show": "Näita laval", + "videomute": "Kasutaja on kaamera peatanud" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Vajuta, et ühineda", + "roomname": "Sisesta ruumi nimi" + }, + "appDescription": "Lase käia, tee videoülekanne kogu meeskonnaga! Kutsu kõik, keda soovid. Rakendus {{app}} on krüpteeritud. 100% vabavara ülekannete tegemiseks, mida saab kasutada iga päev tasuta - ilma konto loomiseta.", + "audioVideoSwitch": { + "audio": "Heli", + "video": "Video" + }, + "calendar": "Kalender", + "connectCalendarButton": "Ühenda kalender", + "connectCalendarText": "Ühenda oma kalender, et kõiki kohtumisi näha rakenduses {{app}}. Lisa {{provider}} kohtumised kalendrisse ja alusta kõnesid ühe klikiga.", + "enterRoomTitle": "Alusta uut kõnet", + "roomNameAllowedChars": "Televastuvõtu nimi ei tohi sisaldada sümboleid: ?, &, :, ', \", %, #.", + "go": "Mine", + "goSmall": "Mine", + "join": "Ühine", + "info": "Info", + "privacy": "Privaatsus", + "recentList": "Hiljutised", + "recentListDelete": "Kustuta", + "recentListEmpty": "Hiljutiste valikute nimekiri on tühi. Vestle kasutajatega ja leia oma hiljutised kõned siit.", + "reducedUIText": "Tere tulemast rakendusse {{app}}!", + "roomname": "Sisesta ruumi nimi", + "roomnameHint": "Sisesta ruumi nimi või link, millega soovid ühinega. Võid nime välja mõelda, aga anna osalejatele sellest teada, et nad saaksid ruumiga ühineda.", + "sendFeedback": "Saada tagasiside", + "terms": "Tingimused", + "title": "Turvaline, võimalusi täis ja tasuta keskkond videokõnede jaoks." + }, + "lonelyMeetingExperience": { + "button": "Kutsu teisi", + "youAreAlone": "Asud hetkel ruumis üksi" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fi.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fi.json index 7ee0aed68..8d2922ab7 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fi.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fi.json @@ -54,7 +54,7 @@ "title": "Chatti" }, "connectingOverlay": { - "joiningRoom": "Yhdistetään kokoukseen..." + "joiningRoom": "Yhdistetään kokoukseen…" }, "connection": { "ATTACHED": "Liitteenä", @@ -111,7 +111,7 @@ "downloadApp": "Lataa sovellus", "launchWebButton": "Käynnistä verkossa", "openApp": "Jatka sovellukseen", - "title": "Käynnistetään kokousta sovelluksessa {{app}}...", + "title": "Käynnistetään kokousta sovelluksessa {{app}}…", "tryAgainButton": "Yritä uudelleen työpöytäsovelluksella" }, "defaultLink": "esim. {{url}}", @@ -145,9 +145,9 @@ "cameraUnsupportedResolutionError": "Kamerasi ei tue vaadittua videoresoluutiota.", "Cancel": "Peruuta", "close": "Sulje", - "conferenceDisconnectMsg": "Tarkista verkkoyhteys. Yhdistetään uudelleen {{seconds}} sekunnin kuluttua...", + "conferenceDisconnectMsg": "Tarkista verkkoyhteys. Yhdistetään uudelleen {{seconds}} sekunnin kuluttua…", "conferenceDisconnectTitle": "Yhteys on katkennut.", - "conferenceReloadMsg": "Yritämme korjata tilannetta. Yhdistetään uudelleen {{seconds}} sekunnin kuluttua...", + "conferenceReloadMsg": "Yritämme korjata tilannetta. Yhdistetään uudelleen {{seconds}} sekunnin kuluttua…", "conferenceReloadTitle": "Valitettavasti jokin meni vikaan.", "confirm": "Vahvista", "confirmNo": "Ei", @@ -254,7 +254,7 @@ "userPassword": "käyttäjän salasana", "WaitForHostMsg": "Kokous <b>{{room}}</b> ei ole vielä alkanut. Jos olet vetäjä, todenna henkilöllisyytesi. Muussa tapauksessa odota vetäjän saapumista.", "WaitForHostMsgWOk": "Kokous <b>{{room}}</b> ei ole vielä alkanut. Jos olet vetäjä, todenna henkilöllisyytesi OK-painikkeella. Muussa tapauksessa odota vetäjän saapumista.", - "WaitingForHost": "Odotetaan vetäjää...", + "WaitingForHost": "Odotetaan vetäjää…", "Yes": "Kyllä", "yourEntireScreen": "Koko näyttö" }, @@ -349,13 +349,13 @@ "errorLiveStreamNotEnabled": "Suoratoisto ei ole käytössä tilillä {{email}}. Ota suoratoisto käyttöön tai kirjaudu tiliin, jossa se on käytössä.", "expandedOff": "Suoratoisto on päättynyt", "expandedOn": "Kokous näkyy parhaillaan YouTubessa suoratoistolähetyksenä.", - "expandedPending": "Suoratoistolähetys on alkamassa...", + "expandedPending": "Suoratoistolähetys on alkamassa…", "failedToStart": "Suoratoiston aloitus ei onnistunut", "getStreamKeyManually": "Suoratoistolähetysten nouto epäonnistui. Hanki suoratoistokoodi YouTubesta.", "invalidStreamKey": "Suoratoistokoodi voi olla virheellinen.", "off": "Suoratoisto päättyi", "on": "Suoratoisto", - "pending": "Suoratoisto alkamassa...", + "pending": "Suoratoisto alkamassa…", "serviceName": "Suoratoistopalvelu", "signedInAs": "Sisäänkirjautunut käyttäjä:", "signIn": "Kirjaudu Googlella", @@ -433,17 +433,17 @@ "poweredby": "tukija:", "presenceStatus": { "busy": "Varattu", - "calling": "Soitetaan...", + "calling": "Soitetaan…", "connected": "Yhdistetty", - "connecting": "Yhdistetään...", + "connecting": "Yhdistetään…", "connecting2": "Yhdistetään*...", "disconnected": "Ei yhteyttä", "expired": "Vanhentunut", "ignored": "Sivuutettu", - "initializingCall": "Käynnistetään puhelua...", + "initializingCall": "Käynnistetään puhelua…", "invited": "Kutsuttu", "rejected": "Hylätty", - "ringing": "Soi..." + "ringing": "Soi…" }, "profile": { "setDisplayNameLabel": "Määritä näyttönimi", @@ -460,14 +460,14 @@ "error": "Nauhoitus epäonnistui. Yritä uudelleen.", "expandedOff": "Nauhoitus päättyi", "expandedOn": "Tätä kokousta nauhoitetaan.", - "expandedPending": "Nauhoitus on alkamassa...", + "expandedPending": "Nauhoitus on alkamassa…", "failedToStart": "Nauhoituksen aloitus epäonnistui", "fileSharingdescription": "Jaa nauhoitus kokouksen osanottajille", "live": "SUORA LÄHETYS", "loggedIn": "Kirjautunut käyttäjänä {{userName}}", "off": "Nauhoitus päättyi", "on": "Nauhoitetaan", - "pending": "Kokouksen nauhoitusta valmistellaan...", + "pending": "Kokouksen nauhoitusta valmistellaan…", "rec": "REC", "serviceDescription": "Nauhoituspalvelu tallentaa nauhoituksen", "serviceName": "Nauhoituspalvelu", @@ -625,7 +625,7 @@ "failedToStart": "Puhtaaksikirjoituksen aloitus epäonnistui", "labelToolTip": "Kokousta kirjoitetaan puhtaaksi", "off": "Puhtaaksikirjoitus päättyi", - "pending": "Kokouksen puhtaaksikirjoitusta valmistellaan...", + "pending": "Kokouksen puhtaaksikirjoitusta valmistellaan…", "start": "Aloita tekstitys", "stop": "Lopeta tekstitys", "tr": "TR" @@ -709,4 +709,4 @@ "terms": "Ehdot", "title": "Turvallinen, täysin varustettu ja maksuton videoneuvottelu" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fr.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fr.json index f323a06c9..38b36d691 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fr.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-fr.json @@ -2,10 +2,10 @@ "addPeople": { "add": "Inviter", "countryNotSupported": "Nous ne supportons pas encore cette destination.", - "countryReminder": "Appel hors États-Unis? Veuillez commencer avec le code du pays!", + "countryReminder": "Appel hors des États-Unis ? Veuillez commencer avec le code du pays !", "disabled": "Vous ne pouvez pas inviter quelqu'un.", "failedToAdd": "Erreur lors de l'ajout des participants", - "footerText": "Appels sortants désactivés", + "footerText": "Appels sortants désactivés.", "loading": "Rechercher des personnes et des numéros de téléphone", "loadingNumber": "Validation du numéro de téléphone", "loadingPeople": "Recherche de personnes à inviter", @@ -14,7 +14,7 @@ "searchNumbers": "Ajouter des numéros de téléphone", "searchPeople": "Rechercher une personne", "searchPeopleAndNumbers": "Rechercher des personnes ou ajouter leurs numéros de téléphone", - "telephone": "Téléphone: {{number}}", + "telephone": "Téléphone : {{number}}", "title": "Inviter une personne à cette réunion" }, "audioDevices": { @@ -29,9 +29,9 @@ }, "calendarSync": { "addMeetingURL": "Ajouter un lien de conférence", - "confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?", + "confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement ?", "error": { - "appConfiguration": "l'intégration du calendrier n'est pas correctement configurée", + "appConfiguration": "L'intégration du calendrier n'est pas correctement configurée.", "generic": "Une erreur s'est produite. Veuillez vérifier les paramètres de votre calendrier ou tenter de l'actualiser.", "notSignedIn": "Une erreur s'est produite lors de l'authentification permettant d'afficher les événements du calendrier. Veuillez vérifier les paramètres de votre calendrier et essayer de vous reconnecter." }, @@ -46,16 +46,26 @@ "today": "Aujourd'hui" }, "chat": { - "error": "Erreur : votre message \"{{originalText}}\" n'a pas été envoyé. Raison : {{error}}", + "error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}", + "fieldPlaceHolder": "Tapez votre message ici", "messagebox": "Saisissez un message", + "messageTo": "Message privé à {{recipient}}", + "noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !", "nickname": { "popover": "Choisissez un pseudonyme", "title": "Entrez un pseudonyme pour utiliser le chat" }, - "title": "Chat" + "privateNotice": "Message privé à {{recipient}}", + "title": "Chat", + "you": "vous" + }, + "chromeExtensionBanner": { + "installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365", + "buttonText": "Installer l'extension Chrome", + "dontShowAgain": "Ne plus me montrer ceci" }, "connectingOverlay": { - "joiningRoom": "Connexion à la réunion..." + "joiningRoom": "Connexion à la réunion…" }, "connection": { "ATTACHED": "Attachée", @@ -67,15 +77,19 @@ "DISCONNECTED": "Déconnecté", "DISCONNECTING": "Déconnexion en cours", "ERROR": "Erreur", - "RECONNECTING": "Un problème réseau est survenue. Reconnexion en cours..." + "RECONNECTING": "Un problème réseau est survenue. Reconnexion en cours...", + "FETCH_SESSION_ID": "Obtention d’un identifiant de session…", + "GET_SESSION_ID_ERROR": "Obtenir une erreur d’identifiant de session : {{code}}", + "GOT_SESSION_ID": "Obtention d’un identifiant de session… Terminée", + "LOW_BANDWIDTH": "La vidéo de {{displayName}} a été désactivée pour économiser de la ba de passante" }, "connectionindicator": { "address": "Adresse :", "bandwidth": "Bande passante estimée :", "bitrate": "Débit :", - "bridgeCount": "Nombre de serveurs :", + "bridgeCount": "Nombre de serveurs : ", "connectedTo": "Connecté à :", - "framerate": "Images par seconde", + "framerate": "Images par seconde :", "less": "Cacher le détail", "localaddress": "Adresse locale :", "localaddress_plural": "Adresses locales :", @@ -92,12 +106,13 @@ }, "remoteaddress": "Adresse distante :", "remoteaddress_plural": "Adresses distantes :", - "remoteport": "Port distant:", - "remoteport_plural": "Ports distants:", + "remoteport": "Port distant :", + "remoteport_plural": "Ports distants :", "resolution": "Résolution :", - "status": "Connexion:", + "status": "Connexion :", "transport": "Transport :", - "transport_plural": "Transports :" + "transport_plural": "Transports :", + "e2e_rtt": "E2E RTT :" }, "dateUtils": { "earlier": "Plus tôt", @@ -106,8 +121,8 @@ }, "deepLinking": { "appNotInstalled": "Vous avez besoin de l'application mobile {{app}} pour participer à cette réunion avec votre téléphone.", - "description": "Rien ne s'est passé? Nous avons essayé de lancer votre réunion dans l'application de bureau {{app}}. Essayez à nouveau ou lancez-la dans l'application web {{app}}.", - "descriptionWithoutWeb": "Rien ne s'est passé? Nous avons essayé de démarrer votre réunion dans l'application bureau {{app}}.", + "description": "Rien ne s'est passé ? Nous avons essayé de lancer votre réunion dans l'application de bureau {{app}}. Essayez à nouveau ou lancez-la dans l'application web {{app}}.", + "descriptionWithoutWeb": "Rien ne s'est passé ? Nous avons essayé de démarrer votre réunion dans l'application bureau {{app}}.", "downloadApp": "Télécharger l'application", "launchWebButton": "Lancer dans le navigateur", "openApp": "Continuer vers l'application", @@ -118,7 +133,7 @@ "defaultNickname": "ex. Jean Dupont", "deviceError": { "cameraError": "Impossible d'accéder à votre caméra", - "cameraPermission": "Erreur lors de l'obtention de la permission de la caméra ", + "cameraPermission": "Erreur lors de l'obtention de la permission de la caméra", "microphoneError": "Impossible d'accéder à votre microphone", "microphonePermission": "Erreur lors de l'obtention de la permission du microphone" }, @@ -126,7 +141,7 @@ "noPermission": "Permission non accordée", "previewUnavailable": "Aperçu non disponible", "selectADevice": "Sélectionner un périphérique", - "testAudio": "Lire un audio de test" + "testAudio": "Tester la sortie audio" }, "dialog": { "accessibilityLabel": { @@ -138,8 +153,8 @@ "applicationWindow": "Fenêtre d'application", "Back": "Retour", "cameraConstraintFailedError": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.", - "cameraNotFoundError": "La caméra n'a pas été trouvée", - "cameraNotSendingData": "Nous sommes incapables d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page", + "cameraNotFoundError": "La caméra n'a pas été trouvée.", + "cameraNotSendingData": "Nous sommes incapables d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page.", "cameraNotSendingDataTitle": "Impossible d'accéder à votre caméra", "cameraPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre caméra. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous voir. Utilisez le bouton de la caméra dans la barre d'adresse pour résoudre ce problème.", "cameraUnknownError": "Vous ne pouvez pas utiliser la caméra pour une raison inconnue.", @@ -148,23 +163,23 @@ "close": "Fermer", "conferenceDisconnectMsg": "Veuillez vérifier votre connexion réseau. Reconnexion dans {{seconds}} sec...", "conferenceDisconnectTitle": "Vous avez été déconnecté.", - "conferenceReloadMsg": "Nous somme en train de régler cela. Reconnexion dans {{seconds}} sec...", + "conferenceReloadMsg": "On essaie d'arranger ça. Reconnexion dans {{seconds}} secondes...", "conferenceReloadTitle": "Malheureusement, un problème est survenu", "confirm": "Confirmer", "confirmNo": "Non", "confirmYes": "Oui", - "connectError": "Oups! Un problème est survenu et la connexion à la conférence est impossible.", - "connectErrorWithMsg": "Oups! Un problème est survenu et la connexion à la conférence est impossible: {{msg}}", + "connectError": "Oups ! Un problème est survenu et la connexion à la conférence est impossible.", + "connectErrorWithMsg": "Oups ! Un problème est survenu et la connexion à la conférence est impossible : {{msg}}", "connecting": "Connexion en cours", "contactSupport": "Contacter le support", "copy": "Copier", "dismiss": "Rejeter", - "displayNameRequired": "Salut! Quel est votre nom?", + "displayNameRequired": "Salut! Quel est votre nom ?", "done": "Terminé", "enterDisplayName": "Merci de saisir votre nom ici", "error": "Erreur", "externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.", - "externalInstallationTitle": "Extension requise : ", + "externalInstallationTitle": "Extension requise", "goToStore": "Aller sur le webstore", "gracefulShutdown": "Le service est actuellement en maintenance. Réessayez plus tard.", "IamHost": "Je suis l’hôte", @@ -172,18 +187,18 @@ "incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect", "inlineInstallationMsg": "Vous devez installer notre extension de partage de bureau.", "inlineInstallExtension": "Installer maintenant", - "internalError": "Oups! Quelque chose s'est mal passée. L'erreur suivante s'est produite: {{error}}", + "internalError": "Oups ! Quelque chose s'est mal passée. L'erreur suivante s'est produite : {{error}}", "internalErrorTitle": "Erreur interne", "kickMessage": "Vous pouvez contacter {{participantDisplayName}} pour plus de détails.", "kickParticipantButton": "Expulser", "kickParticipantDialog": "Êtes-vous sûr(e) de vouloir expulser ce participant ?", - "kickParticipantTitle": "Expulser ce participant?", + "kickParticipantTitle": "Expulser ce participant ?", "kickTitle": "Oups! vous avez été expulsé(e) par {{participantDisplayName}}", "liveStreaming": "Direct", "liveStreamingDisabledForGuestTooltip": "Les invités ne peuvent démarrer la diffusion en direct.", "liveStreamingDisabledTooltip": "La diffusion en direct est désactivé", "lockMessage": "Impossible de verrouiller la conférence.", - "lockRoom": "Ajouter la réunion $t(lockRoomPasswordUppercase)", + "lockRoom": "Ajouter un $t(lockRoomPassword) à la réunion ", "lockTitle": "Échec du verrouillage", "logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence ?", "logoutTitle": "Déconnexion", @@ -195,14 +210,20 @@ "micNotSendingDataTitle": "Votre micro est désactivé par les paramètres de votre système", "micPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre microphone. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour résoudre ce problème.", "micUnknownError": "Vous ne pouvez pas utiliser le microphone pour une raison inconnue.", - "muteParticipantBody": "Vous ne pourrez plus réactiver leurs micros, mais ils peuvent l'activer par eux-même à tout moment.", + "muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.", + "muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}} ?", + "muteEveryoneDialog": "Etes-vous sûr de vouloir couper les micros de tout le monde ? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.", + "muteEveryoneTitle": "Couper le micro de tout le monde ?", + "muteEveryoneSelf": "vous", + "muteEveryoneStartMuted": "Tout le monde démarre avec le micro coupé", + "muteParticipantBody": "Vous ne pourrez plus réactiver son micro, mais il pourra l'activer par lui-même à tout moment.", "muteParticipantButton": "Couper le micro", "muteParticipantDialog": "Êtes-vous sûr(e) de vouloir couper le micro de ce participant ? Seul le participant pourra ensuite réactiver son micro à tout moment.", - "muteParticipantTitle": "Couper le micro de ce participant?", + "muteParticipantTitle": "Couper le micro de ce participant ?", "Ok": "Ok", - "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordLabel": "La réunion a été verrouillée par un·e participant·e. Veuillez entrer le $t(lockRoomPassword) pour la rejoindre.", "passwordNotSupported": "La définition d'un $t(lockRoomPassword) de réunion n'est pas prise en charge.", - "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) n'est pas supporté", + "passwordNotSupportedTitle": "L'ajout d'un $t(lockRoomPassword) n'est pas supporté", "passwordRequired": "$t(lockRoomPasswordUppercase) requis", "popupError": "Votre navigateur bloque les fenêtres pop-up. Veuillez autoriser les fenêtres pop-up dans les paramètres de votre navigateur.", "popupErrorTitle": "Pop-up bloquée", @@ -210,15 +231,15 @@ "recordingDisabledForGuestTooltip": "Les invités ne peuvent enregistrer.", "recordingDisabledTooltip": "L'enregistrement est désactivé.", "rejoinNow": "Rejoindre maintenant", - "remoteControlAllowedMessage": "Une erreur s'est produite lors de la demande d’autorisation de prise en main à distance avec {{user}}!", - "remoteControlDeniedMessage": "{{user}} a refusé votre demande de prise en main à distance!", - "remoteControlErrorMessage": "Une erreur s'est produite lors de la demande d’autorisation de prise en main à distance avec {{user}}!", - "remoteControlRequestMessage": "Voulez-vous autoriser {{user}} à contrôler votre bureau?", - "remoteControlShareScreenWarning": "Si vous appuyez sur \"Autoriser\" vous allez partager votre écran!", + "remoteControlAllowedMessage": "{{user}} a accepté votre demande de prise en main à distance !", + "remoteControlDeniedMessage": "{{user}} a refusé votre demande de prise en main à distance !", + "remoteControlErrorMessage": "Une erreur s'est produite lors de la demande d’autorisation de prise en main à distance avec {{user}} !", + "remoteControlRequestMessage": "Voulez-vous autoriser {{user}} à contrôler votre bureau ?", + "remoteControlShareScreenWarning": "Si vous appuyez sur \"Autoriser\", vous allez partager votre écran !", "remoteControlStopMessage": "La prise en main à distance est terminée!", "remoteControlTitle": "Contrôle de bureau à distance", "Remove": "Supprimer", - "removePassword": "Supprimer $t(lockRoomPassword)", + "removePassword": "Supprimer le $t(lockRoomPassword)", "removeSharedVideoMsg": "Voulez-vous vraiment supprimer votre vidéo partagée ?", "removeSharedVideoTitle": "Supprimer la vidéo partagée", "reservationError": "Erreur du système de réservation", @@ -226,9 +247,13 @@ "retry": "Réessayer", "screenSharingFailedToInstall": "Oups! Votre extension de partage d'écran n'a pas pu être installée.", "screenSharingFailedToInstallTitle": "L'extension de partage d'écran n'a pas pu être installée", - "screenSharingFirefoxPermissionDeniedError": "Quelque chose s'est mal passé pendant que nous essayions de partager votre écran. S'il vous plaît assurez-vous que vous nous avez donné la permission de le faire.", - "screenSharingFirefoxPermissionDeniedTitle": "Oups! Nous ne pouvions pas démarrer le partage d'écran!", + "screenSharingFirefoxPermissionDeniedError": "Quelque chose s'est mal passé pendant que nous essayions de partager votre écran. S'il vous plaît assurez-vous que vous nous avez donné la permission de le faire. ", + "screenSharingFirefoxPermissionDeniedTitle": "Oups! Nous ne pouvions pas démarrer le partage d'écran !", "screenSharingPermissionDeniedError": "Oups! Une erreur s'est produite avec vos autorisations d'extension de partage d'écran. Veuillez rafraîchir et réessayer.", + "sendPrivateMessage": "Vous avez récemment reçu un message privé. Aviez-vous l'intention d'y répondre en privé, ou vouliez-vous envoyer votre message au groupe ?", + "sendPrivateMessageCancel": "Envoyer au groupe", + "sendPrivateMessageOk": "Envoyer en privé", + "sendPrivateMessageTitle": "Envoyer en privé ?", "serviceUnavailable": "Service indisponible", "sessTerminated": "Appel terminé", "Share": "Partager", @@ -242,8 +267,8 @@ "startRemoteControlErrorMessage": "Une erreur est survenue lors de la tentative de démarrage de la session de contrôle à distance!", "stopLiveStreaming": "Arrêter la diffusion en direct", "stopRecording": "Arrêter l'enregistrement", - "stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement?", - "stopStreamingWarning": "Désirez-vous vraiment arrêter le direct?", + "stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement ?", + "stopStreamingWarning": "Désirez-vous vraiment arrêter le direct ?", "streamKey": "Clé Live stream", "Submit": "Soumettre", "thankYou": "Merci d'avoir utilisé {{appName}} !", @@ -251,23 +276,27 @@ "tokenAuthFailed": "Désolé, vous n'êtes pas autorisé à rejoindre cette conversation.", "tokenAuthFailedTitle": "Échec de l'authentification", "transcribing": "Transcription", - "unlockRoom": "Supprimer $t(lockRoomPassword) de la réunion", + "unlockRoom": "Supprimer le $t(lockRoomPassword) de la réunion", "userPassword": "mot de passe utilisateur", "WaitForHostMsg": "La conférence <b>{{room}}</b> n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.", "WaitForHostMsgWOk": "La conférence <b>{{room}}</b> n'a pas encore commencé. Si vous en êtes l'hôte, veuillez appuyer sur Ok pour vous authentifier. Sinon, veuillez attendre son arrivée.", "WaitingForHost": "En attente de l'hôte ...", "Yes": "Oui", - "yourEntireScreen": "Votre écran entier" + "yourEntireScreen": "Votre écran entier", + "screenSharingAudio": "Partager l’audio" }, "dialOut": { "statusMessage": "est maintenant {{status}}" }, + "documentSharing": { + "title": "Document partagé" + }, "feedback": { "average": "Moyen", "bad": "Mauvais", "detailsLabel": "Dites nous en plus à ce sujet.", "good": "Bien", - "rateExperience": "Veuillez évaluer votre expérience.", + "rateExperience": "Veuillez évaluer votre expérience", "veryBad": "Très mauvais", "veryGood": "Très bon" }, @@ -280,30 +309,30 @@ }, "info": { "accessibilityLabel": "Afficher les informations", - "addPassword": "Ajouter $t(lockRoomPassword)", - "cancelPassword": "Annuler $t(lockRoomPassword)", - "conferenceURL": "Lien:", + "addPassword": "Ajouter un $t(lockRoomPassword)", + "cancelPassword": "Supprimer le $t(lockRoomPassword)", + "conferenceURL": "Lien :", "country": "Pays", "dialANumber": "Pour rejoindre votre réunion, composez l'un de ces numéros, puis saisissez le code confidentiel.", - "dialInConferenceID": "PIN:", + "dialInConferenceID": "PIN :", "dialInNotSupported": "Désolé, l'accès par téléphone n'est pas pris en charge pour l'instant.", - "dialInNumber": "Composer:", + "dialInNumber": "Composer :", "dialInSummaryError": "Erreur lors de la récupération des informations de numérotation. Veuillez réessayer plus tard.", "dialInTollFree": "Numéro gratuit", "genericError": "Oups, quelque chose a mal tourné.", "inviteLiveStream": "Pour voir la diffusion en direct de cette réunion, cliquez sur ce lien : {{url}}", "invitePhone": "Pour rejoindre depuis un téléphone, saisissez : {{number}},,{{conferenceID}}#\n", - "invitePhoneAlternatives": "Vous cherchez un numéro d'appel différent?\nAfficher les numéros d'appel de la réunion: {{url}}\n\n\nSi vous appelez également via un téléphone de salle, vous pouvez vous connecter sans audio: {{silentUrl}}", + "invitePhoneAlternatives": "Vous cherchez un numéro d'appel différent ?\nAfficher les numéros d'appel de la réunion: {{url}}\n\n\nSi vous appelez également via un téléphone de salle, vous pouvez vous connecter sans audio: {{silentUrl}}", "inviteURLFirstPartGeneral": "Vous êtes invité(e) à participer à une réunion.", "inviteURLFirstPartPersonal": "{{name}} vous invite à une réunion.\n", - "inviteURLSecondPart": "\nRejoindre la réunion:\n{{url}}\n", + "inviteURLSecondPart": "\nRejoindre la réunion :\n{{url}}\n", "liveStreamURL": "Diffusion en direct :", - "moreNumbers": "Plus de numéros ", - "noNumbers": "Numéros à composer non trouvés", + "moreNumbers": "Plus de numéros", + "noNumbers": "Aucun numéro à composer.", "noPassword": "Aucun", "noRoom": "Aucune réunion n'a été spécifiée pour l'appel entrant.", "numbers": "Numéros d'appel", - "password": "$t(lockRoomPasswordUppercase):", + "password": "$t(lockRoomPasswordUppercase) :", "title": "Partager", "tooltip": "Partager le lien et les informations de connexion pour cette conférence", "label": "Information de la réunion" @@ -345,10 +374,10 @@ "changeSignIn": "Changer de compte.", "choose": "Choisir un flux live", "chooseCTA": "Choisissez une option de diffusion. Vous êtes actuellement connecté comme {{email}}.", - "enterStreamKey": "Entrez votre clé de flux live Youtube ici", + "enterStreamKey": "Entrez votre clé de flux direct YouTube ici.", "error": "Le Streaming a échoué. Veuillez réessayer.", "errorAPI": "Une erreur s'est produite lors de l'accès à vos diffusions YouTube. Veuillez réessayer de vous connecter.", - "errorLiveStreamNotEnabled": "La diffusion en direct n'est pas activée pour {{email}}. Merci de l'activer ou de vous connecter avec un compte où elle est déjà activée.", + "errorLiveStreamNotEnabled": "La diffusion en direct n'est pas activée pour {{email}}. Merci de l'activer ou de vous connecter avec un compte où elle est déjà activée.", "expandedOff": "La diffusion en direct a été arrêtée", "expandedOn": "La conférence est en cours de diffusion sur YouTube.", "expandedPending": "La diffusion en direct a commencé...", @@ -356,7 +385,9 @@ "getStreamKeyManually": "Nous n'avons pu récupérer aucun flux en direct. Essayez d’obtenir votre clé de diffusion en direct sur YouTube.", "invalidStreamKey": "La clé de diffusion en direct n'est peut-être pas correcte.", "off": "Le Streaming a été arrêté", + "offBy": "{{name}} a arrêté la diffusion en continu", "on": "Direct", + "onBy": "{{name}} démarré la diffusion en continu", "pending": "Commencer le direct...", "serviceName": "Service de diffusion en direct", "signedInAs": "Vous êtes connecté en tant que :", @@ -364,8 +395,10 @@ "signInCTA": "Connectez vous ou entrez votre clé de flux live provenant de Youtube.", "signOut": "Se déconnecter", "start": "Démarrer la diffusion en direct", - "streamIdHelp": "Qu'est-ce que c'est?", - "unavailableTitle": "Le Streaming est indisponible" + "streamIdHelp": "Qu'est-ce que c'est ?", + "unavailableTitle": "Le Streaming est indisponible", + "youtubeTerms": "Conditions d’utilisation des services YouTube", + "googlePrivacyPolicy": "Politique de confidentialité de Google" }, "localRecording": { "clientState": { @@ -378,18 +411,18 @@ "durationNA": "N/A", "encoding": "Encodage", "label": "ENR-LOC", - "labelToolTip": "L'enregistrement local est engagé", + "labelToolTip": "L'enregistrement local est démarré", "localRecording": "Enregistrement local", "me": "Moi", "messages": { - "engaged": "Enregistrement local engagé.", + "engaged": "L'enregistrement local a démarré.", "finished": "L'enregistrement de la session {{token}} s'est terminé. Merci d'envoyer le fichier au modérateur.", "finishedModerator": "L'enregistrement de la session {{token}} s'est terminé. La piste a bien été sauvegardée. Merci de demander aux autres participants de soumettre leurs enregistrements.", "notModerator": "Vous n'êtes pas le modérateur. Vous ne pouvez pas démarrer ou arrêter un enregistrement local." }, - "moderator": "Moderateur", + "moderator": "Modérateur", "no": "Non", - "participant": "Participant", + "participant": "Participant·e", "participantStats": "Statistiques du participant", "sessionToken": "Token de la session", "start": "Démarrer l'enregistrement", @@ -400,14 +433,14 @@ "lockRoomPasswordUppercase": "Mot de passe", "me": "moi", "notify": { - "connectedOneMember": "{{name}} a rejoint la réunion.", - "connectedThreePlusMembers": "{{name}} et {{count}} autres personnes ont rejoint la réunion.", - "connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion.", + "connectedOneMember": "{{name}} a rejoint la réunion", + "connectedThreePlusMembers": "{{name}} et {{count}} autres personnes ont rejoint la réunion", + "connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion", "disconnected": "déconnecté", "focus": "Focus de conférence", "focusFail": "{{component}} n'est pas disponible - réessayez dans {{ms}} sec", "grantedTo": "Droits modérateur accordés à {{to}} !", - "invitedOneMember": "{{displayName}} a été invité(e)", + "invitedOneMember": "{{name}} a été invité·e", "invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités", "invitedTwoMembers": "{{first}} et {{second}} ont été invités", "kickParticipant": "{{kicked}} a été expulsé par {{kicker}}", @@ -415,13 +448,13 @@ "moderator": "Droits modérateur accordés !", "muted": "Vous avez commencé la conversation en muet.", "mutedTitle": "Vous êtes en muet !", - "mutedRemotelyTitle": "Votre micro a été coupé par {{participantDisplayName}}!", + "mutedRemotelyTitle": "Votre micro a été coupé par {{participantDisplayName}} !", "mutedRemotelyDescription": "Vous pouvez toujours activer votre micro pour prendre la parole. Désactivez votre micro quand vous terminez pour éviter les bruits parasites.", - "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) a été supprimé par un autre participant", - "passwordSetRemotely": "$t(lockRoomPasswordUppercase) défini par un autre participant", + "passwordRemovedRemotely": "Le $t(lockRoomPassword) a été supprimé par un autre participant", + "passwordSetRemotely": "Un $t(lockRoomPassword) a été défini par un autre participant", "raisedHand": "{{name}} aimerait prendre la parole.", "somebody": "Quelqu'un", - "startSilentTitle": "Vous avez rejoint sans sortie audio!", + "startSilentTitle": "Vous avez rejoint sans sortie audio !", "startSilentDescription": "Rejoignez la réunion de nouveau pour activer l'audio", "suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit bonne ici. Nous cherchons des moyens d’améliorer cela, mais d’ici-là, essayez d’utiliser l’un des <a href='static/recommendedBrowsers.html' target='_blank'>navigateurs supportés</a>.", "suboptimalExperienceTitle": "Avertissement du navigateur", @@ -432,7 +465,7 @@ }, "passwordSetRemotely": "défini par un autre participant", "passwordDigitsOnly": "Jusqu'à {{number}} chiffres", - "poweredby": "Produit par", + "poweredby": "produit par", "presenceStatus": { "busy": "Occupé", "calling": "Appel...", @@ -456,7 +489,7 @@ "raisedHand": "Aimerait prendre la parole", "recording": { "authDropboxText": "Téléchargement vers Dropbox", - "availableSpace": "Espace disponible: {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)", + "availableSpace": "Espace disponible : {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)", "beta": "BETA", "busy": "Nous sommes en train de libérer les ressources d'enregistrement. Réessayez dans quelques minutes.", "busyTitle": "Tous les enregistreurs sont actuellement occupés", @@ -469,14 +502,16 @@ "live": "DIRECT", "loggedIn": "Connecté en tant que {{userName}}", "off": "Enregistrement arrêté", + "offBy": "{{name}} a arrêté l'enregistrement", "on": "Enregistrement", + "onBy": "{{name}} a démarré l'enregistrement", "pending": "Préparation de l'enregistrement de la réunion...", "rec": "REC", - "serviceDescription": "Votre enregistrement sera enregistré par le service dédié.", + "serviceDescription": "Votre enregistrement sera enregistré par le service dédié", "serviceName": "Service d'enregistrement", "signIn": "Se connecter", "signOut": "Se déconnecter", - "unavailable": "Oups! Le {{serviceName}} est actuellement indisponible. Nous travaillons sur la résolution du problème. Veuillez réessayer plus tard.", + "unavailable": "Oups ! Le {{serviceName}} est actuellement indisponible. Nous travaillons sur la résolution du problème. Veuillez réessayer plus tard.", "unavailableTitle": "Enregistrement indisponible" }, "sectionList": { @@ -494,7 +529,7 @@ "followMe": "Tout le monde me suit", "language": "Langue", "loggedIn": "Connecté en tant que {{name}}", - "moderator": "Moderateur", + "moderator": "Modérateur", "more": "Plus", "name": "Nom", "noDevice": "Aucun", @@ -503,25 +538,31 @@ "selectMic": "Microphone", "startAudioMuted": "Tout le monde commence en muet", "startVideoMuted": "Tout le monde commence sans vidéo", - "title": "Paramètres" + "title": "Paramètres", + "microphones": "Microphones", + "speakers": "Intervenants" }, "settingsView": { + "advanced": "Avancé", "alertOk": "D'accord", "alertTitle": "Avertissement", "alertURLText": "L'URL du serveur est invalide", "buildInfoSection": "Informations de build", "conferenceSection": "Conférence", + "disableCallIntegration": "Désactiver l'intégration d'appels native", + "disableP2P": "Désactiver le mode pair à pair", "displayName": "Pseudo", "email": "Email", "header": "Paramètres", "profileSection": "Profil", "serverURL": "URL du serveur", - "startWithAudioMuted": "Commencez avec la vidéo en sourdine", + "showAdvanced": "Afficher les paramètres avancés", + "startWithAudioMuted": "Commencez avec l'audio en sourdine", "startWithVideoMuted": "Commencez avec la vidéo en sourdine", "version": "Version" }, "share": { - "dialInfoText": "\n\n=====\n\nVoulez-vous appeler depuis votre téléphone?\n\n{{defaultDialInNumber}}Cliquez sur ce lien pour afficher les numéros d'appels pour cette réunion\n{{dialInfoPageUrl}}", + "dialInfoText": "\n\n=====\n\nVoulez-vous appeler depuis votre téléphone ?\n\n{{defaultDialInNumber}}Cliquez sur ce lien pour afficher les numéros d'appels pour cette réunion\n{{dialInfoPageUrl}}", "mainText": "Cliquez sur le lien suivant pour rejoindre une conférence :\n{{roomUrl}}" }, "speaker": "Haut-parleur", @@ -535,7 +576,7 @@ }, "startupoverlay": { "policyText": " ", - "title": " {{app}} a besoin d'accéder à votre microphone et votre caméra." + "title": "{{app}} a besoin d'accéder à votre microphone et votre caméra." }, "suspendedoverlay": { "rejoinKeyTitle": "Rejoindre", @@ -550,9 +591,11 @@ "cc": "Activer/désactiver les sous-titres", "chat": "Afficher/masquer la discussion instantanée", "document": "Activer/désactiver le document partagé", + "download": "Télécharger nos applications", "feedback": "Laisser des commentaires", "fullScreen": "Activer/désactiver le plein écran", "hangup": "Quitter la conversation", + "help": "Aide", "invite": "Inviter des participants", "kick": "Expulser le participant", "localRecording": "Activer/désactiver les contrôles d'enregistrement local", @@ -561,21 +604,25 @@ "moreActionsMenu": "Menu d'actions supplémentaires", "mute": "Activer/désactiver l'audio", "pip": "Activer/désactiver le mode Picture in Picture", + "privateMessage": "Envoyer un message privé", "profile": "Éditer votre profil", "raiseHand": "Lever/baisser la main", "recording": "Activer/désactiver l'enregistrement", "remoteMute": "Désactiver le micro du participant", "Settings": "Afficher/masquer le menu des paramètres", - "sharedvideo": "Démarrer/arrêter le partage de vidéo Youtube", + "sharedvideo": "Démarrer/arrêter le partage de vidéo YouTube", "shareRoom": "Inviter quelqu'un", "shareYourScreen": "Activer/désactiver le partage d’écran", "shortcuts": "Afficher/masquer les raccourcis", "show": "Afficher en premier plan", "speakerStats": "Afficher/cacher les statistiques de parole", "tileView": "Activer/désactiver la vue mosaïque", - "toggleCamera": "Activer/désactiver la caméra", + "toggleCamera": "Changer de caméra", "videomute": "Activer/désactiver la vidéo", - "videoblur": "Activer/désactiver le flou de la vidéo" + "videoblur": "Activer/désactiver le flou de la vidéo", + "muteEveryone": "Mettre tout le monde en sourdine", + "moreOptions": "Afficher plus d'options", + "toggleFilmstrip": "Basculer la bande de film" }, "addPeople": "Ajouter des personnes à votre appel", "audioOnlyOff": "Désactiver le mode bande passante réduite", @@ -587,20 +634,30 @@ "closeChat": "Fermer le chat", "documentClose": "Fermer le document partagé", "documentOpen": "Ouvrir le document partagé", + "download": "Télécharger nos applications", "enterFullScreen": "Afficher en plein écran", "enterTileView": "Accéder au mode mosaïque", "exitFullScreen": "Quitter le mode plein écran", "exitTileView": "Quitter le mode mosaïque", "feedback": "Laisser des commentaires", "hangup": "Quitter", + "help": "Aide", "invite": "Inviter des participants", "login": "Connexion", "logout": "Déconnexion", "lowerYourHand": "Baisser la main", "moreActions": "Plus d'actions", - "mute": "Muet / Actif", + "moreOptions": "Plus d'options", + "mute": "Activer / Désactiver le micro", + "muteEveryone": "Couper le micro de tout le monde", + "noAudioSignalTitle": "Il n'y a pas d'entrée provenant de votre micro !", + "noAudioSignalDesc": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, envisagez de changer le périphérique.", + "noAudioSignalDescSuggestion": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, pensez à utiliser le périphérique suivant.", + "noisyAudioInputTitle": "Votre microphone semble être bruyant !", + "noisyAudioInputDesc": "Il semble que votre microphone fasse du bruit, pensez à le mettre en sourdine ou à changer d'appareil.", "openChat": "Ouvrir le chat", "pip": "Entrer en mode Picture-in-Picture", + "privateMessage": "Envoyer un message privé", "profile": "Éditer votre profil", "raiseHand": "Lever / Baisser la main", "raiseYourHand": "Lever la main", @@ -614,12 +671,15 @@ "stopScreenSharing": "Arrêter le partage d'écran", "stopSubtitles": "Désactiver les sous-titres", "stopSharedVideo": "Arrêter la vidéo YouTube", - "talkWhileMutedPopup": "Vous voulez parler? Vous êtes en muet.", + "talkWhileMutedPopup": "Vous voulez parler ? Vôtre micro est coupé.", "tileViewToggle": "Activer/désactiver la vue mosaïque", - "toggleCamera": "Activer/désactiver la caméra", + "toggleCamera": "Changer de caméra", "videomute": "Démarrer / Arrêter la caméra", "startvideoblur": "Flouter mon arrière plan", - "stopvideoblur": "Désactiver le flou d'arrière-plan" + "stopvideoblur": "Désactiver le flou d'arrière-plan", + "muteEveryone": "Mettre tout le monde en sourdine", + "noAudioSignalDialInDesc": "Vous pouvez également composer un numéro en utilisant :", + "noAudioSignalDialInLinkDesc": "Numéros d'appel" }, "transcribing": { "ccButtonTooltip": "Activer/Désactiver les sous-titres", @@ -671,19 +731,20 @@ "onlyAudioSupported": "Nous ne supportons que l'audio sur ce navigateur.", "p2pEnabled": "Peer to Peer activé", "p2pVideoQualityDescription": "En mode peer to peer, la qualité vidéo reçue ne peut être basculée qu'entre haute et audio uniquement. Les autres paramètres ne seront pas pris en compte jusqu'à ce que vous quittiez le mode peer to peer.", - "recHighDefinitionOnly": "Va préférer la haute définition", + "recHighDefinitionOnly": "Va préférer la haute définition.", "sd": "MD", "sdTooltip": "Regardez la vidéo en définition standard", "standardDefinition": "Moyenne Définition" }, "videothumbnail": { "domute": "Couper le micro", + "domuteOthers": "Couper le micro de tous les autres", "flip": "Balancer", "kick": "Exclure", - "moderator": "Moderateur", + "moderator": "Modérateur", "mute": "Un participant a coupé son micro", "muted": "Muet", - "remoteControl": "Contrôle à distance", + "remoteControl": "Démarrer / Arrêter le contrôle à distance", "show": "Afficher en premier plan", "videomute": "Le participant a arrêté la caméra" }, @@ -692,7 +753,7 @@ "join": "Touchez pour rejoindre", "roomname": "Saisissez un nom de salle" }, - "appDescription": "Allez-y, chat vidéo avec toute l'équipe. En fait, invitez tout le monde que vous connaissez. {{app}} est une solution de visioconférence entièrement cryptée et 100% open source que vous pouvez utiliser toute la journée, tous les jours, gratuitement— aucun compte requis.", + "appDescription": "Allez-y, chat vidéo avec toute l'équipe. En fait, invitez tout le monde que vous connaissez. {{app}} est une solution de visioconférence entièrement chiffrée et 100% open source que vous pouvez utiliser toute la journée, tous les jours, gratuitement— aucun compte requis.", "audioVideoSwitch": { "audio": "Voix", "video": "Vidéo" @@ -701,18 +762,28 @@ "connectCalendarButton": "Connecter votre calendrier", "connectCalendarText": "Connectez-vous à votre calendrier pour afficher toutes les réunions {{app}}. Ajoutez également les réunions de {{provider}} à votre calendrier et démarrez-les d'un simple clic.", "enterRoomTitle": "Démarrer une nouvelle réunion", + "roomNameAllowedChars": "Le nom de la réunion ne doit contenir aucun de ces caractères : ?, &, :, ', \", %, #.", "go": "Créer", - "join": "REJOINDRE", + "goSmall": "Créer", + "join": "CRÉER / REJOINDRE", "info": "Infos", "privacy": "Confidentialité", "recentList": "Récent", "recentListDelete": "Supprimer", "recentListEmpty": "Votre liste récente est actuellement vide. Discuter avec votre équipe et vous trouverez toutes vos réunions récentes ici.", - "reducedUIText": "Bienvenue sur {{app}}!", + "reducedUIText": "Bienvenue sur {{app}} !", "roomname": "Saisissez un nom de salle", "roomnameHint": "Entrez le nom ou l'URL de la salle que vous souhaitez rejoindre. Vous pouvez faire un nom, laissez les gens que vous rencontrerez le savoir afin qu'ils entrent le même nom.", "sendFeedback": "Envoyer votre avis", "terms": "Termes", - "title": "Vidéoconférence Sécurisée, entièrement en vedette et gratuite" + "title": "Vidéoconférence Sécurisée, entièrement en vedette et gratuite", + "getHelp": "Obtenir de l'aide" + }, + "lonelyMeetingExperience": { + "button": "Inviter d'autres personnes", + "youAreAlone": "Vous êtes le seul participant de la réunion" + }, + "helpView": { + "header": "Centre d'aide" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-frCA.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-frCA.json index 27d3a6552..6b9562afb 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-frCA.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-frCA.json @@ -1,6 +1,6 @@ { "addPeople": { - "add": "", + "add": "Inviter", "countryNotSupported": "Nous ne prenons pas encore cette destination en charge.", "countryReminder": "Vous appelez en dehors des É.-U.? Veuillez vous assurer de commencer par le code de pays!", "disabled": "Vous ne pouvez pas inviter d'autres personnes.", @@ -21,14 +21,15 @@ "bluetooth": "Bluetooth", "headphones": "Écouteurs", "phone": "Téléphone", - "speaker": "" + "speaker": "Intervenant", + "none": "Aucun périphérique audio n'est disponible" }, "audioOnly": { - "audioOnly": "Audio seulement" + "audioOnly": "Bande passante faible" }, "calendarSync": { "addMeetingURL": "Ajouter un lien de réunion", - "confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?", + "confirmAddLink": "Voulez-vous ajouter un lien Jitsi Meet à cet événement ?", "error": { "appConfiguration": "L'intégration de l'agenda n'est pas correctement configurée.", "generic": "Une erreur s'est produite. Veuillez vérifier vos paramètres d'agenda ou essayer de rafraîchir l'agenda.", @@ -42,30 +43,35 @@ "permissionButton": "Ouvrir les paramètres", "permissionMessage": "L'autorisation de l'Agenda est nécessaire pour consulter vos réunions dans l'application.", "refresh": "Rafraîchir l'agenda", - "today": "" + "today": "Aujourd'hui" }, "chat": { - "error": "Erreur : votre message \"{{originalText}}\" n'a pas été envoyé. Raison : {{error}}", + "error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}", + "fieldPlaceHolder": "Tapez votre message ici", "messagebox": "Tapez un message", + "messageTo": "", + "noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !", "nickname": { "popover": "Choisissez un nom d'affichage", "title": "Entrer un nom d'affichage pour utiliser le clavardage" }, - "title": "Clavardage" + "privateNotice": "Message privé à {{recipient}}", + "title": "Clavardage", + "you": "vous" }, "connectingOverlay": { - "joiningRoom": "Connexion à la réunion en cours..." + "joiningRoom": "Connexion à la réunion en cours…" }, "connection": { "ATTACHED": "Joint", "AUTHENTICATING": "Authentification", - "AUTHFAIL": "", - "CONNECTED": "", - "CONNECTING": "", + "AUTHFAIL": "Échec de l'authentification", + "CONNECTED": "Connecté", + "CONNECTING": "Connexion en cours", "CONNFAIL": "Échec de la connexion", - "DISCONNECTED": "", + "DISCONNECTED": "Déconnecté", "DISCONNECTING": "Déconnexion en cours", - "ERROR": "", + "ERROR": "Erreur", "RECONNECTING": "Un problème de réseau est survenu. Reconnexion en cours..." }, "connectionindicator": { @@ -76,28 +82,27 @@ "connectedTo": "Connecté à :", "framerate": "Fréquence d'images :", "less": "Afficher moins", - "localaddress_plural": "Adresse locale :", - "localaddress": "Adresses locales :", - "localport_plural": "Port local :", - "localport": "Ports locaux :", + "localaddress": "Adresse locale :", + "localaddress_plural": "Adresses locales :", + "localport": "Port local :", + "localport_plural": "Ports locaux :", "more": "Afficher plus", "packetloss": "Perte de paquet :", "quality": { - "good": "", + "good": "Bonne", "inactive": "Inactive", "lost": "Perdue", "nonoptimal": "Non-optimale", "poor": "Faible" }, - "remoteaddress_plural": "Adresse distante :", - "remoteaddress": "Adresses distantes :", - "remoteport_plural": "Port distant :", - "remoteport": "Ports distants :", + "remoteaddress": "Adresse distante :", + "remoteaddress_plural": "Adresses distantes :", + "remoteport": "Port distant :", + "remoteport_plural": "Ports distants :", "resolution": "Résolution :", "status": "Connexion :", - "transport_plural": "Tranport :", - "transport": "Transports :", - "turn": " (tour)" + "transport": "Tranport :", + "transport_plural": "Transports :" }, "dateUtils": { "earlier": "Plus tôt", @@ -107,14 +112,15 @@ "deepLinking": { "appNotInstalled": "L'application mobile {{app}} est nécessaire pour rejoindre cette réunion sur votre téléphone.", "description": "Il ne s'est rien passé? Nous avons essayés de démarrer votre réunion dans l'application de bureau {{app}}. Veuillez réessayer ou démarrer la réunion dans l'application Web {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "Rien ne s'est passé? Nous avons essayé de démarrer votre réunion dans l'application bureau {{app}}.", "downloadApp": "Télécharger l'application", "launchWebButton": "Démarrer dans l'application Web", "openApp": "Continuer vers l'application", - "title": "Démarrage de votre réunion dans {{app}} en cours...", + "title": "Démarrage de votre réunion dans {{app}} en cours…", "tryAgainButton": "Veuillez réessayer sur votre ordinateur" }, "defaultLink": "p. ex. {{url}}", + "defaultNickname": "p. ex. Jane Pink", "deviceError": { "cameraError": "Échec de l'accès à votre caméra", "cameraPermission": "Erreur lors de l'obtention de l'autorisation de la caméra", @@ -145,29 +151,29 @@ "cameraUnsupportedResolutionError": "Votre caméra ne prend pas en charge la résolution vidéo nécessaire.", "Cancel": "Annuler", "close": "Fermer", - "conferenceDisconnectMsg": "Vous devriez vérifier votre connexion au réseau. Reconnexion dans {{seconds}} sec...", + "conferenceDisconnectMsg": "Vous devriez vérifier votre connexion au réseau. Reconnexion dans {{seconds}} sec…", "conferenceDisconnectTitle": "Vous avez été déconnecté.", - "conferenceReloadMsg": "Nous tentons de résoudre le problème. Reconnexion dans {{seconds}} sec...", + "conferenceReloadMsg": "Nous tentons de résoudre le problème. Reconnexion dans {{seconds}} sec…", "conferenceReloadTitle": "Malheureusement, une erreur s'est produite.", "confirm": "Confirmer", - "confirmNo": "", - "confirmYes": "", + "confirmNo": "Non", + "confirmYes": "Oui", "connectError": "Oups! Une erreur s'est produite. La connexion à la conférence a échouée.", "connectErrorWithMsg": "Oups! Une erreur s'est produite. La connexion à la conférence a échoué : {{msg}}", "connecting": "Connexion en cours", "contactSupport": "Communiquez avec le service de soutien", "copy": "Copier", - "dismiss": "", + "dismiss": "Rejeter", "displayNameRequired": "Un nom d'affichage est requis", "done": "Terminé", "enterDisplayName": "Veuillez saisir votre nom d'affichage", "error": "Erreur", - "externalInstallationMsg": "", + "externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.", "externalInstallationTitle": "Extension requise", "goToStore": "Rendez-vous sur notre boutique en ligne", "gracefulShutdown": "Notre service est actuellement hors service pour l'entretien. Veuillez réessayer plus tard.", "IamHost": "Je suis l'hôte", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Mot de passe incorrect", "incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect", "inlineInstallationMsg": "Vous devez installer notre extension de partage de bureau.", "inlineInstallExtension": "Installer maintenant", @@ -178,14 +184,14 @@ "kickParticipantDialog": "Êtes-vous certain de vouloir expulser ce participant?", "kickParticipantTitle": "Expulser ce membre?", "kickTitle": "Expulsé de la réunion", - "liveStreaming": "", + "liveStreaming": "Diffusion en direct", "liveStreamingDisabledForGuestTooltip": "Les invités ne peuvent pas démarrer la diffusion en direct.", "liveStreamingDisabledTooltip": "Démarrage de la diffusion en direct désactivé.", "lockMessage": "Échec du verrouillage de la conférence.", "lockRoom": "Ajouter un mot de passe à la réunion", "lockTitle": "Échec du verrouillage", "logoutQuestion": "Êtes-vous certain de vouloir vous déconnecter et arrêter la conférence?", - "logoutTitle": "", + "logoutTitle": "Déconnexion", "maxUsersLimitReached": "La limite du nombre maximum de membres a été atteinte. La conférence est pleine. Veuillez communiquer avec l'hôte de la réunion ou réessayer plus tard.", "maxUsersLimitReachedTitle": "Limite du nombre de membres maximum atteinte", "micConstraintFailedError": "Votre micro ne répond pas à certaines exigences", @@ -195,7 +201,7 @@ "micPermissionDeniedError": "Vous n'avez pas accordé l'autorisation d'utilisation de votre micro. Vous pouvez toujours rejoindre la conférence, mais les autres membres ne pourront pas vous entendre. Utilisez le bouton de caméra dans la barre d'adresse pour remédier à cela.", "micUnknownError": "Impossible d'utiliser le micro pour une raison inconnue.", "muteParticipantBody": "Vous ne pourrez pas réactiver leur micro, mais ils peuvent le réactiver eux-mêmes à tout moment.", - "muteParticipantButton": "", + "muteParticipantButton": "Discrétion", "muteParticipantDialog": "Êtes-vous certain de vouloir désactiver le micro de ce participant? Vous ne pourrez pas le réactiver, mais il peut le réactiver lui-même à tout moment.", "muteParticipantTitle": "Désactiver le micro de ce membre?", "Ok": "OK", @@ -205,7 +211,7 @@ "passwordRequired": "Mot de passe requis", "popupError": "Votre navigateur bloque les fenêtres surgissantes provenant de ce site. Veuillez activer les fenêtres surgissantes dans les paramètres de sécurité de votre navigateur et réessayer.", "popupErrorTitle": "Fenêtre surgissante bloquée", - "recording": "", + "recording": "Enregistrement", "recordingDisabledForGuestTooltip": "Les invités ne peuvent pas démarrer l'enregistrement.", "recordingDisabledTooltip": "Démarrage de l'enregistrement désactivé.", "rejoinNow": "Rejoindre maintenant", @@ -228,9 +234,13 @@ "screenSharingFirefoxPermissionDeniedError": "Une erreur s'est produite lors de la tentative de partage d'écran. Veuillez vous assurer d'avoir donné votre autorisation. ", "screenSharingFirefoxPermissionDeniedTitle": "Oups! Il est impossible de démarrer le partage d'écran!", "screenSharingPermissionDeniedError": "Oups! Une erreur s'est produite avec les autorisations de l'extension de partage d'écran. Veuillez recharger et réessayer.", + "sendPrivateMessage": "Vous avez récemment reçu un message privé. Aviez-vous l'intention d'y répondre en privé, ou vouliez-vous envoyer votre message au groupe ?", + "sendPrivateMessageCancel": "Envoyer au groupe", + "sendPrivateMessageOk": "Envoyer en privé", + "sendPrivateMessageTitle": "Envoyer en privé ?", "serviceUnavailable": "Service non disponible", "sessTerminated": "Appel terminé", - "Share": "", + "Share": "Oui", "shareVideoLinkError": "Veuillez fournir un lien YouTube correct.", "shareVideoTitle": "Partager une vidéo", "shareYourScreen": "Partager votre écran", @@ -254,13 +264,16 @@ "userPassword": "mot de passe d'utilisateur", "WaitForHostMsg": "La conférence <b>{{room}}</b> n'a pas encore démarré. Si vous êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre que l'hôte arrive.", "WaitForHostMsgWOk": "La conférence <b>{{room}}</b> n'a pas encore démarré. Si vous êtes l'hôte, veuillez appuyer sur OK pour vous authentifier. Sinon, veuillez attendre que l'hôte arrive.", - "WaitingForHost": "En attente de l'hôte...", - "Yes": "", + "WaitingForHost": "En attente de l'hôte…", + "Yes": "Oui", "yourEntireScreen": "Votre écran entier" }, "dialOut": { "statusMessage": "est maintenant {{status}}" }, + "documentSharing": { + "title": "Document partagé" + }, "feedback": { "average": "Moyenne", "bad": "Mauvaise", @@ -291,15 +304,15 @@ "dialInTollFree": "Sans frais", "genericError": "Oups, une erreur s'est produite.", "inviteLiveStream": "Pour voir la diffusion en directe de cette réunion, cliquez sur ce lien : {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", + "invitePhone": "Pour rejoindre depuis un téléphone, composez : {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Vous cherchez un numéro d'appel différent ?\nConsultez la liste de numéros d'appel de la réunion : {{url}}\n\n\nSi vous appelez également via un téléphone de salle, vous pouvez vous connecter sans audio : {{silentUrl}}", "inviteURLFirstPartGeneral": "Vous avez été invité à rejoindre une réunion.", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartPersonal": "{{name}} vous invite à une réunion.\n", + "inviteURLSecondPart": "\nRejoindre la réunion:\n{{url}}\n", "liveStreamURL": "Diffusion en direct :", "moreNumbers": "Plus de numéros", "noNumbers": "Aucun numéro d'appel interne.", - "noPassword": "", + "noPassword": "Aucun", "noRoom": "Vous n'avez pas précisé de salle pour l'appel interne.", "numbers": "Numéros d'appel interne", "password": "Mot de passe :", @@ -313,7 +326,7 @@ "searchCallOnlyPlaceholder": "Entrer le numéro de téléphone", "searchPeopleOnlyPlaceholder": "Rechercher des participants", "searchPlaceholder": "Participant ou numéro de téléphone", - "send": "Envoyer..." + "send": "Envoyer…" }, "inlineDialogFailure": { "msg": "Nous avons rencontré un obstacle.", @@ -335,7 +348,8 @@ "toggleFilmstrip": "Afficher ou masquer les icônes vidéos", "toggleScreensharing": "Basculer entre la caméra et le partage d'écran", "toggleShortcuts": "Afficher ou masquer les raccourcis clavier", - "videoMute": "Démarrer ou arrêter votre caméra" + "videoMute": "Démarrer ou arrêter votre caméra", + "videoQuality": "Gérer la qualité d'appel" }, "liveStreaming": { "busy": "Libération de ressources de diffusion en cours. Veuillez réessayer dans quelques minutes.", @@ -349,18 +363,20 @@ "errorLiveStreamNotEnabled": "La diffusion en direct n'est pas activée pour {{email}}. Veuillez activer la diffusion en direct ou vous connecter à un compte pour lequel la diffusion en direct est activée.", "expandedOff": "La diffusion en direct a été arrêtée", "expandedOn": "La réunion est actuellement diffusée sur YouTube.", - "expandedPending": "Le démarrage de la diffusion en direct est en cours...", + "expandedPending": "Le démarrage de la diffusion en direct est en cours…", "failedToStart": "Le démarrage de la diffusion en direct a échoué", "getStreamKeyManually": "La récupération de diffusions en direct a échoué. Essayez d'obtenir une clé de diffusion en direct sur YouTube.", "invalidStreamKey": "La clé de diffusion en direct peut être erronée.", "off": "La diffusion en direct s'est arrêtée", + "offBy": "{{name}} a arrêté la diffusion en continu", "on": "Diffusion en direct", - "pending": "Démarrage de la diffusion en direct...", + "onBy": "{{name}} démarré la diffusion en continu", + "pending": "Démarrage de la diffusion en direct…", "serviceName": "Service de diffusion en direct", "signedInAs": "Vous êtes actuellement connecté en tant que :", "signIn": "Se connecter avec Google", "signInCTA": "Connectez-vous ou entrez votre clé de diffusion en direct de YouTube", - "signOut": "", + "signOut": "Se déconnecter", "start": "Démarrer une diffusion en direct", "streamIdHelp": "Qu'est-ce que c'est?", "unavailableTitle": "Diffusion en direct non disponible" @@ -378,14 +394,14 @@ "label": "LOR", "labelToolTip": "L'enregistrement local est en cours", "localRecording": "Enregistrement local", - "me": "", + "me": "Moi", "messages": { "engaged": "Enregistrement local activé.", "finished": "Enregistrement de la séance {{token}} terminé. Veuillez envoyer le fichier d'enregistrement au modérateur.", "finishedModerator": "Enregistrement de la séance {{token}} terminé. L'enregistrement de la piste locale a été enregistrée. Veuillez demander aux autres participants de soumettre leurs enregistrements.", "notModerator": "Vous n'êtes pas le modérateur. Vous ne pouvez pas démarrer ou arrêter l'enregistrement local." }, - "moderator": "", + "moderator": "Modérateur", "no": "Non", "participant": "Participant", "participantStats": "Statistiques de participants", @@ -394,8 +410,8 @@ "stop": "Arrêter l'enregistrement", "yes": "Oui" }, - "lockRoomPassword": "", - "lockRoomPasswordUppercase": "", + "lockRoomPassword": "mot de passe", + "lockRoomPasswordUppercase": "Mot de passe", "me": "moi", "notify": { "connectedOneMember": "{{name}} a rejoint la réunion", @@ -405,25 +421,25 @@ "focus": "Sujet de la conférence", "focusFail": "{{component}} non disponible; réessayez dans {{ms}} sec", "grantedTo": "Droits de modérateur accordés à {{to}}!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{displayName}} a été invité", + "invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités", + "invitedTwoMembers": "{{first}} et {{second}} ont été invités", + "kickParticipant": "{{kicked}} a été expulsé par {{kicker}}", "me": "Moi", "moderator": "Droits de modérateur accordés!", "muted": "Vous avez joint la conversation en sourdine.", "mutedTitle": "Vous êtes en sourdine!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyTitle": "Votre micro a été coupé par {{participantDisplayName}}!", + "mutedRemotelyDescription": "Vous pouvez toujours activer votre micro pour prendre la parole. Désactivez votre micro quand vous terminez pour éviter les bruits parasites.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) supprimé par un autre participant", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) défini par un autre participant", "raisedHand": "{{name}} voudrait parler.", "somebody": "Quelqu'un", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Euh... nous sommes désolés que vous expérience avec {{appName}} ne se déroule pas comme prévu. Nous cherchons à améliorer cela, mais en attendant, veuillez essayer d'utiliser un des <a href='static/recommendedBrowsers.html' target='_blank'>navigateurs pris en charge</a>.", + "startSilentTitle": "Vous avez rejoint sans sortie audio!", + "startSilentDescription": "Rejoignez la réunion de nouveau pour activer l'audio", + "suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit bonne ici. Nous cherchons des moyens d’améliorer cela, mais d’ici-là, essayez d’utiliser l’un des <a href='static/recommendedBrowsers.html' target='_blank'>navigateurs supportés</a>.", "suboptimalExperienceTitle": "Avertissement de navigateur", - "unmute": "", + "unmute": "Rétablir le son", "newDeviceCameraTitle": "Nouvelle caméra détectée", "newDeviceAudioTitle": "Nouveau dispositif audio détecté", "newDeviceAction": "Utiliser" @@ -433,14 +449,14 @@ "poweredby": "optimisé par", "presenceStatus": { "busy": "Occupé", - "calling": "Appel en cours...", + "calling": "Appel en cours…", "connected": "Connecté", - "connecting": "Connexion en cours...", + "connecting": "Connexion en cours…", "connecting2": "Connexion en cours*...", "disconnected": "Déconnecté", "expired": "Expiré", "ignored": "Ignoré", - "initializingCall": "Initialisation de l'appel...", + "initializingCall": "Initialisation de l'appel…", "invited": "Invité", "rejected": "Refusé", "ringing": "Sonnerie" @@ -448,9 +464,10 @@ "profile": { "setDisplayNameLabel": "Définir votre nom d'affichage", "setEmailInput": "Entrer votre adresse courriel", - "setEmailLabel": "Définir votre courriel gravatar", - "title": "" + "setEmailLabel": "Définir votre courriel Gravatar", + "title": "Profil" }, + "raisedHand": "Aimerait prendre la parole", "recording": { "authDropboxText": "Téléverser à Dropbox", "availableSpace": "Espace disponible : {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)", @@ -460,14 +477,16 @@ "error": "L'enregistrement a échoué. Veuillez réessayer.", "expandedOff": "L'enregistrement a été arrêté.", "expandedOn": "La réunion est actuellement enregistrée.", - "expandedPending": "Démarrage de l'enregistrement en cours...", + "expandedPending": "Démarrage de l'enregistrement en cours…", "failedToStart": "Échec du démarrage de l'enregistrement", "fileSharingdescription": "Partager l'enregistrement avec les participants de la réunion", "live": "EN DIRECT", "loggedIn": "Connecté en tant que {{userName}}", "off": "L'enregistrement est arrêté", + "offBy": "{{name}} a arrêté l'enregistrement", "on": "Enregistrement", - "pending": "Enregistrement de la réunion en préparation...", + "onBy": "{{name}} a démarré l'enregistrement", + "pending": "Enregistrement de la réunion en préparation…", "rec": "REC", "serviceDescription": "Votre enregistrement sera sauvegardé par le service d'enregistrement", "serviceName": "Service d'enregistrement", @@ -485,41 +504,45 @@ "disconnect": "Déconnexion", "microsoftSignIn": "Se connecter avec Microsoft", "signedIn": "Accès aux événements de votre agenda en cours pour {{email}}. Cliquez sur le bouton de déconnexion ci-dessous pour terminer l'accès aux événements d'agenda.", - "title": "" + "title": "Calendrier" }, "devices": "Dispositifs", "followMe": "Tous les participants me suivent", "language": "Language", "loggedIn": "Connecté en tant que {{name}}", - "moderator": "", + "moderator": "Modérateur", "more": "Plus", - "name": "", + "name": "Nom", "noDevice": "Aucun", "selectAudioOutput": "Sortie audio", "selectCamera": "Caméra", "selectMic": "Micro", "startAudioMuted": "Tous les participants débutent en sourdine", "startVideoMuted": "Tous les participants débutent masqués", - "title": "" + "title": "Paramètres" }, "settingsView": { + "advanced": "Avancé", "alertOk": "OK", "alertTitle": "Alerte", "alertURLText": "L'URL de serveur saisi n'est pas valide", "buildInfoSection": "Information de version", "conferenceSection": "Conférence", + "disableCallIntegration": "Désactiver l'intégration d'appels native", + "disableP2P": "Désactiver le mode pair à pair", "displayName": "Nom d'affichage", "email": "Courriel", - "header": "", + "header": "Paramètres", "profileSection": "Profil", "serverURL": "URL du serveur", + "showAdvanced": "Afficher les paramètres avancés", "startWithAudioMuted": "Démarrer avec l'audio en sourdine", "startWithVideoMuted": "Démarrer avec la vidéo en sourdine", "version": "Version" }, "share": { - "dialInfoText": "", - "mainText": "" + "dialInfoText": "\n\n=====\n\nVoulez-vous appeler depuis votre téléphone ?\n\n{{defaultDialInNumber}}Voici la liste des numéros d'appels pour cette réunion :\n{{dialInfoPageUrl}}", + "mainText": "Cliquez sur ce lien pour rejoindre la réunion :\n{{roomUrl}}" }, "speaker": "Intervenant", "speakerStats": { @@ -542,15 +565,17 @@ "toolbar": { "accessibilityLabel": { "audioOnly": "Basculement du mode audio uniquement", - "audioRoute": "", - "callQuality": "", + "audioRoute": "Sélectionner le dispositif audio", + "callQuality": "Gestion de la qualité d'appel", "cc": "Basculement des sous-titres", "chat": "Basculement de la fenêtre de clavardage", "document": "Basculement du document partagé", - "feedback": "", + "download": "", + "feedback": "Laisser un commentaire", "fullScreen": "Basculement de l'affichage plein écran", "hangup": "Quitter l'appel", - "invite": "", + "help": "", + "invite": "Inviter des personnes", "kick": "Expulser le participant", "localRecording": "Basculement des commandes d'enregistrement local", "lockRoom": "Basculement du mot de passe de la réunion", @@ -558,25 +583,26 @@ "moreActionsMenu": "Menu d'actions supplémentaires", "mute": "Basculement de la sourdine", "pip": "Basculement du mode image dans l'image", - "profile": "", + "privateMessage": "", + "profile": "Modifier votre profil", "raiseHand": "Basculement de la main levée", "recording": "Basculement de l'enregistrement", "remoteMute": "Mettre le participant en sourdine", "Settings": "Basculement des paramètres", "sharedvideo": "Basculement du partage de vidéo YouTube", - "shareRoom": "", + "shareRoom": "Inviter quelqu'un", "shareYourScreen": "Basculement du partage d'écran", "shortcuts": "Basculement des raccourcis", "show": "", "speakerStats": "Basculement des statistiques d'intervenant", - "tileView": "", - "toggleCamera": "", + "tileView": "Basculement de l'affichage mosaïque", + "toggleCamera": "Basculement de la caméra", "videomute": "Basculement de la sourdine vidéo", - "videoblur": "" + "videoblur": "Alterner le brouillage vidéo" }, "addPeople": "Ajouter des personnes à votre appel", - "audioOnlyOff": "Désactiver le mode audio uniquement", - "audioOnlyOn": "Activer le mode audio uniquement", + "audioOnlyOff": "Désactiver le mode bande passante faible", + "audioOnlyOn": "Activer le mode bande passante faible", "audioRoute": "Sélectionner le dispositif audio", "authenticate": "Authentification", "callQuality": "Gestion de la qualité d'appel", @@ -584,20 +610,26 @@ "closeChat": "Fermer le clavardage", "documentClose": "Fermer le document partagé", "documentOpen": "Ouvrir le document partagé", + "download": "Télécharger nos applications", "enterFullScreen": "Afficher le mode plein écran", "enterTileView": "Passer à l'affichage mosaïque", "exitFullScreen": "Quitter le mode plein écran", "exitTileView": "Quitter l'affichage mosaïque", "feedback": "Laisser un commentaire", "hangup": "Quitter", + "help": "Aide", "invite": "Inviter des personnes", "login": "Connexion", "logout": "Déconnexion", "lowerYourHand": "Abaisser votre main", "moreActions": "Plus d'actions", "mute": "Activer / Réactiver le son", + "noAudioSignalTitle": "Il n'y a pas d'entrée provenant de votre micro !", + "noAudioSignalDesc": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, envisagez de changer le périphérique.", + "noAudioSignalDescSuggestion": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, pensez à utiliser le périphérique suivant :", "openChat": "Ouvrir le clavardage", "pip": "Passer en mode image dans l'image", + "privateMessage": "Envoyer un message privé", "profile": "Modifier votre profil", "raiseHand": "Lever / Abaisser votre main", "raiseYourHand": "Lever votre main", @@ -615,8 +647,8 @@ "tileViewToggle": "Basculement de l'affichage mosaïque", "toggleCamera": "Basculement de la caméra", "videomute": "Démarrer / Arrêter la caméra", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Brouiller mon arrière plan", + "stopvideoblur": "Désactiver le brouillage d'arrière-plan" }, "transcribing": { "ccButtonTooltip": "Activer / Désactiver les sous-titres", @@ -625,20 +657,20 @@ "failedToStart": "Échec du démarrage de la transcription", "labelToolTip": "La réunion est transcrite", "off": "La transcription est arrêtée", - "pending": "Préparation de la transcription de la réunion en cours...", + "pending": "Préparation de la transcription de la réunion en cours…", "start": "Activer l'affichage des sous-titres", "stop": "Désactiver l'affichage des sous-titres", "tr": "PI" }, "userMedia": { - "androidGrantPermissions": "", - "chromeGrantPermissions": "", + "androidGrantPermissions": "Sélectionner <b><i>Autoriser</i></b> lorsque votre navigateur vous demande l'autorisation.", + "chromeGrantPermissions": "Sélectionner <b><i>Autoriser</i></b> lorsque votre navigateur vous demande l'autorisation.", "edgeGrantPermissions": "Sélectionner <b><i>Oui</i></b> lorsque votre navigateur demande l'autorisation.", - "electronGrantPermissions": "", + "electronGrantPermissions": "Veuillez accorder l'autorisation d'utiliser votre caméra et votre micro", "firefoxGrantPermissions": "Sélectionner <b><i>Partager le dispositif sélectionné</i></b> lorsque votre navigateur demande l'autorisation.", - "iexplorerGrantPermissions": "", + "iexplorerGrantPermissions": "Sélectionner <b><i>OK</i></b> lorsque votre navigateur demande l'autorisation.", "nwjsGrantPermissions": "Veuillez accorder l'autorisation d'utiliser votre caméra et votre micro", - "operaGrantPermissions": "", + "operaGrantPermissions": "Sélectionner <b><i>Autoriser</i></b> lorsque votre navigateur vous demande l'autorisation.", "react-nativeGrantPermissions": "Sélectionner <b><i>Autoriser</i></b> lorsque votre navigateur vous demande l'autorisation.", "safariGrantPermissions": "Sélectionner <b><i>OK</i></b> lorsque votre navigateur demande l'autorisation." }, @@ -654,13 +686,15 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "Vous êtes en mode audio uniquement. Ce mode économise de la bande passante, mais bloque les vidéos des autres.", + "audioOnlyExpanded": "Vous êtes en mode bande passante faible. Dans ce mode, vous ne recevrez que le partage audio et le partage d’écran.", "callQuality": "Qualité d'appel", "hd": "HD", + "hdTooltip": "Visionnement de vidéo en haute définition", "highDefinition": "Haute définition", "labelTooiltipNoVideo": "Aucune vidéo", - "labelTooltipAudioOnly": "Mode audio uniquement activé", + "labelTooltipAudioOnly": "Mode bande passante faible activé", "ld": "LD", + "ldTooltip": "Visionnement de vidéo en basse définition", "lowDefinition": "Basse définition", "onlyAudioAvailable": "Seulement l'audio est disponible", "onlyAudioSupported": "Ce navigateur prend seulement l'audio en charge.", @@ -668,6 +702,7 @@ "p2pVideoQualityDescription": "En mode pair à pair, il est possible de basculer entre la haute qualité d'appel entrant et l'audio seulement. Certains paramètres ne seront pas activés tant que vous ne quittez le mode pair à pair.", "recHighDefinitionOnly": "La haute définition est préférable.", "sd": "SD", + "sdTooltip": "Visionnement de vidéo en définition standard", "standardDefinition": "Définition standard" }, "videothumbnail": { @@ -678,13 +713,13 @@ "mute": "Le membre est en sourdine", "muted": "Discrétion", "remoteControl": "Contrôle à distance", - "show": "", + "show": "Afficher en premier plan", "videomute": "Le membre a arrêté la caméra" }, "welcomepage": { "accessibilityLabel": { "join": "Toucher pour rejoindre", - "roomname": "" + "roomname": "Entrer le nom de la salle" }, "appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement cryptée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.", "audioVideoSwitch": { @@ -693,20 +728,22 @@ }, "calendar": "Calendrier", "connectCalendarButton": "Connecter votre agenda", - "connectCalendarText": "", + "connectCalendarText": "Connectez-vous à votre calendrier pour afficher toutes les réunions {{app}}. Ajoutez également les réunions de {{provider}} à votre calendrier et démarrez-les d'un simple clic.", "enterRoomTitle": "Démarrer une nouvelle réunion", + "roomNameAllowedChars": "Le nom de la réunion ne doit contenir aucun de ces caractères : ?, &, :, ', \", %, #.", "go": "Commencer", - "join": "Rejoindre", + "goSmall": "Commencer", + "join": "CRÉER / REJOINDRE", "info": "Ret. arr.", "privacy": "Confidentialité", "recentList": "Récent", "recentListDelete": "Supprimer", "recentListEmpty": "Votre liste récente est actuellement vide. Clavardez avec votre équipe et vous y trouverez toutes vos réunions récentes.", - "reducedUIText": "", + "reducedUIText": "Bienvenue sur {{app}}!", "roomname": "Entrer le nom de la salle", "roomnameHint": "Entrez le nom ou l'URL de la salle que vous voulez rejoindre. Vous pouvez inventer un nom, mais assurez-vous de le partager avec les participants de la réunion pour qu'ils utilisent le même nom.", "sendFeedback": "Envoyer un commentaire", "terms": "Termes", "title": "Conférence vidéo sécurisée, pleinement fonctionnelle et entièrement gratuite" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hr.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hr.json index ab5774168..572acfae2 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hr.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hr.json @@ -116,7 +116,7 @@ "downloadApp": "Preuzmite aplikaciju", "launchWebButton": "Pokreni na webu", "openApp": "Nastavite do aplikacije", - "title": "Pokretanje sastanka u {{app}}...", + "title": "Pokretanje sastanka u {{app}}…", "tryAgainButton": "Pokušajte ponovo na radnoj površini" }, "defaultLink": "npr. {{url}}", @@ -150,9 +150,9 @@ "cameraUnsupportedResolutionError": "Fotoaparat ne podržava potrebnu razlučivost videozapisa.", "Cancel": "Odustani", "close": "Zatvori", - "conferenceDisconnectMsg": "Provjerite vašu mrežnu vezu. Ponovno spajanje za {{seconds}} sekundi...", + "conferenceDisconnectMsg": "Provjerite vašu mrežnu vezu. Ponovno spajanje za {{seconds}} sekundi…", "conferenceDisconnectTitle": "Odspojeni ste.", - "conferenceReloadMsg": "Pokušavamo popraviti. Ponovno spajanje za {{seconds}} sekundi...", + "conferenceReloadMsg": "Pokušavamo popraviti. Ponovno spajanje za {{seconds}} sekundi…", "conferenceReloadTitle": "Nažalost, nešto je pošlo po zlu.", "confirm": "Potvrdi", "confirmNo": "Ne", @@ -354,13 +354,13 @@ "errorLiveStreamNotEnabled": "Emitiranje uživo nije omogućeno na {{email}}. Omogućite prijenos uživo ili se prijavite na račun s omogućenim prijenosom uživo.", "expandedOff": "Emitiranje uživo je zaustavljeno", "expandedOn": "Ovaj sastanak se emitira uživo na YouTube.", - "expandedPending": "Emitiranje uživo se pokreće...", + "expandedPending": "Emitiranje uživo se pokreće…", "failedToStart": "Pokretanje emitiranja uživo nije uspjelo", "getStreamKeyManually": "Nismo mogli dohvatiti niti jedan prijenos uživo. Pokušajte dobiti ključ uživo iz usluge YouTube.", "invalidStreamKey": "Ključ za emitiranje uživo možda je pogrešan.", "off": "Emitiranje uživo je zaustavljeno", "on": "Emitiranje uživo", - "pending": "Pokretanje emitiranja uživo...", + "pending": "Pokretanje emitiranja uživo…", "serviceName": "Usluga emitiranja uživo", "signedInAs": "", "signIn": "", @@ -465,7 +465,7 @@ "error": "Emitiranje uživo nije uspjelo. Pokušajte ponovno.", "expandedOff": "", "expandedOn": "", - "expandedPending": "Snimanje se pokreće...", + "expandedPending": "Snimanje se pokreće…", "failedToStart": "", "fileSharingdescription": "", "live": "", @@ -714,4 +714,4 @@ "terms": "Uvjeti", "title": "Sigurna, potpuno opremljena i potpuno besplatna videokonferencija" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hu.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hu.json new file mode 100644 index 000000000..90dbb4e5d --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-hu.json @@ -0,0 +1,788 @@ +{ + "addPeople": { + "add": "Meghívás", + "countryNotSupported": "Ez a célállomás jelenleg nem támogatott.", + "countryReminder": "Ez USA-n kívüli hívás? Országhívószámmal kell kezdődnie!", + "disabled": "Nem hívhat meg senkit.", + "failedToAdd": "Nem sikerült a résztvevők hozzáadása", + "footerText": "A külső tárcsázás le van tiltva.", + "loading": "Személyek és telefonszámaik keresése", + "loadingNumber": "Telefonszám ellenőrzése", + "loadingPeople": "Meghívható személyek keresése", + "noResults": "Nincs találat a keresésre", + "noValidNumbers": "Adjon meg egy telefonszámot", + "searchNumbers": "Telefonszámok hozzáadása", + "searchPeople": "Személyek keresése", + "searchPeopleAndNumbers": "Személyek keresése vagy telefonszámaik hozzáadása", + "telephone": "Telefon: {{number}}", + "title": "Személyek meghívása az értekezletre" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Fejhallgató", + "phone": "Telefon", + "speaker": "Hangszóró", + "none": "Nincsenek elérhető hangeszközök" + }, + "audioOnly": { + "audioOnly": "Alacsony sávszélesség" + }, + "calendarSync": { + "addMeetingURL": "Értekezlet hivatkozásának hozzáadása", + "confirmAddLink": "Hozzáadható egy Jitsi hivatkozás az eseményhez?", + "error": { + "appConfiguration": "A naptárintegráció nincs helyesen konfigurálva.", + "generic": "Hiba történt. Ellenőrizze a naptár beállításait vagy próbálja frissíteni a naptárat.", + "notSignedIn": "Hiba történt a naptár eseményeihez való hozzáféréshez szükséges azonosítás során. Ellenőrizze a naptár beállításait és próbáljon meg újra belépni." + }, + "join": "Részvétel", + "joinTooltip": "Bekapcsolódás az értekezletbe", + "nextMeeting": "következő értekezlet", + "noEvents": "Nincs következő ütemezett értekezlet.", + "ongoingMeeting": "folyamatban lévő értekezlet", + "permissionButton": "Beállítások megnyitása", + "permissionMessage": "A naptár engedélyezése szükséges az értekezletek applikációban való megtekintéséhez.", + "refresh": "Naptár frissítése", + "today": "Ma" + }, + "chat": { + "error": "Hiba: az üzenetet nem sikerült elküldeni. Hiba oka: {{error}}", + "fieldPlaceHolder": "Írja ide az üzenetét", + "messagebox": "Írja be az üzenetet", + "messageTo": "Privát üzenet a felhasználónak: {{recipient}}", + "noMessagesMessage": "A találkozón még nincsenek üzenetek. Itt kezdhet beszélgetést!", + "nickname": { + "popover": "Becenév kiválasztása", + "title": "Adjon meg egy becenevet a csevegés számára" + }, + "privateNotice": "Privát üzenet a felhasználónak: {{recipient}}", + "title": "Csevegés", + "you": "neked" + }, + "connectingOverlay": { + "joiningRoom": "Kapcsolódás az értekezlethez…" + }, + "connection": { + "ATTACHED": "Kapcsolódva", + "AUTHENTICATING": "Azonosítás", + "AUTHFAIL": "Hitelesítés meghiúsult", + "CONNECTED": "Kapcsolódva", + "CONNECTING": "Kapcsolódás", + "CONNFAIL": "A kapcsolat meghiúsult", + "DISCONNECTED": "Szétkapcsolva", + "DISCONNECTING": "Szétkapcsolódás", + "ERROR": "Hiba", + "RECONNECTING": "Hálózati hiba történt. Újracsatlakozás...", + "LOW_BANDWIDTH": "{{displayName}} videója le lett kapcsolva a sávszélesség csökkentése érdekében", + "GOT_SESSION_ID": "Munkamenet-azonosító beszerzése… Kész", + "GET_SESSION_ID_ERROR": "Hiba a munkamenet-azonosítása beszerzése közben: {{code}}", + "FETCH_SESSION_ID": "Munkamenet-azonosító beszerzése…" + }, + "connectionindicator": { + "address": "Cím:", + "bandwidth": "Becsült sávszélesség:", + "bitrate": "Bitsebesség:", + "bridgeCount": "Kiszolgáló száma: ", + "connectedTo": "Kapcsolódva ehhez:", + "framerate": "Képkockasebesség:", + "less": "Kevesebb megjelenítése", + "localaddress": "Helyi cím:", + "localaddress_plural": "Helyi címek:", + "localport": "Helyi port:", + "localport_plural": "Helyi portok:", + "more": "Több megjelenítése", + "packetloss": "Csomagvesztés:", + "quality": { + "good": "Jó", + "inactive": "Inaktív", + "lost": "Elveszett", + "nonoptimal": "Nem optimális", + "poor": "Silány" + }, + "remoteaddress": "Távoli cím:", + "remoteaddress_plural": "Távoli címek:", + "remoteport": "Távoli port:", + "remoteport_plural": "Távoli portok:", + "resolution": "Felbontás:", + "status": "Kapcsolat:", + "transport": "Átvitel:", + "transport_plural": "Átvitelek:", + "e2e_rtt": "E2E RTT:" + }, + "dateUtils": { + "earlier": "Korábban", + "today": "Ma", + "yesterday": "Tegnap" + }, + "deepLinking": { + "appNotInstalled": "Szükség van a {{app}} mobilapplikációra az értekezlethez való telefonos csatlakozásra.", + "description": "Semmi sem történt? Megpróbáltuk az értekezletet a {{app}} asztali alkalmazásban elindítani. Próbálja ezt újra vagy a {{app}} webes applikációban indítani.", + "descriptionWithoutWeb": "Semmi sem történt? Megpróbáltuk az értekezletet a {{app}} asztali alkalmazásban elindítani.", + "downloadApp": "Applikáció letöltése", + "launchWebButton": "Indítás weben", + "openApp": "Továbblépés az applikációhoz", + "title": "Értekezlet indítása a {{app}} applikációban…", + "tryAgainButton": "Újbóli próbálkozás az asztali változatban" + }, + "defaultLink": "pl.: {{url}}", + "defaultNickname": "pl.: Minta Miklós", + "deviceError": { + "cameraError": "A kamerához való hozzáférés meghiúsult", + "cameraPermission": "Hiba történt a kamera engedélyeztetése során", + "microphoneError": "A mikrofonhoz való hozzáférés meghiúsult", + "microphonePermission": "Hiba történt a mikrofon engedélyeztetése során" + }, + "deviceSelection": { + "noPermission": "Nem lett engedélyezve", + "previewUnavailable": "Nincs előnézet", + "selectADevice": "Eszköz kijelölése", + "testAudio": "Teszthang lejátszása" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Élő közvetítés" + }, + "allow": "Engedélyez", + "alreadySharedVideoMsg": "Egy másik résztvevő is videót oszt meg. E konferencia számára csak egy videomegosztás engedélyezett.", + "alreadySharedVideoTitle": "Egyszerre csak egy videomegosztás engedélyezése", + "applicationWindow": "Alkalmazásablak", + "Back": "Vissza", + "cameraConstraintFailedError": "A kamera nem felel meg bizonyos kikötéseknek.", + "cameraNotFoundError": "Nem található kamera.", + "cameraNotSendingData": "Nem sikerült hozzáférni kamerához. Kérem, ellenőrizze, hogy egy másik alkalmazás nem használja-e a kamerát, vagy tessék egy másik eszköz kijelölni a beállítások menüben vagy esetleg érdemes újratölteni az applikációt.", + "cameraNotSendingDataTitle": "Nem lehet hozzáférni a kamerához", + "cameraPermissionDeniedError": "Nem adott engedélyt a kamera használatához. Csatlakozhat a beszélgetéshez, de a többiek nem fogják Önt látni. A címsorban lévő kamera ikonnal lehet ezt helyrehozni.", + "cameraUnknownError": "Ismeretlen ok miatt nem lehet a kamerát használni.", + "cameraUnsupportedResolutionError": "A kamera nem támogatja a szükséges videofelbontást.", + "Cancel": "Mégsem", + "close": "Bezárás", + "conferenceDisconnectMsg": "Ellenőrizni kellene a hálózati kapcsolatokat. Újracsatlakozás {{seconds}} másodperc múlva…", + "conferenceDisconnectTitle": "Lecsatlakozott.", + "conferenceReloadMsg": "Próbálkozás a hiba javítására. Újracsatlakozás {{seconds}} másodperc múlva…", + "conferenceReloadTitle": "Sajnos valami félresikerült.", + "confirm": "Megerősítés", + "confirmNo": "Nem", + "confirmYes": "Igen", + "connectError": "Hoppá! Valami elromlott és nem lehetett kapcsolódni a konferenciához.", + "connectErrorWithMsg": "Hoppá! Valami elromlott és nem lehetett kapcsolódni a konferenciához: {{msg}}", + "connecting": "Kapcsolódás", + "contactSupport": "Ügyfélszolgálat", + "copy": "Másolás", + "dismiss": "Elutasítás", + "displayNameRequired": "Helló! Mi a neve?", + "done": "Kész", + "enterDisplayName": "Adja meg itt a nevét", + "error": "Hiba", + "externalInstallationMsg": "Telepíteni kell a munkaasztal megosztására való kiterjesztést.", + "externalInstallationTitle": "Kiterjesztésre van szükség", + "goToStore": "Ugrás az alkalmazásbolthoz", + "gracefulShutdown": "Jelenleg a szolgáltatás karbantartás miatt nem elérhető. Később próbálja meg ismét.", + "IamHost": "Én vagyok a szervező", + "incorrectRoomLockPassword": "Helytelen jelszó", + "incorrectPassword": "Helytelen felhasználói név és jelszó", + "inlineInstallationMsg": "Telepíteni kell a munkaasztal megosztására való kiterjesztést.", + "inlineInstallExtension": "Telepítés azonnal", + "internalError": "Hoppá! Valami elromlott. Az alábbi hiba történt: {{error}}", + "internalErrorTitle": "Belső hiba", + "kickMessage": "További részletek kérhetők a {{participantDisplayName}} résztvevőtől.", + "kickParticipantButton": "Kirúgás", + "kickParticipantDialog": "Valóban kirúgható ez a résztvevő?", + "kickParticipantTitle": "Résztvevő kirúgása?", + "kickTitle": "Jaj! {{participantDisplayName}} kirúgta Önt az értekezletről", + "liveStreaming": "Élő közvetítés", + "liveStreamingDisabledForGuestTooltip": "A vendégek nem kezdhetnek élő közvetítést.", + "liveStreamingDisabledTooltip": "Az élő közvetítés indítása le van tiltva.", + "lockMessage": "A konferencia zárolása meghiúsult.", + "lockRoom": "Értekezlet $t(lockRoomPasswordUppercase) hozzáadása", + "lockTitle": "Zárolás meghiúsult", + "logoutQuestion": "Valóban ki akar lépni és leállítja a konferenciát?", + "logoutTitle": "Kilépés", + "maxUsersLimitReached": "A lehetséges résztvevők maximális száma elérve. A konferencia tele van. Lépjen kapcsolatba az értekezlet tulajdonosával vagy próbálkozzon később!", + "maxUsersLimitReachedTitle": "A lehetséges résztvevők maximális száma elérve", + "micConstraintFailedError": "A mikrofon nem felel meg bizonyos kikötéseknek.", + "micNotFoundError": "Nem található mikrofon.", + "micNotSendingData": "A számítógép beállításai között kell visszahangosítani a mikrofont vagy beállítani a hangfelvétel szintjét", + "micNotSendingDataTitle": "A mikrofon le van némítva a rendszerbeállításokban", + "micPermissionDeniedError": "Nem adott engedélyt a mikrofon használatához. Csatlakozhat a beszélgetéshez, de a többiek nem fogják Önt hallani. A címsorban lévő kamera ikonnal lehet ezt helyrehozni.", + "micUnknownError": "Ismeretlen ok miatt nem lehet a mikrofont használni.", + "muteParticipantBody": "Nem fogja tudni visszahangosítani, de ő önmagát bármikor vissza tudja majd hangosítani.", + "muteParticipantButton": "Némítás", + "muteParticipantDialog": "Valóban elnémítható ez a résztvevő? Nem fogja tudni visszahangosítani, de ő önmagát bármikor vissza tudja majd hangosítani.", + "muteParticipantTitle": "Elnémítható a résztvevő?", + "Ok": "Ok", + "passwordLabel": "Az értekezletet zárolta egy résztvevő. Csatlakozáshoz adja meg a $t(lockRoomPassword).", + "passwordNotSupported": "Az értekezlet $t(lockRoomPassword) beállítása nem támogatott.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nem támogatott", + "passwordRequired": "$t(lockRoomPasswordUppercase) szükséges", + "popupError": "A böngésző blokkolja a felugró ablakokat ezen az oldalon. Engedélyezze a böngésző biztonsági beállításaiban a felugró ablakokat és próbálja újra.", + "popupErrorTitle": "Felugró ablak blokkolva", + "recording": "Felvétel", + "recordingDisabledForGuestTooltip": "Vendég nem indíthat felvételt.", + "recordingDisabledTooltip": "Felvétel rögzítése letiltva.", + "rejoinNow": "Újracsatlakozás azonnal", + "remoteControlAllowedMessage": "{{user}} elfogadta az Ön távoli vezérlési kérését!", + "remoteControlDeniedMessage": "{{user}} visszautasította az Ön távoli vezérlési kérését!", + "remoteControlErrorMessage": "Hiba történt a résztvevőhöz {{user}} intézett távoli vezérlési kérés küldése közben!", + "remoteControlRequestMessage": "Engedélyezi {{user}} résztvevőnek, hogy távolról vezérelje a munkaasztalt?", + "remoteControlShareScreenWarning": "Vegye figyelembe, hogy ha megnyomja az „Engedélyezés” lehetőséget, akkor megosztja a képernyőt!", + "remoteControlStopMessage": "A távoli munkamenet irányítása befejeződött!", + "remoteControlTitle": "Távoli asztal vezérlése", + "Remove": "Eltávolítás", + "removePassword": "$t(lockRoomPassword) eltávolítása", + "removeSharedVideoMsg": "Valóban eltávolítható a megosztott videó?", + "removeSharedVideoTitle": "Megosztott videó eltávolítása", + "reservationError": "Foglalási rendszerhiba", + "reservationErrorMsg": "Hibakód: {{code}}, üzenet: {{msg}}", + "retry": "Újra", + "screenSharingFailedToInstall": "Hoppá! A képernyőmegosztó kiterjesztés telepítése meghiúsult.", + "screenSharingFailedToInstallTitle": "A képernyőmegosztó kiterjesztés telepítése meghiúsult", + "screenSharingFirefoxPermissionDeniedError": "Valami elromlott a képernyőmegosztás indítása közben. Ellenőrizze, hogy megadta-e a szükséges engedélyeket. ", + "screenSharingFirefoxPermissionDeniedTitle": "Hoppá! Nem sikerült a képernyőmegosztás indítása!", + "screenSharingPermissionDeniedError": "Hoppá! Valami elromlott a képernyőmegosztás kiterjesztés jogosultságaival. Töltse újra és kísérelje meg újra.", + "sendPrivateMessage": "Nemrég egy csak Önnek szóló üzenete érkezett. A válaszát privát üzenetként, vagy inkább az egész csoportnak akarja elküldeni?", + "sendPrivateMessageCancel": "Küldés a csoportnak", + "sendPrivateMessageOk": "Küldés privátban", + "sendPrivateMessageTitle": "Privátban legyen elküldve?", + "serviceUnavailable": "Szolgáltatás nem elérhető", + "sessTerminated": "Hívás megszakadt", + "Share": "Megosztás", + "shareVideoLinkError": "Adjon meg egy helyes YouTube linket.", + "shareVideoTitle": "Videó megosztása", + "shareYourScreen": "Képernyő megosztása", + "shareYourScreenDisabled": "Képernyőmegosztás letiltva.", + "shareYourScreenDisabledForGuest": "Vendég nem végezhet képernyőmegosztást.", + "startLiveStreaming": "Élő közvetítés kezdése", + "startRecording": "Felvétel indítása", + "startRemoteControlErrorMessage": "Hiba történt a távoli vezérlés munkamenetének indítása közben!", + "stopLiveStreaming": "Élő közvetítés leállítása", + "stopRecording": "Felvétel leállítása", + "stopRecordingWarning": "Valóban leállítható a felvétel?", + "stopStreamingWarning": "Valóban leállítható az élő közvetítés?", + "streamKey": "Élő közvetítés kulcsa", + "Submit": "Elküldés", + "thankYou": "Köszönjük a {{appName}} használatát!", + "token": "jelsor", + "tokenAuthFailed": "Sajnáljuk, a csatlakozása nem megengedett ehhez a híváshoz.", + "tokenAuthFailedTitle": "Hitelesítés meghiúsult", + "transcribing": "Átirat készítése", + "unlockRoom": "Értekezlet $t(lockRoomPassword) eltávolítása", + "userPassword": "felhasználói jelszó", + "WaitForHostMsg": "A <b>{{room}}</b> konferencia még nem kezdődött meg. Ha Ön a házigazda, akkor hitelesítse magát. Ellenkező esetben, kérjük várjon a házigazda érkezésére.", + "WaitForHostMsgWOk": "A <b>{{room}}</b> konferencia még nem kezdődött meg. Ha Ön a házigazda, kérjük az „OK” gombra kattintva hitelesítse magát. Ellenkező esetben, kérjük várjon a házigazda érkezésére.", + "WaitingForHost": "Várakozás a házigazdára…", + "Yes": "Igen", + "yourEntireScreen": "A teljes képernyő", + "screenSharingAudio": "Hang megosztása", + "muteEveryoneStartMuted": "Mindenki elnémítva kezd ezután", + "muteEveryoneSelf": "önmagamat", + "muteEveryoneTitle": "Mindenki elnémítása?", + "muteEveryoneDialog": "Valóban mindenki elnémítható? Nem fogja tudni visszahangosítani, de ő önmagát bármikor vissza tudja majd hangosítani.", + "muteEveryoneElseTitle": "Mindenki elnémítása, kivéve: {{whom}}?", + "muteEveryoneElseDialog": "Némítás után már nem fogja tudni visszahangosítani, de ő önmagát bármikor vissza tudja hangosítani." + }, + "dialOut": { + "statusMessage": "jelenleg {{status}}" + }, + "documentSharing": { + "title": "Megosztott dokumentum" + }, + "feedback": { + "average": "Átlag", + "bad": "Rossz", + "detailsLabel": "Osszon meg velünk többet erről.", + "good": "Jó", + "rateExperience": "Értékeld az értekezlet élményét", + "veryBad": "Nagyon rossz", + "veryGood": "Nagyon jó" + }, + "incomingCall": { + "answer": "Válasz", + "audioCallTitle": "Bejövő hívás", + "decline": "Elutasítás", + "productLabel": "a Jitsi Meettől", + "videoCallTitle": "Bejövő videohívás" + }, + "info": { + "accessibilityLabel": "Információk megjelenítése", + "addPassword": "$t(lockRoomPassword) hozzáadása", + "cancelPassword": "$t(lockRoomPassword) törlése", + "conferenceURL": "Hivatkozás:", + "country": "Ország", + "dialANumber": "Az értekezlethez való csatlakozáshoz ezeket a számokat kell feltárcsázni és a PIN-kódot megadni.", + "dialInConferenceID": "PIN-kód:", + "dialInNotSupported": "Sajnáljuk, a tárcsázás jelenleg nem támogatott.", + "dialInNumber": "Betárcsázás:", + "dialInSummaryError": "Hiba a betárcsázási információk lekérdezése közben. Később érdemes újra próbálkozni.", + "dialInTollFree": "Ingyenes", + "genericError": "Hoppá, valami elromlott.", + "inviteLiveStream": "Az értekezlet élő közvetítésének megtekintése: {{url}}", + "invitePhone": "Telefonról való csatlakozáshoz ezeket kell tárcsázni: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Más betárcsázási számot keres?\nLásd az értekezlet betárcsázási számait: {{url}}\n\n\nHa termi telefonon keresztül tárcsáz, csatlakozhat hang nélkül is: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Önt meghívták egy értekezletre.", + "inviteURLFirstPartPersonal": "{{name}} meghívta Önt egy értekezletre.\n", + "inviteURLSecondPart": "\nCsatlakozás az értekezlethez:\n{{url}}\n", + "liveStreamURL": "Élő közvetítés:", + "moreNumbers": "További számok", + "noNumbers": "Nincsenek betárcsázási számok.", + "noPassword": "Nincs", + "noRoom": "Nem volt szoba megadva, amelybe be lehetne lépni.", + "numbers": "Betárcsázási számok", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Megosztás", + "tooltip": "Az értekezlet hivatkozásának és behívó információinak megosztása", + "label": "Értekezlet információi" + }, + "inviteDialog": { + "alertText": "Néhány résztvevő meghívása meghiúsult.", + "header": "Meghívás", + "searchCallOnlyPlaceholder": "Telefonszám megadása", + "searchPeopleOnlyPlaceholder": "Résztvevők keresése", + "searchPlaceholder": "Résztvevő vagy telefonszám", + "send": "Küldés" + }, + "inlineDialogFailure": { + "msg": "Hiba történt.", + "retry": "Újrapróbálkozás", + "support": "Támogatás", + "supportMsg": "Ismételt hiba esetén érdemes kapcsolatba lépnie" + }, + "keyboardShortcuts": { + "focusLocal": "Fókusz a saját videómra", + "focusRemote": "Fókusz egy másik személy videójára", + "fullScreen": "Belépés vagy kilépés a teljes képernyőből", + "keyboardShortcuts": "Gyorsbillentyűk", + "localRecording": "Helyi rögzítési vezérlőelemek megjelenítése vagy elrejtése", + "mute": "Mikrofon némítása vagy visszahangosítása", + "pushToTalk": "Nyomd, hogy beszélj (adó-vevő)", + "raiseHand": "Kéz felemelése vagy leengedése", + "showSpeakerStats": "Beszéd statisztikák megjelenítése", + "toggleChat": "Csevegés megnyitása vagy bezárása", + "toggleFilmstrip": "Videó bélyegképek megjelenítése vagy elrejtése", + "toggleScreensharing": "Váltás kamera és képernyőmegosztás között", + "toggleShortcuts": "Gyorsbillentyűk megjelenítése vagy elrejtése", + "videoMute": "Kamera elindítása vagy leállítása", + "videoQuality": "Hívás minőségének kezelése" + }, + "liveStreaming": { + "busy": "Dolgozunk a közvetítési erőforrások felszabadításán. Kísérelje meg újra néhány perc múlva.", + "busyTitle": "Jelenleg minden közvetítő foglalt", + "changeSignIn": "Fiók váltása.", + "choose": "Élő közvetítés kijelölése", + "chooseCTA": "Válasszon egy közvetítési opciót. Ön jelenleg {{email}} címmel van belépve.", + "enterStreamKey": "Adja meg itt a YouTube élő közvetítési kulcsot.", + "error": "Az élő közvetítés meghiúsult. Próbálja újra.", + "errorAPI": "Hiba történt a YouTube adás elérése közben. Próbáljon meg újra belépni.", + "errorLiveStreamNotEnabled": "Az élő közvetítés nincs engedélyezve ezzel az email címmel: {{email}}. Engedélyezze az élő közvetítést vagy lépjen be egy másik felhasználóval, ahol az engedélyezve van.", + "expandedOff": "Az élő közvetítés leállt", + "expandedOn": "Az értekezlet jelenleg közvetítve van a YouTube-on.", + "expandedPending": "Az élvő közvetítés elindult…", + "failedToStart": "Az élvő közvetítés indítása meghiúsult", + "getStreamKeyManually": "Nem lehet semmilyen élő közvetítést letölteni. Az élő közvetítéshez szükséges kulcsot a Youtube-tól lehet beszerezni.", + "invalidStreamKey": "Az élő közvetítési kulcs valószínűleg hibás.", + "off": "Élő közvetítés leállt", + "offBy": "{{name}} leállította az élő közvetítést", + "on": "Élő közvetítés", + "onBy": "{{name}} elindította az élő közvetítést", + "pending": "Élő közvetítés kezdése…", + "serviceName": "Élő közvetítési szolgáltatás", + "signedInAs": "Jelenleg bejelentkezve mint:", + "signIn": "Bejelentkezés a Google-lel", + "signInCTA": "Jelentkezzen be vagy adja meg a élő közvetítési kulcsot a YouTube-ról.", + "signOut": "Kilépés", + "start": "Élő közvetítés kezdése", + "streamIdHelp": "Mi ez?", + "unavailableTitle": "Élő közvetítés elérhetetlen", + "googlePrivacyPolicy": "Google adatvédelmi irányelvek", + "youtubeTerms": "YouTube szolgáltatási feltételek" + }, + "localRecording": { + "clientState": { + "off": "Kikapcsolva", + "on": "Bekapcsolva", + "unknown": "Ismeretlen" + }, + "dialogTitle": "Helyi felvétel vezérlőelemei", + "duration": "Időtartam", + "durationNA": "N/A", + "encoding": "Kódolás", + "label": "HeF", + "labelToolTip": "Helyi felvétel aktiválva", + "localRecording": "Helyi felvétel", + "me": "Én", + "messages": { + "engaged": "Helyi felvétel aktiválva.", + "finished": "A {{token}} rögzítési munkamenet befejeződött. Küldje el a rögzített fájlt a moderátornak.", + "finishedModerator": "A {{token}} rögzítési munkamenet befejeződött. A helyi sáv rögzítésre került. Kérje el a többi résztvevőtől, amit rögzítettek.", + "notModerator": "Ön nem moderátor. Nem tudja elindítani vagy leállítani a helyi felvételt." + }, + "moderator": "Moderátor", + "no": "Nem", + "participant": "Résztvevő", + "participantStats": "Résztvevői statisztika", + "sessionToken": "Munkamenet jelsor", + "start": "Felvétel indítása", + "stop": "Felvétel leállítása", + "yes": "Igen" + }, + "lockRoomPassword": "jelszó", + "lockRoomPasswordUppercase": "Jelszó", + "me": "én", + "notify": { + "connectedOneMember": "{{name}} bekapcsolódott az értekezletbe", + "connectedThreePlusMembers": "{{name}} és {{count}} másik résztvevő kapcsolódott az értekezlethez", + "connectedTwoMembers": "{{first}} és {{second}} bekapcsolódott az értekezletbe", + "disconnected": "szétkapcsolva", + "focus": "Konferencia fókusza", + "focusFail": "{{component}} nem elérhető – újrapróbálkozás {{ms}} másodperc múlva", + "grantedTo": "Moderátori jogok biztosítva {{to}} számára!", + "invitedOneMember": "{{name}} meg lett hívva", + "invitedThreePlusMembers": "{{name}} és {{count}} másik felhasználó meg lett hívva", + "invitedTwoMembers": "{{first}} és {{second}} lett meghívva", + "kickParticipant": "{{kicked}} résztvevőt kirúgta {{kicker}}", + "me": "Én", + "moderator": "Moderátori jogok biztosítva!", + "muted": "A beszélgetést elnémítva kezdte meg.", + "mutedTitle": "Le lett némítva!", + "mutedRemotelyTitle": "{{participantDisplayName}} elnémította Önt!", + "mutedRemotelyDescription": "Bármikor visszahangosíthatja magát, ha készen áll a beszédre. Némítsa le magát ismét, ha a felesleges zajoktól meg kívánja védeni az értekezletet.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) egy másik résztvevő által eltávolítva", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) egy másik résztvevő által beállítva", + "raisedHand": "{{name}} beszélni szeretne.", + "somebody": "Valaki", + "startSilentTitle": "Hang nélkül csatlakozott!", + "startSilentDescription": "Csatlakozzon újra a hang engedélyezéséhez", + "suboptimalBrowserWarning": "Sajnáljuk, de nem nagy élmény így csatlakozni a konferenciához. Keressük a megoldást, de addig is, érdemes inkább egy <a href='static/recommendedBrowsers.html' target='_blank'>teljesen támogatott böngészővel</a> csatlakozni.", + "suboptimalExperienceTitle": "Böngészőhiba", + "unmute": "Visszahangosítás", + "newDeviceCameraTitle": "Új kamera észlelve", + "newDeviceAudioTitle": "Új hangeszköz észlelve", + "newDeviceAction": "Alkalmaz" + }, + "passwordSetRemotely": "egy másik résztvevő által beállítva", + "passwordDigitsOnly": "Legfeljebb {{number}} szám", + "poweredby": "Működteti a", + "presenceStatus": { + "busy": "Foglalt", + "calling": "Hívás…", + "connected": "Kapcsolódva", + "connecting": "Kapcsolódás…", + "connecting2": "Kapcsolódás*...", + "disconnected": "Szétkapcsolva", + "expired": "Lejárt", + "ignored": "Figyelmen kívül hagyva", + "initializingCall": "Hívás kezdeményezése…", + "invited": "Meghívva", + "rejected": "Elutasítva", + "ringing": "Csörgetés…" + }, + "profile": { + "setDisplayNameLabel": "Állítsa be a megjelenő nevet", + "setEmailInput": "Adjon meg egy e-mail címet", + "setEmailLabel": "Adja meg a gravatar e-mail címet", + "title": "Profil" + }, + "raisedHand": "Beszélni szeretnék", + "recording": { + "authDropboxText": "Feltöltés Dropboxra", + "availableSpace": "Elérhető hely: {{spaceLeft}} MB (körülbelül {{duration}} perc felvétel)", + "beta": "BÉTA", + "busy": "Dolgozunk a rögzítési erőforrások felszabadításán. Kísérelje meg újra néhány perc múlva.", + "busyTitle": "Jelenleg minden rögzítő foglalt", + "error": "A felvétel meghiúsult. Próbálja újra.", + "expandedOff": "A felvétel leállt", + "expandedOn": "Az értekezlet jelenleg rögzítés alatt áll.", + "expandedPending": "A felvétel elindult…", + "failedToStart": "A felvétel indítása meghiúsult", + "fileSharingdescription": "Felvétel megosztása az értekezlet résztvevőivel", + "live": "ÉLŐ", + "loggedIn": "Belépve mint {{userName}}", + "off": "Felvétel leállítva", + "offBy": "{{name}} leállította a felvételt", + "on": "Felvétel", + "onBy": "{{name}} elindította a felvételt", + "pending": "Értekezlet rögzítésének előkészítése…", + "rec": "REC", + "serviceDescription": "A felvételt a rögzítési szolgáltatás veszi fel", + "serviceName": "Felvétel szolgáltatás", + "signIn": "Belépés", + "signOut": "Kilépés", + "unavailable": "Hoppá! A {{serviceName}} szolgáltatás jelenleg nem elérhető. Dolgozunk a hiba elhárításán. Próbálja meg később.", + "unavailableTitle": "Felvétel nem elérhető" + }, + "sectionList": { + "pullToRefresh": "Húzás a frissítéshez" + }, + "settings": { + "calendar": { + "about": "A {{appName}} naptárintegráció a naptár biztonságos elérésére szolgál, így olvasni tudja a soron következő eseményeket.", + "disconnect": "Szétkapcsolás", + "microsoftSignIn": "Bejelentkezés a Microsofttal", + "signedIn": "Jelenleg ehhez az címhez tartozó naptár eseményei érhetőek el: {{email}}. Alább a „szétkapcsolás” gombra kattintva lehet leállítani a naptár eseményeinek elérését.", + "title": "Naptár" + }, + "devices": "Eszközök", + "followMe": "Mindenki engem kövessen", + "language": "Nyelv", + "loggedIn": "Belépve mint {{name}}", + "moderator": "Moderátor", + "more": "Továbbiak", + "name": "Név", + "noDevice": "Nincs", + "selectAudioOutput": "Hangkimenet", + "selectCamera": "Kamera", + "selectMic": "Mikrofon", + "startAudioMuted": "Mindenki elnémítva kezd", + "startVideoMuted": "Mindenki videó nélkül kezd", + "title": "Beállítások", + "speakers": "Hangszórók", + "microphones": "Mikrofonok" + }, + "settingsView": { + "advanced": "Haladó", + "alertOk": "OK", + "alertTitle": "Figyelmeztetés", + "alertURLText": "A megadott kiszolgáló URL-je érvénytelen", + "buildInfoSection": "Fordítási információk", + "conferenceSection": "Konferencia", + "disableCallIntegration": "A natív hívások integrációjának letiltása", + "disableP2P": "Kapcsolja ki a peer-to-peer módot", + "displayName": "Megjelenő név", + "email": "E-mail", + "header": "Beállítások", + "profileSection": "Profil", + "serverURL": "Kiszolgáló URL", + "showAdvanced": "Speciális beállítások megjelenítése", + "startWithAudioMuted": "Kezdés a hang elnémításával", + "startWithVideoMuted": "Kezdés a videó letiltásával", + "version": "Verzió" + }, + "share": { + "dialInfoText": "\n\n=====\n\nA saját telefonján szeretne betárcsázni?\n\n{{defaultDialInNumber}}Az értekezlet betárcsázási száma erre a hivatkozásra kattintva tekinthető meg\n{{dialInfoPageUrl}}", + "mainText": "Az alábbi hivatkozásra kattintva lehet csatlakozni az értkezlethez:\n{{roomUrl}}" + }, + "speaker": "Hangszóró", + "speakerStats": { + "hours": "{{count}} h", + "minutes": "{{count}} perc", + "name": "Név", + "seconds": "{{count}} mp", + "speakerStats": "Beszélő statisztika", + "speakerTime": "Beszélő ideje" + }, + "startupoverlay": { + "policyText": " ", + "title": "A {{app}} használni szeretné a mikrofont és a kamerát." + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Újracsatlakozás", + "text": "Az <i>újracsatlakozás</i> gombbal lehet ismételten csatlakozni.", + "title": "A videohívás megszakadt, mivel ez a számítógép aló állapotba helyezte magát." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Csak a hang átváltása", + "audioRoute": "Hangeszköz kijelölése", + "callQuality": "Videóminőség kezelése", + "cc": "Feliratok átváltása", + "chat": "Csevegés ablak átváltása", + "document": "Megosztott dokumentum átváltása", + "download": "Alkalmazás letöltése", + "feedback": "Visszajelzés küldése", + "fullScreen": "Teljes képernyő átváltása", + "hangup": "Beszélgetés elhagyása", + "help": "Súgó", + "invite": "Személyek meghívása", + "kick": "Résztvevő kirúgása", + "localRecording": "Helyi felvétel vezérlőelemeinek átváltása", + "lockRoom": "Értekezlet jelszavának átváltása", + "moreActions": "További műveltek menü átváltása", + "moreActionsMenu": "További műveltek menü", + "mute": "Hang némításának átváltása", + "pip": "Kép és képben mód átváltása", + "privateMessage": "Privát üzenet küldése", + "profile": "Adja meg a profilját", + "raiseHand": "Kéz felemelésének átváltása", + "recording": "Felvétel átváltása", + "remoteMute": "Résztvevők némítása", + "Settings": "Beállítások átváltása", + "sharedvideo": "YouTube videó megosztásának átváltása", + "shareRoom": "Valaki meghívása", + "shareYourScreen": "Képernyőmegosztás átváltása", + "shortcuts": "Gyorsbillentyűk átváltása", + "show": "Megjelenítés a színpadon", + "speakerStats": "Beszélő statisztika átváltása", + "tileView": "Mozaikos nézet átváltása", + "toggleCamera": "Kamera átváltása", + "videomute": "Videó letiltásának átváltása", + "videoblur": "Videoelmosás átváltása", + "toggleFilmstrip": "Filmszalag átváltása", + "muteEveryone": "Mindenki elnémítása", + "moreOptions": "További beállítások megjelenítése" + }, + "addPeople": "Személy hozzáadása a híváshoz", + "audioOnlyOff": "Alacsony sávszélességű mód letiltása", + "audioOnlyOn": "Alacsony sávszélességű mód engedélyezése", + "audioRoute": "Hangeszköz kijelölése", + "authenticate": "Hitelesítés", + "callQuality": "Videominőség kezelése", + "chat": "Csevegés megnyitása / bezárása", + "closeChat": "Csevegés bezárása", + "documentClose": "Megosztott dokumentum bezárása", + "documentOpen": "Megosztott dokumentum megnyitása", + "download": "Alkalmazás letöltése", + "enterFullScreen": "Teljes képernyős megtekintés", + "enterTileView": "Mozaikos nézet indítása", + "exitFullScreen": "Kilépés a teljes képernyőből", + "exitTileView": "Kilépés a mozaikos nézetből", + "feedback": "Visszajelzés küldése", + "hangup": "Kilépés", + "help": "Súgó", + "invite": "Személyek meghívása", + "login": "Bejelentkezés", + "logout": "Kijelentkezés", + "lowerYourHand": "Kéz leengedése", + "moreActions": "További műveltek", + "mute": "Némítás / Visszahangosítás", + "noAudioSignalTitle": "Nincs bemenet a mikrofonjáról!", + "noAudioSignalDesc": "Ha nem szándékosan némította el a rendszerbeállításokban vagy az eszközön, akkor fontolja meg az eszköz cseréjét.", + "noAudioSignalDescSuggestion": "Ha nem szándékosan némította el a rendszerbeállításokban vagy az eszközön, akkor fontolja meg a következő eszköz használatát.", + "openChat": "Csevegés megnyitása", + "pip": "Belépés kép a képben módba", + "privateMessage": "Privát üzenet küldése", + "profile": "Adja meg a profilját", + "raiseHand": "Kéz felemelése / leengedése", + "raiseYourHand": "Kéz felemelése", + "Settings": "Beállítások", + "sharedvideo": "YouTube videó megosztása", + "shareRoom": "Valaki meghívása", + "shortcuts": "Gyorsbillentyűk megtekintése", + "speakerStats": "Beszélő statisztika", + "startScreenSharing": "Képernyőmegosztás kezdése", + "startSubtitles": "Feliratok kezdése", + "stopScreenSharing": "Képernyőmegosztás leállítása", + "stopSubtitles": "Felirat leállítása", + "stopSharedVideo": "YouTube videó leállítása", + "talkWhileMutedPopup": "Úgy tűnik beszélni szeretne, de le van némítva.", + "tileViewToggle": "Mozaikos nézet átváltása", + "toggleCamera": "Kamera átváltása", + "videomute": "Kamera indítása / leállítása", + "startvideoblur": "Háttér elhomályosítása", + "stopvideoblur": "Háttér elhomályosításának letiltása", + "noisyAudioInputDesc": "Úgy tűnik, hogy ez a mikrofon zajos. Le kellene némítani vagy cserélni az eszközt.", + "noisyAudioInputTitle": "Zajosnak tűnik a mikrofonja!", + "noAudioSignalDialInLinkDesc": "Betárcsázási számok", + "noAudioSignalDialInDesc": "Be is tárcsázhat:", + "muteEveryone": "Mindenki elnémítása", + "moreOptions": "További beállítások" + }, + "transcribing": { + "ccButtonTooltip": "Feliratok indítása / leállítása", + "error": "Átírat meghiúsult. Próbálja meg ismét.", + "expandedLabel": "Az átirat jelenleg be van kapcsolva", + "failedToStart": "Meghiúsult az átirat kezdése", + "labelToolTip": "Az értekezlet átirata elkezdődött", + "off": "Az átirat készítése le lett állítva", + "pending": "Előkészület az értekezlet átiratának készítésére…", + "start": "Felirat megjelenítésének kezdése", + "stop": "Felirat megjelenítésének befejezése", + "tr": "Átirat" + }, + "userMedia": { + "androidGrantPermissions": "Válaszd az <b><i>Engedélyezés</i></b> opciót, ha a böngésző engedélyt kér.", + "chromeGrantPermissions": "Válaszd az <b><i>Engedélyezés</i></b> opciót, ha a böngésző engedélyt kér.", + "edgeGrantPermissions": "Válaszd az <b><i>Igen</i></b> opciót, ha a böngésző hozzáférést kér.", + "electronGrantPermissions": "A kamera és a mikrofon használatát engedélyezni kell", + "firefoxGrantPermissions": "Válaszd <b><i>Kiválasztott eszköz megosztása</i></b> opciót, ha a böngésző hozzáférést kér.", + "iexplorerGrantPermissions": "Válaszd az <b><i>OK</i></b> opciót, ha a böngésző engedélyezést kér.", + "nwjsGrantPermissions": "A kamera és a mikrofon használatát engedélyezni kell", + "operaGrantPermissions": "Válaszd az <b><i>Engedélyezés</i></b> opciót, ha a böngésző engedélyt kér.", + "react-nativeGrantPermissions": "Válaszd az <b><i>Engedélyezés</i></b> opciót, ha a böngésző engedélyt kér.", + "safariGrantPermissions": "Válaszd az <b><i>OK</i></b> opciót, ha a böngésző engedélyezést kér." + }, + "videoSIPGW": { + "busy": "Dolgozunk az erőforrások felszabadításán. Kísérelje meg újra néhány perc múlva.", + "busyTitle": "A szoba szolgáltatás jelenleg foglalt", + "errorAlreadyInvited": "{{displayName}} -t már meghívták", + "errorInvite": "A konferencia még nem lett megalakítva. Később próbálja újra.", + "errorInviteFailed": "Dolgozunk a hiba elhárításán. Próbálja meg később.", + "errorInviteFailedTitle": "{{displayName}} meghívása meghiúsult", + "errorInviteTitle": "Hiba a meghívó szobával", + "pending": "{{displayName}} -t meghívta" + }, + "videoStatus": { + "audioOnly": "CsH", + "audioOnlyExpanded": "Jelenleg az alacsony sávszélességű mód az aktív, vagyis csak hangot lehet fogadni és képernyőmegosztást.", + "callQuality": "Videominőség", + "hd": "MF", + "hdTooltip": "Magas felbontású videó megtekintése", + "highDefinition": "Magas felbontású", + "labelTooiltipNoVideo": "Nincs videó", + "labelTooltipAudioOnly": "Alacsony sávszélességű mód aktiválva", + "ld": "AF", + "ldTooltip": "Alacsony felbontású videó", + "lowDefinition": "Alacsony felbontású", + "onlyAudioAvailable": "„Csak hang” mód elérhető", + "onlyAudioSupported": "Csak a hang támogatott ebben a böngészőben.", + "p2pEnabled": "Egyenrangú kapcsolat engedélyezve", + "p2pVideoQualityDescription": "Az egyenrangú (peer to peer) módban a kapott videominőség csak a magas felbontású és a „csak hang” mód között váltható. A további beállítások nem lesznek figyelembe véve az egyenrangú kapcsolat alatt.", + "recHighDefinitionOnly": "Lehetőleg magas felbontású.", + "sd": "SF", + "sdTooltip": "Szabványos felbontású videó megtekintése", + "standardDefinition": "Szabványos felbontású" + }, + "videothumbnail": { + "domute": "Némítás", + "flip": "Tükrözés", + "kick": "Kirúgás", + "moderator": "Moderátor", + "mute": "A résztvevő le van némítva", + "muted": "Némítva", + "remoteControl": "Távoli vezérlés", + "show": "Megjelenítés a színpadon", + "videomute": "A résztvevő leállította a kameráját", + "domuteOthers": "Mindenki más elnémítása" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Koppintson a csatlakozáshoz", + "roomname": "Adja meg a terem nevét" + }, + "appDescription": "Rajta, csevegjen az egész csapattal. Valóban hívjon meg mindenkit, akit ismer. A {{app}} teljesen titkosított, 100% -ban nyílt forráskódú videokonferencia-megoldás, amelyet egész nap, minden nap ingyenesen használhat – fiók nélkül.", + "audioVideoSwitch": { + "audio": "Hang", + "video": "Videó" + }, + "calendar": "Naptár", + "connectCalendarButton": "Naptár csatolása", + "connectCalendarText": "Csatlakoztassa a naptárát, hogy láthassa az értekezleteket a {{app}} alkalmazásban. Továbbá, adja hozzá a {{provider}} szolgáltatót és egy kattintással lehet kezelni azokat.", + "enterRoomTitle": "Új értekezlet kezdése", + "roomNameAllowedChars": "Az értekezlet neve nem tartalmazhatja a következő karaktereket: ?, &, :, ', \", %, #.", + "go": "Indítás", + "goSmall": "Indítás", + "join": "LÉTREHOZ /HOZZÁAD", + "info": "Információ", + "privacy": "Adatvédelem", + "recentList": "Legutóbbi", + "recentListDelete": "Törlés", + "recentListEmpty": "A legutóbbi lista jelenleg üres. Csevegjen a csapattal és minden előző értekezlet itt lesz megtalálható.", + "reducedUIText": "Üdvözlet a {{app}} programban!", + "roomname": "Adja meg a szoba nevét", + "roomnameHint": "Adja meg a kívánt nevet vagy URL-t, amelyhez csatlakozni szeretne. Bármiképp elnevezheti, csak ossza meg az értekezlet résztvevőivel, hogy ők ugyanezt a nevet tudják majd megadni.", + "sendFeedback": "Visszajelzés küldése", + "terms": "Feltételek", + "title": "Biztonságos, maradéktalanul felszerelt és teljesen ingyenes videokonferencia", + "getHelp": "Segítség kérése" + }, + "helpView": { + "header": "Súgó" + }, + "lonelyMeetingExperience": { + "youAreAlone": "Egyedül van ezen az értekezleten", + "button": "Mások meghívása" + }, + "chromeExtensionBanner": { + "dontShowAgain": "Ne jelenjen meg újra", + "buttonText": "Chrome kiterjesztés telepítése", + "installExtensionText": "Kiterjesztés telepítése a Google Calendar és az Office 365 integrációjához" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-it.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-it.json index 68dcd6a54..d6ecc95c1 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-it.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-it.json @@ -2,15 +2,15 @@ "addPeople": { "add": "Invita", "countryNotSupported": "Non supportiamo ancora questa destinazione.", - "countryReminder": "Stai chiamando fuori dagli Stati Uniti? Assicurati di iniziare inserendo il codice paese!", + "countryReminder": "Stai chiamando fuori dagli Stati Uniti? Assicurati d'inserire il prefisso internazionale!", "disabled": "Non puoi invitare persone.", - "failedToAdd": "", - "footerText": "La chiamata dall'esterno è disabilitata.", - "loading": "Cercando persone e numeri di telefono", + "failedToAdd": "L'aggiunta di nuove persone è fallita", + "footerText": "La chiamata all'esterno è disabilitata.", + "loading": "Sto cercando persone e numeri di telefono", "loadingNumber": "Sto validando il numero di telefono", - "loadingPeople": "Ricerca delle persone da invitare", + "loadingPeople": "Sto cercando le persone da invitare", "noResults": "Nessun risultato corrispondente", - "noValidNumbers": "Inserire un numero di telefono", + "noValidNumbers": "Per favore inserire un numero di telefono", "searchNumbers": "Aggiungi numeri di telefono", "searchPeople": "Cerca persone", "searchPeopleAndNumbers": "Cerca persone o aggiungi i loro numeri di telefono", @@ -21,17 +21,17 @@ "bluetooth": "Bluetooth", "headphones": "Cuffie", "phone": "Telefono", - "speaker": "Relatore" + "speaker": "Vivavoce" }, "audioOnly": { "audioOnly": "Solo audio" }, "calendarSync": { "addMeetingURL": "Aggiungi un collegamento alla conferenza", - "confirmAddLink": "Vuoi aggiungere un collegamento a ANDI Conference a questo evento?", + "confirmAddLink": "Vuoi aggiungere un collegamento Jitsi a questo evento?", "error": { "appConfiguration": "L'integrazione del calendario non è configurata in modo appropriato.", - "generic": "È stato riscontrato un errore. Controllare le impostazioni del calendario e ricaricare la pagina.", + "generic": "È stato riscontrato un errore. Controllare le impostazioni del calendario o ricaricare la pagina.", "notSignedIn": "È stato riscontrato un errore durante l'autenticazione per la visualizzazione degli eventi del calendario. Controllare le impostazioni del calendario e provare a ripetere l'accesso." }, "join": "Partecipa", @@ -45,7 +45,7 @@ "today": "Oggi" }, "chat": { - "error": "Errore: il tuo messaggio “{{originalText}}” non e’ stato inviato. Ragione: {{error}}", + "error": "Errore: il tuo messaggio “{{originalText}}” non e’ stato inviato. Motivo: {{error}}", "messagebox": "Digitare un messaggio", "nickname": { "popover": "Scegli un nickname", @@ -54,7 +54,7 @@ "title": "Chat" }, "connectingOverlay": { - "joiningRoom": "Collegamento al meeting in corso…" + "joiningRoom": "Collegamento al tuo meeting in corso…" }, "connection": { "ATTACHED": "Collegato", @@ -63,7 +63,7 @@ "CONNECTED": "Connesso", "CONNECTING": "Connessione", "CONNFAIL": "Connessione non riuscita", - "DISCONNECTED": "Occupato", + "DISCONNECTED": "Disconnesso", "DISCONNECTING": "Disconnessione in corso", "ERROR": "Errore", "RECONNECTING": "Si è verificato un problema di rete. Riconnessione..." @@ -110,7 +110,7 @@ "downloadApp": "Scarica l'app", "launchWebButton": "Avvia sul web", "openApp": "Prosegui verso l'app", - "title": "Sto avviando la tua videoconferenza su {{app}}...", + "title": "Sto avviando la tua videoconferenza su {{app}}…", "tryAgainButton": "Prova di nuovo sul desktop" }, "defaultLink": "es. {{url}}", @@ -123,15 +123,15 @@ "deviceSelection": { "noPermission": "Permesso negato", "previewUnavailable": "Anteprima non disponibile", - "selectADevice": "Seleziona un dispositivo", - "testAudio": "Riproduci un suono di test" + "selectADevice": "Scegli un dispositivo", + "testAudio": "Riproduci un suono di prova" }, "dialog": { "accessibilityLabel": { "liveStreaming": "Diretta" }, "allow": "Consenti", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Un altro utente sta condividendo un video. Questa conferenza permette di condividere un solo video alla volta.", "alreadySharedVideoTitle": "È permesso un solo video alla volta", "applicationWindow": "Finestra applicazione", "Back": "Indietro", @@ -144,22 +144,22 @@ "cameraUnsupportedResolutionError": "La tua videocamera non supporta la risoluzione richiesta.", "Cancel": "Annulla", "close": "Chiudi", - "conferenceDisconnectMsg": "Controlla la tua connessione. Riconnessione in {{seconds}} secondi...", + "conferenceDisconnectMsg": "Controlla la tua connessione. Riconnessione in {{seconds}} secondi…", "conferenceDisconnectTitle": "Sei stato disconnesso.", - "conferenceReloadMsg": "Stiamo cercando di risolvere il problema. Riconnessione in {{seconds}} secondi...", + "conferenceReloadMsg": "Stiamo cercando di risolvere il problema. Riconnessione in {{seconds}} secondi…", "conferenceReloadTitle": "Purtroppo qualcosa è andato storto.", "confirm": "Conferma", "confirmNo": "No", "confirmYes": "Sì", - "connectError": "Oops! Qualcosa è andato storto e non ti puoi collegare alla conferenza.", + "connectError": "Oh! Qualcosa è andato storto e non ti puoi collegare alla conferenza.", "connectErrorWithMsg": "Oops! Qualcosa è andato storto e non ti puoi collegare alla conferenza: {{msg}}", "connecting": "Connessione", "contactSupport": "Contatta il supporto", "copy": "Copia", "dismiss": "Scarta", - "displayNameRequired": "", + "displayNameRequired": "Tutti devono avere un nome", "done": "Fatto", - "enterDisplayName": "", + "enterDisplayName": "Inserisci il nome da visualizzare", "error": "Errore", "externalInstallationMsg": "Devi installare la nostra estensione per la condivisione desktop.", "externalInstallationTitle": "Richiesta estensione", @@ -172,31 +172,31 @@ "inlineInstallExtension": "Installa adesso", "internalError": "Ops! Qualcosa è andato storto. Questo è l'errore: {{error}}", "internalErrorTitle": "Errore interno", - "kickMessage": "", + "kickMessage": "Acc! Sei stato espulso dal meeting!", "kickParticipantButton": "Espelli", - "kickParticipantDialog": "Espellere questo partecipante?", - "kickParticipantTitle": "", - "kickTitle": "", + "kickParticipantDialog": "Sei sicuro di voler espellere questo partecipante?", + "kickParticipantTitle": "Espellere questi partecipante?", + "kickTitle": "Espulso dal meeting", "liveStreaming": "Live Streaming", "liveStreamingDisabledForGuestTooltip": "Gli ospiti non possono avviare una diretta.", "liveStreamingDisabledTooltip": "Trasmissioni in diretta disabilitate.", "lockMessage": "Impossibile bloccare la conferenza.", - "lockRoom": "", + "lockRoom": "Aggiungi una password al meeting", "lockTitle": "Blocco fallito", "logoutQuestion": "Vuoi disconnetterti e interrompere la conferenza ?", "logoutTitle": "Logout", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "È stato raggiunto il numero massimo di partecipanti. La conferenza è al completo. Contatta l'organizzatore, o riprova più tardi!", + "maxUsersLimitReachedTitle": "Raggiunto limite partecipanti", "micConstraintFailedError": "Il tuo microfono non soddisfa alcuni dei requisiti richiesti.", "micNotFoundError": "Microfono non trovato.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Non riusciamo a ricevere suoni dal microfono scelto. Prova a selezionare nelle impostazioni un microfono diverso, o a riavvare l'applicazione.", + "micNotSendingDataTitle": "Impossibile accedere al microfono", "micPermissionDeniedError": "Non hai concesso il permesso di usare il microfono. Puoi comunque partecipare alla conferenza ma gli altri non potranno sentirti. Usa il bottone a forma di telecamera nella barra degli indirizzi per cambiare impostazioni.", "micUnknownError": "Impossibile usare il microfono per un motivo sconosciuto.", "muteParticipantBody": "Tu non sarai in grado di riattivare il loro audio, ma loro potranno riattivarlo in qualsiasi momento.", - "muteParticipantButton": "Disattiva audio", - "muteParticipantDialog": "", - "muteParticipantTitle": "", + "muteParticipantButton": "Silenzia partecipante", + "muteParticipantDialog": "Sei sicuro di voler disattivare l'audio di questo partecipante? Saranno loro a doversi riattivare l'audio, per parlare.", + "muteParticipantTitle": "Silenzio questo partecipante?", "Ok": "Ok", "passwordLabel": "", "passwordNotSupported": "Le password per le videoconferenze non sono supportate.", @@ -216,13 +216,13 @@ "remoteControlStopMessage": "Sessione di controllo remoto terminata!", "remoteControlTitle": "Connessione desktop remoto", "Remove": "Rimuovi", - "removePassword": "", + "removePassword": "Togli la password", "removeSharedVideoMsg": "Sei sicuro di voler rimuovere il tuo video condiviso?", "removeSharedVideoTitle": "Rimuovi video condiviso", "reservationError": "Errore di sistema in prenotazione", "reservationErrorMsg": "Codice di errore: {{code}}, messaggio: {{msg}}", "retry": "Riprova", - "screenSharingFailedToInstall": "Oops! Non è stato possibile installare l'estensione per la condivisione schermo. ", + "screenSharingFailedToInstall": "Oh! Non è stato possibile installare l'estensione per la condivisione schermo.", "screenSharingFailedToInstallTitle": "Impossibile installare l'estensione per la condivisione schermo", "screenSharingFirefoxPermissionDeniedError": "Qualcosa è andato storto mentre cercavamo di condividere il tuo schermo. Assicurati di averci dato il premesso di condivisione.", "screenSharingFirefoxPermissionDeniedTitle": "Ops! Non siamo stati in grado di avviare la condivisione schermo!", @@ -249,10 +249,10 @@ "tokenAuthFailed": "Ci dispiace ma non sei autorizzato a partecipare a questa chiamata.", "tokenAuthFailedTitle": "Autenticazione fallita", "transcribing": "Trascrizione", - "unlockRoom": "", + "unlockRoom": "Togli la password al meeting", "userPassword": "password utente", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", + "WaitForHostMsg": "La conferenza <b>{{room}}</b> non è ancora cominciata. Se sei l'organizzatore, per favore autenticati. Altrimenti, aspetta l'arrivo dell'organizzatore.", + "WaitForHostMsgWOk": "La conferenza <b>{{room}}</b> non è ancora cominciata. Se sei l'organizzatore, allora premi OK per autenticarti. Altrimenti, aspetta l'arrivo dell'organizzatore.", "WaitingForHost": "In attesa dell'organizzatore ...", "Yes": "Sì", "yourEntireScreen": "Schermo intero" @@ -270,41 +270,41 @@ "veryGood": "Molto Buona" }, "incomingCall": { - "answer": "Risposta", + "answer": "Rispondi", "audioCallTitle": "Chiamata in arrivo", "decline": "Scarta", - "productLabel": "da ANDI Conference", + "productLabel": "da Jitsi Meet", "videoCallTitle": "Videochiamata in arrivo" }, "info": { "accessibilityLabel": "Mostra informazioni", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Aggiungi password", + "cancelPassword": "Togli password", "conferenceURL": "Collegamento:", "country": "Paese", - "dialANumber": "", + "dialANumber": "Per collegarti telefonicamente al meeting, chiama uno di questi numeri e metti il pin.", "dialInConferenceID": "PIN:", - "dialInNotSupported": "Spiacenti, la chiamata per partecipare attualmente non è supportata", + "dialInNotSupported": "Spiacenti, la partecipazionne solo telefonica non è supportata attualmente", "dialInNumber": "Componi:", - "dialInSummaryError": "", - "dialInTollFree": "", + "dialInSummaryError": "Errore nella ricerca dei numeri telefonici. Riprova più tardi.", + "dialInTollFree": "Numero verde", "genericError": "Ops, qualcosa è andato storto.", - "inviteLiveStream": "Per visualizzare la trasmissione in diretta di questo meeting, clicca su questo link: {{url}}", - "invitePhone": "", + "inviteLiveStream": "Per vedere la diretta di questo meeting, clicca su questo link: {{url}}", + "invitePhone": "Per seguire solo telefonicamente, clicca: {{number}},,{{conferenceID}}#", "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartGeneral": "Invito a connettersi ad una conferenza.", + "inviteURLFirstPartPersonal": "{{name}} ti sta invitando ad un meeting.\n", + "inviteURLSecondPart": "\nPartecipa al meeting:\n{{url}}\n", "liveStreamURL": "Trasmissione in diretta:", "moreNumbers": "Più numeri", "noNumbers": "Nessun numero da chiamare.", - "noPassword": "Nessuno", + "noPassword": "Nessuna", "noRoom": "Non è stata specificata nessuna stanza da chiamare.", "numbers": "Numeri da chiamare", - "password": "", + "password": "$t(lockRoomPasswordUppercase):", "title": "Condividi", - "tooltip": "Condividi collegamento e informazioni di chiamata per questa conferenza", - "label": "" + "tooltip": "Invia il collegamento e i numeri telefonici di questa conferenza", + "label": "Informazioni meeting" }, "inviteDialog": { "alertText": "", @@ -328,13 +328,13 @@ "localRecording": "Mostra o nascondi i controlli per la registrazione", "mute": "Attiva o disattiva il microfono", "pushToTalk": "Premi per parlare", - "raiseHand": "Mostra / Nascondi i video", - "showSpeakerStats": "Mostra statistiche conversanti", + "raiseHand": "Alza o abbassa la mano", + "showSpeakerStats": "Mostra statistiche", "toggleChat": "Apri o chiudi la chat", "toggleFilmstrip": "Mostra o nascondi anteprime video", "toggleScreensharing": "Cambia modalità tra videocamera e condivisione schermo", "toggleShortcuts": "Mostra o nascondi le scorciatoie", - "videoMute": "Attiva / disattiva videocamera" + "videoMute": "Accendo o spegni la videocamera" }, "liveStreaming": { "busy": "Stiamo cercando di liberare risorse per lo streaming. Riprova tra qualche minuto.", @@ -349,11 +349,11 @@ "expandedOff": "La diretta è stata interrotta", "expandedOn": "La conferenza è attualmente in diretta su YouTube.", "expandedPending": "La diretta è in fase di avvio...", - "failedToStart": "Avvio live streaming fallito", - "getStreamKeyManually": "", - "invalidStreamKey": "", - "off": "Il live streaming si è interrotto", - "on": "Live Streaming", + "failedToStart": "Avvio trasmissione in diretta fallito", + "getStreamKeyManually": "Non siamo stati in grado di trovare nessuna trasmissione dal vivo. Prova ad ottenere una chiave stream da Youtube", + "invalidStreamKey": "La chiave stream potrebbe non essere corretta.", + "off": "La diretta si è interrotta", + "on": "Trasmissione in diretta", "pending": "Avvio live stream...", "serviceName": "Servizio live streaming", "signedInAs": "Sei attualmente collegato come:", @@ -397,39 +397,39 @@ "lockRoomPasswordUppercase": "Password", "me": "io", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", + "connectedOneMember": "{{name}} si è connesso", + "connectedThreePlusMembers": "{{name}} e altri {{count}} si sono connessi", + "connectedTwoMembers": "{{first}} e {{second}} si sono connessi", "disconnected": "disconnesso", "focus": "Focus su conferenza", "focusFail": "{{component}} non disponibile - riprova in {{ms}} sec", "grantedTo": "Permessi di moderatore garantiti a {{to}}!", "invitedOneMember": "{{displayName}} è stato invitato", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", - "me": "io", + "invitedThreePlusMembers": "Hai invitato {{name}} e altri {{count}}", + "invitedTwoMembers": "Hai invitato {{first}} e {{second}}", + "kickParticipant": "{{kicked}} è stato espulso da {{kicker}}", + "me": "Io", "moderator": "Impostati i permessi di moderatore!", "muted": "Hai iniziato la conversazione con l'audio disattivato.", "mutedTitle": "Hai l'audio disattivato!", - "mutedRemotelyTitle": "", + "mutedRemotelyTitle": "Ti è stato disattivato l'audio da {{participantDisplayName}}!", "mutedRemotelyDescription": "", "passwordRemovedRemotely": "", "passwordSetRemotely": "", - "raisedHand": "", + "raisedHand": "{{name}} vorrebbe intervenire.", "somebody": "Qualcuno", "startSilentTitle": "", "startSilentDescription": "", "suboptimalExperienceDescription": "Ehm... temiamo che la tua esperienza con {{appName}} non sarà granché su questo browser. Stiamo cercando di migliorare la situazione ma, per il momento, prova ad utilizzare uno di questi <a href='static/recommendedBrowsers.html' target='_blank'>browser supportati</a>.", "suboptimalExperienceTitle": "Problemi con il browser", "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "newDeviceCameraTitle": "Trovata nuova videocamera", + "newDeviceAudioTitle": "Trovata nuova origine audio", + "newDeviceAction": "Usala" }, - "passwordSetRemotely": "", - "passwordDigitsOnly": "", - "poweredby": "powered by", + "passwordSetRemotely": "definita da altro utente", + "passwordDigitsOnly": "Fino a {{number}} cifre", + "poweredby": "offerto da", "presenceStatus": { "busy": "Occupato", "calling": "Chiamata…", @@ -498,26 +498,26 @@ "selectCamera": "Videocamera", "selectMic": "Microfono", "startAudioMuted": "Tutti cominciano con il microfono disattivato", - "startVideoMuted": "Tutti cominciano nascosti", + "startVideoMuted": "Tutti cominciano con il video disattivato", "title": "Impostazioni" }, "settingsView": { "alertOk": "OK", "alertTitle": "Attenzione", "alertURLText": "L'URL del server inserito non è valido", - "buildInfoSection": "", + "buildInfoSection": "Versione", "conferenceSection": "Conferenza", "displayName": "Nome visualizzato", "email": "Email", "header": "Impostazioni", "profileSection": "Profilo", "serverURL": "URL del server", - "startWithAudioMuted": "Inizia con l'audio mutato", - "startWithVideoMuted": "Avvia con video mutato", - "version": "" + "startWithAudioMuted": "Inizia con l'audio disattivato", + "startWithVideoMuted": "Avvia con il video disattivato", + "version": "Versione" }, "share": { - "dialInfoText": "", + "dialInfoText": "\n\n=====\n\nVuoi solo ascoltare la conferenza da un telefono?\n\n{{defaultDialInNumber}}Clicca questo link per vedere i numeri telefonici di questo meeting\n{{dialInfoPageUrl}}", "mainText": "Clicca sul link seguente per partecipare alla conferenza:\n{{roomUrl}}" }, "speaker": "Relatore", @@ -526,8 +526,8 @@ "minutes": "{{count}}m", "name": "Nome", "seconds": "{{count}}s", - "speakerStats": "Statistiche del parlante", - "speakerTime": "Tempo del conversante" + "speakerStats": "Statistiche del relatore", + "speakerTime": "Tempo del relatore" }, "startupoverlay": { "policyText": " ", @@ -541,7 +541,7 @@ "toolbar": { "accessibilityLabel": { "audioOnly": "Attiva/disattiva solo audio", - "audioRoute": "Seleziona la periferica audio", + "audioRoute": "Scegli l'uscita audio", "callQuality": "Gestisci qualità della chiamata", "cc": "Attiva/disattiva sottotitoli", "chat": "Attiva/disattiva la chat", @@ -550,75 +550,76 @@ "fullScreen": "Attiva/disattiva schermo intero", "hangup": "Lascia la conferenza", "invite": "Invita persone", - "kick": "", + "kick": "Espelli partecipante", "localRecording": "Abilita controlli di registrazione locale", - "lockRoom": "", - "moreActions": "Attiva/disattiva Menu avanzato", + "lockRoom": "Attiva o disattiva password", + "moreActions": "Attiva o disattiva menu avanzato", "moreActionsMenu": "Menu avanzato", - "mute": "Attiva/disattiva audio muto", + "mute": "Attiva/disattiva audio", "pip": "Attiva/disattiva immagine nell’immagine", "profile": "Modifica profilo", "raiseHand": "Attiva/disattiva alzata di mano", "recording": "Attiva/disattiva registrazione", - "remoteMute": "", + "remoteMute": "Disattiva audio partecipante", "Settings": "Attiva/disattiva impostazioni", "sharedvideo": "Attiva/disattiva condivisione YouTube", - "shareRoom": "Invita partecipante", + "shareRoom": "Invita qualcuno", "shareYourScreen": "Attiva/disattiva condivisione schermo", "shortcuts": "Attiva/disattiva scorciatoie", "show": "", "speakerStats": "Attiva/disattiva statistiche relatore", - "tileView": "Attiva/disattiva visualizzazione griglia", - "toggleCamera": "Attiva/disattiva webcam", - "videomute": "Attiva/disattiva silenziamento video", - "videoblur": "" + "tileView": "Vedi tutti i partecipanti insieme, o uno solo", + "toggleCamera": "Cambia videocamera", + "videomute": "Attiva/disattiva videocamera", + "videoblur": "Attiva/disattiva offuscamento video" }, "addPeople": "Aggiungi persone alla chiamata", - "audioOnlyOff": "Disattiva modalità solo audio", - "audioOnlyOn": "Disattiva modalità solo audio", - "audioRoute": "Seleziona la periferica audio", + "audioOnlyOff": "Anche video", + "audioOnlyOn": "Solo audio", + "audioRoute": "Scegli l'uscita audio", "authenticate": "Autenticazione", "callQuality": "Gestisci qualità della chiamata", "chat": "Apri / Chiudi chat", - "closeChat": "", + "closeChat": "Chiudi chat", "documentClose": "Chiudi documento condiviso", "documentOpen": "Apri documento condiviso", "enterFullScreen": "Visualizza a schermo intero", - "enterTileView": "", + "enterTileView": "Vedi tutti i partecipanti", "exitFullScreen": "Esci da schermo intero", - "exitTileView": "", + "exitTileView": "Vedi una persona sola", "feedback": "Lascia un feedback", "hangup": "Esci", "invite": "Invita persone", "login": "Login", "logout": "Logout", - "lowerYourHand": "", + "lowerYourHand": "Abbassa la mano", "moreActions": "Più azioni", + "moreOptions": "Più opzioni", "mute": "Microfono Attiva / Disattiva", - "openChat": "", + "openChat": "Apri una chat", "pip": "Abilita visualizzazione immagine nell’immagine", "profile": "Modifica profilo", "raiseHand": "Alza / Abbassa la mano", - "raiseYourHand": "", + "raiseYourHand": "Alza la mano", "Settings": "Impostazioni", "sharedvideo": "Condividi un video Youtube", "shareRoom": "Invita partecipante", "shortcuts": "Visualizza scorciatoie", "speakerStats": "Statistiche dell'interlocutore", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", + "startScreenSharing": "Inizia la condivisione dello schermo", + "startSubtitles": "Avvia sottotitoli", + "stopScreenSharing": "Ferma la condivisione dello schermo", + "stopSubtitles": "Ferma sottotitoli", "stopSharedVideo": "Ferma video YouTube", "talkWhileMutedPopup": "Stai provando a parlare? Il microfono è disattivato.", - "tileViewToggle": "Attiva/disattiva visualizzazione griglia", - "toggleCamera": "Attiva/disattiva webcam", + "tileViewToggle": "Vedi tutti i partecipanti insieme, o uno solo", + "toggleCamera": "Cambia videocamera", "videomute": "Attiva / Disattiva videocamera", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Offusca il video", + "stopvideoblur": "Non offuscare il video" }, "transcribing": { - "ccButtonTooltip": "", + "ccButtonTooltip": "Inizia / Ferma i sottotitoli", "error": "Registrazione fallita. Prova di nuovo.", "expandedLabel": "La trascrizione della conferenza è attiva", "failedToStart": "C’è stato un errore nell’avvio del servizio di trascrizione.", @@ -653,12 +654,12 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "Modalita' solo audio attiva. Questa modalità permette di rispamiare banda ma non vedrai gli altri partecipanti.", - "callQuality": "", + "audioOnlyExpanded": "Hai attivato la modalità solo audio. Questa modalità permette di rispamiare banda, ma non vedrai gli altri partecipanti.", + "callQuality": "Qualità video", "hd": "HD", "highDefinition": "Alta definizione", "labelTooiltipNoVideo": "Nessun video", - "labelTooltipAudioOnly": "Modalità solo audio abilitata", + "labelTooltipAudioOnly": "Hai attivato la modalità solo audio", "ld": "LD", "lowDefinition": "Bassa definizione", "onlyAudioAvailable": "È disponibile solo l'audio", @@ -678,14 +679,14 @@ "muted": "Audio disattivato", "remoteControl": "Controllo remoto", "show": "", - "videomute": "" + "videomute": "Silenzia il video" }, "welcomepage": { "accessibilityLabel": { "join": "Tap per accedere", "roomname": "Inserisci Nome Stanza" }, - "appDescription": "Via avanti, video chatta con l'intero team. In effetti, invita tutti quelli che conosci. {{app}} è una soluzione di video conference totalmente crittografata, 100% open cource, che puoi utilizzare tutto il giorno, ogni giorno, gratuitamente - senza bisogno di un account.", + "appDescription": "Avvia una videochiamata con tutto il team. Invita tutti quelli che conosci. {{app}} è una soluzione per effettuare videoconferenze totalmente crittografata, 100% open source, che puoi usare sempre, ogni giorno, gratuitamente – senza bisogno di un account.", "audioVideoSwitch": { "audio": "Voce", "video": "Video" @@ -696,7 +697,7 @@ "enterRoomTitle": "Avvia una nuova conferenza", "go": "VAI", "join": "UNISCITI", - "info": "", + "info": "Informazioni", "privacy": "Privacy", "recentList": "Recente", "recentListDelete": "Cancella", @@ -707,5 +708,9 @@ "sendFeedback": "Invia feedback", "terms": "Termini di utilizzo", "title": "Il sistema di conferenza sicuro, funzionale e completamente gratuito." + }, + "lonelyMeetingExperience": { + "button": "Invita gli altri", + "youAreAlone": "Sei l'unico in riunione" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ja.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ja.json index d7b61c548..495f244bb 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ja.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ja.json @@ -5,7 +5,7 @@ "countryReminder": "米国外にコールしますか? 国コードで始まることを確認してください!", "disabled": "人を招待することはできません。", "failedToAdd": "", - "footerText": "ダイヤルアウトが無効しています。", + "footerText": "ダイヤルアウトが無効です。", "loading": "人と電話番号を検索する", "loadingNumber": "電話番号を検証しています", "loadingPeople": "招待する人を検索する", @@ -17,14 +17,12 @@ "telephone": "電話: {{number}}", "title": "この会議に人を招待する" }, - "\u0005addPeople": {}, "audioDevices": { "bluetooth": "Bluetooth", "headphones": "ヘッドフォン", "phone": "電話", "speaker": "スピーカー" }, - "\u0005audioDevices": {}, "audioOnly": { "audioOnly": "オーディオのみ" }, @@ -33,7 +31,7 @@ "confirmAddLink": "このイベントにビデオ会議リンクを張り付けますか。", "error": { "appConfiguration": "カレンダー機能が正しく設定されていない", - "generic": "エラーが発生した。カレンダー機能設定を確認してください、もしくはカレンダーを更新してください", + "generic": "エラーが発生しました。カレンダー機能設定を確認してください、もしくはカレンダーを更新してください", "notSignedIn": "認証中エラーが発生しました。カレンダーの設定を確認し、ログインし直してください。" }, "join": "参加", @@ -46,7 +44,6 @@ "refresh": "カレンダーを更新する", "today": "今日" }, - "\u0005calendarSync": {}, "chat": { "error": "エラー: \"{{originalText}}\" がまだ送信されていません. エラー: {{error}}", "messagebox": "メッセージを書いてください", @@ -73,7 +70,7 @@ }, "connectionindicator": { "address": "アドレス:", - "bandwidth": "推定バンドワイズ", + "bandwidth": "推定帯域幅", "bitrate": "ビットレート:", "bridgeCount": "サーバー数:", "connectedTo": "接続先:", @@ -86,7 +83,7 @@ "quality": { "good": "良い", "inactive": "休止中", - "lost": "接続切れた", + "lost": "接続ロスト", "nonoptimal": "最適ではない", "poor": "悪い" }, @@ -109,7 +106,7 @@ "downloadApp": "アプリをダウンロードする", "launchWebButton": "Webで起動する", "openApp": "アプリで続く", - "title": "{{app}} で会議を開始する...", + "title": "{{app}} で会議を開始する…", "tryAgainButton": "デスクトップでもう一度お試しください" }, "defaultLink": "例: {{url}}", @@ -139,19 +136,19 @@ "cameraNotSendingData": "あなたのカメラにアクセスすることができません。 他のアプリケーションがこのデバイスを使用しているかどうかを確認し、設定メニューから別のデバイスを選択するか、アプリケーションをリロードしてみてください。", "cameraNotSendingDataTitle": "カメラにアクセスできません", "cameraPermissionDeniedError": "あなたはカメラを使用する許可を与えていません。 あなたはまだ会議に参加することができますが、他の参加者はあなたを見ることはできません。 この問題を解決するには、アドレスバーのカメラボタンを使用します。", - "cameraUnknownError": "不明な理由でカメラを使用することはできません。", + "cameraUnknownError": "不明な理由でカメラが使用できません。", "cameraUnsupportedResolutionError": "お使いのカメラは、必要なビデオ解像度をサポートしていません。", "Cancel": "キャンセル", "close": "閉じる", - "conferenceDisconnectMsg": "ネットワーク接続を確認することができます。 {{seconds}} 秒で再接続します...", + "conferenceDisconnectMsg": "ネットワーク接続を確認することができます。 {{seconds}} 秒で再接続します…", "conferenceDisconnectTitle": "あなたは切断されました。", - "conferenceReloadMsg": "私たちはこれを解決しようとしています。 {{seconds}} 秒で再接続します...", - "conferenceReloadTitle": "残念ながら、何かが間違っていました。", + "conferenceReloadMsg": "私たちはこれを解決しようとしています。 {{seconds}} 秒で再接続します…", + "conferenceReloadTitle": "残念ながら、何か問題が発生しています。", "confirm": "確認", "confirmNo": "いいえ", "confirmYes": "はい", - "connectError": "Oops! 何かがうまくいかず、会議に接続できませんでした。", - "connectErrorWithMsg": "Oops! 何か問題が発生し、会議に接続できませんでした: {{msg}}", + "connectError": "おおっと! 何かがうまくいかず、会議に接続できませんでした。", + "connectErrorWithMsg": "おおっと! 何か問題が発生し、会議に接続できませんでした: {{msg}}", "connecting": "接続中", "contactSupport": "サポート問い合わせ先", "copy": "コピー", @@ -194,7 +191,7 @@ "micUnknownError": "不明な理由でマイクを使用することはできません。", "muteParticipantBody": "あなたはそのミュートを解除することはできませんが、彼らはいつでも自分自身のミュートを解除することができます。", "muteParticipantButton": "ミュート", - "muteParticipantDialog": "この参加者をミュートしますか。あなたが取り戻せませんが、本人が自分でいつでも解除でいます。", + "muteParticipantDialog": "この参加者をミュートしますか。あなた解除できませんが、本人が自分でいつでも解除できます。", "muteParticipantTitle": "", "Ok": "Ok", "passwordLabel": "", @@ -252,13 +249,10 @@ "userPassword": "ユーザーのパスワード", "WaitForHostMsg": "", "WaitForHostMsgWOk": "", - "WaitingForHost": "ホストを待っています...", + "WaitingForHost": "ホストを待っています…", "Yes": "はい", "yourEntireScreen": "あなたの画面全体" }, - "\u0005dialog": { - "accessibilityLabel": {} - }, "dialOut": { "statusMessage": "は現在 {{status}} です" }, @@ -308,7 +302,6 @@ "tooltip": "この会議のリンクとダイヤルイン情報を共有する", "label": "ミーティング情報" }, - "\u0005info": {}, "inviteDialog": { "alertText": "", "header": "招待", @@ -339,7 +332,6 @@ "toggleShortcuts": "キーボード ショートカットを表示", "videoMute": "カメラを有効/無効" }, - "\u0005keyboardShortcuts": {}, "liveStreaming": { "busy": "私たちはストリーミングリソースを解放するために取り組んでいます。 数分後にもう一度お試しください。", "busyTitle": "すべてのストリーマーは現在ビジー状態です", @@ -358,7 +350,7 @@ "invalidStreamKey": "ライブストリーミングキーが間に合いました。", "off": "ライブストリーミングが停止しました", "on": "ライブストリーミング", - "pending": "ライブストリームを開始しています...", + "pending": "ライブストリームを開始しています…", "serviceName": "ライブストリーミングサービス", "signedInAs": "", "signIn": "Googleでログイン", @@ -368,7 +360,6 @@ "streamIdHelp": "これは何ですか?", "unavailableTitle": "ライブストリーミングは利用できません" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { "off": "オフ", @@ -398,7 +389,6 @@ "stop": "録画を停止する", "yes": "はい" }, - "\u0005localRecording": {}, "lockRoomPassword": "パスワード", "lockRoomPasswordUppercase": "パスワード", "me": "私", @@ -450,7 +440,6 @@ "rejected": "", "ringing": "着信している・・" }, - "\u0005presenceStatus": {}, "profile": { "setDisplayNameLabel": "表示名を設定してください", "setEmailInput": "メールアドレスを入力してください", @@ -482,7 +471,6 @@ "unavailable": "Oops! {{serviceName}} は現在使用できません。 私たちはこの問題の解決に取り組んでいます。 後でもう一度お試しください。", "unavailableTitle": "録画できません" }, - "\u0005recording": {}, "sectionList": { "pullToRefresh": "プルしてリフレッシュする" }, @@ -509,9 +497,6 @@ "startVideoMuted": "全員非表示にする", "title": "設定" }, - "\u0005settings": { - "calendar": {} - }, "settingsView": { "alertOk": "OK", "alertTitle": "Warning", @@ -533,19 +518,17 @@ }, "speaker": "スピーカー", "speakerStats": { - "hours": "{{count}} 秒", - "minutes": "{{count}} 秒", + "hours": "{{count}} 時間", + "minutes": "{{count}} 分", "name": "名前", "seconds": "{{count}} 秒", "speakerStats": "話者の統計", "speakerTime": "話す時間" }, - "\u0005speakerStats": {}, "startupoverlay": { "policyText": " ", "title": "{{app}} を使用するには、マイクとカメラが必要です。" }, - "\u0005startupoverlay": {}, "suspendedoverlay": { "rejoinKeyTitle": "再参加", "text": "再接続するには、<i>再参加</i> ボタンを押してください。", @@ -575,7 +558,7 @@ "recording": "レコーディングに切り替える", "remoteMute": "参加者を追い出す", "Settings": "設定に切り替える", - "sharedvideo": "Youtubeビデオ共有に切り替える", + "sharedvideo": "YouTubeビデオ共有に切り替える", "shareRoom": "誰かを招待する", "shareYourScreen": "画面共有に切り替える", "shortcuts": "ショートカットに切り替える", @@ -630,9 +613,6 @@ "startvideoblur": "", "stopvideoblur": "" }, - "\u0005toolbar": { - "accessibilityLabel": {} - }, "transcribing": { "ccButtonTooltip": "字幕を表示・非表示する", "error": "録画に失敗しました。 もう一度お試しください。", @@ -645,7 +625,6 @@ "stop": "字幕を非表示する", "tr": "TR" }, - "\u0005transcribing": {}, "userMedia": { "androidGrantPermissions": "ブラウザーのポップアップで<b><i>許可する</i></b>を選択してください", "chromeGrantPermissions": "ブラウザーのポップアップで<b><i>許可する</i></b>を選択してください", @@ -664,7 +643,7 @@ "errorAlreadyInvited": "{{displayName}}さんがすでに招待されました", "errorInvite": "会議がまだ設定されていません。もう一度お試しください。", "errorInviteFailed": "問題を解決しています。しばらくしたらまたお試しください。", - "errorInviteFailedTitle": " {{displayName}}さんが招待できません。", + "errorInviteFailedTitle": "{{displayName}}さんが招待できません。", "errorInviteTitle": "招集エラー", "pending": "{{displayName}} が招待されました" }, @@ -724,6 +703,5 @@ "sendFeedback": "フィードバックを送信", "terms": "利用規約", "title": "安全で、機能豊富で、完全に無料のビデオ会議" - }, - "\u0005welcomepage": {} -} \ No newline at end of file + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ko.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ko.json index ffea6c19e..4b80491af 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ko.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ko.json @@ -106,7 +106,7 @@ "downloadApp": "앱 다운로드", "launchWebButton": "웹에서 실행", "openApp": "방으로 이동하기", - "title": "{{app}}에서 회의 시작...", + "title": "{{app}}에서 회의 시작…", "tryAgainButton": "데스크톱에서 다시 시도하십시오" }, "defaultLink": "e.g. {{url}}", @@ -140,7 +140,7 @@ "cameraUnsupportedResolutionError": "카메라가 필요한 비디오 해상도를 지원하지 않습니다", "Cancel": "취소", "close": "닫기", - "conferenceDisconnectMsg": "네트워크 연결을 확인하고 있습니다. {{seconds}} 초 내에 다시 연결중입니다...", + "conferenceDisconnectMsg": "네트워크 연결을 확인하고 있습니다. {{seconds}} 초 내에 다시 연결중입니다…", "conferenceDisconnectTitle": "연결이 끊어졌습니다.", "conferenceReloadMsg": "문제를 해결하려고 노력하고 있습니다. {{seconds}} 초 안에 다시 연결중입니다.", "conferenceReloadTitle": "불행하게도 문제가 발생했습니다", @@ -249,13 +249,10 @@ "userPassword": "사용자 비밀번호", "WaitForHostMsg": "", "WaitForHostMsgWOk": "", - "WaitingForHost": "호스트를 기다리는 중입니다...", + "WaitingForHost": "호스트를 기다리는 중입니다…", "Yes": "", "yourEntireScreen": "전체 화면" }, - "\u0005dialog": { - "accessibilityLabel": {} - }, "dialOut": { "statusMessage": "지금은 {{status}}입니다" }, @@ -305,7 +302,6 @@ "tooltip": "링크 공유 및 회의에 대한 정보", "label": "" }, - "\u0005info": {}, "inviteDialog": { "alertText": "", "header": "초대", @@ -354,7 +350,7 @@ "invalidStreamKey": "", "off": "실시간 스트리밍이 중지됨", "on": "실시간 스트리밍", - "pending": "실시간 스트리밍 시작...", + "pending": "실시간 스트리밍 시작…", "serviceName": "실시간 스트리밍 서비스", "signedInAs": "", "signIn": "Google로 로그인", @@ -393,7 +389,6 @@ "stop": "레코딩 종료", "yes": "" }, - "\u0005localRecording": {}, "lockRoomPassword": "패스워드", "lockRoomPasswordUppercase": "패스워드", "me": "Me", @@ -467,7 +462,7 @@ "loggedIn": "", "off": "레코딩이 중지됨", "on": "레코딩", - "pending": "참석할 멤버를 기다리는 중입니다...", + "pending": "참석할 멤버를 기다리는 중입니다…", "rec": "REC", "serviceDescription": "", "serviceName": "레코딩 서비스", @@ -476,7 +471,6 @@ "unavailable": "죄송합니다. {{serviceName}}은 현재 사용할 수 없습니다. 저희는 문제를 해결하기 위해 노력하고 있습니다. 나중에 다시 시도 해주십시오.", "unavailableTitle": "레코딩을 사용할 수 없습니다" }, - "\u0005recording": {}, "sectionList": { "pullToRefresh": "당겨서 새로고침" }, @@ -503,9 +497,6 @@ "startVideoMuted": "모두가 비디오 비활성화로 시작합니다", "title": "세티" }, - "\u0005settings": { - "calendar": {} - }, "settingsView": { "alertOk": "확인", "alertTitle": "경고", @@ -622,7 +613,6 @@ "startvideoblur": "", "stopvideoblur": "" }, - "\u0005toolbar": {}, "transcribing": { "ccButtonTooltip": "", "error": "레코딩이 실패했습니다. 다시 시도하십시오.", @@ -630,12 +620,11 @@ "failedToStart": "", "labelToolTip": "", "off": "", - "pending": "참석할 멤버를 기다리는 중입니다...", + "pending": "참석할 멤버를 기다리는 중입니다…", "start": "", "stop": "", "tr": "" }, - "\u0005transcribing": {}, "userMedia": { "androidGrantPermissions": "브라우저에서 권한을 요청할 때 <b><i>허락</i></b>을 선택", "chromeGrantPermissions": "브라우저에서 권한을 요청할 때 <b><i>허락</i></b>을 선택", @@ -715,4 +704,4 @@ "terms": "이용약관", "title": "" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-mn.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-mn.json new file mode 100644 index 000000000..4e05db4b6 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-mn.json @@ -0,0 +1,777 @@ +{ + "addPeople": { + "add": "Урих", + "countryNotSupported": "Танай улсыг хараахан дэмжихгүй байна.", + "countryReminder": "АНУ-аас өөр улсруу залгах бол улсын кодоо оруулна уу!", + "disabled": "Хүмүүсийг урих боломжгүй.", + "failedToAdd": "Оролцогч нэмж чадсангүй", + "footerText": "Дуудлага хийх идэвхгүй.", + "loading": "Хүмүүс болон утасны дугаарыг хайж байна", + "loadingNumber": "Утасны дугаарыг баталгаажуулж байна", + "loadingPeople": "Урих хүмүүсийг хайж байна", + "noResults": "Үр дүн олдсонгүй", + "noValidNumbers": "Утасны дугаараа оруулна уу", + "searchNumbers": "Утасны дугаар нэмэх", + "searchPeople": "Хүмүүс хайх", + "searchPeopleAndNumbers": "Хүмүүсээс хайх эсвэл утасны дугаар нэмнэ үү", + "telephone": "Утас: {{number}}", + "title": "Хүмүүсийг хуралд урих" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Чихэвч", + "phone": "Утас", + "speaker": "Яригч", + "none": "Дууны төхөөрөмж байхгүй" + }, + "audioOnly": { + "audioOnly": "Чанар багасгах" + }, + "calendarSync": { + "addMeetingURL": "Хурлын холбоосыг нэмнэ үү", + "confirmAddLink": "Та энэ үйл явдалд холбоосыг нэмэхийг хүсч байна уу?", + "error": { + "appConfiguration": "Цагалбарын холболтыг зөв тохируулаагүй байна.", + "generic": "Алдаа гарсан байна. Цагалбарын тохиргоогоо шалгах эсвэл цагалбар дахин ачаалж үзнэ үү.", + "notSignedIn": "An error occurred while authenticating to see calendar events. Please check your calendar settings and try logging in again." + }, + "join": "Нэгдэх", + "joinTooltip": "Уулзалтад нэглэх", + "nextMeeting": "Дараагийн уулзалт", + "noEvents": "Удахгүй болох үйл явдал байхгүй байна.", + "ongoingMeeting": "идэвхтэй уулзалт", + "permissionButton": "Тохиргоо нээх", + "permissionMessage": "Апп-д уулзалтуудаа нэмэхийн тулд цагалбарын зөвшөөрөл шаардлагатай.", + "refresh": "Цагалбараа дахин ачаалах", + "today": "Өнөөдөр" + }, + "chat": { + "error": "Алдаа: таны мессеж илгээгдээгүй байна. Шалтгаан: {{error}}", + "fieldPlaceHolder": "Энд чатаа бичнэ үү", + "messagebox": "Зурвас бичих", + "messageTo": "Хувийн зурвас {{recipient}}", + "noMessagesMessage": "Энэ хуралд ямар ч зурвас байхгүй. Эндээс зурвасаа эхлүүл!", + "nickname": { + "popover": "Нэр бичнэ үү", + "title": "Нэрээ оруулна уу" + }, + "privateNotice": "Хувийн зурвас {{recipient}}", + "title": "Чат", + "you": "чи" + }, + "chromeExtensionBanner": { + "installExtensionText": "Install the extension for Google Calendar and Office 365 integration", + "buttonText": "Chrome Extension суулгах", + "dontShowAgain": "Энийг дахин харахгүй" + }, + "connectingOverlay": { + "joiningRoom": "Таныг хуралд холбож байна..." + }, + "connection": { + "ATTACHED": "Хавсаргасан", + "AUTHENTICATING": "Нэвтэрч байна", + "AUTHFAIL": "Нэвтрэлт амжилтгүй болсон", + "CONNECTED": "Холбогдсон", + "CONNECTING": "Холбож байна", + "CONNFAIL": "Холболт амжилтгүй болсон", + "DISCONNECTED": "Холбогдоогүй", + "DISCONNECTING": "Салгаж байна", + "ERROR": "Алдаа", + "FETCH_SESSION_ID": "Оролцогчийн холболтыг олох...", + "GET_SESSION_ID_ERROR": "Оролцогчийн хоболт дээр алдаа: {{code}}", + "GOT_SESSION_ID": "Оролцогчийн холболтыг олох... Дууссан", + "LOW_BANDWIDTH": "{{displayName}} чанарыг бууруулж видео унтраасан байна" + }, + "connectionindicator": { + "address": "Хаяг:", + "bandwidth": "Тооцоолсон чанар:", + "bitrate": "Битрат:", + "bridgeCount": "Серверийн тоо: ", + "connectedTo": "Холбогдсон:", + "e2e_rtt": "E2E RTT:", + "framerate": "Нягтаршил:", + "less": "Хураах", + "localaddress": "Дотоод хаяг:", + "localaddress_plural": "Дотоод хаягууд:", + "localport": "Дотоод порт:", + "localport_plural": "Дотоод портууд:", + "more": "Дэлгэрэнгүй", + "packetloss": "Алдагдал:", + "quality": { + "good": "Сайн", + "inactive": "Идэвхгүй", + "lost": "Алдагдалтай", + "nonoptimal": "Оромгүй", + "poor": "Муу" + }, + "remoteaddress": "Гадаад хаяг:", + "remoteaddress_plural": "Гадаад хаягууд:", + "remoteport": "Гадаад порт:", + "remoteport_plural": "Гадаад портууд:", + "resolution": "Хэмжээ:", + "status": "Холболт:", + "transport": "Transport:", + "transport_plural": "Transports:" + }, + "dateUtils": { + "earlier": "Өмнөх", + "today": "Өнөөдөр", + "yesterday": "Өчигдөр" + }, + "deepLinking": { + "appNotInstalled": "Та утсан дээрээ хурал оролцохын тулд танд {{app}} мобайл апп шаардлагатай.", + "description": "Бид таны уулзалтыг {{app}} desktop апп дотор эхлүүлэхийг оролдсон. Дахин оролдох эсвэл {{app}} веб апп дээр эхлүүлнэ үү.", + "descriptionWithoutWeb": "Бид таны хурлыг {{app}} desktop апп дотор эхлүүлэхийг оролдсон.", + "downloadApp": "Апп татаж авах", + "launchWebButton": "Вэб дээр ажиллуулах", + "openApp": "Апп-аар үргэлжлүүлэх", + "title": "Таны уулзалтыг {{app}} эхлүүлж байна....", + "tryAgainButton": "Дахин оролдоно уу" + }, + "defaultLink": "e.g. {{url}}", + "defaultNickname": "Нэрээ бич", + "deviceError": { + "cameraError": "Таны камер руу нэвтрэх үйлдэл амжилтгүй болсон байна", + "cameraPermission": "Камерын зөвшөөрөл авахад алдаа гарлаа", + "microphoneError": "Таны микрофон руу нэвтрэлт амжилтгүй боллоо", + "microphonePermission": "Микрофоны зөвшөөрөл авахад алдаа гарлаа" + }, + "deviceSelection": { + "noPermission": "Зөвшөөрөл өгөөгүй байна", + "previewUnavailable": "Урьдчилан харах боломжгүй", + "selectADevice": "Төхөөрөмж сонгоно уу", + "testAudio": "Туршилтын дуу тоглуул" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Шууд дамжуулалт" + }, + "allow": "Зөвшөөр", + "alreadySharedVideoMsg": "Видео хуваалцаж байна. Хуралд нэг зэрэг зөвхөн нэг л видеог хуваалцах боломжийг олгодог..", + "alreadySharedVideoTitle": "Нэг удаад зөвхөн нэг л хуваалцсан видеог зөвшөөрнө", + "applicationWindow": "Програмын цонх", + "Back": "Буцах", + "cameraConstraintFailedError": "Таны камер зарим шаардлагатай хязгаарлалтыг хангаж чадахгүй байна.", + "cameraNotFoundError": "Камер олдсонгүй.", + "cameraNotSendingData": "Таны камерт хандах боломжгүй байна. Хэрэв өөр програм энэ төхөөрөмжийг ашиглаж байгаа эсэхийг шалгаад, тохиргоог цэснээс өөр төхөөрөмжийг сонгох эсвэл дахин ачаалж үзнэ үү.", + "cameraNotSendingDataTitle": "Камерт хандах боломжгүй байна", + "cameraPermissionDeniedError": "Та камераа ашиглах зөвшөөрөл өгөөгүй байна. Та энэ хуралд оролцох боломжтой ч бусад хүмүүс таныг харж чадахгүй. Үүнийг засахын тулд хаяг бичдэг хэсэгийн хойно байгаа камерын товчийг дарна уу.", + "cameraUnknownError": "Ямар нэг шалтгааны улмаас камерыг ашиглаж чадахгүй.", + "cameraUnsupportedResolutionError": "Таны камер шаардлагатай видеоны хэмжээг дэмждэггүй.", + "Cancel": "Цуцлах", + "close": "Хаах", + "conferenceDisconnectMsg": "Сүлжээний холболт шалгаж байна. Дахин холбогдож байна {{seconds}} сек...", + "conferenceDisconnectTitle": "Та салсан байна.", + "conferenceReloadMsg": "Бид засахаар оролдож байна. Дахин холбогдож байна {{seconds}} сек...", + "conferenceReloadTitle": "Уучлаарай. Ямар нэг алдаа гарсан байна.", + "confirm": "Батлах", + "confirmNo": "Үгүй", + "confirmYes": "Тийт", + "connectError": "Алдаа гарсан тул бид хуралтай холбогдож чадахгүй байна.", + "connectErrorWithMsg": "Алдаа гарсан тул бид конференцтай холбогдож чадахгүй байна: {{msg}}", + "connecting": "Холбож байна", + "contactSupport": "Дэмжлэгтэй холбоо барина уу", + "copy": "Хуулах", + "dismiss": "хаах", + "displayNameRequired": "Таны нэр хэн бэ?", + "done": "Дуусгах", + "enterDisplayName": "Энд нэрээ оруулна уу", + "error": "Алдаа", + "externalInstallationMsg": "Та манай десктоп хуваалцах өргөтгөлийг суулгах хэрэгтэй.", + "externalInstallationTitle": "Өргөтгөл суулгах шаардлагатай", + "goToStore": "webstore орох", + "gracefulShutdown": "Манай үйлчилгээ одоогоор засвартай байна. Дараа дахин оролдож үзнэ үү.", + "IamHost": "Үүсгэгч", + "incorrectRoomLockPassword": "Нууц үг буруу", + "incorrectPassword": "Хэрэглэгчийн нэр эсвэл нууц үг буруу байна", + "inlineInstallationMsg": "Та манай десктоп хуваалцах өргөтгөлийг суулгах хэрэгтэй.", + "inlineInstallExtension": "Одоо суулгах", + "internalError": "Ямар нэгэн зүйл буруу байна. Дараах алдаа гарсан байна: {{error}}", + "internalErrorTitle": "Дотоод алдаа", + "kickMessage": "Та дэлгэрэнгүй мэдээллийг {{participantDisplayName}} холбогдож авна уу..", + "kickParticipantButton": "Гаргах", + "kickParticipantDialog": "Та энэ оролцогчийг гаргахдаа итгэлтэй байна уу?", + "kickParticipantTitle": "Энэ оролцогчийг гаргах үү?", + "kickTitle": "{{participantDisplayName}} чамайг хурлаас гаргасан", + "liveStreaming": "Шууд дамжуулалт", + "liveStreamingDisabledForGuestTooltip": "Зочид шууд дамжуулалт эхлүүлэх боломжгүй.", + "liveStreamingDisabledTooltip": "Шууд дамжуулалтыг идэвхгүй болгох.", + "lockMessage": "Хурал түгжигдсэнгүй.", + "lockRoom": "Хурал нэмэх $t(lockRoomPasswordUppercase)", + "lockTitle": "Түгжихэд алдаа гарлаа", + "logoutQuestion": "Та хурлаас гарч, хурлыг зогсоохыг хүсэж байна уу?", + "logoutTitle": "Гарах", + "maxUsersLimitReached": "Оролцогчдын тооны дээд хязгаарт хүрсэн байна. Уулзалтын эзэмшигчтэй холбоо барина уу эсвэл дараа дахин оролдоно уу!", + "maxUsersLimitReachedTitle": "Оролцогчдын дээд хязгаарт хүрсэн байна", + "micConstraintFailedError": "Таны микрофон зарим шаардлагатай хязгаарлалтыг хангахгүй байна.", + "micNotFoundError": "Микрофон олдсонгүй.", + "micNotSendingData": "Микрофоныг дуугүй болгохын тулд компьютерийн тохиргоо руу очно уу", + "micNotSendingDataTitle": "Таны микрофоныг системийн тохиргооноос хаасан байна", + "micPermissionDeniedError": "Та микрофон ашиглах зөвшөөрөл өгөөгүй байна. Та одоо чуулга уулзалтанд оролцож болох ч бусад хүмүүс таныг сонсох боломжгүй. Үүнийг засахын тулд хаягийн талбарт байгаа камерын товчийг ашиглана уу.", + "micUnknownError": "Ямар нэг шалтгааны улмаас микрофоныг ашиглах боломжгүй байна.", + "muteEveryoneElseDialog": "Бусад оролцогчдын дуу хаалттай үед та дууг нээх боломжгүй, гэхдээ өөрийнхөө дууг нээх боломжтой.", + "muteEveryoneElseTitle": "Бүх оролцогчдын дууг хаах уу?", + "muteEveryoneDialog": "Та бүх оролцогчдын дууг хаахдаа итгэлтэй байна уу? Та тэдний дууг идэвхжүүлэх боломжгүй ч хүссэн үедээ өөрийнхөө дуу нээх боломжтой.", + "muteEveryoneTitle": "Бүх оролцогчдын дууг хаах уу?", + "muteEveryoneSelf": "Чи өөрөө", + "muteEveryoneStartMuted": "Одооноос эхлэн бүх оролцогчдын дуу хаагдана", + "muteParticipantBody": "Та тэдний дууг идэвхжүүлэх боломжгүй ч хүссэн үедээ өөрийн дууг нээх боломжтой.", + "muteParticipantButton": "Дуугүй болгох", + "muteParticipantDialog": "Та энэ оролцогчийн дууг хаахдаа итгэлтэй байна уу? Та дууг идэвхжүүлэх боломжгүй ч хүссэн үедээ өөрийн дууг нээх боломжтой.", + "muteParticipantTitle": "Энэ оролцогчийн дууг хаах уу?", + "Ok": "Ok", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "$t(lockRoomPassword) тохируулах нь дэмжигдэхгүй байна.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) дэмжигдэхгүй байна", + "passwordRequired": "$t(lockRoomPasswordUppercase) заавал", + "popupError": "Таны хөтөч энэ сайтаас попап цонхуудыг блоклож байна. Хөтчөө аюулгүй байдлын тохиргоон дээр гарч ирэх цонхыг идэвхжүүлээд дахин оролдоно уу.", + "popupErrorTitle": "Попап хаалттай байна", + "recording": "Бичлэг хийх", + "recordingDisabledForGuestTooltip": "Зочид бичлэг хийж болохгүй.", + "recordingDisabledTooltip": "Бичлэг хийх хаалттай.", + "rejoinNow": "Одоо дахин нэгдэх", + "remoteControlAllowedMessage": "{{user}} алсын удирдлагын хүсэлтийг хүлээн авлаа!", + "remoteControlDeniedMessage": "{{user}} алсын удирдлагын хүсэлтээс татгалзлаа!", + "remoteControlErrorMessage": "Алсын удирдлагаас {{user}} зөвшөөрөл хүсэх үед алдаа гарлаа!", + "remoteControlRequestMessage": "Та {{user}} таны десктопоос алсын зайнаас удирдахыг зөвшөөрөх үү?", + "remoteControlShareScreenWarning": "Хэрэв та \"Allow\" товчийг дарвал, таны дэлгэцийг хуваалцах болно гэдгийг анхаарна уу!", + "remoteControlStopMessage": "Алсын удирдлагын хэсэг дууссан!", + "remoteControlTitle": "Алсын удирдлагын", + "Remove": "Устгах", + "removePassword": "$t(lockRoomPassword) устгах", + "removeSharedVideoMsg": "Та хуваалцсан видеогоо устгахдаа итгэлтэй байна уу?", + "removeSharedVideoTitle": "Хуваалцсан видеог устгах", + "reservationError": "Захиалгын системийн алдаа", + "reservationErrorMsg": "Алдааны код: {{code}}, мессеж: {{msg}}", + "retry": "Дахиж", + "screenSharingFailedToInstall": "Дэлгэц хуваалцах өргөтгөлийг суулгаж чадсангүй.", + "screenSharingFailedToInstallTitle": "Дэлгэц хуваалцах өргөтгөлийг суулгаж чадсангүй", + "screenSharingFirefoxPermissionDeniedError": "Таны дэлгэцийг хуваалцах үед ямар нэг алдаа гарлаа. Та бидэнд зөвшөөрөл өгсөн эсэхээ шалгана уу. ", + "screenSharingFirefoxPermissionDeniedTitle": "Дэлгэц хуваалцахыг эхлүүлэх боломжгүй байна!", + "screenSharingPermissionDeniedError": "Таны дэлгэцийг хуваалцах зөвшөөрлийг суулгахад алдаа гарсан байна. Файлыг дахин ачааллаад, дахин оролдоно уу.", + "sendPrivateMessage": "Хувийн мессеж хүлээн авсан. Та тусдаа хариу өгөх гэж байна уу, эсвэл та бүлэгт илгээх үү?", + "sendPrivateMessageCancel": "Бүлэгт илгээнэ үү", + "sendPrivateMessageOk": "Хувийн байдлаар илгээх", + "sendPrivateMessageTitle": "Хувиараа илгээх үү?", + "serviceUnavailable": "Үйлчилгээ авах боломжгүй", + "sessTerminated": "Дуудлага саллаа", + "Share": "Хуваалцах", + "shareVideoLinkError": "YouTube-ийн зөв линк оруулна уу.", + "shareVideoTitle": "Видео хуваалцах", + "shareYourScreen": "Дэлгэцээ хуваалцах", + "shareYourScreenDisabled": "Дэлгэц хуваалцах идэвхгүй", + "shareYourScreenDisabledForGuest": "Зочид дэлгэц хуваалцах боломжгүй.", + "startLiveStreaming": "Шууд дамжуулалтыг эхлүүл", + "startRecording": "Бичлэг хийж эхэл", + "startRemoteControlErrorMessage": "Алсын удирдлага эхлүүлэх үед алдаа гарлаа!", + "stopLiveStreaming": "Шууд дамжуулалтыг зогсоо", + "stopRecording": "Бичлэгээ зогсоо", + "stopRecordingWarning": "Та бичлэгээ зогсоохдоо итгэлтэй байна уу?", + "stopStreamingWarning": "Та шууд дамжуулалтыг зогсоохдоо итгэлтэй байна уу?", + "streamKey": "Шууд дамжуулалтын түлхүүр", + "Submit": "Илгээх", + "thankYou": "{{appName}} ашиглаж буй танд баярлалаа!", + "token": "token", + "tokenAuthFailed": "Уучлаарай, та энд нэвтрэх эрхгүй байна.", + "tokenAuthFailedTitle": "Нэвтрэлт амжилтгүй", + "transcribing": "Орчуулах", + "unlockRoom": "$t(lockRoomPassword) хурлыг устгана уу", + "userPassword": "хэрэглэгчийн нууц үг", + "WaitForHostMsg": "<b>{{room}}</b> хурал хараахан эхлээгүй байна. Хэрэв та хост байгаа бол нэвтэрнэ үү. Үгүй бол хост ирэхийг хүлээнэ үү.", + "WaitForHostMsgWOk": "<b>{{room}}</b> хурал хараахан эхлээгүй байна. Хэрэв та хост эзэмшигч бол баталгаажуулахын тулд Ok дээр дарна уу. Үгүй бол хост ирэхийг хүлээнэ үү.", + "WaitingForHost": "Хостыг хүлээж байна ...", + "Yes": "Тийм", + "yourEntireScreen": "Таны дэлгэцийг бүхэлдээ" + }, + "dialOut": { + "statusMessage": "одоо {{status}} байна" + }, + "documentSharing": { + "title": "Бичиг баримт хуваалцах" + }, + "feedback": { + "average": "Дундаж", + "bad": "Муу", + "detailsLabel": "Энэ талаар илүү дэлгэрэнгүй.", + "good": "Сайн", + "rateExperience": "Хурлын туршлагаа үнэлэх", + "veryBad": "Маш муу", + "veryGood": "Маш сайн" + }, + "incomingCall": { + "answer": "Хариул", + "audioCallTitle": "Дуудлага ирлээ", + "decline": "Гаргах", + "productLabel": "Сангийн Яам", + "videoCallTitle": "Видео дуудлага ирлээ" + }, + "info": { + "accessibilityLabel": "Мэдээллийг харуулах", + "addPassword": "$t(lockRoomPassword) нэмэх", + "cancelPassword": "$t(lockRoomPassword) цуцлах", + "conferenceURL": "Холбоос:", + "country": "Улс", + "dialANumber": "Уулзалтанд оролцохын тулд эдгээр дугааруудын аль нэгрүү нь залгаад, пинг оруулна уу.", + "dialInConferenceID": "PIN:", + "dialInNotSupported": "Уучлаарай, одоогоор дуудлага хийх боломжгүй байна.", + "dialInNumber": "Дуудлага хийж байна:", + "dialInSummaryError": "Залгаж мэдээллийг авах үед алдаа гарлаа. Дараа дахин оролдож үзнэ үү.", + "dialInTollFree": "Toll Free", + "genericError": "Ямар нэг асуудал гарлаа.", + "inviteLiveStream": "Энэ уулзалтын шууд дамжуулалтыг үзэхийн тулд энэ холбоосыг дарна уу: {{url}}", + "invitePhone": "Утсаар холбогдохын тулд энэ дээр дарна уу: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Өөр залгах дугаар хайж байна уу?\nУулзалтын дугаарыг харах: {{url}}\n\n\nХэрэв өрөөний утас руу залгах бол аудиод холбогдохгүйгээр нэгдээрэй: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Та бүхнийг уулзалтад урьж байна.", + "inviteURLFirstPartPersonal": "{{name}} таныг уулзалтанд урьж байна.\n", + "inviteURLSecondPart": "\nУулзалтад нэгдээрэй:\n{{url}}\n", + "liveStreamURL": "Шууд дамжуулалт:", + "moreNumbers": "Бусад дугаар", + "noNumbers": "Залгах дугаар байхгүй байна.", + "noPassword": "Хоосон", + "noRoom": "Нэвтрэх өрөө олдсонгүй.", + "numbers": "Залгах дугаарууд", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Хуваалцах", + "tooltip": "Хурлын холбоосыг хуваалцах", + "label": "Хурлын мэдээлэл" + }, + "inviteDialog": { + "alertText": "Зарим оролцогчдыг урьж чадсангүй.", + "header": "Урих", + "searchCallOnlyPlaceholder": "Утасны дугаараа оруулна уу", + "searchPeopleOnlyPlaceholder": "Оролцогчдыг хайх", + "searchPlaceholder": "Оролцогч эсвэл утасны дугаар", + "send": "Илгээх" + }, + "inlineDialogFailure": { + "msg": "Асуудал гарлаа", + "retry": "Дахин оролд", + "support": "Дэмжлэг", + "supportMsg": "Хэрэв ийм асуудал дахиад тохиолдвол холбоо бариарай" + }, + "keyboardShortcuts": { + "focusLocal": "Өөрийн видеог үзэх", + "focusRemote": "Өөр хүний видеог үзэх", + "fullScreen": "Дэлгэц дүүргэх эсвэл гарах", + "keyboardShortcuts": "Гарны товчлол", + "localRecording": "Өөрийн бичлэгийн хяналтыг үзүүлэх эсвэл нуух", + "mute": "Микрофоныг хаах эсвэл нээх", + "pushToTalk": "Яриа руу", + "raiseHand": "Гараа өргөх эсвэл доошлуул", + "showSpeakerStats": "Яригчийн статистик харах", + "toggleChat": "Зурвасыг нээх буюу хаах", + "toggleFilmstrip": "Видео бичлэгийн өнгөц зургийг үзүүлэх эсвэл нуух", + "toggleScreensharing": "Камер ба дэлгэц хуваалцах хооронд шилжинэ", + "toggleShortcuts": "Товчлолуудыг харуулах эсвэл нуух", + "videoMute": "Камераа эхлүүлэх эсвэл зогсоох", + "videoQuality": "Видеоны чанарыг удирдах" + }, + "liveStreaming": { + "busy": "Бид урсгалын чөлөөлөхөөр ажиллаж байна. Хэдэн минутын дараа дахин оролдоно уу.", + "busyTitle": "Бүх дамжуулагчид завгүй байна", + "changeSignIn": "Бүртгэл солих.", + "choose": "Шууд дамжуулалтыг сонгоно уу", + "chooseCTA": "Дамжуулалтыг сонгоно уу. Та {{email}} ашиглан нэвтэрсэн байна.", + "enterStreamKey": "YouTube шууд дамжуулалтын түлхүүрийг энд оруулна уу.", + "error": "Шууд дамжуулалт амжилтгүй болсон. Дахин оролдоно уу.", + "errorAPI": "Таны YouTube рүү нэвтрэх үед алдаа гарлаа. Дараа дахин нэвтэрнэ үү.", + "errorLiveStreamNotEnabled": "Шууд дамжуулалт {{email}} дээр идэвхжүүлээгүй байна. Шууд дамжуулалтыг идэвхжүүл эсвэл шууд дамжуулалт хийх боломжтой бүртгэлээр нэвтэрнэ үү.", + "expandedOff": "Шууд дамжуулалт зогссон байна", + "expandedOn": "Энэ хурлыг YouTube дээр шууд дамжуулж байна.", + "expandedPending": "Шууд дамжуулалтыг эхлүүлж байна...", + "failedToStart": "Шууд дамжуулалтыг эхлүүлж чадсангүй", + "getStreamKeyManually": "Шууд дамжуулалтыг хийх чадсангүй. YouTube-ээс шууд дамжуулалтын түлхүүрээ шалгаж үзээрэй.", + "invalidStreamKey": "Шууд дамжуулалтын түлхүүрээ шалгана уу.", + "off": "Шууд дамжуулалт зогссон", + "offBy": "{{name}} шууд дамжуулалтыг зогсоосон", + "on": "Шууд дамжуулалт", + "onBy": "{{name}} шууд дамжуулалт эхлүүлсэн", + "pending": "Шууд дамжуулалтыг эхлүүлж байна...", + "serviceName": "Шууд дамжуулалт үйлчилгээ", + "signedInAs": "Та одоо нэвтэрсэн байна:", + "signIn": "Google-р нэвтрэх", + "signInCTA": "YouTube дээр шууд дамжуулалтын түлхүүрээ оруулна уу.", + "signOut": "Гарах", + "start": "Шууд дамжуулалт эхлүүл", + "streamIdHelp": "Энэ юу вэ?", + "unavailableTitle": "Шууд дамжуулалт боломжгүй" + }, + "localRecording": { + "clientState": { + "off": "Off", + "on": "On", + "unknown": "Танигдаагүй" + }, + "dialogTitle": "Дотоод бичлэгийн хяналт", + "duration": "Үргэлжлэх хугацаа", + "durationNA": "N/A", + "encoding": "Encoding", + "label": "LOR", + "labelToolTip": "Дотоод бичлэг хийнэ", + "localRecording": "Дотоод бичлэг", + "me": "Надад", + "messages": { + "engaged": "Дотоод бичлэг хийдэг", + "finished": "Recording session {{token}} finished. Please send the recorded file to the moderator.", + "finishedModerator": "Recording session {{token}} finished. The recording of the local track has been saved. Please ask the other participants to submit their recordings.", + "notModerator": "Та зохицуулагч биш. Та дотоод бичлэгийг эхлүүлэх эсвэл зогсоох боломжгүй." + }, + "moderator": "Зохицуулагч", + "no": "Үгүй", + "participant": "Оролцогч", + "participantStats": "Оролцогчдын статистик", + "sessionToken": "Идэвхтэй Токен", + "start": "Бичлэг хийх", + "stop": "Бичлэг болих", + "yes": "Тийм" + }, + "lockRoomPassword": "нууц үг", + "lockRoomPasswordUppercase": "Нууц үг", + "me": "Надад", + "notify": { + "connectedOneMember": "{{name}} хуралд нэгдсэн", + "connectedThreePlusMembers": "{{name}} болон бусад {{count}} хуралд нэгдсэн", + "connectedTwoMembers": "{{first}} ба {{second}} хуралд нэгдсэн", + "disconnected": "Хурлаас гарлаа", + "focus": "Хурал фокус", + "focusFail": "{{component}} боломжгүй - {{ms}} сек дараа дахин оролдоно уу", + "grantedTo": "{{to}} зохицуулагч эрх өгөгдсөн!", + "invitedOneMember": "{{name}} урьсан байна", + "invitedThreePlusMembers": "{{name}} болон бусад {{count}} урьсан байна", + "invitedTwoMembers": "{{first}} ба {{second}} урьсан байна", + "kickParticipant": "{{kicked}} -г {{kicker}} гаргасан", + "me": "Надад", + "moderator": "Зохицуулагчийн эрх олгосон!", + "muted": "Та харилцан яриаг хаасан байна.", + "mutedTitle": "Таны дуу хаалтай байна!", + "mutedRemotelyTitle": "Таны дууг {{participantDisplayName}} хаасан байна!!", + "mutedRemotelyDescription": "Та ярихад бэлэн үедээ дууг нээж ярих боломжтой. Уулзалтад дуу чимээ гаргахгүй байхын тулд дуугаа хаагаарай.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) өөр оролцогч устгасан", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) өөр оролцогчоос хийсэн", + "raisedHand": "{{name}} ярихыг хүсч байна.", + "somebody": "Хэн нэгэн", + "startSilentTitle": "Та ямар ч аудио төхөөрөмжгүй нэгдсэн!", + "startSilentDescription": "Дууг идэвхжүүлэхийн тулд хуралд дахин нэгдээрэй", + "suboptimalBrowserWarning": "Таны хөтөч дээр бүрэн дэмжлэг ороогүй байна. Та бүрэн дэмжигдсэн <a href='static/recommendedBrowsers.html' target='_blank'>хөтөчийг</a> ашиглана уу.", + "suboptimalExperienceTitle": "Хөтөчийн анхааруулга", + "unmute": "Дуутай", + "newDeviceCameraTitle": "Шинэ камер", + "newDeviceAudioTitle": "Шинэ аудио төхөөрөмж", + "newDeviceAction": "Хэрэглэх" + }, + "passwordSetRemotely": "өөр оролцогч хийсэн", + "passwordDigitsOnly": "{{number}} хүртэлх тоо", + "poweredby": "дэмжигдсэн", + "presenceStatus": { + "busy": "Завгүй", + "calling": "Дуудаж байна...", + "connected": "Холбогдсон", + "connecting": "Холбож байна...", + "connecting2": "Холбож байна*...", + "disconnected": "Холбогдоогүй", + "expired": "Хугацаа нь дууссан", + "ignored": "Ignored", + "initializingCall": "Дуудлагыг эхлүүлж байна...", + "invited": "Урьсан", + "rejected": "Татгалзсан", + "ringing": "Дуудаж байна..." + }, + "profile": { + "setDisplayNameLabel": "Нэрээ оруулна уу", + "setEmailInput": "И-мэйл оруулна уу", + "setEmailLabel": "И-мэйл хаягаа оруулна уу", + "title": "Профайл" + }, + "raisedHand": "Ярьмаар байна", + "recording": { + "authDropboxText": "Dropbox байршуулах", + "availableSpace": "Боломжтой зай: {{spaceLeft}} MB (ойролцоогоор {{duration}} минутын бичлэг)", + "beta": "BETA", + "busy": "Бичлэгийн нөөцийг сул болгохоор ажиллаж байна. Хэдэн минутын дараа дахин оролдоно уу.", + "busyTitle": "Бүх бичигчид завгүй байна", + "error": "Бичлэг хийх амжилтгүй боллоо. Дахин оролдоно уу.", + "expandedOff": "Бичлэг хийх зогссон", + "expandedOn": "Хурлыг одоо тэмдэглэж байна.", + "expandedPending": "Бичлэгийг хийж байна...", + "failedToStart": "Бичлэг хийх амжилтгүй боллоо", + "fileSharingdescription": "Бичлэгийг хурлын оролцогчидтой хуваалцах", + "live": "ШУУД", + "loggedIn": "{{userName}} нэвтэрнэ үү", + "off": "Бичлэг зогссон", + "offBy": "{{name}} бичлэгийг зогсоосон", + "on": "Бичиж байна", + "onBy": "{{name}} бичлэг хийж эхлэв", + "pending": "Хурлыг бичихээр бэлтгэж байна...", + "rec": "REC", + "serviceDescription": "Таны бичлэгийг хадгална", + "serviceName": "Бичлэгийн үйлчилгээ", + "signIn": "Нэвтрэх", + "signOut": "Гарах", + "unavailable": "{{serviceName}} одоогоор ажиллахгүй байна. Бид асуудлыг шийдвэрлэхээр ажиллаж байна. Дараа дахин оролдож үзнэ үү.", + "unavailableTitle": "Бичлэг хийх боломжгүй" + }, + "sectionList": { + "pullToRefresh": "Шинэчлэх бол татна уу" + }, + "settings": { + "calendar": { + "about": "The {{appName}} calendar integration is used to securely access your calendar so it can read upcoming events.", + "disconnect": "Салгалаа", + "microsoftSignIn": "Sign in with Microsoft", + "signedIn": "Currently accessing calendar events for {{email}}. Click the Disconnect button below to stop accessing calendar events.", + "title": "Цагалбар" + }, + "devices": "Төхөөрөмжүүд", + "followMe": "Бүгд намайг дагаж байна", + "language": "Хэл", + "loggedIn": "{{name}} нэвтэрсэн", + "moderator": "Зохицуулагч", + "more": "Цааш", + "name": "Нэр", + "noDevice": "Байхгүй", + "selectAudioOutput": "Аудио гаралт", + "selectCamera": "Камер", + "selectMic": "Микрофон", + "startAudioMuted": "Бүгд дуугүй эхлэх", + "startVideoMuted": "Бүгд дүрсгүй эхлэх", + "title": "Тохиргоо" + }, + "settingsView": { + "advanced": "Нарийвчилсан", + "alertOk": "OK", + "alertTitle": "Анхааруулга", + "alertURLText": "Оруулсан серверийн URL хүчингүй байна", + "buildInfoSection": "Мэдээлэл оруулах", + "conferenceSection": "Хурал", + "disableCallIntegration": "Үндсэн дуудлагад өгөх тохиргоог идэвхгүй болгох", + "disableP2P": "Peer-To-Peer горим идэвхгүй болгох", + "displayName": "Дэлгэц нэр", + "email": "Имэйл", + "header": "Тохиргоо", + "profileSection": "Профайл", + "serverURL": "Серверийн URL", + "showAdvanced": "Нарийвчилсан тохиргоог харуулах", + "startWithAudioMuted": "Аудио дуугүй болгох", + "startWithVideoMuted": "Видеог дууг хаах", + "version": "Хувилбар" + }, + "share": { + "dialInfoText": "\n\n=====\n\nУтсан дээрээ залгахыг хүсч байна уу?\n\n{{defaultDialInNumber}}Энэ уулзалтын утасны дугаарлуу залгахыг харахын тулд энэ холбоос дээр дарна уу\n{{dialInfoPageUrl}}", + "mainText": "Уулзалтанд нэгдэхийн тулд дараах холбоосыг дарна уу:\n{{roomUrl}}" + }, + "speaker": "Яригч", + "speakerStats": { + "hours": "{{count}}ц", + "minutes": "{{count}}м", + "name": "Нэр", + "seconds": "{{count}}с", + "speakerStats": "Яригчийн статистик", + "speakerTime": "Яригчийн цаг" + }, + "startupoverlay": { + "policyText": " ", + "title": "{{app}} нь таны микрофон болон камерыг ашиглана." + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Дахин нэгдэх", + "text": "Дахин холбохын тулд <i>Rejoin</i> товчийг дарна уу.", + "title": "Энэ компьютер унтарсан учир таны видео дуудлага тасарлаа." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Зөвхөн аудио", + "audioRoute": "Дууны төхөөрөмж сонго", + "callQuality": "Видеоны чанар", + "cc": "Хадмал орчуулга", + "chat": "Зурвасын цонх", + "document": "Бичиг баримт хуваалцах", + "download": "Манай програмуудыг татах", + "feedback": "Санал хүсэлтээ үлдээх", + "fullScreen": "Бүтэн дэлгэц", + "hangup": "Салгах", + "help": "Тусламж", + "invite": "Хүмүүсийг урих", + "kick": "Оролцогчийг гаргах", + "localRecording": "Дотоод бичлэгийн хяналт", + "lockRoom": "Нууц үг өгөх", + "moreActions": "Бусад цэс", + "moreActionsMenu": "Бусад цэс", + "moreOptions": "Илүү сонголт", + "mute": "Дууг хаах", + "muteEveryone": "Бүх дууг хаах", + "pip": "Зураг зураг горим", + "privateMessage": "Хувийн зурвас илгээх", + "profile": "Өөрийн профайлыг засах", + "raiseHand": "Гараа өргө", + "recording": "Бичлэг хийх", + "remoteMute": "Оролцогч дууг хаах", + "Settings": "Тохиргоо", + "sharedvideo": "Youtube-н видео хуваалцах", + "shareRoom": "Хүн урих", + "shareYourScreen": "Дэлгэц хуваалцах", + "shortcuts": "Товчлол", + "show": "Үзүүлэх", + "speakerStats": "Яригчийн статистик", + "tileView": "Зэрэгцүүлж харах", + "toggleCamera": "Камер", + "videomute": "Дүрсгүй видео", + "videoblur": "Видео бүдэгрүүлэх" + }, + "addPeople": "Таны дуудлагад хүн нэмэх", + "audioOnlyOff": "Бага дамжуулах горимыг идэвхгүй болгох", + "audioOnlyOn": "Бага дамжуулах горимыг идэвхжүүлэх", + "audioRoute": "Дууны төхөөрөмж сонгох", + "authenticate": "Нэвтрэлт", + "callQuality": "Видеоны чанар", + "chat": "Зурвас нээх/хаах", + "closeChat": "Зурвас хаах", + "documentClose": "Хуваалцсан бичиг баримт хаах", + "documentOpen": "Хуваалцсан бичиг баримт нээх", + "download": "Манай програмуудыг татах", + "enterFullScreen": "Бүтэн дэлгэцээр харах", + "enterTileView": "Зэрэгцүүлж харах", + "exitFullScreen": "Бүтэн дэлгэцээс гарах", + "exitTileView": "Зэрэгцүүлж харах болих", + "feedback": "Санал хүсэлтээ үлдээх", + "hangup": "Салгах", + "help": "Тусламж", + "invite": "Хүмүүс урих", + "login": "Нэвтрэх", + "logout": "Гарах", + "lowerYourHand": "Гараа болих", + "moreActions": "Бусад үйлдэл", + "moreOptions": "Бусад тохиргоо", + "mute": "Дуу хаах/нээх", + "muteEveryone": "Бүх дууг хаах", + "noAudioSignalTitle": "Таны микрофоноос оруулах оролт байхгүй байна!", + "noAudioSignalDesc": "Хэрэв та системийн тохиргоо эсвэл техник хангамжаас үүнийг идэвхгүй болгоогүй бол төхөөрөмжийг солиж үзнэ үү.", + "noAudioSignalDescSuggestion": "Хэрэв та системийн тохиргоо эсвэл техник хангамжаас үүнийг идэвхгүй болгоогүй бол санал болгосон төхөөрөмжид шилжүүлнэ үү.", + "noAudioSignalDialInDesc": "Залгаж ашиглаж болно:", + "noAudioSignalDialInLinkDesc": "Залгах дугаарууд", + "noisyAudioInputTitle": "Таны микрофон шуугиантай байна!", + "noisyAudioInputDesc": "Таны микрофон шуугиж байна, дууг нь хаах эсвэл өөрчилнө үү.", + "openChat": "Зурвас нээлттэй", + "pip": "Зураг-зураг горим оруулах", + "privateMessage": "Хувийн зурвас илгээх", + "profile": "Профайлаа засна уу", + "raiseHand": "Гараа өргөх/болих", + "raiseYourHand": "Гараа өргө", + "Settings": "Тохиргоо", + "sharedvideo": "YouTube-н видео хуваалцах", + "shareRoom": "Хэн нэгнийг урих", + "shortcuts": "Товчлол харах", + "speakerStats": "Яригчийн статистик", + "startScreenSharing": "Дэлгэц хуваалцахыг эхлүүл", + "startSubtitles": "Хадмал орчуулгыг эхлүүл", + "stopScreenSharing": "Дэлгэц хуваалцахыг зогсоох", + "stopSubtitles": "Хадмал орчуулгыг зогсоох", + "stopSharedVideo": "YouTube видео зогсоох", + "talkWhileMutedPopup": "Ярьж үзмээр байна уу? Дуу хаалттай байна.", + "tileViewToggle": "Зэрэгцүүлж харах", + "toggleCamera": "Камер", + "videomute": "Камер нээх/хаах", + "startvideoblur": "Дэвсгэрийг бүдгэрүүл", + "stopvideoblur": "Бүдэгрүүлэлтийг болиулах" + }, + "transcribing": { + "ccButtonTooltip": "Хадмал орчуулгыг харуулах/болих", + "error": "Орчуулга амжилтгүй боллоо. Дахин оролдоно уу.", + "expandedLabel": "Хадмал орчуулгыг бичиж байна", + "failedToStart": "Хадмал орчуулга эхлүүлж чадсангүй", + "labelToolTip": "Хурлын хадмал орчуулга", + "off": "Хадмал орчуулга больсон", + "pending": "Хурлын хадмал орчуулга хийхэд бэлдэж байна...", + "start": "Хадмал орчуулгыг харуулах", + "stop": "Хадмал орчуулгыг болиул", + "tr": "TR" + }, + "userMedia": { + "androidGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Allow</i></b> дарна уу.", + "chromeGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Allow</i></b> дарна уу.", + "edgeGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Yes</i></b> дарна уу.", + "electronGrantPermissions": "Камер болон микрофон ашиглах зөвшөөрөл өгнө үү", + "firefoxGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Share Selected Device</i></b> дарна уу.", + "iexplorerGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>OK</i></b> дарна уу.", + "nwjsGrantPermissions": "Камер болон микрофон ашиглах зөвшөөрөл өгнө үү", + "operaGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Allow</i></b> дарна уу.", + "react-nativeGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>Allow</i></b> дарна уу.", + "safariGrantPermissions": "Хөтөчөөс зөвшөөрөл хүсэх тохиолдолд <b><i>OK</i></b> дарна уу." + }, + "videoSIPGW": { + "busy": "Бид нөөцийг чөлөөлөхөөр ажиллаж байна. Хэдэн минутын дараа дахин оролдоно уу.", + "busyTitle": "Үйлчилгээ одоогоор завгүй байна", + "errorAlreadyInvited": "{{displayName}} урьсан байна", + "errorInvite": "Хурал хараахан эхлээгүй байна. Дараа дахин оролдож үзнэ үү.", + "errorInviteFailed": "Бид асуудлыг шийдвэрлэхээр ажиллаж байна. Дараа дахин оролдож үзнэ үү.", + "errorInviteFailedTitle": "{{displayName}} урилга амжилтгүй болсон", + "errorInviteTitle": "Хуралд урихад алдаа гарлаа", + "pending": "{{displayName}} урьсан байна" + }, + "videoStatus": { + "audioOnly": "AUD", + "audioOnlyExpanded": "Таны дамжуулах чадвар муу байна. Энэ горимд та зөвхөн аудио болон дэлгэцийн хуваалцахыг хүлээн авах боломжтой.", + "callQuality": "Видеоны чанар", + "hd": "HD", + "hdTooltip": "Өндөр нягтаршилтай видеог үзэж байна", + "highDefinition": "Өндөр нягтаршил", + "labelTooiltipNoVideo": "Видео байхгүй", + "labelTooltipAudioOnly": "Дамжуулалтын багтаамж бага горим идэвхжсэн", + "ld": "LD", + "ldTooltip": "Бага нягтаршилтай видео үзэж байна", + "lowDefinition": "Бага нягтаршил", + "onlyAudioAvailable": "Зөвхөн аудио ашиглах боломжтой", + "onlyAudioSupported": "Энэ хөтчид аудио ашиглах боломжтой.", + "p2pEnabled": "P2P идэвхтэй", + "p2pVideoQualityDescription": "P2P горим дээр зөвхөн өндөр нягтаршилтай видео болон аудио хооронд л шилжүүлэх боломжтой. P2P гарах хүртэл бусад тохиргоог хийх боломжгүй.", + "recHighDefinitionOnly": "Өндөр нягтаршилтайг илүүд үздэг.", + "sd": "SD", + "sdTooltip": "Стандарт нарийвчлалтай видео үзэж байна", + "standardDefinition": "Стандарт нягтаршил" + }, + "videothumbnail": { + "domute": "Дуу хаах", + "domuteOthers": "Бүх дууг хаах", + "flip": "Урсах", + "kick": "Гаргах", + "moderator": "Зохицуулагч", + "mute": "Оролцогч дуугүй байна", + "muted": "Дуугүй", + "remoteControl": "Алсын удирдлагыг эхлүүлэх / зогсоох", + "show": "Үзүүлэх", + "videomute": "Оролцогч камераа зогсоосон байна" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Товшоод нэгдэх", + "roomname": "Хурлын нэрийг оруулна уу" + }, + "appDescription": "Таньдаг бүх хүмүүсийг урь. {{app}} нь бүрэн шифрлэгдсэн, 100% нээлттэй, видео хурлын шийдэл бөгөөд та өдөр бүр үнэ төлбөргүй ашиглаж болно.", + "audioVideoSwitch": { + "audio": "Дуутай", + "video": "Видео" + }, + "calendar": "Цагалбар", + "connectCalendarButton": "Цагалбараа холбоно уу", + "connectCalendarText": "Цагалбар дээрх уулзалтуудаа {{app}} дээр оруулж болно. Та цагалбараа холбоход болно.", + "enterRoomTitle": "Шинэ хурал эхлүүлэх", + "roomNameAllowedChars": "Хурлын нэр нь эдгээр тэмдэгтүүдийн аль нэгийг агуулж болохгүй: ?, &, :, ', \", %, #.", + "go": "OK", + "goSmall": "OK", + "join": "ҮҮСГЭХ / НЭГДЭХ", + "info": "Мэдээлэл", + "privacy": "Нууцлал", + "recentList": "Онцлох", + "recentListDelete": "Устгах", + "recentListEmpty": "Таны жагсаалт хоосон байна. Таны хийсэн бүх хурлууд энд байна.", + "reducedUIText": "{{app}}, тавтай морил!", + "roomname": "Хурлын нэрийг оруулна уу", + "roomnameHint": "Нэгдэхийг хүсч буй хурлын нэр, URL-ийг оруулна уу. Та хурлын нэрээ үүсгэж болно. хуралд оролцох хүмүүстээ тэр нэрийг өгөөрэй.", + "sendFeedback": "Санал илгээх", + "terms": "Нөхцөлүүд", + "title": "Аюулгүй, үнэгүй видео хурал хийх боломжтой" + }, + "lonelyMeetingExperience": { + "button": "Бусдыг урь", + "youAreAlone": "Та энэ хуралд ганцаараа байна" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-nl.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-nl.json index 04614beb5..b560bb125 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-nl.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-nl.json @@ -21,16 +21,16 @@ "bluetooth": "Bluetooth", "headphones": "Hoofdtelefoon", "phone": "Telefoon", - "speaker": "Spreker" + "speaker": "Speaker" }, "audioOnly": { "audioOnly": "Alleen audio" }, "calendarSync": { "addMeetingURL": "Een link naar de vergadering toevoegen", - "confirmAddLink": "Wilt u een Jitsi-link naar deze gebeurtenis toevoegen?", + "confirmAddLink": "Wilt u een Jitsi-link aan deze gebeurtenis toevoegen?", "error": { - "appConfiguration": "De Agenda-integratie is niet naar behoren geconfigureerd.", + "appConfiguration": "De agenda-integratie is niet juist ingesteld.", "generic": "Er is een fout opgetreden. Controleer de agenda-instellingen of vernieuw de agenda.", "notSignedIn": "Er is een fout opgetreden tijdens de verificatie voor het weergeven van agendagebeurtenissen. Controleer de agenda-instellingen en probeer u opnieuw aan te melden." }, @@ -40,21 +40,26 @@ "noEvents": "Er zijn geen gebeurtenissen gepland.", "ongoingMeeting": "actieve vergadering", "permissionButton": "Instellingen openen", - "permissionMessage": "U hebt een machtiging voor Agenda nodig om uw vergaderingen weer te geven in de app.", + "permissionMessage": "U hebt een machtiging voor Agenda nodig om uw afspraken weer te geven in de app.", "refresh": "Agenda vernieuwen", "today": "Vandaag" }, "chat": { "error": "Fout: uw bericht \"{{originalText}}\" is niet verzonden. Reden: {{error}}", + "fieldPlaceHolder": "Type hier je bericht", "messagebox": "Typ een bericht", + "messageTo": "Privébericht aan {{recipient}}", + "noMessagesMessage": "Er zijn nog geen berichten in deze bijkeenkomst. Begin een gesprek!", "nickname": { "popover": "Kies een bijnaam", "title": "Voer een bijnaam in om de chatfunctie te gebruiken" }, - "title": "Chat" + "privateNotice": "Privébericht aan {{recipient}}", + "title": "Chat", + "you": "jij" }, "connectingOverlay": { - "joiningRoom": "Er wordt verbinding gemaakt met de vergadering..." + "joiningRoom": "Er wordt verbinding gemaakt met de vergadering…" }, "connection": { "ATTACHED": "Bijgesloten", @@ -111,7 +116,7 @@ "downloadApp": "De app downloaden", "launchWebButton": "Starten via web", "openApp": "Doorgaan naar de app", - "title": "Uw vergadering wordt gestart in {{app}}...", + "title": "Uw vergadering wordt gestart in {{app}}…", "tryAgainButton": "Opnieuw proberen op desktop" }, "defaultLink": "bijv. {{url}}", @@ -132,7 +137,7 @@ "liveStreaming": "Livestream" }, "allow": "Toestaan", - "alreadySharedVideoMsg": "Er wordt al een video gedeeld door een ander lid. In deze vergadering kan slechts één video tegelijkertijd worden gedeeld.", + "alreadySharedVideoMsg": "Er wordt al een video gedeeld door een andere deelnemer. In deze vergadering kan slechts één video tegelijkertijd worden gedeeld.", "alreadySharedVideoTitle": "Slechts één gedeelde video tegelijkertijd toegestaan", "applicationWindow": "Toepassingsvenster", "Back": "Terug", @@ -145,9 +150,9 @@ "cameraUnsupportedResolutionError": "De camera biedt geen ondersteuning voor de vereiste videoresolutie.", "Cancel": "Annuleren", "close": "Sluiten", - "conferenceDisconnectMsg": "Controleer de netwerkverbinding. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...", + "conferenceDisconnectMsg": "Controleer de netwerkverbinding. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken…", "conferenceDisconnectTitle": "De verbinding is verbroken.", - "conferenceReloadMsg": "We proberen het probleem op te lossen. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...", + "conferenceReloadMsg": "We proberen het probleem op te lossen. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken…", "conferenceReloadTitle": "Er is iets misgegaan.", "confirm": "Bevestigen", "confirmNo": "Nee", @@ -176,10 +181,10 @@ "kickMessage": "U bent uit de vergadering verwijderd.", "kickParticipantButton": "Verwijderen", "kickParticipantDialog": "Weet u zeker dat u deze deelnemer wilt verwijderen?", - "kickParticipantTitle": "Dit lid verwijderen?", + "kickParticipantTitle": "Deze deelnemer verwijderen?", "kickTitle": "Verwijderd uit vergadering", "liveStreaming": "Livestream", - "liveStreamingDisabledForGuestTooltip": "Gasten kunnen livestream niet starten.", + "liveStreamingDisabledForGuestTooltip": "Gasten kunnen de livestream niet starten.", "liveStreamingDisabledTooltip": "Het starten van de livestream is uitgeschakeld.", "lockMessage": "Het vergrendelen van de vergadering is mislukt.", "lockRoom": "Wachtwoord voor vergadering toevoegen", @@ -188,7 +193,7 @@ "logoutTitle": "Afmelden", "maxUsersLimitReached": "Het maximale aantal leden is bereikt. De vergadering is vol. Neem contact op met de eigenaar van de vergadering of probeer het later opnieuw.", "maxUsersLimitReachedTitle": "Maximaal aantal leden bereikt", - "micConstraintFailedError": "Uw microfoon voldoet niet aan alle vereiste beperkingen.", + "micConstraintFailedError": "Uw microfoon voldoet niet aan alle vereiste randvoorwaarden.", "micNotFoundError": "Microfoon niet gevonden.", "micNotSendingData": "Er is geen toegang tot uw microfoon verkregen. Selecteer een ander apparaat in de instellingen of laad de toepassing opnieuw.", "micNotSendingDataTitle": "Geen toegang tot de microfoon", @@ -196,8 +201,8 @@ "micUnknownError": "Kan de microfoon om een onbekende reden niet gebruiken.", "muteParticipantBody": "U kunt het dempen van anderen niet opheffen, maar zij kunnen dit wel op elk gewenst moment voor zichzelf doen.", "muteParticipantButton": "Dempen", - "muteParticipantDialog": "Weet u zeker dat u deze deelnemer wilt dempen? U kunt het dempen niet opheffen, maar dit kan deze persoon wel op elk gewenst moment zelf doen.", - "muteParticipantTitle": "Dit lid dempen?", + "muteParticipantDialog": "Weet u zeker dat u deze deelnemer wilt dempen? U kunt het dempen niet opheffen, maar deze deelnemer kan dat wel op elk gewenst moment zelf doen.", + "muteParticipantTitle": "Deze deelnemer dempen?", "Ok": "OK", "passwordLabel": "Wachtwoord", "passwordNotSupported": "Het instellen van een wachtwoord voor een vergadering wordt niet ondersteund.", @@ -227,7 +232,7 @@ "screenSharingFailedToInstallTitle": "Installatie van extensie voor het delen van het scherm is mislukt", "screenSharingFirefoxPermissionDeniedError": "Er is iets misgegaan tijdens het delen van uw scherm. Controleer of u hier toestemming voor hebt verleend.", "screenSharingFirefoxPermissionDeniedTitle": "Scherm delen kan niet worden gestart.", - "screenSharingPermissionDeniedError": "Er is iets misgegaan met de machtigingen voor de extensie voor het delen van het scherm. Laad de toepassing opnieuw en probeer het nog eens.", + "screenSharingPermissionDeniedError": "Er is iets misgegaan met de permissies voor het delen van het scherm. Laad de toepassing opnieuw en probeer het nog eens.", "serviceUnavailable": "Service niet beschikbaar", "sessTerminated": "Gesprek beëindigd", "Share": "Delen", @@ -254,7 +259,7 @@ "userPassword": "gebruikerswachtwoord", "WaitForHostMsg": "De vergadering <b>{{room}}</b> is nog niet gestart. Verifieer de vergadering als u de host bent. Anders wacht u tot de host aanwezig is.", "WaitForHostMsgWOk": "De vergadering <b>{{room}}</b> is nog niet gestart. Als u de host bent, drukt u op OK om te verifiëren. Anders wacht u tot de host aanwezig is.", - "WaitingForHost": "Wachten op de host...", + "WaitingForHost": "Wachten op de host…", "Yes": "Ja", "yourEntireScreen": "Uw gehele scherm" }, @@ -332,13 +337,13 @@ "raiseHand": "Uw hand opsteken of laten zakken", "showSpeakerStats": "Sprekerstatistieken weergeven", "toggleChat": "Chatgesprek openen of sluiten", - "toggleFilmstrip": "Videominiaturen weergeven of verbergen", + "toggleFilmstrip": "Toon of verberg videominiaturen", "toggleScreensharing": "Schakelen tussen camera en het delen van het scherm", "toggleShortcuts": "Sneltoetsen weergeven of verbergen", "videoMute": "Uw camera starten of stoppen" }, "liveStreaming": { - "busy": "Er worden streamingresources vrijgemaakt. Probeer het over enkele minuten opnieuw.", + "busy": "We werken aan het vrijmaken van streaming-middelen. Probeer het over enkele minuten opnieuw.", "busyTitle": "Alle streamers zijn momenteel bezet", "changeSignIn": "Wissel van account.", "choose": "Een livestream kiezen", @@ -349,13 +354,13 @@ "errorLiveStreamNotEnabled": "Livestreaming is niet ingeschakeld voor {{email}}. Schakel livestreaming in of meld u aan bij een account waarvoor livestreaming is ingeschakeld.", "expandedOff": "De livestream is gestopt", "expandedOn": "De vergadering wordt momenteel gestreamd naar YouTube.", - "expandedPending": "De livestream wordt gestart...", + "expandedPending": "De livestream wordt gestart…", "failedToStart": "Livestream niet gestart", "getStreamKeyManually": "Er zijn geen livestreams opgehaald. Haal de sleutel voor uw livestream op uit YouTube.", "invalidStreamKey": "De sleutel voor de livestream is mogelijk onjuist.", "off": "Livestream gestopt", "on": "Livestream", - "pending": "Livestream starten...", + "pending": "Livestream starten…", "serviceName": "Livestreamservice", "signedInAs": "U bent momenteel aangemeld als:", "signIn": "Aanmelden via Google", @@ -421,34 +426,34 @@ "somebody": "Iemand", "startSilentTitle": "", "startSilentDescription": "", - "suboptimalExperienceDescription": "Helaas zal uw {{appName}}-ervaring hier niet optimaal zijn. Mogelijk wordt dit in de toekomst verbeterd, maar tot die tijd vragen we u een van de <a href='static/recommendedBrowsers.html' target='_blank'>volledig ondersteunde browsers</a> te gebruiken.", + "suboptimalExperienceDescription": "Helaas zal uw {{appName}}-ervaring hier niet optimaal zijn. We proberen dit in de toekomst te verbeteren, maar tot die tijd kunt u proberen een van de <a href='static/recommendedBrowsers.html' target='_blank'>volledig ondersteunde browsers</a> te gebruiken.", "suboptimalExperienceTitle": "Browserwaarschuwing", "unmute": "", "newDeviceCameraTitle": "Nieuwe camera gedetecteerd", "newDeviceAudioTitle": "Nieuw audioapparaat gedetecteerd", "newDeviceAction": "Gebruik" }, - "passwordSetRemotely": "ingesteld door een ander lid", + "passwordSetRemotely": "ingesteld door een andere deelnemer", "passwordDigitsOnly": "Maximaal {{number}} cijfers", - "poweredby": "powered by", + "poweredby": "mogelijk gemaakt door", "presenceStatus": { "busy": "Bezet", - "calling": "Bellen...", + "calling": "Bellen…", "connected": "Verbonden", - "connecting": "Verbinding maken...", + "connecting": "Verbinding maken…", "connecting2": "Verbinding maken*...", "disconnected": "Verbinding verbroken", "expired": "Verlopen", "ignored": "Genegeerd", - "initializingCall": "Gesprek starten...", + "initializingCall": "Gesprek starten…", "invited": "Uitgenodigd", "rejected": "Geweigerd", - "ringing": "Gaat over..." + "ringing": "Gaat over…" }, "profile": { "setDisplayNameLabel": "Uw weergavenaam instellen", "setEmailInput": "E-mailadres invoeren", - "setEmailLabel": "Uw gravatar voor e-mail instellen", + "setEmailLabel": "Uw Gravatar voor e-mail instellen", "title": "Profiel" }, "recording": { @@ -460,14 +465,14 @@ "error": "Opname is mislukt. Probeer het opnieuw.", "expandedOff": "Opname is gestopt", "expandedOn": "De vergadering wordt momenteel opgenomen.", - "expandedPending": "Opname wordt gestart...", + "expandedPending": "Opname wordt gestart…", "failedToStart": "Opname starten is mislukt", "fileSharingdescription": "Opname delen met deelnemers aan vergadering", "live": "LIVE", "loggedIn": "Aangemeld als {{userName}}", "off": "Opname gestopt", "on": "Opnemen", - "pending": "Voorbereiden op opname van vergadering...", + "pending": "Voorbereiden op opname van vergadering…", "rec": "OPN.", "serviceDescription": "Uw opname wordt opgeslagen door de opnameservice", "serviceName": "Opnameservice", @@ -503,16 +508,18 @@ "title": "Instellingen" }, "settingsView": { + "advanced": "Geavanceerd", "alertOk": "OK", "alertTitle": "Waarschuwing", "alertURLText": "De ingevoerde server-URL is ongeldig", - "buildInfoSection": "Buildgegevens", - "conferenceSection": "Conferentie", + "buildInfoSection": "Versiegegevens", + "conferenceSection": "Bijeenkomsten", "displayName": "Weergavenaam", "email": "E‑mail", "header": "Instellingen", "profileSection": "Profiel", "serverURL": "Server-URL", + "showAdvanced": "Toon geavanceerde instellingen", "startWithAudioMuted": "Starten met audio gedempt", "startWithVideoMuted": "Starten met video gedempt", "version": "Versie" @@ -595,7 +602,9 @@ "logout": "Afmelden", "lowerYourHand": "Uw hand laten zakken", "moreActions": "Meer acties", + "moreOptions": "Meer opties", "mute": "Dempen/dempen opheffen", + "muteEveryone": "Iedereen dempen", "openChat": "Chat openen", "pip": "Beeld-in-beeld-modus activeren", "profile": "Uw profiel bewerken", @@ -625,7 +634,7 @@ "failedToStart": "Transcriberen starten is mislukt", "labelToolTip": "De vergadering wordt getranscribeerd", "off": "Transcriberen gestopt", - "pending": "Voorbereiden op transcriberen van vergadering...", + "pending": "Voorbereiden op transcriberen van vergadering…", "start": "Weergave van ondertiteling starten", "stop": "Weergave van ondertiteling stoppen", "tr": "TR" @@ -675,11 +684,11 @@ "flip": "Omslaan", "kick": "Verwijderen", "moderator": "Moderator", - "mute": "Lid is gedempt", + "mute": "Deelnemer is gedempt", "muted": "Gedempt", "remoteControl": "Extern beheer", "show": "", - "videomute": "Lid heeft de camera gestopt" + "videomute": "Deelnemer heeft de camera gestopt" }, "welcomepage": { "accessibilityLabel": { @@ -705,8 +714,12 @@ "reducedUIText": "", "roomname": "Naam van ruimte invoeren", "roomnameHint": "Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar geef de naam wel door aan de andere deelnemers, zodat zij dezelfde naam kunnen invoeren.", - "sendFeedback": "Feedback verzenden", + "sendFeedback": "Feedback sturen", "terms": "Voorwaarden", "title": "Veilige, volledig uitgeruste en geheel gratis videovergaderingen" + }, + "lonelyMeetingExperience": { + "button": "Anderen uitnodigen", + "youAreAlone": "Je bent de enige in dit gesprek" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-oc.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-oc.json index 78f503b60..86491aef2 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-oc.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-oc.json @@ -22,7 +22,7 @@ "headphones": "Escotadors", "phone": "Telefòn", "speaker": "Nautparlaire", - "none": "" + "none": "Cap de periferic àudio pas disponible" }, "audioOnly": { "audioOnly": "Benda passanta febla" @@ -46,13 +46,18 @@ "today": "Uèi" }, "chat": { - "error": "Error : vòstre messatge « {{originalText}} » es pas estat enviat. Rason : {{error}}", + "error": "Error : lo messatge es pas estat enviat. Rason : {{error}}", + "fieldPlaceHolder": "Picatz vòstre messatge aquí", "messagebox": "Picatz un messatge", + "messageTo": "Messatge privat per {{recipient}}", + "noMessagesMessage": "I a pas cap de messatge dins la conferéncia pel moment. Començat una conversacion aquí !", "nickname": { "popover": "Causissètz un escais", "title": "Picatz un escais-nom per utilizar la messatjariá" }, - "title": "Messatjariá" + "privateNotice": "Messatge privat per {{recipient}}", + "title": "Messatjariá", + "you": "vos" }, "connectingOverlay": { "joiningRoom": "Connexion a vòstra reünion…" @@ -67,7 +72,11 @@ "DISCONNECTED": "Desconnectat", "DISCONNECTING": "Desconnexion en cors", "ERROR": "Error", - "RECONNECTING": "Un problèma ret s'es produita. Reconnexion en cors..." + "RECONNECTING": "Un problèma ret s'es produita. Reconnexion en cors...", + "LOW_BANDWIDTH": "", + "GOT_SESSION_ID": "", + "GET_SESSION_ID_ERROR": "", + "FETCH_SESSION_ID": "" }, "connectionindicator": { "address": "Adreça :", @@ -96,7 +105,9 @@ "remoteport_plural": "Pòrts distants :", "resolution": "Resolucion :", "status": "Connexion :", - "transport": "Transpòrt :" + "transport": "Transpòrt :", + "transport_plural": "", + "e2e_rtt": "" }, "dateUtils": { "earlier": "Mai d’ora", @@ -110,7 +121,7 @@ "downloadApp": "Telecargar l’aplicacion", "launchWebButton": "Lançar del navigador", "openApp": "Telecargar l’aplicacion", - "title": "Aviada de vòstra conferéncia dins {{app}}...", + "title": "Aviada de vòstra conferéncia dins {{app}}…", "tryAgainButton": "Tornar ensajar del burèu" }, "defaultLink": "ex. {{url}}", @@ -145,9 +156,9 @@ "cameraUnsupportedResolutionError": "Vòstra camèra pren pas en carga la resolucion vidèo que cal.", "Cancel": "Anullar", "close": "Tampar", - "conferenceDisconnectMsg": "Vos cal benlèu verificar vòstra connexion al malhum. Nòva connexion dins {{seconds}} segondas...", + "conferenceDisconnectMsg": "Vos cal benlèu verificar vòstra connexion al malhum. Nòva connexion dins {{seconds}} segondas…", "conferenceDisconnectTitle": "Sètz estat desconnectat.", - "conferenceReloadMsg": "Sèm a reglar aquò ! Nòva connexion dins {{seconds}} segondas...", + "conferenceReloadMsg": "Sèm a reglar aquò ! Nòva connexion dins {{seconds}} segondas…", "conferenceReloadTitle": "Malurosament, quicòm truquèt.", "confirm": "Confirmar", "confirmNo": "Non", @@ -182,7 +193,7 @@ "liveStreamingDisabledForGuestTooltip": "Los convidats pòdon pas aviar una difusion en dirècte", "liveStreamingDisabledTooltip": "Difusion en dirècte desactivada.", "lockMessage": "Impossible de verrolhar la conferéncia.", - "lockRoom": "", + "lockRoom": "Ajustar un $t(lockRoomPasswordUppercase) a la conferéncia", "lockTitle": "Fracàs del verrolhatge", "logoutQuestion": "Sètz segur que vos volètz desconnectar e arrestar la conferéncia ?", "logoutTitle": "Desconnexion", @@ -196,13 +207,13 @@ "micUnknownError": "Impossible d'utilizar lo microfòn per una rason desconeguda.", "muteParticipantBody": "Poiretz pas lo tornar activar lo microfòn, mai eles pòdon o far quand vòlon.", "muteParticipantButton": "Copar lo son", - "muteParticipantDialog": "", + "muteParticipantDialog": "Volètz vertadièrament copar lo son a aqueste participant ? Poiretz pas lo tornar la votz, mas eles pòdon restablir la comunicacion quand vòlgan.", "muteParticipantTitle": "Copar lo micro als participants ?", "Ok": "D'acòrdi", - "passwordLabel": "", - "passwordNotSupported": "Ajustar un senhal a una conferéncia es pas suportat.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "SENHAL", + "passwordNotSupported": "Ajustar un $t(lockRoomPassword) a una conferéncia es pas suportat.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) pas suportat", + "passwordRequired": "$t(lockRoomPasswordUppercase) requesit", "popupError": "Vòstre navigator bloca las fenèstras que sorgisson a partir d'aqueste site. Mercés d'activar aquelas fenèstras dins los paramètres de vòstre navigator e de tornar ensajar.", "popupErrorTitle": "Fenèstra que sorgís blocada", "recording": "Enregistrament", @@ -217,7 +228,7 @@ "remoteControlStopMessage": "La session de contraròtle alonhat es acabada !", "remoteControlTitle": "Contraròtle a distància", "Remove": "Suprimir", - "removePassword": "", + "removePassword": "Suprimir lo", "removeSharedVideoMsg": "Sètz segur que volètz suprimir vòstra vidèo partejada ?", "removeSharedVideoTitle": "Suprimir la vidèo partejada", "reservationError": "Error del sistèma de reservacion", @@ -228,6 +239,10 @@ "screenSharingFirefoxPermissionDeniedError": "Quicòm a fach mèuca quand èrem a ensajar de partejar vòstre ecran. Mercés de verificar qu’avètz donat l’autorizacion de lo partejar.", "screenSharingFirefoxPermissionDeniedTitle": "Ops ! Avèm pas pogut aviar lo partatge d’ecran.", "screenSharingPermissionDeniedError": "Òups ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra extension de partatge d'ecran. Mercés de recargar e tornar ensajar.", + "sendPrivateMessage": "Avètz recentament recebut un messatge privat. Avètz ensajat d’i respondre en privat, o volètz enviar lo messatge al grop ?", + "sendPrivateMessageCancel": "Enviar al grop", + "sendPrivateMessageOk": "Enviar en privat", + "sendPrivateMessageTitle": "Enviar en privat ?", "serviceUnavailable": "Servici indisponible", "sessTerminated": "Sonada acabada", "Share": "Partejar", @@ -250,17 +265,27 @@ "tokenAuthFailed": "O planhèm, sètz pas autorizat a rejónher l'apèl.", "tokenAuthFailedTitle": "Fracàs de l'autentificacion", "transcribing": "Transcripcion", - "unlockRoom": "", + "unlockRoom": "Suprimir lo $t(lockRoomPassword) de la conferéncia", "userPassword": "senhal utilizaire", "WaitForHostMsg": "La conferéncia <b>{{room}}</b> a pas encara començat. Se sètz l’òst volgatz ben vos identificar. Autrament esperatz qu’arribe l’òste.", "WaitForHostMsgWOk": "La conferéncia <b>{{room}}</b> a pas encara començat. Se sètz l’òst volgatz ben clicar Ok per vos identificar. Autrament esperatz qu’arribe l’òste.", "WaitingForHost": "Òm pòt pas que partejar una vidèo a l'encòp", "Yes": "Òc", - "yourEntireScreen": "Vòstre ecran complet" + "yourEntireScreen": "Vòstre ecran complet", + "screenSharingAudio": "", + "muteEveryoneStartMuted": "", + "muteEveryoneSelf": "", + "muteEveryoneTitle": "", + "muteEveryoneDialog": "", + "muteEveryoneElseTitle": "", + "muteEveryoneElseDialog": "" }, "dialOut": { "statusMessage": "ara es {{status}}" }, + "documentSharing": { + "title": "Document partejat" + }, "feedback": { "average": "Mejana", "bad": "Marrida", @@ -279,20 +304,20 @@ }, "info": { "accessibilityLabel": "Mostrar las info", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Ajustar un $t(lockRoomPassword)", + "cancelPassword": "Anullar lo $t(lockRoomPassword)", "conferenceURL": "Ligam :", "country": "País", "dialANumber": "Per participar a la conferéncia, sonatz un d’aquestes numèros puèi picatz lo senhal.", "dialInConferenceID": "PIN :", "dialInNotSupported": "Las sonadas son pas encara foncionalas.", "dialInNumber": "Compausar :", - "dialInSummaryError": "", + "dialInSummaryError": "Error en recuperant las informacions per sonar. Volgatz tornar mai tard.", "dialInTollFree": "Sonada gratuïta", "genericError": "Ops, quicòm a fach mèuca.", "inviteLiveStream": "Per veire lo flux en dirècte de la conferéncia, clicatz aqueste ligam : {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", + "invitePhone": "Per jónher per telefòn a la plaça, compausatz aquò : {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Cercatz mai de numèros de telefòn ?\nFar veire los numèros de sonada de la reünion : {{url}}\n\n\nSe volètz sonar tanben un telefòn de sala, jonhètz sens connectar l’àudio : {{silentUrl}}", "inviteURLFirstPartGeneral": "Sètz convidat a participar a la conferéncia.", "inviteURLFirstPartPersonal": "{{name}} vos convida a la conferéncia.\n", "inviteURLSecondPart": "\nParticipar a la conferéncia :\n{{url}}\n", @@ -302,7 +327,7 @@ "noPassword": "Pas cap", "noRoom": "Cap de sala pas donada per la jónher.", "numbers": "Sonar de numèros", - "password": "", + "password": "$t(lockRoomPasswordUppercase) :", "title": "Partejar", "tooltip": "Partejar lo ligam e las informacions d’aquesta conferéncia", "label": "Info conferéncia" @@ -350,13 +375,15 @@ "errorLiveStreamNotEnabled": "La difusion en dirècte es pas activada per {{email}}. Volgatz ben activar la difusion en dirècte o vos connectar amb un compte que l’a activada.", "expandedOff": "La difusion en dirècte es estada arrestada", "expandedOn": "La conferéncia es difusada sus YouTube.", - "expandedPending": "La difusion en dirècte comença...", + "expandedPending": "La difusion en dirècte comença…", "failedToStart": "La difusion en dirècte a pas capitat de s'aviar", - "getStreamKeyManually": "", + "getStreamKeyManually": "Avèm pas pogut recuperar cap de flux en dirècte. Ensajatz d’obténer vòstre clau de difusion sus YouTube.", "invalidStreamKey": "La clau de difusion en dirècte es benlèu pas corrècta.", "off": "La difusion en dirècte es estada arrestada", + "offBy": "{{name}} a arrestat la difusion en dirècte", "on": "La difusion en dirècte es estada arrestada", - "pending": "Començar lo dirècte...", + "onBy": "{{name}} a començat la difusion en dirècte", + "pending": "Començar lo dirècte…", "serviceName": "Servici de difusion en dirècte", "signedInAs": "Sètz connectat coma :", "signIn": "Se connectar amb Google", @@ -364,7 +391,9 @@ "signOut": "Se desconnectar", "start": "Aviar una difusion en dirècte", "streamIdHelp": "Qu’es aquò ?", - "unavailableTitle": "Difusion en dirècte indisponibla" + "unavailableTitle": "Difusion en dirècte indisponibla", + "googlePrivacyPolicy": "", + "youtubeTerms": "" }, "localRecording": { "clientState": { @@ -415,14 +444,14 @@ "muted": "Avètz començat la conversacion en mut.", "mutedTitle": "Sètz en mut !", "mutedRemotelyTitle": "{{participantDisplayName}} vos a mes en silenci !", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyDescription": "Podètz totjorn activar vòstre microfòn per prendre la paraula. Desactivatz lo microfò quand terminetz per evitar los bruches parasits.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) tirat per un autre participant", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) definit per un autre participant", "raisedHand": "{{name}} volriá parlar.", "somebody": "Qualqu'un", "startSilentTitle": "Avètz jonch sens cap de sortida àudio !", "startSilentDescription": "Rejónher la conferéncia per activar l’àudio", - "suboptimalBrowserWarning": "", + "suboptimalBrowserWarning": "Planhèm que vòstra experiéncia de la conferéncia siá pas de las bonas. Sèm a cercar de solucions per melhorar aquò, d’aquel temps, ensajatz un dels <a href='static/recommendedBrowsers.html' target='_blank'>navegators compatibles</a>.", "suboptimalExperienceTitle": "Avertiment del navegador", "unmute": "Restablir lo son", "newDeviceCameraTitle": "Nòva camèra detectada", @@ -434,22 +463,22 @@ "poweredby": "produit per", "presenceStatus": { "busy": "Ocupat", - "calling": "Sonada...", + "calling": "Sonada…", "connected": "Connectat", - "connecting": "Connexion en cors...", + "connecting": "Connexion en cors…", "connecting2": "Connexion*...", "disconnected": "Desconnectat", "expired": "Expirat", "ignored": "Ignorat", - "initializingCall": "Començament de la sonada...", + "initializingCall": "Començament de la sonada…", "invited": "Convidat", "rejected": "Refusat", - "ringing": "A sonar..." + "ringing": "A sonar…" }, "profile": { "setDisplayNameLabel": "Causissètz vòstre escais", "setEmailInput": "Picatz lo corrièl", - "setEmailLabel": "Definissètz vòstre corrièl per gravatar", + "setEmailLabel": "Definissètz vòstre corrièl per Gravatar", "title": "Perfil" }, "raisedHand": "Volriá charrar", @@ -462,14 +491,16 @@ "error": "Fracàs de l'enregistrament. Mercés de tornar ensajar.", "expandedOff": "Enregistrament arrestat", "expandedOn": "La conferéncia es enregistrada.", - "expandedPending": "Aviada de l’enregistrament...", + "expandedPending": "Aviada de l’enregistrament…", "failedToStart": "L'enregistrament n'as pas réussi a démarrer", "fileSharingdescription": "Partejar l’enregistrament amb los participants de la reünion", "live": "DIRÈCTE", "loggedIn": "Session a {{userName}}", "off": "Enregistrament arrestar", + "offBy": "{{name}} a arrestat l’enregistrament", "on": "Enregistrament", - "pending": "Preparacion de l’enregistrament de la conferéncia...", + "onBy": "{{name}} a començat l’enregistrament", + "pending": "Preparacion de l’enregistrament de la conferéncia…", "rec": "ENRG", "serviceDescription": "Vòstre enregistrament serà salvagardat pel servici dedicat.", "serviceName": "Servici d’enregistrament", @@ -502,25 +533,31 @@ "selectMic": "Microfòn", "startAudioMuted": "Començan totes sens son", "startVideoMuted": "Començan totes sens vidèo", - "title": "Paramètres" + "title": "Paramètres", + "speakers": "", + "microphones": "" }, "settingsView": { + "advanced": "Avançat", "alertOk": "D’acòrdi", "alertTitle": "Avertiment", "alertURLText": "L’URL del servidor es pas valida", - "buildInfoSection": "", + "buildInfoSection": "Informacions de generacion", "conferenceSection": "Conferéncia", + "disableCallIntegration": "Desactivar l’integracion nativa de las sonadas", + "disableP2P": "Desactivar lo mòde par a par", "displayName": "Escais-nom", "email": "Corrièl", "header": "Paramètres", "profileSection": "Perfil", "serverURL": "URL del servidor", + "showAdvanced": "Mostrar los paramètres avançats", "startWithAudioMuted": "Començar sens son", "startWithVideoMuted": "Començar sens vièdo", "version": "Version" }, "share": { - "dialInfoText": "", + "dialInfoText": "\n\n=====\n\nVolètz sonar de vòstre telefòn estant ?\n\n{{defaultDialInNumber}}Clicatz lo ligam per veire los numèros de telefòn d’aquesta conferéncia\n{{dialInfoPageUrl}}", "mainText": "Copiatz lo ligam seguent per dintrar dins la conferéncia :\n{{roomUrl}}" }, "speaker": "Nautparlaire", @@ -549,36 +586,41 @@ "cc": "Passar als jostítols", "chat": "Passar a la fenèstra chat", "document": "Tampar los documents partejats", + "download": "Telecargar nòstra aplicacion", "feedback": "Daissar un comentari", "fullScreen": "Passar al ecran complèt", "hangup": "Quitar la sonada", + "help": "Ajuda", "invite": "Convidar de monde", - "kick": "", + "kick": "Exclure un participan ", "localRecording": "Passar al panèl d’enregistraments locals", - "lockRoom": "Tirar lo senhal de la conferéncia", + "lockRoom": "Tirar/Metre lo senhal de la conferéncia", "moreActions": "Passar al menú mai d’accions", "moreActionsMenu": "Mai de menús d’accion", "mute": "Copar lo son", "pip": "Activar/Desactivar lo mòde Picture-in-Picture", + "privateMessage": "Enviar un messatge privat", "profile": "Modificar vòstre perfil", "raiseHand": "Demandar la paraula", "recording": "Passar al enregistraments", - "remoteMute": "", + "remoteMute": "Copar lo son del participant", "Settings": "Passar als paramètres", - "sharedvideo": "Passar al partatge de vidèo Youtube", + "sharedvideo": "Passar al partatge de vidèo YouTube", "shareRoom": "Convidar qualqu’un", "shareYourScreen": "Passar a la captura d’ecran", "shortcuts": "Passar als acorchis", - "show": "", + "show": "Mostrar davant", "speakerStats": "Mostrar/Amagar los estatisticas de paraula", "tileView": "Activar/Desactivar la vista en mosaïc", "toggleCamera": "Passar a la camèra", "videomute": "Silenciar la vidèo", - "videoblur": "" + "videoblur": "Enfoscar o non la vidèo", + "muteEveryone": "", + "moreOptions": "" }, "addPeople": "Ajustar de monde a vòstra sonada", - "audioOnlyOff": "", - "audioOnlyOn": "", + "audioOnlyOff": "Desactivar lo mòde connexion febla", + "audioOnlyOn": "Activar lo mòde connexion febla", "audioRoute": "Seleccionar lo periferic àudio", "authenticate": "Autentificatz-vos", "callQuality": "Gerir la qualitat vidèo", @@ -586,20 +628,26 @@ "closeChat": "Tampar la messatjariá", "documentClose": "Tampar los documents partejats", "documentOpen": "Dobrir los documents partejats", + "download": "Telecargar nòstra aplicacion", "enterFullScreen": "Veire l’ecran complèt", - "enterTileView": "", + "enterTileView": "Dintrar dins la vista mosaïca", "exitFullScreen": "Sortir de l’ecran complèt", - "exitTileView": "", + "exitTileView": "Quitar la vista mosaïca", "feedback": "Daissar un comentari", "hangup": "Quitar", + "help": "Ajuda", "invite": "Convidar de monde", "login": "Connexion", "logout": "Desconnexion", "lowerYourHand": "Baissar la man", "moreActions": "Mai d’opcions", "mute": "Mut / Actiu", + "noAudioSignalTitle": "I a pas cap de son en entrada del microfòn !", + "noAudioSignalDesc": "S’avètz pas volontàriament copat lo son a partir dels paramètres sistèma o material, pensatz de cambiar d’aparelh.", + "noAudioSignalDescSuggestion": "S’avètz pas volontàriament copat lo son a partir dels paramètres sistèma o material, pensatz d’utilizar un dels aparelhs seguents :", "openChat": "Dobrir la messatjariá ", "pip": "Passar al mòde Picture-in-Picture", + "privateMessage": "Enviar un messatge privat", "profile": "Modificar vòstre perfil", "raiseHand": "Demandar / Daissar la paraula", "raiseYourHand": "Levar la man", @@ -617,8 +665,14 @@ "tileViewToggle": "Activar/Desactivar la vista en mosaïc", "toggleCamera": "Passar a la camèra", "videomute": "Aviar / Arrestar la camèra", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Trebolar mon rèire-plan", + "stopvideoblur": "Desactivar lo borrolatge del rèire-plan", + "noisyAudioInputDesc": "", + "noisyAudioInputTitle": "", + "noAudioSignalDialInLinkDesc": "", + "noAudioSignalDialInDesc": "", + "muteEveryone": "", + "moreOptions": "" }, "transcribing": { "ccButtonTooltip": "Aviar / Arrestat los sostítols", @@ -627,7 +681,7 @@ "failedToStart": "Fracàs de l’aviada de la transcripcion", "labelToolTip": "La conferéncia es a èsser transcricha", "off": "Transcripcion arrestada", - "pending": "Preparacion de l’enregistrament de la conferéncia...", + "pending": "Preparacion de l’enregistrament de la conferéncia…", "start": "Mostrar los sostítols", "stop": "Levar los sostítols", "tr": "TR" @@ -656,20 +710,20 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "", + "audioOnlyExpanded": "Sètz en mòde connexion febla. Amb aqueste mòde recebretz pas que l’àudio e lo partatge d’ecran.", "callQuality": "Qualitat vidèo", "hd": "HD", "hdTooltip": "Difusion vidèo en nauta definicion", "highDefinition": "Nauta definicion", "labelTooiltipNoVideo": "Pas cap de vidèo", - "labelTooltipAudioOnly": "", + "labelTooltipAudioOnly": "Mòde connexion febla activat", "ld": "Bassa definicion", "ldTooltip": "Difusion vidèo en bassa definicion", "lowDefinition": "Bassa definicion", "onlyAudioAvailable": "Pas que l’àudio es disponible", "onlyAudioSupported": "Sèm compatibles solament amb l’àudio dins aqueste navigator.", "p2pEnabled": "Connexion par a par activada", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "En mòde par a par, la qualitat vidèo que recebètz pòt solament passar de nauta a àudio solament. Los autres paramètres seràn pas preses en compte fins que quitetz lo mòde par a par.", "recHighDefinitionOnly": "Nauta definicion preferida.", "sd": "SD", "sdTooltip": "Difusion vidèo en definicion estandard", @@ -683,8 +737,9 @@ "mute": "Un participant a copat son micro", "muted": "Mut", "remoteControl": "Contraròtle alonhat", - "show": "", - "videomute": "" + "show": "Mostrar davant", + "videomute": "Lo participant a arrestat la camèra", + "domuteOthers": "" }, "welcomepage": { "accessibilityLabel": { @@ -698,11 +753,13 @@ }, "calendar": "Calendari", "connectCalendarButton": "Connectar lo calendari", - "connectCalendarText": "", + "connectCalendarText": "Connectatz vòstre calendièr per veire vòstras reünions dins {{app}}. Ajustatz tanben las reünions de {{provider}} a vòstre calendièr e aviatz-las amb un sol clic.", "enterRoomTitle": "Començar una nòva conferéncia", + "roomNameAllowedChars": "Lo nom de la conferéncia deu pas conténer aqueles caractèrs : ?, &, :, ', \", %, #.", "go": "Crear", - "join": "PARTICIPATZ", - "info": "", + "goSmall": "Crear", + "join": "CREAR / REJÓNHER", + "info": "Infor", "privacy": "Vida privada", "recentList": "Recents", "recentListDelete": "Suprimits", @@ -712,6 +769,19 @@ "roomnameHint": "Picatz lo nom o l’URL de la sala que volètz jónher. Podètz inventar un nom, cal pas que lo monde que volètz convidar lo sàpian. ", "sendFeedback": "Mandar vòstra opinion", "terms": "Tèrmes", - "title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas" + "title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas", + "getHelp": "" + }, + "helpView": { + "header": "" + }, + "lonelyMeetingExperience": { + "youAreAlone": "", + "button": "" + }, + "chromeExtensionBanner": { + "dontShowAgain": "", + "buttonText": "", + "installExtensionText": "" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-pl.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-pl.json index f2e253ced..0b99eeba7 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-pl.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-pl.json @@ -1,60 +1,66 @@ { "addPeople": { - "add": "", - "countryNotSupported": "", - "countryReminder": "", - "disabled": "", - "failedToAdd": "", - "footerText": "", - "loading": "", - "loadingNumber": "", - "loadingPeople": "", - "noResults": "", - "noValidNumbers": "", - "searchNumbers": "", - "searchPeople": "", - "searchPeopleAndNumbers": "", - "telephone": "", - "title": "" + "add": "Zaproś", + "countryNotSupported": "Nie obsługujemy jeszcze tej lokalizacji.", + "countryReminder": "Dzwonisz spoza USA? Upewnij się, że zaczynasz od kodu kraju!", + "disabled": "Nie możesz zapraszać ludzi.", + "failedToAdd": "Błąd dodawania uczestników", + "footerText": "Wybieranie numeru jest wyłączone.", + "loading": "Szukaj ludzi i numerów telefonów", + "loadingNumber": "Weryfikacja numeru telefonu", + "loadingPeople": "Wyszukiwanie osób do zaproszenia", + "noResults": "Brak pasujących wyników wyszukiwania", + "noValidNumbers": "Proszę wpisać numer telefonu", + "searchNumbers": "Dodaj numery telefonów", + "searchPeople": "Szukaj ludzi", + "searchPeopleAndNumbers": "Wyszukaj osoby i dodaj ich numery telefonu", + "telephone": "Telefon: {{number}}", + "title": "Zaproś ludzi na to spotkanie" }, "audioDevices": { "bluetooth": "Bluetooth", "headphones": "Słuchawki", - "phone": "", - "speaker": "głośnik" + "phone": "Telefon", + "speaker": "Głośnik", + "none": "Brak dostępnych urządzeń dźwiękowych" }, "audioOnly": { - "audioOnly": "Tylko dźwięk" + "audioOnly": "Niska przepustowość" }, "calendarSync": { - "addMeetingURL": "", - "confirmAddLink": "", + "addMeetingURL": "Dodaj odnośnik do spotkania", + "confirmAddLink": "Czy chcesz dodać odnośnik Jitsi do tego wydarzenia?", "error": { - "appConfiguration": "", - "generic": "", - "notSignedIn": "" + "appConfiguration": "Integracja z kalendarzem nie jest skonfigurowana poprawnie.", + "generic": "Wystąpił problem. Sprawdź ustawienia kalendarza lub spróbuj odświeżyć kalendarz.", + "notSignedIn": "Wystąpił problem podczas uwierzytelniania dostępu do wydarzeń w kalendarzu. Sprawdź ustawienia kalendarza i spróbuj zalogować się ponownie." }, - "join": "", - "joinTooltip": "", - "nextMeeting": "", - "noEvents": "", - "ongoingMeeting": "", - "permissionButton": "", - "permissionMessage": "", - "refresh": "", - "today": "" + "join": "Dołącz", + "joinTooltip": "Dołącz do spotkania", + "nextMeeting": "następne spotkanie", + "noEvents": "Nie ma zaplanowanych nadchodzących wydarzeń.", + "ongoingMeeting": "trwające spotkanie", + "permissionButton": "Otwórz ustawienia", + "permissionMessage": "Aby zobaczyć spotkania w aplikacji, wymagane jest zezwolenie dostępu do kalendarza.", + "refresh": "Odśwież kalendarz", + "today": "Dzisiaj" }, "chat": { - "error": "", - "messagebox": "", + "error": "Błąd: Twoja wiadomość nie została wysłana. Powód: {{error}}", + "fieldPlaceHolder": "", + "messagebox": "Wpisz wiadomość", + "messageTo": "Prywatna wiadomość do {{recipient}}", + "noMessagesMessage": "", "nickname": { "popover": "Wybierz swój nick", - "title": "" + "title": "Wpisz swoją nazwę, aby użyć rozmowy" }, - "title": "" + "privateNotice": "Prywatna wiadomość do {{recipient}}", + "title": "Rozmowa", + "you": "Ty" }, "connectingOverlay": { - "joiningRoom": "" + "joiningRoom": "Łączenie z Twoim spotkaniem…" }, "connection": { "ATTACHED": "Załącznik", @@ -66,641 +72,654 @@ "DISCONNECTED": "Rozłączony", "DISCONNECTING": "Rozłączanie", "ERROR": "Błąd", - "RECONNECTING": "Wystąpił problem w sieci. Ponowienie połaczenia...." + "RECONNECTING": "Wystąpił problem w sieci. Ponowienie połączenia..." }, "connectionindicator": { "address": "Adres:", - "bandwidth": "Zakładana przepustowość:", + "bandwidth": "Szacowana przepustowość:", "bitrate": "Szybkość transmisji:", - "bridgeCount": "Liczba serwerów", + "bridgeCount": "Liczba serwerów: ", "connectedTo": "Podłączone do:", - "framerate": "Częstotliwość odświeżania", + "framerate": "Klatek na sekundę:", "less": "Pokaż mniej", - "localaddress": "Lokalny adres:Lokalne Adresy:", - "localaddress_plural_2": "", - "localaddress_plural_5": "", - "localport": "Lokalny port:Lokalne porty:", - "localport_plural_2": "", - "localport_plural_5": "", + "localaddress_0": "Adres lokalny:", + "localaddress_1": "Adresy lokalne:", + "localaddress_2": "Adresy lokalne:", + "localport_0": "Port lokalny:", + "localport_1": "Porty lokalne:", + "localport_2": "Porty lokalne:", "more": "Pokaż więcej", - "packetloss": "Strata pakietów:", + "packetloss": "Utrata pakietów:", "quality": { - "good": "Prawdziwy", - "inactive": "nieaktywny", - "lost": "Zaginiony", + "good": "Dobre", + "inactive": "Nieaktywne", + "lost": "Utracone", "nonoptimal": "Nieoptymalne", - "poor": "Biedny" + "poor": "Słabe" }, - "remoteaddress": "Zdalny adres:Zdalne adresy:", - "remoteaddress_plural_2": "", - "remoteaddress_plural_5": "", - "remoteport": "Zdalny port:Zdalne porty:", - "remoteport_plural_2": "", - "remoteport_plural_5": "", + "remoteaddress_0": "Adres zdalny:", + "remoteaddress_1": "Adresy zdalne:", + "remoteaddress_2": "Adresy zdalne:", + "remoteport_0": "Port zdalny:", + "remoteport_1": "Porty zdalne:", + "remoteport_2": "Porty zdalne:", "resolution": "Rozdzielczość:", - "status": "Nawiązywanie połączenia", - "transport": "Przekazywanie:", - "turn": "skręt" + "status": "Połączenie:", + "transport_0": "Transport:", + "transport_1": "Transporty:", + "transport_2": "Transporty:" }, - "\u0005connectionindicator": {}, "dateUtils": { - "earlier": "", - "today": "", - "yesterday": "" + "earlier": "Wcześniej", + "today": "Dzisiaj", + "yesterday": "Wczoraj" }, "deepLinking": { - "appNotInstalled": "", - "description": "", - "descriptionWithoutWeb": "", - "downloadApp": "", - "launchWebButton": "", - "openApp": "", - "title": "", - "tryAgainButton": "" + "appNotInstalled": "Potrzebujesz aplikacji mobilnej {{app}}, aby móc dołączyć do tego spotkania przez telefon.", + "description": "Nic się nie wydarzyło? Spróbowaliśmy uruchomić Twoje spotkanie w aplikacji stacjonarnej {{app}}. Spróbuj ponownie lub uruchom spotkanie w aplikacji webowej {{app}}.", + "descriptionWithoutWeb": "Nic się nie wydarzyło? Spróbowaliśmy uruchomić Twoje spotkanie w aplikacji stacjonarnej {{app}}.", + "downloadApp": "Pobierz aplikację", + "launchWebButton": "Uruchom przez przeglądarkę", + "openApp": "Kontynuuj w aplikacji", + "title": "Trwa uruchamianie Twojego spotkania w {{app}}…", + "tryAgainButton": "Spróbuj ponownie w aplikacji stacjonarnej" }, - "defaultLink": "np. _url_", + "defaultLink": "np. {{url}}", + "defaultNickname": "np. Ziutek Kowalski", "deviceError": { - "cameraError": "", - "cameraPermission": "", - "microphoneError": "", - "microphonePermission": "" + "cameraError": "Błąd dostępu do Twojej kamery", + "cameraPermission": "Błąd podczas otrzymywania uprawnień do kamery", + "microphoneError": "Błąd dostępu do Twojego mikrofonu", + "microphonePermission": "Błąd podczas otrzymywania uprawnień do mikrofonu" }, "deviceSelection": { - "noPermission": "", - "previewUnavailable": "", - "selectADevice": "", - "testAudio": "" + "noPermission": "Nie przyznano uprawnienia", + "previewUnavailable": "Podgląd niedostępny", + "selectADevice": "Wybierz urządzenie", + "testAudio": "Odtwórz dźwięk testowy" }, "dialog": { "accessibilityLabel": { - "liveStreaming": "Strumień live" + "liveStreaming": "Transmisja na żywo" }, - "allow": "", - "alreadySharedVideoMsg": "", - "alreadySharedVideoTitle": "", - "applicationWindow": "", + "allow": "Pozwól", + "alreadySharedVideoMsg": "Inny użytkownik już prezentuje wideo. Ta konferencja pozwala tylko na prezentację jednego wideo w tym samym czasie.", + "alreadySharedVideoTitle": "Tylko jedna prezentacja wideo jest dozwolona w tym samym czasie", + "applicationWindow": "Okno aplikacji", "Back": "Wstecz", - "cameraConstraintFailedError": "Twoja kamera nie spełnia wymagań.", + "cameraConstraintFailedError": "Twoja kamera nie spełnia niektórych obowiązkowych wymagań.", "cameraNotFoundError": "Kamera nie znaleziona.", - "cameraNotSendingData": "", - "cameraNotSendingDataTitle": "", - "cameraPermissionDeniedError": "Nie udzieliłeś pozwolenia na użycie twojej kamery. Nadal możesz włączyć się do konferencji ale inni nie będą cię widzieli. Naciśnij przycisk kamera w pasku menu aby użyć właściwą kamerę. ", - "cameraUnknownError": "Z nieznanej przyczyny nie można użyć kamery ", + "cameraNotSendingData": "Nie możemy połączyć się z Twoją kamerą. Sprawdź, czy inna aplikacja nie używa tego urządzenia, wybierz inne urządzenie z menu ustawień lub spróbuj zrestartować aplikację.", + "cameraNotSendingDataTitle": "Brak dostępu do kamery", + "cameraPermissionDeniedError": "Nie udzieliłeś pozwolenia na użycie swojej kamery. Nadal możesz dołączyć do konferencji, ale inni nie będą Cię widzieć. Naciśnij przycisk kamery na pasku adresu, aby to poprawić.", + "cameraUnknownError": "Z nieznanej przyczyny nie można użyć kamery.", "cameraUnsupportedResolutionError": "Twoja kamera nie obsługuje wymaganej rozdzielczości.", "Cancel": "Anuluj", - "close": "", - "conferenceDisconnectMsg": "", - "conferenceDisconnectTitle": "", - "conferenceReloadMsg": "", - "conferenceReloadTitle": "", - "confirm": "", - "confirmNo": "", + "close": "Zamknij", + "conferenceDisconnectMsg": "Być może należy sprawdzić połączenie sieciowe. Ponowne połączenie za {{seconds}} sekund…", + "conferenceDisconnectTitle": "Zostałeś rozłączony.", + "conferenceReloadMsg": "Staramy się to naprawić. Ponowne połączenie za {{seconds}} sekund…", + "conferenceReloadTitle": "Niestety, coś poszło nie tak.", + "confirm": "Potwierdź", + "confirmNo": "Nie", "confirmYes": "Tak", "connectError": "Ocho! Cos poszło nie tak, nie można podłaczyć się do tej konferencji.", - "connectErrorWithMsg": "Ocho! Coś poszło nie tak i nie można podłączyć się do tej konferencji:_msg_", + "connectErrorWithMsg": "Upsss! Coś poszło nie tak i nie możemy podłączyć się do tej konferencji: {{msg}}", "connecting": "Nawiązywanie połączenia", - "contactSupport": "", + "contactSupport": "Skontaktuj się ze wsparciem", "copy": "Kopiuj", - "dismiss": "", - "displayNameRequired": "", - "done": "Brak", - "enterDisplayName": "", + "dismiss": "Odrzuć", + "displayNameRequired": "Cześć! Jak się nazywasz?", + "done": "Zrobione", + "enterDisplayName": "Wpisz tutaj swoje imię", "error": "Błąd", "externalInstallationMsg": "Zainstaluj rozszerzenie naszego współdzielenia ekranu.", "externalInstallationTitle": "Wymagane rozszerzenie", "goToStore": "Idź do sklepu", "gracefulShutdown": "Aktualnie serwis jest konserwowany. Prosze spróbować później.", "IamHost": "Jestem gospodarzem", - "incorrectRoomLockPassword": "", - "incorrectPassword": "", + "incorrectRoomLockPassword": "Hasło nieprawidłowe", + "incorrectPassword": "Niepoprawna nazwa użytkownika lub hasło", "inlineInstallationMsg": "Zainstaluj rozszerzenie naszego współdzielenia ekranu.", - "inlineInstallExtension": "", - "internalError": "", + "inlineInstallExtension": "Zainstaluj teraz", + "internalError": "Ups! Coś poszło nie tak. Wystąpił następujący błąd: {{error}}", "internalErrorTitle": "Błąd wewnętrzny", - "kickMessage": "", - "kickParticipantButton": "", - "kickParticipantDialog": "", - "kickParticipantTitle": "", - "kickTitle": "", + "kickMessage": "Możesz skontaktować się z {{participantDisplayName}}, aby uzyskać więcej szczegółów.", + "kickParticipantButton": "Usuń", + "kickParticipantDialog": "Czy na pewno chcesz usunąć tego uczestnika?", + "kickParticipantTitle": "Usunąć tego uczestnika?", + "kickTitle": "Ups! {{participantDisplayName}} usunął Cię z tego spotkania", "liveStreaming": "Strumień live", - "liveStreamingDisabledForGuestTooltip": "", - "liveStreamingDisabledTooltip": "", + "liveStreamingDisabledForGuestTooltip": "Goście nie mogą używać transmisji na żywo.", + "liveStreamingDisabledTooltip": "Rozpoczęcie transmisji na żywo jest wyłączone.", "lockMessage": "Zabezpieczenie konferencji nie powiodło się.", - "lockRoom": "", + "lockRoom": "Dodaj spotkanie $t(lockRoomPasswordUppercase)", "lockTitle": "Nie powiodło się zabezpieczenie konferencji", "logoutQuestion": "Na pewno chcesz się wylogować i zakończyć konferencję?", "logoutTitle": "Wyloguj", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", - "micConstraintFailedError": "Twój mikrofon nie obsługuje wymaganych parametrów.", + "maxUsersLimitReached": "Osiągnięto limit maksymalnej liczby uczestników. Konferencja jest zapełniona. Skontaktuj się z właścicielem spotkania lub spróbuj ponownie później!", + "maxUsersLimitReachedTitle": "Osiągnięto maksymalną liczbę uczestników", + "micConstraintFailedError": "Twój mikrofon nie spełnia niektórych wymaganych parametrów.", "micNotFoundError": "Mikrofon nie jest odnaleziony.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Wejdź w ustawienia komputera, aby wyłączyć wyciszenie i dostosować poziom głośności", + "micNotSendingDataTitle": "Twój mikrofon jest wyciszony przez ustawienia systemowe", "micPermissionDeniedError": "Nie udzieliłeś pozwolenia na użycie twojego mikrofonu. Nadal możesz uczestniczyc w konferencji ale inni nie będą cię słyszeli. Użyj przycisku kamera aby to naprawić.", - "micUnknownError": "Z przyczyn nieznanych nie można użyć mikrofonu. ", - "muteParticipantBody": "", + "micUnknownError": "Z nieznanej przyczyny nie można użyć mikrofonu.", + "muteParticipantBody": "Nie możesz wyłączyć ich wyciszenia, ale oni mogą samodzielnie wyłączyć wyciszenie w dowolnym momencie.", "muteParticipantButton": "Wyciszenie", - "muteParticipantDialog": "", - "muteParticipantTitle": "", - "Ok": "Ok", - "passwordLabel": "", - "passwordNotSupported": "", - "passwordNotSupportedTitle": "", - "passwordRequired": "", - "popupError": "", - "popupErrorTitle": "", + "muteParticipantDialog": "Czy na pewno wyciszyć tego uczestnika? Nie będziesz mógł wyłączyć wyciszenia uczestników, ale oni mogą samodzielnie wyłączyć wyciszenie w dowolnym momencie.", + "muteParticipantTitle": "Wyciszyć tego uczestnika?", + "Ok": "OK", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "Ustanowienie spotkania $t(lockRoomPassword) nie jest obsługiwane.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nie jest obsługiwane", + "passwordRequired": "$t(lockRoomPasswordUppercase) jest wymagane", + "popupError": "Twoja przeglądarka blokuje wyskakujące okienka pochodzące z tej witryny. Włącz wyświetlanie wyskakujących okienek w ustawieniach bezpieczeństwa Twojej przeglądarki i spróbuj ponownie.", + "popupErrorTitle": "Wyskakujące okienko zostało zablokowane", "recording": "Nagrywanie", - "recordingDisabledForGuestTooltip": "", - "recordingDisabledTooltip": "", - "rejoinNow": "", - "remoteControlAllowedMessage": "", - "remoteControlDeniedMessage": "", - "remoteControlErrorMessage": "", - "remoteControlRequestMessage": "", - "remoteControlShareScreenWarning": "", - "remoteControlStopMessage": "", - "remoteControlTitle": "", + "recordingDisabledForGuestTooltip": "Goście nie mogą rozpocząć nagrywania.", + "recordingDisabledTooltip": "Rozpoczęcie nagrywania wyłączone.", + "rejoinNow": "Połącz ponownie teraz", + "remoteControlAllowedMessage": "{{user}} zaakceptował Twoją prośbę o kontrolę zdalną!", + "remoteControlDeniedMessage": "{{user}} odrzucił Twoją prośbę o kontrolę zdalną!", + "remoteControlErrorMessage": "Wystąpił błąd podczas próby uzyskania zgody na zdalną kontrolę od {{user}}!", + "remoteControlRequestMessage": "Czy zgadzasz się, żeby {{user}} zdalnie kontrolował Twój komputer?", + "remoteControlShareScreenWarning": "Zwróć uwagę, że jeśli wybierzesz \"Pozwól\" jednocześnie zostanie współdzielony Twój ekran!", + "remoteControlStopMessage": "Sesja zdalnej kontroli dobiegła końca!", + "remoteControlTitle": "Zdalna kontrola komputera", "Remove": "Usuń", - "removePassword": "", + "removePassword": "Usuń $t(lockRoomPassword)", "removeSharedVideoMsg": "Na pewno chcesz usunąć współdzielone wideo?", "removeSharedVideoTitle": "Usuń wideo współdzielone", "reservationError": "Błąd systemu rezerwacji", - "reservationErrorMsg": "Kod błędu: _code_, treść: _msg_", + "reservationErrorMsg": "Kod błędu: {{code}}, treść: {{msg}}", "retry": "Ponów", - "screenSharingFailedToInstall": "", - "screenSharingFailedToInstallTitle": "", - "screenSharingFirefoxPermissionDeniedError": "", - "screenSharingFirefoxPermissionDeniedTitle": "", - "screenSharingPermissionDeniedError": "", + "screenSharingFailedToInstall": "Ups! Nie udało się zainstalować wtyczki do współdzielenia ekranu.", + "screenSharingFailedToInstallTitle": "Nie udało się zainstalować wtyczki do współdzielenia ekranu", + "screenSharingFirefoxPermissionDeniedError": "Coś poszło nie tak podczas próby współdzielenia Twojego ekranu. Upewnij się, że udzieliłeś zgody na tą próbę. ", + "screenSharingFirefoxPermissionDeniedTitle": "Ups! Nie byliśmy w stanie rozpocząć współdzielenia ekranu!", + "screenSharingPermissionDeniedError": "Ups! Coś poszło nie tak z prawami dostępu do wtyczki współdzielenia ekranu. Wczytaj ponownie i spróbuj jeszcze raz.", + "sendPrivateMessage": "Niedawno otrzymałeś prywatną wiadomość. Czy zamierzałeś odpowiedzieć na nią prywatnie, czy chcesz wysłać wiadomość do grupy?", + "sendPrivateMessageCancel": "Wyślij do grupy", + "sendPrivateMessageOk": "Wyślij prywatnie", + "sendPrivateMessageTitle": "Wysłać prywatnie?", "serviceUnavailable": "Usługa jest niedostępna", - "sessTerminated": "", + "sessTerminated": "Połączenie przerwane", "Share": "Współdziel", "shareVideoLinkError": "Podaj proszę prawidłowy link youtube.", "shareVideoTitle": "Współdziel wideo", - "shareYourScreen": "", - "shareYourScreenDisabled": "", - "shareYourScreenDisabledForGuest": "", - "startLiveStreaming": "Zatrzymaj transmisję live", - "startRecording": "Zatrzymaj nagrywanie", - "startRemoteControlErrorMessage": "", - "stopLiveStreaming": "Zatrzymaj transmisję live", + "shareYourScreen": "Włącz współdzielenie ekranu", + "shareYourScreenDisabled": "Współdzielenie ekranu wyłączone.", + "shareYourScreenDisabledForGuest": "Goście nie mogą współdzielić ekranu.", + "startLiveStreaming": "Rozpocznij transmisję na żywo", + "startRecording": "Rozpocznij nagrywanie", + "startRemoteControlErrorMessage": "Wystąpił błąd podczas próby rozpoczęcie sesji zdalnej kontroli!", + "stopLiveStreaming": "Zatrzymaj transmisję na żywo", "stopRecording": "Zatrzymaj nagrywanie", "stopRecordingWarning": "Naprawdę chcesz zatrzymać nagrywanie?", - "stopStreamingWarning": "Czy jesteś pewny, że chcesz zatrzymać ten strumień live?", - "streamKey": "", - "Submit": "", - "thankYou": "Dziękujemy Ci za używanie _appName_!", + "stopStreamingWarning": "Czy jesteś pewny, że chcesz zatrzymać tę transmisję na żywo?", + "streamKey": "Klucz transmisji na żywo", + "Submit": "Wyślij", + "thankYou": "Dziękujemy Ci za używanie {{appName}}!", "token": "token", - "tokenAuthFailed": "Przepraszam, ale nie jesteś upoważniony do uczestnictwa w tym połączeniu", + "tokenAuthFailed": "Przepraszamy, ale nie jesteś upoważniony do uczestnictwa w tym połączeniu.", "tokenAuthFailedTitle": "Uwierzytelnianie nie powiodło się", - "transcribing": "", - "unlockRoom": "", + "transcribing": "Transkrypcja", + "unlockRoom": "Usuń spotkanie $t(lockRoomPassword)", "userPassword": "hasło użytkownika", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", - "WaitingForHost": "Oczekiwanie na komputer", + "WaitForHostMsg": "Konferencja <b>{{room}}</b> jeszcze się nie rozpoczęła. Jeśli jesteś gospodarzem, prosimy o uwierzytelnienie. Jeśli nie, prosimy czekać na przybycie gospodarza.", + "WaitForHostMsgWOk": "Konferencja <b>{{room}}</b> jeszcze się nie zaczęła. Jeśli jesteś jej gospodarzem, wybierz Ok, aby się uwierzytelnić. Jeśli nie, prosimy czekać na przybycie gospodarza.", + "WaitingForHost": "Oczekiwanie na gospodarza…", "Yes": "Tak", - "yourEntireScreen": "" - }, - "\u0005dialog": { - "accessibilityLabel": {} + "yourEntireScreen": "Cały Twój ekran" }, "dialOut": { - "statusMessage": "" + "statusMessage": "jest teraz {{status}}" + }, + "documentSharing": { + "title": "Współdzielony dokument" }, "feedback": { - "average": "Średni", - "bad": "Źle", - "detailsLabel": "", - "good": "Prawdziwy", - "rateExperience": "Oceń proszę swoje doświadczenia z konferencji.", - "veryBad": "bardzo źle", - "veryGood": "1: Bardzo dobrze" + "average": "Średnio", + "bad": "Źle", + "detailsLabel": "Powiedz nam o tym więcej.", + "good": "Dobrze", + "rateExperience": "Jak oceniasz tę konferencję?", + "veryBad": "Bardzo źle", + "veryGood": "Bardzo dobrze" }, - "\u0005feedback": {}, "incomingCall": { - "answer": "", - "audioCallTitle": "", - "decline": "", - "productLabel": "", - "videoCallTitle": "" + "answer": "Odbierz", + "audioCallTitle": "Przychodzące połączenie", + "decline": "Odrzuć", + "productLabel": "z Jitsi Meet", + "videoCallTitle": "Przychodzące połączenie wideo" }, "info": { - "accessibilityLabel": "", - "addPassword": "", - "cancelPassword": "", - "conferenceURL": "", - "country": "", - "dialANumber": "", - "dialInConferenceID": "", - "dialInNotSupported": "", - "dialInNumber": "", - "dialInSummaryError": "", - "dialInTollFree": "", - "genericError": "", - "inviteLiveStream": "", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", - "liveStreamURL": "Strumień live", - "moreNumbers": "", - "noNumbers": "", + "accessibilityLabel": "Pokaż informacje", + "addPassword": "Dodaj $t(lockRoomPassword)", + "cancelPassword": "Anuluj $t(lockRoomPassword)", + "conferenceURL": "Odnośnik:", + "country": "Kraj", + "dialANumber": "Aby dołączyć do spotkania, wprowadź jeden z tych numerów i podaj pin.", + "dialInConferenceID": "PIN:", + "dialInNotSupported": "Przepraszamy, aktualnie wybieranie nie jest obsługiwane.", + "dialInNumber": "Wdzwoń się:", + "dialInSummaryError": "Błąd podczas przetwarzania danych do wdzwonienia. Spróbuj ponownie później.", + "dialInTollFree": "Numer bezpłatny", + "genericError": "Ups, coś poszło nie tak.", + "inviteLiveStream": "Aby obejrzeć transmisję na żywo z tego spotkania, kliknij ten odnośnik: {{url}}", + "invitePhone": "Aby skorzystać z połączenia telefonicznego, wprowadź: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Poszukujesz innego numeru do wdzwonienia?\nZobacz listę numerów: {{url}}\n\n\nJeśli wdzwaniasz się z telefonu pokojowego, połącz się bez dźwięku: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Zostałeś zaproszony do dołączenia do spotkania.", + "inviteURLFirstPartPersonal": "{{name}} zaprasza Cię na spotkanie.\n", + "inviteURLSecondPart": "\nDołącz do spotkania:\n{{url}}\n", + "liveStreamURL": "Transmisja na żywo:", + "moreNumbers": "Więcej numerów", + "noNumbers": "Brak numerów do wdzwonienia.", "noPassword": "Brak", - "noRoom": "", - "numbers": "", - "password": "", + "noRoom": "Nie podano pokoju do wdzwonienia.", + "numbers": "Numery do wdzwonienia", + "password": "$t(lockRoomPasswordUppercase):", "title": "Współdziel", - "tooltip": "", - "label": "" + "tooltip": "Udostępnij odnośnik i informacje do wdzwonienia się na to spotkanie", + "label": "Poinformuj o spotkaniu" }, - "\u0005info": {}, "inviteDialog": { - "alertText": "", - "header": "", - "searchCallOnlyPlaceholder": "", - "searchPeopleOnlyPlaceholder": "", - "searchPlaceholder": "", - "send": "" + "alertText": "Nie udało się zaprosić niektórych uczestników.", + "header": "Zaproś", + "searchCallOnlyPlaceholder": "Podaj numer telefonu", + "searchPeopleOnlyPlaceholder": "Szukaj uczestników", + "searchPlaceholder": "Uczestnik lub numer telefonu", + "send": "Wyślij" }, "inlineDialogFailure": { - "msg": "", - "retry": "", - "support": "", - "supportMsg": "" + "msg": "Nieco niedopisaliśmy.", + "retry": "Spróbuj ponownie", + "support": "Wsparcie", + "supportMsg": "Jeśli to się powtarza, skontaktuj się z nami przez" }, "keyboardShortcuts": { - "focusLocal": "Focus on your video", - "focusRemote": "Focus on another person's video", - "fullScreen": "Otwórz / Zamknij pełny ekran", - "keyboardShortcuts": "Skróty klawiaturowe:", + "focusLocal": "Powiększ wideo", + "focusRemote": "Skup na obrazie innej osoby", + "fullScreen": "Przejdź w tryb lub zamknij pełny ekran", + "keyboardShortcuts": "Skróty klawiaturowe", "localRecording": "Wyświetlanie lub ukrywanie lokalnych elementów sterujących zapisem", - "mute": "Wyłącz lub włącz mikrofon.", - "pushToTalk": "naciśnij i mów", - "raiseHand": "Podnieś lub opuść rękę.", - "showSpeakerStats": "Pokaż statystyki głośników", - "toggleChat": "Otwórz lub zamknij panel czat.", - "toggleFilmstrip": "Pokazywanie lub ukrywanie miniatur wideo", - "toggleScreensharing": "Przełączanie pomiędzy kamerą i wspóldzieleniem ekranu", - "toggleShortcuts": "Pokaż lub ukryj skróty klawiaturowe", - "videoMute": "Włączanie i wyłączanie aparatu fotograficznego" + "mute": "Włącz lub wyłącz swój mikrofon", + "pushToTalk": "Naciśnij, aby mówić", + "raiseHand": "Podnieś lub opuść rękę", + "showSpeakerStats": "Pokaż statystyki mówcy", + "toggleChat": "Otwórz lub zamknij rozmowę", + "toggleFilmstrip": "Wyświetl lub ukryj miniaturki video", + "toggleScreensharing": "Przełącz pomiędzy kamerą i wspóldzieleniem ekranu", + "toggleShortcuts": "Wyświetl lub ukryj skróty klawiaturowe", + "videoMute": "Uruchom lub zatrzymaj kamerę", + "videoQuality": "Zarządzanie jakością połączeń" }, - "\u0005keyboardShortcuts": {}, "liveStreaming": { - "busy": "", - "busyTitle": "", - "changeSignIn": "", - "choose": "", - "chooseCTA": "", - "enterStreamKey": "", - "error": "Strumieniowanie live nie powiodło się. Spróbuj później.", - "errorAPI": "", + "busy": "Pracujemy nad zwolnieniem zasobów transmisyjnych. Spróbuj ponownie za kilka minut.", + "busyTitle": "Wszyscy transmitujący są aktualnie zajęci", + "changeSignIn": "Przełącz konta.", + "choose": "Wybierz transmisję na żywo", + "chooseCTA": "Wybierz opcję transmisji. Jesteś obecnie zalogowany jako {{email}}.", + "enterStreamKey": "Wpisz tutaj swój klucz transmisji na żywo YouTube.", + "error": "Transmitowanie na żywo nie powiodło się. Spróbuj ponownie.", + "errorAPI": "Wystąpił błąd podczas uzyskiwania dostępu do transmisji w YouTube. Proszę spróbować zalogować się ponownie.", "errorLiveStreamNotEnabled": "", - "expandedOff": "", - "expandedOn": "", - "expandedPending": "", - "failedToStart": "Strumieniowanie live nie powiodło się", - "getStreamKeyManually": "", - "invalidStreamKey": "", - "off": "Strumieniowanie live zastopowane", + "expandedOff": "Transmisja na żywo została zatrzymana", + "expandedOn": "Spotkanie jest obecnie transmitowane na YouTube.", + "expandedPending": "Transmisja na żywo rozpoczyna się…", + "failedToStart": "Transmitowanie na żywo nie uruchomiło się", + "getStreamKeyManually": "Nie byliśmy w stanie pobrać żadnych transmisji na żywo. Spróbuj uzyskać klucz do transmisji na żywo z YouTube.", + "invalidStreamKey": "Klucz transmisji na żywo może być nieprawidłowy.", + "off": "Transmitowanie na żywo zostało zatrzymane", + "offBy": "{{name}} zatrzymał transmisję na żywo", "on": "Strumień live", - "pending": "Start strumieniowania live...", - "serviceName": "", - "signedInAs": "", - "signIn": "", - "signInCTA": "", - "signOut": "", - "start": "Zatrzymaj transmisję live", - "streamIdHelp": "", - "unavailableTitle": "" + "onBy": "{{name}} rozpoczął transmisję na żywo", + "pending": "Start strumieniowania live…", + "serviceName": "Usługa transmisji na żywo", + "signedInAs": "Jesteś obecnie zalogowany jako:", + "signIn": "Zaloguj się z Google", + "signInCTA": "Zaloguj się lub wpisz swój klucz do transmisji na żywo YouTube.", + "signOut": "Wyloguj się", + "start": "Rozpocznij transmisję na żywo", + "streamIdHelp": "Co to jest?", + "unavailableTitle": "Transmisja na żywo jest niedostępna" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { - "off": "", - "on": "", - "unknown": "" + "off": "Wyłączone", + "on": "Włączone", + "unknown": "Nieznane" }, - "dialogTitle": "", - "duration": "", - "durationNA": "", - "encoding": "", + "dialogTitle": "Kontrolki lokalnego nagrywania", + "duration": "Długość", + "durationNA": "N/D", + "encoding": "Kodowanie", "label": "", - "labelToolTip": "", - "localRecording": "", + "labelToolTip": "Nagrywanie lokalne jest włączone", + "localRecording": "Nagrywanie lokalne", "me": "To ja", "messages": { - "engaged": "", + "engaged": "Włączono nagrywanie lokalne.", "finished": "", "finishedModerator": "", - "notModerator": "" + "notModerator": "Nie jesteś moderatorem. Nie możesz rozpoczynać i zatrzymywać lokalnego nagrywania." }, - "moderator": "", - "no": "", - "participant": "", - "participantStats": "", - "sessionToken": "", - "start": "Zatrzymaj nagrywanie", + "moderator": "Moderujący", + "no": "Nie", + "participant": "Uczestnik", + "participantStats": "Statystyki uczestników", + "sessionToken": "Token sesji", + "start": "Rozpocznij nagrywanie", "stop": "Zatrzymaj nagrywanie", "yes": "Tak" }, - "\u0005localRecording": {}, - "lockRoomPassword": "", - "lockRoomPasswordUppercase": "", + "lockRoomPassword": "hasło", + "lockRoomPasswordUppercase": "Hasło", "me": "to ja", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", + "connectedOneMember": "{{name}} dołączył do spotkania", + "connectedThreePlusMembers": "{{name}} i {{count}} innych osób dołączyło do spotkania", + "connectedTwoMembers": "{{first}} i {{second}} dołączyli do spotkania", "disconnected": "rozłączone", "focus": "Fokus konferencji", - "focusFail": "_składnik_nie dostępny - zastosuj w _ms_sek", - "grantedTo": "Prawa moderatora przyznane _to_!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "focusFail": "{{component}} jest niedostępny - ponowienie w ciągu {{ms}} sec", + "grantedTo": "Prawa moderatora przyznane {{to}}!", + "invitedOneMember": "{{name}} został zaproszony", + "invitedThreePlusMembers": "{{name}} i {{count}} innych osób zostało zaproszone", + "invitedTwoMembers": "{{first}} i {{second}} zostali zaproszeni", + "kickParticipant": "{{kicked}} został usunięty przez {{kicker}}", "me": "To ja", "moderator": "Prawa moderatora przydzielone!", - "muted": "Masz wyciszony mikrofon", + "muted": "Rozpoczęto wyciszenie konwersacji.", "mutedTitle": "Jesteś wyciszony!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "", + "mutedRemotelyTitle": "Zostałeś wyciszony przez {{participantDisplayName}}!", + "mutedRemotelyDescription": "Zawsze możesz wyłączyć wyciszenie, gdy będziesz gotowy do mówienia. Wycisz, gdy skończysz, aby nie hałasować podczas spotkania.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) usunięty przez innego uczestnika", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) ustawiony przez innego uczestnika", + "raisedHand": "{{name}} chce mówić.", "somebody": "Ktoś", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "", - "suboptimalExperienceTitle": "", - "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "startSilentTitle": "Dołączyłeś bez wyjścia dźwiękowego!", + "startSilentDescription": "Ponownie dołącz do spotkania, aby włączyć dźwięk", + "suboptimalBrowserWarning": "", + "suboptimalExperienceTitle": "Ostrzeżenie przeglądarki", + "unmute": "Wyłącz wyciszenie", + "newDeviceCameraTitle": "Wykryto nową kamerę", + "newDeviceAudioTitle": "Wykryto nowe urządzenie dźwiękowe", + "newDeviceAction": "Użyj" }, "passwordSetRemotely": "wybrane przez innego uczestnika", "passwordDigitsOnly": "", - "poweredby": "Uruchomiono", + "poweredby": "napędzane dzięki", "presenceStatus": { - "busy": "", - "calling": "", + "busy": "Zajęte", + "calling": "Dzwonienie…", "connected": "Połączono", - "connecting": "Nawiązywanie połączenia", - "connecting2": "Nawiązywanie połączenia", + "connecting": "Łączenie…", + "connecting2": "Łączenie*...", "disconnected": "Rozłączony", - "expired": "", - "ignored": "", - "initializingCall": "", - "invited": "", - "rejected": "", - "ringing": "" + "expired": "Wygasłe", + "ignored": "Zignorowane", + "initializingCall": "Inicjalizacja połączenia…", + "invited": "Zaproszony", + "rejected": "Odrzucony", + "ringing": "Dzwonek…" }, - "\u0005presenceStatus": {}, "profile": { "setDisplayNameLabel": "Podaj swoją wyświetlaną nazwę", "setEmailInput": "Wprowadź adres e-mail", - "setEmailLabel": "Ustaw email swojego gravatara", - "title": "" + "setEmailLabel": "Ustaw adres poczty elektronicznej swojego Gravatara", + "title": "Profil" }, + "raisedHand": "Chcesz się odezwać ?", "recording": { - "authDropboxText": "", + "authDropboxText": "Prześlij na Dropbox", "availableSpace": "", - "beta": "", - "busy": "", - "busyTitle": "", - "error": "Nagranie się nie powiodło. Proszę, spróbuj ponownie.", - "expandedOff": "Nagrywanie zatrzymane", - "expandedOn": "", - "expandedPending": "", + "beta": "BETA", + "busy": "Pracujemy nad uwolnieniem zasobów nagrywania. Proszę spróbować ponownie za kilka minut.", + "busyTitle": "Wszystkie urządzenia nagrywania są obecnie zajete", + "error": "Nagranie się nie powiodło. Proszę spróbować ponownie.", + "expandedOff": "Nagrywanie zostało zatrzymane", + "expandedOn": "Spotkanie jest obecnie nagrywane.", + "expandedPending": "Nagrywanie się rozpoczyna…", "failedToStart": "Nagrywanie nie jest możliwe", - "fileSharingdescription": "", - "live": "", - "loggedIn": "", + "fileSharingdescription": "Współdziel nagranie z uczestnikami spotkania", + "live": "NA ŻYWO", + "loggedIn": "Zalogowano jako {{userName}}", "off": "Nagrywanie zatrzymane", + "offBy": "{{name}} zatrzymał nagrywanie", "on": "Nagrywanie", - "pending": "", - "rec": "", - "serviceDescription": "", - "serviceName": "", - "signIn": "", - "signOut": "", + "onBy": "{{name}} rozpoczął nagrywanie", + "pending": "Przygotowanie do nagrania spotkania…", + "rec": "NAGRYWANIE", + "serviceDescription": "Twoje nagranie zostanie zapisane przez usługę nagrywania", + "serviceName": "Usługa nagrywania", + "signIn": "Zaloguj się", + "signOut": "Wyloguj się", "unavailable": "", - "unavailableTitle": "" + "unavailableTitle": "Nagrywanie niedostępne" }, - "\u0005recording": {}, "sectionList": { - "pullToRefresh": "" + "pullToRefresh": "Przeciągnij, aby odświeżyć" }, "settings": { "calendar": { "about": "", - "disconnect": "Rozłączony", - "microsoftSignIn": "Zaloguj się w firmie Microsoft", + "disconnect": "Rozłącz", + "microsoftSignIn": "Zaloguj się z Microsoft", "signedIn": "", - "title": "" + "title": "Kalendarz" }, "devices": "Urządzenia", - "followMe": "Wszyscy za mną", + "followMe": "Wszyscy widzą mnie", "language": "Język", - "loggedIn": "", - "moderator": "", + "loggedIn": "Zalogowano jako {{name}}", + "moderator": "Moderacja", "more": "Więcej", "name": "Nazwa", "noDevice": "Brak", "selectAudioOutput": "Wyjście audio", "selectCamera": "Kamera", "selectMic": "Mikrofon", - "startAudioMuted": "Wszyscy się wyciszyli", - "startVideoMuted": "Wszyscy się ukryli", + "startAudioMuted": "Wycisz wszystkich dołączających", + "startVideoMuted": "Ukryj wszystkich dołączających", "title": "Ustawienia" }, - "\u0005settings": { - "calendar": {} - }, "settingsView": { - "alertOk": "", + "advanced": "", + "alertOk": "OK", "alertTitle": "Uwaga", - "alertURLText": "", - "buildInfoSection": "", - "conferenceSection": "", - "displayName": "", - "email": "", + "alertURLText": "Wprowadzony adres URL serwera jest nieprawidłowy", + "buildInfoSection": "Informacja o kompilacji", + "conferenceSection": "Konferencja", + "disableCallIntegration": "", + "disableP2P": "", + "displayName": "Wyświetlana nazwa", + "email": "E-mail", "header": "Ustawienia", - "profileSection": "", - "serverURL": "", - "startWithAudioMuted": "", - "startWithVideoMuted": "", - "version": "" + "profileSection": "Profil", + "serverURL": "Adres URL serwera", + "showAdvanced": "", + "startWithAudioMuted": "Rozpocznij z wyciszonym dźwiękiem", + "startWithVideoMuted": "Rozpocznij z wyłączonym obrazem", + "version": "Wersja" }, "share": { "dialInfoText": "", - "mainText": "" + "mainText": "Kliknij na poniższy odnośnik, aby dołączyć do spotkania:\n{{roomUrl}}" }, - "speaker": "głośnik", + "speaker": "Głośnik", "speakerStats": { - "hours": "", - "minutes": "", + "hours": "{{count}} godz.", + "minutes": "{{count}} min.", "name": "Nazwa", - "seconds": "", - "speakerStats": "Statystyki głośników", + "seconds": "{{count}} sek.", + "speakerStats": "Statystyki mówców", "speakerTime": "" }, - "\u0005speakerStats": {}, "startupoverlay": { - "policyText": "", - "title": "" + "policyText": " ", + "title": "{{app}} potrzebuje używać Twój mikrofon i kamerę." }, "suspendedoverlay": { - "rejoinKeyTitle": "Dołącz do nas", - "text": "", + "rejoinKeyTitle": "Dołącz ponownie", + "text": "Naciśnij przycisk <i>Dołącz ponownie</i>, aby połączyć się ponownie.", "title": "Twoja rozmowa wideo została przerwana, ponieważ komputer zasnął." }, "toolbar": { "accessibilityLabel": { "audioOnly": "Przełączanie tylko audio", "audioRoute": "Wybierz urządzenie dźwiękowe", - "callQuality": "", + "callQuality": "Zarządzanie jakością obrazu", "cc": "Przełączanie napisów", - "chat": "Przełączanie okna czatu", + "chat": "Przełączanie okna rozmowy", "document": "Przełączanie wspólnego dokumentu", - "feedback": "Zostaw informację zwrotną", + "download": "Pobierz nasze aplikacje", + "feedback": "Zostaw swoją opinię", "fullScreen": "Przełączanie trybu pełnoekranowego", "hangup": "Zostaw rozmowę", + "help": "Pomoc", "invite": "Zapraszaj ludzi", - "kick": "", + "kick": "Usuń uczestnika", "localRecording": "Przełączanie lokalnych urządzeń sterujących zapisem danych", - "lockRoom": "", + "lockRoom": "Przełączenie hasła spotkania", "moreActions": "Przełączanie menu więcej działań", "moreActionsMenu": "Więcej działań w menu", "mute": "Uruchamianie wyciszonego audycji", "pip": "Tryb przełączania obrazu-w-obrazie", + "privateMessage": "Wyślij wiadomość prywatną", "profile": "Edytuj swój profil", "raiseHand": "Przełączyć rękę w górę", - "recording": "Zapisywanie przełączania", - "remoteMute": "", + "recording": "Przełączanie nagrywania", + "remoteMute": "Wycisz uczestnika", "Settings": "Ustawienia przełączania", - "sharedvideo": "", + "sharedvideo": "Przełącz udostępnianie obrazu na YouTube", "shareRoom": "Zaproś kogoś", "shareYourScreen": "Przełączanie podziału ekranu", "shortcuts": "Przełączanie skrótów klawiszowych", "show": "", - "speakerStats": "Przełączanie statystyk dotyczących głośników", - "tileView": "Przełączanie widoku dachówki", - "toggleCamera": "", + "speakerStats": "Przełączanie statystyk dotyczących mówców", + "tileView": "Przełącz widok kafelkowy", + "toggleCamera": "Przełączanie kamery", "videomute": "Przełączanie wyciszonego filmu wideo", - "videoblur": "" + "videoblur": "Przełącz rozmazanie obrazu" }, "addPeople": "Dodaj ludzi do swojego telefonu", - "audioOnlyOff": "Wyłącz tryb tylko audio", - "audioOnlyOn": "Wyłącz tryb tylko audio", + "audioOnlyOff": "Wyłącz tryb słabego łącza", + "audioOnlyOn": "Włącz tryb słabego łącza", "audioRoute": "Wybierz urządzenie dźwiękowe", "authenticate": "Uwierzytelnianie", - "callQuality": "Zarządzanie jakością połączeń", - "chat": "Otwórz / Zamknij Czat", - "closeChat": "", + "callQuality": "Zarządzanie jakością obrazu", + "chat": "Otwórz / Zamknij rozmowę", + "closeChat": "Zamknij rozmowę", "documentClose": "Zamknij wspólny dokument", "documentOpen": "Otwarty współdzielony dokument", + "download": "Pobierz nasze aplikacje", "enterFullScreen": "Wyświetlanie pełnego ekranu", - "enterTileView": "", + "enterTileView": "Wejdź w kafelkowy widok", "exitFullScreen": "Wyświetlanie pełnego ekranu", - "exitTileView": "", - "feedback": "Zostaw informację zwrotną", - "hangup": "Wyjazd", + "exitTileView": "Wyjdź z kafelkowego widoku", + "feedback": "Zostaw swoją opinię", + "hangup": "Opuść", + "help": "Pomoc", "invite": "Zapraszaj ludzi", "login": "Zaloguj", "logout": "Wyloguj", - "lowerYourHand": "", + "lowerYourHand": "Opuść rękę", "moreActions": "Więcej działań", - "mute": "Wycisz / Pogłośnij", - "openChat": "", + "mute": "Włącz / Wyłącz mikrofon", + "noAudioSignalTitle": "", + "noAudioSignalDesc": "", + "noAudioSignalDescSuggestion": "", + "openChat": "Otwórz rozmowę", "pip": "Wprowadź tryb obrazu w obrazie", + "privateMessage": "Wyślij wiadomość prywatną", "profile": "Edytuj swój profil", - "raiseHand": "Podnoszenie / opuszczanie ręki", - "raiseYourHand": "", + "raiseHand": "Podnieś / Opuść rękę", + "raiseYourHand": "Podnieś rękę", "Settings": "Ustawienia", - "sharedvideo": "Udostępniaj wideo w Youtube", + "sharedvideo": "Udostępnij wideo w Youtube", "shareRoom": "Zaproś kogoś", - "shortcuts": "Wyświetlanie skrótów", - "speakerStats": "Statystyki głośników", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", + "shortcuts": "Wyświetl skróty", + "speakerStats": "Statystyki mówców", + "startScreenSharing": "Zacznij współdzielenie ekranu", + "startSubtitles": "Uruchom napisy", + "stopScreenSharing": "Zatrzymaj współdzielenie ekranu", + "stopSubtitles": "Zatrzymaj napisy", "stopSharedVideo": "Zatrzymaj wideo z YouTube", - "talkWhileMutedPopup": "Próbujesz mówić? Jesteś wyciszony", - "tileViewToggle": "Przełączanie widoku dachówki", - "toggleCamera": "", - "videomute": "Kamera start / stop ", - "startvideoblur": "", - "stopvideoblur": "" + "talkWhileMutedPopup": "Próbujesz mówić? Jesteś wyciszony.", + "tileViewToggle": "Przełączanie kafelkowego widoku", + "toggleCamera": "Przełączanie kamery", + "videomute": "Włącz / Wyłącz kamerę", + "startvideoblur": "Rozmaż moje tło", + "stopvideoblur": "Wyłącz rozmazanie tła" }, - "\u0005toolbar": {}, "transcribing": { - "ccButtonTooltip": "", - "error": "Nagranie się nie powiodło. Proszę, spróbuj ponownie.", - "expandedLabel": "", - "failedToStart": "", - "labelToolTip": "", - "off": "", - "pending": "", - "start": "", - "stop": "", - "tr": "" + "ccButtonTooltip": "Uruchom / Zatrzymaj napisy", + "error": "Przepisywanie się nie powiodło. Proszę spróbować ponownie.", + "expandedLabel": "Transkrypcja jest obecnie włączona", + "failedToStart": "Błąd uruchomienia transkrypcji", + "labelToolTip": "Spotkanie jest transkrybowane", + "off": "Transkrypcja została zatrzymana", + "pending": "Przygotowanie do transkrypcji spotkania…", + "start": "Rozpocznij wyświetlanie napisów", + "stop": "Zatrzymaj wyświetlanie napisów", + "tr": "TR" }, - "\u0005transcribing": {}, "userMedia": { - "androidGrantPermissions": "", - "chromeGrantPermissions": "", - "edgeGrantPermissions": "Wybierz <b><i>OK</i></b>, gdy przegladarka zapyta o pozwolenie.", - "electronGrantPermissions": "wyraź zgodę na użycie kamery i mikrofonu", - "firefoxGrantPermissions": "", + "androidGrantPermissions": "Wybierz <b><i>Pozwól</i></b>, gdy przeglądarka zapyta o pozwolenie.", + "chromeGrantPermissions": "Wybierz <b><i>Pozwól</i></b>, gdy przeglądarka zapyta o pozwolenie.", + "edgeGrantPermissions": "Wybierz <b><i>Tak</i></b>, gdy przeglądarka zapyta o pozwolenie.", + "electronGrantPermissions": "Udziel przyzwolenia na użycie swej kamery i mikrofonu", + "firefoxGrantPermissions": "Wybierz <b><i>Udostępnij wybrane urządzenie</i></b>, gdy przeglądarka zapyta o pozwolenie.", "iexplorerGrantPermissions": "Wybierz <b><i>OK</i></b>, gdy przegladarka zapyta o pozwolenie.", - "nwjsGrantPermissions": "wyraź zgodę na użycie kamery i mikrofonu", - "operaGrantPermissions": "", - "react-nativeGrantPermissions": "Wybierz <b><i>OK</i></b>, gdy przegladarka zapyta o pozwolenie.", + "nwjsGrantPermissions": "Udziel przyzwolenia na użycie swej kamery i mikrofonu", + "operaGrantPermissions": "Wybierz <b><i>Pozwól</i></b>, gdy przeglądarka zapyta o pozwolenie.", + "react-nativeGrantPermissions": "Wybierz <b><i>Pozwól</i></b>, gdy przeglądarka zapyta o pozwolenie.", "safariGrantPermissions": "Wybierz <b><i>OK</i></b>, gdy przegladarka zapyta o pozwolenie." }, - "\u0005userMedia": {}, "videoSIPGW": { "busy": "", - "busyTitle": "", - "errorAlreadyInvited": "", - "errorInvite": "", - "errorInviteFailed": "", - "errorInviteFailedTitle": "", - "errorInviteTitle": "", - "pending": "" + "busyTitle": "Usługa pokoju jest obecnie zajęta", + "errorAlreadyInvited": "{{displayName}} jest już zaproszony", + "errorInvite": "Konferencja nie została jeszcze ustanowiona. Prosimy spróbować ponownie później.", + "errorInviteFailed": "Pracujemy nad rozwiązaniem tego problemu. Prosimy ponownie spróbować później.", + "errorInviteFailedTitle": "Błąd zaproszenia {{displayName}}", + "errorInviteTitle": "Błąd zaproszenia do pokoju", + "pending": "{{displayName}} został zaproszony" }, "videoStatus": { - "audioOnly": "", - "audioOnlyExpanded": "", - "callQuality": "", - "hd": "", - "highDefinition": "", - "labelTooiltipNoVideo": "", - "labelTooltipAudioOnly": "", - "ld": "", - "lowDefinition": "", - "onlyAudioAvailable": "", - "onlyAudioSupported": "", - "p2pEnabled": "", + "audioOnly": "DŹW", + "audioOnlyExpanded": "Jesteś w trybie słabego łącza. W tym trybie będziesz otrzymywać tylko dźwięk i udostępnianie ekranu.", + "callQuality": "Jakość obrazu", + "hd": "HD", + "hdTooltip": "Podgląd obrazu w wysokiej rozdzielczości", + "highDefinition": "Wysoka rozdzielczość", + "labelTooiltipNoVideo": "Brak obrazu", + "labelTooltipAudioOnly": "Włączono tryb słabego łącza", + "ld": "LD", + "ldTooltip": "Podgląd obrazu w niskiej rozdzielczości", + "lowDefinition": "Niska rozdzielczość", + "onlyAudioAvailable": "Dostępny jest tylko dźwięk", + "onlyAudioSupported": "Obsługujemy tylko dźwięk w tej przeglądarce.", + "p2pEnabled": "Połączenie Peer to Peer włączone", "p2pVideoQualityDescription": "", - "recHighDefinitionOnly": "", - "sd": "", - "standardDefinition": "" + "recHighDefinitionOnly": "Preferowana wysoka rozdzielczość.", + "sd": "SD", + "sdTooltip": "Podgląd obrazu w standardowej rozdzielczości", + "standardDefinition": "Standardowa rozdzielczość" }, "videothumbnail": { "domute": "Wyciszenie", "flip": "Odwrócenie", - "kick": "Spadaj!", - "moderator": "", + "kick": "Wyrzuć", + "moderator": "Moderujący", "mute": "Uczestnik ma wyciszone audio", "muted": "Wyciszony", - "remoteControl": "Zdalne sterowanie", + "remoteControl": "Kontrola zdalna", "show": "", - "videomute": "" + "videomute": "Uczestnik zatrzymał kamerę" }, "welcomepage": { "accessibilityLabel": { @@ -710,24 +729,26 @@ "appDescription": "No dalej, pogawędka wideo z całym zespołem. W rzeczywistości, zaproś wszystkich, których znasz. {{app}} jest w pełni zaszyfrowanym, w 100% otwartym rozwiązaniem wideokonferencyjnym, z którego możesz korzystać przez cały dzień, codziennie, za darmo - bez konieczności posiadania konta.", "audioVideoSwitch": { "audio": "Głos", - "video": "nagranie" + "video": "Obraz" }, - "calendar": "", + "calendar": "Kalendarz", "connectCalendarButton": "Podłącz swój kalendarz", "connectCalendarText": "", "enterRoomTitle": "Rozpocznij nowe spotkanie", + "roomNameAllowedChars": "Nazwa spotkania nie powinna zawierać żadnego z tych znaków: ?, &, :, ', \", %, #.", "go": "IDŹ", + "goSmall": "IDŹ", "join": "", - "info": "", - "privacy": "Prywatność", + "info": "Informacje", + "privacy": "Polityka prywatności", "recentList": "Niedawno", "recentListDelete": "Usuń", "recentListEmpty": "Twoja ostatnia lista jest obecnie pusta. Rozmawiaj ze swoim zespołem, a wszystkie ostatnie spotkania znajdziesz tutaj.", - "reducedUIText": "", + "reducedUIText": "Witamy w {{app}}!", "roomname": "Podaj nazwę sali konferencyjnej", "roomnameHint": "Wprowadź nazwę lub adres URL pokoju, do którego chcesz dołączyć. Możesz wymyślić nazwę, po prostu pozwól, aby osoby, z którymi się spotykasz, znały ją tak, aby wpisały tę samą nazwę.", - "sendFeedback": "Wyślij informację zwrotną", - "terms": "określenia", - "title": "Bezpieczna, w pełni funkcjonalna i całkowicie bezpłatna wideokonferencja." + "sendFeedback": "Wyślij opinię", + "terms": "Warunki korzystania", + "title": "Bezpieczna, w pełni funkcjonalna i całkowicie bezpłatna wideokonferencja" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ptBR.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ptBR.json index 0ac585d85..a53cea581 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ptBR.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ptBR.json @@ -21,10 +21,11 @@ "bluetooth": "Bluetooth", "headphones": "Fones de ouvido", "phone": "Celular", - "speaker": "Apresentador" + "speaker": "Alto-falantes", + "none": "Sem dispositivos de áudio disponível" }, "audioOnly": { - "audioOnly": "Somente áudio" + "audioOnly": "Largura de banda baixa" }, "calendarSync": { "addMeetingURL": "Adicionar um link da reunião", @@ -45,13 +46,18 @@ "today": "Hoje" }, "chat": { - "error": "Erro: sua mensagem \"{{originalText}}\" não foi enviada. Motivo: {{error}}", + "error": "Erro: sua mensagem não foi enviada. Motivo: {{error}}", + "fieldPlaceHolder": "Digite sua mensagem aqui", "messagebox": "Digite uma mensagem", + "messageTo": "Mensagem privada para {{recipient}}", + "noMessagesMessage": "Não há mensagens na reunião ainda. Inicie uma conversa aqui!", "nickname": { "popover": "Escolha um apelido", - "title": "Digite um apelido para usar o chat" + "title": "Digite um apelido para usar o bate-papo" }, - "title": "Chat" + "privateNotice": "Mensagem privada para {{recipient}}", + "title": "Bate-papo", + "you": "você" }, "connectingOverlay": { "joiningRoom": "Conectando você à reunião…" @@ -66,7 +72,11 @@ "DISCONNECTED": "Desconectado", "DISCONNECTING": "Desconectando", "ERROR": "Erro", - "RECONNECTING": "Ocorreu um problema de rede. Reconectando..." + "RECONNECTING": "Ocorreu um problema de rede. Reconectando...", + "LOW_BANDWIDTH": "O vídeo de {{displayName}} foi desativado para economizar largura de banda", + "GOT_SESSION_ID": "Obtendo ID da sessão... Feito", + "GET_SESSION_ID_ERROR": "Erro ao obter o ID da sessão: {{code}}", + "FETCH_SESSION_ID": "Obtendo ID da sessão..." }, "connectionindicator": { "address": "Endereço:", @@ -97,7 +107,7 @@ "status": "Conexão:", "transport": "Transporte:", "transport_plural": "Transportes:", - "turn": " (virar)" + "e2e_rtt": "E2E RTT:" }, "dateUtils": { "earlier": "Mais cedo", @@ -107,7 +117,7 @@ "deepLinking": { "appNotInstalled": "Você precisa do aplicativo móvel {{app}} para participar da reunião no seu telefone.", "description": "Nada acontece? Estamos tentando iniciar sua reunião no aplicativo desktop {{app}}. Tente novamente ou inicie ele na aplicação web {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "Nada aconteceu? Tentamos iniciar sua reunião no aplicativo de desktop {{app}}.", "downloadApp": "Baixe o Aplicativo", "launchWebButton": "Iniciar na web", "openApp": "Continue na aplicação", @@ -115,6 +125,7 @@ "tryAgainButton": "Tente novamente no desktop" }, "defaultLink": "ex.: {{url}}", + "defaultNickname": "ex.: João Pedro", "deviceError": { "cameraError": "Falha ao acessar sua câmera", "cameraPermission": "Erro ao obter permissão para a câmera", @@ -132,7 +143,7 @@ "liveStreaming": "Transmissão ao vivo" }, "allow": "Permitir", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Outro participante já está compartilhando um vídeo. Esta conferência permite apenas um vídeo compartilhado por vez.", "alreadySharedVideoTitle": "Somente um vídeo compartilhado é permitido por vez", "applicationWindow": "Janela de aplicativo", "Back": "Voltar", @@ -158,51 +169,51 @@ "contactSupport": "Contate o suporte", "copy": "Copiar", "dismiss": "Dispensar", - "displayNameRequired": "", + "displayNameRequired": "Oi! Qual o seu nome?", "done": "Feito", - "enterDisplayName": "", + "enterDisplayName": "Digite seu nome aqui", "error": "Erro", "externalInstallationMsg": "Você precisa instalar nossa extensão de compartilhamento de tela.", "externalInstallationTitle": "Extensão requerida", "goToStore": "Vá para a loja virtual", "gracefulShutdown": "O sistema está em manutenção. Por favor tente novamente mais tarde.", "IamHost": "Eu sou o anfitrião", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Senha incorreta", "incorrectPassword": "Usuário ou senha incorretos", "inlineInstallationMsg": "Você precisa instalar nossa extensão de compartilhamento de tela.", "inlineInstallExtension": "Instalar agora", "internalError": "Oops! Alguma coisa está errada. O seguinte erro ocorreu: {{error}}", "internalErrorTitle": "Erro interno", - "kickMessage": "", + "kickMessage": "Você pode contatar com {{participantDisplayName}} para obter mais detalhes.", "kickParticipantButton": "Remover", "kickParticipantDialog": "Tem certeza de que deseja remover este participante?", - "kickParticipantTitle": "Deixar mudo este participante?", - "kickTitle": "", + "kickParticipantTitle": "Chutar este participante?", + "kickTitle": "Ai! {{participantDisplayName}} expulsou você da reunião", "liveStreaming": "Transmissão ao Vivo", "liveStreamingDisabledForGuestTooltip": "Visitantes não podem iniciar transmissão ao vivo.", "liveStreamingDisabledTooltip": "Iniciar transmissão ao vivo desativada.", "lockMessage": "Falha ao travar a conferência.", - "lockRoom": "", + "lockRoom": "Adicionar reunião $t(lockRoomPasswordUppercase)", "lockTitle": "Bloqueio falhou", "logoutQuestion": "Deseja encerrar a sessão e finalizar a conferência?", "logoutTitle": "Encerrar sessão", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "O limite para o número máximo de participantes foi atingido. A conferência está cheia. Entre em contato com o proprietário da reunião ou tente novamente mais tarde!", + "maxUsersLimitReachedTitle": "Limite máximo de participantes atingido", "micConstraintFailedError": "Seu microfone não satisfaz algumas condições necessárias.", "micNotFoundError": "O microfone não foi encontrado.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Vá para as configurações do seu computador para ativar o som do microfone e ajustar seu nível", + "micNotSendingDataTitle": "Seu microfone está mudo pelas configurações do sistema", "micPermissionDeniedError": "Não foi permitido acessar o seu microfone. Você ainda pode entrar na conferência, mas sem enviar áudio. Clique no botão do microfone para tentar reparar.", "micUnknownError": "Não pode usar o microfone por uma razão desconhecida.", "muteParticipantBody": "Você não está habilitado para tirar o mudo deles, mas eles podem tirar o mudo deles mesmos a qualquer tempo.", "muteParticipantButton": "Mudo", - "muteParticipantDialog": "Tem certeza de que deseja silenciar este participante? Você não poderá desativar a opção silenciar dele, mas ele poderá fazer isso quando desejar.", + "muteParticipantDialog": "Tem certeza de que deseja silenciar este participante? Você não poderá desfazer isso, mas o participante pode reabilitar o áudio a qualquer momento.", "muteParticipantTitle": "Deixar mudo este participante?", "Ok": "Ok", - "passwordLabel": "", - "passwordNotSupported": "Configuração de senha para a reunião não é suportada.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "A reunião foi travada por um participante. Por favor, insira a $t(lockRoomPassword) para entrar.", + "passwordNotSupported": "A configuração de uma reunião $t(lockRoomPassword) não é suportada.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) não suportado", + "passwordRequired": "$t(lockRoomPasswordUppercase) requerido", "popupError": "Seu navegador está bloqueando janelas popup deste site. Habilite os popups nas configurações de segurança no seu navegador e tente novamente.", "popupErrorTitle": "Popup bloqueado", "recording": "Gravando", @@ -217,7 +228,7 @@ "remoteControlStopMessage": "A sessão de controle remoto terminou!", "remoteControlTitle": "Conexão de área de trabalho remota", "Remove": "Remover", - "removePassword": "", + "removePassword": "Remove $t(lockRoomPassword)", "removeSharedVideoMsg": "Deseja remover seu vídeo compartilhado?", "removeSharedVideoTitle": "Remover vídeo compartilhado", "reservationError": "Erro de sistema de reserva", @@ -226,8 +237,12 @@ "screenSharingFailedToInstall": "Oops! Falhou a instalação da extensão de compartilhamento de tela.", "screenSharingFailedToInstallTitle": "A extensão de compartilhamento de tela falhou ao instalar", "screenSharingFirefoxPermissionDeniedError": "Algo deu errado enquanto estávamos tentando compartilhar sua tela. Por favor, certifique-se de que você nos deu permissão para fazê-lo. ", - "screenSharingFirefoxPermissionDeniedTitle": "Opa! Não foi possível iniciar o compartilhamento de tela.", + "screenSharingFirefoxPermissionDeniedTitle": "Opa! Não foi possível iniciar o compartilhamento de tela!", "screenSharingPermissionDeniedError": "Oops! Alguma coisa está errada com suas permissões de compartilhamento de tela. Recarregue e tente de novo.", + "sendPrivateMessage": "Você enviou uma mensagem privada recentemente. Tem intenção de responder em privado, ou deseja enviar sua mensagem para o grupo?", + "sendPrivateMessageCancel": "Enviar para o grupo", + "sendPrivateMessageOk": "Enviar em privado", + "sendPrivateMessageTitle": "Enviar em privado?", "serviceUnavailable": "Serviço indisponível", "sessTerminated": "Chamada terminada", "Share": "Compartilhar", @@ -250,17 +265,27 @@ "tokenAuthFailed": "Desculpe, você não está autorizado a entrar nesta chamada.", "tokenAuthFailedTitle": "Falha de autenticação", "transcribing": "Transcrevendo", - "unlockRoom": "", + "unlockRoom": "Remove a reunião $t(lockRoomPassword)", "userPassword": "senha do usuário", "WaitForHostMsg": "A conferência <b>{{room}}</b> ainda não começou. Se você é o anfitrião, faça a autenticação. Do contrário, aguarde a chegada do anfitrião.", "WaitForHostMsgWOk": "A conferência <b>{{room}}</b> ainda não começou. Se você é o anfitrião, pressione Ok para autenticar. Do contrário, aguarde a chegada do anfitrião.", "WaitingForHost": "Esperando o hospedeiro...", "Yes": "Sim", - "yourEntireScreen": "Toda sua tela" + "yourEntireScreen": "Toda sua tela", + "screenSharingAudio": "Compartilhar áudio", + "muteEveryoneStartMuted": "Todos iniciam silenciados daqui para frente", + "muteEveryoneSelf": "a si próprio", + "muteEveryoneDialog": "Tem certeza que deseja silenciar todos? Você não poderá ativar o som deles, mas eles podem ativar o som eles mesmo a qualquer momento.", + "muteEveryoneTitle": "Silenciar todos?", + "muteEveryoneElseTitle": "Silenciar todo mundo exceto {{whom}}?", + "muteEveryoneElseDialog": "Uma vez silenciados, você não poderá reativar o som deles, mas eles poderão reativar o som a qualquer momento." }, "dialOut": { "statusMessage": "está agora {{status}}" }, + "documentSharing": { + "title": "Documento compartilhado" + }, "feedback": { "average": "Média", "bad": "Ruim", @@ -279,8 +304,8 @@ }, "info": { "accessibilityLabel": "Mostrar info", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Adicione $t(lockRoomPassword)", + "cancelPassword": "Cancela $t(lockRoomPassword)", "conferenceURL": "Link:", "country": "País", "dialANumber": "Para entrar na reunião, disque um desses números e depois insira o PIN.", @@ -291,18 +316,18 @@ "dialInTollFree": "Chamada gratuita", "genericError": "Oops, alguma coisa deu errado.", "inviteLiveStream": "Para ver a transmissão ao vivo da reunião, clique no link: {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", + "invitePhone": "Para participar por telefone, toque aqui: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Procurando um número de discagem diferente?\nVeja os números de discagem da reunião: {{url}} \n\n\nSe você também estiver discando através de um telefone da sala, participe sem conectar-se ao áudio: {{silentUrl}}", "inviteURLFirstPartGeneral": "Você foi convidado para uma reunião.", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartPersonal": "{{name}} está convidando você para uma reunião.\n", + "inviteURLSecondPart": "\nEntre na reunião:\n{{url}}\n", "liveStreamURL": "Transmissão ao vivo:", "moreNumbers": "Mais números", "noNumbers": "Sem números de discagem.", "noPassword": "Nenhum", "noRoom": "Nenhuma sala foi especificada para entrar.", "numbers": "Números de discagem", - "password": "", + "password": "$t(lockRoomPasswordUppercase):", "title": "Compartilhar", "tooltip": "Compartilhar link e discagem para esta reunião", "label": "Informações da reunião" @@ -335,7 +360,8 @@ "toggleFilmstrip": "Mostrar ou ocultar miniaturas de vídeo", "toggleScreensharing": "Trocar entre câmera e compartilhamento de tela", "toggleShortcuts": "Mostrar ou ocultar atalhos de teclado", - "videoMute": "Iniciar ou parar sua câmera" + "videoMute": "Iniciar ou parar sua câmera", + "videoQuality": "Gerenciar qualidade da chamada" }, "liveStreaming": { "busy": "Estamos trabalhando para liberar os recursos de transmissão. Tente novamente em alguns minutos.", @@ -349,34 +375,38 @@ "errorLiveStreamNotEnabled": "Transmissão ao vivo não está ativada em {{email}}. Ative a transmissão ao vivo ou registre numa conta com transmissão ao vivo ativada.", "expandedOff": "A transmissão ao vivo foi encerrada", "expandedOn": "A reunião está sendo transmitida pelo YouTube.", - "expandedPending": "A transmissão ao vivo está sendo iniciada…", + "expandedPending": "Iniciando a transmissão ao vivo...", "failedToStart": "Falha ao iniciar a transmissão ao vivo", - "getStreamKeyManually": "", + "getStreamKeyManually": "Não conseguimos buscar nenhuma transmissão ao vivo. Tente obter sua chave de transmissão ao vivo no YouTube.", "invalidStreamKey": "A senha para transmissão ao vivo pode estar incorreta.", "off": "Transmissão ao vivo encerrada", + "offBy": "{{name}} parou a transmissão ao vivo", "on": "Transmissão ao Vivo", + "onBy": "{{name}} iniciou a transmissão ao vivo", "pending": "Iniciando Transmissão ao Vivo...", "serviceName": "Serviço de Transmissão ao Vivo", - "signedInAs": "Você está conectado atualmente como:", + "signedInAs": "Você está conectado como:", "signIn": "Faça login no Google", "signInCTA": "Faça login ou insira sua chave de transmissão ao vivo do YouTube.", "signOut": "Sair", "start": "Iniciar uma transmissão ao vivo", "streamIdHelp": "O que é isso?", - "unavailableTitle": "Transmissão ao vivo indisponível" + "unavailableTitle": "Transmissão ao vivo indisponível", + "googlePrivacyPolicy": "Política de Privacidade do Google", + "youtubeTerms": "Termos de serviços do YouTube" }, "localRecording": { "clientState": { - "off": "Off", - "on": "On", + "off": "Desligado", + "on": "Ligado", "unknown": "Desconhecido" }, "dialogTitle": "Controles da Gravação Local", "duration": "Duração", - "durationNA": "N/A", + "durationNA": "N/D", "encoding": "Codificando", "label": "LOR", - "labelToolTip": "Gravação local está envolvida", + "labelToolTip": "Gravação local ativada", "localRecording": "Gravação local", "me": "Eu", "messages": { @@ -403,33 +433,33 @@ "connectedTwoMembers": "{{first}} e {{second}} entraram na reunião", "disconnected": "desconectado", "focus": "Foco da conferência", - "focusFail": "{{component}} não disponĩvel - tente em {{ms}} seg.", + "focusFail": "{{component}} não disponível - tente em {{ms}} seg", "grantedTo": "Direitos de moderador concedido para {{to}}!", - "invitedOneMember": "{{displayName}} foi convidado", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{name}} foi convidado(a)", + "invitedThreePlusMembers": "{{name}} e {{count}} outros foram convidados", + "invitedTwoMembers": "{{first}} e {{second}} foram convidados", + "kickParticipant": "{{kicked}} foi chutado por {{kicker}}", "me": "Eu", "moderator": "Direitos de moderador concedidos!", "muted": "Você iniciou uma conversa em mudo.", "mutedTitle": "Você está mudo!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyTitle": "Você foi silenciado por {{participantDisplayName}}!", + "mutedRemotelyDescription": "Você sempre pode ativar o som quando estiver pronto para falar. Retire o som quando terminar para manter o ruído longe da reunião.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removido por outro participante", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) definido por outro participante", "raisedHand": "{{name}} gostaria de falar.", "somebody": "Alguém", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Eer ... temos medo de que sua experiência com o {{appName}} não seja tão boa aqui. Estamos procurando maneiras de melhorar isso, mas até lá tente usar um dos <a href='static/recommendedBrowsers.html' target='_blank'> navegadores totalmente compatíveis</a>.", + "startSilentTitle": "Você entrou sem saída de áudio!", + "startSilentDescription": "Volte à reunião para habilitar o áudio", + "suboptimalBrowserWarning": "Tememos que sua experiência de reunião não seja tão boa aqui. Estamos procurando maneiras de melhorar isso, mas até então, tente usar um dos <a href='static/recommendedBrowsers.html' target='_blank'>navegadores completamente suportados</a>.", "suboptimalExperienceTitle": "Alerta do navegador", - "unmute": "", + "unmute": "Ativar som", "newDeviceCameraTitle": "Nova câmera detectada", "newDeviceAudioTitle": "Novo dispositivo de áudio detectado", "newDeviceAction": "Usar" }, "passwordSetRemotely": "Definido por outro participante", - "passwordDigitsOnly": "", + "passwordDigitsOnly": "Até {{number}} dígitos", "poweredby": "distribuído por", "presenceStatus": { "busy": "Ocupado", @@ -443,16 +473,17 @@ "initializingCall": "Iniciando Chamada...", "invited": "Convidar", "rejected": "Rejeitado", - "ringing": "Chamando..." + "ringing": "Tocando..." }, "profile": { "setDisplayNameLabel": "Definir seu nome de exibição", "setEmailInput": "Digite e-mail", - "setEmailLabel": "Definir seu email de gravatar", + "setEmailLabel": "Definir seu e-mail de Gravatar", "title": "Perfil" }, + "raisedHand": "Gostaria de falar", "recording": { - "authDropboxText": "Enviar para o Dropbox.", + "authDropboxText": "Enviar para o Dropbox", "availableSpace": "Espaço disponível: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de gravação)", "beta": "BETA", "busy": "Estamos trabalhando para liberar recursos de gravação. Tente novamente em alguns minutos.", @@ -466,12 +497,14 @@ "live": "AOVIVO", "loggedIn": "Conectado como {{userName}}", "off": "Gravação parada", + "offBy": "{{name}} parou a gravação", "on": "Gravando", + "onBy": "{{name}} iniciou a gravação", "pending": "Preparando para gravar a reunião...", "rec": "REC", "serviceDescription": "Sua gravação será salva pelo serviço de gravação", "serviceName": "Serviço de gravação", - "signIn": "entrar", + "signIn": "Entrar", "signOut": "Sair", "unavailable": "Oops! O {{serviceName}} está indisponível. Estamos trabalhando para resolver o problema. Por favor, tente mais tarde.", "unavailableTitle": "Gravação indisponível" @@ -500,28 +533,34 @@ "selectMic": "Microfone", "startAudioMuted": "Todos iniciam mudos", "startVideoMuted": "Todos iniciam ocultos", - "title": "Configurações" + "title": "Configurações", + "speakers": "Alto-faltantes", + "microphones": "Microfones" }, "settingsView": { + "advanced": "Avançado", "alertOk": "OK", "alertTitle": "Atenção", "alertURLText": "A URL digitada do servidor é inválida", "buildInfoSection": "Informações de compilação", "conferenceSection": "Conferência", + "disableCallIntegration": "Desativar integração de chamada nativa", + "disableP2P": "Desativar modo ponto a ponto", "displayName": "Nome de exibição", "email": "E-mail", "header": "Configurações", "profileSection": "Perfil", "serverURL": "URL do servidor", + "showAdvanced": "Mostrar configurações avançadas", "startWithAudioMuted": "Iniciar sem áudio", "startWithVideoMuted": "Iniciar sem vídeo", "version": "Versão" }, "share": { - "dialInfoText": "", - "mainText": "Clique no seguinte link para entrar na reunião:{{roomUrl}}\n" + "dialInfoText": "\n\n=====\n\nDeseja apenas discar no seu telefone?\n\n{{defaultDialInNumber}}Clique neste link para ver os números de telefone para esta reunião\n{{dialInfoPageUrl}}", + "mainText": "Clique no seguinte link para entrar na reunião:\n{{roomUrl}}" }, - "speaker": "Apresentador", + "speaker": "Alto-falantes", "speakerStats": { "hours": "{{count}}h", "minutes": "{{count}}m", @@ -543,13 +582,15 @@ "accessibilityLabel": { "audioOnly": "Alternar para apenas áudio", "audioRoute": "Selecionar o dispositivo de som", - "callQuality": "Gerenciar qualidade da chamada", + "callQuality": "Gerenciar qualidade do vídeo", "cc": "Alternar legendas", "chat": "Alternar para janela de chat", "document": "Alternar para documento compartilhado", + "download": "Baixe nossos aplicativos", "feedback": "Deixar feedback", "fullScreen": "Alternar para tela cheia", "hangup": "Sair da chamada", + "help": "Ajuda", "invite": "Convidar pessoas", "kick": "Remover participante", "localRecording": "Alternar controles de gravação local", @@ -558,46 +599,56 @@ "moreActionsMenu": "Menu de mais ações", "mute": "Alternar mudo do áudio", "pip": "Alternar modo Picture-in-Picture", + "privateMessage": "Enviar mensagem privada", "profile": "Editar seu perfil", "raiseHand": "Alternar levantar a mão", "recording": "Alternar gravação", "remoteMute": "Silenciar participante", "Settings": "Alternar configurações", - "sharedvideo": "Alternar compartilhamento de vídeo do Youtube", + "sharedvideo": "Alternar compartilhamento de vídeo do YouTube", "shareRoom": "Convidar alguém", "shareYourScreen": "Alternar compartilhamento de tela", "shortcuts": "Alternar atalhos", - "show": "", + "show": "Mostrar no palco", "speakerStats": "Alternar estatísticas do apresentador", "tileView": "Alternar visualização em blocos", "toggleCamera": "Alternar câmera", "videomute": "Alternar mudo do vídeo", - "videoblur": "" + "videoblur": "Alternar desfoque de vídeo", + "toggleFilmstrip": "Alterar tira de filme", + "muteEveryone": "Silenciar todos", + "moreOptions": "Mostrar mais opções" }, "addPeople": "Adicionar pessoas à sua chamada", - "audioOnlyOff": "Desativar modo somente áudio", - "audioOnlyOn": "Desativar modo somente áudio", + "audioOnlyOff": "Desabilitar modo de largura de banda baixa", + "audioOnlyOn": "Habilitar modo de largura de banda baixa", "audioRoute": "Selecionar o dispositivo de som", "authenticate": "Autenticar", - "callQuality": "Gerenciar qualidade da chamada", + "callQuality": "Gerenciar qualidade do vídeo", "chat": "Abrir ou fechar o bate-papo", "closeChat": "Fechar chat", "documentClose": "Fechar documento compartilhado", "documentOpen": "Abrir documento compartilhado", + "download": "Baixe nossos aplicativos", "enterFullScreen": "Ver em tela cheia", "enterTileView": "Entrar em exibição de bloco", "exitFullScreen": "Sair da tela cheia", "exitTileView": "Sair de exibição de bloco", "feedback": "Deixar feedback", "hangup": "Sair", + "help": "Ajuda", "invite": "Convidar pessoas", "login": "Iniciar sessão", "logout": "Encerrar sessão", "lowerYourHand": "Baixar a mão", "moreActions": "Mais ações", "mute": "Mudo / Não mudo", + "noAudioSignalTitle": "Não há entrada de áudio vindo do seu microfone!", + "noAudioSignalDesc": "Se você não o desativou propositalmente das configurações do sistema ou do hardware, considere trocar o dispositivo.", + "noAudioSignalDescSuggestion": "Se você não o desativou propositalmente das configurações do sistema ou do hardware, considere trocar para o dispositivo sugerido.", "openChat": "Abrir chat", "pip": "Entrar em modo Quadro-a-Quadro", + "privateMessage": "Enviar mensagem privada", "profile": "Editar seu perfil", "raiseHand": "Erguer / Baixar sua mão", "raiseYourHand": "Levantar a mão", @@ -615,19 +666,25 @@ "tileViewToggle": "Alternar visualização em blocos", "toggleCamera": "Alternar câmera", "videomute": "Iniciar ou parar a câmera", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Desfocar meu plano de fundo", + "stopvideoblur": "Desativar desfoque de fundo", + "noisyAudioInputDesc": "Parece que o microfone está fazendo barulho, considere silenciar ou alterar o dispositivo.", + "noisyAudioInputTitle": "O seu microfone parece estar barulhento!", + "noAudioSignalDialInLinkDesc": "Discar números", + "noAudioSignalDialInDesc": "Você também pode discar usando:", + "muteEveryone": "Silenciar todos", + "moreOptions": "Mais opções" }, "transcribing": { "ccButtonTooltip": "Iniciar/parar legendas", "error": "Transcrição falhou. Tente novamente.", - "expandedLabel": "Transcrição ligada", + "expandedLabel": "Transcrição ativada", "failedToStart": "Transcrição falhou ao iniciar", "labelToolTip": "A reunião esta sendo transcrita", "off": "Transcrição parada", "pending": "Preparando a transcrição da reunião...", - "start": "Iniciar / Parar de mostrar as legendas", - "stop": "Iniciar / Parar de mostrar as legendas", + "start": "Exibir legendas", + "stop": "Não exibir legendas", "tr": "TR" }, "userMedia": { @@ -654,20 +711,23 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "Você está no modo somente áudio. Esse modo economiza internet mas não permite ver o vídeo dos outros.", - "callQuality": "", + "audioOnlyExpanded": "Você está em modo de banda baixa. Neste modo, se recebe somente áudio e compartilhamento de tela.", + "callQuality": "Qualidade de vídeo", "hd": "HD", + "hdTooltip": "Ver vídeo em alta definição", "highDefinition": "Alta definição (HD)", "labelTooiltipNoVideo": "Sem vídeo", - "labelTooltipAudioOnly": "Modo somente de áudio habilitado", + "labelTooltipAudioOnly": "Modo de largura de banda baixa habilitada", "ld": "LD", + "ldTooltip": "Ver vídeo em baixa definição", "lowDefinition": "Baixa definição (LD)", "onlyAudioAvailable": "Somente áudio disponível", "onlyAudioSupported": "Suportamos somente áudio neste navegador.", "p2pEnabled": "Ponto-a-ponto habilitada", - "p2pVideoQualityDescription": "", - "recHighDefinitionOnly": "Preferência para alta definição", + "p2pVideoQualityDescription": "No modo ponto a ponto, a qualidade do vídeo recebido só pode ser alternada entre alta e apenas áudio. Outras configurações não serão respeitadas até que o ponto a ponto seja encerrado.", + "recHighDefinitionOnly": "Preferência para alta definição.", "sd": "SD", + "sdTooltip": "Ver vídeo em definição padrão", "standardDefinition": "Definição padrão" }, "videothumbnail": { @@ -678,15 +738,16 @@ "mute": "Participante está mudo", "muted": "Mudo", "remoteControl": "Controle remoto", - "show": "", - "videomute": "" + "show": "Mostrar no palco", + "videomute": "O participante parou a câmera", + "domuteOthers": "Silenciar todos os demais" }, "welcomepage": { "accessibilityLabel": { "join": "Toque para entrar", "roomname": "Digite o nome da sala" }, - "appDescription": "Vá em frente, converse por vídeo com toda a equipe. De fato, convide todos que você conhece. {{app}} é uma solução de videoconferência totalmente criptografada e 100% de código aberto que você pode usar todos os dias, a cada dia, gratuitamente — sem necessidade de conta.", + "appDescription": "Vá em frente, converse por vídeo com toda a equipe. De fato, convide todos que você conhece. {{app}} é uma solução de videoconferência totalmente criptografada e 100% de código aberto que você pode usar todos os dias, a cada dia, gratuitamente — sem necessidade de conta.", "audioVideoSwitch": { "audio": "Voz", "video": "Vídeo" @@ -695,18 +756,33 @@ "connectCalendarButton": "Conectar seu calendário", "connectCalendarText": "Conecte seu calendário para ver todas as reuniões em {{app}}. Além disso, adicione reuniões de {{provider}} ao seu calendário e inicie-as com apenas um clique.", "enterRoomTitle": "Iniciar uma nova reunião", + "roomNameAllowedChars": "Nome da reunião não deve conter qualquer um destes caracteres: ?. &, :, ', \", %, #.", "go": "IR", - "join": "Entrar", + "goSmall": "IR", + "join": "CRIAR / ENTRAR", "info": "Informações", "privacy": "Política de Privacidade", "recentList": "Recente", "recentListDelete": "Remover", "recentListEmpty": "Sua lista recente está vazia. As reuniões que você realizar serão exibidas aqui.", - "reducedUIText": "", + "reducedUIText": "Bem-vindo ao {{app}}!", "roomname": "Digite o nome da sala", "roomnameHint": "Digite o nome ou a URL da sala que você deseja entrar. Você pode digitar um nome, e apenas deixe para as pessoas que você quer se reunir digitem o mesmo nome.", "sendFeedback": "Enviar comentários", "terms": "Termos", - "title": "Videoconferências mais seguras, flexíveis e totalmente gratuitas" + "title": "Videoconferências mais seguras, flexíveis e totalmente gratuitas", + "getHelp": "Obter ajuda" + }, + "helpView": { + "header": "Centro de ajuda" + }, + "lonelyMeetingExperience": { + "youAreAlone": "Você é o único na reunião", + "button": "Convidar outros" + }, + "chromeExtensionBanner": { + "dontShowAgain": "Não me mostre isso de novo", + "buttonText": "Instalar extensão do Chrome", + "installExtensionText": "Instale a extensão par integração com Google Calendar e Office 365" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ru.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ru.json index ddbbfeacd..63d9b7765 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ru.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-ru.json @@ -3,16 +3,16 @@ "add": "Пригласить", "countryNotSupported": "Эта страна пока не поддерживается.", "countryReminder": "Вызов не в США? Пожалуйста, убедитесь, что указали код страны!", - "disabled": "Поиск не дал результата", - "failedToAdd": "", + "disabled": "Поиск не дал результата.", + "failedToAdd": "Не удалось добавить участников", "footerText": "Вызов номера отключен.", "loading": "Поиск людей и номеров телефонов", - "loadingNumber": "Поиск людей для приглашения", + "loadingNumber": "Проверка номера телефона", "loadingPeople": "Поиск людей для приглашения", "noResults": "Поиск не дал результата", "noValidNumbers": "Пожалуйста, введите номер телефона", "searchNumbers": "Добавить номера телефонов", - "searchPeople": "Поиск не дал результата", + "searchPeople": "Поиск людей", "searchPeopleAndNumbers": "Поиск людей или добавление их телефонов", "telephone": "Номер: {{number}}", "title": "Пригласить людей на эту встречу" @@ -22,22 +22,22 @@ "headphones": "Наушники", "phone": "Телефон", "speaker": "Колонка", - "none": "" + "none": "Не обнаружены звуковые устройства" }, "audioOnly": { - "audioOnly": "Не нагружает канал" + "audioOnly": "Только звук" }, "calendarSync": { "addMeetingURL": "Добавить ссылку конференции", - "confirmAddLink": "", + "confirmAddLink": "Вы хотите добавить ссылку Jitsi к этому календарному событию?", "error": { - "appConfiguration": "", - "generic": "", - "notSignedIn": "" + "appConfiguration": "Неправильно настроена интеграция календаря.", + "generic": "Произошла ошибка. Проверьте настройки календаря или попробуйте обновить его.", + "notSignedIn": "В процессе аутентификации для просмотра событий календаря произошла ошибка. Пожалуйста, проверьте настройки календаря и попробуйте снова войти в систему." }, - "join": "", - "joinTooltip": "", - "nextMeeting": "следующая встреча", + "join": "Присоединиться", + "joinTooltip": "Присоединиться к конференции", + "nextMeeting": "следующая конференция", "noEvents": "Нет запланированных событий.", "ongoingMeeting": "текущая конференция", "permissionButton": "Открыть настройки", @@ -46,16 +46,26 @@ "today": "Сегодня" }, "chat": { - "error": "", - "messagebox": "", + "error": "Ошибка: Ваше сообщение не было отправлено. Причина: {{error}}", + "fieldPlaceHolder": "Введите здесь ваше сообщение", + "messagebox": "Введите сообщение", + "messageTo": "Личное сообщение пользователю {{recipient}}", + "noMessagesMessage": "В конференции пока нет никаких сообщений. Начните разговор!", "nickname": { "popover": "Выберите имя", - "title": "" + "title": "Введите имя для использования чата" }, - "title": "" + "privateNotice": "Личное сообщение пользователю {{recipient}}", + "title": "Чат", + "you": "вы" + }, + "chromeExtensionBanner": { + "installExtensionText": "Установите расширение для интеграции с Google Календарь и Office 365", + "buttonText": "Установить расширение Chrome", + "dontShowAgain": "Не показывай мне это снова" }, "connectingOverlay": { - "joiningRoom": "" + "joiningRoom": "Пытаемся присоединиться к вашей конференции..." }, "connection": { "ATTACHED": "Прикреплено", @@ -67,23 +77,26 @@ "DISCONNECTED": "Отключено", "DISCONNECTING": "Отключение", "ERROR": "Ошибка", - "RECONNECTING": "Проблема с сетью. Переподключение..." + "RECONNECTING": "Проблема с сетью. Переподключение...", + "LOW_BANDWIDTH": "Видео для {{displayName}} приостановлено из-за низкой пропускной способности", + "GOT_SESSION_ID": "Получение идентификатора сеанса … Готово", + "GET_SESSION_ID_ERROR": "Ошибка получения идентификатора сеанса: {{code}}" }, "connectionindicator": { "address": "Адрес:", "bandwidth": "Средняя скорость:", "bitrate": "Битрейт:", - "bridgeCount": "", - "connectedTo": "", + "bridgeCount": "Количество серверов: ", + "connectedTo": "Подключен к:", "framerate": "Частота кадров:", - "less": "Меньше", - "localaddress_0": "Локальные адреса:", - "localaddress_1": "Локальные адреса:", - "localaddress_2": "Локальные адреса:", - "localport_0": "Локальные порты:", - "localport_1": "Локальные порты:", - "localport_2": "Локальные порты:", - "more": "Больше", + "less": "Краткая информация", + "localaddress_0": "Локальный адрес:", + "localaddress_1": "Локальных адреса:", + "localaddress_2": "Локальных адресов:", + "localport_0": "Локальный порт:", + "localport_1": "Локальных порта:", + "localport_2": "Локальных портов:", + "more": "Подробная информация", "packetloss": "Потери пакетов:", "quality": { "good": "Хорошо", @@ -92,17 +105,18 @@ "nonoptimal": "не оптимально", "poor": "плохо" }, - "remoteaddress_0": "Удаленные адреса:", - "remoteaddress_1": "Удаленные адреса:", - "remoteaddress_2": "Удаленные адреса:", - "remoteport_0": "Удаленные порты:", - "remoteport_1": "Удаленные порты:", - "remoteport_2": "Удаленные порты:", + "remoteaddress_0": "Удаленный адрес:", + "remoteaddress_1": "Удаленных адреса:", + "remoteaddress_2": "Удаленных адресов:", + "remoteport_0": "Удаленный порт:", + "remoteport_1": "Удаленных порта:", + "remoteport_2": "Удаленных портов:", "resolution": "Разрешение:", "status": "Связь:", - "transport_0": "Методы отправки:", - "transport_1": "Методы отправки:", - "transport_2": "Методы отправки:" + "transport_0": "Метод отправки:", + "transport_1": "Метода отправки:", + "transport_2": "Методов отправки:", + "e2e_rtt": "" }, "dateUtils": { "earlier": "Ранее", @@ -112,7 +126,7 @@ "deepLinking": { "appNotInstalled": "Чтобы присоединиться к этой встрече на телефоне, нужно мобильное приложение {{app}}.", "description": "Ничего не случилось? Мы попытались запустить вашу встречу в настольном приложении {{app}}. Повторите попытку или запустите ее в веб-приложении {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "Ничего не произошло? Мы попытались запустить вашу конференцию в настольном приложении {{app}}", "downloadApp": "Скачать приложение", "launchWebButton": "Запустить в браузере", "openApp": "Перейти к приложению", @@ -138,7 +152,7 @@ "liveStreaming": "Трансляция" }, "allow": "Разрешить", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Другой участник уже поделился ссылкой на видео. Данная конференция позволяет одновременно делиться только одним видео.", "alreadySharedVideoTitle": "Допускается показ только одного видео", "applicationWindow": "Окно приложения", "Back": "Назад", @@ -164,66 +178,66 @@ "contactSupport": "Связь с поддержкой", "copy": "Копировать", "dismiss": "Отклонить", - "displayNameRequired": "", + "displayNameRequired": "Привет! Как тебя зовут?", "done": "Готово", - "enterDisplayName": "", + "enterDisplayName": "Пожалуйста, введите свое имя", "error": "Ошибка", "externalInstallationMsg": "Вам необходимо установить наше дополнение для совместного использования рабочего стола.", "externalInstallationTitle": "Требуется расширение", "goToStore": "Перейти к интернет-магазину", "gracefulShutdown": "Технические работы. Пожалуйста, попробуйте позже.", "IamHost": "Я организатор", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Неверный пароль", "incorrectPassword": "Ошибка имени пользователя или пароля", "inlineInstallationMsg": "Вам необходимо установить наше дополнение для совместного использования рабочего стола.", "inlineInstallExtension": "Установить", "internalError": "Что-то пошло не так. Ошибка: {{error}}", "internalErrorTitle": "Внутренняя ошибка", - "kickMessage": "", - "kickParticipantButton": "", - "kickParticipantDialog": "", - "kickParticipantTitle": "", - "kickTitle": "", + "kickMessage": "Вы можете связаться с {{participantDisplayName}} для получения более подробной информации.", + "kickParticipantButton": "Выгнать", + "kickParticipantDialog": "Вы уверены, что хотите выгнать этого участника?", + "kickParticipantTitle": "Выгнать этого участника?", + "kickTitle": "Ай! {{participantDisplayName}} выгнал вас из конференции.", "liveStreaming": "Трансляция", - "liveStreamingDisabledForGuestTooltip": "Гости не могут начать трансляцию.", + "liveStreamingDisabledForGuestTooltip": "Гости не могут начать трансляцию", "liveStreamingDisabledTooltip": "Возможность трансляции отключена", "lockMessage": "Не удалось запереть конференцию", - "lockRoom": "", + "lockRoom": "Добавить конференцию $t(lockRoomPasswordUppercase)", "lockTitle": "Блокировка не удалась", "logoutQuestion": "Уверены, что хотите выйти и остановить встречу?", "logoutTitle": "Завершить сеанс", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "Достигнут лимит на максимальное количество участников. Конференция переполнена. Пожалуйста, свяжитесь с организатором конференции или повторите попытку позже!", + "maxUsersLimitReachedTitle": "Достигнут максимальный лимит участников", "micConstraintFailedError": "Ваш микрофон не отвечает определенным требованиям.", "micNotFoundError": "Микрофон не обнаружен.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Перейдите в настройки компьютера, чтобы включить микрофон и настроить уровень чувствительности.", + "micNotSendingDataTitle": "Ваш микрофон отключен системными настройками", "micPermissionDeniedError": "Нет доступа к микрофону. Вы можете участвовать во встрече, но другие не будут вас слышать. Используйте значок камеры в адресной строке браузера, чтобы устранить проблему.", "micUnknownError": "Неизвестная ошибка использования микрофона.", "muteParticipantBody": "Вы не можете включить им звук, но они могут сделать это сами в любое время.", "muteParticipantButton": "Выключить звук", - "muteParticipantDialog": "", + "muteParticipantDialog": "Вы уверены, что хотите отключить микрофон у данного пользователя? Вы не сможете отменить это действие, однако он сможет сам снова включить микрофон в любое время.", "muteParticipantTitle": "Приглушить этого участника?", "Ok": "Ok", - "passwordLabel": "", - "passwordNotSupported": "Установка пароля не поддерживается.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "Установка $t(lockRoomPassword) для конференции не поддерживается.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) не поддерживается", + "passwordRequired": "Требуется $t(lockRoomPasswordUppercase)", "popupError": "Ваш браузер блокирует всплывающие окна этого сайта. Пожалуйста, разрешите всплывающие окна в настройках безопасности браузера и попробуйте снова.", "popupErrorTitle": "Заблокировано всплывающее окно", "recording": "Запись", - "recordingDisabledForGuestTooltip": "Гости не могут записывать.", - "recordingDisabledTooltip": "Невозможно начать запись.", + "recordingDisabledForGuestTooltip": "Гости не могут записывать", + "recordingDisabledTooltip": "Невозможно начать запись", "rejoinNow": "Подключиться снова", "remoteControlAllowedMessage": "{{user}} принял ваш запрос на удаленное управление!", "remoteControlDeniedMessage": "{{user}} отклонил ваш запрос на удаленное управление!", - "remoteControlErrorMessage": "Произошла ошибка при попытке запросить разрешения удаленного управления от {{user}}.", + "remoteControlErrorMessage": "Произошла ошибка при попытке запросить разрешения удаленного управления от {{user}}!", "remoteControlRequestMessage": "Разрешить {{user}} удаленное управление вашим рабочим столом?", "remoteControlShareScreenWarning": "Если нажмете \"Разрешить\", то поделитесь своим экраном!", "remoteControlStopMessage": "Сессия удаленного управления завершена!", "remoteControlTitle": "Удаленное управление рабочим столом", "Remove": "Удалить", - "removePassword": "", + "removePassword": "Убрать $t(lockRoomPassword)", "removeSharedVideoMsg": "Уверены, что хотите убрать видео, которым поделились?", "removeSharedVideoTitle": "Убрать видео", "reservationError": "Ошибка системы резервирования", @@ -231,17 +245,21 @@ "retry": "Повторить", "screenSharingFailedToInstall": "Ошибка установки расширения для показа экрана.", "screenSharingFailedToInstallTitle": "Расширение для показа экрана не установлено", - "screenSharingFirefoxPermissionDeniedError": "Что-то пошло не так, когда мы пытались поделиться вашим экраном. Пожалуйста, убедитесь, что вы дали нам разрешение на это.", + "screenSharingFirefoxPermissionDeniedError": "Что-то пошло не так, когда мы пытались поделиться вашим экраном. Пожалуйста, убедитесь, что вы дали нам разрешение на это. ", "screenSharingFirefoxPermissionDeniedTitle": "Ошибка показа экрана!", "screenSharingPermissionDeniedError": "Ошибка доступа к вашему расширению для показа экрана. Пожалуйста, перезапустите браузер и попробуйте снова.", + "sendPrivateMessage": "Вы недавно получили личное сообщение. Вы хотели ответить на него, или отправить свое сообщение группе?", + "sendPrivateMessageCancel": "Отправить в общий чат", + "sendPrivateMessageOk": "Отправить приватное сообщение", + "sendPrivateMessageTitle": "Отправить личное сообщение?", "serviceUnavailable": "Служба недоступна", "sessTerminated": "Связь прервана", "Share": "Поделиться", "shareVideoLinkError": "Пожалуйста, укажите корректную ссылку Youtube.", "shareVideoTitle": "Поделиться видео", "shareYourScreen": "Показать экран", - "shareYourScreenDisabled": "Демонстрация экрана отключена.", - "shareYourScreenDisabledForGuest": "Гости не могут демонстрировать экран.", + "shareYourScreenDisabled": "Демонстрация экрана отключена", + "shareYourScreenDisabledForGuest": "Гости не могут демонстрировать экран", "startLiveStreaming": "Начать трансляцию", "startRecording": "Начать запись", "startRemoteControlErrorMessage": "Ошибка начала сессии удаленного управления!", @@ -256,17 +274,23 @@ "tokenAuthFailed": "Извините, вам не разрешено присоединиться к этому сеансу связи.", "tokenAuthFailedTitle": "Ошибка аутентификации", "transcribing": "Расшифровка", - "unlockRoom": "", + "unlockRoom": "Убрать $t(lockRoomPassword)", "userPassword": "пароль пользователя", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", + "WaitForHostMsg": "Конференция <b>{{room}}</b> еще не началась. Если вы организатор, пожалуйста, авторизируйтесь. В противном случае дождитесь организатора.", + "WaitForHostMsgWOk": "Конференция <b>{{room}}</b> еще не началась. Если вы организатор, пожалуйста, нажмите Ok для аутентификации. В противном случае, дождитесь организатора.", "WaitingForHost": "Ждем организатора...", "Yes": "Да", - "yourEntireScreen": "Весь экран" + "yourEntireScreen": "Весь экран", + "muteEveryoneElseTitle": "Заглушить всех, за исключением {{whom}}?", + "screenSharingAudio": "Поделиться аудио", + "muteEveryoneSelf": "себя" }, "dialOut": { "statusMessage": "сейчас {{status}}" }, + "documentSharing": { + "title": "Общий Документ" + }, "feedback": { "average": "Средне", "bad": "Плохо", @@ -285,41 +309,41 @@ }, "info": { "accessibilityLabel": "Показать информацию", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Установить $t(lockRoomPassword)", + "cancelPassword": "Отменить $t(lockRoomPassword)", "conferenceURL": "Ссылка:", "country": "Страна", - "dialANumber": "", + "dialANumber": "Чтобы присоединиться к конференции, наберите один из этих номеров и введите pin-код", "dialInConferenceID": "PIN:", "dialInNotSupported": "К сожалению, набор номера в настоящее время не поддерживается.", "dialInNumber": "Номер:", - "dialInSummaryError": "", - "dialInTollFree": "", + "dialInSummaryError": "Ошибка получения информации о наборе номера. Пожалуйста, повторите попытку позже", + "dialInTollFree": "Звонок бесплатный", "genericError": "Что-то пошло не так.", "inviteLiveStream": "Трансляция этой встречи: {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "invitePhone": "Для того, чтобы присоединиться по телефону, нажмите {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Ищите дпугой номер для набора?\nПосмотреть номера для набора для конференции: {{url}}\n\n\nЕсли также выполняется набор номера через телефон в комнате, подключитесь в режиме слушателя: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Вас приглашают присоединиться к конференции.", + "inviteURLFirstPartPersonal": "{{name}} приглашает Вас присоединиться к конференции. \n", + "inviteURLSecondPart": "\nПрисоединиться к конференции:\n{{url}}\n", "liveStreamURL": "Трансляция:", "moreNumbers": "Больше номеров", "noNumbers": "Нет номеров для набора.", "noPassword": "нет", "noRoom": "Для набора номера не было указано ни одной комнаты.", "numbers": "Номера для набора", - "password": "", + "password": "$t(lockRoomPasswordUppercase):", "title": "Поделиться", "tooltip": "Поделитесь ссылкой и номером для подключения к этой конференции", - "label": "" + "label": "Информация о конференции" }, "inviteDialog": { - "alertText": "", + "alertText": "Не удалось пригласить некоторых участников.", "header": "Пригласить", "searchCallOnlyPlaceholder": "Укажите номер телефона", - "searchPeopleOnlyPlaceholder": "", - "searchPlaceholder": "", - "send": "" + "searchPeopleOnlyPlaceholder": "Поиск участников", + "searchPlaceholder": "Участник или номер телефона", + "send": "Отправить" }, "inlineDialogFailure": { "msg": "Небольшая заминка.", @@ -330,9 +354,9 @@ "keyboardShortcuts": { "focusLocal": "Фокус на ваше видео", "focusRemote": "Фокус на видео другого участника", - "fullScreen": "Вкл/выкл полноэкранный режим ", + "fullScreen": "Вкл/выкл полноэкранный режим", "keyboardShortcuts": "Комбинации клавиш", - "localRecording": "", + "localRecording": "Отобразить или скрыть элементы управления локальной записи", "mute": "Микрофон (вкл./выкл.)", "pushToTalk": "Нажмите, чтобы говорить", "raiseHand": "Поднять или опустить руку", @@ -340,7 +364,7 @@ "toggleChat": "Чат (открыть/закрыть)", "toggleFilmstrip": "Показать/Скрыть краткое описание", "toggleScreensharing": "Переключиться между камерой и показом экрана", - "toggleShortcuts": "Скрыть/Показать горячие клавиши", + "toggleShortcuts": "Скрыть/Показать клавиша быстрого доступа", "videoMute": "Камера (вкл./выкл.)", "videoQuality": "Качество связи" }, @@ -349,28 +373,32 @@ "busyTitle": "Все ресурсы для трансляции уже задействованы", "changeSignIn": "Переключить аккаунты.", "choose": "Выбрать трансляцию", - "chooseCTA": "Выберите трансляцию. Вы вошли в систему как {{email}}. ", + "chooseCTA": "Выберите трансляцию. Вы вошли в систему как {{email}}.", "enterStreamKey": "Введите ваш ключ трансляции YouTube.", "error": "Ошибка трансляции. Пожалуйста, попробуйте снова.", "errorAPI": "Произошла ошибка при доступе к вашим трансляциям на YouTube. Повторите попытку входа в систему.", "errorLiveStreamNotEnabled": "Трансляция не подключена на {{email}}. Пожалуйста подключите трансляцию или войдите в аккаунт с подключенной трансляцией.", - "expandedOff": "", - "expandedOn": "", - "expandedPending": "", + "expandedOff": "Прямая трансляция остановлена", + "expandedOn": "В настоящий момент конференция транслируется на YouTube.", + "expandedPending": "Начинается прямая трансляция...", "failedToStart": "Ошибка трансляции видео", - "getStreamKeyManually": "", - "invalidStreamKey": "", + "getStreamKeyManually": "Прямые трансляций не найдены. Попробуйте получить ключ прямой трансляции от YouTube.", + "invalidStreamKey": "Похоже ключ прямой трансляции неверен.", "off": "Трансляция остановлена", + "offBy": "{{name}} остановил прямую трансляцию", "on": "Трансляция", + "onBy": "{{name}} начал прямую трансляцию", "pending": "Начинаем трансляцию...", "serviceName": "Служба трансляции", - "signedInAs": "", + "signedInAs": "В настоящее время вы вошли в систему как:", "signIn": "Войти через Google", "signInCTA": "Войдите или введите свой ключ трансляции YouTube.", "signOut": "Выход", "start": "Начать трансляцию", "streamIdHelp": "Что это?", - "unavailableTitle": "Трансляция недоступна" + "unavailableTitle": "Трансляция недоступна", + "googlePrivacyPolicy": "Политика конфиденциальности Google", + "youtubeTerms": "Условия использования YouTube" }, "localRecording": { "clientState": { @@ -384,13 +412,13 @@ "encoding": "Кодировка", "label": "Левый/Правый", "labelToolTip": "Локальная запись активна", - "localRecording": "", + "localRecording": "Локальная запись", "me": "Я", "messages": { - "engaged": "", + "engaged": "Локальная запись началась.", "finished": "Запись сессии {{token}} завершена. Пожалуйста отправьте записанный файл модератору.", - "finishedModerator": "", - "notModerator": "" + "finishedModerator": "Запись сессии {{token}} завершена. Запись локального трека сохранена. Пожалуйста, попросите других участников представить их записи", + "notModerator": "Вы не модератор и не можете начинать или останавливать локальную запись." }, "moderator": "Модератор", "no": "Нет", @@ -405,38 +433,38 @@ "lockRoomPasswordUppercase": "Пароль", "me": "я", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", + "connectedOneMember": "{{name}} присоединился к конференции", + "connectedThreePlusMembers": "{{name}} и {{count}} других пользователей присоединились к конференции", + "connectedTwoMembers": "{{first}} и {{second}} присоединились к конференции", "disconnected": "соединение разорвано", "focus": "Фокус встречи", "focusFail": "{{component}} недоступен, повторите через {{ms}} с", "grantedTo": "{{to}} получил права модератора!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{name}} был приглашен", + "invitedThreePlusMembers": "Приглашены {{name}} и {{count}} других пользователей(ля)", + "invitedTwoMembers": "{{first}} и {{second}} присоединились к конференции", + "kickParticipant": "{{kicker}} выгнал {{kicked}}", "me": "Я", "moderator": "Получены права модератора!", "muted": "Вы начали разговор без звука.", "mutedTitle": "Вы без звука!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "", + "mutedRemotelyTitle": "{{participantDisplayName}} отключил Вам микрофон!", + "mutedRemotelyDescription": "Вы всегда можете включить микрофон, когда будете готовы говорить. Отключите его, когда закончите, чтобы не транслировать шумы в конференцию.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) удален другим участником.", + "passwordSetRemotely": "Другой участник установил $t(lockRoomPasswordUppercase)", + "raisedHand": "{{name}} хотел бы выступить.", "somebody": "Кто-то", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalBrowserWarning": "", - "suboptimalExperienceTitle": "Предупреждение", - "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "startSilentTitle": "У вас отсутствует звук!", + "startSilentDescription": "Перезайдите в конференцию, чтобы включить звук", + "suboptimalBrowserWarning": "К сожалению, ваш браузер не полностью поддерживает данную систему вэбконференций. Мы работаем над проблемой, однако, пока рекомендуем вам воспользоваться <a href='static/recommendedBrowsers.html' target='_blank'> следующими браузерами</a>.", + "suboptimalExperienceTitle": "Предупреждение браузера", + "unmute": "Включить микрофон", + "newDeviceCameraTitle": "Обнаружена новая камера", + "newDeviceAudioTitle": "Обнаружено новое аудиоустройство", + "newDeviceAction": "Использовать" }, "passwordSetRemotely": "установлен другим участником", - "passwordDigitsOnly": "", + "passwordDigitsOnly": "До {{number}} цифр", "poweredby": "работает на", "presenceStatus": { "busy": "Занят", @@ -455,29 +483,31 @@ "profile": { "setDisplayNameLabel": "Отображаемое имя", "setEmailInput": "Введите e-mail", - "setEmailLabel": "E-mail для gravatar", + "setEmailLabel": "E-mail для Gravatar", "title": "Профиль" }, "raisedHand": "Хочет говорить", "recording": { - "authDropboxText": "", + "authDropboxText": "Загрузить в Dropbox", "availableSpace": "Доступно места: {{spaceLeft}} MB (примерно {{duration}} минут записи)", "beta": "БЕТА", "busy": "Мы стараемся обеспечить больше ресурсов для записи. Пожалуйста, попробуйте через несколько минут.", "busyTitle": "Все записывающие устройства заняты", "error": "Ошибка записи. Пожалуйста, попробуйте позже.", "expandedOff": "Запись остановлена", - "expandedOn": "", - "expandedPending": "", + "expandedOn": "Данная конференция записывается.", + "expandedPending": "Начинаем запись конференции...", "failedToStart": "Ошибка начала записи", - "fileSharingdescription": "", + "fileSharingdescription": "Поделиться записью с участниками конференции", "live": "Прямая трансляция", "loggedIn": "Вошел как {{userName}}", "off": "Запись остановлена", + "offBy": "{{name}} остановил запись", "on": "Запись", - "pending": "Подготовка к записи конференции...", + "onBy": "{{name}} включил запись", + "pending": "Подготовка записи конференции. . .", "rec": "Идет запись", - "serviceDescription": "", + "serviceDescription": "Ваша запись будет сохранена соответствующей службой", "serviceName": "Служба записи", "signIn": "Вход", "signOut": "Выход", @@ -508,25 +538,31 @@ "selectMic": "Микрофон", "startAudioMuted": "Все начинают с выключенным звуком", "startVideoMuted": "Все начинают в скрытом режиме", - "title": "Настройки" + "title": "Настройки", + "speakers": "Динамики", + "microphones": "Микрофоны" }, "settingsView": { + "advanced": "Дополнительные", "alertOk": "OK", "alertTitle": "Внимание", "alertURLText": "Ошибка адреса сервера", - "buildInfoSection": "", + "buildInfoSection": "Информация о сборке", "conferenceSection": "Номера для набора", + "disableCallIntegration": "Отключить встроенную интеграцию вызовов", + "disableP2P": "Отключить режим Peer-To-Peer", "displayName": "Отображаемое имя", "email": "Email", "header": "Настройки", "profileSection": "Профиль", "serverURL": "Адрес сервера", + "showAdvanced": "Показать дополнительные настройки", "startWithAudioMuted": "Начать с отключенным звуком", "startWithVideoMuted": "Начать с отключенным видео", - "version": "" + "version": "Версия" }, "share": { - "dialInfoText": "", + "dialInfoText": "\n\n=====\n\nПросто хотите набрать номер на Вашем телефоне?\n\n{{defaultDialInNumber}}Щелкните на эту ссылку, чтобы просмотреть телефонные номера для этой конференции\n{{dialInfoPageUrl}}", "mainText": "Нажмите на ссылку чтобы присоединиться к конференции:\n{{roomUrl}}" }, "speaker": "Колонка", @@ -549,91 +585,105 @@ }, "toolbar": { "accessibilityLabel": { - "audioOnly": "Вкл/выкл только звук", + "audioOnly": "Вкл/Выкл только звук", "audioRoute": "Выбрать аудиоустройство", "callQuality": "Качество связи", - "cc": "Вкл/выкл субтитры", + "cc": "Вкл/Выкл субтитры", "chat": "Показать/скрыть окно чата", "document": "Закрыть общий документ", + "download": "Скачать приложение", "feedback": "Оставить отзыв", "fullScreen": "Полноэкранный/оконный режим", "hangup": "Завершить звонок", + "help": "Справка", "invite": "Пригласить", - "kick": "", - "localRecording": "", - "lockRoom": "", + "kick": "Выкинуть участника", + "localRecording": "Вкл/Выкл кнопки записи", + "lockRoom": "Установить пароль", "moreActions": "Показать/скрыть меню доп. настроек", + "moreOptions": "Меню доп. настроек", "moreActionsMenu": "Меню доп. настроек", - "mute": "Вкл/выкл звук", - "pip": "", + "mute": "Вкл/Выкл звук", + "pip": "Вкл/Выкл режим Картинка-в-картинке", + "privateMessage": "Отправить личное сообщение", "profile": "Редактировать профиль", - "raiseHand": "", + "raiseHand": "Поднять руку", "recording": "Вкл/Выкл запись", - "remoteMute": "", - "Settings": "Вкл/выкл меню настроек", + "remoteMute": "Отключить участнику микрофон", + "Settings": "Вкл/Выкл меню настроек", "sharedvideo": "Вкл/Выкл Youtube - трансляцию", "shareRoom": "Отправить приглашение", "shareYourScreen": "Вкл/Выкл демонстрацию экрана", "shortcuts": "Вкл/Выкл значки", - "show": "", + "show": "Показать крупным планом", "speakerStats": "Вкл/Выкл статистику", - "tileView": "", - "toggleCamera": "Вкл/выкл камеру", - "videomute": "Вкл/Выкл звук в видео", - "videoblur": "" + "tileView": "Вкл/Выкл плитку", + "toggleCamera": "Вкл/Выкл камеру", + "videomute": "Вкл/Выкл видео", + "muteEveryone": "Выкл. микрофон у всех", + "videoblur": "Вкл/Выкл размытие фона" }, "addPeople": "Добавить людей к вашему сеансу связи", - "audioOnlyOff": "", - "audioOnlyOn": "", + "audioOnlyOff": "Отключить режим экономии пропуской способности", + "audioOnlyOn": "Включить режим экономии пропускной способности", "audioRoute": "Выбрать аудиоустройство", "authenticate": "Аутентифицировать", "callQuality": "Качество связи", "chat": "Чат", - "closeChat": "", + "closeChat": "Закрыть чат", "documentClose": "Закрыть общий документ", "documentOpen": "Открыть общий документ", + "download": "Скачать приложение", "enterFullScreen": "Полный экран", - "enterTileView": "", + "enterTileView": "Общий план", "exitFullScreen": "Полный экран", - "exitTileView": "", + "exitTileView": "Крупный план", "feedback": "Оставить отзыв", "hangup": "Выход", + "help": "Справка", "invite": "Пригласить", "login": "Войти", "logout": "Завершить сеанс", - "lowerYourHand": "", + "lowerYourHand": "Опустить руку", "moreActions": "Больше", - "mute": "Звук (вкл./выкл.)", - "openChat": "", - "pip": "", + "mute": "Микрофон (вкл./выкл.)", + "muteEveryone": "Выкл. микрофон у всех", + "noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!", + "noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.", + "noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:", + "noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!", + "noisyAudioInputDesc": "Возможно, ваш микрофон создает шум. Вы можете выключить его или смените устройство.", + "openChat": "Открыть чат", + "pip": "Вкл режим Картинка-в-картинке", + "privateMessage": "Отправить личное сообщение", "profile": "Редактировать профиль", "raiseHand": "Хочу говорить", - "raiseYourHand": "", + "raiseYourHand": "Поднять руку", "Settings": "Настройки", "sharedvideo": "Видео YouTube", "shareRoom": "Отправить приглашение", "shortcuts": "Комбинации клавиш", "speakerStats": "Статистика", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", + "startScreenSharing": "Начать трансляцию с экрана", + "startSubtitles": "Включить субтитры", + "stopScreenSharing": "Остановить трансляцию с экрана", + "stopSubtitles": "Отключить субтитры", "stopSharedVideo": "Остановить видео на YouTube", "talkWhileMutedPopup": "Пытаетесь говорить? У вас отключен звук.", - "tileViewToggle": "", + "tileViewToggle": "Вкл/выкл плитку", "toggleCamera": "Вкл/выкл камеру", "videomute": "Камера", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Размыть фон на видео", + "stopvideoblur": "Отключить размытие фона" }, "transcribing": { - "ccButtonTooltip": "", + "ccButtonTooltip": "Вкл. / Выкл. субтитры", "error": "Ошибка записи. Пожалуйста, попробуйте позже.", - "expandedLabel": "", - "failedToStart": "", - "labelToolTip": "", - "off": "", - "pending": "Подготовка к записи конференции...", + "expandedLabel": "Транскрипция включена", + "failedToStart": "Неудалось начать расшифровку", + "labelToolTip": "Создается транскрипция конференции.", + "off": "Расшифровка остановлена", + "pending": "Подготовка расшифровки конференции...", "start": "Вкл/Выкл показ субтитров", "stop": "Вкл/Выкл показ субтитров", "tr": "" @@ -662,20 +712,20 @@ }, "videoStatus": { "audioOnly": "Только звук", - "audioOnlyExpanded": "", - "callQuality": "", + "audioOnlyExpanded": "Активен режим экономии пропускной способности. В этом режиме доступны только звук и трансляция с экрана", + "callQuality": "Качество видео", "hd": "HD", "hdTooltip": "Видео высокого качества", "highDefinition": "Высокое качество", "labelTooiltipNoVideo": "Нет видео", - "labelTooltipAudioOnly": "", + "labelTooltipAudioOnly": "Включен режим экономии пропускной способности", "ld": "LD", "ldTooltip": "Видео низкого качества", "lowDefinition": "Низкое качество", "onlyAudioAvailable": "Только звук", "onlyAudioSupported": "В этом браузере разрешен только звук.", "p2pEnabled": "Включен режим \"точка-к-точке\"", - "p2pVideoQualityDescription": "", + "p2pVideoQualityDescription": "В режиме прямого канала связи между абонентами можно переключаться только между режимами \"только звук\" и \"высокое качество\". Остальные настройки станут доступными, когда закончится этот режим.", "recHighDefinitionOnly": "Предпочтительно высокое качество.", "sd": "SD", "sdTooltip": "Видео стандартного качества", @@ -683,14 +733,15 @@ }, "videothumbnail": { "domute": "Выключить звук", + "domuteOthers": "Выключить остальных", "flip": "Отразить", "kick": "Выкинуть", "moderator": "Модератор", "mute": "Без звука", "muted": "Звук выключен", - "remoteControl": "Дистанционное управление", - "show": "", - "videomute": "" + "remoteControl": "Начать / Остановить дистанционный контроль", + "show": "Показать крупным планом", + "videomute": "Участник отключил камеру" }, "welcomepage": { "accessibilityLabel": { @@ -699,25 +750,33 @@ }, "appDescription": "Попробуйте видеочат со всей командой. Приглашайте знакомых! {{app}} — полностью зашифрованное решение для видеоконференций с открытым исходным кодом. Пользуйтесь каждый день, бесплатно и без регистрации.", "audioVideoSwitch": { - "audio": "Календарь", + "audio": "Звук", "video": "Видео" }, "calendar": "Календарь", "connectCalendarButton": "Привязать календарь", - "connectCalendarText": "", - "enterRoomTitle": "", + "connectCalendarText": "Подключите календарь, чтобы увидеть все ваши конференции в {{app}}. Кроме того, добавив {{provider}} конференций в календарь, вы сможете запускать их одним щелчком мышки.", + "enterRoomTitle": "Начать новую видеоконференцию", + "roomNameAllowedChars": "Название конференции не должно содержать следующие символы: ?, &, :, ', \", %, #.", "go": "ОК", - "join": "ПРИСОЕДИНИТЬСЯ", - "info": "", + "goSmall": "ОК", + "join": "СОЗДАТЬ / ПРИСОЕДИНИТЬСЯ", + "info": "Инфо", "privacy": "Приватность", - "recentList": "", - "recentListDelete": "", - "recentListEmpty": "", - "reducedUIText": "", + "recentList": "Недавние", + "recentListDelete": "Удалить", + "recentListEmpty": "Сейчас ваш список недавно проведенных конференций пуст. По мере вашего пользования сервисом он будет пополняться.", + "reducedUIText": "Добро пожаловать в {{app}}!", "roomname": "Укажите название комнаты", "roomnameHint": "Укажите название комнаты или ее адрес. Можете сами создать название и передать его будущим участникам встречи, чтобы они использовали именно его.", "sendFeedback": "Обратная связь", "terms": "Условия", - "title": "" + "title": "Защищенная, полнофункциональная и совершенно бесплатная система видеоконференций" + }, + "lonelyMeetingExperience": { + "button": "Пригласить", + "youAreAlone": "Вы один в видеоконференции", + "title": "Защищенная, полнофункциональная и совершенно бесплатная система видеоконференций", + "getHelp": "Получить помощь" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sc.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sc.json new file mode 100644 index 000000000..02e6fcb94 --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sc.json @@ -0,0 +1,786 @@ +{ + "addPeople": { + "add": "Invita", + "countryNotSupported": "No est ancora possìbile de impreare custa destinatzione.", + "countryReminder": "Ses mutende in foras de is Istados Unidos? Verìfica chi insertas su còdighe de istadu!", + "disabled": "Non podes invitare gente.", + "failedToAdd": "Faddina in s'agiunta de partetzipantes", + "footerText": "Is mutidas in essida sunt disativadas.", + "loading": "Chirchende gente e nùmeros de telèfonu", + "loadingNumber": "Verifichende su nùmeru de telèfonu", + "loadingPeople": "Chirchende gente de invitare", + "noResults": "Non cointzidet perunu resultadu", + "noValidNumbers": "Inserta·nche unu nùmeru de telèfonu", + "searchNumbers": "Agiunghe nùmeros de telèfonu", + "searchPeople": "Chirca gente", + "searchPeopleAndNumbers": "Chirca gente o agiunghe is nùmeros de telèfonu issoro", + "telephone": "Telèfonu: {{number}}", + "title": "Invita gente a custa riunione" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Auriculares", + "phone": "Telèfonu", + "speaker": "Altoparlante", + "none": "Perunu dispositivu de àudio a disponimentu" + }, + "audioOnly": { + "audioOnly": "Àmpiu de banda bàsciu" + }, + "calendarSync": { + "addMeetingURL": "Agiunghe unu ligàmene a s'addòbiu", + "confirmAddLink": "Boles agiùnghere unu ligàmene de Jitsi a custu addòbiu?", + "error": { + "appConfiguration": "S'integratzione de su calendàriu no est cunfigurada bene.", + "generic": "Faddina. Controlla sa cunfiguratzione de calendàriu o proa de atualizare su calendàriu.", + "notSignedIn": "Faddina in s'autenticatzione pro visualizare eventos de calendàriu. Controlla sa cunfiguratzione de calendàriu e proa de ti torrare a autenticare." + }, + "join": "Aderi", + "joinTooltip": "Aderi a sa riunione", + "nextMeeting": "riunione imbeniente", + "noEvents": "Perunu eventu programmadu in futuru.", + "ongoingMeeting": "riunione in cursu", + "permissionButton": "Aberi sa cunfiguratzione", + "permissionMessage": "Su permissu de su calendàriu est rechèdidu pro bìdere is riuniones tuss in s'aplicatzione.", + "refresh": "Atualiza su calendàriu", + "today": "Oe" + }, + "chat": { + "error": "Faddina: su messàgiu tuo no est istadu imbiadu. Resone: {{error}}", + "fieldPlaceHolder": "Iscrie su messàgiu inoghe", + "messagebox": "Iscrie unu messàgiu", + "messageTo": "Messàgiu privadu a {{recipient}}", + "noMessagesMessage": "Perunu messàgiu ancora in sa riunione. Cumintza una tzarrada inoghe!", + "nickname": { + "popover": "Sèbera unu nòmine", + "title": "Inserta su nòmine pro impreare sa tzarrada" + }, + "privateNotice": "Messàgiu privadu a {{recipient}}", + "title": "Tzarrada", + "you": "tue" + }, + "chromeExtensionBanner": { + "installExtensionText": "Installa s'estensione de integratzione cun Google Calendar e Office 365", + "buttonText": "Installa s'estensione de Google", + "dontShowAgain": "Non ddu torres a ammustrare" + }, + "connectingOverlay": { + "joiningRoom": "Connetende a sa riunione..." + }, + "connection": { + "ATTACHED": "Allegados", + "AUTHENTICATING": "Autenticatzione in cursu", + "AUTHFAIL": "Faddina in s'autenticatzione", + "CONNECTED": "Connessione istabilida", + "CONNECTING": "Connetende", + "CONNFAIL": "Faddina in sa connessione", + "DISCONNECTED": "Disconnètidu", + "DISCONNECTING": "Disconnetende", + "ERROR": "Faddina", + "FETCH_SESSION_ID": "Otenende id de sessione...", + "GET_SESSION_ID_ERROR": "Faddina in su ritzevimentu de s'ide de sessione: {{code}}", + "GOT_SESSION_ID": "Otenende id de sessione... Fatu", + "LOW_BANDWIDTH": "Vìdeu disativadu pro {{displayName}} pro istraviare àmpiu de banda" + }, + "connectionindicator": { + "address": "Indiritzu:", + "bandwidth": "Àmpiu de banda istimadu:", + "bitrate": "Velotzidade de bits:", + "bridgeCount": "Nùmeru de servidores: ", + "connectedTo": "Connessione cun:", + "e2e_rtt": "E2E RTT:", + "framerate": "Velotzidade de fotogrammas:", + "less": "Prus pagu informatziones", + "localaddress": "Indiritzu locale:", + "localaddress_plural": "Indiritzos locales:", + "localport": "Portu locale:", + "localport_plural": "Portos locales:", + "more": "Àteras informatziones", + "packetloss": "Pèrdida de pachetes:", + "quality": { + "good": "Bonu", + "inactive": "Inativa", + "lost": "Pèrdida", + "nonoptimal": "No òtima", + "poor": "Pòbera" + }, + "remoteaddress": "Indiritzu remotu:", + "remoteaddress_plural": "Indiritzos remotos:", + "remoteport": "Portu remotu:", + "remoteport_plural": "Portos remotos:", + "resolution": "Risolutzione:", + "status": "Connessione:", + "transport": "Trasportu:", + "transport_plural": "Trasportos:" + }, + "dateUtils": { + "earlier": "Prus antigu", + "today": "Oe", + "yesterday": "Eris" + }, + "deepLinking": { + "appNotInstalled": "Tenes bisòngiu de s'aplicatzione mòbile {{app}} pro aderire a custa tzarrada dae su telèfonu.", + "description": "No est sutzèdidu nudda? Amus chircadu de aviare sa riunione tua in s'aplicatzione de iscrivania {{app}}. Torra·bi a proare o avia·la dae s'aplicatzione web {{app}}.", + "descriptionWithoutWeb": "No est sutzèdidu nudda? Amus chircadu de aviare sa riunione tua in s'aplicatzione de iscrivania {{app}}.", + "downloadApp": "Iscàrriga s'aplicatzione", + "launchWebButton": "Avia in sa web", + "openApp": "Sighi in s'aplicatzione", + "title": "Aviende sa reunione in {{app}}...", + "tryAgainButton": "Torra·bi a proare in s'aplicatzione de iscrivania" + }, + "defaultLink": "p. es. {{url}}", + "defaultNickname": "es. Rosa Pink", + "deviceError": { + "cameraError": "Impossìbile atzèdere a sa càmera", + "cameraPermission": "Faddina in is permissos pro sa càmera", + "microphoneError": "Impossìbile atzèdere a su micròfonu", + "microphonePermission": "Faddina in is permissos pro su micròfonu" + }, + "deviceSelection": { + "noPermission": "No as cuntzèdidu permissos", + "previewUnavailable": "Sa pre-visualizatzione no est a disponimentu", + "selectADevice": "Sèbera unu dispositivu", + "testAudio": "Riprodue unu sonu de proa" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Trasmissione in direta" + }, + "allow": "Permite", + "alreadySharedVideoMsg": "Un'àteru partetzipante est giai cumpartende unu vìdeu. Custa cunferèntzia permitit de cumpartzire isceti unu vìdeu in contemporànea.", + "alreadySharedVideoTitle": "Isceti unu vìdeu cumpartzidu in contemporànea", + "applicationWindow": "Ventana de s'aplicatzione", + "Back": "In segus", + "cameraConstraintFailedError": "La càmera no satisfà algun dels requeriments.", + "cameraNotFoundError": "Càmera no agatada.", + "cameraNotSendingData": "Impossìbile atzèdere a sa càmera tua. Controlla si un'àtera aplicatzione est impreende custu dispositivu, sèbera un'àteru dispositivu dae su menù de cunfiguratziones o torra a carrigare s'aplicatzione.", + "cameraNotSendingDataTitle": "Non si podet atzèdere a sa càmera", + "cameraPermissionDeniedError": "No as donadu permissos pro impreare sa càmera. Podes intrare in sa cunferèntzia su pròpiu, però s'àtera gente non ti at a bìdere. Imprea su butone de sa càmera in sa barra de indiritzos pro acontzare custu problema.", + "cameraUnknownError": "Non si podet impreare sa càmera (resone disconnota).", + "cameraUnsupportedResolutionError": "Sa càmera no est cumpatìbile cun sa risolutzione de vìdeu rechèdida.", + "Cancel": "Annulla", + "close": "Serra", + "conferenceDisconnectMsg": "Controlla sa cunfiguratzione de rete. Torrende a connètere in {{seconds}} segundos...", + "conferenceDisconnectTitle": "Mutida disconnètida.", + "conferenceReloadMsg": "Semus chirchende de acontzare custu problema. Torrende a connètere in {{seconds}} segundos...", + "conferenceReloadTitle": "B'est istada una faddina.", + "confirm": "Cunfirma", + "confirmNo": "Nono", + "confirmYes": "Eja", + "connectError": "B'àt àpidu una faddina e non podimus connètere cun sa cunferèntzia.", + "connectErrorWithMsg": "B'àt àpidu una faddina e non podimus connètere cun sa cunferèntzia: {{msg}}", + "connecting": "Connetende", + "contactSupport": "Cuntatu s'agiudu", + "copy": "Còpia", + "dismiss": "Iscarta", + "displayNameRequired": "Salude! Comente ti tzèrrias?", + "done": "Fatu", + "enterDisplayName": "Inserta su nòmine inoghe", + "error": "Faddina", + "externalInstallationMsg": "Installa s'estensione nostra pro sa cumpartzidura de iscrivania.", + "externalInstallationTitle": "Estensione rechèdida", + "goToStore": "Bae a sa butega", + "gracefulShutdown": "Su servìtziu nostru est in mantenimentu. Torra·bi a proare a pustis.", + "IamHost": "So mere", + "incorrectRoomLockPassword": "Sa crae no est curreta", + "incorrectPassword": "Su nòmine o sa crae no sunt curretos", + "inlineInstallationMsg": "Installa s'estensione nostra pro sa cumpartzidura de iscrivania.", + "inlineInstallExtension": "Installa immoe", + "internalError": "B'est istada una faddina: {{error}}", + "internalErrorTitle": "Faddina interna", + "kickMessage": "Podes cuntatare {{participantDisplayName}} pro àteras informatziones.", + "kickParticipantButton": "Boga", + "kickParticipantDialog": "Seguru chi boles bogare custa persone?", + "kickParticipantTitle": "Cheres bogare custa persone?", + "kickTitle": "{{participantDisplayName}} t'at bogadu de sa riunione", + "liveStreaming": "Trasmissione in direta", + "liveStreamingDisabledForGuestTooltip": "Is persones invitadas non podent aviare una trasmissione in direta.", + "liveStreamingDisabledTooltip": "Faddina in s'aviu de sa trasmissione in direta.", + "lockMessage": "Impossìbile blocare sa cunferèntzia.", + "lockRoom": "Agiunghe riunione $t(lockRoomPasswordUppercase)", + "lockTitle": "Faddina in su blocu", + "logoutQuestion": "Seguru chi boles essire e firmare sa cunferèntzia?", + "logoutTitle": "Essi", + "maxUsersLimitReached": "Lìmite de partetzipantes cròmpidu. Sa cunferèntzia est prena. Cuntata su mere de sa riunione o torra·bi a proare.", + "maxUsersLimitReachedTitle": "Lìmite de partetzipantes cròmpidu", + "micConstraintFailedError": "Su micròfonu no at soddisfatu is rechestas.", + "micNotFoundError": "Micròfonu no agatadu.", + "micNotSendingData": "Bae a sa cunfiguratzione de s'elaboradore tuo pro ativare su micròfonu tuo e acontzare su livellu", + "micNotSendingDataTitle": "Su micròfonu tuo est in silèntziu pro more de is cunfiguratziones de su sistema tuo", + "micPermissionDeniedError": "No as donadu permissos pro impreare su micròfonu. Podes intrare in sa cunferèntzia su pròpiu, però s'àtera gente non ti at a intèndere. Imprea su butone de sa càmera in sa barra de indiritzos pro acontzare custu problema.", + "micUnknownError": "Non si podet impreare su micròfonu (resone disconnota).", + "muteEveryoneElseDialog": "Una borta chi as postu calicunu a sa muda, no as a pòdere torrare a aviare s'àudio suo, però isse ddu at a pòdere fàghere in cale si siat momentu.", + "muteEveryoneElseTitle": "Boles pònnere totus a sa muda francu {{whom}}?", + "muteEveryoneDialog": "Seguru chi boles pònnere totus a sa muda? No as a pòdere torrare a ativare s'àudio issoro, però is utentes ddu ant a pòdere ativare in cale si siat momentu.", + "muteEveryoneTitle": "Boles pònnere totus a sa muda?", + "muteEveryoneSelf": "tue", + "muteEveryoneStartMuted": "Dae immoe, is tzarradas cumintzant cun is utentes a sa muda", + "muteParticipantBody": "No as a pòdere torrare a ativare s'àudio issoro, però is utentes ddu ant a pòdere ativare in cale si siat momentu.", + "muteParticipantButton": "A sa muda", + "muteParticipantDialog": "Seguru chi boles pònnere custa persone a sa muda? No as a pòdere torrare a ativare s'àudio issoro, però is utentes ddu ant a pòdere ativare in cale si siat momentu.", + "muteParticipantTitle": "Boles pònnere custa persone a sa muda?", + "Ok": "AB", + "passwordLabel": "Unu partetzipante at blocadu sa riunione. Inserta sa $t(lockRoomPassword) pro intrare.", + "passwordNotSupported": "No est possìbile istabilire una $t(lockRoomPassword).", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) non suportadu", + "passwordRequired": "$t(lockRoomPasswordUppercase) rechèdida", + "popupError": "Su navigadore tuo est blochende is ventanas emergentes de custu situ. Ativa is ventanas emergentes dae sa cunfiguratzione de seguresa de su navigadore e torra·bi a proare.", + "popupErrorTitle": "Finestres emergents blocades", + "recording": "Registrende", + "recordingDisabledForGuestTooltip": "Is persones invitadas non podent registrare.", + "recordingDisabledTooltip": "S'aviu de registratziones est istadu disativadu.", + "rejoinNow": "Torra a intrare", + "remoteControlAllowedMessage": "{{user}} at atzetadu sa rechesta tua de controllu remotu.", + "remoteControlDeniedMessage": "{{user}} at refudadu sa rechesta tua de controllu remotu.", + "remoteControlErrorMessage": "Faddina in sa rechesta de permissos pro su controllu remotu de {{user}}.", + "remoteControlRequestMessage": "Permitis chi {{user}} controllet in remotu s'elaboradore tuo?", + "remoteControlShareScreenWarning": "Si incarcas \"Permite\" as a cumpartzire s'ischermu tuo.", + "remoteControlStopMessage": "Sessione de controllu remotu acabada.", + "remoteControlTitle": "Controllu remotu de elaboradore", + "Remove": "Boga", + "removePassword": "Boga $t(lockRoomPassword)", + "removeSharedVideoMsg": "Seguru chi boles bogare su vìdeu chi as cumpartzidu?", + "removeSharedVideoTitle": "Boga vìdeu cumpartzidu", + "reservationError": "Faddina de riserva de sistema", + "reservationErrorMsg": "Còdighe de faddina: {{code}}, messàgiu: {{msg}}", + "retry": "Torra·bi a proare", + "screenSharingFailedToInstall": "Faddina in s'installatzione de s'estensione de cumpartzidura de ischermu.", + "screenSharingFailedToInstallTitle": "Faddina installatzione estensione cumpartzidura ischermu", + "screenSharingFirefoxPermissionDeniedError": "B'àt àpidu una faddina proende de cumpartzire s'ischermu tuo. Verìfica chi si as donadu permissos pro ddu fàghere. ", + "screenSharingFirefoxPermissionDeniedTitle": "No amus pòdidu aviare sa cumpartzidura de ischermu.", + "screenSharingPermissionDeniedError": "B'àt àpidu una faddina cun is permissos de s'estensione pro sa cumpartzidura de ischermu. Torra a carrigare e torra·bi a proare.", + "sendPrivateMessage": "As retzidu de reghente unu messàgiu privadu. Boles rispòndere a custu messàgiu in privadu, o boles imbiare su messàgiu a su grupu?", + "sendPrivateMessageCancel": "Imbia a su grupu", + "sendPrivateMessageOk": "Imbia in privadu", + "sendPrivateMessageTitle": "Boles imbiare custu messàgiu in privadu?", + "serviceUnavailable": "Su servìtziu no est a disponimentu", + "sessTerminated": "Mutida acabada", + "Share": "Cumpartzi", + "shareVideoLinkError": "Fruni unu ligàmene de youtube curretu.", + "shareVideoTitle": "Cumpartzi unu vìdeu", + "shareYourScreen": "Cumpartzi s'ischermu", + "shareYourScreenDisabled": "Cumpartzidura de ischermu disativada.", + "shareYourScreenDisabledForGuest": "Is persones invitadas non podent cumpartzire s'ischermu.", + "startLiveStreaming": "Avia sa trasmissione in direta", + "startRecording": "Avia sa registratzione", + "startRemoteControlErrorMessage": "Faddina aviende sa sessione de controllu remotu.", + "stopLiveStreaming": "Firma sa trasmissione in direta", + "stopRecording": "Firma sa registratzione", + "stopRecordingWarning": "Seguru chi boles firmare sa registratzione?", + "stopStreamingWarning": "Seguru chi boles firmare sa trasmissione in direta?", + "streamKey": "Crae de sa trasmissione in direta", + "Submit": "Imbia", + "thankYou": "Gràtzias de àere impreadu {{appName}}.", + "token": "còdighe", + "tokenAuthFailed": "No tenes permissu pro intrare in custa mutida.", + "tokenAuthFailedTitle": "Faddina in s'autenticatzione", + "transcribing": "Trascritzione", + "unlockRoom": "Boga riunione $t(lockRoomPassword)", + "userPassword": "crae de utente", + "WaitForHostMsg": "Sa cunferèntzia <b>{{room}}</b> no est cumintzada. Si ses mere de custa cunferèntzia, autèntica·ti. Si nono, iseta chi arribet.", + "WaitForHostMsgWOk": "Sa cunferèntzia <b>{{room}}</b> no est cumintzada. Si ses mere, incarca AB pro ti autenticare. Si nono, iseta chi arribet.", + "WaitingForHost": "Isetende mere...", + "Yes": "Eja", + "yourEntireScreen": "S'ischermu intreu", + "screenSharingAudio": "Cumpartzi s'àudio" + }, + "dialOut": { + "statusMessage": "est immoe {{status}}" + }, + "documentSharing": { + "title": "Documentu cumpartzidu" + }, + "feedback": { + "average": "Mèdiu", + "bad": "Malu", + "detailsLabel": "Nara·si de prus.", + "good": "Bonu", + "rateExperience": "Vota s'esperièntzia tua in custa riunione", + "veryBad": "Mala meda", + "veryGood": "Bona meda" + }, + "incomingCall": { + "answer": "Risponde", + "audioCallTitle": "Mutida in intrada", + "decline": "Iscarta", + "productLabel": "dae Jitsi Meet", + "videoCallTitle": "Mutida de vìdeu in intrada" + }, + "info": { + "accessibilityLabel": "Ammustra informatziones", + "addPassword": "Agiunghe $t(lockRoomPassword)", + "cancelPassword": "Annulla $t(lockRoomPassword)", + "conferenceURL": "Ligàmene:", + "country": "Paisu", + "dialANumber": "Pro intrare in sa riunione, cumpone unu de custos nùmeros e inserta su PIN.", + "dialInConferenceID": "PIN:", + "dialInNotSupported": "Sa partetzipatzione telefònica ebbia no est suportada a oe.", + "dialInNumber": "Cumpone:", + "dialInSummaryError": "Faddina in su ritzevimentu de is nùmeros de telèfonu. Torra·bi a proare a pustis.", + "dialInTollFree": "Sena pedàgios", + "genericError": "Faddina.", + "inviteLiveStream": "Pro bìdere sa trasmissione in direta de custa riunione, incarca custu ligàmene: {{url}}", + "invitePhone": "Si boles fàghere una connessione telefònica, toca custu: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Ses chirchende unu nùmeru diferente?\nControlla is nùmeros de telèfonu de sa riunione: {{url}}\n\n\nSi ses giai mutende tràmite unu telèfonu de sa riunione, intra sena ti connètere a s'àudio: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Tenes un'invitu pro intrare in una riunione.", + "inviteURLFirstPartPersonal": "{{name}} t'at invitadu a sa riunione.\n", + "inviteURLSecondPart": "\nIntra in sa riunione:\n{{url}}\n", + "liveStreamURL": "Trasmissione in direta:", + "moreNumbers": "Àteros nùmeros", + "noNumbers": "Perunu nùmeru de mutire.", + "noPassword": "Perunu", + "noRoom": "No as ispetzificadu peruna sala de mutire.", + "numbers": "Nùmeros de mutire", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Cumpartzi", + "tooltip": "Imbia su ligàmene e is nùmeros de telèfonu de custa riunione", + "label": "Informatziones de sa riunione" + }, + "inviteDialog": { + "alertText": "Faddina in s'invitu de àtera gente.", + "header": "Invita", + "searchCallOnlyPlaceholder": "Inserta·nche unu nùmeru de telèfonu", + "searchPeopleOnlyPlaceholder": "Chirca gente", + "searchPlaceholder": "Partetzipante o nùmeru de telèfonu", + "send": "Imbia" + }, + "inlineDialogFailure": { + "msg": "Bi amus postu unu pagu.", + "retry": "Torra·bi a proare", + "support": "Agiudu", + "supportMsg": "Si custu sighit a sutzèdere, nara·si·ddu a" + }, + "keyboardShortcuts": { + "focusLocal": "Ammustra su vìdeu tuo", + "focusRemote": "Ammustra su vìdeu de s'àtera persone", + "fullScreen": "Visualiza in mannària prena o essi", + "keyboardShortcuts": "Incurtzaduras de tecladu", + "localRecording": "Ammustra o cua controllos de registratzione locale", + "mute": "Pone su micròfonu a sa muda o torra·ddu a ativare", + "pushToTalk": "Incarca pro chistionare", + "raiseHand": "Àrtzia o abassa sa manu", + "showSpeakerStats": "Ammustra istatìsticas de partetzipantes", + "toggleChat": "Aberi o serra sa tzarrada", + "toggleFilmstrip": "Ammustra o cua miniaturas de vìdeu", + "toggleScreensharing": "Cuncàmbia intre càmera e cumpartzidura de ischermu", + "toggleShortcuts": "Ammustra o cua incurtzaduras de tecladu", + "videoMute": "Avia o firma sa càmera tua", + "videoQuality": "Gesti sa calidade de sa mutida" + }, + "liveStreaming": { + "busy": "Semus chirchende de liberare resursas de trasmissione. Torra·bi a proare dae immoe a carchi minutu.", + "busyTitle": "Is trasmitentes sunt ocupados immoe", + "changeSignIn": "Càmbia contos.", + "choose": "Sèbera una trasmissione in direta", + "chooseCTA": "Sèbera un'optzione pro sa trasmissione. Autenticatzione che a {{email}}.", + "enterStreamKey": "Inserta inoghe sa crae tua pro sa trasmissione in direta de YouTube.", + "error": "Faddina in sa trasmissione in direta. Torra·bi a proare.", + "errorAPI": "Faddina in s'atzessu a is trasmissiones tuas de Youtube. Torra·bi a proare.", + "errorLiveStreamNotEnabled": "Sa trasmissione in direta no est ativa in {{email}}. Ativa sa trasmissione in direta o autèntica·ti in unu contu chi tèngiat ativa sa trasmissione in direta.", + "expandedOff": "Trasmissione in direta firmada", + "expandedOn": "Custa riunione est trasmìtida in direta in YouTube.", + "expandedPending": "Trasmissione in direta aviada...", + "failedToStart": "Faddina in s'aviu de sa trasmissione in direta", + "getStreamKeyManually": "No amus pòdidu retzire peruna trasmissione in direta. Chirca de otènnere sa crae de YouTube tua pro is trasmissiones in idreta.", + "invalidStreamKey": "Sa crae pro is trasmissiones in direta podet èssere iscurreta.", + "off": "Trasmissione in direta firmada", + "offBy": "{{name}} at firmadu sa trasmissione in direta", + "on": "Trasmissione in direta", + "onBy": "{{name}} at aviadu sa trasmissione in direta", + "pending": "Aviende sa trasmissione in direta...", + "serviceName": "Servìtziu de trasmissione in direta", + "signedInAs": "Autenticatzione cun:", + "signIn": "Autèntica·ti cun Google", + "signInCTA": "Autèntica·ti o inserta sa crae tua pro sa trasmissione in direta de YouTube.", + "signOut": "Essi", + "start": "Avia sa trasmissione in direta", + "streamIdHelp": "It'est custu?", + "unavailableTitle": "Sa trasmissione in direta no est a disponimentu", + "googlePrivacyPolicy": "Polìtica de riservadesa de Google", + "youtubeTerms": "Cunditziones de servìtziu de YouTube" + }, + "localRecording": { + "clientState": { + "off": "Disativada", + "on": "Ativa", + "unknown": "Disconnota" + }, + "dialogTitle": "Controllos de registratzione locale", + "duration": "Durada", + "durationNA": "Non a disponimentu", + "encoding": "Codìfica", + "label": "Registr. locale", + "labelToolTip": "Sa registratzione in locale est funtzionende", + "localRecording": "Registratzione in locale", + "me": "Deo", + "messages": { + "engaged": "Sa registratzione in locale est funtzionende.", + "finished": "Acabada sa registratzione de sessione {{token}}. Imbia s'archìviu registradu a sa persone chi mòderat.", + "finishedModerator": "Acabada sa registratzione de sessione {{token}}. Registratzione locale sarvada. Dimanda a su restu de su grupu de imbiare is registratziones.", + "notModerator": "No ses moderadore. Non podes aviare o firmare registratziones in locale." + }, + "moderator": "Moderadore", + "no": "Nono", + "participant": "Partetzipante", + "participantStats": "Istatìsticas de partetzipantes", + "sessionToken": "Còdighe de sessione", + "start": "Avia sa registratzione", + "stop": "Firma sa registratzione", + "yes": "Eja" + }, + "lockRoomPassword": "crae", + "lockRoomPasswordUppercase": "Crae", + "me": "deo", + "notify": { + "connectedOneMember": "{{name}} at aderidu a sa riunione", + "connectedThreePlusMembers": "{{name}} è ateras {{count}} persones ant aderidu a sa riunione", + "connectedTwoMembers": "{{first}} e {{second}} ant aderidu a sa riunione", + "disconnected": "disconnètidu", + "focus": "Focus de sa cunferèntzia", + "focusFail": "{{component}} no est a disponimentu - torra·bi a proare in {{ms}} seg", + "grantedTo": "Permissos pro sa moderatzione cuntzèdidos a {{to}}.", + "invitedOneMember": "Invitu imbiadu a {{name}}", + "invitedThreePlusMembers": "Invitu imbiadu a {{name}} e àteras {{count}} persones", + "invitedTwoMembers": "Invitu imbiadu a {{first}} e {{second}}", + "kickParticipant": "{{kicker}} at bogadu a {{kicked}}", + "me": "Deo", + "moderator": "As donadu permissos pro sa moderatzione.", + "muted": "As cumintzadu una tzarrada a sa muda.", + "mutedTitle": "Ses a sa muda.", + "mutedRemotelyTitle": "{{participantDisplayName}} t'at postu a sa muda.", + "mutedRemotelyDescription": "Podes torrare a aviare s'àudio cando depas chistionare. Torra a sa muda cando as acabadu pro evitare remore in sa riunione.", + "passwordRemovedRemotely": "Un'utente at bogadu $t(lockRoomPasswordUppercase)", + "passwordSetRemotely": "Un'utente at cunfiguradu $t(lockRoomPasswordUppercase)", + "raisedHand": "{{name}} bolet chistionare.", + "somebody": "Calicunu", + "startSilentTitle": "Ses intradu sena àudio.", + "startSilentDescription": "Torra a intrare pro ativare s'àudio", + "suboptimalBrowserWarning": "Timimus chi s'esperièntzia tua de custa riunione no at a èssere bona meda. Semus chirchende de megiorare custu, però in su mentras proa de impreare unu de is <a href='static/recommendedBrowsers.html' target='_blank'>navigadores cumpatìbiles</a>.", + "suboptimalExperienceTitle": "Avisu subra de su navigadore", + "unmute": "Ativa su sonu", + "newDeviceCameraTitle": "Càmera noa rilevada", + "newDeviceAudioTitle": "Dispositivu de àudio nou rilevadu", + "newDeviceAction": "Imprea" + }, + "passwordSetRemotely": "cunfiguradu dae un'àtera persone", + "passwordDigitsOnly": "Finas a {{number}} tzifras", + "poweredby": "de", + "presenceStatus": { + "busy": "No a disponimentu", + "calling": "Mutende...", + "connected": "Connessione istabilida", + "connecting": "Connetende...", + "connecting2": "Connetende*...", + "disconnected": "Disconnètidu", + "expired": "Iscadidu", + "ignored": "Ignoradu", + "initializingCall": "Aviende sa mutida...", + "invited": "Invitadu", + "rejected": "Refudadu", + "ringing": "Sonende..." + }, + "profile": { + "setDisplayNameLabel": "Cunfigura su nòmine visìbile tuo", + "setEmailInput": "Inserta posta eletrònica", + "setEmailLabel": "Cunfigura indiritzu eletrònicu de Gravatar", + "title": "Profilu" + }, + "raisedHand": "Bògio chistionare", + "recording": { + "authDropboxText": "Càrriga a Dropbox", + "availableSpace": "Ispàtziu a disponimentu: {{spaceLeft}} MB (prus o mancu {{duration}} minutos de registratzione)", + "beta": "BETA", + "busy": "Semus traballende pro liberare resursas de registratzione. Torra·bi a proare dae immoe a carchi minutu.", + "busyTitle": "Totu is registradores sunt ocupados", + "error": "Faddina in sa registratzione. Torra·bi a proare.", + "expandedOff": "Registratzione firmada", + "expandedOn": "Registrende sa riunione.", + "expandedPending": "Aviende sa registratzione...", + "failedToStart": "Faddina in s'aviu de sa registratzione", + "fileSharingdescription": "Cumpartzi sa registratzione cun is partetzipantes de sa riunione", + "live": "IN DIRETA", + "loggedIn": "Autenticatzione: {{userName}}", + "off": "Registratzione firmada", + "offBy": "{{name}} at firmadu sa registratzione", + "on": "Registrende", + "onBy": "{{name}} at aviadu sa registratzione", + "pending": "Preparende pro registrare sa riunione...", + "rec": "REG", + "serviceDescription": "Sa registratzione at a èssere sarvada dae su servìtziu de registratzione", + "serviceName": "Servìtziu de registratzione", + "signIn": "Identìfica·ti", + "signOut": "Essi", + "unavailable": "{{serviceName}} no est a disponimentu. Semus traballende pro acontzare su problema. Torra·bi a proare a pustis.", + "unavailableTitle": "Sa registratzione no est a disponimentu" + }, + "sectionList": { + "pullToRefresh": "Ispinghe pro atualizare" + }, + "settings": { + "calendar": { + "about": "S'integratzione cun su calendàriu {{appName}} est impreada pro atzèdere in manera segura a su calendàriu tuo pro chi potzat lèghere is eventos imbenientes.", + "disconnect": "Disconnete", + "microsoftSignIn": "Autèntica·ti cun Microsoft", + "signedIn": "Ses atzedende a is eventos de calendàriu pro {{email}}. Incarca su butone Disconnete in bàsciu pro firmare s'atzessu a eventos de calendàriu.", + "title": "Calendàriu" + }, + "devices": "Dispositivos", + "followMe": "Totus mi sighint", + "language": "Limba", + "loggedIn": "Autenticatzione: {{name}}", + "moderator": "Moderadore", + "more": "Àteru", + "name": "Nòmine", + "noDevice": "Perunu", + "selectAudioOutput": "Essida de àudio", + "selectCamera": "Càmera", + "selectMic": "Micròfonu", + "startAudioMuted": "Totus cumintzant a sa muda", + "startVideoMuted": "Totus cumintzant a sa cua", + "title": "Cunfiguratzione", + "speakers": "Altoparlantes", + "microphones": "Micròfonos" + }, + "settingsView": { + "advanced": "Avantzadas", + "alertOk": "AB", + "alertTitle": "Atentzione", + "alertURLText": "Custu URL no est vàlidu", + "buildInfoSection": "Informatzione de sa versione", + "conferenceSection": "Cunferèntzia", + "disableCallIntegration": "Disativa s'integratzione de mutidas nativas", + "disableP2P": "Disativa sa modalidade a nodu terminale (p2p)", + "displayName": "Nòmine visìbile", + "email": "Indiritzu eletrònicu", + "header": "Cunfiguratzione", + "profileSection": "Profilu", + "serverURL": "URL de su servidore", + "showAdvanced": "Ammustra cunfiguratziones avantzadas", + "startWithAudioMuted": "Cumintza cun s'àudio a sa muda", + "startWithVideoMuted": "Cumintza cun su vìdeu disativadu", + "version": "Versione" + }, + "share": { + "dialInfoText": "\n\n=====\n\nBoles isceti ascurtare sa cunferèntzia dae su telèfonu?\n\n{{defaultDialInNumber}}Incarca custu ligàmene pro bìdere is nùmeros de telèfonu de custa riunione\n{{dialInfoPageUrl}}\"", + "mainText": "Incarca custu ligàmene pro intrare a sa riunione:\n{{roomUrl}}" + }, + "speaker": "Altoparlante", + "speakerStats": { + "hours": "{{count}} h", + "minutes": "{{count}} min", + "name": "Nòmine", + "seconds": "{{count}} seg", + "speakerStats": "Istatìsticas de partetzipante", + "speakerTime": "Tempus de partetzipante" + }, + "startupoverlay": { + "title": "{{app}} tenet bisòngiu de impreare sa càmera e su micròfonu tuos.", + "policyText": " " + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Torra a intrare", + "text": "Incarca su butone <i>Torra a intrare</i> pro torrare a connètere.", + "title": "S'elaboradore est andadu a riposu e custu at firmadu sa mutida de vìdeu." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Càmbia àudio isceti", + "audioRoute": "Sèbera su dispositivu de àudio", + "callQuality": "Gesti sa calidade de su vìdeu", + "cc": "Càmbia s'istadu de is sutatìtulos", + "chat": "Càmbia ventana de tzarrada", + "document": "Càmbia documentu cumpartzidu", + "download": "Iscàrriga is aplicatziones nostras", + "feedback": "Lassa cummentos", + "fullScreen": "Ativa o disativa ischermu in mannària prena", + "hangup": "Lassa sa mutida", + "help": "Agiudu", + "invite": "Invita gente", + "kick": "Boga partetzipante", + "localRecording": "Ativa o disativa is controllos de registratzione in locale", + "lockRoom": "Ativa o disativa crae de riunione", + "moreActions": "Càmbia su menù de atziones additzionales", + "moreActionsMenu": "Menù de atziones additzionales", + "moreOptions": "Ammustra àteras optziones", + "mute": "Ativa o disativa su silèntziu de s'àudio", + "muteEveryone": "Pone totus a sa muda", + "pip": "Ativa o disativa sa modalidade immàgine in immàgine", + "privateMessage": "Imbia messàgiu de testu privadu", + "profile": "Modìfica su profilu", + "raiseHand": "Àrtzia o abassa sa manu", + "recording": "Ativa o disativa sa registratzione", + "remoteMute": "Pone partetzipante a sa muda", + "Settings": "Càmbia sa cunfiguratzione", + "sharedvideo": "Ativa o disativa sa cumpartzidura de vìdeos de YouTube", + "shareRoom": "Invita una persone", + "shareYourScreen": "Ativa o disativa sa cumpartzidura de ischermu", + "shortcuts": "Ativa o disativa incurtzaduras", + "show": "Ammustra in s'iscena", + "speakerStats": "Càmbia istatìsticas de partetzipante", + "tileView": "Càmbia a visualizatzione in mosàicu", + "toggleCamera": "Càmbia càmera", + "videomute": "Ativa o disativa su vìdeu", + "videoblur": "Ativa o disativa isfocadu" + }, + "addPeople": "Agiunghe gente a sa mutida", + "audioOnlyOff": "Disativa modalidade de àmpiu de banda bàsciu", + "audioOnlyOn": "Ativa modalidade de àmpiu de banda bàsciu", + "audioRoute": "Sèbera su dispositivu de àudio", + "authenticate": "Autentica·ti", + "callQuality": "Gesti sa calidade de su vìdeu", + "chat": "Aberi o serra sa tzarrada", + "closeChat": "Serra sa tzarrada", + "documentClose": "Serra su documentu cumpartzidu", + "documentOpen": "Aberi su documentu cumpartzidu", + "download": "Iscàrriga is aplicatziones nostras", + "enterFullScreen": "Ammustra in mannària prena", + "enterTileView": "Intra in visualizatzione in mosàicu", + "exitFullScreen": "Essi de ischermu in mannària prena", + "exitTileView": "Essi de sa visualizatzione in mosàicu", + "feedback": "Lassa cummentos", + "hangup": "Essi", + "help": "Agiudu", + "invite": "Invita gente", + "login": "Intra", + "logout": "Essi", + "lowerYourHand": "Abassa sa manu", + "moreActions": "Àteras atziones", + "moreOptions": "Àteras optziones", + "mute": "Ativa o disativa s'àudio", + "muteEveryone": "Pone totus a sa muda", + "noAudioSignalTitle": "Perunu sinnale dae su micròfonu tuo.", + "noAudioSignalDesc": "Si no dd'as postu a sa muda dae sa cunfiguratzione de sistema o dae su dispositivu, forsis depes cambiare dispositivu.", + "noAudioSignalDescSuggestion": "Si no dd'as postu a sa muda dae sa cunfiguratzione de sistema o dae su dispositivu, forsis depes cambiare a su dispositivu cussigiadu.", + "noAudioSignalDialInDesc": "Podes fintzas intrare cun una mutida:", + "noAudioSignalDialInLinkDesc": "Nùmeros de mutida", + "noisyAudioInputTitle": "Su micròfonu tuo faghet remore.", + "noisyAudioInputDesc": "Su micròfonu tuo faghet remore, forsis ti depes pònnere a sa muda o depes cambiare dispositivu.", + "openChat": "Aberi sa tzarrada", + "pip": "Intra in modalidade immàgine in immàgine", + "privateMessage": "Imbia messàgiu de testu privadu", + "profile": "Modìfica su profilu", + "raiseHand": "Àrtzia o abassa sa manu", + "raiseYourHand": "Àrtzia sa manu", + "Settings": "Cunfiguratzione", + "sharedvideo": "Cumpartzi unu vìdeu de YouTube", + "shareRoom": "Invita una persone", + "shortcuts": "Ammustra incurtzaduras", + "speakerStats": "Istatìsticas de partetzipante", + "startScreenSharing": "Avia sa cumpartzidura de s'ischermu", + "startSubtitles": "Avia sutatìtulos", + "stopScreenSharing": "Firma sa cumpartzidura de s'ischermu", + "stopSubtitles": "Firma sutatìtulos", + "stopSharedVideo": "Firma vìdeu de YouTube", + "talkWhileMutedPopup": "Ses chirchende de chistionare? Ses a sa muda.", + "tileViewToggle": "Càmbia a visualizatzione in mosàicu", + "toggleCamera": "Càmbia càmera", + "videomute": "Avia o firma sa càmera", + "startvideoblur": "Isfoca s'isfundu meu", + "stopvideoblur": "Disativa s'isfocadu de s'isfundu" + }, + "transcribing": { + "ccButtonTooltip": "Avia o firma sutatìtulos", + "error": "Faddina in sa trascritzione. Torra·bi a proare.", + "expandedLabel": "Trascritzione ativada", + "failedToStart": "Faddina in s'aviu de sa trascritzione", + "labelToolTip": "Trascriende sa riunione", + "off": "Trascritzione firmada", + "pending": "Preparende pro trascrìere sa riunione...", + "start": "Cumintza a ammustrare sutatìtulos", + "stop": "No ammustres prus sutatìtluos", + "tr": "TR" + }, + "userMedia": { + "androidGrantPermissions": "Sèbera <b><i>Permite</i></b> cando su navigadore ti dimandet permissos.", + "chromeGrantPermissions": "Sèbera <b><i>Permite</i></b> cando su navigadore ti dimandet permissos.", + "edgeGrantPermissions": "Sèbera <b><i>Eja</i></b> cando su navigadore ti dimandet permissos.", + "electronGrantPermissions": "Cuntzede permissos pro atzèdere a sa càmera e a su micròfonu tuos", + "firefoxGrantPermissions": "Sèbera <b><i>Cumpartzi dispositivos seletzionados</i></b> cando su navigadore ti dimandet permissos.", + "iexplorerGrantPermissions": "Sèbera <b><i>AB</i></b> cando su navigadore ti dimandet permissos.", + "nwjsGrantPermissions": "Cuntzede permissos pro atzèdere a sa càmera e a su micròfonu tuos", + "operaGrantPermissions": "Sèbera <b><i>Permite</i></b> cando su navigadore ti dimandet permissos.", + "react-nativeGrantPermissions": "Sèbera <b><i>Permite</i></b> cando su navigadore ti dimandet permissos.", + "safariGrantPermissions": "Sèbera <b><i>AB</i></b> cando su navigadore ti dimandet permissos." + }, + "videoSIPGW": { + "busy": "Semus traballende pro liberare resursas. Torra·bi a proare dae immoe a carchi minutu.", + "busyTitle": "Su servìtziu Room est ocupadu", + "errorAlreadyInvited": "Invitu a {{displayName}} giai imbiadu", + "errorInvite": "Sa connessione no est istada istabilida. Torra·bi a proare a pustis.", + "errorInviteFailed": "Semus traballende pro acontzare su problema. Torra·bi a proare a pustis.", + "errorInviteFailedTitle": "Faddina in s'invitu de {{displayName}}", + "errorInviteTitle": "Faddina in s'aposentu de invitos", + "pending": "Invitu a {{displayName}} imbiadu" + }, + "videoStatus": { + "audioOnly": "AUD", + "audioOnlyExpanded": "Ses in modalidade de àmpiu de banda bàsciu. In custa modalidade isceti su ritzevimentu de àudio e de cumpartzidura de ischermu sunt a disponimentu.", + "callQuality": "Calidade de su vìdeu", + "hd": "HD", + "hdTooltip": "Vìdeu in arta definitzione (HD)", + "highDefinition": "Arta definitzione (HD)", + "labelTooiltipNoVideo": "Sena vìdeu", + "labelTooltipAudioOnly": "Modalidade de àmpiu de banda bàsciu ativada", + "ld": "LD", + "ldTooltip": "Vìdeu in definitzione bàscia (LD)", + "lowDefinition": "Definitzione bàscia (LD)", + "onlyAudioAvailable": "Isceti àudio a disponimentu", + "onlyAudioSupported": "Custu navigadore est isceti cumpatìbile cun s'àudio.", + "p2pEnabled": "Rete a nodu terminale (p2p) ativada", + "p2pVideoQualityDescription": "In sa modalidade a nodu terminale (p2p), sa calidade de vìdeu retzida podet èssere cambiada intre arta calidade e isceti àudio. Depes essire de sa modalidade a nodu terminale pro pòdere cunfigurare àteras optziones.", + "recHighDefinitionOnly": "S'arta definitzione (HD) at a èssere preferida.", + "sd": "SD", + "sdTooltip": "Vìdeu in definitzione istandard (SD)", + "standardDefinition": "Definitzione istàndard (SD)" + }, + "videothumbnail": { + "domute": "A sa muda", + "domuteOthers": "Pone totus a sa muda", + "flip": "Fùrria", + "kick": "Boga", + "moderator": "Moderadore", + "mute": "Partetzipante a sa muda", + "muted": "A sa muda", + "remoteControl": "Avia o firma su controllu remotu", + "show": "Ammustra in s'iscena", + "videomute": "Custa persone at firmadu sa càmera" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Toca pro intrare", + "roomname": "Inserta su nòmine de s'aposentu" + }, + "appDescription": "Bae, tzarrada de vìdeu cun totu s'iscuadra. Difatis, podes invitare totu sa gente chi connosches. {{app}} est un'aplicatzione de vìdeu-cunferèntzia de còdighe abertu 100% cun critografia cumpleta chi podes impreare cada die, totu sa die, de badas — sena perunu contu creadu.", + "audioVideoSwitch": { + "audio": "Boghe", + "video": "Vìdeu" + }, + "calendar": "Calendàriu", + "connectCalendarButton": "Connete a su calendàriu tuo", + "connectCalendarText": "Connete a su calendàriu tuo pro bìdere totu is riuniones tuas in {{app}}. In prus, agiunghe riuniones de {{provider}} a su calendàriu tuo e avia·ddas cun unu clic.", + "enterRoomTitle": "Cumintza una riunione noa", + "roomNameAllowedChars": "Su nòmine de sa riunione non podet inclùdere custos caràteres: ?, &, :, ', \", %, #.", + "go": "BAE", + "goSmall": "BAE", + "join": "CREA / INTRA", + "info": "Informatziones", + "privacy": "Riservadesa", + "recentList": "Reghentes", + "recentListDelete": "Cantzella", + "recentListEmpty": "Sa lista de reghentes est bòida. Tzarra cun s'iscuadra tua e as a agatare totu is riuniones reghentes tuas inoghe.", + "reducedUIText": "Ti donamus su benebènnidu a {{app}}!", + "roomname": "Inserta su nòmine de s'aposentu", + "roomnameHint": "Inserta su nòmine o s'URL de s'aposentu a su chi boles intrare. Ti podes imbentare unu nòmine, bastat chi ddu fatzas ischire a sa gente chi ses addobiende pro chi ddu potzant insertare su pròpiu.", + "sendFeedback": "Imbia cummentos", + "terms": "Cunditziones", + "title": "Vìdeu-cunferèntzia segura, prena de funtzionalidades, lìbera e de badas", + "getHelp": "Agiudu" + }, + "lonelyMeetingExperience": { + "button": "Invita gente", + "youAreAlone": "Ses a sa sola in custa riunione" + }, + "helpView": { + "header": "Tzentru de agiudu" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sk.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sk.json new file mode 100644 index 000000000..1a53771dd --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sk.json @@ -0,0 +1,773 @@ +{ + "addPeople": { + "add": "Pozvať", + "countryNotSupported": "Zatiaľ nepodporujeme túto krajinu.", + "countryReminder": "Medzinárodný hovor? Prosím skontrolujte, či telefónne číslo začína smerovým číslo krajiny.", + "disabled": "Nemôžete pozvať ďalších účastníkov.", + "failedToAdd": "Nepodarilo sa pridať účastníka.", + "footerText": "Odchádzajúce hovory sú zablokované.", + "loading": "Hľadanie účastníkov a telefónnych čísiel", + "loadingNumber": "Kontrola telefónneho čísla", + "loadingPeople": "Hľadanie účastníkov na pozvanie", + "noResults": "Žiadne výsledky hľadania", + "noValidNumbers": "Prosím zadajte telefónne číslo", + "searchNumbers": "Zadajte telefónne čísla", + "searchPeople": "Hľadanie účastníkov", + "searchPeopleAndNumbers": "Hľadanie účastníkov alebo pridávanie telefónny čísel", + "telephone": "Telefón: {{number}}", + "title": "Pozvať účastníkov do tejto konferencie" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Sluchátka", + "phone": "Telefón", + "speaker": "Rečník", + "none": "Žiadne zvukové zariadenia" + }, + "audioOnly": { + "audioOnly": "Iba zvuk" + }, + "calendarSync": { + "addMeetingURL": "Pridať odkaz na stretnutie", + "confirmAddLink": "Chcete pridal Jitsi odkaz do tejto udalosti?", + "error": { + "appConfiguration": "Integrácia s kalendárom nie je správne nastavená.", + "generic": "Stala sa chyba. Skontrolujte si nastavenia kalendáru a skúste aktualizovať kalendár. ", + "notSignedIn": "Stala sa chyba počas autentifikácie pre zobrazovanie kaledárových udalosti. Skontrolujte si nastavenia kalendáru a skúste sa znovu prihlásiť." + }, + "join": "Zúčastniť sa", + "joinTooltip": "Zúčastniť sa stretnutia", + "nextMeeting": "nasledujúce stretnutie", + "noEvents": "Niesú naplánované žiadne ďalšie udalosti.", + "ongoingMeeting": "prebiehajúce stretnutie", + "permissionButton": "Otvoriť nastavenia", + "permissionMessage": "Aplikácia potrebuje kalendárové oprávnenie pre zobranie termínov a stretnutí ", + "refresh": "Aktualizovať kalendár", + "today": "Dnes" + }, + "chat": { + "error": "Chyba: vaša správa \"{{originalText}}\" nebola poslaná. Dôvod: {{error}}", + "fieldPlaceHolder": "Zadajte sem vašu správu", + "messagebox": "Napíšte správu", + "messageTo": "Správa pre {{recipient}}", + "noMessagesMessage": "V tejto konferencií ešte nie je žiadna správa. Začnite tu vašu diskusiu!", + "nickname": { + "popover": "Zvoľte meno", + "title": "Zadajte sem vašu prezývku" + }, + "privateNotice": "Súkromná správa pre {{recipient}}", + "title": "Písanie", + "you": "Vy" + }, + "chromeExtensionBanner": { + "installExtensionText": "Nainštalujte rozšírenie pre integráciu s Google Calendar a Office 365", + "buttonText": "Inštalovať Chrome rozšírenie", + "dontShowAgain": "Upozornenie viac nezobrazovať" + }, + "connectingOverlay": { + "joiningRoom": "Vytvára sa spojenie do vašej konferencie…" + }, + "connection": { + "ATTACHED": "Priložený", + "AUTHENTICATING": "Overujem", + "AUTHFAIL": "Overenie zlyhalo", + "CONNECTED": "Pripojený", + "CONNECTING": "Pripájam", + "CONNFAIL": "Spojenie zlyhalo", + "DISCONNECTED": "Odpojený", + "DISCONNECTING": "Odpájam", + "ERROR": "Chyba", + "RECONNECTING": "Chyba siete. Skúšam sa znova pripojiť ...", + "LOW_BANDWIDTH": "Video pre {{displayName}} bolo vypnuté, aby sa ušetrila prenosová kapacita" + }, + "connectionindicator": { + "address": "Adresa:", + "bandwidth": "Predpokladaný dat. tok:", + "bitrate": "Prenos. rýchlosť", + "bridgeCount": "Počet serverov:", + "connectedTo": "Spojenie s:", + "e2e_rtt": "E2E RTT:", + "framerate": "Rýchlosť snímkovania:", + "less": "Zobraz menej", + "localaddress_0": "Lokálna adresa:", + "localaddress_1": "Lokálne adresy:", + "localaddress_2": "", + "localport_0": "Lokálny port:", + "localport_1": "Lokálne porty:", + "localport_2": "", + "more": "Zobraz viac", + "packetloss": "Strata packetov:", + "quality": { + "good": "Dobré", + "inactive": "Neaktívne", + "lost": "Stratené", + "nonoptimal": "Nie je optimálne", + "poor": "Slabé" + }, + "remoteaddress_0": "Vzdialená adresa:", + "remoteaddress_1": "Vzdialené adresy:", + "remoteaddress_2": "", + "remoteport_0": "Vzdialený port:", + "remoteport_1": "Vzdialené porty:", + "remoteport_2": "", + "resolution": "Rozlíšenie:", + "status": "Spojenie:", + "transport": "Prenos:" + }, + "dateUtils": { + "earlier": "Skôr", + "today": "Dnes", + "yesterday": "Včera" + }, + "deepLinking": { + "appNotInstalled": "Potrebujete aplikáciu {{app}}, aby ste sa mohli pripojiť do tejto konferencie na vašom telefóne.", + "description": "Nič sa nestalo? Snažili sme sa otvoriť konferenciu v {{app}}. Skúste to znovu, alebo sa pripojte na konferenciu v {{app}} cez Web.", + "descriptionWithoutWeb": "Nič sa nestalo? Snažili sme sa spustiť váš rozhovor v desktopovej aplikácií {{app}}.", + "downloadApp": "Stiahnutie aplikácie", + "launchWebButton": "Otvoriť na webe", + "openApp": "Pokračovať na aplikáciu", + "title": "Konferencia sa otvára v {{app}}...", + "tryAgainButton": "Skúsiť znova s natívnou aplikáciou" + }, + "defaultLink": "napr. {{url}}", + "defaultNickname": "napr. Jane Pink", + "deviceError": { + "cameraError": "Chyba pri prístupe ku kamere", + "cameraPermission": "Aplikácia nemá oprávnenie pristupovať ku kamere", + "microphoneError": "Chyba pri prístupe k mikrofónu", + "microphonePermission": "Aplikácia nemá oprávnenie pristupovať k mikrofónu" + }, + "deviceSelection": { + "noPermission": "Oprávnenie nie je poskytnuté", + "previewUnavailable": "Náhľad nie je dostupný", + "selectADevice": "Vyberte zvukové zariadenie", + "testAudio": "Vyskúšať zvuk" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Živé vysielanie" + }, + "allow": "Povoliť", + "alreadySharedVideoMsg": "Iný účastník už poskytuje video. Pri tejto konferencií môže poskytovať súčasne iba jeden účastník.", + "alreadySharedVideoTitle": "Naraz sa dá poskytovať iba jedno video", + "applicationWindow": "Okno aplikácie", + "Back": "Späť", + "cameraConstraintFailedError": "Vaša kamera nespĺňa potrebné požiadavky.", + "cameraNotFoundError": "Kamera nebola nájdená.", + "cameraNotSendingData": "Kamera nie je dostupná. Skontrolujte či iná aplikácia používa kameru, vyberte inú kameru v nastaveniach ale znovu spustite aplikáciu.", + "cameraNotSendingDataTitle": "Prístup na kameru nie je možný.", + "cameraPermissionDeniedError": "Nebolo udelené oprávnenie používať kameru. Napriek tomu sa môže zúčastniť na konferencií, ale ostatný účastníci vás nebudu vidieť. Pre pridelenie oprávnenia môžete použiť ikonu kamery na adresnej lište.", + "cameraUnknownError": "Z neznámeho dôvodu sa kamera nedá použiť.", + "cameraUnsupportedResolutionError": "Táto kamera nepodporuje požadované rozlíšenie.", + "Cancel": "Zrušiť", + "close": "Zatvoriť", + "conferenceDisconnectMsg": "Skontrolujte prípadne vaše sieťové pripojenie. Pripájam znovu o {{seconds}} sekúnd...", + "conferenceDisconnectTitle": "Vaše spojenie bolo prerušené.", + "conferenceReloadMsg": "Snažíme sa to napraviť. Pripájam znovu o {{seconds}} sekund...", + "conferenceReloadTitle": "Žiaľ niečo sa nepodarilo.", + "confirm": "Potvrdiť", + "confirmNo": "Nie", + "confirmYes": "Áno", + "connectError": "Oops! Niečo je zle a nemôžem sa pripojiť do konferencie.", + "connectErrorWithMsg": "Oops! Niečo je zle a nemôžem sa pripojiť do konferencie. Správa: {{msg}}", + "connecting": "Pripájam", + "contactSupport": "Spojiť sa s podporou", + "copy": "Kopírovať", + "dismiss": "Zavrieť", + "displayNameRequired": "Ahoj! Ako sa voláš?", + "done": "Hotovo", + "enterDisplayName": "Prosím zadajte sem vaše meno", + "error": "Chyba", + "externalInstallationMsg": "Zlyhanie pri inštalácií rozšírenia pre zdieľanie prac. plochy", + "externalInstallationTitle": "Potrebné rozšírenie:", + "goToStore": "", + "gracefulShutdown": "Naša služba je momentálne vypnutá pre údržbu. Skúste to neskor.", + "IamHost": "Ja som hostiteľ", + "incorrectRoomLockPassword": "Nesprávne heslo", + "incorrectPassword": "Používateľské meno alebo heslo je nesprávne", + "inlineInstallationMsg": "Musí byť nainštalované rozšírenie pre zdieľanie pracovnej prochy.", + "inlineInstallExtension": "Teraz inštalovať", + "internalError": "Ups! Niečo nefunguje. Vyskytla sa nasledujúca chyba: {{error}}", + "internalErrorTitle": "Interná chyba", + "kickMessage": "Pre podrobnosti sa môžete spojiť s {{participantDisplayName}}.", + "kickParticipantButton": "Odstrániť", + "kickParticipantDialog": "Skutočne chcete odstrániť tohto účastnika?", + "kickParticipantTitle": "Odstrániť účastníka?", + "kickTitle": "Ouch! {{participantDisplayName}} vás odstránil zo stretnutia.", + "liveStreaming": "Živé vysielanie", + "liveStreamingDisabledForGuestTooltip": "Hostia nemôžu začať živé vysielanie.", + "liveStreamingDisabledTooltip": "Štartovanie živého vysielania je vypnuté.", + "lockMessage": "Zlyhanie pri pokuse o zabezpečenie konferencie.", + "lockRoom": "Pridať stretnutie $t(lockRoomPasswordUppercase)", + "lockTitle": "Zabezpečenie zlyhalo", + "logoutQuestion": "Ste si istý, že sa chcete odhlásiť a skončiť konferenciu?", + "logoutTitle": "Odhlásiť", + "maxUsersLimitReached": "Bol dosiahnutý maximálny počet účastníkov. Konferencia je plná. Spojte sa prosím s organizátorom stretnutia, alebo to skúste neskôr.", + "maxUsersLimitReachedTitle": "Dosiahnutý maximálny počet účastníkov", + "micConstraintFailedError": "Váš mikrofón nespĺňa potrebné požiadavky.", + "micNotFoundError": "Mikrofón nebol nájdený.", + "micNotSendingData": "Choďte do nastavení vašeho počítača, aby ste odblokovali stlmenie vášho mikrofónu a upravte jeho úroveň.", + "micNotSendingDataTitle": "Mikrofón je stlmený vašimi systémovými nastaveniami.", + "micPermissionDeniedError": "Nebolo udelené oprávnenie používať mikrofón. Napriek tomu sa môže zúčastniť na konferencií, ale ostatný účastníci vás nebudú počuť. Pre pridelenie oprávnenia môžete použiť ikonu kamery na adresnej lište.", + "micUnknownError": "Mikrofón sa nedá použiť z neznámeho dôvodu.", + "muteEveryoneElseDialog": "Keď všetkým vypnete mikrofón, nedokážete spať zapnuť mikrofóny. Účastníci si ale môžu zapnúť mikrofóny sami.", + "muteEveryoneElseTitle": "Vypnúť mikrofón všetkým okrem {{whom}}?", + "muteEveryoneDialog": "Chcete naozaj všetkým vypnúť mikrofón. Keď všetkým vypnete mikrofón, nedokážete spať zapnúť mikrofóny. Účastníci si ale môžu zapnúť mikrofóny sami.", + "muteEveryoneTitle": "Všetkým vypnúť mikrofón?", + "muteEveryoneSelf": "seba samého", + "muteEveryoneStartMuted": "Všetci odteraz začínajú s vypnutým mikrofónom", + "muteParticipantBody": "Nedokážete zapnúť vypnuté mikrofóny ostatných účastníkov, ale každý účastník si vie sám zapnúť mikrofón.", + "muteParticipantButton": "Vypnúť mikrofón", + "muteParticipantDialog": "Chcete naozaj tomuto účastníkovi vypnúť mikrofón? Keď vypnete mikrofón, nedokážete späť zapnúť mikrofón tohto účastníka. Účastníci si ale dokážu zapnúť mikrofóny sami.", + "muteParticipantTitle": "Vypnúť účastníkovi mikrofón?", + "Ok": "Ok", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "Nastavovanie $t(lockRoomPassword) nie je podporované.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nie je podporované", + "passwordRequired": "$t(lockRoomPasswordUppercase) je potrebné", + "popupError": "Váš prehliadať blokuje vyskakovacie okná tejto stránky. Prosím aktivujte vyskakovacie okná v bezpečnostných nastaveniach vašeho prehliadača a skúste znovu.", + "popupErrorTitle": "Vyskakovacie okná sú zablokované", + "recording": "Nahrávanie", + "recordingDisabledForGuestTooltip": "Hostia nemôžu začať nahrávanie.", + "recordingDisabledTooltip": "Štartovanie nahrávania je vypnuté.", + "rejoinNow": "Teraz sa znovu pridať.", + "remoteControlAllowedMessage": "{{user}} prijal požiadavku o vzdialené ovládanie.", + "remoteControlDeniedMessage": "{{user}} odmietol prijal požiadavku o vzdialené ovládanie.", + "remoteControlErrorMessage": "Stala sa chyba počas žiadania o vzdialené ovládanie od {{user}}", + "remoteControlRequestMessage": "Povolíte {{user}} ovládať vášu pracovnú plochu?", + "remoteControlShareScreenWarning": "Pozor, keď povolíte požiadavku budete zdielať vašu obrazovku!", + "remoteControlStopMessage": "Vzdialené ovládanie bolo ukončené.", + "remoteControlTitle": "Vzdialené ovládanie", + "Remove": "Odstrániť", + "removePassword": "$t(lockRoomPassword) odstránené", + "removeSharedVideoMsg": "Ste si istý že chcete odstrániť zdielané video?", + "removeSharedVideoTitle": "Odstrániť zdielané video", + "reservationError": "Systémová chyba rezervácie", + "reservationErrorMsg": "Chyba: {{code}}, správa: {{msg}}", + "retry": "Skúsiť znovu", + "screenSharingFailedToInstall": "Ups! Nepodarilo sa nainštalovať rozšírenie pre zdieľanie obrazovky.", + "screenSharingFailedToInstallTitle": "Chyba v inštalácii rozšírenie pre zdieľanie obrazovky", + "screenSharingFirefoxPermissionDeniedError": "Niečo sa nepodarilo pri pokuse o zdielanie obrazovky. Skontrolujte prosím či ste dali oprávnenie v prehliadači.", + "screenSharingFirefoxPermissionDeniedTitle": "Nepodarilo sa zdielať obrazovku", + "screenSharingPermissionDeniedError": "Ups! Niečo sa nepodarilo pri žiadaní o oprávnenie zdielať obrazovku. Prosím aktualizovať a skúsiť znovu.", + "sendPrivateMessage": "Dostali ste súkromnú správu. Chceli ste na ňu odpovedať súkromne, alebo chcete poslať správu skupine?", + "sendPrivateMessageCancel": "Poslať skupine", + "sendPrivateMessageOk": "Poslať súkromne", + "sendPrivateMessageTitle": "Súkromne poslať?", + "serviceUnavailable": "Služba nedostupná", + "sessTerminated": "Volanie ukončené", + "Share": "Zdieľať", + "shareVideoLinkError": "Prosím, zadajte správny Youtube odkaz.", + "shareVideoTitle": "Zdielať video", + "shareYourScreen": "Zdielať obrazovku", + "shareYourScreenDisabled": "Zdieľanie obrazovky vypnuté.", + "shareYourScreenDisabledForGuest": "Hostia nemôžu zdielať obrazovku.", + "startLiveStreaming": "Spustiť priamy prenos", + "startRecording": "Začať záznam", + "startRemoteControlErrorMessage": "Chyba pri pokuse o začatie vzdialeného riadenia!", + "stopLiveStreaming": "Prerušiť priamy prenos", + "stopRecording": "Zastaviť záznam", + "stopRecordingWarning": "Chcete zastaviť záznam?", + "stopStreamingWarning": "Chcete prerušiť priamy prenos", + "streamKey": "Klúč živého vysielania", + "Submit": "OK", + "thankYou": "Ďakujeme vám za používanie {{appName}}!", + "token": "token", + "tokenAuthFailed": "Prepáčte, nie ste oprávnený zúčastniť tejto sa konferencie.", + "tokenAuthFailedTitle": "Overenie zlyhalo", + "transcribing": "", + "unlockRoom": "Odstrániť stretnutie $t(lockRoomPassword)", + "userPassword": "užívateľské heslo", + "WaitForHostMsg": "Konferencia <b>{{room}}</b> sa ešte nezačala. Autorizujte sa prosím ak ste hostiteľ. V opačnom prípade čakajte na hostiteľa.", + "WaitForHostMsgWOk": "Konferencia <b>{{room}}</b> sa ešte nezačala. Ak ste hostiteľ autorizujte sa stlačením Ok. V opačnom prípade čakajte na hostiteľa.", + "WaitingForHost": "Čakám na hostiteľa ...", + "Yes": "Áno", + "yourEntireScreen": "Celú obrazovku" + }, + "dialOut": { + "statusMessage": "je teraz {{status}}" + }, + "documentSharing": { + "title": "Zdielaný dokument" + }, + "feedback": { + "average": "Priemerný", + "bad": "Zlý", + "detailsLabel": "Povedzte nám viac.", + "good": "Dobrý", + "rateExperience": "Ohodnoťte dojem", + "veryBad": "Veľmi zlý", + "veryGood": "Veľmi dobrý" + }, + "incomingCall": { + "answer": "Odpovedať", + "audioCallTitle": "Prichádzajúci hovor", + "decline": "Odmietnuť", + "productLabel": "", + "videoCallTitle": "Prichádzajúci video-hovor" + }, + "info": { + "accessibilityLabel": "Zobraziť informácie", + "addPassword": "$t(lockRoomPassword) pridať", + "cancelPassword": "$t(lockRoomPassword) zmazať", + "conferenceURL": "Odkaz:", + "country": "Krajina", + "dialANumber": "Aby ste sa zúčastnili stretnutia, zavolajte jedno z týchto čísel a zadajte pin.", + "dialInConferenceID": "", + "dialInNotSupported": "Prepáčte, volanie nie je podporované.", + "dialInNumber": "Volanie:", + "dialInSummaryError": "Chyba pri získavaná informácií o volaniach. Skúste neskôr.", + "dialInTollFree": "Bezplatné", + "genericError": "Niečo sa nepodarilo.", + "inviteLiveStream": "Kliknite túto linku {{url}}, pre zobrazenie živého vysielania z tohto stretnutia.", + "invitePhone": "Keď sa chcete pripojiť cez telefón, klikni na: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Hľadáte iné pripojovacie číslo? Pripojovacie čísla pre konferenciu: {{{url}}\n\n\n\nTaktiež pokiaľ sa telefonicky pripájate cez konferenčný celomiestnostný telefón pripojte sa bez prenosu zvuku {{silentUrl}}", + "inviteURLFirstPartGeneral": "Ste pozývaný pripojiť sa na stretnutie.", + "inviteURLFirstPartPersonal": "{{name}} vás pozýva na stretnutie.", + "inviteURLSecondPart": "\n Zúčastniť sa stretnutia:\n{{url}}\n", + "liveStreamURL": "Živý prenos:", + "moreNumbers": "Ďalšie telefónne čísla", + "noNumbers": "Žiadne pripojovacie telefónne čísla.", + "noPassword": "Žiadne", + "noRoom": "Nebola zadaná žiadna konferencia na pripojenie.", + "numbers": "Pripojovacie čísla", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Zdieľať", + "tooltip": "Zdieľať odkaz a informácie o vytáčaní pre toto stretnutie", + "label": "Informácie o stretnutí" + }, + "inviteDialog": { + "alertText": "Nepodarilo sa pozvať niektorych účastníkov.", + "header": "Pozvať", + "searchCallOnlyPlaceholder": "Zadajte telefónne číslo", + "searchPeopleOnlyPlaceholder": "Hľadať účastníkov", + "searchPlaceholder": "Účastník alebo telefónne číslo", + "send": "Poslať" + }, + "inlineDialogFailure": { + "msg": "Vyskytla sa chyba.", + "retry": "Skúsiť znovu", + "support": "Podpora", + "supportMsg": "Pokiaľ sa chyba zopakuje, spojte sa s" + }, + "keyboardShortcuts": { + "focusLocal": "Zamerať sa na vaše video", + "focusRemote": "Zamerať sa na video iného účastníka", + "fullScreen": "Zapnúť / Vypnúť plnú obrazovku", + "keyboardShortcuts": "Klávesové skratky", + "localRecording": "Zobraziť, alebo skryť tlačítka pre lokálne nahrávanie", + "mute": "Vypnúť alebo zapnúť mikrofón", + "pushToTalk": "Stlačiť, pre hovorenie", + "raiseHand": "Zdvihnúť alebo dať dole ruku", + "showSpeakerStats": "Zobrať štatistiky rečníka", + "toggleChat": "Zobraziť alebo skryť textové správy", + "toggleFilmstrip": "Zobraziť alebo skryť video náhľady", + "toggleScreensharing": "Prepnúť medzi kamerou a zdieľaním obrazovky", + "toggleShortcuts": "Zobraziť alebo skryť klávesové skratky", + "videoMute": "Zapnúť alebo vypnúť kameru", + "videoQuality": "Nastavenie kvality volania" + }, + "liveStreaming": { + "busy": "Chystajú sa zdroje pre vysielanie. Skúste znova za pár minút.", + "busyTitle": "Všetky vysielacie inštancie sú obsadené", + "changeSignIn": "Prepnúť konto", + "choose": "Vyberte živé vysielanie", + "chooseCTA": "Vyberte vysielaciu možnosť. Ste prihlásený ako {{email}}", + "enterStreamKey": "Zadajte meno/heslo pre YouTube vysielanie.", + "error": "Živé vysielanie zlyhalo. Prosím skúste to znovu.", + "errorAPI": "Došlo chybe pri prístupe k vašemu YouTube vysielaniu. Prosím skúste sa znovu prihlásiť.", + "errorLiveStreamNotEnabled": "Živé vysielanie pre {{email}} nie je aktivované. Aktivujte živé vysielanie, alebo sa prihláste pomocou konta s aktivovaným živým vysielaním.", + "expandedOff": "Živé vysielanie bolo zastavené", + "expandedOn": "Stretnutie je momentálne vysielané na YouTube.", + "expandedPending": "Spúšťa živé vysielanie...", + "failedToStart": "Nepodarilo sa naštartovať živé vysielanie", + "getStreamKeyManually": "Nepodarilo sa získať žiadne živé vysielania. Skúste získať kľúč pre živé vysielanie z YouTube.", + "invalidStreamKey": "Kľúč pre živé vysielanie je nesprávny.", + "off": "Živé vysielanie ukončené", + "offBy": "{{name}} ukončil živé vysielanie", + "on": "Živé vysielanie", + "onBy": "{{name}} začal živé vysielanie", + "pending": "Štartuje sa živé vysielanie...", + "serviceName": "Služba pre živé vysielanie", + "signedInAs": "Ste prihlásený ako:", + "signIn": "Prihlásiť sa pomocou Google", + "signInCTA": "Prihláste sa, alebo zadajte váš YouTube klúč pre živé vysielanie.", + "signOut": "Odhlásiť", + "start": "Začať živé vysielanie", + "streamIdHelp": "Čo je to?", + "unavailableTitle": "Živé vysielanie nie je k dispozícií" + }, + "localRecording": { + "clientState": { + "off": "Vypnutý", + "on": "Zapnutý", + "unknown": "Neznámy" + }, + "dialogTitle": "Lokálne ovládacie prvky nahrávania", + "duration": "Dĺžka", + "durationNA": "neznáma", + "encoding": "Kódovanie", + "label": "", + "labelToolTip": "Lokálny nahrávanie je aktivovaný", + "localRecording": "Lokálne nahrávanie", + "me": "Ja", + "messages": { + "engaged": "Lokálne nahrávanie je spustené", + "finished": "Nahrávanie sedenia {{token}} je ukončené. Prosím pošlite nahratý súbor moderátorovi.", + "finishedModerator": "Nahrávanie sedenia {{token}} je ukončené. Bola uložená nahrávka lokálnej stopy. Poproste ostatných účastníkov, aby vám poslali ich nahrávky.", + "notModerator": "Nieste moderátor. Nemôže začať, alebo skončiť lokálne nahrávanie." + }, + "moderator": "Moderátor", + "no": "Nie", + "participant": "Účastník", + "participantStats": "Štatistiky účastníkov", + "sessionToken": "Token sedenia", + "start": "Zapnúť nahrávanie", + "stop": "Vypnúť nahrávanie", + "yes": "Áno" + }, + "lockRoomPassword": "heslo", + "lockRoomPasswordUppercase": "Heslo", + "me": "ja", + "notify": { + "connectedOneMember": "{{name}} sa pripojil", + "connectedThreePlusMembers": "{{name}} a {{count}} ďalších osôb sa pripojilo", + "connectedTwoMembers": "{{first}} a {{second}} sa pripojili", + "disconnected": "odpojený", + "focus": "Konferenčný focus", + "focusFail": "{{component}} je nedostupný - skúste znova za {{ms}} sek", + "grantedTo": "Práva moderátora boli udelené {{to}}!", + "invitedOneMember": "{{displayName}} bol pozvaný", + "invitedThreePlusMembers": "{{name}} a {{count}} ďalší boli pozvaný", + "invitedTwoMembers": "{{first}} a {{second}} boli pozvaný", + "kickParticipant": "Pre ďalšie podrobnosti sa môže obrátiť na {{participantDisplayName}}", + "me": "Ja", + "moderator": "Boli vám udelené práva moderátora!", + "muted": "Začali ste rozhovor s vypnutým mikrofónom.", + "mutedTitle": "Boli ste stíšený!", + "mutedRemotelyTitle": "{{participantDisplayName}} vás stíšil", + "mutedRemotelyDescription": "Kedykoľvek môžete stíšenie zrušiť, keď ste prichystaný rozprávať. Keď skončite môžete sa znova stíšiť, aby ste znížili hluk na stretnutí.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) bolo odstránené iným účastníkom", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) bolo nastavené iným účastníkom", + "raisedHand": "{{name}} chce hovoriť", + "somebody": "Niekto", + "startSilentTitle": "Pripojili ste sa bez zvukového výstupu!", + "startSilentDescription": "Pripojte sa k stretnutiu ešte raz, aby ste aktivovali zvuk.", + "suboptimalBrowserWarning": "Obávame sa, že dojem z tohto stretnutia nebude až taký dobrý. Hladáme spôsob ako to zlepšiť, ale dovtedy prosím skúste jeden z <a href='static/recommendedBrowsers.html' target='_blank'>plne podporovaných prehliadačov</a>.", + "suboptimalExperienceTitle": "Prehliadačové varovanie", + "unmute": "Zapnúť mikrofón", + "newDeviceCameraTitle": "Bola zistená nová kamera", + "newDeviceAudioTitle": "Bolo zistené nové audio zariadenie", + "newDeviceAction": "Použiť" + }, + "passwordSetRemotely": "nastavené iným účastníkom", + "passwordDigitsOnly": "až {{number}} číslic", + "poweredby": "založené na", + "presenceStatus": { + "busy": "Obsadený", + "calling": "Je volaný", + "connected": "Pripojený", + "connecting": "Pripájam", + "connecting2": "Pripájam", + "disconnected": "Odpojený", + "expired": "Vypršaní", + "ignored": "Ignorovaný", + "initializingCall": "Volanie je inicializované...", + "invited": "Pozvať", + "rejected": "Zložiť", + "ringing": "Zvoní..." + }, + "profile": { + "setDisplayNameLabel": "Nastavte si meno", + "setEmailInput": "Zadajte e-mail", + "setEmailLabel": "Nastavte si email vašeho gravataru", + "title": "Profil" + }, + "raisedHand": "Chcel by som hovoriť", + "recording": { + "authDropboxText": "Nahrať na Dropbox", + "availableSpace": "Dostupná kapacita {{spaceLeft}} MB (ca. {{duration}} minút nahrávania)", + "beta": "BETA", + "busy": "Pripravujú sa prostriedky na nahrávanie. Skúste znova o pár minút.", + "busyTitle": "Všetky nahrávacie inštancie sú obsadené", + "error": "Nahrávka sa nepodarila. Skúste prosím znovu.", + "expandedOff": "Nahrávanie bolo zastavené", + "expandedOn": "Stretnutie sa momentálne nahráva.", + "expandedPending": "Začína sa nahrávanie...", + "failedToStart": "Nepodarilo sa začať nahrávanie", + "fileSharingdescription": "Nahrávku zdielať s účastníkmi stretnutia", + "live": "LIVE", + "loggedIn": "Prihlásený ako {{userName}}", + "off": "Nahrávanie zastavené", + "offBy": "{{name}} zastavil nahrávanie", + "on": "Nahrávanie", + "onBy": "{{name}} začal nahrávanie", + "pending": "Pripravuje sa nahrávanie stretnutia...", + "rec": "REC", + "serviceDescription": "Vaša nahrávka sa ukladá nahrávacou službou", + "serviceName": "Nahrávacia služba", + "signIn": "Prihlásiť", + "signOut": "Odhlásiť", + "unavailable": "Oh! Služba {{serviceName}} nie je dostupná. Pracujeme na riešeni problému. Skúste prosím neskôr.", + "unavailableTitle": "Nahrávanie nie je dostupné" + }, + "sectionList": { + "pullToRefresh": "Potiahnuť pre aktualizáciu" + }, + "settings": { + "calendar": { + "about": "Používa sa kalendárová integrácia {{appName}} pre zabezpečený prístup ku vašemu kalendáru.", + "disconnect": "Odpojený", + "microsoftSignIn": "Prihlásiť pomocou Microsoft", + "signedIn": "Momentálne sa pristupuje na kalendárové udalosti pre {{email}}. Stlačte tlačidlo „Odpojiť“ pre prerušenie spojenia ku kalendárovým udalostiam.", + "title": "Kalendár" + }, + "devices": "Zariadenia", + "followMe": "Všetci sledujú mňa", + "language": "Jazyk", + "loggedIn": "Prihlásený ako {{name}}", + "moderator": "Moderátor", + "more": "Viac", + "name": "Meno", + "noDevice": "Žiadne zariadenie", + "selectAudioOutput": "Zvukový výstup", + "selectCamera": "Kamera", + "selectMic": "Mikrofón", + "startAudioMuted": "Pri pripojení všetkým stlmiť zvuk", + "startVideoMuted": "Pri pripojení všetkým vypnúť video", + "title": "Nastavenia" + }, + "settingsView": { + "advanced": "Rozšírené", + "alertOk": "OK", + "alertTitle": "Upozornenie", + "alertURLText": "Zadaná serverový URL je neplatná", + "buildInfoSection": "informácie o kompilácií", + "conferenceSection": "Konferencia", + "disableCallIntegration": "Deaktivovať integráciu s natívnymi volaniami", + "disableP2P": "Deaktivovať mód s koncovými zariadeniami", + "displayName": "Ukázať", + "email": "E-mail", + "header": "Nastavenia", + "profileSection": "Profil", + "serverURL": "Adresa URL servera", + "showAdvanced": "Ukázať rozšírené nastavenia", + "startWithAudioMuted": "Začať so stlmeným zvukom", + "startWithVideoMuted": "Začať so vypnutým videom", + "version": "Verzia" + }, + "share": { + "dialInfoText": "\n\n=====\n\n Chcete ma zavolať na Vašom telefóne?\n\n{{{defaultDialInNumber}}}Kliknite na nasledujúci odkaz, aby ste videli vybrané telefónne čislo na stretnutí\n{{dialInfoPageUrl}}", + "mainText": "Kliknite na nasledujúci odkaz, aby ste sa pripojili na stretnutie:\n{{roomUrl}}" + }, + "speaker": "Rečník", + "speakerStats": { + "hours": "", + "minutes": "", + "name": "Meno", + "seconds": "", + "speakerStats": "Štatistiky rečníka", + "speakerTime": "Čas rečníka" + }, + "startupoverlay": { + "policyText": "", + "title": "{{app}} potrebuje prístup k vašemu mikrofónu a kamere." + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Znovu pripojiť", + "text": "Stlačte tlačidlo <i>Znovu pripojiť</i> na opätovné spojenie.", + "title": "Konferencia sa prerušila lebo váš počítač bol uspaní." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Zapnúť/vypnúť iba zvukový prenos", + "audioRoute": "Vybrať zvukové zariadenie", + "callQuality": "Spravovať kvalitu videa", + "cc": "Zapnúť/vypnúť titulky", + "chat": "Zapnúť/vypnúť textovú diskusiu", + "document": "Zatvoriť zdielaný dokument", + "download": "Stiahnuť našu aplikáciu", + "feedback": "Zanechať spätnú väzbu", + "fullScreen": "Zapnúť/vypnúť zobrazenie na celú obrazovku", + "hangup": "Ukončiť volanie", + "help": "Pomoc", + "invite": "Pozvať účastníka", + "kick": "Odstrániť účastníka", + "localRecording": "Zapnúť/vypnúť ovládanie lokálneho nahrávania", + "lockRoom": "Zapnúť/vypnúť heslo pre stretnutie", + "moreActions": "Menu „Ďalšie akcie“ zapnúť/vypnúť", + "moreActionsMenu": "Menu „Ďalšie akcie“", + "moreOptions": "Zobraz viac možností", + "mute": "„mikrofón stlmiť“ zapnúť/vypnúť", + "muteEveryone": "Všetkých stlmiť", + "pip": "Zapnuť/vypnuť mód obraz-v-obraze", + "privateMessage": "Poslať súkromnú správu", + "profile": "Upraviť profil", + "raiseHand": "„Ohlásiť sa“ zapnúť/vypnúť", + "recording": "Nahrávanie zapnúť/vypnúť", + "remoteMute": "Účastníka stlmiť", + "Settings": "Nastavenia zapnúť/vypnúť", + "sharedvideo": "Zdieľanie YouTube videa zapnúť/vypnúť", + "shareRoom": "Pozvať osobu", + "shareYourScreen": "Zdieľanie obrazovky zapnúť/vypnúť", + "shortcuts": "Klávesové skratky zobraziť/skryť", + "show": "V popredí zobraziť", + "speakerStats": "Štatistiky rečníka zobraziť/skryť", + "tileView": "Prepnúť dlaždicové zobrazenie", + "toggleCamera": "Zmeniť kameru", + "videomute": "„Video odpojiť“ zapnúť/vypnúť", + "videoblur": "Rozmazanie pozadia zapnúť/vypnúť" + }, + "addPeople": "Pridať účastníka do konferencie", + "audioOnlyOff": "Mód „Iba zvuk“ deaktivovať", + "audioOnlyOn": "Mód „Iba zvuk“ aktivovať", + "audioRoute": "Vybrať zvukové zariadenie", + "authenticate": "Overiť", + "callQuality": "Spravovať kvalitu videa", + "chat": "Otvoriť / Zatvoriť chat", + "closeChat": "Chat zatvoriť", + "documentClose": "Zdielaný dokument zatvoriť", + "documentOpen": "Zdielaný dokument otvoriť", + "download": "Stiahnuť našu aplikáciu", + "enterFullScreen": "Zobraziť na celú obrazovku", + "enterTileView": "Kachličkové zobrazenie", + "exitFullScreen": "Opustiť celú obrazovku", + "exitTileView": "Kachličkové zobrazenie vypnúť", + "feedback": "Nechať spätnú väzbu", + "hangup": "Odísť", + "help": "Pomoc", + "invite": "Pozvať účastníkov", + "login": "Prihlásiť", + "logout": "Odhlásiť", + "lowerYourHand": "Dať dole ruku", + "moreActions": "Viac akcií", + "moreOptions": "Viac možností", + "mute": "Vypnúť / Zapnúť mikrofón", + "muteEveryone": "Všetkých stlmiť", + "noAudioSignalTitle": "Neprichádza žiaden vstup z vašeho mikrofónu!", + "noAudioSignalDesc": "Pokiaľ ste zámerne nestlmili váš mikrofón v systémových nastavenia alebo hardvery, pouvažujte nad prepnutím zariadenia.", + "noAudioSignalDescSuggestion": "Pokiaľ ste zámerne nestlmili váš mikrofón v systémových nastavenia alebo hardvery, pouvažujte nad prepnutím na odporúčané zariadenie.", + "noAudioSignalDialInDesc": "Môže zavolať pomocou:", + "noAudioSignalDialInLinkDesc" : "Pripojovacie telefónne čísla", + "noisyAudioInputTitle": "Váš mikrofón vyzerá byť zašumený!", + "noisyAudioInputDesc": "Vyzerá, že váš mikrofón je zašumený, skúste ho vypnuť, alebo zmeňte zariadenie.", + "openChat": "Otvoriť chat", + "pip": "Zapnuť obraz-v-obraze", + "privateMessage": "Poslať súkromnú správu", + "profile": "Úprava profilu", + "raiseHand": "Prihlásiť / Odhlásiť sa o slovo", + "raiseYourHand": "Prihlásiť sa o slovo", + "Settings": "Nastavenia", + "sharedvideo": "Zdielať YouTube video", + "shareRoom": "Pozvať niekoho", + "shortcuts": "Ukázať klávesové skratky", + "speakerStats": "Štatistiky rečníka", + "startScreenSharing": "Začať zdieľanie obrazovky", + "startSubtitles": "Začať titulky", + "stopScreenSharing": "Ukončiť zdieľanie obrazovky", + "stopSubtitles": "Ukončiť titulky", + "stopSharedVideo": "Ukončiť zdielané YouTube video", + "talkWhileMutedPopup": "Skúšate hovoriť? Ste stlmený.", + "tileViewToggle": "Prepnúť dlaždicové zobrazenie", + "toggleCamera": "Zmeniť kameru", + "videomute": "Vypnúť / Zapnúť kameru", + "startvideoblur": "Rozmazať pozadie", + "stopvideoblur": "Ukončiť rozmazanie pozadia" + }, + "transcribing": { + "ccButtonTooltip": "titulky vypnuť/zapnúť", + "error": "Prepisovanie zlyhalo. Skúsiť znovu.", + "expandedLabel": "Prepisovanie je zapnuté", + "failedToStart": "Prepisovanie sa nepodarilo naštartovať", + "labelToolTip": "Stretnutie je prepisované", + "off": "Prepisovanie sa skončilo", + "pending": "Pripravuje sa prepisovanie stretnutia...", + "start": "Začni zobrazovať titulky", + "stop": "Skonči zobrazovať titulky", + "tr": "" + }, + "userMedia": { + "androidGrantPermissions": "Vyberte <b><i>Povoliť</i></b> keď sa prehliadač bude pýtať na povolenie.", + "chromeGrantPermissions": "Vyberte <b><i>Povoliť</i></b> keď sa prehliadač bude pýtať na povolenie.", + "edgeGrantPermissions": "Vyberte <b><i>Áno</i></b> keď sa prehliadač bude pýtať na povolenie.", + "electronGrantPermissions": "Prosím povoľte prístup k vašemu mikrofónu a kamere.", + "firefoxGrantPermissions": "Vyberte <b><i>Zdieľaj vybrané zariadenie</i></b> keď sa prehliadač bude pýtať na povolenie.", + "iexplorerGrantPermissions": "Vyberte <b><i>Ok</i></b> keď sa prehliadač bude pýtať na povolenie.", + "nwjsGrantPermissions": "Prosím povoľte prístup k vašemu mikrofónu a kamere.", + "operaGrantPermissions": "Vyberte <b><i>Povoliť</i></b> keď sa prehliadač bude pýtať na povolenie.", + "react-nativeGrantPermissions": "Vyber <b><i>Povoliť</i></b> keď sa prehliadač bude pýtať na povolenie.", + "safariGrantPermissions": "Vyberte <b><i>OK</i></b> keď sa prehliadač bude pýtať na povolenie." + }, + "videoSIPGW": { + "busy": "", + "busyTitle": "", + "errorAlreadyInvited": "", + "errorInvite": "", + "errorInviteFailed": "", + "errorInviteFailedTitle": "", + "errorInviteTitle": "", + "pending": "" + }, + "videoStatus": { + "audioOnly": "AUD", + "audioOnlyExpanded": "Ste v móde s nízkou prenosovou kapacitou. V tomto móde budete prijímať iba zvuk a zdielanú obrazovku.", + "callQuality": "Nastavenie kvality hovoru", + "hd": "HD", + "hdTooltip": "Sledujete obraz vo vysokej kvalite", + "highDefinition": "Vysoká kvalita", + "labelTooiltipNoVideo": "Žiadne video", + "labelTooltipAudioOnly": "Mód s nízkou prenosovou kapacitou je zapnutý", + "ld": "LD", + "ldTooltip": "Sledujete obraz v nízkej kvalite", + "lowDefinition": "Nízka kvalita", + "onlyAudioAvailable": "Iba zvuk je k dispozícií", + "onlyAudioSupported": "Na vašom prehliadači podporujeme iba zvuk.", + "p2pEnabled": "Prenos medzi koncovými používateľmi", + "p2pVideoQualityDescription": "Pri prenose medzi koncovými používateľmi sa dá video kvalita nastaviť iba na vysokú alebo iba zvukový prenos. Ostatné nastavenia sa budú používať iba ak prenose medzi koncovými používateľmi bude ukončený", + "recHighDefinitionOnly": "Uprednostňovať vysokú kvalitu", + "sd": "SD", + "sdTooltip": "Sledujete obraz v štandardnej kvalite", + "standardDefinition": "Štandardná kvalita" + }, + "videothumbnail": { + "domute": "Vypnúť mikrofón", + "flip": "Prevrátiť", + "kick": "Vyhodiť", + "moderator": "Moderátor", + "mute": "Účastník s vypnutým mikrofónom", + "muted": "Vypnutý mikrofón", + "remoteControl": "Vzdialené ovládanie", + "show": "Ukázať v popredí", + "videomute": "Účastník vypol kameru" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Kliknúť pre pripojenie", + "roomname": "Zadajte názov miestnosti" + }, + "appDescription": "Poďte na to, videokonferencie v rámci celého tímu. Do konferencie môžete pozvať kohokoľvek. Všetka komunikácia cez aplikáciu {{app}} je plne šifrovaná, 100% open source zaisťuje, že aplikáciu môžete využívať bez obmedzenia a navyše sa nemusíte ani registrovať.", + "audioVideoSwitch": { + "audio": "Zvuk", + "video": "Video" + }, + "calendar": "Kalendár", + "connectCalendarButton": "Pripojte váš kalendár", + "connectCalendarText": "", + "enterRoomTitle": "Začať nové stretnutie", + "roomNameAllowedChars": "Meno stretnutia by nemalo obsahovať žiaden z týchto znakov: ?, &, :, ', \", %, #.", + "go": "Začať", + "goSmall": "Začať", + "join": "Pripojiť", + "info": "Info", + "privacy": "Súkromie", + "recentList": "Posledné", + "recentListDelete": "Vymazať", + "recentListEmpty": "Váš zoznam posledných hovorov je prázdny. Spojte sa s kolegami z Vášho tímu a potom tu nájdete všetky vaše stretnutia.", + "reducedUIText": "Vítajte v {{app}}!", + "roomname": "Zadajte názov miestnosti", + "roomnameHint": "Zadajte názov alebo URL odkaz miestnosti ku ktorej sa chcete pripojiť. Pokial ste miestnosť vytvorili, uistite sa, že ostatný účastníci schôdzky zadajú rovnaké meno ako vy.", + "sendFeedback": "Odoslať spätnú väzbu", + "terms": "Podmienky používania", + "title": "Zabezpečené, plnohodnotné a úplne bezplatné videokonferencie" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sv.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sv.json index 699b65ef8..954b83832 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sv.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-sv.json @@ -4,7 +4,7 @@ "countryNotSupported": "Vi stöder inte den här platsen ännu.", "countryReminder": "Ringer du till utlandsnummer? Börja alltid med landskoden.", "disabled": "Du kan inte bjuda in andra.", - "failedToAdd": "", + "failedToAdd": "Kunde inte lägga till deltagare", "footerText": "Utringningsfunktionen är avstängd.", "loading": "Söker efter personer och telefonnummer", "loadingNumber": "Bekräftar telefonnummer", @@ -21,7 +21,8 @@ "bluetooth": "Bluetooth", "headphones": "Hörlurar", "phone": "Telefon", - "speaker": "Talare" + "speaker": "Talare", + "none": "Inga ljudenheter tillgängliga" }, "audioOnly": { "audioOnly": "Enbart ljud" @@ -45,16 +46,21 @@ "today": "Idag" }, "chat": { - "error": "", + "error": "Fel: ditt meddelande skickades inte. Orsak: {{error}}", "messagebox": "Skriv ett meddelande", "nickname": { "popover": "Välj ett namn", "title": "Skriv in ett namn för att börja använda chatten" }, - "title": "Chatt" + "title": "Chatt", + "you": "du", + "privateNotice": "Privat meddelande till {{recipient}}", + "noMessagesMessage": "Det finns ännu inga meddelanden i mötet. Påbörja en konversation!", + "messageTo": "Privat meddelande till {{recipient}}", + "fieldPlaceHolder": "Skriv ditt meddelande här" }, "connectingOverlay": { - "joiningRoom": "Ansluter till mötet ..." + "joiningRoom": "Ansluter till mötet …" }, "connection": { "ATTACHED": "Ansluten", @@ -66,7 +72,11 @@ "DISCONNECTED": "Frånkopplad", "DISCONNECTING": "Kopplar från", "ERROR": "Fel", - "RECONNECTING": "Ett nätverksproblem uppstod. Återansluter..." + "RECONNECTING": "Ett nätverksproblem uppstod. Återansluter...", + "LOW_BANDWIDTH": "Video för {{displayName}} har stängts av för att spara bandbredd", + "GOT_SESSION_ID": "Hämta sessions-id ... Klart", + "GET_SESSION_ID_ERROR": "Hämta sessions-id-fel: {{code}}", + "FETCH_SESSION_ID": "Hämtar sessions-id …" }, "connectionindicator": { "address": "Adress:", @@ -96,7 +106,9 @@ "resolution": "Upplösning:", "status": "Anslutning:", "transport": "Transport:", - "turn": " (turn)" + "transport_plural": "Transporter:", + "turn": " (turn)", + "e2e_rtt": "E2E RTT:" }, "dateUtils": { "earlier": "Tidigare", @@ -106,11 +118,11 @@ "deepLinking": { "appNotInstalled": "Du behöver mobilappen {{app}} för att gå med i det här mötet från din telefon.", "description": "Hände inget? Vi försökte starta mötet i programmet {{app}} i din skrivbordsapp. Försök igen eller starta det i webbappen {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "Händer inget? Vi försökte starta mötet i {{app}}-skrivbordsappen.", "downloadApp": "Hämta appen", "launchWebButton": "Starta på webben", "openApp": "Fortsätt till appen", - "title": "Startar ditt möte i {{app}} ...", + "title": "Startar ditt möte i {{app}} …", "tryAgainButton": "Försök igen på skrivbordet" }, "defaultLink": "t ex. {{url}}", @@ -131,7 +143,7 @@ "liveStreaming": "Livesändning" }, "allow": "Tillåt", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "En annan deltagare delar redan en video. Konferensen tillåter bara en video-delning åt gången.", "alreadySharedVideoTitle": "Endast en delad video åt gången tillåts", "applicationWindow": "Applikationsfönster", "Back": "Tillbaka", @@ -144,9 +156,9 @@ "cameraUnsupportedResolutionError": "Din kamera stöder inte den krävda videoupplösningen.", "Cancel": "Avbryt", "close": "Stäng", - "conferenceDisconnectMsg": "Kolla din internetanslutning. Återansluter om {{seconds}} sekunder...", + "conferenceDisconnectMsg": "Kolla din internetanslutning. Återansluter om {{seconds}} sekunder…", "conferenceDisconnectTitle": "Du har kopplats ner.", - "conferenceReloadMsg": "Vi försöker fixa problemet. Återansluter om {{seconds}} sekunder...", + "conferenceReloadMsg": "Vi försöker fixa problemet. Återansluter om {{seconds}} sekunder…", "conferenceReloadTitle": "Något gick snett.", "confirm": "Bekräfta", "confirmNo": "Nej", @@ -157,40 +169,40 @@ "contactSupport": "Kontakta kundtjänst", "copy": "Kopiera", "dismiss": "Förkasta", - "displayNameRequired": "", + "displayNameRequired": "Hej, vilket är ditt namn?", "done": "Klar", - "enterDisplayName": "", + "enterDisplayName": "Ange namn", "error": "Fel", "externalInstallationMsg": "Misslyckades att installera skrivbordsdelnings-tillägget.", "externalInstallationTitle": "Tillägg krävs", "goToStore": "Gå till appbutiken", "gracefulShutdown": "Vår tjänst är för tillfället nedstängd för underhåll. Vänligen försök senare.", "IamHost": "Jag är värd", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "Felaktigt lösenord", "incorrectPassword": "Fel användarnamn eller lösenord", "inlineInstallationMsg": "Misslyckades att installera skrivbordsdelnings-tillägget.", "inlineInstallExtension": "Installera nu", "internalError": "Ett fel uppstod. Fel: {{error}}", "internalErrorTitle": "Internt fel", - "kickMessage": "", + "kickMessage": "Du kan kontakta {{participantDisplayName}} för mer information.", "kickParticipantButton": "Sparka ut", "kickParticipantDialog": "Vill du sparka ut den här deltagaren?", "kickParticipantTitle": "Tysta deltagaren?", - "kickTitle": "", + "kickTitle": "Aj! {{participantDisplayName}} sparkade ut dig ur mötet", "liveStreaming": "Strömma", "liveStreamingDisabledForGuestTooltip": "Gäster kan inte starta en livesändning.", "liveStreamingDisabledTooltip": "Starta livesändning har inaktiverats.", "lockMessage": "Misslyckades att låsa konferensen.", - "lockRoom": "", + "lockRoom": "Lägg till möte $t(lockRoomPasswordUppercase)", "lockTitle": "Låsning misslyckades", "logoutQuestion": "Är du säker på att du vill logga ut och stoppa konferensen?", "logoutTitle": "Logga ut", "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReachedTitle": "Maximal deltagarantal uppnått", "micConstraintFailedError": "Din mikrofon uppfyller inte kraven för användning.", "micNotFoundError": "Hittar ingen mikrofon.", "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingDataTitle": "Din mikrofon är tystad av dina systeminställningar", "micPermissionDeniedError": "Du har inte tillåtit användning av din mikrofon. Du kan gå med i mötet men de andra kan då inte höra dej. Om du vill tillåta användning av din mikrofon gör du det via mikrofonknappen i URL-fältet.", "micUnknownError": "Av okänd anledning kan inte din mikrofon användas.", "muteParticipantBody": "Du kan inte aktivera deras mikrofoner, men de kan göra det själva.", @@ -198,10 +210,10 @@ "muteParticipantDialog": "Vill du tysta den här deltagaren? Du kan inte aktivera mikrofonen igen, men deltagaren kan när som helst göra det själv.", "muteParticipantTitle": "Tysta deltagaren?", "Ok": "Ok", - "passwordLabel": "", - "passwordNotSupported": "Att sätta ett lösenord för konferensrummet stöds inte.", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordLabel": "Mötet har låsts av en deltagare. Ange $t(lockRoomPassword) för att gå med.", + "passwordNotSupported": "Att sätta ett $t(lockRoomPassword) för mötesrummet stöds ej.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) stöds inte", + "passwordRequired": "$t(lockRoomPasswordUppercase) krävs", "popupError": "Din webbläsare blockerar pop-up-fönster från sajten. Tillåt pop-up-fönster från den här sajten i inställningarna och försök igen.", "popupErrorTitle": "Pop-up blockerad", "recording": "Inspelning", @@ -216,7 +228,7 @@ "remoteControlStopMessage": "Fjärrstyrningssessionen avslutades.", "remoteControlTitle": "Anslutning till fjärrskrivbord", "Remove": "Ta bort", - "removePassword": "", + "removePassword": "Ta bort $t(lockRoomPassword)", "removeSharedVideoMsg": "Är du säker på att du vill ta bort din delade video?", "removeSharedVideoTitle": "Ta bort den delade videon", "reservationError": "Fel i reservationssystemet", @@ -249,15 +261,24 @@ "tokenAuthFailed": "Du är inte behörig att delta i det här samtalet.", "tokenAuthFailedTitle": "Autentisering misslyckades", "transcribing": "Transkriberar", - "unlockRoom": "", + "unlockRoom": "Ta bort möte $t(lockRoomPassword)", "userPassword": "användarlösenord", - "WaitForHostMsg": "Konferensen <b>{{room}}<b> har inte börjat än. Autentisera konferensen om du är värd. Vänta annars på att värden startar konferensen.", - "WaitForHostMsgWOk": "Konferensen <b>{{room}}<b> har inte börjat än. Om du är värd, autentisera konferensen genom att trycka på Ok. Vänta annars på att värden startar konferensen.", - "WaitingForHost": "Väntar på värden ...", + "WaitForHostMsg": "Konferensen <b>{{room}}</b> har inte börjat än. Autentisera konferensen om du är värd. Vänta annars på att värden startar konferensen.", + "WaitForHostMsgWOk": "Konferensen <b>{{room}}</b> har inte börjat än. Om du är värd, autentisera konferensen genom att trycka på Ok. Vänta annars på att värden startar konferensen.", + "WaitingForHost": "Väntar på värden …", "Yes": "Ja", - "yourEntireScreen": "Helskärm" + "yourEntireScreen": "Helskärm", + "muteEveryoneElseDialog": "När någon tystats kan du inte slå på mikrofonen, men de kan själva slå på sin egen mikrofon när som helst.", + "muteEveryoneElseTitle": "Tysta alla utom {{whom}}?", + "muteEveryoneDialog": "Är du säker på att du vill tysta alla? Du kan inte slå på mikrofonen åt dem, men de kan själva slå på sin egen mikrofon när som helst.", + "muteEveryoneStartMuted": "Alla börjar tystade", + "muteEveryoneTitle": "Tysta alla?", + "sendPrivateMessage": "Du har fått ett privat meddelande. Tänkte du svara på det privat, eller vill du skicka ditt meddelande till alla deltagare?", + "screenSharingAudio": "", + "sendPrivateMessageCancel": "Skicka till alla deltagare", + "sendPrivateMessageTitle": "", + "sendPrivateMessageOk": "" }, - "\u0005dialog": {}, "dialOut": { "statusMessage": "är nu {{status}}" }, @@ -279,8 +300,8 @@ }, "info": { "accessibilityLabel": "Visa info", - "addPassword": "", - "cancelPassword": "", + "addPassword": "Lägg till $t(lockRoomPassword)", + "cancelPassword": "Avbryt $t(lockRoomPassword)", "conferenceURL": "Länk:", "country": "Land", "dialANumber": "Om du vill gå med i mötet ringer du något av dessa nummer och fyller sedan i PIN-koden.", @@ -291,18 +312,18 @@ "dialInTollFree": "Avgiftsfritt nummer", "genericError": "Oj då, något gick fel.", "inviteLiveStream": "Om du vill se livesändningen av mötet klickar du här: {{url}}", - "invitePhone": "", + "invitePhone": "För att gå med via telefon istället trycker du in: {{number}} ,, {{conferenceID}} #\n", "invitePhoneAlternatives": "", "inviteURLFirstPartGeneral": "Du är inbjuden till ett möte.", "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLSecondPart": "\nGå med i mötet:\n{{url}}\n", "liveStreamURL": "Livesändning:", "moreNumbers": "Fler nummer", "noNumbers": "Inga inringningsnummer.", "noPassword": "Inga enheter", "noRoom": "Inget rum specificerades för inringning.", "numbers": "Inringningsnummer", - "password": "", + "password": "$t(lockRoomPasswordUppercase):", "title": "Dela", "tooltip": "Dela länk och information om inringning för mötet", "label": "Mötesinformation" @@ -335,7 +356,8 @@ "toggleFilmstrip": "Visa eller dölj videominiatyrer", "toggleScreensharing": "Växla mellan kamera och skärmdelning", "toggleShortcuts": "Visa eller dölj kortkommandon", - "videoMute": "Aktivera / avaktivera din kamera" + "videoMute": "Aktivera / avaktivera din kamera", + "videoQuality": "" }, "liveStreaming": { "busy": "Vi försöker frigöra fler strömningsresurser. Försök igen senare.", @@ -344,18 +366,18 @@ "choose": "Välj en ström", "chooseCTA": "Välj ett livesändningsalternativ. Du är nu inloggad som {{email}}.", "enterStreamKey": "Skriv in lösenordet till YouTube-livesändningen här.", - "error": "Strömning misslyckades. Försök igen.", + "error": "Direktströmning misslyckades. Försök igen.", "errorAPI": "Ett fel inträffade vid åtkomst till dina YouTube-sändningar. Försök att logga in igen.", "errorLiveStreamNotEnabled": "Livesändning är otillgänglig för {{email}}. Aktivera livesändning eller logga in på ett konto där det är aktiverat.", "expandedOff": "Livesändningen har avslutats", "expandedOn": "Mötet livesänds just nu på YouTube.", - "expandedPending": "Livesändningen startas ...", + "expandedPending": "Livesändningen startas …", "failedToStart": "Strömningen kunde inte påbörjas", - "getStreamKeyManually": "", + "getStreamKeyManually": "Vi kunde inte hämta några direktströmmar. Försök att få din direktströmningsnyckel från YouTube.", "invalidStreamKey": "Livesändningslösenordet kan vara felaktigt.", "off": "Strömning avslutad", "on": "Strömma", - "pending": "Börja strömma ...", + "pending": "Börja strömma …", "serviceName": "Livesändningstjänst", "signedInAs": "Du är nu inloggad som:", "signIn": "Logga in med Google", @@ -363,9 +385,12 @@ "signOut": "Logga ut", "start": "Starta en livesändning", "streamIdHelp": "Vad är det här?", - "unavailableTitle": "Livesändning otillgänglig" + "unavailableTitle": "Livesändning otillgänglig", + "offBy": "{{name}} stoppade direktströmningen", + "onBy": "{{name}} startade direktströmningen", + "youtubeTerms": "Tjänstevillkor för YouTube", + "googlePrivacyPolicy": "" }, - "\u0005liveStreaming": {}, "localRecording": { "clientState": { "off": "Av", @@ -409,42 +434,43 @@ "invitedOneMember": "", "invitedThreePlusMembers": "", "invitedTwoMembers": "", - "kickParticipant": "", + "kickParticipant": "{{kicked}} sparkades ut av {{kicker}}", "me": "Jag", "moderator": "Moderatorsrättigheter tilldelades!", "muted": "Du har startat konversationen utan mikrofon.", "mutedTitle": "Du har mikrofonen avstängd!", "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", + "mutedRemotelyDescription": "Du kan alltid slå på mikrofonen när du är redo att tala. Stäng av när du är klar för att hålla brus borta från mötet.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) togs bort av en annan deltagare", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) satt av en annan deltagare", "raisedHand": "{{name}} vill prata.", "somebody": "Någon", - "startSilentTitle": "", - "startSilentDescription": "", + "startSilentTitle": "Du gick med utan ljud aktiverat!", + "startSilentDescription": "Anslut till mötet igen för att aktivera ljud", "suboptimalExperienceDescription": "Hmm... din upplevelse med {{appName}} kommer inte att bli särskilt bra. Vi försöker hitta sätt att förbättra det, men till dess använd en av <a href='static/recommendedBrowsers.html' target='_blank'>de helt stödda webbläsarna</a>.", "suboptimalExperienceTitle": "Webbläsarvarning", - "unmute": "", + "unmute": "Slå på mikrofonen", "newDeviceCameraTitle": "Ny kamera hittad", "newDeviceAudioTitle": "Ny ljudenhet hittad", - "newDeviceAction": "Använd" + "newDeviceAction": "Använd", + "suboptimalBrowserWarning": "Din mötesupplevelse kommer tyvärr inte att bli så bra. Vi letar efter sätt att förbättra detta, men fram till dess kan du försöka använda en av de <a href='static/recommendedBrowsers.html' target='_blank'> fullt stödda webbläsarna </a>." }, "passwordSetRemotely": "satt av en annan deltagare", "passwordDigitsOnly": "", "poweredby": "drivs av", "presenceStatus": { "busy": "Upptagen", - "calling": "Ringer ...", + "calling": "Ringer …", "connected": "Ansluten", - "connecting": "Ansluter ...", + "connecting": "Ansluter …", "connecting2": "Ansluter* ...", "disconnected": "Frånkopplad", "expired": "Utgången", "ignored": "Ignorerad", - "initializingCall": "Startar samtal ...", + "initializingCall": "Startar samtal …", "invited": "Inbjuden", "rejected": "Avvisad", - "ringing": "Ringer ..." + "ringing": "Ringer …" }, "profile": { "setDisplayNameLabel": "Ange ditt visningsnamn", @@ -461,21 +487,23 @@ "error": "Inspelningen misslyckades. Försök igen.", "expandedOff": "Inspelningen har avslutats", "expandedOn": "Mötet spelas nu in.", - "expandedPending": "Inspelningen startar ...", + "expandedPending": "Inspelningen påbörjas …", "failedToStart": "Inspelningen kunde inte påbörjas", "fileSharingdescription": "Dela inspelningen med mötesdeltagare", "live": "LIVE", "loggedIn": "Inloggad som {{userName}}", "off": "Inspelningen avslutades", "on": "Inspelning", - "pending": "Förbereder inspelning av mötet ...", + "pending": "Förbereder inspelning av mötet …", "rec": "REC", "serviceDescription": "Din inspelning kommer att sparas av inspelningstjänsten", "serviceName": "Inspelningstjänst", "signIn": "Logga in", "signOut": "Logga ut", "unavailable": "{{serviceName}} är inte tillgänglig. Vi försöker åtgärda felet. Försök igen senare.", - "unavailableTitle": "Inspelning kan inte göras" + "unavailableTitle": "Inspelning kan inte göras", + "onBy": "{{name}} påbörjade inspelningen", + "offBy": "{{name}} avslutade inspelningen" }, "sectionList": { "pullToRefresh": "Dra för att uppdatera" @@ -501,7 +529,9 @@ "selectMic": "Mikrofon", "startAudioMuted": "Alla börjar tystade", "startVideoMuted": "Alla börjar osynliga", - "title": "Inställningar" + "title": "Inställningar", + "speakers": "Talare", + "microphones": "" }, "settingsView": { "alertOk": "OK", @@ -516,11 +546,15 @@ "serverURL": "Serverlänk", "startWithAudioMuted": "Starta med ljudet avstängt", "startWithVideoMuted": "Starta med videon avstängd", - "version": "Version" + "version": "Version", + "disableCallIntegration": "Tillåt inte deltagande via telefon", + "showAdvanced": "Visa avancerade inställningar", + "disableP2P": "", + "advanced": "" }, "share": { - "dialInfoText": "", - "mainText": "" + "dialInfoText": "\n\n=====\n\nVill du istället ringa in via telefon?\n\n{{defaultDialInNumber}} Klicka på den här länken för att se telefonnumret för detta möte\n{{dialInfoPageUrl}}", + "mainText": "Klicka på länken för att delta i mötet:\n{{roomUrl}}" }, "speaker": "Talare", "speakerStats": { @@ -564,16 +598,22 @@ "recording": "Slå av eller på inspelning", "remoteMute": "Tysta deltagare", "Settings": "Öppna eller stäng inställningar", - "sharedvideo": "Slå av eller på Youtube-videodelning", + "sharedvideo": "Slå av eller på YouTube-videodelning", "shareRoom": "Bjud in någon", "shareYourScreen": "Slå av eller på skärmdelning", "shortcuts": "Stäng eller öppna genvägar", "show": "", - "speakerStats": "Stäng eller öppna högstalarstatistik", + "speakerStats": "Stäng eller öppna talarstatistik", "tileView": "Öppna eller stäng panelvyn", - "toggleCamera": "Öppna eller stäng kamera", + "toggleCamera": "Växla kamera", "videomute": "Sätt på eller stäng av mikrofonen", - "videoblur": "" + "videoblur": "", + "muteEveryone": "Tysta alla", + "toggleFilmstrip": "", + "privateMessage": "", + "moreOptions": "", + "help": "", + "download": "" }, "addPeople": "Lägg till personer i samtal", "audioOnlyOff": "Avsluta ljudläget", @@ -606,7 +646,7 @@ "sharedvideo": "Dela en Youtube-video", "shareRoom": "Bjud in någon", "shortcuts": "Visa genvägar", - "speakerStats": "Högtalarspecifikationer", + "speakerStats": "Talarstatistik", "startScreenSharing": "Starta skärmdelning", "startSubtitles": "Starta undertextning", "stopScreenSharing": "Avsluta skämdelning", @@ -614,10 +654,22 @@ "stopSharedVideo": "Pausa YouTube-video", "talkWhileMutedPopup": "Försöker du tala? Din mikrofon är tystad.", "tileViewToggle": "Öppna eller stäng panelvyn", - "toggleCamera": "Öppna eller stäng kamera", + "toggleCamera": "Byta kamera", "videomute": "Aktivera / avaktivera kameran", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Blurra min bakgrund", + "stopvideoblur": "Inaktivera bakgrundsblurr", + "noisyAudioInputDesc": "Din mikrofon skapar brus, tysta din mikrofon eller byt ljudenhet.", + "noAudioSignalTitle": "Det kommer inget ljud från din mikrofon!", + "muteEveryone": "Tysta alla", + "privateMessage": "Skicka privat meddelande", + "noisyAudioInputTitle": "", + "noAudioSignalDialInLinkDesc": "", + "noAudioSignalDialInDesc": "", + "noAudioSignalDescSuggestion": "", + "noAudioSignalDesc": "", + "moreOptions": "", + "help": "", + "download": "" }, "transcribing": { "ccButtonTooltip": "Starta / Avsluta undertexter", @@ -626,7 +678,7 @@ "failedToStart": "Det gick inte att starta transkribering", "labelToolTip": "Mötet transkriberas", "off": "Transkribering avslutades", - "pending": "Förbereder transkribering av mötet ...", + "pending": "Förbereder transkribering av mötet …", "start": "Börja visa undertexter", "stop": "Sluta visa undertexter", "tr": "TR" @@ -656,7 +708,7 @@ "videoStatus": { "audioOnly": "AUD", "audioOnlyExpanded": "Du använder ljudläget. Läget sparar bandbredd men du kan inte se andras videor.", - "callQuality": "", + "callQuality": "Videokvalitet", "hd": "HD", "highDefinition": "High definition", "labelTooiltipNoVideo": "Ingen video", @@ -664,12 +716,14 @@ "ld": "LD", "lowDefinition": "Low definition", "onlyAudioAvailable": "Enbart ljud tillgängligt", - "onlyAudioSupported": "Vi stöder bara ljud i denna webbläsare", - "p2pEnabled": "Peer to peer används", - "p2pVideoQualityDescription": "", + "onlyAudioSupported": "Vi stöder bara ljud i denna webbläsare.", + "p2pEnabled": "Peer to peer-läget används", + "p2pVideoQualityDescription": "I peer to peer-läget kan mottagen videokvalitet bara växlas mellan hög kvalitet eller enbart ljud. Andra inställningar kommer inte att aktiveras förrän peer to peer-läget har avslutats.", "recHighDefinitionOnly": "Föredrar high definition.", "sd": "SD", - "standardDefinition": "Standard definition" + "standardDefinition": "Standard definition", + "ldTooltip": "", + "hdTooltip": "" }, "videothumbnail": { "domute": "Tysta", @@ -680,7 +734,7 @@ "muted": "Tystad", "remoteControl": "Fjärrkontroll", "show": "", - "videomute": "" + "videomute": "Deltagaren har stäng av kameran" }, "welcomepage": { "accessibilityLabel": { @@ -708,6 +762,23 @@ "roomnameHint": "Ange namnet eller URL:en till mötesrummet du vill ansluta till. Du kan hitta på ett nytt namn, berätta då för de andra du tänker möta så de anger samma namn.", "sendFeedback": "Ge återkoppling", "terms": "Termer", - "title": "Säkra, välutrustade och helt kostnadsfria videokonferenser" + "title": "Säkra, välutrustade och helt kostnadsfria videokonferenser", + "roomNameAllowedChars": "Mötesnamn kan inte innehålla dessa tecken: ?, &,:, ', \",%, #.", + "getHelp": "", + "goSmall": "BÖRJA" + }, + "defaultNickname": "till exempel Julia Eriksson", + "chromeExtensionBanner": { + "dontShowAgain": "Visa inte det här igen", + "buttonText": "Installera Chrome-utökningen", + "installExtensionText": "Installera utökning för Google Kalender och integrationen av Office 365" + }, + "documentSharing": { + "title": "" + }, + "raisedHand": "Jag vill prata", + "lonelyMeetingExperience": { + "youAreAlone": "Du är ensam i mötet", + "button": "Bjud in andra" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-tr.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-tr.json new file mode 100644 index 000000000..781f928ed --- /dev/null +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-tr.json @@ -0,0 +1,729 @@ +{ + "addPeople": { + "add": "Davet et", + "countryNotSupported": "Ülke henüz desteklenmiyor", + "countryReminder": "Dış bir ülkeyi mi arıyorsunuz? Lütfen ülke koduyla başlayın!", + "disabled": "Kişi davet edemezsiniz.", + "failedToAdd": "Kişi eklenemedi", + "footerText": "Dış arama devre dışı.", + "loading": "Kişiler ve telefon numaraları aranıyor..", + "loadingNumber": "Telefon numarası doğrulanıyor", + "loadingPeople": "Davet edilecek kişi aranıyor", + "noResults": "Eşleşen sonuç bulunamadı", + "noValidNumbers": "Lütfen bir telefon numarası girin", + "searchNumbers": "Telefon numarası ekle", + "searchPeople": "Kişi ara", + "searchPeopleAndNumbers": "Kişi arayın veya telefon numarası ekleyin", + "telephone": "Telefon numarası: {{number}}", + "title": "Bu toplantıya kişi davet edin" + }, + "audioDevices": { + "bluetooth": "Bluetooth", + "headphones": "Kulaklık", + "phone": "Telefon", + "speaker": "Konuşmacı", + "none": "Ses cihazı yok" + }, + "audioOnly": { + "audioOnly": "Düşük bant genişliği" + }, + "calendarSync": { + "addMeetingURL": "Toplantı bağlantısı ekle", + "confirmAddLink": "Bu etkinliğe bir toplantı bağlantısı eklensin mi?", + "error": { + "appConfiguration": "Takvim entegrasyonu doğru yapılandırılmadı.", + "generic": "Bir hata oluştu. Lütfen takvim ayarlarını kontrol edin veya takvimi yenilemeyi deneyin.", + "notSignedIn": "Takvim etkinlikleri getirilirken bir hata meydana geldi. Lütfen takvim ayarlarını kontrol edin ve tekrar giriş yapın." + }, + "join": "Katıl", + "joinTooltip": "Toplantıya katıl", + "nextMeeting": "sonraki toplantı", + "noEvents": "Planlanmış bir etkinlik bulunmuyor.", + "ongoingMeeting": "devam eden toplantı", + "permissionButton": "Ayarları aç", + "permissionMessage": "Uygulama içinde toplantılarınızı görüntülemek için takvim erişim izni gereklidir.", + "refresh": "Takvimi yenile", + "today": "Bugün" + }, + "chat": { + "error": "Hata: mesajınız gönderilmedi. Gerekçe: {{error}}", + "messagebox": "Bir mesaj yazın", + "nickname": { + "popover": "Bir takma ad seçin", + "title": "Sohbette kullanmak için bir takma ad girin" + }, + "title": "Sohbet", + "you": "sen", + "privateNotice": "{{recipient}} için özel mesaj", + "noMessagesMessage": "Toplantıda henüz mesaj yok. Buradan bir konuşma başlatın!", + "messageTo": "{{recipient}} için özel mesaj", + "fieldPlaceHolder": "Mesajınızı buraya yazın" + }, + "connectingOverlay": { + "joiningRoom": "Toplantınıza bağlanılıyor.." + }, + "connection": { + "ATTACHED": "Eklenmiş", + "AUTHENTICATING": "Kimlik doğrulanıyor", + "AUTHFAIL": "Kimlik doğrulama başarısız", + "CONNECTED": "Bağlandı", + "CONNECTING": "Bağlanıyor", + "CONNFAIL": "Bağlantı başarısız", + "DISCONNECTED": "Bağlantı kesildi", + "DISCONNECTING": "Bağlantı kesildi", + "ERROR": "Hata", + "RECONNECTING": "Bir bağlantı hatası oluştu. Tekrar bağlanıyor...", + "LOW_BANDWIDTH": "Bant genişliğinden tasarruf etmek için {{displayName}} kişisinin videosu kapatıldı", + "GOT_SESSION_ID": "Oturum kimliği alınıyor… Tamam", + "GET_SESSION_ID_ERROR": "Oturum kimliği alma hatası: {{code}}", + "FETCH_SESSION_ID": "Oturum kimliği alınıyor…" + }, + "connectionindicator": { + "address": "Adres:", + "bandwidth": "Tahmini bant genişliği:", + "bitrate": "Bit hızı:", + "bridgeCount": "Sunucu sayısı: ", + "connectedTo": "Bağlandı:", + "framerate": "Çerçeve hızı:", + "less": "Daha az göster", + "localaddress": "Yerel adres:", + "localaddress_plural": "Yerel adresler:", + "localport": "Yerel port:", + "localport_plural": "Yerel portlar:", + "more": "Daha fazla göster", + "packetloss": "Paket kaybı:", + "quality": { + "good": "İyi", + "inactive": "Aktif değil", + "lost": "Kayıp", + "nonoptimal": "Optimal seviyede değil", + "poor": "Zayıf" + }, + "remoteaddress": "Uzak adres:", + "remoteaddress_plural": "Uzak adresler:", + "remoteport": "Uzak port:", + "remoteport_plural": "Uzak portlar:", + "resolution": "Çözünürlük:", + "status": "Bağlantı:", + "transport": "Transport:", + "transport_plural": "Transportlar:", + "turn": " (turn)" + }, + "dateUtils": { + "earlier": "Daha eski", + "today": "Bugün", + "yesterday": "Dün" + }, + "deepLinking": { + "appNotInstalled": "Bu toplantıya katılmak için {{app}} uygulamasına ihtiyacınız var.", + "description": "Hiçbir şey olmadı mı? Toplantınızı {{app}} masaüstü uygulamasında başlatmaya çalıştık. Tekrar deneyin veya {{app}} web uygulamasını açın.", + "descriptionWithoutWeb": "", + "downloadApp": "Uygulamayı indir", + "launchWebButton": "Web'de aç", + "openApp": "Uygulamaya devam et", + "title": "Toplantınız {{app}} uygulamasında açılıyor...", + "tryAgainButton": "Masaüstünde tekrar deneyin" + }, + "defaultLink": "örneğin {{url}}", + "deviceError": { + "cameraError": "Kameraya erişilemedi", + "cameraPermission": "Kameraya erişim izni alınamadı", + "microphoneError": "Mikrofona erişilemedi", + "microphonePermission": "Mikrofon erişim izni alınamadı" + }, + "deviceSelection": { + "noPermission": "İzin alınamadı", + "previewUnavailable": "Önizleme mevcut değil", + "selectADevice": "Cihaz seç", + "testAudio": "Bir test sesi çal" + }, + "dialog": { + "accessibilityLabel": { + "liveStreaming": "Canlı akış" + }, + "allow": "İzin ver", + "alreadySharedVideoMsg": "Başka zaten bir video paylaşıyor. Bu toplantı aynı anda yalnızca bir paylaşılan videoya izin veriyor.", + "alreadySharedVideoTitle": "Yalnızca bir paylaşılan videoya izin veriliyor", + "applicationWindow": "Uygulama penceresi", + "Back": "Geri", + "cameraConstraintFailedError": "Kameranız gerekli bazı özellikleri karşılayamıyor.", + "cameraNotFoundError": "Kamera bulunamadı", + "cameraNotSendingData": "Kameranıza erişemiyoruz. Lütfen başka bir uygulamanın bu cihazı kullanıp kullanmadığını kontrol edin, Ayarlar menüsünden başka bir cihaz seçin veya uygulamayı yeniden yüklemeyi deneyin.", + "cameraNotSendingDataTitle": "Kameraya erişilemiyor", + "cameraPermissionDeniedError": "Kamera kullanımına izin vermediniz. Yine de toplantıya katılabilirsiniz, ancak diğerleri sizi göremez. Bunu düzeltmek için kamera butonunu kullanın.", + "cameraUnknownError": "Bilinmeyen bir nedenden dolayı kamera kullanılamıyor.", + "cameraUnsupportedResolutionError": "Kameranız gerekli video çözünürlüğünü desteklemiyor.", + "Cancel": "İptal", + "close": "Kapat", + "conferenceDisconnectMsg": "Ağ bağlantınızı kontrol etmek isteyebilirsiniz. {{seconds}} saniye içinde yeniden bağlanıyor ...", + "conferenceDisconnectTitle": "Bağlantınız kesildi.", + "conferenceReloadMsg": "Bunu düzeltmeye çalışıyoruz. {{seconds}} saniye içinde yeniden bağlanıyor ...", + "conferenceReloadTitle": "Ne yazık ki bir şeyler ters gitti.", + "confirm": "Onayla", + "confirmNo": "Hayır", + "confirmYes": "Evet", + "connectError": "Hata! Bir şeyler ters gitti ve toplantıya bağlanamadık.", + "connectErrorWithMsg": "Hata! Bir şeyler ters gitti ve konferansa bağlanamadık: {{msg}}", + "connecting": "Bağlanıyor", + "contactSupport": "Destek ekibine erişin", + "copy": "Kopyala", + "dismiss": "Son ver", + "displayNameRequired": "Görünür ad gerekli", + "done": "Bitti", + "enterDisplayName": "Lütfen bir görünür ad girin", + "error": "Hata", + "externalInstallationMsg": "Masaüstü paylaşım uzantımızı yüklemeniz gerekmektedir.", + "externalInstallationTitle": "Uzantı gerekli", + "goToStore": "Mağazaya git", + "gracefulShutdown": "Hizmetimiz şu anda bakım için devre dışı. Lütfen daha sonra tekrar deneyiniz.", + "IamHost": "Toplantı sahibiyim", + "incorrectRoomLockPassword": "", + "incorrectPassword": "Kullanıcı adı veya parola hatalı", + "inlineInstallationMsg": "Masaüstü paylaşım uzantımızı yüklemeniz gerekmektedir.", + "inlineInstallExtension": "Şimdi yükle", + "internalError": "Hata! Bir şeyler ters gitti. Şu hata oluştu: {{error}}", + "internalErrorTitle": "İç hata", + "kickMessage": "Ah! Toplantıdan çıkarıldınız!", + "kickParticipantButton": "Çıkar", + "kickParticipantDialog": "Bu katılımcıyı çıkarmak istediğinizden emin misiniz?", + "kickParticipantTitle": "Bu katılımcı çıkarılsın mı?", + "kickTitle": "Toplantıdan çıkarıldı", + "liveStreaming": "Canlı akış", + "liveStreamingDisabledForGuestTooltip": "Konuklar canlı akışa başlayamaz.", + "liveStreamingDisabledTooltip": "Canlı akışı başlatma devre dışı.", + "lockMessage": "Toplantı kilitlenemedi.", + "lockRoom": "Toplantı parolası ekle", + "lockTitle": "Kilitlenemedi", + "logoutQuestion": "Oturumu kapatmak ve toplantıyı durdurmak istediğinizden emin misiniz?", + "logoutTitle": "Oturumu kapat", + "maxUsersLimitReached": "Maksimum katılımcı sayısı sınırına ulaşıldı. Toplantı dolu. Lütfen toplantı sahibiyle iletişime geçin veya daha sonra tekrar deneyin!", + "maxUsersLimitReachedTitle": "Maksimum katılımcı sınırına ulaşıldı", + "micConstraintFailedError": "Mikrofonunuz gerekli özelliklerin bazılarını karşılayamıyor.", + "micNotFoundError": "Mikrofon bulunamadı.", + "micNotSendingData": "Mikrofonunuza erişemiyoruz. Lütfen Ayarlar menüsünden başka bir cihaz seçin veya uygulamayı yeniden yüklemeyi deneyin.", + "micNotSendingDataTitle": "Mikrofona erişilemiyor", + "micPermissionDeniedError": "Mikrofon kullanımına izin vermediniz. Yine de toplantıya katılabilirsiniz, ancak diğerleri sizi duyamaz. Bunu düzeltmek için mikrofon butonunu kullanın.", + "micUnknownError": "Bilinmeyen bir nedenden dolayı mikrofon kullanılamıyor.", + "muteParticipantBody": "Bunların sesini açamazsınız, ancak istedikleri zaman kendileri seslerini açabilirler.", + "muteParticipantButton": "Sustur", + "muteParticipantDialog": "Bu katılımcının sesini kapatmak istediğinizden emin misiniz? Bunların sesini açamazsınız, ancak istedikleri zaman kendileri seslerini açabilirler.", + "muteParticipantTitle": "Bu katılımcı susturulsun mu?", + "Ok": "Tamam", + "passwordLabel": "Parola", + "passwordNotSupported": "Toplantı parolası ayarlama desteklenmiyor.", + "passwordNotSupportedTitle": "Parola desteklenmiyor", + "passwordRequired": "Parola gerekli", + "popupError": "Tarayıcınız bu siteden açılan pencereleri engelliyor. Lütfen tarayıcınızın güvenlik ayarlarından açılır pencereleri etkinleştirin ve tekrar deneyin.", + "popupErrorTitle": "Açılır pencere engellendi", + "recording": "Kaydediliyor", + "recordingDisabledForGuestTooltip": "Misafirler kayıt etmeye başlayamaz.", + "recordingDisabledTooltip": "Kaydetmeye başlama devre dışı.", + "rejoinNow": "Tekrar katıl", + "remoteControlAllowedMessage": "{{user}} uzaktan kontrol isteğinizi kabul etti!", + "remoteControlDeniedMessage": "{{user}} uzaktan kontrol isteğinizi reddetti!", + "remoteControlErrorMessage": "{{user}} katılımcısından uzaktan kontrol izinleri istenmeye çalışılırken bir hata oluştu!", + "remoteControlRequestMessage": "{{user}} katılımcısının masaüstünüzü uzaktan kontrol etmesine izin veriyor musunuz?", + "remoteControlShareScreenWarning": "\"Allow\" butonuna bastığınızda ekranınızı paylaşacağınızı unutmayın!", + "remoteControlStopMessage": "Uzaktan kontrol oturumu sona erdi!", + "remoteControlTitle": "Uzak masaüstü kontrolü", + "Remove": "Kaldır", + "removePassword": "Şifreyi kaldır", + "removeSharedVideoMsg": "Paylaşılan videonuzu kaldırmak istediğinizden emin misiniz?", + "removeSharedVideoTitle": "Paylaşılan videoyu kaldır", + "reservationError": "Rezervasyon sistemi hatası", + "reservationErrorMsg": "Hata kodu: {{code}}, mesaj: {{msg}}", + "retry": "Yeniden Dene", + "screenSharingFailedToInstall": "Hata! Ekran paylaşım uzantınız yüklenemedi.", + "screenSharingFailedToInstallTitle": "Ekran paylaşım uzantısı yüklenemedi", + "screenSharingFirefoxPermissionDeniedError": "Ekranınızı paylaşmaya çalışırken bir şeyler ters gitti. Lütfen bize izin verdiğinizden emin olun.", + "screenSharingFirefoxPermissionDeniedTitle": "Hata! Ekran paylaşımına başlayamadık!", + "screenSharingPermissionDeniedError": "Hata! Ekran paylaşma uzantısı izinlerinizle ilgili bir sorun oluştu. Lütfen yeniden yükleyin ve tekrar deneyin.", + "serviceUnavailable": "Hizmet kullanılamıyor", + "sessTerminated": "Arama sonlandırıldı", + "Share": "Paylaş", + "shareVideoLinkError": "Lütfen doğru bir Youtube bağlantısı sağlayın.", + "shareVideoTitle": "Bir video paylaş", + "shareYourScreen": "Ekranınızı paylaşın", + "shareYourScreenDisabled": "Ekran paylaşımı devre dışı.", + "shareYourScreenDisabledForGuest": "Konuklar ekran paylaşımı yapamaz.", + "startLiveStreaming": "Canlı akışı başlat", + "startRecording": "Kaydı başlat", + "startRemoteControlErrorMessage": "Uzaktan kontrol oturumunu başlatmaya çalışırken bir hata oluştu!", + "stopLiveStreaming": "Canlı akışı durdur", + "stopRecording": "Kaydı durdur", + "stopRecordingWarning": "Kaydı durdurmak istediğinizden emin misiniz?", + "stopStreamingWarning": "Canlı akışı durdurmak istediğinizden emin misiniz?", + "streamKey": "Canlı akış anahtarı", + "Submit": "Gönder", + "thankYou": "{{appName}} kullandığınız için teşekkürler!", + "token": "token", + "tokenAuthFailed": "Üzgünüz, bu görüşmeye katılmanıza izin verilmiyor.", + "tokenAuthFailedTitle": "Kimlik doğrulama başarısız", + "transcribing": "Deşifre ediliyor", + "unlockRoom": "Toplantı parolasını kaldır", + "userPassword": "kullancı parolası", + "WaitForHostMsg": "<b>{{room}}</b> toplantısı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.", + "WaitForHostMsgWOk": "<b>{{room}}</b> toplantısı henüz başlamadı. Toplantı sahibi sizseniz, kimlik doğrulaması için Tamam butonuna basın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.", + "WaitingForHost": "Toplantı sahibi bekleniyor...", + "Yes": "Evet", + "yourEntireScreen": "Tüm ekranınız" + }, + "dialOut": { + "statusMessage": "şimdi {{status}}" + }, + "feedback": { + "average": "Orta", + "bad": "Kötü", + "detailsLabel": "Bize daha fazla bilgi verin.", + "good": "İyi", + "rateExperience": "Toplantı deneyiminizi derecelendirin", + "veryBad": "Çok kötü", + "veryGood": "Çok iyi" + }, + "incomingCall": { + "answer": "Cevapla", + "audioCallTitle": "Gelen sesli arama", + "decline": "Reddet", + "productLabel": "Jitsi Meet'den", + "videoCallTitle": "Gelen görüntülü arama" + }, + "info": { + "accessibilityLabel": "Bilgiyi göster", + "addPassword": "Şifre ekle", + "cancelPassword": "Şifreyi iptal et", + "conferenceURL": "Bağlantı:", + "country": "Ülke", + "dialANumber": "Toplantınıza katılmak için bu numaralardan birini çevirin ve ardından kodu girin.", + "dialInConferenceID": "KOD:", + "dialInNotSupported": "Maalesef arama şu anda desteklenmiyor.", + "dialInNumber": "Arama:", + "dialInSummaryError": "Arama bilgisi getirilirken hata oluştu. Lütfen daha sonra tekrar deneyin.", + "dialInTollFree": "Ücretsiz", + "genericError": "Ah! Bir şeyler ters gitti.", + "inviteLiveStream": "Bu toplantının canlı akışını görüntülemek için şu bağlantıyı tıklayın: {{url}}", + "invitePhone": "Tek dokunuşla sesli arama: {{number}},,{{conferenceID}}#", + "invitePhoneAlternatives": "", + "inviteURLFirstPartGeneral": "Bir toplantıya katılmaya davet edildiniz.", + "inviteURLFirstPartPersonal": "{{name}} sizi bir toplantıya davet ediyor.\n", + "inviteURLSecondPart": "\nToplantıya katıl:\n{{url}}\n", + "liveStreamURL": "Canlı akış:", + "moreNumbers": "Daha fazla numara", + "noNumbers": "Arama numarası yok", + "noPassword": "Yok", + "noRoom": "Aranacak oda belirtilmedi.", + "numbers": "Arama Numaraları", + "password": "Şifre:", + "title": "Paylaş", + "tooltip": "Bu toplantı için bağlantıyı ve arama bilgilerini paylaşın", + "label": "Toplantı bilgileri" + }, + "inviteDialog": { + "alertText": "Bazı katılımcılar davet edilemedi.", + "header": "Davet et", + "searchCallOnlyPlaceholder": "Telefon numarasını girin", + "searchPeopleOnlyPlaceholder": "Katılımcı ara", + "searchPlaceholder": "Katılımcı veya telefon numarası", + "send": "Gönder" + }, + "inlineDialogFailure": { + "msg": "Biraz tökezledik.", + "retry": "Tekrar dene", + "support": "Destek", + "supportMsg": "Bu olmaya devam ederse, ulaşın" + }, + "keyboardShortcuts": { + "focusLocal": "Videoma odaklan", + "focusRemote": "Başka bir kişinin videosuna odaklan", + "fullScreen": "Tam ekran görüntüle veya çık", + "keyboardShortcuts": "Klavye kısayolları", + "localRecording": "Kayıt denetimlerini göster veya gizle", + "mute": "Mikrofonu aç veya kapat", + "pushToTalk": "Konuşmak için bas", + "raiseHand": "Elinizi kaldırın veya indirin", + "showSpeakerStats": "Konuşmacı istatistiklerini göster", + "toggleChat": "Mesajlaşmayı aç veya kapat", + "toggleFilmstrip": "Video önizlemelerini göster veya gizle", + "toggleScreensharing": "Kamera ve ekran paylaşımı arasında geçiş yap", + "toggleShortcuts": "Klavye kısayollarını göster veya gizle", + "videoMute": "Kamerayı aç veya kapat" + }, + "liveStreaming": { + "busy": "Akış kaynaklarını serbest bırakmaya çalışıyoruz. Lütfen birkaç dakika içinde tekrar deneyin.", + "busyTitle": "Tüm yayıncılar şu anda meşgul", + "changeSignIn": "Hesap değiştir.", + "choose": "Canlı bir akış seçin", + "chooseCTA": "Bir akış seçeneği belirleyin. Şu anda {{email}} olarak giriş yaptınız.", + "enterStreamKey": "Youtube canlı akış anahtarınızı buraya girin.", + "error": "Canlı Akış başarısız oldu. Lütfen tekrar deneyin.", + "errorAPI": "Youtube yayınlarınıza erişirken bir hata oluştu. Lütfen tekrar giriş yapmayı deneyin.", + "errorLiveStreamNotEnabled": "{{email}} için Canlı Akış etkin değil. Lütfen canlı akışı etkinleştirin veya canlı akışın etkin olduğu bir hesaba giriş yapın.", + "expandedOff": "Canlı akış durdu", + "expandedOn": "Toplantı şu anda Youtube'da yayınlanıyor.", + "expandedPending": "Canlı akış başlatılıyor...", + "failedToStart": "Canlı Akış başlatılamadı", + "getStreamKeyManually": "Canlı akış alınamadı. Canlı akış anahtarınızı Youtube'dan almayı deneyin.", + "invalidStreamKey": "Canlı akış anahtarı yanlış olabilir.", + "off": "Canlı Akış durduruldu", + "on": "Canlı Akış", + "pending": "Canlı Akış başlatılıyor...", + "serviceName": "Canlı Akış hizmeti", + "signedInAs": "Şu anda oturum açmış durumdasınız:", + "signIn": "Google ile giriş yap", + "signInCTA": "Oturum açın veya Youtube'dan canlı akış anahtarınızı girin.", + "signOut": "Çıkış yap", + "start": "Bir canlı akış başlat", + "streamIdHelp": "Bu nedir?", + "unavailableTitle": "Canlı Akış kullanılamıyor" + }, + "localRecording": { + "clientState": { + "off": "Kapalı", + "on": "Açık", + "unknown": "Bilinmiyor" + }, + "dialogTitle": "Kayıt Kontrolleri", + "duration": "Süre", + "durationNA": "Kullanılamaz", + "encoding": "Kodlama", + "label": "KK", + "labelToolTip": "Kayıt meşgul", + "localRecording": "Kayıt", + "me": "Ben", + "messages": { + "engaged": "Kayıt meşgul", + "finished": "{{token}} kayıt oturumu tamamlandı. Lütfen kaydedilen dosyayı yöneticiye gönderin.", + "finishedModerator": "{{token}} kayıt oturumu tamamlandı. Parça kaydedildi. Lütfen diğer katılımcılardan kayıtlarını göndermelerini isteyin.", + "notModerator": "Yönetici değilsiniz. Kaydı başlatamaz veya durduramazsınız." + }, + "moderator": "Yönetici", + "no": "Hayır", + "participant": "Katılımcı", + "participantStats": "Katılımcı İstatistikleri", + "sessionToken": "Oturum Tokeni", + "start": "Kaydı başlat", + "stop": "Kaydı durdur", + "yes": "Evet" + }, + "lockRoomPassword": "parola", + "lockRoomPasswordUppercase": "Parola", + "me": "ben", + "notify": { + "connectedOneMember": "{{name}} toplantıya katıldı", + "connectedThreePlusMembers": "{{name}} ve {{count}} kişi daha toplantıya katıldı", + "connectedTwoMembers": "{{first}} ve {{second}} toplantıya katıldı", + "disconnected": "bağlantı kesildi", + "focus": "Toplantı odağı", + "focusFail": "{{component}} uygun değil - {{ms}} saniye içinde tekrar deneyin", + "grantedTo": "{{to}} kişisine yönetici hakları verildi!", + "invitedOneMember": "{{name}} davet edildi", + "invitedThreePlusMembers": "{{name}} ve {{count}} kişi daha davet edildi", + "invitedTwoMembers": "{{first}} ve {{second}} davet edildi", + "kickParticipant": "{{kicked}} kişisi {{kicker}} tarafından çıkarıldı", + "me": "Ben", + "moderator": "Yönetici hakları alındı!", + "muted": "Görüşmeye sesiniz kapalı olarak başladınız.", + "mutedTitle": "Sesiniz kapalı!", + "mutedRemotelyTitle": "{{participantDisplayName}} tarafından sessize alındınız!", + "mutedRemotelyDescription": "", + "passwordRemovedRemotely": "", + "passwordSetRemotely": "", + "raisedHand": "{{name}} konuşmak istiyor.", + "somebody": "Birisi", + "startSilentTitle": "", + "startSilentDescription": "", + "suboptimalExperienceDescription": "Mmm... {{appName}} ile olan deneyiminizin burada çok iyi olmayacağından korkuyoruz. Bunu iyileştirmenin yollarını arıyoruz, ancak o zamana kadar lütfen şunlardan birini deneyin: <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>.", + "suboptimalExperienceTitle": "Tarayıcı Uyarısı", + "unmute": "", + "newDeviceCameraTitle": "Yeni kamera algılandı", + "newDeviceAudioTitle": "Yeni ses aygıtı algılandı", + "newDeviceAction": "Kullan" + }, + "passwordSetRemotely": "başka katılımcı tarafından ayarlandı", + "passwordDigitsOnly": "{{number}} rakama kadar", + "poweredby": "gücünün kaynağı", + "presenceStatus": { + "busy": "Meşgul", + "calling": "Arıyor...", + "connected": "Bağlandı", + "connecting": "Bağlanıyor...", + "connecting2": "Bağlanıyor*...", + "disconnected": "Bağlantı kesildi", + "expired": "Süresi doldu", + "ignored": "Yok sayıldı", + "initializingCall": "Arama başlatılıyor...", + "invited": "Davet edildi", + "rejected": "Reddedildi", + "ringing": "Çalıyor..." + }, + "profile": { + "setDisplayNameLabel": "Görünür adınızı ayarlayın", + "setEmailInput": "E-posta adresinizi girin", + "setEmailLabel": "Gravatar e-postanızı ayarlayın", + "title": "Profil" + }, + "recording": { + "authDropboxText": "Dropbox'a yükle", + "availableSpace": "Kullanılabilir alan: {{spaceLeft}} MB (yaklaşık {{duration}} dakika kayıt)", + "beta": "BETA", + "busy": "Kayıt kaynaklarını boşaltmaya çalışıyoruz. Lütfen birkaç dakika içinde tekrar deneyin.", + "busyTitle": "Tüm kayıt cihazları şu anda meşgul", + "error": "Kayıt başarısız oldu. Lütfen tekrar deneyin.", + "expandedOff": "Kayıt durdu", + "expandedOn": "Toplantı şu anda kaydediliyor.", + "expandedPending": "Kayıt başlatılıyor ...", + "failedToStart": "Kayıt başlatılamadı", + "fileSharingdescription": "Toplantı katılımcılarıyla kaydı paylaş", + "live": "CANLI", + "loggedIn": "{{userName}} olarak giriş yapıldı", + "off": "Kayıt durdu", + "on": "Kaydediliyor", + "pending": "Toplantıyı kaydetmeye hazırlanıyor ...", + "rec": "KAYIT", + "serviceDescription": "Kaydınız kayıt hizmeti tarafından kaydedilecektir", + "serviceName": "Kayıt hizmeti", + "signIn": "Giriş yap", + "signOut": "Çıkış yap", + "unavailable": "Ah! {{serviceName}} şu anda kullanılamıyor. Sorunu çözmek için çalışıyoruz. Lütfen daha sonra tekrar deneyin.", + "unavailableTitle": "Kayıt yapılamıyor" + }, + "sectionList": { + "pullToRefresh": "Yenilemek için çekin" + }, + "settings": { + "calendar": { + "about": "{{appName}} takvim entegrasyonu, yaklaşan etkinlikleri okuyabilmesi için takviminize güvenli bir şekilde erişmek için kullanılır.", + "disconnect": "Bağlantıyı Kes", + "microsoftSignIn": "Microsoft ile oturum aç", + "signedIn": "Şu anda {{email}} için takvim etkinliklerine erişiliyor. Takvim etkinliklerine erişmeyi durdurmak için aşağıdaki Bağlantıyı Kes butonuna tıklayın.", + "title": "Takvim" + }, + "devices": "Cihazlar", + "followMe": "Beni takip edenler", + "language": "Dil", + "loggedIn": "{{name}} olarak giriş yapıldı", + "moderator": "Yönetici", + "more": "Daha fazla", + "name": "Ad", + "noDevice": "Yok", + "selectAudioOutput": "Ses çıkışı", + "selectCamera": "Kamera", + "selectMic": "Mikrofon", + "startAudioMuted": "Herkes ses kapalı başlasın", + "startVideoMuted": "Herkes görüntü kapalı başlasın", + "title": "Ayarlar" + }, + "settingsView": { + "alertOk": "Tamam", + "alertTitle": "Uyarı", + "alertURLText": "Girilen sunucu bağlantısı geçersiz", + "buildInfoSection": "Yapı Bilgisi", + "conferenceSection": "Toplantı", + "displayName": "Görünür ad", + "email": "E-posta", + "header": "Ayarlar", + "profileSection": "Profil", + "serverURL": "Sunucu Bağlantısı", + "startWithAudioMuted": "Ses kapalı başla", + "startWithVideoMuted": "Görüntü kapalı başla", + "version": "Versiyon" + }, + "share": { + "dialInfoText": "\n\n=====\n\nTelefonunuzdan mı aramak istiyorsunuz?\n\n{{defaultDialInNumber}}Bu toplantının telefon numaralarını aramak için bu bağlantıyı tıklayın\n{{dialInfoPageUrl}}", + "mainText": "Toplantıya katılmak için aşağıdaki bağlantıyı tıklayın:\n{{roomUrl}}" + }, + "speaker": "Konuşmacı", + "speakerStats": { + "hours": "{{count}}sa", + "minutes": "{{count}}dk", + "name": "Ad", + "seconds": "{{count}}sn", + "speakerStats": "Konuşmacı İstatistikleri", + "speakerTime": "Konuşmacı Süresi" + }, + "startupoverlay": { + "policyText": " ", + "title": "{{app}} kameranızı ve mikrofonunuzu kullanmaya ihtiyaç duyuyor." + }, + "suspendedoverlay": { + "rejoinKeyTitle": "Tekrar katıl", + "text": "Tekrar bağlanmak için <i>Tekrar katıl</i> butonuna basın.", + "title": "Bu bilgisayar uyku moduna geçtiği için görüntülü görüşmeniz kesildi." + }, + "toolbar": { + "accessibilityLabel": { + "audioOnly": "Yalnızca sesi aç/kapat", + "audioRoute": "Ses aygıtını seçin", + "callQuality": "Arama kalitesini yönetin", + "cc": "Altyazıları aç/kapat", + "chat": "Mesajlaşma penceresini aç/kapat", + "document": "Paylaşılan dokümanı aç/kapat", + "feedback": "Geri bildirim bırakın", + "fullScreen": "Tam ekranı aç/kapat", + "hangup": "Aramadan ayrıl", + "invite": "İnsanları davet et", + "kick": "Katılımcı çıkar", + "localRecording": "Kayıt denetimlerini aç/kapat", + "lockRoom": "Toplantı parolasını aç/kapat", + "moreActions": "Diğer işlemler menüsünü aç/kapat", + "moreActionsMenu": "Diğer işlemler menüsü", + "mute": "Sesi aç/kapat", + "pip": "Resim içinde Resim modunu aç/kapat", + "profile": "Profilinizi düzenleyin", + "raiseHand": "El kaldırmayı aç/kapat", + "recording": "Kaydetmeyi aç/kapat", + "remoteMute": "Katılımcının sesini kapat", + "Settings": "Ayarları aç/kapat", + "sharedvideo": "Youtube video paylaşmayı aç/kapat", + "shareRoom": "Birini davet et", + "shareYourScreen": "Ekran paylaşımını aç/kapat", + "shortcuts": "Kısayolları aç/kapat", + "show": "", + "speakerStats": "Konuşmacı istatistiklerini aç/kapat", + "tileView": "Döşeme görünümünü aç/kapat", + "toggleCamera": "Kamerayı aç/kapat", + "videomute": "Sessiz videoyu aç/kapat", + "videoblur": "" + }, + "addPeople": "Aramanıza kişi ekleyin", + "audioOnlyOff": "Yalnızca ses modunu devre dışı bırak", + "audioOnlyOn": "Yalnızca ses modunu etkinleştir", + "audioRoute": "Ses aygıtını seçin", + "authenticate": "Kimlik doğrula", + "callQuality": "Arama kalitesini yönetin", + "chat": "Mesajlaşmayı aç/kapat", + "closeChat": "Mesajlaşmayı kapat", + "documentClose": "Paylaşılan dokümanı kapat", + "documentOpen": "Paylaşılan dokümanı aç", + "enterFullScreen": "Tam ekran görüntüle", + "enterTileView": "Döşeme görünümüne geç", + "exitFullScreen": "Tam ekrandan çık", + "exitTileView": "Döşeme görünümünden çık", + "feedback": "Geri bildirim bırakın", + "hangup": "Ayrıl", + "invite": "Kişi davet et", + "login": "Oturum aç", + "logout": "Oturum kapat", + "lowerYourHand": "Elinizi indirin", + "moreActions": "Daha fazla işlem", + "mute": "Sessiz / Sesli", + "openChat": "Mesajlaşmayı aç", + "pip": "Resim içinde Resim moduna gir", + "profile": "Profilinizi düzenleyin", + "raiseHand": "Elinizi kaldırın/indirin", + "raiseYourHand": "Elinizi kaldırın", + "Settings": "Ayarlar", + "sharedvideo": "Bir Youtube videosu paylaş", + "shareRoom": "Birini davet et", + "shortcuts": "Kısayolları göster", + "speakerStats": "Konuşmacı istatistikleri", + "startScreenSharing": "Ekran paylaşımını başlat", + "startSubtitles": "Altyazıları başlat", + "stopScreenSharing": "Ekran paylaşımını durdur", + "stopSubtitles": "Altyazıları durdur", + "stopSharedVideo": "Youtube videosunu durdur", + "talkWhileMutedPopup": "Bir şey mi dediniz? Mikrofonunuz kapalı.", + "tileViewToggle": "Döşeme görünümünü aç/kapat", + "toggleCamera": "Kamerayı aç/kapat", + "videomute": "Kamera başlat / durdur", + "startvideoblur": "", + "stopvideoblur": "" + }, + "transcribing": { + "ccButtonTooltip": "Altyazılıar başlat / durdur", + "error": "Deşifre etme başarısız oldu. Lütfen tekrar deneyin.", + "expandedLabel": "Deşifre etme açık", + "failedToStart": "Deşifre etme başlatılamadı", + "labelToolTip": "Toplantı deşifre ediliyor", + "off": "Deşifre etme durdu", + "pending": "Toplantıyı deşifre etmeye hazırlanıyor...", + "start": "Altyazıları göstermeye başla", + "stop": "Altyazıları göstermeyi durdur", + "tr": "TR" + }, + "userMedia": { + "androidGrantPermissions": "Tarayıcınız izin istediğinde <b><i>İzin Ver</i></b> seçeneğini seçin.", + "chromeGrantPermissions": "Tarayıcınız izin istediğinde <b><i>İzin Ver</i></b> seçeneğini seçin.", + "edgeGrantPermissions": "Tarayıcınız izin istediğinde <b><i>Evet</i></b> seçeneğini seçin.", + "electronGrantPermissions": "Lütfen kameranızı ve mikrofonunuzu kullanmak için izin verin", + "firefoxGrantPermissions": "Tarayıcınız izin istediğinde <b><i>Seçilen Aygıtı Paylaş</i></b> seçeneğini seçin.", + "iexplorerGrantPermissions": "Tarayıcınız izin istediğinde <b><i>Tamam</i></b> seçeneğini seçin.", + "nwjsGrantPermissions": "Lütfen kameranızı ve mikrofonunuzu kullanmak için izin verin", + "operaGrantPermissions": "Tarayıcınız izin istediğinde <b><i>İzin Ver</i></b> seçeneğini seçin.", + "react-nativeGrantPermissions": "Tarayıcınız izin istediğinde <b><i>İzin Ver</i></b> seçeneğini seçin.", + "safariGrantPermissions": "Tarayıcınız izin istediğinde <b><i>Tamam</i></b> seçeneğini seçin." + }, + "videoSIPGW": { + "busy": "Kaynakları serbest bırakmaya çalışıyoruz. Lütfen birkaç dakika içinde tekrar deneyin.", + "busyTitle": "Oda hizmeti şu anda meşgul", + "errorAlreadyInvited": "{{displayName}} zaten davet edildi", + "errorInvite": "Toplantı henüz oluşturulmadı. Lütfen daha sonra tekrar deneyin.", + "errorInviteFailed": "Sorunu çözmek için çalışıyoruz. Lütfen daha sonra tekrar deneyin.", + "errorInviteFailedTitle": "{{displayName}} davet edilemedi", + "errorInviteTitle": "Odaya davet edilirken hata oluştu", + "pending": "{{displayName}} davet edildi" + }, + "videoStatus": { + "audioOnly": "SES", + "audioOnlyExpanded": "Yalnızca ses modundasınız. Bu mod bant genişliğinden tasarruf sağlar, ancak başkalarının videolarını göremezsiniz.", + "callQuality": "Arama Kalitesi", + "hd": "HD", + "highDefinition": "Yüksek çözünürlük", + "labelTooiltipNoVideo": "Görüntü yok", + "labelTooltipAudioOnly": "Yalnızca ses modu etkin", + "ld": "LD", + "lowDefinition": "Düşük çözünürlük", + "onlyAudioAvailable": "Yalnızca ses kullanılabilir", + "onlyAudioSupported": "Bu tarayıcıda yalnızca sesi destekliyoruz.", + "p2pEnabled": "Peer to peer Etkin", + "p2pVideoQualityDescription": "Peer to peer modunda, arama kalitesi yalnızca yüksek çözünürlük ve yalnızca ses arasında değiştirilebilir. Peer to peer modundan çıkılmadıkça diğer ayarlar kabul edilmez.", + "recHighDefinitionOnly": "Yalnızca yüksek çözünürlüğü tercih et.", + "sd": "SD", + "standardDefinition": "Standart çözünürlük" + }, + "videothumbnail": { + "domute": "Sustur", + "flip": "Döndür", + "kick": "Çıkarıldı", + "moderator": "Yönetici", + "mute": "Katılımcı sessiz", + "muted": "Sessiz", + "remoteControl": "Uzaktan kontrol", + "show": "", + "videomute": "Katılımcı kamerayı durdurdu" + }, + "welcomepage": { + "accessibilityLabel": { + "join": "Katılmak için dokunun", + "roomname": "Oda adı girin" + }, + "appDescription": "Durma ve tüm ekiple görüntülü sohbet et. Hatta tanıdığın herkesi davet et. {{app}} tüm gün, her gün ücretsiz olarak kullanabileceğiniz, hesap gerektirmeden kullanbilieceğiniz tamamen şifrelenmiş, % 100 özgür bir video konferans çözümüdür.", + "audioVideoSwitch": { + "audio": "Ses", + "video": "Görüntü" + }, + "calendar": "Takvim", + "connectCalendarButton": "Takviminizi bağlayın", + "connectCalendarText": "", + "enterRoomTitle": "Yeni bir toplantı başlat", + "go": "GİT", + "join": "KATIL", + "info": "Bilgi", + "privacy": "Gizlilik", + "recentList": "En son", + "recentListDelete": "Sil", + "recentListEmpty": "En son görüşülenler listeniz şu anda boş. Sohbet edin ve son toplantılarınızı burada görüntüleyin.", + "reducedUIText": "", + "roomname": "Oda adı girin", + "roomnameHint": "Katılmak istediğiniz odanın adını veya bağlantısını girin. İstediğiniz oda adını uydurabilirsiniz. Aynı odada buluşmak için görüşmek istediğiniz kişilere bunu iletmeniz yeterli.", + "sendFeedback": "Geri bildirim gönder", + "terms": "Kurallar", + "title": "Güvenli, tüm özelliklere erişimli ve tamamen ücretsiz görüntülü arama", + "getHelp": "Yardım alın" + }, + "defaultNickname": "örnek Jane Pink", + "chromeExtensionBanner": { + "dontShowAgain": "Bunu bir daha gösterme", + "buttonText": "Chrome Eklentisi'ni indirin", + "installExtensionText": "Google Takvim ve Office 365 entegrasyonu için uzantıyı yükleyin" + } +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-vi.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-vi.json index 17918ce46..dac4b5984 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-vi.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-vi.json @@ -54,7 +54,7 @@ "title": "Chat" }, "connectingOverlay": { - "joiningRoom": "Đang kết nối tới cuộc họp của bạn..." + "joiningRoom": "Đang kết nối tới cuộc họp của bạn…" }, "connection": { "ATTACHED": "Đã đính kèm", @@ -111,7 +111,7 @@ "downloadApp": "Tải phần mềm", "launchWebButton": "Chạy trên web", "openApp": "Tiếp tục ứng dụng này", - "title": "Thực hiện cuộc họp trên {{app}}...", + "title": "Thực hiện cuộc họp trên {{app}}…", "tryAgainButton": "Thử lại trên desktop" }, "defaultLink": "ví dụ: {{url}}", @@ -145,9 +145,9 @@ "cameraUnsupportedResolutionError": "Camera của bạn không hỗ trợ độ phân giải video yêu cầu.", "Cancel": "Hủy", "close": "Đóng", - "conferenceDisconnectMsg": "Bạn có thể muốn kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây...", + "conferenceDisconnectMsg": "Bạn có thể muốn kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây…", "conferenceDisconnectTitle": "Bạn đã bị ngắt kết nối.", - "conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây...", + "conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây…", "conferenceReloadTitle": "Thật không may, có điều gì đó đã sai.", "confirm": "Xác nhận", "confirmNo": "Không", @@ -254,7 +254,7 @@ "userPassword": "mật khẩu người dùng", "WaitForHostMsg": "Cuộc họp <b>{{room}}</b> chưa được khởi tạo. Nếu bạn là chủ nghị vui lòng xác thực. Nếu không, vui lòng đợi chủ nghị.", "WaitForHostMsgWOk": "Cuộc họp <b>{{room}}</b> chưa được khởi tạo. Nếu bạn là chủ nghị vui lòng nhấn OK để xác thực. Nếu không, vui lòng đợi chủ nghị.", - "WaitingForHost": "Đang đợi chủ nghị ...", + "WaitingForHost": "Đang đợi chủ nghị …", "Yes": "Có", "yourEntireScreen": "Toàn bộ màn hình của bạn" }, @@ -290,7 +290,7 @@ "dialInSummaryError": "Lỗi nạp thông tin quay số. Vui lòng thử lại.", "dialInTollFree": "Miễn phí", "genericError": "Chà, có gì đó không ổn.", - "inviteLiveStream": "Để xem phát trực tuyến cuộc họp này, chọn liên kết: {{url}}", + "inviteLiveStream": "Để xem phát trực tuyến cuộc họp này, chọn liên kết: {{url}}", "invitePhone": "", "invitePhoneAlternatives": "", "inviteURLFirstPartGeneral": "Bạn được mời tham gia một cuộc họp.", @@ -346,16 +346,16 @@ "enterStreamKey": "Nhập key trực tuyến Youtube của bạn.", "error": "Phát trực tuyến thất bại. Xin vui lòng thử lại.", "errorAPI": "Lỗi xảy ra khi truy cập phát sóng Youtube của bạn. Vui lòng truy cập lại.", - "errorLiveStreamNotEnabled": "Phát trực tuyến không được bật với email {{email}}. Vui lòng bật phát trực tuyến hoặc truy cập một tài khoản đã bật phát trực tuyến.", + "errorLiveStreamNotEnabled": "Phát trực tuyến không được bật với email {{email}}. Vui lòng bật phát trực tuyến hoặc truy cập một tài khoản đã bật phát trực tuyến.", "expandedOff": "Phát trực tuyến đã dừng", "expandedOn": "Cuộc họp đang được phát trên Youtube.", - "expandedPending": "Phát trực tuyến đang bắt đầu...", + "expandedPending": "Phát trực tuyến đang bắt đầu…", "failedToStart": "Không thể bắt đầu phát trực tuyến", "getStreamKeyManually": "Không thể thu nhận phát trực tuyến nào. Thử lấy mã phát trực tuyến từ Youtube.", "invalidStreamKey": "Mã phát trực tuyến có thể sai.", "off": "Phát trực tuyến đã dừng", "on": "Phát trực tuyến", - "pending": "Đang bắt đầu phát trực tuyến...", + "pending": "Đang bắt đầu phát trực tuyến…", "serviceName": "Dịch vụ Phát trực tuyến", "signedInAs": "Bạn đang đăng nhập theo:", "signIn": "Đăng nhập với Google", @@ -398,7 +398,7 @@ "lockRoomPasswordUppercase": "Mật khẩu", "me": "Tôi", "notify": { - "connectedOneMember": "{{name}} đã tham gia cuộc họp", + "connectedOneMember": "{{name}} đã tham gia cuộc họp", "connectedThreePlusMembers": "{{name}} và{{count}} khác đã tham gia cuộc họp", "connectedTwoMembers": "{{first}} và{{second}} đã tham gia cuộc họp", "disconnected": "đã ngắt kết nối", @@ -433,22 +433,22 @@ "poweredby": "Được hỗ trợ bởi", "presenceStatus": { "busy": "Bận", - "calling": "Đang gọi...", + "calling": "Đang gọi…", "connected": "Đã kết nối", - "connecting": "Đang kết nối...", + "connecting": "Đang kết nối…", "connecting2": "Đang kết nối*...", "disconnected": "Đã ngắt kết nối", "expired": "Hết hạn", "ignored": "Đã bỏ qua", - "initializingCall": "Đang tạo cuộc gọi...", + "initializingCall": "Đang tạo cuộc gọi…", "invited": "Đã mời", "rejected": "Đã từ chối", - "ringing": "Đang đổ chuông..." + "ringing": "Đang đổ chuông…" }, "profile": { "setDisplayNameLabel": "Nhập tên hiển thị của bạn", "setEmailInput": "Nhập địa chỉ e-mail", - "setEmailLabel": "Nhập địa chỉ gravatar email của bạn", + "setEmailLabel": "Nhập địa chỉ Gravatar thư điện tử của bạn", "title": "Hồ sơ" }, "recording": { @@ -460,14 +460,14 @@ "error": "Ghi âm không thành công. Vui lòng thử lại.", "expandedOff": "Ghi hình đã dừng", "expandedOn": "Cuộc họp đang được ghi hình.", - "expandedPending": "Ghi hình đang khởi động...", + "expandedPending": "Ghi hình đang khởi động…", "failedToStart": "Khởi động ghi âm thất bại", "fileSharingdescription": "Chia sẻ ghi hình với người tham gia họp", "live": "Trực tuyến", "loggedIn": "Đã đăng nhập dưới tên {{userName}}", "off": "Đã ngừng ghi âm", "on": "Đang ghi âm", - "pending": "Đang chuẩn bị để ghi hình cuộc họp...", + "pending": "Đang chuẩn bị để ghi hình cuộc họp…", "rec": "REC", "serviceDescription": "Ghi hình của bạn sẽ được lưu bởi dịch vụ ghi hình", "serviceName": "Dịch vụ ghi hình", @@ -563,7 +563,7 @@ "recording": "Mở/Đóng Ghi hình", "remoteMute": "Tắt tiếng người tham gia", "Settings": "Mở/Đóng Cấu hình", - "sharedvideo": "Mở/Đóng Chia sẻ Youtube", + "sharedvideo": "Mở/Đóng Chia sẻ YouTube", "shareRoom": "Mời ai đó", "shareYourScreen": "Mở/Đóng Chia sẻ màn hình", "shortcuts": "Mở/Đóng Phím tắt", @@ -625,7 +625,7 @@ "failedToStart": "Khởi chạy phiên âm thất bại", "labelToolTip": "Cuộc họp đang được phiên âm", "off": "Phiên âm đã dừng", - "pending": "Đang chuẩn bị phiên âm cuộc họp...", + "pending": "Đang chuẩn bị phiên âm cuộc họp…", "start": "Bắt đầu hiển thị phụ đề", "stop": "Dừng hiển thị phụ đề", "tr": "TR" @@ -650,7 +650,7 @@ "errorInviteFailed": "Chúng tôi đang xử lý vấn đề. Vui lòng thử lại sau.", "errorInviteFailedTitle": "Mời {{displayName}} thất bại", "errorInviteTitle": "Lỗi mời họp", - "pending": "{{displayName}} đã được mời" + "pending": "{{displayName}} đã được mời" }, "videoStatus": { "audioOnly": "AUD", @@ -709,4 +709,4 @@ "terms": "Điều kiện", "title": "Bảo mật, đầy đủ tính năng và miễn phí hoàn toàn" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhCN.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhCN.json index a35bde1a5..dffbd0751 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhCN.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhCN.json @@ -1,66 +1,77 @@ { "addPeople": { - "add": "邀请", - "countryNotSupported": "目的国家暂时未被支持。", + "add": "添加", + "countryNotSupported": "当前国家暂时未被支持。", "countryReminder": "尝试在美国之外通话?请检查国家代码!", - "disabled": "您不能邀请成员", - "failedToAdd": "", - "footerText": "禁止拨号。", - "loading": "查找联系人或者电话号码", - "loadingNumber": "验证电话号码", - "loadingPeople": "正在搜索需要邀请的成员", - "noResults": "没有符合要求的搜索结果", - "noValidNumbers": "请输入一个电话号码", - "searchNumbers": "新增电话号码", - "searchPeople": "搜索成员", + "disabled": "关闭", + "failedToAdd": "添加失败", + "footerText": "底部文本", + "loading": "正在加载", + "loadingNumber": "正在加载号码", + "loadingPeople": "正在加载会议人员", + "noResults": "没有找到查询结果", + "noValidNumbers": "无效号码", + "searchNumbers": "查询号码", + "searchPeople": "查找人员", "searchPeopleAndNumbers": "搜索成员或添加其电话号码", "telephone": "电话号码: {{number}}", - "title": "邀请成员与会" + "title": "会议标题" }, "audioDevices": { "bluetooth": "蓝牙", "headphones": "耳机", "phone": "电话", - "speaker": "发言人" + "speaker": "发言人", + "none": "没有可用的音频设备" }, "audioOnly": { - "audioOnly": "只有音频" + "audioOnly": "仅语音支持" }, "calendarSync": { "addMeetingURL": "添加会议链接", "confirmAddLink": "是否在此事件中添加Jitsi链接", "error": { - "appConfiguration": "日历集成配置不正确。", + "appConfiguration": "配置错误", "generic": "发生错误。请检查日历设置或尝试刷新日历。", "notSignedIn": "查看日历事件验证出错。请检查日历设置,然后再次尝试登录。" }, "join": "加入", "joinTooltip": "加入会议", - "nextMeeting": "近期无活动。", + "nextMeeting": "下一场会议", "noEvents": "近期无活动。", "ongoingMeeting": "正在进行的会议", - "permissionButton": "打开设置", + "permissionButton": "设置", "permissionMessage": "在应用中查看会议需要日历权限。", "refresh": "刷新日历", "today": "今日" }, "chat": { - "error": "错误:你的消息 \"{{originalText}}\" 未被发送。原因: {{error}}", + "error": "错误:消息未发送。原因:{{error}}", + "fieldPlaceHolder": "在这里输入你的信息", "messagebox": "输入消息", + "messageTo": "与 {{recipient}} 的私人聊天", + "noMessagesMessage": "会议中还没有消息,在这里开始谈话吧!", "nickname": { "popover": "选择一个昵称", "title": "输入一个昵称用于聊天" }, - "title": "聊天" + "privateNotice": "与 {{recipient}} 的私人聊天", + "title": "话题", + "you": "您" + }, + "chromeExtensionBanner": { + "installExtensionText": "", + "buttonText": "", + "dontShowAgain": "" }, "connectingOverlay": { - "joiningRoom": "会议连接中..." + "joiningRoom": "会议连接中…" }, "connection": { "ATTACHED": "已接入", "AUTHENTICATING": "认证中", "AUTHFAIL": "认证失败", - "CONNECTED": "连接中...", + "CONNECTED": "连接中…", "CONNECTING": "连接中", "CONNFAIL": "连接失败", "DISCONNECTED": "已断开连接", @@ -91,8 +102,7 @@ "remoteport": "远程端口:", "resolution": "分辨率:", "status": "连接:", - "transport": "传输:", - "turn": "路由" + "transport": "传输:" }, "dateUtils": { "earlier": "更早的", @@ -102,14 +112,15 @@ "deepLinking": { "appNotInstalled": "您需要在手机上安装 {{app}} 这个应用才能参加会议。", "description": "无响应?正在尝试启动桌面{{app}}召开会议。重试或启动网页版{{app}}召开会议。", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "无响应?已尝试启动客户端{{app}}召开会议。", "downloadApp": "下载应用", "launchWebButton": "在网页中启动", - "openApp": "继续打开应用", - "title": "在 {{app}}中登录会议...", + "openApp": "继续", + "title": "在 {{app}}中登录会议…", "tryAgainButton": "请尝试重启桌面版应用程序" }, "defaultLink": "例如 {{url}}", + "defaultNickname": "例如 星视通", "deviceError": { "cameraError": "无法访问您的摄像头", "cameraPermission": "无法获得摄像头访问权限", @@ -127,8 +138,8 @@ "liveStreaming": "流媒体直播" }, "allow": "允许", - "alreadySharedVideoMsg": "", - "alreadySharedVideoTitle": "只能同时分享 一个视频", + "alreadySharedVideoMsg": "另一位参与者已经在分享视频了,这次会议一次只允许一个人分享视频。", + "alreadySharedVideoTitle": "只能共享一个视频", "applicationWindow": "应用窗口", "Back": "返回", "cameraConstraintFailedError": "你的摄像头不满足要求。", @@ -153,40 +164,40 @@ "contactSupport": "联系我们", "copy": "复制", "dismiss": "解除,离开", - "displayNameRequired": "", + "displayNameRequired": "嗨! 你叫什么名字?", "done": "完成", - "enterDisplayName": "", + "enterDisplayName": "请输入您的名称", "error": "错误", "externalInstallationMsg": "您需要安装桌面共享扩展", "externalInstallationTitle": "需要扩展程序", "goToStore": "跳转至应用商店", "gracefulShutdown": "服务器正在维护,请稍后再试。", "IamHost": "我是主持人。", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "密码错误", "incorrectPassword": "错误的用户名或者密码", "inlineInstallationMsg": "您需要安装桌面共享扩展", "inlineInstallExtension": "立刻安装", "internalError": "哎呀!出现了点问题。错误: {{error}}", "internalErrorTitle": "内部错误", - "kickMessage": "", + "kickMessage": "你可以联系{{participantDisplayName}}以了解更多信息。", "kickParticipantButton": "踢除", "kickParticipantDialog": "确定要踢除此成员吗?", "kickParticipantTitle": "静音该与会者吗?", - "kickTitle": "", + "kickTitle": "对不起,您被 {{participantDisplayName}} 踢出了会议。", "liveStreaming": "流媒体直播中", "liveStreamingDisabledForGuestTooltip": "访客无法启动流媒体直播。", "liveStreamingDisabledTooltip": "禁止启动流媒体。", "lockMessage": "锁定会议失败。", - "lockRoom": "", + "lockRoom": "添加会议 $t(lockRoomPasswordUppercase)", "lockTitle": "锁定失败", - "logoutQuestion": "你确定要登出并停止会议吗?", + "logoutQuestion": "你确定要退出并停止会议吗?", "logoutTitle": "登出", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", - "micConstraintFailedError": "你的麦克风不满足要求。", + "maxUsersLimitReached": "已达到参与人数上限。房间已满。请联系房主或稍后再试!", + "maxUsersLimitReachedTitle": "已达到参与人数上限。", + "micConstraintFailedError": "您的麦克风不符合要求。", "micNotFoundError": "未发现麦克风", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "在您的电脑设置中开启麦克风并调整音量", + "micNotSendingDataTitle": "您的麦克风被系统静音", "micPermissionDeniedError": "您未授权使用麦克风,您仍可参加会议但是其他人无法听到,使用地址栏里的摄像头按钮来启动麦克风。", "micUnknownError": "未知错误,麦克风不可用。", "muteParticipantBody": "您无法对他们解除静音,但是他们自己可以随时解除静音。", @@ -194,10 +205,10 @@ "muteParticipantDialog": "您确定要将此参与者静音吗?您将无法取消静音,但他们可以随时取消静音。", "muteParticipantTitle": "静音该与会者吗?", "Ok": "好的", - "passwordLabel": "", + "passwordLabel": "$t(lockRoomPasswordUppercase)", "passwordNotSupported": "不支持设置会议密码。", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordNotSupportedTitle": "不支持 $t(lockRoomPasswordUppercase) not supported", + "passwordRequired": "需 $t(lockRoomPasswordUppercase)", "popupError": "您的浏览器在此网站上阻止了弹出式窗口。请在浏览器的安全设置中打开它并再试一次。", "popupErrorTitle": "弹出窗口被拦截", "recording": "录制中", @@ -212,7 +223,7 @@ "remoteControlStopMessage": "远程控制结束!", "remoteControlTitle": "远程桌面控制", "Remove": "移除", - "removePassword": "", + "removePassword": "移除 $t(lockRoomPassword)", "removeSharedVideoMsg": "您确定要移除共享的视频吗?", "removeSharedVideoTitle": "移除共享的视频", "reservationError": "预定系统错误", @@ -222,9 +233,13 @@ "screenSharingFailedToInstallTitle": "屏幕共享插件安装失败", "screenSharingFirefoxPermissionDeniedError": "尝试进行屏幕共享时遇到了问题。请确认给予了相应的权限。", "screenSharingFirefoxPermissionDeniedTitle": "哎呀!我们无法启动屏幕共享!", - "screenSharingPermissionDeniedError": "哎呀!您的屏幕共享插件似乎遇到了权限问题。请重新加载并重试。", + "screenSharingPermissionDeniedError": "哎呀!您的屏幕共享插件似乎遇到了权限问题。请刷新页面并重试。", + "sendPrivateMessage": "", + "sendPrivateMessageCancel": "", + "sendPrivateMessageOk": "", + "sendPrivateMessageTitle": "", "serviceUnavailable": "服务不可用", - "sessTerminated": "通话已终止", + "sessTerminated": "会话结束", "Share": "分享", "shareVideoLinkError": "请提供正确的youtube链接。", "shareVideoTitle": "分享视频", @@ -256,17 +271,20 @@ "dialOut": { "statusMessage": "现在状态为 {{status}}" }, + "documentSharing": { + "title": "" + }, "feedback": { "average": "平均", "bad": "差", - "detailsLabel": "告诉我们更多建议和意见。", + "detailsLabel": "提供更多建议或意见。", "good": "好", "rateExperience": "请评价您的会议体验。", "veryBad": "非常差", "veryGood": "非常好" }, "incomingCall": { - "answer": "回复", + "answer": "应答", "audioCallTitle": "来电", "decline": "解除,离开", "productLabel": "来自Jitsi Meet", @@ -274,14 +292,14 @@ }, "info": { "accessibilityLabel": "显示信息", - "addPassword": "", - "cancelPassword": "", - "conferenceURL": "链接:", + "addPassword": "添加密码", + "cancelPassword": "取消密码", + "conferenceURL": "会议链接:", "country": "国家", "dialANumber": "若要加入会议,请拨打其中一个号码,然后输入pin码。", "dialInConferenceID": "PIN:", "dialInNotSupported": "抱歉,不支持电话呼入。", - "dialInNumber": "播入:", + "dialInNumber": "拨入:", "dialInSummaryError": "获取拨入信息时出错。请稍后再试。", "dialInTollFree": "免费电话", "genericError": "糟糕!出错了。", @@ -297,7 +315,7 @@ "noPassword": "未发现设备", "noRoom": "没有指定要呼入的房间。", "numbers": "呼入号码", - "password": "", + "password": "密码", "title": "分享", "tooltip": "共享此会议的链接和拨入信息", "label": "会议信息" @@ -330,7 +348,8 @@ "toggleFilmstrip": "显示/隐藏 视频缩略图", "toggleScreensharing": "在摄像头和屏幕共享之间切换", "toggleShortcuts": "显示/隐藏 快捷键", - "videoMute": "开启或关闭视频" + "videoMute": "开启或关闭视频", + "videoQuality": "管理通话质量" }, "liveStreaming": { "busy": "我们正在释放串流资源。请几分钟后再试。", @@ -349,14 +368,16 @@ "getStreamKeyManually": "我们无法获取任何直播。尝试从YouTube获取流媒体直播密钥。", "invalidStreamKey": "流媒体直播密钥可能不正确。", "off": "流媒体直播已停止", + "offBy": "", "on": "流媒体直播中", + "onBy": "", "pending": "启动流媒体。。。", "serviceName": "直播服务", "signedInAs": "您当前登录为:", "signIn": "使用谷歌登录", "signInCTA": "输入 YouTube 串流密钥或者登录 YouTube 帐号。", "signOut": "登出", - "start": "开始直播", + "start": "开始", "streamIdHelp": "这是什么?", "unavailableTitle": "流媒体直播不可用" }, @@ -395,10 +416,10 @@ "notify": { "connectedOneMember": "{{name}} 加入会议", "connectedThreePlusMembers": "{{name}} 和其他 {{count}} 人加入会议", - "connectedTwoMembers": "{{first}} 和 {{second}} 加入会议", + "connectedTwoMembers": "{{first}} 和 {{second}} 加入会议", "disconnected": "已断开连接", "focus": "会议聚焦", - "focusFail": "{{component}} 不可用 - 在{{ms}}秒后重试", + "focusFail": "{{component}} 不可用 - 在{{ms}}秒后重试", "grantedTo": "主持权限已授予{{to}}!", "invitedOneMember": "{{name}} 已被邀请", "invitedThreePlusMembers": "", @@ -416,7 +437,7 @@ "somebody": "某人", "startSilentTitle": "", "startSilentDescription": "", - "suboptimalExperienceDescription": "呃…恐怕您对 {{appName}} 的体验会很不好。我们正在尝试优化对此浏览器的支持。眼下,请尝试使用 <a href='static/recommendedBrowsers.html' target='_blank'>已知体验很好的浏览器</a>。", + "suboptimalBrowserWarning": "", "suboptimalExperienceTitle": "浏览器警告", "unmute": "", "newDeviceCameraTitle": "检测到新相机", @@ -428,17 +449,17 @@ "poweredby": "技术支持", "presenceStatus": { "busy": "忙碌", - "calling": "通话中...", - "connected": "连接中...", - "connecting": "连接中...", + "calling": "通话中…", + "connected": "连接中…", + "connecting": "连接中…", "connecting2": "连接中...", "disconnected": "已断开连接", "expired": "已过期", "ignored": "已忽略", - "initializingCall": "初始化呼叫...", + "initializingCall": "初始化呼叫…", "invited": "邀请", "rejected": "拒绝", - "ringing": "响铃..." + "ringing": "响铃…" }, "profile": { "setDisplayNameLabel": "设定您的显示名称", @@ -446,6 +467,7 @@ "setEmailLabel": "设置您的个人全球统一标识邮箱", "title": "简介" }, + "raisedHand": "请求发言", "recording": { "authDropboxText": "上传至Dropbox", "availableSpace": "可用空间:{{spaceLeft}} MB(大约可录 {{duration}} 分钟) ", @@ -455,14 +477,16 @@ "error": "录制失败。请重新尝试。", "expandedOff": "录制已停止", "expandedOn": "此会议正在被录制。", - "expandedPending": "录制正在启动...", + "expandedPending": "录制正在启动…", "failedToStart": "录制启动失败", "fileSharingdescription": "跟与会者分享录制", "live": "直播", "loggedIn": "以 {{userName}} 登录", "off": "录制已停止", + "offBy": "", "on": "录制中", - "pending": "正在准备录制会议....", + "onBy": "", + "pending": "正在准备录制会议…", "rec": "REC录制", "serviceDescription": "录制服务将保存您的录制", "serviceName": "录制服务", @@ -480,10 +504,10 @@ "disconnect": "断开连接", "microsoftSignIn": "Microsoft帐号登录", "signedIn": "目前通过{{email}}获取日历事件。点击下方断开连接按钮停止访问。", - "title": "日历" + "title": "题目" }, "devices": "设备", - "followMe": "所有人跟随我", + "followMe": "分机随行", "language": "语言", "loggedIn": "以{{name}} 登录", "moderator": "管理员", @@ -495,25 +519,29 @@ "selectMic": "麦克风", "startAudioMuted": "所有人开始时静音", "startVideoMuted": "所有人开始时隐藏视频画面", - "title": "设置" + "title": "抬头" }, "settingsView": { + "advanced": "高级", "alertOk": "确认", "alertTitle": "警告", "alertURLText": "服务器 URL 无效", "buildInfoSection": "生成信息", "conferenceSection": "会议", + "disableCallIntegration": "", + "disableP2P": "", "displayName": "显示名称", "email": "电子邮件", "header": "设置", "profileSection": "简介", "serverURL": "服务器 URL", + "showAdvanced": "显示高级设置", "startWithAudioMuted": "启动并关闭音频", "startWithVideoMuted": "启动并关闭视频", "version": "版本" }, "share": { - "dialInfoText": "", + "dialInfoText": "拨号文本", "mainText": "点击以下链接加入会议:{{roomUrl}}\n" }, "speaker": "发言人", @@ -538,27 +566,30 @@ "accessibilityLabel": { "audioOnly": "打开 /关闭 仅音频", "audioRoute": "选择音频设备", - "callQuality": "", + "callQuality": "管理通话质量", "cc": "打开 / 关闭 字幕", "chat": "显示 / 隐藏 聊天窗口", "document": "开启 / 关闭 文档共享", - "feedback": "留下反馈", + "download": "下载应用", + "feedback": "提供反馈", "fullScreen": "进入 / 退出 全屏模式", "hangup": "退出聊天室", + "help": "", "invite": "邀请", "kick": "踢除成员", "localRecording": "显示 / 隐藏 本地录制选项", "lockRoom": "切换会议室锁定", "moreActions": "显示 / 隐藏 更多选择", - "moreActionsMenu": "更多功能菜单", + "moreActionsMenu": "更多选择", "mute": "静音 / 取消静音", "pip": "切换子母画面模式", + "privateMessage": "", "profile": "编辑您的简介", "raiseHand": "举手 / 取消举手", "recording": "开启 / 停止 视频录制", "remoteMute": "静音与会者", "Settings": "显示 / 隐藏 设置", - "sharedvideo": "开启 / 关闭 Youtube 影片分享", + "sharedvideo": "开启 / 关闭 YouTube 影片分享", "shareRoom": "邀请他人", "shareYourScreen": "开启 / 关闭 屏幕分享", "shortcuts": "切换快捷方式", @@ -570,8 +601,8 @@ "videoblur": "" }, "addPeople": "添加成员到您的通话中", - "audioOnlyOff": "禁用仅音频模式", - "audioOnlyOn": "启用仅音频模式", + "audioOnlyOff": "", + "audioOnlyOn": "", "audioRoute": "选择音频设备", "authenticate": "认证", "callQuality": "管理通话质量", @@ -579,20 +610,28 @@ "closeChat": "关闭聊天", "documentClose": "关闭文档共享", "documentOpen": "开启文档共享", - "enterFullScreen": "全屏观看", + "download": "下载应用", + "enterFullScreen": "开启全屏", "enterTileView": "切换视图", - "exitFullScreen": "退出全屏模式", + "exitFullScreen": "退出全屏", "exitTileView": "退出平铺模式", - "feedback": "留下反馈", + "feedback": "提供反馈", "hangup": "离开", + "help": "", "invite": "邀请", "login": "登录", "logout": "登出", "lowerYourHand": "放手", "moreActions": "更多操作", "mute": "静音 / 解除静音", + "noAudioSignalTitle": "", + "noAudioSignalDesc": "", + "noAudioSignalDescSuggestion": "", + "noisyAudioInputTitle": "", + "noisyAudioInputDesc": "", "openChat": "开启聊天", "pip": "进入子母画面模式", + "privateMessage": "", "profile": "编辑您的简介", "raiseHand": "请求 / 取消 发言", "raiseYourHand": "举手", @@ -605,7 +644,7 @@ "startSubtitles": "开启字幕", "stopScreenSharing": "停止屏幕共享", "stopSubtitles": "关闭字幕", - "stopSharedVideo": "关闭YouTube视频", + "stopSharedVideo": "停止YouTube视频", "talkWhileMutedPopup": "您在尝试发言吗? 当前您已被静音。", "tileViewToggle": "画面模式", "toggleCamera": "切换相机", @@ -620,7 +659,7 @@ "failedToStart": "开启转录失败", "labelToolTip": "会议正在转录", "off": "停止转录", - "pending": "正在准备转录会议...", + "pending": "正在准备转录会议…", "start": "开启显示字幕", "stop": "停止显示字幕", "tr": "TR" @@ -648,14 +687,16 @@ "pending": "{{displayName}} 已被邀请" }, "videoStatus": { - "audioOnly": "AUD声音", - "audioOnlyExpanded": "您处于仅用音讯模式。该模式可节省频宽,但无法看见他人影像。", - "callQuality": "", + "audioOnly": "仅语音支持", + "audioOnlyExpanded": "", + "callQuality": "呼叫质量", "hd": "高清", + "hdTooltip": "观看高清视频", "highDefinition": "高清", "labelTooiltipNoVideo": "无视频", - "labelTooltipAudioOnly": "已启用仅音频模式", + "labelTooltipAudioOnly": "", "ld": "低清", + "ldTooltip": "观看普清视频", "lowDefinition": "低清", "onlyAudioAvailable": "只能使用音频", "onlyAudioSupported": "我们只支持此浏览器的音频功能。", @@ -663,6 +704,7 @@ "p2pVideoQualityDescription": "", "recHighDefinitionOnly": "将会首选高清模式。", "sd": "标清", + "sdTooltip": "观看标清视频", "standardDefinition": "标清" }, "videothumbnail": { @@ -672,7 +714,7 @@ "moderator": "管理员", "mute": "与会者已被静音", "muted": "已静音", - "remoteControl": "远程控制", + "remoteControl": "", "show": "", "videomute": "" }, @@ -687,11 +729,13 @@ "video": "视频" }, "calendar": "日历", - "connectCalendarButton": "连接你的日历", - "connectCalendarText": "连接你的日历以查看你在{{app}}中的全部会议。此外,新增{{provider}} 会议到你的日历中,点击即可启动。", + "connectCalendarButton": "连接您的日历", + "connectCalendarText": "连接您的日历", "enterRoomTitle": "开启一个新的会议", + "roomNameAllowedChars": "", "go": "开始", - "join": "加入", + "goSmall": "开始", + "join": "", "info": "信息", "privacy": "隐私", "recentList": "最近", @@ -704,4 +748,4 @@ "terms": "条款", "title": "安全,功能完善和完全开源的视频会议" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhTW.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhTW.json index e617cb37f..c3af672ab 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhTW.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main-zhTW.json @@ -2,13 +2,13 @@ "addPeople": { "add": "邀請", "countryNotSupported": "此目標區域尚未支援。", - "countryReminder": "嘗試在美國外地通話?請確認開頭使用的國家代碼!", + "countryReminder": "嘗試撥打到美國以外的地區嗎?請確認在開頭加入國家代碼!", "disabled": "您不可以邀請人員。", - "failedToAdd": "", - "footerText": "播打已關閉。", + "failedToAdd": "增加參與人失敗", + "footerText": "對外播號已停用。", "loading": "尋找聯絡人及電話號碼", "loadingNumber": "驗證電話號碼", - "loadingPeople": "正在尋搜人員進行邀請", + "loadingPeople": "正在搜尋邀請的人員", "noResults": "沒有符合要求的搜尋結果", "noValidNumbers": "請輸入一組電話號碼", "searchNumbers": "新增電話號碼", @@ -21,78 +21,93 @@ "bluetooth": "藍牙", "headphones": "耳機", "phone": "電話", - "speaker": "發言者" + "speaker": "發言者", + "none": "沒有可用的音效裝置" }, "audioOnly": { - "audioOnly": "僅用音訊" + "audioOnly": "低頻寬" }, "calendarSync": { "addMeetingURL": "增加會議連結", - "confirmAddLink": "你要加上 Jitsi 連結於此事件嗎?", + "confirmAddLink": "您要加上 Jitsi 連結於此事件嗎?", "error": { - "appConfiguration": "", - "generic": "", - "notSignedIn": "" + "appConfiguration": "行事曆整合尚未正確設定。", + "generic": "發生錯誤。請檢查行事曆設定,或是重新整理行事曆。", + "notSignedIn": "查看行事曆事件進行認證時發生錯誤。請檢查您的行事曆設定,並再次登入。" }, "join": "參加", "joinTooltip": "參加會議", - "nextMeeting": "下次會議", - "noEvents": "沒有預定事件排入行程。", - "ongoingMeeting": "即將進行會議", + "nextMeeting": "下一個會議", + "noEvents": "已經沒有排程的事件。", + "ongoingMeeting": "正在進行的會議", "permissionButton": "開啟設定", - "permissionMessage": "日曆允許權限是必須的,以查看你的會議於應用程式中。", + "permissionMessage": "必須先取得行事曆的存取權限才能查看您應用程式中的會議。", "refresh": "重新整理行事曆", "today": "今日" }, "chat": { - "error": "錯誤:你的訊息 \"{{originalText}}\" 未被送出。原因: {{error}}", - "messagebox": "", + "error": "錯誤:您的訊息未被傳送。原因: {{error}}", + "fieldPlaceHolder": "在此輸入您的訊息", + "messagebox": "輸入訊息", + "messageTo": "私人訊息傳送至 {{recipient}}", + "noMessagesMessage": "此會議尚無訊息。在此開始對話交談!", "nickname": { - "popover": "選擇暱稱", - "title": "" + "popover": "選擇名稱", + "title": "輸入名稱來使用交談" }, - "title": "" + "privateNotice": "私人訊息傳送至 {{recipient}}", + "title": "交談", + "you": "自己" }, "connectingOverlay": { - "joiningRoom": "" + "joiningRoom": "將自己連接至自訂會議..." }, "connection": { "ATTACHED": "已經附加", - "AUTHENTICATING": "驗證中", - "AUTHFAIL": "驗證失敗", + "AUTHENTICATING": "認證中", + "AUTHFAIL": "認證失敗", "CONNECTED": "已經連接", "CONNECTING": "連接中", "CONNFAIL": "連接失敗", "DISCONNECTED": "已經中斷連接", "DISCONNECTING": "中斷連接中", "ERROR": "錯誤", - "RECONNECTING": "網絡錯誤發生。重新連接中………" + "RECONNECTING": "網路發生問題。重新連線中.........", + "GOT_SESSION_ID": "正在取得 session-id… 完成", + "GET_SESSION_ID_ERROR": "取得 session-id 時發生錯誤:{{code}}", + "FETCH_SESSION_ID": "正在取得 session-id…", + "LOW_BANDWIDTH": "已關閉 {{displayName}} 的影片以節省流量" }, "connectionindicator": { - "address": "地址:", + "address": "位址:", "bandwidth": "估計頻寬:", - "bitrate": "比特率:", - "bridgeCount": "伺服器計數:", + "bitrate": "位元率:", + "bridgeCount": "伺服器數量:", "connectedTo": "已連接至:", "framerate": "影格率:", "less": "顯示較少", - "localaddress": "本地地址:", + "localaddress": "本地位址:", "localport": "本地端口:", "more": "顯示更多", "packetloss": "丟包:", "quality": { "good": "很好", "inactive": "未啟用", - "lost": "漏失", + "lost": "遺失", "nonoptimal": "不甚理想", "poor": "不好" }, - "remoteaddress": "遠端地址:", + "remoteaddress": "遠端位址:", "remoteport": "遠端端口:", "resolution": "解析度:", "status": "連接:", "transport": "傳輸:", - "turn": " (轉)" + "transport_plural": "傳輸:", + "remoteport_plural": "遠端連線埠:", + "remoteaddress_plural": "遠端位址:", + "localport_plural": "本機連線埠:", + "localaddress_plural": "本機位址:", + "e2e_rtt": "E2E RTT:" }, "dateUtils": { "earlier": "稍早", @@ -101,25 +116,26 @@ }, "deepLinking": { "appNotInstalled": "在您的手機上需要 {{app}} 行動應用程式去加入這場會議。", - "description": "沒有發生作用嗎?我們嘗試發起您的會議於 {{app}} desktop 桌面應用程式。請再試一次,或是發起會議於 {{app}} 網路應用程式。", - "descriptionWithoutWeb": "", + "description": "沒有發生作用嗎?我們嘗試在您的 {{app}} 桌面應用程式發起會議。請再試一次,或是在 {{app}} 網路應用程式發起會議。", + "descriptionWithoutWeb": "毫無作用嗎?我們已試著將您的會議在桌機應用工具 {{app}} 中啟動。", "downloadApp": "下載應用 APP", "launchWebButton": "在網路上發起", "openApp": "繼續前往此應用程式", - "title": "發起您的會議於 {{app}}...", + "title": "正在 {{app}} 發起您的會議...", "tryAgainButton": "在桌面上再試一次" }, "defaultLink": "例如 {{url}}", + "defaultNickname": "例如 春嬌 志明", "deviceError": { - "cameraError": "無法取用您的攝影裝置", - "cameraPermission": "無法獲得攝影裝置取用權限", - "microphoneError": "無法取用您的麥克風", - "microphonePermission": "無法獲得麥克風取用權限" + "cameraError": "無法存取您的攝影裝置", + "cameraPermission": "無法獲得攝影裝置存取權限", + "microphoneError": "無法存取您的麥克風", + "microphonePermission": "無法獲得麥克風存取權限" }, "deviceSelection": { "noPermission": "未取得權限", "previewUnavailable": "預覽無法使用", - "selectADevice": "選擇設備", + "selectADevice": "選擇裝置", "testAudio": "播放測試聲音" }, "dialog": { @@ -127,25 +143,25 @@ "liveStreaming": "直播串流" }, "allow": "允許", - "alreadySharedVideoMsg": "", - "alreadySharedVideoTitle": "一次只能允許一位視訊分享", + "alreadySharedVideoMsg": "另一位參與者已經進行分享影像了。此會議同個時間只能允許一人分享影像畫面。", + "alreadySharedVideoTitle": "一次只允許一位影像分享", "applicationWindow": "應用程式視窗", "Back": "返回", "cameraConstraintFailedError": "您的攝影裝置不符合要求。", "cameraNotFoundError": "未發現攝影裝置。", - "cameraNotSendingData": "我們無法取用您的攝影裝置。請檢查是否有其他程序正在使用這個設備,否則請從設置選單裡選擇其他設備或者重新裝載。", - "cameraNotSendingDataTitle": "無法取用攝影裝置", - "cameraPermissionDeniedError": "您未取得權限使用您的攝影裝置。您仍然可參加會議,但是其他人無法看到。可以利用位址欄中的攝影裝置按鈕來修復啟動。", + "cameraNotSendingData": "我們無法存取您的攝影裝置。請檢查是否有其他應用程式正在使用這個裝置,否則請從裝置選單裡選擇其他設備或者重新裝載。", + "cameraNotSendingDataTitle": "無法存取攝影裝置", + "cameraPermissionDeniedError": "您未取得權限使用攝影裝置。您仍可參加會議,但其他人無法看到。可以利用位址欄中的攝影裝置按鈕來修正。", "cameraUnknownError": "由於不明原因,無法使用攝影裝置。", - "cameraUnsupportedResolutionError": "您的攝影裝置不支援所需的視訊解析度。", + "cameraUnsupportedResolutionError": "您的攝影裝置不支援所需的影像解析度。", "Cancel": "取消", "close": "關閉", - "conferenceDisconnectMsg": "請檢查一下網路連接。將在 {{seconds}} 秒後重新連接…", + "conferenceDisconnectMsg": "請檢查一下網路連線。將在 {{seconds}} 秒後重新連接...", "conferenceDisconnectTitle": "您已經被中斷連接。", - "conferenceReloadMsg": "我們正試著修復狀況。重新連接於 {{seconds}} 秒內……", - "conferenceReloadTitle": "不好意思,出錯了。", + "conferenceReloadMsg": "我們正試著修復狀況。重新連接於 {{seconds}} 秒內......", + "conferenceReloadTitle": "很不幸,有點出錯了。", "confirm": "確認", - "confirmNo": "沒有", + "confirmNo": "否", "confirmYes": "是的", "connectError": "喔哦!發生錯誤,無法連接至會議。", "connectErrorWithMsg": "喔哦!發生錯誤,無法連接至會議: {{msg}}", @@ -153,115 +169,129 @@ "contactSupport": "聯絡支援", "copy": "複製", "dismiss": "解除", - "displayNameRequired": "", + "displayNameRequired": "嗨!請問大名?", "done": "完成", - "enterDisplayName": "", + "enterDisplayName": "請在此輸入您自己的名字", "error": "錯誤", "externalInstallationMsg": "您需要安裝桌面分享擴充應用程式。", "externalInstallationTitle": "需要擴充應用程式", "goToStore": "前往應用商店", - "gracefulShutdown": "本伺服器閉關維護中,請稍後再試。", + "gracefulShutdown": "我們的服務目前關閉維護中,請稍後再試。", "IamHost": "我是主辦人", - "incorrectRoomLockPassword": "", + "incorrectRoomLockPassword": "密碼不符", "incorrectPassword": "錯誤的用戶名稱或密碼", "inlineInstallationMsg": "您需要安裝桌面分享擴充應用程式。", "inlineInstallExtension": "立即安裝", "internalError": "喔哦!出現了點問題。發生錯誤: {{error}}", "internalErrorTitle": "內部錯誤", - "kickMessage": "", - "kickParticipantButton": "", - "kickParticipantDialog": "", - "kickParticipantTitle": "", - "kickTitle": "", + "kickMessage": "您可以聯絡 {{participantDisplayName}} 取得更詳細資訊。", + "kickParticipantButton": "踢出", + "kickParticipantDialog": "確定要將這位參與者踢出會議?", + "kickParticipantTitle": "踢出這位參與者?", + "kickTitle": "噢! {{participantDisplayName}} 已將您踢出會議", "liveStreaming": "直播串流中", "liveStreamingDisabledForGuestTooltip": "訪客無法啟動直播串流。", - "liveStreamingDisabledTooltip": "啟動直播串流已關閉。", + "liveStreamingDisabledTooltip": "啟動直播串流已停用。", "lockMessage": "鎖定會議失敗。", - "lockRoom": "", + "lockRoom": "增加會議 $t(lockRoomPasswordUppercase)", "lockTitle": "鎖定失敗", "logoutQuestion": "您確定要登出並停止會議嗎?", "logoutTitle": "登出", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "maxUsersLimitReached": "參與人數已達上限,此會議目前滿額。請聯絡會議主人,或是稍後再試!", + "maxUsersLimitReachedTitle": "參與人數已達上限", "micConstraintFailedError": "您的麥克風不符合要求。", "micNotFoundError": "未發現麥克風。", - "micNotSendingData": "", - "micNotSendingDataTitle": "", - "micPermissionDeniedError": "您未取得權限使用麥克風。您仍然可參加會議,但是其他人無法聽到。可以利用位址欄中的攝影裝置按鈕來修復啟動。", + "micNotSendingData": "至電腦設定中解除麥克風靜音並調整大小", + "micNotSendingDataTitle": "您的麥克風由電腦系統設定為靜音", + "micPermissionDeniedError": "您未取得權限使用麥克風。您仍然可參加會議,但是其他人無法聽到。可以利用位址欄中的攝影裝置按鈕來修正。", "micUnknownError": "不明原因造成麥克風無法使用。", "muteParticipantBody": "您無法對他們解除靜音,但是他們自己隨時可以解除靜音。", "muteParticipantButton": "靜音", - "muteParticipantDialog": "", - "muteParticipantTitle": "", - "Ok": "Ok", - "passwordLabel": "", - "passwordNotSupported": "不支援設置會議密碼。", - "passwordNotSupportedTitle": "", - "passwordRequired": "", - "popupError": "您的瀏覽器在此網站上阻攔彈出視窗。請在瀏覽器的安全設置中開啟它並再試一次。", - "popupErrorTitle": "彈出視窗遭到阻攔", - "recording": "錄製作業中", + "muteParticipantDialog": "確定要將這位參與者設為靜音?您無法為他們解除,但他們可以隨時自行解除靜音。", + "muteParticipantTitle": "將這位參與者設為靜音?", + "Ok": "確定", + "passwordLabel": "會議已被參與者鎖定。請輸入 $t(lockRoomPassword) 以加入。", + "passwordNotSupported": "尚未支援設定會議密碼 $t(lockRoomPassword)。", + "passwordNotSupportedTitle": "尚未支援 $t(lockRoomPasswordUppercase)", + "passwordRequired": "必須要有 $t(lockRoomPasswordUppercase)", + "popupError": "您的瀏覽器在此網站上阻擋彈出視窗。請在瀏覽器的安全設定中啟用並再試一次。", + "popupErrorTitle": "彈出視窗遭到阻擋", + "recording": "錄影中", "recordingDisabledForGuestTooltip": "訪客無法啟動錄影。", - "recordingDisabledTooltip": "啟動錄影已關閉。", + "recordingDisabledTooltip": "啟動錄影已停用。", "rejoinNow": "立即重新加入", - "remoteControlAllowedMessage": "{{user}} 接受您進行遠端控制的要求!", - "remoteControlDeniedMessage": "{{user}} 拒絕您進行遠端控制的要求!", + "remoteControlAllowedMessage": "{{user}} 接受您進行遠端控制的請求!", + "remoteControlDeniedMessage": "{{user}} 拒絕您進行遠端控制的請求!", "remoteControlErrorMessage": "在嘗試向 {{user}} 請求遠端控制權限時發生錯誤!", - "remoteControlRequestMessage": "您要允許 {{user}} 遠端控制您的桌面嗎?", - "remoteControlShareScreenWarning": "注意:如果按下 \"允許\" 您將分享自己的螢幕!", + "remoteControlRequestMessage": "您要允許 {{user}} 遠端控制您的桌面嗎?", + "remoteControlShareScreenWarning": "注意:如果按下 \"允許\" 您將會分享出自己的螢幕!", "remoteControlStopMessage": "遠端控制階段結束!", "remoteControlTitle": "遠端桌面控制", "Remove": "移除", - "removePassword": "", - "removeSharedVideoMsg": "您確定要移除自己的分享視訊嗎?", - "removeSharedVideoTitle": "移除分享視訊", + "removePassword": "移除 $t(lockRoomPassword)", + "removeSharedVideoMsg": "您確定要移除自己分享的影像嗎?", + "removeSharedVideoTitle": "移除分享的影像", "reservationError": "預約系統錯誤", - "reservationErrorMsg": "錯誤碼: {{code}}, 訊息: {{msg}}", + "reservationErrorMsg": "錯誤碼: {{code}} , 訊息: {{msg}}", "retry": "重試", "screenSharingFailedToInstall": "喔哦!螢幕分享擴充程式安裝失敗。", "screenSharingFailedToInstallTitle": "螢幕分享擴充安裝失敗", - "screenSharingFirefoxPermissionDeniedError": "嘗試進行螢幕分享時遇到問題。請確認您有賦予相對的權限允許。", + "screenSharingFirefoxPermissionDeniedError": "嘗試進行螢幕分享時遇到問題。請確認您有授予權限以分享。 ", "screenSharingFirefoxPermissionDeniedTitle": "喔哦!我們無法啟動螢幕分享!", - "screenSharingPermissionDeniedError": "喔哦!您的視訊分享擴充權限發生一點問題。請重新載入再試一次。", + "screenSharingPermissionDeniedError": "噢喔!您的影像分享擴充權限發生問題。請重新載入,再試一次。", + "sendPrivateMessage": "您最近有收到私人訊息。您要進行私人回覆,或是要將自己的訊息發佈至群組?", + "sendPrivateMessageCancel": "發佈至群組", + "sendPrivateMessageOk": "私人回覆", + "sendPrivateMessageTitle": "私人傳訊?", "serviceUnavailable": "服務無法使用", "sessTerminated": "通話已經終止", "Share": "分享", "shareVideoLinkError": "請提供正確的 YouTube 連結。", - "shareVideoTitle": "分享視訊", + "shareVideoTitle": "分享影像", "shareYourScreen": "分享自己的螢幕", - "shareYourScreenDisabled": "螢幕分享已關閉。", + "shareYourScreenDisabled": "螢幕分享已停用。", "shareYourScreenDisabledForGuest": "訪客無法螢幕分享。", "startLiveStreaming": "啟動直播串流", - "startRecording": "啟動錄製作業", + "startRecording": "啟動錄影作業", "startRemoteControlErrorMessage": "嘗試啟動遠端控制階段時發生錯誤!", "stopLiveStreaming": "停止直播串流", - "stopRecording": "停止錄製作業", - "stopRecordingWarning": "確定要停止錄製作業嗎?", + "stopRecording": "停止錄影", + "stopRecordingWarning": "確定要停止錄影嗎?", "stopStreamingWarning": "確定要停止直播串流嗎?", "streamKey": "直播串流密鑰", "Submit": "提交", "thankYou": "感謝您使用 {{appName}}!", "token": "標記", - "tokenAuthFailed": "對不起,您未被允許加入此會議。", - "tokenAuthFailedTitle": "驗證失敗", + "tokenAuthFailed": "抱歉,您未被允許加入此會議。", + "tokenAuthFailedTitle": "認證失敗", "transcribing": "轉錄中", - "unlockRoom": "", + "unlockRoom": "移除會議 $t(lockRoomPassword)", "userPassword": "用戶密碼", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", - "WaitingForHost": "等侯主辦人………", + "WaitForHostMsg": "此會議 <b>{{room}}</b> 尚未啟動。如果您是會議主人,請進行認證;否者,請等待會議主人到達。", + "WaitForHostMsgWOk": "此會議 <b>{{room}}</b> 尚未啟動。如果您是會議主人,請按 [確定] 進行認證;否者,請等待會議主人到達。", + "WaitingForHost": "等侯主辦人 ...", "Yes": "是的", - "yourEntireScreen": "自己的全螢幕" + "yourEntireScreen": "自己的全螢幕", + "screenSharingAudio": "分享音訊", + "muteEveryoneStartMuted": "現在所有人皆已靜音", + "muteEveryoneSelf": "您自己", + "muteEveryoneTitle": "靜音所有人?", + "muteEveryoneDialog": "是否要靜音所有人?您將不能解除對方的靜音,但對方可以隨時解除自己的靜音狀態。", + "muteEveryoneElseTitle": "是否要讓除了 {{whom}} 以外的人靜音?", + "muteEveryoneElseDialog": "靜音後,你就不能再解除對方的靜音,但對方可以隨時解除自己的靜音狀態。" }, "dialOut": { "statusMessage": "現在狀態為 {{status}}" }, + "documentSharing": { + "title": "分享的文件" + }, "feedback": { "average": "普通中等", "bad": "很差", - "detailsLabel": "告訴我們本次會議使用上更多結果。", + "detailsLabel": "告訴我們關於本次會議使用上更多回饋。", "good": "很好", - "rateExperience": "請您評價這次會議的體驗成效", + "rateExperience": "對本次會議的體驗評分", "veryBad": "極差", "veryGood": "極好" }, @@ -274,63 +304,64 @@ }, "info": { "accessibilityLabel": "顯示資訊", - "addPassword": "", - "cancelPassword": "", + "addPassword": "增加 $t(lockRoomPassword)", + "cancelPassword": "取消 $t(lockRoomPassword)", "conferenceURL": "連結:", "country": "國家", - "dialANumber": "", + "dialANumber": "要參加您的會議,撥打以下號碼其中之一,然後輸入 PIN 碼。", "dialInConferenceID": "PIN 號碼:", - "dialInNotSupported": "抱歉,目前不支援電話播入。", - "dialInNumber": "播入:", - "dialInSummaryError": "", - "dialInTollFree": "", + "dialInNotSupported": "抱歉,目前不支援電話撥入。", + "dialInNumber": "撥入:", + "dialInSummaryError": "目前解析撥入資訊錯誤。請稍後再試一次。", + "dialInTollFree": "完全免費", "genericError": "糟糕!出錯了。", - "inviteLiveStream": "要觀看這場會議的直播串流,點按此連結: {{url}}", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteLiveStream": "要查看這場會議的直播串流,點按此連結: {{url}}", + "invitePhone": "要用電話參加會議,請使用: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "要找另一組撥入號碼?\n請見會議撥入號碼: {{url}}\n\n\n如果也要用室內電話撥打,不用連接語音進行加入: {{silentUrl}}", + "inviteURLFirstPartGeneral": "您受邀參加會議。", + "inviteURLFirstPartPersonal": "{{name}} 正在邀請您加入會議。\n", + "inviteURLSecondPart": "\n加入會議:\n{{url}}\n", "liveStreamURL": "直播串流:", "moreNumbers": "更多成員", - "noNumbers": "無播入號碼。", + "noNumbers": "無撥入號碼。", "noPassword": "無", - "noRoom": "沒有會議室是指定要播打進入。", - "numbers": "播入號碼", - "password": "", + "noRoom": "沒有會議室是指定要撥入。", + "numbers": "撥入號碼", + "password": "$t(lockRoomPasswordUppercase):", "title": "分享", - "tooltip": "顯示此會議的連結及電話播入號碼", - "label": "" + "tooltip": "顯示此會議的連結及電話撥入號碼", + "label": "會議資訊" }, "inviteDialog": { - "alertText": "", + "alertText": "邀請某些參與者失敗。", "header": "邀請", - "searchCallOnlyPlaceholder": "", - "searchPeopleOnlyPlaceholder": "", - "searchPlaceholder": "", - "send": "" + "searchCallOnlyPlaceholder": "輸入電話號碼", + "searchPeopleOnlyPlaceholder": "搜尋參與者", + "searchPlaceholder": "參與者或電話號碼", + "send": "發送" }, "inlineDialogFailure": { "msg": "好像有點卡卡不順。", "retry": "重試", "support": "支援", - "supportMsg": "如果狀況一直發生,請聯絡" + "supportMsg": "如果這樣狀況一直發生,請聯絡" }, "keyboardShortcuts": { - "focusLocal": "聚焦於自己的視訊", - "focusRemote": "聚焦於另一人的視訊", - "fullScreen": "觀看 或 離開 全螢幕", + "focusLocal": "聚焦於自己的影像", + "focusRemote": "聚焦於另一人的影像", + "fullScreen": "觀看或離開全螢幕", "keyboardShortcuts": "快捷鍵", - "localRecording": "顯示或顯示本地端錄影控制", + "localRecording": "顯示或隱藏本機端錄影操控", "mute": "靜音或解除靜音", "pushToTalk": "按鍵通話", "raiseHand": "舉手發言或不作發言", "showSpeakerStats": "顯示發言者數據", - "toggleChat": "開啟或關閉聊天", - "toggleFilmstrip": "顯示或隱藏視訊影片縮圖", + "toggleChat": "開啟或關閉交談", + "toggleFilmstrip": "顯示或隱藏影片縮圖", "toggleScreensharing": "在攝影鏡頭和螢幕分享之間進行切換", - "toggleShortcuts": "顯示或顯示鍵盤快捷鍵", - "videoMute": "啟動或停止自己的攝影裝置" + "toggleShortcuts": "顯示或隱藏鍵盤快捷鍵", + "videoMute": "啟動或停止自己的攝影裝置", + "videoQuality": "管理通話品質" }, "liveStreaming": { "busy": "我們正在釋放串流資源。請過幾分鐘後再試。", @@ -340,25 +371,29 @@ "chooseCTA": "請選擇直播串流選項。您目前是以 {{email}} 身份登入。", "enterStreamKey": "在此輸入您的 YouTube 直播串流密鑰。", "error": "直播串流失敗。請重試。", - "errorAPI": "取用您的 YouTube 播出時發生錯誤。請重新登入。", + "errorAPI": "在存取您的 YouTube 直播串流時發生問題。請重新登入。", "errorLiveStreamNotEnabled": "直播串流在 {{email}} 尚未啟用。請開啟直播串流或登入有啟用直播串流的帳戶。", "expandedOff": "直播串流已停止", "expandedOn": "會議串流目前送至 YouTube 。", - "expandedPending": "直播串流正被啟動…", + "expandedPending": "直播串流正被啟動...", "failedToStart": "直播串流啟動失敗", - "getStreamKeyManually": "", - "invalidStreamKey": "", + "getStreamKeyManually": "我們無法解析任何直播串流,請從 YouTube 取得您的直播串流 Key 。", + "invalidStreamKey": "直播串流 Key 可能不正確。", "off": "直播串流已經停止", + "offBy": "{{name}} 停止了直播串流", "on": "直播串流中", - "pending": "啟動直播串流………", + "onBy": "{{name}} 啟動了直播串流", + "pending": "啟動直播串流...", "serviceName": "直播串流服務", - "signedInAs": "你目前登入名為:", + "signedInAs": "您目前登入名稱為:", "signIn": "使用 Google 帳戶登入", "signInCTA": "輸入 YouTube 直播串流密鑰,或登入 YouTube 帳號。", "signOut": "登出", "start": "啟動直播串流", "streamIdHelp": "這是什麼?", - "unavailableTitle": "直播串流無法使用" + "unavailableTitle": "直播串流無法使用", + "googlePrivacyPolicy": "Google 隱私權政策", + "youtubeTerms": "YouTube 服務條款" }, "localRecording": { "clientState": { @@ -368,119 +403,122 @@ }, "dialogTitle": "本地端錄影控制", "duration": "期間", - "durationNA": "N/A", - "encoding": "解碼中", + "durationNA": "不適用", + "encoding": "編碼中", "label": "LOR", - "labelToolTip": "本地端錄影使用中", + "labelToolTip": "本地端錄影投入中", "localRecording": "本地端錄影中", "me": "自己", "messages": { - "engaged": "本地端錄影已使用。", + "engaged": "本地端錄影已投入。", "finished": "錄影階段 {{token}} 已完成。請傳送錄影檔案至主持人。", "finishedModerator": "錄影階段 {{token}} 已完成。本地端錄影追蹤已存檔。請要求各參與者提交其錄影檔案。", - "notModerator": "你不是主持人,無法啟動或停止本地端錄影。" + "notModerator": "您不是主持人,無法啟動或停止本地端錄影。" }, "moderator": "主持人", - "no": "沒有", + "no": "否", "participant": "參與者", "participantStats": "參與者狀態", - "sessionToken": "階段標記", - "start": "啟動錄影作業", - "stop": "停止錄影作業", + "sessionToken": "工作階段標記", + "start": "啟動錄影", + "stop": "停止錄影", "yes": "是的" }, "lockRoomPassword": "密碼", "lockRoomPasswordUppercase": "密碼", "me": "我", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", + "connectedOneMember": "{{name}} 加入了會議", + "connectedThreePlusMembers": "{{name}} 及 {{count}} 位人員加入了會議", + "connectedTwoMembers": "{{first}} 及 {{second}} 加入了會議", "disconnected": "已經中斷連接", "focus": "會議焦點", "focusFail": "{{component}} 無法使用 - 請在 {{ms}} 秒後重試", - "grantedTo": "主持人權限已授予 {{to}}!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "grantedTo": "主持人權限已授予 {{to}} !", + "invitedOneMember": "{{name}} 已受邀請", + "invitedThreePlusMembers": "{{name}} 及 {{count}} 位人員已受邀請", + "invitedTwoMembers": "{{first}} 及 {{second}} 已受邀請", + "kickParticipant": "{{kicked}} 已被 {{kicker}} 踢出會議", "me": "自己", "moderator": "主持人權限已經取得!", - "muted": "您已經啟動通話,並處於靜音狀態。", + "muted": "您已經啟動通話,處於靜音。", "mutedTitle": "您目前處於靜音!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "", + "mutedRemotelyTitle": "您已經被 {{participantDisplayName}} 設為靜音了!", + "mutedRemotelyDescription": "當您準備好要發言,就可以取消靜音。當您結束了,在會議中要避開噪音,即可回復成靜音。", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) 已被其他參與者移除", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) 已被其他參與者設定", + "raisedHand": "{{name}} 想要發言。", "somebody": "某人", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "呃……恐怕您對 {{appName}} 的體驗不是很好,我們正在嘗試找方法改進對此瀏覽器的支援。現下敬請選用 <a href='static/recommendedBrowsers.html' target='_blank'>全力支援的瀏覽器</a> 來進行。", + "startSilentTitle": "您加入了會議而無聲音輸出!", + "startSilentDescription": "重新加入會議以啟用語音", + "suboptimalBrowserWarning": "恐怕您本次會議體驗並不太好,我們會想辦法改進的。但在此之前,敬請使用 <a href='static/recommendedBrowsers.html' target='_blank'>完全支援的瀏覽器</a> 。", "suboptimalExperienceTitle": "瀏覽器警告", - "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "unmute": "取消靜音", + "newDeviceCameraTitle": "偵測到新的攝影裝置", + "newDeviceAudioTitle": "偵測到新的音效裝置", + "newDeviceAction": "使用" }, - "passwordSetRemotely": "", - "passwordDigitsOnly": "", + "passwordSetRemotely": "被其他參與者設定", + "passwordDigitsOnly": "提昇到 {{number}} 位元", "poweredby": "技術支援", "presenceStatus": { "busy": "忙線", - "calling": "來電…", + "calling": "來電...", "connected": "已經連接", "connecting": "連線中...", "connecting2": "通話中*...", "disconnected": "已經中斷連接", - "expired": "未接", - "ignored": "忽略", - "initializingCall": "播打電話…", - "invited": "被邀請的", + "expired": "逾時未接", + "ignored": "忽略不接", + "initializingCall": "撥打電話...", + "invited": "受邀請的", "rejected": "拒接", - "ringing": "鈴鈴鈴……" + "ringing": "鈴鈴鈴..." }, "profile": { "setDisplayNameLabel": "設定您的顯示名稱", "setEmailInput": "輸入您的電子信箱", - "setEmailLabel": "設置您的大頭人像電子信箱", + "setEmailLabel": "設定您的大頭人像電子信箱", "title": "簡介" }, + "raisedHand": "請求發言", "recording": { "authDropboxText": "上傳至 Dropbox", - "availableSpace": "可用空間: {{spaceLeft}} MB (大約錄影時間 {{duration}} 分鐘)", + "availableSpace": "可用空間: {{spaceLeft}} MB (錄影時間大約 {{duration}} 分鐘)", "beta": "BETA", - "busy": "我們正在釋放錄製資源。請過幾分鐘後再試。", - "busyTitle": "全部錄製設備正在忙碌", - "error": "錄製作業失敗。請再次重試。", + "busy": "我們正在釋放錄影資源。請過幾分鐘後再試。", + "busyTitle": "全部錄影目前忙碌", + "error": "錄影失敗。請重試。", "expandedOff": "錄影已經停止", "expandedOn": "此會議目前正在錄影。", - "expandedPending": "錄影正在啟動…", - "failedToStart": "錄製啟動失敗", - "fileSharingdescription": "", + "expandedPending": "錄影正在啟動...", + "failedToStart": "錄影啟動失敗", + "fileSharingdescription": "分享錄影給會議參與者", "live": "直播", "loggedIn": "以 {{userName}} 登入", - "off": "錄製作業已經停止", - "on": "錄製作業中", - "pending": "準備錄影此會議…", + "off": "錄影已經停止", + "offBy": "{{name}} 停止了錄影", + "on": "錄影中", + "onBy": "{{name}} 啟動了錄影", + "pending": "準備錄影此會議...", "rec": "REC 錄影", - "serviceDescription": "", - "serviceName": "錄製作業服務", - "signIn": "jde bp ", + "serviceDescription": "您的錄影會被存在錄影伺服器中", + "serviceName": "錄影伺服", + "signIn": "登入", "signOut": "登出", "unavailable": "喔哦!{{serviceName}} 目前無法使用。我們正在解決此問題,請稍後再試。", - "unavailableTitle": "錄製作業無法使用" + "unavailableTitle": "錄影無法使用" }, "sectionList": { - "pullToRefresh": "下滑以重新整理" + "pullToRefresh": "拉動以重新整理" }, "settings": { "calendar": { - "about": "此 {{appName}} 行事曆整合是安全存取你的行事曆,所以可以讀取即將發生的事件。", + "about": "此 {{appName}} 行事曆整合是安全存取您的行事曆,所以可以讀取即將發生的事件。", "disconnect": "中斷連接", "microsoftSignIn": "使用 Microsoft 帳戶登入", "signedIn": "目前是以 {{email}} 來存取行事曆事件。點按下方取消連接鈕可以停止存取行事曆事件。", - "title": "日曆" + "title": "行事曆" }, "devices": "裝置", "followMe": "全部人跟隨仿照我", @@ -494,34 +532,40 @@ "selectCamera": "攝影裝置", "selectMic": "麥克風", "startAudioMuted": "全部人啟動時處於靜音", - "startVideoMuted": "全部人啟動時隱藏視訊畫面", - "title": "設置" + "startVideoMuted": "全部人啟動時處於隱藏", + "title": "設定", + "speakers": "揚聲器", + "microphones": "麥克風" }, "settingsView": { - "alertOk": "確認", + "advanced": "進階", + "alertOk": "確定", "alertTitle": "警告", "alertURLText": "所輸入的伺服器 URL 是無效的", - "buildInfoSection": "", + "buildInfoSection": "建立資訊", "conferenceSection": "會議", + "disableCallIntegration": "停用原生電話整合", + "disableP2P": "停用端對端模式", "displayName": "顯示名稱", "email": "電子郵件", - "header": "設置", + "header": "設定", "profileSection": "簡介", "serverURL": "伺服器 URL", - "startWithAudioMuted": "啟動並音訊靜音", - "startWithVideoMuted": "啟動並視訊靜音", - "version": "" + "showAdvanced": "顯示進階設定", + "startWithAudioMuted": "啟動並靜音", + "startWithVideoMuted": "啟動並關閉影像", + "version": "版本" }, "share": { - "dialInfoText": "", - "mainText": "點按以下連結參加會議:{{roomUrl}}\n" + "dialInfoText": "\n\n=====\n\n正要撥入您的會議電話?\n\n{{defaultDialInNumber}} 點按此連結來查看此會議的電話撥入號碼\n{{dialInfoPageUrl}}", + "mainText": "按一下下方連結加入會議:\n{{roomUrl}}" }, "speaker": "發言者", "speakerStats": { - "hours": "{{count}}h", - "minutes": "{{count}}m", + "hours": "{{count}}時", + "minutes": "{{count}}分", "name": "名稱", - "seconds": "{{count}}s", + "seconds": "{{count}}秒", "speakerStats": "發言者數據", "speakerTime": "發言者時間" }, @@ -538,170 +582,207 @@ "accessibilityLabel": { "audioOnly": "切換僅有聲音", "audioRoute": "選擇聲音裝置", - "callQuality": "", + "callQuality": "管理影像品質", "cc": "切換字幕", - "chat": "切換聊天視窗", + "chat": "切換交談視窗", "document": "切換分享的文件", + "download": "下載我們的 APPs", "feedback": "留言回報", "fullScreen": "切換全螢幕", "hangup": "離開來電", + "help": "說明", "invite": "邀請人員", - "kick": "", + "kick": "踢出參與者", "localRecording": "切換本地端錄影控制", - "lockRoom": "", + "lockRoom": "切換會議密碼", "moreActions": "切換更多動作功能表", "moreActionsMenu": "更多動作功能表", "mute": "切換靜音", "pip": "切換子母畫面模式", + "privateMessage": "發送私人訊息", "profile": "編輯您的簡介", "raiseHand": "切換舉手", "recording": "切換錄影", - "remoteMute": "", - "Settings": "切換設置", - "sharedvideo": "切換 Youtube 影片分享", + "remoteMute": "靜音參與者", + "Settings": "切換設定", + "sharedvideo": "切換 YouTube 影片分享", "shareRoom": "邀請某人", "shareYourScreen": "切換螢幕分享", "shortcuts": "切換快捷鍵", - "show": "", + "show": "顯示在台上", "speakerStats": "切換發言人統計", "tileView": "切換平鋪檢視", "toggleCamera": "切換攝影機", - "videomute": "切換靜音視訊", - "videoblur": "" + "videomute": "切換無影像", + "videoblur": "切換影像模糊", + "toggleFilmstrip": "切換幻燈片", + "muteEveryone": "靜音所有人", + "moreOptions": "顯示更多選項" }, "addPeople": "新增人員到您的通話中", - "audioOnlyOff": "關閉僅用音訊模式", - "audioOnlyOn": "關閉僅用音訊模式", + "audioOnlyOff": "停用低頻寬模式", + "audioOnlyOn": "啟用低頻寬模式", "audioRoute": "選擇聲音裝置", - "authenticate": "驗證", - "callQuality": "管理通話品質", - "chat": "開啟/關閉 聊天", - "closeChat": "", + "authenticate": "認證", + "callQuality": "管理影像品質", + "chat": "開啟/關閉 交談", + "closeChat": "關閉交談", "documentClose": "關閉分享的文件檔案", "documentOpen": "開啟分享的文件檔案", + "download": "下載我們的 APPs", "enterFullScreen": "觀看全螢幕", - "enterTileView": "", + "enterTileView": "進入平鋪檢視", "exitFullScreen": "跳出全螢幕", - "exitTileView": "", + "exitTileView": "跳出平鋪檢視", "feedback": "留言回報", "hangup": "留言", + "help": "說明", "invite": "邀請人員", "login": "登入", "logout": "登出", - "lowerYourHand": "", + "lowerYourHand": "放下舉手", "moreActions": "更多動作", "mute": "靜音 / 解除靜音", - "openChat": "", - "pip": "進入子母畫模式", + "noAudioSignalTitle": "您的麥克風沒有輸入訊號!", + "noAudioSignalDesc": "如果您沒有特別從系統設定或硬體靜音,請考慮更換裝置。", + "noAudioSignalDescSuggestion": "如果您沒有特別從系統設定或硬體靜音,請考慮切換至建議裝置。", + "openChat": "開啟交談", + "pip": "進入子母畫面模式", + "privateMessage": "發送私人訊息", "profile": "編輯您的簡介", "raiseHand": "舉手/取消 請求發言", - "raiseYourHand": "", - "Settings": "設置", - "sharedvideo": "分享 YouTube 視訊", + "raiseYourHand": "舉手發言", + "Settings": "設定", + "sharedvideo": "分享 YouTube 影片", "shareRoom": "邀請某人", "shortcuts": "查看快捷鍵", "speakerStats": "發言者數據", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", - "stopSharedVideo": "停止 YouTube 視訊", + "startScreenSharing": "啟動螢幕分享", + "startSubtitles": "啟動字幕", + "stopScreenSharing": "停止螢幕分享", + "stopSubtitles": "停止字幕", + "stopSharedVideo": "停止 YouTube 影片", "talkWhileMutedPopup": "您要發言嗎? 目前您處於靜音。", "tileViewToggle": "切換平鋪檢視", "toggleCamera": "切換攝影機", "videomute": "啟動/停止 攝影裝置", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "模糊我的背景畫面", + "stopvideoblur": "停用背景模糊畫面", + "noisyAudioInputDesc": "噪音聽起來是從麥克風傳來的,請考慮靜音或更換裝置。", + "noisyAudioInputTitle": "您的麥克風疑似有雜音!", + "noAudioSignalDialInLinkDesc": "撥入號碼", + "noAudioSignalDialInDesc": "您亦可使用下述方式撥入:", + "muteEveryone": "靜音所有人", + "moreOptions": "更多選項" }, "transcribing": { - "ccButtonTooltip": "", - "error": "錄影作業失敗。請重試。", - "expandedLabel": "轉錄目前開啟", + "ccButtonTooltip": "啟動/停止 字幕", + "error": "轉錄失敗。請重試。", + "expandedLabel": "目前轉錄開啟", "failedToStart": "轉錄啟動失敗", - "labelToolTip": "此會議正被轉錄", + "labelToolTip": "此會議正在轉錄", "off": "轉錄已停止", - "pending": "正在準備轉錄會議…", + "pending": "準備轉錄會議...", "start": "啟動顯示字幕", "stop": "停止顯示字幕", "tr": "TR 轉錄" }, "userMedia": { - "androidGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>允許</i></b>", - "chromeGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>允許</i></b>", - "edgeGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>是的</i></b>", + "androidGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>允許</i></b>。", + "chromeGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>允許</i></b>。", + "edgeGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>是的</i></b>。", "electronGrantPermissions": "請允許權限使用您的攝影裝置和麥克風", - "firefoxGrantPermissions": "當瀏覽器要求權限允許時,請選擇<b><i>分享設備</i></b> ", - "iexplorerGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>OK</i></b>", + "firefoxGrantPermissions": "當瀏覽器請求權限時,請選擇<b><i>分享選取裝置</i></b>。", + "iexplorerGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>確定</i></b>。", "nwjsGrantPermissions": "請允許權限使用您的攝影裝置和麥克風", - "operaGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>允許</i></b>", - "react-nativeGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>允許</i></b>", - "safariGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>OK</i></b>" + "operaGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>允許</i></b>。", + "react-nativeGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>允許</i></b>。", + "safariGrantPermissions": "當瀏覽器要求權限時,請選擇 <b><i>確定</i></b>。" }, "videoSIPGW": { "busy": "我們正在清理釋放資源。請過幾分鐘後再試。", "busyTitle": "會議室服務正處於忙碌中", - "errorAlreadyInvited": "{{displayName}} 已受邀請", - "errorInvite": "會議尚未開始,請稍後再來。", + "errorAlreadyInvited": "{{displayName}} 已經邀請", + "errorInvite": "會議尚未開始,請稍後再試。", "errorInviteFailed": "我們正在解決問題。請稍後再試。", "errorInviteFailedTitle": "邀請 {{displayName}} 失敗", "errorInviteTitle": "錯誤邀請會議室", - "pending": "{{displayName}} 已經邀請" + "pending": "{{displayName}} 已受邀請" }, "videoStatus": { "audioOnly": "AUD 聲音", - "audioOnlyExpanded": "你處於僅用音訊模式。這個模式節省頻寬,但無法看見他人影像。", - "callQuality": "", - "hd": "HD 高清", - "highDefinition": "高清品質 HD", - "labelTooiltipNoVideo": "沒有視訊", - "labelTooltipAudioOnly": "僅有音訊模式已經啟用", - "ld": "LD 低清", - "lowDefinition": "低清品質 LD", + "audioOnlyExpanded": "您目前處於低頻寬模式。在此模式下您僅會收到語音及螢幕分享。", + "callQuality": "影像品質", + "hd": "HD", + "hdTooltip": "觀看高解析度影像 HD", + "highDefinition": "高解析度 HD", + "labelTooiltipNoVideo": "無影像", + "labelTooltipAudioOnly": "低頻寬模式已啟用", + "ld": "LD", + "ldTooltip": "觀看低解析度影像 LD", + "lowDefinition": "低解析度 LD", "onlyAudioAvailable": "僅有音訊可以使用", "onlyAudioSupported": "在此瀏覽器我們僅支援音訊功能。", "p2pEnabled": "點對點功能已經啟用", - "p2pVideoQualityDescription": "", - "recHighDefinitionOnly": "將會偏好使用高清模式 HD。", - "sd": "SD 標清", - "standardDefinition": "標清品質 SD" + "p2pVideoQualityDescription": "在點對點模式下收到的影像品質只能在高檔及僅有語音間切換。其他的設定必須在點對點退出後才會生效。", + "recHighDefinitionOnly": "將會偏好使用高解析度 HD。", + "sd": "SD", + "sdTooltip": "觀看標準解析度影像 SD", + "standardDefinition": "標準解析度 SD" }, "videothumbnail": { "domute": "靜音", "flip": "翻轉", "kick": "踢出", "moderator": "主持人", - "mute": "", + "mute": "參與者處於靜音", "muted": "處於靜音", "remoteControl": "遠端控制", - "show": "", - "videomute": "" + "show": "顯示在台上", + "videomute": "參與者已經停止攝影裝置", + "domuteOthers": "靜音其他所有人" }, "welcomepage": { "accessibilityLabel": { - "join": "輕觸即可參加", + "join": "點按即可加入", "roomname": "輸入會議室名稱" }, - "appDescription": "快來使用吧,團隊全部成員使用視訊通話,可以邀請任何您所認識的人。 {{app}} 是一套完全加密、100% 開放源碼的視訊會議解決方案。無需註冊帳號,無時無刻不分日夜均可免費使用。", + "appDescription": "快來使用吧,團隊全部成員都來使用視訊通話。事實上,任何認識的人都可以邀請。 {{app}} 是一套完全加密、100% 開放源碼的視訊會議解決方案。無需註冊帳號,無時無刻不分日夜均可免費使用。", "audioVideoSwitch": { "audio": "語音", - "video": "視訊" + "video": "影像" }, - "calendar": "日曆", - "connectCalendarButton": "連接你的行事曆", - "connectCalendarText": "", + "calendar": "行事曆", + "connectCalendarButton": "連接自己的行事曆", + "connectCalendarText": "連接自己的行事曆來查看在 {{app}} 中的會議。此外,增加 {{provider}} 的會議至自己的行事曆,只要點按一下即可啟動。", "enterRoomTitle": "啟動新的會議", + "roomNameAllowedChars": "會議的名稱不可包含這些字元: ?, &, :, ', \", %, #.", "go": "開始", - "join": "加入", - "info": "", + "goSmall": "開始", + "join": "建立 / 加入", + "info": "資訊", "privacy": "隱私", "recentList": "最近使用", "recentListDelete": "刪除", - "recentListEmpty": "目前最近使用是空白的。與你的團隊成員聊天,即會在此處找到最近的會議。", - "reducedUIText": "", + "recentListEmpty": "目前最近使用是空白的。與您的團隊成員交談,即會在此處找到最近使用會議。", + "reducedUIText": "歡迎使用 {{app}}!", "roomname": "輸入會議室名稱", - "roomnameHint": "請輸入您想加入的會議室 URL 網址或名稱。您可以用個名稱來建立會議室,只要其他人輸入相同的名稱就能加入會議室喔。", + "roomnameHint": "請輸入您想加入的會議室名稱或 URL 網址。您可以用個名稱來建立會議室,只要其他人輸入相同的名稱就能加入會議室喔。", "sendFeedback": "發送回報", "terms": "條款", - "title": "安全、全功能、完全免費的視訊會議" + "title": "安全、全功能、完全免費的視訊會議", + "getHelp": "取得協助" + }, + "chromeExtensionBanner": { + "dontShowAgain": "不再顯示此訊息", + "buttonText": "安裝 Chrome 擴充套件", + "installExtensionText": "安裝適用於 Google 行事曆及 Office 365 整合的擴充套件" + }, + "helpView": { + "header": "說明中心" + }, + "lonelyMeetingExperience": { + "youAreAlone": "您是會議中的唯一一個人", + "button": "邀請其他人" } -} \ No newline at end of file +} diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main.json b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main.json index d767bcd89..267340cfb 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main.json +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/assets/lang/main.json @@ -46,13 +46,23 @@ "today": "Today" }, "chat": { - "error": "Error: your message \"{{originalText}}\" was not sent. Reason: {{error}}", + "error": "Error: your message was not sent. Reason: {{error}}", + "fieldPlaceHolder": "Type your message here", "messagebox": "Type a message", + "messageTo": "Private message to {{recipient}}", + "noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!", "nickname": { "popover": "Choose a nickname", "title": "Enter a nickname to use chat" }, - "title": "Chat" + "privateNotice": "Private message to {{recipient}}", + "title": "Chat", + "you": "you" + }, + "chromeExtensionBanner": { + "installExtensionText": "Install the extension for Google Calendar and Office 365 integration", + "buttonText": "Install Chrome Extension", + "dontShowAgain": "Don’t show me this again" }, "connectingOverlay": { "joiningRoom": "Connecting you to your meeting..." @@ -70,9 +80,7 @@ "FETCH_SESSION_ID": "Obtaining session-id...", "GET_SESSION_ID_ERROR": "Get session-id error: {{code}}", "GOT_SESSION_ID": "Obtaining session-id... Done", - "LOW_BANDWIDTH": "Video for {{displayName}} has been turned off to save bandwidth", - "RECONNECTING": "A network problem occurred. Reconnecting...", - "USER_CONNECTION_INTERRUPTED": "{{displayName}} is having connectivity issues..." + "LOW_BANDWIDTH": "Video for {{displayName}} has been turned off to save bandwidth" }, "connectionindicator": { "address": "Address:", @@ -167,6 +175,10 @@ "dismiss": "Dismiss", "displayNameRequired": "Hi! What’s your name?", "done": "Done", + "e2eeDescription": "<p>End-to-End Encryption is currently <strong>EXPERIMENTAL</strong>. Please see <a href='https://jitsi.org/blog/e2ee/' target='_blank'>this post</a> for details.</p><br/><p>Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: recording, live streaming and phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.</p>", + "e2eeLabel": "Key", + "e2eeTitle": "End-to-End Encryption", + "e2eeWarning": "<br /><p><strong>WARNING:</strong> Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.</p>", "enterDisplayName": "Please enter your name here", "error": "Error", "externalInstallationMsg": "You need to install our desktop sharing extension.", @@ -201,12 +213,18 @@ "micNotSendingDataTitle": "Your mic is muted by your system settings", "micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.", "micUnknownError": "Cannot use microphone for an unknown reason.", + "muteEveryoneElseDialog": "Once muted, you won't be able to unmute them, but they can unmute themselves at any time.", + "muteEveryoneElseTitle": "Mute everyone except {{whom}}?", + "muteEveryoneDialog": "Are you sure you want to mute everyone? You won't be able to unmute them, but they can unmute themselves at any time.", + "muteEveryoneTitle": "Mute everyone?", + "muteEveryoneSelf": "yourself", + "muteEveryoneStartMuted": "Everyone starts muted from now on", "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.", "muteParticipantButton": "Mute", "muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.", "muteParticipantTitle": "Mute this participant?", "Ok": "Ok", - "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordLabel": "The meeting has been locked by a participant. Please enter the $t(lockRoomPassword) to join.", "passwordNotSupported": "Setting a meeting $t(lockRoomPassword) is not supported.", "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) not supported", "passwordRequired": "$t(lockRoomPasswordUppercase) required", @@ -230,11 +248,16 @@ "reservationError": "Reservation system error", "reservationErrorMsg": "Error code: {{code}}, message: {{msg}}", "retry": "Retry", + "screenSharingAudio": "Share audio", "screenSharingFailedToInstall": "Oops! Your screen sharing extension failed to install.", "screenSharingFailedToInstallTitle": "Screen sharing extension failed to install", - "screenSharingFirefoxPermissionDeniedError": "Something went wrong while we were trying to share your screen. Please make sure that you have given us permission to do so. ", + "screenSharingFirefoxPermissionDeniedError": "Something went wrong while we were trying to share your screen. Please make sure that you have given us permission to do so.", "screenSharingFirefoxPermissionDeniedTitle": "Oops! We weren’t able to start screen sharing!", "screenSharingPermissionDeniedError": "Oops! Something went wrong with your screen sharing extension permissions. Please reload and try again.", + "sendPrivateMessage": "You recently received a private message. Did you intend to reply to that privately, or you want to send your message to the group?", + "sendPrivateMessageCancel": "Send to the group", + "sendPrivateMessageOk": "Send privately", + "sendPrivateMessageTitle": "Send privately?", "serviceUnavailable": "Service unavailable", "sessTerminated": "Call terminated", "Share": "Share", @@ -268,6 +291,12 @@ "dialOut": { "statusMessage": "is now {{status}}" }, + "documentSharing": { + "title": "Shared Document" + }, + "e2ee": { + "labelToolTip": "All participants in this meeting have enabled End-to-End encryption" + }, "feedback": { "average": "Average", "bad": "Bad", @@ -362,7 +391,9 @@ "getStreamKeyManually": "We weren’t able to fetch any live streams. Try getting your live stream key from YouTube.", "invalidStreamKey": "Live stream key may be incorrect.", "off": "Live Streaming stopped", + "offBy": "{{name}} stopped the live streaming", "on": "Live Streaming", + "onBy": "{{name}} started the live streaming", "pending": "Starting Live Stream...", "serviceName": "Live Streaming service", "signedInAs": "You are currently signed in as:", @@ -371,7 +402,9 @@ "signOut": "Sign out", "start": "Start a live stream", "streamIdHelp": "What's this?", - "unavailableTitle": "Live Streaming unavailable" + "unavailableTitle": "Live Streaming unavailable", + "youtubeTerms": "YouTube terms of services", + "googlePrivacyPolicy": "Google Privacy Policy" }, "localRecording": { "clientState": { @@ -434,11 +467,42 @@ "unmute": "Unmute", "newDeviceCameraTitle": "New camera detected", "newDeviceAudioTitle": "New audio device detected", - "newDeviceAction": "Use" + "newDeviceAction": "Use", + "OldElectronAPPTitle": "Security vulnerability!", + "oldElectronClientDescription1": "You appear to be using an old version of the Jitsi Meet client which has known security vulnerabilities. Please make sure you update to our ", + "oldElectronClientDescription2": "latest build", + "oldElectronClientDescription3": " now!" }, "passwordSetRemotely": "set by another participant", "passwordDigitsOnly": "Up to {{number}} digits", "poweredby": "powered by", + "prejoin": { + "audioAndVideoError": "Audio and video error:", + "audioOnlyError": "Audio error:", + "audioTrackError": "Could not create audio track.", + "callMe": "Call me", + "callMeAtNumber": "Call me at this number:", + "configuringDevices": "Configuring devices...", + "connectedWithAudioQ": "You’re connected with audio?", + "copyAndShare": "Copy & share meeting link", + "dialInMeeting": "Dial into the meeting", + "dialInPin": "Dial into the meeting and enter PIN code:", + "dialing": "Dialing", + "iWantToDialIn": "I want to dial in", + "joinAudioByPhone": "Join with phone audio", + "joinMeeting": "Join meeting", + "joinWithoutAudio": "Join without audio", + "initiated": "Call initiated", + "linkCopied": "Link copied to clipboard", + "lookGood": "Speaker and microphone look good", + "or": "or", + "calling": "Calling", + "startWithPhone": "Start with phone audio", + "screenSharingError": "Screen sharing error:", + "videoOnlyError": "Video error:", + "videoTrackError": "Could not create video track.", + "viewAllNumbers": "view all numbers" + }, "presenceStatus": { "busy": "Busy", "calling": "Calling...", @@ -475,7 +539,9 @@ "live": "LIVE", "loggedIn": "Logged in as {{userName}}", "off": "Recording stopped", + "offBy": "{{name}} stopped the recording", "on": "Recording", + "onBy": "{{name}} started the recording", "pending": "Preparing to record the meeting...", "rec": "REC", "serviceDescription": "Your recording will be saved by the recording service", @@ -500,6 +566,7 @@ "followMe": "Everyone follows me", "language": "Language", "loggedIn": "Logged in as {{name}}", + "microphones": "Microphones", "moderator": "Moderator", "more": "More", "name": "Name", @@ -507,21 +574,29 @@ "selectAudioOutput": "Audio output", "selectCamera": "Camera", "selectMic": "Microphone", + "speakers": "Speakers", "startAudioMuted": "Everyone starts muted", "startVideoMuted": "Everyone starts hidden", "title": "Settings" }, "settingsView": { + "advanced": "Advanced", "alertOk": "OK", + "alertCancel": "Cancel", "alertTitle": "Warning", "alertURLText": "The entered server URL is invalid", "buildInfoSection": "Build Information", "conferenceSection": "Conference", + "disableCallIntegration": "Disable native call integration", + "disableP2P": "Disable Peer-To-Peer mode", + "disableCrashReporting": "Disable crash reporting", + "disableCrashReportingWarning": "Are you sure you want to disable crash reporting? The setting will be applied after you restart the app.", "displayName": "Display name", "email": "Email", "header": "Settings", "profileSection": "Profile", "serverURL": "Server URL", + "showAdvanced": "Show advanced settings", "startWithAudioMuted": "Start with audio muted", "startWithVideoMuted": "Start with video muted", "version": "Version" @@ -556,17 +631,23 @@ "cc": "Toggle subtitles", "chat": "Toggle chat window", "document": "Toggle shared document", + "download": "Download our apps", + "e2ee": "End-to-End Encryption", "feedback": "Leave feedback", "fullScreen": "Toggle full screen", "hangup": "Leave the call", + "help": "Help", "invite": "Invite people", "kick": "Kick participant", "localRecording": "Toggle local recording controls", "lockRoom": "Toggle meeting password", "moreActions": "Toggle more actions menu", "moreActionsMenu": "More actions menu", + "moreOptions": "Show more options", "mute": "Toggle mute audio", + "muteEveryone": "Mute everyone", "pip": "Toggle Picture-in-Picture mode", + "privateMessage": "Send private message", "profile": "Edit your profile", "raiseHand": "Toggle raise hand", "recording": "Toggle recording", @@ -580,6 +661,7 @@ "speakerStats": "Toggle speaker statistics", "tileView": "Toggle tile view", "toggleCamera": "Toggle camera", + "toggleFilmstrip": "Toggle filmstrip", "videomute": "Toggle mute video", "videoblur": "Toggle video blur" }, @@ -593,20 +675,33 @@ "closeChat": "Close chat", "documentClose": "Close shared document", "documentOpen": "Open shared document", + "download": "Download our apps", + "e2ee": "End-to-End Encryption", "enterFullScreen": "View full screen", "enterTileView": "Enter tile view", "exitFullScreen": "Exit full screen", "exitTileView": "Exit tile view", "feedback": "Leave feedback", "hangup": "Leave", + "help": "Help", "invite": "Invite people", "login": "Login", "logout": "Logout", "lowerYourHand": "Lower your hand", "moreActions": "More actions", + "moreOptions": "More options", "mute": "Mute / Unmute", + "muteEveryone": "Mute everyone", + "noAudioSignalTitle": "There is no input coming from your mic!", + "noAudioSignalDesc": "If you did not purposely mute it from system settings or hardware, consider switching the device.", + "noAudioSignalDescSuggestion": "If you did not purposely mute it from system settings or hardware, consider switching to the suggested device.", + "noAudioSignalDialInDesc": "You can also dial-in using:", + "noAudioSignalDialInLinkDesc": "Dial-in numbers", + "noisyAudioInputTitle": "Your microphone appears to be noisy!", + "noisyAudioInputDesc": "It sounds like your microphone is making noise, please consider muting or changing the device.", "openChat": "Open chat", "pip": "Enter Picture-in-Picture mode", + "privateMessage": "Send private message", "profile": "Edit your profile", "raiseHand": "Raise / Lower your hand", "raiseYourHand": "Raise your hand", @@ -684,12 +779,13 @@ }, "videothumbnail": { "domute": "Mute", + "domuteOthers": "Mute everyone else", "flip": "Flip", "kick": "Kick out", "moderator": "Moderator", "mute": "Participant is muted", "muted": "Muted", - "remoteControl": "Remote control", + "remoteControl": "Start / Stop remote control", "show": "Show on stage", "videomute": "Participant has stopped the camera" }, @@ -707,9 +803,11 @@ "connectCalendarButton": "Connect your calendar", "connectCalendarText": "Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.", "enterRoomTitle": "Start a new meeting", - "onlyAsciiAllowed": "Meeting name should only contain latin characters and numbers.", + "getHelp": "Get help", + "roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.", "go": "GO", - "join": "JOIN", + "goSmall": "GO", + "join": "CREATE / JOIN", "info": "Info", "privacy": "Privacy", "recentList": "Recent", @@ -721,5 +819,12 @@ "sendFeedback": "Send feedback", "terms": "Terms", "title": "Secure, fully featured, and completely free video conferencing" + }, + "lonelyMeetingExperience": { + "button": "Invite others", + "youAreAlone": "You are the only one in the meeting" + }, + "helpView": { + "header": "Help center" } } diff --git a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/main.jsbundle b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/main.jsbundle index 1730bbca5..f5dbff96b 100644 --- a/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/main.jsbundle +++ b/ios/Pods/JitsiMeetSDK/Frameworks/JitsiMeet.framework/main.jsbundle @@ -1,10 +1,10 @@ var __BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),__DEV__=false,process=this.process||{};process.env=process.env||{};process.env.NODE_ENV=process.env.NODE_ENV||"production"; !(function(r){"use strict";r.__r=o,r.__d=function(r,i,n){if(null!=e[i])return;var o={dependencyMap:n,factory:r,hasError:!1,importedAll:t,importedDefault:t,isInitialized:!1,publicModule:{exports:{}}};e[i]=o},r.__c=n,r.__registerSegment=function(r,e){s[r]=e};var e=n(),t={},i={}.hasOwnProperty;function n(){return e=Object.create(null)}function o(r){var t=r,i=e[t];return i&&i.isInitialized?i.publicModule.exports:d(t,i)}function l(r){var i=r;if(e[i]&&e[i].importedDefault!==t)return e[i].importedDefault;var n=o(i),l=n&&n.__esModule?n.default:n;return e[i].importedDefault=l}function u(r){var n=r;if(e[n]&&e[n].importedAll!==t)return e[n].importedAll;var l,u=o(n);if(u&&u.__esModule)l=u;else{if(l={},u)for(var a in u)i.call(u,a)&&(l[a]=u[a]);l.default=u}return e[n].importedAll=l}o.importDefault=l,o.importAll=u;var a=!1;function d(e,t){if(!a&&r.ErrorUtils){var i;a=!0;try{i=v(e,t)}catch(e){r.ErrorUtils.reportFatalError(e)}return a=!1,i}return v(e,t)}var c=16,f=65535;function p(r){return{segmentId:r>>>c,localId:r&f}}o.unpackModuleId=p,o.packModuleId=function(r){return(r.segmentId<<c)+r.localId};var s=[];function v(t,i){if(!i&&s.length>0){var n=p(t),a=n.segmentId,d=n.localId,c=s[a];null!=c&&(c(d),i=e[t])}var f=r.nativeRequire;if(!i&&f){var v=p(t),h=v.segmentId;f(v.localId,h),i=e[t]}if(!i)throw Error('Requiring unknown module "'+t+'".');if(i.hasError)throw m(t,i.error);i.isInitialized=!0;var I=i,g=I.factory,y=I.dependencyMap;try{var _=i.publicModule;return _.id=t,g(r,o,l,u,_,_.exports,y),i.factory=void 0,i.dependencyMap=void 0,_.exports}catch(r){throw i.hasError=!0,i.error=r,i.isInitialized=!1,i.publicModule.exports=void 0,r}}function m(r,e){return Error('Requiring module "'+r+'", which threw an exception: '+e)}})('undefined'!=typeof globalThis?globalThis:'undefined'!=typeof global?global:'undefined'!=typeof window?window:this); -!(function(n){var e=(function(){function n(n,e){return n}function e(n){var e={};return n.forEach(function(n,r){e[n]=!0}),e}function r(n,r,l){if(n.formatValueCalls++,n.formatValueCalls>200)return"[TOO BIG formatValueCalls "+n.formatValueCalls+" exceeded limit of 200]";var f=t(n,r);if(f)return f;var c=Object.keys(r),g=e(c);if(d(r)&&(c.indexOf('message')>=0||c.indexOf('description')>=0))return o(r);if(0===c.length){if(h(r)){var p=r.name?': '+r.name:'';return n.stylize('[Function'+p+']','special')}if(s(r))return n.stylize(RegExp.prototype.toString.call(r),'regexp');if(y(r))return n.stylize(Date.prototype.toString.call(r),'date');if(d(r))return o(r)}var v,b,m='',j=!1,z=['{','}'];(v=r,Array.isArray(v)&&(j=!0,z=['[',']']),h(r))&&(m=' [Function'+(r.name?': '+r.name:'')+']');return s(r)&&(m=' '+RegExp.prototype.toString.call(r)),y(r)&&(m=' '+Date.prototype.toUTCString.call(r)),d(r)&&(m=' '+o(r)),0!==c.length||j&&0!=r.length?l<0?s(r)?n.stylize(RegExp.prototype.toString.call(r),'regexp'):n.stylize('[Object]','special'):(n.seen.push(r),b=j?i(n,r,l,g,c):c.map(function(e){return a(n,r,l,g,e,j)}),n.seen.pop(),u(b,m,z)):z[0]+m+z[1]}function t(n,e){if(g(e))return n.stylize('undefined','undefined');if('string'==typeof e){var r="'"+JSON.stringify(e).replace(/^"|"$/g,'').replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return n.stylize(r,'string')}return c(e)?n.stylize(''+e,'number'):l(e)?n.stylize(''+e,'boolean'):f(e)?n.stylize('null','null'):void 0}function o(n){return'['+Error.prototype.toString.call(n)+']'}function i(n,e,r,t,o){for(var i=[],u=0,l=e.length;u<l;++u)b(e,String(u))?i.push(a(n,e,r,t,String(u),!0)):i.push('');return o.forEach(function(o){o.match(/^\d+$/)||i.push(a(n,e,r,t,o,!0))}),i}function a(n,e,t,o,i,a){var u,l,c;if((c=Object.getOwnPropertyDescriptor(e,i)||{value:e[i]}).get?l=c.set?n.stylize('[Getter/Setter]','special'):n.stylize('[Getter]','special'):c.set&&(l=n.stylize('[Setter]','special')),b(o,i)||(u='['+i+']'),l||(n.seen.indexOf(c.value)<0?(l=f(t)?r(n,c.value,null):r(n,c.value,t-1)).indexOf('\n')>-1&&(l=a?l.split('\n').map(function(n){return' '+n}).join('\n').substr(2):'\n'+l.split('\n').map(function(n){return' '+n}).join('\n')):l=n.stylize('[Circular]','special')),g(u)){if(a&&i.match(/^\d+$/))return l;(u=JSON.stringify(''+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(u=u.substr(1,u.length-2),u=n.stylize(u,'name')):(u=u.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),u=n.stylize(u,'string'))}return u+': '+l}function u(n,e,r){return n.reduce(function(n,e){return 0,e.indexOf('\n')>=0&&0,n+e.replace(/\u001b\[\d\d?m/g,'').length+1},0)>60?r[0]+(''===e?'':e+'\n ')+' '+n.join(',\n ')+' '+r[1]:r[0]+e+' '+n.join(', ')+' '+r[1]}function l(n){return'boolean'==typeof n}function f(n){return null===n}function c(n){return'number'==typeof n}function g(n){return void 0===n}function s(n){return p(n)&&'[object RegExp]'===v(n)}function p(n){return'object'==typeof n&&null!==n}function y(n){return p(n)&&'[object Date]'===v(n)}function d(n){return p(n)&&('[object Error]'===v(n)||n instanceof Error)}function h(n){return'function'==typeof n}function v(n){return Object.prototype.toString.call(n)}function b(n,e){return Object.prototype.hasOwnProperty.call(n,e)}return function(e,t){return r({seen:[],formatValueCalls:0,stylize:n},e,t.depth)}})(),r='(index)',t={trace:0,info:1,warn:2,error:3},o=[];o[t.trace]='debug',o[t.info]='log',o[t.warn]='warning',o[t.error]='error';var i=1;function a(r){return function(){var a;a=1===arguments.length&&'string'==typeof arguments[0]?arguments[0]:Array.prototype.map.call(arguments,function(n){return e(n,{depth:10})}).join(', ');var u=r;'Warning: '===a.slice(0,9)&&u>=t.error&&(u=t.warn),n.__inspectorLog&&n.__inspectorLog(o[u],a,[].slice.call(arguments),i),g.length&&(a=s('',a)),n.nativeLoggingHook(a,u)}}function u(n,e){return Array.apply(null,Array(e)).map(function(){return n})}var l="\u2502",f="\u2510",c="\u2518",g=[];function s(n,e){return g.join('')+n+' '+(e||'')}if(n.nativeLoggingHook){n.console;n.console={error:a(t.error),info:a(t.info),log:a(t.info),warn:a(t.warn),trace:a(t.trace),debug:a(t.trace),table:function(e){if(!Array.isArray(e)){var o=e;for(var i in e=[],o)if(o.hasOwnProperty(i)){var a=o[i];a[r]=i,e.push(a)}}if(0!==e.length){var l=Object.keys(e[0]).sort(),f=[],c=[];l.forEach(function(n,r){c[r]=n.length;for(var t=0;t<e.length;t++){var o=(e[t][n]||'?').toString();f[t]=f[t]||[],f[t][r]=o,c[r]=Math.max(c[r],o.length)}});for(var g=y(c.map(function(n){return u('-',n).join('')}),'-'),s=[y(l),g],p=0;p<e.length;p++)s.push(y(f[p]));n.nativeLoggingHook('\n'+s.join('\n'),t.info)}else n.nativeLoggingHook('',t.info);function y(n,e){var r=n.map(function(n,e){return n+u(' ',c[e]-n.length).join('')});return e=e||' ',r.join(e+'|'+e)}},group:function(e){n.nativeLoggingHook(s(f,e),t.info),g.push(l)},groupEnd:function(){g.pop(),n.nativeLoggingHook(s(c),t.info)},groupCollapsed:function(e){n.nativeLoggingHook(s(c,e),t.info),g.push(l)},assert:function(e,r){e||n.nativeLoggingHook('Assertion failed: '+r,t.error)}}}else if(!n.console){var p=n.print||function(){};n.console={error:p,info:p,log:p,warn:p,trace:p,debug:p,table:p}}})('undefined'!=typeof globalThis?globalThis:'undefined'!=typeof global?global:'undefined'!=typeof window?window:this); +!(function(n){var e=(function(){function n(n,e){return n}function e(n){var e={};return n.forEach(function(n,r){e[n]=!0}),e}function r(n,r,u){if(n.formatValueCalls++,n.formatValueCalls>200)return"[TOO BIG formatValueCalls "+n.formatValueCalls+" exceeded limit of 200]";var f=t(n,r);if(f)return f;var c=Object.keys(r),s=e(c);if(d(r)&&(c.indexOf('message')>=0||c.indexOf('description')>=0))return o(r);if(0===c.length){if(v(r)){var p=r.name?': '+r.name:'';return n.stylize('[Function'+p+']','special')}if(g(r))return n.stylize(RegExp.prototype.toString.call(r),'regexp');if(y(r))return n.stylize(Date.prototype.toString.call(r),'date');if(d(r))return o(r)}var h,b,m='',j=!1,O=['{','}'];(h=r,Array.isArray(h)&&(j=!0,O=['[',']']),v(r))&&(m=' [Function'+(r.name?': '+r.name:'')+']');return g(r)&&(m=' '+RegExp.prototype.toString.call(r)),y(r)&&(m=' '+Date.prototype.toUTCString.call(r)),d(r)&&(m=' '+o(r)),0!==c.length||j&&0!=r.length?u<0?g(r)?n.stylize(RegExp.prototype.toString.call(r),'regexp'):n.stylize('[Object]','special'):(n.seen.push(r),b=j?i(n,r,u,s,c):c.map(function(e){return a(n,r,u,s,e,j)}),n.seen.pop(),l(b,m,O)):O[0]+m+O[1]}function t(n,e){if(s(e))return n.stylize('undefined','undefined');if('string'==typeof e){var r="'"+JSON.stringify(e).replace(/^"|"$/g,'').replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return n.stylize(r,'string')}return c(e)?n.stylize(''+e,'number'):u(e)?n.stylize(''+e,'boolean'):f(e)?n.stylize('null','null'):void 0}function o(n){return'['+Error.prototype.toString.call(n)+']'}function i(n,e,r,t,o){for(var i=[],l=0,u=e.length;l<u;++l)b(e,String(l))?i.push(a(n,e,r,t,String(l),!0)):i.push('');return o.forEach(function(o){o.match(/^\d+$/)||i.push(a(n,e,r,t,o,!0))}),i}function a(n,e,t,o,i,a){var l,u,c;if((c=Object.getOwnPropertyDescriptor(e,i)||{value:e[i]}).get?u=c.set?n.stylize('[Getter/Setter]','special'):n.stylize('[Getter]','special'):c.set&&(u=n.stylize('[Setter]','special')),b(o,i)||(l='['+i+']'),u||(n.seen.indexOf(c.value)<0?(u=f(t)?r(n,c.value,null):r(n,c.value,t-1)).indexOf('\n')>-1&&(u=a?u.split('\n').map(function(n){return' '+n}).join('\n').substr(2):'\n'+u.split('\n').map(function(n){return' '+n}).join('\n')):u=n.stylize('[Circular]','special')),s(l)){if(a&&i.match(/^\d+$/))return u;(l=JSON.stringify(''+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(l=l.substr(1,l.length-2),l=n.stylize(l,'name')):(l=l.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),l=n.stylize(l,'string'))}return l+': '+u}function l(n,e,r){return n.reduce(function(n,e){return 0,e.indexOf('\n')>=0&&0,n+e.replace(/\u001b\[\d\d?m/g,'').length+1},0)>60?r[0]+(''===e?'':e+'\n ')+' '+n.join(',\n ')+' '+r[1]:r[0]+e+' '+n.join(', ')+' '+r[1]}function u(n){return'boolean'==typeof n}function f(n){return null===n}function c(n){return'number'==typeof n}function s(n){return void 0===n}function g(n){return p(n)&&'[object RegExp]'===h(n)}function p(n){return'object'==typeof n&&null!==n}function y(n){return p(n)&&'[object Date]'===h(n)}function d(n){return p(n)&&('[object Error]'===h(n)||n instanceof Error)}function v(n){return'function'==typeof n}function h(n){return Object.prototype.toString.call(n)}function b(n,e){return Object.prototype.hasOwnProperty.call(n,e)}return function(e,t){return r({seen:[],formatValueCalls:0,stylize:n},e,t.depth)}})(),r='(index)',t={trace:0,info:1,warn:2,error:3},o=[];o[t.trace]='debug',o[t.info]='log',o[t.warn]='warning',o[t.error]='error';var i=1;function a(r){return function(){var a;a=1===arguments.length&&'string'==typeof arguments[0]?arguments[0]:Array.prototype.map.call(arguments,function(n){return e(n,{depth:10})}).join(', ');var l=r;'Warning: '===a.slice(0,9)&&l>=t.error&&(l=t.warn),n.__inspectorLog&&n.__inspectorLog(o[l],a,[].slice.call(arguments),i),s.length&&(a=g('',a)),n.nativeLoggingHook(a,l)}}function l(n,e){return Array.apply(null,Array(e)).map(function(){return n})}var u="\u2502",f="\u2510",c="\u2518",s=[];function g(n,e){return s.join('')+n+' '+(e||'')}if(n.nativeLoggingHook){n.console;n.console={error:a(t.error),info:a(t.info),log:a(t.info),warn:a(t.warn),trace:a(t.trace),debug:a(t.trace),table:function(e){if(!Array.isArray(e)){var o=e;for(var i in e=[],o)if(o.hasOwnProperty(i)){var a=o[i];a[r]=i,e.push(a)}}if(0!==e.length){var u=Object.keys(e[0]).sort(),f=[],c=[];u.forEach(function(n,r){c[r]=n.length;for(var t=0;t<e.length;t++){var o=(e[t][n]||'?').toString();f[t]=f[t]||[],f[t][r]=o,c[r]=Math.max(c[r],o.length)}});for(var s=y(c.map(function(n){return l('-',n).join('')}),'-'),g=[y(u),s],p=0;p<e.length;p++)g.push(y(f[p]));n.nativeLoggingHook('\n'+g.join('\n'),t.info)}else n.nativeLoggingHook('',t.info);function y(n,e){var r=n.map(function(n,e){return n+l(' ',c[e]-n.length).join('')});return e=e||' ',r.join(e+'|'+e)}},group:function(e){n.nativeLoggingHook(g(f,e),t.info),s.push(u)},groupEnd:function(){s.pop(),n.nativeLoggingHook(g(c),t.info)},groupCollapsed:function(e){n.nativeLoggingHook(g(c,e),t.info),s.push(u)},assert:function(e,r){e||n.nativeLoggingHook('Assertion failed: '+r,t.error)}},Object.defineProperty(console,'_isPolyfilled',{value:!0,enumerable:!1})}else if(!n.console){var p=n.print||function(){};n.console={error:p,info:p,log:p,warn:p,trace:p,debug:p,table:p},Object.defineProperty(console,'_isPolyfilled',{value:!0,enumerable:!1})}})('undefined'!=typeof globalThis?globalThis:'undefined'!=typeof global?global:'undefined'!=typeof window?window:this); !(function(n){var r=0,t=function(n,r){throw n},l={setGlobalHandler:function(n){t=n},getGlobalHandler:function(){return t},reportError:function(n){t&&t(n,!1)},reportFatalError:function(n){t&&t(n,!0)},applyWithGuard:function(n,t,u,o,e){try{return r++,n.apply(t,u)}catch(n){l.reportError(n)}finally{r--}return null},applyWithGuardIfNeeded:function(n,r,t){return l.inGuard()?n.apply(r,t):(l.applyWithGuard(n,r,t),null)},inGuard:function(){return!!r},guard:function(n,r,t){var u,o;if('function'!=typeof n)return console.warn('A function must be passed to ErrorUtils.guard, got ',n),null;var e=null!=(u=null!=(o=r)?o:n.name)?u:'<generated guard>';return function(){for(var r,u=arguments.length,o=new Array(u),a=0;a<u;a++)o[a]=arguments[a];return l.applyWithGuard(n,null!=(r=t)?r:this,o,null,e)}}};n.ErrorUtils=l})('undefined'!=typeof globalThis?globalThis:'undefined'!=typeof global?global:'undefined'!=typeof window?window:this); 'undefined'!=typeof globalThis?globalThis:'undefined'!=typeof global?global:'undefined'!=typeof window&&window,(function(){'use strict';var e=Object.prototype.hasOwnProperty;'function'!=typeof Object.entries&&(Object.entries=function(n){if(null==n)throw new TypeError('Object.entries called on non-object');var o=[];for(var t in n)e.call(n,t)&&o.push([t,n[t]]);return o}),'function'!=typeof Object.values&&(Object.values=function(n){if(null==n)throw new TypeError('Object.values called on non-object');var o=[];for(var t in n)e.call(n,t)&&o.push(n[t]);return o})})(); __d(function(g,r,i,a,m,e,d){r(d[0])},0,[1]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=t(r(d[2])),p=t(r(d[3])),u=t(r(d[4])),l=t(r(d[5])),c=t(r(d[6]));r(d[7]);var f=n(r(d[8])),s=r(d[9]),A=r(d[10]),y=r(d[11]),v=r(d[12]),C=(function(n){function t(){return(0,o.default)(this,t),(0,u.default)(this,(0,l.default)(t).apply(this,arguments))}return(0,c.default)(t,n),(0,p.default)(t,[{key:"render",value:function(){return f.default.createElement(A.App,this.props)}}]),t})(f.PureComponent);(0,v._initLogging)();var h=console.log,R=s.AppRegistry.runApplication;s.AppRegistry.runApplication=function(){console.log=function(){},R.apply(void 0,arguments),console.log=h},s.AppRegistry.registerComponent('App',function(){return C}),s.AppRegistry.registerComponent('IncomingCallApp',function(){return y.IncomingCallApp})},1,[2,3,4,5,6,9,10,12,13,17,381,1595,576]); +__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=t(r(d[2])),p=t(r(d[3])),u=t(r(d[4])),l=t(r(d[5])),c=t(r(d[6]));r(d[7]);var f=n(r(d[8])),s=r(d[9]),A=r(d[10]),y=r(d[11]),v=r(d[12]),C=(function(n){function t(){return(0,o.default)(this,t),(0,u.default)(this,(0,l.default)(t).apply(this,arguments))}return(0,c.default)(t,n),(0,p.default)(t,[{key:"render",value:function(){return f.default.createElement(A.App,this.props)}}]),t})(f.PureComponent);(0,v._initLogging)();var h=console.log,R=s.AppRegistry.runApplication;s.AppRegistry.runApplication=function(){console.log=function(){},R.apply(void 0,arguments),console.log=h},s.AppRegistry.registerComponent('App',function(){return C}),s.AppRegistry.registerComponent('IncomingCallApp',function(){return y.IncomingCallApp})},1,[2,3,4,5,6,9,10,12,59,15,435,1689,453]); __d(function(g,r,i,a,m,e,d){m.exports=function(t){if(t&&t.__esModule)return t;var o={};if(null!=t)for(var n in t)if(Object.prototype.hasOwnProperty.call(t,n)){var c=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(t,n):{};c.get||c.set?Object.defineProperty(o,n,c):o[n]=t[n]}return o.default=t,o}},2,[]); __d(function(g,r,i,a,m,e,d){m.exports=function(n){return n&&n.__esModule?n:{default:n}}},3,[]); __d(function(g,r,i,a,m,e,d){m.exports=function(n,o){if(!(n instanceof o))throw new TypeError("Cannot call a class as a function")}},4,[]); @@ -15,1599 +15,1693 @@ __d(function(g,r,i,a,m,e,d){m.exports=function(n){if(void 0===n)throw new Refere __d(function(g,r,i,a,m,e,d){function t(o){return m.exports=t=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},t(o)}m.exports=t},9,[]); __d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=function(o,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");o.prototype=Object.create(n&&n.prototype,{constructor:{value:o,writable:!0,configurable:!0}}),n&&t(o,n)}},10,[11]); __d(function(g,r,i,a,m,e,d){function t(o,n){return m.exports=t=Object.setPrototypeOf||function(t,o){return t.__proto__=o,t},t(o,n)}m.exports=t},11,[]); -__d(function(g,r,i,a,m,e,d){var _;void 0===(_=g||window||this).__filename&&(_.__filename='__filename')},12,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},13,[14]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.for:"@@for"),o=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.element"):60103,u=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.portal"):60106,f=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.fragment"):60107,l=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.strict_mode"):60108,c=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.profiler"):60114,s=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.provider"):60109,y=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.context"):60110,p=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.forward_ref"):60112,b=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense"):60113,v=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense_list"):60120,S=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.memo"):60115,h=n?("function"==typeof Symbol?Symbol.for:"@@for")("react.lazy"):60116;n&&("function"==typeof Symbol?Symbol.for:"@@for")("react.fundamental"),n&&("function"==typeof Symbol?Symbol.for:"@@for")("react.responder");var _="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.iterator:"@@iterator");function k(t){for(var n=t.message,o="https://reactjs.org/docs/error-decoder.html?invariant="+n,u=1;u<arguments.length;u++)o+="&args[]="+encodeURIComponent(arguments[u]);return t.message="Minified React error #"+n+"; visit "+o+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings. ",t}var $={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w={};function C(t,n,o){this.props=t,this.context=n,this.refs=w,this.updater=o||$}function E(){}function R(t,n,o){this.props=t,this.context=n,this.refs=w,this.updater=o||$}C.prototype.isReactComponent={},C.prototype.setState=function(t,n){if("object"!=typeof t&&"function"!=typeof t&&null!=t)throw k(Error(85));this.updater.enqueueSetState(this,t,n,"setState")},C.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")},E.prototype=C.prototype;var x=R.prototype=new E;x.constructor=R,t(x,C.prototype),x.isPureReactComponent=!0;var P={current:null},j={suspense:null},O={current:null},A=Object.prototype.hasOwnProperty,I={key:!0,ref:!0,__self:!0,__source:!0};function U(t,n,u){var f=void 0,l={},c=null,s=null;if(null!=n)for(f in void 0!==n.ref&&(s=n.ref),void 0!==n.key&&(c=""+n.key),n)A.call(n,f)&&!I.hasOwnProperty(f)&&(l[f]=n[f]);var y=arguments.length-2;if(1===y)l.children=u;else if(1<y){for(var p=Array(y),b=0;b<y;b++)p[b]=arguments[b+2];l.children=p}if(t&&t.defaultProps)for(f in y=t.defaultProps)void 0===l[f]&&(l[f]=y[f]);return{$$typeof:o,type:t,key:c,ref:s,props:l,_owner:O.current}}function L(t,n){return{$$typeof:o,type:t.type,key:n,ref:t.ref,props:t.props,_owner:t._owner}}function q(t){return"object"==typeof t&&null!==t&&t.$$typeof===o}function F(t){var n={"=":"=0",":":"=2"};return"$"+(""+t).replace(/[=:]/g,function(t){return n[t]})}var M=/\/+/g,D=[];function V(t,n,o,u){if(D.length){var f=D.pop();return f.result=t,f.keyPrefix=n,f.func=o,f.context=u,f.count=0,f}return{result:t,keyPrefix:n,func:o,context:u,count:0}}function B(t){t.result=null,t.keyPrefix=null,t.func=null,t.context=null,t.count=0,10>D.length&&D.push(t)}function N(t,n,f,l){var c=typeof t;"undefined"!==c&&"boolean"!==c||(t=null);var s=!1;if(null===t)s=!0;else switch(c){case"string":case"number":s=!0;break;case"object":switch(t.$$typeof){case o:case u:s=!0}}if(s)return f(l,t,""===n?"."+z(t,0):n),1;if(s=0,n=""===n?".":n+":",Array.isArray(t))for(var y=0;y<t.length;y++){var p=n+z(c=t[y],y);s+=N(c,p,f,l)}else if(null===t||"object"!=typeof t?p=null:p="function"==typeof(p=_&&t[_]||t["@@iterator"])?p:null,"function"==typeof p)for(t=p.call(t),y=0;!(c=t.next()).done;)s+=N(c=c.value,p=n+z(c,y++),f,l);else if("object"===c)throw f=""+t,k(Error(31),"[object Object]"===f?"object with keys {"+Object.keys(t).join(", ")+"}":f,"");return s}function T(t,n,o){return null==t?0:N(t,"",n,o)}function z(t,n){return"object"==typeof t&&null!==t&&null!=t.key?F(t.key):n.toString(36)}function H(t,n){t.func.call(t.context,n,t.count++)}function W(t,n,o){var u=t.result,f=t.keyPrefix;t=t.func.call(t.context,n,t.count++),Array.isArray(t)?Y(t,u,o,function(t){return t}):null!=t&&(q(t)&&(t=L(t,f+(!t.key||n&&n.key===t.key?"":(""+t.key).replace(M,"$&/")+"/")+o)),u.push(t))}function Y(t,n,o,u,f){var l="";null!=o&&(l=(""+o).replace(M,"$&/")+"/"),T(t,W,n=V(n,l,u,f)),B(n)}function G(){var t=P.current;if(null===t)throw k(Error(321));return t}var J={Children:{map:function(t,n,o){if(null==t)return t;var u=[];return Y(t,u,null,n,o),u},forEach:function(t,n,o){if(null==t)return t;T(t,H,n=V(null,null,n,o)),B(n)},count:function(t){return T(t,function(){return null},null)},toArray:function(t){var n=[];return Y(t,n,null,function(t){return t}),n},only:function(t){if(!q(t))throw k(Error(143));return t}},createRef:function(){return{current:null}},Component:C,PureComponent:R,createContext:function(t,n){return void 0===n&&(n=null),(t={$$typeof:y,_calculateChangedBits:n,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null}).Provider={$$typeof:s,_context:t},t.Consumer=t},forwardRef:function(t){return{$$typeof:p,render:t}},lazy:function(t){return{$$typeof:h,_ctor:t,_status:-1,_result:null}},memo:function(t,n){return{$$typeof:S,type:t,compare:void 0===n?null:n}},useCallback:function(t,n){return G().useCallback(t,n)},useContext:function(t,n){return G().useContext(t,n)},useEffect:function(t,n){return G().useEffect(t,n)},useImperativeHandle:function(t,n,o){return G().useImperativeHandle(t,n,o)},useDebugValue:function(){},useLayoutEffect:function(t,n){return G().useLayoutEffect(t,n)},useMemo:function(t,n){return G().useMemo(t,n)},useReducer:function(t,n,o){return G().useReducer(t,n,o)},useRef:function(t){return G().useRef(t)},useState:function(t){return G().useState(t)},Fragment:f,Profiler:c,StrictMode:l,Suspense:b,unstable_SuspenseList:v,createElement:U,cloneElement:function(n,u,f){if(null===n||void 0===n)throw k(Error(267),n);var l=void 0,c=t({},n.props),s=n.key,y=n.ref,p=n._owner;if(null!=u){void 0!==u.ref&&(y=u.ref,p=O.current),void 0!==u.key&&(s=""+u.key);var b=void 0;for(l in n.type&&n.type.defaultProps&&(b=n.type.defaultProps),u)A.call(u,l)&&!I.hasOwnProperty(l)&&(c[l]=void 0===u[l]&&void 0!==b?b[l]:u[l])}if(1===(l=arguments.length-2))c.children=f;else if(1<l){b=Array(l);for(var v=0;v<l;v++)b[v]=arguments[v+2];c.children=b}return{$$typeof:o,type:n.type,key:s,ref:y,props:c,_owner:p}},createFactory:function(t){var n=U.bind(null,t);return n.type=t,n},isValidElement:q,version:"16.9.0",unstable_withSuspenseConfig:function(t,n){var o=j.suspense;j.suspense=void 0===n?null:n;try{t()}finally{j.suspense=o}},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentDispatcher:P,ReactCurrentBatchConfig:j,ReactCurrentOwner:O,IsSomeRendererActing:{current:!1},assign:t}},K={default:J},Q=K&&J||K;m.exports=Q.default||Q},14,[15]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable;function f(t){if(null===t||void 0===t)throw new TypeError('Object.assign cannot be called with null or undefined');return Object(t)}m.exports=(function(){try{if(!Object.assign)return!1;var n=new String('abc');if(n[5]='de','5'===Object.getOwnPropertyNames(n)[0])return!1;for(var o={},c=0;c<10;c++)o['_'+String.fromCharCode(c)]=c;if('0123456789'!==Object.getOwnPropertyNames(o).map(function(t){return o[t]}).join(''))return!1;var f={};return'abcdefghijklmnopqrst'.split('').forEach(function(t){f[t]=t}),'abcdefghijklmnopqrst'===Object.keys(t({},f)).join('')}catch(t){return!1}})()?Object.assign:function(t,u){for(var s,b,l=f(t),p=1;p<arguments.length;p++){for(var j in s=Object(arguments[p]))o.call(s,j)&&(l[j]=s[j]);if(n){b=n(s);for(var O=0;O<b.length;O++)c.call(s,b[O])&&(l[b[O]]=s[b[O]])}}return l}},15,[16]); -__d(function(g,r,i,a,m,e,d){function t(){return m.exports=t=Object.assign||function(t){for(var n=1;n<arguments.length;n++){var o=arguments[n];for(var p in o)Object.prototype.hasOwnProperty.call(o,p)&&(t[p]=o[p])}return t},t.apply(this,arguments)}m.exports=t},16,[]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);var t=r(d[1]);m.exports={get AccessibilityInfo(){return r(d[2])},get ActivityIndicator(){return r(d[3])},get ART(){return t('art-moved',"React Native ART has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. See https://github.com/react-native-community/art"),r(d[4])},get Button(){return r(d[5])},get CheckBox(){return t('checkBox-moved',"CheckBox has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/checkbox' instead of 'react-native'. See https://github.com/react-native-community/react-native-checkbox"),r(d[6])},get DatePickerIOS(){return t('DatePickerIOS-merged',"DatePickerIOS has been merged with DatePickerAndroid and will be removed in a future release. It can now be installed and imported from '@react-native-community/datetimepicker' instead of 'react-native'. See https://github.com/react-native-community/react-native-datetimepicker"),r(d[7])},get DrawerLayoutAndroid(){return r(d[8])},get FlatList(){return r(d[9])},get Image(){return r(d[10])},get ImageBackground(){return r(d[11])},get InputAccessoryView(){return r(d[12])},get KeyboardAvoidingView(){return r(d[13])},get MaskedViewIOS(){return t('maskedviewios-moved',"MaskedViewIOS has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/masked-view' instead of 'react-native'. See https://github.com/react-native-community/react-native-masked-view"),r(d[14])},get Modal(){return r(d[15])},get Picker(){return r(d[16])},get PickerIOS(){return r(d[17])},get ProgressBarAndroid(){return r(d[18])},get ProgressViewIOS(){return r(d[19])},get SafeAreaView(){return r(d[20])},get ScrollView(){return r(d[21])},get SectionList(){return r(d[22])},get SegmentedControlIOS(){return r(d[23])},get Slider(){return t('slider-moved',"Slider has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/slider' instead of 'react-native'. See https://github.com/react-native-community/react-native-slider"),r(d[24])},get Switch(){return r(d[25])},get RefreshControl(){return r(d[26])},get StatusBar(){return r(d[27])},get Text(){return r(d[28])},get TextInput(){return r(d[29])},get Touchable(){return r(d[30])},get TouchableHighlight(){return r(d[31])},get TouchableNativeFeedback(){return r(d[32])},get TouchableOpacity(){return r(d[33])},get TouchableWithoutFeedback(){return r(d[34])},get View(){return r(d[35])},get VirtualizedList(){return r(d[36])},get VirtualizedSectionList(){return r(d[37])},get ActionSheetIOS(){return r(d[38])},get Alert(){return r(d[39])},get Animated(){return r(d[40])},get AppRegistry(){return r(d[41])},get AppState(){return r(d[42])},get AsyncStorage(){return t('async-storage-moved',"AsyncStorage has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/async-storage' instead of 'react-native'. See https://github.com/react-native-community/react-native-async-storage"),r(d[43])},get BackHandler(){return r(d[44])},get Clipboard(){return r(d[45])},get DatePickerAndroid(){return t('DatePickerAndroid-merged',"DatePickerAndroid has been merged with DatePickerIOS and will be removed in a future release. It can now be installed and imported from '@react-native-community/datetimepicker' instead of 'react-native'. See https://github.com/react-native-community/react-native-datetimepicker"),r(d[46])},get DeviceInfo(){return r(d[47])},get Dimensions(){return r(d[48])},get Easing(){return r(d[49])},get findNodeHandle(){return r(d[50]).findNodeHandle},get I18nManager(){return r(d[51])},get ImagePickerIOS(){return t('imagePickerIOS-moved',"ImagePickerIOS has been extracted from react-native core and will be removed in a future release. Please upgrade to use either '@react-native-community/react-native-image-picker' or 'expo-image-picker'. If you cannot upgrade to a different library, please install the deprecated '@react-native-community/image-picker-ios' package. See https://github.com/react-native-community/react-native-image-picker-ios"),r(d[52])},get InteractionManager(){return r(d[53])},get Keyboard(){return r(d[54])},get LayoutAnimation(){return r(d[55])},get Linking(){return r(d[56])},get NativeDialogManagerAndroid(){return r(d[57]).default},get NativeEventEmitter(){return r(d[58])},get PanResponder(){return r(d[59])},get PermissionsAndroid(){return r(d[60])},get PixelRatio(){return r(d[61])},get PushNotificationIOS(){return t('pushNotificationIOS-moved',"PushNotificationIOS has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-community/push-notification-ios' instead of 'react-native'. See https://github.com/react-native-community/react-native-push-notification-ios"),r(d[62])},get Settings(){return r(d[63])},get Share(){return r(d[64])},get StatusBarIOS(){return t('StatusBarIOS-merged','StatusBarIOS has been merged with StatusBar and will be removed in a future release. Use StatusBar for mutating the status bar'),r(d[65])},get StyleSheet(){return r(d[66])},get Systrace(){return r(d[67])},get TimePickerAndroid(){return t('TimePickerAndroid-merged',"TimePickerAndroid has been merged with DatePickerIOS and DatePickerAndroid and will be removed in a future release. It can now be installed and imported from '@react-native-community/datetimepicker' instead of 'react-native'. See https://github.com/react-native-community/react-native-datetimepicker"),r(d[68])},get ToastAndroid(){return r(d[69])},get TurboModuleRegistry(){return r(d[70])},get TVEventHandler(){return r(d[71])},get UIManager(){return r(d[72])},get unstable_batchedUpdates(){return r(d[50]).unstable_batchedUpdates},get useWindowDimensions(){return r(d[73]).default},get UTFSequence(){return r(d[74])},get Vibration(){return r(d[75])},get YellowBox(){return r(d[76])},get DeviceEventEmitter(){return r(d[77])},get NativeAppEventEmitter(){return r(d[78])},get NativeModules(){return r(d[79])},get Platform(){return r(d[80])},get processColor(){return r(d[81])},get requireNativeComponent(){return r(d[82])},get unstable_RootTagContext(){return r(d[83])},get ColorPropType(){return r(d[84])},get EdgeInsetsPropType(){return r(d[85])},get PointPropType(){return r(d[86])},get ViewPropTypes(){return r(d[87])}}},17,[18,19,22,53,186,196,286,288,290,246,267,291,292,294,295,297,304,306,308,309,311,253,274,313,315,317,250,320,197,322,203,331,212,213,279,88,247,275,332,140,214,334,348,351,345,353,355,356,62,242,90,302,357,223,257,259,359,141,123,361,363,61,365,367,369,371,60,37,372,374,24,207,75,375,376,377,379,46,151,25,58,82,182,301,66,199,380,324]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(n,o,t,f,s,u,c,l){if(!n){var v;if(void 0===o)v=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var p=[t,f,s,u,c,l],h=0;(v=new Error(o.replace(/%s/g,function(){return p[h++]}))).name='Invariant Violation'}throw v.framesToPop=1,v}}},18,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n={};m.exports=function(c,o){n[c]||(t(!1,o),n[c]=!0)}},19,[20]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t},20,[21]); -__d(function(g,r,i,a,m,e,d){"use strict";function t(t){return function(){return t}}var n=function(){};n.thatReturns=t,n.thatReturnsFalse=t(!1),n.thatReturnsTrue=t(!0),n.thatReturnsNull=t(null),n.thatReturnsThis=function(){return this},n.thatReturnsArgument=function(t){return t},m.exports=n},21,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0])(r(d[1])),t=r(d[2]),u=r(d[3]),c={announcementFinished:'announcementFinished',boldTextChanged:'boldTextChanged',grayscaleChanged:'grayscaleChanged',invertColorsChanged:'invertColorsChanged',reduceMotionChanged:'reduceMotionChanged',reduceTransparencyChanged:'reduceTransparencyChanged',screenReaderChanged:'screenReaderChanged'},o=new Map,s={isBoldTextEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentBoldTextState(t,u):u(u)})},isGrayscaleEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentGrayscaleState(t,u):u(u)})},isInvertColorsEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentInvertColorsState(t,u):u(u)})},isReduceMotionEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentReduceMotionState(t,u):u(u)})},isReduceTransparencyEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentReduceTransparencyState(t,u):u(u)})},isScreenReaderEnabled:function(){return new t(function(t,u){n.default?n.default.getCurrentVoiceOverState(t,u):u(u)})},get fetch(){return this.isScreenReaderEnabled},addEventListener:function(n,t){var l;return'change'===n?l=u.addListener(c.screenReaderChanged,t):c[n]&&(l=u.addListener(n,t)),o.set(t,l),{remove:s.removeEventListener.bind(null,n,t)}},setAccessibilityFocus:function(t){n.default&&n.default.setAccessibilityFocus(t)},announceForAccessibility:function(t){n.default&&n.default.announceForAccessibility(t)},removeEventListener:function(n,t){var u=o.get(t);u&&(u.remove(),o.delete(t))}};m.exports=s},22,[3,23,41,46]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('AccessibilityManager');e.default=u},23,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.get=o,e.getEnforcing=function(n){var u=o(n);return(0,t.default)(null!=u,"TurboModuleRegistry.getEnforcing(...): '"+n+"' could not be found. Verify that a module by this name is registered in the native binary."),u};var t=n(r(d[1])),u=r(d[2]),l=g.__turboModuleProxy;function o(n){if(!g.RN$Bridgeless){var t=u[n];if(null!=t)return t}return null!=l?l(n):null}},24,[3,18,25]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=r(d[2]),u=r(d[3]);function l(o,l){if(!o)return null;var f=t(o,5),v=f[0],h=f[1],y=f[2],C=f[3],p=f[4];if(u(!v.startsWith('RCT')&&!v.startsWith('RK'),"Module name prefixes should've been stripped by the native side but wasn't for "+v),!h&&!y)return{name:v};var M={};return y&&y.forEach(function(n,t){var o=C&&c(C,t),f=p&&c(p,t);u(!o||!f,'Cannot have a method that is both async and a sync hook');var v=o?'promise':f?'sync':'async';M[n]=s(l,t,v)}),n(M,h),null==M.getConstants?M.getConstants=function(){return h||Object.freeze({})}:console.warn("Unable to define method 'getConstants()' on NativeModule '"+v+"'. NativeModule '"+v+"' already has a constant or method called 'getConstants'. Please remove it."),{name:v,module:M}}function f(n,t){u(g.nativeRequireModuleConfig,"Can't lazily create module without nativeRequireModuleConfig");var o=l(g.nativeRequireModuleConfig(n),t);return o&&o.module}function s(n,t,l){var f=null;return(f='promise'===l?function(){for(var u=arguments.length,l=new Array(u),f=0;f<u;f++)l[f]=arguments[f];var s=new Error;return s.framesToPop=1,new Promise(function(u,f){o.enqueueNativeCall(n,t,l,function(n){return u(n)},function(n){return f(v(n,s))})})}:function(){for(var f=arguments.length,s=new Array(f),c=0;c<f;c++)s[c]=arguments[c];var v=s.length>0?s[s.length-1]:null,h=s.length>1?s[s.length-2]:null,y='function'==typeof v,C='function'==typeof h;C&&u(y,'Cannot have a non-function arg after a function arg.');var p=y?v:null,M=C?h:null,b=y+C;if(s=s.slice(0,s.length-b),'sync'===l)return o.callNativeSyncHook(n,t,s,M,p);o.enqueueNativeCall(n,t,s,M,p)}).type=l,f}function c(n,t){return-1!==n.indexOf(t)}function v(t,o){return n(o,t||{})}g.__fbGenNativeModule=l;var h={};if(g.nativeModuleProxy)h=g.nativeModuleProxy;else if(!g.nativeExtensions){var y=g.__fbBatchedBridgeConfig;u(y,'__fbBatchedBridgeConfig is not set, cannot invoke native modules');var C=r(d[4]);(y.remoteModuleConfig||[]).forEach(function(n,t){var o=l(n,t);o&&(o.module?h[o.name]=o.module:C(h,o.name,{get:function(){return f(o.name,t)}}))})}m.exports=h},25,[16,26,30,18,40]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]);m.exports=function(u,c){return n(u)||t(u,c)||o()}},26,[27,28,29]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){if(Array.isArray(n))return n}},27,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(t,n){var o=[],l=!0,u=!1,f=void 0;try{for(var y,c=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(l=(y=c.next()).done)&&(o.push(y.value),!n||o.length!==n);l=!0);}catch(t){u=!0,f=t}finally{try{l||null==c.return||c.return()}finally{if(u)throw f}}return o}},28,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},29,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=new(r(d[0]));Object.defineProperty(g,'__fbBatchedBridge',{configurable:!0,value:t}),m.exports=t},30,[31]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),l=r(d[1]),u=r(d[2]),s=r(d[3]),n=r(d[4]),h=(r(d[5]),r(d[6])),o=r(d[7]),c=(function(){function c(){l(this,c),this._lazyCallableModules={},this._queue=[[],[],[],0],this._successCallbacks=new Map,this._failureCallbacks=new Map,this._callID=0,this._lastFlush=0,this._eventLoopStartTime=Date.now(),this._immediatesCallback=null,this.callFunctionReturnFlushedQueue=this.callFunctionReturnFlushedQueue.bind(this),this.callFunctionReturnResultAndFlushedQueue=this.callFunctionReturnResultAndFlushedQueue.bind(this),this.flushedQueue=this.flushedQueue.bind(this),this.invokeCallbackAndReturnFlushedQueue=this.invokeCallbackAndReturnFlushedQueue.bind(this)}return u(c,[{key:"callFunctionReturnFlushedQueue",value:function(t,l,u){var s=this;return this.__guard(function(){s.__callFunction(t,l,u)}),this.flushedQueue()}},{key:"callFunctionReturnResultAndFlushedQueue",value:function(t,l,u){var s,n=this;return this.__guard(function(){s=n.__callFunction(t,l,u)}),[s,this.flushedQueue()]}},{key:"invokeCallbackAndReturnFlushedQueue",value:function(t,l){var u=this;return this.__guard(function(){u.__invokeCallback(t,l)}),this.flushedQueue()}},{key:"flushedQueue",value:function(){var t=this;this.__guard(function(){t.__callImmediates()});var l=this._queue;return this._queue=[[],[],[],this._callID],l[0].length?l:null}},{key:"getEventLoopRunningTime",value:function(){return Date.now()-this._eventLoopStartTime}},{key:"registerCallableModule",value:function(t,l){this._lazyCallableModules[t]=function(){return l}}},{key:"registerLazyCallableModule",value:function(t,l){var u,s=l;this._lazyCallableModules[t]=function(){return s&&(u=s(),s=null),u}}},{key:"getCallableModule",value:function(t){var l=this._lazyCallableModules[t];return l?l():null}},{key:"callNativeSyncHook",value:function(t,l,u,s,n){this.processCallbacks(t,l,u,s,n);try{return g.nativeCallSyncHook(t,l,u)}catch(t){throw'object'==typeof t&&null!=t&&void 0===t.framesToPop&&/^Exception in HostFunction: /.test(t.message)&&(t.framesToPop=2),t}}},{key:"processCallbacks",value:function(t,l,u,s,n){(s||n)&&(s&&u.push(this._callID<<1),n&&u.push(this._callID<<1|1),this._successCallbacks.set(this._callID,n),this._failureCallbacks.set(this._callID,s)),this._callID++}},{key:"enqueueNativeCall",value:function(t,l,u,s,h){this.processCallbacks(t,l,u,s,h),this._queue[0].push(t),this._queue[1].push(l),this._queue[2].push(u);var o=Date.now();if(g.nativeFlushQueueImmediate&&o-this._lastFlush>=5){var c=this._queue;this._queue=[[],[],[],this._callID],this._lastFlush=o,g.nativeFlushQueueImmediate(c)}n.counterEvent('pending_js_to_native_queue',this._queue[0].length),this.__spy&&this.__spy({type:1,module:t+'',method:l,args:u})}},{key:"createDebugLookup",value:function(t,l,u){}},{key:"setImmediatesCallback",value:function(t){this._immediatesCallback=t}},{key:"__guard",value:function(t){if(this.__shouldPauseOnThrow())t();else try{t()}catch(t){s.reportFatalError(t)}}},{key:"__shouldPauseOnThrow",value:function(){return'undefined'!=typeof DebuggerInternal&&!0===DebuggerInternal.shouldPauseOnThrow}},{key:"__callImmediates",value:function(){n.beginEvent('JSTimers.callImmediates()'),null!=this._immediatesCallback&&this._immediatesCallback(),n.endEvent()}},{key:"__callFunction",value:function(t,l,u){this._lastFlush=Date.now(),this._eventLoopStartTime=this._lastFlush,this.__spy?n.beginEvent(t+"."+l+"("+o(u)+")"):n.beginEvent(t+"."+l+"(...)"),this.__spy&&this.__spy({type:0,module:t,method:l,args:u});var s=this.getCallableModule(t);h(!!s,'Module %s is not a registered callable module (calling %s)',t,l),h(!!s[l],'Method %s does not exist on module %s',l,t);var c=s[l].apply(s,u);return n.endEvent(),c}},{key:"__invokeCallback",value:function(l,u){this._lastFlush=Date.now(),this._eventLoopStartTime=this._lastFlush;var s=l>>>1,n=1&l?this._successCallbacks.get(s):this._failureCallbacks.get(s);n&&(this._successCallbacks.delete(s),this._failureCallbacks.delete(s),n.apply(void 0,t(u)))}}],[{key:"spy",value:function(t){c.prototype.__spy=!0===t?function(t){console.log((0===t.type?'N->JS':'JS->N')+" : "+(t.module?t.module+'.':'')+t.method+"("+JSON.stringify(t.args)+")")}:!1===t?null:t}}]),c})();m.exports=c},31,[32,4,5,36,37,38,18,39]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]);m.exports=function(u){return n(u)||t(u)||o()}},32,[33,34,35]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){if(Array.isArray(n)){for(var t=0,f=new Array(n.length);t<n.length;t++)f[t]=n[t];return f}}},33,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(t){if(("function"==typeof Symbol?Symbol.iterator:"@@iterator")in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}},34,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},35,[]); -__d(function(g,r,i,a,m,e,d){m.exports=g.ErrorUtils},36,[]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);var n=!1,t=0,c={installReactHook:function(){!0},setEnabled:function(t){n!==t&&(n=t)},isEnabled:function(){return n},beginEvent:function(t,c){n&&(t='function'==typeof t?t():t,g.nativeTraceBeginSection(131072,t,c))},endEvent:function(){n&&g.nativeTraceEndSection(131072)},beginAsyncEvent:function(c){var o=t;return n&&(t++,c='function'==typeof c?c():c,g.nativeTraceBeginAsyncSection(131072,c,o)),o},endAsyncEvent:function(t,c){n&&(t='function'==typeof t?t():t,g.nativeTraceEndAsyncSection(131072,t,c))},counterEvent:function(t,c){n&&(t='function'==typeof t?t():t,g.nativeTraceCounter&&g.nativeTraceCounter(131072,t,c))}};m.exports=c},37,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(t){return t}},38,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(t){var n,f=typeof t;if(void 0===t)n='undefined';else if(null===t)n='null';else if('string'===f)n='"'+t+'"';else if('function'===f)try{n=t.toString()}catch(t){n='[function unknown]'}else if(t instanceof Error)n=t.name+': '+t.message;else try{n=JSON.stringify(t)}catch(f){if('function'==typeof t.toString)try{n=t.toString()}catch(t){}}return n||'["'+f+'" failed to stringify]'}},39,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(t,n,u){var b,c=u.get,o=!1!==u.enumerable,f=!1!==u.writable,l=!1;function s(u){b=u,l=!0,Object.defineProperty(t,n,{value:u,configurable:!0,enumerable:o,writable:f})}Object.defineProperty(t,n,{get:function(){return l||(l=!0,s(c())),b},set:s,configurable:!0,enumerable:o})}},40,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);r(d[1]),r(d[2]),m.exports=t},41,[42,44,45]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=n;var t=h(!0),o=h(!1),f=h(null),u=h(void 0),c=h(0),l=h('');function h(t){var o=new n(n._61);return o._65=1,o._55=t,o}n.resolve=function(v){if(v instanceof n)return v;if(null===v)return f;if(void 0===v)return u;if(!0===v)return t;if(!1===v)return o;if(0===v)return c;if(''===v)return l;if('object'==typeof v||'function'==typeof v)try{var p=v.then;if('function'==typeof p)return new n(p.bind(v))}catch(t){return new n(function(n,o){o(t)})}return h(v)},n.all=function(t){var o=Array.prototype.slice.call(t);return new n(function(t,f){if(0===o.length)return t([]);var u=o.length;function c(l,h){if(h&&('object'==typeof h||'function'==typeof h)){if(h instanceof n&&h.then===n.prototype.then){for(;3===h._65;)h=h._55;return 1===h._65?c(l,h._55):(2===h._65&&f(h._55),void h.then(function(n){c(l,n)},f))}var v=h.then;if('function'==typeof v)return void new n(v.bind(h)).then(function(n){c(l,n)},f)}o[l]=h,0==--u&&t(o)}for(var l=0;l<o.length;l++)c(l,o[l])})},n.reject=function(t){return new n(function(n,o){o(t)})},n.race=function(t){return new n(function(o,f){t.forEach(function(t){n.resolve(t).then(o,f)})})},n.prototype.catch=function(n){return this.then(null,n)}},42,[43]); -__d(function(g,r,i,a,m,e,d){'use strict';function n(){}var t=null,o={};function u(n){try{return n.then}catch(n){return t=n,o}}function f(n,u){try{return n(u)}catch(n){return t=n,o}}function c(n,u,f){try{n(u,f)}catch(n){return t=n,o}}function _(t){if('object'!=typeof this)throw new TypeError('Promises must be constructed via new');if('function'!=typeof t)throw new TypeError('Promise constructor\'s argument is not a function');this._40=0,this._65=0,this._55=null,this._72=null,t!==n&&b(t,this)}function s(t,o,u){return new t.constructor(function(f,c){var s=new _(n);s.then(f,c),l(t,new w(o,u,s))})}function l(n,t){for(;3===n._65;)n=n._55;if(_._37&&_._37(n),0===n._65)return 0===n._40?(n._40=1,void(n._72=t)):1===n._40?(n._40=2,void(n._72=[n._72,t])):void n._72.push(t);h(n,t)}function h(n,u){setImmediate(function(){var c=1===n._65?u.onFulfilled:u.onRejected;if(null!==c){var _=f(c,n._55);_===o?v(u.promise,t):p(u.promise,_)}else 1===n._65?p(u.promise,n._55):v(u.promise,n._55)})}function p(n,f){if(f===n)return v(n,new TypeError('A promise cannot be resolved with itself.'));if(f&&('object'==typeof f||'function'==typeof f)){var c=u(f);if(c===o)return v(n,t);if(c===n.then&&f instanceof _)return n._65=3,n._55=f,void y(n);if('function'==typeof c)return void b(c.bind(f),n)}n._65=1,n._55=f,y(n)}function v(n,t){n._65=2,n._55=t,_._87&&_._87(n,t),y(n)}function y(n){if(1===n._40&&(l(n,n._72),n._72=null),2===n._40){for(var t=0;t<n._72.length;t++)l(n,n._72[t]);n._72=null}}function w(n,t,o){this.onFulfilled='function'==typeof n?n:null,this.onRejected='function'==typeof t?t:null,this.promise=o}function b(n,u){var f=!1,_=c(n,function(n){f||(f=!0,p(u,n))},function(n){f||(f=!0,v(u,n))});f||_!==o||(f=!0,v(u,t))}m.exports=_,_._37=null,_._87=null,_._61=n,_.prototype.then=function(t,o){if(this.constructor!==_)return s(this,t,o);var u=new _(n);return l(this,new w(t,o,u)),u}},43,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t,t.prototype.done=function(t,n){(arguments.length?this.then.apply(this,arguments):this).then(null,function(t){setTimeout(function(){throw t},0)})}},44,[43]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=n,n.prototype.finally=function(t){return this.then(function(o){return n.resolve(t()).then(function(){return o})},function(o){return n.resolve(t()).then(function(){throw o})})}},45,[43]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),u=r(d[4]),c=r(d[5]),l=r(d[6]),h=r(d[7]),v=(function(v){function p(){var n;t(this,p);var u=new h;return(n=s(this,o(p).call(this,u))).sharedSubscriber=u,n}return c(p,l),n(p,[{key:"addListener",value:function(t,n,s){return u(o(p.prototype),"addListener",this).call(this,t,n,s)}},{key:"removeAllListeners",value:function(t){u(o(p.prototype),"removeAllListeners",this).call(this,t)}},{key:"removeSubscription",value:function(t){t.emitter!==this?t.emitter.removeSubscription(t):u(o(p.prototype),"removeSubscription",this).call(this,t)}}]),p})();m.exports=new v},46,[4,5,6,9,47,10,49,52]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);function n(c,f,o){return"undefined"!=typeof Reflect&&Reflect.get?m.exports=n=Reflect.get:m.exports=n=function(n,c,f){var o=t(n,c);if(o){var u=Object.getOwnPropertyDescriptor(o,c);return u.get?u.get.call(f):u.value}},n(c,f,o||c)}m.exports=n},47,[48]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=function(n,o){for(;!Object.prototype.hasOwnProperty.call(n,o)&&null!==(n=t(n)););return n}},48,[9]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),u=r(d[3]),o=r(d[4]),c=function(){return!0},b=(function(){function b(n){t(this,b),this._subscriber=n||new u}return n(b,[{key:"addListener",value:function(t,n,u){return this._subscriber.addSubscription(t,new s(this,this._subscriber,n,u))}},{key:"once",value:function(t,n,s){var u=this;return this.addListener(t,function(){u.removeCurrentListener();for(var t=arguments.length,o=new Array(t),c=0;c<t;c++)o[c]=arguments[c];n.apply(s,o)})}},{key:"removeAllListeners",value:function(t){this._subscriber.removeAllSubscriptions(t)}},{key:"removeCurrentListener",value:function(){o(!!this._currentSubscription,'Not in an emitting cycle; there is no current subscription'),this.removeSubscription(this._currentSubscription)}},{key:"removeSubscription",value:function(t){o(t.emitter===this,'Subscription does not belong to this emitter.'),this._subscriber.removeSubscription(t)}},{key:"listeners",value:function(t){var n=this._subscriber.getSubscriptionsForType(t);return n?n.filter(c).map(function(t){return t.listener}):[]}},{key:"emit",value:function(t){var n=this._subscriber.getSubscriptionsForType(t);if(n){for(var s=0,u=n.length;s<u;s++){var o=n[s];o&&o.listener&&(this._currentSubscription=o,o.listener.apply(o.context,Array.prototype.slice.call(arguments,1)))}this._currentSubscription=null}}},{key:"removeListener",value:function(t,n){var s=this._subscriber.getSubscriptionsForType(t);if(s)for(var u=0,o=s.length;u<o;u++){var c=s[u];c&&c.listener===n&&c.remove()}}}]),b})();m.exports=b},49,[4,5,50,52,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),u=r(d[4]),c=r(d[5]),h=(function(h){function v(n,u,c,h){var f;return t(this,v),(f=s(this,o(v).call(this,u))).emitter=n,f.listener=c,f.context=h,f}return u(v,c),n(v,[{key:"remove",value:function(){this.emitter.removeSubscription(this)}}]),v})();m.exports=h},50,[4,5,6,9,10,51]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n=(function(){function n(s){t(this,n),this.subscriber=s}return s(n,[{key:"remove",value:function(){this.subscriber.removeSubscription(this)}}]),n})();m.exports=n},51,[4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';var s=r(d[0]),t=r(d[1]),n=r(d[2]),o=(function(){function o(){s(this,o),this._subscriptionsForType={},this._currentSubscription=null}return t(o,[{key:"addSubscription",value:function(s,t){n(t.subscriber===this,'The subscriber of the subscription is incorrectly set.'),this._subscriptionsForType[s]||(this._subscriptionsForType[s]=[]);var o=this._subscriptionsForType[s].length;return this._subscriptionsForType[s].push(t),t.eventType=s,t.key=o,t}},{key:"removeAllSubscriptions",value:function(s){void 0===s?this._subscriptionsForType={}:delete this._subscriptionsForType[s]}},{key:"removeSubscription",value:function(s){var t=s.eventType,n=s.key,o=this._subscriptionsForType[t];o&&delete o[n]}},{key:"getSubscriptionsForType",value:function(s){return this._subscriptionsForType[s]}}]),o})();m.exports=o},52,[4,5,18]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);var t=r(d[1]),s=r(d[2]),l=(r(d[3]),r(d[4])),n=r(d[5]),o=r(d[6]),c=r(d[7]).default,h=l.forwardRef(function(h,y){var z,f,p=h.onLayout,v=h.style,w=h.size,L=s(h,["onLayout","style","size"]);switch(w){case'small':z=u.sizeSmall,f='small';break;case'large':z=u.sizeLarge,f='large';break;default:z={height:h.size,width:h.size}}var S=t({},L,{ref:y,style:z,size:f});return l.createElement(o,{onLayout:p,style:n.compose(u.container,v)},l.createElement(c,S))});h.displayName='ActivityIndicator',h.defaultProps={animating:!0,color:'#999999',hidesWhenStopped:!0,size:'small'};var u=n.create({container:{alignItems:'center',justifyContent:'center'},sizeSmall:{width:20,height:20},sizeLarge:{width:36,height:36}});m.exports=h},53,[16,54,56,58,13,60,88,184]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=function(n){for(var o=1;o<arguments.length;o++){var c=null!=arguments[o]?arguments[o]:{},f=Object.keys(c);'function'==typeof Object.getOwnPropertySymbols&&(f=f.concat(Object.getOwnPropertySymbols(c).filter(function(t){return Object.getOwnPropertyDescriptor(c,t).enumerable}))),f.forEach(function(o){t(n,o,c[o])})}return n}},54,[55]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n,t,u){return t in n?Object.defineProperty(n,t,{value:u,enumerable:!0,configurable:!0,writable:!0}):n[t]=u,n}},55,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=function(n,o){if(null==n)return{};var l,p,b=t(n,o);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(n);for(p=0;p<c.length;p++)l=c[p],o.indexOf(l)>=0||Object.prototype.propertyIsEnumerable.call(n,l)&&(b[l]=n[l])}return b}},56,[57]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n,t){if(null==n)return{};var f,u,o={},c=Object.keys(n);for(u=0;u<c.length;u++)f=c[u],t.indexOf(f)>=0||(o[f]=n[f]);return o}},57,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),s={__constants:null,OS:'ios',get Version(){return this.constants.osVersion},get constants(){return null==this.__constants&&(this.__constants=t.default.getConstants()),this.__constants},get isPad(){return'pad'===this.constants.interfaceIdiom},get isTVOS(){return s.isTV},get isTV(){return'tv'===this.constants.interfaceIdiom},get isTesting(){return!1},select:function(t){return'ios'in t?t.ios:t.default}};m.exports=s},58,[3,59]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('PlatformConstants');e.default=n},59,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]),l=r(d[2]),n=(r(d[3]),r(d[4])),s=o.roundToNearestPixel(.4);0===s&&(s=1/o.get());var u={position:'absolute',left:0,right:0,top:0,bottom:0};m.exports={hairlineWidth:s,absoluteFill:u,absoluteFillObject:u,compose:function(t,o){return null!=t&&null!=o?[t,o]:null!=t?t:o},flatten:n,setStyleAttributePreprocessor:function(o,n){var s;if(!0===l[o])s={};else{if('object'!=typeof l[o])return void console.error(o+" is not a valid style attribute");s=l[o]}l[o]=t({},s,{process:n})},create:function(t){return t}}},60,[54,61,64,86,87]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),u=r(d[2]),o=(function(){function o(){t(this,o)}return n(o,null,[{key:"get",value:function(){return u.get('window').scale}},{key:"getFontScale",value:function(){return u.get('window').fontScale||o.get()}},{key:"getPixelSizeForLayoutSize",value:function(t){return Math.round(t*o.get())}},{key:"roundToNearestPixel",value:function(t){var n=o.get();return Math.round(t*n)/n}},{key:"startDetecting",value:function(){}}]),o})();m.exports=o},61,[4,5,62]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,t=r(d[0]),s=t(r(d[1])),o=t(r(d[2])),l=t(r(d[3])),c=t(r(d[4])),u=t(r(d[5])),f=t(r(d[6])),v=new l.default,h=!1,w=(function(){function t(){(0,s.default)(this,t)}return(0,o.default)(t,null,[{key:"get",value:function(t){return(0,f.default)(n[t],'No dimension set for key '+t),n[t]}},{key:"set",value:function(t){var s=t.screen,o=t.window,l=t.windowPhysicalPixels;l&&(o={width:l.width/l.scale,height:l.height/l.scale,scale:l.scale,fontScale:l.fontScale});var c=t.screenPhysicalPixels;c?s={width:c.width/c.scale,height:c.height/c.scale,scale:c.scale,fontScale:c.fontScale}:null==s&&(s=o),n={window:o,screen:s},h?v.emit('change',n):h=!0}},{key:"addEventListener",value:function(n,t){(0,f.default)('change'===n,'Trying to subscribe to unknown event: "%s"',n),v.addListener(n,t)}},{key:"removeEventListener",value:function(n,t){(0,f.default)('change'===n,'Trying to remove listener for unknown event: "%s"',n),v.removeListener(n,t)}}]),t})(),y=g.nativeExtensions&&g.nativeExtensions.DeviceInfo&&g.nativeExtensions.DeviceInfo.Dimensions;y||(c.default.addListener('didUpdateDimensions',function(n){w.set(n)}),y=u.default.getConstants().Dimensions),w.set(y),m.exports=w},62,[3,4,5,49,46,63,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var f=t(r(d[1])).getEnforcing('DeviceInfo');e.default=f},63,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';for(var o=r(d[0]),t=r(d[1]),l=r(d[2]),C=r(d[3]),s=r(d[4]),b=r(d[5]),c=r(d[6]),f={},n=Object.keys(o({},C,l,t)),h=0;h<n.length;h++){f[n[h]]=!0}f.transform={process:b},f.shadowOffset={diff:c};var p={process:s};f.backgroundColor=p,f.borderBottomColor=p,f.borderColor=p,f.borderLeftColor=p,f.borderRightColor=p,f.borderTopColor=p,f.borderStartColor=p,f.borderEndColor=p,f.color=p,f.shadowColor=p,f.textDecorationColor=p,f.tintColor=p,f.textShadowColor=p,f.overlayColor=p,m.exports=f},64,[54,65,80,81,82,83,85]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),t=r(d[1]),n=r(d[2]),b=r(d[3]),u=r(d[4]),s=r(d[5]),c=o({},n,b,u,{resizeMode:s.oneOf(['center','contain','cover','repeat','stretch']),backfaceVisibility:s.oneOf(['visible','hidden']),backgroundColor:t,borderColor:t,borderWidth:s.number,borderRadius:s.number,overflow:s.oneOf(['visible','hidden']),tintColor:t,opacity:s.number,overlayColor:s.string,borderTopLeftRadius:s.number,borderTopRightRadius:s.number,borderBottomLeftRadius:s.number,borderBottomRightRadius:s.number});m.exports=c},65,[54,66,68,72,73,69]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),f=function(f,l,o,b,t,u){var s=l[o];return void 0===s||null===s?f?new Error('Required '+t+' `'+(u||o)+'` was not specified in `'+b+'`.'):void 0:'number'!=typeof s&&null===n(s)?new Error('Invalid '+t+' `'+(u||o)+'` supplied to `'+b+'`: '+s+"\nValid color formats are\n - '#f0f' (#rgb)\n - '#f0fc' (#rgba)\n - '#ff00ff' (#rrggbb)\n - '#ff00ff00' (#rrggbbaa)\n - 'rgb(255, 255, 255)'\n - 'rgba(255, 255, 255, 1.0)'\n - 'hsl(360, 100%, 100%)'\n - 'hsla(360, 100%, 100%, 1.0)'\n - 'transparent'\n - 'red'\n - 0xff00ff00 (0xrrggbbaa)\n"):void 0},l=f.bind(null,!1);l.isRequired=f.bind(null,!0),m.exports=l},66,[67]); -__d(function(g,r,i,a,m,e,d){'use strict';function l(l,n,t){return t<0&&(t+=1),t>1&&(t-=1),t<.16666666666666666?l+6*(n-l)*t:t<.5?n:t<.6666666666666666?l+(n-l)*(.6666666666666666-t)*6:l}function n(n,t,o){var u=o<.5?o*(1+t):o+t-o*t,s=2*o-u,h=l(s,u,n+.3333333333333333),c=l(s,u,n),b=l(s,u,n-.3333333333333333);return Math.round(255*h)<<24|Math.round(255*c)<<16|Math.round(255*b)<<8}var t,o='[-+]?\\d*\\.?\\d+',u="[-+]?\\d*\\.?\\d+%";function s(){for(var l=arguments.length,n=new Array(l),t=0;t<l;t++)n[t]=arguments[t];return'\\(\\s*('+n.join(')\\s*,\\s*(')+')\\s*\\)'}function h(l){var n=parseInt(l,10);return n<0?0:n>255?255:n}function c(l){return(parseFloat(l)%360+360)%360/360}function b(l){var n=parseFloat(l);return n<0?0:n>1?255:Math.round(255*n)}function p(l){var n=parseFloat(l);return n<0?0:n>100?1:n/100}var y={transparent:0,aliceblue:4042850303,antiquewhite:4209760255,aqua:16777215,aquamarine:2147472639,azure:4043309055,beige:4126530815,bisque:4293182719,black:255,blanchedalmond:4293643775,blue:65535,blueviolet:2318131967,brown:2771004159,burlywood:3736635391,burntsienna:3934150143,cadetblue:1604231423,chartreuse:2147418367,chocolate:3530104575,coral:4286533887,cornflowerblue:1687547391,cornsilk:4294499583,crimson:3692313855,cyan:16777215,darkblue:35839,darkcyan:9145343,darkgoldenrod:3095792639,darkgray:2846468607,darkgreen:6553855,darkgrey:2846468607,darkkhaki:3182914559,darkmagenta:2332068863,darkolivegreen:1433087999,darkorange:4287365375,darkorchid:2570243327,darkred:2332033279,darksalmon:3918953215,darkseagreen:2411499519,darkslateblue:1211993087,darkslategray:793726975,darkslategrey:793726975,darkturquoise:13554175,darkviolet:2483082239,deeppink:4279538687,deepskyblue:12582911,dimgray:1768516095,dimgrey:1768516095,dodgerblue:512819199,firebrick:2988581631,floralwhite:4294635775,forestgreen:579543807,fuchsia:4278255615,gainsboro:3705462015,ghostwhite:4177068031,gold:4292280575,goldenrod:3668254975,gray:2155905279,green:8388863,greenyellow:2919182335,grey:2155905279,honeydew:4043305215,hotpink:4285117695,indianred:3445382399,indigo:1258324735,ivory:4294963455,khaki:4041641215,lavender:3873897215,lavenderblush:4293981695,lawngreen:2096890111,lemonchiffon:4294626815,lightblue:2916673279,lightcoral:4034953471,lightcyan:3774873599,lightgoldenrodyellow:4210742015,lightgray:3553874943,lightgreen:2431553791,lightgrey:3553874943,lightpink:4290167295,lightsalmon:4288707327,lightseagreen:548580095,lightskyblue:2278488831,lightslategray:2005441023,lightslategrey:2005441023,lightsteelblue:2965692159,lightyellow:4294959359,lime:16711935,limegreen:852308735,linen:4210091775,magenta:4278255615,maroon:2147483903,mediumaquamarine:1724754687,mediumblue:52735,mediumorchid:3126187007,mediumpurple:2473647103,mediumseagreen:1018393087,mediumslateblue:2070474495,mediumspringgreen:16423679,mediumturquoise:1221709055,mediumvioletred:3340076543,midnightblue:421097727,mintcream:4127193855,mistyrose:4293190143,moccasin:4293178879,navajowhite:4292783615,navy:33023,oldlace:4260751103,olive:2155872511,olivedrab:1804477439,orange:4289003775,orangered:4282712319,orchid:3664828159,palegoldenrod:4008225535,palegreen:2566625535,paleturquoise:2951671551,palevioletred:3681588223,papayawhip:4293907967,peachpuff:4292524543,peru:3448061951,pink:4290825215,plum:3718307327,powderblue:2967529215,purple:2147516671,rebeccapurple:1714657791,red:4278190335,rosybrown:3163525119,royalblue:1097458175,saddlebrown:2336560127,salmon:4202722047,sandybrown:4104413439,seagreen:780883967,seashell:4294307583,sienna:2689740287,silver:3233857791,skyblue:2278484991,slateblue:1784335871,slategray:1887473919,slategrey:1887473919,snow:4294638335,springgreen:16744447,steelblue:1182971135,tan:3535047935,teal:8421631,thistle:3636451583,tomato:4284696575,turquoise:1088475391,violet:4001558271,wheat:4125012991,white:4294967295,whitesmoke:4126537215,yellow:4294902015,yellowgreen:2597139199};m.exports=function(l){var k,f=(void 0===t&&(t={rgb:new RegExp('rgb'+s(o,o,o)),rgba:new RegExp('rgba'+s(o,o,o,o)),hsl:new RegExp('hsl'+s(o,u,u)),hsla:new RegExp('hsla'+s(o,u,u,o)),hex3:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex4:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#([0-9a-fA-F]{6})$/,hex8:/^#([0-9a-fA-F]{8})$/}),t);return'number'==typeof l?l>>>0===l&&l>=0&&l<=4294967295?l:null:(k=f.hex6.exec(l))?parseInt(k[1]+'ff',16)>>>0:y.hasOwnProperty(l)?y[l]:(k=f.rgb.exec(l))?(h(k[1])<<24|h(k[2])<<16|h(k[3])<<8|255)>>>0:(k=f.rgba.exec(l))?(h(k[1])<<24|h(k[2])<<16|h(k[3])<<8|b(k[4]))>>>0:(k=f.hex3.exec(l))?parseInt(k[1]+k[1]+k[2]+k[2]+k[3]+k[3]+'ff',16)>>>0:(k=f.hex8.exec(l))?parseInt(k[1],16)>>>0:(k=f.hex4.exec(l))?parseInt(k[1]+k[1]+k[2]+k[2]+k[3]+k[3]+k[4]+k[4],16)>>>0:(k=f.hsl.exec(l))?(255|n(c(k[1]),p(k[2]),p(k[3])))>>>0:(k=f.hsla.exec(l))?(n(c(k[1]),p(k[2]),p(k[3]))|b(k[4]))>>>0:null}},67,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t={display:n.oneOf(['none','flex']),width:n.oneOfType([n.number,n.string]),height:n.oneOfType([n.number,n.string]),start:n.oneOfType([n.number,n.string]),end:n.oneOfType([n.number,n.string]),top:n.oneOfType([n.number,n.string]),left:n.oneOfType([n.number,n.string]),right:n.oneOfType([n.number,n.string]),bottom:n.oneOfType([n.number,n.string]),minWidth:n.oneOfType([n.number,n.string]),maxWidth:n.oneOfType([n.number,n.string]),minHeight:n.oneOfType([n.number,n.string]),maxHeight:n.oneOfType([n.number,n.string]),margin:n.oneOfType([n.number,n.string]),marginVertical:n.oneOfType([n.number,n.string]),marginHorizontal:n.oneOfType([n.number,n.string]),marginTop:n.oneOfType([n.number,n.string]),marginBottom:n.oneOfType([n.number,n.string]),marginLeft:n.oneOfType([n.number,n.string]),marginRight:n.oneOfType([n.number,n.string]),marginStart:n.oneOfType([n.number,n.string]),marginEnd:n.oneOfType([n.number,n.string]),padding:n.oneOfType([n.number,n.string]),paddingVertical:n.oneOfType([n.number,n.string]),paddingHorizontal:n.oneOfType([n.number,n.string]),paddingTop:n.oneOfType([n.number,n.string]),paddingBottom:n.oneOfType([n.number,n.string]),paddingLeft:n.oneOfType([n.number,n.string]),paddingRight:n.oneOfType([n.number,n.string]),paddingStart:n.oneOfType([n.number,n.string]),paddingEnd:n.oneOfType([n.number,n.string]),borderWidth:n.number,borderTopWidth:n.number,borderStartWidth:n.number,borderEndWidth:n.number,borderRightWidth:n.number,borderBottomWidth:n.number,borderLeftWidth:n.number,position:n.oneOf(['absolute','relative']),flexDirection:n.oneOf(['row','row-reverse','column','column-reverse']),flexWrap:n.oneOf(['wrap','nowrap','wrap-reverse']),justifyContent:n.oneOf(['flex-start','flex-end','center','space-between','space-around','space-evenly']),alignItems:n.oneOf(['flex-start','flex-end','center','stretch','baseline']),alignSelf:n.oneOf(['auto','flex-start','flex-end','center','stretch','baseline']),alignContent:n.oneOf(['flex-start','flex-end','center','stretch','space-between','space-around']),overflow:n.oneOf(['visible','hidden','scroll']),flex:n.number,flexGrow:n.number,flexShrink:n.number,flexBasis:n.oneOfType([n.number,n.string]),aspectRatio:n.number,zIndex:n.number,direction:n.oneOf(['inherit','ltr','rtl'])};m.exports=t},68,[69]); -__d(function(g,r,i,a,m,e,d){m.exports=r(d[0])()},69,[70]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);function t(){}function o(){}o.resetWarningCache=t,m.exports=function(){function p(t,o,p,c,s,y){if(y!==n){var f=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw f.name='Invariant Violation',f}}function c(){return p}p.isRequired=p;var s={array:p,bool:p,func:p,number:p,object:p,string:p,symbol:p,any:p,arrayOf:c,element:p,elementType:p,instanceOf:c,node:p,objectOf:c,oneOf:c,oneOfType:c,shape:c,exact:c,checkPropTypes:o,resetWarningCache:t};return s.PropTypes=s,s}},70,[71]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports='SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'},71,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var s=r(d[0]),h=r(d[1]),o={shadowColor:s,shadowOffset:h.shape({width:h.number,height:h.number}),shadowOpacity:h.number,shadowRadius:h.number};m.exports=o},72,[66,69]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n={transform:t.arrayOf(t.oneOfType([t.shape({perspective:t.number}),t.shape({rotate:t.string}),t.shape({rotateX:t.string}),t.shape({rotateY:t.string}),t.shape({rotateZ:t.string}),t.shape({scale:t.number}),t.shape({scaleX:t.number}),t.shape({scaleY:t.number}),t.shape({translateX:t.number}),t.shape({translateY:t.number}),t.shape({skewX:t.string}),t.shape({skewY:t.string})])),transformMatrix:function(t,s,n){if(t[s])return new Error("The transformMatrix style property is deprecated. Use `transform: [{ matrix: ... }]` instead.")},decomposedMatrix:function(t,s,n){if(t[s])return new Error("The decomposedMatrix style property is deprecated. Use `transform: [...]` instead.")},scaleX:s(t.number,'Use the transform prop instead.'),scaleY:s(t.number,'Use the transform prop instead.'),rotation:s(t.number,'Use the transform prop instead.'),translateX:s(t.number,'Use the transform prop instead.'),translateY:s(t.number,'Use the transform prop instead.')};m.exports=n},73,[69,74]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=function(t,o){return function(c,u,p){n.getViewManagerConfig(p)||void 0===c[u]||console.warn("`"+u+"` supplied to `"+p+"` has been deprecated. "+o);for(var s=arguments.length,f=new Array(s>3?s-3:0),l=3;l<s;l++)f[l-3]=arguments[l];return t.apply(void 0,[c,u,p].concat(f))}}},74,[75]); -__d(function(g,r,i,a,m,e,d){'use strict';var s=!0===g.RN$Bridgeless?r(d[0]):r(d[1]);m.exports=s},75,[76,77]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports={getViewManagerConfig:function(n){return console.warn('Attempting to get config for view manager: '+n),null},getConstants:function(){return{}},getConstantsForViewManager:function(n){},getDefaultEventTypes:function(){return[]},playTouchSound:function(){},lazilyLoadView:function(n){},createView:function(n,t,o,u){},updateView:function(n,t,o){},focus:function(n){},blur:function(n){},findSubviewIn:function(n,t,o){},dispatchViewManagerCommand:function(n,t,o){},measure:function(n,t){},measureInWindow:function(n,t){},viewIsDescendantOf:function(n,t,o){},measureLayout:function(n,t,o,u){},measureLayoutRelativeToParent:function(n,t,o){},setJSResponder:function(n,t){},clearJSResponder:function(){},configureNextLayoutAnimation:function(n,t,o){},removeSubviewsFromContainerWithID:function(n){},replaceExistingNonRootView:function(n,t){},setChildren:function(n,t){},manageChildren:function(n,t,o,u,c,f){},setLayoutAnimationEnabledExperimental:function(n){},sendAccessibilityEvent:function(n,t){},showPopupMenu:function(n,t,o,u){},dismissPopupMenu:function(){}}},76,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=n(r(d[1])),o=n(r(d[2])),f=r(d[3]),u=(r(d[4]),r(d[5])),c=r(d[6]),s={},l=new Set,v={},C=!1;function w(){return C||(v=o.default.getConstants(),C=!0),v}var M=(0,t.default)({},o.default,{getConstants:function(){return w()},getViewManagerConfig:function(n){if(void 0===s[n]&&o.default.getConstantsForViewManager)try{s[n]=o.default.getConstantsForViewManager(n)}catch(t){s[n]=null}var t=s[n];if(t)return t;if(!g.nativeCallSyncHook)return t;if(o.default.lazilyLoadView&&!l.has(n)){var f=o.default.lazilyLoadView(n);l.add(n),f.viewConfig&&(w()[n]=f.viewConfig,y(n))}return s[n]}});function y(n){var t=w()[n];s[n]=t,t.Manager&&(c(t,'Constants',{get:function(){var n=f[t.Manager],o={};return n&&Object.keys(n).forEach(function(t){var f=n[t];'function'!=typeof f&&(o[t]=f)}),o}}),c(t,'Commands',{get:function(){var n=f[t.Manager],o={},u=0;return n&&Object.keys(n).forEach(function(t){'function'==typeof n[t]&&(o[t]=u++)}),o}}))}o.default.getViewManagerConfig=M.getViewManagerConfig,Object.keys(w()).forEach(function(n){y(n)}),g.nativeCallSyncHook||Object.keys(w()).forEach(function(n){u.includes(n)||(s[n]||(s[n]=w()[n]),c(o.default,n,{get:function(){return console.warn("Accessing view manager configs directly off UIManager via UIManager['"+n+"'] is no longer supported. Use UIManager.getViewManagerConfig('"+n+"') instead."),M.getViewManagerConfig(n)}}))}),m.exports=M},77,[3,54,78,25,58,79,40]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('UIManager');e.default=n},78,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=['clearJSResponder','configureNextLayoutAnimation','createView','dismissPopupMenu','dispatchViewManagerCommand','findSubviewIn','getConstantsForViewManager','getDefaultEventTypes','manageChildren','measure','measureInWindow','measureLayout','measureLayoutRelativeToParent','playTouchSound','removeRootView','removeSubviewsFromContainerWithID','replaceExistingNonRootView','sendAccessibilityEvent','setChildren','setJSResponder','setLayoutAnimationEnabledExperimental','showPopupMenu','updateView','viewIsDescendantOf','PopupMenu','LazyViewManagersEnabled','ViewManagerNames','StyleConstants','AccessibilityEventTypes','UIView','getViewManagerConfig','blur','focus','genericBubblingEventTypes','genericDirectEventTypes','lazilyLoadView']},79,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),l=r(d[3]),u=t({},o,{color:n,fontFamily:l.string,fontSize:l.number,fontStyle:l.oneOf(['normal','italic']),fontWeight:l.oneOf(['normal','bold','100','200','300','400','500','600','700','800','900']),fontVariant:l.arrayOf(l.oneOf(['small-caps','oldstyle-nums','lining-nums','tabular-nums','proportional-nums'])),textShadowOffset:l.shape({width:l.number,height:l.number}),textShadowRadius:l.number,textShadowColor:n,letterSpacing:l.number,lineHeight:l.number,textAlign:l.oneOf(['auto','left','right','center','justify']),textAlignVertical:l.oneOf(['auto','top','bottom','center']),includeFontPadding:l.bool,textDecorationLine:l.oneOf(['none','underline','line-through','underline line-through']),textDecorationStyle:l.oneOf(['solid','double','dotted','dashed']),textDecorationColor:n,textTransform:l.oneOf(['none','capitalize','uppercase','lowercase']),writingDirection:l.oneOf(['auto','ltr','rtl'])});m.exports=u},80,[54,66,81,69]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),b=r(d[1]),t=r(d[2]),u=r(d[3]),n=r(d[4]),s=r(d[5]),l=o({},t,u,n,{backfaceVisibility:s.oneOf(['visible','hidden']),backgroundColor:b,borderColor:b,borderTopColor:b,borderRightColor:b,borderBottomColor:b,borderLeftColor:b,borderStartColor:b,borderEndColor:b,borderRadius:s.number,borderTopLeftRadius:s.number,borderTopRightRadius:s.number,borderTopStartRadius:s.number,borderTopEndRadius:s.number,borderBottomLeftRadius:s.number,borderBottomRightRadius:s.number,borderBottomStartRadius:s.number,borderBottomEndRadius:s.number,borderStyle:s.oneOf(['solid','dotted','dashed']),borderWidth:s.number,borderTopWidth:s.number,borderRightWidth:s.number,borderBottomWidth:s.number,borderLeftWidth:s.number,opacity:s.number,elevation:s.number});m.exports=l},81,[54,66,68,72,73,69]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);var n=r(d[1]);m.exports=function(t){if(void 0===t||null===t)return t;var u=n(t);return null!==u&&void 0!==u?u=(u<<24|u>>>8)>>>0:void 0}},82,[58,67]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]),r(d[1]),r(d[2]),r(d[3]);m.exports=function(t){return t}},83,[84,58,18,39]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o={createIdentityMatrix:function(){return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},createCopy:function(t){return[t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15]]},createOrthographic:function(t,n,o,u,s,c){return[2/(n-t),0,0,0,0,2/(u-o),0,0,0,0,-2/(c-s),0,-(n+t)/(n-t),-(u+o)/(u-o),-(c+s)/(c-s),1]},createFrustum:function(t,n,o,u,s,c){var v=1/(n-t),f=1/(u-o),h=1/(s-c);return[s*v*2,0,0,0,0,s*f*2,0,0,(n+t)*v,(u+o)*f,(c+s)*h,-1,0,0,c*s*h*2,0]},createPerspective:function(t,n,o,u){var s=1/Math.tan(t/2),c=1/(o-u);return[s/n,0,0,0,0,s,0,0,0,0,(u+o)*c,-1,0,0,u*o*c*2,0]},createTranslate2d:function(t,n){var u=o.createIdentityMatrix();return o.reuseTranslate2dCommand(u,t,n),u},reuseTranslate2dCommand:function(t,n,o){t[12]=n,t[13]=o},reuseTranslate3dCommand:function(t,n,o,u){t[12]=n,t[13]=o,t[14]=u},createScale:function(t){var n=o.createIdentityMatrix();return o.reuseScaleCommand(n,t),n},reuseScaleCommand:function(t,n){t[0]=n,t[5]=n},reuseScale3dCommand:function(t,n,o,u){t[0]=n,t[5]=o,t[10]=u},reusePerspectiveCommand:function(t,n){t[11]=-1/n},reuseScaleXCommand:function(t,n){t[0]=n},reuseScaleYCommand:function(t,n){t[5]=n},reuseScaleZCommand:function(t,n){t[10]=n},reuseRotateXCommand:function(t,n){t[5]=Math.cos(n),t[6]=Math.sin(n),t[9]=-Math.sin(n),t[10]=Math.cos(n)},reuseRotateYCommand:function(t,n){t[0]=Math.cos(n),t[2]=-Math.sin(n),t[8]=Math.sin(n),t[10]=Math.cos(n)},reuseRotateZCommand:function(t,n){t[0]=Math.cos(n),t[1]=Math.sin(n),t[4]=-Math.sin(n),t[5]=Math.cos(n)},createRotateZ:function(t){var n=o.createIdentityMatrix();return o.reuseRotateZCommand(n,t),n},reuseSkewXCommand:function(t,n){t[4]=Math.tan(n)},reuseSkewYCommand:function(t,n){t[1]=Math.tan(n)},multiplyInto:function(t,n,o){var u=n[0],s=n[1],c=n[2],v=n[3],f=n[4],h=n[5],M=n[6],l=n[7],C=n[8],p=n[9],x=n[10],T=n[11],y=n[12],S=n[13],D=n[14],P=n[15],q=o[0],X=o[1],Y=o[2],I=o[3];t[0]=q*u+X*f+Y*C+I*y,t[1]=q*s+X*h+Y*p+I*S,t[2]=q*c+X*M+Y*x+I*D,t[3]=q*v+X*l+Y*T+I*P,q=o[4],X=o[5],Y=o[6],I=o[7],t[4]=q*u+X*f+Y*C+I*y,t[5]=q*s+X*h+Y*p+I*S,t[6]=q*c+X*M+Y*x+I*D,t[7]=q*v+X*l+Y*T+I*P,q=o[8],X=o[9],Y=o[10],I=o[11],t[8]=q*u+X*f+Y*C+I*y,t[9]=q*s+X*h+Y*p+I*S,t[10]=q*c+X*M+Y*x+I*D,t[11]=q*v+X*l+Y*T+I*P,q=o[12],X=o[13],Y=o[14],I=o[15],t[12]=q*u+X*f+Y*C+I*y,t[13]=q*s+X*h+Y*p+I*S,t[14]=q*c+X*M+Y*x+I*D,t[15]=q*v+X*l+Y*T+I*P},determinant:function(n){var o=t(n,16),u=o[0],s=o[1],c=o[2],v=o[3],f=o[4],h=o[5],M=o[6],l=o[7],C=o[8],p=o[9],x=o[10],T=o[11],y=o[12],S=o[13],D=o[14],P=o[15];return v*M*p*y-c*l*p*y-v*h*x*y+s*l*x*y+c*h*T*y-s*M*T*y-v*M*C*S+c*l*C*S+v*f*x*S-u*l*x*S-c*f*T*S+u*M*T*S+v*h*C*D-s*l*C*D-v*f*p*D+u*l*p*D+s*f*T*D-u*h*T*D-c*h*C*P+s*M*C*P+c*f*p*P-u*M*p*P-s*f*x*P+u*h*x*P},inverse:function(n){var u=o.determinant(n);if(!u)return n;var s=t(n,16),c=s[0],v=s[1],f=s[2],h=s[3],M=s[4],l=s[5],C=s[6],p=s[7],x=s[8],T=s[9],y=s[10],S=s[11],D=s[12],P=s[13],q=s[14],X=s[15];return[(C*S*P-p*y*P+p*T*q-l*S*q-C*T*X+l*y*X)/u,(h*y*P-f*S*P-h*T*q+v*S*q+f*T*X-v*y*X)/u,(f*p*P-h*C*P+h*l*q-v*p*q-f*l*X+v*C*X)/u,(h*C*T-f*p*T-h*l*y+v*p*y+f*l*S-v*C*S)/u,(p*y*D-C*S*D-p*x*q+M*S*q+C*x*X-M*y*X)/u,(f*S*D-h*y*D+h*x*q-c*S*q-f*x*X+c*y*X)/u,(h*C*D-f*p*D-h*M*q+c*p*q+f*M*X-c*C*X)/u,(f*p*x-h*C*x+h*M*y-c*p*y-f*M*S+c*C*S)/u,(l*S*D-p*T*D+p*x*P-M*S*P-l*x*X+M*T*X)/u,(h*T*D-v*S*D-h*x*P+c*S*P+v*x*X-c*T*X)/u,(v*p*D-h*l*D+h*M*P-c*p*P-v*M*X+c*l*X)/u,(h*l*x-v*p*x-h*M*T+c*p*T+v*M*S-c*l*S)/u,(C*T*D-l*y*D-C*x*P+M*y*P+l*x*q-M*T*q)/u,(v*y*D-f*T*D+f*x*P-c*y*P-v*x*q+c*T*q)/u,(f*l*D-v*C*D-f*M*P+c*C*P+v*M*q-c*l*q)/u,(v*C*x-f*l*x+f*M*T-c*C*T-v*M*y+c*l*y)/u]},transpose:function(t){return[t[0],t[4],t[8],t[12],t[1],t[5],t[9],t[13],t[2],t[6],t[10],t[14],t[3],t[7],t[11],t[15]]},multiplyVectorByMatrix:function(n,o){var u=t(n,4),s=u[0],c=u[1],v=u[2],f=u[3];return[s*o[0]+c*o[4]+v*o[8]+f*o[12],s*o[1]+c*o[5]+v*o[9]+f*o[13],s*o[2]+c*o[6]+v*o[10]+f*o[14],s*o[3]+c*o[7]+v*o[11]+f*o[15]]},v3Length:function(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2])},v3Normalize:function(t,n){var u=1/(n||o.v3Length(t));return[t[0]*u,t[1]*u,t[2]*u]},v3Dot:function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]},v3Combine:function(t,n,o,u){return[o*t[0]+u*n[0],o*t[1]+u*n[1],o*t[2]+u*n[2]]},v3Cross:function(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]},quaternionToDegreesXYZ:function(n,u,s){var c=t(n,4),v=c[0],f=c[1],h=c[2],M=c[3],l=v*v,C=f*f,p=h*h,x=v*f+h*M,T=M*M+l+C+p,y=180/Math.PI;return x>.49999*T?[0,2*Math.atan2(v,M)*y,90]:x<-.49999*T?[0,-2*Math.atan2(v,M)*y,-90]:[o.roundTo3Places(Math.atan2(2*v*M-2*f*h,1-2*l-2*p)*y),o.roundTo3Places(Math.atan2(2*f*M-2*v*h,1-2*C-2*p)*y),o.roundTo3Places(Math.asin(2*v*f+2*h*M)*y)]},roundTo3Places:function(t){var n=t.toString().split('e');return.001*Math.round(n[0]+'e'+(n[1]?+n[1]-3:3))},decomposeMatrix:function(t){n(16===t.length,'Matrix decomposition needs a list of 3d matrix values, received %s',t);var u=[],s=[],c=[],v=[],f=[];if(t[15]){for(var h=[],M=[],l=0;l<4;l++){h.push([]);for(var C=0;C<4;C++){var p=t[4*l+C]/t[15];h[l].push(p),M.push(3===C?0:p)}}if(M[15]=1,o.determinant(M)){if(0!==h[0][3]||0!==h[1][3]||0!==h[2][3]){var x=[h[0][3],h[1][3],h[2][3],h[3][3]],T=o.inverse(M),y=o.transpose(T);u=o.multiplyVectorByMatrix(x,y)}else u[0]=u[1]=u[2]=0,u[3]=1;for(var S=0;S<3;S++)f[S]=h[3][S];for(var D=[],P=0;P<3;P++)D[P]=[h[P][0],h[P][1],h[P][2]];c[0]=o.v3Length(D[0]),D[0]=o.v3Normalize(D[0],c[0]),v[0]=o.v3Dot(D[0],D[1]),D[1]=o.v3Combine(D[1],D[0],1,-v[0]),v[0]=o.v3Dot(D[0],D[1]),D[1]=o.v3Combine(D[1],D[0],1,-v[0]),c[1]=o.v3Length(D[1]),D[1]=o.v3Normalize(D[1],c[1]),v[0]/=c[1],v[1]=o.v3Dot(D[0],D[2]),D[2]=o.v3Combine(D[2],D[0],1,-v[1]),v[2]=o.v3Dot(D[1],D[2]),D[2]=o.v3Combine(D[2],D[1],1,-v[2]),c[2]=o.v3Length(D[2]),D[2]=o.v3Normalize(D[2],c[2]),v[1]/=c[2],v[2]/=c[2];var q,X=o.v3Cross(D[1],D[2]);if(o.v3Dot(D[0],X)<0)for(var Y=0;Y<3;Y++)c[Y]*=-1,D[Y][0]*=-1,D[Y][1]*=-1,D[Y][2]*=-1;return s[0]=.5*Math.sqrt(Math.max(1+D[0][0]-D[1][1]-D[2][2],0)),s[1]=.5*Math.sqrt(Math.max(1-D[0][0]+D[1][1]-D[2][2],0)),s[2]=.5*Math.sqrt(Math.max(1-D[0][0]-D[1][1]+D[2][2],0)),s[3]=.5*Math.sqrt(Math.max(1+D[0][0]+D[1][1]+D[2][2],0)),D[2][1]>D[1][2]&&(s[0]=-s[0]),D[0][2]>D[2][0]&&(s[1]=-s[1]),D[1][0]>D[0][1]&&(s[2]=-s[2]),{rotationDegrees:q=s[0]<.001&&s[0]>=0&&s[1]<.001&&s[1]>=0?[0,0,o.roundTo3Places(180*Math.atan2(D[0][1],D[0][0])/Math.PI)]:o.quaternionToDegreesXYZ(s,h,D),perspective:u,quaternion:s,scale:c,skew:v,translation:f,rotate:q[2],rotateX:q[0],rotateY:q[1],scaleX:c[0],scaleY:c[1],translateX:f[0],translateY:f[1]}}}}};m.exports=o},84,[26,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t={width:void 0,height:void 0};m.exports=function(h,n){return(h=h||t)!==(n=n||t)&&(h.width!==n.width||h.height!==n.height)}},85,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),u=(r(d[2]),r(d[3]),r(d[4]),r(d[5]),(function(){function u(){t(this,u)}return n(u,null,[{key:"validateStyleProp",value:function(t,n,u){}},{key:"validateStyle",value:function(t,n){}},{key:"addValidStylePropTypes",value:function(t){}}]),u})());m.exports=u},86,[4,5,65,80,81,18]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function t(n){if(null!==n&&'object'==typeof n){if(!Array.isArray(n))return n;for(var f={},o=0,u=n.length;o<u;++o){var c=t(n[o]);if(c)for(var s in c)f[s]=c[s]}return f}}},87,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0]).default},88,[89]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.__INTERNAL_VIEW_CONFIG=void 0;var _;r(d[0]),r(d[1]),r(d[2]),r(d[3]);_=r(d[4])('RCTView');e.__INTERNAL_VIEW_CONFIG=void 0;var t=_;e.default=t},89,[58,90,168,169,182]); -__d(function(g,r,i,a,m,e,d){'use strict';var t;t=r(d[0]),m.exports=t},90,[91]); -__d(function(e,t,n,r,i,l,a){"use strict";var o=t(a[0]);t(a[1]);var u=t(a[2]),c=t(a[3]),s=t(a[4]);function f(e){return e.name="Invariant Violation",e}var d=null,p={};function h(){if(d)for(var e in p){var t=p[e],n=d.indexOf(e);if(!(-1<n))throw f(Error("EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `"+e+"`."));if(!g[n]){if(!t.extractEvents)throw f(Error("EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `"+e+"` does not."));for(var r in g[n]=t,n=t.eventTypes){var i=void 0,l=n[r],a=t,o=r;if(v.hasOwnProperty(o))throw f(Error("EventPluginHub: More than one plugin attempted to publish the same event name, `"+o+"`."));v[o]=l;var u=l.phasedRegistrationNames;if(u){for(i in u)u.hasOwnProperty(i)&&m(u[i],a);i=!0}else l.registrationName?(m(l.registrationName,a),i=!0):i=!1;if(!i)throw f(Error("EventPluginRegistry: Failed to publish event `"+r+"` for plugin `"+e+"`."))}}}}function m(e,t){if(y[e])throw f(Error("EventPluginHub: More than one plugin attempted to publish the same registration name, `"+e+"`."));y[e]=t}var g=[],v={},y={};function b(e,t,n,r,i,l,a,o,u){var c=Array.prototype.slice.call(arguments,3);try{t.apply(n,c)}catch(e){this.onError(e)}}var T=!1,E=null,x=!1,w=null,S={onError:function(e){T=!0,E=e}};function k(e,t,n,r,i,l,a,o,u){T=!1,E=null,b.apply(S,arguments)}function C(e,t,n,r,i,l,a,o,u){if(k.apply(this,arguments),T){if(!T)throw f(Error("clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue."));var c=E;T=!1,E=null,x||(x=!0,w=c)}}var _=null,P=null,R=null;function N(e,t,n){var r=e.type||"unknown-event";e.currentTarget=R(n),C(r,t,void 0,e),e.currentTarget=null}function I(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t))throw f(Error("executeDirectDispatch(...): Invalid `event`."));return e.currentTarget=t?R(n):null,t=t?t(e):null,e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,t}function U(e,t){if(null==t)throw f(Error("accumulateInto(...): Accumulated items must not be null or undefined."));return null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}function z(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}var M=null;function A(e){if(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t))for(var r=0;r<t.length&&!e.isPropagationStopped();r++)N(e,t[r],n[r]);else t&&N(e,t,n);e._dispatchListeners=null,e._dispatchInstances=null,e.isPersistent()||e.constructor.release(e)}}var D=function(e){if(d)throw f(Error("EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React."));d=Array.prototype.slice.call(e),h()},F=function(e){var t,n=!1;for(t in e)if(e.hasOwnProperty(t)){var r=e[t];if(!p.hasOwnProperty(t)||p[t]!==r){if(p[t])throw f(Error("EventPluginRegistry: Cannot inject two different event plugins using the same name, `"+t+"`."));p[t]=r,n=!0}}n&&h()};function O(e,t){var n=e.stateNode;if(!n)return null;var r=_(n);if(!r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw f(Error("Expected `"+t+"` listener to be a function, instead got a value of `"+typeof n+"` type."));return n}function j(e){do{e=e.return}while(e&&5!==e.tag);return e||null}function W(e,t,n){for(var r=[];e;)r.push(e),e=j(e);for(e=r.length;0<e--;)t(r[e],"captured",n);for(e=0;e<r.length;e++)t(r[e],"bubbled",n)}function H(e,t,n){(t=O(e,n.dispatchConfig.phasedRegistrationNames[t]))&&(n._dispatchListeners=U(n._dispatchListeners,t),n._dispatchInstances=U(n._dispatchInstances,e))}function Q(e){e&&e.dispatchConfig.phasedRegistrationNames&&W(e._targetInst,H,e)}function B(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var t=e._targetInst;W(t=t?j(t):null,H,e)}}function V(e){if(e&&e.dispatchConfig.registrationName){var t=e._targetInst;if(t&&e&&e.dispatchConfig.registrationName){var n=O(t,e.dispatchConfig.registrationName);n&&(e._dispatchListeners=U(e._dispatchListeners,n),e._dispatchInstances=U(e._dispatchInstances,t))}}}function L(){return!0}function Y(){return!1}function X(e,t,n,r){for(var i in this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n,e=this.constructor.Interface)e.hasOwnProperty(i)&&((t=e[i])?this[i]=t(n):"target"===i?this.target=r:this[i]=n[i]);return this.isDefaultPrevented=(null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue)?L:Y,this.isPropagationStopped=Y,this}function q(e,t,n,r){if(this.eventPool.length){var i=this.eventPool.pop();return this.call(i,e,t,n,r),i}return new this(e,t,n,r)}function $(e){if(!(e instanceof this))throw f(Error("Trying to release an event instance into a pool of a different type."));e.destructor(),10>this.eventPool.length&&this.eventPool.push(e)}function G(e){e.eventPool=[],e.getPooled=q,e.release=$}o(X.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=L)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=L)},persist:function(){this.isPersistent=L},isPersistent:Y,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=Y,this._dispatchInstances=this._dispatchListeners=null}}),X.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},X.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var i=new t;return o(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=o({},r.Interface,e),n.extend=r.extend,G(n),n},G(X);var J=X.extend({touchHistory:function(){return null}});function K(e){return"topTouchStart"===e}function Z(e){return"topTouchMove"===e}var ee=["topTouchStart"],te=["topTouchMove"],ne=["topTouchCancel","topTouchEnd"],re=[],ie={touchBank:re,numberActiveTouches:0,indexOfSingleActiveTouch:-1,mostRecentTimeStamp:0};function le(e){return e.timeStamp||e.timestamp}function ae(e){if(null==(e=e.identifier))throw f(Error("Touch object is missing identifier."));return e}function oe(e){var t=ae(e),n=re[t];n?(n.touchActive=!0,n.startPageX=e.pageX,n.startPageY=e.pageY,n.startTimeStamp=le(e),n.currentPageX=e.pageX,n.currentPageY=e.pageY,n.currentTimeStamp=le(e),n.previousPageX=e.pageX,n.previousPageY=e.pageY,n.previousTimeStamp=le(e)):(n={touchActive:!0,startPageX:e.pageX,startPageY:e.pageY,startTimeStamp:le(e),currentPageX:e.pageX,currentPageY:e.pageY,currentTimeStamp:le(e),previousPageX:e.pageX,previousPageY:e.pageY,previousTimeStamp:le(e)},re[t]=n),ie.mostRecentTimeStamp=le(e)}function ue(e){var t=re[ae(e)];t?(t.touchActive=!0,t.previousPageX=t.currentPageX,t.previousPageY=t.currentPageY,t.previousTimeStamp=t.currentTimeStamp,t.currentPageX=e.pageX,t.currentPageY=e.pageY,t.currentTimeStamp=le(e),ie.mostRecentTimeStamp=le(e)):console.warn("Cannot record touch move without a touch start.\nTouch Move: %s\n","Touch Bank: %s",se(e),fe())}function ce(e){var t=re[ae(e)];t?(t.touchActive=!1,t.previousPageX=t.currentPageX,t.previousPageY=t.currentPageY,t.previousTimeStamp=t.currentTimeStamp,t.currentPageX=e.pageX,t.currentPageY=e.pageY,t.currentTimeStamp=le(e),ie.mostRecentTimeStamp=le(e)):console.warn("Cannot record touch end without a touch start.\nTouch End: %s\n","Touch Bank: %s",se(e),fe())}function se(e){return JSON.stringify({identifier:e.identifier,pageX:e.pageX,pageY:e.pageY,timestamp:le(e)})}function fe(){var e=JSON.stringify(re.slice(0,20));return 20<re.length&&(e+=" (original size: "+re.length+")"),e}var de={recordTouchTrack:function(e,t){if(Z(e))t.changedTouches.forEach(ue);else if(K(e))t.changedTouches.forEach(oe),ie.numberActiveTouches=t.touches.length,1===ie.numberActiveTouches&&(ie.indexOfSingleActiveTouch=t.touches[0].identifier);else if(("topTouchEnd"===e||"topTouchCancel"===e)&&(t.changedTouches.forEach(ce),ie.numberActiveTouches=t.touches.length,1===ie.numberActiveTouches))for(e=0;e<re.length;e++)if(null!=(t=re[e])&&t.touchActive){ie.indexOfSingleActiveTouch=e;break}},touchHistory:ie};function pe(e,t){if(null==t)throw f(Error("accumulate(...): Accumulated items must not be null or undefined."));return null==e?t:Array.isArray(e)?e.concat(t):Array.isArray(t)?[e].concat(t):[e,t]}var he=null,me=0;function ge(e,t){var n=he;he=e,null!==ye.GlobalResponderHandler&&ye.GlobalResponderHandler.onChange(n,e,t)}var ve={startShouldSetResponder:{phasedRegistrationNames:{bubbled:"onStartShouldSetResponder",captured:"onStartShouldSetResponderCapture"},dependencies:ee},scrollShouldSetResponder:{phasedRegistrationNames:{bubbled:"onScrollShouldSetResponder",captured:"onScrollShouldSetResponderCapture"},dependencies:["topScroll"]},selectionChangeShouldSetResponder:{phasedRegistrationNames:{bubbled:"onSelectionChangeShouldSetResponder",captured:"onSelectionChangeShouldSetResponderCapture"},dependencies:["topSelectionChange"]},moveShouldSetResponder:{phasedRegistrationNames:{bubbled:"onMoveShouldSetResponder",captured:"onMoveShouldSetResponderCapture"},dependencies:te},responderStart:{registrationName:"onResponderStart",dependencies:ee},responderMove:{registrationName:"onResponderMove",dependencies:te},responderEnd:{registrationName:"onResponderEnd",dependencies:ne},responderRelease:{registrationName:"onResponderRelease",dependencies:ne},responderTerminationRequest:{registrationName:"onResponderTerminationRequest",dependencies:[]},responderGrant:{registrationName:"onResponderGrant",dependencies:[]},responderReject:{registrationName:"onResponderReject",dependencies:[]},responderTerminate:{registrationName:"onResponderTerminate",dependencies:[]}},ye={_getResponder:function(){return he},eventTypes:ve,extractEvents:function(e,t,n,r){if(K(e))me+=1;else if("topTouchEnd"===e||"topTouchCancel"===e){if(!(0<=me))return console.error("Ended a touch event which was not counted in `trackedTouchCount`."),null;--me}if(de.recordTouchTrack(e,n),t&&("topScroll"===e&&!n.responderIgnoreScroll||0<me&&"topSelectionChange"===e||K(e)||Z(e))){var i=K(e)?ve.startShouldSetResponder:Z(e)?ve.moveShouldSetResponder:"topSelectionChange"===e?ve.selectionChangeShouldSetResponder:ve.scrollShouldSetResponder;if(he)e:{for(var l=he,a=0,o=l;o;o=j(o))a++;o=0;for(var u=t;u;u=j(u))o++;for(;0<a-o;)l=j(l),a--;for(;0<o-a;)t=j(t),o--;for(;a--;){if(l===t||l===t.alternate)break e;l=j(l),t=j(t)}l=null}else l=t;t=l===he,(l=J.getPooled(i,l,n,r)).touchHistory=de.touchHistory,z(l,t?B:Q);e:{if(i=l._dispatchListeners,t=l._dispatchInstances,Array.isArray(i)){for(a=0;a<i.length&&!l.isPropagationStopped();a++)if(i[a](l,t[a])){i=t[a];break e}}else if(i&&i(l,t)){i=t;break e}i=null}l._dispatchInstances=null,l._dispatchListeners=null,l.isPersistent()||l.constructor.release(l),i&&i!==he?(l=void 0,(t=J.getPooled(ve.responderGrant,i,n,r)).touchHistory=de.touchHistory,z(t,V),a=!0===I(t),he?((o=J.getPooled(ve.responderTerminationRequest,he,n,r)).touchHistory=de.touchHistory,z(o,V),u=!o._dispatchListeners||I(o),o.isPersistent()||o.constructor.release(o),u?((o=J.getPooled(ve.responderTerminate,he,n,r)).touchHistory=de.touchHistory,z(o,V),l=pe(l,[t,o]),ge(i,a)):((i=J.getPooled(ve.responderReject,i,n,r)).touchHistory=de.touchHistory,z(i,V),l=pe(l,i))):(l=pe(l,t),ge(i,a)),i=l):i=null}else i=null;if(l=he&&K(e),t=he&&Z(e),a=he&&("topTouchEnd"===e||"topTouchCancel"===e),(l=l?ve.responderStart:t?ve.responderMove:a?ve.responderEnd:null)&&((l=J.getPooled(l,he,n,r)).touchHistory=de.touchHistory,z(l,V),i=pe(i,l)),l=he&&"topTouchCancel"===e,e=he&&!l&&("topTouchEnd"===e||"topTouchCancel"===e))e:{if((e=n.touches)&&0!==e.length)for(t=0;t<e.length;t++)if(null!==(a=e[t].target)&&void 0!==a&&0!==a){o=P(a);t:{for(a=he;o;){if(a===o||a===o.alternate){a=!0;break t}o=j(o)}a=!1}if(a){e=!1;break e}}e=!0}return(e=l?ve.responderTerminate:e?ve.responderRelease:null)&&((n=J.getPooled(e,he,n,r)).touchHistory=de.touchHistory,z(n,V),i=pe(i,n),ge(null)),i},GlobalResponderHandler:null,injection:{injectGlobalResponderHandler:function(e){ye.GlobalResponderHandler=e}}},be=u.ReactNativeViewConfigRegistry.customBubblingEventTypes,Te=u.ReactNativeViewConfigRegistry.customDirectEventTypes;D(["ResponderEventPlugin","ReactNativeBridgeEventPlugin"]),F({ResponderEventPlugin:ye,ReactNativeBridgeEventPlugin:{eventTypes:{},extractEvents:function(e,t,n,r){if(null==t)return null;var i=be[e],l=Te[e];if(!i&&!l)throw f(Error('Unsupported top level event type "'+e+'" dispatched'));if(e=X.getPooled(i||l,t,n,r),i)z(e,Q);else{if(!l)return null;z(e,V)}return e}}});var Ee=new Map,xe=new Map;function we(e){return Ee.get(e)||null}var Se=null,ke=null;function Ce(e){if(P(e))throw f(Error("setRestoreImplementation() needs to be called to handle a target for controlled events. This error is likely caused by a bug in React. Please file an issue."))}function _e(e,t){return e(t)}function Pe(){}var Re=!1;function Ne(e,t){if(Re)return e(t);Re=!0;try{return _e(e,t)}finally{if(Re=!1,(null!==Se||null!==ke)&&(Pe(),Se&&(t=Se,e=ke,ke=Se=null,Ce(t),e)))for(t=0;t<e.length;t++)Ce(e[t])}}var Ie={};function Ue(e,t,n){var r=n||Ie,i=we(e);Ne(function(){for(var e=r.target,n=null,l=0;l<g.length;l++){var a=g[l];a&&(a=a.extractEvents(t,i,r,e))&&(n=U(n,a))}if(null!==(e=n)&&(M=U(M,e)),e=M,M=null,e){if(z(e,A),M)throw f(Error("processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."));if(x)throw e=w,x=!1,w=null,e}})}u.RCTEventEmitter.register({receiveEvent:function(e,t,n){Ue(e,t,n)},receiveTouches:function(e,t,n){if("topTouchEnd"===e||"topTouchCancel"===e){for(var r=[],i=0;i<n.length;i++){var l=n[i];r.push(t[l]),t[l]=null}for(i=n=0;i<t.length;i++)null!==(l=t[i])&&(t[n++]=l);t.length=n}else for(r=[],i=0;i<n.length;i++)r.push(t[n[i]]);for(n=0;n<r.length;n++){(i=r[n]).changedTouches=r,i.touches=t,l=null;var a=i.target;null===a||void 0===a||1>a||(l=a),Ue(l,e,i)}}}),_=function(e){return xe.get(e._nativeTag)||null},P=we,R=function(e){var t=e.stateNode._nativeTag;if(void 0===t&&(t=e.stateNode.canonical._nativeTag),!t)throw f(Error("All native instances should have a tag."));return t},ye.injection.injectGlobalResponderHandler({onChange:function(e,t,n){null!==t?u.UIManager.setJSResponder(t.stateNode._nativeTag,n):u.UIManager.clearJSResponder()}});var ze=c.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;ze.hasOwnProperty("ReactCurrentDispatcher")||(ze.ReactCurrentDispatcher={current:null}),ze.hasOwnProperty("ReactCurrentBatchConfig")||(ze.ReactCurrentBatchConfig={suspense:null});var Me="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.for:"@@for"),Ae=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.element"):60103,De=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.portal"):60106,Fe=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.fragment"):60107,Oe=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.strict_mode"):60108,je=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.profiler"):60114,We=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.provider"):60109,He=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.context"):60110,Qe=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.concurrent_mode"):60111,Be=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.forward_ref"):60112,Ve=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense"):60113,Le=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense_list"):60120,Ye=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.memo"):60115,Xe=Me?("function"==typeof Symbol?Symbol.for:"@@for")("react.lazy"):60116;Me&&("function"==typeof Symbol?Symbol.for:"@@for")("react.fundamental"),Me&&("function"==typeof Symbol?Symbol.for:"@@for")("react.responder");var qe="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.iterator:"@@iterator");function $e(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=qe&&e[qe]||e["@@iterator"])?e:null}function Ge(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case Fe:return"Fragment";case De:return"Portal";case je:return"Profiler";case Oe:return"StrictMode";case Ve:return"Suspense";case Le:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case He:return"Context.Consumer";case We:return"Context.Provider";case Be:var t=e.render;return t=t.displayName||t.name||"",e.displayName||(""!==t?"ForwardRef("+t+")":"ForwardRef");case Ye:return Ge(e.type);case Xe:if(e=1===e._status?e._result:null)return Ge(e)}return null}function Je(e){var t=e;if(e.alternate)for(;t.return;)t=t.return;else{if(0!=(2&t.effectTag))return 1;for(;t.return;)if(0!=(2&(t=t.return).effectTag))return 1}return 3===t.tag?2:3}function Ke(e){if(2!==Je(e))throw f(Error("Unable to find node on an unmounted component."))}function Ze(e){var t=e.alternate;if(!t){if(3===(t=Je(e)))throw f(Error("Unable to find node on an unmounted component."));return 1===t?null:e}for(var n=e,r=t;;){var i=n.return;if(null===i)break;var l=i.alternate;if(null===l){if(null!==(r=i.return)){n=r;continue}break}if(i.child===l.child){for(l=i.child;l;){if(l===n)return Ke(i),e;if(l===r)return Ke(i),t;l=l.sibling}throw f(Error("Unable to find node on an unmounted component."))}if(n.return!==r.return)n=i,r=l;else{for(var a=!1,o=i.child;o;){if(o===n){a=!0,n=i,r=l;break}if(o===r){a=!0,r=i,n=l;break}o=o.sibling}if(!a){for(o=l.child;o;){if(o===n){a=!0,n=l,r=i;break}if(o===r){a=!0,r=l,n=i;break}o=o.sibling}if(!a)throw f(Error("Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue."))}}if(n.alternate!==r)throw f(Error("Return fibers should always be each others' alternates. This error is likely caused by a bug in React. Please file an issue."))}if(3!==n.tag)throw f(Error("Unable to find node on an unmounted component."));return n.stateNode.current===n?e:t}function et(e){if(!(e=Ze(e)))return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}var tt={},nt=null,rt=0;function it(e,t,n){if(Array.isArray(t))for(var r=t.length;r--&&0<rt;)it(e,t[r],n);else if(t&&0<rt)for(r in nt)if(nt[r]){var i=t[r];if(void 0!==i){var l=n[r];l&&("function"==typeof i&&(i=!0),void 0===i&&(i=null),"object"!=typeof l?e[r]=i:"function"!=typeof l.diff&&"function"!=typeof l.process||(i="function"==typeof l.process?l.process(i):i,e[r]=i),nt[r]=!1,rt--)}}}function lt(e,t,n,r){if(!e&&t===n)return e;if(!t||!n)return n?at(e,n,r):t?ot(e,t,r):e;if(!Array.isArray(t)&&!Array.isArray(n))return ut(e,t,n,r);if(Array.isArray(t)&&Array.isArray(n)){var i,l=t.length<n.length?t.length:n.length;for(i=0;i<l;i++)e=lt(e,t[i],n[i],r);for(;i<t.length;i++)e=ot(e,t[i],r);for(;i<n.length;i++)e=at(e,n[i],r);return e}return Array.isArray(t)?ut(e,u.flattenStyle(t),n,r):ut(e,t,u.flattenStyle(n),r)}function at(e,t,n){if(!t)return e;if(!Array.isArray(t))return ut(e,tt,t,n);for(var r=0;r<t.length;r++)e=at(e,t[r],n);return e}function ot(e,t,n){if(!t)return e;if(!Array.isArray(t))return ut(e,t,tt,n);for(var r=0;r<t.length;r++)e=ot(e,t[r],n);return e}function ut(e,t,n,r){var i,l;for(l in n)if(i=r[l]){var a=t[l],o=n[l];"function"==typeof o&&(o=!0,"function"==typeof a&&(a=!0)),void 0===o&&(o=null,void 0===a&&(a=null)),nt&&(nt[l]=!1),e&&void 0!==e[l]?"object"!=typeof i?e[l]=o:"function"!=typeof i.diff&&"function"!=typeof i.process||(i="function"==typeof i.process?i.process(o):o,e[l]=i):a!==o&&("object"!=typeof i?("object"!=typeof o||null===o||u.deepDiffer(a,o))&&((e||(e={}))[l]=o):"function"==typeof i.diff||"function"==typeof i.process?(void 0===a||("function"==typeof i.diff?i.diff(a,o):"object"!=typeof o||null===o||u.deepDiffer(a,o)))&&(i="function"==typeof i.process?i.process(o):o,(e||(e={}))[l]=i):(nt=null,rt=0,e=lt(e,a,o,i),0<rt&&e&&(it(e,o,i),nt=null)))}for(var c in t)void 0===n[c]&&(!(i=r[c])||e&&void 0!==e[c]||void 0!==(a=t[c])&&("object"!=typeof i||"function"==typeof i.diff||"function"==typeof i.process?((e||(e={}))[c]=null,nt||(nt={}),nt[c]||(nt[c]=!0,rt++)):e=ot(e,a,i)));return e}function ct(e,t){return function(){if(t&&("boolean"!=typeof e.__isMounted||e.__isMounted))return t.apply(e,arguments)}}var st=(function(){function e(t,n){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this._nativeTag=t,this._children=[],this.viewConfig=n}return e.prototype.blur=function(){u.TextInputState.blurTextInput(this._nativeTag)},e.prototype.focus=function(){u.TextInputState.focusTextInput(this._nativeTag)},e.prototype.measure=function(e){u.UIManager.measure(this._nativeTag,ct(this,e))},e.prototype.measureInWindow=function(e){u.UIManager.measureInWindow(this._nativeTag,ct(this,e))},e.prototype.measureLayout=function(e,t,n){var r=void 0;"number"==typeof e?r=e:e._nativeTag?r=e._nativeTag:e.canonical&&e.canonical._nativeTag&&(r=e.canonical._nativeTag),null!=r&&u.UIManager.measureLayout(this._nativeTag,r,ct(this,n),ct(this,t))},e.prototype.setNativeProps=function(e){null!=(e=ut(null,tt,e,this.viewConfig.validAttributes))&&u.UIManager.updateView(this._nativeTag,this.viewConfig.uiViewClassName,e)},e})();function ft(){throw f(Error("The current renderer does not support hydration. This error is likely caused by a bug in React. Please file an issue."))}var dt=u.ReactNativeViewConfigRegistry.get,pt={},ht=3;function mt(){var e=ht;return 1==e%10&&(e+=2),ht=e+2,e}function gt(e){if("number"==typeof e)Ee.delete(e),xe.delete(e);else{var t=e._nativeTag;Ee.delete(t),xe.delete(t),e._children.forEach(gt)}}function vt(e){if(0===e._children.length)return!1;var t=e._children.map(function(e){return"number"==typeof e?e:e._nativeTag});return u.UIManager.setChildren(e._nativeTag,t),!1}var yt=setTimeout,bt=clearTimeout,Tt=/^(.*)[\\\/]/;function Et(e){var t="";do{e:switch(e.tag){case 3:case 4:case 6:case 7:case 10:case 9:var n="";break e;default:var r=e._debugOwner,i=e._debugSource,l=Ge(e.type);n=null,r&&(n=Ge(r.type)),r=l,l="",i?l=" (at "+i.fileName.replace(Tt,"")+":"+i.lineNumber+")":n&&(l=" (created by "+n+")"),n="\n in "+(r||"Unknown")+l}t+=n,e=e.return}while(e);return t}new Set;var xt=[],wt=-1;function St(e){0>wt||(e.current=xt[wt],xt[wt]=null,wt--)}function kt(e,t){xt[++wt]=e.current,e.current=t}var Ct={},_t={current:Ct},Pt={current:!1},Rt=Ct;function Nt(e,t){var n=e.type.contextTypes;if(!n)return Ct;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i,l={};for(i in n)l[i]=t[i];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function It(e){return null!==(e=e.childContextTypes)&&void 0!==e}function Ut(e){St(Pt),St(_t)}function zt(e){St(Pt),St(_t)}function Mt(e,t,n){if(_t.current!==Ct)throw f(Error("Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue."));kt(_t,t),kt(Pt,n)}function At(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var i in r=r.getChildContext())if(!(i in e))throw f(Error((Ge(t)||"Unknown")+'.getChildContext(): key "'+i+'" is not defined in childContextTypes.'));return o({},n,r)}function Dt(e){var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||Ct,Rt=_t.current,kt(_t,t),kt(Pt,Pt.current),!0}function Ft(e,t,n){var r=e.stateNode;if(!r)throw f(Error("Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue."));n?(t=At(e,t,Rt),r.__reactInternalMemoizedMergedChildContext=t,St(Pt),St(_t),kt(_t,t)):St(Pt),kt(Pt,n)}var Ot=s.unstable_runWithPriority,jt=s.unstable_scheduleCallback,Wt=s.unstable_cancelCallback,Ht=s.unstable_shouldYield,Qt=s.unstable_requestPaint,Bt=s.unstable_now,Vt=s.unstable_getCurrentPriorityLevel,Lt=s.unstable_ImmediatePriority,Yt=s.unstable_UserBlockingPriority,Xt=s.unstable_NormalPriority,qt=s.unstable_LowPriority,$t=s.unstable_IdlePriority,Gt={},Jt=void 0!==Qt?Qt:function(){},Kt=null,Zt=null,en=!1,tn=Bt(),nn=1e4>tn?Bt:function(){return Bt()-tn};function rn(){switch(Vt()){case Lt:return 99;case Yt:return 98;case Xt:return 97;case qt:return 96;case $t:return 95;default:throw f(Error("Unknown priority level."))}}function ln(e){switch(e){case 99:return Lt;case 98:return Yt;case 97:return Xt;case 96:return qt;case 95:return $t;default:throw f(Error("Unknown priority level."))}}function an(e,t){return e=ln(e),Ot(e,t)}function on(e,t,n){return e=ln(e),jt(e,t,n)}function un(e){return null===Kt?(Kt=[e],Zt=jt(Lt,sn)):Kt.push(e),Gt}function cn(){null!==Zt&&Wt(Zt),sn()}function sn(){if(!en&&null!==Kt){en=!0;var e=0;try{var t=Kt;an(99,function(){for(;e<t.length;e++){var n=t[e];do{n=n(!0)}while(null!==n)}}),Kt=null}catch(t){throw null!==Kt&&(Kt=Kt.slice(e+1)),jt(Lt,cn),t}finally{en=!1}}}function fn(e,t){return 1073741823===t?99:1===t?95:0>=(e=10*(1073741821-t)-10*(1073741821-e))?99:250>=e?98:5250>=e?97:95}function dn(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t}var pn=Object.prototype.hasOwnProperty;function hn(e,t){if(dn(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++)if(!pn.call(t,n[r])||!dn(e[n[r]],t[n[r]]))return!1;return!0}function mn(e,t){if(e&&e.defaultProps)for(var n in t=o({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}function gn(e){var t=e._result;switch(e._status){case 1:return t;case 2:case 0:throw t;default:switch(e._status=0,(t=(t=e._ctor)()).then(function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)},function(t){0===e._status&&(e._status=2,e._result=t)}),e._status){case 1:return e._result;case 2:throw e._result}throw e._result=t,t}}var vn={current:null},yn=null,bn=null,Tn=null;function En(){Tn=bn=yn=null}function xn(e,t){var n=e.type._context;kt(vn,n._currentValue),n._currentValue=t}function wn(e){var t=vn.current;St(vn),e.type._context._currentValue=t}function Sn(e,t){for(;null!==e;){var n=e.alternate;if(e.childExpirationTime<t)e.childExpirationTime=t,null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t);else{if(!(null!==n&&n.childExpirationTime<t))break;n.childExpirationTime=t}e=e.return}}function kn(e,t){yn=e,Tn=bn=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(e.expirationTime>=t&&(li=!0),e.firstContext=null)}function Cn(e,t){if(Tn!==e&&!1!==t&&0!==t)if("number"==typeof t&&1073741823!==t||(Tn=e,t=1073741823),t={context:e,observedBits:t,next:null},null===bn){if(null===yn)throw f(Error("Context can only be read while React is rendering. In classes, you can read it in the render method or getDerivedStateFromProps. In function components, you can read it directly in the function body, but not inside Hooks like useReducer() or useMemo()."));bn=t,yn.dependencies={expirationTime:0,firstContext:t,responders:null}}else bn=bn.next=t;return e._currentValue}var _n=!1;function Pn(e){return{baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Rn(e){return{baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Nn(e,t){return{expirationTime:e,suspenseConfig:t,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function In(e,t){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t)}function Un(e,t){var n=e.alternate;if(null===n){var r=e.updateQueue,i=null;null===r&&(r=e.updateQueue=Pn(e.memoizedState))}else r=e.updateQueue,i=n.updateQueue,null===r?null===i?(r=e.updateQueue=Pn(e.memoizedState),i=n.updateQueue=Pn(n.memoizedState)):r=e.updateQueue=Rn(i):null===i&&(i=n.updateQueue=Rn(r));null===i||r===i?In(r,t):null===r.lastUpdate||null===i.lastUpdate?(In(r,t),In(i,t)):(In(r,t),i.lastUpdate=t)}function zn(e,t){var n=e.updateQueue;null===(n=null===n?e.updateQueue=Pn(e.memoizedState):Mn(e,n)).lastCapturedUpdate?n.firstCapturedUpdate=n.lastCapturedUpdate=t:(n.lastCapturedUpdate.next=t,n.lastCapturedUpdate=t)}function Mn(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=Rn(t)),t}function An(e,t,n,r,i,l){switch(n.tag){case 1:return"function"==typeof(e=n.payload)?e.call(l,r,i):e;case 3:e.effectTag=-2049&e.effectTag|64;case 0:if(null===(i="function"==typeof(e=n.payload)?e.call(l,r,i):e)||void 0===i)break;return o({},r,i);case 2:_n=!0}return r}function Dn(e,t,n,r,i){_n=!1;for(var l=(t=Mn(e,t)).baseState,a=null,o=0,u=t.firstUpdate,c=l;null!==u;){var s=u.expirationTime;s<i?(null===a&&(a=u,l=c),o<s&&(o=s)):(zl(s,u.suspenseConfig),c=An(e,0,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastEffect?t.firstEffect=t.lastEffect=u:(t.lastEffect.nextEffect=u,t.lastEffect=u))),u=u.next}for(s=null,u=t.firstCapturedUpdate;null!==u;){var f=u.expirationTime;f<i?(null===s&&(s=u,null===a&&(l=c)),o<f&&(o=f)):(c=An(e,0,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastCapturedEffect?t.firstCapturedEffect=t.lastCapturedEffect=u:(t.lastCapturedEffect.nextEffect=u,t.lastCapturedEffect=u))),u=u.next}null===a&&(t.lastUpdate=null),null===s?t.lastCapturedUpdate=null:e.effectTag|=32,null===a&&null===s&&(l=c),t.baseState=l,t.firstUpdate=a,t.firstCapturedUpdate=s,e.expirationTime=o,e.memoizedState=c}function Fn(e,t,n){null!==t.firstCapturedUpdate&&(null!==t.lastUpdate&&(t.lastUpdate.next=t.firstCapturedUpdate,t.lastUpdate=t.lastCapturedUpdate),t.firstCapturedUpdate=t.lastCapturedUpdate=null),On(t.firstEffect,n),t.firstEffect=t.lastEffect=null,On(t.firstCapturedEffect,n),t.firstCapturedEffect=t.lastCapturedEffect=null}function On(e,t){for(;null!==e;){var n=e.callback;if(null!==n){e.callback=null;var r=t;if("function"!=typeof n)throw f(Error("Invalid argument passed as callback. Expected a function. Instead received: "+n));n.call(r)}e=e.nextEffect}}var jn=ze.ReactCurrentBatchConfig,Wn=(new c.Component).refs;function Hn(e,t,n,r){n=null===(n=n(r,t=e.memoizedState))||void 0===n?t:o({},t,n),e.memoizedState=n,null!==(r=e.updateQueue)&&0===e.expirationTime&&(r.baseState=n)}var Qn={isMounted:function(e){return!!(e=e._reactInternalFiber)&&2===Je(e)},enqueueSetState:function(e,t,n){e=e._reactInternalFiber;var r=wl(),i=jn.suspense;(i=Nn(r=Sl(r,e,i),i)).payload=t,void 0!==n&&null!==n&&(i.callback=n),Un(e,i),kl(e,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternalFiber;var r=wl(),i=jn.suspense;(i=Nn(r=Sl(r,e,i),i)).tag=1,i.payload=t,void 0!==n&&null!==n&&(i.callback=n),Un(e,i),kl(e,r)},enqueueForceUpdate:function(e,t){e=e._reactInternalFiber;var n=wl(),r=jn.suspense;(r=Nn(n=Sl(n,e,r),r)).tag=2,void 0!==t&&null!==t&&(r.callback=t),Un(e,r),kl(e,n)}};function Bn(e,t,n,r,i,l,a){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,l,a):!t.prototype||!t.prototype.isPureReactComponent||(!hn(n,r)||!hn(i,l))}function Vn(e,t,n){var r=!1,i=Ct,l=t.contextType;return"object"==typeof l&&null!==l?l=Cn(l):(i=It(t)?Rt:_t.current,l=(r=null!==(r=t.contextTypes)&&void 0!==r)?Nt(e,i):Ct),t=new t(n,l),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=Qn,e.stateNode=t,t._reactInternalFiber=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=i,e.__reactInternalMemoizedMaskedChildContext=l),t}function Ln(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&Qn.enqueueReplaceState(t,t.state,null)}function Yn(e,t,n,r){var i=e.stateNode;i.props=n,i.state=e.memoizedState,i.refs=Wn;var l=t.contextType;"object"==typeof l&&null!==l?i.context=Cn(l):(l=It(t)?Rt:_t.current,i.context=Nt(e,l)),null!==(l=e.updateQueue)&&(Dn(e,l,n,i,r),i.state=e.memoizedState),"function"==typeof(l=t.getDerivedStateFromProps)&&(Hn(e,t,l,n),i.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof i.getSnapshotBeforeUpdate||"function"!=typeof i.UNSAFE_componentWillMount&&"function"!=typeof i.componentWillMount||(t=i.state,"function"==typeof i.componentWillMount&&i.componentWillMount(),"function"==typeof i.UNSAFE_componentWillMount&&i.UNSAFE_componentWillMount(),t!==i.state&&Qn.enqueueReplaceState(i,i.state,null),null!==(l=e.updateQueue)&&(Dn(e,l,n,i,r),i.state=e.memoizedState)),"function"==typeof i.componentDidMount&&(e.effectTag|=4)}var Xn=Array.isArray;function qn(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){var r=void 0;if(n=n._owner){if(1!==n.tag)throw f(Error("Function components cannot have refs. Did you mean to use React.forwardRef()?"));r=n.stateNode}if(!r)throw f(Error("Missing owner for string ref "+e+". This error is likely caused by a bug in React. Please file an issue."));var i=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===i?t.ref:((t=function(e){var t=r.refs;t===Wn&&(t=r.refs={}),null===e?delete t[i]:t[i]=e})._stringRef=i,t)}if("string"!=typeof e)throw f(Error("Expected ref to be a function, a string, an object returned by React.createRef(), or null."));if(!n._owner)throw f(Error("Element ref was specified as a string ("+e+") but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://fb.me/react-refs-must-have-owner for more information."))}return e}function $n(e,t){if("textarea"!==e.type)throw f(Error("Objects are not valid as a React child (found: "+("[object Object]"===Object.prototype.toString.call(t)?"object with keys {"+Object.keys(t).join(", ")+"}":t)+")."))}function Gn(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.effectTag=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function i(e,t,n){return(e=Kl(e,t)).index=0,e.sibling=null,e}function l(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.effectTag=2,n):r:(t.effectTag=2,n):n}function a(t){return e&&null===t.alternate&&(t.effectTag=2),t}function o(e,t,n,r){return null===t||6!==t.tag?((t=ta(n,e.mode,r)).return=e,t):((t=i(t,n)).return=e,t)}function u(e,t,n,r){return null!==t&&t.elementType===n.type?((r=i(t,n.props)).ref=qn(e,t,n),r.return=e,r):((r=Zl(n.type,n.key,n.props,null,e.mode,r)).ref=qn(e,t,n),r.return=e,r)}function c(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=na(n,e.mode,r)).return=e,t):((t=i(t,n.children||[])).return=e,t)}function s(e,t,n,r,l){return null===t||7!==t.tag?((t=ea(n,e.mode,r,l)).return=e,t):((t=i(t,n)).return=e,t)}function d(e,t,n){if("string"==typeof t||"number"==typeof t)return(t=ta(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case Ae:return(n=Zl(t.type,t.key,t.props,null,e.mode,n)).ref=qn(e,null,t),n.return=e,n;case De:return(t=na(t,e.mode,n)).return=e,t}if(Xn(t)||$e(t))return(t=ea(t,e.mode,n,null)).return=e,t;$n(e,t)}return null}function p(e,t,n,r){var i=null!==t?t.key:null;if("string"==typeof n||"number"==typeof n)return null!==i?null:o(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case Ae:return n.key===i?n.type===Fe?s(e,t,n.props.children,r,i):u(e,t,n,r):null;case De:return n.key===i?c(e,t,n,r):null}if(Xn(n)||$e(n))return null!==i?null:s(e,t,n,r,null);$n(e,n)}return null}function h(e,t,n,r,i){if("string"==typeof r||"number"==typeof r)return o(t,e=e.get(n)||null,""+r,i);if("object"==typeof r&&null!==r){switch(r.$$typeof){case Ae:return e=e.get(null===r.key?n:r.key)||null,r.type===Fe?s(t,e,r.props.children,i,r.key):u(t,e,r,i);case De:return c(t,e=e.get(null===r.key?n:r.key)||null,r,i)}if(Xn(r)||$e(r))return s(t,e=e.get(n)||null,r,i,null);$n(t,r)}return null}function m(i,a,o,u){for(var c=null,s=null,f=a,m=a=0,g=null;null!==f&&m<o.length;m++){f.index>m?(g=f,f=null):g=f.sibling;var v=p(i,f,o[m],u);if(null===v){null===f&&(f=g);break}e&&f&&null===v.alternate&&t(i,f),a=l(v,a,m),null===s?c=v:s.sibling=v,s=v,f=g}if(m===o.length)return n(i,f),c;if(null===f){for(;m<o.length;m++)null!==(f=d(i,o[m],u))&&(a=l(f,a,m),null===s?c=f:s.sibling=f,s=f);return c}for(f=r(i,f);m<o.length;m++)null!==(g=h(f,i,m,o[m],u))&&(e&&null!==g.alternate&&f.delete(null===g.key?m:g.key),a=l(g,a,m),null===s?c=g:s.sibling=g,s=g);return e&&f.forEach(function(e){return t(i,e)}),c}function g(i,a,o,u){var c=$e(o);if("function"!=typeof c)throw f(Error("An object is not an iterable. This error is likely caused by a bug in React. Please file an issue."));if(null==(o=c.call(o)))throw f(Error("An iterable object provided no iterator."));for(var s=c=null,m=a,g=a=0,v=null,y=o.next();null!==m&&!y.done;g++,y=o.next()){m.index>g?(v=m,m=null):v=m.sibling;var b=p(i,m,y.value,u);if(null===b){null===m&&(m=v);break}e&&m&&null===b.alternate&&t(i,m),a=l(b,a,g),null===s?c=b:s.sibling=b,s=b,m=v}if(y.done)return n(i,m),c;if(null===m){for(;!y.done;g++,y=o.next())null!==(y=d(i,y.value,u))&&(a=l(y,a,g),null===s?c=y:s.sibling=y,s=y);return c}for(m=r(i,m);!y.done;g++,y=o.next())null!==(y=h(m,i,g,y.value,u))&&(e&&null!==y.alternate&&m.delete(null===y.key?g:y.key),a=l(y,a,g),null===s?c=y:s.sibling=y,s=y);return e&&m.forEach(function(e){return t(i,e)}),c}return function(e,r,l,o){var u="object"==typeof l&&null!==l&&l.type===Fe&&null===l.key;u&&(l=l.props.children);var c="object"==typeof l&&null!==l;if(c)switch(l.$$typeof){case Ae:e:{for(c=l.key,u=r;null!==u;){if(u.key===c){if(7===u.tag?l.type===Fe:u.elementType===l.type){n(e,u.sibling),(r=i(u,l.type===Fe?l.props.children:l.props)).ref=qn(e,u,l),r.return=e,e=r;break e}n(e,u);break}t(e,u),u=u.sibling}l.type===Fe?((r=ea(l.props.children,e.mode,o,l.key)).return=e,e=r):((o=Zl(l.type,l.key,l.props,null,e.mode,o)).ref=qn(e,r,l),o.return=e,e=o)}return a(e);case De:e:{for(u=l.key;null!==r;){if(r.key===u){if(4===r.tag&&r.stateNode.containerInfo===l.containerInfo&&r.stateNode.implementation===l.implementation){n(e,r.sibling),(r=i(r,l.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=na(l,e.mode,o)).return=e,e=r}return a(e)}if("string"==typeof l||"number"==typeof l)return l=""+l,null!==r&&6===r.tag?(n(e,r.sibling),(r=i(r,l)).return=e,e=r):(n(e,r),(r=ta(l,e.mode,o)).return=e,e=r),a(e);if(Xn(l))return m(e,r,l,o);if($e(l))return g(e,r,l,o);if(c&&$n(e,l),void 0===l&&!u)switch(e.tag){case 1:case 0:throw e=e.type,f(Error((e.displayName||e.name||"Component")+"(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null."))}return n(e,r)}}var Jn=Gn(!0),Kn=Gn(!1),Zn={},er={current:Zn},tr={current:Zn},nr={current:Zn};function rr(e){if(e===Zn)throw f(Error("Expected host context to exist. This error is likely caused by a bug in React. Please file an issue."));return e}function ir(e,t){kt(nr,t),kt(tr,e),kt(er,Zn),St(er),kt(er,{isInAParentText:!1})}function lr(e){St(er),St(tr),St(nr)}function ar(e){rr(nr.current);var t=rr(er.current),n=e.type;n="AndroidTextInput"===n||"RCTMultilineTextInputView"===n||"RCTSinglelineTextInputView"===n||"RCTText"===n||"RCTVirtualText"===n,t!==(n=t.isInAParentText!==n?{isInAParentText:n}:t)&&(kt(tr,e),kt(er,n))}function or(e){tr.current===e&&(St(er),St(tr))}var ur=1,cr=1,sr=2,fr={current:0};function dr(e){for(var t=e;null!==t;){if(13===t.tag){if(null!==t.memoizedState)return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.effectTag))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function pr(e,t){return{responder:e,props:t}}var hr=0,mr=2,gr=4,vr=8,yr=16,br=32,Tr=64,Er=128,xr=ze.ReactCurrentDispatcher,wr=0,Sr=null,kr=null,Cr=null,_r=null,Pr=null,Rr=null,Nr=0,Ir=null,Ur=0,zr=!1,Mr=null,Ar=0;function Dr(){throw f(Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem."))}function Fr(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!dn(e[n],t[n]))return!1;return!0}function Or(e,t,n,r,i,l){if(wr=l,Sr=t,Cr=null!==e?e.memoizedState:null,xr.current=null===Cr?Jr:Kr,t=n(r,i),zr){do{zr=!1,Ar+=1,Cr=null!==e?e.memoizedState:null,Rr=_r,Ir=Pr=kr=null,xr.current=Kr,t=n(r,i)}while(zr);Mr=null,Ar=0}if(xr.current=Gr,(e=Sr).memoizedState=_r,e.expirationTime=Nr,e.updateQueue=Ir,e.effectTag|=Ur,e=null!==kr&&null!==kr.next,wr=0,Rr=Pr=_r=Cr=kr=Sr=null,Nr=0,Ir=null,Ur=0,e)throw f(Error("Rendered fewer hooks than expected. This may be caused by an accidental early return statement."));return t}function jr(){xr.current=Gr,wr=0,Rr=Pr=_r=Cr=kr=Sr=null,Nr=0,Ir=null,Ur=0,zr=!1,Mr=null,Ar=0}function Wr(){var e={memoizedState:null,baseState:null,queue:null,baseUpdate:null,next:null};return null===Pr?_r=Pr=e:Pr=Pr.next=e,Pr}function Hr(){if(null!==Rr)Rr=(Pr=Rr).next,Cr=null!==(kr=Cr)?kr.next:null;else{if(null===Cr)throw f(Error("Rendered more hooks than during the previous render."));var e={memoizedState:(kr=Cr).memoizedState,baseState:kr.baseState,queue:kr.queue,baseUpdate:kr.baseUpdate,next:null};Pr=null===Pr?_r=e:Pr.next=e,Cr=kr.next}return Pr}function Qr(e,t){return"function"==typeof t?t(e):t}function Br(e){var t=Hr(),n=t.queue;if(null===n)throw f(Error("Should have a queue. This is likely a bug in React. Please file an issue."));if(n.lastRenderedReducer=e,0<Ar){var r=n.dispatch;if(null!==Mr){var i=Mr.get(n);if(void 0!==i){Mr.delete(n);var l=t.memoizedState;do{l=e(l,i.action),i=i.next}while(null!==i);return dn(l,t.memoizedState)||(li=!0),t.memoizedState=l,t.baseUpdate===n.last&&(t.baseState=l),n.lastRenderedState=l,[l,r]}}return[t.memoizedState,r]}r=n.last;var a=t.baseUpdate;if(l=t.baseState,null!==a?(null!==r&&(r.next=null),r=a.next):r=null!==r?r.next:null,null!==r){var o=i=null,u=r,c=!1;do{var s=u.expirationTime;s<wr?(c||(c=!0,o=a,i=l),s>Nr&&(Nr=s)):(zl(s,u.suspenseConfig),l=u.eagerReducer===e?u.eagerState:e(l,u.action)),a=u,u=u.next}while(null!==u&&u!==r);c||(o=a,i=l),dn(l,t.memoizedState)||(li=!0),t.memoizedState=l,t.baseUpdate=o,t.baseState=i,n.lastRenderedState=l}return[t.memoizedState,n.dispatch]}function Vr(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===Ir?(Ir={lastEffect:null}).lastEffect=e.next=e:null===(t=Ir.lastEffect)?Ir.lastEffect=e.next=e:(n=t.next,t.next=e,e.next=n,Ir.lastEffect=e),e}function Lr(e,t,n,r){var i=Wr();Ur|=e,i.memoizedState=Vr(t,n,void 0,void 0===r?null:r)}function Yr(e,t,n,r){var i=Hr();r=void 0===r?null:r;var l=void 0;if(null!==kr){var a=kr.memoizedState;if(l=a.destroy,null!==r&&Fr(r,a.deps))return void Vr(hr,n,l,r)}Ur|=e,i.memoizedState=Vr(t,n,l,r)}function Xr(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!==t&&void 0!==t?(e=e(),t.current=e,function(){t.current=null}):void 0}function qr(){}function $r(e,t,n){if(!(25>Ar))throw f(Error("Too many re-renders. React limits the number of renders to prevent an infinite loop."));var r=e.alternate;if(e===Sr||null!==r&&r===Sr)if(zr=!0,e={expirationTime:wr,suspenseConfig:null,action:n,eagerReducer:null,eagerState:null,next:null},null===Mr&&(Mr=new Map),void 0===(n=Mr.get(t)))Mr.set(t,e);else{for(t=n;null!==t.next;)t=t.next;t.next=e}else{var i=wl(),l=jn.suspense;l={expirationTime:i=Sl(i,e,l),suspenseConfig:l,action:n,eagerReducer:null,eagerState:null,next:null};var a=t.last;if(null===a)l.next=l;else{var o=a.next;null!==o&&(l.next=o),a.next=l}if(t.last=l,0===e.expirationTime&&(null===r||0===r.expirationTime)&&null!==(r=t.lastRenderedReducer))try{var u=t.lastRenderedState,c=r(u,n);if(l.eagerReducer=r,l.eagerState=c,dn(c,u))return}catch(e){}kl(e,i)}}var Gr={readContext:Cn,useCallback:Dr,useContext:Dr,useEffect:Dr,useImperativeHandle:Dr,useLayoutEffect:Dr,useMemo:Dr,useReducer:Dr,useRef:Dr,useState:Dr,useDebugValue:Dr,useResponder:Dr},Jr={readContext:Cn,useCallback:function(e,t){return Wr().memoizedState=[e,void 0===t?null:t],e},useContext:Cn,useEffect:function(e,t){return Lr(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,Lr(4,36,Xr.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Lr(4,36,e,t)},useMemo:function(e,t){var n=Wr();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Wr();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={last:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=$r.bind(null,Sr,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},Wr().memoizedState=e},useState:function(e){var t=Wr();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={last:null,dispatch:null,lastRenderedReducer:Qr,lastRenderedState:e}).dispatch=$r.bind(null,Sr,e),[t.memoizedState,e]},useDebugValue:qr,useResponder:pr},Kr={readContext:Cn,useCallback:function(e,t){var n=Hr();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Fr(t,r[1])?r[0]:(n.memoizedState=[e,t],e)},useContext:Cn,useEffect:function(e,t){return Yr(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,Yr(4,36,Xr.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Yr(4,36,e,t)},useMemo:function(e,t){var n=Hr();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Fr(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)},useReducer:Br,useRef:function(){return Hr().memoizedState},useState:function(e){return Br(Qr)},useDebugValue:qr,useResponder:pr},Zr=null,ei=null,ti=!1;function ni(e,t){switch(e.tag){case 5:return null!==(t=ft(e.type,e.pendingProps))&&(e.stateNode=t,!0);case 6:return null!==(t=ft(e.pendingProps))&&(e.stateNode=t,!0);case 13:default:return!1}}function ri(e){if(ti){var t=ei;if(t){var n=t;if(!ni(e,t)){if(!(t=ft())||!ni(e,t))return e.effectTag|=2,ti=!1,void(Zr=e);var r=Zr,i=$l(5,null,null,0);i.elementType="DELETED",i.type="DELETED",i.stateNode=n,i.return=r,i.effectTag=8,null!==r.lastEffect?(r.lastEffect.nextEffect=i,r.lastEffect=i):r.firstEffect=r.lastEffect=i}Zr=e,ei=ft()}else e.effectTag|=2,ti=!1,Zr=e}}var ii=ze.ReactCurrentOwner,li=!1;function ai(e,t,n,r){t.child=null===e?Kn(t,null,n,r):Jn(t,e.child,n,r)}function oi(e,t,n,r,i){n=n.render;var l=t.ref;return kn(t,i),r=Or(e,t,n,r,l,i),null===e||li?(t.effectTag|=1,ai(e,t,r,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),bi(e,t,i))}function ui(e,t,n,r,i,l){if(null===e){var a=n.type;return"function"!=typeof a||Gl(a)||void 0!==a.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Zl(n.type,null,r,null,t.mode,l)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=a,ci(e,t,a,r,i,l))}return a=e.child,i<l&&(i=a.memoizedProps,(n=null!==(n=n.compare)?n:hn)(i,r)&&e.ref===t.ref)?bi(e,t,l):(t.effectTag|=1,(e=Kl(a,r)).ref=t.ref,e.return=t,t.child=e)}function ci(e,t,n,r,i,l){return null!==e&&hn(e.memoizedProps,r)&&e.ref===t.ref&&(li=!1,i<l)?bi(e,t,l):fi(e,t,n,r,l)}function si(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.effectTag|=128)}function fi(e,t,n,r,i){var l=It(n)?Rt:_t.current;return l=Nt(t,l),kn(t,i),n=Or(e,t,n,r,l,i),null===e||li?(t.effectTag|=1,ai(e,t,n,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),bi(e,t,i))}function di(e,t,n,r,i){if(It(n)){var l=!0;Dt(t)}else l=!1;if(kn(t,i),null===t.stateNode)null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),Vn(t,n,r),Yn(t,n,r,i),r=!0;else if(null===e){var a=t.stateNode,o=t.memoizedProps;a.props=o;var u=a.context,c=n.contextType;"object"==typeof c&&null!==c?c=Cn(c):c=Nt(t,c=It(n)?Rt:_t.current);var s=n.getDerivedStateFromProps,f="function"==typeof s||"function"==typeof a.getSnapshotBeforeUpdate;f||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(o!==r||u!==c)&&Ln(t,a,r,c),_n=!1;var d=t.memoizedState;u=a.state=d;var p=t.updateQueue;null!==p&&(Dn(t,p,r,a,i),u=t.memoizedState),o!==r||d!==u||Pt.current||_n?("function"==typeof s&&(Hn(t,n,s,r),u=t.memoizedState),(o=_n||Bn(t,n,o,r,d,u,c))?(f||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||("function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount()),"function"==typeof a.componentDidMount&&(t.effectTag|=4)):("function"==typeof a.componentDidMount&&(t.effectTag|=4),t.memoizedProps=r,t.memoizedState=u),a.props=r,a.state=u,a.context=c,r=o):("function"==typeof a.componentDidMount&&(t.effectTag|=4),r=!1)}else a=t.stateNode,o=t.memoizedProps,a.props=t.type===t.elementType?o:mn(t.type,o),u=a.context,"object"==typeof(c=n.contextType)&&null!==c?c=Cn(c):c=Nt(t,c=It(n)?Rt:_t.current),(f="function"==typeof(s=n.getDerivedStateFromProps)||"function"==typeof a.getSnapshotBeforeUpdate)||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(o!==r||u!==c)&&Ln(t,a,r,c),_n=!1,u=t.memoizedState,d=a.state=u,null!==(p=t.updateQueue)&&(Dn(t,p,r,a,i),d=t.memoizedState),o!==r||u!==d||Pt.current||_n?("function"==typeof s&&(Hn(t,n,s,r),d=t.memoizedState),(s=_n||Bn(t,n,o,r,u,d,c))?(f||"function"!=typeof a.UNSAFE_componentWillUpdate&&"function"!=typeof a.componentWillUpdate||("function"==typeof a.componentWillUpdate&&a.componentWillUpdate(r,d,c),"function"==typeof a.UNSAFE_componentWillUpdate&&a.UNSAFE_componentWillUpdate(r,d,c)),"function"==typeof a.componentDidUpdate&&(t.effectTag|=4),"function"==typeof a.getSnapshotBeforeUpdate&&(t.effectTag|=256)):("function"!=typeof a.componentDidUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),"function"!=typeof a.getSnapshotBeforeUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),t.memoizedProps=r,t.memoizedState=d),a.props=r,a.state=d,a.context=c,r=s):("function"!=typeof a.componentDidUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),"function"!=typeof a.getSnapshotBeforeUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),r=!1);return pi(e,t,n,r,l,i)}function pi(e,t,n,r,i,l){si(e,t);var a=0!=(64&t.effectTag);if(!r&&!a)return i&&Ft(t,n,!1),bi(e,t,l);r=t.stateNode,ii.current=t;var o=a&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.effectTag|=1,null!==e&&a?(t.child=Jn(t,e.child,null,l),t.child=Jn(t,null,o,l)):ai(e,t,o,l),t.memoizedState=r.state,i&&Ft(t,n,!0),t.child}function hi(e){var t=e.stateNode;t.pendingContext?Mt(0,t.pendingContext,t.pendingContext!==t.context):t.context&&Mt(0,t.context,!1),ir(e,t.containerInfo)}var mi={};function gi(e,t,n){var r,i=t.mode,l=t.pendingProps,a=fr.current,o=null,u=!1;if((r=0!=(64&t.effectTag))||(r=0!=(a&sr)&&(null===e||null!==e.memoizedState)),r?(o=mi,u=!0,t.effectTag&=-65):null!==e&&null===e.memoizedState||void 0===l.fallback||!0===l.unstable_avoidThisFallback||(a|=cr),kt(fr,a&=ur),null===e)if(u){if(l=l.fallback,(e=ea(null,i,0,null)).return=t,0==(2&t.mode))for(u=null!==t.memoizedState?t.child.child:t.child,e.child=u;null!==u;)u.return=e,u=u.sibling;(n=ea(l,i,n,null)).return=t,e.sibling=n,i=e}else i=n=Kn(t,null,l.children,n);else{if(null!==e.memoizedState)if(i=(a=e.child).sibling,u){if(l=l.fallback,(n=Kl(a,a.pendingProps)).return=t,0==(2&t.mode)&&(u=null!==t.memoizedState?t.child.child:t.child)!==a.child)for(n.child=u;null!==u;)u.return=n,u=u.sibling;(l=Kl(i,l,i.expirationTime)).return=t,n.sibling=l,i=n,n.childExpirationTime=0,n=l}else i=n=Jn(t,a.child,l.children,n);else if(a=e.child,u){if(u=l.fallback,(l=ea(null,i,0,null)).return=t,l.child=a,null!==a&&(a.return=l),0==(2&t.mode))for(a=null!==t.memoizedState?t.child.child:t.child,l.child=a;null!==a;)a.return=l,a=a.sibling;(n=ea(u,i,n,null)).return=t,l.sibling=n,n.effectTag|=2,i=l,l.childExpirationTime=0}else n=i=Jn(t,a,l.children,n);t.stateNode=e.stateNode}return t.memoizedState=o,t.child=i,n}function vi(e,t,n,r,i){var l=e.memoizedState;null===l?e.memoizedState={isBackwards:t,rendering:null,last:r,tail:n,tailExpiration:0,tailMode:i}:(l.isBackwards=t,l.rendering=null,l.last=r,l.tail=n,l.tailExpiration=0,l.tailMode=i)}function yi(e,t,n){var r=t.pendingProps,i=r.revealOrder,l=r.tail;if(ai(e,t,r.children,n),0!=((r=fr.current)&sr))r=r&ur|sr,t.effectTag|=64;else{if(null!==e&&0!=(64&e.effectTag))e:for(e=t.child;null!==e;){if(13===e.tag){if(null!==e.memoizedState){e.expirationTime<n&&(e.expirationTime=n);var a=e.alternate;null!==a&&a.expirationTime<n&&(a.expirationTime=n),Sn(e.return,n)}}else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=ur}if(kt(fr,r),0==(2&t.mode))t.memoizedState=null;else switch(i){case"forwards":for(n=t.child,i=null;null!==n;)null!==(r=n.alternate)&&null===dr(r)&&(i=n),n=n.sibling;null===(n=i)?(i=t.child,t.child=null):(i=n.sibling,n.sibling=null),vi(t,!1,i,n,l);break;case"backwards":for(n=null,i=t.child,t.child=null;null!==i;){if(null!==(r=i.alternate)&&null===dr(r)){t.child=i;break}r=i.sibling,i.sibling=n,n=i,i=r}vi(t,!0,n,null,l);break;case"together":vi(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function bi(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),t.childExpirationTime<n)return null;if(null!==e&&t.child!==e.child)throw f(Error("Resuming work not yet implemented."));if(null!==t.child){for(n=Kl(e=t.child,e.pendingProps,e.expirationTime),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Kl(e,e.pendingProps,e.expirationTime)).return=t;n.sibling=null}return t.child}var Ti=void 0,Ei=void 0,xi=void 0,wi=void 0;function Si(e,t){switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function ki(e){switch(e.tag){case 1:It(e.type)&&Ut();var t=e.effectTag;return 2048&t?(e.effectTag=-2049&t|64,e):null;case 3:if(lr(),zt(),0!=(64&(t=e.effectTag)))throw f(Error("The root failed to unmount after an error. This is likely a bug in React. Please file an issue."));return e.effectTag=-2049&t|64,e;case 5:return or(e),null;case 13:return St(fr),2048&(t=e.effectTag)?(e.effectTag=-2049&t|64,e):null;case 18:return null;case 19:return St(fr),null;case 4:return lr(),null;case 10:return wn(e),null;default:return null}}function Ci(e,t){return{value:e,source:t,stack:Et(t)}}if(Ti=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e._children.push(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},Ei=function(){},xi=function(e,t,n,r){e.memoizedProps!==r&&(rr(er.current),t.updateQueue=pt)&&(t.effectTag|=4)},wi=function(e,t,n,r){n!==r&&(t.effectTag|=4)},"function"!=typeof u.ReactFiberErrorDialog.showErrorDialog)throw f(Error("Expected ReactFiberErrorDialog.showErrorDialog to be a function."));var _i="function"==typeof WeakSet?WeakSet:Set;function Pi(e,t){var n,r=t.source,i=t.stack;null===i&&null!==r&&(i=Et(r)),t={componentName:null!==r?Ge(r.type):null,componentStack:null!==i?i:"",error:t.value,errorBoundary:null,errorBoundaryName:null,errorBoundaryFound:!1,willRetry:!1},null!==e&&1===e.tag&&(t.errorBoundary=e.stateNode,t.errorBoundaryName=Ge(e.type),t.errorBoundaryFound=!0,t.willRetry=!0);try{n=t,!1!==u.ReactFiberErrorDialog.showErrorDialog(n)&&console.error(n.error)}catch(e){setTimeout(function(){throw e})}}function Ri(e,t){try{t.props=e.memoizedProps,t.state=e.memoizedState,t.componentWillUnmount()}catch(t){Hl(e,t)}}function Ni(e){var t=e.ref;if(null!==t)if("function"==typeof t)try{t(null)}catch(t){Hl(e,t)}else t.current=null}function Ii(e,t,n){if(null!==(n=null!==(n=n.updateQueue)?n.lastEffect:null)){var r=n=n.next;do{if((r.tag&e)!==hr){var i=r.destroy;r.destroy=void 0,void 0!==i&&i()}(r.tag&t)!==hr&&(i=r.create,r.destroy=i()),r=r.next}while(r!==n)}}function Ui(e,t){switch("function"==typeof Yl&&Yl(e),e.tag){case 0:case 11:case 14:case 15:var n=e.updateQueue;if(null!==n&&null!==(n=n.lastEffect)){var r=n.next;an(97<t?97:t,function(){var t=r;do{var n=t.destroy;if(void 0!==n){var i=e;try{n()}catch(e){Hl(i,e)}}t=t.next}while(t!==r)})}break;case 1:Ni(e),"function"==typeof(t=e.stateNode).componentWillUnmount&&Ri(e,t);break;case 5:Ni(e);break;case 4:Di(e,t)}}function zi(e){var t=e.alternate;e.return=null,e.child=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null,e.alternate=null,e.firstEffect=null,e.lastEffect=null,e.pendingProps=null,e.memoizedProps=null,null!==t&&zi(t)}function Mi(e){return 5===e.tag||3===e.tag||4===e.tag}function Ai(e){e:{for(var t=e.return;null!==t;){if(Mi(t)){var n=t;break e}t=t.return}throw f(Error("Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue."))}switch(t=n.stateNode,n.tag){case 5:var r=!1;break;case 3:case 4:t=t.containerInfo,r=!0;break;default:throw f(Error("Invalid host parent fiber. This error is likely caused by a bug in React. Please file an issue."))}16&n.effectTag&&(n.effectTag&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||Mi(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag&&18!==n.tag;){if(2&n.effectTag)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.effectTag)){n=n.stateNode;break e}}for(var i=e;;){var l=5===i.tag||6===i.tag;if(l){var a=l?i.stateNode:i.stateNode.instance;if(n)if(r){if("number"==typeof t)throw f(Error("Container does not support insertBefore operation"))}else{var o=n,c=(l=t)._children,s=c.indexOf(a);0<=s?(c.splice(s,1),o=c.indexOf(o),c.splice(o,0,a),u.UIManager.manageChildren(l._nativeTag,[s],[o],[],[],[])):(s=c.indexOf(o),c.splice(s,0,a),u.UIManager.manageChildren(l._nativeTag,[],[],["number"==typeof a?a:a._nativeTag],[s],[]))}else r?u.UIManager.setChildren(t,["number"==typeof a?a:a._nativeTag]):(l=t,c="number"==typeof a?a:a._nativeTag,0<=(o=(s=l._children).indexOf(a))?(s.splice(o,1),s.push(a),u.UIManager.manageChildren(l._nativeTag,[o],[s.length-1],[],[],[])):(s.push(a),u.UIManager.manageChildren(l._nativeTag,[],[],[c],[s.length-1],[])))}else if(4!==i.tag&&null!==i.child){i.child.return=i,i=i.child;continue}if(i===e)break;for(;null===i.sibling;){if(null===i.return||i.return===e)return;i=i.return}i.sibling.return=i.return,i=i.sibling}}function Di(e,t){for(var n=e,r=!1,i=void 0,l=void 0;;){if(!r){r=n.return;e:for(;;){if(null===r)throw f(Error("Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue."));switch(i=r.stateNode,r.tag){case 5:l=!1;break e;case 3:case 4:i=i.containerInfo,l=!0;break e}r=r.return}r=!0}if(5===n.tag||6===n.tag){e:for(var a=n,o=t,c=a;;)if(Ui(c,o),null!==c.child&&4!==c.tag)c.child.return=c,c=c.child;else{if(c===a)break;for(;null===c.sibling;){if(null===c.return||c.return===a)break e;c=c.return}c.sibling.return=c.return,c=c.sibling}l?(a=i,gt(n.stateNode),u.UIManager.manageChildren(a,[],[],[],[],[0])):(a=i,gt(c=n.stateNode),c=(o=a._children).indexOf(c),o.splice(c,1),u.UIManager.manageChildren(a._nativeTag,[],[],[],[],[c]))}else if(4===n.tag){if(null!==n.child){i=n.stateNode.containerInfo,l=!0,n.child.return=n,n=n.child;continue}}else if(Ui(n,t),null!==n.child){n.child.return=n,n=n.child;continue}if(n===e)break;for(;null===n.sibling;){if(null===n.return||n.return===e)return;4===(n=n.return).tag&&(r=!1)}n.sibling.return=n.return,n=n.sibling}}function Fi(e,t){switch(t.tag){case 0:case 11:case 14:case 15:Ii(gr,vr,t);break;case 1:break;case 5:var n=t.stateNode;if(null!=n){var r=t.memoizedProps;e=null!==e?e.memoizedProps:r;var i=t.updateQueue;t.updateQueue=null,null!==i&&(t=n.viewConfig,xe.set(n._nativeTag,r),null!=(r=ut(null,e,r,t.validAttributes))&&u.UIManager.updateView(n._nativeTag,t.uiViewClassName,r))}break;case 6:if(null===t.stateNode)throw f(Error("This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue."));u.UIManager.updateView(t.stateNode,"RCTRawText",{text:t.memoizedProps});break;case 3:case 12:break;case 13:if(n=t,null===t.memoizedState?r=!1:(r=!0,n=t.child,cl=nn()),null!==n)e:for(e=n;;){if(5===e.tag)if(i=e.stateNode,r){var l=i.viewConfig,a=ut(null,tt,{style:{display:"none"}},l.validAttributes);u.UIManager.updateView(i._nativeTag,l.uiViewClassName,a)}else{i=e.stateNode,a=e.memoizedProps,l=i.viewConfig,a=ut(null,o({},a,{style:[a.style,{display:"none"}]}),a,l.validAttributes),u.UIManager.updateView(i._nativeTag,l.uiViewClassName,a)}else{if(6===e.tag)throw Error("Not yet implemented.");if(13===e.tag&&null!==e.memoizedState){(i=e.child.sibling).return=e,e=i;continue}if(null!==e.child){e.child.return=e,e=e.child;continue}}if(e===n)break e;for(;null===e.sibling;){if(null===e.return||e.return===n)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}Oi(t);break;case 19:Oi(t);break;case 17:case 20:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}function Oi(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new _i),t.forEach(function(t){var r=Bl.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))})}}var ji="function"==typeof WeakMap?WeakMap:Map;function Wi(e,t,n){(n=Nn(n,null)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){dl||(dl=!0,pl=r),Pi(e,t)},n}function Hi(e,t,n){(n=Nn(n,null)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var i=t.value;n.payload=function(){return Pi(e,t),r(i)}}var l=e.stateNode;return null!==l&&"function"==typeof l.componentDidCatch&&(n.callback=function(){"function"!=typeof r&&(null===hl?hl=new Set([this]):hl.add(this),Pi(e,t));var n=t.stack;this.componentDidCatch(t.value,{componentStack:null!==n?n:""})}),n}var Qi=Math.ceil,Bi=ze.ReactCurrentDispatcher,Vi=ze.ReactCurrentOwner,Li=0,Yi=8,Xi=16,qi=32,$i=0,Gi=1,Ji=2,Ki=3,Zi=4,el=Li,tl=null,nl=null,rl=0,il=$i,ll=1073741823,al=1073741823,ol=null,ul=!1,cl=0,sl=500,fl=null,dl=!1,pl=null,hl=null,ml=!1,gl=null,vl=90,yl=0,bl=null,Tl=0,El=null,xl=0;function wl(){return(48&el)!==Li?1073741821-(nn()/10|0):0!==xl?xl:xl=1073741821-(nn()/10|0)}function Sl(e,t,n){if(0==(2&(t=t.mode)))return 1073741823;var r=rn();if(0==(4&t))return 99===r?1073741823:1073741822;if((el&Xi)!==Li)return rl;if(null!==n)e=1073741821-25*(1+((1073741821-e+(0|n.timeoutMs||5e3)/10)/25|0));else switch(r){case 99:e=1073741823;break;case 98:e=1073741821-10*(1+((1073741821-e+15)/10|0));break;case 97:case 96:e=1073741821-25*(1+((1073741821-e+500)/25|0));break;case 95:e=1;break;default:throw f(Error("Expected a valid priority level"))}return null!==tl&&e===rl&&--e,e}function kl(e,t){if(50<Tl)throw Tl=0,El=null,f(Error("Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."));if(null!==(e=Cl(e,t))){e.pingTime=0;var n=rn();if(1073741823===t)if((el&Yi)!==Li&&(48&el)===Li)for(var r=Ul(e,1073741823,!0);null!==r;)r=r(!0);else _l(e,99,1073741823),el===Li&&cn();else _l(e,n,t);(4&el)===Li||98!==n&&99!==n||(null===bl?bl=new Map([[e,t]]):(void 0===(n=bl.get(e))||n>t)&&bl.set(e,t))}}function Cl(e,t){e.expirationTime<t&&(e.expirationTime=t);var n=e.alternate;null!==n&&n.expirationTime<t&&(n.expirationTime=t);var r=e.return,i=null;if(null===r&&3===e.tag)i=e.stateNode;else for(;null!==r;){if(n=r.alternate,r.childExpirationTime<t&&(r.childExpirationTime=t),null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t),null===r.return&&3===r.tag){i=r.stateNode;break}r=r.return}return null!==i&&(t>i.firstPendingTime&&(i.firstPendingTime=t),0===(e=i.lastPendingTime)||t<e)&&(i.lastPendingTime=t),i}function _l(e,t,n){if(e.callbackExpirationTime<n){var r=e.callbackNode;null!==r&&r!==Gt&&Wt(r),e.callbackExpirationTime=n,1073741823===n?e.callbackNode=un(Pl.bind(null,e,Ul.bind(null,e,n))):(r=null,1!==n&&(r={timeout:10*(1073741821-n)-nn()}),e.callbackNode=on(t,Pl.bind(null,e,Ul.bind(null,e,n)),r))}}function Pl(e,t,n){var r=e.callbackNode,i=null;try{return null!==(i=t(n))?Pl.bind(null,e,i):null}finally{null===i&&r===e.callbackNode&&(e.callbackNode=null,e.callbackExpirationTime=0)}}function Rl(e,t){var n=e.firstBatch;return!!(null!==n&&n._defer&&n._expirationTime>=t)&&(on(97,function(){return n._onComplete(),null}),!0)}function Nl(){if(null!==bl){var e=bl;bl=null,e.forEach(function(e,t){un(Ul.bind(null,t,e))}),cn()}}function Il(e,t){e.finishedWork=null,e.finishedExpirationTime=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,bt(n)),null!==nl)for(n=nl.return;null!==n;){var r=n;switch(r.tag){case 1:var i=r.type.childContextTypes;null!==i&&void 0!==i&&Ut();break;case 3:lr(),zt();break;case 5:or(r);break;case 4:lr();break;case 13:case 19:St(fr);break;case 10:wn(r)}n=n.return}tl=e,nl=Kl(e.current,null),rl=t,il=$i,al=ll=1073741823,ol=null,ul=!1}function Ul(e,t,n){if((48&el)!==Li)throw f(Error("Should not already be working."));if(e.firstPendingTime<t)return null;if(n&&e.finishedExpirationTime===t)return Dl.bind(null,e);if(Ol(),e!==tl||t!==rl)Il(e,t);else if(il===Ki)if(ul)Il(e,t);else{var r=e.lastPendingTime;if(r<t)return Ul.bind(null,e,r)}if(null!==nl){r=el,el|=Xi;var i=Bi.current;if(null===i&&(i=Gr),Bi.current=Gr,n){if(1073741823!==t){var l=wl();if(l<t)return el=r,En(),Bi.current=i,Ul.bind(null,e,l)}}else xl=0;for(;;)try{if(n)for(;null!==nl;)nl=Ml(nl);else for(;null!==nl&&!Ht();)nl=Ml(nl);break}catch(n){if(En(),jr(),null===(l=nl)||null===l.return)throw Il(e,t),el=r,n;e:{var a=e,o=l.return,u=l,c=n,s=rl;if(u.effectTag|=1024,u.firstEffect=u.lastEffect=null,null!==c&&"object"==typeof c&&"function"==typeof c.then){var d=c,p=0!=(fr.current&cr);c=o;do{var h;if((h=13===c.tag)&&(null!==c.memoizedState?h=!1:h=void 0!==(h=c.memoizedProps).fallback&&(!0!==h.unstable_avoidThisFallback||!p)),h){if(null===(o=c.updateQueue)?((o=new Set).add(d),c.updateQueue=o):o.add(d),0==(2&c.mode)){c.effectTag|=64,u.effectTag&=-1957,1===u.tag&&(null===u.alternate?u.tag=17:((s=Nn(1073741823,null)).tag=2,Un(u,s))),u.expirationTime=1073741823;break e}u=a,a=s,null===(p=u.pingCache)?(p=u.pingCache=new ji,o=new Set,p.set(d,o)):void 0===(o=p.get(d))&&(o=new Set,p.set(d,o)),o.has(a)||(o.add(a),u=Ql.bind(null,u,d,a),d.then(u,u)),c.effectTag|=2048,c.expirationTime=s;break e}c=c.return}while(null!==c);c=Error((Ge(u.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display."+Et(u))}il!==Zi&&(il=Gi),c=Ci(c,u),u=o;do{switch(u.tag){case 3:u.effectTag|=2048,u.expirationTime=s,zn(u,s=Wi(u,c,s));break e;case 1:if(d=c,a=u.type,o=u.stateNode,0==(64&u.effectTag)&&("function"==typeof a.getDerivedStateFromError||null!==o&&"function"==typeof o.componentDidCatch&&(null===hl||!hl.has(o)))){u.effectTag|=2048,u.expirationTime=s,zn(u,s=Hi(u,d,s));break e}}u=u.return}while(null!==u)}nl=Al(l)}if(el=r,En(),Bi.current=i,null!==nl)return Ul.bind(null,e,t)}if(e.finishedWork=e.current.alternate,e.finishedExpirationTime=t,Rl(e,t))return null;switch(tl=null,il){case $i:throw f(Error("Should have a work-in-progress."));case Gi:return(r=e.lastPendingTime)<t?Ul.bind(null,e,r):n?Dl.bind(null,e):(Il(e,t),un(Ul.bind(null,e,t)),null);case Ji:return 1073741823===ll&&!n&&10<(n=cl+sl-nn())?ul?(Il(e,t),Ul.bind(null,e,t)):(r=e.lastPendingTime)<t?Ul.bind(null,e,r):(e.timeoutHandle=yt(Dl.bind(null,e),n),null):Dl.bind(null,e);case Ki:if(!n){if(ul)return Il(e,t),Ul.bind(null,e,t);if((n=e.lastPendingTime)<t)return Ul.bind(null,e,n);if(1073741823!==al?n=10*(1073741821-al)-nn():1073741823===ll?n=0:(n=10*(1073741821-ll)-5e3,t=10*(1073741821-t)-(r=nn()),0>(n=r-n)&&(n=0),t<(n=(120>n?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*Qi(n/1960))-n)&&(n=t)),10<n)return e.timeoutHandle=yt(Dl.bind(null,e),n),null}return Dl.bind(null,e);case Zi:return!n&&1073741823!==ll&&null!==ol&&(r=ll,0>=(t=0|(i=ol).busyMinDurationMs)?t=0:(n=0|i.busyDelayMs,t=(r=nn()-(10*(1073741821-r)-(0|i.timeoutMs||5e3)))<=n?0:n+t-r),10<t)?(e.timeoutHandle=yt(Dl.bind(null,e),t),null):Dl.bind(null,e);default:throw f(Error("Unknown root exit status."))}}function zl(e,t){e<ll&&1<e&&(ll=e),null!==t&&e<al&&1<e&&(al=e,ol=t)}function Ml(e){var t=Vl(e.alternate,e,rl);return e.memoizedProps=e.pendingProps,null===t&&(t=Al(e)),Vi.current=null,t}function Al(e){nl=e;do{var t=nl.alternate;if(e=nl.return,0==(1024&nl.effectTag)){e:{var n=t,r=rl,i=(t=nl).pendingProps;switch(t.tag){case 2:case 16:break;case 15:case 0:break;case 1:It(t.type)&&Ut();break;case 3:lr(),zt(),(i=t.stateNode).pendingContext&&(i.context=i.pendingContext,i.pendingContext=null),null!==n&&null!==n.child||(t.effectTag&=-3),Ei(t);break;case 5:or(t),r=rr(nr.current);var l=t.type;if(null!==n&&null!=t.stateNode)xi(n,t,l,i,r),n.ref!==t.ref&&(t.effectTag|=128);else if(i){n=rr(er.current);var a=l,o=i,c=r,s=t,d=mt();a=dt(a);var p=ut(null,tt,o,a.validAttributes);u.UIManager.createView(d,a.uiViewClassName,c,p),c=new st(d,a),Ee.set(d,s),xe.set(d,o),Ti(o=c,t,!1,!1),vt(o)&&(t.effectTag|=4),t.stateNode=o,null!==t.ref&&(t.effectTag|=128)}else if(null===t.stateNode)throw f(Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."));break;case 6:if(n&&null!=t.stateNode)wi(n,t,n.memoizedProps,i);else{if("string"!=typeof i&&null===t.stateNode)throw f(Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."));if(l=rr(nr.current),n=t,!(r=rr(er.current)).isInAParentText)throw f(Error("Text strings must be rendered within a <Text> component."));r=mt(),u.UIManager.createView(r,"RCTRawText",l,{text:i}),Ee.set(r,t),n.stateNode=r}break;case 11:break;case 13:if(St(fr),i=t.memoizedState,0!=(64&t.effectTag)){t.expirationTime=r;break e}i=null!==i,r=!1,null!==n&&(r=null!==(l=n.memoizedState),i||null===l||null!==(l=n.child.sibling)&&(null!==(o=t.firstEffect)?(t.firstEffect=l,l.nextEffect=o):(t.firstEffect=t.lastEffect=l,l.nextEffect=null),l.effectTag=8)),i&&!r&&0!=(2&t.mode)&&(null===n&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!=(fr.current&cr)?il===$i&&(il=Ji):il!==$i&&il!==Ji||(il=Ki)),(i||r)&&(t.effectTag|=4);break;case 7:case 8:case 12:break;case 4:lr(),Ei(t);break;case 10:wn(t);break;case 9:case 14:break;case 17:It(t.type)&&Ut();break;case 18:break;case 19:if(St(fr),null===(i=t.memoizedState))break;if(l=0!=(64&t.effectTag),null===(o=i.rendering)){if(l)Si(i,!1);else if(il!==$i||null!==n&&0!=(64&n.effectTag))for(n=t.child;null!==n;){if(null!==(o=dr(n))){for(t.effectTag|=64,Si(i,!1),null!==(i=o.updateQueue)&&(t.updateQueue=i,t.effectTag|=4),t.firstEffect=t.lastEffect=null,i=r,n=t.child;null!==n;)l=i,(r=n).effectTag&=2,r.nextEffect=null,r.firstEffect=null,r.lastEffect=null,null===(o=r.alternate)?(r.childExpirationTime=0,r.expirationTime=l,r.child=null,r.memoizedProps=null,r.memoizedState=null,r.updateQueue=null,r.dependencies=null):(r.childExpirationTime=o.childExpirationTime,r.expirationTime=o.expirationTime,r.child=o.child,r.memoizedProps=o.memoizedProps,r.memoizedState=o.memoizedState,r.updateQueue=o.updateQueue,l=o.dependencies,r.dependencies=null===l?null:{expirationTime:l.expirationTime,firstContext:l.firstContext,responders:l.responders}),n=n.sibling;kt(fr,fr.current&ur|sr),t=t.child;break e}n=n.sibling}}else{if(!l)if(null!==(n=dr(o))){if(t.effectTag|=64,l=!0,Si(i,!0),null===i.tail&&"hidden"===i.tailMode){null!==(n=n.updateQueue)&&(t.updateQueue=n,t.effectTag|=4),null!==(t=t.lastEffect=i.lastEffect)&&(t.nextEffect=null);break}}else nn()>i.tailExpiration&&1<r&&(t.effectTag|=64,l=!0,Si(i,!1),t.expirationTime=t.childExpirationTime=r-1);i.isBackwards?(o.sibling=t.child,t.child=o):(null!==(n=i.last)?n.sibling=o:t.child=o,i.last=o)}if(null!==i.tail){0===i.tailExpiration&&(i.tailExpiration=nn()+500),n=i.tail,i.rendering=n,i.tail=n.sibling,i.lastEffect=t.lastEffect,n.sibling=null,i=fr.current,kt(fr,i=l?i&ur|sr:i&ur),t=n;break e}break;case 20:break;default:throw f(Error("Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue."))}t=null}if(i=nl,1===rl||1!==i.childExpirationTime){for(n=0,r=i.child;null!==r;)l=r.expirationTime,o=r.childExpirationTime,l>n&&(n=l),o>n&&(n=o),r=r.sibling;i.childExpirationTime=n}if(null!==t)return t;null!==e&&0==(1024&e.effectTag)&&(null===e.firstEffect&&(e.firstEffect=nl.firstEffect),null!==nl.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=nl.firstEffect),e.lastEffect=nl.lastEffect),1<nl.effectTag&&(null!==e.lastEffect?e.lastEffect.nextEffect=nl:e.firstEffect=nl,e.lastEffect=nl))}else{if(null!==(t=ki(nl)))return t.effectTag&=1023,t;null!==e&&(e.firstEffect=e.lastEffect=null,e.effectTag|=1024)}if(null!==(t=nl.sibling))return t;nl=e}while(null!==nl);return il===$i&&(il=Zi),null}function Dl(e){var t=rn();return an(99,Fl.bind(null,e,t)),null!==gl&&on(97,function(){return Ol(),null}),null}function Fl(e,t){if(Ol(),(48&el)!==Li)throw f(Error("Should not already be working."));var n=e.finishedWork,r=e.finishedExpirationTime;if(null===n)return null;if(e.finishedWork=null,e.finishedExpirationTime=0,n===e.current)throw f(Error("Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue."));e.callbackNode=null,e.callbackExpirationTime=0;var i=n.expirationTime,l=n.childExpirationTime;if(i=l>i?l:i,e.firstPendingTime=i,i<e.lastPendingTime&&(e.lastPendingTime=i),e===tl&&(nl=tl=null,rl=0),1<n.effectTag?null!==n.lastEffect?(n.lastEffect.nextEffect=n,i=n.firstEffect):i=n:i=n.firstEffect,null!==i){l=el,el|=qi,Vi.current=null,fl=i;do{try{for(;null!==fl;){if(0!=(256&fl.effectTag)){var a=fl.alternate,o=fl;switch(o.tag){case 0:case 11:case 15:Ii(mr,hr,o);break;case 1:if(256&o.effectTag&&null!==a){var u=a.memoizedProps,c=a.memoizedState,s=o.stateNode,d=s.getSnapshotBeforeUpdate(o.elementType===o.type?u:mn(o.type,u),c);s.__reactInternalSnapshotBeforeUpdate=d}break;case 3:case 5:case 6:case 4:case 17:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}fl=fl.nextEffect}}catch(e){if(null===fl)throw f(Error("Should be working on an effect."));Hl(fl,e),fl=fl.nextEffect}}while(null!==fl);fl=i;do{try{for(a=t;null!==fl;){var p=fl.effectTag;if(128&p){var h=fl.alternate;if(null!==h){var m=h.ref;null!==m&&("function"==typeof m?m(null):m.current=null)}}switch(14&p){case 2:Ai(fl),fl.effectTag&=-3;break;case 6:Ai(fl),fl.effectTag&=-3,Fi(fl.alternate,fl);break;case 4:Fi(fl.alternate,fl);break;case 8:Di(u=fl,a),zi(u)}fl=fl.nextEffect}}catch(e){if(null===fl)throw f(Error("Should be working on an effect."));Hl(fl,e),fl=fl.nextEffect}}while(null!==fl);e.current=n,fl=i;do{try{for(p=r;null!==fl;){var g=fl.effectTag;if(36&g){var v=fl.alternate;switch(m=p,(h=fl).tag){case 0:case 11:case 15:Ii(yr,br,h);break;case 1:var y=h.stateNode;if(4&h.effectTag)if(null===v)y.componentDidMount();else{var b=h.elementType===h.type?v.memoizedProps:mn(h.type,v.memoizedProps);y.componentDidUpdate(b,v.memoizedState,y.__reactInternalSnapshotBeforeUpdate)}var T=h.updateQueue;null!==T&&Fn(0,T,y);break;case 3:var E=h.updateQueue;if(null!==E){if(a=null,null!==h.child)switch(h.child.tag){case 5:a=h.child.stateNode;break;case 1:a=h.child.stateNode}Fn(0,E,a)}break;case 5:case 6:case 4:case 12:break;case 13:case 19:case 17:case 20:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}if(128&g){var x=fl.ref;if(null!==x){var w=fl.stateNode;switch(fl.tag){case 5:var S=w;break;default:S=w}"function"==typeof x?x(S):x.current=S}}512&g&&(ml=!0),fl=fl.nextEffect}}catch(e){if(null===fl)throw f(Error("Should be working on an effect."));Hl(fl,e),fl=fl.nextEffect}}while(null!==fl);fl=null,Jt(),el=l}else e.current=n;if(ml)ml=!1,gl=e,yl=r,vl=t;else for(fl=i;null!==fl;)t=fl.nextEffect,fl.nextEffect=null,fl=t;if(0!==(t=e.firstPendingTime)?_l(e,g=fn(g=wl(),t),t):hl=null,"function"==typeof Ll&&Ll(n.stateNode,r),1073741823===t?e===El?Tl++:(Tl=0,El=e):Tl=0,dl)throw dl=!1,e=pl,pl=null,e;return(el&Yi)!==Li?null:(cn(),null)}function Ol(){if(null===gl)return!1;var e=gl,t=yl,n=vl;return gl=null,yl=0,vl=90,an(97<n?97:n,jl.bind(null,e,t))}function jl(e){if((48&el)!==Li)throw f(Error("Cannot flush passive effects while already rendering."));var t=el;for(el|=qi,e=e.current.firstEffect;null!==e;){try{var n=e;if(0!=(512&n.effectTag))switch(n.tag){case 0:case 11:case 15:Ii(Er,hr,n),Ii(hr,Tr,n)}}catch(t){if(null===e)throw f(Error("Should be working on an effect."));Hl(e,t)}n=e.nextEffect,e.nextEffect=null,e=n}return el=t,cn(),!0}function Wl(e,t,n){Un(e,t=Wi(e,t=Ci(n,t),1073741823)),null!==(e=Cl(e,1073741823))&&_l(e,99,1073741823)}function Hl(e,t){if(3===e.tag)Wl(e,e,t);else for(var n=e.return;null!==n;){if(3===n.tag){Wl(n,e,t);break}if(1===n.tag){var r=n.stateNode;if("function"==typeof n.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===hl||!hl.has(r))){Un(n,e=Hi(n,e=Ci(t,e),1073741823)),null!==(n=Cl(n,1073741823))&&_l(n,99,1073741823);break}}n=n.return}}function Ql(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),tl===e&&rl===n?il===Ki||il===Ji&&1073741823===ll&&nn()-cl<sl?Il(e,rl):ul=!0:e.lastPendingTime<n||(0!==(t=e.pingTime)&&t<n||(e.pingTime=n,e.finishedExpirationTime===n&&(e.finishedExpirationTime=0,e.finishedWork=null),_l(e,t=fn(t=wl(),n),n)))}function Bl(e,t){var n=e.stateNode;null!==n&&n.delete(t),n=fn(n=wl(),t=Sl(n,e,null)),null!==(e=Cl(e,t))&&_l(e,n,t)}var Vl=void 0;Vl=function(e,t,n){var r=t.expirationTime;if(null!==e){if(e.memoizedProps!==t.pendingProps||Pt.current)li=!0;else if(r<n){switch(li=!1,t.tag){case 3:hi(t);break;case 5:ar(t);break;case 1:It(t.type)&&Dt(t);break;case 4:ir(t,t.stateNode.containerInfo);break;case 10:xn(t,t.memoizedProps.value);break;case 13:if(null!==t.memoizedState)return 0!==(r=t.child.childExpirationTime)&&r>=n?gi(e,t,n):(kt(fr,fr.current&ur),null!==(t=bi(e,t,n))?t.sibling:null);kt(fr,fr.current&ur);break;case 19:if(r=t.childExpirationTime>=n,0!=(64&e.effectTag)){if(r)return yi(e,t,n);t.effectTag|=64}var i=t.memoizedState;if(null!==i&&(i.rendering=null,i.tail=null),kt(fr,fr.current),!r)return null}return bi(e,t,n)}}else li=!1;switch(t.expirationTime=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=Nt(t,_t.current),kn(t,n),i=Or(null,t,r,e,i,n),t.effectTag|=1,"object"==typeof i&&null!==i&&"function"==typeof i.render&&void 0===i.$$typeof){if(t.tag=1,jr(),It(r)){var l=!0;Dt(t)}else l=!1;t.memoizedState=null!==i.state&&void 0!==i.state?i.state:null;var a=r.getDerivedStateFromProps;"function"==typeof a&&Hn(t,r,a,e),i.updater=Qn,t.stateNode=i,i._reactInternalFiber=t,Yn(t,r,e,n),t=pi(null,t,r,!0,l,n)}else t.tag=0,ai(null,t,i,n),t=t.child;return t;case 16:switch(i=t.elementType,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=gn(i),t.type=i,l=t.tag=Jl(i),e=mn(i,e),l){case 0:t=fi(null,t,i,e,n);break;case 1:t=di(null,t,i,e,n);break;case 11:t=oi(null,t,i,e,n);break;case 14:t=ui(null,t,i,mn(i.type,e),r,n);break;default:throw f(Error("Element type is invalid. Received a promise that resolves to: "+i+". Lazy element type must resolve to a class or function."))}return t;case 0:return r=t.type,i=t.pendingProps,fi(e,t,r,i=t.elementType===r?i:mn(r,i),n);case 1:return r=t.type,i=t.pendingProps,di(e,t,r,i=t.elementType===r?i:mn(r,i),n);case 3:if(hi(t),null===(r=t.updateQueue))throw f(Error("If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue."));return i=null!==(i=t.memoizedState)?i.element:null,Dn(t,r,t.pendingProps,null,n),(r=t.memoizedState.element)===i?t=bi(e,t,n):(ai(e,t,r,n),t=t.child),t;case 5:return ar(t),null===e&&ri(t),r=t.pendingProps.children,si(e,t),ai(e,t,r,n),t.child;case 6:return null===e&&ri(t),null;case 13:return gi(e,t,n);case 4:return ir(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=Jn(t,null,r,n):ai(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,oi(e,t,r,i=t.elementType===r?i:mn(r,i),n);case 7:return ai(e,t,t.pendingProps,n),t.child;case 8:case 12:return ai(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,a=t.memoizedProps,xn(t,l=i.value),null!==a){var o=a.value;if(0===(l=dn(o,l)?0:0|("function"==typeof r._calculateChangedBits?r._calculateChangedBits(o,l):1073741823))){if(a.children===i.children&&!Pt.current){t=bi(e,t,n);break e}}else for(null!==(o=t.child)&&(o.return=t);null!==o;){var u=o.dependencies;if(null!==u){a=o.child;for(var c=u.firstContext;null!==c;){if(c.context===r&&0!=(c.observedBits&l)){1===o.tag&&((c=Nn(n,null)).tag=2,Un(o,c)),o.expirationTime<n&&(o.expirationTime=n),null!==(c=o.alternate)&&c.expirationTime<n&&(c.expirationTime=n),Sn(o.return,n),u.expirationTime<n&&(u.expirationTime=n);break}c=c.next}}else a=10===o.tag&&o.type===t.type?null:o.child;if(null!==a)a.return=o;else for(a=o;null!==a;){if(a===t){a=null;break}if(null!==(o=a.sibling)){o.return=a.return,a=o;break}a=a.return}o=a}}ai(e,t,i.children,n),t=t.child}return t;case 9:return i=t.type,r=(l=t.pendingProps).children,kn(t,n),r=r(i=Cn(i,l.unstable_observedBits)),t.effectTag|=1,ai(e,t,r,n),t.child;case 14:return l=mn(i=t.type,t.pendingProps),ui(e,t,i,l=mn(i.type,l),r,n);case 15:return ci(e,t,t.type,t.pendingProps,r,n);case 17:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:mn(r,i),null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),t.tag=1,It(r)?(e=!0,Dt(t)):e=!1,kn(t,n),Vn(t,r,i),Yn(t,r,i,n),pi(null,t,r,!0,e,n);case 19:return yi(e,t,n)}throw f(Error("Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue."))};var Ll=null,Yl=null;function Xl(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);Ll=function(e){try{t.onCommitFiberRoot(n,e,void 0,64==(64&e.current.effectTag))}catch(e){}},Yl=function(e){try{t.onCommitFiberUnmount(n,e)}catch(e){}}}catch(e){}return!0}function ql(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childExpirationTime=this.expirationTime=0,this.alternate=null}function $l(e,t,n,r){return new ql(e,t,n,r)}function Gl(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Jl(e){if("function"==typeof e)return Gl(e)?1:0;if(void 0!==e&&null!==e){if((e=e.$$typeof)===Be)return 11;if(e===Ye)return 14}return 2}function Kl(e,t){var n=e.alternate;return null===n?((n=$l(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.effectTag=0,n.nextEffect=null,n.firstEffect=null,n.lastEffect=null),n.childExpirationTime=e.childExpirationTime,n.expirationTime=e.expirationTime,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{expirationTime:t.expirationTime,firstContext:t.firstContext,responders:t.responders},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Zl(e,t,n,r,i,l){var a=2;if(r=e,"function"==typeof e)Gl(e)&&(a=1);else if("string"==typeof e)a=5;else e:switch(e){case Fe:return ea(n.children,i,l,t);case Qe:a=8,i|=7;break;case Oe:a=8,i|=1;break;case je:return(e=$l(12,n,t,8|i)).elementType=je,e.type=je,e.expirationTime=l,e;case Ve:return(e=$l(13,n,t,i)).type=Ve,e.elementType=Ve,e.expirationTime=l,e;case Le:return(e=$l(19,n,t,i)).elementType=Le,e.expirationTime=l,e;default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case We:a=10;break e;case He:a=9;break e;case Be:a=11;break e;case Ye:a=14;break e;case Xe:a=16,r=null;break e}throw f(Error("Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: "+(null==e?e:typeof e)+"."))}return(t=$l(a,n,t,i)).elementType=e,t.type=r,t.expirationTime=l,t}function ea(e,t,n,r){return(e=$l(7,e,r,t)).expirationTime=n,e}function ta(e,t,n){return(e=$l(6,e,null,t)).expirationTime=n,e}function na(e,t,n){return(t=$l(4,null!==e.children?e.children:[],e.key,t)).expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function ra(e,t,n){this.tag=t,this.current=null,this.containerInfo=e,this.pingCache=this.pendingChildren=null,this.finishedExpirationTime=0,this.finishedWork=null,this.timeoutHandle=-1,this.pendingContext=this.context=null,this.hydrate=n,this.callbackNode=this.firstBatch=null,this.pingTime=this.lastPendingTime=this.firstPendingTime=this.callbackExpirationTime=0}function ia(e){var t=e._reactInternalFiber;if(void 0===t){if("function"==typeof e.render)throw f(Error("Unable to find node on an unmounted component."));throw f(Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(e)))}return null===(e=et(t))?null:e.stateNode}function la(e,t,n,r){var i=t.current,l=wl(),a=jn.suspense;i=Sl(l,i,a),l=t.current;e:if(n){n=n._reactInternalFiber;t:{if(2!==Je(n)||1!==n.tag)throw f(Error("Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue."));var o=n;do{switch(o.tag){case 3:o=o.stateNode.context;break t;case 1:if(It(o.type)){o=o.stateNode.__reactInternalMemoizedMergedChildContext;break t}}o=o.return}while(null!==o);throw f(Error("Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue."))}if(1===n.tag){var u=n.type;if(It(u)){n=At(n,u,o);break e}}n=o}else n=Ct;return null===t.context?t.context=n:t.pendingContext=n,t=r,(a=Nn(i,a)).payload={element:e},null!==(t=void 0===t?null:t)&&(a.callback=t),Un(l,a),kl(l,i),i}function aa(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:De,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}function oa(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var ua;function ca(e){return null==e?null:"number"==typeof e?e:e._nativeTag?e._nativeTag:e.canonical&&e.canonical._nativeTag?e.canonical._nativeTag:null==(e=ia(e))?e:e.canonical?e.canonical._nativeTag:e._nativeTag}ua=function(){throw f(Error("getInspectorDataForViewTag() is not available in production"))},_e=function(e,t){var n=el;el|=1;try{return e(t)}finally{(el=n)===Li&&cn()}},Pe=function(){(49&el)===Li&&(Nl(),Ol())};var sa,fa,da=new Map,pa={NativeComponent:(function(e,t){return(function(n){function r(){if(!(this instanceof r))throw new TypeError("Cannot call a class as a function");var e=n.apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?this:e}return oa(r,n),r.prototype.blur=function(){u.TextInputState.blurTextInput(e(this))},r.prototype.focus=function(){u.TextInputState.focusTextInput(e(this))},r.prototype.measure=function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measure(r.node,ct(this,n)):u.UIManager.measure(e(this),ct(this,n)))},r.prototype.measureInWindow=function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measureInWindow(r.node,ct(this,n)):u.UIManager.measureInWindow(e(this),ct(this,n)))},r.prototype.measureLayout=function(n,r,i){var l=void 0;try{l=t(this)}catch(e){}null==l||l.canonical||(l=void 0,"number"==typeof n?l=n:n._nativeTag&&(l=n._nativeTag),null!=l&&u.UIManager.measureLayout(e(this),l,ct(this,i),ct(this,r)))},r.prototype.setNativeProps=function(e){var n=void 0;try{n=t(this)}catch(e){}if(null!=n&&!n.canonical){var r=n._nativeTag||n.canonical._nativeTag;n=n.viewConfig||n.canonical.viewConfig,null!=(e=ut(null,tt,e,n.validAttributes))&&u.UIManager.updateView(r,n.uiViewClassName,e)}},r})(c.Component)})(ca,ia),findNodeHandle:ca,dispatchCommand:function(e,t,n){null!=e._nativeTag&&u.UIManager.dispatchViewManagerCommand(e._nativeTag,t,n)},setNativeProps:function(e,t){null!=e._nativeTag&&(null!=(t=ut(null,tt,t,e.viewConfig.validAttributes))&&u.UIManager.updateView(e._nativeTag,e.viewConfig.uiViewClassName,t))},render:function(e,t,n){var r=da.get(t);if(!r){r=new ra(t,0,!1);var i=$l(3,null,null,0);r.current=i,i.stateNode=r,da.set(t,r)}la(e,r,null,n);e:if(e=r.current,e.child)switch(e.child.tag){case 5:e=e.child.stateNode;break e;default:e=e.child.stateNode}else e=null;return e},unmountComponentAtNode:function(e){var t=da.get(e);t&&la(null,t,null,function(){da.delete(e)})},unmountComponentAtNodeAndRemoveContainer:function(e){pa.unmountComponentAtNode(e),u.UIManager.removeRootView(e)},createPortal:function(e,t){return aa(e,t,null,2<arguments.length&&void 0!==arguments[2]?arguments[2]:null)},unstable_batchedUpdates:Ne,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{NativeMethodsMixin:(function(e,t){return{measure:function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measure(r.node,ct(this,n)):u.UIManager.measure(e(this),ct(this,n)))},measureInWindow:function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measureInWindow(r.node,ct(this,n)):u.UIManager.measureInWindow(e(this),ct(this,n)))},measureLayout:function(n,r,i){var l=void 0;try{l=t(this)}catch(e){}null==l||l.canonical||(l=void 0,"number"==typeof n?l=n:n._nativeTag&&(l=n._nativeTag),null!=l&&u.UIManager.measureLayout(e(this),l,ct(this,i),ct(this,r)))},setNativeProps:function(e){var n=void 0;try{n=t(this)}catch(e){}if(null!=n&&!n.canonical){var r=n._nativeTag||n.canonical._nativeTag;n=n.viewConfig||n.canonical.viewConfig,null!=(e=ut(null,tt,e,n.validAttributes))&&u.UIManager.updateView(r,n.uiViewClassName,e)}},focus:function(){u.TextInputState.focusTextInput(e(this))},blur:function(){u.TextInputState.blurTextInput(e(this))}}})(ca,ia),computeComponentStackForErrorReporting:function(e){return(e=we(e))?Et(e):""}}};fa=(sa={findFiberByHostInstance:we,getInspectorDataForViewTag:ua,bundleType:0,version:"16.8.6",rendererPackageName:"react-native-renderer"}).findFiberByHostInstance,Xl(o({},sa,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:ze.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=et(e))?null:e.stateNode},findFiberByHostInstance:function(e){return fa?fa(e):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}));var ha={default:pa},ma=ha&&pa||ha;i.exports=ma.default||ma},91,[16,92,160,13,166]); -__d(function(g,r,i,a,m,e,d){r(d[0])},92,[93]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=Date.now();r(d[0]),r(d[1]),r(d[2]),r(d[3]),r(d[4]),r(d[5]),r(d[6]),r(d[7]),r(d[8]),r(d[9]),r(d[10]);var n=r(d[11]);n.markPoint('initializeCore_start',n.currentTimestamp()-(Date.now()-t)),n.markPoint('initializeCore_end')},93,[94,95,96,104,106,108,114,139,144,145,158,152]); -__d(function(g,r,i,a,m,e,d){'use strict';void 0===g.GLOBAL&&(g.GLOBAL=g),void 0===g.window&&(g.window=g),void 0===g.self&&(g.self=g),g.process=g.process||{},g.process.env=g.process.env||{},g.process.env.NODE_ENV||(g.process.env.NODE_ENV='production')},94,[]); -__d(function(g,r,i,a,m,e,d){'use strict';if(g.__RCTProfileIsProfiling){var t=r(d[0]);t.installReactHook(),t.setEnabled(!0)}},95,[37]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]);if(o.installConsoleErrorReporter(),!g.__fbDisableExceptionsManager){r(d[1]).setGlobalHandler(function(t,n){try{o.handleException(t,n)}catch(o){throw console.log('Failed to print error: ',o.message),t}})}},96,[97,36]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),o=r(d[1]),s=r(d[2]),t=r(d[3]),l=(function(l){function c(){var t,l;n(this,c);for(var p=arguments.length,f=new Array(p),u=0;u<p;u++)f[u]=arguments[u];return(l=o(this,(t=s(c)).call.apply(t,[this].concat(f)))).name='',l}return t(c,l),c})(r(d[4])(Error)),c=0;function p(n,o){var s=r(d[5]).default;if(s){var t=r(d[6])(n),l=++c,p=n.message||'',f=p;null!=n.componentStack&&(f+="\n\nThis error is located at:"+n.componentStack);var u=null==n.name||''===n.name?'':n.name+": ",E='console.error'===n.name;f.startsWith(u)||(f=u+f),E||(console._errorOriginal?console._errorOriginal(f):console.error(f)),f=null==n.jsEngine?f:f+", js engine: "+n.jsEngine,s.reportException({message:f,originalMessage:f===p?null:p,name:null==n.name||''===n.name?null:n.name,componentStack:'string'==typeof n.componentStack?n.componentStack:null,stack:t,id:l,isFatal:o,extraData:{jsEngine:n.jsEngine,rawStack:n.stack,framesPopped:n.framesToPop}})}}function f(){if(console.reportErrorsAsExceptions)if(arguments[0]&&arguments[0].stack)p(arguments[0],!1);else{console._errorOriginal.apply(console,arguments);var n=r(d[7]),o=Array.prototype.map.call(arguments,n).join(', ');if('"Warning: '===o.slice(0,10))return;var s=new l(o);s.name='console.error',s.framesToPop=(s.framesToPop||0)+1,p(s,!1)}else console._errorOriginal.apply(console,arguments)}m.exports={handleException:function(n,o){p(n instanceof Error?n:new l(n),o)},installConsoleErrorReporter:function(){console._errorOriginal||(console._errorOriginal=console.error.bind(console),console.error=f,void 0===console.reportErrorsAsExceptions&&(console.reportErrorsAsExceptions=!0))},SyntheticError:l}},97,[4,6,9,10,98,101,102,39]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),o=r(d[2]),u=r(d[3]);function c(f){var p="function"==typeof Map?new Map:void 0;return m.exports=c=function(c){if(null===c||!o(c))return c;if("function"!=typeof c)throw new TypeError("Super expression must either be null or a function");if(void 0!==p){if(p.has(c))return p.get(c);p.set(c,f)}function f(){return u(c,arguments,t(this).constructor)}return f.prototype=Object.create(c.prototype,{constructor:{value:f,enumerable:!1,writable:!0,configurable:!0}}),n(f,c)},c(f)}m.exports=c},98,[9,11,99,100]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){return-1!==Function.toString.call(n).indexOf("[native code]")}},99,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);function n(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}function c(o,u,f){return n()?m.exports=c=Reflect.construct:m.exports=c=function(n,c,o){var u=[null];u.push.apply(u,c);var f=new(Function.bind.apply(n,u));return o&&t(f,o.prototype),f},c.apply(null,arguments)}m.exports=c},100,[11]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])),n=(r(d[2]),o.getEnforcing('ExceptionsManager')),c={reportFatalException:function(t,o,c){n.reportFatalException(t,o,c)},reportSoftException:function(t,o,c){n.reportSoftException(t,o,c)},updateExceptionMessage:function(t,o,c){n.updateExceptionMessage(t,o,c)},dismissRedbox:function(){},reportException:function(t){t.isFatal?c.reportFatalException(t.message,t.stack,t.id):c.reportSoftException(t.message,t.stack,t.id)}},p=c;e.default=p},101,[2,24,58]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(t){if(!t||!t.stack)return[];for(var s=r(d[0]),o=Array.isArray(t.stack)?t.stack:s.parse(t.stack),f='number'==typeof t.framesToPop?t.framesToPop:0;f--;)o.shift();return o}},102,[103]); -__d(function(g,r,i,a,m,e,d){var n,l;n=this,l=function(n){'use strict';var l='<unknown>';var u=/^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,t=/\((\S*)(?::(\d+))(?::(\d+))\)/;function o(n){var o=u.exec(n);if(!o)return null;var c=o[2]&&0===o[2].indexOf('native'),s=o[2]&&0===o[2].indexOf('eval'),f=t.exec(o[2]);return s&&null!=f&&(o[2]=f[1],o[3]=f[2],o[4]=f[3]),{file:c?null:o[2],methodName:o[1]||l,arguments:c?[o[2]]:[],lineNumber:o[3]?+o[3]:null,column:o[4]?+o[4]:null}}var c=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;function s(n){var u=c.exec(n);return u?{file:u[2],methodName:u[1]||l,arguments:[],lineNumber:+u[3],column:u[4]?+u[4]:null}:null}var f=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,v=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i;function b(n){var u=f.exec(n);if(!u)return null;var t=u[3]&&u[3].indexOf(' > eval')>-1,o=v.exec(u[3]);return t&&null!=o&&(u[3]=o[1],u[4]=o[2],u[5]=null),{file:u[3],methodName:u[1]||l,arguments:u[2]?u[2].split(','):[],lineNumber:u[4]?+u[4]:null,column:u[5]?+u[5]:null}}var p=/^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;function x(n){var u=p.exec(n);return u?{file:u[3],methodName:u[1]||l,arguments:[],lineNumber:+u[4],column:u[5]?+u[5]:null}:null}var h=/^\s*at (?:((?:\[object object\])?.+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;function N(n){var u=h.exec(n);return u?{file:u[2],methodName:u[1]||l,arguments:[],lineNumber:+u[3],column:u[4]?+u[4]:null}:null}n.parse=function(n){return n.split('\n').reduce(function(n,l){var u=o(l)||s(l)||b(l)||N(l)||x(l);return u&&n.push(u),n},[])},Object.defineProperty(n,'__esModule',{value:!0})},'object'==typeof e&&void 0!==m?l(e):'function'==typeof define&&define.amd?define(['exports'],l):l((n=n||self).stackTraceParser={})},103,[]); -__d(function(g,r,i,a,m,e,d){'use strict';(0,r(d[0]).polyfillGlobal)('Promise',function(){return r(d[1])})},104,[105,41]); -__d(function(g,r,i,a,m,e,d){'use strict';var l=r(d[0]);function o(o,t,n){var c=Object.getOwnPropertyDescriptor(o,t),b=c||{},f=b.enumerable,u=b.writable,p=b.configurable;!c||p?l(o,t,{get:n,enumerable:!1!==f,writable:!1!==u}):console.error('Failed to set polyfill. '+t+' is not configurable.')}m.exports={polyfillObjectProperty:o,polyfillGlobal:function(l,t){o(g,l,t)}}},105,[40]); -__d(function(g,r,i,a,m,e,d){'use strict';(0,r(d[0]).polyfillGlobal)('regeneratorRuntime',function(){return delete g.regeneratorRuntime,r(d[1])})},106,[105,107]); -__d(function(g,r,i,a,m,e,d){var t=(function(t){"use strict";var n,o=Object.prototype,c=o.hasOwnProperty,u="function"==typeof Symbol?Symbol:{},h=u.iterator||"@@iterator",f=u.asyncIterator||"@@asyncIterator",s=u.toStringTag||"@@toStringTag";function l(t,n,o,c){var u=n&&n.prototype instanceof E?n:E,h=Object.create(u.prototype),f=new A(c||[]);return h._invoke=F(t,o,f),h}function p(t,n,o){try{return{type:"normal",arg:t.call(n,o)}}catch(t){return{type:"throw",arg:t}}}t.wrap=l;var y="suspendedStart",v="suspendedYield",w="executing",L="completed",x={};function E(){}function b(){}function _(){}var j={};j[h]=function(){return this};var O=Object.getPrototypeOf,k=O&&O(O(R([])));k&&k!==o&&c.call(k,h)&&(j=k);var G=_.prototype=E.prototype=Object.create(j);function N(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function P(t){function n(o,u,h,f){var s=p(t[o],t,u);if("throw"!==s.type){var l=s.arg,y=l.value;return y&&"object"==typeof y&&c.call(y,"__await")?Promise.resolve(y.__await).then(function(t){n("next",t,h,f)},function(t){n("throw",t,h,f)}):Promise.resolve(y).then(function(t){l.value=t,h(l)},function(t){return n("throw",t,h,f)})}f(s.arg)}var o;this._invoke=function(t,c){function u(){return new Promise(function(o,u){n(t,c,o,u)})}return o=o?o.then(u,u):u()}}function F(t,n,o){var c=y;return function(u,h){if(c===w)throw new Error("Generator is already running");if(c===L){if("throw"===u)throw h;return Y()}for(o.method=u,o.arg=h;;){var f=o.delegate;if(f){var s=S(f,o);if(s){if(s===x)continue;return s}}if("next"===o.method)o.sent=o._sent=o.arg;else if("throw"===o.method){if(c===y)throw c=L,o.arg;o.dispatchException(o.arg)}else"return"===o.method&&o.abrupt("return",o.arg);c=w;var l=p(t,n,o);if("normal"===l.type){if(c=o.done?L:v,l.arg===x)continue;return{value:l.arg,done:o.done}}"throw"===l.type&&(c=L,o.method="throw",o.arg=l.arg)}}}function S(t,o){var c=t.iterator[o.method];if(c===n){if(o.delegate=null,"throw"===o.method){if(t.iterator.return&&(o.method="return",o.arg=n,S(t,o),"throw"===o.method))return x;o.method="throw",o.arg=new TypeError("The iterator does not provide a 'throw' method")}return x}var u=p(c,t.iterator,o.arg);if("throw"===u.type)return o.method="throw",o.arg=u.arg,o.delegate=null,x;var h=u.arg;return h?h.done?(o[t.resultName]=h.value,o.next=t.nextLoc,"return"!==o.method&&(o.method="next",o.arg=n),o.delegate=null,x):h:(o.method="throw",o.arg=new TypeError("iterator result is not an object"),o.delegate=null,x)}function T(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function I(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function A(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(T,this),this.reset(!0)}function R(t){if(t){var o=t[h];if(o)return o.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var u=-1,f=function o(){for(;++u<t.length;)if(c.call(t,u))return o.value=t[u],o.done=!1,o;return o.value=n,o.done=!0,o};return f.next=f}}return{next:Y}}function Y(){return{value:n,done:!0}}return b.prototype=G.constructor=_,_.constructor=b,_[s]=b.displayName="GeneratorFunction",t.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===b||"GeneratorFunction"===(n.displayName||n.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,_):(t.__proto__=_,s in t||(t[s]="GeneratorFunction")),t.prototype=Object.create(G),t},t.awrap=function(t){return{__await:t}},N(P.prototype),P.prototype[f]=function(){return this},t.AsyncIterator=P,t.async=function(n,o,c,u){var h=new P(l(n,o,c,u));return t.isGeneratorFunction(o)?h:h.next().then(function(t){return t.done?t.value:h.next()})},N(G),G[s]="Generator",G[h]=function(){return this},G.toString=function(){return"[object Generator]"},t.keys=function(t){var n=[];for(var o in t)n.push(o);return n.reverse(),function o(){for(;n.length;){var c=n.pop();if(c in t)return o.value=c,o.done=!1,o}return o.done=!0,o}},t.values=R,A.prototype={constructor:A,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=n,this.done=!1,this.delegate=null,this.method="next",this.arg=n,this.tryEntries.forEach(I),!t)for(var o in this)"t"===o.charAt(0)&&c.call(this,o)&&!isNaN(+o.slice(1))&&(this[o]=n)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var o=this;function u(c,u){return s.type="throw",s.arg=t,o.next=c,u&&(o.method="next",o.arg=n),!!u}for(var h=this.tryEntries.length-1;h>=0;--h){var f=this.tryEntries[h],s=f.completion;if("root"===f.tryLoc)return u("end");if(f.tryLoc<=this.prev){var l=c.call(f,"catchLoc"),p=c.call(f,"finallyLoc");if(l&&p){if(this.prev<f.catchLoc)return u(f.catchLoc,!0);if(this.prev<f.finallyLoc)return u(f.finallyLoc)}else if(l){if(this.prev<f.catchLoc)return u(f.catchLoc,!0)}else{if(!p)throw new Error("try statement without catch or finally");if(this.prev<f.finallyLoc)return u(f.finallyLoc)}}}},abrupt:function(t,n){for(var o=this.tryEntries.length-1;o>=0;--o){var u=this.tryEntries[o];if(u.tryLoc<=this.prev&&c.call(u,"finallyLoc")&&this.prev<u.finallyLoc){var h=u;break}}h&&("break"===t||"continue"===t)&&h.tryLoc<=n&&n<=h.finallyLoc&&(h=null);var f=h?h.completion:{};return f.type=t,f.arg=n,h?(this.method="next",this.next=h.finallyLoc,x):this.complete(f)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),x},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.finallyLoc===t)return this.complete(o.completion,o.afterLoc),I(o),x}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc===t){var c=o.completion;if("throw"===c.type){var u=c.arg;I(o)}return u}}throw new Error("illegal catch attempt")},delegateYield:function(t,o,c){return this.delegate={iterator:R(t),resultName:o,nextLoc:c},"next"===this.method&&(this.arg=n),x}},t})("object"==typeof m?m.exports:{});try{regeneratorRuntime=t}catch(n){Function("r","regeneratorRuntime = r")(t)}},107,[]); -__d(function(g,r,i,a,m,e,d){'use strict';if(!g.RN$Bridgeless){var l=r(d[0]).polyfillGlobal,t=function(t){l(t,function(){return r(d[1])[t]})};t('setTimeout'),t('setInterval'),t('setImmediate'),t('clearTimeout'),t('clearInterval'),t('clearImmediate'),t('requestAnimationFrame'),t('cancelAnimationFrame'),t('requestIdleCallback'),t('cancelIdleCallback')}},108,[105,109]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),n=r(d[2]),l=(r(d[3]),r(d[4]),r(d[5])),u=null;function o(){return u||(u=r(d[6])),u()}var c=16.666666666666668,f=[],s=[],v=[],h=[],I=[],T={},b=[],p=1,w=null,k=!1;function q(){var t=v.indexOf(null);return-1===t&&(t=v.length),t}function x(t,n){var l=p++,u=q();return v[u]=l,f[u]=t,s[u]=n,l}function y(t,n,l){r(d[7])(t<=p,'Tried to call timer with ID %s but no such timer exists.',t);var u=v.indexOf(t);if(-1!==u){var h=s[u],I=f[u];if(I&&h){'setTimeout'!==h&&'setImmediate'!==h&&'requestAnimationFrame'!==h&&'requestIdleCallback'!==h||A(u);try{'setTimeout'===h||'setInterval'===h||'setImmediate'===h?I():'requestAnimationFrame'===h?I(o()):'requestIdleCallback'===h?I({timeRemaining:function(){return Math.max(0,c-(o()-n))},didTimeout:!!l}):console.error('Tried to call a callback with invalid type: '+h)}catch(t){w?w.push(t):w=[t]}}else console.error('No callback found for timerID '+t)}}function C(){if(h.length>0){var t=h.slice();h=[];for(var n=0;n<t.length;++n)y(t[n],0)}return h.length>0}function A(t){v[t]=null,f[t]=null,s[t]=null,b[t]=null}function D(t){if(null!=t){var n=v.indexOf(t);if(-1!==n){A(n);var l=s[n];'setImmediate'!==l&&'requestIdleCallback'!==l&&E(t)}}}var O,F={setTimeout:function(t,n){for(var l=arguments.length,u=new Array(l>2?l-2:0),o=2;o<l;o++)u[o-2]=arguments[o];var c=x(function(){return t.apply(void 0,u)},'setTimeout');return N(c,n||0,Date.now(),!1),c},setInterval:function(t,n){for(var l=arguments.length,u=new Array(l>2?l-2:0),o=2;o<l;o++)u[o-2]=arguments[o];var c=x(function(){return t.apply(void 0,u)},'setInterval');return N(c,n||0,Date.now(),!0),c},setImmediate:function(t){for(var n=arguments.length,l=new Array(n>1?n-1:0),u=1;u<n;u++)l[u-1]=arguments[u];var o=x(function(){return t.apply(void 0,l)},'setImmediate');return h.push(o),o},requestAnimationFrame:function(t){var n=x(t,'requestAnimationFrame');return N(n,1,Date.now(),!1),n},requestIdleCallback:function(t,n){0===I.length&&_(!0);var l=n&&n.timeout,u=x(null!=l?function(n){var l=T[u];return l&&(F.clearTimeout(l),delete T[u]),t(n)}:t,'requestIdleCallback');if(I.push(u),null!=l){var c=F.setTimeout(function(){var t=I.indexOf(u);t>-1&&(I.splice(t,1),y(u,o(),!0)),delete T[u],0===I.length&&_(!1)},l);T[u]=c}return u},cancelIdleCallback:function(t){D(t);var n=I.indexOf(t);-1!==n&&I.splice(n,1);var l=T[t];l&&(F.clearTimeout(l),delete T[t]),0===I.length&&_(!1)},clearTimeout:function(t){D(t)},clearInterval:function(t){D(t)},clearImmediate:function(t){D(t);var n=h.indexOf(t);-1!==n&&h.splice(n,1)},cancelAnimationFrame:function(t){D(t)},callTimers:function(t){l(0!==t.length,'Cannot call `callTimers` with an empty list of IDs.'),w=null;for(var n=0;n<t.length;n++)y(t[n],0);if(w){var u=w.length;if(u>1)for(var o=1;o<u;o++)F.setTimeout(function(t){throw t}.bind(null,w[o]),0);throw w[0]}},callIdleCallbacks:function(t){if(!(c-(o()-t)<1)){if(w=null,I.length>0){var n=I.slice();I=[];for(var l=0;l<n.length;++l)y(n[l],t)}0===I.length&&_(!1),w&&w.forEach(function(t){return F.setTimeout(function(){throw t},0)})}},callImmediates:function(){for(w=null;C(););w&&w.forEach(function(t){return F.setTimeout(function(){throw t},0)})},emitTimeDriftWarning:function(t){k||(k=!0,console.warn(t))}};function N(n,u,o,c){l(t.default,'NativeTiming is available'),t.default.createTimer(n,u,o,c)}function E(n){l(t.default,'NativeTiming is available'),t.default.deleteTimer(n)}function _(n){l(t.default,'NativeTiming is available'),t.default.setSendIdleEvents(n)}t.default?O=F:(console.warn("Timing native module is not available, can't set timers."),O={callImmediates:F.callImmediates,setImmediate:F.setImmediate}),n.setImmediatesCallback(O.callImmediates.bind(O)),m.exports=O},109,[3,110,30,58,37,18,111,20]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('Timing');e.default=u},110,[2,24]); -__d(function(g,r,i,a,m,e,d){"use strict";var n,t=r(d[0]);n=t.now?function(){return t.now()}:function(){return Date.now()},m.exports=n},111,[112]); -__d(function(g,r,i,a,m,e,d){'use strict';var n;r(d[0]).canUseDOM&&(n=window.performance||window.msPerformance||window.webkitPerformance),m.exports=n||{}},112,[113]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=!('undefined'==typeof window||!window.document||!window.document.createElement),t={canUseDOM:n,canUseWorkers:'undefined'!=typeof Worker,canUseEventListeners:n&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:n&&!!window.screen,isInWorker:!n};m.exports=t},113,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]).polyfillGlobal;n('XMLHttpRequest',function(){return r(d[1])}),n('FormData',function(){return r(d[2])}),n('fetch',function(){return r(d[3]).fetch}),n('Headers',function(){return r(d[3]).Headers}),n('Request',function(){return r(d[3]).Request}),n('Response',function(){return r(d[3]).Response}),n('WebSocket',function(){return r(d[4])}),n('Blob',function(){return r(d[5])}),n('File',function(){return r(d[6])}),n('FileReader',function(){return r(d[7])}),n('URL',function(){return r(d[8]).URL}),n('URLSearchParams',function(){return r(d[8]).URLSearchParams}),n('AbortController',function(){return r(d[9]).AbortController}),n('AbortSignal',function(){return r(d[9]).AbortSignal})},114,[105,115,127,128,130,118,133,134,136,137]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n=r(d[2]),o=r(d[3]),h=r(d[4]),p=r(d[5]),u=r(d[6]),_=r(d[7]),c=r(d[8]),l=r(d[9]),y=r(d[10]),v=r(d[11]),f=r(d[12]);_.isAvailable&&_.addNetworkingHandler();var R=0,E=1,b=2,k=3,N=4,w={arraybuffer:'function'==typeof g.ArrayBuffer,blob:'function'==typeof g.Blob,document:!1,json:!0,text:!0,'':!0},S=['abort','error','load','loadstart','progress','timeout','loadend'],D=S.concat('readystatechange'),T=(function(t){function s(){return o(this,s),h(this,p(s).apply(this,arguments))}return u(s,t),s})(c.apply(void 0,S)),q=(function(c){function S(){var t;return o(this,S),(t=h(this,p(S).call(this))).UNSENT=R,t.OPENED=E,t.HEADERS_RECEIVED=b,t.LOADING=k,t.DONE=N,t.readyState=R,t.status=0,t.timeout=0,t.withCredentials=!0,t.upload=new T,t._aborted=!1,t._hasError=!1,t._method=null,t._response='',t._url=null,t._timedOut=!1,t._trackingName='unknown',t._incrementalEvents=!1,t._reset(),t}return u(S,c),n(S,null,[{key:"setInterceptor",value:function(t){S._interceptor=t}}]),n(S,[{key:"_reset",value:function(){this.readyState=this.UNSENT,this.responseHeaders=void 0,this.status=0,delete this.responseURL,this._requestId=null,this._cachedResponse=void 0,this._hasError=!1,this._headers={},this._response='',this._responseType='',this._sent=!1,this._lowerCaseResponseHeaders={},this._clearSubscriptions(),this._timedOut=!1}},{key:"__didCreateRequest",value:function(t){this._requestId=t,S._interceptor&&S._interceptor.requestSent(t,this._url||'',this._method||'GET',this._headers)}},{key:"__didUploadProgress",value:function(t,s,n){t===this._requestId&&this.upload.dispatchEvent({type:'progress',lengthComputable:!0,loaded:s,total:n})}},{key:"__didReceiveResponse",value:function(t,s,n,o){t===this._requestId&&(this.status=s,this.setResponseHeaders(n),this.setReadyState(this.HEADERS_RECEIVED),o||''===o?this.responseURL=o:delete this.responseURL,S._interceptor&&S._interceptor.responseReceived(t,o||this._url||'',s,n||{}))}},{key:"__didReceiveData",value:function(t,s){t===this._requestId&&(this._response=s,this._cachedResponse=void 0,this.setReadyState(this.LOADING),S._interceptor&&S._interceptor.dataReceived(t,s))}},{key:"__didReceiveIncrementalData",value:function(t,s,n,o){t===this._requestId&&(this._response?this._response+=s:this._response=s,S._interceptor&&S._interceptor.dataReceived(t,s),this.setReadyState(this.LOADING),this.__didReceiveDataProgress(t,n,o))}},{key:"__didReceiveDataProgress",value:function(t,s,n){t===this._requestId&&this.dispatchEvent({type:'progress',lengthComputable:n>=0,loaded:s,total:n})}},{key:"__didCompleteResponse",value:function(t,s,n){t===this._requestId&&(s&&(''!==this._responseType&&'text'!==this._responseType||(this._response=s),this._hasError=!0,n&&(this._timedOut=!0)),this._clearSubscriptions(),this._requestId=null,this.setReadyState(this.DONE),s?S._interceptor&&S._interceptor.loadingFailed(t,s):S._interceptor&&S._interceptor.loadingFinished(t,this._response.length))}},{key:"_clearSubscriptions",value:function(){(this._subscriptions||[]).forEach(function(t){t&&t.remove()}),this._subscriptions=[]}},{key:"getAllResponseHeaders",value:function(){if(!this.responseHeaders)return null;var t=this.responseHeaders||{};return Object.keys(t).map(function(s){return s+': '+t[s]}).join('\r\n')}},{key:"getResponseHeader",value:function(t){var s=this._lowerCaseResponseHeaders[t.toLowerCase()];return void 0!==s?s:null}},{key:"setRequestHeader",value:function(t,s){if(this.readyState!==this.OPENED)throw new Error('Request has not been opened');this._headers[t.toLowerCase()]=String(s)}},{key:"setTrackingName",value:function(t){return this._trackingName=t,this}},{key:"open",value:function(t,s,n){if(this.readyState!==this.UNSENT)throw new Error('Cannot open, already sending');if(void 0!==n&&!n)throw new Error('Synchronous http requests are not supported');if(!s)throw new Error('Cannot load an empty url');this._method=t.toUpperCase(),this._url=s,this._aborted=!1,this.setReadyState(this.OPENED)}},{key:"send",value:function(s){var n=this;if(this.readyState!==this.OPENED)throw new Error('Request has not been opened');if(this._sent)throw new Error('Request has already been sent');this._sent=!0;var o=this._incrementalEvents||!!this.onreadystatechange||!!this.onprogress;this._subscriptions.push(l.addListener('didSendNetworkData',function(s){return n.__didUploadProgress.apply(n,t(s))})),this._subscriptions.push(l.addListener('didReceiveNetworkResponse',function(s){return n.__didReceiveResponse.apply(n,t(s))})),this._subscriptions.push(l.addListener('didReceiveNetworkData',function(s){return n.__didReceiveData.apply(n,t(s))})),this._subscriptions.push(l.addListener('didReceiveNetworkIncrementalData',function(s){return n.__didReceiveIncrementalData.apply(n,t(s))})),this._subscriptions.push(l.addListener('didReceiveNetworkDataProgress',function(s){return n.__didReceiveDataProgress.apply(n,t(s))})),this._subscriptions.push(l.addListener('didCompleteNetworkResponse',function(s){return n.__didCompleteResponse.apply(n,t(s))}));var h='text';'arraybuffer'===this._responseType&&(h='base64'),'blob'===this._responseType&&(h='blob'),v(this._method,'Request method needs to be defined.'),v(this._url,'Request URL needs to be defined.'),l.sendRequest(this._method,this._trackingName,this._url,this._headers,s,h,o,this.timeout,this.__didCreateRequest.bind(this),this.withCredentials)}},{key:"abort",value:function(){this._aborted=!0,this._requestId&&l.abortRequest(this._requestId),this.readyState===this.UNSENT||this.readyState===this.OPENED&&!this._sent||this.readyState===this.DONE||(this._reset(),this.setReadyState(this.DONE)),this._reset()}},{key:"setResponseHeaders",value:function(t){this.responseHeaders=t||null;var s=t||{};this._lowerCaseResponseHeaders=Object.keys(s).reduce(function(t,n){return t[n.toLowerCase()]=s[n],t},{})}},{key:"setReadyState",value:function(t){this.readyState=t,this.dispatchEvent({type:'readystatechange'}),t===this.DONE&&(this._aborted?this.dispatchEvent({type:'abort'}):this._hasError?this._timedOut?this.dispatchEvent({type:'timeout'}):this.dispatchEvent({type:'error'}):this.dispatchEvent({type:'load'}),this.dispatchEvent({type:'loadend'}))}},{key:"addEventListener",value:function(t,n){'readystatechange'!==t&&'progress'!==t||(this._incrementalEvents=!0),s(p(S.prototype),"addEventListener",this).call(this,t,n)}},{key:"responseType",get:function(){return this._responseType},set:function(t){if(this._sent)throw new Error("Failed to set the 'responseType' property on 'XMLHttpRequest': The response type cannot be set after the request has been sent.");w.hasOwnProperty(t)?(v(w[t]||'document'===t,"The provided value '"+t+"' is unsupported in this environment."),'blob'===t&&v(_.isAvailable,'Native module BlobModule is required for blob support'),this._responseType=t):f(!1,"The provided value '"+t+"' is not a valid 'responseType'.")}},{key:"responseText",get:function(){if(''!==this._responseType&&'text'!==this._responseType)throw new Error("The 'responseText' property is only available if 'responseType' is set to '' or 'text', but it is '"+this._responseType+"'.");return this.readyState<k?'':this._response}},{key:"response",get:function(){var t=this.responseType;if(''===t||'text'===t)return this.readyState<k||this._hasError?'':this._response;if(this.readyState!==N)return null;if(void 0!==this._cachedResponse)return this._cachedResponse;switch(t){case'document':this._cachedResponse=null;break;case'arraybuffer':this._cachedResponse=y.toByteArray(this._response).buffer;break;case'blob':if('object'==typeof this._response&&this._response)this._cachedResponse=_.createFromOptions(this._response);else{if(''!==this._response)throw new Error("Invalid response for blob: "+this._response);this._cachedResponse=null}break;case'json':try{this._cachedResponse=JSON.parse(this._response)}catch(t){this._cachedResponse=null}break;default:this._cachedResponse=null}return this._cachedResponse}}]),S})(c.apply(void 0,t(D)));q.UNSENT=R,q.OPENED=E,q.HEADERS_RECEIVED=b,q.LOADING=k,q.DONE=N,q._interceptor=null,m.exports=q},115,[32,47,5,4,6,9,10,116,120,121,126,18,20]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),l=t(r(d[1])),o=t(r(d[2])),u=t(r(d[3])),n=t(r(d[4])),f=t(r(d[5])),c=t(r(d[6])),s=r(d[7]),x=r(d[8]);var b=(function(){function t(){(0,u.default)(this,t)}return(0,n.default)(t,null,[{key:"createFromParts",value:function(l,o){(0,c.default)(f.default,'NativeBlobModule is available.');var u='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(t){var l=16*Math.random()|0;return('x'==t?l:3&l|8).toString(16)}),n=l.map(function(t){if(t instanceof ArrayBuffer||g.ArrayBufferView&&t instanceof g.ArrayBufferView)throw new Error("Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported");return t instanceof s?{data:t.data,type:'blob'}:{data:String(t),type:'string'}}),x=n.reduce(function(t,l){return'string'===l.type?t+g.unescape(encodeURI(l.data)).length:t+l.data.size},0);return f.default.createFromParts(n,u),t.createFromOptions({blobId:u,offset:0,size:x,type:o?o.type:'',lastModified:o?o.lastModified:Date.now()})}},{key:"createFromOptions",value:function(t){return x.register(t.blobId),(0,o.default)(Object.create(s.prototype),{data:null==t.__collector?(0,l.default)({},t,{__collector:(u=t.blobId,null==g.__blobCollectorProvider?null:g.__blobCollectorProvider(u))}):t});var u}},{key:"release",value:function(t){(0,c.default)(f.default,'NativeBlobModule is available.'),x.unregister(t),x.has(t)||f.default.release(t)}},{key:"addNetworkingHandler",value:function(){(0,c.default)(f.default,'NativeBlobModule is available.'),f.default.addNetworkingHandler()}},{key:"addWebSocketHandler",value:function(t){(0,c.default)(f.default,'NativeBlobModule is available.'),f.default.addWebSocketHandler(t)}},{key:"removeWebSocketHandler",value:function(t){(0,c.default)(f.default,'NativeBlobModule is available.'),f.default.removeWebSocketHandler(t)}},{key:"sendOverSocket",value:function(t,l){(0,c.default)(f.default,'NativeBlobModule is available.'),f.default.sendOverSocket(t.data,l)}}]),t})();b.isAvailable=!!f.default,m.exports=b},116,[3,54,16,4,5,117,18,118,119]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('BlobModule');e.default=u},117,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=(function(){function s(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],o=arguments.length>1?arguments[1]:void 0;t(this,s);var u=r(d[2]);this.data=u.createFromParts(n,o).data}return n(s,[{key:"slice",value:function(t,n){var s=r(d[2]),o=this.data,u=o.offset,l=o.size;return'number'==typeof t&&(t>l&&(t=l),u+=t,l-=t,'number'==typeof n&&(n<0&&(n=this.size+n),l=n-t)),s.createFromOptions({blobId:this.data.blobId,offset:u,size:l})}},{key:"close",value:function(){r(d[2]).release(this.data.blobId),this.data=null}},{key:"data",set:function(t){this._data=t},get:function(){if(!this._data)throw new Error('Blob has been closed and is no longer available');return this._data}},{key:"size",get:function(){return this.data.size}},{key:"type",get:function(){return this.data.type||''}}]),s})();m.exports=s},118,[4,5,116]); -__d(function(g,r,i,a,m,e,d){var n={};m.exports={register:function(t){n[t]?n[t]++:n[t]=1},unregister:function(t){n[t]&&(n[t]--,n[t]<=0&&delete n[t])},has:function(t){return n[t]&&n[t]>0}}},119,[]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,'__esModule',{value:!0});var t=new WeakMap,n=new WeakMap;function o(n){var o=t.get(n);return console.assert(null!=o,"'this' is expected an Event object, but got",n),o}function l(t){null==t.passiveListener?t.event.cancelable&&(t.canceled=!0,"function"==typeof t.event.preventDefault&&t.event.preventDefault()):"undefined"!=typeof console&&"function"==typeof console.error&&console.error("Unable to preventDefault inside passive event listener invocation.",t.passiveListener)}function u(n,o){t.set(this,{eventTarget:n,event:o,eventPhase:2,currentTarget:n,canceled:!1,stopped:!1,immediateStopped:!1,passiveListener:null,timeStamp:o.timeStamp||Date.now()}),Object.defineProperty(this,"isTrusted",{value:!1,enumerable:!0});for(var l=Object.keys(o),u=0;u<l.length;++u){var p=l[u];p in this||Object.defineProperty(this,p,s(p))}}function s(t){return{get:function(){return o(this).event[t]},set:function(n){o(this).event[t]=n},configurable:!0,enumerable:!0}}function p(t){return{value:function(){var n=o(this).event;return n[t].apply(n,arguments)},configurable:!0,enumerable:!0}}function c(t,n){var o=Object.keys(n);if(0===o.length)return t;function l(n,o){t.call(this,n,o)}l.prototype=Object.create(t.prototype,{constructor:{value:l,configurable:!0,writable:!0}});for(var u=0;u<o.length;++u){var c=o[u];if(!(c in t.prototype)){var f="function"==typeof Object.getOwnPropertyDescriptor(n,c).value;Object.defineProperty(l.prototype,c,f?p(c):s(c))}}return l}function f(t){if(null==t||t===Object.prototype)return u;var o=n.get(t);return null==o&&(o=c(f(Object.getPrototypeOf(t)),t),n.set(t,o)),o}function v(t,n){return new(f(Object.getPrototypeOf(n)))(t,n)}function y(t){return o(t).immediateStopped}function b(t,n){o(t).eventPhase=n}function h(t,n){o(t).currentTarget=n}function w(t,n){o(t).passiveListener=n}u.prototype={get type(){return o(this).event.type},get target(){return o(this).eventTarget},get currentTarget(){return o(this).currentTarget},composedPath:function(){var t=o(this).currentTarget;return null==t?[]:[t]},get NONE(){return 0},get CAPTURING_PHASE(){return 1},get AT_TARGET(){return 2},get BUBBLING_PHASE(){return 3},get eventPhase(){return o(this).eventPhase},stopPropagation:function(){var t=o(this);t.stopped=!0,"function"==typeof t.event.stopPropagation&&t.event.stopPropagation()},stopImmediatePropagation:function(){var t=o(this);t.stopped=!0,t.immediateStopped=!0,"function"==typeof t.event.stopImmediatePropagation&&t.event.stopImmediatePropagation()},get bubbles(){return Boolean(o(this).event.bubbles)},get cancelable(){return Boolean(o(this).event.cancelable)},preventDefault:function(){l(o(this))},get defaultPrevented(){return o(this).canceled},get composed(){return Boolean(o(this).event.composed)},get timeStamp(){return o(this).timeStamp},get srcElement(){return o(this).eventTarget},get cancelBubble(){return o(this).stopped},set cancelBubble(t){if(t){var n=o(this);n.stopped=!0,"boolean"==typeof n.event.cancelBubble&&(n.event.cancelBubble=!0)}},get returnValue(){return!o(this).canceled},set returnValue(t){t||l(o(this))},initEvent:function(){}},Object.defineProperty(u.prototype,"constructor",{value:u,configurable:!0,writable:!0}),"undefined"!=typeof window&&void 0!==window.Event&&(Object.setPrototypeOf(u.prototype,window.Event.prototype),n.set(window.Event.prototype,u));var T=new WeakMap,P=3;function x(t){return null!==t&&"object"==typeof t}function E(t){var n=T.get(t);if(null==n)throw new TypeError("'this' is expected an EventTarget object, but got another value.");return n}function O(t){return{get:function(){for(var n=E(this).get(t);null!=n;){if(n.listenerType===P)return n.listener;n=n.next}return null},set:function(n){"function"==typeof n||x(n)||(n=null);for(var o=E(this),l=null,u=o.get(t);null!=u;)u.listenerType===P?null!==l?l.next=u.next:null!==u.next?o.set(t,u.next):o.delete(t):l=u,u=u.next;if(null!==n){var s={listener:n,listenerType:P,passive:!1,once:!1,next:null};null===l?o.set(t,s):l.next=s}},configurable:!0,enumerable:!0}}function j(t,n){Object.defineProperty(t,"on"+n,O(n))}function B(t){function n(){A.call(this)}n.prototype=Object.create(A.prototype,{constructor:{value:n,configurable:!0,writable:!0}});for(var o=0;o<t.length;++o)j(n.prototype,t[o]);return n}function A(){if(!(this instanceof A)){if(1===arguments.length&&Array.isArray(arguments[0]))return B(arguments[0]);if(arguments.length>0){for(var t=new Array(arguments.length),n=0;n<arguments.length;++n)t[n]=arguments[n];return B(t)}throw new TypeError("Cannot call a class as a function")}T.set(this,new Map)}A.prototype={addEventListener:function(t,n,o){if(null!=n){if("function"!=typeof n&&!x(n))throw new TypeError("'listener' should be a function or an object.");var l=E(this),u=x(o),s=(u?Boolean(o.capture):Boolean(o))?1:2,p={listener:n,listenerType:s,passive:u&&Boolean(o.passive),once:u&&Boolean(o.once),next:null},c=l.get(t);if(void 0!==c){for(var f=null;null!=c;){if(c.listener===n&&c.listenerType===s)return;f=c,c=c.next}f.next=p}else l.set(t,p)}},removeEventListener:function(t,n,o){if(null!=n)for(var l=E(this),u=(x(o)?Boolean(o.capture):Boolean(o))?1:2,s=null,p=l.get(t);null!=p;){if(p.listener===n&&p.listenerType===u)return void(null!==s?s.next=p.next:null!==p.next?l.set(t,p.next):l.delete(t));s=p,p=p.next}},dispatchEvent:function(t){if(null==t||"string"!=typeof t.type)throw new TypeError('"event.type" should be a string.');var n=E(this),o=t.type,l=n.get(o);if(null==l)return!0;for(var u=v(this,t),s=null;null!=l;){if(l.once?null!==s?s.next=l.next:null!==l.next?n.set(o,l.next):n.delete(o):s=l,w(u,l.passive?l.listener:null),"function"==typeof l.listener)try{l.listener.call(this,u)}catch(t){"undefined"!=typeof console&&"function"==typeof console.error&&console.error(t)}else l.listenerType!==P&&"function"==typeof l.listener.handleEvent&&l.listener.handleEvent(u);if(y(u))break;l=l.next}return w(u,null),b(u,0),h(u,null),!u.defaultPrevented}},Object.defineProperty(A.prototype,"constructor",{value:A,configurable:!0,writable:!0}),"undefined"!=typeof window&&void 0!==window.EventTarget&&Object.setPrototypeOf(A.prototype,window.EventTarget.prototype),e.defineEventAttribute=j,e.EventTarget=A,e.default=A,m.exports=A,m.exports.EventTarget=m.exports.default=A,m.exports.defineEventAttribute=j},120,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),u=t(r(d[1])),n=t(r(d[2])),l=t(r(d[3])),s=t(r(d[4])),f=t(r(d[5])),o=t(r(d[6])),c=t(r(d[7])),h=r(d[8]),k=r(d[9]),v=(function(t){function h(){return(0,n.default)(this,h),(0,s.default)(this,(0,f.default)(h).call(this,c.default))}return(0,o.default)(h,t),(0,l.default)(h,[{key:"sendRequest",value:function(t,n,l,s,f,o,h,v,p,q){var y=k(f);c.default.sendRequest({method:t,url:l,data:(0,u.default)({},y,{trackingName:n}),headers:s,responseType:o,incrementalUpdates:h,timeout:v,withCredentials:q},p)}},{key:"abortRequest",value:function(t){c.default.abortRequest(t)}},{key:"clearCookies",value:function(t){c.default.clearCookies(t)}}]),h})(h);m.exports=new v},121,[3,54,4,5,6,9,10,122,123,124]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('Networking');e.default=n},122,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),l=r(d[3]),o=r(d[4]),u=r(d[5]),v=r(d[6]),h=(r(d[7]),r(d[8])),c=r(d[9]),p=(function(p){function _(n){var o;return t(this,_),o=s(this,l(_).call(this,h.sharedSubscriber)),c(n,'Native module cannot be null.'),o._nativeModule=n,o}return u(_,v),n(_,[{key:"addListener",value:function(t,n,s){return null!=this._nativeModule&&this._nativeModule.addListener(t),o(l(_.prototype),"addListener",this).call(this,t,n,s)}},{key:"removeAllListeners",value:function(t){c(t,'eventType argument is required.');var n=this.listeners(t).length;null!=this._nativeModule&&this._nativeModule.removeListeners(n),o(l(_.prototype),"removeAllListeners",this).call(this,t)}},{key:"removeSubscription",value:function(t){null!=this._nativeModule&&this._nativeModule.removeListeners(1),o(l(_.prototype),"removeSubscription",this).call(this,t)}}]),_})();m.exports=p},123,[4,5,6,9,47,10,49,58,46,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),f=r(d[2]);m.exports=function(s){return'string'==typeof s?{string:s}:s instanceof n?{blob:s.data}:s instanceof f?{formData:s.getParts()}:s instanceof ArrayBuffer||ArrayBuffer.isView(s)?{base64:t(s)}:s}},124,[125,118,127]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=function(f){if(f instanceof ArrayBuffer&&(f=new Uint8Array(f)),f instanceof Uint8Array)return t.fromByteArray(f);if(!ArrayBuffer.isView(f))throw new Error('data must be ArrayBuffer or typed array');var n=f,y=n.buffer,o=n.byteOffset,u=n.byteLength;return t.fromByteArray(new Uint8Array(y,o,u))}},125,[126]); -__d(function(g,r,i,a,m,e,d){'use strict';e.byteLength=function(t){return 3*t.length/4-A(t)},e.toByteArray=function(t){var h,c,u,f,C,y=t.length;f=A(t),C=new o(3*y/4-f),c=f>0?y-4:y;var l=0;for(h=0;h<c;h+=4)u=n[t.charCodeAt(h)]<<18|n[t.charCodeAt(h+1)]<<12|n[t.charCodeAt(h+2)]<<6|n[t.charCodeAt(h+3)],C[l++]=u>>16&255,C[l++]=u>>8&255,C[l++]=255&u;2===f?(u=n[t.charCodeAt(h)]<<2|n[t.charCodeAt(h+1)]>>4,C[l++]=255&u):1===f&&(u=n[t.charCodeAt(h)]<<10|n[t.charCodeAt(h+1)]<<4|n[t.charCodeAt(h+2)]>>2,C[l++]=u>>8&255,C[l++]=255&u);return C},e.fromByteArray=function(n){for(var o,h=n.length,c=h%3,u='',A=[],C=0,y=h-c;C<y;C+=16383)A.push(f(n,C,C+16383>y?y:C+16383));1===c?(o=n[h-1],u+=t[o>>2],u+=t[o<<4&63],u+='=='):2===c&&(o=(n[h-2]<<8)+n[h-1],u+=t[o>>10],u+=t[o>>4&63],u+=t[o<<2&63],u+='=');return A.push(u),A.join('')};for(var t=[],n=[],o='undefined'!=typeof Uint8Array?Uint8Array:Array,h='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',c=0,u=h.length;c<u;++c)t[c]=h[c],n[h.charCodeAt(c)]=c;function A(t){var n=t.length;if(n%4>0)throw new Error('Invalid string. Length must be a multiple of 4');return'='===t[n-2]?2:'='===t[n-1]?1:0}function f(n,o,h){for(var c,u,A=[],f=o;f<h;f+=3)c=(n[f]<<16&16711680)+(n[f+1]<<8&65280)+(255&n[f+2]),A.push(t[(u=c)>>18&63]+t[u>>12&63]+t[u>>6&63]+t[63&u]);return A.join('')}n['-'.charCodeAt(0)]=62,n['_'.charCodeAt(0)]=63},126,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),p=(function(){function p(){s(this,p),this._parts=[]}return o(p,[{key:"append",value:function(t,n){this._parts.push([t,n])}},{key:"getParts",value:function(){return this._parts.map(function(s){var o=n(s,2),p=o[0],f=o[1],u={'content-disposition':'form-data; name="'+p+'"'};return'object'==typeof f&&f?('string'==typeof f.name&&(u['content-disposition']+='; filename="'+f.name+'"'),'string'==typeof f.type&&(u['content-type']=f.type),t({},f,{headers:u,fieldName:p})):{string:String(f),headers:u,fieldName:p}})}}]),p})();m.exports=p},127,[54,26,4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]),m.exports={fetch:fetch,Headers:Headers,Request:Request,Response:Response}},128,[129]); -__d(function(g,r,i,a,m,e,d){var t,o;t=this,o=function(t){'use strict';var o={searchParams:'URLSearchParams'in self,iterable:'Symbol'in self&&'iterator'in Symbol,blob:'FileReader'in self&&'Blob'in self&&(function(){try{return new Blob,!0}catch(t){return!1}})(),formData:'FormData'in self,arrayBuffer:'ArrayBuffer'in self};if(o.arrayBuffer)var n=['[object Int8Array]','[object Uint8Array]','[object Uint8ClampedArray]','[object Int16Array]','[object Uint16Array]','[object Int32Array]','[object Uint32Array]','[object Float32Array]','[object Float64Array]'],s=ArrayBuffer.isView||function(t){return t&&n.indexOf(Object.prototype.toString.call(t))>-1};function f(t){if('string'!=typeof t&&(t=String(t)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(t))throw new TypeError('Invalid character in header field name');return t.toLowerCase()}function h(t){return'string'!=typeof t&&(t=String(t)),t}function u(t){var n={next:function(){var o=t.shift();return{done:void 0===o,value:o}}};return o.iterable&&(n["function"==typeof Symbol?Symbol.iterator:"@@iterator"]=function(){return n}),n}function c(t){this.map={},t instanceof c?t.forEach(function(t,o){this.append(o,t)},this):Array.isArray(t)?t.forEach(function(t){this.append(t[0],t[1])},this):t&&Object.getOwnPropertyNames(t).forEach(function(o){this.append(o,t[o])},this)}function y(t){if(t.bodyUsed)return Promise.reject(new TypeError('Already read'));t.bodyUsed=!0}function l(t){return new Promise(function(o,n){t.onload=function(){o(t.result)},t.onerror=function(){n(t.error)}})}function p(t){var o=new FileReader,n=l(o);return o.readAsArrayBuffer(t),n}function b(t){for(var o=new Uint8Array(t),n=new Array(o.length),s=0;s<o.length;s++)n[s]=String.fromCharCode(o[s]);return n.join('')}function w(t){if(t.slice)return t.slice(0);var o=new Uint8Array(t.byteLength);return o.set(new Uint8Array(t)),o.buffer}function v(){return this.bodyUsed=!1,this._initBody=function(t){var n;this._bodyInit=t,t?'string'==typeof t?this._bodyText=t:o.blob&&Blob.prototype.isPrototypeOf(t)?this._bodyBlob=t:o.formData&&FormData.prototype.isPrototypeOf(t)?this._bodyFormData=t:o.searchParams&&URLSearchParams.prototype.isPrototypeOf(t)?this._bodyText=t.toString():o.arrayBuffer&&o.blob&&((n=t)&&DataView.prototype.isPrototypeOf(n))?(this._bodyArrayBuffer=w(t.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):o.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(t)||s(t))?this._bodyArrayBuffer=w(t):this._bodyText=t=Object.prototype.toString.call(t):this._bodyText='',this.headers.get('content-type')||('string'==typeof t?this.headers.set('content-type','text/plain;charset=UTF-8'):this._bodyBlob&&this._bodyBlob.type?this.headers.set('content-type',this._bodyBlob.type):o.searchParams&&URLSearchParams.prototype.isPrototypeOf(t)&&this.headers.set('content-type','application/x-www-form-urlencoded;charset=UTF-8'))},o.blob&&(this.blob=function(){var t=y(this);if(t)return t;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error('could not read FormData body as blob');return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?y(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(p)}),this.text=function(){var t,o,n,s=y(this);if(s)return s;if(this._bodyBlob)return t=this._bodyBlob,o=new FileReader,n=l(o),o.readAsText(t),n;if(this._bodyArrayBuffer)return Promise.resolve(b(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error('could not read FormData body as text');return Promise.resolve(this._bodyText)},o.formData&&(this.formData=function(){return this.text().then(A)}),this.json=function(){return this.text().then(JSON.parse)},this}c.prototype.append=function(t,o){t=f(t),o=h(o);var n=this.map[t];this.map[t]=n?n+', '+o:o},c.prototype.delete=function(t){delete this.map[f(t)]},c.prototype.get=function(t){return t=f(t),this.has(t)?this.map[t]:null},c.prototype.has=function(t){return this.map.hasOwnProperty(f(t))},c.prototype.set=function(t,o){this.map[f(t)]=h(o)},c.prototype.forEach=function(t,o){for(var n in this.map)this.map.hasOwnProperty(n)&&t.call(o,this.map[n],n,this)},c.prototype.keys=function(){var t=[];return this.forEach(function(o,n){t.push(n)}),u(t)},c.prototype.values=function(){var t=[];return this.forEach(function(o){t.push(o)}),u(t)},c.prototype.entries=function(){var t=[];return this.forEach(function(o,n){t.push([n,o])}),u(t)},o.iterable&&(c.prototype["function"==typeof Symbol?Symbol.iterator:"@@iterator"]=c.prototype.entries);var E=['DELETE','GET','HEAD','OPTIONS','POST','PUT'];function _(t,o){var n,s,f=(o=o||{}).body;if(t instanceof _){if(t.bodyUsed)throw new TypeError('Already read');this.url=t.url,this.credentials=t.credentials,o.headers||(this.headers=new c(t.headers)),this.method=t.method,this.mode=t.mode,this.signal=t.signal,f||null==t._bodyInit||(f=t._bodyInit,t.bodyUsed=!0)}else this.url=String(t);if(this.credentials=o.credentials||this.credentials||'same-origin',!o.headers&&this.headers||(this.headers=new c(o.headers)),this.method=(n=o.method||this.method||'GET',s=n.toUpperCase(),E.indexOf(s)>-1?s:n),this.mode=o.mode||this.mode||null,this.signal=o.signal||this.signal,this.referrer=null,('GET'===this.method||'HEAD'===this.method)&&f)throw new TypeError('Body not allowed for GET or HEAD requests');this._initBody(f)}function A(t){var o=new FormData;return t.trim().split('&').forEach(function(t){if(t){var n=t.split('='),s=n.shift().replace(/\+/g,' '),f=n.join('=').replace(/\+/g,' ');o.append(decodeURIComponent(s),decodeURIComponent(f))}}),o}function x(t,o){o||(o={}),this.type='default',this.status=void 0===o.status?200:o.status,this.ok=this.status>=200&&this.status<300,this.statusText='statusText'in o?o.statusText:'OK',this.headers=new c(o.headers),this.url=o.url||'',this._initBody(t)}_.prototype.clone=function(){return new _(this,{body:this._bodyInit})},v.call(_.prototype),v.call(x.prototype),x.prototype.clone=function(){return new x(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new c(this.headers),url:this.url})},x.error=function(){var t=new x(null,{status:0,statusText:''});return t.type='error',t};var B=[301,302,303,307,308];x.redirect=function(t,o){if(-1===B.indexOf(o))throw new RangeError('Invalid status code');return new x(null,{status:o,headers:{location:t}})},t.DOMException=self.DOMException;try{new t.DOMException}catch(o){t.DOMException=function(t,o){this.message=t,this.name=o;var n=Error(t);this.stack=n.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function T(n,s){return new Promise(function(f,h){var u=new _(n,s);if(u.signal&&u.signal.aborted)return h(new t.DOMException('Aborted','AbortError'));var y=new XMLHttpRequest;function l(){y.abort()}y.onload=function(){var t,o,n={status:y.status,statusText:y.statusText,headers:(t=y.getAllResponseHeaders()||'',o=new c,t.replace(/\r?\n[\t ]+/g,' ').split(/\r?\n/).forEach(function(t){var n=t.split(':'),s=n.shift().trim();if(s){var f=n.join(':').trim();o.append(s,f)}}),o)};n.url='responseURL'in y?y.responseURL:n.headers.get('X-Request-URL');var s='response'in y?y.response:y.responseText;f(new x(s,n))},y.onerror=function(){h(new TypeError('Network request failed'))},y.ontimeout=function(){h(new TypeError('Network request failed'))},y.onabort=function(){h(new t.DOMException('Aborted','AbortError'))},y.open(u.method,u.url,!0),'include'===u.credentials?y.withCredentials=!0:'omit'===u.credentials&&(y.withCredentials=!1),'responseType'in y&&o.blob&&(y.responseType='blob'),u.headers.forEach(function(t,o){y.setRequestHeader(o,t)}),u.signal&&(u.signal.addEventListener('abort',l),y.onreadystatechange=function(){4===y.readyState&&u.signal.removeEventListener('abort',l)}),y.send(void 0===u._bodyInit?null:u._bodyInit)})}T.polyfill=!0,self.fetch||(self.fetch=T,self.Headers=c,self.Request=_,self.Response=x),t.Headers=c,t.Request=_,t.Response=x,t.fetch=T,Object.defineProperty(t,'__esModule',{value:!0})},'object'==typeof e&&void 0!==m?o(e):'function'==typeof define&&define.amd?define(['exports'],o):o(t.WHATWGFetch={})},129,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=t(r(d[1])),o=t(r(d[2])),n=t(r(d[3])),c=t(r(d[4])),u=t(r(d[5])),l=t(r(d[6])),f=t(r(d[7])),h=r(d[8]),b=r(d[9]),y=r(d[10]),p=r(d[11]),v=(r(d[12]),r(d[13])),_=r(d[14]),E=r(d[15]),k=r(d[16]),I=0,S=1,N=2,w=3,O=0,C=(function(t){function y(t,n,l){var h;(0,o.default)(this,y),(h=(0,c.default)(this,(0,u.default)(y).call(this))).CONNECTING=I,h.OPEN=S,h.CLOSING=N,h.CLOSED=w,h.readyState=I,'string'==typeof n&&(n=[n]);var b=l||{},v=b.headers,_=void 0===v?{}:v,E=(0,s.default)(b,["headers"]);return E&&'string'==typeof E.origin&&(console.warn('Specifying `origin` as a WebSocket connection option is deprecated. Include it under `headers` instead.'),_.origin=E.origin,delete E.origin),Object.keys(E).length>0&&console.warn('Unrecognized WebSocket connection option(s) `'+Object.keys(E).join('`, `')+"`. Did you mean to put these under `headers`?"),Array.isArray(n)||(n=null),h._eventEmitter=new p(f.default),h._socketId=O++,h._registerEvents(),f.default.connect(t,n,{headers:_},h._socketId),h}return(0,l.default)(y,t),(0,n.default)(y,[{key:"close",value:function(t,s){this.readyState!==this.CLOSING&&this.readyState!==this.CLOSED&&(this.readyState=this.CLOSING,this._close(t,s))}},{key:"send",value:function(t){if(this.readyState===this.CONNECTING)throw new Error('INVALID_STATE_ERR');if(t instanceof h)return k(b.isAvailable,'Native module BlobModule is required for blob support'),void b.sendOverSocket(t,this._socketId);if('string'!=typeof t){if(!(t instanceof ArrayBuffer||ArrayBuffer.isView(t)))throw new Error('Unsupported data type');f.default.sendBinary(E(t),this._socketId)}else f.default.send(t,this._socketId)}},{key:"ping",value:function(){if(this.readyState===this.CONNECTING)throw new Error('INVALID_STATE_ERR');f.default.ping(this._socketId)}},{key:"_close",value:function(t,s){var o='number'==typeof t?t:1e3,n='string'==typeof s?s:'';f.default.close(o,n,this._socketId),b.isAvailable&&'blob'===this._binaryType&&b.removeWebSocketHandler(this._socketId)}},{key:"_unregisterEvents",value:function(){this._subscriptions.forEach(function(t){return t.remove()}),this._subscriptions=[]}},{key:"_registerEvents",value:function(){var t=this;this._subscriptions=[this._eventEmitter.addListener('websocketMessage',function(s){if(s.id===t._socketId){var o=s.data;switch(s.type){case'binary':o=_.toByteArray(s.data).buffer;break;case'blob':o=b.createFromOptions(s.data)}t.dispatchEvent(new v('message',{data:o}))}}),this._eventEmitter.addListener('websocketOpen',function(s){s.id===t._socketId&&(t.readyState=t.OPEN,t.protocol=s.protocol,t.dispatchEvent(new v('open')))}),this._eventEmitter.addListener('websocketClosed',function(s){s.id===t._socketId&&(t.readyState=t.CLOSED,t.dispatchEvent(new v('close',{code:s.code,reason:s.reason})),t._unregisterEvents(),t.close())}),this._eventEmitter.addListener('websocketFailed',function(s){s.id===t._socketId&&(t.readyState=t.CLOSED,t.dispatchEvent(new v('error',{message:s.message})),t.dispatchEvent(new v('close',{message:s.message})),t._unregisterEvents(),t.close())})]}},{key:"binaryType",get:function(){return this._binaryType},set:function(t){if('blob'!==t&&'arraybuffer'!==t)throw new Error("binaryType must be either 'blob' or 'arraybuffer'");'blob'!==this._binaryType&&'blob'!==t||(k(b.isAvailable,'Native module BlobModule is required for blob support'),'blob'===t?b.addWebSocketHandler(this._socketId):b.removeWebSocketHandler(this._socketId)),this._binaryType=t}}]),y})(y.apply(void 0,['close','error','message','open']));C.CONNECTING=I,C.OPEN=S,C.CLOSING=N,C.CLOSED=w,m.exports=C},130,[3,56,4,5,6,9,10,131,118,116,120,123,58,132,126,125,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).getEnforcing('WebSocketModule');e.default=o},131,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]);m.exports=function n(o,c){s(this,n),this.type=o.toString(),t(this,c)}},132,[16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),u=r(d[3]),l=r(d[4]),o=r(d[5]),c=r(d[6]),h=(function(h){function f(n,l,o){var h;return t(this,f),c(null!=n&&null!=l,'Failed to construct `File`: Must pass both `parts` and `name` arguments.'),(h=s(this,u(f).call(this,n,o))).data.name=l,h}return l(f,o),n(f,[{key:"name",get:function(){return c(null!=this.data.name,'Files must have a name set.'),this.data.name}},{key:"lastModified",get:function(){return this.data.lastModified||0}}]),f})();m.exports=h},133,[4,5,6,9,10,118,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=t(r(d[1])),n=t(r(d[2])),o=t(r(d[3])),u=t(r(d[4])),h=t(r(d[5])),_=t(r(d[6])),l=(r(d[7]),0),c=1,y=2,f=(function(t){function f(){var t;return(0,s.default)(this,f),(t=(0,o.default)(this,(0,u.default)(f).call(this))).EMPTY=l,t.LOADING=c,t.DONE=y,t._aborted=!1,t._subscriptions=[],t._reset(),t}return(0,h.default)(f,t),(0,n.default)(f,[{key:"_reset",value:function(){this._readyState=l,this._error=null,this._result=null}},{key:"_clearSubscriptions",value:function(){this._subscriptions.forEach(function(t){return t.remove()}),this._subscriptions=[]}},{key:"_setReadyState",value:function(t){this._readyState=t,this.dispatchEvent({type:'readystatechange'}),t===y&&(this._aborted?this.dispatchEvent({type:'abort'}):this._error?this.dispatchEvent({type:'error'}):this.dispatchEvent({type:'load'}),this.dispatchEvent({type:'loadend'}))}},{key:"readAsArrayBuffer",value:function(){throw new Error('FileReader.readAsArrayBuffer is not implemented')}},{key:"readAsDataURL",value:function(t){var s=this;this._aborted=!1,_.default.readAsDataURL(t.data).then(function(t){s._aborted||(s._result=t,s._setReadyState(y))},function(t){s._aborted||(s._error=t,s._setReadyState(y))})}},{key:"readAsText",value:function(t){var s=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:'UTF-8';this._aborted=!1,_.default.readAsText(t.data,n).then(function(t){s._aborted||(s._result=t,s._setReadyState(y))},function(t){s._aborted||(s._error=t,s._setReadyState(y))})}},{key:"abort",value:function(){this._aborted=!0,this._readyState!==l&&this._readyState!==y&&(this._reset(),this._setReadyState(y)),this._reset()}},{key:"readyState",get:function(){return this._readyState}},{key:"error",get:function(){return this._error}},{key:"result",get:function(){return this._result}}]),f})(r(d[8]).apply(void 0,['abort','error','load','loadstart','loadend','progress']));f.EMPTY=l,f.LOADING=c,f.DONE=y,m.exports=f},134,[3,4,5,6,9,10,135,118,120]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).getEnforcing('FileReaderModule');e.default=u},135,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.URL=e.URLSearchParams=void 0;var n=t(r(d[1])),o=t(r(d[2])),u=t(r(d[3])),s=(r(d[4]),null);if(u.default&&'string'==typeof u.default.getConstants().BLOB_URI_SCHEME){var f=u.default.getConstants();s=f.BLOB_URI_SCHEME+':','string'==typeof f.BLOB_URI_HOST&&(s+="//"+f.BLOB_URI_HOST+"/")}var l="function"==typeof Symbol?Symbol.iterator:"@@iterator",h=(function(){function t(o){var u=this;(0,n.default)(this,t),this._searchParams=[],'object'==typeof o&&Object.keys(o).forEach(function(t){return u.append(t,o[t])})}return(0,o.default)(t,[{key:"append",value:function(t,n){this._searchParams.push([t,n])}},{key:"delete",value:function(t){throw new Error('not implemented')}},{key:"get",value:function(t){throw new Error('not implemented')}},{key:"getAll",value:function(t){throw new Error('not implemented')}},{key:"has",value:function(t){throw new Error('not implemented')}},{key:"set",value:function(t,n){throw new Error('not implemented')}},{key:"sort",value:function(){throw new Error('not implemented')}},{key:l,value:function(){return this._searchParams["function"==typeof Symbol?Symbol.iterator:"@@iterator"]()}},{key:"toString",value:function(){if(0===this._searchParams.length)return'';var t=this._searchParams.length-1;return this._searchParams.reduce(function(n,o,u){return n+o.join('=')+(u===t?'':'&')},'')}}]),t})();function c(t){return/^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(t)}e.URLSearchParams=h;var y=(function(){function t(o,u){(0,n.default)(this,t),this._searchParamsInstance=null;var s=null;if(!u||c(o))this._url=o,this._url.endsWith('/')||(this._url+='/');else{if('string'==typeof u){if(!c(s=u))throw new TypeError("Invalid base URL: "+s)}else'object'==typeof u&&(s=u.toString());s.endsWith('/')&&o.startsWith('/')&&(s=s.slice(0,s.length-1)),s.endsWith(o)&&(o=''),this._url=""+s+o}}return(0,o.default)(t,null,[{key:"createObjectURL",value:function(t){if(null===s)throw new Error('Cannot create URL for blob!');return""+s+t.data.blobId+"?offset="+t.data.offset+"&size="+t.size}},{key:"revokeObjectURL",value:function(t){}}]),(0,o.default)(t,[{key:"toJSON",value:function(){return this.toString()}},{key:"toString",value:function(){if(null===this._searchParamsInstance)return this._url;var t=this._url.indexOf('?')>-1?'&':'?';return this._url+t+this._searchParamsInstance.toString()}},{key:"hash",get:function(){throw new Error('not implemented')}},{key:"host",get:function(){throw new Error('not implemented')}},{key:"hostname",get:function(){throw new Error('not implemented')}},{key:"href",get:function(){return this.toString()}},{key:"origin",get:function(){throw new Error('not implemented')}},{key:"password",get:function(){throw new Error('not implemented')}},{key:"pathname",get:function(){throw new Error('not implemented')}},{key:"port",get:function(){throw new Error('not implemented')}},{key:"protocol",get:function(){throw new Error('not implemented')}},{key:"search",get:function(){throw new Error('not implemented')}},{key:"searchParams",get:function(){return null==this._searchParamsInstance&&(this._searchParamsInstance=new h),this._searchParamsInstance}},{key:"username",get:function(){throw new Error('not implemented')}}]),t})();e.URL=y},136,[3,4,5,117,118]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]),n=r(d[2]),l=r(d[3]),b=r(d[4]);Object.defineProperty(e,'__esModule',{value:!0});var u=r(d[5]),p=(function(u){function p(){throw t(this,p),n(this,l(p).call(this)),new TypeError("AbortSignal cannot be constructed directly")}return b(p,u),o(p,[{key:"aborted",get:function(){var t=y.get(this);if("boolean"!=typeof t)throw new TypeError("Expected 'this' to be an 'AbortSignal' object, but got "+(null===this?"null":typeof this));return t}}]),p})(u.EventTarget);u.defineEventAttribute(p.prototype,"abort");var y=new WeakMap;Object.defineProperties(p.prototype,{aborted:{enumerable:!0}}),"function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.toStringTag:"@@toStringTag")&&Object.defineProperty(p.prototype,"function"==typeof Symbol?Symbol.toStringTag:"@@toStringTag",{configurable:!0,value:"AbortSignal"});var f=(function(){function n(){var o;t(this,n),c.set(this,(o=Object.create(p.prototype),u.EventTarget.call(o),y.set(o,!1),o))}return o(n,[{key:"abort",value:function(){var t;t=s(this),!1===y.get(t)&&(y.set(t,!0),t.dispatchEvent({type:"abort"}))}},{key:"signal",get:function(){return s(this)}}]),n})(),c=new WeakMap;function s(t){var o=c.get(t);if(null==o)throw new TypeError("Expected 'this' to be an 'AbortController' object, but got "+(null===t?"null":typeof t));return o}Object.defineProperties(f.prototype,{signal:{enumerable:!0},abort:{enumerable:!0}}),"function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.toStringTag:"@@toStringTag")&&Object.defineProperty(f.prototype,"function"==typeof Symbol?Symbol.toStringTag:"@@toStringTag",{configurable:!0,value:"AbortController"}),e.AbortController=f,e.AbortSignal=p,e.default=f,m.exports=f,m.exports.AbortController=m.exports.default=f,m.exports.AbortSignal=p},137,[4,5,6,9,10,138]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,'__esModule',{value:!0});var t=new WeakMap,n=new WeakMap;function o(n){var o=t.get(n);return console.assert(null!=o,"'this' is expected an Event object, but got",n),o}function l(t){null==t.passiveListener?t.event.cancelable&&(t.canceled=!0,"function"==typeof t.event.preventDefault&&t.event.preventDefault()):"undefined"!=typeof console&&"function"==typeof console.error&&console.error("Unable to preventDefault inside passive event listener invocation.",t.passiveListener)}function u(n,o){t.set(this,{eventTarget:n,event:o,eventPhase:2,currentTarget:n,canceled:!1,stopped:!1,immediateStopped:!1,passiveListener:null,timeStamp:o.timeStamp||Date.now()}),Object.defineProperty(this,"isTrusted",{value:!1,enumerable:!0});for(var l=Object.keys(o),u=0;u<l.length;++u){var p=l[u];p in this||Object.defineProperty(this,p,s(p))}}function s(t){return{get:function(){return o(this).event[t]},set:function(n){o(this).event[t]=n},configurable:!0,enumerable:!0}}function p(t){return{value:function(){var n=o(this).event;return n[t].apply(n,arguments)},configurable:!0,enumerable:!0}}function c(t,n){var o=Object.keys(n);if(0===o.length)return t;function l(n,o){t.call(this,n,o)}l.prototype=Object.create(t.prototype,{constructor:{value:l,configurable:!0,writable:!0}});for(var u=0;u<o.length;++u){var c=o[u];if(!(c in t.prototype)){var f="function"==typeof Object.getOwnPropertyDescriptor(n,c).value;Object.defineProperty(l.prototype,c,f?p(c):s(c))}}return l}function f(t){if(null==t||t===Object.prototype)return u;var o=n.get(t);return null==o&&(o=c(f(Object.getPrototypeOf(t)),t),n.set(t,o)),o}function v(t,n){return new(f(Object.getPrototypeOf(n)))(t,n)}function y(t){return o(t).immediateStopped}function b(t,n){o(t).eventPhase=n}function h(t,n){o(t).currentTarget=n}function w(t,n){o(t).passiveListener=n}u.prototype={get type(){return o(this).event.type},get target(){return o(this).eventTarget},get currentTarget(){return o(this).currentTarget},composedPath:function(){var t=o(this).currentTarget;return null==t?[]:[t]},get NONE(){return 0},get CAPTURING_PHASE(){return 1},get AT_TARGET(){return 2},get BUBBLING_PHASE(){return 3},get eventPhase(){return o(this).eventPhase},stopPropagation:function(){var t=o(this);t.stopped=!0,"function"==typeof t.event.stopPropagation&&t.event.stopPropagation()},stopImmediatePropagation:function(){var t=o(this);t.stopped=!0,t.immediateStopped=!0,"function"==typeof t.event.stopImmediatePropagation&&t.event.stopImmediatePropagation()},get bubbles(){return Boolean(o(this).event.bubbles)},get cancelable(){return Boolean(o(this).event.cancelable)},preventDefault:function(){l(o(this))},get defaultPrevented(){return o(this).canceled},get composed(){return Boolean(o(this).event.composed)},get timeStamp(){return o(this).timeStamp},get srcElement(){return o(this).eventTarget},get cancelBubble(){return o(this).stopped},set cancelBubble(t){if(t){var n=o(this);n.stopped=!0,"boolean"==typeof n.event.cancelBubble&&(n.event.cancelBubble=!0)}},get returnValue(){return!o(this).canceled},set returnValue(t){t||l(o(this))},initEvent:function(){}},Object.defineProperty(u.prototype,"constructor",{value:u,configurable:!0,writable:!0}),"undefined"!=typeof window&&void 0!==window.Event&&(Object.setPrototypeOf(u.prototype,window.Event.prototype),n.set(window.Event.prototype,u));var T=new WeakMap,P=3;function x(t){return null!==t&&"object"==typeof t}function E(t){var n=T.get(t);if(null==n)throw new TypeError("'this' is expected an EventTarget object, but got another value.");return n}function O(t){return{get:function(){for(var n=E(this).get(t);null!=n;){if(n.listenerType===P)return n.listener;n=n.next}return null},set:function(n){"function"==typeof n||x(n)||(n=null);for(var o=E(this),l=null,u=o.get(t);null!=u;)u.listenerType===P?null!==l?l.next=u.next:null!==u.next?o.set(t,u.next):o.delete(t):l=u,u=u.next;if(null!==n){var s={listener:n,listenerType:P,passive:!1,once:!1,next:null};null===l?o.set(t,s):l.next=s}},configurable:!0,enumerable:!0}}function j(t,n){Object.defineProperty(t,"on"+n,O(n))}function B(t){function n(){A.call(this)}n.prototype=Object.create(A.prototype,{constructor:{value:n,configurable:!0,writable:!0}});for(var o=0;o<t.length;++o)j(n.prototype,t[o]);return n}function A(){if(!(this instanceof A)){if(1===arguments.length&&Array.isArray(arguments[0]))return B(arguments[0]);if(arguments.length>0){for(var t=new Array(arguments.length),n=0;n<arguments.length;++n)t[n]=arguments[n];return B(t)}throw new TypeError("Cannot call a class as a function")}T.set(this,new Map)}A.prototype={addEventListener:function(t,n,o){if(null!=n){if("function"!=typeof n&&!x(n))throw new TypeError("'listener' should be a function or an object.");var l=E(this),u=x(o),s=(u?Boolean(o.capture):Boolean(o))?1:2,p={listener:n,listenerType:s,passive:u&&Boolean(o.passive),once:u&&Boolean(o.once),next:null},c=l.get(t);if(void 0!==c){for(var f=null;null!=c;){if(c.listener===n&&c.listenerType===s)return;f=c,c=c.next}f.next=p}else l.set(t,p)}},removeEventListener:function(t,n,o){if(null!=n)for(var l=E(this),u=(x(o)?Boolean(o.capture):Boolean(o))?1:2,s=null,p=l.get(t);null!=p;){if(p.listener===n&&p.listenerType===u)return void(null!==s?s.next=p.next:null!==p.next?l.set(t,p.next):l.delete(t));s=p,p=p.next}},dispatchEvent:function(t){if(null==t||"string"!=typeof t.type)throw new TypeError('"event.type" should be a string.');var n=E(this),o=t.type,l=n.get(o);if(null==l)return!0;for(var u=v(this,t),s=null;null!=l;){if(l.once?null!==s?s.next=l.next:null!==l.next?n.set(o,l.next):n.delete(o):s=l,w(u,l.passive?l.listener:null),"function"==typeof l.listener)try{l.listener.call(this,u)}catch(t){"undefined"!=typeof console&&"function"==typeof console.error&&console.error(t)}else l.listenerType!==P&&"function"==typeof l.listener.handleEvent&&l.listener.handleEvent(u);if(y(u))break;l=l.next}return w(u,null),b(u,0),h(u,null),!u.defaultPrevented}},Object.defineProperty(A.prototype,"constructor",{value:A,configurable:!0,writable:!0}),"undefined"!=typeof window&&void 0!==window.EventTarget&&Object.setPrototypeOf(A.prototype,window.EventTarget.prototype),e.defineEventAttribute=j,e.EventTarget=A,e.default=A,m.exports=A,m.exports.EventTarget=m.exports.default=A,m.exports.defineEventAttribute=j},138,[]); -__d(function(g,r,i,a,m,e,d){'use strict';g.alert||(g.alert=function(t){r(d[0]).alert('Alert',''+t)})},139,[140]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),o=t(r(d[2])),l=t(r(d[3])),s=t(r(d[4])),u=t(r(d[5])),c=(function(){function t(){(0,n.default)(this,t)}return(0,o.default)(t,null,[{key:"alert",value:function(n,o,u,c){if('ios'===l.default.OS)t.prompt(n,o,u,'default');else if('android'===l.default.OS){if(!s.default)return;var f=s.default.getConstants(),p={title:n||'',message:o||'',cancelable:!1};c&&c.cancelable&&(p.cancelable=c.cancelable);var v=u?u.slice(0,3):[{text:"OK"}],y=v.pop(),b=v.pop(),h=v.pop();h&&(p.buttonNeutral=h.text||''),b&&(p.buttonNegative=b.text||''),y&&(p.buttonPositive=y.text||"OK");s.default.showAlert(p,function(t){return console.warn(t)},function(t,n){t===f.buttonClicked?n===f.buttonNeutral?h.onPress&&h.onPress():n===f.buttonNegative?b.onPress&&b.onPress():n===f.buttonPositive&&y.onPress&&y.onPress():t===f.dismissed&&c&&c.onDismiss&&c.onDismiss()})}}},{key:"prompt",value:function(t,n,o){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:'plain-text',c=arguments.length>4?arguments[4]:void 0,f=arguments.length>5?arguments[5]:void 0;if('ios'===l.default.OS){if('function'==typeof s){console.warn("You passed a callback function as the \"type\" argument to Alert.prompt(). React Native is assuming you want to use the deprecated Alert.prompt(title, defaultValue, buttons, callback) signature. The current signature is Alert.prompt(title, message, callbackOrButtons, type, defaultValue, keyboardType) and the old syntax will be removed in a future version.");var p=s;return void u.default.alertWithArgs({title:t||'',type:'plain-text',defaultValue:n||''},function(t,n){p(n)})}var v,y,b=[],h=[];'function'==typeof o?b=[o]:Array.isArray(o)&&o.forEach(function(t,n){if(b[n]=t.onPress,'cancel'===t.style?v=String(n):'destructive'===t.style&&(y=String(n)),t.text||n<(o||[]).length-1){var l={};l[n]=t.text||'',h.push(l)}}),u.default.alertWithArgs({title:t||'',message:n||void 0,buttons:h,type:s||void 0,defaultValue:c,cancelButtonKey:v,destructiveButtonKey:y,keyboardType:f},function(t,n){var o=b[t];o&&o(n)})}}}]),t})();m.exports=c},140,[3,4,5,58,141,142]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).get('DialogManagerAndroid');e.default=o},141,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1]));m.exports={alertWithArgs:function(l,u){null!=t.default&&t.default.alertWithArgs(l,u)}}},142,[3,143]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('AlertManager');e.default=u},143,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]).polyfillObjectProperty,o=g.navigator;void 0===o&&(g.navigator=o={}),t(o,'product',function(){return'ReactNative'})},144,[105]); -__d(function(g,r,i,a,m,e,d){'use strict';if(!g.RN$Bridgeless){var l=r(d[0]);l.registerLazyCallableModule('Systrace',function(){return r(d[1])}),l.registerLazyCallableModule('JSTimers',function(){return r(d[2])}),l.registerLazyCallableModule('HeapCapture',function(){return r(d[3])}),l.registerLazyCallableModule('SamplingProfiler',function(){return r(d[4])}),l.registerLazyCallableModule('RCTLog',function(){return r(d[5])}),l.registerLazyCallableModule('RCTDeviceEventEmitter',function(){return r(d[6])}),l.registerLazyCallableModule('RCTNativeAppEventEmitter',function(){return r(d[7])}),l.registerLazyCallableModule('GlobalPerformanceLogger',function(){return r(d[8])}),l.registerLazyCallableModule('JSDevSupportModule',function(){return r(d[9])}),l.registerCallableModule('HMRClient',r(d[10]))}},145,[30,37,109,146,148,150,46,151,152,155,157]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),p={captureHeap:function(p){var u=null;try{g.nativeCaptureHeap(p),console.log('HeapCapture.captureHeap succeeded: '+p)}catch(t){console.log('HeapCapture.captureHeap error: '+t.toString()),u=t.toString()}t.default&&t.default.captureComplete(p,u)}};m.exports=p},146,[3,147]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('HeapCapture');e.default=u},147,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var o={poke:function(o){var l=null,n=null;try{null===(n=g.pokeSamplingProfiler())?console.log('The JSC Sampling Profiler has started'):console.log('The JSC Sampling Profiler has stopped')}catch(o){console.log('Error occurred when restarting Sampling Profiler: '+o.toString()),l=o.toString()}var t=r(d[0]).default;t&&t.operationComplete(o,n,l)}};m.exports=o},148,[149]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=t(r(d[1])).get('JSCSamplingProfiler');e.default=l},149,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),n={log:'log',info:'info',warn:'warn',error:'error',fatal:'error'},l=null,t={logIfNoNativeHook:function(o){for(var n=arguments.length,f=new Array(n>1?n-1:0),c=1;c<n;c++)f[c-1]=arguments[c];void 0===g.nativeLoggingHook?t.logToConsole.apply(t,[o].concat(f)):l&&'warn'===o&&l.apply(void 0,f)},logToConsole:function(l){var t,f=n[l];o(f,'Level "'+l+'" not one of '+Object.keys(n).toString());for(var c=arguments.length,v=new Array(c>1?c-1:0),s=1;s<c;s++)v[s-1]=arguments[s];(t=console)[f].apply(t,v)},setWarningHandler:function(o){l=o}};m.exports=t},150,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t},151,[46]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])();m.exports=t},152,[153]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=(r(d[1]),g.nativeQPLTimestamp||g.nativePerformanceNow||r(d[2])),n={};m.exports=function(){return{_timespans:{},_extras:{},_points:{},addTimespan:function(t,s,n){this._timespans[t]||(this._timespans[t]={description:n,totalTime:s})},startTimespan:function(o,p){this._timespans[o]||(this._timespans[o]={description:p,startTime:s()},n[o]=t.beginAsyncEvent(o))},stopTimespan:function(o){var p=this._timespans[o];p&&p.startTime&&(p.endTime||(p.endTime=s(),p.totalTime=p.endTime-(p.startTime||0),t.endAsyncEvent(o,n[o]),delete n[o]))},clear:function(){this._timespans={},this._extras={},this._points={}},clearCompleted:function(){for(var t in this._timespans)this._timespans[t].totalTime&&delete this._timespans[t];this._extras={},this._points={}},clearExceptTimespans:function(t){this._timespans=Object.keys(this._timespans).reduce(function(s,n){return-1!==t.indexOf(n)&&(s[n]=this._timespans[n]),s},{}),this._extras={},this._points={}},currentTimestamp:function(){return s()},getTimespans:function(){return this._timespans},hasTimespan:function(t){return!!this._timespans[t]},logTimespans:function(){},addTimespans:function(t,s){for(var n=0,o=t.length;n<o;n+=2){var p=s[n/2];this.addTimespan(p,t[n+1]-t[n],p)}},setExtra:function(t,s){this._extras[t]||(this._extras[t]=s)},getExtras:function(){return this._extras},removeExtra:function(t){var s=this._extras[t];return delete this._extras[t],s},logExtras:function(){},markPoint:function(t,n){var o;this._points[t]||(this._points[t]=null!=(o=n)?o:s())},getPoints:function(){return this._points},logPoints:function(){},logEverything:function(){this.logTimespans(),this.logExtras(),this.logPoints()}}}},153,[37,154,111]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(){var n;return(n=console).log.apply(n,arguments)}},154,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),_=r(d[2]),o={getJSHierarchy:function(o){if(t.default){var n=t.default.getConstants();try{var E=(0,_.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.computeComponentStackForErrorReporting)(o);E?t.default.onSuccess(E):t.default.onFailure(n.ERROR_CODE_VIEW_NOT_FOUND,"Component stack doesn't exist for tag "+o)}catch(_){t.default.onFailure(n.ERROR_CODE_EXCEPTION,_.message)}}}};m.exports=o},155,[3,156,90]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('JSDevSupport');e.default=u},156,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n={setup:function(){},enable:function(){console.error("Fast Refresh is disabled in JavaScript bundles built in production mode. Did you forget to run Metro?")},disable:function(){},registerBundle:function(){},log:function(){}};m.exports=n},157,[]); -__d(function(g,r,i,a,m,e,d){'use strict';g.__fetchSegment=function(t,n,c){r(d[0]).default.fetchSegment(t,n,function(t){if(t){var n=new Error(t.message);n.code=t.code,c(n)}c(null)})},g.__getSegment=function(t,n,c){var f=r(d[0]).default;if(!f.getSegment)throw new Error('SegmentFetcher.getSegment must be defined');f.getSegment(t,n,function(t,n){if(t){var f=new Error(t.message);f.code=t.code,c(f)}c(null,n)})}},158,[159]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('SegmentFetcher');e.default=n},159,[2,24]); -__d(function(g,r,i,a,m,e,d){m.exports={get BatchedBridge(){return r(d[0])},get ExceptionsManager(){return r(d[1])},get Platform(){return r(d[2])},get RCTEventEmitter(){return r(d[3])},get ReactNativeViewConfigRegistry(){return r(d[4])},get TextInputState(){return r(d[5])},get UIManager(){return r(d[6])},get deepDiffer(){return r(d[7])},get deepFreezeAndThrowOnMutationInDev(){return r(d[8])},get flattenStyle(){return r(d[9])},get ReactFiberErrorDialog(){return r(d[10])}}},160,[30,97,58,161,162,163,75,164,38,87,165]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n={register:function(n){t.registerCallableModule('RCTEventEmitter',n)}};m.exports=n},161,[30]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n={},s={};e.customBubblingEventTypes=n,e.customDirectEventTypes=s;var o=new Map,u=new Map;function f(t){var o=t.bubblingEventTypes,u=t.directEventTypes;if(null!=o)for(var f in o)null==n[f]&&(n[f]=o[f]);if(null!=u)for(var l in u)null==s[l]&&(s[l]=u[l])}e.register=function(n,s){return t(!o.has(n),'Tried to register two views with the same name %s',n),o.set(n,s),n},e.get=function(n){var s;if(u.has(n))s=u.get(n);else{var l=o.get(n);'function'!=typeof l&&t(!1,'View config not found for name %s.%s',n,'string'==typeof n[0]&&/[a-z]/.test(n[0])?' Make sure to start component names with a capital letter.':''),o.set(n,null),f(s=l()),u.set(n,s)}return t(s,'View config not found for name %s',n),s}},162,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);var n=r(d[1]),u=null,t=new Set;m.exports={currentlyFocusedField:function(){return u},focusTextInput:function(t){u!==t&&null!==t&&(u=t,n.focus(t))},blurTextInput:function(t){u===t&&null!==t&&(u=null,n.blur(t))},registerInput:function(n){t.add(n)},unregisterInput:function(n){t.delete(n)},isTextInput:function(n){return t.has(n)}}},163,[58,75]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function t(n,f){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-1;if(0===o)return!0;if(n===f)return!1;if('function'==typeof n&&'function'==typeof f)return!1;if('object'!=typeof n||null===n)return n!==f;if('object'!=typeof f||null===f)return!0;if(n.constructor!==f.constructor)return!0;if(Array.isArray(n)){var u=n.length;if(f.length!==u)return!0;for(var c=0;c<u;c++)if(t(n[c],f[c],o-1))return!0}else{for(var l in n)if(t(n[l],f[l],o-1))return!0;for(var v in f)if(void 0===n[v]&&void 0!==f[v])return!0}return!1}},164,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);m.exports={showErrorDialog:function(o){var t,c=o.componentStack,p=o.error;t=p instanceof Error?p:'string'==typeof p?new n.SyntheticError(p):new n.SyntheticError('Unspecified error');try{t.componentStack=c}catch(n){}return(0,n.handleException)(t,!1),!1}}},165,[97]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},166,[167]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var n=void 0,t=void 0,l=void 0,o=void 0,u=void 0;if(e.unstable_now=void 0,e.unstable_forceFrameRate=void 0,"undefined"==typeof window||"function"!=typeof MessageChannel){var s=null,c=null,f=function(){if(null!==s)try{var n=e.unstable_now();s(!0,n),s=null}catch(n){throw setTimeout(f,0),n}};e.unstable_now=function(){return Date.now()},n=function(t){null!==s?setTimeout(n,0,t):(s=t,setTimeout(f,0))},t=function(n,t){c=setTimeout(n,t)},l=function(){clearTimeout(c)},o=function(){return!1},u=e.unstable_forceFrameRate=function(){}}else{var p=window.performance,v=window.Date,b=window.setTimeout,w=window.clearTimeout,y=window.requestAnimationFrame,_=window.cancelAnimationFrame;"undefined"!=typeof console&&("function"!=typeof y&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof _&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")),e.unstable_now="object"==typeof p&&"function"==typeof p.now?function(){return p.now()}:function(){return v.now()};var x=!1,h=null,T=-1,k=-1,F=33.33,P=-1,M=-1,C=0,A=!1;o=function(){return e.unstable_now()>=C},u=function(){},e.unstable_forceFrameRate=function(n){0>n||125<n?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):0<n?(F=Math.floor(1e3/n),A=!0):(F=33.33,A=!1)};var L=function(){if(null!==h){var n=e.unstable_now(),t=0<C-n;try{h(t,n)||(h=null)}catch(n){throw j.postMessage(null),n}}},R=new MessageChannel,j=R.port2;R.port1.onmessage=L;var q=function n(t){if(null===h)M=P=-1,x=!1;else{x=!0,y(function(t){w(T),n(t)});if(T=b(function n(){C=e.unstable_now()+F/2,L(),T=b(n,3*F)},3*F),-1!==P&&.1<t-P){var l=t-P;!A&&-1!==M&&l<F&&M<F&&(8.33>(F=l<M?M:l)&&(F=8.33)),M=l}P=t,C=t+F,j.postMessage(null)}};n=function(n){h=n,x||(x=!0,y(function(n){q(n)}))},t=function e(n,t){k=b(function(){n(e.unstable_now())},t)},l=function(){w(k),k=-1}}var D=null,E=null,I=null,N=3,B=!1,O=!1,U=!1;function W(n,t){var l=n.next;if(l===n)D=null;else{n===D&&(D=l);var o=n.previous;o.next=l,l.previous=o}n.next=n.previous=null,l=n.callback,o=N;var u=I;N=n.priorityLevel,I=n;try{var s=n.expirationTime<=t;switch(N){case 1:var c=l(s);break;case 2:case 3:case 4:c=l(s);break;case 5:c=l(s)}}catch(n){throw n}finally{N=o,I=u}if("function"==typeof c)if(t=n.expirationTime,n.callback=c,null===D)D=n.next=n.previous=n;else{c=null,s=D;do{if(t<=s.expirationTime){c=s;break}s=s.next}while(s!==D);null===c?c=D:c===D&&(D=n),(t=c.previous).next=c.previous=n,n.next=c,n.previous=t}}function Y(n){if(null!==E&&E.startTime<=n)do{var t=E,l=t.next;if(t===l)E=null;else{E=l;var o=t.previous;o.next=l,l.previous=o}t.next=t.previous=null,J(t,t.expirationTime)}while(null!==E&&E.startTime<=n)}function z(l){U=!1,Y(l),O||(null!==D?(O=!0,n(G)):null!==E&&t(z,E.startTime-l))}function G(n,u){O=!1,U&&(U=!1,l()),Y(u),B=!0;try{if(n){if(null!==D)do{W(D,u),Y(u=e.unstable_now())}while(null!==D&&!o())}else for(;null!==D&&D.expirationTime<=u;)W(D,u),Y(u=e.unstable_now());return null!==D||(null!==E&&t(z,E.startTime-u),!1)}finally{B=!1}}function H(n){switch(n){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}function J(n,t){if(null===D)D=n.next=n.previous=n;else{var l=null,o=D;do{if(t<o.expirationTime){l=o;break}o=o.next}while(o!==D);null===l?l=D:l===D&&(D=n),(t=l.previous).next=l.previous=n,n.next=l,n.previous=t}}var K=u;e.unstable_ImmediatePriority=1,e.unstable_UserBlockingPriority=2,e.unstable_NormalPriority=3,e.unstable_IdlePriority=5,e.unstable_LowPriority=4,e.unstable_runWithPriority=function(n,t){switch(n){case 1:case 2:case 3:case 4:case 5:break;default:n=3}var l=N;N=n;try{return t()}finally{N=l}},e.unstable_next=function(n){switch(N){case 1:case 2:case 3:var t=3;break;default:t=N}var l=N;N=t;try{return n()}finally{N=l}},e.unstable_scheduleCallback=function(o,u,s){var c=e.unstable_now();if("object"==typeof s&&null!==s){var f=s.delay;f="number"==typeof f&&0<f?c+f:c,s="number"==typeof s.timeout?s.timeout:H(o)}else s=H(o),f=c;if(o={callback:u,priorityLevel:o,startTime:f,expirationTime:s=f+s,next:null,previous:null},f>c){if(s=f,null===E)E=o.next=o.previous=o;else{u=null;var p=E;do{if(s<p.startTime){u=p;break}p=p.next}while(p!==E);null===u?u=E:u===E&&(E=o),(s=u.previous).next=u.previous=o,o.next=u,o.previous=s}null===D&&E===o&&(U?l():U=!0,t(z,f-c))}else J(o,s),O||B||(O=!0,n(G));return o},e.unstable_cancelCallback=function(n){var t=n.next;if(null!==t){if(n===t)n===D?D=null:n===E&&(E=null);else{n===D?D=t:n===E&&(E=t);var l=n.previous;l.next=t,t.previous=l}n.next=n.previous=null}},e.unstable_wrapCallback=function(n){var t=N;return function(){var l=N;N=t;try{return n.apply(this,arguments)}finally{N=l}}},e.unstable_getCurrentPriorityLevel=function(){return N},e.unstable_shouldYield=function(){var n=e.unstable_now();return Y(n),null!==I&&null!==D&&D.startTime<=n&&D.expirationTime<I.expirationTime||o()},e.unstable_requestPaint=K,e.unstable_continueExecution=function(){O||B||(O=!0,n(G))},e.unstable_pauseExecution=function(){},e.unstable_getFirstCallbackNode=function(){return D}},167,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports={uiViewClassName:'RCTView',bubblingEventTypes:{topSelect:{phasedRegistrationNames:{bubbled:'onSelect',captured:'onSelectCapture'}}},directEventTypes:{topClick:{registrationName:'onClick'},topContentSizeChange:{registrationName:'onContentSizeChange'},topLoadingError:{registrationName:'onLoadingError'},topLoadingFinish:{registrationName:'onLoadingFinish'},topLoadingStart:{registrationName:'onLoadingStart'},topMessage:{registrationName:'onMessage'},topMomentumScrollBegin:{registrationName:'onMomentumScrollBegin'},topMomentumScrollEnd:{registrationName:'onMomentumScrollEnd'},topScroll:{registrationName:'onScroll'},topScrollBeginDrag:{registrationName:'onScrollBeginDrag'},topScrollEndDrag:{registrationName:'onScrollEndDrag'},topSelectionChange:{registrationName:'onSelectionChange'}},validAttributes:{hasTVPreferredFocus:!0,focusable:!0,nativeBackgroundAndroid:!0,nativeForegroundAndroid:!0,nextFocusDown:!0,nextFocusForward:!0,nextFocusLeft:!0,nextFocusRight:!0,nextFocusUp:!0}}},168,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),s=t(r(d[2])),u=r(d[3]),b=r(d[4]);m.exports=function(t,l){var v={uiViewClassName:t,Commands:{},bubblingEventTypes:(0,n.default)({},b.bubblingEventTypes,l.bubblingEventTypes||{}),directEventTypes:(0,n.default)({},b.directEventTypes,l.directEventTypes||{}),validAttributes:(0,n.default)({},b.validAttributes,l.validAttributes||{})};u.register(t,function(){return(0,s.default)(t,v),v})}},169,[3,54,170,162,171]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.lefthandObjectDiff=function t(n,u){var o={};function c(n,f,u){if(typeof n==typeof f||null==n)if('object'!=typeof n)n===f||(o[u]=f);else{var c=t(n,f);Object.keys(c).length>1&&(o[u]=c)}else o[u]=f}for(var l in n)f.includes(l)||(u?n.hasOwnProperty(l)&&c(n[l],u[l],l):o[l]={});return o},e.getConfigWithoutViewProps=function(t,f){if(!t[f])return{};return Object.keys(t[f]).filter(function(t){return!n.default[f][t]}).reduce(function(n,u){return n[u]=t[f][u],n},{})},e.stringifyViewConfig=function(t){return JSON.stringify(t,function(t,n){return'function'==typeof n?"\u0192 "+n.name:n},2)},e.default=void 0;var n=t(r(d[1])),f=(r(d[2]),['transform','hitSlop']);var u=function(t,n){};e.default=u},170,[3,171,174]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=t(r(d[1])),s=t(r(d[2])),n={uiViewClassName:'RCTView',baseModuleName:null,Manager:'ViewManager',Commands:{},Constants:{},bubblingEventTypes:(0,o.default)({},s.default.bubblingEventTypes,{topBlur:{phasedRegistrationNames:{bubbled:'onBlur',captured:'onBlurCapture'}},topChange:{phasedRegistrationNames:{bubbled:'onChange',captured:'onChangeCapture'}},topEndEditing:{phasedRegistrationNames:{bubbled:'onEndEditing',captured:'onEndEditingCapture'}},topFocus:{phasedRegistrationNames:{bubbled:'onFocus',captured:'onFocusCapture'}},topKeyPress:{phasedRegistrationNames:{bubbled:'onKeyPress',captured:'onKeyPressCapture'}},topPress:{phasedRegistrationNames:{bubbled:'onPress',captured:'onPressCapture'}},topSubmitEditing:{phasedRegistrationNames:{bubbled:'onSubmitEditing',captured:'onSubmitEditingCapture'}},topTouchCancel:{phasedRegistrationNames:{bubbled:'onTouchCancel',captured:'onTouchCancelCapture'}},topTouchEnd:{phasedRegistrationNames:{bubbled:'onTouchEnd',captured:'onTouchEndCapture'}},topTouchMove:{phasedRegistrationNames:{bubbled:'onTouchMove',captured:'onTouchMoveCapture'}},topTouchStart:{phasedRegistrationNames:{bubbled:'onTouchStart',captured:'onTouchStartCapture'}}}),directEventTypes:(0,o.default)({},s.default.directEventTypes,{topAccessibilityAction:{registrationName:'onAccessibilityAction'},topAccessibilityEscape:{registrationName:'onAccessibilityEscape'},topAccessibilityTap:{registrationName:'onAccessibilityTap'},topLayout:{registrationName:'onLayout'},topMagicTap:{registrationName:'onMagicTap'},onGestureHandlerEvent:{registrationName:'onGestureHandlerEvent'},onGestureHandlerStateChange:{registrationName:'onGestureHandlerStateChange'}}),validAttributes:(0,o.default)({},s.default.validAttributes,{accessibilityActions:!0,accessibilityElementsHidden:!0,accessibilityHint:!0,accessibilityIgnoresInvertColors:!0,accessibilityLabel:!0,accessibilityLiveRegion:!0,accessibilityRole:!0,accessibilityStates:!0,accessibilityState:!0,accessibilityViewIsModal:!0,accessible:!0,alignContent:!0,alignItems:!0,alignSelf:!0,aspectRatio:!0,backfaceVisibility:!0,backgroundColor:{process:r(d[3])},borderBottomColor:{process:r(d[3])},borderBottomEndRadius:!0,borderBottomLeftRadius:!0,borderBottomRightRadius:!0,borderBottomStartRadius:!0,borderBottomWidth:!0,borderColor:{process:r(d[3])},borderEndColor:{process:r(d[3])},borderEndWidth:!0,borderLeftColor:{process:r(d[3])},borderLeftWidth:!0,borderRadius:!0,borderRightColor:{process:r(d[3])},borderRightWidth:!0,borderStartColor:{process:r(d[3])},borderStartWidth:!0,borderStyle:!0,borderTopColor:{process:r(d[3])},borderTopEndRadius:!0,borderTopLeftRadius:!0,borderTopRightRadius:!0,borderTopStartRadius:!0,borderTopWidth:!0,borderWidth:!0,bottom:!0,clickable:!0,collapsable:!0,direction:!0,display:!0,elevation:!0,end:!0,flex:!0,flexBasis:!0,flexDirection:!0,flexGrow:!0,flexShrink:!0,flexWrap:!0,height:!0,hitSlop:{diff:r(d[4])},importantForAccessibility:!0,justifyContent:!0,left:!0,margin:!0,marginBottom:!0,marginEnd:!0,marginHorizontal:!0,marginLeft:!0,marginRight:!0,marginStart:!0,marginTop:!0,marginVertical:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,nativeID:!0,needsOffscreenAlphaCompositing:!0,onAccessibilityAction:!0,onAccessibilityEscape:!0,onAccessibilityTap:!0,onLayout:!0,onMagicTap:!0,opacity:!0,overflow:!0,padding:!0,paddingBottom:!0,paddingEnd:!0,paddingHorizontal:!0,paddingLeft:!0,paddingRight:!0,paddingStart:!0,paddingTop:!0,paddingVertical:!0,pointerEvents:!0,position:!0,removeClippedSubviews:!0,renderToHardwareTextureAndroid:!0,right:!0,rotation:!0,scaleX:!0,scaleY:!0,shadowColor:{process:r(d[3])},shadowOffset:{diff:r(d[5])},shadowOpacity:!0,shadowRadius:!0,shouldRasterizeIOS:!0,start:!0,style:{alignContent:!0,alignItems:!0,alignSelf:!0,aspectRatio:!0,backfaceVisibility:!0,backgroundColor:{process:r(d[3])},borderBottomColor:{process:r(d[3])},borderBottomEndRadius:!0,borderBottomLeftRadius:!0,borderBottomRightRadius:!0,borderBottomStartRadius:!0,borderBottomWidth:!0,borderColor:{process:r(d[3])},borderEndColor:{process:r(d[3])},borderEndWidth:!0,borderLeftColor:{process:r(d[3])},borderLeftWidth:!0,borderRadius:!0,borderRightColor:{process:r(d[3])},borderRightWidth:!0,borderStartColor:{process:r(d[3])},borderStartWidth:!0,borderStyle:!0,borderTopColor:{process:r(d[3])},borderTopEndRadius:!0,borderTopLeftRadius:!0,borderTopRightRadius:!0,borderTopStartRadius:!0,borderTopWidth:!0,borderWidth:!0,bottom:!0,color:{process:r(d[3])},decomposedMatrix:!0,direction:!0,display:!0,elevation:!0,end:!0,flex:!0,flexBasis:!0,flexDirection:!0,flexGrow:!0,flexShrink:!0,flexWrap:!0,fontFamily:!0,fontSize:!0,fontStyle:!0,fontVariant:!0,fontWeight:!0,height:!0,includeFontPadding:!0,justifyContent:!0,left:!0,letterSpacing:!0,lineHeight:!0,margin:!0,marginBottom:!0,marginEnd:!0,marginHorizontal:!0,marginLeft:!0,marginRight:!0,marginStart:!0,marginTop:!0,marginVertical:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,opacity:!0,overflow:!0,overlayColor:{process:r(d[3])},padding:!0,paddingBottom:!0,paddingEnd:!0,paddingHorizontal:!0,paddingLeft:!0,paddingRight:!0,paddingStart:!0,paddingTop:!0,paddingVertical:!0,position:!0,resizeMode:!0,right:!0,rotation:!0,scaleX:!0,scaleY:!0,shadowColor:{process:r(d[3])},shadowOffset:{diff:r(d[5])},shadowOpacity:!0,shadowRadius:!0,start:!0,textAlign:!0,textAlignVertical:!0,textDecorationColor:{process:r(d[3])},textDecorationLine:!0,textDecorationStyle:!0,textShadowColor:{process:r(d[3])},textShadowOffset:!0,textShadowRadius:!0,textTransform:!0,tintColor:{process:r(d[3])},top:!0,transform:{diff:r(d[6])},transformMatrix:!0,translateX:!0,translateY:!0,width:!0,writingDirection:!0,zIndex:!0},testID:!0,top:!0,transform:{diff:r(d[6])},translateX:!0,translateY:!0,width:!0,zIndex:!0})};m.exports=n},171,[3,54,168,82,172,85,173]); -__d(function(g,r,i,a,m,e,d){'use strict';var t={top:void 0,left:void 0,right:void 0,bottom:void 0};m.exports=function(o,f){return(o=o||t)!==(f=f||t)&&(o.top!==f.top||o.left!==f.left||o.right!==f.right||o.bottom!==f.bottom)}},172,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(t,n){return!(t===n||t&&n&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[5]===n[5]&&t[10]===n[10]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[11]===n[11]&&t[15]===n[15])}},173,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),s=r(d[2]),o=r(d[3]),u=r(d[4]),c=r(d[5]),l=r(d[6]),v=r(d[7]),b=r(d[8]),p=r(d[9]),f=r(d[10]),y=r(d[11]),C=r(d[12]);var E=!1;function T(n){var t=o.getConstants();t.ViewManagerNames||t.LazyViewManagersEnabled?n=w(n,o.getDefaultEventTypes()):(n.bubblingEventTypes=w(n.bubblingEventTypes,t.genericBubblingEventTypes),n.directEventTypes=w(n.directEventTypes,t.genericDirectEventTypes))}function w(n,t){if(!t)return n;if(!n)return t;for(var s in t)if(t.hasOwnProperty(s)){var o=t[s];if(n.hasOwnProperty(s)){var u=n[s];'object'==typeof o&&'object'==typeof u&&(o=w(u,o))}n[s]=o}return n}function I(n){switch(n){case'CATransform3D':return l;case'CGPoint':return v;case'CGSize':return y;case'UIEdgeInsets':return u}return null}function N(n){switch(n){case'CGColor':case'UIColor':return b;case'CGColorArray':case'UIColorArray':return p;case'CGImage':case'UIImage':case'RCTImageSource':return f;case'Color':return b;case'ColorArray':return p}return null}m.exports=function(u){var l=o.getViewManagerConfig(u);c(null!=l&&null!=l.NativeProps,'requireNativeComponent: "%s" was not found in the UIManager.',u);for(var v=l.baseModuleName,b=l.bubblingEventTypes,p=l.directEventTypes,f=l.NativeProps;v;){var y=o.getViewManagerConfig(v);y?(b=t({},y.bubblingEventTypes,b),p=t({},y.directEventTypes,p),f=t({},y.NativeProps,f),v=y.baseModuleName):(C(!1,'Base module "%s" does not exist',v),v=null)}var w={};for(var M in f){var P=f[M],h=I(P),A=N(P);w[M]=null==h&&null==A||{diff:h,process:A}}return w.style=s,n(l,{uiViewClassName:u,validAttributes:w,bubblingEventTypes:b,directEventTypes:p}),E||(T(l),E=!0),l}},174,[16,54,64,75,172,18,173,175,82,176,177,85,20]); -__d(function(g,r,i,a,m,e,d){'use strict';var t={x:void 0,y:void 0};m.exports=function(n,o){return(n=n||t)!==(o=o||t)&&(n.x!==o.x||n.y!==o.y)}},175,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=function(t){return null==t?null:t.map(n)}},176,[82]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n,s,u,o=r(d[0]),f=r(d[1]);function c(){if(u)return u;var t=g.nativeExtensions&&g.nativeExtensions.SourceCode;return t||(t=r(d[2]).default),u=t.getConstants().scriptURL}function l(){if(void 0===n){var t=c(),s=t&&t.match(/^https?:\/\/.*?\//);n=s?s[0]:null}return n}function v(t){if(t){if(t.startsWith('assets://'))return null;(t=t.substring(0,t.lastIndexOf('/')+1)).includes('://')||(t='file://'+t)}return t}m.exports=function(n){if('object'==typeof n)return n;var u=o.getAssetByID(n);if(!u)return null;var p=new f(l(),(void 0===s&&(s=v(c())),s),u);return t?t(p):p.defaultAsset()},m.exports.pickScale=f.pickScale,m.exports.setCustomSourceTransformer=function(n){t=n}},177,[178,179,181]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=[];m.exports={registerAsset:function(s){return t.push(s)},getAssetByID:function(s){return t[s-1]}}},178,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n=r(d[2]),u=(r(d[3]),r(d[4])),o=r(d[5]);function l(t){var s=h.pickScale(t.scales,n.get()),o=1===s?'':'@'+s+'x';return u.getBasePath(t)+'/'+t.name+o+'.'+t.type}function c(t){var s=h.pickScale(t.scales,n.get());return u.getAndroidResourceFolderName(t,s)+'/'+u.getAndroidResourceIdentifier(t)+'.'+t.type}var h=(function(){function h(s,n,u){t(this,h),this.serverUrl=s,this.jsbundleUrl=n,this.asset=u}return s(h,[{key:"isLoadedFromServer",value:function(){return!!this.serverUrl}},{key:"isLoadedFromFileSystem",value:function(){return!(!this.jsbundleUrl||!this.jsbundleUrl.startsWith('file://'))}},{key:"defaultAsset",value:function(){return this.isLoadedFromServer()?this.assetServerURL():this.scaledAssetURLNearBundle()}},{key:"assetServerURL",value:function(){return o(!!this.serverUrl,'need server to load from'),this.fromSource(this.serverUrl+l(this.asset)+"?platform=ios&hash="+this.asset.hash)}},{key:"scaledAssetPath",value:function(){return this.fromSource(l(this.asset))}},{key:"scaledAssetURLNearBundle",value:function(){var t=this.jsbundleUrl||'file://';return this.fromSource(t+l(this.asset))}},{key:"resourceIdentifierWithoutScale",value:function(){return o(!1,'resource identifiers work on Android'),this.fromSource(u.getAndroidResourceIdentifier(this.asset))}},{key:"drawableFolderInBundle",value:function(){var t=this.jsbundleUrl||'file://';return this.fromSource(t+c(this.asset))}},{key:"fromSource",value:function(t){return{__packager_asset:!0,width:this.asset.width,height:this.asset.height,uri:t,scale:h.pickScale(this.asset.scales,n.get())}}}],[{key:"pickScale",value:function(t,s){for(var n=0;n<t.length;n++)if(t[n]>=s)return t[n];return t[t.length-1]||1}}]),h})();m.exports=h},179,[4,5,61,58,180,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t={.75:'ldpi',1:'mdpi',1.5:'hdpi',2:'xhdpi',3:'xxhdpi',4:'xxxhdpi'};function n(n){if(n.toString()in t)return t[n.toString()];throw new Error('no such scale '+n.toString())}var o=new Set(['gif','jpeg','jpg','png','svg','webp','xml']);function s(t){var n=t.httpServerLocation;return'/'===n[0]&&(n=n.substr(1)),n}m.exports={getAndroidAssetSuffix:n,getAndroidResourceFolderName:function(s,u){if(!o.has(s.type))return'raw';var c=n(u);if(!c)throw new Error("Don't know which android drawable suffix to use for scale: "+u+'\nAsset: '+JSON.stringify(s,null,'\t')+'\nPossible scales are:'+JSON.stringify(t,null,'\t'));return'drawable-'+c},getAndroidResourceIdentifier:function(t){return(s(t)+'/'+t.name).toLowerCase().replace(/\//g,'_').replace(/([^a-z0-9_])/g,'').replace(/^assets_/,'')},getBasePath:s}},180,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).getEnforcing('SourceCode');e.default=o},181,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]);m.exports=function(u){return n(u,function(){return t(u)})}},182,[183,174]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]).ReactNativeViewConfigRegistry.register;m.exports=function(n,s){return t(n,s)}},183,[160]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=(0,t(r(d[1])).default)('ActivityIndicatorView',{paperComponentName:'RCTActivityIndicatorView'});e.default=o},184,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[1])),t=r(d[2]);var p=function(n,p){var f=p&&p.paperComponentName?p.paperComponentName:n;if(null!=p&&null!=p.paperComponentNameDeprecated)if(t.UIManager.getViewManagerConfig(n))f=n;else{if(null==p.paperComponentNameDeprecated||!t.UIManager.getViewManagerConfig(p.paperComponentNameDeprecated))throw new Error("Failed to find native component for either "+n+" or "+(p.paperComponentNameDeprecated||'(unknown)'));f=p.paperComponentNameDeprecated}return(0,o.default)(f)};e.default=p},185,[3,182,17]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),l=r(d[3]),u=r(d[4]),s=r(d[5]),f=r(d[6]),c=r(d[7]),h=r(d[8]),p=r(d[9]),v=r(d[10]),y=r(d[11]),b=r(d[12]),k=r(d[13]);function S(t,n){if(null==t||null==n)return!0;if(t.length!==n.length)return!0;for(var o=0;o<t.length;o++)if(t[o]!==n[o])return!0;return!1}var w=b(v.UIView,{}),x={transform:{diff:S},opacity:!0},C=b(x,{clipping:{diff:S}}),T=b(x,{fill:{diff:S},stroke:{diff:S},strokeWidth:!0,strokeCap:!0,strokeJoin:!0,strokeDash:{diff:S}}),_=b(T,{d:{diff:S}}),A=b(T,{alignment:!0,frame:{diff:function(t,n){if(t===n)return!1;if(t.font!==n.font){if(null===t.font)return!0;if(null===n.font)return!0;if(t.font.fontFamily!==n.font.fontFamily||t.font.fontSize!==n.font.fontSize||t.font.fontWeight!==n.font.fontWeight||t.font.fontStyle!==n.font.fontStyle)return!0}return S(t.lines,n.lines)}},path:{diff:S}}),R=y('ARTSurfaceView',function(){return{validAttributes:w,uiViewClassName:'ARTSurfaceView'}}),W=y('ARTGroup',function(){return{validAttributes:C,uiViewClassName:'ARTGroup'}}),J=y('ARTShape',function(){return{validAttributes:_,uiViewClassName:'ARTShape'}}),V=y('ARTText',function(){return{validAttributes:A,uiViewClassName:'ARTText'}});function G(t){return t?'string'==typeof t?t:t.length?t.join('\n'):'':''}var I=(function(s){function f(){return t(this,f),o(this,l(f).apply(this,arguments))}return u(f,s),n(f,[{key:"getChildContext",value:function(){return{isInSurface:!0}}},{key:"render",value:function(){var t=N(this.props.height,0),n=N(this.props.width,0);return h.createElement(R,{style:[this.props.style,{height:t,width:n}]},this.props.children)}}]),f})(h.Component);function N(t,n){return null==t?n:+t}I.childContextTypes={isInSurface:p.bool};var z=new c;function D(t){var n=null!=t.scaleX?t.scaleX:null!=t.scale?t.scale:1,o=null!=t.scaleY?t.scaleY:null!=t.scale?t.scale:1;return z.transformTo(1,0,0,1,0,0).move(t.x||0,t.y||0).rotate(t.rotation||0,t.originX,t.originY).scale(n,o,t.originX,t.originY),null!=t.transform&&z.transform(t.transform),[z.xx,z.yx,z.xy,z.yy,z.x,z.y]}function E(t){return!1===t.visible?0:null==t.opacity?1:+t.opacity}var F=(function(s){function f(){return t(this,f),o(this,l(f).apply(this,arguments))}return u(f,s),n(f,[{key:"render",value:function(){var t=this.props;return k(this.context.isInSurface,'ART: <Group /> must be a child of a <Surface />'),h.createElement(W,{opacity:E(t),transform:D(t)},this.props.children)}}]),f})(h.Component);F.contextTypes={isInSurface:p.bool.isRequired};var M=(function(s){function f(){return t(this,f),o(this,l(f).apply(this,arguments))}return u(f,s),n(f,[{key:"render",value:function(){var t=this.props,n=[N(t.x,0),N(t.y,0),N(t.width,0),N(t.height,0)],o=b(t);return delete o.x,delete o.y,h.createElement(W,{clipping:n,opacity:E(t),transform:D(o)},this.props.children)}}]),f})(h.Component),P=0,X=1,Y=2,O=3;function q(t,n,o){var l=new s(t);n[o+0]=l.red/255,n[o+1]=l.green/255,n[o+2]=l.blue/255,n[o+3]=l.alpha}function j(t,n,o){var l=0;if('length'in t)for(;l<t.length;)q(t[l],n,o+4*l),l++;else for(var u in t)q(t[u],n,o+4*l),l++;return o+4*l}function L(t,n,o,l,u){var s,f=0;if('length'in t)for(;f<t.length;)s=f/(t.length-1)*l,n[o+f]=u?1-s:s,f++;else for(var c in t)s=+c*l,n[o+f]=u?1-s:s,f++;return o+f}function U(t,n,o){L(t,n,j(t,n,o),1,!1)}function $(t,n,o){var l=j(t,n,o);L(t,n,l=L(t,n,l=j(t,n,l),.5,!1),.5,!0)}function B(t,n){var o=t[0],l=+n.width,u=+n.height;o===X?(t[1]*=l,t[2]*=u,t[3]*=l,t[4]*=u):o===Y&&(t[1]*=l,t[2]*=u,t[3]*=l,t[4]*=u,t[5]*=l,t[6]*=u)}function H(t,n){if(null==t)return null;if(t._brush)return t._bb&&(B(t._brush,n),t._bb=!1),t._brush;var o=new s(t);return[P,o.red/255,o.green/255,o.blue/255,o.alpha]}function K(t){if(null==t)return null;var n=new s(t);return[n.red/255,n.green/255,n.blue/255,n.alpha]}function Q(t){switch(t){case'butt':return 0;case'square':return 2;default:return 1}}function Z(t){switch(t){case'miter':return 0;case'bevel':return 2;default:return 1}}var tt=(function(s){function c(){return t(this,c),o(this,l(c).apply(this,arguments))}return u(c,s),n(c,[{key:"render",value:function(){var t=this.props,n=t.d||G(t.children),o=(n instanceof f?n:new f(n)).toJSON();return h.createElement(J,{fill:H(t.fill,t),opacity:E(t),stroke:K(t.stroke),strokeCap:Q(t.strokeCap),strokeDash:t.strokeDash||null,strokeJoin:Z(t.strokeJoin),strokeWidth:N(t.strokeWidth,1),transform:D(t),d:o})}}]),c})(h.Component),nt={},et=/^[\s"']*/,rt=/[\s"']*$/;function it(t){return t.split(',')[0].replace(et,'').replace(rt,'')}function ot(t){if(nt.hasOwnProperty(t))return nt[t];var n=/^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i.exec(t);if(!n)return null;var o=it(n[3]),l=+n[2]||12,u=/bold/.exec(n[1]),s=/italic/.exec(n[1]);return nt[t]={fontFamily:o,fontSize:l,fontWeight:u?'bold':'normal',fontStyle:s?'italic':'normal'},nt[t]}function lt(t){return null==t?null:'string'==typeof t?ot(t):{fontFamily:it(t.fontFamily),fontSize:+t.fontSize||12,fontWeight:null!=t.fontWeight?t.fontWeight.toString():'400',fontStyle:t.fontStyle}}var ut=/\n/g;function at(t){switch(t){case'right':return 1;case'center':return 2;default:return 0}}var st=(function(s){function c(){return t(this,c),o(this,l(c).apply(this,arguments))}return u(c,s),n(c,[{key:"render",value:function(){var t,n,o=this.props,l=o.path,u=l?(l instanceof f?l:new f(l)).toJSON():null,s=(t=o.font,n=G(o.children),{font:lt(t),lines:n.split(ut)});return h.createElement(V,{fill:H(o.fill,o),opacity:E(o),stroke:K(o.stroke),strokeCap:Q(o.strokeCap),strokeDash:o.strokeDash||null,strokeJoin:Z(o.strokeJoin),strokeWidth:N(o.strokeWidth,1),transform:D(o),alignment:at(o.alignment),frame:s,path:u})}}]),c})(h.Component);var ft={LinearGradient:function(t,n,o,l,u){var s=X;if(arguments.length<5){var f=(null==n?270:n)*Math.PI/180,c=Math.cos(f),h=-Math.sin(f),p=(Math.abs(c)+Math.abs(h))/2;n=.5-(c*=p),l=.5+c,o=.5-(h*=p),u=.5+h,this._bb=!0}else this._bb=!1;var v=[s,+n,+o,+l,+u];U(t,v,5),this._brush=v},RadialGradient:function(t,n,o,l,u,s,f){null==u&&(u=l),null==s&&(s=n),null==f&&(f=o),null==n?(n=o=l=u=s=f=.5,this._bb=!0):this._bb=!1;var c=[Y,+n,+o,2*+l,2*+u,+s,+f];$(t,c,7),this._brush=c},Pattern:function(t,n,o,l,u){this._brush=[O,t,+l||0,+u||0,+n,+o]},Transform:c,Path:f,Surface:I,Group:F,ClippingRectangle:M,Shape:tt,Text:st};m.exports=ft},186,[4,5,6,9,10,187,188,191,13,69,192,183,193,18]); -__d(function(g,r,i,a,m,e,d){var t={maroon:'#800000',red:'#ff0000',orange:'#ffA500',yellow:'#ffff00',olive:'#808000',purple:'#800080',fuchsia:"#ff00ff",white:'#ffffff',lime:'#00ff00',green:'#008000',navy:'#000080',blue:'#0000ff',aqua:'#00ffff',teal:'#008080',black:'#000000',silver:'#c0c0c0',gray:'#808080'},n=function(t,n){for(var h=[],u=0,s=t.length;u<s;u++)h[u]=n(t[u],u);return h},h=function n(h,u){if(h.isColor)this.red=h.red,this.green=h.green,this.blue=h.blue,this.alpha=h.alpha;else{var s=t[h];switch(s&&(h=s,u='hex'),typeof h){case'string':u||(u=(u=h.match(/^rgb|^hsb|^hsl/))?u[0]:'hex');break;case'object':u=u||'rgb',h=h.toString();break;case'number':u='hex',h=h.toString(16)}h=n['parse'+u.toUpperCase()](h),this.red=h[0],this.green=h[1],this.blue=h[2],this.alpha=h[3]}this.isColor=!0},u=function(t,n,h){return Math.min(h,Math.max(n,t))},s=/([-.\d]+\%?)\s*,\s*([-.\d]+\%?)\s*,\s*([-.\d]+\%?)\s*,?\s*([-.\d]*\%?)/,o=/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{0,2})$/i;h.parseRGB=function(t){return n(t.match(s).slice(1),function(t,n){return t&&(t=parseFloat(t)*('%'==t[t.length-1]?2.55:1)),n<3?Math.round((t%=256)<0?t+256:t):u(''===t?1:Number(t),0,1)})},h.parseHEX=function(t){return 1==t.length&&(t=t+t+t),n(t.match(o).slice(1),function(t,n){return 3==n?t?parseInt(t,16)/255:1:parseInt(1==t.length?t+t:t,16)})},h.parseHSB=function(t){var h=n(t.match(s).slice(1),function(t,n){return t&&(t=parseFloat(t)),0===n?Math.round((t%=360)<0?t+360:t):n<3?u(Math.round(t),0,100):u(''===t?1:Number(t),0,1)}),o=h[3],f=Math.round(h[2]/100*255);if(0==h[1])return[f,f,f,o];var l=h[0],c=l%60,b=Math.round(h[2]*(100-h[1])/1e4*255),p=Math.round(h[2]*(6e3-h[1]*c)/6e5*255),M=Math.round(h[2]*(6e3-h[1]*(60-c))/6e5*255);switch(Math.floor(l/60)){case 0:return[f,M,b,o];case 1:return[p,f,b,o];case 2:return[b,f,M,o];case 3:return[b,p,f,o];case 4:return[M,b,f,o];default:return[f,b,p,o]}},h.parseHSL=function(t){var h=n(t.match(s).slice(1),function(t,n){return t&&(t=parseFloat(t)),0===n?Math.round((t%=360)<0?t+360:t):n<3?u(Math.round(t),0,100):u(''===t?1:Number(t),0,1)}),o=h[0]/60,f=h[1]/100,l=h[2]/100,c=h[3],b=(1-Math.abs(2*l-1))*f,p=b*(1-Math.abs(o%2-1)),M=l-b/2,v=Math.round(255*(b+M)),x=Math.round(255*(p+M)),w=Math.round(255*M);switch(Math.floor(o)){case 0:return[v,x,w,c];case 1:return[x,v,w,c];case 2:return[w,v,x,c];case 3:return[w,x,v,c];case 4:return[x,w,v,c];default:return[v,w,x,c]}};var f=function(t,n){return 1!=n[3]?t+='a':n.pop(),t+'('+n.join(', ')+')'};(h.prototype={toHSB:function(t){var n=this.red,h=this.green,u=this.blue,s=this.alpha,o=Math.max(n,h,u),l=o-Math.min(n,h,u),c=0,b=0!=l?l/o:0,p=o/255;if(b){var M=(o-n)/l,v=(o-h)/l,x=(o-u)/l;c=n==o?x-v:h==o?2+M-x:4+v-M,(c/=6)<0&&c++}var w=[Math.round(360*c),Math.round(100*b),Math.round(100*p),s];return t?w:f('hsb',w)},toHSL:function(t){var n=this.red,h=this.green,u=this.blue,s=this.alpha,o=Math.max(n,h,u),l=Math.min(n,h,u),c=o-l,b=0,p=0!=c?c/(255-Math.abs(o+l-255)):0,M=(o+l)/512;if(p){var v=(o-n)/c,x=(o-h)/c,w=(o-u)/c;b=n==o?w-x:h==o?2+v-w:4+x-v,(b/=6)<0&&b++}var S=[Math.round(360*b),Math.round(100*p),Math.round(100*M),s];return t?S:f('hsl',S)},toHEX:function(t){var h=this.alpha,u=1==(h=Math.round(255*h).toString(16)).length?h+h:h,s=n([this.red,this.green,this.blue],function(t){return 1==(t=t.toString(16)).length?'0'+t:t});return t?s.concat(u):'#'+s.join('')+('ff'==u?'':u)},toRGB:function(t){var n=[this.red,this.green,this.blue,this.alpha];return t?n:f('rgb',n)}}).toString=h.prototype.toRGB,h.hex=function(t){return new h(t,'hex')},null==this.hex&&(this.hex=h.hex),h.hsb=function(t,n,u,s){return new h([t||0,n||0,u||0,null==s?1:s],'hsb')},null==this.hsb&&(this.hsb=h.hsb),h.hsl=function(t,n,u,s){return new h([t||0,n||0,u||0,null==s?1:s],'hsl')},null==this.hsl&&(this.hsl=h.hsl),h.rgb=function(t,n,u,s){return new h([t||0,n||0,u||0,null==s?1:s],'rgb')},null==this.rgb&&(this.rgb=h.rgb),h.detach=function(t){return t=new h(t),[h.rgb(t.red,t.green,t.blue).toString(),t.alpha]},m.exports=h},187,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),h=t(n,{initialize:function(t){this.reset(),t instanceof h?this.path=t.path.slice(0):t&&(t.applyToPath?t.applyToPath(this):this.push(t))},onReset:function(){this.path=[]},onMove:function(t,n,h,o){this.path.push(0,h,o)},onLine:function(t,n,h,o){this.path.push(2,h,o)},onBezierCurve:function(t,n,h,o,s,p,u,c){this.path.push(3,h,o,s,p,u,c)},_arcToBezier:n.prototype.onArc,onArc:function(t,n,h,o,s,p,u,c,f,l,z,T){if(u!==c||T)return this._arcToBezier(t,n,h,o,s,p,u,c,f,l,z,T);this.path.push(4,s,p,u,f,l,z?0:1)},onClose:function(){this.path.push(1)},toJSON:function(){return this.path}});m.exports=h},188,[189,190]); -__d(function(g,r,i,a,m,e,d){m.exports=function(t){for(var n={},o=0,c=arguments.length;o<c;o++){var u=arguments[o];for(var f in'function'==typeof u&&(u=u.prototype),u)n[f]=u[f]}return n.initialize||(n.initialize=function(){}),n.constructor=function(t,o,c,u,f,p,l,s){return new n.initialize(t,o,c,u,f,p,l,s)},n.constructor.prototype=n.initialize.prototype=n,n.constructor}},189,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=t({initialize:function(t){this.reset().push(t)},push:function(){var t=Array.prototype.join.call(arguments,' ').match(/[a-df-z]|[\-+]?(?:[\d\.]e[\-+]?|[^\s\-+,a-z])+/gi);if(!t)return this;for(var n,s=t[0],h=1;s;){switch(s){case'm':this.move(t[h++],t[h++]);break;case'l':this.line(t[h++],t[h++]);break;case'c':this.curve(t[h++],t[h++],t[h++],t[h++],t[h++],t[h++]);break;case's':this.curve(t[h++],t[h++],null,null,t[h++],t[h++]);break;case'q':this.curve(t[h++],t[h++],t[h++],t[h++]);break;case't':this.curve(t[h++],t[h++]);break;case'a':this.arc(t[h+5],t[h+6],t[h],t[h+1],t[h+3],!+t[h+4],t[h+2]),h+=7;break;case'h':this.line(t[h++],0);break;case'v':this.line(0,t[h++]);break;case'M':this.moveTo(t[h++],t[h++]);break;case'L':this.lineTo(t[h++],t[h++]);break;case'C':this.curveTo(t[h++],t[h++],t[h++],t[h++],t[h++],t[h++]);break;case'S':this.curveTo(t[h++],t[h++],null,null,t[h++],t[h++]);break;case'Q':this.curveTo(t[h++],t[h++],t[h++],t[h++]);break;case'T':this.curveTo(t[h++],t[h++]);break;case'A':this.arcTo(t[h+5],t[h+6],t[h],t[h+1],t[h+3],!+t[h+4],t[h+2]),h+=7;break;case'H':this.lineTo(t[h++],this.penY);break;case'V':this.lineTo(this.penX,t[h++]);break;case'Z':case'z':this.close();break;default:s=n,h--;continue}'m'==(n=s)?n='l':'M'==n&&(n='L'),s=t[h++]}return this},reset:function(){return this.penX=this.penY=0,this.penDownX=this.penDownY=null,this._pivotX=this._pivotY=0,this.onReset(),this},move:function(t,n){return this.onMove(this.penX,this.penY,this._pivotX=this.penX+=+t,this._pivotY=this.penY+=+n),this},moveTo:function(t,n){return this.onMove(this.penX,this.penY,this._pivotX=this.penX=+t,this._pivotY=this.penY=+n),this},line:function(t,n){return this.lineTo(this.penX+ +t,this.penY+ +n)},lineTo:function(t,n){return null==this.penDownX&&(this.penDownX=this.penX,this.penDownY=this.penY),this.onLine(this.penX,this.penY,this._pivotX=this.penX=+t,this._pivotY=this.penY=+n),this},curve:function(t,n,s,h,o,u){var p=this.penX,c=this.penY;return this.curveTo(p+ +t,c+ +n,null==s?null:p+ +s,null==h?null:c+ +h,null==o?null:p+ +o,null==u?null:c+ +u)},curveTo:function(t,n,s,h,o,u){var p=this.penX,c=this.penY;return null==s&&(s=+t,h=+n,t=2*p-(this._pivotX||0),n=2*c-(this._pivotY||0)),null==o?(this._pivotX=+t,this._pivotY=+n,s=((o=+s)+2*+t)/3,h=((u=+h)+2*+n)/3,t=(p+2*+t)/3,n=(c+2*+n)/3):(this._pivotX=+s,this._pivotY=+h),null==this.penDownX&&(this.penDownX=p,this.penDownY=c),this.onBezierCurve(p,c,+t,+n,+s,+h,this.penX=+o,this.penY=+u),this},arc:function(t,n,s,h,o,u,p){return this.arcTo(this.penX+ +t,this.penY+ +n,s,h,o,u,p)},arcTo:function(t,n,s,h,o,u,p){if(h=Math.abs(+h||+s||+n-this.penY),!(s=Math.abs(+s||+t-this.penX))||!h||t==this.penX&&n==this.penY)return this.lineTo(t,n);var c=this.penX,l=this.penY,v=!+u,X=!!+o,Y=p?p*Math.PI/180:0,f=Math.cos(Y),M=Math.sin(Y),b=f*(t-=c)/2+M*(n-=l)/2,T=-M*t/2+f*n/2,k=s*s*h*h,w=h*h*b*b,_=s*s*T*T,D=k-_-w;if(D<0)s*=D=Math.sqrt(1-D/k),h*=D,b=t/2,T=n/2;else{D=Math.sqrt(D/(_+w)),X==v&&(D=-D);var z=-D*T*s/h,C=D*b*h/s;b=f*z-M*C+t/2,T=M*z+f*C+n/2}var B=f/s,A=M/s,L=-M/h,I=f/h,P=Math.atan2(L*-b+I*-T,B*-b+A*-T),N=Math.atan2(L*(t-b)+I*(n-T),B*(t-b)+A*(n-T));return b+=c,T+=l,t+=c,n+=l,null==this.penDownX&&(this.penDownX=this.penX,this.penDownY=this.penY),this.onArc(c,l,this._pivotX=this.penX=t,this._pivotY=this.penY=n,b,T,s,h,P,N,!v,p),this},counterArc:function(t,n,s,h,o){return this.arc(t,n,s,h,o,!0)},counterArcTo:function(t,n,s,h,o){return this.arcTo(t,n,s,h,o,!0)},close:function(){return null!=this.penDownX&&(this.onClose(this.penX,this.penY,this.penX=this.penDownX,this.penY=this.penDownY),this.penDownX=null),this},onReset:function(){},onMove:function(t,n,s,h){},onLine:function(t,n,s,h){this.onBezierCurve(t,n,t,n,s,h,s,h)},onBezierCurve:function(t,n,s,h,o,u,p,c){var l,v,X,Y,f,M=p-t,b=c-n,T=M*M+b*b;if((f=(X=s-t)*M+(Y=h-n)*b)>T?(X-=M,Y-=b):f>0&&0!=T&&(X-=f/T*M,Y-=f/T*b),l=X*X+Y*Y,(f=(X=o-t)*M+(Y=u-n)*b)>T?(X-=M,Y-=b):f>0&&0!=T&&(X-=f/T*M,Y-=f/T*b),v=X*X+Y*Y,l<.01&&v<.01)this.onLine(t,n,p,c);else{if(isNaN(l)||isNaN(v))throw new Error('Bad input');var k=.5*(s+o),w=.5*(h+u),_=.5*(s+t),D=.5*(h+n),z=.5*(_+k),C=.5*(D+w),B=.5*(p+o),A=.5*(c+u),L=.5*(B+k),I=.5*(A+w),P=.5*(z+L),N=.5*(C+I);this.onBezierCurve(t,n,_,D,z,C,P,N),this.onBezierCurve(P,N,L,I,B,A,p,c)}},onArc:function(t,n,s,h,o,u,p,c,l,v,X,Y){var f=Y?Y*Math.PI/180:0,M=Math.cos(f),b=Math.sin(f),T=M*p,k=-b*c,w=b*p,_=M*c,D=v-l;D<0&&!X?D+=2*Math.PI:D>0&&X&&(D-=2*Math.PI);for(var z=Math.ceil(Math.abs(D/(Math.PI/2))),C=D/z,B=1.3333333333333333*Math.tan(C/4),A=Math.cos(l),L=Math.sin(l),I=0;I<z;I++){var P=A-B*L,N=L+B*A;l+=C;var q=(A=Math.cos(l))+B*(L=Math.sin(l)),y=L-B*A;this.onBezierCurve(t,n,o+T*P+k*N,u+w*P+_*N,o+T*q+k*y,u+w*q+_*y,t=o+T*A+k*L,n=u+w*A+_*L)}},onClose:function(t,n,s,h){this.onLine(t,n,s,h)}})},190,[189]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);function s(t,s,h,x,y,n){return t&&'object'==typeof t&&(s=t.yx,x=t.yy,n=t.y,h=t.xy,y=t.x,t=t.xx),this.xx=null==t?1:t,this.yx=s||0,this.xy=h||0,this.yy=null==x?1:x,this.x=(null==y?this.x:y)||0,this.y=(null==n?this.y:n)||0,this._transform(),this}m.exports=t({initialize:s,_transform:function(){},xx:1,yx:0,x:0,xy:0,yy:1,y:0,transform:function(t,s,h,x,y,n){var o=this;return t&&'object'==typeof t&&(s=t.yx,x=t.yy,n=t.y,h=t.xy,y=t.x,t=t.xx),y||(y=0),n||(n=0),this.transformTo(o.xx*t+o.xy*s,o.yx*t+o.yy*s,o.xx*h+o.xy*x,o.yx*h+o.yy*x,o.xx*y+o.xy*n+o.x,o.yx*y+o.yy*n+o.y)},transformTo:s,translate:function(t,s){return this.transform(1,0,0,1,t,s)},move:function(t,s){return this.x+=t||0,this.y+=s||0,this._transform(),this},scale:function(t,s){return null==s&&(s=t),this.transform(t,0,0,s,0,0)},rotate:function(t,s,h){null!=s&&null!=h||(s=(this.left||0)+(this.width||0)/2,h=(this.top||0)+(this.height||0)/2);var x=t*Math.PI/180,y=Math.sin(x),n=Math.cos(x);this.transform(1,0,0,1,s,h);return this.transformTo(n*this.xx-y*this.yx,y*this.xx+n*this.yx,n*this.xy-y*this.yy,y*this.xy+n*this.yy,this.x,this.y).transform(1,0,0,1,-s,-h)},moveTo:function(t,s){return this.transformTo(this.xx,this.yx,this.xy,this.yy,t,s)},rotateTo:function(t,s,h){var x=this.yx/this.xx>this.yy/this.xy?-1:1;return(this.xx<0?this.xy>=0:this.xy<0)&&(x=-x),this.rotate(t-180*Math.atan2(x*this.yx,x*this.xx)/Math.PI,s,h)},scaleTo:function(t,s){var h=Math.sqrt(this.xx*this.xx+this.yx*this.yx);return this.xx/=h,this.yx/=h,h=Math.sqrt(this.yy*this.yy+this.xy*this.xy),this.yy/=h,this.xy/=h,this.scale(t,s)},resizeTo:function(t,s){var h=this.width,x=this.height;return h&&x?this.scaleTo(t/h,s/x):this},inversePoint:function(t,s){var h=this.xx,x=this.yx,y=this.xy,n=this.yy,o=this.x,u=this.y,f=x*y-h*n;return 0==f?null:{x:(n*(o-t)+y*(s-u))/f,y:(h*(u-s)+x*(t-o))/f}},point:function(t,s){return{x:this.xx*t+this.xy*s+this.x,y:this.yx*t+this.yy*s+this.y}}})},191,[189]); -__d(function(g,r,i,a,m,e,d){'use strict';var s=r(d[0]),t={pointerEvents:!0,accessible:!0,accessibilityActions:!0,accessibilityLabel:!0,accessibilityLiveRegion:!0,accessibilityRole:!0,accessibilityStates:!0,accessibilityState:!0,accessibilityHint:!0,importantForAccessibility:!0,nativeID:!0,testID:!0,renderToHardwareTextureAndroid:!0,shouldRasterizeIOS:!0,onLayout:!0,onAccessibilityAction:!0,onAccessibilityTap:!0,onMagicTap:!0,onAccessibilityEscape:!0,collapsable:!0,needsOffscreenAlphaCompositing:!0,style:r(d[1])},c={UIView:t,RCTView:s({},t,{removeClippedSubviews:!0})};m.exports=c},192,[54,64]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);m.exports=function(n,u){var c={};return t(c,n),t(c,u),c}},193,[194]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]),c=t.checkMergeObjectArg,n=t.checkMergeIntoObjectArg;m.exports=function(t,o){if(n(t),null!=o)for(var f in c(o),o)Object.prototype.hasOwnProperty.call(o,f)&&(t[f]=o[f])}},194,[195]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=function(t){return'object'!=typeof t||t instanceof Date||null===t},c={MAX_MERGE_DEPTH:36,isTerminal:n,normalizeMergeArg:function(t){return void 0===t||null===t?{}:t},checkMergeArrayArgs:function(n,c){t(Array.isArray(n)&&Array.isArray(c),'Tried to merge arrays, instead got %s and %s.',n,c)},checkMergeObjectArgs:function(t,n){c.checkMergeObjectArg(t),c.checkMergeObjectArg(n)},checkMergeObjectArg:function(c){t(!n(c)&&!Array.isArray(c),'Tried to merge an object, instead got %s.',c)},checkMergeIntoObjectArg:function(c){t(!(n(c)&&'function'!=typeof c||Array.isArray(c)),'Tried to merge into an object, instead got %s.',c)},checkMergeLevel:function(n){t(n<36,"Maximum deep merge depth exceeded. You may be attempting to merge circular structures in an unsupported way.")},checkArrayStrategy:function(n){t(void 0===n||n in c.ArrayStrategies,"You must provide an array strategy to deep merge functions to instruct the deep merge how to resolve merging two arrays.")},ArrayStrategies:{Clobber:'Clobber',Concat:'Concat',IndexByIndex:'IndexByIndex'}};m.exports=c},195,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),o=r(d[2]),n=r(d[3]),c=r(d[4]),u=r(d[5]),l=(r(d[6]),r(d[7])),b=r(d[8]),p=r(d[9]),x=(r(d[10]),r(d[11])),F=r(d[12]),h=r(d[13]),f=(function(t){function b(){return s(this,b),n(this,c(b).apply(this,arguments))}return u(b,t),o(b,[{key:"render",value:function(){var t=this.props,s=t.accessibilityLabel,o=t.color,n=t.onPress,c=t.touchSoundDisabled,u=t.title,b=t.hasTVPreferredFocus,f=t.nextFocusDown,y=t.nextFocusForward,v=t.nextFocusLeft,w=t.nextFocusRight,L=t.nextFocusUp,P=t.disabled,S=t.testID,E=[D.button],R=[D.text];o&&R.push({color:o});var T=[];P&&(E.push(D.buttonDisabled),R.push(D.textDisabled),T.push('disabled')),h('string'==typeof u,'The title prop of a Button must be a string');var A=u,I=x;return l.createElement(I,{accessibilityLabel:s,accessibilityRole:"button",accessibilityStates:T,hasTVPreferredFocus:b,nextFocusDown:f,nextFocusForward:y,nextFocusLeft:v,nextFocusRight:w,nextFocusUp:L,testID:S,disabled:P,onPress:n,touchSoundDisabled:c},l.createElement(F,{style:E},l.createElement(p,{style:R,disabled:P},A)))}}]),b})(l.Component),D=b.create({button:{},text:t({textAlign:'center',padding:8},{color:'#007AFF',fontSize:18}),buttonDisabled:{},textDisabled:{color:'#cdcdcd'}});m.exports=f},196,[54,4,5,6,9,10,58,13,60,197,212,213,88,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),u=r(d[5]),p=r(d[6]),c=r(d[7]),h=r(d[8]),f=r(d[9]),R=r(d[10]),H=r(d[11]),v=r(d[12]),T=r(d[13]),b=r(d[14]),S=r(d[15]),y=r(d[16]),w={top:20,left:20,right:20,bottom:30},x={validAttributes:c({},R.UIView,{isHighlighted:!0,numberOfLines:!0,ellipsizeMode:!0,allowFontScaling:!0,maxFontSizeMultiplier:!0,disabled:!0,selectable:!0,selectionColor:!0,adjustsFontSizeToFit:!0,minimumFontScale:!0,textBreakStrategy:!0,onTextLayout:!0,onInlineViewLayout:!0,dataDetectorType:!0}),directEventTypes:{topTextLayout:{registrationName:'onTextLayout'},topInlineViewLayout:{registrationName:'onInlineViewLayout'}},uiViewClassName:'RCTText'},C=(function(h){function R(){var n,o;t(this,R);for(var u=arguments.length,h=new Array(u),f=0;f<u;f++)h[f]=arguments[f];return(o=s(this,(n=l(R)).call.apply(n,[this].concat(h)))).state=c({},v.Mixin.touchableGetInitialState(),{isHighlighted:!1,createResponderHandlers:o._createResponseHandlers.bind(p(p(o))),responseHandlers:null}),o}return u(R,h),o(R,[{key:"render",value:function(){var t=this.props;return P(t)&&(t=c({},t,this.state.responseHandlers,{isHighlighted:this.state.isHighlighted})),null!=t.selectionColor&&(t=c({},t,{selectionColor:y(t.selectionColor)})),f.createElement(H.Consumer,null,function(o){return o?f.createElement(V,n({},t,{ref:t.forwardedRef})):f.createElement(H.Provider,{value:!0},f.createElement(M,n({},t,{ref:t.forwardedRef})))})}},{key:"_createResponseHandlers",value:function(){var n=this;return{onStartShouldSetResponder:function(){var t=n.props.onStartShouldSetResponder,o=null!=t&&t()||P(n.props);return o&&n._attachTouchHandlers(),o},onResponderGrant:function(t,o){S(n.touchableHandleResponderGrant)(t,o),null!=n.props.onResponderGrant&&n.props.onResponderGrant.call(n,t,o)},onResponderMove:function(t){S(n.touchableHandleResponderMove)(t),null!=n.props.onResponderMove&&n.props.onResponderMove.call(n,t)},onResponderRelease:function(t){S(n.touchableHandleResponderRelease)(t),null!=n.props.onResponderRelease&&n.props.onResponderRelease.call(n,t)},onResponderTerminate:function(t){S(n.touchableHandleResponderTerminate)(t),null!=n.props.onResponderTerminate&&n.props.onResponderTerminate.call(n,t)},onResponderTerminationRequest:function(){var t=n.props.onResponderTerminationRequest;return!!S(n.touchableHandleResponderTerminationRequest)()&&(null==t||t())}}}},{key:"_attachTouchHandlers",value:function(){var n=this;if(null==this.touchableGetPressRectOffset){for(var t in v.Mixin)'function'==typeof v.Mixin[t]&&(this[t]=v.Mixin[t].bind(this));this.touchableHandleActivePressIn=function(){!n.props.suppressHighlighting&&P(n.props)&&n.setState({isHighlighted:!0})},this.touchableHandleActivePressOut=function(){!n.props.suppressHighlighting&&P(n.props)&&n.setState({isHighlighted:!1})},this.touchableHandlePress=function(t){null!=n.props.onPress&&n.props.onPress(t)},this.touchableHandleLongPress=function(t){null!=n.props.onLongPress&&n.props.onLongPress(t)},this.touchableGetPressRectOffset=function(){return null==n.props.pressRetentionOffset?w:n.props.pressRetentionOffset}}}}],[{key:"getDerivedStateFromProps",value:function(n,t){return null==t.responseHandlers&&P(n)?{responseHandlers:t.createResponderHandlers()}:null}}]),R})(f.Component);C.defaultProps={accessible:!0,allowFontScaling:!0,ellipsizeMode:'tail'},C.viewConfig=x;var P=function(n){return null!=n.onPress||null!=n.onLongPress||null!=n.onStartShouldSetResponder},M=b(x.uiViewClassName,function(){return x}),V=null==T.getViewManagerConfig('RCTVirtualText')?M:b('RCTVirtualText',function(){return{validAttributes:c({},R.UIView,{isHighlighted:!0,maxFontSizeMultiplier:!0}),uiViewClassName:'RCTVirtualText'}}),L=f.forwardRef(function(t,o){return f.createElement(C,n({},t,{forwardedRef:o}))});L.displayName='Text',L.propTypes=h,m.exports=L},197,[16,4,5,6,9,10,8,54,198,13,192,202,203,75,183,211,82]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),n=r(d[1]),t=r(d[2]),l=r(d[3]),s=t(r(d[4]));m.exports={ellipsizeMode:l.oneOf(['head','middle','tail','clip']),numberOfLines:l.number,textBreakStrategy:l.oneOf(['simple','highQuality','balanced']),onLayout:l.func,onPress:l.func,onLongPress:l.func,pressRetentionOffset:n,selectable:l.bool,selectionColor:o,suppressHighlighting:l.bool,style:s,testID:l.string,nativeID:l.string,allowFontScaling:l.bool,maxFontSizeMultiplier:l.number,accessible:l.bool,adjustsFontSizeToFit:l.bool,minimumFontScale:l.number,disabled:l.bool,dataDetectorType:l.oneOf(['phoneNumber','link','email','none','all'])}},198,[66,199,200,69,80]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t.shape({top:t.number,left:t.number,bottom:t.number,right:t.number});m.exports=n},199,[69]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]);m.exports=function(o){var c=n(o);return function(n,o,u,v){var f=n;n[o]&&((f={})[o]=t(n[o]));for(var p=arguments.length,s=new Array(p>4?p-4:0),l=4;l<p;l++)s[l-4]=arguments[l];return c.apply(void 0,[f,o,u,v].concat(s))}}},200,[201,87]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]);m.exports=function(o){function l(l,c,s,u,p){if(c[s]){var f=c[s],y=typeof f;'object'!==y&&n(!1,"Invalid "+(p||'(unknown)')+" `"+s+"` of type `"+y+"` supplied to `"+u+"`, expected `object`.");for(var v=t(c[s],o),b=arguments.length,j=new Array(b>5?b-5:0),k=5;k<b;k++)j[k-5]=arguments[k];for(var w in v){var O=o[w];O||n(!1,"Invalid props."+s+" key `"+w+"` supplied to `"+u+"`.\nBad object: "+JSON.stringify(c[s],null,' ')+'\nValid keys: '+JSON.stringify(Object.keys(o),null,' '));var J=O.apply(void 0,[f,w,u,p].concat(j));J&&n(!1,J.message+'\nBad object: '+JSON.stringify(c[s],null,' '))}}else l&&n(!1,"Required object `"+s+"` was not specified in `"+u+"`.")}function c(n,t,o,c){for(var s=arguments.length,u=new Array(s>4?s-4:0),p=4;p<s;p++)u[p-4]=arguments[p];return l.apply(void 0,[!1,n,t,o,c].concat(u))}return c.isRequired=l.bind(null,!0),c}},201,[18,193]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t.createContext(!1)},202,[13]); -__d(function(g,r,i,a,m,e,d){'use strict';var E=r(d[0]),t=r(d[1]),R=r(d[2]),_=r(d[3]),s=r(d[4]),o=(r(d[5]),r(d[6])),S=r(d[7]),n=r(d[8]),l=r(d[9]),N=(r(d[10]),r(d[11])),h=(r(d[12]),function(E){var t=E.touches,R=E.changedTouches,_=t&&t.length>0,s=R&&R.length>0;return!_&&s?R[0]:_?t[0]:E}),T=N({NOT_RESPONDER:null,RESPONDER_INACTIVE_PRESS_IN:null,RESPONDER_INACTIVE_PRESS_OUT:null,RESPONDER_ACTIVE_PRESS_IN:null,RESPONDER_ACTIVE_PRESS_OUT:null,RESPONDER_ACTIVE_LONG_PRESS_IN:null,RESPONDER_ACTIVE_LONG_PRESS_OUT:null,ERROR:null}),u={NOT_RESPONDER:!1,RESPONDER_INACTIVE_PRESS_IN:!1,RESPONDER_INACTIVE_PRESS_OUT:!1,RESPONDER_ACTIVE_PRESS_IN:!1,RESPONDER_ACTIVE_PRESS_OUT:!1,RESPONDER_ACTIVE_LONG_PRESS_IN:!1,RESPONDER_ACTIVE_LONG_PRESS_OUT:!1,ERROR:!1},P=t({},u,{RESPONDER_ACTIVE_PRESS_OUT:!0,RESPONDER_ACTIVE_PRESS_IN:!0}),O=t({},u,{RESPONDER_INACTIVE_PRESS_IN:!0,RESPONDER_ACTIVE_PRESS_IN:!0,RESPONDER_ACTIVE_LONG_PRESS_IN:!0}),D=t({},u,{RESPONDER_ACTIVE_LONG_PRESS_IN:!0}),c=N({DELAY:null,RESPONDER_GRANT:null,RESPONDER_RELEASE:null,RESPONDER_TERMINATED:null,ENTER_PRESS_RECT:null,LEAVE_PRESS_RECT:null,LONG_PRESS_DETECTED:null}),A={NOT_RESPONDER:{DELAY:T.ERROR,RESPONDER_GRANT:T.RESPONDER_INACTIVE_PRESS_IN,RESPONDER_RELEASE:T.ERROR,RESPONDER_TERMINATED:T.ERROR,ENTER_PRESS_RECT:T.ERROR,LEAVE_PRESS_RECT:T.ERROR,LONG_PRESS_DETECTED:T.ERROR},RESPONDER_INACTIVE_PRESS_IN:{DELAY:T.RESPONDER_ACTIVE_PRESS_IN,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_INACTIVE_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_INACTIVE_PRESS_OUT,LONG_PRESS_DETECTED:T.ERROR},RESPONDER_INACTIVE_PRESS_OUT:{DELAY:T.RESPONDER_ACTIVE_PRESS_OUT,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_INACTIVE_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_INACTIVE_PRESS_OUT,LONG_PRESS_DETECTED:T.ERROR},RESPONDER_ACTIVE_PRESS_IN:{DELAY:T.ERROR,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_ACTIVE_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_ACTIVE_PRESS_OUT,LONG_PRESS_DETECTED:T.RESPONDER_ACTIVE_LONG_PRESS_IN},RESPONDER_ACTIVE_PRESS_OUT:{DELAY:T.ERROR,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_ACTIVE_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_ACTIVE_PRESS_OUT,LONG_PRESS_DETECTED:T.ERROR},RESPONDER_ACTIVE_LONG_PRESS_IN:{DELAY:T.ERROR,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_ACTIVE_LONG_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_ACTIVE_LONG_PRESS_OUT,LONG_PRESS_DETECTED:T.RESPONDER_ACTIVE_LONG_PRESS_IN},RESPONDER_ACTIVE_LONG_PRESS_OUT:{DELAY:T.ERROR,RESPONDER_GRANT:T.ERROR,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.RESPONDER_ACTIVE_LONG_PRESS_IN,LEAVE_PRESS_RECT:T.RESPONDER_ACTIVE_LONG_PRESS_OUT,LONG_PRESS_DETECTED:T.ERROR},error:{DELAY:T.NOT_RESPONDER,RESPONDER_GRANT:T.RESPONDER_INACTIVE_PRESS_IN,RESPONDER_RELEASE:T.NOT_RESPONDER,RESPONDER_TERMINATED:T.NOT_RESPONDER,ENTER_PRESS_RECT:T.NOT_RESPONDER,LEAVE_PRESS_RECT:T.NOT_RESPONDER,LONG_PRESS_DETECTED:T.NOT_RESPONDER}},I={componentDidMount:function(){_.isTV&&(this._tvEventHandler=new n,this._tvEventHandler.enable(this,function(E,t){var R=o.findNodeHandle(E);t.dispatchConfig={},R===t.tag&&('focus'===t.eventType?E.touchableHandleFocus(t):'blur'===t.eventType?E.touchableHandleBlur(t):'select'===t.eventType&&E.touchableHandlePress&&!E.props.disabled&&E.touchableHandlePress(t))}))},componentWillUnmount:function(){this._tvEventHandler&&(this._tvEventHandler.disable(),delete this._tvEventHandler),this.touchableDelayTimeout&&clearTimeout(this.touchableDelayTimeout),this.longPressDelayTimeout&&clearTimeout(this.longPressDelayTimeout),this.pressOutDelayTimeout&&clearTimeout(this.pressOutDelayTimeout)},touchableGetInitialState:function(){return{touchable:{touchState:void 0,responderID:null}}},touchableHandleResponderTerminationRequest:function(){return!this.props.rejectResponderTermination},touchableHandleStartShouldSetResponder:function(){return!this.props.disabled},touchableLongPressCancelsPress:function(){return!0},touchableHandleResponderGrant:function(E){var t=E.currentTarget;E.persist(),this.pressOutDelayTimeout&&clearTimeout(this.pressOutDelayTimeout),this.pressOutDelayTimeout=null,this.state.touchable.touchState=T.NOT_RESPONDER,this.state.touchable.responderID=t,this._receiveSignal(c.RESPONDER_GRANT,E);var R=void 0!==this.touchableGetHighlightDelayMS?Math.max(this.touchableGetHighlightDelayMS(),0):130;0!==(R=isNaN(R)?130:R)?this.touchableDelayTimeout=setTimeout(this._handleDelay.bind(this,E),R):this._handleDelay(E);var _=void 0!==this.touchableGetLongPressDelayMS?Math.max(this.touchableGetLongPressDelayMS(),10):370;_=isNaN(_)?370:_,this.longPressDelayTimeout=setTimeout(this._handleLongDelay.bind(this,E),_+R)},touchableHandleResponderRelease:function(E){this.pressInLocation=null,this._receiveSignal(c.RESPONDER_RELEASE,E)},touchableHandleResponderTerminate:function(E){this.pressInLocation=null,this._receiveSignal(c.RESPONDER_TERMINATED,E)},touchableHandleResponderMove:function(E){if(this.state.touchable.positionOnActivate){var t=this.state.touchable.positionOnActivate,R=this.state.touchable.dimensionsOnActivate,_=this.touchableGetPressRectOffset?this.touchableGetPressRectOffset():{left:20,right:20,top:20,bottom:20},s=_.left,o=_.top,S=_.right,n=_.bottom,l=this.touchableGetHitSlop?this.touchableGetHitSlop():null;l&&(s+=l.left||0,o+=l.top||0,S+=l.right||0,n+=l.bottom||0);var N=h(E.nativeEvent),u=N&&N.pageX,P=N&&N.pageY;if(this.pressInLocation)this._getDistanceBetweenPoints(u,P,this.pressInLocation.pageX,this.pressInLocation.pageY)>10&&this._cancelLongPressDelayTimeout();if(u>t.left-s&&P>t.top-o&&u<t.left+R.width+S&&P<t.top+R.height+n){var O=this.state.touchable.touchState;this._receiveSignal(c.ENTER_PRESS_RECT,E),this.state.touchable.touchState===T.RESPONDER_INACTIVE_PRESS_IN&&O!==T.RESPONDER_INACTIVE_PRESS_IN&&this._cancelLongPressDelayTimeout()}else this._cancelLongPressDelayTimeout(),this._receiveSignal(c.LEAVE_PRESS_RECT,E)}},touchableHandleFocus:function(E){this.props.onFocus&&this.props.onFocus(E)},touchableHandleBlur:function(E){this.props.onBlur&&this.props.onBlur(E)},_remeasureMetricsOnActivation:function(){var E=this.state.touchable.responderID;null!=E&&l.measure(E,this._handleQueryLayout)},_handleQueryLayout:function(E,t,_,o,S,n){(E||t||_||o||S||n)&&(this.state.touchable.positionOnActivate&&s.release(this.state.touchable.positionOnActivate),this.state.touchable.dimensionsOnActivate&&R.release(this.state.touchable.dimensionsOnActivate),this.state.touchable.positionOnActivate=s.getPooled(S,n),this.state.touchable.dimensionsOnActivate=R.getPooled(_,o))},_handleDelay:function(E){this.touchableDelayTimeout=null,this._receiveSignal(c.DELAY,E)},_handleLongDelay:function(E){this.longPressDelayTimeout=null;var t=this.state.touchable.touchState;t!==T.RESPONDER_ACTIVE_PRESS_IN&&t!==T.RESPONDER_ACTIVE_LONG_PRESS_IN?console.error('Attempted to transition from state `'+t+'` to `'+T.RESPONDER_ACTIVE_LONG_PRESS_IN+"`, which is not supported. This is most likely due to `Touchable.longPressDelayTimeout` not being cancelled."):this._receiveSignal(c.LONG_PRESS_DETECTED,E)},_receiveSignal:function(E,t){var R=this.state.touchable.responderID,_=this.state.touchable.touchState,s=A[_]&&A[_][E];if(R||E!==c.RESPONDER_RELEASE){if(!s)throw new Error('Unrecognized signal `'+E+'` or state `'+_+'` for Touchable responder `'+R+'`');if(s===T.ERROR)throw new Error('Touchable cannot transition from `'+_+'` to `'+E+'` for responder `'+R+'`');_!==s&&(this._performSideEffectsForTransition(_,s,E,t),this.state.touchable.touchState=s)}},_cancelLongPressDelayTimeout:function(){this.longPressDelayTimeout&&clearTimeout(this.longPressDelayTimeout),this.longPressDelayTimeout=null},_isHighlight:function(E){return E===T.RESPONDER_ACTIVE_PRESS_IN||E===T.RESPONDER_ACTIVE_LONG_PRESS_IN},_savePressInLocation:function(E){var t=h(E.nativeEvent),R=t&&t.pageX,_=t&&t.pageY,s=t&&t.locationX,o=t&&t.locationY;this.pressInLocation={pageX:R,pageY:_,locationX:s,locationY:o}},_getDistanceBetweenPoints:function(E,t,R,_){var s=E-R,o=t-_;return Math.sqrt(s*s+o*o)},_performSideEffectsForTransition:function(E,t,R,_){var s=this._isHighlight(E),o=this._isHighlight(t);(R===c.RESPONDER_TERMINATED||R===c.RESPONDER_RELEASE)&&this._cancelLongPressDelayTimeout();var S=E===T.NOT_RESPONDER&&t===T.RESPONDER_INACTIVE_PRESS_IN,n=!P[E]&&P[t];if((S||n)&&this._remeasureMetricsOnActivation(),O[E]&&R===c.LONG_PRESS_DETECTED&&this.touchableHandleLongPress&&this.touchableHandleLongPress(_),o&&!s?this._startHighlight(_):!o&&s&&this._endHighlight(_),O[E]&&R===c.RESPONDER_RELEASE){var l=!!this.props.onLongPress,N=D[E]&&(!l||!this.touchableLongPressCancelsPress());(!D[E]||N)&&this.touchableHandlePress&&(o||s||(this._startHighlight(_),this._endHighlight(_)),this.touchableHandlePress(_))}this.touchableDelayTimeout&&clearTimeout(this.touchableDelayTimeout),this.touchableDelayTimeout=null},_playTouchSound:function(){l.playTouchSound()},_startHighlight:function(E){this._savePressInLocation(E),this.touchableHandleActivePressIn&&this.touchableHandleActivePressIn(E)},_endHighlight:function(E){var t=this;this.touchableHandleActivePressOut&&(this.touchableGetPressOutDelayMS&&this.touchableGetPressOutDelayMS()?this.pressOutDelayTimeout=setTimeout(function(){t.touchableHandleActivePressOut(E)},this.touchableGetPressOutDelayMS()):this.touchableHandleActivePressOut(E))},withoutDefaultFocusAndBlur:{}},b=(I.touchableHandleFocus,I.touchableHandleBlur,E(I,["touchableHandleFocus","touchableHandleBlur"]));I.withoutDefaultFocusAndBlur=b;var C={Mixin:I,TOUCH_TARGET_DEBUG:!1,renderDebugView:function(E){E.color,E.hitSlop;if(!C.TOUCH_TARGET_DEBUG)return null;throw Error('Touchable.TOUCH_TARGET_DEBUG should not be enabled in prod!')}};S.create({debug:{position:'absolute',borderWidth:1,borderStyle:'dashed'}});m.exports=C},203,[56,54,204,58,206,13,90,60,207,75,88,209,67]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=t.twoArgumentPooler;function n(t,o){this.width=t,this.height=o}n.prototype.destructor=function(){this.width=null,this.height=null},n.getPooledFromElement=function(t){return n.getPooled(t.offsetWidth,t.offsetHeight)},t.addPoolingTo(n,o),m.exports=n},204,[205]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=function(t){if(this.instancePool.length){var n=this.instancePool.pop();return this.call(n,t),n}return new this(t)},o=function(n){t(n instanceof this,'Trying to release an instance into a pool of a different type.'),n.destructor(),this.instancePool.length<this.poolSize&&this.instancePool.push(n)},s=n,l={addPoolingTo:function(t,n){var l=t;return l.instancePool=[],l.getPooled=n||s,l.poolSize||(l.poolSize=10),l.release=o,l},oneArgumentPooler:n,twoArgumentPooler:function(t,n){if(this.instancePool.length){var o=this.instancePool.pop();return this.call(o,t,n),o}return new this(t,n)},threeArgumentPooler:function(t,n,o){if(this.instancePool.length){var s=this.instancePool.pop();return this.call(s,t,n,o),s}return new this(t,n,o)},fourArgumentPooler:function(t,n,o,s){if(this.instancePool.length){var l=this.instancePool.pop();return this.call(l,t,n,o,s),l}return new this(t,n,o,s)}};m.exports=l},205,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=t.twoArgumentPooler;function n(t,o){this.left=t,this.top=o}n.prototype.destructor=function(){this.left=null,this.top=null},t.addPoolingTo(n,o),m.exports=n},206,[205]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),n=(r(d[2]),r(d[3]));function v(){this.__nativeTVNavigationEventListener=null,this.__nativeTVNavigationEventEmitter=null}v.prototype.enable=function(v,o){t.default&&(this.__nativeTVNavigationEventEmitter=new n(t.default),this.__nativeTVNavigationEventListener=this.__nativeTVNavigationEventEmitter.addListener('onHWKeyEvent',function(t){o&&o(v,t)}))},v.prototype.disable=function(){this.__nativeTVNavigationEventListener&&(this.__nativeTVNavigationEventListener.remove(),delete this.__nativeTVNavigationEventListener),this.__nativeTVNavigationEventEmitter&&delete this.__nativeTVNavigationEventEmitter},m.exports=v},207,[3,208,58,123]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('TVNavigationEventEmitter');e.default=u},208,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=function(t){var o,s={};for(o in t instanceof Object&&!Array.isArray(t)||n(!1),t)t.hasOwnProperty(o)&&(s[o]=o);return s}},209,[210]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=function(n){if(void 0===n)throw new Error('invariant(...): Second argument must be a string.')};m.exports=function(o,t){for(var f=arguments.length,s=new Array(f>2?f-2:0),u=2;u<f;u++)s[u-2]=arguments[u];if(n(t),!o){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var v=0;(c=new Error(t.replace(/%s/g,function(){return String(s[v++])}))).name='Invariant Violation'}throw c.framesToPop=1,c}}},210,[]); -__d(function(g,r,i,a,m,e,d){'use strict';function t(t,o){if(null!=t)return t;var n=new Error(void 0!==o?o:'Got unexpected '+t);throw n.framesToPop=1,n}m.exports=t,m.exports.default=t,Object.defineProperty(m.exports,'__esModule',{value:!0})},211,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),c=r(d[3]),u=r(d[4]),l=r(d[5]),s=r(d[6]),f=r(d[7]),p=r(d[8]),h=(function(s){function h(){return t(this,h),o(this,c(h).apply(this,arguments))}return u(h,s),n(h,[{key:"render",value:function(){return l.createElement(p,{style:[b.container,this.props.style]},l.createElement(f,{style:b.info},"TouchableNativeFeedback is not supported on this platform!"))}}]),h})(l.Component);h.SelectableBackground=function(){return{}},h.SelectableBackgroundBorderless=function(){return{}},h.Ripple=function(){return{}},h.canUseNativeForeground=function(){return!1};var b=s.create({container:{height:100,width:300,backgroundColor:'#ffbcbc',borderWidth:1,borderColor:'red',alignItems:'center',justifyContent:'center',margin:10},info:{color:'#333333',margin:20}});m.exports=h},212,[4,5,6,9,10,13,60,197,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),o=r(d[2]),n=r(d[3]),p=r(d[4]),c=r(d[5]),l=r(d[6]),u=r(d[7]),h=r(d[8]),y=r(d[9]),b=r(d[10]),f=r(d[11]),P={top:20,left:20,right:20,bottom:30},v=y({displayName:'TouchableOpacity',mixins:[u.Mixin.withoutDefaultFocusAndBlur,n],propTypes:t({},h.propTypes,{activeOpacity:l.number,hasTVPreferredFocus:l.bool,nextFocusDown:l.number,nextFocusForward:l.number,nextFocusLeft:l.number,nextFocusRight:l.number,nextFocusUp:l.number,tvParallaxProperties:l.object}),getDefaultProps:function(){return{activeOpacity:.2}},getInitialState:function(){return t({},this.touchableGetInitialState(),{anim:new s.Value(this._getChildStyleOpacityWithDefault())})},componentDidMount:function(){b(this.props)},UNSAFE_componentWillReceiveProps:function(t){b(t)},componentDidUpdate:function(t,s){this.props.disabled!==t.disabled&&this._opacityInactive(250)},setOpacityTo:function(t,n){s.timing(this.state.anim,{toValue:t,duration:n,easing:o.inOut(o.quad),useNativeDriver:!0}).start()},touchableHandleActivePressIn:function(t){'onResponderGrant'===t.dispatchConfig.registrationName?this._opacityActive(0):this._opacityActive(150),this.props.onPressIn&&this.props.onPressIn(t)},touchableHandleActivePressOut:function(t){this._opacityInactive(250),this.props.onPressOut&&this.props.onPressOut(t)},touchableHandleFocus:function(t){p.isTV&&this._opacityActive(150),this.props.onFocus&&this.props.onFocus(t)},touchableHandleBlur:function(t){p.isTV&&this._opacityInactive(250),this.props.onBlur&&this.props.onBlur(t)},touchableHandlePress:function(t){this.props.onPress&&this.props.onPress(t)},touchableHandleLongPress:function(t){this.props.onLongPress&&this.props.onLongPress(t)},touchableGetPressRectOffset:function(){return this.props.pressRetentionOffset||P},touchableGetHitSlop:function(){return this.props.hitSlop},touchableGetHighlightDelayMS:function(){return this.props.delayPressIn||0},touchableGetLongPressDelayMS:function(){return 0===this.props.delayLongPress?0:this.props.delayLongPress||500},touchableGetPressOutDelayMS:function(){return this.props.delayPressOut},_opacityActive:function(t){this.setOpacityTo(this.props.activeOpacity,t)},_opacityInactive:function(t){this.setOpacityTo(this._getChildStyleOpacityWithDefault(),t)},_getChildStyleOpacityWithDefault:function(){var t=f(this.props.style)||{};return null==t.opacity?1:t.opacity},render:function(){return c.createElement(s.View,{accessible:!1!==this.props.accessible,accessibilityLabel:this.props.accessibilityLabel,accessibilityHint:this.props.accessibilityHint,accessibilityRole:this.props.accessibilityRole,accessibilityStates:this.props.accessibilityStates,accessibilityState:this.props.accessibilityState,accessibilityActions:this.props.accessibilityActions,onAccessibilityAction:this.props.onAccessibilityAction,style:[this.props.style,{opacity:this.state.anim}],nativeID:this.props.nativeID,testID:this.props.testID,onLayout:this.props.onLayout,isTVSelectable:!0,nextFocusDown:this.props.nextFocusDown,nextFocusForward:this.props.nextFocusForward,nextFocusLeft:this.props.nextFocusLeft,nextFocusRight:this.props.nextFocusRight,nextFocusUp:this.props.nextFocusUp,hasTVPreferredFocus:this.props.hasTVPreferredFocus,tvParallaxProperties:this.props.tvParallaxProperties,hitSlop:this.props.hitSlop,focusable:!1!==this.props.focusable&&void 0!==this.props.onPress,onClick:this.touchableHandlePress,onStartShouldSetResponder:this.touchableHandleStartShouldSetResponder,onResponderTerminationRequest:this.touchableHandleResponderTerminationRequest,onResponderGrant:this.touchableHandleResponderGrant,onResponderMove:this.touchableHandleResponderMove,onResponderRelease:this.touchableHandleResponderRelease,onResponderTerminate:this.touchableHandleResponderTerminate},this.props.children,u.renderDebugView({color:'cyan',hitSlop:this.props.hitSlop}))}});m.exports=v},213,[54,214,242,278,58,13,69,203,279,280,284,87]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),u=t(r(d[2])),s=r(d[3]),l=r(d[4]),c=u.default.isTesting?s:l;m.exports=(0,n.default)({get FlatList(){return r(d[5])},get Image(){return r(d[6])},get ScrollView(){return r(d[7])},get SectionList(){return r(d[8])},get Text(){return r(d[9])},get View(){return r(d[10])}},c)},214,[3,54,58,215,225,245,266,272,273,276,277]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),u=n.AnimatedEvent,o=n.attachNativeEvent,f=r(d[2]),c=r(d[3]),l=r(d[4]),s=r(d[5]),v=r(d[6]),p=r(d[7]),E=r(d[8]),V={start:function(){},stop:function(){},reset:function(){},_startNativeLoop:function(){},_isUsingNativeDriver:function(){return!1}};m.exports={Value:v,ValueXY:p,Interpolation:c,Node:l,decay:function(t,n){return V},timing:function(n,u){var o=n;return t({},V,{start:function(t){o.setValue(u.toValue),t&&t({finished:!0})}})},spring:function(n,u){var o=n;return t({},V,{start:function(t){o.setValue(u.toValue),t&&t({finished:!0})}})},add:f.add,subtract:f.subtract,divide:f.divide,multiply:f.multiply,modulo:f.modulo,diffClamp:f.diffClamp,delay:function(t){return V},sequence:function(t){return V},parallel:function(t,n){return V},stagger:function(t,n){return V},loop:function(t){(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).iterations;return V},event:function(t,n){return null},createAnimatedComponent:E,attachNativeEvent:o,forkEvent:f.forkEvent,unforkEvent:f.unforkEvent,Event:u,__PropsOnlyForTests:s}},215,[54,216,225,218,219,231,217,236,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),v=r(d[3]),o=r(d[4]),c=r(d[5]),_=r(d[3]).shouldUseNativeDriver;function l(t,n,_){var l=[];c(_[0]&&_[0].nativeEvent,'Native driven events only support animated values contained inside `nativeEvent`.'),(function t(n,v){if(n instanceof s)n.__makeNative(),l.push({nativeEventPath:v,animatedValueTag:n.__getNativeTag()});else if('object'==typeof n)for(var o in n)t(n[o],v.concat(o))})(_[0].nativeEvent,[]);var h=o.findNodeHandle(t);return l.forEach(function(t){v.API.addAnimatedEventToView(h,n,t)}),{detach:function(){l.forEach(function(t){v.API.removeAnimatedEventFromView(h,n,t.animatedValueTag)})}}}var h=(function(){function v(n){var s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};t(this,v),this._listeners=[],this._argMapping=n,s.listener&&this.__addListener(s.listener),this._callListeners=this._callListeners.bind(this),this._attachedEvent=null,this.__isNative=_(s)}return n(v,[{key:"__addListener",value:function(t){this._listeners.push(t)}},{key:"__removeListener",value:function(t){this._listeners=this._listeners.filter(function(n){return n!==t})}},{key:"__attach",value:function(t,n){c(this.__isNative,'Only native driven events need to be attached.'),this._attachedEvent=l(t,n,this._argMapping)}},{key:"__detach",value:function(t,n){c(this.__isNative,'Only native driven events need to be detached.'),this._attachedEvent&&this._attachedEvent.detach()}},{key:"__getHandler",value:function(){var t=this;return this.__isNative?this._callListeners:function(){for(var n=arguments.length,v=new Array(n),o=0;o<n;o++)v[o]=arguments[o];var c=function t(n,v,o){if('number'==typeof v&&n instanceof s)n.setValue(v);else if('object'==typeof n)for(var c in n)t(n[c],v[c],c)};t.__isNative||t._argMapping.forEach(function(t,n){c(t,v[n])}),t._callListeners.apply(t,v)}}},{key:"_callListeners",value:function(){for(var t=arguments.length,n=new Array(t),s=0;s<t;s++)n[s]=arguments[s];this._listeners.forEach(function(t){return t.apply(void 0,n)})}},{key:"_validateMapping",value:function(){}}]),v})();m.exports={AnimatedEvent:h,attachNativeEvent:l}},216,[4,5,217,220,90,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),u=r(d[3]),_=r(d[4]),o=r(d[5]),l=r(d[6]),h=r(d[7]),f=r(d[8]),c=r(d[9]).API;function v(t){var n=new Set;!(function t(s){'function'==typeof s.update?n.add(s):s.__getChildren().forEach(t)})(t),n.forEach(function(t){return t.update()})}var p=(function(p){function k(n){var _;return t(this,k),(_=s(this,u(k).call(this)))._startingValue=_._value=n,_._offset=0,_._animation=null,_}return o(k,h),n(k,[{key:"__detach",value:function(){this.stopAnimation(),_(u(k.prototype),"__detach",this).call(this)}},{key:"__getValue",value:function(){return this._value+this._offset}},{key:"setValue",value:function(t){this._animation&&(this._animation.stop(),this._animation=null),this._updateValue(t,!this.__isNative),this.__isNative&&c.setAnimatedNodeValue(this.__getNativeTag(),t)}},{key:"setOffset",value:function(t){this._offset=t,this.__isNative&&c.setAnimatedNodeOffset(this.__getNativeTag(),t)}},{key:"flattenOffset",value:function(){this._value+=this._offset,this._offset=0,this.__isNative&&c.flattenAnimatedNodeOffset(this.__getNativeTag())}},{key:"extractOffset",value:function(){this._offset+=this._value,this._value=0,this.__isNative&&c.extractAnimatedNodeOffset(this.__getNativeTag())}},{key:"stopAnimation",value:function(t){this.stopTracking(),this._animation&&this._animation.stop(),this._animation=null,t&&t(this.__getValue())}},{key:"resetAnimation",value:function(t){this.stopAnimation(t),this._value=this._startingValue}},{key:"_onAnimatedValueUpdateReceived",value:function(t){this._updateValue(t,!1)}},{key:"interpolate",value:function(t){return new l(this,t)}},{key:"animate",value:function(t,n){var s=this,u=null;t.__isInteraction&&(u=f.createInteractionHandle());var _=this._animation;this._animation&&this._animation.stop(),this._animation=t,t.start(this._value,function(t){s._updateValue(t,!0)},function(t){s._animation=null,null!==u&&f.clearInteractionHandle(u),n&&n(t)},_,this)}},{key:"stopTracking",value:function(){this._tracking&&this._tracking.__detach(),this._tracking=null}},{key:"track",value:function(t){this.stopTracking(),this._tracking=t}},{key:"_updateValue",value:function(t,n){this._value=t,n&&v(this),_(u(k.prototype),"__callListeners",this).call(this,this.__getValue())}},{key:"__getNativeConfig",value:function(){return{type:'value',value:this._value,offset:this._offset}}}]),k})();m.exports=p},217,[4,5,6,9,47,10,218,222,223,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),u=r(d[3]),p=r(d[4]),l=r(d[5]),h=r(d[6]),f=(r(d[7]),r(d[8])),c=r(d[9]),s=r(d[10]),_=r(d[11]),v=function(t){return t};function y(t){if(t.outputRange&&'string'==typeof t.outputRange[0])return b(t);var n=t.outputRange;w('outputRange',n);var o=t.inputRange;w('inputRange',o),N(o),s(o.length===n.length,'inputRange ('+o.length+') and outputRange ('+n.length+') must have the same length');var u=t.easing||v,p='extend';void 0!==t.extrapolateLeft?p=t.extrapolateLeft:void 0!==t.extrapolate&&(p=t.extrapolate);var l='extend';return void 0!==t.extrapolateRight?l=t.extrapolateRight:void 0!==t.extrapolate&&(l=t.extrapolate),function(t){s('number'==typeof t,'Cannot interpolation an input which is not a number');var h=L(t,o);return R(t,o[h],o[h+1],n[h],n[h+1],u,p,l)}}function R(t,n,o,u,p,l,h,f){var c=t;if(c<n){if('identity'===h)return c;'clamp'===h&&(c=n)}if(c>o){if('identity'===f)return c;'clamp'===f&&(c=o)}return u===p?u:n===o?t<=n?u:p:(n===-1/0?c=-c:o===1/0?c-=n:c=(c-n)/(o-n),c=l(c),u===-1/0?c=-c:p===1/0?c+=u:c=c*(p-u)+u,c)}function x(t){var n=_(t);return null===n?t:"rgba("+((4278190080&(n=n||0))>>>24)+", "+((16711680&n)>>>16)+", "+((65280&n)>>>8)+", "+(255&n)/255+")"}var k=/[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;function b(t){var n=t.outputRange;s(n.length>=2,'Bad output range'),C(n=n.map(x));var o=n[0].match(k).map(function(){return[]});n.forEach(function(t){t.match(k).forEach(function(t,n){o[n].push(+t)})});var u,p=n[0].match(k).map(function(n,u){return y(h({},t,{outputRange:o[u]}))}),l='string'==typeof(u=n[0])&&u.startsWith('rgb');return function(t){var o=0;return n[0].replace(k,function(){var n=+p[o++](t);return l&&(n=o<4?Math.round(n):Math.round(1e3*n)/1e3),String(n)})}}function C(t){for(var n=t[0].replace(k,''),o=1;o<t.length;++o)s(n===t[o].replace(k,''),'invalid pattern '+t[0]+' and '+t[o])}function L(t,n){var o;for(o=1;o<n.length-1&&!(n[o]>=t);++o);return o-1}function N(t){s(t.length>=2,'inputRange must have at least 2 elements');for(var n=1;n<t.length;++n)s(t[n]>=t[n-1],'inputRange must be monotonically non-decreasing '+t)}function w(t,n){s(n.length>=2,t+' must have at least 2 elements'),s(2!==n.length||n[0]!==-1/0||n[1]!==1/0,t+'cannot be ]-infinity;+infinity[ '+n)}var D=(function(h){function _(n,p){var l;return t(this,_),(l=o(this,u(_).call(this)))._parent=n,l._config=p,l._interpolation=y(p),l}return l(_,f),n(_,[{key:"__makeNative",value:function(){this._parent.__makeNative(),p(u(_.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){var t=this._parent.__getValue();return s('number'==typeof t,'Cannot interpolate an input which is not a number.'),this._interpolation(t)}},{key:"interpolate",value:function(t){return new _(this,t)}},{key:"__attach",value:function(){this._parent.__addChild(this)}},{key:"__detach",value:function(){this._parent.__removeChild(this),p(u(_.prototype),"__detach",this).call(this)}},{key:"__transformDataType",value:function(t){return t.map(c.transformDataType)}},{key:"__getNativeConfig",value:function(){return{inputRange:this._config.inputRange,outputRange:this.__transformDataType(this._config.outputRange),extrapolateLeft:this._config.extrapolateLeft||this._config.extrapolate||'extend',extrapolateRight:this._config.extrapolateRight||this._config.extrapolate||'extend',type:'interpolation'}}}]),_})();D.__createInterpolation=y,m.exports=D},218,[4,5,6,9,47,10,54,219,222,220,18,67]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),_=s.API,u=r(d[3]),o=1,v=(function(){function v(){t(this,v),this._listeners={}}return n(v,[{key:"__attach",value:function(){}},{key:"__detach",value:function(){this.__isNative&&null!=this.__nativeTag&&(s.API.dropAnimatedNode(this.__nativeTag),this.__nativeTag=void 0)}},{key:"__getValue",value:function(){}},{key:"__getAnimatedValue",value:function(){return this.__getValue()}},{key:"__addChild",value:function(t){}},{key:"__removeChild",value:function(t){}},{key:"__getChildren",value:function(){return[]}}]),n(v,[{key:"__makeNative",value:function(){if(!this.__isNative)throw new Error('This node cannot be made a "native" animated node');this.hasListeners()&&this._startListeningToNativeValueUpdates()}},{key:"addListener",value:function(t){var n=String(o++);return this._listeners[n]=t,this.__isNative&&this._startListeningToNativeValueUpdates(),n}},{key:"removeListener",value:function(t){delete this._listeners[t],this.__isNative&&!this.hasListeners()&&this._stopListeningForNativeValueUpdates()}},{key:"removeAllListeners",value:function(){this._listeners={},this.__isNative&&this._stopListeningForNativeValueUpdates()}},{key:"hasListeners",value:function(){return!!Object.keys(this._listeners).length}},{key:"_startListeningToNativeValueUpdates",value:function(){var t=this;this.__nativeAnimatedValueListener&&!this.__shouldUpdateListenersForNewNativeTag||(this.__shouldUpdateListenersForNewNativeTag&&(this.__shouldUpdateListenersForNewNativeTag=!1,this._stopListeningForNativeValueUpdates()),_.startListeningToAnimatedNodeValue(this.__getNativeTag()),this.__nativeAnimatedValueListener=s.nativeEventEmitter.addListener('onAnimatedValueUpdate',function(n){n.tag===t.__getNativeTag()&&t._onAnimatedValueUpdateReceived(n.value)}))}},{key:"_onAnimatedValueUpdateReceived",value:function(t){this.__callListeners(t)}},{key:"__callListeners",value:function(t){for(var n in this._listeners)this._listeners[n]({value:t})}},{key:"_stopListeningForNativeValueUpdates",value:function(){this.__nativeAnimatedValueListener&&(this.__nativeAnimatedValueListener.remove(),this.__nativeAnimatedValueListener=null,_.stopListeningToAnimatedNodeValue(this.__getNativeTag()))}},{key:"__getNativeTag",value:function(){if(s.assertNativeAnimatedModule(),u(this.__isNative,'Attempt to get native tag from node not marked as "native"'),null==this.__nativeTag){var t=s.generateNewNodeTag();this.__nativeTag=t,s.API.createAnimatedNode(t,this.__getNativeConfig()),this.__shouldUpdateListenersForNewNativeTag=!0}return this.__nativeTag}},{key:"__getNativeConfig",value:function(){throw new Error('This JS animated node type cannot be used as native animated node')}},{key:"toJSON",value:function(){return this.__getValue()}}]),v})();m.exports=v},219,[4,5,220,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n=r(d[0]),o=n(r(d[1])),l=n(r(d[2])),u=n(r(d[3])),s=1,f=1,v=!1,c=[],N={enableQueue:function(){v=!0},disableQueue:function(){(0,u.default)(l.default,'Native animated module is not available'),v=!1;for(var t=0,n=c.length;t<n;t++){var o=c[t];l.default.connectAnimatedNodes(o[0],o[1])}c.length=0},createAnimatedNode:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.createAnimatedNode(t,n)},startListeningToAnimatedNodeValue:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.startListeningToAnimatedNodeValue(t)},stopListeningToAnimatedNodeValue:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.stopListeningToAnimatedNodeValue(t)},connectAnimatedNodes:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),v?c.push([t,n]):l.default.connectAnimatedNodes(t,n)},disconnectAnimatedNodes:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.disconnectAnimatedNodes(t,n)},startAnimatingNode:function(t,n,o,s){(0,u.default)(l.default,'Native animated module is not available'),l.default.startAnimatingNode(t,n,o,s)},stopAnimation:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.stopAnimation(t)},setAnimatedNodeValue:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.setAnimatedNodeValue(t,n)},setAnimatedNodeOffset:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.setAnimatedNodeOffset(t,n)},flattenAnimatedNodeOffset:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.flattenAnimatedNodeOffset(t)},extractAnimatedNodeOffset:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.extractAnimatedNodeOffset(t)},connectAnimatedNodeToView:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.connectAnimatedNodeToView(t,n)},disconnectAnimatedNodeFromView:function(t,n){(0,u.default)(l.default,'Native animated module is not available'),l.default.disconnectAnimatedNodeFromView(t,n)},dropAnimatedNode:function(t){(0,u.default)(l.default,'Native animated module is not available'),l.default.dropAnimatedNode(t)},addAnimatedEventToView:function(t,n,o){(0,u.default)(l.default,'Native animated module is not available'),l.default.addAnimatedEventToView(t,n,o)},removeAnimatedEventFromView:function(t,n,o){(0,u.default)(l.default,'Native animated module is not available'),l.default.removeAnimatedEventFromView(t,n,o)}},p={opacity:!0,transform:!0,borderRadius:!0,borderBottomEndRadius:!0,borderBottomLeftRadius:!0,borderBottomRightRadius:!0,borderBottomStartRadius:!0,borderTopEndRadius:!0,borderTopLeftRadius:!0,borderTopRightRadius:!0,borderTopStartRadius:!0,elevation:!0,shadowOpacity:!0,shadowRadius:!0,scaleX:!0,scaleY:!0,translateX:!0,translateY:!0},A={translateX:!0,translateY:!0,scale:!0,scaleX:!0,scaleY:!0,rotate:!0,rotateX:!0,rotateY:!0,rotateZ:!0,perspective:!0},b={inputRange:!0,outputRange:!0,extrapolate:!0,extrapolateRight:!0,extrapolateLeft:!0};var h=!1;m.exports={API:N,addWhitelistedStyleProp:function(t){p[t]=!0},addWhitelistedTransformProp:function(t){A[t]=!0},addWhitelistedInterpolationParam:function(t){b[t]=!0},validateStyles:function(t){for(var n in t)if(!p.hasOwnProperty(n))throw new Error("Style property '"+n+"' is not supported by native animated module")},validateTransform:function(t){t.forEach(function(t){if(!A.hasOwnProperty(t.property))throw new Error("Property '"+t.property+"' is not supported by native animated module")})},validateInterpolation:function(t){for(var n in t)if(!b.hasOwnProperty(n))throw new Error("Interpolation property '"+n+"' is not supported by native animated module")},generateNewNodeTag:function(){return s++},generateNewAnimationId:function(){return f++},assertNativeAnimatedModule:function(){(0,u.default)(l.default,'Native animated module is not available')},shouldUseNativeDriver:function(t){return!0!==t.useNativeDriver||l.default?t.useNativeDriver||!1:(h||(console.warn("Animated: `useNativeDriver` is not supported because the native animated module is missing. Falling back to JS-based animation. To resolve this, add `RCTAnimation` module to this app, or remove `useNativeDriver`. More info: https://github.com/facebook/react-native/issues/11094#issuecomment-263240420"),h=!0),!1)},transformDataType:function(t){return'string'!=typeof t?t:/deg$/.test(t)?(parseFloat(t)||0)*Math.PI/180:t},get nativeEventEmitter(){return t||(t=new o.default(l.default)),t}}},220,[3,123,221,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('NativeAnimatedModule');e.default=u},221,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),s=r(d[3]),h=r(d[4]),l=r(d[5]),o=r(d[6]),c=r(d[7]),v=(function(v){function u(){var _;return t(this,u),(_=n(this,s(u).call(this)))._children=[],_}return l(u,o),_(u,[{key:"__makeNative",value:function(){if(!this.__isNative){this.__isNative=!0;var t=this._children,_=Array.isArray(t),n=0;for(t=_?t:t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var l;if(_){if(n>=t.length)break;l=t[n++]}else{if((n=t.next()).done)break;l=n.value}var o=l;o.__makeNative(),c.API.connectAnimatedNodes(this.__getNativeTag(),o.__getNativeTag())}}h(s(u.prototype),"__makeNative",this).call(this)}},{key:"__addChild",value:function(t){0===this._children.length&&this.__attach(),this._children.push(t),this.__isNative&&(t.__makeNative(),c.API.connectAnimatedNodes(this.__getNativeTag(),t.__getNativeTag()))}},{key:"__removeChild",value:function(t){var _=this._children.indexOf(t);-1!==_?(this.__isNative&&t.__isNative&&c.API.disconnectAnimatedNodes(this.__getNativeTag(),t.__getNativeTag()),this._children.splice(_,1),0===this._children.length&&this.__detach()):console.warn("Trying to remove a child that doesn't exist")}},{key:"__getChildren",value:function(){return this._children}},{key:"__callListeners",value:function(t){if(h(s(u.prototype),"__callListeners",this).call(this,t),!this.__isNative){var _=this._children,n=Array.isArray(_),l=0;for(_=n?_:_["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var o;if(n){if(l>=_.length)break;o=_[l++]}else{if((l=_.next()).done)break;o=l.value}var c=o;c.__getValue&&c.__callListeners(c.__getValue())}}}}]),u})();m.exports=v},222,[4,5,6,9,47,10,219,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=r(d[2]),c=(r(d[3]),r(d[4])),s=r(d[5]),u=new t,l={Events:s({interactionStart:!0,interactionComplete:!0}),runAfterInteractions:function(n){var t=[],o=new Promise(function(o){k(),n&&t.push(n),t.push({run:o,name:'resolve '+(n&&n.name||'?')}),h.enqueueTasks(t)});return{then:o.then.bind(o),done:function(){if(o.done)return o.done.apply(o,arguments);console.warn('Tried to call done when not supported by current Promise implementation.')},cancel:function(){h.cancelTasks(t)}}},createInteractionHandle:function(){k();var n=++T;return p.add(n),n},clearInteractionHandle:function(n){c(!!n,'InteractionManager: Must provide a handle to clear.'),k(),p.delete(n),v.add(n)},addListener:u.addListener.bind(u),setDeadline:function(n){E=n}},f=new Set,p=new Set,v=new Set,h=new o({onMoreTasks:k}),w=0,T=0,E=-1;function k(){w||(w=E>0?setTimeout(I,0):setImmediate(I))}function I(){w=0;var t=f.size;p.forEach(function(n){return f.add(n)}),v.forEach(function(n){return f.delete(n)});var o=f.size;if(0!==t&&0===o?u.emit(l.Events.interactionComplete):0===t&&0!==o&&u.emit(l.Events.interactionStart),0===o)for(;h.hasTasksToProcess();)if(h.processNext(),E>0&&n.getEventLoopRunningTime()>=E){k();break}p.clear(),v.clear()}m.exports=l},223,[30,49,224,154,18,209]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),u=r(d[1]),s=r(d[2]),n=(r(d[3]),r(d[4])),o=(function(){function o(t){var s=t.onMoreTasks;u(this,o),this._onMoreTasks=s,this._queueStack=[{tasks:[],popable:!1}]}return s(o,[{key:"enqueue",value:function(t){this._getCurrentQueue().push(t)}},{key:"enqueueTasks",value:function(t){var u=this;t.forEach(function(t){return u.enqueue(t)})}},{key:"cancelTasks",value:function(u){this._queueStack=this._queueStack.map(function(s){return t({},s,{tasks:s.tasks.filter(function(t){return-1===u.indexOf(t)})})}).filter(function(t,u){return t.tasks.length>0||0===u})}},{key:"hasTasksToProcess",value:function(){return this._getCurrentQueue().length>0}},{key:"processNext",value:function(){var t=this._getCurrentQueue();if(t.length){var u=t.shift();try{u.gen?this._genPromise(u):u.run?u.run():(n('function'==typeof u,'Expected Function, SimpleTask, or PromiseTask, but got:\n'+JSON.stringify(u,null,2)),u())}catch(t){throw t.message='TaskQueue: Error with task '+(u.name||'')+': '+t.message,t}}}},{key:"_getCurrentQueue",value:function(){var t=this._queueStack.length-1,u=this._queueStack[t];return u.popable&&0===u.tasks.length&&this._queueStack.length>1?(this._queueStack.pop(),this._getCurrentQueue()):u.tasks}},{key:"_genPromise",value:function(t){var u=this;this._queueStack.push({tasks:[],popable:!1});var s=this._queueStack.length-1;t.gen().then(function(){u._queueStack[s].popable=!0,u.hasTasksToProcess()&&u._onMoreTasks()}).catch(function(u){throw u.message="TaskQueue: Error resolving Promise in task "+t.name+": "+u.message,u}).done()}}]),o})();m.exports=o},224,[54,4,5,154,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=t.AnimatedEvent,u=t.attachNativeEvent,s=r(d[2]),c=r(d[3]),f=r(d[4]),v=r(d[5]),p=r(d[6]),l=r(d[7]),h=r(d[8]),_=r(d[9]),N=r(d[10]),w=r(d[11]),E=r(d[12]),y=r(d[13]),D=r(d[14]),L=r(d[15]),A=r(d[16]),U=r(d[17]),k=function(n,t){return n&&t.onComplete?function(){t.onComplete&&t.onComplete.apply(t,arguments),n&&n.apply(void 0,arguments)}:n||t.onComplete},V=function(t,o,u){if(t instanceof y){var s=n({},o),c=n({},o);for(var f in o){var v=o[f],p=v.x,l=v.y;void 0!==p&&void 0!==l&&(s[f]=p,c[f]=l)}var h=u(t.x,s),_=u(t.y,c);return x([h,_],{stopTogether:!1})}return null},C=function t(o,u){var s=function(n,t,o){o=k(o,t);var u=n,s=t;u.stopTracking(),t.toValue instanceof h?u.track(new w(u,t.toValue,A,s,o)):u.animate(new A(s),o)};return V(o,u,t)||{start:function(n){s(o,u,n)},stop:function(){o.stopAnimation()},reset:function(){o.resetAnimation()},_startNativeLoop:function(t){var c=n({},u,{iterations:t});s(o,c)},_isUsingNativeDriver:function(){return u.useNativeDriver||!1}}},T=function(n){var t=0;return{start:function(o){0===n.length?o&&o({finished:!0}):n[t].start(function u(s){s.finished&&++t!==n.length?n[t].start(u):o&&o(s)})},stop:function(){t<n.length&&n[t].stop()},reset:function(){n.forEach(function(n,o){o<=t&&n.reset()}),t=0},_startNativeLoop:function(){throw new Error('Loops run using the native driver cannot contain Animated.sequence animations')},_isUsingNativeDriver:function(){return!1}}},x=function(n,t){var o=0,u={},s=!(t&&!1===t.stopTogether),c={start:function(t){o!==n.length?n.forEach(function(f,v){var p=function(f){if(u[v]=!0,++o===n.length)return o=0,void(t&&t(f));!f.finished&&s&&c.stop()};f?f.start(p):p({finished:!0})}):t&&t({finished:!0})},stop:function(){n.forEach(function(n,t){!u[t]&&n.stop(),u[t]=!0})},reset:function(){n.forEach(function(n,t){n.reset(),u[t]=!1,o=0})},_startNativeLoop:function(){throw new Error('Loops run using the native driver cannot contain Animated.parallel animations')},_isUsingNativeDriver:function(){return!1}};return c},q=function(n){return C(new E(0),{toValue:0,delay:n,duration:0,useNativeDriver:!1})};m.exports={Value:E,ValueXY:y,Interpolation:v,Node:h,decay:function t(o,u){var s=function(n,t,o){o=k(o,t);var u=n,s=t;u.stopTracking(),u.animate(new D(s),o)};return V(o,u,t)||{start:function(n){s(o,u,n)},stop:function(){o.stopAnimation()},reset:function(){o.resetAnimation()},_startNativeLoop:function(t){var c=n({},u,{iterations:t});s(o,c)},_isUsingNativeDriver:function(){return u.useNativeDriver||!1}}},timing:C,spring:function t(o,u){var s=function(n,t,o){o=k(o,t);var u=n,s=t;u.stopTracking(),t.toValue instanceof h?u.track(new w(u,t.toValue,L,s,o)):u.animate(new L(s),o)};return V(o,u,t)||{start:function(n){s(o,u,n)},stop:function(){o.stopAnimation()},reset:function(){o.resetAnimation()},_startNativeLoop:function(t){var c=n({},u,{iterations:t});s(o,c)},_isUsingNativeDriver:function(){return u.useNativeDriver||!1}}},add:function(n,t){return new s(n,t)},subtract:function(n,t){return new N(n,t)},divide:function(n,t){return new f(n,t)},multiply:function(n,t){return new l(n,t)},modulo:function(n,t){return new p(n,t)},diffClamp:function(n,t,o){return new c(n,t,o)},delay:q,sequence:T,parallel:x,stagger:function(n,t){return x(t.map(function(t,o){return T([q(n*o),t])}))},loop:function(n){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=t.iterations,u=void 0===o?-1:o,s=t.resetBeforeIteration,c=void 0===s||s,f=!1,v=0;return{start:function(t){n&&0!==u?n._isUsingNativeDriver()?n._startNativeLoop(u):(function o(){var s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{finished:!0};f||v===u||!1===s.finished?t&&t(s):(v++,c&&n.reset(),n.start(o))})():t&&t({finished:!0})},stop:function(){f=!0,n.stop()},reset:function(){v=0,f=!1,n.reset()},_startNativeLoop:function(){throw new Error('Loops run using the native driver cannot contain Animated.loop animations')},_isUsingNativeDriver:function(){return n._isUsingNativeDriver()}}},event:function(n,t){var u=new o(n,t);return u.__isNative?u:u.__getHandler()},createAnimatedComponent:U,attachNativeEvent:u,forkEvent:function(n,t){return n?n instanceof o?(n.__addListener(t),n):function(){'function'==typeof n&&n.apply(void 0,arguments),t.apply(void 0,arguments)}:t},unforkEvent:function(n,t){n&&n instanceof o&&n.__removeListener(t)},Event:o,__PropsOnlyForTests:_}},225,[54,216,226,227,228,218,229,230,219,231,234,235,217,236,237,239,241,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),h=r(d[3]),u=r(d[4]),s=r(d[5]),o=r(d[6]),l=(r(d[7]),r(d[8])),v=r(d[9]),c=(function(c){function f(_,u){var s;return t(this,f),(s=n(this,h(f).call(this)))._a='number'==typeof _?new l(_):_,s._b='number'==typeof u?new l(u):u,s}return s(f,v),_(f,[{key:"__makeNative",value:function(){this._a.__makeNative(),this._b.__makeNative(),u(h(f.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){return this._a.__getValue()+this._b.__getValue()}},{key:"interpolate",value:function(t){return new o(this,t)}},{key:"__attach",value:function(){this._a.__addChild(this),this._b.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),this._b.__removeChild(this),u(h(f.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'addition',input:[this._a.__getNativeTag(),this._b.__getNativeTag()]}}}]),f})();m.exports=c},226,[4,5,6,9,47,10,218,219,217,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),u=r(d[3]),h=r(d[4]),s=r(d[5]),l=r(d[6]),v=(r(d[7]),r(d[8])),c=(function(c){function o(_,h,s){var l;return t(this,o),(l=n(this,u(o).call(this)))._a=_,l._min=h,l._max=s,l._value=l._lastValue=l._a.__getValue(),l}return s(o,v),_(o,[{key:"__makeNative",value:function(){this._a.__makeNative(),h(u(o.prototype),"__makeNative",this).call(this)}},{key:"interpolate",value:function(t){return new l(this,t)}},{key:"__getValue",value:function(){var t=this._a.__getValue(),_=t-this._lastValue;return this._lastValue=t,this._value=Math.min(Math.max(this._value+_,this._min),this._max),this._value}},{key:"__attach",value:function(){this._a.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),h(u(o.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'diffclamp',input:this._a.__getNativeTag(),min:this._min,max:this._max}}}]),o})();m.exports=c},227,[4,5,6,9,47,10,218,219,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),h=r(d[3]),s=r(d[4]),o=r(d[5]),u=r(d[6]),l=(r(d[7]),r(d[8])),v=r(d[9]),c=(function(c){function f(_,s){var o;return t(this,f),(o=n(this,h(f).call(this)))._a='number'==typeof _?new l(_):_,o._b='number'==typeof s?new l(s):s,o}return o(f,v),_(f,[{key:"__makeNative",value:function(){this._a.__makeNative(),this._b.__makeNative(),s(h(f.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){var t=this._a.__getValue(),_=this._b.__getValue();return 0===_&&console.error('Detected division by zero in AnimatedDivision'),t/_}},{key:"interpolate",value:function(t){return new u(this,t)}},{key:"__attach",value:function(){this._a.__addChild(this),this._b.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),this._b.__removeChild(this),s(h(f.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'division',input:[this._a.__getNativeTag(),this._b.__getNativeTag()]}}}]),f})();m.exports=c},228,[4,5,6,9,47,10,218,219,217,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),u=r(d[1]),_=r(d[2]),s=r(d[3]),n=r(d[4]),h=r(d[5]),l=r(d[6]),o=(r(d[7]),r(d[8])),c=(function(c){function v(u,n){var h;return t(this,v),(h=_(this,s(v).call(this)))._a=u,h._modulus=n,h}return h(v,o),u(v,[{key:"__makeNative",value:function(){this._a.__makeNative(),n(s(v.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){return(this._a.__getValue()%this._modulus+this._modulus)%this._modulus}},{key:"interpolate",value:function(t){return new l(this,t)}},{key:"__attach",value:function(){this._a.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),n(s(v.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'modulus',input:this._a.__getNativeTag(),modulus:this._modulus}}}]),v})();m.exports=c},229,[4,5,6,9,47,10,218,219,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),h=r(d[3]),u=r(d[4]),s=r(d[5]),l=r(d[6]),o=(r(d[7]),r(d[8])),c=r(d[9]),v=(function(v){function f(_,u){var s;return t(this,f),(s=n(this,h(f).call(this)))._a='number'==typeof _?new o(_):_,s._b='number'==typeof u?new o(u):u,s}return s(f,c),_(f,[{key:"__makeNative",value:function(){this._a.__makeNative(),this._b.__makeNative(),u(h(f.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){return this._a.__getValue()*this._b.__getValue()}},{key:"interpolate",value:function(t){return new l(this,t)}},{key:"__attach",value:function(){this._a.__addChild(this),this._b.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),this._b.__removeChild(this),u(h(f.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'multiplication',input:[this._a.__getNativeTag(),this._b.__getNativeTag()]}}}]),f})();m.exports=v},230,[4,5,6,9,47,10,218,219,217,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),_=r(d[2]),s=r(d[3]),o=r(d[4]),c=r(d[5]),v=r(d[6]),h=r(d[7]).AnimatedEvent,l=r(d[8]),p=r(d[9]),u=r(d[10]),f=r(d[11]),N=r(d[12]),V=(function(V){function k(_,c){var v;return n(this,k),v=s(this,o(k).call(this)),_.style&&(_=t({},_,{style:new p(_.style)})),v._props=_,v._callback=c,v.__attach(),v}return v(k,l),_(k,[{key:"__getValue",value:function(){var t={};for(var n in this._props){var _=this._props[n];_ instanceof l?(!_.__isNative||_ instanceof p)&&(t[n]=_.__getValue()):t[n]=_ instanceof h?_.__getHandler():_}return t}},{key:"__getAnimatedValue",value:function(){var t={};for(var n in this._props){var _=this._props[n];_ instanceof l&&(t[n]=_.__getAnimatedValue())}return t}},{key:"__attach",value:function(){for(var t in this._props){var n=this._props[t];n instanceof l&&n.__addChild(this)}}},{key:"__detach",value:function(){for(var t in this.__isNative&&this._animatedView&&this.__disconnectAnimatedView(),this._props){var n=this._props[t];n instanceof l&&n.__removeChild(this)}c(o(k.prototype),"__detach",this).call(this)}},{key:"update",value:function(){this._callback()}},{key:"__makeNative",value:function(){if(!this.__isNative){for(var t in this.__isNative=!0,this._props){var n=this._props[t];n instanceof l&&n.__makeNative()}this._animatedView&&this.__connectAnimatedView()}}},{key:"setNativeView",value:function(t){this._animatedView!==t&&(this._animatedView=t,this.__isNative&&this.__connectAnimatedView())}},{key:"__connectAnimatedView",value:function(){N(this.__isNative,'Expected node to be marked as "native"');var t=f.findNodeHandle(this._animatedView);N(null!=t,'Unable to locate attached view in the native tree'),u.API.connectAnimatedNodeToView(this.__getNativeTag(),t)}},{key:"__disconnectAnimatedView",value:function(){N(this.__isNative,'Expected node to be marked as "native"');var t=f.findNodeHandle(this._animatedView);N(null!=t,'Unable to locate attached view in the native tree'),u.API.disconnectAnimatedNodeFromView(this.__getNativeTag(),t)}},{key:"__getNativeConfig",value:function(){var t={};for(var n in this._props){var _=this._props[n];_ instanceof l&&(_.__makeNative(),t[n]=_.__getNativeTag())}return{type:'props',props:t}}}]),k})();m.exports=V},231,[54,4,5,6,9,47,10,216,219,232,220,90,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),l=r(d[3]),_=r(d[4]),y=r(d[5]),o=r(d[6]),u=r(d[7]),v=r(d[8]),f=r(d[9]),h=r(d[10]),c=r(d[11]),k=(function(k){function A(s){var y;return n(this,A),y=l(this,_(A).call(this)),(s=c(s)||{}).transform&&(s=t({},s,{transform:new v(s.transform)})),y._style=s,y}return o(A,f),s(A,[{key:"_walkStyleAndGetValues",value:function(t){var n={};for(var s in t){var l=t[s];l instanceof u?l.__isNative||(n[s]=l.__getValue()):l&&!Array.isArray(l)&&'object'==typeof l?n[s]=this._walkStyleAndGetValues(l):n[s]=l}return n}},{key:"__getValue",value:function(){return this._walkStyleAndGetValues(this._style)}},{key:"_walkStyleAndGetAnimatedValues",value:function(t){var n={};for(var s in t){var l=t[s];l instanceof u?n[s]=l.__getAnimatedValue():l&&!Array.isArray(l)&&'object'==typeof l&&(n[s]=this._walkStyleAndGetAnimatedValues(l))}return n}},{key:"__getAnimatedValue",value:function(){return this._walkStyleAndGetAnimatedValues(this._style)}},{key:"__attach",value:function(){for(var t in this._style){var n=this._style[t];n instanceof u&&n.__addChild(this)}}},{key:"__detach",value:function(){for(var t in this._style){var n=this._style[t];n instanceof u&&n.__removeChild(this)}y(_(A.prototype),"__detach",this).call(this)}},{key:"__makeNative",value:function(){for(var t in this._style){var n=this._style[t];n instanceof u&&n.__makeNative()}y(_(A.prototype),"__makeNative",this).call(this)}},{key:"__getNativeConfig",value:function(){var t={};for(var n in this._style)if(this._style[n]instanceof u){var s=this._style[n];s.__makeNative(),t[n]=s.__getNativeTag()}return h.validateStyles(t),{type:'style',style:t}}}]),A})();m.exports=k},232,[54,4,5,6,9,47,10,219,233,222,220,87]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),f=r(d[4]),_=r(d[5]),u=r(d[6]),c=r(d[7]),v=r(d[8]),h=(function(h){function l(n){var f;return t(this,l),(f=o(this,s(l).call(this)))._transforms=n,f}return _(l,c),n(l,[{key:"__makeNative",value:function(){this._transforms.forEach(function(t){for(var n in t){var o=t[n];o instanceof u&&o.__makeNative()}}),f(s(l.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){return this._transforms.map(function(t){var n={};for(var o in t){var s=t[o];n[o]=s instanceof u?s.__getValue():s}return n})}},{key:"__getAnimatedValue",value:function(){return this._transforms.map(function(t){var n={};for(var o in t){var s=t[o];n[o]=s instanceof u?s.__getAnimatedValue():s}return n})}},{key:"__attach",value:function(){var t=this;this._transforms.forEach(function(n){for(var o in n){var s=n[o];s instanceof u&&s.__addChild(t)}})}},{key:"__detach",value:function(){var t=this;this._transforms.forEach(function(n){for(var o in n){var s=n[o];s instanceof u&&s.__removeChild(t)}}),f(s(l.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){var t=[];return this._transforms.forEach(function(n){for(var o in n){var s=n[o];s instanceof u?t.push({type:'animated',property:o,nodeTag:s.__getNativeTag()}):t.push({type:'static',property:o,value:v.transformDataType(s)})}}),v.validateTransform(t),{type:'transform',transforms:t}}}]),l})();m.exports=h},233,[4,5,6,9,47,10,219,222,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),_=r(d[1]),n=r(d[2]),h=r(d[3]),u=r(d[4]),s=r(d[5]),o=r(d[6]),l=(r(d[7]),r(d[8])),c=r(d[9]),v=(function(v){function f(_,u){var s;return t(this,f),(s=n(this,h(f).call(this)))._a='number'==typeof _?new l(_):_,s._b='number'==typeof u?new l(u):u,s}return s(f,c),_(f,[{key:"__makeNative",value:function(){this._a.__makeNative(),this._b.__makeNative(),u(h(f.prototype),"__makeNative",this).call(this)}},{key:"__getValue",value:function(){return this._a.__getValue()-this._b.__getValue()}},{key:"interpolate",value:function(t){return new o(this,t)}},{key:"__attach",value:function(){this._a.__addChild(this),this._b.__addChild(this)}},{key:"__detach",value:function(){this._a.__removeChild(this),this._b.__removeChild(this),u(h(f.prototype),"__detach",this).call(this)}},{key:"__getNativeConfig",value:function(){return{type:'subtraction',input:[this._a.__getNativeTag(),this._b.__getNativeTag()]}}}]),f})();m.exports=v},234,[4,5,6,9,47,10,218,219,217,222]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),_=r(d[2]),s=r(d[3]),o=r(d[4]),u=r(d[5]),l=r(d[6]),h=(r(d[7]),r(d[8])),v=r(d[9]),c=v.generateNewAnimationId,f=v.shouldUseNativeDriver,k=(function(v){function k(t,_,u,l,h){var v;return n(this,k),(v=s(this,o(k).call(this)))._value=t,v._parent=_,v._animationClass=u,v._animationConfig=l,v._useNativeDriver=f(l),v._callback=h,v.__attach(),v}return l(k,h),_(k,[{key:"__makeNative",value:function(){this.__isNative=!0,this._parent.__makeNative(),u(o(k.prototype),"__makeNative",this).call(this),this._value.__makeNative()}},{key:"__getValue",value:function(){return this._parent.__getValue()}},{key:"__attach",value:function(){this._parent.__addChild(this),this._useNativeDriver&&this.__makeNative()}},{key:"__detach",value:function(){this._parent.__removeChild(this),u(o(k.prototype),"__detach",this).call(this)}},{key:"update",value:function(){this._value.animate(new this._animationClass(t({},this._animationConfig,{toValue:this._animationConfig.toValue.__getValue()})),this._callback)}},{key:"__getNativeConfig",value:function(){var n=new this._animationClass(t({},this._animationConfig,{toValue:void 0})).__getNativeAnimationConfig();return{type:'tracking',animationId:c(),animationConfig:n,toValue:this._parent.__getNativeTag(),value:this._value.__getNativeTag()}}}]),k})();m.exports=k},235,[54,4,5,6,9,47,10,217,219,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n=r(d[2]),u=r(d[3]),l=r(d[4]),f=r(d[5]),o=r(d[6]),y=r(d[7]),h=1,x=(function(x){function c(s){var l;t(this,c),l=n(this,u(c).call(this));var o=s||{x:0,y:0};return'number'==typeof o.x&&'number'==typeof o.y?(l.x=new f(o.x),l.y=new f(o.y)):(y(o.x instanceof f&&o.y instanceof f,"AnimatedValueXY must be initialized with an object of numbers or AnimatedValues."),l.x=o.x,l.y=o.y),l._listeners={},l}return l(c,o),s(c,[{key:"setValue",value:function(t){this.x.setValue(t.x),this.y.setValue(t.y)}},{key:"setOffset",value:function(t){this.x.setOffset(t.x),this.y.setOffset(t.y)}},{key:"flattenOffset",value:function(){this.x.flattenOffset(),this.y.flattenOffset()}},{key:"extractOffset",value:function(){this.x.extractOffset(),this.y.extractOffset()}},{key:"__getValue",value:function(){return{x:this.x.__getValue(),y:this.y.__getValue()}}},{key:"resetAnimation",value:function(t){this.x.resetAnimation(),this.y.resetAnimation(),t&&t(this.__getValue())}},{key:"stopAnimation",value:function(t){this.x.stopAnimation(),this.y.stopAnimation(),t&&t(this.__getValue())}},{key:"addListener",value:function(t){var s=this,n=String(h++),u=function(n){n.value;t(s.__getValue())};return this._listeners[n]={x:this.x.addListener(u),y:this.y.addListener(u)},n}},{key:"removeListener",value:function(t){this.x.removeListener(this._listeners[t].x),this.y.removeListener(this._listeners[t].y),delete this._listeners[t]}},{key:"removeAllListeners",value:function(){this.x.removeAllListeners(),this.y.removeAllListeners(),this._listeners={}}},{key:"getLayout",value:function(){return{left:this.x,top:this.y}}},{key:"getTranslateTransform",value:function(){return[{translateX:this.x},{translateY:this.y}]}}]),c})();m.exports=x},236,[4,5,6,9,10,217,222,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),_=r(d[4]),h=r(d[5]),l=r(d[6]),c=r(d[7]).shouldUseNativeDriver,u=(function(u){function v(n){var _,h,l,u;return t(this,v),(u=s(this,o(v).call(this)))._deceleration=null!=(_=n.deceleration)?_:.998,u._velocity=n.velocity,u._useNativeDriver=c(n),u.__isInteraction=null!=(h=n.isInteraction)?h:!u._useNativeDriver,u.__iterations=null!=(l=n.iterations)?l:1,u}return h(v,l),n(v,[{key:"__getNativeAnimationConfig",value:function(){return{type:'decay',deceleration:this._deceleration,velocity:this._velocity,iterations:this.__iterations}}},{key:"start",value:function(t,n,s,o,_){this.__active=!0,this._lastValue=t,this._fromValue=t,this._onUpdate=n,this.__onEnd=s,this._startTime=Date.now(),this._useNativeDriver?this.__startNativeAnimation(_):this._animationFrame=requestAnimationFrame(this.onUpdate.bind(this))}},{key:"onUpdate",value:function(){var t=Date.now(),n=this._fromValue+this._velocity/(1-this._deceleration)*(1-Math.exp(-(1-this._deceleration)*(t-this._startTime)));this._onUpdate(n),Math.abs(this._lastValue-n)<.1?this.__debouncedOnEnd({finished:!0}):(this._lastValue=n,this.__active&&(this._animationFrame=requestAnimationFrame(this.onUpdate.bind(this))))}},{key:"stop",value:function(){_(o(v.prototype),"stop",this).call(this),this.__active=!1,g.cancelAnimationFrame(this._animationFrame),this.__debouncedOnEnd({finished:!1})}}]),v})();m.exports=u},237,[4,5,6,9,47,10,238,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=r(d[2]),_=(function(){function _(){n(this,_)}return t(_,[{key:"start",value:function(n,t,o,_,u){}},{key:"stop",value:function(){this.__nativeId&&o.API.stopAnimation(this.__nativeId)}},{key:"__getNativeAnimationConfig",value:function(){throw new Error('This animation type cannot be offloaded to native')}},{key:"__debouncedOnEnd",value:function(n){var t=this.__onEnd;this.__onEnd=null,t&&t(n)}},{key:"__startNativeAnimation",value:function(n){o.API.enableQueue(),n.__makeNative(),o.API.disableQueue(),this.__nativeId=o.generateNewAnimationId(),o.API.startAnimatingNode(this.__nativeId,n.__getNativeTag(),this.__getNativeAnimationConfig(),this.__debouncedOnEnd.bind(this))}}]),_})();m.exports=_},238,[4,5,220]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),n=r(d[2]),o=r(d[3]),l=r(d[4]),h=r(d[5]),_=(r(d[6]),r(d[7]),r(d[8])),u=r(d[9]),f=r(d[10]),c=r(d[11]).shouldUseNativeDriver,v=(function(v){function p(s){var l,h,_,v,y,V,T,b,M,D,P,S;if(t(this,p),(M=n(this,o(p).call(this)))._overshootClamping=null!=(l=s.overshootClamping)&&l,M._restDisplacementThreshold=null!=(h=s.restDisplacementThreshold)?h:.001,M._restSpeedThreshold=null!=(_=s.restSpeedThreshold)?_:.001,M._initialVelocity=null!=(v=s.velocity)?v:0,M._lastVelocity=null!=(y=s.velocity)?y:0,M._toValue=s.toValue,M._delay=null!=(V=s.delay)?V:0,M._useNativeDriver=c(s),M.__isInteraction=null!=(T=s.isInteraction)?T:!M._useNativeDriver,M.__iterations=null!=(b=s.iterations)?b:1,void 0!==s.stiffness||void 0!==s.damping||void 0!==s.mass)f(void 0===s.bounciness&&void 0===s.speed&&void 0===s.tension&&void 0===s.friction,'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one'),M._stiffness=null!=(D=s.stiffness)?D:100,M._damping=null!=(P=s.damping)?P:10,M._mass=null!=(S=s.mass)?S:1;else if(void 0!==s.bounciness||void 0!==s.speed){var U,A;f(void 0===s.tension&&void 0===s.friction&&void 0===s.stiffness&&void 0===s.damping&&void 0===s.mass,'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one');var C=u.fromBouncinessAndSpeed(null!=(U=s.bounciness)?U:8,null!=(A=s.speed)?A:12);M._stiffness=C.stiffness,M._damping=C.damping,M._mass=1}else{var N,k,F=u.fromOrigamiTensionAndFriction(null!=(N=s.tension)?N:40,null!=(k=s.friction)?k:7);M._stiffness=F.stiffness,M._damping=F.damping,M._mass=1}return f(M._stiffness>0,'Stiffness value must be greater than 0'),f(M._damping>0,'Damping value must be greater than 0'),f(M._mass>0,'Mass value must be greater than 0'),M}return h(p,_),s(p,[{key:"__getNativeAnimationConfig",value:function(){var t;return{type:'spring',overshootClamping:this._overshootClamping,restDisplacementThreshold:this._restDisplacementThreshold,restSpeedThreshold:this._restSpeedThreshold,stiffness:this._stiffness,damping:this._damping,mass:this._mass,initialVelocity:null!=(t=this._initialVelocity)?t:this._lastVelocity,toValue:this._toValue,iterations:this.__iterations}}},{key:"start",value:function(t,s,n,o,l){var h=this;if(this.__active=!0,this._startPosition=t,this._lastPosition=this._startPosition,this._onUpdate=s,this.__onEnd=n,this._lastTime=Date.now(),this._frameTime=0,o instanceof p){var _=o.getInternalState();this._lastPosition=_.lastPosition,this._lastVelocity=_.lastVelocity,this._initialVelocity=this._lastVelocity,this._lastTime=_.lastTime}var u=function(){h._useNativeDriver?h.__startNativeAnimation(l):h.onUpdate()};this._delay?this._timeout=setTimeout(u,this._delay):u()}},{key:"getInternalState",value:function(){return{lastPosition:this._lastPosition,lastVelocity:this._lastVelocity,lastTime:this._lastTime}}},{key:"onUpdate",value:function(){var t=Date.now();t>this._lastTime+64&&(t=this._lastTime+64);var s=(t-this._lastTime)/1e3;this._frameTime+=s;var n=this._damping,o=this._mass,l=this._stiffness,h=-this._initialVelocity,_=n/(2*Math.sqrt(l*o)),u=Math.sqrt(l/o),f=u*Math.sqrt(1-_*_),c=this._toValue-this._startPosition,v=0,p=0,y=this._frameTime;if(_<1){var V=Math.exp(-_*u*y);v=this._toValue-V*((h+_*u*c)/f*Math.sin(f*y)+c*Math.cos(f*y)),p=_*u*V*(Math.sin(f*y)*(h+_*u*c)/f+c*Math.cos(f*y))-V*(Math.cos(f*y)*(h+_*u*c)-f*c*Math.sin(f*y))}else{var T=Math.exp(-u*y);v=this._toValue-T*(c+(h+u*c)*y),p=T*(h*(y*u-1)+y*c*(u*u))}if(this._lastTime=t,this._lastPosition=v,this._lastVelocity=p,this._onUpdate(v),this.__active){var b=!1;this._overshootClamping&&0!==this._stiffness&&(b=this._startPosition<this._toValue?v>this._toValue:v<this._toValue);var M=Math.abs(p)<=this._restSpeedThreshold,D=!0;if(0!==this._stiffness&&(D=Math.abs(this._toValue-v)<=this._restDisplacementThreshold),b||M&&D)return 0!==this._stiffness&&(this._lastPosition=this._toValue,this._lastVelocity=0,this._onUpdate(this._toValue)),void this.__debouncedOnEnd({finished:!0});this._animationFrame=requestAnimationFrame(this.onUpdate.bind(this))}}},{key:"stop",value:function(){l(o(p.prototype),"stop",this).call(this),this.__active=!1,clearTimeout(this._timeout),g.cancelAnimationFrame(this._animationFrame),this.__debouncedOnEnd({finished:!1})}}]),p})();m.exports=v},239,[4,5,6,9,47,10,217,236,238,240,18,220]); -__d(function(g,r,i,a,m,e,d){'use strict';function n(n){return 3.62*(n-30)+194}function t(n){return 3*(n-8)+25}m.exports={fromOrigamiTensionAndFriction:function(o,u){return{stiffness:n(o),damping:t(u)}},fromBouncinessAndSpeed:function(o,u){function f(n,t,o){return(n-t)/(o-t)}function c(n,t,o){return t+n*(o-t)}function s(n,t,o){return n*o+(1-n)*t}function p(n){return 44e-6*Math.pow(n,3)-.006*Math.pow(n,2)+.36*n+2}function h(n){return 4.5e-7*Math.pow(n,3)-332e-6*Math.pow(n,2)+.1078*n+5.84}var w=f(o/1.7,0,20);w=c(w,0,.8);var M,v,A,_,x=c(f(u/1.7,0,20),.5,200),B=(M=w,v=(A=x)<=18?(_=A,7e-4*Math.pow(_,3)-.031*Math.pow(_,2)+.64*_+1.28):A>18&&A<=44?p(A):h(A),s(2*M-M*M,v,.01));return{stiffness:n(x),damping:t(B)}}}},240,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n=r(d[0]),s=r(d[1]),o=r(d[2]),_=r(d[3]),u=r(d[4]),h=r(d[5]),l=(r(d[6]),r(d[7]),r(d[8])),v=r(d[9]).shouldUseNativeDriver;function c(){if(!t){var n=r(d[10]);t=n.inOut(n.ease)}return t}var f=(function(t){function f(t){var s,u,h,l,p,V;return n(this,f),(V=o(this,_(f).call(this)))._toValue=t.toValue,V._easing=null!=(s=t.easing)?s:c(),V._duration=null!=(u=t.duration)?u:500,V._delay=null!=(h=t.delay)?h:0,V.__iterations=null!=(l=t.iterations)?l:1,V._useNativeDriver=v(t),V.__isInteraction=null!=(p=t.isInteraction)?p:!V._useNativeDriver,V}return h(f,l),s(f,[{key:"__getNativeAnimationConfig",value:function(){for(var t=[],n=0;n<this._duration;n+=16.666666666666668)t.push(this._easing(n/this._duration));return t.push(this._easing(1)),{type:'frames',frames:t,toValue:this._toValue,iterations:this.__iterations}}},{key:"start",value:function(t,n,s,o,_){var u=this;this.__active=!0,this._fromValue=t,this._onUpdate=n,this.__onEnd=s;var h=function(){0!==u._duration||u._useNativeDriver?(u._startTime=Date.now(),u._useNativeDriver?u.__startNativeAnimation(_):u._animationFrame=requestAnimationFrame(u.onUpdate.bind(u))):(u._onUpdate(u._toValue),u.__debouncedOnEnd({finished:!0}))};this._delay?this._timeout=setTimeout(h,this._delay):h()}},{key:"onUpdate",value:function(){var t=Date.now();if(t>=this._startTime+this._duration)return 0===this._duration?this._onUpdate(this._toValue):this._onUpdate(this._fromValue+this._easing(1)*(this._toValue-this._fromValue)),void this.__debouncedOnEnd({finished:!0});this._onUpdate(this._fromValue+this._easing((t-this._startTime)/this._duration)*(this._toValue-this._fromValue)),this.__active&&(this._animationFrame=requestAnimationFrame(this.onUpdate.bind(this)))}},{key:"stop",value:function(){u(_(f.prototype),"stop",this).call(this),this.__active=!1,clearTimeout(this._timeout),g.cancelAnimationFrame(this._animationFrame),this.__debouncedOnEnd({finished:!1})}}]),f})();m.exports=f},241,[4,5,6,9,47,10,217,236,238,220,242]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,u=r(d[0]),t=r(d[1]),o=(function(){function o(){u(this,o)}return t(o,null,[{key:"step0",value:function(n){return n>0?1:0}},{key:"step1",value:function(n){return n>=1?1:0}},{key:"linear",value:function(n){return n}},{key:"ease",value:function(u){return n||(n=o.bezier(.42,0,1,1)),n(u)}},{key:"quad",value:function(n){return n*n}},{key:"cubic",value:function(n){return n*n*n}},{key:"poly",value:function(n){return function(u){return Math.pow(u,n)}}},{key:"sin",value:function(n){return 1-Math.cos(n*Math.PI/2)}},{key:"circle",value:function(n){return 1-Math.sqrt(1-n*n)}},{key:"exp",value:function(n){return Math.pow(2,10*(n-1))}},{key:"elastic",value:function(){var n=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:1)*Math.PI;return function(u){return 1-Math.pow(Math.cos(u*Math.PI/2),3)*Math.cos(u*n)}}},{key:"back",value:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1.70158;return function(u){return u*u*((n+1)*u-n)}}},{key:"bounce",value:function(n){if(n<.36363636363636365)return 7.5625*n*n;if(n<.7272727272727273){var u=n-.5454545454545454;return 7.5625*u*u+.75}if(n<.9090909090909091){var t=n-.8181818181818182;return 7.5625*t*t+.9375}var o=n-.9545454545454546;return 7.5625*o*o+.984375}},{key:"bezier",value:function(n,u,t,o){return r(d[2])(n,u,t,o)}},{key:"in",value:function(n){return n}},{key:"out",value:function(n){return function(u){return 1-n(1-u)}}},{key:"inOut",value:function(n){return function(u){return u<.5?n(2*u)/2:1-n(2*(1-u))/2}}}]),o})();m.exports=o},242,[4,5,243]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=4,t=.001,u=1e-7,o=10,f=.1,c='function'==typeof Float32Array;function v(n,t){return 1-3*t+3*n}function s(n,t){return 3*t-6*n}function w(n){return 3*n}function l(n,t,u){return((v(t,u)*n+s(t,u))*n+w(t))*n}function y(n,t,u){return 3*v(t,u)*n*n+2*s(t,u)*n+w(t)}function b(n,t,f,c,v){var s,w,y=0,b=t,h=f;do{(s=l(w=b+(h-b)/2,c,v)-n)>0?h=w:b=w}while(Math.abs(s)>u&&++y<o);return w}function h(t,u,o,f){for(var c=u,v=0;v<n;++v){var s=y(c,o,f);if(0===s)return c;c-=(l(c,o,f)-t)/s}return c}m.exports=function(n,u,o,v){if(!(n>=0&&n<=1&&o>=0&&o<=1))throw new Error('bezier x values must be in [0, 1] range');var s=c?new Float32Array(11):new Array(11);if(n!==u||o!==v)for(var w=0;w<11;++w)s[w]=l(w*f,n,o);function A(u){for(var c=0,v=1;10!==v&&s[v]<=u;++v)c+=f;var w=c+(u-s[--v])/(s[v+1]-s[v])*f,l=y(w,n,o);return l>=t?h(u,w,n,o):0===l?w:b(u,c,c+f,n,o)}return function(t){return n===u&&o===v?t:0===t?0:1===t?1:l(A(t),u,v)}}},243,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),p=r(d[4]),c=r(d[5]),_=r(d[6]).AnimatedEvent,h=r(d[7]),l=r(d[8]),v=r(d[9]),u=r(d[10]);m.exports=function(f,N){u('function'!=typeof f||f.prototype&&f.prototype.isReactComponent,"`createAnimatedComponent` does not support stateless functional components; use a class component instead.");var k=(function(v){function u(t){var o;return n(this,u),(o=s(this,p(u).call(this,t)))._invokeAnimatedPropsCallbackOnMount=!1,o._eventDetachers=[],o._animatedPropsCallback=function(){if(null==o._component)o._invokeAnimatedPropsCallbackOnMount=!0;else if(u.__skipSetNativeProps_FOR_TESTS_ONLY||'function'!=typeof o._component.setNativeProps)o.forceUpdate();else{if(o._propsAnimated.__isNative)throw new Error("Attempting to run JS driven animation on animated node that has been moved to \"native\" earlier by starting an animation with `useNativeDriver: true`");o._component.setNativeProps(o._propsAnimated.__getAnimatedValue())}},o._setComponentRef=function(t){o._prevComponent=o._component,o._component=t},o}return c(u,v),o(u,[{key:"componentWillUnmount",value:function(){this._propsAnimated&&this._propsAnimated.__detach(),this._detachNativeEvents()}},{key:"setNativeProps",value:function(t){this._component.setNativeProps(t)}},{key:"UNSAFE_componentWillMount",value:function(){this._attachProps(this.props)}},{key:"componentDidMount",value:function(){this._invokeAnimatedPropsCallbackOnMount&&(this._invokeAnimatedPropsCallbackOnMount=!1,this._animatedPropsCallback()),this._propsAnimated.setNativeView(this._component),this._attachNativeEvents()}},{key:"_attachNativeEvents",value:function(){var t,n=this,o=(null==(t=this._component)?void 0:t.getScrollableNode)?this._component.getScrollableNode():this._component,s=function(t){var s=n.props[t];s instanceof _&&s.__isNative&&(s.__attach(o,t),n._eventDetachers.push(function(){return s.__detach(o,t)}))};for(var p in this.props)s(p)}},{key:"_detachNativeEvents",value:function(){this._eventDetachers.forEach(function(t){return t()}),this._eventDetachers=[]}},{key:"_attachProps",value:function(t){var n=this._propsAnimated;this._propsAnimated=new h(t,this._animatedPropsCallback),n&&n.__detach()}},{key:"UNSAFE_componentWillReceiveProps",value:function(t){this._attachProps(t)}},{key:"componentDidUpdate",value:function(t){this._component!==this._prevComponent&&this._propsAnimated.setNativeView(this._component),this._component===this._prevComponent&&t===this.props||(this._detachNativeEvents(),this._attachNativeEvents())}},{key:"render",value:function(){var n=this._propsAnimated.__getValue();return l.createElement(f,t({},N,n,{ref:this._setComponentRef,collapsable:!this._propsAnimated.__isNative&&n.collapsable}))}},{key:"getNode",value:function(){return this._component}}]),u})(l.Component);k.__skipSetNativeProps_FOR_TESTS_ONLY=!1;var y=f.propTypes;return k.propTypes={style:function(t,n,o){if(y)for(var s in v)y[s]||void 0===t[s]||console.warn('You are setting the style `{ '+s+": ... }` as a prop. You should nest it in a style object. E.g. `{ style: { "+s+': ... } }`')}},k}},244,[16,4,5,6,9,10,216,231,13,81,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]);m.exports=o(t,{scrollEventThrottle:1e-4})},245,[246,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),u=r(d[5]),c=r(d[6]),f=r(d[7]),p=(r(d[8]),r(d[9])),h=r(d[10]),C=r(d[11]),v=r(d[12]),y=r(d[13]),b=r(d[14]),_=f({},v.defaultProps,{numColumns:1,removeClippedSubviews:!1}),w=(function(_){function w(t){var u;return o(this,w),(u=s(this,l(w).call(this,t)))._virtualizedListPairs=[],u._captureRef=function(t){u._listRef=t},u._getItem=function(t,n){var o=u.props.numColumns;if(o>1){for(var s=[],l=0;l<o;l++){var c=t[n*o+l];null!=c&&s.push(c)}return s}return t[n]},u._getItemCount=function(t){return t?Math.ceil(t.length/u.props.numColumns):0},u._keyExtractor=function(t,n){var o=u.props,s=o.keyExtractor,l=o.numColumns;return l>1?(b(Array.isArray(t),"FlatList: Encountered internal consistency error, expected each item to consist of an array with 1-%s columns; instead, received a single item.",l),t.map(function(t,o){return s(t,n*l+o)}).join(':')):s(t,n)},u._renderer=function(){var t=u.props,o=t.ListItemComponent,s=t.renderItem,l=t.numColumns,c=t.columnWrapperStyle,f=function(t){return o?h.createElement(o,t):s?s(t):null};return n({},o?'ListItemComponent':'renderItem',function(t){if(l>1){var n=t.item,o=t.index;return b(Array.isArray(n),'Expected array of items with numColumns > 1'),h.createElement(C,{style:y.compose(I.row,c)},n.map(function(n,s){var u=f({item:n,index:o*l+s,separators:t.separators});return null!=u?h.createElement(h.Fragment,{key:s},u):null}))}return f(t)})},u._checkProps(u.props),u.props.viewabilityConfigCallbackPairs?u._virtualizedListPairs=u.props.viewabilityConfigCallbackPairs.map(function(t){return{viewabilityConfig:t.viewabilityConfig,onViewableItemsChanged:u._createOnViewableItemsChanged(t.onViewableItemsChanged)}}):u.props.onViewableItemsChanged&&u._virtualizedListPairs.push({viewabilityConfig:u.props.viewabilityConfig,onViewableItemsChanged:u._createOnViewableItemsChanged(u.props.onViewableItemsChanged)}),u}return c(w,_),u(w,[{key:"scrollToEnd",value:function(t){this._listRef&&this._listRef.scrollToEnd(t)}},{key:"scrollToIndex",value:function(t){this._listRef&&this._listRef.scrollToIndex(t)}},{key:"scrollToItem",value:function(t){this._listRef&&this._listRef.scrollToItem(t)}},{key:"scrollToOffset",value:function(t){this._listRef&&this._listRef.scrollToOffset(t)}},{key:"recordInteraction",value:function(){this._listRef&&this._listRef.recordInteraction()}},{key:"flashScrollIndicators",value:function(){this._listRef&&this._listRef.flashScrollIndicators()}},{key:"getScrollResponder",value:function(){if(this._listRef)return this._listRef.getScrollResponder()}},{key:"getScrollableNode",value:function(){if(this._listRef)return this._listRef.getScrollableNode()}},{key:"setNativeProps",value:function(t){this._listRef&&this._listRef.setNativeProps(t)}}]),u(w,[{key:"componentDidUpdate",value:function(t){b(t.numColumns===this.props.numColumns,"Changing numColumns on the fly is not supported. Change the key prop on FlatList when changing the number of columns to force a fresh render of the component."),b(t.onViewableItemsChanged===this.props.onViewableItemsChanged,'Changing onViewableItemsChanged on the fly is not supported'),b(!p(t.viewabilityConfig,this.props.viewabilityConfig),'Changing viewabilityConfig on the fly is not supported'),b(t.viewabilityConfigCallbackPairs===this.props.viewabilityConfigCallbackPairs,'Changing viewabilityConfigCallbackPairs on the fly is not supported'),this._checkProps(this.props)}},{key:"_checkProps",value:function(t){var n=t.getItem,o=t.getItemCount,s=t.horizontal,l=t.numColumns,u=t.columnWrapperStyle,c=t.onViewableItemsChanged,f=t.viewabilityConfigCallbackPairs;b(!n&&!o,'FlatList does not support custom data formats.'),l>1?b(!s,'numColumns does not support horizontal.'):b(!u,'columnWrapperStyle not supported for single column lists'),b(!(c&&f),"FlatList does not support setting both onViewableItemsChanged and viewabilityConfigCallbackPairs.")}},{key:"_pushMultiColumnViewable",value:function(t,n){var o=this.props,s=o.numColumns,l=o.keyExtractor;n.item.forEach(function(o,u){b(null!=n.index,'Missing index!');var c=n.index*s+u;t.push(f({},n,{item:o,key:l(o,c),index:c}))})}},{key:"_createOnViewableItemsChanged",value:function(t){var n=this;return function(o){var s=n.props.numColumns;if(t)if(s>1){var l=[],u=[];o.viewableItems.forEach(function(t){return n._pushMultiColumnViewable(u,t)}),o.changed.forEach(function(t){return n._pushMultiColumnViewable(l,t)}),t({viewableItems:u,changed:l})}else t(o)}}},{key:"render",value:function(){return h.createElement(v,t({},this.props,{getItem:this._getItem,getItemCount:this._getItemCount,keyExtractor:this._keyExtractor,ref:this._captureRef,viewabilityConfigCallbackPairs:this._virtualizedListPairs},this._renderer()))}}]),w})(h.PureComponent);w.defaultProps=_;var I=y.create({row:{flexDirection:'row'}});m.exports=w},246,[16,55,4,6,9,5,10,54,58,164,13,88,247,60,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),o=r(d[2]),n=r(d[3]),l=r(d[4]),h=r(d[5]),c=r(d[6]),p=r(d[7]),u=r(d[8]),f=r(d[9]),_=r(d[10]),v=r(d[11]),y=r(d[12]),C=r(d[13]),L=r(d[14]),S=r(d[15]),b=r(d[16]),M=r(d[17]),I=r(d[18]),x=(r(d[19]),r(d[20])),R=r(d[21]),k=(r(d[22]),r(d[23]).computeWindowedRenderLimits),E=!1,w='',T=(function(v){function T(t,c){var p;n(this,T),(p=l(this,h(T).call(this,t,c)))._getScrollMetrics=function(){return p._scrollMetrics},p._getOutermostParentListRef=function(){return p._isNestedWithSameOrientation()?p.context.virtualizedList.getOutermostParentListRef():u(u(p))},p._getNestedChildState=function(t){var s=p._nestedChildLists.get(t);return s&&s.state},p._registerAsNestedChild=function(t){var s=p._cellKeysToChildListKeys.get(t.cellKey)||new Set;s.add(t.key),p._cellKeysToChildListKeys.set(t.cellKey,s);var o=p._nestedChildLists.get(t.key);o&&null!==o.ref&&console.error("A VirtualizedList contains a cell which itself contains more than one VirtualizedList of the same orientation as the parent list. You must pass a unique listKey prop to each sibling list."),p._nestedChildLists.set(t.key,{ref:t.ref,state:null}),p._hasInteracted&&t.ref.recordInteraction()},p._unregisterAsNestedChild=function(t){p._nestedChildLists.set(t.key,{ref:null,state:t.state})},p._onUpdateSeparators=function(t,s){t.forEach(function(t){var o=null!=t&&p._cellRefs[t];o&&o.updateSeparatorProps(s)})},p._averageCellLength=0,p._cellKeysToChildListKeys=new Map,p._cellRefs={},p._frames={},p._footerLength=0,p._hasDataChangedSinceEndReached=!0,p._hasDoneInitialScroll=!1,p._hasInteracted=!1,p._hasMore=!1,p._hasWarned={},p._headerLength=0,p._hiPriInProgress=!1,p._highestMeasuredFrameIndex=0,p._indicesToKeys=new Map,p._nestedChildLists=new Map,p._offsetFromParentVirtualizedList=0,p._prevParentOffset=0,p._scrollMetrics={contentLength:0,dOffset:0,dt:10,offset:0,timestamp:0,velocity:0,visibleLength:0},p._scrollRef=null,p._sentEndForContentLength=0,p._totalCellLength=0,p._totalCellsMeasured=0,p._viewabilityTuples=[],p._captureScrollRef=function(t){p._scrollRef=t},p._defaultRenderScrollComponent=function(t){var o=t.onRefresh;return p._isNestedWithSameOrientation()?y.createElement(M,t):o?(R('boolean'==typeof t.refreshing,'`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `'+JSON.stringify(t.refreshing)+'`'),y.createElement(S,s({},t,{refreshControl:null==t.refreshControl?y.createElement(L,{refreshing:t.refreshing,onRefresh:o,progressViewOffset:t.progressViewOffset}):t.refreshControl}))):y.createElement(S,t)},p._onCellUnmount=function(t){var s=p._frames[t];s&&(p._frames[t]=o({},s,{inLayout:!1}))},p._onLayout=function(t){p._isNestedWithSameOrientation()?p.measureLayoutRelativeToContainingList():p._scrollMetrics.visibleLength=p._selectLength(t.nativeEvent.layout),p.props.onLayout&&p.props.onLayout(t),p._scheduleCellsToRenderUpdate(),p._maybeCallOnEndReached()},p._onLayoutEmpty=function(t){p.props.onLayout&&p.props.onLayout(t)},p._onLayoutFooter=function(t){p._footerLength=p._selectLength(t.nativeEvent.layout)},p._onLayoutHeader=function(t){p._headerLength=p._selectLength(t.nativeEvent.layout)},p._onContentSizeChange=function(t,s){t>0&&s>0&&null!=p.props.initialScrollIndex&&p.props.initialScrollIndex>0&&!p._hasDoneInitialScroll&&(p.scrollToIndex({animated:!1,index:p.props.initialScrollIndex}),p._hasDoneInitialScroll=!0),p.props.onContentSizeChange&&p.props.onContentSizeChange(t,s),p._scrollMetrics.contentLength=p._selectLength({height:s,width:t}),p._scheduleCellsToRenderUpdate(),p._maybeCallOnEndReached()},p._convertParentScrollMetrics=function(t){var s=t.offset-p._offsetFromParentVirtualizedList,o=t.visibleLength,n=s-p._scrollMetrics.offset;return{visibleLength:o,contentLength:p._scrollMetrics.contentLength,offset:s,dOffset:n}},p._onScroll=function(t){p._nestedChildLists.forEach(function(s){s.ref&&s.ref._onScroll(t)}),p.props.onScroll&&p.props.onScroll(t);var s=t.timeStamp,o=p._selectLength(t.nativeEvent.layoutMeasurement),n=p._selectLength(t.nativeEvent.contentSize),l=p._selectOffset(t.nativeEvent.contentOffset),h=l-p._scrollMetrics.offset;if(p._isNestedWithSameOrientation()){if(0===p._scrollMetrics.contentLength)return;var c=p._convertParentScrollMetrics({visibleLength:o,offset:l});o=c.visibleLength,n=c.contentLength,l=c.offset,h=c.dOffset}var u=p._scrollMetrics.timestamp?Math.max(1,s-p._scrollMetrics.timestamp):1,f=h/u;u>500&&p._scrollMetrics.dt>500&&n>5*o&&!p._hasWarned.perf&&(x("VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc.",{dt:u,prevDt:p._scrollMetrics.dt,contentLength:n}),p._hasWarned.perf=!0),p._scrollMetrics={contentLength:n,dt:u,dOffset:h,offset:l,timestamp:s,velocity:f,visibleLength:o},p._updateViewableItems(p.props.data),p.props&&(p._maybeCallOnEndReached(),0!==f&&p._fillRateHelper.activate(),p._computeBlankness(),p._scheduleCellsToRenderUpdate())},p._onScrollBeginDrag=function(t){p._nestedChildLists.forEach(function(s){s.ref&&s.ref._onScrollBeginDrag(t)}),p._viewabilityTuples.forEach(function(t){t.viewabilityHelper.recordInteraction()}),p._hasInteracted=!0,p.props.onScrollBeginDrag&&p.props.onScrollBeginDrag(t)},p._onScrollEndDrag=function(t){var s=t.nativeEvent.velocity;s&&(p._scrollMetrics.velocity=p._selectOffset(s)),p._computeBlankness(),p.props.onScrollEndDrag&&p.props.onScrollEndDrag(t)},p._onMomentumScrollEnd=function(t){p._scrollMetrics.velocity=0,p._computeBlankness(),p.props.onMomentumScrollEnd&&p.props.onMomentumScrollEnd(t)},p._updateCellsToRender=function(){var t=p.props,s=t.data,o=t.getItemCount,n=t.onEndReachedThreshold,l=p._isVirtualizationDisabled();p._updateViewableItems(s),s&&p.setState(function(t){var h;if(l){var c=p._scrollMetrics,u=c.contentLength,f=c.offset,_=c.visibleLength,v=u-_-f<n*_?p.props.maxToRenderPerBatch:0;h={first:0,last:Math.min(t.last+v,o(s)-1)}}else p._scrollMetrics.visibleLength&&(p.props.initialScrollIndex&&!p._scrollMetrics.offset||(h=k(p.props,t,p._getFrameMetricsApprox,p._scrollMetrics)));if(h&&p._nestedChildLists.size>0)for(var y=h.first,C=h.last,L=y;L<=C;L++){var S=p._indicesToKeys.get(L),b=S&&p._cellKeysToChildListKeys.get(S);if(b){var M=!1,I=b,x=Array.isArray(I),R=0;for(I=x?I:I["function"==typeof Symbol&&"function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var E;if(x){if(R>=I.length)break;E=I[R++]}else{if((R=I.next()).done)break;E=R.value}var w=E,T=p._nestedChildLists.get(w);if(T&&T.ref&&T.ref.hasMore()){M=!0;break}}if(M){h.last=L;break}}}return h})},p._createViewToken=function(t,s){var o=p.props,n=o.data,l=o.getItem,h=o.keyExtractor,c=l(n,t);return{index:t,item:c,key:h(c,t),isViewable:s}},p._getFrameMetricsApprox=function(t){var s=p._getFrameMetrics(t);if(s&&s.index===t)return s;var o=p.props.getItemLayout;return R(!o,'Should not have to estimate frames when a measurement metrics function is provided'),{length:p._averageCellLength,offset:p._averageCellLength*t}},p._getFrameMetrics=function(t){var s=p.props,o=s.data,n=s.getItem,l=s.getItemCount,h=s.getItemLayout,c=s.keyExtractor;R(l(o)>t,'Tried to get frame for out of range index '+t);var u=n(o,t),f=u&&p._frames[c(u,t)];return f&&f.index===t||h&&(f=h(o,t)),f},R(!t.onScroll||!t.onScroll.__isNative,"Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent to support native onScroll events with useNativeDriver"),R(t.windowSize>0,'VirtualizedList: The windowSize prop must be present and set to a value greater than 0.'),p._fillRateHelper=new _(p._getFrameMetrics),p._updateCellsToRenderBatcher=new f(p._updateCellsToRender,p.props.updateCellsBatchingPeriod),p.props.viewabilityConfigCallbackPairs?p._viewabilityTuples=p.props.viewabilityConfigCallbackPairs.map(function(t){return{viewabilityHelper:new I(t.viewabilityConfig),onViewableItemsChanged:t.onViewableItemsChanged}}):p.props.onViewableItemsChanged&&p._viewabilityTuples.push({viewabilityHelper:new I(p.props.viewabilityConfig),onViewableItemsChanged:p.props.onViewableItemsChanged});var v={first:p.props.initialScrollIndex||0,last:Math.min(p.props.getItemCount(p.props.data),(p.props.initialScrollIndex||0)+p.props.initialNumToRender)-1};if(p._isNestedWithSameOrientation()){var C=p.context.virtualizedList.getNestedChildState(p.props.listKey||p._getCellKey());C&&(v=C,p.state=C,p._frames=C.frames)}return p.state=v,p}return p(T,v),c(T,[{key:"scrollToEnd",value:function(t){var s=!t||t.animated,o=this.props.getItemCount(this.props.data)-1,n=this._getFrameMetricsApprox(o),l=Math.max(0,n.offset+n.length+this._footerLength-this._scrollMetrics.visibleLength);this._scrollRef.scrollTo(this.props.horizontal?{x:l,animated:s}:{y:l,animated:s})}},{key:"scrollToIndex",value:function(t){var s=this.props,o=s.data,n=s.horizontal,l=s.getItemCount,h=s.getItemLayout,c=s.onScrollToIndexFailed,p=t.animated,u=t.index,f=t.viewOffset,_=t.viewPosition;if(R(u>=0&&u<l(o),"scrollToIndex out of range: requested index "+u+" but maximum is "+(l(o)-1)),!h&&u>this._highestMeasuredFrameIndex)return R(!!c,"scrollToIndex should be used in conjunction with getItemLayout or onScrollToIndexFailed, otherwise there is no way to know the location of offscreen indices or handle failures."),void c({averageItemLength:this._averageCellLength,highestMeasuredFrameIndex:this._highestMeasuredFrameIndex,index:u});var v=this._getFrameMetricsApprox(u),y=Math.max(0,v.offset-(_||0)*(this._scrollMetrics.visibleLength-v.length))-(f||0);this._scrollRef.scrollTo(n?{x:y,animated:p}:{y:y,animated:p})}},{key:"scrollToItem",value:function(t){for(var s=t.item,n=this.props,l=n.data,h=n.getItem,c=(0,n.getItemCount)(l),p=0;p<c;p++)if(h(l,p)===s){this.scrollToIndex(o({},t,{index:p}));break}}},{key:"scrollToOffset",value:function(t){var s=t.animated,o=t.offset;this._scrollRef.scrollTo(this.props.horizontal?{x:o,animated:s}:{y:o,animated:s})}},{key:"recordInteraction",value:function(){this._nestedChildLists.forEach(function(t){t.ref&&t.ref.recordInteraction()}),this._viewabilityTuples.forEach(function(t){t.viewabilityHelper.recordInteraction()}),this._updateViewableItems(this.props.data)}},{key:"flashScrollIndicators",value:function(){this._scrollRef.flashScrollIndicators()}},{key:"getScrollResponder",value:function(){if(this._scrollRef&&this._scrollRef.getScrollResponder)return this._scrollRef.getScrollResponder()}},{key:"getScrollableNode",value:function(){return this._scrollRef&&this._scrollRef.getScrollableNode?this._scrollRef.getScrollableNode():C.findNodeHandle(this._scrollRef)}},{key:"getScrollRef",value:function(){return this._scrollRef&&this._scrollRef.getScrollRef?this._scrollRef.getScrollRef():this._scrollRef}},{key:"setNativeProps",value:function(t){this._scrollRef&&this._scrollRef.setNativeProps(t)}},{key:"getChildContext",value:function(){return{virtualizedList:{getScrollMetrics:this._getScrollMetrics,horizontal:this.props.horizontal,getOutermostParentListRef:this._getOutermostParentListRef,getNestedChildState:this._getNestedChildState,registerAsNestedChild:this._registerAsNestedChild,unregisterAsNestedChild:this._unregisterAsNestedChild}}}},{key:"_getCellKey",value:function(){return this.context.virtualizedCell&&this.context.virtualizedCell.cellKey||'rootList'}},{key:"hasMore",value:function(){return this._hasMore}}]),c(T,[{key:"componentDidMount",value:function(){this._isNestedWithSameOrientation()&&this.context.virtualizedList.registerAsNestedChild({cellKey:this._getCellKey(),key:this.props.listKey||this._getCellKey(),ref:this})}},{key:"componentWillUnmount",value:function(){this._isNestedWithSameOrientation()&&this.context.virtualizedList.unregisterAsNestedChild({key:this.props.listKey||this._getCellKey(),state:{first:this.state.first,last:this.state.last,frames:this._frames}}),this._updateViewableItems(null),this._updateCellsToRenderBatcher.dispose({abort:!0}),this._viewabilityTuples.forEach(function(t){t.viewabilityHelper.dispose()}),this._fillRateHelper.deactivateAndFlush()}},{key:"_pushCells",value:function(t,s,o,n,l,h){var c,p=this,u=this.props,f=u.CellRendererComponent,_=u.ItemSeparatorComponent,v=u.data,C=u.getItem,L=u.getItemCount,S=u.horizontal,b=u.keyExtractor,M=this.props.ListHeaderComponent?1:0,I=L(v)-1;l=Math.min(I,l);for(var x=function(n){var l=C(v,n),u=b(l,n);p._indicesToKeys.set(n,u),o.has(n+M)&&s.push(t.length),t.push(y.createElement(z,{CellRendererComponent:f,ItemSeparatorComponent:n<I?_:void 0,cellKey:u,fillRateHelper:p._fillRateHelper,horizontal:S,index:n,inversionStyle:h,item:l,key:u,prevCellKey:c,onUpdateSeparators:p._onUpdateSeparators,onLayout:function(t){return p._onCellLayout(t,u,n)},onUnmount:p._onCellUnmount,parentProps:p.props,ref:function(t){p._cellRefs[u]=t}})),c=u},R=n;R<=l;R++)x(R)}},{key:"_isVirtualizationDisabled",value:function(){return this.props.disableVirtualization||!1}},{key:"_isNestedWithSameOrientation",value:function(){var t=this.context.virtualizedList;return!(!t||!!t.horizontal!=!!this.props.horizontal)}},{key:"render",value:function(){var s=this,n=this.props,l=n.ListEmptyComponent,h=n.ListFooterComponent,c=n.ListHeaderComponent,p=this.props,u=p.data,f=p.horizontal,_=this._isVirtualizationDisabled(),v=this.props.inverted?this.props.horizontal?P.horizontallyInverted:P.verticallyInverted:null,C=[],L=new Set(this.props.stickyHeaderIndices),S=[];if(c){L.has(0)&&S.push(0);var I=y.isValidElement(c)?c:y.createElement(c,null);C.push(y.createElement(O,{cellKey:this._getCellKey()+'-header',key:"$header"},y.createElement(M,{onLayout:this._onLayoutHeader,style:b.compose(v,this.props.ListHeaderComponentStyle)},I)))}var x=this.props.getItemCount(u);if(x>0){E=!1,w='';var R=f?'width':'height',k=this.props.initialScrollIndex?-1:this.props.initialNumToRender-1,T=this.state,z=T.first,F=T.last;this._pushCells(C,S,L,0,k,v);var K=Math.max(k+1,z);if(!_&&z>k+1){var N=!1;if(L.size>0)for(var V=c?1:0,A=K-1;A>k;A--)if(L.has(A+V)){var D=this._getFrameMetricsApprox(k),B=this._getFrameMetricsApprox(A),H=B.offset-D.offset-(this.props.initialScrollIndex?0:D.length);C.push(y.createElement(M,{key:"$sticky_lead",style:t({},R,H)})),this._pushCells(C,S,L,A,A,v);var U=this._getFrameMetricsApprox(z).offset-(B.offset+B.length);C.push(y.createElement(M,{key:"$sticky_trail",style:t({},R,U)})),N=!0;break}if(!N){var W=this._getFrameMetricsApprox(k),$=this._getFrameMetricsApprox(z).offset-(W.offset+W.length);C.push(y.createElement(M,{key:"$lead_spacer",style:t({},R,$)}))}}if(this._pushCells(C,S,L,K,F,v),!this._hasWarned.keys&&E&&(console.warn("VirtualizedList: missing keys for items, make sure to specify a key or id property on each item or provide a custom keyExtractor.",w),this._hasWarned.keys=!0),!_&&F<x-1){var q=this._getFrameMetricsApprox(F),Y=this.props.getItemLayout?x-1:Math.min(x-1,this._highestMeasuredFrameIndex),j=this._getFrameMetricsApprox(Y),J=j.offset+j.length-(q.offset+q.length);C.push(y.createElement(M,{key:"$tail_spacer",style:t({},R,J)}))}}else if(l){var X=y.isValidElement(l)?l:y.createElement(l,null);C.push(y.cloneElement(X,{key:'$empty',onLayout:function(t){s._onLayoutEmpty(t),X.props.onLayout&&X.props.onLayout(t)},style:b.compose(v,X.props.style)}))}if(h){var G=y.isValidElement(h)?h:y.createElement(h,null);C.push(y.createElement(O,{cellKey:this._getCellKey()+'-footer',key:"$footer"},y.createElement(M,{onLayout:this._onLayoutFooter,style:b.compose(v,this.props.ListFooterComponentStyle)},G)))}var Q=o({},this.props,{onContentSizeChange:this._onContentSizeChange,onLayout:this._onLayout,onScroll:this._onScroll,onScrollBeginDrag:this._onScrollBeginDrag,onScrollEndDrag:this._onScrollEndDrag,onMomentumScrollEnd:this._onMomentumScrollEnd,scrollEventThrottle:this.props.scrollEventThrottle,invertStickyHeaders:void 0!==this.props.invertStickyHeaders?this.props.invertStickyHeaders:this.props.inverted,stickyHeaderIndices:S});v&&(Q.style=[v,this.props.style]),this._hasMore=this.state.last<this.props.getItemCount(this.props.data)-1;var Z=y.cloneElement((this.props.renderScrollComponent||this._defaultRenderScrollComponent)(Q),{ref:this._captureScrollRef},C);return this.props.debug?y.createElement(M,{style:P.debug},Z,this._renderDebugOverlay()):Z}},{key:"componentDidUpdate",value:function(t){var s=this.props,o=s.data,n=s.extraData;o===t.data&&n===t.extraData||(this._hasDataChangedSinceEndReached=!0,this._viewabilityTuples.forEach(function(t){t.viewabilityHelper.resetViewableIndices()}));var l=this._hiPriInProgress;this._scheduleCellsToRenderUpdate(),l&&(this._hiPriInProgress=!1)}},{key:"_computeBlankness",value:function(){this._fillRateHelper.computeBlankness(this.props,this.state,this._scrollMetrics)}},{key:"_onCellLayout",value:function(t,s,o){var n=t.nativeEvent.layout,l={offset:this._selectOffset(n),length:this._selectLength(n),index:o,inLayout:!0},h=this._frames[s];h&&l.offset===h.offset&&l.length===h.length&&o===h.index?this._frames[s].inLayout=!0:(this._totalCellLength+=l.length-(h?h.length:0),this._totalCellsMeasured+=h?0:1,this._averageCellLength=this._totalCellLength/this._totalCellsMeasured,this._frames[s]=l,this._highestMeasuredFrameIndex=Math.max(this._highestMeasuredFrameIndex,o),this._scheduleCellsToRenderUpdate());var c=this._cellKeysToChildListKeys.get(s);if(c){var p=c,u=Array.isArray(p),f=0;for(p=u?p:p["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var _;if(u){if(f>=p.length)break;_=p[f++]}else{if((f=p.next()).done)break;_=f.value}var v=_,y=this._nestedChildLists.get(v);y&&y.ref&&y.ref.measureLayoutRelativeToContainingList()}}this._computeBlankness(),this._updateViewableItems(this.props.data)}},{key:"measureLayoutRelativeToContainingList",value:function(){var t=this;try{if(!this._scrollRef)return;this._scrollRef.measureLayout(this.context.virtualizedList.getOutermostParentListRef().getScrollRef().getNativeScrollRef(),function(s,o,n,l){t._offsetFromParentVirtualizedList=t._selectOffset({x:s,y:o}),t._scrollMetrics.contentLength=t._selectLength({width:n,height:l});var h=t._convertParentScrollMetrics(t.context.virtualizedList.getScrollMetrics());t._scrollMetrics.visibleLength=h.visibleLength,t._scrollMetrics.offset=h.offset},function(t){console.warn("VirtualizedList: Encountered an error while measuring a list's offset from its containing VirtualizedList.")})}catch(t){console.warn('measureLayoutRelativeToContainingList threw an error',t.stack)}}},{key:"_renderDebugOverlay",value:function(){for(var t=this._scrollMetrics.visibleLength/(this._scrollMetrics.contentLength||1),s=[],o=this.props.getItemCount(this.props.data),n=0;n<o;n++){var l=this._getFrameMetricsApprox(n);l.inLayout&&s.push(l)}var h=this._getFrameMetricsApprox(this.state.first).offset,c=this._getFrameMetricsApprox(this.state.last),p=c.offset+c.length-h,u=this._scrollMetrics.offset,f=this._scrollMetrics.visibleLength;return y.createElement(M,{style:[P.debugOverlayBase,P.debugOverlay]},s.map(function(s,o){return y.createElement(M,{key:'f'+o,style:[P.debugOverlayBase,P.debugOverlayFrame,{top:s.offset*t,height:s.length*t}]})}),y.createElement(M,{style:[P.debugOverlayBase,P.debugOverlayFrameLast,{top:h*t,height:p*t}]}),y.createElement(M,{style:[P.debugOverlayBase,P.debugOverlayFrameVis,{top:u*t,height:f*t}]}))}},{key:"_selectLength",value:function(t){return this.props.horizontal?t.width:t.height}},{key:"_selectOffset",value:function(t){return this.props.horizontal?t.x:t.y}},{key:"_maybeCallOnEndReached",value:function(){var t=this.props,s=t.data,o=t.getItemCount,n=t.onEndReached,l=t.onEndReachedThreshold,h=this._scrollMetrics,c=h.contentLength,p=h.visibleLength,u=c-p-h.offset;n&&this.state.last===o(s)-1&&u<l*p&&(this._hasDataChangedSinceEndReached||this._scrollMetrics.contentLength!==this._sentEndForContentLength)&&(this._hasDataChangedSinceEndReached=!1,this._sentEndForContentLength=this._scrollMetrics.contentLength,n({distanceFromEnd:u}))}},{key:"_scheduleCellsToRenderUpdate",value:function(){var t=this.state,s=t.first,o=t.last,n=this._scrollMetrics,l=n.offset,h=n.visibleLength,c=n.velocity,p=this.props.getItemCount(this.props.data),u=!1,f=this.props.onEndReachedThreshold*h/2;if(s>0){var _=l-this._getFrameMetricsApprox(s).offset;u=u||_<0||c<-2&&_<f}if(o<p-1){var v=this._getFrameMetricsApprox(o).offset-(l+h);u=u||v<0||c>2&&v<f}if(u&&(this._averageCellLength||this.props.getItemLayout)&&!this._hiPriInProgress)return this._hiPriInProgress=!0,this._updateCellsToRenderBatcher.dispose({abort:!0}),void this._updateCellsToRender();this._updateCellsToRenderBatcher.schedule()}},{key:"_updateViewableItems",value:function(t){var s=this,o=this.props.getItemCount;this._viewabilityTuples.forEach(function(n){n.viewabilityHelper.onUpdate(o(t),s._scrollMetrics.offset,s._scrollMetrics.visibleLength,s._getFrameMetrics,s._createViewToken,n.onViewableItemsChanged,s.state)})}}],[{key:"getDerivedStateFromProps",value:function(t,s){var o=t.data,n=t.getItemCount,l=t.maxToRenderPerBatch;return{first:Math.max(0,Math.min(s.first,n(o)-1-l)),last:Math.max(0,Math.min(s.last,n(o)-1))}}}]),T})(y.PureComponent);T.defaultProps={disableVirtualization:!1,horizontal:!1,initialNumToRender:10,keyExtractor:function(t,s){return null!=t.key?t.key:null!=t.id?t.id:(E=!0,t.type&&t.type.displayName&&(w=t.type.displayName),String(s))},maxToRenderPerBatch:10,onEndReachedThreshold:2,scrollEventThrottle:50,updateCellsBatchingPeriod:50,windowSize:21},T.contextTypes={virtualizedCell:v.shape({cellKey:v.string}),virtualizedList:v.shape({getScrollMetrics:v.func,horizontal:v.bool,getOutermostParentListRef:v.func,getNestedChildState:v.func,registerAsNestedChild:v.func,unregisterAsNestedChild:v.func})},T.childContextTypes={virtualizedList:v.shape({getScrollMetrics:v.func,horizontal:v.bool,getOutermostParentListRef:v.func,getNestedChildState:v.func,registerAsNestedChild:v.func,unregisterAsNestedChild:v.func})};var z=(function(t){function u(){var t,s;n(this,u);for(var o=arguments.length,c=new Array(o),p=0;p<o;p++)c[p]=arguments[p];return(s=l(this,(t=h(u)).call.apply(t,[this].concat(c)))).state={separatorProps:{highlighted:!1,leadingItem:s.props.item}},s._separators={highlight:function(){var t=s.props,o=t.cellKey,n=t.prevCellKey;s.props.onUpdateSeparators([o,n],{highlighted:!0})},unhighlight:function(){var t=s.props,o=t.cellKey,n=t.prevCellKey;s.props.onUpdateSeparators([o,n],{highlighted:!1})},updateProps:function(t,o){var n=s.props,l=n.cellKey,h=n.prevCellKey;s.props.onUpdateSeparators(['leading'===t?h:l],o)}},s}return p(u,t),c(u,[{key:"getChildContext",value:function(){return{virtualizedCell:{cellKey:this.props.cellKey}}}},{key:"updateSeparatorProps",value:function(t){this.setState(function(s){return{separatorProps:o({},s.separatorProps,t)}})}},{key:"componentWillUnmount",value:function(){this.props.onUnmount(this.props.cellKey)}},{key:"_renderElement",value:function(t,s,o,n){return t&&s&&console.warn("VirtualizedList: Both ListItemComponent and renderItem props are present. ListItemComponent will take precedence over renderItem."),s?y.createElement(s,{item:o,index:n,separators:this._separators}):t?t({item:o,index:n,separators:this._separators}):void R(!1,'VirtualizedList: Either ListItemComponent or renderItem props are required but none were found.')}},{key:"render",value:function(){var t=this.props,o=t.CellRendererComponent,n=t.ItemSeparatorComponent,l=t.fillRateHelper,h=t.horizontal,c=t.item,p=t.index,u=t.inversionStyle,f=t.parentProps,_=f.renderItem,v=f.getItemLayout,C=f.ListItemComponent,L=this._renderElement(_,C,c,p),S=!v||f.debug||l.enabled()?this.props.onLayout:void 0,b=n&&y.createElement(n,this.state.separatorProps),I=u?h?[P.rowReverse,u]:[P.columnReverse,u]:h?[P.row,u]:u;return o?y.createElement(o,s({},this.props,{style:I,onLayout:S}),L,b):y.createElement(M,{style:I,onLayout:S},L,b)}}],[{key:"getDerivedStateFromProps",value:function(t,s){return{separatorProps:o({},s.separatorProps,{leadingItem:t.item})}}}]),u})(y.Component);z.childContextTypes={virtualizedCell:v.shape({cellKey:v.string})};var O=(function(t){function s(){return n(this,s),l(this,h(s).apply(this,arguments))}return p(s,t),c(s,[{key:"getChildContext",value:function(){return{virtualizedCell:{cellKey:this.props.cellKey}}}},{key:"render",value:function(){return this.props.children}}]),s})(y.Component);O.childContextTypes={virtualizedCell:v.shape({cellKey:v.string})};var P=b.create({verticallyInverted:{transform:[{scaleY:-1}]},horizontallyInverted:{transform:[{scaleX:-1}]},row:{flexDirection:'row'},rowReverse:{flexDirection:'row-reverse'},columnReverse:{flexDirection:'column-reverse'},debug:{flex:1},debugOverlayBase:{position:'absolute',top:0,right:0},debugOverlay:{bottom:0,width:20,borderColor:'blue',borderWidth:1},debugOverlayFrame:{left:0,backgroundColor:'orange'},debugOverlayFrameLast:{left:0,borderColor:'green',borderWidth:2},debugOverlayFrameVis:{left:0,borderColor:'red',borderWidth:2}});m.exports=T},247,[55,16,54,4,6,9,5,10,8,248,249,69,13,90,250,253,60,88,264,87,154,18,20,265]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),l=r(d[2]),s=(function(){function s(n,l){t(this,s),this._delay=l,this._callback=n}return n(s,[{key:"dispose",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{abort:!1};this._taskHandle&&(this._taskHandle.cancel(),t.abort||this._callback(),this._taskHandle=null)}},{key:"schedule",value:function(){var t=this;if(!this._taskHandle){var n=setTimeout(function(){t._taskHandle=l.runAfterInteractions(function(){t._taskHandle=null,t._callback()})},this._delay);this._taskHandle={cancel:function(){return clearTimeout(n)}}}}}]),s})();m.exports=s},248,[4,5,223]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),l=r(d[3]),_=r(d[4]),h=function t(){s(this,t),this.any_blank_count=0,this.any_blank_ms=0,this.any_blank_speed_sum=0,this.mostly_blank_count=0,this.mostly_blank_ms=0,this.pixels_blank=0,this.pixels_sampled=0,this.pixels_scrolled=0,this.total_time_spent=0,this.sample_count=0},o=[],u=10,f=null,c=(function(){function c(t){s(this,c),this._anyBlankStartTime=null,this._enabled=!1,this._info=new h,this._mostlyBlankStartTime=null,this._samplesStartTime=null,this._getFrameMetrics=t,this._enabled=(f||0)>Math.random(),this._resetData()}return n(c,null,[{key:"addListener",value:function(t){return _(null!==f,'Call `FillRateHelper.setSampleRate` before `addListener`.'),o.push(t),{remove:function(){o=o.filter(function(n){return t!==n})}}}},{key:"setSampleRate",value:function(t){f=t}},{key:"setMinSampleCount",value:function(t){u=t}}]),n(c,[{key:"activate",value:function(){this._enabled&&null==this._samplesStartTime&&(this._samplesStartTime=l())}},{key:"deactivateAndFlush",value:function(){if(this._enabled){var n=this._samplesStartTime;if(null!=n)if(this._info.sample_count<u)this._resetData();else{var s=l()-n,_=t({},this._info,{total_time_spent:s});o.forEach(function(t){return t(_)}),this._resetData()}}}},{key:"computeBlankness",value:function(t,n,s){if(!this._enabled||0===t.getItemCount(t.data)||null==this._samplesStartTime)return 0;var _=s.dOffset,h=s.offset,o=s.velocity,u=s.visibleLength;this._info.sample_count++,this._info.pixels_sampled+=Math.round(u),this._info.pixels_scrolled+=Math.round(Math.abs(_));var f=Math.round(1e3*Math.abs(o)),c=l();null!=this._anyBlankStartTime&&(this._info.any_blank_ms+=c-this._anyBlankStartTime),this._anyBlankStartTime=null,null!=this._mostlyBlankStartTime&&(this._info.mostly_blank_ms+=c-this._mostlyBlankStartTime),this._mostlyBlankStartTime=null;for(var k=0,y=n.first,p=this._getFrameMetrics(y);y<=n.last&&(!p||!p.inLayout);)p=this._getFrameMetrics(y),y++;p&&y>0&&(k=Math.min(u,Math.max(0,p.offset-h)));for(var b=0,v=n.last,S=this._getFrameMetrics(v);v>=n.first&&(!S||!S.inLayout);)S=this._getFrameMetrics(v),v--;if(S&&v<t.getItemCount(t.data)-1){var M=S.offset+S.length;b=Math.min(u,Math.max(0,h+u-M))}var T=Math.round(k+b),B=T/u;return B>0?(this._anyBlankStartTime=c,this._info.any_blank_speed_sum+=f,this._info.any_blank_count++,this._info.pixels_blank+=T,B>.5&&(this._mostlyBlankStartTime=c,this._info.mostly_blank_count++)):(f<.01||Math.abs(_)<1)&&this.deactivateAndFlush(),B}},{key:"enabled",value:function(){return this._enabled}},{key:"_resetData",value:function(){this._anyBlankStartTime=null,this._info=new h,this._mostlyBlankStartTime=null,this._samplesStartTime=null}}]),c})();m.exports=c},249,[54,5,4,111,20]); -__d(function(g,r,i,a,m,e,d){'use strict';var s,t=r(d[0]),n=t(r(d[1])),o=t(r(d[2])),f=t(r(d[3])),h=t(r(d[4])),p=t(r(d[5])),l=t(r(d[6])),u=t(r(d[7])),c=(t(r(d[8])),t(r(d[9]))),v=(r(d[10]),r(d[11]));r(d[12]);s={SIZE:{}};var R=(function(s){function t(){var s,n;(0,f.default)(this,t);for(var o=arguments.length,h=new Array(o),u=0;u<o;u++)h[u]=arguments[u];return(n=(0,p.default)(this,(s=(0,l.default)(t)).call.apply(s,[this].concat(h))))._lastNativeRefreshing=!1,n._onRefresh=function(){n._lastNativeRefreshing=!0,n.props.onRefresh&&n.props.onRefresh(),n.forceUpdate()},n}return(0,u.default)(t,s),(0,h.default)(t,[{key:"componentDidMount",value:function(){this._lastNativeRefreshing=this.props.refreshing}},{key:"componentDidUpdate",value:function(s){this.props.refreshing!==s.refreshing?this._lastNativeRefreshing=this.props.refreshing:this.props.refreshing!==this._lastNativeRefreshing&&this._setNativePropsOnRef&&(this._setNativePropsOnRef({refreshing:this.props.refreshing}),this._lastNativeRefreshing=this.props.refreshing)}},{key:"render",value:function(){var s=this,t=this.props,f=(t.enabled,t.colors,t.progressBackgroundColor,t.size,t.progressViewOffset,(0,o.default)(t,["enabled","colors","progressBackgroundColor","size","progressViewOffset"]));return v.createElement(c.default,(0,n.default)({},f,{ref:function(t){return s._setNativePropsOnRef=t?t.setNativeProps.bind(t):null},onRefresh:this._onRefresh}))}}]),t})(v.Component);R.SIZE=s.SIZE,m.exports=R},250,[3,16,56,4,5,6,9,10,251,252,58,13,211]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('AndroidSwipeRefreshLayout');e.default=u},251,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=(0,t(r(d[1])).default)('PullToRefreshView',{paperComponentName:'RCTRefreshControl'});e.default=o},252,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n,o=r(d[0]),l=r(d[1]),s=r(d[2]),c=r(d[3]),p=r(d[4]),h=r(d[5]),u=r(d[6]),f=r(d[7]),R=r(d[8]),y=r(d[9]),_=r(d[10]),v=r(d[11]),S=r(d[12]),H=r(d[13]),w=r(d[14]),V=(r(d[15]),r(d[16]),r(d[17]),r(d[18])),k=r(d[19]),C=r(d[20]),T=r(d[21]);r(d[22]);function A(t){var n=f({},S.Mixin);for(var o in n)'function'==typeof n[o]&&(n[o]=n[o].bind(t));return n}t=C('RCTScrollView'),n=C('RCTScrollContentView');var E=_.createContext(null),x=Object.freeze({horizontal:!0}),M=Object.freeze({horizontal:!1}),I=(function(w){function C(t){var n;for(var o in l(this,C),(n=c(this,p(C).call(this,t)))._scrollResponder=A(u(u(n))),n._scrollAnimatedValue=new R.Value(0),n._scrollAnimatedValueAttachment=null,n._stickyHeaderRefs=new Map,n._headerLayoutYs=new Map,n.state=f({layoutHeight:null},S.Mixin.scrollResponderMixinGetInitialState()),n._handleScroll=function(t){n._scrollResponder.scrollResponderHandleScroll(t)},n._handleLayout=function(t){!0===n.props.invertStickyHeaders&&n.setState({layoutHeight:t.nativeEvent.layout.height}),n.props.onLayout&&n.props.onLayout(t)},n._handleContentOnLayout=function(t){var o=t.nativeEvent.layout,l=o.width,s=o.height;n.props.onContentSizeChange&&n.props.onContentSizeChange(l,s)},n._scrollViewRef=null,n._setScrollViewRef=function(t){n._scrollViewRef=t},n._innerViewRef=null,n._setInnerViewRef=function(t){n._innerViewRef=t},S.Mixin)'function'==typeof S.Mixin[o]&&o.startsWith('scrollResponder')&&(u(u(n))[o]=S.Mixin[o].bind(u(u(n))));return Object.keys(S.Mixin).filter(function(t){return'function'!=typeof S.Mixin[t]}).forEach(function(t){u(u(n))[t]=S.Mixin[t]}),n}return h(C,w),s(C,[{key:"UNSAFE_componentWillMount",value:function(){this._scrollResponder.UNSAFE_componentWillMount(),this._scrollAnimatedValue=new R.Value(this.props.contentOffset?this.props.contentOffset.y:0),this._scrollAnimatedValue.setOffset(this.props.contentInset?this.props.contentInset.top:0),this._stickyHeaderRefs=new Map,this._headerLayoutYs=new Map}},{key:"UNSAFE_componentWillReceiveProps",value:function(t){var n=this.props.contentInset?this.props.contentInset.top:0,o=t.contentInset?t.contentInset.top:0;n!==o&&this._scrollAnimatedValue.setOffset(o||0)}},{key:"componentDidMount",value:function(){this._updateAnimatedNodeAttachment()}},{key:"componentDidUpdate",value:function(){this._updateAnimatedNodeAttachment()}},{key:"componentWillUnmount",value:function(){this._scrollResponder.componentWillUnmount(),this._scrollAnimatedValueAttachment&&this._scrollAnimatedValueAttachment.detach()}},{key:"setNativeProps",value:function(t){this._scrollViewRef&&this._scrollViewRef.setNativeProps(t)}},{key:"getScrollResponder",value:function(){return this}},{key:"getScrollableNode",value:function(){return v.findNodeHandle(this._scrollViewRef)}},{key:"getInnerViewNode",value:function(){return v.findNodeHandle(this._innerViewRef)}},{key:"getNativeScrollRef",value:function(){return this._scrollViewRef}},{key:"scrollTo",value:function(t,n,o){var l,s,c;'number'==typeof t?(console.warn("`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead."),s=t,l=n,c=o):t&&(s=t.y,l=t.x,c=t.animated),this._scrollResponder.scrollResponderScrollTo({x:l||0,y:s||0,animated:!1!==c})}},{key:"scrollToEnd",value:function(t){var n=!1!==(t&&t.animated);this._scrollResponder.scrollResponderScrollToEnd({animated:n})}},{key:"scrollWithoutAnimationTo",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;console.warn('`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead'),this.scrollTo({x:n,y:t,animated:!1})}},{key:"flashScrollIndicators",value:function(){this._scrollResponder.scrollResponderFlashScrollIndicators()}},{key:"_getKeyForIndex",value:function(t,n){var o=n[t];return o&&o.key}},{key:"_updateAnimatedNodeAttachment",value:function(){this._scrollAnimatedValueAttachment&&this._scrollAnimatedValueAttachment.detach(),this.props.stickyHeaderIndices&&this.props.stickyHeaderIndices.length>0&&(this._scrollAnimatedValueAttachment=R.attachNativeEvent(this._scrollViewRef,'onScroll',[{nativeEvent:{contentOffset:{y:this._scrollAnimatedValue}}}]))}},{key:"_setStickyHeaderRef",value:function(t,n){n?this._stickyHeaderRefs.set(t,n):this._stickyHeaderRefs.delete(t)}},{key:"_onStickyHeaderLayout",value:function(t,n,o){var l=this.props.stickyHeaderIndices;if(l){var s=_.Children.toArray(this.props.children);if(o===this._getKeyForIndex(t,s)){var c=n.nativeEvent.layout.y;this._headerLayoutYs.set(o,c);var p=l[l.indexOf(t)-1];if(null!=p){var h=this._stickyHeaderRefs.get(this._getKeyForIndex(p,s));h&&h.setNextHeaderY&&h.setNextHeaderY(c)}}}}},{key:"render",value:function(){var l,s,c=this;s=n,V(void 0!==(l=t),'ScrollViewClass must not be undefined'),V(void 0!==s,'ScrollContentContainerViewClass must not be undefined');var p=[!0===this.props.horizontal&&b.contentContainerHorizontal,this.props.contentContainerStyle],h={};this.props.onContentSizeChange&&(h={onLayout:this._handleContentOnLayout});var u=this.props.stickyHeaderIndices,R=this.props.children;if(null!=u&&u.length>0){var v=_.Children.toArray(this.props.children);R=v.map(function(t,n){var o=t?u.indexOf(n):-1;if(o>-1){var l=t.key,s=u[o+1],p=c.props.StickyHeaderComponent||H;return _.createElement(p,{key:l,ref:function(t){return c._setStickyHeaderRef(l,t)},nextHeaderLayoutY:c._headerLayoutYs.get(c._getKeyForIndex(s,v)),onLayout:function(t){return c._onStickyHeaderLayout(n,t,l)},scrollAnimatedValue:c._scrollAnimatedValue,inverted:c.props.invertStickyHeaders,scrollViewHeight:c.state.layoutHeight},t)}return t})}R=_.createElement(E.Provider,{value:!0===this.props.horizontal?x:M},R);var S=Array.isArray(u)&&u.length>0,w=_.createElement(s,o({},h,{ref:this._setInnerViewRef,style:p,removeClippedSubviews:this.props.removeClippedSubviews,collapsable:!1}),R),C=void 0!==this.props.alwaysBounceHorizontal?this.props.alwaysBounceHorizontal:this.props.horizontal,A=void 0!==this.props.alwaysBounceVertical?this.props.alwaysBounceVertical:!this.props.horizontal,I=!!this.props.DEPRECATED_sendUpdatedChildFrames,z=!0===this.props.horizontal?b.baseHorizontal:b.baseVertical,L=f({},this.props,{alwaysBounceHorizontal:C,alwaysBounceVertical:A,style:[z,this.props.style],onContentSizeChange:null,onLayout:this._handleLayout,onMomentumScrollBegin:this._scrollResponder.scrollResponderHandleMomentumScrollBegin,onMomentumScrollEnd:this._scrollResponder.scrollResponderHandleMomentumScrollEnd,onResponderGrant:this._scrollResponder.scrollResponderHandleResponderGrant,onResponderReject:this._scrollResponder.scrollResponderHandleResponderReject,onResponderRelease:this._scrollResponder.scrollResponderHandleResponderRelease,onResponderTerminate:this._scrollResponder.scrollResponderHandleTerminate,onResponderTerminationRequest:this._scrollResponder.scrollResponderHandleTerminationRequest,onScrollBeginDrag:this._scrollResponder.scrollResponderHandleScrollBeginDrag,onScrollEndDrag:this._scrollResponder.scrollResponderHandleScrollEndDrag,onScrollShouldSetResponder:this._scrollResponder.scrollResponderHandleScrollShouldSetResponder,onStartShouldSetResponder:this._scrollResponder.scrollResponderHandleStartShouldSetResponder,onStartShouldSetResponderCapture:this._scrollResponder.scrollResponderHandleStartShouldSetResponderCapture,onTouchEnd:this._scrollResponder.scrollResponderHandleTouchEnd,onTouchMove:this._scrollResponder.scrollResponderHandleTouchMove,onTouchStart:this._scrollResponder.scrollResponderHandleTouchStart,onTouchCancel:this._scrollResponder.scrollResponderHandleTouchCancel,onScroll:this._handleScroll,scrollBarThumbImage:T(this.props.scrollBarThumbImage),scrollEventThrottle:S?1:this.props.scrollEventThrottle,sendMomentumEvents:!(!this.props.onMomentumScrollBegin&&!this.props.onMomentumScrollEnd),DEPRECATED_sendUpdatedChildFrames:I,snapToStart:!1!==this.props.snapToStart,snapToEnd:!1!==this.props.snapToEnd,pagingEnabled:!0===this.props.pagingEnabled&&null==this.props.snapToInterval&&null==this.props.snapToOffsets}),N=this.props.decelerationRate;null!=N&&(L.decelerationRate=k(N));var B=this.props.refreshControl;return B?_.createElement(l,o({},L,{ref:this._setScrollViewRef}),y.isTV?null:B,w):_.createElement(l,o({},L,{ref:this._setScrollViewRef}),w)}}]),C})(_.Component);I.Context=E;var b=w.create({baseVertical:{flexGrow:1,flexShrink:1,flexDirection:'column',overflow:'scroll'},baseHorizontal:{flexGrow:1,flexShrink:1,flexDirection:'row',overflow:'scroll'},contentContainerHorizontal:{flexDirection:'row'}});m.exports=I},253,[16,4,5,6,9,10,8,54,225,58,13,90,254,261,60,88,260,87,18,262,182,177,263]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),s=r(d[1]),n=r(d[2]),l=r(d[3]),t=r(d[4]),c=r(d[5]),p=r(d[6]),h=r(d[7]),u=r(d[8]),S=r(d[9]),b=r(d[10]).ScrollViewManager,R={Mixin:{_subscriptionKeyboardWillShow:null,_subscriptionKeyboardWillHide:null,_subscriptionKeyboardDidShow:null,_subscriptionKeyboardDidHide:null,scrollResponderMixinGetInitialState:function(){return{isTouching:!1,lastMomentumScrollBeginTime:0,lastMomentumScrollEndTime:0,observedScrollSinceBecomingResponder:!1,becameResponderWhileAnimating:!1}},scrollResponderHandleScrollShouldSetResponder:function(){return!0!==this.props.disableScrollViewPanResponder&&this.state.isTouching},scrollResponderHandleStartShouldSetResponder:function(o){if(!0===this.props.disableScrollViewPanResponder)return!1;var s=t.currentlyFocusedField();return'handled'===this.props.keyboardShouldPersistTaps&&null!=s&&o.target!==s},scrollResponderHandleStartShouldSetResponderCapture:function(o){if(this.scrollResponderIsAnimating())return!0;if(!0===this.props.disableScrollViewPanResponder)return!1;var s=t.currentlyFocusedField(),n=this.props.keyboardShouldPersistTaps;return!(n&&'never'!==n||null==s||!o.target||t.isTextInput(o.target))},scrollResponderHandleResponderReject:function(){},scrollResponderHandleTerminationRequest:function(){return!this.state.observedScrollSinceBecomingResponder},scrollResponderHandleTouchEnd:function(o){var s=o.nativeEvent;this.state.isTouching=0!==s.touches.length,this.props.onTouchEnd&&this.props.onTouchEnd(o)},scrollResponderHandleTouchCancel:function(o){this.state.isTouching=!1,this.props.onTouchCancel&&this.props.onTouchCancel(o)},scrollResponderHandleResponderRelease:function(o){this.props.onResponderRelease&&this.props.onResponderRelease(o);var s=t.currentlyFocusedField();!0===this.props.keyboardShouldPersistTaps||'always'===this.props.keyboardShouldPersistTaps||null==s||o.target===s||this.state.observedScrollSinceBecomingResponder||this.state.becameResponderWhileAnimating||(this.props.onScrollResponderKeyboardDismissed&&this.props.onScrollResponderKeyboardDismissed(o),t.blurTextInput(s))},scrollResponderHandleScroll:function(o){this.state.observedScrollSinceBecomingResponder=!0,this.props.onScroll&&this.props.onScroll(o)},scrollResponderHandleResponderGrant:function(o){this.state.observedScrollSinceBecomingResponder=!1,this.props.onResponderGrant&&this.props.onResponderGrant(o),this.state.becameResponderWhileAnimating=this.scrollResponderIsAnimating()},scrollResponderHandleScrollBeginDrag:function(o){s.beginScroll(),this.props.onScrollBeginDrag&&this.props.onScrollBeginDrag(o)},scrollResponderHandleScrollEndDrag:function(o){var n=o.nativeEvent.velocity;this.scrollResponderIsAnimating()||n&&(0!==n.x||0!==n.y)||s.endScroll(),this.props.onScrollEndDrag&&this.props.onScrollEndDrag(o)},scrollResponderHandleMomentumScrollBegin:function(o){this.state.lastMomentumScrollBeginTime=u(),this.props.onMomentumScrollBegin&&this.props.onMomentumScrollBegin(o)},scrollResponderHandleMomentumScrollEnd:function(o){s.endScroll(),this.state.lastMomentumScrollEndTime=u(),this.props.onMomentumScrollEnd&&this.props.onMomentumScrollEnd(o)},scrollResponderHandleTouchStart:function(o){this.state.isTouching=!0,this.props.onTouchStart&&this.props.onTouchStart(o)},scrollResponderHandleTouchMove:function(o){this.props.onTouchMove&&this.props.onTouchMove(o)},scrollResponderIsAnimating:function(){return u()-this.state.lastMomentumScrollEndTime<16||this.state.lastMomentumScrollEndTime<this.state.lastMomentumScrollBeginTime},scrollResponderGetScrollableNode:function(){return this.getScrollableNode?this.getScrollableNode():l.findNodeHandle(this)},scrollResponderScrollTo:function(o,s,n){if('number'==typeof o)console.warn('`scrollResponderScrollTo(x, y, animated)` is deprecated. Use `scrollResponderScrollTo({x: 5, y: 5, animated: true})` instead.');else{var l=o||{};o=l.x,s=l.y,n=l.animated}c.dispatchViewManagerCommand(h(this.scrollResponderGetScrollableNode()),c.getViewManagerConfig('RCTScrollView').Commands.scrollTo,[o||0,s||0,!1!==n])},scrollResponderScrollToEnd:function(o){var s=!1!==(o&&o.animated);c.dispatchViewManagerCommand(this.scrollResponderGetScrollableNode(),c.getViewManagerConfig('RCTScrollView').Commands.scrollToEnd,[s])},scrollResponderScrollWithoutAnimationTo:function(o,s){console.warn('`scrollResponderScrollWithoutAnimationTo` is deprecated. Use `scrollResponderScrollTo` instead'),this.scrollResponderScrollTo({x:o,y:s,animated:!1})},scrollResponderZoomTo:function(o,s){p(b&&b.zoomToRect,'zoomToRect is not implemented'),'animated'in o?(s=o.animated,delete o.animated):void 0!==s&&console.warn('`scrollResponderZoomTo` `animated` argument is deprecated. Use `options.animated` instead'),b.zoomToRect(this.scrollResponderGetScrollableNode(),o,!1!==s)},scrollResponderFlashScrollIndicators:function(){c.dispatchViewManagerCommand(this.scrollResponderGetScrollableNode(),c.getViewManagerConfig('RCTScrollView').Commands.flashScrollIndicators,[])},scrollResponderScrollNativeHandleToKeyboard:function(o,s,n){this.additionalScrollOffset=s||0,this.preventNegativeScrollOffset=!!n,c.measureLayout(o,l.findNodeHandle(this.getInnerViewNode()),this.scrollResponderTextInputFocusError,this.scrollResponderInputMeasureAndScrollToKeyboard)},scrollResponderInputMeasureAndScrollToKeyboard:function(s,n,l,t){var c=o.get('window').height;this.keyboardWillOpenTo&&(c=this.keyboardWillOpenTo.endCoordinates.screenY);var p=n-c+t+this.additionalScrollOffset;this.preventNegativeScrollOffset&&(p=Math.max(0,p)),this.scrollResponderScrollTo({x:0,y:p,animated:!0}),this.additionalOffset=0,this.preventNegativeScrollOffset=!1},scrollResponderTextInputFocusError:function(o){console.error('Error measuring text field: ',o)},UNSAFE_componentWillMount:function(){var o=this.props.keyboardShouldPersistTaps;S('boolean'!=typeof o,"'keyboardShouldPersistTaps={"+o+"}' is deprecated. Use 'keyboardShouldPersistTaps=\""+(o?'always':'never')+"\"' instead"),this.keyboardWillOpenTo=null,this.additionalScrollOffset=0,this._subscriptionKeyboardWillShow=n.addListener('keyboardWillShow',this.scrollResponderKeyboardWillShow),this._subscriptionKeyboardWillHide=n.addListener('keyboardWillHide',this.scrollResponderKeyboardWillHide),this._subscriptionKeyboardDidShow=n.addListener('keyboardDidShow',this.scrollResponderKeyboardDidShow),this._subscriptionKeyboardDidHide=n.addListener('keyboardDidHide',this.scrollResponderKeyboardDidHide)},componentWillUnmount:function(){null!=this._subscriptionKeyboardWillShow&&this._subscriptionKeyboardWillShow.remove(),null!=this._subscriptionKeyboardWillHide&&this._subscriptionKeyboardWillHide.remove(),null!=this._subscriptionKeyboardDidShow&&this._subscriptionKeyboardDidShow.remove(),null!=this._subscriptionKeyboardDidHide&&this._subscriptionKeyboardDidHide.remove()},scrollResponderKeyboardWillShow:function(o){this.keyboardWillOpenTo=o,this.props.onKeyboardWillShow&&this.props.onKeyboardWillShow(o)},scrollResponderKeyboardWillHide:function(o){this.keyboardWillOpenTo=null,this.props.onKeyboardWillHide&&this.props.onKeyboardWillHide(o)},scrollResponderKeyboardDidShow:function(o){o&&(this.keyboardWillOpenTo=o),this.props.onKeyboardDidShow&&this.props.onKeyboardDidShow(o)},scrollResponderKeyboardDidHide:function(o){this.keyboardWillOpenTo=null,this.props.onKeyboardDidHide&&this.props.onKeyboardDidHide(o)}}};m.exports=R},254,[62,255,257,90,163,75,18,211,111,20,25]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),o=r(d[2]),l={setGlobalOptions:function(l){if(void 0!==l.debug&&o(t.default,'Trying to debug FrameRateLogger without the native module!'),t.default){var u={debug:!!l.debug,reportStackTraces:!!l.reportStackTraces};t.default.setGlobalOptions(u)}},setContext:function(o){t.default&&t.default.setContext(o)},beginScroll:function(){t.default&&t.default.beginScroll()},endScroll:function(){t.default&&t.default.endScroll()}};m.exports=l},255,[3,256,18]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).get('FrameRateLogger');e.default=o},256,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0])(r(d[1])),o=r(d[2]),t=r(d[3]),u=r(d[4]),s=r(d[5]),c={addListener:function(n,o){s(!1,'Dummy method used for documentation')},removeListener:function(n,o){s(!1,'Dummy method used for documentation')},removeAllListeners:function(n){s(!1,'Dummy method used for documentation')},dismiss:function(){s(!1,'Dummy method used for documentation')},scheduleLayoutAnimation:function(n){s(!1,'Dummy method used for documentation')}};(c=new t(n.default)).dismiss=u,c.scheduleLayoutAnimation=function(n){var t=n.duration,u=n.easing;null!=t&&0!==t&&o.configureNext({duration:t,update:{duration:t,type:null!=u&&o.Types[u]||'keyboard'}})},m.exports=c},257,[3,258,259,123,260,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('KeyboardObserver');e.default=u},258,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0])(r(d[1])),t=r(d[2]);function s(s,o){var p;n.default.isTesting||t.configureNextLayoutAnimation(s,null!=(p=o)?p:function(){},function(){})}function o(n,t,s){return{duration:n,create:{type:t,property:s},update:{type:t},delete:{type:t,property:s}}}var p={easeInEaseOut:o(300,'easeInEaseOut','opacity'),linear:o(500,'linear','opacity'),spring:{duration:700,create:{type:'linear',property:'opacity'},update:{type:'spring',springDamping:.4},delete:{type:'linear',property:'opacity'}}},c={configureNext:s,create:o,Types:Object.freeze({spring:'spring',linear:'linear',easeInEaseOut:'easeInEaseOut',easeIn:'easeIn',easeOut:'easeOut',keyboard:'keyboard'}),Properties:Object.freeze({opacity:'opacity',scaleX:'scaleX',scaleY:'scaleY',scaleXY:'scaleXY'}),checkConfig:function(){console.error('LayoutAnimation.checkConfig(...) has been disabled.')},Presets:p,easeInEaseOut:s.bind(null,p.easeInEaseOut),linear:s.bind(null,p.linear),spring:s.bind(null,p.spring)};m.exports=c},259,[3,58,75]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=function(){t.blurTextInput(t.currentlyFocusedField())}},260,[163]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),u=r(d[3]),s=r(d[4]),l=r(d[5]),p=r(d[6]),h=r(d[7]),y=r(d[8]),c=l.createAnimatedComponent(y),v=(function(l){function h(){var n,s;t(this,h);for(var l=arguments.length,y=new Array(l),c=0;c<l;c++)y[c]=arguments[c];return(s=o(this,(n=u(h)).call.apply(n,[this].concat(y)))).state={measured:!1,layoutY:0,layoutHeight:0,nextHeaderLayoutY:s.props.nextHeaderLayoutY},s._onLayout=function(t){s.setState({measured:!0,layoutY:t.nativeEvent.layout.y,layoutHeight:t.nativeEvent.layout.height}),s.props.onLayout(t);var n=p.Children.only(s.props.children);n.props.onLayout&&n.props.onLayout(t)},s}return s(h,l),n(h,[{key:"setNextHeaderY",value:function(t){this.setState({nextHeaderLayoutY:t})}},{key:"render",value:function(){var t=this.props,n=t.inverted,o=t.scrollViewHeight,u=this.state,s=u.measured,l=u.layoutHeight,h=u.layoutY,y=u.nextHeaderLayoutY,v=[-1,0],L=[0,0];if(s)if(n){if(null!=o){var H=h+l-o;if(H>0){v.push(H),L.push(0),v.push(H+1),L.push(1);var Y=(y||0)-l-o;Y>H&&(v.push(Y,Y+1),L.push(Y-H,Y-H))}}}else{v.push(h),L.push(0);var x=(y||0)-l;x>=h?(v.push(x,x+1),L.push(x-h,x-h)):(v.push(h+1),L.push(1))}var C=this.props.scrollAnimatedValue.interpolate({inputRange:v,outputRange:L}),E=p.Children.only(this.props.children);return p.createElement(c,{collapsable:!1,onLayout:this._onLayout,style:[E.props.style,f.header,{transform:[{translateY:C}]}]},p.cloneElement(E,{style:f.fill,onLayout:void 0}))}}]),h})(p.Component),f=h.create({header:{zIndex:10},fill:{flex:1}});m.exports=v},261,[4,5,6,9,10,225,13,60,88]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]);m.exports=function(t){return'normal'===t?.998:'fast'===t?.99:t}},262,[58]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(Object.create(null),{margin:!0,marginHorizontal:!0,marginVertical:!0,marginBottom:!0,marginTop:!0,marginLeft:!0,marginRight:!0,flex:!0,flexGrow:!0,flexShrink:!0,flexBasis:!0,alignSelf:!0,height:!0,minHeight:!0,maxHeight:!0,width:!0,minWidth:!0,maxWidth:!0,position:!0,left:!0,right:!0,bottom:!0,top:!0});m.exports=function(n){var o={},f={};return n&&Object.keys(n).forEach(function(h){var l=n[h];t[h]?f[h]=l:o[h]=l}),{outer:f,inner:o}}},263,[16]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),l=r(d[4]),h=(function(){function h(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{viewAreaCoveragePercentThreshold:0};s(this,h),this._hasInteracted=!1,this._timers=new Set,this._viewableIndices=[],this._viewableItems=new Map,this._config=t}return o(h,[{key:"dispose",value:function(){this._timers.forEach(clearTimeout)}},{key:"computeViewableItems",value:function(t,n,s,o,h){var c=this._config,f=c.itemVisiblePercentThreshold,v=c.viewAreaCoveragePercentThreshold,b=null!=v,_=b?v:f;l(null!=_&&null!=f!=(null!=v),'Must set exactly one of itemVisiblePercentThreshold or viewAreaCoveragePercentThreshold');var w=[];if(0===t)return w;var y=-1,I=h||{first:0,last:t-1},p=I.first,k=I.last;if(k>=t)return console.warn('Invalid render range computing viewability '+JSON.stringify({renderRange:h,itemCount:t})),[];for(var S=p;S<=k;S++){var T=o(S);if(T){var A=T.offset-n,V=A+T.length;if(A<s&&V>0)y=S,u(b,_,A,V,s,T.length)&&w.push(S);else if(y>=0)break}}return w}},{key:"onUpdate",value:function(t,n,s,o,l,h,u){var c=this;if((!this._config.waitForInteraction||this._hasInteracted)&&0!==t&&o(0)){var f=[];if(t&&(f=this.computeViewableItems(t,n,s,o,u)),this._viewableIndices.length!==f.length||!this._viewableIndices.every(function(t,n){return t===f[n]}))if(this._viewableIndices=f,this._config.minimumViewTime){var v=setTimeout(function(){c._timers.delete(v),c._onUpdateSync(f,h,l)},this._config.minimumViewTime);this._timers.add(v)}else this._onUpdateSync(f,h,l)}}},{key:"resetViewableIndices",value:function(){this._viewableIndices=[]}},{key:"recordInteraction",value:function(){this._hasInteracted=!0}},{key:"_onUpdateSync",value:function(s,o,l){var h=this;s=s.filter(function(t){return h._viewableIndices.includes(t)});var u=this._viewableItems,c=new Map(s.map(function(t){var n=l(t,!0);return[n.key,n]})),f=[],v=c,b=Array.isArray(v),_=0;for(v=b?v:v["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var w;if(b){if(_>=v.length)break;w=v[_++]}else{if((_=v.next()).done)break;w=_.value}var y=n(w,2),I=y[0],p=y[1];u.has(I)||f.push(p)}var k=u,S=Array.isArray(k),T=0;for(k=S?k:k["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var A;if(S){if(T>=k.length)break;A=k[T++]}else{if((T=k.next()).done)break;A=T.value}var V=n(A,2),x=V[0],M=V[1];c.has(x)||f.push(t({},M,{isViewable:!1}))}f.length>0&&(this._viewableItems=c,o({viewableItems:Array.from(c.values()),changed:f,viewabilityConfig:this._config}))}}]),h})();function u(t,n,s,o,l,h){if(f(s,o,l))return!0;var u=c(s,o,l);return 100*(t?u/l:u/h)>=n}function c(t,n,s){var o=Math.min(n,s)-Math.max(t,0);return Math.max(0,o)}function f(t,n,s){return t>=0&&n<=s&&n>t}m.exports=h},264,[54,26,4,5,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]);function s(t,s,f){for(var l=[],o=0,u=0;u<s;u++)for(var h=f(u),v=h.offset+h.length,c=0;c<t.length;c++)if(null==l[c]&&v>=t[c]&&(l[c]=u,o++,c===t.length-1))return n(o===t.length,'bad offsets input, should be in increasing order: %s',JSON.stringify(t)),l;return l}function f(t,n){return n.last-n.first+1-Math.max(0,1+Math.min(n.last,t.last)-Math.max(n.first,t.first))}var l={computeWindowedRenderLimits:function(n,l,o,u){var h=n.data,v=n.getItemCount,c=n.maxToRenderPerBatch,x=n.windowSize,M=v(h);if(0===M)return l;var w=u.offset,b=u.velocity,p=u.visibleLength,C=Math.max(0,w),O=C+p,y=(x-1)*p,L=b>1?'after':b<-1?'before':'none',R=Math.max(0,C-.5*y),S=Math.max(0,O+.5*y);if(o(M-1).offset<R)return{first:Math.max(0,M-1-c),last:M-1};var B=s([R,C,O,S],n.getItemCount(n.data),o),I=t(B,4),J=I[0],N=I[1],T=I[2],_=I[3];J=null==J?0:J,N=null==N?Math.max(0,J):N,_=null==_?M-1:_;for(var k={first:N,last:T=null==T?Math.min(_,N+c-1):T},z=f(l,k);!(N<=J&&T>=_);){var E=z>=c,F=N<=l.first||N>l.last,P=N>J&&(!E||!F),W=T>=l.last||T<l.first,j=T<_&&(!E||!W);if(E&&!P&&!j)break;!P||'after'===L&&j&&W||(F&&z++,N--),!j||'before'===L&&P&&F||(W&&z++,T++)}if(!(T>=N&&N>=0&&T<M&&N>=J&&T<=_&&N<=k.first&&T>=k.last))throw new Error('Bad window calculation '+JSON.stringify({first:N,last:T,itemCount:M,overscanFirst:J,overscanLast:_,visible:k}));return{first:N,last:T}},elementsThatOverlapOffsets:s,newRangeCount:f};m.exports=l},265,[26,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]);m.exports=s(t)},266,[267,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),c=r(d[4]),u=(r(d[5]),r(d[6])),h=r(d[7]),l=r(d[8]),f=r(d[9]),p=s.ImageViewManager,w=l('RCTImageView');var v=function(n,o){var s,u,l=f(n.source)||{uri:void 0,width:void 0,height:void 0};if(Array.isArray(l))u=h([y.base,n.style])||{},s=l;else{var p=l.width,v=l.height,z=l.uri;u=h([{width:p,height:v},y.base,n.style])||{},s=[l],''===z&&console.warn('source.uri should not be an empty string')}var I=n.resizeMode||u.resizeMode||'cover',b=u.tintColor;if(null!=n.src&&console.warn('The <Image> component requires a `source` property rather than `src`.'),null!=n.children)throw new Error('The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.');return c.createElement(w,t({},n,{ref:o,style:u,resizeMode:I,tintColor:b,source:s}))};(v=c.forwardRef(v)).displayName='Image',v.getSize=function(t,n,o){p.getSize(t,n,o||function(){console.warn('Failed to get size for image: '+t)})},v.getSizeWithHeaders=function(t,n,o,s){return p.getSizeWithHeaders({uri:t,headers:n}).then(function(t){o(t.width,t.height)}).catch(s||function(){console.warn('Failed to get size for image: '+t)})},v.prefetch=function(t){return p.prefetchImage(t)},v.queryCache=function(t){return n.async(function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,n.awrap(p.queryCache(t));case 2:return o.abrupt("return",o.sent);case 3:case"end":return o.stop()}},null,this)},v.resolveAssetSource=f,v.propTypes=o;var y=u.create({base:{overflow:'hidden'}});m.exports=v},267,[16,268,270,25,13,90,60,87,182,177]); -__d(function(g,r,i,a,m,e,d){m.exports=r(d[0])},268,[269]); -__d(function(g,r,i,a,m,e,d){var t=(function(t){"use strict";var n,o=Object.prototype,c=o.hasOwnProperty,u="function"==typeof Symbol?Symbol:{},h=u.iterator||"@@iterator",f=u.asyncIterator||"@@asyncIterator",s=u.toStringTag||"@@toStringTag";function l(t,n,o,c){var u=n&&n.prototype instanceof E?n:E,h=Object.create(u.prototype),f=new A(c||[]);return h._invoke=F(t,o,f),h}function p(t,n,o){try{return{type:"normal",arg:t.call(n,o)}}catch(t){return{type:"throw",arg:t}}}t.wrap=l;var y="suspendedStart",v="suspendedYield",w="executing",L="completed",x={};function E(){}function b(){}function _(){}var j={};j[h]=function(){return this};var O=Object.getPrototypeOf,k=O&&O(O(R([])));k&&k!==o&&c.call(k,h)&&(j=k);var G=_.prototype=E.prototype=Object.create(j);function N(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function P(t){function n(o,u,h,f){var s=p(t[o],t,u);if("throw"!==s.type){var l=s.arg,y=l.value;return y&&"object"==typeof y&&c.call(y,"__await")?Promise.resolve(y.__await).then(function(t){n("next",t,h,f)},function(t){n("throw",t,h,f)}):Promise.resolve(y).then(function(t){l.value=t,h(l)},function(t){return n("throw",t,h,f)})}f(s.arg)}var o;this._invoke=function(t,c){function u(){return new Promise(function(o,u){n(t,c,o,u)})}return o=o?o.then(u,u):u()}}function F(t,n,o){var c=y;return function(u,h){if(c===w)throw new Error("Generator is already running");if(c===L){if("throw"===u)throw h;return Y()}for(o.method=u,o.arg=h;;){var f=o.delegate;if(f){var s=S(f,o);if(s){if(s===x)continue;return s}}if("next"===o.method)o.sent=o._sent=o.arg;else if("throw"===o.method){if(c===y)throw c=L,o.arg;o.dispatchException(o.arg)}else"return"===o.method&&o.abrupt("return",o.arg);c=w;var l=p(t,n,o);if("normal"===l.type){if(c=o.done?L:v,l.arg===x)continue;return{value:l.arg,done:o.done}}"throw"===l.type&&(c=L,o.method="throw",o.arg=l.arg)}}}function S(t,o){var c=t.iterator[o.method];if(c===n){if(o.delegate=null,"throw"===o.method){if(t.iterator.return&&(o.method="return",o.arg=n,S(t,o),"throw"===o.method))return x;o.method="throw",o.arg=new TypeError("The iterator does not provide a 'throw' method")}return x}var u=p(c,t.iterator,o.arg);if("throw"===u.type)return o.method="throw",o.arg=u.arg,o.delegate=null,x;var h=u.arg;return h?h.done?(o[t.resultName]=h.value,o.next=t.nextLoc,"return"!==o.method&&(o.method="next",o.arg=n),o.delegate=null,x):h:(o.method="throw",o.arg=new TypeError("iterator result is not an object"),o.delegate=null,x)}function T(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function I(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function A(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(T,this),this.reset(!0)}function R(t){if(t){var o=t[h];if(o)return o.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var u=-1,f=function o(){for(;++u<t.length;)if(c.call(t,u))return o.value=t[u],o.done=!1,o;return o.value=n,o.done=!0,o};return f.next=f}}return{next:Y}}function Y(){return{value:n,done:!0}}return b.prototype=G.constructor=_,_.constructor=b,_[s]=b.displayName="GeneratorFunction",t.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===b||"GeneratorFunction"===(n.displayName||n.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,_):(t.__proto__=_,s in t||(t[s]="GeneratorFunction")),t.prototype=Object.create(G),t},t.awrap=function(t){return{__await:t}},N(P.prototype),P.prototype[f]=function(){return this},t.AsyncIterator=P,t.async=function(n,o,c,u){var h=new P(l(n,o,c,u));return t.isGeneratorFunction(o)?h:h.next().then(function(t){return t.done?t.value:h.next()})},N(G),G[s]="Generator",G[h]=function(){return this},G.toString=function(){return"[object Generator]"},t.keys=function(t){var n=[];for(var o in t)n.push(o);return n.reverse(),function o(){for(;n.length;){var c=n.pop();if(c in t)return o.value=c,o.done=!1,o}return o.done=!0,o}},t.values=R,A.prototype={constructor:A,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=n,this.done=!1,this.delegate=null,this.method="next",this.arg=n,this.tryEntries.forEach(I),!t)for(var o in this)"t"===o.charAt(0)&&c.call(this,o)&&!isNaN(+o.slice(1))&&(this[o]=n)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var o=this;function u(c,u){return s.type="throw",s.arg=t,o.next=c,u&&(o.method="next",o.arg=n),!!u}for(var h=this.tryEntries.length-1;h>=0;--h){var f=this.tryEntries[h],s=f.completion;if("root"===f.tryLoc)return u("end");if(f.tryLoc<=this.prev){var l=c.call(f,"catchLoc"),p=c.call(f,"finallyLoc");if(l&&p){if(this.prev<f.catchLoc)return u(f.catchLoc,!0);if(this.prev<f.finallyLoc)return u(f.finallyLoc)}else if(l){if(this.prev<f.catchLoc)return u(f.catchLoc,!0)}else{if(!p)throw new Error("try statement without catch or finally");if(this.prev<f.finallyLoc)return u(f.finallyLoc)}}}},abrupt:function(t,n){for(var o=this.tryEntries.length-1;o>=0;--o){var u=this.tryEntries[o];if(u.tryLoc<=this.prev&&c.call(u,"finallyLoc")&&this.prev<u.finallyLoc){var h=u;break}}h&&("break"===t||"continue"===t)&&h.tryLoc<=n&&n<=h.finallyLoc&&(h=null);var f=h?h.completion:{};return f.type=t,f.arg=n,h?(this.method="next",this.next=h.finallyLoc,x):this.complete(f)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),x},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.finallyLoc===t)return this.complete(o.completion,o.afterLoc),I(o),x}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc===t){var c=o.completion;if("throw"===c.type){var u=c.arg;I(o)}return u}}throw new Error("illegal catch attempt")},delegateYield:function(t,o,c){return this.delegate={iterator:R(t),resultName:o,nextLoc:c},"next"===this.method&&(this.arg=n),x}},t})("object"==typeof m?m.exports:{});try{regeneratorRuntime=t}catch(n){Function("r","regeneratorRuntime = r")(t)}},269,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),o=r(d[1]),t=r(d[2]),s=r(d[3]),c=r(d[4]);m.exports={style:s(t),source:o,defaultSource:c.oneOfType([c.shape({uri:c.string,width:c.number,height:c.number,scale:c.number}),c.number]),accessible:c.bool,accessibilityLabel:c.node,blurRadius:c.number,capInsets:n,resizeMethod:c.oneOf(['auto','resize','scale']),resizeMode:c.oneOf(['cover','contain','stretch','repeat','center']),testID:c.string,onLayout:c.func,onLoadStart:c.func,onProgress:c.func,onError:c.func,onPartialLoad:c.func,onLoad:c.func,onLoadEnd:c.func}},270,[199,271,65,200,69]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=n.shape({uri:n.string,bundle:n.string,method:n.string,headers:n.objectOf(n.string),body:n.string,cache:n.oneOf(['default','reload','force-cache','only-if-cached']),width:n.number,height:n.number,scale:n.number}),c=n.oneOfType([t,n.number,n.arrayOf(t)]);m.exports=c},271,[69]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]);m.exports=o(t,{scrollEventThrottle:1e-4})},272,[253,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]);m.exports=o(t,{scrollEventThrottle:1e-4})},273,[274,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),f=r(d[4]),p=r(d[5]),c=r(d[6]),l=(r(d[7]),r(d[8])),u=(r(d[9]),r(d[10])),R=c({},u.defaultProps,{stickySectionHeadersEnabled:!0}),h=(function(c){function R(){var t,s;n(this,R);for(var p=arguments.length,c=new Array(p),l=0;l<p;l++)c[l]=arguments[l];return(s=o(this,(t=f(R)).call.apply(t,[this].concat(c))))._captureRef=function(t){s._wrapperListRef=t},s}return p(R,c),s(R,[{key:"scrollToLocation",value:function(t){null!=this._wrapperListRef&&this._wrapperListRef.scrollToLocation(t)}},{key:"recordInteraction",value:function(){var t=this._wrapperListRef&&this._wrapperListRef.getListRef();t&&t.recordInteraction()}},{key:"flashScrollIndicators",value:function(){var t=this._wrapperListRef&&this._wrapperListRef.getListRef();t&&t.flashScrollIndicators()}},{key:"getScrollResponder",value:function(){var t=this._wrapperListRef&&this._wrapperListRef.getListRef();if(t)return t.getScrollResponder()}},{key:"getScrollableNode",value:function(){var t=this._wrapperListRef&&this._wrapperListRef.getListRef();if(t)return t.getScrollableNode()}},{key:"setNativeProps",value:function(t){var n=this._wrapperListRef&&this._wrapperListRef.getListRef();n&&n.setNativeProps(t)}},{key:"render",value:function(){return l.createElement(u,t({},this.props,{ref:this._captureRef,getItemCount:function(t){return t.length},getItem:function(t,n){return t[n]}}))}}]),R})(l.PureComponent);h.defaultProps=R,m.exports=h},274,[16,4,5,6,9,10,54,58,13,253,275]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),p=r(d[5]),c=r(d[6]),u=r(d[7]),h=r(d[8]),f=r(d[9]),S=r(d[10]),v=r(d[11]),I=(function(f){function I(t,p){var c;return o(this,I),(c=s(this,l(I).call(this,t,p)))._keyExtractor=function(t,n){var o=c._subExtractor(n);return o&&o.key||String(n)},c._convertViewable=function(t){v(null!=t.index,'Received a broken ViewToken');var o=c._subExtractor(t.index);if(!o)return null;var s=o.section.keyExtractor||c.props.keyExtractor;return n({},t,{index:o.index,key:s(t.item,o.index),section:o.section})},c._onViewableItemsChanged=function(t){var n=t.viewableItems,o=t.changed;c.props.onViewableItemsChanged&&c.props.onViewableItemsChanged({viewableItems:n.map(c._convertViewable,u(u(c))).filter(Boolean),changed:o.map(c._convertViewable,u(u(c))).filter(Boolean)})},c._renderItem=function(t){var n=t.item,o=t.index,s=c._subExtractor(o);if(!s)return null;var l=s.index;if(null==l){var p=s.section;if(!0===s.header){var u=c.props.renderSectionHeader;return u?u({section:p}):null}var f=c.props.renderSectionFooter;return f?f({section:p}):null}var S=s.section.renderItem||c.props.renderItem,I=c._getSeparatorComponent(o,s);return v(S,'no renderItem!'),h.createElement(_,{SeparatorComponent:I,LeadingSeparatorComponent:0===l?c.props.SectionSeparatorComponent:void 0,cellKey:s.key,index:l,item:n,leadingItem:s.leadingItem,leadingSection:s.leadingSection,onUpdateSeparator:c._onUpdateSeparator,prevCellKey:(c._subExtractor(o-1)||{}).key,ref:function(t){c._cellRefs[s.key]=t},renderItem:S,section:s.section,trailingItem:s.trailingItem,trailingSection:s.trailingSection})},c._onUpdateSeparator=function(t,n){var o=c._cellRefs[t];o&&o.updateSeparatorProps(n)},c._cellRefs={},c._captureRef=function(t){c._listRef=t},c.state=c._computeState(t),c}return c(I,f),p(I,[{key:"scrollToLocation",value:function(t){for(var o=t.itemIndex,s=0;s<t.sectionIndex;s++)o+=this.props.getItemCount(this.props.sections[s].data)+2;var l=t.viewOffset||0;t.itemIndex>0&&this.props.stickySectionHeadersEnabled&&(l+=this._listRef._getFrameMetricsApprox(o-t.itemIndex).length);var p=n({},t,{viewOffset:l,index:o});this._listRef.scrollToIndex(p)}},{key:"getListRef",value:function(){return this._listRef}}]),p(I,[{key:"UNSAFE_componentWillReceiveProps",value:function(t){this.setState(this._computeState(t))}},{key:"_computeState",value:function(t){var o=t.ListHeaderComponent?1:0,s=[],l=t.sections?t.sections.reduce(function(n,l){return s.push(n+o),n+t.getItemCount(l.data)+2},0):0;return{childProps:n({},t,{renderItem:this._renderItem,ItemSeparatorComponent:void 0,data:t.sections,getItemCount:function(){return l},getItem:function(n,o){return x(t,n,o)},keyExtractor:this._keyExtractor,onViewableItemsChanged:t.onViewableItemsChanged?this._onViewableItemsChanged:void 0,stickyHeaderIndices:t.stickySectionHeadersEnabled?s:void 0})}}},{key:"render",value:function(){return h.createElement(S,t({},this.state.childProps,{ref:this._captureRef}))}},{key:"_subExtractor",value:function(t){for(var n=t,o=this.props,s=o.getItem,l=o.getItemCount,p=o.keyExtractor,c=o.sections,u=0;u<c.length;u++){var h=c[u],f=h.data,S=h.key||String(u);if((n-=1)>=l(f)+1)n-=l(f)+1;else return-1===n?{section:h,key:S+':header',index:null,header:!0,trailingSection:c[u+1]}:n===l(f)?{section:h,key:S+':footer',index:null,header:!1,trailingSection:c[u+1]}:{section:h,key:S+':'+(h.keyExtractor||p)(s(f,n),n),index:n,leadingItem:s(f,n-1),leadingSection:c[u-1],trailingItem:s(f,n+1),trailingSection:c[u+1]}}}},{key:"_getSeparatorComponent",value:function(t,n){if(!(n=n||this._subExtractor(t)))return null;var o=n.section.ItemSeparatorComponent||this.props.ItemSeparatorComponent,s=this.props.SectionSeparatorComponent,l=t===this.state.childProps.getItemCount()-1,p=n.index===this.props.getItemCount(n.section.data)-1;return s&&p?s:!o||p||l?null:o}}]),I})(h.PureComponent);I.defaultProps=n({},S.defaultProps,{data:[]});var _=(function(t){function u(){var t,p;o(this,u);for(var c=arguments.length,h=new Array(c),f=0;f<c;f++)h[f]=arguments[f];return(p=s(this,(t=l(u)).call.apply(t,[this].concat(h)))).state={separatorProps:{highlighted:!1,leadingItem:p.props.item,leadingSection:p.props.leadingSection,section:p.props.section,trailingItem:p.props.trailingItem,trailingSection:p.props.trailingSection},leadingSeparatorProps:{highlighted:!1,leadingItem:p.props.leadingItem,leadingSection:p.props.leadingSection,section:p.props.section,trailingItem:p.props.item,trailingSection:p.props.trailingSection}},p._separators={highlight:function(){['leading','trailing'].forEach(function(t){return p._separators.updateProps(t,{highlighted:!0})})},unhighlight:function(){['leading','trailing'].forEach(function(t){return p._separators.updateProps(t,{highlighted:!1})})},updateProps:function(t,o){var s=p.props,l=s.LeadingSeparatorComponent,c=s.cellKey,u=s.prevCellKey;'leading'===t&&null!=l?p.setState(function(t){return{leadingSeparatorProps:n({},t.leadingSeparatorProps,o)}}):p.props.onUpdateSeparator('leading'===t&&u||c,o)}},p}return c(u,t),p(u,[{key:"updateSeparatorProps",value:function(t){this.setState(function(o){return{separatorProps:n({},o.separatorProps,t)}})}},{key:"render",value:function(){var t=this.props,n=t.LeadingSeparatorComponent,o=t.SeparatorComponent,s=t.item,l=t.index,p=t.section,c=this.props.renderItem({item:s,index:l,section:p,separators:this._separators}),u=n&&h.createElement(n,this.state.leadingSeparatorProps),S=o&&h.createElement(o,this.state.separatorProps);return u||S?h.createElement(f,null,u,c,S):c}}],[{key:"getDerivedStateFromProps",value:function(t,o){return{separatorProps:n({},o.separatorProps,{leadingItem:t.item,leadingSection:t.leadingSection,section:t.section,trailingItem:t.trailingItem,trailingSection:t.trailingSection}),leadingSeparatorProps:n({},o.leadingSeparatorProps,{leadingItem:t.leadingItem,leadingSection:t.leadingSection,section:t.section,trailingItem:t.item,trailingSection:t.trailingSection})}}}]),u})(h.Component);function x(t,n,o){if(!n)return null;for(var s=o-1,l=0;l<n.length;l++){var p=n[l],c=p.data,u=t.getItemCount(c);if(-1===s||s===u)return p;if(s<u)return t.getItem(c,s);s-=u+2}return null}m.exports=I},275,[16,54,4,6,9,5,10,8,13,88,247,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]);m.exports=s(t)},276,[197,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]);m.exports=s(t)},277,[88,244]); -__d(function(g,r,i,a,m,e,d){'use strict';var _=r(d[0]).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.NativeMethodsMixin;m.exports=_},278,[90]); -__d(function(g,r,i,a,m,e,d){'use strict';var s=r(d[0]),t=r(d[1]),o=r(d[2]),n=r(d[3]),c=r(d[4]),l=r(d[5]),p=r(d[6]),u=r(d[7]),h=r(d[8]).DeprecatedAccessibilityRoles,b={top:20,left:20,right:20,bottom:30},y=['accessibilityLabel','accessibilityHint','accessibilityIgnoresInvertColors','accessibilityRole','accessibilityStates','accessibilityState','accessibilityActions','onAccessibilityAction','hitSlop','nativeID','onBlur','onFocus','onLayout','testID'],f=p({displayName:'TouchableWithoutFeedback',mixins:[c.Mixin],propTypes:{accessible:n.bool,accessibilityLabel:n.node,accessibilityHint:n.string,accessibilityIgnoresInvertColors:n.bool,accessibilityRole:n.oneOf(h),accessibilityStates:n.array,accessibilityState:n.object,accessibilityActions:n.array,onAccessibilityAction:n.func,onFocus:n.func,onBlur:n.func,disabled:n.bool,onPress:n.func,onPressIn:n.func,onPressOut:n.func,onLayout:n.func,touchSoundDisabled:n.bool,onLongPress:n.func,nativeID:n.string,testID:n.string,delayPressIn:n.number,delayPressOut:n.number,delayLongPress:n.number,pressRetentionOffset:t,hitSlop:t},getInitialState:function(){return this.touchableGetInitialState()},componentDidMount:function(){u(this.props)},UNSAFE_componentWillReceiveProps:function(s){u(s)},touchableHandlePress:function(s){this.props.onPress&&this.props.onPress(s)},touchableHandleActivePressIn:function(s){this.props.onPressIn&&this.props.onPressIn(s)},touchableHandleActivePressOut:function(s){this.props.onPressOut&&this.props.onPressOut(s)},touchableHandleLongPress:function(s){this.props.onLongPress&&this.props.onLongPress(s)},touchableGetPressRectOffset:function(){return this.props.pressRetentionOffset||b},touchableGetHitSlop:function(){return this.props.hitSlop},touchableGetHighlightDelayMS:function(){return this.props.delayPressIn||0},touchableGetLongPressDelayMS:function(){return 0===this.props.delayLongPress?0:this.props.delayLongPress||500},touchableGetPressOutDelayMS:function(){return this.props.delayPressOut||0},render:function(){var t=o.Children.only(this.props.children),n=t.props.children;c.TOUCH_TARGET_DEBUG&&t.type===l&&(n=o.Children.toArray(n)).push(c.renderDebugView({color:'red',hitSlop:this.props.hitSlop}));for(var p={},u=0;u<y.length;u++){var h=y[u];void 0!==this.props[h]&&(p[h]=this.props[h])}return o.cloneElement(t,s({},p,{accessible:!1!==this.props.accessible,focusable:!1!==this.props.focusable&&void 0!==this.props.onPress,onClick:this.touchableHandlePress,onStartShouldSetResponder:this.touchableHandleStartShouldSetResponder,onResponderTerminationRequest:this.touchableHandleResponderTerminationRequest,onResponderGrant:this.touchableHandleResponderGrant,onResponderMove:this.touchableHandleResponderMove,onResponderRelease:this.touchableHandleResponderRelease,onResponderTerminate:this.touchableHandleResponderTerminate,children:n}))}});m.exports=f},279,[54,199,13,69,203,88,280,284,285]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=r(d[1]);if(void 0===t)throw Error("create-react-class could not find the React object. If you are using script tags, make sure that React is being loaded before create-react-class.");var c=(new t.Component).updater;m.exports=o(t.Component,t.isValidElement,c)},280,[13,281]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s='mixins';m.exports=function(p,c,u){var l=[],E={mixins:'DEFINE_MANY',statics:'DEFINE_MANY',propTypes:'DEFINE_MANY',contextTypes:'DEFINE_MANY',childContextTypes:'DEFINE_MANY',getDefaultProps:'DEFINE_MANY_MERGED',getInitialState:'DEFINE_MANY_MERGED',getChildContext:'DEFINE_MANY_MERGED',render:'DEFINE_ONCE',componentWillMount:'DEFINE_MANY',componentDidMount:'DEFINE_MANY',componentWillReceiveProps:'DEFINE_MANY',shouldComponentUpdate:'DEFINE_ONCE',componentWillUpdate:'DEFINE_MANY',componentDidUpdate:'DEFINE_MANY',componentWillUnmount:'DEFINE_MANY',UNSAFE_componentWillMount:'DEFINE_MANY',UNSAFE_componentWillReceiveProps:'DEFINE_MANY',UNSAFE_componentWillUpdate:'DEFINE_MANY',updateComponent:'OVERRIDE_BASE'},f={getDerivedStateFromProps:'DEFINE_MANY_MERGED'},h={displayName:function(t,n){t.displayName=n},mixins:function(t,n){if(n)for(var o=0;o<n.length;o++)N(t,n[o])},childContextTypes:function(n,o){n.childContextTypes=t({},n.childContextTypes,o)},contextTypes:function(n,o){n.contextTypes=t({},n.contextTypes,o)},getDefaultProps:function(t,n){t.getDefaultProps?t.getDefaultProps=M(t.getDefaultProps,n):t.getDefaultProps=n},propTypes:function(n,o){n.propTypes=t({},n.propTypes,o)},statics:function(t,n){_(t,n)},autobind:function(){}};function y(t,n){var s=E.hasOwnProperty(n)?E[n]:null;P.hasOwnProperty(n)&&o('OVERRIDE_BASE'===s,"ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.",n),t&&o('DEFINE_MANY'===s||'DEFINE_MANY_MERGED'===s,"ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.",n)}function N(t,n){if(n){o('function'!=typeof n,"ReactClass: You're attempting to use a component class or function as a mixin. Instead, just use a regular object."),o(!c(n),"ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.");var p=t.prototype,u=p.__reactAutoBindPairs;for(var l in n.hasOwnProperty(s)&&h.mixins(t,n.mixins),n)if(n.hasOwnProperty(l)&&l!==s){var f=n[l],N=p.hasOwnProperty(l);if(y(N,l),h.hasOwnProperty(l))h[l](t,f);else{var _=E.hasOwnProperty(l);if('function'!=typeof f||_||N||!1===n.autobind)if(N){var D=E[l];o(_&&('DEFINE_MANY_MERGED'===D||'DEFINE_MANY'===D),"ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.",D,l),'DEFINE_MANY_MERGED'===D?p[l]=M(p[l],f):'DEFINE_MANY'===D&&(p[l]=I(p[l],f))}else p[l]=f;else u.push(l,f),p[l]=f}}}}function _(t,n){if(n)for(var s in n){var p=n[s];if(n.hasOwnProperty(s)){if(o(!(s in h),"ReactClass: You are attempting to define a reserved property, `%s`, that shouldn't be on the \"statics\" key. Define it as an instance property instead; it will still be accessible on the constructor.",s),s in t){var c=f.hasOwnProperty(s)?f[s]:null;return o('DEFINE_MANY_MERGED'===c,"ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.",s),void(t[s]=M(t[s],p))}t[s]=p}}}function D(t,n){for(var s in o(t&&n&&'object'==typeof t&&'object'==typeof n,'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.'),n)n.hasOwnProperty(s)&&(o(void 0===t[s],"mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.",s),t[s]=n[s]);return t}function M(t,n){return function(){var o=t.apply(this,arguments),s=n.apply(this,arguments);if(null==o)return s;if(null==s)return o;var p={};return D(p,o),D(p,s),p}}function I(t,n){return function(){t.apply(this,arguments),n.apply(this,arguments)}}function A(t,n){return n.bind(t)}function F(t){for(var n=t.__reactAutoBindPairs,o=0;o<n.length;o+=2){var s=n[o],p=n[o+1];t[s]=A(t,p)}}var Y={componentDidMount:function(){this.__isMounted=!0}},v={componentWillUnmount:function(){this.__isMounted=!1}},P={replaceState:function(t,n){this.updater.enqueueReplaceState(this,t,n)},isMounted:function(){return!!this.__isMounted}},R=function(){};return t(R.prototype,p.prototype,P),function(t){var s=function(t,p,c){this.__reactAutoBindPairs.length&&F(this),this.props=t,this.context=p,this.refs=n,this.updater=c||u,this.state=null;var l=this.getInitialState?this.getInitialState():null;o('object'==typeof l&&!Array.isArray(l),'%s.getInitialState(): must return an object or null',s.displayName||'ReactCompositeComponent'),this.state=l};for(var p in s.prototype=new R,s.prototype.constructor=s,s.prototype.__reactAutoBindPairs=[],l.forEach(N.bind(null,s)),N(s,Y),N(s,t),N(s,v),s.getDefaultProps&&(s.defaultProps=s.getDefaultProps()),o(s.prototype.render,'createClass(...): Class specification must implement a `render` method.'),E)s.prototype[p]||(s.prototype[p]=null);return s}}},281,[15,282,283]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports={}},282,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=function(n){};m.exports=function(o,t,f,s,u,c,l,v){if(n(t),!o){var p;if(void 0===t)p=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var h=[f,s,u,c,l,v],w=0;(p=new Error(t.replace(/%s/g,function(){return h[w++]}))).name='Invariant Violation'}throw p.framesToPop=1,p}}},283,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);m.exports=function(s){n(!(s.delayPressIn<0||s.delayPressOut<0||s.delayLongPress<0),'Touchable components cannot have negative delay properties')}},284,[18]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports={DeprecatedAccessibilityRoles:['none','button','link','search','image','keyboardkey','text','adjustable','imagebutton','header','summary','alert','checkbox','combobox','menu','menubar','menuitem','progressbar','radio','radiogroup','scrollbar','spinbutton','switch','tab','tablist','timer','toolbar'],DeprecatedAccessibilityStates:['selected','disabled','checked','unchecked','busy','expanded','collapsed','hasPopup']}},285,[]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},286,[287]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),u=r(d[3]),p=r(d[4]),o=r(d[5]),c=r(d[6]),l=(function(c){function l(){return t(this,l),s(this,u(l).apply(this,arguments))}return p(l,c),n(l,[{key:"render",value:function(){var t=r(d[7]);return o.createElement(t,{style:[h.unimplementedView,this.props.style]},this.props.children)}}]),l})(o.Component),h=c.create({unimplementedView:{}});m.exports=l},287,[4,5,6,9,10,13,60,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),u=r(d[5]),p=r(d[6]),c=r(d[7]),h=r(d[8]),f=r(d[9]),v=(function(c){function v(){var n,l;t(this,v);for(var u=arguments.length,p=new Array(u),c=0;c<u;c++)p[c]=arguments[c];return(l=o(this,(n=s(v)).call.apply(n,[this].concat(p))))._picker=null,l._onChange=function(t){var n=t.nativeEvent.timestamp;l.props.onDateChange&&l.props.onDateChange(new Date(n)),l.props.onChange&&l.props.onChange(t)},l}return l(v,c),n(v,[{key:"componentDidUpdate",value:function(){if(this.props.date){var t=this.props.date.getTime();this._picker&&this._picker.setNativeProps({date:t})}}},{key:"render",value:function(){var t=this,n=this.props;return f(n.date||n.initialDate,'A selected date or initial date should be specified.'),p.createElement(h,{style:n.style},p.createElement(u,{testID:n.testID,ref:function(n){t._picker=n},style:D.datePickerIOS,date:n.date?n.date.getTime():n.initialDate?n.initialDate.getTime():void 0,locale:null!=n.locale&&''!==n.locale?n.locale:void 0,maximumDate:n.maximumDate?n.maximumDate.getTime():void 0,minimumDate:n.minimumDate?n.minimumDate.getTime():void 0,mode:n.mode,minuteInterval:n.minuteInterval,timeZoneOffsetInMinutes:n.timeZoneOffsetInMinutes,onChange:this._onChange,onStartShouldSetResponder:function(){return!0},onResponderTerminationRequest:function(){return!1}}))}}]),v})(p.Component);v.DefaultProps={mode:'datetime'};var D=c.create({datePickerIOS:{height:216}});m.exports=v},288,[4,5,6,9,10,289,13,60,88,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t('RCTDatePicker')},289,[182]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},290,[287]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),l=r(d[2]),s=r(d[3]),c=r(d[4]),o=r(d[5]),f=r(d[6]),u=r(d[7]),h=r(d[8]),v=r(d[9]),y=r(d[10]),p=(function(p){function R(){var t,n;l(this,R);for(var s=arguments.length,f=new Array(s),u=0;u<s;u++)f[u]=arguments[u];return(n=c(this,(t=o(R)).call.apply(t,[this].concat(f))))._viewRef=null,n._captureRef=function(t){n._viewRef=t},n}return f(R,p),s(R,[{key:"setNativeProps",value:function(t){var n=this._viewRef;n&&n.setNativeProps(t)}},{key:"render",value:function(){var l=this.props,s=l.children,c=l.style,o=l.imageStyle,f=l.imageRef,p=n(l,["children","style","imageStyle","imageRef"]);return h.createElement(y,{accessibilityIgnoresInvertColors:!0,style:c,ref:this._captureRef},h.createElement(u,t({},p,{style:[v.absoluteFill,{width:c.width,height:c.height},o],ref:f})),s)}}]),R})(h.Component);m.exports=p},291,[16,56,4,5,6,9,10,267,13,60,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),o=t(r(d[2])),s=t(r(d[3])),u=t(r(d[4])),l=t(r(d[5])),p=t(r(d[6])),c=(r(d[7]),r(d[8]),r(d[9])),h=r(d[10]),f=(function(t){function h(){return(0,n.default)(this,h),(0,s.default)(this,(0,u.default)(h).apply(this,arguments))}return(0,l.default)(h,t),(0,o.default)(h,[{key:"render",value:function(){return 0===c.Children.count(this.props.children)?null:c.createElement(p.default,{style:[this.props.style,v.container],nativeID:this.props.nativeID,backgroundColor:this.props.backgroundColor},this.props.children)}}]),h})(c.Component),v=h.create({container:{position:'absolute'}});m.exports=f},292,[3,4,5,6,9,10,293,66,58,13,60]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('RCTInputAccessoryView');e.default=u},293,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),u=r(d[5]),h=r(d[6]),c=r(d[7]),f=r(d[8]),y=(r(d[9]),r(d[10])),b=r(d[11]),v=r(d[12]),_=(function(_){function p(t){var n;return o(this,p),(n=l(this,u(p).call(this,t)))._frame=null,n._subscriptions=[],n._initialFrameHeight=0,n._onKeyboardChange=function(t){if(null!=t){var o=t.duration,s=t.easing,l=t.endCoordinates,u=n._relativeKeyboardHeight(l);n.state.bottom!==u&&(o&&s&&f.configureNext({duration:o>10?o:10,update:{duration:o>10?o:10,type:f.Types[s]||'keyboard'}}),n.setState({bottom:u}))}else n.setState({bottom:0})},n._onLayout=function(t){n._frame=t.nativeEvent.layout,n._initialFrameHeight||(n._initialFrameHeight=n._frame.height)},n.state={bottom:0},n.viewRef=y.createRef(),n}return h(p,_),s(p,[{key:"_relativeKeyboardHeight",value:function(t){var n=this._frame;if(!n||!t)return 0;var o=t.screenY-this.props.keyboardVerticalOffset;return Math.max(n.y+n.height-o,0)}},{key:"componentDidMount",value:function(){this._subscriptions=[c.addListener('keyboardWillChangeFrame',this._onKeyboardChange)]}},{key:"componentWillUnmount",value:function(){this._subscriptions.forEach(function(t){t.remove()})}},{key:"render",value:function(){var o=this.props,s=o.behavior,l=o.children,u=o.contentContainerStyle,h=o.enabled,c=(o.keyboardVerticalOffset,o.style),f=n(o,["behavior","children","contentContainerStyle","enabled","keyboardVerticalOffset","style"]),_=h?this.state.bottom:0;switch(s){case'height':var p;return null!=this._frame&&this.state.bottom>0&&(p={height:this._initialFrameHeight-_,flex:0}),y.createElement(v,t({ref:this.viewRef,style:b.compose(c,p),onLayout:this._onLayout},f),l);case'position':return y.createElement(v,t({ref:this.viewRef,style:c,onLayout:this._onLayout},f),y.createElement(v,{style:b.compose(u,{bottom:_})},l));case'padding':return y.createElement(v,t({ref:this.viewRef,style:b.compose(c,{paddingBottom:_}),onLayout:this._onLayout},f),l);default:return y.createElement(v,t({ref:this.viewRef,onLayout:this._onLayout,style:c},f),l)}}}]),p})(y.Component);_.defaultProps={enabled:!0,keyboardVerticalOffset:0},m.exports=_},294,[16,56,4,5,6,9,10,257,259,58,13,60,88]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=n(r(d[1])),l=n(r(d[2])),s=n(r(d[3])),o=n(r(d[4])),u=n(r(d[5])),c=n(r(d[6])),f=n(r(d[7])),h=r(d[8]),p=r(d[9]),k=r(d[10]),v=(function(n){function v(){var n,t;(0,l.default)(this,v);for(var s=arguments.length,c=new Array(s),f=0;f<s;f++)c[f]=arguments[f];return(t=(0,o.default)(this,(n=(0,u.default)(v)).call.apply(n,[this].concat(c))))._hasWarnedInvalidRenderMask=!1,t}return(0,c.default)(v,n),(0,s.default)(v,[{key:"render",value:function(){var n=this.props,l=n.maskElement,s=n.children,o=(0,t.default)(n,["maskElement","children"]);return h.isValidElement(l)?h.createElement(f.default,o,h.createElement(k,{pointerEvents:"none",style:p.absoluteFill},l),s):(this._hasWarnedInvalidRenderMask||(console.warn("MaskedView: Invalid `maskElement` prop was passed to MaskedView. Expected a React Element. No mask will render."),this._hasWarnedInvalidRenderMask=!0),h.createElement(k,o,s))}}]),v})(h.Component);m.exports=v},295,[3,56,4,5,6,9,10,296,13,60,88]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('RCTMaskedView');e.default=u},296,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n=r(d[0]),o=n(r(d[1])),s=n(r(d[2])),l=n(r(d[3])),p=n(r(d[4])),u=n(r(d[5])),c=n(r(d[6])),h=n(r(d[7])),f=n(r(d[8])),v=(r(d[9]),r(d[10])),y=r(d[11]),S=(r(d[12]),r(d[13])),_=r(d[14]),C=r(d[15]),b=r(d[16]),w=r(d[17]),k=null!=h.default?new y(h.default):null,R=0,x=(function(t){function n(t){var o;return(0,s.default)(this,n),o=(0,p.default)(this,(0,u.default)(n).call(this,t)),n._confirmProps(t),o._identifier=R++,o}return(0,c.default)(n,t),(0,l.default)(n,[{key:"getChildContext",value:function(){return{virtualizedList:null}}},{key:"componentDidMount",value:function(){var t=this;k&&(this._eventSubscription=k.addListener('modalDismissed',function(n){n.modalID===t._identifier&&t.props.onDismiss&&t.props.onDismiss()}))}},{key:"componentWillUnmount",value:function(){this._eventSubscription&&this._eventSubscription.remove()}},{key:"UNSAFE_componentWillReceiveProps",value:function(t){n._confirmProps(t)}},{key:"render",value:function(){if(!0!==this.props.visible)return null;var t={backgroundColor:this.props.transparent?'transparent':'white'},n=this.props.animationType;n||(n='none',this.props.animated&&(n='slide'));var o=this.props.presentationStyle;o||(o='fullScreen',this.props.transparent&&(o='overFullScreen'));var s=this.props.children;return S.createElement(f.default,{animationType:n,presentationStyle:o,transparent:this.props.transparent,hardwareAccelerated:this.props.hardwareAccelerated,onRequestClose:this.props.onRequestClose,onShow:this.props.onShow,identifier:this._identifier,style:T.modal,onStartShouldSetResponder:this._shouldSetResponder,supportedOrientations:this.props.supportedOrientations,onOrientationChange:this.props.onOrientationChange},S.createElement(C.Context.Provider,{value:null},S.createElement(w,{style:[T.container,t]},s)))}},{key:"_shouldSetResponder",value:function(){return!0}}],[{key:"_confirmProps",value:function(t){t.presentationStyle&&'overFullScreen'!==t.presentationStyle&&t.transparent&&console.warn("Modal with '"+t.presentationStyle+"' presentation style and 'transparent' value is not supported.")}}]),n})(S.Component);x.defaultProps={visible:!0,hardwareAccelerated:!1},x.contextTypes={rootTag:_.number},x.childContextTypes={virtualizedList:_.object};var P=v.getConstants().isRTL?'right':'left',T=b.create({modal:{position:'absolute'},container:(t={},(0,o.default)(t,P,0),(0,o.default)(t,"top",0),(0,o.default)(t,"flex",1),t)});m.exports=x},297,[3,55,4,5,6,9,10,298,299,300,302,123,58,13,69,253,60,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('ModalManager');e.default=u},298,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=(0,t(r(d[1])).default)('ModalHostView',{interfaceOnly:!0,paperComponentName:'RCTModalHostView'});e.default=o},299,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),l=r(d[4]),p=(r(d[5]),r(d[6])),u=(r(d[7]),r(d[8])),c=(r(d[9]),r(d[10])),h=r(d[11]),v=r(d[12]),f=(function(p){function h(){var n,l;t(this,h);for(var p=arguments.length,u=new Array(p),c=0;c<p;c++)u[c]=arguments[c];return(l=o(this,(n=s(h)).call.apply(n,[this].concat(u)))).state={inspector:null,mainKey:1},l._subscription=null,l}return l(h,p),n(h,[{key:"getChildContext",value:function(){return{rootTag:this.props.rootTag}}},{key:"componentDidMount",value:function(){}},{key:"componentWillUnmount",value:function(){null!=this._subscription&&this._subscription.remove()}},{key:"render",value:function(){var t=this,n=u.createElement(v,{collapsable:!this.state.inspector,key:this.state.mainKey,pointerEvents:"box-none",style:y.appContainer,ref:function(n){t._mainRef=n}},this.props.children),o=this.props.WrapperComponent;return null!=o&&(n=u.createElement(o,null,n)),u.createElement(c.Provider,{value:this.props.rootTag},u.createElement(v,{style:y.appContainer,pointerEvents:"box-none"},n,null,this.state.inspector))}}]),h})(u.Component);f.childContextTypes={rootTag:p.number};var y=h.create({appContainer:{flex:1}});m.exports=f},300,[4,5,6,9,10,50,69,46,13,90,301,60,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t.createContext(0)},301,[13]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),n=t.default?t.default.getConstants():{isRTL:!1,doLeftAndRightSwapInRTL:!0};m.exports={getConstants:function(){return n},allowRTL:function(n){t.default&&t.default.allowRTL(n)},forceRTL:function(n){t.default&&t.default.forceRTL(n)},swapLeftAndRightInRTL:function(n){t.default&&t.default.swapLeftAndRightInRTL(n)},isRTL:n.isRTL,doLeftAndRightSwapInRTL:n.doLeftAndRightSwapInRTL}},302,[3,303]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('I18nManager');e.default=u},303,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),u=r(d[3]),s=r(d[4]),p=(r(d[5]),r(d[6])),l=(r(d[7]),r(d[8])),c=(r(d[9]),(function(p){function l(){return t(this,l),o(this,u(l).apply(this,arguments))}return s(l,p),n(l,[{key:"render",value:function(){throw null}}]),l})(l.Component)),h=(function(c){function h(){return t(this,h),o(this,u(h).apply(this,arguments))}return s(h,c),n(h,[{key:"render",value:function(){return l.createElement(p,this.props,this.props.children)}}]),h})(l.Component);h.MODE_DIALOG="dialog",h.MODE_DROPDOWN='dropdown',h.Item=c,h.defaultProps={mode:"dialog"},m.exports=h},304,[4,5,6,9,10,305,306,58,13,287]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},305,[287]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),l=r(d[4]),p=r(d[5]),c=r(d[6]),u=(r(d[7]),r(d[8])),h=r(d[9]),v=r(d[10]),f=(function(u){function f(){var n,l;t(this,f);for(var p=arguments.length,c=new Array(p),u=0;u<p;u++)c[u]=arguments[u];return(l=s(this,(n=o(f)).call.apply(n,[this].concat(c))))._picker=null,l.state={selectedIndex:0,items:[]},l._onChange=function(t){l.props.onChange&&l.props.onChange(t),l.props.onValueChange&&l.props.onValueChange(t.nativeEvent.newValue,t.nativeEvent.newIndex),l._picker&&l.state.selectedIndex!==t.nativeEvent.newIndex&&l._picker.setNativeProps({selectedIndex:l.state.selectedIndex})},l}return l(f,u),n(f,[{key:"render",value:function(){var t=this;return c.createElement(h,{style:this.props.style},c.createElement(p,{ref:function(n){t._picker=n},testID:this.props.testID,style:[I.pickerIOS,this.props.itemStyle],items:this.state.items,selectedIndex:this.state.selectedIndex,onChange:this._onChange,onStartShouldSetResponder:function(){return!0},onResponderTerminationRequest:function(){return!1}}))}}],[{key:"getDerivedStateFromProps",value:function(t){var n=0,s=[];return c.Children.toArray(t.children).filter(function(t){return null!==t}).forEach(function(o,l){o.props.value===t.selectedValue&&(n=l),s.push({value:o.props.value,label:o.props.label,textColor:v(o.props.color)})}),{selectedIndex:n,items:s}}}]),f})(c.Component);f.Item=function(t){return null};var I=u.create({pickerIOS:{height:216}});m.exports=f},306,[4,5,6,9,10,307,13,90,60,88,82]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);m.exports=t('RCTPicker')},307,[182]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=r(d[0])},308,[287]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=t(r(d[1])),f=t(r(d[2])),n=r(d[3]),o=r(d[4]).create({progressView:{height:2}}),u=n.forwardRef(function(t,u){return n.createElement(f.default,(0,s.default)({},t,{style:[o.progressView,t.style],ref:u}))});m.exports=u},309,[3,16,310,13,60]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('RCTProgressView');e.default=u},310,[3,185]); -__d(function(g,r,i,a,m,e,d){var t,f=r(d[0]),n=(r(d[1]),r(d[2]),r(d[3])),u=(r(d[4]),r(d[5]).default),l=n.forwardRef(function(t,l){return n.createElement(u,f({emulateUnlessSupported:!0},t,{ref:l}))});l.displayName='SafeAreaView',t=l,m.exports=t},311,[16,56,58,13,88,312]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('RCTSafeAreaView');e.default=u},312,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]),o=t(r(d[2])),l=t(r(d[3])),u=t(r(d[4])),f=t(r(d[5])),s=t(r(d[6])),h=t(r(d[7])),p=t(r(d[8])),c=n(r(d[9])),C=t(r(d[10])),v=t(r(d[11])),y=(function(n){function t(){var n,o;(0,u.default)(this,t);for(var l=arguments.length,f=new Array(l),p=0;p<l;p++)f[p]=arguments[p];return(o=(0,s.default)(this,(n=(0,h.default)(t)).call.apply(n,[this].concat(f))))._onChange=function(n){o.props.onChange&&o.props.onChange(n),o.props.onValueChange&&o.props.onValueChange(n.nativeEvent.value)},o}return(0,p.default)(t,n),(0,f.default)(t,[{key:"render",value:function(){var n=this.props,t=n.forwardedRef,u=(n.onValueChange,n.style),f=(0,l.default)(n,["forwardedRef","onValueChange","style"]);return c.createElement(v.default,(0,o.default)({},f,{ref:t,style:[w.segmentedControl,u],onChange:this._onChange}))}}]),t})(c.Component);y.defaultProps={values:[],enabled:!0};var w=C.default.create({segmentedControl:{height:28}}),R=c.forwardRef(function(n,t){return c.createElement(y,(0,o.default)({},n,{forwardedRef:t}))});m.exports=R},313,[2,3,16,56,4,5,6,9,10,13,60,314]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('RCTSegmentedControl');e.default=u},314,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,t=r(d[0]),l=t(r(d[1])),u=t(r(d[2])),o=t(r(d[3])),s=(r(d[4]),r(d[5])),f=(r(d[6]),r(d[7])),c=s.forwardRef(function(t,c){var p=f.compose(n.slider,t.style),v=t.onValueChange,h=t.onSlidingComplete,C=(0,u.default)(t,["onValueChange","onSlidingComplete"]),S=v?function(n){v(n.nativeEvent.value)}:null,V=S,R=h?function(n){h(n.nativeEvent.value)}:null;return s.createElement(o.default,(0,l.default)({},C,{ref:c,style:p,onChange:V,onSlidingComplete:R,onValueChange:S,enabled:!t.disabled,onStartShouldSetResponder:function(){return!0},onResponderTerminationRequest:function(){return!1}}))});c.defaultProps={disabled:!1,value:0,minimumValue:0,maximumValue:1,step:0},n=f.create({slider:{height:40}}),m.exports=c},315,[3,16,56,316,58,13,90,60]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=(0,t(r(d[1])).default)('Slider',{interfaceOnly:!0,paperComponentName:'RCTSlider'});e.default=l},316,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),o=n(r(d[1])),t=n(r(d[2])),l=n(r(d[3])),u=n(r(d[4])),s=n(r(d[5])),h=n(r(d[6])),c=n(r(d[7])),C=n(r(d[8])),f=(r(d[9]),r(d[10]),r(d[11])),p=r(d[12]),v=(function(n){function v(){var n,o;(0,l.default)(this,v);for(var t=arguments.length,u=new Array(t),c=0;c<t;c++)u[c]=arguments[c];return(o=(0,s.default)(this,(n=(0,h.default)(v)).call.apply(n,[this].concat(u))))._handleChange=function(n){if(null!=o._nativeSwitchRef){var t=!0===o.props.value;o._nativeSwitchRef.setNativeProps({value:t}),null!=o.props.onChange&&o.props.onChange(n),null!=o.props.onValueChange&&o.props.onValueChange(n.nativeEvent.value)}},o._handleSwitchNativeComponentRef=function(n){o._nativeSwitchRef=n},o}return(0,c.default)(v,n),(0,u.default)(v,[{key:"render",value:function(){var n,l=this.props,u=l.disabled,s=l.ios_backgroundColor,h=(l.onChange,l.onValueChange,l.style),c=l.thumbColor,v=l.trackColor,_=l.value,y=(0,t.default)(l,["disabled","ios_backgroundColor","onChange","onValueChange","style","thumbColor","trackColor","value"]),S=c,w=null==v?void 0:v.false,k=null==v?void 0:v.true,T=y,V=T.thumbTintColor,N=T.tintColor,E=T.onTintColor;null!=V&&(S=V),null!=N&&(w=N),null!=E&&(k=E);var q={disabled:u,onTintColor:k,style:p.compose({height:31,width:51},p.compose(h,null==s?null:{backgroundColor:s,borderRadius:16})),thumbTintColor:S,tintColor:w,value:!0===_};return f.createElement(C.default,(0,o.default)({},y,q,{accessibilityRole:null!=(n=y.accessibilityRole)?n:'button',onChange:this._handleChange,onResponderTerminationRequest:b,onStartShouldSetResponder:R,ref:this._handleSwitchNativeComponentRef}))}}]),v})(f.Component),b=function(){return!1},R=function(){return!0};m.exports=v},317,[3,16,56,4,5,6,9,10,318,319,58,13,60]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=(0,t(r(d[1])).default)('Switch',{paperComponentName:'RCTSwitch'});e.default=u},318,[3,185]); -__d(function(g,r,i,a,m,e,d){'use strict';r(d[0]).NativeComponent;var t=r(d[1]);m.exports=t('AndroidSwitch')},319,[90,182]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),l=t(r(d[2])),o=t(r(d[3])),u=t(r(d[4])),c=t(r(d[5])),s=t(r(d[6])),p=t(r(d[7])),k=(r(d[8]),r(d[9]));r(d[10]);function y(t){return{backgroundColor:null!=t.backgroundColor?{value:t.backgroundColor,animated:t.animated}:null,barStyle:null!=t.barStyle?{value:t.barStyle,animated:t.animated}:null,translucent:t.translucent,hidden:null!=t.hidden?{value:t.hidden,animated:t.animated,transition:t.showHideTransition}:null,networkActivityIndicatorVisible:t.networkActivityIndicatorVisible}}var f=(function(t){function s(){var t,l;(0,n.default)(this,s);for(var c=arguments.length,p=new Array(c),k=0;k<c;k++)p[k]=arguments[k];return(l=(0,o.default)(this,(t=(0,u.default)(s)).call.apply(t,[this].concat(p))))._stackEntry=null,l}return(0,c.default)(s,t),(0,l.default)(s,[{key:"componentDidMount",value:function(){this._stackEntry=s.pushStackEntry(this.props)}},{key:"componentWillUnmount",value:function(){s.popStackEntry(this._stackEntry)}},{key:"componentDidUpdate",value:function(){this._stackEntry=s.replaceStackEntry(this._stackEntry,this.props)}},{key:"render",value:function(){return null}}],[{key:"setHidden",value:function(t,n){n=n||'none',s._defaultProps.hidden.value=t,p.default.setHidden(t,n)}},{key:"setBarStyle",value:function(t,n){n=n||!1,s._defaultProps.barStyle.value=t,p.default.setStyle(t,n)}},{key:"setNetworkActivityIndicatorVisible",value:function(t){s._defaultProps.networkActivityIndicatorVisible=t,p.default.setNetworkActivityIndicatorVisible(t)}},{key:"setBackgroundColor",value:function(t,n){console.warn('`setBackgroundColor` is only available on Android')}},{key:"setTranslucent",value:function(t){console.warn('`setTranslucent` is only available on Android')}},{key:"pushStackEntry",value:function(t){var n=y(t);return s._propsStack.push(n),s._updatePropsStack(),n}},{key:"popStackEntry",value:function(t){var n=s._propsStack.indexOf(t);-1!==n&&s._propsStack.splice(n,1),s._updatePropsStack()}},{key:"replaceStackEntry",value:function(t,n){var l=y(n),o=s._propsStack.indexOf(t);return-1!==o&&(s._propsStack[o]=l),s._updatePropsStack(),l}}]),s})(k.Component);f._propsStack=[],f._defaultProps=y({animated:!1,showHideTransition:'fade',backgroundColor:'black',barStyle:'default',translucent:!1,hidden:!1,networkActivityIndicatorVisible:!1}),f._updateImmediate=null,f._currentValues=null,f.currentHeight=p.default.getConstants().HEIGHT,f.defaultProps={animated:!1,showHideTransition:'fade'},f._updatePropsStack=function(){clearImmediate(f._updateImmediate),f._updateImmediate=setImmediate(function(){var t,n,l=f._currentValues,o=(t=f._propsStack,n=f._defaultProps,t.reduce(function(t,n){for(var l in n)null!=n[l]&&(t[l]=n[l]);return t},(0,s.default)({},n)));l&&l.barStyle.value===o.barStyle.value||p.default.setStyle(o.barStyle.value,o.barStyle.animated||!1),l&&l.hidden.value===o.hidden.value||p.default.setHidden(o.hidden.value,o.hidden.animated?o.hidden.transition:'none'),l&&l.networkActivityIndicatorVisible===o.networkActivityIndicatorVisible||p.default.setNetworkActivityIndicatorVisible(o.networkActivityIndicatorVisible),f._currentValues=o})},m.exports=f},320,[3,4,5,6,9,10,16,321,58,13,82]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).getEnforcing('StatusBarManager');e.default=u},321,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,n,s=r(d[0]),o=r(d[1]),l=r(d[2]),c=r(d[3]),p=r(d[4]),u=r(d[5]),h=r(d[6]),f=(r(d[7]),r(d[8])),_=(r(d[9]),r(d[10])),S=r(d[11]),v=r(d[12]),y=r(d[13]),b=r(d[14]),C=r(d[15]),I=r(d[16]),x=r(d[17]),T=r(d[18]),R=r(d[19]),F=r(d[20]);r(d[21]);t=F('RCTMultilineTextInputView'),n=F('RCTSinglelineTextInputView');var N=function(){return!0},D=T({displayName:'TextInput',statics:{State:{currentlyFocusedField:C.currentlyFocusedField,focusTextInput:C.focusTextInput,blurTextInput:C.blurTextInput}},propTypes:h,getDefaultProps:function(){return{allowFontScaling:!0,rejectResponderTermination:!0,underlineColorAndroid:'transparent'}},mixins:[f],isFocused:function(){return C.currentlyFocusedField()===S.findNodeHandle(this._inputRef)},_inputRef:void 0,_focusSubscription:void 0,_lastNativeText:void 0,_lastNativeSelection:void 0,_rafId:null,componentDidMount:function(){this._lastNativeText=this.props.value;var t=S.findNodeHandle(this._inputRef);null!=t&&C.registerInput(t),this.props.autoFocus&&(this._rafId=requestAnimationFrame(this.focus))},componentWillUnmount:function(){this._focusSubscription&&this._focusSubscription.remove(),this.isFocused()&&this.blur();var t=S.findNodeHandle(this._inputRef);null!=t&&C.unregisterInput(t),null!=this._rafId&&cancelAnimationFrame(this._rafId)},clear:function(){this.setNativeProps({text:''})},render:function(){var t;return t=x.getViewManagerConfig('RCTVirtualText')?this._renderIOS():this._renderIOSLegacy(),_.createElement(b.Provider,{value:!0},t)},_getText:function(){return'string'==typeof this.props.value?this.props.value:'string'==typeof this.props.defaultValue?this.props.defaultValue:''},_setNativeRef:function(t){this._inputRef=t},_renderIOSLegacy:function(){var s,o=u({},this.props);if(o.style=[this.props.style],o.selection&&null==o.selection.end&&(o.selection={start:o.selection.start,end:o.selection.start}),o.multiline){var l=o.children,c=0;_.Children.forEach(l,function(){return++c}),R(!(o.value&&c),'Cannot specify both value and children.'),c>=1&&(l=_.createElement(y,{style:o.style,allowFontScaling:o.allowFontScaling,maxFontSizeMultiplier:o.maxFontSizeMultiplier},l)),o.inputView&&(l=[l,o.inputView]),o.style.unshift(L.multilineInput),s=_.createElement(t,u({ref:this._setNativeRef},o,{children:l,onFocus:this._onFocus,onBlur:this._onBlur,onChange:this._onChange,onContentSizeChange:this.props.onContentSizeChange,onSelectionChange:this._onSelectionChange,onTextInput:this._onTextInput,onSelectionChangeShouldSetResponder:N,text:this._getText(),dataDetectorTypes:this.props.dataDetectorTypes,onScroll:this._onScroll}))}else s=_.createElement(n,u({ref:this._setNativeRef},o,{onFocus:this._onFocus,onBlur:this._onBlur,onChange:this._onChange,onSelectionChange:this._onSelectionChange,onSelectionChangeShouldSetResponder:N,text:this._getText()}));return _.createElement(I,{onLayout:o.onLayout,onPress:this._onPress,rejectResponderTermination:!0,accessible:o.accessible,accessibilityLabel:o.accessibilityLabel,accessibilityRole:o.accessibilityRole,accessibilityStates:o.accessibilityStates,accessibilityState:o.accessibilityState,nativeID:this.props.nativeID,testID:o.testID},s)},_renderIOS:function(){var s=u({},this.props);s.style=[this.props.style],s.selection&&null==s.selection.end&&(s.selection={start:s.selection.start,end:s.selection.start});var o=s.multiline?t:n;s.multiline&&s.style.unshift(L.multilineInput);var l=_.createElement(o,u({ref:this._setNativeRef},s,{onFocus:this._onFocus,onBlur:this._onBlur,onChange:this._onChange,onContentSizeChange:this.props.onContentSizeChange,onSelectionChange:this._onSelectionChange,onTextInput:this._onTextInput,onSelectionChangeShouldSetResponder:N,text:this._getText(),dataDetectorTypes:this.props.dataDetectorTypes,onScroll:this._onScroll}));return _.createElement(I,{onLayout:s.onLayout,onPress:this._onPress,rejectResponderTermination:s.rejectResponderTermination,accessible:s.accessible,accessibilityLabel:s.accessibilityLabel,accessibilityRole:s.accessibilityRole,accessibilityStates:s.accessibilityStates,accessibilityState:s.accessibilityState,nativeID:this.props.nativeID,testID:s.testID},l)},_renderAndroid:function(){var t=u({},this.props);t.style=[this.props.style],t.autoCapitalize=t.autoCapitalize||'sentences';var n=this.props.children,s=0;_.Children.forEach(n,function(){return++s}),R(!(this.props.value&&s),'Cannot specify both value and children.'),s>1&&(n=_.createElement(y,null,n)),t.selection&&null==t.selection.end&&(t.selection={start:t.selection.start,end:t.selection.start});var o=_.createElement(void 0,u({ref:this._setNativeRef},t,{mostRecentEventCount:0,onFocus:this._onFocus,onBlur:this._onBlur,onChange:this._onChange,onSelectionChange:this._onSelectionChange,onTextInput:this._onTextInput,text:this._getText(),children:n,disableFullscreenUI:this.props.disableFullscreenUI,textBreakStrategy:this.props.textBreakStrategy,onScroll:this._onScroll}));return _.createElement(I,{onLayout:t.onLayout,onPress:this._onPress,accessible:this.props.accessible,accessibilityLabel:this.props.accessibilityLabel,accessibilityRole:this.props.accessibilityRole,accessibilityStates:this.props.accessibilityStates,accessibilityState:this.props.accessibilityState,nativeID:this.props.nativeID,testID:this.props.testID},o)},_onFocus:function(t){this.props.onFocus&&this.props.onFocus(t),this.props.selectionState&&this.props.selectionState.focus()},_onPress:function(t){(this.props.editable||void 0===this.props.editable)&&this.focus()},_onChange:function(t){this._inputRef&&this._inputRef.setNativeProps&&S.setNativeProps(this._inputRef,{mostRecentEventCount:t.nativeEvent.eventCount});var n=t.nativeEvent.text;this.props.onChange&&this.props.onChange(t),this.props.onChangeText&&this.props.onChangeText(n),this._inputRef&&(this._lastNativeText=n,this.forceUpdate())},_onSelectionChange:function(t){this.props.onSelectionChange&&this.props.onSelectionChange(t),this._inputRef&&(this._lastNativeSelection=t.nativeEvent.selection,(this.props.selection||this.props.selectionState)&&this.forceUpdate())},componentDidUpdate:function(){var t={};this._lastNativeText!==this.props.value&&'string'==typeof this.props.value&&(t.text=this.props.value);var n=this.props.selection;this._lastNativeSelection&&n&&(this._lastNativeSelection.start!==n.start||this._lastNativeSelection.end!==n.end)&&(t.selection=this.props.selection),Object.keys(t).length>0&&this._inputRef&&this._inputRef.setNativeProps&&S.setNativeProps(this._inputRef,t),this.props.selectionState&&n&&this.props.selectionState.update(n.start,n.end)},_onBlur:function(t){this.blur(),this.props.onBlur&&this.props.onBlur(t),this.props.selectionState&&this.props.selectionState.blur()},_onTextInput:function(t){this.props.onTextInput&&this.props.onTextInput(t)},_onScroll:function(t){this.props.onScroll&&this.props.onScroll(t)}}),E=((function(t){function n(){return s(this,n),l(this,c(n).apply(this,arguments))}p(n,t),o(n,[{key:"clear",value:function(){}},{key:"isFocused",value:function(){}}])})(S.NativeComponent),D),L=v.create({multilineInput:{paddingTop:5}});m.exports=E},322,[4,5,6,9,10,16,323,326,278,58,13,90,60,197,202,163,279,75,280,18,182,20]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),o=r(d[1]),t=r(d[2]),l=r(d[3]),s=r(d[4]),c=r(d[5]),u=['phoneNumber','link','address','calendarEvent','none','all'];m.exports=n({},l,{autoCapitalize:o.oneOf(['none','sentences','words','characters']),autoCompleteType:o.oneOf(['cc-csc','cc-exp','cc-exp-month','cc-exp-year','cc-number','email','name','password','postal-code','street-address','tel','username','off']),autoCorrect:o.bool,spellCheck:o.bool,autoFocus:o.bool,allowFontScaling:o.bool,maxFontSizeMultiplier:o.number,editable:o.bool,keyboardType:o.oneOf(['default','email-address','numeric','phone-pad','number-pad','ascii-capable','numbers-and-punctuation','url','name-phone-pad','decimal-pad','twitter','web-search','visible-password']),keyboardAppearance:o.oneOf(['default','light','dark']),returnKeyType:o.oneOf(['done','go','next','search','send','none','previous','default','emergency-call','google','join','route','yahoo']),returnKeyLabel:o.string,maxLength:o.number,numberOfLines:o.number,disableFullscreenUI:o.bool,enablesReturnKeyAutomatically:o.bool,multiline:o.bool,textBreakStrategy:o.oneOf(['simple','highQuality','balanced']),onBlur:o.func,onFocus:o.func,onChange:o.func,onChangeText:o.func,onContentSizeChange:o.func,onTextInput:o.func,onEndEditing:o.func,onSelectionChange:o.func,onSubmitEditing:o.func,onKeyPress:o.func,onLayout:o.func,onScroll:o.func,placeholder:o.string,placeholderTextColor:t,scrollEnabled:o.bool,secureTextEntry:o.bool,selectionColor:t,selectionState:o.instanceOf(s),selection:o.shape({start:o.number.isRequired,end:o.number}),value:o.string,defaultValue:o.string,clearButtonMode:o.oneOf(['never','while-editing','unless-editing','always']),clearTextOnFocus:o.bool,selectTextOnFocus:o.bool,blurOnSubmit:o.bool,style:c.propTypes.style,underlineColorAndroid:t,inlineImageLeft:o.string,inlineImagePadding:o.number,rejectResponderTermination:o.bool,dataDetectorTypes:o.oneOfType([o.oneOf(u),o.arrayOf(o.oneOf(u))]),caretHidden:o.bool,contextMenuHidden:o.bool,inputAccessoryViewID:o.string,textContentType:o.oneOf(['none','URL','addressCity','addressCityAndState','addressState','countryName','creditCardNumber','emailAddress','familyName','fullStreetAddress','givenName','jobTitle','location','middleName','name','namePrefix','nameSuffix','nickname','organizationName','postalCode','streetAddressLine1','streetAddressLine2','sublocality','telephoneNumber','username','password','newPassword','oneTimeCode']),showSoftInputOnFocus:o.bool})},323,[54,69,66,324,326,197]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),n=r(d[1]),s=r(d[2]),t=r(d[3]),c=r(d[4]),l=r(d[5]),b=r(d[6]),u=b.DeprecatedAccessibilityRoles,p=b.DeprecatedAccessibilityStates,f=s(t);m.exports=o({accessible:l.bool,accessibilityLabel:l.node,accessibilityHint:l.string,accessibilityActions:l.arrayOf(l.string),accessibilityIgnoresInvertColors:l.bool,accessibilityRole:l.oneOf(u),accessibilityStates:l.arrayOf(l.oneOf(p)),accessibilityState:l.object,accessibilityLiveRegion:l.oneOf(['none','polite','assertive']),importantForAccessibility:l.oneOf(['auto','yes','no','no-hide-descendants']),accessibilityViewIsModal:l.bool,accessibilityElementsHidden:l.bool,onAccessibilityAction:l.func,onAccessibilityTap:l.func,onMagicTap:l.func,testID:l.string,nativeID:l.string,onResponderGrant:l.func,onResponderMove:l.func,onResponderReject:l.func,onResponderRelease:l.func,onResponderTerminate:l.func,onResponderTerminationRequest:l.func,onStartShouldSetResponder:l.func,onStartShouldSetResponderCapture:l.func,onMoveShouldSetResponder:l.func,onMoveShouldSetResponderCapture:l.func,hitSlop:n,onLayout:l.func,pointerEvents:l.oneOf(['box-none','none','box-only','auto']),style:f,removeClippedSubviews:l.bool,renderToHardwareTextureAndroid:l.bool,shouldRasterizeIOS:l.bool,collapsable:l.bool,needsOffscreenAlphaCompositing:l.bool},c)},324,[54,199,200,81,325,69,285]); -__d(function(g,r,i,a,m,e,d){'use strict'},325,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),f=r(d[2]),u=(function(){function f(s,u){t(this,f),this._anchorOffset=s,this._focusOffset=u,this._hasFocus=!1}return s(f,[{key:"update",value:function(t,s){this._anchorOffset===t&&this._focusOffset===s||(this._anchorOffset=t,this._focusOffset=s,this.emit('update'))}},{key:"constrainLength",value:function(t){this.update(Math.min(this._anchorOffset,t),Math.min(this._focusOffset,t))}},{key:"focus",value:function(){this._hasFocus||(this._hasFocus=!0,this.emit('focus'))}},{key:"blur",value:function(){this._hasFocus&&(this._hasFocus=!1,this.emit('blur'))}},{key:"hasFocus",value:function(){return this._hasFocus}},{key:"isCollapsed",value:function(){return this._anchorOffset===this._focusOffset}},{key:"isBackward",value:function(){return this._anchorOffset>this._focusOffset}},{key:"getAnchorOffset",value:function(){return this._hasFocus?this._anchorOffset:null}},{key:"getFocusOffset",value:function(){return this._hasFocus?this._focusOffset:null}},{key:"getStartOffset",value:function(){return this._hasFocus?Math.min(this._anchorOffset,this._focusOffset):null}},{key:"getEndOffset",value:function(){return this._hasFocus?Math.max(this._anchorOffset,this._focusOffset):null}},{key:"overlaps",value:function(t,s){return this.hasFocus()&&this.getStartOffset()<=s&&t<=this.getEndOffset()}}]),f})();f(u,{blur:!0,focus:!0,update:!0}),m.exports=u},326,[4,5,327]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),_=r(d[3]),o=r(d[4]),v=r(d[5])({__types:!0});var E={emit:function(t,n,s,_,o,v,E){return this.__getEventEmitter().emit(t,n,s,_,o,v,E)},emitAndHold:function(t,n,s,_,o,v,E){return this.__getEventEmitter().emitAndHold(t,n,s,_,o,v,E)},addListener:function(t,n,s){return this.__getEventEmitter().addListener(t,n,s)},once:function(t,n,s){return this.__getEventEmitter().once(t,n,s)},addRetroactiveListener:function(t,n,s){return this.__getEventEmitter().addRetroactiveListener(t,n,s)},addListenerMap:function(t,n){return this.__getEventEmitter().addListenerMap(t,n)},addRetroactiveListenerMap:function(t,n){return this.__getEventEmitter().addListenerMap(t,n)},removeAllListeners:function(){this.__getEventEmitter().removeAllListeners()},removeCurrentListener:function(){this.__getEventEmitter().removeCurrentListener()},releaseHeldEventType:function(t){this.__getEventEmitter().releaseHeldEventType(t)},__getEventEmitter:function(){if(!this.__eventEmitter){var t=new n,o=new _;this.__eventEmitter=new s(t,o)}return this.__eventEmitter}};m.exports=function(n,s){o(s,'Must supply set of valid event types');var _=n.prototype||n;o(!_.__eventEmitter,'An active emitter is already mixed in');var u=n.constructor;u&&o(u===Object||u===Function,'Mix EventEmitter into a class, not an instance'),_.hasOwnProperty(v)?t(_.__types,s):_.__types?_.__types=t({},_.__types,s):_.__types=s,t(_,E)}},327,[16,49,328,329,18,330]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=(function(){function s(n,l){t(this,s),this._emitter=n,this._eventHolder=l,this._currentEventToken=null,this._emittingHeldEvents=!1}return n(s,[{key:"addListener",value:function(t,n,s){return this._emitter.addListener(t,n,s)}},{key:"once",value:function(t,n,s){return this._emitter.once(t,n,s)}},{key:"addRetroactiveListener",value:function(t,n,s){var l=this._emitter.addListener(t,n,s);return this._emittingHeldEvents=!0,this._eventHolder.emitToListener(t,n,s),this._emittingHeldEvents=!1,l}},{key:"removeAllListeners",value:function(t){this._emitter.removeAllListeners(t)}},{key:"removeCurrentListener",value:function(){this._emitter.removeCurrentListener()}},{key:"listeners",value:function(t){return this._emitter.listeners(t)}},{key:"emit",value:function(t){for(var n,s=arguments.length,l=new Array(s>1?s-1:0),o=1;o<s;o++)l[o-1]=arguments[o];(n=this._emitter).emit.apply(n,[t].concat(l))}},{key:"emitAndHold",value:function(t){for(var n,s,l=arguments.length,o=new Array(l>1?l-1:0),u=1;u<l;u++)o[u-1]=arguments[u];this._currentEventToken=(n=this._eventHolder).holdEvent.apply(n,[t].concat(o)),(s=this._emitter).emit.apply(s,[t].concat(o)),this._currentEventToken=null}},{key:"releaseCurrentEvent",value:function(){this._currentEventToken?this._eventHolder.releaseEvent(this._currentEventToken):this._emittingHeldEvents&&this._eventHolder.releaseCurrentEvent()}},{key:"releaseHeldEventType",value:function(t){this._eventHolder.releaseEventType(t)}}]),s})();m.exports=s},328,[4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),s=r(d[2]),v=(function(){function v(){t(this,v),this._heldEvents={},this._currentEventKey=null}return n(v,[{key:"holdEvent",value:function(t){this._heldEvents[t]=this._heldEvents[t]||[];for(var n=this._heldEvents[t],s={eventType:t,index:n.length},v=arguments.length,h=new Array(v>1?v-1:0),u=1;u<v;u++)h[u-1]=arguments[u];return n.push(h),s}},{key:"emitToListener",value:function(t,n,s){var v=this,h=this._heldEvents[t];if(h){var u=this._currentEventKey;h.forEach(function(h,u){h&&(v._currentEventKey={eventType:t,index:u},n.apply(s,h))}),this._currentEventKey=u}}},{key:"releaseCurrentEvent",value:function(){s(null!==this._currentEventKey,'Not in an emitting cycle; there is no current event'),this._currentEventKey&&this.releaseEvent(this._currentEventKey)}},{key:"releaseEvent",value:function(t){delete this._heldEvents[t.eventType][t.index]}},{key:"releaseEventType",value:function(t){this._heldEvents[t]=[]}}]),v})();m.exports=v},329,[4,5,18]); -__d(function(g,r,i,a,m,e,d){"use strict";m.exports=function(n){var t;for(t in n)if(n.hasOwnProperty(t))return t;return null}},330,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=r(d[1]),o=r(d[2]),n=r(d[3]),l=r(d[4]),p=r(d[5]),h=r(d[6]),u=r(d[7]),c=r(d[8]),y=r(d[9]),b=r(d[10]),P=r(d[11]),f=r(d[12]),S=r(d[13]),x={activeOpacity:.85,delayPressOut:100,underlayColor:'black'},T={top:20,left:20,right:20,bottom:30},_=f({displayName:'TouchableHighlight',propTypes:t({},b.propTypes,{activeOpacity:p.number,underlayColor:s,style:o.style,onShowUnderlay:p.func,onHideUnderlay:p.func,hasTVPreferredFocus:p.bool,nextFocusDown:p.number,nextFocusForward:p.number,nextFocusLeft:p.number,nextFocusRight:p.number,nextFocusUp:p.number,tvParallaxProperties:p.object,testOnly_pressed:p.bool}),mixins:[n,y.Mixin.withoutDefaultFocusAndBlur],getDefaultProps:function(){return x},getInitialState:function(){return this._isMounted=!1,this.props.testOnly_pressed?t({},this.touchableGetInitialState(),{extraChildStyle:{opacity:this.props.activeOpacity},extraUnderlayStyle:{backgroundColor:this.props.underlayColor}}):t({},this.touchableGetInitialState(),{extraChildStyle:null,extraUnderlayStyle:null})},componentDidMount:function(){this._isMounted=!0,S(this.props)},componentWillUnmount:function(){this._isMounted=!1,clearTimeout(this._hideTimeout)},UNSAFE_componentWillReceiveProps:function(t){S(t)},viewConfig:{uiViewClassName:'RCTView',validAttributes:u.RCTView},touchableHandleActivePressIn:function(t){clearTimeout(this._hideTimeout),this._hideTimeout=null,this._showUnderlay(),this.props.onPressIn&&this.props.onPressIn(t)},touchableHandleActivePressOut:function(t){this._hideTimeout||this._hideUnderlay(),this.props.onPressOut&&this.props.onPressOut(t)},touchableHandleFocus:function(t){l.isTV&&this._showUnderlay(),this.props.onFocus&&this.props.onFocus(t)},touchableHandleBlur:function(t){l.isTV&&this._hideUnderlay(),this.props.onBlur&&this.props.onBlur(t)},touchableHandlePress:function(t){clearTimeout(this._hideTimeout),l.isTV||(this._showUnderlay(),this._hideTimeout=setTimeout(this._hideUnderlay,this.props.delayPressOut)),this.props.onPress&&this.props.onPress(t)},touchableHandleLongPress:function(t){this.props.onLongPress&&this.props.onLongPress(t)},touchableGetPressRectOffset:function(){return this.props.pressRetentionOffset||T},touchableGetHitSlop:function(){return this.props.hitSlop},touchableGetHighlightDelayMS:function(){return this.props.delayPressIn},touchableGetLongPressDelayMS:function(){return this.props.delayLongPress},touchableGetPressOutDelayMS:function(){return this.props.delayPressOut},_showUnderlay:function(){this._isMounted&&this._hasPressHandler()&&(this.setState({extraChildStyle:{opacity:this.props.activeOpacity},extraUnderlayStyle:{backgroundColor:this.props.underlayColor}}),this.props.onShowUnderlay&&this.props.onShowUnderlay())},_hideUnderlay:function(){clearTimeout(this._hideTimeout),this._hideTimeout=null,this.props.testOnly_pressed||this._hasPressHandler()&&(this.setState({extraChildStyle:null,extraUnderlayStyle:null}),this.props.onHideUnderlay&&this.props.onHideUnderlay())},_hasPressHandler:function(){return!!(this.props.onPress||this.props.onPressIn||this.props.onPressOut||this.props.onLongPress)},render:function(){var t=h.Children.only(this.props.children);return h.createElement(P,{accessible:!1!==this.props.accessible,accessibilityLabel:this.props.accessibilityLabel,accessibilityHint:this.props.accessibilityHint,accessibilityRole:this.props.accessibilityRole,accessibilityStates:this.props.accessibilityStates,accessibilityState:this.props.accessibilityState,accessibilityActions:this.props.accessibilityActions,onAccessibilityAction:this.props.onAccessibilityAction,style:c.compose(this.props.style,this.state.extraUnderlayStyle),onLayout:this.props.onLayout,hitSlop:this.props.hitSlop,isTVSelectable:!0,tvParallaxProperties:this.props.tvParallaxProperties,hasTVPreferredFocus:this.props.hasTVPreferredFocus,nextFocusDown:this.props.nextFocusDown,nextFocusForward:this.props.nextFocusForward,nextFocusLeft:this.props.nextFocusLeft,nextFocusRight:this.props.nextFocusRight,nextFocusUp:this.props.nextFocusUp,focusable:!1!==this.props.focusable&&void 0!==this.props.onPress,onClick:this.touchableHandlePress,onStartShouldSetResponder:this.touchableHandleStartShouldSetResponder,onResponderTerminationRequest:this.touchableHandleResponderTerminationRequest,onResponderGrant:this.touchableHandleResponderGrant,onResponderMove:this.touchableHandleResponderMove,onResponderRelease:this.touchableHandleResponderRelease,onResponderTerminate:this.touchableHandleResponderTerminate,nativeID:this.props.nativeID,testID:this.props.testID},h.cloneElement(t,{style:c.compose(t.props.style,this.state.extraChildStyle)}),y.renderDebugView({color:'green',hitSlop:this.props.hitSlop}))}});m.exports=_},331,[54,66,324,278,58,69,13,192,60,203,279,88,280,284]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=t(r(d[1])),n=t(r(d[2])),l=t(r(d[3])),s=r(d[4]),c=r(d[5]),u={showActionSheetWithOptions:function(t,u){s('object'==typeof t&&null!==t,'Options must be a valid object'),s('function'==typeof u,'Must provide a valid callback'),s(l.default,"ActionSheetManager does't exist");var f=t.tintColor,h=(0,n.default)(t,["tintColor"]);l.default.showActionSheetWithOptions((0,o.default)({},h,{tintColor:c(f)}),u)},showShareActionSheetWithOptions:function(t,n,u){s('object'==typeof t&&null!==t,'Options must be a valid object'),s('function'==typeof n,'Must provide a valid failureCallback'),s('function'==typeof u,'Must provide a valid successCallback'),s(l.default,"ActionSheetManager does't exist"),l.default.showShareActionSheetWithOptions((0,o.default)({},t,{tintColor:c(t.tintColor)}),n,u)}};m.exports=u},332,[3,54,56,333,18,82]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('ActionSheetManager');e.default=u},333,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,t=r(d[0]),o=t(r(d[1])),s=t(r(d[2])),u=t(r(d[3])),c=r(d[4]),f=r(d[5]),l=r(d[6]),p=r(d[7]),y=r(d[8]),h=r(d[9]),k=r(d[10]),C=r(d[11]),b={},v=1,T={},A=new Map,R=new Map,w=function(n){return n()},H=!1,S={setWrapperComponentProvider:function(t){n=t},enableFabricIndicator:function(n){H=n},registerConfig:function(n){n.forEach(function(n){n.run?S.registerRunnable(n.appKey,n.run):(h(null!=n.component,"AppRegistry.registerConfig(...): Every config is expected to set either `run` or `component`, but `%s` has neither.",n.appKey),S.registerComponent(n.appKey,n.component,n.section))})},registerComponent:function(t,o,s){var u=C();return b[t]={componentProvider:o,run:function(t){k(w(o,u),t.initialProps,t.rootTag,n&&n(t),t.fabric,H,u)}},s&&(T[t]=b[t]),t},registerRunnable:function(n,t){return b[n]={run:t},n},registerSection:function(n,t){S.registerComponent(n,t,!0)},getAppKeys:function(){return Object.keys(b)},getSectionKeys:function(){return Object.keys(T)},getSections:function(){return(0,o.default)({},T)},getRunnable:function(n){return b[n]},getRegistry:function(){return{sections:S.getSectionKeys(),runnables:(0,o.default)({},b)}},setComponentProviderInstrumentationHook:function(n){w=n},runApplication:function(n,t){var o='Running "'+n+'" with '+JSON.stringify(t);y(o),f.addSource('AppRegistry.runApplication'+v++,function(){return o}),h(b[n]&&b[n].run,"\""+n+"\" has not been registered. This can happen if:\n* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.\n* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called."),p.setActiveScene({name:n}),b[n].run(t)},unmountApplicationComponentAtRootTag:function(n){l.unmountComponentAtNodeAndRemoveContainer(n)},registerHeadlessTask:function(n,t){this.registerCancellableHeadlessTask(n,t,function(){return function(){}})},registerCancellableHeadlessTask:function(n,t,o){A.has(n)&&console.warn("registerHeadlessTask or registerCancellableHeadlessTask called multiple times for same key '"+n+"'"),A.set(n,t),R.set(n,o)},startHeadlessTask:function(n,t,o){var c=A.get(t);if(!c)return console.warn("No task registered for key "+t),void(s.default&&s.default.notifyTaskFinished(n));c()(o).then(function(){s.default&&s.default.notifyTaskFinished(n)}).catch(function(t){console.error(t),s.default&&t instanceof u.default&&s.default.notifyTaskRetry(n).then(function(t){t||s.default.notifyTaskFinished(n)})})},cancelHeadlessTask:function(n,t){var o=R.get(t);if(!o)throw new Error("No task canceller registered for key '"+t+"'");o()()}};c.registerCallableModule('AppRegistry',S),m.exports=S},334,[3,54,335,336,30,337,90,341,154,18,342,153]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=t(r(d[1])).get('HeadlessJsTaskSupport');e.default=s},335,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2])),l=t(r(d[3])),n=t(r(d[4])),o=(function(t){function o(){return(0,u.default)(this,o),(0,f.default)(this,(0,l.default)(o).apply(this,arguments))}return(0,n.default)(o,t),o})((0,t(r(d[5])).default)(Error));e.default=o},336,[3,4,6,9,10,98]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),u=t(r(d[1])),l=t(r(d[2])),n=t(r(d[3])),o=t(r(d[4])),c=t(r(d[5])),s=r(d[6]),f=r(d[7]);function x(){_.addFileSource('react_hierarchy.txt',function(){return r(d[8])()})}var _=(function(){function t(){(0,l.default)(this,t)}return(0,n.default)(t,null,[{key:"_maybeInit",value:function(){t._subscription||(t._subscription=s.addListener('collectBugExtraData',t.collectExtraData,null),x()),t._redboxSubscription||(t._redboxSubscription=s.addListener('collectRedBoxExtraData',t.collectExtraData,null))}},{key:"addSource",value:function(u,l){return this._addSource(u,l,t._extraSources)}},{key:"addFileSource",value:function(u,l){return this._addSource(u,l,t._fileSources)}},{key:"_addSource",value:function(u,l,n){return t._maybeInit(),n.has(u)&&console.warn("BugReporting.add* called multiple times for same key '"+u+"'"),n.set(u,l),{remove:function(){n.delete(u)}}}},{key:"collectExtraData",value:function(){var l={},n=t._extraSources,s=Array.isArray(n),x=0;for(n=s?n:n["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var _;if(s){if(x>=n.length)break;_=n[x++]}else{if((x=n.next()).done)break;_=x.value}var b=_,y=(0,u.default)(b,2),S=y[0],v=y[1];l[S]=v()}var p={},k=t._fileSources,D=Array.isArray(k),E=0;for(k=D?k:k["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var h;if(D){if(E>=k.length)break;h=k[E++]}else{if((E=k.next()).done)break;h=E.value}var B=h,A=(0,u.default)(B,2),R=A[0],w=A[1];p[R]=w()}return f('BugReporting extraData:',l),null!=o.default&&null!=o.default.setExtraData&&o.default.setExtraData(l,p),null!=c.default&&null!=c.default.setExtraData&&c.default.setExtraData(l,'From BugReporting.js'),{extras:l,files:p}}}]),t})();_._extraSources=new Map,_._fileSources=new Map,_._subscription=null,_._redboxSubscription=null,m.exports=_},337,[3,26,4,5,338,339,46,154,340]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('BugReporting');e.default=u},338,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('RedBox');e.default=u},339,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(){try{return"React tree dumps have been temporarily disabled while React is upgraded to Fiber."}catch(t){return'Failed to dump react tree: '+t}}},340,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=[],t={name:'default'},c={setActiveScene:function(c){t=c,n.forEach(function(n){return n(t)})},getActiveScene:function(){return t},addActiveSceneChangedListener:function(t){return n.push(t),{remove:function(){n=n.filter(function(n){return t!==n})}}}};m.exports=c},341,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),l=t(r(d[2])),o=t(r(d[3])),c=r(d[4]),p=r(d[5]),u=r(d[6]),s=r(d[7]);r(d[8]),m.exports=function(t,f,v,_,E,T,x){var A;s(v,'Expect to have a valid rootTag, instead got ',v);var R=p.createElement(o.default.Provider,{value:null!=(A=x)?A:l.default},p.createElement(c,{rootTag:v,WrapperComponent:_},p.createElement(t,(0,n.default)({},f,{rootTag:v})),!0===E&&!0===T?p.createElement(u,null):null));l.default.startTimespan('renderApplication_React_render'),E?r(d[9]).render(R,v):r(d[10]).render(R,v),l.default.stopTimespan('renderApplication_React_render')}},342,[3,16,152,343,300,13,344,18,345,346,90]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),c=r(d[1])(r(d[2])),n=t(r(d[3])),o=c.createContext(n.default);m.exports=o},343,[3,2,13,152]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),c=r(d[3]);var f=n.create({container:{alignItems:'center',justifyContent:'center',backgroundColor:'rgba(0,0,0, 0.25)',position:'absolute',top:0,right:0,padding:2},text:{fontSize:6,color:'#ffffff'}});m.exports=function(){return t.createElement(c,{style:f.container},t.createElement(o,{style:f.text},"FABRIC"))}},344,[13,60,197,88]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,t=r(d[0]),o=r(d[1]);function v(){}if(t.isTV){var u=new o,f=new Set;u.enable(this,function(t,o){if(o&&'menu'===o.eventType){for(var v=!0,u=Array.from(f.values()).reverse(),s=0;s<u.length;++s)if(u[s]()){v=!1;break}v&&n.exitApp()}}),n={exitApp:v,addEventListener:function(t,o){return f.add(o),{remove:function(){return n.removeEventListener(t,o)}}},removeEventListener:function(n,t){f.delete(t)}}}else n={exitApp:v,addEventListener:function(n,t){return{remove:v}},removeEventListener:function(n,t){}};m.exports=n},345,[58,207]); -__d(function(g,r,i,a,m,e,d){'use strict';var t,c=r(d[0]);t=r(d[1]),c.BatchedBridge.registerCallableModule('ReactFabric',t),m.exports=t},346,[160,347]); -__d(function(e,t,n,r,i,l,a){"use strict";var o=t(a[0]);t(a[1]);var u=t(a[2]),c=t(a[3]),s=t(a[4]);function f(e){return e.name="Invariant Violation",e}var d=null,p={};function h(){if(d)for(var e in p){var t=p[e],n=d.indexOf(e);if(!(-1<n))throw f(Error("EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `"+e+"`."));if(!g[n]){if(!t.extractEvents)throw f(Error("EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `"+e+"` does not."));for(var r in g[n]=t,n=t.eventTypes){var i=void 0,l=n[r],a=t,o=r;if(y.hasOwnProperty(o))throw f(Error("EventPluginHub: More than one plugin attempted to publish the same event name, `"+o+"`."));y[o]=l;var u=l.phasedRegistrationNames;if(u){for(i in u)u.hasOwnProperty(i)&&m(u[i],a);i=!0}else l.registrationName?(m(l.registrationName,a),i=!0):i=!1;if(!i)throw f(Error("EventPluginRegistry: Failed to publish event `"+r+"` for plugin `"+e+"`."))}}}}function m(e,t){if(v[e])throw f(Error("EventPluginHub: More than one plugin attempted to publish the same registration name, `"+e+"`."));v[e]=t}var g=[],y={},v={};function b(e,t,n,r,i,l,a,o,u){var c=Array.prototype.slice.call(arguments,3);try{t.apply(n,c)}catch(e){this.onError(e)}}var T=!1,E=null,x=!1,S=null,w={onError:function(e){T=!0,E=e}};function k(e,t,n,r,i,l,a,o,u){T=!1,E=null,b.apply(w,arguments)}function P(e,t,n,r,i,l,a,o,u){if(k.apply(this,arguments),T){if(!T)throw f(Error("clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue."));var c=E;T=!1,E=null,x||(x=!0,S=c)}}var C=null,_=null,R=null;function N(e,t,n){var r=e.type||"unknown-event";e.currentTarget=R(n),P(r,t,void 0,e),e.currentTarget=null}function I(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t))throw f(Error("executeDirectDispatch(...): Invalid `event`."));return e.currentTarget=t?R(n):null,t=t?t(e):null,e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,t}function z(e,t){if(null==t)throw f(Error("accumulateInto(...): Accumulated items must not be null or undefined."));return null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}function U(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}var M=null;function A(e){if(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t))for(var r=0;r<t.length&&!e.isPropagationStopped();r++)N(e,t[r],n[r]);else t&&N(e,t,n);e._dispatchListeners=null,e._dispatchInstances=null,e.isPersistent()||e.constructor.release(e)}}var D=function(e){if(d)throw f(Error("EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React."));d=Array.prototype.slice.call(e),h()},F=function(e){var t,n=!1;for(t in e)if(e.hasOwnProperty(t)){var r=e[t];if(!p.hasOwnProperty(t)||p[t]!==r){if(p[t])throw f(Error("EventPluginRegistry: Cannot inject two different event plugins using the same name, `"+t+"`."));p[t]=r,n=!0}}n&&h()};function j(e,t){var n=e.stateNode;if(!n)return null;var r=C(n);if(!r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw f(Error("Expected `"+t+"` listener to be a function, instead got a value of `"+typeof n+"` type."));return n}function O(e){do{e=e.return}while(e&&5!==e.tag);return e||null}function W(e,t,n){for(var r=[];e;)r.push(e),e=O(e);for(e=r.length;0<e--;)t(r[e],"captured",n);for(e=0;e<r.length;e++)t(r[e],"bubbled",n)}function H(e,t,n){(t=j(e,n.dispatchConfig.phasedRegistrationNames[t]))&&(n._dispatchListeners=z(n._dispatchListeners,t),n._dispatchInstances=z(n._dispatchInstances,e))}function B(e){e&&e.dispatchConfig.phasedRegistrationNames&&W(e._targetInst,H,e)}function Q(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var t=e._targetInst;W(t=t?O(t):null,H,e)}}function L(e){if(e&&e.dispatchConfig.registrationName){var t=e._targetInst;if(t&&e&&e.dispatchConfig.registrationName){var n=j(t,e.dispatchConfig.registrationName);n&&(e._dispatchListeners=z(e._dispatchListeners,n),e._dispatchInstances=z(e._dispatchInstances,t))}}}function Y(){return!0}function V(){return!1}function X(e,t,n,r){for(var i in this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n,e=this.constructor.Interface)e.hasOwnProperty(i)&&((t=e[i])?this[i]=t(n):"target"===i?this.target=r:this[i]=n[i]);return this.isDefaultPrevented=(null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue)?Y:V,this.isPropagationStopped=V,this}function q(e,t,n,r){if(this.eventPool.length){var i=this.eventPool.pop();return this.call(i,e,t,n,r),i}return new this(e,t,n,r)}function $(e){if(!(e instanceof this))throw f(Error("Trying to release an event instance into a pool of a different type."));e.destructor(),10>this.eventPool.length&&this.eventPool.push(e)}function G(e){e.eventPool=[],e.getPooled=q,e.release=$}o(X.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=Y)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=Y)},persist:function(){this.isPersistent=Y},isPersistent:V,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=V,this._dispatchInstances=this._dispatchListeners=null}}),X.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},X.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var i=new t;return o(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=o({},r.Interface,e),n.extend=r.extend,G(n),n},G(X);var J=X.extend({touchHistory:function(){return null}});function K(e){return"topTouchStart"===e}function Z(e){return"topTouchMove"===e}var ee=["topTouchStart"],te=["topTouchMove"],ne=["topTouchCancel","topTouchEnd"],re=[],ie={touchBank:re,numberActiveTouches:0,indexOfSingleActiveTouch:-1,mostRecentTimeStamp:0};function le(e){return e.timeStamp||e.timestamp}function ae(e){if(null==(e=e.identifier))throw f(Error("Touch object is missing identifier."));return e}function oe(e){var t=ae(e),n=re[t];n?(n.touchActive=!0,n.startPageX=e.pageX,n.startPageY=e.pageY,n.startTimeStamp=le(e),n.currentPageX=e.pageX,n.currentPageY=e.pageY,n.currentTimeStamp=le(e),n.previousPageX=e.pageX,n.previousPageY=e.pageY,n.previousTimeStamp=le(e)):(n={touchActive:!0,startPageX:e.pageX,startPageY:e.pageY,startTimeStamp:le(e),currentPageX:e.pageX,currentPageY:e.pageY,currentTimeStamp:le(e),previousPageX:e.pageX,previousPageY:e.pageY,previousTimeStamp:le(e)},re[t]=n),ie.mostRecentTimeStamp=le(e)}function ue(e){var t=re[ae(e)];t?(t.touchActive=!0,t.previousPageX=t.currentPageX,t.previousPageY=t.currentPageY,t.previousTimeStamp=t.currentTimeStamp,t.currentPageX=e.pageX,t.currentPageY=e.pageY,t.currentTimeStamp=le(e),ie.mostRecentTimeStamp=le(e)):console.warn("Cannot record touch move without a touch start.\nTouch Move: %s\n","Touch Bank: %s",se(e),fe())}function ce(e){var t=re[ae(e)];t?(t.touchActive=!1,t.previousPageX=t.currentPageX,t.previousPageY=t.currentPageY,t.previousTimeStamp=t.currentTimeStamp,t.currentPageX=e.pageX,t.currentPageY=e.pageY,t.currentTimeStamp=le(e),ie.mostRecentTimeStamp=le(e)):console.warn("Cannot record touch end without a touch start.\nTouch End: %s\n","Touch Bank: %s",se(e),fe())}function se(e){return JSON.stringify({identifier:e.identifier,pageX:e.pageX,pageY:e.pageY,timestamp:le(e)})}function fe(){var e=JSON.stringify(re.slice(0,20));return 20<re.length&&(e+=" (original size: "+re.length+")"),e}var de={recordTouchTrack:function(e,t){if(Z(e))t.changedTouches.forEach(ue);else if(K(e))t.changedTouches.forEach(oe),ie.numberActiveTouches=t.touches.length,1===ie.numberActiveTouches&&(ie.indexOfSingleActiveTouch=t.touches[0].identifier);else if(("topTouchEnd"===e||"topTouchCancel"===e)&&(t.changedTouches.forEach(ce),ie.numberActiveTouches=t.touches.length,1===ie.numberActiveTouches))for(e=0;e<re.length;e++)if(null!=(t=re[e])&&t.touchActive){ie.indexOfSingleActiveTouch=e;break}},touchHistory:ie};function pe(e,t){if(null==t)throw f(Error("accumulate(...): Accumulated items must not be null or undefined."));return null==e?t:Array.isArray(e)?e.concat(t):Array.isArray(t)?[e].concat(t):[e,t]}var he=null,me=0;function ge(e,t){var n=he;he=e,null!==ve.GlobalResponderHandler&&ve.GlobalResponderHandler.onChange(n,e,t)}var ye={startShouldSetResponder:{phasedRegistrationNames:{bubbled:"onStartShouldSetResponder",captured:"onStartShouldSetResponderCapture"},dependencies:ee},scrollShouldSetResponder:{phasedRegistrationNames:{bubbled:"onScrollShouldSetResponder",captured:"onScrollShouldSetResponderCapture"},dependencies:["topScroll"]},selectionChangeShouldSetResponder:{phasedRegistrationNames:{bubbled:"onSelectionChangeShouldSetResponder",captured:"onSelectionChangeShouldSetResponderCapture"},dependencies:["topSelectionChange"]},moveShouldSetResponder:{phasedRegistrationNames:{bubbled:"onMoveShouldSetResponder",captured:"onMoveShouldSetResponderCapture"},dependencies:te},responderStart:{registrationName:"onResponderStart",dependencies:ee},responderMove:{registrationName:"onResponderMove",dependencies:te},responderEnd:{registrationName:"onResponderEnd",dependencies:ne},responderRelease:{registrationName:"onResponderRelease",dependencies:ne},responderTerminationRequest:{registrationName:"onResponderTerminationRequest",dependencies:[]},responderGrant:{registrationName:"onResponderGrant",dependencies:[]},responderReject:{registrationName:"onResponderReject",dependencies:[]},responderTerminate:{registrationName:"onResponderTerminate",dependencies:[]}},ve={_getResponder:function(){return he},eventTypes:ye,extractEvents:function(e,t,n,r){if(K(e))me+=1;else if("topTouchEnd"===e||"topTouchCancel"===e){if(!(0<=me))return console.error("Ended a touch event which was not counted in `trackedTouchCount`."),null;--me}if(de.recordTouchTrack(e,n),t&&("topScroll"===e&&!n.responderIgnoreScroll||0<me&&"topSelectionChange"===e||K(e)||Z(e))){var i=K(e)?ye.startShouldSetResponder:Z(e)?ye.moveShouldSetResponder:"topSelectionChange"===e?ye.selectionChangeShouldSetResponder:ye.scrollShouldSetResponder;if(he)e:{for(var l=he,a=0,o=l;o;o=O(o))a++;o=0;for(var u=t;u;u=O(u))o++;for(;0<a-o;)l=O(l),a--;for(;0<o-a;)t=O(t),o--;for(;a--;){if(l===t||l===t.alternate)break e;l=O(l),t=O(t)}l=null}else l=t;t=l===he,(l=J.getPooled(i,l,n,r)).touchHistory=de.touchHistory,U(l,t?Q:B);e:{if(i=l._dispatchListeners,t=l._dispatchInstances,Array.isArray(i)){for(a=0;a<i.length&&!l.isPropagationStopped();a++)if(i[a](l,t[a])){i=t[a];break e}}else if(i&&i(l,t)){i=t;break e}i=null}l._dispatchInstances=null,l._dispatchListeners=null,l.isPersistent()||l.constructor.release(l),i&&i!==he?(l=void 0,(t=J.getPooled(ye.responderGrant,i,n,r)).touchHistory=de.touchHistory,U(t,L),a=!0===I(t),he?((o=J.getPooled(ye.responderTerminationRequest,he,n,r)).touchHistory=de.touchHistory,U(o,L),u=!o._dispatchListeners||I(o),o.isPersistent()||o.constructor.release(o),u?((o=J.getPooled(ye.responderTerminate,he,n,r)).touchHistory=de.touchHistory,U(o,L),l=pe(l,[t,o]),ge(i,a)):((i=J.getPooled(ye.responderReject,i,n,r)).touchHistory=de.touchHistory,U(i,L),l=pe(l,i))):(l=pe(l,t),ge(i,a)),i=l):i=null}else i=null;if(l=he&&K(e),t=he&&Z(e),a=he&&("topTouchEnd"===e||"topTouchCancel"===e),(l=l?ye.responderStart:t?ye.responderMove:a?ye.responderEnd:null)&&((l=J.getPooled(l,he,n,r)).touchHistory=de.touchHistory,U(l,L),i=pe(i,l)),l=he&&"topTouchCancel"===e,e=he&&!l&&("topTouchEnd"===e||"topTouchCancel"===e))e:{if((e=n.touches)&&0!==e.length)for(t=0;t<e.length;t++)if(null!==(a=e[t].target)&&void 0!==a&&0!==a){o=_(a);t:{for(a=he;o;){if(a===o||a===o.alternate){a=!0;break t}o=O(o)}a=!1}if(a){e=!1;break e}}e=!0}return(e=l?ye.responderTerminate:e?ye.responderRelease:null)&&((n=J.getPooled(e,he,n,r)).touchHistory=de.touchHistory,U(n,L),i=pe(i,n),ge(null)),i},GlobalResponderHandler:null,injection:{injectGlobalResponderHandler:function(e){ve.GlobalResponderHandler=e}}},be=u.ReactNativeViewConfigRegistry.customBubblingEventTypes,Te=u.ReactNativeViewConfigRegistry.customDirectEventTypes;function Ee(e){return e}D(["ResponderEventPlugin","ReactNativeBridgeEventPlugin"]),F({ResponderEventPlugin:ve,ReactNativeBridgeEventPlugin:{eventTypes:{},extractEvents:function(e,t,n,r){if(null==t)return null;var i=be[e],l=Te[e];if(!i&&!l)throw f(Error('Unsupported top level event type "'+e+'" dispatched'));if(e=X.getPooled(i||l,t,n,r),i)U(e,B);else{if(!l)return null;U(e,L)}return e}}}),C=function(e){return e.canonical.currentProps},_=Ee,R=function(e){if(!(e=e.stateNode.canonical._nativeTag))throw f(Error("All native instances should have a tag."));return e},ve.injection.injectGlobalResponderHandler({onChange:function(e,t,n){null!==t?u.UIManager.setJSResponder(t.stateNode.canonical._nativeTag,n):u.UIManager.clearJSResponder()}});var xe=c.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;xe.hasOwnProperty("ReactCurrentDispatcher")||(xe.ReactCurrentDispatcher={current:null}),xe.hasOwnProperty("ReactCurrentBatchConfig")||(xe.ReactCurrentBatchConfig={suspense:null});var Se="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.for:"@@for"),we=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.element"):60103,ke=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.portal"):60106,Pe=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.fragment"):60107,Ce=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.strict_mode"):60108,_e=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.profiler"):60114,Re=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.provider"):60109,Ne=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.context"):60110,Ie=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.concurrent_mode"):60111,ze=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.forward_ref"):60112,Ue=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense"):60113,Me=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.suspense_list"):60120,Ae=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.memo"):60115,De=Se?("function"==typeof Symbol?Symbol.for:"@@for")("react.lazy"):60116;Se&&("function"==typeof Symbol?Symbol.for:"@@for")("react.fundamental"),Se&&("function"==typeof Symbol?Symbol.for:"@@for")("react.responder");var Fe="function"==typeof Symbol&&("function"==typeof Symbol?Symbol.iterator:"@@iterator");function je(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=Fe&&e[Fe]||e["@@iterator"])?e:null}function Oe(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case Pe:return"Fragment";case ke:return"Portal";case _e:return"Profiler";case Ce:return"StrictMode";case Ue:return"Suspense";case Me:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case Ne:return"Context.Consumer";case Re:return"Context.Provider";case ze:var t=e.render;return t=t.displayName||t.name||"",e.displayName||(""!==t?"ForwardRef("+t+")":"ForwardRef");case Ae:return Oe(e.type);case De:if(e=1===e._status?e._result:null)return Oe(e)}return null}function We(e){var t=e;if(e.alternate)for(;t.return;)t=t.return;else{if(0!=(2&t.effectTag))return 1;for(;t.return;)if(0!=(2&(t=t.return).effectTag))return 1}return 3===t.tag?2:3}function He(e){if(2!==We(e))throw f(Error("Unable to find node on an unmounted component."))}function Be(e){var t=e.alternate;if(!t){if(3===(t=We(e)))throw f(Error("Unable to find node on an unmounted component."));return 1===t?null:e}for(var n=e,r=t;;){var i=n.return;if(null===i)break;var l=i.alternate;if(null===l){if(null!==(r=i.return)){n=r;continue}break}if(i.child===l.child){for(l=i.child;l;){if(l===n)return He(i),e;if(l===r)return He(i),t;l=l.sibling}throw f(Error("Unable to find node on an unmounted component."))}if(n.return!==r.return)n=i,r=l;else{for(var a=!1,o=i.child;o;){if(o===n){a=!0,n=i,r=l;break}if(o===r){a=!0,r=i,n=l;break}o=o.sibling}if(!a){for(o=l.child;o;){if(o===n){a=!0,n=l,r=i;break}if(o===r){a=!0,r=l,n=i;break}o=o.sibling}if(!a)throw f(Error("Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue."))}}if(n.alternate!==r)throw f(Error("Return fibers should always be each others' alternates. This error is likely caused by a bug in React. Please file an issue."))}if(3!==n.tag)throw f(Error("Unable to find node on an unmounted component."));return n.stateNode.current===n?e:t}function Qe(e){if(!(e=Be(e)))return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}function Le(e,t){return function(){if(t&&("boolean"!=typeof e.__isMounted||e.__isMounted))return t.apply(e,arguments)}}var Ye={},Ve=null,Xe=0;function qe(e,t,n){if(Array.isArray(t))for(var r=t.length;r--&&0<Xe;)qe(e,t[r],n);else if(t&&0<Xe)for(r in Ve)if(Ve[r]){var i=t[r];if(void 0!==i){var l=n[r];l&&("function"==typeof i&&(i=!0),void 0===i&&(i=null),"object"!=typeof l?e[r]=i:"function"!=typeof l.diff&&"function"!=typeof l.process||(i="function"==typeof l.process?l.process(i):i,e[r]=i),Ve[r]=!1,Xe--)}}}function $e(e,t,n,r){if(!e&&t===n)return e;if(!t||!n)return n?Ge(e,n,r):t?Je(e,t,r):e;if(!Array.isArray(t)&&!Array.isArray(n))return Ke(e,t,n,r);if(Array.isArray(t)&&Array.isArray(n)){var i,l=t.length<n.length?t.length:n.length;for(i=0;i<l;i++)e=$e(e,t[i],n[i],r);for(;i<t.length;i++)e=Je(e,t[i],r);for(;i<n.length;i++)e=Ge(e,n[i],r);return e}return Array.isArray(t)?Ke(e,u.flattenStyle(t),n,r):Ke(e,t,u.flattenStyle(n),r)}function Ge(e,t,n){if(!t)return e;if(!Array.isArray(t))return Ke(e,Ye,t,n);for(var r=0;r<t.length;r++)e=Ge(e,t[r],n);return e}function Je(e,t,n){if(!t)return e;if(!Array.isArray(t))return Ke(e,t,Ye,n);for(var r=0;r<t.length;r++)e=Je(e,t[r],n);return e}function Ke(e,t,n,r){var i,l;for(l in n)if(i=r[l]){var a=t[l],o=n[l];"function"==typeof o&&(o=!0,"function"==typeof a&&(a=!0)),void 0===o&&(o=null,void 0===a&&(a=null)),Ve&&(Ve[l]=!1),e&&void 0!==e[l]?"object"!=typeof i?e[l]=o:"function"!=typeof i.diff&&"function"!=typeof i.process||(i="function"==typeof i.process?i.process(o):o,e[l]=i):a!==o&&("object"!=typeof i?("object"!=typeof o||null===o||u.deepDiffer(a,o))&&((e||(e={}))[l]=o):"function"==typeof i.diff||"function"==typeof i.process?(void 0===a||("function"==typeof i.diff?i.diff(a,o):"object"!=typeof o||null===o||u.deepDiffer(a,o)))&&(i="function"==typeof i.process?i.process(o):o,(e||(e={}))[l]=i):(Ve=null,Xe=0,e=$e(e,a,o,i),0<Xe&&e&&(qe(e,o,i),Ve=null)))}for(var c in t)void 0===n[c]&&(!(i=r[c])||e&&void 0!==e[c]||void 0!==(a=t[c])&&("object"!=typeof i||"function"==typeof i.diff||"function"==typeof i.process?((e||(e={}))[c]=null,Ve||(Ve={}),Ve[c]||(Ve[c]=!0,Xe++)):e=Je(e,a,i)));return e}var Ze=null,et=null;function tt(e){if(_(e))throw f(Error("setRestoreImplementation() needs to be called to handle a target for controlled events. This error is likely caused by a bug in React. Please file an issue."))}function nt(e,t){return e(t)}function rt(){}var it=!1;function lt(e,t){if(it)return e(t);it=!0;try{return nt(e,t)}finally{if(it=!1,(null!==Ze||null!==et)&&(rt(),Ze&&(t=Ze,e=et,et=Ze=null,tt(t),e)))for(t=0;t<e.length;t++)tt(e[t])}}function at(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function ot(){throw f(Error("The current renderer does not support hydration. This error is likely caused by a bug in React. Please file an issue."))}!(function(e){function t(){if(!(this instanceof t))throw new TypeError("Cannot call a class as a function");var n=e.apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?this:n}at(t,e),t.prototype.blur=function(){},t.prototype.focus=function(){},t.prototype.measure=function(){},t.prototype.measureInWindow=function(){},t.prototype.measureLayout=function(){},t.prototype.setNativeProps=function(){}})(c.Component),new Map,new Map,new Set,new Map;var ut=nativeFabricUIManager,ct=ut.createNode,st=ut.cloneNode,ft=ut.cloneNodeWithNewChildren,dt=ut.cloneNodeWithNewChildrenAndProps,pt=ut.cloneNodeWithNewProps,ht=ut.createChildSet,mt=ut.appendChild,gt=ut.appendChildToSet,yt=ut.completeRoot,vt=ut.registerEventHandler,bt=ut.measure,Tt=ut.measureInWindow,Et=ut.measureLayout,xt=u.ReactNativeViewConfigRegistry.get,St=2;vt&&vt(function(e,t,n){lt(function(){for(var r=n.target,i=null,l=0;l<g.length;l++){var a=g[l];a&&(a=a.extractEvents(t,e,n,r))&&(i=z(i,a))}if(null!==(r=i)&&(M=z(M,r)),r=M,M=null,r){if(U(r,A),M)throw f(Error("processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."));if(x)throw r=S,x=!1,S=null,r}})});var wt=(function(){function e(t,n,r,i){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this._nativeTag=t,this.viewConfig=n,this.currentProps=r,this._internalInstanceHandle=i}return e.prototype.blur=function(){u.TextInputState.blurTextInput(this._nativeTag)},e.prototype.focus=function(){u.TextInputState.focusTextInput(this._nativeTag)},e.prototype.measure=function(e){bt(this._internalInstanceHandle.stateNode.node,Le(this,e))},e.prototype.measureInWindow=function(e){Tt(this._internalInstanceHandle.stateNode.node,Le(this,e))},e.prototype.measureLayout=function(t,n,r){"number"!=typeof t&&t instanceof e&&Et(this._internalInstanceHandle.stateNode.node,t._internalInstanceHandle.stateNode.node,Le(this,r),Le(this,n))},e.prototype.setNativeProps=function(){},e})();function kt(e,t,n,r){if(!n.isInAParentText)throw f(Error("Text strings must be rendered within a <Text> component."));return n=St,St+=2,{node:ct(n,"RCTRawText",t,{text:e},r)}}var Pt=setTimeout,Ct=clearTimeout;function _t(e){var t=e.node,n=Ke(null,Ye,{style:{display:"none"}},e.canonical.viewConfig.validAttributes);return{node:pt(t,n),canonical:e.canonical}}var Rt=/^(.*)[\\\/]/;function Nt(e){var t="";do{e:switch(e.tag){case 3:case 4:case 6:case 7:case 10:case 9:var n="";break e;default:var r=e._debugOwner,i=e._debugSource,l=Oe(e.type);n=null,r&&(n=Oe(r.type)),r=l,l="",i?l=" (at "+i.fileName.replace(Rt,"")+":"+i.lineNumber+")":n&&(l=" (created by "+n+")"),n="\n in "+(r||"Unknown")+l}t+=n,e=e.return}while(e);return t}new Set;var It=[],zt=-1;function Ut(e){0>zt||(e.current=It[zt],It[zt]=null,zt--)}function Mt(e,t){It[++zt]=e.current,e.current=t}var At={},Dt={current:At},Ft={current:!1},jt=At;function Ot(e,t){var n=e.type.contextTypes;if(!n)return At;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i,l={};for(i in n)l[i]=t[i];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function Wt(e){return null!==(e=e.childContextTypes)&&void 0!==e}function Ht(e){Ut(Ft),Ut(Dt)}function Bt(e){Ut(Ft),Ut(Dt)}function Qt(e,t,n){if(Dt.current!==At)throw f(Error("Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue."));Mt(Dt,t),Mt(Ft,n)}function Lt(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var i in r=r.getChildContext())if(!(i in e))throw f(Error((Oe(t)||"Unknown")+'.getChildContext(): key "'+i+'" is not defined in childContextTypes.'));return o({},n,r)}function Yt(e){var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||At,jt=Dt.current,Mt(Dt,t),Mt(Ft,Ft.current),!0}function Vt(e,t,n){var r=e.stateNode;if(!r)throw f(Error("Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue."));n?(t=Lt(e,t,jt),r.__reactInternalMemoizedMergedChildContext=t,Ut(Ft),Ut(Dt),Mt(Dt,t)):Ut(Ft),Mt(Ft,n)}var Xt=s.unstable_runWithPriority,qt=s.unstable_scheduleCallback,$t=s.unstable_cancelCallback,Gt=s.unstable_shouldYield,Jt=s.unstable_requestPaint,Kt=s.unstable_now,Zt=s.unstable_getCurrentPriorityLevel,en=s.unstable_ImmediatePriority,tn=s.unstable_UserBlockingPriority,nn=s.unstable_NormalPriority,rn=s.unstable_LowPriority,ln=s.unstable_IdlePriority,an={},on=void 0!==Jt?Jt:function(){},un=null,cn=null,sn=!1,fn=Kt(),dn=1e4>fn?Kt:function(){return Kt()-fn};function pn(){switch(Zt()){case en:return 99;case tn:return 98;case nn:return 97;case rn:return 96;case ln:return 95;default:throw f(Error("Unknown priority level."))}}function hn(e){switch(e){case 99:return en;case 98:return tn;case 97:return nn;case 96:return rn;case 95:return ln;default:throw f(Error("Unknown priority level."))}}function mn(e,t){return e=hn(e),Xt(e,t)}function gn(e,t,n){return e=hn(e),qt(e,t,n)}function yn(e){return null===un?(un=[e],cn=qt(en,bn)):un.push(e),an}function vn(){null!==cn&&$t(cn),bn()}function bn(){if(!sn&&null!==un){sn=!0;var e=0;try{var t=un;mn(99,function(){for(;e<t.length;e++){var n=t[e];do{n=n(!0)}while(null!==n)}}),un=null}catch(t){throw null!==un&&(un=un.slice(e+1)),qt(en,vn),t}finally{sn=!1}}}function Tn(e,t){return 1073741823===t?99:1===t?95:0>=(e=10*(1073741821-t)-10*(1073741821-e))?99:250>=e?98:5250>=e?97:95}function En(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t}var xn=Object.prototype.hasOwnProperty;function Sn(e,t){if(En(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++)if(!xn.call(t,n[r])||!En(e[n[r]],t[n[r]]))return!1;return!0}function wn(e,t){if(e&&e.defaultProps)for(var n in t=o({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}function kn(e){var t=e._result;switch(e._status){case 1:return t;case 2:case 0:throw t;default:switch(e._status=0,(t=(t=e._ctor)()).then(function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)},function(t){0===e._status&&(e._status=2,e._result=t)}),e._status){case 1:return e._result;case 2:throw e._result}throw e._result=t,t}}var Pn={current:null},Cn=null,_n=null,Rn=null;function Nn(){Rn=_n=Cn=null}function In(e,t){var n=e.type._context;Mt(Pn,n._currentValue2),n._currentValue2=t}function zn(e){var t=Pn.current;Ut(Pn),e.type._context._currentValue2=t}function Un(e,t){for(;null!==e;){var n=e.alternate;if(e.childExpirationTime<t)e.childExpirationTime=t,null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t);else{if(!(null!==n&&n.childExpirationTime<t))break;n.childExpirationTime=t}e=e.return}}function Mn(e,t){Cn=e,Rn=_n=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(e.expirationTime>=t&&(hi=!0),e.firstContext=null)}function An(e,t){if(Rn!==e&&!1!==t&&0!==t)if("number"==typeof t&&1073741823!==t||(Rn=e,t=1073741823),t={context:e,observedBits:t,next:null},null===_n){if(null===Cn)throw f(Error("Context can only be read while React is rendering. In classes, you can read it in the render method or getDerivedStateFromProps. In function components, you can read it directly in the function body, but not inside Hooks like useReducer() or useMemo()."));_n=t,Cn.dependencies={expirationTime:0,firstContext:t,responders:null}}else _n=_n.next=t;return e._currentValue2}var Dn=!1;function Fn(e){return{baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function jn(e){return{baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function On(e,t){return{expirationTime:e,suspenseConfig:t,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function Wn(e,t){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t)}function Hn(e,t){var n=e.alternate;if(null===n){var r=e.updateQueue,i=null;null===r&&(r=e.updateQueue=Fn(e.memoizedState))}else r=e.updateQueue,i=n.updateQueue,null===r?null===i?(r=e.updateQueue=Fn(e.memoizedState),i=n.updateQueue=Fn(n.memoizedState)):r=e.updateQueue=jn(i):null===i&&(i=n.updateQueue=jn(r));null===i||r===i?Wn(r,t):null===r.lastUpdate||null===i.lastUpdate?(Wn(r,t),Wn(i,t)):(Wn(r,t),i.lastUpdate=t)}function Bn(e,t){var n=e.updateQueue;null===(n=null===n?e.updateQueue=Fn(e.memoizedState):Qn(e,n)).lastCapturedUpdate?n.firstCapturedUpdate=n.lastCapturedUpdate=t:(n.lastCapturedUpdate.next=t,n.lastCapturedUpdate=t)}function Qn(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=jn(t)),t}function Ln(e,t,n,r,i,l){switch(n.tag){case 1:return"function"==typeof(e=n.payload)?e.call(l,r,i):e;case 3:e.effectTag=-2049&e.effectTag|64;case 0:if(null===(i="function"==typeof(e=n.payload)?e.call(l,r,i):e)||void 0===i)break;return o({},r,i);case 2:Dn=!0}return r}function Yn(e,t,n,r,i){Dn=!1;for(var l=(t=Qn(e,t)).baseState,a=null,o=0,u=t.firstUpdate,c=l;null!==u;){var s=u.expirationTime;s<i?(null===a&&(a=u,l=c),o<s&&(o=s)):(Wl(s,u.suspenseConfig),c=Ln(e,0,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastEffect?t.firstEffect=t.lastEffect=u:(t.lastEffect.nextEffect=u,t.lastEffect=u))),u=u.next}for(s=null,u=t.firstCapturedUpdate;null!==u;){var f=u.expirationTime;f<i?(null===s&&(s=u,null===a&&(l=c)),o<f&&(o=f)):(c=Ln(e,0,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastCapturedEffect?t.firstCapturedEffect=t.lastCapturedEffect=u:(t.lastCapturedEffect.nextEffect=u,t.lastCapturedEffect=u))),u=u.next}null===a&&(t.lastUpdate=null),null===s?t.lastCapturedUpdate=null:e.effectTag|=32,null===a&&null===s&&(l=c),t.baseState=l,t.firstUpdate=a,t.firstCapturedUpdate=s,e.expirationTime=o,e.memoizedState=c}function Vn(e,t,n){null!==t.firstCapturedUpdate&&(null!==t.lastUpdate&&(t.lastUpdate.next=t.firstCapturedUpdate,t.lastUpdate=t.lastCapturedUpdate),t.firstCapturedUpdate=t.lastCapturedUpdate=null),Xn(t.firstEffect,n),t.firstEffect=t.lastEffect=null,Xn(t.firstCapturedEffect,n),t.firstCapturedEffect=t.lastCapturedEffect=null}function Xn(e,t){for(;null!==e;){var n=e.callback;if(null!==n){e.callback=null;var r=t;if("function"!=typeof n)throw f(Error("Invalid argument passed as callback. Expected a function. Instead received: "+n));n.call(r)}e=e.nextEffect}}var qn=xe.ReactCurrentBatchConfig,$n=(new c.Component).refs;function Gn(e,t,n,r){n=null===(n=n(r,t=e.memoizedState))||void 0===n?t:o({},t,n),e.memoizedState=n,null!==(r=e.updateQueue)&&0===e.expirationTime&&(r.baseState=n)}var Jn={isMounted:function(e){return!!(e=e._reactInternalFiber)&&2===We(e)},enqueueSetState:function(e,t,n){e=e._reactInternalFiber;var r=Nl(),i=qn.suspense;(i=On(r=Il(r,e,i),i)).payload=t,void 0!==n&&null!==n&&(i.callback=n),Hn(e,i),zl(e,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternalFiber;var r=Nl(),i=qn.suspense;(i=On(r=Il(r,e,i),i)).tag=1,i.payload=t,void 0!==n&&null!==n&&(i.callback=n),Hn(e,i),zl(e,r)},enqueueForceUpdate:function(e,t){e=e._reactInternalFiber;var n=Nl(),r=qn.suspense;(r=On(n=Il(n,e,r),r)).tag=2,void 0!==t&&null!==t&&(r.callback=t),Hn(e,r),zl(e,n)}};function Kn(e,t,n,r,i,l,a){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,l,a):!t.prototype||!t.prototype.isPureReactComponent||(!Sn(n,r)||!Sn(i,l))}function Zn(e,t,n){var r=!1,i=At,l=t.contextType;return"object"==typeof l&&null!==l?l=An(l):(i=Wt(t)?jt:Dt.current,l=(r=null!==(r=t.contextTypes)&&void 0!==r)?Ot(e,i):At),t=new t(n,l),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=Jn,e.stateNode=t,t._reactInternalFiber=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=i,e.__reactInternalMemoizedMaskedChildContext=l),t}function er(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&Jn.enqueueReplaceState(t,t.state,null)}function tr(e,t,n,r){var i=e.stateNode;i.props=n,i.state=e.memoizedState,i.refs=$n;var l=t.contextType;"object"==typeof l&&null!==l?i.context=An(l):(l=Wt(t)?jt:Dt.current,i.context=Ot(e,l)),null!==(l=e.updateQueue)&&(Yn(e,l,n,i,r),i.state=e.memoizedState),"function"==typeof(l=t.getDerivedStateFromProps)&&(Gn(e,t,l,n),i.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof i.getSnapshotBeforeUpdate||"function"!=typeof i.UNSAFE_componentWillMount&&"function"!=typeof i.componentWillMount||(t=i.state,"function"==typeof i.componentWillMount&&i.componentWillMount(),"function"==typeof i.UNSAFE_componentWillMount&&i.UNSAFE_componentWillMount(),t!==i.state&&Jn.enqueueReplaceState(i,i.state,null),null!==(l=e.updateQueue)&&(Yn(e,l,n,i,r),i.state=e.memoizedState)),"function"==typeof i.componentDidMount&&(e.effectTag|=4)}var nr=Array.isArray;function rr(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){var r=void 0;if(n=n._owner){if(1!==n.tag)throw f(Error("Function components cannot have refs. Did you mean to use React.forwardRef()?"));r=n.stateNode}if(!r)throw f(Error("Missing owner for string ref "+e+". This error is likely caused by a bug in React. Please file an issue."));var i=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===i?t.ref:((t=function(e){var t=r.refs;t===$n&&(t=r.refs={}),null===e?delete t[i]:t[i]=e})._stringRef=i,t)}if("string"!=typeof e)throw f(Error("Expected ref to be a function, a string, an object returned by React.createRef(), or null."));if(!n._owner)throw f(Error("Element ref was specified as a string ("+e+") but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://fb.me/react-refs-must-have-owner for more information."))}return e}function ir(e,t){if("textarea"!==e.type)throw f(Error("Objects are not valid as a React child (found: "+("[object Object]"===Object.prototype.toString.call(t)?"object with keys {"+Object.keys(t).join(", ")+"}":t)+")."))}function lr(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.effectTag=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function i(e,t,n){return(e=la(e,t)).index=0,e.sibling=null,e}function l(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.effectTag=2,n):r:(t.effectTag=2,n):n}function a(t){return e&&null===t.alternate&&(t.effectTag=2),t}function o(e,t,n,r){return null===t||6!==t.tag?((t=ua(n,e.mode,r)).return=e,t):((t=i(t,n)).return=e,t)}function u(e,t,n,r){return null!==t&&t.elementType===n.type?((r=i(t,n.props)).ref=rr(e,t,n),r.return=e,r):((r=aa(n.type,n.key,n.props,null,e.mode,r)).ref=rr(e,t,n),r.return=e,r)}function c(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=ca(n,e.mode,r)).return=e,t):((t=i(t,n.children||[])).return=e,t)}function s(e,t,n,r,l){return null===t||7!==t.tag?((t=oa(n,e.mode,r,l)).return=e,t):((t=i(t,n)).return=e,t)}function d(e,t,n){if("string"==typeof t||"number"==typeof t)return(t=ua(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case we:return(n=aa(t.type,t.key,t.props,null,e.mode,n)).ref=rr(e,null,t),n.return=e,n;case ke:return(t=ca(t,e.mode,n)).return=e,t}if(nr(t)||je(t))return(t=oa(t,e.mode,n,null)).return=e,t;ir(e,t)}return null}function p(e,t,n,r){var i=null!==t?t.key:null;if("string"==typeof n||"number"==typeof n)return null!==i?null:o(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case we:return n.key===i?n.type===Pe?s(e,t,n.props.children,r,i):u(e,t,n,r):null;case ke:return n.key===i?c(e,t,n,r):null}if(nr(n)||je(n))return null!==i?null:s(e,t,n,r,null);ir(e,n)}return null}function h(e,t,n,r,i){if("string"==typeof r||"number"==typeof r)return o(t,e=e.get(n)||null,""+r,i);if("object"==typeof r&&null!==r){switch(r.$$typeof){case we:return e=e.get(null===r.key?n:r.key)||null,r.type===Pe?s(t,e,r.props.children,i,r.key):u(t,e,r,i);case ke:return c(t,e=e.get(null===r.key?n:r.key)||null,r,i)}if(nr(r)||je(r))return s(t,e=e.get(n)||null,r,i,null);ir(t,r)}return null}function m(i,a,o,u){for(var c=null,s=null,f=a,m=a=0,g=null;null!==f&&m<o.length;m++){f.index>m?(g=f,f=null):g=f.sibling;var y=p(i,f,o[m],u);if(null===y){null===f&&(f=g);break}e&&f&&null===y.alternate&&t(i,f),a=l(y,a,m),null===s?c=y:s.sibling=y,s=y,f=g}if(m===o.length)return n(i,f),c;if(null===f){for(;m<o.length;m++)null!==(f=d(i,o[m],u))&&(a=l(f,a,m),null===s?c=f:s.sibling=f,s=f);return c}for(f=r(i,f);m<o.length;m++)null!==(g=h(f,i,m,o[m],u))&&(e&&null!==g.alternate&&f.delete(null===g.key?m:g.key),a=l(g,a,m),null===s?c=g:s.sibling=g,s=g);return e&&f.forEach(function(e){return t(i,e)}),c}function g(i,a,o,u){var c=je(o);if("function"!=typeof c)throw f(Error("An object is not an iterable. This error is likely caused by a bug in React. Please file an issue."));if(null==(o=c.call(o)))throw f(Error("An iterable object provided no iterator."));for(var s=c=null,m=a,g=a=0,y=null,v=o.next();null!==m&&!v.done;g++,v=o.next()){m.index>g?(y=m,m=null):y=m.sibling;var b=p(i,m,v.value,u);if(null===b){null===m&&(m=y);break}e&&m&&null===b.alternate&&t(i,m),a=l(b,a,g),null===s?c=b:s.sibling=b,s=b,m=y}if(v.done)return n(i,m),c;if(null===m){for(;!v.done;g++,v=o.next())null!==(v=d(i,v.value,u))&&(a=l(v,a,g),null===s?c=v:s.sibling=v,s=v);return c}for(m=r(i,m);!v.done;g++,v=o.next())null!==(v=h(m,i,g,v.value,u))&&(e&&null!==v.alternate&&m.delete(null===v.key?g:v.key),a=l(v,a,g),null===s?c=v:s.sibling=v,s=v);return e&&m.forEach(function(e){return t(i,e)}),c}return function(e,r,l,o){var u="object"==typeof l&&null!==l&&l.type===Pe&&null===l.key;u&&(l=l.props.children);var c="object"==typeof l&&null!==l;if(c)switch(l.$$typeof){case we:e:{for(c=l.key,u=r;null!==u;){if(u.key===c){if(7===u.tag?l.type===Pe:u.elementType===l.type){n(e,u.sibling),(r=i(u,l.type===Pe?l.props.children:l.props)).ref=rr(e,u,l),r.return=e,e=r;break e}n(e,u);break}t(e,u),u=u.sibling}l.type===Pe?((r=oa(l.props.children,e.mode,o,l.key)).return=e,e=r):((o=aa(l.type,l.key,l.props,null,e.mode,o)).ref=rr(e,r,l),o.return=e,e=o)}return a(e);case ke:e:{for(u=l.key;null!==r;){if(r.key===u){if(4===r.tag&&r.stateNode.containerInfo===l.containerInfo&&r.stateNode.implementation===l.implementation){n(e,r.sibling),(r=i(r,l.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=ca(l,e.mode,o)).return=e,e=r}return a(e)}if("string"==typeof l||"number"==typeof l)return l=""+l,null!==r&&6===r.tag?(n(e,r.sibling),(r=i(r,l)).return=e,e=r):(n(e,r),(r=ua(l,e.mode,o)).return=e,e=r),a(e);if(nr(l))return m(e,r,l,o);if(je(l))return g(e,r,l,o);if(c&&ir(e,l),void 0===l&&!u)switch(e.tag){case 1:case 0:throw e=e.type,f(Error((e.displayName||e.name||"Component")+"(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null."))}return n(e,r)}}var ar=lr(!0),or=lr(!1),ur={},cr={current:ur},sr={current:ur},fr={current:ur};function dr(e){if(e===ur)throw f(Error("Expected host context to exist. This error is likely caused by a bug in React. Please file an issue."));return e}function pr(e,t){Mt(fr,t),Mt(sr,e),Mt(cr,ur),Ut(cr),Mt(cr,{isInAParentText:!1})}function hr(e){Ut(cr),Ut(sr),Ut(fr)}function mr(e){dr(fr.current);var t=dr(cr.current),n=e.type;n="AndroidTextInput"===n||"RCTMultilineTextInputView"===n||"RCTSinglelineTextInputView"===n||"RCTText"===n||"RCTVirtualText"===n,t!==(n=t.isInAParentText!==n?{isInAParentText:n}:t)&&(Mt(sr,e),Mt(cr,n))}function gr(e){sr.current===e&&(Ut(cr),Ut(sr))}var yr=1,vr=1,br=2,Tr={current:0};function Er(e){for(var t=e;null!==t;){if(13===t.tag){if(null!==t.memoizedState)return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.effectTag))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function xr(e,t){return{responder:e,props:t}}var Sr=0,wr=2,kr=4,Pr=8,Cr=16,_r=32,Rr=64,Nr=128,Ir=xe.ReactCurrentDispatcher,zr=0,Ur=null,Mr=null,Ar=null,Dr=null,Fr=null,jr=null,Or=0,Wr=null,Hr=0,Br=!1,Qr=null,Lr=0;function Yr(){throw f(Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem."))}function Vr(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!En(e[n],t[n]))return!1;return!0}function Xr(e,t,n,r,i,l){if(zr=l,Ur=t,Ar=null!==e?e.memoizedState:null,Ir.current=null===Ar?ai:oi,t=n(r,i),Br){do{Br=!1,Lr+=1,Ar=null!==e?e.memoizedState:null,jr=Dr,Wr=Fr=Mr=null,Ir.current=oi,t=n(r,i)}while(Br);Qr=null,Lr=0}if(Ir.current=li,(e=Ur).memoizedState=Dr,e.expirationTime=Or,e.updateQueue=Wr,e.effectTag|=Hr,e=null!==Mr&&null!==Mr.next,zr=0,jr=Fr=Dr=Ar=Mr=Ur=null,Or=0,Wr=null,Hr=0,e)throw f(Error("Rendered fewer hooks than expected. This may be caused by an accidental early return statement."));return t}function qr(){Ir.current=li,zr=0,jr=Fr=Dr=Ar=Mr=Ur=null,Or=0,Wr=null,Hr=0,Br=!1,Qr=null,Lr=0}function $r(){var e={memoizedState:null,baseState:null,queue:null,baseUpdate:null,next:null};return null===Fr?Dr=Fr=e:Fr=Fr.next=e,Fr}function Gr(){if(null!==jr)jr=(Fr=jr).next,Ar=null!==(Mr=Ar)?Mr.next:null;else{if(null===Ar)throw f(Error("Rendered more hooks than during the previous render."));var e={memoizedState:(Mr=Ar).memoizedState,baseState:Mr.baseState,queue:Mr.queue,baseUpdate:Mr.baseUpdate,next:null};Fr=null===Fr?Dr=e:Fr.next=e,Ar=Mr.next}return Fr}function Jr(e,t){return"function"==typeof t?t(e):t}function Kr(e){var t=Gr(),n=t.queue;if(null===n)throw f(Error("Should have a queue. This is likely a bug in React. Please file an issue."));if(n.lastRenderedReducer=e,0<Lr){var r=n.dispatch;if(null!==Qr){var i=Qr.get(n);if(void 0!==i){Qr.delete(n);var l=t.memoizedState;do{l=e(l,i.action),i=i.next}while(null!==i);return En(l,t.memoizedState)||(hi=!0),t.memoizedState=l,t.baseUpdate===n.last&&(t.baseState=l),n.lastRenderedState=l,[l,r]}}return[t.memoizedState,r]}r=n.last;var a=t.baseUpdate;if(l=t.baseState,null!==a?(null!==r&&(r.next=null),r=a.next):r=null!==r?r.next:null,null!==r){var o=i=null,u=r,c=!1;do{var s=u.expirationTime;s<zr?(c||(c=!0,o=a,i=l),s>Or&&(Or=s)):(Wl(s,u.suspenseConfig),l=u.eagerReducer===e?u.eagerState:e(l,u.action)),a=u,u=u.next}while(null!==u&&u!==r);c||(o=a,i=l),En(l,t.memoizedState)||(hi=!0),t.memoizedState=l,t.baseUpdate=o,t.baseState=i,n.lastRenderedState=l}return[t.memoizedState,n.dispatch]}function Zr(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===Wr?(Wr={lastEffect:null}).lastEffect=e.next=e:null===(t=Wr.lastEffect)?Wr.lastEffect=e.next=e:(n=t.next,t.next=e,e.next=n,Wr.lastEffect=e),e}function ei(e,t,n,r){var i=$r();Hr|=e,i.memoizedState=Zr(t,n,void 0,void 0===r?null:r)}function ti(e,t,n,r){var i=Gr();r=void 0===r?null:r;var l=void 0;if(null!==Mr){var a=Mr.memoizedState;if(l=a.destroy,null!==r&&Vr(r,a.deps))return void Zr(Sr,n,l,r)}Hr|=e,i.memoizedState=Zr(t,n,l,r)}function ni(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!==t&&void 0!==t?(e=e(),t.current=e,function(){t.current=null}):void 0}function ri(){}function ii(e,t,n){if(!(25>Lr))throw f(Error("Too many re-renders. React limits the number of renders to prevent an infinite loop."));var r=e.alternate;if(e===Ur||null!==r&&r===Ur)if(Br=!0,e={expirationTime:zr,suspenseConfig:null,action:n,eagerReducer:null,eagerState:null,next:null},null===Qr&&(Qr=new Map),void 0===(n=Qr.get(t)))Qr.set(t,e);else{for(t=n;null!==t.next;)t=t.next;t.next=e}else{var i=Nl(),l=qn.suspense;l={expirationTime:i=Il(i,e,l),suspenseConfig:l,action:n,eagerReducer:null,eagerState:null,next:null};var a=t.last;if(null===a)l.next=l;else{var o=a.next;null!==o&&(l.next=o),a.next=l}if(t.last=l,0===e.expirationTime&&(null===r||0===r.expirationTime)&&null!==(r=t.lastRenderedReducer))try{var u=t.lastRenderedState,c=r(u,n);if(l.eagerReducer=r,l.eagerState=c,En(c,u))return}catch(e){}zl(e,i)}}var li={readContext:An,useCallback:Yr,useContext:Yr,useEffect:Yr,useImperativeHandle:Yr,useLayoutEffect:Yr,useMemo:Yr,useReducer:Yr,useRef:Yr,useState:Yr,useDebugValue:Yr,useResponder:Yr},ai={readContext:An,useCallback:function(e,t){return $r().memoizedState=[e,void 0===t?null:t],e},useContext:An,useEffect:function(e,t){return ei(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,ei(4,36,ni.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ei(4,36,e,t)},useMemo:function(e,t){var n=$r();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=$r();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={last:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=ii.bind(null,Ur,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},$r().memoizedState=e},useState:function(e){var t=$r();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={last:null,dispatch:null,lastRenderedReducer:Jr,lastRenderedState:e}).dispatch=ii.bind(null,Ur,e),[t.memoizedState,e]},useDebugValue:ri,useResponder:xr},oi={readContext:An,useCallback:function(e,t){var n=Gr();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Vr(t,r[1])?r[0]:(n.memoizedState=[e,t],e)},useContext:An,useEffect:function(e,t){return ti(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,ti(4,36,ni.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ti(4,36,e,t)},useMemo:function(e,t){var n=Gr();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Vr(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)},useReducer:Kr,useRef:function(){return Gr().memoizedState},useState:function(e){return Kr(Jr)},useDebugValue:ri,useResponder:xr},ui=null,ci=null,si=!1;function fi(e,t){switch(e.tag){case 5:return null!==(t=ot(e.type,e.pendingProps))&&(e.stateNode=t,!0);case 6:return null!==(t=ot(e.pendingProps))&&(e.stateNode=t,!0);case 13:default:return!1}}function di(e){if(si){var t=ci;if(t){var n=t;if(!fi(e,t)){if(!(t=ot())||!fi(e,t))return e.effectTag|=2,si=!1,void(ui=e);var r=ui,i=na(5,null,null,0);i.elementType="DELETED",i.type="DELETED",i.stateNode=n,i.return=r,i.effectTag=8,null!==r.lastEffect?(r.lastEffect.nextEffect=i,r.lastEffect=i):r.firstEffect=r.lastEffect=i}ui=e,ci=ot()}else e.effectTag|=2,si=!1,ui=e}}var pi=xe.ReactCurrentOwner,hi=!1;function mi(e,t,n,r){t.child=null===e?or(t,null,n,r):ar(t,e.child,n,r)}function gi(e,t,n,r,i){n=n.render;var l=t.ref;return Mn(t,i),r=Xr(e,t,n,r,l,i),null===e||hi?(t.effectTag|=1,mi(e,t,r,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),_i(e,t,i))}function yi(e,t,n,r,i,l){if(null===e){var a=n.type;return"function"!=typeof a||ra(a)||void 0!==a.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=aa(n.type,null,r,null,t.mode,l)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=a,vi(e,t,a,r,i,l))}return a=e.child,i<l&&(i=a.memoizedProps,(n=null!==(n=n.compare)?n:Sn)(i,r)&&e.ref===t.ref)?_i(e,t,l):(t.effectTag|=1,(e=la(a,r)).ref=t.ref,e.return=t,t.child=e)}function vi(e,t,n,r,i,l){return null!==e&&Sn(e.memoizedProps,r)&&e.ref===t.ref&&(hi=!1,i<l)?_i(e,t,l):Ti(e,t,n,r,l)}function bi(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.effectTag|=128)}function Ti(e,t,n,r,i){var l=Wt(n)?jt:Dt.current;return l=Ot(t,l),Mn(t,i),n=Xr(e,t,n,r,l,i),null===e||hi?(t.effectTag|=1,mi(e,t,n,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),_i(e,t,i))}function Ei(e,t,n,r,i){if(Wt(n)){var l=!0;Yt(t)}else l=!1;if(Mn(t,i),null===t.stateNode)null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),Zn(t,n,r),tr(t,n,r,i),r=!0;else if(null===e){var a=t.stateNode,o=t.memoizedProps;a.props=o;var u=a.context,c=n.contextType;"object"==typeof c&&null!==c?c=An(c):c=Ot(t,c=Wt(n)?jt:Dt.current);var s=n.getDerivedStateFromProps,f="function"==typeof s||"function"==typeof a.getSnapshotBeforeUpdate;f||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(o!==r||u!==c)&&er(t,a,r,c),Dn=!1;var d=t.memoizedState;u=a.state=d;var p=t.updateQueue;null!==p&&(Yn(t,p,r,a,i),u=t.memoizedState),o!==r||d!==u||Ft.current||Dn?("function"==typeof s&&(Gn(t,n,s,r),u=t.memoizedState),(o=Dn||Kn(t,n,o,r,d,u,c))?(f||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||("function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount()),"function"==typeof a.componentDidMount&&(t.effectTag|=4)):("function"==typeof a.componentDidMount&&(t.effectTag|=4),t.memoizedProps=r,t.memoizedState=u),a.props=r,a.state=u,a.context=c,r=o):("function"==typeof a.componentDidMount&&(t.effectTag|=4),r=!1)}else a=t.stateNode,o=t.memoizedProps,a.props=t.type===t.elementType?o:wn(t.type,o),u=a.context,"object"==typeof(c=n.contextType)&&null!==c?c=An(c):c=Ot(t,c=Wt(n)?jt:Dt.current),(f="function"==typeof(s=n.getDerivedStateFromProps)||"function"==typeof a.getSnapshotBeforeUpdate)||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(o!==r||u!==c)&&er(t,a,r,c),Dn=!1,u=t.memoizedState,d=a.state=u,null!==(p=t.updateQueue)&&(Yn(t,p,r,a,i),d=t.memoizedState),o!==r||u!==d||Ft.current||Dn?("function"==typeof s&&(Gn(t,n,s,r),d=t.memoizedState),(s=Dn||Kn(t,n,o,r,u,d,c))?(f||"function"!=typeof a.UNSAFE_componentWillUpdate&&"function"!=typeof a.componentWillUpdate||("function"==typeof a.componentWillUpdate&&a.componentWillUpdate(r,d,c),"function"==typeof a.UNSAFE_componentWillUpdate&&a.UNSAFE_componentWillUpdate(r,d,c)),"function"==typeof a.componentDidUpdate&&(t.effectTag|=4),"function"==typeof a.getSnapshotBeforeUpdate&&(t.effectTag|=256)):("function"!=typeof a.componentDidUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),"function"!=typeof a.getSnapshotBeforeUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),t.memoizedProps=r,t.memoizedState=d),a.props=r,a.state=d,a.context=c,r=s):("function"!=typeof a.componentDidUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),"function"!=typeof a.getSnapshotBeforeUpdate||o===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),r=!1);return xi(e,t,n,r,l,i)}function xi(e,t,n,r,i,l){bi(e,t);var a=0!=(64&t.effectTag);if(!r&&!a)return i&&Vt(t,n,!1),_i(e,t,l);r=t.stateNode,pi.current=t;var o=a&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.effectTag|=1,null!==e&&a?(t.child=ar(t,e.child,null,l),t.child=ar(t,null,o,l)):mi(e,t,o,l),t.memoizedState=r.state,i&&Vt(t,n,!0),t.child}function Si(e){var t=e.stateNode;t.pendingContext?Qt(0,t.pendingContext,t.pendingContext!==t.context):t.context&&Qt(0,t.context,!1),pr(e,t.containerInfo)}var wi={};function ki(e,t,n){var r,i=t.mode,l=t.pendingProps,a=Tr.current,o=null,u=!1;if((r=0!=(64&t.effectTag))||(r=0!=(a&br)&&(null===e||null!==e.memoizedState)),r?(o=wi,u=!0,t.effectTag&=-65):null!==e&&null===e.memoizedState||void 0===l.fallback||!0===l.unstable_avoidThisFallback||(a|=vr),Mt(Tr,a&=yr),null===e)if(u){if(l=l.fallback,(e=oa(null,i,0,null)).return=t,0==(2&t.mode))for(u=null!==t.memoizedState?t.child.child:t.child,e.child=u;null!==u;)u.return=e,u=u.sibling;(n=oa(l,i,n,null)).return=t,e.sibling=n,i=e}else i=n=or(t,null,l.children,n);else{if(null!==e.memoizedState)if(i=(a=e.child).sibling,u){if(l=l.fallback,(n=la(a,a.pendingProps)).return=t,0==(2&t.mode)&&(u=null!==t.memoizedState?t.child.child:t.child)!==a.child)for(n.child=u;null!==u;)u.return=n,u=u.sibling;(l=la(i,l,i.expirationTime)).return=t,n.sibling=l,i=n,n.childExpirationTime=0,n=l}else i=n=ar(t,a.child,l.children,n);else if(a=e.child,u){if(u=l.fallback,(l=oa(null,i,0,null)).return=t,l.child=a,null!==a&&(a.return=l),0==(2&t.mode))for(a=null!==t.memoizedState?t.child.child:t.child,l.child=a;null!==a;)a.return=l,a=a.sibling;(n=oa(u,i,n,null)).return=t,l.sibling=n,n.effectTag|=2,i=l,l.childExpirationTime=0}else n=i=ar(t,a,l.children,n);t.stateNode=e.stateNode}return t.memoizedState=o,t.child=i,n}function Pi(e,t,n,r,i){var l=e.memoizedState;null===l?e.memoizedState={isBackwards:t,rendering:null,last:r,tail:n,tailExpiration:0,tailMode:i}:(l.isBackwards=t,l.rendering=null,l.last=r,l.tail=n,l.tailExpiration=0,l.tailMode=i)}function Ci(e,t,n){var r=t.pendingProps,i=r.revealOrder,l=r.tail;if(mi(e,t,r.children,n),0!=((r=Tr.current)&br))r=r&yr|br,t.effectTag|=64;else{if(null!==e&&0!=(64&e.effectTag))e:for(e=t.child;null!==e;){if(13===e.tag){if(null!==e.memoizedState){e.expirationTime<n&&(e.expirationTime=n);var a=e.alternate;null!==a&&a.expirationTime<n&&(a.expirationTime=n),Un(e.return,n)}}else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=yr}if(Mt(Tr,r),0==(2&t.mode))t.memoizedState=null;else switch(i){case"forwards":for(n=t.child,i=null;null!==n;)null!==(r=n.alternate)&&null===Er(r)&&(i=n),n=n.sibling;null===(n=i)?(i=t.child,t.child=null):(i=n.sibling,n.sibling=null),Pi(t,!1,i,n,l);break;case"backwards":for(n=null,i=t.child,t.child=null;null!==i;){if(null!==(r=i.alternate)&&null===Er(r)){t.child=i;break}r=i.sibling,i.sibling=n,n=i,i=r}Pi(t,!0,n,null,l);break;case"together":Pi(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function _i(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),t.childExpirationTime<n)return null;if(null!==e&&t.child!==e.child)throw f(Error("Resuming work not yet implemented."));if(null!==t.child){for(n=la(e=t.child,e.pendingProps,e.expirationTime),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=la(e,e.pendingProps,e.expirationTime)).return=t;n.sibling=null}return t.child}var Ri=void 0,Ni=void 0,Ii=void 0,zi=void 0;function Ui(e,t,n,r){for(var i=t.child;null!==i;){if(5===i.tag){var l=i.stateNode;n&&r&&(l=_t(l,i.type,i.memoizedProps)),gt(e,l.node)}else if(6===i.tag){if(l=i.stateNode,n&&r)throw Error("Not yet implemented.");gt(e,l.node)}else if(4!==i.tag){if(13===i.tag&&0!=(4&i.effectTag)&&(l=null!==i.memoizedState)){var a=i.child;if(null!==a&&(null!==a.child&&(a.child.return=a,Ui(e,a,!0,l)),null!==(l=a.sibling))){l.return=i,i=l;continue}}if(null!==i.child){i.child.return=i,i=i.child;continue}}if(i===t)break;for(;null===i.sibling;){if(null===i.return||i.return===t)return;i=i.return}i.sibling.return=i.return,i=i.sibling}}function Mi(e,t){switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Ai(e){switch(e.tag){case 1:Wt(e.type)&&Ht();var t=e.effectTag;return 2048&t?(e.effectTag=-2049&t|64,e):null;case 3:if(hr(),Bt(),0!=(64&(t=e.effectTag)))throw f(Error("The root failed to unmount after an error. This is likely a bug in React. Please file an issue."));return e.effectTag=-2049&t|64,e;case 5:return gr(e),null;case 13:return Ut(Tr),2048&(t=e.effectTag)?(e.effectTag=-2049&t|64,e):null;case 18:return null;case 19:return Ut(Tr),null;case 4:return hr(),null;case 10:return zn(e),null;default:return null}}function Di(e,t){return{value:e,source:t,stack:Nt(t)}}if(Ri=function(e,t,n,r){for(var i=t.child;null!==i;){if(5===i.tag){var l=i.stateNode;n&&r&&(l=_t(l,i.type,i.memoizedProps)),mt(e.node,l.node)}else if(6===i.tag){if(l=i.stateNode,n&&r)throw Error("Not yet implemented.");mt(e.node,l.node)}else if(4!==i.tag){if(13===i.tag&&0!=(4&i.effectTag)&&(l=null!==i.memoizedState)){var a=i.child;if(null!==a&&(null!==a.child&&(a.child.return=a,Ri(e,a,!0,l)),null!==(l=a.sibling))){l.return=i,i=l;continue}}if(null!==i.child){i.child.return=i,i=i.child;continue}}if(i===t)break;for(;null===i.sibling;){if(null===i.return||i.return===t)return;i=i.return}i.sibling.return=i.return,i=i.sibling}},Ni=function(e){var t=e.stateNode;if(null!==e.firstEffect){var n=t.containerInfo,r=ht(n);Ui(r,e,!1,!1),t.pendingChildren=r,e.effectTag|=4,yt(n,r)}},Ii=function(e,t,n,r){n=e.stateNode;var i=e.memoizedProps;if((e=null===t.firstEffect)&&i===r)t.stateNode=n;else{var l=t.stateNode;dr(cr.current);var a=null;i!==r&&(i=Ke(null,i,r,l.canonical.viewConfig.validAttributes),l.canonical.currentProps=r,a=i),e&&null===a?t.stateNode=n:(r=a,l=n.node,n={node:e?null!==r?pt(l,r):st(l):null!==r?dt(l,r):ft(l),canonical:n.canonical},t.stateNode=n,e?t.effectTag|=4:Ri(n,t,!1,!1))}},zi=function(e,t,n,r){n!==r&&(e=dr(fr.current),n=dr(cr.current),t.stateNode=kt(r,e,n,t),t.effectTag|=4)},"function"!=typeof u.ReactFiberErrorDialog.showErrorDialog)throw f(Error("Expected ReactFiberErrorDialog.showErrorDialog to be a function."));var Fi="function"==typeof WeakSet?WeakSet:Set;function ji(e,t){var n,r=t.source,i=t.stack;null===i&&null!==r&&(i=Nt(r)),t={componentName:null!==r?Oe(r.type):null,componentStack:null!==i?i:"",error:t.value,errorBoundary:null,errorBoundaryName:null,errorBoundaryFound:!1,willRetry:!1},null!==e&&1===e.tag&&(t.errorBoundary=e.stateNode,t.errorBoundaryName=Oe(e.type),t.errorBoundaryFound=!0,t.willRetry=!0);try{n=t,!1!==u.ReactFiberErrorDialog.showErrorDialog(n)&&console.error(n.error)}catch(e){setTimeout(function(){throw e})}}function Oi(e,t){try{t.props=e.memoizedProps,t.state=e.memoizedState,t.componentWillUnmount()}catch(t){ql(e,t)}}function Wi(e){var t=e.ref;if(null!==t)if("function"==typeof t)try{t(null)}catch(t){ql(e,t)}else t.current=null}function Hi(e,t,n){if(null!==(n=null!==(n=n.updateQueue)?n.lastEffect:null)){var r=n=n.next;do{if((r.tag&e)!==Sr){var i=r.destroy;r.destroy=void 0,void 0!==i&&i()}(r.tag&t)!==Sr&&(i=r.create,r.destroy=i()),r=r.next}while(r!==n)}}function Bi(e,t){switch("function"==typeof Zl&&Zl(e),e.tag){case 0:case 11:case 14:case 15:var n=e.updateQueue;if(null!==n&&null!==(n=n.lastEffect)){var r=n.next;mn(97<t?97:t,function(){var t=r;do{var n=t.destroy;if(void 0!==n){var i=e;try{n()}catch(e){ql(i,e)}}t=t.next}while(t!==r)})}break;case 1:Wi(e),"function"==typeof(t=e.stateNode).componentWillUnmount&&Oi(e,t);break;case 5:Wi(e);break;case 4:ht(e.stateNode.containerInfo)}}function Qi(e){var t=e.alternate;e.return=null,e.child=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null,e.alternate=null,e.firstEffect=null,e.lastEffect=null,e.pendingProps=null,e.memoizedProps=null,null!==t&&Qi(t)}function Li(e,t){switch(t.tag){case 0:case 11:case 14:case 15:return void Hi(kr,Pr,t);case 12:return;case 13:return null!==t.memoizedState&&(gl=dn()),void Yi(t);case 19:return void Yi(t)}switch(t.tag){case 1:case 5:case 6:case 20:break;case 3:case 4:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}function Yi(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new Fi),t.forEach(function(t){var r=Gl.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))})}}var Vi="function"==typeof WeakMap?WeakMap:Map;function Xi(e,t,n){(n=On(n,null)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){bl||(bl=!0,Tl=r),ji(e,t)},n}function qi(e,t,n){(n=On(n,null)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var i=t.value;n.payload=function(){return ji(e,t),r(i)}}var l=e.stateNode;return null!==l&&"function"==typeof l.componentDidCatch&&(n.callback=function(){"function"!=typeof r&&(null===El?El=new Set([this]):El.add(this),ji(e,t));var n=t.stack;this.componentDidCatch(t.value,{componentStack:null!==n?n:""})}),n}var $i=Math.ceil,Gi=xe.ReactCurrentDispatcher,Ji=xe.ReactCurrentOwner,Ki=0,Zi=8,el=16,tl=32,nl=0,rl=1,il=2,ll=3,al=4,ol=Ki,ul=null,cl=null,sl=0,fl=nl,dl=1073741823,pl=1073741823,hl=null,ml=!1,gl=0,yl=500,vl=null,bl=!1,Tl=null,El=null,xl=!1,Sl=null,wl=90,kl=0,Pl=null,Cl=0,_l=null,Rl=0;function Nl(){return(48&ol)!==Ki?1073741821-(dn()/10|0):0!==Rl?Rl:Rl=1073741821-(dn()/10|0)}function Il(e,t,n){if(0==(2&(t=t.mode)))return 1073741823;var r=pn();if(0==(4&t))return 99===r?1073741823:1073741822;if((ol&el)!==Ki)return sl;if(null!==n)e=1073741821-25*(1+((1073741821-e+(0|n.timeoutMs||5e3)/10)/25|0));else switch(r){case 99:e=1073741823;break;case 98:e=1073741821-10*(1+((1073741821-e+15)/10|0));break;case 97:case 96:e=1073741821-25*(1+((1073741821-e+500)/25|0));break;case 95:e=1;break;default:throw f(Error("Expected a valid priority level"))}return null!==ul&&e===sl&&--e,e}function zl(e,t){if(50<Cl)throw Cl=0,_l=null,f(Error("Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."));if(null!==(e=Ul(e,t))){e.pingTime=0;var n=pn();if(1073741823===t)if((ol&Zi)!==Ki&&(48&ol)===Ki)for(var r=Ol(e,1073741823,!0);null!==r;)r=r(!0);else Ml(e,99,1073741823),ol===Ki&&vn();else Ml(e,n,t);(4&ol)===Ki||98!==n&&99!==n||(null===Pl?Pl=new Map([[e,t]]):(void 0===(n=Pl.get(e))||n>t)&&Pl.set(e,t))}}function Ul(e,t){e.expirationTime<t&&(e.expirationTime=t);var n=e.alternate;null!==n&&n.expirationTime<t&&(n.expirationTime=t);var r=e.return,i=null;if(null===r&&3===e.tag)i=e.stateNode;else for(;null!==r;){if(n=r.alternate,r.childExpirationTime<t&&(r.childExpirationTime=t),null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t),null===r.return&&3===r.tag){i=r.stateNode;break}r=r.return}return null!==i&&(t>i.firstPendingTime&&(i.firstPendingTime=t),0===(e=i.lastPendingTime)||t<e)&&(i.lastPendingTime=t),i}function Ml(e,t,n){if(e.callbackExpirationTime<n){var r=e.callbackNode;null!==r&&r!==an&&$t(r),e.callbackExpirationTime=n,1073741823===n?e.callbackNode=yn(Al.bind(null,e,Ol.bind(null,e,n))):(r=null,1!==n&&(r={timeout:10*(1073741821-n)-dn()}),e.callbackNode=gn(t,Al.bind(null,e,Ol.bind(null,e,n)),r))}}function Al(e,t,n){var r=e.callbackNode,i=null;try{return null!==(i=t(n))?Al.bind(null,e,i):null}finally{null===i&&r===e.callbackNode&&(e.callbackNode=null,e.callbackExpirationTime=0)}}function Dl(e,t){var n=e.firstBatch;return!!(null!==n&&n._defer&&n._expirationTime>=t)&&(gn(97,function(){return n._onComplete(),null}),!0)}function Fl(){if(null!==Pl){var e=Pl;Pl=null,e.forEach(function(e,t){yn(Ol.bind(null,t,e))}),vn()}}function jl(e,t){e.finishedWork=null,e.finishedExpirationTime=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,Ct(n)),null!==cl)for(n=cl.return;null!==n;){var r=n;switch(r.tag){case 1:var i=r.type.childContextTypes;null!==i&&void 0!==i&&Ht();break;case 3:hr(),Bt();break;case 5:gr(r);break;case 4:hr();break;case 13:case 19:Ut(Tr);break;case 10:zn(r)}n=n.return}ul=e,cl=la(e.current,null),sl=t,fl=nl,pl=dl=1073741823,hl=null,ml=!1}function Ol(e,t,n){if((48&ol)!==Ki)throw f(Error("Should not already be working."));if(e.firstPendingTime<t)return null;if(n&&e.finishedExpirationTime===t)return Ql.bind(null,e);if(Yl(),e!==ul||t!==sl)jl(e,t);else if(fl===ll)if(ml)jl(e,t);else{var r=e.lastPendingTime;if(r<t)return Ol.bind(null,e,r)}if(null!==cl){r=ol,ol|=el;var i=Gi.current;if(null===i&&(i=li),Gi.current=li,n){if(1073741823!==t){var l=Nl();if(l<t)return ol=r,Nn(),Gi.current=i,Ol.bind(null,e,l)}}else Rl=0;for(;;)try{if(n)for(;null!==cl;)cl=Hl(cl);else for(;null!==cl&&!Gt();)cl=Hl(cl);break}catch(n){if(Nn(),qr(),null===(l=cl)||null===l.return)throw jl(e,t),ol=r,n;e:{var a=e,o=l.return,u=l,c=n,s=sl;if(u.effectTag|=1024,u.firstEffect=u.lastEffect=null,null!==c&&"object"==typeof c&&"function"==typeof c.then){var d=c,p=0!=(Tr.current&vr);c=o;do{var h;if((h=13===c.tag)&&(null!==c.memoizedState?h=!1:h=void 0!==(h=c.memoizedProps).fallback&&(!0!==h.unstable_avoidThisFallback||!p)),h){if(null===(o=c.updateQueue)?((o=new Set).add(d),c.updateQueue=o):o.add(d),0==(2&c.mode)){c.effectTag|=64,u.effectTag&=-1957,1===u.tag&&(null===u.alternate?u.tag=17:((s=On(1073741823,null)).tag=2,Hn(u,s))),u.expirationTime=1073741823;break e}u=a,a=s,null===(p=u.pingCache)?(p=u.pingCache=new Vi,o=new Set,p.set(d,o)):void 0===(o=p.get(d))&&(o=new Set,p.set(d,o)),o.has(a)||(o.add(a),u=$l.bind(null,u,d,a),d.then(u,u)),c.effectTag|=2048,c.expirationTime=s;break e}c=c.return}while(null!==c);c=Error((Oe(u.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display."+Nt(u))}fl!==al&&(fl=rl),c=Di(c,u),u=o;do{switch(u.tag){case 3:u.effectTag|=2048,u.expirationTime=s,Bn(u,s=Xi(u,c,s));break e;case 1:if(d=c,a=u.type,o=u.stateNode,0==(64&u.effectTag)&&("function"==typeof a.getDerivedStateFromError||null!==o&&"function"==typeof o.componentDidCatch&&(null===El||!El.has(o)))){u.effectTag|=2048,u.expirationTime=s,Bn(u,s=qi(u,d,s));break e}}u=u.return}while(null!==u)}cl=Bl(l)}if(ol=r,Nn(),Gi.current=i,null!==cl)return Ol.bind(null,e,t)}if(e.finishedWork=e.current.alternate,e.finishedExpirationTime=t,Dl(e,t))return null;switch(ul=null,fl){case nl:throw f(Error("Should have a work-in-progress."));case rl:return(r=e.lastPendingTime)<t?Ol.bind(null,e,r):n?Ql.bind(null,e):(jl(e,t),yn(Ol.bind(null,e,t)),null);case il:return 1073741823===dl&&!n&&10<(n=gl+yl-dn())?ml?(jl(e,t),Ol.bind(null,e,t)):(r=e.lastPendingTime)<t?Ol.bind(null,e,r):(e.timeoutHandle=Pt(Ql.bind(null,e),n),null):Ql.bind(null,e);case ll:if(!n){if(ml)return jl(e,t),Ol.bind(null,e,t);if((n=e.lastPendingTime)<t)return Ol.bind(null,e,n);if(1073741823!==pl?n=10*(1073741821-pl)-dn():1073741823===dl?n=0:(n=10*(1073741821-dl)-5e3,t=10*(1073741821-t)-(r=dn()),0>(n=r-n)&&(n=0),t<(n=(120>n?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*$i(n/1960))-n)&&(n=t)),10<n)return e.timeoutHandle=Pt(Ql.bind(null,e),n),null}return Ql.bind(null,e);case al:return!n&&1073741823!==dl&&null!==hl&&(r=dl,0>=(t=0|(i=hl).busyMinDurationMs)?t=0:(n=0|i.busyDelayMs,t=(r=dn()-(10*(1073741821-r)-(0|i.timeoutMs||5e3)))<=n?0:n+t-r),10<t)?(e.timeoutHandle=Pt(Ql.bind(null,e),t),null):Ql.bind(null,e);default:throw f(Error("Unknown root exit status."))}}function Wl(e,t){e<dl&&1<e&&(dl=e),null!==t&&e<pl&&1<e&&(pl=e,hl=t)}function Hl(e){var t=Jl(e.alternate,e,sl);return e.memoizedProps=e.pendingProps,null===t&&(t=Bl(e)),Ji.current=null,t}function Bl(e){cl=e;do{var t=cl.alternate;if(e=cl.return,0==(1024&cl.effectTag)){e:{var n=t,r=sl,i=(t=cl).pendingProps;switch(t.tag){case 2:case 16:break;case 15:case 0:break;case 1:Wt(t.type)&&Ht();break;case 3:hr(),Bt(),(r=t.stateNode).pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==n&&null!==n.child||(t.effectTag&=-3),Ni(t);break;case 5:gr(t),r=dr(fr.current);var l=t.type;if(null!==n&&null!=t.stateNode)Ii(n,t,l,i,r),n.ref!==t.ref&&(t.effectTag|=128);else if(i){dr(cr.current),n=i;var a=r;r=t,i=St,St+=2,l=xt(l);var o=Ke(null,Ye,n,l.validAttributes);a=ct(i,l.uiViewClassName,a,o,r),n=new wt(i,l,n,r),Ri(n={node:a,canonical:n},t,!1,!1),t.stateNode=n,null!==t.ref&&(t.effectTag|=128)}else if(null===t.stateNode)throw f(Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."));break;case 6:if(n&&null!=t.stateNode)zi(n,t,n.memoizedProps,i);else{if("string"!=typeof i&&null===t.stateNode)throw f(Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."));n=dr(fr.current),r=dr(cr.current),t.stateNode=kt(i,n,r,t)}break;case 11:break;case 13:if(Ut(Tr),i=t.memoizedState,0!=(64&t.effectTag)){t.expirationTime=r;break e}r=null!==i,i=!1,null!==n&&(i=null!==(l=n.memoizedState),r||null===l||null!==(l=n.child.sibling)&&(null!==(a=t.firstEffect)?(t.firstEffect=l,l.nextEffect=a):(t.firstEffect=t.lastEffect=l,l.nextEffect=null),l.effectTag=8)),r&&!i&&0!=(2&t.mode)&&(null===n&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!=(Tr.current&vr)?fl===nl&&(fl=il):fl!==nl&&fl!==il||(fl=ll)),r&&(t.effectTag|=4);break;case 7:case 8:case 12:break;case 4:hr(),Ni(t);break;case 10:zn(t);break;case 9:case 14:break;case 17:Wt(t.type)&&Ht();break;case 18:break;case 19:if(Ut(Tr),null===(i=t.memoizedState))break;if(l=0!=(64&t.effectTag),null===(a=i.rendering)){if(l)Mi(i,!1);else if(fl!==nl||null!==n&&0!=(64&n.effectTag))for(n=t.child;null!==n;){if(null!==(a=Er(n))){for(t.effectTag|=64,Mi(i,!1),null!==(n=a.updateQueue)&&(t.updateQueue=n,t.effectTag|=4),t.firstEffect=t.lastEffect=null,n=r,r=t.child;null!==r;)l=n,(i=r).effectTag&=2,i.nextEffect=null,i.firstEffect=null,i.lastEffect=null,null===(a=i.alternate)?(i.childExpirationTime=0,i.expirationTime=l,i.child=null,i.memoizedProps=null,i.memoizedState=null,i.updateQueue=null,i.dependencies=null):(i.childExpirationTime=a.childExpirationTime,i.expirationTime=a.expirationTime,i.child=a.child,i.memoizedProps=a.memoizedProps,i.memoizedState=a.memoizedState,i.updateQueue=a.updateQueue,l=a.dependencies,i.dependencies=null===l?null:{expirationTime:l.expirationTime,firstContext:l.firstContext,responders:l.responders}),r=r.sibling;Mt(Tr,Tr.current&yr|br),t=t.child;break e}n=n.sibling}}else{if(!l)if(null!==(n=Er(a))){if(t.effectTag|=64,l=!0,Mi(i,!0),null===i.tail&&"hidden"===i.tailMode){null!==(n=n.updateQueue)&&(t.updateQueue=n,t.effectTag|=4),null!==(t=t.lastEffect=i.lastEffect)&&(t.nextEffect=null);break}}else dn()>i.tailExpiration&&1<r&&(t.effectTag|=64,l=!0,Mi(i,!1),t.expirationTime=t.childExpirationTime=r-1);i.isBackwards?(a.sibling=t.child,t.child=a):(null!==(n=i.last)?n.sibling=a:t.child=a,i.last=a)}if(null!==i.tail){0===i.tailExpiration&&(i.tailExpiration=dn()+500),n=i.tail,i.rendering=n,i.tail=n.sibling,i.lastEffect=t.lastEffect,n.sibling=null,r=Tr.current,Mt(Tr,r=l?r&yr|br:r&yr),t=n;break e}break;case 20:break;default:throw f(Error("Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue."))}t=null}if(n=cl,1===sl||1!==n.childExpirationTime){for(r=0,i=n.child;null!==i;)l=i.expirationTime,a=i.childExpirationTime,l>r&&(r=l),a>r&&(r=a),i=i.sibling;n.childExpirationTime=r}if(null!==t)return t;null!==e&&0==(1024&e.effectTag)&&(null===e.firstEffect&&(e.firstEffect=cl.firstEffect),null!==cl.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=cl.firstEffect),e.lastEffect=cl.lastEffect),1<cl.effectTag&&(null!==e.lastEffect?e.lastEffect.nextEffect=cl:e.firstEffect=cl,e.lastEffect=cl))}else{if(null!==(t=Ai(cl)))return t.effectTag&=1023,t;null!==e&&(e.firstEffect=e.lastEffect=null,e.effectTag|=1024)}if(null!==(t=cl.sibling))return t;cl=e}while(null!==cl);return fl===nl&&(fl=al),null}function Ql(e){var t=pn();return mn(99,Ll.bind(null,e,t)),null!==Sl&&gn(97,function(){return Yl(),null}),null}function Ll(e,t){if(Yl(),(48&ol)!==Ki)throw f(Error("Should not already be working."));var n=e.finishedWork,r=e.finishedExpirationTime;if(null===n)return null;if(e.finishedWork=null,e.finishedExpirationTime=0,n===e.current)throw f(Error("Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue."));e.callbackNode=null,e.callbackExpirationTime=0;var i=n.expirationTime,l=n.childExpirationTime;if(i=l>i?l:i,e.firstPendingTime=i,i<e.lastPendingTime&&(e.lastPendingTime=i),e===ul&&(cl=ul=null,sl=0),1<n.effectTag?null!==n.lastEffect?(n.lastEffect.nextEffect=n,i=n.firstEffect):i=n:i=n.firstEffect,null!==i){l=ol,ol|=tl,Ji.current=null,vl=i;do{try{for(;null!==vl;){if(0!=(256&vl.effectTag)){var a=vl.alternate,o=vl;switch(o.tag){case 0:case 11:case 15:Hi(wr,Sr,o);break;case 1:if(256&o.effectTag&&null!==a){var u=a.memoizedProps,c=a.memoizedState,s=o.stateNode,d=s.getSnapshotBeforeUpdate(o.elementType===o.type?u:wn(o.type,u),c);s.__reactInternalSnapshotBeforeUpdate=d}break;case 3:case 5:case 6:case 4:case 17:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}vl=vl.nextEffect}}catch(e){if(null===vl)throw f(Error("Should be working on an effect."));ql(vl,e),vl=vl.nextEffect}}while(null!==vl);vl=i;do{try{for(a=t;null!==vl;){var p=vl.effectTag;if(128&p){var h=vl.alternate;if(null!==h){var m=h.ref;null!==m&&("function"==typeof m?m(null):m.current=null)}}switch(14&p){case 2:vl.effectTag&=-3;break;case 6:vl.effectTag&=-3,Li(vl.alternate,vl);break;case 4:Li(vl.alternate,vl);break;case 8:e:for(c=u=vl,s=a,d=c;;)if(Bi(d,s),null!==d.child)d.child.return=d,d=d.child;else{if(d===c)break;for(;null===d.sibling;){if(null===d.return||d.return===c)break e;d=d.return}d.sibling.return=d.return,d=d.sibling}Qi(u)}vl=vl.nextEffect}}catch(e){if(null===vl)throw f(Error("Should be working on an effect."));ql(vl,e),vl=vl.nextEffect}}while(null!==vl);e.current=n,vl=i;do{try{for(p=r;null!==vl;){var g=vl.effectTag;if(36&g){var y=vl.alternate;switch(m=p,(h=vl).tag){case 0:case 11:case 15:Hi(Cr,_r,h);break;case 1:var v=h.stateNode;if(4&h.effectTag)if(null===y)v.componentDidMount();else{var b=h.elementType===h.type?y.memoizedProps:wn(h.type,y.memoizedProps);v.componentDidUpdate(b,y.memoizedState,v.__reactInternalSnapshotBeforeUpdate)}var T=h.updateQueue;null!==T&&Vn(0,T,v);break;case 3:var E=h.updateQueue;if(null!==E){if(a=null,null!==h.child)switch(h.child.tag){case 5:a=h.child.stateNode.canonical;break;case 1:a=h.child.stateNode}Vn(0,E,a)}break;case 5:if(null===y&&4&h.effectTag)throw f(Error("The current renderer does not support mutation. This error is likely caused by a bug in React. Please file an issue."));break;case 6:case 4:case 12:break;case 13:case 19:case 17:case 20:break;default:throw f(Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."))}}if(128&g){var x=vl.ref;if(null!==x){var S=vl.stateNode;switch(vl.tag){case 5:var w=S.canonical;break;default:w=S}"function"==typeof x?x(w):x.current=w}}512&g&&(xl=!0),vl=vl.nextEffect}}catch(e){if(null===vl)throw f(Error("Should be working on an effect."));ql(vl,e),vl=vl.nextEffect}}while(null!==vl);vl=null,on(),ol=l}else e.current=n;if(xl)xl=!1,Sl=e,kl=r,wl=t;else for(vl=i;null!==vl;)t=vl.nextEffect,vl.nextEffect=null,vl=t;if(0!==(t=e.firstPendingTime)?Ml(e,g=Tn(g=Nl(),t),t):El=null,"function"==typeof Kl&&Kl(n.stateNode,r),1073741823===t?e===_l?Cl++:(Cl=0,_l=e):Cl=0,bl)throw bl=!1,e=Tl,Tl=null,e;return(ol&Zi)!==Ki?null:(vn(),null)}function Yl(){if(null===Sl)return!1;var e=Sl,t=kl,n=wl;return Sl=null,kl=0,wl=90,mn(97<n?97:n,Vl.bind(null,e,t))}function Vl(e){if((48&ol)!==Ki)throw f(Error("Cannot flush passive effects while already rendering."));var t=ol;for(ol|=tl,e=e.current.firstEffect;null!==e;){try{var n=e;if(0!=(512&n.effectTag))switch(n.tag){case 0:case 11:case 15:Hi(Nr,Sr,n),Hi(Sr,Rr,n)}}catch(t){if(null===e)throw f(Error("Should be working on an effect."));ql(e,t)}n=e.nextEffect,e.nextEffect=null,e=n}return ol=t,vn(),!0}function Xl(e,t,n){Hn(e,t=Xi(e,t=Di(n,t),1073741823)),null!==(e=Ul(e,1073741823))&&Ml(e,99,1073741823)}function ql(e,t){if(3===e.tag)Xl(e,e,t);else for(var n=e.return;null!==n;){if(3===n.tag){Xl(n,e,t);break}if(1===n.tag){var r=n.stateNode;if("function"==typeof n.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===El||!El.has(r))){Hn(n,e=qi(n,e=Di(t,e),1073741823)),null!==(n=Ul(n,1073741823))&&Ml(n,99,1073741823);break}}n=n.return}}function $l(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),ul===e&&sl===n?fl===ll||fl===il&&1073741823===dl&&dn()-gl<yl?jl(e,sl):ml=!0:e.lastPendingTime<n||(0!==(t=e.pingTime)&&t<n||(e.pingTime=n,e.finishedExpirationTime===n&&(e.finishedExpirationTime=0,e.finishedWork=null),Ml(e,t=Tn(t=Nl(),n),n)))}function Gl(e,t){var n=e.stateNode;null!==n&&n.delete(t),n=Tn(n=Nl(),t=Il(n,e,null)),null!==(e=Ul(e,t))&&Ml(e,n,t)}var Jl=void 0;Jl=function(e,t,n){var r=t.expirationTime;if(null!==e){if(e.memoizedProps!==t.pendingProps||Ft.current)hi=!0;else if(r<n){switch(hi=!1,t.tag){case 3:Si(t);break;case 5:mr(t);break;case 1:Wt(t.type)&&Yt(t);break;case 4:pr(t,t.stateNode.containerInfo);break;case 10:In(t,t.memoizedProps.value);break;case 13:if(null!==t.memoizedState)return 0!==(r=t.child.childExpirationTime)&&r>=n?ki(e,t,n):(Mt(Tr,Tr.current&yr),null!==(t=_i(e,t,n))?t.sibling:null);Mt(Tr,Tr.current&yr);break;case 19:if(r=t.childExpirationTime>=n,0!=(64&e.effectTag)){if(r)return Ci(e,t,n);t.effectTag|=64}var i=t.memoizedState;if(null!==i&&(i.rendering=null,i.tail=null),Mt(Tr,Tr.current),!r)return null}return _i(e,t,n)}}else hi=!1;switch(t.expirationTime=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=Ot(t,Dt.current),Mn(t,n),i=Xr(null,t,r,e,i,n),t.effectTag|=1,"object"==typeof i&&null!==i&&"function"==typeof i.render&&void 0===i.$$typeof){if(t.tag=1,qr(),Wt(r)){var l=!0;Yt(t)}else l=!1;t.memoizedState=null!==i.state&&void 0!==i.state?i.state:null;var a=r.getDerivedStateFromProps;"function"==typeof a&&Gn(t,r,a,e),i.updater=Jn,t.stateNode=i,i._reactInternalFiber=t,tr(t,r,e,n),t=xi(null,t,r,!0,l,n)}else t.tag=0,mi(null,t,i,n),t=t.child;return t;case 16:switch(i=t.elementType,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=kn(i),t.type=i,l=t.tag=ia(i),e=wn(i,e),l){case 0:t=Ti(null,t,i,e,n);break;case 1:t=Ei(null,t,i,e,n);break;case 11:t=gi(null,t,i,e,n);break;case 14:t=yi(null,t,i,wn(i.type,e),r,n);break;default:throw f(Error("Element type is invalid. Received a promise that resolves to: "+i+". Lazy element type must resolve to a class or function."))}return t;case 0:return r=t.type,i=t.pendingProps,Ti(e,t,r,i=t.elementType===r?i:wn(r,i),n);case 1:return r=t.type,i=t.pendingProps,Ei(e,t,r,i=t.elementType===r?i:wn(r,i),n);case 3:if(Si(t),null===(r=t.updateQueue))throw f(Error("If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue."));return i=null!==(i=t.memoizedState)?i.element:null,Yn(t,r,t.pendingProps,null,n),(r=t.memoizedState.element)===i?t=_i(e,t,n):(mi(e,t,r,n),t=t.child),t;case 5:return mr(t),null===e&&di(t),r=t.pendingProps.children,bi(e,t),mi(e,t,r,n),t.child;case 6:return null===e&&di(t),null;case 13:return ki(e,t,n);case 4:return pr(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=ar(t,null,r,n):mi(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,gi(e,t,r,i=t.elementType===r?i:wn(r,i),n);case 7:return mi(e,t,t.pendingProps,n),t.child;case 8:case 12:return mi(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,a=t.memoizedProps,In(t,l=i.value),null!==a){var o=a.value;if(0===(l=En(o,l)?0:0|("function"==typeof r._calculateChangedBits?r._calculateChangedBits(o,l):1073741823))){if(a.children===i.children&&!Ft.current){t=_i(e,t,n);break e}}else for(null!==(o=t.child)&&(o.return=t);null!==o;){var u=o.dependencies;if(null!==u){a=o.child;for(var c=u.firstContext;null!==c;){if(c.context===r&&0!=(c.observedBits&l)){1===o.tag&&((c=On(n,null)).tag=2,Hn(o,c)),o.expirationTime<n&&(o.expirationTime=n),null!==(c=o.alternate)&&c.expirationTime<n&&(c.expirationTime=n),Un(o.return,n),u.expirationTime<n&&(u.expirationTime=n);break}c=c.next}}else a=10===o.tag&&o.type===t.type?null:o.child;if(null!==a)a.return=o;else for(a=o;null!==a;){if(a===t){a=null;break}if(null!==(o=a.sibling)){o.return=a.return,a=o;break}a=a.return}o=a}}mi(e,t,i.children,n),t=t.child}return t;case 9:return i=t.type,r=(l=t.pendingProps).children,Mn(t,n),r=r(i=An(i,l.unstable_observedBits)),t.effectTag|=1,mi(e,t,r,n),t.child;case 14:return l=wn(i=t.type,t.pendingProps),yi(e,t,i,l=wn(i.type,l),r,n);case 15:return vi(e,t,t.type,t.pendingProps,r,n);case 17:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:wn(r,i),null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),t.tag=1,Wt(r)?(e=!0,Yt(t)):e=!1,Mn(t,n),Zn(t,r,i),tr(t,r,i,n),xi(null,t,r,!0,e,n);case 19:return Ci(e,t,n)}throw f(Error("Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue."))};var Kl=null,Zl=null;function ea(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);Kl=function(e){try{t.onCommitFiberRoot(n,e,void 0,64==(64&e.current.effectTag))}catch(e){}},Zl=function(e){try{t.onCommitFiberUnmount(n,e)}catch(e){}}}catch(e){}return!0}function ta(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childExpirationTime=this.expirationTime=0,this.alternate=null}function na(e,t,n,r){return new ta(e,t,n,r)}function ra(e){return!(!(e=e.prototype)||!e.isReactComponent)}function ia(e){if("function"==typeof e)return ra(e)?1:0;if(void 0!==e&&null!==e){if((e=e.$$typeof)===ze)return 11;if(e===Ae)return 14}return 2}function la(e,t){var n=e.alternate;return null===n?((n=na(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.effectTag=0,n.nextEffect=null,n.firstEffect=null,n.lastEffect=null),n.childExpirationTime=e.childExpirationTime,n.expirationTime=e.expirationTime,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{expirationTime:t.expirationTime,firstContext:t.firstContext,responders:t.responders},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function aa(e,t,n,r,i,l){var a=2;if(r=e,"function"==typeof e)ra(e)&&(a=1);else if("string"==typeof e)a=5;else e:switch(e){case Pe:return oa(n.children,i,l,t);case Ie:a=8,i|=7;break;case Ce:a=8,i|=1;break;case _e:return(e=na(12,n,t,8|i)).elementType=_e,e.type=_e,e.expirationTime=l,e;case Ue:return(e=na(13,n,t,i)).type=Ue,e.elementType=Ue,e.expirationTime=l,e;case Me:return(e=na(19,n,t,i)).elementType=Me,e.expirationTime=l,e;default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case Re:a=10;break e;case Ne:a=9;break e;case ze:a=11;break e;case Ae:a=14;break e;case De:a=16,r=null;break e}throw f(Error("Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: "+(null==e?e:typeof e)+"."))}return(t=na(a,n,t,i)).elementType=e,t.type=r,t.expirationTime=l,t}function oa(e,t,n,r){return(e=na(7,e,r,t)).expirationTime=n,e}function ua(e,t,n){return(e=na(6,e,null,t)).expirationTime=n,e}function ca(e,t,n){return(t=na(4,null!==e.children?e.children:[],e.key,t)).expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function sa(e,t,n){this.tag=t,this.current=null,this.containerInfo=e,this.pingCache=this.pendingChildren=null,this.finishedExpirationTime=0,this.finishedWork=null,this.timeoutHandle=-1,this.pendingContext=this.context=null,this.hydrate=n,this.callbackNode=this.firstBatch=null,this.pingTime=this.lastPendingTime=this.firstPendingTime=this.callbackExpirationTime=0}function fa(e){var t=e._reactInternalFiber;if(void 0===t){if("function"==typeof e.render)throw f(Error("Unable to find node on an unmounted component."));throw f(Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(e)))}return null===(e=Qe(t))?null:e.stateNode}function da(e,t,n,r){var i=t.current,l=Nl(),a=qn.suspense;i=Il(l,i,a),l=t.current;e:if(n){n=n._reactInternalFiber;t:{if(2!==We(n)||1!==n.tag)throw f(Error("Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue."));var o=n;do{switch(o.tag){case 3:o=o.stateNode.context;break t;case 1:if(Wt(o.type)){o=o.stateNode.__reactInternalMemoizedMergedChildContext;break t}}o=o.return}while(null!==o);throw f(Error("Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue."))}if(1===n.tag){var u=n.type;if(Wt(u)){n=Lt(n,u,o);break e}}n=o}else n=At;return null===t.context?t.context=n:t.pendingContext=n,t=r,(a=On(i,a)).payload={element:e},null!==(t=void 0===t?null:t)&&(a.callback=t),Hn(l,a),zl(l,i),i}function pa(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:ke,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}function ha(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var ma;ma=function(){throw f(Error("getInspectorDataForViewTag() is not available in production"))};var ga=nativeFabricUIManager.dispatchCommand;function ya(e){return null==e?null:"number"==typeof e?e:e._nativeTag?e._nativeTag:e.canonical&&e.canonical._nativeTag?e.canonical._nativeTag:null==(e=fa(e))?e:e.canonical?e.canonical._nativeTag:e._nativeTag}nt=function(e,t){var n=ol;ol|=1;try{return e(t)}finally{(ol=n)===Ki&&vn()}},rt=function(){(49&ol)===Ki&&(Fl(),Yl())};var va,ba,Ta=new Map,Ea={NativeComponent:(function(e,t){return(function(n){function r(){if(!(this instanceof r))throw new TypeError("Cannot call a class as a function");var e=n.apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?this:e}return ha(r,n),r.prototype.blur=function(){u.TextInputState.blurTextInput(e(this))},r.prototype.focus=function(){u.TextInputState.focusTextInput(e(this))},r.prototype.measure=function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measure(r.node,Le(this,n)):u.UIManager.measure(e(this),Le(this,n)))},r.prototype.measureInWindow=function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measureInWindow(r.node,Le(this,n)):u.UIManager.measureInWindow(e(this),Le(this,n)))},r.prototype.measureLayout=function(n,r,i){var l=void 0;try{l=t(this)}catch(e){}null==l||l.canonical||(l=void 0,"number"==typeof n?l=n:n._nativeTag&&(l=n._nativeTag),null!=l&&u.UIManager.measureLayout(e(this),l,Le(this,i),Le(this,r)))},r.prototype.setNativeProps=function(e){var n=void 0;try{n=t(this)}catch(e){}if(null!=n&&!n.canonical){var r=n._nativeTag||n.canonical._nativeTag;n=n.viewConfig||n.canonical.viewConfig,null!=(e=Ke(null,Ye,e,n.validAttributes))&&u.UIManager.updateView(r,n.uiViewClassName,e)}},r})(c.Component)})(ya,fa),findNodeHandle:ya,setNativeProps:function(){},dispatchCommand:function(e,t,n){null!=e._nativeTag&&null!=e._internalInstanceHandle&&ga(e._internalInstanceHandle.stateNode.node,t,n)},render:function(e,t,n){var r=Ta.get(t);if(!r){r=new sa(t,0,!1);var i=na(3,null,null,0);r.current=i,i.stateNode=r,Ta.set(t,r)}da(e,r,null,n);e:if(e=r.current,e.child)switch(e.child.tag){case 5:e=e.child.stateNode.canonical;break e;default:e=e.child.stateNode}else e=null;return e},unmountComponentAtNode:function(e){var t=Ta.get(e);t&&da(null,t,null,function(){Ta.delete(e)})},createPortal:function(e,t){return pa(e,t,null,2<arguments.length&&void 0!==arguments[2]?arguments[2]:null)},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{NativeMethodsMixin:(function(e,t){return{measure:function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measure(r.node,Le(this,n)):u.UIManager.measure(e(this),Le(this,n)))},measureInWindow:function(n){var r=void 0;try{r=t(this)}catch(e){}null!=r&&(r.canonical?nativeFabricUIManager.measureInWindow(r.node,Le(this,n)):u.UIManager.measureInWindow(e(this),Le(this,n)))},measureLayout:function(n,r,i){var l=void 0;try{l=t(this)}catch(e){}null==l||l.canonical||(l=void 0,"number"==typeof n?l=n:n._nativeTag&&(l=n._nativeTag),null!=l&&u.UIManager.measureLayout(e(this),l,Le(this,i),Le(this,r)))},setNativeProps:function(e){var n=void 0;try{n=t(this)}catch(e){}if(null!=n&&!n.canonical){var r=n._nativeTag||n.canonical._nativeTag;n=n.viewConfig||n.canonical.viewConfig,null!=(e=Ke(null,Ye,e,n.validAttributes))&&u.UIManager.updateView(r,n.uiViewClassName,e)}},focus:function(){u.TextInputState.focusTextInput(e(this))},blur:function(){u.TextInputState.blurTextInput(e(this))}}})(ya,fa)}};ba=(va={findFiberByHostInstance:Ee,getInspectorDataForViewTag:ma,bundleType:0,version:"16.8.6",rendererPackageName:"react-native-renderer"}).findFiberByHostInstance,ea(o({},va,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:xe.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Qe(e))?null:e.stateNode},findFiberByHostInstance:function(e){return ba?ba(e):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}));var xa={default:Ea},Sa=xa&&Ea||xa;i.exports=Sa.default||Sa},347,[16,92,160,13,166]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),s=t(r(d[2])),u=t(r(d[3])),l=t(r(d[4])),o=t(r(d[5])),c=t(r(d[6])),f=r(d[7]),v=r(d[8]),p=r(d[9]),h=r(d[10]),_=(function(t){function f(){var t;(0,n.default)(this,f),(t=(0,u.default)(this,(0,l.default)(f).call(this,c.default)))._supportedEvents=['change','memoryWarning','blur','focus'],t.isAvailable=!0,t._eventHandlers=t._supportedEvents.reduce(function(t,n){return t[n]=new Map,t},{}),t.currentState=c.default.getConstants().initialAppState;var s=!1;return t.addListener('appStateDidChange',function(n){s=!0,t.currentState=n.app_state}),c.default.getCurrentAppState(function(n){s||t.currentState===n.app_state||(t.currentState=n.app_state,t.emit('appStateDidChange',n))},p),t}return(0,o.default)(f,t),(0,s.default)(f,[{key:"addEventListener",value:function(t,n){switch(h(-1!==this._supportedEvents.indexOf(t),'Trying to subscribe to unknown event: "%s"',t),t){case'change':this._eventHandlers[t].set(n,this.addListener('appStateDidChange',function(t){n(t.app_state)}));break;case'memoryWarning':this._eventHandlers[t].set(n,this.addListener('memoryWarning',n));break;case'blur':case'focus':this._eventHandlers[t].set(n,this.addListener('appStateFocusChange',function(s){'blur'!==t||s||n(),'focus'===t&&s&&n()}))}}},{key:"removeEventListener",value:function(t,n){h(-1!==this._supportedEvents.indexOf(t),'Trying to remove listener for unknown event: "%s"',t),this._eventHandlers[t].has(n)&&(this._eventHandlers[t].get(n).remove(),this._eventHandlers[t].delete(n))}}]),f})(v);function y(){h(!1,"Cannot use AppState module when native RCTAppState is not included in the build.\nEither include it, or check AppState.isAvailable before calling any methods.")}var S=(function(t){function c(){var t,s;(0,n.default)(this,c);for(var o=arguments.length,f=new Array(o),v=0;v<o;v++)f[v]=arguments[v];return(s=(0,u.default)(this,(t=(0,l.default)(c)).call.apply(t,[this].concat(f)))).isAvailable=!1,s.currentState=null,s}return(0,o.default)(c,t),(0,s.default)(c,[{key:"addEventListener",value:function(){y()}},{key:"removeEventListener",value:function(){y()}},{key:"addListener",value:function(){y()}},{key:"removeAllListeners",value:function(){y()}},{key:"removeSubscription",value:function(){y()}}]),c})(f);_=c.default?new _:new S,m.exports=_},348,[3,4,5,6,9,10,349,49,123,350,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).getEnforcing('AppState');e.default=u},349,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(){for(var o=arguments.length,n=new Array(o),s=0;s<o;s++)n[s]=arguments[s];if(1===n.length&&n[0]instanceof Error){var t=n[0];console.error('Error: "'+t.message+'". Stack:\n'+t.stack)}else console.error.apply(console,n)}},350,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),u=t(r(d[2])),l=t(r(d[3])),o=u.default,s={_getRequests:[],_getKeys:[],_immediate:null,getItem:function(t,n){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(u,l){o.multiGet([t],function(t,o){var s=o&&o[0]&&o[0][1]?o[0][1]:null,f=c(t);n&&n(f&&f[0],s),f?l(f[0]):u(s)})})},setItem:function(t,n,u){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(l,s){o.multiSet([[t,n]],function(t){var n=c(t);u&&u(n&&n[0]),n?s(n[0]):l(null)})})},removeItem:function(t,n){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(u,l){o.multiRemove([t],function(t){var o=c(t);n&&n(o&&o[0]),o?l(o[0]):u(null)})})},mergeItem:function(t,n,u){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(l,s){o.multiMerge([[t,n]],function(t){var n=c(t);u&&u(n&&n[0]),n?s(n[0]):l(null)})})},clear:function(t){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(n,u){o.clear(function(l){t&&t(f(l)),l&&f(l)?u(f(l)):n(null)})})},getAllKeys:function(t){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(n,u){o.getAllKeys(function(l,o){t&&t(f(l),o),l?u(f(l)):n(o)})})},flushGetRequests:function(){var t=this._getRequests,u=this._getKeys;this._getRequests=[],this._getKeys=[],(0,l.default)(o,'RCTAsyncStorage not available'),o.multiGet(u,function(u,l){var o={};l&&l.forEach(function(t){var u=(0,n.default)(t,2),l=u[0],s=u[1];return o[l]=s,s});for(var s=t.length,c=0;c<s;c++){var f=t[c],v=f.keys.map(function(t){return[t,o[t]]});f.callback&&f.callback(null,v),f.resolve&&f.resolve(v)}})},multiGet:function(t,n){var u=this;this._immediate||(this._immediate=setImmediate(function(){u._immediate=null,u.flushGetRequests()}));var l={keys:t,callback:n,keyIndex:this._getKeys.length,resolve:null,reject:null},o=new Promise(function(t,n){l.resolve=t,l.reject=n});return this._getRequests.push(l),t.forEach(function(t){-1===u._getKeys.indexOf(t)&&u._getKeys.push(t)}),o},multiSet:function(t,n){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(u,l){o.multiSet(t,function(t){var o=c(t);n&&n(o),o?l(o):u(null)})})},multiRemove:function(t,n){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(u,l){o.multiRemove(t,function(t){var o=c(t);n&&n(o),o?l(o):u(null)})})},multiMerge:function(t,n){return(0,l.default)(o,'RCTAsyncStorage not available'),new Promise(function(u,l){o.multiMerge(t,function(t){var o=c(t);n&&n(o),o?l(o):u(null)})})}};function c(t){return t?(Array.isArray(t)?t:[t]).map(function(t){return f(t)}):null}function f(t){if(!t)return null;var n=new Error(t.message);return n.key=t.key,n}o.multiMerge||(delete s.mergeItem,delete s.multiMerge),m.exports=s},351,[3,26,352,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])),c=o.get('AsyncSQLiteDBStorage')||o.get('AsyncLocalStorage');e.default=c},352,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1]));m.exports={getString:function(){return t.default.getString()},setString:function(n){t.default.setString(n)}}},353,[3,354]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).getEnforcing('Clipboard');e.default=o},354,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=(function(){function s(){n(this,s)}return o(s,null,[{key:"open",value:function(n){return t.async(function(t){for(;;)switch(t.prev=t.next){case 0:throw new Error('DatePickerAndroid is not supported on this platform.');case 1:case"end":return t.stop()}},null,this)}}]),s})();s.dateSetAction='dateSetAction',s.dismissedAction='dismissedAction',m.exports=s},355,[268,4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1]));m.exports=t.default},356,[3,63]); -__d(function(g,r,i,a,m,e,d){'use strict';var o=r(d[0]),n=o(r(d[1])),l=o(r(d[2])),t={canRecordVideos:function(o){return(0,l.default)(n.default,'ImagePickerIOS is not available'),n.default.canRecordVideos(o)},canUseCamera:function(o){return(0,l.default)(n.default,'ImagePickerIOS is not available'),n.default.canUseCamera(o)},openCameraDialog:function(o,t,u){(0,l.default)(n.default,'ImagePickerIOS is not available');var s={videoMode:!0,unmirrorFrontFacingCamera:!1};return null!=o.videoMode&&(s.videoMode=o.videoMode),null!=o.unmirrorFrontFacingCamera&&(s.unmirrorFrontFacingCamera=o.unmirrorFrontFacingCamera),n.default.openCameraDialog(s,t,u)},openSelectDialog:function(o,t,u){(0,l.default)(n.default,'ImagePickerIOS is not available');var s={showImages:!0,showVideos:!1};return null!=o.showImages&&(s.showImages=o.showImages),null!=o.showVideos&&(s.showVideos=o.showVideos),n.default.openSelectDialog(s,t,u)}};m.exports=t},357,[3,358,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('ImagePickerIOS');e.default=u},358,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),u=t(r(d[2])),l=t(r(d[3])),o=t(r(d[4])),f=t(r(d[5])),s=t(r(d[6])),v=(r(d[7]),r(d[8])),c=(r(d[9]),r(d[10])),L=(function(t){function v(){return(0,n.default)(this,v),(0,l.default)(this,(0,o.default)(v).call(this,s.default))}return(0,f.default)(v,t),(0,u.default)(v,[{key:"addEventListener",value:function(t,n){this.addListener(t,n)}},{key:"removeEventListener",value:function(t,n){this.removeListener(t,n)}},{key:"openURL",value:function(t){return this._validateURL(t),s.default.openURL(t)}},{key:"canOpenURL",value:function(t){return this._validateURL(t),s.default.canOpenURL(t)}},{key:"openSettings",value:function(){return s.default.openSettings()}},{key:"getInitialURL",value:function(){return s.default.getInitialURL()}},{key:"sendIntent",value:function(t,n){return new Promise(function(t,n){return n(new Error('Unsupported'))})}},{key:"_validateURL",value:function(t){c('string'==typeof t,'Invalid URL: should be a string. Was: '+t),c(t,'Invalid URL: cannot be empty')}}]),v})(v);m.exports=new L},359,[3,4,5,6,9,10,360,223,123,58,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[2])),f='android'===n(r(d[3])).default.OS?o.getEnforcing('IntentAndroid'):o.getEnforcing('LinkingManager');e.default=f},360,[3,2,24,58]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),o=r(d[1]),t=o.currentCentroidXOfTouchesChangedAfter,u=o.currentCentroidYOfTouchesChangedAfter,s=o.previousCentroidXOfTouchesChangedAfter,c=o.previousCentroidYOfTouchesChangedAfter,p=o.currentCentroidX,v=o.currentCentroidY,h={_initializeGestureState:function(n){n.moveX=0,n.moveY=0,n.x0=0,n.y0=0,n.dx=0,n.dy=0,n.vx=0,n.vy=0,n.numberActiveTouches=0,n._accountsForMovesUpTo=0},_updateGestureStateOnMove:function(n,o){n.numberActiveTouches=o.numberActiveTouches,n.moveX=t(o,n._accountsForMovesUpTo),n.moveY=u(o,n._accountsForMovesUpTo);var p=n._accountsForMovesUpTo,v=s(o,p),h=t(o,p),l=c(o,p),S=u(o,p),R=n.dx+(h-v),T=n.dy+(S-l),f=o.mostRecentTimeStamp-n._accountsForMovesUpTo;n.vx=(R-n.dx)/f,n.vy=(T-n.dy)/f,n.dx=R,n.dy=T,n._accountsForMovesUpTo=o.mostRecentTimeStamp},create:function(o){var t={handle:null},u={stateID:Math.random(),moveX:0,moveY:0,x0:0,y0:0,dx:0,dy:0,vx:0,vy:0,numberActiveTouches:0,_accountsForMovesUpTo:0};return{panHandlers:{onStartShouldSetResponder:function(n){return null!=o.onStartShouldSetPanResponder&&o.onStartShouldSetPanResponder(n,u)},onMoveShouldSetResponder:function(n){return null!=o.onMoveShouldSetPanResponder&&o.onMoveShouldSetPanResponder(n,u)},onStartShouldSetResponderCapture:function(n){return 1===n.nativeEvent.touches.length&&h._initializeGestureState(u),u.numberActiveTouches=n.touchHistory.numberActiveTouches,null!=o.onStartShouldSetPanResponderCapture&&o.onStartShouldSetPanResponderCapture(n,u)},onMoveShouldSetResponderCapture:function(n){var t=n.touchHistory;return u._accountsForMovesUpTo!==t.mostRecentTimeStamp&&(h._updateGestureStateOnMove(u,t),!!o.onMoveShouldSetPanResponderCapture&&o.onMoveShouldSetPanResponderCapture(n,u))},onResponderGrant:function(s){return t.handle||(t.handle=n.createInteractionHandle()),u.x0=p(s.touchHistory),u.y0=v(s.touchHistory),u.dx=0,u.dy=0,o.onPanResponderGrant&&o.onPanResponderGrant(s,u),null==o.onShouldBlockNativeResponder||o.onShouldBlockNativeResponder(s,u)},onResponderReject:function(n){l(t,o.onPanResponderReject,n,u)},onResponderRelease:function(n){l(t,o.onPanResponderRelease,n,u),h._initializeGestureState(u)},onResponderStart:function(n){var t=n.touchHistory;u.numberActiveTouches=t.numberActiveTouches,o.onPanResponderStart&&o.onPanResponderStart(n,u)},onResponderMove:function(n){var t=n.touchHistory;u._accountsForMovesUpTo!==t.mostRecentTimeStamp&&(h._updateGestureStateOnMove(u,t),o.onPanResponderMove&&o.onPanResponderMove(n,u))},onResponderEnd:function(n){var s=n.touchHistory;u.numberActiveTouches=s.numberActiveTouches,l(t,o.onPanResponderEnd,n,u)},onResponderTerminate:function(n){l(t,o.onPanResponderTerminate,n,u),h._initializeGestureState(u)},onResponderTerminationRequest:function(n){return null==o.onPanResponderTerminationRequest||o.onPanResponderTerminationRequest(n,u)}},getInteractionHandle:function(){return t.handle}}}};function l(o,t,u,s){o.handle&&(n.clearInteractionHandle(o.handle),o.handle=null),t&&t(u,s)}m.exports=h},361,[223,362]); -__d(function(g,r,i,a,m,e,d){var n={centroidDimension:function(t,o,u,c){var f=t.touchBank,s=0,h=0,v=1===t.numberActiveTouches?t.touchBank[t.indexOfSingleActiveTouch]:null;if(null!==v)v.touchActive&&v.currentTimeStamp>o&&(s+=c&&u?v.currentPageX:c&&!u?v.currentPageY:!c&&u?v.previousPageX:v.previousPageY,h=1);else for(var C=0;C<f.length;C++){var l=f[C];if(null!==l&&void 0!==l&&l.touchActive&&l.currentTimeStamp>=o){s+=c&&u?l.currentPageX:c&&!u?l.currentPageY:!c&&u?l.previousPageX:l.previousPageY,h++}}return h>0?s/h:n.noCentroid},currentCentroidXOfTouchesChangedAfter:function(t,o){return n.centroidDimension(t,o,!0,!0)},currentCentroidYOfTouchesChangedAfter:function(t,o){return n.centroidDimension(t,o,!1,!0)},previousCentroidXOfTouchesChangedAfter:function(t,o){return n.centroidDimension(t,o,!0,!1)},previousCentroidYOfTouchesChangedAfter:function(t,o){return n.centroidDimension(t,o,!1,!1)},currentCentroidX:function(t){return n.centroidDimension(t,0,!0,!0)},currentCentroidY:function(t){return n.centroidDimension(t,0,!1,!0)},noCentroid:-1};m.exports=n},362,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),s=n(r(d[1])),o=n(r(d[2])),t=n(r(d[3])),E=n(r(d[4])),A=n(r(d[5])),u=n(r(d[6])),_=n(r(d[7])),S=(r(d[8]),Object.freeze({GRANTED:'granted',DENIED:'denied',NEVER_ASK_AGAIN:'never_ask_again'})),l=Object.freeze({READ_CALENDAR:'android.permission.READ_CALENDAR',WRITE_CALENDAR:'android.permission.WRITE_CALENDAR',CAMERA:'android.permission.CAMERA',READ_CONTACTS:'android.permission.READ_CONTACTS',WRITE_CONTACTS:'android.permission.WRITE_CONTACTS',GET_ACCOUNTS:'android.permission.GET_ACCOUNTS',ACCESS_FINE_LOCATION:'android.permission.ACCESS_FINE_LOCATION',ACCESS_COARSE_LOCATION:'android.permission.ACCESS_COARSE_LOCATION',RECORD_AUDIO:'android.permission.RECORD_AUDIO',READ_PHONE_STATE:'android.permission.READ_PHONE_STATE',CALL_PHONE:'android.permission.CALL_PHONE',READ_CALL_LOG:'android.permission.READ_CALL_LOG',WRITE_CALL_LOG:'android.permission.WRITE_CALL_LOG',ADD_VOICEMAIL:'com.android.voicemail.permission.ADD_VOICEMAIL',USE_SIP:'android.permission.USE_SIP',PROCESS_OUTGOING_CALLS:'android.permission.PROCESS_OUTGOING_CALLS',BODY_SENSORS:'android.permission.BODY_SENSORS',SEND_SMS:'android.permission.SEND_SMS',RECEIVE_SMS:'android.permission.RECEIVE_SMS',READ_SMS:'android.permission.READ_SMS',RECEIVE_WAP_PUSH:'android.permission.RECEIVE_WAP_PUSH',RECEIVE_MMS:'android.permission.RECEIVE_MMS',READ_EXTERNAL_STORAGE:'android.permission.READ_EXTERNAL_STORAGE',WRITE_EXTERNAL_STORAGE:'android.permission.WRITE_EXTERNAL_STORAGE'}),R=(function(){function n(){(0,t.default)(this,n),this.PERMISSIONS=l,this.RESULTS=S}return(0,E.default)(n,[{key:"checkPermission",value:function(n){return console.warn('"PermissionsAndroid.checkPermission" is deprecated. Use "PermissionsAndroid.check" instead'),console.warn('"PermissionsAndroid" module works only for Android platform.'),Promise.resolve(!1)}},{key:"check",value:function(n){return console.warn('"PermissionsAndroid" module works only for Android platform.'),Promise.resolve(!1)}},{key:"requestPermission",value:function(n,s){var t;return o.default.async(function(E){for(;;)switch(E.prev=E.next){case 0:return console.warn('"PermissionsAndroid.requestPermission" is deprecated. Use "PermissionsAndroid.request" instead'),console.warn('"PermissionsAndroid" module works only for Android platform.'),E.abrupt("return",Promise.resolve(!1));case 4:return E.next=6,o.default.awrap(this.request(n,s));case 6:return t=E.sent,E.abrupt("return",t===this.RESULTS.GRANTED);case 8:case"end":return E.stop()}},null,this)}},{key:"request",value:function(n,t){return o.default.async(function(E){for(;;)switch(E.prev=E.next){case 0:return console.warn('"PermissionsAndroid" module works only for Android platform.'),E.abrupt("return",Promise.resolve(this.RESULTS.DENIED));case 3:if((0,_.default)(u.default,'PermissionsAndroid is not installed correctly.'),!t){E.next=10;break}return E.next=7,o.default.awrap(u.default.shouldShowRequestPermissionRationale(n));case 7:if(!E.sent||!A.default){E.next=10;break}return E.abrupt("return",new Promise(function(o,E){var _=(0,s.default)({},t);A.default.showAlert(_,function(){return E(new Error('Error showing rationale'))},function(){return o(u.default.requestPermission(n))})}));case 10:return E.abrupt("return",u.default.requestPermission(n));case 11:case"end":return E.stop()}},null,this)}},{key:"requestMultiple",value:function(n){return console.warn('"PermissionsAndroid" module works only for Android platform.'),Promise.resolve({})}}]),n})();R=new R,m.exports=R},363,[3,54,268,4,5,141,364,18,58]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).get('PermissionsAndroid');e.default=o},364,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o=t(r(d[1])),n=t(r(d[2])),l=t(r(d[3])),u=r(d[4]),c=r(d[5]),s=new u(l.default),f=new Map,v=(function(){function t(n){var l=this;(0,o.default)(this,t),this._data={},this._remoteNotificationCompleteCallbackCalled=!1,this._isRemote=n.remote,this._isRemote&&(this._notificationId=n.notificationId),n.remote?Object.keys(n).forEach(function(t){var o=n[t];'aps'===t?(l._alert=o.alert,l._sound=o.sound,l._badgeCount=o.badge,l._category=o.category,l._contentAvailable=o['content-available'],l._threadID=o['thread-id']):l._data[t]=o}):(this._badgeCount=n.applicationIconBadgeNumber,this._sound=n.soundName,this._alert=n.alertBody,this._data=n.userInfo,this._category=n.category)}return(0,n.default)(t,null,[{key:"presentLocalNotification",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.presentLocalNotification(t)}},{key:"scheduleLocalNotification",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.scheduleLocalNotification(t)}},{key:"cancelAllLocalNotifications",value:function(){c(l.default,'PushNotificationManager is not available.'),l.default.cancelAllLocalNotifications()}},{key:"removeAllDeliveredNotifications",value:function(){c(l.default,'PushNotificationManager is not available.'),l.default.removeAllDeliveredNotifications()}},{key:"getDeliveredNotifications",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.getDeliveredNotifications(t)}},{key:"removeDeliveredNotifications",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.removeDeliveredNotifications(t)}},{key:"setApplicationIconBadgeNumber",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.setApplicationIconBadgeNumber(t)}},{key:"getApplicationIconBadgeNumber",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.getApplicationIconBadgeNumber(t)}},{key:"cancelLocalNotifications",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.cancelLocalNotifications(t)}},{key:"getScheduledLocalNotifications",value:function(t){c(l.default,'PushNotificationManager is not available.'),l.default.getScheduledLocalNotifications(t)}},{key:"addEventListener",value:function(o,n){var l;c('notification'===o||'register'===o||'registrationError'===o||'localNotification'===o,'PushNotificationIOS only supports `notification`, `register`, `registrationError`, and `localNotification` events'),'notification'===o?l=s.addListener("remoteNotificationReceived",function(o){n(new t(o))}):'localNotification'===o?l=s.addListener("localNotificationReceived",function(o){n(new t(o))}):'register'===o?l=s.addListener("remoteNotificationsRegistered",function(t){n(t.deviceToken)}):'registrationError'===o&&(l=s.addListener("remoteNotificationRegistrationError",function(t){n(t)})),f.set(o,l)}},{key:"removeEventListener",value:function(t,o){c('notification'===t||'register'===t||'registrationError'===t||'localNotification'===t,'PushNotificationIOS only supports `notification`, `register`, `registrationError`, and `localNotification` events');var n=f.get(t);n&&(n.remove(),f.delete(t))}},{key:"requestPermissions",value:function(t){var o={};return o=t?{alert:!!t.alert,badge:!!t.badge,sound:!!t.sound}:{alert:!0,badge:!0,sound:!0},c(l.default,'PushNotificationManager is not available.'),l.default.requestPermissions(o)}},{key:"abandonPermissions",value:function(){c(l.default,'PushNotificationManager is not available.'),l.default.abandonPermissions()}},{key:"checkPermissions",value:function(t){c('function'==typeof t,'Must provide a valid callback'),c(l.default,'PushNotificationManager is not available.'),l.default.checkPermissions(t)}},{key:"getInitialNotification",value:function(){return c(l.default,'PushNotificationManager is not available.'),l.default.getInitialNotification().then(function(o){return o&&new t(o)})}}]),(0,n.default)(t,[{key:"finish",value:function(t){this._isRemote&&this._notificationId&&!this._remoteNotificationCompleteCallbackCalled&&(this._remoteNotificationCompleteCallbackCalled=!0,c(l.default,'PushNotificationManager is not available.'),l.default.onFinishRemoteNotification(this._notificationId,t))}},{key:"getMessage",value:function(){return this._alert}},{key:"getSound",value:function(){return this._sound}},{key:"getCategory",value:function(){return this._category}},{key:"getAlert",value:function(){return this._alert}},{key:"getContentAvailable",value:function(){return this._contentAvailable}},{key:"getBadgeCount",value:function(){return this._badgeCount}},{key:"getData",value:function(){return this._data}},{key:"getThreadID",value:function(){return this._threadID}}]),t})();v.FetchResult={NewData:'UIBackgroundFetchResultNewData',NoData:'UIBackgroundFetchResultNoData',ResultFailed:'UIBackgroundFetchResultFailed'},m.exports=v},365,[3,4,5,366,123,18]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[1])).get('PushNotificationManager');e.default=o},366,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),s=t(r(d[1])),n=t(r(d[2])),c=r(d[3]),l=r(d[4]),u=[],o={_settings:n.default&&n.default.getConstants().settings,get:function(t){return this._settings[t]},set:function(t){this._settings=(0,s.default)(this._settings,t),n.default.setValues(t)},watchKeys:function(t,s){'string'==typeof t&&(t=[t]),l(Array.isArray(t),'keys should be a string or array of strings');var n=u.length;return u.push({keys:t,callback:s}),n},clearWatch:function(t){t<u.length&&(u[t]={keys:[],callback:null})},_sendObservations:function(t){var s=this;Object.keys(t).forEach(function(n){var c=t[n],l=s._settings[n]!==c;s._settings[n]=c,l&&u.forEach(function(t){-1!==t.keys.indexOf(n)&&t.callback&&t.callback()})})}};c.addListener('settingsUpdated',o._sendObservations.bind(o)),m.exports=o},367,[3,16,368,46,18]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('SettingsManager');e.default=n},368,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),o=t(r(d[2])),s=t(r(d[3])),u=(t(r(d[4])),r(d[5]),r(d[6])),c=r(d[7]),l=(function(){function t(){(0,n.default)(this,t)}return(0,o.default)(t,null,[{key:"share",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return u('object'==typeof t&&null!==t,'Content to share must be a valid object'),u('string'==typeof t.url||'string'==typeof t.message,'At least one of URL and message is required'),u('object'==typeof n&&null!==n,'Options must be a valid object'),new Promise(function(o,l){var f=c(n.tintColor);u(s.default,'NativeActionSheetManager is not registered on iOS, but it should be.'),s.default.showShareActionSheetWithOptions({message:'string'==typeof t.message?t.message:void 0,url:'string'==typeof t.url?t.url:void 0,subject:n.subject,tintColor:null!=f?f:void 0,excludedActivityTypes:n.excludedActivityTypes},function(t){return l(t)},function(t,n){o(t?{action:'sharedAction',activityType:n}:{action:'dismissedAction'})})})}}]),t})();l.sharedAction='sharedAction',l.dismissedAction='dismissedAction',m.exports=l},369,[3,4,5,333,370,58,18,82]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('ShareModule');e.default=u},370,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),u=t(r(d[1])),n=t(r(d[2])),f=t(r(d[3])),l=t(r(d[4])),s=t(r(d[5])),c=(function(t){function s(){return(0,u.default)(this,s),(0,n.default)(this,(0,f.default)(s).apply(this,arguments))}return(0,l.default)(s,t),s})(r(d[6]));m.exports=new c(s.default)},371,[3,4,6,9,10,321,123]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t(r(d[1])),s=t(r(d[2])),u=t(r(d[3])),o=t(r(d[4])),c=(function(){function t(){(0,s.default)(this,t)}return(0,u.default)(t,null,[{key:"open",value:function(t){return n.default.async(function(n){for(;;)switch(n.prev=n.next){case 0:if(!o.default){n.next=4;break}return n.abrupt("return",o.default.open(t));case 4:return n.abrupt("return",Promise.reject({message:'TimePickerAndroid is not supported on this platform.'}));case 5:case"end":return n.stop()}},null,this)}}]),t})();c.timeSetAction='timeSetAction',c.dismissedAction='dismissedAction',m.exports=c},372,[3,268,4,5,373]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])).get('TimePickerAndroid');e.default=u},373,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o={show:function(o,s){t(!1,'ToastAndroid is not supported on this platform.')},showWithGravity:function(o,s,n){t(!1,'ToastAndroid is not supported on this platform.')},showWithGravityAndOffset:function(o,s,n,p,f){t(!1,'ToastAndroid is not supported on this platform.')}};m.exports=o},374,[20]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(){var t=u.default.get('window'),n=f.useState(!1)[1].bind(null,function(t){return!t}),c=f.useState(t)[0];return f.useEffect(function(){return u.default.addEventListener('change',n),u.default.get('window')!==c&&n(),function(){u.default.removeEventListener('change',n)}},[n,c]),t};var u=n(r(d[2])),f=t(r(d[3]))},375,[2,3,62,13]); -__d(function(g,r,i,a,m,e,d){'use strict';var A=r(d[0])({BOM:"\ufeff",BULLET:"\u2022",BULLET_SP:"\xa0\u2022\xa0",MIDDOT:"\xb7",MIDDOT_SP:"\xa0\xb7\xa0",MIDDOT_KATAKANA:"\u30fb",MDASH:"\u2014",MDASH_SP:"\xa0\u2014\xa0",NDASH:"\u2013",NDASH_SP:"\xa0\u2013\xa0",NBSP:"\xa0",PIZZA:"\ud83c\udf55",TRIANGLE_LEFT:"\u25c0",TRIANGLE_RIGHT:"\u25b6"});m.exports=A},376,[38]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0])(r(d[1])),n=(r(d[2]),!1),o=0;function u(u){var l=arguments.length>1&&void 0!==arguments[1]&&arguments[1];n||(n=!0,0===u[0]&&(t.default.vibrate(),u=u.slice(1)),0!==u.length?setTimeout(function(){return f(++o,u,l,1)},u[0]):n=!1)}function f(u,l,v,c){if(n&&u===o){if(t.default.vibrate(),c>=l.length){if(!v)return void(n=!1);c=0}setTimeout(function(){return f(u,l,v,c+1)},l[c])}}var l={vibrate:function(){var o=arguments.length>0&&void 0!==arguments[0]?arguments[0]:400,f=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(!n)if('number'==typeof o)t.default.vibrate();else{if(!Array.isArray(o))throw new Error('Vibration pattern should be a number or array');u(o,f)}},cancel:function(){n=!1}};m.exports=l},377,[3,378,58]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])).getEnforcing('Vibration');e.default=n},378,[2,24]); -__d(function(g,r,i,a,m,e,d){'use strict';var n,t=r(d[0]),u=r(d[1]),l=r(d[2]),o=r(d[3]),s=r(d[4]);n=(function(n){function c(){return t(this,c),l(this,o(c).apply(this,arguments))}return s(c,n),u(c,[{key:"render",value:function(){return null}}],[{key:"ignoreWarnings",value:function(n){}},{key:"install",value:function(){}},{key:"uninstall",value:function(){}}]),c})(r(d[5]).Component),m.exports=n},379,[4,5,6,9,10,13]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),s=n.shape({x:n.number,y:n.number});m.exports=s},380,[69]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})}),r(d[3])},381,[382,1126,1124,1449]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.appNavigate=b,e.redirectWithStoredParams=R,e.redirectToStaticPage=L,e.reloadNow=function(){return function(t,o){t((0,h.setFatalError)(void 0));var n=o()['features/base/connection'].locationURL;v.default.info("Reloading the conference using URL: "+n),'ReactNative'===navigator.product?t(b((0,l.toURLString)(n))):t(C())}},e.reloadWithStoredParams=C,e.maybeRedirectToWelcomePage=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(o,n){var c=n()['features/base/config'].enableClosePage;if(c){var s=n()['features/base/jwt'].isGuest;return window.sessionStorage.setItem('guest',s),void o(L("static/"+(t.feedbackSubmitted?'close.html':'close2.html')))}t.showThankYou&&o((0,p.showNotification)({titleArguments:{appName:(0,w.getName)()},titleKey:'dialog.thankYou'})),n()['features/base/config'].enableWelcomePage&&setTimeout(function(){o(R('/'))},t.showThankYou?3e3:500)}};var o=t(r(d[1])),n=r(d[2]),c=r(d[3]),s=r(d[4]),u=r(d[5]),f=r(d[6]),l=r(d[7]),p=r(d[8]),h=r(d[9]),w=r(d[10]),v=t(r(d[11]));function b(t){return function(p,h){var b,R,L,C,k,U,S,x,N,P,W;return o.default.async(function(T){for(;;)switch(T.prev=T.next){case 0:if((b=(0,l.parseURIString)(t))&&b.host||(R=(0,l.parseURIString)((0,w.getDefaultURL)(h)),b?(b.host=R.host,b.hostname=R.hostname,b.pathname=R.pathname+b.pathname.substr(1),b.port=R.port,b.protocol=R.protocol):b=R),b.protocol||(b.protocol='https:'),C=(L=b).contextRoot,k=L.host,U=L.room,S=new URL(b.toString()),'ReactNative'===navigator.product&&p((0,s.disconnect)()),p((0,c.configWillLoad)(S,U)),'http:'!==(x=b.protocol.toLowerCase())&&'https:'!==x&&(x='https:'),P=(N=x+"//"+k+(C||'/'))+"config.js",U&&(P+="?room="+U.toLowerCase()),U||(W=(0,c.restoreConfig)(N)),W){T.next=30;break}return T.prev=14,T.next=17,o.default.awrap((0,u.loadConfig)(P));case 17:W=T.sent,p((0,c.storeConfig)(N,W)),T.next=30;break;case 21:if(T.prev=21,T.t0=T.catch(14),W=(0,c.restoreConfig)(N)){T.next=30;break}if(!U){T.next=28;break}return p((0,c.loadConfigError)(T.t0,S)),T.abrupt("return");case 28:v.default.warn('Failed to load config but there is no room, applying a fake one'),W=(0,c.createFakeConfig)(N);case 30:if(h()['features/base/config'].locationURL===S){T.next=33;break}return p((0,c.loadConfigError)(new Error('Config no longer needed!'),S)),T.abrupt("return");case 33:p((0,s.setLocationURL)(S)),p((0,c.setConfig)(W)),p((0,n.setRoom)(U)),U&&'ReactNative'===navigator.product&&(p((0,f.createDesiredLocalTracks)()),p((0,s.connect)()));case 37:case"end":return T.stop()}},null,this,[[14,21]])}}function R(t){return function(o,n){var c=n()['features/base/connection'].locationURL,s=new URL(c.href);s.pathname=t,window.location.assign(s.toString())}}function L(t){return function(){var o=window.location,n=t;n.startsWith('/')||(n.startsWith('./')&&(n=n.substring(2)),n=(0,l.getLocationContextRoot)(o)+n),o.pathname=n}}function C(){return function(t,o){var n=o()['features/base/connection'].locationURL,c=window.location,s=c.search;c.replace(n.toString()),window.self!==window.top&&n.search===s&&c.reload()}}},382,[3,268,383,838,1051,388,793,801,587,1106,1124,1125]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})}),r(d[4]),r(d[5])},383,[384,1058,1059,1060,1062,1096]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.authStatusChanged=function(n,t){return{type:l.AUTH_STATUS_CHANGED,authEnabled:n,authLogin:t}},e.conferenceFailed=S,e.conferenceJoined=R,e.conferenceLeft=D,e.conferenceSubjectChanged=y,e.conferenceWillJoin=L,e.conferenceWillLeave=function(n){return{type:l.CONFERENCE_WILL_LEAVE,conference:n}},e.createConference=function(){return function(n,o){var u=o(),f=u['features/base/connection'],s=f.connection,C=f.locationURL;if(!s)throw new Error('Cannot create a conference without a connection!');var p=u['features/base/conference'],l=p.password,v=p.room;if(!v)throw new Error('Cannot join a conference without a room name!');var S=s.initJitsiConference(v.toLowerCase(),(0,t.default)({},u['features/base/config'],{applicationName:(0,c.getName)(),getWiFiStatsMethod:(0,_.getJitsiMeetGlobalNS)().getWiFiStats,confID:""+C.host+C.pathname}));s[E.JITSI_CONNECTION_CONFERENCE_KEY]=S,S[A.JITSI_CONFERENCE_URL_KEY]=C,n(O(S)),T(S,n),(0,N.sendLocalParticipant)(u,S),S.join(l)}},e.checkIfCanJoin=function(){return function(n,t){var o=t()['features/base/conference'],c=o.authRequired,u=o.password;c&&n(O(c)),c&&c.join(u)}},e.dataChannelOpened=function(){return{type:l.DATA_CHANNEL_OPENED}},e.kickedOut=I,e.lockStateChanged=h,e.onStartMutedPolicyChanged=J,e.p2pStatusChanged=function(n){return{type:l.P2P_STATUS_CHANGED,p2p:n}},e.sendTones=function(n,t,o){return{type:l.SEND_TONES,tones:n,duration:t,pause:o}},e.setDesktopSharingEnabled=function(n){return{type:l.SET_DESKTOP_SHARING_ENABLED,desktopSharingEnabled:n}},e.setFollowMe=function(n){return{type:l.SET_FOLLOW_ME,enabled:n}},e.setMaxReceiverVideoQuality=function(n){return{type:l.SET_MAX_RECEIVER_VIDEO_QUALITY,maxReceiverVideoQuality:n}},e.setPassword=function(n,t,o){return function(c,u){switch(t){case n.join:var E=u()['features/base/conference'];E.passwordRequired===n&&(c({type:l.SET_PASSWORD,conference:n,method:t,password:o}),(E=u()['features/base/conference']).password!==o||E.passwordRequired||E.conference||t.call(n,o));break;case n.lock:var f=u()['features/base/conference'];return f.conference===n?t.call(n,o).then(function(){return c({type:l.SET_PASSWORD,conference:n,method:t,password:o})}).catch(function(n){return c({type:l.SET_PASSWORD_FAILED,error:n})}):Promise.reject()}}},e.setPreferredReceiverVideoQuality=function(n){return{type:l.SET_PREFERRED_RECEIVER_VIDEO_QUALITY,preferredReceiverVideoQuality:n}},e.setRoom=function(n){return{type:l.SET_ROOM,room:n}},e.setStartMutedPolicy=function(n,t){return function(o,c){var u=(0,N.getCurrentConference)(c());return u&&u.setStartMutedPolicy({audio:n,video:t}),o(J(n,t))}},e.setSubject=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:'';return function(t,o){var c=o()['features/base/conference'].conference;c?c.setSubject(n):t({type:l.SET_PENDING_SUBJECT_CHANGE,subject:n})}};var t=n(r(d[1])),o=r(d[2]),c=r(d[3]),u=r(d[4]),E=r(d[5]),f=r(d[6]),s=r(d[7]),C=r(d[8]),p=r(d[9]),_=r(d[10]),l=r(d[11]),A=r(d[12]),N=r(d[13]),v=n(r(d[14]));function T(n,t){n.on(f.JitsiConferenceEvents.CONFERENCE_FAILED,function(){for(var o=arguments.length,c=new Array(o),u=0;u<o;u++)c[u]=arguments[u];return t(S.apply(void 0,[n].concat(c)))}),n.on(f.JitsiConferenceEvents.CONFERENCE_JOINED,function(){for(var o=arguments.length,c=new Array(o),u=0;u<o;u++)c[u]=arguments[u];return t(R.apply(void 0,[n].concat(c)))}),n.on(f.JitsiConferenceEvents.CONFERENCE_LEFT,function(){for(var o=arguments.length,c=new Array(o),u=0;u<o;u++)c[u]=arguments[u];return t(D.apply(void 0,[n].concat(c)))}),n.on(f.JitsiConferenceEvents.SUBJECT_CHANGED,function(){return t(y.apply(void 0,arguments))}),n.on(f.JitsiConferenceEvents.KICKED,function(){for(var o=arguments.length,c=new Array(o),u=0;u<o;u++)c[u]=arguments[u];return t(I.apply(void 0,[n].concat(c)))}),n.on(f.JitsiConferenceEvents.PARTICIPANT_KICKED,function(n,o){return t((0,C.participantKicked)(n,o))}),n.on(f.JitsiConferenceEvents.LOCK_STATE_CHANGED,function(){for(var o=arguments.length,c=new Array(o),u=0;u<o;u++)c[u]=arguments[u];return t(h.apply(void 0,[n].concat(c)))}),n.on(f.JitsiConferenceEvents.STARTED_MUTED,function(){var c=Boolean(n.startAudioMuted),u=Boolean(n.startVideoMuted);(0,o.sendAnalytics)((0,o.createStartMutedConfigurationEvent)('remote',c,u)),v.default.log("Start muted: "+(c?'audio, ':'')+(u?'video':'')),t((0,s.setAudioMuted)(c)),t((0,s.setVideoMuted)(u))}),n.on(f.JitsiConferenceEvents.TRACK_ADDED,function(n){return n&&!n.isLocal()&&t((0,p.trackAdded)(n))}),n.on(f.JitsiConferenceEvents.TRACK_REMOVED,function(n){return n&&!n.isLocal()&&t((0,p.trackRemoved)(n))}),n.on(f.JitsiConferenceEvents.TRACK_MUTE_CHANGED,function(n,o){o&&t((0,C.participantMutedUs)(o))}),n.on(f.JitsiConferenceEvents.DISPLAY_NAME_CHANGED,function(o,c){return t((0,C.participantUpdated)({conference:n,id:o,name:(0,C.getNormalizedDisplayName)(c)}))}),n.on(f.JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,function(o){return t((0,C.dominantSpeakerChanged)(o,n))}),n.on(f.JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,function(){return t(u.endpointMessageReceived.apply(void 0,arguments))}),n.on(f.JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,function(){return t(C.participantConnectionStatusChanged.apply(void 0,arguments))}),n.on(f.JitsiConferenceEvents.USER_JOINED,function(o,c){return(0,N.commonUserJoinedHandling)({dispatch:t},n,c)}),n.on(f.JitsiConferenceEvents.USER_LEFT,function(o,c){return(0,N.commonUserLeftHandling)({dispatch:t},n,c)}),n.on(f.JitsiConferenceEvents.USER_ROLE_CHANGED,function(){return t(C.participantRoleChanged.apply(void 0,arguments))}),n.on(f.JitsiConferenceEvents.USER_STATUS_CHANGED,function(){return t(C.participantPresenceChanged.apply(void 0,arguments))}),n.on(f.JitsiConferenceEvents.BOT_TYPE_CHANGED,function(o,c){return t((0,C.participantUpdated)({conference:n,id:o,botType:c}))}),n.addCommandListener(A.AVATAR_ID_COMMAND,function(o,c){return t((0,C.participantUpdated)({conference:n,id:c,avatarID:o.value}))}),n.addCommandListener(A.AVATAR_URL_COMMAND,function(o,c){return t((0,C.participantUpdated)({conference:n,id:c,avatarURL:o.value}))}),n.addCommandListener(A.EMAIL_COMMAND,function(o,c){return t((0,C.participantUpdated)({conference:n,id:c,email:o.value}))})}function S(n,t){return{type:l.CONFERENCE_FAILED,conference:n,error:{name:t,recoverable:void 0}}}function R(n){return{type:l.CONFERENCE_JOINED,conference:n}}function D(n){return{type:l.CONFERENCE_LEFT,conference:n}}function y(n){return{type:l.CONFERENCE_SUBJECT_CHANGED,subject:n}}function O(n){return function(t,o){var c=(0,p.getLocalTracks)(o()['features/base/tracks']).map(function(n){return n.jitsiTrack});c.length&&(0,N._addLocalTracksToConference)(n,c),t(L(n))}}function L(n){return{type:l.CONFERENCE_WILL_JOIN,conference:n}}function I(n,t){return{type:l.KICKED_OUT,conference:n,participant:t}}function h(n,t){return{type:l.LOCK_STATE_CHANGED,conference:n,locked:t}}function J(n,t){return{type:l.SET_START_MUTED_POLICY,startAudioMutedPolicy:n,startVideoMutedPolicy:t}}},384,[3,54,385,381,1039,1051,388,744,532,793,801,1058,1059,1060,1061]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})}),r(d[2]),r(d[3])},385,[386,387,1017,1038]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.createApiEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:t,attributes:n,source:'jitsi-meet-api'}},e.createAudioOnlyChangedEvent=function(t){return{action:"audio.only."+(t?'enabled':'disabled')}},e.createConnectionEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:t,actionSubject:'connection',attributes:n}},e.createCalendarClickedEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:'clicked',actionSubject:t,attributes:n,source:'calendar',type:o}},e.createCalendarSelectedEvent=function(){return{action:'selected',actionSubject:'calendar.selected',attributes:arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},source:'calendar',type:o}},e.createCalendarConnectedEvent=function(){return{action:'calendar.connected',actionSubject:'calendar.connected',attributes:arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}}},e.createRecentClickedEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:'clicked',actionSubject:t,attributes:n,source:'recent.list',type:o}},e.createRecentSelectedEvent=function(){return{action:'selected',actionSubject:'recent.list.selected',attributes:arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},source:'recent.list',type:o}},e.createDeepLinkingPageEvent=function(t,n){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{action:t,actionSubject:n,source:'deepLinkingPage',attributes:c}},e.createDeviceChangedEvent=function(t,n){return{action:'device.changed',attributes:{device_type:n,media_type:t}}},e.createFeedbackOpenEvent=function(){return{action:'feedback.opened'}},e.createInviteDialogEvent=function(t,n){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{action:t,actionSubject:n,attributes:c,source:'inviteDialog'}},e.createNetworkInfoEvent=function(t){var n=t.isOnline,c=t.networkType,o=t.details,u={isOnline:n};return c&&(u.networkType=c),o&&(u.details=o),{action:'network.info',attributes:u}},e.createOfferAnswerFailedEvent=function(){return{action:'offer.answer.failure'}},e.createPageReloadScheduledEvent=function(t,c,o){return{action:'page.reload.scheduled',attributes:(0,n.default)({reason:t,timeout:c},o)}},e.createPinnedEvent=function(t,n,o){return{type:c,action:t,actionSubject:'participant',objectType:'participant',objectId:n,attributes:o}},e.createProfilePanelButtonEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:'clicked',actionSubject:t,attributes:n,source:'profile.panel',type:o}},e.createRecordingDialogEvent=function(t,n){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{action:'clicked',actionSubject:n,attributes:c,source:t+".recording.dialog",type:o}},e.createLiveStreamingDialogEvent=function(t,n){return{action:'clicked',actionSubject:n,source:t+".liveStreaming.dialog",type:o}},e.createLocalTracksDurationEvent=function(t){var n=t.audio,c=t.video,o=t.conference,u=c.camera,l=c.desktop;return{action:'local.tracks.durations',attributes:{audio:n.value,camera:u.value,conference:o.value,desktop:l.value}}},e.createRecordingEvent=function(t,n,c){return{action:t,actionSubject:"recording."+n,attributes:{value:c}}},e.createRejoinedEvent=function(t){var n=t.url,c=t.lastConferenceDuration,o=t.timeSinceLeft;return{action:'rejoined',attributes:{lastConferenceDuration:c,timeSinceLeft:o,url:n}}},e.createRemoteMuteConfirmedEvent=function(t){return{action:'clicked',actionSubject:'remote.mute.dialog.confirm.button',attributes:{participant_id:t},source:'remote.mute.dialog',type:o}},e.createRemoteVideoMenuButtonEvent=function(t,n){return{action:'clicked',actionSubject:t,attributes:n,source:'remote.video.menu',type:o}},e.createVideoBlurEvent=function(t){return{action:t,actionSubject:'video.blur'}},e.createScreenSharingEvent=function(t){return{action:t,actionSubject:'screen.sharing'}},e.createSelectParticipantFailedEvent=function(t){var n={action:'select.participant.failed'};t&&(n.error=t.toString());return n},e.createSharedVideoEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:t,attributes:n,actionSubject:'shared.video'}},e.createShortcutEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:u,c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{action:n,actionSubject:'keyboard.shortcut',actionSubjectId:t,attributes:c,source:'keyboard.shortcut',type:o}},e.createStartAudioOnlyEvent=function(t){return{action:'start.audio.only',attributes:{enabled:t}}},e.createStartSilentEvent=function(){return{action:'start.silent'}},e.createStartMutedConfigurationEvent=function(t,n,c){return{action:'start.muted.configuration',attributes:{source:t,audio_mute:n,video_mute:c}}},e.createStreamSwitchDelayEvent=function(t){return{action:'stream.switch.delay',attributes:t}},e.createSyncTrackStateEvent=function(t,n){return{action:'sync.track.state',attributes:{media_type:t,muted:n}}},e.createToolbarEvent=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{action:'clicked',actionSubject:t,attributes:n,source:'toolbar.button',type:o}},e.createTrackMutedEvent=function(t,n){var c=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return{action:'track.muted',attributes:{media_type:t,muted:c,reason:n}}},e.createWelcomePageEvent=function(t,n){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{action:t,actionSubject:n,attributes:c,source:'welcomePage'}},e.VIDEO_MUTE=e.AUDIO_MUTE=e.ACTION_SHORTCUT_TRIGGERED=e.ACTION_SHORTCUT_RELEASED=e.ACTION_SHORTCUT_PRESSED=e.ACTION_UNPINNED=e.ACTION_PINNED=void 0;var n=t(r(d[1])),c='track',o='ui';e.ACTION_PINNED='pinned';e.ACTION_UNPINNED='unpinned';e.ACTION_SHORTCUT_PRESSED='pressed';e.ACTION_SHORTCUT_RELEASED='released';var u='triggered';e.ACTION_SHORTCUT_TRIGGERED=u;e.AUDIO_MUTE='audio.mute';e.VIDEO_MUTE='video.mute'},386,[3,54]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.sendAnalytics=function(t){try{s.analytics.sendEvent(t)}catch(t){u.default.warn("Error sending analytics event: "+t)}},e.resetAnalytics=function(){s.analytics.reset()},e.initAnalytics=function(t){var n=t.getState;if((0,l.getJitsiMeetGlobalNS)().analyticsHandlers=[],window.analyticsHandlers=[],!s.analytics||!(0,s.isAnalyticsEnabled)(n))return;var o=n(),c=o['features/base/config'],y=o['features/base/connection'].locationURL,v=y?y.host:'',p=c.analytics,b=void 0===p?{}:p,h=c.deploymentInfo,A=b.amplitudeAPPKey,w=b.blackListedEvents,k=b.scriptURLs,S=b.googleAnalyticsTrackingId,P=b.whiteListedEvents,E=o['features/base/jwt'],L=E.group,H=E.server,T=E.user,_={amplitudeAPPKey:A,blackListedEvents:w,envType:h&&h.envType||'dev',googleAnalyticsTrackingId:S,group:L,host:v,product:h&&h.product,subproduct:h&&h.environment,user:T&&T.id,version:s.default.version,whiteListedEvents:P};f(k,_).then(function(t){var n=o['features/base/conference'].room,l={};if(H&&(l.server=H),L&&(l.group=L),h)for(var c in h)h.hasOwnProperty(c)&&(l[c]=h[c]);s.analytics.addPermanentProperties(l),s.analytics.setConferenceName(n),s.analytics.setAnalyticsHandlers(t)}).catch(function(t){s.analytics.dispose(),u.default.error(t)})};var o=n(r(d[2])),s=t(r(d[3])),l=r(d[4]),c=r(d[5]),u=n(r(d[6]));function f(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,s=[],f=function(t){s.push((0,l.loadScript)(t).then(function(){return{type:'success'}},function(n){return{type:'error',error:n,url:t}}))},y=t,v=Array.isArray(y),p=0;for(y=v?y:y["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var b;if(v){if(p>=y.length)break;b=y[p++]}else{if((p=y.next()).done)break;b=p.value}f(b)}return Promise.all(s).then(function(t){var s=t,f=Array.isArray(s),y=0;for(s=f?s:s["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var v;if(f){if(y>=s.length)break;v=s[y++]}else{if((y=s.next()).done)break;v=y.value}var p=v;'error'===p.type&&u.default.warn("Failed to load "+p.url+": "+p.error)}var b=[],h=(0,o.default)((0,l.getJitsiMeetGlobalNS)().analyticsHandlers).concat((0,o.default)(window.analyticsHandlers),[c.AmplitudeHandler]),A=Array.isArray(h),w=0;for(h=A?h:h["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var k;if(A){if(w>=h.length)break;k=h[w++]}else{if((w=h.next()).done)break;k=w.value}var S=k;try{b.push(new S(n))}catch(t){u.default.warn("Error creating analytics handler: "+t)}}return u.default.debug("Loaded "+b.length+" analytics handlers"),b})}},387,[2,3,32,388,801,1011,1016]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var n={analytics:!0,browser:!0,JitsiConferenceErrors:!0,JitsiConferenceEvents:!0,JitsiConnectionErrors:!0,JitsiConnectionEvents:!0,JitsiConnectionQualityEvents:!0,JitsiE2ePingEvents:!0,JitsiMediaDevicesEvents:!0,JitsiParticipantConnectionStatus:!0,JitsiRecordingConstants:!0,JitsiSIPVideoGWStatus:!0,JitsiTrackErrors:!0,JitsiTrackEvents:!0};Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s.default}}),e.JitsiTrackEvents=e.JitsiTrackErrors=e.JitsiSIPVideoGWStatus=e.JitsiRecordingConstants=e.JitsiParticipantConnectionStatus=e.JitsiMediaDevicesEvents=e.JitsiE2ePingEvents=e.JitsiConnectionQualityEvents=e.JitsiConnectionEvents=e.JitsiConnectionErrors=e.JitsiConferenceEvents=e.JitsiConferenceErrors=e.browser=e.analytics=void 0;var s=t(r(d[1])),o=r(d[2]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}}))});var c=r(d[3]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}}))});var u=r(d[4]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}}))}),r(d[5]),r(d[6]);var v=s.default.analytics;e.analytics=v;var f=s.default.util.browser;e.browser=f;var l=s.default.errors.conference;e.JitsiConferenceErrors=l;var J=s.default.events.conference;e.JitsiConferenceEvents=J;var E=s.default.errors.connection;e.JitsiConnectionErrors=E;var y=s.default.events.connection;e.JitsiConnectionEvents=y;var C=s.default.events.connectionQuality;e.JitsiConnectionQualityEvents=C;var p=s.default.events.e2eping;e.JitsiE2ePingEvents=p;var b=s.default.events.mediaDevices;e.JitsiMediaDevicesEvents=b;var P=s.default.constants.participantConnectionStatus;e.JitsiParticipantConnectionStatus=P;var O=s.default.constants.recording;e.JitsiRecordingConstants=O;var j=s.default.constants.sipVideoGW;e.JitsiSIPVideoGWStatus=j;var k=s.default.errors.track;e.JitsiTrackErrors=k;var S=s.default.events.track;e.JitsiTrackEvents=S},388,[3,389,992,993,994,995,1010]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return n.default}}),r(d[1]);var n=t(r(d[2]));!(function(t){if(void 0===t.$){var n=r(d[3]);n(t),t.$=n}})(g||window||this)},389,[3,390,990,991]); -__d(function(g,r,i,a,m,e,d){r(d[0]),r(d[1])},390,[391,989]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1]));r(d[2]);var n=r(d[3]);function o(t,n){return t===n?t:(l=Object.getPrototypeOf(t))&&(l=o(n,l))||(l=Object.getPrototypeOf(n))&&(l=o(t,l))?l:void 0;var l}function l(t,n){var o=null;return t&&f(t,function(t){return 1===t.nodeType&&t.nodeName===n&&(o=t,!0)}),o}function f(t,n){if(n(t))return!0;if(t=t.firstChild)do{if(f(t,n))return!0}while(t=t.nextSibling);return!1}r(d[4]),(function(f){var u=r(d[5]).DOMParser;if(f.DOMParser=u,void 0===f.addEventListener&&(f.addEventListener=function(){}),void 0===f.removeEventListener&&(f.removeEventListener=function(){}),void 0===f.document){var c=(new u).parseFromString('<html><head></head><body></body></html>','text/xml');void 0===c.addEventListener&&(c.addEventListener=function(){}),void 0===c.cookie&&(c.cookie=''),void 0===c.implementation.createHTMLDocument&&(c.implementation.createHTMLDocument=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:'',n=(new u).parseFromString("<html>\n <head><title>"+t+"\n \n ",'text/xml');return Object.defineProperty(n,'body',{get:function(){return n.getElementsByTagName('body')[0]}}),n});var s=Object.getPrototypeOf(c.documentElement);s&&(void 0===s.querySelector&&(s.querySelector=function(t){return l(this,t)}),void 0===s.remove&&(s.remove=function(){null!==this.parentNode&&this.parentNode.removeChild(this)}),s.hasOwnProperty('innerHTML')||Object.defineProperty(s,'innerHTML',{get:function(){return this.childNodes.toString()},set:function(t){this.textContent='';for(var n,o=(new u).parseFromString("

"+t+"
",'text/xml').documentElement;n=o.firstChild;)this.appendChild(n)}}),s.hasOwnProperty('children')||Object.defineProperty(s,'children',{get:function(){for(var t=this.childNodes,n=[],o=0,l=t[o];l;)1===l.nodeType&&n.push(l),l=t[o+=1];return n}}));var v=o(Object.getPrototypeOf(c),s);if(v&&v!==Object.getPrototypeOf({})){var h=f.console;if(h){var p=r(d[6]).levels;Object.keys(p).forEach(function(t){var n=p[t],o=h[n];'function'==typeof o&&(h[n]=function(){for(var t=arguments.length,l=new Array(t),f=0;f1&&void 0!==arguments[1]?arguments[1]:0;return t.default.setTimeout(n,o)}})(g||window||this)},391,[3,392,393,394,821,978,577,981,982,985,988]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),u=t(r(d[2])),c=r(d[3]),l=c.NativeModules.RNBackgroundTimer,o=new c.NativeEventEmitter(l),s=new((function(){function t(){var u=this;(0,n.default)(this,t),this.uniqueId=0,this.callbacks={},o.addListener('backgroundTimer.timeout',function(t){if(u.callbacks[t]){var n=u.callbacks[t].callback;u.callbacks[t].interval?l.setTimeout(t,u.callbacks[t].timeout):delete u.callbacks[t],n()}})}return(0,u.default)(t,[{key:"start",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;return l.start(t)}},{key:"stop",value:function(){return l.stop()}},{key:"runBackgroundTimer",value:function(t,n){var u=this,l=c.Platform.select({ios:function(){return c.NativeAppEventEmitter},android:function(){return c.DeviceEventEmitter}})();this.start(0),this.backgroundListener=l.addListener('backgroundTimer',function(){u.backgroundListener.remove(),u.backgroundClockMethod(t,n)})}},{key:"backgroundClockMethod",value:function(t,n){var u=this;this.backgroundTimer=this.setTimeout(function(){t(),u.backgroundClockMethod(t,n)},n)}},{key:"stopBackgroundTimer",value:function(){this.stop(),this.clearTimeout(this.backgroundTimer)}},{key:"setTimeout",value:function(t,n){this.uniqueId+=1;var u=this.uniqueId;return this.callbacks[u]={callback:t,interval:!1,timeout:n},l.setTimeout(u,n),u}},{key:"clearTimeout",value:function(t){this.callbacks[t]&&delete this.callbacks[t]}},{key:"setInterval",value:function(t,n){this.uniqueId+=1;var u=this.uniqueId;return this.callbacks[u]={callback:t,interval:!0,timeout:n},l.setTimeout(u,n),u}},{key:"clearInterval",value:function(t){this.callbacks[t]&&delete this.callbacks[t]}}]),t})());e.default=s},392,[3,4,5,17]); -__d(function(g,r,i,a,m,e,d){!(function(t){'use strict';var s=!1;if(!t.forceJURL)try{var h=new URL('b','http://a');h.pathname='c%20d',s='http://a/c%20d'===h.href}catch(t){}if(!s){var n=Object.create(null);n.ftp=21,n.file=0,n.gopher=70,n.http=80,n.https=443,n.ws=80,n.wss=443;var o=Object.create(null);o['%2e']='.',o['.%2e']='..',o['%2e.']='..',o['%2e%2e']='..';var l=void 0,_=/[a-zA-Z]/,c=/[a-zA-Z0-9\+\-\.]/;I.prototype={toString:function(){return this.href},get href(){if(this._isInvalid)return this._url;var t='';return''==this._username&&null==this._password||(t=this._username+(null!=this._password?':'+this._password:'')+'@'),this.protocol+(this._isRelative?'//'+t+this.host:'')+this.pathname+this._query+this._fragment},set href(t){k.call(this),b.call(this,t)},get protocol(){return this._scheme+':'},set protocol(t){this._isInvalid||b.call(this,t+':','scheme start')},get host(){return this._isInvalid?'':this._port?this._host+':'+this._port:this._host},set host(t){!this._isInvalid&&this._isRelative&&b.call(this,t,'host')},get hostname(){return this._host},set hostname(t){!this._isInvalid&&this._isRelative&&b.call(this,t,'hostname')},get port(){return this._port},set port(t){!this._isInvalid&&this._isRelative&&b.call(this,t,'port')},get pathname(){return this._isInvalid?'':this._isRelative?'/'+this._path.join('/'):this._schemeData},set pathname(t){!this._isInvalid&&this._isRelative&&(this._path=[],b.call(this,t,'relative path start'))},get search(){return this._isInvalid||!this._query||'?'==this._query?'':this._query},set search(t){!this._isInvalid&&this._isRelative&&(this._query='?','?'==t[0]&&(t=t.slice(1)),b.call(this,t,'query'))},get hash(){return this._isInvalid||!this._fragment||'#'==this._fragment?'':this._fragment},set hash(t){this._isInvalid||(this._fragment='#','#'==t[0]&&(t=t.slice(1)),b.call(this,t,'fragment'))},get origin(){var t;if(this._isInvalid||!this._scheme)return'';switch(this._scheme){case'data':case'file':case'javascript':case'mailto':return'null'}return(t=this.host)?this._scheme+'://'+t:''}};var p=t.URL;p&&(I.createObjectURL=function(t){return p.createObjectURL.apply(p,arguments)},I.revokeObjectURL=function(t){p.revokeObjectURL(t)}),t.URL=I}function u(t){return void 0!==n[t]}function f(){k.call(this),this._isInvalid=!0}function v(t){return''==t&&f.call(this),t.toLowerCase()}function y(t){var s=t.charCodeAt(0);return s>32&&s<127&&-1==[34,35,60,62,63,96].indexOf(s)?t:encodeURIComponent(t)}function w(t){var s=t.charCodeAt(0);return s>32&&s<127&&-1==[34,35,60,62,96].indexOf(s)?t:encodeURIComponent(t)}function b(t,s,h){function p(t){L.push(t)}var b=s||'scheme start',k=0,I='',R=!1,q=!1,L=[];t:for(;(t[k-1]!=l||0==k)&&!this._isInvalid;){var U=t[k];switch(b){case'scheme start':if(!U||!_.test(U)){if(s){p('Invalid scheme.');break t}I='',b='no scheme';continue}I+=U.toLowerCase(),b='scheme';break;case'scheme':if(U&&c.test(U))I+=U.toLowerCase();else{if(':'!=U){if(s){if(l==U)break t;p('Code point not allowed in scheme: '+U);break t}I='',k=0,b='no scheme';continue}if(this._scheme=I,I='',s)break t;u(this._scheme)&&(this._isRelative=!0),b='file'==this._scheme?'relative':this._isRelative&&h&&h._scheme==this._scheme?'relative or authority':this._isRelative?'authority first slash':'scheme data'}break;case'scheme data':'?'==U?(this._query='?',b='query'):'#'==U?(this._fragment='#',b='fragment'):l!=U&&'\t'!=U&&'\n'!=U&&'\r'!=U&&(this._schemeData+=y(U));break;case'no scheme':if(h&&u(h._scheme)){b='relative';continue}p('Missing scheme.'),f.call(this);break;case'relative or authority':if('/'!=U||'/'!=t[k+1]){p('Expected /, got: '+U),b='relative';continue}b='authority ignore slashes';break;case'relative':if(this._isRelative=!0,'file'!=this._scheme&&(this._scheme=h._scheme),l==U){this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query=h._query,this._username=h._username,this._password=h._password;break t}if('/'==U||'\\'==U)'\\'==U&&p('\\ is an invalid code point.'),b='relative slash';else if('?'==U)this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query='?',this._username=h._username,this._password=h._password,b='query';else{if('#'!=U){var C=t[k+1],j=t[k+2];('file'!=this._scheme||!_.test(U)||':'!=C&&'|'!=C||l!=j&&'/'!=j&&'\\'!=j&&'?'!=j&&'#'!=j)&&(this._host=h._host,this._port=h._port,this._username=h._username,this._password=h._password,this._path=h._path.slice(),this._path.pop()),b='relative path';continue}this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query=h._query,this._fragment='#',this._username=h._username,this._password=h._password,b='fragment'}break;case'relative slash':if('/'!=U&&'\\'!=U){'file'!=this._scheme&&(this._host=h._host,this._port=h._port,this._username=h._username,this._password=h._password),b='relative path';continue}'\\'==U&&p('\\ is an invalid code point.'),b='file'==this._scheme?'file host':'authority ignore slashes';break;case'authority first slash':if('/'!=U){p("Expected '/', got: "+U),b='authority ignore slashes';continue}b='authority second slash';break;case'authority second slash':if(b='authority ignore slashes','/'!=U){p("Expected '/', got: "+U);continue}break;case'authority ignore slashes':if('/'!=U&&'\\'!=U){b='authority';continue}p('Expected authority, got: '+U);break;case'authority':if('@'==U){R&&(p('@ already seen.'),I+='%40'),R=!0;for(var O=0;O=f.length)break;l=f[p++]}else{if((p=f.next()).done)break;l=p.value}var c=l;(0,o.default)(u,t(c))}return u}return n},e.combineStyles=function(t,n){var o=[];t&&(Array.isArray(t)?o.push.apply(o,(0,u.default)(t)):o.push(t));n&&(Array.isArray(n)?o.push.apply(o,(0,u.default)(n)):o.push(n));return o},e.createStyleSheet=function(t){for(var u=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o={},f=Object.keys(t),s=0;s>>0===n&&n>=0&&n<=4294967295)return[0,(0,t.integerColor)(n)];if(!n||'none'===n)return null;if('currentColor'===n)return o;var l='string'==typeof n&&n.match(u);if(l)return[1,l[1]];var f=(0,t.default)(n);if('number'==typeof f)return[0,f];return console.warn("\""+n+"\" is not a valid color or brush"),null};var t=n(r(d[1])),u=/^url\(#(.+)\)$/,o=[2]},424,[2,425]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(l){if('number'==typeof l)return l>>>0===l&&l>=0&&l<=4294967295?O(l):null;var n='string'==typeof l?I(l):l;if(!Array.isArray(n))return n;var t=n[0],o=n[1],s=n[2],u=n[3],h=((void 0===u?4278190080:Math.round(255*u)<<24)|Math.round(255*t)<<16|Math.round(255*o)<<8|Math.round(255*s))>>>0;return O(h)},e.integerColor=e.colorNames=void 0;var l=r(d[0]),n={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};for(var t in e.colorNames=n,n)if(n.hasOwnProperty(t)){var o=n[t],s=o[0],u=o[1],h=o[2];n[t]=(4278190080|s<<16|u<<8|h)>>>0}function c(l,n,t,o){var s,u,h,c,f,b=l/360,p=n/100,y=t/100;if(0===p)return[f=y,f,f,o];s=2*y-(u=y<.5?y*(1+p):y+p-y*p),c=[0,0,0,o];for(var k=0;k<3;k++)(h=b+.3333333333333333*-(k-1))<0&&h++,h>1&&h--,f=6*h<1?s+6*(u-s)*h:2*h<1?u:3*h<2?s+(u-s)*(.6666666666666666-h)*6:s,c[k]=f;return c}function f(l,n,t,o){var s,u,h,c,f,b,p,y=l/360,k=n/100,v=t/100,w=k+v;switch(w>1&&(k/=w,v/=w),u=1-v,h=6*y-(s=Math.floor(6*y)),0!=(1&s)&&(h=1-h),c=k+h*(u-k),s){default:case 6:case 0:f=u,b=c,p=k;break;case 1:f=c,b=u,p=k;break;case 2:f=k,b=u,p=c;break;case 3:f=k,b=c,p=u;break;case 4:f=c,b=k,p=u;break;case 5:f=u,b=k,p=c}return[f,b,p,o]}function b(l,n,t){return Math.min(Math.max(n,l),t)}Object.freeze(n);var p=/^#([a-f0-9]{3,4})$/i,y=/^#([a-f0-9]{6})([a-f0-9]{2})?$/i,k=/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/,v=/^rgba?\(\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/,w=/(\D+)/;function F(l){var t,o,s,u=[0,0,0,1];if(t=l.match(y)){for(s=t[2],t=t[1],o=0;o<3;o++){var h=2*o;u[o]=parseInt(t.slice(h,h+2),16)/255}s&&(u[3]=Math.round(parseInt(s,16)/255*100)/100)}else if(t=l.match(p)){for(s=(t=t[1])[3],o=0;o<3;o++)u[o]=parseInt(t[o]+t[o],16)/255;s&&(u[3]=Math.round(parseInt(s+s,16)/255*100)/100)}else if(t=l.match(k)){for(o=0;o<3;o++)u[o]=parseInt(t[o+1],0)/255;t[4]&&(u[3]=parseFloat(t[4]))}else{if(!(t=l.match(v)))return(t=l.match(w))?'transparent'===t[1]?[0,0,0,0]:'number'!=typeof(u=n[t[1]])?null:O(u):null;for(o=0;o<3;o++)u[o]=parseFloat(t[o+1])/100;t[4]&&(u[3]=parseFloat(t[4]))}for(o=0;o<4;o++)u[o]=b(u[o],0,1);return u}var M=/^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;function q(l){var n=l.match(M);if(!n)return null;var t=parseFloat(n[4]);return c((parseFloat(n[1])+360)%360,b(parseFloat(n[2]),0,100),b(parseFloat(n[3]),0,100),isNaN(t)?1:b(t,0,1))}var N=/^hwb\(\s*([+-]?\d*[.]?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;function $(l){var n=l.match(N);if(!n)return null;var t=parseFloat(n[4]);return f((parseFloat(n[1])%360+360)%360,b(parseFloat(n[2]),0,100),b(parseFloat(n[3]),0,100),isNaN(t)?1:b(t,0,1))}function I(l){switch(l.substring(0,3).toLowerCase()){case'hsl':return q(l);case'hwb':return $(l);default:return F(l)}}var O='android'===l.Platform.OS?function(l){return 0|l}:function(l){return l};e.integerColor=O},425,[17]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(n){var t=+n;return'number'!=typeof t||isNaN(t)?1:t}},426,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t,h){var f=t.stroke,c=t.strokeOpacity,p=t.strokeLinecap,v=t.strokeLinejoin,y=t.strokeDasharray,D=t.strokeWidth,L=t.strokeDashoffset,j=t.strokeMiterlimit,M=t.vectorEffect;null!=f&&h.push('stroke');null!=D&&h.push('strokeWidth');null!=c&&h.push('strokeOpacity');null!=y&&h.push('strokeDasharray');null!=L&&h.push('strokeDashoffset');null!=p&&h.push('strokeLinecap');null!=v&&h.push('strokeLinejoin');null!=j&&h.push('strokeMiterlimit');var O=y&&'none'!==y?(0,n.default)(y):null;return{stroke:(0,o.default)(f),strokeOpacity:(0,s.default)(c),strokeLinecap:l[p]||0,strokeLinejoin:u[v]||0,strokeDasharray:O&&O.length%2==1?O.concat(O):O,strokeWidth:null!=D?D:1,strokeDashoffset:y?+L||0:null,strokeMiterlimit:parseFloat(j)||4,vectorEffect:k[M]||0}};var o=t(r(d[1])),s=t(r(d[2])),n=t(r(d[3])),l={butt:0,square:2,round:1},u={miter:0,bevel:2,round:1},k={none:0,default:0,nonScalingStroke:1,'non-scaling-stroke':1,inherit:2,uri:3}},427,[3,424,426,428]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(u){return Array.isArray(u)?u:'number'==typeof u?[u]:'string'==typeof u?u.trim().replace(n,' ').split(t):[]};var t=/\s+/,n=/,/g},428,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.props2transform=f,e.transformToMatrix=l,e.default=function(s){if(Array.isArray(s))return s;if('string'==typeof s)try{var o=(0,n.parse)(s);return[o[0],o[3],o[1],o[4],o[2],o[5]]}catch(n){return console.error(n),t.identity}return l(f(s),s.transform)};var t=r(d[0]),n=r(d[1]);function s(n){var s=n.x,o=n.y,f=n.originX,l=n.originY,c=n.scaleX,y=n.scaleY,u=n.rotation,p=n.skewX,X=n.skewY;(0,t.appendTransform)(s+f,o+l,c,y,u,p,X,f,l)}function o(t,n,s,o){var f,l;if('number'==typeof t)f=l=t;else if('string'==typeof t){var c=t.split(/\s*,\s*/);2===c.length?(f=+c[0],l=+c[1]):1===c.length&&(f=l=+c[0])}else Array.isArray(t)&&(2===t.length?(f=+t[0],l=+t[1]):1===t.length&&(f=l=+t[0]));return n=+n,isNaN(n)||(f=n),s=+s,isNaN(s)||(l=s),[f||o||0,l||o||0]}function f(t){var n=t.translate,s=t.translateX,f=t.translateY,l=t.origin,c=t.originX,y=t.originY,u=t.scale,p=t.scaleX,X=t.scaleY,Y=t.skew,v=t.skewX,k=t.skewY,w=t.rotation,A=t.x,h=t.y,x=o(n,s||A,f||h),N=o(l,c,y),_=o(u,p,X,1),b=o(Y,v,k);return{rotation:+w||0,originX:N[0],originY:N[1],scaleX:_[0],scaleY:_[1],skewX:b[0],skewY:b[1],x:x[0],y:x[1]}}function l(o,l){if((0,t.reset)(),s(o),l)if(Array.isArray(l))'number'==typeof l[0]&&(0,t.append)(l[0],l[1],l[2],l[3],l[4],l[5]);else if('string'==typeof l)try{var c=(0,n.parse)(l);(0,t.append)(c[0],c[3],c[1],c[4],c[2],c[5])}catch(t){console.error(t)}else s(f(l));return(0,t.toArray)()}},429,[430,431]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.reset=function(){if(M)return;f=v=1,o=u=c=s=0,M=!0},e.toArray=function(){if(M)return n;return[f,o,u,v,c,s]},e.append=h,e.appendTransform=function(n,p,l,y,_,P,b,j,A){if(0===n&&0===p&&1===l&&1===y&&0===_&&0===P&&0===b&&0===j&&0===A)return;var I,O;if(_%360){var T=_*t;I=Math.cos(T),O=Math.sin(T)}else I=1,O=0;var k=I*l,q=O*l,w=-O*y,x=I*y;if(P||b){var z=Math.tan(b*t),B=Math.tan(P*t);h(k+B*q,z*k+q,w+B*x,z*w+x,n,p)}else h(k,q,w,x,n,p);(j||A)&&(c-=j*f+A*u,s-=j*o+A*v,M=!1)},e.identity=void 0;var t=Math.PI/180,n=[1,0,0,1,0,0];e.identity=n;var f=1,o=0,u=0,v=1,c=0,s=0,M=!0;function h(t,n,h,p,l,y){var _=1!==t||0!==n||0!==h||1!==p,P=0!==l||0!==y;if(_||P){if(M)return M=!1,f=t,o=n,u=h,v=p,c=l,void(s=y);var b=f,j=o,A=u,I=v;_&&(f=b*t+A*n,o=j*t+I*n,u=b*h+A*p,v=j*h+I*p),P&&(c=b*l+A*y+c,s=j*l+I*y+s)}}},430,[]); -__d(function(g,r,i,a,m,e,d){"use strict";function n(t,u,o,f){this.message=t,this.expected=u,this.found=o,this.location=f,this.name="SyntaxError","function"==typeof Error.captureStackTrace&&Error.captureStackTrace(this,n)}!(function(n,t){function u(){this.constructor=n}u.prototype=t.prototype,n.prototype=new u})(n,Error),n.buildMessage=function(n,t){var u={literal:function(n){return"\""+f(n.text)+"\""},class:function(n){var t,u="";for(t=0;t0){for(t=1,o=1;tse&&(se=ie,le=[]),le.push(n))}function ve(){var n,t,u,o,s;for(n=ie,t=[],u=Pe();u!==f;)t.push(u),u=Pe();if(t!==f)if((u=Ae())===f&&(u=null),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();o!==f?n=t=c(u):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;return n}function Ae(){var n,t,u,o;if(n=ie,(t=xe())!==f){for(u=[],o=ke();o!==f;)u.push(o),o=ke();u!==f&&(o=Ae())!==f?n=t=h(t,o):(ie=n,n=f)}else ie=n,n=f;return n===f&&(n=xe()),n}function xe(){var n;return(n=Ce())===f&&(n=me())===f&&(n=ye())===f&&(n=je())===f&&(n=we())===f&&(n=be()),n}function Ce(){var n,u,o,s,l,c,h,w,b,E,F,M,k;if(n=ie,t.substr(ie,6)===p?(u=p,ie+=6):(u=f,0===ae&&de(v)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f)if(ke()!==f)if((h=Ee())!==f)if(ke()!==f)if((w=Ee())!==f)if(ke()!==f)if((b=Ee())!==f)if(ke()!==f)if((E=Ee())!==f)if(ke()!==f)if((F=Ee())!==f){for(M=[],k=Pe();k!==f;)M.push(k),k=Pe();M!==f?(41===t.charCodeAt(ie)?(k=C,ie++):(k=f,0===ae&&de(y)),k!==f?n=u=j(c,h,w,b,E,F):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function me(){var n,u,o,s,l,c,h,p,v;if(n=ie,t.substr(ie,9)===w?(u=w,ie+=9):(u=f,0===ae&&de(b)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f)if((h=Fe())===f&&(h=null),h!==f){for(p=[],v=Pe();v!==f;)p.push(v),v=Pe();p!==f?(41===t.charCodeAt(ie)?(v=C,ie++):(v=f,0===ae&&de(y)),v!==f?n=u=E(c,h):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function ye(){var n,u,o,s,l,c,h,p,v;if(n=ie,t.substr(ie,5)===F?(u=F,ie+=5):(u=f,0===ae&&de(M)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f)if((h=Fe())===f&&(h=null),h!==f){for(p=[],v=Pe();v!==f;)p.push(v),v=Pe();p!==f?(41===t.charCodeAt(ie)?(v=C,ie++):(v=f,0===ae&&de(y)),v!==f?n=u=k(c,h):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function je(){var n,u,o,s,l,c,h,p,v;if(n=ie,t.substr(ie,6)===S?(u=S,ie+=6):(u=f,0===ae&&de(R)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f)if((h=Me())===f&&(h=null),h!==f){for(p=[],v=Pe();v!==f;)p.push(v),v=Pe();p!==f?(41===t.charCodeAt(ie)?(v=C,ie++):(v=f,0===ae&&de(y)),v!==f?n=u=I(c,h):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function we(){var n,u,o,s,l,c,h,p;if(n=ie,t.substr(ie,5)===T?(u=T,ie+=5):(u=f,0===ae&&de(X)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f){for(h=[],p=Pe();p!==f;)h.push(p),p=Pe();h!==f?(41===t.charCodeAt(ie)?(p=C,ie++):(p=f,0===ae&&de(y)),p!==f?n=u=Y(c):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function be(){var n,u,o,s,l,c,h,p;if(n=ie,t.substr(ie,5)===_?(u=_,ie+=5):(u=f,0===ae&&de(L)),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();if(o!==f)if(40===t.charCodeAt(ie)?(s=A,ie++):(s=f,0===ae&&de(x)),s!==f){for(l=[],c=Pe();c!==f;)l.push(c),c=Pe();if(l!==f)if((c=Ee())!==f){for(h=[],p=Pe();p!==f;)h.push(p),p=Pe();h!==f?(41===t.charCodeAt(ie)?(p=C,ie++):(p=f,0===ae&&de(y)),p!==f?n=u=P(c):(ie=n,n=f)):(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;else ie=n,n=f}else ie=n,n=f;return n}function Ee(){var n,t,u,o;return n=ie,t=ie,(u=Ye())===f&&(u=null),u!==f&&(o=Ie())!==f?t=u=[u,o]:(ie=t,t=f),t!==f&&(t=U(t)),(n=t)===f&&(n=ie,t=ie,(u=Ye())===f&&(u=null),u!==f&&(o=Re())!==f?t=u=[u,o]:(ie=t,t=f),t!==f&&(t=q(t)),n=t),n}function Fe(){var n,t;return n=ie,ke()!==f&&(t=Ee())!==f?n=z(t):(ie=n,n=f),n}function Me(){var n,t,u;return n=ie,ke()!==f&&(t=Ee())!==f&&ke()!==f&&(u=Ee())!==f?n=B(t,u):(ie=n,n=f),n}function ke(){var n,t,u,o,s;if(n=ie,t=[],(u=Pe())!==f)for(;u!==f;)t.push(u),u=Pe();else t=f;if(t!==f)if((u=Se())===f&&(u=null),u!==f){for(o=[],s=Pe();s!==f;)o.push(s),s=Pe();o!==f?n=t=[t,u,o]:(ie=n,n=f)}else ie=n,n=f;else ie=n,n=f;if(n===f)if(n=ie,(t=Se())!==f){for(u=[],o=Pe();o!==f;)u.push(o),o=Pe();u!==f?n=t=[t,u]:(ie=n,n=f)}else ie=n,n=f;return n}function Se(){var n;return 44===t.charCodeAt(ie)?(n=D,ie++):(n=f,0===ae&&de(G)),n}function Re(){var n;return ie,(n=_e())!==f&&(n=H(n)),n}function Ie(){var n,t,u,o;return n=ie,t=ie,(u=Te())!==f?((o=Xe())===f&&(o=null),o!==f?t=u=[u,o]:(ie=t,t=f)):(ie=t,t=f),t!==f&&(t=J(t)),(n=t)===f&&(n=ie,t=ie,(u=_e())!==f&&(o=Xe())!==f?t=u=[u,o]:(ie=t,t=f),t!==f&&(t=K(t)),n=t),n}function Te(){var n,u,o,s;return ae++,n=ie,(u=_e())===f&&(u=null),u!==f?(46===t.charCodeAt(ie)?(o=O,ie++):(o=f,0===ae&&de(Q)),o!==f&&(s=_e())!==f?n=u=V(u,s):(ie=n,n=f)):(ie=n,n=f),n===f&&(n=ie,(u=_e())!==f?(46===t.charCodeAt(ie)?(o=O,ie++):(o=f,0===ae&&de(Q)),o!==f?n=u=K(u):(ie=n,n=f)):(ie=n,n=f)),ae--,n===f&&(u=f,0===ae&&de(N)),n}function Xe(){var n,u,o,s;return ie,n=ie,W.test(t.charAt(ie))?(u=t.charAt(ie),ie++):(u=f,0===ae&&de(Z)),u!==f?((o=Ye())===f&&(o=null),o!==f&&(s=_e())!==f?n=u=[u,o,s]:(ie=n,n=f)):(ie=n,n=f),n!==f&&(n=$(n)),n}function Ye(){var n;return ee.test(t.charAt(ie))?(n=t.charAt(ie),ie++):(n=f,0===ae&&de(re)),n}function _e(){var n,t;if(n=[],(t=Le())!==f)for(;t!==f;)n.push(t),t=Le();else n=f;return n}function Le(){var n;return ne.test(t.charAt(ie))?(n=t.charAt(ie),ie++):(n=f,0===ae&&de(te)),n}function Pe(){var n;return ue.test(t.charAt(ie))?(n=t.charAt(ie),ie++):(n=f,0===ae&&de(oe)),n}var Ue,qe,ze,Be=Math.PI/180;if((o=l())!==f&&ie===t.length)return o;throw o!==f&&ie1||Array.isArray(y)?f.Children.map(y,A):y;return{content:null===V?String(y):null,children:V,inlineSize:h,baselineShift:x,verticalAlign:v,alignmentBaseline:F,font:b(t),x:(0,u.default)(o),y:(0,u.default)(c),dx:(0,u.default)(s),dy:(0,u.default)(S),rotate:(0,u.default)(p)}};var l,o=n(r(d[2])),f=t(r(d[3])),u=n(r(d[4])),c=r(d[5]),s=/^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?(?:%|px|em|pt|pc|mm|cm|in]))*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i,S=/^[\s"']*/,p=/[\s"']*$/,y=/\s*,\s*/g,h={};function x(t){return t?t.split(y)[0].replace(S,'').replace(p,''):null}function v(t){if(h.hasOwnProperty(t))return h[t];var n=s.exec(t);if(!n)return h[t]=null,null;var l=/bold/.exec(n[1]),o=/italic/.exec(n[1]);return h[t]={fontSize:n[2]||12,fontWeight:l?'bold':'normal',fontStyle:o?'italic':'normal',fontFamily:x(n[3])},h[t]}function b(t){var n=t.fontData,l=t.fontStyle,f=t.fontVariant,u=t.fontWeight,s=t.fontStretch,S=t.fontSize,p=t.fontFamily,y=t.textAnchor,h=t.textDecoration,b=t.letterSpacing,A=t.wordSpacing,F=t.kerning,V=t.fontFeatureSettings,z=t.fontVariantLigatures,D=t.fontVariationSettings,_=t.font,k=(0,c.pickNotNil)({fontData:n,fontStyle:l,fontVariant:f,fontWeight:u,fontStretch:s,fontSize:S,fontFamily:x(p),textAnchor:y,textDecoration:h,letterSpacing:b,wordSpacing:A,kerning:F,fontFeatureSettings:V,fontVariantLigatures:z,fontVariationSettings:D}),w='string'==typeof _?v(_):_;return(0,o.default)({},w,k)}function A(t){return'string'==typeof t||'number'==typeof t?f.default.createElement(l,null,String(t)):t}},447,[2,3,54,13,428,433]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),l=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=l(r(d[2])),f=l(r(d[3])),n=l(r(d[4])),o=l(r(d[5])),s=l(r(d[6])),v=l(r(d[7])),p=l(r(d[8])),c=l(r(d[9])),h=r(d[10]),y=l(r(d[11])),N=t(r(d[12])),x=l(r(d[13])),_=r(d[14]),A=l(r(d[15]));r(d[16]);var P=(function(t){function l(){var t,u;(0,n.default)(this,l);for(var o=arguments.length,p=new Array(o),c=0;c=c)){var s=t.slice(0,n).replace(/^\t+/,W),p=/(^|\n).*$/.exec(s)[0],v=t.slice(n);return{line:f,column:o,snippet:""+p+/.*(\n|$)/.exec(v)[0]+"\n"+Q(' ',p.length)+"^"}}o-=c}}var ee=/[a-zA-Z0-9:_-]/,te=/[\s\t\r\n]/,re=/['"]/;function ne(t){var n=t.length,u=null,l=function(){for(;U+1'!==t[U]&&s('Expected >'),D||(u=b,o=b.children,c.push(b)),p}function h(){var n=t.indexOf('--\x3e',U);return~n||s('expected --\x3e'),U=n+2,p}function x(){var n=t.indexOf(']]>',U);return~n||s('expected ]]>'),U=n+2,p}function y(){var n=S();(n||s('Expected tag name'),n!==u.tag&&s("Expected closing tag to match opening tag <"+u.tag+">"),'>'!==t[U]&&s('Expected >'),c.pop(),u=c[c.length-1])&&(o=u.children);return p}function S(){for(var u,l='';U'===l||'/'===l)return u;u+=l,U+=1}while(U0&&void 0!==arguments[0]?arguments[0]:65;return{alignItems:'center',borderRadius:t/2,height:t,justifyContent:'center',overflow:'hidden',width:t}},avatarContent:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:65;return{height:t,width:t}},initialsContainer:{alignItems:'center',alignSelf:'stretch',flex:1,justifyContent:'center'},initialsText:function(){return{color:'rgba(255, 255, 255, 0.6)',fontSize:.45*(arguments.length>0&&void 0!==arguments[0]?arguments[0]:65),fontWeight:'100'}},staticAvatar:{backgroundColor:r(d[0]).ColorPalette.lightGrey,opacity:.4}};e.default=t},529,[406]); -__d(function(g,r,i,a,m,e,d){m.exports=r(d[0]).registerAsset({__packager_asset:!0,httpServerLocation:"/assets/images",width:200,height:200,scales:[1],hash:"d424259cf27ba5c430d387e32c8cca56",name:"avatar",type:"png"})},530,[178]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),o=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e._mapStateToProps=S,e.default=e.DEFAULT_SIZE=void 0;var l=o(r(d[2])),n=o(r(d[3])),s=o(r(d[4])),u=o(r(d[5])),v=o(r(d[6])),c=o(r(d[7])),f=t(r(d[8])),p=r(d[9]),_=r(d[10]),A=r(d[11]),h=r(d[12]),E=r(d[13]);e.DEFAULT_SIZE=65;var L=(function(t){function o(t){var n;return(0,l.default)(this,o),(n=(0,s.default)(this,(0,u.default)(o).call(this,t))).state={avatarFailed:!1},n._onAvatarLoadError=n._onAvatarLoadError.bind((0,c.default)((0,c.default)(n))),n}return(0,v.default)(o,t),(0,n.default)(o,[{key:"componentDidUpdate",value:function(t){t.url!==this.props.url&&this.setState({avatarFailed:!1})}},{key:"render",value:function(){var t=this.props,o=t._initialsBase,l=t._loadableAvatarUrl,n=t.className,s=t.colorBase,u=t.id,v=t.size,c=t.url,p={className:n,color:void 0,id:u,initials:void 0,onAvatarLoadError:void 0,size:v,url:void 0},_=!this.state.avatarFailed&&c||l;_&&(p.onAvatarLoadError=this._onAvatarLoadError,p.url=_);var A=(0,h.getInitials)(o);return A&&(p.color=(0,h.getAvatarColor)(s||o),p.initials=A),f.default.createElement(E.StatelessAvatar,p)}},{key:"_onAvatarLoadError",value:function(){this.setState({avatarFailed:!0})}}]),o})(f.PureComponent);function S(t,o){var l,n=o.colorBase,s=o.displayName,u=o.participantId,v=u&&(0,_.getParticipantById)(t,u),c=null!=(l=null==v?void 0:v.name)?l:s,f=t['features/video-layout'].screenShares||[],A=null==v?void 0:v.loadableAvatarUrl;return u&&f.includes(u)&&(A=p.IconShareDesktop),{_initialsBase:c,_loadableAvatarUrl:A,colorBase:!n&&v?v.id:n}}var y=(0,A.connect)(S)(L);e.default=y},531,[2,3,4,5,6,9,10,8,13,403,532,534,920,400]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})});var c=r(d[4]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}})}),r(d[5]),r(d[6])},532,[533,740,897,895,741,913,919]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.dominantSpeakerChanged=function(t,n){return{type:p.DOMINANT_SPEAKER_CHANGED,participant:{conference:n,id:t}}},e.kickParticipant=function(t){return{type:p.KICK_PARTICIPANT,id:t}},e.localParticipantConnectionStatusChanged=function(t){return function(n,c){var o=(0,u.getLocalParticipant)(c);if(o)return n(f(o.id,t))}},e.localParticipantIdChanged=function(t){return function(n,c){var o=(0,u.getLocalParticipant)(c);if(o)return n({type:p.PARTICIPANT_ID_CHANGED,conference:void 0,newValue:t,oldValue:o.id})}},e.localParticipantJoined=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return l((0,c.set)(t,'local',!0))},e.localParticipantLeft=function(){return function(t,n){var c=(0,u.getLocalParticipant)(n);if(c)return t(P(c.id,void 0))}},e.localParticipantRoleChanged=function(t){return function(n,c){var o=(0,u.getLocalParticipant)(c);if(o)return n(I(o.id,t))}},e.muteRemoteParticipant=function(t){return{type:p.MUTE_REMOTE_PARTICIPANT,id:t}},e.participantConnectionStatusChanged=f,e.participantJoined=l,e.hiddenParticipantJoined=function(t,n){return{type:p.HIDDEN_PARTICIPANT_JOINED,id:t,displayName:n}},e.hiddenParticipantLeft=function(t){return{type:p.HIDDEN_PARTICIPANT_LEFT,id:t}},e.participantLeft=P,e.participantPresenceChanged=function(t,n){return A({id:t,presence:n})},e.participantRoleChanged=I,e.participantUpdated=A,e.participantMutedUs=function(t){return function(n,c){t&&n((0,o.showNotification)({descriptionKey:'notify.mutedRemotelyDescription',titleKey:'notify.mutedRemotelyTitle',titleArguments:{participantDisplayName:(0,u.getParticipantDisplayName)(c,t.getId())}}))}},e.participantKicked=function(t,n){return function(c,f){c({type:p.PARTICIPANT_KICKED,kicked:n.getId(),kicker:t.getId()}),c((0,o.showNotification)({titleArguments:{kicked:(0,u.getParticipantDisplayName)(f,n.getId()),kicker:(0,u.getParticipantDisplayName)(f,t.getId())},titleKey:'notify.kickParticipant'},2*o.NOTIFICATION_TIMEOUT))}},e.pinParticipant=function(t){return{type:p.PIN_PARTICIPANT,participant:{id:t}}},e.setLoadableAvatarUrl=function(t,n){return{type:p.SET_LOADABLE_AVATAR_URL,participant:{id:t,loadableAvatarUrl:n}}};var n=t(r(d[1])),c=r(d[2]),o=r(d[3]),p=r(d[4]),u=r(d[5]);function f(t,n){return{type:p.PARTICIPANT_UPDATED,participant:{connectionStatus:n,id:t}}}function l(t){if(t.local)return{type:p.PARTICIPANT_JOINED,participant:t};var n=t.conference;if(!n)throw Error('A remote participant must be associated with a JitsiConference!');return function(c,o){var u=o()['features/base/conference'];if(n===u.conference||n===u.joining)return c({type:p.PARTICIPANT_JOINED,participant:t})}}function P(t,n){return{type:p.PARTICIPANT_LEFT,participant:{conference:n,id:t}}}function I(t,n){return A({id:t,role:n})}function A(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},c=(0,n.default)({},t);return t.name&&(c.name=(0,u.getNormalizedDisplayName)(t.name)),{type:p.PARTICIPANT_UPDATED,participant:c}}},533,[3,54,534,587,740,741]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var n={MiddlewareRegistry:!0,ReducerRegistry:!0,StateListenerRegistry:!0};Object.defineProperty(e,"MiddlewareRegistry",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(e,"ReducerRegistry",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(e,"StateListenerRegistry",{enumerable:!0,get:function(){return f.default}});var u=r(d[1]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}}))});var c=t(r(d[2])),o=t(r(d[3])),f=t(r(d[4]))},534,[3,535,572,573,574]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.assign=function(t,n){var u=t;for(var f in n)u=c(u,f,n[f],u===t);return u},e.connect=function(t,n){return(0,o.connect)(t,n)},e.equals=function(t,n){return f.default.isEqual(t,n)},e.set=function(t,n,u){return c(t,n,u,!0)},e.toState=function(t){if(t){if('function'==typeof t)return t();var n=t.getState;if('function'==typeof n)return n()}return t};var n=t(r(d[1])),u=t(r(d[2])),f=t(r(d[3])),o=r(d[4]);function c(t,f,o,c){if(void 0===o&&Object.prototype.hasOwnProperty.call(t,f)){var l=c?(0,u.default)({},t):t;if(delete l[f])return l}if(t[f]!==o){if(c)return(0,u.default)({},t,(0,n.default)({},f,o));t[f]=o}return t}},535,[3,55,54,536,537]); -__d(function(n,t,r,e,u,i,o){(function(){var t,r=200,e='Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',o='Expected a function',f='__lodash_hash_undefined__',a=500,c='__lodash_placeholder__',l=1,s=2,h=4,p=1,v=2,_=1,g=2,d=4,y=8,x=16,b=32,w=64,m=128,j=256,A=512,k=30,z='...',O=800,I=16,R=1,E=2,S=1/0,L=9007199254740991,W=1.7976931348623157e308,C=NaN,T=4294967295,U=4294967294,B=2147483647,$=[['ary',m],['bind',_],['bindKey',g],['curry',y],['curryRight',x],['flip',A],['partial',b],['partialRight',w],['rearg',j]],D='[object Arguments]',M='[object Array]',F='[object AsyncFunction]',N='[object Boolean]',P='[object Date]',Z='[object DOMException]',q='[object Error]',K='[object Function]',V='[object GeneratorFunction]',G='[object Map]',H='[object Number]',J='[object Null]',Y='[object Object]',Q='[object Proxy]',X='[object RegExp]',nn='[object Set]',tn='[object String]',rn='[object Symbol]',en='[object Undefined]',un='[object WeakMap]',on='[object WeakSet]',fn='[object ArrayBuffer]',an='[object DataView]',cn='[object Float32Array]',ln='[object Float64Array]',sn='[object Int8Array]',hn='[object Int16Array]',pn='[object Int32Array]',vn='[object Uint8Array]',_n='[object Uint8ClampedArray]',gn='[object Uint16Array]',dn='[object Uint32Array]',yn=/\b__p \+= '';/g,xn=/\b(__p \+=) '' \+/g,bn=/(__e\(.*?\)|\b__t\)) \+\n'';/g,wn=/&(?:amp|lt|gt|quot|#39);/g,mn=/[&<>"']/g,jn=RegExp(wn.source),An=RegExp(mn.source),kn=/<%-([\s\S]+?)%>/g,zn=/<%([\s\S]+?)%>/g,On=/<%=([\s\S]+?)%>/g,In=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Rn=/^\w*$/,En=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,Sn=/[\\^$.*+?()[\]{}|]/g,Ln=RegExp(Sn.source),Wn=/^\s+|\s+$/g,Cn=/^\s+/,Tn=/\s+$/,Un=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Bn=/\{\n\/\* \[wrapped with (.+)\] \*/,$n=/,? & /,Dn=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,Mn=/\\(\\)?/g,Fn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Nn=/\w*$/,Pn=/^[-+]0x[0-9a-f]+$/i,Zn=/^0b[01]+$/i,qn=/^\[object .+?Constructor\]$/,Kn=/^0o[0-7]+$/i,Vn=/^(?:0|[1-9]\d*)$/,Gn=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,Hn=/($^)/,Jn=/['\n\r\u2028\u2029\\]/g,Yn="[\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000]",Qn="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",Xn="(?:\\ud83c[\\udde6-\\uddff]){2}",nt="[\\ud800-\\udbff][\\udc00-\\udfff]",tt="[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?"+("(?:\\u200d(?:"+["[^\\ud800-\\udfff]",Xn,nt].join('|')+")[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?)*"),rt='(?:'+["[\\u2700-\\u27bf]",Xn,nt].join('|')+')'+tt,et='(?:'+["[^\\ud800-\\udfff][\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]?",Qn,Xn,nt,"[\\ud800-\\udfff]"].join('|')+')',ut=RegExp("['\u2019]",'g'),it=RegExp(Qn,'g'),ot=RegExp("\\ud83c[\\udffb-\\udfff](?=\\ud83c[\\udffb-\\udfff])|"+et+tt,'g'),ft=RegExp(["[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+(?:['\u2019](?:d|ll|m|re|s|t|ve))?(?="+[Yn,"[A-Z\\xc0-\\xd6\\xd8-\\xde]",'$'].join('|')+')',"(?:[A-Z\\xc0-\\xd6\\xd8-\\xde]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?(?="+[Yn,"[A-Z\\xc0-\\xd6\\xd8-\\xde](?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])",'$'].join('|')+')',"[A-Z\\xc0-\\xd6\\xd8-\\xde]?(?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:d|ll|m|re|s|t|ve))?","[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?",'\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])','\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])','\\d+',rt].join('|'),'g'),at=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]"),ct=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,lt=['Array','Buffer','DataView','Date','Error','Float32Array','Float64Array','Function','Int8Array','Int16Array','Int32Array','Map','Math','Object','Promise','RegExp','Set','String','Symbol','TypeError','Uint8Array','Uint8ClampedArray','Uint16Array','Uint32Array','WeakMap','_','clearTimeout','isFinite','parseInt','setTimeout'],st=-1,ht={};ht[cn]=ht[ln]=ht[sn]=ht[hn]=ht[pn]=ht[vn]=ht[_n]=ht[gn]=ht[dn]=!0,ht[D]=ht[M]=ht[fn]=ht[N]=ht[an]=ht[P]=ht[q]=ht[K]=ht[G]=ht[H]=ht[Y]=ht[X]=ht[nn]=ht[tn]=ht[un]=!1;var pt={};pt[D]=pt[M]=pt[fn]=pt[an]=pt[N]=pt[P]=pt[cn]=pt[ln]=pt[sn]=pt[hn]=pt[pn]=pt[G]=pt[H]=pt[Y]=pt[X]=pt[nn]=pt[tn]=pt[rn]=pt[vn]=pt[_n]=pt[gn]=pt[dn]=!0,pt[q]=pt[K]=pt[un]=!1;var vt={'\\':'\\',"'":"'",'\n':'n','\r':'r',"\u2028":'u2028',"\u2029":'u2029'},_t=parseFloat,gt=parseInt,dt='object'==typeof n&&n&&n.Object===Object&&n,yt='object'==typeof self&&self&&self.Object===Object&&self,xt=dt||yt||Function('return this')(),bt='object'==typeof i&&i&&!i.nodeType&&i,wt=bt&&'object'==typeof u&&u&&!u.nodeType&&u,mt=wt&&wt.exports===bt,jt=mt&&dt.process,At=(function(){try{var n=wt&&wt.require&&wt.require('util').types;return n||jt&&jt.binding&&jt.binding('util')}catch(n){}})(),kt=At&&At.isArrayBuffer,zt=At&&At.isDate,Ot=At&&At.isMap,It=At&&At.isRegExp,Rt=At&&At.isSet,Et=At&&At.isTypedArray;function St(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function Lt(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u-1}function $t(n,t,r){for(var e=-1,u=null==n?0:n.length;++e-1;);return r}function lr(n,t){for(var r=n.length;r--&&Ht(t,n[r],0)>-1;);return r}function sr(n,t){for(var r=n.length,e=0;r--;)n[r]===t&&++e;return e}var hr=nr({'\xc0':'A','\xc1':'A','\xc2':'A','\xc3':'A','\xc4':'A','\xc5':'A','\xe0':'a','\xe1':'a','\xe2':'a','\xe3':'a','\xe4':'a','\xe5':'a','\xc7':'C','\xe7':'c','\xd0':'D','\xf0':'d','\xc8':'E','\xc9':'E','\xca':'E','\xcb':'E','\xe8':'e','\xe9':'e','\xea':'e','\xeb':'e','\xcc':'I','\xcd':'I','\xce':'I','\xcf':'I','\xec':'i','\xed':'i','\xee':'i','\xef':'i','\xd1':'N','\xf1':'n','\xd2':'O','\xd3':'O','\xd4':'O','\xd5':'O','\xd6':'O','\xd8':'O','\xf2':'o','\xf3':'o','\xf4':'o','\xf5':'o','\xf6':'o','\xf8':'o','\xd9':'U','\xda':'U','\xdb':'U','\xdc':'U','\xf9':'u','\xfa':'u','\xfb':'u','\xfc':'u','\xdd':'Y','\xfd':'y','\xff':'y','\xc6':'Ae','\xe6':'ae','\xde':'Th','\xfe':'th','\xdf':'ss',"\u0100":'A',"\u0102":'A',"\u0104":'A',"\u0101":'a',"\u0103":'a',"\u0105":'a',"\u0106":'C',"\u0108":'C',"\u010a":'C',"\u010c":'C',"\u0107":'c',"\u0109":'c',"\u010b":'c',"\u010d":'c',"\u010e":'D',"\u0110":'D',"\u010f":'d',"\u0111":'d',"\u0112":'E',"\u0114":'E',"\u0116":'E',"\u0118":'E',"\u011a":'E',"\u0113":'e',"\u0115":'e',"\u0117":'e',"\u0119":'e',"\u011b":'e',"\u011c":'G',"\u011e":'G',"\u0120":'G',"\u0122":'G',"\u011d":'g',"\u011f":'g',"\u0121":'g',"\u0123":'g',"\u0124":'H',"\u0126":'H',"\u0125":'h',"\u0127":'h',"\u0128":'I',"\u012a":'I',"\u012c":'I',"\u012e":'I',"\u0130":'I',"\u0129":'i',"\u012b":'i',"\u012d":'i',"\u012f":'i',"\u0131":'i',"\u0134":'J',"\u0135":'j',"\u0136":'K',"\u0137":'k',"\u0138":'k',"\u0139":'L',"\u013b":'L',"\u013d":'L',"\u013f":'L',"\u0141":'L',"\u013a":'l',"\u013c":'l',"\u013e":'l',"\u0140":'l',"\u0142":'l',"\u0143":'N',"\u0145":'N',"\u0147":'N',"\u014a":'N',"\u0144":'n',"\u0146":'n',"\u0148":'n',"\u014b":'n',"\u014c":'O',"\u014e":'O',"\u0150":'O',"\u014d":'o',"\u014f":'o',"\u0151":'o',"\u0154":'R',"\u0156":'R',"\u0158":'R',"\u0155":'r',"\u0157":'r',"\u0159":'r',"\u015a":'S',"\u015c":'S',"\u015e":'S',"\u0160":'S',"\u015b":'s',"\u015d":'s',"\u015f":'s',"\u0161":'s',"\u0162":'T',"\u0164":'T',"\u0166":'T',"\u0163":'t',"\u0165":'t',"\u0167":'t',"\u0168":'U',"\u016a":'U',"\u016c":'U',"\u016e":'U',"\u0170":'U',"\u0172":'U',"\u0169":'u',"\u016b":'u',"\u016d":'u',"\u016f":'u',"\u0171":'u',"\u0173":'u',"\u0174":'W',"\u0175":'w',"\u0176":'Y',"\u0177":'y',"\u0178":'Y',"\u0179":'Z',"\u017b":'Z',"\u017d":'Z',"\u017a":'z',"\u017c":'z',"\u017e":'z',"\u0132":'IJ',"\u0133":'ij',"\u0152":'Oe',"\u0153":'oe',"\u0149":"'n","\u017f":'s'}),pr=nr({'&':'&','<':'<','>':'>','"':'"',"'":'''});function vr(n){return'\\'+vt[n]}function _r(n,r){return null==n?t:n[r]}function gr(n){return at.test(n)}function dr(n){return ct.test(n)}function yr(n){for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}function xr(n){var t=-1,r=Array(n.size);return n.forEach(function(n,e){r[++t]=[e,n]}),r}function br(n,t){return function(r){return n(t(r))}}function wr(n,t){for(var r=-1,e=n.length,u=0,i=[];++r','"':'"',''':"'"});function Rr(n){for(var t=ot.lastIndex=0;ot.test(n);)++t;return t}function Er(n){return n.match(ot)||[]}function Sr(n){return n.match(ft)||[]}var Lr=(function n(u){var i,Dn=(u=null==u?xt:Lr.defaults(xt.Object(),u,Lr.pick(xt,lt))).Array,Yn=u.Date,Qn=u.Error,Xn=u.Function,nt=u.Math,tt=u.Object,rt=u.RegExp,et=u.String,ot=u.TypeError,ft=Dn.prototype,at=Xn.prototype,ct=tt.prototype,vt=u['__core-js_shared__'],dt=at.toString,yt=ct.hasOwnProperty,bt=0,wt=(i=/[^.]+$/.exec(vt&&vt.keys&&vt.keys.IE_PROTO||''))?'Symbol(src)_1.'+i:'',jt=ct.toString,At=dt.call(tt),Zt=xt._,qt=rt('^'+dt.call(yt).replace(Sn,'\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,'$1.*?')+'$'),nr=mt?u.Buffer:t,Ar=u.Symbol,Rr=u.Uint8Array,Er=nr?nr.allocUnsafe:t,Wr=br(tt.getPrototypeOf,tt),Cr=tt.create,Tr=ct.propertyIsEnumerable,Ur=ft.splice,Br=Ar?"function"==typeof Ar?Ar.isConcatSpreadable:"@@isConcatSpreadable":t,$r=Ar?"function"==typeof Ar?Ar.iterator:"@@iterator":t,Dr=Ar?"function"==typeof Ar?Ar.toStringTag:"@@toStringTag":t,Mr=(function(){try{var n=co(tt,'defineProperty');return n({},'',{}),n}catch(n){}})(),Fr=u.clearTimeout!==xt.clearTimeout&&u.clearTimeout,Nr=Yn&&Yn.now!==xt.Date.now&&Yn.now,Pr=u.setTimeout!==xt.setTimeout&&u.setTimeout,Zr=nt.ceil,qr=nt.floor,Kr=tt.getOwnPropertySymbols,Vr=nr?nr.isBuffer:t,Gr=u.isFinite,Hr=ft.join,Jr=br(tt.keys,tt),Yr=nt.max,Qr=nt.min,Xr=Yn.now,ne=u.parseInt,te=nt.random,re=ft.reverse,ee=co(u,'DataView'),ue=co(u,'Map'),ie=co(u,'Promise'),oe=co(u,'Set'),fe=co(u,'WeakMap'),ae=co(tt,'create'),ce=fe&&new fe,le={},se=Ko(ee),he=Ko(ue),pe=Ko(ie),ve=Ko(oe),_e=Ko(fe),ge=Ar?"function"==typeof Ar?Ar.prototype:"@@prototype":t,de=ge?ge.valueOf:t,ye=ge?ge.toString:t;function xe(n){if(sa(n)&&!na(n)&&!(n instanceof je)){if(n instanceof me)return n;if(yt.call(n,'__wrapped__'))return Go(n)}return new me(n)}var be=(function(){function n(){}return function(r){if(!la(r))return{};if(Cr)return Cr(r);n.prototype=r;var e=new n;return n.prototype=t,e}})();function we(){}function me(n,r){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!r,this.__index__=0,this.__values__=t}function je(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=T,this.__views__=[]}function Ae(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t=r?n:r)),n}function Ne(n,r,e,u,i,o){var f,a=r&l,c=r&s,p=r&h;if(e&&(f=i?e(n,u,i,o):e(n)),f!==t)return f;if(!la(n))return n;var v=na(n);if(v){if(f=yo(n),!a)return bi(n,f)}else{var _=po(n),g=_==K||_==V;if(ua(n))return hi(n,a);if(_==Y||_==D||g&&!i){if(f=c||g?{}:xo(n),!a)return c?ji(n,$e(f,n)):mi(n,Be(f,n))}else{if(!pt[_])return i?n:{};f=bo(n,_,a)}}o||(o=new Ie);var d=o.get(n);if(d)return d;o.set(n,f),ga(n)?n.forEach(function(t){f.add(Ne(t,r,e,t,n,o))}):ha(n)&&n.forEach(function(t,u){f.set(u,Ne(t,r,e,u,n,o))});var y=v?t:(p?c?ro:to:c?Na:Fa)(n);return Wt(y||n,function(t,u){y&&(t=n[u=t]),Ce(f,u,Ne(t,r,e,u,n,o))}),f}function Pe(n){var t=Fa(n);return function(r){return Ze(r,n,t)}}function Ze(n,r,e){var u=e.length;if(null==n)return!u;for(n=tt(n);u--;){var i=e[u],o=r[i],f=n[i];if(f===t&&!(i in n)||!o(f))return!1}return!0}function qe(n,r,e){if('function'!=typeof n)throw new ot(o);return Do(function(){n.apply(t,e)},r)}function Ke(n,t,e,u){var i=-1,o=Bt,f=!0,a=n.length,c=[],l=t.length;if(!a)return c;e&&(t=Dt(t,or(e))),u?(o=$t,f=!1):t.length>=r&&(o=ar,f=!1,t=new Oe(t));n:for(;++i-1},ke.prototype.set=function(n,t){var r=this.__data__,e=Te(r,n);return e<0?(++this.size,r.push([n,t])):r[e][1]=t,this},ze.prototype.clear=function(){this.size=0,this.__data__={hash:new Ae,map:new(ue||ke),string:new Ae}},ze.prototype.delete=function(n){var t=fo(this,n).delete(n);return this.size-=t?1:0,t},ze.prototype.get=function(n){return fo(this,n).get(n)},ze.prototype.has=function(n){return fo(this,n).has(n)},ze.prototype.set=function(n,t){var r=fo(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this},Oe.prototype.add=Oe.prototype.push=function(n){return this.__data__.set(n,f),this},Oe.prototype.has=function(n){return this.__data__.has(n)},Ie.prototype.clear=function(){this.__data__=new ke,this.size=0},Ie.prototype.delete=function(n){var t=this.__data__,r=t.delete(n);return this.size=t.size,r},Ie.prototype.get=function(n){return this.__data__.get(n)},Ie.prototype.has=function(n){return this.__data__.has(n)},Ie.prototype.set=function(n,t){var r=this.__data__;if(r instanceof ke){var e=r.__data__;if(!ue||e.length<199)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new ze(e)}return r.set(n,t),this.size=r.size,this};var Ve=zi(ru),Ge=zi(eu,!0);function He(n,t){var r=!0;return Ve(n,function(n,e,u){return r=!!t(n,e,u)}),r}function Je(n,r,e){for(var u=-1,i=n.length;++ui?0:i+e),(u=u===t||u>i?i:Aa(u))<0&&(u+=i),u=e>u?0:ka(u);e0&&r(f)?t>1?Xe(f,t-1,r,e,u):Mt(u,f):e||(u[u.length]=f)}return u}var nu=Oi(),tu=Oi(!0);function ru(n,t){return n&&nu(n,t,Fa)}function eu(n,t){return n&&tu(n,t,Fa)}function uu(n,t){return Ut(t,function(t){return fa(n[t])})}function iu(n,r){for(var e=0,u=(r=ai(r,n)).length;null!=n&&et}function cu(n,t){return null!=n&&yt.call(n,t)}function lu(n,t){return null!=n&&t in tt(n)}function su(n,t,r){return n>=Qr(t,r)&&n=120&&s.length>=120)?new Oe(f&&s):t}s=n[0];var h=-1,p=a[0];n:for(;++h-1;)f!==n&&Ur.call(f,a,1),Ur.call(n,a,1);return n}function Tu(n,t){for(var r=n?t.length:0,e=r-1;r--;){var u=t[r];if(r==e||u!==i){var i=u;jo(u)?Ur.call(n,u,1):ni(n,u)}}return n}function Uu(n,t){return n+qr(te()*(t-n+1))}function Bu(n,t,r,e){for(var u=-1,i=Yr(Zr((t-n)/(r||1)),0),o=Dn(i);i--;)o[e?i:++u]=n,n+=r;return o}function $u(n,t){var r='';if(!n||t<1||t>L)return r;do{t%2&&(r+=n),(t=qr(t/2))&&(n+=n)}while(t);return r}function Du(n,t){return Mo(Co(n,t,pc),n+'')}function Mu(n){return Ee(Ja(n))}function Fu(n,t){var r=Ja(n);return Po(r,Fe(t,0,r.length))}function Nu(n,r,e,u){if(!la(n))return n;for(var i=-1,o=(r=ai(r,n)).length,f=o-1,a=n;null!=a&&++iu?0:u+t),(r=r>u?u:r)<0&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0;for(var i=Dn(u);++e>>1,o=n[i];null!==o&&!ya(o)&&(r?o<=t:o=r){var l=t?null:qi(n);if(l)return mr(l);f=!1,i=ar,c=new Oe}else c=t?[]:a;n:for(;++u=u?n:Ku(n,r,e)}var si=Fr||function(n){return xt.clearTimeout(n)};function hi(n,t){if(t)return n.slice();var r=n.length,e=Er?Er(r):new n.constructor(r);return n.copy(e),e}function pi(n){var t=new n.constructor(n.byteLength);return new Rr(t).set(new Rr(n)),t}function vi(n,t){var r=t?pi(n.buffer):n.buffer;return new n.constructor(r,n.byteOffset,n.byteLength)}function _i(n,t){var r=t?pi(n.buffer):n.buffer;return new n.constructor(r,n.byteOffset,n.length)}function gi(n,r){if(n!==r){var e=n!==t,u=null===n,i=n==n,o=ya(n),f=r!==t,a=null===r,c=r==r,l=ya(r);if(!a&&!l&&!o&&n>r||o&&f&&c&&!a&&!l||u&&f&&c||!e&&c||!i)return 1;if(!u&&!o&&!l&&n=f?a:a*('desc'==r[e]?-1:1)}return n.index-t.index}function yi(n,t,r,e){for(var u=-1,i=n.length,o=r.length,f=-1,a=t.length,c=Yr(i-o,0),l=Dn(a+c),s=!e;++f1?e[i-1]:t,f=i>2?e[2]:t;for(o=n.length>3&&'function'==typeof o?(i--,o):t,f&&Ao(e[0],e[1],f)&&(o=i<3?t:o,i=1),r=tt(r);++u-1?i[o?r[f]:f]:t}}function Ci(n){return no(function(r){var e=r.length,u=e,i=me.prototype.thru;for(n&&r.reverse();u--;){var f=r[u];if('function'!=typeof f)throw new ot(o);if(i&&!a&&'wrapper'==uo(f))var a=new me([],!0)}for(u=a?u:e;++u1&&g.reverse(),s&&c<_&&(g.length=c),this&&this!==xt&&this instanceof t&&(A=y||Si(A)),A.apply(j,g)}}function Ui(n,t){return function(r,e){return pu(r,n,t(e),{})}}function Bi(n,r){return function(e,u){var i;if(e===t&&u===t)return r;if(e!==t&&(i=e),u!==t){if(i===t)return u;'string'==typeof e||'string'==typeof u?(e=Qu(e),u=Qu(u)):(e=Yu(e),u=Yu(u)),i=n(e,u)}return i}}function $i(n){return no(function(t){return t=Dt(t,or(oo())),Du(function(r){var e=this;return n(t,function(n){return St(n,e,r)})})})}function Di(n,r){var e=(r=r===t?' ':Qu(r)).length;if(e<2)return e?$u(r,n):r;var u=$u(r,Zr(n/zr(r)));return gr(r)?li(Or(u),0,n).join(''):u.slice(0,n)}function Mi(n,t,r,e){var u=t&_,i=Si(n);return function t(){for(var o=-1,f=arguments.length,a=-1,c=e.length,l=Dn(c+f),s=this&&this!==xt&&this instanceof t?i:n;++aa))return!1;var l=o.get(n);if(l&&o.get(r))return l==r;var s=-1,h=!0,_=e&v?new Oe:t;for(o.set(n,r),o.set(r,n);++s1?'& ':'')+t[e],t=t.join(r>2?', ':' '),n.replace(Un,'{\n/* [wrapped with '+t+'] */\n')}function mo(n){return na(n)||Xf(n)||!!(Br&&n&&n[Br])}function jo(n,t){var r=typeof n;return!!(t=null==t?L:t)&&('number'==r||'symbol'!=r&&Vn.test(n))&&n>-1&&n%1==0&&n0){if(++r>=O)return arguments[0]}else r=0;return n.apply(t,arguments)}}function Po(n,r){var e=-1,u=n.length,i=u-1;for(r=r===t?u:r;++e1?n[r-1]:t;return _f(n,e='function'==typeof e?(n.pop(),e):t)});function mf(n){var t=xe(n);return t.__chain__=!0,t}function jf(n,t){return t(n)}var Af=no(function(n){var r=n.length,e=r?n[0]:0,u=this.__wrapped__,i=function(t){return Me(t,n)};return!(r>1||this.__actions__.length)&&u instanceof je&&jo(e)?((u=u.slice(e,+e+(r?1:0))).__actions__.push({func:jf,args:[i],thisArg:t}),new me(u,this.__chain__).thru(function(n){return r&&!n.length&&n.push(t),n})):this.thru(i)});var kf=Ai(function(n,t,r){yt.call(n,r)?++n[r]:De(n,r,1)});var zf=Wi(Qo),Of=Wi(Xo);function If(n,t){return(na(n)?Wt:Ve)(n,oo(t,3))}function Rf(n,t){return(na(n)?Ct:Ge)(n,oo(t,3))}var Ef=Ai(function(n,t,r){yt.call(n,r)?n[r].push(t):De(n,r,[t])});var Sf=Du(function(n,t,r){var e=-1,u='function'==typeof t,i=ra(n)?Dn(n.length):[];return Ve(n,function(n){i[++e]=u?St(t,n,r):vu(n,t,r)}),i}),Lf=Ai(function(n,t,r){De(n,r,t)});function Wf(n,t){return(na(n)?Dt:Au)(n,oo(t,3))}var Cf=Ai(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]});var Tf=Du(function(n,t){if(null==n)return[];var r=t.length;return r>1&&Ao(n,t[0],t[1])?t=[]:r>2&&Ao(t[0],t[1],t[2])&&(t=[t[0]]),Eu(n,Xe(t,1),[])}),Uf=Nr||function(){return xt.Date.now()};function Bf(n,r,e){return r=e?t:r,r=n&&null==r?n.length:r,Vi(n,m,t,t,t,t,r)}function $f(n,r){var e;if('function'!=typeof r)throw new ot(o);return n=Aa(n),function(){return--n>0&&(e=r.apply(this,arguments)),n<=1&&(r=t),e}}var Df=Du(function(n,t,r){var e=_;if(r.length){var u=wr(r,io(Df));e|=b}return Vi(n,e,t,r,u)}),Mf=Du(function(n,t,r){var e=3;if(r.length){var u=wr(r,io(Mf));e|=b}return Vi(t,e,n,r,u)});function Ff(n,r,e){var u,i,f,a,c,l,s=0,h=!1,p=!1,v=!0;if('function'!=typeof n)throw new ot(o);function _(r){var e=u,o=i;return u=i=t,s=r,a=n.apply(o,e)}function g(n){return s=n,c=Do(x,r),h?_(n):a}function d(n){var t=r-(n-l);return p?Qr(t,f-(n-s)):t}function y(n){var e=n-l;return l===t||e>=r||e<0||p&&n-s>=f}function x(){var n=Uf();if(y(n))return b(n);c=Do(x,d(n))}function b(n){return c=t,v&&u?_(n):(u=i=t,a)}function w(){var n=Uf(),e=y(n);if(u=arguments,i=this,l=n,e){if(c===t)return g(l);if(p)return si(c),c=Do(x,r),_(l)}return c===t&&(c=Do(x,r)),a}return r=za(r)||0,la(e)&&(h=!!e.leading,f=(p='maxWait'in e)?Yr(za(e.maxWait)||0,r):f,v='trailing'in e?!!e.trailing:v),w.cancel=function(){c!==t&&si(c),s=0,u=l=i=c=t},w.flush=function(){return c===t?a:b(Uf())},w}var Nf=Du(function(n,t){return qe(n,1,t)}),Pf=Du(function(n,t,r){return qe(n,za(t)||0,r)});function Zf(n,t){if('function'!=typeof n||null!=t&&'function'!=typeof t)throw new ot(o);var r=function r(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var o=n.apply(this,e);return r.cache=i.set(u,o)||i,o};return r.cache=new(Zf.Cache||ze),r}function qf(n){if('function'!=typeof n)throw new ot(o);return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}Zf.Cache=ze;var Kf=ci(function(n,t){var r=(t=1==t.length&&na(t[0])?Dt(t[0],or(oo())):Dt(Xe(t,1),or(oo()))).length;return Du(function(e){for(var u=-1,i=Qr(e.length,r);++u=t}),Xf=_u((function(){return arguments})())?_u:function(n){return sa(n)&&yt.call(n,'callee')&&!Tr.call(n,'callee')},na=Dn.isArray,ta=kt?or(kt):function(n){return sa(n)&&fu(n)==fn};function ra(n){return null!=n&&ca(n.length)&&!fa(n)}function ea(n){return sa(n)&&ra(n)}var ua=Vr||zc,ia=zt?or(zt):function(n){return sa(n)&&fu(n)==P};function oa(n){if(!sa(n))return!1;var t=fu(n);return t==q||t==Z||'string'==typeof n.message&&'string'==typeof n.name&&!va(n)}function fa(n){if(!la(n))return!1;var t=fu(n);return t==K||t==V||t==F||t==Q}function aa(n){return'number'==typeof n&&n==Aa(n)}function ca(n){return'number'==typeof n&&n>-1&&n%1==0&&n<=L}function la(n){var t=typeof n;return null!=n&&('object'==t||'function'==t)}function sa(n){return null!=n&&'object'==typeof n}var ha=Ot?or(Ot):function(n){return sa(n)&&po(n)==G};function pa(n){return'number'==typeof n||sa(n)&&fu(n)==H}function va(n){if(!sa(n)||fu(n)!=Y)return!1;var t=Wr(n);if(null===t)return!0;var r=yt.call(t,'constructor')&&t.constructor;return'function'==typeof r&&r instanceof r&&dt.call(r)==At}var _a=It?or(It):function(n){return sa(n)&&fu(n)==X};var ga=Rt?or(Rt):function(n){return sa(n)&&po(n)==nn};function da(n){return'string'==typeof n||!na(n)&&sa(n)&&fu(n)==tn}function ya(n){return'symbol'==typeof n||sa(n)&&fu(n)==rn}var xa=Et?or(Et):function(n){return sa(n)&&ca(n.length)&&!!ht[fu(n)]};var ba=Ni(ju),wa=Ni(function(n,t){return n<=t});function ma(n){if(!n)return[];if(ra(n))return da(n)?Or(n):bi(n);if($r&&n[$r])return yr(n[$r]());var t=po(n);return(t==G?xr:t==nn?mr:Ja)(n)}function ja(n){return n?(n=za(n))===S||n===-1/0?(n<0?-1:1)*W:n==n?n:0:0===n?n:0}function Aa(n){var t=ja(n),r=t%1;return t==t?r?t-r:t:0}function ka(n){return n?Fe(Aa(n),0,T):0}function za(n){if('number'==typeof n)return n;if(ya(n))return C;if(la(n)){var t='function'==typeof n.valueOf?n.valueOf():n;n=la(t)?t+'':t}if('string'!=typeof n)return 0===n?n:+n;n=n.replace(Wn,'');var r=Zn.test(n);return r||Kn.test(n)?gt(n.slice(2),r?2:8):Pn.test(n)?C:+n}function Oa(n){return wi(n,Na(n))}function Ia(n){return null==n?'':Qu(n)}var Ra=ki(function(n,t){if(Io(t)||ra(t))wi(t,Fa(t),n);else for(var r in t)yt.call(t,r)&&Ce(n,r,t[r])}),Ea=ki(function(n,t){wi(t,Na(t),n)}),Sa=ki(function(n,t,r,e){wi(t,Na(t),n,e)}),La=ki(function(n,t,r,e){wi(t,Fa(t),n,e)}),Wa=no(Me);var Ca=Du(function(n,r){n=tt(n);var e=-1,u=r.length,i=u>2?r[2]:t;for(i&&Ao(r[0],r[1],i)&&(u=1);++e1),t}),wi(n,ro(n),r),e&&(r=Ne(r,7,Ji));for(var u=t.length;u--;)ni(r,t[u]);return r});var Ka=no(function(n,t){return null==n?{}:Su(n,t)});function Va(n,t){if(null==n)return{};var r=Dt(ro(n),function(n){return[n]});return t=oo(t),Lu(n,r,function(n,r){return t(n,r[0])})}var Ga=Ki(Fa),Ha=Ki(Na);function Ja(n){return null==n?[]:fr(n,Fa(n))}var Ya=Ei(function(n,t,r){return t=t.toLowerCase(),n+(r?Qa(t):t)});function Qa(n){return oc(Ia(n).toLowerCase())}function Xa(n){return(n=Ia(n))&&n.replace(Gn,hr).replace(it,'')}var nc=Ei(function(n,t,r){return n+(r?'-':'')+t.toLowerCase()}),tc=Ei(function(n,t,r){return n+(r?' ':'')+t.toLowerCase()}),rc=Ri('toLowerCase');var ec=Ei(function(n,t,r){return n+(r?'_':'')+t.toLowerCase()});var uc=Ei(function(n,t,r){return n+(r?' ':'')+oc(t)});var ic=Ei(function(n,t,r){return n+(r?' ':'')+t.toUpperCase()}),oc=Ri('toUpperCase');function fc(n,r,e){return n=Ia(n),(r=e?t:r)===t?dr(n)?Sr(n):Kt(n):n.match(r)||[]}var ac=Du(function(n,r){try{return St(n,t,r)}catch(n){return oa(n)?n:new Qn(n)}}),cc=no(function(n,t){return Wt(t,function(t){t=qo(t),De(n,t,Df(n[t],n))}),n});function lc(n){return function(){return n}}var sc=Ci(),hc=Ci(!0);function pc(n){return n}function vc(n){return bu('function'==typeof n?n:Ne(n,l))}var _c=Du(function(n,t){return function(r){return vu(r,n,t)}}),gc=Du(function(n,t){return function(r){return vu(n,r,t)}});function dc(n,t,r){var e=Fa(t),u=uu(t,e);null!=r||la(t)&&(u.length||!e.length)||(r=t,t=n,n=this,u=uu(t,Fa(t)));var i=!(la(r)&&'chain'in r&&!r.chain),o=fa(n);return Wt(u,function(r){var e=t[r];n[r]=e,o&&(n.prototype[r]=function(){var t=this.__chain__;if(i||t){var r=n(this.__wrapped__);return(r.__actions__=bi(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,Mt([this.value()],arguments))})}),n}function yc(){}var xc=$i(Dt),bc=$i(Tt),wc=$i(Pt);function mc(n){return ko(n)?Xt(qo(n)):Wu(n)}var jc=Fi(),Ac=Fi(!0);function kc(){return[]}function zc(){return!1}var Oc=Bi(function(n,t){return n+t},0),Ic=Zi('ceil'),Rc=Bi(function(n,t){return n/t},1),Ec=Zi('floor');var Sc,Lc=Bi(function(n,t){return n*t},1),Wc=Zi('round'),Cc=Bi(function(n,t){return n-t},0);return xe.after=function(n,t){if('function'!=typeof t)throw new ot(o);return n=Aa(n),function(){if(--n<1)return t.apply(this,arguments)}},xe.ary=Bf,xe.assign=Ra,xe.assignIn=Ea,xe.assignInWith=Sa,xe.assignWith=La,xe.at=Wa,xe.before=$f,xe.bind=Df,xe.bindAll=cc,xe.bindKey=Mf,xe.castArray=function(){if(!arguments.length)return[];var n=arguments[0];return na(n)?n:[n]},xe.chain=mf,xe.chunk=function(n,r,e){r=(e?Ao(n,r,e):r===t)?1:Yr(Aa(r),0);var u=null==n?0:n.length;if(!u||r<1)return[];for(var i=0,o=0,f=Dn(Zr(u/r));i>>0)?(n=Ia(n))&&('string'==typeof r||null!=r&&!_a(r))&&!(r=Qu(r))&&gr(n)?li(Or(n),0,e):n.split(r,e):[]},xe.spread=function(n,t){if('function'!=typeof n)throw new ot(o);return t=null==t?0:Yr(Aa(t),0),Du(function(r){var e=r[t],u=li(r,0,t);return e&&Mt(u,e),St(n,this,u)})},xe.tail=function(n){var t=null==n?0:n.length;return t?Ku(n,1,t):[]},xe.take=function(n,r,e){return n&&n.length?Ku(n,0,(r=e||r===t?1:Aa(r))<0?0:r):[]},xe.takeRight=function(n,r,e){var u=null==n?0:n.length;return u?Ku(n,(r=u-(r=e||r===t?1:Aa(r)))<0?0:r,u):[]},xe.takeRightWhile=function(n,t){return n&&n.length?ri(n,oo(t,3),!1,!0):[]},xe.takeWhile=function(n,t){return n&&n.length?ri(n,oo(t,3)):[]},xe.tap=function(n,t){return t(n),n},xe.throttle=function(n,t,r){var e=!0,u=!0;if('function'!=typeof n)throw new ot(o);return la(r)&&(e='leading'in r?!!r.leading:e,u='trailing'in r?!!r.trailing:u),Ff(n,t,{leading:e,maxWait:t,trailing:u})},xe.thru=jf,xe.toArray=ma,xe.toPairs=Ga,xe.toPairsIn=Ha,xe.toPath=function(n){return na(n)?Dt(n,qo):ya(n)?[n]:bi(Zo(Ia(n)))},xe.toPlainObject=Oa,xe.transform=function(n,t,r){var e=na(n),u=e||ua(n)||xa(n);if(t=oo(t,4),null==r){var i=n&&n.constructor;r=u?e?new i:[]:la(n)&&fa(i)?be(Wr(n)):{}}return(u?Wt:ru)(n,function(n,e,u){return t(r,n,e,u)}),r},xe.unary=function(n){return Bf(n,1)},xe.union=sf,xe.unionBy=hf,xe.unionWith=pf,xe.uniq=function(n){return n&&n.length?Xu(n):[]},xe.uniqBy=function(n,t){return n&&n.length?Xu(n,oo(t,2)):[]},xe.uniqWith=function(n,r){return r='function'==typeof r?r:t,n&&n.length?Xu(n,t,r):[]},xe.unset=function(n,t){return null==n||ni(n,t)},xe.unzip=vf,xe.unzipWith=_f,xe.update=function(n,t,r){return null==n?n:ti(n,t,fi(r))},xe.updateWith=function(n,r,e,u){return u='function'==typeof u?u:t,null==n?n:ti(n,r,fi(e),u)},xe.values=Ja,xe.valuesIn=function(n){return null==n?[]:fr(n,Na(n))},xe.without=gf,xe.words=fc,xe.wrap=function(n,t){return Vf(fi(t),n)},xe.xor=df,xe.xorBy=yf,xe.xorWith=xf,xe.zip=bf,xe.zipObject=function(n,t){return ii(n||[],t||[],Ce)},xe.zipObjectDeep=function(n,t){return ii(n||[],t||[],Nu)},xe.zipWith=wf,xe.entries=Ga,xe.entriesIn=Ha,xe.extend=Ea,xe.extendWith=Sa,dc(xe,xe),xe.add=Oc,xe.attempt=ac,xe.camelCase=Ya,xe.capitalize=Qa,xe.ceil=Ic,xe.clamp=function(n,r,e){return e===t&&(e=r,r=t),e!==t&&(e=(e=za(e))==e?e:0),r!==t&&(r=(r=za(r))==r?r:0),Fe(za(n),r,e)},xe.clone=function(n){return Ne(n,h)},xe.cloneDeep=function(n){return Ne(n,5)},xe.cloneDeepWith=function(n,r){return Ne(n,5,r='function'==typeof r?r:t)},xe.cloneWith=function(n,r){return Ne(n,h,r='function'==typeof r?r:t)},xe.conformsTo=function(n,t){return null==t||Ze(n,t,Fa(t))},xe.deburr=Xa,xe.defaultTo=function(n,t){return null==n||n!=n?t:n},xe.divide=Rc,xe.endsWith=function(n,r,e){n=Ia(n),r=Qu(r);var u=n.length,i=e=e===t?u:Fe(Aa(e),0,u);return(e-=r.length)>=0&&n.slice(e,i)==r},xe.eq=Jf,xe.escape=function(n){return(n=Ia(n))&&An.test(n)?n.replace(mn,pr):n},xe.escapeRegExp=function(n){return(n=Ia(n))&&Ln.test(n)?n.replace(Sn,'\\$&'):n},xe.every=function(n,r,e){var u=na(n)?Tt:He;return e&&Ao(n,r,e)&&(r=t),u(n,oo(r,3))},xe.find=zf,xe.findIndex=Qo,xe.findKey=function(n,t){return Vt(n,oo(t,3),ru)},xe.findLast=Of,xe.findLastIndex=Xo,xe.findLastKey=function(n,t){return Vt(n,oo(t,3),eu)},xe.floor=Ec,xe.forEach=If,xe.forEachRight=Rf,xe.forIn=function(n,t){return null==n?n:nu(n,oo(t,3),Na)},xe.forInRight=function(n,t){return null==n?n:tu(n,oo(t,3),Na)},xe.forOwn=function(n,t){return n&&ru(n,oo(t,3))},xe.forOwnRight=function(n,t){return n&&eu(n,oo(t,3))},xe.get=Ua,xe.gt=Yf,xe.gte=Qf,xe.has=function(n,t){return null!=n&&go(n,t,cu)},xe.hasIn=Ba,xe.head=tf,xe.identity=pc,xe.includes=function(n,t,r,e){n=ra(n)?n:Ja(n),r=r&&!e?Aa(r):0;var u=n.length;return r<0&&(r=Yr(u+r,0)),da(n)?r<=u&&n.indexOf(t,r)>-1:!!u&&Ht(n,t,r)>-1},xe.indexOf=function(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:Aa(r);return u<0&&(u=Yr(e+u,0)),Ht(n,t,u)},xe.inRange=function(n,r,e){return r=ja(r),e===t?(e=r,r=0):e=ja(e),su(n=za(n),r,e)},xe.invoke=Ma,xe.isArguments=Xf,xe.isArray=na,xe.isArrayBuffer=ta,xe.isArrayLike=ra,xe.isArrayLikeObject=ea,xe.isBoolean=function(n){return!0===n||!1===n||sa(n)&&fu(n)==N},xe.isBuffer=ua,xe.isDate=ia,xe.isElement=function(n){return sa(n)&&1===n.nodeType&&!va(n)},xe.isEmpty=function(n){if(null==n)return!0;if(ra(n)&&(na(n)||'string'==typeof n||'function'==typeof n.splice||ua(n)||xa(n)||Xf(n)))return!n.length;var t=po(n);if(t==G||t==nn)return!n.size;if(Io(n))return!wu(n).length;for(var r in n)if(yt.call(n,r))return!1;return!0},xe.isEqual=function(n,t){return gu(n,t)},xe.isEqualWith=function(n,r,e){var u=(e='function'==typeof e?e:t)?e(n,r):t;return u===t?gu(n,r,t,e):!!u},xe.isError=oa,xe.isFinite=function(n){return'number'==typeof n&&Gr(n)},xe.isFunction=fa,xe.isInteger=aa,xe.isLength=ca,xe.isMap=ha,xe.isMatch=function(n,t){return n===t||yu(n,t,ao(t))},xe.isMatchWith=function(n,r,e){return e='function'==typeof e?e:t,yu(n,r,ao(r),e)},xe.isNaN=function(n){return pa(n)&&n!=+n},xe.isNative=function(n){if(Oo(n))throw new Qn(e);return xu(n)},xe.isNil=function(n){return null==n},xe.isNull=function(n){return null===n},xe.isNumber=pa,xe.isObject=la,xe.isObjectLike=sa,xe.isPlainObject=va,xe.isRegExp=_a,xe.isSafeInteger=function(n){return aa(n)&&n>=-9007199254740991&&n<=L},xe.isSet=ga,xe.isString=da,xe.isSymbol=ya,xe.isTypedArray=xa,xe.isUndefined=function(n){return n===t},xe.isWeakMap=function(n){return sa(n)&&po(n)==un},xe.isWeakSet=function(n){return sa(n)&&fu(n)==on},xe.join=function(n,t){return null==n?'':Hr.call(n,t)},xe.kebabCase=nc,xe.last=of,xe.lastIndexOf=function(n,r,e){var u=null==n?0:n.length;if(!u)return-1;var i=u;return e!==t&&(i=(i=Aa(e))<0?Yr(u+i,0):Qr(i,u-1)),r==r?kr(n,r,i):Gt(n,Yt,i,!0)},xe.lowerCase=tc,xe.lowerFirst=rc,xe.lt=ba,xe.lte=wa,xe.max=function(n){return n&&n.length?Je(n,pc,au):t},xe.maxBy=function(n,r){return n&&n.length?Je(n,oo(r,2),au):t},xe.mean=function(n){return Qt(n,pc)},xe.meanBy=function(n,t){return Qt(n,oo(t,2))},xe.min=function(n){return n&&n.length?Je(n,pc,ju):t},xe.minBy=function(n,r){return n&&n.length?Je(n,oo(r,2),ju):t},xe.stubArray=kc,xe.stubFalse=zc,xe.stubObject=function(){return{}},xe.stubString=function(){return''},xe.stubTrue=function(){return!0},xe.multiply=Lc,xe.nth=function(n,r){return n&&n.length?Ru(n,Aa(r)):t},xe.noConflict=function(){return xt._===this&&(xt._=Zt),this},xe.noop=yc,xe.now=Uf,xe.pad=function(n,t,r){n=Ia(n);var e=(t=Aa(t))?zr(n):0;if(!t||e>=t)return n;var u=(t-e)/2;return Di(qr(u),r)+n+Di(Zr(u),r)},xe.padEnd=function(n,t,r){n=Ia(n);var e=(t=Aa(t))?zr(n):0;return t&&er){var u=n;n=r,r=u}if(e||n%1||r%1){var i=te();return Qr(n+i*(r-n+_t('1e-'+((i+'').length-1))),r)}return Uu(n,r)},xe.reduce=function(n,t,r){var e=na(n)?Ft:tr,u=arguments.length<3;return e(n,oo(t,4),r,u,Ve)},xe.reduceRight=function(n,t,r){var e=na(n)?Nt:tr,u=arguments.length<3;return e(n,oo(t,4),r,u,Ge)},xe.repeat=function(n,r,e){return r=(e?Ao(n,r,e):r===t)?1:Aa(r),$u(Ia(n),r)},xe.replace=function(){var n=arguments,t=Ia(n[0]);return n.length<3?t:t.replace(n[1],n[2])},xe.result=function(n,r,e){var u=-1,i=(r=ai(r,n)).length;for(i||(i=1,n=t);++uL)return[];var r=T,e=Qr(n,T);t=oo(t),n-=T;for(var u=ur(e,t);++r=o)return n;var a=e-zr(u);if(a<1)return u;var c=f?li(f,0,a).join(''):n.slice(0,a);if(i===t)return c+u;if(f&&(a+=c.length-a),_a(i)){if(n.slice(a).search(i)){var l,s=c;for(i.global||(i=rt(i.source,Ia(Nn.exec(i))+'g')),i.lastIndex=0;l=i.exec(s);)var h=l.index;c=c.slice(0,h===t?a:h)}}else if(n.indexOf(Qu(i),a)!=a){var p=c.lastIndexOf(i);p>-1&&(c=c.slice(0,p))}return c+u},xe.unescape=function(n){return(n=Ia(n))&&jn.test(n)?n.replace(wn,Ir):n},xe.uniqueId=function(n){var t=++bt;return Ia(n)+t},xe.upperCase=ic,xe.upperFirst=oc,xe.each=If,xe.eachRight=Rf,xe.first=tf,dc(xe,(Sc={},ru(xe,function(n,t){yt.call(xe.prototype,t)||(Sc[t]=n)}),Sc),{chain:!1}),xe.VERSION="4.17.13",Wt(['bind','bindKey','curry','curryRight','partial','partialRight'],function(n){xe[n].placeholder=xe}),Wt(['drop','take'],function(n,r){je.prototype[n]=function(e){e=e===t?1:Yr(Aa(e),0);var u=this.__filtered__&&!r?new je(this):this.clone();return u.__filtered__?u.__takeCount__=Qr(e,u.__takeCount__):u.__views__.push({size:Qr(e,T),type:n+(u.__dir__<0?'Right':'')}),u},je.prototype[n+'Right']=function(t){return this.reverse()[n](t).reverse()}}),Wt(['filter','map','takeWhile'],function(n,t){var r=t+1,e=r==R||3==r;je.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:oo(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),Wt(['head','last'],function(n,t){var r='take'+(t?'Right':'');je.prototype[n]=function(){return this[r](1).value()[0]}}),Wt(['initial','tail'],function(n,t){var r='drop'+(t?'':'Right');je.prototype[n]=function(){return this.__filtered__?new je(this):this[r](1)}}),je.prototype.compact=function(){return this.filter(pc)},je.prototype.find=function(n){return this.filter(n).head()},je.prototype.findLast=function(n){return this.reverse().find(n)},je.prototype.invokeMap=Du(function(n,t){return'function'==typeof n?new je(this):this.map(function(r){return vu(r,n,t)})}),je.prototype.reject=function(n){return this.filter(qf(oo(n)))},je.prototype.slice=function(n,r){n=Aa(n);var e=this;return e.__filtered__&&(n>0||r<0)?new je(e):(n<0?e=e.takeRight(-n):n&&(e=e.drop(n)),r!==t&&(e=(r=Aa(r))<0?e.dropRight(-r):e.take(r-n)),e)},je.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},je.prototype.toArray=function(){return this.take(T)},ru(je.prototype,function(n,r){var e=/^(?:filter|find|map|reject)|While$/.test(r),u=/^(?:head|last)$/.test(r),i=xe[u?'take'+('last'==r?'Right':''):r],o=u||/^find/.test(r);i&&(xe.prototype[r]=function(){var r=this.__wrapped__,f=u?[1]:arguments,a=r instanceof je,c=f[0],l=a||na(r),s=function(n){var t=i.apply(xe,Mt([n],f));return u&&h?t[0]:t};l&&e&&'function'==typeof c&&1!=c.length&&(a=l=!1);var h=this.__chain__,p=!!this.__actions__.length,v=o&&!h,_=a&&!p;if(!o&&l){r=_?r:new je(this);var g=n.apply(r,f);return g.__actions__.push({func:jf,args:[s],thisArg:t}),new me(g,h)}return v&&_?n.apply(this,f):(g=this.thru(s),v?u?g.value()[0]:g.value():g)})}),Wt(['pop','push','shift','sort','splice','unshift'],function(n){var t=ft[n],r=/^(?:push|sort|unshift)$/.test(n)?'tap':'thru',e=/^(?:pop|shift)$/.test(n);xe.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(na(u)?u:[],n)}return this[r](function(r){return t.apply(na(r)?r:[],n)})}}),ru(je.prototype,function(n,t){var r=xe[t];if(r){var e=r.name+'';yt.call(le,e)||(le[e]=[]),le[e].push({name:t,func:r})}}),le[Ti(t,g).name]=[{name:'wrapper',func:t}],je.prototype.clone=function(){var n=new je(this.__wrapped__);return n.__actions__=bi(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=bi(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=bi(this.__views__),n},je.prototype.reverse=function(){if(this.__filtered__){var n=new je(this);n.__dir__=-1,n.__filtered__=!0}else(n=this.clone()).__dir__*=-1;return n},je.prototype.value=function(){var n=this.__wrapped__.value(),t=this.__dir__,r=na(n),e=t<0,u=r?n.length:0,i=vo(0,u,this.__views__),o=i.start,f=i.end,a=f-o,c=e?f:o-1,l=this.__iteratees__,s=l.length,h=0,p=Qr(a,this.__takeCount__);if(!r||!e&&u==a&&p==a)return ei(n,this.__actions__);var v=[];n:for(;a--&&h=this.__values__.length;return{done:n,value:n?t:this.__values__[this.__index__++]}},xe.prototype.plant=function(n){for(var r,e=this;e instanceof we;){var u=Go(e);u.__index__=0,u.__values__=t,r?i.__wrapped__=u:r=u;var i=u;e=e.__wrapped__}return i.__wrapped__=n,r},xe.prototype.reverse=function(){var n=this.__wrapped__;if(n instanceof je){var r=n;return this.__actions__.length&&(r=new je(this)),(r=r.reverse()).__actions__.push({func:jf,args:[lf],thisArg:t}),new me(r,this.__chain__)}return this.thru(lf)},xe.prototype.toJSON=xe.prototype.valueOf=xe.prototype.value=function(){return ei(this.__wrapped__,this.__actions__)},xe.prototype.first=xe.prototype.head,$r&&(xe.prototype[$r]=function(){return this}),xe})();'function'==typeof define&&'object'==typeof define.amd&&define.amd?(xt._=Lr,define(function(){return Lr})):wt?((wt.exports=Lr)._=Lr,bt._=Lr):xt._=Lr}).call(this)},536,[]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);e.__esModule=!0;var u=t(r(d[1]));e.Provider=u.default;var c=t(r(d[2]));e.connectAdvanced=c.default;var s=r(d[3]);e.ReactReduxContext=s.ReactReduxContext;var l=t(r(d[4]));e.connect=l.default;var o=r(d[5]);e.useDispatch=o.useDispatch;var v=r(d[6]);e.useSelector=v.useSelector;var n=r(d[7]);e.useStore=n.useStore;var h=r(d[8]),_=r(d[9]);e.batch=_.unstable_batchedUpdates;var b=t(r(d[10]));e.shallowEqual=b.default,(0,h.setBatch)(_.unstable_batchedUpdates)},537,[3,538,546,543,553,567,570,568,545,571,554]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]),s=r(d[1]);e.__esModule=!0,e.default=void 0;var n=s(r(d[2])),o=s(r(d[3])),u=t(r(d[4])),c=s(r(d[5])),b=r(d[6]),p=s(r(d[7])),f=(function(t){function s(s){var o;o=t.call(this,s)||this;var u=s.store;o.notifySubscribers=o.notifySubscribers.bind((0,n.default)(o));var c=new p.default(u);return c.onStateChange=o.notifySubscribers,o.state={store:u,subscription:c},o.previousState=u.getState(),o}(0,o.default)(s,t);var c=s.prototype;return c.componentDidMount=function(){this._isMounted=!0,this.state.subscription.trySubscribe(),this.previousState!==this.props.store.getState()&&this.state.subscription.notifyNestedSubs()},c.componentWillUnmount=function(){this.unsubscribe&&this.unsubscribe(),this.state.subscription.tryUnsubscribe(),this._isMounted=!1},c.componentDidUpdate=function(t){if(this.props.store!==t.store){this.state.subscription.tryUnsubscribe();var s=new p.default(this.props.store);s.onStateChange=this.notifySubscribers,this.setState({store:this.props.store,subscription:s})}},c.notifySubscribers=function(){this.state.subscription.notifyNestedSubs()},c.render=function(){var t=this.props.context||b.ReactReduxContext;return u.default.createElement(t.Provider,{value:this.state},this.props.children)},s})(u.Component);f.propTypes={store:c.default.shape({subscribe:c.default.func.isRequired,dispatch:c.default.func.isRequired,getState:c.default.func.isRequired}),context:c.default.object,children:c.default.any};var h=f;e.default=h},538,[2,3,8,539,13,540,543,544]); -__d(function(g,r,i,a,m,e,d){m.exports=function(t,o){t.prototype=Object.create(o.prototype),t.prototype.constructor=t,t.__proto__=o}},539,[]); -__d(function(g,r,i,a,m,e,d){m.exports=r(d[0])()},540,[541]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);function t(){}function o(){}o.resetWarningCache=t,m.exports=function(){function p(t,o,p,c,s,y){if(y!==n){var f=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw f.name='Invariant Violation',f}}function c(){return p}p.isRequired=p;var s={array:p,bool:p,func:p,number:p,object:p,string:p,symbol:p,any:p,arrayOf:c,element:p,elementType:p,instanceOf:c,node:p,objectOf:c,oneOf:c,oneOfType:c,shape:c,exact:c,checkPropTypes:o,resetWarningCache:t};return s.PropTypes=s,s}},541,[542]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports='SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'},542,[]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);e.__esModule=!0,e.default=e.ReactReduxContext=void 0;var u=t(r(d[1])).default.createContext(null);e.ReactReduxContext=u;var l=u;e.default=l},543,[3,13]); -__d(function(g,r,i,a,m,e,d){"use strict";e.__esModule=!0,e.default=void 0;var t=r(d[0]),n=null,s={notify:function(){}};var u=(function(){function u(t,n){this.store=t,this.parentSub=n,this.unsubscribe=null,this.listeners=s,this.handleChangeWrapper=this.handleChangeWrapper.bind(this)}var h=u.prototype;return h.addNestedSub=function(t){return this.trySubscribe(),this.listeners.subscribe(t)},h.notifyNestedSubs=function(){this.listeners.notify()},h.handleChangeWrapper=function(){this.onStateChange&&this.onStateChange()},h.isSubscribed=function(){return Boolean(this.unsubscribe)},h.trySubscribe=function(){var s,u,h;this.unsubscribe||(this.unsubscribe=this.parentSub?this.parentSub.addNestedSub(this.handleChangeWrapper):this.store.subscribe(this.handleChangeWrapper),this.listeners=(s=(0,t.getBatch)(),u=[],h=[],{clear:function(){h=n,u=n},notify:function(){var t=u=h;s(function(){for(var n=0;n. You may also pass a {context : MyContext} option to connect");var L=j;return function(n){var v=n.displayName||n.name||'Component',x=N(v),M=(0,o.default)({},k,{getDisplayName:N,methodName:P,renderCountProp:E,shouldHandleStateChanges:T,storeKey:K,displayName:x,wrappedComponentName:v,WrappedComponent:n}),b=k.pure;function S(n){return t(n.dispatch,M)}var D=b?f.useMemo:function(t){return t()};function _(t){var c=(0,f.useMemo)(function(){var n=t.forwardedRef,o=(0,u.default)(t,["forwardedRef"]);return[t.context,n,o]},[t]),v=c[0],M=c[1],N=c[2],b=(0,f.useMemo)(function(){return v&&v.Consumer&&(0,l.isContextConsumer)(f.default.createElement(v.Consumer,null))?v:L},[v,L]),P=(0,f.useContext)(b),E=Boolean(t.store),_=Boolean(P)&&Boolean(P.store);(0,s.default)(E||_,"Could not find \"store\" in the context of \""+x+"\". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to "+x+" in connect options.");var K=t.store||P.store,A=(0,f.useMemo)(function(){return S(K)},[K]),B=(0,f.useMemo)(function(){if(!T)return C;var t=new p.default(K,E?null:P.subscription),n=t.notifyNestedSubs.bind(t);return[t,n]},[K,E,P]),H=B[0],W=B[1],U=(0,f.useMemo)(function(){return E?P:(0,o.default)({},P,{subscription:H})},[E,P,H]),j=(0,f.useReducer)(y,h,R),k=j[0],O=k[0],Y=j[1];if(O&&O.error)throw O.error;var q=(0,f.useRef)(),z=(0,f.useRef)(N),F=(0,f.useRef)(),G=(0,f.useRef)(!1),I=D(function(){return F.current&&N===z.current?F.current:A(K.getState(),N)},[K,O,N]);w(function(){z.current=N,q.current=I,G.current=!1,F.current&&(F.current=null,W())}),w(function(){if(T){var t=!1,n=null,o=function(){if(!t){var o,u,c=K.getState();try{o=A(c,z.current)}catch(t){u=t,n=t}u||(n=null),o===q.current?G.current||W():(q.current=o,F.current=o,G.current=!0,Y({type:'STORE_UPDATED',payload:{latestStoreState:c,error:u}}))}};H.onStateChange=o,H.trySubscribe(),o();return function(){if(t=!0,H.tryUnsubscribe(),n)throw n}}},[K,H,A]);var J=(0,f.useMemo)(function(){return f.default.createElement(n,(0,o.default)({},I,{ref:M}))},[M,n,I]),Q=(0,f.useMemo)(function(){return T?f.default.createElement(b.Provider,{value:U},J):J},[b,J,U]);return Q}var A=b?f.default.memo(_):_;if(A.WrappedComponent=n,A.displayName=x,W){var B=f.default.forwardRef(function(t,n){return f.default.createElement(A,(0,o.default)({},t,{forwardedRef:n}))});return B.displayName=x,B.WrappedComponent=n,(0,c.default)(B,n)}return(0,c.default)(A,n)}};var o=n(r(d[2])),u=n(r(d[3])),c=n(r(d[4])),s=n(r(d[5])),f=t(r(d[6])),l=r(d[7]),p=n(r(d[8])),v=r(d[9]),h=[],C=[null,null];function y(t,n){var o=t[1];return[n.payload,o+1]}var R=function(){return[null,0]},w='undefined'!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?f.useLayoutEffect:f.useEffect},546,[2,3,16,57,547,550,13,551,544,543]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},p={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},y={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},n={};function s(p){return t.isMemo(p)?y:n[p.$$typeof]||o}n[t.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0};var c=Object.defineProperty,f=Object.getOwnPropertyNames,l=Object.getOwnPropertySymbols,u=Object.getOwnPropertyDescriptor,O=Object.getPrototypeOf,P=Object.prototype;m.exports=function t(o,y,n){if('string'!=typeof y){if(P){var v=O(y);v&&v!==P&&t(o,v,n)}var b=f(y);l&&(b=b.concat(l(y)));for(var j=s(o),T=s(y),$=0;$=0;u--){var p=o[u](t);if(p)return p}return function(o,u){throw new Error("Invalid value of type "+typeof t+" for "+n+" argument when connecting component "+u.wrappedComponentName+".")}}function P(t,o){return t===o}function E(t){var E=void 0===t?{}:t,q=E.connectHOC,S=void 0===q?u.default:q,h=E.mapStateToPropsFactories,w=void 0===h?l.default:h,M=E.mapDispatchToPropsFactories,T=void 0===M?s.default:M,C=E.mergePropsFactories,y=void 0===C?c.default:C,D=E.selectorFactory,F=void 0===D?f.default:D;return function(t,u,s,l){void 0===l&&(l={});var c=l,f=c.pure,E=void 0===f||f,q=c.areStatesEqual,h=void 0===q?P:q,M=c.areOwnPropsEqual,C=void 0===M?p.default:M,D=c.areStatePropsEqual,O=void 0===D?p.default:D,_=c.areMergedPropsEqual,N=void 0===_?p.default:_,H=(0,n.default)(c,["pure","areStatesEqual","areOwnPropsEqual","areStatePropsEqual","areMergedPropsEqual"]),B=v(t,w,'mapStateToProps'),I=v(u,T,'mapDispatchToProps'),b=v(s,y,'mergeProps');return S(F,(0,o.default)({methodName:'connect',getDisplayName:function(t){return"Connect("+t+")"},shouldHandleStateChanges:Boolean(t),initMapStateToProps:B,initMapDispatchToProps:I,initMergeProps:b,pure:E,areStatesEqual:h,areOwnPropsEqual:C,areStatePropsEqual:O,areMergedPropsEqual:N},H))}}var q=E();e.default=q},553,[3,16,57,546,554,555,563,564,565]); -__d(function(g,r,i,a,m,e,d){"use strict";e.__esModule=!0,e.default=function(u,o){if(n(u,o))return!0;if('object'!=typeof u||null===u||'object'!=typeof o||null===o)return!1;var f=Object.keys(u),l=Object.keys(o);if(f.length!==l.length)return!1;for(var c=0;c'),t};var n=r(d[1]),u=t(r(d[2])),o=r(d[3])},569,[3,13,550,543]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);e.__esModule=!0,e.useSelector=function(t,l){void 0===l&&(l=f);(0,u.default)(t,"You must pass a selector to useSelectors");var h,v=(0,c.useReduxContext)(),w=v.store,y=v.subscription,b=(0,n.useReducer)(function(t){return t+1},0)[1],S=(0,n.useMemo)(function(){return new o.default(w,y)},[w,y]),R=(0,n.useRef)(),p=(0,n.useRef)(),_=(0,n.useRef)();try{h=t!==p.current||R.current?t(w.getState()):_.current}catch(t){var E="An error occured while selecting the store state: "+t.message+".";throw R.current&&(E+="\nThe error may be correlated with this previous error:\n"+R.current.stack+"\n\nOriginal stack trace:"),new Error(E)}return s(function(){p.current=t,_.current=h,R.current=void 0}),s(function(){function t(){try{var t=p.current(w.getState());if(l(t,_.current))return;_.current=t}catch(t){R.current=t}b({})}return S.onStateChange=t,S.trySubscribe(),t(),function(){return S.tryUnsubscribe()}},[w,S]),h};var n=r(d[1]),u=t(r(d[2])),c=r(d[3]),o=t(r(d[4])),s='undefined'!=typeof window?n.useLayoutEffect:n.useEffect,f=function(t,n){return t===n}},570,[3,13,550,569,544]); -__d(function(g,r,i,a,m,e,d){"use strict";e.__esModule=!0;var t=r(d[0]);e.unstable_batchedUpdates=t.unstable_batchedUpdates},571,[17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),l=t(r(d[2])),u=t(r(d[3])),f=r(d[4]),o=new((function(){function t(){(0,l.default)(this,t),this._elements=[]}return(0,u.default)(t,[{key:"applyMiddleware",value:function(){for(var t=arguments.length,l=new Array(t),u=0;u0&&void 0!==arguments[0]?arguments[0]:{};return(0,f.combineReducers)((0,n.default)({},this._elements,t))}},{key:"register",value:function(t,n){this._elements[t]=n}}]),t})());e.default=o},573,[3,54,4,5,556]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=t(r(d[1])),n=t(r(d[2])),o=t(r(d[3])),l=new((function(){function t(){(0,s.default)(this,t),this._selectorListeners=new Set}return(0,n.default)(t,[{key:"_listener",value:function(t){var s=t.prevSelections,n=t.store,l=this._selectorListeners,u=Array.isArray(l),c=0;for(l=u?l:l["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var f;if(u){if(c>=l.length)break;f=l[c++]}else{if((c=l.next()).done)break;f=c.value}var v=f,b=s.get(v);try{var y=v.selector(n.getState(),b);b!==y&&(s.set(v,y),v.listener(y,n,b))}catch(t){o.default.error(t)}}}},{key:"register",value:function(t,s){this._selectorListeners.add({listener:s,selector:t})}},{key:"subscribe",value:function(t){this._selectorListeners.size&&t.subscribe(this._listener.bind(this,{prevSelections:new Map,store:t}))}}]),t})());e.default=l},574,[3,4,5,575]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/redux');e.default=t},575,[576]); -__d(function(g,r,i,a,m,e,d){var o=r(d[0]),t=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.getLogger=function(o){var t='ReactNative'===navigator.product?f:v;return(0,n.getLogger)(o,void 0,t)},e._initLogging=void 0;var l=t(r(d[2])),n=o(r(d[3])),u=t(r(d[4])),v={},f={disableCallerInfo:!0};var s=l.default.once(function(){if('ReactNative'===navigator.product){var o=r(d[5]).default;n.default.setGlobalOptions(f),o.setGlobalLogOptions(f),n.default.removeGlobalTransport(console),o.removeGlobalLogTransport(console),n.default.addGlobalTransport(u.default),o.addGlobalLogTransport(u.default)}});e._initLogging=s},576,[2,3,536,577,580,389]); -__d(function(g,r,i,a,m,e,d){var o=r(d[0]),l=r(d[1]),t={},n=[],s=o.levels.TRACE;m.exports={addGlobalTransport:function(l){o.addGlobalTransport(l)},removeGlobalTransport:function(l){o.removeGlobalTransport(l)},setGlobalOptions:function(l){o.setGlobalOptions(l)},getLogger:function(l,v,f){var u=new o(s,l,v,f);return l?(t[l]=t[l]||[],t[l].push(u)):n.push(u),u},setLogLevelById:function(o,l){for(var s=l?t[l]||[]:n,v=0;v1&&O.push("<"+h.methodName+">: ");var N=O.concat(f);b.bind(v).apply(v,N)}}}function c(n,o,l,c){this.id=o,this.options=c||{},this.transports=l,this.transports||(this.transports=[]),this.level=t[n];for(var f=Object.keys(t),h=0;h=this.maxEntryLength&&this._flush(!0,!0)},s.prototype.start=function(){this._reschedulePublishInterval()},s.prototype._reschedulePublishInterval=function(){this.storeLogsIntervalID&&(window.clearTimeout(this.storeLogsIntervalID),this.storeLogsIntervalID=null),this.storeLogsIntervalID=window.setTimeout(this._flush.bind(this,!1,!0),this.storeInterval)},s.prototype.flush=function(){this._flush(!1,!0)},s.prototype._flush=function(t,s){this.totalLen>0&&(this.logStorage.isReady()||t)&&(this.logStorage.isReady()?(this.outputCache.length&&(this.outputCache.forEach(function(t){this.logStorage.storeLogs(t)}.bind(this)),this.outputCache=[]),this.logStorage.storeLogs(this.queue)):this.outputCache.push(this.queue),this.queue=[],this.totalLen=0),s&&this._reschedulePublishInterval()},s.prototype.stop=function(){this._flush(!1,!1)},m.exports=s},579,[578]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=n(r(d[1])),o=r(d[2]),u=r(d[3]),f=o.NativeModules.LogBridge;function c(n){var t,o=n.stack;return'function'==typeof n.cause&&(t=n.cause())&&(o+="\nCaused by: "+c(t)),o}var s=['trace','debug','info','log','warn','error'].reduce(function(n,o){return n[o]=function(){for(var n=arguments.length,s=new Array(n),v=0;v=l)return t;switch(t){case'%s':return String(c[u++]);case'%d':return Number(c[u++]);case'%j':try{return JSON.stringify(c[u++])}catch(t){return'[Circular]'}default:return t}}),f=c[u];u=3&&(o.depth=arguments[2]),arguments.length>=4&&(o.colors=arguments[3]),E(n)?o.showHidden=n:n&&e._extend(o,n),D(o.showHidden)&&(o.showHidden=!1),D(o.depth)&&(o.depth=2),D(o.colors)&&(o.colors=!1),D(o.customInspect)&&(o.customInspect=!0),o.colors&&(o.stylize=l),y(o,t,o.depth)}function l(t,n){var o=s.styles[n];return o?"\x1b["+s.colors[o][0]+'m'+t+"\x1b["+s.colors[o][1]+'m':t}function p(t,n){return t}function f(t){var n={};return t.forEach(function(t,o){n[t]=!0}),n}function y(t,n,o){if(t.customInspect&&n&&F(n.inspect)&&n.inspect!==e.inspect&&(!n.constructor||n.constructor.prototype!==n)){var u=n.inspect(o,t);return x(u)||(u=y(t,u,o)),u}var c=b(t,n);if(c)return c;var s=Object.keys(n),l=f(s);if(t.showHidden&&(s=Object.getOwnPropertyNames(n)),$(n)&&(s.indexOf('message')>=0||s.indexOf('description')>=0))return h(n);if(0===s.length){if(F(n)){var p=n.name?': '+n.name:'';return t.stylize('[Function'+p+']','special')}if(P(n))return t.stylize(RegExp.prototype.toString.call(n),'regexp');if(T(n))return t.stylize(Date.prototype.toString.call(n),'date');if($(n))return h(n)}var E,S='',z=!1,D=['{','}'];(w(n)&&(z=!0,D=['[',']']),F(n))&&(S=' [Function'+(n.name?': '+n.name:'')+']');return P(n)&&(S=' '+RegExp.prototype.toString.call(n)),T(n)&&(S=' '+Date.prototype.toUTCString.call(n)),$(n)&&(S=' '+h(n)),0!==s.length||z&&0!=n.length?o<0?P(n)?t.stylize(RegExp.prototype.toString.call(n),'regexp'):t.stylize('[Object]','special'):(t.seen.push(n),E=z?v(t,n,o,l,s):s.map(function(u){return O(t,n,o,l,u,z)}),t.seen.pop(),j(E,S,D)):D[0]+S+D[1]}function b(t,n){if(D(n))return t.stylize('undefined','undefined');if(x(n)){var o='\''+JSON.stringify(n).replace(/^"|"$/g,'').replace(/'/g,"\\'").replace(/\\"/g,'"')+'\'';return t.stylize(o,'string')}return z(n)?t.stylize(''+n,'number'):E(n)?t.stylize(''+n,'boolean'):S(n)?t.stylize('null','null'):void 0}function h(t){return'['+Error.prototype.toString.call(t)+']'}function v(t,n,o,u,c){for(var s=[],l=0,p=n.length;l-1&&(p=s?p.split('\n').map(function(t){return' '+t}).join('\n').substr(2):'\n'+p.split('\n').map(function(t){return' '+t}).join('\n')):p=t.stylize('[Circular]','special')),D(l)){if(s&&c.match(/^\d+$/))return p;(l=JSON.stringify(''+c)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(l=l.substr(1,l.length-2),l=t.stylize(l,'name')):(l=l.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),l=t.stylize(l,'string'))}return l+': '+p}function j(t,n,o){return t.reduce(function(t,n){return 0,n.indexOf('\n')>=0&&0,t+n.replace(/\u001b\[\d\d?m/g,'').length+1},0)>60?o[0]+(''===n?'':n+'\n ')+' '+t.join(',\n ')+' '+o[1]:o[0]+n+' '+t.join(', ')+' '+o[1]}function w(t){return Array.isArray(t)}function E(t){return'boolean'==typeof t}function S(t){return null===t}function z(t){return'number'==typeof t}function x(t){return'string'==typeof t}function D(t){return void 0===t}function P(t){return N(t)&&'[object RegExp]'===_(t)}function N(t){return'object'==typeof t&&null!==t}function T(t){return N(t)&&'[object Date]'===_(t)}function $(t){return N(t)&&('[object Error]'===_(t)||t instanceof Error)}function F(t){return'function'==typeof t}function _(t){return Object.prototype.toString.call(t)}function k(t){return t<10?'0'+t.toString(10):t.toString(10)}e.debuglog=function(t){if(t=t.toUpperCase(),!o[t])if(u.test(t)){var n=process.pid;o[t]=function(){var o=e.format.apply(e,arguments);console.error('%s %d: %s',t,n,o)}}else o[t]=function(){};return o[t]},e.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:'cyan',number:'yellow',boolean:'yellow',undefined:'grey',null:'bold',string:'green',date:'magenta',regexp:'red'},e.types=r(d[0]),e.isArray=w,e.isBoolean=E,e.isNull=S,e.isNullOrUndefined=function(t){return null==t},e.isNumber=z,e.isString=x,e.isSymbol=function(t){return'symbol'==typeof t},e.isUndefined=D,e.isRegExp=P,e.types.isRegExp=P,e.isObject=N,e.isDate=T,e.types.isDate=T,e.isError=$,e.types.isNativeError=$,e.isFunction=F,e.isPrimitive=function(t){return null===t||'boolean'==typeof t||'number'==typeof t||'string'==typeof t||'symbol'==typeof t||void 0===t},e.isBuffer=r(d[1]);var A=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];function R(t,n){return Object.prototype.hasOwnProperty.call(t,n)}e.log=function(){var t,n;console.log('%s - %s',(t=new Date,n=[k(t.getHours()),k(t.getMinutes()),k(t.getSeconds())].join(':'),[t.getDate(),A[t.getMonth()],n].join(' ')),e.format.apply(e,arguments))},e.inherits=r(d[2]),e._extend=function(t,n){if(!n||!N(n))return t;for(var o=Object.keys(n),u=o.length;u--;)t[o[u]]=n[o[u]];return t};var U='undefined'!=typeof Symbol?Symbol('util.promisify.custom'):void 0;function J(t,n){if(!t){var o=new Error('Promise was rejected with a falsy value');o.reason=t,t=o}return n(t)}e.promisify=function(n){if('function'!=typeof n)throw new TypeError('The "original" argument must be of type Function');if(U&&n[U]){var o;if('function'!=typeof(o=n[U]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(o,U,{value:o,enumerable:!1,writable:!1,configurable:!0}),o}function o(){for(var t,o,u=new Promise(function(n,u){t=n,o=u}),c=[],s=0;s=0&&'[object Array]'!==o.call(t)&&'[object Function]'===o.call(t.callee)},l=(function(){return n(arguments)})();n.isLegacyArguments=c,m.exports=l?n:c},584,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=Object.prototype.toString,o=Function.prototype.toString,n=/^\s*(?:function)?\*/,c='function'==typeof Symbol&&'symbol'==typeof("function"==typeof Symbol?Symbol.toStringTag:"@@toStringTag"),u=Object.getPrototypeOf,f=(function(){if(!c)return!1;try{return Function('return function*() {}')()}catch(t){}})(),y=f?u(f):{};m.exports=function(f){return'function'==typeof f&&(!!n.test(o.call(f))||(c?u(f)===y:'[object GeneratorFunction]'===t.call(f)))}},585,[]); -__d(function(g,r,i,a,m,e,d){'function'==typeof Object.create?m.exports=function(t,o){t.super_=o,t.prototype=Object.create(o.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}})}:m.exports=function(t,o){t.super_=o;var p=function(){};p.prototype=o.prototype,t.prototype=new p,t.prototype.constructor=t}},586,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})});var c=r(d[4]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}})}),r(d[5]),r(d[6])},587,[588,602,604,603,737,738,739]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.clearNotifications=function(){return{type:u.CLEAR_NOTIFICATIONS}},e.hideNotification=function(t){return{type:u.HIDE_NOTIFICATION,uid:t}},e.setNotificationsEnabled=function(t){return{type:u.SET_NOTIFICATIONS_ENABLED,enabled:t}},e.showErrorNotification=function(t){return f((0,n.default)({},t,{appearance:c.NOTIFICATION_TYPE.ERROR}))},e.showNotification=f,e.showWarningNotification=function(t){return f((0,n.default)({},t,{appearance:c.NOTIFICATION_TYPE.WARNING}))},e.showParticipantJoinedNotification=function(t){return I.push(t),function(t){return N(t)}};var n=t(r(d[1])),o=t(r(d[2])),u=r(d[3]),c=r(d[4]);function f(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1?arguments[1]:void 0;return{type:u.SHOW_NOTIFICATION,props:t,timeout:n,uid:window.Date.now()}}var I=[],N=(0,o.default)(function(t){var n,o=I.length;o>=3?n={titleArguments:{name:I[0],count:o-1},titleKey:'notify.connectedThreePlusMembers'}:2===o?n={titleArguments:{first:I[0],second:I[1]},titleKey:'notify.connectedTwoMembers'}:o&&(n={titleArguments:{name:I[0]},titleKey:'notify.connectedOneMember'}),n&&t(f(n,c.NOTIFICATION_TIMEOUT)),I=[]},500,{leading:!1})},588,[3,54,589,602,603]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o='Expected a function';m.exports=function(f,l,c){var u=!0,p=!0;if('function'!=typeof f)throw new TypeError(o);return t(c)&&(u='leading'in c?!!c.leading:u,p='trailing'in c?!!c.trailing:p),n(f,l,{leading:u,maxWait:l,trailing:p})}},589,[590,591]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]),u='Expected a function',f=Math.max,c=Math.min;m.exports=function(v,l,s){var T,p,h,x,y,w,E=0,M=!1,W=!1,_=!0;if('function'!=typeof v)throw new TypeError(u);function b(n){var t=T,o=p;return T=p=void 0,E=n,x=v.apply(o,t)}function j(n){return E=n,y=setTimeout(z,l),M?b(n):x}function k(n){var t=l-(n-w);return W?c(t,h-(n-E)):t}function q(n){var t=n-w;return void 0===w||t>=l||t<0||W&&n-E>=h}function z(){var n=t();if(q(n))return A(n);y=setTimeout(z,k(n))}function A(n){return y=void 0,_&&T?b(n):(T=p=void 0,x)}function B(){var n=t(),o=q(n);if(T=arguments,p=this,w=n,o){if(void 0===y)return j(w);if(W)return clearTimeout(y),y=setTimeout(z,l),b(w)}return void 0===y&&(y=setTimeout(z,l)),x}return l=o(l)||0,n(s)&&(M=!!s.leading,h=(W='maxWait'in s)?f(o(s.maxWait)||0,l):h,_='trailing'in s?!!s.trailing:_),B.cancel=function(){void 0!==y&&clearTimeout(y),E=0,T=w=p=y=void 0},B.flush=function(){return void 0===y?x:A(t())},B}},590,[591,592,595]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){var t=typeof n;return null!=n&&('object'==t||'function'==t)}},591,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);m.exports=function(){return n.Date.now()}},592,[593]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),f='object'==typeof self&&self&&self.Object===Object&&self,s=t||f||Function('return this')();m.exports=s},593,[594]); -__d(function(g,r,i,a,m,e,d){var t='object'==typeof g&&g&&g.Object===Object&&g;m.exports=t},594,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),f=r(d[1]),n=NaN,u=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,p=/^0o[0-7]+$/i,c=parseInt;m.exports=function(v){if('number'==typeof v)return v;if(f(v))return n;if(t(v)){var l='function'==typeof v.valueOf?v.valueOf():v;v=t(l)?l+'':l}if('string'!=typeof v)return 0===v?v:+v;v=v.replace(u,'');var $=o.test(v);return $||p.test(v)?c(v.slice(2),$?2:8):s.test(v)?n:+v}},595,[591,596]); -__d(function(g,r,i,a,m,e,d){var o=r(d[0]),t=r(d[1]),n='[object Symbol]';m.exports=function(b){return'symbol'==typeof b||t(b)&&o(b)==n}},596,[597,601]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]),c='[object Null]',u='[object Undefined]',f=n?"function"==typeof n?n.toStringTag:"@@toStringTag":void 0;m.exports=function(n){return null==n?void 0===n?u:c:f&&f in Object(n)?t(n):o(n)}},597,[598,599,600]); -__d(function(g,r,i,a,m,e,d){var o=r(d[0]).Symbol;m.exports=o},598,[593]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),o=Object.prototype,n=o.hasOwnProperty,c=o.toString,l=t?"function"==typeof t?t.toStringTag:"@@toStringTag":void 0;m.exports=function(t){var o=n.call(t,l),p=t[l];try{t[l]=void 0}catch(t){}var v=c.call(t);return o?t[l]=p:delete t[l],v}},599,[598]); -__d(function(g,r,i,a,m,e,d){var t=Object.prototype.toString;m.exports=function(n){return t.call(n)}},600,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){return null!=n&&'object'==typeof n}},601,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SET_NOTIFICATIONS_ENABLED=e.SHOW_NOTIFICATION=e.HIDE_NOTIFICATION=e.CLEAR_NOTIFICATIONS=void 0;e.CLEAR_NOTIFICATIONS='CLEAR_NOTIFICATIONS';e.HIDE_NOTIFICATION='HIDE_NOTIFICATION';e.SHOW_NOTIFICATION='SHOW_NOTIFICATION';e.SET_NOTIFICATIONS_ENABLED='SET_NOTIFICATIONS_ENABLED'},602,[]); -__d(function(g,r,i,a,m,e,d){var I=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.NOTIFICATION_TYPE_PRIORITIES=e.NOTIFICATION_TYPE=e.NOTIFICATION_TIMEOUT=void 0;var O,T=I(r(d[1]));e.NOTIFICATION_TIMEOUT=2500;var N={ERROR:'error',INFO:'info',NORMAL:'normal',SUCCESS:'success',WARNING:'warning'};e.NOTIFICATION_TYPE=N;var R=(O={},(0,T.default)(O,N.ERROR,5),(0,T.default)(O,N.INFO,3),(0,T.default)(O,N.NORMAL,3),(0,T.default)(O,N.SUCCESS,3),(0,T.default)(O,N.WARNING,4),O);e.NOTIFICATION_TYPE_PRIORITIES=R},603,[3,55]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})},604,[605]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"Notification",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"NotificationsContainer",{enumerable:!0,get:function(){return o.default}});var n=t(r(d[1])),o=t(r(d[2]))},605,[3,606,735]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),l=t(r(d[2])),u=t(r(d[3])),s=t(r(d[4])),o=t(r(d[5])),f=t(r(d[6])),c=r(d[7]),y=r(d[8]),p=r(d[9]),h=t(r(d[10])),v=t(r(d[11])),E=(function(t){function y(){return(0,n.default)(this,y),(0,u.default)(this,(0,s.default)(y).apply(this,arguments))}return(0,o.default)(y,t),(0,l.default)(y,[{key:"render",value:function(){var t=this.props.isDismissAllowed;return f.default.createElement(c.View,{pointerEvents:"box-none",style:v.default.notification},f.default.createElement(c.View,{style:v.default.contentColumn},f.default.createElement(c.View,{pointerEvents:"box-none",style:v.default.notificationContent},this._renderContent())),t&&f.default.createElement(c.TouchableOpacity,{onPress:this._onDismissed},f.default.createElement(p.Icon,{src:p.IconClose,style:v.default.dismissIcon})))}},{key:"_renderContent",value:function(){var t=this.props,n=t.t,l=t.title,u=t.titleArguments,s=t.titleKey,o=l||s&&n(s,u),y=this._getDescription();return y&&y.length?y.map(function(t,n){return f.default.createElement(c.Text,{key:n,numberOfLines:1,style:v.default.contentText},t)}):f.default.createElement(c.Text,{numberOfLines:1,style:v.default.contentText},o)}}]),y})(h.default),_=(0,y.translate)(E);e.default=_},606,[3,4,5,6,9,10,13,17,607,403,733,734]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var n={i18next:!0,DEFAULT_LANGUAGE:!0,LANGUAGES:!0};Object.defineProperty(e,"i18next",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(e,"DEFAULT_LANGUAGE",{enumerable:!0,get:function(){return c.DEFAULT_LANGUAGE}}),Object.defineProperty(e,"LANGUAGES",{enumerable:!0,get:function(){return c.LANGUAGES}});var o=r(d[1]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}}))});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}}))});var c=t(r(d[3]))},607,[2,608,703,610]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.getLocalizedDateFormatter=function(t){return(0,n.default)(t).locale(o())},e.getLocalizedDurationFormatter=function(t){if('0'!==n.default.duration(t).format('h'))return n.default.duration(t).format('h:mm:ss');return n.default.duration(t).format('mm:ss',{trim:!1})};var n=t(r(d[1])),u=t(r(d[2]));function o(){var t,o=u.default.language;if(o){var f=new RegExp('^([a-z]{2,2})(-)*([a-z]{2,2})*$').exec(o.toLowerCase());if(f){var l=new RegExp("^"+f[1]+"(-)*"+("("+f[3]+")*"||''));t=n.default.locales().find(function(t){return l.exec(t)})}}return t||'en'}r(d[3]),r(d[4]),r(d[5]),r(d[6]),r(d[7]),r(d[8]),r(d[9]),r(d[10]),r(d[11]),r(d[12]),r(d[13]),r(d[14]),r(d[15]),r(d[16]),r(d[17]),r(d[18]),r(d[19]),r(d[20])},608,[3,609,610,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702]); -__d(function(g,r,i,a,m,e,d){var t,n;t=this,n=function(){'use strict';var t,n;function s(){return t.apply(null,arguments)}function o(t){return t instanceof Array||'[object Array]'===Object.prototype.toString.call(t)}function u(t){return null!=t&&'[object Object]'===Object.prototype.toString.call(t)}function l(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var n;for(n in t)if(t.hasOwnProperty(n))return!1;return!0}function h(t){return void 0===t}function c(t){return'number'==typeof t||'[object Number]'===Object.prototype.toString.call(t)}function f(t){return t instanceof Date||'[object Date]'===Object.prototype.toString.call(t)}function _(t,n){var s,o=[];for(s=0;s>>0,o=0;o0)for(s=0;s=0?s?'+':'':'-')+Math.pow(10,Math.max(0,u)).toString().substr(1)+o}var $=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,J=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,q={},B={};function Q(t,n,s,o){var u=o;'string'==typeof o&&(u=function(){return this[o]()}),t&&(B[t]=u),n&&(B[n[0]]=function(){return Z(u.apply(this,arguments),n[1],n[2])}),s&&(B[s]=function(){return this.localeData().ordinal(u.apply(this,arguments),t)})}function X(t){var n,s,o,u=t.match($);for(n=0,s=u.length;n=0&&J.test(t);)t=t.replace(J,o),J.lastIndex=0,s-=1;return t}var te=/\d/,ne=/\d\d/,se=/\d{3}/,ie=/\d{4}/,re=/[+-]?\d{6}/,ae=/\d\d?/,oe=/\d\d\d\d?/,ue=/\d\d\d\d\d\d?/,le=/\d{1,3}/,de=/\d{1,4}/,he=/[+-]?\d{1,6}/,ce=/\d+/,fe=/[+-]?\d+/,me=/Z|[+-]\d\d:?\d\d/gi,_e=/Z|[+-]\d\d(?::?\d\d)?/gi,ye=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ge={};function ve(t,n,s){ge[t]=N(n)?n:function(t,o){return t&&s?s:n}}function pe(t,n){return y(ge,t)?ge[t](n._strict,n._locale):new RegExp(we(t.replace('\\','').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,n,s,o,u){return n||s||o||u})))}function we(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,'\\$&')}var Me={};function ke(t,n){var s,o=n;for('string'==typeof t&&(t=[t]),c(n)&&(o=function(t,s){s[n]=b(t)}),s=0;s68?1900:2e3)};var Ne,He=Le('FullYear',!0);function Le(t,n){return function(o){return null!=o?(Ve(this,t,o),s.updateOffset(this,n),this):Ge(this,t)}}function Ge(t,n){return t.isValid()?t._d['get'+(t._isUTC?'UTC':'')+n]():NaN}function Ve(t,n,s){t.isValid()&&!isNaN(s)&&('FullYear'===n&&Ue(t.year())&&1===t.month()&&29===t.date()?t._d['set'+(t._isUTC?'UTC':'')+n](s,t.month(),je(s,t.month())):t._d['set'+(t._isUTC?'UTC':'')+n](s))}function je(t,n){if(isNaN(t)||isNaN(n))return NaN;var s,o=(n%(s=12)+s)%s;return t+=(n-o)/12,1===o?Ue(t)?29:28:31-o%7%2}Ne=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var n;for(n=0;n=0&&isFinite(c.getFullYear())&&c.setFullYear(t),c}function Xe(t){var n=new Date(Date.UTC.apply(null,arguments));return t<100&&t>=0&&isFinite(n.getUTCFullYear())&&n.setUTCFullYear(t),n}function Ke(t,n,s){var o=7+n-s;return-((7+Xe(t,0,o).getUTCDay()-n)%7)+o-1}function et(t,n,s,o,u){var l,h,c=1+7*(n-1)+(7+s-o)%7+Ke(t,o,u);return c<=0?h=Fe(l=t-1)+c:c>Fe(t)?(l=t+1,h=c-Fe(t)):(l=t,h=c),{year:l,dayOfYear:h}}function tt(t,n,s){var o,u,l=Ke(t.year(),n,s),h=Math.floor((t.dayOfYear()-l-1)/7)+1;return h<1?o=h+nt(u=t.year()-1,n,s):h>nt(t.year(),n,s)?(o=h-nt(t.year(),n,s),u=t.year()+1):(u=t.year(),o=h),{week:o,year:u}}function nt(t,n,s){var o=Ke(t,n,s),u=Ke(t+1,n,s);return(Fe(t)-o+u)/7}Q('w',['ww',2],'wo','week'),Q('W',['WW',2],'Wo','isoWeek'),V('week','w'),V('isoWeek','W'),A('week',5),A('isoWeek',5),ve('w',ae),ve('ww',ae,ne),ve('W',ae),ve('WW',ae,ne),Se(['w','ww','W','WW'],function(t,n,s,o){n[o.substr(0,1)]=b(t)});function st(t,n){return'string'!=typeof t?t:isNaN(t)?'number'==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10)}function it(t,n){return'string'==typeof t?n.weekdaysParse(t)%7||7:isNaN(t)?null:t}Q('d',0,'do','day'),Q('dd',0,0,function(t){return this.localeData().weekdaysMin(this,t)}),Q('ddd',0,0,function(t){return this.localeData().weekdaysShort(this,t)}),Q('dddd',0,0,function(t){return this.localeData().weekdays(this,t)}),Q('e',0,0,'weekday'),Q('E',0,0,'isoWeekday'),V('day','d'),V('weekday','e'),V('isoWeekday','E'),A('day',11),A('weekday',11),A('isoWeekday',11),ve('d',ae),ve('e',ae),ve('E',ae),ve('dd',function(t,n){return n.weekdaysMinRegex(t)}),ve('ddd',function(t,n){return n.weekdaysShortRegex(t)}),ve('dddd',function(t,n){return n.weekdaysRegex(t)}),Se(['dd','ddd','dddd'],function(t,n,s,o){var u=s._locale.weekdaysParse(t,o,s._strict);null!=u?n.d=u:w(s).invalidWeekday=t}),Se(['d','e','E'],function(t,n,s,o){n[o]=b(t)});var rt='Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');var at='Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');var ot='Su_Mo_Tu_We_Th_Fr_Sa'.split('_');function ut(t,n,s){var o,u,l,h=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],o=0;o<7;++o)l=p([2e3,1]).day(o),this._minWeekdaysParse[o]=this.weekdaysMin(l,'').toLocaleLowerCase(),this._shortWeekdaysParse[o]=this.weekdaysShort(l,'').toLocaleLowerCase(),this._weekdaysParse[o]=this.weekdays(l,'').toLocaleLowerCase();return s?'dddd'===n?-1!==(u=Ne.call(this._weekdaysParse,h))?u:null:'ddd'===n?-1!==(u=Ne.call(this._shortWeekdaysParse,h))?u:null:-1!==(u=Ne.call(this._minWeekdaysParse,h))?u:null:'dddd'===n?-1!==(u=Ne.call(this._weekdaysParse,h))?u:-1!==(u=Ne.call(this._shortWeekdaysParse,h))?u:-1!==(u=Ne.call(this._minWeekdaysParse,h))?u:null:'ddd'===n?-1!==(u=Ne.call(this._shortWeekdaysParse,h))?u:-1!==(u=Ne.call(this._weekdaysParse,h))?u:-1!==(u=Ne.call(this._minWeekdaysParse,h))?u:null:-1!==(u=Ne.call(this._minWeekdaysParse,h))?u:-1!==(u=Ne.call(this._weekdaysParse,h))?u:-1!==(u=Ne.call(this._shortWeekdaysParse,h))?u:null}var lt=ye;var dt=ye;var ht=ye;function ct(){function t(t,n){return n.length-t.length}var n,s,o,u,l,h=[],c=[],f=[],_=[];for(n=0;n<7;n++)s=p([2e3,1]).day(n),o=this.weekdaysMin(s,''),u=this.weekdaysShort(s,''),l=this.weekdays(s,''),h.push(o),c.push(u),f.push(l),_.push(o),_.push(u),_.push(l);for(h.sort(t),c.sort(t),f.sort(t),_.sort(t),n=0;n<7;n++)c[n]=we(c[n]),f[n]=we(f[n]),_[n]=we(_[n]);this._weekdaysRegex=new RegExp('^('+_.join('|')+')','i'),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp('^('+f.join('|')+')','i'),this._weekdaysShortStrictRegex=new RegExp('^('+c.join('|')+')','i'),this._weekdaysMinStrictRegex=new RegExp('^('+h.join('|')+')','i')}function ft(){return this.hours()%12||12}function mt(t,n){Q(t,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),n)})}function _t(t,n){return n._meridiemParse}Q('H',['HH',2],0,'hour'),Q('h',['hh',2],0,ft),Q('k',['kk',2],0,function(){return this.hours()||24}),Q('hmm',0,0,function(){return''+ft.apply(this)+Z(this.minutes(),2)}),Q('hmmss',0,0,function(){return''+ft.apply(this)+Z(this.minutes(),2)+Z(this.seconds(),2)}),Q('Hmm',0,0,function(){return''+this.hours()+Z(this.minutes(),2)}),Q('Hmmss',0,0,function(){return''+this.hours()+Z(this.minutes(),2)+Z(this.seconds(),2)}),mt('a',!0),mt('A',!1),V('hour','h'),A('hour',13),ve('a',_t),ve('A',_t),ve('H',ae),ve('h',ae),ve('k',ae),ve('HH',ae,ne),ve('hh',ae,ne),ve('kk',ae,ne),ve('hmm',oe),ve('hmmss',ue),ve('Hmm',oe),ve('Hmmss',ue),ke(['H','HH'],xe),ke(['k','kk'],function(t,n,s){var o=b(t);n[xe]=24===o?0:o}),ke(['a','A'],function(t,n,s){s._isPm=s._locale.isPM(t),s._meridiem=t}),ke(['h','hh'],function(t,n,s){n[xe]=b(t),w(s).bigHour=!0}),ke('hmm',function(t,n,s){var o=t.length-2;n[xe]=b(t.substr(0,o)),n[be]=b(t.substr(o)),w(s).bigHour=!0}),ke('hmmss',function(t,n,s){var o=t.length-4,u=t.length-2;n[xe]=b(t.substr(0,o)),n[be]=b(t.substr(o,2)),n[Pe]=b(t.substr(u)),w(s).bigHour=!0}),ke('Hmm',function(t,n,s){var o=t.length-2;n[xe]=b(t.substr(0,o)),n[be]=b(t.substr(o))}),ke('Hmmss',function(t,n,s){var o=t.length-4,u=t.length-2;n[xe]=b(t.substr(0,o)),n[be]=b(t.substr(o,2)),n[Pe]=b(t.substr(u))});var yt,gt=Le('Hours',!0),vt={calendar:{sameDay:'[Today at] LT',nextDay:'[Tomorrow at] LT',nextWeek:'dddd [at] LT',lastDay:'[Yesterday at] LT',lastWeek:'[Last] dddd [at] LT',sameElse:'L'},longDateFormat:{LTS:'h:mm:ss A',LT:'h:mm A',L:'MM/DD/YYYY',LL:'MMMM D, YYYY',LLL:'MMMM D, YYYY h:mm A',LLLL:'dddd, MMMM D, YYYY h:mm A'},invalidDate:'Invalid date',ordinal:'%d',dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:'in %s',past:'%s ago',s:'a few seconds',ss:'%d seconds',m:'a minute',mm:'%d minutes',h:'an hour',hh:'%d hours',d:'a day',dd:'%d days',M:'a month',MM:'%d months',y:'a year',yy:'%d years'},months:Ee,monthsShort:Ae,week:{dow:0,doy:6},weekdays:rt,weekdaysMin:ot,weekdaysShort:at,meridiemParse:/[ap]\.?m?\.?/i},pt={},wt={};function Mt(t){return t?t.toLowerCase().replace('_','-'):t}function kt(t){for(var n,s,o,u,l=0;l0;){if(o=St(u.slice(0,n).join('-')))return o;if(s&&s.length>=n&&P(u,s,!0)>=n-1)break;n--}l++}return null}function St(t){var n=null;if(!pt[t]&&void 0!==m&&m&&m.exports)try{n=yt._abbr,r('./locale/'+t),Dt(n)}catch(t){}return pt[t]}function Dt(t,n){var s;return t&&(s=h(n)?Ot(t):Yt(t,n))&&(yt=s),yt._abbr}function Yt(t,n){if(null!==n){var s=vt;if(n.abbr=t,null!=pt[t])U('defineLocaleOverride',"use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=pt[t]._config;else if(null!=n.parentLocale){if(null==pt[n.parentLocale])return wt[n.parentLocale]||(wt[n.parentLocale]=[]),wt[n.parentLocale].push({name:t,config:n}),null;s=pt[n.parentLocale]._config}return pt[t]=new L(H(s,n)),wt[t]&&wt[t].forEach(function(t){Yt(t.name,t.config)}),Dt(t),pt[t]}return delete pt[t],null}function Ot(t){var n;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return yt;if(!o(t)){if(n=St(t))return n;t=[t]}return kt(t)}function Tt(t){var n,s=t._a;return s&&-2===w(t).overflow&&(n=s[Oe]<0||s[Oe]>11?Oe:s[Te]<1||s[Te]>je(s[Ye],s[Oe])?Te:s[xe]<0||s[xe]>24||24===s[xe]&&(0!==s[be]||0!==s[Pe]||0!==s[We])?xe:s[be]<0||s[be]>59?be:s[Pe]<0||s[Pe]>59?Pe:s[We]<0||s[We]>999?We:-1,w(t)._overflowDayOfYear&&(nTe)&&(n=Te),w(t)._overflowWeeks&&-1===n&&(n=Re),w(t)._overflowWeekday&&-1===n&&(n=Ce),w(t).overflow=n),t}function xt(t,n,s){return null!=t?t:null!=n?n:s}function bt(t){var n=new Date(s.now());return t._useUTC?[n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate()]:[n.getFullYear(),n.getMonth(),n.getDate()]}function Pt(t){var n,s,o,u,l,h=[];if(!t._d){for(o=bt(t),t._w&&null==t._a[Te]&&null==t._a[Oe]&&Wt(t),null!=t._dayOfYear&&(l=xt(t._a[Ye],o[Ye]),(t._dayOfYear>Fe(l)||0===t._dayOfYear)&&(w(t)._overflowDayOfYear=!0),s=Xe(l,0,t._dayOfYear),t._a[Oe]=s.getUTCMonth(),t._a[Te]=s.getUTCDate()),n=0;n<3&&null==t._a[n];++n)t._a[n]=h[n]=o[n];for(;n<7;n++)t._a[n]=h[n]=null==t._a[n]?2===n?1:0:t._a[n];24===t._a[xe]&&0===t._a[be]&&0===t._a[Pe]&&0===t._a[We]&&(t._nextDay=!0,t._a[xe]=0),t._d=(t._useUTC?Xe:Qe).apply(null,h),u=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[xe]=24),t._w&&void 0!==t._w.d&&t._w.d!==u&&(w(t).weekdayMismatch=!0)}}function Wt(t){var n,s,o,u,l,h,c,f;if(null!=(n=t._w).GG||null!=n.W||null!=n.E)l=1,h=4,s=xt(n.GG,t._a[Ye],tt(Kt(),1,4).year),o=xt(n.W,1),((u=xt(n.E,1))<1||u>7)&&(f=!0);else{l=t._locale._week.dow,h=t._locale._week.doy;var _=tt(Kt(),l,h);s=xt(n.gg,t._a[Ye],_.year),o=xt(n.w,_.week),null!=n.d?((u=n.d)<0||u>6)&&(f=!0):null!=n.e?(u=n.e+l,(n.e<0||n.e>6)&&(f=!0)):u=l}o<1||o>nt(s,l,h)?w(t)._overflowWeeks=!0:null!=f?w(t)._overflowWeekday=!0:(c=et(s,o,u,l,h),t._a[Ye]=c.year,t._dayOfYear=c.dayOfYear)}var Rt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ct=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ft=/Z|[+-]\d\d(?::?\d\d)?/,Ut=[['YYYYYY-MM-DD',/[+-]\d{6}-\d\d-\d\d/],['YYYY-MM-DD',/\d{4}-\d\d-\d\d/],['GGGG-[W]WW-E',/\d{4}-W\d\d-\d/],['GGGG-[W]WW',/\d{4}-W\d\d/,!1],['YYYY-DDD',/\d{4}-\d{3}/],['YYYY-MM',/\d{4}-\d\d/,!1],['YYYYYYMMDD',/[+-]\d{10}/],['YYYYMMDD',/\d{8}/],['GGGG[W]WWE',/\d{4}W\d{3}/],['GGGG[W]WW',/\d{4}W\d{2}/,!1],['YYYYDDD',/\d{7}/]],Nt=[['HH:mm:ss.SSSS',/\d\d:\d\d:\d\d\.\d+/],['HH:mm:ss,SSSS',/\d\d:\d\d:\d\d,\d+/],['HH:mm:ss',/\d\d:\d\d:\d\d/],['HH:mm',/\d\d:\d\d/],['HHmmss.SSSS',/\d\d\d\d\d\d\.\d+/],['HHmmss,SSSS',/\d\d\d\d\d\d,\d+/],['HHmmss',/\d\d\d\d\d\d/],['HHmm',/\d\d\d\d/],['HH',/\d\d/]],Ht=/^\/?Date\((\-?\d+)/i;function Lt(t){var n,s,o,u,l,h,c=t._i,f=Rt.exec(c)||Ct.exec(c);if(f){for(w(t).iso=!0,n=0,s=Ut.length;n0&&w(t).unusedInput.push(h),c=c.slice(c.indexOf(o)+o.length),_+=o.length),B[l]?(o?w(t).empty=!1:w(t).unusedTokens.push(l),De(l,o,t)):t._strict&&!o&&w(t).unusedTokens.push(l);w(t).charsLeftOver=f-_,c.length>0&&w(t).unusedInput.push(c),t._a[xe]<=12&&!0===w(t).bigHour&&t._a[xe]>0&&(w(t).bigHour=void 0),w(t).parsedDateParts=t._a.slice(0),w(t).meridiem=t._meridiem,t._a[xe]=$t(t._locale,t._a[xe],t._meridiem),Pt(t),Tt(t)}else At(t);else Lt(t)}function $t(t,n,s){var o;return null==s?n:null!=t.meridiemHour?t.meridiemHour(n,s):null!=t.isPM?((o=t.isPM(s))&&n<12&&(n+=12),o||12!==n||(n=0),n):n}function Jt(t){var n,s,o,u,l;if(0===t._f.length)return w(t).invalidFormat=!0,void(t._d=new Date(NaN));for(u=0;uthis?this:t:k()});function nn(t,n){var s,u;if(1===n.length&&o(n[0])&&(n=n[0]),!n.length)return Kt();for(s=n[0],u=1;u(l=nt(t,o,u))&&(n=l),Wn.call(this,t,n,s,o,u))}function Wn(t,n,s,o,u){var l=et(t,n,s,o,u),h=Xe(l.year,0,l.dayOfYear);return this.year(h.getUTCFullYear()),this.month(h.getUTCMonth()),this.date(h.getUTCDate()),this}Q(0,['gg',2],0,function(){return this.weekYear()%100}),Q(0,['GG',2],0,function(){return this.isoWeekYear()%100}),bn('gggg','weekYear'),bn('ggggg','weekYear'),bn('GGGG','isoWeekYear'),bn('GGGGG','isoWeekYear'),V('weekYear','gg'),V('isoWeekYear','GG'),A('weekYear',1),A('isoWeekYear',1),ve('G',fe),ve('g',fe),ve('GG',ae,ne),ve('gg',ae,ne),ve('GGGG',de,ie),ve('gggg',de,ie),ve('GGGGG',he,re),ve('ggggg',he,re),Se(['gggg','ggggg','GGGG','GGGGG'],function(t,n,s,o){n[o.substr(0,2)]=b(t)}),Se(['gg','GG'],function(t,n,o,u){n[u]=s.parseTwoDigitYear(t)}),Q('Q',0,'Qo','quarter'),V('quarter','Q'),A('quarter',7),ve('Q',te),ke('Q',function(t,n){n[Oe]=3*(b(t)-1)}),Q('D',['DD',2],'Do','date'),V('date','D'),A('date',9),ve('D',ae),ve('DD',ae,ne),ve('Do',function(t,n){return t?n._dayOfMonthOrdinalParse||n._ordinalParse:n._dayOfMonthOrdinalParseLenient}),ke(['D','DD'],Te),ke('Do',function(t,n){n[Te]=b(t.match(ae)[0])});var Rn=Le('Date',!0);Q('DDD',['DDDD',3],'DDDo','dayOfYear'),V('dayOfYear','DDD'),A('dayOfYear',4),ve('DDD',le),ve('DDDD',se),ke(['DDD','DDDD'],function(t,n,s){s._dayOfYear=b(t)}),Q('m',['mm',2],0,'minute'),V('minute','m'),A('minute',14),ve('m',ae),ve('mm',ae,ne),ke(['m','mm'],be);var Cn=Le('Minutes',!1);Q('s',['ss',2],0,'second'),V('second','s'),A('second',15),ve('s',ae),ve('ss',ae,ne),ke(['s','ss'],Pe);var Fn,Un=Le('Seconds',!1);for(Q('S',0,0,function(){return~~(this.millisecond()/100)}),Q(0,['SS',2],0,function(){return~~(this.millisecond()/10)}),Q(0,['SSS',3],0,'millisecond'),Q(0,['SSSS',4],0,function(){return 10*this.millisecond()}),Q(0,['SSSSS',5],0,function(){return 100*this.millisecond()}),Q(0,['SSSSSS',6],0,function(){return 1e3*this.millisecond()}),Q(0,['SSSSSSS',7],0,function(){return 1e4*this.millisecond()}),Q(0,['SSSSSSSS',8],0,function(){return 1e5*this.millisecond()}),Q(0,['SSSSSSSSS',9],0,function(){return 1e6*this.millisecond()}),V('millisecond','ms'),A('millisecond',16),ve('S',le,te),ve('SS',le,ne),ve('SSS',le,se),Fn='SSSS';Fn.length<=9;Fn+='S')ve(Fn,ce);function Nn(t,n){n[We]=b(1e3*('0.'+t))}for(Fn='S';Fn.length<=9;Fn+='S')ke(Fn,Nn);var Hn=Le('Milliseconds',!1);Q('z',0,0,'zoneAbbr'),Q('zz',0,0,'zoneName');var Ln=O.prototype;function Gn(t){return t}Ln.add=Sn,Ln.calendar=function(t,n){var o=t||Kt(),u=cn(o,this).startOf('day'),l=s.calendarFormat(this,u)||'sameElse',h=n&&(N(n[l])?n[l].call(this,o):n[l]);return this.format(h||this.localeData().calendar(l,this,Kt(o)))},Ln.clone=function(){return new O(this)},Ln.diff=function(t,n,s){var o,u,l;if(!this.isValid())return NaN;if(!(o=cn(t,this)).isValid())return NaN;switch(u=6e4*(o.utcOffset()-this.utcOffset()),n=j(n)){case'year':l=Yn(this,o)/12;break;case'month':l=Yn(this,o);break;case'quarter':l=Yn(this,o)/3;break;case'second':l=(this-o)/1e3;break;case'minute':l=(this-o)/6e4;break;case'hour':l=(this-o)/36e5;break;case'day':l=(this-o-u)/864e5;break;case'week':l=(this-o-u)/6048e5;break;default:l=this-o}return s?l:x(l)},Ln.endOf=function(t){return void 0===(t=j(t))||'millisecond'===t?this:('date'===t&&(t='day'),this.startOf(t).add(1,'isoWeek'===t?'week':t).subtract(1,'ms'))},Ln.format=function(t){t||(t=this.isUtc()?s.defaultFormatUtc:s.defaultFormat);var n=K(this,t);return this.localeData().postformat(n)},Ln.from=function(t,n){return this.isValid()&&(T(t)&&t.isValid()||Kt(t).isValid())?gn({to:this,from:t}).locale(this.locale()).humanize(!n):this.localeData().invalidDate()},Ln.fromNow=function(t){return this.from(Kt(),t)},Ln.to=function(t,n){return this.isValid()&&(T(t)&&t.isValid()||Kt(t).isValid())?gn({from:this,to:t}).locale(this.locale()).humanize(!n):this.localeData().invalidDate()},Ln.toNow=function(t){return this.to(Kt(),t)},Ln.get=function(t){return N(this[t=j(t)])?this[t]():this},Ln.invalidAt=function(){return w(this).overflow},Ln.isAfter=function(t,n){var s=T(t)?t:Kt(t);return!(!this.isValid()||!s.isValid())&&('millisecond'===(n=j(h(n)?'millisecond':n))?this.valueOf()>s.valueOf():s.valueOf()9999?K(t,'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'):N(Date.prototype.toISOString)?this.toDate().toISOString():K(t,'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]')},Ln.inspect=function(){if(!this.isValid())return'moment.invalid(/* '+this._i+' */)';var t='moment',n='';this.isLocal()||(t=0===this.utcOffset()?'moment.utc':'moment.parseZone',n='Z');var s='['+t+'("]',o=0<=this.year()&&this.year()<=9999?'YYYY':'YYYYYY',u=n+'[")]';return this.format(s+o+'-MM-DD[T]HH:mm:ss.SSS'+u)},Ln.toJSON=function(){return this.isValid()?this.toISOString():null},Ln.toString=function(){return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ')},Ln.unix=function(){return Math.floor(this.valueOf()/1e3)},Ln.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Ln.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Ln.year=He,Ln.isLeapYear=function(){return Ue(this.year())},Ln.weekYear=function(t){return Pn.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Ln.isoWeekYear=function(t){return Pn.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Ln.quarter=Ln.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Ln.month=$e,Ln.daysInMonth=function(){return je(this.year(),this.month())},Ln.week=Ln.weeks=function(t){var n=this.localeData().week(this);return null==t?n:this.add(7*(t-n),'d')},Ln.isoWeek=Ln.isoWeeks=function(t){var n=tt(this,1,4).week;return null==t?n:this.add(7*(t-n),'d')},Ln.weeksInYear=function(){var t=this.localeData()._week;return nt(this.year(),t.dow,t.doy)},Ln.isoWeeksInYear=function(){return nt(this.year(),1,4)},Ln.date=Rn,Ln.day=Ln.days=function(t){if(!this.isValid())return null!=t?this:NaN;var n=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=st(t,this.localeData()),this.add(t-n,'d')):n},Ln.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var n=(this.day()+7-this.localeData()._week.dow)%7;return null==t?n:this.add(t-n,'d')},Ln.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var n=it(t,this.localeData());return this.day(this.day()%7?n:n-7)}return this.day()||7},Ln.dayOfYear=function(t){var n=Math.round((this.clone().startOf('day')-this.clone().startOf('year'))/864e5)+1;return null==t?n:this.add(t-n,'d')},Ln.hour=Ln.hours=gt,Ln.minute=Ln.minutes=Cn,Ln.second=Ln.seconds=Un,Ln.millisecond=Ln.milliseconds=Hn,Ln.utcOffset=function(t,n,o){var u,l=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if('string'==typeof t){if(null===(t=hn(_e,t)))return this}else Math.abs(t)<16&&!o&&(t*=60);return!this._isUTC&&n&&(u=fn(this)),this._offset=t,this._isUTC=!0,null!=u&&this.add(u,'m'),l!==t&&(!n||this._changeInProgress?kn(this,gn(t-l,'m'),1,!1):this._changeInProgress||(this._changeInProgress=!0,s.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?l:fn(this)},Ln.utc=function(t){return this.utcOffset(0,t)},Ln.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(fn(this),'m')),this},Ln.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if('string'==typeof this._i){var t=hn(me,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Ln.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Kt(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Ln.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Ln.isLocal=function(){return!!this.isValid()&&!this._isUTC},Ln.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Ln.isUtc=mn,Ln.isUTC=mn,Ln.zoneAbbr=function(){return this._isUTC?'UTC':''},Ln.zoneName=function(){return this._isUTC?'Coordinated Universal Time':''},Ln.dates=R('dates accessor is deprecated. Use date instead.',Rn),Ln.months=R('months accessor is deprecated. Use month instead',$e),Ln.years=R('years accessor is deprecated. Use year instead',He),Ln.zone=R('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',function(t,n){return null!=t?('string'!=typeof t&&(t=-t),this.utcOffset(t,n),this):-this.utcOffset()}),Ln.isDSTShifted=R('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',function(){if(!h(this._isDSTShifted))return this._isDSTShifted;var t={};if(D(t,this),(t=Bt(t))._a){var n=t._isUTC?p(t._a):Kt(t._a);this._isDSTShifted=this.isValid()&&P(t._a,n.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted});var Vn=L.prototype;function jn(t,n,s,o){var u=Ot(),l=p().set(o,n);return u[s](l,t)}function In(t,n,s){if(c(t)&&(n=t,t=void 0),t=t||'',null!=n)return jn(t,n,s,'month');var o,u=[];for(o=0;o<12;o++)u[o]=jn(t,o,s,'month');return u}function En(t,n,s,o){'boolean'==typeof t?(c(n)&&(s=n,n=void 0),n=n||''):(s=n=t,t=!1,c(n)&&(s=n,n=void 0),n=n||'');var u,l=Ot(),h=t?l._week.dow:0;if(null!=s)return jn(n,(s+h)%7,o,'day');var f=[];for(u=0;u<7;u++)f[u]=jn(n,(u+h)%7,o,'day');return f}Vn.calendar=function(t,n,s){var o=this._calendar[t]||this._calendar.sameElse;return N(o)?o.call(n,s):o},Vn.longDateFormat=function(t){var n=this._longDateFormat[t],s=this._longDateFormat[t.toUpperCase()];return n||!s?n:(this._longDateFormat[t]=s.replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t])},Vn.invalidDate=function(){return this._invalidDate},Vn.ordinal=function(t){return this._ordinal.replace('%d',t)},Vn.preparse=Gn,Vn.postformat=Gn,Vn.relativeTime=function(t,n,s,o){var u=this._relativeTime[s];return N(u)?u(t,n,s,o):u.replace(/%d/i,t)},Vn.pastFuture=function(t,n){var s=this._relativeTime[t>0?'future':'past'];return N(s)?s(n):s.replace(/%s/i,n)},Vn.set=function(t){var n,s;for(s in t)N(n=t[s])?this[s]=n:this['_'+s]=n;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+'|'+/\d{1,2}/.source)},Vn.months=function(t,n){return t?o(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Ie).test(n)?'format':'standalone'][t.month()]:o(this._months)?this._months:this._months.standalone},Vn.monthsShort=function(t,n){return t?o(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Ie.test(n)?'format':'standalone'][t.month()]:o(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Vn.monthsParse=function(t,n,s){var o,u,l;if(this._monthsParseExact)return ze.call(this,t,n,s);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),o=0;o<12;o++){if(u=p([2e3,o]),s&&!this._longMonthsParse[o]&&(this._longMonthsParse[o]=new RegExp('^'+this.months(u,'').replace('.','')+'$','i'),this._shortMonthsParse[o]=new RegExp('^'+this.monthsShort(u,'').replace('.','')+'$','i')),s||this._monthsParse[o]||(l='^'+this.months(u,'')+'|^'+this.monthsShort(u,''),this._monthsParse[o]=new RegExp(l.replace('.',''),'i')),s&&'MMMM'===n&&this._longMonthsParse[o].test(t))return o;if(s&&'MMM'===n&&this._shortMonthsParse[o].test(t))return o;if(!s&&this._monthsParse[o].test(t))return o}},Vn.monthsRegex=function(t){return this._monthsParseExact?(y(this,'_monthsRegex')||Be.call(this),t?this._monthsStrictRegex:this._monthsRegex):(y(this,'_monthsRegex')||(this._monthsRegex=qe),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Vn.monthsShortRegex=function(t){return this._monthsParseExact?(y(this,'_monthsRegex')||Be.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(y(this,'_monthsShortRegex')||(this._monthsShortRegex=Je),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Vn.week=function(t){return tt(t,this._week.dow,this._week.doy).week},Vn.firstDayOfYear=function(){return this._week.doy},Vn.firstDayOfWeek=function(){return this._week.dow},Vn.weekdays=function(t,n){return t?o(this._weekdays)?this._weekdays[t.day()]:this._weekdays[this._weekdays.isFormat.test(n)?'format':'standalone'][t.day()]:o(this._weekdays)?this._weekdays:this._weekdays.standalone},Vn.weekdaysMin=function(t){return t?this._weekdaysMin[t.day()]:this._weekdaysMin},Vn.weekdaysShort=function(t){return t?this._weekdaysShort[t.day()]:this._weekdaysShort},Vn.weekdaysParse=function(t,n,s){var o,u,l;if(this._weekdaysParseExact)return ut.call(this,t,n,s);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),o=0;o<7;o++){if(u=p([2e3,1]).day(o),s&&!this._fullWeekdaysParse[o]&&(this._fullWeekdaysParse[o]=new RegExp('^'+this.weekdays(u,'').replace('.','.?')+'$','i'),this._shortWeekdaysParse[o]=new RegExp('^'+this.weekdaysShort(u,'').replace('.','.?')+'$','i'),this._minWeekdaysParse[o]=new RegExp('^'+this.weekdaysMin(u,'').replace('.','.?')+'$','i')),this._weekdaysParse[o]||(l='^'+this.weekdays(u,'')+'|^'+this.weekdaysShort(u,'')+'|^'+this.weekdaysMin(u,''),this._weekdaysParse[o]=new RegExp(l.replace('.',''),'i')),s&&'dddd'===n&&this._fullWeekdaysParse[o].test(t))return o;if(s&&'ddd'===n&&this._shortWeekdaysParse[o].test(t))return o;if(s&&'dd'===n&&this._minWeekdaysParse[o].test(t))return o;if(!s&&this._weekdaysParse[o].test(t))return o}},Vn.weekdaysRegex=function(t){return this._weekdaysParseExact?(y(this,'_weekdaysRegex')||ct.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(y(this,'_weekdaysRegex')||(this._weekdaysRegex=lt),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Vn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(y(this,'_weekdaysRegex')||ct.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(y(this,'_weekdaysShortRegex')||(this._weekdaysShortRegex=dt),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Vn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(y(this,'_weekdaysRegex')||ct.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(y(this,'_weekdaysMinRegex')||(this._weekdaysMinRegex=ht),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Vn.isPM=function(t){return'p'===(t+'').toLowerCase().charAt(0)},Vn.meridiem=function(t,n,s){return t>11?s?'pm':'PM':s?'am':'AM'},Dt('en',{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var n=t%10;return t+(1===b(t%100/10)?'th':1===n?'st':2===n?'nd':3===n?'rd':'th')}}),s.lang=R('moment.lang is deprecated. Use moment.locale instead.',Dt),s.langData=R('moment.langData is deprecated. Use moment.localeData instead.',Ot);var An=Math.abs;function zn(t,n,s,o){var u=gn(n,s);return t._milliseconds+=o*u._milliseconds,t._days+=o*u._days,t._months+=o*u._months,t._bubble()}function Zn(t){return t<0?Math.floor(t):Math.ceil(t)}function $n(t){return 4800*t/146097}function Jn(t){return 146097*t/4800}function qn(t){return function(){return this.as(t)}}var Bn=qn('ms'),Qn=qn('s'),Xn=qn('m'),Kn=qn('h'),es=qn('d'),ts=qn('w'),ns=qn('M'),ss=qn('y');function is(t){return function(){return this.isValid()?this._data[t]:NaN}}var rs=is('milliseconds'),as=is('seconds'),os=is('minutes'),us=is('hours'),ls=is('days'),ds=is('months'),hs=is('years');var cs=Math.round,fs={ss:44,s:45,m:45,h:22,d:26,M:11};function ms(t,n,s,o,u){return u.relativeTime(n||1,!!s,t,o)}function _s(t,n,s){var o=gn(t).abs(),u=cs(o.as('s')),l=cs(o.as('m')),h=cs(o.as('h')),c=cs(o.as('d')),f=cs(o.as('M')),_=cs(o.as('y')),y=u<=fs.ss&&['s',u]||u0,y[4]=s,ms.apply(null,y)}var ys=Math.abs;function gs(t){return(t>0)-(t<0)||+t}function vs(){if(!this.isValid())return this.localeData().invalidDate();var t,n,s=ys(this._milliseconds)/1e3,o=ys(this._days),u=ys(this._months);n=x((t=x(s/60))/60),s%=60,t%=60;var l=x(u/12),h=u%=12,c=o,f=n,_=t,y=s?s.toFixed(3).replace(/\.?0+$/,''):'',v=this.asSeconds();if(!v)return'P0D';var p=v<0?'-':'',w=gs(this._months)!==gs(v)?'-':'',M=gs(this._days)!==gs(v)?'-':'',k=gs(this._milliseconds)!==gs(v)?'-':'';return p+'P'+(l?w+l+'Y':'')+(h?w+h+'M':'')+(c?M+c+'D':'')+(f||_||y?'T':'')+(f?k+f+'H':'')+(_?k+_+'M':'')+(y?k+y+'S':'')}var ps=an.prototype;return ps.isValid=function(){return this._isValid},ps.abs=function(){var t=this._data;return this._milliseconds=An(this._milliseconds),this._days=An(this._days),this._months=An(this._months),t.milliseconds=An(t.milliseconds),t.seconds=An(t.seconds),t.minutes=An(t.minutes),t.hours=An(t.hours),t.months=An(t.months),t.years=An(t.years),this},ps.add=function(t,n){return zn(this,t,n,1)},ps.subtract=function(t,n){return zn(this,t,n,-1)},ps.as=function(t){if(!this.isValid())return NaN;var n,s,o=this._milliseconds;if('month'===(t=j(t))||'year'===t)return n=this._days+o/864e5,s=this._months+$n(n),'month'===t?s:s/12;switch(n=this._days+Math.round(Jn(this._months)),t){case'week':return n/7+o/6048e5;case'day':return n+o/864e5;case'hour':return 24*n+o/36e5;case'minute':return 1440*n+o/6e4;case'second':return 86400*n+o/1e3;case'millisecond':return Math.floor(864e5*n)+o;default:throw new Error('Unknown unit '+t)}},ps.asMilliseconds=Bn,ps.asSeconds=Qn,ps.asMinutes=Xn,ps.asHours=Kn,ps.asDays=es,ps.asWeeks=ts,ps.asMonths=ns,ps.asYears=ss,ps.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*b(this._months/12):NaN},ps._bubble=function(){var t,n,s,o,u,l=this._milliseconds,h=this._days,c=this._months,f=this._data;return l>=0&&h>=0&&c>=0||l<=0&&h<=0&&c<=0||(l+=864e5*Zn(Jn(c)+h),h=0,c=0),f.milliseconds=l%1e3,t=x(l/1e3),f.seconds=t%60,n=x(t/60),f.minutes=n%60,s=x(n/60),f.hours=s%24,c+=u=x($n(h+=x(s/24))),h-=Zn(Jn(u)),o=x(c/12),c%=12,f.days=h,f.months=c,f.years=o,this},ps.clone=function(){return gn(this)},ps.get=function(t){return t=j(t),this.isValid()?this[t+'s']():NaN},ps.milliseconds=rs,ps.seconds=as,ps.minutes=os,ps.hours=us,ps.days=ls,ps.weeks=function(){return x(this.days()/7)},ps.months=ds,ps.years=hs,ps.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var n=this.localeData(),s=_s(this,!t,n);return t&&(s=n.pastFuture(+this,s)),n.postformat(s)},ps.toISOString=vs,ps.toString=vs,ps.toJSON=vs,ps.locale=On,ps.localeData=xn,ps.toIsoString=R('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',vs),ps.lang=Tn,Q('X',0,0,'unix'),Q('x',0,0,'valueOf'),ve('x',fe),ve('X',/[+-]?\d+(\.\d{1,3})?/),ke('X',function(t,n,s){s._d=new Date(1e3*parseFloat(t,10))}),ke('x',function(t,n,s){s._d=new Date(b(t))}),s.version='2.19.4',t=Kt,s.fn=Ln,s.min=function(){return nn('isBefore',[].slice.call(arguments,0))},s.max=function(){return nn('isAfter',[].slice.call(arguments,0))},s.now=function(){return Date.now?Date.now():+new Date},s.utc=p,s.unix=function(t){return Kt(1e3*t)},s.months=function(t,n){return In(t,n,'months')},s.isDate=f,s.locale=Dt,s.invalid=k,s.duration=gn,s.isMoment=T,s.weekdays=function(t,n,s){return En(t,n,s,'weekdays')},s.parseZone=function(){return Kt.apply(null,arguments).parseZone()},s.localeData=Ot,s.isDuration=on,s.monthsShort=function(t,n){return In(t,n,'monthsShort')},s.weekdaysMin=function(t,n,s){return En(t,n,s,'weekdaysMin')},s.defineLocale=Yt,s.updateLocale=function(t,n){if(null!=n){var s,o,u=vt;null!=(o=St(t))&&(u=o._config),(s=new L(n=H(u,n))).parentLocale=pt[t],pt[t]=s,Dt(t)}else null!=pt[t]&&(null!=pt[t].parentLocale?pt[t]=pt[t].parentLocale:null!=pt[t]&&delete pt[t]);return pt[t]},s.locales=function(){return C(pt)},s.weekdaysShort=function(t,n,s){return En(t,n,s,'weekdaysShort')},s.normalizeUnits=j,s.relativeTimeRounding=function(t){return void 0===t?cs:'function'==typeof t&&(cs=t,!0)},s.relativeTimeThreshold=function(t,n){return void 0!==fs[t]&&(void 0===n?fs[t]:(fs[t]=n,'s'===t&&(fs.ss=n-1),!0))},s.calendarFormat=function(t,n){var s=t.diff(n,'days',!0);return s<-6?'sameElse':s<-1?'lastWeek':s<0?'lastDay':s<1?'sameDay':s<2?'nextDay':s<7?'nextWeek':'sameElse'},s.prototype=Ln,s},'object'==typeof e&&void 0!==m?m.exports=n():'function'==typeof define&&define.amd?define(n):t.moment=n()},609,[]); -__d(function(g,r,i,a,m,e,d){var u=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.DEFAULT_LANGUAGE=e.LANGUAGES=void 0;var l=u(r(d[1])),t=u(r(d[2])),n=u(r(d[3])),s=u(r(d[4])),f=u(r(d[5])),o=u(r(d[6])),c=Object.keys(s.default);e.LANGUAGES=c;var A=c[0];e.DEFAULT_LANGUAGE=A;var v={backend:{loadPath:'lang/{{ns}}-{{lng}}.json'},defaultNS:'main',fallbackLng:A,interpolation:{escapeValue:!1},load:'languageOnly',ns:['main','languages','countries'],react:{useSuspense:!1},returnEmptyString:!1,returnNull:!1,whitelist:c.slice()};l.default.use('ReactNative'===navigator.product?{}:t.default).use(o.default).init(v),l.default.addResourceBundle(A,'countries',n.default,!0,!0),l.default.addResourceBundle(A,'languages',s.default,!0,!0),l.default.addResourceBundle(A,'main',f.default,!0,!0),r(d[7]);var G=l.default;e.default=G},610,[3,611,630,634,635,636,637,638]); -__d(function(g,r,i,a,m,e,d){'use strict';function t(t){return t&&'object'==typeof t&&'default'in t?t.default:t}var n=t(r(d[0])),o=t(r(d[1])),s=t(r(d[2])),u=t(r(d[3])),l=t(r(d[4])),c=t(r(d[5])),f=t(r(d[6])),p=t(r(d[7])),h=t(r(d[8])),v=t(r(d[9])),y={type:'logger',log:function(t){this.output('log',t)},warn:function(t){this.output('warn',t)},error:function(t){this.output('error',t)},output:function(t,n){var o;console&&console[t]&&(o=console)[t].apply(o,h(n))}},b=new((function(){function t(n){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};s(this,t),this.init(n,o)}return u(t,[{key:"init",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=n.prefix||'i18next:',this.logger=t||y,this.options=n,this.debug=n.debug}},{key:"setDebug",value:function(t){this.debug=t}},{key:"log",value:function(){for(var t=arguments.length,n=new Array(t),o=0;o-1&&o.observers[t].splice(s,1)}else delete o.observers[t]})}},{key:"emit",value:function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),s=1;s-1?t.replace(/###/g,'.'):t}function u(){return!t||'string'==typeof t}for(var l='string'!=typeof n?[].concat(n):n.split('.');l.length>1;){if(u())return{};var c=s(l.shift());!t[c]&&o&&(t[c]=new o),t=t[c]}return u()?{}:{obj:t,k:s(l.shift())}}function R(t,n,o){var s=w(t,n,Object);s.obj[s.k]=o}function O(t,n){var o=w(t,n),s=o.obj,u=o.k;if(s)return s[u]}function L(t,n,o){for(var s in n)s in t?'string'==typeof t[s]||t[s]instanceof String||'string'==typeof n[s]||n[s]instanceof String?o&&(t[s]=n[s]):L(t[s],n[s],o):t[s]=n[s];return t}function N(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,'\\$&')}var C={'&':'&','<':'<','>':'>','"':'"',"'":''','/':'/'};function j(t){return'string'==typeof t?t.replace(/[&<>"'\/]/g,function(t){return C[t]}):t}var E=(function(t){function n(t){var o,u=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{ns:['translation'],defaultNS:'translation'};return s(this,n),o=l(this,c(n).call(this)),k.call(f(o)),o.data=t||{},o.options=u,void 0===o.options.keySeparator&&(o.options.keySeparator='.'),o}return p(n,k),u(n,[{key:"addNamespaces",value:function(t){this.options.ns.indexOf(t)<0&&this.options.ns.push(t)}},{key:"removeNamespaces",value:function(t){var n=this.options.ns.indexOf(t);n>-1&&this.options.ns.splice(n,1)}},{key:"getResource",value:function(t,n,o){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},u=void 0!==s.keySeparator?s.keySeparator:this.options.keySeparator,l=[t,n];return o&&'string'!=typeof o&&(l=l.concat(o)),o&&'string'==typeof o&&(l=l.concat(u?o.split(u):o)),t.indexOf('.')>-1&&(l=t.split('.')),O(this.data,l)}},{key:"addResource",value:function(t,n,o,s){var u=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1},l=this.options.keySeparator;void 0===l&&(l='.');var c=[t,n];o&&(c=c.concat(l?o.split(l):o)),t.indexOf('.')>-1&&(s=n,n=(c=t.split('.'))[1]),this.addNamespaces(n),R(this.data,c,s),u.silent||this.emit('added',t,n,o,s)}},{key:"addResources",value:function(t,n,o){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(var u in o)'string'!=typeof o[u]&&'[object Array]'!==Object.prototype.toString.apply(o[u])||this.addResource(t,n,u,o[u],{silent:!0});s.silent||this.emit('added',t,n,o)}},{key:"addResourceBundle",value:function(t,n,s,u,l){var c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1},f=[t,n];t.indexOf('.')>-1&&(u=s,s=n,n=(f=t.split('.'))[1]),this.addNamespaces(n);var p=O(this.data,f)||{};u?L(p,s,l):p=o({},p,s),R(this.data,f,p),c.silent||this.emit('added',t,n,s)}},{key:"removeResourceBundle",value:function(t,n){this.hasResourceBundle(t,n)&&delete this.data[t][n],this.removeNamespaces(n),this.emit('removed',t,n)}},{key:"hasResourceBundle",value:function(t,n){return void 0!==this.getResource(t,n)}},{key:"getResourceBundle",value:function(t,n){return n||(n=this.options.defaultNS),'v1'===this.options.compatibilityAPI?o({},{},this.getResource(t,n)):this.getResource(t,n)}},{key:"getDataByLanguage",value:function(t){return this.data[t]}},{key:"toJSON",value:function(){return this.data}}]),n})(),P={processors:{},addPostProcessor:function(t){this.processors[t.name]=t},handle:function(t,n,o,s,u){var l=this;return t.forEach(function(t){l.processors[t]&&(n=l.processors[t].process(n,o,s,u))}),n}},F=(function(t){function h(t){var n,o,u,p,v=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return s(this,h),n=l(this,c(h).call(this)),k.call(f(n)),o=['resourceStore','languageUtils','pluralResolver','interpolator','backendConnector','i18nFormat'],u=t,p=f(n),o.forEach(function(t){u[t]&&(p[t]=u[t])}),n.options=v,void 0===n.options.keySeparator&&(n.options.keySeparator='.'),n.logger=b.create('translator'),n}return p(h,k),u(h,[{key:"changeLanguage",value:function(t){t&&(this.language=t)}},{key:"exists",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}},o=this.resolve(t,n);return o&&void 0!==o.res}},{key:"extractFromKey",value:function(t,n){var o=n.nsSeparator||this.options.nsSeparator;void 0===o&&(o=':');var s=void 0!==n.keySeparator?n.keySeparator:this.options.keySeparator,u=n.ns||this.options.defaultNS;if(o&&t.indexOf(o)>-1){var l=t.split(o);(o!==s||o===s&&this.options.ns.indexOf(l[0])>-1)&&(u=l.shift()),t=l.join(s)}return'string'==typeof u&&(u=[u]),{key:t,namespaces:u}}},{key:"translate",value:function(t,s){var u=this;if('object'!==n(s)&&this.options.overloadTranslationOptionHandler&&(s=this.options.overloadTranslationOptionHandler(arguments)),s||(s={}),void 0===t||null===t)return'';Array.isArray(t)||(t=[String(t)]);var l=void 0!==s.keySeparator?s.keySeparator:this.options.keySeparator,c=this.extractFromKey(t[t.length-1],s),f=c.key,p=c.namespaces,h=p[p.length-1],v=s.lng||this.language,y=s.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(v&&'cimode'===v.toLowerCase()){if(y){var b=s.nsSeparator||this.options.nsSeparator;return h+b+f}return f}var k=this.resolve(t,s),x=k&&k.res,S=k&&k.usedKey||f,w=k&&k.exactUsedKey||f,R=Object.prototype.toString.apply(x),O=void 0!==s.joinArrays?s.joinArrays:this.options.joinArrays,L=!this.i18nFormat||this.i18nFormat.handleAsObject;if(L&&x&&('string'!=typeof x&&'boolean'!=typeof x&&'number'!=typeof x)&&['[object Number]','[object Function]','[object RegExp]'].indexOf(R)<0&&('string'!=typeof O||'[object Array]'!==R)){if(!s.returnObjects&&!this.options.returnObjects)return this.logger.warn('accessing an object - but returnObjects options is not enabled!'),this.options.returnedObjectHandler?this.options.returnedObjectHandler(S,x,s):"key '".concat(f," (").concat(this.language,")' returned an object instead of string.");if(l){var N='[object Array]'===R,C=N?[]:{},j=N?w:S;for(var E in x)if(Object.prototype.hasOwnProperty.call(x,E)){var P="".concat(j).concat(l).concat(E);C[E]=this.translate(P,o({},s,{joinArrays:!1,ns:p})),C[E]===P&&(C[E]=x[E])}x=C}}else if(L&&'string'==typeof O&&'[object Array]'===R)(x=x.join(O))&&(x=this.extendTranslation(x,t,s));else{var F=!1,V=!1;if(!this.isValidLookup(x)&&void 0!==s.defaultValue){if(F=!0,void 0!==s.count){var A=this.pluralResolver.getSuffix(v,s.count);x=s["defaultValue".concat(A)]}x||(x=s.defaultValue)}this.isValidLookup(x)||(V=!0,x=f);var U=s.defaultValue&&s.defaultValue!==x&&this.options.updateMissing;if(V||F||U){this.logger.log(U?'updateKey':'missingKey',v,h,f,U?s.defaultValue:x);var T=[],K=this.languageUtils.getFallbackCodes(this.options.fallbackLng,s.lng||this.language);if('fallback'===this.options.saveMissingTo&&K&&K[0])for(var H=0;H1&&void 0!==arguments[1]?arguments[1]:{};return'string'==typeof t&&(t=[t]),t.forEach(function(t){if(!c.isValidLookup(n)){var p=c.extractFromKey(t,f),h=p.key;o=h;var v=p.namespaces;c.options.fallbackNS&&(v=v.concat(c.options.fallbackNS));var y=void 0!==f.count&&'string'!=typeof f.count,b=void 0!==f.context&&'string'==typeof f.context&&''!==f.context,k=f.lngs?f.lngs:c.languageUtils.toResolveHierarchy(f.lng||c.language,f.fallbackLng);v.forEach(function(t){c.isValidLookup(n)||(l=t,k.forEach(function(o){if(!c.isValidLookup(n)){u=o;var l,p,v=h,k=[v];if(c.i18nFormat&&c.i18nFormat.addLookupKeys)c.i18nFormat.addLookupKeys(k,h,o,t,f);else y&&(l=c.pluralResolver.getSuffix(o,f.count)),y&&b&&k.push(v+l),b&&k.push(v+="".concat(c.options.contextSeparator).concat(f.context)),y&&k.push(v+=l);for(;p=k.pop();)c.isValidLookup(n)||(s=p,n=c.getResource(o,t,p,f))}}))})}}),{res:n,usedKey:o,exactUsedKey:s,usedLng:u,usedNS:l}}},{key:"isValidLookup",value:function(t){return!(void 0===t||!this.options.returnNull&&null===t||!this.options.returnEmptyString&&''===t)}},{key:"getResource",value:function(t,n,o){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return this.i18nFormat&&this.i18nFormat.getResource?this.i18nFormat.getResource(t,n,o,s):this.resourceStore.getResource(t,n,o,s)}}]),h})();function V(t){return t.charAt(0).toUpperCase()+t.slice(1)}var A=(function(){function t(n){s(this,t),this.options=n,this.whitelist=this.options.whitelist||!1,this.logger=b.create('languageUtils')}return u(t,[{key:"getScriptPartFromCode",value:function(t){if(!t||t.indexOf('-')<0)return null;var n=t.split('-');return 2===n.length?null:(n.pop(),this.formatLanguageCode(n.join('-')))}},{key:"getLanguagePartFromCode",value:function(t){if(!t||t.indexOf('-')<0)return t;var n=t.split('-');return this.formatLanguageCode(n[0])}},{key:"formatLanguageCode",value:function(t){if('string'==typeof t&&t.indexOf('-')>-1){var n=['hans','hant','latn','cyrl','cans','mong','arab'],o=t.split('-');return this.options.lowerCaseLng?o=o.map(function(t){return t.toLowerCase()}):2===o.length?(o[0]=o[0].toLowerCase(),o[1]=o[1].toUpperCase(),n.indexOf(o[1].toLowerCase())>-1&&(o[1]=V(o[1].toLowerCase()))):3===o.length&&(o[0]=o[0].toLowerCase(),2===o[1].length&&(o[1]=o[1].toUpperCase()),'sgn'!==o[0]&&2===o[2].length&&(o[2]=o[2].toUpperCase()),n.indexOf(o[1].toLowerCase())>-1&&(o[1]=V(o[1].toLowerCase())),n.indexOf(o[2].toLowerCase())>-1&&(o[2]=V(o[2].toLowerCase()))),o.join('-')}return this.options.cleanCode||this.options.lowerCaseLng?t.toLowerCase():t}},{key:"isWhitelisted",value:function(t){return('languageOnly'===this.options.load||this.options.nonExplicitWhitelist)&&(t=this.getLanguagePartFromCode(t)),!this.whitelist||!this.whitelist.length||this.whitelist.indexOf(t)>-1}},{key:"getFallbackCodes",value:function(t,n){if(!t)return[];if('string'==typeof t&&(t=[t]),'[object Array]'===Object.prototype.toString.apply(t))return t;if(!n)return t.default||[];var o=t[n];return o||(o=t[this.getScriptPartFromCode(n)]),o||(o=t[this.formatLanguageCode(n)]),o||(o=t.default),o||[]}},{key:"toResolveHierarchy",value:function(t,n){var o=this,s=this.getFallbackCodes(n||this.options.fallbackLng||[],t),u=[],l=function(t){t&&(o.isWhitelisted(t)?u.push(t):o.logger.warn("rejecting non-whitelisted language code: ".concat(t)))};return'string'==typeof t&&t.indexOf('-')>-1?('languageOnly'!==this.options.load&&l(this.formatLanguageCode(t)),'languageOnly'!==this.options.load&&'currentOnly'!==this.options.load&&l(this.getScriptPartFromCode(t)),'currentOnly'!==this.options.load&&l(this.getLanguagePartFromCode(t))):'string'==typeof t&&l(this.formatLanguageCode(t)),s.forEach(function(t){u.indexOf(t)<0&&l(o.formatLanguageCode(t))}),u}}]),t})(),U=[{lngs:['ach','ak','am','arn','br','fil','gun','ln','mfe','mg','mi','oc','pt','pt-BR','tg','ti','tr','uz','wa'],nr:[1,2],fc:1},{lngs:['af','an','ast','az','bg','bn','ca','da','de','dev','el','en','eo','es','et','eu','fi','fo','fur','fy','gl','gu','ha','hi','hu','hy','ia','it','kn','ku','lb','mai','ml','mn','mr','nah','nap','nb','ne','nl','nn','no','nso','pa','pap','pms','ps','pt-PT','rm','sco','se','si','so','son','sq','sv','sw','ta','te','tk','ur','yo'],nr:[1,2],fc:2},{lngs:['ay','bo','cgg','fa','id','ja','jbo','ka','kk','km','ko','ky','lo','ms','sah','su','th','tt','ug','vi','wo','zh'],nr:[1],fc:3},{lngs:['be','bs','cnr','dz','hr','ru','sr','uk'],nr:[1,2,5],fc:4},{lngs:['ar'],nr:[0,1,2,3,11,100],fc:5},{lngs:['cs','sk'],nr:[1,2,5],fc:6},{lngs:['csb','pl'],nr:[1,2,5],fc:7},{lngs:['cy'],nr:[1,2,3,8],fc:8},{lngs:['fr'],nr:[1,2],fc:9},{lngs:['ga'],nr:[1,2,3,7,11],fc:10},{lngs:['gd'],nr:[1,2,3,20],fc:11},{lngs:['is'],nr:[1,2],fc:12},{lngs:['jv'],nr:[0,1],fc:13},{lngs:['kw'],nr:[1,2,3,4],fc:14},{lngs:['lt'],nr:[1,2,10],fc:15},{lngs:['lv'],nr:[1,2,0],fc:16},{lngs:['mk'],nr:[1,2],fc:17},{lngs:['mnk'],nr:[0,1,2],fc:18},{lngs:['mt'],nr:[1,2,11,20],fc:19},{lngs:['or'],nr:[2,1],fc:2},{lngs:['ro'],nr:[1,2,20],fc:20},{lngs:['sl'],nr:[5,1,2,3],fc:21},{lngs:['he'],nr:[1,2,20,21],fc:22}],T={1:function(t){return Number(t>1)},2:function(t){return Number(1!=t)},3:function(t){return 0},4:function(t){return Number(t%10==1&&t%100!=11?0:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?1:2)},5:function(t){return Number(0===t?0:1==t?1:2==t?2:t%100>=3&&t%100<=10?3:t%100>=11?4:5)},6:function(t){return Number(1==t?0:t>=2&&t<=4?1:2)},7:function(t){return Number(1==t?0:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?1:2)},8:function(t){return Number(1==t?0:2==t?1:8!=t&&11!=t?2:3)},9:function(t){return Number(t>=2)},10:function(t){return Number(1==t?0:2==t?1:t<7?2:t<11?3:4)},11:function(t){return Number(1==t||11==t?0:2==t||12==t?1:t>2&&t<20?2:3)},12:function(t){return Number(t%10!=1||t%100==11)},13:function(t){return Number(0!==t)},14:function(t){return Number(1==t?0:2==t?1:3==t?2:3)},15:function(t){return Number(t%10==1&&t%100!=11?0:t%10>=2&&(t%100<10||t%100>=20)?1:2)},16:function(t){return Number(t%10==1&&t%100!=11?0:0!==t?1:2)},17:function(t){return Number(1==t||t%10==1?0:1)},18:function(t){return Number(0==t?0:1==t?1:2)},19:function(t){return Number(1==t?0:0===t||t%100>1&&t%100<11?1:t%100>10&&t%100<20?2:3)},20:function(t){return Number(1==t?0:0===t||t%100>0&&t%100<20?1:2)},21:function(t){return Number(t%100==1?1:t%100==2?2:t%100==3||t%100==4?3:0)},22:function(t){return Number(1===t?0:2===t?1:(t<0||t>10)&&t%10==0?2:3)}};var K=(function(){function t(n){var o,u=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};s(this,t),this.languageUtils=n,this.options=u,this.logger=b.create('pluralResolver'),this.rules=(o={},U.forEach(function(t){t.lngs.forEach(function(n){o[n]={numbers:t.nr,plurals:T[t.fc]}})}),o)}return u(t,[{key:"addRule",value:function(t,n){this.rules[t]=n}},{key:"getRule",value:function(t){return this.rules[t]||this.rules[this.languageUtils.getLanguagePartFromCode(t)]}},{key:"needsPlural",value:function(t){var n=this.getRule(t);return n&&n.numbers.length>1}},{key:"getPluralFormsOfKey",value:function(t,n){var o=this,s=[],u=this.getRule(t);return u?(u.numbers.forEach(function(u){var l=o.getSuffix(t,u);s.push("".concat(n).concat(l))}),s):s}},{key:"getSuffix",value:function(t,n){var o=this,s=this.getRule(t);if(s){var u=s.noAbs?s.plurals(n):s.plurals(Math.abs(n)),l=s.numbers[u];this.options.simplifyPluralSuffix&&2===s.numbers.length&&1===s.numbers[0]&&(2===l?l='plural':1===l&&(l=''));var c=function(){return o.options.prepend&&l.toString()?o.options.prepend+l.toString():l.toString()};return'v1'===this.options.compatibilityJSON?1===l?'':'number'==typeof l?"_plural_".concat(l.toString()):c():'v2'===this.options.compatibilityJSON?c():this.options.simplifyPluralSuffix&&2===s.numbers.length&&1===s.numbers[0]?c():this.options.prepend&&u.toString()?this.options.prepend+u.toString():u.toString()}return this.logger.warn("no plural rule found for: ".concat(t)),''}}]),t})(),H=(function(){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};s(this,t),this.logger=b.create('interpolator'),this.init(n,!0)}return u(t,[{key:"init",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(arguments.length>1?arguments[1]:void 0)&&(this.options=t,this.format=t.interpolation&&t.interpolation.format||function(t){return t}),t.interpolation||(t.interpolation={escapeValue:!0});var n=t.interpolation;this.escape=void 0!==n.escape?n.escape:j,this.escapeValue=void 0===n.escapeValue||n.escapeValue,this.useRawValueToEscape=void 0!==n.useRawValueToEscape&&n.useRawValueToEscape,this.prefix=n.prefix?N(n.prefix):n.prefixEscaped||'{{',this.suffix=n.suffix?N(n.suffix):n.suffixEscaped||'}}',this.formatSeparator=n.formatSeparator?n.formatSeparator:n.formatSeparator||',',this.unescapePrefix=n.unescapeSuffix?'':n.unescapePrefix||'-',this.unescapeSuffix=this.unescapePrefix?'':n.unescapeSuffix||'',this.nestingPrefix=n.nestingPrefix?N(n.nestingPrefix):n.nestingPrefixEscaped||N('$t('),this.nestingSuffix=n.nestingSuffix?N(n.nestingSuffix):n.nestingSuffixEscaped||N(')'),this.maxReplaces=n.maxReplaces?n.maxReplaces:1e3,this.resetRegExp()}},{key:"reset",value:function(){this.options&&this.init(this.options)}},{key:"resetRegExp",value:function(){var t="".concat(this.prefix,"(.+?)").concat(this.suffix);this.regexp=new RegExp(t,'g');var n="".concat(this.prefix).concat(this.unescapePrefix,"(.+?)").concat(this.unescapeSuffix).concat(this.suffix);this.regexpUnescape=new RegExp(n,'g');var o="".concat(this.nestingPrefix,"(.+?)").concat(this.nestingSuffix);this.nestingRegexp=new RegExp(o,'g')}},{key:"interpolate",value:function(t,n,o,s){var u,l,c,f=this;function p(t){return t.replace(/\$/g,'$$$$')}var h=function(t){if(t.indexOf(f.formatSeparator)<0)return O(n,t);var s=t.split(f.formatSeparator),u=s.shift().trim(),l=s.join(f.formatSeparator).trim();return f.format(O(n,u),l,o)};this.resetRegExp();var v=s&&s.missingInterpolationHandler||this.options.missingInterpolationHandler;for(c=0;(u=this.regexpUnescape.exec(t))&&(l=h(u[1].trim()),t=t.replace(u[0],l),this.regexpUnescape.lastIndex=0,!(++c>=this.maxReplaces)););for(c=0;u=this.regexp.exec(t);){if(void 0===(l=h(u[1].trim())))if('function'==typeof v){var y=v(t,u,s);l='string'==typeof y?y:''}else this.logger.warn("missed to pass in variable ".concat(u[1]," for interpolating ").concat(t)),l='';else'string'==typeof l||this.useRawValueToEscape||(l=S(l));if(l=this.escapeValue?p(this.escape(l)):p(l),t=t.replace(u[0],l),this.regexp.lastIndex=0,++c>=this.maxReplaces)break}return t}},{key:"nest",value:function(t,n){var s,u,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},c=o({},l);function f(t,n){if(t.indexOf(',')<0)return t;var s=t.split(',');t=s.shift();var u=s.join(',');u=(u=this.interpolate(u,c)).replace(/'/g,'"');try{c=JSON.parse(u),n&&(c=o({},n,c))}catch(n){this.logger.error("failed parsing options string in nesting for key ".concat(t),n)}return t}for(c.applyPostProcessor=!1;s=this.nestingRegexp.exec(t);){if((u=n(f.call(this,s[1].trim(),c),c))&&s[0]===t&&'string'!=typeof u)return u;'string'!=typeof u&&(u=S(u)),u||(this.logger.warn("missed to resolve ".concat(s[1]," for nesting ").concat(t)),u=''),t=t.replace(s[0],u),this.regexp.lastIndex=0}return t}}]),t})();function D(t,n){for(var o=t.indexOf(n);-1!==o;)t.splice(o,1),o=t.indexOf(n)}var M=(function(t){function n(t,o,u){var p,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return s(this,n),p=l(this,c(n).call(this)),k.call(f(p)),p.backend=t,p.store=o,p.languageUtils=u.languageUtils,p.options=h,p.logger=b.create('backendConnector'),p.state={},p.queue=[],p.backend&&p.backend.init&&p.backend.init(u,h.backend,h),p}return p(n,k),u(n,[{key:"queueLoad",value:function(t,n,o,s){var u=this,l=[],c=[],f=[],p=[];return t.forEach(function(t){var s=!0;n.forEach(function(n){var f="".concat(t,"|").concat(n);!o.reload&&u.store.hasResourceBundle(t,n)?u.state[f]=2:u.state[f]<0||(1===u.state[f]?c.indexOf(f)<0&&c.push(f):(u.state[f]=1,s=!1,c.indexOf(f)<0&&c.push(f),l.indexOf(f)<0&&l.push(f),p.indexOf(n)<0&&p.push(n)))}),s||f.push(t)}),(l.length||c.length)&&this.queue.push({pending:c,loaded:{},errors:[],callback:s}),{toLoad:l,pending:c,toLoadLanguages:f,toLoadNamespaces:p}}},{key:"loaded",value:function(t,n,o){var s=t.split('|'),u=v(s,2),l=u[0],c=u[1];n&&this.emit('failedLoading',l,c,n),o&&this.store.addResourceBundle(l,c,o),this.state[t]=n?-1:2;var f={};this.queue.forEach(function(o){var s,u,p,h,v,y;s=o.loaded,u=c,h=w(s,[l],Object),v=h.obj,y=h.k,v[y]=v[y]||[],p&&(v[y]=v[y].concat(u)),p||v[y].push(u),D(o.pending,t),n&&o.errors.push(n),0!==o.pending.length||o.done||(Object.keys(o.loaded).forEach(function(t){f[t]||(f[t]=[]),o.loaded[t].length&&o.loaded[t].forEach(function(n){f[t].indexOf(n)<0&&f[t].push(n)})}),o.done=!0,o.errors.length?o.callback(o.errors):o.callback())}),this.emit('loaded',f),this.queue=this.queue.filter(function(t){return!t.done})}},{key:"read",value:function(t,n,o){var s=this,u=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,l=arguments.length>4&&void 0!==arguments[4]?arguments[4]:250,c=arguments.length>5?arguments[5]:void 0;return t.length?this.backend[o](t,n,function(f,p){f&&p&&u<5?setTimeout(function(){s.read.call(s,t,n,o,u+1,2*l,c)},l):c(f,p)}):c(null,{})}},{key:"prepareLoading",value:function(t,n){var o=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},u=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn('No backend was added via i18next.use. Will not load resources.'),u&&u();'string'==typeof t&&(t=this.languageUtils.toResolveHierarchy(t)),'string'==typeof n&&(n=[n]);var l=this.queueLoad(t,n,s,u);if(!l.toLoad.length)return l.pending.length||u(),null;l.toLoad.forEach(function(t){o.loadOne(t)})}},{key:"load",value:function(t,n,o){this.prepareLoading(t,n,{},o)}},{key:"reload",value:function(t,n,o){this.prepareLoading(t,n,{reload:!0},o)}},{key:"loadOne",value:function(t){var n=this,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:'',s=t.split('|'),u=v(s,2),l=u[0],c=u[1];this.read(l,c,'read',null,null,function(s,u){s&&n.logger.warn("".concat(o,"loading namespace ").concat(c," for language ").concat(l," failed"),s),!s&&u&&n.logger.log("".concat(o,"loaded namespace ").concat(c," for language ").concat(l),u),n.loaded(t,s,u)})}},{key:"saveMissing",value:function(t,n,s,u,l){var c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{};this.backend&&this.backend.create&&this.backend.create(t,n,s,u,null,o({},c,{isUpdate:l})),t&&t[0]&&this.store.addResource(t[0],n,s,u)}}]),n})();function I(t){return'string'==typeof t.ns&&(t.ns=[t.ns]),'string'==typeof t.fallbackLng&&(t.fallbackLng=[t.fallbackLng]),'string'==typeof t.fallbackNS&&(t.fallbackNS=[t.fallbackNS]),t.whitelist&&t.whitelist.indexOf('cimode')<0&&(t.whitelist=t.whitelist.concat(['cimode'])),t}function B(){}var q=new((function(t){function h(){var t,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=arguments.length>1?arguments[1]:void 0;if(s(this,h),t=l(this,c(h).call(this)),k.call(f(t)),t.options=I(n),t.services={},t.logger=b,t.modules={external:[]},o&&!t.isInitialized&&!n.isClone){if(!t.options.initImmediate)return t.init(n,o),l(t,f(t));setTimeout(function(){t.init(n,o)},0)}return t}return p(h,k),u(h,[{key:"init",value:function(){var t=this,s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},u=arguments.length>1?arguments[1]:void 0;function l(t){return t?'function'==typeof t?new t:t:null}if('function'==typeof s&&(u=s,s={}),this.options=o({},{debug:!1,initImmediate:!0,ns:['translation'],defaultNS:['translation'],fallbackLng:['dev'],fallbackNS:!1,whitelist:!1,nonExplicitWhitelist:!1,load:'all',preload:!1,simplifyPluralSuffix:!0,keySeparator:'.',nsSeparator:':',pluralSeparator:'_',contextSeparator:'_',partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:'fallback',saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,returnNull:!0,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:function(){},parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:function(t){var o={};if('object'===n(t[1])&&(o=t[1]),'string'==typeof t[1]&&(o.defaultValue=t[1]),'string'==typeof t[2]&&(o.tDescription=t[2]),'object'===n(t[2])||'object'===n(t[3])){var s=t[3]||t[2];Object.keys(s).forEach(function(t){o[t]=s[t]})}return o},interpolation:{escapeValue:!0,format:function(t,n,o){return t},prefix:'{{',suffix:'}}',formatSeparator:',',unescapePrefix:'-',nestingPrefix:'$t(',nestingSuffix:')',maxReplaces:1e3}},this.options,I(s)),this.format=this.options.interpolation.format,u||(u=B),!this.options.isClone){this.modules.logger?b.init(l(this.modules.logger),this.options):b.init(null,this.options);var c=new A(this.options);this.store=new E(this.options.resources,this.options);var f=this.services;f.logger=b,f.resourceStore=this.store,f.languageUtils=c,f.pluralResolver=new K(c,{prepend:this.options.pluralSeparator,compatibilityJSON:this.options.compatibilityJSON,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),f.interpolator=new H(this.options),f.backendConnector=new M(l(this.modules.backend),f.resourceStore,f,this.options),f.backendConnector.on('*',function(n){for(var o=arguments.length,s=new Array(o>1?o-1:0),u=1;u1?o-1:0),u=1;u0&&void 0!==arguments[0]?arguments[0]:B;if(!this.options.resources||this.options.partialBundledLanguages){if(this.language&&'cimode'===this.language.toLowerCase())return n();var o=[],s=function(n){n&&t.services.languageUtils.toResolveHierarchy(n).forEach(function(t){o.indexOf(t)<0&&o.push(t)})};if(this.language)s(this.language);else this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(function(t){return s(t)});this.options.preload&&this.options.preload.forEach(function(t){return s(t)}),this.services.backendConnector.load(o,this.options.ns,n)}else n(null)}},{key:"reloadResources",value:function(t,n,o){var s=x();return t||(t=this.languages),n||(n=this.options.ns),o||(o=B),this.services.backendConnector.reload(t,n,function(t){s.resolve(),o(t)}),s}},{key:"use",value:function(t){return'backend'===t.type&&(this.modules.backend=t),('logger'===t.type||t.log&&t.warn&&t.error)&&(this.modules.logger=t),'languageDetector'===t.type&&(this.modules.languageDetector=t),'i18nFormat'===t.type&&(this.modules.i18nFormat=t),'postProcessor'===t.type&&P.addPostProcessor(t),'3rdParty'===t.type&&this.modules.external.push(t),this}},{key:"changeLanguage",value:function(t,n){var o=this,s=x();this.emit('languageChanging',t);var u=function(t,u){o.translator.changeLanguage(u),u&&(o.emit('languageChanged',u),o.logger.log('languageChanged',u)),s.resolve(function(){return o.t.apply(o,arguments)}),n&&n(t,function(){return o.t.apply(o,arguments)})},l=function(t){t&&(o.language=t,o.languages=o.services.languageUtils.toResolveHierarchy(t),o.translator.language||o.translator.changeLanguage(t),o.services.languageDetector&&o.services.languageDetector.cacheUserLanguage(t)),o.loadResources(function(n){u(n,t)})};return t||!this.services.languageDetector||this.services.languageDetector.async?!t&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect(l):l(t):l(this.services.languageDetector.detect()),s}},{key:"getFixedT",value:function(t,s){var u=this,l=function t(s,l){var c=o({},l);if('object'!==n(l)){for(var f=arguments.length,p=new Array(f>2?f-2:0),h=2;h0?this.languages[0]:this.language),!t)return'rtl';return['ar','shu','sqr','ssh','xaa','yhd','yud','aao','abh','abv','acm','acq','acw','acx','acy','adf','ads','aeb','aec','afb','ajp','apc','apd','arb','arq','ars','ary','arz','auz','avl','ayh','ayl','ayn','ayp','bbz','pga','he','iw','ps','pbt','pbu','pst','prp','prd','ur','ydd','yds','yih','ji','yi','hbo','men','xmn','fa','jpr','peo','pes','prs','dv','sam'].indexOf(this.services.languageUtils.getLanguagePartFromCode(t))>=0?'rtl':'ltr'}},{key:"createInstance",value:function(){return new h(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},arguments.length>1?arguments[1]:void 0)}},{key:"cloneInstance",value:function(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:B,u=o({},this.options,n,{isClone:!0}),l=new h(u);return['store','services','language'].forEach(function(n){l[n]=t[n]}),l.translator=new F(l.services,l.options),l.translator.on('*',function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),s=1;s3&&o&&o(u.responseText,u)},u.send(l)}catch(t){console&&console.log(t)}}var v=(function(){function t(o){var s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};n(this,t),this.init(o,s),this.type='backend'}return o(t,[{key:"init",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.services=t,this.options=p(n,this.options||{},{loadPath:'/locales/{{lng}}/{{ns}}.json',addPath:'/locales/add/{{lng}}/{{ns}}',allowMultiLoading:!1,parse:JSON.parse,crossDomain:!1,ajax:h})}},{key:"readMulti",value:function(t,n,o){var s=this.options.loadPath;'function'==typeof this.options.loadPath&&(s=this.options.loadPath(t,n));var l=this.services.interpolator.interpolate(s,{lng:t.join('+'),ns:n.join('+')});this.loadUrl(l,o)}},{key:"read",value:function(t,n,o){var s=this.options.loadPath;'function'==typeof this.options.loadPath&&(s=this.options.loadPath([t],[n]));var l=this.services.interpolator.interpolate(s,{lng:t,ns:n});this.loadUrl(l,o)}},{key:"loadUrl",value:function(t,n){var o=this;this.options.ajax(t,this.options,function(s,l){if(l.status>=500&&l.status<600)return n('failed loading '+t,!0);if(l.status>=400&&l.status<500)return n('failed loading '+t,!1);var c,u;try{c=o.options.parse(s,t)}catch(n){u='failed parsing '+t+' to json'}if(u)return n(u,!1);n(null,c)})}},{key:"create",value:function(t,n,o,s){var l=this;'string'==typeof t&&(t=[t]);var c={};c[o]=s||'',t.forEach(function(t){var o=l.services.interpolator.interpolate(l.options.addPath,{lng:t,ns:n});l.options.ajax(o,l.options,function(t,n){},c)})}}]),t})();v.type='backend',m.exports=v},630,[631,632,633]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n,o){if(!(n instanceof o))throw new TypeError("Cannot call a class as a function")}},631,[]); -__d(function(g,r,i,a,m,e,d){function n(n,t){for(var o=0;o{{room}} has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",WaitForHostMsgWOk:"The conference {{room}} has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",WaitingForHost:"Waiting for the host ...",Yes:"Yes",yourEntireScreen:"Your entire screen"},dialOut:{statusMessage:"is now {{status}}"},feedback:{average:"Average",bad:"Bad",detailsLabel:"Tell us more about it.",good:"Good",rateExperience:"Rate your meeting experience",veryBad:"Very Bad",veryGood:"Very Good"},incomingCall:{answer:"Answer",audioCallTitle:"Incoming call",decline:"Dismiss",productLabel:"from Jitsi Meet",videoCallTitle:"Incoming video call"},info:{accessibilityLabel:"Show info",addPassword:"Add $t(lockRoomPassword)",cancelPassword:"Cancel $t(lockRoomPassword)",conferenceURL:"Link:",country:"Country",dialANumber:"To join your meeting, dial one of these numbers and then enter the pin.",dialInConferenceID:"PIN:",dialInNotSupported:"Sorry, dialing in is currently not supported.",dialInNumber:"Dial-in:",dialInSummaryError:"Error fetching dial-in info now. Please try again later.",dialInTollFree:"Toll Free",genericError:"Whoops, something went wrong.",inviteLiveStream:"To view the live stream of this meeting, click this link: {{url}}",invitePhone:"To join by phone instead, tap this: {{number}},,{{conferenceID}}#\n",invitePhoneAlternatives:"Looking for a different dial-in number?\nSee meeting dial-in numbers: {{url}}\n\n\nIf also dialing-in through a room phone, join without connecting to audio: {{silentUrl}}",inviteURLFirstPartGeneral:"You are invited to join a meeting.",inviteURLFirstPartPersonal:"{{name}} is inviting you to a meeting.\n",inviteURLSecondPart:"\nJoin the meeting:\n{{url}}\n",liveStreamURL:"Live stream:",moreNumbers:"More numbers",noNumbers:"No dial-in numbers.",noPassword:"None",noRoom:"No room was specified to dial-in into.",numbers:"Dial-in Numbers",password:"$t(lockRoomPasswordUppercase):",title:"Share",tooltip:"Share link and dial-in info for this meeting",label:"Meeting info"},inviteDialog:{alertText:"Failed to invite some participants.",header:"Invite",searchCallOnlyPlaceholder:"Enter phone number",searchPeopleOnlyPlaceholder:"Search for participants",searchPlaceholder:"Participant or phone number",send:"Send"},inlineDialogFailure:{msg:"We stumbled a bit.",retry:"Try again",support:"Support",supportMsg:"If this keeps happening, reach out to"},keyboardShortcuts:{focusLocal:"Focus on your video",focusRemote:"Focus on another person's video",fullScreen:"View or exit full screen",keyboardShortcuts:"Keyboard shortcuts",localRecording:"Show or hide local recording controls",mute:"Mute or unmute your microphone",pushToTalk:"Push to talk",raiseHand:"Raise or lower your hand",showSpeakerStats:"Show speaker stats",toggleChat:"Open or close the chat",toggleFilmstrip:"Show or hide video thumbnails",toggleScreensharing:"Switch between camera and screen sharing",toggleShortcuts:"Show or hide keyboard shortcuts",videoMute:"Start or stop your camera",videoQuality:"Manage call quality"},liveStreaming:{busy:"We're working on freeing streaming resources. Please try again in a few minutes.",busyTitle:"All streamers are currently busy",changeSignIn:"Switch accounts.",choose:"Choose a live stream",chooseCTA:"Choose a streaming option. You're currently logged in as {{email}}.",enterStreamKey:"Enter your YouTube live stream key here.",error:"Live Streaming failed. Please try again.",errorAPI:"An error occurred while accessing your YouTube broadcasts. Please try logging in again.",errorLiveStreamNotEnabled:"Live Streaming is not enabled on {{email}}. Please enable live streaming or log into an account with live streaming enabled.",expandedOff:"The live streaming has stopped",expandedOn:"The meeting is currently being streamed to YouTube.",expandedPending:"The live streaming is being started...",failedToStart:"Live Streaming failed to start",getStreamKeyManually:"We weren\u2019t able to fetch any live streams. Try getting your live stream key from YouTube.",invalidStreamKey:"Live stream key may be incorrect.",off:"Live Streaming stopped",on:"Live Streaming",pending:"Starting Live Stream...",serviceName:"Live Streaming service",signedInAs:"You are currently signed in as:",signIn:"Sign in with Google",signInCTA:"Sign in or enter your live stream key from YouTube.",signOut:"Sign out",start:"Start a live stream",streamIdHelp:"What's this?",unavailableTitle:"Live Streaming unavailable"},localRecording:{clientState:{off:"Off",on:"On",unknown:"Unknown"},dialogTitle:"Local Recording Controls",duration:"Duration",durationNA:"N/A",encoding:"Encoding",label:"LOR",labelToolTip:"Local recording is engaged",localRecording:"Local Recording",me:"Me",messages:{engaged:"Local recording engaged.",finished:"Recording session {{token}} finished. Please send the recorded file to the moderator.",finishedModerator:"Recording session {{token}} finished. The recording of the local track has been saved. Please ask the other participants to submit their recordings.",notModerator:"You are not the moderator. You cannot start or stop local recording."},moderator:"Moderator",no:"No",participant:"Participant",participantStats:"Participant Stats",sessionToken:"Session Token",start:"Start Recording",stop:"Stop Recording",yes:"Yes"},lockRoomPassword:"password",lockRoomPasswordUppercase:"Password",me:"me",notify:{connectedOneMember:"{{name}} joined the meeting",connectedThreePlusMembers:"{{name}} and {{count}} others joined the meeting",connectedTwoMembers:"{{first}} and {{second}} joined the meeting",disconnected:"disconnected",focus:"Conference focus",focusFail:"{{component}} not available - retry in {{ms}} sec",grantedTo:"Moderator rights granted to {{to}}!",invitedOneMember:"{{name}} has been invited",invitedThreePlusMembers:"{{name}} and {{count}} others have been invited",invitedTwoMembers:"{{first}} and {{second}} have been invited",kickParticipant:"{{kicked}} was kicked by {{kicker}}",me:"Me",moderator:"Moderator rights granted!",muted:"You have started the conversation muted.",mutedTitle:"You're muted!",mutedRemotelyTitle:"You have been muted by {{participantDisplayName}}!",mutedRemotelyDescription:"You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.",passwordRemovedRemotely:"$t(lockRoomPasswordUppercase) removed by another participant",passwordSetRemotely:"$t(lockRoomPasswordUppercase) set by another participant",raisedHand:"{{name}} would like to speak.",somebody:"Somebody",startSilentTitle:"You joined with no audio output!",startSilentDescription:"Rejoin the meeting to enable audio",suboptimalBrowserWarning:"We are afraid your meeting experience isn't going to be that great here. We are looking for ways to improve this, but until then please try using one of the fully supported browsers.",suboptimalExperienceTitle:"Browser Warning",unmute:"Unmute",newDeviceCameraTitle:"New camera detected",newDeviceAudioTitle:"New audio device detected",newDeviceAction:"Use"},passwordSetRemotely:"set by another participant",passwordDigitsOnly:"Up to {{number}} digits",poweredby:"powered by",presenceStatus:{busy:"Busy",calling:"Calling...",connected:"Connected",connecting:"Connecting...",connecting2:"Connecting*...",disconnected:"Disconnected",expired:"Expired",ignored:"Ignored",initializingCall:"Initializing Call...",invited:"Invited",rejected:"Rejected",ringing:"Ringing..."},profile:{setDisplayNameLabel:"Set your display name",setEmailInput:"Enter e-mail",setEmailLabel:"Set your gravatar email",title:"Profile"},raisedHand:"Would like to speak",recording:{authDropboxText:"Upload to Dropbox",availableSpace:"Available space: {{spaceLeft}} MB (approximately {{duration}} minutes of recording)",beta:"BETA",busy:"We're working on freeing recording resources. Please try again in a few minutes.",busyTitle:"All recorders are currently busy",error:"Recording failed. Please try again.",expandedOff:"Recording has stopped",expandedOn:"The meeting is currently being recorded.",expandedPending:"Recording is being started...",failedToStart:"Recording failed to start",fileSharingdescription:"Share recording with meeting participants",live:"LIVE",loggedIn:"Logged in as {{userName}}",off:"Recording stopped",on:"Recording",pending:"Preparing to record the meeting...",rec:"REC",serviceDescription:"Your recording will be saved by the recording service",serviceName:"Recording service",signIn:"Sign in",signOut:"Sign out",unavailable:"Oops! The {{serviceName}} is currently unavailable. We're working on resolving the issue. Please try again later.",unavailableTitle:"Recording unavailable"},sectionList:{pullToRefresh:"Pull to refresh"},settings:{calendar:{about:"The {{appName}} calendar integration is used to securely access your calendar so it can read upcoming events.",disconnect:"Disconnect",microsoftSignIn:"Sign in with Microsoft",signedIn:"Currently accessing calendar events for {{email}}. Click the Disconnect button below to stop accessing calendar events.",title:"Calendar"},devices:"Devices",followMe:"Everyone follows me",language:"Language",loggedIn:"Logged in as {{name}}",moderator:"Moderator",more:"More",name:"Name",noDevice:"None",selectAudioOutput:"Audio output",selectCamera:"Camera",selectMic:"Microphone",startAudioMuted:"Everyone starts muted",startVideoMuted:"Everyone starts hidden",title:"Settings"},settingsView:{alertOk:"OK",alertTitle:"Warning",alertURLText:"The entered server URL is invalid",buildInfoSection:"Build Information",conferenceSection:"Conference",displayName:"Display name",email:"Email",header:"Settings",profileSection:"Profile",serverURL:"Server URL",startWithAudioMuted:"Start with audio muted",startWithVideoMuted:"Start with video muted",version:"Version"},share:{dialInfoText:"\n\n=====\n\nJust want to dial in on your phone?\n\n{{defaultDialInNumber}}Click this link to see the dial in phone numbers for this meeting\n{{dialInfoPageUrl}}",mainText:"Click the following link to join the meeting:\n{{roomUrl}}"},speaker:"Speaker",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Name",seconds:"{{count}}s",speakerStats:"Speaker Stats",speakerTime:"Speaker Time"},startupoverlay:{policyText:" ",title:"{{app}} needs to use your microphone and camera."},suspendedoverlay:{rejoinKeyTitle:"Rejoin",text:"Press the Rejoin button to reconnect.",title:"Your video call was interrupted because this computer went to sleep."},toolbar:{accessibilityLabel:{audioOnly:"Toggle audio only",audioRoute:"Select the sound device",callQuality:"Manage video quality",cc:"Toggle subtitles",chat:"Toggle chat window",document:"Toggle shared document",feedback:"Leave feedback",fullScreen:"Toggle full screen",hangup:"Leave the call",invite:"Invite people",kick:"Kick participant",localRecording:"Toggle local recording controls",lockRoom:"Toggle meeting password",moreActions:"Toggle more actions menu",moreActionsMenu:"More actions menu",mute:"Toggle mute audio",pip:"Toggle Picture-in-Picture mode",profile:"Edit your profile",raiseHand:"Toggle raise hand",recording:"Toggle recording",remoteMute:"Mute participant",Settings:"Toggle settings",sharedvideo:"Toggle Youtube video sharing",shareRoom:"Invite someone",shareYourScreen:"Toggle screenshare",shortcuts:"Toggle shortcuts",show:"Show on stage",speakerStats:"Toggle speaker statistics",tileView:"Toggle tile view",toggleCamera:"Toggle camera",videomute:"Toggle mute video",videoblur:"Toggle video blur"},addPeople:"Add people to your call",audioOnlyOff:"Disable low bandwidth mode",audioOnlyOn:"Enable low bandwidth mode",audioRoute:"Select the sound device",authenticate:"Authenticate",callQuality:"Manage video quality",chat:"Open / Close chat",closeChat:"Close chat",documentClose:"Close shared document",documentOpen:"Open shared document",enterFullScreen:"View full screen",enterTileView:"Enter tile view",exitFullScreen:"Exit full screen",exitTileView:"Exit tile view",feedback:"Leave feedback",hangup:"Leave",invite:"Invite people",login:"Login",logout:"Logout",lowerYourHand:"Lower your hand",moreActions:"More actions",mute:"Mute / Unmute",openChat:"Open chat",pip:"Enter Picture-in-Picture mode",profile:"Edit your profile",raiseHand:"Raise / Lower your hand",raiseYourHand:"Raise your hand",Settings:"Settings",sharedvideo:"Share a YouTube video",shareRoom:"Invite someone",shortcuts:"View shortcuts",speakerStats:"Speaker stats",startScreenSharing:"Start screen sharing",startSubtitles:"Start subtitles",stopScreenSharing:"Stop screen sharing",stopSubtitles:"Stop subtitles",stopSharedVideo:"Stop YouTube video",talkWhileMutedPopup:"Trying to speak? You are muted.",tileViewToggle:"Toggle tile view",toggleCamera:"Toggle camera",videomute:"Start / Stop camera",startvideoblur:"Blur my background",stopvideoblur:"Disable background blur"},transcribing:{ccButtonTooltip:"Start / Stop subtitles",error:"Transcribing failed. Please try again.",expandedLabel:"Transcribing is currently on",failedToStart:"Transcribing failed to start",labelToolTip:"The meeting is being transcribed",off:"Transcribing stopped",pending:"Preparing to transcribe the meeting...",start:"Start showing subtitles",stop:"Stop showing subtitles",tr:"TR"},userMedia:{androidGrantPermissions:"Select Allow when your browser asks for permissions.",chromeGrantPermissions:"Select Allow when your browser asks for permissions.",edgeGrantPermissions:"Select Yes when your browser asks for permissions.",electronGrantPermissions:"Please grant permissions to use your camera and microphone",firefoxGrantPermissions:"Select Share Selected Device when your browser asks for permissions.",iexplorerGrantPermissions:"Select OK when your browser asks for permissions.",nwjsGrantPermissions:"Please grant permissions to use your camera and microphone",operaGrantPermissions:"Select Allow when your browser asks for permissions.","react-nativeGrantPermissions":"Select Allow when your browser asks for permissions.",safariGrantPermissions:"Select OK when your browser asks for permissions."},videoSIPGW:{busy:"We're working on freeing resources. Please try again in a few minutes.",busyTitle:"The Room service is currently busy",errorAlreadyInvited:"{{displayName}} already invited",errorInvite:"Conference not established yet. Please try again later.",errorInviteFailed:"We're working on resolving the issue. Please try again later.",errorInviteFailedTitle:"Inviting {{displayName}} failed",errorInviteTitle:"Error inviting room",pending:"{{displayName}} has been invited"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"You are in low bandwidth mode. In this mode you will receive only audio and screen sharing.",callQuality:"Video Quality",hd:"HD",hdTooltip:"Viewing high definition video",highDefinition:"High definition",labelTooiltipNoVideo:"No video",labelTooltipAudioOnly:"Low bandwidth mode enabled",ld:"LD",ldTooltip:"Viewing low definition video",lowDefinition:"Low definition",onlyAudioAvailable:"Only audio is available",onlyAudioSupported:"We only support audio in this browser.",p2pEnabled:"Peer to Peer Enabled",p2pVideoQualityDescription:"In peer to peer mode, received video quality can only be toggled between high and audio only. Other settings will not be honored until peer to peer is exited.",recHighDefinitionOnly:"Will prefer high definition.",sd:"SD",sdTooltip:"Viewing standard definition video",standardDefinition:"Standard definition"},videothumbnail:{domute:"Mute",flip:"Flip",kick:"Kick out",moderator:"Moderator",mute:"Participant is muted",muted:"Muted",remoteControl:"Remote control",show:"Show on stage",videomute:"Participant has stopped the camera"},welcomepage:{accessibilityLabel:{join:"Tap to join",roomname:"Enter room name"},appDescription:"Go ahead, video chat with the whole team. In fact, invite everyone you know. {{app}} is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free \u2014 with no account needed.",audioVideoSwitch:{audio:"Voice",video:"Video"},calendar:"Calendar",connectCalendarButton:"Connect your calendar",connectCalendarText:"Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.",enterRoomTitle:"Start a new meeting",onlyAsciiAllowed:"Meeting name should only contain latin characters and numbers.",go:"GO",join:"JOIN",info:"Info",privacy:"Privacy",recentList:"Recent",recentListDelete:"Delete",recentListEmpty:"Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",reducedUIText:"Welcome to {{app}}!",roomname:"Enter room name",roomnameHint:"Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",sendFeedback:"Send feedback",terms:"Terms",title:"Secure, fully featured, and completely free video conferencing"}}},636,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=r(d[0]),o={cacheUserLanguage:Function.prototype,detect:function(){return t.NativeModules.LocaleDetector.locale.replace(/_/,'-')},init:Function.prototype,type:'languageDetector'};e.default=o},637,[17]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0])(r(d[1])),u={af:{languages:r(d[2]),main:r(d[3])},bg:{languages:r(d[4]),main:r(d[5])},de:{languages:r(d[6]),main:r(d[7])},enGB:{languages:r(d[8]),main:r(d[9])},eo:{languages:r(d[10]),main:r(d[11])},es:{languages:r(d[12]),main:r(d[13])},esUS:{languages:r(d[14]),main:r(d[15])},fi:{languages:r(d[16]),main:r(d[17])},fr:{languages:r(d[18]),main:r(d[19])},frCA:{languages:r(d[20]),main:r(d[21])},hr:{languages:r(d[22]),main:r(d[23])},it:{languages:r(d[24]),main:r(d[25])},ja:{languages:r(d[26]),main:r(d[27])},ko:{languages:r(d[28]),main:r(d[29])},nl:{languages:r(d[30]),main:r(d[31])},oc:{languages:r(d[32]),main:r(d[33])},pl:{languages:r(d[34]),main:r(d[35])},ptBR:{languages:r(d[36]),main:r(d[37])},ru:{languages:r(d[38]),main:r(d[39])},sv:{languages:r(d[40]),main:r(d[41])},vi:{languages:r(d[42]),main:r(d[43])},zhCN:{languages:r(d[44]),main:r(d[45])},zhTW:{languages:r(d[46]),main:r(d[47])}};for(var l in u){var s=u[l],f=s.languages,o=s.main;n.default.addResourceBundle(l,'languages',f,!0,!0),n.default.addResourceBundle(l,'main',o,!0,!0)}},638,[3,611,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684]); -__d(function(s,e,a,n,i,o,r){i.exports={en:"Engels",af:"",az:"Azerbeidjans",bg:"Bulgaars",cs:"Tsjeggies",de:"Duits",el:"Grieks",eo:"Esperanto",es:"Spaans",fr:"Frans",hy:"Armeens",it:"Italiaans",ja:"Japannees",ko:"Koreaans",nb:"Bokmal-Noorweegs",oc:"Oksitaans",pl:"Pools",ptBR:"Portugees (Brasili\xeb)",ru:"Russies",sk:"Slowaaks",sl:"Sloweens",sv:"Sweeds",tr:"Turks",vi:"Vi\xebtnamees",zhCN:"Sjinees (Sjina)"}},639,[]); -__d(function(e,i,n,o,r,a,t){r.exports={addPeople:{add:"Nooi uit",countryNotSupported:"Ons ondersteun nog nie di\xe9 bestemming nie.",countryReminder:"",disabled:"U kan nie mense nooi nie.",failedToAdd:"",footerText:"",loading:"",loadingNumber:"Valideer tans foonnommer",loadingPeople:"",noResults:"Geen soekresultate wat pas nie",noValidNumbers:"Gee asseblief \u2019n foonnommer",searchNumbers:"Voeg foonnommers by",searchPeople:"Soek mense",searchPeopleAndNumbers:"Soek mense of voeg hulle foonnommers by",telephone:"Telefoon: {{number}}",title:"Nooi mense na di\xe9 vergadering"},audioDevices:{bluetooth:"Bluetooth",headphones:"Oorfone",phone:"Foon",speaker:"Luidspreker"},audioOnly:{audioOnly:"Net klank"},calendarSync:{addMeetingURL:"Voeg \u2019n vergaderingskakel by",confirmAddLink:"Wil u \u2019n Jitsi-skakel by di\xe9 geleentheid voeg?",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"Sluit aan",joinTooltip:"Sluit aan by die vergadering",nextMeeting:"volgende vergadering",noEvents:"Geen komende geleenthede is geskeduleer nie.",ongoingMeeting:"vergadering onderweg",permissionButton:"Open instellings",permissionMessage:"",refresh:"Verfris kalender",today:"Vandag"},chat:{error:"",messagebox:"",nickname:{popover:"Kies \u2019n bynaam",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"",AUTHENTICATING:"Verifieer",AUTHFAIL:"",CONNECTED:"Gekoppel",CONNECTING:"Koppel tans",CONNFAIL:"Koppeling het misluk",DISCONNECTED:"Ontkoppeld",DISCONNECTING:"Ontkoppel tans",ERROR:"Fout",RECONNECTING:"\u2019n Netwerkprobleem het voorgekom. Herkoppel tans..."},connectionindicator:{address:"Adres:",bandwidth:"Geraamde bandwydte:",bitrate:"Bistempo:",bridgeCount:"Aantal bedieners: ",connectedTo:"Gekoppel aan:",framerate:"Raampietempo:",less:"Wys minder",localaddress:"Plaaslike adres:",localaddress_plural:"Plaaslike adresse:",localport:"Plaaslike poort:",localport_plural:"Plaaslike poorte:",more:"Wys meer",packetloss:"Pakkies verloor:",quality:{good:"Goed",inactive:"Onaktief",lost:"",nonoptimal:"",poor:"Swak"},remoteaddress:"Afgele\xeb adres:",remoteaddress_plural:"Afgele\xeb adresse:",remoteport:"Afgele\xeb poort:",remoteport_plural:"Afgele\xeb poorte:",resolution:"Resolusie:",status:"Verbinding:",transport:"",transport_plural:"",turn:""},dateUtils:{earlier:"Vroe\xebr",today:"Vandag",yesterday:"Gister"},deepLinking:{appNotInstalled:"",description:"",descriptionWithoutWeb:"",downloadApp:"",launchWebButton:"",openApp:"",title:"",tryAgainButton:""},defaultLink:"bv. {{url}}",deviceError:{cameraError:"Toegang na u kamera het misluk",cameraPermission:"Fout met verkryging van kameratoestemming",microphoneError:"Toegang na u mikrofoon het misluk",microphonePermission:"Fout met verkryging van mikrofoontoestemming"},deviceSelection:{noPermission:"Toestemming nie gegee nie",previewUnavailable:"Voorskou nie beskikbaar nie",selectADevice:"Kies 'n toestel",testAudio:"Speel \u2019n toetsklank"},dialog:{accessibilityLabel:{liveStreaming:"Regstreekse stroom"},allow:"Laat toe",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Slegs een gedeelde video op \u2019n slag word toegelaat",applicationWindow:"Toepassingsvenster",Back:"Terug",cameraConstraintFailedError:"Die kamera voldoen nie aan sekere van die vereistes nie.",cameraNotFoundError:"Kamera is nie gevind nie.",cameraNotSendingData:"Ons kry nie toegang tot u kamera nie. Kontroleer of \u2019n ander toepassing di\xe9 toestel gebruik, kies asb. \u2019n ander toestel by die instellingskieslys of probeer om die toepassing op nuut te laai.",cameraNotSendingDataTitle:"Kan nie toegang tot kamera kry nie",cameraPermissionDeniedError:"U het nie toestemming gegee om u kamera te gebruik nie. U kan steeds by die konferensie aansluit, maar ander sal u nie kan sien nie. Gebruik die kameraknoppie in die adresbalk om dit reg te stel.",cameraUnknownError:"Kan weens onbekende rede nie die kamera gebruik nie.",cameraUnsupportedResolutionError:"Die kamera ondersteun nie die nodige videoresolusie nie.",Cancel:"Kanselleer",close:"Sluit",conferenceDisconnectMsg:"Kontroleer dalk die netwerkverbinding. Gaan oor {{seconds}} sekondes weer koppel...",conferenceDisconnectTitle:"Die verbinding is verbreek.",conferenceReloadMsg:"Ons probeer om dit reg te stel. Gaan herkoppel oor {{seconds}} sekondes...",conferenceReloadTitle:"Iets het ongelukkig skeefgeloop.",confirm:"Bevestig",confirmNo:"Nee",confirmYes:"Ja",connectError:"Oeps! Iets het skeefgeloop en ons kon nie aan die konferensie koppel nie.",connectErrorWithMsg:"Oeps! Iets het skeefgeloop en ons kon nie aan die konferensie koppel nie: {{msg}}",connecting:"Koppel tans",contactSupport:"Kontak ondersteuning",copy:"Kopieer",dismiss:"Weier",displayNameRequired:"",done:"Klaar",enterDisplayName:"",error:"Fout",externalInstallationMsg:"U moet ons uitbreiding vir werkskermdeling installeer.",externalInstallationTitle:"Uitbreiding is nodig",goToStore:"Gaan na die webwinkel",gracefulShutdown:"Ons diens is tans buite werking t.w.v. onderhoud. Probeer gerus weer later.",IamHost:"Ek is die gasheer",incorrectRoomLockPassword:"",incorrectPassword:"Verkeerde gebruikernaam of wagwoord",inlineInstallationMsg:"U moet ons uitbreiding vir werkskermdeling installeer.",inlineInstallExtension:"Installeer nou",internalError:"Oeps! Iets het skeefgeloop. Die volgende fout het voorgekom: {{error}}",internalErrorTitle:"Interne fout",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Regstreekse stroom",liveStreamingDisabledForGuestTooltip:"Gaste kan nie regstreekse strome begin nie.",liveStreamingDisabledTooltip:"Begin van regstreekse stroom gedeaktiveer.",lockMessage:"Kon nie die konferensie sluit nie.",lockRoom:"",lockTitle:"Sluit het misluk",logoutQuestion:"Wil u definitief afmeld en die konferensie stop?",logoutTitle:"Meld af",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Die mikrofoon voldoen nie aan sekere van die vereistes nie.",micNotFoundError:"Mikrofoon is nie gevind nie.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"U het nie toestemming gegee om u mikrofoon te gebruik nie. U kan steeds by die konferensie aansluit, maar ander sal u nie kan hoor nie. Gebruik die kameraknoppie in die adresbalk om dit reg te stel.",micUnknownError:"Kan weens onbekende rede nie die mikrofoon gebruik nie.",muteParticipantBody:"U sal hulle nie kan ontdemp nie, maar hulle sal hulself enige tyd kan ontdemp.",muteParticipantButton:"Demp",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"Regso",passwordLabel:"",passwordNotSupported:"Die instel van \u2019n vergaderingwagwoord word nie ondersteun nie.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"U blaaier blokkeer opspringers vanaf hierdie werf. Aktiveer opspringers in die blaaier se sekuriteitopsies en probeer weer.",popupErrorTitle:"Opspringer geblok",recording:"Neem tans op",recordingDisabledForGuestTooltip:"Gaste kan nie opnames begin nie.",recordingDisabledTooltip:"Begin van opname gedeaktiveer.",rejoinNow:"Sluit nou weer aan",remoteControlAllowedMessage:"",remoteControlDeniedMessage:"",remoteControlErrorMessage:"",remoteControlRequestMessage:"",remoteControlShareScreenWarning:"",remoteControlStopMessage:"",remoteControlTitle:"",Remove:"Verwyder",removePassword:"",removeSharedVideoMsg:"Wil u definitief u gedeelde video verwyder?",removeSharedVideoTitle:"Verwyder gedeelde video",reservationError:"",reservationErrorMsg:"Foutkode: {{code}}, boodskap: {{msg}}",retry:"Herprobeer",screenSharingFailedToInstall:"Oeps! Die uitbreiding vir skermdeling kon nie installeer nie.",screenSharingFailedToInstallTitle:"Uitbreiding vir skermdeling kon nie installeer nie",screenSharingFirefoxPermissionDeniedError:"Iets het skeefgeloop toe ons die skerm probeer deel het. Maak seker dat ons dei toestemming gegee word om dit te doen. ",screenSharingFirefoxPermissionDeniedTitle:"Oeps! Ons kon nie skermdeling begin nie!",screenSharingPermissionDeniedError:"",serviceUnavailable:"Diens nie beskikbaar nie",sessTerminated:"Oproep gestaak",Share:"Deel",shareVideoLinkError:"Gee asb. \u2019n korrekte YouTube-skakel.",shareVideoTitle:"Deel \u2019n video",shareYourScreen:"Deel u skerm",shareYourScreenDisabled:"Skermdeling gedeaktiveer.",shareYourScreenDisabledForGuest:"Gaste kan nie skerms deel nie.",startLiveStreaming:"Begin regstreekse stroom",startRecording:"Begin opname",startRemoteControlErrorMessage:"",stopLiveStreaming:"Stop regstreekse stroom",stopRecording:"Stop opname",stopRecordingWarning:"Wil u definitief die opname stop?",stopStreamingWarning:"Wil u definitief die regstreekse stroom stop?",streamKey:"Sleutel vir regstreekse stroom",Submit:"Dien in",thankYou:"Dankie dat u {{appName}} gebruik!",token:"",tokenAuthFailed:"Jammer! U mag nie by di\xe9 oproep aansluit nie.",tokenAuthFailedTitle:"",transcribing:"Transkribering",unlockRoom:"",userPassword:"gebruikerwagwoord",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"Wag tans vir die gasheer ...",Yes:"Ja",yourEntireScreen:"U hele skerm"},dialOut:{statusMessage:"is nou {{status}}"},feedback:{average:"Gemiddeld",bad:"Sleg",detailsLabel:"Vertel ons meer.",good:"Goed",rateExperience:"",veryBad:"Baie sleg",veryGood:"Baie goed"},incomingCall:{answer:"Antwoord",audioCallTitle:"Inkomende oproep",decline:"Weier",productLabel:"vanaf Jitsi Meet",videoCallTitle:"Inkomende video-oproep"},info:{accessibilityLabel:"Wys inligting",addPassword:"",cancelPassword:"",conferenceURL:"Skakel:",country:"Land",dialANumber:"",dialInConferenceID:"PIN:",dialInNotSupported:"Jammer. Inbel word nie tans ondersteun nie.",dialInNumber:"Inbel:",dialInSummaryError:"",dialInTollFree:"",genericError:"Oeps! Iets het skeefgeloop.",inviteLiveStream:"Om die regstreekse stroom van di\xe9 vergadering te sien, klik di\xe9 skakel: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Regstreekse stroom:",moreNumbers:"Meer nommers",noNumbers:"Geen inbelnommers.",noPassword:"Geen",noRoom:"Geen kamer is gegee om na in te bel nie.",numbers:"Inbelnommers",password:"",title:"Deel",tooltip:"Deelskakel en inbelinligting vir di\xe9 vergadering",label:""},inviteDialog:{alertText:"",header:"Nooi uit",searchCallOnlyPlaceholder:"",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"Ons het gestruikel.",retry:"Probeer weer",support:"Ondersteuning",supportMsg:"Indien dit aanhou, maak kontak met"},keyboardShortcuts:{focusLocal:"Fokus op u video",focusRemote:"Fokus op \u2019n ander persoon se video",fullScreen:"Bekyk of verlaat volskerm",keyboardShortcuts:"Sleutelbordkortpaaie",localRecording:"Wys of versteek kontroles vir plaaslike opname",mute:"Demp of ontdemp jou mikrofoon",pushToTalk:"Druk om te praat",raiseHand:"Steek hand op of laat sak hom",showSpeakerStats:"Wys sprekerstatistiek",toggleChat:"Maak gesels oop of toe",toggleFilmstrip:"Wys of versteek duimnaels vir video\u2019s",toggleScreensharing:"Wissel tussen kamera- en skermdeling",toggleShortcuts:"Wys of versteek sleutelbordkortpaaie",videoMute:"Begin of stop u kamera"},liveStreaming:{busy:"",busyTitle:"Alle opnemers is tans besig",changeSignIn:"Wissel rekeninge.",choose:"Kies \u2019n regstreekse stroom",chooseCTA:"Kies \u2019n stroomopsie. U is tans aangemeld as {{email}}.",enterStreamKey:"Gee u sleutel vir regstreekse stroom by YouTube hier.",error:"Kon nie regstreeks stroom nie. Probeer gerus weer.",errorAPI:"\u2019n Fout het voorgekom tydens toegang tot u YouTube-uitsendings. Probeer om weer aan te meld.",errorLiveStreamNotEnabled:"Regstreekse stroom is nie geaktiveer op {{email}} nie. Aktiveer asb. regstreekse strome of meld aan met \u2019n rekening met regstreekse strome geaktiveer.",expandedOff:"Die regstreekse stroom het gestop",expandedOn:"Die vergadering word tans gestroom na YouTube.",expandedPending:"Die regstreekse stroom begin tans...",failedToStart:"Regstreekse stroom kon nie begin nie",getStreamKeyManually:"",invalidStreamKey:"",off:"Regstreekse stroom het gestop",on:"Regstreekse stroom",pending:"Begin tans regstreekse stroom...",serviceName:"Regstreekse stroomdiens",signedInAs:"U is tans aangemeld as:",signIn:"Meld aan met Google",signInCTA:"Meld aan of gee u sleutel vir regstreekse stroom vanaf YouTube.",signOut:"Meld af",start:"Begin \u2019n regstreekse stroom",streamIdHelp:"Wat\u2019s di\xe9?",unavailableTitle:"Regstreekse strome nie beskikbaar nie"},localRecording:{clientState:{off:"Af",on:"Aan",unknown:"Onbekend"},dialogTitle:"Kontroles vir plaaslike opname",duration:"Duur",durationNA:"",encoding:"Enkodering",label:"",labelToolTip:"",localRecording:"Plaaslike opname",me:"Ek",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:"U is nie die moderator nie. U kan nie \u2019n plaaslike opname begin of stop nie."},moderator:"Moderator",no:"Nee",participant:"Deelnemer",participantStats:"Deelnemerstatistiek",sessionToken:"",start:"Begin opname",stop:"Stop opname",yes:"Ja"},lockRoomPassword:"Wagwoord",lockRoomPasswordUppercase:"Wagwoord",me:"ek",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"ontkoppel",focus:"",focusFail:"",grantedTo:"{{to}} is nou moderator!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Ek",moderator:"U is ou moderator!",muted:"U het die gesprek gedemp begin.",mutedTitle:"U is gedemp!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"Iemand",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Gits... ons is bevrees u ervaring met {{appName}} gaan nie so goed wees hier nie. Ons soek maniere om dit die hoof te bied, maar probeer intussen een van die volledig ondersteunde blaaiers.",suboptimalExperienceTitle:"Blaaierwaarskuwing",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"aangedryf deur",presenceStatus:{busy:"Besig",calling:"Bel tans...",connected:"Gekoppel",connecting:"Koppel tans...",connecting2:"Koppel tans*...",disconnected:"Ontkoppeld",expired:"Verval",ignored:"Ge\xefgnoreer",initializingCall:"Inisialiseer tans oproep...",invited:"Uitgenooi",rejected:"Geweier",ringing:"Lui tans..."},profile:{setDisplayNameLabel:"Stel u vertoonnaam",setEmailInput:"Gee e-posadres",setEmailLabel:"Stel u gravatar-e-posadres",title:"Profiel"},recording:{authDropboxText:"Laai op na Dropbox",availableSpace:"Beskikbare spasie: {{spaceLeft}} MB (ongeveer {{duration}} minute se opname)",beta:"",busy:"",busyTitle:"Alle opnemers is tans besig",error:"Opname het misluk. Probeer gerus weer.",expandedOff:"Opname het gestop",expandedOn:"Die vergadering word tans opgeneem.",expandedPending:"Opname word begin...",failedToStart:"Kon nie begin opneem nie",fileSharingdescription:"",live:"",loggedIn:"Aangemeld as {{name}}",off:"Opname gestop",on:"Neem tans op",pending:"Berei voor om vergadering op te neem...",rec:"",serviceDescription:"",serviceName:"Opneemdiens",signIn:"meld aan",signOut:"Meld af",unavailable:"",unavailableTitle:""},sectionList:{pullToRefresh:""},settings:{calendar:{about:"",disconnect:"Ontkoppel",microsoftSignIn:"Meld aan met Microsoft",signedIn:"",title:"Kalender"},devices:"Toestelle",followMe:"Almal volg my",language:"Taal",loggedIn:"Aangemeld as {{name}}",moderator:"Moderator",more:"Meer",name:"Naam",noDevice:"Geen",selectAudioOutput:"Klankafvoer",selectCamera:"Kamera",selectMic:"Mikrofoon",startAudioMuted:"Almal begin gedemp",startVideoMuted:"Almal begin versteek",title:"Instellings"},settingsView:{alertOk:"Regso",alertTitle:"Waarskuwing",alertURLText:"Die gegewe bediener-URL is ongeldig",buildInfoSection:"",conferenceSection:"Konferensie",displayName:"Vertoonnaam",email:"E-pos",header:"Instellings",profileSection:"Profiel",serverURL:"Bediener-URL",startWithAudioMuted:"Begin met klank gedemp",startWithVideoMuted:"Begin met video gedemp",version:""},share:{dialInfoText:"",mainText:"Klik die volgende skakel om by die vergadering aan te sluit:\n{{roomUrl}}"},speaker:"Luidspreker",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Naam",seconds:"{{count}}s",speakerStats:"Sprekerstatistiek",speakerTime:"Sprekertyd"},startupoverlay:{policyText:"",title:"{{app}} benodig u mikrofoon en kamera."},suspendedoverlay:{rejoinKeyTitle:"Sluit weer aan",text:"Druk die Sluit weer aan-knoppie om te herkoppel.",title:"U video-oproep is onderbreek omdat die rekenaar gaan slaap het."},toolbar:{accessibilityLabel:{audioOnly:"Wissel Net klank",audioRoute:"Kies die klanktoestel",callQuality:"",cc:"Wissel onderskrifte",chat:"Wissel geselsvenster",document:"Wissel gedeelde dokument",feedback:"Laat terugvoer",fullScreen:"Wissel volskerm",hangup:"Verlaat die oproep",invite:"Nooi mense",kick:"",localRecording:"Wissel kontroles vir plaaslike opname",lockRoom:"",moreActions:"Wissel kieslys vir meer aksies",moreActionsMenu:"Kieslys vir meer aksies",mute:"",pip:"Wissel Prent-in-Prent-modus",profile:"Redigeer u profiel",raiseHand:"Wissel handopsteek",recording:"Wissel opname",remoteMute:"",Settings:"Wissel instellings",sharedvideo:"Wissel Youtube-videodeling",shareRoom:"Nooi iemand",shareYourScreen:"Wissel skermdeling",shortcuts:"Wissel kortpaaie",show:"",speakerStats:"Wissel sprekerstatistiek",tileView:"Wissel te\xeblaansig",toggleCamera:"Wissel kamera",videomute:"",videoblur:""},addPeople:"Voeg mense by die oproep",audioOnlyOff:"Deaktiveer Net klank-modus",audioOnlyOn:"Deaktiveer Net klank-modus",audioRoute:"Kies die klanktoestel",authenticate:"Verifieer",callQuality:"Bestuur oproepkwaliteit",chat:"Open / sluit gesels",closeChat:"",documentClose:"Sluit gedeelde dokument",documentOpen:"Open gedeelde dokument",enterFullScreen:"Volskermaansig",enterTileView:"",exitFullScreen:"Verlaat volskerm",exitTileView:"",feedback:"Laat terugvoer",hangup:"Verlaat",invite:"Nooi mense",login:"Meld aan",logout:"Meld af",lowerYourHand:"",moreActions:"Meer aksies",mute:"Demp / ontdemp",openChat:"",pip:"Betree Prent-in-Prent-modus",profile:"Redigeer u profiel",raiseHand:"Lig / laat sak u hand",raiseYourHand:"",Settings:"Instellings",sharedvideo:"Deel \u2019n YouTube-video",shareRoom:"Nooi iemand",shortcuts:"Sien kortpaaie",speakerStats:"Sprekerstatistiek",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"Stop YouTube-video",talkWhileMutedPopup:"Besig om te praat? U is gedemp.",tileViewToggle:"Wissel te\xeblaansig",toggleCamera:"Wissel kamera",videomute:"Begin / stop kamera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"",error:"Opname het misluk. Probeer gerus weer.",expandedLabel:"",failedToStart:"",labelToolTip:"Die vergadering word getranskribeer",off:"",pending:"Berei tans voor om die vergadering te transkribeer...",start:"",stop:"",tr:""},userMedia:{androidGrantPermissions:"Kies Allow wanneer die blaaier vir toestemming vra.",chromeGrantPermissions:"Kies Allow wanneer die blaaier vir toestemming vra.",edgeGrantPermissions:"Kies Yes wanneer die blaaier vir toestemming vra.",electronGrantPermissions:"Gee asb. toestemming vir die gebruik van u kamera en mikrofoon",firefoxGrantPermissions:"Kies Deel gekose toestel wanneer die blaaier vir toestemming vra.",iexplorerGrantPermissions:"Kies OK wanneer die blaaier vir toestemming vra.",nwjsGrantPermissions:"Gee asb. toestemming vir die gebruik van u kamera en mikrofoon",operaGrantPermissions:"Kies Allow wanneer die blaaier vir toestemming vra.","react-nativeGrantPermissions":"Kies Allow wanneer die blaaier vir toestemming vra.",safariGrantPermissions:"Kies OK wanneer die blaaier vir toestemming vra."},videoSIPGW:{busy:"Ons probeer tans hulpbronne vry te stel. Probeer gerus weer oor \u2019n paar minute.",busyTitle:"Die Kamerdiens is tans besig",errorAlreadyInvited:"{{displayName}} is reeds genooi",errorInvite:"Konferensie is nog nie gestig nie. Probeer gerus weer later.",errorInviteFailed:"Ons werk aan \u2019n oplossing vir die probleem. Probeer gerus weer later.",errorInviteFailedTitle:"Kon nie {{displayName}} nooi nie",errorInviteTitle:"",pending:"{{displayName}} is genooi"},videoStatus:{audioOnly:"",audioOnlyExpanded:"U is in Net klank-modus. Di\xe9 modus spaar bandwydte maar u sal nie video\u2019s van ander sien nie.",callQuality:"",hd:"HD",highDefinition:"Ho\xebdefinisie",labelTooiltipNoVideo:"Geen video",labelTooltipAudioOnly:"Net klank-modus geaktiveer",ld:"LD",lowDefinition:"Laedefinisie",onlyAudioAvailable:"Net klank is beskikbaar",onlyAudioSupported:"Op di\xe9 blaaier ondersteun ons slegs klank.",p2pEnabled:"",p2pVideoQualityDescription:"",recHighDefinitionOnly:"",sd:"SD",standardDefinition:"Standaarddefinisie"},videothumbnail:{domute:"Demp",flip:"Swaai om",kick:"Skop uit",moderator:"Moderator",mute:"",muted:"Gedemp",remoteControl:"",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Raak om aan te sluit",roomname:"Gee kamernaam"},appDescription:"Hou gerus \u2019n videogesprek met die hele span. Om die waarheid te s\xea, nooi sommer almal. {{app}} is \u2019n 100% oopbronoplossing vir ge\xebnkripteerde videokonferensies wat mens heeldag, elke dag gratis kan geniet \u2014 geen rekening nodig nie.",audioVideoSwitch:{audio:"Stem",video:"Video"},calendar:"Kalender",connectCalendarButton:"Koppel u kalender",connectCalendarText:"",enterRoomTitle:"Begin \u2019n nuwe vergadering",go:"GAAN",join:"SLUIT AAN",info:"",privacy:"Privaatheid",recentList:"Onlangs",recentListDelete:"Skrap",recentListEmpty:"Die lys van onlangse gesprekke is leeg. Gesels met u span en al u onlangse gesprekke sal hier wys.",reducedUIText:"",roomname:"Gee kamernaam",roomnameHint:"Gee die naam of URL van die kamer waar u wil aansluit. Dink gerus enige naam uit. Laat weet net die mense wat u ontmoet wat dit is sodat hulle die selfde naam gee.",sendFeedback:"Stuur terugvoer",terms:"Voorwaardes",title:"Veilige en volledig gratis videokonferensies propvol funksionaliteit"}}},640,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"\u0410\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438",af:"\u0410\u0444\u0440\u0438\u043a\u0430\u043d\u0441",az:"\u0410\u0437\u0435\u0440\u0431\u0430\u0439\u0434\u0436\u0430\u043d\u0441\u043a\u0438",bg:"\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438",cs:"\u0427\u0435\u0448\u043a\u0438",de:"\u041d\u0435\u043c\u0441\u043a\u0438",el:"\u0413\u0440\u044a\u0446\u043a\u0438",eo:"\u0415\u0441\u043f\u0435\u0440\u0430\u043d\u0442\u043e",es:"\u0418\u0441\u043f\u0430\u043d\u0441\u043a\u0438",fr:"\u0424\u0440\u0435\u043d\u0441\u043a\u0438",hy:"\u0410\u0440\u043c\u0435\u043d\u0441\u043a\u0438",it:"\u0418\u0442\u0430\u043b\u0438\u0430\u043d\u0441\u043a\u0438",ja:"\u042f\u043f\u043e\u043d\u0441\u043a\u0438",ko:"\u041a\u043e\u0440\u0435\u0439\u0441\u043a\u0438",nb:"\u041d\u043e\u0440\u0432\u0435\u0436\u043a\u0438 \u0431\u0443\u043a\u043c\u043e\u043b",oc:"\u041e\u043a\u0441\u0438\u0442\u0430\u043d\u0441\u043a\u0438",pl:"\u041f\u043e\u043b\u0441\u043a\u0438",ptBR:"\u041f\u043e\u0440\u0442\u0443\u0433\u0430\u043b\u0441\u043a\u0438 (\u0411\u0440\u0430\u0437\u0438\u043b\u0438\u044f)",ru:"\u0420\u0443\u0441\u043a\u0438",sk:"\u0421\u043b\u043e\u0432\u0430\u0448\u043a\u0438",sl:"\u0421\u043b\u043e\u0432\u0435\u043d\u0441\u043a\u0438",sv:"\u0428\u0432\u0435\u0434\u0441\u043a\u0438",tr:"\u0422\u0443\u0440\u0441\u043a\u0438",vi:"\u0412\u0438\u0435\u0442\u043d\u0430\u043c\u0441\u043a\u0438",zhCN:"\u041a\u0438\u0442\u0430\u0439\u0441\u043a\u0438 (\u041a\u0438\u0442\u0430\u0439)"}},641,[]); -__d(function(e,o,i,r,t,n,a){t.exports={addPeople:{add:"\u041f\u043e\u043a\u0430\u043d\u0438",countryNotSupported:"\u0416\u0435\u043b\u0430\u043d\u0430\u0442\u0430 \u0434\u0435\u0441\u0442\u0438\u043d\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.",countryReminder:"\u041c\u0435\u0436\u0434\u0443\u043d\u0430\u0440\u043e\u0434\u043d\u043e \u043e\u0431\u0430\u0436\u0434\u0430\u043d\u0435? \u0417\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u043e\u043c\u0435\u0440\u0430 \u0441 \u043c\u0435\u0436\u0434\u0443\u043d\u0430\u0440\u043e\u0434\u043d\u0438\u044f\u0442 \u043a\u043e\u0434!",disabled:"\u041d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u043a\u0430\u043d\u0438\u0442\u0435 \u0445\u043e\u0440\u0430.",failedToAdd:"",footerText:"\u0418\u0437\u0445\u043e\u0434\u044f\u0449\u0438\u0438\u0442\u0435 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u0438 \u043d\u0435 \u0441\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438.",loading:"\u0422\u044a\u0440\u0441\u0435\u043d\u0435 \u043d\u0430 \u0445\u043e\u0440\u0430 \u0438 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u043d\u0438 \u043d\u043e\u043c\u0435\u0440\u0430.",loadingNumber:"\u0412\u0430\u043b\u0438\u0434\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043d\u043e\u043c\u0435\u0440\u0430",loadingPeople:"\u0422\u044a\u0440\u0441\u0435\u043d\u0435 \u043d\u0430 \u0445\u043e\u0440\u0430",noResults:"\u041d\u044f\u043c\u0430 \u0440\u0435\u0437\u0443\u043b\u0442\u0430\u0442\u0438",noValidNumbers:"\u041c\u043e\u043b\u044f \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435\u043d \u043d\u043e\u043c\u0435\u0440",searchNumbers:"\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u043d\u043e\u043c\u0435\u0440\u0430",searchPeople:"\u0422\u044a\u0440\u0441\u0435\u043d\u0435 \u043d\u0430 \u0445\u043e\u0440\u0430",searchPeopleAndNumbers:"",telephone:"",title:""},audioDevices:{bluetooth:"",headphones:"\u0421\u043b\u0443\u0448\u0430\u043b\u043a\u0438",phone:"\u0422\u0435\u043b\u0435\u0444\u043e\u043d",speaker:"\u0413\u043e\u0432\u043e\u0440\u0435\u0449"},audioOnly:{audioOnly:"\u0421\u0430\u043c\u043e \u0437\u0432\u0443\u043a"},calendarSync:{addMeetingURL:"",confirmAddLink:"",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"",joinTooltip:"",nextMeeting:"",noEvents:"",ongoingMeeting:"",permissionButton:"",permissionMessage:"",refresh:"",today:""},chat:{error:"",messagebox:"",nickname:{popover:"\u0418\u0437\u0431\u043e\u0440 \u043d\u0430 \u0438\u043c\u0435",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"\u041f\u0440\u0438\u043a\u0440\u0435\u043f\u0435\u043d",AUTHENTICATING:"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f",AUTHFAIL:"\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f",CONNECTED:"\u0421\u0432\u044a\u0440\u0437\u0430\u043d",CONNECTING:"\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",CONNFAIL:"\u0412\u0440\u044a\u0437\u043a\u0430\u0442\u0430 \u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430",DISCONNECTED:"\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d",DISCONNECTING:"\u041f\u0440\u0435\u043a\u044a\u0441\u0432\u0430\u043d\u0435 \u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0430\u0442\u0430",ERROR:"\u0413\u0440\u0435\u0448\u043a\u0430",RECONNECTING:"\u041f\u043e\u044f\u0432\u0438 \u0441\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043c\u0440\u0435\u0436\u0430\u0442\u0430. \u0412\u0440\u044a\u0437\u0432\u0430\u043c\u0435 \u0441\u0435 \u043d\u0430\u043d\u043e\u0432\u043e..."},connectionindicator:{address:"\u0410\u0434\u0440\u0435\u0441:",bandwidth:"\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442:",bitrate:"\u0421\u043a\u043e\u0440\u043e\u0441\u0442:",bridgeCount:"",connectedTo:"",framerate:"\u041a\u0430\u0434\u0440\u0438 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430:",less:"\u0421\u043a\u0440\u0438\u0432\u0430\u043d\u0435",localaddress:"\u041b\u043e\u043a\u0430\u043b\u0435\u043d \u0430\u0434\u0440\u0435\u0441:",localaddress_plural:"\u041b\u043e\u043a\u0430\u043b\u043d\u0438 \u0430\u0434\u0440\u0435\u0441\u0438:",localport:"\u041b\u043e\u043a\u0430\u043b\u0435\u043d \u043f\u043e\u0440\u0442:",localport_plural:"\u041b\u043e\u043a\u0430\u043b\u043d\u0438 \u043f\u043e\u0440\u0442\u043e\u0432\u0435:",more:"\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435",packetloss:"\u0417\u0430\u0433\u0443\u0431\u0430 \u043d\u0430 \u043f\u0430\u043a\u0435\u0442\u0438:",quality:{good:"\u0414\u043e\u0431\u0440\u0430",inactive:"\u041d\u044f\u043c\u0430",lost:"\u0420\u0430\u0437\u043a\u0430\u0447\u0435\u043d\u0430",nonoptimal:"\u041d\u0435\u043e\u043f\u0442\u0438\u043c\u0430\u043b\u043d\u0430",poor:"\u041b\u043e\u0448\u0430"},remoteaddress:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d \u0430\u0434\u0440\u0435\u0441:",remoteaddress_plural:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u0438 \u0430\u0434\u0440\u0435\u0441\u0438:",remoteport:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d \u043f\u043e\u0440\u0442:",remoteport_plural:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u0438 \u043f\u043e\u0440\u0442\u043e\u0432\u0435:",resolution:"\u0420\u0435\u0437\u043e\u043b\u044e\u0446\u0438\u044f:",status:"\u0412\u0440\u044a\u0437\u043a\u0430:",transport:"\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442:",transport_plural:"\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0438:",turn:" (\u043e\u0431\u0440\u044a\u0449\u0430\u043d\u0435)"},dateUtils:{earlier:"",today:"",yesterday:""},deepLinking:{appNotInstalled:"",description:"",descriptionWithoutWeb:"",downloadApp:"\u0421\u0432\u0430\u043b\u044f\u043d\u0435 \u043d\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e",launchWebButton:"",openApp:"",title:"",tryAgainButton:""},"\x05deepLinking":{},defaultLink:"\u043d\u0430\u043f\u0440. {{url}}",deviceError:{cameraError:"\u041a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u044a\u043f\u043d\u0430",cameraPermission:"\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430",microphoneError:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u044a\u0442 \u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u044a\u043f\u0435\u043d",microphonePermission:"\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430"},deviceSelection:{noPermission:"\u041d\u0435 \u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435",previewUnavailable:"\u041d\u044f\u043c\u0430 \u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442 \u0437\u0430 \u043f\u0440\u0435\u0433\u043b\u0435\u0434",selectADevice:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",testAudio:""},dialog:{accessibilityLabel:{liveStreaming:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e"},allow:"\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0435 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435\u0442\u043e \u0441\u0430\u043c\u043e \u043d\u0430 \u0435\u0434\u043d\u043e \u0432\u0438\u0434\u0435\u043e \u0432 \u0434\u0430\u0434\u0435\u043d \u043c\u043e\u043c\u0435\u043d\u0442",applicationWindow:"\u041f\u0440\u043e\u0437\u043e\u0440\u0435\u0446\u0430 \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430",Back:"\u041d\u0430\u0437\u0430\u0434",cameraConstraintFailedError:"\u041a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0412\u0438 \u043d\u0435 \u043f\u043e\u043a\u0440\u0438\u0432\u0430 \u043d\u044f\u043a\u043e\u0438 \u043e\u0442 \u0438\u0437\u0438\u0441\u043a\u0432\u0430\u043d\u0438\u044f\u0442\u0430.",cameraNotFoundError:"\u041d\u0435 \u0435 \u043e\u0442\u043a\u0440\u0438\u0442\u0430 \u043a\u0430\u043c\u0435\u0440\u0430.",cameraNotSendingData:"\u041a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u044a\u043f\u043d\u0430. \u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0434\u0430\u043b\u0438 \u0434\u0440\u0443\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u0442\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0434\u0440\u0443\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0442 \u043c\u0435\u043d\u044e\u0442\u043e \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435, \u0438\u043b\u0438 \u043f\u0440\u0435\u0437\u0430\u0440\u0435\u0434\u0435\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e.",cameraNotSendingDataTitle:"\u041a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u044a\u043f\u043d\u0430",cameraPermissionDeniedError:"\u041d\u0435 \u0441\u0442\u0435 \u0434\u0430\u043b\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430. \u0429\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0441\u0435 \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u0435 \u0432 \u0431\u0435\u0441\u0435\u0434\u0430\u0442\u0430, \u043d\u043e \u0434\u0440\u0443\u0433\u0438\u0442\u0435 \u043d\u044f\u043c\u0430 \u0434\u0430 \u0412\u0438 \u0432\u0438\u0436\u0434\u0430\u0442. \u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 \u0431\u0443\u0442\u043e\u043d\u0430 \u0441 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0432 \u0430\u0434\u0440\u0435\u0441\u043d\u0430\u0442\u0430 \u043b\u0435\u043d\u0442\u0430, \u0437\u0430 \u0434\u0430 \u043e\u043f\u0440\u0430\u0432\u0438\u0442\u0435 \u0442\u043e\u0432\u0430.",cameraUnknownError:"\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u043f\u043e \u043d\u0435\u044f\u0441\u043d\u0430 \u043f\u0440\u0438\u0447\u0438\u043d\u0430.",cameraUnsupportedResolutionError:"\u041a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0412\u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u043d\u0443\u0436\u043d\u0430\u0442\u0430 \u0440\u0435\u0437\u043e\u043b\u044e\u0446\u0438\u044f.",Cancel:"\u041e\u0442\u043a\u0430\u0437",close:"\u0417\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435",conferenceDisconnectMsg:"\u041c\u043e\u0436\u0435 \u0431\u0438 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u0435 \u043c\u0440\u0435\u0436\u043e\u0432\u0430\u0442\u0430 \u0441\u0438 \u0432\u0440\u044a\u0437\u043a\u0430. \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 {{seconds}} \u0441\u0435\u043a\u2026",conferenceDisconnectTitle:"\u0412\u0440\u044a\u0437\u043a\u0430\u0442\u0430 \u0441\u0435 \u0440\u0430\u0437\u043f\u0430\u0434\u043d\u0430.",conferenceReloadMsg:"\u041e\u043f\u0438\u0442\u0432\u0430\u043c\u0435 \u0441\u0435 \u0434\u0430 \u043e\u043f\u0440\u0430\u0432\u0438\u043c \u043d\u0435\u0449\u0430\u0442\u0430. \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 {{seconds}} \u0441\u0435\u043a\u2026",conferenceReloadTitle:"\u0417\u0430 \u0441\u044a\u0436\u0430\u043b\u0435\u043d\u0438\u0435, \u043d\u0435\u0449\u043e \u0441\u0435 \u043e\u0431\u044a\u0440\u043a\u0430.",confirm:"",confirmNo:"",confirmYes:"\u0414\u0430",connectError:"\u041e\u043f\u0430! \u041d\u0435\u0449\u043e \u0441\u0435 \u043e\u0431\u044a\u0440\u043a\u0430 \u0438 \u043d\u0435 \u0443\u0441\u043f\u044f\u0445\u043c\u0435 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u043c \u0441 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044f\u0442\u0430.",connectErrorWithMsg:"\u041e\u043f\u0430! \u041d\u0435\u0449\u043e \u0441\u0435 \u043e\u0431\u044a\u0440\u043a\u0430 \u0438 \u043d\u0435 \u0443\u0441\u043f\u044f\u0445\u043c\u0435 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u043c \u0441 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044f\u0442\u0430: {{msg}}",connecting:"\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",contactSupport:"\u0412\u0440\u044a\u0437\u043a\u0430 \u0441 \u043e\u0442\u0434\u0435\u043b\u0430 \u043f\u043e \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430",copy:"\u041a\u043e\u043f\u0438\u0440\u0430\u043d\u0435",dismiss:"\u041e\u0442\u0445\u0432\u044a\u0440\u043b\u044f\u043d\u0435",displayNameRequired:"",done:"\u0413\u043e\u0442\u043e\u0432\u043e",enterDisplayName:"",error:"\u0413\u0440\u0435\u0448\u043a\u0430",externalInstallationMsg:"\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u0442\u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430.",externalInstallationTitle:"\u041d\u0443\u0436\u043d\u043e \u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435",goToStore:"\u041a\u044a\u043c \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430 \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442",gracefulShutdown:"\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435 \u0435 \u0434\u043e\u0441\u0442\u044a\u043f\u043d\u0430 \u043f\u043e\u0440\u0430\u0434\u0438 \u043f\u0440\u043e\u0444\u0438\u043b\u0430\u043a\u0442\u0438\u043a\u0430. \u041c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043f\u043e-\u043a\u044a\u0441\u043d\u043e.",IamHost:"\u0410\u0437 \u0441\u044a\u043c \u0434\u043e\u043c\u0430\u043a\u0438\u043d\u0430",incorrectRoomLockPassword:"",incorrectPassword:"\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u043d\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435 \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u0430",inlineInstallationMsg:"\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u0442\u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430.",inlineInstallExtension:"\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u0441\u0435\u0433\u0430",internalError:"\u041e\u043f\u0430! \u041d\u0435\u0449\u043e \u0441\u0435 \u043e\u0431\u044a\u0440\u043a\u0430. \u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0441\u043b\u0435\u0434\u043d\u0430\u0442\u0430 \u0433\u0440\u0435\u0448\u043a\u0430: {{error}}",internalErrorTitle:"\u0412\u044a\u0442\u0440\u0435\u0448\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e",liveStreamingDisabledForGuestTooltip:"",liveStreamingDisabledTooltip:"",lockMessage:"\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044f\u0442\u0430.",lockRoom:"",lockTitle:"\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0432\u0430\u043d\u0435",logoutQuestion:"\u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u0438\u0437\u043b\u0435\u0437\u0435\u0442\u0435 \u0438 \u0434\u0430 \u043f\u0440\u0435\u043a\u044a\u0441\u043d\u0435\u0442\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044f\u0442\u0430?",logoutTitle:"\u0418\u0437\u0445\u043e\u0434",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u044a\u0442 \u0412\u0438 \u043d\u0435 \u043f\u043e\u043a\u0440\u0438\u0432\u0430 \u043d\u044f\u043a\u043e\u0438 \u043e\u0442 \u0438\u0437\u0438\u0441\u043a\u0432\u0430\u043d\u0438\u044f\u0442\u0430.",micNotFoundError:"\u041d\u0435 \u0435 \u043e\u0442\u043a\u0440\u0438\u0442 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\u041d\u0435 \u0441\u0442\u0435 \u0434\u0430\u043b\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430. \u0429\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0441\u0435 \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u0435 \u0432 \u0431\u0435\u0441\u0435\u0434\u0430\u0442\u0430, \u043d\u043e \u0434\u0440\u0443\u0433\u0438\u0442\u0435 \u043d\u044f\u043c\u0430 \u0434\u0430 \u0412\u0438 \u0447\u0443\u0432\u0430\u0442. \u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 \u0431\u0443\u0442\u043e\u043d\u0430 \u0441 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0432 \u0430\u0434\u0440\u0435\u0441\u043d\u0430\u0442\u0430 \u043b\u0435\u043d\u0442\u0430, \u0437\u0430 \u0434\u0430 \u043e\u043f\u0440\u0430\u0432\u0438\u0442\u0435 \u0442\u043e\u0432\u0430.",micUnknownError:"\u041d\u0435 \u0432\u044a\u0437\u043c\u043e\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430 \u043f\u043e \u043d\u0435\u044f\u0441\u043d\u0430 \u043f\u0440\u0438\u0447\u0438\u043d\u0430.",muteParticipantBody:"\u0412\u0438\u0435 \u043d\u044f\u043c\u0430 \u0434\u0430 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0441\u043f\u0440\u0435\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u0430\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430, \u043d\u043e \u0442\u043e\u0439 \u0449\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0433\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u0438 \u043f\u043e \u0432\u0441\u044f\u043a\u043e \u0432\u0440\u0435\u043c\u0435.",muteParticipantButton:"\u0418\u0437\u043a\u043b\u044e\u0447\u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"\u0413\u043e\u0442\u043e\u0432\u043e",passwordLabel:"",passwordNotSupported:"\u0417\u0430\u0434\u0430\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u043f\u0430\u0440\u043e\u043b\u0430 \u0437\u0430 \u0441\u0440\u0435\u0449\u0430\u0442\u0430 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\u0411\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u0430 \u0438\u0437\u0441\u043a\u0430\u0447\u0430\u0449\u0438\u0442\u0435 \u043f\u0440\u043e\u0437\u043e\u0440\u0446\u0438 \u043e\u0442 \u0442\u043e\u0437\u0438 \u0443\u0435\u0431 \u0441\u0430\u0439\u0442. \u041c\u043e\u043b\u044f, \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u0442\u0435 \u0438\u0437\u0441\u043a\u0430\u0447\u0430\u0449\u0438\u0442\u0435 \u043f\u0440\u043e\u0437\u043e\u0440\u0446\u0438 \u043e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442 \u043d\u0430 \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u0430 \u0441\u0438 \u0438 \u0441\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",popupErrorTitle:"\u0411\u043b\u043e\u043a\u0438\u0440\u0430\u043d \u0438\u0437\u0441\u043a\u0430\u0447\u0430\u0449 \u043f\u0440\u043e\u0437\u043e\u0440\u0435\u0446",recording:"\u0417\u0430\u043f\u0438\u0441",recordingDisabledForGuestTooltip:"",recordingDisabledTooltip:"",rejoinNow:"\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u044f\u0432\u0430\u043d\u0435 \u0441\u0435\u0433\u0430",remoteControlAllowedMessage:"{{user}} \u043f\u0440\u0438\u0435 \u0437\u0430\u044f\u0432\u043a\u0430\u0442\u0430 \u0412\u0438 \u0437\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435!",remoteControlDeniedMessage:"{{user}} \u043e\u0442\u043a\u0430\u0437\u0430 \u0437\u0430\u044f\u0432\u043a\u0430\u0442\u0430 \u0412\u0438 \u0437\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435!",remoteControlErrorMessage:"\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043e\u043f\u0438\u0442\u0430 \u0437\u0430 \u0438\u0441\u043a\u0430\u043d\u0430 \u043d\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0442 {{user}}!",remoteControlRequestMessage:"\u0429\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u0435 \u043b\u0438 \u043d\u0430 {{user}} \u0434\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0432\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440\u0430 \u0412\u0438?",remoteControlShareScreenWarning:"\u0410\u043a\u043e \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \u201e\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435\u201c, \u0449\u0435 \u0441\u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435 \u0435\u043a\u0440\u0430\u043d\u0430 \u0441\u0438!",remoteControlStopMessage:"\u0421\u0435\u0441\u0438\u044f\u0442\u0430 \u0437\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0438!",remoteControlTitle:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440\u0430",Remove:"\u041f\u0440\u0435\u043c\u0430\u0445\u0432\u0430\u043d\u0435",removePassword:"",removeSharedVideoMsg:"\u041d\u0430\u0438\u0441\u0442\u0438\u043d\u0430 \u043b\u0438 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d\u043e\u0442\u043e \u0441\u0438 \u0432\u0438\u0434\u0435\u043e?",removeSharedVideoTitle:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435\u0442\u043e \u043d\u0430 \u0432\u0438\u0434\u0435\u043e",reservationError:"\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0435\u0440\u0432\u0430\u0446\u0438\u0438",reservationErrorMsg:"\u0413\u0440\u0435\u0448\u043a\u0430 \u043d\u043e\u043c\u0435\u0440: {{code}}, \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u0435: {{msg}}",retry:"\u041f\u043e\u0432\u0442\u043e\u0440\u0435\u043d \u043e\u043f\u0438\u0442",screenSharingFailedToInstall:"\u041e\u043f\u0430! \u0420\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430 \u043d\u0435 \u0443\u0441\u043f\u044f \u0434\u0430 \u0441\u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430.",screenSharingFailedToInstallTitle:"\u0420\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430 \u043d\u0435 \u0443\u0441\u043f\u044f \u0434\u0430 \u0441\u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430",screenSharingFirefoxPermissionDeniedError:"",screenSharingFirefoxPermissionDeniedTitle:"",screenSharingPermissionDeniedError:"\u041e\u043f\u0430! \u041d\u0435\u0449\u043e \u0441\u0435 \u043e\u0431\u044a\u0440\u043a\u0430 \u0441 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0442\u0430 \u043d\u0430 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430. \u041c\u043e\u043b\u044f, \u043f\u0440\u0435\u0437\u0430\u0440\u0435\u0434\u0435\u0442\u0435 \u0438 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",serviceUnavailable:"\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u043d\u0435 \u0435 \u043d\u0430\u043b\u0438\u0447\u043d\u0430",sessTerminated:"\u0420\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u044a\u0442 \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0438",Share:"\u0421\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435",shareVideoLinkError:"\u041c\u043e\u043b\u044f \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u043a\u044a\u043c YouTube.",shareVideoTitle:"\u0421\u043f\u043e\u0434\u0435\u043b\u0438 \u0432\u0438\u0434\u0435\u043e",shareYourScreen:"\u0421\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430",shareYourScreenDisabled:"",shareYourScreenDisabledForGuest:"",startLiveStreaming:"\u0417\u0430\u043f\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e",startRecording:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0430",startRemoteControlErrorMessage:"\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043e\u043f\u0438\u0442\u0430 \u0437\u0430 \u0437\u0430\u043f\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0441\u0435\u0441\u0438\u044f\u0442\u0430 \u0437\u0430 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435!",stopLiveStreaming:"\u0421\u043f\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e",stopRecording:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0430",stopRecordingWarning:"\u041d\u0430\u0438\u0441\u0442\u0438\u043d\u0430 \u043b\u0438 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u0441\u043f\u0440\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0430?",stopStreamingWarning:"\u041d\u0430\u0438\u0441\u0442\u0438\u043d\u0430 \u043b\u0438 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u0441\u043f\u0440\u0435\u0442\u0435 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e?",streamKey:"",Submit:"\u0418\u0437\u043f\u0440\u0430\u0449\u0430\u043d\u0435",thankYou:"\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0438\u043c, \u0447\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0445\u0442\u0435 {{appName}}!",token:"\u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f",tokenAuthFailed:"\u0421\u044a\u0436\u0430\u043b\u044f\u0432\u0430\u043c\u0435, \u043d\u043e \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0441\u0435 \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u0435 \u043a\u044a\u043c \u0442\u043e\u0437\u0438 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440.",tokenAuthFailedTitle:"\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f",transcribing:"",unlockRoom:"",userPassword:"\u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u0430",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"\u0427\u0430\u043a\u0430\u043c\u0435 \u0434\u043e\u043c\u0430\u043a\u0438\u043d\u0430 ...",Yes:"\u0414\u0430",yourEntireScreen:"\u0426\u0435\u043b\u0438\u044f \u0435\u043a\u0440\u0430\u043d"},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:"\u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0435 {{status}}"},feedback:{average:"\u0421\u0440\u0435\u0434\u043d\u043e",bad:"\u041b\u043e\u0448\u043e",detailsLabel:"",good:"\u0414\u043e\u0431\u0440\u0430",rateExperience:"\u041c\u043e\u043b\u044f, \u043e\u0446\u0435\u043d\u0435\u0442\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0430 \u0441\u0440\u0435\u0449\u0430\u0442\u0430.",veryBad:"\u041c\u043d\u043e\u0433\u043e \u043b\u043e\u0448\u043e",veryGood:"\u041c\u043d\u043e\u0433\u043e \u0434\u043e\u0431\u0440\u0430"},"\x05feedback":{},incomingCall:{answer:"",audioCallTitle:"",decline:"\u041e\u0442\u0445\u0432\u044a\u0440\u043b\u044f\u043d\u0435",productLabel:"",videoCallTitle:""},info:{accessibilityLabel:"",addPassword:"",cancelPassword:"",conferenceURL:"",country:"",dialANumber:"",dialInConferenceID:"",dialInNotSupported:"",dialInNumber:"",dialInSummaryError:"",dialInTollFree:"",genericError:"",inviteLiveStream:"",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e",moreNumbers:"",noNumbers:"",noPassword:"\u041d\u044f\u043c\u0430",noRoom:"",numbers:"",password:"",title:"\u0421\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435",tooltip:"",label:""},"\x05info":{},inviteDialog:{alertText:"",header:"\u041f\u043e\u043a\u0430\u043d\u0438",searchCallOnlyPlaceholder:"\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435\u043d \u043d\u043e\u043c\u0435\u0440",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"\u0418\u043c\u0430\u0448\u0435 \u0433\u0440\u0435\u0448\u043a\u0430.",retry:"\u041e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e",support:"\u041f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430",supportMsg:"\u0410\u043a\u043e \u0442\u043e\u0432\u0430 \u0441\u0435 \u0441\u043b\u0443\u0447\u0432\u0430 \u0447\u0435\u0441\u0442\u043e, \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435 \u0441\u0435 \u0441 \u043d\u0430\u0448\u0430\u0442\u0430"},keyboardShortcuts:{focusLocal:"\u0424\u043e\u043a\u0443\u0441\u0438\u0440\u0430\u043d\u0435 \u0432\u044a\u0440\u0445\u0443 \u0412\u0430\u0448\u0435\u0442\u043e \u0432\u0438\u0434\u0435\u043e",focusRemote:"\u0424\u043e\u043a\u0443\u0441\u0438\u0440\u0430\u043d\u0435 \u0432\u044a\u0440\u0445\u0443 \u0432\u0438\u0434\u0435\u043e\u0442\u043e \u043d\u0430 \u0434\u0440\u0443\u0433 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a",fullScreen:"\u0412\u043b\u0438\u0437\u0430\u043d\u0435/\u0438\u0437\u043b\u0438\u0437\u0430\u043d\u0435 \u043e\u0442 \u0440\u0435\u0436\u0438\u043c \u043d\u0430 \u0446\u044f\u043b \u0435\u043a\u0440\u0430\u043d",keyboardShortcuts:"\u041a\u043b\u0430\u0432\u0438\u0448\u043d\u0438 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438",localRecording:"",mute:"\u0421\u043f\u0438\u0440\u0430\u043d\u0435/\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",pushToTalk:"\u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435, \u0437\u0430 \u0434\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435",raiseHand:"\u0412\u0434\u0438\u0433\u043d\u0435\u0442\u0435 \u0438\u043b\u0438 \u0441\u0432\u0430\u043b\u0435\u0442\u0435 \u0440\u044a\u043a\u0430",showSpeakerStats:"\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0437\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435\u043b\u044f",toggleChat:"\u041e\u0442\u0432\u0430\u0440\u044f\u043d\u0435/\u0441\u043a\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u0438\u0442\u0435 \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u044f",toggleFilmstrip:"",toggleScreensharing:"\u0421\u043c\u044f\u043d\u0430 \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043c\u0435\u0440\u0430 \u0438 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d \u0435\u043a\u0440\u0430\u043d",toggleShortcuts:"",videoMute:"\u041f\u0443\u0441\u043a\u0430\u043d\u0435/\u0441\u043f\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430"},"\x05keyboardShortcuts":{},liveStreaming:{busy:"\u0420\u0430\u0431\u043e\u0442\u0438\u043c \u0432\u044a\u0440\u0445\u0443 \u0442\u043e\u0432\u0430 \u0434\u0430 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0438 \u0437\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435. \u041c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u0441\u043b\u0435\u0434 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u043c\u0438\u043d\u0443\u0442\u0438.",busyTitle:"\u0412\u0441\u0438\u0447\u043a\u0438 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u0442\u0435\u043b\u0438 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0441\u0430 \u0437\u0430\u0435\u0442\u0438.",changeSignIn:"",choose:"",chooseCTA:"",enterStreamKey:"",error:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e. \u041c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",errorAPI:"",errorLiveStreamNotEnabled:"",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e \u043d\u0435 \u0443\u0441\u043f\u044f \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435",getStreamKeyManually:"",invalidStreamKey:"",off:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e",on:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e",pending:"\u0417\u0430\u043f\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e\u2026",serviceName:"",signedInAs:"",signIn:"",signInCTA:"",signOut:"",start:"\u0417\u0430\u043f\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0436\u0438\u0432\u043e",streamIdHelp:"",unavailableTitle:"\u0418\u0437\u043b\u044a\u0447\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0436\u0438\u0432\u043e \u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u044a\u043f\u043d\u043e"},"\x05liveStreaming":{},localRecording:{clientState:{off:"",on:"",unknown:""},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"",labelToolTip:"",localRecording:"",me:"\u0410\u0437",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:""},moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",no:"",participant:"\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a",participantStats:"",sessionToken:"",start:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0430",stop:"\u041a\u0440\u0430\u0439 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0430",yes:"\u0414\u0430"},"\x05localRecording":{},lockRoomPassword:"\u043f\u0430\u0440\u043e\u043b\u0430",lockRoomPasswordUppercase:"\u041f\u0430\u0440\u043e\u043b\u0430",me:"\u0430\u0437",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"\u0412\u0440\u044a\u0437\u043a\u0430:",focus:"\u041a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0442\u0435\u043d \u0444\u043e\u043a\u0443\u0441",focusFail:"{{component}} \u043d\u0435 \u0435 \u043d\u0430 \u0440\u0430\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f - \u0441\u043b\u0435\u0434\u0432\u0430\u0449 \u043e\u043f\u0438\u0442 \u0441\u043b\u0435\u0434 {{ms}} \u0441\u0435\u043a\u0443\u043d\u0434\u0438",grantedTo:"\u0414\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0440\u043e\u043b\u044f \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440 \u043d\u0430 {{to}}!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"\u0410\u0437",moderator:"\u041f\u0440\u0438\u0434\u043e\u0431\u0438\u0445\u0442\u0435 \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440!",muted:"\u0417\u0430\u043f\u043e\u0447\u0432\u0430\u0442\u0435 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u0430 \u0431\u0435\u0437 \u0437\u0432\u0443\u043a.",mutedTitle:"\u0417\u0432\u0443\u043a\u044a\u0442 \u0432\u0438 \u0435 \u0441\u043f\u0440\u044f\u043d!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"\u041d\u044f\u043a\u043e\u0439",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"",suboptimalExperienceTitle:"",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"\u0441 \u043f\u043e\u0434\u043a\u0440\u0435\u043f\u0430\u0442\u0430 \u043d\u0430",presenceStatus:{busy:"",calling:"",connected:"\u0421\u0432\u044a\u0440\u0437\u0430\u043d",connecting:"\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",connecting2:"\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",disconnected:"\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d",expired:"",ignored:"",initializingCall:"",invited:"\u041f\u043e\u043a\u0430\u043d\u0438",rejected:"",ringing:""},"\x05presenceStatus":{},profile:{setDisplayNameLabel:"\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0435\u043a\u0440\u0430\u043d\u043d\u043e\u0442\u043e \u0441\u0438 \u0438\u043c\u0435",setEmailInput:"\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0435-\u043f\u043e\u0449\u0430",setEmailLabel:"\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0435-\u043f\u043e\u0449\u0430\u0442\u0430 \u0441\u0438 \u0432 \u201egravatar\u201c",title:"\u041f\u0440\u043e\u0444\u0438\u043b"},recording:{authDropboxText:"",availableSpace:"",beta:"",busy:"\u0420\u0430\u0431\u043e\u0442\u0438\u043c \u0432\u044a\u0440\u0445\u0443 \u0442\u043e\u0432\u0430 \u0434\u0430 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0438 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441. \u041c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u0441\u043b\u0435\u0434 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u043c\u0438\u043d\u0443\u0442\u0438.",busyTitle:"\u0412\u0441\u0438\u0447\u043a\u0438 \u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0441\u0430 \u0437\u0430\u0435\u0442\u0438",error:"\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043e\u043f\u0438\u0442 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441. \u041c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",expandedOff:"\u0417\u0430\u043f\u0438\u0441\u044a\u0442 \u0441\u043f\u0440\u044f\u043d",expandedOn:"",expandedPending:"\u0417\u0430\u043f\u0438\u0441\u044a\u0442 \u0437\u0430\u043f\u043e\u0447\u043d\u0430",failedToStart:"\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u0435\u043d \u043e\u043f\u0438\u0442 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441\u0432\u0430\u043d\u0435",fileSharingdescription:"",live:"",loggedIn:"",off:"\u0417\u0430\u043f\u0438\u0441\u044a\u0442 \u0441\u043f\u0440\u044f\u043d",on:"\u0417\u0430\u043f\u0438\u0441",pending:"",rec:"",serviceDescription:"",serviceName:"",signIn:"",signOut:"",unavailable:"",unavailableTitle:"\u0417\u0430\u043f\u0438\u0441\u044a\u0442 \u0435 \u043d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u0435\u043d"},"\x05recording":{},sectionList:{pullToRefresh:""},settings:{calendar:{about:"",disconnect:"\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d",microsoftSignIn:"",signedIn:"",title:""},devices:"",followMe:"\u0412\u0441\u0438\u0447\u043a\u0438 \u043c\u0435 \u0441\u043b\u0435\u0434\u0432\u0430\u0442",language:"",loggedIn:"",moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",more:"",name:"\u0418\u043c\u0435",noDevice:"\u041d\u044f\u043c\u0430",selectAudioOutput:"\u0417\u0432\u0443\u043a\u043e\u0432 \u0438\u0437\u0445\u043e\u0434",selectCamera:"\u041a\u0430\u043c\u0435\u0440\u0430",selectMic:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d",startAudioMuted:"\u0412\u0441\u0438\u0447\u043a\u0438 \u0437\u0430\u043f\u043e\u0447\u0432\u0430\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043d\u0438",startVideoMuted:"\u0412\u0441\u0438\u0447\u043a\u0438 \u0437\u0430\u043f\u043e\u0447\u0432\u0430\u0442 \u0441\u043a\u0440\u0438\u0442\u0438",title:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438"},"\x05settings":{calendar:{}},settingsView:{alertOk:"",alertTitle:"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435",alertURLText:"",buildInfoSection:"",conferenceSection:"",displayName:"",email:"",header:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",profileSection:"\u041f\u0440\u043e\u0444\u0438\u043b",serverURL:"",startWithAudioMuted:"",startWithVideoMuted:"",version:""},share:{dialInfoText:"",mainText:""},speaker:"\u0413\u043e\u0432\u043e\u0440\u0435\u0449",speakerStats:{hours:"{{count}}\u0447",minutes:"{{count}}\u043c\u0438\u043d",name:"\u0418\u043c\u0435",seconds:"{{count}}\u0441\u0435\u043a",speakerStats:"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043d\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435\u043b\u044f",speakerTime:"\u0412\u0440\u0435\u043c\u0435 \u043d\u0430 \u0433\u043e\u0432\u043e\u0440\u0435\u043d\u0435"},startupoverlay:{policyText:" ",title:"\u201e{{app}}\u201c \u0441\u0435 \u043d\u0443\u0436\u0434\u0430\u0435 \u043e\u0442 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430 \u0438 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430."},suspendedoverlay:{rejoinKeyTitle:"\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u044f\u0432\u0430\u043d\u0435",text:"\u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \u0431\u0443\u0442\u043e\u043d\u0430 \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0440\u0438\u0441\u044a\u0435\u0434\u0438\u043d\u044f\u0432\u0430\u043d\u0435, \u0437\u0430 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",title:"\u0412\u0430\u0448\u0438\u044f\u0442 \u0432\u0438\u0434\u0435\u043e \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440 \u0431\u0435\u0448\u0435 \u043f\u0440\u0435\u043a\u044a\u0441\u043d\u0430\u0442, \u0442\u044a\u0439 \u043a\u0430\u0442\u043e \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440\u044a\u0442 \u0412\u0438 \u0437\u0430\u0441\u043f\u0430."},toolbar:{accessibilityLabel:{audioOnly:"",audioRoute:"",callQuality:"",cc:"",chat:"",document:"\u041e\u0442\u0432\u0430\u0440\u044f\u043d\u0435/\u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u043d\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",feedback:"",fullScreen:"",hangup:"",invite:"\u041f\u043e\u043a\u0430\u043d\u0435\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u0446\u0438",kick:"",localRecording:"",lockRoom:"",moreActions:"",moreActionsMenu:"",mute:"",pip:"",profile:"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u0440\u043e\u0444\u0438\u043b\u0430",raiseHand:"",recording:"",remoteMute:"",Settings:"",sharedvideo:"",shareRoom:"",shareYourScreen:"",shortcuts:"",show:"",speakerStats:"",tileView:"",toggleCamera:"",videomute:"",videoblur:""},addPeople:"\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u0446\u0438 \u0432 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u0430",audioOnlyOff:"",audioOnlyOn:"",audioRoute:"",authenticate:"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f",callQuality:"",chat:"\u041e\u0442\u0432\u0430\u0440\u044f\u043d\u0435/\u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u043d\u0430 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u0438\u0442\u0435 \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u044f",closeChat:"",documentClose:"\u041e\u0442\u0432\u0430\u0440\u044f\u043d\u0435/\u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u043d\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",documentOpen:"\u041e\u0442\u0432\u0430\u0440\u044f\u043d\u0435/\u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u043d\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",enterFullScreen:"",enterTileView:"",exitFullScreen:"",exitTileView:"",feedback:"",hangup:"\u041d\u0430\u043f\u0443\u0441\u043a\u0430\u043d\u0435",invite:"\u041f\u043e\u043a\u0430\u043d\u0435\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u0446\u0438",login:"\u0412\u043b\u0435\u0437",logout:"\u0418\u0437\u0445\u043e\u0434",lowerYourHand:"",moreActions:"",mute:"\u0421\u043f\u0438\u0440\u0430\u043d\u0435/\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",openChat:"",pip:"",profile:"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u0440\u043e\u0444\u0438\u043b\u0430",raiseHand:"\u0412\u0434\u0438\u0433\u0430\u043d\u0435/\u0441\u0432\u0430\u043b\u044f\u043d\u0435 \u043d\u0430 \u0440\u044a\u043a\u0430",raiseYourHand:"\u0412\u0434\u0438\u0433\u043d\u0438 \u0440\u044a\u043a\u0430.",Settings:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",sharedvideo:"\u041f\u0443\u0441\u043a\u0430\u043d\u0435/\u0441\u043f\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435\u0442\u043e \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0430",shareRoom:"",shortcuts:"",speakerStats:"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043d\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435\u043b\u044f",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"",talkWhileMutedPopup:"\u041e\u043f\u0438\u0442\u0432\u0430\u0442\u0435 \u0441\u0435 \u0434\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435? \u0412 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u044a\u0442 \u0412\u0438 \u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043d.",tileViewToggle:"",toggleCamera:"",videomute:"\u041f\u0443\u0441\u043a\u0430\u043d\u0435/\u0441\u043f\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{accessibilityLabel:{}},transcribing:{ccButtonTooltip:"",error:"\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u043e\u043f\u0438\u0442 \u0437\u0430 \u0437\u0430\u043f\u0438\u0441. \u041c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"",start:"",stop:"",tr:""},"\x05transcribing":{},userMedia:{androidGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",chromeGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",edgeGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0414\u0430, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",electronGrantPermissions:"\u041c\u043e\u043b\u044f, \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u0442\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",firefoxGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0421\u043f\u043e\u0434\u0435\u043b\u044f\u043d\u0435 \u043d\u0430 \u0438\u0437\u0431\u0440\u0430\u043d\u043e\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",iexplorerGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0414\u043e\u0431\u0440\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",nwjsGrantPermissions:"\u041c\u043e\u043b\u044f, \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u0442\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430 \u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",operaGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.","react-nativeGrantPermissions":"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435.",safariGrantPermissions:"\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0414\u043e\u0431\u0440\u0435, \u043a\u043e\u0433\u0430\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u044a\u0440\u044a\u0442 \u0412\u0438 \u043f\u043e\u043c\u043e\u043b\u0438 \u0437\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435."},videoSIPGW:{busy:"",busyTitle:"",errorAlreadyInvited:"",errorInvite:"",errorInviteFailed:"",errorInviteFailedTitle:"",errorInviteTitle:"",pending:""},videoStatus:{audioOnly:"",audioOnlyExpanded:"",callQuality:"",hd:"\u0412\u041a",highDefinition:"\u0412\u0438\u0441\u043e\u043a\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e",labelTooiltipNoVideo:"",labelTooltipAudioOnly:"\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0435 \u0440\u0435\u0436\u0438\u043c \u0441\u0430\u043c\u043e \u0441\u044a\u0441 \u0437\u0432\u0443\u043a",ld:"\u041d\u041a",lowDefinition:"\u041d\u0438\u0441\u043a\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e",onlyAudioAvailable:"",onlyAudioSupported:"",p2pEnabled:"\u0412\u043a\u043b. \u0434\u0438\u0440\u0435\u043a\u0442\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\u0429\u0435 \u0441\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430 \u0432\u0438\u0441\u043e\u043a\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e.",sd:"\u0421\u041a",standardDefinition:"\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e"},videothumbnail:{domute:"\u0418\u0437\u043a\u043b\u044e\u0447\u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430",flip:"\u041e\u0433\u043b\u0435\u0434\u0430\u043b\u043d\u043e",kick:"\u0418\u0437\u0433\u043e\u043d\u0438",moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",mute:"\u0423\u0447\u0430\u0441\u043d\u0438\u043a\u0430 \u0435 \u0441 \u0438\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d",muted:"\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d",remoteControl:"\u041e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"",roomname:"\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0438\u043c\u0435 \u043d\u0430 \u0441\u0442\u0430\u044f\u0442\u0430"},appDescription:"",audioVideoSwitch:{audio:"",video:""},calendar:"",connectCalendarButton:"",connectCalendarText:"",enterRoomTitle:"",go:"\u041d\u0410\u041f\u0420\u0415\u0414",join:"\u041f\u0420\u0418\u0421\u042a\u0415\u0414\u0418\u041d\u042f\u0412\u0410\u041d\u0415",info:"",privacy:"\u041f\u043e\u0432\u0435\u0440\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442",recentList:"",recentListDelete:"",recentListEmpty:"",reducedUIText:"",roomname:"\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0438\u043c\u0435 \u043d\u0430 \u0441\u0442\u0430\u044f\u0442\u0430",roomnameHint:"",sendFeedback:"\u0418\u0437\u043f\u0440\u0430\u0449\u0430\u043d\u0435 \u043d\u0430 \u043e\u0442\u0437\u0438\u0432\u0438",terms:"\u0423\u0441\u043b\u043e\u0432\u0438\u044f",title:""}}},642,[]); -__d(function(s,i,h,c,e,n,a){e.exports={en:"Englisch",af:"",az:"",bg:"Bulgarisch",cs:"",de:"Deutsch",el:"",eo:"Esperanto",es:"Spanisch",fr:"Franz\xf6sisch",hy:"Armenisch",it:"Italienisch",ja:"",ko:"",nb:"Norwegisch (Bokmal)",oc:"Okzitanisch",pl:"Polnisch",ptBR:"Portugiesisch (Brasilien)",ru:"Russisch",sk:"Slowakisch",sl:"Slowenisch",sv:"Schwedisch",tr:"T\xfcrkisch",vi:"",zhCN:"Chinesisch (China)"}},643,[]); -__d(function(e,n,i,r,t,a,s){t.exports={addPeople:{add:"Einladen",countryNotSupported:"Wir unterst\xfctzen dieses Land noch nicht.",countryReminder:"Telefonnummer nicht in den USA? Bitte sicherstellen, dass die Telefonnummer mit dem L\xe4ndercode beginnt.",disabled:"Sie k\xf6nnen keine Teilnehmer einladen.",failedToAdd:"Fehler beim Hinzuf\xfcgen von Teilnehmern",footerText:"Abgehender Ruf ist deaktiviert.",loading:"Suche nach Teilnehmern und Telefonnummern",loadingNumber:"Telefonnummer wird \xfcberpr\xfcft",loadingPeople:"Suche nach einzuladenden Teilnehmern",noResults:"Keine passenden Ergebnisse",noValidNumbers:"Telefonnummer eingeben",searchNumbers:"Telefonnummern hinzuf\xfcgen",searchPeople:"Nach Teilnehmern suchen",searchPeopleAndNumbers:"Nach Teilnehmen suchen oder deren Telefonnummern hinzuf\xfcgen",telephone:"Telefon: {{number}}",title:"Teilnehmer zu dieser Konferenz einladen"},audioDevices:{bluetooth:"Bluetooth",headphones:"Kopfh\xf6rer",phone:"Telefon",speaker:"Sprecher"},audioOnly:{audioOnly:"Nur Audio"},calendarSync:{addMeetingURL:"Meeting-Link hinzuf\xfcgen",confirmAddLink:"Wollen Sie einen Jitsi-Link zu dieser Veranstaltung hinzuf\xfcgen?",error:{appConfiguration:"Kalenderintegration ist nicht richtig konfiguriert.",generic:"Ein Fehler ist aufgetreten. Pr\xfcfen Sie Ihre Kalendereinstellungen oder versuchen Sie, den Kalender zu aktualisieren.",notSignedIn:"Ein Fehler ist w\xe4hrend der Authentifizierung zur Anzeige von Kalendererveranstaltungen aufgetreten. Pr\xfcfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden."},join:"Teilnehmen",joinTooltip:"Am Meeting teilnehmen",nextMeeting:"N\xe4chste Konferenz",noEvents:"Es sind keine bevorstehenden Veranstaltungen geplant.",ongoingMeeting:"Laufendes Meeting",permissionButton:"Einstellungen \xf6ffnen",permissionMessage:"Die App ben\xf6tigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen.",refresh:"Kalender aktualisieren",today:"Heute"},"\x05calendarSync":{},chat:{error:"",messagebox:"Nachricht eingeben",nickname:{popover:"Name",title:"Geben Sie einen Alias zum Chatten ein"},title:"Chatten"},connectingOverlay:{joiningRoom:"Eine Verbindung zu Ihrem Meeting wird hergestellt\u2026"},connection:{ATTACHED:"Angeh\xe4ngt",AUTHENTICATING:"Anmeldung l\xe4uft",AUTHFAIL:"Authentifizierung fehlgeschlagen",CONNECTED:"Verbunden",CONNECTING:"Verbindung wird hergestellt",CONNFAIL:"Verbindungsaufbau gescheitert",DISCONNECTED:"Getrennt",DISCONNECTING:"Verbindung wird getrennt",ERROR:"Fehler",RECONNECTING:"Es ist ein Netzwerkproblem aufgetreten. Verbinde..."},connectionindicator:{address:"Adresse:",bandwidth:"Gesch\xe4tzte Bandbreite:",bitrate:"Bitrate:",bridgeCount:"Serverzahl: ",connectedTo:"Verbunden mit:",framerate:"Bildwiederholrate:",less:"Weniger anzeigen",localaddress:"Lokale Adresse:",localaddress_plural:"Lokale Adressen:",localport:"Lokaler Port:",localport_plural:"Lokale Ports:",more:"Mehr anzeigen",packetloss:"Paketverlust:",quality:{good:"Gut",inactive:"Inaktiv",lost:"Verloren",nonoptimal:"Nicht optimal",poor:"D\xfcrftig"},remoteaddress:"Entfernte Adresse:",remoteaddress_plural:"Entfernte Adressen:",remoteport:"Entfernter Port:",remoteport_plural:"Entfernte Ports:",resolution:"Aufl\xf6sung:",status:"Verbindung:",transport:"Protokoll:",turn:" (TURN)"},dateUtils:{earlier:"Fr\xfcher",today:"Heute",yesterday:"Gestern"},deepLinking:{appNotInstalled:"Sie ben\xf6tigen die {{app}} App um der Konferenz auf dem Smartphone beizutreten.",description:"Nichts passiert? Wir haben versucht die Konferenz in {{app}} zu \xf6ffnen. Versuchen Sie es erneut oder treten Sie der Konferenz in {{app}} im Web bei.",descriptionWithoutWeb:"",downloadApp:"App herunterladen",launchWebButton:"Im Web \xf6ffnen",openApp:"In der App fortfahren",title:"Die Konferenz wird in {{app}} ge\xf6ffnet...",tryAgainButton:"Erneut mit der nativen Applikation versuchen"},defaultLink:"Bsp.: {{url}}",deviceError:{cameraError:"Fehler beim Zugriff auf die Kamera",cameraPermission:"Fehler beim Bezug der Kamera-Zugriffsberechtigungen",microphoneError:"Fehler beim Zugriff auf das Mikrofon",microphonePermission:"Fehler beim Bezug der Mikrofon-Zugriffsberechtigungen"},deviceSelection:{noPermission:"Berechtigungen nicht erteilt",previewUnavailable:"Keine Vorschau verf\xfcgbar",selectADevice:"Ein Ger\xe4t w\xe4hlen",testAudio:"Pr\xfcfton wiedergeben"},dialog:{accessibilityLabel:{liveStreaming:"Livestream:"},allow:"Erlauben",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Nur ein geteiltes Video gleichzeitig",applicationWindow:"Anwendungsfenster",Back:"Zur\xfcck",cameraConstraintFailedError:"Ihre Kamera erf\xfcllt die notwendigen Anforderungen nicht.",cameraNotFoundError:"Kamera nicht gefunden.",cameraNotSendingData:"Die Kamera ist nicht verf\xfcgbar. Bitte pr\xfcfen ob eine andere Applikation die Kamera verwendet, eine andere Kamera vom Einstellungs-Menu ausw\xe4hlen oder die Applikation neu laden.",cameraNotSendingDataTitle:"Zugriff auf Kamera nicht m\xf6glich",cameraPermissionDeniedError:"Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie k\xf6nnen trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer k\xf6nnen Sie nicht sehen. Verwenden Sie die Kamera-Schaltfl\xe4che in der Adressleiste um die Berechtigungen zu erteilen.",cameraUnknownError:"Die Kamera kann aus einem unbekannten Grund nicht verwendet werden.",cameraUnsupportedResolutionError:"Die Kamera unterst\xfctzt die erforderliche Aufl\xf6sung nicht.",Cancel:"Abbrechen",close:"Schliessen",conferenceDisconnectMsg:"Pr\xfcfen Sie allenfalls Ihre Netzwerkverbindung. Verbinde in {{seconds}} Sekunden...",conferenceDisconnectTitle:"Ihre Verbindung ist getrennt worden.",conferenceReloadMsg:"Wir versuchen das zu beheben. Verbinde in {{seconds}} Sekunden...",conferenceReloadTitle:"Leider ist etwas schiefgegangen.",confirm:"Best\xe4tigen",confirmNo:"Nein",confirmYes:"Ja",connectError:"Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden.",connectErrorWithMsg:"Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden: {{msg}}",connecting:"Verbindung wird hergestellt",contactSupport:"Support kontaktieren",copy:"Kopieren",dismiss:"OK",displayNameRequired:"",done:"Fertig",enterDisplayName:"",error:"Fehler",externalInstallationMsg:"Die Bildschirmfreigabeerweiterung muss installiert werden.",externalInstallationTitle:"Erweiterung erforderlich",goToStore:"Zum Store",gracefulShutdown:"Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verf\xfcgung. Bitte versuchen Sie es sp\xe4ter noch einmal.",IamHost:"Ich bin der Organisator",incorrectRoomLockPassword:"",incorrectPassword:"Benutzername oder Passwort ung\xfcltig",inlineInstallationMsg:"Die Bildschirmfreigabeerweiterung muss installiert werden.",inlineInstallExtension:"Jetzt installieren",internalError:"Oh! Es hat etwas nicht funktioniert. Der folgende Fehler ist aufgetreten: {{error}}",internalErrorTitle:"Interner Fehler",kickMessage:"",kickParticipantButton:"Entfernen",kickParticipantDialog:"Wollen Sie diesen Teilnehmer wirklich entfernen?",kickParticipantTitle:"Teilnehmer stummschalten?",kickTitle:"",liveStreaming:"Live-Streaming",liveStreamingDisabledForGuestTooltip:"G\xe4ste k\xf6nnen kein Live-Streaming starten.",liveStreamingDisabledTooltip:"Starten des Live-Streams deaktiviert.",lockMessage:"Die Konferenz konnte nicht gesperrt werden.",lockRoom:"",lockTitle:"Sperren fehlgeschlagen",logoutQuestion:"Sind Sie sicher, dass Sie sich abmelden und die Konferenz verlassen m\xf6chten?",logoutTitle:"Abmelden",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Ihr Mikrofon erf\xfcllt die notwendigen Anforderungen nicht.",micNotFoundError:"Mikrofon nicht gefunden.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Die Berechtigung zur Verwendung des Mikrofons wurde nicht erteilt. Sie k\xf6nnen trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer k\xf6nnen Sie nicht h\xf6ren. Verwenden Sie die Kamera-Schaltfl\xe4che in der Adressleiste um die Berechtigungen zu erteilen.",micUnknownError:"Das Mikrofon kann aus einem unbekannten Grund nicht verwendet werden.",muteParticipantBody:"Sie k\xf6nnen die Stummschaltung anderer Teilnehmer nicht aufheben, aber ein Teilnehmer kann seine eigene Stummschaltung jederzeit beenden.",muteParticipantButton:"Stummschalten",muteParticipantDialog:"Wollen Sie diesen Teilnehmer wirklich stummschalten? Sie k\xf6nnen die Stummschaltung nicht wieder aufheben, der Teilnehmer kann dies aber jederzeit selbst tun.",muteParticipantTitle:"Teilnehmer stummschalten?",Ok:"OK",passwordLabel:"",passwordNotSupported:"Setzen eines Konferenz-Passworts ist nicht unterst\xfctzt",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Ihr Browser blockiert Popups von dieser Website. Bitte aktivieren Sie Popups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.",popupErrorTitle:"Popup blockiert",recording:"Aufnahme",recordingDisabledForGuestTooltip:"G\xe4ste k\xf6nnen kein Aufzeichnungen starten.",recordingDisabledTooltip:"Start der Aufzeichnung deaktiviert.",rejoinNow:"Jetzt erneut beitreten",remoteControlAllowedMessage:"{{user}} hat die Anfrage zur Fernsteuerung angenommen.",remoteControlDeniedMessage:"{{user}} hat die Anfrage zur Fernsteuerung verweigert.",remoteControlErrorMessage:"Beim Anfordern der Fernsteuerungsberechtigung von {{user}} ist ein Fehler aufgetreten.",remoteControlRequestMessage:"M\xf6chten Sie {{user}} erlauben den Computer fernzusteuern?",remoteControlShareScreenWarning:"Achtung, wenn Sie die Anfrage genehmigen starten Sie die Bildschirmfreigabe!",remoteControlStopMessage:"Die Fernsteuerung wurde beendet.",remoteControlTitle:"Fernsteuerung",Remove:"Entfernen",removePassword:"",removeSharedVideoMsg:"Sind Sie sicher dass Sie das geteilte Video entfernen m\xf6chten?",removeSharedVideoTitle:"Freigegebenes Video entfernen",reservationError:"Fehler im Reservationssystem",reservationErrorMsg:"Fehler, Nummer: {{code}}, Nachricht: {{msg}}",retry:"Wiederholen",screenSharingFailedToInstall:"Oh! Die Erweiterung f\xfcr die Bildschirmfreigabe konnte nicht installiert werden.",screenSharingFailedToInstallTitle:"Bildschirmfreigabe-Erweiterung konnte nicht installiert werden",screenSharingFirefoxPermissionDeniedError:"Die Bildschirmfreigabe ist leider fehlgeschlagen. Bitte stellen Sie sicher, dass die Berechtigung f\xfcr die Bildschirmfreigabe im Browser erteilt wurde.",screenSharingFirefoxPermissionDeniedTitle:"Die Bildschirmfreigabe konnte nicht gestartet werden.",screenSharingPermissionDeniedError:"Oh! Beim Anfordern der Bildschirmfreigabe-Berechtigungen hat etwas nicht funktioniert. Bitte aktualisieren und erneut versuchen.",serviceUnavailable:"Dienst nicht verf\xfcgbar",sessTerminated:"Konferenz beendet",Share:"Teilen",shareVideoLinkError:"Bitte einen g\xfcltigen YouTube-Link angeben.",shareVideoTitle:"Video teilen",shareYourScreen:"Bildschirm freigeben",shareYourScreenDisabled:"Bildschirmfreigabe deaktiviert.",shareYourScreenDisabledForGuest:"G\xe4ste k\xf6nnen den Bildschirm nicht freigeben.",startLiveStreaming:"Einen Livestream starten",startRecording:"Aufnahme starten",startRemoteControlErrorMessage:"Beim Versuch die Fernsteuerung zu starten ist ein Fehler aufgetreten.",stopLiveStreaming:"Live-Streaming stoppen",stopRecording:"Aufnahme stoppen",stopRecordingWarning:"Sind Sie sicher dass Sie die Aufnahme stoppen m\xf6chten?",stopStreamingWarning:"Sind Sie sicher dass Sie das Live-Streaming stoppen m\xf6chten?",streamKey:"Name/Schl\xfcssel f\xfcr den Stream",Submit:"OK",thankYou:"Danke f\xfcr die Verwendung von {{appName}}!",token:"Token",tokenAuthFailed:"Sie sind nicht berechtigt dieser Konferenz beizutreten.",tokenAuthFailedTitle:"Authentifizierung fehlgeschlagen",transcribing:"Wird transkribiert",unlockRoom:"",userPassword:"Benutzerpasswort",WaitForHostMsg:"Die Konferenz {{room}} wurde noch nicht gestartet. Wenn Sie der Veranstalter sind, authentifizieren Sie sich. Warten Sie andernfalls, bis der Veranstalter erscheint.",WaitForHostMsgWOk:"Die Konferenz {{room}} wurde noch nicht gestartet. Wenn Sie der Veranstalter sind, dr\xfccken Sie zum Authentifizieren auf OK. Warten Sie andernfalls, bis der Veranstalter erscheint.",WaitingForHost:"Warten auf den Organisator...",Yes:"Ja",yourEntireScreen:"Ganzer Bildschirm"},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:"ist jetzt {{status}}"},feedback:{average:"Durschnittlich",bad:"Schlecht",detailsLabel:"Sagen Sie uns mehr dazu.",good:"Gut",rateExperience:"Bitte bewerten Sie diese Konferenz",veryBad:"Sehr schlecht",veryGood:"Sehr gut"},incomingCall:{answer:"Antworten",audioCallTitle:"Eingehender Anruf",decline:"OK",productLabel:"von Jitsi Meet",videoCallTitle:"Eingehender Videoanruf"},info:{accessibilityLabel:"Informationen anzeigen",addPassword:"",cancelPassword:"",conferenceURL:"Link:",country:"Land",dialANumber:"Um am Metting teilzunehmen, m\xfcssen Sie eine dieser Nummern w\xe4hlen und dann die PIN eingeben.",dialInConferenceID:"PIN:",dialInNotSupported:"Entschuldigung, leider wird das Einw\xe4hlen derzeit nicht unterst\xfctzt.",dialInNumber:"Einw\xe4hlen:",dialInSummaryError:"Fehler beim Abrufen der Einw\xe4hlinformationen. Versuchen Sie es sp\xe4ter erneut.",dialInTollFree:"Geb\xfchrenfrei",genericError:"Es ist leider etwas schiefgegangen.",inviteLiveStream:"Klicken Sie auf {{url}} um den Livestream dieser Konferenz zu \xf6ffnen",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Sie wurden zur Teilnahme an einem Meeting eingeladen.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Livestream:",moreNumbers:"Weitere Telefonnummern",noNumbers:"Keine Telefonnummern verf\xfcgbar.",noPassword:"Kein",noRoom:"Keine Konferenz f\xfcr die Einw\xe4hl-Informationen angegeben.",numbers:"Einw\xe4hlnummern",password:"",title:"Teilen",tooltip:"Freigabe-Link und Einw\xe4hlinformationen f\xfcr dieses Meeting",label:"Meeting-Informationen"},inviteDialog:{alertText:"Die Einladung einiger Teilnehmer ist fehlgeschlagen.",header:"Einladen",searchCallOnlyPlaceholder:"Telefonnummer eingeben",searchPeopleOnlyPlaceholder:"Nach Teilnehmern suchen",searchPlaceholder:"Teilnehmer oder Telefonnummer",send:"Senden"},inlineDialogFailure:{msg:"Es ist ein Fehler aufgetreten.",retry:"Erneut versuchen",support:"Support",supportMsg:"Wenn der Fehler erneut auftritt, bitte kontaktieren sie"},keyboardShortcuts:{focusLocal:"Lokales Video fokussieren",focusRemote:"Auf das Video eines anderen Teilnehmers fokussieren",fullScreen:"Vollbildmodus aktivieren / deaktivieren",keyboardShortcuts:"Tastenk\xfcrzel",localRecording:"Lokale Aufzeichnungssteuerelemente ein- oder ausblenden",mute:"Stummschaltung aktivieren oder deaktivieren",pushToTalk:"Dr\xfccken um zu sprechen",raiseHand:"Hand erheben",showSpeakerStats:"Statistiken f\xfcr Sprecher anzeigen",toggleChat:"Chat \xf6ffnen oder schliessen",toggleFilmstrip:"Video-Miniaturansichten ein- oder ausblenden",toggleScreensharing:"Zwischen Kamera und Bildschirmfreigabe wechseln",toggleShortcuts:"Tastenkombinationen ein- oder ausblenden",videoMute:"Kamera starten oder stoppen"},"\x05keyboardShortcuts":{},liveStreaming:{busy:"Es werden Resourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",busyTitle:"Alle Streaming-Instanzen sind in Gebrauch",changeSignIn:"Konten wechseln.",choose:"Live stream ausw\xe4hlen",chooseCTA:"Streaming-Option ausw\xe4hlen. Sie sind aktuell als {{email}} angemeldet.",enterStreamKey:"Name/Schl\xfcssel f\xfcr den YouTube Livestream hier eingeben.",error:"Das Live-Streaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.",errorAPI:"Beim abrufen der YouTube Livestreams ist ein Fehler aufgetreten. Bitte versuchen Sie sich erneut anzumelden.",errorLiveStreamNotEnabled:"Live-Streaming ist f\xfcr {{email}} nicht aktiviert. Aktivieren Sie das Live-Streaming oder melden Sie sich bei einem Konto mit aktiviertem Live-Streaming an.",expandedOff:"Live-Streaming wurde angehalten",expandedOn:"Das Meeting wird momentan an YouTube gestreamt.",expandedPending:"Live-Streaming wird gestartet...",failedToStart:"Live-Streaming konnte nicht gestartet werden",getStreamKeyManually:"",invalidStreamKey:"Der Live-Stream-Schl\xfcssel ist u. U. falsch.",off:"Live-Streaming gestoppt",on:"Live-Streaming",pending:"Live-Stream wird gestartet...",serviceName:"Live Streaming-Dienst",signedInAs:"Sie sind derzeit angemeldet als:",signIn:"Mit Google anmelden",signInCTA:"Anmelden oder den Name/Schl\xfcssel des YouTube Livestreams eingeben.",signOut:"Abmelden",start:"Einen Livestream starten",streamIdHelp:"Was ist das?",unavailableTitle:"Live-Streaming nicht verf\xfcgbar"},"\x05liveStreaming":{},localRecording:{clientState:{off:"Aus",on:"Ein",unknown:"Unbekannt"},dialogTitle:"Lokale Aufzeichnungssteuerelemente",duration:"Dauer",durationNA:"N. v.",encoding:"Codierung",label:"LOR",labelToolTip:"Lokale Aufzeichnung ist aktiviert",localRecording:"Lokale Aufzeichnung",me:"Ich",messages:{engaged:"Lokale Aufzeichnung ist aktiviert",finished:"Aufzeichnung der Sitzung {{token}} ist beendet. Senden Sie die aufgezeichnete Datei an den Moderator.",finishedModerator:"Aufzeichnung der Sitzung {{token}} ist beendet. Die Aufzeichnung der lokalen Verlaufs wurde gespeichert. Bitten Sie die anderen Teilnehmer, ihre Aufzeichnungen zu \xfcbermitteln.",notModerator:"Sie sind nicht der Moderator. Sie k\xf6nnen die lokale Aufzeichnung nicht starten oder stoppen."},moderator:"Moderator",no:"Nein",participant:"Teilnehmer",participantStats:"Teilnehmerstatistik",sessionToken:"Sitzungs-Token",start:"Aufnahme starten",stop:"Aufnahme stoppen",yes:"Ja"},"\x05localRecording":{},lockRoomPassword:"Passwort",lockRoomPasswordUppercase:"Passwort",me:"ich",notify:{connectedOneMember:"{{name}} nimmt am Meeting teil",connectedThreePlusMembers:"{{name}} und {{count}} andere Personen nehmen am Meeting teil",connectedTwoMembers:"{{first}} und {{second}} nehmen am Meeting teil",disconnected:"getrennt",focus:"Konferenz-Organisator",focusFail:"{{component}} ist im Moment nicht verf\xfcgbar - wiederholen in {{ms}} Sekunden",grantedTo:"Moderatorenrechte an {{to}} vergeben.",invitedOneMember:"{{displayName}} wurde eingeladen",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Ich",moderator:"Moderatorenrechte vergeben",muted:"Der Konferenz wurde stumm beigetreten.",mutedTitle:"Stummschaltung aktiv!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} m\xf6chte sprechen.",somebody:"Jemand",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Tut uns leid, aber die Konferenz wird mit {{appName}} kein grossartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der vollst\xe4ndig unterst\xfctzen Browser.",suboptimalExperienceTitle:"Browserwarnung",unmute:"",newDeviceCameraTitle:"Neue Kamera erkannt",newDeviceAudioTitle:"Neues Audioger\xe4t erkannt",newDeviceAction:"Verwenden"},passwordSetRemotely:"von einem anderen Teilnehmer gesetzt",passwordDigitsOnly:"",poweredby:"Betrieben von",presenceStatus:{busy:"Besch\xe4ftigt",calling:"Wird angerufen\u2026",connected:"Verbunden",connecting:"Verbindung wird hergestellt",connecting2:"Wird verbunden*\u2026",disconnected:"Getrennt",expired:"Abgelaufen",ignored:"Ignoriert",initializingCall:"Anruf wird initialisiert\u2026",invited:"Einladen",rejected:"Abgelehnt",ringing:"Es klingelt\u2026"},"\x05presenceStatus":{},profile:{setDisplayNameLabel:"Anzeigename festlegen",setEmailInput:"E-Mail eingeben",setEmailLabel:"E-Mail Adresse f\xfcr Gravatar",title:"Profil"},recording:{authDropboxText:"In Dropbox hochladen",availableSpace:"Verf\xfcgbarer Speicherplatz: {{spaceLeft}} MB (ca. {{duration}} Minuten Aufzeichnung)",beta:"BETA",busy:"Es werden Resourcen f\xfcr eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",busyTitle:"Alle Aufnahme-Instanzen sind in Gebrauch",error:"Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",expandedOff:"Aufzeichnung wurde gestoppt",expandedOn:"Das Meeting wird momentan aufgezeichnet.",expandedPending:"Aufzeichnung wird gestartet\u2026",failedToStart:"Die Aufnahme konnte nicht gestartet werden",fileSharingdescription:"Aufzeichnung mit Meeting-Teilnehmer teilen",live:"LIVE",loggedIn:"Als {{userName}} angemeldet",off:"Aufnahme gestoppt",on:"Aufnahme",pending:"Aufzeichnung des Meetings wird vorbereitet\u2026",rec:"AUFZ",serviceDescription:"Ihre Aufzeichnung wird vom Aufzeichnungsdienst gespeichert",serviceName:"Aufnahmedienst",signIn:"Anmelden",signOut:"Abmelden",unavailable:"Oh! Der {{serviceName}} ist aktuell nicht verf\xfcgbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es sp\xe4ter noch einmal.",unavailableTitle:"Aufnahme nicht verf\xfcgbar"},sectionList:{pullToRefresh:"Ziehen um zu aktualisieren"},settings:{calendar:{about:"Die Kalenderintegration von {{appName}} wird verwendet, um ein sicheres Zugreifen auf Ihren Kalender und Auslesen der bevorstehenden Veranstaltungen zu erm\xf6glichen.",disconnect:"Getrennt",microsoftSignIn:"Mit Microsoft anmelden",signedIn:"Momentan wird auf Kalenderveranstaltungen von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfl\xe4che \u201eTrennen\u201c, um den Zugriff auf die Kalenderveranstaltungen zu stoppen.",title:"Kalender"},devices:"Ger\xe4te",followMe:"Follow-me f\xfcr alle Teilnehmer",language:"Sprache",loggedIn:"Als {{name}} angemeldet",moderator:"Moderator",more:"Mehr",name:"Name",noDevice:"Kein",selectAudioOutput:"Audioausgabe",selectCamera:"Kamera",selectMic:"Mikrofon",startAudioMuted:"Alle Teilnehmer treten stumm geschaltet bei",startVideoMuted:"Alle Teilnehmer treten ohne Video bei",title:"Einstellungen"},"\x05settings":{calendar:{}},settingsView:{alertOk:"OK",alertTitle:"Warnung",alertURLText:"Die angegebene Server URL ist ung\xfcltig",buildInfoSection:"Build-Informationen",conferenceSection:"Konferenz",displayName:"Anzeigename",email:"E-Mail",header:"Einstellungen",profileSection:"Profil",serverURL:"Server URL",startWithAudioMuted:"Stumm beitreten",startWithVideoMuted:"Ohne Video beitreten",version:"Version"},share:{dialInfoText:"",mainText:""},speaker:"Sprecher",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Name",seconds:"{{count}}s",speakerStats:"Sprecher-Statistiken",speakerTime:"Sprecher-Zeit"},startupoverlay:{policyText:" ",title:"{{app}} ben\xf6tigt Kamera und Mikrofon."},suspendedoverlay:{rejoinKeyTitle:"Erneut teilnehmen",text:"Erneut teilnehmen Schaltfl\xe4che bet\xe4tigen um erneut zu verbinden.",title:"Die Konferenz wurde unterbrochen weil der Standbymodus aktiviert wurde."},toolbar:{accessibilityLabel:{audioOnly:"Nur Audio ein-/ausschalten",audioRoute:"Audioger\xe4t ausw\xe4hlen",callQuality:"Qualit\xe4tseinstellungen",cc:"Untertitel ein-/ausschalten",chat:"Chatfenster ein-/ausblenden",document:"Geteiltes Dokument schliessen",feedback:"Feedback hinterlasen",fullScreen:"Vollbildschirm ein-/ausblenden",hangup:"Anruf beenden",invite:"Teilnehmer einladen",kick:"Teilnehmer entfernen",localRecording:"Lokale Aufzeichnungssteuerelemente ein-/ausschalten",lockRoom:"Meeting-Passwort ein-/auschalten",moreActions:"Men\xfc \u201eWeitere Aktionen\u201c ein-/ausschalten",moreActionsMenu:"Men\xfc \u201eWeitere Aktionen\u201c",mute:"\u201eAudio stummschalten\u201c ein-/ausschalten",pip:"Bild-in-Bild-Modus ein-/ausschalten",profile:"Profil bearbeiten",raiseHand:"\u201eMelden\u201c ein-/ausschalten",recording:"Aufzeichnung ein-/ausschalten",remoteMute:"Teilnehmer stummschalten",Settings:"Einstellungen ein-/ausschalten",sharedvideo:"YouTube-Videofreigabe ein-/ausschalten",shareRoom:"Person einladen",shareYourScreen:"Bildschirmfreigabe ein-/ausschalten",shortcuts:"Tastenkombinationen ein-/ausblenden",show:"",speakerStats:"Sprecherstatistik ein-/ausblenden",tileView:"Kachelansicht ein-/ausschalten",toggleCamera:"Kamera ein-/ausschalten",videomute:"\u201eVideo stummschalten\u201c ein-/ausschalten",videoblur:""},addPeople:"Teilnehmer zur Konferenz hinzuf\xfcgen",audioOnlyOff:"Modus \u201eNur Audio\u201c deaktivieren",audioOnlyOn:"Modus \u201eNur Audio\u201c aktivieren",audioRoute:"Audioger\xe4t ausw\xe4hlen",authenticate:"Anmelden",callQuality:"Qualit\xe4tseinstellungen",chat:"Chat \xf6ffnen / schliessen",closeChat:"Chat schlie\xdfen",documentClose:"Geteiltes Dokument schliessen",documentOpen:"Geteiltes Dokument \xf6ffnen",enterFullScreen:"Vollbildmodus",enterTileView:"Kachelansicht einschalten",exitFullScreen:"Vollbildmodus verlassen",exitTileView:"Kachelansicht ausschalten",feedback:"Feedback hinterlasen",hangup:"Verlassen",invite:"Teilnehmer einladen",login:"Anmelden",logout:"Abmelden",lowerYourHand:"Hand senken",moreActions:"Weitere Einstellungen",mute:"Stummschaltung aktivieren / deaktivieren",openChat:"Chat \xf6ffnen",pip:"Bild-in-Bild-Modus einschalten",profile:"Profil bearbeiten",raiseHand:"Hand erheben",raiseYourHand:"Melden",Settings:"Einstellungen",sharedvideo:"YouTube-Video teilen",shareRoom:"Person einladen",shortcuts:"Tastenk\xfcrzel anzeigen",speakerStats:"Sprecher-Statistiken",startScreenSharing:"Bildschirmfreigabe starten",startSubtitles:"Untertitel einschalten",stopScreenSharing:"Bildschirmfreigabe stoppen",stopSubtitles:"Untertitel ausschalten",stopSharedVideo:"YouTube Video stoppen",talkWhileMutedPopup:"Versuchen sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",tileViewToggle:"Kachelansicht ein-/ausschalten",toggleCamera:"Kamera ein-/ausschalten",videomute:"Kamera starten / stoppen",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{accessibilityLabel:{}},transcribing:{ccButtonTooltip:"Untertitel ein-/ausschalten",error:"Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",expandedLabel:"Transkribieren ist derzeit eingeschaltet",failedToStart:"Transkribieren konnte nicht gestartet werden",labelToolTip:"Das Meeting wird transkribiert",off:"Transkribieren gestoppt",pending:"Transkribieren des Meetings wird vorbereitet\u2026",start:"Anzeige der Untertitel starten",stop:"Anzeige der Untertitel stoppen",tr:"TR"},"\x05transcribing":{},userMedia:{androidGrantPermissions:"W\xe4hlen Sie Erlauben wenn der Browser um Berechtigungen bittet.",chromeGrantPermissions:"W\xe4hlen Sie Erlauben wenn der Browser um Berechtigungen bittet.",edgeGrantPermissions:"W\xe4hlen Sie Ja wenn der Browser um Berechtigungen bittet.",electronGrantPermissions:"Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons erteilen",firefoxGrantPermissions:"W\xe4hlen Sie Markiertes Ger\xe4t teilen wenn der Browser um Berechtigungen bittet.",iexplorerGrantPermissions:"W\xe4hlen Sie OK wenn der Browser um Berechtigungen bittet.",nwjsGrantPermissions:"Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons erteilen",operaGrantPermissions:"W\xe4hlen Sie Erlauben wenn der Browser um Berechtigungen bittet.","react-nativeGrantPermissions":"W\xe4hlen Sie Erlauben wenn der Browser um Berechtigungen bittet.",safariGrantPermissions:"W\xe4hlen Sie OK wenn der Browser um Berechtigungen bittet."},videoSIPGW:{busy:"Es stehen keine freien Ressourcen zur Verf\xfcgung. Bitte versuchen Sie es sp\xe4ter noch einmal.",busyTitle:"Keine freien Ressourcen",errorAlreadyInvited:"{{displayName}} ist bereits eingeladen",errorInvite:"Die Konferenz konnte nicht gestartet werden. Bitte versuchen Sie es sp\xe4ter noch einmal.",errorInviteFailed:"Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es sp\xe4ter noch einmal.",errorInviteFailedTitle:"{{displayName}} konnte nicht eingeladen werden",errorInviteTitle:"Fehler beim Erstellen der Konferenz",pending:"{{displayName}} wurde eingeladen"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Sie befinden sich im Modus \u201eNur Audio\u201c. Dieser Modus ben\xf6tigt weniger Bandbreite, Sie sehen jedoch nicht die Videos der anderen.",callQuality:"",hd:"HD",highDefinition:"Hohe Aufl\xf6sung",labelTooiltipNoVideo:"Kein Video",labelTooltipAudioOnly:"Nur-Audio Modus aktiv",ld:"LD",lowDefinition:"Niedrige Aufl\xf6sung",onlyAudioAvailable:"Nur Ton",onlyAudioSupported:"In diesem Browser wird nur Audio unterst\xfctzt.",p2pEnabled:"Ende-zu-Ende aktiviert",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Hohe Qualit\xe4t wird bevorzugt.",sd:"SD",standardDefinition:"Standardaufl\xf6sung"},videothumbnail:{domute:"Stummschalten",flip:"Spiegeln",kick:"Hinauswerfen",moderator:"Moderator",mute:"Teilnehmer ist stumm geschaltet",muted:"Stummgeschaltet",remoteControl:"Fernsteuerung",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Zum Teilnehmen tippen",roomname:"Konferenzname eingeben"},appDescription:"Auf geht's! Beginne eine Videokonferenz mit dem ganzen Team. Oder eigentlich, lade alle ein die du kennst. {{app}} ist eine vollst\xe4ndig verschl\xfcsselte, aus 100% Open-Source-Software bestehende Videokonferenzl\xf6sung die du den ganzen Tag kostenlos verwenden kannst \u2014 ohne Registrierung.",audioVideoSwitch:{audio:"Sprache",video:"Video"},calendar:"Kalender",connectCalendarButton:"Kalender verbinden",connectCalendarText:"Verbinden Sie Ihren Kalender, um all Ihre Meetings in {{app}} anzuzeigen. F\xfcgen Sie zudem {{provider}}-Meetings in Ihren Kalender ein und starten Sie sie mit nur einem Klick.",enterRoomTitle:"Neues Meeting starten",go:"Los",join:"Beitreten",info:"Informationen",privacy:"Privatsph\xe4re",recentList:"Letzte\"",recentListDelete:"L\xf6schen",recentListEmpty:"Die Liste \u201eLetzte\u201c ist momentan leer. Chatten Sie mit Ihrem Team. Sie finden all Ihre letzten Meetings hier.",reducedUIText:"",roomname:"Konferenzname eingeben",roomnameHint:"Name oder URL der Konferenz der Sie beitreten m\xf6chten. Sie k\xf6nnen einen Namen erfinden, er muss nur den anderen Teilnehmern \xfcbermittelt werden damit sie der gleichen Konferenz beitreten.",sendFeedback:"Senden Sie uns Ihr Feedback",terms:"Bedingungen",title:"Sichere, mit umfassenden Funktionen ausgestattete und vollkommen kostenlose Videokonferenzen"}}},644,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"",af:"",az:"",bg:"",cs:"",de:"",el:"",eo:"",es:"",fr:"",hy:"",it:"",ja:"",ko:"",nb:"",oc:"",pl:"",ptBR:"",ru:"",sk:"",sl:"",sv:"",tr:"",vi:"",zhCN:""}},645,[]); -__d(function(e,o,t,r,n,i,a){n.exports={addPeople:{add:"Invite",countryNotSupported:"We do not support this destination yet.",countryReminder:"Calling outside the US? Please make sure you start with the country code!",disabled:"You can't invite people.",failedToAdd:"Failed to add members",footerText:"Dialling out is disabled.",loading:"Searching for people and phone numbers",loadingNumber:"Validating phone number",loadingPeople:"Searching for people to invite",noResults:"No matching search results",noValidNumbers:"Please enter a phone number",searchNumbers:"Add phone numbers",searchPeople:"Search for people",searchPeopleAndNumbers:"Search for people or add their phone numbers",telephone:"Telephone: {{number}}",title:"Invite people to this meeting"},audioDevices:{bluetooth:"Bluetooth",headphones:"Headphones",phone:"Phone",speaker:"Speaker"},audioOnly:{audioOnly:"Audio only"},calendarSync:{addMeetingURL:"Add a meeting link",confirmAddLink:"Do you want to add a Jitsi link to this event?",error:{appConfiguration:"Calendar integration is not properly configured.",generic:"An error has occurred. Please check your calendar settings or try refreshing the calendar.",notSignedIn:"An error occurred while authenticating to see calendar events. Please check your calendar settings and try logging in again."},join:"Join",joinTooltip:"Join the meeting",nextMeeting:"next meeting",noEvents:"There are no upcoming events scheduled.",ongoingMeeting:"ongoing meeting",permissionButton:"Open settings",permissionMessage:"The Calendar permission is required to see your meetings in the app.",refresh:"Refresh calendar",today:"Today"},chat:{error:"Error: your message \"{{originalText}}\" was not sent. Reason: {{error}}",messagebox:"Type a message",nickname:{popover:"Choose a nickname",title:"Enter a nickname to use chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Connecting you to your meeting..."},connection:{ATTACHED:"Attached",AUTHENTICATING:"Authenticating",AUTHFAIL:"Authentication failed",CONNECTED:"Connected",CONNECTING:"Connecting",CONNFAIL:"Connection failed",DISCONNECTED:"Disconnected",DISCONNECTING:"Disconnecting",ERROR:"Error",RECONNECTING:"A network problem occurred. Reconnecting..."},connectionindicator:{address:"Address:",bandwidth:"Estimated bandwidth:",bitrate:"Bitrate:",bridgeCount:"Server count: ",connectedTo:"Connected to:",framerate:"Frame rate:",less:"Show less",localaddress:"Local address:",localaddress_plural:"Local addresses:",localport:"Local port:",localport_plural:"Local ports:",more:"Show more",packetloss:"Packet loss:",quality:{good:"Good",inactive:"Inactive",lost:"Lost",nonoptimal:"Nonoptimal",poor:"Poor"},remoteaddress:"Remote address:",remoteaddress_plural:"Remote addresses:",remoteport:"Remote port:",remoteport_plural:"Remote ports:",resolution:"Resolution:",status:"Connection:",transport:"Transport:",transport_plural:"Transports:",turn:" (turn)"},dateUtils:{earlier:"Earlier",today:"Today",yesterday:"Yesterday"},deepLinking:{appNotInstalled:"You need the {{app}} mobile app to join this meeting on your phone.",description:"Nothing happened? We tried launching your meeting in the {{app}} desktop app. Try again or launch it in the {{app}} web app.",descriptionWithoutWeb:"",downloadApp:"Download the app",launchWebButton:"Launch in web",openApp:"Continue to the app",title:"Launching your meeting in {{app}}...",tryAgainButton:"Try again in desktop"},defaultLink:"e.g. {{url}}",deviceError:{cameraError:"Failed to access your camera",cameraPermission:"Error obtaining camera permission",microphoneError:"Failed to access your microphone",microphonePermission:"Error obtaining microphone permission"},deviceSelection:{noPermission:"Permission not granted",previewUnavailable:"Preview unavailable",selectADevice:"Select a device",testAudio:"Play a test sound"},dialog:{accessibilityLabel:{liveStreaming:"Live Stream"},allow:"Allow",alreadySharedVideoMsg:"Another member is already sharing a video. This conference allows only one shared video at a time.",alreadySharedVideoTitle:"Only one shared video is allowed at a time",applicationWindow:"Application window",Back:"Back",cameraConstraintFailedError:"Your camera does not satisfy some of the required constraints.",cameraNotFoundError:"Camera was not found.",cameraNotSendingData:"We are unable to access your camera. Please check if another application is using this device, select another device from the settings menu or try to reload the application.",cameraNotSendingDataTitle:"Unable to access camera",cameraPermissionDeniedError:"You have not granted permission to use your camera. You can still join the conference but others won't see you. Use the camera button in the address bar to fix this.",cameraUnknownError:"Cannot use camera for an unknown reason.",cameraUnsupportedResolutionError:"Your camera does not support required video resolution.",Cancel:"Cancel",close:"Close",conferenceDisconnectMsg:"You may want to check your network connection. Reconnecting in {{seconds}} sec...",conferenceDisconnectTitle:"You have been disconnected.",conferenceReloadMsg:"We're trying to fix this. Reconnecting in {{seconds}} sec...",conferenceReloadTitle:"Unfortunately, something went wrong.",confirm:"Confirm",confirmNo:"No",confirmYes:"Yes",connectError:"Oops! Something went wrong and we couldn't connect to the conference.",connectErrorWithMsg:"Oops! Something went wrong and we couldn't connect to the conference: {{msg}}",connecting:"Connecting",contactSupport:"Contact support",copy:"Copy",dismiss:"Dismiss",displayNameRequired:"Display name is required",done:"Done",enterDisplayName:"Please enter your display name",error:"Error",externalInstallationMsg:"You need to install our desktop sharing extension.",externalInstallationTitle:"Extension required",goToStore:"Go to the webstore",gracefulShutdown:"Our service is currently down for maintenance. Please try again later.",IamHost:"I am the host",incorrectRoomLockPassword:"",incorrectPassword:"Incorrect username or password",inlineInstallationMsg:"You need to install our desktop sharing extension.",inlineInstallExtension:"Install now",internalError:"Oops! Something went wrong. The following error occurred: {{error}}",internalErrorTitle:"Internal error",kickMessage:"Ouch! You have been kicked out of the meet!",kickParticipantButton:"Kick",kickParticipantDialog:"Are you sure you want to kick this participant?",kickParticipantTitle:"Kick this member?",kickTitle:"Kicked from meeting",liveStreaming:"Live Streaming",liveStreamingDisabledForGuestTooltip:"Guests can't start live streaming.",liveStreamingDisabledTooltip:"Start live stream disabled.",lockMessage:"Failed to lock the conference.",lockRoom:"Add meeting password",lockTitle:"Lock failed",logoutQuestion:"Are you sure you want to logout and stop the conference?",logoutTitle:"Log out",maxUsersLimitReached:"The limit for maximum number of members has been reached. The conference is full. Please contact the meeting owner or try again later!",maxUsersLimitReachedTitle:"Maximum members limit reached",micConstraintFailedError:"Your microphone does not satisfy some of the required constraints.",micNotFoundError:"Microphone was not found.",micNotSendingData:"We are unable to access your microphone. Please select another device from the settings menu or try to reload the application.",micNotSendingDataTitle:"Unable to access microphone",micPermissionDeniedError:"You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",micUnknownError:"Cannot use microphone for an unknown reason.",muteParticipantBody:"You won't be able to unmute them, but they can unmute themselves at any time.",muteParticipantButton:"Mute",muteParticipantDialog:"Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",muteParticipantTitle:"Mute this member?",Ok:"Ok",passwordLabel:"Password",passwordNotSupported:"Setting a meeting password is not supported.",passwordNotSupportedTitle:"Password not supported",passwordRequired:"Password required",popupError:"Your browser is blocking pop-up windows from this site. Please enable pop-ups in your browser's security settings and try again.",popupErrorTitle:"Pop-up blocked",recording:"Recording",recordingDisabledForGuestTooltip:"Guests can't start recordings.",recordingDisabledTooltip:"Start recording disabled.",rejoinNow:"Rejoin now",remoteControlAllowedMessage:"{{user}} accepted your remote control request!",remoteControlDeniedMessage:"{{user}} rejected your remote control request!",remoteControlErrorMessage:"An error occurred while trying to request remote control permissions from {{user}}!",remoteControlRequestMessage:"Will you allow {{user}} to remotely control your desktop?",remoteControlShareScreenWarning:"Note that if you press \"Allow\" you will share your screen!",remoteControlStopMessage:"The remote control session ended!",remoteControlTitle:"Remote desktop control",Remove:"Remove",removePassword:"Remove password",removeSharedVideoMsg:"Are you sure you would like to remove your shared video?",removeSharedVideoTitle:"Remove shared video",reservationError:"Reservation system error",reservationErrorMsg:"Error code: {{code}}, message: {{msg}}",retry:"Retry",screenSharingFailedToInstall:"Oops! Your screen sharing extension failed to install.",screenSharingFailedToInstallTitle:"Screen sharing extension failed to install",screenSharingFirefoxPermissionDeniedError:"Something went wrong while we were trying to share your screen. Please make sure that you have given us permission to do so. ",screenSharingFirefoxPermissionDeniedTitle:"Oops! We weren\u2019t able to start screen sharing!",screenSharingPermissionDeniedError:"Oops! Something went wrong with your screen sharing extension permissions. Please reload and try again.",serviceUnavailable:"Service unavailable",sessTerminated:"Call terminated",Share:"Share",shareVideoLinkError:"Please provide a correct youtube link.",shareVideoTitle:"Share a video",shareYourScreen:"Share your screen",shareYourScreenDisabled:"Screen sharing disabled.",shareYourScreenDisabledForGuest:"Guests can't screen share.",startLiveStreaming:"Start live stream",startRecording:"Start recording",startRemoteControlErrorMessage:"An error occurred while trying to start the remote control session!",stopLiveStreaming:"Stop live stream",stopRecording:"Stop recording",stopRecordingWarning:"Are you sure you would like to stop the recording?",stopStreamingWarning:"Are you sure you would like to stop the live streaming?",streamKey:"Live stream key",Submit:"Submit",thankYou:"Thank you for using {{appName}}!",token:"token",tokenAuthFailed:"Sorry, you're not allowed to join this call.",tokenAuthFailedTitle:"Authentication failed",transcribing:"Transcribing",unlockRoom:"Remove meeting password",userPassword:"user password",WaitForHostMsg:"The conference {{room}} has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",WaitForHostMsgWOk:"The conference {{room}} has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",WaitingForHost:"Waiting for the host ...",Yes:"Yes",yourEntireScreen:"Your entire screen"},dialOut:{statusMessage:"is now {{status}}"},feedback:{average:"Average",bad:"Bad",detailsLabel:"Tell us more about it.",good:"Good",rateExperience:"Rate your meeting experience",veryBad:"Very Bad",veryGood:"Very Good"},incomingCall:{answer:"Answer",audioCallTitle:"Incoming call",decline:"Dismiss",productLabel:"from Jitsi Meet",videoCallTitle:"Incoming video call"},info:{accessibilityLabel:"Show info",addPassword:"Add password",cancelPassword:"Cancel password",conferenceURL:"Link:",country:"Country",dialANumber:"To join your meeting, dial one of these numbers and then enter the pin.",dialInConferenceID:"PIN:",dialInNotSupported:"Sorry, dialling in is currently not supported.",dialInNumber:"Dial-in:",dialInSummaryError:"Error fetching dial-in info now. Please try again later.",dialInTollFree:"Toll Free",genericError:"Whoops, something went wrong.",inviteLiveStream:"To view the live stream of this meeting, click this link: {{url}}",invitePhone:"One tap audio Dial In: {{number}},,{{conferenceID}}#",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"You are invited to join a meeting.",inviteURLFirstPartPersonal:"{{name}} is inviting you to a meeting.\n",inviteURLSecondPart:"\nJoin the meeting:\n{{url}}\n",liveStreamURL:"Live stream:",moreNumbers:"More numbers",noNumbers:"No dial-in numbers.",noPassword:"None",noRoom:"No room was specified to dial-in into.",numbers:"Dial-in Numbers",password:"Password:",title:"Share",tooltip:"Share link and dial-in info for this meeting",label:"Meeting info"},inviteDialog:{alertText:"Failed to invite some participants.",header:"Invite",searchCallOnlyPlaceholder:"Enter phone number",searchPeopleOnlyPlaceholder:"Search for participants",searchPlaceholder:"Participant or phone number",send:"Send"},inlineDialogFailure:{msg:"We stumbled a bit.",retry:"Try again",support:"Support",supportMsg:"If this keeps happening, reach out to"},keyboardShortcuts:{focusLocal:"Focus on your video",focusRemote:"Focus on another person's video",fullScreen:"View or exit full screen",keyboardShortcuts:"Keyboard shortcuts",localRecording:"Show or hide local recording controls",mute:"Mute or unmute your microphone",pushToTalk:"Push to talk",raiseHand:"Raise or lower your hand",showSpeakerStats:"Show speaker stats",toggleChat:"Open or close the chat",toggleFilmstrip:"Show or hide video thumbnails",toggleScreensharing:"Switch between camera and screen sharing",toggleShortcuts:"Show or hide keyboard shortcuts",videoMute:"Start or stop your camera"},liveStreaming:{busy:"We're working on freeing streaming resources. Please try again in a few minutes.",busyTitle:"All streamers are currently busy",changeSignIn:"Switch accounts.",choose:"Choose a live stream",chooseCTA:"Choose a streaming option. You're currently logged in as {{email}}.",enterStreamKey:"Enter your YouTube live stream key here.",error:"Live Streaming failed. Please try again.",errorAPI:"An error occurred while accessing your YouTube broadcasts. Please try logging in again.",errorLiveStreamNotEnabled:"Live Streaming is not enabled on {{email}}. Please enable live streaming or log into an account with live streaming enabled.",expandedOff:"The live streaming has stopped",expandedOn:"The meeting is currently being streamed to YouTube.",expandedPending:"The live streaming is being started...",failedToStart:"Live Streaming failed to start",getStreamKeyManually:"We weren\u2019t able to fetch any live streams. Try getting your live stream key from YouTube.",invalidStreamKey:"Live stream key may be incorrect.",off:"Live Streaming stopped",on:"Live Streaming",pending:"Starting Live Stream...",serviceName:"Live Streaming service",signedInAs:"You are currently signed in as:",signIn:"Sign in with Google",signInCTA:"Sign in or enter your live stream key from YouTube.",signOut:"Sign out",start:"Start a live stream",streamIdHelp:"What's this?",unavailableTitle:"Live Streaming unavailable"},localRecording:{clientState:{off:"Off",on:"On",unknown:"Unknown"},dialogTitle:"Local Recording Controls",duration:"Duration",durationNA:"N/A",encoding:"Encoding",label:"LOR",labelToolTip:"Local recording is engaged",localRecording:"Local Recording",me:"Me",messages:{engaged:"Local recording engaged.",finished:"Recording session {{token}} finished. Please send the recorded file to the moderator.",finishedModerator:"Recording session {{token}} finished. The recording of the local track has been saved. Please ask the other participants to submit their recordings.",notModerator:"You are not the moderator. You cannot start or stop local recording."},moderator:"Moderator",no:"No",participant:"Participant",participantStats:"Participant Stats",sessionToken:"Session Token",start:"Start Recording",stop:"Stop Recording",yes:"Yes"},lockRoomPassword:"password",lockRoomPasswordUppercase:"Password",me:"me",notify:{connectedOneMember:"{{name}} joined the meeting",connectedThreePlusMembers:"{{name}} and {{count}} others joined the meeting",connectedTwoMembers:"{{first}} and {{second}} joined the meeting",disconnected:"disconnected",focus:"Conference focus",focusFail:"{{component}} not available - retry in {{ms}} sec",grantedTo:"Moderator rights granted to {{to}}!",invitedOneMember:"{{name}} has been invited",invitedThreePlusMembers:"{{name}} and {{count}} others have been invited",invitedTwoMembers:"{{first}} and {{second}} have been invited",kickParticipant:"{{kicked}} was kicked by {{kicker}}",me:"Me",moderator:"Moderator rights granted!",muted:"You have started the conversation muted.",mutedTitle:"You're muted!",mutedRemotelyTitle:"You have been muted by {{participantDisplayName}}!",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} would like to speak.",somebody:"Somebody",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Eer... we are afraid your experience with {{appName}} isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the fully supported browsers.",suboptimalExperienceTitle:"Browser Warning",unmute:"",newDeviceCameraTitle:"New camera detected",newDeviceAudioTitle:"New audio device detected",newDeviceAction:"Use"},passwordSetRemotely:"set by another member",passwordDigitsOnly:"Up to {{number}} digits",poweredby:"powered by",presenceStatus:{busy:"Busy",calling:"Calling...",connected:"Connected",connecting:"Connecting...",connecting2:"Connecting*...",disconnected:"Disconnected",expired:"Expired",ignored:"Ignored",initializingCall:"Initialising Call...",invited:"Invited",rejected:"Rejected",ringing:"Ringing..."},profile:{setDisplayNameLabel:"Set your display name",setEmailInput:"Enter e-mail",setEmailLabel:"Set your gravatar email",title:"Profile"},recording:{authDropboxText:"Upload to Dropbox",availableSpace:"Available space: {{spaceLeft}} MB (approximately {{duration}} minutes of recording)",beta:"BETA",busy:"We're working on freeing recording resources. Please try again in a few minutes.",busyTitle:"All recorders are currently busy",error:"Recording failed. Please try again.",expandedOff:"Recording has stopped",expandedOn:"The meeting is currently being recorded.",expandedPending:"Recording is being started...",failedToStart:"Recording failed to start",fileSharingdescription:"Share recording with meeting participants",live:"LIVE",loggedIn:"Logged in as {{userName}}",off:"Recording stopped",on:"Recording",pending:"Preparing to record the meeting...",rec:"REC",serviceDescription:"Your recording will be saved by the recording service",serviceName:"Recording service",signIn:"Sign in",signOut:"Sign out",unavailable:"Oops! The {{serviceName}} is currently unavailable. We're working on resolving the issue. Please try again later.",unavailableTitle:"Recording unavailable"},sectionList:{pullToRefresh:"Pull to refresh"},settings:{calendar:{about:"The {{appName}} calendar integration is used to securely access your calendar so it can read upcoming events.",disconnect:"Disconnect",microsoftSignIn:"Sign in with Microsoft",signedIn:"Currently accessing calendar events for {{email}}. Click the Disconnect button below to stop accessing calendar events.",title:"Calendar"},devices:"Devices",followMe:"Everyone follows me",language:"Language",loggedIn:"Logged in as {{name}}",moderator:"Moderator",more:"More",name:"Name",noDevice:"None",selectAudioOutput:"Audio output",selectCamera:"Camera",selectMic:"Microphone",startAudioMuted:"Everyone starts muted",startVideoMuted:"Everyone starts hidden",title:"Settings"},settingsView:{alertOk:"OK",alertTitle:"Warning",alertURLText:"The entered server URL is invalid",buildInfoSection:"Build Information",conferenceSection:"Conference",displayName:"Display name",email:"Email",header:"Settings",profileSection:"Profile",serverURL:"Server URL",startWithAudioMuted:"Start with audio muted",startWithVideoMuted:"Start with video muted",version:"Version"},share:{dialInfoText:"\n\n=====\n\nJust want to dial in on your phone?\n\n{{defaultDialInNumber}}Click this link to see the dial in phone numbers for this meeting\n{{dialInfoPageUrl}}",mainText:"Click the following link to join the meeting:\n{{roomUrl}}"},speaker:"Speaker",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Name",seconds:"{{count}}s",speakerStats:"Speaker Stats",speakerTime:"Speaker Time"},startupoverlay:{policyText:" ",title:"{{app}} needs to use your microphone and camera."},suspendedoverlay:{rejoinKeyTitle:"Rejoin",text:"Press the Rejoin button to reconnect.",title:"Your video call was interrupted because this computer went to sleep."},toolbar:{accessibilityLabel:{audioOnly:"Toggle audio only",audioRoute:"Select the sound device",callQuality:"Manage call quality",cc:"Toggle subtitles",chat:"Toggle chat window",document:"Toggle shared document",feedback:"Leave feedback",fullScreen:"Toggle full screen",hangup:"Leave the call",invite:"Invite people",kick:"Kick participant",localRecording:"Toggle local recording controls",lockRoom:"Toggle meeting password",moreActions:"Toggle more actions menu",moreActionsMenu:"More actions menu",mute:"Toggle mute audio",pip:"Toggle Picture-in-Picture mode",profile:"Edit your profile",raiseHand:"Toggle raise hand",recording:"Toggle recording",remoteMute:"Mute participant",Settings:"Toggle settings",sharedvideo:"Toggle Youtube video sharing",shareRoom:"Invite someone",shareYourScreen:"Toggle screenshare",shortcuts:"Toggle shortcuts",show:"",speakerStats:"Toggle speaker statistics",tileView:"Toggle tile view",toggleCamera:"Toggle camera",videomute:"Toggle mute video",videoblur:""},addPeople:"Add people to your call",audioOnlyOff:"Disable audio only mode",audioOnlyOn:"Enable audio only mode",audioRoute:"Select the sound device",authenticate:"Authenticate",callQuality:"Manage call quality",chat:"Open / Close chat",closeChat:"Close chat",documentClose:"Close shared document",documentOpen:"Open shared document",enterFullScreen:"View full screen",enterTileView:"Enter tile view",exitFullScreen:"Exit full screen",exitTileView:"Exit tile view",feedback:"Leave feedback",hangup:"Leave",invite:"Invite people",login:"Log in",logout:"Log out",lowerYourHand:"Lower your hand",moreActions:"More actions",mute:"Mute / Unmute",openChat:"Open chat",pip:"Enter Picture-in-Picture mode",profile:"Edit your profile",raiseHand:"Raise / Lower your hand",raiseYourHand:"Raise your hand",Settings:"Settings",sharedvideo:"Share a YouTube video",shareRoom:"Invite someone",shortcuts:"View shortcuts",speakerStats:"Speaker stats",startScreenSharing:"Start screen sharing",startSubtitles:"Start subtitles",stopScreenSharing:"Stop screen sharing",stopSubtitles:"Stop subtitles",stopSharedVideo:"Stop YouTube video",talkWhileMutedPopup:"Trying to speak? You are muted.",tileViewToggle:"Toggle tile view",toggleCamera:"Toggle camera",videomute:"Start / Stop camera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Start / Stop subtitles",error:"Transcribing failed. Please try again.",expandedLabel:"Transcribing is currently on",failedToStart:"Transcribing failed to start",labelToolTip:"The meeting is being transcribed",off:"Transcribing stopped",pending:"Preparing to transcribe the meeting...",start:"Start showing subtitles",stop:"Stop showing subtitles",tr:"TR"},userMedia:{androidGrantPermissions:"Select Allow when your browser asks for permissions.",chromeGrantPermissions:"Select Allow when your browser asks for permissions.",edgeGrantPermissions:"Select Yes when your browser asks for permissions.",electronGrantPermissions:"Please grant permissions to use your camera and microphone",firefoxGrantPermissions:"Select Share Selected Device when your browser asks for permissions.",iexplorerGrantPermissions:"Select OK when your browser asks for permissions.",nwjsGrantPermissions:"Please grant permissions to use your camera and microphone",operaGrantPermissions:"Select Allow when your browser asks for permissions.","react-nativeGrantPermissions":"Select Allow when your browser asks for permissions.",safariGrantPermissions:"Select OK when your browser asks for permissions."},videoSIPGW:{busy:"We're working on freeing resources. Please try again in a few minutes.",busyTitle:"The Room service is currently busy",errorAlreadyInvited:"{{displayName}} already invited",errorInvite:"Conference not established yet. Please try again later.",errorInviteFailed:"We're working on resolving the issue. Please try again later.",errorInviteFailedTitle:"Inviting {{displayName}} failed",errorInviteTitle:"Error inviting room",pending:"{{displayName}} has been invited"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"You are in audio only mode. This mode saves bandwidth but you won't see videos of others.",callQuality:"Call Quality",hd:"HD",highDefinition:"High definition",labelTooiltipNoVideo:"No video",labelTooltipAudioOnly:"Audio-only mode enabled",ld:"LD",lowDefinition:"Low definition",onlyAudioAvailable:"Only audio is available",onlyAudioSupported:"We only support audio in this browser.",p2pEnabled:"Peer to Peer Enabled",p2pVideoQualityDescription:"In peer to peer mode, received call quality can only be toggled between high and audio only. Other settings will not be honoured until peer to peer is exited.",recHighDefinitionOnly:"Will prefer high definition.",sd:"SD",standardDefinition:"Standard definition"},videothumbnail:{domute:"Mute",flip:"Flip",kick:"Kick out",moderator:"Moderator",mute:"Member is muted",muted:"Muted",remoteControl:"Remote control",show:"",videomute:"Member has stopped the camera"},welcomepage:{accessibilityLabel:{join:"Tap to join",roomname:"Enter room name"},appDescription:"Go ahead, video chat with the whole team. In fact, invite everyone you know. {{app}} is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free \u2014 with no account needed.",audioVideoSwitch:{audio:"Voice",video:"Video"},calendar:"Calendar",connectCalendarButton:"Connect your calendar",connectCalendarText:"",enterRoomTitle:"Start a new meeting",go:"GO",join:"JOIN",info:"Info",privacy:"Privacy",recentList:"Recent",recentListDelete:"Delete",recentListEmpty:"Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",reducedUIText:"",roomname:"Enter room name",roomnameHint:"Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",sendFeedback:"Send feedback",terms:"Terms",title:"Secure, fully featured, and completely free video conferencing"}}},646,[]); -__d(function(a,n,r,e,l,o,s){l.exports={en:"Angla",af:"",az:"",bg:"Bulgara",cs:"",de:"Germana",el:"",eo:"Esperanto",es:"Hispana",fr:"Franca",hy:"Armena",it:"Itala",ja:"",ko:"",nb:"Norvega (Bukmola)",oc:"Okcitana",pl:"Pola",ptBR:"Portugala (Brazila)",ru:"Rusa",sk:"Slovaka",sl:"Slovena",sv:"Sveda",tr:"Turka",vi:"",zhCN:"\u0108ina (\u0108inuja)"}},647,[]); -__d(function(e,o,i,a,n,r,t){n.exports={addPeople:{add:"",countryNotSupported:"",countryReminder:"",disabled:"",failedToAdd:"",footerText:"",loading:"",loadingNumber:"",loadingPeople:"",noResults:"Nenio trovita",noValidNumbers:"",searchNumbers:"",searchPeople:"",searchPeopleAndNumbers:"",telephone:"",title:""},audioDevices:{bluetooth:"Bludento",headphones:"Kapa\u016dskultiloj",phone:"Telefono",speaker:"Parolanto"},audioOnly:{audioOnly:"Nur sono"},calendarSync:{addMeetingURL:"",confirmAddLink:"",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"",joinTooltip:"",nextMeeting:"",noEvents:"",ongoingMeeting:"",permissionButton:"",permissionMessage:"",refresh:"",today:""},chat:{error:"",messagebox:"",nickname:{popover:"Elektu ka\u015dnomon",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"Kunligita",AUTHENTICATING:"A\u016dtentiganta",AUTHFAIL:"A\u016dtentigo malsukcesis",CONNECTED:"Konektita",CONNECTING:"Konektanta",CONNFAIL:"Konekto malsukcesis",DISCONNECTED:"Malkonektita",DISCONNECTING:"Malkonektanta",ERROR:"Eraro",RECONNECTING:"Reta eraro okazis. Rekonektanta..."},connectionindicator:{address:"Adreso:",bandwidth:"Anta\u016dkalkulita kapacito:",bitrate:"Bitrapido:",bridgeCount:"",connectedTo:"",framerate:"Bildrapido:",less:"Montri malpli",localaddress:"Loka adreso:",localaddress_plural:"Lokaj adresoj:",localport:"Loka pordo:",localport_plural:"Lokaj pordoj:",more:"Motri pli",packetloss:"Paketperdo:",quality:{good:"Bona",inactive:"Neaktiva",lost:"Perdita",nonoptimal:"Neideala",poor:"Malbona"},remoteaddress:"Fora adreso:",remoteaddress_plural:"Foraj adresoj:",remoteport:"Fora pordo:",remoteport_plural:"Foraj pordoj:",resolution:"Distingivo:",status:"Konekto:",transport:"Transporto:",turn:" (truni)"},dateUtils:{earlier:"",today:"",yesterday:""},deepLinking:{appNotInstalled:"",description:"",descriptionWithoutWeb:"",downloadApp:"El\u015duti la aplika\u0135on",launchWebButton:"",openApp:"",title:"",tryAgainButton:""},defaultLink:"ekz. {{url}}",deviceError:{cameraError:"Atingo de via kamerao malsukcesis",cameraPermission:"Eraro akirante permeson por kamerao",microphoneError:"Atingo de via mikrofono malsukcesis",microphonePermission:"Eraro akirante permeson por mikrofono"},deviceSelection:{noPermission:"Permeso ne estis donita",previewUnavailable:"Anta\u016drigardo ne disponeblas",selectADevice:"Elektu aparaton",testAudio:""},dialog:{accessibilityLabel:{liveStreaming:"Tuja Elsendfluo"},allow:"Permesi",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Nur unu video estas permesata samtempe.",applicationWindow:"Programa fenestro",Back:"Reen",cameraConstraintFailedError:"Via kamerao ne observas kelkajn neprajn limigojn.",cameraNotFoundError:"Kamerao ne trovita.",cameraNotSendingData:"Via kamerao ne atingeblas al ni. Bonvolu kontroli, \u0109u alia programo jam uzas la aparaton, elekti alian de la agorda menuo, a\u016d provu \u011disdatigi la programon.",cameraNotSendingDataTitle:"Kamerao ne atingeblas",cameraPermissionDeniedError:"Vi ne permesis uzi vian kameraon. Vi povas ali\u011di al la kunveno, sed aliaj ne povos vin vidi. Vi povas tion \u015dan\u011di per la kameraa butono en la adresbreto.",cameraUnknownError:"Ne eblas uzi la kameraon, pro kialo nekonata.",cameraUnsupportedResolutionError:"Via kamerao ne subtenas la bezonatan distingivon.",Cancel:"Rezigni",close:"Fermi",conferenceDisconnectMsg:"Eble kontrolu vian retkonekton. Rekonekto post {{seconds}} sekundoj\u2026",conferenceDisconnectTitle:"Vi malkonekti\u011dis.",conferenceReloadMsg:"Ni penas funkciigi \u0109i tion. Rekonekto post {{seconds}} sekundoj\u2026",conferenceReloadTitle:"Malfeli\u0109e, io misokazis.",confirm:"",confirmNo:"",confirmYes:"Jes",connectError:"Oj! Io misokazis kaj ni ne povis vin konekti al la kunveno.",connectErrorWithMsg:"Oj! Io misokazis kaj ni ne povis vin konekti al la kunveno: {{msg}}",connecting:"Konektanta",contactSupport:"Kontakti helpon",copy:"Kopii",dismiss:"Formeti",displayNameRequired:"",done:"Finita",enterDisplayName:"",error:"Eraro",externalInstallationMsg:"Vi devas instali nian ekranvidadan kromprogramon.",externalInstallationTitle:"Kromprogramo bezonata",goToStore:"Iri al la retvendejo",gracefulShutdown:"Nia servo nun estas eksterreta pro prizorgado. Bonvolu reprovi poste.",IamHost:"Mi estas la gastiganto",incorrectRoomLockPassword:"",incorrectPassword:"Mal\u011dusta pasvorto a\u016d uzantnomo",inlineInstallationMsg:"Vi devas instali nian ekranvidadan kromprogramon.",inlineInstallExtension:"Instali nun",internalError:"Oj! La jena eraro okazis: {{error}}",internalErrorTitle:"Interna eraro",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Tuja Elsendfluo",liveStreamingDisabledForGuestTooltip:"",liveStreamingDisabledTooltip:"",lockMessage:"\u015closo de la kunveno malsukcesis.",lockRoom:"",lockTitle:"\u015closo malsukcesis",logoutQuestion:"\u0108u vi certe volas adia\u016di kaj fini la kunvenon?",logoutTitle:"Adia\u016di",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Via mikrofono ne observas kelkajn neprajn limigojn.",micNotFoundError:"Mikrofono ne trovita.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Vi ne permesis uzi vian mikrofonon. Vi povas ali\u011di al la kunveno, sed aliaj ne povos vin a\u016ddi. Vi povas tion \u015dan\u011di per la kameraa butono en la adresbreto.",micUnknownError:"Ne eblas uzi mikrofonon pro kialo nekonata.",muteParticipantBody:"Vi ne povos \u011din malsilentigi, sed \u011di povas sin malsilentigi kiam ajn.",muteParticipantButton:"Silentigi",muteParticipantDialog:"",muteParticipantTitle:"\u0108u silentigi \u0109i tiun partoprenanton?",Ok:"Bone",passwordLabel:"",passwordNotSupported:"Agordo de kunvena pasvorto ne estas subtenata",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Via foliumilo forbaras \u015dprucfenestrojn de tiu \u0109i retejo. Bonvolu permesi \u015dprucfenestrojn en la prisekuraj agordoj de via fenestro kaj reprovi.",popupErrorTitle:"\u015cprucfenestro barita",recording:"Registranta",recordingDisabledForGuestTooltip:"",recordingDisabledTooltip:"",rejoinNow:"Reali\u011di nun",remoteControlAllowedMessage:"{{user}} akceptis vian teleregan peton!",remoteControlDeniedMessage:"{{user}} rifuzis vian teleregan peton!",remoteControlErrorMessage:"Eraro petante teleregajn permesojn de {{user}}!",remoteControlRequestMessage:"\u0108u vi permesos al {{user}} teleregi vian komputilon?",remoteControlShareScreenWarning:"Sciu, ke se vi premos \xabPermesi\xbb, vi kunhavigos vian ekranon!",remoteControlStopMessage:"Telerega seanco finita!",remoteControlTitle:"Labortabla telerego",Remove:"Forigi",removePassword:"",removeSharedVideoMsg:"\u0108u vi vere volas forigi vian kunhavatan videon?",removeSharedVideoTitle:"Forigi kunhavatan videon",reservationError:"Rezerva sistema eraro",reservationErrorMsg:"Kodo de eraro: {{code}}, mesa\u011do: {{msg}}",retry:"Reprovi",screenSharingFailedToInstall:"Oj! Via ekranvidada kromprogramo malsukcesis instalon.",screenSharingFailedToInstallTitle:"Ekranvidada kromprogramo malsukcesis instalon",screenSharingFirefoxPermissionDeniedError:"",screenSharingFirefoxPermissionDeniedTitle:"",screenSharingPermissionDeniedError:"Oj! Io misokazis pri la permesoj al via ekranvidada kromprogramo. Bonvolu reviziti kaj reprovi.",serviceUnavailable:"Servo ne disponeblas",sessTerminated:"Voko finita",Share:"Kunhavi",shareVideoLinkError:"Bonvolu doni \u011dustan ligilon de YouTube",shareVideoTitle:"Kunhavi videon",shareYourScreen:"Kunhavigi vian ekranon",shareYourScreenDisabled:"",shareYourScreenDisabledForGuest:"",startLiveStreaming:"Fini tujan elsendfluon",startRecording:"Fini registradon",startRemoteControlErrorMessage:"Eraro okazis dum komenco de la telerega seanco!",stopLiveStreaming:"Fini tujan elsendfluon",stopRecording:"Fini registradon",stopRecordingWarning:"\u0108u vi certe volas fini la registradon?",stopStreamingWarning:"\u0108u vi certe volas fini la tujan elsendfluon?",streamKey:"",Submit:"Sendi",thankYou:"Dankon \u0109ar vi uzas {{appName}}!",token:"\u0135etono",tokenAuthFailed:"Pardonu, vi ne rajtas ali\u011di al \u0109i tiu voko.",tokenAuthFailedTitle:"A\u016dtentigo malsukcesis",transcribing:"",unlockRoom:"",userPassword:"uzantopasvorto",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"Atendanta la gastigan komputilon ...",Yes:"Jes",yourEntireScreen:"Via tuta ekrano"},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:"nun estas {{status}}"},feedback:{average:"Mezbona",bad:"Malbona",detailsLabel:"",good:"Bona",rateExperience:"Bonvolu priskribi vian sperton.",veryBad:"Tre malbona",veryGood:"Tre bona"},"\x05feedback":{},incomingCall:{answer:"",audioCallTitle:"",decline:"Formeti",productLabel:"",videoCallTitle:""},info:{accessibilityLabel:"",addPassword:"",cancelPassword:"",conferenceURL:"",country:"",dialANumber:"",dialInConferenceID:"",dialInNotSupported:"",dialInNumber:"",dialInSummaryError:"",dialInTollFree:"",genericError:"",inviteLiveStream:"",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Tuja Elsendfluo",moreNumbers:"",noNumbers:"",noPassword:"Neniu",noRoom:"",numbers:"",password:"",title:"Kunhavi",tooltip:"",label:""},"\x05info":{},inviteDialog:{alertText:"",header:"",searchCallOnlyPlaceholder:"Enigu telefonnumeron",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"Ni iom faletis.",retry:"Bonvolu reprovi",support:"Helpo",supportMsg:"Se tio \u0109i ripeti\u011dos, kontakti\u011du kun"},keyboardShortcuts:{focusLocal:"Fokusi vian propran videon",focusRemote:"Fokusi videon de alia vokano",fullScreen:"\u015calti / Mal\u015dalti tutekranan re\u011dimon",keyboardShortcuts:"Fulmoklavoj",localRecording:"",mute:"Silentigi a\u016d malsilentigi vian mikrofonon",pushToTalk:"Premi por paroli",raiseHand:"Levi a\u016d mallevi manon",showSpeakerStats:"Montri statistikon pri parolintoj",toggleChat:"Malfermi a\u016d fermi la babilon",toggleFilmstrip:"",toggleScreensharing:"Komuti inter kameraa kaj ekrana vidado",toggleShortcuts:"",videoMute:"\u015calti a\u016d mal\u015dalti vian kameraon"},"\x05keyboardShortcuts":{},liveStreaming:{busy:"Ni penas liberigi tujajn elsendilojn. Bonvolu reprovi post kelkaj minutoj.",busyTitle:"\u0108iuj elsendiloj nun okupi\u011das",changeSignIn:"",choose:"",chooseCTA:"",enterStreamKey:"",error:"Tuja elsendfluo malsukcesis. Bonvolu provi denove.",errorAPI:"",errorLiveStreamNotEnabled:"",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"Tuja elsendfluo malsukcesis komenci",getStreamKeyManually:"",invalidStreamKey:"",off:"Tuja elsendfluo fini\u011dis",on:"Tuja Elsendfluo",pending:"Komencanta Tujan Elsendfluon...",serviceName:"",signedInAs:"",signIn:"",signInCTA:"",signOut:"",start:"Fini tujan elsendfluon",streamIdHelp:"",unavailableTitle:"Tuja elsendfluo ne disponeblas"},"\x05liveStreaming":{},localRecording:{clientState:{off:"",on:"",unknown:""},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"",labelToolTip:"",localRecording:"",me:"Mi",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:""},moderator:"Kunvenestro",no:"",participant:"",participantStats:"",sessionToken:"",start:"Fini registradon",stop:"Fini registradon",yes:"Jes"},"\x05localRecording":{},lockRoomPassword:"Pasvorto",lockRoomPasswordUppercase:"Pasvorto",me:"mi",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"malkonektita",focus:"Kunvena atento",focusFail:"{{component}} ne atingelbas - reprovo post {{ms}} sekundoj",grantedTo:"Kunvenestraj rajtoj donitaj al {{to}}!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Mi",moderator:"Kunvenestraj rajtoj donitaj!",muted:"Vi komencis la interparolon silente.",mutedTitle:"Vi estas silentigita!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"Iu",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"",suboptimalExperienceTitle:"",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"agordita de alia partoprenanto",passwordDigitsOnly:"",poweredby:"povigita de",presenceStatus:{busy:"",calling:"",connected:"Konektita",connecting:"Konektanta",connecting2:"Konektanta",disconnected:"Malkonektita",expired:"",ignored:"",initializingCall:"",invited:"",rejected:"",ringing:""},"\x05presenceStatus":{},profile:{setDisplayNameLabel:"Agordi vian videblan nomon",setEmailInput:"Enigu retpo\u015dtadreson",setEmailLabel:"Retpo\u015dtadreso ligita al Gravatar",title:"Profilo"},recording:{authDropboxText:"",availableSpace:"",beta:"",busy:"Ni penas liberigi registrilojn. Bonvolu reprovi post kelkaj minutoj.",busyTitle:"\u0108iuj registriloj nun okupi\u011das",error:"Registrado malsukcesis. Bonvolu provi denove.",expandedOff:"Registrado finita",expandedOn:"",expandedPending:"",failedToStart:"Registrado malsukcesis komenci",fileSharingdescription:"",live:"",loggedIn:"",off:"Registrado finita",on:"Registranta",pending:"",rec:"",serviceDescription:"",serviceName:"",signIn:"",signOut:"",unavailable:"",unavailableTitle:"Registrado ne disponeblas"},"\x05recording":{},sectionList:{pullToRefresh:""},settings:{calendar:{about:"",disconnect:"Malkonektita",microsoftSignIn:"",signedIn:"",title:""},devices:"",followMe:"\u0108iuj sekvas min",language:"",loggedIn:"",moderator:"Kunvenestro",more:"",name:"Nomo",noDevice:"Neniu",selectAudioOutput:"Sona eligo",selectCamera:"Kamerao",selectMic:"Mikrofono",startAudioMuted:"\u0108iuj komenci\u011das silentaj",startVideoMuted:"\u0108iuj komenci\u011das ka\u015ditaj",title:"Agordoj"},"\x05settings":{calendar:{}},settingsView:{alertOk:"",alertTitle:"Averto",alertURLText:"",buildInfoSection:"",conferenceSection:"",displayName:"",email:"",header:"Agordoj",profileSection:"Profilo",serverURL:"",startWithAudioMuted:"",startWithVideoMuted:"",version:""},share:{dialInfoText:"",mainText:""},speaker:"Parolanto",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Nomo",seconds:"{{count}}s",speakerStats:"Statistikoj pri parolintoj",speakerTime:"Tempoj de parolintoj"},startupoverlay:{policyText:" ",title:"{{app}} bezonas viajn mikrofonon kaj kameraon."},suspendedoverlay:{rejoinKeyTitle:"Reali\u011di",text:"Premu la butonon Reali\u011di por rekonekti\u011di.",title:"Via vidvoko estis interrompita, \u0109ar la komputilo ekdormis."},toolbar:{accessibilityLabel:{audioOnly:"",audioRoute:"",callQuality:"",cc:"",chat:"",document:"Malfermi / Fermi komunan dokumenton",feedback:"",fullScreen:"",hangup:"",invite:"Inviti homojn",kick:"",localRecording:"",lockRoom:"",moreActions:"",moreActionsMenu:"",mute:"",pip:"",profile:"Redakti vian profilon",raiseHand:"",recording:"",remoteMute:"",Settings:"",sharedvideo:"",shareRoom:"",shareYourScreen:"",shortcuts:"",show:"",speakerStats:"",tileView:"",toggleCamera:"",videomute:"",videoblur:""},addPeople:"Aldoni homojn al via voko",audioOnlyOff:"",audioOnlyOn:"",audioRoute:"",authenticate:"A\u016dtentigi",callQuality:"",chat:"Malfermi / Fermi babilon",closeChat:"",documentClose:"Malfermi / Fermi komunan dokumenton",documentOpen:"Malfermi / Fermi komunan dokumenton",enterFullScreen:"",enterTileView:"",exitFullScreen:"",exitTileView:"",feedback:"",hangup:"Foriri",invite:"Inviti homojn",login:"Saluti",logout:"Adia\u016di",lowerYourHand:"",moreActions:"",mute:"Siletnigi / Malsilentigi",openChat:"",pip:"",profile:"Redakti vian profilon",raiseHand:"Levi / Mallevi vian manon",raiseYourHand:"",Settings:"Agordoj",sharedvideo:"Kunhavi videon de YouTube",shareRoom:"",shortcuts:"",speakerStats:"Statistikoj pri parolintoj",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"",talkWhileMutedPopup:"\u0108u vi klopodas paroli? Vi estas silentigita.",tileViewToggle:"",toggleCamera:"",videomute:"\u015calti / Mal\u015dalti kameraon",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{accessibilityLabel:{}},transcribing:{ccButtonTooltip:"",error:"Registrado malsukcesis. Bonvolu provi denove.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"",start:"",stop:"",tr:""},"\x05transcribing":{},userMedia:{androidGrantPermissions:"Elektu Permesi kiam via foliumilo petos permesojn.",chromeGrantPermissions:"Elektu Permesi kiam via foliumilo petos permesojn.",edgeGrantPermissions:"Elektu Jes kiam via foliumilo petos permesojn.",electronGrantPermissions:"Bonvolu doni la permeson uzi viajn kameraon kaj mikrofonon",firefoxGrantPermissions:"Elektu Havigi elektitan aparaton kiam via foliumilo petos permesojn.",iexplorerGrantPermissions:"Elektu Bone kiam via foliumilo petos permesojn.",nwjsGrantPermissions:"Bonvolu doni la permeson uzi viajn kameraon kaj mikrofonon",operaGrantPermissions:"Elektu Permesi kiam via foliumilo petos permesojn.","react-nativeGrantPermissions":"Elektu Permesi kiam via foliumilo petos permesojn.",safariGrantPermissions:"Elektu Bone kiam via foliumilo petos permesojn."},videoSIPGW:{busy:"",busyTitle:"",errorAlreadyInvited:"",errorInvite:"",errorInviteFailed:"",errorInviteFailedTitle:"",errorInviteTitle:"",pending:""},videoStatus:{audioOnly:"",audioOnlyExpanded:"",callQuality:"",hd:"AD",highDefinition:"Altkvalita distingivo",labelTooiltipNoVideo:"",labelTooltipAudioOnly:"Nure sona re\u011dimo \u015daltita",ld:"MD",lowDefinition:"Malaltkvalito distingivo",onlyAudioAvailable:"",onlyAudioSupported:"",p2pEnabled:"Samtavola re\u011dimo \u015daltita",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Preferos altkvalitan distingivon.",sd:"ND",standardDefinition:"Normalkvalita distingivo"},videothumbnail:{domute:"Silentigi",flip:"Renversi",kick:"Forpeli",moderator:"Kunvenestro",mute:"Partoprenanto silentigita",muted:"Silentigita",remoteControl:"Defora rego",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"",roomname:"Enigu nomon de \u0109ambro"},appDescription:"",audioVideoSwitch:{audio:"",video:""},calendar:"",connectCalendarButton:"",connectCalendarText:"",enterRoomTitle:"",go:"IRI",join:"ALI\u011cI",info:"",privacy:"Privateco",recentList:"",recentListDelete:"",recentListEmpty:"",reducedUIText:"",roomname:"Enigu nomon de \u0109ambro",roomnameHint:"",sendFeedback:"Sendi rimarkojn",terms:"Kondi\u0109oj",title:""}}},648,[]); -__d(function(o,a,n,e,r,s,i){r.exports={en:"Ingl\xe9s",af:"Africano",az:"Azerbaijani",bg:"B\xfalgaro",cs:"Czech",de:"Alem\xe1n",el:"Griego",eo:"Esperanto",es:"Espa\xf1ol",fr:"Franc\xe9s",hy:"Armenio",it:"Italiano",ja:"Jopones",ko:"Coreano",nb:"Noruego (bokmal)",oc:"Occitano",pl:"Polaco",ptBR:"Portugu\xe9s (Brasil)",ru:"Ruso",sk:"Eslovaco",sl:"Esloveno",sv:"Sueco",tr:"Turco",vi:"Vietnamita",zhCN:"Chino (China)"}},649,[]); -__d(function(e,a,o,r,i,n,t){i.exports={addPeople:{add:"Invitar",countryNotSupported:"Aun no contamos con soporte a este destino.",countryReminder:"\xbfLlamando fuera de los Estados Unidos? \xa1Por favor, aseg\xfarese de empezar con el c\xf3digo de pa\xeds!",disabled:"No puede invitar a otras personas.",failedToAdd:"Error al agregar participantes",footerText:"La marcaci\xf3n est\xe1 desactivada.",loading:"B\xfasqueda de personas y n\xfameros de tel\xe9fono",loadingNumber:"Validando el n\xfamero de tel\xe9fono",loadingPeople:"Buscando contactos a invitar",noResults:"No se encontraron coincidencias",noValidNumbers:"Por favor ingrese un n\xfamero de tel\xe9fono",searchNumbers:"Agregar n\xfameros de tel\xe9fono",searchPeople:"B\xfasqueda de personas",searchPeopleAndNumbers:"Buscar personas o a\xf1adir sus n\xfameros de tel\xe9fono",telephone:"Tel\xe9fono: {{number}}",title:"Invitar a otras personas a esta reuni\xf3n"},audioDevices:{bluetooth:"Bluetooth",headphones:"Aud\xedfonos",phone:"Tel\xe9fono",speaker:"Orador"},audioOnly:{audioOnly:"Solo audio"},calendarSync:{addMeetingURL:"Agregar un v\xednculo a la reuni\xf3n",confirmAddLink:"\xbfQuiere a\xf1adir un enlace de Jitsi a este evento?",error:{appConfiguration:"La integraci\xf3n del calendario no se est\xe1 configurada correctamente",generic:"Se ha producido un error. Compruebe la configuraci\xf3n del calendario o pruebe a recargarlo",notSignedIn:"Se ha producido un error de autenticaci\xf3n para ver los eventos del calendario. Compruebe la configuraci\xf3n del calendario e intente iniciar sesi\xf3n de nuevo"},join:"Unir",joinTooltip:"Unirse a la reuni\xf3n",nextMeeting:"pr\xf3xima reuni\xf3n",noEvents:"No hay eventos pr\xf3ximos programados.",ongoingMeeting:"reuni\xf3n en proceso",permissionButton:"Abrir ajustes",permissionMessage:"Los permisos al calendario son necesarios para ver sus reuniones en la aplicaci\xf3n.",refresh:"Actualizar calendario",today:"Hoy"},chat:{error:"Error: su mensaje \"{{originalText}}\" no fue enviado. Motivo: {{error}}",messagebox:"Escriba un mensaje",nickname:{popover:"Seleccione un apodo",title:"Introduzca un apodo para usar el chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Conect\xe1ndose a su reuni\xf3n\u2026"},connection:{ATTACHED:"Adjunto",AUTHENTICATING:"Autenticando",AUTHFAIL:"Fallo\u0301 la autenticacio\u0301n",CONNECTED:"Conectado",CONNECTING:"Conectando",CONNFAIL:"Conexi\xf3n fallida",DISCONNECTED:"Desconectado",DISCONNECTING:"Desconectando",ERROR:"Error",RECONNECTING:"Ocurri\xf3 un problema en la red. Reconectando..."},connectionindicator:{address:"Direcci\xf3n:",bandwidth:"Ancho de banda estimado:",bitrate:"Tasa de bits:",bridgeCount:"Contador del servidor: ",connectedTo:"Conectado a:",framerate:"Tasa de cuadros:",less:"Mostrar menos",localaddress:"Direcci\xf3n local:",localaddress_plural:"Direcciones locales:",localport:"Puerto local:",localport_plural:"Puertos locales:",more:"Ver m\xe1s",packetloss:"P\xe9rdida de paquetes:",quality:{good:"Bueno",inactive:"Inactivo",lost:"Perdida",nonoptimal:"No \xf3ptima",poor:"Pobre"},remoteaddress:"Direcci\xf3n remota:",remoteaddress_plural:"Direcciones remotas:",remoteport:"Puerto remoto:",remoteport_plural:"Puertos remotos:",resolution:"Resolucio\u0301n:",status:"Conexi\xf3n:",transport:"Transporte:",transport_plural:"Transportes:",turn:" (turnar)"},dateUtils:{earlier:"Anterior",today:"Hoy",yesterday:"Ayer"},deepLinking:{appNotInstalled:"Usted necesita la aplicaci\xf3n m\xf3vil {{app}} para unirse a esta reuni\xf3n en su tel\xe9fono.",description:"\xbfNo pas\xf3 nada? Hemos intentado iniciar su reuni\xf3n en la aplicaci\xf3n de escritorio {{app}}. intente de nuevo o inicie en la aplicaci\xf3n web {{app}}.",descriptionWithoutWeb:"",downloadApp:"Descargar la app",launchWebButton:"Iniciar en web",openApp:"Continuar a la aplicaci\xf3n",title:"Iniciar su reuni\xf3n en {{app}}...",tryAgainButton:"Intentar de nuevo en el escritorio"},defaultLink:"ej. {{url}}",deviceError:{cameraError:"Error al acceder a su c\xe1mara",cameraPermission:"Error al obtener permiso de la c\xe1mara",microphoneError:"Error al acceder a tu micr\xf3fono",microphonePermission:"Error al obtener permiso del micr\xf3fono"},deviceSelection:{noPermission:"Permiso no concedido",previewUnavailable:"Vista previa no disponible",selectADevice:"Seleccionar un dispositivo",testAudio:"Reproducir un sonido de prueba"},dialog:{accessibilityLabel:{liveStreaming:"Transmisi\xf3n en Vivo"},allow:"Permitir",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Solo se permite un video compartido a la vez",applicationWindow:"Ventana de aplicaci\xf3n",Back:"Anterior",cameraConstraintFailedError:"Su c\xe1mara no satisface algunos de los requerimientos.",cameraNotFoundError:"No se encontr\xf3 la c\xe1mara.",cameraNotSendingData:"No podemos acceder a su c\xe1mara. Verifique si otra aplicaci\xf3n est\xe1 usando este dispositivo, seleccione otro dispositivo en el men\xfa de configuraci\xf3n o intente volver a cargar la aplicaci\xf3n.",cameraNotSendingDataTitle:"No se puede acceder a la c\xe1mara",cameraPermissionDeniedError:"No ha otorgado permisos para usar su c\xe1mara. Puede unirse a la conferencia, pero no lo podr\xe1n ver. Utilice el bot\xf3n en la barra de direcci\xf3n para solucionar esto.",cameraUnknownError:"No se puede usar su c\xe1mara por motivos desconocidos.",cameraUnsupportedResolutionError:"Su c\xe1mara no soporta la resoluci\xf3n de video.",Cancel:"Cancelar",close:"Cerrar",conferenceDisconnectMsg:"Es posible que desee comprobar la conexi\xf3n de red. Reconectando en {{seconds}} segundos...",conferenceDisconnectTitle:"Ha sido desconectado.",conferenceReloadMsg:"Estamos tratando de arreglar esto. Reconectando en {{seconds}} segundos...",conferenceReloadTitle:"Desafortunadamente, algo sali\xf3 mal.",confirm:"Confirmar",confirmNo:"No",confirmYes:"S\xed",connectError:"\xa1Oops! Algo salio mal y no fue posible conectarnos a la conferencia.",connectErrorWithMsg:"\xa1Oops! Algo salio mal y no fue posible conectarnos a la conferencia: {{msg}}",connecting:"Conectando",contactSupport:"Contacte al soporte t\xe9cnico",copy:"Copiar",dismiss:"Descartar",displayNameRequired:"",done:"Ninguno",enterDisplayName:"",error:"Error",externalInstallationMsg:"Necesita instalar nuestra extensi\xf3n para compartir escritorio.",externalInstallationTitle:"Extensi\xf3n requerida",goToStore:"Ir al webstore",gracefulShutdown:"Nuestro servicio se encuentra en mantenimiento. Por favor, intente m\xe1s tarde.",IamHost:"Yo soy el anfitri\xf3n",incorrectRoomLockPassword:"",incorrectPassword:"Nombre de usuario o contrase\xf1a incorrecta",inlineInstallationMsg:"Necesita instalar nuestra extensi\xf3n para compartir escritorio.",inlineInstallExtension:"Instalar ahora",internalError:"\xa1Oops! Algo sali\xf3 mal. El siguiente error ocurri\xf3: {{error}}",internalErrorTitle:"Error interno",kickMessage:"",kickParticipantButton:"Expulsar",kickParticipantDialog:"\xbfSeguro que quiere expulsar a este participante?",kickParticipantTitle:"\xbfSilenciar a este participante?",kickTitle:"",liveStreaming:"Emisi\xf3n en Directo",liveStreamingDisabledForGuestTooltip:"Los invitados no pueden iniciar transmisiones en vivo.",liveStreamingDisabledTooltip:"Iniciar transmisi\xf3n en vivo deshabilitado.",lockMessage:"No se pudo bloquear la conferencia.",lockRoom:"",lockTitle:"El bloqueo fall\xf3",logoutQuestion:"\xbfEst\xe1 seguro que desea salir y detener la conferencia?",logoutTitle:"Cerrar sesi\xf3n",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"El micr\xf3fono no satisface algunos de los requerimientos.",micNotFoundError:"No se encontr\xf3 el micr\xf3fono.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"No ha otorgado permisos para usar su micr\xf3fono. Puede unirse a la conferencia, pero no lo podr\xe1n escuchar. Utilice el bot\xf3n en la barra de direcci\xf3n para solucionar esto.",micUnknownError:"No se puede usar su micr\xf3fono por motivos desconocidos.",muteParticipantBody:"No podr\xe1s quitarles el modo en silencio, pero ellos pueden quit\xe1rselo en cualquier momento.",muteParticipantButton:"Control de escritorio remoto",muteParticipantDialog:"\xbfSeguro que quiere silenciar a este participante? No podr\xe1 revertir esta acci\xf3n, pero el participante podr\xe1 hacerlo en cualquier momento",muteParticipantTitle:"\xbfSilenciar a este participante?",Ok:"Aceptar",passwordLabel:"",passwordNotSupported:"No se soporta establecer contrase\xf1a para una reuni\xf3n.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Su navegador est\xe1 bloqueando las ventanas emergentes de este sitio. Habilite las ventanas emergentes en la configuraci\xf3n de seguridad de su navegador y vuelva a intentarlo.",popupErrorTitle:"Ventana emergente bloqueada",recording:"Grabando",recordingDisabledForGuestTooltip:"Los hu\xe9spedes no pueden iniciar grabaciones.",recordingDisabledTooltip:"Inicio de grabaci\xf3n desactivado.",rejoinNow:"Reunirse ahora",remoteControlAllowedMessage:"{{user}} ha aceptado tu solicitud de control remoto!",remoteControlDeniedMessage:"{{user}} ha rechazado tu solicitud de control remoto!",remoteControlErrorMessage:"Ha ocurrido un error tratando de solicitar permiso de control remoto de {{user}}!",remoteControlRequestMessage:"\xbfPermitir\xe1 que {{user}} controle remotamente su escritorio?",remoteControlShareScreenWarning:"\xa1Tenga en cuenta que si presiona \"Permitir\" usted compartir\xe1 su pantalla!",remoteControlStopMessage:"La sesi\xf3n de control remoto ha finalizado!",remoteControlTitle:"Control de escritorio remoto",Remove:"Eliminar",removePassword:"",removeSharedVideoMsg:"\xbfEst\xe1 seguro que desea eliminar su v\xeddeo compartido?",removeSharedVideoTitle:"Eliminar video compartido",reservationError:"Error del sistema de reservaci\xf3n",reservationErrorMsg:"C\xf3digo de error: {{code}}, message: {{msg}}",retry:"Reintentar",screenSharingFailedToInstall:"Oops! Su extensi\xf3n de uso compartido de pantalla no se pudo instalar.",screenSharingFailedToInstallTitle:"La extensi\xf3n para compartir la pantalla no se pudo instalar",screenSharingFirefoxPermissionDeniedError:"Algo sali\xf3 mal mientras trat\xe1bamos de compartir la pantalla. Por favor, aseg\xfarese de que nos ha dado permiso para hacerlo. ",screenSharingFirefoxPermissionDeniedTitle:"OOPS! \xa1 No pudimos empezar a compartir la pantalla!",screenSharingPermissionDeniedError:"Oops! Algo sali\xf3 mal con sus permisos de extensi\xf3n para compartir pantalla. Por favor, vuelva a cargar e intente de nuevo.",serviceUnavailable:"Servicio no disponible",sessTerminated:"Llamada terminada",Share:"Compartir",shareVideoLinkError:"Por favor introduzca un enlace correcto de Youtube.",shareVideoTitle:"Compartir un v\xeddeo",shareYourScreen:"Compartir su pantalla",shareYourScreenDisabled:"Pantalla compartida desactivada.",shareYourScreenDisabledForGuest:"Los hu\xe9spedes no pueden compartir la pantalla.",startLiveStreaming:"Iniciar transmisi\xf3n en vivo",startRecording:"Iniciar la grabaci\xf3n",startRemoteControlErrorMessage:"\xa1Se ha producido un error al intentar iniciar la sesi\xf3n de control remoto!",stopLiveStreaming:"Detener transmisi\xf3n en vivo",stopRecording:"Parar grabaci\xf3n",stopRecordingWarning:"\xbfEst\xe1s seguro que quieres parar la grabaci\xf3n?",stopStreamingWarning:"\xbfEstas seguro que quieres parar la retransmisi\xf3n en directo?",streamKey:"Tecla de transmisi\xf3n en directo",Submit:"Enviar",thankYou:"\xa1Gracias por usar {{appName}}!",token:"token",tokenAuthFailed:"Lo siento, usted no tiene permiso para unirse a este llamada.",tokenAuthFailedTitle:"Fallo\u0301 la autenticacio\u0301n",transcribing:"Transcribiendo",unlockRoom:"",userPassword:"contrase\xf1a del usuario",WaitForHostMsg:"La conferencia {{room}} a\xfan no ha comenzado. Si usted es el anfitri\xf3n, por favor autent\xedquese. De lo contrario, espere a que llegue el anfitri\xf3n.",WaitForHostMsgWOk:"La conferencia {{room}} a\xfan no ha comenzado. Si usted es el anfitri\xf3n, presione Ok para autenticar. De lo contrario, espere a que llegue el anfitri\xf3n.",WaitingForHost:"Esperando al anfitri\xf3n ...",Yes:"S\xed",yourEntireScreen:"Su pantalla completa"},dialOut:{statusMessage:"esta {{status}}"},feedback:{average:"Promedio",bad:"Malo",detailsLabel:"Nos puede decir m\xe1s al respecto.",good:"Bueno",rateExperience:"Valore la experiencia de su reuni\xf3n.",veryBad:"Muy Mal",veryGood:"Muy Bien"},incomingCall:{answer:"Contestar",audioCallTitle:"Llamada entrante",decline:"Descartar",productLabel:"de Jitsi Meet",videoCallTitle:"Llamada de v\xeddeo"},info:{accessibilityLabel:"Mostrar Informaci\xf3n",addPassword:"",cancelPassword:"",conferenceURL:"Enlace:",country:"Pa\xeds",dialANumber:"Para unirse a su reuni\xf3n, marque uno de estos n\xfameros y luego ingrese el pin.",dialInConferenceID:"PIN:",dialInNotSupported:"Lo sentimos, actualmente no se admite la marcaci\xf3n.",dialInNumber:"Marcar:",dialInSummaryError:"Error al obtener informaci\xf3n de acceso telef\xf3nico ahora. Por favor, int\xe9ntelo de nuevo m\xe1s tarde.",dialInTollFree:"Llamada gratuita",genericError:"Ups, algo sali\xf3 mal.",inviteLiveStream:"Marcado de un solo toque: {{number}},,{{conferenceID}}#",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Usted est\xe1 invitado a unirse a una reuni\xf3n.",inviteURLFirstPartPersonal:"{{name}} te esta invitando a una sesi\xf3n.\n",inviteURLSecondPart:"",liveStreamURL:"Transmisi\xf3n en vivo:",moreNumbers:"M\xe1s n\xfameros",noNumbers:"Sin n\xfameros a marcar.",noPassword:"Ninguno",noRoom:"No se especific\xf3 la sala a marcar.",numbers:"N\xfameros de marcado",password:"",title:"Compartir",tooltip:"Compartir el enlace y la informaci\xf3n de acceso telef\xf3nico para esta reuni\xf3n",label:"Informaci\xf3n de la sesi\xf3n"},inviteDialog:{alertText:"Error al invitar a algunos participantes ",header:"Invitar",searchCallOnlyPlaceholder:"Introduzca n\xfamero de t\xe9lefono",searchPeopleOnlyPlaceholder:"Buscar participantes ",searchPlaceholder:"Participante o n\xfamero de tel\xe9fono ",send:"Enviar"},inlineDialogFailure:{msg:"Tuvimos un peque\xf1o tropiezo.",retry:"Intentar de nuevo",support:"Soporte",supportMsg:"Si esto sigue ocurriendo, cont\xe1ctenos para"},keyboardShortcuts:{focusLocal:"Enf\xf3cate en tu video",focusRemote:"Centrarse en el v\xeddeo de otra persona",fullScreen:"Ver o salir de pantalla completa",keyboardShortcuts:"Atajos de teclado",localRecording:"Mostrar u ocultar controles de grabaci\xf3n locales",mute:"Activar o desactivar micr\xf3fono",pushToTalk:"Presione para hablar",raiseHand:"Levantar o bajar la mano",showSpeakerStats:"Mostrar estad\xedsticas del locutor",toggleChat:"Abrir o cerrar panel de chat",toggleFilmstrip:"Mostrar/Ocultar miniaturas de video",toggleScreensharing:"Cambiar entre c\xe1mara y compartir pantalla",toggleShortcuts:"Mostrar/ocultar atajos del teclado",videoMute:"Activar o desactivar tu c\xe1mara"},liveStreaming:{busy:"Estamos trabajando para liberar recursos de transmisi\xf3n. Por favor, int\xe9ntelo de nuevo en unos minutos.",busyTitle:"Todos los streamers est\xe1n ocupados actualmente",changeSignIn:"Cambiar cuentas.",choose:"Elija una secuencia en directo",chooseCTA:"Elija una opci\xf3n de streaming. Actualmente est\xe1 registrado como {{email}}.",enterStreamKey:"Ingrese su clave de YouTube live stream aqu\xed.",error:"La transmisi\xf3n en vivo fall\xf3. Por favor, int\xe9ntelo de nuevo.",errorAPI:"Se produjo un error al acceder a las transmisiones de YouTube. Por favor intente iniciando sesi\xf3n nuevamente.",errorLiveStreamNotEnabled:"La transmisi\xf3n en vivo no est\xe1 activada en {{email}}. Por favor, active la transmisi\xf3n en vivo o inicie sesi\xf3n en una cuenta con transmisi\xf3n en vivo activada.",expandedOff:"La transmisi\xf3n en vivo se ha detenido",expandedOn:"La reuni\xf3n se est\xe1 transmitiendo a YouTube.",expandedPending:"La transmisi\xf3n en vivo se est\xe1 iniciando ...",failedToStart:"La transmisi\xf3n en vivo no se puso iniciar",getStreamKeyManually:"No pudimos buscar ninguna transmisi\xf3n en vivo. Trate de obtener su clave de transmisi\xf3n en vivo de YouTube.",invalidStreamKey:"La clave de transmisi\xf3n en vivo puede ser incorrecta.",off:"Transmisi\xf3n en vivo detenida",on:"Emisi\xf3n en Directo",pending:"Iniciando Emisi\xf3n en Directo...",serviceName:"Servicio de streaming en vivo",signedInAs:"Actualmente est\xe1 conectado como:",signIn:"Iniciar sesi\xf3n con Google",signInCTA:"Iniciar sesi\xf3n o ingrese su clave de transmisi\xf3n en vivo de YouTube.",signOut:"Cerrar sesi\xf3n",start:"Iniciar una transmisi\xf3n en vivo",streamIdHelp:"\xbfQu\xe9 es esto?",unavailableTitle:"Transmisi\xf3n en vivo no disponible"},localRecording:{clientState:{off:"Apagado",on:"Encendido",unknown:"Desconocido"},dialogTitle:"Controles de grabaci\xf3n local",duration:"Duraci\xf3n",durationNA:"N/A",encoding:"Codificaci\xf3n",label:"GLO",labelToolTip:"Grabaci\xf3n local activada",localRecording:"Grabaci\xf3n local",me:"Yo",messages:{engaged:"Grabaci\xf3n local activada.",finished:"Sesi\xf3n de grabaci\xf3n {{token}} terminada. Por favor, env\xede el archivo grabado al moderador.",finishedModerator:"Sesi\xf3n de grabaci\xf3n {{token}} terminado. Se ha guardado la grabaci\xf3n de la pista local. Por favor pida a los dem\xe1s participantes que presenten sus grabaciones.",notModerator:"Usted no es el moderador. No puede iniciar o detener la grabaci\xf3n local."},moderator:"Moderador",no:"No",participant:"Participante",participantStats:"Estad\xedstica de participantes",sessionToken:"Token de sesi\xf3n",start:"Iniciar grabaci\xf3n",stop:"Detener grabaci\xf3n",yes:"S\xed"},lockRoomPassword:"contrase\xf1a",lockRoomPasswordUppercase:"Contrase\xf1a",me:"yo",notify:{connectedOneMember:"{{name}} se uni\xf3 a la sesi\xf3n ",connectedThreePlusMembers:"{{name}} and {{count}} otros se unieron a la sesi\xf3n",connectedTwoMembers:"{{first}} and {{second}} se han unido a la sesi\xf3n",disconnected:"desconectado",focus:"Enfocar conferencia",focusFail:"{{component}} no disponible - reintentar en {{ms}} seg",grantedTo:"\xa1Se otorgaron privilegios de moderador a {{to}}!",invitedOneMember:"{{displayName}} ha sido invitado",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Yo",moderator:"\xa1Se otorgaron privilegios de moderador!",muted:"Has iniciado la conversaci\xf3n silenciado.",mutedTitle:"\xa1Est\xe1s silenciado!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} quisiera hablar.",somebody:"Alguien",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Eer... Al parecer su experiencia con {{appName}} no ser\xe1 tan buena aqu\xed. Estamos buscando formas de mejorar esto pero hasta entonces, intente utilizar uno de los navegadores compatibles.",suboptimalExperienceTitle:"Advertencia del Explorador",unmute:"",newDeviceCameraTitle:"Nueva c\xe1mara detectada ",newDeviceAudioTitle:"Nuevo dispositivo de audio detectado ",newDeviceAction:"Usar"},passwordSetRemotely:"definido por otro participante",passwordDigitsOnly:"",poweredby:"proporcionado por",presenceStatus:{busy:"Ocupado",calling:"Llamando...",connected:"Conectado",connecting:"Conectando\u2026",connecting2:"Conectando*\u2026",disconnected:"Desconectado",expired:"Expirado",ignored:"Ignorado",initializingCall:"Iniciando llamada...",invited:"Invitado",rejected:"Rechazado",ringing:"Timbrando..."},profile:{setDisplayNameLabel:"Establecer nombre a mostrar",setEmailInput:"Introducir e-mail",setEmailLabel:"Establecer su gravatar",title:"Perfil"},recording:{authDropboxText:"Subir a Dropbox",availableSpace:"Espacio disponible: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de grabaci\xf3n)",beta:"BETA",busy:"Estamos trabajando para liberar recursos de grabaci\xf3n. Por favor, int\xe9ntelo de nuevo en unos minutos.",busyTitle:"Todas las grabadoras est\xe1n actualmente ocupadas",error:"Falla de grabaci\xf3n. Vuelva a intentarlo.",expandedOff:"Grabaci\xf3n detenida",expandedOn:"La reuni\xf3n est\xe1 siendo grabada.",expandedPending:"La grabaci\xf3n se est\xe1 inciando...",failedToStart:"No se pudo iniciar la grabaci\xf3n",fileSharingdescription:"Compartir grabaci\xf3n con los participantes de la sesi\xf3n ",live:"Directo",loggedIn:"Sesi\xf3n iniciada como {{userName}}",off:"Grabaci\xf3n detenida",on:"Grabando",pending:"Preparando para grabar la reuni\xf3n...",rec:"REC",serviceDescription:"Tu grabaci\xf3n sera guardada por el servicio de grabaci\xf3n ",serviceName:"Servicio de grabaci\xf3n",signIn:"Entrar",signOut:"Cerrar sesi\xf3n",unavailable:"Oops! El {{serviceName}} no est\xe1 disponible actualmente. Estamos trabajando para resolver la situaci\xf3n. Por favor intente m\xe1s tarde.",unavailableTitle:"Grabaci\xf3n no disponible"},sectionList:{pullToRefresh:"Actualizar"},settings:{calendar:{about:"La integraci\xf3n del calendario {{appName}} se utiliza para acceder de forma segura a su calendario para que pueda leer los pr\xf3ximos eventos.",disconnect:"Desconectar",microsoftSignIn:"Iniciar sesi\xf3n con Microsoft",signedIn:"Actualmente accediendo a eventos de calendario para {{email}}. Haga clic en el bot\xf3n desconectar de abajo para detener el acceso a eventos de calendario.",title:"Calendario"},devices:"Dispositivos",followMe:"Todos me siguen",language:"Idioma",loggedIn:"Sesi\xf3n iniciada como {{name}}",moderator:"Moderador",more:"M\xe1s",name:"Nombre",noDevice:"Ninguno",selectAudioOutput:"Salida de audio",selectCamera:"Ca\u0301mara",selectMic:"Micr\xf3fono",startAudioMuted:"Todos inician en silencio",startVideoMuted:"Todos inician ocultos",title:"Ajustes"},settingsView:{alertOk:"OK",alertTitle:"Aviso",alertURLText:"La direcci\xf3n URL del servidor no es v\xe1lida",buildInfoSection:"Generar informaci\xf3n ",conferenceSection:"Conferencia",displayName:"Nombre a mostrar",email:"Email",header:"Ajustes",profileSection:"Perfil",serverURL:"URL del servidor",startWithAudioMuted:"Inicio con audio en silencio",startWithVideoMuted:"Iniciar con el v\xeddeo en silencio",version:"Versi\xf3n "},share:{dialInfoText:"",mainText:"Pulse en el siguiente enlace para unirse a la reuni\xf3n:\n{{roomUrl}}"},speaker:"Orador",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Nombre",seconds:"{{count}}s",speakerStats:"Estad\xedsticas del locutor",speakerTime:"Tiempo del locutor"},startupoverlay:{policyText:" ",title:"La video llamada se interrumpi\xf3 porque se detuvo este equipo."},suspendedoverlay:{rejoinKeyTitle:"Volver",text:"Presione el bot\xf3n Reunir para reconectarse.",title:"La video llamada se interrumpi\xf3 porque se detuvo este equipo."},toolbar:{accessibilityLabel:{audioOnly:"Alternar s\xf3lo audio",audioRoute:"Seleccione el dispositivo de sonido",callQuality:"Administrar la calidad de llamadas",cc:"Alternar subt\xedtulos",chat:"Alternar ventana de chat",document:"Alternar documento compartido",feedback:"Dejar comentarios",fullScreen:"Alternar pantalla completa",hangup:"Dejar la llamada",invite:"Invitar personas",kick:"Expulsar participante ",localRecording:"Alternar controles de grabaci\xf3n locales",lockRoom:"Cambiar contrase\xf1a de sesi\xf3n ",moreActions:"Men\xfa alternar m\xe1s acciones",moreActionsMenu:"Men\xfa m\xe1s acciones",mute:"Alternar audio mudo",pip:"Alternar modo de Picture-in-Picture",profile:"Editar tu perfil",raiseHand:"Levantar / Bajar tu mano",recording:"Activar grabaci\xf3n",remoteMute:"Silenciar participante ",Settings:"Alternar configuraci\xf3n",sharedvideo:"Alternar compartir un v\xeddeo de YouTube",shareRoom:"Invitar a alguien",shareYourScreen:"Alternar compartir pantalla",shortcuts:"Alternar accesos directos",show:"",speakerStats:"Alternar estad\xedsticas del orador",tileView:"Alternar vista de mosaico",toggleCamera:"Alternar c\xe1mara",videomute:"Alternar silencio de video",videoblur:""},addPeople:"Agregar personas a su llamada",audioOnlyOff:"Habilitar el modo de solo audio",audioOnlyOn:"Habilitar el modo de solo audio",audioRoute:"Seleccione el dispositivo de sonido",authenticate:"Autenticar",callQuality:"Administrar la calidad de llamadas",chat:"Abrir / cerrar sala de charla",closeChat:"Cerrar chat ",documentClose:"Cerrar documento compartido",documentOpen:"Abrir documento compartido",enterFullScreen:"Ver pantalla completa",enterTileView:"Entrar en la vista de mosaico",exitFullScreen:"Salir de pantalla completa",exitTileView:"Salir de la vista de mosaico",feedback:"Dejar comentarios",hangup:"Salir",invite:"Invitar personas",login:"Inicio de sesi\xf3n",logout:"Cerrar sesi\xf3n",lowerYourHand:"Baja tu mano",moreActions:"M\xe1s acciones",mute:"Activar / Desactivar Silencio",openChat:"Abrir chat",pip:"Entra en el modo Picture-in-Picture",profile:"Editar tu perfil",raiseHand:"Levantar / Bajar tu mano",raiseYourHand:"Levanta tu mano",Settings:"Ajustes",sharedvideo:"Compartir un v\xeddeo de YouTube",shareRoom:"Invitar a alguien",shortcuts:"Ver accesos directos",speakerStats:"Estad\xedsticas del locutor",startScreenSharing:"Iniciar el uso compartido de pantalla",startSubtitles:"Iniciar subt\xedtulos",stopScreenSharing:"Detener el uso compartido de pantalla ",stopSubtitles:"Detener subt\xedtulos ",stopSharedVideo:"Detener v\xeddeo de YouTube",talkWhileMutedPopup:"Tratas de hablar? Est\xe1s silenciado.",tileViewToggle:"Alternar vista de mosaico",toggleCamera:"Alternar c\xe1mara",videomute:"Iniciar / detener c\xe1mara",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Iniciar / Detener Subt\xedtulos",error:"La Transcripci\xf3n fall\xf3. Por favor, int\xe9ntelo nuevamente.",expandedLabel:"Transcripci\xf3n encendida",failedToStart:"No es posible iniciar la transcripci\xf3n",labelToolTip:"La reuni\xf3n se esta transcribiendo",off:"Transcripci\xf3n detenida",pending:"Preparando la transcripci\xf3n de la reuni\xf3n...",start:"Mostrar subt\xedtulos",stop:"Dejar de mostrar subt\xedtulos",tr:"TR"},userMedia:{androidGrantPermissions:"Seleccione Permitir cuando su navegador pida permisos.",chromeGrantPermissions:"Seleccione Permitir cuando su navegador pida permisos.",edgeGrantPermissions:"Seleccione S\xed cuando su navegador pida permisos.",electronGrantPermissions:"Por favor, conceda permisos para utilizar su c\xe1mara y micr\xf3fono",firefoxGrantPermissions:"Seleccione Compartir Dispositivo Seleccionado cuando su navegador pida permisos.",iexplorerGrantPermissions:"Seleccione OK cuando su navegador pida permisos.",nwjsGrantPermissions:"Por favor, conceda permisos para utilizar su c\xe1mara y micr\xf3fono",operaGrantPermissions:"Seleccione Permitir cuando su navegador pida permisos.","react-nativeGrantPermissions":"Seleccione Permitir cuando su navegador pida permisos.",safariGrantPermissions:"Seleccione OK cuando su navegador pida permisos."},videoSIPGW:{busy:"Estamos trabajando en liberar recursos. Por favor intente nuevamente en unos minutos.",busyTitle:"El servicio de las salas est\xe1 actualmente ocupado",errorAlreadyInvited:"{{displayName}} ya ha sido invitado",errorInvite:"La conferencia no se ha establecido aun. Por favor intente m\xe1s tarde.",errorInviteFailed:"Estamos trabajando en resolver la situaci\xf3n. Por favor intente de nuevo m\xe1s tarde.",errorInviteFailedTitle:"Invitar a {{displayName}} fall\xf3",errorInviteTitle:"Error al invitar a la sala",pending:"{{displayName}} ha sido invitado"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Se encuentra en modalidad solo audio. Esta modalidad ahorra ancho de banda sin embargo no ver\xe1 el video de otros.",callQuality:"",hd:"HD",highDefinition:"Alta definici\xf3n",labelTooiltipNoVideo:"No hay v\xeddeo",labelTooltipAudioOnly:"Modo de s\xf3lo audio activado",ld:"LD",lowDefinition:"Baja definici\xf3n",onlyAudioAvailable:"Solo hay audio disponible",onlyAudioSupported:"Solo soportamos audio en este navegador.",p2pEnabled:"Punto a Punto Activado",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Preferir\xe1 alta definici\xf3n.",sd:"SD",standardDefinition:"Definici\xf3n est\xe1ndar"},videothumbnail:{domute:"Control de escritorio remoto",flip:"Voltear",kick:"Expulsar",moderator:"Moderador",mute:"Participante est\xe1 silenciado",muted:"Silenciado",remoteControl:"Control remoto",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Toque para unirse",roomname:"Introduzca un nombre de sala"},appDescription:"Adelante, video chat con todo el equipo. De hecho, invita a todos los que conozcas. {{app}} es una soluci\xf3n de videoconferencia de c\xf3digo abierto de 100%, totalmente encriptada, que puede usar todo el d\xeda, todos los d\xedas, de forma gratuita, sin necesidad de contar con ninguna cuenta.",audioVideoSwitch:{audio:"Voz",video:"Video"},calendar:"Calendario",connectCalendarButton:"Conecte su calendario",connectCalendarText:"Conecte su calendario para ver todas sus reuniones en {{app}}. Plus, add {{provider}}reuniones a tu calendario e iniciarlas con un solo clic.",enterRoomTitle:"Comenzar una reuni\xf3n",go:"IR",join:"UNIRSE",info:"Informaci\xf3n",privacy:"Privacidad",recentList:"Reciente",recentListDelete:"Borrar",recentListEmpty:"Su lista de recientes est\xe1 actualmente vac\xeda. Chatea con tu equipo y encontrar\xe1s todas tus reuniones aqu\xed.",reducedUIText:"",roomname:"Introduzca un nombre de sala",roomnameHint:"Introduce el nombre o URL de la sala a la que quieres unirte. Puedes crear un nombre nuevo, s\xf3lo tienes que hacer llegar este nombre al resto de participantes para que puedan unirse a esta sala.",sendFeedback:"Enviar comentarios",terms:"T\xe9rminos",title:"Seguro, lleno de funcionalidades y videoconferencias completamente gratuitas"}}},650,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"",af:"",az:"",bg:"",cs:"",de:"",el:"",eo:"",es:"",fr:"",hy:"",it:"",ja:"",ko:"",nb:"",oc:"",pl:"",ptBR:"",ru:"",sk:"",sl:"",sv:"",tr:"",vi:"",zhCN:""}},651,[]); -__d(function(e,a,o,r,n,i,t){n.exports={addPeople:{add:"Invitar",countryNotSupported:"Todav\xeda no admitimos este destino.",countryReminder:"\xbfLlamas fuera de los EE.\xa0UU.? Aseg\xfarate de comenzar con el c\xf3digo de pa\xeds.",disabled:"No puedes invitar personas.",failedToAdd:"",footerText:"La marcaci\xf3n externa est\xe1 deshabilitada.",loading:"Buscar personas y n\xfameros de tel\xe9fono",loadingNumber:"Validar n\xfamero de tel\xe9fono",loadingPeople:"Buscar personas para invitar",noResults:"No se encontraron resultados de b\xfasqueda que coincidan",noValidNumbers:"Introduce un n\xfamero de tel\xe9fono",searchNumbers:"Agregar n\xfameros de tel\xe9fono",searchPeople:"Buscar personas",searchPeopleAndNumbers:"Buscar personas o agregar sus n\xfameros de tel\xe9fono",telephone:"Tel\xe9fono: {{number}}",title:"Invitar personas a esta reuni\xf3n"},audioDevices:{bluetooth:"Bluetooth",headphones:"Aud\xedfonos",phone:"Tel\xe9fono",speaker:"Altavoz"},audioOnly:{audioOnly:"Solo audio"},calendarSync:{addMeetingURL:"Agregar un enlace de reuni\xf3n",confirmAddLink:"\xbfDeseas agregar un enlace de Jitsi a este evento?",error:{appConfiguration:"La integraci\xf3n del calendario no est\xe1 correctamente configurada.",generic:"Se produjo un error. Comprueba la configuraci\xf3n del calendario o intenta actualizarlo.",notSignedIn:"Se produjo un error al autenticar para ver eventos de calendario. Comprueba la configuraci\xf3n del calendario e intenta volver a iniciar sesi\xf3n."},join:"Unirse",joinTooltip:"Unir a la reuni\xf3n",nextMeeting:"reuni\xf3n siguiente",noEvents:"No hay pr\xf3ximos eventos programados.",ongoingMeeting:"reuni\xf3n en progreso",permissionButton:"Abrir configuraci\xf3n",permissionMessage:"Se requiere el permiso del calendario para ver las reuniones en la aplicaci\xf3n.",refresh:"Actualizar calendario",today:"Hoy"},chat:{error:"Error: el mensaje \"{{originalText}}\" no se envi\xf3. Motivo: {{error}}",messagebox:"Escribir un mensaje",nickname:{popover:"Elegir un apodo",title:"Introducir un apodo para usar el chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Conect\xe1ndote a la reuni\xf3n..."},connection:{ATTACHED:"Adjunto",AUTHENTICATING:"Autenticaci\xf3n",AUTHFAIL:"Error de autenticaci\xf3n",CONNECTED:"Conectado",CONNECTING:"Conexi\xf3n",CONNFAIL:"Error de conexi\xf3n",DISCONNECTED:"Desconectado",DISCONNECTING:"Desconexi\xf3n",ERROR:"Error",RECONNECTING:"Se produjo un problema de red. Reconectando..."},connectionindicator:{address:"Direcci\xf3n:",bandwidth:"Ancho de banda estimado:",bitrate:"Velocidad de transferencia:",bridgeCount:"Recuento de servidor: ",connectedTo:"Conectado a:",framerate:"Velocidad de cuadro:",less:"Mostrar menos",localaddress:"Direcci\xf3n local:",localaddress_plural:"Direcciones locales:",localport:"Puerto local:",localport_plural:"Puertos locales:",more:"Mostrar m\xe1s",packetloss:"P\xe9rdida de paquetes:",quality:{good:"Bueno",inactive:"Inactivo",lost:"Perdido",nonoptimal:"No es \xf3ptimo",poor:"Deficiente"},remoteaddress:"Direcci\xf3n remota:",remoteaddress_plural:"Direcciones remotas:",remoteport:"Puerto remoto:",remoteport_plural:"Puertos remotos:",resolution:"Resoluci\xf3n:",status:"Conexi\xf3n:",transport:"Transporte:",transport_plural:"Transportes:",turn:" (activar/desactivar)"},dateUtils:{earlier:"M\xe1s temprano",today:"Hoy",yesterday:"Ayer"},deepLinking:{appNotInstalled:"Necesitas la aplicaci\xf3n m\xf3vil de {{app}} para unirte a esta reuni\xf3n en el tel\xe9fono.",description:"\xbfNo sucedi\xf3 nada? Intentamos iniciar la reuni\xf3n en la aplicaci\xf3n de escritorio de {{app}}. Vuelve a intentarlo o in\xedciala en la aplicaci\xf3n web de {{app}}.",descriptionWithoutWeb:"",downloadApp:"Descargar la aplicaci\xf3n",launchWebButton:"Iniciar en la Web",openApp:"Continuar a la aplicaci\xf3n",title:"Iniciando la reuni\xf3n en {{app}}...",tryAgainButton:"Volver a intentar en escritorio"},defaultLink:"por ejemplo, {{url}}",deviceError:{cameraError:"No se pudo acceder a la c\xe1mara",cameraPermission:"Error al obtener el permiso de la c\xe1mara",microphoneError:"No se pudo acceder al micr\xf3fono",microphonePermission:"Error al obtener el permiso del micr\xf3fono"},deviceSelection:{noPermission:"No se otorg\xf3 permiso",previewUnavailable:"Vista previa no disponible",selectADevice:"Seleccionar un dispositivo",testAudio:"Reproducir un sonido de prueba"},dialog:{accessibilityLabel:{liveStreaming:"Transmisi\xf3n en vivo"},allow:"Permitir",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Solo se permite un video compartido por vez",applicationWindow:"Ventana de aplicaci\xf3n",Back:"Volver",cameraConstraintFailedError:"La c\xe1mara no satisface algunas de las limitaciones requeridas.",cameraNotFoundError:"No se encontr\xf3 la c\xe1mara.",cameraNotSendingData:"No podemos acceder a la c\xe1mara. Comprueba si otra aplicaci\xf3n est\xe1 usando este dispositivo, selecciona otro dispositivo del men\xfa de configuraci\xf3n o intenta volver a cargar la aplicaci\xf3n.",cameraNotSendingDataTitle:"No es posible acceder a la c\xe1mara",cameraPermissionDeniedError:"No has otorgado permiso para usar la c\xe1mara. Puedes unirte a la conferencia de todos modos, pero los dem\xe1s asistentes no te podr\xe1n ver. Usa el bot\xf3n de la c\xe1mara en la barra de direcciones para solucionarlo.",cameraUnknownError:"No se puede usar la c\xe1mara por un motivo desconocido.",cameraUnsupportedResolutionError:"La c\xe1mara no admite la resoluci\xf3n de video requerida.",Cancel:"Cancelar",close:"Cerrar",conferenceDisconnectMsg:"Se recomienda que compruebes la conexi\xf3n de la red. Reconectando en {{seconds}} segundos...",conferenceDisconnectTitle:"Se te ha desconectado.",conferenceReloadMsg:"Estamos intentando solucionarlo. Reconectando en {{seconds}} segundos...",conferenceReloadTitle:"Lamentablemente, algo sali\xf3 mal.",confirm:"Confirmar",confirmNo:"No",confirmYes:"S\xed",connectError:"\xa1Uy! Algo sali\xf3 mal y no pudimos conectar con la conferencia.",connectErrorWithMsg:"\xa1Uy! Algo sali\xf3 mal y no pudimos conectar con la conferencia: {{msg}}",connecting:"Conexi\xf3n",contactSupport:"Contactar con soporte",copy:"Copiar",dismiss:"Descartar",displayNameRequired:"",done:"Listo",enterDisplayName:"",error:"Error",externalInstallationMsg:"Tienes que instalar nuestra extensi\xf3n de uso compartido del escritorio.",externalInstallationTitle:"Se requiere extensi\xf3n",goToStore:"Ir a la tienda web",gracefulShutdown:"Nuestro servicio est\xe1 actualmente interrumpido debido a tareas de mantenimiento. Vuelve a intentarlo m\xe1s tarde.",IamHost:"Soy el anfitri\xf3n",incorrectRoomLockPassword:"",incorrectPassword:"Nombre de usuario o contrase\xf1a incorrectos",inlineInstallationMsg:"Tienes que instalar nuestra extensi\xf3n de uso compartido del escritorio.",inlineInstallExtension:"Instalar ahora",internalError:"\xa1Uy! Algo sali\xf3 mal. Se produjo el siguiente error: {{error}}",internalErrorTitle:"Error interno",kickMessage:"",kickParticipantButton:"Echar",kickParticipantDialog:"\xbfEst\xe1s seguro de que deseas echar a este participante?",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Transmisi\xf3n en vivo",liveStreamingDisabledForGuestTooltip:"Los invitados no pueden iniciar la transmisi\xf3n en vivo.",liveStreamingDisabledTooltip:"Inicio de transmisi\xf3n en vivo deshabilitado.",lockMessage:"No se pudo bloquear la conferencia.",lockRoom:"",lockTitle:"Error de bloqueo",logoutQuestion:"\xbfEst\xe1s seguro de que deseas cerrar la sesi\xf3n y detener la conferencia?",logoutTitle:"Cierre de sesi\xf3n",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"El micr\xf3fono no cumple algunas de las restricciones requeridas.",micNotFoundError:"No se encontr\xf3 el micr\xf3fono.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"No has otorgado permiso para usar el micr\xf3fono. Puedes unirte a la conferencia de todos modos, pero los dem\xe1s asistente no te podr\xe1n escuchar. Usa el bot\xf3n de la c\xe1mara en la barra de direcciones para solucionarlo.",micUnknownError:"No se puede usar el micr\xf3fono por un motivo desconocido.",muteParticipantBody:"No podr\xe1s anular el silencio, pero \xe9l/ella podr\xe1 hacerlo en cualquier momento.",muteParticipantButton:"Silenciar",muteParticipantDialog:"\xbfEst\xe1s seguro de que deseas silenciar a este participante? No podr\xe1s anular el silencio, pero \xe9l/ella podr\xe1 hacerlo en cualquier momento.",muteParticipantTitle:"",Ok:"Aceptar",passwordLabel:"",passwordNotSupported:"",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"El navegador bloquea las ventanas emergentes de este sitio. Habil\xedtalas en la configuraci\xf3n de seguridad del navegador y vuelve a intentarlo.",popupErrorTitle:"Ventana emergente bloqueada",recording:"Grabaci\xf3n",recordingDisabledForGuestTooltip:"Los invitados no pueden iniciar grabaciones.",recordingDisabledTooltip:"Inicio de grabaci\xf3n deshabilitado.",rejoinNow:"Volver a unirse ahora",remoteControlAllowedMessage:"{{user}} acept\xf3 tu solicitud de control remoto.",remoteControlDeniedMessage:"{{user}} rechaz\xf3 tu solicitud de control remoto.",remoteControlErrorMessage:"Se produjo un error al intentar solicitar permisos de control remoto de {{user}}.",remoteControlRequestMessage:"\xbfPermites que {{user}} controle tu escritorio de manera remota?",remoteControlShareScreenWarning:"Ten en cuenta que si presionas \"Permitir\", compartir\xe1s tu pantalla.",remoteControlStopMessage:"La sesi\xf3n de control remoto finaliz\xf3.",remoteControlTitle:"Control de escritorio remoto",Remove:"Eliminar",removePassword:"",removeSharedVideoMsg:"\xbfEst\xe1s seguro de que deseas eliminar el video compartido?",removeSharedVideoTitle:"Eliminar video compartido",reservationError:"Error del sistema de reservaciones",reservationErrorMsg:"C\xf3digo de error: {{code}}, mensaje: {{msg}}",retry:"Volver a intentar",screenSharingFailedToInstall:"\xa1Uy! La extensi\xf3n de uso compartido de pantalla no se pudo instalar.",screenSharingFailedToInstallTitle:"La extensi\xf3n de uso compartido de pantalla no se pudo instalar",screenSharingFirefoxPermissionDeniedError:"Algo sali\xf3 mal cuando intentamos compartir tu pantalla. Aseg\xfarate de habernos dado permiso para hacerlo. ",screenSharingFirefoxPermissionDeniedTitle:"\xa1Uy! No pudimos iniciar el uso compartido de la pantalla.",screenSharingPermissionDeniedError:"\xa1Uy! Algo sali\xf3 mal con tus permisos de extensi\xf3n de uso compartido de pantalla. Vuelve a cargar e int\xe9ntalo nuevamente.",serviceUnavailable:"Servicio no disponible",sessTerminated:"Llamada finalizada",Share:"Compartir",shareVideoLinkError:"Proporciona un enlace de YouTube correcto.",shareVideoTitle:"Compartir un video",shareYourScreen:"Compartir tu pantalla",shareYourScreenDisabled:"Uso compartido de pantalla deshabilitado.",shareYourScreenDisabledForGuest:"Los invitados no pueden compartir la pantalla.",startLiveStreaming:"Iniciar transmisi\xf3n en vivo",startRecording:"Iniciar grabaci\xf3n",startRemoteControlErrorMessage:"Se produjo un error al intentar iniciar la sesi\xf3n de control remoto.",stopLiveStreaming:"Detener transmisi\xf3n en vivo",stopRecording:"Detener grabaci\xf3n",stopRecordingWarning:"\xbfEst\xe1s seguro de que deseas detener la grabaci\xf3n?",stopStreamingWarning:"\xbfEst\xe1s seguro de que deseas detener la transmisi\xf3n en vivo?",streamKey:"Clave de transmisi\xf3n en vivo",Submit:"Enviar",thankYou:"\xa1Gracias por usar {{appName}}!",token:"token",tokenAuthFailed:"Lo sentimos, no tienes permiso para unirte a esta llamada.",tokenAuthFailedTitle:"Error de autenticaci\xf3n",transcribing:"Transcripci\xf3n",unlockRoom:"",userPassword:"contrase\xf1a de usuario",WaitForHostMsg:"La conferencia {{room}} a\xfan no ha comenzado. Si eres el anfitri\xf3n, inicia la autenticaci\xf3n. De lo contrario, espera a que llegue el anfitri\xf3n.",WaitForHostMsgWOk:"La conferencia {{room}} a\xfan no ha comenzado. Si eres el anfitri\xf3n, presiona Aceptar para autenticar. De lo contrario, espera a que llegue el anfitri\xf3n.",WaitingForHost:"Esperando al anfitri\xf3n...",Yes:"S\xed",yourEntireScreen:"Toda tu pantalla"},dialOut:{statusMessage:"ahora est\xe1 {{status}}"},feedback:{average:"Promedio",bad:"Malo",detailsLabel:"Cu\xe9ntanos m\xe1s sobre eso.",good:"Bueno",rateExperience:"Califica tu experiencia con la reuni\xf3n",veryBad:"Muy malo",veryGood:"Muy bueno"},incomingCall:{answer:"Respuesta",audioCallTitle:"Llamada entrante",decline:"Descartar",productLabel:"de Jitsi Meet",videoCallTitle:"Llamada de video entrante"},info:{accessibilityLabel:"Mostrar informaci\xf3n",addPassword:"",cancelPassword:"",conferenceURL:"Enlace:",country:"Pa\xeds",dialANumber:"Para unirte a la reuni\xf3n, marca uno de estos n\xfameros y, luego introduce el PIN.",dialInConferenceID:"PIN:",dialInNotSupported:"Lo sentimos, la marcaci\xf3n interna actualmente no se admite.",dialInNumber:"Marcaci\xf3n interna:",dialInSummaryError:"Error al obtener informaci\xf3n de marcaci\xf3n interna ahora. Vuelve a intentarlo m\xe1s tarde.",dialInTollFree:"Sin cargo",genericError:"Epa, algo sali\xf3 mal.",inviteLiveStream:"Para ver la transmisi\xf3n en vivo de esta reuni\xf3n, haz clic en este enlace: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Est\xe1s invitado a unirte a una reuni\xf3n.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Transmisi\xf3n en vivo:",moreNumbers:"M\xe1s n\xfameros",noNumbers:"Sin n\xfameros de marcaci\xf3n interna.",noPassword:"Ninguno",noRoom:"No se especific\xf3 ninguna sala para la marcaci\xf3n interna.",numbers:"N\xfameros de marcaci\xf3n interna",password:"",title:"Compartir",tooltip:"Compartir enlace e informaci\xf3n de marcaci\xf3n interna para esta reuni\xf3n",label:"Informaci\xf3n de reuni\xf3n"},inviteDialog:{alertText:"No se pudieron invitar a algunos participantes.",header:"Invitar",searchCallOnlyPlaceholder:"Introducir n\xfamero de tel\xe9fono",searchPeopleOnlyPlaceholder:"Buscar participantes",searchPlaceholder:"Participante o n\xfamero de tel\xe9fono",send:"Enviar"},inlineDialogFailure:{msg:"Tartamudeamos un poco.",retry:"Volver a intentar",support:"Soporte",supportMsg:"Si esto sigue ocurriendo, contacta con"},keyboardShortcuts:{focusLocal:"Concentrar en tu video",focusRemote:"Concentrar en el video de otra persona",fullScreen:"Ver pantalla completa o salir de ella",keyboardShortcuts:"Accesos directos del teclado",localRecording:"Mostrar u ocultar controles de grabaci\xf3n local",mute:"Silenciar o anular silencio del micr\xf3fono",pushToTalk:"Presionar para hablar",raiseHand:"Levantar o bajar la mano",showSpeakerStats:"Mostrar estad\xedsticas del altavoz",toggleChat:"Abrir o cerrar el chat",toggleFilmstrip:"Mostrar u ocultar miniaturas de video",toggleScreensharing:"Cambiar entre el uso compartido de pantalla y de c\xe1mara",toggleShortcuts:"Mostrar u ocultar accesos directos del teclado",videoMute:"Iniciar o detener la c\xe1mara"},liveStreaming:{busy:"Estamos trabajando para liberar recursos de transmisi\xf3n. Vuelve a intentarlo en unos minutos.",busyTitle:"Todos los transmisores est\xe1n actualmente ocupados",changeSignIn:"Cambiar cuentas.",choose:"Elegir una transmisi\xf3n en vivo",chooseCTA:"Elegir una opci\xf3n de transmisi\xf3n. Actualmente, la sesi\xf3n est\xe1 iniciada como {{email}}.",enterStreamKey:"Introduce tu clave de transmisi\xf3n en vivo de YouTube aqu\xed.",error:"Error de transmisi\xf3n en vivo. Vuelve a intentarlo.",errorAPI:"Se produjo un error al acceder a tus difusiones de YouTube. Vuelve a intentar iniciar sesi\xf3n.",errorLiveStreamNotEnabled:"La transmisi\xf3n en vivo no est\xe1 habilitada en {{email}}. Habil\xedtala o inicia sesi\xf3n en una cuenta con la transmisi\xf3n en vivo habilitada.",expandedOff:"La transmisi\xf3n en vivo se ha detenido",expandedOn:"La reuni\xf3n se est\xe1 transmitiendo actualmente a YouTube.",expandedPending:"La transmisi\xf3n en vivo se est\xe1 iniciando...",failedToStart:"La transmisi\xf3n en vivo no se pudo iniciar",getStreamKeyManually:"No pudimos obtener ninguna transmisi\xf3n en vivo. Intenta obtener la clave de transmisi\xf3n en vivo de YouTube.",invalidStreamKey:"Es posible que la clave de transmisi\xf3n en vivo sea incorrecta.",off:"Transmisi\xf3n en vivo detenida",on:"Transmisi\xf3n en vivo",pending:"Iniciando transmisi\xf3n en vivo...",serviceName:"Servicio de transmisi\xf3n en vivo",signedInAs:"Actualmente, la sesi\xf3n est\xe1 iniciada como:",signIn:"Iniciar sesi\xf3n con Google",signInCTA:"Inicia sesi\xf3n o introduce la clave de transmisi\xf3n en vivo de YouTube.",signOut:"Cerrar sesi\xf3n",start:"Iniciar una transmisi\xf3n en vivo",streamIdHelp:"\xbfQu\xe9 es esto?",unavailableTitle:"Transmisi\xf3n en vivo no disponible"},localRecording:{clientState:{off:"Desactivado",on:"Activado",unknown:"Desconocido"},dialogTitle:"Controles de grabaci\xf3n local",duration:"Duraci\xf3n",durationNA:"N/d",encoding:"Codificaci\xf3n",label:"LOR",labelToolTip:"La grabaci\xf3n local est\xe1 activada",localRecording:"Grabaci\xf3n local",me:"Yo",messages:{engaged:"Grabaci\xf3n local activada.",finished:"Finaliz\xf3 la grabaci\xf3n del {{token}} de la sesi\xf3n. Env\xeda el archivo grabado al moderador.",finishedModerator:"Finaliz\xf3 la grabaci\xf3n del {{token}} de la sesi\xf3n. La grabaci\xf3n de la pista local se ha guardado. P\xeddeles a los otros participantes que env\xeden sus grabaciones.",notModerator:"No eres el moderador. No puedes iniciar o detener la grabaci\xf3n local."},moderator:"Moderador",no:"No",participant:"Participante",participantStats:"Estad\xedsticas del participante",sessionToken:"Token de la sesi\xf3n",start:"Iniciar grabaci\xf3n",stop:"Detener grabaci\xf3n",yes:"S\xed"},lockRoomPassword:"",lockRoomPasswordUppercase:"",me:"yo",notify:{connectedOneMember:"{{name}} se uni\xf3 a la reuni\xf3n",connectedThreePlusMembers:"{{name}} y {{count}} m\xe1s se unieron a la reuni\xf3n",connectedTwoMembers:"{{first}} y {{second}} se unieron a la reuni\xf3n",disconnected:"desconectado",focus:"Enfoque de la conferencia",focusFail:"{{component}} no disponible. Vuelve a intentar en {{ms}} segundos",grantedTo:"Se otorgaron derechos de moderador a {{to}}.",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Yo",moderator:"Derechos de moderador otorgados.",muted:"Has iniciado la conversaci\xf3n con el silencio activado.",mutedTitle:"Tienes el silencio activado.",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} desea hablar.",somebody:"Alguien",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Bueno... Lamentamos que tu experiencia con {{appName}} no sea tan buena aqu\xed. Estamos viendo la manera de mejorarlo, pero, hasta entonces, prueba con usar uno de los navegadores totalmente compatibles.",suboptimalExperienceTitle:"Advertencia del navegador",unmute:"",newDeviceCameraTitle:"Se detect\xf3 una c\xe1mara nueva",newDeviceAudioTitle:"Se detect\xf3 un dispositivo de audio nuevo",newDeviceAction:"Usar"},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"con tecnolog\xeda de",presenceStatus:{busy:"Ocupado",calling:"Llamando...",connected:"Conectado",connecting:"Conectando...",connecting2:"Conectando*...",disconnected:"Desconectado",expired:"Vencido",ignored:"Omitido",initializingCall:"Inicializando llamada...",invited:"Invitado",rejected:"Rechazado",ringing:"Sonando..."},profile:{setDisplayNameLabel:"Configurar tu nombre para mostrar",setEmailInput:"Introducir correo electr\xf3nico",setEmailLabel:"Configurar tu correo electr\xf3nico de Gravatar",title:"Perfil"},recording:{authDropboxText:"Cargar a Dropbox",availableSpace:"Espacio disponible: {{spaceLeft}}\xa0MB (aproximadamente {{duration}} minutos de grabaci\xf3n)",beta:"BETA",busy:"Estamos trabajando para liberar recursos de grabaci\xf3n. Vuelve a intentarlo en unos minutos.",busyTitle:"Todas las grabadoras est\xe1n actualmente ocupadas",error:"Error de grabaci\xf3n. Vuelve a intentarlo.",expandedOff:"La grabaci\xf3n se ha detenido",expandedOn:"La reuni\xf3n se est\xe1 grabando en este momento.",expandedPending:"La grabaci\xf3n se est\xe1 iniciando...",failedToStart:"La grabaci\xf3n no se pudo iniciar",fileSharingdescription:"Compartir grabaci\xf3n con participantes de la reuni\xf3n",live:"EN VIVO",loggedIn:"Sesi\xf3n iniciada como {{userName}}",off:"Grabaci\xf3n detenida",on:"Grabaci\xf3n",pending:"Preparando para grabar la reuni\xf3n...",rec:"REC",serviceDescription:"El servicio de grabaci\xf3n guardar\xe1 la grabaci\xf3n",serviceName:"Servicio de grabaci\xf3n",signIn:"Iniciar sesi\xf3n",signOut:"Cerrar sesi\xf3n",unavailable:"\xa1Uy! {{serviceName}} actualmente no est\xe1 disponible. Estamos trabajando para resolver el problema. Vuelve a intentarlo m\xe1s tarde.",unavailableTitle:"Grabaci\xf3n no disponible"},sectionList:{pullToRefresh:"Tirar para actualizar"},settings:{calendar:{about:"La integraci\xf3n del calendario de {{appName}} se usa para acceder al calendario de manera segura para que puedas leer los pr\xf3ximos eventos.",disconnect:"Desconectar",microsoftSignIn:"Iniciar sesi\xf3n con Microsoft",signedIn:"Actualmente se accede a eventos del calendario para {{email}}. Haz clic en el bot\xf3n Desconectar m\xe1s abajo para detener el acceso a eventos del calendario.",title:"Calendario"},devices:"Dispositivos",followMe:"Todos me siguen",language:"Idioma",loggedIn:"Sesi\xf3n iniciada como {{name}}",moderator:"Moderador",more:"M\xe1s",name:"Nombre",noDevice:"Ninguno",selectAudioOutput:"Salida de audio",selectCamera:"C\xe1mara",selectMic:"Micr\xf3fono",startAudioMuted:"Todos comienzan con el silencio activado",startVideoMuted:"Todos comienzan ocultos",title:"Configuraci\xf3n"},settingsView:{alertOk:"Aceptar",alertTitle:"Advertencia",alertURLText:"La direcci\xf3n URL de servidor introducida no es v\xe1lida",buildInfoSection:"Informaci\xf3n de compilaci\xf3n",conferenceSection:"Conferencia",displayName:"Nombre para mostrar",email:"Correo electr\xf3nico",header:"Configuraci\xf3n",profileSection:"Perfil",serverURL:"Direcci\xf3n URL del servidor",startWithAudioMuted:"Iniciar con el audio en silencio",startWithVideoMuted:"Iniciar con el video en silencio",version:"Versi\xf3n"},share:{dialInfoText:"",mainText:"Haz clic en el enlace siguiente para unirte a la reuni\xf3n:\n{{roomUrl}}"},speaker:"Altavoz",speakerStats:{hours:"{{count}}\xa0h",minutes:"{{count}}\xa0min",name:"Nombre",seconds:"{{count}}\xa0s",speakerStats:"Estad\xedsticas del altavoz",speakerTime:"Hora del altavoz"},startupoverlay:{policyText:" ",title:"{{app}} tiene que usar el micr\xf3fono y la c\xe1mara."},suspendedoverlay:{rejoinKeyTitle:"Volver a unirme",text:"Presiona el bot\xf3n Volver a unirme para volver a conectarte.",title:"La llamada de video se interrumpi\xf3 porque el equipo entr\xf3 en modo nocturno."},toolbar:{accessibilityLabel:{audioOnly:"Alternar solo audio",audioRoute:"Seleccionar el dispositivo de sonido",callQuality:"",cc:"Alternar subt\xedtulos",chat:"Alternar ventana de chat",document:"Alternar documento compartido",feedback:"Dejar comentario",fullScreen:"Alternar pantalla completa",hangup:"Dejar la llamada",invite:"Invitar personas",kick:"Echar participante",localRecording:"Alternar controles de grabaci\xf3n local",lockRoom:"Alternar contrase\xf1a de reuni\xf3n",moreActions:"Alternar men\xfa de m\xe1s acciones",moreActionsMenu:"Men\xfa de m\xe1s acciones",mute:"Alternar silenciar audio",pip:"Alternar modo de imagen en imagen",profile:"Editar el perfil",raiseHand:"Alternar levantar la mano",recording:"Alternar grabaci\xf3n",remoteMute:"Silenciar participante",Settings:"Alternar configuraci\xf3n",sharedvideo:"Alternar uso compartido de video de YouTube",shareRoom:"Invitar a alguien",shareYourScreen:"Alternar uso compartido de pantalla",shortcuts:"Alternar accesos directos",show:"",speakerStats:"Alternar estad\xedsticas del altavoz",tileView:"Alternar vista de mosaico",toggleCamera:"Alternar c\xe1mara",videomute:"Alternar silenciar video",videoblur:""},addPeople:"Agregar personas a la llamada",audioOnlyOff:"Deshabilitar modo de solo audio",audioOnlyOn:"Habilitar modo de solo audio",audioRoute:"Seleccionar el dispositivo de sonido",authenticate:"Autenticar",callQuality:"",chat:"Abrir/cerrar chat",closeChat:"Cerrar chat",documentClose:"Cerrar documento compartido",documentOpen:"Abrir documento compartido",enterFullScreen:"Ver pantalla completa",enterTileView:"Introducir vista de mosaico",exitFullScreen:"Salir de pantalla completa",exitTileView:"Salir de vista de mosaico",feedback:"Dejar comentario",hangup:"Dejar",invite:"Invitar personas",login:"Inicio de sesi\xf3n",logout:"Cierre de sesi\xf3n",lowerYourHand:"Bajar la mano",moreActions:"M\xe1s acciones",mute:"Silenciar/anular silencio",openChat:"Abrir chat",pip:"Introducir modo de imagen en imagen",profile:"Editar el perfil",raiseHand:"Levantar/bajar la mano",raiseYourHand:"Levantar la mano",Settings:"Configuraci\xf3n",sharedvideo:"Compartir un video de YouTube",shareRoom:"Invitar a alguien",shortcuts:"Ver accesos directos",speakerStats:"Estad\xedsticas del altavoz",startScreenSharing:"Iniciar uso compartido de pantalla",startSubtitles:"Iniciar subt\xedtulos",stopScreenSharing:"Detener uso compartido de pantalla",stopSubtitles:"Detener subt\xedtulos",stopSharedVideo:"Detener video de YouTube",talkWhileMutedPopup:"\xbfIntentas hablar? Est\xe1s silenciado.",tileViewToggle:"Alternar vista de mosaico",toggleCamera:"Alternar c\xe1mara",videomute:"Iniciar/detener c\xe1mara",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Iniciar/detener subt\xedtulos",error:"Error de transcripci\xf3n. Vuelve a intentarlo.",expandedLabel:"La transcripci\xf3n est\xe1 actualmente activada",failedToStart:"La transcripci\xf3n no se pudo iniciar",labelToolTip:"La reuni\xf3n se est\xe1 transcribiendo",off:"Transcripci\xf3n detenida",pending:"Preparando para transcribir la reuni\xf3n...",start:"Comenzar a mostrar subt\xedtulos",stop:"Dejar de mostrar subt\xedtulos",tr:"TR"},userMedia:{androidGrantPermissions:"Selecciona Permitir cuando el navegador solicite permisos.",chromeGrantPermissions:"Selecciona Permitir cuando el navegador solicite permisos.",edgeGrantPermissions:"Selecciona S\xed cuando el navegador solicite permisos.",electronGrantPermissions:"Otorga permisos para usar la c\xe1mara y el micr\xf3fono",firefoxGrantPermissions:"Selecciona Compartir dispositivo seleccionado cuando el navegador solicite permisos.",iexplorerGrantPermissions:"Selecciona Aceptar cuando el navegador solicite permisos.",nwjsGrantPermissions:"Otorga permisos para usar la c\xe1mara y el micr\xf3fono",operaGrantPermissions:"Selecciona Permitir cuando el navegador solicite permisos.","react-nativeGrantPermissions":"Selecciona Permitir cuando el navegador solicite permisos.",safariGrantPermissions:"Selecciona Aceptar cuando el navegador solicite permisos."},videoSIPGW:{busy:"Estamos trabajando para liberar recursos. Vuelve a intentarlo en unos minutos.",busyTitle:"El servicio de sala est\xe1 actualmente ocupado",errorAlreadyInvited:"{{displayName}} ya est\xe1 invitado",errorInvite:"Conferencia a\xfan no establecida. Vuelve a intentarlo m\xe1s tarde.",errorInviteFailed:"Estamos trabajando para resolver el problema. Vuelve a intentarlo m\xe1s tarde.",errorInviteFailedTitle:"No se pudo invitar a {{displayName}}",errorInviteTitle:"Error al invitar en la sala",pending:"{{displayName}} ha sido invitado"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Est\xe1s en modo de solo audio. Este modo ahorra ancho de banda, pero no podr\xe1s ver los videos de otras personas.",callQuality:"",hd:"HD",highDefinition:"Alta definici\xf3n",labelTooiltipNoVideo:"Sin video",labelTooltipAudioOnly:"Modo de solo audio habilitado",ld:"LD",lowDefinition:"Baja definici\xf3n",onlyAudioAvailable:"Modo de solo audio disponible",onlyAudioSupported:"Solo admitimos audio en este navegador.",p2pEnabled:"Punto a punto habilitado",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Preferir\xe9 alta definici\xf3n.",sd:"SD",standardDefinition:"Definici\xf3n est\xe1ndar"},videothumbnail:{domute:"Silenciar",flip:"Dar vuelta",kick:"Echar",moderator:"Moderador",mute:"",muted:"Silenciado",remoteControl:"Control remoto",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Toca para unirte",roomname:"Introducir nombre de sala"},appDescription:"Adelante, ten una sesi\xf3n de chat de video con todo el equipo. De hecho, invita a todas las personas que conozcas. {{app}} es una soluci\xf3n de conferencias en video en c\xf3digo abierto al 100% y completamente cifrada que puedes usar todo el d\xeda, todos los d\xedas y sin cargo, sin necesidad de tener una cuenta.",audioVideoSwitch:{audio:"Voz",video:"Video"},calendar:"Calendario",connectCalendarButton:"Conectar el calendario",connectCalendarText:"",enterRoomTitle:"Iniciar una nueva reuni\xf3n",go:"IR",join:"UNIRSE",info:"Informaci\xf3n",privacy:"Privacidad",recentList:"Reciente",recentListDelete:"Eliminar",recentListEmpty:"Tu lista reciente est\xe1 actualmente vac\xeda. Ten una sesi\xf3n de chat con tu equipo y encontrar\xe1s todas tus reuniones recientes aqu\xed.",reducedUIText:"",roomname:"Introducir nombre de sala",roomnameHint:"Introduce el nombre o la direcci\xf3n URL de la sala a la que deseas unirte. Puedes inventar un nombre, simplemente inf\xf3rmaselo a las personas con las que te reunir\xe1s para que introduzcan el mismo nombre.",sendFeedback:"Enviar comentario",terms:"T\xe9rminos",title:"Conferencias en video seguras, con gran variedad de funciones y completamente gratuitas"}}},652,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"",af:"",az:"",bg:"",cs:"",de:"",el:"",eo:"",es:"",fr:"",hy:"",it:"",ja:"",ko:"",nb:"",oc:"",pl:"",ptBR:"",ru:"",sk:"",sl:"",sv:"",tr:"",vi:"",zhCN:""}},653,[]); -__d(function(t,e,i,a,o,n,s){o.exports={addPeople:{add:"Kutsu",countryNotSupported:"T\xe4t\xe4 maata ei viel\xe4 tueta.",countryReminder:"Soitatko ulkomaille? Muista lis\xe4t\xe4 alkuun maakoodi!",disabled:"Et voi l\xe4hett\xe4\xe4 kutsuja.",failedToAdd:"",footerText:"Soittaminen on estetty.",loading:"Etsit\xe4\xe4n henkil\xf6it\xe4 ja puhelinnumeroita",loadingNumber:"Tarkistetaan puhelinnumeroa",loadingPeople:"Etsit\xe4\xe4n kutsuttavia henkil\xf6it\xe4",noResults:"Ei hakua vastaavia tuloksia",noValidNumbers:"Anna puhelinnumero",searchNumbers:"Lis\xe4\xe4 puhelinnumeroita",searchPeople:"Etsi henkil\xf6it\xe4",searchPeopleAndNumbers:"Etsi henkil\xf6it\xe4 tai lis\xe4\xe4 heid\xe4n puhelinnumeronsa",telephone:"Puhelin: {{number}}",title:"Kutsu henkil\xf6it\xe4 t\xe4h\xe4n kokoukseen"},audioDevices:{bluetooth:"Bluetooth",headphones:"Kuulokkeet",phone:"Puhelin",speaker:"Puhuja"},audioOnly:{audioOnly:"Vain \xe4\xe4ni"},calendarSync:{addMeetingURL:"Lis\xe4\xe4 kokouslinkki",confirmAddLink:"Haluatko lis\xe4t\xe4 tapahtumaan Jitsi-linkin?",error:{appConfiguration:"Kalenterin integrointia ei ole m\xe4\xe4ritetty asianmukaisesti.",generic:"Tapahtui virhe. Tarkista kalenterin m\xe4\xe4ritykset tai p\xe4ivit\xe4 kalenterin\xe4kym\xe4.",notSignedIn:"Kalenterin tapahtumien tarkasteluun tarvittavassa todennuksessa tapahtui virhe. Tarkista kalenterin asetukset ja kirjaudu sis\xe4\xe4n uudelleen."},join:"Liity",joinTooltip:"Liity kokoukseen",nextMeeting:"seuraava kokous",noEvents:"Aikataulussa ei ole tulevia tapahtumia.",ongoingMeeting:"meneill\xe4\xe4n oleva kokous",permissionButton:"Avaa asetukset",permissionMessage:"Kalenterin k\xe4ytt\xf6oikeus vaaditaan, jotta kokouksesi n\xe4kyv\xe4t sovelluksessa.",refresh:"P\xe4ivit\xe4 kalenteri",today:"T\xe4n\xe4\xe4n"},chat:{error:"Virhe: viesti\xe4 \"{{originalText}}\" ei l\xe4hetetty. Syy: {{error}}",messagebox:"Kirjoita viesti",nickname:{popover:"Valitse lempinimi",title:"Anna chatiss\xe4 k\xe4ytett\xe4v\xe4 lempinimi"},title:"Chatti"},connectingOverlay:{joiningRoom:"Yhdistet\xe4\xe4n kokoukseen..."},connection:{ATTACHED:"Liitteen\xe4",AUTHENTICATING:"Todennetaan",AUTHFAIL:"Todennus ep\xe4onnistui",CONNECTED:"Yhdistetty",CONNECTING:"Yhdistet\xe4\xe4n",CONNFAIL:"Yhdist\xe4minen ep\xe4onnistui",DISCONNECTED:"Ei yhteytt\xe4",DISCONNECTING:"Yhteytt\xe4 katkaistaan",ERROR:"Virhe",RECONNECTING:"Tapahtui verkkovirhe. Yhdistet\xe4\xe4n uudelleen..."},connectionindicator:{address:"Osoite:",bandwidth:"Arvioitu kaistanleveys:",bitrate:"Bittinopeus:",bridgeCount:"Palvelimien m\xe4\xe4r\xe4: ",connectedTo:"Yhdistetty kohteeseen:",framerate:"Kuvataajuus:",less:"N\xe4yt\xe4 v\xe4hemm\xe4n",localaddress:"Paikallinen osoite:",localaddress_plural:"Paikalliset osoitteet:",localport:"Paikallinen portti:",localport_plural:"Paikalliset portit:",more:"N\xe4yt\xe4 lis\xe4\xe4",packetloss:"Pakettien menetys:",quality:{good:"Hyv\xe4",inactive:"Ep\xe4aktiivinen",lost:"Katkennut",nonoptimal:"Ei optimaalinen",poor:"Huono"},remoteaddress:"Et\xe4osoite:",remoteaddress_plural:"Et\xe4osoitteet:",remoteport:"Et\xe4portti:",remoteport_plural:"Et\xe4portit:",resolution:"Resoluutio:",status:"Yhteys:",transport:"Kuljetus:",transport_plural:"Kuljetukset:",turn:" (vuoro)"},dateUtils:{earlier:"Aikaisemmin",today:"T\xe4n\xe4\xe4n",yesterday:"Eilen"},deepLinking:{appNotInstalled:"Tarvitset mobiilisovelluksen {{app}}, jotta voit liitty\xe4 t\xe4h\xe4n kokoukseen puhelimellasi.",description:"Eik\xf6 mit\xe4\xe4n tapahtunut? Yritimme k\xe4ynnist\xe4\xe4 kokouksen ty\xf6p\xf6yt\xe4sovelluksessa {{app}}. Yrit\xe4 uudelleen tai k\xe4ynnist\xe4 se verkkosovelluksessa {{app}}.",descriptionWithoutWeb:"",downloadApp:"Lataa sovellus",launchWebButton:"K\xe4ynnist\xe4 verkossa",openApp:"Jatka sovellukseen",title:"K\xe4ynnistet\xe4\xe4n kokousta sovelluksessa {{app}}...",tryAgainButton:"Yrit\xe4 uudelleen ty\xf6p\xf6yt\xe4sovelluksella"},defaultLink:"esim. {{url}}",deviceError:{cameraError:"Kameran k\xe4ytt\xf6 ei onnistunut",cameraPermission:"Virhe kameran k\xe4ytt\xf6oikeuksien hankkimisessa",microphoneError:"Mikrofonin k\xe4ytt\xf6 ei onnistunut",microphonePermission:"Virhe mikrofonin k\xe4ytt\xf6oikeuksien hankkimisessa"},deviceSelection:{noPermission:"K\xe4ytt\xf6oikeuksia ei my\xf6nnetty",previewUnavailable:"Esikatselu ei k\xe4ytett\xe4viss\xe4",selectADevice:"Valitse laite",testAudio:"Soita testi\xe4\xe4ni"},dialog:{accessibilityLabel:{liveStreaming:"Suoratoisto"},allow:"Salli",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Vain yksi jaettu video kerrallaan",applicationWindow:"Sovellusikkuna",Back:"Takaisin",cameraConstraintFailedError:"Kamerasi ei t\xe4yt\xe4 joitain pakollisia vaatimuksia.",cameraNotFoundError:"Kameraa ei l\xf6ydy.",cameraNotSendingData:"Kameran k\xe4ytt\xf6 ei onnistu. Tarkista, k\xe4ytt\xe4\xe4k\xf6 jokin toinen sovellus t\xe4t\xe4 laitetta, valitse toinen laite asetusvalikosta tai yrit\xe4 k\xe4ynnist\xe4\xe4 sovellus uudelleen.",cameraNotSendingDataTitle:"Kameran k\xe4ytt\xf6 ei onnistu",cameraPermissionDeniedError:"Et ole my\xf6nt\xe4nyt lupaa kamerasi k\xe4ytt\xf6\xf6n. Voit liitty\xe4 kokoukseen, mutta muut eiv\xe4t n\xe4e sinua. Korjaa tilanne osoitekent\xe4n kamerapainikkeella.",cameraUnknownError:"Kameran k\xe4ytt\xf6 ei onnistu tuntemattomasta syyst\xe4.",cameraUnsupportedResolutionError:"Kamerasi ei tue vaadittua videoresoluutiota.",Cancel:"Peruuta",close:"Sulje",conferenceDisconnectMsg:"Tarkista verkkoyhteys. Yhdistet\xe4\xe4n uudelleen {{seconds}} sekunnin kuluttua...",conferenceDisconnectTitle:"Yhteys on katkennut.",conferenceReloadMsg:"Yrit\xe4mme korjata tilannetta. Yhdistet\xe4\xe4n uudelleen {{seconds}} sekunnin kuluttua...",conferenceReloadTitle:"Valitettavasti jokin meni vikaan.",confirm:"Vahvista",confirmNo:"Ei",confirmYes:"Kyll\xe4",connectError:"Hups! Jokin meni vikaan, ja kokoukseen yhdist\xe4minen ei onnistunut.",connectErrorWithMsg:"Hups! Jokin meni pieleen ja kokoukseen yhdist\xe4minen ei onnistunut: {{msg}}",connecting:"Yhdistet\xe4\xe4n",contactSupport:"Ota yhteytt\xe4 tukeen",copy:"Kopioi",dismiss:"Hylk\xe4\xe4",displayNameRequired:"",done:"Valmis",enterDisplayName:"",error:"Virhe",externalInstallationMsg:"Asenna ty\xf6p\xf6yd\xe4n jakolaajennuksemme.",externalInstallationTitle:"Laajennus vaaditaan",goToStore:"Siirry verkkokauppaan",gracefulShutdown:"Palvelu on keskeytetty huoltoa varten. Yrit\xe4 my\xf6hemmin uudelleen.",IamHost:"Olen vet\xe4j\xe4",incorrectRoomLockPassword:"",incorrectPassword:"Virheellinen k\xe4ytt\xe4j\xe4tunnus tai salasana",inlineInstallationMsg:"Asenna ty\xf6p\xf6yd\xe4n jakolaajennuksemme.",inlineInstallExtension:"Asenna nyt",internalError:"Hups! Jokin meni vikaan. Tapahtui seuraava virhe: {{error}}",internalErrorTitle:"Sis\xe4inen virhe",kickMessage:"",kickParticipantButton:"Poista",kickParticipantDialog:"Haluatko varmasti poistaa t\xe4m\xe4n osanottajan kokouksesta?",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Suoratoisto",liveStreamingDisabledForGuestTooltip:"Vieraat eiv\xe4t voi aloittaa suoratoistol\xe4hetyst\xe4.",liveStreamingDisabledTooltip:"Suoratoiston aloitus ei ole k\xe4yt\xf6ss\xe4.",lockMessage:"Kokouksen lukitseminen ei onnistunut.",lockRoom:"",lockTitle:"Lukitus ep\xe4onnistui",logoutQuestion:"Haluatko varmasti kirjautua ulos ja lopettaa kokouksen?",logoutTitle:"Kirjaudu ulos",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Mikrofonisi ei t\xe4yt\xe4 joitain pakollisia vaatimuksia.",micNotFoundError:"Mikrofonia ei l\xf6ytynyt.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Et ole my\xf6nt\xe4nyt lupaa mikrofonisi k\xe4ytt\xf6\xf6n. Voit liitty\xe4 kokoukseen, mutta muut eiv\xe4t kuule sinua. Korjaa tilanne osoitekent\xe4n kamerapainikkeella.",micUnknownError:"Mikrofonin k\xe4ytt\xf6 ei onnistu tuntemattomasta syyst\xe4.",muteParticipantBody:"Et voi poistaa muiden mykistyst\xe4, mutta he voivat poistaa oman mykistyksens\xe4 milloin tahansa.",muteParticipantButton:"Mykist\xe4",muteParticipantDialog:"Haluatko varmasti mykist\xe4\xe4 t\xe4m\xe4n osanottajan? Et voi sen j\xe4lkeen poistaa mykistyst\xe4, mutta h\xe4n voi poistaa sen itse milloin tahansa.",muteParticipantTitle:"",Ok:"Ok",passwordLabel:"",passwordNotSupported:"",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Selaimesi est\xe4\xe4 t\xe4m\xe4 sivuston ponnahdusikkunat. Salli ponnahdusikkunat selaimen suojausasetuksista ja yrit\xe4 uudelleen.",popupErrorTitle:"Ponnahdusikkuna estetty",recording:"Nauhoitetaan",recordingDisabledForGuestTooltip:"Vieraat eiv\xe4t voi nauhoittaa.",recordingDisabledTooltip:"Nauhoitus ei ole k\xe4yt\xf6ss\xe4.",rejoinNow:"Liity nyt uudelleen",remoteControlAllowedMessage:"{{user}} hyv\xe4ksyi et\xe4k\xe4ytt\xf6pyynt\xf6si!",remoteControlDeniedMessage:"{{user}} hylk\xe4si et\xe4k\xe4ytt\xf6pyynt\xf6si!",remoteControlErrorMessage:"Tapahtui virhe, kun et\xe4k\xe4ytt\xf6oikeuksia pyydettiin henkil\xf6lt\xe4 {{user}}!",remoteControlRequestMessage:"Sallitko, ett\xe4 {{user}} et\xe4k\xe4ytt\xe4\xe4 tietokonettasi?",remoteControlShareScreenWarning:"Huomaa, ett\xe4 Salli-vaihtoehdon valitsemalla jaat n\xe4ytt\xf6si!",remoteControlStopMessage:"Et\xe4k\xe4ytt\xf6istunto p\xe4\xe4ttyi!",remoteControlTitle:"Tietokoneen et\xe4k\xe4ytt\xf6",Remove:"Poista",removePassword:"",removeSharedVideoMsg:"Haluatko varmasti poistaa jaetun videon?",removeSharedVideoTitle:"Poista jaettu video",reservationError:"Varausj\xe4rjestelm\xe4n virhe",reservationErrorMsg:"Virhekoodi: {{code}}, viesti: {{msg}}",retry:"Yrit\xe4 uudelleen",screenSharingFailedToInstall:"Hups! N\xe4yt\xf6njakolaajennuksen asennus ep\xe4onnistui.",screenSharingFailedToInstallTitle:"N\xe4yt\xf6njakolaajennuksen asennus ep\xe4onnistui",screenSharingFirefoxPermissionDeniedError:"Jokin meni vikaan, kun yritimme jakaa n\xe4ytt\xf6si. Tarkista, ett\xe4 annoit meille siihen luvan. ",screenSharingFirefoxPermissionDeniedTitle:"Hups! N\xe4yt\xf6njakoa ei voitu aloittaa!",screenSharingPermissionDeniedError:"Hups!Jokin meni vikaan n\xe4yt\xf6njakolaajennuksen k\xe4ytt\xf6oikeuksissa. K\xe4ynnist\xe4 uudelleen ja yrit\xe4 sitten uudelleen.",serviceUnavailable:"Palvelu ei k\xe4ytett\xe4viss\xe4",sessTerminated:"Puhelu lopetettu",Share:"Jaa",shareVideoLinkError:"Anna oikea YouTube-linkki.",shareVideoTitle:"Jaa video",shareYourScreen:"Jaa n\xe4ytt\xf6",shareYourScreenDisabled:"N\xe4yt\xf6njako ei ole k\xe4yt\xf6ss\xe4.",shareYourScreenDisabledForGuest:"Vieraat eiv\xe4t voi jakaa n\xe4ytt\xf6\xe4.",startLiveStreaming:"Aloita suoratoisto",startRecording:"Aloita nauhoitus",startRemoteControlErrorMessage:"Et\xe4k\xe4ytt\xf6istunnon aloittamisessa tapahtui virhe!",stopLiveStreaming:"Lopeta suoratoisto",stopRecording:"Lopeta nauhoitus",stopRecordingWarning:"Haluatko varmasti lopettaa nauhoituksen?",stopStreamingWarning:"Haluatko varmasti lopettaa suoratoiston?",streamKey:"Suoratoistokoodi",Submit:"L\xe4het\xe4",thankYou:"Kiitos sovelluksen {{appName}} k\xe4yt\xf6st\xe4!",token:"koodi",tokenAuthFailed:"Valitettavasti et saa osallistua t\xe4h\xe4n puheluun.",tokenAuthFailedTitle:"Todennus ep\xe4onnistui",transcribing:"Puhtaaksikirjoitus",unlockRoom:"",userPassword:"k\xe4ytt\xe4j\xe4n salasana",WaitForHostMsg:"Kokous {{room}} ei ole viel\xe4 alkanut. Jos olet vet\xe4j\xe4, todenna henkil\xf6llisyytesi. Muussa tapauksessa odota vet\xe4j\xe4n saapumista.",WaitForHostMsgWOk:"Kokous {{room}} ei ole viel\xe4 alkanut. Jos olet vet\xe4j\xe4, todenna henkil\xf6llisyytesi OK-painikkeella. Muussa tapauksessa odota vet\xe4j\xe4n saapumista.",WaitingForHost:"Odotetaan vet\xe4j\xe4\xe4...",Yes:"Kyll\xe4",yourEntireScreen:"Koko n\xe4ytt\xf6"},dialOut:{statusMessage:"on nyt tilassa {{status}}"},feedback:{average:"Keskiverto",bad:"Huono",detailsLabel:"Kerro lis\xe4\xe4.",good:"Hyv\xe4",rateExperience:"Arvioi kokouskokemus",veryBad:"Eritt\xe4in huono",veryGood:"Eritt\xe4in hyv\xe4"},incomingCall:{answer:"Vastaus",audioCallTitle:"Saapuva puhelu",decline:"Hylk\xe4\xe4",productLabel:"Jitsi Meetist\xe4",videoCallTitle:"Saapuva videopuhelu"},info:{accessibilityLabel:"N\xe4yt\xe4 tiedot",addPassword:"",cancelPassword:"",conferenceURL:"Linkki:",country:"Maa",dialANumber:"Liity kokoukseen soittamalla yhteen n\xe4ist\xe4 numeroista ja antamalla PIN-koodi.",dialInConferenceID:"PIN:",dialInNotSupported:"Valitettavasti soittoa ei t\xe4ll\xe4 hetkell\xe4 tueta.",dialInNumber:"Liittymissoitto:",dialInSummaryError:"Virhe soittotietojen noudossa. Yrit\xe4 my\xf6hemmin uudelleen.",dialInTollFree:"Maksuton",genericError:"Hupsista, jokin meni vikaan.",inviteLiveStream:"Katso suora kokousl\xe4hetys seuraavasta linkist\xe4: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Olet saanut kokouskutsun.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Suoratoisto:",moreNumbers:"Lis\xe4\xe4 numeroita",noNumbers:"Ei liittymispuhelinnumeroita.",noPassword:"Ei yht\xe4\xe4n",noRoom:"Liitytt\xe4v\xe4\xe4 huonetta ei valittu.",numbers:"Liittymispuhelinnumerot",password:"",title:"Jaa",tooltip:"Jaa kokouksen linkki ja liittymissoittotiedot",label:"Kokoustiedot"},inviteDialog:{alertText:"Osa kutsuista ep\xe4onnistui.",header:"Kutsu",searchCallOnlyPlaceholder:"Anna puhelinnumero",searchPeopleOnlyPlaceholder:"Etsi osanottajia",searchPlaceholder:"Osanottaja tai puhelinnumero",send:"L\xe4het\xe4"},inlineDialogFailure:{msg:"K\xf6mm\xe4hdimme.",retry:"Yrit\xe4 uudelleen",support:"Tuki",supportMsg:"Jos t\xe4m\xe4 jatkuu, ota yhteytt\xe4:"},keyboardShortcuts:{focusLocal:"Keskity omaan videoon",focusRemote:"Keskity jonkun muun videoon",fullScreen:"N\xe4yt\xe4 tai poista koko n\xe4ytt\xf6",keyboardShortcuts:"Pikan\xe4pp\xe4imet",localRecording:"N\xe4yt\xe4 tai piilota paikalliset nauhoitusohjaimet",mute:"S\xe4\xe4d\xe4 mikrofonin mykistyst\xe4",pushToTalk:"Paina ja puhu",raiseHand:"K\xe4den nosto tai lasku",showSpeakerStats:"N\xe4yt\xe4 puhujatilastot",toggleChat:"Avaa tai sulje chatti",toggleFilmstrip:"N\xe4yt\xe4 tai piilota videon pikkukuvat",toggleScreensharing:"Siirry kameran ja n\xe4yt\xf6n jakamisen v\xe4lill\xe4",toggleShortcuts:"N\xe4yt\xe4 tai piilota pikan\xe4pp\xe4imet",videoMute:"K\xe4ynnist\xe4 tai pys\xe4yt\xe4 kamera"},liveStreaming:{busy:"Yrit\xe4mme vapauttaa suoratoistoresursseja. Yrit\xe4 uudelleen muutaman minuutin kuluttua.",busyTitle:"Kaikki suoratoistokanavat ovat juuri nyt varattuja",changeSignIn:"Vaihda tili\xe4.",choose:"Valitse suoratoistol\xe4hetys",chooseCTA:"Valitse suoratoistovaihtoehto. Olet kirjautunut s\xe4hk\xf6postilla {{email}}.",enterStreamKey:"Kirjoita YouTube-suoratoistokoodi t\xe4h\xe4n.",error:"Suoratoisto ep\xe4onnistui. Yrit\xe4 uudelleen.",errorAPI:"YouTube-l\xe4hetykseen yhdist\xe4misess\xe4 tapahtui virhe. Yrit\xe4 kirjautua uudelleen sis\xe4\xe4n.",errorLiveStreamNotEnabled:"Suoratoisto ei ole k\xe4yt\xf6ss\xe4 tilill\xe4 {{email}}. Ota suoratoisto k\xe4ytt\xf6\xf6n tai kirjaudu tiliin, jossa se on k\xe4yt\xf6ss\xe4.",expandedOff:"Suoratoisto on p\xe4\xe4ttynyt",expandedOn:"Kokous n\xe4kyy parhaillaan YouTubessa suoratoistol\xe4hetyksen\xe4.",expandedPending:"Suoratoistol\xe4hetys on alkamassa...",failedToStart:"Suoratoiston aloitus ei onnistunut",getStreamKeyManually:"Suoratoistol\xe4hetysten nouto ep\xe4onnistui. Hanki suoratoistokoodi YouTubesta.",invalidStreamKey:"Suoratoistokoodi voi olla virheellinen.",off:"Suoratoisto p\xe4\xe4ttyi",on:"Suoratoisto",pending:"Suoratoisto alkamassa...",serviceName:"Suoratoistopalvelu",signedInAs:"Sis\xe4\xe4nkirjautunut k\xe4ytt\xe4j\xe4:",signIn:"Kirjaudu Googlella",signInCTA:"Kirjaudu sis\xe4\xe4n tai anna YouTube-suoratoistokoodi.",signOut:"Kirjaudu ulos",start:"Aloita suoratoisto",streamIdHelp:"Mik\xe4 t\xe4m\xe4 on?",unavailableTitle:"Suoratoisto ei k\xe4ytett\xe4viss\xe4"},localRecording:{clientState:{off:"Pois p\xe4\xe4lt\xe4",on:"P\xe4\xe4ll\xe4",unknown:"Tuntematon"},dialogTitle:"Paikalliset nauhoitusohjaimet",duration:"Kesto",durationNA:"\xad\u2013",encoding:"Koodaus",label:"LOR",labelToolTip:"Paikallinen nauhoitus k\xe4yt\xf6ss\xe4",localRecording:"Paikallinen nauhoitus",me:"Min\xe4",messages:{engaged:"Paikallinen nauhoitus k\xe4yt\xf6ss\xe4.",finished:"Nauhoitus {{token}} p\xe4\xe4ttyi. L\xe4het\xe4 nauhoite valvojalle.",finishedModerator:"Nauhoitus {{token}} p\xe4\xe4ttyi. Paikallisen raidan nauhoitus on tallennettu. Pyyd\xe4 muita osanottajia l\xe4hett\xe4m\xe4\xe4n omat nauhoituksensa.",notModerator:"Et ole valvoja. Et voi aloittaa tai lopettaa paikallista nauhoitusta."},moderator:"Valvoja",no:"Ei",participant:"Osanottaja",participantStats:"Osanottajatilastot",sessionToken:"Istuntokoodi",start:"Aloita nauhoitus",stop:"Lopeta nauhoitus",yes:"Kyll\xe4"},lockRoomPassword:"",lockRoomPasswordUppercase:"",me:"min\xe4",notify:{connectedOneMember:"{{name}} liittyi kokoukseen",connectedThreePlusMembers:"{{name}} ja {{count}} muuta liittyiv\xe4t kokoukseen",connectedTwoMembers:"{{first}} ja {{second}} liittyiv\xe4t kokoukseen",disconnected:"ei yhteytt\xe4",focus:"Kokouksen painopiste",focusFail:"{{component}} ei k\xe4ytett\xe4viss\xe4. Yrit\xe4 uudelleen {{ms}} sekunnin kuluttua",grantedTo:"Valvojan oikeudet my\xf6nnetty k\xe4ytt\xe4j\xe4lle {{to}}!",invitedOneMember:"{{name}} on kutsuttu",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Min\xe4",moderator:"Valvojan oikeudet my\xf6nnetty!",muted:"Aloitit keskustelun mykistettyn\xe4.",mutedTitle:"Olet mykistetty!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} pyyt\xe4\xe4 puheenvuoroa.",somebody:"Joku",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Valitettavasti {{appName}} ei taida toimia kovin hyvin t\xe4\xe4ll\xe4. Yrit\xe4mme l\xf6yt\xe4\xe4 parannuskeinoja, mutta sill\xe4 v\xe4lin kannattaa k\xe4ytt\xe4\xe4 jotain t\xe4ysin tuettua selainta.",suboptimalExperienceTitle:"Selainvaroitus",unmute:"",newDeviceCameraTitle:"Uusi kamera havaittu",newDeviceAudioTitle:"Uusi \xe4\xe4nilaite havaittu",newDeviceAction:"K\xe4yt\xe4"},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"tukija:",presenceStatus:{busy:"Varattu",calling:"Soitetaan...",connected:"Yhdistetty",connecting:"Yhdistet\xe4\xe4n...",connecting2:"Yhdistet\xe4\xe4n*...",disconnected:"Ei yhteytt\xe4",expired:"Vanhentunut",ignored:"Sivuutettu",initializingCall:"K\xe4ynnistet\xe4\xe4n puhelua...",invited:"Kutsuttu",rejected:"Hyl\xe4tty",ringing:"Soi..."},profile:{setDisplayNameLabel:"M\xe4\xe4rit\xe4 n\xe4ytt\xf6nimi",setEmailInput:"Anna s\xe4hk\xf6postiosoite",setEmailLabel:"M\xe4\xe4rit\xe4 Gravatar-s\xe4hk\xf6posti",title:"Profiili"},recording:{authDropboxText:"Lataa Dropboxiin",availableSpace:"Tilaa j\xe4ljell\xe4: {{spaceLeft}} Mt (noin {{duration}} minuuttia nauhoitetta)",beta:"BEETA",busy:"Yrit\xe4mme vapauttaa nauhoitusresursseja. Yrit\xe4 uudelleen muutaman minuutin kuluttua.",busyTitle:"Kaikki nauhoittimet ovat juuri nyt varattuja",error:"Nauhoitus ep\xe4onnistui. Yrit\xe4 uudelleen.",expandedOff:"Nauhoitus p\xe4\xe4ttyi",expandedOn:"T\xe4t\xe4 kokousta nauhoitetaan.",expandedPending:"Nauhoitus on alkamassa...",failedToStart:"Nauhoituksen aloitus ep\xe4onnistui",fileSharingdescription:"Jaa nauhoitus kokouksen osanottajille",live:"SUORA L\xc4HETYS",loggedIn:"Kirjautunut k\xe4ytt\xe4j\xe4n\xe4 {{userName}}",off:"Nauhoitus p\xe4\xe4ttyi",on:"Nauhoitetaan",pending:"Kokouksen nauhoitusta valmistellaan...",rec:"REC",serviceDescription:"Nauhoituspalvelu tallentaa nauhoituksen",serviceName:"Nauhoituspalvelu",signIn:"Kirjaudu sis\xe4\xe4n",signOut:"Kirjaudu ulos",unavailable:"Hups! {{serviceName}} ei ole k\xe4ytett\xe4viss\xe4. Yrit\xe4mme ratkaista ongelman. Yrit\xe4 my\xf6hemmin uudelleen.",unavailableTitle:"Nauhoitus ei k\xe4ytett\xe4viss\xe4"},sectionList:{pullToRefresh:"P\xe4ivit\xe4 vet\xe4m\xe4ll\xe4"},settings:{calendar:{about:"{{appName}} tarkistaa tulevat tapahtumat kalenteristasi turvallisesti kalenteri-integrointitoimintonsa avulla.",disconnect:"Katkaise yhteys",microsoftSignIn:"Kirjaudu Microsoftilla",signedIn:"Tarkastellaan k\xe4ytt\xe4j\xe4n {{email}} kalenteritapahtumia. Est\xe4 tarkastelu napsauttamalla Katkaise yhteys -painiketta.",title:"Kalenteri"},devices:"Laitteet",followMe:"Kaikki seuraavat minua",language:"Kieli",loggedIn:"Kirjautunut k\xe4ytt\xe4j\xe4n\xe4 {{name}}",moderator:"Valvoja",more:"Lis\xe4\xe4",name:"Nimi",noDevice:"Ei yht\xe4\xe4n",selectAudioOutput:"\xc4\xe4niulostulo",selectCamera:"Kamera",selectMic:"Mikrofoni",startAudioMuted:"Kaikki aluksi mykistettyin\xe4",startVideoMuted:"Kaikki aluksi piilotettuina",title:"Asetukset"},settingsView:{alertOk:"OK",alertTitle:"Varoitus",alertURLText:"Annettu palvelimen URL on virheellinen",buildInfoSection:"Koontiversion tiedot",conferenceSection:"Kokous",displayName:"N\xe4ytt\xf6nimi",email:"S\xe4hk\xf6posti",header:"Asetukset",profileSection:"Profiili",serverURL:"Palvelimen URL",startWithAudioMuted:"Aloita \xe4\xe4ni mykistettyn\xe4",startWithVideoMuted:"Aloita video mykistettyn\xe4",version:"Versio"},share:{dialInfoText:"",mainText:"Liity kokoukseen seuraavasta linkist\xe4:\n{{roomUrl}}"},speaker:"Puhuja",speakerStats:{hours:"{{count}} t",minutes:"{{count}} min",name:"Nimi",seconds:"{{count}} s",speakerStats:"Puhujatilastot",speakerTime:"Puhujan aika"},startupoverlay:{policyText:" ",title:"{{app}} tarvitsee mikrofoniasi ja kameraasi."},suspendedoverlay:{rejoinKeyTitle:"Liity uudelleen",text:"Yhdist\xe4 uudelleen napsauttamalla Liity uudelleen -painiketta.",title:"Videopuhelu keskeytyi, koska tietokone siirtyi lepotilaan."},toolbar:{accessibilityLabel:{audioOnly:"S\xe4\xe4d\xe4 Vain \xe4\xe4ni -tilaa",audioRoute:"Valitse \xe4\xe4nilaite",callQuality:"",cc:"S\xe4\xe4d\xe4 tekstityst\xe4",chat:"S\xe4\xe4d\xe4 chatti-ikkunaa",document:"S\xe4\xe4d\xe4 jaettua asiakirjaa",feedback:"Anna palautetta",fullScreen:"S\xe4\xe4d\xe4 koko n\xe4ytt\xf6\xe4",hangup:"Poistu puhelusta",invite:"L\xe4het\xe4 kutsuja",kick:"Poista osanottaja kokouksesta",localRecording:"S\xe4\xe4d\xe4 paikallisia nauhoitusohjaimia",lockRoom:"S\xe4\xe4d\xe4 kokouksen salasanaa",moreActions:"S\xe4\xe4d\xe4 Lis\xe4\xe4 toimintoja -valikkoa",moreActionsMenu:"Lis\xe4\xe4 toimintoja -valikko",mute:"S\xe4\xe4d\xe4 \xe4\xe4nen mykistyst\xe4",pip:"S\xe4\xe4d\xe4 Kuva kuvassa (PiP) -tilaa",profile:"Muokkaa profiilia",raiseHand:"S\xe4\xe4d\xe4 k\xe4den nostoa",recording:"S\xe4\xe4d\xe4 nauhoitusta",remoteMute:"Mykist\xe4 osanottaja",Settings:"S\xe4\xe4d\xe4 asetuksia",sharedvideo:"S\xe4\xe4d\xe4 YouTube-videon jakoa",shareRoom:"Kutsu joku",shareYourScreen:"S\xe4\xe4d\xe4 n\xe4yt\xf6n jakoa",shortcuts:"S\xe4\xe4d\xe4 pikan\xe4pp\xe4imi\xe4",show:"",speakerStats:"S\xe4\xe4d\xe4 puhujatilastoja",tileView:"S\xe4\xe4d\xe4 ruudukkon\xe4kym\xe4\xe4",toggleCamera:"S\xe4\xe4d\xe4 kameraa",videomute:"S\xe4\xe4d\xe4 videon mykistyst\xe4",videoblur:""},addPeople:"Lis\xe4\xe4 osanottajia puheluun",audioOnlyOff:"Poista Vain \xe4\xe4ni -tila k\xe4yt\xf6st\xe4",audioOnlyOn:"Ota Vain \xe4\xe4ni -tila k\xe4ytt\xf6\xf6n",audioRoute:"Valitse \xe4\xe4nilaite",authenticate:"Todenna",callQuality:"",chat:"Avaa/sulje chatti",closeChat:"Sulje chatti",documentClose:"Sulje jaettu asiakirja",documentOpen:"Avaa jaettu asiakirja",enterFullScreen:"N\xe4yt\xe4 koko n\xe4yt\xf6ss\xe4",enterTileView:"Siirry ruudukkon\xe4kym\xe4\xe4n",exitFullScreen:"Poisti koko n\xe4yt\xf6st\xe4",exitTileView:"Poistu ruudukkon\xe4kym\xe4st\xe4",feedback:"Anna palautetta",hangup:"Poistu",invite:"L\xe4het\xe4 kutsuja",login:"Kirjaudu sis\xe4\xe4n",logout:"Kirjaudu ulos",lowerYourHand:"Laske k\xe4si",moreActions:"Lis\xe4\xe4 toimintoja",mute:"Mykistys p\xe4\xe4ll\xe4/pois",openChat:"Avaa chatti",pip:"Siirry Kuva kuvassa (PiP) -tilaan",profile:"Muokkaa profiilia",raiseHand:"Nosta/laske k\xe4si",raiseYourHand:"Nosta k\xe4si",Settings:"Asetukset",sharedvideo:"Jaa YouTube-video",shareRoom:"Kutsu joku",shortcuts:"N\xe4yt\xe4 pikan\xe4pp\xe4imet",speakerStats:"Puhujatilastot",startScreenSharing:"Aloita n\xe4yt\xf6n jako",startSubtitles:"K\xe4ynnist\xe4 tekstitys",stopScreenSharing:"Lopeta n\xe4yt\xf6n jako",stopSubtitles:"Lopeta tekstitys",stopSharedVideo:"Pys\xe4yt\xe4 YouTube-video",talkWhileMutedPopup:"Yrit\xe4tk\xf6 puhua? Olet mykistettyn\xe4.",tileViewToggle:"S\xe4\xe4d\xe4 ruudukkon\xe4kym\xe4\xe4",toggleCamera:"S\xe4\xe4d\xe4 kameraa",videomute:"K\xe4ynnist\xe4/pys\xe4yt\xe4 kamera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Tekstitys p\xe4\xe4lle/pois",error:"Puhtaaksikirjoitus ep\xe4onnistui. Yrit\xe4 uudelleen.",expandedLabel:"Puhtaaksikirjoitus on k\xe4yt\xf6ss\xe4",failedToStart:"Puhtaaksikirjoituksen aloitus ep\xe4onnistui",labelToolTip:"Kokousta kirjoitetaan puhtaaksi",off:"Puhtaaksikirjoitus p\xe4\xe4ttyi",pending:"Kokouksen puhtaaksikirjoitusta valmistellaan...",start:"Aloita tekstitys",stop:"Lopeta tekstitys",tr:"TR"},userMedia:{androidGrantPermissions:"Valitse Salli, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",chromeGrantPermissions:"Valitse Salli, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",edgeGrantPermissions:"Valitse Kyll\xe4, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",electronGrantPermissions:"My\xf6nn\xe4 k\xe4ytt\xf6oikeudet laitteesi kameran ja mikrofonin k\xe4ytt\xf6\xf6n.",firefoxGrantPermissions:"Valitse Jaa havaittu laite, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",iexplorerGrantPermissions:"Valitse OK, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",nwjsGrantPermissions:"My\xf6nn\xe4 k\xe4ytt\xf6oikeudet laitteesi kameran ja mikrofonin k\xe4ytt\xf6\xf6n.",operaGrantPermissions:"Valitse Salli, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.","react-nativeGrantPermissions":"Valitse Salli, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia.",safariGrantPermissions:"Valitse OK, kun selain pyyt\xe4\xe4 k\xe4ytt\xf6oikeuksia."},videoSIPGW:{busy:"Yrit\xe4mme vapauttaa resursseja. Yrit\xe4 uudelleen muutaman minuutin kuluttua.",busyTitle:"Huonepalvelu on t\xe4ll\xe4 hetkell\xe4 varattu",errorAlreadyInvited:"{{displayName}} on jo kutsuttu",errorInvite:"Kokousta ei ole viel\xe4 luotu. Yrit\xe4 my\xf6hemmin uudelleen.",errorInviteFailed:"Ratkaisemme ongelmaa. Yrit\xe4 my\xf6hemmin uudelleen.",errorInviteFailedTitle:"Henkil\xf6n {{displayName}} kutsuminen ep\xe4onnistui",errorInviteTitle:"Virhe huoneen kutsumisessa",pending:"{{displayName}} on kutsuttu"},videoStatus:{audioOnly:"\xc4\xc4NI",audioOnlyExpanded:"K\xe4yt\xf6ss\xe4si on Vain \xe4\xe4ni -tila. Se s\xe4\xe4st\xe4\xe4 kaistanleveytt\xe4, mutta et n\xe4e muiden videoita.",callQuality:"",hd:"HD",highDefinition:"Ter\xe4v\xe4piirto",labelTooiltipNoVideo:"Ei videota",labelTooltipAudioOnly:"Vain \xe4\xe4ni -tila k\xe4yt\xf6ss\xe4",ld:"LD",lowDefinition:"Alhainen kuvanlaatu",onlyAudioAvailable:"Vain \xe4\xe4ni -tila on k\xe4ytett\xe4viss\xe4",onlyAudioSupported:"T\xe4ss\xe4 selaimessa tuemme vain \xe4\xe4nt\xe4.",p2pEnabled:"Vertaisverkko k\xe4yt\xf6ss\xe4",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Suositaan ter\xe4v\xe4piirtoa.",sd:"SD",standardDefinition:"Vakiopiirto"},videothumbnail:{domute:"Mykist\xe4",flip:"K\xe4\xe4nn\xe4",kick:"Poista",moderator:"Valvoja",mute:"",muted:"Mykistetty",remoteControl:"Et\xe4k\xe4ytt\xf6",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Liity napauttamalla",roomname:"Anna huoneen nimi"},appDescription:"Anna menn\xe4! Keskustele videochatiss\xe4 koko tiimin kanssa tai kutsu vaikka kaikki tutut. {{app}} on t\xe4ysin salattu, avoimen l\xe4hdekoodin videokokousratkaisu kokop\xe4iv\xe4iseen k\xe4ytt\xf6\xf6n joka p\xe4iv\xe4. Se on maksuton, eik\xe4 vaadi tili\xe4.",audioVideoSwitch:{audio:"\xc4\xe4ni",video:"Video"},calendar:"Kalenteri",connectCalendarButton:"Yhdist\xe4 oma kalenteri",connectCalendarText:"",enterRoomTitle:"Aloita uusi kokous",go:"ALOITA",join:"LIITY",info:"Tiedot",privacy:"Tietosuoja",recentList:"Viimeisimm\xe4t",recentListDelete:"Poista",recentListEmpty:"Viimeisimpien kokousten luettelo on tyhj\xe4. Kun pid\xe4t kokouksia tiimisi kanssa, niist\xe4 viimeisimm\xe4t n\xe4kyv\xe4t t\xe4\xe4ll\xe4.",reducedUIText:"",roomname:"Anna huoneen nimi",roomnameHint:"Kirjoita sen huoneen nimi tai URL-osoite, johon haluat liitty\xe4. Voit my\xf6s nimet\xe4 huoneen itse ja kertoa nimen muille, jotta he voivat l\xf6yt\xe4\xe4 sen.",sendFeedback:"L\xe4het\xe4 palautetta",terms:"Ehdot",title:"Turvallinen, t\xe4ysin varustettu ja maksuton videoneuvottelu"}}},654,[]); -__d(function(a,n,i,e,s,r,o){s.exports={en:"Anglais",af:"Afrikaans",bg:"Bulgare",ca:"Catalan",cs:"Tch\xe8que",de:"Allemand",el:"Grec",enGB:"Anglais (Royaume-Uni) ",eo:"Esp\xe9ranto",es:"Espagnol",esUS:"Espagnol (Am\xe9rique latine)",fi:"Finlandais",fr:"Fran\xe7ais",frCA:"Fran\xe7ais (Canadien)",hr:"Croate",hy:"Arm\xe9nien",it:"Italien",ja:"Japonais",ko:"Cor\xe9en",nl:"N\xe9erlandais",oc:"Occitan",pl:"Polonais",ptBR:"Portugais (Br\xe9sil)",ru:"Russe",sv:"Su\xe9dois",tr:"Turc",vi:"Vietnamien",zhCN:"Chinois (Chine)",zhTW:"Chinois (Taiwan)"}},655,[]); -__d(function(e,r,n,t,i,o,a){i.exports={addPeople:{add:"Inviter",countryNotSupported:"Nous ne supportons pas encore cette destination.",countryReminder:"Appel hors \xc9tats-Unis? Veuillez commencer avec le code du pays!",disabled:"Vous ne pouvez pas inviter quelqu'un.",failedToAdd:"Erreur lors de l'ajout des participants",footerText:"Appels sortants d\xe9sactiv\xe9s",loading:"Rechercher des personnes et des num\xe9ros de t\xe9l\xe9phone",loadingNumber:"Validation du num\xe9ro de t\xe9l\xe9phone",loadingPeople:"Recherche de personnes \xe0 inviter",noResults:"Aucun r\xe9sultat de recherche correspondant",noValidNumbers:"Veuillez entrer un num\xe9ro de t\xe9l\xe9phone",searchNumbers:"Ajouter des num\xe9ros de t\xe9l\xe9phone",searchPeople:"Rechercher une personne",searchPeopleAndNumbers:"Rechercher des personnes ou ajouter leurs num\xe9ros de t\xe9l\xe9phone",telephone:"T\xe9l\xe9phone: {{number}}",title:"Inviter une personne \xe0 cette r\xe9union"},audioDevices:{bluetooth:"Bluetooth",headphones:"\xc9couteurs",phone:"T\xe9l\xe9phone",speaker:"Haut-parleur",none:"Aucune source audio n'est disponible"},audioOnly:{audioOnly:"Bande passante faible"},calendarSync:{addMeetingURL:"Ajouter un lien de conf\xe9rence",confirmAddLink:"Voulez-vous ajouter un lien Jitsi \xe0 cet \xe9v\xe9nement?",error:{appConfiguration:"l'int\xe9gration du calendrier n'est pas correctement configur\xe9e",generic:"Une erreur s'est produite. Veuillez v\xe9rifier les param\xe8tres de votre calendrier ou tenter de l'actualiser.",notSignedIn:"Une erreur s'est produite lors de l'authentification permettant d'afficher les \xe9v\xe9nements du calendrier. Veuillez v\xe9rifier les param\xe8tres de votre calendrier et essayer de vous reconnecter."},join:"Joindre",joinTooltip:"Rejoindre la r\xe9union",nextMeeting:"prochaine r\xe9union",noEvents:"Il n'y a pas d\u2019\xe9v\xe9nement \xe0 venir.",ongoingMeeting:"La r\xe9union en cours",permissionButton:"Afficher les r\xe9glages",permissionMessage:"La permission du calendrier est requise pour afficher vos r\xe9unions dans l'application.",refresh:"Rafra\xeechir le calendrier",today:"Aujourd'hui"},chat:{error:"Erreur : votre message \"{{originalText}}\" n'a pas \xe9t\xe9 envoy\xe9. Raison : {{error}}",messagebox:"Saisissez un message",nickname:{popover:"Choisissez un pseudonyme",title:"Entrez un pseudonyme pour utiliser le chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Connexion \xe0 la r\xe9union..."},connection:{ATTACHED:"Attach\xe9e",AUTHENTICATING:"Authentification en cours",AUTHFAIL:"\xc9chec de l'authentification",CONNECTED:"Connect\xe9",CONNECTING:"Connexion en cours",CONNFAIL:"\xc9chec de la connexion",DISCONNECTED:"D\xe9connect\xe9",DISCONNECTING:"D\xe9connexion en cours",ERROR:"Erreur",RECONNECTING:"Un probl\xe8me r\xe9seau est survenue. Reconnexion en cours..."},connectionindicator:{address:"Adresse :",bandwidth:"Bande passante estim\xe9e :",bitrate:"D\xe9bit\xa0:",bridgeCount:"Nombre de serveurs :",connectedTo:"Connect\xe9 \xe0 :",framerate:"Images par seconde",less:"Cacher le d\xe9tail",localaddress:"Adresse locale :",localaddress_plural:"Adresses locales :",localport:"Port local :",localport_plural:"Ports locaux :",more:"Montrer le d\xe9tail",packetloss:"Perte de paquets :",quality:{good:"Bien",inactive:"Inactif",lost:"Perdu",nonoptimal:"Non-optimale",poor:"Mauvaise"},remoteaddress:"Adresse distante :",remoteaddress_plural:"Adresses distantes :",remoteport:"Port distant:",remoteport_plural:"Ports distants:",resolution:"R\xe9solution\xa0:",status:"Connexion:",transport:"Transport :",transport_plural:"Transports :"},dateUtils:{earlier:"Plus t\xf4t",today:"Aujourd'hui",yesterday:"Hier"},deepLinking:{appNotInstalled:"Vous avez besoin de l'application mobile {{app}} pour participer \xe0 cette r\xe9union avec votre t\xe9l\xe9phone.",description:"Rien ne s'est pass\xe9? Nous avons essay\xe9 de lancer votre r\xe9union dans l'application de bureau {{app}}. Essayez \xe0 nouveau ou lancez-la dans l'application web {{app}}.",descriptionWithoutWeb:"Rien ne s'est pass\xe9? Nous avons essay\xe9 de d\xe9marrer votre r\xe9union dans l'application bureau {{app}}.",downloadApp:"T\xe9l\xe9charger l'application",launchWebButton:"Lancer dans le navigateur",openApp:"Continuer vers l'application",title:"Lancement de votre r\xe9union dans {{app}} en cours...",tryAgainButton:"R\xe9essayez sur le bureau"},defaultLink:"ex. {{url}}",defaultNickname:"ex. Jean Dupont",deviceError:{cameraError:"Impossible d'acc\xe9der \xe0 votre cam\xe9ra",cameraPermission:"Erreur lors de l'obtention de la permission de la cam\xe9ra ",microphoneError:"Impossible d'acc\xe9der \xe0 votre microphone",microphonePermission:"Erreur lors de l'obtention de la permission du microphone"},deviceSelection:{noPermission:"Permission non accord\xe9e",previewUnavailable:"Aper\xe7u non disponible",selectADevice:"S\xe9lectionner un p\xe9riph\xe9rique",testAudio:"Lire un audio de test"},dialog:{accessibilityLabel:{liveStreaming:"Diffusion en direct"},allow:"Autoriser",alreadySharedVideoMsg:"Un autre participant est en train de partager sa vid\xe9o. Cette conf\xe9rence ne permet de partager qu'une seule vid\xe9o \xe0 la fois.",alreadySharedVideoTitle:"Une seule vid\xe9o partag\xe9e est autoris\xe9e \xe0 la fois",applicationWindow:"Fen\xeatre d'application",Back:"Retour",cameraConstraintFailedError:"Votre cam\xe9ra ne satisfait pas certaines des contraintes n\xe9cessaires.",cameraNotFoundError:"La cam\xe9ra n'a pas \xe9t\xe9 trouv\xe9e",cameraNotSendingData:"Nous sommes incapables d'acc\xe9der \xe0 votre cam\xe9ra. Veuillez s\xe9lectionner un autre p\xe9riph\xe9rique dans les param\xe8tres ou rafra\xeechir la page",cameraNotSendingDataTitle:"Impossible d'acc\xe9der \xe0 votre cam\xe9ra",cameraPermissionDeniedError:"Vous n'avez pas autoris\xe9 l'utilisation de votre cam\xe9ra. Vous pouvez toujours participer \xe0 la conf\xe9rence, mais les autres ne vont pas vous voir. Utilisez le bouton de la cam\xe9ra dans la barre d'adresse pour r\xe9soudre ce probl\xe8me.",cameraUnknownError:"Vous ne pouvez pas utiliser la cam\xe9ra pour une raison inconnue.",cameraUnsupportedResolutionError:"Votre appareil ne prend pas en charge la r\xe9solution vid\xe9o requise.",Cancel:"Annuler",close:"Fermer",conferenceDisconnectMsg:"Veuillez v\xe9rifier votre connexion r\xe9seau. Reconnexion dans {{seconds}} sec...",conferenceDisconnectTitle:"Vous avez \xe9t\xe9 d\xe9connect\xe9.",conferenceReloadMsg:"Nous somme en train de r\xe9gler cela. Reconnexion dans {{seconds}} sec...",conferenceReloadTitle:"Malheureusement, un probl\xe8me est survenu",confirm:"Confirmer",confirmNo:"Non",confirmYes:"Oui",connectError:"Oups! Un probl\xe8me est survenu et la connexion \xe0 la conf\xe9rence est impossible.",connectErrorWithMsg:"Oups! Un probl\xe8me est survenu et la connexion \xe0 la conf\xe9rence est impossible: {{msg}}",connecting:"Connexion en cours",contactSupport:"Contacter le support",copy:"Copier",dismiss:"Rejeter",displayNameRequired:"Salut! Quel est votre nom?",done:"Termin\xe9",enterDisplayName:"Merci de saisir votre nom ici",error:"Erreur",externalInstallationMsg:"Vous devez installer notre extension de partage de bureau.",externalInstallationTitle:"Extension requise : ",goToStore:"Aller sur le webstore",gracefulShutdown:"Le service est actuellement en maintenance. R\xe9essayez plus tard.",IamHost:"Je suis l\u2019h\xf4te",incorrectRoomLockPassword:"Mot de passe incorrect",incorrectPassword:"Nom d'utilisateur ou mot de passe incorrect",inlineInstallationMsg:"Vous devez installer notre extension de partage de bureau.",inlineInstallExtension:"Installer maintenant",internalError:"Oups! Quelque chose s'est mal pass\xe9e. L'erreur suivante s'est produite: {{error}}",internalErrorTitle:"Erreur interne",kickMessage:"Vous pouvez contacter {{participantDisplayName}} pour plus de d\xe9tails.",kickParticipantButton:"Expulser",kickParticipantDialog:"\xcates-vous s\xfbr(e) de vouloir expulser ce participant ?",kickParticipantTitle:"Expulser ce participant?",kickTitle:"Oups! vous avez \xe9t\xe9 expuls\xe9(e) par {{participantDisplayName}}",liveStreaming:"Direct",liveStreamingDisabledForGuestTooltip:"Les invit\xe9s ne peuvent d\xe9marrer la diffusion en direct.",liveStreamingDisabledTooltip:"La diffusion en direct est d\xe9sactiv\xe9",lockMessage:"Impossible de verrouiller la conf\xe9rence.",lockRoom:"Ajouter la r\xe9union $t(lockRoomPasswordUppercase)",lockTitle:"\xc9chec du verrouillage",logoutQuestion:"Voulez-vous vraiment vous d\xe9connecter et arr\xeater la conf\xe9rence ?",logoutTitle:"D\xe9connexion",maxUsersLimitReached:"Le nombre maximal de participant est atteint. Le conf\xe9rence est compl\xe8te. Merci de contacter l'organisateur de la r\xe9union ou r\xe9essayer plus tard!",maxUsersLimitReachedTitle:"Le nombre maximal de participants est atteint",micConstraintFailedError:"Votre microphone ne satisfait pas certaines des contraintes n\xe9cessaires.",micNotFoundError:"Le microphone n'a pas \xe9t\xe9 d\xe9tect\xe9.",micNotSendingData:"Acc\xe9dez aux param\xe8tres de votre ordinateur pour r\xe9activer le micro et ajuster son niveau",micNotSendingDataTitle:"Votre micro est d\xe9sactiv\xe9 par les param\xe8tres de votre syst\xe8me",micPermissionDeniedError:"Vous n'avez pas autoris\xe9 l'utilisation de votre microphone. Vous pouvez toujours participer \xe0 la conf\xe9rence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour r\xe9soudre ce probl\xe8me.",micUnknownError:"Vous ne pouvez pas utiliser le microphone pour une raison inconnue.",muteParticipantBody:"Vous ne pourrez plus r\xe9activer leurs micros, mais ils peuvent l'activer par eux-m\xeame \xe0 tout moment.",muteParticipantButton:"Couper le micro",muteParticipantDialog:"\xcates-vous s\xfbr(e) de vouloir couper le micro de ce participant ? Seul le participant pourra ensuite r\xe9activer son micro \xe0 tout moment.",muteParticipantTitle:"Couper le micro de ce participant?",Ok:"Ok",passwordLabel:"$t(lockRoomPasswordUppercase)",passwordNotSupported:"La d\xe9finition d'un $t(lockRoomPassword) de r\xe9union n'est pas prise en charge.",passwordNotSupportedTitle:"$t(lockRoomPasswordUppercase) n'est pas support\xe9",passwordRequired:"$t(lockRoomPasswordUppercase) requis",popupError:"Votre navigateur bloque les fen\xeatres pop-up. Veuillez autoriser les fen\xeatres pop-up dans les param\xe8tres de votre navigateur.",popupErrorTitle:"Pop-up bloqu\xe9e",recording:"Enregistrement",recordingDisabledForGuestTooltip:"Les invit\xe9s ne peuvent enregistrer.",recordingDisabledTooltip:"L'enregistrement est d\xe9sactiv\xe9.",rejoinNow:"Rejoindre maintenant",remoteControlAllowedMessage:"Une erreur s'est produite lors de la demande d\u2019autorisation de prise en main \xe0 distance avec {{user}}!",remoteControlDeniedMessage:"{{user}} a refus\xe9 votre demande de prise en main \xe0 distance!",remoteControlErrorMessage:"Une erreur s'est produite lors de la demande d\u2019autorisation de prise en main \xe0 distance avec {{user}}!",remoteControlRequestMessage:"Voulez-vous autoriser {{user}} \xe0 contr\xf4ler votre bureau?",remoteControlShareScreenWarning:"Si vous appuyez sur \"Autoriser\" vous allez partager votre \xe9cran!",remoteControlStopMessage:"La prise en main \xe0 distance est termin\xe9e!",remoteControlTitle:"Contr\xf4le de bureau \xe0 distance",Remove:"Supprimer",removePassword:"Supprimer $t(lockRoomPassword)",removeSharedVideoMsg:"Voulez-vous vraiment supprimer votre vid\xe9o partag\xe9e ?",removeSharedVideoTitle:"Supprimer la vid\xe9o partag\xe9e",reservationError:"Erreur du syst\xe8me de r\xe9servation",reservationErrorMsg:"Code d'erreur: {{code}}, message: {{msg}}",retry:"R\xe9essayer",screenSharingFailedToInstall:"Oups! Votre extension de partage d'\xe9cran n'a pas pu \xeatre install\xe9e.",screenSharingFailedToInstallTitle:"L'extension de partage d'\xe9cran n'a pas pu \xeatre install\xe9e",screenSharingFirefoxPermissionDeniedError:"Quelque chose s'est mal pass\xe9 pendant que nous essayions de partager votre \xe9cran. S'il vous pla\xeet assurez-vous que vous nous avez donn\xe9 la permission de le faire.",screenSharingFirefoxPermissionDeniedTitle:"Oups! Nous ne pouvions pas d\xe9marrer le partage d'\xe9cran!",screenSharingPermissionDeniedError:"Oups! Une erreur s'est produite avec vos autorisations d'extension de partage d'\xe9cran. Veuillez rafra\xeechir et r\xe9essayer.",serviceUnavailable:"Service indisponible",sessTerminated:"Appel termin\xe9",Share:"Partager",shareVideoLinkError:"Fournissez s'il vous pla\xeet un lien Youtube fonctionnel.",shareVideoTitle:"Partager une vid\xe9o",shareYourScreen:"Partagez votre \xe9cran",shareYourScreenDisabled:"Le partage d\u2019\xe9cran est d\xe9sactiv\xe9.",shareYourScreenDisabledForGuest:"Les invit\xe9s ne peuvent partager l'\xe9cran.",startLiveStreaming:"D\xe9marrer la diffusion en direct",startRecording:"Commencer l'enregistrement",startRemoteControlErrorMessage:"Une erreur est survenue lors de la tentative de d\xe9marrage de la session de contr\xf4le \xe0 distance!",stopLiveStreaming:"Arr\xeater la diffusion en direct",stopRecording:"Arr\xeater l'enregistrement",stopRecordingWarning:"D\xe9sirez-vous vraiment arr\xeater l'enregistrement?",stopStreamingWarning:"D\xe9sirez-vous vraiment arr\xeater le direct?",streamKey:"Cl\xe9 Live stream",Submit:"Soumettre",thankYou:"Merci d'avoir utilis\xe9 {{appName}} !",token:"jeton",tokenAuthFailed:"D\xe9sol\xe9, vous n'\xeates pas autoris\xe9 \xe0 rejoindre cette conversation.",tokenAuthFailedTitle:"\xc9chec de l'authentification",transcribing:"Transcription",unlockRoom:"Supprimer $t(lockRoomPassword) de la r\xe9union",userPassword:"mot de passe utilisateur",WaitForHostMsg:"La conf\xe9rence {{room}} n'a pas encore commenc\xe9. Si vous en \xeates l'h\xf4te, veuillez vous authentifier. Sinon, veuillez attendre son arriv\xe9e.",WaitForHostMsgWOk:"La conf\xe9rence {{room}} n'a pas encore commenc\xe9. Si vous en \xeates l'h\xf4te, veuillez appuyer sur Ok pour vous authentifier. Sinon, veuillez attendre son arriv\xe9e.",WaitingForHost:"En attente de l'h\xf4te ...",Yes:"Oui",yourEntireScreen:"Votre \xe9cran entier"},dialOut:{statusMessage:"est maintenant {{status}}"},feedback:{average:"Moyen",bad:"Mauvais",detailsLabel:"Dites nous en plus \xe0 ce sujet.",good:"Bien",rateExperience:"Veuillez \xe9valuer votre exp\xe9rience.",veryBad:"Tr\xe8s mauvais",veryGood:"Tr\xe8s bon"},incomingCall:{answer:"R\xe9pondre",audioCallTitle:"Appel entrant",decline:"Rejeter",productLabel:"de Jitsi Meet",videoCallTitle:"Appel vid\xe9o entrant"},info:{accessibilityLabel:"Afficher les informations",addPassword:"Ajouter $t(lockRoomPassword)",cancelPassword:"Annuler $t(lockRoomPassword)",conferenceURL:"Lien:",country:"Pays",dialANumber:"Pour rejoindre votre r\xe9union, composez l'un de ces num\xe9ros, puis saisissez le code confidentiel.",dialInConferenceID:"PIN:",dialInNotSupported:"D\xe9sol\xe9, l'acc\xe8s par t\xe9l\xe9phone n'est pas pris en charge pour l'instant.",dialInNumber:"Composer:",dialInSummaryError:"Erreur lors de la r\xe9cup\xe9ration des informations de num\xe9rotation. Veuillez r\xe9essayer plus tard.",dialInTollFree:"Num\xe9ro gratuit",genericError:"Oups, quelque chose a mal tourn\xe9.",inviteLiveStream:"Pour voir la diffusion en direct de cette r\xe9union, cliquez sur ce lien : {{url}}",invitePhone:"Pour rejoindre depuis un t\xe9l\xe9phone, saisissez : {{number}},,{{conferenceID}}#\n",invitePhoneAlternatives:"Vous cherchez un num\xe9ro d'appel diff\xe9rent?\nAfficher les num\xe9ros d'appel de la r\xe9union: {{url}}\n\n\nSi vous appelez \xe9galement via un t\xe9l\xe9phone de salle, vous pouvez vous connecter sans audio: {{silentUrl}}",inviteURLFirstPartGeneral:"Vous \xeates invit\xe9(e) \xe0 participer \xe0 une r\xe9union.",inviteURLFirstPartPersonal:"{{name}} vous invite \xe0 une r\xe9union.\n",inviteURLSecondPart:"\nRejoindre la r\xe9union:\n{{url}}\n",liveStreamURL:"Diffusion en direct :",moreNumbers:"Plus de num\xe9ros ",noNumbers:"Num\xe9ros \xe0 composer non trouv\xe9s",noPassword:"Aucun",noRoom:"Aucune r\xe9union n'a \xe9t\xe9 sp\xe9cifi\xe9e pour l'appel entrant.",numbers:"Num\xe9ros d'appel",password:"$t(lockRoomPasswordUppercase):",title:"Partager",tooltip:"Partager le lien et les informations de connexion pour cette conf\xe9rence",label:"Information de la r\xe9union"},inviteDialog:{alertText:"\xc9chec lors de l'invitation de certains participants.",header:"Inviter",searchCallOnlyPlaceholder:"Saisissez un num\xe9ro de t\xe9l\xe9phone",searchPeopleOnlyPlaceholder:"Rechercher des participants",searchPlaceholder:"Participant ou num\xe9ro de t\xe9l\xe9phone",send:"Envoyer"},inlineDialogFailure:{msg:"Nous avons tr\xe9buch\xe9 un peu.",retry:"R\xe9essayer",support:"Support",supportMsg:"Si cela continue, prenez contact avec"},keyboardShortcuts:{focusLocal:"\xc9pingler ma vid\xe9o",focusRemote:"\xc9pingler la vid\xe9o de quelqu'un d'autre",fullScreen:"Activer / D\xe9sactiver le mode plein \xe9cran",keyboardShortcuts:"Raccourcis clavier",localRecording:"Afficher ou masquer les commandes de l'enregistrement local",mute:"Activer ou d\xe9sactiver le microphone",pushToTalk:"Appuyer pour parler",raiseHand:"Lever ou baisser la main",showSpeakerStats:"Afficher les statistiques de l'interlocuteur",toggleChat:"Ouvrir ou fermer le panneau de conversation",toggleFilmstrip:"Afficher ou masquer les vignettes vid\xe9os",toggleScreensharing:"Basculer entre la cam\xe9ra et le partage d'\xe9cran",toggleShortcuts:"Afficher ou masquer les raccourcis clavier",videoMute:"D\xe9marrer ou arr\xeater votre cam\xe9ra",videoQuality:"Accorder la qualit\xe9 des appels"},liveStreaming:{busy:"Nous travaillons sur la lib\xe9ration des ressources de Streaming. Veuillez r\xe9essayez dans quelques minutes.",busyTitle:"Tous les streamers sont actuellement occup\xe9s",changeSignIn:"Changer de compte.",choose:"Choisir un flux live",chooseCTA:"Choisissez une option de diffusion. Vous \xeates actuellement connect\xe9 comme {{email}}.",enterStreamKey:"Entrez votre cl\xe9 de flux live Youtube ici",error:"Le Streaming a \xe9chou\xe9. Veuillez r\xe9essayer.",errorAPI:"Une erreur s'est produite lors de l'acc\xe8s \xe0 vos diffusions YouTube. Veuillez r\xe9essayer de vous connecter.",errorLiveStreamNotEnabled:"La diffusion en direct n'est pas activ\xe9e pour {{email}}. Merci de l'activer ou de vous connecter avec un compte o\xf9 elle est d\xe9j\xe0 activ\xe9e.",expandedOff:"La diffusion en direct a \xe9t\xe9 arr\xeat\xe9e",expandedOn:"La conf\xe9rence est en cours de diffusion sur YouTube.",expandedPending:"La diffusion en direct a commenc\xe9...",failedToStart:"Le Streaming n'as pas r\xe9ussi \xe0 d\xe9marrer",getStreamKeyManually:"Nous n'avons pu r\xe9cup\xe9rer aucun flux en direct. Essayez d\u2019obtenir votre cl\xe9 de diffusion en direct sur YouTube.",invalidStreamKey:"La cl\xe9 de diffusion en direct n'est peut-\xeatre pas correcte.",off:"Le Streaming a \xe9t\xe9 arr\xeat\xe9",on:"Direct",pending:"Commencer le direct...",serviceName:"Service de diffusion en direct",signedInAs:"Vous \xeates connect\xe9 en tant que :",signIn:"Se connecter avec Google",signInCTA:"Connectez vous ou entrez votre cl\xe9 de flux live provenant de Youtube.",signOut:"Se d\xe9connecter",start:"D\xe9marrer la diffusion en direct",streamIdHelp:"Qu'est-ce que c'est?",unavailableTitle:"Le Streaming est indisponible"},localRecording:{clientState:{off:"Inactif",on:"Actif",unknown:"Inconnu"},dialogTitle:"Commandes de l'enregistrement local",duration:"Dur\xe9e",durationNA:"N/A",encoding:"Encodage",label:"ENR-LOC",labelToolTip:"L'enregistrement local est engag\xe9",localRecording:"Enregistrement local",me:"Moi",messages:{engaged:"Enregistrement local engag\xe9.",finished:"L'enregistrement de la session {{token}} s'est termin\xe9. Merci d'envoyer le fichier au mod\xe9rateur.",finishedModerator:"L'enregistrement de la session {{token}} s'est termin\xe9. La piste a bien \xe9t\xe9 sauvegard\xe9e. Merci de demander aux autres participants de soumettre leurs enregistrements.",notModerator:"Vous n'\xeates pas le mod\xe9rateur. Vous ne pouvez pas d\xe9marrer ou arr\xeater un enregistrement local."},moderator:"Moderateur",no:"Non",participant:"Participant",participantStats:"Statistiques du participant",sessionToken:"Token de la session",start:"D\xe9marrer l'enregistrement",stop:"Arr\xeater l'enregistrement",yes:"Oui"},lockRoomPassword:"mot de passe",lockRoomPasswordUppercase:"Mot de passe",me:"moi",notify:{connectedOneMember:"{{name}} a rejoint la r\xe9union.",connectedThreePlusMembers:"{{name}} et {{count}} autres personnes ont rejoint la r\xe9union.",connectedTwoMembers:"{{first}} et {{second}} ont rejoint la r\xe9union.",disconnected:"d\xe9connect\xe9",focus:"Focus de conf\xe9rence",focusFail:"{{component}} n'est pas disponible - r\xe9essayez dans {{ms}} sec",grantedTo:"Droits mod\xe9rateur accord\xe9s \xe0 {{to}} !",invitedOneMember:"{{displayName}} a \xe9t\xe9 invit\xe9(e)",invitedThreePlusMembers:"{{name}} et {{count}} autres ont \xe9t\xe9 invit\xe9s",invitedTwoMembers:"{{first}} et {{second}} ont \xe9t\xe9 invit\xe9s",kickParticipant:"{{kicked}} a \xe9t\xe9 expuls\xe9 par {{kicker}}",me:"Moi",moderator:"Droits mod\xe9rateur accord\xe9s !",muted:"Vous avez commenc\xe9 la conversation en muet.",mutedTitle:"Vous \xeates en muet !",mutedRemotelyTitle:"Votre micro a \xe9t\xe9 coup\xe9 par {{participantDisplayName}}!",mutedRemotelyDescription:"Vous pouvez toujours activer votre micro pour prendre la parole. D\xe9sactivez votre micro quand vous terminez pour \xe9viter les bruits parasites.",passwordRemovedRemotely:"$t(lockRoomPasswordUppercase) a \xe9t\xe9 supprim\xe9 par un autre participant",passwordSetRemotely:"$t(lockRoomPasswordUppercase) d\xe9fini par un autre participant",raisedHand:"{{name}} aimerait prendre la parole.",somebody:"Quelqu'un",startSilentTitle:"Vous avez rejoint sans sortie audio!",startSilentDescription:"Rejoignez la r\xe9union de nouveau pour activer l'audio",suboptimalBrowserWarning:"Nous craignons que votre exp\xe9rience de r\xe9union en ligne ne soit bonne ici. Nous cherchons des moyens d\u2019am\xe9liorer cela, mais d\u2019ici-l\xe0, essayez d\u2019utiliser l\u2019un des navigateurs support\xe9s.",suboptimalExperienceTitle:"Avertissement du navigateur",unmute:"R\xe9tablir le son",newDeviceCameraTitle:"Nouvelle cam\xe9ra d\xe9tect\xe9e",newDeviceAudioTitle:"Nouveau p\xe9riph\xe9rique audio d\xe9tect\xe9",newDeviceAction:"Utiliser"},passwordSetRemotely:"d\xe9fini par un autre participant",passwordDigitsOnly:"Jusqu'\xe0 {{number}} chiffres",poweredby:"Produit par",presenceStatus:{busy:"Occup\xe9",calling:"Appel...",connected:"Connect\xe9",connecting:"Connexion en cours...",connecting2:"Connexion en cours*...",disconnected:"D\xe9connect\xe9",expired:"Expir\xe9",ignored:"Ignor\xe9",initializingCall:"Lancement de l'appel...",invited:"Invit\xe9(e)",rejected:"Rejet\xe9",ringing:"Appel en cours..."},profile:{setDisplayNameLabel:"Choisissez un pseudo",setEmailInput:"Entrez une adresse e-mail",setEmailLabel:"D\xe9finir votre courriel Gravatar",title:"Profil"},raisedHand:"Aimerait prendre la parole",recording:{authDropboxText:"T\xe9l\xe9chargement vers Dropbox",availableSpace:"Espace disponible: {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)",beta:"BETA",busy:"Nous sommes en train de lib\xe9rer les ressources d'enregistrement. R\xe9essayez dans quelques minutes.",busyTitle:"Tous les enregistreurs sont actuellement occup\xe9s",error:"\xc9chec de l'enregistrement. Veuillez r\xe9essayer.",expandedOff:"L'enregistrement a \xe9t\xe9 arr\xeat\xe9",expandedOn:"Cette conf\xe9rence est actuellement en cours d'enregistrement.",expandedPending:"D\xe9marrage de l'enregistrement...",failedToStart:"L'enregistrement n'as pas r\xe9ussi \xe0 d\xe9marrer",fileSharingdescription:"Partager l'enregistrement avec les participants de la r\xe9union",live:"DIRECT",loggedIn:"Connect\xe9 en tant que {{userName}}",off:"Enregistrement arr\xeat\xe9",on:"Enregistrement",pending:"Pr\xe9paration de l'enregistrement de la r\xe9union...",rec:"REC",serviceDescription:"Votre enregistrement sera enregistr\xe9 par le service d\xe9di\xe9.",serviceName:"Service d'enregistrement",signIn:"Se connecter",signOut:"Se d\xe9connecter",unavailable:"Oups! Le {{serviceName}} est actuellement indisponible. Nous travaillons sur la r\xe9solution du probl\xe8me. Veuillez r\xe9essayer plus tard.",unavailableTitle:"Enregistrement indisponible"},sectionList:{pullToRefresh:"Tirer pour recharger"},settings:{calendar:{about:"L'int\xe9gration de {{appName}} avec votre calendrier permet d\u2019acc\xe9der de mani\xe8re s\xe9curis\xe9e aux \xe9v\xe9nement \xe0 venir.",disconnect:"Se d\xe9connecter",microsoftSignIn:"Se connecter avec Microsoft",signedIn:"Acc\xe8s aux \xe9v\xe9nements du calendrier {{email}}. Cliquez sur le bouton se d\xe9connecter ci-dessous pour arr\xeater l'acc\xe8s aux \xe9v\xe9nements du calendrier.",title:"Calendrier"},devices:"P\xe9riph\xe9riques",followMe:"Tout le monde me suit",language:"Langue",loggedIn:"Connect\xe9 en tant que {{name}}",moderator:"Moderateur",more:"Plus",name:"Nom",noDevice:"Aucun",selectAudioOutput:"Sortie audio",selectCamera:"Cam\xe9ra",selectMic:"Microphone",startAudioMuted:"Tout le monde commence en muet",startVideoMuted:"Tout le monde commence sans vid\xe9o",title:"Param\xe8tres"},settingsView:{alertOk:"D'accord",alertTitle:"Avertissement",alertURLText:"L'URL du serveur est invalide",buildInfoSection:"Informations de build",conferenceSection:"Conf\xe9rence",displayName:"Pseudo",email:"Email",header:"Param\xe8tres",profileSection:"Profil",serverURL:"URL du serveur",startWithAudioMuted:"Commencez avec la vid\xe9o en sourdine",startWithVideoMuted:"Commencez avec la vid\xe9o en sourdine",version:"Version"},share:{dialInfoText:"\n\n=====\n\nVoulez-vous appeler depuis votre t\xe9l\xe9phone?\n\n{{defaultDialInNumber}}Cliquez sur ce lien pour afficher les num\xe9ros d'appels pour cette r\xe9union\n{{dialInfoPageUrl}}",mainText:"Cliquez sur le lien suivant pour rejoindre une conf\xe9rence :\n{{roomUrl}}"},speaker:"Haut-parleur",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Nom",seconds:"{{count}}s",speakerStats:"Statistiques de l'interlocuteur",speakerTime:"Temps de l'interlocuteur"},startupoverlay:{policyText:" ",title:" {{app}} a besoin d'acc\xe9der \xe0 votre microphone et votre cam\xe9ra."},suspendedoverlay:{rejoinKeyTitle:"Rejoindre",text:"Cliquez sur le bouton Rejoindre pour se reconnecter.",title:"Votre visioconf\xe9rence s'est interrompue parce que votre ordinateur s'est mis en veille."},toolbar:{accessibilityLabel:{audioOnly:"Activer/d\xe9sactiver le mode voix uniquement",audioRoute:"S\xe9lectionner la source audio",callQuality:"Ajuster la qualit\xe9 vid\xe9o",cc:"Activer/d\xe9sactiver les sous-titres",chat:"Afficher/masquer la discussion instantan\xe9e",document:"Activer/d\xe9sactiver le document partag\xe9",feedback:"Laisser des commentaires",fullScreen:"Activer/d\xe9sactiver le plein \xe9cran",hangup:"Quitter la conversation",invite:"Inviter des participants",kick:"Expulser le participant",localRecording:"Activer/d\xe9sactiver les contr\xf4les d'enregistrement local",lockRoom:"Activer/D\xe9sactiver le mot de passe de la r\xe9union",moreActions:"Activer/d\xe9sactiver le menu d'actions suppl\xe9mentaires",moreActionsMenu:"Menu d'actions suppl\xe9mentaires",mute:"Activer/d\xe9sactiver l'audio",pip:"Activer/d\xe9sactiver le mode Picture in Picture",profile:"\xc9diter votre profil",raiseHand:"Lever/baisser la main",recording:"Activer/d\xe9sactiver l'enregistrement",remoteMute:"D\xe9sactiver le micro du participant",Settings:"Afficher/masquer le menu des param\xe8tres",sharedvideo:"D\xe9marrer/arr\xeater le partage de vid\xe9o Youtube",shareRoom:"Inviter quelqu'un",shareYourScreen:"Activer/d\xe9sactiver le partage d\u2019\xe9cran",shortcuts:"Afficher/masquer les raccourcis",show:"Afficher en premier plan",speakerStats:"Afficher/cacher les statistiques de parole",tileView:"Activer/d\xe9sactiver la vue mosa\xefque",toggleCamera:"Activer/d\xe9sactiver la cam\xe9ra",videomute:"Activer/d\xe9sactiver la vid\xe9o",videoblur:"Activer/d\xe9sactiver le flou de la vid\xe9o"},addPeople:"Ajouter des personnes \xe0 votre appel",audioOnlyOff:"D\xe9sactiver le mode bande passante r\xe9duite",audioOnlyOn:"Activer le mode bande passante r\xe9duite",audioRoute:"S\xe9lectionner la source audio",authenticate:"Authentifiez-vous",callQuality:"Ajuster la qualit\xe9 vid\xe9o",chat:"Ouvrir / Fermer le chat",closeChat:"Fermer le chat",documentClose:"Fermer le document partag\xe9",documentOpen:"Ouvrir le document partag\xe9",enterFullScreen:"Afficher en plein \xe9cran",enterTileView:"Acc\xe9der au mode mosa\xefque",exitFullScreen:"Quitter le mode plein \xe9cran",exitTileView:"Quitter le mode mosa\xefque",feedback:"Laisser des commentaires",hangup:"Quitter",invite:"Inviter des participants",login:"Connexion",logout:"D\xe9connexion",lowerYourHand:"Baisser la main",moreActions:"Plus d'actions",mute:"Muet / Actif",openChat:"Ouvrir le chat",pip:"Entrer en mode Picture-in-Picture",profile:"\xc9diter votre profil",raiseHand:"Lever / Baisser la main",raiseYourHand:"Lever la main",Settings:"Param\xe8tres",sharedvideo:"Partager une vid\xe9o YouTube",shareRoom:"Inviter quelqu'un",shortcuts:"Afficher les raccourcis",speakerStats:"Statistiques de l'interlocuteur",startScreenSharing:"D\xe9marrer le partage d'\xe9cran",startSubtitles:"Activer les sous-titres",stopScreenSharing:"Arr\xeater le partage d'\xe9cran",stopSubtitles:"D\xe9sactiver les sous-titres",stopSharedVideo:"Arr\xeater la vid\xe9o YouTube",talkWhileMutedPopup:"Vous voulez parler? Vous \xeates en muet.",tileViewToggle:"Activer/d\xe9sactiver la vue mosa\xefque",toggleCamera:"Activer/d\xe9sactiver la cam\xe9ra",videomute:"D\xe9marrer / Arr\xeater la cam\xe9ra",startvideoblur:"Flouter mon arri\xe8re plan",stopvideoblur:"D\xe9sactiver le flou d'arri\xe8re-plan"},transcribing:{ccButtonTooltip:"Activer/D\xe9sactiver les sous-titres",error:"\xc9chec de la transcription. Veuillez r\xe9essayer.",expandedLabel:"La transcription est actuellement activ\xe9e",failedToStart:"\xc9chec de d\xe9marrage de la transcription",labelToolTip:"La transcription de la r\xe9union est en cours",off:"La transcription d\xe9sactiv\xe9e",pending:"Pr\xe9paration de la transcription de la r\xe9union...",start:"Afficher/masquer les sous-titres",stop:"D\xe9sactiver le sous-titrage",tr:"TR"},userMedia:{androidGrantPermissions:"S\xe9lectionnez Autoriser lorsque votre navigateur demande des autorisations.",chromeGrantPermissions:"S\xe9lectionnez Autoriser lorsque votre navigateur demande des autorisations.",edgeGrantPermissions:"S\xe9lectionnez Oui quand le navigateur demande les permissions.",electronGrantPermissions:"Merci d'autoriser le partage de votre camera et microphone",firefoxGrantPermissions:"S\xe9lectionnez Partager le p\xe9riph\xe9rique s\xe9lectionn\xe9 lorsque votre navigateur demande des autorisations.",iexplorerGrantPermissions:"S\xe9lectionnez OK quand le navigateur demande les permissions.",nwjsGrantPermissions:"Merci d'autoriser le partage de votre camera et microphone",operaGrantPermissions:"S\xe9lectionnez Autoriser lorsque votre navigateur demande des autorisations.","react-nativeGrantPermissions":"S\xe9lectionnez Autoriser lorsque votre navigateur demande des autorisations.",safariGrantPermissions:"S\xe9lectionnez OK quand le navigateur demande les permissions."},videoSIPGW:{busy:"Nous travaillons sur la lib\xe9ration des ressources. Veuillez r\xe9essayez dans quelques minutes.",busyTitle:"Le service du Salon est actuellement occup\xe9",errorAlreadyInvited:"{{displayName}} est d\xe9j\xe0 invit\xe9(e)",errorInvite:"La conf\xe9rence n'est pas encore \xe9tablie. Veuillez r\xe9essayer plus tard.",errorInviteFailed:"Nous travaillons sur la r\xe9solution du probl\xe8me. Veuillez r\xe9essayer plus tard.",errorInviteFailedTitle:"l'invitation de {{displayName}} a \xe9chou\xe9",errorInviteTitle:"Erreur lors de l'invitation",pending:"{{displayName}} a \xe9t\xe9 invit\xe9(e)"},videoStatus:{audioOnly:"VOIX",audioOnlyExpanded:"Vous \xeates en mode bande passante r\xe9duite. Dans ce mode, vous ne recevrez que le partage audio et le partage d\u2019\xe9cran.",callQuality:"Qualit\xe9 vid\xe9o",hd:"HD",hdTooltip:"Regardez la vid\xe9o en haute d\xe9finition",highDefinition:"Haute d\xe9finition",labelTooiltipNoVideo:"Aucune vid\xe9o",labelTooltipAudioOnly:"Mode bande passante r\xe9duite activ\xe9",ld:"BD",ldTooltip:"Regardez la vid\xe9o en basse d\xe9finition",lowDefinition:"Basse d\xe9finition",onlyAudioAvailable:"Seul l'audio est disponible",onlyAudioSupported:"Nous ne supportons que l'audio sur ce navigateur.",p2pEnabled:"Peer to Peer activ\xe9",p2pVideoQualityDescription:"En mode peer to peer, la qualit\xe9 vid\xe9o re\xe7ue ne peut \xeatre bascul\xe9e qu'entre haute et audio uniquement. Les autres param\xe8tres ne seront pas pris en compte jusqu'\xe0 ce que vous quittiez le mode peer to peer.",recHighDefinitionOnly:"Va pr\xe9f\xe9rer la haute d\xe9finition",sd:"MD",sdTooltip:"Regardez la vid\xe9o en d\xe9finition standard",standardDefinition:"Moyenne D\xe9finition"},videothumbnail:{domute:"Couper le micro",flip:"Balancer",kick:"Exclure",moderator:"Moderateur",mute:"Un participant a coup\xe9 son micro",muted:"Muet",remoteControl:"Contr\xf4le \xe0 distance",show:"Afficher en premier plan",videomute:"Le participant a arr\xeat\xe9 la cam\xe9ra"},welcomepage:{accessibilityLabel:{join:"Touchez pour rejoindre",roomname:"Saisissez un nom de salle"},appDescription:"Allez-y, chat vid\xe9o avec toute l'\xe9quipe. En fait, invitez tout le monde que vous connaissez. {{app}} est une solution de visioconf\xe9rence enti\xe8rement crypt\xe9e et 100% open source que vous pouvez utiliser toute la journ\xe9e, tous les jours, gratuitement\u2014 aucun compte requis.",audioVideoSwitch:{audio:"Voix",video:"Vid\xe9o"},calendar:"Calendrier",connectCalendarButton:"Connecter votre calendrier",connectCalendarText:"Connectez-vous \xe0 votre calendrier pour afficher toutes les r\xe9unions {{app}}. Ajoutez \xe9galement les r\xe9unions de {{provider}} \xe0 votre calendrier et d\xe9marrez-les d'un simple clic.",enterRoomTitle:"D\xe9marrer une nouvelle r\xe9union",go:"Cr\xe9er",join:"REJOINDRE",info:"Infos",privacy:"Confidentialit\xe9",recentList:"R\xe9cent",recentListDelete:"Supprimer",recentListEmpty:"Votre liste r\xe9cente est actuellement vide. Discuter avec votre \xe9quipe et vous trouverez toutes vos r\xe9unions r\xe9centes ici.",reducedUIText:"Bienvenue sur {{app}}!",roomname:"Saisissez un nom de salle",roomnameHint:"Entrez le nom ou l'URL de la salle que vous souhaitez rejoindre. Vous pouvez faire un nom, laissez les gens que vous rencontrerez le savoir afin qu'ils entrent le m\xeame nom.",sendFeedback:"Envoyer votre avis",terms:"Termes",title:"Vid\xe9oconf\xe9rence S\xe9curis\xe9e, enti\xe8rement en vedette et gratuite"}}},656,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"",af:"",az:"",bg:"",cs:"",de:"",el:"",eo:"",es:"",fr:"",hy:"",it:"",ja:"",ko:"",nb:"",oc:"",pl:"",ptBR:"",ru:"",sk:"",sl:"",sv:"",tr:"",vi:"",zhCN:""}},657,[]); -__d(function(e,r,n,t,i,o,a){i.exports={addPeople:{add:"",countryNotSupported:"Nous ne prenons pas encore cette destination en charge.",countryReminder:"Vous appelez en dehors des \xc9.-U.? Veuillez vous assurer de commencer par le code de pays!",disabled:"Vous ne pouvez pas inviter d'autres personnes.",failedToAdd:"L'ajout de membres a \xe9chou\xe9",footerText:"Les appels sont d\xe9sactiv\xe9s.",loading:"Rechercher des personnes et des num\xe9ros de t\xe9l\xe9phone",loadingNumber:"Validation du num\xe9ro de t\xe9l\xe9phone",loadingPeople:"Rechercher des personnes \xe0 inviter",noResults:"Aucun r\xe9sultat de recherche correspondant",noValidNumbers:"Veuillez entrer un num\xe9ro de t\xe9l\xe9phone",searchNumbers:"Ajouter des num\xe9ros de t\xe9l\xe9phone",searchPeople:"Rechercher des personnes",searchPeopleAndNumbers:"Rechercher des personnes ou ajouter des num\xe9ros de t\xe9l\xe9phone",telephone:"T\xe9l\xe9phone : {{number}}",title:"Inviter des personnes \xe0 cette r\xe9union"},audioDevices:{bluetooth:"Bluetooth",headphones:"\xc9couteurs",phone:"T\xe9l\xe9phone",speaker:""},audioOnly:{audioOnly:"Audio seulement"},calendarSync:{addMeetingURL:"Ajouter un lien de r\xe9union",confirmAddLink:"Voulez-vous ajouter un lien Jitsi \xe0 cet \xe9v\xe9nement?",error:{appConfiguration:"L'int\xe9gration de l'agenda n'est pas correctement configur\xe9e.",generic:"Une erreur s'est produite. Veuillez v\xe9rifier vos param\xe8tres d'agenda ou essayer de rafra\xeechir l'agenda.",notSignedIn:"Une erreur s'est produite lors de l'authentification des \xe9v\xe9nements d'agenda. Veuillez v\xe9rifier vos param\xe8tres d'agenda ou essayer de vous reconnecter."},join:"Rejoindre",joinTooltip:"Rejoindre la r\xe9union",nextMeeting:"prochaine r\xe9union",noEvents:"Il n'y a aucun \xe9v\xe9nement planifi\xe9 \xe0 venir.",ongoingMeeting:"r\xe9union en cours",permissionButton:"Ouvrir les param\xe8tres",permissionMessage:"L'autorisation de l'Agenda est n\xe9cessaire pour consulter vos r\xe9unions dans l'application.",refresh:"Rafra\xeechir l'agenda",today:""},chat:{error:"Erreur : votre message \"{{originalText}}\" n'a pas \xe9t\xe9 envoy\xe9. Raison : {{error}}",messagebox:"Tapez un message",nickname:{popover:"Choisissez un nom d'affichage",title:"Entrer un nom d'affichage pour utiliser le clavardage"},title:"Clavardage"},connectingOverlay:{joiningRoom:"Connexion \xe0 la r\xe9union en cours..."},connection:{ATTACHED:"Joint",AUTHENTICATING:"Authentification",AUTHFAIL:"",CONNECTED:"",CONNECTING:"",CONNFAIL:"\xc9chec de la connexion",DISCONNECTED:"",DISCONNECTING:"D\xe9connexion en cours",ERROR:"",RECONNECTING:"Un probl\xe8me de r\xe9seau est survenu. Reconnexion en cours..."},connectionindicator:{address:"Adresse:",bandwidth:"Bande passante estim\xe9e :",bitrate:"D\xe9bit binaire :",bridgeCount:"Nombre de serveur : ",connectedTo:"Connect\xe9 \xe0 :",framerate:"Fr\xe9quence d'images :",less:"Afficher moins",localaddress_plural:"Adresse locale :",localaddress:"Adresses locales :",localport_plural:"Port local :",localport:"Ports locaux :",more:"Afficher plus",packetloss:"Perte de paquet :",quality:{good:"",inactive:"Inactive",lost:"Perdue",nonoptimal:"Non-optimale",poor:"Faible"},remoteaddress_plural:"Adresse distante :",remoteaddress:"Adresses distantes :",remoteport_plural:"Port distant :",remoteport:"Ports distants :",resolution:"R\xe9solution :",status:"Connexion :",transport_plural:"Tranport :",transport:"Transports :",turn:" (tour)"},dateUtils:{earlier:"Plus t\xf4t",today:"Aujourd'hui",yesterday:"Hier"},deepLinking:{appNotInstalled:"L'application mobile {{app}} est n\xe9cessaire pour rejoindre cette r\xe9union sur votre t\xe9l\xe9phone.",description:"Il ne s'est rien pass\xe9? Nous avons essay\xe9s de d\xe9marrer votre r\xe9union dans l'application de bureau {{app}}. Veuillez r\xe9essayer ou d\xe9marrer la r\xe9union dans l'application Web {{app}}.",descriptionWithoutWeb:"",downloadApp:"T\xe9l\xe9charger l'application",launchWebButton:"D\xe9marrer dans l'application Web",openApp:"Continuer vers l'application",title:"D\xe9marrage de votre r\xe9union dans {{app}} en cours...",tryAgainButton:"Veuillez r\xe9essayer sur votre ordinateur"},defaultLink:"p. ex. {{url}}",deviceError:{cameraError:"\xc9chec de l'acc\xe8s \xe0 votre cam\xe9ra",cameraPermission:"Erreur lors de l'obtention de l'autorisation de la cam\xe9ra",microphoneError:"\xc9chec de l'acc\xe8s \xe0 votre micro",microphonePermission:"Erreur lors de l'obtention de l'autorisation du micro"},deviceSelection:{noPermission:"L'autorisation n'a pas \xe9t\xe9 accord\xe9e",previewUnavailable:"Pr\xe9visualisation non disponible",selectADevice:"S\xe9lectionner un dispositif",testAudio:"Jouer un son de test"},dialog:{accessibilityLabel:{liveStreaming:"Diffusion en direct"},allow:"Autoriser",alreadySharedVideoMsg:"Un autre membre partage d\xe9j\xe0 une vid\xe9o. Cette conf\xe9rence permet le partage d'une seule vid\xe9o \xe0 la fois.",alreadySharedVideoTitle:"Seulement une vid\xe9o \xe0 la fois peut \xeatre partag\xe9e",applicationWindow:"Fen\xeatre d'application",Back:"Retour",cameraConstraintFailedError:"Votre cam\xe9ra ne r\xe9pond pas \xe0 certaines exigences.",cameraNotFoundError:"Impossible de trouver la cam\xe9ra.",cameraNotSendingData:"Il est impossible d'acc\xe9der \xe0 la cam\xe9ra. Veuillez v\xe9rifier si une autre application utilise actuellement ce dispositif, s\xe9lectionner un autre dispositif \xe0 partir du menu des param\xe8tres ou essayer de recharger l'application.",cameraNotSendingDataTitle:"Impossible d'acc\xe9der \xe0 la cam\xe9ra",cameraPermissionDeniedError:"Vous n'avez pas re\xe7u l'autorisation d'utiliser votre cam\xe9ra. Vous pouvez toujours rejoindre la conf\xe9rence, mais les autres membres ne pourront pas vous voir. Utilisez le bouton de cam\xe9ra dans la barre d'adresse pour corriger cela.",cameraUnknownError:"Impossible d'utiliser la cam\xe9ra pour une raison inconnue.",cameraUnsupportedResolutionError:"Votre cam\xe9ra ne prend pas en charge la r\xe9solution vid\xe9o n\xe9cessaire.",Cancel:"Annuler",close:"Fermer",conferenceDisconnectMsg:"Vous devriez v\xe9rifier votre connexion au r\xe9seau. Reconnexion dans {{seconds}} sec...",conferenceDisconnectTitle:"Vous avez \xe9t\xe9 d\xe9connect\xe9.",conferenceReloadMsg:"Nous tentons de r\xe9soudre le probl\xe8me. Reconnexion dans {{seconds}} sec...",conferenceReloadTitle:"Malheureusement, une erreur s'est produite.",confirm:"Confirmer",confirmNo:"",confirmYes:"",connectError:"Oups! Une erreur s'est produite. La connexion \xe0 la conf\xe9rence a \xe9chou\xe9e.",connectErrorWithMsg:"Oups! Une erreur s'est produite. La connexion \xe0 la conf\xe9rence a \xe9chou\xe9 : {{msg}}",connecting:"Connexion en cours",contactSupport:"Communiquez avec le service de soutien",copy:"Copier",dismiss:"",displayNameRequired:"Un nom d'affichage est requis",done:"Termin\xe9",enterDisplayName:"Veuillez saisir votre nom d'affichage",error:"Erreur",externalInstallationMsg:"",externalInstallationTitle:"Extension requise",goToStore:"Rendez-vous sur notre boutique en ligne",gracefulShutdown:"Notre service est actuellement hors service pour l'entretien. Veuillez r\xe9essayer plus tard.",IamHost:"Je suis l'h\xf4te",incorrectRoomLockPassword:"",incorrectPassword:"Nom d'utilisateur ou mot de passe incorrect",inlineInstallationMsg:"Vous devez installer notre extension de partage de bureau.",inlineInstallExtension:"Installer maintenant",internalError:"Oups! Une erreur s'est produite. L'erreur suivante est survenue : {{error}}",internalErrorTitle:"Erreur interne.",kickMessage:"A\xefe! Vous avez \xe9t\xe9 expuls\xe9 de la r\xe9union!",kickParticipantButton:"Expulser",kickParticipantDialog:"\xcates-vous certain de vouloir expulser ce participant?",kickParticipantTitle:"Expulser ce membre?",kickTitle:"Expuls\xe9 de la r\xe9union",liveStreaming:"",liveStreamingDisabledForGuestTooltip:"Les invit\xe9s ne peuvent pas d\xe9marrer la diffusion en direct.",liveStreamingDisabledTooltip:"D\xe9marrage de la diffusion en direct d\xe9sactiv\xe9.",lockMessage:"\xc9chec du verrouillage de la conf\xe9rence.",lockRoom:"Ajouter un mot de passe \xe0 la r\xe9union",lockTitle:"\xc9chec du verrouillage",logoutQuestion:"\xcates-vous certain de vouloir vous d\xe9connecter et arr\xeater la conf\xe9rence?",logoutTitle:"",maxUsersLimitReached:"La limite du nombre maximum de membres a \xe9t\xe9 atteinte. La conf\xe9rence est pleine. Veuillez communiquer avec l'h\xf4te de la r\xe9union ou r\xe9essayer plus tard.",maxUsersLimitReachedTitle:"Limite du nombre de membres maximum atteinte",micConstraintFailedError:"Votre micro ne r\xe9pond pas \xe0 certaines exigences",micNotFoundError:"Impossible de trouver le micro.",micNotSendingData:"Impossible d'acc\xe9der \xe0 votre micro. Veuillez s\xe9lectionner un autre dispositif \xe0 partir du menu des param\xe8tres ou essayer de recharger l'application.",micNotSendingDataTitle:"Impossible d'acc\xe9der \xe0 votre micro",micPermissionDeniedError:"Vous n'avez pas accord\xe9 l'autorisation d'utilisation de votre micro. Vous pouvez toujours rejoindre la conf\xe9rence, mais les autres membres ne pourront pas vous entendre. Utilisez le bouton de cam\xe9ra dans la barre d'adresse pour rem\xe9dier \xe0 cela.",micUnknownError:"Impossible d'utiliser le micro pour une raison inconnue.",muteParticipantBody:"Vous ne pourrez pas r\xe9activer leur micro, mais ils peuvent le r\xe9activer eux-m\xeames \xe0 tout moment.",muteParticipantButton:"",muteParticipantDialog:"\xcates-vous certain de vouloir d\xe9sactiver le micro de ce participant? Vous ne pourrez pas le r\xe9activer, mais il peut le r\xe9activer lui-m\xeame \xe0 tout moment.",muteParticipantTitle:"D\xe9sactiver le micro de ce membre?",Ok:"OK",passwordLabel:"Mot de passe",passwordNotSupported:"La mise en place d'un mot de passe de r\xe9union n'est pas prise en charge.",passwordNotSupportedTitle:"Mot de passe non pris en charge",passwordRequired:"Mot de passe requis",popupError:"Votre navigateur bloque les fen\xeatres surgissantes provenant de ce site. Veuillez activer les fen\xeatres surgissantes dans les param\xe8tres de s\xe9curit\xe9 de votre navigateur et r\xe9essayer.",popupErrorTitle:"Fen\xeatre surgissante bloqu\xe9e",recording:"",recordingDisabledForGuestTooltip:"Les invit\xe9s ne peuvent pas d\xe9marrer l'enregistrement.",recordingDisabledTooltip:"D\xe9marrage de l'enregistrement d\xe9sactiv\xe9.",rejoinNow:"Rejoindre maintenant",remoteControlAllowedMessage:"{{user}} a accept\xe9 votre demande de contr\xf4le \xe0 distance!",remoteControlDeniedMessage:"{{user}} a refus\xe9 votre demande de contr\xf4le \xe0 distance!",remoteControlErrorMessage:"Une erreur s'est produite lors de la demande d'autorisation de contr\xf4le \xe0 distance de {{user}}!",remoteControlRequestMessage:"Voulez-vous permettre \xe0 {{user}} de contr\xf4ler votre bureau \xe0 distance?",remoteControlShareScreenWarning:"Notez que si vous appuyez sur \xab Permettre \xbb, vous partagerez votre \xe9cran!",remoteControlStopMessage:"La s\xe9ance de contr\xf4le \xe0 distance est termin\xe9e!",remoteControlTitle:"Contr\xf4le du bureau \xe0 distance",Remove:"Supprimer",removePassword:"Supprimer un mot de passe",removeSharedVideoMsg:"\xcates-vous certain de vouloir supprimer votre vid\xe9o partag\xe9e?",removeSharedVideoTitle:"Supprimer la vid\xe9o partag\xe9e",reservationError:"Erreur du syst\xe8me de r\xe9servation",reservationErrorMsg:"Code d'erreur : {{code}}, message : {{msg}}",retry:"R\xe9essayer",screenSharingFailedToInstall:"Oups! L'installation de votre extension de partage d'\xe9cran a \xe9chou\xe9e.",screenSharingFailedToInstallTitle:"L'installation de l'extension de partage d'\xe9cran a \xe9chou\xe9e",screenSharingFirefoxPermissionDeniedError:"Une erreur s'est produite lors de la tentative de partage d'\xe9cran. Veuillez vous assurer d'avoir donn\xe9 votre autorisation. ",screenSharingFirefoxPermissionDeniedTitle:"Oups! Il est impossible de d\xe9marrer le partage d'\xe9cran!",screenSharingPermissionDeniedError:"Oups! Une erreur s'est produite avec les autorisations de l'extension de partage d'\xe9cran. Veuillez recharger et r\xe9essayer.",serviceUnavailable:"Service non disponible",sessTerminated:"Appel termin\xe9",Share:"",shareVideoLinkError:"Veuillez fournir un lien YouTube correct.",shareVideoTitle:"Partager une vid\xe9o",shareYourScreen:"Partager votre \xe9cran",shareYourScreenDisabled:"Le partage d'\xe9cran est d\xe9sactiv\xe9.",shareYourScreenDisabledForGuest:"Les invit\xe9s ne peuvent pas partager leur \xe9cran.",startLiveStreaming:"D\xe9marrer la diffusion en direct",startRecording:"Commencer l'enregistrement",startRemoteControlErrorMessage:"Une erreur s'est produite lors de la tentative de d\xe9marrage de la s\xe9ance de contr\xf4le \xe0 distance!",stopLiveStreaming:"Arr\xeater la diffusion en direct",stopRecording:"Arr\xeater l'enregistrement",stopRecordingWarning:"\xcates-vous certain de vouloir arr\xeater l'enregistrement?",stopStreamingWarning:"\xcates-vous certain de vouloir arr\xeater la diffusion en direct?",streamKey:"Cl\xe9 de diffusion en direct",Submit:"Envoyer",thankYou:"Merci d'utiliser {{appName}}!",token:"jeton",tokenAuthFailed:"D\xe9sol\xe9, vous n'avez pas la permission de rejoindre cet appel.",tokenAuthFailedTitle:"\xc9chec de l'authentification",transcribing:"Transcription en cours",unlockRoom:"Supprimer le mot de passe de la r\xe9union",userPassword:"mot de passe d'utilisateur",WaitForHostMsg:"La conf\xe9rence {{room}} n'a pas encore d\xe9marr\xe9. Si vous \xeates l'h\xf4te, veuillez vous authentifier. Sinon, veuillez attendre que l'h\xf4te arrive.",WaitForHostMsgWOk:"La conf\xe9rence {{room}} n'a pas encore d\xe9marr\xe9. Si vous \xeates l'h\xf4te, veuillez appuyer sur OK pour vous authentifier. Sinon, veuillez attendre que l'h\xf4te arrive.",WaitingForHost:"En attente de l'h\xf4te...",Yes:"",yourEntireScreen:"Votre \xe9cran entier"},dialOut:{statusMessage:"est maintenant {{status}}"},feedback:{average:"Moyenne",bad:"Mauvaise",detailsLabel:"Dites-nous en plus.",good:"Bonne",rateExperience:"\xc9valuez votre exp\xe9rience de cette conf\xe9rence",veryBad:"Tr\xe8s mauvaise",veryGood:"Tr\xe8s bonne"},incomingCall:{answer:"R\xe9ponse",audioCallTitle:"Appel entrant",decline:"Rejeter",productLabel:"de Jitsi Meet",videoCallTitle:"Appel vid\xe9o entrant"},info:{accessibilityLabel:"Afficher l'information",addPassword:"Ajouter un mot de passe",cancelPassword:"Annuler le mot de passe",conferenceURL:"Liens",country:"Pays",dialANumber:"Pour rejoindre votre r\xe9union, composez un de ces num\xe9ros et entrez le NIP.",dialInConferenceID:"NIP :",dialInNotSupported:"D\xe9sol\xe9, les appels internes ne sont pas pris en charge pour le moment.",dialInNumber:"Appel interne :",dialInSummaryError:"Erreur lors de la r\xe9cup\xe9ration des informations d'appel interne. Veuillez r\xe9essayer plus tard.",dialInTollFree:"Sans frais",genericError:"Oups, une erreur s'est produite.",inviteLiveStream:"Pour voir la diffusion en directe de cette r\xe9union, cliquez sur ce lien : {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Vous avez \xe9t\xe9 invit\xe9 \xe0 rejoindre une r\xe9union.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Diffusion en direct :",moreNumbers:"Plus de num\xe9ros",noNumbers:"Aucun num\xe9ro d'appel interne.",noPassword:"",noRoom:"Vous n'avez pas pr\xe9cis\xe9 de salle pour l'appel interne.",numbers:"Num\xe9ros d'appel interne",password:"Mot de passe\xa0:",title:"Oui",tooltip:"Lien de partage et informations d'appel interne pour cette r\xe9union",label:"Informations de r\xe9union"},inviteDialog:{alertText:"L'invitation de certains participants a \xe9chou\xe9.",header:"Inviter",searchCallOnlyPlaceholder:"Entrer le num\xe9ro de t\xe9l\xe9phone",searchPeopleOnlyPlaceholder:"Rechercher des participants",searchPlaceholder:"Participant ou num\xe9ro de t\xe9l\xe9phone",send:"Envoyer..."},inlineDialogFailure:{msg:"Nous avons rencontr\xe9 un obstacle.",retry:"% abandonn\xe9s",support:"Soutien",supportMsg:"Si cela se produit \xe0 nouveau, veuillez communiquer avec"},keyboardShortcuts:{focusLocal:"Focaliser sur votre vid\xe9o",focusRemote:"Focaliser sur la vid\xe9o d'une autre personne",fullScreen:"Afficher ou quitter le mode plein \xe9cran",keyboardShortcuts:"Raccourcis clavier",localRecording:"Afficher ou masquer les commandes d'enregistrement local",mute:"Activer ou d\xe9sactiver votre micro",pushToTalk:"Messagerie vocale instantan\xe9e",raiseHand:"Lever ou abaisser votre main",showSpeakerStats:"Afficher les statistiques d'intervenant",toggleChat:"Ouvrir ou fermer le clavardage",toggleFilmstrip:"Afficher ou masquer les ic\xf4nes vid\xe9os",toggleScreensharing:"Basculer entre la cam\xe9ra et le partage d'\xe9cran",toggleShortcuts:"Afficher ou masquer les raccourcis clavier",videoMute:"D\xe9marrer ou arr\xeater votre cam\xe9ra"},liveStreaming:{busy:"Lib\xe9ration de ressources de diffusion en cours. Veuillez r\xe9essayer dans quelques minutes.",busyTitle:"Tous les diffuseurs sont actuellement occup\xe9s",changeSignIn:"Changer de compte.",choose:"S\xe9lectionner une diffusion en direct",chooseCTA:"S\xe9lectionner une option de diffusion en direct. Vous \xeates actuellement connect\xe9 en tant que {{email}}.",enterStreamKey:"Saisissez votre cl\xe9 de diffusion en direct YouTube ici.",error:"\xc9chec de la diffusion en direct. Veuillez r\xe9essayer.",errorAPI:"Une erreur s'est produite lors de l'acc\xe8s \xe0 vos diffusions YouTube.Veuillez r\xe9essayer de vous connecter.",errorLiveStreamNotEnabled:"La diffusion en direct n'est pas activ\xe9e pour {{email}}. Veuillez activer la diffusion en direct ou vous connecter \xe0 un compte pour lequel la diffusion en direct est activ\xe9e.",expandedOff:"La diffusion en direct a \xe9t\xe9 arr\xeat\xe9e",expandedOn:"La r\xe9union est actuellement diffus\xe9e sur YouTube.",expandedPending:"Le d\xe9marrage de la diffusion en direct est en cours...",failedToStart:"Le d\xe9marrage de la diffusion en direct a \xe9chou\xe9",getStreamKeyManually:"La r\xe9cup\xe9ration de diffusions en direct a \xe9chou\xe9. Essayez d'obtenir une cl\xe9 de diffusion en direct sur YouTube.",invalidStreamKey:"La cl\xe9 de diffusion en direct peut \xeatre erron\xe9e.",off:"La diffusion en direct s'est arr\xeat\xe9e",on:"Diffusion en direct",pending:"D\xe9marrage de la diffusion en direct...",serviceName:"Service de diffusion en direct",signedInAs:"Vous \xeates actuellement connect\xe9 en tant que :",signIn:"Se connecter avec Google",signInCTA:"Connectez-vous ou entrez votre cl\xe9 de diffusion en direct de YouTube",signOut:"",start:"D\xe9marrer une diffusion en direct",streamIdHelp:"Qu'est-ce que c'est?",unavailableTitle:"Diffusion en direct non disponible"},localRecording:{clientState:{off:"D\xe9sactiv\xe9",on:"Pr\xeat",unknown:"Inconnu"},dialogTitle:"Commandes d'enregistrement local",duration:"Dur\xe9e",durationNA:"S. O.",encoding:"Encodage",label:"LOR",labelToolTip:"L'enregistrement local est en cours",localRecording:"Enregistrement local",me:"",messages:{engaged:"Enregistrement local activ\xe9.",finished:"Enregistrement de la s\xe9ance {{token}} termin\xe9. Veuillez envoyer le fichier d'enregistrement au mod\xe9rateur.",finishedModerator:"Enregistrement de la s\xe9ance {{token}} termin\xe9. L'enregistrement de la piste locale a \xe9t\xe9 enregistr\xe9e. Veuillez demander aux autres participants de soumettre leurs enregistrements.",notModerator:"Vous n'\xeates pas le mod\xe9rateur. Vous ne pouvez pas d\xe9marrer ou arr\xeater l'enregistrement local."},moderator:"",no:"Non",participant:"Participant",participantStats:"Statistiques de participants",sessionToken:"Jeton de s\xe9ance",start:"Commencer l'enregistrement",stop:"Arr\xeater l'enregistrement",yes:"Oui"},lockRoomPassword:"",lockRoomPasswordUppercase:"",me:"moi",notify:{connectedOneMember:"{{name}} a rejoint la r\xe9union",connectedThreePlusMembers:"{{name}} et {{count}} autres ont rejoint la r\xe9union",connectedTwoMembers:"{{first}} et {{second}} ont rejoint la r\xe9union",disconnected:"d\xe9connect\xe9",focus:"Sujet de la conf\xe9rence",focusFail:"{{component}} non disponible; r\xe9essayez dans {{ms}} sec",grantedTo:"Droits de mod\xe9rateur accord\xe9s \xe0 {{to}}!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Moi",moderator:"Droits de mod\xe9rateur accord\xe9s!",muted:"Vous avez joint la conversation en sourdine.",mutedTitle:"Vous \xeates en sourdine!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} voudrait parler.",somebody:"Quelqu'un",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Euh... nous sommes d\xe9sol\xe9s que vous exp\xe9rience avec {{appName}} ne se d\xe9roule pas comme pr\xe9vu. Nous cherchons \xe0 am\xe9liorer cela, mais en attendant, veuillez essayer d'utiliser un des navigateurs pris en charge.",suboptimalExperienceTitle:"Avertissement de navigateur",unmute:"",newDeviceCameraTitle:"Nouvelle cam\xe9ra d\xe9tect\xe9e",newDeviceAudioTitle:"Nouveau dispositif audio d\xe9tect\xe9",newDeviceAction:"Utiliser"},passwordSetRemotely:"r\xe9gl\xe9 par un autre membre",passwordDigitsOnly:"Jusqu'\xe0 {{number}} chiffres",poweredby:"optimis\xe9 par",presenceStatus:{busy:"Occup\xe9",calling:"Appel en cours...",connected:"Connect\xe9",connecting:"Connexion en cours...",connecting2:"Connexion en cours*...",disconnected:"D\xe9connect\xe9",expired:"Expir\xe9",ignored:"Ignor\xe9",initializingCall:"Initialisation de l'appel...",invited:"Invit\xe9",rejected:"Refus\xe9",ringing:"Sonnerie"},profile:{setDisplayNameLabel:"D\xe9finir votre nom d'affichage",setEmailInput:"Entrer votre adresse courriel",setEmailLabel:"D\xe9finir votre courriel gravatar",title:""},recording:{authDropboxText:"T\xe9l\xe9verser \xe0 Dropbox",availableSpace:"Espace disponible : {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)",beta:"BETA",busy:"Lib\xe9ration de ressources pour l'enregistrement. Veuillez r\xe9essayer dans quelques minutes.",busyTitle:"Tous les enregistreurs sont actuellement occup\xe9s",error:"L'enregistrement a \xe9chou\xe9. Veuillez r\xe9essayer.",expandedOff:"L'enregistrement a \xe9t\xe9 arr\xeat\xe9.",expandedOn:"La r\xe9union est actuellement enregistr\xe9e.",expandedPending:"D\xe9marrage de l'enregistrement en cours...",failedToStart:"\xc9chec du d\xe9marrage de l'enregistrement",fileSharingdescription:"Partager l'enregistrement avec les participants de la r\xe9union",live:"EN DIRECT",loggedIn:"Connect\xe9 en tant que {{userName}}",off:"L'enregistrement est arr\xeat\xe9",on:"Enregistrement",pending:"Enregistrement de la r\xe9union en pr\xe9paration...",rec:"REC",serviceDescription:"Votre enregistrement sera sauvegard\xe9 par le service d'enregistrement",serviceName:"Service d'enregistrement",signIn:"Se connecter",signOut:"Se d\xe9connecter",unavailable:"Oups! Le {{serviceName}} n'est pas disponible pour le moment. Nous nous effor\xe7ons de r\xe9gler le probl\xe8me. Veuillez r\xe9essayer plus tard.",unavailableTitle:"Enregistrement non disponible"},sectionList:{pullToRefresh:"Tirer pour rafra\xeechir"},settings:{calendar:{about:"L'int\xe9gration de l'agenda de {{appName}} est utilis\xe9e pour acc\xe9der en toute s\xe9curit\xe9 \xe0 votre agenda pour qu'il puisse lire les \xe9v\xe9nements \xe0 venir.",disconnect:"D\xe9connexion",microsoftSignIn:"Se connecter avec Microsoft",signedIn:"Acc\xe8s aux \xe9v\xe9nements de votre agenda en cours pour {{email}}. Cliquez sur le bouton de d\xe9connexion ci-dessous pour terminer l'acc\xe8s aux \xe9v\xe9nements d'agenda.",title:""},devices:"Dispositifs",followMe:"Tous les participants me suivent",language:"Language",loggedIn:"Connect\xe9 en tant que {{name}}",moderator:"",more:"Plus",name:"",noDevice:"Aucun",selectAudioOutput:"Sortie audio",selectCamera:"Cam\xe9ra",selectMic:"Micro",startAudioMuted:"Tous les participants d\xe9butent en sourdine",startVideoMuted:"Tous les participants d\xe9butent masqu\xe9s",title:""},settingsView:{alertOk:"OK",alertTitle:"Alerte",alertURLText:"L'URL de serveur saisi n'est pas valide",buildInfoSection:"Information de version",conferenceSection:"Conf\xe9rence",displayName:"Nom d'affichage",email:"Courriel",header:"",profileSection:"Profil",serverURL:"URL du serveur",startWithAudioMuted:"D\xe9marrer avec l'audio en sourdine",startWithVideoMuted:"D\xe9marrer avec la vid\xe9o en sourdine",version:"Version"},share:{dialInfoText:"",mainText:""},speaker:"Intervenant",speakerStats:{hours:"{{count}} h",minutes:"{{count}} min",name:"Nom",seconds:"{{count}} s",speakerStats:"Statistiques d'intervenant",speakerTime:"Temps d'intervention"},startupoverlay:{policyText:" ",title:"{{app}} doit utiliser votre micro et votre cam\xe9ra."},suspendedoverlay:{rejoinKeyTitle:"Rejoindre \xe0 nouveau",text:"Appuyez sur le bouton Rejoindre pour vous reconnecter.",title:"Votre appel vid\xe9o a \xe9t\xe9 interrompu parce que cet ordinateur est tomb\xe9 en veille."},toolbar:{accessibilityLabel:{audioOnly:"Basculement du mode audio uniquement",audioRoute:"",callQuality:"",cc:"Basculement des sous-titres",chat:"Basculement de la fen\xeatre de clavardage",document:"Basculement du document partag\xe9",feedback:"",fullScreen:"Basculement de l'affichage plein \xe9cran",hangup:"Quitter l'appel",invite:"",kick:"Expulser le participant",localRecording:"Basculement des commandes d'enregistrement local",lockRoom:"Basculement du mot de passe de la r\xe9union",moreActions:"Basculement du menu d'actions suppl\xe9mentaires",moreActionsMenu:"Menu d'actions suppl\xe9mentaires",mute:"Basculement de la sourdine",pip:"Basculement du mode image dans l'image",profile:"",raiseHand:"Basculement de la main lev\xe9e",recording:"Basculement de l'enregistrement",remoteMute:"Mettre le participant en sourdine",Settings:"Basculement des param\xe8tres",sharedvideo:"Basculement du partage de vid\xe9o YouTube",shareRoom:"",shareYourScreen:"Basculement du partage d'\xe9cran",shortcuts:"Basculement des raccourcis",show:"",speakerStats:"Basculement des statistiques d'intervenant",tileView:"",toggleCamera:"",videomute:"Basculement de la sourdine vid\xe9o",videoblur:""},addPeople:"Ajouter des personnes \xe0 votre appel",audioOnlyOff:"D\xe9sactiver le mode audio uniquement",audioOnlyOn:"Activer le mode audio uniquement",audioRoute:"S\xe9lectionner le dispositif audio",authenticate:"Authentification",callQuality:"Gestion de la qualit\xe9 d'appel",chat:"Ouvrir / Fermer le clavardage",closeChat:"Fermer le clavardage",documentClose:"Fermer le document partag\xe9",documentOpen:"Ouvrir le document partag\xe9",enterFullScreen:"Afficher le mode plein \xe9cran",enterTileView:"Passer \xe0 l'affichage mosa\xefque",exitFullScreen:"Quitter le mode plein \xe9cran",exitTileView:"Quitter l'affichage mosa\xefque",feedback:"Laisser un commentaire",hangup:"Quitter",invite:"Inviter des personnes",login:"Connexion",logout:"D\xe9connexion",lowerYourHand:"Abaisser votre main",moreActions:"Plus d'actions",mute:"Activer / R\xe9activer le son",openChat:"Ouvrir le clavardage",pip:"Passer en mode image dans l'image",profile:"Modifier votre profil",raiseHand:"Lever / Abaisser votre main",raiseYourHand:"Lever votre main",Settings:"Param\xe8tres",sharedvideo:"Partager une vid\xe9o YouTube",shareRoom:"Inviter quelqu'un",shortcuts:"Voir les raccourcis",speakerStats:"Statistiques d'intervenant",startScreenSharing:"D\xe9marrer le partage d'\xe9cran",startSubtitles:"Activer les sous-titres",stopScreenSharing:"Arr\xeater le partage d'\xe9cran",stopSubtitles:"D\xe9sactiver les sous-titres",stopSharedVideo:"Arr\xeater la vid\xe9o YouTube",talkWhileMutedPopup:"Vous essayez de parler? Vous \xeates en sourdine.",tileViewToggle:"Basculement de l'affichage mosa\xefque",toggleCamera:"Basculement de la cam\xe9ra",videomute:"D\xe9marrer / Arr\xeater la cam\xe9ra",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Activer / D\xe9sactiver les sous-titres",error:"\xc9chec de la transcription. Veuillez r\xe9essayer.",expandedLabel:"La transcription est actuellement activ\xe9e",failedToStart:"\xc9chec du d\xe9marrage de la transcription",labelToolTip:"La r\xe9union est transcrite",off:"La transcription est arr\xeat\xe9e",pending:"Pr\xe9paration de la transcription de la r\xe9union en cours...",start:"Activer l'affichage des sous-titres",stop:"D\xe9sactiver l'affichage des sous-titres",tr:"PI"},userMedia:{androidGrantPermissions:"",chromeGrantPermissions:"",edgeGrantPermissions:"S\xe9lectionner Oui lorsque votre navigateur demande l'autorisation.",electronGrantPermissions:"",firefoxGrantPermissions:"S\xe9lectionner Partager le dispositif s\xe9lectionn\xe9 lorsque votre navigateur demande l'autorisation.",iexplorerGrantPermissions:"",nwjsGrantPermissions:"Veuillez accorder l'autorisation d'utiliser votre cam\xe9ra et votre micro",operaGrantPermissions:"","react-nativeGrantPermissions":"S\xe9lectionner Autoriser lorsque votre navigateur vous demande l'autorisation.",safariGrantPermissions:"S\xe9lectionner OK lorsque votre navigateur demande l'autorisation."},videoSIPGW:{busy:"Lib\xe9ration des ressources en cours. Veuillez r\xe9essayer dans quelques minutes.",busyTitle:"Le service de Salle est actuellement occup\xe9.",errorAlreadyInvited:"{{displayName}} a d\xe9j\xe0 \xe9t\xe9 invit\xe9",errorInvite:"La conf\xe9rence n'est pas encore configur\xe9e. Veuillez r\xe9essayer plus tard.",errorInviteFailed:"Nous nous effor\xe7ons de r\xe9gler ce probl\xe8me. Veuillez r\xe9essayer plus tard.",errorInviteFailedTitle:"L'invitation de {{displayName}} a \xe9chou\xe9",errorInviteTitle:"Erreur lors de l'invitation de la salle",pending:"{{displayName}} a \xe9t\xe9 invit\xe9"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Vous \xeates en mode audio uniquement. Ce mode \xe9conomise de la bande passante, mais bloque les vid\xe9os des autres.",callQuality:"Qualit\xe9 d'appel",hd:"HD",highDefinition:"Haute d\xe9finition",labelTooiltipNoVideo:"Aucune vid\xe9o",labelTooltipAudioOnly:"Mode audio uniquement activ\xe9",ld:"LD",lowDefinition:"Basse d\xe9finition",onlyAudioAvailable:"Seulement l'audio est disponible",onlyAudioSupported:"Ce navigateur prend seulement l'audio en charge.",p2pEnabled:"Pair \xe0 pair activ\xe9",p2pVideoQualityDescription:"En mode pair \xe0 pair, il est possible de basculer entre la haute qualit\xe9 d'appel entrant et l'audio seulement. Certains param\xe8tres ne seront pas activ\xe9s tant que vous ne quittez le mode pair \xe0 pair.",recHighDefinitionOnly:"La haute d\xe9finition est pr\xe9f\xe9rable.",sd:"SD",standardDefinition:"D\xe9finition standard"},videothumbnail:{domute:"Discr\xe9tion",flip:"Actionner",kick:"Expulser",moderator:"Mod\xe9rateur",mute:"Le membre est en sourdine",muted:"Discr\xe9tion",remoteControl:"Contr\xf4le \xe0 distance",show:"",videomute:"Le membre a arr\xeat\xe9 la cam\xe9ra"},welcomepage:{accessibilityLabel:{join:"Toucher pour rejoindre",roomname:""},appDescription:"Profitez de la conversation vid\xe9o avec toute votre \xe9quipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100\xa0% libre de conf\xe9rence vid\xe9o enti\xe8rement crypt\xe9e que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",audioVideoSwitch:{audio:"T\xe9l\xe9phone",video:"Vid\xe9o"},calendar:"Calendrier",connectCalendarButton:"Connecter votre agenda",connectCalendarText:"",enterRoomTitle:"D\xe9marrer une nouvelle r\xe9union",go:"Commencer",join:"Rejoindre",info:"Ret. arr.",privacy:"Confidentialit\xe9",recentList:"R\xe9cent",recentListDelete:"Supprimer",recentListEmpty:"Votre liste r\xe9cente est actuellement vide. Clavardez avec votre \xe9quipe et vous y trouverez toutes vos r\xe9unions r\xe9centes.",reducedUIText:"",roomname:"Entrer le nom de la salle",roomnameHint:"Entrez le nom ou l'URL de la salle que vous voulez rejoindre. Vous pouvez inventer un nom, mais assurez-vous de le partager avec les participants de la r\xe9union pour qu'ils utilisent le m\xeame nom.",sendFeedback:"Envoyer un commentaire",terms:"Termes",title:"Conf\xe9rence vid\xe9o s\xe9curis\xe9e, pleinement fonctionnelle et enti\xe8rement gratuite"}}},658,[]); -__d(function(i,k,s,a,e,n,r){e.exports={en:"Engleski",af:"Afrikanski",az:"A\u017eerbejd\u017eanski",bg:"Bugarski",cs:"\u010ce\u0161ki",de:"Njema\u010dki",el:"Gr\u010dki",eo:"Esperanto",es:"\u0160panjolski",fr:"Francuski",hy:"Armenski",it:"Talijanski",ja:"Japanski",ko:"Korejski",nb:"Norve\u0161ki Bokmal",oc:"Okcitanski",pl:"Poljski",ptBR:"Portugalski (Brazil)",ru:"Ruski",sk:"Slova\u010dki",sl:"Slovenski",sv:"\u0160vedski",tr:"Turski",vi:"Vijetnamski",zhCN:"Kineski (Kina)"}},659,[]); -__d(function(e,i,a,o,n,r,t){n.exports={addPeople:{add:"Pozivnica",countryNotSupported:"Ova destinacija jo\u0161 nije podr\u017eana.",countryReminder:"Zovete izvan zemlje? Molim provjerite da li ste unijeli predbroj dr\u017eave!",disabled:"Ne mo\u017eete slati pozivnice.",failedToAdd:"",footerText:"Upu\u0107ivanje poziva je zabranjeno.",loading:"Pretra\u017eujem ljude i brojeve telefona",loadingNumber:"Provjera telefonskog broja",loadingPeople:"Tra\u017eenje osoba koje \u017eelite pozvati",noResults:"Nema podudarnih rezultata pretra\u017eivanja",noValidNumbers:"Unesite telefonski broj",searchNumbers:"Dodajte telefonske brojeve",searchPeople:"Potra\u017eite osobe",searchPeopleAndNumbers:"Potra\u017eite osobe ili dodajte njihove telefonske brojeve",telephone:"Telefon: {{number}}",title:"Pozovite ljude na ovaj sastanak"},audioDevices:{bluetooth:"Bluetooth",headphones:"Slu\u0161alice",phone:"Telefon",speaker:"Zvu\u010dnik"},audioOnly:{audioOnly:"Samo zvuk"},calendarSync:{addMeetingURL:"Dodajte vezu sastanka",confirmAddLink:"\u017delite li dodati Jitsi vezu u ovaj doga\u0111aj?",error:{appConfiguration:"Integracija kalendara nije ispravno konfigurirana.",generic:"Do\u0161lo je do pogre\u0161ke. Provjerite postavke kalendara ili poku\u0161ajte osvje\u017eiti kalendar.",notSignedIn:"Do\u0161lo je do pogre\u0161ke prilikom provjere autenti\u010dnosti za prikaz doga\u0111aja u kalendaru. Provjerite postavke kalendara i poku\u0161ajte se ponovno prijaviti."},join:"Pridru\u017eite",joinTooltip:"Pridru\u017eite se sastanku",nextMeeting:"slijede\u0107i sastanak",noEvents:"Nema zakazanih doga\u0111aja.",ongoingMeeting:"sastanak u tijeku",permissionButton:"Otvori postavke",permissionMessage:"Dozvola za kalendar je obavezna kako biste vidjeli sastanke u aplikaciji.",refresh:"Osvje\u017ei kalendar",today:"Danas"},chat:{error:"Gre\u0161ka: poruka \"{{originalText}}\" nije poslana. Razlog: {{error}}",messagebox:"",nickname:{popover:"Odaberite nadimak",title:"Unesite nadimak za \u010davrljanje"},title:"\u010cavrljanje"},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"Spojen",AUTHENTICATING:"Autentikacija",AUTHFAIL:"Autentikacija nije uspjela",CONNECTED:"Povezan",CONNECTING:"Povezivanje",CONNFAIL:"Povezivanje nije uspjelo",DISCONNECTED:"Nije povezano",DISCONNECTING:"Prekid povezivanja",ERROR:"Gre\u0161ka",RECONNECTING:"Dogodio se problem s mre\u017eom. Ponovno povezivanje..."},connectionindicator:{address:"Adresa:",bandwidth:"Procijenjena propusnost:",bitrate:"Brzina prijenosa:",bridgeCount:"Broj poslu\u017eitelja:",connectedTo:"Spojen na:",framerate:"Sli\u010dice po sekundi:",less:"Prika\u017ei manje",localaddress_0:"Lokalna adresa:",localaddress_1:"Lokalne adrese:",localaddress_2:"",localport_0:"Lokalni priklju\u010dak:",localport_1:"Lokalni priklju\u010dci:",localport_2:"",more:"Prika\u017ei vi\u0161e",packetloss:"Gubitak paketa:",quality:{good:"Dobro",inactive:"Neaktivno",lost:"Izgubljeno",nonoptimal:"Nije optimalno",poor:"Slaba"},remoteaddress_0:"Udaljena adresa:",remoteaddress_1:"Udaljene adrese:",remoteaddress_2:"",remoteport_0:"Udaljeni priklju\u010dak:",remoteport_1:"Udaljeni priklju\u010dci:",remoteport_2:"",resolution:"Rezolucija:",status:"Povezivanje",transport_0:"",transport_1:"",transport_2:"",turn:""},dateUtils:{earlier:"Ranije",today:"Danas",yesterday:"Ju\u010der"},deepLinking:{appNotInstalled:"Da biste se pridru\u017eili ovom sastanku na telefonu, potrebna vam je mobilna aplikacija {{app}}.",description:"Ni\u0161ta se nije dogodilo? Poku\u0161ali smo pokrenuti va\u0161 sastanak u aplikaciji {{app}} na radnoj povr\u0161ini. Poku\u0161ajte ponovno ili ga pokrenite u web-aplikaciji {{app}}.",descriptionWithoutWeb:"",downloadApp:"Preuzmite aplikaciju",launchWebButton:"Pokreni na webu",openApp:"Nastavite do aplikacije",title:"Pokretanje sastanka u {{app}}...",tryAgainButton:"Poku\u0161ajte ponovo na radnoj povr\u0161ini"},defaultLink:"npr. {{url}}",deviceError:{cameraError:"Pristup va\u0161oj kameri nije uspio",cameraPermission:"Gre\u0161ka kod dohvata dozvole za kameru",microphoneError:"Pristup va\u0161em mikrofonu nije uspio",microphonePermission:"Gre\u0161ka kod dohvata dozvole za mikrofon"},deviceSelection:{noPermission:"Dozvola nije odobrena",previewUnavailable:"Pregled nije dostupan",selectADevice:"Odaberite ure\u0111aj",testAudio:"Reproducirajte testni zvuk"},dialog:{accessibilityLabel:{liveStreaming:"Emitiranje u\u017eivo"},allow:"Dozvoli",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Istodobno je dopu\u0161ten samo jedan zajedni\u010dki videozapis",applicationWindow:"Prozor aplikacije",Back:"Nazad",cameraConstraintFailedError:"Va\u0161a kamera ne zadovoljava neka od potrebnih ograni\u010denja.",cameraNotFoundError:"Kamera nije prona\u0111ena.",cameraNotSendingData:"Ne mo\u017eemo pristupiti va\u0161oj kameri. Provjerite koristi li ovaj ure\u0111aj neki drugi program, odaberite drugi ure\u0111aj s izbornika postavki ili poku\u0161ajte ponovno u\u010ditati program.",cameraNotSendingDataTitle:"Nije mogu\u0107e pristupiti kameri",cameraPermissionDeniedError:"Niste odobrili kori\u0161tenje kamere. I dalje se mo\u017eete pridru\u017eiti konferenciji, ali drugi vas ne\u0107e vidjeti. Pomo\u0107u gumba kamere u adresnoj traci to popravite.",cameraUnknownError:"Nije mogu\u0107e koristiti kameru iz nepoznatog razloga.",cameraUnsupportedResolutionError:"Fotoaparat ne podr\u017eava potrebnu razlu\u010divost videozapisa.",Cancel:"Odustani",close:"Zatvori",conferenceDisconnectMsg:"Provjerite va\u0161u mre\u017enu vezu. Ponovno spajanje za {{seconds}} sekundi...",conferenceDisconnectTitle:"Odspojeni ste.",conferenceReloadMsg:"Poku\u0161avamo popraviti. Ponovno spajanje za {{seconds}} sekundi...",conferenceReloadTitle:"Na\u017ealost, ne\u0161to je po\u0161lo po zlu.",confirm:"Potvrdi",confirmNo:"Ne",confirmYes:"Da",connectError:"Uh! Ne\u0161to je po\u0161lo po zlu i nismo se mogli povezati s konferencijom.",connectErrorWithMsg:"Uh! Ne\u0161to je po\u0161lo po zlu i nismo se mogli povezati s konferencijom: {{msg}}",connecting:"Povezivanje",contactSupport:"Kontaktirajte podr\u0161ku",copy:"Kopiraj",dismiss:"Odbaciti",displayNameRequired:"",done:"Gotovo",enterDisplayName:"",error:"Gre\u0161ka",externalInstallationMsg:"Trebate instalirati pro\u0161irenje za dijeljenje radne povr\u0161ine.",externalInstallationTitle:"Potrebno je pro\u0161irenje",goToStore:"",gracefulShutdown:"",IamHost:"Ja sam doma\u0107in",incorrectRoomLockPassword:"",incorrectPassword:"Pogre\u0161no korisni\u010dko ime ili lozinka",inlineInstallationMsg:"Trebate instalirati pro\u0161irenje za dijeljenje radne povr\u0161ine.",inlineInstallExtension:"Sada instalirati",internalError:"Ups! Ne\u0161to je po\u0161lo po zlu. Dogodila se gre\u0161ka: {{error}}",internalErrorTitle:"Interna gre\u0161ka",kickMessage:"",kickParticipantButton:"Izbaci",kickParticipantDialog:"Jeste li sigurni da \u017eelite izbaciti ovog sudionika?",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Emitiranje u\u017eivo",liveStreamingDisabledForGuestTooltip:"Gosti ne mogu pokrenuti emitiranje u\u017eivo.",liveStreamingDisabledTooltip:"Pokretanje emitiranja u\u017eivo je isklju\u010deno.",lockMessage:"",lockRoom:"",lockTitle:"Zaklju\u010davanje nije uspjelo",logoutQuestion:"Jeste li sigurni da se \u017eelite odjaviti i zaustaviti konferenciju?",logoutTitle:"Odjava",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Va\u0161 mikrofon ne zadovoljava neka od potrebnih ograni\u010denja.",micNotFoundError:"Mikrofon nije prona\u0111en.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Niste odobrili kori\u0161tenje mikrofona. I dalje se mo\u017eete pridru\u017eiti konferenciji, ali vas drugi ne\u0107e \u010duti. Pomo\u0107u gumba kamere u adresnoj traci to popravite.",micUnknownError:"Nije mogu\u0107e koristiti mikrofon iz nepoznatog razloga.",muteParticipantBody:"Ne mo\u017eete ih ponovno uklju\u010diti, ali oni se mogu sami uklju\u010diti u bilo kojem trenutku.",muteParticipantButton:"Uti\u0161aj",muteParticipantDialog:"Jeste li sigurni da \u017eelite isklju\u010diti ovog sudionika? Ne mo\u017eete ih uklju\u010diti, ali oni mogu biti uklju\u010deni u bilo kojem trenutku.",muteParticipantTitle:"",Ok:"U redu",passwordLabel:"",passwordNotSupported:"Postavljanje lozinke za susret nije podr\u017eano.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Va\u0161 preglednik blokira sko\u010dne prozore s ove web-lokacije. Omogu\u0107ite sko\u010dne prozore u sigurnosnim postavkama preglednika i poku\u0161ajte ponovno.",popupErrorTitle:"Sko\u010dni prozor je blokiran",recording:"Snimanje",recordingDisabledForGuestTooltip:"Gosti ne mogu pokrenuti snimanje.",recordingDisabledTooltip:"Isklju\u010deno je pokretanje snimanja.",rejoinNow:"Pridru\u017eite se sada",remoteControlAllowedMessage:"{{user}} je prihvatio va\u0161 zahtjev daljinskog upravljanja!",remoteControlDeniedMessage:"{{user}} je odbio va\u0161 zahtjev daljinskog upravljanja!",remoteControlErrorMessage:"Do\u0161lo je do pogre\u0161ke prilikom poku\u0161aja zahtjeva za dozvole daljinskog upravljanja od {{user}}!",remoteControlRequestMessage:"Ho\u0107ete li dopustiti {{user}} daljinski upravljati radnom povr\u0161inom?",remoteControlShareScreenWarning:"Imajte na umu da ako pritisnete \"Dopusti\" podijelit \u0107ete zaslon!",remoteControlStopMessage:"Sesija udaljenog upravlja\u010da je zavr\u0161ena!",remoteControlTitle:"Udaljeno upravljanje",Remove:"Ukloni",removePassword:"",removeSharedVideoMsg:"Jeste li sigurni da \u017eelite ukloniti dijeljeni videozapis?",removeSharedVideoTitle:"Uklanjanje dijeljenog videozapisa",reservationError:"Pogre\u0161ka sustava rezervacija",reservationErrorMsg:"Oznaka gre\u0161ke: {{code}}, poruka: {{msg}}",retry:"Poku\u0161aj ponovno",screenSharingFailedToInstall:"Uh! Nije uspjelo instaliranje pro\u0161irenja dijeljenja zaslona.",screenSharingFailedToInstallTitle:"Nije uspjelo instaliranje pro\u0161irenja dijeljenja zaslona",screenSharingFirefoxPermissionDeniedError:"Ne\u0161to je po\u0161lo po krivu dok smo poku\u0161avali dijeliti va\u0161 zaslon. Provjerite jeste li nam dali dopu\u0161tenje.",screenSharingFirefoxPermissionDeniedTitle:"Uh! Nismo mogli pokrenuti dijeljenje zaslona!",screenSharingPermissionDeniedError:"Uh! Ne\u0161to se dogodilo s va\u0161im dijeljenjem dozvola za pro\u0161irenje na zaslonu. Ponovno u\u010ditajte i poku\u0161ajte ponovno.",serviceUnavailable:"",sessTerminated:"",Share:"",shareVideoLinkError:"Unesite to\u010dnu vezu na youtube.",shareVideoTitle:"Dijelite videozapis",shareYourScreen:"Dijelite va\u0161 ekran",shareYourScreenDisabled:"Dijeljenje ekrana je isklju\u010deno.",shareYourScreenDisabledForGuest:"",startLiveStreaming:"Pokreni emitiranje u\u017eivo",startRecording:"Pokreni snimanje",startRemoteControlErrorMessage:"Do\u0161lo je do pogre\u0161ke prilikom poku\u0161aja pokretanja sesije daljinskog upravlja\u010da!",stopLiveStreaming:"Zaustavi emitiranje u\u017eivo",stopRecording:"Zaustavi snimanje",stopRecordingWarning:"Da li ste sigurni da \u017eelite zaustaviti snimanje?",stopStreamingWarning:"Da li ste sigurani da \u017eelite zaustaviti emitiranje u\u017eivo?",streamKey:"Klju\u010d za emitiranje u\u017eivo",Submit:"Po\u0161alji",thankYou:"Hvala \u0161to koristite {{appName}}!",token:"token",tokenAuthFailed:"\u017dao nam je, nije vam dozvoljeno pridru\u017eiti se pozivu.",tokenAuthFailedTitle:"Autentikacija nije uspjela",transcribing:"",unlockRoom:"",userPassword:"korisni\u010dka lozinka",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"",Yes:"Da",yourEntireScreen:"Va\u0161 cijeli zaslon"},dialOut:{statusMessage:"je sada {{status}}"},feedback:{average:"Prosje\u010dno",bad:"Lo\u0161e",detailsLabel:"Recite nam vi\u0161e o tome.",good:"Dobro",rateExperience:"",veryBad:"",veryGood:""},incomingCall:{answer:"",audioCallTitle:"",decline:"Odbaciti",productLabel:"",videoCallTitle:""},info:{accessibilityLabel:"Prika\u017ei informacije",addPassword:"",cancelPassword:"",conferenceURL:"Veza:",country:"Dr\u017eava",dialANumber:"Da biste se pridru\u017eili sastanku, nazovite jedan od tih brojeva i unesite pin.",dialInConferenceID:"PIN:",dialInNotSupported:"Na\u017ealost, pozivanje u trenutno nije podr\u017eano.",dialInNumber:"Nazvati:",dialInSummaryError:"",dialInTollFree:"Besplatno",genericError:"Ups, ne\u0161to je po\u0161lo po zlu.",inviteLiveStream:"Da biste pogledali prijenos u\u017eivo ovog sastanka, kliknite ovu vezu: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Emitiranje u\u017eivo:",moreNumbers:"",noNumbers:"",noPassword:"",noRoom:"",numbers:"Brojevi za nazivanje:",password:"",title:"",tooltip:"Podijelite vezu i informacije za nazivanje ovog sastanka",label:"Informacije o sastanku"},inviteDialog:{alertText:"Pozivanje nekih sudionika nije uspjelo.",header:"Pozivnica",searchCallOnlyPlaceholder:"Unesite telefonski broj",searchPeopleOnlyPlaceholder:"Potra\u017eite sudionike",searchPlaceholder:"Broj sudionika ili telefon",send:""},inlineDialogFailure:{msg:"",retry:"",support:"",supportMsg:""},keyboardShortcuts:{focusLocal:"",focusRemote:"",fullScreen:"Prika\u017ei / Iza\u0111i iz cijelog ekrana",keyboardShortcuts:"Pre\u010daci tipkovnice",localRecording:"",mute:"Isklju\u010dite ili uklju\u010dite mikrofon",pushToTalk:"",raiseHand:"Podigni / spusti ruku",showSpeakerStats:"Prika\u017ei statistiku zvu\u010dnika",toggleChat:"Otvori ili zatvori \u010davrljanje",toggleFilmstrip:"",toggleScreensharing:"",toggleShortcuts:"Prika\u017ei ili sakrij pre\u010dace tipkovnice",videoMute:"Pokreni ili zaustavi kameru"},liveStreaming:{busy:"Radimo na osloba\u0111anju streaming resursa. Molim poku\u0161ajte ponovno za par minuta.",busyTitle:"Svi stream-ovi su trenutno zauzeti",changeSignIn:"",choose:"Odaberite emitiranje u\u017eivo",chooseCTA:"Odaberite opciju emitiranja. Trenutno ste prijavljeni kao {{email}}.",enterStreamKey:"Ovdje unesite klju\u010d YouTube prijenos u\u017eivo.",error:"Emitiranje u\u017eivo nije uspjelo. Poku\u0161ajte ponovno.",errorAPI:"",errorLiveStreamNotEnabled:"Emitiranje u\u017eivo nije omogu\u0107eno na {{email}}. Omogu\u0107ite prijenos u\u017eivo ili se prijavite na ra\u010dun s omogu\u0107enim prijenosom u\u017eivo.",expandedOff:"Emitiranje u\u017eivo je zaustavljeno",expandedOn:"Ovaj sastanak se emitira u\u017eivo na YouTube.",expandedPending:"Emitiranje u\u017eivo se pokre\u0107e...",failedToStart:"Pokretanje emitiranja u\u017eivo nije uspjelo",getStreamKeyManually:"Nismo mogli dohvatiti niti jedan prijenos u\u017eivo. Poku\u0161ajte dobiti klju\u010d u\u017eivo iz usluge YouTube.",invalidStreamKey:"Klju\u010d za emitiranje u\u017eivo mo\u017eda je pogre\u0161an.",off:"Emitiranje u\u017eivo je zaustavljeno",on:"Emitiranje u\u017eivo",pending:"Pokretanje emitiranja u\u017eivo...",serviceName:"Usluga emitiranja u\u017eivo",signedInAs:"",signIn:"",signInCTA:"Prijavite se ili unisete va\u0161 YouTube klju\u010d za emitiranje u\u017eivo.",signOut:"Pokrenite emitiranje u\u017eivo",start:"Pokreni emitiranje u\u017eivo",streamIdHelp:"",unavailableTitle:""},localRecording:{clientState:{off:"",on:"",unknown:""},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"",labelToolTip:"",localRecording:"",me:"",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:""},moderator:"",no:"Ne",participant:"",participantStats:"",sessionToken:"",start:"Pokreni snimanje",stop:"Zaustavi snimanje",yes:"Da"},lockRoomPassword:"Lozinka",lockRoomPasswordUppercase:"Lozinka",me:"",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"Nije povezano",focus:"",focusFail:"",grantedTo:"",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"",moderator:"",muted:"",mutedTitle:"",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"",suboptimalExperienceTitle:"",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"",presenceStatus:{busy:"",calling:"",connected:"Povezan",connecting:"Povezivanje",connecting2:"Povezivanje",disconnected:"Nije povezano",expired:"",ignored:"",initializingCall:"",invited:"Pozivnica",rejected:"",ringing:""},profile:{setDisplayNameLabel:"",setEmailInput:"",setEmailLabel:"",title:""},recording:{authDropboxText:"",availableSpace:"",beta:"",busy:"",busyTitle:"Svi stream-ovi su trenutno zauzeti",error:"Emitiranje u\u017eivo nije uspjelo. Poku\u0161ajte ponovno.",expandedOff:"",expandedOn:"",expandedPending:"Snimanje se pokre\u0107e...",failedToStart:"",fileSharingdescription:"",live:"",loggedIn:"Prijavljen kao {{userName}}",off:"",on:"Snimanje",pending:"",rec:"",serviceDescription:"",serviceName:"",signIn:"",signOut:"Pokrenite emitiranje u\u017eivo",unavailable:"",unavailableTitle:""},sectionList:{pullToRefresh:""},settings:{calendar:{about:"Integracija kalendara {{appName}} koristi se za siguran pristup kalendaru tako da mo\u017ee \u010ditati nadolaze\u0107e doga\u0111aje.",disconnect:"Odspoji",microsoftSignIn:"Prijavite se sa Microsoftom",signedIn:"Trenutno pristupate doga\u0111ajima kalendara za {{email}}. Kliknite gumb Odspoji u nastavku da biste zaustavili pristup doga\u0111ajima u kalendaru.",title:"Kalendar"},devices:"Ure\u0111aji",followMe:"Svatko me slijedi",language:"Jezik",loggedIn:"Prijavljen kao {{name}}",moderator:"",more:"Vi\u0161e",name:"Ime",noDevice:"Gotovo",selectAudioOutput:"Zvu\u010dni izlaz",selectCamera:"Kamera",selectMic:"Mikrofon",startAudioMuted:"Svi po\u010dinju sa isklju\u010denim zvukom",startVideoMuted:"Svi po\u010dinju sakriveni",title:"Postavke"},settingsView:{alertOk:"U redu",alertTitle:"",alertURLText:"Uneseni URL poslu\u017eitelja nije ispravan",buildInfoSection:"",conferenceSection:"Konferencija",displayName:"Ime za prikaz",email:"",header:"Postavke",profileSection:"",serverURL:"URL poslu\u017eitelja",startWithAudioMuted:"Pokreni sa isklju\u010denim zvukom",startWithVideoMuted:"Pokreni sa uti\u0161anim videozapisom",version:""},share:{dialInfoText:"",mainText:""},speaker:"Zvu\u010dnik",speakerStats:{hours:"",minutes:"",name:"Ime",seconds:"",speakerStats:"Statistika govornika",speakerTime:"Vrijeme govornika"},startupoverlay:{policyText:"",title:"{{app}} treba koristiti va\u0161 mikrofon i kameru."},suspendedoverlay:{rejoinKeyTitle:"",text:"",title:""},toolbar:{accessibilityLabel:{audioOnly:"",audioRoute:"Odaberite ure\u0111aj za zvuk",callQuality:"",cc:"",chat:"",document:"Prika\u017ei ili sakrij dijeljeni dokument",feedback:"",fullScreen:"Prebacite cijeli ekran",hangup:"Iza\u0111ite iz poziva",invite:"",kick:"",localRecording:"",lockRoom:"",moreActions:"",moreActionsMenu:"",mute:"Nijemi videozapis",pip:"Otvori na\u010din slika-u-slici",profile:"",raiseHand:"",recording:"",remoteMute:"",Settings:"Prika\u017ei postavke",sharedvideo:"",shareRoom:"Pozovi nekoga",shareYourScreen:"Uklju\u010di / isklju\u010di dijeljenje ekrana",shortcuts:"Uklju\u010di / isklju\u010di pre\u010dace",show:"",speakerStats:"Uklju\u010di / isklju\u010di statistiku govornika",tileView:"Prikaz plo\u010dica",toggleCamera:"Uklju\u010di / isklju\u010di kameru",videomute:"Nijemi videozapis",videoblur:""},addPeople:"Dodaj osobe u poziv",audioOnlyOff:"Isklju\u010di na\u010din samo zvuk",audioOnlyOn:"Uklju\u010di na\u010din samo zvuk",audioRoute:"Odaberite ure\u0111aj za zvuk",authenticate:"Autentikacija",callQuality:"Upravljaj kvalitetom poziva",chat:"Otvori / Zatvori \u010davrljanje",closeChat:"Zatvori \u010davrljanje",documentClose:"Zatvori dijeljeni dokument",documentOpen:"Otvori dijeljenji dokument",enterFullScreen:"Prikaz preko cijelog ekrana",enterTileView:"Uklju\u010di prikaz plo\u010dica",exitFullScreen:"Iza\u0111i iz prikaza cijelog ekrana",exitTileView:"Iza\u0111i iz prikaza plo\u010dica",feedback:"",hangup:"Izlaz",invite:"",login:"Prijava",logout:"Odjava",lowerYourHand:"Spusti ruku",moreActions:"Vi\u0161e akcija",mute:"Isklju\u010di / Uklju\u010di zvuk",openChat:"Otvori \u010davrljanje",pip:"Otvori na\u010din slika-u-slici",profile:"",raiseHand:"Podigni / spusti ruku",raiseYourHand:"Podigni ruku",Settings:"Postavke",sharedvideo:"Podijeli YouTube videozapis",shareRoom:"Pozovi nekoga",shortcuts:"Prikaz pre\u010daca",speakerStats:"Statistika govornika",startScreenSharing:"Pokreni dijeljenje ekrana",startSubtitles:"Pokreni podnaslove",stopScreenSharing:"Zaustavi dijeljenje ekrana",stopSubtitles:"Zaustavi podnaslove",stopSharedVideo:"Zaustavi YouTube videozapis",talkWhileMutedPopup:"Poku\u0161ava\u0161 govoriti? Uti\u0161an si.",tileViewToggle:"Prikaz plo\u010dica",toggleCamera:"Uklju\u010di / isklju\u010di kameru",videomute:"Pokreni / Zaustavi kameru",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"",error:"Emitiranje u\u017eivo nije uspjelo. Poku\u0161ajte ponovno.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"",start:"",stop:"",tr:""},userMedia:{androidGrantPermissions:"",chromeGrantPermissions:"",edgeGrantPermissions:"",electronGrantPermissions:"Molim dozvolite upotrebu kamere i mikrofona",firefoxGrantPermissions:"Odaberite Dijeljenje odabranog ure\u0111aja kada preglednik zatra\u017ei dopu\u0161tenja.",iexplorerGrantPermissions:"",nwjsGrantPermissions:"Molim dozvolite upotrebu kamere i mikrofona",operaGrantPermissions:"","react-nativeGrantPermissions":"",safariGrantPermissions:""},videoSIPGW:{busy:"",busyTitle:"",errorAlreadyInvited:"",errorInvite:"",errorInviteFailed:"",errorInviteFailedTitle:"",errorInviteTitle:"",pending:""},videoStatus:{audioOnly:"",audioOnlyExpanded:"",callQuality:"",hd:"",highDefinition:"",labelTooiltipNoVideo:"",labelTooltipAudioOnly:"",ld:"",lowDefinition:"",onlyAudioAvailable:"",onlyAudioSupported:"",p2pEnabled:"",p2pVideoQualityDescription:"",recHighDefinitionOnly:"",sd:"",standardDefinition:""},videothumbnail:{domute:"Uti\u0161aj",flip:"",kick:"",moderator:"",mute:"",muted:"Uti\u0161aj",remoteControl:"",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Dodirnite za pridru\u017eivanje",roomname:"Unesi naziv sobe"},appDescription:"Samo naprijed, koristite videopozive sa cijelim timom. Zapravo, pozovite sve koje znate. {{app}} je potpuno \u0161ifrirano, videokonferencijsko rje\u0161enje 100% otvorenog koda koje mo\u017eete koristiti cijeli dan, svaki dan, besplatno - bez potrebe za ra\u010dunom.",audioVideoSwitch:{audio:"Glas",video:"Videozapis"},calendar:"Kalendar",connectCalendarButton:"Pove\u017eite svoj kalendar",connectCalendarText:"Pove\u017eite svoj kalendar da biste vidjeli sve svoje sastanke u {{app}}. Osim toga, dodajte {{provider}} sastanke u svoj kalendar i pokrenite ih jednim klikom.",enterRoomTitle:"Zapo\u010dnite novi sastanak",go:"KRENI",join:"U\u0110I",info:"",privacy:"Privatnost",recentList:"Nedavni",recentListDelete:"Izbri\u0161i",recentListEmpty:"Va\u0161 popis nedavnih razgovora je prazan. \u010cavrljajte sa svojim timom i ovdje \u0107ete prona\u0107i popis nedavnih sastanaka.",reducedUIText:"",roomname:"Unesi naziv sobe",roomnameHint:"Unesite naziv ili URL sobe koju \u017eelite pridru\u017eiti. Mo\u017eete izmijeniti ime, samo neka ljudi s kojima se sastajete to znaju kako bi unijeli isto ime.",sendFeedback:"Po\u0161aljite povratnu informaciju",terms:"Uvjeti",title:"Sigurna, potpuno opremljena i potpuno besplatna videokonferencija"}}},660,[]); -__d(function(e,o,a,s,n,r,c){n.exports={en:"Inglese",af:"",az:"Azero",bg:"Bulgaro",cs:"Ceco",de:"Tedesco",el:"Greco",eo:"Esperanto",es:"Spagnolo",fr:"Francese",hy:"Armeno",it:"Italiano",ja:"Giapponese",ko:"Coreano",nb:"Norvegese bokmal",oc:"Occitano",pl:"Polacco",ptBR:"Portoghese (Brasile)",ru:"Russo",sk:"Slovacco",sl:"Sloveno",sv:"Svedese",tr:"Turco",vi:"Vietnamita",zhCN:"Cinese (Cina)"}},661,[]); -__d(function(e,i,o,a,t,r,n){t.exports={addPeople:{add:"Invita",countryNotSupported:"Non supportiamo ancora questa destinazione.",countryReminder:"Stai chiamando fuori dagli Stati Uniti? Assicurati di iniziare inserendo il codice paese!",disabled:"Non puoi invitare persone.",failedToAdd:"",footerText:"La chiamata dall'esterno \xe8 disabilitata.",loading:"Cercando persone e numeri di telefono",loadingNumber:"Sto validando il numero di telefono",loadingPeople:"Ricerca delle persone da invitare",noResults:"Nessun risultato corrispondente",noValidNumbers:"Inserire un numero di telefono",searchNumbers:"Aggiungi numeri di telefono",searchPeople:"Cerca persone",searchPeopleAndNumbers:"Cerca persone o aggiungi i loro numeri di telefono",telephone:"Telefono: {{number}}",title:"Invita persone a questo meeting"},audioDevices:{bluetooth:"Bluetooth",headphones:"Cuffie",phone:"Telefono",speaker:"Relatore"},audioOnly:{audioOnly:"Solo audio"},calendarSync:{addMeetingURL:"Aggiungi un collegamento alla conferenza",confirmAddLink:"Vuoi aggiungere un collegamento a ANDI Conference a questo evento?",error:{appConfiguration:"L'integrazione del calendario non \xe8 configurata in modo appropriato.",generic:"\xc8 stato riscontrato un errore. Controllare le impostazioni del calendario e ricaricare la pagina.",notSignedIn:"\xc8 stato riscontrato un errore durante l'autenticazione per la visualizzazione degli eventi del calendario. Controllare le impostazioni del calendario e provare a ripetere l'accesso."},join:"Partecipa",joinTooltip:"Partecipa alla conferenza",nextMeeting:"prossimo meeting",noEvents:"Non ci sono eventi programmati a breve.",ongoingMeeting:"conferenza in corso",permissionButton:"Apri impostazioni",permissionMessage:"Per visualizzare la lista delle conferenze nell'app \xe8 richiesto il permesso Calendario",refresh:"Aggiorna calendario",today:"Oggi"},chat:{error:"Errore: il tuo messaggio \u201c{{originalText}}\u201d non e\u2019 stato inviato. Ragione: {{error}}",messagebox:"Digitare un messaggio",nickname:{popover:"Scegli un nickname",title:"Inserire un nickname per utilizzare la chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Collegamento al meeting in corso\u2026"},connection:{ATTACHED:"Collegato",AUTHENTICATING:"Autenticazione",AUTHFAIL:"Autenticazione fallita",CONNECTED:"Connesso",CONNECTING:"Connessione",CONNFAIL:"Connessione non riuscita",DISCONNECTED:"Occupato",DISCONNECTING:"Disconnessione in corso",ERROR:"Errore",RECONNECTING:"Si \xe8 verificato un problema di rete. Riconnessione..."},connectionindicator:{address:"Indirizzo:",bandwidth:"Banda stimata:",bitrate:"Bitrate:",bridgeCount:"Contatore server:",connectedTo:"Connesso a:",framerate:"Fotogrammi al secondo:",less:"Mostra meno",localaddress:"Indirizzo locale:",localaddress_plural:"Indirizzi locali:",localport:"Porta locale:",localport_plural:"Porte locali:",more:"Mostra di pi\xf9",packetloss:"Perdita pacchetti:",quality:{good:"Buona",inactive:"Inattiva",lost:"Persa",nonoptimal:"Non ottimale",poor:"Scarsa"},remoteaddress:"Indirizzo remoto:",remoteaddress_plural:"Indirizzi remoti:",remoteport:"Porta remota:",remoteport_plural:"Porte remote:",resolution:"Risoluzione:",status:"Connessione:",transport:"Trasporto:",turn:"(ruota)"},dateUtils:{earlier:"Prima",today:"Oggi",yesterday:"Ieri"},deepLinking:{appNotInstalled:"Per partecipare a questo meeting sul tuo telefono ti serve l'app mobile di {{app}}",description:"Non \xe8 successo nulla? Abbiamo provato ad avviare la tua videoconferenza sull'app desktop di {{app}}. Prova di nuovo o avviala nell'app web di {{app}}.",descriptionWithoutWeb:"",downloadApp:"Scarica l'app",launchWebButton:"Avvia sul web",openApp:"Prosegui verso l'app",title:"Sto avviando la tua videoconferenza su {{app}}...",tryAgainButton:"Prova di nuovo sul desktop"},defaultLink:"es. {{url}}",deviceError:{cameraError:"Impossibile accedere alla videocamera",cameraPermission:"Errore nell'ottenere i permessi per la videocamera",microphoneError:"Impossibile accedere al microfono",microphonePermission:"Errore nell'ottenere i permessi per il microfono"},deviceSelection:{noPermission:"Permesso negato",previewUnavailable:"Anteprima non disponibile",selectADevice:"Seleziona un dispositivo",testAudio:"Riproduci un suono di test"},dialog:{accessibilityLabel:{liveStreaming:"Diretta"},allow:"Consenti",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\xc8 permesso un solo video alla volta",applicationWindow:"Finestra applicazione",Back:"Indietro",cameraConstraintFailedError:"La tua videocamera non soddisfa alcuni dei requisiti richiesti.",cameraNotFoundError:"Videocamera non trovata.",cameraNotSendingData:"Non possiamo accedere alla tua videocamera. Controlla che non sia gi\xe0 usata da un'altra applicazione, seleziona un altro dispositivo dalle impostazioni o prova a ricaricare l'applicazione.",cameraNotSendingDataTitle:"Impossibile accedere alla videocamera",cameraPermissionDeniedError:"Non hai concesso il permesso di usare la videocamera. Potrai partecipare comunque alla conferenza ma gli altri non potranno vederti. Usa il pulsante a forma di videocamera nella barra degli indirizzi per risolvere il problema.",cameraUnknownError:"Impossibile usare la videocamera per un motivo sconosciuto.",cameraUnsupportedResolutionError:"La tua videocamera non supporta la risoluzione richiesta.",Cancel:"Annulla",close:"Chiudi",conferenceDisconnectMsg:"Controlla la tua connessione. Riconnessione in {{seconds}} secondi...",conferenceDisconnectTitle:"Sei stato disconnesso.",conferenceReloadMsg:"Stiamo cercando di risolvere il problema. Riconnessione in {{seconds}} secondi...",conferenceReloadTitle:"Purtroppo qualcosa \xe8 andato storto.",confirm:"Conferma",confirmNo:"No",confirmYes:"S\xec",connectError:"Oops! Qualcosa \xe8 andato storto e non ti puoi collegare alla conferenza.",connectErrorWithMsg:"Oops! Qualcosa \xe8 andato storto e non ti puoi collegare alla conferenza: {{msg}}",connecting:"Connessione",contactSupport:"Contatta il supporto",copy:"Copia",dismiss:"Scarta",displayNameRequired:"",done:"Fatto",enterDisplayName:"",error:"Errore",externalInstallationMsg:"Devi installare la nostra estensione per la condivisione desktop.",externalInstallationTitle:"Richiesta estensione",goToStore:"Vai al negozio on-line",gracefulShutdown:"Il nostro servizio \xe8 al momento spento per manutenzione. Si prega di riprovare pi\xf9 tardi.",IamHost:"Sono l'organizzatore",incorrectRoomLockPassword:"",incorrectPassword:"Nome utente o password errati",inlineInstallationMsg:"Devi installare la nostra estensione per la condivisione desktop.",inlineInstallExtension:"Installa adesso",internalError:"Ops! Qualcosa \xe8 andato storto. Questo \xe8 l'errore: {{error}}",internalErrorTitle:"Errore interno",kickMessage:"",kickParticipantButton:"Espelli",kickParticipantDialog:"Espellere questo partecipante?",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Live Streaming",liveStreamingDisabledForGuestTooltip:"Gli ospiti non possono avviare una diretta.",liveStreamingDisabledTooltip:"Trasmissioni in diretta disabilitate.",lockMessage:"Impossibile bloccare la conferenza.",lockRoom:"",lockTitle:"Blocco fallito",logoutQuestion:"Vuoi disconnetterti e interrompere la conferenza ?",logoutTitle:"Logout",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Il tuo microfono non soddisfa alcuni dei requisiti richiesti.",micNotFoundError:"Microfono non trovato.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Non hai concesso il permesso di usare il microfono. Puoi comunque partecipare alla conferenza ma gli altri non potranno sentirti. Usa il bottone a forma di telecamera nella barra degli indirizzi per cambiare impostazioni.",micUnknownError:"Impossibile usare il microfono per un motivo sconosciuto.",muteParticipantBody:"Tu non sarai in grado di riattivare il loro audio, ma loro potranno riattivarlo in qualsiasi momento.",muteParticipantButton:"Disattiva audio",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"Ok",passwordLabel:"",passwordNotSupported:"Le password per le videoconferenze non sono supportate.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Il tuo browser sta bloccando i pop-up da questo sito. Per favore abilit\xe0 i pop-up dalle impostazioni di sicurezza del browser e riprova.",popupErrorTitle:"Pop-up bloccato",recording:"Registrazione",recordingDisabledForGuestTooltip:"Gli ospiti non possono avviare una registrazione.",recordingDisabledTooltip:"Registrazione disabilitata.",rejoinNow:"Ricollegati ora",remoteControlAllowedMessage:"{{user}} ha accettato la tua richiesta di controllo remoto!",remoteControlDeniedMessage:"{{user}} ha respinto la tua richiesta di controllo remoto!",remoteControlErrorMessage:"Si \xe8 verificato un errore mentre si cercava di richiedere il controllo remoto a {{user}}!",remoteControlRequestMessage:"Vuoi consentire ad {{user}} di controllare da remoto il tuo desktop?",remoteControlShareScreenWarning:"Tieni conto che premendo \"Permetti\" condividerai il tuo schermo.",remoteControlStopMessage:"Sessione di controllo remoto terminata!",remoteControlTitle:"Connessione desktop remoto",Remove:"Rimuovi",removePassword:"",removeSharedVideoMsg:"Sei sicuro di voler rimuovere il tuo video condiviso?",removeSharedVideoTitle:"Rimuovi video condiviso",reservationError:"Errore di sistema in prenotazione",reservationErrorMsg:"Codice di errore: {{code}}, messaggio: {{msg}}",retry:"Riprova",screenSharingFailedToInstall:"Oops! Non \xe8 stato possibile installare l'estensione per la condivisione schermo. ",screenSharingFailedToInstallTitle:"Impossibile installare l'estensione per la condivisione schermo",screenSharingFirefoxPermissionDeniedError:"Qualcosa \xe8 andato storto mentre cercavamo di condividere il tuo schermo. Assicurati di averci dato il premesso di condivisione.",screenSharingFirefoxPermissionDeniedTitle:"Ops! Non siamo stati in grado di avviare la condivisione schermo!",screenSharingPermissionDeniedError:"Oops! Qualcosa \xe8 andato storto con le impostazioni dell'estensione per la condivisione dello schermo. Ricarica la pagina e prova di nuovo.",serviceUnavailable:"Servizio non disponibile",sessTerminated:"Chiamata terminata",Share:"Condividi",shareVideoLinkError:"Fornire un link youtube corretto.",shareVideoTitle:"Condividi un video",shareYourScreen:"Condividi schermo",shareYourScreenDisabled:"Condivisione schermo disabilitata.",shareYourScreenDisabledForGuest:"Gli ospiti non possono condividere lo schermo.",startLiveStreaming:"Inizia una diretta",startRecording:"Inizia a registrare",startRemoteControlErrorMessage:"Si \xe8 verificato un errore cercando di avviare la sessione di controllo remoto!",stopLiveStreaming:"Ferma la diretta",stopRecording:"Ferma registrazione",stopRecordingWarning:"Sei sicuro di voler interrompere la registrazione?",stopStreamingWarning:"Sei sicuro di voler interrompere il live streaming?",streamKey:"Chiave per trasmissione in diretta",Submit:"Invia",thankYou:"Grazie per aver usato {{appName}}!",token:"token",tokenAuthFailed:"Ci dispiace ma non sei autorizzato a partecipare a questa chiamata.",tokenAuthFailedTitle:"Autenticazione fallita",transcribing:"Trascrizione",unlockRoom:"",userPassword:"password utente",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"In attesa dell'organizzatore ...",Yes:"S\xec",yourEntireScreen:"Schermo intero"},dialOut:{statusMessage:"\xe8 ora {{status}}"},feedback:{average:"Media",bad:"Scadente",detailsLabel:"Dicci qualcosa di pi\xf9 in proposito.",good:"Buona",rateExperience:"Valuta la qualit\xe0 della videoconferenza.",veryBad:"Pessima",veryGood:"Molto Buona"},incomingCall:{answer:"Risposta",audioCallTitle:"Chiamata in arrivo",decline:"Scarta",productLabel:"da ANDI Conference",videoCallTitle:"Videochiamata in arrivo"},info:{accessibilityLabel:"Mostra informazioni",addPassword:"",cancelPassword:"",conferenceURL:"Collegamento:",country:"Paese",dialANumber:"",dialInConferenceID:"PIN:",dialInNotSupported:"Spiacenti, la chiamata per partecipare attualmente non \xe8 supportata",dialInNumber:"Componi:",dialInSummaryError:"",dialInTollFree:"",genericError:"Ops, qualcosa \xe8 andato storto.",inviteLiveStream:"Per visualizzare la trasmissione in diretta di questo meeting, clicca su questo link: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Trasmissione in diretta:",moreNumbers:"Pi\xf9 numeri",noNumbers:"Nessun numero da chiamare.",noPassword:"Nessuno",noRoom:"Non \xe8 stata specificata nessuna stanza da chiamare.",numbers:"Numeri da chiamare",password:"",title:"Condividi",tooltip:"Condividi collegamento e informazioni di chiamata per questa conferenza",label:""},inviteDialog:{alertText:"",header:"Invita",searchCallOnlyPlaceholder:"Inserisci numero di telefono",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"Un piccolo inconveniente.",retry:"Prova ancora",support:"Supporto",supportMsg:"Se succede ancora contatta"},keyboardShortcuts:{focusLocal:"Sposta il focus sul tuo video",focusRemote:"Sposta il focus sul video di un altro partecipante",fullScreen:"Attiva o disattiva schermo intero",keyboardShortcuts:"Scorciatoie da tastiera",localRecording:"Mostra o nascondi i controlli per la registrazione",mute:"Attiva o disattiva il microfono",pushToTalk:"Premi per parlare",raiseHand:"Mostra / Nascondi i video",showSpeakerStats:"Mostra statistiche conversanti",toggleChat:"Apri o chiudi la chat",toggleFilmstrip:"Mostra o nascondi anteprime video",toggleScreensharing:"Cambia modalit\xe0 tra videocamera e condivisione schermo",toggleShortcuts:"Mostra o nascondi le scorciatoie",videoMute:"Attiva / disattiva videocamera"},liveStreaming:{busy:"Stiamo cercando di liberare risorse per lo streaming. Riprova tra qualche minuto.",busyTitle:"Tutti gli streamer sono impegnati al momento",changeSignIn:"Cambia account",choose:"Scegli una trasmissione in diretta",chooseCTA:"Scegli un'opzione di trasmissione. Attualmente sei loggato come {{email}}.",enterStreamKey:"Inserisci qui la tua chiave YouTube per le trasmissioni in diretta.",error:"Live streaming fallito. Prova di nuovo.",errorAPI:"Si \xe8 verificato un errore durante l'accesso ai tuoi broadcast YouTube. Prova a effettuare nuovamente il login.",errorLiveStreamNotEnabled:"La diretta non \xe8 attivata su {{email}}. Per favore abilita la diretta o effettua l'accesso con un account abilitato alle dirette.",expandedOff:"La diretta è stata interrotta",expandedOn:"La conferenza è attualmente in diretta su YouTube.",expandedPending:"La diretta è in fase di avvio...",failedToStart:"Avvio live streaming fallito",getStreamKeyManually:"",invalidStreamKey:"",off:"Il live streaming si \xe8 interrotto",on:"Live Streaming",pending:"Avvio live stream...",serviceName:"Servizio live streaming",signedInAs:"Sei attualmente collegato come:",signIn:"Registrati con Google",signInCTA:"Registrati o inserisci la tua chiave YouTube per la trasmissione in diretta.",signOut:"Esci",start:"Inizia una diretta",streamIdHelp:"Cos'\xe8 questo?",unavailableTitle:"Live streaming non disponibile"},localRecording:{clientState:{off:"Spento",on:"Acceso",unknown:"Sconosciuto"},dialogTitle:"Controlli di registrazione",duration:"Durata",durationNA:"N/A",encoding:"Codifica",label:"LOR",labelToolTip:"Registrazione locale avviata",localRecording:"Registrazione locale",me:"io",messages:{engaged:"Registrazione locale avviata.",finished:"La registrazione della sessione {{token}} è terminata. Invia il file della registrazione al moderatore.",finishedModerator:"La registrazione della sessione {{token}} è terminata. Il file della traccia local è stato salvato. Richiedere ai partecipanti di inviare le loro registrazioni.",notModerator:"Non sei un moderatore. Non puoi avviare o interrompere la registrazione"},moderator:"Moderatore",no:"No",participant:"Partecipante",participantStats:"Statistiche partecipanti",sessionToken:"Token della sessione ",start:"Avvia Registrazione",stop:"Ferma registrazione",yes:"S\xec"},lockRoomPassword:"password",lockRoomPasswordUppercase:"Password",me:"io",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"disconnesso",focus:"Focus su conferenza",focusFail:"{{component}} non disponibile - riprova in {{ms}} sec",grantedTo:"Permessi di moderatore garantiti a {{to}}!",invitedOneMember:"{{displayName}} \xe8 stato invitato",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"io",moderator:"Impostati i permessi di moderatore!",muted:"Hai iniziato la conversazione con l'audio disattivato.",mutedTitle:"Hai l'audio disattivato!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"Qualcuno",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Ehm... temiamo che la tua esperienza con {{appName}} non sar\xe0 granch\xe9 su questo browser. Stiamo cercando di migliorare la situazione ma, per il momento, prova ad utilizzare uno di questi browser supportati.",suboptimalExperienceTitle:"Problemi con il browser",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"powered by",presenceStatus:{busy:"Occupato",calling:"Chiamata\u2026",connected:"Connesso",connecting:"Connessione...",connecting2:"Connessione*...",disconnected:"Occupato",expired:"Scaduto",ignored:"Ignorato",initializingCall:"Inizializzazione chiamata\u2026",invited:"Invitato",rejected:"Rifiutato",ringing:"Sta suonando\u2026"},profile:{setDisplayNameLabel:"Imposta il nome da visualizzare",setEmailInput:"Inserisci e-mail",setEmailLabel:"Imposta la mail gravatar",title:"Profilo"},recording:{authDropboxText:"Carica su Dropbox",availableSpace:"Spazio disponibile: {{spaceLeft}} MB (rimangono approssimativamente {{duration}} minuti di registrazione)",beta:"BETA",busy:"Stiamo cercando di liberare risorse per la registrazione. Riprova tra qualche minuto.",busyTitle:"Tutti i registratori sono occupati",error:"Registrazione fallita. Prova di nuovo.",expandedOff:"Registrazione interrotta",expandedOn:"La registrazione della conferenza \xe8 attiva.",expandedPending:"La registrazione \xe8 in fase di avvio\u2026",failedToStart:"Non \xe8 stato possibile avviare la registrazione",fileSharingdescription:"",live:"DIRETTA",loggedIn:"Accesso effettuato come {{userName}}",off:"Registrazione interrotta",on:"Registrazione",pending:"In preparazione alla registrazione della conferenza\u2026",rec:"REC",serviceDescription:"",serviceName:"Servizio di registrazione",signIn:"Entra",signOut:"Esci",unavailable:"Ops! Il {{serviceName}} non \xe8 al momento disponibile. Stiamo lavorando per risolvere il problema. Riprova pi\xf9 tardi.",unavailableTitle:"Registrazione non disponibile"},sectionList:{pullToRefresh:"Trascina per aggiornare"},settings:{calendar:{about:"L\u2019integrazione del calendario con {{appName}} e\u2019 consigliata per accedere in sicurezza al proprio calendario per poter leggere i prossimi appuntamenti ",disconnect:"Disconnetti",microsoftSignIn:"Connettiti con un account Microsoft",signedIn:"Sto accedendo agli eventi del calendario per {{email}}. Clicca su Disconnetti per interrompere l\u2019accesso agli eventi del calendario.",title:"Calendario"},devices:"Dispositivi",followMe:"Tutti mi seguono",language:"Lingua",loggedIn:"Connesso come {{name}}",moderator:"Moderatore",more:"Altro",name:"Nome",noDevice:"Nessuno",selectAudioOutput:"Uscita audio",selectCamera:"Videocamera",selectMic:"Microfono",startAudioMuted:"Tutti cominciano con il microfono disattivato",startVideoMuted:"Tutti cominciano nascosti",title:"Impostazioni"},settingsView:{alertOk:"OK",alertTitle:"Attenzione",alertURLText:"L'URL del server inserito non \xe8 valido",buildInfoSection:"",conferenceSection:"Conferenza",displayName:"Nome visualizzato",email:"Email",header:"Impostazioni",profileSection:"Profilo",serverURL:"URL del server",startWithAudioMuted:"Inizia con l'audio mutato",startWithVideoMuted:"Avvia con video mutato",version:""},share:{dialInfoText:"",mainText:"Clicca sul link seguente per partecipare alla conferenza:\n{{roomUrl}}"},speaker:"Relatore",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Nome",seconds:"{{count}}s",speakerStats:"Statistiche del parlante",speakerTime:"Tempo del conversante"},startupoverlay:{policyText:" ",title:"{{app}} chiede di usare il tuo microfono e la tua videocamera."},suspendedoverlay:{rejoinKeyTitle:"Ricollegati",text:"Premi il pulsante Ricollegati per ricollegarti.",title:"La video chiamata si \xe8 interrotta perch\xe8 il computer \xe8 stato sospeso."},toolbar:{accessibilityLabel:{audioOnly:"Attiva/disattiva solo audio",audioRoute:"Seleziona la periferica audio",callQuality:"Gestisci qualit\xe0 della chiamata",cc:"Attiva/disattiva sottotitoli",chat:"Attiva/disattiva la chat",document:"Attiva/disattiva documento condiviso",feedback:"Lascia un feedback",fullScreen:"Attiva/disattiva schermo intero",hangup:"Lascia la conferenza",invite:"Invita persone",kick:"",localRecording:"Abilita controlli di registrazione locale",lockRoom:"",moreActions:"Attiva/disattiva Menu avanzato",moreActionsMenu:"Menu avanzato",mute:"Attiva/disattiva audio muto",pip:"Attiva/disattiva immagine nell\u2019immagine",profile:"Modifica profilo",raiseHand:"Attiva/disattiva alzata di mano",recording:"Attiva/disattiva registrazione",remoteMute:"",Settings:"Attiva/disattiva impostazioni",sharedvideo:"Attiva/disattiva condivisione YouTube",shareRoom:"Invita partecipante",shareYourScreen:"Attiva/disattiva condivisione schermo",shortcuts:"Attiva/disattiva scorciatoie",show:"",speakerStats:"Attiva/disattiva statistiche relatore",tileView:"Attiva/disattiva visualizzazione griglia",toggleCamera:"Attiva/disattiva webcam",videomute:"Attiva/disattiva silenziamento video",videoblur:""},addPeople:"Aggiungi persone alla chiamata",audioOnlyOff:"Disattiva modalit\xe0 solo audio",audioOnlyOn:"Disattiva modalit\xe0 solo audio",audioRoute:"Seleziona la periferica audio",authenticate:"Autenticazione",callQuality:"Gestisci qualit\xe0 della chiamata",chat:"Apri / Chiudi chat",closeChat:"",documentClose:"Chiudi documento condiviso",documentOpen:"Apri documento condiviso",enterFullScreen:"Visualizza a schermo intero",enterTileView:"",exitFullScreen:"Esci da schermo intero",exitTileView:"",feedback:"Lascia un feedback",hangup:"Esci",invite:"Invita persone",login:"Login",logout:"Logout",lowerYourHand:"",moreActions:"Pi\xf9 azioni",mute:"Microfono Attiva / Disattiva",openChat:"",pip:"Abilita visualizzazione immagine nell\u2019immagine",profile:"Modifica profilo",raiseHand:"Alza / Abbassa la mano",raiseYourHand:"",Settings:"Impostazioni",sharedvideo:"Condividi un video Youtube",shareRoom:"Invita partecipante",shortcuts:"Visualizza scorciatoie",speakerStats:"Statistiche dell'interlocutore",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"Ferma video YouTube",talkWhileMutedPopup:"Stai provando a parlare? Il microfono \xe8 disattivato.",tileViewToggle:"Attiva/disattiva visualizzazione griglia",toggleCamera:"Attiva/disattiva webcam",videomute:"Attiva / Disattiva videocamera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"",error:"Registrazione fallita. Prova di nuovo.",expandedLabel:"La trascrizione della conferenza \xe8 attiva",failedToStart:"C\u2019\xe8 stato un errore nell\u2019avvio del servizio di trascrizione.",labelToolTip:"Il servizio di trascrizione \xe8 in fase di avvio",off:"Trascrizione interrotta",pending:"Avvio del servizio di trascrizione della conferenza\u2026",start:"Avvia visualizzazione sottotitoli",stop:"Interrompi la visualizzazione dei sottotitoli",tr:"TR"},userMedia:{androidGrantPermissions:"Seleziona consenti quando richiesto dal browser.",chromeGrantPermissions:"Seleziona consenti quando richiesto dal browser.",edgeGrantPermissions:"Seleziona Si quando richiesto dal browser.",electronGrantPermissions:"Concedi l'autorizzazione ad usare telecamera e microfono",firefoxGrantPermissions:"Seleziona condividi i dispositivi selezionati quando richiesto dal browser.",iexplorerGrantPermissions:"Seleziona OK quando richiesto dal browser.",nwjsGrantPermissions:"Concedi l'autorizzazione ad usare telecamera e microfono",operaGrantPermissions:"Seleziona consenti quando richiesto dal browser.","react-nativeGrantPermissions":"Seleziona consenti quando richiesto dal browser.",safariGrantPermissions:"Seleziona OK quando richiesto dal browser."},videoSIPGW:{busy:"Stiamo lavorando per liberare le risorse. Riprova tra qualche minuto.",busyTitle:"Il servizio Stanza al momento \xe8 occupato",errorAlreadyInvited:"{{displayName}} gi\xe0 invitato",errorInvite:"Conferenza non ancora stabilita. Riprova pi\xf9 tardi.",errorInviteFailed:"Stiamo lavorando per risolvere il problema. Riprova pi\xf9 tardi.",errorInviteFailedTitle:"Invito a {{displayName}} fallito",errorInviteTitle:"Errore nell'invito alla stanza",pending:"{{displayName}} \xe8 stato invitato"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Modalita' solo audio attiva. Questa modalit\xe0 permette di rispamiare banda ma non vedrai gli altri partecipanti.",callQuality:"",hd:"HD",highDefinition:"Alta definizione",labelTooiltipNoVideo:"Nessun video",labelTooltipAudioOnly:"Modalit\xe0 solo audio abilitata",ld:"LD",lowDefinition:"Bassa definizione",onlyAudioAvailable:"\xc8 disponibile solo l'audio",onlyAudioSupported:"Per questo browser \xe8 supportato solo l'audio.",p2pEnabled:"Peer to Peer abilitato",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Preferisci alta risoluzione.",sd:"SD",standardDefinition:"Definizione standard"},videothumbnail:{domute:"Disattiva audio",flip:"Rifletti",kick:"Espelli",moderator:"Moderatore",mute:"Il partecipante \xe8 in muto",muted:"Audio disattivato",remoteControl:"Controllo remoto",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Tap per accedere",roomname:"Inserisci Nome Stanza"},appDescription:"Via avanti, video chatta con l'intero team. In effetti, invita tutti quelli che conosci. {{app}} \xe8 una soluzione di video conference totalmente crittografata, 100% open cource, che puoi utilizzare tutto il giorno, ogni giorno, gratuitamente - senza bisogno di un account.",audioVideoSwitch:{audio:"Voce",video:"Video"},calendar:"Calendario",connectCalendarButton:"Collega calendario",connectCalendarText:"",enterRoomTitle:"Avvia una nuova conferenza",go:"VAI",join:"UNISCITI",info:"",privacy:"Privacy",recentList:"Recente",recentListDelete:"Cancella",recentListEmpty:"La tua lista è vuota. Chatta con qualcuno del tuo team e lo vedrai apparire nella lista di meeting recenti.",reducedUIText:"",roomname:"Inserisci Nome Stanza",roomnameHint:"Inserisci il nome o l'URL della stanza alla quale vuoi accedere. Puoi anche inventarti un nome, assicurati solo che le persone che vuoi contattare lo sappiano, cos\xec che possano inserire lo stesso nome.",sendFeedback:"Invia feedback",terms:"Termini di utilizzo",title:"Il sistema di conferenza sicuro, funzionale e completamente gratuito."}}},662,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"\u82f1\u8a9e",af:"\u30a2\u30d5\u30ea\u30ab\u30fc\u30f3\u30b9\u8a9e",az:"\u30a2\u30bc\u30eb\u30d0\u30a4\u30b8\u30e3\u30f3\u8a9e",bg:"\u30d6\u30eb\u30ac\u30ea\u30a2\u8a9e",cs:"\u30c1\u30a7\u30b3\u8a9e",de:"\u30c9\u30a4\u30c4\u8a9e",el:"\u30ae\u30ea\u30b7\u30a2\u8a9e",eo:"\u30a8\u30b9\u30da\u30e9\u30f3\u30c8\u8a9e",es:"\u30b9\u30da\u30a4\u30f3\u8a9e",fr:"\u30d5\u30e9\u30f3\u30b9\u8a9e",hy:"\u30a2\u30eb\u30e1\u30cb\u30a2\u8a9e",it:"\u30a4\u30bf\u30ea\u30a2\u8a9e",ja:"\u65e5\u672c\u8a9e",ko:"\u97d3\u56fd\u8a9e",nb:"\u30ce\u30eb\u30a6\u30a7\u30fc\u8a9e (\u30d6\u30fc\u30af\u30e2\u30fc\u30eb)",oc:"\u30aa\u30c3\u30af\u8a9e",pl:"\u30dd\u30fc\u30e9\u30f3\u30c9\u8a9e",ptBR:"\u30dd\u30eb\u30c8\u30ac\u30eb\u8a9e (\u30d6\u30e9\u30b8\u30eb)",ru:"\u30ed\u30b7\u30a2\u8a9e",sk:"\u30b9\u30ed\u30d0\u30ad\u30a2\u8a9e",sl:"\u30b9\u30ed\u30d9\u30cb\u30a2\u8a9e",sv:"\u30b9\u30a6\u30a7\u30fc\u30c7\u30f3\u8a9e",tr:"\u30c8\u30eb\u30b3\u8a9e",vi:"\u30d9\u30c8\u30ca\u30e0\u8a9e",zhCN:"\u4e2d\u56fd\u8a9e (\u4e2d\u56fd)"}},663,[]); -__d(function(e,o,i,t,r,n,a){r.exports={addPeople:{add:"\u62db\u5f85",countryNotSupported:"\u79c1\u305f\u3061\u306f\u3053\u306e\u5b9b\u5148\u3092\u307e\u3060\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002",countryReminder:"\u7c73\u56fd\u5916\u306b\u30b3\u30fc\u30eb\u3057\u307e\u3059\u304b\uff1f \u56fd\u30b3\u30fc\u30c9\u3067\u59cb\u307e\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\uff01",disabled:"\u4eba\u3092\u62db\u5f85\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002",failedToAdd:"",footerText:"\u30c0\u30a4\u30e4\u30eb\u30a2\u30a6\u30c8\u304c\u7121\u52b9\u3057\u3066\u3044\u307e\u3059\u3002",loading:"\u4eba\u3068\u96fb\u8a71\u756a\u53f7\u3092\u691c\u7d22\u3059\u308b",loadingNumber:"\u96fb\u8a71\u756a\u53f7\u3092\u691c\u8a3c\u3057\u3066\u3044\u307e\u3059",loadingPeople:"\u62db\u5f85\u3059\u308b\u4eba\u3092\u691c\u7d22\u3059\u308b",noResults:"\u4e00\u81f4\u3059\u308b\u691c\u7d22\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093",noValidNumbers:"\u96fb\u8a71\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044",searchNumbers:"\u96fb\u8a71\u756a\u53f7\u3092\u8ffd\u52a0",searchPeople:"\u4eba\u3092\u691c\u7d22\u3059\u308b",searchPeopleAndNumbers:"\u4eba\u3092\u691c\u7d22\u3059\u308b\u304b\u3001\u96fb\u8a71\u756a\u53f7\u3092\u8ffd\u52a0\u3059\u308b",telephone:"\u96fb\u8a71\uff1a {{number}}",title:"\u3053\u306e\u4f1a\u8b70\u306b\u4eba\u3092\u62db\u5f85\u3059\u308b"},"\x05addPeople":{},audioDevices:{bluetooth:"Bluetooth",headphones:"\u30d8\u30c3\u30c9\u30d5\u30a9\u30f3",phone:"\u96fb\u8a71",speaker:"\u30b9\u30d4\u30fc\u30ab\u30fc"},"\x05audioDevices":{},audioOnly:{audioOnly:"\u30aa\u30fc\u30c7\u30a3\u30aa\u306e\u307f"},calendarSync:{addMeetingURL:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u30ea\u30f3\u30af\u3092\u8ffd\u52a0\u3059\u308b",confirmAddLink:"\u3053\u306e\u30a4\u30d9\u30f3\u30c8\u306b\u30d3\u30c7\u30aa\u4f1a\u8b70\u30ea\u30f3\u30af\u3092\u5f35\u308a\u4ed8\u3051\u307e\u3059\u304b\u3002",error:{appConfiguration:"\u30ab\u30ec\u30f3\u30c0\u30fc\u6a5f\u80fd\u304c\u6b63\u3057\u304f\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044",generic:"\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u305f\u3002\u30ab\u30ec\u30f3\u30c0\u30fc\u6a5f\u80fd\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3001\u3082\u3057\u304f\u306f\u30ab\u30ec\u30f3\u30c0\u30fc\u3092\u66f4\u65b0\u3057\u3066\u304f\u3060\u3055\u3044",notSignedIn:"\u8a8d\u8a3c\u4e2d\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30ab\u30ec\u30f3\u30c0\u30fc\u306e\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3001\u30ed\u30b0\u30a4\u30f3\u3057\u76f4\u3057\u3066\u304f\u3060\u3055\u3044\u3002"},join:"\u53c2\u52a0",joinTooltip:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u53c2\u52a0\u3059\u308b",nextMeeting:"\u6b21\u306e\u4f1a\u8b70",noEvents:"",ongoingMeeting:"\u4f1a\u8b70\u4e2d",permissionButton:"\u8a2d\u5b9a\u3092\u958b\u304f",permissionMessage:"\u30a2\u30d7\u30ea\u306b\u4f1a\u8b70\u3092\u8868\u793a\u3059\u308b\u306b\u306f\u3001\u30ab\u30ec\u30f3\u30c0\u30fc\u306e\u30a2\u30af\u30bb\u30b9\u8a31\u53ef\u304c\u5fc5\u8981\u3067\u3059\u3002",refresh:"\u30ab\u30ec\u30f3\u30c0\u30fc\u3092\u66f4\u65b0\u3059\u308b",today:"\u4eca\u65e5"},"\x05calendarSync":{},chat:{error:"\u30a8\u30e9\u30fc: \"{{originalText}}\" \u304c\u307e\u3060\u9001\u4fe1\u3055\u308c\u3066\u3044\u307e\u305b\u3093. \u30a8\u30e9\u30fc\uff1a {{error}}",messagebox:"\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u66f8\u3044\u3066\u304f\u3060\u3055\u3044",nickname:{popover:"\u30cb\u30c3\u30af\u30cd\u30fc\u30e0\u3092\u5165\u529b",title:"\u30c1\u30e3\u30c3\u30c8\u306e\u30cb\u30c3\u30af\u30cd\u30fc\u30e0\u3092\u8a18\u5165\u3057\u3066\u304f\u3060\u3055\u3044"},title:"\u30c1\u30e3\u30c3\u30c8"},connectingOverlay:{joiningRoom:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u53c2\u52a0\u3057\u3066\u3044\u307e\u3059\u3002\u3002\u3002"},connection:{ATTACHED:"\u6dfb\u4ed8\u3055\u308c\u305f",AUTHENTICATING:"\u8a8d\u8a3c\u4e2d",AUTHFAIL:"\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f",CONNECTED:"\u63a5\u7d9a\u3055\u308c\u307e\u3057\u305f",CONNECTING:"\u63a5\u7d9a\u4e2d",CONNFAIL:"\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f",DISCONNECTED:"\u5207\u65ad\u3055\u308c\u307e\u3057\u305f",DISCONNECTING:"\u5207\u65ad\u3057\u3066\u3044\u307e\u3059",ERROR:"\u30a8\u30e9\u30fc",RECONNECTING:"\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u518d\u63a5\u7d9a\u4e2d..."},connectionindicator:{address:"\u30a2\u30c9\u30ec\u30b9\uff1a",bandwidth:"\u63a8\u5b9a\u30d0\u30f3\u30c9\u30ef\u30a4\u30ba",bitrate:"\u30d3\u30c3\u30c8\u30ec\u30fc\u30c8:",bridgeCount:"\u30b5\u30fc\u30d0\u30fc\u6570\uff1a",connectedTo:"\u63a5\u7d9a\u5148\uff1a",framerate:"\u30d5\u30ec\u30fc\u30e0\u30ec\u30fc\u30c8:",less:"\u5c11\u306a\u304f\u898b\u308b",localaddress:"",localport:"",more:"\u591a\u304f\u898b\u308b",packetloss:"\u30d1\u30b1\u30c3\u30c8\u30ed\u30b9:",quality:{good:"\u826f\u3044",inactive:"\u4f11\u6b62\u4e2d",lost:"\u63a5\u7d9a\u5207\u308c\u305f",nonoptimal:"\u6700\u9069\u3067\u306f\u306a\u3044",poor:"\u60aa\u3044"},remoteaddress:"\u30ea\u30e2\u30fc\u30c8\u30a2\u30c9\u30ec\u30b9\uff1a",remoteport:"\u30ea\u30e2\u30fc\u30c8\u30dd\u30fc\u30c8\uff1a",resolution:"\u89e3\u50cf\u5ea6:",status:"\u63a5\u7d9a\u72b6\u614b:",transport:"\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\uff1a",turn:""},dateUtils:{earlier:"\u305d\u306e\u524d",today:"\u4eca\u65e5",yesterday:"\u6628\u65e5"},deepLinking:{appNotInstalled:"\u3053\u306e\u4f1a\u8b70\u306b\u53c2\u52a0\u3059\u308b\u306b\u306f\u3001{{app}} \u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u304c\u5fc5\u8981\u3067\u3059\u3002",description:"\u4f55\u3082\u8d77\u3053\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u304b\uff1f {{app}} \u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u30a2\u30d7\u30ea\u3067\u4f1a\u8b70\u3092\u958b\u59cb\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u3002 \u518d\u5ea6\u8a66\u3057\u3066\u307f\u308b\u304b\u3001{{app}} Web\u30a2\u30d7\u30ea\u3067\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002",descriptionWithoutWeb:"",downloadApp:"\u30a2\u30d7\u30ea\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b",launchWebButton:"Web\u3067\u8d77\u52d5\u3059\u308b",openApp:"\u30a2\u30d7\u30ea\u3067\u7d9a\u304f",title:"{{app}} \u3067\u4f1a\u8b70\u3092\u958b\u59cb\u3059\u308b...",tryAgainButton:"\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044"},defaultLink:"\u4f8b\uff1a {{url}}",deviceError:{cameraError:"\u30ab\u30e1\u30e9\u3078\u306e\u30a2\u30af\u30bb\u30b9\u306b\u5931\u6557\u3057\u307e\u3057\u305f",cameraPermission:"\u30ab\u30e1\u30e9\u306e\u6a29\u9650\u3092\u53d6\u5f97\u3059\u308b\u969b\u306b\u30a8\u30e9\u30fc",microphoneError:"\u30de\u30a4\u30af\u3078\u306e\u30a2\u30af\u30bb\u30b9\u306b\u5931\u6557\u3057\u307e\u3057\u305f",microphonePermission:"\u30de\u30a4\u30af\u306e\u6a29\u9650\u3092\u53d6\u5f97\u3059\u308b\u969b\u306b\u30a8\u30e9\u30fc"},deviceSelection:{noPermission:"\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093",previewUnavailable:"\u30d7\u30ec\u30d3\u30e5\u30fc\u3067\u304d\u307e\u305b\u3093",selectADevice:"\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e",testAudio:""},dialog:{accessibilityLabel:{liveStreaming:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\uff1a"},allow:"\u8a31\u53ef",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\u4e00\u5ea6\u306b1\u3064\u306e\u5171\u6709\u30d3\u30c7\u30aa\u306e\u307f\u304c\u8a31\u53ef\u3055\u308c\u307e\u3059",applicationWindow:"\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a6\u30a3\u30f3\u30c9\u30a6",Back:"\u623b\u308b",cameraConstraintFailedError:"\u3042\u306a\u305f\u306e\u30ab\u30e1\u30e9\u306f\u3001\u5fc5\u8981\u306a\u5236\u7d04\u306e\u3044\u304f\u3064\u304b\u3092\u6e80\u305f\u3057\u3066\u3044\u307e\u305b\u3093\u3002",cameraNotFoundError:"\u30ab\u30e1\u30e9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002",cameraNotSendingData:"\u3042\u306a\u305f\u306e\u30ab\u30e1\u30e9\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3002 \u4ed6\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u3057\u3001\u8a2d\u5b9a\u30e1\u30cb\u30e5\u30fc\u304b\u3089\u5225\u306e\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b\u304b\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30ea\u30ed\u30fc\u30c9\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002",cameraNotSendingDataTitle:"\u30ab\u30e1\u30e9\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093",cameraPermissionDeniedError:"\u3042\u306a\u305f\u306f\u30ab\u30e1\u30e9\u3092\u4f7f\u7528\u3059\u308b\u8a31\u53ef\u3092\u4e0e\u3048\u3066\u3044\u307e\u305b\u3093\u3002 \u3042\u306a\u305f\u306f\u307e\u3060\u4f1a\u8b70\u306b\u53c2\u52a0\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u304c\u3001\u4ed6\u306e\u53c2\u52a0\u8005\u306f\u3042\u306a\u305f\u3092\u898b\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002 \u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001\u30a2\u30c9\u30ec\u30b9\u30d0\u30fc\u306e\u30ab\u30e1\u30e9\u30dc\u30bf\u30f3\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002",cameraUnknownError:"\u4e0d\u660e\u306a\u7406\u7531\u3067\u30ab\u30e1\u30e9\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002",cameraUnsupportedResolutionError:"\u304a\u4f7f\u3044\u306e\u30ab\u30e1\u30e9\u306f\u3001\u5fc5\u8981\u306a\u30d3\u30c7\u30aa\u89e3\u50cf\u5ea6\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002",Cancel:"\u30ad\u30e3\u30f3\u30bb\u30eb",close:"\u9589\u3058\u308b",conferenceDisconnectMsg:"\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u63a5\u7d9a\u3092\u78ba\u8a8d\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 {{seconds}} \u79d2\u3067\u518d\u63a5\u7d9a\u3057\u307e\u3059...",conferenceDisconnectTitle:"\u3042\u306a\u305f\u306f\u5207\u65ad\u3055\u308c\u307e\u3057\u305f\u3002",conferenceReloadMsg:"\u79c1\u305f\u3061\u306f\u3053\u308c\u3092\u89e3\u6c7a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002 {{seconds}} \u79d2\u3067\u518d\u63a5\u7d9a\u3057\u307e\u3059...",conferenceReloadTitle:"\u6b8b\u5ff5\u306a\u304c\u3089\u3001\u4f55\u304b\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3057\u305f\u3002",confirm:"\u78ba\u8a8d",confirmNo:"\u3044\u3044\u3048",confirmYes:"\u306f\u3044",connectError:"Oops! \u4f55\u304b\u304c\u3046\u307e\u304f\u3044\u304b\u305a\u3001\u4f1a\u8b70\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002",connectErrorWithMsg:"Oops! \u4f55\u304b\u554f\u984c\u304c\u767a\u751f\u3057\u3001\u4f1a\u8b70\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a {{msg}}",connecting:"\u63a5\u7d9a\u4e2d",contactSupport:"\u30b5\u30dd\u30fc\u30c8\u554f\u3044\u5408\u308f\u305b\u5148",copy:"\u30b3\u30d4\u30fc",dismiss:"\u5374\u4e0b",displayNameRequired:"",done:"\u5b8c\u4e86",enterDisplayName:"",error:"\u30a8\u30e9\u30fc",externalInstallationMsg:"\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u5171\u6709\u62e1\u5f35\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002",externalInstallationTitle:"\u62e1\u5f35\u6a5f\u80fd\u304c\u5fc5\u8981\u3067\u3059",goToStore:"\u30a6\u30a7\u30d6\u30b9\u30c8\u30a2\u306b\u884c\u304f",gracefulShutdown:"\u73fe\u5728\u3001\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u306e\u305f\u3081\u30b5\u30fc\u30d3\u30b9\u304c\u505c\u6b62\u3057\u3066\u3044\u307e\u3059\u3002 \u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",IamHost:"\u79c1\u306f\u30db\u30b9\u30c8\u3067\u3059",incorrectRoomLockPassword:"",incorrectPassword:"\u30e6\u30fc\u30b6\u30fc\u30cd\u30fc\u30e0\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9055\u3044\u307e\u3059",inlineInstallationMsg:"\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u5171\u6709\u62e1\u5f35\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002",inlineInstallExtension:"\u4eca\u3059\u3050\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb",internalError:"Oops! \u4f55\u304b\u306e\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u6b21\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a{{error}}",internalErrorTitle:"\u5185\u90e8\u30a8\u30e9\u30fc",kickMessage:"",kickParticipantButton:"\u8ffd\u3044\u51fa\u3059",kickParticipantDialog:"\u3053\u306e\u53c2\u52a0\u8005\u3092\u8ffd\u3044\u51fa\u3057\u307e\u3059\u304b",kickParticipantTitle:"",kickTitle:"",liveStreaming:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0",liveStreamingDisabledForGuestTooltip:"\u30b2\u30b9\u30c8\u304c\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u304c\u958b\u59cb\u3067\u304d\u307e\u305b\u3093",liveStreamingDisabledTooltip:"",lockMessage:"\u4f1a\u8b70\u3092\u30ed\u30c3\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002",lockRoom:"",lockTitle:"\u30ed\u30c3\u30af\u306b\u5931\u6557\u3057\u307e\u3057\u305f",logoutQuestion:"\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u3066\u4f1a\u8b70\u3092\u505c\u6b62\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f",logoutTitle:"\u30ed\u30b0\u30a2\u30a6\u30c8",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\u3042\u306a\u305f\u306e\u30de\u30a4\u30af\u30ed\u30d5\u30a9\u30f3\u306f\u3001\u5fc5\u8981\u306a\u5236\u7d04\u306e\u3044\u304f\u3064\u304b\u3092\u6e80\u305f\u3057\u3066\u3044\u307e\u305b\u3093\u3002",micNotFoundError:"\u30de\u30a4\u30af\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\u30de\u30a4\u30af\u3092\u4f7f\u7528\u3059\u308b\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093\u3002 \u3042\u306a\u305f\u306f\u307e\u3060\u4f1a\u8b70\u306b\u53c2\u52a0\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u304c\u3001\u4ed6\u306e\u4eba\u306f\u3042\u306a\u305f\u306e\u58f0\u3092\u805e\u3051\u307e\u305b\u3093\u3002 \u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001\u30a2\u30c9\u30ec\u30b9\u30d0\u30fc\u306e\u30ab\u30e1\u30e9\u30dc\u30bf\u30f3\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002",micUnknownError:"\u4e0d\u660e\u306a\u7406\u7531\u3067\u30de\u30a4\u30af\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002",muteParticipantBody:"\u3042\u306a\u305f\u306f\u305d\u306e\u30df\u30e5\u30fc\u30c8\u3092\u89e3\u9664\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u304c\u3001\u5f7c\u3089\u306f\u3044\u3064\u3067\u3082\u81ea\u5206\u81ea\u8eab\u306e\u30df\u30e5\u30fc\u30c8\u3092\u89e3\u9664\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002",muteParticipantButton:"\u30df\u30e5\u30fc\u30c8",muteParticipantDialog:"\u3053\u306e\u53c2\u52a0\u8005\u3092\u30df\u30e5\u30fc\u30c8\u3057\u307e\u3059\u304b\u3002\u3042\u306a\u305f\u304c\u53d6\u308a\u623b\u305b\u307e\u305b\u3093\u304c\u3001\u672c\u4eba\u304c\u81ea\u5206\u3067\u3044\u3064\u3067\u3082\u89e3\u9664\u3067\u3044\u307e\u3059\u3002",muteParticipantTitle:"",Ok:"Ok",passwordLabel:"",passwordNotSupported:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u8a2d\u5b9a\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\u3042\u306a\u305f\u306e\u30d6\u30e9\u30a6\u30b6\u306f\u3053\u306e\u30b5\u30a4\u30c8\u304b\u3089\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u30d6\u30ed\u30c3\u30af\u3057\u3066\u3044\u307e\u3059\u3002 \u30d6\u30e9\u30a6\u30b6\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u8a2d\u5b9a\u3067\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3092\u6709\u52b9\u306b\u3057\u3066\u304b\u3089\u3001\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",popupErrorTitle:"\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u304c\u30d6\u30ed\u30c3\u30af\u3055\u308c\u307e\u3057\u305f",recording:"\u9332\u753b",recordingDisabledForGuestTooltip:"\u30b2\u30b9\u30c8\u304c\u9332\u753b\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3002",recordingDisabledTooltip:"",rejoinNow:"\u4eca\u3059\u3050\u518d\u53c2\u52a0",remoteControlAllowedMessage:"{{user}} \u306f\u3042\u306a\u305f\u306e\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u8981\u6c42\u3092\u53d7\u3051\u5165\u308c\u307e\u3057\u305f\uff01",remoteControlDeniedMessage:"{{user}} \u306f\u3042\u306a\u305f\u306e\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u8981\u6c42\u3092\u62d2\u5426\u3057\u307e\u3057\u305f\uff01",remoteControlErrorMessage:"\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u306e\u30a2\u30af\u30bb\u30b9\u8a31\u53ef\u3092 {{user}} \u304b\u3089\u8981\u6c42\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3068\u304d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff01",remoteControlRequestMessage:"{{user}} \u306b\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u306e\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u3092\u8a31\u53ef\u3057\u307e\u3059\u304b\uff1f",remoteControlShareScreenWarning:"\u300c\u8a31\u53ef\u300d\u3092\u62bc\u3059\u3068\u753b\u9762\u3092\u5171\u6709\u3059\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\uff01",remoteControlStopMessage:"\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u7d42\u4e86\u3057\u307e\u3057\u305f\uff01",remoteControlTitle:"\u30ea\u30e2\u30fc\u30c8\u30c7\u30b9\u30af\u30c8\u30c3\u30d7\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb",Remove:"\u9664\u53bb",removePassword:"",removeSharedVideoMsg:"\u5171\u6709\u30d3\u30c7\u30aa\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f",removeSharedVideoTitle:"\u5171\u6709\u30d3\u30c7\u30aa\u3092\u524a\u9664\u3059\u308b",reservationError:"\u4e88\u7d04\u30b7\u30b9\u30c6\u30e0\u30a8\u30e9\u30fc",reservationErrorMsg:"\u30a8\u30e9\u30fc\u30b3\u30fc\u30c9: {{code}}, \u30e1\u30c3\u30bb\u30fc\u30b8: {{msg}}",retry:"\u518d\u8a66\u884c",screenSharingFailedToInstall:"Oops! \u753b\u9762\u5171\u6709\u62e1\u5f35\u6a5f\u80fd\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002",screenSharingFailedToInstallTitle:"\u753b\u9762\u5171\u6709\u62e1\u5f35\u6a5f\u80fd\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u5931\u6557\u3057\u307e\u3057\u305f",screenSharingFirefoxPermissionDeniedError:"",screenSharingFirefoxPermissionDeniedTitle:"Oops! \u753b\u9762\u5171\u6709\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff01",screenSharingPermissionDeniedError:"Oops! \u753b\u9762\u5171\u6709\u306e\u62e1\u5f35\u30a2\u30af\u30bb\u30b9\u8a31\u53ef\u3067\u4f55\u304b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u518d\u8aad\u307f\u8fbc\u307f\u3057\u3066\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",serviceUnavailable:"\u30b5\u30fc\u30d3\u30b9\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093",sessTerminated:"\u901a\u8a71\u7d42\u4e86",Share:"\u5171\u6709",shareVideoLinkError:"\u6b63\u3057\u3044YouTube\u306e\u30ea\u30f3\u30af\u3092\u63d0\u4f9b\u3057\u3066\u304f\u3060\u3055\u3044\u3002",shareVideoTitle:"\u52d5\u753b\u3092\u5171\u6709\u3059\u308b",shareYourScreen:"\u753b\u9762\u3092\u5171\u6709\u3059\u308b",shareYourScreenDisabled:"",shareYourScreenDisabledForGuest:"",startLiveStreaming:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u958b\u59cb\u3059\u308b",startRecording:"\u9332\u753b\u3092\u958b\u59cb",startRemoteControlErrorMessage:"\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u958b\u59cb\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002",stopLiveStreaming:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u3092\u505c\u6b62\u3059\u308b",stopRecording:"\u9332\u753b\u3092\u505c\u6b62\u3059\u308b",stopRecordingWarning:"\u9332\u753b\u3092\u505c\u6b62\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f",stopStreamingWarning:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u3092\u505c\u6b62\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f",streamKey:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u30ad\u30fc",Submit:"\u6295\u7a3f",thankYou:"{{appName}} \u3092\u4f7f\u7528\u3057\u3066\u3044\u305f\u3060\u304d\u3042\u308a\u304c\u3068\u3046\u3054\u3056\u3044\u307e\u3059\uff01",token:"\u30c8\u30fc\u30af\u30f3",tokenAuthFailed:"\u7533\u3057\u8a33\u3042\u308a\u307e\u305b\u3093\u304c\u3001\u3053\u306e\u901a\u8a71\u306b\u53c2\u52a0\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002",tokenAuthFailedTitle:"\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f",transcribing:"\u66f8\u304d\u5199\u3057",unlockRoom:"",userPassword:"\u30e6\u30fc\u30b6\u30fc\u306e\u30d1\u30b9\u30ef\u30fc\u30c9",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"\u30db\u30b9\u30c8\u3092\u5f85\u3063\u3066\u3044\u307e\u3059...",Yes:"\u306f\u3044",yourEntireScreen:"\u3042\u306a\u305f\u306e\u753b\u9762\u5168\u4f53"},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:"\u306f\u73fe\u5728 {{status}} \u3067\u3059"},feedback:{average:"\u666e\u901a",bad:"\u60aa\u3044",detailsLabel:"\u305d\u308c\u306b\u3064\u3044\u3066\u3082\u3063\u3068\u6559\u3048\u3066\u304f\u3060\u3055\u3044\u3002",good:"\u826f\u3044",rateExperience:"\u30d3\u30c7\u30aa\u901a\u8a71\u3092\u8a55\u4fa1\u3057\u3066\u304f\u3060\u3055\u3044",veryBad:"\u3068\u3066\u3082\u60aa\u3044",veryGood:"\u3068\u3066\u3082\u826f\u3044"},incomingCall:{answer:"",audioCallTitle:"",decline:"\u5374\u4e0b",productLabel:"",videoCallTitle:"\u30d3\u30c7\u30aa\u901a\u8a71\u7740\u4fe1"},info:{accessibilityLabel:"\u60c5\u5831\u3092\u8868\u793a\u3059\u308b",addPassword:"",cancelPassword:"",conferenceURL:"\u30ea\u30f3\u30af\uff1a",country:"\u56fd",dialANumber:"",dialInConferenceID:"PIN:",dialInNotSupported:"\u7533\u3057\u8a33\u3042\u308a\u307e\u305b\u3093\u304c\u3001\u73fe\u5728\u30c0\u30a4\u30e4\u30eb\u30a4\u30f3\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002",dialInNumber:"\u30c0\u30a4\u30a2\u30eb\u30a4\u30f3\uff1a",dialInSummaryError:"",dialInTollFree:"",genericError:"\u304a\u3063\u3068\u3001\u4f55\u304b\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3057\u305f\u3002",inviteLiveStream:"\u3053\u306e\u4f1a\u8b70\u306e\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u8868\u793a\u3059\u308b\u306b\u306f\u3001\u3053\u306e\u30ea\u30f3\u30af\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\uff1a{{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\uff1a",moreNumbers:"\u305d\u306e\u4ed6\u306e\u756a\u53f7",noNumbers:"\u30c0\u30a4\u30e4\u30eb\u30a4\u30f3\u756a\u53f7\u306f\u3042\u308a\u307e\u305b\u3093\u3002",noPassword:"\u306a\u3057",noRoom:"\u30c0\u30a4\u30e4\u30eb\u30a4\u30f3\u3059\u308b\u90e8\u5c4b\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3067\u3057\u305f\u3002",numbers:"\u30c0\u30a4\u30e4\u30eb\u30a4\u30f3\u756a\u53f7",password:"",title:"\u5171\u6709",tooltip:"\u3053\u306e\u4f1a\u8b70\u306e\u30ea\u30f3\u30af\u3068\u30c0\u30a4\u30e4\u30eb\u30a4\u30f3\u60c5\u5831\u3092\u5171\u6709\u3059\u308b",label:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u60c5\u5831"},"\x05info":{},inviteDialog:{alertText:"",header:"\u62db\u5f85",searchCallOnlyPlaceholder:"\u643a\u5e2f\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044",searchPeopleOnlyPlaceholder:"\u53c2\u52a0\u8005\u3092\u691c\u7d22\u3059\u308b",searchPlaceholder:"\u30cb\u30c3\u30af\u30cd\u30fc\u30e0\u3001\u307e\u305f\u306f\u96fb\u8a71\u756a\u53f7",send:"\u9001\u4fe1"},inlineDialogFailure:{msg:"\u79c1\u305f\u3061\u306f\u5c11\u3057\u3064\u307e\u305a\u304d\u307e\u3057\u305f\u3002",retry:"\u518d\u8a66\u884c\u3059\u308b",support:"\u30b5\u30dd\u30fc\u30c8",supportMsg:"\u3053\u308c\u304c\u8d77\u3053\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001"},keyboardShortcuts:{focusLocal:"\u81ea\u5206\u306e\u30d3\u30c7\u30aa\u306b\u7126\u70b9",focusRemote:"\u4ed6\u306e\u30e1\u30f3\u30d0\u30fc\u306e\u30d3\u30c7\u30aa\u306b\u7126\u70b9",fullScreen:"\u5168\u753b\u9762\u8868\u793a/\u7d42\u4e86",keyboardShortcuts:"\u30ad\u30fc\u30dc\u30fc\u30c9\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8",localRecording:"\u30ed\u30fc\u30ab\u30eb\u9332\u753b\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u306e\u8868\u793a\uff0f\u975e\u8868\u793a",mute:"\u30de\u30a4\u30af\u306e\u6d88\u97f3 ( \u30df\u30e5\u30fc\u30c8 )",pushToTalk:"\u8a71\u3059\u305f\u3081\u306b\u62bc\u3059",raiseHand:"\u624b\u3092\u4e0a\u3052\u308b/\u4e0b\u3052\u308b",showSpeakerStats:"\u6f14\u8aac\u8005\u306e\u30c7\u30fc\u30bf\u3092\u8868\u793a",toggleChat:"\u30c1\u30e3\u30c3\u30c8\u3092\u8868\u793a/\u975e\u8868\u793a",toggleFilmstrip:"\u52d5\u753b\u30b5\u30e0\u30cd\u30a4\u30eb\u8868\u793a/\u975e\u8868\u793a",toggleScreensharing:"\u30ab\u30e1\u30e9\u3068\u753b\u9762\u5171\u6709\u3092\u5207\u308a\u66ff\u3048\u308b",toggleShortcuts:"\u30ad\u30fc\u30dc\u30fc\u30c9 \u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u3092\u8868\u793a",videoMute:"\u30ab\u30e1\u30e9\u3092\u6709\u52b9/\u7121\u52b9"},"\x05keyboardShortcuts":{},liveStreaming:{busy:"\u79c1\u305f\u3061\u306f\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u30ea\u30bd\u30fc\u30b9\u3092\u89e3\u653e\u3059\u308b\u305f\u3081\u306b\u53d6\u308a\u7d44\u3093\u3067\u3044\u307e\u3059\u3002 \u6570\u5206\u5f8c\u306b\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",busyTitle:"\u3059\u3079\u3066\u306e\u30b9\u30c8\u30ea\u30fc\u30de\u30fc\u306f\u73fe\u5728\u30d3\u30b8\u30fc\u72b6\u614b\u3067\u3059",changeSignIn:"\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u5207\u308a\u66ff\u3048\u307e\u3059\u3002",choose:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044",chooseCTA:"\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u307e\u3059\u3002 \u3042\u306a\u305f\u306f\u73fe\u5728\u3001{{email}} \u3068\u3057\u3066\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u3044\u307e\u3059\u3002",enterStreamKey:"YouTube\u306e\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u30ad\u30fc\u3092\u3053\u3053\u306b\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002",error:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 \u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",errorAPI:"YouTube\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8\u306b\u30a2\u30af\u30bb\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u3082\u3046\u4e00\u5ea6\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\u3002",errorLiveStreamNotEnabled:"",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u306e\u958b\u59cb\u306b\u5931\u6557\u3057\u307e\u3057\u305f",getStreamKeyManually:"",invalidStreamKey:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u30ad\u30fc\u304c\u9593\u306b\u5408\u3044\u307e\u3057\u305f\u3002",off:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u304c\u505c\u6b62\u3057\u307e\u3057\u305f",on:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0",pending:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u958b\u59cb\u3057\u3066\u3044\u307e\u3059...",serviceName:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u30b5\u30fc\u30d3\u30b9",signedInAs:"",signIn:"Google\u3067\u30ed\u30b0\u30a4\u30f3",signInCTA:"\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u304b\u3001YouTube\u306e\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u30ad\u30fc\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002",signOut:"\u30b5\u30a4\u30f3\u30a2\u30a6\u30c8",start:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u958b\u59cb\u3059\u308b",streamIdHelp:"\u3053\u308c\u306f\u4f55\u3067\u3059\u304b\uff1f",unavailableTitle:"\u30e9\u30a4\u30d6\u30b9\u30c8\u30ea\u30fc\u30df\u30f3\u30b0\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093"},"\x05liveStreaming":{},localRecording:{clientState:{off:"\u30aa\u30d5",on:"\u30aa\u30f3",unknown:"\u4e0d\u660e"},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"LOR",labelToolTip:"\u30ed\u30fc\u30ab\u30eb\u30ec\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u5b9f\u65bd\u4e2d",localRecording:"",me:"\u79c1",messages:{engaged:"\u30ed\u30fc\u30ab\u30eb\u30ec\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u5b9f\u65bd",finished:"",finishedModerator:"",notModerator:""},moderator:"\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc",no:"\u3044\u3044\u3048",participant:"",participantStats:"",sessionToken:"",start:"\u9332\u753b\u3092\u958b\u59cb",stop:"\u9332\u753b\u3092\u505c\u6b62\u3059\u308b",yes:"\u306f\u3044"},"\x05localRecording":{},lockRoomPassword:"\u30d1\u30b9\u30ef\u30fc\u30c9",lockRoomPasswordUppercase:"\u30d1\u30b9\u30ef\u30fc\u30c9",me:"\u79c1",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"\u5207\u65ad\u3055\u308c\u307e\u3057\u305f",focus:"",focusFail:"",grantedTo:"{{to}} \u3078\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc\u306e\u6a29\u5229\u304c\u4ed8\u4e0e\u3055\u308c\u307e\u3057\u305f\uff01",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"\u79c1",moderator:"\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc\u306e\u6a29\u5229\u304c\u4ed8\u4e0e\u3055\u308c\u307e\u3057\u305f\uff01",muted:"$t(notify.somebody) \u3078\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc\u306e\u6a29\u5229\u304c\u4ed8\u4e0e\u3055\u308c\u307e\u3057\u305f\uff01",mutedTitle:"\u3042\u306a\u305f\u306f\u30df\u30e5\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\uff01",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"\u8ab0\u304b",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"",suboptimalExperienceTitle:"",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"powered by",presenceStatus:{busy:"",calling:"",connected:"\u63a5\u7d9a\u3055\u308c\u307e\u3057\u305f",connecting:"\u63a5\u7d9a\u4e2d",connecting2:"\u63a5\u7d9a\u4e2d",disconnected:"\u5207\u65ad\u3055\u308c\u307e\u3057\u305f",expired:"",ignored:"",initializingCall:"",invited:"\u62db\u5f85",rejected:"",ringing:"\u7740\u4fe1\u3057\u3066\u3044\u308b\u30fb\u30fb"},"\x05presenceStatus":{},profile:{setDisplayNameLabel:"\u8868\u793a\u540d\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044",setEmailInput:"\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044",setEmailLabel:"\u30e1\u30fc\u30eb\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044",title:"\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb"},recording:{authDropboxText:"Dropbox\u306b\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9",availableSpace:"",beta:"BETA",busy:"\u79c1\u305f\u3061\u306f\u3001\u9332\u753b\u30ea\u30bd\u30fc\u30b9\u306e\u89e3\u653e\u306b\u53d6\u308a\u7d44\u3093\u3067\u3044\u307e\u3059\u3002 \u6570\u5206\u5f8c\u306b\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",busyTitle:"\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c0\u30fc\u304c\u73fe\u5728\u30d3\u30b8\u30fc\u72b6\u614b\u3067\u3059",error:"\u9332\u753b\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 \u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",expandedOff:"\u9332\u753b\u304c\u505c\u6b62\u3057\u307e\u3057\u305f",expandedOn:"",expandedPending:"\u9332\u753b\u958b\u59cb\u3057\u3066\u3044\u308b\u3002\u3002\u3002",failedToStart:"\u9332\u753b\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f",fileSharingdescription:"\u9332\u753b\u30c7\u30fc\u30bf\u3092\u5171\u6709\u3059\u308b",live:"\u30e9\u30a4\u30d6",loggedIn:"",off:"\u9332\u753b\u304c\u505c\u6b62\u3057\u307e\u3057\u305f",on:"\u9332\u753b",pending:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u767b\u9332\u3092\u6e96\u5099\u3057\u3066\u3044\u307e\u3059\u3002\u3002\u3002",rec:"REC",serviceDescription:"\u30d3\u30c7\u30aa\u304c\u9332\u753b\u30b5\u30fc\u30d3\u30b9\u3067\u9332\u753b\u3055\u308c\u307e\u3059",serviceName:"\u8a18\u9332\u30b5\u30fc\u30d3\u30b9",signIn:"\u30b5\u30a4\u30f3\u30a4\u30f3",signOut:"\u30b5\u30a4\u30f3\u30a2\u30a6\u30c8",unavailable:"Oops! {{serviceName}} \u306f\u73fe\u5728\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002 \u79c1\u305f\u3061\u306f\u3053\u306e\u554f\u984c\u306e\u89e3\u6c7a\u306b\u53d6\u308a\u7d44\u3093\u3067\u3044\u307e\u3059\u3002 \u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",unavailableTitle:"\u9332\u753b\u3067\u304d\u307e\u305b\u3093"},"\x05recording":{},sectionList:{pullToRefresh:"\u30d7\u30eb\u3057\u3066\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5\u3059\u308b"},settings:{calendar:{about:"",disconnect:"\u5207\u65ad\u3055\u308c\u307e\u3057\u305f",microsoftSignIn:"\u30de\u30a4\u30af\u30ed\u30bd\u30d5\u30c8\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u30ed\u30b0\u30a4\u30f3",signedIn:"",title:"\u30ab\u30ec\u30f3\u30c0\u30fc"},devices:"\u7aef\u672b",followMe:"\u5168\u54e1\u30d5\u30a9\u30ed\u30fc\u30df\u30fc",language:"\u8a00\u8a9e",loggedIn:"",moderator:"\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc",more:"\u305d\u306e\u4ed6",name:"\u540d\u524d",noDevice:"\u306a\u3057",selectAudioOutput:"\u97f3\u58f0\u51fa\u529b",selectCamera:"\u30ab\u30e1\u30e9",selectMic:"\u30de\u30a4\u30af",startAudioMuted:"\u5168\u54e1\u30df\u30e5\u30fc\u30c8\u306b\u3059\u308b",startVideoMuted:"\u5168\u54e1\u975e\u8868\u793a\u306b\u3059\u308b",title:"\u8a2d\u5b9a"},"\x05settings":{calendar:{}},settingsView:{alertOk:"OK",alertTitle:"Warning",alertURLText:"\u5165\u529b\u3055\u308c\u305f\u30b5\u30fc\u30d0\u30fc\u306eURL\u306f\u7121\u52b9\u3067\u3059",buildInfoSection:"",conferenceSection:"\u4f1a\u8b70",displayName:"\u8868\u793a\u540d",email:"E\u30e1\u30fc\u30eb",header:"\u8a2d\u5b9a",profileSection:"\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb",serverURL:"\u30b5\u30fc\u30d0\u30fc\u306eURL",startWithAudioMuted:"\u30aa\u30fc\u30c7\u30a3\u30aa\u3092\u30df\u30e5\u30fc\u30c8\u3067\u958b\u59cb",startWithVideoMuted:"\u30d3\u30c7\u30aa\u3092\u30df\u30e5\u30fc\u30c8\u3067\u958b\u59cb",version:"\u30d0\u30fc\u30b8\u30e7\u30f3"},share:{dialInfoText:"",mainText:""},speaker:"\u30b9\u30d4\u30fc\u30ab\u30fc",speakerStats:{hours:"{{count}} \u79d2",minutes:"{{count}} \u79d2",name:"\u540d\u524d",seconds:"{{count}} \u79d2",speakerStats:"\u8a71\u8005\u306e\u7d71\u8a08",speakerTime:"\u8a71\u3059\u6642\u9593"},"\x05speakerStats":{},startupoverlay:{policyText:" ",title:"{{app}} \u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u30de\u30a4\u30af\u3068\u30ab\u30e1\u30e9\u304c\u5fc5\u8981\u3067\u3059\u3002"},"\x05startupoverlay":{},suspendedoverlay:{rejoinKeyTitle:"\u518d\u53c2\u52a0",text:"\u518d\u63a5\u7d9a\u3059\u308b\u306b\u306f\u3001\u518d\u53c2\u52a0 \u30dc\u30bf\u30f3\u3092\u62bc\u3057\u3066\u304f\u3060\u3055\u3044\u3002",title:"\u3053\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u304c\u30b9\u30ea\u30fc\u30d7\u72b6\u614b\u306b\u306a\u3063\u305f\u305f\u3081\u3001\u30d3\u30c7\u30aa\u901a\u8a71\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f\u3002"},toolbar:{accessibilityLabel:{audioOnly:"\u97f3\u58f0\u306e\u307f\u306b\u5207\u308a\u66ff\u3048\u308b",audioRoute:"\u30b5\u30a6\u30f3\u30c9\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b",callQuality:"",cc:"\u30b5\u30d6\u30bf\u30a4\u30c8\u30eb\u306b\u5207\u308a\u66ff\u3048\u308b",chat:"\u30c1\u30e3\u30c3\u30c8\u753b\u9762\u306b\u5207\u308a\u66ff\u3048\u308b",document:"\u5168\u753b\u9762\u306b\u5207\u308a\u66ff\u3048\u308b",feedback:"\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3092\u6b8b\u3059",fullScreen:"\u5168\u753b\u9762\u306b\u5207\u308a\u66ff\u3048\u308b",hangup:"\u96fb\u8a71\u3092\u304b\u3051\u308b",invite:"\u30e1\u30f3\u30d0\u30fc\u3092\u62db\u5f85\u3059\u308b",kick:"\u53c2\u52a0\u8005\u3092\u8ffd\u3044\u51fa\u3059",localRecording:"\u30ed\u30fc\u30ab\u30eb\u30ec\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u306b\u5207\u308a\u66ff\u3048\u308b",lockRoom:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5207\u308a\u66ff\u3048\u308b",moreActions:"\u3088\u308a\u591a\u304f\u306e\u64cd\u4f5c\u30e1\u30cb\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u308b",moreActionsMenu:"\u4ed6\u306e\u30e1\u30cb\u30e5\u30fc",mute:"\u30df\u30e5\u30fc\u30c8\u30e2\u30fc\u30c9\u3092\u5207\u308a\u66ff\u3048\u308b",pip:"\u30d4\u30af\u30c1\u30e3\u30fc\u30a4\u30f3\u30d4\u30af\u30c1\u30e3\u30fc\u30e2\u30fc\u30c9\u3092\u5207\u308a\u66ff\u3048\u308b",profile:"\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u7de8\u96c6",raiseHand:"\u624b\u3092\u6319\u3052\u308b\u30fb\u4e0b\u3052\u308b",recording:"\u30ec\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u306b\u5207\u308a\u66ff\u3048\u308b",remoteMute:"\u53c2\u52a0\u8005\u3092\u8ffd\u3044\u51fa\u3059",Settings:"\u8a2d\u5b9a\u306b\u5207\u308a\u66ff\u3048\u308b",sharedvideo:"Youtube\u30d3\u30c7\u30aa\u5171\u6709\u306b\u5207\u308a\u66ff\u3048\u308b",shareRoom:"\u8ab0\u304b\u3092\u62db\u5f85\u3059\u308b",shareYourScreen:"\u753b\u9762\u5171\u6709\u306b\u5207\u308a\u66ff\u3048\u308b",shortcuts:"\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u306b\u5207\u308a\u66ff\u3048\u308b",show:"",speakerStats:"\u30b9\u30d4\u30fc\u30ab\u30fc\u7d71\u8a08\u306b\u5207\u308a\u66ff\u3048\u308b",tileView:"",toggleCamera:"\u30ab\u30e1\u30e9\u3092\u5207\u308a\u66ff\u3048\u308b",videomute:"\u30df\u30e5\u30fc\u30c8\u30d3\u30c7\u30aa\u306b\u5207\u308a\u66ff\u3048\u308b",videoblur:""},addPeople:"\u3042\u306a\u305f\u306e\u901a\u8a71\u306b\u4eba\u3092\u8ffd\u52a0\u3059\u308b",audioOnlyOff:"\u97f3\u58f0\u306e\u307f\u30e2\u30fc\u30c9\u3092\u7121\u52b9\u306b\u3059\u308b",audioOnlyOn:"\u97f3\u58f0\u306e\u307f\u30e2\u30fc\u30c9\u958b\u59cb",audioRoute:"\u30b5\u30a6\u30f3\u30c9\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b",authenticate:"\u8a8d\u8a3c",callQuality:"\u901a\u8a71\u54c1\u8cea\u3092\u7ba1\u7406\u3059\u308b",chat:"\u30c1\u30e3\u30c3\u30c8\u3092\u958b\u304f / \u9589\u3058\u308b",closeChat:"\u30c1\u30e3\u30c3\u30c8\u3092\u9589\u3058\u308b",documentClose:"\u5171\u6709\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u9589\u3058\u308b",documentOpen:"\u5171\u6709\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u958b\u304f",enterFullScreen:"\u30d5\u30eb\u30b9\u30af\u30ea\u30fc\u30f3\u8868\u793a",enterTileView:"\u30bf\u30a4\u30c8\u30eb\u30d3\u30e5\u30fc\u3092\u958b\u59cb",exitFullScreen:"\u30d5\u30eb\u30b9\u30af\u30ea\u30fc\u30f3\u3092\u7d42\u4e86",exitTileView:"\u30bf\u30a4\u30c8\u30eb\u30d3\u30e5\u30fc\u3092\u7d42\u4e86",feedback:"\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3092\u6b8b\u3059",hangup:"\u9000\u51fa",invite:"\u30e1\u30f3\u30d0\u30fc\u3092\u62db\u5f85\u3059\u308b",login:"\u30ed\u30b0\u30a4\u30f3",logout:"\u30ed\u30b0\u30a2\u30a6\u30c8",lowerYourHand:"\u624b\u3092\u4e0b\u3052\u308b",moreActions:"\u305d\u306e\u4ed6\u306e\u30a2\u30af\u30b7\u30e7\u30f3",mute:"\u30df\u30e5\u30fc\u30c8 / \u30df\u30e5\u30fc\u30c8\u89e3\u9664",openChat:"\u30c1\u30e3\u30c3\u30c8\u3092\u958b\u304f",pip:"Picture-in-Picture\u30e2\u30fc\u30c9\u306b\u5165\u308b",profile:"\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u7de8\u96c6",raiseHand:"\u624b\u3092\u4e0a\u3052\u308b / \u4e0b\u3052\u308b",raiseYourHand:"\u624b\u3092\u6319\u3052\u308b",Settings:"\u8a2d\u5b9a",sharedvideo:"YouTube\u52d5\u753b\u3092\u5171\u6709\u3059\u308b",shareRoom:"\u8ab0\u304b\u3092\u62db\u5f85\u3059\u308b",shortcuts:"\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u3092\u8868\u793a",speakerStats:"\u8a71\u8005\u306e\u7d71\u8a08",startScreenSharing:"\u753b\u9762\u5171\u6709\u958b\u59cb",startSubtitles:"\u5b57\u5e55\u958b\u59cb",stopScreenSharing:"\u753b\u9762\u5171\u6709\u505c\u6b62",stopSubtitles:"\u5b57\u5e55\u505c\u6b62",stopSharedVideo:"YouTube\u52d5\u753b\u3092\u505c\u6b62\u3059\u308b",talkWhileMutedPopup:"\u8a71\u305d\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u304b\uff1f \u3042\u306a\u305f\u306f\u30df\u30e5\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002",tileViewToggle:"",toggleCamera:"\u30ab\u30e1\u30e9\u3092\u5207\u308a\u66ff\u3048\u308b",videomute:"\u30ab\u30e1\u30e9\u306e\u958b\u59cb / \u505c\u6b62",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{accessibilityLabel:{}},transcribing:{ccButtonTooltip:"\u5b57\u5e55\u3092\u8868\u793a\u30fb\u975e\u8868\u793a\u3059\u308b",error:"\u9332\u753b\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 \u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",expandedLabel:"\u66f8\u304d\u5199\u3057\u304c\u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u308b",failedToStart:"\u66f8\u304d\u5199\u3057\u3092\u30b9\u30bf\u30fc\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002",labelToolTip:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u304c\u66f8\u304d\u5199\u3057\u3055\u308c\u3066\u3044\u307e\u3059\u3002",off:"\u66f8\u304d\u5199\u3057\u304c\u505c\u6b62\u3055\u308c\u3066\u3044\u308b",pending:"\u30df\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u66f8\u304d\u5199\u3057\u3092\u6e96\u5099\u3057\u3066\u3044\u307e\u3059\u3002\u3002\u3002",start:"\u5b57\u5e55\u3092\u8868\u793a\u3059\u308b",stop:"\u5b57\u5e55\u3092\u975e\u8868\u793a\u3059\u308b",tr:"TR"},"\x05transcribing":{},userMedia:{androidGrantPermissions:"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044",chromeGrantPermissions:"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044",edgeGrantPermissions:"\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002",electronGrantPermissions:"\u30de\u30a4\u30af\u3068\u30ab\u30e1\u30e9\u306e\u5171\u6709\u3092\u8a31\u53ef\u3057\u3066\u304f\u3060\u3055\u3044",firefoxGrantPermissions:"\u30c7\u30d0\u30a4\u30b9\u3092\u5171\u6709 \u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002",iexplorerGrantPermissions:"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002",nwjsGrantPermissions:"\u30de\u30a4\u30af\u3068\u30ab\u30e1\u30e9\u306e\u5171\u6709\u3092\u8a31\u53ef\u3057\u3066\u304f\u3060\u3055\u3044",operaGrantPermissions:"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044","react-nativeGrantPermissions":"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044",safariGrantPermissions:"\u30d6\u30e9\u30a6\u30b6\u30fc\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3067\u8a31\u53ef\u3059\u308b\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"},videoSIPGW:{busy:"\u30ea\u30bd\u30fc\u30b9\u3092\u6574\u7406\u3057\u3066\u3044\u307e\u3059\u3002\u5c11\u3005\u304a\u5f85\u3061\u304f\u3060\u3055\u3044\u3002",busyTitle:"\u30eb\u30fc\u30e0\u30b5\u30fc\u30d3\u30b9\u304c\u305f\u3060\u3044\u307e\u6df7\u3093\u3067\u3044\u307e\u3059\u3002",errorAlreadyInvited:"{{displayName}}\u3055\u3093\u304c\u3059\u3067\u306b\u62db\u5f85\u3055\u308c\u307e\u3057\u305f",errorInvite:"\u4f1a\u8b70\u304c\u307e\u3060\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",errorInviteFailed:"\u554f\u984c\u3092\u89e3\u6c7a\u3057\u3066\u3044\u307e\u3059\u3002\u3057\u3070\u3089\u304f\u3057\u305f\u3089\u307e\u305f\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",errorInviteFailedTitle:" {{displayName}}\u3055\u3093\u304c\u62db\u5f85\u3067\u304d\u307e\u305b\u3093\u3002",errorInviteTitle:"\u62db\u96c6\u30a8\u30e9\u30fc",pending:"{{displayName}} \u304c\u62db\u5f85\u3055\u308c\u307e\u3057\u305f"},videoStatus:{audioOnly:"\u97f3\u58f0\u306e\u307f",audioOnlyExpanded:"\u97f3\u58f0\u306e\u307f\u30e2\u30fc\u30c9\u3067\u3001\u4ed6\u306e\u53c2\u52a0\u8005\u306e\u30d3\u30c7\u30aa\u304c\u898b\u3048\u306a\u3044\u3002",callQuality:"",hd:"HD",highDefinition:"\u9ad8\u753b\u8cea",labelTooiltipNoVideo:"\u30d3\u30c7\u30aa\u304c\u306a\u3044",labelTooltipAudioOnly:"\u97f3\u58f0\u306e\u307f\u30e2\u30fc\u30c9\u304c\u6709\u52b9",ld:"LD",lowDefinition:"\u4f4e\u753b\u8cea",onlyAudioAvailable:"\u97f3\u58f0\u306e\u307f\u5229\u7528\u53ef\u80fd",onlyAudioSupported:"\u3053\u306e\u30d6\u30e9\u30a6\u30b6\u3067\u306f\u97f3\u58f0\u306e\u307f\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002",p2pEnabled:"\u30d4\u30a2\u30fb\u30c4\u30fc\u30fb\u30d4\u30a2\u30e2\u30fc\u30c9\u6709\u52b9\u3057\u3066\u3044\u308b",p2pVideoQualityDescription:"",recHighDefinitionOnly:"",sd:"SD",standardDefinition:"\u6a19\u6e96\u753b\u8cea"},videothumbnail:{domute:"\u30df\u30e5\u30fc\u30c8",flip:"\u30d5\u30ea\u30c3\u30d7",kick:"\u8ffd\u3044\u51fa\u3059",moderator:"\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc",mute:"",muted:"\u30df\u30e5\u30fc\u30c8",remoteControl:"\u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"\u30bf\u30c3\u30d7\u3057\u3066\u53c2\u52a0",roomname:"\u30eb\u30fc\u30e0\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044"},appDescription:"\u30c1\u30fc\u30e0\u5168\u4f53\u3068\u30d3\u30c7\u30aa\u30c1\u30e3\u30c3\u30c8\u3057\u307e\u3057\u3087\u3046\u3002\u3042\u306a\u305f\u304c\u77e5\u3063\u3066\u3044\u308b\u7686\u3055\u3093\u3092\u62db\u5f85\u3057\u3066\u304f\u3060\u3055\u3044\u3002{{app}}\u306f\u5b8c\u5168\u306b\u6697\u53f7\u5316\u3055\u308c\u305f100\uff05\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u306e\u30d3\u30c7\u30aa\u4f1a\u8b70\u30bd\u30ea\u30e5\u30fc\u30b7\u30e7\u30f3\u3067\u3001\u4e00\u65e5\u4e2d\u3001\u6bce\u65e5\u7121\u6599\u3067\u3054\u5229\u7528\u3044\u305f\u3060\u3051\u307e\u3059\u3002\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u5fc5\u8981\u3042\u308a\u307e\u305b\u3093\u3002",audioVideoSwitch:{audio:"\u97f3\u58f0",video:"\u6620\u50cf"},calendar:"\u30ab\u30ec\u30f3\u30c0\u30fc",connectCalendarButton:"\u30ab\u30ec\u30f3\u30c0\u30fc\u306b\u63a5\u7d9a",connectCalendarText:"",enterRoomTitle:"\u65b0\u3057\u3044\u30df\u30fc\u30c6\u30a4\u30f3\u30b0\u3092\u958b\u59cb",go:"GO",join:"\u53c2\u52a0",info:"\u60c5\u5831",privacy:"\u30d7\u30e9\u30a4\u30d0\u30b7\u30fc",recentList:"\u6700\u8fd1\u306e\u5c65\u6b74",recentListDelete:"\u524a\u9664",recentListEmpty:"",reducedUIText:"",roomname:"\u30eb\u30fc\u30e0\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044",roomnameHint:"\u53c2\u52a0\u3057\u305f\u3044\u90e8\u5c4b\u306e\u540d\u524d\u307e\u305f\u306fURL\u3092\u5165\u529b\u3057\u307e\u3059\u3002 \u3042\u306a\u305f\u304c\u540d\u524d\u3092\u4ed8\u3051\u3001\u3042\u306a\u305f\u304c\u4f1a\u3063\u3066\u3044\u308b\u4eba\u3005\u306b\u77e5\u3089\u305b\u3066\u3001\u540c\u3058\u540d\u524d\u3092\u5165\u529b\u3057\u3066\u3082\u3089\u3046\u3060\u3051\u3067\u3059\u3002",sendFeedback:"\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3092\u9001\u4fe1",terms:"\u5229\u7528\u898f\u7d04",title:"\u5b89\u5168\u3067\u3001\u6a5f\u80fd\u8c4a\u5bcc\u3067\u3001\u5b8c\u5168\u306b\u7121\u6599\u306e\u30d3\u30c7\u30aa\u4f1a\u8b70"},"\x05welcomepage":{}}},664,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"\uc601\uc5b4",af:"",az:"\uc544\uc81c\ub974\ubc14\uc774\uc794\uc5b4",bg:"\ubd88\uac00\ub9ac\uc5b4",cs:"\uccb4\ucf54\uc5b4",de:"\ub3c5\uc77c\uc5b4",el:"\uadf8\ub9ac\uc2a4\uc5b4",eo:"\uc5d0\uc2a4\ud398\ub780\ud1a0\uc5b4",es:"\uc2a4\ud398\uc778\uc5b4",fr:"\ud504\ub791\uc2a4\uc5b4",hy:"\uc544\ub974\uba54\ub2c8\uc544\uc5b4",it:"\uc774\ud0c8\ub9ac\uc544\uc5b4",ja:"\uc77c\ubcf8\uc5b4",ko:"\ud55c\uad6d\uc5b4",nb:"\ub178\ub974\uc6e8\uc774\uc5b4",oc:"",pl:"\ud3f4\ub780\ub4dc\uc5b4",ptBR:"\ud3ec\ub974\ud22c\uac08\uc5b4(\ube0c\ub77c\uc9c8)",ru:"\ub7ec\uc2dc\uc544\uc5b4",sk:"\uc2ac\ub85c\ubc14\ud0a4\uc544\uc5b4",sl:"\uc2ac\ub85c\ubca0\ub2c8\uc544\uc5b4",sv:"\uc2a4\uc6e8\ub374\uc5b4",tr:"\ud130\ud0a4\uc5b4",vi:"\ubca0\ud2b8\ub0a8\uc5b4",zhCN:"\uc911\uad6d\uc5b4(\uc911\uad6d)"}},665,[]); -__d(function(e,o,i,t,r,n,a){r.exports={addPeople:{add:"\ucd08\ub300",countryNotSupported:"\uc544\uc9c1 \ud574\ub2f9 \uc9c0\uc5ed\uc744 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4",countryReminder:"\ubbf8\uad6d \uc774\uc678\uc758 \uc9c0\uc5ed\uc73c\ub85c \uc804\ud654\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? \uad6d\uac00 \ubc88\ud638\ub85c \uc2dc\uc791\ud574\uc57c\ud569\ub2c8\ub2e4!",disabled:"\uc0ac\ub78c\ub4e4\uc744 \ucd08\ub300 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",failedToAdd:"",footerText:"",loading:"\uc0ac\ub78c \ubc0f \uc804\ud654\ubc88\ud638 \uac80\uc0c9",loadingNumber:"\uc804\ud654\ubc88\ud638 \ud655\uc778 \uc911",loadingPeople:"\ucd08\ub300\ud560 \uc0ac\ub78c \ucc3e\uae30",noResults:"\uc77c\uce58\ud558\ub294 \uac80\uc0c9 \uacb0\uacfc \uc5c6\uc74c",noValidNumbers:"\uc804\ud654 \ubc88\ud638\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624.",searchNumbers:"\uc804\ud654\ubc88\ud638 \ucd94\uac00",searchPeople:"\uc778\uba85 \uac80\uc0c9",searchPeopleAndNumbers:"\uc778\uba85 \uac80\uc0c9 \ub610\ub294 \uc804\ud654\ubc88\ud638 \ucd94\uac00",telephone:"\uc804\ud654: {{number}}",title:"\uc774 \ud68c\uc758\uc5d0 \uc0ac\ub78c\ub4e4\uc744 \ucd08\ub300\ud558\uc2ed\uc2dc\uc624"},audioDevices:{bluetooth:"\ube14\ub8e8\ud22c\uc2a4",headphones:"\ud5e4\ub4dc\ud3f0",phone:"\ud3f0",speaker:"\uc2a4\ud53c\ucee4"},audioOnly:{audioOnly:"\uc74c\uc131 \uc804\uc6a9"},calendarSync:{addMeetingURL:"",confirmAddLink:"",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"",joinTooltip:"",nextMeeting:"\ub2e4\uc74c \ud68c\uc758",noEvents:"",ongoingMeeting:"",permissionButton:"\uc124\uc815 \uc5f4\uae30",permissionMessage:"\uc571\uc5d0 \ud68c\uc758\ub97c \ub098\uc5f4\ud558\ub824\uba74 \uce98\ub9b0\ub354 \uad8c\ud55c\uc774 \ud544\uc694\ud569\ub2c8\ub2e4",refresh:"",today:""},chat:{error:"",messagebox:"",nickname:{popover:"\ub2c9\ub124\uc784\uc744 \uc120\ud0dd\ud558\uc138\uc694",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"\ucca8\ubd80",AUTHENTICATING:"\uc778\uc99d \uc911",AUTHFAIL:"\uc778\uc99d \uc2e4\ud328",CONNECTED:"\uc5f0\uacb0 \ub428",CONNECTING:"\uc5f0\uacb0 \uc911",CONNFAIL:"\uc5f0\uacb0 \uc2e4\ud328",DISCONNECTED:"\uc5f0\uacb0 \ub04a\uae40",DISCONNECTING:"\uc5f0\uacb0 \uc885\ub8cc \uc911",ERROR:"\uc5d0\ub7ec",RECONNECTING:"\ub124\ud2b8\uc6cc\ud06c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc5f0\uacb0 \uc911..."},connectionindicator:{address:"\uc8fc\uc18c:",bandwidth:"\uc608\uc0c1 \ub300\uc5ed\ud3ed:",bitrate:"\uc804\uc1a1\ub960:",bridgeCount:"",connectedTo:"",framerate:"\ud504\ub808\uc784 \uc18d\ub3c4:",less:"\uac04\ub7b5\ud788 \ubcf4\uae30",localaddress:"",localport:"",more:"\uc790\uc138\ud788 \ubcf4\uae30",packetloss:"\ud328\ud0b7 \uc190\uc2e4:",quality:{good:"\uc88b\uc74c",inactive:"Inactive",lost:"Lost",nonoptimal:"Nonoptimal",poor:"Poor"},remoteaddress:"",remoteport:"",resolution:"\ud574\uc0c1\ub3c4:",status:"\uc5f0\uacb0:",transport:"",turn:" (turn)"},dateUtils:{earlier:"",today:"",yesterday:""},deepLinking:{appNotInstalled:"\uc911\uacc4 \uc11c\ube44\uc2a4\uc5d0 \ucc38\uc5ec\ud558\ub824\uba74 \ubaa8\ubc14\uc77c \uc571 \uc124\uce58\uac00 \ud544\uc694\ud569\ub2c8\ub2e4",description:"{{app}} \ub370\uc2a4\ud06c\ud1b1 \uc571\uc5d0\uc11c \ud68c\uc758\ub97c \uc2dc\uc791\ud588\uc2b5\ub2c8\ub2e4. {{app}} \uc6f9 \uc751\uc6a9 \ud504\ub85c\uadf8\ub7a8\uc5d0\uc11c \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uac70\ub098 \uc2e4\ud589\ud558\uc2ed\uc2dc\uc624.",descriptionWithoutWeb:"",downloadApp:"\uc571 \ub2e4\uc6b4\ub85c\ub4dc",launchWebButton:"\uc6f9\uc5d0\uc11c \uc2e4\ud589",openApp:"\ubc29\uc73c\ub85c \uc774\ub3d9\ud558\uae30",title:"{{app}}\uc5d0\uc11c \ud68c\uc758 \uc2dc\uc791...",tryAgainButton:"\ub370\uc2a4\ud06c\ud1b1\uc5d0\uc11c \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624"},defaultLink:"e.g. {{url}}",deviceError:{cameraError:"\uce74\uba54\ub77c\uc5d0 \uc561\uc138\uc2a4\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",cameraPermission:"\uce74\uba54\ub77c \uad8c\ud55c\uc744 \uc5bb\ub294 \uc911 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",microphoneError:"\ub9c8\uc774\ud06c\uc5d0 \uc561\uc138\uc2a4\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",microphonePermission:"\ub9c8\uc774\ud06c \uad8c\ud55c\uc744 \uc5bb\ub294 \uc911 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4"},deviceSelection:{noPermission:"\ud5c8\uac00 \uac70\ubd80",previewUnavailable:"\ubbf8\ub9ac\ubcf4\uae30\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc74c",selectADevice:"\ub514\ubc14\uc774\uc2a4 \uc120\ud0dd",testAudio:"\ud14c\uc2a4\ud2b8 \uc0ac\uc6b4\ub4dc \uc7ac\uc0dd"},dialog:{accessibilityLabel:{liveStreaming:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d:"},allow:"\ud5c8\ub77d",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\ud55c \ubc88\uc5d0 \ud558\ub098\uc758 \uacf5\uc720 \ube44\ub514\uc624 \ub9cc \ud5c8\uc6a9\ub429\ub2c8\ub2e4",applicationWindow:"\uc751\uc6a9 \ud504\ub85c\uadf8\ub7a8 \ucc3d",Back:"\ub4a4\ub85c\uac00\uae30",cameraConstraintFailedError:"\uce74\uba54\ub77c\uac00 \ud544\uc694\ud55c \uc81c\uc57d \uc870\uac74 \uc911 \uc77c\ubd80\ub97c \ub9cc\uc871\ud558\uc9c0 \ubabb\ud569\ub2c8\ub2e4",cameraNotFoundError:"\uce74\uba54\ub77c\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",cameraNotSendingData:"\uce74\uba54\ub77c\uc5d0 \uc561\uc138\uc2a4 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ub2e4\ub978 \uc751\uc6a9 \ud504\ub85c\uadf8\ub7a8\uc774\uc774 \uc7a5\uce58\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub294\uc9c0 \ud655\uc778\ud55c \ud6c4 \uc124\uc815 \uba54\ub274\uc5d0\uc11c \ub2e4\ub978 \uc7a5\uce58\ub97c \uc120\ud0dd\ud558\uac70\ub098 \uc751\uc6a9 \ud504\ub85c\uadf8\ub7a8\uc744 \ub2e4\uc2dc\ub85c\ub4dc\ud558\uc2ed\uc2dc\uc624.",cameraNotSendingDataTitle:"\uce74\uba54\ub77c\uc5d0 \uc561\uc138\uc2a4 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",cameraPermissionDeniedError:"\uce74\uba54\ub77c \uc0ac\uc6a9 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ud68c\uc758\uc5d0 \uacc4\uc18d \ucc38\uc5ec\ud560 \uc218 \uc788\uc9c0\ub9cc \ub2e4\ub978 \ucc38\uc11d\uc790\ub294 \uadc0\ud558\ub97c \ubcfc \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uac80\uc0c9 \uc8fc\uc18c\ucc3d\uc758 \uce74\uba54\ub77c \ubc84\ud2bc\uc744 \uc0ac\uc6a9\ud558\uc5ec \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uc2ed\uc2dc\uc624.",cameraUnknownError:"\uc54c \uc218\uc5c6\ub294 \uc774\uc720\ub85c \uce74\uba54\ub77c\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",cameraUnsupportedResolutionError:"\uce74\uba54\ub77c\uac00 \ud544\uc694\ud55c \ube44\ub514\uc624 \ud574\uc0c1\ub3c4\ub97c \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4",Cancel:"\ucde8\uc18c",close:"\ub2eb\uae30",conferenceDisconnectMsg:"\ub124\ud2b8\uc6cc\ud06c \uc5f0\uacb0\uc744 \ud655\uc778\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. {{seconds}} \ucd08 \ub0b4\uc5d0 \ub2e4\uc2dc \uc5f0\uacb0\uc911\uc785\ub2c8\ub2e4...",conferenceDisconnectTitle:"\uc5f0\uacb0\uc774 \ub04a\uc5b4\uc84c\uc2b5\ub2c8\ub2e4.",conferenceReloadMsg:"\ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub824\uace0 \ub178\ub825\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. {{seconds}} \ucd08 \uc548\uc5d0 \ub2e4\uc2dc \uc5f0\uacb0\uc911\uc785\ub2c8\ub2e4.",conferenceReloadTitle:"\ubd88\ud589\ud558\uac8c\ub3c4 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",confirm:"\ud655\uc778",confirmNo:"",confirmYes:"",connectError:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\uc5ec \ud68c\uc758\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",connectErrorWithMsg:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ubb54\uac00 \uc798\ubabb\ub418\uc5b4 \ud68c\uc758\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4: {{msg}}",connecting:"\uc5f0\uacb0 \uc911",contactSupport:"\uc9c0\uc6d0 \uc5f0\ub77d\ucc98",copy:"\ubcf5\uc0ac",dismiss:"",displayNameRequired:"",done:"\uc644\ub8cc",enterDisplayName:"",error:"\uc5d0\ub7ec",externalInstallationMsg:"\ub370\uc2a4\ud06c\ud1b1 \uacf5\uc720 \ud655\uc7a5 \ud504\ub85c\uadf8\ub7a8\uc744 \uc124\uce58\ud574\uc57c\ud569\ub2c8\ub2e4",externalInstallationTitle:"\ud655\uc7a5 \ud504\ub85c\uadf8\ub7a8\uc774 \ud544\uc694\ud569\ub2c8\ub2e4",goToStore:"\uc6f9 \uc2a4\ud1a0\uc5b4\ub85c \uc774\ub3d9",gracefulShutdown:"\uc11c\ube44\uc2a4\ub294 \ud604\uc7ac \uc720\uc9c0 \uad00\ub9ac\ub97c \uc704\ud574 \uc911\ub2e8\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4 \ud574\uc8fc\uc2ed\uc2dc\uc624.",IamHost:"\ub0b4\uac00 \ud638\uc2a4\ud2b8",incorrectRoomLockPassword:"",incorrectPassword:"\uc798\ubabb\ub41c \uc0ac\uc6a9\uc790 \uc774\ub984 \ub610\ub294 \ube44\ubc00\ubc88\ud638",inlineInstallationMsg:"\ub370\uc2a4\ud06c\ud1b1 \uacf5\uc720 \ud655\uc7a5 \ud504\ub85c\uadf8\ub7a8\uc744 \uc124\uce58\ud574\uc57c\ud569\ub2c8\ub2e4",inlineInstallExtension:"\uc9c0\uae08 \uc124\uce58",internalError:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ubb54\uac00 \uc798\ubabb \ub410\uc2b5\ub2c8\ub2e4. \ub2e4\uc74c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4: {{error}}",internalErrorTitle:"\ub0b4\ubd80 \uc5d0\ub7ec",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d",liveStreamingDisabledForGuestTooltip:"",liveStreamingDisabledTooltip:"",lockMessage:"\ud68c\uc758\ub97c \ube44\uacf5\uac1c\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",lockRoom:"",lockTitle:"\ube44\uacf5\uac1c \uc2e4\ud328",logoutQuestion:"\ub85c\uadf8 \uc544\uc6c3\ud558\uace0 \ucee8\ud37c\ub7f0\uc2a4\ub97c \uc911\uc9c0\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",logoutTitle:"\ub85c\uadf8\uc544\uc6c3",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\ub9c8\uc774\ud06c\uac00 \ud544\uc694\ud55c \uc81c\uc57d \uc870\uac74 \uc911 \uc77c\ubd80\ub97c \ucda9\uc871\ud558\uc9c0 \ubabb\ud569\ub2c8\ub2e4",micNotFoundError:"\ub9c8\uc774\ud06c\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\ub9c8\uc774\ud06c\ub97c \uc0ac\uc6a9\ud560 \uc218\uc788\ub294 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ud68c\uc758\uc5d0 \uacc4\uc18d \ucc38\uc5ec\ud560 \uc218\ub294 \uc788\uc9c0\ub9cc \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc740 \ub4e3\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uac80\uc0c9 \uc8fc\uc18c\ucc3d\uc758 \uce74\uba54\ub77c \ubc84\ud2bc\uc744 \uc0ac\uc6a9\ud558\uc5ec \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uc2ed\uc2dc\uc624.",micUnknownError:"\uc54c \uc218 \uc5c6\ub294 \uc774\uc720\ub85c \ub9c8\uc774\ud06c\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4",muteParticipantBody:"\ub2f9\uc2e0\uc774 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc758 \uc74c\uc18c\uac70\ub97c \ud574\uc81c \ud560 \uc218\ub294 \uc5c6\uc9c0\ub9cc \uc5b8\uc81c\ub4e0\uc9c0 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc740 \uc2a4\uc2a4\ub85c \uc74c\uc18c\uac70\ub97c \ud574\uc81c\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.",muteParticipantButton:"\uc74c\uc18c\uac70",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"\ud655\uc778",passwordLabel:"",passwordNotSupported:"\ubbf8\ud305 \ube44\ubc00\ubc88\ud638 \uc124\uc815\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\ube0c\ub77c\uc6b0\uc800\uac00\uc774 \uc0ac\uc774\ud2b8\uc758 \ud31d\uc5c5 \ucc3d\uc744 \ucc28\ub2e8\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \ube0c\ub77c\uc6b0\uc800\uc758 \ubcf4\uc548 \uc124\uc815\uc5d0\uc11c \ud31d\uc5c5\uc744 \ud65c\uc131\ud654\ud558\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",popupErrorTitle:"\ud31d\uc5c5 \ucc28\ub2e8\ub428",recording:"\ub808\ucf54\ub529",recordingDisabledForGuestTooltip:"",recordingDisabledTooltip:"",rejoinNow:"\uc9c0\uae08 \uc7ac\uac00\uc785",remoteControlAllowedMessage:"{{user}}\uc774(\uac00) \uc6d0\uaca9 \uc81c\uc5b4 \uc694\uccad\uc744 \uc218\ub77d\ud588\uc2b5\ub2c8\ub2e4",remoteControlDeniedMessage:"{{user}}\uc774(\uac00) \uc6d0\uaca9 \uc81c\uc5b4 \uc694\uccad\uc744 \uac70\ubd80\ud588\uc2b5\ub2c8\ub2e4",remoteControlErrorMessage:"{{user}}\ub85c\ubd80\ud130 \uc6d0\uaca9 \uc81c\uc5b4 \uad8c\ud55c\uc744 \uc694\uccad\ud558\ub294 \ub3d9\uc548 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",remoteControlRequestMessage:"{{user}}\uc5d0\uac8c \ub2f9\uc2e0\uc758 \ub370\uc2a4\ud06c\ud0d1\uc744 \uc6d0\uaca9 \uc81c\uc5b4\ud558\ub3c4\ub85d \ud5c8\uc6a9\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",remoteControlShareScreenWarning:"\"\ud5c8\ub77d\"\uc744 \ub204\ub974\uba74 \ud654\uba74\uc744 \uacf5\uc720\ud569\ub2c8\ub2e4",remoteControlStopMessage:"\uc6d0\uaca9 \uc81c\uc5b4 \uc138\uc158\uc774 \uc885\ub8cc\ub418\uc5c8\uc2b5\ub2c8\ub2e4",remoteControlTitle:"\uc6d0\uaca9 \ub370\uc2a4\ud06c\ud0d1 \ucee8\ud2b8\ub864",Remove:"\uc81c\uac70",removePassword:"",removeSharedVideoMsg:"\uacf5\uc720\ud55c \ub3d9\uc601\uc0c1\uc744 \uc0ad\uc81c\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",removeSharedVideoTitle:"\uacf5\uc720\ub41c \ub3d9\uc601\uc0c1 \uc0ad\uc81c",reservationError:"\uc608\uc57d \uc2dc\uc2a4\ud15c \uc624\ub958",reservationErrorMsg:"\uc624\ub958 \ucf54\ub4dc: {{code}}, \uba54\uc2dc\uc9c0: {{msg}}",retry:"\uc7ac\uc2dc\ub3c4",screenSharingFailedToInstall:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ud654\uba74 \uacf5\uc720 \ud655\uc7a5 \ud504\ub85c\uadf8\ub7a8\uc744 \uc124\uce58\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.",screenSharingFailedToInstallTitle:"\ud654\uba74 \uacf5\uc720 \ud655\uc7a5 \ud504\ub85c\uadf8\ub7a8\uc744 \uc124\uce58\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",screenSharingFirefoxPermissionDeniedError:"\ud654\uba74\uc744 \uacf5\uc720\ud558\ub294 \ub3d9\uc548 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. \uadf8\ub807\uac8c \ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud588\ub294\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.",screenSharingFirefoxPermissionDeniedTitle:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ud654\uba74 \uacf5\uc720\ub97c \uc2dc\uc791\ud560 \uc218 \uc5c6\uc5c8\uc2b5\ub2c8\ub2e4!",screenSharingPermissionDeniedError:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ud654\uba74 \uacf5\uc720 \ud655\uc7a5 \uad8c\ud55c\uc73c\ub85c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \ub85c\ub4dc\ud558\uace0 \uc7ac\uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",serviceUnavailable:"\uc11c\ube44\uc2a4\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc74c",sessTerminated:"\ud1b5\ud654 \uc885\ub8cc",Share:"\uacf5\uc720",shareVideoLinkError:"\uc62c\ubc14\ub978 YouTube \ub9c1\ud06c\ub97c \uc81c\uacf5\ud558\uc2ed\uc2dc\uc624",shareVideoTitle:"\ube44\ub514\uc624 \uacf5\uc720",shareYourScreen:"\ud654\uba74\uacf5\uc720",shareYourScreenDisabled:"",shareYourScreenDisabledForGuest:"",startLiveStreaming:"\ub77c\uc774\ube0c \uc2a4\ud2b8\ub9ac\ubc0d \uc2dc\uc791",startRecording:"\ub808\ucf54\ub529 \uc2dc\uc791",startRemoteControlErrorMessage:"\uc6d0\uaca9 \uc81c\uc5b4 \uc138\uc158\uc744 \uc2dc\uc791\ud558\ub294 \ub3d9\uc548 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",stopLiveStreaming:"\ub77c\uc774\ube0c \uc2a4\ud2b8\ub9ac\ubc0d \uc885\ub8cc",stopRecording:"\ub808\ucf54\ub529 \uc885\ub8cc",stopRecordingWarning:"\ub808\ucf54\ub529\uc744 \uc911\ub2e8\ud558\uace0 \uc2f6\uc73c\uc2ed\ub2c8\uae4c?",stopStreamingWarning:"\ub77c\uc774\ube0c \uc2a4\ud2b8\ub9ac\ubc0d\uc744 \uc911\ub2e8\ud558\uace0 \uc2f6\uc73c\uc2ed\ub2c8\uae4c?",streamKey:"\ub77c\uc774\ube0c \uc2a4\ud2b8\ub9ac\ubc0d \ud0a4",Submit:"\uc81c\ucd9c",thankYou:"{{appName}}\uc744 \uc774\uc6a9\ud574 \uc8fc\uc154\uc11c \uac10\uc0ac\ud569\ub2c8\ub2e4!",token:"\ud1a0\ud070",tokenAuthFailed:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ud1b5\ud654\uc5d0 \ucc38\uc5ec\ud558\uc2e4 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",tokenAuthFailedTitle:"\uc778\uc99d \uc2e4\ud328",transcribing:"",unlockRoom:"",userPassword:"\uc0ac\uc6a9\uc790 \ube44\ubc00\ubc88\ud638",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"\ud638\uc2a4\ud2b8\ub97c \uae30\ub2e4\ub9ac\ub294 \uc911\uc785\ub2c8\ub2e4...",Yes:"",yourEntireScreen:"\uc804\uccb4 \ud654\uba74"},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:"\uc9c0\uae08\uc740 {{status}}\uc785\ub2c8\ub2e4"},feedback:{average:"\ubcf4\ud1b5",bad:"\ub098\uc068",detailsLabel:"\ub354 \uc790\uc138\ud788 \uc54c\ub824\uc8fc\uc2ed\uc2dc\uc624",good:"\uc88b\uc74c",rateExperience:"\ud68c\uc758 \uacbd\ud5d8 \ud3c9\uac00\ud558\uae30",veryBad:"\ub9e4\uc6b0 \ub098\uc068",veryGood:"\ub9e4\uc6b0 \uc88b\uc74c"},incomingCall:{answer:"",audioCallTitle:"",decline:"",productLabel:"",videoCallTitle:""},info:{accessibilityLabel:"",addPassword:"",cancelPassword:"",conferenceURL:"\ub9c1\ud06c:",country:"\uc9c0\uc5ed",dialANumber:"",dialInConferenceID:"PIN:",dialInNotSupported:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \ud604\uc7ac \uc804\ud654\ub97c \uac78 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",dialInNumber:"Dial-in:",dialInSummaryError:"",dialInTollFree:"",genericError:"\uc77c\ubc18\uc801\uc778 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",inviteLiveStream:"\uc774 \ud68c\uc758\uc758 \uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9bc\uc744 \ubcf4\ub824\uba74\uc774 \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc2ed\uc2dc\uc624: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d:",moreNumbers:"\ub354 \ub9ce\uc740 \ubc88\ud638",noNumbers:"\uc804\ud654 \uc811\uc18d \ubc88\ud638 \uc5c6\uc74c",noPassword:"\uc5c6\uc74c",noRoom:"\uc804\ud654 \uc811\uc18d\uc774 \uac00\ub2a5\ud55c \ubc29\uc744 \uc9c0\uc815\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4",numbers:"\uc804\ud654 \uc811\uc18d \ubc88\ud638",password:"",title:"\uacf5\uc720",tooltip:"\ub9c1\ud06c \uacf5\uc720 \ubc0f \ud68c\uc758\uc5d0 \ub300\ud55c \uc815\ubcf4",label:""},"\x05info":{},inviteDialog:{alertText:"",header:"\ucd08\ub300",searchCallOnlyPlaceholder:"",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"\uc57d\uac04\uc758 \ubb38\uc81c\uac00 \uc788\uc2b5\ub2c8\ub2e4",retry:"\ub2e4\uc2dc \uc2dc\ub3c4",support:"\uc9c0\uc6d0",supportMsg:"\ubb38\uc81c\uac00 \uacc4\uc18d \ubc1c\uc0dd\ud558\uba74 \uc5f0\ub77d\ud558\uc138\uc694"},keyboardShortcuts:{focusLocal:"\uc790\uc2e0\uc758 \ub3d9\uc601\uc0c1\uc5d0 \ud3ec\ucee4\uc2a4",focusRemote:"\ub2e4\ub978 \ubc1c\uc2e0\uc790\uc758 \ub3d9\uc601\uc0c1\uc5d0 \ud3ec\ucee4\uc2a4",fullScreen:"\uc804\uccb4\ud654\uba74 \ud45c\uc2dc \ub610\ub294 \uc885\ub8cc",keyboardShortcuts:"\ud0a4\ubcf4\ub4dc \ub2e8\ucd95\ud0a4",localRecording:"",mute:"\ub9c8\uc774\ud06c \uc74c\uc18c\uac70 \ub610\ub294 \uc74c\uc18c\uac70 \ud574\uc81c",pushToTalk:"\ub300\ud654 \uc694\uccad",raiseHand:"\ub9d0\ud558\uae30 \uc694\uccad/\ud574\uc81c",showSpeakerStats:"\uc811\uc18d\uc790 \ud1b5\uacc4 \ud45c\uc2dc",toggleChat:"\ucc44\ud305 \ud45c\uc2dc \ub610\ub294 \uc228\uae30\uae30",toggleFilmstrip:"\ub3d9\uc601\uc0c1 \ud45c\uc2dc \ub610\ub294 \uc228\uae30\uae30",toggleScreensharing:"\uce74\uba54\ub77c\uc640 \ud654\uba74 \uacf5\uc720\uac04\uc5d0 \uc804\ud658",toggleShortcuts:"\ub3c4\uc6c0\ub9d0 \uba54\ub274 \ud45c\uc2dc \ub610\ub294 \uc228\uae30\uae30",videoMute:"\uce74\uba54\ub77c \uc2dc\uc791 \ub610\ub294 \uc911\uc9c0"},liveStreaming:{busy:"\uc2a4\ud2b8\ub9ac\ubc0d \uc790\uc6d0\uc744 \ud655\ubcf4\ud558\uae30 \uc704\ud574 \ub178\ub825\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \uba87 \ubd84 \ud6c4\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",busyTitle:"\ubaa8\ub4e0 \uc2a4\ud2b8\ub9ac\uba38\uac00 \ud604\uc7ac \uc0ac\uc6a9 \uc911\uc785\ub2c8\ub2e4",changeSignIn:"\uacc4\uc815\uc744 \uc804\ud658\ud558\uc2ed\uc2dc\uc624",choose:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d\uc744 \uc120\ud0dd\ud558\uc138\uc694",chooseCTA:"\uc2a4\ud2b8\ub9ac\ubc0d \uc635\uc158\uc744 \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624. \ud604\uc7ac {{email}} (\uc73c)\ub85c \ub85c\uadf8\uc778\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.",enterStreamKey:"YouTube \uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \ud0a4\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624",error:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",errorAPI:"YouTube \ubc29\uc1a1\uc5d0 \uc561\uc138\uc2a4\ud558\ub294 \uc911\uc5d0 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \ub85c\uadf8\uc778\ud558\uc2ed\uc2dc\uc624.",errorLiveStreamNotEnabled:"",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \uc2dc\uc791 \uc2e4\ud328",getStreamKeyManually:"",invalidStreamKey:"",off:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d\uc774 \uc911\uc9c0\ub428",on:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d",pending:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \uc2dc\uc791...",serviceName:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \uc11c\ube44\uc2a4",signedInAs:"",signIn:"Google\ub85c \ub85c\uadf8\uc778",signInCTA:"YouTube\uc5d0\uc11c \ub85c\uadf8\uc778\ud558\uac70\ub098 \uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \ud0a4\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624",signOut:"",start:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d \uc2dc\uc791",streamIdHelp:"\ub3c4\uc6c0\ub9d0?",unavailableTitle:"\uc2e4\uc2dc\uac04 \uc2a4\ud2b8\ub9ac\ubc0d\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc74c"},localRecording:{clientState:{off:"",on:"",unknown:""},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"",labelToolTip:"",localRecording:"",me:"",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:""},moderator:"",no:"",participant:"",participantStats:"",sessionToken:"",start:"\ub808\ucf54\ub529 \uc2dc\uc791",stop:"\ub808\ucf54\ub529 \uc885\ub8cc",yes:""},"\x05localRecording":{},lockRoomPassword:"\ud328\uc2a4\uc6cc\ub4dc",lockRoomPasswordUppercase:"\ud328\uc2a4\uc6cc\ub4dc",me:"Me",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"\uc5f0\uacb0\uc774 \ub04a\uae40",focus:"\ucee8\ud37c\ub7f0\uc2a4 \ud3ec\ucee4\uc2a4",focusFail:"{{component}}\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc74c - {{ms}} \ucd08 \ud6c4\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624",grantedTo:"{{to}}\uc5d0\uac8c \ubc29\uc7a5 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"",moderator:"\ubc29\uc7a5 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4!",muted:"\uc74c\uc18c\uac70\ub85c \ub300\ud654\uac00 \uc2dc\uc791\ub418\uc5c8\uc2b5\ub2c8\ub2e4",mutedTitle:"\uc74c\uc18c\uac70 \uc0c1\ud0dc\uc785\ub2c8\ub2e4!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"\ub204\uad70\uac00",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"{{appName}}\uc5d0 \ub300\ud55c \uadc0\ud558\uc758 \uacbd\ud5d8\uc774 \uc5c6\uc73c\uc2dc\ub2e4\uba74 \uc644\ubcbd\ud558\uac8c \uc9c0\uc6d0\ub418\ub294 \ube0c\ub77c\uc6b0\uc800 \uc911 \ud558\ub098\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc2ed\uc2dc\uc624.",suboptimalExperienceTitle:"\ube0c\ub77c\uc6b0\uc800 \uacbd\uace0",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"powered by",presenceStatus:{busy:"\ubc14\uc068",calling:"\uc804\ud654 \uac70\ub294 \uc911",connected:"\uc5f0\uacb0 \ub428",connecting:"\uc5f0\uacb0 \uc911",connecting2:"\uc5f0\uacb0 \uc911*",disconnected:"\uc5f0\uacb0 \ub04a\uae40",expired:"\ub9cc\ub8cc \ub428",ignored:"\ubb34\uc2dc \ub428",initializingCall:"\ud1b5\ud654 \ucd08\uae30\ud654 \uc911",invited:"\ucd08\ub300 \ub428",rejected:"\uac70\ubd80 \ub428",ringing:"\uc804\ud654 \uc911"},profile:{setDisplayNameLabel:"\ud45c\uc2dc \uc774\ub984 \uc124\uc815",setEmailInput:"\uc774\uba54\uc77c \uc785\ub825",setEmailLabel:"\uc774\uba54\uc77c \uc124\uc815",title:"\ud504\ub85c\ud544"},recording:{authDropboxText:"",availableSpace:"",beta:"\ubca0\ud0c0",busy:"\ub808\ucf54\ub529 \uc790\uc6d0\uc744 \ud655\ubcf4\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \uba87 \ubd84 \ud6c4\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",busyTitle:"\ubaa8\ub4e0 \ub808\ucf54\ub354\uac00 \ud604\uc7ac \uc0ac\uc6a9 \uc911\uc785\ub2c8\ub2e4",error:"\ub808\ucf54\ub529\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",expandedOff:"\ub808\ucf54\ub529\uc774 \uc911\uc9c0\ub428",expandedOn:"",expandedPending:"",failedToStart:"\ub808\ucf54\ub529\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",fileSharingdescription:"",live:"\ub77c\uc774\ube0c",loggedIn:"",off:"\ub808\ucf54\ub529\uc774 \uc911\uc9c0\ub428",on:"\ub808\ucf54\ub529",pending:"\ucc38\uc11d\ud560 \uba64\ubc84\ub97c \uae30\ub2e4\ub9ac\ub294 \uc911\uc785\ub2c8\ub2e4...",rec:"REC",serviceDescription:"",serviceName:"\ub808\ucf54\ub529 \uc11c\ube44\uc2a4",signIn:"",signOut:"",unavailable:"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. {{serviceName}}\uc740 \ud604\uc7ac \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc800\ud76c\ub294 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub178\ub825\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4 \ud574\uc8fc\uc2ed\uc2dc\uc624.",unavailableTitle:"\ub808\ucf54\ub529\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4"},"\x05recording":{},sectionList:{pullToRefresh:"\ub2f9\uaca8\uc11c \uc0c8\ub85c\uace0\uce68"},settings:{calendar:{about:"",disconnect:"\uc5f0\uacb0 \ub04a\uae40",microsoftSignIn:"",signedIn:"",title:""},devices:"",followMe:"\ubaa8\ub450 \ub098\uc640 \uac19\uc740 \uc124\uc815 \uc0c1\ud0dc\ub85c",language:"",loggedIn:"",moderator:"",more:"",name:"\uc774\ub984",noDevice:"\uc5c6\uc74c",selectAudioOutput:"\uc624\ub514\uc624 \ucd9c\ub825",selectCamera:"\uce74\uba54\ub77c",selectMic:"\uc624\ub514\uc624",startAudioMuted:"\ubaa8\ub450\uac00 \uc74c\uc18c\uac70\ub97c \uc2dc\uc791\ud569\ub2c8\ub2e4",startVideoMuted:"\ubaa8\ub450\uac00 \ube44\ub514\uc624 \ube44\ud65c\uc131\ud654\ub85c \uc2dc\uc791\ud569\ub2c8\ub2e4",title:"\uc138\ud2f0"},"\x05settings":{calendar:{}},settingsView:{alertOk:"\ud655\uc778",alertTitle:"\uacbd\uace0",alertURLText:"\uc785\ub825\ub41c \uc11c\ubc84 URL\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4",buildInfoSection:"",conferenceSection:"\ud68c\uc758",displayName:"\uc720\uc800\uc774\ub984",email:"\uc774\uba54\uc77c",header:"\uc138\ud2f0",profileSection:"\ud504\ub85c\ud544",serverURL:"\uc11c\ubc84 URL",startWithAudioMuted:"\uc624\ub514\uc624 \uc74c\uc18c\uac70 \uc0c1\ud0dc\ub85c \uc2dc\uc791",startWithVideoMuted:"\ube44\ub514\uc624 \ube44\ud65c\uc131\ud654 \uc0c1\ud0dc\ub85c \uc2dc\uc791",version:""},share:{dialInfoText:"",mainText:""},speaker:"\uc2a4\ud53c\ucee4",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"\uc774\ub984",seconds:"{{count}}s",speakerStats:"\uc811\uc18d\uc790 \ud1b5\uacc4",speakerTime:"\uc811\uc18d\uc790 \uc624\ub514\uc624 \uc0ac\uc6a9 \uc2dc\uac04"},startupoverlay:{policyText:" ",title:"{{app}}\uc740 \ub9c8\uc774\ud06c\uc640 \uce74\uba54\ub77c\ub97c \uc0ac\uc6a9\ud574\uc57c\ud569\ub2c8\ub2e4"},suspendedoverlay:{rejoinKeyTitle:"\uc7ac\uc811\uc18d",text:"\ub2e4\uc2dc \uc5f0\uacb0\ud558\ub824\uba74 \uc7ac\uc811\uc18d \ubc84\ud2bc\uc744 \ub204\ub974\uc2ed\uc2dc\uc624",title:"\ucef4\ud4e8\ud130 \ud734\uc2dd \ubaa8\ub4dc\uac00 \ub418\uc5b4 \ud654\uc0c1 \ud1b5\ud654\uac00 \uc911\ub2e8\ub418\uc5c8\uc2b5\ub2c8\ub2e4"},toolbar:{accessibilityLabel:{audioOnly:"",audioRoute:"\uc74c\uc131 \uc7a5\ube44 \uc120\ud0dd\ud558\uae30",callQuality:"",cc:"",chat:"",document:"",feedback:"\ud53c\ub4dc\ubc31 \ub0a8\uae30\uae30",fullScreen:"",hangup:"",invite:"",kick:"",localRecording:"",lockRoom:"",moreActions:"",moreActionsMenu:"",mute:"",pip:"",profile:"",raiseHand:"",recording:"",remoteMute:"",Settings:"",sharedvideo:"",shareRoom:"",shareYourScreen:"",shortcuts:"\ub2e8\ucd95\ud0a4 \ud1a0\uadf8",show:"",speakerStats:"",tileView:"",toggleCamera:"\uce74\uba54\ub77c \ud1a0\u3131",videomute:"",videoblur:""},addPeople:"\ud1b5\ud654\uc5d0 \uc0ac\uc6a9\uc790 \ucd94\uac00",audioOnlyOff:"\uc74c\uc131\uc804\uc6a9 \ubaa8\ub4dc \ub044\uae30",audioOnlyOn:"\uc74c\uc131\uc804\uc6a9 \ubaa8\ub4dc \ub044\uae30",audioRoute:"\uc74c\uc131 \uc7a5\ube44 \uc120\ud0dd\ud558\uae30",authenticate:"\uc778\uc99d \uc911",callQuality:"\ud488\uc9c8 \uc124\uc815\ud558\uae30",chat:"",closeChat:"",documentClose:"",documentOpen:"",enterFullScreen:"\uc804\uccb4\ud654\uba74 \ubcf4\uae30",enterTileView:"",exitFullScreen:"\uc804\uccb4\ud654\uba74 \ucde8\uc18c",exitTileView:"",feedback:"\ud53c\ub4dc\ubc31 \ub0a8\uae30\uae30",hangup:"",invite:"",login:"",logout:"\ub85c\uadf8\uc544\uc6c3",lowerYourHand:"",moreActions:"\ucd94\uac00 \uc561\uc158",mute:"\ub9c8\uc774\ud06c",openChat:"",pip:"",profile:"",raiseHand:"\ub9d0\ud558\uae30 \uc694\uccad/\ud574\uc81c",raiseYourHand:"",Settings:"\uc138\ud2f0",sharedvideo:"",shareRoom:"",shortcuts:"",speakerStats:"\uc811\uc18d\uc790 \ud1b5\uacc4",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"",talkWhileMutedPopup:"",tileViewToggle:"",toggleCamera:"\uce74\uba54\ub77c \ud1a0\u3131",videomute:"",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{},transcribing:{ccButtonTooltip:"",error:"\ub808\ucf54\ub529\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"\ucc38\uc11d\ud560 \uba64\ubc84\ub97c \uae30\ub2e4\ub9ac\ub294 \uc911\uc785\ub2c8\ub2e4...",start:"",stop:"",tr:""},"\x05transcribing":{},userMedia:{androidGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c \ud5c8\ub77d\uc744 \uc120\ud0dd",chromeGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c \ud5c8\ub77d\uc744 \uc120\ud0dd",edgeGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c Yes\ub97c \uc120\ud0dd",electronGrantPermissions:"\uce74\uba54\ub77c\uc640 \ub9c8\uc774\ud06c \uc0ac\uc6a9 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\uc2ed\uc2dc\uc624",firefoxGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c \uc120\ud0dd\ub41c \ub514\ubc14\uc774\uc2a4 \uacf5\uc720\ub97c \uc120\ud0dd",iexplorerGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c OK\ub97c \uc120\ud0dd",nwjsGrantPermissions:"\uce74\uba54\ub77c\uc640 \ub9c8\uc774\ud06c \uc0ac\uc6a9 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\uc2ed\uc2dc\uc624",operaGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c \ud5c8\ub77d\uc744 \uc120\ud0dd","react-nativeGrantPermissions":"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c \ud5c8\ub77d\uc744 \uc120\ud0dd",safariGrantPermissions:"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uad8c\ud55c\uc744 \uc694\uccad\ud560 \ub54c OK\ub97c \uc120\ud0dd"},videoSIPGW:{busy:"\uc790\uc6d0\uc744 \ud655\ubcf4\ud558\uae30 \uc704\ud574 \ub178\ub825\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \uba87 \ubd84 \ud6c4\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uc2ed\uc2dc\uc624.",busyTitle:"\uc11c\ube44\uc2a4\uac00 \ud604\uc7ac \uc0ac\uc6a9 \uc911\uc785\ub2c8\ub2e4",errorAlreadyInvited:"{{displayName}} \uc774\ubbf8 \ucd08\ub300\ub418\uc5c8\uc2b5\ub2c8\ub2e4",errorInvite:"\ud68c\uc758\uac00 \uc544\uc9c1 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4 \ud574\uc8fc\uc2ed\uc2dc\uc624.",errorInviteFailed:"\ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub178\ub825\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4 \ud574\uc8fc\uc2ed\uc2dc\uc624.",errorInviteFailedTitle:"{{displayName}} \ucd08\ub300 \uc2e4\ud328",errorInviteTitle:"\ucd08\ub300\ud558\ub294 \uc911\uc5d0 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4",pending:"{{displayName}} \uc774\ubbf8 \ucd08\ub300\ub418\uc5c8\uc2b5\ub2c8\ub2e4"},videoStatus:{audioOnly:"\uc624\ub514\uc624 \uc804\uc6a9",audioOnlyExpanded:"",callQuality:"",hd:"HD",highDefinition:"\uace0\ud574\uc0c1\ub3c4",labelTooiltipNoVideo:"\ube44\ub514\uc624 \uc5c6\uc74c",labelTooltipAudioOnly:"\uc624\ub514\uc624 \uc804\uc6a9 \ubaa8\ub4dc \uc0ac\uc6a9",ld:"LD",lowDefinition:"\uc800\ud654\uc9c8",onlyAudioAvailable:"\uc624\ub514\uc624\ub9cc \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4",onlyAudioSupported:"\uc774 \ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c\ub294 \uc624\ub514\uc624\ub9cc \uc9c0\uc6d0\ud569\ub2c8\ub2e4",p2pEnabled:"Peer to Peer \uc0ac\uc6a9 \uac00\ub2a5",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\uace0\ud654\uc9c8\uc744 \uc120\ud638\ud569\ub2c8\ub2e4",sd:"SD",standardDefinition:"\ud45c\uc900 \ud574\uc0c1\ub3c4"},videothumbnail:{domute:"\uc74c\uc18c\uac70",flip:"\ud50c\ub9bd",kick:"\ub0b4\ubcf4\ub0b4\uae30",moderator:"",mute:"",muted:"\uc74c\uc18c\uac70\ub428",remoteControl:"\uc6d0\uaca9 \uc81c\uc5b4",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"\uac00\uc785",roomname:"\ubc29 \uc774\ub984 \uc785\ub825"},appDescription:"\uc804\uccb4 \ud300\uacfc \ud654\uc0c1 \ucc44\ud305\uc744\ud558\uc2ed\uc2dc\uc624. \ub2f9\uc2e0\uc774 \uc544\ub294 \ubaa8\ub4e0 \uc0ac\ub78c\ub4e4\uc744 \ucd08\ub300\ud558\uc2ed\uc2dc\uc624. {{app}}\uc740 \uc644\uc804\ud788 \uc554\ud638\ud654 \ub41c 100 % \uc624\ud508 \uc18c\uc2a4 \ud654\uc0c1 \ud68c\uc758 \uc194\ub8e8\uc158\uc73c\ub85c \uacc4\uc815 \uc5c6\uc774\ub3c4 \ud558\ub8e8 \uc885\uc77c, \ub9e4\uc77c \ubb34\ub8cc\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.",audioVideoSwitch:{audio:"\uc74c\uc131",video:"\ube44\ub514\uc624"},calendar:"",connectCalendarButton:"",connectCalendarText:"",enterRoomTitle:"",go:"\uacc4\uc18d",join:"\uac00\uc785",info:"",privacy:"\uac1c\uc778\uc815\ubcf4",recentList:"",recentListDelete:"",recentListEmpty:"",reducedUIText:"",roomname:"\ubc29 \uc774\ub984 \uc785\ub825",roomnameHint:"",sendFeedback:"",terms:"\uc774\uc6a9\uc57d\uad00",title:""}}},666,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"",af:"",az:"",bg:"",cs:"",de:"",el:"",eo:"",es:"",fr:"",hy:"",it:"",ja:"",ko:"",nb:"",oc:"",pl:"",ptBR:"",ru:"",sk:"",sl:"",sv:"",tr:"",vi:"",zhCN:""}},667,[]); -__d(function(e,n,r,t,i,o,a){i.exports={addPeople:{add:"Uitnodigen",countryNotSupported:"Deze bestemming wordt nog niet ondersteund.",countryReminder:"Belt u naar een bestemming buiten de Verenigde Staten? Vergeet dan niet de landcode te gebruiken.",disabled:"U kunt geen personen uitnodigen.",failedToAdd:"Het toevoegen van leden is mislukt",footerText:"Uitgaande oproep is uitgeschakeld.",loading:"Personen en telefoonnummers zoeken",loadingNumber:"Telefoonnummer valideren",loadingPeople:"Personen zoeken om uit te nodigen",noResults:"Geen resultaten die overeenkomen met de zoekopdracht",noValidNumbers:"Voer een telefoonnummer in",searchNumbers:"Telefoonnummers toevoegen",searchPeople:"Personen zoeken",searchPeopleAndNumbers:"Personen zoeken of hun telefoonnummers toevoegen",telephone:"Telefoonnummer: {{number}}",title:"Personen uitnodigen voor deze vergadering"},audioDevices:{bluetooth:"Bluetooth",headphones:"Hoofdtelefoon",phone:"Telefoon",speaker:"Spreker"},audioOnly:{audioOnly:"Alleen audio"},calendarSync:{addMeetingURL:"Een link naar de vergadering toevoegen",confirmAddLink:"Wilt u een Jitsi-link naar deze gebeurtenis toevoegen?",error:{appConfiguration:"De Agenda-integratie is niet naar behoren geconfigureerd.",generic:"Er is een fout opgetreden. Controleer de agenda-instellingen of vernieuw de agenda.",notSignedIn:"Er is een fout opgetreden tijdens de verificatie voor het weergeven van agendagebeurtenissen. Controleer de agenda-instellingen en probeer u opnieuw aan te melden."},join:"Deelnemen",joinTooltip:"Deelnemen aan de vergadering",nextMeeting:"volgende vergadering",noEvents:"Er zijn geen gebeurtenissen gepland.",ongoingMeeting:"actieve vergadering",permissionButton:"Instellingen openen",permissionMessage:"U hebt een machtiging voor Agenda nodig om uw vergaderingen weer te geven in de app.",refresh:"Agenda vernieuwen",today:"Vandaag"},chat:{error:"Fout: uw bericht \"{{originalText}}\" is niet verzonden. Reden: {{error}}",messagebox:"Typ een bericht",nickname:{popover:"Kies een bijnaam",title:"Voer een bijnaam in om de chatfunctie te gebruiken"},title:"Chat"},connectingOverlay:{joiningRoom:"Er wordt verbinding gemaakt met de vergadering..."},connection:{ATTACHED:"Bijgesloten",AUTHENTICATING:"Verifi\xebren",AUTHFAIL:"Verificatie mislukt",CONNECTED:"Verbonden",CONNECTING:"Verbinding maken",CONNFAIL:"Verbinding mislukt",DISCONNECTED:"Verbinding verbroken",DISCONNECTING:"Verbinding verbreken",ERROR:"Fout",RECONNECTING:"Er is een netwerkprobleem opgetreden. Er wordt opnieuw verbinding gemaakt..."},connectionindicator:{address:"Adres:",bandwidth:"Geschatte bandbreedte:",bitrate:"Bitrate:",bridgeCount:"Aantal servers:",connectedTo:"Verbonden met:",framerate:"Framesnelheid:",less:"Minder weergeven",localaddress:"Lokaal adres:",localaddress_plural:"Lokale adressen:",localport:"Lokale poort:",localport_plural:"Lokale poorten:",more:"Meer weergeven",packetloss:"Pakketverlies:",quality:{good:"Goed",inactive:"Inactief",lost:"Verbroken",nonoptimal:"Niet optimaal",poor:"Slecht"},remoteaddress:"Extern adres:",remoteaddress_plural:"Externe adressen:",remoteport:"Externe poort:",remoteport_plural:"Externe poorten:",resolution:"Resolutie:",status:"Verbinding:",transport:"Transport:",transport_plural:"Transporten:",turn:" (draaien)"},dateUtils:{earlier:"Eerder",today:"Vandaag",yesterday:"Gisteren"},deepLinking:{appNotInstalled:"U hebt de mobiele app {{app}} nodig om op uw telefoon aan deze vergadering deel te nemen.",description:"Gebeurt er niets? Er is geprobeerd uw vergadering te starten in de desktop-app {{app}}. Probeer het opnieuw of start de vergadering in de web-app {{app}}.",descriptionWithoutWeb:"",downloadApp:"De app downloaden",launchWebButton:"Starten via web",openApp:"Doorgaan naar de app",title:"Uw vergadering wordt gestart in {{app}}...",tryAgainButton:"Opnieuw proberen op desktop"},defaultLink:"bijv. {{url}}",deviceError:{cameraError:"Geen toegang tot de camera",cameraPermission:"Fout bij het verkrijgen van toestemming voor de camera",microphoneError:"Geen toegang tot de microfoon",microphonePermission:"Fout bij het verkrijgen van toestemming voor de microfoon"},deviceSelection:{noPermission:"Geen toestemming verleend",previewUnavailable:"Voorbeeld niet beschikbaar",selectADevice:"Selecteer een apparaat",testAudio:"Een testgeluid afspelen"},dialog:{accessibilityLabel:{liveStreaming:"Livestream"},allow:"Toestaan",alreadySharedVideoMsg:"Er wordt al een video gedeeld door een ander lid. In deze vergadering kan slechts \xe9\xe9n video tegelijkertijd worden gedeeld.",alreadySharedVideoTitle:"Slechts \xe9\xe9n gedeelde video tegelijkertijd toegestaan",applicationWindow:"Toepassingsvenster",Back:"Terug",cameraConstraintFailedError:"Uw camera voldoet niet aan alle vereiste beperkingen.",cameraNotFoundError:"Camera niet gevonden.",cameraNotSendingData:"Er is geen toegang tot uw camera verkregen. Controleer of dit apparaat wordt gebruikt door een andere toepassing, selecteer een ander apparaat in de instellingen of laad de toepassing opnieuw.",cameraNotSendingDataTitle:"Geen toegang tot de camera",cameraPermissionDeniedError:"U hebt geen toestemming verleend voor het gebruik van de camera. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet zien. Gebruik de cameraknop in de adresbalk als u dit wilt wijzigen.",cameraUnknownError:"Kan de camera om een onbekende reden niet gebruiken.",cameraUnsupportedResolutionError:"De camera biedt geen ondersteuning voor de vereiste videoresolutie.",Cancel:"Annuleren",close:"Sluiten",conferenceDisconnectMsg:"Controleer de netwerkverbinding. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...",conferenceDisconnectTitle:"De verbinding is verbroken.",conferenceReloadMsg:"We proberen het probleem op te lossen. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...",conferenceReloadTitle:"Er is iets misgegaan.",confirm:"Bevestigen",confirmNo:"Nee",confirmYes:"Ja",connectError:"Er is iets misgegaan. Er kan geen verbinding met de vergadering worden gemaakt.",connectErrorWithMsg:"Er is iets misgegaan. Er kan geen verbinding met de vergadering worden gemaakt: {{msg}}",connecting:"Verbinding maken",contactSupport:"Contact opnemen met ondersteuning",copy:"Kopi\xebren",dismiss:"Negeren",displayNameRequired:"Weergavenaam is vereist",done:"Gereed",enterDisplayName:"Voer uw weergavenaam in",error:"Fout",externalInstallationMsg:"U moet onze extensie voor het delen van het bureaublad installeren.",externalInstallationTitle:"Extensie vereist",goToStore:"Naar de webwinkel",gracefulShutdown:"Onze service is momenteel niet beschikbaar wegens onderhoud. Probeer het later opnieuw.",IamHost:"Ik ben de host",incorrectRoomLockPassword:"",incorrectPassword:"Gebruikersnaam of wachtwoord onjuist",inlineInstallationMsg:"U moet onze extensie voor het delen van het bureaublad installeren.",inlineInstallExtension:"Nu installeren",internalError:"Er is iets misgegaan. De volgende fout is opgetreden: {{error}}",internalErrorTitle:"Interne fout",kickMessage:"U bent uit de vergadering verwijderd.",kickParticipantButton:"Verwijderen",kickParticipantDialog:"Weet u zeker dat u deze deelnemer wilt verwijderen?",kickParticipantTitle:"Dit lid verwijderen?",kickTitle:"Verwijderd uit vergadering",liveStreaming:"Livestream",liveStreamingDisabledForGuestTooltip:"Gasten kunnen livestream niet starten.",liveStreamingDisabledTooltip:"Het starten van de livestream is uitgeschakeld.",lockMessage:"Het vergrendelen van de vergadering is mislukt.",lockRoom:"Wachtwoord voor vergadering toevoegen",lockTitle:"Vergrendelen mislukt",logoutQuestion:"Weet u zeker dat u zich wilt afmelden en de vergadering wilt stoppen?",logoutTitle:"Afmelden",maxUsersLimitReached:"Het maximale aantal leden is bereikt. De vergadering is vol. Neem contact op met de eigenaar van de vergadering of probeer het later opnieuw.",maxUsersLimitReachedTitle:"Maximaal aantal leden bereikt",micConstraintFailedError:"Uw microfoon voldoet niet aan alle vereiste beperkingen.",micNotFoundError:"Microfoon niet gevonden.",micNotSendingData:"Er is geen toegang tot uw microfoon verkregen. Selecteer een ander apparaat in de instellingen of laad de toepassing opnieuw.",micNotSendingDataTitle:"Geen toegang tot de microfoon",micPermissionDeniedError:"U hebt geen toestemming verleend voor het gebruik van de microfoon. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet horen. Gebruik de cameraknop in de adresbalk als u dit wilt wijzigen.",micUnknownError:"Kan de microfoon om een onbekende reden niet gebruiken.",muteParticipantBody:"U kunt het dempen van anderen niet opheffen, maar zij kunnen dit wel op elk gewenst moment voor zichzelf doen.",muteParticipantButton:"Dempen",muteParticipantDialog:"Weet u zeker dat u deze deelnemer wilt dempen? U kunt het dempen niet opheffen, maar dit kan deze persoon wel op elk gewenst moment zelf doen.",muteParticipantTitle:"Dit lid dempen?",Ok:"OK",passwordLabel:"Wachtwoord",passwordNotSupported:"Het instellen van een wachtwoord voor een vergadering wordt niet ondersteund.",passwordNotSupportedTitle:"Wachtwoord niet ondersteund",passwordRequired:"Wachtwoord vereist",popupError:"Er wordt een pop-upvenster van deze site geblokkeerd door uw browser. Ga naar de beveiligingsinstellingen van uw browser, schakel pop-upvensters in en probeer het opnieuw.",popupErrorTitle:"Pop-up wordt geblokkeerd",recording:"Opnemen",recordingDisabledForGuestTooltip:"Gasten kunnen geen opnamen starten.",recordingDisabledTooltip:"Het starten van opnamen is uitgeschakeld.",rejoinNow:"Nu opnieuw deelnemen",remoteControlAllowedMessage:"{{user}} heeft uw aanvraag voor extern beheer geaccepteerd.",remoteControlDeniedMessage:"{{user}} heeft uw aanvraag voor extern beheer geweigerd.",remoteControlErrorMessage:"Er is een fout opgetreden tijdens het aanvragen van machtigingen voor extern beheer bij {{user}}.",remoteControlRequestMessage:"Wilt u {{user}} een machtiging verlenen om uw bureaublad extern te beheren?",remoteControlShareScreenWarning:"Let op: als u 'Toestaan' kiest, wordt uw scherm gedeeld.",remoteControlStopMessage:"De sessie voor extern beheer is be\xebindigd.",remoteControlTitle:"Extern beheer van bureaublad",Remove:"Verwijderen",removePassword:"Wachtwoord verwijderen",removeSharedVideoMsg:"Weet u zeker dat u uw gedeelde video wilt verwijderen?",removeSharedVideoTitle:"Gedeelde video verwijderen",reservationError:"Fout in reserveringssysteem",reservationErrorMsg:"Foutcode: {{code}}, bericht: {{msg}}",retry:"Opnieuw proberen",screenSharingFailedToInstall:"De installatie van de extensie voor het delen van het scherm is mislukt.",screenSharingFailedToInstallTitle:"Installatie van extensie voor het delen van het scherm is mislukt",screenSharingFirefoxPermissionDeniedError:"Er is iets misgegaan tijdens het delen van uw scherm. Controleer of u hier toestemming voor hebt verleend.",screenSharingFirefoxPermissionDeniedTitle:"Scherm delen kan niet worden gestart.",screenSharingPermissionDeniedError:"Er is iets misgegaan met de machtigingen voor de extensie voor het delen van het scherm. Laad de toepassing opnieuw en probeer het nog eens.",serviceUnavailable:"Service niet beschikbaar",sessTerminated:"Gesprek be\xebindigd",Share:"Delen",shareVideoLinkError:"Geef een juiste YouTube-link op.",shareVideoTitle:"Een video delen",shareYourScreen:"Uw scherm delen",shareYourScreenDisabled:"Scherm delen is uitgeschakeld.",shareYourScreenDisabledForGuest:"Gasten kunnen hun scherm niet delen.",startLiveStreaming:"Livestream starten",startRecording:"Opname starten",startRemoteControlErrorMessage:"Er is een fout opgetreden tijdens het starten van de sessie voor extern beheer.",stopLiveStreaming:"Livestream stoppen",stopRecording:"Opname stoppen",stopRecordingWarning:"Weet u zeker dat u de opname wilt stoppen?",stopStreamingWarning:"Weet u zeker dat u de livestream wilt stoppen?",streamKey:"Sleutel voor livestream",Submit:"Verzenden",thankYou:"Bedankt voor het gebruik van {{appName}}.",token:"token",tokenAuthFailed:"U hebt geen toestemming om aan dit gesprek deel te nemen.",tokenAuthFailedTitle:"Verificatie mislukt",transcribing:"Transcriberen",unlockRoom:"Wachtwoord voor vergadering verwijderen",userPassword:"gebruikerswachtwoord",WaitForHostMsg:"De vergadering {{room}} is nog niet gestart. Verifieer de vergadering als u de host bent. Anders wacht u tot de host aanwezig is.",WaitForHostMsgWOk:"De vergadering {{room}} is nog niet gestart. Als u de host bent, drukt u op OK om te verifi\xebren. Anders wacht u tot de host aanwezig is.",WaitingForHost:"Wachten op de host...",Yes:"Ja",yourEntireScreen:"Uw gehele scherm"},dialOut:{statusMessage:"is nu {{status}}"},feedback:{average:"Gemiddeld",bad:"Slecht",detailsLabel:"We horen er graag meer over.",good:"Goed",rateExperience:"Beoordeel uw ervaring tijdens de vergadering",veryBad:"Zeer slecht",veryGood:"Zeer goed"},incomingCall:{answer:"Beantwoorden",audioCallTitle:"Inkomende oproep",decline:"Negeren",productLabel:"vanuit Jitsi Meet",videoCallTitle:"Inkomende video-oproep"},info:{accessibilityLabel:"Informatie weergeven",addPassword:"Wachtwoord toevoegen",cancelPassword:"Wachtwoord annuleren",conferenceURL:"Link:",country:"Land",dialANumber:"Als u wilt deelnemen aan de vergadering, belt u een van deze nummers en voert u vervolgens de pincode in.",dialInConferenceID:"Pincode:",dialInNotSupported:"Inbellen wordt momenteel niet ondersteund.",dialInNumber:"Inbellen:",dialInSummaryError:"Fout bij het ophalen van inbelgegevens. Probeer het later opnieuw.",dialInTollFree:"Gratis",genericError:"Er is iets misgegaan.",inviteLiveStream:"Klik op de volgende link als u de livestream van deze vergadering wilt bekijken: {{url}}",invitePhone:"Met \xe9\xe9n druk op de knop inbellen: {{number}},,{{conferenceID}}#\n",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"U bent uitgenodigd om aan een vergadering deel te nemen.",inviteURLFirstPartPersonal:"{{name}} nodigt u uit voor een vergadering.\n",inviteURLSecondPart:"\nDeelnemen aan vergadering:\n{{url}}\n",liveStreamURL:"Livestream:",moreNumbers:"Meer nummers",noNumbers:"Geen inbelnummers.",noPassword:"Geen",noRoom:"Er is geen ruimte opgegeven om naar in te bellen.",numbers:"Inbelnummers",password:"Wachtwoord:",title:"Delen",tooltip:"De link en inbelgegevens voor deze vergadering delen",label:"Vergaderingsgegevens"},inviteDialog:{alertText:"Niet alle deelnemers zijn uitgenodigd.",header:"Uitnodigen",searchCallOnlyPlaceholder:"Telefoonnummer invoeren",searchPeopleOnlyPlaceholder:"Deelnemers zoeken",searchPlaceholder:"Deelnemer of telefoonnummer",send:"Verzenden"},inlineDialogFailure:{msg:"Er is een fout opgetreden.",retry:"Opnieuw proberen",support:"Ondersteuning",supportMsg:"Als dit probleem opnieuw optreedt, neemt u contact op met"},keyboardShortcuts:{focusLocal:"Focus op uw video",focusRemote:"Focus op de video van een andere persoon",fullScreen:"Volledig scherm weergeven of afsluiten",keyboardShortcuts:"Sneltoetsen",localRecording:"Besturingselementen voor lokale opnamen weergeven of verbergen",mute:"Uw microfoon dempen of het dempen opheffen",pushToTalk:"Druk om te spreken",raiseHand:"Uw hand opsteken of laten zakken",showSpeakerStats:"Sprekerstatistieken weergeven",toggleChat:"Chatgesprek openen of sluiten",toggleFilmstrip:"Videominiaturen weergeven of verbergen",toggleScreensharing:"Schakelen tussen camera en het delen van het scherm",toggleShortcuts:"Sneltoetsen weergeven of verbergen",videoMute:"Uw camera starten of stoppen"},liveStreaming:{busy:"Er worden streamingresources vrijgemaakt. Probeer het over enkele minuten opnieuw.",busyTitle:"Alle streamers zijn momenteel bezet",changeSignIn:"Wissel van account.",choose:"Een livestream kiezen",chooseCTA:"Kies een streamingoptie. U bent momenteel aangemeld als {{email}}.",enterStreamKey:"Voer hier de sleutel van YouTube voor de livestream in.",error:"Livestream is mislukt. Probeer het opnieuw.",errorAPI:"Er is een fout opgetreden tijdens het openen van uw YouTube-uitzendingen. Meld u opnieuw aan.",errorLiveStreamNotEnabled:"Livestreaming is niet ingeschakeld voor {{email}}. Schakel livestreaming in of meld u aan bij een account waarvoor livestreaming is ingeschakeld.",expandedOff:"De livestream is gestopt",expandedOn:"De vergadering wordt momenteel gestreamd naar YouTube.",expandedPending:"De livestream wordt gestart...",failedToStart:"Livestream niet gestart",getStreamKeyManually:"Er zijn geen livestreams opgehaald. Haal de sleutel voor uw livestream op uit YouTube.",invalidStreamKey:"De sleutel voor de livestream is mogelijk onjuist.",off:"Livestream gestopt",on:"Livestream",pending:"Livestream starten...",serviceName:"Livestreamservice",signedInAs:"U bent momenteel aangemeld als:",signIn:"Aanmelden via Google",signInCTA:"Meld u aan of voer de sleutel van YouTube voor uw livestream in.",signOut:"Afmelden",start:"Een livestream starten",streamIdHelp:"Wat is dit?",unavailableTitle:"Livestream niet beschikbaar"},localRecording:{clientState:{off:"Uit",on:"Aan",unknown:"Onbekend"},dialogTitle:"Besturingselementen voor lokale opnamen",duration:"Duur",durationNA:"N.v.t.",encoding:"Codering",label:"LOR",labelToolTip:"Lokale opname is ingeschakeld",localRecording:"Lokale opname",me:"Ik",messages:{engaged:"Lokale opname ingeschakeld.",finished:"Opnamesessie {{token}} is voltooid. Stuur het opnamebestand naar de moderator.",finishedModerator:"Opnamesessie {{token}} is voltooid. De opname van de lokale track is opgeslagen. Vraag de andere deelnemers om hun opnamen te verzenden.",notModerator:"U bent niet de moderator. U kunt geen lokale opnamen starten of stoppen."},moderator:"Moderator",no:"Nee",participant:"Deelnemer",participantStats:"Deelnemerstatistieken",sessionToken:"Sessietoken",start:"Opname starten",stop:"Opname stoppen",yes:"Ja"},lockRoomPassword:"",lockRoomPasswordUppercase:"",me:"ik",notify:{connectedOneMember:"{{name}} neemt nu deel aan de vergadering",connectedThreePlusMembers:"{{name}} en {{count}} anderen nemen nu deel aan de vergadering",connectedTwoMembers:"{{first}} en {{second}} nemen nu deel aan de vergadering",disconnected:"verbinding verbroken",focus:"Focus van vergadering",focusFail:"{{component}} is niet beschikbaar. Probeer het over {{ms}} sec. opnieuw.",grantedTo:"Moderatorrechten verleend aan {{to}}.",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Ik",moderator:"Moderatorrechten verleend.",muted:"U hebt het gesprek gedempt gestart.",mutedTitle:"U bent gedempt.",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} wil spreken.",somebody:"Iemand",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Helaas zal uw {{appName}}-ervaring hier niet optimaal zijn. Mogelijk wordt dit in de toekomst verbeterd, maar tot die tijd vragen we u een van de volledig ondersteunde browsers te gebruiken.",suboptimalExperienceTitle:"Browserwaarschuwing",unmute:"",newDeviceCameraTitle:"Nieuwe camera gedetecteerd",newDeviceAudioTitle:"Nieuw audioapparaat gedetecteerd",newDeviceAction:"Gebruik"},passwordSetRemotely:"ingesteld door een ander lid",passwordDigitsOnly:"Maximaal {{number}} cijfers",poweredby:"powered by",presenceStatus:{busy:"Bezet",calling:"Bellen...",connected:"Verbonden",connecting:"Verbinding maken...",connecting2:"Verbinding maken*...",disconnected:"Verbinding verbroken",expired:"Verlopen",ignored:"Genegeerd",initializingCall:"Gesprek starten...",invited:"Uitgenodigd",rejected:"Geweigerd",ringing:"Gaat over..."},profile:{setDisplayNameLabel:"Uw weergavenaam instellen",setEmailInput:"E-mailadres invoeren",setEmailLabel:"Uw gravatar voor e-mail instellen",title:"Profiel"},recording:{authDropboxText:"Uploaden naar Dropbox",availableSpace:"Beschikbare ruimte: {{spaceLeft}} MB (circa {{duration}} minuten aan opname)",beta:"B\xc8TA",busy:"Er worden opnameresources vrijgemaakt. Probeer het over enkele minuten opnieuw.",busyTitle:"Alle opnamefuncties zijn momenteel bezet",error:"Opname is mislukt. Probeer het opnieuw.",expandedOff:"Opname is gestopt",expandedOn:"De vergadering wordt momenteel opgenomen.",expandedPending:"Opname wordt gestart...",failedToStart:"Opname starten is mislukt",fileSharingdescription:"Opname delen met deelnemers aan vergadering",live:"LIVE",loggedIn:"Aangemeld als {{userName}}",off:"Opname gestopt",on:"Opnemen",pending:"Voorbereiden op opname van vergadering...",rec:"OPN.",serviceDescription:"Uw opname wordt opgeslagen door de opnameservice",serviceName:"Opnameservice",signIn:"Aanmelden",signOut:"Afmelden",unavailable:"{{serviceName}} is momenteel niet beschikbaar. Er wordt aan een oplossing gewerkt. Probeer het later opnieuw.",unavailableTitle:"Opname niet beschikbaar"},sectionList:{pullToRefresh:"Naar beneden slepen om te vernieuwen"},settings:{calendar:{about:"De agenda-integratie van {{appName}} wordt gebruikt voor een veilige toegang tot uw agenda, zodat geplande gebeurtenissen kunnen worden geraadpleegd.",disconnect:"Verbinding verbreken",microsoftSignIn:"Aanmelden via Microsoft",signedIn:"Agendagebeurtenissen voor {{email}} worden geraadpleegd. Klik op de knop 'Verbinding verbreken' hieronder om de toegang tot agendagebeurtenissen in te trekken.",title:"Agenda"},devices:"Apparaten",followMe:"Iedereen volgt mij",language:"Taal",loggedIn:"Aangemeld als {{name}}",moderator:"Moderator",more:"Meer",name:"Naam",noDevice:"Geen",selectAudioOutput:"Audio-uitvoer",selectCamera:"Camera",selectMic:"Microfoon",startAudioMuted:"Iedereen start gedempt",startVideoMuted:"Iedereen start verborgen",title:"Instellingen"},settingsView:{alertOk:"OK",alertTitle:"Waarschuwing",alertURLText:"De ingevoerde server-URL is ongeldig",buildInfoSection:"Buildgegevens",conferenceSection:"Conferentie",displayName:"Weergavenaam",email:"E\u2011mail",header:"Instellingen",profileSection:"Profiel",serverURL:"Server-URL",startWithAudioMuted:"Starten met audio gedempt",startWithVideoMuted:"Starten met video gedempt",version:"Versie"},share:{dialInfoText:"",mainText:"Klik op de volgende link om deel te nemen aan de vergadering:\n{{roomUrl}}"},speaker:"Spreker",speakerStats:{hours:"{{count}} u",minutes:"{{count}} m",name:"Naam",seconds:"{{count}} s",speakerStats:"Sprekerstatistieken",speakerTime:"Sprekertijd"},startupoverlay:{policyText:" ",title:"{{app}} heeft toegang tot uw microfoon en camera nodig."},suspendedoverlay:{rejoinKeyTitle:"Opnieuw deelnemen",text:"Druk op de knop Opnieuw deelnemen om opnieuw verbinding te maken.",title:"Uw videogesprek is onderbroken, omdat de slaapstand van de computer is geactiveerd."},toolbar:{accessibilityLabel:{audioOnly:"Alleen audio schakelen",audioRoute:"Het afspeelapparaat selecteren",callQuality:"Kwaliteit van gesprek beheren",cc:"Ondertiteling schakelen",chat:"Chatvenster schakelen",document:"Gedeeld document schakelen",feedback:"Feedback achterlaten",fullScreen:"Volledig scherm schakelen",hangup:"Het gesprek verlaten",invite:"Personen uitnodigen",kick:"Deelnemer verwijderen",localRecording:"Besturingselementen voor lokale opname schakelen",lockRoom:"Wachtwoord voor vergadering schakelen",moreActions:"Menu 'Meer acties' schakelen",moreActionsMenu:"Menu 'Meer acties'",mute:"Audio dempen schakelen",pip:"Beeld-in-beeld-modus schakelen",profile:"Uw profiel bewerken",raiseHand:"Hand opsteken schakelen",recording:"Opname schakelen",remoteMute:"Deelnemer dempen",Settings:"Instellingen schakelen",sharedvideo:"YouTube-video delen schakelen",shareRoom:"Iemand uitnodigen",shareYourScreen:"Scherm delen schakelen",shortcuts:"Sneltoetsen schakelen",show:"",speakerStats:"Sprekerstatistieken schakelen",tileView:"Tegelweergave schakelen",toggleCamera:"Camera schakelen",videomute:"Video dempen schakelen",videoblur:""},addPeople:"Personen aan uw gesprek toevoegen",audioOnlyOff:"Modus 'Alleen audio' uitschakelen",audioOnlyOn:"Modus 'Alleen audio' inschakelen",audioRoute:"Het afspeelapparaat selecteren",authenticate:"Verifi\xebren",callQuality:"Kwaliteit van gesprek beheren",chat:"Chat openen/sluiten",closeChat:"Chat sluiten",documentClose:"Gedeeld document sluiten",documentOpen:"Gedeeld document openen",enterFullScreen:"Volledig scherm weergeven",enterTileView:"Tegelweergave openen",exitFullScreen:"Volledig scherm sluiten",exitTileView:"Tegelweergave sluiten",feedback:"Feedback achterlaten",hangup:"Verlaten",invite:"Personen uitnodigen",login:"Aanmelden",logout:"Afmelden",lowerYourHand:"Uw hand laten zakken",moreActions:"Meer acties",mute:"Dempen/dempen opheffen",openChat:"Chat openen",pip:"Beeld-in-beeld-modus activeren",profile:"Uw profiel bewerken",raiseHand:"Uw hand opsteken/laten zakken",raiseYourHand:"Uw hand opsteken",Settings:"Instellingen",sharedvideo:"Een YouTube-video delen",shareRoom:"Iemand uitnodigen",shortcuts:"Sneltoetsen weergeven",speakerStats:"Sprekerstatistieken",startScreenSharing:"Scherm delen starten",startSubtitles:"Ondertiteling starten",stopScreenSharing:"Scherm delen stoppen",stopSubtitles:"Ondertiteling stoppen",stopSharedVideo:"YouTube-video stoppen",talkWhileMutedPopup:"Wilt u spreken? U bent gedempt.",tileViewToggle:"Tegelweergave schakelen",toggleCamera:"Camera schakelen",videomute:"Camera starten/stoppen",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Ondertiteling starten/stoppen",error:"Transcriberen is mislukt. Probeer het opnieuw.",expandedLabel:"Transcriberen is momenteel ingeschakeld",failedToStart:"Transcriberen starten is mislukt",labelToolTip:"De vergadering wordt getranscribeerd",off:"Transcriberen gestopt",pending:"Voorbereiden op transcriberen van vergadering...",start:"Weergave van ondertiteling starten",stop:"Weergave van ondertiteling stoppen",tr:"TR"},userMedia:{androidGrantPermissions:"Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.",chromeGrantPermissions:"Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.",edgeGrantPermissions:"Selecteer Ja wanneer u in de browser om machtigingen wordt gevraagd.",electronGrantPermissions:"Verleen machtigingen voor het gebruik van uw camera en microfoon",firefoxGrantPermissions:"Selecteer Geselecteerd apparaat delen wanneer u in de browser om machtigingen wordt gevraagd.",iexplorerGrantPermissions:"Selecteer OK wanneer u in de browser om machtigingen wordt gevraagd.",nwjsGrantPermissions:"Verleen machtigingen voor het gebruik van uw camera en microfoon",operaGrantPermissions:"Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.","react-nativeGrantPermissions":"Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.",safariGrantPermissions:"Selecteer OK wanneer u in de browser om machtigingen wordt gevraagd."},videoSIPGW:{busy:"Er worden resources vrijgemaakt. Probeer het over enkele minuten opnieuw.",busyTitle:"De ruimteservice is momenteel bezet",errorAlreadyInvited:"{{displayName}} is al uitgenodigd",errorInvite:"Vergadering is nog niet van start gegaan. Probeer het later opnieuw.",errorInviteFailed:"Er wordt aan een oplossing gewerkt. Probeer het later opnieuw.",errorInviteFailedTitle:"Het uitnodigen van {{displayName}} is mislukt",errorInviteTitle:"Fout bij uitnodiging voor ruimte",pending:"{{displayName}} is uitgenodigd"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"De modus 'Alleen audio' is geactiveerd. In deze modus wordt er bandbreedte bespaard, maar ziet u geen video's van anderen.",callQuality:"Kwaliteit van gesprek",hd:"HD",highDefinition:"Hoge resolutie",labelTooiltipNoVideo:"Geen video",labelTooltipAudioOnly:"Modus 'Alleen audio' ingeschakeld",ld:"LD",lowDefinition:"Lage resolutie",onlyAudioAvailable:"Alleen audio is beschikbaar",onlyAudioSupported:"In deze browser wordt alleen audio ondersteund.",p2pEnabled:"Peer-to-peer ingeschakeld",p2pVideoQualityDescription:"In de peer-to-peer-modus kan de kwaliteit van het gesprek alleen worden geschakeld tussen hoge resolutie en alleen audio. Andere instellingen zijn pas beschikbaar nadat peer-to-peer is gesloten.",recHighDefinitionOnly:"Voorkeur voor hoge resolutie",sd:"SD",standardDefinition:"Standaardresolutie"},videothumbnail:{domute:"Dempen",flip:"Omslaan",kick:"Verwijderen",moderator:"Moderator",mute:"Lid is gedempt",muted:"Gedempt",remoteControl:"Extern beheer",show:"",videomute:"Lid heeft de camera gestopt"},welcomepage:{accessibilityLabel:{join:"Tik om deel te nemen",roomname:"Naam van ruimte invoeren"},appDescription:"U kunt nu videochatten met het hele team. U kunt uitnodigen wie u maar wilt. {{app}} is een volledig versleutelde, 100% open-sourceoplossing voor videovergaderingen die u wanneer en zo lang u maar wilt gratis kunt gebruiken. Hier hebt u geen account voor nodig.",audioVideoSwitch:{audio:"Spraak",video:"Video"},calendar:"Agenda",connectCalendarButton:"Uw agenda koppelen",connectCalendarText:"",enterRoomTitle:"Een nieuwe vergadering starten",go:"Start",join:"Deelnemen",info:"Informatie",privacy:"Privacy",recentList:"Recent",recentListDelete:"Verwijderen",recentListEmpty:"Uw lijst met recente items is momenteel leeg. Als u chat met uw team, worden alle recente vergaderingen hier weergegeven.",reducedUIText:"",roomname:"Naam van ruimte invoeren",roomnameHint:"Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar geef de naam wel door aan de andere deelnemers, zodat zij dezelfde naam kunnen invoeren.",sendFeedback:"Feedback verzenden",terms:"Voorwaarden",title:"Veilige, volledig uitgeruste en geheel gratis videovergaderingen"}}},668,[]); -__d(function(n,a,s,r,e,i,o){e.exports={en:"Angl\xe9s",af:"Afrikaans",bg:"Bulgar",ca:"",cs:"Ch\xe8c",de:"Aleman",el:"Gr\xe8c",enGB:"",eo:"Esperanto",es:"Castelhan",esUS:"",fi:"",fr:"Franc\xe9s",frCA:"",hr:"",hy:"Armenian",it:"Italian",ja:"Japon\xe9s",ko:"Corean",nl:"",oc:"Occitan",pl:"Polon\xe9s",ptBR:"Portugu\xe9s (Brasil)",ru:"Rus",sv:"Sued\xe9s",tr:"Turc",vi:"Vietnamian",zhCN:"Chin\xe9s (China)",zhTW:""}},669,[]); -__d(function(a,e,r,n,t,i,o){t.exports={addPeople:{add:"Convidar",countryNotSupported:"S\xe8m pas encara compatibles amb aquesta destinacion.",countryReminder:"Sonatz def\xf2ra los US\u202f? Merc\xe9s de vos assegurar de comen\xe7ar amb lo c\xf2di pa\xeds.",disabled:"Pod\xe8tz pas convidar de monde.",failedToAdd:"Frac\xe0s de l'ajust de participants",footerText:"Sonadas ext. desactivadas.",loading:"Rec\xe8rca de monde e de num\xe8ro de telef\xf2n",loadingNumber:"Validacion del num\xe8ro de telef\xf2n",loadingPeople:"Rec\xe8rca de monde de convidar",noResults:"Pas cap de resultat trobat",noValidNumbers:"Picatz lo num\xe8ro de telef\xf2n",searchNumbers:"Apondre de num\xe8ros de telef\xf2n",searchPeople:"Cercar de monde",searchPeopleAndNumbers:"Cercar de monde o apondre lor num\xe8ros de telef\xf2n",telephone:"Telef\xf2n\u202f: {{number}}",title:"Convidatz de monde a v\xf2stra confer\xe9ncia"},audioDevices:{bluetooth:"Bluetooth",headphones:"Escotadors",phone:"Telef\xf2n",speaker:"Nautparlaire",none:""},audioOnly:{audioOnly:"Benda passanta febla"},calendarSync:{addMeetingURL:"Ajustar un ligam de re\xfcnion",confirmAddLink:"Vol\xe8tz ajustar un ligam Jitsi cap a aqueste eveniment ?",error:{appConfiguration:"L\u2019integracion del calendi\xe8r es pas corr\xe8ctament configurada.",generic:"Una error s\u2019es producha. Verificatz v\xf2stres param\xe8tres de calendi\xe8r o ensajatz de l\u2019actualizar.",notSignedIn:"Una error s\u2019es producha pendent l\u2019autentificacion per veire los eveniments del calendi\xe8r. Verificatz los param\xe8tres del calendi\xe8r e connectatz-vos tornamai."},join:"J\xf3nher",joinTooltip:"Rej\xf3nher la confer\xe9ncia",nextMeeting:"confer\xe9ncia venenta",noEvents:"Cap d\u2019eveniments venents pas prevists.",ongoingMeeting:"confer\xe9ncia en cors",permissionButton:"Dobrir los param\xe8tres",permissionMessage:"La permission pel calendari es necess\xe0ria per listar v\xf2stres RDV dins l\u2019aplicacion.",refresh:"Actualizar lo calendari",today:"U\xe8i"},chat:{error:"Error : v\xf2stre messatge \xab {{originalText}} \xbb es pas estat enviat. Rason : {{error}}",messagebox:"Picatz un messatge",nickname:{popover:"Causiss\xe8tz un escais",title:"Picatz un escais-nom per utilizar la messatjari\xe1"},title:"Messatjari\xe1"},connectingOverlay:{joiningRoom:"Connexion a v\xf2stra re\xfcnion\u2026"},connection:{ATTACHED:"Estacada",AUTHENTICATING:"Autentificacion en cors",AUTHFAIL:"Frac\xe0s de l'autentificacion",CONNECTED:"Connectat",CONNECTING:"Connexion en cors",CONNFAIL:"Frac\xe0s de la connexion",DISCONNECTED:"Desconnectat",DISCONNECTING:"Desconnexion en cors",ERROR:"Error",RECONNECTING:"Un probl\xe8ma ret s'es produita. Reconnexion en cors..."},connectionindicator:{address:"Adre\xe7a\xa0:",bandwidth:"Benda passanta estimada :",bitrate:"Debit\xa0:",bridgeCount:"Nombre de servidor\xa0:",connectedTo:"Connectat a\xa0:",framerate:"Frequ\xe9ncia imatge :",less:"Amagar lo detalh",localaddress:"Adre\xe7a locala :",localaddress_plural:"Adre\xe7as localas :",localport:"P\xf2rt local :",localport_plural:"P\xf2rts locals :",more:"Ne veire mai",packetloss:"P\xe8rda de paquets :",quality:{good:"Bona",inactive:"Inactiu",lost:"Perdut",nonoptimal:"Pas optimal",poor:"Marrit"},remoteaddress:"Adre\xe7a distanta :",remoteaddress_plural:"Adre\xe7as distantas :",remoteport:"P\xf2rt distant\xa0:",remoteport_plural:"P\xf2rts distants :",resolution:"Resolucion\xa0:",status:"Connexion :",transport:"Transp\xf2rt :"},dateUtils:{earlier:"Mai d\u2019ora",today:"U\xe8i",yesterday:"I\xe8r"},deepLinking:{appNotInstalled:"Vos cal l\u2019aplicacion {{app}} per j\xf3nher la confer\xe9ncia amb v\xf2stre mobil.",description:"Res s\u2019es pas passat\u202f? Av\xe8m ensajat de lan\xe7ar la confer\xe9ncia dins l\u2019aplicacion {{app}}. Merc\xe9s de tornar ensajar o de la lan\xe7ar dins l\u2019aplicacion web {{app}}.",descriptionWithoutWeb:"Res se passa pas ? Av\xe8m ensajat de lan\xe7ar la confer\xe9ncia dins l\u2019aplicacion de bur\xe8u {{app}}.",downloadApp:"Telecargar l\u2019aplicacion",launchWebButton:"Lan\xe7ar del navigador",openApp:"Telecargar l\u2019aplicacion",title:"Aviada de v\xf2stra confer\xe9ncia dins {{app}}...",tryAgainButton:"Tornar ensajar del bur\xe8u"},defaultLink:"ex. {{url}}",defaultNickname:"ex. Joan Delpu\xe8ch",deviceError:{cameraError:"Frac\xe0s de l\u2019acc\xe8s a v\xf2stra cam\xe8ra",cameraPermission:"Error d'obtencion de la permission per la cam\xe8ra",microphoneError:"Frac\xe0s de l\u2019acc\xe8s a v\xf2stre microf\xf2n",microphonePermission:"Error d'obtencion de la permission pel microf\xf2n"},deviceSelection:{noPermission:"Autorizacion pas acordada",previewUnavailable:"Apercebut pas disponible",selectADevice:"Seleccionatz un aparelh",testAudio:"Legir un son de pr\xf2va"},dialog:{accessibilityLabel:{liveStreaming:"Difusion en dir\xe8cte"},allow:"Autorizar",alreadySharedVideoMsg:"Un autre participant parteja ja sa vid\xe8o. Aquesta confer\xe9ncia permet pas qu\u2019un partatge de vid\xe8o a l\u2019enc\xf2p.",alreadySharedVideoTitle:"Solament un partatge de vid\xe8o a l'enc\xf2p es autorizat",applicationWindow:"Fen\xe8stra de l'aplicacion",Back:"Retorn",cameraConstraintFailedError:"V\xf2stra cam\xe8ra satisf\xe0 pas totas las constrentas necess\xe0rias.",cameraNotFoundError:"La cam\xe8ra es pas estada trobada.",cameraNotSendingData:"Pod\xe8m pas accedir a v\xf2stra cam\xe8ra. Merc\xe9s de verificar se una autra aplicacion es a utilizar aqueste periferic, seleccionatz un autre periferic dins lo men\xfa de param\xe8tres o ensajatz de recargar l'aplicacion.",cameraNotSendingDataTitle:"Impossible d'accedir a v\xf2stra cam\xe8ra",cameraPermissionDeniedError:"La cam\xe8ra es pas estada trobada. Pasmens pod\xe8tz participar a la confer\xe9ncia mas los demai vos veir\xe0n pas. Utilizatz lo boton de la barra d\u2019adre\xe7a per res\xf2lver aqueste probl\xe8ma.",cameraUnknownError:"Impossible d\u2019emplegar la cam\xe8ra per una rason desconeguda.",cameraUnsupportedResolutionError:"V\xf2stra cam\xe8ra pren pas en carga la resolucion vid\xe8o que cal.",Cancel:"Anullar",close:"Tampar",conferenceDisconnectMsg:"Vos cal benl\xe8u verificar v\xf2stra connexion al malhum. N\xf2va connexion dins {{seconds}} segondas...",conferenceDisconnectTitle:"S\xe8tz estat desconnectat.",conferenceReloadMsg:"S\xe8m a reglar aqu\xf2 ! N\xf2va connexion dins {{seconds}} segondas...",conferenceReloadTitle:"Malurosament, quic\xf2m truqu\xe8t.",confirm:"Confirmar",confirmNo:"Non",confirmYes:"\xd2c",connectError:"Ops! Quic\xf2m a trucat e la connexion a la confer\xe9ncia es impossibla.",connectErrorWithMsg:"Ops! Quic\xf2m a trucat e la connexion a la confer\xe9ncia es impossibla: {{msg}}",connecting:"Connexion en cors",contactSupport:"Contactar l'assist\xe9ncia",copy:"Copiar",dismiss:"Regetar",displayNameRequired:"Adiu\u202f! Coss\xed vos dison\u202f?",done:"Tampar",enterDisplayName:"Volgatz picar v\xf2stre nom aqu\xed",error:"Error",externalInstallationMsg:"Av\xe8tz d'installar n\xf2stra extension de partiment d'ecran.",externalInstallationTitle:"Extension requesida :",goToStore:"Anar al webstore",gracefulShutdown:"Lo servici es actualament en mantenen\xe7a. Ensajatz tornamai pus tard.",IamHost:"Soi l\u2019\xf2ste",incorrectRoomLockPassword:"Senhal incorr\xe8cte",incorrectPassword:"Nom de compte o senhal incorr\xe8cte",inlineInstallationMsg:"Av\xe8tz d'installar n\xf2stra extension de partiment d'ecran.",inlineInstallExtension:"Installar ara",internalError:"\xd2u ! Quic\xf2m a pas foncionat. L'error seguenta s'es producha : {{error}}",internalErrorTitle:"Error int\xe8rna",kickMessage:"Pod\xe8tz contactat {{participantDisplayName}} per mai de detalhs.",kickParticipantButton:"Forabandir",kickParticipantDialog:"Vol\xe8tz vertadi\xe8rament forabandir aqueste participant\u202f?",kickParticipantTitle:"Forabandir aqueste participant\u202f?",kickTitle:"Ai\u202f! {{participantDisplayName}} vos a forabandit de la confer\xe9ncia",liveStreaming:"La difusion en dir\xe8cte es estada arrestada",liveStreamingDisabledForGuestTooltip:"Los convidats p\xf2don pas aviar una difusion en dir\xe8cte",liveStreamingDisabledTooltip:"Difusion en dir\xe8cte desactivada.",lockMessage:"Impossible de verrolhar la confer\xe9ncia.",lockRoom:"",lockTitle:"Frac\xe0s del verrolhatge",logoutQuestion:"S\xe8tz segur que vos vol\xe8tz desconnectar e arrestar la confer\xe9ncia ?",logoutTitle:"Desconnexion",maxUsersLimitReached:"La limita de nombre maximum de participant es estada atenguda. La confer\xe9ncia es compl\xe8ta. Contactatz lo proprietari de la confer\xe9ncia o tornatz ensajar mai tard.",maxUsersLimitReachedTitle:"Limita de participants maximum atenguda ",micConstraintFailedError:"V\xf2stre microf\xf2n satisf\xe0 pas totas las constrentas necess\xe0rias.",micNotFoundError:"Lo microf\xf2n es pas estat trobat.",micNotSendingData:"Anatz als param\xe8tres de l\u2019ordenador per tornar metre lo son del microf\xf2n e ajustar son niv\xe8l.",micNotSendingDataTitle:"Lo microf\xf2n es copat pels param\xe8tres del sist\xe8ma",micPermissionDeniedError:"Av\xe8tz pas donat l'autorizacion d'utilizar v\xf2stre microf\xf2n. Pod\xe8tz encara participar a la confer\xe9ncia mai los demai vos ausir\xe0n pas. Utilizatz lo boton del microf\xf2n dins la barra d'adre\xe7a per res\xf2lvre aqu\xf2.",micUnknownError:"Impossible d'utilizar lo microf\xf2n per una rason desconeguda.",muteParticipantBody:"Poiretz pas lo tornar activar lo microf\xf2n, mai eles p\xf2don o far quand v\xf2lon.",muteParticipantButton:"Copar lo son",muteParticipantDialog:"",muteParticipantTitle:"Copar lo micro als participants ?",Ok:"D'ac\xf2rdi",passwordLabel:"",passwordNotSupported:"Ajustar un senhal a una confer\xe9ncia es pas suportat.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"V\xf2stre navigator bloca las fen\xe8stras que sorgisson a partir d'aqueste site. Merc\xe9s d'activar aquelas fen\xe8stras dins los param\xe8tres de v\xf2stre navigator e de tornar ensajar.",popupErrorTitle:"Fen\xe8stra que sorg\xeds blocada",recording:"Enregistrament",recordingDisabledForGuestTooltip:"Los convits p\xf2don pas lan\xe7ar d\u2019enregistraments.",recordingDisabledTooltip:"L\u2019enregistrament es desactivat.",rejoinNow:"Participar ara",remoteControlAllowedMessage:"{{user}} a acceptat v\xf2stra demanda de contrar\xf2tle alonhat !",remoteControlDeniedMessage:"{{user}} a refusat v\xf2stra demanda de contrar\xf2tle alonhat !",remoteControlErrorMessage:"Error al moment de demandar lo contrar\xf2tle alonhat a {{user}} !",remoteControlRequestMessage:"Vol\xe8tz autorizar {{user}} a contrarotlar v\xf2stre ordinador ?",remoteControlShareScreenWarning:"Remarcatz que se botatz \xab\u202fAutorizar\u202f\xbb partejaretz v\xf2stre ecran !",remoteControlStopMessage:"La session de contrar\xf2tle alonhat es acabada !",remoteControlTitle:"Contrar\xf2tle a dist\xe0ncia",Remove:"Suprimir",removePassword:"",removeSharedVideoMsg:"S\xe8tz segur que vol\xe8tz suprimir v\xf2stra vid\xe8o partejada ?",removeSharedVideoTitle:"Suprimir la vid\xe8o partejada",reservationError:"Error del sist\xe8ma de reservacion",reservationErrorMsg:"C\xf2di d'error: {{code}}, messatge: {{msg}}",retry:"Ensajar tornamai",screenSharingFailedToInstall:"\xd2u ! Frac\xe0s de l'installacion de partatge d'ecran.",screenSharingFailedToInstallTitle:"Frac\xe0s de l'installacion de partatge d'ecran",screenSharingFirefoxPermissionDeniedError:"Quic\xf2m a fach m\xe8uca quand \xe8rem a ensajar de partejar v\xf2stre ecran. Merc\xe9s de verificar qu\u2019av\xe8tz donat l\u2019autorizacion de lo partejar.",screenSharingFirefoxPermissionDeniedTitle:"Ops\u202f! Av\xe8m pas pogut aviar lo partatge d\u2019ecran.",screenSharingPermissionDeniedError:"\xd2ups ! Quic\xf2m s'es pas ben passat amb l'autorizacion de v\xf2stra extension de partatge d'ecran. Merc\xe9s de recargar e tornar ensajar.",serviceUnavailable:"Servici indisponible",sessTerminated:"Sonada acabada",Share:"Partejar",shareVideoLinkError:"Se vos plai, provesiss\xe8tz un ligam Youtube foncional.",shareVideoTitle:"Partejar una vid\xe8o",shareYourScreen:"Partejar v\xf2stre ecran",shareYourScreenDisabled:"Lo partiment d\u2019ecran es desactivat.",shareYourScreenDisabledForGuest:"Los convits p\xf2don pas partejar l\u2019ecran",startLiveStreaming:"Aviar una difusion en dir\xe8cte",startRecording:"Arrestar l'enregistrament",startRemoteControlErrorMessage:"Una error s'es produsida en ensajar de comen\xe7ar la session de contrar\xf2tle a dist\xe0ncia !",stopLiveStreaming:"Arrestar lo dir\xe8cte",stopRecording:"Arrestar l'enregistrament",stopRecordingWarning:"S\xe8tz segur que vol\xe8tz arrestar l'enregistrament?",stopStreamingWarning:"S\xe8tz segur que vol\xe8tz arrestar lo dir\xe8cte?",streamKey:"Clau del dir\xe8cte",Submit:"Validar",thankYou:"Merc\xe9 d'aver utilizat {{appName}} !",token:"geton",tokenAuthFailed:"O planh\xe8m, s\xe8tz pas autorizat a rej\xf3nher l'ap\xe8l.",tokenAuthFailedTitle:"Frac\xe0s de l'autentificacion",transcribing:"Transcripcion",unlockRoom:"",userPassword:"senhal utilizaire",WaitForHostMsg:"La confer\xe9ncia {{room}} a pas encara comen\xe7at. Se s\xe8tz l\u2019\xf2st volgatz ben vos identificar. Autrament esperatz qu\u2019arribe l\u2019\xf2ste.",WaitForHostMsgWOk:"La confer\xe9ncia {{room}} a pas encara comen\xe7at. Se s\xe8tz l\u2019\xf2st volgatz ben clicar Ok per vos identificar. Autrament esperatz qu\u2019arribe l\u2019\xf2ste.",WaitingForHost:"\xd2m p\xf2t pas que partejar una vid\xe8o a l'enc\xf2p",Yes:"\xd2c",yourEntireScreen:"V\xf2stre ecran complet"},dialOut:{statusMessage:"ara es {{status}}"},feedback:{average:"Mejana",bad:"Marrida",detailsLabel:"Digatz-nos-ne mai a prepaus d\u2019aqu\xf2.",good:"Bona",rateExperience:"Merc\xe9s de donar una n\xf2ta a v\xf2stra experi\xe9ncia.",veryBad:"F\xf2r\xe7a marrida",veryGood:"F\xf2r\xe7a bona"},incomingCall:{answer:"U\xe8i",audioCallTitle:"Sonada entranta",decline:"Regetar",productLabel:"de Jitsi Meet estant",videoCallTitle:"Sonada vid\xe8o entranta"},info:{accessibilityLabel:"Mostrar las info",addPassword:"",cancelPassword:"",conferenceURL:"Ligam\u202f:",country:"Pa\xeds",dialANumber:"Per participar a la confer\xe9ncia, sonatz un d\u2019aquestes num\xe8ros pu\xe8i picatz lo senhal.",dialInConferenceID:"PIN\u202f:",dialInNotSupported:"Las sonadas son pas encara foncionalas.",dialInNumber:"Compausar\u202f:",dialInSummaryError:"",dialInTollFree:"Sonada gratu\xefta",genericError:"Ops, quic\xf2m a fach m\xe8uca.",inviteLiveStream:"Per veire lo flux en dir\xe8cte de la confer\xe9ncia, clicatz aqueste ligam\u202f: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"S\xe8tz convidat a participar a la confer\xe9ncia.",inviteURLFirstPartPersonal:"{{name}} vos convida a la confer\xe9ncia.\n",inviteURLSecondPart:"\nParticipar a la confer\xe9ncia :\n{{url}}\n",liveStreamURL:"Flux dir\xe8cte\u202f:",moreNumbers:"Mai de num\xe8ros",noNumbers:"Pas cap de num\xe8ro.",noPassword:"Pas cap",noRoom:"Cap de sala pas donada per la j\xf3nher.",numbers:"Sonar de num\xe8ros",password:"",title:"Partejar",tooltip:"Partejar lo ligam e las informacions d\u2019aquesta confer\xe9ncia",label:"Info confer\xe9ncia"},inviteDialog:{alertText:"Frac\xe0s en convidant unes participants.",header:"Convidar",searchCallOnlyPlaceholder:"Picatz un numer\xf2 de telef\xf2n",searchPeopleOnlyPlaceholder:"Cercar de participants",searchPlaceholder:"Participant o num\xe8ro de telef\xf2n",send:"Mandar"},inlineDialogFailure:{msg:"Av\xe8m un pauc patit a mant\xe9ner la connexion.",retry:"Ensajar tornarmai",support:"Assist\xe9ncia",supportMsg:"Se ten d'arribat, contactatz l'"},keyboardShortcuts:{focusLocal:"Centrar sus v\xf2stra vid\xe8o",focusRemote:"Centrar sus la vid\xe8o de qualqu'un mai",fullScreen:"Activar / Desactivar l'ecran complet",keyboardShortcuts:"Acorchis de clavi\xe8r",localRecording:"Mostrar o amagar los contrar\xf2tles d\u2019enregistrament local",mute:"Activar o desactivar lo microf\xf2n",pushToTalk:"Butar per parlar",raiseHand:"Demandar o pas la paraula",showSpeakerStats:"Mostrar las estatisticas del microf\xf2n",toggleChat:"Dobrir o tampar lo pan\xe8l de conversacion",toggleFilmstrip:"Mostrar o amagar la vinheta vid\xe8o",toggleScreensharing:"Caplevar entre cam\xe8ra e partatge d'ecran",toggleShortcuts:"Mostrar o amagar los acorchis clavi\xe8r",videoMute:"Aviar o arrestar v\xf2stra cam\xe8ra",videoQuality:"Gerir la qualitat de las sonadas"},liveStreaming:{busy:"S\xe8m a ensajar de liurar de ressor\xe7as flux. Merc\xe9s de tornar ensajar dins una estona.",busyTitle:"Totes los difusors son ocupats",changeSignIn:"Cambiar de compte.",choose:"Causiss\xe8tz un flux dir\xe8cte",chooseCTA:"Causiss\xe8tz un flux dir\xe8cte. S\xe8tz connectat coma {{email}}.",enterStreamKey:"Picatz v\xf2stre clau de flux dir\xe8cte Youtube aqu\xed.",error:"Frac\xe0s de la difusion en dir\xe8cte. Merc\xe9s de tornar ensajar.",errorAPI:"Una error s\u2019es producha pendent l\u2019acc\xe8s al flux YouTube. Merc\xe9 d\u2019ensajar de vos connectar mai tard.",errorLiveStreamNotEnabled:"La difusion en dir\xe8cte es pas activada per {{email}}. Volgatz ben activar la difusion en dir\xe8cte o vos connectar amb un compte que l\u2019a activada.",expandedOff:"La difusion en dir\xe8cte es estada arrestada",expandedOn:"La confer\xe9ncia es difusada sus YouTube.",expandedPending:"La difusion en dir\xe8cte comen\xe7a...",failedToStart:"La difusion en dir\xe8cte a pas capitat de s'aviar",getStreamKeyManually:"",invalidStreamKey:"La clau de difusion en dir\xe8cte es benl\xe8u pas corr\xe8cta.",off:"La difusion en dir\xe8cte es estada arrestada",on:"La difusion en dir\xe8cte es estada arrestada",pending:"Comen\xe7ar lo dir\xe8cte...",serviceName:"Servici de difusion en dir\xe8cte",signedInAs:"S\xe8tz connectat coma\xa0:",signIn:"Se connectar amb Google",signInCTA:"Connectatz-vos o picatz la clau de v\xf2stre flux YouTube.",signOut:"Se desconnectar",start:"Aviar una difusion en dir\xe8cte",streamIdHelp:"Qu\u2019es aqu\xf2 ?",unavailableTitle:"Difusion en dir\xe8cte indisponibla"},localRecording:{clientState:{off:"Desactivat",on:"Activat",unknown:"Desconeguts"},dialogTitle:"Contrar\xf2tles dels enregistraments locals",duration:"Durada",durationNA:"N/A",encoding:"Encodatge",label:"ENR-LOC",labelToolTip:"Enregistrament local comen\xe7at",localRecording:"Enregistrament local",me:"Ieu",messages:{engaged:"Enregistrament local comen\xe7at.",finished:"Enregistrament de la session {{token}} acabat. Merc\xe9s d\u2019enviar lo fichi\xe8r enregistrat al moderator.",finishedModerator:"Enregistrament de la session {{token}} acabat. Es estat salvagardat. Merc\xe9s de demandar als autres participants d\u2019enviar lor enregistraments.",notModerator:"S\xe8tz pas moderator. Pod\xe8tz pas comen\xe7ar o arrestar un enregistrament local."},moderator:"Moderator",no:"Non",participant:"Participant",participantStats:"Estatisticas del participant",sessionToken:"Geton de session",start:"Aviar l'enregistrament",stop:"Arrestar l'enregistrament",yes:"\xd2c"},lockRoomPassword:"senhal",lockRoomPasswordUppercase:"Senhal",me:"ieu",notify:{connectedOneMember:"{{name}} a jonch la confer\xe9ncia",connectedThreePlusMembers:"{{name}} e {{count}} personas mai participan a la confer\xe9ncia",connectedTwoMembers:"{{first}} e {{second}} participan a la confer\xe9ncia",disconnected:"desconnectat",focus:"Focus de confer\xe9ncia",focusFail:"{{component}} es pas disponible - ensajatz tornamai dins {{ms}} sec",grantedTo:"Dreits moderator acordats a {{to}} !",invitedOneMember:"{{name}} es estat convidat",invitedThreePlusMembers:"{{name}} e {{count}} autres son estats convidats",invitedTwoMembers:"{{first}} e {{second}} son estats convidats",kickParticipant:"{{kicked}} es estat expulsat per {{kicker}}",me:"Ieu",moderator:"Dreits moderator acordats !",muted:"Av\xe8tz comen\xe7at la conversacion en mut.",mutedTitle:"S\xe8tz en mut !",mutedRemotelyTitle:"{{participantDisplayName}} vos a mes en silenci !",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} volri\xe1 parlar.",somebody:"Qualqu'un",startSilentTitle:"Av\xe8tz jonch sens cap de sortida \xe0udio !",startSilentDescription:"Rej\xf3nher la confer\xe9ncia per activar l\u2019\xe0udio",suboptimalBrowserWarning:"",suboptimalExperienceTitle:"Avertiment del navegador",unmute:"Restablir lo son",newDeviceCameraTitle:"N\xf2va cam\xe8ra detectada",newDeviceAudioTitle:"N\xf2u periferic \xe0udio detectat",newDeviceAction:"Utilizar"},passwordSetRemotely:"causit per qualqu'un mai",passwordDigitsOnly:"Fins a {{number}} chifras",poweredby:"produit per",presenceStatus:{busy:"Ocupat",calling:"Sonada...",connected:"Connectat",connecting:"Connexion en cors...",connecting2:"Connexion*...",disconnected:"Desconnectat",expired:"Expirat",ignored:"Ignorat",initializingCall:"Comen\xe7ament de la sonada...",invited:"Convidat",rejected:"Refusat",ringing:"A sonar..."},profile:{setDisplayNameLabel:"Causiss\xe8tz v\xf2stre escais",setEmailInput:"Picatz lo corri\xe8l",setEmailLabel:"Definiss\xe8tz v\xf2stre corri\xe8l per gravatar",title:"Perfil"},raisedHand:"Volri\xe1 charrar",recording:{authDropboxText:"Enviar a Dropbox",availableSpace:"Espaci disponible : {{spaceLeft}} Mo (altorn de {{duration}} minutas d\u2019enregistrament)",beta:"BETA",busy:"S\xe8m a desliurar de resorgas d'enregistrament. Merc\xe9s de tornar ensajar dins una estona.",busyTitle:"Totes los enregistradors son ocupats pel moment",error:"Frac\xe0s de l'enregistrament. Merc\xe9s de tornar ensajar.",expandedOff:"Enregistrament arrestat",expandedOn:"La confer\xe9ncia es enregistrada.",expandedPending:"Aviada de l\u2019enregistrament...",failedToStart:"L'enregistrament n'as pas r\xe9ussi a d\xe9marrer",fileSharingdescription:"Partejar l\u2019enregistrament amb los participants de la re\xfcnion",live:"DIR\xc8CTE",loggedIn:"Session a {{userName}}",off:"Enregistrament arrestar",on:"Enregistrament",pending:"Preparacion de l\u2019enregistrament de la confer\xe9ncia...",rec:"ENRG",serviceDescription:"V\xf2stre enregistrament ser\xe0 salvagardat pel servici dedicat.",serviceName:"Servici d\u2019enregistrament",signIn:"Connexion",signOut:"Se desconnectar",unavailable:"Ops\u202f! Lo {{serviceName}} es pas disponible pel moment. S\xe8m a reglar aqueste probl\xe8ma. Merc\xe9s de tornar ensajar mai tard.",unavailableTitle:"Enregistrament indisponible"},sectionList:{pullToRefresh:"Tirar per actualizar"},settings:{calendar:{about:"L\u2019integracion de {{appName}} amb v\xf2stre calendi\xe8r permet d\u2019accedir d\u2019un biais segur als eveniments venents.",disconnect:"Desconnectar",microsoftSignIn:"Se connectar amb Microsoft",signedIn:"Acc\xe8s als eveniments del calendi\xe8r {{email}}. Clicatz lo boton Se desconnectar \xe7ai-jos per arrestar l\u2019acc\xe8s als eveniments del calendi\xe8r.",title:"Calendari"},devices:"Periferics",followMe:"Tot lo mond me s\xe8c",language:"Lenga",loggedIn:"Session a {{userName}}",moderator:"Moderator",more:"Mai",name:"Escais",noDevice:"Pas cap",selectAudioOutput:"Sortida \xe0udio",selectCamera:"Cam\xe8ra",selectMic:"Microf\xf2n",startAudioMuted:"Comen\xe7an totes sens son",startVideoMuted:"Comen\xe7an totes sens vid\xe8o",title:"Param\xe8tres"},settingsView:{alertOk:"D\u2019ac\xf2rdi",alertTitle:"Avertiment",alertURLText:"L\u2019URL del servidor es pas valida",buildInfoSection:"",conferenceSection:"Confer\xe9ncia",displayName:"Escais-nom",email:"Corri\xe8l",header:"Param\xe8tres",profileSection:"Perfil",serverURL:"URL del servidor",startWithAudioMuted:"Comen\xe7ar sens son",startWithVideoMuted:"Comen\xe7ar sens vi\xe8do",version:"Version"},share:{dialInfoText:"",mainText:"Copiatz lo ligam seguent per dintrar dins la confer\xe9ncia\xa0:\n{{roomUrl}}"},speaker:"Nautparlaire",speakerStats:{hours:"{{count}} oras",minutes:"{{count}} minutas",name:"Escais",seconds:"{{count}} segondas",speakerStats:"Estatisticas parladors",speakerTime:"Temps de paraula"},startupoverlay:{policyText:" ",title:"{{app}} a besonh d'utilizar v\xf2stre microf\xf2n e cam\xe8ra."},suspendedoverlay:{rejoinKeyTitle:"Tornar participar",text:"Quichatz lo boton Tornar participar.",title:"V\xf2stra confer\xe9ncia vid\xe8o es estada arrestada perque v\xf2stre ordenador se bot\xe8t en velha."},toolbar:{accessibilityLabel:{audioOnly:"Passar al sol \xe0udio",audioRoute:"Seleccionar lo periferic \xe0udio",callQuality:"Gerir la qualitat vid\xe8o",cc:"Passar als jost\xedtols",chat:"Passar a la fen\xe8stra chat",document:"Tampar los documents partejats",feedback:"Daissar un comentari",fullScreen:"Passar al ecran compl\xe8t",hangup:"Quitar la sonada",invite:"Convidar de monde",kick:"",localRecording:"Passar al pan\xe8l d\u2019enregistraments locals",lockRoom:"Tirar lo senhal de la confer\xe9ncia",moreActions:"Passar al men\xfa mai d\u2019accions",moreActionsMenu:"Mai de men\xfas d\u2019accion",mute:"Copar lo son",pip:"Activar/Desactivar lo m\xf2de Picture-in-Picture",profile:"Modificar v\xf2stre perfil",raiseHand:"Demandar la paraula",recording:"Passar al enregistraments",remoteMute:"",Settings:"Passar als param\xe8tres",sharedvideo:"Passar al partatge de vid\xe8o Youtube",shareRoom:"Convidar qualqu\u2019un",shareYourScreen:"Passar a la captura d\u2019ecran",shortcuts:"Passar als acorchis",show:"",speakerStats:"Mostrar/Amagar los estatisticas de paraula",tileView:"Activar/Desactivar la vista en mosa\xefc",toggleCamera:"Passar a la cam\xe8ra",videomute:"Silenciar la vid\xe8o",videoblur:""},addPeople:"Ajustar de monde a v\xf2stra sonada",audioOnlyOff:"",audioOnlyOn:"",audioRoute:"Seleccionar lo periferic \xe0udio",authenticate:"Autentificatz-vos",callQuality:"Gerir la qualitat vid\xe8o",chat:"Dobrir / tampar la conversacion",closeChat:"Tampar la messatjari\xe1",documentClose:"Tampar los documents partejats",documentOpen:"Dobrir los documents partejats",enterFullScreen:"Veire l\u2019ecran compl\xe8t",enterTileView:"",exitFullScreen:"Sortir de l\u2019ecran compl\xe8t",exitTileView:"",feedback:"Daissar un comentari",hangup:"Quitar",invite:"Convidar de monde",login:"Connexion",logout:"Desconnexion",lowerYourHand:"Baissar la man",moreActions:"Mai d\u2019opcions",mute:"Mut / Actiu",openChat:"Dobrir la messatjari\xe1 ",pip:"Passar al m\xf2de Picture-in-Picture",profile:"Modificar v\xf2stre perfil",raiseHand:"Demandar / Daissar la paraula",raiseYourHand:"Levar la man",Settings:"Param\xe8tres",sharedvideo:"Partejar una vid\xe8o Youtube",shareRoom:"Convidar qualqu\u2019un",shortcuts:"Veire los acorchis clavi\xe8r",speakerStats:"Estatisticas parladors",startScreenSharing:"Aviar lo partatge d\u2019ecran",startSubtitles:"Aviar los sost\xedtols",stopScreenSharing:"Arrestar lo partatge d\u2019ecran",stopSubtitles:"Arrestar los sost\xedtols ",stopSharedVideo:"Arrestar la vid\xe8o Youtube",talkWhileMutedPopup:"Ensajatz de parlar ? V\xf2stre microf\xf2n es copat.",tileViewToggle:"Activar/Desactivar la vista en mosa\xefc",toggleCamera:"Passar a la cam\xe8ra",videomute:"Aviar / Arrestar la cam\xe8ra",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Aviar / Arrestat los sost\xedtols",error:"Frac\xe0s de la transcripcion. Merc\xe9s de tornar ensajar.",expandedLabel:"La transcripcion es activada",failedToStart:"Frac\xe0s de l\u2019aviada de la transcripcion",labelToolTip:"La confer\xe9ncia es a \xe8sser transcricha",off:"Transcripcion arrestada",pending:"Preparacion de l\u2019enregistrament de la confer\xe9ncia...",start:"Mostrar los sost\xedtols",stop:"Levar los sost\xedtols",tr:"TR"},userMedia:{androidGrantPermissions:"Causiss\xe8tz Autorizar quand v\xf2stre navigador vos demanda l'autorizacion.",chromeGrantPermissions:"Causiss\xe8tz Autorizar quand v\xf2stre navigador vos demanda l'autorizacion.",edgeGrantPermissions:"Causiss\xe8tz \xd2c quand v\xf2stre navigador vos demanda l'autorizacion.",electronGrantPermissions:"Merc\xe9s de donar las permissions d'utilizar v\xf2stra cam\xe8ra e v\xf2stre microf\xf2n",firefoxGrantPermissions:"Causiss\xe8tz Partejar l'aparelh seleccionat quand v\xf2stre navigador vos demanda l'autorizacion.",iexplorerGrantPermissions:"Causiss\xe8tz OK quand v\xf2stre navigador vos demanda l'autorizacion.",nwjsGrantPermissions:"Merc\xe9s de donar las permissions d'utilizar v\xf2stra cam\xe8ra e v\xf2stre microf\xf2n",operaGrantPermissions:"Causiss\xe8tz Autorizar quand v\xf2stre navigador vos demanda l'autorizacion.","react-nativeGrantPermissions":"Causiss\xe8tz Autorizar quand v\xf2stre navigador vos demanda l'autorizacion.",safariGrantPermissions:"Causiss\xe8tz OK quand v\xf2stre navigador vos demanda l'autorizacion."},videoSIPGW:{busy:"S\xe8m a liurar de resorgas. Merc\xe9s de tornar ensajar dins una estona.",busyTitle:"Lo servici de sala es ocupat pel moment",errorAlreadyInvited:"{{displayName}} es ja convidat",errorInvite:"La confer\xe9ncia es pas encara establida. Tornatz ensajar mai tard.",errorInviteFailed:"S\xe8m a reglar aqueste probl\xe8ma. Ensajatz mai tard.",errorInviteFailedTitle:"L\u2019invitacion a {{displayName}} a fracassat",errorInviteTitle:"Error en convidar a la sala",pending:"{{displayName}} es estat convidat"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"",callQuality:"Qualitat vid\xe8o",hd:"HD",hdTooltip:"Difusion vid\xe8o en nauta definicion",highDefinition:"Nauta definicion",labelTooiltipNoVideo:"Pas cap de vid\xe8o",labelTooltipAudioOnly:"",ld:"Bassa definicion",ldTooltip:"Difusion vid\xe8o en bassa definicion",lowDefinition:"Bassa definicion",onlyAudioAvailable:"Pas que l\u2019\xe0udio es disponible",onlyAudioSupported:"S\xe8m compatibles solament amb l\u2019\xe0udio dins aqueste navigator.",p2pEnabled:"Connexion par a par activada",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Nauta definicion preferida.",sd:"SD",sdTooltip:"Difusion vid\xe8o en definicion estandard",standardDefinition:"Definicion estandard"},videothumbnail:{domute:"Copar lo son",flip:"Revirar",kick:"Exclure",moderator:"Moderator",mute:"Un participant a copat son micro",muted:"Mut",remoteControl:"Contrar\xf2tle alonhat",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Tocatz per participar",roomname:"Sasiss\xe8tz un nom de sala"},appDescription:"Endavant, charratz en vid\xe8o amb tota la c\xf2la. Per dire de convidar tot lo monde que coneiss\xe8tz. {{app}} es una solucion de vid\xe8o-confer\xe9ncia compl\xe8tament chifrada e 100% liura que pod\xe8tz utilizar tota la jornada, totes los jorns, gratuitament\u2014 sens cap de compte pas requesit.",audioVideoSwitch:{audio:"Votz",video:"Vid\xe8o"},calendar:"Calendari",connectCalendarButton:"Connectar lo calendari",connectCalendarText:"",enterRoomTitle:"Comen\xe7ar una n\xf2va confer\xe9ncia",go:"Crear",join:"PARTICIPATZ",info:"",privacy:"Vida privada",recentList:"Recents",recentListDelete:"Suprimits",recentListEmpty:"V\xf2stra lista de contactes recents es voida. Charratz amb v\xf2stra c\xf2la e trobaretz totes v\xf2stras confer\xe9ncias recentas aqu\xed.",reducedUIText:"Benvengut a {{app}} !",roomname:"Sasiss\xe8tz un nom de sala",roomnameHint:"Picatz lo nom o l\u2019URL de la sala que vol\xe8tz j\xf3nher. Pod\xe8tz inventar un nom, cal pas que lo monde que vol\xe8tz convidar lo s\xe0pian. ",sendFeedback:"Mandar v\xf2stra opinion",terms:"T\xe8rmes",title:"Confer\xe9ncias vid\xe8o securizadas amb plen de foncionalitats e compl\xe8tament gratuitas"}}},670,[]); -__d(function(i,k,s,e,a,o,r){a.exports={en:"Anglik",af:"",az:"Azerski",bg:"Bu\u0142garski",cs:"Czeski",de:"Niemiecki",el:"Grecki",eo:"Esperanto",es:"Hiszpa\u0144ski",fr:"Francuski",hy:"Ormia\u0144ski",it:"W\u0142oski",ja:"Japo\u0144ski",ko:"Korea\u0144ski",nb:"Norweski Bokmal",oc:"Oksyta\u0144ski",pl:"Polski",ptBR:"portugalski (brazylijski)",ru:"Rosyjski",sk:"S\u0142owacki",sl:"S\u0142owe\u0144ski",sv:"Szwedzki",tr:"Turecki",vi:"Wietnamski",zhCN:"Chi\u0144ski (Chiny)"}},671,[]); -__d(function(e,i,o,a,n,r,t){n.exports={addPeople:{add:"",countryNotSupported:"",countryReminder:"",disabled:"",failedToAdd:"",footerText:"",loading:"",loadingNumber:"",loadingPeople:"",noResults:"",noValidNumbers:"",searchNumbers:"",searchPeople:"",searchPeopleAndNumbers:"",telephone:"",title:""},audioDevices:{bluetooth:"Bluetooth",headphones:"S\u0142uchawki",phone:"",speaker:"g\u0142o\u015bnik"},audioOnly:{audioOnly:"Tylko d\u017awi\u0119k"},calendarSync:{addMeetingURL:"",confirmAddLink:"",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"",joinTooltip:"",nextMeeting:"",noEvents:"",ongoingMeeting:"",permissionButton:"",permissionMessage:"",refresh:"",today:""},chat:{error:"",messagebox:"",nickname:{popover:"Wybierz sw\xf3j nick",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"Za\u0142\u0105cznik",AUTHENTICATING:"Uwierzytelnianie",AUTHFAIL:"Uwierzytelnianie nie powiod\u0142o si\u0119",CONNECTED:"Po\u0142\u0105czono",CONNECTING:"Nawi\u0105zywanie po\u0142\u0105czenia",CONNFAIL:"Po\u0142\u0105czenie si\u0119 nie powiod\u0142o",DISCONNECTED:"Roz\u0142\u0105czony",DISCONNECTING:"Roz\u0142\u0105czanie",ERROR:"B\u0142\u0105d",RECONNECTING:"Wyst\u0105pi\u0142 problem w sieci. Ponowienie po\u0142aczenia...."},connectionindicator:{address:"Adres:",bandwidth:"Zak\u0142adana przepustowo\u015b\u0107:",bitrate:"Szybko\u015b\u0107 transmisji:",bridgeCount:"Liczba serwer\xf3w",connectedTo:"Pod\u0142\u0105czone do:",framerate:"Cz\u0119stotliwo\u015b\u0107 od\u015bwie\u017cania",less:"Poka\u017c mniej",localaddress:"Lokalny adres:Lokalne Adresy:",localaddress_plural_2:"",localaddress_plural_5:"",localport:"Lokalny port:Lokalne porty:",localport_plural_2:"",localport_plural_5:"",more:"Poka\u017c wi\u0119cej",packetloss:"Strata pakiet\xf3w:",quality:{good:"Prawdziwy",inactive:"nieaktywny",lost:"Zaginiony",nonoptimal:"Nieoptymalne",poor:"Biedny"},remoteaddress:"Zdalny adres:Zdalne adresy:",remoteaddress_plural_2:"",remoteaddress_plural_5:"",remoteport:"Zdalny port:Zdalne porty:",remoteport_plural_2:"",remoteport_plural_5:"",resolution:"Rozdzielczo\u015b\u0107:",status:"Nawi\u0105zywanie po\u0142\u0105czenia",transport:"Przekazywanie:",turn:"skr\u0119t"},"\x05connectionindicator":{},dateUtils:{earlier:"",today:"",yesterday:""},deepLinking:{appNotInstalled:"",description:"",descriptionWithoutWeb:"",downloadApp:"",launchWebButton:"",openApp:"",title:"",tryAgainButton:""},defaultLink:"np. _url_",deviceError:{cameraError:"",cameraPermission:"",microphoneError:"",microphonePermission:""},deviceSelection:{noPermission:"",previewUnavailable:"",selectADevice:"",testAudio:""},dialog:{accessibilityLabel:{liveStreaming:"Strumie\u0144 live"},allow:"",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"",applicationWindow:"",Back:"Wstecz",cameraConstraintFailedError:"Twoja kamera nie spe\u0142nia wymaga\u0144.",cameraNotFoundError:"Kamera nie znaleziona.",cameraNotSendingData:"",cameraNotSendingDataTitle:"",cameraPermissionDeniedError:"Nie udzieli\u0142e\u015b pozwolenia na u\u017cycie twojej kamery. Nadal mo\u017cesz w\u0142\u0105czy\u0107 si\u0119 do konferencji ale inni nie b\u0119d\u0105 ci\u0119 widzieli. Naci\u015bnij przycisk kamera w pasku menu aby u\u017cy\u0107 w\u0142a\u015bciw\u0105 kamer\u0119. ",cameraUnknownError:"Z nieznanej przyczyny nie mo\u017cna u\u017cy\u0107 kamery ",cameraUnsupportedResolutionError:"Twoja kamera nie obs\u0142uguje wymaganej rozdzielczo\u015bci.",Cancel:"Anuluj",close:"",conferenceDisconnectMsg:"",conferenceDisconnectTitle:"",conferenceReloadMsg:"",conferenceReloadTitle:"",confirm:"",confirmNo:"",confirmYes:"Tak",connectError:"Ocho! Cos posz\u0142o nie tak, nie mo\u017cna pod\u0142aczy\u0107 si\u0119 do tej konferencji.",connectErrorWithMsg:"Ocho! Co\u015b posz\u0142o nie tak i nie mo\u017cna pod\u0142\u0105czy\u0107 si\u0119 do tej konferencji:_msg_",connecting:"Nawi\u0105zywanie po\u0142\u0105czenia",contactSupport:"",copy:"Kopiuj",dismiss:"",displayNameRequired:"",done:"Brak",enterDisplayName:"",error:"B\u0142\u0105d",externalInstallationMsg:"Zainstaluj rozszerzenie naszego wsp\xf3\u0142dzielenia ekranu.",externalInstallationTitle:"Wymagane rozszerzenie",goToStore:"Id\u017a do sklepu",gracefulShutdown:"Aktualnie serwis jest konserwowany. Prosze spr\xf3bowa\u0107 p\xf3\u017aniej.",IamHost:"Jestem gospodarzem",incorrectRoomLockPassword:"",incorrectPassword:"",inlineInstallationMsg:"Zainstaluj rozszerzenie naszego wsp\xf3\u0142dzielenia ekranu.",inlineInstallExtension:"",internalError:"",internalErrorTitle:"B\u0142\u0105d wewn\u0119trzny",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"Strumie\u0144 live",liveStreamingDisabledForGuestTooltip:"",liveStreamingDisabledTooltip:"",lockMessage:"Zabezpieczenie konferencji nie powiod\u0142o si\u0119.",lockRoom:"",lockTitle:"Nie powiod\u0142o si\u0119 zabezpieczenie konferencji",logoutQuestion:"Na pewno chcesz si\u0119 wylogowa\u0107 i zako\u0144czy\u0107 konferencj\u0119?",logoutTitle:"Wyloguj",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Tw\xf3j mikrofon nie obs\u0142uguje wymaganych parametr\xf3w.",micNotFoundError:"Mikrofon nie jest odnaleziony.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Nie udzieli\u0142e\u015b pozwolenia na u\u017cycie twojego mikrofonu. Nadal mo\u017cesz uczestniczyc w konferencji ale inni nie b\u0119d\u0105 ci\u0119 s\u0142yszeli. U\u017cyj przycisku kamera aby to naprawi\u0107.",micUnknownError:"Z przyczyn nieznanych nie mo\u017cna u\u017cy\u0107 mikrofonu. ",muteParticipantBody:"",muteParticipantButton:"Wyciszenie",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"Ok",passwordLabel:"",passwordNotSupported:"",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"",popupErrorTitle:"",recording:"Nagrywanie",recordingDisabledForGuestTooltip:"",recordingDisabledTooltip:"",rejoinNow:"",remoteControlAllowedMessage:"",remoteControlDeniedMessage:"",remoteControlErrorMessage:"",remoteControlRequestMessage:"",remoteControlShareScreenWarning:"",remoteControlStopMessage:"",remoteControlTitle:"",Remove:"Usu\u0144",removePassword:"",removeSharedVideoMsg:"Na pewno chcesz usun\u0105\u0107 wsp\xf3\u0142dzielone wideo?",removeSharedVideoTitle:"Usu\u0144 wideo wsp\xf3\u0142dzielone",reservationError:"B\u0142\u0105d systemu rezerwacji",reservationErrorMsg:"Kod b\u0142\u0119du: _code_, tre\u015b\u0107: _msg_",retry:"Pon\xf3w",screenSharingFailedToInstall:"",screenSharingFailedToInstallTitle:"",screenSharingFirefoxPermissionDeniedError:"",screenSharingFirefoxPermissionDeniedTitle:"",screenSharingPermissionDeniedError:"",serviceUnavailable:"Us\u0142uga jest niedost\u0119pna",sessTerminated:"",Share:"Wsp\xf3\u0142dziel",shareVideoLinkError:"Podaj prosz\u0119 prawid\u0142owy link youtube.",shareVideoTitle:"Wsp\xf3\u0142dziel wideo",shareYourScreen:"",shareYourScreenDisabled:"",shareYourScreenDisabledForGuest:"",startLiveStreaming:"Zatrzymaj transmisj\u0119 live",startRecording:"Zatrzymaj nagrywanie",startRemoteControlErrorMessage:"",stopLiveStreaming:"Zatrzymaj transmisj\u0119 live",stopRecording:"Zatrzymaj nagrywanie",stopRecordingWarning:"Naprawd\u0119 chcesz zatrzyma\u0107 nagrywanie?",stopStreamingWarning:"Czy jeste\u015b pewny, \u017ce chcesz zatrzyma\u0107 ten strumie\u0144 live?",streamKey:"",Submit:"",thankYou:"Dzi\u0119kujemy Ci za u\u017cywanie _appName_!",token:"token",tokenAuthFailed:"Przepraszam, ale nie jeste\u015b upowa\u017cniony do uczestnictwa w tym po\u0142\u0105czeniu",tokenAuthFailedTitle:"Uwierzytelnianie nie powiod\u0142o si\u0119",transcribing:"",unlockRoom:"",userPassword:"has\u0142o u\u017cytkownika",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"Oczekiwanie na komputer",Yes:"Tak",yourEntireScreen:""},"\x05dialog":{accessibilityLabel:{}},dialOut:{statusMessage:""},feedback:{average:"\u015aredni",bad:"\u0179le\x7f\x7f",detailsLabel:"",good:"Prawdziwy",rateExperience:"Oce\u0144 prosz\u0119 swoje do\u015bwiadczenia z konferencji.",veryBad:"bardzo \u017ale\x7f",veryGood:"1: Bardzo dobrze"},"\x05feedback":{},incomingCall:{answer:"",audioCallTitle:"",decline:"",productLabel:"",videoCallTitle:""},info:{accessibilityLabel:"",addPassword:"",cancelPassword:"",conferenceURL:"",country:"",dialANumber:"",dialInConferenceID:"",dialInNotSupported:"",dialInNumber:"",dialInSummaryError:"",dialInTollFree:"",genericError:"",inviteLiveStream:"",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Strumie\u0144 live",moreNumbers:"",noNumbers:"",noPassword:"Brak",noRoom:"",numbers:"",password:"",title:"Wsp\xf3\u0142dziel",tooltip:"",label:""},"\x05info":{},inviteDialog:{alertText:"",header:"",searchCallOnlyPlaceholder:"",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"",retry:"",support:"",supportMsg:""},keyboardShortcuts:{focusLocal:"Focus on your video",focusRemote:"Focus on another person's video",fullScreen:"Otw\xf3rz / Zamknij pe\u0142ny ekran",keyboardShortcuts:"Skr\xf3ty klawiaturowe:",localRecording:"Wy\u015bwietlanie lub ukrywanie lokalnych element\xf3w steruj\u0105cych zapisem",mute:"Wy\u0142\u0105cz lub w\u0142\u0105cz mikrofon.",pushToTalk:"naci\u015bnij i m\xf3w",raiseHand:"Podnie\u015b lub opu\u015b\u0107 r\u0119k\u0119.",showSpeakerStats:"Poka\u017c statystyki g\u0142o\u015bnik\xf3w",toggleChat:"Otw\xf3rz lub zamknij panel czat.",toggleFilmstrip:"Pokazywanie lub ukrywanie miniatur wideo",toggleScreensharing:"Prze\u0142\u0105czanie pomi\u0119dzy kamer\u0105 i wsp\xf3ldzieleniem ekranu",toggleShortcuts:"Poka\u017c lub ukryj skr\xf3ty klawiaturowe",videoMute:"W\u0142\u0105czanie i wy\u0142\u0105czanie aparatu fotograficznego"},"\x05keyboardShortcuts":{},liveStreaming:{busy:"",busyTitle:"",changeSignIn:"",choose:"",chooseCTA:"",enterStreamKey:"",error:"Strumieniowanie live nie powiod\u0142o si\u0119. Spr\xf3buj p\xf3\u017aniej.",errorAPI:"",errorLiveStreamNotEnabled:"",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"Strumieniowanie live nie powiod\u0142o si\u0119",getStreamKeyManually:"",invalidStreamKey:"",off:"Strumieniowanie live zastopowane",on:"Strumie\u0144 live",pending:"Start strumieniowania live...",serviceName:"",signedInAs:"",signIn:"",signInCTA:"",signOut:"",start:"Zatrzymaj transmisj\u0119 live",streamIdHelp:"",unavailableTitle:""},"\x05liveStreaming":{},localRecording:{clientState:{off:"",on:"",unknown:""},dialogTitle:"",duration:"",durationNA:"",encoding:"",label:"",labelToolTip:"",localRecording:"",me:"To ja",messages:{engaged:"",finished:"",finishedModerator:"",notModerator:""},moderator:"",no:"",participant:"",participantStats:"",sessionToken:"",start:"Zatrzymaj nagrywanie",stop:"Zatrzymaj nagrywanie",yes:"Tak"},"\x05localRecording":{},lockRoomPassword:"",lockRoomPasswordUppercase:"",me:"to ja",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"roz\u0142\u0105czone",focus:"Fokus konferencji",focusFail:"_sk\u0142adnik_nie dost\u0119pny - zastosuj w _ms_sek",grantedTo:"Prawa moderatora przyznane _to_!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"To ja",moderator:"Prawa moderatora przydzielone!",muted:"Masz wyciszony mikrofon",mutedTitle:"Jeste\u015b wyciszony!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"Kto\u015b",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"",suboptimalExperienceTitle:"",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"wybrane przez innego uczestnika\x7f",passwordDigitsOnly:"",poweredby:"Uruchomiono",presenceStatus:{busy:"",calling:"",connected:"Po\u0142\u0105czono",connecting:"Nawi\u0105zywanie po\u0142\u0105czenia",connecting2:"Nawi\u0105zywanie po\u0142\u0105czenia",disconnected:"Roz\u0142\u0105czony",expired:"",ignored:"",initializingCall:"",invited:"",rejected:"",ringing:""},"\x05presenceStatus":{},profile:{setDisplayNameLabel:"Podaj swoj\u0105 wy\u015bwietlan\u0105 nazw\u0119",setEmailInput:"Wprowad\u017a adres e-mail",setEmailLabel:"Ustaw email swojego gravatara",title:""},recording:{authDropboxText:"",availableSpace:"",beta:"",busy:"",busyTitle:"",error:"Nagranie si\u0119 nie powiod\u0142o. Prosz\u0119, spr\xf3buj ponownie.",expandedOff:"Nagrywanie zatrzymane",expandedOn:"",expandedPending:"",failedToStart:"Nagrywanie nie jest mo\u017cliwe",fileSharingdescription:"",live:"",loggedIn:"",off:"Nagrywanie zatrzymane",on:"Nagrywanie",pending:"",rec:"",serviceDescription:"",serviceName:"",signIn:"",signOut:"",unavailable:"",unavailableTitle:""},"\x05recording":{},sectionList:{pullToRefresh:""},settings:{calendar:{about:"",disconnect:"Roz\u0142\u0105czony",microsoftSignIn:"Zaloguj si\u0119 w firmie Microsoft",signedIn:"",title:""},devices:"Urz\u0105dzenia",followMe:"Wszyscy za mn\u0105",language:"J\u0119zyk",loggedIn:"",moderator:"",more:"Wi\u0119cej",name:"Nazwa",noDevice:"Brak",selectAudioOutput:"Wyj\u015bcie audio",selectCamera:"Kamera",selectMic:"Mikrofon",startAudioMuted:"Wszyscy si\u0119 wyciszyli",startVideoMuted:"Wszyscy si\u0119 ukryli",title:"Ustawienia"},"\x05settings":{calendar:{}},settingsView:{alertOk:"",alertTitle:"Uwaga",alertURLText:"",buildInfoSection:"",conferenceSection:"",displayName:"",email:"",header:"Ustawienia",profileSection:"",serverURL:"",startWithAudioMuted:"",startWithVideoMuted:"",version:""},share:{dialInfoText:"",mainText:""},speaker:"g\u0142o\u015bnik",speakerStats:{hours:"",minutes:"",name:"Nazwa",seconds:"",speakerStats:"Statystyki g\u0142o\u015bnik\xf3w",speakerTime:""},"\x05speakerStats":{},startupoverlay:{policyText:"",title:""},suspendedoverlay:{rejoinKeyTitle:"Do\u0142\u0105cz do nas",text:"",title:"Twoja rozmowa wideo zosta\u0142a przerwana, poniewa\u017c komputer zasn\u0105\u0142."},toolbar:{accessibilityLabel:{audioOnly:"Prze\u0142\u0105czanie tylko audio",audioRoute:"Wybierz urz\u0105dzenie d\u017awi\u0119kowe",callQuality:"",cc:"Prze\u0142\u0105czanie napis\xf3w",chat:"Prze\u0142\u0105czanie okna czatu",document:"Prze\u0142\u0105czanie wsp\xf3lnego dokumentu",feedback:"Zostaw informacj\u0119 zwrotn\u0105",fullScreen:"Prze\u0142\u0105czanie trybu pe\u0142noekranowego",hangup:"Zostaw rozmow\u0119",invite:"Zapraszaj ludzi",kick:"",localRecording:"Prze\u0142\u0105czanie lokalnych urz\u0105dze\u0144 steruj\u0105cych zapisem danych",lockRoom:"",moreActions:"Prze\u0142\u0105czanie menu wi\u0119cej dzia\u0142a\u0144",moreActionsMenu:"Wi\u0119cej dzia\u0142a\u0144 w menu",mute:"Uruchamianie wyciszonego audycji",pip:"Tryb prze\u0142\u0105czania obrazu-w-obrazie",profile:"Edytuj sw\xf3j profil",raiseHand:"Prze\u0142\u0105czy\u0107 r\u0119k\u0119 w g\xf3r\u0119",recording:"Zapisywanie prze\u0142\u0105czania",remoteMute:"",Settings:"Ustawienia prze\u0142\u0105czania",sharedvideo:"",shareRoom:"Zapro\u015b kogo\u015b",shareYourScreen:"Prze\u0142\u0105czanie podzia\u0142u ekranu",shortcuts:"Prze\u0142\u0105czanie skr\xf3t\xf3w klawiszowych",show:"",speakerStats:"Prze\u0142\u0105czanie statystyk dotycz\u0105cych g\u0142o\u015bnik\xf3w",tileView:"Prze\u0142\u0105czanie widoku dach\xf3wki",toggleCamera:"",videomute:"Prze\u0142\u0105czanie wyciszonego filmu wideo",videoblur:""},addPeople:"Dodaj ludzi do swojego telefonu",audioOnlyOff:"Wy\u0142\u0105cz tryb tylko audio",audioOnlyOn:"Wy\u0142\u0105cz tryb tylko audio",audioRoute:"Wybierz urz\u0105dzenie d\u017awi\u0119kowe",authenticate:"Uwierzytelnianie",callQuality:"Zarz\u0105dzanie jako\u015bci\u0105 po\u0142\u0105cze\u0144",chat:"Otw\xf3rz / Zamknij Czat",closeChat:"",documentClose:"Zamknij wsp\xf3lny dokument",documentOpen:"Otwarty wsp\xf3\u0142dzielony dokument",enterFullScreen:"Wy\u015bwietlanie pe\u0142nego ekranu",enterTileView:"",exitFullScreen:"Wy\u015bwietlanie pe\u0142nego ekranu",exitTileView:"",feedback:"Zostaw informacj\u0119 zwrotn\u0105",hangup:"Wyjazd",invite:"Zapraszaj ludzi",login:"Zaloguj",logout:"Wyloguj",lowerYourHand:"",moreActions:"Wi\u0119cej dzia\u0142a\u0144",mute:"Wycisz / Pog\u0142o\u015bnij",openChat:"",pip:"Wprowad\u017a tryb obrazu w obrazie",profile:"Edytuj sw\xf3j profil",raiseHand:"Podnoszenie / opuszczanie r\u0119ki",raiseYourHand:"",Settings:"Ustawienia",sharedvideo:"Udost\u0119pniaj wideo w Youtube",shareRoom:"Zapro\u015b kogo\u015b",shortcuts:"Wy\u015bwietlanie skr\xf3t\xf3w",speakerStats:"Statystyki g\u0142o\u015bnik\xf3w",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"Zatrzymaj wideo z YouTube",talkWhileMutedPopup:"Pr\xf3bujesz m\xf3wi\u0107? Jeste\u015b wyciszony",tileViewToggle:"Prze\u0142\u0105czanie widoku dach\xf3wki",toggleCamera:"",videomute:"Kamera start / stop ",startvideoblur:"",stopvideoblur:""},"\x05toolbar":{},transcribing:{ccButtonTooltip:"",error:"Nagranie si\u0119 nie powiod\u0142o. Prosz\u0119, spr\xf3buj ponownie.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"",start:"",stop:"",tr:""},"\x05transcribing":{},userMedia:{androidGrantPermissions:"",chromeGrantPermissions:"",edgeGrantPermissions:"Wybierz OK, gdy przegladarka zapyta o pozwolenie.",electronGrantPermissions:"wyra\u017a zgod\u0119 na u\u017cycie kamery i mikrofonu",firefoxGrantPermissions:"",iexplorerGrantPermissions:"Wybierz OK, gdy przegladarka zapyta o pozwolenie.",nwjsGrantPermissions:"wyra\u017a zgod\u0119 na u\u017cycie kamery i mikrofonu",operaGrantPermissions:"","react-nativeGrantPermissions":"Wybierz OK, gdy przegladarka zapyta o pozwolenie.",safariGrantPermissions:"Wybierz OK, gdy przegladarka zapyta o pozwolenie."},"\x05userMedia":{},videoSIPGW:{busy:"",busyTitle:"",errorAlreadyInvited:"",errorInvite:"",errorInviteFailed:"",errorInviteFailedTitle:"",errorInviteTitle:"",pending:""},videoStatus:{audioOnly:"",audioOnlyExpanded:"",callQuality:"",hd:"",highDefinition:"",labelTooiltipNoVideo:"",labelTooltipAudioOnly:"",ld:"",lowDefinition:"",onlyAudioAvailable:"",onlyAudioSupported:"",p2pEnabled:"",p2pVideoQualityDescription:"",recHighDefinitionOnly:"",sd:"",standardDefinition:""},videothumbnail:{domute:"Wyciszenie",flip:"Odwr\xf3cenie",kick:"Spadaj!",moderator:"",mute:"Uczestnik ma wyciszone audio",muted:"Wyciszony",remoteControl:"Zdalne sterowanie",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Stuknij aby do\u0142\u0105czy\u0107",roomname:"Podaj nazw\u0119 sali konferencyjnej"},appDescription:"No dalej, pogaw\u0119dka wideo z ca\u0142ym zespo\u0142em. W rzeczywisto\u015bci, zapro\u015b wszystkich, kt\xf3rych znasz. {{app}} jest w pe\u0142ni zaszyfrowanym, w 100% otwartym rozwi\u0105zaniem wideokonferencyjnym, z kt\xf3rego mo\u017cesz korzysta\u0107 przez ca\u0142y dzie\u0144, codziennie, za darmo - bez konieczno\u015bci posiadania konta.",audioVideoSwitch:{audio:"G\u0142os",video:"nagranie"},calendar:"",connectCalendarButton:"Pod\u0142\u0105cz sw\xf3j kalendarz",connectCalendarText:"",enterRoomTitle:"Rozpocznij nowe spotkanie",go:"ID\u0179",join:"",info:"",privacy:"Prywatno\u015b\u0107",recentList:"Niedawno",recentListDelete:"Usu\u0144",recentListEmpty:"Twoja ostatnia lista jest obecnie pusta. Rozmawiaj ze swoim zespo\u0142em, a wszystkie ostatnie spotkania znajdziesz tutaj.",reducedUIText:"",roomname:"Podaj nazw\u0119 sali konferencyjnej",roomnameHint:"Wprowad\u017a nazw\u0119 lub adres URL pokoju, do kt\xf3rego chcesz do\u0142\u0105czy\u0107. Mo\u017cesz wymy\u015bli\u0107 nazw\u0119, po prostu pozw\xf3l, aby osoby, z kt\xf3rymi si\u0119 spotykasz, zna\u0142y j\u0105 tak, aby wpisa\u0142y t\u0119 sam\u0105 nazw\u0119.",sendFeedback:"Wy\u015blij informacj\u0119 zwrotn\u0105",terms:"okre\u015blenia",title:"Bezpieczna, w pe\u0142ni funkcjonalna i ca\u0142kowicie bezp\u0142atna wideokonferencja."}}},672,[]); -__d(function(o,n,s,a,e,r,i){e.exports={en:"Ingl\xeas",af:"Afric\xe2ner",az:"Azerbaijan\xeas",bg:"B\xfalgaro",cs:"Checo",de:"Alem\xe3o",el:"Grego",eo:"Esperanto",es:"Espanhol",fr:"Franc\xeas",hy:"Arm\xeanio",it:"Italiano",ja:"Japon\xeas",ko:"Coreano",nb:"Bokmal noruegu\xeas",oc:"Occitano",pl:"Polon\xeas",ptBR:"Portugu\xeas (Brasil)",ru:"Russo",sk:"Eslovaco",sl:"Esloveno",sv:"Sueco",tr:"Turco",vi:"Vietnamita",zhCN:"Chin\xeas (China)"}},673,[]); -__d(function(e,a,o,r,i,n,t){i.exports={addPeople:{add:"Convidar",countryNotSupported:"Ainda n\xe3o suportamos este destino.",countryReminder:"Ligando de fora dos EUA? Por favor, certifique-se de come\xe7ar com o c\xf3digo do pa\xeds!",disabled:"Voc\xea n\xe3o pode convidar pessoas.",failedToAdd:"Falha em adicionar participantes",footerText:"Discagem est\xe1 desativada.",loading:"Procurando por pessoas e n\xfameros de telefone",loadingNumber:"Validando o n\xfamero de telefone",loadingPeople:"Procurando por pessoas para convidar",noResults:"Nenhum resultado de busca correspondente",noValidNumbers:"Por favor, digite um n\xfamero de telefone",searchNumbers:"Adicionar n\xfameros de telefone",searchPeople:"Pesquisar pessoas",searchPeopleAndNumbers:"Pesquisar por pessoas ou adicionar seus n\xfameros de telefone",telephone:"Telefone: {{number}}",title:"Convide pessoas para sua reuni\xe3o"},audioDevices:{bluetooth:"Bluetooth",headphones:"Fones de ouvido",phone:"Celular",speaker:"Apresentador"},audioOnly:{audioOnly:"Somente \xe1udio"},calendarSync:{addMeetingURL:"Adicionar um link da reuni\xe3o",confirmAddLink:"Gostaria de adicionar um link do Jitsi a esse evento?",error:{appConfiguration:"A integra\xe7\xe3o com calend\xe1rio n\xe3o est\xe1 configurada adequadamente.",generic:"Ocorreu um erro. Verifique as configura\xe7\xf5es de calend\xe1rio ou tente atualizar o calend\xe1rio.",notSignedIn:"Ocorreu um erro durante a autentica\xe7\xe3o para visualiza\xe7\xe3o dos eventos do calend\xe1rio. Verifique as configura\xe7\xf5es de calend\xe1rio e tente entrar novamente."},join:"Participar",joinTooltip:"Participar da reuni\xe3o",nextMeeting:"pr\xf3xima reuni\xe3o",noEvents:"N\xe3o h\xe1 eventos pr\xf3ximos agendados.",ongoingMeeting:"reuni\xe3o em progresso",permissionButton:"Abrir configura\xe7\xf5es",permissionMessage:"Permiss\xe3o do calend\xe1rio \xe9 requerida para ver suas reuni\xf5es na aplica\xe7\xe3o.",refresh:"Atualizar calend\xe1rio",today:"Hoje"},chat:{error:"Erro: sua mensagem \"{{originalText}}\" n\xe3o foi enviada. Motivo: {{error}}",messagebox:"Digite uma mensagem",nickname:{popover:"Escolha um apelido",title:"Digite um apelido para usar o chat"},title:"Chat"},connectingOverlay:{joiningRoom:"Conectando voc\xea \xe0 reuni\xe3o\u2026"},connection:{ATTACHED:"Anexado",AUTHENTICATING:"Autenticando",AUTHFAIL:"Falha de autentica\xe7\xe3o",CONNECTED:"Conectado",CONNECTING:"Conectando",CONNFAIL:"Falha de conex\xe3o",DISCONNECTED:"Desconectado",DISCONNECTING:"Desconectando",ERROR:"Erro",RECONNECTING:"Ocorreu um problema de rede. Reconectando..."},connectionindicator:{address:"Endere\xe7o:",bandwidth:"Largura de banda estimada:",bitrate:"Taxa de bits:",bridgeCount:"Servidores: ",connectedTo:"Conectado a:",framerate:"Taxa de quadros:",less:"Mostrar menos",localaddress:"Endere\xe7o local:",localaddress_plural:"Endere\xe7os locais:",localport:"Porta local:",localport_plural:"Portas locais:",more:"Mostrar mais",packetloss:"Perda de pacote:",quality:{good:"Boa",inactive:"Inativo",lost:"Perdido",nonoptimal:"N\xe3o \xf3tima",poor:"Ruim"},remoteaddress:"Endere\xe7o remoto:",remoteaddress_plural:"Endere\xe7os remotos:",remoteport:"Porta remota:",remoteport_plural:"Portas remotas:",resolution:"Resolu\xe7\xe3o:",status:"Conex\xe3o:",transport:"Transporte:",transport_plural:"Transportes:",turn:" (virar)"},dateUtils:{earlier:"Mais cedo",today:"Hoje",yesterday:"Ontem"},deepLinking:{appNotInstalled:"Voc\xea precisa do aplicativo m\xf3vel {{app}} para participar da reuni\xe3o no seu telefone.",description:"Nada acontece? Estamos tentando iniciar sua reuni\xe3o no aplicativo desktop {{app}}. Tente novamente ou inicie ele na aplica\xe7\xe3o web {{app}}.",descriptionWithoutWeb:"",downloadApp:"Baixe o Aplicativo",launchWebButton:"Iniciar na web",openApp:"Continue na aplica\xe7\xe3o",title:"Iniciando sua reuni\xe3o no {{app}}...",tryAgainButton:"Tente novamente no desktop"},defaultLink:"ex.: {{url}}",deviceError:{cameraError:"Falha ao acessar sua c\xe2mera",cameraPermission:"Erro ao obter permiss\xe3o para a c\xe2mera",microphoneError:"Falha ao acessar seu microfone",microphonePermission:"Erro ao obter permiss\xe3o para o microfone"},deviceSelection:{noPermission:"Permiss\xe3o n\xe3o concedida",previewUnavailable:"Visualiza\xe7\xe3o indispon\xedvel",selectADevice:"Selecione um dispositivo",testAudio:"Tocar um som de teste"},dialog:{accessibilityLabel:{liveStreaming:"Transmiss\xe3o ao vivo"},allow:"Permitir",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Somente um v\xeddeo compartilhado \xe9 permitido por vez",applicationWindow:"Janela de aplicativo",Back:"Voltar",cameraConstraintFailedError:"Sua c\xe2mera n\xe3o satisfaz algumas condi\xe7\xf5es necess\xe1rias.",cameraNotFoundError:"A c\xe2mera n\xe3o foi encontrada.",cameraNotSendingData:"Estamos incapazes de acessar sua c\xe2mera. Verifique se outra aplica\xe7\xe3o est\xe1 usando este dispositivo, selecione outro dispositivo do menu de configura\xe7\xf5es ou recarregue a aplica\xe7\xe3o.",cameraNotSendingDataTitle:"Incapaz de acessar a c\xe2mera",cameraPermissionDeniedError:"N\xe3o foi permitido acessar a sua c\xe2mera. Voc\xea ainda pode entrar na confer\xeancia, mas sem exibir o seu v\xeddeo. Clique no bot\xe3o da c\xe2mera para tentar reparar.",cameraUnknownError:"N\xe3o pode usar a c\xe2mera por uma raz\xe3o desconhecida.",cameraUnsupportedResolutionError:"Sua c\xe2mera n\xe3o suporta a resolu\xe7\xe3o de v\xeddeo requerida.",Cancel:"Cancelar",close:"Fechar",conferenceDisconnectMsg:"Voc\xea pode querer verificar sua conex\xe3o de rede. Reconectando em {{seconds}} segundos ...",conferenceDisconnectTitle:"Voc\xea foi desconectado.",conferenceReloadMsg:"Estamos tentando consertar isto. Reconectando em {{seconds}} segundos...",conferenceReloadTitle:"Infelizmente, algo deu errado.",confirm:"Confirmar",confirmNo:"N\xe3o",confirmYes:"Sim",connectError:"Oops! Alguma coisa est\xe1 errada e n\xf3s n\xe3o pudemos conectar \xe0 confer\xeancia.",connectErrorWithMsg:"Oops! Alguma coisa est\xe1 errada e n\xe3o podemos conectar \xe0 confer\xeancia: {{msg}}",connecting:"Conectando",contactSupport:"Contate o suporte",copy:"Copiar",dismiss:"Dispensar",displayNameRequired:"",done:"Feito",enterDisplayName:"",error:"Erro",externalInstallationMsg:"Voc\xea precisa instalar nossa extens\xe3o de compartilhamento de tela.",externalInstallationTitle:"Extens\xe3o requerida",goToStore:"V\xe1 para a loja virtual",gracefulShutdown:"O sistema est\xe1 em manuten\xe7\xe3o. Por favor tente novamente mais tarde.",IamHost:"Eu sou o anfitri\xe3o",incorrectRoomLockPassword:"",incorrectPassword:"Usu\xe1rio ou senha incorretos",inlineInstallationMsg:"Voc\xea precisa instalar nossa extens\xe3o de compartilhamento de tela.",inlineInstallExtension:"Instalar agora",internalError:"Oops! Alguma coisa est\xe1 errada. O seguinte erro ocorreu: {{error}}",internalErrorTitle:"Erro interno",kickMessage:"",kickParticipantButton:"Remover",kickParticipantDialog:"Tem certeza de que deseja remover este participante?",kickParticipantTitle:"Deixar mudo este participante?",kickTitle:"",liveStreaming:"Transmiss\xe3o ao Vivo",liveStreamingDisabledForGuestTooltip:"Visitantes n\xe3o podem iniciar transmiss\xe3o ao vivo.",liveStreamingDisabledTooltip:"Iniciar transmiss\xe3o ao vivo desativada.",lockMessage:"Falha ao travar a confer\xeancia.",lockRoom:"",lockTitle:"Bloqueio falhou",logoutQuestion:"Deseja encerrar a sess\xe3o e finalizar a confer\xeancia?",logoutTitle:"Encerrar sess\xe3o",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Seu microfone n\xe3o satisfaz algumas condi\xe7\xf5es necess\xe1rias.",micNotFoundError:"O microfone n\xe3o foi encontrado.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"N\xe3o foi permitido acessar o seu microfone. Voc\xea ainda pode entrar na confer\xeancia, mas sem enviar \xe1udio. Clique no bot\xe3o do microfone para tentar reparar.",micUnknownError:"N\xe3o pode usar o microfone por uma raz\xe3o desconhecida.",muteParticipantBody:"Voc\xea n\xe3o est\xe1 habilitado para tirar o mudo deles, mas eles podem tirar o mudo deles mesmos a qualquer tempo.",muteParticipantButton:"Mudo",muteParticipantDialog:"Tem certeza de que deseja silenciar este participante? Voc\xea n\xe3o poder\xe1 desativar a op\xe7\xe3o silenciar dele, mas ele poder\xe1 fazer isso quando desejar.",muteParticipantTitle:"Deixar mudo este participante?",Ok:"Ok",passwordLabel:"",passwordNotSupported:"Configura\xe7\xe3o de senha para a reuni\xe3o n\xe3o \xe9 suportada.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Seu navegador est\xe1 bloqueando janelas popup deste site. Habilite os popups nas configura\xe7\xf5es de seguran\xe7a no seu navegador e tente novamente.",popupErrorTitle:"Popup bloqueado",recording:"Gravando",recordingDisabledForGuestTooltip:"Visitantes n\xe3o podem iniciar grava\xe7\xf5es.",recordingDisabledTooltip:"Iniciar grava\xe7\xe3o desativada.",rejoinNow:"Reconectar agora",remoteControlAllowedMessage:"{{user}} aceitou sua requisi\xe7\xe3o de controle remoto!",remoteControlDeniedMessage:"{{user}} rejeitou sua requisi\xe7\xe3o de controle remoto!",remoteControlErrorMessage:"Um erro ocorreu enquanto tentava requerer a permiss\xe3o de controle remoto de {{user}}!",remoteControlRequestMessage:"Deseja permitir que {{user}} controle remotamente sua \xe1rea de trabalho?",remoteControlShareScreenWarning:"Note que se voc\xea pressionar \"Permitir\" voc\xea vai compartilhar sua tela!",remoteControlStopMessage:"A sess\xe3o de controle remoto terminou!",remoteControlTitle:"Conex\xe3o de \xe1rea de trabalho remota",Remove:"Remover",removePassword:"",removeSharedVideoMsg:"Deseja remover seu v\xeddeo compartilhado?",removeSharedVideoTitle:"Remover v\xeddeo compartilhado",reservationError:"Erro de sistema de reserva",reservationErrorMsg:"C\xf3digo do erro: {{code}}, mensagem: {{msg}}",retry:"Tentar novamente",screenSharingFailedToInstall:"Oops! Falhou a instala\xe7\xe3o da extens\xe3o de compartilhamento de tela.",screenSharingFailedToInstallTitle:"A extens\xe3o de compartilhamento de tela falhou ao instalar",screenSharingFirefoxPermissionDeniedError:"Algo deu errado enquanto est\xe1vamos tentando compartilhar sua tela. Por favor, certifique-se de que voc\xea nos deu permiss\xe3o para faz\xea-lo. ",screenSharingFirefoxPermissionDeniedTitle:"Opa! N\xe3o foi poss\xedvel iniciar o compartilhamento de tela.",screenSharingPermissionDeniedError:"Oops! Alguma coisa est\xe1 errada com suas permiss\xf5es de compartilhamento de tela. Recarregue e tente de novo.",serviceUnavailable:"Servi\xe7o indispon\xedvel",sessTerminated:"Chamada terminada",Share:"Compartilhar",shareVideoLinkError:"Por favor, forne\xe7a um link do youtube correto.",shareVideoTitle:"Compartilhar um v\xeddeo",shareYourScreen:"Compartilhar sua tela",shareYourScreenDisabled:"Compartilhamento de tela desativada.",shareYourScreenDisabledForGuest:"Visitantes n\xe3o podem compartilhar tela.",startLiveStreaming:"Iniciar transmiss\xe3o ao vivo",startRecording:"Iniciar grava\xe7\xe3o",startRemoteControlErrorMessage:"Um erro ocorreu enquanto tentava iniciar uma sess\xe3o de controle remoto!",stopLiveStreaming:"Parar transmiss\xe3o ao vivo",stopRecording:"Parar a grava\xe7\xe3o",stopRecordingWarning:"Tem certeza que deseja parar a grava\xe7\xe3o?",stopStreamingWarning:"Tem certeza que deseja parar a transmiss\xe3o ao vivo?",streamKey:"Chave para transmiss\xe3o ao vivo",Submit:"Enviar",thankYou:"Obrigado por usar o {{appName}}!",token:"token",tokenAuthFailed:"Desculpe, voc\xea n\xe3o est\xe1 autorizado a entrar nesta chamada.",tokenAuthFailedTitle:"Falha de autentica\xe7\xe3o",transcribing:"Transcrevendo",unlockRoom:"",userPassword:"senha do usu\xe1rio",WaitForHostMsg:"A confer\xeancia {{room}} ainda n\xe3o come\xe7ou. Se voc\xea \xe9 o anfitri\xe3o, fa\xe7a a autentica\xe7\xe3o. Do contr\xe1rio, aguarde a chegada do anfitri\xe3o.",WaitForHostMsgWOk:"A confer\xeancia {{room}} ainda n\xe3o come\xe7ou. Se voc\xea \xe9 o anfitri\xe3o, pressione Ok para autenticar. Do contr\xe1rio, aguarde a chegada do anfitri\xe3o.",WaitingForHost:"Esperando o hospedeiro...",Yes:"Sim",yourEntireScreen:"Toda sua tela"},dialOut:{statusMessage:"est\xe1 agora {{status}}"},feedback:{average:"M\xe9dia",bad:"Ruim",detailsLabel:"Nos conte mais sobre isso.",good:"Boa",rateExperience:"Avalie sua experi\xeancia na reuni\xe3o",veryBad:"Muito ruim",veryGood:"Muito boa"},incomingCall:{answer:"Responder",audioCallTitle:"Chamada recebida",decline:"Dispensar",productLabel:"do Jitsi Meet",videoCallTitle:"Chamada de v\xeddeo recebida"},info:{accessibilityLabel:"Mostrar info",addPassword:"",cancelPassword:"",conferenceURL:"Link:",country:"Pa\xeds",dialANumber:"Para entrar na reuni\xe3o, disque um desses n\xfameros e depois insira o PIN.",dialInConferenceID:"PIN:",dialInNotSupported:"Desculpe, a discagem n\xe3o \xe9 atualmente suportada.",dialInNumber:"Discar:",dialInSummaryError:"Ocorreu um erro ao buscar a informa\xe7\xe3o de discagem. Tente novamente mais tarde.",dialInTollFree:"Chamada gratuita",genericError:"Oops, alguma coisa deu errado.",inviteLiveStream:"Para ver a transmiss\xe3o ao vivo da reuni\xe3o, clique no link: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Voc\xea foi convidado para uma reuni\xe3o.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Transmiss\xe3o ao vivo:",moreNumbers:"Mais n\xfameros",noNumbers:"Sem n\xfameros de discagem.",noPassword:"Nenhum",noRoom:"Nenhuma sala foi especificada para entrar.",numbers:"N\xfameros de discagem",password:"",title:"Compartilhar",tooltip:"Compartilhar link e discagem para esta reuni\xe3o",label:"Informa\xe7\xf5es da reuni\xe3o"},inviteDialog:{alertText:"N\xe3o foi poss\xedvel convidar alguns participantes.",header:"Convidar",searchCallOnlyPlaceholder:"Digite o n\xfamero do telefone",searchPeopleOnlyPlaceholder:"Buscar participantes",searchPlaceholder:"Participante ou n\xba de telefone",send:"Enviar"},inlineDialogFailure:{msg:"Tivemos um pequeno problema.",retry:"Tentar novamente",support:"Suporte",supportMsg:"Se isso continuar acontecendo, chegar a"},keyboardShortcuts:{focusLocal:"Focar no seu v\xeddeo",focusRemote:"Focar no v\xeddeo de outro participante",fullScreen:"Entrar ou sair da tela cheia",keyboardShortcuts:"Atalhos de teclado",localRecording:"Mostrar ou ocultar controles de grava\xe7\xe3o local",mute:"Deixar mudo ou n\xe3o o microfone",pushToTalk:"Pressione para falar",raiseHand:"Erga ou baixe sua m\xe3o",showSpeakerStats:"Exibir estat\xedsticas do alto falante",toggleChat:"Abrir ou fechar o painel de bate-papo",toggleFilmstrip:"Mostrar ou ocultar miniaturas de v\xeddeo",toggleScreensharing:"Trocar entre c\xe2mera e compartilhamento de tela",toggleShortcuts:"Mostrar ou ocultar atalhos de teclado",videoMute:"Iniciar ou parar sua c\xe2mera"},liveStreaming:{busy:"Estamos trabalhando para liberar os recursos de transmiss\xe3o. Tente novamente em alguns minutos.",busyTitle:"Todas as transmiss\xf5es est\xe3o atualmente ocupadas",changeSignIn:"Alternar contas.",choose:"Escolha uma transmiss\xe3o ao vivo",chooseCTA:"Escolha uma op\xe7\xe3o de transmiss\xe3o. Voc\xea est\xe1 conectado atualmente como {{email}}.",enterStreamKey:"Insira sua chave de transmiss\xe3o ao vivo do YouTube aqui.",error:"Falha na transmiss\xe3o ao vivo. Tente de novo.",errorAPI:"Ocorreu um erro ao acessar suas transmiss\xf5es do YouTube. Por favor tente logar novamente.",errorLiveStreamNotEnabled:"Transmiss\xe3o ao vivo n\xe3o est\xe1 ativada em {{email}}. Ative a transmiss\xe3o ao vivo ou registre numa conta com transmiss\xe3o ao vivo ativada.",expandedOff:"A transmiss\xe3o ao vivo foi encerrada",expandedOn:"A reuni\xe3o est\xe1 sendo transmitida pelo YouTube.",expandedPending:"A transmiss\xe3o ao vivo est\xe1 sendo iniciada\u2026",failedToStart:"Falha ao iniciar a transmiss\xe3o ao vivo",getStreamKeyManually:"",invalidStreamKey:"A senha para transmiss\xe3o ao vivo pode estar incorreta.",off:"Transmiss\xe3o ao vivo encerrada",on:"Transmiss\xe3o ao Vivo",pending:"Iniciando Transmiss\xe3o ao Vivo...",serviceName:"Servi\xe7o de Transmiss\xe3o ao Vivo",signedInAs:"Voc\xea est\xe1 conectado atualmente como:",signIn:"Fa\xe7a login no Google",signInCTA:"Fa\xe7a login ou insira sua chave de transmiss\xe3o ao vivo do YouTube.",signOut:"Sair",start:"Iniciar uma transmiss\xe3o ao vivo",streamIdHelp:"O que \xe9 isso?",unavailableTitle:"Transmiss\xe3o ao vivo indispon\xedvel"},localRecording:{clientState:{off:"Off",on:"On",unknown:"Desconhecido"},dialogTitle:"Controles da Grava\xe7\xe3o Local",duration:"Dura\xe7\xe3o",durationNA:"N/A",encoding:"Codificando",label:"LOR",labelToolTip:"Grava\xe7\xe3o local est\xe1 envolvida",localRecording:"Grava\xe7\xe3o local",me:"Eu",messages:{engaged:"Grava\xe7\xe3o local iniciada.",finished:"Sess\xe3o de grava\xe7\xe3o {{token}} terminada. Por favor, envie o arquivo gravado para o moderador.",finishedModerator:"Sess\xe3o de grava\xe7\xe3o {{token}} terminada. A grava\xe7\xe3o da faixa local foi salva. Por favor, pe\xe7a aos outros participantes para enviar suas grava\xe7\xf5es.",notModerator:"Voc\xea n\xe3o \xe9 o moderador. Voc\xea n\xe3o pode iniciar ou parar a grava\xe7\xe3o local."},moderator:"Moderador",no:"N\xe3o",participant:"Participante",participantStats:"Estat\xedsticas dos Participantes",sessionToken:"Token de Sess\xe3o",start:"Iniciar grava\xe7\xe3o",stop:"Parar a Grava\xe7\xe3o",yes:"Sim"},lockRoomPassword:"senha",lockRoomPasswordUppercase:"Senha",me:"eu",notify:{connectedOneMember:"{{name}} entrou na reuni\xe3o",connectedThreePlusMembers:"{{name}} e outros {{count}} entraram na reuni\xe3o",connectedTwoMembers:"{{first}} e {{second}} entraram na reuni\xe3o",disconnected:"desconectado",focus:"Foco da confer\xeancia",focusFail:"{{component}} n\xe3o dispon\u0129vel - tente em {{ms}} seg.",grantedTo:"Direitos de moderador concedido para {{to}}!",invitedOneMember:"{{displayName}} foi convidado",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Eu",moderator:"Direitos de moderador concedidos!",muted:"Voc\xea iniciou uma conversa em mudo.",mutedTitle:"Voc\xea est\xe1 mudo!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} gostaria de falar.",somebody:"Algu\xe9m",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Eer ... temos medo de que sua experi\xeancia com o {{appName}} n\xe3o seja t\xe3o boa aqui. Estamos procurando maneiras de melhorar isso, mas at\xe9 l\xe1 tente usar um dos navegadores totalmente compat\xedveis.",suboptimalExperienceTitle:"Alerta do navegador",unmute:"",newDeviceCameraTitle:"Nova c\xe2mera detectada",newDeviceAudioTitle:"Novo dispositivo de \xe1udio detectado",newDeviceAction:"Usar"},passwordSetRemotely:"Definido por outro participante",passwordDigitsOnly:"",poweredby:"distribu\xeddo por",presenceStatus:{busy:"Ocupado",calling:"Chamando...",connected:"Conectado",connecting:"Conectando...",connecting2:"Conectando*...",disconnected:"Desconectado",expired:"Expirado",ignored:"Ignorado",initializingCall:"Iniciando Chamada...",invited:"Convidar",rejected:"Rejeitado",ringing:"Chamando..."},profile:{setDisplayNameLabel:"Definir seu nome de exibi\xe7\xe3o",setEmailInput:"Digite e-mail",setEmailLabel:"Definir seu email de gravatar",title:"Perfil"},recording:{authDropboxText:"Enviar para o Dropbox.",availableSpace:"Espa\xe7o dispon\xedvel: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de grava\xe7\xe3o)",beta:"BETA",busy:"Estamos trabalhando para liberar recursos de grava\xe7\xe3o. Tente novamente em alguns minutos.",busyTitle:"Todas as grava\xe7\xf5es est\xe3o atualmente ocupadas",error:"A grava\xe7\xe3o falhou. Tente novamente.",expandedOff:"Grava\xe7\xe3o finalizada",expandedOn:"A reuni\xe3o est\xe1 sendo gravada.",expandedPending:"Iniciando grava\xe7\xe3o...",failedToStart:"Falha ao iniciar a grava\xe7\xe3o",fileSharingdescription:"Compartilhar grava\xe7\xe3o com participantes da reuni\xe3o",live:"AOVIVO",loggedIn:"Conectado como {{userName}}",off:"Grava\xe7\xe3o parada",on:"Gravando",pending:"Preparando para gravar a reuni\xe3o...",rec:"REC",serviceDescription:"Sua grava\xe7\xe3o ser\xe1 salva pelo servi\xe7o de grava\xe7\xe3o",serviceName:"Servi\xe7o de grava\xe7\xe3o",signIn:"entrar",signOut:"Sair",unavailable:"Oops! O {{serviceName}} est\xe1 indispon\xedvel. Estamos trabalhando para resolver o problema. Por favor, tente mais tarde.",unavailableTitle:"Grava\xe7\xe3o indispon\xedvel"},sectionList:{pullToRefresh:"Puxe para atualizar"},settings:{calendar:{about:"A integra\xe7\xe3o do calend\xe1rio {{appName}} \xe9 usada para acessar com seguran\xe7a o seu calend\xe1rio para que ele possa ler os pr\xf3ximos eventos.",disconnect:"Desconectar",microsoftSignIn:"Entrar com Microsoft",signedIn:"Atualmente acessando eventos do calend\xe1rio para {{email}}. Clique no bot\xe3o Desconectar abaixo para parar de acessar os eventos da agenda.",title:"Calend\xe1rio"},devices:"Dispositivos",followMe:"Todos me seguem",language:"Idioma",loggedIn:"Conectado como {{name}}",moderator:"Moderador",more:"Mais",name:"Nome",noDevice:"Nenhum",selectAudioOutput:"Sa\xedda de \xe1udio",selectCamera:"C\xe2mera",selectMic:"Microfone",startAudioMuted:"Todos iniciam mudos",startVideoMuted:"Todos iniciam ocultos",title:"Configura\xe7\xf5es"},settingsView:{alertOk:"OK",alertTitle:"Aten\xe7\xe3o",alertURLText:"A URL digitada do servidor \xe9 inv\xe1lida",buildInfoSection:"Informa\xe7\xf5es de compila\xe7\xe3o",conferenceSection:"Confer\xeancia",displayName:"Nome de exibi\xe7\xe3o",email:"E-mail",header:"Configura\xe7\xf5es",profileSection:"Perfil",serverURL:"URL do servidor",startWithAudioMuted:"Iniciar sem \xe1udio",startWithVideoMuted:"Iniciar sem v\xeddeo",version:"Vers\xe3o"},share:{dialInfoText:"",mainText:"Clique no seguinte link para entrar na reuni\xe3o:{{roomUrl}}\n"},speaker:"Apresentador",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Nome",seconds:"{{count}}s",speakerStats:"Estat\xedsticas do Apresentador",speakerTime:"Tempo do Apresentador"},startupoverlay:{policyText:" ",title:"O {{app}} precisa usar seu microfone e c\xe2mera."},suspendedoverlay:{rejoinKeyTitle:"Reconectar",text:"Pressione o bot\xe3o Reentrar para reconectar.",title:"Sua chamada de v\xeddeo foi interrompida, porque seu computador foi dormir."},toolbar:{accessibilityLabel:{audioOnly:"Alternar para apenas \xe1udio",audioRoute:"Selecionar o dispositivo de som",callQuality:"Gerenciar qualidade da chamada",cc:"Alternar legendas",chat:"Alternar para janela de chat",document:"Alternar para documento compartilhado",feedback:"Deixar feedback",fullScreen:"Alternar para tela cheia",hangup:"Sair da chamada",invite:"Convidar pessoas",kick:"Remover participante",localRecording:"Alternar controles de grava\xe7\xe3o local",lockRoom:"Ativar/desativar senha de reuni\xe3o",moreActions:"Alternar mais menu de a\xe7\xf5es",moreActionsMenu:"Menu de mais a\xe7\xf5es",mute:"Alternar mudo do \xe1udio",pip:"Alternar modo Picture-in-Picture",profile:"Editar seu perfil",raiseHand:"Alternar levantar a m\xe3o",recording:"Alternar grava\xe7\xe3o",remoteMute:"Silenciar participante",Settings:"Alternar configura\xe7\xf5es",sharedvideo:"Alternar compartilhamento de v\xeddeo do Youtube",shareRoom:"Convidar algu\xe9m",shareYourScreen:"Alternar compartilhamento de tela",shortcuts:"Alternar atalhos",show:"",speakerStats:"Alternar estat\xedsticas do apresentador",tileView:"Alternar visualiza\xe7\xe3o em blocos",toggleCamera:"Alternar c\xe2mera",videomute:"Alternar mudo do v\xeddeo",videoblur:""},addPeople:"Adicionar pessoas \xe0 sua chamada",audioOnlyOff:"Desativar modo somente \xe1udio",audioOnlyOn:"Desativar modo somente \xe1udio",audioRoute:"Selecionar o dispositivo de som",authenticate:"Autenticar",callQuality:"Gerenciar qualidade da chamada",chat:"Abrir ou fechar o bate-papo",closeChat:"Fechar chat",documentClose:"Fechar documento compartilhado",documentOpen:"Abrir documento compartilhado",enterFullScreen:"Ver em tela cheia",enterTileView:"Entrar em exibi\xe7\xe3o de bloco",exitFullScreen:"Sair da tela cheia",exitTileView:"Sair de exibi\xe7\xe3o de bloco",feedback:"Deixar feedback",hangup:"Sair",invite:"Convidar pessoas",login:"Iniciar sess\xe3o",logout:"Encerrar sess\xe3o",lowerYourHand:"Baixar a m\xe3o",moreActions:"Mais a\xe7\xf5es",mute:"Mudo / N\xe3o mudo",openChat:"Abrir chat",pip:"Entrar em modo Quadro-a-Quadro",profile:"Editar seu perfil",raiseHand:"Erguer / Baixar sua m\xe3o",raiseYourHand:"Levantar a m\xe3o",Settings:"Configura\xe7\xf5es",sharedvideo:"Compartilhar um v\xeddeo do YouTube",shareRoom:"Convidar algu\xe9m",shortcuts:"Ver atalhos",speakerStats:"Estat\xedsticas do Apresentador",startScreenSharing:"Iniciar compart. de tela",startSubtitles:"Iniciar legendas",stopScreenSharing:"Parar compart. de tela",stopSubtitles:"Parar legendas",stopSharedVideo:"Parar v\xeddeo do YouTube",talkWhileMutedPopup:"Tentando falar? Voc\xea est\xe1 em mudo.",tileViewToggle:"Alternar visualiza\xe7\xe3o em blocos",toggleCamera:"Alternar c\xe2mera",videomute:"Iniciar ou parar a c\xe2mera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Iniciar/parar legendas",error:"Transcri\xe7\xe3o falhou. Tente novamente.",expandedLabel:"Transcri\xe7\xe3o ligada",failedToStart:"Transcri\xe7\xe3o falhou ao iniciar",labelToolTip:"A reuni\xe3o esta sendo transcrita",off:"Transcri\xe7\xe3o parada",pending:"Preparando a transcri\xe7\xe3o da reuni\xe3o...",start:"Iniciar / Parar de mostrar as legendas",stop:"Iniciar / Parar de mostrar as legendas",tr:"TR"},userMedia:{androidGrantPermissions:"Selecione Permitir quando seu navegador perguntar pelas permiss\xf5es.",chromeGrantPermissions:"Selecione Permitir quando seu navegador perguntar pelas permiss\xf5es.",edgeGrantPermissions:"Selecione Sim quando seu navegador perguntar pelas permiss\xf5es.",electronGrantPermissions:"D\xea as permiss\xf5es para usar sua c\xe2mera e microfone",firefoxGrantPermissions:"Selecione Compartilhar Dispositivos Selecionados quando seu navegador perguntar pelas permiss\xf5es.",iexplorerGrantPermissions:"Selecione OK quando seu navegador perguntar pelas permiss\xf5es.",nwjsGrantPermissions:"D\xea as permiss\xf5es para usar sua c\xe2mera e microfone",operaGrantPermissions:"Selecione Permitir quando seu navegador perguntar pelas permiss\xf5es.","react-nativeGrantPermissions":"Selecione Permitir quando seu navegador perguntar pelas permiss\xf5es.",safariGrantPermissions:"Selecione OK quando seu navegador perguntar pelas permiss\xf5es."},videoSIPGW:{busy:"Estamos trabalhando para liberar recursos. Por favor, tente novamente em alguns minutos.",busyTitle:"O servi\xe7o da sala est\xe1 ocupado",errorAlreadyInvited:"{{displayName}} j\xe1 convidado",errorInvite:"A confer\xeancia ainda n\xe3o foi estabelecida. Por favor, tente mais tarde.",errorInviteFailed:"Estamos trabalhando para resolver o problema. Por favor, tente mais tarde.",errorInviteFailedTitle:"Convite para {{displayName}} falhou",errorInviteTitle:"Erro no convite da sala",pending:"{{displayName}} foi convidado"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Voc\xea est\xe1 no modo somente \xe1udio. Esse modo economiza internet mas n\xe3o permite ver o v\xeddeo dos outros.",callQuality:"",hd:"HD",highDefinition:"Alta defini\xe7\xe3o (HD)",labelTooiltipNoVideo:"Sem v\xeddeo",labelTooltipAudioOnly:"Modo somente de \xe1udio habilitado",ld:"LD",lowDefinition:"Baixa defini\xe7\xe3o (LD)",onlyAudioAvailable:"Somente \xe1udio dispon\xedvel",onlyAudioSupported:"Suportamos somente \xe1udio neste navegador.",p2pEnabled:"Ponto-a-ponto habilitada",p2pVideoQualityDescription:"",recHighDefinitionOnly:"Prefer\xeancia para alta defini\xe7\xe3o",sd:"SD",standardDefinition:"Defini\xe7\xe3o padr\xe3o"},videothumbnail:{domute:"Mudo",flip:"Inverter",kick:"Expulsar",moderator:"Moderador",mute:"Participante est\xe1 mudo",muted:"Mudo",remoteControl:"Controle remoto",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Toque para entrar",roomname:"Digite o nome da sala"},appDescription:"V\xe1 em frente, converse por v\xeddeo com toda a equipe. De fato, convide todos que voc\xea conhece. {{app}} \xe9 uma solu\xe7\xe3o de videoconfer\xeancia totalmente criptografada e 100% de c\xf3digo aberto que voc\xea pode usar todos os dias, a cada dia, gratuitamente \u2014 sem necessidade de conta.",audioVideoSwitch:{audio:"Voz",video:"V\xeddeo"},calendar:"Calend\xe1rio",connectCalendarButton:"Conectar seu calend\xe1rio",connectCalendarText:"Conecte seu calend\xe1rio para ver todas as reuni\xf5es em {{app}}. Al\xe9m disso, adicione reuni\xf5es de {{provider}} ao seu calend\xe1rio e inicie-as com apenas um clique.",enterRoomTitle:"Iniciar uma nova reuni\xe3o",go:"IR",join:"Entrar",info:"Informa\xe7\xf5es",privacy:"Pol\xedtica de Privacidade",recentList:"Recente",recentListDelete:"Remover",recentListEmpty:"Sua lista recente est\xe1 vazia. As reuni\xf5es que voc\xea realizar ser\xe3o exibidas aqui.",reducedUIText:"",roomname:"Digite o nome da sala",roomnameHint:"Digite o nome ou a URL da sala que voc\xea deseja entrar. Voc\xea pode digitar um nome, e apenas deixe para as pessoas que voc\xea quer se reunir digitem o mesmo nome.",sendFeedback:"Enviar coment\xe1rios",terms:"Termos",title:"Videoconfer\xeancias mais seguras, flex\xedveis e totalmente gratuitas"}}},674,[]); -__d(function(e,r,f,n,o,s,t){o.exports={en:"\u0410\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439",af:"",bg:"\u0411\u043e\u043b\u0433\u0430\u0440\u0441\u043a\u0438\u0439",ca:"",cs:"\u0427\u0435\u0448\u0441\u043a\u0438\u0439",de:"\u041d\u0435\u043c\u0435\u0446\u043a\u0438\u0439",el:"\u0413\u0440\u0435\u0447\u0435\u0441\u043a\u0438\u0439",enGB:"",eo:"\u042d\u0441\u043f\u0435\u0440\u0430\u043d\u0442\u043e",es:"\u0418\u0441\u043f\u0430\u043d\u0441\u043a\u0438\u0439",esUS:"",fi:"",fr:"\u0424\u0440\u0430\u043d\u0446\u0443\u0437\u0441\u043a\u0438\u0439",frCA:"",hr:"",hy:"\u0410\u0440\u043c\u044f\u043d\u0441\u043a\u0438\u0439",it:"\u0418\u0442\u0430\u043b\u044c\u044f\u043d\u0441\u043a\u0438\u0439",ja:"\u042f\u043f\u043e\u043d\u0441\u043a\u0438\u0439",ko:"\u041a\u043e\u0440\u0435\u0439\u0441\u043a\u0438\u0439",nl:"",oc:"\u041e\u043a\u0441\u0438\u0442\u0430\u043d\u0441\u043a\u0438\u0439",pl:"\u041f\u043e\u043b\u044c\u0441\u043a\u0438\u0439",ptBR:"\u041f\u043e\u0440\u0442\u0443\u0433\u0430\u043b\u044c\u0441\u043a\u0438\u0439 (\u0411\u0440\u0430\u0437\u0438\u043b\u0438\u044f)",ru:"\u0420\u0443\u0441\u0441\u043a\u0438\u0439",sv:"\u0428\u0432\u0435\u0434\u0441\u043a\u0438\u0439",tr:"\u0422\u0443\u0440\u0435\u0446\u043a\u0438\u0439",vi:"\u0412\u044c\u0435\u0442\u043d\u0430\u043c\u0441\u043a\u0438\u0439",zhCN:"\u041a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0439 (\u041a\u0438\u0442\u0430\u0439)",zhTW:""}},675,[]); -__d(function(e,o,i,t,r,a,n){r.exports={addPeople:{add:"\u041f\u0440\u0438\u0433\u043b\u0430\u0441\u0438\u0442\u044c",countryNotSupported:"\u042d\u0442\u0430 \u0441\u0442\u0440\u0430\u043d\u0430 \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.",countryReminder:"\u0412\u044b\u0437\u043e\u0432 \u043d\u0435 \u0432 \u0421\u0428\u0410? \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043a\u043e\u0434 \u0441\u0442\u0440\u0430\u043d\u044b!",disabled:"\u041f\u043e\u0438\u0441\u043a \u043d\u0435 \u0434\u0430\u043b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430",failedToAdd:"",footerText:"\u0412\u044b\u0437\u043e\u0432 \u043d\u043e\u043c\u0435\u0440\u0430 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d.",loading:"\u041f\u043e\u0438\u0441\u043a \u043b\u044e\u0434\u0435\u0439 \u0438 \u043d\u043e\u043c\u0435\u0440\u043e\u0432 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u043e\u0432",loadingNumber:"\u041f\u043e\u0438\u0441\u043a \u043b\u044e\u0434\u0435\u0439 \u0434\u043b\u044f \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f",loadingPeople:"\u041f\u043e\u0438\u0441\u043a \u043b\u044e\u0434\u0435\u0439 \u0434\u043b\u044f \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f",noResults:"\u041f\u043e\u0438\u0441\u043a \u043d\u0435 \u0434\u0430\u043b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430",noValidNumbers:"\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430",searchNumbers:"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u043c\u0435\u0440\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u043e\u0432",searchPeople:"\u041f\u043e\u0438\u0441\u043a \u043d\u0435 \u0434\u0430\u043b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430",searchPeopleAndNumbers:"\u041f\u043e\u0438\u0441\u043a \u043b\u044e\u0434\u0435\u0439 \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0445 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u043e\u0432",telephone:"\u041d\u043e\u043c\u0435\u0440: {{number}}",title:"\u041f\u0440\u0438\u0433\u043b\u0430\u0441\u0438\u0442\u044c \u043b\u044e\u0434\u0435\u0439 \u043d\u0430 \u044d\u0442\u0443 \u0432\u0441\u0442\u0440\u0435\u0447\u0443"},audioDevices:{bluetooth:"Bluetooth",headphones:"\u041d\u0430\u0443\u0448\u043d\u0438\u043a\u0438",phone:"\u0422\u0435\u043b\u0435\u0444\u043e\u043d",speaker:"\u041a\u043e\u043b\u043e\u043d\u043a\u0430",none:""},audioOnly:{audioOnly:"\u041d\u0435 \u043d\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043a\u0430\u043d\u0430\u043b"},calendarSync:{addMeetingURL:"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438",confirmAddLink:"",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"",joinTooltip:"",nextMeeting:"\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0432\u0441\u0442\u0440\u0435\u0447\u0430",noEvents:"\u041d\u0435\u0442 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439.",ongoingMeeting:"\u0442\u0435\u043a\u0443\u0449\u0430\u044f \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044f",permissionButton:"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",permissionMessage:"\u0414\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u0432\u0430\u0448\u0438\u0445 \u0432\u0441\u0442\u0440\u0435\u0447 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0443\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044e.",refresh:"\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c",today:"\u0421\u0435\u0433\u043e\u0434\u043d\u044f"},chat:{error:"",messagebox:"",nickname:{popover:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u043c\u044f",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"\u041f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u0435\u043d\u043e",AUTHENTICATING:"\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f",AUTHFAIL:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438",CONNECTED:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e",CONNECTING:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435",CONNFAIL:"\u0421\u0431\u043e\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f",DISCONNECTED:"\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e",DISCONNECTING:"\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435",ERROR:"\u041e\u0448\u0438\u0431\u043a\u0430",RECONNECTING:"\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441 \u0441\u0435\u0442\u044c\u044e. \u041f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435..."},connectionindicator:{address:"\u0410\u0434\u0440\u0435\u0441:",bandwidth:"\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c:",bitrate:"\u0411\u0438\u0442\u0440\u0435\u0439\u0442:",bridgeCount:"",connectedTo:"",framerate:"\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432:",less:"\u041c\u0435\u043d\u044c\u0448\u0435",localaddress_0:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",localaddress_1:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",localaddress_2:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",localport_0:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",localport_1:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",localport_2:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",more:"\u0411\u043e\u043b\u044c\u0448\u0435",packetloss:"\u041f\u043e\u0442\u0435\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432:",quality:{good:"\u0425\u043e\u0440\u043e\u0448\u043e",inactive:"\u043d\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u043e",lost:"\u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043e",nonoptimal:"\u043d\u0435 \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u043e",poor:"\u043f\u043b\u043e\u0445\u043e"},remoteaddress_0:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",remoteaddress_1:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",remoteaddress_2:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430:",remoteport_0:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",remoteport_1:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",remoteport_2:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b:",resolution:"\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435:",status:"\u0421\u0432\u044f\u0437\u044c:",transport_0:"\u041c\u0435\u0442\u043e\u0434\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438:",transport_1:"\u041c\u0435\u0442\u043e\u0434\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438:",transport_2:"\u041c\u0435\u0442\u043e\u0434\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438:"},dateUtils:{earlier:"\u0420\u0430\u043d\u0435\u0435",today:"\u0421\u0435\u0433\u043e\u0434\u043d\u044f",yesterday:"\u0412\u0447\u0435\u0440\u0430"},deepLinking:{appNotInstalled:"\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u044d\u0442\u043e\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0435 \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435, \u043d\u0443\u0436\u043d\u043e \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 {{app}}.",description:"\u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c? \u041c\u044b \u043f\u043e\u043f\u044b\u0442\u0430\u043b\u0438\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u0430\u0448\u0443 \u0432\u0441\u0442\u0440\u0435\u0447\u0443 \u0432 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 {{app}}. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0438\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0435\u0435 \u0432 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 {{app}}.",descriptionWithoutWeb:"",downloadApp:"\u0421\u043a\u0430\u0447\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435",launchWebButton:"\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435",openApp:"\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e",title:"\u0417\u0430\u043f\u0443\u0441\u043a \u0432\u0430\u0448\u0435\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0438 \u0432 {{app}}...",tryAgainButton:"\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u0432 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438"},defaultLink:"\u043d\u0430\u043f\u0440. {{url}}",defaultNickname:"\u043d\u0430\u043f\u0440. \u042f\u043d\u0430 \u0426\u0432\u0435\u0442\u043a\u043e\u0432\u0430",deviceError:{cameraError:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0430\u043c\u0435\u0440\u0435",cameraPermission:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443",microphoneError:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443",microphonePermission:"\u041d\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443"},deviceSelection:{noPermission:"\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430",previewUnavailable:"\u041f\u0440\u0435\u0434\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d",selectADevice:"\u0412\u044b\u0431\u043e\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",testAudio:"\u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0432\u0443\u043a"},dialog:{accessibilityLabel:{liveStreaming:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f"},allow:"\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\u0414\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043a\u0430\u0437 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e",applicationWindow:"\u041e\u043a\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",Back:"\u041d\u0430\u0437\u0430\u0434",cameraConstraintFailedError:"\u041a\u0430\u043c\u0435\u0440\u0430 \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c.",cameraNotFoundError:"\u041a\u0430\u043c\u0435\u0440\u0430 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430.",cameraNotSendingData:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0430\u043c\u0435\u0440\u0435. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435, \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043b\u0438 \u043a\u0430\u043c\u0435\u0440\u0443 \u043a\u0430\u043a\u0430\u044f-\u043d\u0438\u0431\u0443\u0434\u044c \u0434\u0440\u0443\u0433\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0442\u0430\u043a\u0436\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0438\u0437 \u043c\u0435\u043d\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0438\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.",cameraNotSendingDataTitle:"\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0430\u043c\u0435\u0440\u0435",cameraPermissionDeniedError:"\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0430\u043c\u0435\u0440\u0435. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0435, \u043d\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0432\u0430\u0441 \u0432\u0438\u0434\u0435\u0442\u044c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0437\u043d\u0430\u0447\u043e\u043a \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.",cameraUnknownError:"\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u043c\u0435\u0440\u044b.",cameraUnsupportedResolutionError:"\u0412\u0430\u0448\u0430 \u043a\u0430\u043c\u0435\u0440\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432\u0438\u0434\u0435\u043e.",Cancel:"\u041e\u0442\u043c\u0435\u043d\u0430",close:"\u0417\u0430\u043a\u0440\u044b\u0442\u044c",conferenceDisconnectMsg:"\u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u041f\u043e\u043f\u044b\u0442\u043a\u0430 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0432\u044f\u0437\u0438 \u0447\u0435\u0440\u0435\u0437 {{seconds}} \u0441.",conferenceDisconnectTitle:"\u0412\u044b \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u044b.",conferenceReloadMsg:"\u041c\u044b \u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c. \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438 \u0447\u0435\u0440\u0435\u0437 {{seconds}} \u0441.",conferenceReloadTitle:"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a.",confirm:"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c",confirmNo:"\u041d\u0435\u0442",confirmYes:"\u0414\u0430",connectError:"\u041e\u0448\u0438\u0431\u043a\u0430. \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0432\u044f\u0437\u044c \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0438.",connectErrorWithMsg:"\u041e\u0448\u0438\u0431\u043a\u0430. \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0432\u044f\u0437\u044c \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0438: {{msg}}",connecting:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435",contactSupport:"\u0421\u0432\u044f\u0437\u044c \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439",copy:"\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c",dismiss:"\u041e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c",displayNameRequired:"",done:"\u0413\u043e\u0442\u043e\u0432\u043e",enterDisplayName:"",error:"\u041e\u0448\u0438\u0431\u043a\u0430",externalInstallationMsg:"\u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u0448\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0441\u0442\u043e\u043b\u0430.",externalInstallationTitle:"\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435",goToStore:"\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0443",gracefulShutdown:"\u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u044b. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",IamHost:"\u042f \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440",incorrectRoomLockPassword:"",incorrectPassword:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044f",inlineInstallationMsg:"\u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u0448\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0441\u0442\u043e\u043b\u0430.",inlineInstallExtension:"\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c",internalError:"\u0427\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a. \u041e\u0448\u0438\u0431\u043a\u0430: {{error}}",internalErrorTitle:"\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0430",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f",liveStreamingDisabledForGuestTooltip:"\u0413\u043e\u0441\u0442\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0447\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e.",liveStreamingDisabledTooltip:"\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430",lockMessage:"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0435\u0440\u0435\u0442\u044c \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044e",lockRoom:"",lockTitle:"\u0411\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c",logoutQuestion:"\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u0439\u0442\u0438 \u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0442\u0440\u0435\u0447\u0443?",logoutTitle:"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0441\u0435\u0430\u043d\u0441",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\u0412\u0430\u0448 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c.",micNotFoundError:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0435, \u043d\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0432\u0430\u0441 \u0441\u043b\u044b\u0448\u0430\u0442\u044c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0437\u043d\u0430\u0447\u043e\u043a \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.",micUnknownError:"\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430.",muteParticipantBody:"\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u043c \u0437\u0432\u0443\u043a, \u043d\u043e \u043e\u043d\u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u0430\u043c\u0438 \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f.",muteParticipantButton:"\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0432\u0443\u043a",muteParticipantDialog:"",muteParticipantTitle:"\u041f\u0440\u0438\u0433\u043b\u0443\u0448\u0438\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430?",Ok:"Ok",passwordLabel:"",passwordNotSupported:"\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\u0412\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u043e\u043a\u043d\u0430 \u044d\u0442\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u043e\u043a\u043d\u0430 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.",popupErrorTitle:"\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043e\u043a\u043d\u043e",recording:"\u0417\u0430\u043f\u0438\u0441\u044c",recordingDisabledForGuestTooltip:"\u0413\u043e\u0441\u0442\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c.",recordingDisabledTooltip:"\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c.",rejoinNow:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0441\u043d\u043e\u0432\u0430",remoteControlAllowedMessage:"{{user}} \u043f\u0440\u0438\u043d\u044f\u043b \u0432\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435!",remoteControlDeniedMessage:"{{user}} \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u043b \u0432\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435!",remoteControlErrorMessage:"\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0442 {{user}}.",remoteControlRequestMessage:"\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c {{user}} \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0430\u0448\u0438\u043c \u0440\u0430\u0431\u043e\u0447\u0438\u043c \u0441\u0442\u043e\u043b\u043e\u043c?",remoteControlShareScreenWarning:"\u0415\u0441\u043b\u0438 \u043d\u0430\u0436\u043c\u0435\u0442\u0435 \"\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c\", \u0442\u043e \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u044d\u043a\u0440\u0430\u043d\u043e\u043c!",remoteControlStopMessage:"\u0421\u0435\u0441\u0441\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430!",remoteControlTitle:"\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0447\u0438\u043c \u0441\u0442\u043e\u043b\u043e\u043c",Remove:"\u0423\u0434\u0430\u043b\u0438\u0442\u044c",removePassword:"",removeSharedVideoMsg:"\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0431\u0440\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c?",removeSharedVideoTitle:"\u0423\u0431\u0440\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e",reservationError:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f",reservationErrorMsg:"\u041a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438: {{code}}, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435: {{msg}}",retry:"\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c",screenSharingFailedToInstall:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u044d\u043a\u0440\u0430\u043d\u0430.",screenSharingFailedToInstallTitle:"\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u044d\u043a\u0440\u0430\u043d\u0430 \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e",screenSharingFirefoxPermissionDeniedError:"\u0427\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u044b\u0442\u0430\u043b\u0438\u0441\u044c \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0432\u0430\u0448\u0438\u043c \u044d\u043a\u0440\u0430\u043d\u043e\u043c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u0434\u0430\u043b\u0438 \u043d\u0430\u043c \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 \u044d\u0442\u043e.",screenSharingFirefoxPermissionDeniedTitle:"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u043a\u0430\u0437\u0430 \u044d\u043a\u0440\u0430\u043d\u0430!",screenSharingPermissionDeniedError:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u044d\u043a\u0440\u0430\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.",serviceUnavailable:"\u0421\u043b\u0443\u0436\u0431\u0430 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430",sessTerminated:"\u0421\u0432\u044f\u0437\u044c \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u0430",Share:"\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f",shareVideoLinkError:"\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443 Youtube.",shareVideoTitle:"\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0432\u0438\u0434\u0435\u043e",shareYourScreen:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u044d\u043a\u0440\u0430\u043d",shareYourScreenDisabled:"\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430.",shareYourScreenDisabledForGuest:"\u0413\u043e\u0441\u0442\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u043a\u0440\u0430\u043d.",startLiveStreaming:"\u041d\u0430\u0447\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e",startRecording:"\u041d\u0430\u0447\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c",startRemoteControlErrorMessage:"\u041e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0435\u0441\u0441\u0438\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f!",stopLiveStreaming:"\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e",stopRecording:"\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c",stopRecordingWarning:"\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c?",stopStreamingWarning:"\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e?",streamKey:"\u041a\u043b\u044e\u0447 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438",Submit:"\u041e\u041a",thankYou:"\u0421\u043f\u0430\u0441\u0438\u0431\u043e, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 {{appName}}!",token:"\u0442\u043e\u043a\u0435\u043d",tokenAuthFailed:"\u0418\u0437\u0432\u0438\u043d\u0438\u0442\u0435, \u0432\u0430\u043c \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u0441\u0435\u0430\u043d\u0441\u0443 \u0441\u0432\u044f\u0437\u0438.",tokenAuthFailedTitle:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438",transcribing:"\u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430",unlockRoom:"",userPassword:"\u043f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"\u0416\u0434\u0435\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430...",Yes:"\u0414\u0430",yourEntireScreen:"\u0412\u0435\u0441\u044c \u044d\u043a\u0440\u0430\u043d"},dialOut:{statusMessage:"\u0441\u0435\u0439\u0447\u0430\u0441 {{status}}"},feedback:{average:"\u0421\u0440\u0435\u0434\u043d\u0435",bad:"\u041f\u043b\u043e\u0445\u043e",detailsLabel:"\u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.",good:"\u0425\u043e\u0440\u043e\u0448\u043e",rateExperience:"\u041e\u0446\u0435\u043d\u043a\u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0441\u0432\u044f\u0437\u0438",veryBad:"\u041e\u0447\u0435\u043d\u044c \u043f\u043b\u043e\u0445\u043e",veryGood:"\u041e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u043e"},incomingCall:{answer:"\u041e\u0442\u0432\u0435\u0442",audioCallTitle:"\u0412\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0437\u0432\u043e\u043d\u043e\u043a",decline:"\u041e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c",productLabel:"\u0438\u0437 Jitsi Meet",videoCallTitle:"\u0412\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0432\u0438\u0434\u0435\u043e\u0437\u0432\u043e\u043d\u043e\u043a"},info:{accessibilityLabel:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e",addPassword:"",cancelPassword:"",conferenceURL:"\u0421\u0441\u044b\u043b\u043a\u0430:",country:"\u0421\u0442\u0440\u0430\u043d\u0430",dialANumber:"",dialInConferenceID:"PIN:",dialInNotSupported:"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0430\u0431\u043e\u0440 \u043d\u043e\u043c\u0435\u0440\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.",dialInNumber:"\u041d\u043e\u043c\u0435\u0440:",dialInSummaryError:"",dialInTollFree:"",genericError:"\u0427\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a.",inviteLiveStream:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f \u044d\u0442\u043e\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0438: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f:",moreNumbers:"\u0411\u043e\u043b\u044c\u0448\u0435 \u043d\u043e\u043c\u0435\u0440\u043e\u0432",noNumbers:"\u041d\u0435\u0442 \u043d\u043e\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u0431\u043e\u0440\u0430.",noPassword:"\u043d\u0435\u0442",noRoom:"\u0414\u043b\u044f \u043d\u0430\u0431\u043e\u0440\u0430 \u043d\u043e\u043c\u0435\u0440\u0430 \u043d\u0435 \u0431\u044b\u043b\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u044b.",numbers:"\u041d\u043e\u043c\u0435\u0440\u0430 \u0434\u043b\u044f \u043d\u0430\u0431\u043e\u0440\u0430",password:"",title:"\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f",tooltip:"\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u0438 \u043d\u043e\u043c\u0435\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u044d\u0442\u043e\u0439 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438",label:""},inviteDialog:{alertText:"",header:"\u041f\u0440\u0438\u0433\u043b\u0430\u0441\u0438\u0442\u044c",searchCallOnlyPlaceholder:"\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"\u041d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0437\u0430\u043c\u0438\u043d\u043a\u0430.",retry:"\u041f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u043d\u043e\u0432\u0430",support:"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430",supportMsg:"\u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0441\u044f, \u0441\u0432\u044f\u0436\u0438\u0442\u0435\u0441\u044c \u0441"},keyboardShortcuts:{focusLocal:"\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0432\u0430\u0448\u0435 \u0432\u0438\u0434\u0435\u043e",focusRemote:"\u0424\u043e\u043a\u0443\u0441 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430",fullScreen:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u043f\u043e\u043b\u043d\u043e\u044d\u043a\u0440\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c ",keyboardShortcuts:"\u041a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 \u043a\u043b\u0430\u0432\u0438\u0448",localRecording:"",mute:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d (\u0432\u043a\u043b./\u0432\u044b\u043a\u043b.)",pushToTalk:"\u041d\u0430\u0436\u043c\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c",raiseHand:"\u041f\u043e\u0434\u043d\u044f\u0442\u044c \u0438\u043b\u0438 \u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0440\u0443\u043a\u0443",showSpeakerStats:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e\u0449\u0435\u0433\u043e",toggleChat:"\u0427\u0430\u0442 (\u043e\u0442\u043a\u0440\u044b\u0442\u044c/\u0437\u0430\u043a\u0440\u044b\u0442\u044c)",toggleFilmstrip:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c/\u0421\u043a\u0440\u044b\u0442\u044c \u043a\u0440\u0430\u0442\u043a\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435",toggleScreensharing:"\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043c\u0435\u0440\u043e\u0439 \u0438 \u043f\u043e\u043a\u0430\u0437\u043e\u043c \u044d\u043a\u0440\u0430\u043d\u0430",toggleShortcuts:"\u0421\u043a\u0440\u044b\u0442\u044c/\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438",videoMute:"\u041a\u0430\u043c\u0435\u0440\u0430 (\u0432\u043a\u043b./\u0432\u044b\u043a\u043b.)",videoQuality:"\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0432\u044f\u0437\u0438"},liveStreaming:{busy:"\u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.",busyTitle:"\u0412\u0441\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0443\u0436\u0435 \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u044b",changeSignIn:"\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u044b.",choose:"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e",chooseCTA:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e. \u0412\u044b \u0432\u043e\u0448\u043b\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043a\u0430\u043a {{email}}. ",enterStreamKey:"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448 \u043a\u043b\u044e\u0447 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 YouTube.",error:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.",errorAPI:"\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435 \u043a \u0432\u0430\u0448\u0438\u043c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f\u043c \u043d\u0430 YouTube. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.",errorLiveStreamNotEnabled:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043d\u0430 {{email}}. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e \u0438\u043b\u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0439 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0435\u0439.",expandedOff:"",expandedOn:"",expandedPending:"",failedToStart:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0432\u0438\u0434\u0435\u043e",getStreamKeyManually:"",invalidStreamKey:"",off:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430",on:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f",pending:"\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e...",serviceName:"\u0421\u043b\u0443\u0436\u0431\u0430 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438",signedInAs:"",signIn:"\u0412\u043e\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 Google",signInCTA:"\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0438\u043b\u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043a\u043b\u044e\u0447 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 YouTube.",signOut:"\u0412\u044b\u0445\u043e\u0434",start:"\u041d\u0430\u0447\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e",streamIdHelp:"\u0427\u0442\u043e \u044d\u0442\u043e?",unavailableTitle:"\u0422\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430"},localRecording:{clientState:{off:"\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d",on:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d",unknown:"\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d"},dialogTitle:"\u041a\u043d\u043e\u043f\u043a\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438",duration:"\u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c",durationNA:"\u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e",encoding:"\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430",label:"\u041b\u0435\u0432\u044b\u0439/\u041f\u0440\u0430\u0432\u044b\u0439",labelToolTip:"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0430\u043a\u0442\u0438\u0432\u043d\u0430",localRecording:"",me:"\u042f",messages:{engaged:"",finished:"\u0417\u0430\u043f\u0438\u0441\u044c \u0441\u0435\u0441\u0441\u0438\u0438 {{token}} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440\u0443.",finishedModerator:"",notModerator:""},moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",no:"\u041d\u0435\u0442",participant:"\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a",participantStats:"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432",sessionToken:"\u0422\u043e\u043a\u0435\u043d \u0441\u0435\u0441\u0441\u0438\u0438",start:"\u041d\u0430\u0447\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c",stop:"\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c",yes:"\u0414\u0430"},lockRoomPassword:"\u043f\u0430\u0440\u043e\u043b\u044c",lockRoomPasswordUppercase:"\u041f\u0430\u0440\u043e\u043b\u044c",me:"\u044f",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043e\u0440\u0432\u0430\u043d\u043e",focus:"\u0424\u043e\u043a\u0443\u0441 \u0432\u0441\u0442\u0440\u0435\u0447\u0438",focusFail:"{{component}} \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 {{ms}} \u0441",grantedTo:"{{to}} \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0430\u0432\u0430 \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440\u0430!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"\u042f",moderator:"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043f\u0440\u0430\u0432\u0430 \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440\u0430!",muted:"\u0412\u044b \u043d\u0430\u0447\u0430\u043b\u0438 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440 \u0431\u0435\u0437 \u0437\u0432\u0443\u043a\u0430.",mutedTitle:"\u0412\u044b \u0431\u0435\u0437 \u0437\u0432\u0443\u043a\u0430!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"\u041a\u0442\u043e-\u0442\u043e",startSilentTitle:"",startSilentDescription:"",suboptimalBrowserWarning:"",suboptimalExperienceTitle:"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u0434\u0440\u0443\u0433\u0438\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u043c",passwordDigitsOnly:"",poweredby:"\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430",presenceStatus:{busy:"\u0417\u0430\u043d\u044f\u0442",calling:"\u0412\u044b\u0437\u044b\u0432\u0430\u044e...",connected:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e",connecting:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435...",connecting2:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435*...",disconnected:"\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e",expired:"\u0418\u0441\u0442\u0435\u043a",ignored:"\u041f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d",initializingCall:"\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0432\u043e\u043d\u043a\u0430...",invited:"\u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u043d\u044b\u0439",rejected:"\u041e\u0442\u043a\u043b\u043e\u043d\u0435\u043d",ringing:"\u0417\u0432\u043e\u043d\u044e. . ."},profile:{setDisplayNameLabel:"\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u0438\u043c\u044f",setEmailInput:"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 e-mail",setEmailLabel:"E-mail \u0434\u043b\u044f gravatar",title:"\u041f\u0440\u043e\u0444\u0438\u043b\u044c"},raisedHand:"\u0425\u043e\u0447\u0435\u0442 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c",recording:{authDropboxText:"",availableSpace:"\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u043c\u0435\u0441\u0442\u0430: {{spaceLeft}} MB (\u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e {{duration}} \u043c\u0438\u043d\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u0438)",beta:"\u0411\u0415\u0422\u0410",busy:"\u041c\u044b \u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.",busyTitle:"\u0412\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0437\u0430\u043d\u044f\u0442\u044b",error:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",expandedOff:"\u0417\u0430\u043f\u0438\u0441\u044c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430",expandedOn:"",expandedPending:"",failedToStart:"\u041e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u0438",fileSharingdescription:"",live:"\u041f\u0440\u044f\u043c\u0430\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f",loggedIn:"\u0412\u043e\u0448\u0435\u043b \u043a\u0430\u043a {{userName}}",off:"\u0417\u0430\u043f\u0438\u0441\u044c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430",on:"\u0417\u0430\u043f\u0438\u0441\u044c",pending:"\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043a \u0437\u0430\u043f\u0438\u0441\u0438 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438...",rec:"\u0418\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044c",serviceDescription:"",serviceName:"\u0421\u043b\u0443\u0436\u0431\u0430 \u0437\u0430\u043f\u0438\u0441\u0438",signIn:"\u0412\u0445\u043e\u0434",signOut:"\u0412\u044b\u0445\u043e\u0434",unavailable:"\u0421\u043b\u0443\u0436\u0431\u0430 {{serviceName}} \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430. \u041c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0434 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",unavailableTitle:"\u0417\u0430\u043f\u0438\u0441\u044c \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430"},sectionList:{pullToRefresh:"\u041f\u043e\u0442\u044f\u043d\u0438\u0442\u0435 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f"},settings:{calendar:{about:"\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u0435\u043c {{appName}} \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044e \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u0439.",disconnect:"\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f",microsoftSignIn:"\u0412\u043e\u0439\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Microsoft",signedIn:"\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0434\u043b\u044f email - \u0430\u0434\u0440\u0435\u0441\u0430 {{email}}. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044f.",title:"\u041a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c"},devices:"\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",followMe:"\u0412\u0441\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0442 \u0437\u0430 \u043c\u043d\u043e\u0439",language:"\u042f\u0437\u044b\u043a",loggedIn:"\u0412\u043e\u0448\u0435\u043b \u043a\u0430\u043a {{name}}",moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",more:"\u0411\u043e\u043b\u044c\u0448\u0435 \u043e\u043f\u0446\u0438\u0439",name:"\u0418\u043c\u044f",noDevice:"\u043d\u0435\u0442",selectAudioOutput:"\u0417\u0432\u0443\u043a\u043e\u0432\u043e\u0439 \u0432\u044b\u0445\u043e\u0434",selectCamera:"\u041a\u0430\u043c\u0435\u0440\u0430",selectMic:"\u041c\u0438\u043a\u0440\u043e\u0444\u043e\u043d",startAudioMuted:"\u0412\u0441\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0441 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u0437\u0432\u0443\u043a\u043e\u043c",startVideoMuted:"\u0412\u0441\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0432 \u0441\u043a\u0440\u044b\u0442\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435",title:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438"},settingsView:{alertOk:"OK",alertTitle:"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435",alertURLText:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0434\u0440\u0435\u0441\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430",buildInfoSection:"",conferenceSection:"\u041d\u043e\u043c\u0435\u0440\u0430 \u0434\u043b\u044f \u043d\u0430\u0431\u043e\u0440\u0430",displayName:"\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u0438\u043c\u044f",email:"Email",header:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",profileSection:"\u041f\u0440\u043e\u0444\u0438\u043b\u044c",serverURL:"\u0410\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430",startWithAudioMuted:"\u041d\u0430\u0447\u0430\u0442\u044c \u0441 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u0437\u0432\u0443\u043a\u043e\u043c",startWithVideoMuted:"\u041d\u0430\u0447\u0430\u0442\u044c \u0441 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u0432\u0438\u0434\u0435\u043e",version:""},share:{dialInfoText:"",mainText:"\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u0441\u0441\u044b\u043b\u043a\u0443 \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438:\n{{roomUrl}}"},speaker:"\u041a\u043e\u043b\u043e\u043d\u043a\u0430",speakerStats:{hours:"{{count}}\u0447",minutes:"{{count}}\u043c",name:"\u0418\u043c\u044f",seconds:"{{count}}\u0441",speakerStats:"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0432\u044b\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0439",speakerTime:"\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0439"},startupoverlay:{policyText:" ",title:"{{app}} \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443 \u0438 \u043a\u0430\u043c\u0435\u0440\u0435."},suspendedoverlay:{rejoinKeyTitle:"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0441\u043d\u043e\u0432\u0430",text:"\u0414\u043b\u044f \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0432\u044f\u0437\u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0441\u043d\u043e\u0432\u0430.",title:"\u0412\u0438\u0434\u0435\u043e\u0441\u0432\u044f\u0437\u044c \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u0430. \u041f\u0440\u0438\u0447\u0438\u043d\u0430: \u044d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440 \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u0440\u0435\u0436\u0438\u043c \u0441\u043d\u0430."},toolbar:{accessibilityLabel:{audioOnly:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0432\u0443\u043a",audioRoute:"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",callQuality:"\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0432\u044f\u0437\u0438",cc:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b",chat:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c/\u0441\u043a\u0440\u044b\u0442\u044c \u043e\u043a\u043d\u043e \u0447\u0430\u0442\u0430",document:"\u0417\u0430\u043a\u0440\u044b\u0442\u044c \u043e\u0431\u0449\u0438\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",feedback:"\u041e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0437\u044b\u0432",fullScreen:"\u041f\u043e\u043b\u043d\u043e\u044d\u043a\u0440\u0430\u043d\u043d\u044b\u0439/\u043e\u043a\u043e\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c",hangup:"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0437\u0432\u043e\u043d\u043e\u043a",invite:"\u041f\u0440\u0438\u0433\u043b\u0430\u0441\u0438\u0442\u044c",kick:"",localRecording:"",lockRoom:"",moreActions:"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c/\u0441\u043a\u0440\u044b\u0442\u044c \u043c\u0435\u043d\u044e \u0434\u043e\u043f. \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a",moreActionsMenu:"\u041c\u0435\u043d\u044e \u0434\u043e\u043f. \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a",mute:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u0437\u0432\u0443\u043a",pip:"",profile:"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0444\u0438\u043b\u044c",raiseHand:"",recording:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u0437\u0430\u043f\u0438\u0441\u044c",remoteMute:"",Settings:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u043c\u0435\u043d\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a",sharedvideo:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b Youtube - \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e",shareRoom:"\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0435",shareYourScreen:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u044d\u043a\u0440\u0430\u043d\u0430",shortcuts:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u0437\u043d\u0430\u0447\u043a\u0438",show:"",speakerStats:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443",tileView:"",toggleCamera:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u043a\u0430\u043c\u0435\u0440\u0443",videomute:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u0437\u0432\u0443\u043a \u0432 \u0432\u0438\u0434\u0435\u043e",videoblur:""},addPeople:"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043b\u044e\u0434\u0435\u0439 \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u0441\u0435\u0430\u043d\u0441\u0443 \u0441\u0432\u044f\u0437\u0438",audioOnlyOff:"",audioOnlyOn:"",audioRoute:"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",authenticate:"\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c",callQuality:"\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0432\u044f\u0437\u0438",chat:"\u0427\u0430\u0442",closeChat:"",documentClose:"\u0417\u0430\u043a\u0440\u044b\u0442\u044c \u043e\u0431\u0449\u0438\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",documentOpen:"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u043e\u0431\u0449\u0438\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442",enterFullScreen:"\u041f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d",enterTileView:"",exitFullScreen:"\u041f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d",exitTileView:"",feedback:"\u041e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0437\u044b\u0432",hangup:"\u0412\u044b\u0445\u043e\u0434",invite:"\u041f\u0440\u0438\u0433\u043b\u0430\u0441\u0438\u0442\u044c",login:"\u0412\u043e\u0439\u0442\u0438",logout:"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0441\u0435\u0430\u043d\u0441",lowerYourHand:"",moreActions:"\u0411\u043e\u043b\u044c\u0448\u0435",mute:"\u0417\u0432\u0443\u043a (\u0432\u043a\u043b./\u0432\u044b\u043a\u043b.)",openChat:"",pip:"",profile:"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0444\u0438\u043b\u044c",raiseHand:"\u0425\u043e\u0447\u0443 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c",raiseYourHand:"",Settings:"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",sharedvideo:"\u0412\u0438\u0434\u0435\u043e YouTube",shareRoom:"\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0435",shortcuts:"\u041a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 \u043a\u043b\u0430\u0432\u0438\u0448",speakerStats:"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u043d\u0430 YouTube",talkWhileMutedPopup:"\u041f\u044b\u0442\u0430\u0435\u0442\u0435\u0441\u044c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c? \u0423 \u0432\u0430\u0441 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0437\u0432\u0443\u043a.",tileViewToggle:"",toggleCamera:"\u0412\u043a\u043b/\u0432\u044b\u043a\u043b \u043a\u0430\u043c\u0435\u0440\u0443",videomute:"\u041a\u0430\u043c\u0435\u0440\u0430",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"",error:"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",expandedLabel:"",failedToStart:"",labelToolTip:"",off:"",pending:"\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043a \u0437\u0430\u043f\u0438\u0441\u0438 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438...",start:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u043f\u043e\u043a\u0430\u0437 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u043e\u0432",stop:"\u0412\u043a\u043b/\u0412\u044b\u043a\u043b \u043f\u043e\u043a\u0430\u0437 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u043e\u0432",tr:""},userMedia:{androidGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",chromeGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",edgeGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0414\u0430, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",electronGrantPermissions:"\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0434\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u0430\u043c\u0435\u0440\u0435 \u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443",firefoxGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",iexplorerGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 OK, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",nwjsGrantPermissions:"\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0434\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u0430\u043c\u0435\u0440\u0435 \u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443",operaGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.","react-nativeGrantPermissions":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445.",safariGrantPermissions:"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 OK, \u043a\u043e\u0433\u0434\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u0445."},videoSIPGW:{busy:"\u041c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0434 \u0432\u044b\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.",busyTitle:"\u0421\u043b\u0443\u0436\u0431\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u043d\u044f\u0442\u0430",errorAlreadyInvited:"{{displayName}} \u0443\u0436\u0435 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d",errorInvite:"\u0412\u0441\u0442\u0440\u0435\u0447\u0430 \u0435\u0449\u0435 \u043d\u0435 \u043d\u0430\u0447\u0430\u043b\u0430\u0441\u044c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",errorInviteFailed:"\u041c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0434 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.",errorInviteFailedTitle:"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f {{displayName}}",errorInviteTitle:"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443",pending:"{{displayName}} \u0431\u044b\u043b \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d"},videoStatus:{audioOnly:"\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0432\u0443\u043a",audioOnlyExpanded:"",callQuality:"",hd:"HD",hdTooltip:"\u0412\u0438\u0434\u0435\u043e \u0432\u044b\u0441\u043e\u043a\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430",highDefinition:"\u0412\u044b\u0441\u043e\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e",labelTooiltipNoVideo:"\u041d\u0435\u0442 \u0432\u0438\u0434\u0435\u043e",labelTooltipAudioOnly:"",ld:"LD",ldTooltip:"\u0412\u0438\u0434\u0435\u043e \u043d\u0438\u0437\u043a\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430",lowDefinition:"\u041d\u0438\u0437\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e",onlyAudioAvailable:"\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0432\u0443\u043a",onlyAudioSupported:"\u0412 \u044d\u0442\u043e\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0432\u0443\u043a.",p2pEnabled:"\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \"\u0442\u043e\u0447\u043a\u0430-\u043a-\u0442\u043e\u0447\u043a\u0435\"",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0441\u043e\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e.",sd:"SD",sdTooltip:"\u0412\u0438\u0434\u0435\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430",standardDefinition:"\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e (SD)"},videothumbnail:{domute:"\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0432\u0443\u043a",flip:"\u041e\u0442\u0440\u0430\u0437\u0438\u0442\u044c",kick:"\u0412\u044b\u043a\u0438\u043d\u0443\u0442\u044c",moderator:"\u041c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440",mute:"\u0411\u0435\u0437 \u0437\u0432\u0443\u043a\u0430",muted:"\u0417\u0432\u0443\u043a \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d",remoteControl:"\u0414\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f",roomname:"\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b"},appDescription:"\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0432\u0438\u0434\u0435\u043e\u0447\u0430\u0442 \u0441\u043e \u0432\u0441\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439. \u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0439\u0442\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0445! {{app}} \u2014 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0438\u0434\u0435\u043e\u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0439 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c. \u041f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435\u0441\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u0438 \u0431\u0435\u0437 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438.",audioVideoSwitch:{audio:"\u041a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c",video:"\u0412\u0438\u0434\u0435\u043e"},calendar:"\u041a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c",connectCalendarButton:"\u041f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c",connectCalendarText:"",enterRoomTitle:"",go:"\u041e\u041a",join:"\u041f\u0420\u0418\u0421\u041e\u0415\u0414\u0418\u041d\u0418\u0422\u042c\u0421\u042f",info:"",privacy:"\u041f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u044c",recentList:"",recentListDelete:"",recentListEmpty:"",reducedUIText:"",roomname:"\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b",roomnameHint:"\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b \u0438\u043b\u0438 \u0435\u0435 \u0430\u0434\u0440\u0435\u0441. \u041c\u043e\u0436\u0435\u0442\u0435 \u0441\u0430\u043c\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u0443\u0434\u0443\u0449\u0438\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0432\u0441\u0442\u0440\u0435\u0447\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0433\u043e.",sendFeedback:"\u041e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0432\u044f\u0437\u044c",terms:"\u0423\u0441\u043b\u043e\u0432\u0438\u044f",title:""}}},676,[]); -__d(function(a,s,k,i,n,e,o){n.exports={en:"Engelska",af:"",az:"",bg:"Bulgariska",cs:"",de:"Tyska",el:"",eo:"Esperanto",es:"Spanska",fr:"Franska",hy:"Armeniska",it:"Italienska",ja:"",ko:"",nb:"Norska (Bokm\xe5l)",oc:"Occitanska",pl:"Polska",ptBR:"Portugisiska (Brasilien)",ru:"Ryska",sk:"Slovakiska",sl:"Slovenska",sv:"Svenska",tr:"Turkiska",vi:"",zhCN:"Kinesiska (Kina)"}},677,[]); -__d(function(e,n,t,a,r,i,l){r.exports={addPeople:{add:"Bjud in",countryNotSupported:"Vi st\xf6der inte den h\xe4r platsen \xe4nnu.",countryReminder:"Ringer du till utlandsnummer? B\xf6rja alltid med landskoden.",disabled:"Du kan inte bjuda in andra.",failedToAdd:"",footerText:"Utringningsfunktionen \xe4r avst\xe4ngd.",loading:"S\xf6ker efter personer och telefonnummer",loadingNumber:"Bekr\xe4ftar telefonnummer",loadingPeople:"S\xf6ker efter personer att bjuda in",noResults:"Inga s\xf6ktr\xe4ffar",noValidNumbers:"Ange ett telefonnummer",searchNumbers:"L\xe4gg till telefonnummer",searchPeople:"S\xf6k efter personer",searchPeopleAndNumbers:"S\xf6k efter personer eller l\xe4gg till deras telefonnummer",telephone:"Telefon: {{number}}",title:"Bjud in andra till m\xf6tet"},audioDevices:{bluetooth:"Bluetooth",headphones:"H\xf6rlurar",phone:"Telefon",speaker:"Talare"},audioOnly:{audioOnly:"Enbart ljud"},calendarSync:{addMeetingURL:"L\xe4gg till en m\xf6tesl\xe4nk",confirmAddLink:"Vill du l\xe4gga till en Jitsi-l\xe4nk i h\xe4ndelsen?",error:{appConfiguration:"Kalenderintegrationen \xe4r inte r\xe4tt inst\xe4lld.",generic:"Ett fel har intr\xe4ffat. Kontrollera dina kalenderinst\xe4llningar eller uppdatera kalendern.",notSignedIn:"Ett fel intr\xe4ffade n\xe4r kalenderh\xe4ndelserna autentiserades f\xf6r visning. Kontrollera kalenderinst\xe4llningarna och f\xf6rs\xf6k att logga in igen."},join:"G\xe5 med",joinTooltip:"G\xe5 med i m\xf6tet",nextMeeting:"n\xe4sta m\xf6te",noEvents:"Det finns inga inbokade kommande aktiviteter.",ongoingMeeting:"p\xe5g\xe5ende m\xf6te",permissionButton:"\xd6ppna inst\xe4llningar",permissionMessage:"Till\xe5telse fr\xe5n kalendern kr\xe4vs f\xf6r att se dina m\xf6ten i appen.",refresh:"Uppdatera kalender",today:"Idag"},chat:{error:"",messagebox:"Skriv ett meddelande",nickname:{popover:"V\xe4lj ett namn",title:"Skriv in ett namn f\xf6r att b\xf6rja anv\xe4nda chatten"},title:"Chatt"},connectingOverlay:{joiningRoom:"Ansluter till m\xf6tet ..."},connection:{ATTACHED:"Ansluten",AUTHENTICATING:"Kontrollerar beh\xf6righet",AUTHFAIL:"Autentisering misslyckades",CONNECTED:"Ansluten",CONNECTING:"Ansluter",CONNFAIL:"Anslutningen misslyckades",DISCONNECTED:"Fr\xe5nkopplad",DISCONNECTING:"Kopplar fr\xe5n",ERROR:"Fel",RECONNECTING:"Ett n\xe4tverksproblem uppstod. \xc5teransluter..."},connectionindicator:{address:"Adress:",bandwidth:"Ber\xe4knad bandbredd:",bitrate:"Bithastighet:",bridgeCount:"Serverantal: ",connectedTo:"Ansluten till:",framerate:"Bildfrekvens:",less:"Visa mindre",localaddress:"Lokal adress:",localaddress_plural:"Lokala adresser:",localport:"Lokal port:",localport_plural:"Lokala portar:",more:"Visa mer",packetloss:"Paketf\xf6rluster:",quality:{good:"Bra",inactive:"Inaktiv",lost:"F\xf6rlorad",nonoptimal:"Ej optimal",poor:"D\xe5ligt"},remoteaddress:"Fj\xe4rradress:",remoteaddress_plural:"Fj\xe4rradresser:",remoteport:"Fj\xe4rrport:",remoteport_plural:"Fj\xe4rrportar:",resolution:"Uppl\xf6sning:",status:"Anslutning:",transport:"Transport:",turn:" (turn)"},dateUtils:{earlier:"Tidigare",today:"Idag",yesterday:"Ig\xe5r"},deepLinking:{appNotInstalled:"Du beh\xf6ver mobilappen {{app}} f\xf6r att g\xe5 med i det h\xe4r m\xf6tet fr\xe5n din telefon.",description:"H\xe4nde inget? Vi f\xf6rs\xf6kte starta m\xf6tet i programmet {{app}} i din skrivbordsapp. F\xf6rs\xf6k igen eller starta det i webbappen {{app}}.",descriptionWithoutWeb:"",downloadApp:"H\xe4mta appen",launchWebButton:"Starta p\xe5 webben",openApp:"Forts\xe4tt till appen",title:"Startar ditt m\xf6te i {{app}} ...",tryAgainButton:"F\xf6rs\xf6k igen p\xe5 skrivbordet"},defaultLink:"t ex. {{url}}",deviceError:{cameraError:"Det gick inte att komma \xe5t kameran",cameraPermission:"Fel vid beg\xe4ran om \xe5tkomst till kamera",microphoneError:"Det gick inte att komma \xe5t mikrofonen",microphonePermission:"Fel vid beg\xe4ran om \xe5tkomst till mikrofon"},deviceSelection:{noPermission:"Beh\xf6righet nekad",previewUnavailable:"F\xf6rhandsgranskning inte tillg\xe4nglig",selectADevice:"V\xe4lj en enhet",testAudio:"Spela upp ett testljud"},dialog:{accessibilityLabel:{liveStreaming:"Lives\xe4ndning"},allow:"Till\xe5t",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"Endast en delad video \xe5t g\xe5ngen till\xe5ts",applicationWindow:"Applikationsf\xf6nster",Back:"Tillbaka",cameraConstraintFailedError:"Din kamera uppfyller inte kraven f\xf6r anv\xe4ndning.",cameraNotFoundError:"Hittar ingen kamera.",cameraNotSendingData:"Vi saknar \xe5tkomst till kameran. Kontrollera om ett annat program anv\xe4nder enheten, v\xe4lj en annan enhet fr\xe5n inst\xe4llningsmenyn eller f\xf6rs\xf6k att starta om programmet.",cameraNotSendingDataTitle:"\xc5tkomst saknas till kameran",cameraPermissionDeniedError:"Du har inte till\xe5tit anv\xe4ndning av din kamera. Du kan g\xe5 med i m\xf6tet men de andra kan d\xe5 inte se dej. Om du vill till\xe5ta anv\xe4ndning av din kamera g\xf6r du det via kameraknappen i URL-f\xe4ltet.",cameraUnknownError:"Av ok\xe4nd anledning kan din kamera inte anv\xe4ndas.",cameraUnsupportedResolutionError:"Din kamera st\xf6der inte den kr\xe4vda videouppl\xf6sningen.",Cancel:"Avbryt",close:"St\xe4ng",conferenceDisconnectMsg:"Kolla din internetanslutning. \xc5teransluter om {{seconds}} sekunder...",conferenceDisconnectTitle:"Du har kopplats ner.",conferenceReloadMsg:"Vi f\xf6rs\xf6ker fixa problemet. \xc5teransluter om {{seconds}} sekunder...",conferenceReloadTitle:"N\xe5got gick snett.",confirm:"Bekr\xe4fta",confirmNo:"Nej",confirmYes:"Ja",connectError:"Ojd\xe5! N\xe5got gick fel och vi kunde inte ansluta till konferensen.",connectErrorWithMsg:"Ojd\xe5! N\xe5got gick fel och vi kunde inte ansluta till konferensen: {{msg}}",connecting:"Ansluter",contactSupport:"Kontakta kundtj\xe4nst",copy:"Kopiera",dismiss:"F\xf6rkasta",displayNameRequired:"",done:"Klar",enterDisplayName:"",error:"Fel",externalInstallationMsg:"Misslyckades att installera skrivbordsdelnings-till\xe4gget.",externalInstallationTitle:"Till\xe4gg kr\xe4vs",goToStore:"G\xe5 till appbutiken",gracefulShutdown:"V\xe5r tj\xe4nst \xe4r f\xf6r tillf\xe4llet nedst\xe4ngd f\xf6r underh\xe5ll. V\xe4nligen f\xf6rs\xf6k senare.",IamHost:"Jag \xe4r v\xe4rd",incorrectRoomLockPassword:"",incorrectPassword:"Fel anv\xe4ndarnamn eller l\xf6senord",inlineInstallationMsg:"Misslyckades att installera skrivbordsdelnings-till\xe4gget.",inlineInstallExtension:"Installera nu",internalError:"Ett fel uppstod. Fel: {{error}}",internalErrorTitle:"Internt fel",kickMessage:"",kickParticipantButton:"Sparka ut",kickParticipantDialog:"Vill du sparka ut den h\xe4r deltagaren?",kickParticipantTitle:"Tysta deltagaren?",kickTitle:"",liveStreaming:"Str\xf6mma",liveStreamingDisabledForGuestTooltip:"G\xe4ster kan inte starta en lives\xe4ndning.",liveStreamingDisabledTooltip:"Starta lives\xe4ndning har inaktiverats.",lockMessage:"Misslyckades att l\xe5sa konferensen.",lockRoom:"",lockTitle:"L\xe5sning misslyckades",logoutQuestion:"\xc4r du s\xe4ker p\xe5 att du vill logga ut och stoppa konferensen?",logoutTitle:"Logga ut",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Din mikrofon uppfyller inte kraven f\xf6r anv\xe4ndning.",micNotFoundError:"Hittar ingen mikrofon.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"Du har inte till\xe5tit anv\xe4ndning av din mikrofon. Du kan g\xe5 med i m\xf6tet men de andra kan d\xe5 inte h\xf6ra dej. Om du vill till\xe5ta anv\xe4ndning av din mikrofon g\xf6r du det via mikrofonknappen i URL-f\xe4ltet.",micUnknownError:"Av ok\xe4nd anledning kan inte din mikrofon anv\xe4ndas.",muteParticipantBody:"Du kan inte aktivera deras mikrofoner, men de kan g\xf6ra det sj\xe4lva.",muteParticipantButton:"Tysta",muteParticipantDialog:"Vill du tysta den h\xe4r deltagaren? Du kan inte aktivera mikrofonen igen, men deltagaren kan n\xe4r som helst g\xf6ra det sj\xe4lv.",muteParticipantTitle:"Tysta deltagaren?",Ok:"Ok",passwordLabel:"",passwordNotSupported:"Att s\xe4tta ett l\xf6senord f\xf6r konferensrummet st\xf6ds inte.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Din webbl\xe4sare blockerar pop-up-f\xf6nster fr\xe5n sajten. Till\xe5t pop-up-f\xf6nster fr\xe5n den h\xe4r sajten i inst\xe4llningarna och f\xf6rs\xf6k igen.",popupErrorTitle:"Pop-up blockerad",recording:"Inspelning",recordingDisabledForGuestTooltip:"G\xe4ster kan inte starta inspelningar.",recordingDisabledTooltip:"Starta inspelning har inaktiverats.",rejoinNow:"\xc5teranslut nu",remoteControlAllowedMessage:"{{user}} godk\xe4nde din beg\xe4ran om fj\xe4rrstyrning.",remoteControlDeniedMessage:"{{user}} avb\xf6jde din beg\xe4ran om fj\xe4rrstyrning.",remoteControlErrorMessage:"Ett fel uppstod n\xe4r fj\xe4rrstyrningsr\xe4ttigheter beg\xe4rdes fr\xe5n {{user}}.",remoteControlRequestMessage:"Vill du till\xe5ta att {{user}} f\xe5r kontrollera din sk\xe4rm?",remoteControlShareScreenWarning:"OBS, om du trycker \"Till\xe5t\" kommer du dela din sk\xe4rm!",remoteControlStopMessage:"Fj\xe4rrstyrningssessionen avslutades.",remoteControlTitle:"Anslutning till fj\xe4rrskrivbord",Remove:"Ta bort",removePassword:"",removeSharedVideoMsg:"\xc4r du s\xe4ker p\xe5 att du vill ta bort din delade video?",removeSharedVideoTitle:"Ta bort den delade videon",reservationError:"Fel i reservationssystemet",reservationErrorMsg:"Felkod: {{code}}, meddelande: {{msg}}",retry:"F\xf6rs\xf6k igen",screenSharingFailedToInstall:"Ut\xf6kningen f\xf6r sk\xe4rmdelning kunde inte installeras.",screenSharingFailedToInstallTitle:"Ut\xf6kningen f\xf6r sk\xe4rmdelning kunde inte installeras",screenSharingFirefoxPermissionDeniedError:"N\xe5got gick fel n\xe4r du f\xf6rs\xf6kte dela sk\xe4rmen. Det kan vara f\xf6r att du inte till\xe5ter det. ",screenSharingFirefoxPermissionDeniedTitle:"Sk\xe4rmdelningen misslyckades!",screenSharingPermissionDeniedError:"N\xe5got \xe4r fel med \xe5tkomstinst\xe4llningarna f\xf6r sk\xe4rmdelningen. Ladda om sidan och f\xf6rs\xf6k igen.",serviceUnavailable:"Tj\xe4nsten otillg\xe4nglig",sessTerminated:"Konferensen avslutades",Share:"Dela",shareVideoLinkError:"Skriv in en fungerande Youtube-l\xe4nk.",shareVideoTitle:"Ta bort en delad video",shareYourScreen:"Dela din sk\xe4rm",shareYourScreenDisabled:"Sk\xe4rmdelning har inaktiverats.",shareYourScreenDisabledForGuest:"G\xe4ster kan inte sk\xe4rmdela.",startLiveStreaming:"Starta lives\xe4ndning",startRecording:"Starta inspelning",startRemoteControlErrorMessage:"Ett fel intr\xe4ffade n\xe4r fj\xe4rrsessionen skulle starta!",stopLiveStreaming:"Avsluta lives\xe4ndning",stopRecording:"Avsluta inspelningen",stopRecordingWarning:"Vill du avsluta den p\xe5g\xe5ende inspelningen?",stopStreamingWarning:"Vill du avsluta den p\xe5g\xe5ende str\xf6mningen?",streamKey:"Nyckel f\xf6r livestr\xf6m",Submit:"Skicka",thankYou:"Tack f\xf6r att du anv\xe4nder {{appName}}!",token:"token",tokenAuthFailed:"Du \xe4r inte beh\xf6rig att delta i det h\xe4r samtalet.",tokenAuthFailedTitle:"Autentisering misslyckades",transcribing:"Transkriberar",unlockRoom:"",userPassword:"anv\xe4ndarl\xf6senord",WaitForHostMsg:"Konferensen {{room}} har inte b\xf6rjat \xe4n. Autentisera konferensen om du \xe4r v\xe4rd. V\xe4nta annars p\xe5 att v\xe4rden startar konferensen.",WaitForHostMsgWOk:"Konferensen {{room}} har inte b\xf6rjat \xe4n. Om du \xe4r v\xe4rd, autentisera konferensen genom att trycka p\xe5 Ok. V\xe4nta annars p\xe5 att v\xe4rden startar konferensen.",WaitingForHost:"V\xe4ntar p\xe5 v\xe4rden ...",Yes:"Ja",yourEntireScreen:"Helsk\xe4rm"},"\x05dialog":{},dialOut:{statusMessage:"\xe4r nu {{status}}"},feedback:{average:"Medel",bad:"D\xe5ligt",detailsLabel:"Ber\xe4tta mer.",good:"Bra",rateExperience:"Betygs\xe4tt din m\xf6tesupplevelse",veryBad:"Mycket d\xe5ligt",veryGood:"Mycket bra"},incomingCall:{answer:"Svara",audioCallTitle:"Inkommande samtal",decline:"F\xf6rkasta",productLabel:"fr\xe5n Jitsi Meet",videoCallTitle:"Inkommande videosamtal"},info:{accessibilityLabel:"Visa info",addPassword:"",cancelPassword:"",conferenceURL:"L\xe4nk:",country:"Land",dialANumber:"Om du vill g\xe5 med i m\xf6tet ringer du n\xe5got av dessa nummer och fyller sedan i PIN-koden.",dialInConferenceID:"PIN-kod:",dialInNotSupported:"Tyv\xe4rr st\xf6ds inte inringning just nu.",dialInNumber:"Inringning:",dialInSummaryError:"Kan inte h\xe4mta inringningsinformation just nu. F\xf6rs\xf6k igen senare.",dialInTollFree:"Avgiftsfritt nummer",genericError:"Oj d\xe5, n\xe5got gick fel.",inviteLiveStream:"Om du vill se lives\xe4ndningen av m\xf6tet klickar du h\xe4r: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"Du \xe4r inbjuden till ett m\xf6te.",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"Lives\xe4ndning:",moreNumbers:"Fler nummer",noNumbers:"Inga inringningsnummer.",noPassword:"Inga enheter",noRoom:"Inget rum specificerades f\xf6r inringning.",numbers:"Inringningsnummer",password:"",title:"Dela",tooltip:"Dela l\xe4nk och information om inringning f\xf6r m\xf6tet",label:"M\xf6tesinformation"},inviteDialog:{alertText:"Det gick inte att bjuda in alla deltagare.",header:"Bjud in",searchCallOnlyPlaceholder:"Ange telefonnummer",searchPeopleOnlyPlaceholder:"Leta efter deltagare",searchPlaceholder:"Deltagare eller telefonnummer",send:"Skicka"},inlineDialogFailure:{msg:"Vi slirade lite.",retry:"F\xf6rs\xf6k igen",support:"Support",supportMsg:"Om detta forts\xe4tter h\xe4nda kontakta"},keyboardShortcuts:{focusLocal:"S\xe4tt fokus p\xe5 din videobild",focusRemote:"Fokusera p\xe5 n\xe5gon annans video",fullScreen:"Visa eller st\xe4ng fullsk\xe4rm",keyboardShortcuts:"Tangentbordsgenv\xe4gar",localRecording:"Visa eller d\xf6lj lokala inspelningsverktyg",mute:"Tysta eller aktivera din mikrofon",pushToTalk:"Tryck-f\xf6r-att-prata",raiseHand:"R\xe4ck upp eller ta ner din jag vill ha ordet-hand",showSpeakerStats:"Visa talarstatistik",toggleChat:"\xd6ppna eller st\xe4ng chatten",toggleFilmstrip:"Visa eller d\xf6lj videominiatyrer",toggleScreensharing:"V\xe4xla mellan kamera och sk\xe4rmdelning",toggleShortcuts:"Visa eller d\xf6lj kortkommandon",videoMute:"Aktivera / avaktivera din kamera"},liveStreaming:{busy:"Vi f\xf6rs\xf6ker frig\xf6ra fler str\xf6mningsresurser. F\xf6rs\xf6k igen senare.",busyTitle:"Alla str\xf6mningsresurser \xe4r upptagna",changeSignIn:"V\xe4xla anv\xe4ndarkonto.",choose:"V\xe4lj en str\xf6m",chooseCTA:"V\xe4lj ett lives\xe4ndningsalternativ. Du \xe4r nu inloggad som {{email}}.",enterStreamKey:"Skriv in l\xf6senordet till YouTube-lives\xe4ndningen h\xe4r.",error:"Str\xf6mning misslyckades. F\xf6rs\xf6k igen.",errorAPI:"Ett fel intr\xe4ffade vid \xe5tkomst till dina YouTube-s\xe4ndningar. F\xf6rs\xf6k att logga in igen.",errorLiveStreamNotEnabled:"Lives\xe4ndning \xe4r otillg\xe4nglig f\xf6r {{email}}. Aktivera lives\xe4ndning eller logga in p\xe5 ett konto d\xe4r det \xe4r aktiverat.",expandedOff:"Lives\xe4ndningen har avslutats",expandedOn:"M\xf6tet lives\xe4nds just nu p\xe5 YouTube.",expandedPending:"Lives\xe4ndningen startas ...",failedToStart:"Str\xf6mningen kunde inte p\xe5b\xf6rjas",getStreamKeyManually:"",invalidStreamKey:"Lives\xe4ndningsl\xf6senordet kan vara felaktigt.",off:"Str\xf6mning avslutad",on:"Str\xf6mma",pending:"B\xf6rja str\xf6mma ...",serviceName:"Lives\xe4ndningstj\xe4nst",signedInAs:"Du \xe4r nu inloggad som:",signIn:"Logga in med Google",signInCTA:"Logga in eller ange ditt lives\xe4ndningsl\xf6senord fr\xe5n YouTube.",signOut:"Logga ut",start:"Starta en lives\xe4ndning",streamIdHelp:"Vad \xe4r det h\xe4r?",unavailableTitle:"Lives\xe4ndning otillg\xe4nglig"},"\x05liveStreaming":{},localRecording:{clientState:{off:"Av",on:"P\xe5",unknown:"Ok\xe4nd"},dialogTitle:"Lokala inspelningsverktyg",duration:"Varaktighet",durationNA:"N/A",encoding:"Kodning",label:"LOR",labelToolTip:"Lokal inspelning aktiverad",localRecording:"Lokal inspelning",me:"Jag",messages:{engaged:"Lokal inspelning aktiverad.",finished:"Inspelningen {{token}} \xe4r avslutad. Skicka den inspelade filen till moderatorn.",finishedModerator:"Inspelningen {{token}} \xe4r avslutad. Den lokala inspelningen har sparats. Be de \xf6vriga deltagarna att dela sina inspelningar.",notModerator:"Du \xe4r inte administrat\xf6r. Du kan inte starta eller avsluta den lokala inspelningen."},moderator:"Moderator",no:"Nej",participant:"Deltagare",participantStats:"Deltagarstatistik",sessionToken:"Sessionstoken",start:"Starta inspelning",stop:"Avsluta inspelning",yes:"Ja"},lockRoomPassword:"l\xf6senord",lockRoomPasswordUppercase:"L\xf6senord",me:"jag",notify:{connectedOneMember:"{{name}} har g\xe5tt med i m\xf6tet",connectedThreePlusMembers:"{{name}} och {{count}} andra har g\xe5tt med i m\xf6tet",connectedTwoMembers:"{{first}} och {{second}} har g\xe5tt med i m\xf6tet",disconnected:"fr\xe5nkopplad",focus:"Konferensfokus",focusFail:"{{component}} inte tillg\xe4nglig - f\xf6rs\xf6ker igen om {{ms}} sek",grantedTo:"Moderatorsr\xe4ttigheter tilldelat till {{to}}!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"Jag",moderator:"Moderatorsr\xe4ttigheter tilldelades!",muted:"Du har startat konversationen utan mikrofon.",mutedTitle:"Du har mikrofonen avst\xe4ngd!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} vill prata.",somebody:"N\xe5gon",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Hmm... din upplevelse med {{appName}} kommer inte att bli s\xe4rskilt bra. Vi f\xf6rs\xf6ker hitta s\xe4tt att f\xf6rb\xe4ttra det, men till dess anv\xe4nd en av de helt st\xf6dda webbl\xe4sarna.",suboptimalExperienceTitle:"Webbl\xe4sarvarning",unmute:"",newDeviceCameraTitle:"Ny kamera hittad",newDeviceAudioTitle:"Ny ljudenhet hittad",newDeviceAction:"Anv\xe4nd"},passwordSetRemotely:"satt av en annan deltagare",passwordDigitsOnly:"",poweredby:"drivs av",presenceStatus:{busy:"Upptagen",calling:"Ringer ...",connected:"Ansluten",connecting:"Ansluter ...",connecting2:"Ansluter* ...",disconnected:"Fr\xe5nkopplad",expired:"Utg\xe5ngen",ignored:"Ignorerad",initializingCall:"Startar samtal ...",invited:"Inbjuden",rejected:"Avvisad",ringing:"Ringer ..."},profile:{setDisplayNameLabel:"Ange ditt visningsnamn",setEmailInput:"Skriv e-postadress",setEmailLabel:"Ange din gravatar-e-postadress",title:"Profil"},recording:{authDropboxText:"Ladda upp till Dropbox",availableSpace:"Tillg\xe4ngligt utrymme: {{spaceLeft}} MB (ungef\xe4r {{duration}} minuters inspelning)",beta:"BETA",busy:"Vi arbetar med att frig\xf6ra inspelningsresurser. F\xf6rs\xf6k igen om n\xe5gra minuter.",busyTitle:"Alla inspelare \xe4r upptagna",error:"Inspelningen misslyckades. F\xf6rs\xf6k igen.",expandedOff:"Inspelningen har avslutats",expandedOn:"M\xf6tet spelas nu in.",expandedPending:"Inspelningen startar ...",failedToStart:"Inspelningen kunde inte p\xe5b\xf6rjas",fileSharingdescription:"Dela inspelningen med m\xf6tesdeltagare",live:"LIVE",loggedIn:"Inloggad som {{userName}}",off:"Inspelningen avslutades",on:"Inspelning",pending:"F\xf6rbereder inspelning av m\xf6tet ...",rec:"REC",serviceDescription:"Din inspelning kommer att sparas av inspelningstj\xe4nsten",serviceName:"Inspelningstj\xe4nst",signIn:"Logga in",signOut:"Logga ut",unavailable:"{{serviceName}} \xe4r inte tillg\xe4nglig. Vi f\xf6rs\xf6ker \xe5tg\xe4rda felet. F\xf6rs\xf6k igen senare.",unavailableTitle:"Inspelning kan inte g\xf6ras"},sectionList:{pullToRefresh:"Dra f\xf6r att uppdatera"},settings:{calendar:{about:"Kalenderintegrationen med {{appName}} anv\xe4nds f\xf6r att h\xe4mta din kalender p\xe5 ett s\xe4kert s\xe4tt s\xe5 att den kan l\xe4sa framtida h\xe4ndelser.",disconnect:"Koppla ifr\xe5n",microsoftSignIn:"Logga in med Microsoft",signedIn:"H\xe4mtar kalenderh\xe4ndelser fr\xe5n {{email}}. Tryck p\xe5 knappen nedan f\xf6r att sluta h\xe4mta kalenderh\xe4ndelser.",title:"Kalender"},devices:"Enheter",followMe:"Alla f\xf6ljer mej",language:"Spr\xe5k",loggedIn:"Inloggad som {{name}}",moderator:"Moderator",more:"Mer",name:"Namn",noDevice:"Inga enheter",selectAudioOutput:"Ljudutmatning",selectCamera:"Kamera",selectMic:"Mikrofon",startAudioMuted:"Alla b\xf6rjar tystade",startVideoMuted:"Alla b\xf6rjar osynliga",title:"Inst\xe4llningar"},settingsView:{alertOk:"OK",alertTitle:"Varning",alertURLText:"Den angivna serverl\xe4nken \xe4r felaktig",buildInfoSection:"Versionsinformation",conferenceSection:"Konferens",displayName:"Sk\xe4rmnamn",email:"E-post",header:"Inst\xe4llningar",profileSection:"Profil",serverURL:"Serverl\xe4nk",startWithAudioMuted:"Starta med ljudet avst\xe4ngt",startWithVideoMuted:"Starta med videon avst\xe4ngd",version:"Version"},share:{dialInfoText:"",mainText:""},speaker:"Talare",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"Namn",seconds:"{{count}}s",speakerStats:"Talarstatistik",speakerTime:"Talartid"},startupoverlay:{policyText:" ",title:"{{app}} vill anv\xe4nda din kamera och mikrofon."},suspendedoverlay:{rejoinKeyTitle:"G\xe5 med igen",text:"Klicka p\xe5 knappen \xc5teranslut f\xf6r att koppla upp igen.",title:"Ditt videosamtal avbr\xf6ts d\xe5 din dator gick in i vilol\xe4ge."},toolbar:{accessibilityLabel:{audioOnly:"Sl\xe5 av eller p\xe5 ljudet",audioRoute:"V\xe4lj ljudenhet",callQuality:"",cc:"Sl\xe5 av eller p\xe5 undertexter",chat:"\xd6ppna eller st\xe4ng chattf\xf6nster",document:"\xd6ppna eller st\xe4ng delat dokument",feedback:"L\xe4mna feedback",fullScreen:"\xd6ppna eller st\xe4ng fullsk\xe4rm",hangup:"L\xe4mna samtalet",invite:"Bjud in andra",kick:"Sparka ut deltagare",localRecording:"\xd6ppna eller st\xe4ng lokala inspelningsverktyg",lockRoom:"Sl\xe5 av eller p\xe5 m\xf6tesl\xf6senord",moreActions:"\xd6ppna eller st\xe4ng menyn f\xf6r fler \xe5tg\xe4rder",moreActionsMenu:"Meny f\xf6r fler \xe5tg\xe4rder",mute:"Sl\xe5 av eller p\xe5 ljud",pip:"\xd6ppna eller st\xe4ng bild-i-bild-l\xe4ge",profile:"Redigera din profil",raiseHand:"R\xe4ck upp eller ta ner handen",recording:"Sl\xe5 av eller p\xe5 inspelning",remoteMute:"Tysta deltagare",Settings:"\xd6ppna eller st\xe4ng inst\xe4llningar",sharedvideo:"Sl\xe5 av eller p\xe5 Youtube-videodelning",shareRoom:"Bjud in n\xe5gon",shareYourScreen:"Sl\xe5 av eller p\xe5 sk\xe4rmdelning",shortcuts:"St\xe4ng eller \xf6ppna genv\xe4gar",show:"",speakerStats:"St\xe4ng eller \xf6ppna h\xf6gstalarstatistik",tileView:"\xd6ppna eller st\xe4ng panelvyn",toggleCamera:"\xd6ppna eller st\xe4ng kamera",videomute:"S\xe4tt p\xe5 eller st\xe4ng av mikrofonen",videoblur:""},addPeople:"L\xe4gg till personer i samtal",audioOnlyOff:"Avsluta ljudl\xe4get",audioOnlyOn:"Starta ljudl\xe4get",audioRoute:"V\xe4lj ljudenhet",authenticate:"Autentisera",callQuality:"",chat:"\xd6ppna / st\xe4ng chatten",closeChat:"St\xe4ng chatt",documentClose:"St\xe4ng delat dokument",documentOpen:"\xd6ppna delat dokument",enterFullScreen:"Visa fullsk\xe4rm",enterTileView:"\xd6ppna panelvy",exitFullScreen:"St\xe4ng fullsk\xe4rm",exitTileView:"St\xe4ng panelvy",feedback:"L\xe4mna feedback",hangup:"L\xe4mna",invite:"Bjud in andra",login:"Logga in",logout:"Logga ut",lowerYourHand:"Ta ner handen",moreActions:"Fler handlingar",mute:"Sl\xe5 av/p\xe5 ljud",openChat:"\xd6ppna chatt",pip:"\xd6ppna bild-i-bild-l\xe4ge",profile:"Redigera din profil",raiseHand:"R\xe4ck upp / ta ner din hand",raiseYourHand:"R\xe4ck upp handen",Settings:"Inst\xe4llningar",sharedvideo:"Dela en Youtube-video",shareRoom:"Bjud in n\xe5gon",shortcuts:"Visa genv\xe4gar",speakerStats:"H\xf6gtalarspecifikationer",startScreenSharing:"Starta sk\xe4rmdelning",startSubtitles:"Starta undertextning",stopScreenSharing:"Avsluta sk\xe4mdelning",stopSubtitles:"Avsluta undertextning",stopSharedVideo:"Pausa YouTube-video",talkWhileMutedPopup:"F\xf6rs\xf6ker du tala? Din mikrofon \xe4r tystad.",tileViewToggle:"\xd6ppna eller st\xe4ng panelvyn",toggleCamera:"\xd6ppna eller st\xe4ng kamera",videomute:"Aktivera / avaktivera kameran",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Starta / Avsluta undertexter",error:"Transkriberingen misslyckades. F\xf6rs\xf6k igen.",expandedLabel:"Transkribering \xe4r aktiverad",failedToStart:"Det gick inte att starta transkribering",labelToolTip:"M\xf6tet transkriberas",off:"Transkribering avslutades",pending:"F\xf6rbereder transkribering av m\xf6tet ...",start:"B\xf6rja visa undertexter",stop:"Sluta visa undertexter",tr:"TR"},userMedia:{androidGrantPermissions:"V\xe4lj Till\xe5t n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",chromeGrantPermissions:"V\xe4lj Till\xe5t n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",edgeGrantPermissions:"V\xe4lj Ja n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",electronGrantPermissions:"Till\xe5t anv\xe4ndning av din kamera och mikrofon",firefoxGrantPermissions:"V\xe4lj Dela vald enhet n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",iexplorerGrantPermissions:"V\xe4lj OK n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",nwjsGrantPermissions:"Till\xe5t anv\xe4ndning av din kamera och mikrofon",operaGrantPermissions:"V\xe4lj Till\xe5t n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.","react-nativeGrantPermissions":"V\xe4lj Till\xe5t n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst.",safariGrantPermissions:"V\xe4lj OK n\xe4r din webbl\xe4sare beg\xe4r \xe5tkomst."},videoSIPGW:{busy:"Vi arbetar med att frig\xf6ra resurser. F\xf6rs\xf6k igen om n\xe5gra minuter.",busyTitle:"Rumtj\xe4nsten \xe4r just nu upptagen",errorAlreadyInvited:"{{displayName}} \xe4r redan inbjuden",errorInvite:"Konferensen \xe4r inte skapad \xe4n. F\xf6rs\xf6k igen senare.",errorInviteFailed:"Vi arbetar f\xf6r att l\xf6sa problemet. F\xf6rs\xf6k igen senare.",errorInviteFailedTitle:"Inbjudan till {{displayName}} misslyckades",errorInviteTitle:"Inbjudan till rum misslyckades",pending:"{{displayName}} har bjudits in"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"Du anv\xe4nder ljudl\xe4get. L\xe4get sparar bandbredd men du kan inte se andras videor.",callQuality:"",hd:"HD",highDefinition:"High definition",labelTooiltipNoVideo:"Ingen video",labelTooltipAudioOnly:"Enbart ljud-l\xe4ge aktiverat",ld:"LD",lowDefinition:"Low definition",onlyAudioAvailable:"Enbart ljud tillg\xe4ngligt",onlyAudioSupported:"Vi st\xf6der bara ljud i denna webbl\xe4sare",p2pEnabled:"Peer to peer anv\xe4nds",p2pVideoQualityDescription:"",recHighDefinitionOnly:"F\xf6redrar high definition.",sd:"SD",standardDefinition:"Standard definition"},videothumbnail:{domute:"Tysta",flip:"V\xe4nd",kick:"Sparka ut",moderator:"Moderator",mute:"Deltagaren har avst\xe4ngd mikrofon",muted:"Tystad",remoteControl:"Fj\xe4rrkontroll",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Tryck f\xf6r att g\xe5 med",roomname:"Skriv in rumsnamn"},appDescription:"S\xe4tt ig\xe5ng, videochatta med hela gruppen. Bjud in alla du k\xe4nner. {{app}} \xe4r en helt krypterad , open source videokonferensl\xf6sning som du kan anv\xe4nda obegr\xe4nsat utan kostnad \u2014 inte ens ett anv\xe4ndarkonto beh\xf6vs.",audioVideoSwitch:{audio:"Ljud",video:"Video"},calendar:"Kalender",connectCalendarButton:"Anslut din kalender",connectCalendarText:"Anslut din kalender f\xf6r att se alla m\xf6ten i {{app}}. L\xe4gg \xe4ven till m\xf6ten med {{provider}} i din kalender och starta dem med ett klick.",enterRoomTitle:"Starta ett nytt m\xf6te",go:"K\xd6R",join:"G\xe5 med",info:"Info",privacy:"Integritet",recentList:"Tidigare",recentListDelete:"Radera",recentListEmpty:"Inga tidigare m\xf6ten. Chatta med ditt team och hitta alla tidigare m\xf6ten d\xe4r.",reducedUIText:"",roomname:"Skriv in rumsnamn",roomnameHint:"Ange namnet eller URL:en till m\xf6tesrummet du vill ansluta till. Du kan hitta p\xe5 ett nytt namn, ber\xe4tta d\xe5 f\xf6r de andra du t\xe4nker m\xf6ta s\xe5 de anger samma namn.",sendFeedback:"Ge \xe5terkoppling",terms:"Termer",title:"S\xe4kra, v\xe4lutrustade och helt kostnadsfria videokonferenser"}}},678,[]); -__d(function(n,i,g,T,a,e,h){a.exports={en:"Ti\u1ebfng Anh",af:"Ti\xea\u0301ng Afrika",az:"Ti\xea\u0301ng Azecbaizan",bg:"Ti\u1ebfng Bulgaria",cs:"Ti\xea\u0301ng Se\u0301c",de:"Ti\u1ebfng \u0110\u1ee9c",el:"Ti\xea\u0301ng Nh\xe2\u0323t",eo:"Ti\u1ebfng Esperanto",es:"Ti\u1ebfng T\xe2y Ban Nha",fr:"Ti\u1ebfng Ph\xe1p",hy:"Ti\u1ebfng Acmenia",it:"Ti\u1ebfng \xdd",ja:"Ti\xea\u0301ng Nh\xe2\u0323t",ko:"Ti\xea\u0301ng Ha\u0300n",nb:"Ti\u1ebfng Na Uy",oc:"Ti\u1ebfng Occitan",pl:"Ti\u1ebfng Ba Lan",ptBR:"Ti\u1ebfng B\u1ed3 \u0110\xe0o Nha (Brazil)",ru:"Ti\u1ebfng Nga",sk:"Ti\u1ebfng Slovak",sl:"Ti\u1ebfng Slovenia",sv:"Ti\u1ebfng Th\u1ee5y \u0110i\u1ec3n",tr:"Ti\u1ebfng Th\u1ed5 Nh\u0129 K\u1ef3",vi:"Ti\xea\u0301ng Vi\xea\u0323t",zhCN:"Ti\u1ebfng Hoa (Trung Qu\u1ed1c)"}},679,[]); -__d(function(n,t,i,h,e,a,c){e.exports={addPeople:{add:"M\u1eddi",countryNotSupported:"Chu\u0301ng t\xf4i ch\u01b0a h\xf4\u0303 tr\u01a1\u0323 \u0111i\u0301ch \u0111\xea\u0301n na\u0300y.",countryReminder:"\u0110ang go\u0323i ra ngoa\u0300i My\u0303? \u0110a\u0309m ba\u0309o b\u0103\u0301t \u0111\xe2\u0300u b\u0103\u0300ng ma\u0303 qu\xf4\u0301c gia!",disabled:"Ba\u0323n kh\xf4ng th\xea\u0309 m\u01a1\u0300i th\xeam ng\u01b0\u01a1\u0300i.",failedToAdd:"",footerText:"Quay s\xf4\u0301 bi\u0323 t\u0103\u0301t.",loading:"\u0110ang ti\u0300m ki\xea\u0301m ng\u01b0\u01a1\u0300i ho\u0103\u0323c s\xf4\u0301 \u0111i\xea\u0323n thoa\u0323i.",loadingNumber:"\u0110ang xa\u0301c nh\xe2\u0323n s\xf4\u0301 \u0111i\xea\u0323n thoa\u0323i.",loadingPeople:"\u0110ang ti\u0300m ki\xea\u0301m ng\u01b0\u01a1\u0300i \u0111\xea\u0309 m\u01a1\u0300i",noResults:"Kh\xf4ng ti\u0300m \u0111\u01b0\u01a1\u0323c k\xea\u0301t qua\u0309 kh\u01a1\u0301p",noValidNumbers:"Xin m\u1eddi nh\u1eadp m\u1ed9t s\u1ed1 \u0111i\u1ec7n tho\u1ea1i",searchNumbers:"Th\xeam s\u1ed1 \u0111i\u1ec7n tho\u1ea1i",searchPeople:"T\xecm ng\u01b0\u1eddi",searchPeopleAndNumbers:"T\xecm ng\u01b0\u1eddi v\xe0 th\xeam s\u1ed1",telephone:"S\u1ed1:{{number}}",title:"M\u1eddi ng\u01b0\u1eddi tham d\u1ef1 cu\u1ed9c h\u1ecdp n\xe0y"},audioDevices:{bluetooth:"Bluetooth",headphones:"Tai nghe",phone:"\u0110i\u1ec7n tho\u1ea1i",speaker:"Di\u1ec5n gi\u1ea3"},audioOnly:{audioOnly:"Ch\u1ec9 \xe2m thanh"},calendarSync:{addMeetingURL:"Th\xeam m\u1ed9t li\xean k\u1ebft h\u1ecdp",confirmAddLink:"B\u1ea1n c\xf3 mu\u1ed1n th\xeam m\u1ed9t li\xean ki\u1ebft t\u1edbi s\u1ef1 ki\u1ec7n n\xe0y?",error:{appConfiguration:"T\xedch h\u1ee3p l\u1ecbch ch\u01b0a \u0111\u01b0\u1ee3c c\u1ea5u h\xecnh \u0111\xfang.",generic:"M\u1ed9t l\u1ed7i xu\u1ea5t hi\u1ec7n. Vui l\xf2ng ki\u1ec3m tra c\u1ea5u h\xecnh l\u1ecbch ho\u1eb7c th\u1eed l\xe0m t\u01b0\u01a1i l\u1ecbch.",notSignedIn:"M\u1ed9t l\u1ed7i x\u1ea3y ra khi x\xe1c th\u1ef1c \u0111\u1ec3 xem l\u1ecbch s\u1ef1 ki\u1ec7n. Vui l\xf2ng ki\u1ec3m tra c\u1ea5u h\xecnh l\u1ecbch v\xe0 th\u1eed \u0111\u0103ng nh\u1eadp l\u1ea1i."},join:"Tham gia",joinTooltip:"Tham gia cu\u1ecdc h\u1ecdp",nextMeeting:"Cu\u1ed9c h\u1ecdp ti\u1ebfp theo",noEvents:"Kh\xf4ng c\xf3 s\u1ef1 ki\u1ec7n \u0111\u01b0\u1ee3c l\xean l\u1ecbch n\xe0o ti\u1ebfp theo.",ongoingMeeting:"cu\u1ed9c h\u1ecdp \u0111ang di\u1ec5n ra",permissionButton:"M\u1edf c\u1ea5u h\xecnh",permissionMessage:"Y\xeau c\u1ea7u quy\u1ec1n truy c\u1eadp L\u1ecbch \u0111\u1ec3 th\u1ea5y cu\u1ed9c h\u1ecdp c\u1ee7a b\u1ea1n tr\xean \u1ee9ng d\u1ee5ng.",refresh:"L\xe0m t\u01b0\u01a1i l\u1ecbch",today:"H\xf4m nay"},chat:{error:"L\u1ed7i: th\xf4ng \u0111i\u1ec7p c\u1ee7a b\u1ea1n \"{{originalText}}\" kh\xf4ng \u0111\u01b0\u1ee3c g\u1eedi. Nguy\xean nh\xe2n: {{error}}",messagebox:"Nh\u1eadp m\u1ed9t th\xf4ng \u0111i\u1ec7p",nickname:{popover:"Ch\u1ecdn bi\u1ec7t danh",title:"Nh\u1eadp m\u1ed9t t\xean \u0111\u1ec3 s\u1eed d\u1ee5ng Chat"},title:"Chat"},connectingOverlay:{joiningRoom:"\u0110ang k\u1ebft n\u1ed1i t\u1edbi cu\u1ed9c h\u1ecdp c\u1ee7a b\u1ea1n..."},connection:{ATTACHED:"\u0110\xe3 \u0111\xednh k\xe8m",AUTHENTICATING:"\u0110ang x\xe1c th\u1ef1c",AUTHFAIL:"X\xe1c th\u1ef1c th\u1ea5t b\u1ea1i",CONNECTED:"\u0110\xe3 k\u1ebft n\u1ed1i",CONNECTING:"\u0110ang k\u1ebft n\u1ed1i",CONNFAIL:"K\u1ebft n\u1ed1i th\u1ea5t b\u1ea1i",DISCONNECTED:"\u0110\xe3 ng\u1eaft k\u1ebft n\u1ed1i",DISCONNECTING:"\u0110ang ng\u1eaft k\u1ebft n\u1ed1i",ERROR:"L\u1ed7i",RECONNECTING:"\u0110\xe3 x\u1ea3y ra s\u1ef1 c\u1ed1 m\u1ea1ng. \u0110ang k\u1ebft n\u1ed1i l\u1ea1i..."},connectionindicator:{address:"\u0110\u1ecba ch\u1ec9:",bandwidth:"B\u0103ng th\xf4ng \u01b0\u1edbc t\xednh:",bitrate:"T\u1ed1c \u0111\u1ed9:",bridgeCount:"M\xe1y ch\u1ee7:",connectedTo:"\u0110\xe3 k\u1ebft n\u1ed1i t\u1edbi:",framerate:"T\u1ef7 l\u1ec7 khung h\xecnh:",less:"Hi\u1ec3n th\u1ecb \xedt h\u01a1n",localaddress_0:"\u0110\u1ecba ch\u1ec9 \u0111\u1ecba ph\u01b0\u01a1ng:",localaddress_1:"C\xe1c \u0111\u1ecba ch\u1ec9 \u0111\u1ecba ph\u01b0\u01a1ng:",localport_0:"C\u1ed5ng \u0111\u1ecba ph\u01b0\u01a1ng:",localport_1:"C\xe1c c\u1ed5ng \u0111\u1ecba ph\u01b0\u01a1ng:",more:"Hi\u1ec3n th\u1ecb nhi\u1ec1u h\u01a1n",packetloss:"M\u1ea5t g\xf3i tin:",quality:{good:"T\u1ed1t",inactive:"Kh\xf4ng active",lost:"M\u1ea5t k\u1ebft n\u1ed1i",nonoptimal:"Kh\xf4ng t\u1ed1i \u01b0u",poor:"K\xe9m ch\u1ea5t l\u01b0\u1ee3ng"},remoteaddress_0:"\u0110\u1ecba ch\u1ec9 t\u1eeb xa:",remoteaddress_1:"C\xe1c \u0111\u1ecba ch\u1ec9 t\u1eeb xa:",remoteport_0:"C\u1ed5ng t\u1eeb xa:",remoteport_1:"C\xe1c c\u1ed5ng t\u1eeb xa:",resolution:"\u0110\u1ed9 ph\xe2n gi\u1ea3i:",status:"K\u1ebft n\u1ed1i:",transport_0:"V\u1eadn chuy\u1ec3n:",transport_1:"C\xe1c v\u1eadn chuy\u1ec3n:",turn:"turn"},dateUtils:{earlier:"S\u1edbm h\u01a1n",today:"H\xf4m nay",yesterday:"H\xf4m qua"},deepLinking:{appNotInstalled:"B\u1ea1n c\u1ea7n \u1ee9ng d\u1ee5ng {{app}} mobile \u0111\u1ec3 tham gia v\xe0o cu\u1ed9c h\u1ecdp n\xe0y b\u1eb1ng \u0111i\u1ec7n tho\u1ea1i.",description:"Kh\xf4ng c\xf3 g\xec di\u1ec5n ra? Ch\xfang t\xf4i \u0111ang ch\u1ea1y cu\u1ed9c h\u1ecdp tr\xean \u1ee9ng d\u1ee5ng desktop {{app}}. Th\u1eed l\u1ea1i ho\u1eb7c ch\u1ea1y tr\xean \u1ee9ng d\u1ee5ng web {{app}}.",descriptionWithoutWeb:"",downloadApp:"T\u1ea3i ph\u1ea7n m\u1ec1m",launchWebButton:"Ch\u1ea1y tr\xean web",openApp:"Ti\u1ebfp t\u1ee5c \u1ee9ng d\u1ee5ng n\xe0y",title:"Th\u1ef1c hi\u1ec7n cu\u1ed9c h\u1ecdp tr\xean {{app}}...",tryAgainButton:"Th\u1eed l\u1ea1i tr\xean desktop"},defaultLink:"v\xed d\u1ee5: {{url}}",deviceError:{cameraError:"Truy c\u1eadp camera th\u1ea5t b\u1ea1i",cameraPermission:"L\u1ed7i \u0111\u1ecdc quy\u1ec1n c\u1ee7a camera",microphoneError:"Truy c\u1eadp Microphone th\u1ea5t b\u1ea1i",microphonePermission:"L\u1ed7i \u0111\u1ecdc quy\u1ec1n c\u1ee7a microphone"},deviceSelection:{noPermission:"Kh\xf4ng \u0111\u01b0\u1ee3c c\u1ea5p quy\u1ec1n",previewUnavailable:"Xem tr\u01b0\u1edbc kh\xf4ng kh\u1ea3 d\u1ee5ng",selectADevice:"Ch\u1ecdn m\u1ed9t thi\u1ebft b\u1ecb",testAudio:"Ch\u1ea1y th\u1eed t\u1ec7p \xe2m thanh"},dialog:{accessibilityLabel:{liveStreaming:"Ph\xe1t tr\u1ef1c tuy\u1ebfn"},allow:"Cho ph\xe9p",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"M\u1ed7i l\xfac ch\u1ec9 m\u1ed9t ng\u01b0\u1eddi \u0111\u01b0\u1ee3c chia s\u1ebb video.",applicationWindow:"C\u1eeda s\u1ed5 \u1ee9ng d\u1ee5ng",Back:"Quay l\u1ea1i",cameraConstraintFailedError:"Camera c\u1ee7a b\u1ea1n kh\xf4ng \u0111\xe1p \u1ee9ng \u0111\u01b0\u1ee3c m\u1ed9t s\u1ed1 y\xeau c\u1ea7u b\u1eaft bu\u1ed9c.",cameraNotFoundError:"Kh\xf4ng t\xecm th\u1ea5y camera.",cameraNotSendingData:"Kh\xf4ng truy c\u1eadp \u0111\u01b0\u1ee3c camera c\u1ee7a b\u1ea1n. Ki\u1ec3m tra xem c\xf3 \u1ee9ng dung kh\xe1c \u0111ang s\u1eed d\u1ee5ng camera kh\xf4ng, ho\u1eb7c ch\u1ecdn m\u1ed9t camera kh\xe1c trong ph\u1ea7n c\xe0i \u0111\u1eb7t, hay t\u1ea3i l\u1ea1i \u1ee9ng d\u1ee5ng",cameraNotSendingDataTitle:"Kh\xf4ng truy c\u1eadp \u0111\u01b0\u1ee3c camera",cameraPermissionDeniedError:"B\u1ea1n ch\u01b0a cho ph\xe9p s\u1eed d\u1ee5ng camera c\u1ee7a m\xecnh. B\u1ea1n v\u1eabn c\xf3 th\u1ec3 tham gia h\u1ed9i ngh\u1ecb nh\u01b0ng nh\u1eefng ng\u01b0\u1eddi kh\xe1c s\u1ebd kh\xf4ng nh\xecn th\u1ea5y b\u1ea1n. S\u1eed d\u1ee5ng n\xfat camera tr\xean thanh \u0111\u1ecba ch\u1ec9 \u0111\u1ec3 s\u1eeda l\u1ed7i n\xe0y.",cameraUnknownError:"Kh\xf4ng th\u1ec3 s\u1eed d\u1ee5ng camera v\xec l\xfd do kh\xf4ng r\xf5 r\xe0ng.",cameraUnsupportedResolutionError:"Camera c\u1ee7a b\u1ea1n kh\xf4ng h\u1ed7 tr\u1ee3 \u0111\u1ed9 ph\xe2n gi\u1ea3i video y\xeau c\u1ea7u.",Cancel:"H\u1ee7y",close:"\u0110\xf3ng",conferenceDisconnectMsg:"B\u1ea1n c\xf3 th\u1ec3 mu\u1ed1n ki\u1ec3m tra k\u1ebft n\u1ed1i m\u1ea1ng c\u1ee7a m\xecnh. \u0110ang k\u1ebft n\u1ed1i l\u1ea1i trong {{seconds}} gi\xe2y...",conferenceDisconnectTitle:"B\u1ea1n \u0111\xe3 b\u1ecb ng\u1eaft k\u1ebft n\u1ed1i.",conferenceReloadMsg:"Ch\xfang t\xf4i \u0111ang c\u1ed1 g\u1eafng s\u1eeda l\u1ed7i n\xe0y. \u0110ang k\u1ebft n\u1ed1i l\u1ea1i trong {{seconds}} gi\xe2y...",conferenceReloadTitle:"Th\u1eadt kh\xf4ng may, c\xf3 \u0111i\u1ec1u g\xec \u0111\xf3 \u0111\xe3 sai.",confirm:"X\xe1c nh\u1eadn",confirmNo:"Kh\xf4ng",confirmYes:"C\xf3",connectError:"R\u1ea5t ti\u1ebfc! \u0110\xe3 x\u1ea3y ra s\u1ef1 c\u1ed1 v\xe0 ch\xfang t\xf4i kh\xf4ng th\u1ec3 k\u1ebft n\u1ed1i v\u1edbi h\u1ed9i ngh\u1ecb.",connectErrorWithMsg:"R\u1ea5t ti\u1ebfc! \u0110\xe3 x\u1ea3y ra s\u1ef1 c\u1ed1 v\xe0 ch\xfang t\xf4i kh\xf4ng th\u1ec3 k\u1ebft n\u1ed1i v\u1edbi h\u1ed9i ngh\u1ecb: {{msg}}",connecting:"\u0110ang k\u1ebft n\u1ed1i",contactSupport:"Li\xean h\u1ec7 h\u1ed7 tr\u1ee3 k\u1ef9 thu\u1eadt",copy:"Sao ch\xe9p",dismiss:"H\u1ee7y",displayNameRequired:"",done:"Xong",enterDisplayName:"",error:"L\u1ed7i",externalInstallationMsg:"B\u1ea1n c\u1ea7n c\xe0i \u0111\u1eb7t ti\u1ec7n \xedch m\u1edf r\u1ed9ng chia s\u1ebb m\xe1y t\xednh c\u1ee7a ch\xfang t\xf4i.",externalInstallationTitle:"Y\xeau c\u1ea7u ti\u1ec7n \xedch m\u1edf r\u1ed9ng",goToStore:"\u0110i t\u1edbi c\u1eeda h\xe0ng tr\xean m\u1ea1ng",gracefulShutdown:"D\u1ecbch v\u1ee5 c\u1ee7a ch\xfang t\xf4i hi\u1ec7n \u0111ang b\u1ea3o tr\xec. Vui l\xf2ng th\u1eed l\u1ea1i sau.",IamHost:"T\xf4i l\xe0 ch\u1ee7 ngh\u1ecb",incorrectRoomLockPassword:"",incorrectPassword:"T\xean ng\u01b0\u1eddi d\xf9ng ho\u1eb7c m\u1eadt kh\u1ea9u kh\xf4ng \u0111\xfang",inlineInstallationMsg:"B\u1ea1n c\u1ea7n c\xe0i \u0111\u1eb7t ti\u1ec7n \xedch m\u1edf r\u1ed9ng chia s\u1ebb m\xe1y t\xednh c\u1ee7a ch\xfang t\xf4i.",inlineInstallExtension:"C\xe0i \u0111\u1eb7t ngay",internalError:"Duh! C\xf3 l\u1ed7i x\u1ea9y ra. L\u1ed7i c\u1ee5 th\u1ec3 l\xe0: {{error}}",internalErrorTitle:"L\u1ed7i c\u1ee5c b\u1ed9",kickMessage:"",kickParticipantButton:"\u0110\u1ea9y ra",kickParticipantDialog:"B\u1ea1n c\xf3 ch\u1eafc mu\u1ed1n \u0111\u1ea9y ng\u01b0\u1eddi n\xe0y ra?",kickParticipantTitle:"T\u1eaft ti\u1ebfng c\u1ee7a ng\u01b0\u1eddi tham d\u1ef1 n\xe0y?",kickTitle:"",liveStreaming:"Ph\xe1t tr\u1ef1c tuy\u1ebfn",liveStreamingDisabledForGuestTooltip:"Kh\xe1ch kh\xf4ng th\u1ec3 ph\xe1t tr\u1ef1c tuy\u1ebfn.",liveStreamingDisabledTooltip:"Kh\u1edfi t\u1ea1o ph\xe1t tr\u1ef1c tuy\u1ebfn \u0111\xe3 t\u1eaft.",lockMessage:"Kh\xf3a h\u1ed9i ngh\u1ecb th\u1ea5t b\u1ea1i.",lockRoom:"",lockTitle:"Kh\xf3a th\u1ea5t b\u1ea1i",logoutQuestion:"B\u1ea1n c\xf3 ch\u1eafc ch\u1eafn mu\u1ed1n \u0111\u0103ng xu\u1ea5t v\xe0 d\u1eebng h\u1ed9i ngh\u1ecb?",logoutTitle:"\u0110\u0103ng xu\u1ea5t",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"Microphone c\u1ee7a b\u1ea1n kh\xf4ng \u0111\xe1p \u1ee9ng \u0111\u01b0\u1ee3c m\u1ed9t s\u1ed1 y\xeau c\u1ea7u b\u1eaft bu\u1ed9c.",micNotFoundError:"Kh\xf4ng t\xecm th\u1ea5y microphone.",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"B\u1ea1n ch\u01b0a c\u1ea5p ph\xe9p s\u1eed d\u1ee5ng microphone c\u1ee7a b\u1ea1n. B\u1ea1n v\u1eabn c\xf3 th\u1ec3 tham gia h\u1ed9i ngh\u1ecb nh\u01b0ng nh\u1eefng ng\u01b0\u1eddi kh\xe1c s\u1ebd kh\xf4ng nghe th\u1ea5y b\u1ea1n. S\u1eed d\u1ee5ng n\xfat camera tr\xean thanh \u0111\u1ecba ch\u1ec9 \u0111\u1ec3 s\u1eeda l\u1ed7i n\xe0y.",micUnknownError:"Kh\xf4ng th\u1ec3 s\u1eed d\u1ee5ng microphone v\xec l\xfd do kh\xf4ng r\xf5 r\xe0ng.",muteParticipantBody:"B\u1ea1n kh\xf4ng th\u1ec3 t\u1eaft ti\u1ebfng c\u1ee7a h\u1ecd, nh\u01b0ng h\u1ecd c\xf3 th\u1ec3 t\u1ef1 t\u1eaft ti\u1ebfng b\u1ea5t c\u1ee9 l\xfac n\xe0o.",muteParticipantButton:"T\u1eaft ti\u1ebfng",muteParticipantDialog:"B\u1ea1n mu\u1ed1n t\u1eaft ti\u1ebfng c\u1ee7a ng\u01b0\u1eddi n\xe0y? B\u1ea1n s\u1ebd kh\xf4ng th\u1ec3 b\u1eadt l\u1ea1i ti\u1ebfng, nh\u01b0ng h\u1ecd c\xf3 th\u1ec3 t\u1ef1 b\u1eadt l\u1ea1i ti\u1ebfng b\u1ea5t c\u1ee9 l\xfac n\xe0o.",muteParticipantTitle:"T\u1eaft ti\u1ebfng c\u1ee7a ng\u01b0\u1eddi tham d\u1ef1 n\xe0y?",Ok:"\u0110\u01b0\u1ee3c",passwordLabel:"",passwordNotSupported:"Ph\xf2ng h\u1ecdp kh\xf4ng h\u1ed7 tr\u1ee3 kh\xf3a b\u1eb1ng m\u1eadt kh\u1ea9u.",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"Tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n \u0111\xe3 ch\u1eb7n c\u1eeda s\u1ed5 pop-up t\u1eeb website hi\u1ec7n h\xe0nh. L\xe0m \u01a1n cho ph\xe9p pop-up trong c\xe0i \u0111\u1eb7t c\u1ee7a tr\xecnh duy\u1ec7t v\xe0 th\u1eed l\u1ea1i",popupErrorTitle:"C\u1eeda s\u1ed5 Pop-Up b\u1ecb ch\u1eb7n",recording:"\u0110ang ghi \xe2m",recordingDisabledForGuestTooltip:"Kh\xe1ch kh\xf4ng th\u1ec3 kh\u1edfi t\u1ea1o ghi h\xecnh.",recordingDisabledTooltip:"Kh\u1edfi \u0111\u1ed9ng ghi \xe2m \u0111\xe3 b\u1ecb t\u1eaft.",rejoinNow:"Tham gia l\u1ea1i lu\xf4n",remoteControlAllowedMessage:"{{user}} \u0111\xe3 ch\u1ea5p nh\u1eadn y\xeau c\u1ea7u \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa c\u1ee7a b\u1ea1n!",remoteControlDeniedMessage:"{{user}} \u0111\xe3 t\u1eeb ch\u1ed1i y\xeau c\u1ea7u \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa c\u1ee7a b\u1ea1n!",remoteControlErrorMessage:"\u0110\xe3 x\u1ea3y ra l\u1ed7i khi c\u1ed1 g\u1eafng y\xeau c\u1ea7u quy\u1ec1n \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa t\u1eeb {{user}}!",remoteControlRequestMessage:"B\u1ea1n c\xf3 cho ph\xe9p {{user}} \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa m\xe0n h\xecnh c\u1ee7a b\u1ea1n kh\xf4ng?",remoteControlShareScreenWarning:"L\u01b0u \xfd r\u1eb1ng n\u1ebfu b\u1ea1n \u1ea5n \"Cho ph\xe9p\" b\u1ea1n s\u1ebd chia s\u1ebb m\xe0n h\xecnh c\u1ee7a m\xecnh!",remoteControlStopMessage:"Phi\xean \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa \u0111\xe3 k\u1ebft th\xfac!",remoteControlTitle:"\u0110i\u1ec1u khi\u1ec3n m\xe0n h\xecnh t\u1eeb xa",Remove:"X\xf3a",removePassword:"",removeSharedVideoMsg:"B\u1ea1n c\xf3 ch\u1eafc ch\u1eafn mu\u1ed1n x\xf3a video \u0111\xe3 chia s\u1ebb c\u1ee7a m\xecnh kh\xf4ng?",removeSharedVideoTitle:"X\xf3a video chia s\u1ebb",reservationError:"L\u1ed7i h\u1ec7 th\u1ed1ng \u0111\u1eb7t ph\xf2ng",reservationErrorMsg:"M\xe3 l\u1ed7i: {{code}}, th\xf4ng b\xe1o: {{msg}}",retry:"Th\u1eed l\u1ea1i",screenSharingFailedToInstall:"Duh! Kh\xf4ng c\xe0i \u0111\u1eb7t \u0111\u01b0\u1ee3c b\u1ed9 m\u1edf r\u1ed9ng chia s\u1ebb m\xe0n h\xecnh",screenSharingFailedToInstallTitle:"Duh! B\u1ed9 m\u1edf r\u1ed9ng chia s\u1ebb m\xe0n h\xecnh c\xf3 v\u1ea5n \u0111\u1ec1 v\u1edbi c\u1ea5u h\xecnh b\u1ea3o m\u1eadt. L\xe0m \u01a1n t\u1ea3i v\xe0 th\u1eed l\u1ea1i ",screenSharingFirefoxPermissionDeniedError:"C\xf3 g\xec \u0111\xf3 sai khi ch\xfang t\xf4i c\u1ed1 g\u1eafng chia s\u1ebb m\xe0n h\xecnh c\u1ee7a b\u1ea1n. Vui l\xf2ng \u0111\u1ea3m b\u1ea3o b\u1ea1n \u0111\xe3 cho ph\xe9p ch\xfang t\xf4i th\u1ef1c hi\u1ec7n.",screenSharingFirefoxPermissionDeniedTitle:"Ch\xfang t\xf4i kh\xf4ng th\u1ec3 chia s\u1ebb m\xe0n h\xecnh!",screenSharingPermissionDeniedError:"Kh\xf4ng th\u1ec3 truy c\u1eadp micro",serviceUnavailable:"D\u1ecbch v\u1ee5 kh\xf4ng kh\u1ea3 d\u1ee5ng",sessTerminated:"Cu\u1ed9c g\u1ecdi k\u1ebft th\xfac",Share:"Chia s\u1ebb",shareVideoLinkError:"Vui l\xf2ng cung c\u1ea5p li\xean k\u1ebft youtube ch\xednh x\xe1c.",shareVideoTitle:"Chia s\u1ebb video",shareYourScreen:"Chia s\u1ebb m\xe0n h\xecnh c\u1ee7a b\u1ea1n",shareYourScreenDisabled:"Chia s\u1ebb m\xe0n h\xecnh \u0111\xe3 t\u1eaft.",shareYourScreenDisabledForGuest:"Kh\xe1ch kh\xf4ng th\u1ec3 chia s\u1ebb m\xe0n h\xecnh.",startLiveStreaming:"B\u1eaft \u0111\u1ea7u ph\xe1t tr\u1ef1c tuy\u1ebfn",startRecording:"B\u1eaft \u0111\u1ea7u ghi \xe2m",startRemoteControlErrorMessage:"C\xf3 l\u1ed7i khi th\u1eed kh\u1edfi \u0111\u1ed9ng phi\xean \u0111i\u1ec1u khi\u1ec3n t\u1eeb xa",stopLiveStreaming:"D\u1eebng ph\xe1t tr\u1ef1c tuy\u1ebfn",stopRecording:"D\u1eebng ghi \xe2m",stopRecordingWarning:"B\u1ea1n c\xf3 ch\u1eafc ch\u1eafn mu\u1ed1n d\u1eebng ghi \xe2m kh\xf4ng?",stopStreamingWarning:"B\u1ea1n c\xf3 ch\u1eafc ch\u1eafn mu\u1ed1n d\u1eebng ph\xe1t tr\u1ef1c tuy\u1ebfn?",streamKey:"Key ph\xe1t tr\u1ef1c tuy\u1ebfn",Submit:"\u0110\u0103ng k\xfd",thankYou:"C\xe1m \u01a1n b\u1ea1n \u0111\xe3 s\u1eed d\u1ee5ng {{appName}}!",token:"m\xe3 th\xf4ng b\xe1o",tokenAuthFailed:"R\u1ea5t ti\u1ebfc, b\u1ea1n kh\xf4ng \u0111\u01b0\u1ee3c ph\xe9p tham gia cu\u1ed9c g\u1ecdi n\xe0y.",tokenAuthFailedTitle:"X\xe1c th\u1ef1c th\u1ea5t b\u1ea1i",transcribing:"\u0110ang phi\xean \xe2m",unlockRoom:"",userPassword:"m\u1eadt kh\u1ea9u ng\u01b0\u1eddi d\xf9ng",WaitForHostMsg:"Cu\u1ed9c h\u1ecdp {{room}} ch\u01b0a \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o. N\u1ebfu b\u1ea1n l\xe0 ch\u1ee7 ngh\u1ecb vui l\xf2ng x\xe1c th\u1ef1c. N\u1ebfu kh\xf4ng, vui l\xf2ng \u0111\u1ee3i ch\u1ee7 ngh\u1ecb.",WaitForHostMsgWOk:"Cu\u1ed9c h\u1ecdp {{room}} ch\u01b0a \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o. N\u1ebfu b\u1ea1n l\xe0 ch\u1ee7 ngh\u1ecb vui l\xf2ng nh\u1ea5n OK \u0111\u1ec3 x\xe1c th\u1ef1c. N\u1ebfu kh\xf4ng, vui l\xf2ng \u0111\u1ee3i ch\u1ee7 ngh\u1ecb.",WaitingForHost:"\u0110ang \u0111\u1ee3i ch\u1ee7 ngh\u1ecb ...",Yes:"C\xf3",yourEntireScreen:"To\xe0n b\u1ed9 m\xe0n h\xecnh c\u1ee7a b\u1ea1n"},dialOut:{statusMessage:"hi\u1ec7n \u0111ang {{status}}"},feedback:{average:"Trung b\xecnh",bad:"K\xe9m",detailsLabel:"N\xf3i v\u1edbi ch\xfang t\xf4i v\u1ec1 n\xf3.",good:"T\u1ed1t",rateExperience:"Vui l\xf2ng \u0111\xe1nh gi\xe1 tr\u1ea3i nghi\u1ec7m cu\u1ed9c h\u1ecdp c\u1ee7a b\u1ea1n.",veryBad:"R\u1ea5t K\xe9m",veryGood:"R\u1ea5t T\u1ed1t"},incomingCall:{answer:"Tr\u1ea3 l\u1eddi",audioCallTitle:"Cu\u1ed9c g\u1ecdi \u0111\u1ebfn",decline:"H\u1ee7y",productLabel:"t\u1eeb DINTE",videoCallTitle:"Cu\u1ed9c g\u1ecdi h\xecnh"},info:{accessibilityLabel:"Hi\u1ec7n th\xf4ng tin",addPassword:"",cancelPassword:"",conferenceURL:"Li\xean k\u1ebft:",country:"Qu\u1ed1c gia",dialANumber:"\u0110\u1ec3 tham gia cu\u1ed9c h\u1ecdp c\u1ee7a b\u1ea1n, quay m\u1ed9t trong c\xe1c s\u1ed1 sau v\xe0 nh\u1eadp m\xe3.",dialInConferenceID:"M\xe3:",dialInNotSupported:"Xin l\u1ed7i, quay s\u1ed1 kh\xf4ng \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3.",dialInNumber:"Quay s\u1ed1:",dialInSummaryError:"L\u1ed7i n\u1ea1p th\xf4ng tin quay s\u1ed1. Vui l\xf2ng th\u1eed l\u1ea1i.",dialInTollFree:"Mi\u1ec5n ph\xed",genericError:"Ch\xe0, c\xf3 g\xec \u0111\xf3 kh\xf4ng \u1ed5n.",inviteLiveStream:"\u0110\u1ec3 xem ph\xe1t tr\u1ef1c tuy\u1ebfn cu\u1ed9c h\u1ecdp n\xe0y, ch\u1ecdn li\xean k\u1ebft: {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"B\u1ea1n \u0111\u01b0\u1ee3c m\u1eddi tham gia m\u1ed9t cu\u1ed9c h\u1ecdp.",inviteURLFirstPartPersonal:"{{name}} m\u1eddi b\u1ea1n tham gia m\u1ed9t cu\u1ed9c h\u1ecdp.\n",inviteURLSecondPart:"",liveStreamURL:"Ph\xe1t tr\u1ef1c tuy\u1ebfn:",moreNumbers:"Nhi\u1ec1u s\u1ed1 h\u01a1n",noNumbers:"Kh\xf4ng c\xf3 th\xf4ng tin quay s\u1ed1.",noPassword:"Kh\xf4ng",noRoom:"Ch\u01b0a ch\u1ec9 ra ph\xf2ng h\u1ecdp \u0111\u1ec3 quay s\u1ed1 g\u1ecdi.",numbers:"S\u1ed1 \u0111\u1ec3 quay",password:"",title:"Chia s\u1ebb",tooltip:"Chia s\u1ebb li\xean k\u1ebft v\xe0 th\xf4ng tin quay s\u1ed1 c\u1ee7a cu\u1ed9c h\u1ecdp n\xe0y",label:"Th\xf4ng tin cu\u1ed9c h\u1ecdp"},inviteDialog:{alertText:"Kh\xf4ng th\u1ec3 m\u1eddi m\u1ed9t v\xe0i ng\u01b0\u1eddi.",header:"M\u1eddi",searchCallOnlyPlaceholder:"Nh\u1eadp s\u1ed1 \u0111i\u1ec7n tho\u1ea1i",searchPeopleOnlyPlaceholder:"T\xecm ki\u1ebfm ng\u01b0\u1eddi tham gia",searchPlaceholder:"Ng\u01b0\u1eddi tham gia ho\u1eb7c s\u1ed1",send:"G\u1eedi"},inlineDialogFailure:{msg:"Ch\xfang t\xf4i h\u01a1i v\u1ea5p ch\xfat.",retry:"Th\u1eed l\u1ea1i",support:"H\u1ed7 tr\u1ee3",supportMsg:"N\u1ebfu v\u1eabn x\u1ea3y ra, h\xe3y li\xean h\u1ec7 v\u1edbi"},keyboardShortcuts:{focusLocal:"T\u1eadp trung v\xe0o video c\u1ee7a b\u1ea1n",focusRemote:"T\u1eadp trung v\xe0o h\xecnh \u1ea3nh c\u1ee7a ng\u01b0\u1eddi kh\xe1c",fullScreen:"Xem ho\u1eb7c tho\xe1t ch\u1ebf \u0111\u1ed9 to\xe0n m\xe0n h\xecnh",keyboardShortcuts:"Ph\xedm t\u1eaft",localRecording:"Hi\u1ec7n ho\u1eb7c \u1ea9n Ki\u1ec3m so\xe1t ghi h\xecnh c\u1ee5c b\u1ed9",mute:"T\u1eaft ho\u1eb7c b\u1eadt microphone c\u1ee7a b\u1ea1n",pushToTalk:"\u1ea4n chu\xf4ng \u0111\u1ec3 n\xf3i chuy\u1ec7n",raiseHand:"Gi\u01a1 ho\u1eb7c H\u1ea1 tay",showSpeakerStats:"Hi\u1ec3n th\u1ecb th\u1ed1ng k\xea c\u1ee7a di\u1ec5n gi\u1ea3",toggleChat:"M\u1edf ho\u1eb7c \u0110\xf3ng cu\u1ed9c h\u1ed9i tho\u1ea1i",toggleFilmstrip:"Hi\u1ec7n ho\u1eb7c \u1ea9n h\xecnh \u1ea3nh thu nh\u1ecf",toggleScreensharing:"Chuy\u1ec3n \u0111\u1ed5i gi\u1eefa camera v\xe0 chia s\u1ebb m\xe0n h\xecnh",toggleShortcuts:"Hi\u1ec7n ho\u1eb7c \u1ea9n ph\xedm t\u1eaft",videoMute:"B\u1eadt ho\u1eb7c T\u1eaft camera c\u1ee7a b\u1ea1n"},liveStreaming:{busy:"Ch\xfang t\xf4i \u0111ang gi\u1ea3i ph\xf3ng t\xe0i nguy\xean streaming. Xin th\u1eed l\u1ea1i sau v\xe0i ph\xfat.",busyTitle:"C\xe1c thi\u1ebft b\u1ecb streaming \u0111\u1ec1u \u0111ang b\u1eadn.",changeSignIn:"Chuy\u1ec3n t\xe0i kho\u1ea3n.",choose:"Ch\u1ecdn m\u1ed9t k\xeanh ph\xe1t tr\u1ef1c tuy\u1ebfn",chooseCTA:"Ch\u1ecdn m\u1ed9t t\xf9y ch\u1ecdn tr\u1ef1c tuy\u1ebfn. B\u1ea1n \u0111ang \u0111\u0103ng nh\u1eadp theo email {{email}}.",enterStreamKey:"Nh\u1eadp key tr\u1ef1c tuy\u1ebfn Youtube c\u1ee7a b\u1ea1n.",error:"Ph\xe1t tr\u1ef1c tuy\u1ebfn th\u1ea5t b\u1ea1i. Xin vui l\xf2ng th\u1eed l\u1ea1i.",errorAPI:"L\u1ed7i x\u1ea3y ra khi truy c\u1eadp ph\xe1t s\xf3ng Youtube c\u1ee7a b\u1ea1n. Vui l\xf2ng truy c\u1eadp l\u1ea1i.",errorLiveStreamNotEnabled:"Ph\xe1t tr\u1ef1c tuy\u1ebfn kh\xf4ng \u0111\u01b0\u1ee3c b\u1eadt v\u1edbi email {{email}}. Vui l\xf2ng b\u1eadt ph\xe1t tr\u1ef1c tuy\u1ebfn ho\u1eb7c truy c\u1eadp m\u1ed9t t\xe0i kho\u1ea3n \u0111\xe3 b\u1eadt ph\xe1t tr\u1ef1c tuy\u1ebfn.",expandedOff:"Ph\xe1t tr\u1ef1c tuy\u1ebfn \u0111\xe3 d\u1eebng",expandedOn:"Cu\u1ed9c h\u1ecdp \u0111ang \u0111\u01b0\u1ee3c ph\xe1t tr\xean Youtube.",expandedPending:"Ph\xe1t tr\u1ef1c tuy\u1ebfn \u0111ang b\u1eaft \u0111\u1ea7u...",failedToStart:"Kh\xf4ng th\u1ec3 b\u1eaft \u0111\u1ea7u ph\xe1t tr\u1ef1c tuy\u1ebfn",getStreamKeyManually:"Kh\xf4ng th\u1ec3 thu nh\u1eadn ph\xe1t tr\u1ef1c tuy\u1ebfn n\xe0o. Th\u1eed l\u1ea5y m\xe3 ph\xe1t tr\u1ef1c tuy\u1ebfn t\u1eeb Youtube.",invalidStreamKey:"M\xe3 ph\xe1t tr\u1ef1c tuy\u1ebfn c\xf3 th\u1ec3 sai.",off:"Ph\xe1t tr\u1ef1c tuy\u1ebfn \u0111\xe3 d\u1eebng",on:"Ph\xe1t tr\u1ef1c tuy\u1ebfn",pending:"\u0110ang b\u1eaft \u0111\u1ea7u ph\xe1t tr\u1ef1c tuy\u1ebfn...",serviceName:"D\u1ecbch v\u1ee5 Ph\xe1t tr\u1ef1c tuy\u1ebfn",signedInAs:"B\u1ea1n \u0111ang \u0111\u0103ng nh\u1eadp theo:",signIn:"\u0110\u0103ng nh\u1eadp v\u1edbi Google",signInCTA:"\u0110\u0103ng nh\u1eadp ho\u1eb7c nh\u1eadp key ph\xe1t tr\u1ef1c tuy\u1ebfn t\u1eeb Youtube.",signOut:"\u0110\u0103ng xu\u1ea5t",start:"B\u1eaft \u0111\u1ea7u ph\xe1t tr\u1ef1c tuy\u1ebfn",streamIdHelp:"\u0110\xe2y l\xe0 g\xec?",unavailableTitle:"Kh\xf4ng Live Stream \u0111\u01b0\u1ee3c"},localRecording:{clientState:{off:"T\u1eaft",on:"B\u1eadt",unknown:"Kh\xf4ng r\xf5"},dialogTitle:"Ki\u1ec3m so\xe1t ghi h\xecnh c\u1ee5c b\u1ed9",duration:"Th\u1eddi l\u01b0\u1ee3ng",durationNA:"Kh\xf4ng",encoding:"M\xe3 h\xf3a",label:"Tr\u1eddi \u01a1i",labelToolTip:"Ghi h\xecnh c\u1ee5c b\u1ed9 \u0111ang b\u1eadn",localRecording:"Ghi h\xecnh c\u1ee5c b\u1ed9",me:"T\xf4i",messages:{engaged:"Ghi h\xecnh c\u1ee5c b\u1ed9 \u0111\xe3 b\u1eadn.",finished:"Phi\xean ghi h\xecnh {{token}} \u0111\xe3 k\u1ebft th\xfac. Vui l\xf2ng g\u1eedi t\u1ec7p ghi h\xecnh cho ng\u01b0\u1eddi \u0111i\u1ec1u h\xe0nh.",finishedModerator:"Token phi\xean ghi h\xecnh {{token}} \u0111\xe3 k\u1ebft th\xfac. Ghi h\xecnh c\u1ee5c b\u1ed9 \u0111\xe3 \u0111\u01b0\u1ee3c l\u01b0u. Vui l\xf2ng h\u1ecfi nh\u1eefng ng\u01b0\u1eddi tham gia kh\xe1c \u0111\u1ec3 cung c\u1ea5p ghi h\xecnh c\u1ee7a h\u1ecd.",notModerator:"B\u1ea1n kh\xf4ng ph\u1ea3i ng\u01b0\u1eddi \u0111i\u1ec1u h\xe0nh. B\u1ea1n kh\xf4ng th\u1ec3 kh\u1edfi t\u1ea1o ho\u1eb7c d\u1eebng ghi h\xecnh."},moderator:"Qu\u1ea3n tr\u1ecb vi\xean",no:"Kh\xf4ng",participant:"Ng\u01b0\u1eddi tham gia",participantStats:"Tr\u1ea1ng th\xe1i ng\u01b0\u1eddi tham gia",sessionToken:"M\xe3 phi\xean",start:"B\u1eaft \u0111\u1ea7u ghi \xe2m",stop:"D\u1eebng ghi \xe2m",yes:"C\xf3"},lockRoomPassword:"M\u1eadt kh\u1ea9u",lockRoomPasswordUppercase:"M\u1eadt kh\u1ea9u",me:"T\xf4i",notify:{connectedOneMember:"{{name}} \u0111\xe3 tham gia cu\u1ed9c h\u1ecdp",connectedThreePlusMembers:"{{name}} v\xe0{{count}} kh\xe1c \u0111\xe3 tham gia cu\u1ed9c h\u1ecdp",connectedTwoMembers:"{{first}} v\xe0{{second}} \u0111\xe3 tham gia cu\u1ed9c h\u1ecdp",disconnected:"\u0111\xe3 ng\u1eaft k\u1ebft n\u1ed1i",focus:"H\u1ed9i ngh\u1ecb t\u1eadp trung",focusFail:"{{component}} kh\xf4ng kh\u1ea3 d\u1ee5ng - th\u1eed l\u1ea1i trong {{ms}} gi\xe2y",grantedTo:"Quy\u1ec1n c\u1ee7a ng\u01b0\u1eddi \u0111i\u1ec1u h\xe0nh \u0111\xe3 \u0111\u01b0\u1ee3c c\u1ea5p cho {{to}}!",invitedOneMember:"{{name}} \u0111\xe3 \u0111\u01b0\u1ee3c m\u1eddi",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"T\xf4i",moderator:"Quy\u1ec1n c\u1ee7a ng\u01b0\u1eddi \u0111i\u1ec1u h\xe0nh \u0111\xe3 \u0111\u01b0\u1ee3c c\u1ea5p!",muted:"B\u1ea1n \u0111\xe3 b\u1eaft \u0111\u1ea7u cu\u1ed9c tr\xf2 chuy\u1ec7n b\u1ecb t\u1eaft ti\u1ebfng.",mutedTitle:"B\u1ea1n b\u1ecb t\u1eaft ti\u1ebfng!",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} mu\u1ed1n ph\xe1t bi\u1ec3u.",somebody:"Ai \u0111\xf3",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"Ch\xfang t\xf4i lo r\u1eb1ng tr\u1ea3i nghi\u1ec7m c\u1ee7a b\u1ea1n v\u1edbi {{appName}} \u0111ang kh\xf4ng t\u1ed1t. Ch\xfang t\xf4i \u0111ang t\xecm c\xe1ch c\u1ea3i thi\u1ec7n, hi\u1ec7n t\u1ea1i th\u1eed m\u1ed9t trong c\xe1c tr\xecnh duy\u1ec7t \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3.",suboptimalExperienceTitle:"C\u1ea3nh b\xe1o tr\xecnh duy\u1ec7t",unmute:"",newDeviceCameraTitle:"Camera m\u1edbi \u0111\u01b0\u1ee3c ph\xe1t hi\u1ec7n",newDeviceAudioTitle:"Thi\u1ebft b\u1ecb \xe2m thanh m\u1edbi \u0111\u01b0\u1ee3c ph\xe1t hi\u1ec7n",newDeviceAction:"S\u1eed d\u1ee5ng"},passwordSetRemotely:"\u0111\u01b0\u1ee3c thi\u1ebft l\u1eadp b\u1edfi m\u1ed9t ng\u01b0\u1eddi kh\xe1c",passwordDigitsOnly:"",poweredby:"\u0110\u01b0\u1ee3c h\u1ed7 tr\u1ee3 b\u1edfi",presenceStatus:{busy:"B\u1eadn",calling:"\u0110ang g\u1ecdi...",connected:"\u0110\xe3 k\u1ebft n\u1ed1i",connecting:"\u0110ang k\u1ebft n\u1ed1i...",connecting2:"\u0110ang k\u1ebft n\u1ed1i*...",disconnected:"\u0110\xe3 ng\u1eaft k\u1ebft n\u1ed1i",expired:"H\u1ebft h\u1ea1n",ignored:"\u0110\xe3 b\u1ecf qua",initializingCall:"\u0110ang t\u1ea1o cu\u1ed9c g\u1ecdi...",invited:"\u0110\xe3 m\u1eddi",rejected:"\u0110\xe3 t\u1eeb ch\u1ed1i",ringing:"\u0110ang \u0111\u1ed5 chu\xf4ng..."},profile:{setDisplayNameLabel:"Nh\u1eadp t\xean hi\u1ec3n th\u1ecb c\u1ee7a b\u1ea1n",setEmailInput:"Nh\u1eadp \u0111\u1ecba ch\u1ec9 e-mail",setEmailLabel:"Nh\u1eadp \u0111\u1ecba ch\u1ec9 gravatar email c\u1ee7a b\u1ea1n",title:"H\u1ed3 s\u01a1"},recording:{authDropboxText:"T\u1ea3i l\xean Dropbox",availableSpace:"Dung l\u01b0\u1ee3ng c\xf2n: {{spaceLeft}} MB (kho\u1ea3ng {{duration}} ph\xfat ghi h\xecnh)",beta:"B\u1ea3n th\u1eed nghi\u1ec7m",busy:"Ch\u01b0\u01a1ng tr\xecnh \u0111ang b\u1eadn gi\u1ea3i ph\xf3ng t\xe0i nguy\xean thu h\xecnh. Xin th\u1eed l\u1ea1i sau v\xe0i ph\xfat.",busyTitle:"T\u1ea5t c\u1ea3 c\xe1c \u0111\u1ea7u ghi h\xecnh hi\u1ec7n \u0111ang b\u1eadn.",error:"Ghi \xe2m kh\xf4ng th\xe0nh c\xf4ng. Vui l\xf2ng th\u1eed l\u1ea1i.",expandedOff:"Ghi h\xecnh \u0111\xe3 d\u1eebng",expandedOn:"Cu\u1ed9c h\u1ecdp \u0111ang \u0111\u01b0\u1ee3c ghi h\xecnh.",expandedPending:"Ghi h\xecnh \u0111ang kh\u1edfi \u0111\u1ed9ng...",failedToStart:"Kh\u1edfi \u0111\u1ed9ng ghi \xe2m th\u1ea5t b\u1ea1i",fileSharingdescription:"Chia s\u1ebb ghi h\xecnh v\u1edbi ng\u01b0\u1eddi tham gia h\u1ecdp",live:"Tr\u1ef1c tuy\u1ebfn",loggedIn:"\u0110\xe3 \u0111\u0103ng nh\u1eadp d\u01b0\u1edbi t\xean {{userName}}",off:"\u0110\xe3 ng\u1eebng ghi \xe2m",on:"\u0110ang ghi \xe2m",pending:"\u0110ang chu\u1ea9n b\u1ecb \u0111\u1ec3 ghi h\xecnh cu\u1ed9c h\u1ecdp...",rec:"REC",serviceDescription:"Ghi h\xecnh c\u1ee7a b\u1ea1n s\u1ebd \u0111\u01b0\u1ee3c l\u01b0u b\u1edfi d\u1ecbch v\u1ee5 ghi h\xecnh",serviceName:"D\u1ecbch v\u1ee5 ghi h\xecnh",signIn:"\u0110\u0103ng nh\u1eadp",signOut:"\u0110\u0103ng xu\u1ea5t",unavailable:"R\u1ea5t ti\u1ebfc! D\u1ecbch v\u1ee5 {{serviceName}} \u0111ang kh\xf4ng s\u1eb5n s\xe0ng. Ch\xfang t\xf4i \u0111ang x\u1eed l\xfd v\u1ea5n \u0111\u1ec1 n\xe0y. Vui l\xf2ng th\u1eed l\u1ea1i sau.",unavailableTitle:"Ghi h\xecnh kh\xf4ng ho\u1ea1t \u0111\u1ed9ng."},sectionList:{pullToRefresh:"K\xe9o \u0111\u1ec3 l\xe0m t\u01b0\u01a1i"},settings:{calendar:{about:"{{appName}} t\xedch h\u1ee3p l\u1ecbch \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 truy c\u1eadp b\u1ea3o m\u1eadt l\u1ecbch \u0111\u1ec3 l\u1ea5y th\xf4ng tin s\u1ef1 ki\u1ec7n s\u1eafp t\u1edbi.",disconnect:"Ng\u1eaft k\u1ebft n\u1ed1i",microsoftSignIn:"\u0110\u0103ng nh\u1eadp v\u1edbi Microsoft",signedIn:"\u0110ang truy c\u1eadp l\u1ecbch s\u1ef1 ki\u1ec7n c\u1ee7a {{email}}. Ch\u1ecdn Ng\u1eaft k\u1ebft n\u1ed1i \u0111\u1ec3 d\u1eebng truy c\u1eadp l\u1ecbch s\u1ef1 ki\u1ec7n.",title:"L\u1ecbch"},devices:"Thi\u1ebft b\u1ecb",followMe:"T\u1ea5t c\u1ea3 m\u1ecdi ng\u01b0\u1eddi theo d\xf5i t\xf4i",language:"Ng\xf4n ng\u1eef",loggedIn:"\u0110\xe3 \u0111\u0103ng nh\u1eadp d\u01b0\u1edbi t\xean {{name}}",moderator:"Qu\u1ea3n tr\u1ecb vi\xean",more:"Th\xeam",name:"T\xean",noDevice:"Kh\xf4ng",selectAudioOutput:"\u0110\u1ea7u ra \xe2m thanh",selectCamera:"Camera",selectMic:"Microphone",startAudioMuted:"M\u1ecdi ng\u01b0\u1eddi b\u1eaft \u0111\u1ea7u \u0111\u1ec1u b\u1ecb t\u1eaft ti\u1ebfng",startVideoMuted:"M\u1ecdi ng\u01b0\u1eddi b\u1eaft \u0111\u1ea7u \u0111\u1ec1u b\u1ecb \u1ea9n",title:"C\xe0i \u0111\u1eb7t"},settingsView:{alertOk:"OK",alertTitle:"C\u1ea3nh b\xe1o",alertURLText:"URL m\xe1y ch\u1ee7 \u0111\xe3 nh\u1eadp kh\xf4ng h\u1ee3p l\u1ec7",buildInfoSection:"Th\xf4ng tin phi\xean b\u1ea3n",conferenceSection:"H\u1ed9i ngh\u1ecb",displayName:"T\xean hi\u1ec3n th\u1ecb",email:"Email",header:"C\xe0i \u0111\u1eb7t",profileSection:"H\u1ed3 s\u01a1",serverURL:"URL m\xe1y ch\u1ee7",startWithAudioMuted:"B\u1eaft \u0111\u1ea7u m\xe0 kh\xf4ng thu ti\u1ebfng",startWithVideoMuted:"B\u1eaft \u0111\u1ea7u kh\xf4ng thu h\xecnh",version:"Phi\xean b\u1ea3n"},share:{dialInfoText:"",mainText:"Ch\u1ecdn li\xean k\u1ebft d\u01b0\u1edbi \u0111\u1ec3 tham gia h\u1ecdp:\n{{roomUrl}}"},speaker:"Di\u1ec5n gi\u1ea3",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"T\xean",seconds:"{{count}}s",speakerStats:"Th\u1ed1ng k\xea v\u1ec1 di\u1ec5n gi\u1ea3",speakerTime:"Th\u1eddi gian c\u1ee7a di\u1ec5n gi\u1ea3"},startupoverlay:{policyText:" ",title:"{{app}} c\u1ea7n s\u1eed d\u1ee5ng microphone v\xe0 camera c\u1ee7a b\u1ea1n."},suspendedoverlay:{rejoinKeyTitle:"Tham gia l\u1ea1i",text:"B\u1ea5m n\xfat Rejoin \u0111\u1ec3 k\u1ebft n\u1ed1i l\u1ea1i.",title:"Cu\u1ed9c g\u1ecdi h\xecnh c\u1ee7a b\u1ea1n b\u1ecb gi\xe1n \u0111o\u1ea1n v\xec m\xe1y t\xednh n\xe0y chuy\u1ec3n sang tr\u1ea1ng th\xe1i ng\u1ee7."},toolbar:{accessibilityLabel:{audioOnly:"Chuy\u1ec3n sang ch\u1ec9 ti\u1ebfng",audioRoute:"Ch\u1ecdn thi\u1ebft b\u1ecb \xe2m thanh",callQuality:"",cc:"M\u1edf/\u0110\xf3ng ph\u1ee5 \u0111\u1ec1",chat:"M\u1edf/\u0110\xf3ng c\u1eeda s\u1ed5 Chat",document:"M\u1edf/\u0110\xf3ng t\xe0i li\u1ec7u \u0111\u01b0\u1ee3c chia s\u1ebb",feedback:"\u0110\u1ec3 l\u1ea1i ph\u1ea3n h\u1ed3i",fullScreen:"M\u1edf/\u0110\xf3ng to\xe0n m\xe0n h\xecnh",hangup:"R\u1eddi cu\u1ed9c g\u1ecdi",invite:"M\u1eddi ng\u01b0\u1eddi tham gia",kick:"\u0110\u1ea9y ng\u01b0\u1eddi tham gia ra",localRecording:"M\u1edf/\u0110\xf3ng \u0111i\u1ec1u khi\u1ec3n ghi h\xecnh c\u1ee5c b\u1ed9",lockRoom:"M\u1edf/\u0110\xf3ng m\u1eadt kh\u1ea9u ph\xf2ng h\u1ecdp",moreActions:"M\u1edf/\u0110\xf3ng Th\xeam h\xe0nh \u0111\u1ed9ng",moreActionsMenu:"Menu Th\xeam h\xe0nh \u0111\u1ed9ng",mute:"M\u1edf/\u0110\xf3ng T\u1eaft ti\u1ebfng",pip:"M\u1edf/\u0110\xf3ng ch\u1ebf \u0111\u1ed9 H\xecnh-trong-H\xecnh",profile:"Ch\u1ec9nh s\u1eeda h\u1ed3 s\u01a1 c\xe1 nh\xe2n",raiseHand:"M\u1edf/\u0110\xf3ng Gi\u01a1 tay",recording:"M\u1edf/\u0110\xf3ng Ghi h\xecnh",remoteMute:"T\u1eaft ti\u1ebfng ng\u01b0\u1eddi tham gia",Settings:"M\u1edf/\u0110\xf3ng C\u1ea5u h\xecnh",sharedvideo:"M\u1edf/\u0110\xf3ng Chia s\u1ebb Youtube",shareRoom:"M\u1eddi ai \u0111\xf3",shareYourScreen:"M\u1edf/\u0110\xf3ng Chia s\u1ebb m\xe0n h\xecnh",shortcuts:"M\u1edf/\u0110\xf3ng Ph\xedm t\u1eaft",show:"",speakerStats:"M\u1edf/\u0110\xf3ng Th\u1ed1ng k\xea",tileView:"M\u1edf/\u0110\xf3ng Xem d\u1ea1ng l\u01b0\u1edbi",toggleCamera:"M\u1edf/\u0110\xf3ng Camera",videomute:"M\u1edf/\u0110\xf3ng Ti\u1ebfng, H\xecnh",videoblur:""},addPeople:"Th\xeam ng\u01b0\u1eddi v\xe0o cu\u1ed9c g\u1ecdi",audioOnlyOff:"Ch\u1ebf \u0111\u1ed9 ch\u1ec9 t\u1eaft ti\u1ebfng",audioOnlyOn:"B\u1eadt ch\u1ebf \u0111\u1ed9 Ch\u1ec9 \xe2m thanh",audioRoute:"Ch\u1ecdn thi\u1ebft b\u1ecb \xe2m thanh",authenticate:"X\xe1c th\u1ef1c",callQuality:"Ch\u1ec9nh ch\u1ea5t l\u01b0\u1ee3ng",chat:"M\u1edf / \u0110\xf3ng cu\u1ed9c h\u1ed9i tho\u1ea1i",closeChat:"\u0110\xf3ng Chat",documentClose:"\u0110\xf3ng t\xe0i li\u1ec7u \u0111\u01b0\u1ee3c chia s\u1ebb",documentOpen:"M\u1edf t\xe0i li\u1ec7u \u0111\u01b0\u1ee3c chia s\u1ebb",enterFullScreen:"Xem to\xe0n m\xe0n h\xecnh",enterTileView:"Xem ch\u1ebf \u0111\u1ed9 l\u01b0\u1edbi",exitFullScreen:"Tho\xe1t to\xe0n m\xe0n h\xecnh",exitTileView:"Tho\xe1t xem d\u1ea1ng l\u01b0\u1edbi",feedback:"\u0110\u1ec3 l\u1ea1i ph\u1ea3n h\u1ed3i",hangup:"Tho\xe1t",invite:"M\u1eddi ng\u01b0\u1eddi tham gia",login:"\u0110\u0103ng nh\u1eadp",logout:"\u0110\u0103ng xu\u1ea5t",lowerYourHand:"H\u1ea1 tay",moreActions:"Th\xeam h\xe0nh \u0111\u1ed9ng",mute:"T\u1eaft ti\u1ebfng / B\u1eadt ti\u1ebfng",openChat:"M\u1edf Chat",pip:"V\xe0o ch\u1ebf \u0111\u1ed9 \u1ea2nh-trong-\u1ea2nh",profile:"Ch\u1ec9nh s\u1eeda h\u1ed3 s\u01a1 c\xe1 nh\xe2n",raiseHand:"Gi\u01a1 / H\u1ea1 tay",raiseYourHand:"Gi\u01a1 tay",Settings:"C\xe0i \u0111\u1eb7t",sharedvideo:"Chia s\u1ebb YouTube video",shareRoom:"M\u1eddi ai \u0111\xf3",shortcuts:"Xem ph\xedm t\u1eaft",speakerStats:"Th\u1ed1ng k\xea v\u1ec1 di\u1ec5n gi\u1ea3",startScreenSharing:"B\u1eaft \u0111\u1ea7u chia s\u1ebb m\xe0n h\xecnh",startSubtitles:"B\u1eaft \u0111\u1ea7u ph\u1ee5 \u0111\u1ec1",stopScreenSharing:"D\u1eebng chia s\u1ebb m\xe0n h\xecnh",stopSubtitles:"D\u1eebng ph\u1ee5 \u0111\u1ec1",stopSharedVideo:"D\u1eebng Youtube",talkWhileMutedPopup:"C\u1ed1 g\u1eafng \u0111\u1ec3 n\xf3i chuy\u1ec7n? B\u1ea1n \u0111ang t\u1eaft ti\u1ebfng.",tileViewToggle:"M\u1edf/\u0110\xf3ng Xem d\u1ea1ng l\u01b0\u1edbi",toggleCamera:"M\u1edf/\u0110\xf3ng Camera",videomute:"B\u1eadt / T\u1eaft camera",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"Ch\u1ea1y/D\u1eebng ph\u1ee5 \u0111\u1ec1",error:"Phi\xean \xe2m kh\xf4ng th\xe0nh c\xf4ng. Vui l\xf2ng th\u1eed l\u1ea1i.",expandedLabel:"Phi\xean \xe2m \u0111ang b\u1eadt",failedToStart:"Kh\u1edfi ch\u1ea1y phi\xean \xe2m th\u1ea5t b\u1ea1i",labelToolTip:"Cu\u1ed9c h\u1ecdp \u0111ang \u0111\u01b0\u1ee3c phi\xean \xe2m",off:"Phi\xean \xe2m \u0111\xe3 d\u1eebng",pending:"\u0110ang chu\u1ea9n b\u1ecb phi\xean \xe2m cu\u1ed9c h\u1ecdp...",start:"B\u1eaft \u0111\u1ea7u hi\u1ec3n th\u1ecb ph\u1ee5 \u0111\u1ec1",stop:"D\u1eebng hi\u1ec3n th\u1ecb ph\u1ee5 \u0111\u1ec1",tr:"TR"},userMedia:{androidGrantPermissions:"Ch\u1ecdn Cho ph\xe9p khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",chromeGrantPermissions:"Ch\u1ecdn Cho ph\xe9p khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",edgeGrantPermissions:"Ch\u1ecdn C\xf3 khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",electronGrantPermissions:"Vui l\xf2ng c\u1ea5p quy\u1ec1n s\u1eed d\u1ee5ng camera v\xe0 microphone c\u1ee7a b\u1ea1n",firefoxGrantPermissions:"Ch\u1ecdn Chia s\u1ebb thi\u1ebft b\u1ecb \u0111\xe3 ch\u1ecdn khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",iexplorerGrantPermissions:"Ch\u1ecdn C\xf3 khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",nwjsGrantPermissions:"Vui l\xf2ng c\u1ea5p quy\u1ec1n s\u1eed d\u1ee5ng camera v\xe0 microphone c\u1ee7a b\u1ea1n",operaGrantPermissions:"Ch\u1ecdn Cho ph\xe9p khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.","react-nativeGrantPermissions":"Ch\u1ecdn Cho ph\xe9p khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p.",safariGrantPermissions:"Ch\u1ecdn C\xf3 khi tr\xecnh duy\u1ec7t c\u1ee7a b\u1ea1n y\xeau c\u1ea7u c\u1ea5p ph\xe9p."},videoSIPGW:{busy:"Ch\xfang t\xf4i \u0111ang gi\u1ea3i ph\xf3ng t\xe0i nguy\xean. Vui l\xf2ng th\u1eed l\u1ea1i sau v\xe0i ph\xfat.",busyTitle:"D\u1ecbch v\u1ee5 Ph\xf2ng h\u1ecdp \u0111ang b\u1eadn",errorAlreadyInvited:"{{displayName}} \u0111\xe3 \u0111\u01b0\u1ee3c m\u1eddi",errorInvite:"Cu\u1ed9c h\u1ecdp ch\u01b0a \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o. Vui l\xf2ng th\u1eed l\u1ea1i.",errorInviteFailed:"Ch\xfang t\xf4i \u0111ang x\u1eed l\xfd v\u1ea5n \u0111\u1ec1. Vui l\xf2ng th\u1eed l\u1ea1i sau.",errorInviteFailedTitle:"M\u1eddi {{displayName}} th\u1ea5t b\u1ea1i",errorInviteTitle:"L\u1ed7i m\u1eddi h\u1ecdp",pending:"{{displayName}} \u0111\xe3 \u0111\u01b0\u1ee3c m\u1eddi"},videoStatus:{audioOnly:"AUD",audioOnlyExpanded:"B\u1ea1n \u0111ang \u1edf ch\u1ebf \u0111\u1ed9 ch\u1ec9 ti\u1ebfng. Ch\u1ebf \u0111\u1ed9 n\xe0y gi\u1ea3m b\u0103ng th\xf4ng nh\u01b0ng kh\xf4ng th\u1ea5y h\xecnh \u1ea3nh ng\u01b0\u1eddi kh\xe1c.",callQuality:"",hd:"HD",highDefinition:"HD",labelTooiltipNoVideo:"Kh\xf4ng h\xecnh \u1ea3nh",labelTooltipAudioOnly:"Ch\u1ebf \u0111\u1ed9 ch\u1ec9 ti\u1ebfng \u0111\xe3 b\u1eadt",ld:"LD",lowDefinition:"Ph\xe2n gi\u1ea3i th\u1ea5p",onlyAudioAvailable:"Ch\u1ec9 c\xf3 \xe2m thanh s\u1eb5n s\xe0ng",onlyAudioSupported:"Ch\u1ec9 h\u1ed7 tr\u1ee3 \xe2m thanh tr\xean tr\xecnh duy\u1ec7t n\xe0y.",p2pEnabled:"Peer to Peer \u0111\xe3 b\u1eadt",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\u01afu ti\xean ph\xe2n gi\u1ea3i HD.",sd:"SD",standardDefinition:"Ph\xe2n gi\u1ea3i SD"},videothumbnail:{domute:"T\u1eaft ti\u1ebfng",flip:"L\u1eadt",kick:"\u0110\u1ea9y ra",moderator:"Qu\u1ea3n tr\u1ecb vi\xean",mute:"Ng\u01b0\u1eddi tham gia b\u1ecb t\u1eaft ti\u1ebfng",muted:"\u0110\xe3 t\u1eaft ti\u1ebfng",remoteControl:"\u0110i\u1ec1u khi\u1ec3n t\u1eeb xa",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"Ch\u1ea1m \u0111\u1ec3 tham gia",roomname:"Nh\u1eadp t\xean ph\xf2ng"},appDescription:"Ti\u1ebfp t\u1ee5c, chat h\xecnh v\u1edbi to\xe0n b\u1ed9 nh\xf3m. Th\u1ef1c t\u1ebf, m\u1eddi ng\u01b0\u1eddi b\u1ea1n bi\u1ebft. {{app}} \u0111\u01b0\u1ee3c m\xe3 h\xf3a, 100% gi\u1ea3i ph\xe1p h\u1ed9i ngh\u1ecb m\xe3 m\u1edf m\xe0 b\u1ea1n c\xf3 th\u1ec3 s\u1eed d\u1ee5ng h\xe0ng ng\xe0y, mi\u1ec5n ph\xed.",audioVideoSwitch:{audio:"Ti\u1ebfng",video:"H\xecnh \u1ea3nh"},calendar:"L\u1ecbch",connectCalendarButton:"K\u1ebft n\u1ed1i L\u1ecbch c\u1ee7a b\u1ea1n",connectCalendarText:"K\u1ebft n\u1ed1i l\u1ecbch c\u1ee7a b\u1ea1n \u0111\u1ec3 xem t\u1ea5t c\u1ea3 c\xe1c cu\u1ed9c h\u1ecdp {{app}}. Th\xeam, th\xeam cu\u1ed9c h\u1ecdp {{provider}} v\xe0o l\u1ecbch c\u1ee7a b\u1ea1n v\xe0 b\u1eaft \u0111\u1ea7u.",enterRoomTitle:"B\u1eaft \u0111\u1ea7u cu\u1ed9c h\u1ecdp m\u1edbi",go:"\u0110I",join:"THAM GIA",info:"Th\xf4ng tin",privacy:"B\u1ea3o m\u1eadt",recentList:"Hi\u1ec7n t\u1ea1i",recentListDelete:"X\xf3a",recentListEmpty:"Danh s\xe1ch cu\u1ed9c h\u1ecdp r\u1ed7ng. Th\u1ef1c hi\u1ec7n cu\u1ed9c h\u1ecdp v\xe0 b\u1ea1n s\u1ebd th\u1ea5y danh s\xe1ch hi\u1ec7n t\u1ea1i \u0111\xe2y.",reducedUIText:"",roomname:"Nh\u1eadp t\xean ph\xf2ng",roomnameHint:"Th\xeam t\xean ho\u1eb7c URL c\u1ee7a ph\xf2ng h\u1ecdp b\u1ea1n mu\u1ed1n tham gia. Ban c\xf3 th\u1ec3 t\u1ea1o t\xean ph\xf2ng, g\u1eedi cho ng\u01b0\u1eddi b\u1ea1n mu\u1ed1n m\u1eddi \u0111\u1ec3 h\u1ecd s\u1eed d\u1ee5ng t\xean \u0111\xf3.",sendFeedback:"G\u1eedi g\xf3p \xfd",terms:"\u0110i\u1ec1u ki\u1ec7n",title:"B\u1ea3o m\u1eadt, \u0111\u1ea7y \u0111\u1ee7 t\xednh n\u0103ng v\xe0 mi\u1ec5n ph\xed ho\xe0n to\xe0n"}}},680,[]); -__d(function(e,s,o,t,n,r,a){n.exports={en:"\u82f1\u8bed",af:"\u5357\u975e\u8377\u5170\u8bed",az:"\u963f\u585e\u62dc\u7586\u8bed",bg:"\u4fdd\u52a0\u5229\u4e9a\u8bed",cs:"\u6377\u514b\u8bed",de:"\u5fb7\u8bed",el:"\u5e0c\u814a\u8bed",eo:"\u4e16\u754c\u8bed",es:"\u897f\u73ed\u7259\u8bed",fr:"\u6cd5\u8bed",hy:"\u4e9a\u7f8e\u5c3c\u4e9a\u8bed",it:"\u610f\u5927\u5229\u8bed",ja:"\u65e5\u8bed",ko:"\u97e9\u8bed",nb:"\u632a\u5a01\u5e03\u514b\u6469\u5c14\u8bed",oc:"\u6b27\u897f\u5766\u8bed",pl:"\u6ce2\u5170\u8bed",ptBR:"\u8461\u8404\u7259\u8bed\uff08\u5df4\u897f\uff09",ru:"\u4fc4\u8bed",sk:"\u65af\u6d1b\u4f10\u514b\u8bed",sl:"\u65af\u6d1b\u6587\u5c3c\u4e9a\u8bed",sv:"\u745e\u5178\u8bed",tr:"\u571f\u8033\u5176\u8bed",vi:"\u8d8a\u5357\u8bed",zhCN:"\u4e2d\u6587(\u4e2d\u56fd)"}},681,[]); -__d(function(e,o,i,t,r,n,a){r.exports={addPeople:{add:"\u9080\u8bf7",countryNotSupported:"\u76ee\u7684\u56fd\u5bb6\u6682\u65f6\u672a\u88ab\u652f\u6301\u3002",countryReminder:"\u5c1d\u8bd5\u5728\u7f8e\u56fd\u4e4b\u5916\u901a\u8bdd\uff1f\u8bf7\u68c0\u67e5\u56fd\u5bb6\u4ee3\u7801\uff01",disabled:"\u60a8\u4e0d\u80fd\u9080\u8bf7\u6210\u5458",failedToAdd:"",footerText:"\u7981\u6b62\u62e8\u53f7\u3002",loading:"\u67e5\u627e\u8054\u7cfb\u4eba\u6216\u8005\u7535\u8bdd\u53f7\u7801",loadingNumber:"\u9a8c\u8bc1\u7535\u8bdd\u53f7\u7801",loadingPeople:"\u6b63\u5728\u641c\u7d22\u9700\u8981\u9080\u8bf7\u7684\u6210\u5458",noResults:"\u6ca1\u6709\u7b26\u5408\u8981\u6c42\u7684\u641c\u7d22\u7ed3\u679c",noValidNumbers:"\u8bf7\u8f93\u5165\u4e00\u4e2a\u7535\u8bdd\u53f7\u7801",searchNumbers:"\u65b0\u589e\u7535\u8bdd\u53f7\u7801",searchPeople:"\u641c\u7d22\u6210\u5458",searchPeopleAndNumbers:"\u641c\u7d22\u6210\u5458\u6216\u6dfb\u52a0\u5176\u7535\u8bdd\u53f7\u7801",telephone:"\u7535\u8bdd\u53f7\u7801\uff1a {{number}}",title:"\u9080\u8bf7\u6210\u5458\u4e0e\u4f1a"},audioDevices:{bluetooth:"\u84dd\u7259",headphones:"\u8033\u673a",phone:"\u7535\u8bdd",speaker:"\u53d1\u8a00\u4eba"},audioOnly:{audioOnly:"\u53ea\u6709\u97f3\u9891"},calendarSync:{addMeetingURL:"\u6dfb\u52a0\u4f1a\u8bae\u94fe\u63a5",confirmAddLink:"\u662f\u5426\u5728\u6b64\u4e8b\u4ef6\u4e2d\u6dfb\u52a0Jitsi\u94fe\u63a5",error:{appConfiguration:"\u65e5\u5386\u96c6\u6210\u914d\u7f6e\u4e0d\u6b63\u786e\u3002",generic:"\u53d1\u751f\u9519\u8bef\u3002\u8bf7\u68c0\u67e5\u65e5\u5386\u8bbe\u7f6e\u6216\u5c1d\u8bd5\u5237\u65b0\u65e5\u5386\u3002",notSignedIn:"\u67e5\u770b\u65e5\u5386\u4e8b\u4ef6\u9a8c\u8bc1\u51fa\u9519\u3002\u8bf7\u68c0\u67e5\u65e5\u5386\u8bbe\u7f6e\uff0c\u7136\u540e\u518d\u6b21\u5c1d\u8bd5\u767b\u5f55\u3002"},join:"\u52a0\u5165",joinTooltip:"\u52a0\u5165\u4f1a\u8bae",nextMeeting:"\u8fd1\u671f\u65e0\u6d3b\u52a8\u3002",noEvents:"\u8fd1\u671f\u65e0\u6d3b\u52a8\u3002",ongoingMeeting:"\u6b63\u5728\u8fdb\u884c\u7684\u4f1a\u8bae",permissionButton:"\u6253\u5f00\u8bbe\u7f6e",permissionMessage:"\u5728\u5e94\u7528\u4e2d\u67e5\u770b\u4f1a\u8bae\u9700\u8981\u65e5\u5386\u6743\u9650\u3002",refresh:"\u5237\u65b0\u65e5\u5386",today:"\u4eca\u65e5"},chat:{error:"\u9519\u8bef\uff1a\u4f60\u7684\u6d88\u606f \"{{originalText}}\" \u672a\u88ab\u53d1\u9001\u3002\u539f\u56e0\uff1a {{error}}",messagebox:"\u8f93\u5165\u6d88\u606f",nickname:{popover:"\u9009\u62e9\u4e00\u4e2a\u6635\u79f0",title:"\u8f93\u5165\u4e00\u4e2a\u6635\u79f0\u7528\u4e8e\u804a\u5929"},title:"\u804a\u5929"},connectingOverlay:{joiningRoom:"\u4f1a\u8bae\u8fde\u63a5\u4e2d..."},connection:{ATTACHED:"\u5df2\u63a5\u5165",AUTHENTICATING:"\u8ba4\u8bc1\u4e2d",AUTHFAIL:"\u8ba4\u8bc1\u5931\u8d25",CONNECTED:"\u8fde\u63a5\u4e2d...",CONNECTING:"\u8fde\u63a5\u4e2d",CONNFAIL:"\u8fde\u63a5\u5931\u8d25",DISCONNECTED:"\u5df2\u65ad\u5f00\u8fde\u63a5",DISCONNECTING:"\u65ad\u5f00\u8fde\u63a5\u4e2d",ERROR:"\u9519\u8bef",RECONNECTING:"\u7f51\u7edc\u9519\u8bef\uff0c\u91cd\u8fde\u4e2d\u3002\u3002\u3002"},connectionindicator:{address:"\u5730\u5740\uff1a",bandwidth:"\u4f30\u8ba1\u5e26\u5bbd\uff1a",bitrate:"\u6bd4\u7279\u7387\uff1a",bridgeCount:"\u670d\u52a1\u5668\u6570\u91cf:",connectedTo:"\u8fde\u63a5\u5230:",framerate:"\u5e27\u7387\uff1a",less:"\u663e\u793a\u66f4\u5c11",localaddress:"\u672c\u5730\u5730\u5740\uff1a",localport:"\u672c\u5730\u7aef\u53e3\uff1a",more:"\u663e\u793a\u66f4\u591a",packetloss:"\u4e22\u5305\uff1a",quality:{good:"\u597d",inactive:"\u672a\u6fc0\u6d3b",lost:"\u6389\u7ebf",nonoptimal:"\u4e2d",poor:"\u5dee"},remoteaddress:"\u8fdc\u7a0b\u5730\u5740\uff1a",remoteport:"\u8fdc\u7a0b\u7aef\u53e3\uff1a",resolution:"\u5206\u8fa8\u7387\uff1a",status:"\u8fde\u63a5\uff1a",transport:"\u4f20\u8f93\uff1a",turn:"\u8def\u7531"},dateUtils:{earlier:"\u66f4\u65e9\u7684",today:"\u4eca\u65e5",yesterday:"\u6628\u5929"},deepLinking:{appNotInstalled:"\u60a8\u9700\u8981\u5728\u624b\u673a\u4e0a\u5b89\u88c5 {{app}} \u8fd9\u4e2a\u5e94\u7528\u624d\u80fd\u53c2\u52a0\u4f1a\u8bae\u3002",description:"\u65e0\u54cd\u5e94\uff1f\u6b63\u5728\u5c1d\u8bd5\u542f\u52a8\u684c\u9762{{app}}\u53ec\u5f00\u4f1a\u8bae\u3002\u91cd\u8bd5\u6216\u542f\u52a8\u7f51\u9875\u7248{{app}}\u53ec\u5f00\u4f1a\u8bae\u3002",descriptionWithoutWeb:"",downloadApp:"\u4e0b\u8f7d\u5e94\u7528",launchWebButton:"\u5728\u7f51\u9875\u4e2d\u542f\u52a8",openApp:"\u7ee7\u7eed\u6253\u5f00\u5e94\u7528",title:"\u5728 {{app}}\u4e2d\u767b\u5f55\u4f1a\u8bae...",tryAgainButton:"\u8bf7\u5c1d\u8bd5\u91cd\u542f\u684c\u9762\u7248\u5e94\u7528\u7a0b\u5e8f"},defaultLink:"\u4f8b\u5982 {{url}}",deviceError:{cameraError:"\u65e0\u6cd5\u8bbf\u95ee\u60a8\u7684\u6444\u50cf\u5934",cameraPermission:"\u65e0\u6cd5\u83b7\u5f97\u6444\u50cf\u5934\u8bbf\u95ee\u6743\u9650",microphoneError:"\u65e0\u6cd5\u8bbf\u95ee\u60a8\u7684\u9ea6\u514b\u98ce",microphonePermission:"\u65e0\u6cd5\u83b7\u5f97\u9ea6\u514b\u98ce\u8bbf\u95ee\u6743\u9650"},deviceSelection:{noPermission:"\u672a\u6388\u6743\u9650",previewUnavailable:"\u9884\u89c8\u4e0d\u53ef\u7528",selectADevice:"\u9009\u62e9\u8bbe\u5907",testAudio:"\u64ad\u653e\u6d4b\u8bd5\u97f3\u9891"},dialog:{accessibilityLabel:{liveStreaming:"\u6d41\u5a92\u4f53\u76f4\u64ad"},allow:"\u5141\u8bb8",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\u53ea\u80fd\u540c\u65f6\u5206\u4eab \u4e00\u4e2a\u89c6\u9891",applicationWindow:"\u5e94\u7528\u7a97\u53e3",Back:"\u8fd4\u56de",cameraConstraintFailedError:"\u4f60\u7684\u6444\u50cf\u5934\u4e0d\u6ee1\u8db3\u8981\u6c42\u3002",cameraNotFoundError:"\u672a\u53d1\u73b0\u6444\u50cf\u5934",cameraNotSendingData:"\u6211\u4eec\u65e0\u6cd5\u8bbf\u95ee\u60a8\u7684\u6444\u50cf\u5934\u3002\u8bf7\u68c0\u67e5\u662f\u5426\u6709\u5176\u4ed6\u7a0b\u5e8f\u6b63\u5728\u4f7f\u7528\u8fd9\u4e2a\u8bbe\u5907\uff0c\u5426\u5219\u8bf7\u4ece\u8bbe\u5b9a\u83dc\u5355\u91cc\u9009\u62e9\u5176\u4ed6\u8bbe\u5907\u6216\u8005\u91cd\u65b0\u52a0\u8f7d\u3002",cameraNotSendingDataTitle:"\u65e0\u6cd5\u8bbf\u95ee\u6444\u50cf\u5934",cameraPermissionDeniedError:"\u60a8\u672a\u6388\u6743\u4f7f\u7528\u60a8\u7684\u6444\u50cf\u5934\u3002\u60a8\u4ecd\u53ef\u53c2\u52a0\u4f1a\u8bae\u4f46\u662f\u5176\u4ed6\u4eba\u65e0\u6cd5\u770b\u5230\uff0c\u4f7f\u7528\u5730\u5740\u680f\u91cc\u7684\u6444\u50cf\u5934\u6309\u94ae\u6765\u542f\u52a8\u6444\u50cf\u5934\u3002",cameraUnknownError:"\u7531\u4e8e\u672a\u77e5\u9519\u8bef\uff0c\u65e0\u6cd5\u4f7f\u7528\u6444\u50cf\u5934\u3002",cameraUnsupportedResolutionError:"\u60a8\u7684\u6444\u50cf\u5934\u4e0d\u652f\u6301\u6240\u9700\u5206\u8fa8\u7387\u3002",Cancel:"\u53d6\u6d88",close:"\u5173\u95ed",conferenceDisconnectMsg:"\u8bf7\u68c0\u67e5\u4f60\u7684\u7f51\u7edc\u8fde\u63a5\u3002\u5c06\u4f1a\u5728 {{seconds}} \u79d2\u540e\u91cd\u65b0\u8fde\u63a5\u2026",conferenceDisconnectTitle:"\u4f60\u5df2\u7ecf\u65ad\u5f00\u3002",conferenceReloadMsg:"\u4e0d\u597d\u610f\u601d\uff0c\u51fa\u9519\u4e86\u3002",conferenceReloadTitle:"\u4e0d\u597d\u610f\u601d\uff0c\u51fa\u9519\u4e86\u3002",confirm:"\u786e\u8ba4",confirmNo:"\u5426",confirmYes:"\u662f",connectError:"\u53d1\u751f\u9519\u8bef\uff0c\u65e0\u6cd5\u8fde\u63a5\u81f3\u4f1a\u8bae\uff01",connectErrorWithMsg:"\u53d1\u751f\u9519\u8bef\uff0c\u65e0\u6cd5\u8fde\u63a5\u81f3\u4f1a\u8bae: {{msg}}",connecting:"\u8fde\u63a5\u4e2d",contactSupport:"\u8054\u7cfb\u6211\u4eec",copy:"\u590d\u5236",dismiss:"\u89e3\u9664,\u79bb\u5f00",displayNameRequired:"",done:"\u5b8c\u6210",enterDisplayName:"",error:"\u9519\u8bef",externalInstallationMsg:"\u60a8\u9700\u8981\u5b89\u88c5\u684c\u9762\u5171\u4eab\u6269\u5c55",externalInstallationTitle:"\u9700\u8981\u6269\u5c55\u7a0b\u5e8f",goToStore:"\u8df3\u8f6c\u81f3\u5e94\u7528\u5546\u5e97",gracefulShutdown:"\u670d\u52a1\u5668\u6b63\u5728\u7ef4\u62a4\uff0c\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002",IamHost:"\u6211\u662f\u4e3b\u6301\u4eba\u3002",incorrectRoomLockPassword:"",incorrectPassword:"\u9519\u8bef\u7684\u7528\u6237\u540d\u6216\u8005\u5bc6\u7801",inlineInstallationMsg:"\u60a8\u9700\u8981\u5b89\u88c5\u684c\u9762\u5171\u4eab\u6269\u5c55",inlineInstallExtension:"\u7acb\u523b\u5b89\u88c5",internalError:"\u54ce\u5440\uff01\u51fa\u73b0\u4e86\u70b9\u95ee\u9898\u3002\u9519\u8bef\uff1a {{error}}",internalErrorTitle:"\u5185\u90e8\u9519\u8bef",kickMessage:"",kickParticipantButton:"\u8e22\u9664",kickParticipantDialog:"\u786e\u5b9a\u8981\u8e22\u9664\u6b64\u6210\u5458\u5417\uff1f",kickParticipantTitle:"\u9759\u97f3\u8be5\u4e0e\u4f1a\u8005\u5417\uff1f",kickTitle:"",liveStreaming:"\u6d41\u5a92\u4f53\u76f4\u64ad\u4e2d",liveStreamingDisabledForGuestTooltip:"\u8bbf\u5ba2\u65e0\u6cd5\u542f\u52a8\u6d41\u5a92\u4f53\u76f4\u64ad\u3002",liveStreamingDisabledTooltip:"\u7981\u6b62\u542f\u52a8\u6d41\u5a92\u4f53\u3002",lockMessage:"\u9501\u5b9a\u4f1a\u8bae\u5931\u8d25\u3002",lockRoom:"",lockTitle:"\u9501\u5b9a\u5931\u8d25",logoutQuestion:"\u4f60\u786e\u5b9a\u8981\u767b\u51fa\u5e76\u505c\u6b62\u4f1a\u8bae\u5417\uff1f",logoutTitle:"\u767b\u51fa",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\u4f60\u7684\u9ea6\u514b\u98ce\u4e0d\u6ee1\u8db3\u8981\u6c42\u3002",micNotFoundError:"\u672a\u53d1\u73b0\u9ea6\u514b\u98ce",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\u60a8\u672a\u6388\u6743\u4f7f\u7528\u9ea6\u514b\u98ce\uff0c\u60a8\u4ecd\u53ef\u53c2\u52a0\u4f1a\u8bae\u4f46\u662f\u5176\u4ed6\u4eba\u65e0\u6cd5\u542c\u5230\uff0c\u4f7f\u7528\u5730\u5740\u680f\u91cc\u7684\u6444\u50cf\u5934\u6309\u94ae\u6765\u542f\u52a8\u9ea6\u514b\u98ce\u3002",micUnknownError:"\u672a\u77e5\u9519\u8bef\uff0c\u9ea6\u514b\u98ce\u4e0d\u53ef\u7528\u3002",muteParticipantBody:"\u60a8\u65e0\u6cd5\u5bf9\u4ed6\u4eec\u89e3\u9664\u9759\u97f3\uff0c\u4f46\u662f\u4ed6\u4eec\u81ea\u5df1\u53ef\u4ee5\u968f\u65f6\u89e3\u9664\u9759\u97f3\u3002",muteParticipantButton:"\u9759\u97f3",muteParticipantDialog:"\u60a8\u786e\u5b9a\u8981\u5c06\u6b64\u53c2\u4e0e\u8005\u9759\u97f3\u5417\uff1f\u60a8\u5c06\u65e0\u6cd5\u53d6\u6d88\u9759\u97f3\uff0c\u4f46\u4ed6\u4eec\u53ef\u4ee5\u968f\u65f6\u53d6\u6d88\u9759\u97f3\u3002",muteParticipantTitle:"\u9759\u97f3\u8be5\u4e0e\u4f1a\u8005\u5417\uff1f",Ok:"\u597d\u7684",passwordLabel:"",passwordNotSupported:"\u4e0d\u652f\u6301\u8bbe\u7f6e\u4f1a\u8bae\u5bc6\u7801\u3002",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\u60a8\u7684\u6d4f\u89c8\u5668\u5728\u6b64\u7f51\u7ad9\u4e0a\u963b\u6b62\u4e86\u5f39\u51fa\u5f0f\u7a97\u53e3\u3002\u8bf7\u5728\u6d4f\u89c8\u5668\u7684\u5b89\u5168\u8bbe\u7f6e\u4e2d\u6253\u5f00\u5b83\u5e76\u518d\u8bd5\u4e00\u6b21\u3002",popupErrorTitle:"\u5f39\u51fa\u7a97\u53e3\u88ab\u62e6\u622a",recording:"\u5f55\u5236\u4e2d",recordingDisabledForGuestTooltip:"\u8bbf\u5ba2\u65e0\u6cd5\u5f00\u542f\u5f55\u5236\u3002",recordingDisabledTooltip:"\u5f00\u59cb\u5f55\u5236\u88ab\u7981\u7528\u3002",rejoinNow:"\u9a6c\u4e0a\u91cd\u65b0\u52a0\u5165",remoteControlAllowedMessage:"{{user}} \u63a5\u53d7\u4e86\u60a8\u7684\u8fdc\u7a0b\u63a7\u5236\u8bf7\u6c42",remoteControlDeniedMessage:"{{user}} \u62d2\u7edd\u4e86\u60a8\u7684\u8fdc\u7a0b\u63a7\u5236\u8bf7\u6c42",remoteControlErrorMessage:"\u5728\u5c1d\u8bd5\u5411{{user}}\u8bf7\u6c42\u8fdc\u7a0b\u63a7\u5236\u6743\u9650\u65f6\u53d1\u751f\u4e86\u4e00\u4e2a\u9519\u8bef\uff01",remoteControlRequestMessage:"\u4f60\u5141\u8bb8 {{user}} \u8fdc\u7a0b\u63a7\u5236\u4f60\u7684\u684c\u9762\u5417?",remoteControlShareScreenWarning:"\u6ce8\u610f\uff1a\u5982\u679c\u6309\u4e0b\u201c\u5141\u8bb8\u201d\u4f60\u5c06\u5171\u4eab\u4f60\u7684\u5c4f\u5e55\uff01",remoteControlStopMessage:"\u8fdc\u7a0b\u63a7\u5236\u7ed3\u675f\uff01",remoteControlTitle:"\u8fdc\u7a0b\u684c\u9762\u63a7\u5236",Remove:"\u79fb\u9664",removePassword:"",removeSharedVideoMsg:"\u60a8\u786e\u5b9a\u8981\u79fb\u9664\u5171\u4eab\u7684\u89c6\u9891\u5417\uff1f",removeSharedVideoTitle:"\u79fb\u9664\u5171\u4eab\u7684\u89c6\u9891",reservationError:"\u9884\u5b9a\u7cfb\u7edf\u9519\u8bef",reservationErrorMsg:"\u9519\u8bef\u4ee3\u53f7: {{code}}, \u63d0\u793a\u4fe1\u606f: {{msg}}",retry:"\u91cd\u8bd5",screenSharingFailedToInstall:"\u54ce\u5440\uff01\u5c4f\u5e55\u5171\u4eab\u63d2\u4ef6\u5b89\u88c5\u5931\u8d25\u3002",screenSharingFailedToInstallTitle:"\u5c4f\u5e55\u5171\u4eab\u63d2\u4ef6\u5b89\u88c5\u5931\u8d25",screenSharingFirefoxPermissionDeniedError:"\u5c1d\u8bd5\u8fdb\u884c\u5c4f\u5e55\u5171\u4eab\u65f6\u9047\u5230\u4e86\u95ee\u9898\u3002\u8bf7\u786e\u8ba4\u7ed9\u4e88\u4e86\u76f8\u5e94\u7684\u6743\u9650\u3002",screenSharingFirefoxPermissionDeniedTitle:"\u54ce\u5440\uff01\u6211\u4eec\u65e0\u6cd5\u542f\u52a8\u5c4f\u5e55\u5171\u4eab\uff01",screenSharingPermissionDeniedError:"\u54ce\u5440\uff01\u60a8\u7684\u5c4f\u5e55\u5171\u4eab\u63d2\u4ef6\u4f3c\u4e4e\u9047\u5230\u4e86\u6743\u9650\u95ee\u9898\u3002\u8bf7\u91cd\u65b0\u52a0\u8f7d\u5e76\u91cd\u8bd5\u3002",serviceUnavailable:"\u670d\u52a1\u4e0d\u53ef\u7528",sessTerminated:"\u901a\u8bdd\u5df2\u7ec8\u6b62",Share:"\u5206\u4eab",shareVideoLinkError:"\u8bf7\u63d0\u4f9b\u6b63\u786e\u7684youtube\u94fe\u63a5\u3002",shareVideoTitle:"\u5206\u4eab\u89c6\u9891",shareYourScreen:"\u5171\u4eab\u4f60\u7684\u5c4f\u5e55",shareYourScreenDisabled:"\u7981\u6b62\u5171\u4eab\u5c4f\u5e55\u3002",shareYourScreenDisabledForGuest:"\u8bbf\u5ba2\u65e0\u6cd5\u5171\u4eab\u5c4f\u5e55\u3002",startLiveStreaming:"\u5f00\u59cb\u76f4\u64ad",startRecording:"\u5f00\u59cb\u5f55\u5236",startRemoteControlErrorMessage:"\u5c1d\u8bd5\u5f00\u59cb\u8fdc\u7a0b\u63a7\u5236\u4f1a\u8bdd\u65f6\u53d1\u751f\u4e86\u4e00\u4e2a\u9519\u8bef\uff01",stopLiveStreaming:"\u505c\u6b62\u6d41\u5a92\u4f53\u76f4\u64ad",stopRecording:"\u505c\u6b62\u5f55\u5236",stopRecordingWarning:"\u786e\u5b9a\u8981\u505c\u6b62\u5f55\u5236\u5417",stopStreamingWarning:"\u786e\u5b9a\u8981\u505c\u6b62\u6d41\u5a92\u4f53\u76f4\u64ad\u5417\uff1f",streamKey:"\u6d41\u5a92\u4f53\u76f4\u64ad\u5bc6\u94a5",Submit:"\u63d0\u4ea4",thankYou:"\u611f\u8c22\u4f7f\u7528{{appName}}\uff01",token:"\u6807\u8bc6",tokenAuthFailed:"\u5bf9\u4e0d\u8d77\uff0c\u60a8\u672a\u88ab\u5141\u8bb8\u53c2\u52a0\u6b64\u4f1a\u8bae\u3002",tokenAuthFailedTitle:"\u8ba4\u8bc1\u5931\u8d25",transcribing:"\u8f6c\u5f55\u4e2d",unlockRoom:"",userPassword:"\u7528\u6237\u5bc6\u7801",WaitForHostMsg:"\u4f1a\u8bae{{room}}\u5c1a\u672a\u5f00\u59cb\u3002\u5982\u679c\u60a8\u662f\u4e3b\u6301\u4eba\uff0c\u8bf7\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u3002\u5426\u5219\uff0c\u8bf7\u7b49\u5f85\u4e3b\u6301\u4eba\u7684\u5230\u6765\u3002",WaitForHostMsgWOk:"\u4f1a\u8bae{{room}}\u5c1a\u672a\u5f00\u59cb\u3002\u5982\u679c\u60a8\u662f\u4e3b\u6301\u4eba\uff0c\u8bf7\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u3002\u5426\u5219\uff0c\u8bf7\u7b49\u5f85\u4e3b\u6301\u4eba\u7684\u5230\u6765\u3002",WaitingForHost:"\u7b49\u5f85\u4e3b\u6301\u4eba\u3002\u3002\u3002",Yes:"\u662f",yourEntireScreen:"\u4f60\u7684\u6574\u4e2a\u5c4f\u5e55"},dialOut:{statusMessage:"\u73b0\u5728\u72b6\u6001\u4e3a {{status}}"},feedback:{average:"\u5e73\u5747",bad:"\u5dee",detailsLabel:"\u544a\u8bc9\u6211\u4eec\u66f4\u591a\u5efa\u8bae\u548c\u610f\u89c1\u3002",good:"\u597d",rateExperience:"\u8bf7\u8bc4\u4ef7\u60a8\u7684\u4f1a\u8bae\u4f53\u9a8c\u3002",veryBad:"\u975e\u5e38\u5dee",veryGood:"\u975e\u5e38\u597d"},incomingCall:{answer:"\u56de\u590d",audioCallTitle:"\u6765\u7535",decline:"\u89e3\u9664,\u79bb\u5f00",productLabel:"\u6765\u81eaJitsi Meet",videoCallTitle:"\u89c6\u9891\u6765\u7535"},info:{accessibilityLabel:"\u663e\u793a\u4fe1\u606f",addPassword:"",cancelPassword:"",conferenceURL:"\u94fe\u63a5\uff1a",country:"\u56fd\u5bb6",dialANumber:"\u82e5\u8981\u52a0\u5165\u4f1a\u8bae\uff0c\u8bf7\u62e8\u6253\u5176\u4e2d\u4e00\u4e2a\u53f7\u7801\uff0c\u7136\u540e\u8f93\u5165pin\u7801\u3002",dialInConferenceID:"PIN:",dialInNotSupported:"\u62b1\u6b49\uff0c\u4e0d\u652f\u6301\u7535\u8bdd\u547c\u5165\u3002",dialInNumber:"\u64ad\u5165\uff1a",dialInSummaryError:"\u83b7\u53d6\u62e8\u5165\u4fe1\u606f\u65f6\u51fa\u9519\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002",dialInTollFree:"\u514d\u8d39\u7535\u8bdd",genericError:"\u7cdf\u7cd5\uff01\u51fa\u9519\u4e86\u3002",inviteLiveStream:"\u82e5\u8981\u67e5\u770b\u6b64\u4f1a\u8bae\u7684\u5b9e\u65f6\u76f4\u64ad\uff0c\u8bf7\u5355\u51fb\u6b64\u94fe\u63a5\uff1a{{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"\u60a8\u88ab\u9080\u8bf7\u52a0\u5165\u4e00\u4e2a\u4f1a\u8bae\u3002",inviteURLFirstPartPersonal:"{{name}} \u6b63\u5728\u9080\u8bf7\u60a8\u52a0\u5165\u4e00\u4e2a\u4f1a\u8bae\u3002\n",inviteURLSecondPart:"",liveStreamURL:"\u76f4\u64ad\uff1a",moreNumbers:"\u66f4\u591a\u6210\u5458",noNumbers:"\u65e0\u547c\u5165\u53f7\u7801\u3002",noPassword:"\u672a\u53d1\u73b0\u8bbe\u5907",noRoom:"\u6ca1\u6709\u6307\u5b9a\u8981\u547c\u5165\u7684\u623f\u95f4\u3002",numbers:"\u547c\u5165\u53f7\u7801",password:"",title:"\u5206\u4eab",tooltip:"\u5171\u4eab\u6b64\u4f1a\u8bae\u7684\u94fe\u63a5\u548c\u62e8\u5165\u4fe1\u606f",label:"\u4f1a\u8bae\u4fe1\u606f"},inviteDialog:{alertText:"\u9080\u8bf7\u6210\u5458\u5931\u8d25\u3002",header:"\u9080\u8bf7",searchCallOnlyPlaceholder:"\u8f93\u5165\u7535\u8bdd\u53f7\u7801",searchPeopleOnlyPlaceholder:"\u67e5\u627e\u6210\u5458",searchPlaceholder:"\u6210\u5458\u6216\u7535\u8bdd\u53f7\u7801",send:"\u53d1\u9001"},inlineDialogFailure:{msg:"\u8c8c\u4f3c\u51fa\u4e86\u70b9\u95ee\u9898\u3002",retry:"\u91cd\u8bd5",support:"\u652f\u6301",supportMsg:"\u5982\u679c\u6b64\u4e8b\u591a\u6b21\u53d1\u751f\uff0c\u8bf7\u8054\u7cfb"},keyboardShortcuts:{focusLocal:"\u5207\u6362\u5230\u672c\u5730\u89c6\u9891\u4e0a",focusRemote:"\u663e\u793a\u5bf9\u65b9\u7684\u89c6\u9891",fullScreen:"\u5f00\u542f / \u9000\u51fa \u5168\u5c4f",keyboardShortcuts:"\u5feb\u6377\u952e",localRecording:"\u663e\u793a / \u9690\u85cf \u672c\u5730\u5f55\u5236\u9009\u9879",mute:"\u9759\u97f3\u6216\u53d6\u6d88\u9759\u97f3",pushToTalk:"\u6309\u4f4f\u8bf4\u8bdd",raiseHand:"\u7533\u8bf7\u6216\u53d6\u6d88\u53d1\u8a00",showSpeakerStats:"\u67e5\u770b\u626c\u58f0\u5668\u72b6\u6001",toggleChat:"\u6253\u5f00\u6216\u5173\u95ed\u804a\u5929",toggleFilmstrip:"\u663e\u793a/\u9690\u85cf \u89c6\u9891\u7f29\u7565\u56fe",toggleScreensharing:"\u5728\u6444\u50cf\u5934\u548c\u5c4f\u5e55\u5171\u4eab\u4e4b\u95f4\u5207\u6362",toggleShortcuts:"\u663e\u793a/\u9690\u85cf \u5feb\u6377\u952e",videoMute:"\u5f00\u542f\u6216\u5173\u95ed\u89c6\u9891"},liveStreaming:{busy:"\u6211\u4eec\u6b63\u5728\u91ca\u653e\u4e32\u6d41\u8d44\u6e90\u3002\u8bf7\u51e0\u5206\u949f\u540e\u518d\u8bd5\u3002",busyTitle:"\u6240\u6709\u7684\u4e32\u6d41\u8bbe\u5907\u6b63\u5fd9",changeSignIn:"\u5207\u6362\u5e10\u53f7",choose:"\u9009\u62e9\u4e00\u4e2a\u76f4\u64ad\u6d41",chooseCTA:"\u8bf7\u9009\u62e9\u76f4\u64ad\u9009\u9879\u3002\u60a8\u73b0\u5728\u4ee5 {{email}} \u8eab\u4efd\u767b\u5f55\u3002",enterStreamKey:"\u5728\u6b64\u8f93\u5165\u60a8\u7684 YouTube \u4e32\u6d41\u5bc6\u94a5\u3002",error:"\u6d41\u5a92\u4f53\u76f4\u64ad\u5931\u8d25\u3002\u8bf7\u91cd\u8bd5\u3002",errorAPI:"\u5728\u8bbf\u95ee\u60a8\u7684 YouTube \u76f4\u64ad\u670d\u52a1\u65f6\u53d1\u751f\u95ee\u9898\u3002\u8bf7\u91cd\u65b0\u767b\u5f55\u3002",errorLiveStreamNotEnabled:"{{email}} \u672a\u542f\u7528\u6d41\u5a92\u4f53\u76f4\u64ad\u3002\u8bf7\u4f7f\u7528\u6d41\u5a92\u4f53\u76f4\u64ad\u6216\u767b\u5f55\u542f\u7528\u4e86\u6d41\u5a92\u4f53\u76f4\u64ad\u7684\u5e10\u6237\u3002",expandedOff:"\u6d41\u5a92\u4f53\u76f4\u64ad\u5df2\u88ab\u5173\u95ed",expandedOn:"\u4f1a\u8bae\u5f53\u524d\u6b63\u5728YouTube\u4e0a\u76f4\u64ad\u3002",expandedPending:"\u542f\u52a8\u76f4\u64ad\u4e2d\u3002\u3002\u3002",failedToStart:"\u76f4\u64ad\u670d\u52a1\u542f\u52a8\u5931\u8d25",getStreamKeyManually:"\u6211\u4eec\u65e0\u6cd5\u83b7\u53d6\u4efb\u4f55\u76f4\u64ad\u3002\u5c1d\u8bd5\u4eceYouTube\u83b7\u53d6\u6d41\u5a92\u4f53\u76f4\u64ad\u5bc6\u94a5\u3002",invalidStreamKey:"\u6d41\u5a92\u4f53\u76f4\u64ad\u5bc6\u94a5\u53ef\u80fd\u4e0d\u6b63\u786e\u3002",off:"\u6d41\u5a92\u4f53\u76f4\u64ad\u5df2\u505c\u6b62",on:"\u6d41\u5a92\u4f53\u76f4\u64ad\u4e2d",pending:"\u542f\u52a8\u6d41\u5a92\u4f53\u3002\u3002\u3002",serviceName:"\u76f4\u64ad\u670d\u52a1",signedInAs:"\u60a8\u5f53\u524d\u767b\u5f55\u4e3a\uff1a",signIn:"\u4f7f\u7528\u8c37\u6b4c\u767b\u5f55",signInCTA:"\u8f93\u5165 YouTube \u4e32\u6d41\u5bc6\u94a5\u6216\u8005\u767b\u5f55 YouTube \u5e10\u53f7\u3002",signOut:"\u767b\u51fa",start:"\u5f00\u59cb\u76f4\u64ad",streamIdHelp:"\u8fd9\u662f\u4ec0\u4e48\uff1f",unavailableTitle:"\u6d41\u5a92\u4f53\u76f4\u64ad\u4e0d\u53ef\u7528"},localRecording:{clientState:{off:"\u5173",on:"\u5f00",unknown:"\u672a\u77e5"},dialogTitle:"\u672c\u5730\u5f55\u5236\u63a7\u5236",duration:"\u5468\u671f",durationNA:"N/A",encoding:"\u7f16\u7801\u4e2d",label:"\u5f55\u97f3",labelToolTip:"\u672c\u5730\u5f55\u97f3\u88ab\u5360\u7528",localRecording:"\u672c\u5730\u5f55\u5236\u4e2d",me:"\u81ea\u5df1",messages:{engaged:"\u672c\u5730\u5f55\u97f3\u88ab\u5360\u7528\u3002",finished:"\u4f1a\u8bdd {{token}} \u5f55\u5236\u7ed3\u675f\u3002\u8bf7\u5c06\u5f55\u5236\u7684\u6587\u4ef6\u53d1\u9001\u7ed9\u4e3b\u6301\u4eba\u3002",finishedModerator:"\u4f1a\u8bdd {{token}} \u5f55\u5236\u7ed3\u675f\u3002\u672c\u5730\u5f55\u5236\u5df2\u4fdd\u5b58\u3002\u8bf7\u8981\u6c42\u5176\u4ed6\u53c2\u4e0e\u8005\u63d0\u4ea4\u4ed6\u4eec\u7684\u5f55\u97f3\u3002",notModerator:"\u4f60\u4e0d\u662f\u4e3b\u6301\u4eba.\u4f60\u4e0d\u80fd\u5f00\u542f\u6216\u5173\u95ed\u672c\u5730\u5f55\u97f3"},moderator:"\u7ba1\u7406\u5458",no:"\u5426",participant:"\u4e0e\u4f1a\u8005",participantStats:"\u4e0e\u4f1a\u8005\u72b6\u6001",sessionToken:"\u4f1a\u8bdd\u4ee4\u724c",start:"\u5f00\u59cb\u5f55\u5236",stop:"\u505c\u6b62\u5f55\u5236",yes:"\u662f"},lockRoomPassword:"\u5bc6\u7801",lockRoomPasswordUppercase:"\u5bc6\u7801",me:"\u6211",notify:{connectedOneMember:"{{name}} \u52a0\u5165\u4f1a\u8bae",connectedThreePlusMembers:"{{name}} \u548c\u5176\u4ed6 {{count}} \u4eba\u52a0\u5165\u4f1a\u8bae",connectedTwoMembers:"{{first}} \u548c {{second}} \u52a0\u5165\u4f1a\u8bae",disconnected:"\u5df2\u65ad\u5f00\u8fde\u63a5",focus:"\u4f1a\u8bae\u805a\u7126",focusFail:"{{component}} \u4e0d\u53ef\u7528 - \u5728{{ms}}\u79d2\u540e\u91cd\u8bd5",grantedTo:"\u4e3b\u6301\u6743\u9650\u5df2\u6388\u4e88{{to}}\uff01",invitedOneMember:"{{name}} \u5df2\u88ab\u9080\u8bf7",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"\u81ea\u5df1",moderator:"\u5df2\u6388\u6743\u4e3b\u6301\u4eba\u6743\u9650\uff01",muted:"\u60a8\u5df2\u7ecf\u5f00\u59cb\u4e86\u901a\u8bdd\uff0c\u5e76\u5904\u4e8e\u9759\u97f3\u72b6\u6001\u3002",mutedTitle:"\u60a8\u5df2\u88ab\u9759\u97f3\uff01",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"{{name}} \u60f3\u8981\u53d1\u8a00\u3002",somebody:"\u67d0\u4eba",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"\u5443\u2026\u6050\u6015\u60a8\u5bf9 {{appName}} \u7684\u4f53\u9a8c\u4f1a\u5f88\u4e0d\u597d\u3002\u6211\u4eec\u6b63\u5728\u5c1d\u8bd5\u4f18\u5316\u5bf9\u6b64\u6d4f\u89c8\u5668\u7684\u652f\u6301\u3002\u773c\u4e0b\uff0c\u8bf7\u5c1d\u8bd5\u4f7f\u7528 \u5df2\u77e5\u4f53\u9a8c\u5f88\u597d\u7684\u6d4f\u89c8\u5668\u3002",suboptimalExperienceTitle:"\u6d4f\u89c8\u5668\u8b66\u544a",unmute:"",newDeviceCameraTitle:"\u68c0\u6d4b\u5230\u65b0\u76f8\u673a",newDeviceAudioTitle:"\u68c0\u6d4b\u5230\u65b0\u97f3\u9891\u8bbe\u5907",newDeviceAction:"\u4f7f\u7528"},passwordSetRemotely:"\u7531\u5176\u4ed6\u4e0e\u4f1a\u8005\u8bbe\u7f6e",passwordDigitsOnly:"",poweredby:"\u6280\u672f\u652f\u6301",presenceStatus:{busy:"\u5fd9\u788c",calling:"\u901a\u8bdd\u4e2d...",connected:"\u8fde\u63a5\u4e2d...",connecting:"\u8fde\u63a5\u4e2d...",connecting2:"\u8fde\u63a5\u4e2d...",disconnected:"\u5df2\u65ad\u5f00\u8fde\u63a5",expired:"\u5df2\u8fc7\u671f",ignored:"\u5df2\u5ffd\u7565",initializingCall:"\u521d\u59cb\u5316\u547c\u53eb...",invited:"\u9080\u8bf7",rejected:"\u62d2\u7edd",ringing:"\u54cd\u94c3..."},profile:{setDisplayNameLabel:"\u8bbe\u5b9a\u60a8\u7684\u663e\u793a\u540d\u79f0",setEmailInput:"\u8f93\u5165\u60a8\u7684\u90ae\u7bb1",setEmailLabel:"\u8bbe\u7f6e\u60a8\u7684\u4e2a\u4eba\u5168\u7403\u7edf\u4e00\u6807\u8bc6\u90ae\u7bb1",title:"\u7b80\u4ecb"},recording:{authDropboxText:"\u4e0a\u4f20\u81f3Dropbox",availableSpace:"\u53ef\u7528\u7a7a\u95f4\uff1a{{spaceLeft}} MB\uff08\u5927\u7ea6\u53ef\u5f55 {{duration}} \u5206\u949f\uff09 ",beta:"BETA",busy:"\u6211\u4eec\u6b63\u5728\u91ca\u653e\u5f55\u5236\u8d44\u6e90\u3002\u8bf7\u51e0\u5206\u949f\u4e4b\u540e\u518d\u8bd5\u3002",busyTitle:"\u6240\u6709\u7684\u5f55\u5236\u8bbe\u5907\u6b63\u5fd9",error:"\u5f55\u5236\u5931\u8d25\u3002\u8bf7\u91cd\u65b0\u5c1d\u8bd5\u3002",expandedOff:"\u5f55\u5236\u5df2\u505c\u6b62",expandedOn:"\u6b64\u4f1a\u8bae\u6b63\u5728\u88ab\u5f55\u5236\u3002",expandedPending:"\u5f55\u5236\u6b63\u5728\u542f\u52a8...",failedToStart:"\u5f55\u5236\u542f\u52a8\u5931\u8d25",fileSharingdescription:"\u8ddf\u4e0e\u4f1a\u8005\u5206\u4eab\u5f55\u5236",live:"\u76f4\u64ad",loggedIn:"\u4ee5 {{userName}} \u767b\u5f55",off:"\u5f55\u5236\u5df2\u505c\u6b62",on:"\u5f55\u5236\u4e2d",pending:"\u6b63\u5728\u51c6\u5907\u5f55\u5236\u4f1a\u8bae....",rec:"REC\u5f55\u5236",serviceDescription:"\u5f55\u5236\u670d\u52a1\u5c06\u4fdd\u5b58\u60a8\u7684\u5f55\u5236",serviceName:"\u5f55\u5236\u670d\u52a1",signIn:"\u767b\u5f55",signOut:"\u767b\u51fa",unavailable:"\u5662\uff01{{serviceName}} \u6682\u65f6\u65e0\u6cd5\u4f7f\u7528\u3002\u6211\u4eec\u6b63\u5728\u89e3\u51b3\u6b64\u95ee\u9898\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002",unavailableTitle:"\u5f55\u5236\u4e0d\u53ef\u7528"},sectionList:{pullToRefresh:"\u4e0b\u62c9\u5237\u65b0"},settings:{calendar:{about:"{{appName}} \u7684\u65e5\u5386\u96c6\u6210\u7528\u4e8e\u5b89\u5168\u8bbf\u95ee\u60a8\u7684\u65e5\u5386\uff0c\u4ee5\u4fbf\u5b83\u53ef\u4ee5\u8bfb\u53d6\u5373\u5c06\u53d1\u751f\u7684\u4e8b\u4ef6\u3002",disconnect:"\u65ad\u5f00\u8fde\u63a5",microsoftSignIn:"Microsoft\u5e10\u53f7\u767b\u5f55",signedIn:"\u76ee\u524d\u901a\u8fc7{{email}}\u83b7\u53d6\u65e5\u5386\u4e8b\u4ef6\u3002\u70b9\u51fb\u4e0b\u65b9\u65ad\u5f00\u8fde\u63a5\u6309\u94ae\u505c\u6b62\u8bbf\u95ee\u3002",title:"\u65e5\u5386"},devices:"\u8bbe\u5907",followMe:"\u6240\u6709\u4eba\u8ddf\u968f\u6211",language:"\u8bed\u8a00",loggedIn:"\u4ee5{{name}} \u767b\u5f55",moderator:"\u7ba1\u7406\u5458",more:"\u66f4\u591a",name:"\u540d\u79f0",noDevice:"\u672a\u53d1\u73b0\u8bbe\u5907",selectAudioOutput:"\u97f3\u9891\u8f93\u51fa",selectCamera:"\u6444\u50cf\u5934",selectMic:"\u9ea6\u514b\u98ce",startAudioMuted:"\u6240\u6709\u4eba\u5f00\u59cb\u65f6\u9759\u97f3",startVideoMuted:"\u6240\u6709\u4eba\u5f00\u59cb\u65f6\u9690\u85cf\u89c6\u9891\u753b\u9762",title:"\u8bbe\u7f6e"},settingsView:{alertOk:"\u786e\u8ba4",alertTitle:"\u8b66\u544a",alertURLText:"\u670d\u52a1\u5668 URL \u65e0\u6548",buildInfoSection:"\u751f\u6210\u4fe1\u606f",conferenceSection:"\u4f1a\u8bae",displayName:"\u663e\u793a\u540d\u79f0",email:"\u7535\u5b50\u90ae\u4ef6",header:"\u8bbe\u7f6e",profileSection:"\u7b80\u4ecb",serverURL:"\u670d\u52a1\u5668 URL",startWithAudioMuted:"\u542f\u52a8\u5e76\u5173\u95ed\u97f3\u9891",startWithVideoMuted:"\u542f\u52a8\u5e76\u5173\u95ed\u89c6\u9891",version:"\u7248\u672c"},share:{dialInfoText:"",mainText:"\u70b9\u51fb\u4ee5\u4e0b\u94fe\u63a5\u52a0\u5165\u4f1a\u8bae\uff1a{{roomUrl}}\n"},speaker:"\u53d1\u8a00\u4eba",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"\u540d\u79f0",seconds:"{{count}}s",speakerStats:"\u53d1\u8a00\u8005\u72b6\u6001",speakerTime:"\u53d1\u8a00\u8005\u65f6\u95f4"},startupoverlay:{policyText:" ",title:"{{app}} \u9700\u8981\u4f7f\u7528\u60a8\u7684\u9ea6\u514b\u98ce\u548c\u6444\u50cf\u5934\u3002"},suspendedoverlay:{rejoinKeyTitle:"\u91cd\u65b0\u52a0\u5165",text:"\u6309\u4e0b \u91cd\u65b0\u52a0\u5165 \u6309\u94ae\u91cd\u65b0\u8fde\u63a5\u3002",title:"\u7531\u4e8e\u60a8\u7684\u7535\u8111\u8fdb\u5165\u4f11\u7720\u6a21\u5f0f\uff0c\u89c6\u9891\u901a\u8bdd\u5df2\u7ecf\u4e2d\u65ad\u3002"},toolbar:{accessibilityLabel:{audioOnly:"\u6253\u5f00 /\u5173\u95ed \u4ec5\u97f3\u9891",audioRoute:"\u9009\u62e9\u97f3\u9891\u8bbe\u5907",callQuality:"",cc:"\u6253\u5f00 / \u5173\u95ed \u5b57\u5e55",chat:"\u663e\u793a / \u9690\u85cf \u804a\u5929\u7a97\u53e3",document:"\u5f00\u542f / \u5173\u95ed \u6587\u6863\u5171\u4eab",feedback:"\u7559\u4e0b\u53cd\u9988",fullScreen:"\u8fdb\u5165 / \u9000\u51fa \u5168\u5c4f\u6a21\u5f0f",hangup:"\u9000\u51fa\u804a\u5929\u5ba4",invite:"\u9080\u8bf7",kick:"\u8e22\u9664\u6210\u5458",localRecording:"\u663e\u793a / \u9690\u85cf \u672c\u5730\u5f55\u5236\u9009\u9879",lockRoom:"\u5207\u6362\u4f1a\u8bae\u5ba4\u9501\u5b9a",moreActions:"\u663e\u793a / \u9690\u85cf \u66f4\u591a\u9009\u62e9",moreActionsMenu:"\u66f4\u591a\u529f\u80fd\u83dc\u5355",mute:"\u9759\u97f3 / \u53d6\u6d88\u9759\u97f3",pip:"\u5207\u6362\u5b50\u6bcd\u753b\u9762\u6a21\u5f0f",profile:"\u7f16\u8f91\u60a8\u7684\u7b80\u4ecb",raiseHand:"\u4e3e\u624b / \u53d6\u6d88\u4e3e\u624b",recording:"\u5f00\u542f / \u505c\u6b62 \u89c6\u9891\u5f55\u5236",remoteMute:"\u9759\u97f3\u4e0e\u4f1a\u8005",Settings:"\u663e\u793a / \u9690\u85cf \u8bbe\u7f6e",sharedvideo:"\u5f00\u542f / \u5173\u95ed Youtube \u5f71\u7247\u5206\u4eab",shareRoom:"\u9080\u8bf7\u4ed6\u4eba",shareYourScreen:"\u5f00\u542f / \u5173\u95ed \u5c4f\u5e55\u5206\u4eab",shortcuts:"\u5207\u6362\u5feb\u6377\u65b9\u5f0f",show:"",speakerStats:"\u663e\u793a / \u9690\u85cf \u6f14\u8bf4\u8005\u8d44\u6599",tileView:"\u753b\u9762\u6a21\u5f0f",toggleCamera:"\u5207\u6362\u76f8\u673a",videomute:"\u9759\u97f3 / \u53d6\u6d88\u9759\u97f3",videoblur:""},addPeople:"\u6dfb\u52a0\u6210\u5458\u5230\u60a8\u7684\u901a\u8bdd\u4e2d",audioOnlyOff:"\u7981\u7528\u4ec5\u97f3\u9891\u6a21\u5f0f",audioOnlyOn:"\u542f\u7528\u4ec5\u97f3\u9891\u6a21\u5f0f",audioRoute:"\u9009\u62e9\u97f3\u9891\u8bbe\u5907",authenticate:"\u8ba4\u8bc1",callQuality:"\u7ba1\u7406\u901a\u8bdd\u8d28\u91cf",chat:"\u5f00\u542f / \u5173\u95ed \u804a\u5929",closeChat:"\u5173\u95ed\u804a\u5929",documentClose:"\u5173\u95ed\u6587\u6863\u5171\u4eab",documentOpen:"\u5f00\u542f\u6587\u6863\u5171\u4eab",enterFullScreen:"\u5168\u5c4f\u89c2\u770b",enterTileView:"\u5207\u6362\u89c6\u56fe",exitFullScreen:"\u9000\u51fa\u5168\u5c4f\u6a21\u5f0f",exitTileView:"\u9000\u51fa\u5e73\u94fa\u6a21\u5f0f",feedback:"\u7559\u4e0b\u53cd\u9988",hangup:"\u79bb\u5f00",invite:"\u9080\u8bf7",login:"\u767b\u5f55",logout:"\u767b\u51fa",lowerYourHand:"\u653e\u624b",moreActions:"\u66f4\u591a\u64cd\u4f5c",mute:"\u9759\u97f3 / \u89e3\u9664\u9759\u97f3",openChat:"\u5f00\u542f\u804a\u5929",pip:"\u8fdb\u5165\u5b50\u6bcd\u753b\u9762\u6a21\u5f0f",profile:"\u7f16\u8f91\u60a8\u7684\u7b80\u4ecb",raiseHand:"\u8bf7\u6c42 / \u53d6\u6d88 \u53d1\u8a00",raiseYourHand:"\u4e3e\u624b",Settings:"\u8bbe\u7f6e",sharedvideo:"\u5206\u4eabYouTube\u89c6\u9891",shareRoom:"\u9080\u8bf7\u4ed6\u4eba",shortcuts:"",speakerStats:"\u53d1\u8a00\u8005\u72b6\u6001",startScreenSharing:"\u5f00\u542f\u5c4f\u5e55\u5171\u4eab",startSubtitles:"\u5f00\u542f\u5b57\u5e55",stopScreenSharing:"\u505c\u6b62\u5c4f\u5e55\u5171\u4eab",stopSubtitles:"\u5173\u95ed\u5b57\u5e55",stopSharedVideo:"\u5173\u95edYouTube\u89c6\u9891",talkWhileMutedPopup:"\u60a8\u5728\u5c1d\u8bd5\u53d1\u8a00\u5417? \u5f53\u524d\u60a8\u5df2\u88ab\u9759\u97f3\u3002",tileViewToggle:"\u753b\u9762\u6a21\u5f0f",toggleCamera:"\u5207\u6362\u76f8\u673a",videomute:"\u5f00\u542f / \u5173\u95ed \u6444\u50cf\u5934",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"\u5f00\u542f / \u5173\u95ed\u5b57\u5e55",error:"\u8f6c\u5f55\u5931\u8d25\u3002\u8bf7\u91cd\u65b0\u5c1d\u8bd5\u3002",expandedLabel:"\u6b63\u5728\u8f6c\u5f55\u4e2d",failedToStart:"\u5f00\u542f\u8f6c\u5f55\u5931\u8d25",labelToolTip:"\u4f1a\u8bae\u6b63\u5728\u8f6c\u5f55",off:"\u505c\u6b62\u8f6c\u5f55",pending:"\u6b63\u5728\u51c6\u5907\u8f6c\u5f55\u4f1a\u8bae...",start:"\u5f00\u542f\u663e\u793a\u5b57\u5e55",stop:"\u505c\u6b62\u663e\u793a\u5b57\u5e55",tr:"TR"},userMedia:{androidGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u5141\u8bb8",chromeGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u5141\u8bb8",edgeGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u662f",electronGrantPermissions:"\u8bf7\u6388\u6743\u4f7f\u7528\u60a8\u7684\u6444\u50cf\u5934\u548c\u9ea6\u514b\u98ce",firefoxGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9\u5171\u4eab\u8bbe\u5907 ",iexplorerGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u53ef\u4ee5",nwjsGrantPermissions:"\u8bf7\u6388\u6743\u4f7f\u7528\u60a8\u7684\u6444\u50cf\u5934\u548c\u9ea6\u514b\u98ce",operaGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u5141\u8bb8","react-nativeGrantPermissions":"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u5141\u8bb8",safariGrantPermissions:"\u5f53\u6d4f\u89c8\u5668\u8981\u6c42\u6743\u9650\u8bb8\u53ef\u65f6\u9009\u62e9 \u53ef\u4ee5"},videoSIPGW:{busy:"\u6211\u4eec\u6b63\u5728\u6e05\u7406\u548c\u91ca\u653e\u8d44\u6e90\u3002\u8bf7\u8fc7\u51e0\u5206\u949f\u540e\u518d\u8bd5\u3002",busyTitle:"\u623f\u95f4\u670d\u52a1\u6b63\u5fd9",errorAlreadyInvited:"{{displayName}} \u5df2\u88ab\u9080\u8bf7\u8fc7\u4e86",errorInvite:"\u4f1a\u8bae\u8fd8\u672a\u5f00\u59cb\u3002\u8bf7\u7a0d\u540e\u518d\u6765\u3002",errorInviteFailed:"\u6211\u4eec\u6b63\u5728\u89e3\u51b3\u95ee\u9898\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002",errorInviteFailedTitle:"\u9080\u8bf7 {{displayName}} \u5931\u8d25",errorInviteTitle:"\u9080\u8bf7\u623f\u95f4\u9519\u8bef",pending:"{{displayName}} \u5df2\u88ab\u9080\u8bf7"},videoStatus:{audioOnly:"AUD\u58f0\u97f3",audioOnlyExpanded:"\u60a8\u5904\u4e8e\u4ec5\u7528\u97f3\u8baf\u6a21\u5f0f\u3002\u8be5\u6a21\u5f0f\u53ef\u8282\u7701\u9891\u5bbd\uff0c\u4f46\u65e0\u6cd5\u770b\u89c1\u4ed6\u4eba\u5f71\u50cf\u3002",callQuality:"",hd:"\u9ad8\u6e05",highDefinition:"\u9ad8\u6e05",labelTooiltipNoVideo:"\u65e0\u89c6\u9891",labelTooltipAudioOnly:"\u5df2\u542f\u7528\u4ec5\u97f3\u9891\u6a21\u5f0f",ld:"\u4f4e\u6e05",lowDefinition:"\u4f4e\u6e05",onlyAudioAvailable:"\u53ea\u80fd\u4f7f\u7528\u97f3\u9891",onlyAudioSupported:"\u6211\u4eec\u53ea\u652f\u6301\u6b64\u6d4f\u89c8\u5668\u7684\u97f3\u9891\u529f\u80fd\u3002",p2pEnabled:"\u70b9\u5bf9\u70b9\u5df2\u542f\u7528",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\u5c06\u4f1a\u9996\u9009\u9ad8\u6e05\u6a21\u5f0f\u3002",sd:"\u6807\u6e05",standardDefinition:"\u6807\u6e05"},videothumbnail:{domute:"\u9759\u97f3",flip:"\u7ffb\u8f6c",kick:"\u8e22\u51fa",moderator:"\u7ba1\u7406\u5458",mute:"\u4e0e\u4f1a\u8005\u5df2\u88ab\u9759\u97f3",muted:"\u5df2\u9759\u97f3",remoteControl:"\u8fdc\u7a0b\u63a7\u5236",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"\u70b9\u51fb\u52a0\u5165",roomname:"\u8bf7\u8f93\u5165\u623f\u95f4\u540d"},appDescription:"\u5feb\u6765\u4f7f\u7528\u5168\u961f\u89c6\u9891\u901a\u8bdd\u3002\u60a8\u53ef\u4ee5\u9080\u8bf7\u4efb\u4f55\u60a8\u8ba4\u8bc6\u7684\u4eba\u3002{{app}} \u662f\u4e00\u4e2a\u5b8c\u5168\u52a0\u5bc6\uff0c100% \u5f00\u6e90\u7684\u89c6\u9891\u4f1a\u8bae\u89e3\u51b3\u65b9\u6848\u3002\u65e0\u9700\u6ce8\u518c\u5e10\u53f7\uff0c\u65e0\u9650\u65f6\u514d\u8d39\u4f7f\u7528\u3002",audioVideoSwitch:{audio:"\u8bed\u97f3",video:"\u89c6\u9891"},calendar:"\u65e5\u5386",connectCalendarButton:"\u8fde\u63a5\u4f60\u7684\u65e5\u5386",connectCalendarText:"\u8fde\u63a5\u4f60\u7684\u65e5\u5386\u4ee5\u67e5\u770b\u4f60\u5728{{app}}\u4e2d\u7684\u5168\u90e8\u4f1a\u8bae\u3002\u6b64\u5916\uff0c\u65b0\u589e{{provider}} \u4f1a\u8bae\u5230\u4f60\u7684\u65e5\u5386\u4e2d\uff0c\u70b9\u51fb\u5373\u53ef\u542f\u52a8\u3002",enterRoomTitle:"\u5f00\u542f\u4e00\u4e2a\u65b0\u7684\u4f1a\u8bae",go:"\u5f00\u59cb",join:"\u52a0\u5165",info:"\u4fe1\u606f",privacy:"\u9690\u79c1",recentList:"\u6700\u8fd1",recentListDelete:"\u5220\u9664",recentListEmpty:"\u76ee\u524d\u6ca1\u6709\u4f7f\u7528\u3002\u4e0e\u4f60\u7684\u56e2\u961f\u6210\u5458\u804a\u5929\uff0c\u5373\u53ef\u5728\u6b64\u5904\u627e\u5230\u6700\u8fd1\u6240\u6709\u4f1a\u8bae\u3002",reducedUIText:"",roomname:"\u8bf7\u8f93\u5165\u623f\u95f4\u540d",roomnameHint:"\u8bf7\u8f93\u5165\u60a8\u60f3\u52a0\u5165\u623f\u95f4\u7684 URL \u5730\u5740\u6216\u8005\u623f\u95f4\u540d\u3002\u60a8\u4e5f\u53ef\u4ee5\u60f3\u4e2a\u623f\u540d\u521b\u5efa\u623f\u95f4\uff0c\u53ea\u8981\u5176\u4ed6\u4eba\u8f93\u5165\u548c\u60a8\u4e00\u6837\u7684\u540d\u79f0\u5c31\u80fd\u52a0\u5165\u60a8\u7684\u623f\u95f4\u3002",sendFeedback:"\u53d1\u9001\u53cd\u9988",terms:"\u6761\u6b3e",title:"\u5b89\u5168\uff0c\u529f\u80fd\u5b8c\u5584\u548c\u5b8c\u5168\u5f00\u6e90\u7684\u89c6\u9891\u4f1a\u8bae"}}},682,[]); -__d(function(e,a,n,i,s,r,o){s.exports={en:"English",af:"",az:"Azerbaijani",bg:"Bulgarian",cs:"Czech",de:"German",el:"Greek",eo:"Esperanto",es:"Spanish",fr:"French",hy:"Armenian",it:"Italian",ja:"\u65e5\u672c\u8a9e",ko:"\u97d3\u6587",nb:"Norwegian Bokmal",oc:"Occitan",pl:"Polish",ptBR:"Portuguese (Brazil)",ru:"Russian",sk:"Slovak",sl:"Slovenian",sv:"Swedish",tr:"Turkish",vi:"Vietnamese",zhCN:"\u4e2d\u6587 \u7b80\u4f53 (\u4e2d\u56fd)"}},683,[]); -__d(function(e,o,i,t,r,n,a){r.exports={addPeople:{add:"\u9080\u8acb",countryNotSupported:"\u6b64\u76ee\u6a19\u5340\u57df\u5c1a\u672a\u652f\u63f4\u3002",countryReminder:"\u5617\u8a66\u5728\u7f8e\u570b\u5916\u5730\u901a\u8a71\uff1f\u8acb\u78ba\u8a8d\u958b\u982d\u4f7f\u7528\u7684\u570b\u5bb6\u4ee3\u78bc\uff01",disabled:"\u60a8\u4e0d\u53ef\u4ee5\u9080\u8acb\u4eba\u54e1\u3002",failedToAdd:"",footerText:"\u64ad\u6253\u5df2\u95dc\u9589\u3002",loading:"\u5c0b\u627e\u806f\u7d61\u4eba\u53ca\u96fb\u8a71\u865f\u78bc",loadingNumber:"\u9a57\u8b49\u96fb\u8a71\u865f\u78bc",loadingPeople:"\u6b63\u5728\u5c0b\u641c\u4eba\u54e1\u9032\u884c\u9080\u8acb",noResults:"\u6c92\u6709\u7b26\u5408\u8981\u6c42\u7684\u641c\u5c0b\u7d50\u679c",noValidNumbers:"\u8acb\u8f38\u5165\u4e00\u7d44\u96fb\u8a71\u865f\u78bc",searchNumbers:"\u65b0\u589e\u96fb\u8a71\u865f\u78bc",searchPeople:"\u5c0b\u627e\u4eba\u54e1",searchPeopleAndNumbers:"\u5c0b\u627e\u4eba\u54e1\u6216\u65b0\u589e\u96fb\u8a71\u865f\u78bc",telephone:"\u96fb\u8a71\uff1a {{number}}",title:"\u9080\u8acb\u4eba\u54e1\u53c3\u52a0\u6703\u8b70"},audioDevices:{bluetooth:"\u85cd\u7259",headphones:"\u8033\u6a5f",phone:"\u96fb\u8a71",speaker:"\u767c\u8a00\u8005"},audioOnly:{audioOnly:"\u50c5\u7528\u97f3\u8a0a"},calendarSync:{addMeetingURL:"\u589e\u52a0\u6703\u8b70\u9023\u7d50",confirmAddLink:"\u4f60\u8981\u52a0\u4e0a Jitsi \u9023\u7d50\u65bc\u6b64\u4e8b\u4ef6\u55ce\uff1f",error:{appConfiguration:"",generic:"",notSignedIn:""},join:"\u53c3\u52a0",joinTooltip:"\u53c3\u52a0\u6703\u8b70",nextMeeting:"\u4e0b\u6b21\u6703\u8b70",noEvents:"\u6c92\u6709\u9810\u5b9a\u4e8b\u4ef6\u6392\u5165\u884c\u7a0b\u3002",ongoingMeeting:"\u5373\u5c07\u9032\u884c\u6703\u8b70",permissionButton:"\u958b\u555f\u8a2d\u5b9a",permissionMessage:"\u65e5\u66c6\u5141\u8a31\u6b0a\u9650\u662f\u5fc5\u9808\u7684\uff0c\u4ee5\u67e5\u770b\u4f60\u7684\u6703\u8b70\u65bc\u61c9\u7528\u7a0b\u5f0f\u4e2d\u3002",refresh:"\u91cd\u65b0\u6574\u7406\u884c\u4e8b\u66c6",today:"\u4eca\u65e5"},chat:{error:"\u932f\u8aa4\uff1a\u4f60\u7684\u8a0a\u606f \"{{originalText}}\" \u672a\u88ab\u9001\u51fa\u3002\u539f\u56e0\uff1a {{error}}",messagebox:"",nickname:{popover:"\u9078\u64c7\u66b1\u7a31",title:""},title:""},connectingOverlay:{joiningRoom:""},connection:{ATTACHED:"\u5df2\u7d93\u9644\u52a0",AUTHENTICATING:"\u9a57\u8b49\u4e2d",AUTHFAIL:"\u9a57\u8b49\u5931\u6557",CONNECTED:"\u5df2\u7d93\u9023\u63a5",CONNECTING:"\u9023\u63a5\u4e2d",CONNFAIL:"\u9023\u63a5\u5931\u6557",DISCONNECTED:"\u5df2\u7d93\u4e2d\u65b7\u9023\u63a5",DISCONNECTING:"\u4e2d\u65b7\u9023\u63a5\u4e2d",ERROR:"\u932f\u8aa4",RECONNECTING:"\u7db2\u7d61\u932f\u8aa4\u767c\u751f\u3002\u91cd\u65b0\u9023\u63a5\u4e2d\u2026\u2026\u2026"},connectionindicator:{address:"\u5730\u5740\uff1a",bandwidth:"\u4f30\u8a08\u983b\u5bec\uff1a",bitrate:"\u6bd4\u7279\u7387\uff1a",bridgeCount:"\u4f3a\u670d\u5668\u8a08\u6578\uff1a",connectedTo:"\u5df2\u9023\u63a5\u81f3\uff1a",framerate:"\u5f71\u683c\u7387\uff1a",less:"\u986f\u793a\u8f03\u5c11",localaddress:"\u672c\u5730\u5730\u5740\uff1a",localport:"\u672c\u5730\u7aef\u53e3\uff1a",more:"\u986f\u793a\u66f4\u591a",packetloss:"\u4e1f\u5305\uff1a",quality:{good:"\u5f88\u597d",inactive:"\u672a\u555f\u7528",lost:"\u6f0f\u5931",nonoptimal:"\u4e0d\u751a\u7406\u60f3",poor:"\u4e0d\u597d"},remoteaddress:"\u9060\u7aef\u5730\u5740\uff1a",remoteport:"\u9060\u7aef\u7aef\u53e3\uff1a",resolution:"\u89e3\u6790\u5ea6\uff1a",status:"\u9023\u63a5\uff1a",transport:"\u50b3\u8f38\uff1a",turn:" (\u8f49)"},dateUtils:{earlier:"\u7a0d\u65e9",today:"\u4eca\u65e5",yesterday:"\u6628\u5929"},deepLinking:{appNotInstalled:"\u5728\u60a8\u7684\u624b\u6a5f\u4e0a\u9700\u8981 {{app}} \u884c\u52d5\u61c9\u7528\u7a0b\u5f0f\u53bb\u52a0\u5165\u9019\u5834\u6703\u8b70\u3002",description:"\u6c92\u6709\u767c\u751f\u4f5c\u7528\u55ce\uff1f\u6211\u5011\u5617\u8a66\u767c\u8d77\u60a8\u7684\u6703\u8b70\u65bc {{app}} desktop \u684c\u9762\u61c9\u7528\u7a0b\u5f0f\u3002\u8acb\u518d\u8a66\u4e00\u6b21\uff0c\u6216\u662f\u767c\u8d77\u6703\u8b70\u65bc {{app}} \u7db2\u8def\u61c9\u7528\u7a0b\u5f0f\u3002",descriptionWithoutWeb:"",downloadApp:"\u4e0b\u8f09\u61c9\u7528 APP",launchWebButton:"\u5728\u7db2\u8def\u4e0a\u767c\u8d77",openApp:"\u7e7c\u7e8c\u524d\u5f80\u6b64\u61c9\u7528\u7a0b\u5f0f",title:"\u767c\u8d77\u60a8\u7684\u6703\u8b70\u65bc {{app}}...",tryAgainButton:"\u5728\u684c\u9762\u4e0a\u518d\u8a66\u4e00\u6b21"},defaultLink:"\u4f8b\u5982 {{url}}",deviceError:{cameraError:"\u7121\u6cd5\u53d6\u7528\u60a8\u7684\u651d\u5f71\u88dd\u7f6e",cameraPermission:"\u7121\u6cd5\u7372\u5f97\u651d\u5f71\u88dd\u7f6e\u53d6\u7528\u6b0a\u9650",microphoneError:"\u7121\u6cd5\u53d6\u7528\u60a8\u7684\u9ea5\u514b\u98a8",microphonePermission:"\u7121\u6cd5\u7372\u5f97\u9ea5\u514b\u98a8\u53d6\u7528\u6b0a\u9650"},deviceSelection:{noPermission:"\u672a\u53d6\u5f97\u6b0a\u9650",previewUnavailable:"\u9810\u89bd\u7121\u6cd5\u4f7f\u7528",selectADevice:"\u9078\u64c7\u8a2d\u5099",testAudio:"\u64ad\u653e\u6e2c\u8a66\u8072\u97f3"},dialog:{accessibilityLabel:{liveStreaming:"\u76f4\u64ad\u4e32\u6d41"},allow:"\u5141\u8a31",alreadySharedVideoMsg:"",alreadySharedVideoTitle:"\u4e00\u6b21\u53ea\u80fd\u5141\u8a31\u4e00\u4f4d\u8996\u8a0a\u5206\u4eab",applicationWindow:"\u61c9\u7528\u7a0b\u5f0f\u8996\u7a97",Back:"\u8fd4\u56de",cameraConstraintFailedError:"\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u4e0d\u7b26\u5408\u8981\u6c42\u3002",cameraNotFoundError:"\u672a\u767c\u73fe\u651d\u5f71\u88dd\u7f6e\u3002",cameraNotSendingData:"\u6211\u5011\u7121\u6cd5\u53d6\u7528\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u3002\u8acb\u6aa2\u67e5\u662f\u5426\u6709\u5176\u4ed6\u7a0b\u5e8f\u6b63\u5728\u4f7f\u7528\u9019\u500b\u8a2d\u5099\uff0c\u5426\u5247\u8acb\u5f9e\u8a2d\u7f6e\u9078\u55ae\u88e1\u9078\u64c7\u5176\u4ed6\u8a2d\u5099\u6216\u8005\u91cd\u65b0\u88dd\u8f09\u3002",cameraNotSendingDataTitle:"\u7121\u6cd5\u53d6\u7528\u651d\u5f71\u88dd\u7f6e",cameraPermissionDeniedError:"\u60a8\u672a\u53d6\u5f97\u6b0a\u9650\u4f7f\u7528\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u3002\u60a8\u4ecd\u7136\u53ef\u53c3\u52a0\u6703\u8b70\uff0c\u4f46\u662f\u5176\u4ed6\u4eba\u7121\u6cd5\u770b\u5230\u3002\u53ef\u4ee5\u5229\u7528\u4f4d\u5740\u6b04\u4e2d\u7684\u651d\u5f71\u88dd\u7f6e\u6309\u9215\u4f86\u4fee\u5fa9\u555f\u52d5\u3002",cameraUnknownError:"\u7531\u65bc\u4e0d\u660e\u539f\u56e0\uff0c\u7121\u6cd5\u4f7f\u7528\u651d\u5f71\u88dd\u7f6e\u3002",cameraUnsupportedResolutionError:"\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u4e0d\u652f\u63f4\u6240\u9700\u7684\u8996\u8a0a\u89e3\u6790\u5ea6\u3002",Cancel:"\u53d6\u6d88",close:"\u95dc\u9589",conferenceDisconnectMsg:"\u8acb\u6aa2\u67e5\u4e00\u4e0b\u7db2\u8def\u9023\u63a5\u3002\u5c07\u5728 {{seconds}} \u79d2\u5f8c\u91cd\u65b0\u9023\u63a5\u2026",conferenceDisconnectTitle:"\u60a8\u5df2\u7d93\u88ab\u4e2d\u65b7\u9023\u63a5\u3002",conferenceReloadMsg:"\u6211\u5011\u6b63\u8a66\u8457\u4fee\u5fa9\u72c0\u6cc1\u3002\u91cd\u65b0\u9023\u63a5\u65bc {{seconds}} \u79d2\u5167\u2026\u2026",conferenceReloadTitle:"\u4e0d\u597d\u610f\u601d\uff0c\u51fa\u932f\u4e86\u3002",confirm:"\u78ba\u8a8d",confirmNo:"\u6c92\u6709",confirmYes:"\u662f\u7684",connectError:"\u5594\u54e6\uff01\u767c\u751f\u932f\u8aa4\uff0c\u7121\u6cd5\u9023\u63a5\u81f3\u6703\u8b70\u3002",connectErrorWithMsg:"\u5594\u54e6\uff01\u767c\u751f\u932f\u8aa4\uff0c\u7121\u6cd5\u9023\u63a5\u81f3\u6703\u8b70: {{msg}}",connecting:"\u9023\u63a5\u4e2d",contactSupport:"\u806f\u7d61\u652f\u63f4",copy:"\u8907\u88fd",dismiss:"\u89e3\u9664",displayNameRequired:"",done:"\u5b8c\u6210",enterDisplayName:"",error:"\u932f\u8aa4",externalInstallationMsg:"\u60a8\u9700\u8981\u5b89\u88dd\u684c\u9762\u5206\u4eab\u64f4\u5145\u61c9\u7528\u7a0b\u5f0f\u3002",externalInstallationTitle:"\u9700\u8981\u64f4\u5145\u61c9\u7528\u7a0b\u5f0f",goToStore:"\u524d\u5f80\u61c9\u7528\u5546\u5e97",gracefulShutdown:"\u672c\u4f3a\u670d\u5668\u9589\u95dc\u7dad\u8b77\u4e2d\uff0c\u8acb\u7a0d\u5f8c\u518d\u8a66\u3002",IamHost:"\u6211\u662f\u4e3b\u8fa6\u4eba",incorrectRoomLockPassword:"",incorrectPassword:"\u932f\u8aa4\u7684\u7528\u6236\u540d\u7a31\u6216\u5bc6\u78bc",inlineInstallationMsg:"\u60a8\u9700\u8981\u5b89\u88dd\u684c\u9762\u5206\u4eab\u64f4\u5145\u61c9\u7528\u7a0b\u5f0f\u3002",inlineInstallExtension:"\u7acb\u5373\u5b89\u88dd",internalError:"\u5594\u54e6\uff01\u51fa\u73fe\u4e86\u9ede\u554f\u984c\u3002\u767c\u751f\u932f\u8aa4\uff1a {{error}}",internalErrorTitle:"\u5167\u90e8\u932f\u8aa4",kickMessage:"",kickParticipantButton:"",kickParticipantDialog:"",kickParticipantTitle:"",kickTitle:"",liveStreaming:"\u76f4\u64ad\u4e32\u6d41\u4e2d",liveStreamingDisabledForGuestTooltip:"\u8a2a\u5ba2\u7121\u6cd5\u555f\u52d5\u76f4\u64ad\u4e32\u6d41\u3002",liveStreamingDisabledTooltip:"\u555f\u52d5\u76f4\u64ad\u4e32\u6d41\u5df2\u95dc\u9589\u3002",lockMessage:"\u9396\u5b9a\u6703\u8b70\u5931\u6557\u3002",lockRoom:"",lockTitle:"\u9396\u5b9a\u5931\u6557",logoutQuestion:"\u60a8\u78ba\u5b9a\u8981\u767b\u51fa\u4e26\u505c\u6b62\u6703\u8b70\u55ce\uff1f",logoutTitle:"\u767b\u51fa",maxUsersLimitReached:"",maxUsersLimitReachedTitle:"",micConstraintFailedError:"\u60a8\u7684\u9ea5\u514b\u98a8\u4e0d\u7b26\u5408\u8981\u6c42\u3002",micNotFoundError:"\u672a\u767c\u73fe\u9ea5\u514b\u98a8\u3002",micNotSendingData:"",micNotSendingDataTitle:"",micPermissionDeniedError:"\u60a8\u672a\u53d6\u5f97\u6b0a\u9650\u4f7f\u7528\u9ea5\u514b\u98a8\u3002\u60a8\u4ecd\u7136\u53ef\u53c3\u52a0\u6703\u8b70\uff0c\u4f46\u662f\u5176\u4ed6\u4eba\u7121\u6cd5\u807d\u5230\u3002\u53ef\u4ee5\u5229\u7528\u4f4d\u5740\u6b04\u4e2d\u7684\u651d\u5f71\u88dd\u7f6e\u6309\u9215\u4f86\u4fee\u5fa9\u555f\u52d5\u3002",micUnknownError:"\u4e0d\u660e\u539f\u56e0\u9020\u6210\u9ea5\u514b\u98a8\u7121\u6cd5\u4f7f\u7528\u3002",muteParticipantBody:"\u60a8\u7121\u6cd5\u5c0d\u4ed6\u5011\u89e3\u9664\u975c\u97f3\uff0c\u4f46\u662f\u4ed6\u5011\u81ea\u5df1\u96a8\u6642\u53ef\u4ee5\u89e3\u9664\u975c\u97f3\u3002",muteParticipantButton:"\u975c\u97f3",muteParticipantDialog:"",muteParticipantTitle:"",Ok:"Ok",passwordLabel:"",passwordNotSupported:"\u4e0d\u652f\u63f4\u8a2d\u7f6e\u6703\u8b70\u5bc6\u78bc\u3002",passwordNotSupportedTitle:"",passwordRequired:"",popupError:"\u60a8\u7684\u700f\u89bd\u5668\u5728\u6b64\u7db2\u7ad9\u4e0a\u963b\u6514\u5f48\u51fa\u8996\u7a97\u3002\u8acb\u5728\u700f\u89bd\u5668\u7684\u5b89\u5168\u8a2d\u7f6e\u4e2d\u958b\u555f\u5b83\u4e26\u518d\u8a66\u4e00\u6b21\u3002",popupErrorTitle:"\u5f48\u51fa\u8996\u7a97\u906d\u5230\u963b\u6514",recording:"\u9304\u88fd\u4f5c\u696d\u4e2d",recordingDisabledForGuestTooltip:"\u8a2a\u5ba2\u7121\u6cd5\u555f\u52d5\u9304\u5f71\u3002",recordingDisabledTooltip:"\u555f\u52d5\u9304\u5f71\u5df2\u95dc\u9589\u3002",rejoinNow:"\u7acb\u5373\u91cd\u65b0\u52a0\u5165",remoteControlAllowedMessage:"{{user}} \u63a5\u53d7\u60a8\u9032\u884c\u9060\u7aef\u63a7\u5236\u7684\u8981\u6c42\uff01",remoteControlDeniedMessage:"{{user}} \u62d2\u7d55\u60a8\u9032\u884c\u9060\u7aef\u63a7\u5236\u7684\u8981\u6c42\uff01",remoteControlErrorMessage:"\u5728\u5617\u8a66\u5411 {{user}} \u8acb\u6c42\u9060\u7aef\u63a7\u5236\u6b0a\u9650\u6642\u767c\u751f\u932f\u8aa4\uff01",remoteControlRequestMessage:"\u60a8\u8981\u5141\u8a31 {{user}} \u9060\u7aef\u63a7\u5236\u60a8\u7684\u684c\u9762\u55ce?",remoteControlShareScreenWarning:"\u6ce8\u610f\uff1a\u5982\u679c\u6309\u4e0b \"\u5141\u8a31\" \u60a8\u5c07\u5206\u4eab\u81ea\u5df1\u7684\u87a2\u5e55\uff01",remoteControlStopMessage:"\u9060\u7aef\u63a7\u5236\u968e\u6bb5\u7d50\u675f\uff01",remoteControlTitle:"\u9060\u7aef\u684c\u9762\u63a7\u5236",Remove:"\u79fb\u9664",removePassword:"",removeSharedVideoMsg:"\u60a8\u78ba\u5b9a\u8981\u79fb\u9664\u81ea\u5df1\u7684\u5206\u4eab\u8996\u8a0a\u55ce\uff1f",removeSharedVideoTitle:"\u79fb\u9664\u5206\u4eab\u8996\u8a0a",reservationError:"\u9810\u7d04\u7cfb\u7d71\u932f\u8aa4",reservationErrorMsg:"\u932f\u8aa4\u78bc: {{code}}, \u8a0a\u606f: {{msg}}",retry:"\u91cd\u8a66",screenSharingFailedToInstall:"\u5594\u54e6\uff01\u87a2\u5e55\u5206\u4eab\u64f4\u5145\u7a0b\u5f0f\u5b89\u88dd\u5931\u6557\u3002",screenSharingFailedToInstallTitle:"\u87a2\u5e55\u5206\u4eab\u64f4\u5145\u5b89\u88dd\u5931\u6557",screenSharingFirefoxPermissionDeniedError:"\u5617\u8a66\u9032\u884c\u87a2\u5e55\u5206\u4eab\u6642\u9047\u5230\u554f\u984c\u3002\u8acb\u78ba\u8a8d\u60a8\u6709\u8ce6\u4e88\u76f8\u5c0d\u7684\u6b0a\u9650\u5141\u8a31\u3002",screenSharingFirefoxPermissionDeniedTitle:"\u5594\u54e6\uff01\u6211\u5011\u7121\u6cd5\u555f\u52d5\u87a2\u5e55\u5206\u4eab\uff01",screenSharingPermissionDeniedError:"\u5594\u54e6\uff01\u60a8\u7684\u8996\u8a0a\u5206\u4eab\u64f4\u5145\u6b0a\u9650\u767c\u751f\u4e00\u9ede\u554f\u984c\u3002\u8acb\u91cd\u65b0\u8f09\u5165\u518d\u8a66\u4e00\u6b21\u3002",serviceUnavailable:"\u670d\u52d9\u7121\u6cd5\u4f7f\u7528",sessTerminated:"\u901a\u8a71\u5df2\u7d93\u7d42\u6b62",Share:"\u5206\u4eab",shareVideoLinkError:"\u8acb\u63d0\u4f9b\u6b63\u78ba\u7684 YouTube \u9023\u7d50\u3002",shareVideoTitle:"\u5206\u4eab\u8996\u8a0a",shareYourScreen:"\u5206\u4eab\u81ea\u5df1\u7684\u87a2\u5e55",shareYourScreenDisabled:"\u87a2\u5e55\u5206\u4eab\u5df2\u95dc\u9589\u3002",shareYourScreenDisabledForGuest:"\u8a2a\u5ba2\u7121\u6cd5\u87a2\u5e55\u5206\u4eab\u3002",startLiveStreaming:"\u555f\u52d5\u76f4\u64ad\u4e32\u6d41",startRecording:"\u555f\u52d5\u9304\u88fd\u4f5c\u696d",startRemoteControlErrorMessage:"\u5617\u8a66\u555f\u52d5\u9060\u7aef\u63a7\u5236\u968e\u6bb5\u6642\u767c\u751f\u932f\u8aa4\uff01",stopLiveStreaming:"\u505c\u6b62\u76f4\u64ad\u4e32\u6d41",stopRecording:"\u505c\u6b62\u9304\u88fd\u4f5c\u696d",stopRecordingWarning:"\u78ba\u5b9a\u8981\u505c\u6b62\u9304\u88fd\u4f5c\u696d\u55ce\uff1f",stopStreamingWarning:"\u78ba\u5b9a\u8981\u505c\u6b62\u76f4\u64ad\u4e32\u6d41\u55ce\uff1f",streamKey:"\u76f4\u64ad\u4e32\u6d41\u5bc6\u9470",Submit:"\u63d0\u4ea4",thankYou:"\u611f\u8b1d\u60a8\u4f7f\u7528 {{appName}}\uff01",token:"\u6a19\u8a18",tokenAuthFailed:"\u5c0d\u4e0d\u8d77\uff0c\u60a8\u672a\u88ab\u5141\u8a31\u52a0\u5165\u6b64\u6703\u8b70\u3002",tokenAuthFailedTitle:"\u9a57\u8b49\u5931\u6557",transcribing:"\u8f49\u9304\u4e2d",unlockRoom:"",userPassword:"\u7528\u6236\u5bc6\u78bc",WaitForHostMsg:"",WaitForHostMsgWOk:"",WaitingForHost:"\u7b49\u4faf\u4e3b\u8fa6\u4eba\u2026\u2026\u2026",Yes:"\u662f\u7684",yourEntireScreen:"\u81ea\u5df1\u7684\u5168\u87a2\u5e55"},dialOut:{statusMessage:"\u73fe\u5728\u72c0\u614b\u70ba {{status}}"},feedback:{average:"\u666e\u901a\u4e2d\u7b49",bad:"\u5f88\u5dee",detailsLabel:"\u544a\u8a34\u6211\u5011\u672c\u6b21\u6703\u8b70\u4f7f\u7528\u4e0a\u66f4\u591a\u7d50\u679c\u3002",good:"\u5f88\u597d",rateExperience:"\u8acb\u60a8\u8a55\u50f9\u9019\u6b21\u6703\u8b70\u7684\u9ad4\u9a57\u6210\u6548",veryBad:"\u6975\u5dee",veryGood:"\u6975\u597d"},incomingCall:{answer:"\u63a5\u901a",audioCallTitle:"\u4f86\u96fb",decline:"\u89e3\u9664",productLabel:"\u4f86\u81ea Jitsi Meet",videoCallTitle:"\u8996\u8a0a\u4f86\u96fb"},info:{accessibilityLabel:"\u986f\u793a\u8cc7\u8a0a",addPassword:"",cancelPassword:"",conferenceURL:"\u9023\u7d50\uff1a",country:"\u570b\u5bb6",dialANumber:"",dialInConferenceID:"PIN \u865f\u78bc\uff1a",dialInNotSupported:"\u62b1\u6b49\uff0c\u76ee\u524d\u4e0d\u652f\u63f4\u96fb\u8a71\u64ad\u5165\u3002",dialInNumber:"\u64ad\u5165\uff1a",dialInSummaryError:"",dialInTollFree:"",genericError:"\u7cdf\u7cd5\uff01\u51fa\u932f\u4e86\u3002",inviteLiveStream:"\u8981\u89c0\u770b\u9019\u5834\u6703\u8b70\u7684\u76f4\u64ad\u4e32\u6d41\uff0c\u9ede\u6309\u6b64\u9023\u7d50\uff1a {{url}}",invitePhone:"",invitePhoneAlternatives:"",inviteURLFirstPartGeneral:"",inviteURLFirstPartPersonal:"",inviteURLSecondPart:"",liveStreamURL:"\u76f4\u64ad\u4e32\u6d41\uff1a",moreNumbers:"\u66f4\u591a\u6210\u54e1",noNumbers:"\u7121\u64ad\u5165\u865f\u78bc\u3002",noPassword:"\u7121",noRoom:"\u6c92\u6709\u6703\u8b70\u5ba4\u662f\u6307\u5b9a\u8981\u64ad\u6253\u9032\u5165\u3002",numbers:"\u64ad\u5165\u865f\u78bc",password:"",title:"\u5206\u4eab",tooltip:"\u986f\u793a\u6b64\u6703\u8b70\u7684\u9023\u7d50\u53ca\u96fb\u8a71\u64ad\u5165\u865f\u78bc",label:""},inviteDialog:{alertText:"",header:"\u9080\u8acb",searchCallOnlyPlaceholder:"",searchPeopleOnlyPlaceholder:"",searchPlaceholder:"",send:""},inlineDialogFailure:{msg:"\u597d\u50cf\u6709\u9ede\u5361\u5361\u4e0d\u9806\u3002",retry:"\u91cd\u8a66",support:"\u652f\u63f4",supportMsg:"\u5982\u679c\u72c0\u6cc1\u4e00\u76f4\u767c\u751f\uff0c\u8acb\u806f\u7d61"},keyboardShortcuts:{focusLocal:"\u805a\u7126\u65bc\u81ea\u5df1\u7684\u8996\u8a0a",focusRemote:"\u805a\u7126\u65bc\u53e6\u4e00\u4eba\u7684\u8996\u8a0a",fullScreen:"\u89c0\u770b \u6216 \u96e2\u958b \u5168\u87a2\u5e55",keyboardShortcuts:"\u5feb\u6377\u9375",localRecording:"\u986f\u793a\u6216\u986f\u793a\u672c\u5730\u7aef\u9304\u5f71\u63a7\u5236",mute:"\u975c\u97f3\u6216\u89e3\u9664\u975c\u97f3",pushToTalk:"\u6309\u9375\u901a\u8a71",raiseHand:"\u8209\u624b\u767c\u8a00\u6216\u4e0d\u4f5c\u767c\u8a00",showSpeakerStats:"\u986f\u793a\u767c\u8a00\u8005\u6578\u64da",toggleChat:"\u958b\u555f\u6216\u95dc\u9589\u804a\u5929",toggleFilmstrip:"\u986f\u793a\u6216\u96b1\u85cf\u8996\u8a0a\u5f71\u7247\u7e2e\u5716",toggleScreensharing:"\u5728\u651d\u5f71\u93e1\u982d\u548c\u87a2\u5e55\u5206\u4eab\u4e4b\u9593\u9032\u884c\u5207\u63db",toggleShortcuts:"\u986f\u793a\u6216\u986f\u793a\u9375\u76e4\u5feb\u6377\u9375",videoMute:"\u555f\u52d5\u6216\u505c\u6b62\u81ea\u5df1\u7684\u651d\u5f71\u88dd\u7f6e"},liveStreaming:{busy:"\u6211\u5011\u6b63\u5728\u91cb\u653e\u4e32\u6d41\u8cc7\u6e90\u3002\u8acb\u904e\u5e7e\u5206\u9418\u5f8c\u518d\u8a66\u3002",busyTitle:"\u5168\u90e8\u4e32\u6d41\u8a2d\u5099\u6b63\u5728\u5fd9\u788c",changeSignIn:"\u5207\u63db\u5e33\u865f\u3002",choose:"\u9078\u64c7\u76f4\u64ad\u4e32\u6d41",chooseCTA:"\u8acb\u9078\u64c7\u76f4\u64ad\u4e32\u6d41\u9078\u9805\u3002\u60a8\u76ee\u524d\u662f\u4ee5 {{email}} \u8eab\u4efd\u767b\u5165\u3002",enterStreamKey:"\u5728\u6b64\u8f38\u5165\u60a8\u7684 YouTube \u76f4\u64ad\u4e32\u6d41\u5bc6\u9470\u3002",error:"\u76f4\u64ad\u4e32\u6d41\u5931\u6557\u3002\u8acb\u91cd\u8a66\u3002",errorAPI:"\u53d6\u7528\u60a8\u7684 YouTube \u64ad\u51fa\u6642\u767c\u751f\u932f\u8aa4\u3002\u8acb\u91cd\u65b0\u767b\u5165\u3002",errorLiveStreamNotEnabled:"\u76f4\u64ad\u4e32\u6d41\u5728 {{email}} \u5c1a\u672a\u555f\u7528\u3002\u8acb\u958b\u555f\u76f4\u64ad\u4e32\u6d41\u6216\u767b\u5165\u6709\u555f\u7528\u76f4\u64ad\u4e32\u6d41\u7684\u5e33\u6236\u3002",expandedOff:"\u76f4\u64ad\u4e32\u6d41\u5df2\u505c\u6b62",expandedOn:"\u6703\u8b70\u4e32\u6d41\u76ee\u524d\u9001\u81f3 YouTube \u3002",expandedPending:"\u76f4\u64ad\u4e32\u6d41\u6b63\u88ab\u555f\u52d5\u2026",failedToStart:"\u76f4\u64ad\u4e32\u6d41\u555f\u52d5\u5931\u6557",getStreamKeyManually:"",invalidStreamKey:"",off:"\u76f4\u64ad\u4e32\u6d41\u5df2\u7d93\u505c\u6b62",on:"\u76f4\u64ad\u4e32\u6d41\u4e2d",pending:"\u555f\u52d5\u76f4\u64ad\u4e32\u6d41\u2026\u2026\u2026",serviceName:"\u76f4\u64ad\u4e32\u6d41\u670d\u52d9",signedInAs:"\u4f60\u76ee\u524d\u767b\u5165\u540d\u70ba\uff1a",signIn:"\u4f7f\u7528 Google \u5e33\u6236\u767b\u5165",signInCTA:"\u8f38\u5165 YouTube \u76f4\u64ad\u4e32\u6d41\u5bc6\u9470\uff0c\u6216\u767b\u5165 YouTube \u5e33\u865f\u3002",signOut:"\u767b\u51fa",start:"\u555f\u52d5\u76f4\u64ad\u4e32\u6d41",streamIdHelp:"\u9019\u662f\u4ec0\u9ebc\uff1f",unavailableTitle:"\u76f4\u64ad\u4e32\u6d41\u7121\u6cd5\u4f7f\u7528"},localRecording:{clientState:{off:"\u95dc",on:"\u958b",unknown:"\u4e0d\u660e"},dialogTitle:"\u672c\u5730\u7aef\u9304\u5f71\u63a7\u5236",duration:"\u671f\u9593",durationNA:"N/A",encoding:"\u89e3\u78bc\u4e2d",label:"LOR",labelToolTip:"\u672c\u5730\u7aef\u9304\u5f71\u4f7f\u7528\u4e2d",localRecording:"\u672c\u5730\u7aef\u9304\u5f71\u4e2d",me:"\u81ea\u5df1",messages:{engaged:"\u672c\u5730\u7aef\u9304\u5f71\u5df2\u4f7f\u7528\u3002",finished:"\u9304\u5f71\u968e\u6bb5 {{token}} \u5df2\u5b8c\u6210\u3002\u8acb\u50b3\u9001\u9304\u5f71\u6a94\u6848\u81f3\u4e3b\u6301\u4eba\u3002",finishedModerator:"\u9304\u5f71\u968e\u6bb5 {{token}} \u5df2\u5b8c\u6210\u3002\u672c\u5730\u7aef\u9304\u5f71\u8ffd\u8e64\u5df2\u5b58\u6a94\u3002\u8acb\u8981\u6c42\u5404\u53c3\u8207\u8005\u63d0\u4ea4\u5176\u9304\u5f71\u6a94\u6848\u3002",notModerator:"\u4f60\u4e0d\u662f\u4e3b\u6301\u4eba\uff0c\u7121\u6cd5\u555f\u52d5\u6216\u505c\u6b62\u672c\u5730\u7aef\u9304\u5f71\u3002"},moderator:"\u4e3b\u6301\u4eba",no:"\u6c92\u6709",participant:"\u53c3\u8207\u8005",participantStats:"\u53c3\u8207\u8005\u72c0\u614b",sessionToken:"\u968e\u6bb5\u6a19\u8a18",start:"\u555f\u52d5\u9304\u5f71\u4f5c\u696d",stop:"\u505c\u6b62\u9304\u5f71\u4f5c\u696d",yes:"\u662f\u7684"},lockRoomPassword:"\u5bc6\u78bc",lockRoomPasswordUppercase:"\u5bc6\u78bc",me:"\u6211",notify:{connectedOneMember:"",connectedThreePlusMembers:"",connectedTwoMembers:"",disconnected:"\u5df2\u7d93\u4e2d\u65b7\u9023\u63a5",focus:"\u6703\u8b70\u7126\u9ede",focusFail:"{{component}} \u7121\u6cd5\u4f7f\u7528 - \u8acb\u5728 {{ms}} \u79d2\u5f8c\u91cd\u8a66",grantedTo:"\u4e3b\u6301\u4eba\u6b0a\u9650\u5df2\u6388\u4e88 {{to}}!",invitedOneMember:"",invitedThreePlusMembers:"",invitedTwoMembers:"",kickParticipant:"",me:"\u81ea\u5df1",moderator:"\u4e3b\u6301\u4eba\u6b0a\u9650\u5df2\u7d93\u53d6\u5f97\uff01",muted:"\u60a8\u5df2\u7d93\u555f\u52d5\u901a\u8a71\uff0c\u4e26\u8655\u65bc\u975c\u97f3\u72c0\u614b\u3002",mutedTitle:"\u60a8\u76ee\u524d\u8655\u65bc\u975c\u97f3\uff01",mutedRemotelyTitle:"",mutedRemotelyDescription:"",passwordRemovedRemotely:"",passwordSetRemotely:"",raisedHand:"",somebody:"\u67d0\u4eba",startSilentTitle:"",startSilentDescription:"",suboptimalExperienceDescription:"\u5443\u2026\u2026\u6050\u6015\u60a8\u5c0d {{appName}} \u7684\u9ad4\u9a57\u4e0d\u662f\u5f88\u597d\uff0c\u6211\u5011\u6b63\u5728\u5617\u8a66\u627e\u65b9\u6cd5\u6539\u9032\u5c0d\u6b64\u700f\u89bd\u5668\u7684\u652f\u63f4\u3002\u73fe\u4e0b\u656c\u8acb\u9078\u7528 \u5168\u529b\u652f\u63f4\u7684\u700f\u89bd\u5668 \u4f86\u9032\u884c\u3002",suboptimalExperienceTitle:"\u700f\u89bd\u5668\u8b66\u544a",unmute:"",newDeviceCameraTitle:"",newDeviceAudioTitle:"",newDeviceAction:""},passwordSetRemotely:"",passwordDigitsOnly:"",poweredby:"\u6280\u8853\u652f\u63f4",presenceStatus:{busy:"\u5fd9\u7dda",calling:"\u4f86\u96fb\u2026",connected:"\u5df2\u7d93\u9023\u63a5",connecting:"\u9023\u7dda\u4e2d...",connecting2:"\u901a\u8a71\u4e2d*...",disconnected:"\u5df2\u7d93\u4e2d\u65b7\u9023\u63a5",expired:"\u672a\u63a5",ignored:"\u5ffd\u7565",initializingCall:"\u64ad\u6253\u96fb\u8a71\u2026",invited:"\u88ab\u9080\u8acb\u7684",rejected:"\u62d2\u63a5",ringing:"\u9234\u9234\u9234\u2026\u2026"},profile:{setDisplayNameLabel:"\u8a2d\u5b9a\u60a8\u7684\u986f\u793a\u540d\u7a31",setEmailInput:"\u8f38\u5165\u60a8\u7684\u96fb\u5b50\u4fe1\u7bb1",setEmailLabel:"\u8a2d\u7f6e\u60a8\u7684\u5927\u982d\u4eba\u50cf\u96fb\u5b50\u4fe1\u7bb1",title:"\u7c21\u4ecb"},recording:{authDropboxText:"\u4e0a\u50b3\u81f3 Dropbox",availableSpace:"\u53ef\u7528\u7a7a\u9593\uff1a {{spaceLeft}} MB (\u5927\u7d04\u9304\u5f71\u6642\u9593 {{duration}} \u5206\u9418)",beta:"BETA",busy:"\u6211\u5011\u6b63\u5728\u91cb\u653e\u9304\u88fd\u8cc7\u6e90\u3002\u8acb\u904e\u5e7e\u5206\u9418\u5f8c\u518d\u8a66\u3002",busyTitle:"\u5168\u90e8\u9304\u88fd\u8a2d\u5099\u6b63\u5728\u5fd9\u788c",error:"\u9304\u88fd\u4f5c\u696d\u5931\u6557\u3002\u8acb\u518d\u6b21\u91cd\u8a66\u3002",expandedOff:"\u9304\u5f71\u5df2\u7d93\u505c\u6b62",expandedOn:"\u6b64\u6703\u8b70\u76ee\u524d\u6b63\u5728\u9304\u5f71\u3002",expandedPending:"\u9304\u5f71\u6b63\u5728\u555f\u52d5\u2026",failedToStart:"\u9304\u88fd\u555f\u52d5\u5931\u6557",fileSharingdescription:"",live:"\u76f4\u64ad",loggedIn:"\u4ee5 {{userName}} \u767b\u5165",off:"\u9304\u88fd\u4f5c\u696d\u5df2\u7d93\u505c\u6b62",on:"\u9304\u88fd\u4f5c\u696d\u4e2d",pending:"\u6e96\u5099\u9304\u5f71\u6b64\u6703\u8b70\u2026",rec:"REC \u9304\u5f71",serviceDescription:"",serviceName:"\u9304\u88fd\u4f5c\u696d\u670d\u52d9",signIn:"jde bp ",signOut:"\u767b\u51fa",unavailable:"\u5594\u54e6\uff01{{serviceName}} \u76ee\u524d\u7121\u6cd5\u4f7f\u7528\u3002\u6211\u5011\u6b63\u5728\u89e3\u6c7a\u6b64\u554f\u984c\uff0c\u8acb\u7a0d\u5f8c\u518d\u8a66\u3002",unavailableTitle:"\u9304\u88fd\u4f5c\u696d\u7121\u6cd5\u4f7f\u7528"},sectionList:{pullToRefresh:"\u4e0b\u6ed1\u4ee5\u91cd\u65b0\u6574\u7406"},settings:{calendar:{about:"\u6b64 {{appName}} \u884c\u4e8b\u66c6\u6574\u5408\u662f\u5b89\u5168\u5b58\u53d6\u4f60\u7684\u884c\u4e8b\u66c6\uff0c\u6240\u4ee5\u53ef\u4ee5\u8b80\u53d6\u5373\u5c07\u767c\u751f\u7684\u4e8b\u4ef6\u3002",disconnect:"\u4e2d\u65b7\u9023\u63a5",microsoftSignIn:"\u4f7f\u7528 Microsoft \u5e33\u6236\u767b\u5165",signedIn:"\u76ee\u524d\u662f\u4ee5 {{email}} \u4f86\u5b58\u53d6\u884c\u4e8b\u66c6\u4e8b\u4ef6\u3002\u9ede\u6309\u4e0b\u65b9\u53d6\u6d88\u9023\u63a5\u9215\u53ef\u4ee5\u505c\u6b62\u5b58\u53d6\u884c\u4e8b\u66c6\u4e8b\u4ef6\u3002",title:"\u65e5\u66c6"},devices:"\u88dd\u7f6e",followMe:"\u5168\u90e8\u4eba\u8ddf\u96a8\u4eff\u7167\u6211",language:"\u8a9e\u8a00",loggedIn:"\u4ee5 {{name}} \u767b\u5165",moderator:"\u4e3b\u6301\u4eba",more:"\u66f4\u591a",name:"\u540d\u7a31",noDevice:"\u7121",selectAudioOutput:"\u97f3\u8a0a\u8f38\u51fa",selectCamera:"\u651d\u5f71\u88dd\u7f6e",selectMic:"\u9ea5\u514b\u98a8",startAudioMuted:"\u5168\u90e8\u4eba\u555f\u52d5\u6642\u8655\u65bc\u975c\u97f3",startVideoMuted:"\u5168\u90e8\u4eba\u555f\u52d5\u6642\u96b1\u85cf\u8996\u8a0a\u756b\u9762",title:"\u8a2d\u7f6e"},settingsView:{alertOk:"\u78ba\u8a8d",alertTitle:"\u8b66\u544a",alertURLText:"\u6240\u8f38\u5165\u7684\u4f3a\u670d\u5668 URL \u662f\u7121\u6548\u7684",buildInfoSection:"",conferenceSection:"\u6703\u8b70",displayName:"\u986f\u793a\u540d\u7a31",email:"\u96fb\u5b50\u90f5\u4ef6",header:"\u8a2d\u7f6e",profileSection:"\u7c21\u4ecb",serverURL:"\u4f3a\u670d\u5668 URL",startWithAudioMuted:"\u555f\u52d5\u4e26\u97f3\u8a0a\u975c\u97f3",startWithVideoMuted:"\u555f\u52d5\u4e26\u8996\u8a0a\u975c\u97f3",version:""},share:{dialInfoText:"",mainText:"\u9ede\u6309\u4ee5\u4e0b\u9023\u7d50\u53c3\u52a0\u6703\u8b70\uff1a{{roomUrl}}\n"},speaker:"\u767c\u8a00\u8005",speakerStats:{hours:"{{count}}h",minutes:"{{count}}m",name:"\u540d\u7a31",seconds:"{{count}}s",speakerStats:"\u767c\u8a00\u8005\u6578\u64da",speakerTime:"\u767c\u8a00\u8005\u6642\u9593"},startupoverlay:{policyText:" ",title:"{{app}} \u9700\u8981\u4f7f\u7528\u60a8\u7684\u9ea5\u514b\u98a8\u548c\u651d\u5f71\u88dd\u7f6e\u3002"},suspendedoverlay:{rejoinKeyTitle:"\u91cd\u65b0\u52a0\u5165",text:"\u6309\u4e0b \u91cd\u65b0\u52a0\u5165 \u6309\u9215\u91cd\u65b0\u9023\u63a5\u3002",title:"\u7531\u65bc\u96fb\u8166\u9032\u5165\u4f11\u7720\uff0c\u60a8\u7684\u8996\u8a0a\u901a\u8a71\u5df2\u7d93\u4e2d\u65b7\u3002"},toolbar:{accessibilityLabel:{audioOnly:"\u5207\u63db\u50c5\u6709\u8072\u97f3",audioRoute:"\u9078\u64c7\u8072\u97f3\u88dd\u7f6e",callQuality:"",cc:"\u5207\u63db\u5b57\u5e55",chat:"\u5207\u63db\u804a\u5929\u8996\u7a97",document:"\u5207\u63db\u5206\u4eab\u7684\u6587\u4ef6",feedback:"\u7559\u8a00\u56de\u5831",fullScreen:"\u5207\u63db\u5168\u87a2\u5e55",hangup:"\u96e2\u958b\u4f86\u96fb",invite:"\u9080\u8acb\u4eba\u54e1",kick:"",localRecording:"\u5207\u63db\u672c\u5730\u7aef\u9304\u5f71\u63a7\u5236",lockRoom:"",moreActions:"\u5207\u63db\u66f4\u591a\u52d5\u4f5c\u529f\u80fd\u8868",moreActionsMenu:"\u66f4\u591a\u52d5\u4f5c\u529f\u80fd\u8868",mute:"\u5207\u63db\u975c\u97f3",pip:"\u5207\u63db\u5b50\u6bcd\u756b\u9762\u6a21\u5f0f",profile:"\u7de8\u8f2f\u60a8\u7684\u7c21\u4ecb",raiseHand:"\u5207\u63db\u8209\u624b",recording:"\u5207\u63db\u9304\u5f71",remoteMute:"",Settings:"\u5207\u63db\u8a2d\u7f6e",sharedvideo:"\u5207\u63db Youtube \u5f71\u7247\u5206\u4eab",shareRoom:"\u9080\u8acb\u67d0\u4eba",shareYourScreen:"\u5207\u63db\u87a2\u5e55\u5206\u4eab",shortcuts:"\u5207\u63db\u5feb\u6377\u9375",show:"",speakerStats:"\u5207\u63db\u767c\u8a00\u4eba\u7d71\u8a08",tileView:"\u5207\u63db\u5e73\u92ea\u6aa2\u8996",toggleCamera:"\u5207\u63db\u651d\u5f71\u6a5f",videomute:"\u5207\u63db\u975c\u97f3\u8996\u8a0a",videoblur:""},addPeople:"\u65b0\u589e\u4eba\u54e1\u5230\u60a8\u7684\u901a\u8a71\u4e2d",audioOnlyOff:"\u95dc\u9589\u50c5\u7528\u97f3\u8a0a\u6a21\u5f0f",audioOnlyOn:"\u95dc\u9589\u50c5\u7528\u97f3\u8a0a\u6a21\u5f0f",audioRoute:"\u9078\u64c7\u8072\u97f3\u88dd\u7f6e",authenticate:"\u9a57\u8b49",callQuality:"\u7ba1\u7406\u901a\u8a71\u54c1\u8cea",chat:"\u958b\u555f/\u95dc\u9589 \u804a\u5929",closeChat:"",documentClose:"\u95dc\u9589\u5206\u4eab\u7684\u6587\u4ef6\u6a94\u6848",documentOpen:"\u958b\u555f\u5206\u4eab\u7684\u6587\u4ef6\u6a94\u6848",enterFullScreen:"\u89c0\u770b\u5168\u87a2\u5e55",enterTileView:"",exitFullScreen:"\u8df3\u51fa\u5168\u87a2\u5e55",exitTileView:"",feedback:"\u7559\u8a00\u56de\u5831",hangup:"\u7559\u8a00",invite:"\u9080\u8acb\u4eba\u54e1",login:"\u767b\u5165",logout:"\u767b\u51fa",lowerYourHand:"",moreActions:"\u66f4\u591a\u52d5\u4f5c",mute:"\u975c\u97f3 / \u89e3\u9664\u975c\u97f3",openChat:"",pip:"\u9032\u5165\u5b50\u6bcd\u756b\u6a21\u5f0f",profile:"\u7de8\u8f2f\u60a8\u7684\u7c21\u4ecb",raiseHand:"\u8209\u624b/\u53d6\u6d88 \u8acb\u6c42\u767c\u8a00",raiseYourHand:"",Settings:"\u8a2d\u7f6e",sharedvideo:"\u5206\u4eab YouTube \u8996\u8a0a",shareRoom:"\u9080\u8acb\u67d0\u4eba",shortcuts:"\u67e5\u770b\u5feb\u6377\u9375",speakerStats:"\u767c\u8a00\u8005\u6578\u64da",startScreenSharing:"",startSubtitles:"",stopScreenSharing:"",stopSubtitles:"",stopSharedVideo:"\u505c\u6b62 YouTube \u8996\u8a0a",talkWhileMutedPopup:"\u60a8\u8981\u767c\u8a00\u55ce? \u76ee\u524d\u60a8\u8655\u65bc\u975c\u97f3\u3002",tileViewToggle:"\u5207\u63db\u5e73\u92ea\u6aa2\u8996",toggleCamera:"\u5207\u63db\u651d\u5f71\u6a5f",videomute:"\u555f\u52d5/\u505c\u6b62 \u651d\u5f71\u88dd\u7f6e",startvideoblur:"",stopvideoblur:""},transcribing:{ccButtonTooltip:"",error:"\u9304\u5f71\u4f5c\u696d\u5931\u6557\u3002\u8acb\u91cd\u8a66\u3002",expandedLabel:"\u8f49\u9304\u76ee\u524d\u958b\u555f",failedToStart:"\u8f49\u9304\u555f\u52d5\u5931\u6557",labelToolTip:"\u6b64\u6703\u8b70\u6b63\u88ab\u8f49\u9304",off:"\u8f49\u9304\u5df2\u505c\u6b62",pending:"\u6b63\u5728\u6e96\u5099\u8f49\u9304\u6703\u8b70\u2026",start:"\u555f\u52d5\u986f\u793a\u5b57\u5e55",stop:"\u505c\u6b62\u986f\u793a\u5b57\u5e55",tr:"TR \u8f49\u9304"},userMedia:{androidGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 \u5141\u8a31",chromeGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 \u5141\u8a31",edgeGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 \u662f\u7684",electronGrantPermissions:"\u8acb\u5141\u8a31\u6b0a\u9650\u4f7f\u7528\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u548c\u9ea5\u514b\u98a8",firefoxGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7\u5206\u4eab\u8a2d\u5099 ",iexplorerGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 OK",nwjsGrantPermissions:"\u8acb\u5141\u8a31\u6b0a\u9650\u4f7f\u7528\u60a8\u7684\u651d\u5f71\u88dd\u7f6e\u548c\u9ea5\u514b\u98a8",operaGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 \u5141\u8a31","react-nativeGrantPermissions":"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 \u5141\u8a31",safariGrantPermissions:"\u7576\u700f\u89bd\u5668\u8981\u6c42\u6b0a\u9650\u5141\u8a31\u6642\uff0c\u8acb\u9078\u64c7 OK"},videoSIPGW:{busy:"\u6211\u5011\u6b63\u5728\u6e05\u7406\u91cb\u653e\u8cc7\u6e90\u3002\u8acb\u904e\u5e7e\u5206\u9418\u5f8c\u518d\u8a66\u3002",busyTitle:"\u6703\u8b70\u5ba4\u670d\u52d9\u6b63\u8655\u65bc\u5fd9\u788c\u4e2d",errorAlreadyInvited:"{{displayName}} \u5df2\u53d7\u9080\u8acb",errorInvite:"\u6703\u8b70\u5c1a\u672a\u958b\u59cb\uff0c\u8acb\u7a0d\u5f8c\u518d\u4f86\u3002",errorInviteFailed:"\u6211\u5011\u6b63\u5728\u89e3\u6c7a\u554f\u984c\u3002\u8acb\u7a0d\u5f8c\u518d\u8a66\u3002",errorInviteFailedTitle:"\u9080\u8acb {{displayName}} \u5931\u6557",errorInviteTitle:"\u932f\u8aa4\u9080\u8acb\u6703\u8b70\u5ba4",pending:"{{displayName}} \u5df2\u7d93\u9080\u8acb"},videoStatus:{audioOnly:"AUD \u8072\u97f3",audioOnlyExpanded:"\u4f60\u8655\u65bc\u50c5\u7528\u97f3\u8a0a\u6a21\u5f0f\u3002\u9019\u500b\u6a21\u5f0f\u7bc0\u7701\u983b\u5bec\uff0c\u4f46\u7121\u6cd5\u770b\u898b\u4ed6\u4eba\u5f71\u50cf\u3002",callQuality:"",hd:"HD \u9ad8\u6e05",highDefinition:"\u9ad8\u6e05\u54c1\u8cea HD",labelTooiltipNoVideo:"\u6c92\u6709\u8996\u8a0a",labelTooltipAudioOnly:"\u50c5\u6709\u97f3\u8a0a\u6a21\u5f0f\u5df2\u7d93\u555f\u7528",ld:"LD \u4f4e\u6e05",lowDefinition:"\u4f4e\u6e05\u54c1\u8cea LD",onlyAudioAvailable:"\u50c5\u6709\u97f3\u8a0a\u53ef\u4ee5\u4f7f\u7528",onlyAudioSupported:"\u5728\u6b64\u700f\u89bd\u5668\u6211\u5011\u50c5\u652f\u63f4\u97f3\u8a0a\u529f\u80fd\u3002",p2pEnabled:"\u9ede\u5c0d\u9ede\u529f\u80fd\u5df2\u7d93\u555f\u7528",p2pVideoQualityDescription:"",recHighDefinitionOnly:"\u5c07\u6703\u504f\u597d\u4f7f\u7528\u9ad8\u6e05\u6a21\u5f0f HD\u3002",sd:"SD \u6a19\u6e05",standardDefinition:"\u6a19\u6e05\u54c1\u8cea SD"},videothumbnail:{domute:"\u975c\u97f3",flip:"\u7ffb\u8f49",kick:"\u8e22\u51fa",moderator:"\u4e3b\u6301\u4eba",mute:"",muted:"\u8655\u65bc\u975c\u97f3",remoteControl:"\u9060\u7aef\u63a7\u5236",show:"",videomute:""},welcomepage:{accessibilityLabel:{join:"\u8f15\u89f8\u5373\u53ef\u53c3\u52a0",roomname:"\u8f38\u5165\u6703\u8b70\u5ba4\u540d\u7a31"},appDescription:"\u5feb\u4f86\u4f7f\u7528\u5427\uff0c\u5718\u968a\u5168\u90e8\u6210\u54e1\u4f7f\u7528\u8996\u8a0a\u901a\u8a71\uff0c\u53ef\u4ee5\u9080\u8acb\u4efb\u4f55\u60a8\u6240\u8a8d\u8b58\u7684\u4eba\u3002 {{app}} \u662f\u4e00\u5957\u5b8c\u5168\u52a0\u5bc6\u3001100% \u958b\u653e\u6e90\u78bc\u7684\u8996\u8a0a\u6703\u8b70\u89e3\u6c7a\u65b9\u6848\u3002\u7121\u9700\u8a3b\u518a\u5e33\u865f\uff0c\u7121\u6642\u7121\u523b\u4e0d\u5206\u65e5\u591c\u5747\u53ef\u514d\u8cbb\u4f7f\u7528\u3002",audioVideoSwitch:{audio:"\u8a9e\u97f3",video:"\u8996\u8a0a"},calendar:"\u65e5\u66c6",connectCalendarButton:"\u9023\u63a5\u4f60\u7684\u884c\u4e8b\u66c6",connectCalendarText:"",enterRoomTitle:"\u555f\u52d5\u65b0\u7684\u6703\u8b70",go:"\u958b\u59cb",join:"\u52a0\u5165",info:"",privacy:"\u96b1\u79c1",recentList:"\u6700\u8fd1\u4f7f\u7528",recentListDelete:"\u522a\u9664",recentListEmpty:"\u76ee\u524d\u6700\u8fd1\u4f7f\u7528\u662f\u7a7a\u767d\u7684\u3002\u8207\u4f60\u7684\u5718\u968a\u6210\u54e1\u804a\u5929\uff0c\u5373\u6703\u5728\u6b64\u8655\u627e\u5230\u6700\u8fd1\u7684\u6703\u8b70\u3002",reducedUIText:"",roomname:"\u8f38\u5165\u6703\u8b70\u5ba4\u540d\u7a31",roomnameHint:"\u8acb\u8f38\u5165\u60a8\u60f3\u52a0\u5165\u7684\u6703\u8b70\u5ba4 URL \u7db2\u5740\u6216\u540d\u7a31\u3002\u60a8\u53ef\u4ee5\u7528\u500b\u540d\u7a31\u4f86\u5efa\u7acb\u6703\u8b70\u5ba4\uff0c\u53ea\u8981\u5176\u4ed6\u4eba\u8f38\u5165\u76f8\u540c\u7684\u540d\u7a31\u5c31\u80fd\u52a0\u5165\u6703\u8b70\u5ba4\u5594\u3002",sendFeedback:"\u767c\u9001\u56de\u5831",terms:"\u689d\u6b3e",title:"\u5b89\u5168\u3001\u5168\u529f\u80fd\u3001\u5b8c\u5168\u514d\u8cbb\u7684\u8996\u8a0a\u6703\u8b70"}}},684,[]); -__d(function(g,r,i,a,m,e,d){!(function(t,n){if('function'==typeof define&&define.amd)define(['moment'],n);else if('object'==typeof e)try{m.exports=n(r(d[0]))}catch(t){m.exports=n}t&&(t.momentDurationFormatSetup=t.moment?n(t.moment):n)})(this,function(t){var n=!1,u=!1,o="escape years months weeks days hours minutes seconds milliseconds general".split(" "),l=[{type:"seconds",targets:[{type:"minutes",value:60},{type:"hours",value:3600},{type:"days",value:86400},{type:"weeks",value:604800},{type:"months",value:2678400},{type:"years",value:31536e3}]},{type:"minutes",targets:[{type:"hours",value:60},{type:"days",value:1440},{type:"weeks",value:10080},{type:"months",value:44640},{type:"years",value:525600}]},{type:"hours",targets:[{type:"days",value:24},{type:"weeks",value:168},{type:"months",value:744},{type:"years",value:8760}]},{type:"days",targets:[{type:"weeks",value:7},{type:"months",value:31},{type:"years",value:365}]},{type:"months",targets:[{type:"years",value:12}]}];function s(t,n){return!(n.length>t.length)&&-1!==t.indexOf(n)}function c(t){for(var n="";t;)n+="0",t-=1;return n}function p(t){for(var n=t.split("").reverse(),u=0,o=!0;o&&u0&&(L.maximumSignificantDigits=w),!u){var b=I({},n);b.useGrouping=!1,b.decimalSeparator=".",t=parseFloat(f(t,b),10)}return t.toLocaleString(o,L)}var M=(w?t.toPrecision(w+1):t.toFixed(_+1)).split("e");h=M[1]||"",s=(M=M[0].split("."))[1]||"";var k=(l=M[0]||"").length,T=s.length,F=k+T,j=l+s;(w&&F===w+1||!w&&T===_+1)&&((j=p(j)).length===F+1&&(k+=1),T&&(j=j.slice(0,-1)),l=j.slice(0,k),s=j.slice(k)),w&&(s=s.replace(/0*$/,""));var G=parseInt(h,10);G>0?s.length<=G?(l+=s+=c(G-s.length),s=""):(l+=s.slice(0,G),s=s.slice(G)):G<0&&(s=c(Math.abs(G)-l.length)+l+s,l="0"),w||((s=s.slice(0,_)).length<_&&(s+=c(_-s.length)),l.lengthn.label.length?-1:t.label.length0,ee=Z?c.precision:0,te=ee,ne=c.minValue,ie=!1,re=c.maxValue,ae=!1,ue=c.useToLocaleString,oe=c.groupingSeparator,le=c.decimalSeparator,se=c.grouping;ue=ue&&n;var ce=c.trim;w(ce)&&(ce=ce.join(" ")),null===ce&&(C||re||Z)&&(ce="all"),null!==ce&&!0!==ce&&"left"!==ce&&"right"!==ce||(ce="large"),!1===ce&&(ce="");var me=function(t){return t.test(ce)},ge=/both/,pe=/^all|[^sm]all/,fe=C>0||G([/large/,ge,pe],me),he=G([/small/,ge,pe],me),ye=G([/mid/,pe],me),de=G([/final/,pe],me),ve=L(R.match(K),function(t,n){var u=$(t);return"*"===t.slice(0,1)&&(t=t.slice(1),"escape"!==u&&"general"!==u&&W.push(u)),{index:n,length:t.length,text:"",token:"escape"===u?t.replace(H.escape,"$1"):t,type:"escape"===u||"general"===u?null:u}}),Se={index:0,length:0,token:"",text:"",type:null},we=[];z&&ve.reverse(),D(ve,function(t){if(t.type)return(Se.type||Se.text)&&we.push(Se),void(Se=t);z?Se.text=t.token+Se.text:Se.text+=t.token}),(Se.type||Se.text)&&we.push(Se),z&&we.reverse();var Ve=T(o,k(M(b(we,"type"))));if(!Ve.length)return b(we,"text").join("");Ve=L(Ve,function(t,n){var u,o=n+1===Ve.length,l=!n;u="years"===t||"months"===t?P.as(t):E.as(t);var s=Math.floor(u),p=u-s,f=x(we,function(n){return t===n.type});return l&&re&&u>re&&(ae=!0),o&&ne&&Math.abs(c.duration.as(t))1&&(N=!0),E.subtract(s,t),P.subtract(s,t),{rawValue:u,wholeValue:s,decimalValue:o?p:0,isSmallest:o,isLargest:l,type:t,tokenLength:f.length}});var _e,xe=X?Math.floor:Math.round,De=function(t,n){var u=Math.pow(10,n);return xe(t*u)/u},Le=!1,be=!1,Me=function(t,n){var u={useGrouping:Q,groupingSeparator:oe,decimalSeparator:le,grouping:se,useToLocaleString:ue};return Z&&(ee<=0?(t.rawValue=0,t.wholeValue=0,t.decimalValue=0):(u.maximumSignificantDigits=ee,t.significantDigits=ee)),ae&&!be&&(t.isLargest?(t.wholeValue=re,t.decimalValue=0):(t.wholeValue=0,t.decimalValue=0)),ie&&!be&&(t.isSmallest?(t.wholeValue=ne,t.decimalValue=0):(t.wholeValue=0,t.decimalValue=0)),t.isSmallest||t.significantDigits&&t.significantDigits-t.wholeValue.toString().length<=0?J<0?t.value=De(t.wholeValue,J):0===J?t.value=xe(t.wholeValue+t.decimalValue):Z?(t.value=X?De(t.rawValue,ee-t.wholeValue.toString().length):t.rawValue,t.wholeValue&&(ee-=t.wholeValue.toString().length)):(u.fractionDigits=J,t.value=X?t.wholeValue+De(t.decimalValue,J):t.wholeValue+t.decimalValue):Z&&t.wholeValue?(t.value=Math.round(De(t.wholeValue,t.significantDigits-t.wholeValue.toString().length)),ee-=t.wholeValue.toString().length):t.value=t.wholeValue,t.tokenLength>1&&(N||Le)&&(u.minimumIntegerDigits=t.tokenLength,be&&u.maximumSignificantDigits0||""===ce||x(W,t.type)||x(U,t.type))&&(Le=!0),t.formattedValue=f(t.value,u,q),u.useGrouping=!1,u.decimalSeparator=".",t.formattedValueEn=f(t.value,u,"en"),2===t.tokenLength&&"milliseconds"===t.type&&(t.formattedValueMS=f(t.value,{minimumIntegerDigits:3,useGrouping:!1},"en").slice(0,2)),t};if((Ve=M(Ve=L(Ve,Me))).length>1){var ke=function(t){return x(Ve,function(n){return n.type===t})};D(l,function(t){var n=ke(t.type);n&&D(t.targets,function(t){var u=ke(t.type);u&&parseInt(n.formattedValueEn,10)===t.value&&(n.rawValue=0,n.wholeValue=0,n.decimalValue=0,u.rawValue+=1,u.wholeValue+=1,u.decimalValue=0,u.formattedValueEn=u.wholeValue.toString(),be=!0)})})}return be&&(Le=!1,ee=te,Ve=M(Ve=L(Ve,Me))),!U||ae&&!c.trim?(fe&&(Ve=F(Ve,function(t){return!t.isSmallest&&!t.wholeValue&&!x(W,t.type)})),C&&Ve.length&&(Ve=Ve.slice(0,C)),he&&Ve.length>1&&(_e=function(t){return!t.wholeValue&&!x(W,t.type)&&!t.isLargest},Ve=F(Ve.slice().reverse(),_e).reverse()),ye&&(Ve=M(Ve=L(Ve,function(t,n){return n>0&&n ",ae=!1,ie=!1),_&&(n.value>0||""===ce||x(W,n.type)||x(U,n.type))&&(u+="-",_=!1),"milliseconds"===t.type&&n.formattedValueMS?u+=n.formattedValueMS:u+=n.formattedValue,z||(u+=t.text),u})).join("").replace(/(,| |:|\.)*$/,"").replace(/^(,| |:|\.)*/,""))}function H(){var t=this.duration,n=function(n){return t._data[n]},u=x(this.types,n),o=_(this.types,n);switch(u){case"milliseconds":return"S __";case"seconds":case"minutes":return"*_MS_";case"hours":return"_HMS_";case"days":if(u===o)return"d __";case"weeks":return u===o?"w __":(null===this.trim&&(this.trim="both"),"w __, d __, h __");case"months":if(u===o)return"M __";case"years":return u===o?"y __":(null===this.trim&&(this.trim="both"),"y __, M __, d __");default:return null===this.trim&&(this.trim="both"),"y __, d __, h __, m __, s __"}}function $(t){if(!t)throw"Moment Duration Format init cannot find moment instance.";t.duration.format=P,t.duration.fn.format=O,t.duration.fn.format.defaults={trim:null,stopTrim:null,largest:null,maxValue:null,minValue:null,precision:0,trunc:!1,forceLength:null,userLocale:null,usePlural:!0,useLeftUnits:!1,useGrouping:!0,useSignificantDigits:!1,template:H,useToLocaleString:!0,groupingSeparator:",",decimalSeparator:".",grouping:[3]},t.updateLocale('en',S)}return n=!!((v=(v=!0)&&E())&&(v=(v=(v=v&&"1"===1..toLocaleString("en",{minimumIntegerDigits:1}))&&"01"===1..toLocaleString("en",{minimumIntegerDigits:2}))&&"001"===1..toLocaleString("en",{minimumIntegerDigits:3}))&&(v=(v=(v=(v=v&&"100"===99.99.toLocaleString("en",{maximumFractionDigits:0,minimumFractionDigits:0}))&&"100.0"===99.99.toLocaleString("en",{maximumFractionDigits:1,minimumFractionDigits:1}))&&"99.99"===99.99.toLocaleString("en",{maximumFractionDigits:2,minimumFractionDigits:2}))&&"99.990"===99.99.toLocaleString("en",{maximumFractionDigits:3,minimumFractionDigits:3}))&&(v=(v=(v=(v=(v=v&&"100"===99.99.toLocaleString("en",{maximumSignificantDigits:1}))&&"100"===99.99.toLocaleString("en",{maximumSignificantDigits:2}))&&"100"===99.99.toLocaleString("en",{maximumSignificantDigits:3}))&&"99.99"===99.99.toLocaleString("en",{maximumSignificantDigits:4}))&&"99.99"===99.99.toLocaleString("en",{maximumSignificantDigits:5}))&&(v=(v=v&&"1,000"===1e3.toLocaleString("en",{useGrouping:!0}))&&"1000"===1e3.toLocaleString("en",{useGrouping:!1}))),u=n&&"3.6"===3.55.toLocaleString("en",{useGrouping:!1,minimumIntegerDigits:1,minimumFractionDigits:1,maximumFractionDigits:1}),$(t),$})},685,[609]); -__d(function(g,r,i,a,m,e,d){var _,t;_=this,t=function(_){'use strict';return _.defineLocale('bg',{months:'\u044f\u043d\u0443\u0430\u0440\u0438_\u0444\u0435\u0432\u0440\u0443\u0430\u0440\u0438_\u043c\u0430\u0440\u0442_\u0430\u043f\u0440\u0438\u043b_\u043c\u0430\u0439_\u044e\u043d\u0438_\u044e\u043b\u0438_\u0430\u0432\u0433\u0443\u0441\u0442_\u0441\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438_\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438_\u043d\u043e\u0435\u043c\u0432\u0440\u0438_\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438'.split('_'),monthsShort:'\u044f\u043d\u0440_\u0444\u0435\u0432_\u043c\u0430\u0440_\u0430\u043f\u0440_\u043c\u0430\u0439_\u044e\u043d\u0438_\u044e\u043b\u0438_\u0430\u0432\u0433_\u0441\u0435\u043f_\u043e\u043a\u0442_\u043d\u043e\u0435_\u0434\u0435\u043a'.split('_'),weekdays:'\u043d\u0435\u0434\u0435\u043b\u044f_\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u043d\u0438\u043a_\u0432\u0442\u043e\u0440\u043d\u0438\u043a_\u0441\u0440\u044f\u0434\u0430_\u0447\u0435\u0442\u0432\u044a\u0440\u0442\u044a\u043a_\u043f\u0435\u0442\u044a\u043a_\u0441\u044a\u0431\u043e\u0442\u0430'.split('_'),weekdaysShort:'\u043d\u0435\u0434_\u043f\u043e\u043d_\u0432\u0442\u043e_\u0441\u0440\u044f_\u0447\u0435\u0442_\u043f\u0435\u0442_\u0441\u044a\u0431'.split('_'),weekdaysMin:'\u043d\u0434_\u043f\u043d_\u0432\u0442_\u0441\u0440_\u0447\u0442_\u043f\u0442_\u0441\u0431'.split('_'),longDateFormat:{LT:'H:mm',LTS:'H:mm:ss',L:'D.MM.YYYY',LL:'D MMMM YYYY',LLL:'D MMMM YYYY H:mm',LLLL:'dddd, D MMMM YYYY H:mm'},calendar:{sameDay:'[\u0414\u043d\u0435\u0441 \u0432] LT',nextDay:'[\u0423\u0442\u0440\u0435 \u0432] LT',nextWeek:'dddd [\u0432] LT',lastDay:'[\u0412\u0447\u0435\u0440\u0430 \u0432] LT',lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return'[\u0412 \u0438\u0437\u043c\u0438\u043d\u0430\u043b\u0430\u0442\u0430] dddd [\u0432] LT';case 1:case 2:case 4:case 5:return'[\u0412 \u0438\u0437\u043c\u0438\u043d\u0430\u043b\u0438\u044f] dddd [\u0432] LT'}},sameElse:'L'},relativeTime:{future:'\u0441\u043b\u0435\u0434 %s',past:'\u043f\u0440\u0435\u0434\u0438 %s',s:'\u043d\u044f\u043a\u043e\u043b\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434\u0438',m:'\u043c\u0438\u043d\u0443\u0442\u0430',mm:'%d \u043c\u0438\u043d\u0443\u0442\u0438',h:'\u0447\u0430\u0441',hh:'%d \u0447\u0430\u0441\u0430',d:'\u0434\u0435\u043d',dd:'%d \u0434\u043d\u0438',M:'\u043c\u0435\u0441\u0435\u0446',MM:'%d \u043c\u0435\u0441\u0435\u0446\u0430',y:'\u0433\u043e\u0434\u0438\u043d\u0430',yy:'%d \u0433\u043e\u0434\u0438\u043d\u0438'},dayOfMonthOrdinalParse:/\d{1,2}-(\u0435\u0432|\u0435\u043d|\u0442\u0438|\u0432\u0438|\u0440\u0438|\u043c\u0438)/,ordinal:function(_){var t=_%10,s=_%100;return 0===_?_+'-\u0435\u0432':0===s?_+'-\u0435\u043d':s>10&&s<20?_+'-\u0442\u0438':1===t?_+'-\u0432\u0438':2===t?_+'-\u0440\u0438':7===t||8===t?_+'-\u043c\u0438':_+'-\u0442\u0438'},week:{dow:1,doy:7}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(_.moment)},686,[609]); -__d(function(g,r,i,a,m,e,d){var n,t;n=this,t=function(n){'use strict';function t(n,t,_,o){var M={m:['eine Minute','einer Minute'],h:['eine Stunde','einer Stunde'],d:['ein Tag','einem Tag'],dd:[n+' Tage',n+' Tagen'],M:['ein Monat','einem Monat'],MM:[n+' Monate',n+' Monaten'],y:['ein Jahr','einem Jahr'],yy:[n+' Jahre',n+' Jahren']};return t?M[_][0]:M[_][1]}return n.defineLocale('de',{months:'Januar_Februar_M\xe4rz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),monthsShort:'Jan._Feb._M\xe4rz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'),monthsParseExact:!0,weekdays:'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),weekdaysShort:'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),weekdaysMin:'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D. MMMM YYYY',LLL:'D. MMMM YYYY HH:mm',LLLL:'dddd, D. MMMM YYYY HH:mm'},calendar:{sameDay:'[heute um] LT [Uhr]',sameElse:'L',nextDay:'[morgen um] LT [Uhr]',nextWeek:'dddd [um] LT [Uhr]',lastDay:'[gestern um] LT [Uhr]',lastWeek:'[letzten] dddd [um] LT [Uhr]'},relativeTime:{future:'in %s',past:'vor %s',s:'ein paar Sekunden',m:t,mm:'%d Minuten',h:t,hh:'%d Stunden',d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(n.moment)},687,[609]); -__d(function(g,r,i,a,m,e,d){var o,t;o=this,t=function(o){'use strict';return o.defineLocale('eo',{months:'januaro_februaro_marto_aprilo_majo_junio_julio_a\u016dgusto_septembro_oktobro_novembro_decembro'.split('_'),monthsShort:'jan_feb_mar_apr_maj_jun_jul_a\u016dg_sep_okt_nov_dec'.split('_'),weekdays:'diman\u0109o_lundo_mardo_merkredo_\u0135a\u016ddo_vendredo_sabato'.split('_'),weekdaysShort:'dim_lun_mard_merk_\u0135a\u016d_ven_sab'.split('_'),weekdaysMin:'di_lu_ma_me_\u0135a_ve_sa'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'YYYY-MM-DD',LL:'D[-a de] MMMM, YYYY',LLL:'D[-a de] MMMM, YYYY HH:mm',LLLL:'dddd, [la] D[-a de] MMMM, YYYY HH:mm'},meridiemParse:/[ap]\.t\.m/i,isPM:function(o){return'p'===o.charAt(0).toLowerCase()},meridiem:function(o,t,n){return o>11?n?'p.t.m.':'P.T.M.':n?'a.t.m.':'A.T.M.'},calendar:{sameDay:'[Hodia\u016d je] LT',nextDay:'[Morga\u016d je] LT',nextWeek:'dddd [je] LT',lastDay:'[Hiera\u016d je] LT',lastWeek:'[pasinta] dddd [je] LT',sameElse:'L'},relativeTime:{future:'post %s',past:'anta\u016d %s',s:'sekundoj',m:'minuto',mm:'%d minutoj',h:'horo',hh:'%d horoj',d:'tago',dd:'%d tagoj',M:'monato',MM:'%d monatoj',y:'jaro',yy:'%d jaroj'},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:'%da',week:{dow:1,doy:7}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(o.moment)},688,[609]); -__d(function(g,r,i,a,m,e,d){var o,n;o=this,n=function(o){'use strict';var n='ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'),t='ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'),s=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],u=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;return o.defineLocale('es',{months:'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),monthsShort:function(o,s){return o?/-MMM-/.test(s)?t[o.month()]:n[o.month()]:n},monthsRegex:u,monthsShortRegex:u,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:'domingo_lunes_martes_mi\xe9rcoles_jueves_viernes_s\xe1bado'.split('_'),weekdaysShort:'dom._lun._mar._mi\xe9._jue._vie._s\xe1b.'.split('_'),weekdaysMin:'do_lu_ma_mi_ju_vi_s\xe1'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'H:mm',LTS:'H:mm:ss',L:'DD/MM/YYYY',LL:'D [de] MMMM [de] YYYY',LLL:'D [de] MMMM [de] YYYY H:mm',LLLL:'dddd, D [de] MMMM [de] YYYY H:mm'},calendar:{sameDay:function(){return'[hoy a la'+(1!==this.hours()?'s':'')+'] LT'},nextDay:function(){return'[ma\xf1ana a la'+(1!==this.hours()?'s':'')+'] LT'},nextWeek:function(){return'dddd [a la'+(1!==this.hours()?'s':'')+'] LT'},lastDay:function(){return'[ayer a la'+(1!==this.hours()?'s':'')+'] LT'},lastWeek:function(){return'[el] dddd [pasado a la'+(1!==this.hours()?'s':'')+'] LT'},sameElse:'L'},relativeTime:{future:'en %s',past:'hace %s',s:'unos segundos',m:'un minuto',mm:'%d minutos',h:'una hora',hh:'%d horas',d:'un d\xeda',dd:'%d d\xedas',M:'un mes',MM:'%d meses',y:'un a\xf1o',yy:'%d a\xf1os'},dayOfMonthOrdinalParse:/\d{1,2}\xba/,ordinal:'%d\xba',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?n(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],n):n(o.moment)},689,[609]); -__d(function(g,r,i,a,m,e,d){var n,s;n=this,s=function(n){'use strict';return n.defineLocale('fr',{months:'janvier_f\xe9vrier_mars_avril_mai_juin_juillet_ao\xfbt_septembre_octobre_novembre_d\xe9cembre'.split('_'),monthsShort:'janv._f\xe9vr._mars_avr._mai_juin_juil._ao\xfbt_sept._oct._nov._d\xe9c.'.split('_'),monthsParseExact:!0,weekdays:'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),weekdaysShort:'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),weekdaysMin:'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD/MM/YYYY',LL:'D MMMM YYYY',LLL:'D MMMM YYYY HH:mm',LLLL:'dddd D MMMM YYYY HH:mm'},calendar:{sameDay:'[Aujourd\u2019hui \xe0] LT',nextDay:'[Demain \xe0] LT',nextWeek:'dddd [\xe0] LT',lastDay:'[Hier \xe0] LT',lastWeek:'dddd [dernier \xe0] LT',sameElse:'L'},relativeTime:{future:'dans %s',past:'il y a %s',s:'quelques secondes',m:'une minute',mm:'%d minutes',h:'une heure',hh:'%d heures',d:'un jour',dd:'%d jours',M:'un mois',MM:'%d mois',y:'un an',yy:'%d ans'},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(n,s){switch(s){case'D':return n+(1===n?'er':'');default:case'M':case'Q':case'DDD':case'd':return n+(1===n?'er':'e');case'w':case'W':return n+(1===n?'re':'e')}},week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?s(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],s):s(n.moment)},690,[609]); -__d(function(g,r,i,a,m,e,d){var _,t;_=this,t=function(_){'use strict';return _.defineLocale('hy-am',{months:{format:'\u0570\u0578\u0582\u0576\u057e\u0561\u0580\u056b_\u0583\u0565\u057f\u0580\u057e\u0561\u0580\u056b_\u0574\u0561\u0580\u057f\u056b_\u0561\u057a\u0580\u056b\u056c\u056b_\u0574\u0561\u0575\u056b\u057d\u056b_\u0570\u0578\u0582\u0576\u056b\u057d\u056b_\u0570\u0578\u0582\u056c\u056b\u057d\u056b_\u0585\u0563\u0578\u057d\u057f\u0578\u057d\u056b_\u057d\u0565\u057a\u057f\u0565\u0574\u0562\u0565\u0580\u056b_\u0570\u0578\u056f\u057f\u0565\u0574\u0562\u0565\u0580\u056b_\u0576\u0578\u0575\u0565\u0574\u0562\u0565\u0580\u056b_\u0564\u0565\u056f\u057f\u0565\u0574\u0562\u0565\u0580\u056b'.split('_'),standalone:'\u0570\u0578\u0582\u0576\u057e\u0561\u0580_\u0583\u0565\u057f\u0580\u057e\u0561\u0580_\u0574\u0561\u0580\u057f_\u0561\u057a\u0580\u056b\u056c_\u0574\u0561\u0575\u056b\u057d_\u0570\u0578\u0582\u0576\u056b\u057d_\u0570\u0578\u0582\u056c\u056b\u057d_\u0585\u0563\u0578\u057d\u057f\u0578\u057d_\u057d\u0565\u057a\u057f\u0565\u0574\u0562\u0565\u0580_\u0570\u0578\u056f\u057f\u0565\u0574\u0562\u0565\u0580_\u0576\u0578\u0575\u0565\u0574\u0562\u0565\u0580_\u0564\u0565\u056f\u057f\u0565\u0574\u0562\u0565\u0580'.split('_')},monthsShort:'\u0570\u0576\u057e_\u0583\u057f\u0580_\u0574\u0580\u057f_\u0561\u057a\u0580_\u0574\u0575\u057d_\u0570\u0576\u057d_\u0570\u056c\u057d_\u0585\u0563\u057d_\u057d\u057a\u057f_\u0570\u056f\u057f_\u0576\u0574\u0562_\u0564\u056f\u057f'.split('_'),weekdays:'\u056f\u056b\u0580\u0561\u056f\u056b_\u0565\u0580\u056f\u0578\u0582\u0577\u0561\u0562\u0569\u056b_\u0565\u0580\u0565\u0584\u0577\u0561\u0562\u0569\u056b_\u0579\u0578\u0580\u0565\u0584\u0577\u0561\u0562\u0569\u056b_\u0570\u056b\u0576\u0563\u0577\u0561\u0562\u0569\u056b_\u0578\u0582\u0580\u0562\u0561\u0569_\u0577\u0561\u0562\u0561\u0569'.split('_'),weekdaysShort:'\u056f\u0580\u056f_\u0565\u0580\u056f_\u0565\u0580\u0584_\u0579\u0580\u0584_\u0570\u0576\u0563_\u0578\u0582\u0580\u0562_\u0577\u0562\u0569'.split('_'),weekdaysMin:'\u056f\u0580\u056f_\u0565\u0580\u056f_\u0565\u0580\u0584_\u0579\u0580\u0584_\u0570\u0576\u0563_\u0578\u0582\u0580\u0562_\u0577\u0562\u0569'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D MMMM YYYY \u0569.',LLL:'D MMMM YYYY \u0569., HH:mm',LLLL:'dddd, D MMMM YYYY \u0569., HH:mm'},calendar:{sameDay:'[\u0561\u0575\u057d\u0585\u0580] LT',nextDay:'[\u057e\u0561\u0572\u0568] LT',lastDay:'[\u0565\u0580\u0565\u056f] LT',nextWeek:function(){return'dddd [\u0585\u0580\u0568 \u056a\u0561\u0574\u0568] LT'},lastWeek:function(){return'[\u0561\u0576\u0581\u0561\u056e] dddd [\u0585\u0580\u0568 \u056a\u0561\u0574\u0568] LT'},sameElse:'L'},relativeTime:{future:'%s \u0570\u0565\u057f\u0578',past:'%s \u0561\u057c\u0561\u057b',s:'\u0574\u056b \u0584\u0561\u0576\u056b \u057e\u0561\u0575\u0580\u056f\u0575\u0561\u0576',m:'\u0580\u0578\u057a\u0565',mm:'%d \u0580\u0578\u057a\u0565',h:'\u056a\u0561\u0574',hh:'%d \u056a\u0561\u0574',d:'\u0585\u0580',dd:'%d \u0585\u0580',M:'\u0561\u0574\u056b\u057d',MM:'%d \u0561\u0574\u056b\u057d',y:'\u057f\u0561\u0580\u056b',yy:'%d \u057f\u0561\u0580\u056b'},meridiemParse:/\u0563\u056b\u0577\u0565\u0580\u057e\u0561|\u0561\u057c\u0561\u057e\u0578\u057f\u057e\u0561|\u0581\u0565\u0580\u0565\u056f\u057e\u0561|\u0565\u0580\u0565\u056f\u0578\u0575\u0561\u0576/,isPM:function(_){return/^(\u0581\u0565\u0580\u0565\u056f\u057e\u0561|\u0565\u0580\u0565\u056f\u0578\u0575\u0561\u0576)$/.test(_)},meridiem:function(_){return _<4?'\u0563\u056b\u0577\u0565\u0580\u057e\u0561':_<12?'\u0561\u057c\u0561\u057e\u0578\u057f\u057e\u0561':_<17?'\u0581\u0565\u0580\u0565\u056f\u057e\u0561':'\u0565\u0580\u0565\u056f\u0578\u0575\u0561\u0576'},dayOfMonthOrdinalParse:/\d{1,2}|\d{1,2}-(\u056b\u0576|\u0580\u0564)/,ordinal:function(_,t){switch(t){case'DDD':case'w':case'W':case'DDDo':return 1===_?_+'-\u056b\u0576':_+'-\u0580\u0564';default:return _}},week:{dow:1,doy:7}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(_.moment)},691,[609]); -__d(function(g,r,i,a,m,e,d){var n,o;n=this,o=function(n){'use strict';return n.defineLocale('it',{months:'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'),monthsShort:'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'),weekdays:'domenica_luned\xec_marted\xec_mercoled\xec_gioved\xec_venerd\xec_sabato'.split('_'),weekdaysShort:'dom_lun_mar_mer_gio_ven_sab'.split('_'),weekdaysMin:'do_lu_ma_me_gi_ve_sa'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD/MM/YYYY',LL:'D MMMM YYYY',LLL:'D MMMM YYYY HH:mm',LLLL:'dddd, D MMMM YYYY HH:mm'},calendar:{sameDay:'[Oggi alle] LT',nextDay:'[Domani alle] LT',nextWeek:'dddd [alle] LT',lastDay:'[Ieri alle] LT',lastWeek:function(){switch(this.day()){case 0:return'[la scorsa] dddd [alle] LT';default:return'[lo scorso] dddd [alle] LT'}},sameElse:'L'},relativeTime:{future:function(n){return(/^[0-9].+$/.test(n)?'tra':'in')+' '+n},past:'%s fa',s:'alcuni secondi',m:'un minuto',mm:'%d minuti',h:'un\'ora',hh:'%d ore',d:'un giorno',dd:'%d giorni',M:'un mese',MM:'%d mesi',y:'un anno',yy:'%d anni'},dayOfMonthOrdinalParse:/\d{1,2}\xba/,ordinal:'%d\xba',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?o(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],o):o(n.moment)},692,[609]); -__d(function(g,r,i,a,m,e,d){var t,n;t=this,n=function(t){'use strict';return t.defineLocale('nb',{months:'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),monthsShort:'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'),monthsParseExact:!0,weekdays:'s\xf8ndag_mandag_tirsdag_onsdag_torsdag_fredag_l\xf8rdag'.split('_'),weekdaysShort:'s\xf8._ma._ti._on._to._fr._l\xf8.'.split('_'),weekdaysMin:'s\xf8_ma_ti_on_to_fr_l\xf8'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D. MMMM YYYY',LLL:'D. MMMM YYYY [kl.] HH:mm',LLLL:'dddd D. MMMM YYYY [kl.] HH:mm'},calendar:{sameDay:'[i dag kl.] LT',nextDay:'[i morgen kl.] LT',nextWeek:'dddd [kl.] LT',lastDay:'[i g\xe5r kl.] LT',lastWeek:'[forrige] dddd [kl.] LT',sameElse:'L'},relativeTime:{future:'om %s',past:'%s siden',s:'noen sekunder',m:'ett minutt',mm:'%d minutter',h:'en time',hh:'%d timer',d:'en dag',dd:'%d dager',M:'en m\xe5ned',MM:'%d m\xe5neder',y:'ett \xe5r',yy:'%d \xe5r'},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?n(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],n):n(t.moment)},693,[609]); -__d(function(g,r,i,a,m,e,d){var t,n;t=this,n=function(t){'use strict';var n='stycze\u0144_luty_marzec_kwiecie\u0144_maj_czerwiec_lipiec_sierpie\u0144_wrzesie\u0144_pa\u017adziernik_listopad_grudzie\u0144'.split('_'),o='stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_wrze\u015bnia_pa\u017adziernika_listopada_grudnia'.split('_');function s(t){return t%10<5&&t%10>1&&~~(t/10)%10!=1}function _(t,n,o){var _=t+' ';switch(o){case'm':return n?'minuta':'minut\u0119';case'mm':return _+(s(t)?'minuty':'minut');case'h':return n?'godzina':'godzin\u0119';case'hh':return _+(s(t)?'godziny':'godzin');case'MM':return _+(s(t)?'miesi\u0105ce':'miesi\u0119cy');case'yy':return _+(s(t)?'lata':'lat')}}return t.defineLocale('pl',{months:function(t,s){return t?''===s?'('+o[t.month()]+'|'+n[t.month()]+')':/D MMMM/.test(s)?o[t.month()]:n[t.month()]:n},monthsShort:'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_pa\u017a_lis_gru'.split('_'),weekdays:'niedziela_poniedzia\u0142ek_wtorek_\u015broda_czwartek_pi\u0105tek_sobota'.split('_'),weekdaysShort:'ndz_pon_wt_\u015br_czw_pt_sob'.split('_'),weekdaysMin:'Nd_Pn_Wt_\u015ar_Cz_Pt_So'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D MMMM YYYY',LLL:'D MMMM YYYY HH:mm',LLLL:'dddd, D MMMM YYYY HH:mm'},calendar:{sameDay:'[Dzi\u015b o] LT',nextDay:'[Jutro o] LT',nextWeek:function(){switch(this.day()){case 0:return'[W niedziel\u0119 o] LT';case 2:return'[We wtorek o] LT';case 3:return'[W \u015brod\u0119 o] LT';case 6:return'[W sobot\u0119 o] LT';default:return'[W] dddd [o] LT'}},lastDay:'[Wczoraj o] LT',lastWeek:function(){switch(this.day()){case 0:return'[W zesz\u0142\u0105 niedziel\u0119 o] LT';case 3:return'[W zesz\u0142\u0105 \u015brod\u0119 o] LT';case 6:return'[W zesz\u0142\u0105 sobot\u0119 o] LT';default:return'[W zesz\u0142y] dddd [o] LT'}},sameElse:'L'},relativeTime:{future:'za %s',past:'%s temu',s:'kilka sekund',m:_,mm:_,h:_,hh:_,d:'1 dzie\u0144',dd:'%d dni',M:'miesi\u0105c',MM:_,y:'rok',yy:_},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?n(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],n):n(t.moment)},694,[609]); -__d(function(g,r,i,a,m,e,d){var o,t;o=this,t=function(o){'use strict';return o.defineLocale('pt',{months:'janeiro_fevereiro_mar\xe7o_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'),monthsShort:'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'),weekdays:'Domingo_Segunda-feira_Ter\xe7a-feira_Quarta-feira_Quinta-feira_Sexta-feira_S\xe1bado'.split('_'),weekdaysShort:'Dom_Seg_Ter_Qua_Qui_Sex_S\xe1b'.split('_'),weekdaysMin:'Do_2\xaa_3\xaa_4\xaa_5\xaa_6\xaa_S\xe1'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD/MM/YYYY',LL:'D [de] MMMM [de] YYYY',LLL:'D [de] MMMM [de] YYYY HH:mm',LLLL:'dddd, D [de] MMMM [de] YYYY HH:mm'},calendar:{sameDay:'[Hoje \xe0s] LT',nextDay:'[Amanh\xe3 \xe0s] LT',nextWeek:'dddd [\xe0s] LT',lastDay:'[Ontem \xe0s] LT',lastWeek:function(){return 0===this.day()||6===this.day()?'[\xdaltimo] dddd [\xe0s] LT':'[\xdaltima] dddd [\xe0s] LT'},sameElse:'L'},relativeTime:{future:'em %s',past:'h\xe1 %s',s:'segundos',m:'um minuto',mm:'%d minutos',h:'uma hora',hh:'%d horas',d:'um dia',dd:'%d dias',M:'um m\xeas',MM:'%d meses',y:'um ano',yy:'%d anos'},dayOfMonthOrdinalParse:/\d{1,2}\xba/,ordinal:'%d\xba',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(o.moment)},695,[609]); -__d(function(g,r,i,a,m,e,d){var o,s;o=this,s=function(o){'use strict';return o.defineLocale('pt-br',{months:'janeiro_fevereiro_mar\xe7o_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'),monthsShort:'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'),weekdays:'Domingo_Segunda-feira_Ter\xe7a-feira_Quarta-feira_Quinta-feira_Sexta-feira_S\xe1bado'.split('_'),weekdaysShort:'Dom_Seg_Ter_Qua_Qui_Sex_S\xe1b'.split('_'),weekdaysMin:'Do_2\xaa_3\xaa_4\xaa_5\xaa_6\xaa_S\xe1'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD/MM/YYYY',LL:'D [de] MMMM [de] YYYY',LLL:'D [de] MMMM [de] YYYY [\xe0s] HH:mm',LLLL:'dddd, D [de] MMMM [de] YYYY [\xe0s] HH:mm'},calendar:{sameDay:'[Hoje \xe0s] LT',nextDay:'[Amanh\xe3 \xe0s] LT',nextWeek:'dddd [\xe0s] LT',lastDay:'[Ontem \xe0s] LT',lastWeek:function(){return 0===this.day()||6===this.day()?'[\xdaltimo] dddd [\xe0s] LT':'[\xdaltima] dddd [\xe0s] LT'},sameElse:'L'},relativeTime:{future:'em %s',past:'%s atr\xe1s',s:'poucos segundos',ss:'%d segundos',m:'um minuto',mm:'%d minutos',h:'uma hora',hh:'%d horas',d:'um dia',dd:'%d dias',M:'um m\xeas',MM:'%d meses',y:'um ano',yy:'%d anos'},dayOfMonthOrdinalParse:/\d{1,2}\xba/,ordinal:'%d\xba'})},'object'==typeof e&&void 0!==m&&'function'==typeof r?s(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],s):s(o.moment)},696,[609]); -__d(function(g,r,i,a,m,e,d){var t,_;t=this,_=function(t){'use strict';function _(t,_,s){var n,o;return'm'===s?_?'\u043c\u0438\u043d\u0443\u0442\u0430':'\u043c\u0438\u043d\u0443\u0442\u0443':t+' '+(n=+t,o={mm:_?'\u043c\u0438\u043d\u0443\u0442\u0430_\u043c\u0438\u043d\u0443\u0442\u044b_\u043c\u0438\u043d\u0443\u0442':'\u043c\u0438\u043d\u0443\u0442\u0443_\u043c\u0438\u043d\u0443\u0442\u044b_\u043c\u0438\u043d\u0443\u0442',hh:'\u0447\u0430\u0441_\u0447\u0430\u0441\u0430_\u0447\u0430\u0441\u043e\u0432',dd:'\u0434\u0435\u043d\u044c_\u0434\u043d\u044f_\u0434\u043d\u0435\u0439',MM:'\u043c\u0435\u0441\u044f\u0446_\u043c\u0435\u0441\u044f\u0446\u0430_\u043c\u0435\u0441\u044f\u0446\u0435\u0432',yy:'\u0433\u043e\u0434_\u0433\u043e\u0434\u0430_\u043b\u0435\u0442'}[s].split('_'),n%10==1&&n%100!=11?o[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?o[1]:o[2])}var s=[/^\u044f\u043d\u0432/i,/^\u0444\u0435\u0432/i,/^\u043c\u0430\u0440/i,/^\u0430\u043f\u0440/i,/^\u043c\u0430[\u0439\u044f]/i,/^\u0438\u044e\u043d/i,/^\u0438\u044e\u043b/i,/^\u0430\u0432\u0433/i,/^\u0441\u0435\u043d/i,/^\u043e\u043a\u0442/i,/^\u043d\u043e\u044f/i,/^\u0434\u0435\u043a/i];return t.defineLocale('ru',{months:{format:'\u044f\u043d\u0432\u0430\u0440\u044f_\u0444\u0435\u0432\u0440\u0430\u043b\u044f_\u043c\u0430\u0440\u0442\u0430_\u0430\u043f\u0440\u0435\u043b\u044f_\u043c\u0430\u044f_\u0438\u044e\u043d\u044f_\u0438\u044e\u043b\u044f_\u0430\u0432\u0433\u0443\u0441\u0442\u0430_\u0441\u0435\u043d\u0442\u044f\u0431\u0440\u044f_\u043e\u043a\u0442\u044f\u0431\u0440\u044f_\u043d\u043e\u044f\u0431\u0440\u044f_\u0434\u0435\u043a\u0430\u0431\u0440\u044f'.split('_'),standalone:'\u044f\u043d\u0432\u0430\u0440\u044c_\u0444\u0435\u0432\u0440\u0430\u043b\u044c_\u043c\u0430\u0440\u0442_\u0430\u043f\u0440\u0435\u043b\u044c_\u043c\u0430\u0439_\u0438\u044e\u043d\u044c_\u0438\u044e\u043b\u044c_\u0430\u0432\u0433\u0443\u0441\u0442_\u0441\u0435\u043d\u0442\u044f\u0431\u0440\u044c_\u043e\u043a\u0442\u044f\u0431\u0440\u044c_\u043d\u043e\u044f\u0431\u0440\u044c_\u0434\u0435\u043a\u0430\u0431\u0440\u044c'.split('_')},monthsShort:{format:'\u044f\u043d\u0432._\u0444\u0435\u0432\u0440._\u043c\u0430\u0440._\u0430\u043f\u0440._\u043c\u0430\u044f_\u0438\u044e\u043d\u044f_\u0438\u044e\u043b\u044f_\u0430\u0432\u0433._\u0441\u0435\u043d\u0442._\u043e\u043a\u0442._\u043d\u043e\u044f\u0431._\u0434\u0435\u043a.'.split('_'),standalone:'\u044f\u043d\u0432._\u0444\u0435\u0432\u0440._\u043c\u0430\u0440\u0442_\u0430\u043f\u0440._\u043c\u0430\u0439_\u0438\u044e\u043d\u044c_\u0438\u044e\u043b\u044c_\u0430\u0432\u0433._\u0441\u0435\u043d\u0442._\u043e\u043a\u0442._\u043d\u043e\u044f\u0431._\u0434\u0435\u043a.'.split('_')},weekdays:{standalone:'\u0432\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435_\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a_\u0432\u0442\u043e\u0440\u043d\u0438\u043a_\u0441\u0440\u0435\u0434\u0430_\u0447\u0435\u0442\u0432\u0435\u0440\u0433_\u043f\u044f\u0442\u043d\u0438\u0446\u0430_\u0441\u0443\u0431\u0431\u043e\u0442\u0430'.split('_'),format:'\u0432\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435_\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a_\u0432\u0442\u043e\u0440\u043d\u0438\u043a_\u0441\u0440\u0435\u0434\u0443_\u0447\u0435\u0442\u0432\u0435\u0440\u0433_\u043f\u044f\u0442\u043d\u0438\u0446\u0443_\u0441\u0443\u0431\u0431\u043e\u0442\u0443'.split('_'),isFormat:/\[ ?[\u0412\u0432] ?(?:\u043f\u0440\u043e\u0448\u043b\u0443\u044e|\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e|\u044d\u0442\u0443)? ?\] ?dddd/},weekdaysShort:'\u0432\u0441_\u043f\u043d_\u0432\u0442_\u0441\u0440_\u0447\u0442_\u043f\u0442_\u0441\u0431'.split('_'),weekdaysMin:'\u0432\u0441_\u043f\u043d_\u0432\u0442_\u0441\u0440_\u0447\u0442_\u043f\u0442_\u0441\u0431'.split('_'),monthsParse:s,longMonthsParse:s,shortMonthsParse:s,monthsRegex:/^(\u044f\u043d\u0432\u0430\u0440[\u044c\u044f]|\u044f\u043d\u0432\.?|\u0444\u0435\u0432\u0440\u0430\u043b[\u044c\u044f]|\u0444\u0435\u0432\u0440?\.?|\u043c\u0430\u0440\u0442\u0430?|\u043c\u0430\u0440\.?|\u0430\u043f\u0440\u0435\u043b[\u044c\u044f]|\u0430\u043f\u0440\.?|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d[\u044c\u044f]|\u0438\u044e\u043d\.?|\u0438\u044e\u043b[\u044c\u044f]|\u0438\u044e\u043b\.?|\u0430\u0432\u0433\u0443\u0441\u0442\u0430?|\u0430\u0432\u0433\.?|\u0441\u0435\u043d\u0442\u044f\u0431\u0440[\u044c\u044f]|\u0441\u0435\u043d\u0442?\.?|\u043e\u043a\u0442\u044f\u0431\u0440[\u044c\u044f]|\u043e\u043a\u0442\.?|\u043d\u043e\u044f\u0431\u0440[\u044c\u044f]|\u043d\u043e\u044f\u0431?\.?|\u0434\u0435\u043a\u0430\u0431\u0440[\u044c\u044f]|\u0434\u0435\u043a\.?)/i,monthsShortRegex:/^(\u044f\u043d\u0432\u0430\u0440[\u044c\u044f]|\u044f\u043d\u0432\.?|\u0444\u0435\u0432\u0440\u0430\u043b[\u044c\u044f]|\u0444\u0435\u0432\u0440?\.?|\u043c\u0430\u0440\u0442\u0430?|\u043c\u0430\u0440\.?|\u0430\u043f\u0440\u0435\u043b[\u044c\u044f]|\u0430\u043f\u0440\.?|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d[\u044c\u044f]|\u0438\u044e\u043d\.?|\u0438\u044e\u043b[\u044c\u044f]|\u0438\u044e\u043b\.?|\u0430\u0432\u0433\u0443\u0441\u0442\u0430?|\u0430\u0432\u0433\.?|\u0441\u0435\u043d\u0442\u044f\u0431\u0440[\u044c\u044f]|\u0441\u0435\u043d\u0442?\.?|\u043e\u043a\u0442\u044f\u0431\u0440[\u044c\u044f]|\u043e\u043a\u0442\.?|\u043d\u043e\u044f\u0431\u0440[\u044c\u044f]|\u043d\u043e\u044f\u0431?\.?|\u0434\u0435\u043a\u0430\u0431\u0440[\u044c\u044f]|\u0434\u0435\u043a\.?)/i,monthsStrictRegex:/^(\u044f\u043d\u0432\u0430\u0440[\u044f\u044c]|\u0444\u0435\u0432\u0440\u0430\u043b[\u044f\u044c]|\u043c\u0430\u0440\u0442\u0430?|\u0430\u043f\u0440\u0435\u043b[\u044f\u044c]|\u043c\u0430[\u044f\u0439]|\u0438\u044e\u043d[\u044f\u044c]|\u0438\u044e\u043b[\u044f\u044c]|\u0430\u0432\u0433\u0443\u0441\u0442\u0430?|\u0441\u0435\u043d\u0442\u044f\u0431\u0440[\u044f\u044c]|\u043e\u043a\u0442\u044f\u0431\u0440[\u044f\u044c]|\u043d\u043e\u044f\u0431\u0440[\u044f\u044c]|\u0434\u0435\u043a\u0430\u0431\u0440[\u044f\u044c])/i,monthsShortStrictRegex:/^(\u044f\u043d\u0432\.|\u0444\u0435\u0432\u0440?\.|\u043c\u0430\u0440[\u0442.]|\u0430\u043f\u0440\.|\u043c\u0430[\u044f\u0439]|\u0438\u044e\u043d[\u044c\u044f.]|\u0438\u044e\u043b[\u044c\u044f.]|\u0430\u0432\u0433\.|\u0441\u0435\u043d\u0442?\.|\u043e\u043a\u0442\.|\u043d\u043e\u044f\u0431?\.|\u0434\u0435\u043a\.)/i,longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D MMMM YYYY \u0433.',LLL:'D MMMM YYYY \u0433., HH:mm',LLLL:'dddd, D MMMM YYYY \u0433., HH:mm'},calendar:{sameDay:'[\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0432] LT',nextDay:'[\u0417\u0430\u0432\u0442\u0440\u0430 \u0432] LT',lastDay:'[\u0412\u0447\u0435\u0440\u0430 \u0432] LT',nextWeek:function(t){if(t.week()===this.week())return 2===this.day()?'[\u0412\u043e] dddd [\u0432] LT':'[\u0412] dddd [\u0432] LT';switch(this.day()){case 0:return'[\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435] dddd [\u0432] LT';case 1:case 2:case 4:return'[\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439] dddd [\u0432] LT';case 3:case 5:case 6:return'[\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e] dddd [\u0432] LT'}},lastWeek:function(t){if(t.week()===this.week())return 2===this.day()?'[\u0412\u043e] dddd [\u0432] LT':'[\u0412] dddd [\u0432] LT';switch(this.day()){case 0:return'[\u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0435] dddd [\u0432] LT';case 1:case 2:case 4:return'[\u0412 \u043f\u0440\u043e\u0448\u043b\u044b\u0439] dddd [\u0432] LT';case 3:case 5:case 6:return'[\u0412 \u043f\u0440\u043e\u0448\u043b\u0443\u044e] dddd [\u0432] LT'}},sameElse:'L'},relativeTime:{future:'\u0447\u0435\u0440\u0435\u0437 %s',past:'%s \u043d\u0430\u0437\u0430\u0434',s:'\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434',m:_,mm:_,h:'\u0447\u0430\u0441',hh:_,d:'\u0434\u0435\u043d\u044c',dd:_,M:'\u043c\u0435\u0441\u044f\u0446',MM:_,y:'\u0433\u043e\u0434',yy:_},meridiemParse:/\u043d\u043e\u0447\u0438|\u0443\u0442\u0440\u0430|\u0434\u043d\u044f|\u0432\u0435\u0447\u0435\u0440\u0430/i,isPM:function(t){return/^(\u0434\u043d\u044f|\u0432\u0435\u0447\u0435\u0440\u0430)$/.test(t)},meridiem:function(t,_,s){return t<4?'\u043d\u043e\u0447\u0438':t<12?'\u0443\u0442\u0440\u0430':t<17?'\u0434\u043d\u044f':'\u0432\u0435\u0447\u0435\u0440\u0430'},dayOfMonthOrdinalParse:/\d{1,2}-(\u0439|\u0433\u043e|\u044f)/,ordinal:function(t,_){switch(_){case'M':case'd':case'DDD':return t+'-\u0439';case'D':return t+'-\u0433\u043e';case'w':case'W':return t+'-\u044f';default:return t}},week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?_(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],_):_(t.moment)},697,[609]); -__d(function(g,r,i,a,m,e,d){var t,n;t=this,n=function(t){'use strict';var n='janu\xe1r_febru\xe1r_marec_apr\xedl_m\xe1j_j\xfan_j\xfal_august_september_okt\xf3ber_november_december'.split('_'),o='jan_feb_mar_apr_m\xe1j_j\xfan_j\xfal_aug_sep_okt_nov_dec'.split('_');function s(t){return t>1&&t<5}function u(t,n,o,u){var c=t+' ';switch(o){case's':return n||u?'p\xe1r sek\xfand':'p\xe1r sekundami';case'm':return n?'min\xfata':u?'min\xfatu':'min\xfatou';case'mm':return n||u?c+(s(t)?'min\xfaty':'min\xfat'):c+'min\xfatami';case'h':return n?'hodina':u?'hodinu':'hodinou';case'hh':return n||u?c+(s(t)?'hodiny':'hod\xedn'):c+'hodinami';case'd':return n||u?'de\u0148':'d\u0148om';case'dd':return n||u?c+(s(t)?'dni':'dn\xed'):c+'d\u0148ami';case'M':return n||u?'mesiac':'mesiacom';case'MM':return n||u?c+(s(t)?'mesiace':'mesiacov'):c+'mesiacmi';case'y':return n||u?'rok':'rokom';case'yy':return n||u?c+(s(t)?'roky':'rokov'):c+'rokmi'}}return t.defineLocale('sk',{months:n,monthsShort:o,weekdays:'nede\u013ea_pondelok_utorok_streda_\u0161tvrtok_piatok_sobota'.split('_'),weekdaysShort:'ne_po_ut_st_\u0161t_pi_so'.split('_'),weekdaysMin:'ne_po_ut_st_\u0161t_pi_so'.split('_'),longDateFormat:{LT:'H:mm',LTS:'H:mm:ss',L:'DD.MM.YYYY',LL:'D. MMMM YYYY',LLL:'D. MMMM YYYY H:mm',LLLL:'dddd D. MMMM YYYY H:mm'},calendar:{sameDay:'[dnes o] LT',nextDay:'[zajtra o] LT',nextWeek:function(){switch(this.day()){case 0:return'[v nede\u013eu o] LT';case 1:case 2:return'[v] dddd [o] LT';case 3:return'[v stredu o] LT';case 4:return'[vo \u0161tvrtok o] LT';case 5:return'[v piatok o] LT';case 6:return'[v sobotu o] LT'}},lastDay:'[v\u010dera o] LT',lastWeek:function(){switch(this.day()){case 0:return'[minul\xfa nede\u013eu o] LT';case 1:case 2:return'[minul\xfd] dddd [o] LT';case 3:return'[minul\xfa stredu o] LT';case 4:case 5:return'[minul\xfd] dddd [o] LT';case 6:return'[minul\xfa sobotu o] LT'}},sameElse:'L'},relativeTime:{future:'za %s',past:'pred %s',s:u,m:u,mm:u,h:u,hh:u,d:u,dd:u,M:u,MM:u,y:u,yy:u},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?n(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],n):n(t.moment)},698,[609]); -__d(function(g,r,i,a,m,e,d){var n,t;n=this,t=function(n){'use strict';function t(n,t,s,o){var u=n+' ';switch(s){case's':return t||o?'nekaj sekund':'nekaj sekundami';case'm':return t?'ena minuta':'eno minuto';case'mm':return u+=1===n?t?'minuta':'minuto':2===n?t||o?'minuti':'minutama':n<5?t||o?'minute':'minutami':t||o?'minut':'minutami';case'h':return t?'ena ura':'eno uro';case'hh':return u+=1===n?t?'ura':'uro':2===n?t||o?'uri':'urama':n<5?t||o?'ure':'urami':t||o?'ur':'urami';case'd':return t||o?'en dan':'enim dnem';case'dd':return u+=1===n?t||o?'dan':'dnem':2===n?t||o?'dni':'dnevoma':t||o?'dni':'dnevi';case'M':return t||o?'en mesec':'enim mesecem';case'MM':return u+=1===n?t||o?'mesec':'mesecem':2===n?t||o?'meseca':'mesecema':n<5?t||o?'mesece':'meseci':t||o?'mesecev':'meseci';case'y':return t||o?'eno leto':'enim letom';case'yy':return u+=1===n?t||o?'leto':'letom':2===n?t||o?'leti':'letoma':n<5?t||o?'leta':'leti':t||o?'let':'leti'}}return n.defineLocale('sl',{months:'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'),monthsShort:'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'),monthsParseExact:!0,weekdays:'nedelja_ponedeljek_torek_sreda_\u010detrtek_petek_sobota'.split('_'),weekdaysShort:'ned._pon._tor._sre._\u010det._pet._sob.'.split('_'),weekdaysMin:'ne_po_to_sr_\u010de_pe_so'.split('_'),weekdaysParseExact:!0,longDateFormat:{LT:'H:mm',LTS:'H:mm:ss',L:'DD.MM.YYYY',LL:'D. MMMM YYYY',LLL:'D. MMMM YYYY H:mm',LLLL:'dddd, D. MMMM YYYY H:mm'},calendar:{sameDay:'[danes ob] LT',nextDay:'[jutri ob] LT',nextWeek:function(){switch(this.day()){case 0:return'[v] [nedeljo] [ob] LT';case 3:return'[v] [sredo] [ob] LT';case 6:return'[v] [soboto] [ob] LT';case 1:case 2:case 4:case 5:return'[v] dddd [ob] LT'}},lastDay:'[v\u010deraj ob] LT',lastWeek:function(){switch(this.day()){case 0:return'[prej\u0161njo] [nedeljo] [ob] LT';case 3:return'[prej\u0161njo] [sredo] [ob] LT';case 6:return'[prej\u0161njo] [soboto] [ob] LT';case 1:case 2:case 4:case 5:return'[prej\u0161nji] dddd [ob] LT'}},sameElse:'L'},relativeTime:{future:'\u010dez %s',past:'pred %s',s:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:7}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(n.moment)},699,[609]); -__d(function(g,r,i,a,m,e,d){var n,t;n=this,t=function(n){'use strict';return n.defineLocale('sv',{months:'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'),monthsShort:'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),weekdays:'s\xf6ndag_m\xe5ndag_tisdag_onsdag_torsdag_fredag_l\xf6rdag'.split('_'),weekdaysShort:'s\xf6n_m\xe5n_tis_ons_tor_fre_l\xf6r'.split('_'),weekdaysMin:'s\xf6_m\xe5_ti_on_to_fr_l\xf6'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'YYYY-MM-DD',LL:'D MMMM YYYY',LLL:'D MMMM YYYY [kl.] HH:mm',LLLL:'dddd D MMMM YYYY [kl.] HH:mm',lll:'D MMM YYYY HH:mm',llll:'ddd D MMM YYYY HH:mm'},calendar:{sameDay:'[Idag] LT',nextDay:'[Imorgon] LT',lastDay:'[Ig\xe5r] LT',nextWeek:'[P\xe5] dddd LT',lastWeek:'[I] dddd[s] LT',sameElse:'L'},relativeTime:{future:'om %s',past:'f\xf6r %s sedan',s:'n\xe5gra sekunder',m:'en minut',mm:'%d minuter',h:'en timme',hh:'%d timmar',d:'en dag',dd:'%d dagar',M:'en m\xe5nad',MM:'%d m\xe5nader',y:'ett \xe5r',yy:'%d \xe5r'},dayOfMonthOrdinalParse:/\d{1,2}(e|a)/,ordinal:function(n){var t=n%10;return n+(1==~~(n%100/10)?'e':1===t?'a':2===t?'a':'e')},week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(n.moment)},700,[609]); -__d(function(g,r,i,a,m,e,d){var n,t;n=this,t=function(n){'use strict';var t={1:'\'inci',5:'\'inci',8:'\'inci',70:'\'inci',80:'\'inci',2:'\'nci',7:'\'nci',20:'\'nci',50:'\'nci',3:'\'\xfcnc\xfc',4:'\'\xfcnc\xfc',100:'\'\xfcnc\xfc',6:'\'nc\u0131',9:'\'uncu',10:'\'uncu',30:'\'uncu',60:'\'\u0131nc\u0131',90:'\'\u0131nc\u0131'};return n.defineLocale('tr',{months:'Ocak_\u015eubat_Mart_Nisan_May\u0131s_Haziran_Temmuz_A\u011fustos_Eyl\xfcl_Ekim_Kas\u0131m_Aral\u0131k'.split('_'),monthsShort:'Oca_\u015eub_Mar_Nis_May_Haz_Tem_A\u011fu_Eyl_Eki_Kas_Ara'.split('_'),weekdays:'Pazar_Pazartesi_Sal\u0131_\xc7ar\u015famba_Per\u015fembe_Cuma_Cumartesi'.split('_'),weekdaysShort:'Paz_Pts_Sal_\xc7ar_Per_Cum_Cts'.split('_'),weekdaysMin:'Pz_Pt_Sa_\xc7a_Pe_Cu_Ct'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'DD.MM.YYYY',LL:'D MMMM YYYY',LLL:'D MMMM YYYY HH:mm',LLLL:'dddd, D MMMM YYYY HH:mm'},calendar:{sameDay:'[bug\xfcn saat] LT',nextDay:'[yar\u0131n saat] LT',nextWeek:'[gelecek] dddd [saat] LT',lastDay:'[d\xfcn] LT',lastWeek:'[ge\xe7en] dddd [saat] LT',sameElse:'L'},relativeTime:{future:'%s sonra',past:'%s \xf6nce',s:'birka\xe7 saniye',m:'bir dakika',mm:'%d dakika',h:'bir saat',hh:'%d saat',d:'bir g\xfcn',dd:'%d g\xfcn',M:'bir ay',MM:'%d ay',y:'bir y\u0131l',yy:'%d y\u0131l'},dayOfMonthOrdinalParse:/\d{1,2}'(inci|nci|\xfcnc\xfc|nc\u0131|uncu|\u0131nc\u0131)/,ordinal:function(n){if(0===n)return n+'\'\u0131nc\u0131';var _=n%10;return n+(t[_]||t[n%100-_]||t[n>=100?100:null])},week:{dow:1,doy:7}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(n.moment)},701,[609]); -__d(function(g,r,i,a,m,e,d){var _,t;_=this,t=function(_){'use strict';return _.defineLocale('zh-cn',{months:'\u4e00\u6708_\u4e8c\u6708_\u4e09\u6708_\u56db\u6708_\u4e94\u6708_\u516d\u6708_\u4e03\u6708_\u516b\u6708_\u4e5d\u6708_\u5341\u6708_\u5341\u4e00\u6708_\u5341\u4e8c\u6708'.split('_'),monthsShort:'1\u6708_2\u6708_3\u6708_4\u6708_5\u6708_6\u6708_7\u6708_8\u6708_9\u6708_10\u6708_11\u6708_12\u6708'.split('_'),weekdays:'\u661f\u671f\u65e5_\u661f\u671f\u4e00_\u661f\u671f\u4e8c_\u661f\u671f\u4e09_\u661f\u671f\u56db_\u661f\u671f\u4e94_\u661f\u671f\u516d'.split('_'),weekdaysShort:'\u5468\u65e5_\u5468\u4e00_\u5468\u4e8c_\u5468\u4e09_\u5468\u56db_\u5468\u4e94_\u5468\u516d'.split('_'),weekdaysMin:'\u65e5_\u4e00_\u4e8c_\u4e09_\u56db_\u4e94_\u516d'.split('_'),longDateFormat:{LT:'HH:mm',LTS:'HH:mm:ss',L:'YYYY\u5e74MMMD\u65e5',LL:'YYYY\u5e74MMMD\u65e5',LLL:'YYYY\u5e74MMMD\u65e5Ah\u70b9mm\u5206',LLLL:'YYYY\u5e74MMMD\u65e5ddddAh\u70b9mm\u5206',l:'YYYY\u5e74MMMD\u65e5',ll:'YYYY\u5e74MMMD\u65e5',lll:'YYYY\u5e74MMMD\u65e5 HH:mm',llll:'YYYY\u5e74MMMD\u65e5dddd HH:mm'},meridiemParse:/\u51cc\u6668|\u65e9\u4e0a|\u4e0a\u5348|\u4e2d\u5348|\u4e0b\u5348|\u665a\u4e0a/,meridiemHour:function(_,t){return 12===_&&(_=0),'\u51cc\u6668'===t||'\u65e9\u4e0a'===t||'\u4e0a\u5348'===t?_:'\u4e0b\u5348'===t||'\u665a\u4e0a'===t?_+12:_>=11?_:_+12},meridiem:function(_,t,n){var s=100*_+t;return s<600?'\u51cc\u6668':s<900?'\u65e9\u4e0a':s<1130?'\u4e0a\u5348':s<1230?'\u4e2d\u5348':s<1800?'\u4e0b\u5348':'\u665a\u4e0a'},calendar:{sameDay:'[\u4eca\u5929]LT',nextDay:'[\u660e\u5929]LT',nextWeek:'[\u4e0b]ddddLT',lastDay:'[\u6628\u5929]LT',lastWeek:'[\u4e0a]ddddLT',sameElse:'L'},dayOfMonthOrdinalParse:/\d{1,2}(\u65e5|\u6708|\u5468)/,ordinal:function(_,t){switch(t){case'd':case'D':case'DDD':return _+'\u65e5';case'M':return _+'\u6708';case'w':case'W':return _+'\u5468';default:return _}},relativeTime:{future:'%s\u5185',past:'%s\u524d',s:'\u51e0\u79d2',m:'1 \u5206\u949f',mm:'%d \u5206\u949f',h:'1 \u5c0f\u65f6',hh:'%d \u5c0f\u65f6',d:'1 \u5929',dd:'%d \u5929',M:'1 \u4e2a\u6708',MM:'%d \u4e2a\u6708',y:'1 \u5e74',yy:'%d \u5e74'},week:{dow:1,doy:4}})},'object'==typeof e&&void 0!==m&&'function'==typeof r?t(r(d[0])):'function'==typeof define&&define.amd?define(['../moment'],t):t(_.moment)},702,[609]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.translate=function(n){return(0,l.withTranslation)(['main','languages','countries'])(n)},e.translateToHTML=function(n,l){var u=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.default.createElement("span",{dangerouslySetInnerHTML:{__html:n(l,u)}})};var t=n(r(d[1])),l=r(d[2])},703,[3,13,704]); -__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"Trans",{enumerable:!0,get:function(){return t.Trans}}),Object.defineProperty(e,"useTranslation",{enumerable:!0,get:function(){return n.useTranslation}}),Object.defineProperty(e,"withTranslation",{enumerable:!0,get:function(){return u.withTranslation}}),Object.defineProperty(e,"Translation",{enumerable:!0,get:function(){return o.Translation}}),Object.defineProperty(e,"I18nextProvider",{enumerable:!0,get:function(){return c.I18nextProvider}}),Object.defineProperty(e,"withSSR",{enumerable:!0,get:function(){return f.withSSR}}),Object.defineProperty(e,"useSSR",{enumerable:!0,get:function(){return b.useSSR}}),Object.defineProperty(e,"I18nContext",{enumerable:!0,get:function(){return l.I18nContext}}),Object.defineProperty(e,"initReactI18next",{enumerable:!0,get:function(){return l.initReactI18next}}),Object.defineProperty(e,"setDefaults",{enumerable:!0,get:function(){return l.setDefaults}}),Object.defineProperty(e,"getDefaults",{enumerable:!0,get:function(){return l.getDefaults}}),Object.defineProperty(e,"setI18n",{enumerable:!0,get:function(){return l.setI18n}}),Object.defineProperty(e,"getI18n",{enumerable:!0,get:function(){return l.getI18n}}),Object.defineProperty(e,"composeInitialProps",{enumerable:!0,get:function(){return l.composeInitialProps}}),Object.defineProperty(e,"getInitialProps",{enumerable:!0,get:function(){return l.getInitialProps}});var t=r(d[0]),n=r(d[1]),u=r(d[2]),o=r(d[3]),c=r(d[4]),f=r(d[5]),b=r(d[6]),l=r(d[7])},704,[705,723,728,729,730,731,732,719]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]),n=r(d[1]),o=r(d[2]);Object.defineProperty(e,"__esModule",{value:!0}),e.nodesToString=b,e.Trans=function(t){var n=t.children,o=t.count,s=t.parent,p=t.i18nKey,y=t.tOptions,v=t.values,E=t.defaults,O=t.components,k=t.ns,x=t.i18n,S=t.t,N=(0,c.default)(t,["children","count","parent","i18nKey","tOptions","values","defaults","components","ns","i18n","t"]),w=(0,f.getHasUsedI18nextProvider)()&&(0,u.useContext)(f.I18nContext)||{},V=w.i18n,A=w.defaultNS,I=x||V||(0,f.getI18n)();if(!I)return(0,h.warnOnce)('You will need pass in an i18next instance by using i18nextReactModule'),n;var K=S||I.t.bind(I)||function(t){return t},T=(0,l.default)({},(0,f.getDefaults)(),I.options&&I.options.react),H=void 0!==s?s:T.defaultTransParent,_=k||K.ns||A||I.options&&I.options.defaultNS;_='string'==typeof _?[_]:_||['translation'];var B=E||b('',n,0,T)||T.transEmptyNodeValue,P=T.hashTransKey,C=p||(P?P(B):B),D=v?{}:{interpolation:{prefix:'#$?',suffix:'?$#'}},F=(0,l.default)({},y,v,D,{defaultValue:B,count:o,ns:_}),M=C?K(C,F):B;return H?u.default.createElement(H,N,j(O||n,M,I,T,F)):j(O||n,M,I,T,F)};var c=o(r(d[3])),l=o(r(d[4])),s=o(r(d[5])),u=n(r(d[6])),p=o(r(d[7])),f=r(d[8]),h=r(d[9]);function y(t){return t&&(t.children||t.props&&t.props.children)}function v(t){return t?t&&t.children?t.children:t.props&&t.props.children:[]}function b(t,n,o,c){if(!n)return'';'[object Array]'!==Object.prototype.toString.call(n)&&(n=[n]);var p=c.transKeepBasicHtmlNodesFor||[];return n.forEach(function(n,o){var f="".concat(o);if('string'==typeof n)t="".concat(t).concat(n);else if(y(n)){var j=p.indexOf(n.type)>-1&&1===Object.keys(n.props).length&&'string'==typeof y(n)?n.type:f;t=n.props&&n.props.i18nIsDynamicList?"".concat(t,"<").concat(j,">"):"".concat(t,"<").concat(j,">").concat(b('',v(n),o+1,c),"")}else if(u.default.isValidElement(n))t=p.indexOf(n.type)>-1&&0===Object.keys(n.props).length?"".concat(t,"<").concat(n.type,"/>"):"".concat(t,"<").concat(f,">");else if('object'===(0,s.default)(n)){var E=(0,l.default)({},n),O=E.format;delete E.format;var k=Object.keys(E);O&&1===k.length?t="".concat(t,"{{").concat(k[0],", ").concat(O,"}}"):1===k.length?t="".concat(t,"{{").concat(k[0],"}}"):(0,h.warn)("react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.",n)}else(0,h.warn)("Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.",n)}),t}function j(n,o,c,f,h){if(''===o)return[];var b=f.transKeepBasicHtmlNodesFor||[],j=o&&new RegExp(b.join('|')).test(o);if(!n&&!j)return[o];var E={};return(function n(o){'[object Array]'!==Object.prototype.toString.call(o)&&(o=[o]),o.forEach(function(o){'string'!=typeof o&&(y(o)?n(v(o)):'object'!==(0,s.default)(o)||u.default.isValidElement(o)||t(E,o))})})(n),o=c.services.interpolator.interpolate(o,(0,l.default)({},E,h),c.language),v((function t(n,o){return'[object Array]'!==Object.prototype.toString.call(n)&&(n=[n]),'[object Array]'!==Object.prototype.toString.call(o)&&(o=[o]),o.reduce(function(o,c,p){var h,b=c.children&&c.children[0]&&c.children[0].content;if('tag'===c.type){var E=n[parseInt(c.name,10)]||{},O=u.default.isValidElement(E);if('string'==typeof E)o.push(E);else if(y(E)){var k=v(E),x=t(k,c.children),S=(h=k,'[object Array]'===Object.prototype.toString.call(h)&&h.every(function(t){return u.default.isValidElement(t)})&&0===x.length?k:x);E.dummy&&(E.children=S),o.push(u.default.cloneElement(E,(0,l.default)({},E.props,{key:p}),S))}else if(j&&'object'===(0,s.default)(E)&&E.dummy&&!O){var N=t(n,c.children);o.push(u.default.cloneElement(E,(0,l.default)({},E.props,{key:p}),N))}else if(isNaN(c.name)&&f.transSupportBasicHtmlNodes)if(c.voidElement)o.push(u.default.createElement(c.name,{key:"".concat(c.name,"-").concat(p)}));else{var w=t(n,c.children);o.push(u.default.createElement(c.name,{key:"".concat(c.name,"-").concat(p)},w))}else if('object'!==(0,s.default)(E)||O)1===c.children.length&&b?o.push(u.default.cloneElement(E,(0,l.default)({},E.props,{key:p}),b)):o.push(u.default.cloneElement(E,(0,l.default)({},E.props,{key:p})));else{var V=c.children[0]?b:null;V&&o.push(V)}}else'text'===c.type&&o.push(c.content);return o},[])})([{dummy:!0,children:n}],p.default.parse("<0>".concat(o,"")))[0])}},705,[706,707,708,709,711,713,13,714,719,722]); -__d(function(g,r,i,a,m,e,d){function t(){return m.exports=t=Object.assign||function(t){for(var n=1;n=0||Object.prototype.propertyIsEnumerable.call(n,l)&&(b[l]=n[l])}return b}},709,[710]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n,t){if(null==n)return{};var f,u,o={},c=Object.keys(n);for(u=0;u=0||(o[f]=n[f]);return o}},710,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);m.exports=function(n){for(var o=1;o|<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>)/g,t=r(d[0]),c=Object.create?Object.create(null):{};function o(n,t,c,o,h){var p=t.indexOf('<',o),s=t.slice(o,-1===p?void 0:p);/^\s*$/.test(s)&&(s=' '),(!h&&p>-1&&c+n.length>=0||' '!==s)&&n.push({type:'text',content:s})}m.exports=function(h,p){p||(p={}),p.components||(p.components=c);var s,l=[],u=-1,f=[],v={},x=!1;return h.replace(n,function(n,c){if(x){if(n!=='')return;x=!1}var O,y='/'!==n.charAt(1),W=0===n.indexOf('\x3c!--'),b=c+n.length,j=h.charAt(b);y&&!W&&(u++,'tag'===(s=t(n)).type&&p.components[s.name]&&(s.type='component',x=!0),s.voidElement||x||!j||'<'===j||o(s.children,h,u,b,p.ignoreWhitespace),v[s.tagName]=s,0===u&&l.push(s),(O=f[u-1])&&O.children.push(s),f[u]=s),(W||!y||s.voidElement)&&(W||u--,!x&&'<'!==j&&j&&o(O=-1===u?l:f[u].children,h,u,b,p.ignoreWhitespace))}),!l.length&&h.length&&o(l,h,0,0,p.ignoreWhitespace),l}},715,[716]); -__d(function(g,r,i,a,m,e,d){var t=/([\w-]+)|=|(['"])([.\s\S]*?)\2/g,n=r(d[0]);m.exports=function(o){var c,l=0,v=!0,s={type:'tag',name:'',voidElement:!1,attrs:{},children:[]};return o.replace(t,function(t){if('='===t)return v=!0,void l++;v?0===l?((n[t]||'/'===o.charAt(o.length-2))&&(s.voidElement=!0),s.name=t):(s.attrs[c]=t.replace(/^['"]|['"]$/g,''),c=void 0):(c&&(s.attrs[c]=c),c=t),l++,v=!1}),s}},716,[717]); -__d(function(g,r,i,a,m,e,d){m.exports={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,menuitem:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}},717,[]); -__d(function(g,r,i,a,m,e,d){function n(n){var t=[];for(var u in n)t.push(u+'="'+n[u]+'"');return t.length?' '+t.join(' '):''}function t(u,c){switch(c.type){case'text':return u+c.content;case'tag':return u+='<'+c.name+(c.attrs?n(c.attrs):'')+(c.voidElement?'/>':'>'),c.voidElement?u:u+c.children.reduce(t,'')+''}}m.exports=function(n){return n.reduce(function(n,u){return n+t('',u)},'')}},718,[]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.usedI18nextProvider=function(t){s=t},e.getHasUsedI18nextProvider=function(){return s},e.setDefaults=l,e.getDefaults=function(){return f},e.setI18n=I,e.getI18n=N,e.composeInitialProps=function(t){return function(n){return new Promise(function(s){var u=P();t.getInitialProps?t.getInitialProps(n).then(function(t){s((0,c.default)({},t,u))}):s(u)})}},e.getInitialProps=P,e.initReactI18next=e.ReportNamespaces=e.I18nContext=void 0;var n,s,u=t(r(d[1])),o=t(r(d[2])),c=t(r(d[3])),f={bindI18n:'languageChanging languageChanged',bindI18nStore:'',transEmptyNodeValue:'',transSupportBasicHtmlNodes:!0,transKeepBasicHtmlNodesFor:['br','strong','i','p'],useSuspense:!0},p=t(r(d[4])).default.createContext();function l(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};f=(0,c.default)({},f,t)}e.I18nContext=p;var v=(function(){function t(){(0,u.default)(this,t),this.usedNamespaces={}}return(0,o.default)(t,[{key:"addUsedNamespaces",value:function(t){var n=this;t.forEach(function(t){n.usedNamespaces[t]||(n.usedNamespaces[t]=!0)})}},{key:"getUsedNamespaces",value:function(){return Object.keys(this.usedNamespaces)}}]),t})();function I(t){n=t}function N(){return n}e.ReportNamespaces=v;var h={type:'3rdParty',init:function(t){l(t.options.react),I(t)}};function P(){var t=N(),n=t.reportNamespaces?t.reportNamespaces.getUsedNamespaces():[],s={},u={};return t.languages.forEach(function(s){u[s]={},n.forEach(function(n){u[s][n]=t.getResourceBundle(s,n)||{}})}),s.initialI18nStore=u,s.initialLanguage=t.language,s}e.initReactI18next=h},719,[708,720,721,711,13]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n,o){if(!(n instanceof o))throw new TypeError("Cannot call a class as a function")}},720,[]); -__d(function(g,r,i,a,m,e,d){function n(n,t){for(var o=0;o0?n:'Unknown')};var t={};function o(){for(var o=arguments.length,s=new Array(o),c=0;c1&&void 0!==arguments[1]?arguments[1]:{},p=f.i18n,l=(0,u.getHasUsedI18nextProvider)()&&(0,o.useContext)(u.I18nContext)||{},v=l.i18n,N=l.defaultNS,I=p||v||(0,u.getI18n)();I&&!I.reportNamespaces&&(I.reportNamespaces=new u.ReportNamespaces);if(!I){(0,c.warnOnce)('You will need pass in an i18next instance by using initReactI18next');var S=[function(n){return n},{},!0];return S.t=function(n){return n},S.i18n={},S.ready=!0,S}var x=(0,s.default)({},(0,u.getDefaults)(),I.options.react),y=f.useSuspense,b=void 0===y?x.useSuspense:y,h=n||N||I.options&&I.options.defaultNS;h='string'==typeof h?[h]:h||['translation'],I.reportNamespaces.addUsedNamespaces&&I.reportNamespaces.addUsedNamespaces(h);var w=(I.isInitialized||I.initializedStoreOnce)&&h.every(function(n){return(0,c.hasLoadedNamespace)(n,I)});function _(){return{t:I.getFixedT(null,'fallback'===x.nsMode?h:h[0])}}var E=(0,o.useState)(_()),O=(0,t.default)(E,2),P=O[0],U=O[1];(0,o.useEffect)(function(){var n=!0,t=x.bindI18n,s=x.bindI18nStore;function o(){n&&U(_())}return w||b||(0,c.loadNamespaces)(I,h,function(){n&&U(_())}),t&&I&&I.on(t,o),s&&I&&I.store.on(s,o),function(){n=!1,t&&I&&t.split(' ').forEach(function(n){return I.off(n,o)}),s&&I&&s.split(' ').forEach(function(n){return I.store.off(n,o)})}},[h.join()]);var j=[P.t,I,w];if(j.t=P.t,j.i18n=I,j.ready=w,w)return j;if(!w&&!b)return j;throw new Promise(function(n){(0,c.loadNamespaces)(I,h,function(){U(_()),n()})})};var t=n(r(d[1])),s=n(r(d[2])),o=r(d[3]),u=r(d[4]),c=r(d[5])},723,[708,724,711,13,719,722]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]);m.exports=function(u,c){return n(u)||t(u,c)||o()}},724,[725,726,727]); -__d(function(g,r,i,a,m,e,d){m.exports=function(n){if(Array.isArray(n))return n}},725,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(t,n){var o=[],l=!0,u=!1,f=void 0;try{for(var y,c=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(l=(y=c.next()).done)&&(o.push(y.value),!n||o.length!==n);l=!0);}catch(t){u=!0,f=t}finally{try{l||null==c.return||c.return()}finally{if(u)throw f}}return o}},726,[]); -__d(function(g,r,i,a,m,e,d){m.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},727,[]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.withTranslation=function(t){var c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(s){function p(l,p){var v=(0,o.useTranslation)(t,l),h=(0,u.default)(v,3),w=h[0],y=h[1],R=h[2],_=(0,n.default)({},l,{t:w,i18n:y,tReady:R});return c.withRef&&p&&(_.ref=p),f.default.createElement(s,_)}return p.displayName="withI18nextTranslation(".concat((0,l.getDisplayName)(s),")"),p.WrappedComponent=s,c.withRef?f.default.forwardRef(p):p}};var n=t(r(d[1])),u=t(r(d[2])),f=t(r(d[3])),o=r(d[4]),l=r(d[5])},728,[708,711,724,13,723,722]); -__d(function(g,r,i,a,m,e,d){"use strict";var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.Translation=function(n){var s=n.ns,c=n.children,o=(0,l.default)(n,["ns","children"]),f=(0,u.useTranslation)(s,o),v=(0,t.default)(f,3),_=v[0],h=v[1],T=v[2];return c(_,{i18n:h,lng:h.language},T)};var t=n(r(d[1])),l=n(r(d[2])),u=(n(r(d[3])),r(d[4]))},729,[708,724,709,13,723]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.I18nextProvider=function(t){var l=t.i18n,o=t.defaultNS,v=t.children;return(0,u.usedI18nextProvider)(!0),n.default.createElement(u.I18nContext.Provider,{value:{i18n:l,defaultNS:o}},v)};var n=t(r(d[1])),u=r(d[2])},730,[708,13,719]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.withSSR=function(){return function(t){function s(c){var p=c.initialI18nStore,s=c.initialLanguage,f=(0,u.default)(c,["initialI18nStore","initialLanguage"]);return(0,l.useSSR)(p,s),o.default.createElement(t,(0,n.default)({},f))}return s.getInitialProps=(0,c.composeInitialProps)(t),s.displayName="withI18nextSSR(".concat((0,p.getDisplayName)(t),")"),s.WrappedComponent=t,s}};var n=t(r(d[1])),u=t(r(d[2])),o=t(r(d[3])),l=r(d[4]),c=r(d[5]),p=r(d[6])},731,[708,711,709,13,732,719,722]); -__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.useSSR=function(o,s){var u=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).i18n,c=((0,t.getHasUsedI18nextProvider)()?(0,n.useContext)(t.I18nContext):{}).i18n,l=u||c||(0,t.getI18n)();if(l.options&&l.options.isClone)return;o&&!l.initializedStoreOnce&&(l.services.resourceStore.data=o,l.initializedStoreOnce=!0);s&&!l.initializedLanguageOnce&&(l.changeLanguage(s),l.initializedLanguageOnce=!0)};var n=r(d[0]),t=r(d[1])},732,[13,719]); -__d(function(g,r,i,a,m,e,d){var s=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=s(r(d[1])),n=s(r(d[2])),u=s(r(d[3])),o=s(r(d[4])),l=s(r(d[5])),p=s(r(d[6])),f=r(d[7]),c=r(d[8]),_=(function(s){function f(s){var n;return(0,t.default)(this,f),(n=(0,u.default)(this,(0,o.default)(f).call(this,s)))._onDismissed=n._onDismissed.bind((0,p.default)((0,p.default)(n))),n}return(0,l.default)(f,s),(0,n.default)(f,[{key:"_getDescription",value:function(){var s=this.props,t=s.description,n=s.descriptionArguments,u=s.descriptionKey,o=s.t,l=[];return u&&l.push(o(u,n)),t&&l.push(t),l}},{key:"_onDismissed",value:function(){this.props.onDismissed(this.props.uid)}}]),f})(f.Component);e.default=_,_.defaultProps={appearance:c.NOTIFICATION_TYPE.NORMAL,isDismissAllowed:!0}},733,[3,4,5,6,9,10,8,13,603]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=r(d[0]),t={contentColumn:{justifyContent:'center',flex:1,flexDirection:'column',paddingLeft:1.5*o.BoxModel.padding},contentText:{alignSelf:'flex-start',color:o.ColorPalette.white},dismissIcon:{color:o.ColorPalette.white,fontSize:20,padding:1.5*o.BoxModel.padding},notification:{backgroundColor:'#768898',flexDirection:'row',height:48,marginTop:.5*o.BoxModel.margin},notificationContainer:{flexGrow:0,justifyContent:'flex-end'},notificationContent:{flexDirection:'column'}};e.default=t},734,[406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=n(r(d[2])),l=n(r(d[3])),o=n(r(d[4])),s=n(r(d[5])),f=n(r(d[6])),c=n(r(d[7])),p=n(r(d[8])),h=r(d[9]),v=r(d[10]),_=t(r(d[11])),y=n(r(d[12])),b=n(r(d[13])),E=(function(t){function n(){return(0,l.default)(this,n),(0,s.default)(this,(0,f.default)(n).apply(this,arguments))}return(0,c.default)(n,t),(0,o.default)(n,[{key:"render",value:function(){var t=this.props._notifications,n=t&&t.length&&t[0];return n?p.default.createElement(h.View,{pointerEvents:"box-none",style:[b.default.notificationContainer,this.props.style]},p.default.createElement(y.default,(0,u.default)({},n.props,{onDismissed:this._onDismissed,uid:n.uid}))):null}}]),n})(_.default),D=(0,v.connect)(_._abstractMapStateToProps)(E);e.default=D},735,[2,3,16,4,5,6,9,10,13,17,534,736,606,734]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e._abstractMapStateToProps=function(t){var o=t['features/notifications'].notifications;return{_notifications:(0,T.areThereNotifications)(t)?o:[],autoDismissTimeout:'undefined'==typeof interfaceConfig?void 0:interfaceConfig.ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT}},e.default=void 0;var o=t(r(d[1])),s=t(r(d[2])),n=t(r(d[3])),u=t(r(d[4])),f=t(r(d[5])),c=t(r(d[6])),l=r(d[7]),_=r(d[8]),T=r(d[9]),h=(function(t){function l(t){var s;return(0,o.default)(this,l),(s=(0,n.default)(this,(0,u.default)(l).call(this,t)))._notificationDismissTimeout=null,s._onDismissed=s._onDismissed.bind((0,c.default)((0,c.default)(s))),s}return(0,f.default)(l,t),(0,s.default)(l,[{key:"componentDidMount",value:function(){this._manageDismissTimeout()}},{key:"componentDidUpdate",value:function(t){this._manageDismissTimeout(t)}},{key:"_manageDismissTimeout",value:function(t){var o=this,s=this.props,n=s._notifications,u=s.autoDismissTimeout;if(n.length){var f=n[0];if(f!==(t&&t._notifications.length?t._notifications[0]:void 0)&&(this._clearNotificationDismissTimeout(),f&&(f.timeout||'number'==typeof u)&&!1!==f.props.isDismissAllowed)){var c=f.timeout,l=void 0===c?u:c,_=f.uid;this._notificationDismissTimeout=setTimeout(function(){o._onDismissed(_)},l)}}else this._notificationDismissTimeout&&this._clearNotificationDismissTimeout()}},{key:"componentWillUnmount",value:function(){this._clearNotificationDismissTimeout()}},{key:"_clearNotificationDismissTimeout",value:function(){this._notificationDismissTimeout&&clearTimeout(this._notificationDismissTimeout),this._notificationDismissTimeout=null}},{key:"_onDismissed",value:function(t){var o=this.props._notifications;o.length&&o[0].uid!==t||this._clearNotificationDismissTimeout(),this.props.dispatch((0,_.hideNotification)(t))}}]),l})(l.Component);e.default=h},736,[3,4,5,6,9,10,8,13,588,737]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.areThereNotifications=function(n){var o=(0,t.toState)(n)['features/notifications'],f=o.enabled,c=o.notifications;return f&&c.length>0};var t=r(d[0])},737,[534]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),c=r(d[2]),o=r(d[3]),s=r(d[4]);c.MiddlewareRegistry.register(function(t){return function(c){return function(f){switch(f.type){case n.PARTICIPANT_JOINED:var p=c(f),u=f.participant;return u.local||t.dispatch((0,o.showParticipantJoinedNotification)((0,n.getParticipantDisplayName)(t.getState,u.id))),p;case n.PARTICIPANT_LEFT:var y=(0,n.getParticipantById)(t.getState(),f.participant.id);return'object'==typeof interfaceConfig&&y&&!y.local&&t.dispatch((0,o.showNotification)({descriptionKey:'notify.disconnected',titleKey:'notify.somebody',title:y.name},s.NOTIFICATION_TIMEOUT)),c(f)}return c(f)}}}),c.StateListenerRegistry.register(function(n){return(0,t.getCurrentConference)(n)},function(t,n){var c=n.dispatch;t||c((0,o.clearNotifications)())})},738,[383,532,534,588,603]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1])),n=r(d[2]),o=r(d[3]),I=r(d[4]),c={enabled:!0,notifications:[]};function u(t,n){for(var o=I.NOTIFICATION_TYPE_PRIORITIES[n.props.appearance]||0,c=t.length,u=1;u0&&void 0!==arguments[0]?arguments[0]:c,I=arguments.length>1?arguments[1]:void 0;switch(I.type){case o.CLEAR_NOTIFICATIONS:return(0,t.default)({},n,{notifications:[]});case o.HIDE_NOTIFICATION:return(0,t.default)({},n,{notifications:n.notifications.filter(function(t){return t.uid!==I.uid})});case o.SET_NOTIFICATIONS_ENABLED:return(0,t.default)({},n,{enabled:I.enabled});case o.SHOW_NOTIFICATION:return(0,t.default)({},n,{notifications:u(n.notifications,{component:I.component,props:I.props,timeout:I.timeout,uid:I.uid})})}return n})},739,[3,54,534,602,603]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SET_LOADABLE_AVATAR_URL=e.HIDDEN_PARTICIPANT_LEFT=e.HIDDEN_PARTICIPANT_JOINED=e.PIN_PARTICIPANT=e.PARTICIPANT_UPDATED=e.PARTICIPANT_LEFT=e.PARTICIPANT_KICKED=e.PARTICIPANT_JOINED=e.PARTICIPANT_ID_CHANGED=e.PARTICIPANT_DISPLAY_NAME_CHANGED=e.MUTE_REMOTE_PARTICIPANT=e.KICK_PARTICIPANT=e.DOMINANT_SPEAKER_CHANGED=void 0;e.DOMINANT_SPEAKER_CHANGED='DOMINANT_SPEAKER_CHANGED';e.KICK_PARTICIPANT='KICK_PARTICIPANT';e.MUTE_REMOTE_PARTICIPANT='MUTE_REMOTE_PARTICIPANT';e.PARTICIPANT_DISPLAY_NAME_CHANGED='PARTICIPANT_DISPLAY_NAME_CHANGED';e.PARTICIPANT_ID_CHANGED='PARTICIPANT_ID_CHANGED';e.PARTICIPANT_JOINED='PARTICIPANT_JOINED';e.PARTICIPANT_KICKED='PARTICIPANT_KICKED';e.PARTICIPANT_LEFT='PARTICIPANT_LEFT';e.PARTICIPANT_UPDATED='PARTICIPANT_UPDATED';e.PIN_PARTICIPANT='PIN_PARTICIPANT';e.HIDDEN_PARTICIPANT_JOINED='HIDDEN_PARTICIPANT_JOINED';e.HIDDEN_PARTICIPANT_LEFT='HIDDEN_PARTICIPANT_LEFT';e.SET_LOADABLE_AVATAR_URL='SET_LOADABLE_AVATAR_URL'},740,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.getFirstLoadableAvatarUrl=function(t){var n=(0,l.createDeferred)(),u=n.promise.then(function(){return E(t)}).then(function(t){if(A.length){var n=A.shift();n.resolve()}return t});A.length?A.push(n):n.resolve();return u},e.getLocalParticipant=y,e.getNormalizedDisplayName=function(t){if(!t||!t.trim())return;return t.trim().substring(0,p.MAX_DISPLAY_NAME_LENGTH)},e.getParticipantById=I,e.getParticipantCount=function(t){return h(t).length},e.getParticipantCountWithFake=function(t){return T(t).length},e.getParticipantDisplayName=function(t,n){var u=I(t,n);if(u){if(u.name)return u.name;if(u.local)return'object'==typeof interfaceConfig?interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME:'me'}return'object'==typeof interfaceConfig?interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME:'Fellow Jitster'},e.getParticipantPresenceStatus=function(t,n){if(!n)return;var u=I(t,n);if(!u)return;return u.presence},e.getParticipants=h,e.getPinnedParticipant=function(t){return T(t).find(function(t){return t.pinned})},e.isEveryoneModerator=function(t){for(var n=T(t),u=Array.isArray(n),o=0,n=u?n:n["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var c;if(u){if(o>=n.length)break;c=n[o++]}else{if((o=n.next()).done)break;c=o.value}var f=c;if(f.role!==p.PARTICIPANT_ROLE.MODERATOR)return!1}return!0},e.isIconUrl=function(t){return Boolean(t)&&'object'==typeof t},e.isLocalParticipantModerator=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],u=(0,o.toState)(t),c=y(u);if(!c)return!1;return c.role===p.PARTICIPANT_ROLE.MODERATOR&&(n||!u['features/base/config'].enableUserRolesBasedOnToken||!u['features/base/jwt'].isGuest)},e.shouldRenderParticipantVideo=function(t,n){var u=(0,o.toState)(t),l=I(u,n);if(!l)return!1;var p=(0,s.getTrackByMediaTypeAndParticipant)(u['features/base/tracks'],f.MEDIA_TYPE.VIDEO,n);if(!(0,f.shouldRenderVideoTrack)(p,!1))return!1;if((l.connectionStatus||c.JitsiParticipantConnectionStatus.ACTIVE)!==c.JitsiParticipantConnectionStatus.ACTIVE)return!1;if(!u['features/base/audio-only'].enabled)return!0;var v=u['features/video-layout'].screenShares||[],A=u['features/large-video'].participantId;return l.id===A&&v.includes(l.id)};var n=t(r(d[1])),u=r(d[2]),o=r(d[3]),c=r(d[4]),f=r(d[5]),s=r(d[6]),l=r(d[7]),p=r(d[8]),v=r(d[9]),A=[],P=new Map,b=[function(t){return t&&t.isJigasi?p.JIGASI_PARTICIPANT_ICON:null},function(t){return t&&t.avatarURL?t.avatarURL:null},function(t){return t&&t.email?(0,u.getGravatarURL)(t.email):null}];function y(t){return T(t).find(function(t){return t.local})}function I(t,n){return T(t).find(function(t){return t.id===n})}function h(t){return T(t).filter(function(t){return!t.isFakeParticipant})}function T(t){return Array.isArray(t)?t:(0,o.toState)(t)['features/base/participants']||[]}function E(t){var u,o,c;return n.default.async(function(f){for(;;)switch(f.prev=f.next){case 0:u=0;case 1:if(!(u1&&void 0!==arguments[1]?arguments[1]:{urlPrefix:'https://abotars.jitsi.net/meeple/',urlSuffix:''},c=v.urlPrefix,s=v.urlSuffix;return f(o)||u(n||l,c,s)},e.getGravatarURL=f;var n=t(r(d[1]));function u(t,u,f){return u+n.default.hex(t.trim().toLowerCase())+f}function f(t){if(o(t))return u(t,'https://www.gravatar.com/avatar/','?d=404&size=200')}function o(t){return t&&t.indexOf('@')>0}},742,[3,743]); -__d(function(g,r,i,a,m,e,d){!(function(){'use strict';var ERROR='input is invalid type',WINDOW='object'==typeof window,root=WINDOW?window:{};root.JS_MD5_NO_WINDOW&&(WINDOW=!1);var WEB_WORKER=!WINDOW&&'object'==typeof self,NODE_JS=!root.JS_MD5_NO_NODE_JS&&'object'==typeof process&&process.versions&&process.versions.node;NODE_JS?root=g:WEB_WORKER&&(root=self);var COMMON_JS=!root.JS_MD5_NO_COMMON_JS&&'object'==typeof m&&m.exports,AMD='function'==typeof define&&define.amd,ARRAY_BUFFER=!root.JS_MD5_NO_ARRAY_BUFFER&&'undefined'!=typeof ArrayBuffer,HEX_CHARS='0123456789abcdef'.split(''),EXTRA=[128,32768,8388608,-2147483648],SHIFT=[0,8,16,24],OUTPUT_TYPES=['hex','array','digest','buffer','arrayBuffer','base64'],BASE64_ENCODE_CHAR='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''),blocks=[],buffer8;if(ARRAY_BUFFER){var buffer=new ArrayBuffer(68);buffer8=new Uint8Array(buffer),blocks=new Uint32Array(buffer)}!root.JS_MD5_NO_NODE_JS&&Array.isArray||(Array.isArray=function(t){return'[object Array]'===Object.prototype.toString.call(t)}),!ARRAY_BUFFER||!root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(t){return'object'==typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer});var createOutputMethod=function(t){return function(s){return new Md5(!0).update(s)[t]()}},createMethod=function(){var t=createOutputMethod('hex');NODE_JS&&(t=nodeWrap(t)),t.create=function(){return new Md5},t.update=function(s){return t.create().update(s)};for(var s=0;s>2]|=t[n]<>6,R[f++]=128|63&h):h<55296||h>=57344?(R[f++]=224|h>>12,R[f++]=128|h>>6&63,R[f++]=128|63&h):(h=65536+((1023&h)<<10|1023&t.charCodeAt(++n)),R[f++]=240|h>>18,R[f++]=128|h>>12&63,R[f++]=128|h>>6&63,R[f++]=128|63&h);else for(f=this.start;n>2]|=h<>2]|=(192|h>>6)<>2]|=(128|63&h)<=57344?(u[f>>2]|=(224|h>>12)<>2]|=(128|h>>6&63)<>2]|=(128|63&h)<>2]|=(240|h>>18)<>2]|=(128|h>>12&63)<>2]|=(128|h>>6&63)<>2]|=(128|63&h)<=64?(this.start=f-64,this.hash(),this.hashed=!0):this.start=f}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},Md5.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,s=this.lastByteIndex;t[s>>2]|=EXTRA[3&s],s>=56&&(this.hashed||this.hash(),t[0]=t[16],t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]=this.bytes<<3,t[15]=this.hBytes<<3|this.bytes>>>29,this.hash()}},Md5.prototype.hash=function(){var t,s,o,h,f,n,A=this.blocks;this.first?s=((s=((t=((t=A[0]-680876937)<<7|t>>>25)-271733879<<0)^(o=((o=(-271733879^(h=((h=(-1732584194^2004318071&t)+A[1]-117830708)<<12|h>>>20)+t<<0)&(-271733879^t))+A[2]-1126478375)<<17|o>>>15)+h<<0)&(h^t))+A[3]-1316259209)<<22|s>>>10)+o<<0:(t=this.h0,s=this.h1,o=this.h2,s=((s+=((t=((t+=((h=this.h3)^s&(o^h))+A[0]-680876936)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[1]-389564586)<<12|h>>>20)+t<<0)&(t^s))+A[2]+606105819)<<17|o>>>15)+h<<0)&(h^t))+A[3]-1044525330)<<22|s>>>10)+o<<0),s=((s+=((t=((t+=(h^s&(o^h))+A[4]-176418897)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[5]+1200080426)<<12|h>>>20)+t<<0)&(t^s))+A[6]-1473231341)<<17|o>>>15)+h<<0)&(h^t))+A[7]-45705983)<<22|s>>>10)+o<<0,s=((s+=((t=((t+=(h^s&(o^h))+A[8]+1770035416)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[9]-1958414417)<<12|h>>>20)+t<<0)&(t^s))+A[10]-42063)<<17|o>>>15)+h<<0)&(h^t))+A[11]-1990404162)<<22|s>>>10)+o<<0,s=((s+=((t=((t+=(h^s&(o^h))+A[12]+1804603682)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[13]-40341101)<<12|h>>>20)+t<<0)&(t^s))+A[14]-1502002290)<<17|o>>>15)+h<<0)&(h^t))+A[15]+1236535329)<<22|s>>>10)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[1]-165796510)<<5|t>>>27)+s<<0)^s))+A[6]-1069501632)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[11]+643717713)<<14|o>>>18)+h<<0)^h))+A[0]-373897302)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[5]-701558691)<<5|t>>>27)+s<<0)^s))+A[10]+38016083)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[15]-660478335)<<14|o>>>18)+h<<0)^h))+A[4]-405537848)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[9]+568446438)<<5|t>>>27)+s<<0)^s))+A[14]-1019803690)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[3]-187363961)<<14|o>>>18)+h<<0)^h))+A[8]+1163531501)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[13]-1444681467)<<5|t>>>27)+s<<0)^s))+A[2]-51403784)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[7]+1735328473)<<14|o>>>18)+h<<0)^h))+A[12]-1926607734)<<20|s>>>12)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[5]-378558)<<4|t>>>28)+s<<0))+A[8]-2022574463)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[11]+1839030562)<<16|o>>>16)+h<<0))+A[14]-35309556)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[1]-1530992060)<<4|t>>>28)+s<<0))+A[4]+1272893353)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[7]-155497632)<<16|o>>>16)+h<<0))+A[10]-1094730640)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[13]+681279174)<<4|t>>>28)+s<<0))+A[0]-358537222)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[3]-722521979)<<16|o>>>16)+h<<0))+A[6]+76029189)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[9]-640364487)<<4|t>>>28)+s<<0))+A[12]-421815835)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[15]+530742520)<<16|o>>>16)+h<<0))+A[2]-995338651)<<23|s>>>9)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[0]-198630844)<<6|t>>>26)+s<<0)|~o))+A[7]+1126891415)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[14]-1416354905)<<15|o>>>17)+h<<0)|~t))+A[5]-57434055)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[12]+1700485571)<<6|t>>>26)+s<<0)|~o))+A[3]-1894986606)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[10]-1051523)<<15|o>>>17)+h<<0)|~t))+A[1]-2054922799)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[8]+1873313359)<<6|t>>>26)+s<<0)|~o))+A[15]-30611744)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[6]-1560198380)<<15|o>>>17)+h<<0)|~t))+A[13]+1309151649)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[4]-145523070)<<6|t>>>26)+s<<0)|~o))+A[11]-1120210379)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[2]+718787259)<<15|o>>>17)+h<<0)|~t))+A[9]-343485551)<<21|s>>>11)+o<<0,this.first?(this.h0=t+1732584193<<0,this.h1=s-271733879<<0,this.h2=o-1732584194<<0,this.h3=h+271733878<<0,this.first=!1):(this.h0=this.h0+t<<0,this.h1=this.h1+s<<0,this.h2=this.h2+o<<0,this.h3=this.h3+h<<0)},Md5.prototype.hex=function(){this.finalize();var t=this.h0,s=this.h1,o=this.h2,h=this.h3;return HEX_CHARS[t>>4&15]+HEX_CHARS[15&t]+HEX_CHARS[t>>12&15]+HEX_CHARS[t>>8&15]+HEX_CHARS[t>>20&15]+HEX_CHARS[t>>16&15]+HEX_CHARS[t>>28&15]+HEX_CHARS[t>>24&15]+HEX_CHARS[s>>4&15]+HEX_CHARS[15&s]+HEX_CHARS[s>>12&15]+HEX_CHARS[s>>8&15]+HEX_CHARS[s>>20&15]+HEX_CHARS[s>>16&15]+HEX_CHARS[s>>28&15]+HEX_CHARS[s>>24&15]+HEX_CHARS[o>>4&15]+HEX_CHARS[15&o]+HEX_CHARS[o>>12&15]+HEX_CHARS[o>>8&15]+HEX_CHARS[o>>20&15]+HEX_CHARS[o>>16&15]+HEX_CHARS[o>>28&15]+HEX_CHARS[o>>24&15]+HEX_CHARS[h>>4&15]+HEX_CHARS[15&h]+HEX_CHARS[h>>12&15]+HEX_CHARS[h>>8&15]+HEX_CHARS[h>>20&15]+HEX_CHARS[h>>16&15]+HEX_CHARS[h>>28&15]+HEX_CHARS[h>>24&15]},Md5.prototype.toString=Md5.prototype.hex,Md5.prototype.digest=function(){this.finalize();var t=this.h0,s=this.h1,o=this.h2,h=this.h3;return[255&t,t>>8&255,t>>16&255,t>>24&255,255&s,s>>8&255,s>>16&255,s>>24&255,255&o,o>>8&255,o>>16&255,o>>24&255,255&h,h>>8&255,h>>16&255,h>>24&255]},Md5.prototype.array=Md5.prototype.digest,Md5.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(16),s=new Uint32Array(t);return s[0]=this.h0,s[1]=this.h1,s[2]=this.h2,s[3]=this.h3,t},Md5.prototype.buffer=Md5.prototype.arrayBuffer,Md5.prototype.base64=function(){for(var t,s,o,h='',f=this.array(),n=0;n<15;)t=f[n++],s=f[n++],o=f[n++],h+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[63&(t<<4|s>>>4)]+BASE64_ENCODE_CHAR[63&(s<<2|o>>>6)]+BASE64_ENCODE_CHAR[63&o];return t=f[n],h+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[t<<4&63]+'=='};var exports=createMethod();COMMON_JS?m.exports=exports:(root.md5=exports,AMD&&define(function(){return exports}))})()},743,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})});var c=r(d[4]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}})}),r(d[5]),r(d[6])},744,[745,746,748,747,887,888,894]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.setAudioAvailable=function(n){return{type:t.SET_AUDIO_AVAILABLE,available:n}},e.setAudioMuted=function(n){var u=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:t.SET_AUDIO_MUTED,ensureTrack:u,muted:n}},e.setCameraFacingMode=function(n){return{type:t.SET_CAMERA_FACING_MODE,cameraFacingMode:n}},e.setVideoAvailable=function(n){return{type:t.SET_VIDEO_AVAILABLE,available:n}},e.setVideoMuted=function(u){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:n.VIDEO_MUTISM_AUTHORITY.USER,_=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return function(n,A){var E=A()['features/base/media'].video.muted,c=u?E|o:E&~o;return n({type:t.SET_VIDEO_MUTED,ensureTrack:_,muted:c})}},e.storeVideoTransform=function(n,u){return{type:t.STORE_VIDEO_TRANSFORM,streamId:n,transform:u}},e.toggleCameraFacingMode=function(){return{type:t.TOGGLE_CAMERA_FACING_MODE}};var t=r(d[0]),n=r(d[1])},745,[746,747]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.TOGGLE_CAMERA_FACING_MODE=e.STORE_VIDEO_TRANSFORM=e.SET_VIDEO_MUTED=e.SET_VIDEO_AVAILABLE=e.SET_CAMERA_FACING_MODE=e.SET_AUDIO_AVAILABLE=e.SET_AUDIO_MUTED=void 0;e.SET_AUDIO_MUTED='SET_AUDIO_MUTED';e.SET_AUDIO_AVAILABLE='SET_AUDIO_AVAILABLE';e.SET_CAMERA_FACING_MODE='SET_CAMERA_FACING_MODE';e.SET_VIDEO_AVAILABLE='SET_VIDEO_AVAILABLE';e.SET_VIDEO_MUTED='SET_VIDEO_MUTED';e.STORE_VIDEO_TRANSFORM='STORE_VIDEO_TRANSFORM';e.TOGGLE_CAMERA_FACING_MODE='TOGGLE_CAMERA_FACING_MODE'},746,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.VIDEO_TYPE=e.VIDEO_MUTISM_AUTHORITY=e.MEDIA_TYPE=e.CAMERA_FACING_MODE=void 0;e.CAMERA_FACING_MODE={ENVIRONMENT:'environment',USER:'user'};e.MEDIA_TYPE={AUDIO:'audio',VIDEO:'video'};e.VIDEO_MUTISM_AUTHORITY={AUDIO_ONLY:1,BACKGROUND:2,USER:4};e.VIDEO_TYPE={CAMERA:'camera',DESKTOP:'desktop'}},747,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})},748,[749]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})},749,[750]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"Audio",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"Video",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(e,"VideoTrack",{enumerable:!0,get:function(){return o.default}});var n=t(r(d[1])),u=t(r(d[2])),o=t(r(d[3]))},750,[3,751,755,791]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),n=t(r(d[2])),l=t(r(d[3])),o=t(r(d[4])),s=t(r(d[5])),f=t(r(d[6])),h=t(r(d[7])),p=t(r(d[8])),c=(function(t){function c(){return(0,u.default)(this,c),(0,l.default)(this,(0,o.default)(c).apply(this,arguments))}return(0,f.default)(c,t),(0,n.default)(c,[{key:"_soundLoadedCallback",value:function(t){t?p.default.error('Failed to load sound',t):this.setAudioElementImpl(this._sound)}},{key:"componentDidMount",value:function(){this._sound=this.props.src?new h.default(this.props.src,null,this._soundLoadedCallback.bind(this)):null}},{key:"componentWillUnmount",value:function(){this._sound&&(this._sound.release(),this._sound=null,this.setAudioElementImpl(null))}},{key:"play",value:function(){this._sound&&(this._sound.setNumberOfLoops(this.props.loop?-1:0),(0,s.default)((0,o.default)(c.prototype),"play",this).call(this))}},{key:"render",value:function(){return null}},{key:"stop",value:function(){this._sound&&this._sound.stop()}}]),c})(t(r(d[9])).default);e.default=c},751,[3,4,5,6,9,47,10,752,753,754]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]),n=t.NativeModules.RNSound,o=n.IsAndroid,s=n.IsWindows,u=r(d[1]),h=new t.NativeEventEmitter(n),p=0;function l(t,l,y,_){var c=this,f=u(t);f?(this._filename=f.uri,y=l):(this._filename=l?l+'/'+t:t,!o||l||/^(\/|http(s?)|asset)/.test(t)||(this._filename=t.toLowerCase().replace(/\.[^.]+$/,''))),this.registerOnPlay=function(){var t=this;null==this.onPlaySubscription?s||(this.onPlaySubscription=h.addListener('onPlayChange',function(n){var o=n.isPlaying;n.playerKey===t._key&&(t._playing=!!o)})):console.warn('On Play change event listener is already registered')},this._loaded=!1,this._key=p++,this._playing=!1,this._duration=-1,this._numberOfChannels=-1,this._volume=1,this._pan=0,this._numberOfLoops=0,this._speed=1,n.prepare(this._filename,this._key,_||{},function(t,n){n&&('number'==typeof n.duration&&(c._duration=n.duration),'number'==typeof n.numberOfChannels&&(c._numberOfChannels=n.numberOfChannels)),null===t&&(c._loaded=!0,c.registerOnPlay()),y&&y(t,n)})}l.prototype.isLoaded=function(){return this._loaded},l.prototype.play=function(t){return this._loaded?n.play(this._key,function(n){return t&&t(n)}):t&&t(!1),this},l.prototype.pause=function(t){var o=this;return this._loaded&&n.pause(this._key,function(){o._playing=!1,t&&t()}),this},l.prototype.stop=function(t){var o=this;return this._loaded&&n.stop(this._key,function(){o._playing=!1,t&&t()}),this},l.prototype.reset=function(){return this._loaded&&o&&(n.reset(this._key),this._playing=!1),this},l.prototype.release=function(){return this._loaded&&(n.release(this._key),this._loaded=!1,s||null!=this.onPlaySubscription&&(this.onPlaySubscription.remove(),this.onPlaySubscription=null)),this},l.prototype.getDuration=function(){return this._duration},l.prototype.getNumberOfChannels=function(){return this._numberOfChannels},l.prototype.getVolume=function(){return this._volume},l.prototype.setVolume=function(t){return this._volume=t,this._loaded&&(o||s?n.setVolume(this._key,t,t):n.setVolume(this._key,t)),this},l.prototype.getSystemVolume=function(t){return s||n.getSystemVolume(t),this},l.prototype.setSystemVolume=function(t){return o&&n.setSystemVolume(t),this},l.prototype.getPan=function(){return this._pan},l.prototype.setPan=function(t){return this._loaded&&n.setPan(this._key,this._pan=t),this},l.prototype.getNumberOfLoops=function(){return this._numberOfLoops},l.prototype.setNumberOfLoops=function(t){return this._numberOfLoops=t,this._loaded&&(o||s?n.setLooping(this._key,!!t):n.setNumberOfLoops(this._key,t)),this},l.prototype.setSpeed=function(t){return this._speed=t,this._loaded&&(s||n.setSpeed(this._key,t)),this},l.prototype.getCurrentTime=function(t){this._loaded&&n.getCurrentTime(this._key,t)},l.prototype.setCurrentTime=function(t){return this._loaded&&n.setCurrentTime(this._key,t),this},l.prototype.setSpeakerphoneOn=function(t){o&&n.setSpeakerphoneOn(this._key,t)},l.prototype.setCategory=function(t){l.setCategory(t,!1)},l.prototype.isPlaying=function(){return this._playing},l.enable=function(t){n.enable(t)},l.enableInSilenceMode=function(t){o||s||n.enableInSilenceMode(t)},l.setActive=function(t){o||s||n.setActive(t)},l.setCategory=function(t){var o=arguments.length>1&&void 0!==arguments[1]&&arguments[1];s||n.setCategory(t,o)},l.setMode=function(t){o||s||n.setMode(t)},l.setSpeakerPhone=function(t){o||s||n.setSpeakerPhone(t)},l.MAIN_BUNDLE=n.MainBundlePath,l.DOCUMENT=n.NSDocumentDirectory,l.LIBRARY=n.NSLibraryDirectory,l.CACHES=n.NSCachesDirectory,m.exports=l},752,[17,177]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/media');e.default=t},753,[576]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=t(r(d[1])),n=t(r(d[2])),u=t(r(d[3])),o=t(r(d[4])),s=t(r(d[5])),f=t(r(d[6])),p=r(d[7]),h=t(r(d[8])),I=(function(t){function p(t){var n;return(0,l.default)(this,p),(n=(0,u.default)(this,(0,o.default)(p).call(this,t))).setAudioElementImpl=n.setAudioElementImpl.bind((0,f.default)((0,f.default)(n))),n}return(0,s.default)(p,t),(0,n.default)(p,[{key:"pause",value:function(){this._audioElementImpl&&this._audioElementImpl.pause()}},{key:"play",value:function(){this._audioElementImpl&&this._audioElementImpl.play()}},{key:"setAudioElementImpl",value:function(t){this._audioElementImpl=t;var l=this.props.setRef;'function'==typeof l&&l(t?this:null)}},{key:"setSinkId",value:function(t){this._audioElementImpl&&'function'==typeof this._audioElementImpl.setSinkId&&this._audioElementImpl.setSinkId(t).catch(function(t){return h.default.error('Error setting sink',t)})}},{key:"stop",value:function(){this._audioElementImpl&&this._audioElementImpl.stop()}}]),p})(p.Component);e.default=I},754,[3,4,5,6,9,10,8,13,753]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),l=n(r(d[3])),u=n(r(d[4])),s=n(r(d[5])),f=n(r(d[6])),c=t(r(d[7])),p=r(d[8]),v=r(d[9]),h=n(r(d[10])),y=n(r(d[11])),b=(function(t){function n(){return(0,o.default)(this,n),(0,u.default)(this,(0,s.default)(n).apply(this,arguments))}return(0,f.default)(n,t),(0,l.default)(n,[{key:"componentDidMount",value:function(){var t=this.props.onPlaying;t&&t()}},{key:"render",value:function(){var t=this.props,n=t.onPress,o=t.stream,l=t.zoomEnabled;if(o){var u=h.default.video,s=l?'contain':u&&u.objectFit||'cover',f=c.default.createElement(p.RTCView,{mirror:this.props.mirror,objectFit:s,streamURL:o.toURL(),style:u,zOrder:this.props.zOrder});return l?c.default.createElement(y.default,{enabled:l,onPress:n,streamId:o.id,style:u},f):c.default.createElement(v.Pressable,{onPress:n},f)}return null}}]),n})(c.Component);e.default=b},755,[2,3,4,5,6,9,10,13,756,394,789,790]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"RTCPeerConnection",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"RTCIceCandidate",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(e,"RTCSessionDescription",{enumerable:!0,get:function(){return f.default}}),Object.defineProperty(e,"RTCView",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(e,"MediaStream",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(e,"MediaStreamTrack",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(e,"mediaDevices",{enumerable:!0,get:function(){return b.default}}),Object.defineProperty(e,"permissions",{enumerable:!0,get:function(){return p.default}});var n=t(r(d[1])),u=t(r(d[2])),f=t(r(d[3])),c=t(r(d[4])),o=t(r(d[5])),l=t(r(d[6])),b=t(r(d[7])),p=t(r(d[8]))},756,[3,757,776,775,780,762,768,785,788]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]),t=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=t(r(d[2])),c=t(r(d[3])),s=t(r(d[4])),u=t(r(d[5])),f=t(r(d[6])),l=t(r(d[7])),v=t(r(d[8])),h=r(d[9]),p=t(r(d[10])),C=t(r(d[11])),_=(t(r(d[12])),t(r(d[13]))),S=t(r(d[14])),E=t(r(d[15])),I=t(r(d[16])),w=t(r(d[17])),k=t(r(d[18])),y=t(r(d[19])),D=n(r(d[20])),O=h.NativeModules.WebRTCModule,L={offerToReceiveAudio:!0,offerToReceiveVideo:!0},T=0,b=(function(n){function t(n){var o;return(0,c.default)(this,t),(o=(0,u.default)(this,(0,f.default)(t).call(this))).signalingState='stable',o.iceGatheringState='new',o.iceConnectionState='new',o._localStreams=[],o._remoteStreams=[],o._dataChannelIds=new Set,o._peerConnectionId=T++,O.peerConnectionInit(n,o._peerConnectionId),o._registerEvents(),o}return(0,l.default)(t,n),(0,s.default)(t,[{key:"addStream",value:function(n){-1===this._localStreams.indexOf(n)&&(O.peerConnectionAddStream(n._reactTag,this._peerConnectionId),this._localStreams.push(n))}},{key:"removeStream",value:function(n){var t=this._localStreams.indexOf(n);-1!==t&&(this._localStreams.splice(t,1),O.peerConnectionRemoveStream(n._reactTag,this._peerConnectionId))}},{key:"createOffer",value:function(){var n=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:L;return new Promise(function(o,c){O.peerConnectionCreateOffer(n._peerConnectionId,D.normalizeOfferAnswerOptions(t),function(n,t){n?o(new I.default(t)):c(t)})})}},{key:"createAnswer",value:function(){var n=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return new Promise(function(o,c){O.peerConnectionCreateAnswer(n._peerConnectionId,D.normalizeOfferAnswerOptions(t),function(n,t){n?o(new I.default(t)):c(t)})})}},{key:"setConfiguration",value:function(n){O.peerConnectionSetConfiguration(n,this._peerConnectionId)}},{key:"setLocalDescription",value:function(n){var t=this;return new Promise(function(o,c){O.peerConnectionSetLocalDescription(n.toJSON?n.toJSON():n,t._peerConnectionId,function(s,u){s?(t.localDescription=n,o()):c(u)})})}},{key:"setRemoteDescription",value:function(n){var t=this;return new Promise(function(o,c){O.peerConnectionSetRemoteDescription(n.toJSON?n.toJSON():n,t._peerConnectionId,function(s,u){s?(t.remoteDescription=n,o()):c(u)})})}},{key:"addIceCandidate",value:function(n){var t=this;return new Promise(function(o,c){O.peerConnectionAddICECandidate(n.toJSON?n.toJSON():n,t._peerConnectionId,function(n){n?o():c(new Error('Failed to add ICE candidate'))})})}},{key:"getStats",value:function(n){var t=this;return new Promise(function(o,c){O.peerConnectionGetStats(n&&n.id||'',t._peerConnectionId,function(n,t){if(n)try{var s=JSON.parse(t);o(s)}catch(n){c(n)}else c(new Error(t))})})}},{key:"getLocalStreams",value:function(){return this._localStreams.slice()}},{key:"getRemoteStreams",value:function(){return this._remoteStreams.slice()}},{key:"close",value:function(){O.peerConnectionClose(this._peerConnectionId)}},{key:"_getTrack",value:function(n,t){var o=this._remoteStreams.find(function(t){return t._reactTag===n});return o&&o._tracks.find(function(n){return n.id===t})}},{key:"_unregisterEvents",value:function(){this._subscriptions.forEach(function(n){return n.remove()}),this._subscriptions=[]}},{key:"_registerEvents",value:function(){var n=this;this._subscriptions=[h.DeviceEventEmitter.addListener('peerConnectionOnRenegotiationNeeded',function(t){t.id===n._peerConnectionId&&n.dispatchEvent(new y.default('negotiationneeded'))}),h.DeviceEventEmitter.addListener('peerConnectionIceConnectionChanged',function(t){t.id===n._peerConnectionId&&(n.iceConnectionState=t.iceConnectionState,n.dispatchEvent(new y.default('iceconnectionstatechange')),'closed'===t.iceConnectionState&&n._unregisterEvents())}),h.DeviceEventEmitter.addListener('peerConnectionSignalingStateChanged',function(t){t.id===n._peerConnectionId&&(n.signalingState=t.signalingState,n.dispatchEvent(new y.default('signalingstatechange')))}),h.DeviceEventEmitter.addListener('peerConnectionAddedStream',function(t){if(t.id===n._peerConnectionId){var o=new p.default(t);n._remoteStreams.push(o),n.dispatchEvent(new C.default('addstream',{stream:o}))}}),h.DeviceEventEmitter.addListener('peerConnectionRemovedStream',function(t){if(t.id===n._peerConnectionId){var o=n._remoteStreams.find(function(n){return n._reactTag===t.streamId});if(o){var c=n._remoteStreams.indexOf(o);-1!==c&&n._remoteStreams.splice(c,1)}n.dispatchEvent(new C.default('removestream',{stream:o}))}}),h.DeviceEventEmitter.addListener('mediaStreamTrackMuteChanged',function(t){if(t.peerConnectionId===n._peerConnectionId){var o=n._getTrack(t.streamReactTag,t.trackId);if(o){o.muted=t.muted;var c=t.muted?'mute':'unmute';o.dispatchEvent(new _.default(c,{track:o}))}}}),h.DeviceEventEmitter.addListener('peerConnectionGotICECandidate',function(t){if(t.id===n._peerConnectionId){var o=new w.default(t.candidate),c=new k.default('icecandidate',{candidate:o});n.dispatchEvent(c)}}),h.DeviceEventEmitter.addListener('peerConnectionIceGatheringChanged',function(t){t.id===n._peerConnectionId&&(n.iceGatheringState=t.iceGatheringState,'complete'===n.iceGatheringState&&n.dispatchEvent(new k.default('icecandidate',null)),n.dispatchEvent(new y.default('icegatheringstatechange')))}),h.DeviceEventEmitter.addListener('peerConnectionDidOpenDataChannel',function(t){if(t.id===n._peerConnectionId){var o=t.dataChannel,c=o.id;if('number'==typeof c&&-1!==c){var s=new S.default(n._peerConnectionId,o.label,o);n._dataChannelIds.add(c),n.dispatchEvent(new E.default('datachannel',{channel:s}))}}})]}},{key:"createDataChannel",value:function(n,t){var c,s=this._dataChannelIds;if(t&&'id'in t){if('number'!=typeof(c=t.id))throw new TypeError('DataChannel id must be a number: '+c);if(s.has(c))throw new ResourceInUse('DataChannel id already in use: '+c)}else{for(c=0;c<65535&&s.has(c);++c);t=(0,o.default)({id:c},t)}return O.createDataChannel(this._peerConnectionId,n,t),s.add(c),new S.default(this._peerConnectionId,n,t)}}]),t})((0,v.default)(['connectionstatechange','icecandidate','icecandidateerror','iceconnectionstatechange','icegatheringstatechange','negotiationneeded','signalingstatechange','datachannel','addstream','removestream']));e.default=b},757,[2,3,16,4,5,6,9,10,758,17,762,770,768,771,772,774,775,776,777,778,779]); -__d(function(g,r,i,a,m,e,d){"use strict";var t=r(d[0]),n=r(d[1]),l=r(d[2]),o=t.LISTENERS,u=t.CAPTURE,f=t.BUBBLE,c=t.ATTRIBUTE,s=t.newNode,v=n.defineCustomEventTarget,h=l.createEventWrapper,E=l.STOP_IMMEDIATE_PROPAGATION_FLAG,p="undefined"!=typeof window&&void 0!==window.EventTarget,w=m.exports=function t(){if(!(this instanceof t)){if(1===arguments.length&&Array.isArray(arguments[0]))return v(t,arguments[0]);if(arguments.length>0){for(var n=Array(arguments.length),l=0;l=f.length)break;T=f[h++]}else{if((h=f.next()).done)break;T=h.value}var _=T;o.addTrack(_)}}else if(Array.isArray(t)){y.mediaStreamCreate(o.id);var b=t,p=Array.isArray(b),S=0;for(b=p?b:b["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var A;if(p){if(S>=b.length)break;A=b[S++]}else{if((S=b.next()).done)break;A=S.value}var R=A;o.addTrack(R)}}else{if(!('object'==typeof t&&t.streamId&&t.streamReactTag&&t.tracks))throw new TypeError("invalid type: "+typeof t);o.id=t.streamId,o._reactTag=t.streamReactTag;var w=t.tracks,x=Array.isArray(w),C=0;for(w=x?w:w["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var I;if(x){if(C>=w.length)break;I=w[C++]}else{if((C=w.next()).done)break;I=C.value}var M=I;o._tracks.push(new v.default(M))}}return o}return(0,f.default)(s,t),(0,o.default)(s,[{key:"addTrack",value:function(t){-1===this._tracks.indexOf(t)&&(this._tracks.push(t),y.mediaStreamAddTrack(this._reactTag,t.id))}},{key:"removeTrack",value:function(t){var n=this._tracks.indexOf(t);-1!==n&&(this._tracks.splice(n,1),y.mediaStreamRemoveTrack(this._reactTag,t.id))}},{key:"getTracks",value:function(){return this._tracks.slice()}},{key:"getTrackById",value:function(t){return this._tracks.find(function(n){return n.id===t})}},{key:"getAudioTracks",value:function(){return this._tracks.filter(function(t){return'audio'===t.kind})}},{key:"getVideoTracks",value:function(){return this._tracks.filter(function(t){return'video'===t.kind})}},{key:"clone",value:function(){throw new Error('Not implemented.')}},{key:"toURL",value:function(){return this._reactTag}},{key:"release",value:function(){y.mediaStreamRelease(this._reactTag)}}]),s})((0,l.default)(['active','inactive','addtrack','removetrack']));e.default=h},762,[3,4,5,6,9,10,17,758,763,768]); -__d(function(g,r,i,a,m,e,d){var v=r(d[0]),n=r(d[1]),o=n;o.v1=v,o.v4=n,m.exports=o},763,[764,767]); -__d(function(g,r,i,a,m,e,d){var n,o,s=r(d[0]),c=r(d[1]),v=0,l=0;m.exports=function(u,t,f){var k=t&&f||0,q=t||[],w=(u=u||{}).node||n,h=void 0!==u.clockseq?u.clockseq:o;if(null==w||null==h){var _=s();null==w&&(w=n=[1|_[0],_[1],_[2],_[3],_[4],_[5]]),null==h&&(h=o=16383&(_[6]<<8|_[7]))}var p=void 0!==u.msecs?u.msecs:(new Date).getTime(),x=void 0!==u.nsecs?u.nsecs:l+1,C=p-v+(x-l)/1e4;if(C<0&&void 0===u.clockseq&&(h=h+1&16383),(C<0||p>v)&&void 0===u.nsecs&&(x=0),x>=1e4)throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');v=p,l=x,o=h;var D=(1e4*(268435455&(p+=122192928e5))+x)%4294967296;q[k++]=D>>>24&255,q[k++]=D>>>16&255,q[k++]=D>>>8&255,q[k++]=255&D;var E=p/4294967296*1e4&268435455;q[k++]=E>>>8&255,q[k++]=255&E,q[k++]=E>>>24&15|16,q[k++]=E>>>16&255,q[k++]=h>>>8|128,q[k++]=255&h;for(var M=0;M<6;++M)q[k+M]=w[M];return t||c(q)}},764,[765,766]); -__d(function(g,r,i,a,m,e,d){var n='undefined'!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||'undefined'!=typeof msCrypto&&'function'==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(n){var t=new Uint8Array(16);m.exports=function(){return n(t),t}}else{var o=new Array(16);m.exports=function(){for(var n,t=0;t<16;t++)0==(3&t)&&(n=4294967296*Math.random()),o[t]=n>>>((3&t)<<3)&255;return o}}},765,[]); -__d(function(g,r,i,a,m,e,d){for(var n=[],t=0;t<256;++t)n[t]=(t+256).toString(16).substr(1);m.exports=function(t,o){var u=o||0,f=n;return[f[t[u++]],f[t[u++]],f[t[u++]],f[t[u++]],'-',f[t[u++]],f[t[u++]],'-',f[t[u++]],f[t[u++]],'-',f[t[u++]],f[t[u++]],'-',f[t[u++]],f[t[u++]],f[t[u++]],f[t[u++]],f[t[u++]],f[t[u++]]].join('')}},766,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),o=r(d[1]);m.exports=function(t,f,u){var l=f&&u||0;'string'==typeof t&&(f='binary'===t?new Array(16):null,t=null);var v=(t=t||{}).random||(t.rng||n)();if(v[6]=15&v[6]|64,v[8]=63&v[8]|128,f)for(var y=0;y<16;++y)f[l+y]=v[y];return f||o(v)}},767,[765,766]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),o=t(r(d[2])),l=t(r(d[3])),u=t(r(d[4])),s=t(r(d[5])),c=r(d[6]),f=t(r(d[7])),h=(t(r(d[8])),c.NativeModules.WebRTCModule),v=(function(t){function c(t){var o;(0,n.default)(this,c),o=(0,l.default)(this,(0,u.default)(c).call(this));var s=t.readyState.toLowerCase();return o._enabled=t.enabled,o.id=t.id,o.kind=t.kind,o.label=t.label,o.muted=!1,o.readonly=!0,o.remote=t.remote,o.readyState="initializing"===s||"live"===s?"live":"ended",o}return(0,s.default)(c,t),(0,o.default)(c,[{key:"stop",value:function(){h.mediaStreamTrackSetEnabled(this.id,!1),this.readyState='ended'}},{key:"_switchCamera",value:function(){if(this.remote)throw new Error('Not implemented for remote tracks');if('video'!==this.kind)throw new Error('Only implemented for video tracks');h.mediaStreamTrackSwitchCamera(this.id)}},{key:"applyConstraints",value:function(){throw new Error('Not implemented.')}},{key:"clone",value:function(){throw new Error('Not implemented.')}},{key:"getCapabilities",value:function(){throw new Error('Not implemented.')}},{key:"getConstraints",value:function(){throw new Error('Not implemented.')}},{key:"getSettings",value:function(){throw new Error('Not implemented.')}},{key:"release",value:function(){h.mediaStreamTrackRelease(this.id)}},{key:"enabled",get:function(){return this._enabled},set:function(t){t!==this._enabled&&(h.mediaStreamTrackSetEnabled(this.id,!this._enabled),this._enabled=!this._enabled,this.muted=!this._enabled)}}]),c})((0,f.default)(['ended','mute','unmute','overconstrained']));e.default=v},768,[3,4,5,6,9,10,17,758,769]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},769,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},770,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},771,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),o=t(r(d[2])),s=t(r(d[3])),f=t(r(d[4])),u=t(r(d[5])),l=t(r(d[6])),c=r(d[7]),h=t(r(d[8])),y=t(r(d[9])),p=t(r(d[10])),v=t(r(d[11])),b=c.NativeModules.WebRTCModule,_=((function(t){function n(){return(0,o.default)(this,n),(0,s.default)(this,(0,f.default)(n).apply(this,arguments))}(0,u.default)(n,t)})((0,l.default)(Error)),(function(t){function l(t,n,u){var c;return(0,o.default)(this,l),(c=(0,s.default)(this,(0,f.default)(l).call(this))).binaryType='arraybuffer',c.bufferedAmount=0,c.bufferedAmountLowThreshold=0,c.maxPacketLifeTime=null,c.maxRetransmits=null,c.negotiated=!1,c.ordered=!0,c.protocol='',c.readyState='connecting',c._peerConnectionId=t,c.label=n,c.id='id'in u?u.id:-1,c.ordered=!!u.ordered,c.maxPacketLifeTime=u.maxPacketLifeTime,c.maxRetransmits=u.maxRetransmits,c.protocol=u.protocol||'',c.negotiated=!!u.negotiated,c._registerEvents(),c}return(0,u.default)(l,t),(0,n.default)(l,[{key:"send",value:function(t){if('string'!=typeof t){if(ArrayBuffer.isView(t))t=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);else{if(!(t instanceof ArrayBuffer))throw new TypeError('Data must be either string, ArrayBuffer, or ArrayBufferView');t=new Uint8Array(t)}b.dataChannelSend(this._peerConnectionId,this.id,h.default.fromByteArray(t),'binary')}else b.dataChannelSend(this._peerConnectionId,this.id,t,'text')}},{key:"close",value:function(){'closing'!==this.readyState&&'closed'!==this.readyState&&(this.readyState='closing',b.dataChannelClose(this._peerConnectionId,this.id))}},{key:"_unregisterEvents",value:function(){this._subscriptions.forEach(function(t){return t.remove()}),this._subscriptions=[]}},{key:"_registerEvents",value:function(){var t=this;this._subscriptions=[c.DeviceEventEmitter.addListener('dataChannelStateChanged',function(n){n.peerConnectionId===t._peerConnectionId&&n.id===t.id&&(t.readyState=n.state,'open'===t.readyState?t.dispatchEvent(new v.default('open',{channel:t})):'close'===t.readyState&&(t.dispatchEvent(new v.default('close',{channel:t})),t._unregisterEvents()))}),c.DeviceEventEmitter.addListener('dataChannelReceiveMessage',function(n){if(n.peerConnectionId===t._peerConnectionId&&n.id===t.id){var o=n.data;'binary'===n.type&&(o=h.default.toByteArray(n.data).buffer),t.dispatchEvent(new p.default('message',{data:o}))}})]}}]),l})((0,y.default)(['open','message','bufferedamountlow','close','error'])));e.default=_},772,[3,5,4,6,9,10,98,17,126,758,773,774]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},773,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},774,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),s=t(r(d[2])),n=(function(){function t(s){(0,u.default)(this,t),this.sdp=s.sdp,this.type=s.type}return(0,s.default)(t,[{key:"toJSON",value:function(){return{sdp:this.sdp,type:this.type}}}]),t})();e.default=n},775,[3,4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),s=t(r(d[2])),u=(function(){function t(s){(0,n.default)(this,t),this.candidate=s.candidate,this.sdpMLineIndex=s.sdpMLineIndex,this.sdpMid=s.sdpMid}return(0,s.default)(t,[{key:"toJSON",value:function(){return{candidate:this.candidate,sdpMLineIndex:this.sdpMLineIndex,sdpMid:this.sdpMid}}}]),t})();e.default=u},776,[3,4,5]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1]));e.default=function t(u,c){(0,n.default)(this,t),this.type=u.toString(),this.candidate=null,c&&c.candidate&&(this.candidate=c.candidate)}},777,[3,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),f=t(r(d[2]));e.default=function t(l,n){(0,f.default)(this,t),this.type=l.toString(),(0,u.default)(this,n)}},778,[3,16,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.normalizeOfferAnswerOptions=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o={};if(!t)return o;t.mandatory&&(t=t.mandatory);for(var f=Object.entries(t),c=0;c0&&void 0!==arguments[0]?arguments[0]:{};if('object'!=typeof o)return Promise.reject(new TypeError('constraints is not a dictionary'));if(!(void 0!==o.audio&&o.audio||void 0!==o.video&&o.video))return Promise.reject(new TypeError('audio and/or video is required'));var t=[];(o=u.normalizeConstraints(o)).audio?t.push(v.default.request({name:'microphone'})):t.push(Promise.resolve(!1));o.video?t.push(v.default.request({name:'camera'})):t.push(Promise.resolve(!1));return new Promise(function(s,u){Promise.all(t).then(function(t){var v=(0,n.default)(t,2),p=v[0],h=v[1];if(p||h){p||delete o.audio,h||delete o.video;f.getUserMedia(o,function(o,t){var n={streamId:o,streamReactTag:o,tracks:t};s(new c.default(n))},function(o,t){var n;switch(o){case'TypeError':n=new TypeError(t)}n||(n=new l.default({message:t,name:o}));u(n)})}else{u(new l.default({message:'Permission denied.',name:'SecurityError'}))}})})};var n=t(r(d[2])),s=r(d[3]),u=o(r(d[4])),c=t(r(d[5])),l=t(r(d[6])),v=t(r(d[7])),f=s.NativeModules.WebRTCModule},786,[2,3,26,17,779,762,787,788]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=t(r(d[1]));e.default=function t(n){(0,s.default)(this,t),this.name=n.name,this.message=n.message,this.constraintName=n.constraintName}},787,[3,4]); -__d(function(g,r,i,a,m,e,d){'use strict';var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=n(r(d[1])),o=n(r(d[2])),s=r(d[3]),u=s.NativeModules.WebRTCModule,P=new((function(){function n(){(0,t.default)(this,n),this.RESULT={DENIED:'denied',GRANTED:'granted',PROMPT:'prompt'},this.VALID_PERMISSIONS=['camera','microphone'],this._lastReq=Promise.resolve()}return(0,o.default)(n,[{key:"_requestPermissionAndroid",value:function(n){return new Promise(function(t,o){s.PermissionsAndroid.request(n).then(function(n){return t(!0===n||n===s.PermissionsAndroid.RESULTS.GRANTED)},function(){return t(!1)})})}},{key:"_validatePermissionDescriptior",value:function(n){if("object"!=typeof n)throw new TypeError("Argument 1 of Permissions.query is not an object.");if(void 0===n.name)throw new TypeError("Missing required 'name' member of PermissionDescriptor.");if(-1===this.VALID_PERMISSIONS.indexOf(n.name))throw new TypeError("'name' member of PermissionDescriptor is not a valid value for enumeration PermissionName.")}},{key:"query",value:function(n){var t=this;try{this._validatePermissionDescriptior(n)}catch(n){return Promise.reject(n)}if('android'===s.Platform.OS){var o='camera'===n.name?s.PermissionsAndroid.PERMISSIONS.CAMERA:s.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO;return new Promise(function(n,u){s.PermissionsAndroid.check(o).then(function(o){return n(o?t.RESULT.GRANTED:t.RESULT.PROMPT)},function(){return n(t.RESULT.PROMPT)})})}return'ios'===s.Platform.OS?u.checkPermission(n.name):Promise.reject(new TypeError("Unsupported platform."))}},{key:"request",value:function(n){var t=this;try{this._validatePermissionDescriptior(n)}catch(n){return Promise.reject(n)}if('android'===s.Platform.OS){var o='camera'===n.name?s.PermissionsAndroid.PERMISSIONS.CAMERA:s.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,P=function(){return t._requestPermissionAndroid(o)};return this._lastReq=this._lastReq.then(P,P),this._lastReq}return'ios'===s.Platform.OS?u.requestPermission(n.name):Promise.reject(new TypeError("Unsupported platform."))}}]),n})());e.default=P},788,[3,4,5,17]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=r(d[0]).StyleSheet.create({videoTranformedView:{flex:1},videoTransformedViewContainer:{overflow:'hidden'},video:{flex:1}});e.default=o},789,[17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=n(r(d[2])),o=n(r(d[3])),l=n(r(d[4])),u=n(r(d[5])),h=n(r(d[6])),f=n(r(d[7])),c=n(r(d[8])),v=n(r(d[9])),p=t(r(d[10])),_=r(d[11]),y=r(d[12]),P=r(d[13]),T=n(r(d[14])),S={scale:1,translateX:0,translateY:0},R=5,M=(function(t){function n(t){var s;return(0,l.default)(this,n),(s=(0,h.default)(this,(0,f.default)(n).call(this,t))).state={layout:null,transform:s._getSavedTransform(t.streamId)||S},s._didMove=s._didMove.bind((0,v.default)((0,v.default)(s))),s._getTransformStyle=s._getTransformStyle.bind((0,v.default)((0,v.default)(s))),s._onGesture=s._onGesture.bind((0,v.default)((0,v.default)(s))),s._onLayout=s._onLayout.bind((0,v.default)((0,v.default)(s))),s._onMoveShouldSetPanResponder=s._onMoveShouldSetPanResponder.bind((0,v.default)((0,v.default)(s))),s._onPanResponderGrant=s._onPanResponderGrant.bind((0,v.default)((0,v.default)(s))),s._onPanResponderMove=s._onPanResponderMove.bind((0,v.default)((0,v.default)(s))),s._onPanResponderRelease=s._onPanResponderRelease.bind((0,v.default)((0,v.default)(s))),s._onStartShouldSetPanResponder=s._onStartShouldSetPanResponder.bind((0,v.default)((0,v.default)(s))),s.moveThreshold=_.PixelRatio.get()*R,s.gestureHandlers=_.PanResponder.create({onPanResponderGrant:s._onPanResponderGrant,onPanResponderMove:s._onPanResponderMove,onPanResponderRelease:s._onPanResponderRelease,onPanResponderTerminationRequest:function(){return!0},onMoveShouldSetPanResponder:s._onMoveShouldSetPanResponder,onShouldBlockNativeResponder:function(){return!1},onStartShouldSetPanResponder:s._onStartShouldSetPanResponder}),s}return(0,c.default)(n,t),(0,u.default)(n,[{key:"componentDidUpdate",value:function(t,n){t.streamId!==this.props.streamId&&(this._storeTransform(t.streamId,n.transform),this._restoreTransform(this.props.streamId))}},{key:"componentWillUnmount",value:function(){this._storeTransform(this.props.streamId,this.state.transform)}},{key:"render",value:function(){var t=this.props,n=t.children,s=t.style;return p.default.createElement(_.View,(0,o.default)({onLayout:this._onLayout,pointerEvents:"box-only",style:[T.default.videoTransformedViewContainer,s]},this.gestureHandlers.panHandlers),p.default.createElement(_.View,{style:[T.default.videoTranformedView,this._getTransformStyle()]},n))}},{key:"_calculateTransformIncrement",value:function(t){var n=this.state.transform,s=n.scale,o=n.translateX,l=n.translateY,u=t.scale,h=t.translateX,f=t.translateY;return{scale:s=Math.min(s*(u||1),5),translateX:o+=(h||0)/s,translateY:l+=(f||0)/s}}},{key:"_didMove",value:function(t){var n=t.dx,s=t.dy;return Math.abs(n)>this.moveThreshold||Math.abs(s)>this.moveThreshold}},{key:"_getSavedTransform",value:function(t){var n=this.props,s=n.enabled,o=n._transforms;return s&&o[t]||null}},{key:"_getTouchDistance",value:function(t){var n=t.nativeEvent.touches,s=Math.abs(n[0].pageX-n[1].pageX),o=Math.abs(n[0].pageY-n[1].pageY);return Math.sqrt(Math.pow(s,2)+Math.pow(o,2))}},{key:"_getTouchPosition",value:function(t){var n=t.nativeEvent.touches;return{x:n[0].pageX,y:n[0].pageY}}},{key:"_getTransformStyle",value:function(){if(!this.props.enabled)return null;var t=this.state.transform;return{transform:[{scale:t.scale},{translateX:t.translateX},{translateY:t.translateY}]}}},{key:"_limitAndApplyTransformation",value:function(t){var n=this.state.layout;if(n){var s=this.state.transform.scale,o=t.scale,l=t.translateX,u=t.translateY,h=Math.max(o,1),f={a:{x:n.x,y:n.y},d:{x:n.x+n.width,y:n.y+n.height}},c={x:(n.x+n.width)/2+l*h,y:(n.y+n.height)/2+u*h},v={height:n.height*h,width:n.width*h},p={a:{x:c.x-v.width/2,y:c.y-v.height/2},d:{x:c.x+v.width/2,y:c.y+v.height/2}},_=100;o0&&void 0!==arguments[0]?arguments[0]:{};return function(n,c){var o=t.devices||[u.MEDIA_TYPE.AUDIO,u.MEDIA_TYPE.VIDEO],s={dispatch:n,getState:c},l=function(o){if((0,T.getLocalTrack)(c()['features/base/tracks'],o,!0))throw new Error("Local track for "+o+" already exists");var l=(0,T.createLocalTracksF)({cameraDeviceId:t.cameraDeviceId,devices:[o],facingMode:t.facingMode||u.CAMERA_FACING_MODE.USER,micDeviceId:t.micDeviceId},!1,s).then(function(t){if(1!==t.length)throw new Error("Expected exactly 1 track, but was given "+t.length+" tracks for device: "+o+".");return l.canceled?I(t).then(function(){return n(M(o))}):n(v(t[0]))},function(t){return n(l.canceled?M(o):h(t,o))});l.cancel=function(){return l.canceled=!0,l},n({type:f.TRACK_WILL_CREATE,track:{gumProcess:l,local:!0,mediaType:o}})},E=o,A=Array.isArray(E),k=0;for(E=A?E:E["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var D;if(A){if(k>=E.length)break;D=E[k++]}else{if((k=E.next()).done)break;D=k.value}l(D)}}}function A(t){return{type:f.TRACK_NO_DATA_FROM_SOURCE,track:t}}function k(t){return function(n,c){var u,s=(0,T.getTrackByJitsiTrack)(c()['features/base/tracks'],t);if(s){if(s.isReceivingData)u=void 0;else{var f=(0,o.showErrorNotification)({descriptionKey:'dialog.cameraNotSendingData',titleKey:'dialog.cameraNotSendingDataTitle'});n(f),u={uid:f.uid}}n(_(t,u))}}}function v(t){return function(n,T){t.on(c.JitsiTrackEvents.TRACK_MUTE_CHANGED,function(){return n(D(t))}),t.on(c.JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,function(c){return n(R(t,c))});var l,E,v,_=t.isLocal(),p=t.getType();if(_){var y=(0,s.getLocalParticipant)(T);if(y&&(v=y.id),l=t.isReceivingData(),t.on(c.JitsiTrackEvents.NO_DATA_FROM_SOURCE,function(){return n(A({jitsiTrack:t}))}),!l)if(p===u.MEDIA_TYPE.AUDIO){var C=(0,o.showNotification)({descriptionKey:'dialog.micNotSendingData',titleKey:'dialog.micNotSendingDataTitle'});n(C),E={uid:C.uid}}else{E={timeout:setTimeout(function(){return n(k(t))},5e3)}}}else v=t.getParticipantId(),l=!0;return n({type:f.TRACK_ADDED,track:{jitsiTrack:t,isReceivingData:l,local:_,mediaType:p,mirror:S(t),muted:t.isMuted(),noDataFromSourceNotificationInfo:E,participantId:v,videoStarted:!1,videoType:t.videoType}})}}function D(t){return{type:f.TRACK_UPDATED,track:{jitsiTrack:t,muted:t.isMuted()}}}function _(t,n){return{type:f.TRACK_UPDATED,track:{jitsiTrack:t,noDataFromSourceNotificationInfo:n}}}function p(t){return t.removeAllListeners(c.JitsiTrackEvents.TRACK_MUTE_CHANGED),t.removeAllListeners(c.JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED),t.removeAllListeners(c.JitsiTrackEvents.NO_DATA_FROM_SOURCE),{type:f.TRACK_REMOVED,track:{jitsiTrack:t}}}function R(t,n){return{type:f.TRACK_UPDATED,track:{jitsiTrack:t,videoType:n}}}function y(t){var n=function(t){return l.default.error('gumProcess.cancel failed',JSON.stringify(t))};return Promise.all(t()['features/base/tracks'].filter(function(t){return t.local}).map(function(t){var c=t.gumProcess;return c&&c.cancel().catch(n)}))}function C(t){return function(n){return I(t).then(function(){return Promise.all(t.map(function(t){return n(p(t))}))})}}function I(t){return Promise.all(t.map(function(t){return t.dispose().catch(function(t){if(t.name!==c.JitsiTrackErrors.TRACK_IS_DISPOSED)throw t})}))}function h(t,n){var c=t.gum;return function(t){if(c){var o=c.error;o&&t({type:f.TRACK_CREATE_ERROR,permissionDenied:'SecurityError'===o.name,trackType:n})}}}function S(t){return t&&t.isLocal()&&t.isVideoTrack()&&t.getCameraFacingMode()===u.CAMERA_FACING_MODE.USER}function M(t){return{type:f.TRACK_CREATE_CANCELED,trackType:t}}},794,[3,385,388,587,744,532,795,796,884]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.TRACK_WILL_CREATE=e.TRACK_UPDATED=e.TRACK_REMOVED=e.TRACK_NO_DATA_FROM_SOURCE=e.TRACK_CREATE_ERROR=e.TRACK_CREATE_CANCELED=e.TRACK_ADDED=e.TOGGLE_SCREENSHARING=void 0;e.TOGGLE_SCREENSHARING='TOGGLE_SCREENSHARING';e.TRACK_ADDED='TRACK_ADDED';e.TRACK_CREATE_CANCELED='TRACK_CREATE_CANCELED';e.TRACK_CREATE_ERROR='TRACK_CREATE_ERROR';e.TRACK_NO_DATA_FROM_SOURCE='TRACK_NO_DATA_FROM_SOURCE';e.TRACK_REMOVED='TRACK_REMOVED';e.TRACK_UPDATED='TRACK_UPDATED';e.TRACK_WILL_CREATE='TRACK_WILL_CREATE'},795,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.createLocalTracksF=function(t,n,u){t||(t={});var l=t,v=l.cameraDeviceId,k=l.micDeviceId;if('undefined'!=typeof APP){u||(u=APP.store);var T=u.getState();void 0!==v&&null!==v||(v=(0,s.getUserSelectedCameraDeviceId)(T)),void 0!==k&&null!==k||(k=(0,s.getUserSelectedMicDeviceId)(T))}var p=u.getState(),S=p['features/base/config'],h=S.constraints,I=S.desktopSharingFrameRate,D=S.firefox_fake_device,E=S.resolution;return(p['features/blur'].blurEnabled?(0,c.getBlurEffect)().then(function(t){return[t]}).catch(function(t){return f.default.error('Failed to obtain the blur effect instance with error: ',t),Promise.resolve([])}):Promise.resolve([])).then(function(c){return o.default.createLocalTracks({cameraDeviceId:v,constraints:h,desktopSharingExtensionExternalInstallation:t.desktopSharingExtensionExternalInstallation,desktopSharingFrameRate:I,desktopSharingSourceDevice:t.desktopSharingSourceDevice,desktopSharingSources:t.desktopSharingSources,devices:t.devices.slice(0),effects:c,firefox_fake_device:D,micDeviceId:k,resolution:E},n).catch(function(n){return f.default.error('Failed to create local tracks',t.devices,n),Promise.reject(n)})})},e.getLocalAudioTrack=function(t){return l(t,u.MEDIA_TYPE.AUDIO)},e.getLocalTrack=l,e.getLocalTracks=v,e.getLocalVideoTrack=function(t){return l(t,u.MEDIA_TYPE.VIDEO)},e.getTrackByMediaTypeAndParticipant=k,e.getTrackByJitsiTrack=function(t,n){return t.find(function(t){return t.jitsiTrack===n})},e.getTracksByMediaType=function(t,n){return t.filter(function(t){return t.mediaType===n})},e.isLocalTrackMuted=function(t,n){var c=l(t,n);return!c||c.muted},e.isRemoteTrackMuted=function(t,n,c){var o=k(t,n,c);return!o||o.muted},e.isUserInteractionRequiredForUnmute=function(t){return o.browser.isUserInteractionRequiredForUnmute()&&window&&window.self!==window.top&&!t['features/base/user-interaction'].interacted},e.setTrackMuted=function(t,n){if(n=Boolean(n),t.isMuted()===n)return Promise.resolve();var c=n?'mute':'unmute';return t[c]().catch(function(t){t.name!==o.JitsiTrackErrors.TRACK_IS_DISPOSED&&f.default.error("set track "+c+" failed",t)})};var c=r(d[2]),o=n(r(d[3])),u=r(d[4]),s=r(d[5]),f=t(r(d[6]));function l(t,n){return v(t,arguments.length>2&&void 0!==arguments[2]&&arguments[2]).find(function(t){return t.mediaType===n})}function v(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return t.filter(function(t){return t.local&&(t.jitsiTrack||n)})}function k(t,n,c){return t.find(function(t){return t.participantId===c&&t.mediaType===n})}},796,[3,2,797,388,744,833,884]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})}),r(d[3])},797,[798,808,800,820]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.toggleBlurEffect=function(t){return function(u,s){var E=s();if(E['features/blur'].blurEnabled!==t){var b=(0,n.getLocalVideoTrack)(E['features/base/tracks']),v=b.jitsiTrack;return(0,f.getBlurEffect)().then(function(n){return v.setEffect(t?n:void 0).then(function(){u(t?o():l())}).catch(function(n){u(t?l():o()),c.default.error('setEffect failed with error:',n)})}).catch(function(t){u(l()),c.default.error('getBlurEffect failed with error:',t)})}return Promise.resolve()}},e.blurEnabled=o,e.blurDisabled=l;var n=r(d[1]),u=r(d[2]),f=r(d[3]),c=t(r(d[4]));function o(){return{type:u.BLUR_ENABLED}}function l(){return{type:u.BLUR_DISABLED}}},798,[3,793,799,800,807]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.BLUR_DISABLED=e.BLUR_ENABLED=void 0;e.BLUR_ENABLED='BLUR_ENABLED';e.BLUR_DISABLED='BLUR_DISABLED'},799,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.getBlurEffect=function(){var f=(0,t.getJitsiMeetGlobalNS)();if(f.effects&&f.effects.createBlurEffect)return f.effects.createBlurEffect();return(0,t.loadScript)('libs/video-blur-effect.min.js').then(function(){return f.effects.createBlurEffect()})};var t=r(d[0])},800,[801]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})})},801,[802,803,805,806]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.createDeferred=function(){var n={};return n.promise=new Promise(function(o,t){n.resolve=o,n.reject=t}),n},e.escapeRegexp=function(o){if('string'!=typeof o)throw new TypeError('Expected a string');return o.replace(n,'\\$&')},e.getBaseUrl=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window,o=n.document.querySelector('base');if(o&&o.href)return o.href;var t=n.location,u=t.protocol,c=t.host;return u+"//"+c},e.getJitsiMeetGlobalNS=function(){window.JitsiMeetJS||(window.JitsiMeetJS={});window.JitsiMeetJS.app||(window.JitsiMeetJS.app={});return window.JitsiMeetJS.app},e.assignIfDefined=function(n,o){var t=Object(n);for(var u in o)if(o.hasOwnProperty(u)){var c=o[u];void 0!==c&&(t[u]=c)}return t},e.reportError=function(n){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:'';console.error(o,n),window.onerror&&window.onerror(o,null,null,null,n)};var n=/[|\\{}()[\]^$+*?.-]/g},802,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.doGetJSON=function(o,u){var c=fetch(o).then(function(t){var n=t.json();return t.ok?n:n.then(function(t){return Promise.reject(t)})});if(u)return(0,t.timeoutPromise)(c,n).catch(function(o){return o.status>=400&&o.status<500?Promise.reject(o):(0,t.timeoutPromise)(c,n)});return c};var t=r(d[0]),n=3e3},803,[804]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.timeoutPromise=function(t,n){return new Promise(function(o,u){var c=setTimeout(function(){return u(new Error('timeout'))},n);t.then(function(t){o(t),clearTimeout(c)},function(t){u(t),clearTimeout(c)})})}},804,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.loadScript=function(n,o){return new Promise(function(u,c){var s=new URL(n),l=s.hostname,f=s.pathname,h=s.protocol;if(l&&f&&h){var p=fetch(n,{method:'GET'});o&&(p=(0,t.timeoutPromise)(p,o)),p.then(function(t){switch(t.status){case 200:return t.responseText||t.text();default:throw t.statusText}}).then(function(t){eval.call(window,t)}).then(u,c)}else c("unexpected url: "+n)})};var t=r(d[0])},805,[804]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.getLocationContextRoot=h,e.parseStandardURIString=f,e.parseURIString=function(t){if('string'!=typeof t)return;var n=f(s(t));n.contextRoot=h(n);var c=n.pathname,u=c.lastIndexOf('/'),v=c.substring(u+1)||void 0;if(v){var p=o(v);p!==v&&(v=p,n.pathname=c.substring(0,u+1)+(v||''))}return n.room=v,n},e.toURLString=function(t){var n;switch(typeof t){case'object':t&&(n=t instanceof URL?t.href:v(t));break;case'string':n=String(t)}return n},e.urlObjectToString=v,e.URI_PROTOCOL_PATTERN=e.APP_LINK_SCHEME=void 0;e.APP_LINK_SCHEME='org.jitsi.meet:';var t='[\\:\\?#\\[\\]@!$&\'()*+,;=>0&&void 0!==arguments[0]?arguments[0]:{},n=[];for(var o in t)try{n.push(o+"="+encodeURIComponent(JSON.stringify(t[o])))}catch(t){console.warn("Error encoding "+o+": "+t)}return n}function f(t){var o,s,h,c={toString:u};if(t=t.replace(/\s/g,''),(s=(o=new RegExp(n,'gi')).exec(t))&&(c.protocol=s[1].toLowerCase(),t=t.substring(o.lastIndex)),s=(o=new RegExp("^(//[^/?#]+)",'gi')).exec(t)){var f=s[1].substring(2);t=t.substring(o.lastIndex);var v=f.indexOf('@');-1!==v&&(f=f.substring(v+1)),c.host=f;var p=f.lastIndexOf(':');-1!==p&&(c.port=f.substring(p+1),f=f.substring(0,p)),c.hostname=f}if((s=(o=new RegExp("^([^?#]*)",'gi')).exec(t))&&(h=s[1],t=t.substring(o.lastIndex)),h?h.startsWith('/')||(h="/"+h):h='/',c.pathname=h,t.startsWith('?')){var l=t.indexOf('#',1);-1===l&&(l=t.length),c.search=t.substring(0,l),t=t.substring(l)}else c.search='';return c.hash=t.startsWith('#')?t:'',c}function u(t){var n=t||this,o=n.hash,s=n.host,h=n.pathname,c=n.protocol,f=n.search,u='';return c&&(u+=c),s&&(u+="//"+s),u+=h||'/',f&&(u+=f),o&&(u+=o),u}function v(t){var n=f(s(t.serverURL&&t.room?new URL(t.room,t.serverURL).toString():t.room?t.room:t.url||''));if(!n.protocol){var o=t.protocol||t.scheme;o&&(o.endsWith(':')||(o+=':'),n.protocol=o)}var h=n.pathname;if(!n.host){var u=t.domain||t.host||t.hostname;if(u){var v=f(s("org.jitsi.meet://"+u)),p=v.host,l=v.hostname,x=v.pathname,R=v.port;p&&(n.host=p,n.hostname=l,n.port=R),'/'===h&&'/'!==x&&(h=x)}}var O=t.roomName||t.room;!O||!n.pathname.endsWith('/')&&n.pathname.endsWith("/"+O)||(h.endsWith('/')||(h+='/'),h+=O),n.pathname=h;var b=t.jwt;if(b){var w=n.search;-1===w.indexOf('?jwt=')&&-1===w.indexOf('&jwt=')&&(w.startsWith('?')||(w="?"+w),1===w.length||(w+='&'),w+="jwt="+b,n.search=w)}for(var I=n.hash,E=['config','interfaceConfig','devices'],L=0;L0&&void 0!==arguments[0]?arguments[0]:{};switch((arguments.length>1?arguments[1]:void 0).type){case l.BLUR_ENABLED:return(0,t.default)({},u,{blurEnabled:!0});case l.BLUR_DISABLED:return(0,t.default)({},u,{blurEnabled:!1})}return u})},820,[3,54,534,821,799]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var n={PersistenceRegistry:!0};Object.defineProperty(e,"PersistenceRegistry",{enumerable:!0,get:function(){return o.default}});var c=r(d[1]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}}))});var o=t(r(d[2]));r(d[3])},821,[3,822,829,832]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})},822,[823]); -__d(function(g,r,i,a,m,e,d){r(d[0])},823,[824]); -__d(function(g,r,i,a,m,e,d){var o,t=r(d[0])(r(d[1]));void 0===(o=g||window||this).localStorage&&(o.localStorage=new t.default('@jitsi-meet/')),void 0===o.sessionStorage&&(o.sessionStorage=new t.default)},824,[3,825]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),l=t(r(d[2])),u=t(r(d[3])),f=t(r(d[4])),o=(function(){function t(n){var u=this;(0,l.default)(this,t),this._keyPrefix=n;var f=this._initializeAsync();f&&(this._initializing=f,f.finally(function(){u._initializing===f&&(u._initializing=void 0)}))}return(0,u.default)(t,[{key:"clear",value:function(){for(var t=Object.keys(this),n=0;n=s.length)break;v=s[y++]}else{if((y=s.next()).done)break;v=y.value}var c=v,k=(0,n.default)(c,2),_=k[0],P=k[1];_=_.substring(o),t.hasOwnProperty(_)||(t[_]=P)}l()})})})}},{key:"key",value:function(t){var n=Object.keys(this);return t "+u),this._checksum=u}}},{key:"register",value:function(t){var s=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],u=arguments.length>2?arguments[2]:void 0;this._elements[t]=s,this._defaultStates[t]=u}},{key:"_calculateChecksum",value:function(t){try{return n.default.hex(JSON.stringify(t)||'')}catch(s){return c.default.error('Error calculating checksum for state',t,s),''}}},{key:"_getFilteredState",value:function(t){for(var s={},u=Object.keys(this._elements),l=0;l>2]|=t[n]<>6,R[f++]=128|63&h):h<55296||h>=57344?(R[f++]=224|h>>12,R[f++]=128|h>>6&63,R[f++]=128|63&h):(h=65536+((1023&h)<<10|1023&t.charCodeAt(++n)),R[f++]=240|h>>18,R[f++]=128|h>>12&63,R[f++]=128|h>>6&63,R[f++]=128|63&h);else for(f=this.start;n>2]|=h<>2]|=(192|h>>6)<>2]|=(128|63&h)<=57344?(u[f>>2]|=(224|h>>12)<>2]|=(128|h>>6&63)<>2]|=(128|63&h)<>2]|=(240|h>>18)<>2]|=(128|h>>12&63)<>2]|=(128|h>>6&63)<>2]|=(128|63&h)<=64?(this.start=f-64,this.hash(),this.hashed=!0):this.start=f}return this}},Md5.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,s=this.lastByteIndex;t[s>>2]|=EXTRA[3&s],s>=56&&(this.hashed||this.hash(),t[0]=t[16],t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]=this.bytes<<3,this.hash()}},Md5.prototype.hash=function(){var t,s,o,h,f,n,A=this.blocks;this.first?s=((s=((t=((t=A[0]-680876937)<<7|t>>>25)-271733879<<0)^(o=((o=(-271733879^(h=((h=(-1732584194^2004318071&t)+A[1]-117830708)<<12|h>>>20)+t<<0)&(-271733879^t))+A[2]-1126478375)<<17|o>>>15)+h<<0)&(h^t))+A[3]-1316259209)<<22|s>>>10)+o<<0:(t=this.h0,s=this.h1,o=this.h2,s=((s+=((t=((t+=((h=this.h3)^s&(o^h))+A[0]-680876936)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[1]-389564586)<<12|h>>>20)+t<<0)&(t^s))+A[2]+606105819)<<17|o>>>15)+h<<0)&(h^t))+A[3]-1044525330)<<22|s>>>10)+o<<0),s=((s+=((t=((t+=(h^s&(o^h))+A[4]-176418897)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[5]+1200080426)<<12|h>>>20)+t<<0)&(t^s))+A[6]-1473231341)<<17|o>>>15)+h<<0)&(h^t))+A[7]-45705983)<<22|s>>>10)+o<<0,s=((s+=((t=((t+=(h^s&(o^h))+A[8]+1770035416)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[9]-1958414417)<<12|h>>>20)+t<<0)&(t^s))+A[10]-42063)<<17|o>>>15)+h<<0)&(h^t))+A[11]-1990404162)<<22|s>>>10)+o<<0,s=((s+=((t=((t+=(h^s&(o^h))+A[12]+1804603682)<<7|t>>>25)+s<<0)^(o=((o+=(s^(h=((h+=(o^t&(s^o))+A[13]-40341101)<<12|h>>>20)+t<<0)&(t^s))+A[14]-1502002290)<<17|o>>>15)+h<<0)&(h^t))+A[15]+1236535329)<<22|s>>>10)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[1]-165796510)<<5|t>>>27)+s<<0)^s))+A[6]-1069501632)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[11]+643717713)<<14|o>>>18)+h<<0)^h))+A[0]-373897302)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[5]-701558691)<<5|t>>>27)+s<<0)^s))+A[10]+38016083)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[15]-660478335)<<14|o>>>18)+h<<0)^h))+A[4]-405537848)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[9]+568446438)<<5|t>>>27)+s<<0)^s))+A[14]-1019803690)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[3]-187363961)<<14|o>>>18)+h<<0)^h))+A[8]+1163531501)<<20|s>>>12)+o<<0,s=((s+=((h=((h+=(s^o&((t=((t+=(o^h&(s^o))+A[13]-1444681467)<<5|t>>>27)+s<<0)^s))+A[2]-51403784)<<9|h>>>23)+t<<0)^t&((o=((o+=(t^s&(h^t))+A[7]+1735328473)<<14|o>>>18)+h<<0)^h))+A[12]-1926607734)<<20|s>>>12)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[5]-378558)<<4|t>>>28)+s<<0))+A[8]-2022574463)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[11]+1839030562)<<16|o>>>16)+h<<0))+A[14]-35309556)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[1]-1530992060)<<4|t>>>28)+s<<0))+A[4]+1272893353)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[7]-155497632)<<16|o>>>16)+h<<0))+A[10]-1094730640)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[13]+681279174)<<4|t>>>28)+s<<0))+A[0]-358537222)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[3]-722521979)<<16|o>>>16)+h<<0))+A[6]+76029189)<<23|s>>>9)+o<<0,s=((s+=((n=(h=((h+=((f=s^o)^(t=((t+=(f^h)+A[9]-640364487)<<4|t>>>28)+s<<0))+A[12]-421815835)<<11|h>>>21)+t<<0)^t)^(o=((o+=(n^s)+A[15]+530742520)<<16|o>>>16)+h<<0))+A[2]-995338651)<<23|s>>>9)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[0]-198630844)<<6|t>>>26)+s<<0)|~o))+A[7]+1126891415)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[14]-1416354905)<<15|o>>>17)+h<<0)|~t))+A[5]-57434055)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[12]+1700485571)<<6|t>>>26)+s<<0)|~o))+A[3]-1894986606)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[10]-1051523)<<15|o>>>17)+h<<0)|~t))+A[1]-2054922799)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[8]+1873313359)<<6|t>>>26)+s<<0)|~o))+A[15]-30611744)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[6]-1560198380)<<15|o>>>17)+h<<0)|~t))+A[13]+1309151649)<<21|s>>>11)+o<<0,s=((s+=((h=((h+=(s^((t=((t+=(o^(s|~h))+A[4]-145523070)<<6|t>>>26)+s<<0)|~o))+A[11]-1120210379)<<10|h>>>22)+t<<0)^((o=((o+=(t^(h|~s))+A[2]+718787259)<<15|o>>>17)+h<<0)|~t))+A[9]-343485551)<<21|s>>>11)+o<<0,this.first?(this.h0=t+1732584193<<0,this.h1=s-271733879<<0,this.h2=o-1732584194<<0,this.h3=h+271733878<<0,this.first=!1):(this.h0=this.h0+t<<0,this.h1=this.h1+s<<0,this.h2=this.h2+o<<0,this.h3=this.h3+h<<0)},Md5.prototype.hex=function(){this.finalize();var t=this.h0,s=this.h1,o=this.h2,h=this.h3;return HEX_CHARS[t>>4&15]+HEX_CHARS[15&t]+HEX_CHARS[t>>12&15]+HEX_CHARS[t>>8&15]+HEX_CHARS[t>>20&15]+HEX_CHARS[t>>16&15]+HEX_CHARS[t>>28&15]+HEX_CHARS[t>>24&15]+HEX_CHARS[s>>4&15]+HEX_CHARS[15&s]+HEX_CHARS[s>>12&15]+HEX_CHARS[s>>8&15]+HEX_CHARS[s>>20&15]+HEX_CHARS[s>>16&15]+HEX_CHARS[s>>28&15]+HEX_CHARS[s>>24&15]+HEX_CHARS[o>>4&15]+HEX_CHARS[15&o]+HEX_CHARS[o>>12&15]+HEX_CHARS[o>>8&15]+HEX_CHARS[o>>20&15]+HEX_CHARS[o>>16&15]+HEX_CHARS[o>>28&15]+HEX_CHARS[o>>24&15]+HEX_CHARS[h>>4&15]+HEX_CHARS[15&h]+HEX_CHARS[h>>12&15]+HEX_CHARS[h>>8&15]+HEX_CHARS[h>>20&15]+HEX_CHARS[h>>16&15]+HEX_CHARS[h>>28&15]+HEX_CHARS[h>>24&15]},Md5.prototype.toString=Md5.prototype.hex,Md5.prototype.digest=function(){this.finalize();var t=this.h0,s=this.h1,o=this.h2,h=this.h3;return[255&t,t>>8&255,t>>16&255,t>>24&255,255&s,s>>8&255,s>>16&255,s>>24&255,255&o,o>>8&255,o>>16&255,o>>24&255,255&h,h>>8&255,h>>16&255,h>>24&255]},Md5.prototype.array=Md5.prototype.digest,Md5.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(16),s=new Uint32Array(t);return s[0]=this.h0,s[1]=this.h1,s[2]=this.h2,s[3]=this.h3,t},Md5.prototype.buffer=Md5.prototype.arrayBuffer,Md5.prototype.base64=function(){for(var t,s,o,h='',f=this.array(),n=0;n<15;)t=f[n++],s=f[n++],o=f[n++],h+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[63&(t<<4|s>>>4)]+BASE64_ENCODE_CHAR[63&(s<<2|o>>>6)]+BASE64_ENCODE_CHAR[63&o];return t=f[n],h+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[t<<4&63]+'=='};var exports=createMethod();COMMON_JS?m.exports=exports:(root.md5=exports,AMD&&define(function(){return exports}))})()},830,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/storage');e.default=t},831,[576]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=t(r(d[1])),u=r(d[2]),o=t(r(d[3])),f=n.default.throttle(function(t){return o.default.persistState(t)},2e3);'function'==typeof window.addEventListener&&window.addEventListener('unload',function(){f.flush()}),u.MiddlewareRegistry.register(function(t){return function(n){return function(o){var c=(0,u.toState)(t),s=n(o),l=(0,u.toState)(t);return c===l||f(l),s}}})},832,[3,536,534,829]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})}),r(d[4]),r(d[5])},833,[834,835,836,837,872,879]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.updateSettings=function(n){return{type:t.SETTINGS_UPDATED,settings:n}};var t=r(d[0])},834,[835]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SETTINGS_UPDATED=void 0;e.SETTINGS_UPDATED='SETTINGS_UPDATED'},835,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.DEFAULT_SERVER_URL=void 0;e.DEFAULT_SERVER_URL='https://meet.jit.si'},836,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.getPropertyValue=function(t,v,n){n=(0,c.default)({config:!0,jwt:!0,settings:!0,urlParams:!0},n);var l=(0,u.toState)(t);if(n.jwt){var f=l['features/base/jwt'][v];if(void 0!==f)return f[v]}if(n.urlParams){var o=(0,s.parseURLParams)(l['features/base/connection'].locationURL),b=o["config."+v];if(void 0!==b)return b}if(n.settings){var D=l['features/base/settings'][v];if(void 0!==D)return D}if(n.config){var S=l['features/base/config'][v];if(void 0!==S)return S}return},e.getServerURL=function(t){return(0,u.toState)(t)['features/base/settings'].serverURL||v.DEFAULT_SERVER_URL},e.getUserSelectedCameraDeviceId=function(t){var c=(0,u.toState)(t),s=c['features/base/settings'],v=s.userSelectedCameraDeviceId,l=s.userSelectedCameraDeviceLabel;return n({availableDevices:c['features/base/devices'].availableDevices.videoInput,matchRegex:/\s#\d*(?!.*\s#\d*)/,userSelectedDeviceId:v,userSelectedDeviceLabel:l,replacement:''})},e.getUserSelectedMicDeviceId=function(t){var c=(0,u.toState)(t),s=c['features/base/settings'],v=s.userSelectedMicDeviceId,l=s.userSelectedMicDeviceLabel;return n({availableDevices:c['features/base/devices'].availableDevices.audioInput,matchRegex:/\s\(\d*-\s(?!.*\s\(\d*-\s)/,userSelectedDeviceId:v,userSelectedDeviceLabel:l,replacement:' ('})},e.getUserSelectedOutputDeviceId=function(t){var c=(0,u.toState)(t),s=c['features/base/settings'],v=s.userSelectedAudioOutputDeviceId,l=s.userSelectedAudioOutputDeviceLabel;return n({availableDevices:c['features/base/devices'].availableDevices.audioOutput,matchRegex:void 0,userSelectedDeviceId:v,userSelectedDeviceLabel:l,replacement:void 0})};var c=t(r(d[1])),s=r(d[2]),u=r(d[3]),v=r(d[4]);function n(t){var c=t.availableDevices,s=t.matchRegex,u=t.userSelectedDeviceId,v=t.userSelectedDeviceLabel,n=t.replacement;if(!v||!u)return u;if(c.find(function(t){return t.deviceId===u}))return u;var l=s?v.replace(s,n):v,f=c.find(function(t){var c=t.label;if(!c)return!1;if(l===c)return!0;var u=c.replace(s,n);return l===u});return f?f.deviceId:u}},837,[3,54,838,534,836]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})}),r(d[3]),r(d[4])},838,[839,863,865,870,871]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.configWillLoad=function(n,o){return{type:t.CONFIG_WILL_LOAD,locationURL:n,room:o}},e.loadConfigError=function(n,o){return{type:t.LOAD_CONFIG_ERROR,error:n,locationURL:o}},e.setConfig=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(o,c){var u=c()['features/base/connection'].locationURL;u&&(0,f.setConfigFromURLParams)(n,window.interfaceConfig,window.loggingConfig,u),o({type:t.SET_CONFIG,config:n})}},e.storeConfig=function(t,f){return function(u){var l=!1;try{void 0!==window.config&&window.config===f||(window.localStorage.setItem(c._CONFIG_STORE_PREFIX+"/"+t,JSON.stringify(f)),l=!0)}catch(n){}if(l)try{u((0,n.addKnownDomains)((0,o.parseURIString)(t).host))}catch(n){}return l}};var n=r(d[0]),o=r(d[1]),t=r(d[2]),c=r(d[3]),f=r(d[4])},839,[840,801,863,864,865]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})}),r(d[2]),r(d[3])},840,[841,842,843,862]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.addKnownDomains=function(o){return{type:n.ADD_KNOWN_DOMAINS,knownDomains:'string'==typeof o?[o]:o}};var n=r(d[0])},841,[842]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.ADD_KNOWN_DOMAINS=void 0;e.ADD_KNOWN_DOMAINS='ADD_KNOWN_DOMAINS'},842,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),o=r(d[2]),s=r(d[3]),c=r(d[4]),u=r(d[5]);function f(t){var n,o=t.dispatch,s=(0,t.getState)()['features/base/connection'].locationURL;s&&(n=s.host)&&o((0,u.addKnownDomains)(n))}s.MiddlewareRegistry.register(function(s){return function(h){return function(R){var _,p,w,L,S=h(R);switch(R.type){case n.APP_WILL_MOUNT:p=(_=s).dispatch,w=_.getState,L=(0,c.parseURIString)((0,t.getDefaultURL)(w)),p((0,u.addKnownDomains)(L.host));break;case o.SET_ROOM:f(s)}return S}}})},843,[381,844,383,534,801,841]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})});var o=r(d[3]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}})}),r(d[4])},844,[845,846,847,860,861]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.appWillMount=function(t){return function(p){'object'==typeof APP&&APP.API.init(),p({type:n.APP_WILL_MOUNT,app:t})}},e.appWillUnmount=function(t){return{type:n.APP_WILL_UNMOUNT,app:t}};var n=r(d[0])},845,[846]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.APP_WILL_UNMOUNT=e.APP_WILL_MOUNT=void 0;e.APP_WILL_MOUNT='APP_WILL_MOUNT';e.APP_WILL_UNMOUNT='APP_WILL_UNMOUNT'},846,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"BaseApp",{enumerable:!0,get:function(){return n.default}});var n=t(r(d[1]))},847,[3,848]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),u=n(r(d[3])),l=n(r(d[4])),s=n(r(d[5])),c=n(r(d[6])),f=n(r(d[7])),v=t(r(d[8])),h=r(d[9]),p=r(d[10]),y=r(d[11]),_=n(r(d[12])),P=r(d[13]),E=r(d[14]),w=r(d[15]),S=r(d[16]),k=r(d[17]),M=n(r(d[18])),R=(function(t){function n(t){var u;return(0,o.default)(this,n),(u=(0,l.default)(this,(0,s.default)(n).call(this,t))).state={route:{},store:void 0},u}return(0,c.default)(n,t),(0,u.default)(n,[{key:"componentDidMount",value:function(){var t=this;this._init=this._initStorage().catch(function(t){M.default.error(t)}).then(function(){return new Promise(function(n){t.setState({store:t._createStore()},n)})}).then(function(){return t.state.store.dispatch((0,k.appWillMount)(t))}).catch(function(t){M.default.error(t)})}},{key:"componentWillUnmount",value:function(){this.state.store.dispatch((0,k.appWillUnmount)(this))}},{key:"_initStorage",value:function(){return window.localStorage._initializing||Promise.resolve()}},{key:"render",value:function(){var t=this.state,n=t.route.component,o=t.store;return o?v.default.createElement(h.I18nextProvider,{i18n:P.i18next},v.default.createElement(p.Provider,{store:o},v.default.createElement(v.Fragment,null,this._createMainElement(n),v.default.createElement(w.SoundCollection,null),this._createExtraElement(),this._renderDialogContainer()))):null}},{key:"_createExtraElement",value:function(){return null}},{key:"_createMainElement",value:function(t,n){return t?v.default.createElement(t,n||{}):null}},{key:"_createStore",value:function(){var t,n=E.ReducerRegistry.combineReducers(),o=E.MiddlewareRegistry.applyMiddleware(_.default);'object'==typeof window&&(t=window.devToolsExtension)&&(o=(0,y.compose)(o,t()));var u=(0,y.createStore)(n,S.PersistenceRegistry.getPersistedState(),o);return E.StateListenerRegistry.subscribe(u),'undefined'!=typeof APP&&(APP.store=u),u}},{key:"_navigate",value:function(t){var n=this;return f.default.isEqual(t,this.state.route)?Promise.resolve():t.href?(window.location.href=t.href,Promise.resolve()):new Promise(function(o){n.setState({route:t},o)})}}]),n})(v.Component);e.default=R},848,[2,3,4,5,6,9,10,536,13,704,537,556,849,607,534,850,821,845,859]); -__d(function(g,r,i,a,m,e,d){'use strict';function t(t){return function(n){var u=n.dispatch,c=n.getState;return function(n){return function(f){return'function'==typeof f?f(u,c,t):n(f)}}}}e.__esModule=!0;var n=t();n.withExtraArgument=t,e.default=n},849,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})});var u=r(d[2]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}})}),r(d[3]),r(d[4])},850,[851,852,854,856,858]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e._addAudioElement=function(t,u){return{type:n._ADD_AUDIO_ELEMENT,audioElement:u,soundId:t}},e._removeAudioElement=function(t){return{type:n._REMOVE_AUDIO_ELEMENT,soundId:t}},e.playSound=function(t){return{type:n.PLAY_SOUND,soundId:t}},e.registerSound=function(u,o){var _=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{type:n.REGISTER_SOUND,soundId:u,src:(0,t.getSoundsPath)()+"/"+o,options:_}},e.stopSound=function(t){return{type:n.STOP_SOUND,soundId:t}},e.unregisterSound=function(t){return{type:n.UNREGISTER_SOUND,soundId:t}};var n=r(d[0]),t=r(d[1])},851,[852,853]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.UNREGISTER_SOUND=e.STOP_SOUND=e.REGISTER_SOUND=e.PLAY_SOUND=e._REMOVE_AUDIO_ELEMENT=e._ADD_AUDIO_ELEMENT=void 0;e._ADD_AUDIO_ELEMENT='_ADD_AUDIO_ELEMENT';e._REMOVE_AUDIO_ELEMENT='_REMOVE_AUDIO_ELEMENT';e.PLAY_SOUND='PLAY_SOUND';e.REGISTER_SOUND='REGISTER_SOUND';e.STOP_SOUND='STOP_SOUND';e.UNREGISTER_SOUND='UNREGISTER_SOUND'},852,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.getSoundsPath=function(){return(0,t.getSdkBundlePath)()};var t=r(d[0])},853,[381]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"SoundCollection",{enumerable:!0,get:function(){return t.default}});var t=n(r(d[1]))},854,[3,855]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e._mapDispatchToProps=A,e.default=void 0;var o=n(r(d[2])),u=n(r(d[3])),s=n(r(d[4])),f=n(r(d[5])),l=n(r(d[6])),p=n(r(d[7])),c=t(r(d[8])),_=r(d[9]),v=r(d[10]),h=r(d[11]),y=(function(t){function n(){return(0,u.default)(this,n),(0,f.default)(this,(0,l.default)(n).apply(this,arguments))}return(0,p.default)(n,t),(0,s.default)(n,[{key:"render",value:function(){var t=0,n=[],u=this.props._sounds.entries(),s=Array.isArray(u),f=0;for(u=s?u:u["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var l;if(s){if(f>=u.length)break;l=u[f++]}else{if((f=u.next()).done)break;l=f.value}var p=l,v=(0,o.default)(p,2),h=v[0],y=v[1],A=y.options,b=y.src;n.push(c.default.createElement(_.Audio,{key:t,setRef:this._setRef.bind(this,h),src:b,loop:A.loop})),t+=1}return n}},{key:"_setRef",value:function(t,n){n?this.props._addAudioElement(t,n):this.props._removeAudioElement(t)}}]),n})(c.Component);function A(t){return{_addAudioElement:function(n,o){t((0,h._addAudioElement)(n,o))},_removeAudioElement:function(n){t((0,h._removeAudioElement)(n))}}}var b=(0,v.connect)(function(t){return{_sounds:t['features/base/sounds']}},A)(y);e.default=b},855,[2,3,26,4,5,6,9,10,13,744,534,851]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]),u=n(r(d[3]));function s(n,t){var o=(0,n.getState)()['features/base/sounds'].get(t);o?o.audioElement?o.audioElement.play():u.default.warn("PLAY_SOUND: sound not loaded yet for id: "+t):u.default.warn("PLAY_SOUND: no sound found for id: "+t)}function f(n,t){var o=(0,n.getState)()['features/base/sounds'].get(t);if(o){var s=o.audioElement;s?s.stop():u.default.warn("STOP_SOUND: sound not loaded yet for id: "+t)}else u.default.warn("STOP_SOUND: no sound found for id: "+t)}t.MiddlewareRegistry.register(function(n){return function(t){return function(u){switch(u.type){case o.PLAY_SOUND:s(n,u.soundId);break;case o.STOP_SOUND:f(n,u.soundId)}return t(u)}}})},856,[3,534,852,857]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/sounds');e.default=t},857,[576]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),s=r(d[2]),u=n(r(d[3])),o=new Map;function E(n,o){var E=o.type===s._ADD_AUDIO_ELEMENT,c=new Map(n),_=o.soundId,f=c.get(_);return f?E?c.set(_,(0,t.assign)(f,{audioElement:o.audioElement})):c.set(_,(0,t.assign)(f,{audioElement:void 0})):u.default.warn(o.type+": no sound for id: "+_),c}function c(n,t){var s=new Map(n);return s.set(t.soundId,{src:t.src,options:t.options}),s}function _(n,t){var s=new Map(n);return s.delete(t.soundId),s}t.ReducerRegistry.register('features/base/sounds',function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s._ADD_AUDIO_ELEMENT:case s._REMOVE_AUDIO_ELEMENT:return E(n,t);case s.REGISTER_SOUND:return c(n,t);case s.UNREGISTER_SOUND:return _(n,t);default:return n}})},858,[3,534,852,857]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/app');e.default=t},859,[576]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.getAppProp=function(p,n){var o=(0,t.toState)(p)['features/base/app'];if(o){var u=o.app;if(u)return u.props[n]}return};var t=r(d[0])},860,[534]); -__d(function(g,r,i,a,m,e,d){var p=r(d[0])(r(d[1])),t=r(d[2]),n=r(d[3]);t.ReducerRegistry.register('features/base/app',function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},u=arguments.length>1?arguments[1]:void 0;switch(u.type){case n.APP_WILL_MOUNT:var f=u.app;if(t.app!==f)return(0,p.default)({},t,{app:f});break;case n.APP_WILL_UNMOUNT:if(t.app===u.app)return(0,p.default)({},t,{app:void 0})}return t})},861,[3,54,534,846]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.DEFAULT_STATE=void 0;var t=r(d[0]),n=r(d[1]),s=r(d[2]),o=r(d[3]),u=['alpha.jitsi.net','beta.meet.jit.si','meet.jit.si','8x8.vc'];e.DEFAULT_STATE=u;function f(t,n){var s=Array.isArray(t)?t:[];if(Array.isArray(n)){s=Array.from(t);var o=n,u=Array.isArray(o),f=0;for(o=u?o:o["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var y;if(u){if(f>=o.length)break;y=o[f++]}else{if((f=o.next()).done)break;y=f.value}var A=y;A=A.toLowerCase(),!s.includes(A)&&s.push(A)}}return s}s.PersistenceRegistry.register("features/base/known-domains"),n.ReducerRegistry.register("features/base/known-domains",function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:u,s=arguments.length>1?arguments[1]:void 0;switch(s.type){case o.ADD_KNOWN_DOMAINS:return f(n,s.knownDomains);case t.APP_WILL_MOUNT:return f(n,u);default:return n}})},862,[844,534,821,842]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SET_CONFIG=e.LOAD_CONFIG_ERROR=e.CONFIG_WILL_LOAD=void 0;e.CONFIG_WILL_LOAD='CONFIG_WILL_LOAD';e.LOAD_CONFIG_ERROR='LOAD_CONFIG_ERROR';e.SET_CONFIG='SET_CONFIG'},863,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e._CONFIG_STORE_PREFIX=void 0;e._CONFIG_STORE_PREFIX='config.js'},864,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t={_cleanupConfig:!0};e._cleanupConfig=function(t){l.NativeModules.AppInfo.LIBRE_BUILD&&(t.analytics.scriptURLs=[],delete t.analytics.amplitudeAPPKey,delete t.analytics.googleAnalyticsTrackingId,delete t.callStatsID,delete t.callStatsSecret)};var l=r(d[0]),n=r(d[1]);Object.keys(n).forEach(function(l){"default"!==l&&"__esModule"!==l&&(Object.prototype.hasOwnProperty.call(t,l)||Object.defineProperty(e,l,{enumerable:!0,get:function(){return n[l]}}))})},865,[17,866]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.createFakeConfig=function(t){var n=new URL(t);return{hosts:{domain:n.hostname,muc:"conference."+n.hostname},bosh:t+"http-bind",clientNode:'https://jitsi.org/jitsi-meet',p2p:{enabled:!0}}},e.obtainConfig=function(t,n){return new Promise(function(o,s){return f(t,n,function(t,n){t?o():s(n)})})},e.overrideConfigJSON=p,e.restoreConfig=function(t){var n,s=o._CONFIG_STORE_PREFIX+"/"+t;try{var l=(n=window.localStorage).getItem(s);if(l)return JSON.parse(l)||void 0}catch(t){n&&n.removeItem(s)}return},e.setConfigFromURLParams=function(t,n,o,l){var c=(0,s.default)(l),u={};t&&(u.config={}),n&&(u.interfaceConfig={}),o&&(u.loggingConfig={});for(var f=Object.keys(c),b=0;b=C.length)break;N=C[R++]}else{if((R=C.next()).done)break;N=R.value}var A=N;h=h[A]=h[A]||{}}h[v]=c[S]}p(t,n,o,u)},Object.defineProperty(e,"parseURLParams",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(e,"getRoomName",{enumerable:!0,get:function(){return c.default}});var n=t(r(d[1])),o=r(d[2]),s=t(r(d[3])),l=t(r(d[4])),c=t(r(d[5])),u=['_desktopSharingSourceDevice','_peerConnStatusOutOfLastNTimeout','_peerConnStatusRtcMuteTimeout','abTesting','analytics.disabled','autoRecord','autoRecordToken','avgRtpStatsN','callFlowsEnabled','callStatsConfIDNamespace','callStatsID','callStatsSecret','callDisplayName','callHandle','callUUID','channelLastN','constraints','debug','debugAudioLevels','defaultLanguage','desktopSharingChromeDisabled','desktopSharingChromeExtId','desktopSharingChromeMinExtVersion','desktopSharingChromeSources','desktopSharingFrameRate','desktopSharingFirefoxDisabled','desktopSharingSources','disable1On1Mode','disableAEC','disableAGC','disableAP','disableAudioLevels','disableDeepLinking','disableH264','disableHPF','disableNS','disableRemoteControl','disableRtx','disableSuspendVideo','displayJids','e2eping','enableDisplayNameInStats','enableLayerSuspension','enableLipSync','disableLocalVideoFlip','enableRemb','enableStatsID','enableTalkWhileMuted','enableTcc','etherpad_base','failICE','fileRecordingsEnabled','firefox_fake_device','forceJVB121Ratio','gatherStats','googleApiApplicationClientID','hiddenDomain','hosts','iAmRecorder','iAmSipGateway','iceTransportPolicy','ignoreStartMuted','liveStreamingEnabled','localRecording','minParticipants','nick','openBridgeChannel','p2p','preferH264','requireDisplayName','resolution','startAudioMuted','startAudioOnly','startBitrate','startSilent','startScreenSharing','startVideoMuted','startWithAudioMuted','startWithVideoMuted','subject','testing','useIPv6','useNicks','useStunTurn','webrtcIceTcpDisable','webrtcIceUdpDisable'];function f(t,n,o){l.default.info("Send config request to "+t+" for room: "+n),$.ajax(t,{contentType:'application/json',data:JSON.stringify({roomName:n}),dataType:'json',method:'POST',error:function(t,n,s){l.default.error('Get config error: ',t,s),o(!1,"Get config response status: "+n)},success:function(t){var n=window,s=n.config,c=n.interfaceConfig,u=n.loggingConfig;try{p(s,c,u,t),o(!0)}catch(t){l.default.error('Parse config error: ',t),o(!1,t)}}})}function p(t,o,s,c){for(var u=Object.keys(c),f=0;f1&&void 0!==arguments[1]&&arguments[1],h=arguments.length>2&&void 0!==arguments[2]?arguments[2]:'hash',s='search'===h?n.search:n.hash,l={},u=s&&s.substr(1).split('&')||[];if('hash'===h&&1===u.length){var v=u[0];if(v.startsWith('/')&&1===v.split('&').length)return l}return u.forEach(function(n){var h=n.split('='),s=h[0];if(s){var u;try{if(u=h[1],!o){var v=decodeURIComponent(u).replace(/\\&/,'&');u='undefined'===v?void 0:JSON.parse(v)}}catch(n){return void(0,t.reportError)(n,"Failed to parse URL parameter value: "+String(u))}l[s]=u}}),l};var t=r(d[0])},867,[801]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/config');e.default=t},868,[576]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(){var o,n=config.getroomnode,t=window.location.pathname;o=n&&'function'==typeof n?n.call(config,t):t.substring(t.lastIndexOf('/')+1).toLowerCase()||void 0;return o}},869,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),o=r(d[2]),s=r(d[3]),u=r(d[4]),c=r(d[5]);function f(n,o,u){var f=o(u),v=window.localStorage;if(v){for(var w=c._CONFIG_STORE_PREFIX+"/",_=[],h=0;;++h){var l=v.key(h);if(!l)break;var I=void 0;if(l.startsWith(w)&&(I=l.substring(w.length))){var S=(0,s.parseURIString)(I),p=void 0;S&&(p=S.host)&&_.push(p)}}_.length&&n.dispatch((0,t.addKnownDomains)(_))}return f}function v(n,t,o){var s=n.getState,u=t(o);return void 0!==window.config&&(window.config=s()['features/base/config']),u}o.MiddlewareRegistry.register(function(t){return function(o){return function(s){switch(s.type){case n.APP_WILL_MOUNT:return f(t,o,s);case u.SET_CONFIG:return v(t,o,s)}return o(s)}}})},870,[844,840,534,801,863,864]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),o=t(r(d[1])),n=t(r(d[2])),c=t(r(d[3])),l=t(r(d[4])),u=t(r(d[5])),f=r(d[6]),s=r(d[7]),v=r(d[8]),y={},R='ReactNative'===navigator.product&&!('ios'===u.default.OS&&10===u.default.Version),p={analytics:{},disableAudioLevels:!0,p2p:{disableH264:!R,preferH264:R}};function b(){return'ReactNative'===navigator.product?p:y}function L(t,o){var n=o.config;n=O(n);var c=l.default.merge({},n,{error:void 0},b());return(0,v._cleanupConfig)(c),(0,f.equals)(t,c)?t:c}function O(t){var l=t,u={analytics:[['analyticsScriptUrls','scriptURLs'],['googleAnalyticsTrackingId','googleAnalyticsTrackingId']]};return Object.keys(u).forEach(function(s){'object'!=typeof t[s]&&(l=(0,f.set)(l,s,{}));var v=u[s],y=Array.isArray(v),R=0;for(v=y?v:v["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var p;if(y){if(R>=v.length)break;p=v[R++]}else{if((R=v.next()).done)break;p=R.value}var b=p,L=(0,c.default)(b,2),O=L[0],_=L[1];if(O in l&&!(_ in l[s])){var A=l[O];l===t&&(l=(0,n.default)({},l)),delete l[O],l[s]=(0,n.default)({},l[s],(0,o.default)({},_,A))}}}),l}f.ReducerRegistry.register('features/base/config',function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:b(),o=arguments.length>1?arguments[1]:void 0;switch(o.type){case s.CONFIG_WILL_LOAD:return{error:void 0,locationURL:o.locationURL};case s.LOAD_CONFIG_ERROR:if(t.locationURL===o.locationURL)return{error:o.error};break;case s.SET_CONFIG:return L(t,o)}return t})},871,[3,55,54,26,536,413,534,863,865]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1])),n=r(d[2]),c=r(d[3]),s=r(d[4]),o=r(d[5]);function u(t){switch(t){case'displayName':return'name'}return t}function f(t,c){var s=t.dispatch,o=c.settings.startAudioOnly;'boolean'==typeof o&&s((0,n.setAudioOnly)(o,!0))}function p(n,s){var o=n.dispatch,f=n.getState,p=s.settings,l=(0,c.getLocalParticipant)(f()),y=(0,t.default)({},l);for(var h in p)p.hasOwnProperty(h)&&(y[u(h)]=p[h]);o((0,c.participantUpdated)(y))}s.MiddlewareRegistry.register(function(t){return function(n){return function(c){var s=n(c);switch(c.type){case o.SETTINGS_UPDATED:f(t,c),p(t,c)}return s}}})},872,[3,54,873,532,534,835]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})}),r(d[2])},873,[874,876,878]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.setAudioOnly=f,e.toggleAudioOnly=function(){return function(n,t){var u=t()['features/base/audio-only'].enabled;return n(f(!u,!0))}};var t=n(r(d[1])),u=r(d[2]),o=r(d[3]),l=n(r(d[4]));function f(n){var f=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return function(y,s){s()['features/base/audio-only'].enabled!==n&&((0,u.sendAnalytics)((0,u.createAudioOnlyChangedEvent)(n)),l.default.log("Audio-only "+(n?'enabled':'disabled')),y({type:o.SET_AUDIO_ONLY,audioOnly:n,ensureVideoTrack:f}),'undefined'!=typeof APP&&APP.UI.emitEvent(t.default.TOGGLE_AUDIO_ONLY,n))}}},874,[3,875,385,876,877]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;e.default={NICKNAME_CHANGED:'UI.nickname_changed',EMAIL_CHANGED:'UI.email_changed',AUDIO_MUTED:'UI.audio_muted',VIDEO_MUTED:'UI.video_muted',ETHERPAD_CLICKED:'UI.etherpad_clicked',SHARED_VIDEO_CLICKED:'UI.start_shared_video',UPDATE_SHARED_VIDEO:'UI.update_shared_video',TOGGLE_FULLSCREEN:'UI.toogle_fullscreen',FULLSCREEN_TOGGLED:'UI.fullscreen_toggled',AUTH_CLICKED:'UI.auth_clicked',TOGGLE_AUDIO_ONLY:'UI.toggle_audioonly',TOGGLE_FILMSTRIP:'UI.toggle_filmstrip',TOGGLE_SCREENSHARING:'UI.toggle_screensharing',HANGUP:'UI.hangup',LOGOUT:'UI.logout',VIDEO_DEVICE_CHANGED:'UI.video_device_changed',AUDIO_DEVICE_CHANGED:'UI.audio_device_changed',LOCAL_FLIPX_CHANGED:'UI.local_flipx_changed',RESOLUTION_CHANGED:'UI.resolution_changed',EXTERNAL_INSTALLATION_CANCELED:'UI.external_installation_canceled',SIDE_TOOLBAR_CONTAINER_TOGGLED:'UI.side_container_toggled',LOCAL_RAISE_HAND_CHANGED:'UI.local_raise_hand_changed',LARGE_VIDEO_AVATAR_VISIBLE:'UI.large_video_avatar_visible',LARGE_VIDEO_ID_CHANGED:'UI.large_video_id_changed'}},875,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SET_AUDIO_ONLY=void 0;e.SET_AUDIO_ONLY='SET_AUDIO_ONLY'},876,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=(0,r(d[0]).getLogger)('features/base/audio-only');e.default=o},877,[576]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1])),n=r(d[2]),u=r(d[3]),l={enabled:!1};n.ReducerRegistry.register('features/base/audio-only',function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:l,o=arguments.length>1?arguments[1]:void 0;switch(o.type){case u.SET_AUDIO_ONLY:return(0,t.default)({},n,{enabled:o.audioOnly});default:return n}})},878,[3,54,534,876]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),o=t(r(d[1])),c=r(d[2]),l=t(r(d[3])),u=r(d[4]),s=r(d[5]),v=r(d[6]),n=r(d[7]),I=r(d[8]),f=r(d[9]),D=t(r(d[10])),p={audioOutputDeviceId:void 0,avatarID:void 0,avatarURL:void 0,cameraDeviceId:void 0,displayName:void 0,email:void 0,localFlipX:!0,micDeviceId:void 0,serverURL:void 0,startAudioOnly:!1,startWithAudioMuted:!1,startWithVideoMuted:!1,userSelectedAudioOutputDeviceId:void 0,userSelectedCameraDeviceId:void 0,userSelectedMicDeviceId:void 0,userSelectedAudioOutputDeviceLabel:void 0,userSelectedCameraDeviceLabel:void 0,userSelectedMicDeviceLabel:void 0},w={};function S(){var t=window.localStorage.getItem('features/base/profile');if(t)try{if((t=JSON.parse(t))&&'object'==typeof t)return t.profile||t}catch(t){D.default.warn('Error parsing persisted legacy profile',t)}return{}}function y(t){var o=t,u=window.localStorage.getItem('displayname'),v=window.localStorage.getItem('email'),n=l.default.escape(window.localStorage.getItem('avatarId')),f=null===u?void 0:l.default.escape(u),D=null===v?void 0:l.default.escape(v);if(n||(n=(0,c.randomHexString)(32)),o=(0,I.assignIfDefined)({avatarID:n,displayName:f,email:D},o),!s.browser.isReactNative()){var p=JSON.parse(window.localStorage.getItem('localFlipX')||'true'),w=window.localStorage.getItem('cameraDeviceId')||'',y=window.localStorage.getItem('micDeviceId')||'',O=window.localStorage.getItem('audioOutputDeviceId')||'default';o=(0,I.assignIfDefined)({audioOutputDeviceId:O,cameraDeviceId:w,localFlipX:p,micDeviceId:y},o)}var b=S();return o=(0,I.assignIfDefined)(b,o)}Object.keys(p).forEach(function(t){w[t]=!0}),w.audioOutputDeviceId=!1,w.cameraDeviceId=!1,w.micDeviceId=!1,n.PersistenceRegistry.register("features/base/settings",w),v.ReducerRegistry.register("features/base/settings",function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:p,c=arguments.length>1?arguments[1]:void 0;switch(c.type){case u.APP_WILL_MOUNT:return y(t);case f.SETTINGS_UPDATED:return(0,o.default)({},t,c.settings)}return t})},879,[3,54,880,536,844,388,534,821,801,835,883]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})})},880,[881,882]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.randomAlphanumString=function(t){return f(t,n)},e.randomElement=o,e.randomHexDigit=function(){return o(t)},e.randomHexString=function(n){return f(n,t)},e.randomInt=u;var n='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',t='0123456789abcdef';function o(n){return n[u(0,n.length-1)]}function u(n,t){return Math.floor(Math.random()*(t-n+1))+n}function f(n,t){for(var u='',f=0;f=0)return!0;return!1}},882,[881]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/settings');e.default=t},883,[576]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=(0,r(d[0]).getLogger)('features/base/tracks');e.default=t},884,[576]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),c=r(d[1]),o=r(d[2]),n=r(d[3]),s=t(r(d[4])),u=r(d[5]),T=r(d[6]),E=r(d[7]);function f(t,o){var n=t.getState,s=t.dispatch,T=(0,E.getTrackByJitsiTrack)(n()['features/base/tracks'],o.track.jitsiTrack);if(T&&T.local){var f=T.jitsiTrack;if(T.mediaType===c.MEDIA_TYPE.AUDIO&&T.isReceivingData&&k(t,o.track),T.mediaType===c.MEDIA_TYPE.VIDEO){var A=T.noDataFromSourceNotificationInfo,D=void 0===A?{}:A;if(T.isReceivingData)D.timeout&&(clearTimeout(D.timeout),s((0,u.trackNoDataFromSourceNotificationInfoChanged)(f,void 0))),k(t,o.track);else{if(D.timeout)return;var I=setTimeout(function(){return s((0,u.showNoDataFromSourceVideoError)(f))},5e3);s((0,u.trackNoDataFromSourceNotificationInfoChanged)(f,{timeout:I}))}}}}function A(t,c){var o=t.getState,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return(0,E.getLocalTrack)(o()['features/base/tracks'],c,n)}function k(t,c){var n=t.getState,s=t.dispatch,T=(0,E.getTrackByJitsiTrack)(n()['features/base/tracks'],c.jitsiTrack)||{},f=T.jitsiTrack,A=T.noDataFromSourceNotificationInfo,k=void 0===A?{}:A;k&&k.uid&&(s((0,o.hideNotification)(k.uid)),s((0,u.trackNoDataFromSourceNotificationInfoChanged)(f,void 0)))}function D(t,c,o){var n=c.ensureTrack,s=c.muted,T=A(t,o,!0);if(T){var f=T.jitsiTrack;f&&(0,E.setTrackMuted)(f,s)}else!s&&n&&'undefined'==typeof APP&&t.dispatch((0,u.createLocalTracksA)({devices:[o]}))}n.MiddlewareRegistry.register(function(t){return function(o){return function(n){switch(n.type){case T.TRACK_NO_DATA_FROM_SOURCE:var u=o(n);return f(t,n),u;case T.TRACK_REMOVED:k(t,n.track);break;case c.SET_AUDIO_MUTED:if(!n.muted&&(0,E.isUserInteractionRequiredForUnmute)(t.getState()))return;D(t,n,c.MEDIA_TYPE.AUDIO);break;case c.SET_CAMERA_FACING_MODE:var I,_=A(t,c.MEDIA_TYPE.VIDEO);_&&(I=_.jitsiTrack)&&I.getCameraFacingMode()!==n.cameraFacingMode&&t.dispatch((0,c.toggleCameraFacingMode)());break;case c.SET_VIDEO_MUTED:if(!n.muted&&(0,E.isUserInteractionRequiredForUnmute)(t.getState()))return;D(t,n,c.MEDIA_TYPE.VIDEO);break;case c.TOGGLE_CAMERA_FACING_MODE:var P,M=A(t,c.MEDIA_TYPE.VIDEO);if(M&&(P=M.jitsiTrack)){P._switchCamera();var v=P.getCameraFacingMode()===c.CAMERA_FACING_MODE.USER;t.dispatch({type:T.TRACK_UPDATED,track:{jitsiTrack:P,mirror:v}})}break;case T.TOGGLE_SCREENSHARING:'object'==typeof APP&&APP.UI.emitEvent(s.default.TOGGLE_SCREENSHARING);break;case T.TRACK_UPDATED:if('undefined'!=typeof APP){var S=n.track.jitsiTrack,C=S.isMuted(),R=S.getParticipantId();S.isVideoTrack()?(S.isLocal()?APP.conference.setVideoMuteStatus(C):APP.UI.setVideoMuted(R,C),APP.UI.onPeerVideoTypeChanged(R,S.videoType)):S.isLocal()?APP.conference.setAudioMuteStatus(C):APP.UI.setAudioMuted(R,C)}}return o(n)}}})},885,[3,744,587,534,875,794,795,796]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),c=t(r(d[1])),n=t(r(d[2])),u=r(d[3]),T=r(d[4]),s=r(d[5]);function R(t,c){switch(c.type){case u.PARTICIPANT_ID_CHANGED:if(t.participantId===c.oldValue)return(0,n.default)({},t,{participantId:c.newValue});break;case s.TRACK_UPDATED:var T=c.track;if(t.jitsiTrack===T.jitsiTrack)for(var R in T)if(t[R]!==T[R])return(0,n.default)({},t,T);break;case s.TRACK_NO_DATA_FROM_SOURCE:var A=c.track;if(t.jitsiTrack===A.jitsiTrack){var _=A.jitsiTrack.isReceivingData();if(t.isReceivingData!==_)return(0,n.default)({},t,{isReceivingData:_})}}return t}T.ReducerRegistry.register('features/base/tracks',function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0;switch(n.type){case u.PARTICIPANT_ID_CHANGED:case s.TRACK_NO_DATA_FROM_SOURCE:case s.TRACK_UPDATED:return t.map(function(t){return R(t,n)});case s.TRACK_ADDED:var T=t;return n.track.local&&(T=t.filter(function(t){return!t.local||t.mediaType!==n.track.mediaType})),(0,c.default)(T).concat([n.track]);case s.TRACK_CREATE_CANCELED:case s.TRACK_CREATE_ERROR:return t.filter(function(t){return!t.local||t.mediaType!==n.trackType});case s.TRACK_REMOVED:return t.filter(function(t){return t.jitsiTrack!==n.track.jitsiTrack});case s.TRACK_WILL_CREATE:return(0,c.default)(t).concat([n.track]);default:return t}})},886,[3,32,54,532,534,795]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.isVideoMutedByAudioOnly=function(t){return u(t,n.VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY)},e.isVideoMutedByUser=function(t){return u(t,n.VIDEO_MUTISM_AUTHORITY.USER)},e.shouldRenderVideoTrack=function(t,n){return t&&!t.muted&&(!n||t.videoStarted)};var t=r(d[0]),n=r(d[1]);function u(n,u){var o=(0,t.toState)(n)['features/base/media'].video.muted;return Boolean(o&u)}},887,[534,747]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),u=r(d[2]),o=r(d[3]),s=r(d[4]),c=t(r(d[5])),l=r(d[6]),A=r(d[7]),T=r(d[8]),_=r(d[9]),y=r(d[10]),M=t(r(d[11])),E=r(d[12]);function I(t,u,o){var s=t.dispatch,c='active'!==o.appState;return(0,n.sendAnalytics)((0,n.createTrackMutedEvent)('video','background mode',c)),s((0,_.setVideoMuted)(c,y.VIDEO_MUTISM_AUTHORITY.BACKGROUND)),u(o)}function S(t,u,o){var s=t.dispatch,c=o.audioOnly,l=o.ensureVideoTrack;return(0,n.sendAnalytics)((0,n.createTrackMutedEvent)('video','audio-only mode',c)),s((0,_.setVideoMuted)(c,y.VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY,l)),u(o)}function O(t,u,l){var T,I=t.dispatch,S=(0,t.getState)(),O=l.room,f=(0,s.isRoomValid)(O),v={config:!0,settings:!0,urlParams:!0,jwt:!1},D=f?Boolean((0,A.getPropertyValue)(S,'startWithAudioMuted',v)):E._AUDIO_INITIAL_MEDIA_STATE.muted,p=f?Boolean((0,A.getPropertyValue)(S,'startWithVideoMuted',v)):E._VIDEO_INITIAL_MEDIA_STATE.muted;return(0,n.sendAnalytics)((0,n.createStartMutedConfigurationEvent)('local',D,p)),M.default.log("Start muted: "+(D?'audio, ':'')+(p?'video':'')),I((0,_.setAudioMuted)(D)),I((0,_.setCameraFacingMode)(y.CAMERA_FACING_MODE.USER)),I((0,_.setVideoMuted)(p)),T=!c.default.mediaDevices.supportsVideo()||Boolean((0,A.getPropertyValue)(S,'startAudioOnly',{config:f,urlParams:!0,jwt:!1,settings:!0})),(0,n.sendAnalytics)((0,n.createStartAudioOnlyEvent)(T)),M.default.log("Start audio only set to "+T.toString()),I((0,o.setAudioOnly)(T,!1)),u(l)}function f(t,u){var o=(0,t.getState)()['features/base/media'],s=Boolean(o[u.mediaType].muted);u.muted!==s&&((0,n.sendAnalytics)((0,n.createSyncTrackStateEvent)(u.mediaType,s)),M.default.log("Sync "+u.mediaType+" track muted state to "+(s?'muted':'unmuted')),u.muted=s,(0,T.setTrackMuted)(u.jitsiTrack,s))}l.MiddlewareRegistry.register(function(t){return function(n){return function(c){switch(c.type){case u.APP_STATE_CHANGED:return I(t,n,c);case o.SET_AUDIO_ONLY:return S(t,n,c);case s.SET_ROOM:return O(t,n,c);case T.TRACK_ADDED:var l=n(c),A=c.track;return A.local&&f(t,A),l}return n(c)}}})},888,[3,385,889,873,383,388,534,833,793,745,747,753,894]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})}),r(d[2]),r(d[3])},889,[890,891,892,893]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e._setAppStateListener=function(n){return{type:t._SET_APP_STATE_LISTENER,listener:n}},e.appStateChanged=function(n){return{type:t.APP_STATE_CHANGED,appState:n}};var t=r(d[0])},890,[891]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.APP_STATE_CHANGED=e._SET_APP_STATE_LISTENER=void 0;e._SET_APP_STATE_LISTENER='_SET_APP_STATE_LISTENER';e.APP_STATE_CHANGED='APP_STATE_CHANGED'},891,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),s=r(d[2]),p=r(d[3]),c=r(d[4]);function u(t,n){t((0,p.appStateChanged)(n))}function _(n,s,p){var c=n.getState,u=c()['features/background'].appStateListener,_=s(p),o=c()['features/background'].appStateListener;return u!==o&&(u&&t.AppState.removeEventListener('change',u),o&&t.AppState.addEventListener('change',o)),_}s.MiddlewareRegistry.register(function(t){return function(s){return function(o){switch(o.type){case c._SET_APP_STATE_LISTENER:return _(t,s,o);case n.APP_WILL_MOUNT:var L=t.dispatch;L((0,p._setAppStateListener)(u.bind(void 0,L)));break;case n.APP_WILL_UNMOUNT:t.dispatch((0,p._setAppStateListener)(void 0))}return s(o)}}})},892,[17,844,534,890,891]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1])),n=r(d[2]),u=r(d[3]),p={appState:'active'};n.ReducerRegistry.register('features/background',function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:p,c=arguments.length>1?arguments[1]:void 0;switch(c.type){case u._SET_APP_STATE_LISTENER:return(0,t.default)({},n,{appStateListener:c.listener});case u.APP_STATE_CHANGED:return(0,t.default)({},n,{appState:c.appState})}return n})},893,[3,54,534,891]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e._VIDEO_INITIAL_MEDIA_STATE=e._AUDIO_INITIAL_MEDIA_STATE=void 0;var E=t(r(d[1])),n=t(r(d[2])),u=r(d[3]),_=r(d[4]),A=r(d[5]),f=r(d[6]),s=r(d[7]),I=r(d[8]),l={available:!0,muted:!1};e._AUDIO_INITIAL_MEDIA_STATE=l;var c={available:!0,facingMode:I.CAMERA_FACING_MODE.USER,muted:0,transforms:{}};function o(t){return(0,n.default)({},t,{transforms:c.transforms})}function M(t,u){var _=u.streamId,A=u.transform;return(0,n.default)({},t,{transforms:(0,n.default)({},t.transforms,(0,E.default)({},_,A))})}function T(t,E){var u=E.track.jitsiTrack;if(u){var _=u.getStreamId();if(_&&_ in t.transforms){var A=(0,n.default)({},t.transforms);return delete A[_],(0,n.default)({},t,{transforms:A})}}return t}e._VIDEO_INITIAL_MEDIA_STATE=c,A.ReducerRegistry.register('features/base/media',(0,u.combineReducers)({audio:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:l,E=arguments.length>1?arguments[1]:void 0;switch(E.type){case s.SET_AUDIO_AVAILABLE:return(0,n.default)({},t,{available:E.available});case s.SET_AUDIO_MUTED:return(0,n.default)({},t,{muted:E.muted});default:return t}},video:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:c,E=arguments.length>1?arguments[1]:void 0;switch(E.type){case _.CONFERENCE_FAILED:case _.CONFERENCE_LEFT:return o(t);case s.SET_CAMERA_FACING_MODE:return(0,n.default)({},t,{facingMode:E.cameraFacingMode});case s.SET_VIDEO_AVAILABLE:return(0,n.default)({},t,{available:E.available});case s.SET_VIDEO_MUTED:return(0,n.default)({},t,{muted:E.muted});case s.STORE_VIDEO_TRANSFORM:return M(t,E);case s.TOGGLE_CAMERA_FACING_MODE:var u=t.facingMode;return u=u===I.CAMERA_FACING_MODE.USER?I.CAMERA_FACING_MODE.ENVIRONMENT:I.CAMERA_FACING_MODE.USER,(0,n.default)({},t,{facingMode:u});case f.TRACK_REMOVED:return T(t,E);default:return t}}}))},894,[3,55,54,556,383,534,793,746,747]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.PARTICIPANT_ROLE=e.PARTICIPANT_LEFT_SOUND_ID=e.PARTICIPANT_JOINED_SOUND_ID=e.MAX_DISPLAY_NAME_LENGTH=e.LOCAL_PARTICIPANT_DEFAULT_ID=e.JIGASI_PARTICIPANT_ICON=e.DEFAULT_AVATAR_RELATIVE_PATH=void 0;var A=r(d[0]);e.DEFAULT_AVATAR_RELATIVE_PATH='images/avatar.png';var I=A.IconPhone;e.JIGASI_PARTICIPANT_ICON=I;e.LOCAL_PARTICIPANT_DEFAULT_ID='local';e.MAX_DISPLAY_NAME_LENGTH=50;e.PARTICIPANT_JOINED_SOUND_ID='PARTICIPANT_JOINED_SOUND';e.PARTICIPANT_LEFT_SOUND_ID='PARTICIPANT_LEFT_SOUND';e.PARTICIPANT_ROLE={MODERATOR:'moderator',NONE:'none',PARTICIPANT:'participant'}},895,[403]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.preloadImage=function(t){if((0,o.isIconUrl)(t))return Promise.resolve(t);return new Promise(function(o,u){n.Image.prefetch(t).then(function(){return o(t)},u)})};var n=r(d[0]),o=r(d[1])},896,[17,741]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"ParticipantView",{enumerable:!0,get:function(){return n.default}});var n=t(r(d[1]))},897,[3,898]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),s=n(r(d[3])),c=n(r(d[4])),l=n(r(d[5])),p=n(r(d[6])),u=n(r(d[7])),f=t(r(d[8])),v=r(d[9]),E=r(d[10]),I=r(d[11]),h=r(d[12]),T=r(d[13]),_=r(d[14]),C=r(d[15]),y=(r(d[16]),r(d[17])),P=r(d[18]),V=r(d[19]),k=n(r(d[20])),b=(function(t){function n(){return(0,s.default)(this,n),(0,l.default)(this,(0,p.default)(n).apply(this,arguments))}return(0,u.default)(n,t),(0,c.default)(n,[{key:"_renderConnectionInfo",value:function(t){var n;switch(t){case h.JitsiParticipantConnectionStatus.INACTIVE:n='connection.LOW_BANDWIDTH';break;case h.JitsiParticipantConnectionStatus.INTERRUPTED:n='connection.USER_CONNECTION_INTERRUPTED';break;default:return null}var s=this.props,c=s.avatarSize,l=s._participantName,p=s.t,u=(0,o.default)({},k.default.connectionInfoContainer,{width:1.5*c});return f.default.createElement(v.View,{pointerEvents:"box-none",style:u},f.default.createElement(v.Text,{style:k.default.connectionInfoText},p(n,{displayName:l})))}},{key:"render",value:function(){var t=this.props,n=t._connectionStatus,s=t._renderVideo,c=t._videoTrack,l=t.onPress,p=t.tintStyle,u=n!==h.JitsiParticipantConnectionStatus.ACTIVE,I=u||this.props.tintEnabled,C=this.props.testHintId?this.props.testHintId:"org.jitsi.meet.Participant#"+this.props.participantId;return f.default.createElement(_.Container,{onClick:s?void 0:l,style:(0,o.default)({},k.default.participantView,this.props.style),touchFeedback:!1},f.default.createElement(y.TestHint,{id:C,onPress:l,value:""}),s&&f.default.createElement(T.VideoTrack,{onPress:l,videoTrack:c,waitForVideoStarted:!1,zOrder:this.props.zOrder,zoomEnabled:this.props.zoomEnabled}),!s&&f.default.createElement(v.View,{style:k.default.avatarContainer},f.default.createElement(E.Avatar,{participantId:this.props.participantId,size:this.props.avatarSize})),I&&f.default.createElement(_.TintedView,{style:u?void 0:p}),this.props.useConnectivityInfoLabel&&this._renderConnectionInfo(n))}}]),n})(f.Component);var S=(0,I.translate)((0,C.connect)(function(t,n){var o=n.disableVideo,s=n.participantId;return{_connectionStatus:h.JitsiParticipantConnectionStatus.ACTIVE,_participantName:void 0,_renderVideo:(0,V.shouldRenderParticipantVideo)(t,s)&&!o,_videoTrack:(0,P.getTrackByMediaTypeAndParticipant)(t['features/base/tracks'],T.MEDIA_TYPE.VIDEO,s)}})(b));e.default=S},898,[2,3,54,4,5,6,9,10,13,17,399,607,388,744,394,534,406,899,793,741,912]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"TestConnectionInfo",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"TestHint",{enumerable:!0,get:function(){return u.default}});var n=t(r(d[1])),u=t(r(d[2]))},899,[3,900,910]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),s=n(r(d[3])),l=n(r(d[4])),c=n(r(d[5])),u=n(r(d[6])),f=n(r(d[7])),p=t(r(d[8])),_=r(d[9]),S=r(d[10]),b=r(d[11]),h=r(d[12]),v=r(d[13]),U=(function(t){function n(t){var s;return(0,o.default)(this,n),(s=(0,l.default)(this,(0,c.default)(n).call(this,t)))._onStatsUpdated=s._onStatsUpdated.bind((0,f.default)((0,f.default)(s))),s.state={stats:{bitrate:{download:0,upload:0}}},s}return(0,u.default)(n,t),(0,s.default)(n,[{key:"_onStatsUpdated",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.setState({stats:{bitrate:{download:t.bitrate.download,upload:t.bitrate.upload}}})}},{key:"componentDidMount",value:function(){b.statsEmitter.subscribeToClientStats(this.props._localUserId,this._onStatsUpdated)}},{key:"componentDidUpdate",value:function(t){t._localUserId!==this.props._localUserId&&(b.statsEmitter.unsubscribeToClientStats(t._localUserId,this._onStatsUpdated),b.statsEmitter.subscribeToClientStats(this.props._localUserId,this._onStatsUpdated))}},{key:"componentWillUnmount",value:function(){b.statsEmitter.unsubscribeToClientStats(this.props._localUserId,this._onStatsUpdated)}},{key:"render",value:function(){return this.props._testMode?p.default.createElement(p.Fragment,{accessible:!1},p.default.createElement(h.TestHint,{id:"org.jitsi.meet.conference.connectionState",value:this.props._conferenceConnectionState}),p.default.createElement(h.TestHint,{id:"org.jitsi.meet.conference.joinedState",value:this.props._conferenceJoinedState}),p.default.createElement(h.TestHint,{id:"org.jitsi.meet.stats.rtp",value:JSON.stringify(this.state.stats)})):null}}]),n})(p.Component);var E=(0,S.connect)(function(t){var n=Boolean(t['features/base/conference'].conference),o=(0,_.getLocalParticipant)(t);return{_conferenceConnectionState:t['features/testing'].connectionState,_conferenceJoinedState:n.toString(),_localUserId:o&&o.id,_testMode:(0,v.isTestModeEnabled)(t)}})(U);e.default=E},900,[2,3,4,5,6,9,10,8,13,532,534,901,899,909]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var n={statsEmitter:!0};Object.defineProperty(e,"statsEmitter",{enumerable:!0,get:function(){return u.default}});var o=r(d[1]);Object.keys(o).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(n,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return o[t]}}))});var u=t(r(d[2]));r(d[3])},901,[3,902,906,908]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})},902,[903]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"ConnectionIndicator",{enumerable:!0,get:function(){return t.default}});var t=n(r(d[1]))},903,[3,904]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),l=t(r(d[2])),o=t(r(d[3])),u=t(r(d[4])),c=t(r(d[5])),f=t(r(d[6])),s=r(d[7]),v=r(d[8]),I=r(d[9]),h=t(r(d[10])),O=r(d[11]),_=[s.IconSignalLevel0,s.IconSignalLevel1,s.IconSignalLevel2],S=(function(t){function s(t){var l;return(0,n.default)(this,s),(l=(0,o.default)(this,(0,u.default)(s).call(this,t))).state={autoHideTimeout:void 0,showIndicator:!1,stats:{}},l}return(0,c.default)(s,t),(0,l.default)(s,[{key:"render",value:function(){var t=this.state,n=t.showIndicator,l=t.stats.percent;if(!n||void 0===l)return null;var o=Math.floor(l/33.4);return f.default.createElement(v.BaseIndicator,{icon:_[o],iconStyle:{color:O.CONNECTOR_INDICATOR_COLORS[o]}})}}]),s})(h.default),C=(0,I.connect)()(S);e.default=C},904,[3,4,5,6,9,10,13,403,394,534,905,907]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.INDICATOR_DISPLAY_THRESHOLD=void 0;var n=t(r(d[1])),o=t(r(d[2])),u=t(r(d[3])),s=t(r(d[4])),p=t(r(d[5])),c=t(r(d[6])),l=t(r(d[7])),f=r(d[8]),h=t(r(d[9]));e.INDICATOR_DISPLAY_THRESHOLD=30;var I=(function(t){function f(t){var n;return(0,o.default)(this,f),(n=(0,s.default)(this,(0,p.default)(f).call(this,t)))._onStatsUpdated=n._onStatsUpdated.bind((0,l.default)((0,l.default)(n))),n}return(0,c.default)(f,t),(0,u.default)(f,[{key:"componentDidMount",value:function(){h.default.subscribeToClientStats(this.props.participantId,this._onStatsUpdated)}},{key:"componentDidUpdate",value:function(t){t.participantId!==this.props.participantId&&(h.default.unsubscribeToClientStats(t.participantId,this._onStatsUpdated),h.default.subscribeToClientStats(this.props.participantId,this._onStatsUpdated))}},{key:"componentWillUnmount",value:function(){h.default.unsubscribeToClientStats(this.props.participantId,this._onStatsUpdated),clearTimeout(this.autoHideTimeout)}},{key:"_onStatsUpdated",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=t.connectionQuality,u=void 0===o?{}:{percent:o},s=(0,n.default)({},this.state.stats,t,u);this.setState({stats:s}),this._updateIndicatorAutoHide(s.percent)}},{key:"_updateIndicatorAutoHide",value:function(t){var n=this;t<30?(clearTimeout(this.autoHideTimeout),this.autoHideTimeout=void 0,this.setState({showIndicator:!0})):this.autoHideTimeout||(this.autoHideTimeout=setTimeout(function(){n.setState({showIndicator:!1})},'undefined'==typeof interfaceConfig?5e3:interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT))}}]),f})(f.Component);e.default=I},905,[3,16,4,5,6,9,10,8,13,906]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),o=t(r(d[2])),s=r(d[3]),u={},f={startListeningForStats:function(t){var n=this;t.on(s.JitsiConnectionQualityEvents.LOCAL_STATS_UPDATED,function(o){return n._onStatsUpdated(t.myUserId(),o)}),t.on(s.JitsiConnectionQualityEvents.REMOTE_STATS_UPDATED,function(t,o){return n._emitStatsUpdate(t,o)}),t.on(s.JitsiE2ePingEvents.E2E_RTT_CHANGED,function(t,o){var s={e2eRtt:o,region:t.getProperty('region')};n._emitStatsUpdate(t.getId(),s)})},subscribeToClientStats:function(t,n){t&&(u[t]||(u[t]=[]),u[t].push(n))},unsubscribeToClientStats:function(t,n){if(u[t]){var o=u[t].filter(function(t){return t!==n});o.length?u[t]=o:delete u[t]}},_emitStatsUpdate:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};(u[t]||[]).forEach(function(t){t(n)})},_onStatsUpdated:function(t,s){var u=this,f=s.framerate||{},c=s.resolution||{},l=(0,n.default)({},s,{framerate:f[t],resolution:c[t]});this._emitStatsUpdate(t,l);var _=Object.keys(f),v=Object.keys(c);o.default.union(_,v).filter(function(n){return n!==t}).forEach(function(t){var n={},o=f[t];o&&(n.framerate=o);var s=c[t];s&&(n.resolution=s),u._emitStatsUpdate(t,n)})}};e.default=f},906,[3,16,536,388]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.CONNECTOR_INDICATOR_COLORS=void 0;var C=r(d[0]),O=[C.ColorPalette.red,C.ColorPalette.Y200,C.ColorPalette.green];e.CONNECTOR_INDICATOR_COLORS=O},907,[406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),s=r(d[2]);t.MiddlewareRegistry.register(function(t){return function(t){return function(c){switch(c.type){case n.CONFERENCE_JOINED:s.statsEmitter.startListeningForStats(c.conference)}return t(c)}}})},908,[534,383,901]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.isTestModeEnabled=function(t){var n=t['features/base/config'].testing;return Boolean(n&&n.testMode)}},909,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=n(r(d[2])),u=n(r(d[3])),l=n(r(d[4])),o=n(r(d[5])),p=n(r(d[6])),f=t(r(d[7])),c=r(d[8]),h=r(d[9]),v=r(d[10]),_=(function(t){function n(){return(0,s.default)(this,n),(0,l.default)(this,(0,o.default)(n).apply(this,arguments))}return(0,p.default)(n,t),(0,u.default)(n,[{key:"render",value:function(){return this.props._testModeEnabled?f.default.createElement(c.Text,{accessibilityLabel:this.props.value,onPress:this.props.onPress,testID:this.props.id}):null}}]),n})(f.Component),b=(0,h.connect)(v._mapStateToProps)(_);e.default=b},910,[2,3,4,5,6,9,10,13,17,534,911]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e._mapStateToProps=function(n){return{_testModeEnabled:(0,t.isTestModeEnabled)(n)}};var t=r(d[0])},911,[909]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=r(d[0]),t={avatarContainer:{alignItems:'center',justifyContent:'center'},connectionInfoText:{color:n.ColorPalette.white,fontSize:12,marginVertical:n.BoxModel.margin,marginHorizontal:n.BoxModel.margin,textAlign:'center'},connectionInfoContainer:{alignSelf:'center',backgroundColor:n.ColorPalette.darkGrey,borderRadius:20,marginTop:n.BoxModel.margin},participantView:{alignItems:'stretch',flex:1,justifyContent:'center'}};e.default=t},912,[406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0])(r(d[1])),n=r(d[2]),c=r(d[3]),s=r(d[4]),o=r(d[5]),I=r(d[6]),f=r(d[7]),P=r(d[8]),A=r(d[9]),p=r(d[10]),u=r(d[11]),T=r(d[12]),_=r(d[13]);function N(t,n,c){var s=t.getState,o=t.dispatch,I=n(c),f=s()['features/base/settings'];return o((0,A.localParticipantJoined)({avatarID:f.avatarID,avatarURL:f.avatarURL,email:f.email,name:f.displayName})),I}function l(t,n,c){var s=t.dispatch,o=n(c);return s((0,A.localParticipantLeft)()),o}function L(t,n){var s=t.getState,o=t.dispatch,I=s(),f=I['features/base/config'].startAudioMuted;if(!n.participant.local&&(!f||(0,T.getParticipantCount)(I)=s.length)break;f=s[I++]}else{if((I=s.next()).done)break;f=I.value}var P=f;!P.local&&(!t||P.conference!==t)&&c((0,A.participantLeft)(P.id,P.conference))}}),f.StateListenerRegistry.register(function(t){return t['features/base/conference']},function(t,n){var c,s=t.leaving,I=n.dispatch,f=(0,n.getState)(),P=(0,T.getLocalParticipant)(f);P&&(c=P.id)!==u.LOCAL_PARTICIPANT_DEFAULT_ID&&((0,o.forEachConference)(f,function(t){return t===s||t.myUserId()!==c})&&I((0,A.localParticipantIdChanged)(u.LOCAL_PARTICIPANT_DEFAULT_ID)))}),f.StateListenerRegistry.register(function(t){return t['features/base/conference'].conference},function(t,n){t?t.on(I.JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,function(c,s,o,I){switch(s){case'features_jigasi':n.dispatch((0,A.participantUpdated)({conference:t,id:c.getId(),isJigasi:I}));break;case'features_screen-sharing':n.dispatch((0,A.participantUpdated)({conference:t,id:c.getId(),features:{'screen-sharing':!0}}));break;case'raisedHand':E(n,t,c.getId(),I)}}):E(n,t,void 0,!1)})},913,[3,875,587,914,844,383,388,534,850,533,740,895,741,918]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){"default"!==n&&"__esModule"!==n&&Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})})},914,[915,917]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"PresenceLabel",{enumerable:!0,get:function(){return t.default}});var t=n(r(d[1]))},915,[3,916]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=n(r(d[2])),l=n(r(d[3])),s=n(r(d[4])),c=n(r(d[5])),f=n(r(d[6])),p=n(r(d[7])),o=t(r(d[8])),v=r(d[9]),_=r(d[10]),h=r(d[11]),y=r(d[12]),P=r(d[13]),T=(function(t){function n(){return(0,l.default)(this,n),(0,c.default)(this,(0,f.default)(n).apply(this,arguments))}return(0,p.default)(n,t),(0,s.default)(n,[{key:"render",value:function(){var t=this._getPresenceText();if(null===t)return null;var n=this.props,l=n.style,s=n.className;return o.default.createElement(h.Text,(0,u.default)({className:s},l),t)}},{key:"_getPresenceText",value:function(){var t=this.props,n=t._presence,u=t.t;if(!n)return null;var l=P.STATUS_TO_I18N_KEY[n];return l?u(l):n}}]),n})(o.Component);T.defaultProps={_presence:''};var x=(0,v.translate)((0,y.connect)(function(t,n){var u=(0,_.getParticipantById)(t,n.participantID);return{_presence:u&&u.presence||n.defaultPresence}})(T));e.default=x},916,[2,3,16,4,5,6,9,10,13,607,532,394,534,917]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.STATUS_TO_I18N_KEY=e.DISCONNECTED=e.CONNECTED_PHONE_NUMBER=e.CONNECTING2=e.CONNECTING=e.INITIALIZING_CALL=e.EXPIRED=e.IGNORED=e.REJECTED=e.BUSY=e.CONNECTED_USER=e.RINGING=e.CALLING=e.INVITED=void 0;var t,c=n(r(d[1]));e.INVITED="Invited";e.CALLING="calling";e.RINGING="ringing";e.CONNECTED_USER="connected";e.BUSY="busy";e.REJECTED="rejected";e.IGNORED="ignored";e.EXPIRED="expired";e.INITIALIZING_CALL="Initializing Call";e.CONNECTING="Connecting";e.CONNECTING2="Connecting*";e.CONNECTED_PHONE_NUMBER="Connected";e.DISCONNECTED="Disconnected";var N=(t={},(0,c.default)(t,"Invited",'presenceStatus.invited'),(0,c.default)(t,"ringing",'presenceStatus.ringing'),(0,c.default)(t,"calling",'presenceStatus.calling'),(0,c.default)(t,"busy",'presenceStatus.busy'),(0,c.default)(t,"rejected",'presenceStatus.rejected'),(0,c.default)(t,"ignored",'presenceStatus.ignored'),(0,c.default)(t,"expired",'presenceStatus.expired'),(0,c.default)(t,"Initializing Call",'presenceStatus.initializingCall'),(0,c.default)(t,"Connecting",'presenceStatus.connecting'),(0,c.default)(t,"Connecting*",'presenceStatus.connecting2'),(0,c.default)(t,"Connected",'presenceStatus.connected'),(0,c.default)(t,"connected",'presenceStatus.connected'),(0,c.default)(t,"Disconnected",'presenceStatus.disconnected'),t);e.STATUS_TO_I18N_KEY=N},917,[3,55]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.PARTICIPANT_LEFT_FILE=e.PARTICIPANT_JOINED_FILE=void 0;e.PARTICIPANT_JOINED_FILE='joined.wav';e.PARTICIPANT_LEFT_FILE='left.wav'},918,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=n(r(d[1])),c=n(r(d[2])),A=r(d[3]),o=r(d[4]),T=r(d[5]),l=['conference','id','local','dominantSpeaker','pinned'];function I(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},c=arguments.length>1?arguments[1]:void 0;switch(c.type){case o.DOMINANT_SPEAKER_CHANGED:return(0,A.set)(n,'dominantSpeaker',n.id===c.participant.id);case o.PARTICIPANT_ID_CHANGED:var I=c.conference;if(n.id===c.oldValue&&n.conference===I&&(I||n.local))return(0,t.default)({},n,{id:c.newValue});break;case o.SET_LOADABLE_AVATAR_URL:case o.PARTICIPANT_UPDATED:var s=c.participant,P=s.id,p=s.local;if(!P&&p&&(P=T.LOCAL_PARTICIPANT_DEFAULT_ID),n.id===P){var _=(0,t.default)({},n);for(var u in s)s.hasOwnProperty(u)&&-1===l.indexOf(u)&&(_[u]=s[u]);return _}break;case o.PIN_PARTICIPANT:return(0,A.set)(n,'pinned',n.id===c.participant.id)}return n}function s(n){var t=n.participant,c=t.avatarID,A=t.avatarURL,o=t.botType,l=t.connectionStatus,I=t.dominantSpeaker,s=t.email,P=t.isFakeParticipant,p=t.isJigasi,_=t.loadableAvatarUrl,u=t.local,N=t.name,f=t.pinned,R=t.presence,D=t.role,v=t.conference,E=t.id;return u&&(v=void 0,E||(E=T.LOCAL_PARTICIPANT_DEFAULT_ID)),{avatarID:c,avatarURL:A,botType:o,conference:v,connectionStatus:l,dominantSpeaker:I||!1,email:s,id:E,isFakeParticipant:P,isJigasi:p,loadableAvatarUrl:_,local:u||!1,name:N,pinned:f||!1,presence:R,role:D||T.PARTICIPANT_ROLE.NONE}}A.ReducerRegistry.register('features/base/participants',function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0;switch(t.type){case o.SET_LOADABLE_AVATAR_URL:case o.DOMINANT_SPEAKER_CHANGED:case o.PARTICIPANT_ID_CHANGED:case o.PARTICIPANT_UPDATED:case o.PIN_PARTICIPANT:return n.map(function(n){return I(n,t)});case o.PARTICIPANT_JOINED:return(0,c.default)(n).concat([s(t)]);case o.PARTICIPANT_LEFT:var A=t.participant,T=A.conference,l=A.id;return n.filter(function(n){return!(n.id===l&&n.conference===T&&(T||n.local))})}return n})},919,[3,54,32,534,740,895]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.getAvatarColor=function(t){var o=0;if(t){for(var l=0,u=t,v=Array.isArray(u),b=0,u=v?u:u["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var s;if(v){if(b>=u.length)break;s=u[b++]}else{if((b=u.next()).done)break;s=b.value}var y=s;l+=y.codePointAt(0)}o=l%n.length}return"rgba("+n[o]+", "+f+")"},e.getInitials=function(t){for(var n=o.default.split(t,'@')[0],f='',l=o.default.words(n),u=Array.isArray(l),v=0,l=u?l:l["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var b;if(u){if(v>=l.length)break;b=l[v++]}else{if((v=l.next()).done)break;b=v.value}var s=b;f.length<2&&(f+=s.substr(0,1).toUpperCase())}return f};var o=t(r(d[1])),n=['232, 105, 156','255, 198, 115','128, 128, 255','105, 232, 194','234, 255, 128'],f=.4},920,[3,536]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.TINTED_VIEW_DEFAULT=e.UNDERLAY_COLOR=e.AVATAR_SIZE=void 0;var o=t(r(d[1])),n=r(d[2]);e.AVATAR_SIZE=65;e.UNDERLAY_COLOR='rgba(255, 255, 255, 0.2)';var l={pageContainer:{flex:1},pageIndicator:{alignItems:'center',flexDirection:'column',justifyContent:'center',padding:n.BoxModel.padding/2},pageIndicatorActive:{color:n.ColorPalette.white},pageIndicatorContainer:{alignItems:'center',backgroundColor:n.ColorPalette.blue,flexDirection:'row',justifyContent:'space-around'},pageIndicatorContent:{alignItems:'center',flexDirection:'column',justifyContent:'center'},pageIndicatorIcon:{color:n.ColorPalette.blueHighlight,fontSize:24},pageIndicatorText:{color:n.ColorPalette.blueHighlight},pagedList:{flex:1},pagedListContainer:{flex:1,flexDirection:'column'},pagedListContainerDisabled:{opacity:.2}},c={avatarContainer:{alignItems:'center',flexDirection:'row',justifyContent:'space-around',padding:5},avatarContent:{backgroundColor:'rgba(0, 0, 0, 0)',color:"rgba(255, 255, 255, 0.6)",fontSize:Math.floor(32.5),fontWeight:'100',textAlign:'center'},container:{flex:1},list:{flex:1,flexDirection:'column'},listItem:{alignItems:'center',flex:1,flexDirection:'row',padding:5},listItemDetails:{flex:1,flexDirection:'column',overflow:'hidden',paddingHorizontal:5},listItemText:{color:"rgba(255, 255, 255, 0.6)",fontSize:14},listItemTitle:{fontWeight:'bold',fontSize:16},listSection:{alignItems:'center',backgroundColor:'rgba(255, 255, 255, 0.2)',flex:1,flexDirection:'row',paddingVertical:5,paddingHorizontal:10},listSectionText:{color:"rgba(255, 255, 255, 0.6)",fontSize:14,fontWeight:'normal'},pullToRefresh:{alignItems:'center',flex:1,flexDirection:'column',justifyContent:'center',padding:20},pullToRefreshIcon:{backgroundColor:'transparent',color:"rgba(255, 255, 255, 0.6)",fontSize:20},pullToRefreshText:{backgroundColor:'transparent',color:"rgba(255, 255, 255, 0.6)"},secondaryActionContainer:{alignItems:'center',backgroundColor:n.ColorPalette.blue,borderRadius:3,height:30,justifyContent:'center',margin:.5*n.BoxModel.margin,marginRight:n.BoxModel.margin,width:30},secondaryActionLabel:{color:n.ColorPalette.white},touchableView:{flexDirection:'row'}},f={backgroundColor:n.ColorPalette.appBackground,opacity:.8};e.TINTED_VIEW_DEFAULT=f;var s=(0,o.default)({},l,c);e.default=s},921,[3,54,406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),l=n(r(d[3])),u=n(r(d[4])),s=n(r(d[5])),c=n(r(d[6])),f=t(r(d[7])),h=r(d[8]),p=r(d[9]),y=r(d[10]),v=r(d[11]),_=(function(t){function n(){return(0,o.default)(this,n),(0,u.default)(this,(0,s.default)(n).apply(this,arguments))}return(0,c.default)(n,t),(0,l.default)(n,[{key:"render",value:function(){return f.default.createElement(h.TouchableOpacity,{accessibilityLabel:'Back',onPress:this.props.onPress},f.default.createElement(y.Icon,{src:y.IconArrowBack,style:[this.props._headerStyles.headerButtonIcon,this.props.style]}))}}]),n})(f.Component);var b=(0,v.connect)(function(t){return{_headerStyles:p.ColorSchemeRegistry.get(t,'Header')}})(_);e.default=b},922,[2,3,4,5,6,9,10,13,17,923,403,534]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var o={ColorSchemeRegistry:!0};Object.defineProperty(e,"ColorSchemeRegistry",{enumerable:!0,get:function(){return l.default}});var n=r(d[1]);Object.keys(n).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(o,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}}))});var c=r(d[2]);Object.keys(c).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(o,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return c[t]}}))});var u=r(d[3]);Object.keys(u).forEach(function(t){"default"!==t&&"__esModule"!==t&&(Object.prototype.hasOwnProperty.call(o,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return u[t]}}))});var l=t(r(d[4]));r(d[5])},923,[3,924,925,926,927,929]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.setColorScheme=function(t){return{type:o.SET_COLOR_SCHEME,colorScheme:t}};var o=r(d[0])},924,[925]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.SET_COLOR_SCHEME=void 0;e.SET_COLOR_SCHEME='SET_COLOR_SCHEME'},925,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.schemeColor=function(n){return function(){return n}}},926,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=t(r(d[1])),s=t(r(d[2])),o=t(r(d[3])),n=t(r(d[4])),u=r(d[5]),f=(r(d[6]),t(r(d[7]))),y=new((function(){function t(){(0,o.default)(this,t),this._schemedStyles=new Map,this._styleTemplates=new Map}return(0,n.default)(t,[{key:"clear",value:function(){this._schemedStyles.clear()}},{key:"get",value:function(t,l){var s=this._schemedStyles.get(l);return s||(s=this._applyColorScheme(t,l,this._styleTemplates.get(l)),this._schemedStyles.set(l,s)),s}},{key:"register",value:function(t,l){this._styleTemplates.set(t,l),this._schemedStyles.delete(t)}},{key:"_applyColorScheme",value:function(t,o,n){var u;if(Array.isArray(n)){u=[];var f=n,y=Array.isArray(f),c=0;for(f=y?f:f["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var h;if(y){if(c>=f.length)break;h=f[c++]}else{if((c=f.next()).done)break;h=c.value}var v=h;u.push(this._applyColorScheme(t,o,v))}}else{u=(0,s.default)({},n);for(var p=Object.entries(u),_=0;_0&&void 0!==arguments[0]?arguments[0]:{},o=arguments.length>1?arguments[1]:void 0;switch(o.type){case n.SET_COLOR_SCHEME:return t.default.cloneDeep(o.colorScheme)||c}return c})},929,[3,536,534,925]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),l=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=l(r(d[2])),u=l(r(d[3])),f=l(r(d[4])),o=l(r(d[5])),c=l(r(d[6])),h=t(r(d[7])),s=r(d[8]),p=r(d[9]),v=l(r(d[10])),y=(function(t){function l(){return(0,n.default)(this,l),(0,f.default)(this,(0,o.default)(l).apply(this,arguments))}return(0,c.default)(l,t),(0,u.default)(l,[{key:"render",value:function(){var t=this.props,l=t.highlight,n=t.icon,u=t.iconStyle;return h.default.createElement(s.View,{style:l?v.default.highlightedIndicator:null},h.default.createElement(p.Icon,{src:n,style:[v.default.indicator,u]}))}}]),l})(h.Component);e.default=y},930,[2,3,4,5,6,9,10,13,17,403,931]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=r(d[0]),t={highlightedIndicator:{backgroundColor:o.ColorPalette.blue,borderRadius:16,padding:4},indicator:{backgroundColor:o.ColorPalette.transparent,color:o.ColorPalette.white,fontSize:12,textShadowColor:o.ColorPalette.black,textShadowOffset:{height:-1,width:0}}};e.default=t},931,[406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=n(r(d[2])),l=n(r(d[3])),o=n(r(d[4])),f=n(r(d[5])),s=n(r(d[6])),p=t(r(d[7])),c=r(d[8]),h=(function(t){function n(){return(0,u.default)(this,n),(0,o.default)(this,(0,f.default)(n).apply(this,arguments))}return(0,s.default)(n,t),(0,l.default)(n,[{key:"render",value:function(){return p.default.createElement(c.TouchableOpacity,{onPress:this.props.onValueChange},p.default.createElement(c.Text,{style:this.props.style},this.props.children))}}]),n})(p.Component);e.default=h},932,[2,3,4,5,6,9,10,13,17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),s=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=s(r(d[2])),n=s(r(d[3])),o=s(r(d[4])),u=s(r(d[5])),p=s(r(d[6])),c=t(r(d[7])),f=r(d[8]),h=r(d[9]),y=r(d[10]),b=r(d[11]),v=(function(t){function s(){return(0,l.default)(this,s),(0,o.default)(this,(0,u.default)(s).apply(this,arguments))}return(0,p.default)(s,t),(0,n.default)(s,[{key:"render",value:function(){var t=this.props._headerStyles;return c.default.createElement(f.TouchableOpacity,{accessibilityLabel:'Forward',disabled:this.props.disabled,onPress:this.props.onPress},c.default.createElement(f.Text,{style:[t.headerButtonText,this.props.disabled&&t.disabledButtonText,this.props.style]},this.props.t(this.props.labelKey)))}}]),s})(c.Component);var _=(0,y.translate)((0,b.connect)(function(t){return{_headerStyles:h.ColorSchemeRegistry.get(t,'Header')}})(v));e.default=_},933,[2,3,4,5,6,9,10,13,17,923,607,534]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=n(r(d[2])),o=n(r(d[3])),u=n(r(d[4])),s=n(r(d[5])),c=n(r(d[6])),f=n(r(d[7])),p=t(r(d[8])),C=r(d[9]),h=r(d[10]),_=r(d[11]),v=r(d[12]),y=r(d[13]),S=(function(t){function n(t){var o;return(0,l.default)(this,n),(o=(0,u.default)(this,(0,s.default)(n).call(this,t)))._getIOS10CompatiblePadding=o._getIOS10CompatiblePadding.bind((0,f.default)((0,f.default)(o))),o}return(0,c.default)(n,t),(0,o.default)(n,[{key:"render",value:function(){var t=this.props._styles;return p.default.createElement(C.View,{style:[t.headerOverlay,this._getIOS10CompatiblePadding()]},p.default.createElement(C.StatusBar,{backgroundColor:t.statusBar,barStyle:this._getStatusBarContentColor(),translucent:!1}),p.default.createElement(C.SafeAreaView,null,p.default.createElement(C.View,{style:[t.screenHeader,this.props.style]},this.props.children)))}},{key:"_getIOS10CompatiblePadding",value:function(){if('ios'===C.Platform.OS&&parseInt(C.Platform.Version,10)<=10)return{paddingTop:y.HEADER_PADDING+20};return null}},{key:"_getStatusBarContentColor",value:function(){var t=this.props._styles,n=t.statusBarContent;if(n)return(0,v.isDarkColor)(n)?"dark-content":"light-content";var l=t.statusBar,o=t.screenHeader;return(0,v.isDarkColor)(l||o.backgroundColor)?"light-content":"dark-content"}}]),n})(p.Component);var b=(0,_.connect)(function(t){return{_styles:h.ColorSchemeRegistry.get(t,'Header')}})(S);e.default=b},934,[2,3,4,5,6,9,10,8,13,17,923,534,406,935]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.HEADER_PADDING=void 0;var o=t(r(d[1])),n=r(d[2]),l=r(d[3]),c=r(d[4]),s=c.BoxModel.padding/2;e.HEADER_PADDING=s,l.ColorSchemeRegistry.register('Header',{disabledButtonText:{opacity:.6},headerButtonIcon:{alignSelf:'center',color:(0,l.schemeColor)('icon'),fontSize:22,marginRight:12,padding:8},headerButtonText:{color:(0,l.schemeColor)('text'),fontSize:18},headerOverlay:{backgroundColor:(0,l.schemeColor)('background')},headerText:{color:(0,l.schemeColor)('text'),fontSize:18},headerTextWrapper:{alignItems:'center',justifyContent:'center',left:0,position:'absolute',right:0},page:(0,o.default)({},n.StyleSheet.absoluteFillObject,{alignItems:'stretch',flex:1,flexDirection:'column',overflow:'hidden'}),screenHeader:{alignItems:'center',backgroundColor:(0,l.schemeColor)('background'),flexDirection:'row',height:48,justifyContent:'space-between',paddingHorizontal:c.BoxModel.padding,paddingVertical:s},statusBar:(0,l.schemeColor)('statusBar'),statusBarContent:(0,l.schemeColor)('statusBarContent')})},935,[3,54,17,923,406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=n(r(d[2])),u=n(r(d[3])),o=n(r(d[4])),s=n(r(d[5])),f=n(r(d[6])),p=t(r(d[7])),c=r(d[8]),h=r(d[9]),y=r(d[10]),v=r(d[11]),_=(function(t){function n(){return(0,l.default)(this,n),(0,o.default)(this,(0,s.default)(n).apply(this,arguments))}return(0,f.default)(n,t),(0,u.default)(n,[{key:"render",value:function(){var t=this.props._headerStyles;return p.default.createElement(c.View,{pointerEvents:"box-none",style:t.headerTextWrapper},p.default.createElement(c.Text,{style:[t.headerText]},this.props.t(this.props.labelKey)))}}]),n})(p.Component);var x=(0,y.translate)((0,v.connect)(function(t){return{_headerStyles:h.ColorSchemeRegistry.get(t,'Header')}})(_));e.default=x},936,[2,3,4,5,6,9,10,13,17,923,607,534]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),l=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=l(r(d[2])),n=l(r(d[3])),s=l(r(d[4])),f=l(r(d[5])),o=l(r(d[6])),p=t(r(d[7])),c=r(d[8]),h=l(r(d[9])),b=l(r(d[10])),y=l(r(d[11])),v=l(r(d[12])),P=(function(t){function l(){return(0,u.default)(this,l),(0,s.default)(this,(0,f.default)(l).apply(this,arguments))}return(0,o.default)(l,t),(0,n.default)(l,[{key:"render",value:function(){var t=this.props,l=t.onPressBack,u=t.onPressForward;return p.default.createElement(y.default,null,l&&p.default.createElement(h.default,{onPress:l}),p.default.createElement(v.default,{labelKey:this.props.headerLabelKey}),u&&p.default.createElement(b.default,{disabled:this.props.forwardDisabled,labelKey:this.props.forwardLabelKey,onPress:u}))}}]),l})(p.Component),E=(0,c.translate)(P);e.default=E},937,[2,3,4,5,6,9,10,13,607,922,933,934,936]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),u=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=u(r(d[2])),l=u(r(d[3])),f=u(r(d[4])),o=u(r(d[5])),s=u(r(d[6])),c=t(r(d[7])),p=r(d[8]),h=(function(t){function u(){return(0,n.default)(this,u),(0,f.default)(this,(0,o.default)(u).apply(this,arguments))}return(0,s.default)(u,t),(0,l.default)(u,[{key:"render",value:function(){return c.default.createElement(p.Image,{source:this.props.src,style:this.props.style})}}]),u})(c.Component);e.default=h},938,[2,3,4,5,6,9,10,13,17]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[2])),s=t(r(d[3])),o=t(r(d[4])),l=t(r(d[5])),f=t(r(d[6])),c=t(r(d[7])),p=n(r(d[8])),h=r(d[9]),v=t(r(d[10])),_=(function(n){function t(n){var s;return(0,u.default)(this,t),(s=(0,o.default)(this,(0,l.default)(t).call(this,n)))._onPress=s._onPress.bind((0,c.default)((0,c.default)(s))),s}return(0,f.default)(t,n),(0,s.default)(t,[{key:"render",value:function(){return p.default.createElement(v.default,{onPress:this._onPress,style:this.props.style},this.props.children)}},{key:"_onLinkingOpenURLRejected",value:function(n){var t=this.props.onLinkingOpenURLRejected;t&&t(n)}},{key:"_onPress",value:function(){var n=this;h.Linking.openURL(this.props.url).catch(function(t){return n._onLinkingOpenURLRejected(t)})}}]),t})(p.Component);e.default=_},939,[2,3,4,5,6,9,10,8,13,17,940]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.Text}});var t=r(d[0])},940,[17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),l=n(r(d[3])),u=n(r(d[4])),c=n(r(d[5])),f=n(r(d[6])),p=n(r(d[7])),s=t(r(d[8])),_=r(d[9]),h=n(r(d[10])),v=n(r(d[11])),y=(function(t){function n(t){var l;return(0,o.default)(this,n),(l=(0,u.default)(this,(0,c.default)(n).call(this,t)))._componentDecorator=l._componentDecorator.bind((0,p.default)((0,p.default)(l))),l}return(0,f.default)(n,t),(0,l.default)(n,[{key:"render",value:function(){return s.default.createElement(h.default,{componentDecorator:this._componentDecorator},s.default.createElement(_.Text,null,this.props.children))}},{key:"_componentDecorator",value:function(t,n,o){return s.default.createElement(v.default,{key:o,style:this.props.linkStyle,url:t},n)}}]),n})(s.Component);e.default=y},941,[2,3,4,5,6,9,10,8,13,17,942,939]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var t,u=r(d[0]),l=(t=u)&&t.__esModule?t:{default:t};e.default=l.default},942,[943]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var t=(function(){function t(t,n){for(var o=0;oc&&u.push(t.substring(c,o.index));var s=n.props.hrefDecorator(o.url),f=n.props.textDecorator(o.text),l=n.props.componentDecorator(s,f,p);u.push(l),c=o.lastIndex}),t.length>c&&u.push(t.substring(c)),1===u.length?u[0]:u}},{key:'parse',value:function(t){var o=this,u=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return'string'==typeof t?this.parseString(t):n.isValidElement(t)&&'a'!==t.type&&'button'!==t.type?n.cloneElement(t,{key:u},this.parse(t.props.children)):Array.isArray(t)?t.map(function(t,n){return o.parse(t,n)}):t}},{key:'render',value:function(){return n.createElement(n.Fragment,null,this.parse(this.props.children))}}]),u})();y.defaultProps={componentDecorator:o.default,hrefDecorator:u.default,matchDecorator:c.default,textDecorator:p.default},e.default=y},943,[13,944,945,946,954]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var t=(function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var u in t)Object.prototype.hasOwnProperty.call(t,u)&&(n[u]=t[u]);return n.default=t,n})(r(d[0]));e.default=function(n,u,f){return t.createElement('a',{href:n,key:f},u)}},944,[13]); -__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return t}},945,[]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var t=n(r(d[0])),u=n(r(d[1]));function n(t){return t&&t.__esModule?t:{default:t}}var f=new t.default;f.tlds(u.default),e.default=function(t){return f.match(t)}},946,[947,953]); -__d(function(g,r,i,a,m,e,d){'use strict';function t(t){return Array.prototype.slice.call(arguments,1).forEach(function(_){_&&Object.keys(_).forEach(function(s){t[s]=_[s]})}),t}function _(t){return Object.prototype.toString.call(t)}function s(t){return'[object String]'===_(t)}function n(t){return'[object RegExp]'===_(t)}function o(t){return'[object Function]'===_(t)}function h(t){return t.replace(/[.?*+^$[\]\\(){}|-]/g,'\\$&')}var c={fuzzyLink:!0,fuzzyEmail:!0,fuzzyIP:!1};var l={'http:':{validate:function(t,_,s){var n=t.slice(_);return s.re.http||(s.re.http=new RegExp('^\\/\\/'+s.re.src_auth+s.re.src_host_port_strict+s.re.src_path,'i')),s.re.http.test(n)?n.match(s.re.http)[0].length:0}},'https:':'http:','ftp:':'http:','//':{validate:function(t,_,s){var n=t.slice(_);return s.re.no_http||(s.re.no_http=new RegExp('^'+s.re.src_auth+'(?:localhost|(?:(?:'+s.re.src_domain+')\\.)+'+s.re.src_domain_root+')'+s.re.src_port+s.re.src_host_terminator+s.re.src_path,'i')),s.re.no_http.test(n)?_>=3&&':'===t[_-3]?0:_>=3&&'/'===t[_-3]?0:n.match(s.re.no_http)[0].length:0}},'mailto:':{validate:function(t,_,s){var n=t.slice(_);return s.re.mailto||(s.re.mailto=new RegExp('^'+s.re.src_email_name+'@'+s.re.src_host_strict,'i')),s.re.mailto.test(n)?n.match(s.re.mailto)[0].length:0}}},u='a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]',p='biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|\u0440\u0444'.split('|');function f(t){t.__index__=-1,t.__text_cache__=''}function x(t){var c=t.re=r(d[0])(t.__opts__),l=t.__tlds__.slice();function p(t){return t.replace('%TLDS%',c.src_tlds)}t.onCompile(),t.__tlds_replaced__||l.push(u),l.push(c.src_xn),c.src_tlds=l.join('|'),c.email_fuzzy=RegExp(p(c.tpl_email_fuzzy),'i'),c.link_fuzzy=RegExp(p(c.tpl_link_fuzzy),'i'),c.link_no_ip_fuzzy=RegExp(p(c.tpl_link_no_ip_fuzzy),'i'),c.host_fuzzy_test=RegExp(p(c.tpl_host_fuzzy_test),'i');var x=[];function z(t,_){throw new Error('(LinkifyIt) Invalid schema "'+t+'": '+_)}t.__compiled__={},Object.keys(t.__schemas__).forEach(function(h){var c=t.__schemas__[h];if(null!==c){var l,u={validate:null,link:null};if(t.__compiled__[h]=u,'[object Object]'===_(c))return n(c.validate)?u.validate=(l=c.validate,function(t,_){var s=t.slice(_);return l.test(s)?s.match(l)[0].length:0}):o(c.validate)?u.validate=c.validate:z(h,c),void(o(c.normalize)?u.normalize=c.normalize:c.normalize?z(h,c):u.normalize=function(t,_){_.normalize(t)});s(c)?x.push(h):z(h,c)}}),x.forEach(function(_){t.__compiled__[t.__schemas__[_]]&&(t.__compiled__[_].validate=t.__compiled__[t.__schemas__[_]].validate,t.__compiled__[_].normalize=t.__compiled__[t.__schemas__[_]].normalize)}),t.__compiled__['']={validate:null,normalize:function(t,_){_.normalize(t)}};var y=Object.keys(t.__compiled__).filter(function(_){return _.length>0&&t.__compiled__[_]}).map(h).join('|');t.re.schema_test=RegExp("(^|(?!_)(?:[><\uff5c]|"+c.src_ZPCc+'))('+y+')','i'),t.re.schema_search=RegExp("(^|(?!_)(?:[><\uff5c]|"+c.src_ZPCc+'))('+y+')','ig'),t.re.pretest=RegExp('('+t.re.schema_test.source+')|('+t.re.host_fuzzy_test.source+')|@','i'),f(t)}function z(t,_){var s=t.__index__,n=t.__last_index__,o=t.__text_cache__.slice(s,n);this.schema=t.__schema__.toLowerCase(),this.index=s+_,this.lastIndex=n+_,this.raw=o,this.text=o,this.url=o}function y(t,_){var s=new z(t,_);return t.__compiled__[s.schema].normalize(s,t),s}function v(_,s){if(!(this instanceof v))return new v(_,s);var n;s||(n=_,Object.keys(n||{}).reduce(function(t,_){return t||c.hasOwnProperty(_)},!1)&&(s=_,_={})),this.__opts__=t({},c,s),this.__index__=-1,this.__last_index__=-1,this.__schema__='',this.__text_cache__='',this.__schemas__=t({},l,_),this.__compiled__={},this.__tlds__=p,this.__tlds_replaced__=!1,this.re={},x(this)}v.prototype.add=function(t,_){return this.__schemas__[t]=_,x(this),this},v.prototype.set=function(_){return this.__opts__=t(this.__opts__,_),this},v.prototype.test=function(t){if(this.__text_cache__=t,this.__index__=-1,!t.length)return!1;var _,s,n,o,h,c,l,u;if(this.re.schema_test.test(t))for((l=this.re.schema_search).lastIndex=0;null!==(_=l.exec(t));)if(o=this.testSchemaAt(t,_[2],l.lastIndex)){this.__schema__=_[2],this.__index__=_.index+_[1].length,this.__last_index__=_.index+_[0].length+o;break}return this.__opts__.fuzzyLink&&this.__compiled__['http:']&&(u=t.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||u=0&&null!==(n=t.match(this.re.email_fuzzy))&&(h=n.index+n[1].length,c=n.index+n[0].length,(this.__index__<0||hthis.__last_index__)&&(this.__schema__='mailto:',this.__index__=h,this.__last_index__=c)),this.__index__>=0},v.prototype.pretest=function(t){return this.re.pretest.test(t)},v.prototype.testSchemaAt=function(t,_,s){return this.__compiled__[_.toLowerCase()]?this.__compiled__[_.toLowerCase()].validate(t,s,this):0},v.prototype.match=function(t){var _=0,s=[];this.__index__>=0&&this.__text_cache__===t&&(s.push(y(this,_)),_=this.__last_index__);for(var n=_?t.slice(_):t;this.test(n);)s.push(y(this,_)),n=n.slice(this.__last_index__),_+=this.__last_index__;return s.length?s:null},v.prototype.tlds=function(t,_){return t=Array.isArray(t)?t:[t],_?(this.__tlds__=this.__tlds__.concat(t).sort().filter(function(t,_,s){return t!==s[_-1]}).reverse(),x(this),this):(this.__tlds__=t.slice(),this.__tlds_replaced__=!0,x(this),this)},v.prototype.normalize=function(t){t.schema||(t.url='http://'+t.url),'mailto:'!==t.schema||/^mailto:/i.test(t.url)||(t.url='mailto:'+t.url)},v.prototype.onCompile=function(){},m.exports=v},947,[948]); -__d(function(g,r,i,a,m,e,d){'use strict';m.exports=function(_){var s={};s.src_Any=r(d[0]).source,s.src_Cc=r(d[1]).source,s.src_Z=r(d[2]).source,s.src_P=r(d[3]).source,s.src_ZPCc=[s.src_Z,s.src_P,s.src_Cc].join('|'),s.src_ZCc=[s.src_Z,s.src_Cc].join('|');return s.src_pseudo_letter="(?:(?![><\uff5c]|"+s.src_ZPCc+')'+s.src_Any+')',s.src_ip4='(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',s.src_auth='(?:(?:(?!'+s.src_ZCc+'|[@/\\[\\]()]).)+@)?',s.src_port='(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?',s.src_host_terminator="(?=$|[><\uff5c]|"+s.src_ZPCc+')(?!-|_|:\\d|\\.-|\\.(?!$|'+s.src_ZPCc+'))',s.src_path="(?:[/?#](?:(?!"+s.src_ZCc+"|[><\uff5c]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+s.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+s.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+s.src_ZCc+"|[}]).)*\\}|\\\"(?:(?!"+s.src_ZCc+"|[\"]).)+\\\"|\\'(?:(?!"+s.src_ZCc+"|[']).)+\\'|\\'(?="+s.src_pseudo_letter+"|[-]).|\\.{2,4}[a-zA-Z0-9%/]|\\.(?!"+s.src_ZCc+'|[.]).|'+(_&&_['---']?'\\-(?!--(?:[^-]|$))(?:-*)|':'\\-+|')+'\\,(?!'+s.src_ZCc+").|\\!(?!"+s.src_ZCc+"|[!]).|\\?(?!"+s.src_ZCc+"|[?]).)+|\\/)?",s.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',s.src_xn='xn--[a-z0-9\\-]{1,59}',s.src_domain_root='(?:'+s.src_xn+'|'+s.src_pseudo_letter+"{1,63})",s.src_domain='(?:'+s.src_xn+"|(?:"+s.src_pseudo_letter+")|(?:"+s.src_pseudo_letter+'(?:-|'+s.src_pseudo_letter+'){0,61}'+s.src_pseudo_letter+"))",s.src_host="(?:(?:(?:(?:"+s.src_domain+')\\.)*'+s.src_domain+"))",s.tpl_host_fuzzy='(?:'+s.src_ip4+"|(?:(?:(?:"+s.src_domain+")\\.)+(?:%TLDS%)))",s.tpl_host_no_ip_fuzzy='(?:(?:(?:'+s.src_domain+')\\.)+(?:%TLDS%))',s.src_host_strict=s.src_host+s.src_host_terminator,s.tpl_host_fuzzy_strict=s.tpl_host_fuzzy+s.src_host_terminator,s.src_host_port_strict=s.src_host+s.src_port+s.src_host_terminator,s.tpl_host_port_fuzzy_strict=s.tpl_host_fuzzy+s.src_port+s.src_host_terminator,s.tpl_host_port_no_ip_fuzzy_strict=s.tpl_host_no_ip_fuzzy+s.src_port+s.src_host_terminator,s.tpl_host_fuzzy_test='localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:'+s.src_ZPCc+'|>|$))',s.tpl_email_fuzzy="(^|[><\uff5c]|\"|\\(|"+s.src_ZCc+")("+s.src_email_name+'@'+s.tpl_host_fuzzy_strict+')',s.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+s.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+s.tpl_host_port_fuzzy_strict+s.src_path+')',s.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+s.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+s.tpl_host_port_no_ip_fuzzy_strict+s.src_path+')',s}},948,[949,950,951,952]); -__d(function(g,r,i,a,m,e,d){m.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},949,[]); -__d(function(g,r,i,a,m,e,d){m.exports=/[\0-\x1F\x7F-\x9F]/},950,[]); -__d(function(g,r,i,a,m,e,d){m.exports=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/},951,[]); -__d(function(g,r,i,a,m,e,d){m.exports=/[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4E\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDF55-\uDF59]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDC3B\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},952,[]); -__d(function(g,r,i,a,m,e,d){m.exports=["aaa","aarp","abarth","abb","abbott","abbvie","abc","able","abogado","abudhabi","ac","academy","accenture","accountant","accountants","aco","active","actor","ad","adac","ads","adult","ae","aeg","aero","aetna","af","afamilycompany","afl","africa","ag","agakhan","agency","ai","aig","aigo","airbus","airforce","airtel","akdn","al","alfaromeo","alibaba","alipay","allfinanz","allstate","ally","alsace","alstom","am","americanexpress","americanfamily","amex","amfam","amica","amsterdam","analytics","android","anquan","anz","ao","aol","apartments","app","apple","aq","aquarelle","ar","arab","aramco","archi","army","arpa","art","arte","as","asda","asia","associates","at","athleta","attorney","au","auction","audi","audible","audio","auspost","author","auto","autos","avianca","aw","aws","ax","axa","az","azure","ba","baby","baidu","banamex","bananarepublic","band","bank","bar","barcelona","barclaycard","barclays","barefoot","bargains","baseball","basketball","bauhaus","bayern","bb","bbc","bbt","bbva","bcg","bcn","bd","be","beats","beauty","beer","bentley","berlin","best","bestbuy","bet","bf","bg","bh","bharti","bi","bible","bid","bike","bing","bingo","bio","biz","bj","black","blackfriday","blanco","blockbuster","blog","bloomberg","blue","bm","bms","bmw","bn","bnl","bnpparibas","bo","boats","boehringer","bofa","bom","bond","boo","book","booking","bosch","bostik","boston","bot","boutique","box","br","bradesco","bridgestone","broadway","broker","brother","brussels","bs","bt","budapest","bugatti","build","builders","business","buy","buzz","bv","bw","by","bz","bzh","ca","cab","cafe","cal","call","calvinklein","cam","camera","camp","cancerresearch","canon","capetown","capital","capitalone","car","caravan","cards","care","career","careers","cars","cartier","casa","case","caseih","cash","casino","cat","catering","catholic","cba","cbn","cbre","cbs","cc","cd","ceb","center","ceo","cern","cf","cfa","cfd","cg","ch","chanel","channel","chase","chat","cheap","chintai","christmas","chrome","chrysler","church","ci","cipriani","circle","cisco","citadel","citi","citic","city","cityeats","ck","cl","claims","cleaning","click","clinic","clinique","clothing","cloud","club","clubmed","cm","cn","co","coach","codes","coffee","college","cologne","com","comcast","commbank","community","company","compare","computer","comsec","condos","construction","consulting","contact","contractors","cooking","cookingchannel","cool","coop","corsica","country","coupon","coupons","courses","cr","credit","creditcard","creditunion","cricket","crown","crs","cruise","cruises","csc","cu","cuisinella","cv","cw","cx","cy","cymru","cyou","cz","dabur","dad","dance","data","date","dating","datsun","day","dclk","dds","de","deal","dealer","deals","degree","delivery","dell","deloitte","delta","democrat","dental","dentist","desi","design","dev","dhl","diamonds","diet","digital","direct","directory","discount","discover","dish","diy","dj","dk","dm","dnp","do","docs","doctor","dodge","dog","doha","domains","dot","download","drive","dtv","dubai","duck","dunlop","duns","dupont","durban","dvag","dvr","dz","earth","eat","ec","eco","edeka","edu","education","ee","eg","email","emerck","energy","engineer","engineering","enterprises","epost","epson","equipment","er","ericsson","erni","es","esq","estate","esurance","et","etisalat","eu","eurovision","eus","events","everbank","exchange","expert","exposed","express","extraspace","fage","fail","fairwinds","faith","family","fan","fans","farm","farmers","fashion","fast","fedex","feedback","ferrari","ferrero","fi","fiat","fidelity","fido","film","final","finance","financial","fire","firestone","firmdale","fish","fishing","fit","fitness","fj","fk","flickr","flights","flir","florist","flowers","fly","fm","fo","foo","food","foodnetwork","football","ford","forex","forsale","forum","foundation","fox","fr","free","fresenius","frl","frogans","frontdoor","frontier","ftr","fujitsu","fujixerox","fun","fund","furniture","futbol","fyi","ga","gal","gallery","gallo","gallup","game","games","gap","garden","gb","gbiz","gd","gdn","ge","gea","gent","genting","george","gf","gg","ggee","gh","gi","gift","gifts","gives","giving","gl","glade","glass","gle","global","globo","gm","gmail","gmbh","gmo","gmx","gn","godaddy","gold","goldpoint","golf","goo","goodhands","goodyear","goog","google","gop","got","gov","gp","gq","gr","grainger","graphics","gratis","green","gripe","grocery","group","gs","gt","gu","guardian","gucci","guge","guide","guitars","guru","gw","gy","hair","hamburg","hangout","haus","hbo","hdfc","hdfcbank","health","healthcare","help","helsinki","here","hermes","hgtv","hiphop","hisamitsu","hitachi","hiv","hk","hkt","hm","hn","hockey","holdings","holiday","homedepot","homegoods","homes","homesense","honda","honeywell","horse","hospital","host","hosting","hot","hoteles","hotels","hotmail","house","how","hr","hsbc","ht","hu","hughes","hyatt","hyundai","ibm","icbc","ice","icu","id","ie","ieee","ifm","ikano","il","im","imamat","imdb","immo","immobilien","in","industries","infiniti","info","ing","ink","institute","insurance","insure","int","intel","international","intuit","investments","io","ipiranga","iq","ir","irish","is","iselect","ismaili","ist","istanbul","it","itau","itv","iveco","iwc","jaguar","java","jcb","jcp","je","jeep","jetzt","jewelry","jio","jlc","jll","jm","jmp","jnj","jo","jobs","joburg","jot","joy","jp","jpmorgan","jprs","juegos","juniper","kaufen","kddi","ke","kerryhotels","kerrylogistics","kerryproperties","kfh","kg","kh","ki","kia","kim","kinder","kindle","kitchen","kiwi","km","kn","koeln","komatsu","kosher","kp","kpmg","kpn","kr","krd","kred","kuokgroup","kw","ky","kyoto","kz","la","lacaixa","ladbrokes","lamborghini","lamer","lancaster","lancia","lancome","land","landrover","lanxess","lasalle","lat","latino","latrobe","law","lawyer","lb","lc","lds","lease","leclerc","lefrak","legal","lego","lexus","lgbt","li","liaison","lidl","life","lifeinsurance","lifestyle","lighting","like","lilly","limited","limo","lincoln","linde","link","lipsy","live","living","lixil","lk","llc","loan","loans","locker","locus","loft","lol","london","lotte","lotto","love","lpl","lplfinancial","lr","ls","lt","ltd","ltda","lu","lundbeck","lupin","luxe","luxury","lv","ly","ma","macys","madrid","maif","maison","makeup","man","management","mango","map","market","marketing","markets","marriott","marshalls","maserati","mattel","mba","mc","mckinsey","md","me","med","media","meet","melbourne","meme","memorial","men","menu","meo","merckmsd","metlife","mg","mh","miami","microsoft","mil","mini","mint","mit","mitsubishi","mk","ml","mlb","mls","mm","mma","mn","mo","mobi","mobile","mobily","moda","moe","moi","mom","monash","money","monster","mopar","mormon","mortgage","moscow","moto","motorcycles","mov","movie","movistar","mp","mq","mr","ms","msd","mt","mtn","mtr","mu","museum","mutual","mv","mw","mx","my","mz","na","nab","nadex","nagoya","name","nationwide","natura","navy","nba","nc","ne","nec","net","netbank","netflix","network","neustar","new","newholland","news","next","nextdirect","nexus","nf","nfl","ng","ngo","nhk","ni","nico","nike","nikon","ninja","nissan","nissay","nl","no","nokia","northwesternmutual","norton","now","nowruz","nowtv","np","nr","nra","nrw","ntt","nu","nyc","nz","obi","observer","off","office","okinawa","olayan","olayangroup","oldnavy","ollo","om","omega","one","ong","onl","online","onyourside","ooo","open","oracle","orange","org","organic","origins","osaka","otsuka","ott","ovh","pa","page","panasonic","panerai","paris","pars","partners","parts","party","passagens","pay","pccw","pe","pet","pf","pfizer","pg","ph","pharmacy","phd","philips","phone","photo","photography","photos","physio","piaget","pics","pictet","pictures","pid","pin","ping","pink","pioneer","pizza","pk","pl","place","play","playstation","plumbing","plus","pm","pn","pnc","pohl","poker","politie","porn","post","pr","pramerica","praxi","press","prime","pro","prod","productions","prof","progressive","promo","properties","property","protection","pru","prudential","ps","pt","pub","pw","pwc","py","qa","qpon","quebec","quest","qvc","racing","radio","raid","re","read","realestate","realtor","realty","recipes","red","redstone","redumbrella","rehab","reise","reisen","reit","reliance","ren","rent","rentals","repair","report","republican","rest","restaurant","review","reviews","rexroth","rich","richardli","ricoh","rightathome","ril","rio","rip","rmit","ro","rocher","rocks","rodeo","rogers","room","rs","rsvp","ru","rugby","ruhr","run","rw","rwe","ryukyu","sa","saarland","safe","safety","sakura","sale","salon","samsclub","samsung","sandvik","sandvikcoromant","sanofi","sap","sapo","sarl","sas","save","saxo","sb","sbi","sbs","sc","sca","scb","schaeffler","schmidt","scholarships","school","schule","schwarz","science","scjohnson","scor","scot","sd","se","search","seat","secure","security","seek","select","sener","services","ses","seven","sew","sex","sexy","sfr","sg","sh","shangrila","sharp","shaw","shell","shia","shiksha","shoes","shop","shopping","shouji","show","showtime","shriram","si","silk","sina","singles","site","sj","sk","ski","skin","sky","skype","sl","sling","sm","smart","smile","sn","sncf","so","soccer","social","softbank","software","sohu","solar","solutions","song","sony","soy","space","spiegel","sport","spot","spreadbetting","sr","srl","srt","st","stada","staples","star","starhub","statebank","statefarm","statoil","stc","stcgroup","stockholm","storage","store","stream","studio","study","style","su","sucks","supplies","supply","support","surf","surgery","suzuki","sv","swatch","swiftcover","swiss","sx","sy","sydney","symantec","systems","sz","tab","taipei","talk","taobao","target","tatamotors","tatar","tattoo","tax","taxi","tc","tci","td","tdk","team","tech","technology","tel","telecity","telefonica","temasek","tennis","teva","tf","tg","th","thd","theater","theatre","tiaa","tickets","tienda","tiffany","tips","tires","tirol","tj","tjmaxx","tjx","tk","tkmaxx","tl","tm","tmall","tn","to","today","tokyo","tools","top","toray","toshiba","total","tours","town","toyota","toys","tr","trade","trading","training","travel","travelchannel","travelers","travelersinsurance","trust","trv","tt","tube","tui","tunes","tushu","tv","tvs","tw","tz","ua","ubank","ubs","uconnect","ug","uk","unicom","university","uno","uol","ups","us","uy","uz","va","vacations","vana","vanguard","vc","ve","vegas","ventures","verisign","versicherung","vet","vg","vi","viajes","video","vig","viking","villas","vin","vip","virgin","visa","vision","vista","vistaprint","viva","vivo","vlaanderen","vn","vodka","volkswagen","volvo","vote","voting","voto","voyage","vu","vuelos","wales","walmart","walter","wang","wanggou","warman","watch","watches","weather","weatherchannel","webcam","weber","website","wed","wedding","weibo","weir","wf","whoswho","wien","wiki","williamhill","win","windows","wine","winners","wme","wolterskluwer","woodside","work","works","world","wow","ws","wtc","wtf","xbox","xerox","xfinity","xihuan","xin","\u0915\u0949\u092e","\u30bb\u30fc\u30eb","\u4f5b\u5c71","\u0cad\u0cbe\u0cb0\u0ca4","\u6148\u5584","\u96c6\u56e2","\u5728\u7ebf","\ud55c\uad6d","\u0b2d\u0b3e\u0b30\u0b24","\u5927\u4f17\u6c7d\u8f66","\u70b9\u770b","\u0e04\u0e2d\u0e21","\u09ad\u09be\u09f0\u09a4","\u09ad\u09be\u09b0\u09a4","\u516b\u5366","\u0645\u0648\u0642\u0639","\u09ac\u09be\u0982\u09b2\u09be","\u516c\u76ca","\u516c\u53f8","\u9999\u683c\u91cc\u62c9","\u7f51\u7ad9","\u79fb\u52a8","\u6211\u7231\u4f60","\u043c\u043e\u0441\u043a\u0432\u0430","\u049b\u0430\u0437","\u043a\u0430\u0442\u043e\u043b\u0438\u043a","\u043e\u043d\u043b\u0430\u0439\u043d","\u0441\u0430\u0439\u0442","\u8054\u901a","\u0441\u0440\u0431","\u0431\u0433","\u0431\u0435\u043b","\u05e7\u05d5\u05dd","\u65f6\u5c1a","\u5fae\u535a","\u6de1\u9a6c\u9521","\u30d5\u30a1\u30c3\u30b7\u30e7\u30f3","\u043e\u0440\u0433","\u0928\u0947\u091f","\u30b9\u30c8\u30a2","\uc0bc\uc131","\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd","\u5546\u6807","\u5546\u5e97","\u5546\u57ce","\u0434\u0435\u0442\u0438","\u043c\u043a\u0434","\u0435\u044e","\u30dd\u30a4\u30f3\u30c8","\u65b0\u95fb","\u5de5\u884c","\u5bb6\u96fb","\u0643\u0648\u0645","\u4e2d\u6587\u7f51","\u4e2d\u4fe1","\u4e2d\u56fd","\u4e2d\u570b","\u5a31\u4e50","\u8c37\u6b4c","\u0c2d\u0c3e\u0c30\u0c24\u0c4d","\u0dbd\u0d82\u0d9a\u0dcf","\u96fb\u8a0a\u76c8\u79d1","\u8d2d\u7269","\u30af\u30e9\u30a6\u30c9","\u0aad\u0abe\u0ab0\u0aa4","\u901a\u8ca9","\u092d\u093e\u0930\u0924\u092e\u094d","\u092d\u093e\u0930\u0924","\u092d\u093e\u0930\u094b\u0924","\u7f51\u5e97","\u0938\u0902\u0917\u0920\u0928","\u9910\u5385","\u7f51\u7edc","\u043a\u043e\u043c","\u0443\u043a\u0440","\u9999\u6e2f","\u8bfa\u57fa\u4e9a","\u98df\u54c1","\u98de\u5229\u6d66","\u53f0\u6e7e","\u53f0\u7063","\u624b\u8868","\u624b\u673a","\u043c\u043e\u043d","\u0627\u0644\u062c\u0632\u0627\u0626\u0631","\u0639\u0645\u0627\u0646","\u0627\u0631\u0627\u0645\u0643\u0648","\u0627\u06cc\u0631\u0627\u0646","\u0627\u0644\u0639\u0644\u064a\u0627\u0646","\u0627\u062a\u0635\u0627\u0644\u0627\u062a","\u0627\u0645\u0627\u0631\u0627\u062a","\u0628\u0627\u0632\u0627\u0631","\u067e\u0627\u06a9\u0633\u062a\u0627\u0646","\u0627\u0644\u0627\u0631\u062f\u0646","\u0645\u0648\u0628\u0627\u064a\u0644\u064a","\u0628\u0627\u0631\u062a","\u0628\u06be\u0627\u0631\u062a","\u0627\u0644\u0645\u063a\u0631\u0628","\u0627\u0628\u0648\u0638\u0628\u064a","\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629","\u0680\u0627\u0631\u062a","\u0643\u0627\u062b\u0648\u0644\u064a\u0643","\u0633\u0648\u062f\u0627\u0646","\u0647\u0645\u0631\u0627\u0647","\u0639\u0631\u0627\u0642","\u0645\u0644\u064a\u0633\u064a\u0627","\u6fb3\u9580","\ub2f7\ucef4","\u653f\u5e9c","\u0634\u0628\u0643\u0629","\u0628\u064a\u062a\u0643","\u0639\u0631\u0628","\u10d2\u10d4","\u673a\u6784","\u7ec4\u7ec7\u673a\u6784","\u5065\u5eb7","\u0e44\u0e17\u0e22","\u0633\u0648\u0631\u064a\u0629","\u62db\u8058","\u0440\u0443\u0441","\u0440\u0444","\u73e0\u5b9d","\u062a\u0648\u0646\u0633","\u5927\u62ff","\u307f\u3093\u306a","\u30b0\u30fc\u30b0\u30eb","\u03b5\u03bb","\u4e16\u754c","\u66f8\u7c4d","\u0d2d\u0d3e\u0d30\u0d24\u0d02","\u0a2d\u0a3e\u0a30\u0a24","\u7f51\u5740","\ub2f7\ub137","\u30b3\u30e0","\u5929\u4e3b\u6559","\u6e38\u620f","verm\xf6gensberater","verm\xf6gensberatung","\u4f01\u4e1a","\u4fe1\u606f","\u5609\u91cc\u5927\u9152\u5e97","\u5609\u91cc","\u0645\u0635\u0631","\u0642\u0637\u0631","\u5e7f\u4e1c","\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8","\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe","\u0570\u0561\u0575","\u65b0\u52a0\u5761","\u0641\u0644\u0633\u0637\u064a\u0646","\u653f\u52a1","xperia","xxx","xyz","yachts","yahoo","yamaxun","yandex","ye","yodobashi","yoga","yokohama","you","youtube","yt","yun","za","zappos","zara","zero","zip","zippo","zm","zone","zuerich","zw"]},953,[]); -__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return t}},954,[]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),l=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=l(r(d[2])),o=l(r(d[3])),u=l(r(d[4])),f=l(r(d[5])),s=l(r(d[6])),c=l(r(d[7])),p=l(r(d[8])),v=t(r(d[9])),h=r(d[10]),y=r(d[11]),_=(function(t){function l(){return(0,u.default)(this,l),(0,s.default)(this,(0,c.default)(l).apply(this,arguments))}return(0,p.default)(l,t),(0,f.default)(l,[{key:"render",value:function(){var t=this.props.color,l=void 0===t?y.ColorPalette.white:t,u=this.props.size,f=void 0===u?'large':u;'medium'===f&&(f='large');var s=(0,o.default)({animating:!0,color:l},this.props,{size:f});return v.default.createElement(h.ActivityIndicator,(0,n.default)({animating:!0},s,{size:f}))}}]),l})(v.Component);e.default=_},955,[2,3,16,54,4,5,6,9,10,13,17,406]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=n(r(d[2])),l=n(r(d[3])),f=n(r(d[4])),o=n(r(d[5])),p=n(r(d[6])),s=n(r(d[7])),c=n(r(d[8])),h=t(r(d[9])),v=r(d[10]),y=(function(t){function n(){return(0,f.default)(this,n),(0,p.default)(this,(0,s.default)(n).apply(this,arguments))}return(0,c.default)(n,t),(0,o.default)(n,[{key:"render",value:function(){var t=this.props,n=t.children,f=(0,l.default)(t,["children"]);return h.default.createElement(v.Modal,(0,u.default)({animationType:'slide',supportedOrientations:['landscape','portrait'],transparent:!0},f),n)}}]),n})(h.Component);e.default=y},956,[2,3,16,56,4,5,6,9,10,13,17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),l=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=l(r(d[2])),n=l(r(d[3])),f=l(r(d[4])),o=l(r(d[5])),s=l(r(d[6])),c=t(r(d[7])),p=r(d[8]),h=r(d[9]),v=r(d[10]),y=l(r(d[11])),T=(function(t){function l(){return(0,u.default)(this,l),(0,f.default)(this,(0,o.default)(l).apply(this,arguments))}return(0,s.default)(l,t),(0,n.default)(l,[{key:"render",value:function(){var t=this.props.t;return c.default.createElement(p.View,{style:y.default.pullToRefresh},c.default.createElement(p.Text,{style:y.default.pullToRefreshText},t('sectionList.pullToRefresh')),c.default.createElement(v.Icon,{src:v.IconMenuDown,style:y.default.pullToRefreshIcon}))}}]),l})(c.Component),R=(0,h.translate)(T);e.default=R},957,[2,3,4,5,6,9,10,13,17,607,403,921]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=n(r(d[2])),o=n(r(d[3])),u=n(r(d[4])),s=n(r(d[5])),f=n(r(d[6])),c=n(r(d[7])),y=t(r(d[8])),v=n(r(d[9])),p=r(d[10]),b=n(r(d[11])),h=n(r(d[12])),_=n(r(d[13])),k=n(r(d[14])),A=(function(t){function n(t){var o;return(0,l.default)(this,n),(o=(0,u.default)(this,(0,s.default)(n).call(this,t)))._renderItemLine=o._renderItemLine.bind((0,c.default)((0,c.default)(o))),o._renderItemLines=o._renderItemLines.bind((0,c.default)((0,c.default)(o))),o}return(0,f.default)(n,t),(0,o.default)(n,[{key:"_renderItemLine",value:function(t,n){return t?y.default.createElement(_.default,{key:n,numberOfLines:1,style:k.default.listItemText},t):null}},{key:"_renderItemLines",value:function(t){return t&&t.length?t.map(this._renderItemLine):null}},{key:"_renderSecondaryAction",value:function(){var t=this.props.secondaryAction;return y.default.createElement(h.default,{onClick:t,style:k.default.secondaryActionContainer},y.default.createElement(_.default,{style:k.default.secondaryActionLabel},"+"))}},{key:"render",value:function(){var t,n=this.props,l=n.item,o=n.slideActions,u=l.id;if(o){t=[];var s=o,f=Array.isArray(s),c=0;for(s=f?s:s["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var h;if(f){if(c>=s.length)break;h=s[c++]}else{if((c=s.next()).done)break;h=c.value}var _=h;t.push({backgroundColor:_.backgroundColor,onPress:_.onPress.bind(void 0,u),text:_.text})}}return y.default.createElement(v.default,{autoClose:!0,backgroundColor:p.ColorPalette.transparent,right:t},y.default.createElement(b.default,{item:l,onPress:this.props.onPress},this.props.secondaryAction&&this._renderSecondaryAction()))}}]),n})(y.Component);e.default=A},958,[2,3,4,5,6,9,10,8,13,959,406,398,416,940,921]); -__d(function(g,r,i,a,m,e,d){'use strict';Object.defineProperty(e,"__esModule",{value:!0});var t=Object.assign||function(t){for(var n=1;nt.props.sensitivity&&Math.abs(o.dy)<=t.props.sensitivity},onPanResponderGrant:this._handlePanResponderGrant,onPanResponderMove:this._handlePanResponderMove,onPanResponderRelease:this._handlePanResponderEnd,onPanResponderTerminate:this._handlePanResponderEnd,onShouldBlockNativeResponder:function(t,n){return!1},onPanResponderTerminationRequest:function(){return!1}})},componentWillReceiveProps:function(t){t.close&&this._close(),t.openRight&&this._openRight(),t.openLeft&&this._openLeft()},_handlePanResponderGrant:function(t,n){var o=this;this.props.disabled||(this.state.openedLeft||this.state.openedRight?this._callOnClose():this._callOnOpen(),this.refs.swipeoutContent.measure(function(t,n,s,h){var p=o.props.buttonWidth||s/5;o.setState({btnWidth:p,btnsLeftWidth:o.props.left?p*o.props.left.length:0,btnsRightWidth:o.props.right?p*o.props.right.length:0,swiping:!0,timeStart:(new Date).getTime()})}))},_handlePanResponderMove:function(t,n){if(!this.props.disabled){var o=n.dx,s=n.dy,h=this.state.btnsLeftWidth,p=this.state.btnsRightWidth;if(this.state.openedRight)o=n.dx-p;else if(this.state.openedLeft)o=n.dx+h;var l=Math.abs(o)>Math.abs(s);this.props.scroll&&(l?this.props.scroll(!1):this.props.scroll(!0)),this.state.swiping&&(o<0&&this.props.right?this.setState({contentPos:Math.min(o,0)}):o>0&&this.props.left&&this.setState({contentPos:Math.max(o,0)}))}},_handlePanResponderEnd:function(t,n){if(!this.props.disabled){var o=n.dx,s=this.state.contentPos,h=this.state.contentWidth,p=this.state.btnsLeftWidth,l=this.state.btnsRightWidth,u=.33*h,f=o>u||o>p/2,c=o<-u||o<-l/2;if(this.state.openedRight)c=o-u<-u;if(this.state.openedLeft)f=o+u>u;if((new Date).getTime()-this.state.timeStart<200)c=o<-u/10&&!this.state.openedLeft,f=o>u/10&&!this.state.openedRight;this.state.swiping&&(c&&s<0&&o<0?this._open(-l,'right'):f&&s>0&&o>0?this._open(p,'left'):this._close()),this.props.scroll&&this.props.scroll(!0)}},_tweenContent:function(t,o){this.tweenState(t,{easing:n.default.easingTypes.easeInOutQuad,duration:0===o?1.5*this.state.tweenDuration:this.state.tweenDuration,endValue:o})},_rubberBandEasing:function(t,n){return t<0&&t0&&t>n?n+Math.pow(t-n,.85):t},_autoClose:function(t){this.state.autoClose&&this._close();var n=t.onPress;n&&n()},_open:function(t,n){var o='left'===n,s=this.props,h=s.sectionID,p=s.rowID,l=s.onOpen;l&&l(h,p,n),this._tweenContent('contentPos',t),this.setState({contentPos:t,openedLeft:o,openedRight:!o,swiping:!1})},_close:function(){var t=this.props,n=t.sectionID,o=t.rowID,s=t.onClose;s&&(this.state.openedLeft||this.state.openedRight)&&s(n,o,this.state.openedRight?'right':'left');this._tweenContent('contentPos',0),this._callOnClose(),this.setState({openedRight:!1,openedLeft:!1,swiping:!1})},_callOnClose:function(){this.props.onClose&&this.props.onClose(this.props.sectionID,this.props.rowID)},_callOnOpen:function(){this.props.onOpen&&this.props.onOpen(this.props.sectionID,this.props.rowID)},_openRight:function(){var t=this;this.refs.swipeoutContent.measure(function(n,o,s,h){var p=t.props.buttonWidth||s/5;t.setState({btnWidth:p,btnsRightWidth:t.props.right?p*t.props.right.length:0},function(){t._tweenContent('contentPos',-t.state.btnsRightWidth),t._callOnOpen(),t.setState({contentPos:-t.state.btnsRightWidth,openedLeft:!1,openedRight:!0,swiping:!1})})})},_openLeft:function(){var t=this;this.refs.swipeoutContent.measure(function(n,o,s,h){var p=t.props.buttonWidth||s/5;t.setState({btnWidth:p,btnsLeftWidth:t.props.left?p*t.props.left.length:0},function(){t._tweenContent('contentPos',t.state.btnsLeftWidth),t._callOnOpen(),t.setState({contentPos:t.state.btnsLeftWidth,openedLeft:!0,openedRight:!1,swiping:!1})})})},render:function(){var n=this.state.contentWidth,o=this.getTweeningValue('contentPos'),p=[s.default.swipeout,this.props.style];this.props.backgroundColor&&p.push([{backgroundColor:this.props.backgroundColor}]);var l=-this.state.btnsRightWidth;if(o>0)l=this.state.btnsLeftWidth;var f={left:{left:0,overflow:'hidden',width:Math.min(l*(o/l),l)}},c={right:{left:Math.abs(n+Math.max(l,o)),right:0}},b={content:{transform:[{translateX:this._rubberBandEasing(o,l)}]}},w=[s.default.swipeoutContent];w.push(b.content);var y=[s.default.swipeoutBtns];y.push(c.right);var _=[s.default.swipeoutBtns];_.push(f.left);var R=o<0,v=o>0;return h.default.createElement(u.View,{style:p},h.default.createElement(u.View,t({ref:'swipeoutContent',style:w,onLayout:this._onLayout},this._panResponder.panHandlers),this.props.children),this._renderButtons(this.props.right,R,y),this._renderButtons(this.props.left,v,_))},_onLayout:function(t){var n=t.nativeEvent.layout,o=n.width,s=n.height;this.setState({contentWidth:o,contentHeight:s})},_renderButtons:function(t,n,o){return t&&n?h.default.createElement(u.View,{style:o},t.map(this._renderButton)):h.default.createElement(u.View,null)},_renderButton:function(t,n){var o=this;return h.default.createElement(c,{backgroundColor:t.backgroundColor,color:t.color,component:t.component,disabled:t.disabled,height:this.state.contentHeight,key:n,onPress:function(){return o._autoClose(t)},text:t.text,type:t.type,underlayColor:t.underlayColor,width:this.state.btnWidth})}});b.NativeButton=o.default,b.SwipeoutButton=c,e.default=b},959,[960,961,962,13,781,280,17]); -__d(function(g,r,i,a,m,e,d){var n,t;n=this,t=function(){return(function(n){function t(o){if(u[o])return u[o].exports;var c=u[o]={exports:{},id:o,loaded:!1};return n[o].call(c.exports,c,c.exports,t),c.loaded=!0,c.exports}var u={};return t.m=n,t.c=u,t.p="",t(0)})({0:function(n,t,u){n.exports=u(90)},1:function(n,t){function u(){v=!1,f.length?h=f.concat(h):p=-1,h.length&&o()}function o(){if(!v){var n=setTimeout(u);v=!0;for(var t=h.length;t;){for(f=h,h=[];++p1)for(var u=1;uv.duration?v.duration:Math.max(0,s-h);u+=(0===v.duration?v.endValue:v.easing(p,v.beginValue,v.endValue,v.duration))-v.endValue}}return u},_rafCb:function(){var n=this.state;if(0!==n.tweenQueue.length){for(var t=Date.now(),u=[],o=0;on?c*Math.pow(2,10*(n-=1))*Math.sin((n*o-f)*(2*Math.PI)/s)*-.5+t:c*Math.pow(2,-10*(n-=1))*Math.sin((n*o-f)*(2*Math.PI)/s)*.5+l+t)},easeInBack:function(n,t,u,o,c){return void 0===c&&(c=1.70158),(u-t)*(n/=o)*n*((c+1)*n-c)+t},easeOutBack:function(n,t,u,o,c){return void 0===c&&(c=1.70158),(u-t)*((n=n/o-1)*n*((c+1)*n+c)+1)+t},easeInOutBack:function(n,t,u,o,c){var s=u-t;return void 0===c&&(c=1.70158),(n/=o/2)<1?s/2*(n*n*((1+(c*=1.525))*n-c))+t:s/2*((n-=2)*n*((1+(c*=1.525))*n+c)+2)+t},easeInBounce:function(n,t,o,c){var s=o-t;return s-u.easeOutBounce(c-n,0,s,c)+t},easeOutBounce:function(n,t,u,o){var c=u-t;return(n/=o)<.36363636363636365?c*(7.5625*n*n)+t:.7272727272727273>n?c*(7.5625*(n-=.5454545454545454)*n+.75)+t:.9090909090909091>n?c*(7.5625*(n-=.8181818181818182)*n+.9375)+t:c*(7.5625*(n-=.9545454545454546)*n+.984375)+t},easeInOutBounce:function(n,t,o,c){var s=o-t;return c/2>n?.5*u.easeInBounce(2*n,0,s,c)+t:.5*u.easeOutBounce(2*n-c,0,s,c)+.5*s+t}};n.exports=u}})},"object"==typeof e&&"object"==typeof m?m.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof e?e.tweenState=t():n.tweenState=t()},960,[]); -__d(function(g,r,i,a,m,e,d){'use strict';var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0});var s=Object.assign||function(t){for(var s=1;s1?this._renderPagedList(t):p.default.createElement(n[0].component,{disabled:t,style:_.default.pagedList}))}},{key:"_getIndicatorStyle",value:function(t){return this.state.pageIndex===t?_.default.pageIndicatorActive:null}},{key:"_maybeRefreshSelectedPage",value:function(){var t,n=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],l=this.props.pages[this.state.pageIndex];l&&(t=l.component)&&(t.WrappedComponent||t).refresh.call(t,this.props.dispatch,n)}},{key:"_onSelectPage",value:function(t){var n=this;return function(){t=n._validatePageIndex(t);var l=n.props.onSelectPage;l&&l(t),n.setState({pageIndex:t},n._maybeRefreshSelectedPage)}}},{key:"_renderPage",value:function(t,n){return t.component?p.default.createElement(h.View,{style:_.default.pageContainer},p.default.createElement(t.component,{disabled:n})):null}},{key:"_renderPagedList",value:function(t){var n=this,l=this.props.pages,u=this.state.pageIndex;return p.default.createElement(h.View,{style:_.default.pagedListContainer},this._renderPage(l[u],t),p.default.createElement(h.SafeAreaView,{style:_.default.pageIndicatorContainer},l.map(function(l,u){return n._renderPageIndicator(l,u,t)})))}},{key:"_renderPageIndicator",value:function(t,n,l){return t.component?p.default.createElement(h.TouchableOpacity,{disabled:l,key:n,onPress:this._onSelectPage(n),style:_.default.pageIndicator},p.default.createElement(h.View,{style:_.default.pageIndicatorContent},p.default.createElement(y.Icon,{src:t.icon,style:[_.default.pageIndicatorIcon,this._getIndicatorStyle(n)]}),p.default.createElement(h.Text,{style:[_.default.pageIndicatorText,this._getIndicatorStyle(n)]},t.title))):null}},{key:"_validatePageIndex",value:function(t){var n=this.props.pages.filter(function(t){return t.component}).length-1;return Math.max(0,Math.min(n,t))}}]),n})(p.Component),P=(0,v.connect)()(I);e.default=P},964,[2,3,4,5,6,9,10,8,13,17,403,534,921]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),u=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=u(r(d[2])),l=u(r(d[3])),o=u(r(d[4])),f=u(r(d[5])),s=u(r(d[6])),c=t(r(d[7])),h=r(d[8]),p=(function(t){function u(){return(0,n.default)(this,u),(0,o.default)(this,(0,f.default)(u).apply(this,arguments))}return(0,s.default)(u,t),(0,l.default)(u,[{key:"render",value:function(){var t=this.props,u=t.children,n=t.onPress;return n?c.default.createElement(h.TouchableWithoutFeedback,{onPress:n},u):u}}]),u})(c.Component);e.default=p},965,[2,3,4,5,6,9,10,13,17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var s=n(r(d[2])),o=n(r(d[3])),p=n(r(d[4])),f=n(r(d[5])),u=n(r(d[6])),l=t(r(d[7])),c=r(d[8]),h=n(r(d[9])),y=(function(t){function n(){return(0,s.default)(this,n),(0,p.default)(this,(0,f.default)(n).apply(this,arguments))}return(0,u.default)(n,t),(0,o.default)(n,[{key:"render",value:function(){return l.default.createElement(c.SafeAreaView,{style:h.default.container},l.default.createElement(c.SectionList,{ListEmptyComponent:this.props.ListEmptyComponent,keyExtractor:this.props.keyExtractor,onRefresh:this.props.onRefresh,refreshing:this.props.refreshing,renderItem:this.props.renderItem,renderSectionHeader:this.props.renderSectionHeader,sections:this.props.sections,style:h.default.list}))}}]),n})(l.Component);e.default=y},966,[2,3,4,5,6,9,10,13,17,921]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),s=n(r(d[3])),l=n(r(d[4])),u=n(r(d[5])),f=n(r(d[6])),h=n(r(d[7])),c=n(r(d[8])),p=n(r(d[9])),v=t(r(d[10])),w=r(d[11]),y=r(d[12]),_=n(r(d[13])),k=(function(t){function n(t){var o;(0,l.default)(this,n),o=(0,u.default)(this,(0,f.default)(n).call(this,t));var s=w.Dimensions.get('window'),h=s.height,c=s.width,v=t.position,y=h;return'left'!==v&&'right'!==v||(y=c),o.state={showOverlay:!1,sliderAnimation:new w.Animated.Value(0),positionOffset:y},o._onHardwareBackPress=o._onHardwareBackPress.bind((0,p.default)((0,p.default)(o))),o._onHide=o._onHide.bind((0,p.default)((0,p.default)(o))),o}return(0,c.default)(n,t),(0,h.default)(n,null,[{key:"getDerivedStateFromProps",value:function(t,n){return{showOverlay:t.show||n.showOverlay}}}]),(0,h.default)(n,[{key:"componentDidMount",value:function(){y.BackButtonRegistry.addListener(this._onHardwareBackPress,!0),this._mounted=!0,this._setShow(this.props.show)}},{key:"componentDidUpdate",value:function(t){var n=this.props.show;t.show!==n&&this._setShow(n)}},{key:"componentWillUnmount",value:function(){y.BackButtonRegistry.removeListener(this._onHardwareBackPress),this._mounted=!1}},{key:"render",value:function(){return this.state.showOverlay?v.default.createElement(w.View,{pointerEvents:"box-none",style:_.default.sliderViewContainer},v.default.createElement(w.TouchableWithoutFeedback,{onPress:this._onHide},v.default.createElement(w.View,{style:_.default.sliderViewShadow})),v.default.createElement(w.Animated.View,{pointerEvents:"box-none",style:this._getContentStyle()},this.props.children)):null}},{key:"_getContentStyle",value:function(){var t=(0,s.default)({},this.props.style,_.default.sliderViewContent),n=this.state.positionOffset;switch(this.props.position){case'bottom':(0,o.default)(t,{bottom:-n,left:0,right:0,top:n},{transform:[{translateY:this.state.sliderAnimation}]});break;case'left':(0,o.default)(t,{bottom:0,left:-n,right:n,top:0},{transform:[{translateX:this.state.sliderAnimation}]})}return t}},{key:"_onHardwareBackPress",value:function(){var t=this.props.onHide;return'function'==typeof t&&t()}},{key:"_onHide",value:function(){var t=this;this._setShow(!1).then(function(){var n=t.props.onHide;n&&n()})}},{key:"_setShow",value:function(t){var n=this;return new Promise(function(o){if(n._mounted){var s=n.state.positionOffset,l=n.props.position,u=s;'bottom'!==l&&'right'!==l||(u=-s),w.Animated.timing(n.state.sliderAnimation,{duration:200,toValue:t?u:0,useNativeDriver:!0}).start(function(s){s.finished&&n._mounted&&!t&&n.setState({showOverlay:!1},function(){n.forceUpdate()}),o()})}else o()})}}]),n})(v.PureComponent);e.default=k},967,[2,3,16,54,4,6,9,5,10,8,13,17,968,971]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"BackButtonRegistry",{enumerable:!0,get:function(){return n.default}});var n=t(r(d[1]));r(d[2])},968,[3,969,970]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var n=t(r(d[1])),s=t(r(d[2])),l=new((function(){function t(){(0,n.default)(this,t),this._listeners=[]}return(0,s.default)(t,[{key:"addListener",value:function(t){arguments.length>1&&void 0!==arguments[1]&&arguments[1]?this._listeners.splice(0,0,t):this._listeners.push(t)}},{key:"removeListener",value:function(t){this._listeners=this._listeners.filter(function(n){return n!==t})}},{key:"onHardwareBackPress",value:function(){var t=this._listeners,n=Array.isArray(t),s=0;for(t=n?t:t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();;){var l;if(n){if(s>=t.length)break;l=t[s++]}else{if((s=t.next()).done)break;l=s.value}if(!0===l())return!0}return!1}}]),t})());e.default=l},969,[3,4,5]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),s=r(d[2]),c=r(d[3]),u=n(r(d[4]));u.default.onHardwareBackPress=u.default.onHardwareBackPress.bind(u.default),c.MiddlewareRegistry.register(function(){return function(n){return function(c){switch(c.type){case s.APP_WILL_MOUNT:t.BackHandler.addEventListener('hardwareBackPress',u.default.onHardwareBackPress);break;case s.APP_WILL_UNMOUNT:t.BackHandler.removeEventListener('hardwareBackPress',u.default.onHardwareBackPress)}return n(c)}}})},970,[3,17,844,534,969]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=t(r(d[1])),o=r(d[2]),u=r(d[3]),n={sliderViewContainer:(0,l.default)({},o.StyleSheet.absoluteFillObject,{zIndex:u.OVERLAY_Z_INDEX}),sliderViewContent:{position:'absolute'},sliderViewShadow:(0,l.default)({},o.StyleSheet.absoluteFillObject,{backgroundColor:'rgba(0, 0, 0, 0.5)'})};e.default=n},971,[3,54,17,972]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.OVERLAY_Z_INDEX=void 0;e.OVERLAY_Z_INDEX=1e3},972,[]); -__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.Switch}});var t=r(d[0])},973,[17]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l=n(r(d[2])),u=n(r(d[3])),o=n(r(d[4])),f=n(r(d[5])),s=n(r(d[6])),c=n(r(d[7])),p=t(r(d[8])),v=r(d[9]),y=r(d[10]),E=(0,c.default)({},v.StyleSheet.absoluteFillObject,{alignItems:'center',justifyContent:'center'}),h=(function(t){function n(){return(0,l.default)(this,n),(0,o.default)(this,(0,f.default)(n).apply(this,arguments))}return(0,s.default)(n,t),(0,u.default)(n,[{key:"render",value:function(){var t=this.props,n=t.children,l=t.style;return p.default.createElement(v.View,{pointerEvents:"box-none",style:E},p.default.createElement(v.View,{pointerEvents:"none",style:[E,y.TINTED_VIEW_DEFAULT,l]}),p.default.createElement(v.View,{pointerEvents:"box-none",style:E},n))}}]),n})(p.Component);e.default=h},974,[2,3,4,5,6,9,10,54,13,17,921]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var u=t(r(d[1])),n=t(r(d[2])),f=t(r(d[3])),l=t(r(d[4])),o=t(r(d[5])),c=(function(t){function c(){return(0,u.default)(this,c),(0,f.default)(this,(0,l.default)(c).apply(this,arguments))}return(0,o.default)(c,t),(0,n.default)(c,null,[{key:"refresh",value:function(){}}]),c})(r(d[6]).Component);e.default=c},975,[3,4,5,6,9,10,13]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var o=n(r(d[2])),u=n(r(d[3])),s=n(r(d[4])),l=n(r(d[5])),f=n(r(d[6])),c=n(r(d[7])),p=t(r(d[8])),y=r(d[9]),_=(function(t){function n(t){var l;return(0,o.default)(this,n),(l=(0,u.default)(this,(0,s.default)(n).call(this,t)))._getItemKey=l._getItemKey.bind((0,c.default)((0,c.default)(l))),l._onPress=l._onPress.bind((0,c.default)((0,c.default)(l))),l._onRefresh=l._onRefresh.bind((0,c.default)((0,c.default)(l))),l._renderItem=l._renderItem.bind((0,c.default)((0,c.default)(l))),l._renderListEmptyComponent=l._renderListEmptyComponent.bind((0,c.default)((0,c.default)(l))),l._renderSectionHeader=l._renderSectionHeader.bind((0,c.default)((0,c.default)(l))),l}return(0,f.default)(n,t),(0,l.default)(n,null,[{key:"createSection",value:function(t,n){return{data:[],key:n,title:t}}}]),(0,l.default)(n,[{key:"render",value:function(){var t=this.props,n=t.renderListEmptyComponent,o=void 0===n?this._renderListEmptyComponent():n,u=t.sections;return p.default.createElement(y.SectionList,{ListEmptyComponent:o,keyExtractor:this._getItemKey,onItemClick:this.props.onPress,onRefresh:this._onRefresh,refreshing:!1,renderItem:this._renderItem,renderSectionHeader:this._renderSectionHeader,sections:u})}},{key:"_getItemKey",value:function(t,n){return n+"-"+t.key}},{key:"_onPress",value:function(t){var n=this.props,o=n.disabled,u=n.onPress;return!o&&t&&'function'==typeof u?function(){return u(t)}:null}},{key:"_onRefresh",value:function(){var t=this.props.onRefresh;'function'==typeof t&&t()}},{key:"_onSecondaryAction",value:function(t){var n=this;return function(){n.props.onSecondaryAction(t)}}},{key:"_renderItem",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:'',o=t.item,u=o.id,s=o.url;return void 0===o.title?null:p.default.createElement(y.NavigateSectionListItem,{item:o,key:n,onPress:s?this._onPress(s):void 0,secondaryAction:s?void 0:this._onSecondaryAction(u),slideActions:this.props.slideActions})}},{key:"_renderListEmptyComponent",value:function(){return'function'==typeof this.props.onRefresh?p.default.createElement(y.NavigateSectionListEmptyComponent,null):null}},{key:"_renderSectionHeader",value:function(t){return p.default.createElement(y.NavigateSectionListSectionHeader,{section:t})}}]),n})(p.Component);e.default=_},976,[2,3,4,6,9,5,10,8,13,396]); -__d(function(g,r,i,a,m,e,d){},977,[]); -__d(function(g,r,i,a,m,e,d){function t(t){this.options=t||{locator:{}}}function n(t,n,c){if(!t){if(n instanceof o)return n;t=n}var s={},u=t instanceof Function;function h(n){var o=t[n];!o&&u&&(o=2==t.length?function(o){t(n,o)}:t),s[n]=o&&function(t){o('[xmldom '+n+']\t'+t+l(c))}||function(){}}return c=c||{},h('warning'),h('error'),h('fatalError'),s}function o(){this.cdata=!1}function c(t,n){n.lineNumber=t.lineNumber,n.columnNumber=t.columnNumber}function l(t){if(t)return'\n@'+(t.systemId||'')+'#[line:'+t.lineNumber+',col:'+t.columnNumber+']'}function s(t,n,o){return'string'==typeof t?t.substr(n,o):t.length>=n+o||n?new java.lang.String(t,n,o)+'':t}function u(t,n){t.currentElement?t.currentElement.appendChild(n):t.doc.appendChild(n)}t.prototype.parseFromString=function(t,c){var l=this.options,s=new h,u=l.domBuilder||new o,f=l.errorHandler,p=l.locator,D=l.xmlns||{},E={lt:'<',gt:'>',amp:'&',quot:'"',apos:"'"};return p&&u.setDocumentLocator(p),s.errorHandler=n(f,u,p),s.domBuilder=l.domBuilder||u,/\/x?html?$/.test(c)&&(E.nbsp='\xa0',E.copy='\xa9',D['']='http://www.w3.org/1999/xhtml'),D.xml=D.xml||'http://www.w3.org/XML/1998/namespace',t?s.parse(t,D,E):s.errorHandler.error("invalid doc source"),u.doc},o.prototype={startDocument:function(){this.doc=(new f).createDocument(null,null,null),this.locator&&(this.doc.documentURI=this.locator.systemId)},startElement:function(t,n,o,l){var s=this.doc,h=s.createElementNS(t,o||n),f=l.length;u(this,h),this.currentElement=h,this.locator&&c(this.locator,h);for(var p=0;p65535){var n=55296+((t-=65536)>>10),s=56320+(1023&t);return String.fromCharCode(n,s)}return String.fromCharCode(t)}function l(t){var n=t.slice(1,-1);return n in s?s[n]:'#'===n.charAt(0)?o(parseInt(n.substr(1).replace('x','0x'))):(u.error('entity not found:'+t),t)}function f(n){if(n>O){var s=t.substring(O,n).replace(/&#?\w+;/g,l);x&&h(O),c.characters(s,0,n-O),O=n}}function h(n,s){for(;n>=w&&(s=b.exec(t));)p=s.index,w=p+s[0].length,x.lineNumber++;x.columnNumber=n-p+1}for(var p=0,w=0,b=/.*(?:\r\n?|\n)|.*$/g,x=c.locator,E=[{currentNSMap:n}],M={},O=0;;){try{var T=t.indexOf('<',O);if(T<0){if(!t.substr(O).match(/^\s*$/)){var S=c.doc,q=S.createTextNode(t.substr(O));S.appendChild(q),c.currentElement=q}return}switch(T>O&&f(T),t.charAt(T+1)){case'/':var y=t.indexOf('>',T+3),_=t.substring(T+2,y),I=E.pop();y<0?(_=t.substring(T+2).replace(/[\s<].*/,''),u.error("end tag name: "+_+' is not complete:'+I.tagName),y=T+1+_.length):_.match(/\sO?O=y:f(Math.max(T,O)+1)}}function v(t,n){return n.lineNumber=t.lineNumber,n.columnNumber=t.columnNumber,n}function N(t,n,s,b,x,v){for(var N,F=++n,D=c;;){var k=t.charAt(F);switch(k){case'=':if(D===u)N=t.slice(n,F),D=l;else{if(D!==o)throw new Error('attribute equal must after attrName');D=l}break;case'\'':case'"':if(D===l||D===u){if(D===u&&(v.warning('attribute value must after "="'),N=t.slice(n,F)),n=F+1,!((F=t.indexOf(k,n))>0))throw new Error('attribute value no end \''+k+'\' match');E=t.slice(n,F).replace(/&#?\w+;/g,x),s.add(N,E,n-1),D=h}else{if(D!=f)throw new Error('attribute value must after "="');E=t.slice(n,F).replace(/&#?\w+;/g,x),s.add(N,E,n),v.warning('attribute "'+N+'" missed start quot('+k+')!!'),n=F+1,D=h}break;case'/':switch(D){case c:s.setTagName(t.slice(n,F));case h:case p:case w:D=w,s.closed=!0;case f:case u:case o:break;default:throw new Error("attribute invalid close char('/')")}break;case'':return v.error('unexpected end of input'),D==c&&s.setTagName(t.slice(n,F)),F;case'>':switch(D){case c:s.setTagName(t.slice(n,F));case h:case p:case w:break;case f:case u:'/'===(E=t.slice(n,F)).slice(-1)&&(s.closed=!0,E=E.slice(0,-1));case o:D===o&&(E=N),D==f?(v.warning('attribute "'+E+'" missed quot(")!!'),s.add(N,E.replace(/&#?\w+;/g,x),n)):('http://www.w3.org/1999/xhtml'===b['']&&E.match(/^(?:disabled|checked|selected)$/i)||v.warning('attribute "'+E+'" missed value!! "'+E+'" instead!!'),s.add(E,E,n));break;case l:throw new Error('attribute value missed!!')}return F;case"\x80":k=' ';default:if(k<=' ')switch(D){case c:s.setTagName(t.slice(n,F)),D=p;break;case u:N=t.slice(n,F),D=o;break;case f:var E=t.slice(n,F).replace(/&#?\w+;/g,x);v.warning('attribute "'+E+'" missed quot(")!!'),s.add(N,E,n);case h:D=p}else switch(D){case o:s.tagName;'http://www.w3.org/1999/xhtml'===b['']&&N.match(/^(?:disabled|checked|selected)$/i)||v.warning('attribute "'+N+'" missed value!! "'+N+'" instead2!!'),s.add(N,N,n),n=F,D=u;break;case h:v.warning('attribute space is required"'+N+'"!!');case p:D=u,n=F;break;case l:D=f,n=F;break;case w:throw new Error("elements closed character '/' and '>' must be connected to")}}F++}}function F(t,n,s){for(var c=t.tagName,u=null,o=t.length;o--;){var l=t[o],f=l.qName,h=l.value;if((x=f.indexOf(':'))>0)var p=l.prefix=f.slice(0,x),w=f.slice(x+1),b='xmlns'===p&&w;else w=f,p=null,b='xmlns'===f&&'';l.localName=w,!1!==b&&(null==u&&(u={},E(s,s={})),s[b]=u[b]=h,l.uri='http://www.w3.org/2000/xmlns/',n.startPrefixMapping(b,h))}for(o=t.length;o--;){(p=(l=t[o]).prefix)&&('xml'===p&&(l.uri='http://www.w3.org/XML/1998/namespace'),'xmlns'!==p&&(l.uri=s[p||'']))}var x;(x=c.indexOf(':'))>0?(p=t.prefix=c.slice(0,x),w=t.localName=c.slice(x+1)):(p=null,w=t.localName=c);var v=t.uri=s[p||''];if(n.startElement(v,w,c,t),!t.closed)return t.currentNSMap=s,t.localNSMap=u,!0;if(n.endElement(v,w,c),u)for(p in u)n.endPrefixMapping(p)}function D(t,n,s,c,u){if(/^(?:script|textarea)$/i.test(s)){var o=t.indexOf('',n),l=t.substring(n+1,o);if(/[&<]/.test(l))return/^script$/i.test(s)?(u.characters(l,0,l.length),o):(l=l.replace(/&#?\w+;/g,c),u.characters(l,0,l.length),o)}return n+1}function k(t,n,s,c){var u=c[s];return null==u&&((u=t.lastIndexOf(''))n?(s.comment(t,n+4,u-n-4),u+3):(c.error("Unclosed comment"),-1):-1;default:if('CDATA['==t.substr(n+3,6)){var u=t.indexOf(']]>',n+9);return s.startCDATA(),s.characters(t,n+9,u-n-9),s.endCDATA(),u+3}var o=O(t,n),l=o.length;if(l>1&&/!doctype/i.test(o[0][0])){var f=o[1][0],h=l>3&&/^public$/i.test(o[2][0])&&o[3][0],p=l>4&&o[4][0],w=o[l-1];return s.startDTD(f,h&&h.replace(/^(['"])(.*?)\1$/,'$2'),p&&p.replace(/^(['"])(.*?)\1$/,'$2')),s.endDTD(),w.index+w[0].length}}return-1}function $(t,n,s){var c=t.indexOf('?>',n);if(c){var u=t.substring(n,c).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);if(u){u[0].length;return s.processingInstruction(u[1],u[2]),c+2}return-1}return-1}function A(t){}function M(t,n){return t.__proto__=n,t}function O(t,n){var s,c=[],u=/'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;for(u.lastIndex=n,u.exec(t);s=u.exec(t);)if(c.push(s),s[1])return c}b.prototype={parse:function(t,n,s){var c=this.domBuilder;c.startDocument(),E(n,n={}),x(t,n,s,c,this.errorHandler),c.endDocument()}},A.prototype={setTagName:function(t){if(!s.test(t))throw new Error('invalid tagName:'+t);this.tagName=t},add:function(t,n,c){if(!s.test(t))throw new Error('invalid attribute:'+t);this[this.length++]={qName:t,value:n,offset:c}},length:0,getLocalName:function(t){return this[t].localName},getLocator:function(t){return this[t].locator},getQName:function(t){return this[t].qName},getURI:function(t){return this[t].uri},getValue:function(t){return this[t].value}},M({},M.prototype)instanceof M||(M=function(t,n){function s(){}for(n in s.prototype=n,s=new s,t)s[n]=t[n];return s}),e.XMLReader=b},979,[]); -__d(function(g,r,i,a,m,e,d){function t(t,n){for(var o in t)n[o]=t[o]}function n(n,o){var u=n.prototype;if(Object.create){var s=Object.create(o.prototype);u.__proto__=s}if(!(u instanceof o)){function l(){}l.prototype=o.prototype,t(u,l=new l),n.prototype=u=l}u.constructor!=n&&('function'!=typeof n&&console.error("unknow Class:"+n),u.constructor=n)}var o='http://www.w3.org/1999/xhtml',u={},s=u.ELEMENT_NODE=1,l=u.ATTRIBUTE_NODE=2,c=u.TEXT_NODE=3,h=u.CDATA_SECTION_NODE=4,p=u.ENTITY_REFERENCE_NODE=5,f=u.ENTITY_NODE=6,N=u.PROCESSING_INSTRUCTION_NODE=7,v=u.COMMENT_NODE=8,w=u.DOCUMENT_NODE=9,E=u.DOCUMENT_TYPE_NODE=10,y=u.DOCUMENT_FRAGMENT_NODE=11,b=u.NOTATION_NODE=12,T={},_={},D=(T.INDEX_SIZE_ERR=(_[1]="Index size error",1),T.DOMSTRING_SIZE_ERR=(_[2]="DOMString size error",2),T.HIERARCHY_REQUEST_ERR=(_[3]="Hierarchy request error",3)),S=(T.WRONG_DOCUMENT_ERR=(_[4]="Wrong document",4),T.INVALID_CHARACTER_ERR=(_[5]="Invalid character",5),T.NO_DATA_ALLOWED_ERR=(_[6]="No data allowed",6),T.NO_MODIFICATION_ALLOWED_ERR=(_[7]="No modification allowed",7),T.NOT_FOUND_ERR=(_[8]="Not found",8)),C=(T.NOT_SUPPORTED_ERR=(_[9]="Not supported",9),T.INUSE_ATTRIBUTE_ERR=(_[10]="Attribute in use",10));T.INVALID_STATE_ERR=(_[11]="Invalid state",11),T.SYNTAX_ERR=(_[12]="Syntax error",12),T.INVALID_MODIFICATION_ERR=(_[13]="Invalid modification",13),T.NAMESPACE_ERR=(_[14]="Invalid namespace",14),T.INVALID_ACCESS_ERR=(_[15]="Invalid access",15);function I(t,n){if(n instanceof Error)var o=n;else o=this,Error.call(this,_[t]),this.message=_[t],Error.captureStackTrace&&Error.captureStackTrace(this,I);return o.code=t,n&&(this.message=this.message+": "+n),o}function A(){}function R(t,n){this._node=t,this._refresh=n,x(this)}function x(n){var o=n._node._inc||n._node.ownerDocument._inc;if(n._inc!=o){var u=n._refresh(n._node);he(n,'length',u.length),t(u,n),n._inc=o}}function O(){}function U(t,n){for(var o=t.length;o--;)if(t[o]===n)return o}function M(t,n,o,u){if(u?n[U(n,u)]=o:n[n.length++]=o,t){o.ownerElement=t;var s=t.ownerDocument;s&&(u&&Y(s,t,u),F(s,t,o))}}function B(t,n,o){var u=U(n,o);if(!(u>=0))throw I(S,new Error(t.tagName+'@'+o));for(var s=n.length-1;u'==t&&'>')||'&'==t&&'&'||'"'==t&&'"'||'&#'+t.charCodeAt()+';'}function k(t,n){if(n(t))return!0;if(t=t.firstChild)do{if(k(t,n))return!0}while(t=t.nextSibling)}function j(){}function F(t,n,o){t&&t._inc++,'http://www.w3.org/2000/xmlns/'==o.namespaceURI&&(n._nsMap[o.prefix?o.localName:'']=o.value)}function Y(t,n,o,u){t&&t._inc++,'http://www.w3.org/2000/xmlns/'==o.namespaceURI&&delete n._nsMap[o.prefix?o.localName:'']}function z(t,n,o){if(t&&t._inc){t._inc++;var u=n.childNodes;if(o)u[u.length++]=o;else{for(var s=n.firstChild,l=0;s;)u[l++]=s,s=s.nextSibling;u.length=l}}}function $(t,n){var o=n.previousSibling,u=n.nextSibling;return o?o.nextSibling=u:t.firstChild=u,u?u.previousSibling=o:t.lastChild=o,z(t.ownerDocument,t),n}function X(t,n,o){var u=n.parentNode;if(u&&u.removeChild(n),n.nodeType===y){var s=n.firstChild;if(null==s)return n;var l=n.lastChild}else s=l=n;var c=o?o.previousSibling:t.lastChild;s.previousSibling=c,l.nextSibling=o,c?c.nextSibling=s:t.firstChild=s,null==o?t.lastChild=l:o.previousSibling=l;do{s.parentNode=t}while(s!==l&&(s=s.nextSibling));return z(t.ownerDocument||t,t),n.nodeType==y&&(n.firstChild=n.lastChild=null),n}function G(t,n){var o=n.parentNode;if(o){var u=t.lastChild;o.removeChild(n);u=t.lastChild}u=t.lastChild;return n.parentNode=t,n.previousSibling=u,n.nextSibling=null,u?u.nextSibling=n:t.firstChild=n,t.lastChild=n,z(t.ownerDocument,t,n),n}function H(){this._nsMap={}}function W(){}function q(){}function Z(){}function Q(){}function J(){}function K(){}function ee(){}function te(){}function ne(){}function re(){}function ie(){}function oe(){}function ae(t,n){var o=[],u=9==this.nodeType?this.documentElement:this,s=u.prefix,l=u.namespaceURI;if(l&&null==s&&null==(s=u.lookupPrefix(l)))var c=[{namespace:l,prefix:null}];return se(this,o,t,n,c),o.join('')}function ue(t,n,o){var u=t.prefix||'',s=t.namespaceURI;if(!u&&!s)return!1;if("xml"===u&&"http://www.w3.org/XML/1998/namespace"===s||'http://www.w3.org/2000/xmlns/'==s)return!1;for(var l=o.length;l--;){var c=o[l];if(c.prefix==u)return c.namespace!=s}return!0}function se(t,n,u,f,b){if(f){if(!(t=f(t)))return;if('string'==typeof t)return void n.push(t)}switch(t.nodeType){case s:b||(b=[]);b.length;var T=t.attributes,_=T.length,D=t.firstChild,S=t.tagName;u=o===t.namespaceURI||u,n.push('<',S);for(var C=0;C<_;C++){'xmlns'==(I=T.item(C)).prefix?b.push({prefix:I.localName,namespace:I.value}):'xmlns'==I.nodeName&&b.push({prefix:'',namespace:I.value})}for(C=0;C<_;C++){var I;if(ue(I=T.item(C),0,b)){var A=I.prefix||'',R=I.namespaceURI,x=A?' xmlns:'+A:" xmlns";n.push(x,'="',R,'"'),b.push({prefix:A,namespace:R})}se(I,n,u,f,b)}if(ue(t,0,b)){A=t.prefix||'',R=t.namespaceURI,x=A?' xmlns:'+A:" xmlns";n.push(x,'="',R,'"'),b.push({prefix:A,namespace:R})}if(D||u&&!/^(?:meta|link|img|br|hr|input)$/i.test(S)){if(n.push('>'),u&&/^script$/i.test(S))for(;D;)D.data?n.push(D.data):se(D,n,u,f,b),D=D.nextSibling;else for(;D;)se(D,n,u,f,b),D=D.nextSibling;n.push('')}else n.push('/>');return;case w:case y:for(D=t.firstChild;D;)se(D,n,u,f,b),D=D.nextSibling;return;case l:return n.push(' ',t.name,'="',t.value.replace(/[<&"]/g,V),'"');case c:return n.push(t.data.replace(/[<&]/g,V));case h:return n.push('');case v:return n.push("\x3c!--",t.data,"--\x3e");case E:var O=t.publicId,U=t.systemId;if(n.push('');else if(U&&'.'!=U)n.push(' SYSTEM "',U,'">');else{var M=t.internalSubset;M&&n.push(" [",M,"]"),n.push(">")}return;case N:return n.push("");case p:return n.push('&',t.nodeName,';');default:n.push('??',t.nodeName)}}function le(t,n,o){var u;switch(n.nodeType){case s:(u=n.cloneNode(!1)).ownerDocument=t;case y:break;case l:o=!0}if(u||(u=n.cloneNode(!1)),u.ownerDocument=t,u.parentNode=null,o)for(var c=n.firstChild;c;)u.appendChild(le(t,c,o)),c=c.nextSibling;return u}function ce(t,n,o){var u=new n.constructor;for(var c in n){var h=n[c];'object'!=typeof h&&h!=u[c]&&(u[c]=h)}switch(n.childNodes&&(u.childNodes=new A),u.ownerDocument=t,u.nodeType){case s:var p=n.attributes,f=u.attributes=new O,N=p.length;f._ownerElement=u;for(var v=0;v0},lookupPrefix:function(t){for(var n=this;n;){var o=n._nsMap;if(o)for(var u in o)if(o[u]==t)return u;n=n.nodeType==l?n.ownerDocument:n.parentNode}return null},lookupNamespaceURI:function(t){for(var n=this;n;){var o=n._nsMap;if(o&&t in o)return o[t];n=n.nodeType==l?n.ownerDocument:n.parentNode}return null},isDefaultNamespace:function(t){return null==this.lookupPrefix(t)}},t(u,L),t(u,L.prototype),j.prototype={nodeName:'#document',nodeType:w,doctype:null,documentElement:null,_inc:1,insertBefore:function(t,n){if(t.nodeType==y){for(var o=t.firstChild;o;){var u=o.nextSibling;this.insertBefore(o,n),o=u}return t}return null==this.documentElement&&t.nodeType==s&&(this.documentElement=t),X(this,t,n),t.ownerDocument=this,t},removeChild:function(t){return this.documentElement==t&&(this.documentElement=null),$(this,t)},importNode:function(t,n){return le(this,t,n)},getElementById:function(t){var n=null;return k(this.documentElement,function(o){if(o.nodeType==s&&o.getAttribute('id')==t)return n=o,!0}),n},createElement:function(t){var n=new H;return n.ownerDocument=this,n.nodeName=t,n.tagName=t,n.childNodes=new A,(n.attributes=new O)._ownerElement=n,n},createDocumentFragment:function(){var t=new re;return t.ownerDocument=this,t.childNodes=new A,t},createTextNode:function(t){var n=new Z;return n.ownerDocument=this,n.appendData(t),n},createComment:function(t){var n=new Q;return n.ownerDocument=this,n.appendData(t),n},createCDATASection:function(t){var n=new J;return n.ownerDocument=this,n.appendData(t),n},createProcessingInstruction:function(t,n){var o=new ie;return o.ownerDocument=this,o.tagName=o.target=t,o.nodeValue=o.data=n,o},createAttribute:function(t){var n=new W;return n.ownerDocument=this,n.name=t,n.nodeName=t,n.localName=t,n.specified=!0,n},createEntityReference:function(t){var n=new ne;return n.ownerDocument=this,n.nodeName=t,n},createElementNS:function(t,n){var o=new H,u=n.split(':'),s=o.attributes=new O;return o.childNodes=new A,o.ownerDocument=this,o.nodeName=n,o.tagName=n,o.namespaceURI=t,2==u.length?(o.prefix=u[0],o.localName=u[1]):o.localName=n,s._ownerElement=o,o},createAttributeNS:function(t,n){var o=new W,u=n.split(':');return o.ownerDocument=this,o.nodeName=n,o.name=n,o.namespaceURI=t,o.specified=!0,2==u.length?(o.prefix=u[0],o.localName=u[1]):o.localName=n,o}},n(j,L),H.prototype={nodeType:s,hasAttribute:function(t){return null!=this.getAttributeNode(t)},getAttribute:function(t){var n=this.getAttributeNode(t);return n&&n.value||''},getAttributeNode:function(t){return this.attributes.getNamedItem(t)},setAttribute:function(t,n){var o=this.ownerDocument.createAttribute(t);o.value=o.nodeValue=""+n,this.setAttributeNode(o)},removeAttribute:function(t){var n=this.getAttributeNode(t);n&&this.removeAttributeNode(n)},appendChild:function(t){return t.nodeType===y?this.insertBefore(t,null):G(this,t)},setAttributeNode:function(t){return this.attributes.setNamedItem(t)},setAttributeNodeNS:function(t){return this.attributes.setNamedItemNS(t)},removeAttributeNode:function(t){return this.attributes.removeNamedItem(t.nodeName)},removeAttributeNS:function(t,n){var o=this.getAttributeNodeNS(t,n);o&&this.removeAttributeNode(o)},hasAttributeNS:function(t,n){return null!=this.getAttributeNodeNS(t,n)},getAttributeNS:function(t,n){var o=this.getAttributeNodeNS(t,n);return o&&o.value||''},setAttributeNS:function(t,n,o){var u=this.ownerDocument.createAttributeNS(t,n);u.value=u.nodeValue=""+o,this.setAttributeNode(u)},getAttributeNodeNS:function(t,n){return this.attributes.getNamedItemNS(t,n)},getElementsByTagName:function(t){return new R(this,function(n){var o=[];return k(n,function(u){u===n||u.nodeType!=s||'*'!==t&&u.tagName!=t||o.push(u)}),o})},getElementsByTagNameNS:function(t,n){return new R(this,function(o){var u=[];return k(o,function(l){l===o||l.nodeType!==s||'*'!==t&&l.namespaceURI!==t||'*'!==n&&l.localName!=n||u.push(l)}),u})}},j.prototype.getElementsByTagName=H.prototype.getElementsByTagName,j.prototype.getElementsByTagNameNS=H.prototype.getElementsByTagNameNS,n(H,L),W.prototype.nodeType=l,n(W,L),q.prototype={data:'',substringData:function(t,n){return this.data.substring(t,t+n)},appendData:function(t){t=this.data+t,this.nodeValue=this.data=t,this.length=t.length},insertData:function(t,n){this.replaceData(t,0,n)},appendChild:function(t){throw new Error(_[D])},deleteData:function(t,n){this.replaceData(t,n,"")},replaceData:function(t,n,o){o=this.data.substring(0,t)+o+this.data.substring(t+n),this.nodeValue=this.data=o,this.length=o.length}},n(q,L),Z.prototype={nodeName:"#text",nodeType:c,splitText:function(t){var n=this.data,o=n.substring(t);n=n.substring(0,t),this.data=this.nodeValue=n,this.length=n.length;var u=this.ownerDocument.createTextNode(o);return this.parentNode&&this.parentNode.insertBefore(u,this.nextSibling),u}},n(Z,q),Q.prototype={nodeName:"#comment",nodeType:v},n(Q,q),J.prototype={nodeName:"#cdata-section",nodeType:h},n(J,q),K.prototype.nodeType=E,n(K,L),ee.prototype.nodeType=b,n(ee,L),te.prototype.nodeType=f,n(te,L),ne.prototype.nodeType=p,n(ne,L),re.prototype.nodeName="#document-fragment",re.prototype.nodeType=y,n(re,L),ie.prototype.nodeType=N,n(ie,L),oe.prototype.serializeToString=function(t,n,o){return ae.call(t,n,o)},L.prototype.toString=ae;try{if(Object.defineProperty){function pe(t){switch(t.nodeType){case s:case y:var n=[];for(t=t.firstChild;t;)7!==t.nodeType&&8!==t.nodeType&&n.push(pe(t)),t=t.nextSibling;return n.join('');default:return t.nodeValue}}Object.defineProperty(R.prototype,'length',{get:function(){return x(this),this.$$length}}),Object.defineProperty(L.prototype,'textContent',{get:function(){return pe(this)},set:function(t){switch(this.nodeType){case s:case y:for(;this.firstChild;)this.removeChild(this.firstChild);(t||String(t))&&this.appendChild(this.ownerDocument.createTextNode(t));break;default:this.data=t,this.value=t,this.nodeValue=t}}}),he=function(t,n,o){t['$$'+n]=o}}}catch(t){}e.DOMImplementation=P,e.XMLSerializer=oe},980,[]); -__d(function(e,t,r,i,s,a,n){s.exports={_args:[["react-native@0.61.1","/Users/djorkaeff/Downloads/rocket-chat/jitsi-meet"]],_from:"react-native@0.61.1",_id:"react-native@0.61.1",_inBundle:!1,_integrity:"sha512-FjOhAgzjrPr2BjBITmCY+SHzWxfP/bv5ve3oN6ItoIPsVPjJG2QZE8yeByVdI4TzaZrGHU4Ag7/7hX4j39cj2Q==",_location:"/react-native",_phantomChildren:{"@hapi/joi":"15.1.1","@react-native-community/cli-platform-android":"3.0.0-alpha.2","@react-native-community/cli-platform-ios":"3.0.0-alpha.2","@react-native-community/cli-tools":"2.8.3","color-convert":"1.9.1",commander:"2.20.0",compression:"1.7.3",connect:"3.7.0","core-js":"2.5.3",cosmiconfig:"5.2.0","cross-spawn":"6.0.5",deepmerge:"3.3.0","end-of-stream":"1.4.1",envinfo:"7.4.0",errorhandler:"1.5.1","escape-string-regexp":"1.0.5","fbjs-css-vars":"1.0.2","fs-extra":"7.0.1",glob:"7.1.2","graceful-fs":"4.1.11",inquirer:"3.3.0","is-stream":"1.1.0","isomorphic-fetch":"2.2.1","js-tokens":"3.0.2",lodash:"4.17.13","loose-envify":"1.3.1",metro:"0.56.0","metro-config":"0.56.0","metro-core":"0.56.0","metro-react-native-babel-transformer":"0.56.0",minimist:"1.2.0",mkdirp:"0.5.1",morgan:"1.9.1","node-notifier":"5.4.3","npm-run-path":"2.0.2","object-assign":"4.1.1",once:"1.4.0",open:"6.4.0",ora:"3.4.0","p-finally":"1.0.0",plist:"3.0.1",promise:"7.3.1",semver:"5.5.0","serve-static":"1.13.2",setimmediate:"1.0.5","shell-quote":"1.6.1","signal-exit":"3.0.2","strip-eof":"1.0.0",ws:"1.1.5"},_requested:{type:"version",registry:!0,raw:"react-native@0.61.1",name:"react-native",escapedName:"react-native",rawSpec:"0.61.1",saveSpec:null,fetchSpec:"0.61.1"},_requiredBy:["/"],_resolved:"https://registry.npmjs.org/react-native/-/react-native-0.61.1.tgz",_spec:"0.61.1",_where:"/Users/djorkaeff/Downloads/rocket-chat/jitsi-meet",bin:{"react-native":"./cli.js"},bugs:{url:"https://github.com/facebook/react-native/issues"},dependencies:{"@babel/runtime":"^7.0.0","@react-native-community/cli":"^3.0.0-alpha.1","@react-native-community/cli-platform-android":"^3.0.0-alpha.1","@react-native-community/cli-platform-ios":"^3.0.0-alpha.1","abort-controller":"^3.0.0",art:"^0.10.0","base64-js":"^1.1.2",connect:"^3.6.5","create-react-class":"^15.6.3","escape-string-regexp":"^1.0.5","event-target-shim":"^5.0.1",fbjs:"^1.0.0","fbjs-scripts":"^1.1.0","hermes-engine":"^0.2.1",invariant:"^2.2.4","jsc-android":"^245459.0.0","metro-babel-register":"^0.56.0","metro-react-native-babel-transformer":"^0.56.0","metro-source-map":"^0.56.0",nullthrows:"^1.1.0","pretty-format":"^24.7.0",promise:"^7.1.1","prop-types":"^15.7.2","react-devtools-core":"^3.6.3","react-refresh":"^0.4.0","regenerator-runtime":"^0.13.2",scheduler:"0.15.0","stacktrace-parser":"^0.1.3","whatwg-fetch":"^3.0.0"},description:"A framework for building native apps using React",detox:{"test-runner":"jest","runner-config":"RNTester/e2e/config.json",specs:"",configurations:{"ios.sim.release":{binaryPath:"RNTester/build/Build/Products/Release-iphonesimulator/RNTester.app/",build:"xcodebuild -workspace RNTester/RNTesterPods.xcworkspace -scheme RNTester -configuration Release -sdk iphonesimulator -derivedDataPath RNTester/build -UseModernBuildSystem=NO -quiet",type:"ios.simulator",name:"iPhone 6s"},"ios.sim.debug":{binaryPath:"RNTester/build/Build/Products/Debug-iphonesimulator/RNTester.app/",build:"xcodebuild -workspace RNTester/RNTesterPods.xcworkspace -scheme RNTester -configuration Debug -sdk iphonesimulator -derivedDataPath RNTester/build -UseModernBuildSystem=NO -quiet",type:"ios.simulator",name:"iPhone 6s"}}},devDependencies:{"@babel/core":"^7.0.0","@babel/generator":"^7.0.0","@react-native-community/eslint-plugin":"1.0.0","@reactions/component":"^2.0.2",async:"^2.4.0","babel-eslint":"10.0.1","clang-format":"^1.2.4",coveralls:"^3.0.2",detox:"12.2.0",eslint:"5.1.0","eslint-config-fb-strict":"24.3.0","eslint-config-fbjs":"2.1.0","eslint-config-prettier":"^6.0.0","eslint-plugin-babel":"^5.3.0","eslint-plugin-eslint-comments":"^3.1.1","eslint-plugin-flowtype":"2.50.3","eslint-plugin-jest":"22.4.1","eslint-plugin-jsx-a11y":"6.2.1","eslint-plugin-prettier":"2.6.2","eslint-plugin-react":"7.12.4","eslint-plugin-react-hooks":"^1.5.1","eslint-plugin-react-native":"3.6.0","eslint-plugin-relay":"1.3.0","flow-bin":"^0.105.0","flow-remove-types":"1.2.3",jest:"^24.8.0","jest-junit":"^6.3.0",jscodeshift:"^0.6.2",mkdirp:"^0.5.1",prettier:"1.17.0",react:"16.9.0","react-test-renderer":"16.9.0",shelljs:"^0.7.8",ws:"^6.1.4",yargs:"^9.0.0"},engines:{node:">=8.3"},files:[".flowconfig","android","cli.js","flow","init.sh","scripts/compose-source-maps.js","scripts/ios-configure-glog.sh","scripts/ios-install-third-party.sh","scripts/launchPackager.bat","scripts/launchPackager.command","scripts/node-binary.sh","scripts/packager.sh","scripts/react-native-xcode.sh","jest-preset.js","jest","lib","rn-get-polyfills.js","Libraries","LICENSE","packager","react-native.config.js","react.gradle","React.podspec","React-Core.podspec","React","ReactAndroid","ReactCommon","README.md","third-party-podspecs","template","local-cli","template.config.js","!template/node_modules","!template/yarn.lock","!template/package-lock.json"],homepage:"https://github.com/facebook/react-native#readme","jest-junit":{outputDirectory:"reports/junit",outputName:"js-test-results.xml"},license:"MIT",main:"Libraries/react-native/react-native-implementation.js",name:"react-native",peerDependencies:{react:"16.9.0"},repository:{type:"git",url:"git+ssh://git@github.com/facebook/react-native.git"},scripts:{"build-ios-e2e":"detox build -c ios.sim.release","clang-format":"clang-format -i --glob=*/**/*.{h,cpp,m,mm}","docker-build-android":"docker build -t reactnativeci/android -f .circleci/Dockerfiles/Dockerfile.android .","docker-setup-android":"docker pull reactnativecommunity/react-native-android",flow:"flow","flow-check-android":"flow check --flowconfig-name .flowconfig.android","flow-check-ios":"flow check",format:"npm run prettier && npm run clang-format","format-check":"prettier --list-different \"./**/*.{js,md,yml}\"",lint:"eslint .","lint-ci":"./scripts/circleci/analyze_code.sh && yarn shellcheck",prettier:"prettier --write \"./**/*.{js,md,yml}\"",shellcheck:"./scripts/circleci/analyze_scripts.sh",start:"react-native start",test:"jest","test-android-all":"yarn run docker-build-android && yarn run test-android-run-unit && yarn run test-android-run-instrumentation && yarn run test-android-run-e2e","test-android-e2e":"yarn run docker-build-android && yarn run test-android-run-e2e","test-android-instrumentation":"yarn run docker-build-android && yarn run test-android-run-instrumentation","test-android-run-e2e":"docker run --privileged -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh --android --js","test-android-run-instrumentation":"docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh","test-android-run-unit":"docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-unit-tests.sh","test-android-unit":"yarn run docker-build-android && yarn run test-android-run-unit","test-ci":"jest --maxWorkers=2 --ci --reporters=\"default\" --reporters=\"jest-junit\"","test-ios":"./scripts/objc-test.sh test","test-ios-e2e":"detox test -c ios.sim.release RNTester/e2e"},version:"0.61.1"}},981,[]); -__d(function(g,r,i,a,m,e,d){var n=r(d[0]),o=r(d[1]),t=n(r(d[2]));!(function(n){void 0===n.MediaStream&&(n.MediaStream=o.MediaStream),void 0===n.MediaStreamTrack&&(n.MediaStreamTrack=o.MediaStreamTrack),void 0===n.RTCIceCandidate&&(n.RTCIceCandidate=o.RTCIceCandidate),void 0===n.RTCPeerConnection&&(n.RTCPeerConnection=t.default),void 0===n.RTCPeerConnection&&(n.webkitRTCPeerConnection=t.default),void 0===n.RTCSessionDescription&&(n.RTCSessionDescription=o.RTCSessionDescription);var s=n.navigator;s&&(void 0===s.mediaDevices&&(s.mediaDevices=o.mediaDevices),void 0===s.permissions&&(s.permissions=o.permissions))})(g||window||this)},982,[3,756,983]); -__d(function(g,r,i,a,m,e,d){var t=r(d[0]);Object.defineProperty(e,"__esModule",{value:!0}),e.default=p;var n=t(r(d[1])),o=t(r(d[2])),u=r(d[3]),s=r(d[4]),f=t(r(d[5]));function p(){for(var t=this,n=arguments.length,o=new Array(n),u=0;u=10&&'typ'===c[6]){for(var v=[c[4]],h=!1,y=8;y>18&63)+s.charAt(h>>12&63)+s.charAt(h>>6&63)+s.charAt(63&h);return 2==A?(n=t.charCodeAt(p)<<8,o=t.charCodeAt(++p),l+=s.charAt((h=n+o)>>10)+s.charAt(h>>4&63)+s.charAt(h<<2&63)+'='):1==A&&(h=t.charCodeAt(p),l+=s.charAt(h>>2)+s.charAt(h<<4&63)+'=='),l},decode:function(t){var n=(t=String(t).replace(A,'')).length;n%4==0&&(n=(t=t.replace(/==?$/,'')).length),(n%4==1||/[^+a-zA-Z0-9/]/.test(t))&&f('Invalid character: the string to be decoded is not correctly encoded.');for(var o,c,h=0,l='',p=-1;++p>(-2*h&6)));return l},version:'0.1.0'};if('function'==typeof define&&'object'==typeof define.amd&&define.amd)define(function(){return l});else if(n&&!n.nodeType)if(o)o.exports=l;else for(var p in l)l.hasOwnProperty(p)&&(n[p]=l[p]);else t.base64=l})(this)},986,[]); -__d(function(g,r,i,a,m,e,d){'use strict';!(function(n){function t(n,t,o){var h,l,A,p,E,H,v,S,U,k=0,F=[],R=0,y=!1,B=[],L=[],T=!1,C=!1,Y=-1;if(h=(o=o||{}).encoding||"UTF8",(U=o.numRounds||1)!==parseInt(U,10)||1>U)throw Error("numRounds must a integer >= 1");if("SHA-1"===n)E=512,H=N,v=j,p=160,S=function(n){return n.slice()};else if(0===n.lastIndexOf("SHA-",0))if(H=function(t,o){return z(t,o,n)},v=function(t,o,u,f){var w,s;if("SHA-224"===n||"SHA-256"===n)w=15+(o+65>>>9<<4),s=16;else{if("SHA-384"!==n&&"SHA-512"!==n)throw Error("Unexpected error in SHA-2 implementation");w=31+(o+129>>>10<<5),s=32}for(;t.length<=w;)t.push(0);for(t[o>>>5]|=128<<24-o%32,o+=u,t[w]=4294967295&o,t[w-1]=o/4294967296|0,u=t.length,o=0;ot;t+=1)o[t]=n[t].slice();return o},Y=1,"SHA3-224"===n)E=1152,p=224;else if("SHA3-256"===n)E=1088,p=256;else if("SHA3-384"===n)E=832,p=384;else if("SHA3-512"===n)E=576,p=512;else if("SHAKE128"===n)E=1344,p=-1,x=31,C=!0;else{if("SHAKE256"!==n)throw Error("Chosen SHA variant is not supported");E=1088,p=-1,x=31,C=!0}v=function(n,t,o,u,f){var w,s=x,c=[],b=(o=E)>>>5,h=0,l=t>>>5;for(w=0;w=o;w+=b)u=Z(n.slice(w,w+b),u),t-=o;for(n=n.slice(w),t%=o;n.length>>3)>>2]^=s<=f));)c.push(n.a),0==64*(h+=1)%o&&Z(null,u);return c}}A=b(t,h,Y),l=K(n),this.setHMACKey=function(t,o,u){var f;if(!0===y)throw Error("HMAC key already set");if(!0===T)throw Error("Cannot set HMAC key after calling update");if(!0===C)throw Error("SHAKE is not supported for HMAC");if(t=(o=b(o,h=(u||{}).encoding||"UTF8",Y)(t)).binLen,o=o.value,u=(f=E>>>3)/4-1,ft/8){for(;o.length<=u;)o.push(0);o[u]&=4294967040}for(t=0;t<=u;t+=1)B[t]=909522486^o[t],L[t]=1549556828^o[t];l=H(B,l),k=E,y=!0},this.update=function(n){var t,o,u,f=0,w=E>>>5;for(n=(t=A(n,F,R)).binLen,o=t.value,t=n>>>5,u=0;u>>5),R=n%E,T=!0},this.getHash=function(t,o){var b,h,A,E;if(!0===y)throw Error("Cannot call getHash after setting HMAC key");if(A=c(o),!0===C){if(-1===A.shakeLen)throw Error("shakeLen must be specified in options");p=A.shakeLen}switch(t){case"HEX":b=function(n){return u(n,p,Y,A)};break;case"B64":b=function(n){return f(n,p,Y,A)};break;case"BYTES":b=function(n){return w(n,p,Y)};break;case"ARRAYBUFFER":try{h=new ArrayBuffer(0)}catch(n){throw Error("ARRAYBUFFER not supported by this environment")}b=function(n){return s(n,p,Y)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER")}for(E=v(F.slice(),R,k,S(l),p),h=1;h>>24-p%32),E=v(E,p,0,K(n),p);return b(E)},this.getHMAC=function(t,o){var b,h,A,U;if(!1===y)throw Error("Cannot call getHMAC without first setting HMAC key");switch(A=c(o),t){case"HEX":b=function(n){return u(n,p,Y,A)};break;case"B64":b=function(n){return f(n,p,Y,A)};break;case"BYTES":b=function(n){return w(n,p,Y)};break;case"ARRAYBUFFER":try{b=new ArrayBuffer(0)}catch(n){throw Error("ARRAYBUFFER not supported by this environment")}b=function(n){return s(n,p,Y)};break;default:throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER")}return h=v(F.slice(),R,k,S(l),p),U=H(L,K(n)),b(U=v(h,p,E,U,p))}}function o(n,t){this.a=n,this.b=t}function u(n,t,o,u){var f,w,s,c="";for(t/=8,s=-1===o?3:0,f=0;f>>2]>>>8*(s+f%4*o),c+="0123456789abcdef".charAt(w>>>4&15)+"0123456789abcdef".charAt(15&w);return u.outputUpper?c.toUpperCase():c}function f(n,t,o,u){var f,w,s,c,b="",h=t/8;for(c=-1===o?3:0,f=0;f>>2]:0,s=f+2>>2]:0,s=(n[f>>>2]>>>8*(c+f%4*o)&255)<<16|(w>>>8*(c+(f+1)%4*o)&255)<<8|s>>>8*(c+(f+2)%4*o)&255,w=0;4>w;w+=1)b+=8*f+6*w<=t?"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(s>>>6*(3-w)&63):u.b64Pad;return b}function w(n,t,o){var u,f,w,s="";for(t/=8,w=-1===o?3:0,u=0;u>>2]>>>8*(w+u%4*o)&255,s+=String.fromCharCode(f);return s}function s(n,t,o){t/=8;var u,f,w,s=new ArrayBuffer(t);for(w=new Uint8Array(s),f=-1===o?3:0,u=0;u>>2]>>>8*(f+u%4*o)&255;return s}function c(n){var t={outputUpper:!1,b64Pad:"=",shakeLen:-1};if(n=n||{},t.outputUpper=n.outputUpper||!1,!0===n.hasOwnProperty("b64Pad")&&(t.b64Pad=n.b64Pad),!0===n.hasOwnProperty("shakeLen")){if(0!=n.shakeLen%8)throw Error("shakeLen must be a multiple of 8");t.shakeLen=n.shakeLen}if("boolean"!=typeof t.outputUpper)throw Error("Invalid outputUpper formatting option");if("string"!=typeof t.b64Pad)throw Error("Invalid b64Pad formatting option");return t}function b(n,t,o){switch(t){case"UTF8":case"UTF16BE":case"UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE")}switch(n){case"HEX":n=function(n,t,u){var f,w,s,c,b,h,l=n.length;if(0!=l%2)throw Error("String of HEX type must be in byte increments");for(t=t||[0],b=(u=u||0)>>>3,h=-1===o?3:0,f=0;f>>1)+b)>>>2;t.length<=s;)t.push(0);t[s]|=w<<8*(h+c%4*o)}return{value:t,binLen:4*l+u}};break;case"TEXT":n=function(n,u,f){var w,s,c,b,h,l,A,p,E=0;if(u=u||[0],h=(f=f||0)>>>3,"UTF8"===t)for(p=-1===o?3:0,c=0;c(w=n.charCodeAt(c))?s.push(w):2048>w?(s.push(192|w>>>6),s.push(128|63&w)):55296>w||57344<=w?s.push(224|w>>>12,128|w>>>6&63,128|63&w):(c+=1,w=65536+((1023&w)<<10|1023&n.charCodeAt(c)),s.push(240|w>>>18,128|w>>>12&63,128|w>>>6&63,128|63&w)),b=0;b>>2;u.length<=l;)u.push(0);u[l]|=s[b]<<8*(p+A%4*o),E+=1}else if("UTF16BE"===t||"UTF16LE"===t)for(p=-1===o?2:0,s="UTF16LE"===t&&1!==o||"UTF16LE"!==t&&1===o,c=0;c>>8),l=(A=E+h)>>>2;u.length<=l;)u.push(0);u[l]|=w<<8*(p+A%4*o),E+=2}return{value:u,binLen:8*E+f}};break;case"B64":n=function(n,t,u){var f,w,s,c,b,h,l,A,p=0;if(-1===n.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");if(w=n.indexOf("="),n=n.replace(/\=/g,""),-1!==w&&w0;){var t=e.shift();if("function"==typeof t){var n=e.shift(),r=e.shift();t.call(n,r)}else t._settlePromises()}},i.prototype._drainQueues=function(){this._drainQueue(this._normalQueue),this._reset(),this._haveDrainedQueues=!0,this._drainQueue(this._lateQueue)},i.prototype._queueTick=function(){this._isTickUsed||(this._isTickUsed=!0,this._schedule(this.drainQueues))},i.prototype._reset=function(){this._isTickUsed=!1},n.exports=i,n.exports.firstLineError=c},{"./queue":26,"./schedule":29,"./util":36}],3:[function(e,t,n){"use strict";t.exports=function(e,t,n,r){var i=!1,a=function(e,t){this._reject(t)},o=function(e,t){t.promiseRejectionQueued=!0,t.bindingPromise._then(a,a,null,this,e)},s=function(e,t){0==(50397184&this._bitField)&&this._resolveCallback(t.target)},c=function(e,t){t.promiseRejectionQueued||this._reject(e)};e.prototype.bind=function(a){i||(i=!0,e.prototype._propagateFrom=r.propagateFromFunction(),e.prototype._boundValue=r.boundValueFunction());var u=n(a),l=new e(t);l._propagateFrom(this,1);var f=this._target();if(l._setBoundTo(u),u instanceof e){var d={promiseRejectionQueued:!1,promise:l,target:f,bindingPromise:u};f._then(t,o,void 0,l,d),u._then(s,c,void 0,l,d),l._setOnCancel(u)}else l._resolveCallback(f);return l},e.prototype._setBoundTo=function(e){void 0!==e?(this._bitField=2097152|this._bitField,this._boundTo=e):this._bitField=-2097153&this._bitField},e.prototype._isBound=function(){return 2097152==(2097152&this._bitField)},e.bind=function(t,n){return e.resolve(n).bind(t)}}},{}],4:[function(e,t,n){"use strict";var r;"undefined"!=typeof Promise&&(r=Promise);var i=e("./promise")();i.noConflict=function(){try{Promise===i&&(Promise=r)}catch(e){}return i},t.exports=i},{"./promise":22}],5:[function(e,t,n){"use strict";var r=Object.create;if(r){var i=r(null),a=r(null);i[" size"]=a[" size"]=0}t.exports=function(t){function n(e,n){var r;if(null!=e&&(r=e[n]),"function"!=typeof r){var i="Object "+o.classString(e)+" has no method '"+o.toString(n)+"'";throw new t.TypeError(i)}return r}function r(e){return n(e,this.pop()).apply(e,this)}function i(e){return e[this]}function a(e){var t=+this;return t<0&&(t=Math.max(0,t+e.length)),e[t]}var o=e("./util"),s=o.canEvaluate;o.isIdentifier,t.prototype.call=function(e){var t=[].slice.call(arguments,1);return t.push(e),this._then(r,void 0,void 0,t,void 0)},t.prototype.get=function(e){var t;if("number"==typeof e)t=a;else if(s){var n=(void 0)(e);t=null!==n?n:i}else t=i;return this._then(t,void 0,void 0,e,void 0)}}},{"./util":36}],6:[function(e,t,n){"use strict";t.exports=function(t,n,r,i){var a=e("./util"),o=a.tryCatch,s=a.errorObj,c=t._async;t.prototype.break=t.prototype.cancel=function(){if(!i.cancellation())return this._warn("cancellation is disabled");for(var e=this,t=e;e._isCancellable();){if(!e._cancelBy(t)){t._isFollowing()?t._followee().cancel():t._cancelBranched();break}var n=e._cancellationParent;if(null==n||!n._isCancellable()){e._isFollowing()?e._followee().cancel():e._cancelBranched();break}e._isFollowing()&&e._followee().cancel(),e._setWillBeCancelled(),t=e,e=n}},t.prototype._branchHasCancelled=function(){this._branchesRemainingToCancel--},t.prototype._enoughBranchesHaveCancelled=function(){return void 0===this._branchesRemainingToCancel||this._branchesRemainingToCancel<=0},t.prototype._cancelBy=function(e){return e===this?(this._branchesRemainingToCancel=0,this._invokeOnCancel(),!0):(this._branchHasCancelled(),!!this._enoughBranchesHaveCancelled()&&(this._invokeOnCancel(),!0))},t.prototype._cancelBranched=function(){this._enoughBranchesHaveCancelled()&&this._cancel()},t.prototype._cancel=function(){this._isCancellable()&&(this._setCancelled(),c.invoke(this._cancelPromises,this,void 0))},t.prototype._cancelPromises=function(){this._length()>0&&this._settlePromises()},t.prototype._unsetOnCancel=function(){this._onCancelField=void 0},t.prototype._isCancellable=function(){return this.isPending()&&!this._isCancelled()},t.prototype.isCancellable=function(){return this.isPending()&&!this.isCancelled()},t.prototype._doInvokeOnCancel=function(e,t){if(a.isArray(e))for(var n=0;n=0)return i[e]}var r=!1,i=[];return e.prototype._promiseCreated=function(){},e.prototype._pushContext=function(){},e.prototype._popContext=function(){return null},e._peekContext=e.prototype._peekContext=function(){},t.prototype._pushContext=function(){void 0!==this._trace&&(this._trace._promiseCreated=null,i.push(this._trace))},t.prototype._popContext=function(){if(void 0!==this._trace){var e=i.pop(),t=e._promiseCreated;return e._promiseCreated=null,t}return null},t.CapturedTrace=null,t.create=function(){if(r)return new t},t.deactivateLongStackTraces=function(){},t.activateLongStackTraces=function(){var i=e.prototype._pushContext,a=e.prototype._popContext,o=e._peekContext,s=e.prototype._peekContext,c=e.prototype._promiseCreated;t.deactivateLongStackTraces=function(){e.prototype._pushContext=i,e.prototype._popContext=a,e._peekContext=o,e.prototype._peekContext=s,e.prototype._promiseCreated=c,r=!1},r=!0,e.prototype._pushContext=t.prototype._pushContext,e.prototype._popContext=t.prototype._popContext,e._peekContext=e.prototype._peekContext=n,e.prototype._promiseCreated=function(){var e=this._peekContext();e&&null==e._promiseCreated&&(e._promiseCreated=this)}},t}},{}],9:[function(t,n,r){"use strict";n.exports=function(n,r){function i(e,t){return{promise:t}}function a(){return!1}function o(e,t,n){var r=this;try{e(t,n,function(e){if("function"!=typeof e)throw new TypeError("onCancel must be a function, got: "+x.toString(e));r._attachCancellationCallback(e)})}catch(e){return e}}function s(e){if(!this._isCancellable())return this;var t=this._onCancel();void 0!==t?x.isArray(t)?t.push(e):this._setOnCancel([t,e]):this._setOnCancel(e)}function c(){return this._onCancelField}function u(e){this._onCancelField=e}function l(){this._cancellationParent=void 0,this._onCancelField=void 0}function f(e,t){if(0!=(1&t)){this._cancellationParent=e;var n=e._branchesRemainingToCancel;void 0===n&&(n=0),e._branchesRemainingToCancel=n+1}0!=(2&t)&&e._isBound()&&this._setBoundTo(e._boundTo)}function d(){var e=this._boundTo;return void 0!==e&&e instanceof n?e.isFulfilled()?e.value():void 0:e}function h(){this._trace=new I(this._peekContext())}function p(e,t){if(D(e)){var n=this._trace;if(void 0!==n&&t&&(n=n._parent),void 0!==n)n.attachExtraTrace(e);else if(!e.__stackCleaned__){var r=k(e);x.notEnumerableProp(e,"stack",r.message+"\n"+r.stack.join("\n")),x.notEnumerableProp(e,"__stackCleaned__",!0)}}}function v(e,t,r){if(ne.warnings){var i,a=new A(e);if(t)r._attachExtraTrace(a);else if(ne.longStackTraces&&(i=n._peekContext()))i.attachExtraTrace(a);else{var o=k(a);a.stack=o.message+"\n"+o.stack.join("\n")}$("warning",a)||w(a,"",!0)}}function g(e,t){for(var n=0;n=0;--s)if(r[s]===a){o=s;break}for(s=o;s>=0;--s){var c=r[s];if(t[i]!==c)break;t.pop(),i--}t=r}}function b(e){for(var t=[],n=0;n0&&"SyntaxError"!=e.name&&(t=t.slice(n)),t}function k(e){var t=e.stack,n=e.toString();return t="string"==typeof t&&t.length>0?S(e):[" (No stack trace)"],{message:n,stack:"SyntaxError"==e.name?t:b(t)}}function w(e,t,n){if("undefined"!=typeof console){var r;if(x.isObject(e)){var i=e.stack;r=t+N(i,e)}else r=t+String(e);"function"==typeof M?M(r,n):"function"!=typeof console.log&&"object"!=typeof console.log||console.log(r)}}function C(e,t,n,r){var i=!1;try{"function"==typeof t&&(i=!0,"rejectionHandled"===e?t(r):t(n,r))}catch(e){j.throwLater(e)}"unhandledRejection"===e?$(e,n,r)||i||w(n,"Unhandled rejection "):$(e,r)}function _(e){var t;if("function"==typeof e)t="[function "+(e.name||"anonymous")+"]";else{if(t=e&&"function"==typeof e.toString?e.toString():x.toString(e),/\[object [a-zA-Z0-9$_]+\]/.test(t))try{t=JSON.stringify(e)}catch(e){}0===t.length&&(t="(empty array)")}return"(<"+T(t)+">, no stack trace)"}function T(e){return e.length<41?e:e.substr(0,38)+"..."}function P(){return"function"==typeof te}function E(e){var t=e.match(ee);if(t)return{fileName:t[1],line:parseInt(t[2],10)}}function I(e){this._parent=e,this._promisesCreated=0;var t=this._length=1+(void 0===e?0:e._length);te(this,I),t>32&&this.uncycle()}var R,O,M,F=n._getDomain,j=n._async,A=t("./errors").Warning,x=t("./util"),D=x.canAttachTrace,U=/[\\\/]bluebird[\\\/]js[\\\/](release|debug|instrumented)/,L=/\((?:timers\.js):\d+:\d+\)/,B=/[\/<\(](.+?):(\d+):(\d+)\)?\s*$/,H=null,N=null,V=!1,W=!(0==x.env("BLUEBIRD_DEBUG")),q=!(0==x.env("BLUEBIRD_WARNINGS")||!W&&!x.env("BLUEBIRD_WARNINGS")),G=!(0==x.env("BLUEBIRD_LONG_STACK_TRACES")||!W&&!x.env("BLUEBIRD_LONG_STACK_TRACES")),z=0!=x.env("BLUEBIRD_W_FORGOTTEN_RETURN")&&(q||!!x.env("BLUEBIRD_W_FORGOTTEN_RETURN"));n.prototype.suppressUnhandledRejections=function(){var e=this._target();e._bitField=-1048577&e._bitField|524288},n.prototype._ensurePossibleRejectionHandled=function(){if(0==(524288&this._bitField)){this._setRejectionIsUnhandled();var e=this;setTimeout(function(){e._notifyUnhandledRejection()},1)}},n.prototype._notifyUnhandledRejectionIsHandled=function(){C("rejectionHandled",R,void 0,this)},n.prototype._setReturnedNonUndefined=function(){this._bitField=268435456|this._bitField},n.prototype._returnedNonUndefined=function(){return 0!=(268435456&this._bitField)},n.prototype._notifyUnhandledRejection=function(){if(this._isRejectionUnhandled()){var e=this._settledValue();this._setUnhandledRejectionIsNotified(),C("unhandledRejection",O,e,this)}},n.prototype._setUnhandledRejectionIsNotified=function(){this._bitField=262144|this._bitField},n.prototype._unsetUnhandledRejectionIsNotified=function(){this._bitField=-262145&this._bitField},n.prototype._isUnhandledRejectionNotified=function(){return(262144&this._bitField)>0},n.prototype._setRejectionIsUnhandled=function(){this._bitField=1048576|this._bitField},n.prototype._unsetRejectionIsUnhandled=function(){this._bitField=-1048577&this._bitField,this._isUnhandledRejectionNotified()&&(this._unsetUnhandledRejectionIsNotified(),this._notifyUnhandledRejectionIsHandled())},n.prototype._isRejectionUnhandled=function(){return(1048576&this._bitField)>0},n.prototype._warn=function(e,t,n){return v(e,t,n||this)},n.onPossiblyUnhandledRejection=function(e){var t=F();O="function"==typeof e?null===t?e:x.domainBind(t,e):void 0},n.onUnhandledRejectionHandled=function(e){var t=F();R="function"==typeof e?null===t?e:x.domainBind(t,e):void 0};var Q=function(){};n.longStackTraces=function(){if(j.haveItemsQueued()&&!ne.longStackTraces)throw new Error("cannot enable long stack traces after promises have been created\n\n See http://goo.gl/MqrFmX\n");if(!ne.longStackTraces&&P()){var e=n.prototype._captureStackTrace,t=n.prototype._attachExtraTrace;ne.longStackTraces=!0,Q=function(){if(j.haveItemsQueued()&&!ne.longStackTraces)throw new Error("cannot enable long stack traces after promises have been created\n\n See http://goo.gl/MqrFmX\n");n.prototype._captureStackTrace=e,n.prototype._attachExtraTrace=t,r.deactivateLongStackTraces(),j.enableTrampoline(),ne.longStackTraces=!1},n.prototype._captureStackTrace=h,n.prototype._attachExtraTrace=p,r.activateLongStackTraces(),j.disableTrampolineIfNecessary()}},n.hasLongStackTraces=function(){return ne.longStackTraces&&P()};var J=(function(){try{if("function"==typeof CustomEvent){var e=new CustomEvent("CustomEvent");return x.global.dispatchEvent(e),function(e,t){var n=new CustomEvent(e.toLowerCase(),{detail:t,cancelable:!0});return!x.global.dispatchEvent(n)}}if("function"==typeof Event){e=new Event("CustomEvent");return x.global.dispatchEvent(e),function(e,t){var n=new Event(e.toLowerCase(),{cancelable:!0});return n.detail=t,!x.global.dispatchEvent(n)}}return(e=document.createEvent("CustomEvent")).initCustomEvent("testingtheevent",!1,!0,{}),x.global.dispatchEvent(e),function(e,t){var n=document.createEvent("CustomEvent");return n.initCustomEvent(e.toLowerCase(),!1,!0,t),!x.global.dispatchEvent(n)}}catch(e){}return function(){return!1}})(),K=x.isNode?function(){return e.emit.apply(e,arguments)}:x.global?function(e){var t="on"+e.toLowerCase(),n=x.global[t];return!!n&&(n.apply(x.global,[].slice.call(arguments,1)),!0)}:function(){return!1},X={promiseCreated:i,promiseFulfilled:i,promiseRejected:i,promiseResolved:i,promiseCancelled:i,promiseChained:function(e,t,n){return{promise:t,child:n}},warning:function(e,t){return{warning:t}},unhandledRejection:function(e,t,n){return{reason:t,promise:n}},rejectionHandled:i},$=function(e){var t=!1;try{t=K.apply(null,arguments)}catch(e){j.throwLater(e),t=!0}var n=!1;try{n=J(e,X[e].apply(null,arguments))}catch(e){j.throwLater(e),n=!0}return n||t};n.config=function(e){if("longStackTraces"in(e=Object(e))&&(e.longStackTraces?n.longStackTraces():!e.longStackTraces&&n.hasLongStackTraces()&&Q()),"warnings"in e){var t=e.warnings;ne.warnings=!!t,z=ne.warnings,x.isObject(t)&&"wForgottenReturn"in t&&(z=!!t.wForgottenReturn)}if("cancellation"in e&&e.cancellation&&!ne.cancellation){if(j.haveItemsQueued())throw new Error("cannot enable cancellation after promises are in use");n.prototype._clearCancellationData=l,n.prototype._propagateFrom=f,n.prototype._onCancel=c,n.prototype._setOnCancel=u,n.prototype._attachCancellationCallback=s,n.prototype._execute=o,Y=f,ne.cancellation=!0}return"monitoring"in e&&(e.monitoring&&!ne.monitoring?(ne.monitoring=!0,n.prototype._fireEvent=$):!e.monitoring&&ne.monitoring&&(ne.monitoring=!1,n.prototype._fireEvent=a)),n},n.prototype._fireEvent=a,n.prototype._execute=function(e,t,n){try{e(t,n)}catch(e){return e}},n.prototype._onCancel=function(){},n.prototype._setOnCancel=function(e){},n.prototype._attachCancellationCallback=function(e){},n.prototype._captureStackTrace=function(){},n.prototype._attachExtraTrace=function(){},n.prototype._clearCancellationData=function(){},n.prototype._propagateFrom=function(e,t){};var Y=function(e,t){0!=(2&t)&&e._isBound()&&this._setBoundTo(e._boundTo)},Z=function(){return!1},ee=/[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;x.inherits(I,Error),r.CapturedTrace=I,I.prototype.uncycle=function(){var e=this._length;if(!(e<2)){for(var t=[],n={},r=0,i=this;void 0!==i;++r)t.push(i),i=i._parent;for(r=(e=this._length=r)-1;r>=0;--r){var a=t[r].stack;void 0===n[a]&&(n[a]=r)}for(r=0;r0&&(t[o-1]._parent=void 0,t[o-1]._length=1),t[r]._parent=void 0,t[r]._length=1;var s=r>0?t[r-1]:this;o=0;--u)t[u]._length=c,c++;return}}}},I.prototype.attachExtraTrace=function(e){if(!e.__stackCleaned__){this.uncycle();for(var t=k(e),n=t.message,r=[t.stack],i=this;void 0!==i;)r.push(b(i.stack.split("\n"))),i=i._parent;m(r),y(r),x.notEnumerableProp(e,"stack",g(n,r)),x.notEnumerableProp(e,"__stackCleaned__",!0)}};var te=(function(){var e=/^\s*at\s*/,t=function(e,t){return"string"==typeof e?e:void 0!==t.name&&void 0!==t.message?t.toString():_(t)};if("number"==typeof Error.stackTraceLimit&&"function"==typeof Error.captureStackTrace){Error.stackTraceLimit+=6,H=e,N=t;var n=Error.captureStackTrace;return Z=function(e){return U.test(e)},function(e,t){Error.stackTraceLimit+=6,n(e,t),Error.stackTraceLimit-=6}}var r,i=new Error;if("string"==typeof i.stack&&i.stack.split("\n")[0].indexOf("stackDetection@")>=0)return H=/@/,N=t,V=!0,function(e){e.stack=(new Error).stack};try{throw new Error}catch(e){r="stack"in e}return"stack"in i||!r||"number"!=typeof Error.stackTraceLimit?(N=function(e,t){return"string"==typeof e?e:"object"!=typeof t&&"function"!=typeof t||void 0===t.name||void 0===t.message?_(t):t.toString()},null):(H=e,N=t,function(e){Error.stackTraceLimit+=6;try{throw new Error}catch(t){e.stack=t.stack}Error.stackTraceLimit-=6})})();"undefined"!=typeof console&&void 0!==console.warn&&(M=function(e){console.warn(e)},x.isNode&&e.stderr.isTTY?M=function(e,t){var n=t?"\x1b[33m":"\x1b[31m";console.warn(n+e+"\x1b[0m\n")}:x.isNode||"string"!=typeof(new Error).stack||(M=function(e,t){console.warn("%c"+e,t?"color: darkorange":"color: red")}));var ne={warnings:q,longStackTraces:!1,cancellation:!1,monitoring:!1};return G&&n.longStackTraces(),{longStackTraces:function(){return ne.longStackTraces},warnings:function(){return ne.warnings},cancellation:function(){return ne.cancellation},monitoring:function(){return ne.monitoring},propagateFromFunction:function(){return Y},boundValueFunction:function(){return d},checkForgottenReturns:function(e,t,n,r,i){if(void 0===e&&null!==t&&z){if(void 0!==i&&i._returnedNonUndefined())return;if(0==(65535&r._bitField))return;n&&(n+=" ");var a="",o="";if(t._trace){for(var s=t._trace.stack.split("\n"),c=b(s),u=c.length-1;u>=0;--u){var l=c[u];if(!L.test(l)){var f=l.match(B);f&&(a="at "+f[1]+":"+f[2]+":"+f[3]+" ");break}}if(c.length>0){var d=c[0];for(u=0;u0&&(o="\n"+s[u-1]);break}}}var h="a promise was created in a "+n+"handler "+a+"but was not returned from it, see http://goo.gl/rRqMUw"+o;r._warn(h,!0,t)}},setBounds:function(e,t){if(P()){for(var n,r,i=e.stack.split("\n"),a=t.stack.split("\n"),o=-1,s=-1,c=0;c=s||(Z=function(e){if(U.test(e))return!0;var t=E(e);return!!(t&&t.fileName===n&&o<=t.line&&t.line<=s)})}},warn:v,deprecated:function(e,t){var n=e+" is deprecated and will be removed in a future version.";return t&&(n+=" Use "+t+" instead."),v(n)},CapturedTrace:I,fireDomEvent:J,fireGlobalEvent:K}}},{"./errors":12,"./util":36}],10:[function(e,t,n){"use strict";t.exports=function(e){function t(){return this.value}function n(){throw this.reason}e.prototype.return=e.prototype.thenReturn=function(n){return n instanceof e&&n.suppressUnhandledRejections(),this._then(t,void 0,void 0,{value:n},void 0)},e.prototype.throw=e.prototype.thenThrow=function(e){return this._then(n,void 0,void 0,{reason:e},void 0)},e.prototype.catchThrow=function(e){if(arguments.length<=1)return this._then(void 0,n,void 0,{reason:e},void 0);var t=arguments[1];return this.caught(e,function(){throw t})},e.prototype.catchReturn=function(n){if(arguments.length<=1)return n instanceof e&&n.suppressUnhandledRejections(),this._then(void 0,t,void 0,{value:n},void 0);var r=arguments[1];r instanceof e&&r.suppressUnhandledRejections();return this.caught(n,function(){return r})}}},{}],11:[function(e,t,n){"use strict";t.exports=function(e,t){function n(){return i(this)}var r=e.reduce,i=e.all;e.prototype.each=function(e){return r(this,e,t,0)._then(n,void 0,void 0,this,void 0)},e.prototype.mapSeries=function(e){return r(this,e,t,t)},e.each=function(e,i){return r(e,i,t,0)._then(n,void 0,void 0,e,void 0)},e.mapSeries=function(e,n){return r(e,n,t,t)}}},{}],12:[function(e,t,n){"use strict";function r(e,t){function n(r){if(!(this instanceof n))return new n(r);f(this,"message","string"==typeof r?r:t),f(this,"name",e),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):Error.call(this)}return l(n,Error),n}function i(e){if(!(this instanceof i))return new i(e);f(this,"name","OperationalError"),f(this,"message",e),this.cause=e,this.isOperational=!0,e instanceof Error?(f(this,"message",e.message),f(this,"stack",e.stack)):Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}var a,o,s=e("./es5"),c=s.freeze,u=e("./util"),l=u.inherits,f=u.notEnumerableProp,d=r("Warning","warning"),h=r("CancellationError","cancellation error"),p=r("TimeoutError","timeout error"),v=r("AggregateError","aggregate error");try{a=TypeError,o=RangeError}catch(e){a=r("TypeError","type error"),o=r("RangeError","range error")}for(var g="join pop push shift unshift slice filter forEach some every map indexOf lastIndexOf reduce reduceRight sort reverse".split(" "),y=0;y1?e.cancelPromise._reject(t):e.cancelPromise._cancel(),e.cancelPromise=null,!0)}function s(){return u.call(this,this.promise._target()._settledValue())}function c(e){if(!o(this,e))return d.e=e,d}function u(e){var i=this.promise,u=this.handler;if(!this.called){this.called=!0;var l=this.isFinallyHandler()?u.call(i._boundValue()):u.call(i._boundValue(),e);if(l===r)return l;if(void 0!==l){i._setReturnedNonUndefined();var h=n(l,i);if(h instanceof t){if(null!=this.cancelPromise){if(h._isCancelled()){var p=new f("late cancellation observer");return i._attachExtraTrace(p),d.e=p,d}h.isPending()&&h._attachCancellationCallback(new a(this))}return h._then(s,c,void 0,this,void 0)}}}return i.isRejected()?(o(this),d.e=e,d):(o(this),e)}var l=e("./util"),f=t.CancellationError,d=l.errorObj,h=e("./catch_filter")(r);return i.prototype.isFinallyHandler=function(){return 0===this.type},a.prototype._resultCancelled=function(){o(this.finallyHandler)},t.prototype._passThrough=function(e,t,n,r){return"function"!=typeof e?this.then():this._then(n,r,void 0,new i(this,t,e),void 0)},t.prototype.lastly=t.prototype.finally=function(e){return this._passThrough(e,0,u,u)},t.prototype.tap=function(e){return this._passThrough(e,1,u)},t.prototype.tapCatch=function(e){var n=arguments.length;if(1===n)return this._passThrough(e,1,void 0,u);var r,i=new Array(n-1),a=0;for(r=0;r0&&"function"==typeof arguments[t]&&(e=arguments[t]);var r=[].slice.call(arguments);e&&r.pop();var i=new n(r).promise();return void 0!==e?i.spread(e):i}}},{"./util":36}],18:[function(e,t,n){"use strict";t.exports=function(t,n,r,i,a,o){function s(e,t,n,r){this.constructor$(e),this._promise._captureStackTrace();var i=u();this._callback=null===i?t:l.domainBind(i,t),this._preservedValues=r===a?new Array(this.length()):null,this._limit=n,this._inFlight=0,this._queue=[],h.invoke(this._asyncInit,this,void 0)}function c(e,n,i,a){if("function"!=typeof n)return r("expecting a function but got "+l.classString(n));var o=0;if(void 0!==i){if("object"!=typeof i||null===i)return t.reject(new TypeError("options argument must be an object but it is "+l.classString(i)));if("number"!=typeof i.concurrency)return t.reject(new TypeError("'concurrency' must be a number but it is "+l.classString(i.concurrency)));o=i.concurrency}return new s(e,n,o="number"==typeof o&&isFinite(o)&&o>=1?o:0,a).promise()}var u=t._getDomain,l=e("./util"),f=l.tryCatch,d=l.errorObj,h=t._async;l.inherits(s,n),s.prototype._asyncInit=function(){this._init$(void 0,-2)},s.prototype._init=function(){},s.prototype._promiseFulfilled=function(e,n){var r=this._values,a=this.length(),s=this._preservedValues,c=this._limit;if(n<0){if(r[n=-1*n-1]=e,c>=1&&(this._inFlight--,this._drainQueue(),this._isResolved()))return!0}else{if(c>=1&&this._inFlight>=c)return r[n]=e,this._queue.push(n),!1;null!==s&&(s[n]=e);var u=this._promise,l=this._callback,h=u._boundValue();u._pushContext();var p=f(l).call(h,e,n,a),v=u._popContext();if(o.checkForgottenReturns(p,v,null!==s?"Promise.filter":"Promise.map",u),p===d)return this._reject(p.e),!0;var g=i(p,this._promise);if(g instanceof t){var y=(g=g._target())._bitField;if(0==(50397184&y))return c>=1&&this._inFlight++,r[n]=g,g._proxy(this,-1*(n+1)),!1;if(0==(33554432&y))return 0!=(16777216&y)?(this._reject(g._reason()),!0):(this._cancel(),!0);p=g._value()}r[n]=p}return++this._totalResolved>=a&&(null!==s?this._filter(r,s):this._resolve(r),!0)},s.prototype._drainQueue=function(){for(var e=this._queue,t=this._limit,n=this._values;e.length>0&&this._inFlight1){a.deprecated("calling Promise.try with more than 1 argument");var u=arguments[1],l=arguments[2];r=o.isArray(u)?s(e).apply(l,u):s(e).call(l,u)}else r=s(e)();var f=c._popContext();return a.checkForgottenReturns(r,f,"Promise.try",c),c._resolveFromSyncValue(r),c},t.prototype._resolveFromSyncValue=function(e){e===o.errorObj?this._rejectCallback(e.e,!1):this._resolveCallback(e,!0)}}},{"./util":36}],20:[function(e,t,n){"use strict";function r(e){return e instanceof Error&&c.getPrototypeOf(e)===Error.prototype}function i(e){var t;if(r(e)){(t=new s(e)).name=e.name,t.message=e.message,t.stack=e.stack;for(var n=c.keys(e),i=0;i1){var n,r=new Array(t-1),i=0;for(n=0;n0&&"function"!=typeof e&&"function"!=typeof t){var n=".then() only accepts functions but was passed: "+p.classString(e);arguments.length>1&&(n+=", "+p.classString(t)),this._warn(n)}return this._then(e,t,void 0,void 0,void 0)},a.prototype.done=function(e,t){this._then(e,t,void 0,void 0,void 0)._setIsFinal()},a.prototype.spread=function(e){return"function"!=typeof e?d("expecting a function but got "+p.classString(e)):this.all()._then(e,void 0,void 0,w,void 0)},a.prototype.toJSON=function(){var e={isFulfilled:!1,isRejected:!1,fulfillmentValue:void 0,rejectionReason:void 0};return this.isFulfilled()?(e.fulfillmentValue=this.value(),e.isFulfilled=!0):this.isRejected()&&(e.rejectionReason=this.reason(),e.isRejected=!0),e},a.prototype.all=function(){return arguments.length>0&&this._warn(".all() was passed arguments but it does not take any"),new T(this).promise()},a.prototype.error=function(e){return this.caught(p.originatesFromRejection,e)},a.getNewLibraryCopy=n.exports,a.is=function(e){return e instanceof a},a.fromNode=a.fromCallback=function(e){var t=new a(k);t._captureStackTrace();var n=arguments.length>1&&!!Object(arguments[1]).multiArgs,r=j(e)(M(t,n));return r===F&&t._rejectCallback(r.e,!0),t._isFateSealed()||t._setAsyncGuaranteed(),t},a.all=function(e){return new T(e).promise()},a.cast=function(e){var t=_(e);return t instanceof a||((t=new a(k))._captureStackTrace(),t._setFulfilled(),t._rejectionHandler0=e),t},a.resolve=a.fulfilled=a.cast,a.reject=a.rejected=function(e){var t=new a(k);return t._captureStackTrace(),t._rejectCallback(e,!0),t},a.setScheduler=function(e){if("function"!=typeof e)throw new b("expecting a function but got "+p.classString(e));return y.setScheduler(e)},a.prototype._then=function(e,t,n,r,i){var o=void 0!==i,s=o?i:new a(k),c=this._target(),l=c._bitField;o||(s._propagateFrom(this,3),s._captureStackTrace(),void 0===r&&0!=(2097152&this._bitField)&&(r=0!=(50397184&l)?this._boundValue():c===this?void 0:this._boundTo),this._fireEvent("promiseChained",this,s));var f=u();if(0!=(50397184&l)){var d,h,v=c._settlePromiseCtx;0!=(33554432&l)?(h=c._rejectionHandler0,d=e):0!=(16777216&l)?(h=c._fulfillmentHandler0,d=t,c._unsetRejectionIsUnhandled()):(v=c._settlePromiseLateCancellationObserver,h=new S("late cancellation observer"),c._attachExtraTrace(h),d=t),y.invoke(v,c,{handler:null===f?d:"function"==typeof d&&p.domainBind(f,d),promise:s,receiver:r,value:h})}else c._addCallbacks(e,t,s,r,f);return s},a.prototype._length=function(){return 65535&this._bitField},a.prototype._isFateSealed=function(){return 0!=(117506048&this._bitField)},a.prototype._isFollowing=function(){return 67108864==(67108864&this._bitField)},a.prototype._setLength=function(e){this._bitField=-65536&this._bitField|65535&e},a.prototype._setFulfilled=function(){this._bitField=33554432|this._bitField,this._fireEvent("promiseFulfilled",this)},a.prototype._setRejected=function(){this._bitField=16777216|this._bitField,this._fireEvent("promiseRejected",this)},a.prototype._setFollowing=function(){this._bitField=67108864|this._bitField,this._fireEvent("promiseResolved",this)},a.prototype._setIsFinal=function(){this._bitField=4194304|this._bitField},a.prototype._isFinal=function(){return(4194304&this._bitField)>0},a.prototype._unsetCancelled=function(){this._bitField=-65537&this._bitField},a.prototype._setCancelled=function(){this._bitField=65536|this._bitField,this._fireEvent("promiseCancelled",this)},a.prototype._setWillBeCancelled=function(){this._bitField=8388608|this._bitField},a.prototype._setAsyncGuaranteed=function(){y.hasCustomScheduler()||(this._bitField=134217728|this._bitField)},a.prototype._receiverAt=function(e){var t=0===e?this._receiver0:this[4*e-4+3];if(t!==h)return void 0===t&&this._isBound()?this._boundValue():t},a.prototype._promiseAt=function(e){return this[4*e-4+2]},a.prototype._fulfillmentHandlerAt=function(e){return this[4*e-4+0]},a.prototype._rejectionHandlerAt=function(e){return this[4*e-4+1]},a.prototype._boundValue=function(){},a.prototype._migrateCallback0=function(e){var t=(e._bitField,e._fulfillmentHandler0),n=e._rejectionHandler0,r=e._promise0,i=e._receiverAt(0);void 0===i&&(i=h),this._addCallbacks(t,n,r,i,null)},a.prototype._migrateCallbackAt=function(e,t){var n=e._fulfillmentHandlerAt(t),r=e._rejectionHandlerAt(t),i=e._promiseAt(t),a=e._receiverAt(t);void 0===a&&(a=h),this._addCallbacks(n,r,i,a,null)},a.prototype._addCallbacks=function(e,t,n,r,i){var a=this._length();if(a>=65531&&(a=0,this._setLength(0)),0===a)this._promise0=n,this._receiver0=r,"function"==typeof e&&(this._fulfillmentHandler0=null===i?e:p.domainBind(i,e)),"function"==typeof t&&(this._rejectionHandler0=null===i?t:p.domainBind(i,t));else{var o=4*a-4;this[o+2]=n,this[o+3]=r,"function"==typeof e&&(this[o+0]=null===i?e:p.domainBind(i,e)),"function"==typeof t&&(this[o+1]=null===i?t:p.domainBind(i,t))}return this._setLength(a+1),a},a.prototype._proxy=function(e,t){this._addCallbacks(void 0,void 0,t,e,null)},a.prototype._resolveCallback=function(e,t){if(0==(117506048&this._bitField)){if(e===this)return this._rejectCallback(l(),!1);var n=_(e,this);if(!(n instanceof a))return this._fulfill(e);t&&this._propagateFrom(n,2);var r=n._target();if(r===this)return void this._reject(l());var i=r._bitField;if(0==(50397184&i)){var o=this._length();o>0&&r._migrateCallback0(this);for(var s=1;s>>16)){if(e===this){var n=l();return this._attachExtraTrace(n),this._reject(n)}this._setFulfilled(),this._rejectionHandler0=e,(65535&t)>0&&(0!=(134217728&t)?this._settlePromises():y.settlePromises(this))}},a.prototype._reject=function(e){var t=this._bitField;if(!((117506048&t)>>>16)){if(this._setRejected(),this._fulfillmentHandler0=e,this._isFinal())return y.fatalError(e,p.isNode);(65535&t)>0?y.settlePromises(this):this._ensurePossibleRejectionHandled()}},a.prototype._fulfillPromises=function(e,t){for(var n=1;n0){if(0!=(16842752&e)){var n=this._fulfillmentHandler0;this._settlePromise0(this._rejectionHandler0,n,e),this._rejectPromises(t,n)}else{var r=this._rejectionHandler0;this._settlePromise0(this._fulfillmentHandler0,r,e),this._fulfillPromises(t,r)}this._setLength(0)}this._clearCancellationData()},a.prototype._settledValue=function(){var e=this._bitField;return 0!=(33554432&e)?this._rejectionHandler0:0!=(16777216&e)?this._fulfillmentHandler0:void 0},a.defer=a.pending=function(){return I.deprecated("Promise.defer","new Promise"),{promise:new a(k),resolve:o,reject:s}},p.notEnumerableProp(a,"_makeSelfResolutionError",l),t("./method")(a,k,_,d,I),t("./bind")(a,k,_,I),t("./cancel")(a,T,d,I),t("./direct_resolve")(a),t("./synchronous_inspection")(a),t("./join")(a,T,_,k,y,u),a.Promise=a,a.version="3.5.1",t("./map.js")(a,T,d,_,k,I),t("./call_get.js")(a),t("./using.js")(a,d,_,E,k,I),t("./timers.js")(a,k,I),t("./generators.js")(a,d,k,_,r,I),t("./nodeify.js")(a),t("./promisify.js")(a,k),t("./props.js")(a,T,_,d),t("./race.js")(a,k,_,d),t("./reduce.js")(a,T,d,_,k,I),t("./settle.js")(a,T,I),t("./some.js")(a,T,d),t("./filter.js")(a,k),t("./each.js")(a,k),t("./any.js")(a),p.toFastProperties(a),p.toFastProperties(a.prototype),c({a:1}),c({b:2}),c({c:3}),c(1),c(function(){}),c(void 0),c(!1),c(new a(k)),I.setBounds(g.firstLineError,p.lastLineError),a}},{"./any.js":1,"./async":2,"./bind":3,"./call_get.js":5,"./cancel":6,"./catch_filter":7,"./context":8,"./debuggability":9,"./direct_resolve":10,"./each.js":11,"./errors":12,"./es5":13,"./filter.js":14,"./finally":15,"./generators.js":16,"./join":17,"./map.js":18,"./method":19,"./nodeback":20,"./nodeify.js":21,"./promise_array":23,"./promisify.js":24,"./props.js":25,"./race.js":27,"./reduce.js":28,"./settle.js":30,"./some.js":31,"./synchronous_inspection":32,"./thenables":33,"./timers.js":34,"./using.js":35,"./util":36}],23:[function(e,t,n){"use strict";t.exports=function(t,n,r,i,a){function o(e){switch(e){case-2:return[];case-3:return{};case-6:return new Map}}function s(e){var r=this._promise=new t(n);e instanceof t&&r._propagateFrom(e,3),r._setOnCancel(this),this._values=e,this._length=0,this._totalResolved=0,this._init(void 0,-2)}var c=e("./util");return c.isArray,c.inherits(s,a),s.prototype.length=function(){return this._length},s.prototype.promise=function(){return this._promise},s.prototype._init=function e(n,a){var s=r(this._values,this._promise);if(s instanceof t){var u=(s=s._target())._bitField;if(this._values=s,0==(50397184&u))return this._promise._setAsyncGuaranteed(),s._then(e,this._reject,void 0,this,a);if(0==(33554432&u))return 0!=(16777216&u)?this._reject(s._reason()):this._cancel();s=s._value()}if(null!==(s=c.asArray(s)))0!==s.length?this._iterate(s):-5===a?this._resolveEmptyArray():this._resolve(o(a));else{var l=i("expecting an array or an iterable object but got "+c.classString(s)).reason();this._promise._rejectCallback(l,!1)}},s.prototype._iterate=function(e){var n=this.getActualLength(e.length);this._length=n,this._values=this.shouldCopyValues()?new Array(n):this._values;for(var i=this._promise,a=!1,o=null,s=0;s=this._length&&(this._resolve(this._values),!0)},s.prototype._promiseCancelled=function(){return this._cancel(),!0},s.prototype._promiseRejected=function(e){return this._totalResolved++,this._reject(e),!0},s.prototype._resultCancelled=function(){if(!this._isResolved()){var e=this._values;if(this._cancel(),e instanceof t)e.cancel();else for(var n=0;n=this._length){var n;if(this._isMap)n=d(this._values);else{n={};for(var r=this.length(),i=0,a=this.length();i>1},t.prototype.props=function(){return o(this)},t.props=function(e){return o(e)}}},{"./es5":13,"./util":36}],26:[function(e,t,n){"use strict";function r(e,t,n,r,i){for(var a=0;a=this._length&&(this._resolve(this._values),!0)},i.prototype._promiseFulfilled=function(e,t){var n=new a;return n._bitField=33554432,n._settledValueField=e,this._promiseResolved(t,n)},i.prototype._promiseRejected=function(e,t){var n=new a;return n._bitField=16777216,n._settledValueField=e,this._promiseResolved(t,n)},t.settle=function(e){return r.deprecated(".settle()",".reflect()"),new i(e).promise()},t.prototype.settle=function(){return t.settle(this)}}},{"./util":36}],31:[function(e,t,n){"use strict";t.exports=function(t,n,r){function i(e){this.constructor$(e),this._howMany=0,this._unwrap=!1,this._initialized=!1}function a(e,t){if((0|t)!==t||t<0)return r("expecting a positive integer\n\n See http://goo.gl/MqrFmX\n");var n=new i(e),a=n.promise();return n.setHowMany(t),n.init(),a}var o=e("./util"),s=e("./errors").RangeError,c=e("./errors").AggregateError,u=o.isArray,l={};o.inherits(i,n),i.prototype._init=function(){if(this._initialized){if(0===this._howMany)return void this._resolve([]);this._init$(void 0,-5);var e=u(this._values);!this._isResolved()&&e&&this._howMany>this._canPossiblyFulfill()&&this._reject(this._getRangeError(this.length()))}},i.prototype.init=function(){this._initialized=!0,this._init()},i.prototype.setUnwrap=function(){this._unwrap=!0},i.prototype.howMany=function(){return this._howMany},i.prototype.setHowMany=function(e){this._howMany=e},i.prototype._promiseFulfilled=function(e){return this._addFulfilled(e),this._fulfilled()===this.howMany()&&(this._values.length=this.howMany(),1===this.howMany()&&this._unwrap?this._resolve(this._values[0]):this._resolve(this._values),!0)},i.prototype._promiseRejected=function(e){return this._addRejected(e),this._checkOutcome()},i.prototype._promiseCancelled=function(){return this._values instanceof t||null==this._values?this._cancel():(this._addRejected(l),this._checkOutcome())},i.prototype._checkOutcome=function(){if(this.howMany()>this._canPossiblyFulfill()){for(var e=new c,t=this.length();t0?this._reject(e):this._cancel(),!0}return!1},i.prototype._fulfilled=function(){return this._totalResolved},i.prototype._rejected=function(){return this._values.length-this.length()},i.prototype._addRejected=function(e){this._values.push(e)},i.prototype._addFulfilled=function(e){this._values[this._totalResolved++]=e},i.prototype._canPossiblyFulfill=function(){return this.length()-this._rejected()},i.prototype._getRangeError=function(e){var t="Input array must contain at least "+this._howMany+" items but contains only "+e+" items";return new s(t)},i.prototype._resolveEmptyArray=function(){this._reject(this._getRangeError(0))},t.some=function(e,t){return a(e,t)},t.prototype.some=function(e){return a(this,e)},t._SomePromiseArray=i}},{"./errors":12,"./util":36}],32:[function(e,t,n){"use strict";t.exports=function(e){function t(e){void 0!==e?(e=e._target(),this._bitField=e._bitField,this._settledValueField=e._isFateSealed()?e._settledValue():void 0):(this._bitField=0,this._settledValueField=void 0)}t.prototype._settledValue=function(){return this._settledValueField};var n=t.prototype.value=function(){if(!this.isFulfilled())throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\n\n See http://goo.gl/MqrFmX\n");return this._settledValue()},r=t.prototype.error=t.prototype.reason=function(){if(!this.isRejected())throw new TypeError("cannot get rejection reason of a non-rejected promise\n\n See http://goo.gl/MqrFmX\n");return this._settledValue()},i=t.prototype.isFulfilled=function(){return 0!=(33554432&this._bitField)},a=t.prototype.isRejected=function(){return 0!=(16777216&this._bitField)},o=t.prototype.isPending=function(){return 0==(50397184&this._bitField)},s=t.prototype.isResolved=function(){return 0!=(50331648&this._bitField)};t.prototype.isCancelled=function(){return 0!=(8454144&this._bitField)},e.prototype.__isCancelled=function(){return 65536==(65536&this._bitField)},e.prototype._isCancelled=function(){return this._target().__isCancelled()},e.prototype.isCancelled=function(){return 0!=(8454144&this._target()._bitField)},e.prototype.isPending=function(){return o.call(this._target())},e.prototype.isRejected=function(){return a.call(this._target())},e.prototype.isFulfilled=function(){return i.call(this._target())},e.prototype.isResolved=function(){return s.call(this._target())},e.prototype.value=function(){return n.call(this._target())},e.prototype.reason=function(){var e=this._target();return e._unsetRejectionIsUnhandled(),r.call(e)},e.prototype._value=function(){return this._settledValue()},e.prototype._reason=function(){return this._unsetRejectionIsUnhandled(),this._settledValue()},e.PromiseInspection=t}},{}],33:[function(e,t,n){"use strict";t.exports=function(t,n){function r(e){return e.then}function i(e){try{return r(e)}catch(e){return c.e=e,c}}function a(e){try{return l.call(e,"_promise0")}catch(e){return!1}}function o(e,r,i){var a=new t(n),o=a;i&&i._pushContext(),a._captureStackTrace(),i&&i._popContext();var u=!0,l=s.tryCatch(r).call(e,function(e){a&&(a._resolveCallback(e),a=null)},function(e){a&&(a._rejectCallback(e,u,!0),a=null)});return u=!1,a&&l===c&&(a._rejectCallback(l.e,!0,!0),a=null),o}var s=e("./util"),c=s.errorObj,u=s.isObject,l={}.hasOwnProperty;return function(e,r){if(u(e)){if(e instanceof t)return e;var s=i(e);if(s===c){r&&r._pushContext();var l=t.reject(s.e);return r&&r._popContext(),l}if("function"==typeof s)return a(e)?(l=new t(n),e._then(l._fulfill,l._reject,void 0,l,null),l):o(e,s,r)}return e}}},{"./util":36}],34:[function(e,t,n){"use strict";t.exports=function(t,n,r){function i(e){this.handle=e}function a(e){return clearTimeout(this.handle),e}function o(e){throw clearTimeout(this.handle),e}var s=e("./util"),c=t.TimeoutError;i.prototype._resultCancelled=function(){clearTimeout(this.handle)};var u=function(e){return l(+this).thenReturn(e)},l=t.delay=function(e,a){var o,s;return void 0!==a?(o=t.resolve(a)._then(u,null,null,e,void 0),r.cancellation()&&a instanceof t&&o._setOnCancel(a)):(o=new t(n),s=setTimeout(function(){o._fulfill()},+e),r.cancellation()&&o._setOnCancel(new i(s)),o._captureStackTrace()),o._setAsyncGuaranteed(),o};t.prototype.delay=function(e){return l(e,this)};var f=function(e,t,n){var r;r="string"!=typeof t?t instanceof Error?t:new c("operation timed out"):new c(t),s.markAsOriginatingFromRejection(r),e._attachExtraTrace(r),e._reject(r),null!=n&&n.cancel()};t.prototype.timeout=function(e,t){e=+e;var n,s,c=new i(setTimeout(function(){n.isPending()&&f(n,t,s)},e));return r.cancellation()?(s=this.then(),(n=s._then(a,o,void 0,c,void 0))._setOnCancel(c)):n=this._then(a,o,void 0,c,void 0),n}}},{"./util":36}],35:[function(e,t,n){"use strict";t.exports=function(t,n,r,i,a,o){function s(e){setTimeout(function(){throw e},0)}function c(e){var t=r(e);return t!==e&&"function"==typeof e._isDisposable&&"function"==typeof e._getDisposer&&e._isDisposable()&&t._setDisposable(e._getDisposer()),t}function u(e,n){var i=0,o=e.length,u=new t(a);return(function a(){if(i>=o)return u._fulfill();var l=c(e[i++]);if(l instanceof t&&l._isDisposable()){try{l=r(l._getDisposer().tryDispose(n),e.promise)}catch(e){return s(e)}if(l instanceof t)return l._then(a,s,null,null,null)}a()})(),u}function l(e,t,n){this._data=e,this._promise=t,this._context=n}function f(e,t,n){this.constructor$(e,t,n)}function d(e){return l.isDisposer(e)?(this.resources[this.index]._setDisposable(e),e.promise()):e}function h(e){this.length=e,this.promise=null,this[e-1]=null}var p=e("./util"),v=e("./errors").TypeError,g=e("./util").inherits,y=p.errorObj,m=p.tryCatch,b={};l.prototype.data=function(){return this._data},l.prototype.promise=function(){return this._promise},l.prototype.resource=function(){return this.promise().isFulfilled()?this.promise().value():b},l.prototype.tryDispose=function(e){var t=this.resource(),n=this._context;void 0!==n&&n._pushContext();var r=t!==b?this.doDispose(t,e):null;return void 0!==n&&n._popContext(),this._promise._unsetDisposable(),this._data=null,r},l.isDisposer=function(e){return null!=e&&"function"==typeof e.resource&&"function"==typeof e.tryDispose},g(f,l),f.prototype.doDispose=function(e,t){return this.data().call(e,e,t)},h.prototype._resultCancelled=function(){for(var e=this.length,n=0;n0},t.prototype._getDisposer=function(){return this._disposer},t.prototype._unsetDisposable=function(){this._bitField=-131073&this._bitField,this._disposer=void 0},t.prototype.disposer=function(e){if("function"==typeof e)return new f(e,this,i());throw new v}}},{"./errors":12,"./util":36}],36:[function(n,r,i){"use strict";function a(){try{var e=d;return d=null,e.apply(this,arguments)}catch(e){return v.e=e,v}}function o(e){return null==e||!0===e||!1===e||"string"==typeof e||"number"==typeof e}function s(e,t,n){if(o(e))return e;var r={value:n,configurable:!0,enumerable:!1,writable:!0};return h.defineProperty(e,t,r),e}function c(e){try{return e+""}catch(e){return"[no string representation]"}}function u(e){return e instanceof Error||null!==e&&"object"==typeof e&&"string"==typeof e.message&&"string"==typeof e.name}function l(e){return u(e)&&h.propertyIsWritable(e,"stack")}function f(e){return{}.toString.call(e)}var d,h=n("./es5"),p="undefined"==typeof navigator,v={e:{}},g="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==t?t:void 0!==this?this:null,y=(function(){var e=[Array.prototype,Object.prototype,Function.prototype],t=function(t){for(var n=0;n1,r=t.length>0&&!(1===t.length&&"constructor"===t[0]),i=m.test(e+"")&&h.names(e).length>0;if(n||r||i)return!0}return!1}catch(e){return!1}},isIdentifier:function(e){return b.test(e)},inheritedDataKeys:y,getDataPropertyOrDefault:function(e,t,n){if(!h.isES5)return{}.hasOwnProperty.call(e,t)?e[t]:void 0;var r=Object.getOwnPropertyDescriptor(e,t);return null!=r?null==r.get&&null==r.set?r.value:n:void 0},thrower:function(e){throw e},isArray:h.isArray,asArray:k,notEnumerableProp:s,isPrimitive:o,isObject:function(e){return"function"==typeof e||"object"==typeof e&&null!==e},isError:u,canEvaluate:p,errorObj:v,tryCatch:function(e){return d=e,a},inherits:function(e,t){function n(){for(var n in this.constructor=e,this.constructor$=t,t.prototype)r.call(t.prototype,n)&&"$"!==n.charAt(n.length-1)&&(this[n+"$"]=t.prototype[n])}var r={}.hasOwnProperty;return n.prototype=t.prototype,e.prototype=new n,e.prototype},withAppended:function(e,t){var n,r=e.length,i=new Array(r+1);for(n=0;n10||t[0]>0})(),T.isNode&&T.toFastProperties(e);try{throw new Error}catch(e){T.lastLineError=e}r.exports=T},{"./es5":13}]},{},[4])(4)}),"undefined"!=typeof window&&null!==window?window.P=window.Promise:"undefined"!=typeof self&&null!==self&&(self.P=self.Promise)}).call(this,t("_process"),void 0!==e?e:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},t("timers").setImmediate)},{_process:15,timers:16}],2:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.PreCallTest=void 0;var i=(function(){function e(e,t){for(var n=0;n=10?e.stop():(e.turnConnection.disconnect(),setTimeout(function(){e._start()},0))}))}},{key:"stop",value:function(){var e=this;if(this.browserInfo.browserName!==l.Constants.browserName.msie&&this.active){this.active=!1,this.activeTurnTest&&this.activeTurnTest.forceStop();var t=this.onlineCheck.stop();this.resultsHandler&&this.resultsHandler.add("onlineStatus",t),this.turnConnection.getIceResults().then(function(t){e.resultsHandler&&e.resultsHandler.add("ice",t),e.turnConnection.disconnect(),e.sendResults()},function(t){e.resultsHandler&&e.resultsHandler.failure(t),e.turnConnection.disconnect(),e.sendResults()})}}},{key:"sendResults",value:function(){if(this.resultsHandler){var e=this.resultsHandler.getResults();this.resultsHandler=null,this.callback&&this.callback(e,null)}else this.callback&&this.callback(null,"No results present")}},{key:"callStarts",value:function(){this.callsInProgress+=1,this.resultsHandler&&this.resultsHandler.setStatusStopped(),this.stop()}},{key:"callFinished",value:function(){this.callsInProgress-=1}},{key:"getId",value:function(){return this.resultsHandler?this.resultsHandler.getId():null}},{key:"crashDisconnect",value:function(){try{this.turnConnection.disconnect()}catch(e){}}},{key:"startTurnTests",value:function(){var e=this;if(this.turnTestCounter>=this.turnTests.length)return new f(function(e,t){e()});var t=this.turnTests[this.turnTestCounter],n=null;switch(t){case d.RTT:n=new o.RttTest(this.turnConnection);break;case d.THROUGHPUT:n=new s.ThroughputTest(this.turnConnection,this.rtt);break;default:return new f(function(e,n){n(new Error("Unknown test: "+t))})}return this.activeTurnTest=n,this.active?n.start().then(function(){return e.handleTestResults(t,n.getResults()),e.turnTestCounter+=1,e.activeTurnTest=null,e.startTurnTests()},function(r){return e.handleTestResults(t,n.getResults(),r),e.turnTestCounter+=1,e.activeTurnTest=null,e.startTurnTests()}):new f(function(e,t){t(new Error("Test trying to start while testing is not active"))})}},{key:"handleTestResults",value:function(e,t){null==(2=this.intervalLength){var r=n-this.intervalStart,i=this.averageThroughput(this.intervalBytes,r),a=null;try{a=n-JSON.parse(this.lastMessage).timestamp}catch(e){}this.intervals.push({startTimestamp:this.intervalStart,endTimestamp:n,bytesReceived:this.intervalBytes,average:i,rtt:a}),this.intervalStart=n,this.intervalBytes=0}this.results.startTimestamp&&n-this.results.startTimestamp>this.duration/2&&(this.secondHalfStart||(this.secondHalfStart=n),this.secondHalfBytes+=e.length)}}},{key:"handleError",value:function(e){this.stop(),this.failed(e)}},{key:"averageThroughput",value:function(e,t){return e/(t/1e3)*8/1024}},{key:"bufferListener",value:function(){this.sendChannel.removeEventListener("bufferedamountlow",this.bufferListener.bind(this)),this.fillBuffer()}},{key:"fillBuffer",value:function(){for(0==this.sendChannel.bufferedAmount&&(this.bufferEmpty+=1);this.isActive();){if(this.sendChannel.bufferedAmount>this.bufferFullThreshold)return void(this.usePolling?setTimeout(this.fillBuffer.bind(this),250):this.sendChannel.addEventListener("bufferedamountlow",this.bufferListener.bind(this)));var e=this.messageMaker.make(this.sentBytes);this.sentBytes+=e.length,this.send(e)}this.sendChannel.removeEventListener("bufferedamountlow",this.bufferListener.bind(this))}},{key:"startSend",value:function(){this.isActive()&&(this.bufferFullThreshold=1e3*this.chunkSize,this.sendChannel=this.connection.sendChannel,this.usePolling=!0,"number"==typeof this.sendChannel.bufferedAmountLowThreshold&&(this.usePolling=!1,this.sendChannel.bufferedAmountLowThreshold=this.bufferFullThreshold/10),setTimeout(this.fillBuffer.bind(this),0))}},{key:"fillResults",value:function(){this.results.endTimestamp=c.getCurrent(),this.results.maxDuration=this.duration,this.results.forceStopped=this.forceStopped,this.results.bufferEmpty=this.bufferEmpty,this.results.intervals=this.intervals,this.results.bytesPrepared=this.sentBytes,this.results.bytesReceived=this.receivedBytes;var e=0,t=0;this.secondHalfStart&&(e=this.results.endTimestamp-this.secondHalfStart,t=this.averageThroughput(this.secondHalfBytes,e));var n=this.results.endTimestamp-this.results.startTimestamp,r=this.averageThroughput(this.receivedBytes,n);t>24;if("rtp"===this.protocol&&0<=n&&n<=2)switch(n){case 0:t="TLS";break;case 1:t="TCP";break;case 2:t="UDP"}return t}},{key:"getString",value:function(){return this.iceCandidateStr}},{key:"getType",value:function(){return this.type}},{key:"isHost",value:function(){return"host"===this.type.toLowerCase()}},{key:"isServerReflexive",value:function(){return"srflx"===this.type.toLowerCase()}},{key:"isPeerReflexive",value:function(){return"prflx"===this.type.toLowerCase()}},{key:"isRelay",value:function(){return"relay"===this.type.toLowerCase()||"relayed"===this.type.toLowerCase()}},{key:"getTypeTransport",value:function(){return this.typeTransport}},{key:"isTypeTransportUdp",value:function(){return"UDP"===this.typeTransport}},{key:"isTypeTransportTcp",value:function(){return"TCP"===this.typeTransport}},{key:"isTypeTransportTls",value:function(){return"TLS"===this.typeTransport}},{key:"getTransport",value:function(){return this.transport}},{key:"isUdp",value:function(){return"udp"===this.transport.toLowerCase()}},{key:"isTcp",value:function(){return"tcp"===this.transport.toLowerCase()}},{key:"getProtocol",value:function(){return this.protocol}},{key:"isRtp",value:function(){return"rtp"===this.protocol}},{key:"isRtcp",value:function(){return"rtcp"===this.protocol}},{key:"isIpv6",value:function(){return this.ipv6}},{key:"getIpAddress",value:function(){return this.ipAddress}},{key:"getPort",value:function(){return this.port}}]),e})();n.ParsedIceCandidate=a},{}],10:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.ResultsHandler=void 0;var i="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},a=(function(){function e(e,t){for(var n=0;n>24;c.mozLocalTransport=this.formatRelayType(u)}c.mozLocalTransport=c.mozLocalTransport.toLowerCase()}t.push(c)}else s.remoteCandidate&&n.push(s.remoteCandidate)}if(i)for(var l=0;l1)for(var n=1;n=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n.setImmediate="function"==typeof t?t:function(e){var t=u++,r=!(arguments.length<2)&&s.call(arguments,1);return c[t]=!0,a(function(){c[t]&&(r?e.apply(null,r):e.call(null),n.clearImmediate(t))}),t},n.clearImmediate="function"==typeof r?r:function(e){delete c[e]}}).call(this,e("timers").setImmediate,e("timers").clearImmediate)},{"process/browser.js":15,timers:16}],17:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:"")}function s(){var e=null,t=v.Registry.getEndpoint().getBrowserName();return"Firefox"===t?e=mozRTCPeerConnection:"Chrome"===t||"Opera"===t?e=webkitRTCPeerConnection:"Safari"===t?g.log("Browser type Safari"):"Edge"===t&&(e=window.RTCPeerConnection),e}function c(e){if(null===e)return!1;var t=v.Registry.getEndpoint().getCodeBase();if(t===f.codeBaseType.firefox)return"undefined"!=typeof mozRTCPeerConnection&&e instanceof mozRTCPeerConnection||"undefined"!=typeof RTCPeerConnection&&e instanceof RTCPeerConnection;if(t===f.codeBaseType.edge||"function"==typeof e)return!0;var n=s();return null!==n&&e instanceof n||void 0!==e.createOffer}var u="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},l=(function(){function e(e,t){for(var n=0;n ("+r+") ["+t.toString()+"]"),null!==t.getLeft()&&e(t.getLeft(),n+1,"Left"),null!==t.getRight()&&e(t.getRight(),n+1,"Right")}})(this.root,0,"Root")):s.log("This tree is empty Duplicate Counter: "+this.duplicate_counter)}}]),e})();n.BinaryTree=c},{"../utility/csiologger":92,"./binarytreenode.js":24}],24:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1];this.length()>1e6?a.error("Maximum cached items reached, dropping."):t?this.priority.push(e):this.queue.push(e)}},{key:"pop",value:function(){return this.priority.length>0?this.priority.shift():this.queue.shift()}},{key:"peak",value:function(){return this.priority.length>0?this.priority[0]:this.queue.length>0?this.queue[0]:null}},{key:"length",value:function(){return this.queue.length+this.priority.length}},{key:"updateConferenceId",value:function(e){for(var t=0;t0&&void 0!==arguments[0]?arguments[0]:"Unknown";r(this,e),this.name=t,this.ports=new Map}return i(e,[{key:"bindPort",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this;this.ports.has(e)?o.warn("Port ("+e+") for "+this.name+" already exists."):this.ports.set(e,new a.Port(t,n))}},{key:"declarePort",value:function(e){this.ports.has(e)?o.warn("Port ("+e+") for "+this.name+" already exists."):this.ports.set(e,null)}},{key:"getPort",value:function(e){return this.ports.get(e)}},{key:"isConnected",value:function(e){return!!this.ports.has(e)&&null!==this.ports.get(e)}},{key:"connect",value:function(e,t){this.ports.has(e)?this.ports.set(e,t):o.warn("Port ("+e+") for "+this.name+" does not exists.")}},{key:"transmit",value:function(e){var t=this.ports.get(e);if(void 0!==t)if(null!==t){for(var n=arguments.length,r=Array(n>1?n-1:0),i=1;i1?n-1:0),i=1;i=a.pctSampleSize&&this.pctMeasurements.shift(),this.pctMeasurements.push(e);var t=e.getProvider();this.pctMeasurementsMap.has(t)||this.pctMeasurementsMap.set(t,[]),this.pctMeasurementsMap.get(t).push(e)}},{key:"clearPCTMeasurements",value:function(){this.pctMeasurements=[],this.pctMeasurementsMap=new Map}},{key:"doStats",value:function(e){var t={};return t.min=this.findMin(e),t.max=this.findMax(e),t.mean=this.calculateAverage(e),t.median=this.calculateMedian(e),t.stddev=this.calculateStandardDeviation(e,t.mean),t}},{key:"findMax",value:function(e){return e&&e.length?Math.max.apply(null,e):null}},{key:"findMin",value:function(e){return e&&e.length?Math.min.apply(null,e):null}},{key:"calculateAverage",value:function(e){return e&&e.length?e.reduce(function(e,t){return e+t},0)/e.length:0}},{key:"calculateMedian",value:function(e){if(!e)return 0;if(!e.length)return 0;var t=e.slice(0).sort(function(e,t){return e-t}),n=Math.floor(t.length/2);return t.length%2==0?(t[n]+t[n-1])/2:t[n]}},{key:"calculateStandardDeviation",value:function(e,t){if(!e)return 0;if(!e.length)return 0;var n=e.map(function(e){var n=e-t;return n*n}).reduce(function(e,t){return e+t},0);return Math.sqrt(n/(e.length-1))}},{key:"setAcceptability",value:function(e){var t=[];return t.push(e.throughput.median<30),t.push(e.loss.median>.05),t.push(e.rtt.median>400),e.acceptable=!t.reduce(function(e,t){return e||t},!1),e}},{key:"doInternalMeasurement",value:function(e){var t={},n=[],r=[],i=[],a=[];if(e)for(var o=0;o=.04?t-=3:t,t=n>=.025?t-=1:t,t=n>=.01?t-=1:t,t=r>=.04?t-=5:t,t=r>=.025?t-=3:t,r>=.005?t-=2:t}},{key:"scoreThroughPut",value:function(e){var t=0;if(!e||!e.throughput)return t;var n=e.throughput.median,r=e.throughput.min;return t=n>5e3?t+=5:t,t=n>2e3?t+=2:t,t=n>200?t+=1:t,t=r<100?t-=3:t,t=r<1e3?t-=2:t,(t=r<2e3?t-=1:t)>0?t:0}},{key:"aggregate",value:function(){var e=this,t=[];return e.pctMeasurementsMap.forEach(function(n,r){var i={};i.provider=r;var a=e.doInternalMeasurement(n);i.rtt=a.rtt,i.jitter=a.jitter,i.loss=a.loss,i.throughput=a.throughput,(i=e.setAcceptability(i)).rttScore=e.scoreRtt(),i.lossesScore=e.scoreLosses(),i.throughputScore=e.scoreThroughPut(),t.push(i)}),t}},{key:"getAggregate",value:function(e){var t=[];if(!e)return t;for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:null;r(this,e),this.target=t,this.object=n}return i(e,[{key:"transmit",value:function(e){this.target.apply(this.object,e)}},{key:"request",value:function(e){return this.target.apply(this.object,e)}}]),e})();n.Port=a},{}],30:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:void 0;r(this,e),this.value=t,this.next=n}return a(e,[{key:"setNext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;this.next=e}}]),e})(),s=(function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;r(this,e),this.capacity=t,this.root=n,this.tail=i,this.currentLength=0}return a(e,[{key:"size",value:function(){return this.currentLength}},{key:"isEmpty",value:function(){return this.currentLength<1}},{key:"isOverflow",value:function(){return this.size()>this.capacity}},{key:"push",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=this.tail;this.tail=new o(e),this.isEmpty()?this.root=this.tail:t.setNext(this.tail),this.currentLength+=1;var n=void 0;return this.isOverflow()&&(n=this.pop()),n}},{key:"pop",value:function(){if(!this.isEmpty()){var e=this.root.value;return this.root=this.root.next,this.currentLength-=1,e}}},{key:"getFront",value:function(){return this.root}},{key:"getBack",value:function(){return this.tail}},{key:"clear",value:function(){this.root=void 0,this.tail=void 0,this.currentLength=0}},{key:"toArray",value:function(){for(var e=i({},this.root),t=[];e;){var n=e,r=n.value;e=n.next,t.push(r)}return t}}]),e})();n.Queue=s},{}],31:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.SlidingWindow=void 0;var i=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:0;return this.items.length<=e?null:this.items[this.items.length-e-1].getValue()}},{key:"attach",value:function(e){this.plugins.push(e)}},{key:"addPreProcess",value:function(e){this.preProcesses.push(e)}},{key:"addPostProcess",value:function(e){this.postProcesses.push(e)}},{key:"detach",value:function(e){this.plugins=this.plugins.filter(function(t){return t!==e})}}]),e})();n.SlidingWindow=s},{"../utility/timestamps":104,"./switem":32}],32:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.SWItem=void 0;var i=(function(){function e(e,t){for(var n=0;n>>0,r=arguments[1],i=void 0,a=0;a0&&f.Registry.getEventMessageBuilder().make(v.precalltestEvents.associate,r,e,{ids:u})}},{key:"onStartPrecallTests",value:function(e,t){var n=f.Registry.getAuthenticator().getIceServers();n[0].label="callstats",e.push(n[0]),f.Registry.getPCTRunnerService().start(e,t)}},{key:"onSendCallDetails",value:function(e,t,n){d.warn("FSM onSendCallDetails");var r={};r.callType=n.callType,r.role=n.role,n.contactQueue&&(r.contactQueue=n.contactQueue),n.contactQueueID&&(r.contactQueueID=n.contactQueueID),n.routingProfile&&(r.routingProfile=n.routingProfile),n.routingProfileID&&(r.routingProfileID=n.routingProfileID),n.contactID&&(r.contactID=n.contactID),n.siteID&&(r.siteID=n.siteID,f.Registry.getCredentials().setSiteId(n.siteID)),f.Registry.getEventMessageBuilder().make(v.fabricEvent.callDetails,t,e,r)}},{key:"onSendFabricEvent",value:function(e,t,n,r){d.warn("FSM onSendFabricEvent");var i={};r&&((i=r).ssrc&&(i.ssrc=String(i.ssrc))),t===v.fabricEvent.activeDeviceList&&(i={mediaDeviceList:h.normalizeMediaDeviceList(r.deviceList)}),f.Registry.getEventMessageBuilder().make(t,n,e,i);var a=f.Registry.getConferenceManager().get(n);if(a){var o=a.getPeerConnectionManager().getPcHandler(e);t!==v.fabricEvent.fabricTerminated&&t!==v.internalFabricEvent.fabricSetupFailed||(o.stopUserAliveHandler(),o.setPcState(v.fabricState.terminated),o.stopStatsPolling(),a.getPeerConnectionManager().removePcStats(o&&o.getPcHash()),a.updateState()),t===v.fabricEvent.fabricHold&&(o.setPcState(v.fabricState.hold),o.stopStatsPolling()),t===v.fabricEvent.fabricResume&&(o.setPcState(v.fabricState.established),o.startStatsPolling())}}},{key:"onReportError",value:function(e,t,n,r,i,a){d.warn("FSM onReportError");var o={delay:0,reason:this.callFailureClassifier(n),function:n,magicKey:f.Registry.getEndpoint().getMagicKey(),endpoint:f.Registry.getEndpoint().serialize(),level:"debug"};if(r){var s=this.formatDomError(r);o.message=s.message,o.messageType=s.messageType,o.name=r.name,o.stack=r.stack}if(e){var c=f.Registry.getConferenceManager().get(t),u=void 0;if(c&&(u=c.getPeerConnectionManager().getPcHandler(e)),!u)return void d.error("onReportError: No handler found for given PeerConnection!");if(o.delay=p.getCurrent()-u.getStartTime(),o.fabricState=u.getPcState(),o.iceConnectionState=u.getIceConnectionState(),(i||a||e&&"closed"!==e.signalingState)&&f.Registry.getCredentials().getCollectSDP()&&n!==v.webRTCFunctions.applicationLog){var l={};l.localSDP=this.pickSDP(e,i,"localDescription"),l.remoteSDP=this.pickSDP(e,a,"remoteDescription"),f.Registry.getEventMessageBuilder().make(v.callstatsChannels.sdpSubmission,t,e,l)}}n===v.webRTCFunctions.applicationLog||n===v.webRTCFunctions.applicationError?f.Registry.getEventMessageBuilder().make(v.fabricEvent.applicationErrorLog,t,e,o):n===v.webRTCFunctions.iceConnectionFailure?this.handleIceConnectionFailure(e,t,o):(f.Registry.getEventMessageBuilder().make(v.fabricEvent.fabricSetupFailed,t,e,o),n===v.webRTCFunctions.getUserMedia&&this.handleGUMErrors(t))}},{key:"onAssociateMstWithUserID",value:function(e,t,n,r,i,a){var o=f.Registry.getConferenceManager().get(n);if(o){var s=o.getPeerConnectionManager().getPcHandler(e);"string"!=typeof r&&(r+=""),s.updateSSRCInfo(r,t,i,a)}}},{key:"onAttachWifiStatsHandler",value:function(e){f.Registry.getWifiStatsExecutor().setGetWifiStatsMethod(e)}},{key:"onSetProxyConfig",value:function(e){d.warn("FSM onSetProxyConfig"),e.collectorURL&&g.setBaseUrl(e.collectorURL),e.authServiceURL&&g.setAuthServiceUrl(e.authServiceURL),e.csioInternalAPIURL&&g.setQmodelThresholdsAPIUrl(e.csioInternalAPIURL),e.wsURL&&g.setWsUrl(e.wsURL),e.restEventURL&&g.setRestEventUrl(e.restEventURL),e.restStatsURL&&g.setRestStatsUrl(e.restStatsURL),e.appSettingsURL&&g.setConfigServiceUrl(e.appSettingsURL)}},{key:"onSendUserFeedback",value:function(e,t,n){var r={feedback:{overallRating:t.overall}};t.video&&(r.feedback.videoQualityRating=t.video),t.audio&&(r.feedback.audioQualityRating=t.audio),t.comment&&(r.feedback.comments=t.comment),f.Registry.getEventMessageBuilder().make(v.callstatsChannels.userFeedback,e,null,r)}},{key:"onOn",value:function(e,t){f.Registry.getCallbacks().set(e,t)}},{key:"onSetIdentifiers",value:function(e,t){if(e.conferenceID&&(f.Registry.getConferenceManager().updateConferenceId(e.conferenceID),f.Registry.getTransmissionManager().updateConferenceId(e.conferenceID),f.Registry.getTransmissionManager().trySend()),e.remoteUserID&&t){var n=f.Registry.getConferenceManager().getConferenceForPc(t);if(!n)return void d.log("onSetIdentifiers conference not found");var r=n.getPeerConnectionManager().getPcHandler(t);if(!r)return void d.log("pcHandler not found");r.updateRemoteId(e.remoteUserID),f.Registry.getTransmissionManager().trySend()}}},{key:"onMakePrecallTest",value:function(e,t){var n=e,r=t||"callstats";e||(n=this.authenticator.getIceServers(),r="callstats"),f.Registry.getPCTRunnerService().doPrecalltest(n,r)}},{key:"handleIceConnectionFailure",value:function(e,t,n){if(e){var r=f.Registry.getConferenceManager().get(t),i=void 0;if(r&&(i=r.getPeerConnectionManager().getPcHandler(e)),i)if(i.isFabricSetupSent()){var a={localIceCandidates:[],remoteIceCandidates:[],iceCandidatePairs:[]};a.delay=p.getCurrent()-i.getStartTime(),a.currIceConnectionState="failed",a.prevIceConnectionState="disconnected",f.Registry.getEventMessageBuilder().make(v.internalFabricEvent.iceFailed,t,e,a)}else n.delay=p.getCurrent()-i.getStartTime(),n.fabricState=i.getPcState(),n.iceConnectionState=i.getIceConnectionState(),f.Registry.getEventMessageBuilder().make(v.fabricEvent.fabricSetupFailed,t,e,n)}}},{key:"handleGUMErrors",value:function(e){var t=f.Registry.getConferenceManager().get(e);if(t||(f.Registry.getConferenceManager().add(e,f.Registry.getCredentials()),t=f.Registry.getConferenceManager().get(e)),!t.getUcId()){var n={endpointInfo:f.Registry.getEndpoint().serialize()};f.Registry.getEventMessageBuilder().make(v.internalFabricEvent.userJoined,e,null,n),t.setUserJoinedSent(!0)}}},{key:"callFailureClassifier",value:function(e){var t=void 0;return v.webRTCFunctions.hasOwnProperty(e)?e===v.webRTCFunctions.createOffer||e===v.webRTCFunctions.createAnswer||e===v.webRTCFunctions.setRemoteDescription?t=v.callFailureReasons.negotiationFailure:e===v.webRTCFunctions.setLocalDescription?t=v.callFailureReasons.sdpError:e===v.webRTCFunctions.addIceCandidate?t=v.callFailureReasons.sdpError:e===v.webRTCFunctions.getUserMedia?t=v.callFailureReasons.mediaConfigError:e===v.webRTCFunctions.iceConnectionFailure?t=v.callFailureReasons.iceFailure:e===v.webRTCFunctions.signalingError?t=v.callFailureReasons.signalingError:(e===v.webRTCFunctions.applicationLog||v.webRTCFunctions.applicationError)&&(t=v.callFailureReasons.applicationLog):t=v.callFailureReasons.invalidWebRTCFunctionName,t}},{key:"formatDomError",value:function(e){var t={},n={};e&&(window.DOMException&&e instanceof window.DOMException?(n.message=e.message,n.name=e.name,t.messageType="domError"):"object"===(void 0===e?"undefined":c(e))?(e.message&&(n.message=e.message),e.name&&(n.name=e.name),e.constraintName&&(n.name=e.constraintName),e.stack&&(n.stack=e.stack),t.messageType="json"):(n=e,t.messageType="text"));var r=this.truncateLog(n);return"object"===(void 0===r?"undefined":c(r))?t.message=JSON.stringify(r):t.message=r,t}},{key:"pickSDP",value:function(e,t,n){return t||(e&&e[n]?e[n].sdp:"")}},{key:"truncateString",value:function(e){return e.length>2e4&&(d.log("Log exceeds 20kb, It will be truncated"),e=e.substring(0,2e4)),e}},{key:"truncateLog",value:function(e){return e?("string"==typeof e?e=this.truncateString(e):"object"===(void 0===e?"undefined":c(e))&&e.message&&(e.message=this.truncateString(e.message)),e):e}}]),t})();n.MainFSM=T},{"../config/constants":36,"../config/settings":37,"../statspipeline/statsadapter":73,"../statspipeline/statsassembler":74,"../statspipeline/statsmonitor":76,"../statspipeline/statsparser":77,"../statspipeline/statstransmitter":78,"../utility/csiologger":92,"../utility/registry":100,"../utility/timestamps":104,"../utility/utils":106,"./statemachine":40}],39:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n1?t-1:0),r=1;r",this.iceConnectionState);var e={changedState:o.fabricStateChangeType.iceConnectionState,prevState:this.oldIceConnectionState,newState:this.iceConnectionState};this.sendFabricStateChange(e)}},{key:"handleIceGatheringState",value:function(){if(this.iceGatheringState!==this.pc.iceGatheringState){"complete"===this.pc.iceGatheringState&&(this.iceGatheringDelay=c.getCurrent()-this.startTime),this.oldIceGatheringState=this.iceGatheringState,this.iceGatheringState=this.pc.iceGatheringState,l.warn("CALLBACK: ICE gathering state change",this.oldIceGatheringState,"->",this.iceGatheringState);var e={changedState:o.fabricStateChangeType.iceGatheringState,prevState:this.oldIceGatheringState,newState:this.iceGatheringState};this.sendFabricStateChange(e)}}},{key:"handleIceChecking",value:function(){var e=c.getCurrent();if("disconnected"===this.oldIceConnectionState&&(this.pcState===o.fabricState.checkingDisrupted||this.pcState===o.fabricState.disrupted)){var t={prevIceConnectionState:this.oldIceConnectionState,currIceConnectionState:this.iceConnectionState,delay:e-this.connectionDisruptedTS};this.pcState===o.fabricState.disrupted&&(t.prevIceCandidatePair=this.prevActiveIceCandidatePair,t.currIceCandidatePair=this.activeIceCandidatePair,t.delay=e-this.disruptedTS,this.emb.make(o.internalFabricEvent.iceDisruptionEnd,this.conferenceId,this.pc,t)),this.pcState=o.fabricState.initializing,this.emb.make(o.internalFabricEvent.iceConnectionDisruptionEnd,this.conferenceId,this.pc,t)}}},{key:"handleIceConnectedOrCompleted",value:function(e){var t=c.getCurrent();this.pcState===o.fabricState.disrupted&&(e.prevIceCandidatePair=this.prevActiveIceCandidatePair,e.currIceCandidatePair=this.activeIceCandidatePair,e.delay=t-this.disruptedTS,this.emb.make(o.internalFabricEvent.iceDisruptionEnd,this.conferenceId,this.pc,e)),this.iceConnectivityDelay=t-this.startTime,this.established=!0,this.pcState=o.fabricState.established,this.sendfabricSetup(),this.startStatsPolling()}},{key:"sendfabricSetup",value:function(){if(this.fabricSetupSent)l.log("fabricSetup has been sent already");else{var e={delay:c.getCurrent()-this.startTime,iceGatheringDelay:this.iceGatheringDelay,iceConnectivityDelay:this.iceConnectivityDelay,localIceCandidates:this.localIceCandidates,remoteIceCandidates:this.remoteIceCandidates,iceCandidatePairs:this.iceCandidatePairs,remoteEndpointType:o.endpointType.peer,fabricTransmissionDirection:o.transmissionDirection.sendrecv,iceServers:this.iceServersURLs,sdpSemantics:this.sdpSemantics},t=this.getActiveIceCandidatePair();t&&(e.selectedCandidatePairID=t.id),this.fabricSetupSent=!0;var n=void 0;if(this.conferenceId){var r=s.Registry.getConferenceManager().get(this.conferenceId);r&&r.getPeerConnectionManager()&&(n=r.getPeerConnectionManager().getPcHandler(this.pc),e.remoteEndpointType=n.getRemoteEndpointType(),e.fabricTransmissionDirection=n.getFabricTransmissionDirection())}this.pcState=o.fabricState.established,this.emb.make(o.internalFabricEvent.fabricSetup,this.conferenceId,this.pc,e)}}},{key:"isFabricSetupSent",value:function(){return this.fabricSetupSent}},{key:"startStatsPolling",value:function(){this.getStatsHandler.startStatsPolling()}},{key:"stopStatsPolling",value:function(){this.getStatsHandler.stopStatsPolling()}},{key:"handleIceFailed",value:function(e){e.currIceCandidatePair=this.activeIceCandidatePair,e.delay=c.getCurrent()-this.startTime,this.pcState=o.fabricState.failed,"checking"===e.prevIceConnectionState?this.sendIceFailed(e):"completed"===e.prevIceConnectionState||"connected"===e.prevIceConnectionState?this.emb.make(o.internalFabricEvent.fabricDropped,this.conferenceId,this.pc,e):"disconnected"===e.prevIceConnectionState&&this.established?this.emb.make(o.internalFabricEvent.fabricDropped,this.conferenceId,this.pc,e):"disconnected"===e.prevIceConnectionState&&this.sendIceFailed(e)}},{key:"getActiveIceCandidatePair",value:function(){var e=null,t=this.iceCandidatePairs;if(t&&t.length>0){var n=t.filter(function(e){return"true"===e.selected||"true"===e.googActiveConnection||!0===e.selected||!0===e.googActiveConnection});n.length>0&&(e=n[0])}return e}},{key:"sendIceFailed",value:function(e){e.localIceCandidates=this.localIceCandidates,e.remoteIceCandidates=this.remoteIceCandidates,e.iceCandidatePairs=this.iceCandidatePairs,l.log("sending icefailed ",e),this.emb.make(o.internalFabricEvent.iceFailed,this.conferenceId,this.pc,e)}},{key:"sendFabricTransportSwitch",value:function(e){var t=this,n={};n.prevIceCandidatePair=t.activeIceCandidatePair,n.relayType=e,n.currIceConnectionState=t.iceConnectionState,n.prevIceConnectionState=t.oldIceConnectionState,n.switchDelay=null,t.getStatsHandler.getIceCandidates().then(function(e){t.localIceCandidates=e.localIceCandidates,t.remoteIceCandidates=e.remoteIceCandidates,t.iceCandidatePairs=e.iceCandidatePairs,t.activeIceCandidatePair=t.getActiveIceCandidatePair(),n.currIceCandidatePair=t.activeIceCandidatePair,n.localIceCandidates=t.localIceCandidates,n.remoteIceCandidates=t.remoteIceCandidates,l.log("sending fabric transport switch ",n),"completed"!==n.currIceConnectionState&&"connected"!==n.currIceConnectionState||"completed"!==n.prevIceConnectionState&&"connected"!==n.prevIceConnectionState||t.emb.make(o.internalFabricEvent.fabricTransportSwitch,t.conferenceId,t.pc,n)})}},{key:"handleIceDisconnected",value:function(e){var t=c.getCurrent();this.startTime=t,e.prevIceConnectionStateTs=this.iceConnectionStateTS,e.currIceCandidatePair=this.activeIceCandidatePair,"connected"===e.prevIceConnectionState||"completed"===e.prevIceConnectionState?(this.pcState=o.fabricState.disrupted,this.disruptedTS=t,this.emb.make(o.internalFabricEvent.iceDisruptionStart,this.conferenceId,this.pc,e),this.callback&&this.callback(u.csError.appConnectivityError,"Connectivity check for PC object to "+this.remoteId+" failed.")):"checking"===e.prevIceConnectionState&&(this.pcState=o.fabricState.checkingDisrupted,this.connectionDisruptedTS=t,this.emb.make(o.internalFabricEvent.iceConnectionDisruptionStart,this.conferenceId,this.pc,e),this.callback&&this.callback(u.csError.appConnectivityError,"Connectivity check for PC object to "+this.remoteId+" failed."))}},{key:"handleIceClosed",value:function(e){"new"===e.prevIceConnectionState||"checking"===e.prevIceConnectionState?(e.delay=c.getCurrent()-this.startTime,e.localIceCandidates=this.localIceCandidates,e.remoteIceCandidates=this.remoteIceCandidates,e.iceCandidatePairs=this.iceCandidatePairs,this.emb.make(o.internalFabricEvent.iceAborted,this.conferenceId,this.pc,e)):"connected"!==e.prevIceConnectionState&&"completed"!==e.prevIceConnectionState||(e.prevIceCandidatePair=this.activeIceCandidatePair,this.emb.make(o.internalFabricEvent.iceTerminated,this.conferenceId,this.pc,e)),this.pcState=o.fabricState.terminated,this.conferenceId&&s.Registry.getConferenceManager().get(this.conferenceId).updateState(),this.stopStatsPolling()}},{key:"handleIceRestart",value:function(e){e.prevIceCandidatePair=this.prevActiveIceCandidatePair,"new"!==e.prevIceConnectionState&&(l.log("iceRestarted sending"),this.established=!1,this.emb.make(o.internalFabricEvent.iceRestarted,this.conferenceId,this.pc,e))}},{key:"pcSignalingStateChangeCallback",value:function(e){var t=this;if(e){var n=this.signalingState;this.signalingState=this.pc.signalingState,l.warn("CALLBACK: signaling state change",n,"->",this.signalingState);var r={changedState:o.fabricStateChangeType.signalingState,prevState:n,newState:this.signalingState};if(this.sendFabricStateChange(r),"have-remote-offer"!==this.signalingState&&"stable"!==this.signalingState||(this.precalltest.callStarts(),s.Registry.getPCTRunnerService().setCallInProgress(!0),this.genericevent.sendEvent(o.logEvents.log,{msg:"precalltest told to stop "})),"closed"===this.signalingState&&(this.emb.make(o.fabricEvent.fabricTerminated,this.conferenceId,this.pc),this.userAlive.stop(),this.precalltest.callFinished(),s.Registry.getPCTRunnerService().setCallInProgress(!1),this.stopStatsPolling(),this.pcState=o.fabricState.terminated,this.conferenceId)){var i=s.Registry.getConferenceManager().get(this.conferenceId);if(i&&i.getPeerConnectionManager()){var a=i.getPeerConnectionManager().getPcHandler(this.pc);i.getPeerConnectionManager().removePcStats(a&&a.getPcHash())}i.updateState()}"have-local-offer"!==this.signalingState&&"have-local-pranswer"!==this.signalingState&&"stable"!==this.signalingState||(l.log(this.signalingState,".. requesting sender config."),s.Registry.getConfigServiceWrapper().initiateSenderConfig(this.pc).then(function(){var e=s.Registry.getConfigServiceWrapper().getSenderConfig();l.log("Sender config:",e);var n=o.callstatsChannels.senderConfiguration;e&&s.Registry.getEventMessageBuilder().make(n,t.conferenceId,t.pc,e)}).catch(function(e){l.warn(e)}))}}},{key:"pcNegotiationNeededCallback",value:function(e){e&&(this.negotiationNeeded++,this.startTime=c.getCurrent(),l.warn("CALLBACK: negotiation needed",this.negotiationNeeded))}}]),e})();n.PcCallbackHandler=f},{"../config/callstatserrors":35,"../config/constants":36,"../utility/csiologger":92,"../utility/registry":100,"../utility/timestamps":104}],43:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.PeerConnectionHandler=void 0;var a=(function(){function e(e,t){for(var n=0;n0)this.handleErrorActions(r["urn:x-callstats:auth:errorActions"]);else{if(200===t.status){if("bearer"!==r.token_type)return S.error(this.name+": successful, but token type was not bearer. Scheduling retry."),void this.sendNextRequest();this.setupToken(r.access_token),r.iceServers&&(this.iceServers=r.iceServers),r.metadata&&r.metadata.urls&&(this.backendURLs=r.metadata.urls,this.backendURLs.events&&f.setRestEventUrl(this.backendURLs.events+"/"),this.backendURLs.stats&&f.setRestStatsUrl(this.backendURLs.stats+"/"),this.backendURLs.config&&f.setConfigServiceUrl(this.backendURLs.config+"/"));var i=parseInt(1e3*parseInt(r.expires_in)*.9);return this.setupReauthTimer(i),this.renew=!1,this.initCallback&&this.initCallback(h.csError.success,p.csCallBackMessages.authSuccessful),this.elapsed=n,void this.completeProcess()}S.error("Authentication failed, but no error actions were defined in response."),this.sendNextRequest()}}},{key:"request",value:function(){var e=this;this.clearReauthTimer();var t=new Promise(function(t,n){e.tempResolve=t,e.tempReject=n});return this.tokenGenerator(this.renew,function(t,n){if(null!==t)return e.initCallback&&e.initCallback(h.csError.tokenGenerationError,t.toString()),e.rejectCb(new Error(e.name+": tokenGenerationError")),S.error("tokenGenerationError ",t),null;var r=f.authServiceUrl+"authenticate",i={client_id:e.credentials.userId+"@"+e.credentials.getAppId(),code:n,grant_type:"authorization_code"};(0,y.sendPostRequest)(r,{"Content-Type":"application/x-www-form-urlencoded"},k,i).then(function(t){e.tempResolve(t)},function(t){e.tempReject(t)})}),t}},{key:"tokenGeneratorCreator",value:function(e,t){var n=this;return function(r,i){var a=null;if(!r&&null!==a)return i(null,a);var o=n.credentials,s={userID:o.getUserId(),appID:o.getAppId()};t&&(s.keyId=t),n.jwt.sign({alg:"HS256"},s,e).then(function(e){a=e,i(null,e)}).catch(function(e){i(e)})}}},{key:"createTokenGenerator",value:function(e){var t=this;return new Promise(function(n,r){var i=null;e.indexOf(":")>-1&&(i=d.strtohex(e.split(":")[0]),e=e.split(":")[1]),t.jwt.importKey(e).then(function(r){e=null;var a=t.tokenGeneratorCreator(r,i);n(a)}).catch(function(e){S.error(t.name+": could not import key ("+e+")"),r(e)})})}}]),t})();n.Authenticator=w},{"../browserapi/localstorage":20,"../config/callstatserrors":35,"../config/constants":36,"../config/settings":37,"../utility/base64":89,"../utility/csiologger":92,"../utility/json":97,"../utility/registry":100,"./jwt/jwt":54,"./jwt/jwttoken":55,"./xmlhttp":60,"./xmlhttpservice":61}],47:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.GenericEventHandler=void 0;var a=(function(){function e(e,t){for(var n=0;n6e4?(h.Registry.getGenericEventHandler().sendEvent(p.logEvents.log,{msg:"latency > MAX_ALLOWED_LATENCY: clockSync reset"}),this.offsetResults=[]):this.addOffset(e,r,n),this.offsetResults.length>=5?(this.calculateOffset(),this.endTime=f.getCurrent(),this.completeProcess()):this.sendRequest()}},{key:"isCompleted",value:function(){return this.completed}},{key:"addOffset",value:function(e,t,n){var r=e.now+t-n;this.offsetResults.push(r)}},{key:"calculateOffset",value:function(){var e=this.offsetResults.reduce(function(e,t){return e+t});this.currentOffset=e/this.offsetResults.length,this.currentOffset=isNaN(this.currentOffset)?0:this.currentOffset,h.Registry.getGenericEventHandler().sendEvent(p.logEvents.log,{msg:"clockSync Done, offset is: "+this.currentOffset+"results length"+this.offsetResults.length}),this.offsetResults=[],this.completed=!0}}]),t})();n.ClockSync=v},{"../config/constants":36,"../config/settings":37,"../utility/registry":100,"../utility/timestamps":104,"./xmlhttp":60,"./xmlhttpservice":61}],49:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n1)return f.warn("Multiple PCs found, skipping"),null;if(!e.getSenders||"function"!=typeof e.getSenders)return f.warn("Get senders error"),t;for(var r=e.getSenders(),i=void 0,o=0;o=0;h--)f[h]=c.charCodeAt(h);var p=r.subtlecrypto.sign(d,n,f);"ie"===r.cryptotype?(p.onerror=a,p.oncomplete=function(e){c=r.buildToken(e.target.result,c),i(c)}):p.then(function(e){c=r.buildToken(e,c),i(c)}).catch(function(e){a(e)})})}},{key:"buildToken",value:function(e,t){for(var n="",r=new Uint8Array(e),i=r.byteLength,a=0;a0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return function(n,r,i){"success"===n.status?e&&e(n,r):t&&t(n)}}Object.defineProperty(n,"__esModule",{value:!0}),n.RestRelays=void 0;var o=(function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:null;i(this,e),this.baseUrl=t,this.urlAppendix=n,this.responseHandler=r}return o(e,[{key:"getRequestUrl",value:function(e,t,n){return this.makeRequestUrl(e,t,n),this.requestUrl.toString()}},{key:"makeRequestUrl",value:function(e,t,n){e===s.precalltestEvents.results?this.requestUrl=new l.Url(this.baseUrl,u.Registry.getCredentials().getAppId(),this.urlAppendix):(e===s.internalFabricEvent.userJoined&&n&&(n=""),this.requestUrl=new l.Url(this.baseUrl,u.Registry.getCredentials().getAppId()+"/conferences/"+t,n,this.urlAppendix))}},{key:"handleResponse",value:function(e,t,n){this.responseHandler(e,t,n)}}]),e})();n.RestRelays=d},{"../config/constants":36,"../config/settings":37,"../utility/csiologger":92,"../utility/registry":100,"../utility/url":105}],59:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.TransmissionManager=void 0;var a=(function(){function e(e,t){for(var n=0;n0&&this.startTimer());this.setupConnection()}},{key:"setupConnection",value:function(){this.connectionManager.setup().then(function(){l.log("Connected to connectionManager")}).catch(function(e){return l.log("Could not connect to connectionManager",e)})}},{key:"canSendUserAlive",value:function(e,t){return!(this.lastUserAliveTS[t]&&e-this.lastUserAliveTS[t]<9e3)}},{key:"sendMessage",value:function(){var e=void 0,t=void 0;if(0===this.getCacheLength())return!1;for(var n in this.cache)if(this.cache.hasOwnProperty(n)&&(t=n,(e=this.cache[t].peak())&&e.canBeSent()))break;if(!e||!e.canBeSent())return!1;if(e=this.cache[t].pop(),this.lastTS=o.getCurrent(),e.eventType===u.internalFabricEvent.userAlive){if(!this.canSendUserAlive(this.lastTS,t))return!0;this.lastUserAliveTS[t]=this.lastTS}return l.log("sending message type :",e.eventType,e.toJson()),this.connectionManager.send(e.toJson()),!0}},{key:"startTimer",value:function(){var e=this;this.timerStarted||0!=this.getCacheLength()&&(this.timerStarted=!0,setTimeout(function(){e.timerStarted=!1,e.trySend()},200))}},{key:"timeToSend",value:function(){return o.getCurrent()>=this.lastTS+200}},{key:"getCacheLength",value:function(){var e=0;for(var t in this.cache)this.cache.hasOwnProperty(t)&&(e+=this.cache[t].length());return e}},{key:"updateConferenceId",value:function(e){var t=u.tmpConferenceId;this.cache[t]&&this.cache[t].updateConferenceId(e)}}]),e})();n.TransmissionManager=f},{"../collections/cache":25,"../config/constants":36,"../utility/csiologger":92,"../utility/registry":100,"../utility/timestamps":104}],60:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t,n,r,i){return new Promise(function(a,o){var f,d=null,h=new XMLHttpRequest,p=u.Registry.getEndpoint().getBrowserName();if(h){for(var v in f=s.getCurrent(),h.open(e,t),p!==c.browserName.msie&&(h.timeout=r),n)n.hasOwnProperty(v)&&h.setRequestHeader(v,n[v]);var g=null;if("POST"===e){if("application/x-www-form-urlencoded"===n["Content-Type"]){var y=[];for(var m in i)i.hasOwnProperty(m)&&y.push(encodeURIComponent(m)+"="+encodeURIComponent(i[m]));g=y.join("&")}"application/json"===n["Content-Type"]&&(g=JSON.stringify(i))}h.onload=function(){d=s.getCurrent();var e=new l(h,d-f);a(e)},h.ontimeout=function(){o(new Error("connection timeout"))},h.onreadystatechange=function(){4===h.readyState&&0===h.status&&o(new Error("no server response received"))},h.send(g)}else o(new Error("creating instance failed"))})}Object.defineProperty(n,"__esModule",{value:!0});var o=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"XMLHttpService",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:s,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:c;r(this,e),this.name=t,this.minRetryTimeout=n,this.sigmoid=new a.Sigmoid(i-n,15,.5),this.reset()}return i(e,[{key:"reset",value:function(){this.inProgress=!1,this.completed=!1,this.resolveCb&&this.rejectCb(new Error(this.name+": resetting")),this.resolveCb=null,this.rejectCb=null}},{key:"initiate",value:function(){var e=this;if(this.inProgress)return new Promise(function(t,n){n(new Error(e.name+": in progress"))});var t=new Promise(function(t,n){e.resolveCb=t,e.rejectCb=n});return this.inProgress=!0,this.sendRequest(),t}},{key:"isCompleted",value:function(){return this.completed}},{key:"handleSendError",value:function(e){o.log(this.name+": send next request ("+e+")"),this.sendNextRequest()}},{key:"handleResponseProxy",value:function(e){this.handleResponse(e),this.resetTimeout()}},{key:"handleResponse",value:function(e){o.error(this.name+": handleResponse() not implemented"),this.completeProcess()}},{key:"completeProcess",value:function(){this.inProgress=!1,this.completed=!0,this.resolveCb.apply(this,arguments)}},{key:"request",value:function(){return o.error(this.name+": request() not implemented!"),new Promise(function(e,t){e()})}},{key:"sendRequest",value:function(){if(this.inProgress){var e=this.request();e&&e.then(this.handleResponseProxy.bind(this),this.handleSendError.bind(this))}}},{key:"sendNextRequest",value:function(){setTimeout(this.sendRequest.bind(this),this.getTimeout())}},{key:"resetTimeout",value:function(){this.sigmoid.reset()}},{key:"getTimeout",value:function(){return this.minRetryTimeout+this.sigmoid.getActual()}}]),e})();n.XMLHttpService=u},{"../utility/csiologger":92,"../utility/sigmoid":102}],62:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.IntervalStatsMonitor=void 0;var a=(function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0,r=this.getTotalBytes(e),i=this.getTotalBytes(t);if(!u.checkForPositiveValue(r)||!u.checkForPositiveValue(i)||!u.checkForPositiveValue(n))return null;var a=(8*r-8*i)/Math.max(n,1);return u.checkForPositiveValue(a)?a:null}},{key:"getIntervalPacketLoss",value:function(e,t,n){var r=this.getTotalLostPackets(e);if(!n)return u.checkForPositiveValue(r)?r:null;var i=this.getTotalLostPackets(t);if(!u.checkForPositiveValue(r)||!u.checkForPositiveValue(i))return null;var a=r-i;return u.checkForPositiveValue(a)?a:null}},{key:"getIntervalPacketRate",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!u.checkForPositiveValue(n))return null;var r=this.getTotalPackets(e),i=this.getTotalPackets(t);if(!u.checkForPositiveValue(r)<0||!u.checkForPositiveValue(i))return null;var a=(r-i)/(Math.max(n,1)/1e3);return u.checkForPositiveValue(a)?a:null}},{key:"getIntervalFractionLost",value:function(e,t,n){var r=this.getIntervalPacketLoss(e,t,n),i=this.getTotalPackets(e)-this.getTotalPackets(t);if(!u.checkForPositiveValue(r)||!u.checkForPositiveValue(i))return null;var a=r/Math.max(i+r,1);return u.checkForPositiveValue(a)?a:null}},{key:"getIntervalStat",value:function(e,t){var n=e.getCurrent(),r=e.getPrevious(t),i=e.getElapsedTime(t),a=new Map;if(r)for(var o=0,s=r.length;o0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1],n=e,r=e.tracks||[],i=0,a=r.length;i0&&this.frameHeight>0?this.resolution=new a.Resolution(this.frameWidth,this.frameHeight,this.frameRateReceived):this.resolution=null}},{key:"getJitter",value:function(){return this.jitter}},{key:"getRTT",value:function(){return this.rtt}},{key:"getLostPackets",value:function(){return this.lostPackets}},{key:"getDiscardedPackets",value:function(){return this.discardedPackets}},{key:"getReceivedPackets",value:function(){return this.receivedPackets}},{key:"getSentPackets",value:function(){return this.sentPackets}},{key:"getSentBytes",value:function(){return this.sentBytes}},{key:"getReceivedBytes",value:function(){return this.receivedBytes}},{key:"setFractionLost",value:function(e){this.fractionLost=void 0===e?null:e}},{key:"getFractionLost",value:function(){return this.fractionLost}},{key:"extractTrack",value:function(){var e=this.track;return this.track=null,e}},{key:"toString",value:function(){return"pcHash: "+this.pcHash+", ssrc: "+this.ssrc+", streamType: "+this.streamType+", mediaType: "+this.mediaType+", reportType: "+this.reportType+", frameRateReceived: "+this.frameRateReceived+", frameHeight: "+this.frameHeight+", frameWidth: "+this.frameWidth+", droppedFramesNum: "+this.droppedFramesNum+", rtt: "+this.rtt+", jitter: "+this.jitter+", lostPackets: "+this.lostPackets+", receivedPackets: "+this.receivedPackets+", sentPackets: "+this.sentPackets+", discardedPackets: "+this.discardedPackets+", sentBytes: "+this.sentBytes+", receivedBytes: "+this.receivedBytes+", fractionLost: "+this.fractionLost}}]),e})();n.Measurement=s},{"./resolution":72,"./validator":87}],64:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.CPULimitationObserver=void 0;var a=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:void 0,t=e&&e.getRequestedMeasurement();if(!t)return!0;if(e.getStreamType()===o.streamType.inbound){if(t.getReceivedBytes()<1)return!0}else if(t.getSentBytes()<1)return!0;return!e.lastTrack||!e.lastTrack.data||!e.lastTrack.data.mimeType&&!e.lastTrack.data.googCodecName}},{key:"isValidTrack",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:o.streamType.inbound;if(!e)return!1;var n=e.getRequestedMeasurement();return!!n&&n.getReportType()===o.reportType.local&&e.getStreamType()===t&&e.getMediaType()===o.mediaType.audio}},{key:"accept",value:function(e){var t=this,n=e.filter(function(e){return t.isValidTrack(e,o.streamType.inbound)}),r=e.filter(function(e){return t.isValidTrack(e,o.streamType.outbound)}),i=this.getDisruptions(n,r);i.length>0&&this.notifierCallback(i)}},{key:"toString",value:function(){return"DisruptionObserver"}},{key:"getDisruptedTrack",value:function(){var e=this;return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).find(function(t){return t&&!1===t.hasTraffic()&&!1===e.isDeadTrack(t)})}},{key:"isStartedAndHasTraffic",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=e.filter(function(e){return 0===e.getStartTime()}).length<1,n=e.filter(function(e){return!e.hasTraffic()}).length<1;return e.length>0&&t&&n}},{key:"hasTracksAndTraffic",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=e.filter(function(e){return!e.hasTraffic()}).length<1;return e.length>0&&t}},{key:"isInitialSetupDelayPassed",value:function(){return c.getCurrent()>this.startTime+15e3}},{key:"isInbOneWayAudioDisruption",value:function(e,t){if(this.isStartedAndHasTraffic(t)&&this.isInitialSetupDelayPassed()){var n=void 0;if(!this.hasTracksAndTraffic(e)){var r=this.getDisruptedTrack(e),i=r&&r.getSSRC();i&&(n=new s.OneWayMediaDisruption(o.oneWayMediaTypes.audio,i,o.streamType.inbound))}return n}}},{key:"isOutbOneWayAudioDisruption",value:function(e,t){if(this.isStartedAndHasTraffic(e)&&this.isInitialSetupDelayPassed()){var n=void 0;if(!this.hasTracksAndTraffic(t)){var r=this.getDisruptedTrack(t),i=r&&r.getSSRC();i&&(n=new s.OneWayMediaDisruption(o.oneWayMediaTypes.audio,i,o.streamType.outbound))}return n}}},{key:"updateDisruptionAndRecoveryCount",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return e?(t=Math.min(t+1,l),n=0):(n=Math.min(n+1,l),t=0),{disruptionCount:t,recoveryCount:n}}},{key:"getDisruptionState",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return!1===n&&e>=5?f:!0===n&&t>=5?d:h}},{key:"getAudioDisruption",value:function(e,t,n){var r=void 0,i=void 0;if(n===o.streamType.inbound?(r=this.isInbOneWayAudioDisruption(e,t),i=this.oneWayInbAudioDisruption):n===o.streamType.outbound&&(r=this.isOutbOneWayAudioDisruption(e,t),i=this.oneWayOutbAudioDisruption),i){var a=this.updateDisruptionAndRecoveryCount(r,i.disruptionCount,i.recoveryCount),s=a.disruptionCount,c=a.recoveryCount,u=this.getDisruptionState(s,c,i.started);return u===f?(i.started=!0,i.disruption=r.setStart(),i.disruptionCount=0,i.recoveryCount=0):u===d?(i.started=!1,i.disruption=i.disruption.setEnd(),i.disruptionCount=0,i.recoveryCount=0):(i.recoveryCount=c,i.disruptionCount=s),u!==h?i.disruption:void 0}}},{key:"getDisruptions",value:function(e,t){var n=[],r=this.getAudioDisruption(e,t,o.streamType.inbound),i=this.getAudioDisruption(e,t,o.streamType.outbound);return r&&n.push(r),i&&n.push(i),n}}]),e})();n.OneWayMediaObserver=p},{"../../config/constants":36,"../../utility/registry":100,"../../utility/timestamps":104,"../onewaymediadisruption":69}],66:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.QPSumObserver=void 0;var a=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;if(null===e||void 0===e||Math.abs(e)i;return a?(this.consecutivePositive+=1,this.consecutivePositive>this.resetThreshold&&this.runningStats.reset()):this.consecutivePositive=0,a}}]),e})(),p=(function(){function e(t){var n=t.maxMarginFactor,r=void 0===n?l.adaptiveStatsThresholds.maxMarginFactor:n,a=t.marginReductionFactor,o=void 0===a?l.adaptiveStatsThresholds.marginReductionFactor:a,s=t.marginIncreaseFactor,c=void 0===s?l.adaptiveStatsThresholds.marginIncreaseFactor:s,u=t.marginDecreaseIntervalInMs,f=void 0===u?l.adaptiveStatsThresholds.marginDecreaseIntervalInMs:u,d=t.maxGradualCounter,p=void 0===d?l.adaptiveStatsThresholds.maxGradualCounter:d,v=t.minIntervalInMs,g=void 0===v?l.adaptiveStatsThresholds.minIntervalInMs:v,y=t.rippleBase,m=void 0===y?l.adaptiveStatsThresholds.rippleBase:y,b=t.windowSize,S=void 0===b?l.adaptiveStatsThresholds.windowSize:b,k=t.resetThreshold,w=void 0===k?l.adaptiveStatsThresholds.resetThreshold:k,C=t.minThreshold,_=void 0===C?l.adaptiveStatsThresholds.minThreshold:C;i(this,e),this.rttTester=new h("RTT Tester",_.rtt,S,w),this.throughputTester=new h("Throughput Tester",_.throughput,S,w),this.FLTester=new h("FL Tester",_.fractionalLoss,S,w),this.marginFactor=1,this.lastDecreased=0,this.lastTriggering=0,this.maxMarginFactor=r,this.marginReductionFactor=o,this.marginIncreaseFactor=c,this.marginDecreaseIntervalInMs=f,this.maxGradualCounter=p,this.gradualCounter=p,this.minIntervalInMs=g,this.rippleBase=m}return o(e,[{key:"test",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,n=t?{}:e.peek();if(!n)return!1;var r=(t?{}:e.getTrack()).data,i=t?t.rtt:n.getRTT(),a=u.checkForNan(parseInt(t?t.throughput:r.intervalBandwidth,10)),o=t?t.loss:n.getFractionLost(),c=t?t.time:s.getCurrent(),l=this.rttTester.doTest(i,this.marginFactor),f=this.throughputTester.doTest(a,this.marginFactor),d=this.FLTester.doTest(o,this.marginFactor);if(l||f||d)return this.marginFactor=Math.min(this.marginFactor*this.marginIncreaseFactor,this.maxMarginFactor),this.lastTriggering=c,this.lastDecreased=c,this.gradualCounter=0,!0;if(c-this.lastDecreased>this.marginDecreaseIntervalInMs&&(this.marginFactor=Math.max(this.marginFactor*this.marginReductionFactor,1),this.lastDecreased=c),this.gradualCounter1&&void 0!==arguments[1]?arguments[1]:{};i(this,e),t?(this.notifyCallback=t,this.minIntervalInMs=n.minIntervalInMs||l.adaptiveStatsThresholds.minIntervalInMs,this.maxIntervalInMs=n.maxIntervalInMs||l.adaptiveStatsThresholds.maxIntervalInMs,this.config=n,this.notified=0,this.testers=new Map):d.warn("notifyCallback was null or undefined : "+t)}return o(e,[{key:"setup",value:function(e,t){this.minIntervalInMs=e,this.maxIntervalInMs=t}},{key:"getMostRecentTrackStartTime",value:function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce(function(e,t){return!!t&&"function"==typeof t.getCreationTime&&Math.max(t.getCreationTime(),e)},Number.MIN_VALUE)}},{key:"accept",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,n=t?t.time:s.getCurrent(),r=n-this.notified,i=this.doSend(e,t);if(this.minIntervalInMs===this.maxIntervalInMs)return d.log("Elapsed time in fix intervalset: "+r),void(r>this.minIntervalInMs&&(this.notifyCallback&&this.notifyCallback(n),this.notified=n));if(!(rl.adaptiveStatsThresholds.initialNotifyIntervalTimeoutInMs)return this.notifyCallback&&this.notifyCallback(n),void(this.notified=n);r0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;if(t){if(this.testers.has("test-ssrc")||this.testers.set("test-ssrc",new p(a({},this.config,{minIntervalInMs:this.minIntervalInMs,maxIntervalInMs:this.maxIntervalInMs}))),this.testers.get("test-ssrc").test(e,t))return!0}else for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"0",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];r(this,e),this.mediaType=t,this.ssrc=""+n,this.streamType=i,this.started=a}return i(e,[{key:"setStart",value:function(){return this.started=!0,this}},{key:"setEnd",value:function(){return this.started=!1,this}},{key:"getContent",value:function(){return{mediaType:this.mediaType,ssrc:this.ssrc,streamType:this.streamType}}},{key:"isStarted",value:function(){return this.started}},{key:"toString",value:function(){return JSON.stringify({started:this.started,ssrc:this.ssrc,mediaType:this.mediaType})}}]),e})();n.OneWayMediaDisruption=a},{}],70:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:o.avQualityRatings.unknown){case o.avQualityRatings.bad:return o.qualityRating.bad;case o.avQualityRatings.fair:return o.qualityRating.fair;case o.avQualityRatings.excellent:return o.qualityRating.excellent}return 0}},{key:"toQualityString",value:function(){switch(arguments.length>0&&void 0!==arguments[0]?arguments[0]:o.qualityRating.excellent){case o.qualityRating.excellent:return o.avQualityRatings.excellent;case o.qualityRating.good:case o.qualityRating.fair:case o.qualityRating.poor:return o.avQualityRatings.fair;case o.qualityRating.bad:return o.avQualityRatings.bad}return o.avQualityRatings.unknown}},{key:"inboundAudioQuality",value:function(){var e=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).data||{},t=[];if(e.csioIntBRKbps&&00&&void 0!==arguments[0]?arguments[0]:{}).data||{},t=[];if(e.csioIntBRKbps&&00&&void 0!==arguments[0]?arguments[0]:{}).data||{},t=[];if(e.csioIntBRKbps&&00&&void 0!==arguments[0]?arguments[0]:{}).data||{},t=[];if(e.csioIntBRKbps&&00&&void 0!==arguments[0]?arguments[0]:[],t=0,n=0,r=0,i=e.length;r0&&void 0!==arguments[0]?arguments[0]:{},t=e.tracks||[],n=0,i=t.length;n0)return r;for(var o=void 0,s=0;s0||parseInt(a.transportStats.bytesSent,10)>0)&&n.transportStats.push(a.transportStats):"true"!==a.transportStats.selected&&!0!==a.transportStats.selected||n.transportStats.push(a.transportStats))}if(n.tracks){var o=f.Registry.getConferenceManager().getConferenceForPcHash(t),s=void 0;o&&(s=o.getPeerConnectionManager().getPcHandlerByHash(t)),n.tracks.forEach(function(e){var t=s.getSSRCInfo(e.data.ssrc);void 0!==t&&t.localStartTime||s&&(s.updateSDP(n.tracks),t=s.getSSRCInfo(e.data.ssrc)),t&&(e.cname=t.cname,e.msid=t.msid,e.associatedVideoTag=t.associatedVideoTag,e.usageLabel=t.usageLabel)})}return n.transportStats&&this.codeBase===u.codeBaseType.firefox&&(n.transportStats=this.getAddressInfoFromCandidates(n.transportStats,n.localIceCandidates,n.remoteIceCandidates)),n}},{key:"getParsedStats",value:function(e){var t={};if(e.timestamp instanceof Date&&(t.timestamp=e.timestamp.getTime().toString()),e.type&&(t.type=e.type),e.names){for(var n=e.names(),r=0;r0)return n[0]}}},{key:"formatRelayType",value:function(e){var t="None";switch(e){case 0:t="TURN/TLS";break;case 1:t="TURN/TCP";break;case 2:t="TURN/UDP"}return t}},{key:"getRelayType",value:function(e,t){var n=this,r=void 0;return t.getIceCandidates().some(function(t){if(t.typePreference){var i=t.typePreference>>24;return"rtp"===t.protocol&&t.address===e&&(i>=0&&i<=2&&(r=n.formatTypePreference(i)),!0)}return!1}),r}},{key:"getTransportInfo",value:function(e,t){for(var n={},r=!1,i=0;i0?a.statsType="outbound-rtp":a.statsType="inbound-rtp"}"remote-inbound-rtp"!==a.statsType&&"remote-outbound-rtp"!==a.statsType&&(!0!==i.isRemote&&"true"!==i.isRemote||(a.statsType="remote-"+a.statsType)),n.streams[i.ssrc]=a,n.mediaStreamTracks.push(a)}}this.callback(n)}}]),e})();n.StatsCallbackBuilder=o},{"../utility/registry":100}],76:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0}),n.StatsMonitor=n.StatsMonitorIO=void 0;var s=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:null;null===e&&(e=k.statsDestination.backend);var t=new h.StatsTuple(this.getCandidatePairs(),this.lastPcStats.getCodec(),this.lastPcStats.getTrackStats(),this.transportsmonitor.getIntervalStats(),this.tracksmonitor.extractTracks(),e),n=this.intervalstatsmonitor.getIntervalStats(e);t=this.intervalstatsmonitor.updateIntervalStats(t,n),t=this.qualityevaluator.updateQualityStats(t),this.transmit(C.StatsTupleOut,t)}},{key:"sendDisruptions",value:function(e){var t=this;e.forEach(function(e){e.isStarted()?t.sendEventMsg(k.internalFabricEvent.oneWayMediaStart,e.getContent()):t.sendEventMsg(k.internalFabricEvent.oneWayMediaStop,e.getContent())})}},{key:"sendCPULimitationObservations",value:function(e){}},{key:"sendQPSumDistortions",value:function(e){}},{key:"sendThroughputObservations",value:function(e){this.sendEventMsg(k.internalFabricEvent.sendingThroughputObservations,e)}},{key:"sendEventMsg",value:function(e,t){if(this.lastPcHash){var n=p.Registry.getConferenceManager().getConferenceForPcHash(this.lastPcHash);if(n){var r=n.getPeerConnectionManager().getPcHandlerByHash(this.lastPcHash),i=r.getPeerConnection(),a=r.getConferenceId();p.Registry.getEventMessageBuilder().make(e,a,i,t)}}}},{key:"delete",value:function(e){this.tracksmonitor&&this.tracksmonitor.deleteTracks(e),this.transportsmonitor&&this.transportsmonitor.delete(e),this.intervalstatsmonitor&&this.intervalstatsmonitor.delete(e),this.candidatepairs&&this.candidatepairs.has(e)&&this.candidatepairs.delete(e)}}]),t})();n.StatsMonitor=_},{"../collections/component":26,"../config/constants":36,"../utility/csiologger":92,"../utility/registry":100,"../utility/timestamps":104,"./intervalstatsmonitor":62,"./monitorhooks/cpulimitationobserver":64,"./monitorhooks/onewaymediaobserver":65,"./monitorhooks/qpsumobserver":66,"./monitorhooks/sendingtrigger":67,"./monitorhooks/throughputobserver":68,"./qualityevaluator":71,"./statstuple":79,"./tracksmonitor":85,"./transportsmonitor":86}],77:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0}),n.StatsParser=n.StatsParserIO=void 0;var s=(function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:null;r(this,t);var o=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,a));return o.sum=0,o.counter=0,o}return a(t,s.SWPlugin),o(t,[{key:"add",value:function(e){this.filter(e)&&(this.sum+=this.extract(e),++this.counter,this.calculate())}},{key:"remove",value:function(e){this.filter(e)&&(this.sum-=this.extract(e),--this.counter,this.calculate())}},{key:"calculate",value:function(){var e=0;this.counter<1?this.notify(e):(e=this.sum/this.counter,this.notify(e))}}]),t})();n.SWAvg=c},{"./swplugin":83}],81:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0}),n.SWBTreePercentile=void 0;var o=(function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(o=s.next()).done)&&(n.push(o.value),!t||n.length!==t);r=!0);}catch(e){i=!0,a=e}finally{try{!r&&s.return&&s.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(("function"==typeof Symbol?Symbol.iterator:"@@iterator")in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}})(),s=(function(){function e(e,t){for(var n=0;n4&&void 0!==arguments[4]?arguments[4]:null,c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,u=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null;r(this,t);var f=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,a,o,c));if(992&&void 0!==arguments[2]?arguments[2]:null;r(this,t);var o=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,null,null,a));return o.addFunc=e,o.remFunc=n,o}return a(t,s.SWPlugin),o(t,[{key:"add",value:function(e){this.filter(e)&&this.addFunc&&this.addFunc(e)}},{key:"remove",value:function(e){this.filter(e)&&this.remFunc&&this.remFunc(e)}},{key:"toString",value:function(){return"SWFuncPlugin"}}]),t})();n.SWFunctor=c},{"./swplugin":83}],83:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.SWPlugin=void 0;var i=(function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:null;r(this,e),this.extractorFnc=t,this.notifierFnc=n,this.filterFunc=i}return i(e,[{key:"notify",value:function(e){this.notifierFnc?this.notifierFnc(e):a.warn("Failed notification for "+this.toString())}},{key:"extract",value:function(e){return this.extractorFnc?this.extractorFnc(e):(a.warn(this.toString()+" tried to extract without extractorFnc"),null)}},{key:"filter",value:function(e){return!this.filterFunc||this.filterFunc(e)}},{key:"add",value:function(e){a.log("Abstract method is called width value: "+e)}},{key:"remove",value:function(e){a.log("Abstract method is called with value: "+e)}},{key:"toString",value:function(){return"SWPlugin"}}]),e})();n.SWPlugin=o},{"../../utility/csiologger":92}],84:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.TrackMonitor=void 0;var c=function e(t,n,r){null===t&&(t=Function.prototype);var i=Object.getOwnPropertyDescriptor(t,n);if(void 0===i){var a=Object.getPrototypeOf(t);return null===a?void 0:e(a,n,r)}if("value"in i)return i.value;var o=i.get;return void 0!==o?o.call(r):void 0},u=(function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:1};this.evaluators.push(t)}}]),e})(),S=(function(e){function t(e,n,r,a){o(this,t);var s=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));return s.thresholds=[],s.enableThresholds=!1,s.requestTime=0,s.started=0,s.created=v.getCurrent(),s.mediaType=a,s.streamType=r,s.qualityHelper=new b,s.metrics=s.makeTrack(),s.lastTrack={},s.prevFrameRateMean=null,s.ssrc=null,s}return a(t,f.SlidingWindow),u(t,null,[{key:"make",value:function(e,n,r,i){var a=new t(e,n,r,i),o=a.metrics;return a.attach(new d.SWAvg(function(e){return e.getJitter()},function(e){o.csioAvgJitter=e},function(e){var t=e.getJitter();return null!==t&&void 0!==t})),a.attach(new d.SWAvg(function(e){return e.getRTT()},function(e){o.csioAvgRtt=e},function(e){var t=e.getRTT();return null!==t&&void 0!==t})),a.attach(new h.SWBTreePercentile(95,function(e,t){var n=e.getJitter(),r=t.getJitter();return n==r?0:n0&&r>=0&&(t.csioEstFrameRatePerSecond=1e3*r/i)}}),e.attach(new p.SWFunctor(function(n){var r=v.getCurrent(),i=n.getResolution(),a=e.getRequestedMeasurement(),o=e.getRequestTime(),s=8*n.getReceivedBytes(),c=n.getReceivedPackets(),u=0,f=0,d=0,h=Math.max(r-e.getStartTime(),d);a?(u=s-8*a.getReceivedBytes(),f=Math.max(r-o,d)):(d=l.Registry.getCredentials().isAdaptiveStatsEnabled()?g.adaptiveStatsIntervalsInMs.min:l.Registry.getCredentials().getStatsSubmissionInterval(),h=Math.max(r-e.getStartTime(),d),u=s,f=Math.max(h,d)),t.csioAvgBRKbps=s/h,t.intervalBandwidth=Math.max(u/f,0),t.csioAvgPacketSize=n.getReceivedBytes()/Math.max(c,1),t.csiores=i},null,null)),e.attach(new h.SWBTreePercentile(95,function(e,t){var n=e.getFractionLost(),r=t.getFractionLost();return n==r?0:n=0},function(e,t){return e}))}},{key:"setupOutboundMonitor",value:function(e){var t=e.metrics;e.addPreProcess(function(t){var n=t.getFractionLost();if(!y.checkForPositiveValue(n)){var r=t.getLostPackets(),i=t.getReceivedPackets(),a=e.getRequestedMeasurement(),o=0,s=0;a?(o=r-a.getLostPackets(),s=i-a.getReceivedPackets()):(o=r,s=i),y.checkForPositiveValue(o)&&y.checkForPositiveValue(s)&&(n=o/Math.max(s+o,1),y.checkForPositiveValue(n)&&t.setFractionLost(n))}}),e.attach(new p.SWFunctor(function(n){var r=v.getCurrent(),i=n.getResolution(),a=e.getRequestedMeasurement(),o=e.getRequestTime(),s=8*n.getSentBytes(),c=n.getSentPackets(),u=0,f=0,d=0,h=Math.max(r-e.getStartTime(),d);a?(u=s-8*a.getSentBytes(),f=Math.max(r-o,d)):(d=l.Registry.getCredentials().isAdaptiveStatsEnabled()?g.adaptiveStatsIntervalsInMs.min:l.Registry.getCredentials().getStatsSubmissionInterval(),h=Math.max(r-e.getStartTime(),d),u=s,f=Math.max(h,d)),t.csioAvgBRKbps=s/h,t.intervalBandwidth=Math.max(u/f,0),t.csioAvgPacketSize=n.getSentBytes()/Math.max(c,1),t.csiores=i},null,null)),e.attach(new h.SWBTreePercentile(95,function(e,t){var n=e.getFractionLost(),r=t.getFractionLost();return n==r?0:n=0},function(e,t){return e}))}},{key:"setupAudioMonitor",value:function(e){var t=e.metrics;e.attach(new p.SWFunctor(function(e){t.csioMediaType=g.mediaType.audio},null,null))}},{key:"setupVideoMonitor",value:function(e){var t=e.metrics;e.attach(new d.SWAvg(function(e){return e.getFrameRateReceived()},function(n){e.prevFrameRateMean=t.csioFrameRateMean,t.csioPrevFrameRateMean=t.csioFrameRateMean,t.csioFrameRateMean=n},function(e){var t=e.getFrameRateReceived();return null!==t&&void 0!==t})),e.attach(new d.SWAvg(function(e){return e.getFrameHeight()},function(e){t.csioFrameHeightMean=e},function(e){var t=e.getFrameHeight();return null!==t&&void 0!==t})),e.attach(new d.SWAvg(function(e){return e.getFrameWidth()},function(e){t.csioFrameWidthMean=e},function(e){var t=e.getFrameWidth();return null!==t&&void 0!==t})),e.attach(new h.SWBTreePercentile(50,function(e,t){var n=e.getFrameRateReceived(),r=t.getFrameRateReceived();return n==r?0:n1&&void 0!==arguments[1]?arguments[1]:1;this.qualityHelper.addEvaluator(e,t)}},{key:"addPreProcess",value:function(e){c(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"addPreProcess",this).call(this,e)}},{key:"addPostProcess",value:function(e){c(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"addPostProcess",this).call(this,e)}}]),t})();n.TrackMonitor=S},{"../collections/slidingwindow":31,"../config/constants":36,"../statspipeline/swplugins/swavg":80,"../statspipeline/swplugins/swbtreepercentile":81,"../statspipeline/swplugins/swfunctor":82,"../utility/csiologger":92,"../utility/registry":100,"../utility/timestamps":104,"./validator":87}],85:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.TracksMonitor=void 0;var i=(function(){function e(e,t){for(var n=0;n0||a.csioIntBytesReceived>0)?(a.csioTurnMins=r+o.csioTurnMins,a.csioBytesSent=o.csioBytesSent+a.csioIntBytesSent,a.csioBytesReceived=o.csioBytesReceived+a.csioIntBytesReceived):a.csioTurnMins=o.csioTurnMins}return t}},{key:"getIntervalStatForChrome",value:function(e){var t=e.getActual(),n=e.getPrevious(),r=e.getElapsedTime();if(!n)return actual;for(var i=0;i0||a.csioIntBytesReceived>0)?(a.csioTurnMins=r+o.csioTurnMins,a.csioBytesSent=o.csioBytesSent+a.csioIntBytesSent,a.csioBytesReceived=o.csioBytesReceived+a.csioIntBytesReceived):a.csioTurnMins=o.csioTurnMins}return t}}]),e})();n.TransportsMonitor=u},{"../config/constants":36,"../utility/timestamps":104}],87:[function(e,t,n){"use strict";function r(e){return isNaN(e)?null:e}Object.defineProperty(n,"__esModule",{value:!0}),n.checkForNan=r,n.checkForNegativeValue=function(e){return null!==r(e)&&0<=e?e:null},n.checkForPositiveValue=function(e){return void 0!==e&&null!==r(e)&&e>=0}},{}],88:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]&&arguments[0];this.adaptiveStatsEnabled=e}}]),e})();n.Credentials=u},{"../config/constants":36,"./csiologger":92}],92:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.info=function(){var e;"true"===r.csioDebug&&(e=console).info.apply(e,arguments)},n.log=function(){var e;"true"===r.csioDebug&&(e=console).log.apply(e,arguments)},n.warn=function(){var e;"true"===r.csioDebug&&(e=console).warn.apply(e,arguments)},n.error=function(){var e;(e=console).warn.apply(e,arguments)};var r=(function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t})(e("../config/settings"))},{"../config/settings":37}],93:[function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.Endpoint=void 0;var a=(function(){function e(e,t){for(var n=0;n3&&void 0!==arguments[3]?arguments[3]:{};if("string"!=typeof e||null===r||"object"!==(void 0===r?"undefined":a(r)))return l.error("failed typeof checks:",e,void 0===e?"undefined":a(e),void 0===r?"undefined":a(r),r),u.Registry.getGenericEventHandler().sendEvent(c.logEvents.error,{msg:"failed typeof checks:"+e+":"+(void 0===r?"undefined":a(r))+":"+(void 0===e?"undefined":a(e))}),!1;if(null===t&&e!==c.precalltestEvents.results)return l.error("failed conferenceId checks:",e,t),u.Registry.getGenericEventHandler().sendEvent(c.logEvents.error,{msg:"failed conferenceId checks:"+e}),!1;if(!n&&e!==c.callstatsChannels.userFeedback&&e!==c.fabricEvent.applicationErrorLog&&e!==c.fabricEvent.fabricSetupFailed&&e!==c.internalFabricEvent.userJoined&&e!==c.precalltestEvents.results)return l.error("peerconnection cannot be null",e),u.Registry.getGenericEventHandler().sendEvent(c.logEvents.error,{msg:"peerconnection cannot be null"+e}),!1;if(!s.EventMessage.checkCustomEntries(e,r))return l.error("failed checks:",e,r),u.Registry.getGenericEventHandler().sendEvent(c.logEvents.error,{msg:"failed customEntries checks:"+e}),!1;var i=void 0;if(t){var o=u.Registry.getConferenceManager().get(t);o&&n&&(i=o.getPeerConnectionManager().getPcHandler(n))}var f=new s.EventMessage(e,t,r,i);return this.transmissionmanager.send(f),!0}}]),e})();n.EventMessageBuilder=f},{"../config/constants":36,"./csiologger":92,"./eventmessage":94,"./registry":100}],96:[function(e,t,n){"use strict";function r(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=0;if(!e)return n;for(var r=0,i=e.length;r127&&r<2048?(t[t.length]=r>>6|192,t[t.length]=63&r|128):(t[t.length]=r>>12|224,t[t.length]=r>>6&63|128,t[t.length]=63&r|128)}return new Uint8Array(t).buffer}function a(e){for(var t=new DataView(e),n="",r=void 0,i=0;i-1){for(var n=!1,r={fileName:e.filename,line:e.lineno,col:e.colno,jsVersion:a.version,eventType:"error",message:e.message,pageURL:window.location.href},s=i.Registry.getConferenceManager().getConferenceIds(),c=0;c0&&void 0!==arguments[0]?arguments[0]:100;r(this,e),this.queue=new a.Queue(t),this.cumulativeSum=0}return i(e,[{key:"add",value:function(e){this.cumulativeSum+=e;var t=this.queue.push(e);t&&(this.cumulativeSum-=t)}},{key:"getMean",value:function(){return this.queue.size()<1?null:this.cumulativeSum/this.queue.size()}},{key:"getVariance",value:function(){if(this.queue.size()<2)return null;for(var e=this.getMean(),t=0,n=this.queue.getFront();n;){var r=n,i=r.value-e;t+=i*i,n=r.next}return t/this.queue.size()}},{key:"getStd",value:function(){if(this.queue.size()<2)return null;var e=this.getVariance();return Math.sqrt(e)}},{key:"reset",value:function(){this.cumulativeSum=0,this.queue.clear()}}]),e})();n.RunningStats=o},{"../collections/queue":30}],102:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:1;return this.actualStep+=e,this.max/(1+Math.exp(-1*this.stepness*(this.actualStep-this.midpoint)))}}]),e})();n.Sigmoid=a},{}],103:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0}),n.TimeoutProcess=void 0;var i=(function(){function e(e,t){for(var n=0;n1?n-1:0),a=1;a0&&(e.splice(l-1,2),l-=2)}e=e.join("/")}if((h||y)&&v){for(l=(n=e.split("/")).length;l>0;l-=1){if(r=n.slice(0,l).join("/"),h)for(p=h.length;p>0;p-=1)if((i=v[h.slice(0,p).join("/")])&&(i=i[r])){o=i,s=l;break}if(o)break;!c&&y&&y[r]&&(c=y[r],u=l)}!o&&c&&(o=c,s=u),o&&(n.splice(0,s,o),e=n.join("/"))}return e}function g(t,n){return function(){var r=h.call(arguments,0);return"string"!=typeof r[0]&&1===r.length&&r.push(null),a.apply(e,r.concat([t,n]))}}function S(e){return function(t){u[e]=t}}function _(n){if(v(l,n)){var r=l[n];delete l[n],p[n]=!0,t.apply(e,r)}if(!v(u,n)&&!v(p,n))throw new Error("No "+n);return u[n]}function b(e){var t,n=e?e.indexOf("!"):-1;return n>-1&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function E(e){return e?b(e):[]}function T(e){return function(){return d&&d.config&&d.config[e]||{}}}s=function(e,t){var n,r,i=b(e),o=i[0],a=t[1];return e=i[1],o&&(n=_(o=y(o,a))),o?e=n&&n.normalize?n.normalize(e,(r=a,function(e){return y(e,r)})):y(e,a):(o=(i=b(e=y(e,a)))[0],e=i[1],o&&(n=_(o))),{f:o?o+"!"+e:e,n:e,pr:o,p:n}},c={require:function(e){return g(e)},exports:function(e){var t=u[e];return void 0!==t?t:u[e]={}},module:function(e){return{id:e,uri:"",exports:u[e],config:T(e)}}},t=function(t,n,r,i){var a,d,f,h,m,y,b,T=[],C=o(r);if(y=E(i=i||t),"undefined"===C||"function"===C){for(n=!n.length&&r.length?["require","exports","module"]:n,m=0;m>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(arguments.length>1&&(n=t),r=0;r>2,a=(3&n)<<4|(r=e.charCodeAt(l++))>>4,s=(15&r)<<2|(i=e.charCodeAt(l++))>>6,c=63&i,isNaN(r)?(a=(3&n)<<4,s=c=64):isNaN(i)&&(c=64),u=u+t.charAt(o)+t.charAt(a)+t.charAt(s)+t.charAt(c)}while(l>4,r=(15&o)<<4|(a=t.indexOf(e.charAt(u++)))>>2,i=(3&a)<<6|(s=t.indexOf(e.charAt(u++))),c+=String.fromCharCode(n),64!==a&&(c+=String.fromCharCode(r)),64!==s&&(c+=String.fromCharCode(i))}while(u>5]|=128<<24-r%32,e[15+(r+64>>9<<4)]=r;var a,s,c,u,l,d,p,f,h=new Array(80),m=1732584193,v=-271733879,y=-1732584194,g=271733878,S=-1009589776;for(a=0;a16&&(r=e(r,8*t.length));for(var i=new Array(16),o=new Array(16),s=0;s<16;s++)i[s]=909522486^r[s],o[s]=1549556828^r[s];var c=e(i.concat(a(n)),512+8*n.length);return e(o.concat(c),672)}function i(e,t){var n=(65535&e)+(65535&t);return(e>>16)+(t>>16)+(n>>16)<<16|65535&n}function o(e,t){return e<>>32-t}function a(e){for(var t=[],n=0;n<8*e.length;n+=8)t[n>>5]|=(255&e.charCodeAt(n/8))<<24-n%32;return t}function s(e){for(var t="",n=0;n<32*e.length;n+=8)t+=String.fromCharCode(e[n>>5]>>>24-n%32&255);return t}function c(e){for(var t,n,r="",i=0;i<4*e.length;i+=3)for(t=(e[i>>2]>>8*(3-i%4)&255)<<16|(e[i+1>>2]>>8*(3-(i+1)%4)&255)<<8|e[i+2>>2]>>8*(3-(i+2)%4)&255,n=0;n<4;n++)8*i+6*n>32*e.length?r+="=":r+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(t>>6*(3-n)&63);return r}return{b64_hmac_sha1:function(e,t){return c(r(e,t))},b64_sha1:function(t){return c(e(a(t),8*t.length))},binb2str:s,core_hmac_sha1:r,str_hmac_sha1:function(e,t){return s(r(e,t))},str_sha1:function(t){return s(e(a(t),8*t.length))}}}),(function(n,r){"function"==typeof i&&i.amd?i("strophe-md5",[],function(){return r()}):"object"===o(t)?e.exports=r():n.MD5=r()})(this,function(){var e=function(e,t){var n=(65535&e)+(65535&t);return(e>>16)+(t>>16)+(n>>16)<<16|65535&n},t=function(e){for(var t=[],n=0;n<8*e.length;n+=8)t[n>>5]|=(255&e.charCodeAt(n/8))<>>32-c,r);var s,c},r=function(e,t,r,i,o,a,s){return n(t&r|~t&i,e,t,o,a,s)},i=function(e,t,r,i,o,a,s){return n(t&i|r&~i,e,t,o,a,s)},o=function(e,t,r,i,o,a,s){return n(t^r^i,e,t,o,a,s)},a=function(e,t,r,i,o,a,s){return n(r^(t|~i),e,t,o,a,s)},s=function(t,n){t[n>>5]|=128<>>9<<4)]=n;for(var s,c,u,l,d=1732584193,p=-271733879,f=-1732584194,h=271733878,m=0;m>2]>>n%4*8+4&15)+"0123456789abcdef".charAt(e[n>>2]>>n%4*8&15);return t})(s(t(e),8*e.length))},hash:function(e){return(function(e){for(var t="",n=0;n<32*e.length;n+=8)t+=String.fromCharCode(e[n>>5]>>>n%32&255);return t})(s(t(e),8*e.length))}}}),(function(n,r){"function"==typeof i&&i.amd?i("strophe-utils",[],function(){return r()}):"object"===o(t)?e.exports=r():n.stropheUtils=r()})(this,function(){return{utf16to8:function(e){var t,n,r="",i=e.length;for(t=0;t=0&&n<=127?r+=e.charAt(t):n>2047?(r+=String.fromCharCode(224|n>>12&15),r+=String.fromCharCode(128|n>>6&63),r+=String.fromCharCode(128|n>>0&63)):(r+=String.fromCharCode(192|n>>6&31),r+=String.fromCharCode(128|n>>0&63));return r},addCookies:function(e){var t,n,r,i,a,s,c;for(t in e||{})a="",s="",c="",r="object"===o(n=e[t]),i=escape(unescape(r?n.value:n)),r&&(a=n.expires?";expires="+n.expires:"",s=n.domain?";domain="+n.domain:"",c=n.path?";path="+n.path:""),document.cookie=t+"="+i+a+s+c}}}),(function(n,a){if("function"==typeof i&&i.amd)i("strophe-core",["strophe-sha1","strophe-md5","strophe-utils"],function(){return a.apply(this,arguments)});else if("object"===o(t))e.exports=a(r("./sha1"),r("./md5"),r("./utils"));else{var s=a(n.SHA1,n.MD5,n.stropheUtils);n.Strophe=s.Strophe,n.$build=s.$build,n.$iq=s.$iq,n.$msg=s.$msg,n.$pres=s.$pres,n.SHA1=s.SHA1,n.MD5=s.MD5,n.b64_hmac_sha1=s.SHA1.b64_hmac_sha1,n.b64_sha1=s.SHA1.b64_sha1,n.str_hmac_sha1=s.SHA1.str_hmac_sha1,n.str_sha1=s.SHA1.str_sha1}})(this,function(e,t,n){var r;function i(e,t){return new r.Builder(e,t)}function a(e){return new r.Builder("iq",e)}function s(e){return new r.Builder("presence",e)}return(r={VERSION:"1.2.16",NS:{HTTPBIND:"http://jabber.org/protocol/httpbind",BOSH:"urn:xmpp:xbosh",CLIENT:"jabber:client",AUTH:"jabber:iq:auth",ROSTER:"jabber:iq:roster",PROFILE:"jabber:iq:profile",DISCO_INFO:"http://jabber.org/protocol/disco#info",DISCO_ITEMS:"http://jabber.org/protocol/disco#items",MUC:"http://jabber.org/protocol/muc",SASL:"urn:ietf:params:xml:ns:xmpp-sasl",STREAM:"http://etherx.jabber.org/streams",FRAMING:"urn:ietf:params:xml:ns:xmpp-framing",BIND:"urn:ietf:params:xml:ns:xmpp-bind",SESSION:"urn:ietf:params:xml:ns:xmpp-session",VERSION:"jabber:iq:version",STANZAS:"urn:ietf:params:xml:ns:xmpp-stanzas",XHTML_IM:"http://jabber.org/protocol/xhtml-im",XHTML:"http://www.w3.org/1999/xhtml"},XHTML:{tags:["a","blockquote","br","cite","em","img","li","ol","p","span","strong","ul","body"],attributes:{a:["href"],blockquote:["style"],br:[],cite:["style"],em:[],img:["src","alt","style","height","width"],li:["style"],ol:["style"],p:["style"],span:["style"],strong:[],ul:["style"],body:[]},css:["background-color","color","font-family","font-size","font-style","font-weight","margin-left","margin-right","text-align","text-decoration"],validTag:function(e){for(var t=0;t0)for(var n=0;n/g,">")).replace(/'/g,"'")).replace(/"/g,""")},xmlunescape:function(e){return(e=(e=(e=(e=e.replace(/\&/g,"&")).replace(/</g,"<")).replace(/>/g,">")).replace(/'/g,"'")).replace(/"/g,'"')},xmlTextNode:function(e){return r.xmlGenerator().createTextNode(e)},xmlHtmlNode:function(e){var t;return DOMParser?t=(new DOMParser).parseFromString(e,"text/xml"):((t=new ActiveXObject("Microsoft.XMLDOM")).async="false",t.loadXML(e)),t},getText:function(e){if(!e)return null;var t="";0===e.childNodes.length&&e.nodeType===r.ElementType.TEXT&&(t+=e.nodeValue);for(var n=0;n0&&(c=u.join("; "),n.setAttribute(s,c))}else n.setAttribute(s,c);for(t=0;t/g,"\\3e").replace(/@/g,"\\40")},unescapeNode:function(e){return"string"!=typeof e?e:e.replace(/\\20/g," ").replace(/\\22/g,'"').replace(/\\26/g,"&").replace(/\\27/g,"'").replace(/\\2f/g,"/").replace(/\\3a/g,":").replace(/\\3c/g,"<").replace(/\\3e/g,">").replace(/\\40/g,"@").replace(/\\5c/g,"\\")},getNodeFromJid:function(e){return e.indexOf("@")<0?null:e.split("@")[0]},getDomainFromJid:function(e){var t=r.getBareJidFromJid(e);if(t.indexOf("@")<0)return t;var n=t.split("@");return n.splice(0,1),n.join("@")},getResourceFromJid:function(e){var t=e.split("/");return t.length<2?null:(t.splice(0,1),t.join("/"))},getBareJidFromJid:function(e){return e?e.split("/")[0]:null},_handleError:function(e){void 0!==e.stack&&r.fatal(e.stack),e.sourceURL?r.fatal("error: "+this.handler+" "+e.sourceURL+":"+e.line+" - "+e.name+": "+e.message):e.fileName?r.fatal("error: "+this.handler+" "+e.fileName+":"+e.lineNumber+" - "+e.name+": "+e.message):r.fatal("error: "+e.message)},log:function(e,t){e===this.LogLevel.FATAL&&"object"===o(window.console)&&"function"==typeof window.console.error&&window.console.error(t)},debug:function(e){this.log(this.LogLevel.DEBUG,e)},info:function(e){this.log(this.LogLevel.INFO,e)},warn:function(e){this.log(this.LogLevel.WARN,e)},error:function(e){this.log(this.LogLevel.ERROR,e)},fatal:function(e){this.log(this.LogLevel.FATAL,e)},serialize:function(e){var t;if(!e)return null;"function"==typeof e.tree&&(e=e.tree());var n,i,o=e.nodeName;for(e.getAttribute("_realname")&&(o=e.getAttribute("_realname")),t="<"+o,n=0;n0){for(t+=">",n=0;n"}t+=""}else t+="/>";return t},_requestId:0,_connectionPlugins:{},addConnectionPlugin:function(e,t){r._connectionPlugins[e]=t}}).Builder=function(e,t){"presence"!==e&&"message"!==e&&"iq"!==e||(t&&!t.xmlns?t.xmlns=r.NS.CLIENT:t||(t={xmlns:r.NS.CLIENT})),this.nodeTree=r.xmlElement(e,t),this.node=this.nodeTree},r.Builder.prototype={tree:function(){return this.nodeTree},toString:function(){return r.serialize(this.nodeTree)},up:function(){return this.node=this.node.parentNode,this},root:function(){return this.node=this.nodeTree,this},attrs:function(e){for(var t in e)e.hasOwnProperty(t)&&(void 0===e[t]?this.node.removeAttribute(t):this.node.setAttribute(t,e[t]));return this},c:function(e,t,n){var i=r.xmlElement(e,t,n);return this.node.appendChild(i),"string"!=typeof n&&"number"!=typeof n&&(this.node=i),this},cnode:function(e){var t,n=r.xmlGenerator();try{t=void 0!==n.importNode}catch(e){t=!1}var i=t?n.importNode(e,!0):r.copyElement(e);return this.node.appendChild(i),this.node=i,this},t:function(e){var t=r.xmlTextNode(e);return this.node.appendChild(t),this},h:function(e){var t=document.createElement("body");t.innerHTML=e;for(var n=r.createHtml(t);n.childNodes.length>0;)this.node.appendChild(n.childNodes[0]);return this}},r.Handler=function(e,t,n,i,o,a,s){this.handler=e,this.ns=t,this.name=n,this.type=i,this.id=o,this.options=s||{matchBareFromJid:!1,ignoreNamespaceFragment:!1},this.options.matchBare&&(r.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.'),this.options.matchBareFromJid=this.options.matchBare,delete this.options.matchBare),this.options.matchBareFromJid?this.from=a?r.getBareJidFromJid(a):null:this.from=a,this.user=!0},r.Handler.prototype={getNamespace:function(e){var t=e.getAttribute("xmlns");return t&&this.options.ignoreNamespaceFragment&&(t=t.split("#")[0]),t},namespaceMatch:function(e){var t=!1;if(!this.ns)return!0;var n=this;return r.forEachChild(e,null,function(e){n.getNamespace(e)===n.ns&&(t=!0)}),t=t||this.getNamespace(e)===this.ns},isMatch:function(e){var t=e.getAttribute("from");this.options.matchBareFromJid&&(t=r.getBareJidFromJid(t));var n=e.getAttribute("type");return!(!this.namespaceMatch(e)||this.name&&!r.isTagEqual(e,this.name)||this.type&&(Array.isArray(this.type)?-1===this.type.indexOf(n):n!==this.type)||this.id&&e.getAttribute("id")!==this.id||this.from&&t!==this.from)},run:function(e){var t=null;try{t=this.handler(e)}catch(e){throw r._handleError(e),e}return t},toString:function(){return"{Handler: "+this.handler+"("+this.name+","+this.id+","+this.ns+")}"}},r.TimedHandler=function(e,t){this.period=e,this.handler=t,this.lastCalled=(new Date).getTime(),this.user=!0},r.TimedHandler.prototype={run:function(){return this.lastCalled=(new Date).getTime(),this.handler()},reset:function(){this.lastCalled=(new Date).getTime()},toString:function(){return"{TimedHandler: "+this.handler+"("+this.period+")}"}},r.Connection=function(e,t){this.service=e,this.options=t||{};var i=this.options.protocol||"";for(var o in 0===e.indexOf("ws:")||0===e.indexOf("wss:")||0===i.indexOf("ws")?this._proto=new r.Websocket(this):this._proto=new r.Bosh(this),this.jid="",this.domain=null,this.features=null,this._sasl_data={},this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this.protocolErrorHandlers={HTTP:{},websocket:{}},this._idleTimeout=null,this._disconnectTimeout=null,this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.do_authentication=!0,this.paused=!1,this.restored=!1,this._data=[],this._uniqueId=0,this._sasl_success_handler=null,this._sasl_failure_handler=null,this._sasl_challenge_handler=null,this.maxRetries=5,this._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this),100),n.addCookies(this.options.cookies),this.registerSASLMechanisms(this.options.mechanisms),r._connectionPlugins)if(r._connectionPlugins.hasOwnProperty(o)){var a=r._connectionPlugins[o],s=function(){};s.prototype=a,this[o]=new s,this[o].init(this)}},r.Connection.prototype={reset:function(){this._proto._reset(),this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.restored=!1,this._data=[],this._requests=[],this._uniqueId=0},pause:function(){this.paused=!0},resume:function(){this.paused=!1},getUniqueId:function(e){var t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)});return"string"==typeof e||"number"==typeof e?t+":"+e:t+""},addProtocolErrorHandler:function(e,t,n){this.protocolErrorHandlers[e][t]=n},connect:function(e,t,n,i,o,a,s){this.jid=e,this.authzid=r.getBareJidFromJid(this.jid),this.authcid=s||r.getNodeFromJid(this.jid),this.pass=t,this.servtype="xmpp",this.connect_callback=n,this.disconnecting=!1,this.connected=!1,this.authenticated=!1,this.restored=!1,this.domain=r.getDomainFromJid(this.jid),this._changeConnectStatus(r.Status.CONNECTING,null),this._proto._connect(i,o,a)},attach:function(e,t,n,i,o,a,s){if(!(this._proto instanceof r.Bosh))throw{name:"StropheSessionError",message:'The "attach" method can only be used with a BOSH connection.'};this._proto._attach(e,t,n,i,o,a,s)},restore:function(e,t,n,r,i){if(!this._sessionCachingSupported())throw{name:"StropheSessionError",message:'The "restore" method can only be used with a BOSH connection.'};this._proto._restore(e,t,n,r,i)},_sessionCachingSupported:function(){if(this._proto instanceof r.Bosh){if(!JSON)return!1;try{sessionStorage.setItem("_strophe_","_strophe_"),sessionStorage.removeItem("_strophe_")}catch(e){return!1}return!0}return!1},xmlInput:function(e){},xmlOutput:function(e){},rawInput:function(e){},rawOutput:function(e){},nextValidRid:function(e){},send:function(e){if(null!==e){if("function"==typeof e.sort)for(var t=0;t=0&&this.addHandlers.splice(t,1)},registerSASLMechanisms:function(e){this.mechanisms={},(e=e||[r.SASLAnonymous,r.SASLExternal,r.SASLMD5,r.SASLOAuthBearer,r.SASLXOAuth2,r.SASLPlain,r.SASLSHA1]).forEach(this.registerSASLMechanism.bind(this))},registerSASLMechanism:function(e){this.mechanisms[e.prototype.name]=e},disconnect:function(e){if(this._changeConnectStatus(r.Status.DISCONNECTING,e),r.info("Disconnect was called because: "+e),this.connected){var t=!1;this.disconnecting=!0,this.authenticated&&(t=s({xmlns:r.NS.CLIENT,type:"unavailable"})),this._disconnectTimeout=this._addSysTimedHandler(3e3,this._onDisconnectTimeout.bind(this)),this._proto._disconnect(t)}else r.info("Disconnect was called before Strophe connected to the server"),this._proto._abortAllRequests(),this._doDisconnect()},_changeConnectStatus:function(e,t,n){for(var i in r._connectionPlugins)if(r._connectionPlugins.hasOwnProperty(i)){var o=this[i];if(o.statusChanged)try{o.statusChanged(e,t)}catch(e){r.error(i+" plugin caused an exception changing status: "+e)}}if(this.connect_callback)try{this.connect_callback(e,t,n)}catch(e){r._handleError(e),r.error("User connection callback caused an exception: "+e)}},_doDisconnect:function(e){"number"==typeof this._idleTimeout&&clearTimeout(this._idleTimeout),null!==this._disconnectTimeout&&(this.deleteTimedHandler(this._disconnectTimeout),this._disconnectTimeout=null),r.info("_doDisconnect was called"),this._proto._doDisconnect(),this.authenticated=!1,this.disconnecting=!1,this.restored=!1,this.handlers=[],this.timedHandlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this._changeConnectStatus(r.Status.DISCONNECTED,e),this.connected=!1},_dataRecv:function(e,t){r.info("_dataRecv called");var n=this._proto._reqToData(e);if(null!==n){var i,o;for(this.xmlInput!==r.Connection.prototype.xmlInput&&(n.nodeName===this._proto.strip&&n.childNodes.length?this.xmlInput(n.childNodes[0]):this.xmlInput(n)),this.rawInput!==r.Connection.prototype.rawInput&&(t?this.rawInput(t):this.rawInput(r.serialize(n)));this.removeHandlers.length>0;)o=this.removeHandlers.pop(),(i=this.handlers.indexOf(o))>=0&&this.handlers.splice(i,1);for(;this.addHandlers.length>0;)this.handlers.push(this.addHandlers.pop());if(this.disconnecting&&this._proto._emptyQueue())this._doDisconnect();else{var a,s,c=n.getAttribute("type");if(null!==c&&"terminate"===c){if(this.disconnecting)return;return a=n.getAttribute("condition"),s=n.getElementsByTagName("conflict"),null!==a?("remote-stream-error"===a&&s.length>0&&(a="conflict"),this._changeConnectStatus(r.Status.CONNFAIL,a)):this._changeConnectStatus(r.Status.CONNFAIL,r.ErrorCondition.UNKOWN_REASON),void this._doDisconnect(a)}var u=this;r.forEachChild(n,null,function(e){var t,n;for(n=u.handlers,u.handlers=[],t=0;t0:i.getElementsByTagName("stream:features").length>0||i.getElementsByTagName("features").length>0){var o,a,s=[],c=i.getElementsByTagName("mechanism");if(c.length>0)for(o=0;oe[r].prototype.priority&&(r=n);r!==t&&(i=e[t],e[t]=e[r],e[r]=i)}return e},_attemptSASLAuth:function(e){e=this.sortMechanismsByPriority(e||[]);var t=0,n=!1;for(t=0;t0&&(t=r.ErrorCondition.CONFLICT),this._changeConnectStatus(r.Status.AUTHFAIL,t,e),!1;var n,i=e.getElementsByTagName("bind");if(!(i.length>0))return r.info("SASL binding failed."),this._changeConnectStatus(r.Status.AUTHFAIL,null,e),!1;(n=i[0].getElementsByTagName("jid")).length>0&&(this.jid=r.getText(n[0]),this.do_session?(this._addSysHandler(this._sasl_session_cb.bind(this),null,null,null,"_session_auth_2"),this.send(a({type:"set",id:"_session_auth_2"}).c("session",{xmlns:r.NS.SESSION}).tree())):(this.authenticated=!0,this._changeConnectStatus(r.Status.CONNECTED,null)))},_sasl_session_cb:function(e){if("result"===e.getAttribute("type"))this.authenticated=!0,this._changeConnectStatus(r.Status.CONNECTED,null);else if("error"===e.getAttribute("type"))return r.info("Session creation failed."),this._changeConnectStatus(r.Status.AUTHFAIL,null,e),!1;return!1},_sasl_failure_cb:function(e){return this._sasl_success_handler&&(this.deleteHandler(this._sasl_success_handler),this._sasl_success_handler=null),this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null),this._sasl_mechanism&&this._sasl_mechanism.onFailure(),this._changeConnectStatus(r.Status.AUTHFAIL,null,e),!1},_auth2_cb:function(e){return"result"===e.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(r.Status.CONNECTED,null)):"error"===e.getAttribute("type")&&(this._changeConnectStatus(r.Status.AUTHFAIL,null,e),this.disconnect("authentication failed")),!1},_addSysTimedHandler:function(e,t){var n=new r.TimedHandler(e,t);return n.user=!1,this.addTimeds.push(n),n},_addSysHandler:function(e,t,n,i,o){var a=new r.Handler(e,t,n,i,o);return a.user=!1,this.addHandlers.push(a),a},_onDisconnectTimeout:function(){return r.info("_onDisconnectTimeout was called"),this._changeConnectStatus(r.Status.CONNTIMEOUT,null),this._proto._onDisconnectTimeout(),this._doDisconnect(),!1},_onIdle:function(){for(var e,t,n;this.addTimeds.length>0;)this.timedHandlers.push(this.addTimeds.pop());for(;this.removeTimeds.length>0;)t=this.removeTimeds.pop(),(e=this.timedHandlers.indexOf(t))>=0&&this.timedHandlers.splice(e,1);var r=(new Date).getTime();for(n=[],e=0;e0&&(n="conflict"),this._conn._changeConnectStatus(e.Status.CONNFAIL,n)):this._conn._changeConnectStatus(e.Status.CONNFAIL,"unknown"),this._conn._doDisconnect(n),e.Status.CONNFAIL;this.sid||(this.sid=t.getAttribute("sid"));var o=t.getAttribute("requests");o&&(this.window=parseInt(o,10));var a=t.getAttribute("hold");a&&(this.hold=parseInt(a,10));var s=t.getAttribute("wait");s&&(this.wait=parseInt(s,10));var c=t.getAttribute("inactivity");c&&(this.inactivity=parseInt(c,10))},_disconnect:function(e){this._sendTerminate(e)},_doDisconnect:function(){this.sid=null,this.rid=Math.floor(4294967295*Math.random()),this._conn._sessionCachingSupported()&&window.sessionStorage.removeItem("strophe-bosh-session"),this._conn.nextValidRid(this.rid)},_emptyQueue:function(){return 0===this._requests.length},_callProtocolErrorHandlers:function(e){var t,n=this._getRequestStatus(e);(t=this._conn.protocolErrorHandlers.HTTP[n])&&t.call(this,n)},_hitError:function(t){this.errors++,e.warn("request errored, status: "+t+", number of errors: "+this.errors),this.errors>4&&this._conn._onDisconnectTimeout()},_no_auth_received:function(t){e.warn("Server did not yet offer a supported authentication mechanism. Sending a blank poll request."),t=t?t.bind(this._conn):this._conn._connect_cb.bind(this._conn);var n=this._buildBody();this._requests.push(new e.Request(n.tree(),this._onRequestStateChange.bind(this,t),n.tree().getAttribute("rid"))),this._throttledRequestHandler()},_onDisconnectTimeout:function(){this._abortAllRequests()},_abortAllRequests:function(){for(var e;this._requests.length>0;)(e=this._requests.pop()).abort=!0,e.xhr.abort(),e.xhr.onreadystatechange=function(){}},_onIdle:function(){var t=this._conn._data;if(this._conn.authenticated&&0===this._requests.length&&0===t.length&&!this._conn.disconnecting&&(e.info("no requests during idle cycle, sending blank request"),t.push(null)),!this._conn.paused){if(this._requests.length<2&&t.length>0){for(var n=this._buildBody(),r=0;r0){var i=this._requests[0].age();null!==this._requests[0].dead&&this._requests[0].timeDead()>Math.floor(e.SECONDARY_TIMEOUT*this.wait)&&this._throttledRequestHandler(),i>Math.floor(e.TIMEOUT*this.wait)&&(e.warn("Request "+this._requests[0].id+" timed out, over "+Math.floor(e.TIMEOUT*this.wait)+" seconds since last activity"),this._throttledRequestHandler())}}},_getRequestStatus:function(t,n){var r;if(4===t.xhr.readyState)try{r=t.xhr.status}catch(t){e.error("Caught an error while retrieving a request's status, reqStatus: "+r)}return void 0===r&&(r="number"==typeof n?n:0),r},_onRequestStateChange:function(t,n){if(e.debug("request id "+n.id+"."+n.sends+" state changed to "+n.xhr.readyState),n.abort)n.abort=!1;else if(4===n.xhr.readyState){var r=this._getRequestStatus(n);if(this.lastResponseHeaders=n.xhr.getAllResponseHeaders(),this.disconnecting&&r>=400)return this._hitError(r),void this._callProtocolErrorHandlers(n);var i=r>0&&r<500,o=n.sends>this._conn.maxRetries;if((i||o)&&(this._removeRequest(n),e.debug("request id "+n.id+" should now be removed")),200===r){var a=this._requests[0]===n;(this._requests[1]===n||a&&this._requests.length>0&&this._requests[0].age()>Math.floor(e.SECONDARY_TIMEOUT*this.wait))&&this._restartRequest(0),this._conn.nextValidRid(Number(n.rid)+1),e.debug("request id "+n.id+"."+n.sends+" got 200"),t(n),this.errors=0}else 0===r||r>=400&&r<600||r>=12e3?(e.error("request id "+n.id+"."+n.sends+" error "+r+" happened"),this._hitError(r),this._callProtocolErrorHandlers(n),r>=400&&r<500&&(this._conn._changeConnectStatus(e.Status.DISCONNECTING,null),this._conn._doDisconnect())):e.error("request id "+n.id+"."+n.sends+" error "+r+" happened");i||o?o&&!this._conn.connected&&this._conn._changeConnectStatus(e.Status.CONNFAIL,"giving-up"):this._throttledRequestHandler()}},_processRequest:function(t){var n=this,r=this._requests[t],i=this._getRequestStatus(r,-1);if(r.sends>this._conn.maxRetries)this._conn._onDisconnectTimeout();else{var o=r.age(),a=!isNaN(o)&&o>Math.floor(e.TIMEOUT*this.wait),s=null!==r.dead&&r.timeDead()>Math.floor(e.SECONDARY_TIMEOUT*this.wait),c=4===r.xhr.readyState&&(i<1||i>=500);if((a||s||c)&&(s&&e.error("Request "+this._requests[t].id+" timed out (secondary), restarting"),r.abort=!0,r.xhr.abort(),r.xhr.onreadystatechange=function(){},this._requests[t]=new e.Request(r.xmlData,r.origFunc,r.rid,r.sends),r=this._requests[t]),0===r.xhr.readyState){e.debug("request id "+r.id+"."+r.sends+" posting");try{var u=this._conn.options.contentType||"text/xml; charset=utf-8";r.xhr.open("POST",this._conn.service,!this._conn.options.sync),void 0!==r.xhr.setRequestHeader&&r.xhr.setRequestHeader("Content-Type",u),this._conn.options.withCredentials&&(r.xhr.withCredentials=!0)}catch(t){return e.error("XHR open failed: "+t.toString()),this._conn.connected||this._conn._changeConnectStatus(e.Status.CONNFAIL,"bad-service"),void this._conn.disconnect()}var l=function(){if(r.date=new Date,n._conn.options.customHeaders){var e=n._conn.options.customHeaders;for(var t in e)e.hasOwnProperty(t)&&r.xhr.setRequestHeader(t,e[t])}r.xhr.send(r.data)};if(r.sends>1){var d=1e3*Math.min(Math.floor(e.TIMEOUT*this.wait),Math.pow(r.sends,3));setTimeout(function(){l()},d)}else l();r.sends++,this._conn.xmlOutput!==e.Connection.prototype.xmlOutput&&(r.xmlData.nodeName===this.strip&&r.xmlData.childNodes.length?this._conn.xmlOutput(r.xmlData.childNodes[0]):this._conn.xmlOutput(r.xmlData)),this._conn.rawOutput!==e.Connection.prototype.rawOutput&&this._conn.rawOutput(r.data)}else e.debug("_processRequest: "+(0===t?"first":"second")+" request has readyState of "+r.xhr.readyState)}},_removeRequest:function(t){var n;for(e.debug("removing request"),n=this._requests.length-1;n>=0;n--)t===this._requests[n]&&this._requests.splice(n,1);t.xhr.onreadystatechange=function(){},this._throttledRequestHandler()},_restartRequest:function(e){var t=this._requests[e];null===t.dead&&(t.dead=new Date),this._processRequest(e)},_reqToData:function(e){try{return e.getResponse()}catch(e){if("parsererror"!==e)throw e;this._conn.disconnect("strophe-parsererror")}},_sendTerminate:function(t){e.info("_sendTerminate was called");var n=this._buildBody().attrs({type:"terminate"});t&&n.cnode(t.tree());var r=new e.Request(n.tree(),this._onRequestStateChange.bind(this,this._conn._dataRecv.bind(this._conn)),n.tree().getAttribute("rid"));this._requests.push(r),this._throttledRequestHandler()},_send:function(){clearTimeout(this._conn._idleTimeout),this._throttledRequestHandler(),this._conn._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this._conn),100)},_sendRestart:function(){this._throttledRequestHandler(),clearTimeout(this._conn._idleTimeout)},_throttledRequestHandler:function(){this._requests?e.debug("_throttledRequestHandler called with "+this._requests.length+" requests"):e.debug("_throttledRequestHandler called with undefined requests"),this._requests&&0!==this._requests.length&&(this._requests.length>0&&this._processRequest(0),this._requests.length>1&&Math.abs(this._requests[0].rid-this._requests[1].rid): "+r);var i=t.getAttribute("version");return"string"!=typeof i?n="Missing version in ":"1.0"!==i&&(n="Wrong version in : "+i),!n||(this._conn._changeConnectStatus(e.Status.CONNFAIL,n),this._conn._doDisconnect(),!1)},_connect_cb_wrapper:function(t){if(0===t.data.indexOf("\s*)*/,"");if(""===n)return;var r=(new DOMParser).parseFromString(n,"text/xml").documentElement;this._conn.xmlInput(r),this._conn.rawInput(t.data),this._handleStreamStart(r)&&this._connect_cb(r)}else if(0===t.data.indexOf("=0&&o.indexOf("wss:")>=0||a.indexOf("ws:")>=0)&&(this._conn._changeConnectStatus(e.Status.REDIRECT,"Received see-other-uri, resetting connection"),this._conn.reset(),this._conn.service=o,this._connect())}else this._conn._changeConnectStatus(e.Status.CONNFAIL,"Received closing stream"),this._conn._doDisconnect()}else{var s=this._streamWrap(t.data),c=(new DOMParser).parseFromString(s,"text/xml").documentElement;this.socket.onmessage=this._onMessage.bind(this),this._conn._connect_cb(c,null,t.data)}},_disconnect:function(n){if(this.socket&&this.socket.readyState!==WebSocket.CLOSED){n&&this._conn.send(n);var r=t("close",{xmlns:e.NS.FRAMING});this._conn.xmlOutput(r.tree());var i=e.serialize(r);this._conn.rawOutput(i);try{this.socket.send(i)}catch(t){e.info("Couldn't send tag.")}}this._conn._doDisconnect()},_doDisconnect:function(){e.info("WebSockets _doDisconnect was called"),this._closeSocket()},_streamWrap:function(e){return""+e+""},_closeSocket:function(){if(this.socket)try{this.socket.onerror=null,this.socket.close()}catch(e){}this.socket=null},_emptyQueue:function(){return!0},_onClose:function(t){this._conn.connected&&!this._conn.disconnecting?(e.error("Websocket closed unexpectedly"),this._conn._doDisconnect()):t&&1006===t.code&&!this._conn.connected&&this.socket?(e.error("Websocket closed unexcectedly"),this._conn._changeConnectStatus(e.Status.CONNFAIL,"The WebSocket connection could not be established or was disconnected."),this._conn._doDisconnect()):e.info("Websocket closed")},_no_auth_received:function(t){e.error("Server did not offer a supported authentication mechanism"),this._changeConnectStatus(e.Status.CONNFAIL,e.ErrorCondition.NO_AUTH_MECH),t&&t.call(this._conn),this._conn._doDisconnect()},_onDisconnectTimeout:function(){},_abortAllRequests:function(){},_onError:function(t){e.error("Websocket error "+t),this._conn._changeConnectStatus(e.Status.CONNFAIL,"The WebSocket connection could not be established or was disconnected."),this._disconnect()},_onIdle:function(){var t=this._conn._data;if(t.length>0&&!this._conn.paused){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};e?(t="object"===v(e)?e:{name:e,properties:n},g.log(JSON.stringify(t)),this.analytics.sendEvent(e,n)):g.warn("No event or event name given.")},b.sendAnalytics=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.analytics.sendEvent(e,t)}}).call(this,"modules/statistics/statistics.js")},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(33),o=n.n(i),a=n(3);function s(e){return(function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t2?t[2]:null]},buildRTPMap:function(e){var t="a=rtpmap:".concat(e.getAttribute("id")," ").concat(e.getAttribute("name"),"/").concat(e.getAttribute("clockrate"));return e.getAttribute("channels")&&"1"!==e.getAttribute("channels")&&(t+="/".concat(e.getAttribute("channels"))),t},parseCrypto:function(e){var t={},n=e.substring(9).split(" ");return t.tag=n.shift(),t["crypto-suite"]=n.shift(),t["key-params"]=n.shift(),n.length&&(t["session-params"]=n.join(" ")),t},parseFingerprint:function(e){var t={},n=e.substring(14).split(" ");return t.hash=n.shift(),t.fingerprint=n.shift(),t},parseFmtp:function(e){var t=[],n=e.split(" ");n.shift(),n=n.join(" ").split(";");for(var r=0;r1&&0===n)){var r=null;if(1===t)r=e.ssrcs[0].id;else if(2===t){var i=e.ssrcGroups.find(function(e){return"FID"===e.semantics});i&&(r=i.ssrcs.split(" ")[0])}else if(t>=3){var o=e.ssrcGroups.find(function(e){return"SIM"===e.semantics});o&&(r=o.ssrcs.split(" ")[0])}return r}},generateSsrc:function(){return o.a.randomInt(1,4294967295)},getSsrcAttribute:function(e,t,n){for(var r=0;r0)return t[0].substr("a=ice-ufrag:".length)},preferVideoCodec:function(e,t){var n=null;if(e&&t){for(var r=0;r0){var l=n.map(function(e){return"apt=".concat(e)}),d=e.fmtp.filter(function(e){return-1!==l.indexOf(e.config)});n.push.apply(n,s(d.map(function(e){return e.payload})));var p=e.payloads.toString().split(" ").map(Number).filter(function(e){return-1===n.indexOf(e)});0===p.length?(e.port=0,e.direction="inactive",e.payloads="*"):e.payloads=p.join(" "),e.rtp=e.rtp.filter(function(e){return-1!==p.indexOf(e.payload)}),e.fmtp=e.fmtp.filter(function(e){return-1!==p.indexOf(e.payload)}),e.rtcpFb&&(e.rtcpFb=e.rtcpFb.filter(function(e){return-1!==p.indexOf(e.payload)}))}}}};t.a=u}).call(this,"modules/xmpp/SDPUtil.js")},function(e,t,n){"use strict";function r(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return{type:o,source:"get.user.media",action:e,attributes:t}},L=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:o,source:"peer.conn.status",action:"duration",attributes:e}},M=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{type:o,action:e,source:"jingle",attributes:t}},x=function(e,t){return{attributes:{media_type:e,value:t},action:"track.no.data.from.source",type:o}},j=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{type:o,action:e,source:"p2p",attributes:t}},F=function(){return{type:o,action:"remotely.muted"}},U=function(e){return{type:o,action:"rtp.stats",attributes:e}},H=function(e){return{type:o,action:"rtt.by.region",attributes:e}};function J(e,t,n){return{type:o,action:"audio.output.problem",attributes:{userID:e,localAudioLevels:t,remoteAudioLevels:n}}}var G=function(e,t){return{type:o,action:"bridge-channel.error",attributes:{code:e,reason:t}}},B=function(e){return I("ttfm",e)}},function(e,t){e.exports={CREATE_ANSWER_FAILED:"rtc.create_answer_failed",CREATE_OFFER_FAILED:"rtc.create_offer_failed",DATA_CHANNEL_OPEN:"rtc.data_channel_open",ENDPOINT_CONN_STATUS_CHANGED:"rtc.endpoint_conn_status_changed",DOMINANT_SPEAKER_CHANGED:"rtc.dominant_speaker_changed",LASTN_ENDPOINT_CHANGED:"rtc.lastn_endpoint_changed",GRANTED_PERMISSIONS:"rtc.granted_permissions",IS_SELECTED_CHANGED:"rtc.is_selected_change",LASTN_VALUE_CHANGED:"rtc.lastn_value_changed",LOCAL_TRACK_SSRC_UPDATED:"rtc.local_track_ssrc_updated",TRACK_ATTACHED:"rtc.track_attached",REMOTE_TRACK_ADDED:"rtc.remote_track_added",REMOTE_TRACK_MUTE:"rtc.remote_track_mute",REMOTE_TRACK_REMOVED:"rtc.remote_track_removed",REMOTE_TRACK_UNMUTE:"rtc.remote_track_unmute",SET_LOCAL_DESCRIPTION_FAILED:"rtc.set_local_description_failed",SET_REMOTE_DESCRIPTION_FAILED:"rtc.set_remote_description_failed",AUDIO_OUTPUT_DEVICE_CHANGED:"rtc.audio_output_device_changed",DEVICE_LIST_CHANGED:"rtc.device_list_changed",DEVICE_LIST_WILL_CHANGE:"rtc.device_list_will_change",DEVICE_LIST_AVAILABLE:"rtc.device_list_available",ENDPOINT_MESSAGE_RECEIVED:"rtc.endpoint_message_received",LOCAL_UFRAG_CHANGED:"rtc.local_ufrag_changed",REMOTE_UFRAG_CHANGED:"rtc.remote_ufrag_changed"}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return i});var r="audio",i="video"},function(e,t,n){"use strict";(function(e){var r=n(7),i=n(39),o=n.n(i),a=n(29),s=n.n(a),c=n(4),u=n(11),l=n.n(u),d=n(15),p=n(24),f=n(9),h=n(30),m=n.n(h),v=n(3),y=n(8),g=n.n(y),S=n(40),_=n(6),b=n(5),E=n(14),T=n.n(E);function C(e){return(C="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function R(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n={audio:!1,video:!1},r=v.a.isFirefox()||v.a.isSafariWithVP8()||v.a.isEdge()||v.a.isReactNative();if(e.indexOf("video")>=0){if(n.video={mandatory:{},optional:[]},t.cameraDeviceId)r&&(n.video.deviceId=t.cameraDeviceId),n.video.mandatory.sourceId=t.cameraDeviceId;else{var i=t.facingMode||o.a.USER;r&&(n.video.facingMode=i),n.video.optional.push({facingMode:i})}(t.minFps||t.maxFps||t.fps)&&((t.minFps||t.fps)&&(t.minFps=t.minFps||t.fps,n.video.mandatory.minFrameRate=t.minFps),t.maxFps&&(n.video.mandatory.maxFrameRate=t.maxFps)),(function(e,t,n){m.a[n]&&(t&&(e.video.width={ideal:m.a[n].width},e.video.height={ideal:m.a[n].height}),e.video.mandatory.minWidth=m.a[n].width,e.video.mandatory.minHeight=m.a[n].height),e.video.mandatory.minWidth&&(e.video.mandatory.maxWidth=e.video.mandatory.minWidth),e.video.mandatory.minHeight&&(e.video.mandatory.maxHeight=e.video.mandatory.minHeight)})(n,r,t.resolution)}if(e.indexOf("audio")>=0&&(v.a.isReactNative()?n.audio=!0:v.a.isFirefox()?t.micDeviceId?n.audio={mandatory:{},deviceId:t.micDeviceId,optional:[{sourceId:t.micDeviceId}]}:n.audio=!0:(n.audio={mandatory:{},optional:[]},t.micDeviceId&&(r&&(n.audio.deviceId=t.micDeviceId),n.audio.optional.push({sourceId:t.micDeviceId})),n.audio.optional.push({echoCancellation:!J&&!H},{googEchoCancellation:!J&&!H},{googAutoGainControl:!B&&!H},{googNoiseSuppression:!G&&!H},{googHighpassFilter:!V&&!H},{googNoiseSuppression2:!G&&!H},{googEchoCancellation2:!J&&!H},{googAutoGainControl2:!B&&!H}))),e.indexOf("screen")>=0)if(v.a.isChrome())n.video={mandatory:z(O({},t,{source:"screen"})),optional:[]};else if(v.a.isFirefox())n.video={mozMediaSource:"window",mediaSource:"window",frameRate:t.frameRate||{min:j,max:j}};else{var a="'screen' WebRTC media source is supported only in Chrome and Firefox";l.a.callErrorHandler(new Error(a)),P.error(a)}return e.indexOf("desktop")>=0&&(n.video={mandatory:z(O({},t,{source:"desktop"})),optional:[]}),t.bandwidth&&(n.video||(n.video={mandatory:{},optional:[]}),n.video.optional.push({bandwidth:t.bandwidth})),v.a.isFirefox()&&t.firefox_fake_device&&(n.fake=!0),n}function z(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.desktopStream,n=e.frameRate,r=void 0===n?{min:j,max:j}:n,i=r.max,o=r.min,a={chromeMediaSource:e.source,maxWidth:window.screen.width,maxHeight:window.screen.height};return"number"==typeof o&&(a.minFrameRate=o),"number"==typeof i&&(a.maxFrameRate=i),void 0!==t&&(a.chromeMediaSourceId=t),a}function $(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={frameRate:j},n=e.desktopSharingFrameRate;return n&&n.max&&(t.frameRate=n.max),t}function X(e,t){var n=Boolean(t)&&t.getAudioTracks().length>0,r=Boolean(t)&&t.getVideoTracks().length>0,i={};-1!==e.indexOf("video")&&(i.video=r),-1!==e.indexOf("audio")&&(i.audio=n),L.emit(g.a.GRANTED_PERMISSIONS,i)}function Q(e){var t=e.filter(function(e){return"audioinput"===e.kind}).length,n=e.filter(function(e){return"audiooutput"===e.kind}).length,i=e.filter(function(e){return"videoinput"===e.kind}).length,o=e.filter(function(e){return"videooutput"===e.kind}).length;e.forEach(function(e){var a={audio_input_device_count:t,audio_output_device_count:n,video_input_device_count:i,video_output_device_count:o,device_id:e.deviceId,device_group_id:e.groupId,device_kind:e.kind,device_label:e.label};b.a.sendAnalytics(r.l,a)})}var Y=new((function(e){function t(){return(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),A(this,w(t).call(this,L))}var n,r;return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&k(e,t)})(t,p.a),n=t,(r=[{key:"init",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if("boolean"==typeof t.disableAEC&&(J=t.disableAEC,P.info("Disable AEC: ".concat(J))),"boolean"==typeof t.disableNS&&(G=t.disableNS,P.info("Disable NS: ".concat(G))),"boolean"==typeof t.disableAP&&(H=t.disableAP,P.info("Disable AP: ".concat(H))),"boolean"==typeof t.disableAGC&&(B=t.disableAGC,P.info("Disable AGC: ".concat(B))),"boolean"==typeof t.disableHPF&&(V=t.disableHPF,P.info("Disable HPF: ".concat(V))),D=void 0,window.clearInterval(N),N=void 0,this.enumerateDevices=(function(){if(navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices)return function(e){navigator.mediaDevices.enumerateDevices().then(e,function(){return e([])})}})(),v.a.usesNewGumFlow())this.RTCPeerConnectionType=RTCPeerConnection,this.attachMediaStream=Z(function(e,t){e&&(e.srcObject=t)}),this.getStreamID=function(e){return e.id},this.getTrackID=function(e){return e.id};else{if(!v.a.isChromiumBased()&&!v.a.isReactNative()){var n="Endpoint does not appear to be WebRTC-capable";throw P.error(n),new Error(n)}this.RTCPeerConnectionType=RTCPeerConnection,this.attachMediaStream=Z(function(e,t){return(function(e,t){var n,r="srcObject";r in e||(r="mozSrcObject")in e||(r=null),r?e[r]=t:(t&&((n=t.jitsiObjectURL)||(t.jitsiObjectURL=n=URL.createObjectURL(t))),e.src=n||"")})(e,t),e}),this.getStreamID=function(e){var t=e.id;return"number"==typeof t?t:_.a.filterSpecialChars(t)},this.getTrackID=function(e){return e.id},MediaStream.prototype.getVideoTracks||(MediaStream.prototype.getVideoTracks=function(){return this.videoTracks}),MediaStream.prototype.getAudioTracks||(MediaStream.prototype.getAudioTracks=function(){return this.audioTracks})}this._initPCConstraints(t),S.a.init(t,this.getUserMediaWithConstraints.bind(this)),this.isDeviceListAvailable()&&this.enumerateDevices(function(t){D=t.splice(0),P.debug("Available devices: ",D),Q(D),L.emit(g.a.DEVICE_LIST_AVAILABLE,D);var n=function(){return e.enumerateDevices(function(e){var t;(function(e){return e.length!==D.length||e.map(t).sort().join("")!==D.map(t).sort().join("");function t(e){return JSON.stringify({kind:e.kind,deviceId:e.deviceId,groupId:e.groupId,label:e.label,facing:e.facing})}})(e)&&(D=(t=e).slice(0),P.info("list of media devices has changed:",D),Q(D),L.emit(g.a.DEVICE_LIST_WILL_CHANGE,t),L.emit(g.a.DEVICE_LIST_CHANGED,t))})};v.a.supportsDeviceChangeEvent()?navigator.mediaDevices.addEventListener("devicechange",n):N=window.setInterval(n,3e3)})}},{key:"_initPCConstraints",value:function(e){v.a.isFirefox()?this.pcConstraints={}:(v.a.isChromiumBased()||v.a.isReactNative())&&(this.pcConstraints={optional:[{googHighStartBitrate:0},{googPayloadPadding:!0},{googScreencastMinBitrate:100},{googCpuOveruseDetection:!0},{googCpuOveruseEncodeUsage:!0},{googCpuUnderuseThreshold:55},{googCpuOveruseThreshold:85}]},e.useIPv6&&this.pcConstraints.optional.push({googIPv6:!0}),this.p2pPcConstraints=JSON.parse(JSON.stringify(this.pcConstraints))),this.p2pPcConstraints=this.p2pPcConstraints||this.pcConstraints}},{key:"getUserMediaWithConstraints",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=W(e,t);return P.info("Get media constraints",n),new Promise(function(t,r){navigator.mediaDevices.getUserMedia(n).then(function(n){P.log("onUserMediaSuccess"),X(e,n),t(n)}).catch(function(t){P.warn("Failed to get access to local media. "+" ".concat(t," ").concat(n," ")),X(e,void 0),r(new d.a(t,n,e))})})}},{key:"_newGetUserMediaWithConstraints",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new Promise(function(n,r){navigator.mediaDevices.getUserMedia(t).then(function(t){P.log("onUserMediaSuccess"),X(e,t),n(t)}).catch(function(n){P.warn("Failed to get access to local media. "+" ".concat(n," ").concat(t," ")),X(e,void 0),r(new d.a(n,t,e))})})}},{key:"_newGetDesktopMedia",value:function(e){var t=this;return S.a.isSupported()&&v.a.supportsVideo()?new Promise(function(n,r){S.a.obtainStream(t._parseDesktopSharingOptions(e),function(e){n(e)},function(e){r(e)})}):Promise.reject(new Error("Desktop sharing is not supported!"))}},{key:"obtainAudioAndVideoPermissions",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e.devices=e.devices||M.concat(),e.resolution=e.resolution||720,e.devices.includes("desktop")&&!S.a.isSupported()?Promise.reject(new Error("Desktop sharing is not supported!")):this._getAudioAndVideoStreams(e).then(function(t){return(function(e,t){var n,r,i,o=[];if(e){var a=e.audioVideo;if(a){var s=a.getAudioTracks();if(s.length){n=new MediaStream;for(var c=0;c0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0,n=[],r=e.includes("audio"),i=t&&t.getAudioTracks().length>0;r&&!i&&n.push("audio");var o=e.includes("video"),a=t&&t.getVideoTracks().length>0;return o&&!a&&n.push("video"),n}},{key:"_parseDesktopSharingOptions",value:function(e){return O({},e.desktopSharingExtensionExternalInstallation,{desktopSharingSources:e.desktopSharingSources,gumOptions:{frameRate:e.desktopSharingFrameRate},trackOptions:$(e)})}},{key:"newObtainAudioAndVideoPermissions",value:function(e){var t=this;P.info("Using the new gUM flow");var n=[],r=function(){if(-1===(e.devices||[]).indexOf("desktop"))return Promise.resolve();var t=e.desktopSharingExtensionExternalInstallation,n=e.desktopSharingSourceDevice,r=e.desktopSharingSources,i=e.desktopSharingFrameRate;if(n){var o=D&&D.find(function(e){return"videoinput"===e.kind&&(e.deviceId===n||e.label===n)}),a=this._parseDesktopSharingOptions(e),s=a.gumOptions,c=a.trackOptions,u={video:O({},s,{deviceId:{exact:o&&o.deviceId||n}})};return this._newGetUserMediaWithConstraints(["video"],u).then(function(e){var t=e&&e.getTracks()[0];return(t&&t.applyConstraints?t.applyConstraints(c):Promise.resolve()).then(function(){return{sourceType:"device",stream:e}})})}return this._newGetDesktopMedia({desktopSharingExtensionExternalInstallation:t,desktopSharingSources:r,desktopSharingFrameRate:i})}.bind(this),i=function(){var t=(e.devices||["audio","video"]).filter(function(e){return"audio"===e||"video"===e&&v.a.supportsVideo()});if(!t.length)return Promise.resolve();var n=(function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=JSON.parse(JSON.stringify(t.constraints||x));if(e.indexOf("video")>=0)if(n.video||(n.video={}),t.cameraDeviceId)n.video.deviceId=t.cameraDeviceId;else{var r=t.facingMode||o.a.USER;n.video.facingMode=r}else n.video=!1;return e.indexOf("audio")>=0?(n.audio&&"boolean"!=typeof n.audio||(n.audio={}),n.audio.optional||(n.audio.optional=[]),n.audio.optional.push({sourceId:t.micDeviceId},{echoCancellation:!J&&!H},{googEchoCancellation:!J&&!H},{googAutoGainControl:!B&&!H},{googNoiseSuppression:!G&&!H},{googHighpassFilter:!V&&!H},{googNoiseSuppression2:!G&&!H},{googEchoCancellation2:!J&&!H},{googAutoGainControl2:!B&&!H})):n.audio=!1,e.indexOf("desktop")>=0&&(n.video&&"boolean"!=typeof n.video||(n.video={}),n.video={mandatory:z(O({},t,{source:"desktop"}))}),n})(t,e);return P.info("Got media constraints: ",n),this._newGetUserMediaWithConstraints(t,n)}.bind(this);return r().then(function(e){if(e){var t=e.stream,r=e.sourceId,i=e.sourceType;n.push({stream:t,sourceId:r,sourceType:i,track:t.getVideoTracks()[0],videoType:T.a.DESKTOP})}}).then(i).then(function(t){if(t){var r=t.getAudioTracks();if(r.length){var i=new MediaStream(r);n.push({stream:i,track:i.getAudioTracks()[0],effects:e.effects})}var o=t.getVideoTracks();if(o.length){var a=new MediaStream(o);n.push({stream:a,track:a.getVideoTracks()[0],videoType:T.a.CAMERA,effects:e.effects})}}}).then(function(){return n}).catch(function(e){return n.forEach(function(e){var n=e.stream;t.stopMediaStream(n)}),Promise.reject(e)})}},{key:"isDeviceListAvailable",value:function(){return Boolean(navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices)}},{key:"isDeviceChangeAvailable",value:function(e){return"output"===e||"audiooutput"===e?K:v.a.isChromiumBased()||v.a.isFirefox()||v.a.isEdge()}},{key:"stopMediaStream",value:function(e){if(e){e.getTracks().forEach(function(e){e.stop&&e.stop()}),e.stop&&e.stop(),e.release&&e.release();var t=e.jitsiObjectURL;t&&(delete e.jitsiObjectURL,URL.revokeObjectURL(t))}}},{key:"isDesktopSharingEnabled",value:function(){return S.a.isSupported()}},{key:"setAudioOutputDevice",value:function(e){return this.isDeviceChangeAvailable("output")?q.setSinkId(e).then(function(){F=e,U=!0,P.log("Audio output device set to ".concat(e)),L.emit(g.a.AUDIO_OUTPUT_DEVICE_CHANGED,e)}):Promise.reject(new Error("Audio output device change is not supported"))}},{key:"getAudioOutputDevice",value:function(){return F}},{key:"getCurrentlyAvailableMediaDevices",value:function(){return D}},{key:"getEventDataForActiveDevice",value:function(e){var t=[],n={deviceId:e.deviceId,kind:e.kind,label:e.label,groupId:e.groupId};return t.push(n),{deviceList:t}}},{key:"setSuspendVideo",value:function(e,t){e.optional||(e.optional=[]),e.optional=e.optional.filter(function(e){return!e.hasOwnProperty("googSuspendBelowMinBitrate")}),t&&e.optional.push({googSuspendBelowMinBitrate:"true"})}}])&&R(n.prototype,r),t})());function Z(e){return function(t,n){var r=e.apply(Y,arguments);return n&&Y.isDeviceChangeAvailable("output")&&n.getAudioTracks&&n.getAudioTracks().length&&U&&t.setSinkId(Y.getAudioOutputDevice()).catch(function(e){var n=new d.a(e,null,["audiooutput"]);l.a.callUnhandledRejectionHandler({promise:this,reason:n}),P.warn("Failed to set audio output device for the element. Default audio output device will be used instead",t,n)}),r}}t.a=Y}).call(this,"modules/RTC/RTCUtils.js")},function(e,t){var n=[],r=window.onerror,i=window.onunhandledrejection;window.onerror=function(){for(var e=arguments.length,t=new Array(e),i=0;i0&&void 0!==arguments[0]?arguments[0]:[]).map(function(e){var t=e.sourceId,n=e.sourceType,r=e.stream,i=e.track,o=e.videoType,a=e.effects,s=i.getSettings(),u=s.deviceId,d=s.facingMode;return k=Object(l.a)(k),new c.a({deviceId:u,facingMode:d,mediaType:i.kind,rtcId:k,sourceId:t,sourceType:n,stream:r,track:i,videoType:o||null,effects:a})})}var P=(function(e){function t(e){var n,r,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),this,(n=!(r=E(t).call(this))||"object"!==_(r)&&"function"!=typeof r?C(this):r).conference=e,n.peerConnections=new Map,n.localTracks=[],n.options=i,n._channel=null,n._channelOpen=!1,n._lastN=-1,n._lastNEndpoints=null,n._maxFrameHeight=void 0,n._pinnedEndpoint=null,n._selectedEndpoints=[],n._lastNChangeListener=n._onLastNChanged.bind(C(C(n))),n._onDeviceListChanged=n._onDeviceListChanged.bind(C(C(n))),n._updateAudioOutputForAudioTracks=n._updateAudioOutputForAudioTracks.bind(C(C(n))),m.a.isDeviceChangeAvailable("output")&&(m.a.addListener(h.a.AUDIO_OUTPUT_DEVICE_CHANGED,n._updateAudioOutputForAudioTracks),m.a.addListener(h.a.DEVICE_LIST_CHANGED,n._onDeviceListChanged)),n}var n,r,o;return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&T(e,t)})(t,u.a),n=t,o=[{key:"newCreateLocalTracks",value:function(e){return I(e)}},{key:"obtainAudioAndVideoPermissions",value:function(e){var t=p.a.usesNewGumFlow();return(t?m.a.newObtainAudioAndVideoPermissions(e):m.a.obtainAudioAndVideoPermissions(e)).then(function(n){return t?I(n):O(n,e)})}},{key:"addListener",value:function(e,t){m.a.addListener(e,t)}},{key:"removeListener",value:function(e,t){m.a.removeListener(e,t)}},{key:"init",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this.options=e,m.a.init(this.options)}},{key:"getPCConstraints",value:function(e){var t=e?m.a.p2pPcConstraints:m.a.pcConstraints;return t?JSON.parse(JSON.stringify(t)):{}}},{key:"attachMediaStream",value:function(e,t){return m.a.attachMediaStream(e,t)}},{key:"getStreamID",value:function(e){return m.a.getStreamID(e)}},{key:"getTrackID",value:function(e){return m.a.getTrackID(e)}},{key:"isDeviceListAvailable",value:function(){return m.a.isDeviceListAvailable()}},{key:"isDeviceChangeAvailable",value:function(e){return m.a.isDeviceChangeAvailable(e)}},{key:"isWebRtcSupported",value:function(){return p.a.isSupported()}},{key:"getAudioOutputDevice",value:function(){return m.a.getAudioOutputDevice()}},{key:"getCurrentlyAvailableMediaDevices",value:function(){return m.a.getCurrentlyAvailableMediaDevices()}},{key:"getEventDataForActiveDevice",value:function(e){return m.a.getEventDataForActiveDevice(e)}},{key:"setAudioOutputDevice",value:function(e){return m.a.setAudioOutputDevice(e)}},{key:"isUserStream",value:function(e){return t.isUserStreamById(m.a.getStreamID(e))}},{key:"isUserStreamById",value:function(e){return e&&"mixedmslabel"!==e&&"default"!==e}},{key:"enumerateDevices",value:function(e){m.a.enumerateDevices(e)}},{key:"stopMediaStream",value:function(e){m.a.stopMediaStream(e)}},{key:"isDesktopSharingEnabled",value:function(){return m.a.isDesktopSharingEnabled()}}],(r=[{key:"destroy",value:function(){m.a.removeListener(h.a.AUDIO_OUTPUT_DEVICE_CHANGED,this._updateAudioOutputForAudioTracks),m.a.removeListener(h.a.DEVICE_LIST_CHANGED,this._onDeviceListChanged),this.removeListener(h.a.LASTN_ENDPOINT_CHANGED,this._lastNChangeListener),this._channelOpenListener&&this.removeListener(h.a.DATA_CHANNEL_OPEN,this._channelOpenListener)}},{key:"initializeBridgeChannel",value:function(e,t){var n=this;this._channel=new i.a(e,t,this.eventEmitter),this._channelOpenListener=function(){n._channelOpen=!0;try{n._channel.sendPinnedEndpointMessage(n._pinnedEndpoint),n._channel.sendSelectedEndpointsMessage(n._selectedEndpoints),void 0!==n._maxFrameHeight&&n._channel.sendReceiverVideoConstraintMessage(n._maxFrameHeight)}catch(e){a.a.callErrorHandler(e),A.error("Cannot send selected(".concat(n._selectedEndpoint,")")+"pinned(".concat(n._pinnedEndpoint,")")+"frameHeight(".concat(n._maxFrameHeight,") endpoint message"),e)}n.removeListener(h.a.DATA_CHANNEL_OPEN,n._channelOpenListener),n._channelOpenListener=null,-1!==n._lastN&&n._channel.sendSetLastNMessage(n._lastN)},this.addListener(h.a.DATA_CHANNEL_OPEN,this._channelOpenListener),this.addListener(h.a.LASTN_ENDPOINT_CHANGED,this._lastNChangeListener)}},{key:"_onDeviceListChanged",value:function(){this._updateAudioOutputForAudioTracks(m.a.getAudioOutputDevice())}},{key:"_onLastNChanged",value:function(){var e,t,n=this,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],i=this._lastNEndpoints||[];this._lastNEndpoints=r,e=i.filter(function(e){return!n.isInLastN(e)}),t=r.filter(function(e){return-1===i.indexOf(e)}),this.conference.eventEmitter.emit(s.LAST_N_ENDPOINTS_CHANGED,e,t)}},{key:"onCallEnded",value:function(){this._channel&&(this._channel&&"websocket"===this._channel.mode&&this._channel.close(),this._channel=null,this._channelOpen=!1)}},{key:"setReceiverVideoConstraint",value:function(e){this._maxFrameHeight=e,this._channel&&this._channelOpen&&this._channel.sendReceiverVideoConstraintMessage(e)}},{key:"selectEndpoints",value:function(e){this._selectedEndpoints=e,this._channel&&this._channelOpen&&this._channel.sendSelectedEndpointsMessage(e)}},{key:"pinEndpoint",value:function(e){this._pinnedEndpoint=e,this._channel&&this._channelOpen&&this._channel.sendPinnedEndpointMessage(e)}},{key:"createPeerConnection",value:function(e,n,r,i){var o=t.getPCConstraints(r);void 0!==i.abtestSuspendVideo&&(m.a.setSuspendVideo(o,i.abtestSuspendVideo),v.a.analytics.addPermanentProperties({abtestSuspendVideo:i.abtestSuspendVideo})),p.a.supportsSdpSemantics()&&(n.sdpSemantics="plan-b"),w=Object(l.a)(w);var a=new y.a(this,w,e,n,o,r,i);return this.peerConnections.set(a.id,a),a}},{key:"_removePeerConnection",value:function(e){var t=e.id;return!!this.peerConnections.has(t)&&(this.peerConnections.delete(t),!0)}},{key:"addLocalTrack",value:function(e){if(!e)throw new Error("track must not be null nor undefined");this.localTracks.push(e),e.conference=this.conference}},{key:"getLastN",value:function(){return this._lastN}},{key:"getLocalVideoTrack",value:function(){var e=this.getLocalTracks(d.b);return e.length?e[0]:void 0}},{key:"getLocalAudioTrack",value:function(){var e=this.getLocalTracks(d.a);return e.length?e[0]:void 0}},{key:"getLocalTracks",value:function(e){var t=this.localTracks.slice();return void 0!==e&&(t=t.filter(function(t){return t.getType()===e})),t}},{key:"getRemoteTracks",value:function(e){var t=[],n=!0,r=!1,i=void 0;try{for(var o,a=this.peerConnections.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value.getRemoteTracks(void 0,e);s&&(t=t.concat(s))}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}return t}},{key:"setAudioMute",value:function(e){var t=[];return this.getLocalTracks(d.a).forEach(function(n){t.push(e?n.mute():n.unmute())}),Promise.all(t)}},{key:"removeLocalTrack",value:function(e){var t=this.localTracks.indexOf(e);-1!==t&&this.localTracks.splice(t,1)}},{key:"removeRemoteTracks",value:function(e){var t=[],n=!0,r=!1,i=void 0;try{for(var o,a=this.peerConnections.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value.removeRemoteTracks(e);t=t.concat(s)}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}return A.debug("Removed remote tracks for ".concat(e)+" count: ".concat(t.length)),t}},{key:"closeBridgeChannel",value:function(){this._channel&&(this._channel.close(),this._channelOpen=!1,this.removeListener(h.a.LASTN_ENDPOINT_CHANGED,this._lastNChangeListener))}},{key:"setAudioLevel",value:function(e,t,n,r){var i=e.getTrackBySSRC(t);i&&(i.isAudioTrack()?(i.isLocal()!==r&&A.error("".concat(i," was expected to ").concat(r?"be":"not be"," local")),i.setAudioLevel(n,e)):A.warn("Received audio level for non-audio track: ".concat(t)))}},{key:"sendChannelMessage",value:function(e,t){if(!this._channel)throw new Error("Channel support is disabled!");this._channel.sendMessage(e,t)}},{key:"setLastN",value:function(e){this._lastN!==e&&(this._lastN=e,this._channel&&this._channelOpen&&this._channel.sendSetLastNMessage(e),this.eventEmitter.emit(h.a.LASTN_VALUE_CHANGED,e))}},{key:"isInLastN",value:function(e){return!this._lastNEndpoints||this._lastNEndpoints.indexOf(e)>-1}},{key:"_updateAudioOutputForAudioTracks",value:function(e){var t=this.getRemoteTracks(d.a),n=!0,r=!1,i=void 0;try{for(var o,a=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0)o.value.setAudioOutput(e)}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}}}])&&b(n.prototype,r),o&&b(n,o),t})()}).call(this,"modules/RTC/RTC.js")},function(e,t){e.exports={CAMERA:"camera",DESKTOP:"desktop"}},function(e,t,n){"use strict";var r=n(12);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var o={};function a(e,t,n){if("object"===i(e)&&void 0!==e.name)switch(this.gum={error:e,constraints:t,devices:n&&Array.isArray(n)?n.slice(0):void 0},e.name){case"NotAllowedError":case"PermissionDeniedError":case"SecurityError":this.name=r.PERMISSION_DENIED,this.message=o[this.name]+(this.gum.devices||[]).join(", ");break;case"DevicesNotFoundError":case"NotFoundError":this.name=r.NOT_FOUND,this.message=o[this.name]+(this.gum.devices||[]).join(", ");break;case"ConstraintNotSatisfiedError":case"OverconstrainedError":var a=e.constraintName||e.constraint;t&&t.video&&(!n||n.indexOf("video")>-1)&&("minWidth"===a||"maxWidth"===a||"minHeight"===a||"maxHeight"===a||"width"===a||"height"===a||"deviceId"===a)?(this.name=r.UNSUPPORTED_RESOLUTION,this.message=o[this.name]+(function(e,t){if(t&&t.video&&t.video.mandatory)switch(e){case"width":return t.video.mandatory.minWidth;case"height":return t.video.mandatory.minHeight;default:return t.video.mandatory[e]||""}return""})(a,t)):(this.name=r.CONSTRAINT_FAILED,this.message=o[this.name]+e.constraintName);break;default:this.name=r.GENERAL,this.message=e.message||o[this.name]}else{if("string"!=typeof e)throw new Error("Invalid arguments");o[e]?(this.name=e,this.message=t||o[e]):this.message=e}this.stack=e.stack||(new Error).stack}o[r.UNSUPPORTED_RESOLUTION]="Video resolution is not supported: ",o[r.CHROME_EXTENSION_INSTALLATION_ERROR]="Failed to install Chrome extension",o[r.CHROME_EXTENSION_USER_GESTURE_REQUIRED]="Failed to install Chrome extension - installations can only be initiated by a user gesture.",o[r.CHROME_EXTENSION_USER_CANCELED]="User canceled Chrome's screen sharing prompt",o[r.CHROME_EXTENSION_GENERIC_ERROR]="Unknown error from Chrome extension",o[r.ELECTRON_DESKTOP_PICKER_ERROR]="Unkown error from desktop picker",o[r.ELECTRON_DESKTOP_PICKER_NOT_FOUND]="Failed to detect desktop picker",o[r.GENERAL]="Generic getUserMedia error",o[r.PERMISSION_DENIED]="User denied permission to use device(s): ",o[r.NOT_FOUND]="Requested device(s) was/were not found: ",o[r.CONSTRAINT_FAILED]="Constraint could not be satisfied: ",o[r.TRACK_IS_DISPOSED]="Track has been already disposed",o[r.TRACK_NO_STREAM_FOUND]="Track does not have an associated Media Stream",a.prototype=Object.create(Error.prototype),a.prototype.constructor=a,t.a=a},function(e,t,n){"use strict";n.r(t),n.d(t,"LOCAL_TRACK_STOPPED",function(){return r}),n.d(t,"TRACK_AUDIO_LEVEL_CHANGED",function(){return i}),n.d(t,"TRACK_AUDIO_OUTPUT_CHANGED",function(){return o}),n.d(t,"TRACK_MUTE_CHANGED",function(){return a}),n.d(t,"TRACK_VIDEOTYPE_CHANGED",function(){return s}),n.d(t,"NO_DATA_FROM_SOURCE",function(){return c});var r="track.stopped",i="track.audioLevelsChanged",o="track.audioOutputChanged",a="track.trackMuteChanged",s="track.videoTypeChanged",c="track.no_data_from_source"},function(e,t,n){"use strict";n.r(t),n.d(t,"AUTHENTICATION_REQUIRED",function(){return r}),n.d(t,"CHAT_ERROR",function(){return i}),n.d(t,"CONFERENCE_DESTROYED",function(){return o}),n.d(t,"CONFERENCE_MAX_USERS",function(){return a}),n.d(t,"CONNECTION_ERROR",function(){return s}),n.d(t,"NOT_ALLOWED_ERROR",function(){return c}),n.d(t,"FOCUS_DISCONNECTED",function(){return u}),n.d(t,"FOCUS_LEFT",function(){return l}),n.d(t,"GRACEFUL_SHUTDOWN",function(){return d}),n.d(t,"INCOMPATIBLE_SERVER_VERSIONS",function(){return p}),n.d(t,"OFFER_ANSWER_FAILED",function(){return f}),n.d(t,"PASSWORD_NOT_SUPPORTED",function(){return h}),n.d(t,"PASSWORD_REQUIRED",function(){return m}),n.d(t,"RESERVATION_ERROR",function(){return v}),n.d(t,"SETUP_FAILED",function(){return y}),n.d(t,"VIDEOBRIDGE_NOT_AVAILABLE",function(){return g});var r="conference.authenticationRequired",i="conference.chatError",o="conference.destroyed",a="conference.max_users",s="conference.connectionError",c="conference.connectionError.notAllowed",u="conference.focusDisconnected",l="conference.focusLeft",d="conference.gracefulShutdown",p="conference.incompatible_server_versions",f="conference.offerAnswerFailed",h="conference.passwordNotSupported",m="conference.passwordRequired",v="conference.reservationError",y="conference.setup_failed",g="conference.videobridgeNotAvailable"},function(e,t,n){"use strict";n.d(t,"a",function(){return o});var r=n(6);function i(e,t){return(function(e){if(Array.isArray(e))return e})(e)||(function(e,t){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){i=!0,o=e}finally{try{r||null==s.return||s.return()}finally{if(i)throw o}}return n})(e,t)||(function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")})()}function o(e){for(var t=e.split("\r\nm="),n=1,r=t.length;n2&&t.attrs({streams:a[2]}),t.up()}if(r.a.findLines(this.media[e],"a=fingerprint:",this.session).forEach(function(o){(n=r.a.parseFingerprint(o)).xmlns="urn:xmpp:jingle:apps:dtls:0",t.c("fingerprint").t(n.fingerprint),delete n.fingerprint,(o=r.a.findLine(i.media[e],"a=setup:",i.session))&&(n.setup=o.substr(8)),t.attrs(n),t.up()}),n=r.a.iceparams(this.media[e],this.session)){n.xmlns="urn:xmpp:jingle:transports:ice-udp:1",t.attrs(n);var s=r.a.findLines(this.media[e],"a=candidate:",this.session);s.length&&s.forEach(function(e){var n=r.a.candidateToJingle(e);i.failICE&&(n.ip="1.1.1.1");var o=n&&"string"==typeof n.protocol?n.protocol.toLowerCase():"";i.removeTcpCandidates&&("tcp"===o||"ssltcp"===o)||i.removeUdpCandidates&&"udp"===o||t.c("candidate",n).up()})}t.up()},o.prototype.rtcpFbToJingle=function(e,t,n){r.a.findLines(this.media[e],"a=rtcp-fb:".concat(n)).forEach(function(e){var n=r.a.parseRTCPFB(e);"trr-int"===n.type?(t.c("rtcp-fb-trr-int",{xmlns:"urn:xmpp:jingle:apps:rtp:rtcp-fb:0",value:n.params[0]}),t.up()):(t.c("rtcp-fb",{xmlns:"urn:xmpp:jingle:apps:rtp:rtcp-fb:0",type:n.type}),n.params.length>0&&t.attrs({subtype:n.params[0]}),t.up())})},o.prototype.rtcpFbFromJingle=function(e,t){var n="",r=e.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');return r.length&&(n+="a=rtcp-fb:* trr-int ",r.attr("value")?n+=r.attr("value"):n+="0",n+="\r\n"),(r=e.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]')).each(function(){n+="a=rtcp-fb:".concat(t," ").concat($(this).attr("type")),$(this).attr("subtype")&&(n+=" ".concat($(this).attr("subtype"))),n+="\r\n"}),n},o.prototype.fromJingle=function(e){var t=this;this.raw="v=0\r\no=- 1923518516 2 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\n";var n=$(e).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]');n.length&&n.each(function(e,n){var r=$(n).find(">content").map(function(e,t){return t.getAttribute("name")}).get();r.length>0&&(t.raw+="a=group:".concat(n.getAttribute("semantics")||n.getAttribute("type")," ").concat(r.join(" "),"\r\n"))}),this.session=this.raw,e.find(">content").each(function(){var e=t.jingle2media($(this));t.media.push(e)}),this.raw=this.session+this.media.join("")},o.prototype.jingle2media=function(e){var t=e.find("description"),n="",i=this,o=e.find('>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]'),a={media:t.attr("media"),port:"1"};if("rejected"===e.attr("senders")&&(a.port="0"),e.find(">transport>fingerprint").length||t.find("encryption").length?a.proto=o.length?"DTLS/SCTP":"RTP/SAVPF":a.proto="RTP/AVPF",o.length){n+="m=application ".concat(a.port," DTLS/SCTP ").concat(o.attr("number"),"\r\n"),n+="a=sctpmap:".concat(o.attr("number")," ").concat(o.attr("protocol"));var s=o.attr("streams");n+=s?" ".concat(s,"\r\n"):"\r\n"}else a.fmt=t.find("payload-type").map(function(){return this.getAttribute("id")}).get(),n+="".concat(r.a.buildMLine(a),"\r\n");switch(n+="c=IN IP4 0.0.0.0\r\n",o.length||(n+="a=rtcp:1 IN IP4 0.0.0.0\r\n"),(a=e.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]')).length&&(a.attr("ufrag")&&(n+="".concat(r.a.buildICEUfrag(a.attr("ufrag")),"\r\n")),a.attr("pwd")&&(n+="".concat(r.a.buildICEPwd(a.attr("pwd")),"\r\n")),a.find(">fingerprint").each(function(){n+="a=fingerprint:".concat(this.getAttribute("hash")),n+=" ".concat($(this).text()),n+="\r\n",this.getAttribute("setup")&&(n+="a=setup:".concat(this.getAttribute("setup"),"\r\n"))})),e.attr("senders")){case"initiator":n+="a=sendonly\r\n";break;case"responder":n+="a=recvonly\r\n";break;case"none":n+="a=inactive\r\n";break;case"both":n+="a=sendrecv\r\n"}return n+="a=mid:".concat(e.attr("name"),"\r\n"),t.find("rtcp-mux").length&&(n+="a=rtcp-mux\r\n"),t.find("encryption").length&&t.find("encryption>crypto").each(function(){n+="a=crypto:".concat(this.getAttribute("tag")),n+=" ".concat(this.getAttribute("crypto-suite")),n+=" ".concat(this.getAttribute("key-params")),this.getAttribute("session-params")&&(n+=" ".concat(this.getAttribute("session-params"))),n+="\r\n"}),t.find("payload-type").each(function(){n+="".concat(r.a.buildRTPMap(this),"\r\n"),$(this).find(">parameter").length&&(n+="a=fmtp:".concat(this.getAttribute("id")," "),n+=$(this).find("parameter").map(function(){var e=this.getAttribute("name");return(e?"".concat(e,"="):"")+this.getAttribute("value")}).get().join("; "),n+="\r\n"),n+=i.rtcpFbFromJingle($(this),this.getAttribute("id"))}),n+=i.rtcpFbFromJingle(t,"*"),(a=t.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]')).each(function(){n+="a=extmap:".concat(this.getAttribute("id")," ").concat(this.getAttribute("uri"),"\r\n")}),e.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function(){var e=this.getAttribute("protocol");e="string"==typeof e?e.toLowerCase():"",i.removeTcpCandidates&&("tcp"===e||"ssltcp"===e)||i.removeUdpCandidates&&"udp"===e||(i.failICE&&this.setAttribute("ip","1.1.1.1"),n+=r.a.candidateFromJingle(this))}),e.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(n+="a=ssrc-group:".concat(e," ").concat(t.join(" "),"\r\n"))}),(a=e.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')).each(function(){var e=this.getAttribute("ssrc");$(this).find(">parameter").each(function(){var t=this.getAttribute("name"),i=this.getAttribute("value");i=r.a.filterSpecialChars(i),n+="a=ssrc:".concat(e," ").concat(t),i&&i.length&&(n+=":".concat(i)),n+="\r\n"})}),n}},function(e,t,n){"use strict";var r={};n.r(r),n.d(r,"CHROME",function(){return a}),n.d(r,"OPERA",function(){return s}),n.d(r,"FIREFOX",function(){return c}),n.d(r,"INTERNET_EXPLORER",function(){return u}),n.d(r,"EDGE",function(){return l}),n.d(r,"SAFARI",function(){return d}),n.d(r,"NWJS",function(){return p}),n.d(r,"ELECTRON",function(){return f}),n.d(r,"REACT_NATIVE",function(){return h}),n.d(r,"UNKNOWN",function(){return m});var i=n(46),o=n.n(i),a="chrome",s="opera",c="firefox",u="iexplorer",l="edge",d="safari",p="nwjs",f="electron",h="react-native",m="unknown";function v(e,t){for(var n=0;n2&&(t=n[1],e=n[2]),t||(t="react-native"),e||(e="unknown"),{name:h,version:e}}function b(){for(var e,t=[_,g,S],n=0;n0&&void 0!==arguments[0]?arguments[0]:new i.a;!(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this.eventEmitter=t,this.addEventListener=this.on=this.addListener,this.removeEventListener=this.off=this.removeListener}var t,n;return t=e,(n=[{key:"addListener",value:function(e,t){this.eventEmitter.addListener(e,t)}},{key:"removeListener",value:function(e,t){this.eventEmitter.removeListener(e,t)}}])&&o(t.prototype,n),e})()},function(e,t,n){"use strict";(function(e){var r,i,o=n(4),a=n(74),s=n.n(a),c=Object(o.getLogger)(e);function u(){var e;try{e=(window||this).localStorage}catch(e){c.error(e)}return e}function l(){return"".concat(Math.random().toString(16),"000000000").substr(2,8)}t.a={get callStatsUserName(){if(!r){var e=u();e&&(r=e.getItem("callStatsUserName")),r||(t=s.a.generateUsername(),c.log("generated callstats uid",t),r=t,e&&e.setItem("callStatsUserName",r))}var t;return r},get machineId(){if(!i){var e=u();e&&(i=e.getItem("jitsiMeetId")),i||(t=l()+l()+l()+l(),c.log("generated id",t),i=t,e&&e.setItem("jitsiMeetId",i))}var t;return i},get sessionId(){var e=u();return e?e.getItem("sessionId"):void 0},set sessionId(e){var t=u();t&&(e?t.setItem("sessionId",e):t.removeItem("sessionId"))}}}).call(this,"modules/settings/Settings.js")},function(e,t,n){"use strict";n.r(t),n.d(t,"LOCAL_STATS_UPDATED",function(){return r}),n.d(t,"REMOTE_STATS_UPDATED",function(){return i});var r="cq.local_stats_updated",i="cq.remote_stats_updated"},function(e,t,n){"use strict";n.d(t,"b",function(){return s}),n.d(t,"c",function(){return c}),n.d(t,"a",function(){return d});var r=n(22);function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){for(var n=0;n0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(e,t){if(!i(t))throw TypeError("listener must be a function");var n=!1;function r(){this.removeListener(e,r),n||(n=!0,t.apply(this,arguments))}return r.listener=t,this.on(e,r),this},r.prototype.removeListener=function(e,t){var n,r,a,s;if(!i(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(a=(n=this._events[e]).length,r=-1,n===t||i(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(s=a;s-- >0;)if(n[s]===t||n[s].listener&&n[s].listener===t){r=s;break}if(r<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(r,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},r.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(i(n=this._events[e]))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},r.prototype.listeners=function(e){return this._events&&this._events[e]?i(this._events[e])?[this._events[e]]:this._events[e].slice():[]},r.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(i(t))return 1;if(t)return t.length}return 0},r.listenerCount=function(e,t){return e.listenerCount(t)}},function(e,t){e.exports={1080:{width:1920,height:1080,order:8},fullhd:{width:1920,height:1080,order:8},720:{width:1280,height:720,order:7},hd:{width:1280,height:720,order:7},960:{width:960,height:720,order:6},540:{width:960,height:540,order:5},qhd:{width:960,height:540,order:5},360:{width:640,height:360,order:4},640:{width:640,height:480,order:3},vga:{width:640,height:480,order:3},180:{width:320,height:180,order:2},320:{width:320,height:240,order:1}}},function(e,t,n){"use strict";n.d(t,"c",function(){return r}),n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var r="pending",i="active",o="ended"},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return m});var r=n(3),i=n(11),o=n.n(i);function a(e,t){for(var n=0;n3?a-3:0),c=3;c0&&void 0!==arguments[0]?arguments[0]:function e(){u(this,e)};return(function(t){function n(){var e,t;u(this,n);for(var r=arguments.length,i=new Array(r),o=0;o1&&void 0!==arguments[1]?arguments[1]:"/http-bind";e&&(t+="".concat(-1===t.indexOf("?")?"?":"&","token=").concat(e));var n=new i.Strophe.Connection(t);return n.maxRetries=3,n})(n,e.bosh),r._lastSuccessTracker=new v.a,r._lastSuccessTracker.startTracking(r.connection),r.caps=new g.a(r.connection,r.options.clientNode),r.initFeaturesList(),$(window).on("beforeunload unload",function(e){r.disconnect(e).catch(function(){})}),r}var n,r;return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&A(e,t)})(t,y.a),n=t,(r=[{key:"initFeaturesList",value:function(){this.caps.addFeature("urn:xmpp:jingle:1"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:1"),this.caps.addFeature("urn:xmpp:jingle:transports:ice-udp:1"),this.caps.addFeature("urn:xmpp:jingle:apps:dtls:0"),this.caps.addFeature("urn:xmpp:jingle:transports:dtls-sctp:1"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:audio"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:video"),!this.options.disableRtx&&u.a.supportsRtx()&&this.caps.addFeature("urn:ietf:rfc:4588"),this.caps.addFeature("urn:ietf:rfc:5761"),this.caps.addFeature("urn:ietf:rfc:5888"),u.a.isChrome()&&!1!==this.options.enableLipSync&&(k.info("Lip-sync enabled !"),this.caps.addFeature("http://jitsi.org/meet/lipsync")),this.connection.rayo&&this.caps.addFeature("urn:xmpp:rayo:client:1")}},{key:"isPingSupported",value:function(){return!1!==this._pingSupported}},{key:"getConnection",value:function(){return this.connection}},{key:"connectionHandler",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1?arguments[1]:void 0,r=arguments.length>2?arguments[2]:void 0,o=window.performance.now(),a=i.Strophe.getStatusString(n).toLowerCase();if(this.connectionTimes[a]=o,k.log("(TIME) Strophe ".concat(a).concat(r?"[".concat(r,"]"):"",":\t"),o),this.eventEmitter.emit(E.a.CONNECTION_STATUS_CHANGED,t,n,r),n===i.Strophe.Status.CONNECTED||n===i.Strophe.Status.ATTACHED){(this.options.useStunTurn||this.options.p2p&&this.options.p2p.useStunTurn)&&this.connection.jingle.getStunAndTurnCredentials(),k.info("My Jabber ID: ".concat(this.connection.jid));var u=this.connection.domain;this.caps.getFeaturesAndIdentities(u).then(function(t){var n=t.features,r=t.identities;n.has(i.Strophe.NS.PING)?(e._pingSupported=!0,e.connection.ping.startInterval(u)):k.warn("Ping NOT supported by ".concat(u)),r.forEach(function(t){"speakerstats"===t.type&&(e.speakerStatsComponentAddress=t.name,e.connection.addHandler(e._onPrivateMessage.bind(e),null,"message",null,null))})}).catch(function(e){var t="Feature discovery error";_.a.callErrorHandler(new Error("".concat(t,": ").concat(e))),k.error(t,e)}),t.password&&(this.authenticatedUser=!0),this.connection&&this.connection.connected&&i.Strophe.getResourceFromJid(this.connection.jid)&&this.eventEmitter.emit(c.CONNECTION_ESTABLISHED,i.Strophe.getResourceFromJid(this.connection.jid))}else if(n===i.Strophe.Status.CONNFAIL)"x-strophe-bad-non-anon-jid"===r?this.anonymousConnectionFailed=!0:this.connectionFailed=!0,this.lastErrorMsg=r,"giving-up"===r&&this.eventEmitter.emit(c.CONNECTION_FAILED,s.OTHER_ERROR,r);else if(n===i.Strophe.Status.DISCONNECTED){this.connection.ping.stopInterval();var l=this.disconnectInProgress,d=r||this.lastErrorMsg;if(this.disconnectInProgress=!1,this.anonymousConnectionFailed)this.eventEmitter.emit(c.CONNECTION_FAILED,s.PASSWORD_REQUIRED);else if(this.connectionFailed)this.eventEmitter.emit(c.CONNECTION_FAILED,s.OTHER_ERROR,d,void 0,this._getConnectionFailedReasonDetails());else if(l)this.eventEmitter.emit(c.CONNECTION_DISCONNECTED,d);else{k.error("XMPP connection dropped!");var p=i.Strophe.getLastErrorStatus();p>=500&&p<600?this.eventEmitter.emit(c.CONNECTION_FAILED,s.SERVER_ERROR,d||"server-error",void 0,this._getConnectionFailedReasonDetails()):this.eventEmitter.emit(c.CONNECTION_FAILED,s.CONNECTION_DROPPED_ERROR,d||"connection-dropped-error",void 0,this._getConnectionFailedReasonDetails())}}else n===i.Strophe.Status.AUTHFAIL&&this.eventEmitter.emit(c.CONNECTION_FAILED,s.PASSWORD_REQUIRED,r,t)}},{key:"_connect",value:function(e,t){this.anonymousConnectionFailed=!1,this.connectionFailed=!1,this.lastErrorMsg=void 0,this.connection.connect(e,t,this.connectionHandler.bind(this,{jid:e,password:t}))}},{key:"attach",value:function(e){var t=this.connectionTimes.attaching=window.performance.now();k.log("(TIME) Strophe Attaching\t:".concat(t)),this.connection.attach(e.jid,e.sid,parseInt(e.rid,10)+1,this.connectionHandler.bind(this,{jid:e.jid,password:e.password}))}},{key:"connect",value:function(e,t){if(!e){var n=this.options.hosts,r=n.anonymousdomain,i=n.domain,o=r||i,a=window.location;if(r){var s=a&&a.search;(s&&-1!==s.indexOf("login=true")||this.token)&&(o=i)}e=o||a&&a.hostname}return this._connect(e,t)}},{key:"createRoom",value:function(e,t,n){var r="".concat(e,"@").concat(this.options.hosts.muc,"/"),i=n?n(this.connection.jid,this.authenticatedUser):a.a.randomHexString(8).toLowerCase();return k.info("JID ".concat(this.connection.jid," using MUC nickname ").concat(i)),r+=i,this.connection.emuc.createRoom(r,null,t)}},{key:"getJid",value:function(){return this.connection.jid}},{key:"getJingleLog",value:function(){var e=this.connection.jingle;return e?e.getLog():{}}},{key:"getXmppLog",value:function(){return(this.connection.logger||{}).log||null}},{key:"dial",value:function(){var e;(e=this.connection.rayo).dial.apply(e,arguments)}},{key:"ping",value:function(e){var t=this;return new Promise(function(n,r){t.isPingSupported()?t.connection.ping.ping(t.connection.domain,n,r,e):r("PING operation is not supported by the server")})}},{key:"getSessions",value:function(){return this.connection.jingle.sessions}},{key:"disconnect",value:function(e){var t=this;return this.disconnectInProgress||!this.connection?(this.eventEmitter.emit(c.WRONG_STATE),Promise.reject(new Error("Wrong connection state!"))):(this.disconnectInProgress=!0,new Promise(function(n){if(t.eventEmitter.on(E.a.CONNECTION_STATUS_CHANGED,function e(r,o){o===i.Strophe.Status.DISCONNECTED&&(n(),t.eventEmitter.removeListener(E.a.CONNECTION_STATUS_CHANGED,e))}),t.connection.flush(),null!=e){var r=e.type;if(("beforeunload"===r||"unload"===r)&&(t.connection.options.sync=!0,navigator.sendBeacon&&!t.connection.disconnecting&&t.connection.connected)){t.connection._changeConnectStatus(i.Strophe.Status.DISCONNECTING),t.connection.disconnecting=!0;var o=t.connection._proto._buildBody().attrs({type:"terminate"}),a=Object(i.$pres)({xmlns:i.Strophe.NS.CLIENT,type:"unavailable"});o.cnode(a.tree());var s=navigator.sendBeacon("https:".concat(t.connection.service),i.Strophe.serialize(o.tree()));return k.info("Successfully send unavailable beacon ".concat(s)),t.connection._proto._abortAllRequests(),void t.connection._doDisconnect()}}t.connection.disconnect(),!0!==t.connection.options.sync&&t.connection.flush()}))}},{key:"_initStrophePlugins",value:function(){var e={jvb:{iceServers:[]},p2p:{iceServers:[]}},t=this.options.p2p&&this.options.p2p.stunServers||O;Array.isArray(t)&&(k.info("P2P STUN servers: ",t),e.p2p.iceServers=t),this.options.p2p&&this.options.p2p.iceTransportPolicy&&(k.info("P2P ICE transport policy: ",this.options.p2p.iceTransportPolicy),e.p2p.iceTransportPolicy=this.options.p2p.iceTransportPolicy),Object(l.a)(this),Object(d.a)(this,this.eventEmitter,e),Object(p.a)(),Object(f.a)(this),Object(h.a)(),Object(m.a)()}},{key:"_getConnectionFailedReasonDetails",value:function(){var e={};if(this.options.deploymentInfo&&this.options.deploymentInfo.shard&&this.connection._proto&&this.connection._proto.lastResponseHeaders){var t={};this.connection._proto.lastResponseHeaders.trim().split(/[\r\n]+/).forEach(function(e){var n=e.split(": "),r=n.shift(),i=n.join(": ");t[r]=i}),e.shard_changed=this.options.deploymentInfo.shard!==t["x-jitsi-shard"]}return e.suspend_time=this.connection.ping.getPingSuspendTime(),e.time_since_last_success=this._lastSuccessTracker.getTimeSinceLastSuccess(),e}},{key:"sendDominantSpeakerEvent",value:function(e){if(this.speakerStatsComponentAddress&&e){var t=Object(i.$msg)({to:this.speakerStatsComponentAddress});t.c("speakerstats",{xmlns:"http://jitsi.org/jitmeet",room:e}).up(),this.connection.send(t)}}},{key:"tryParseJSONAndVerify",value:function(e){try{var t=JSON.parse(e);if(t&&"object"===T(t)){var n=t[I];if(void 0!==n)return t;k.debug("parsing valid json but does not have correct structure","topic: ",n)}}catch(e){return!1}return!1}},{key:"_onPrivateMessage",value:function(e){var t=e.getAttribute("from");if(this.speakerStatsComponentAddress&&t===this.speakerStatsComponentAddress){var n=$(e).find(">json-message").text(),r=this.tryParseJSONAndVerify(n);return r&&"speakerstats"===r[I]&&r.users&&this.eventEmitter.emit(E.a.SPEAKER_STATS_RECEIVED,r.users),!0}}}])&&C(n.prototype,r),t})()}).call(this,"modules/xmpp/xmpp.js")},function(e,t,n){"use strict";n.r(t),n.d(t,"CONNECTION_DROPPED_ERROR",function(){return r}),n.d(t,"OTHER_ERROR",function(){return i}),n.d(t,"PASSWORD_REQUIRED",function(){return o}),n.d(t,"SERVER_ERROR",function(){return a});var r="connection.droppedError",i="connection.otherError",o="connection.passwordRequired",a="connection.serverError"},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return i});var r="signaling.peerMuted",i="signaling.peerVideoType"},function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var i=!0,o=!0,a={disableLog:function(e){return"boolean"!=typeof e?new Error("Argument type: "+r(e)+". Please use a boolean."):(i=e,e?"adapter.js logging disabled":"adapter.js logging enabled")},disableWarnings:function(e){return"boolean"!=typeof e?new Error("Argument type: "+r(e)+". Please use a boolean."):(o=!e,"adapter.js deprecation warnings "+(e?"disabled":"enabled"))},log:function(){if("object"===("undefined"==typeof window?"undefined":r(window))){if(i)return;"undefined"!=typeof console&&"function"==typeof console.log&&console.log.apply(console,arguments)}},deprecated:function(e,t){o&&console.warn(e+" is deprecated, please use "+t+" instead.")},extractVersion:function(e,t,n){var r=e.match(t);return r&&r.length>=n&&parseInt(r[n],10)},detectBrowser:function(e){var t=e&&e.navigator,n={browser:null,version:null};if(void 0===e||!e.navigator)return n.browser="Not a browser.",n;if(t.mozGetUserMedia)n.browser="firefox",n.version=this.extractVersion(t.userAgent,/Firefox\/(\d+)\./,1);else if(t.webkitGetUserMedia)if(e.webkitRTCPeerConnection)n.browser="chrome",n.version=this.extractVersion(t.userAgent,/Chrom(e|ium)\/(\d+)\./,2);else{if(!t.userAgent.match(/Version\/(\d+).(\d+)/))return n.browser="Unsupported webkit-based browser with GUM support but no WebRTC support.",n;n.browser="safari",n.version=this.extractVersion(t.userAgent,/AppleWebKit\/(\d+)\./,1)}else if(t.mediaDevices&&t.userAgent.match(/Edge\/(\d+).(\d+)$/))n.browser="edge",n.version=this.extractVersion(t.userAgent,/Edge\/(\d+).(\d+)$/,2);else{if(!t.mediaDevices||!t.userAgent.match(/AppleWebKit\/(\d+)\./))return n.browser="Not a supported browser.",n;n.browser="safari",n.version=this.extractVersion(t.userAgent,/AppleWebKit\/(\d+)\./,1)}return n}};e.exports={log:a.log,deprecated:a.deprecated,disableLog:a.disableLog,disableWarnings:a.disableWarnings,extractVersion:a.extractVersion,shimCreateObjectURL:a.shimCreateObjectURL,detectBrowser:a.detectBrowser.bind(a)}},function(e,t){e.exports={ENVIRONMENT:"environment",USER:"user"}},function(e,t,n){"use strict";(function(e){var r=n(15),i=n(12),o=n(3);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var s=n(4).getLogger(e),c=n(11),u=!1,l=!1,d=null,p={intChromeExtPromise:null,obtainStream:null,init:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{desktopSharingChromeDisabled:!1,desktopSharingChromeExtId:null,desktopSharingFirefoxDisabled:!1},t=arguments.length>1?arguments[1]:void 0;this.options=e,d=t,this.obtainStream=this._createObtainStreamMethod(e),this.obtainStream||s.info("Desktop sharing disabled")},_createObtainStreamMethod:function(e){var t=this;return o.a.isNWJS()?function(e,t,n){window.JitsiMeetNW.obtainDesktopStream(t,function(e,t){var o;o=e&&"InvalidStateError"===e.name?new r.a(i.CHROME_EXTENSION_USER_CANCELED):new r.a(e,t,["desktop"]),"function"==typeof n&&n(o)})}:o.a.isElectron()?this.obtainScreenOnElectron:o.a.isChrome()||o.a.isOpera()?o.a.supportsGetDisplayMedia()&&!e.desktopSharingChromeDisabled?this.obtainScreenFromGetDisplayMedia:e.desktopSharingChromeDisabled||!e.desktopSharingChromeExtId?null:(s.info("Using Chrome extension for desktop sharing"),this.intChromeExtPromise=(function(e){return(function(e){0===$("link[rel=chrome-webstore-item]").length&&$("head").append(''),$("link[rel=chrome-webstore-item]").attr("href",f(e))})(e),new Promise(function(t){h(function(e,n){u=e,l=n,s.info("Chrome extension installed: ".concat(u," updateRequired: ").concat(l)),t()},e)})})(e).then(function(){t.intChromeExtPromise=null}),this.obtainScreenFromExtension):o.a.isFirefox()?e.desktopSharingFirefoxDisabled?null:o.a.supportsGetDisplayMedia()?this.obtainScreenFromGetDisplayMedia:this.obtainScreenOnFirefox:o.a.isEdge()&&o.a.supportsGetDisplayMedia()?this.obtainScreenFromGetDisplayMedia:(s.log("Screen sharing not supported by the current browser: ",o.a.getName()),null)},isSupported:function(){return null!==this.obtainStream},obtainScreenOnFirefox:function(e,t,n){!(function(e,t,n){d(["screen"],e).then(function(e){return t({stream:e})},n)})(e.gumOptions,t,n)},obtainScreenOnElectron:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0,n=arguments.length>2?arguments[2]:void 0;if(window.JitsiMeetScreenObtainer&&window.JitsiMeetScreenObtainer.openDesktopPicker){var o=e.desktopSharingSources,a=e.gumOptions;window.JitsiMeetScreenObtainer.openDesktopPicker({desktopSharingSources:o||this.options.desktopSharingChromeSources},function(e,r){return m({response:{streamId:e,streamType:r},gumOptions:a},t,n)},function(e){return n(new r.a(i.ELECTRON_DESKTOP_PICKER_ERROR,e))})}else n(new r.a(i.ELECTRON_DESKTOP_PICKER_NOT_FOUND))},obtainScreenFromExtension:function(e,t,n){var o=this;if(null===this.intChromeExtPromise){var a=this.options,c=a.desktopSharingChromeExtId,d=a.desktopSharingChromeSources,p=e.gumOptions,f={desktopSharingChromeExtId:c,desktopSharingChromeSources:e.desktopSharingSources||d,gumOptions:p};u?(function(e,t,n){var o=f.desktopSharingChromeSources,a=f.desktopSharingChromeExtId,c=f.gumOptions;chrome.runtime.sendMessage(a,{getStream:!0,sources:o},function(e){if(e)s.log("Response from extension: ",e),m({response:e,gumOptions:c},t,n);else{var o=chrome.runtime.lastError;n(o instanceof Error?o:new r.a(i.CHROME_EXTENSION_GENERIC_ERROR,o))}})})(0,t,n):(l&&alert("Jitsi Desktop Streamer requires update. Changes will take effect after next Chrome restart."),this.handleExternalInstall(e,t,n))}else this.intChromeExtPromise.then(function(){o.obtainScreenFromExtension(e,t,n)})},handleExternalInstall:function(e,t,n,r){var i=f(this.options);e.listener("waitingForExtension",i),this.checkForChromeExtensionOnInterval(e,t,n,r)},checkForChromeExtensionOnInterval:function(e,t,n){var o=this;!1!==e.checkAgain()?(function(e,t,n){return new Promise(function(n,r){var i=1,o=window.setInterval(function(){h(function(e){e?(window.clearInterval(o),n()):0==--i&&(r(),window.clearInterval(o))},e)},t)})})(this.options,e.interval).then(function(){u=!0,e.listener("extensionFound"),o.obtainScreenFromExtension(e,t,n)}).catch(function(){o.checkForChromeExtensionOnInterval(e,t,n)}):n(new r.a(i.CHROME_EXTENSION_INSTALLATION_ERROR))},obtainScreenFromGetDisplayMedia:function(e,t,n){s.info("Using getDisplayMedia for screen sharing"),(navigator.getDisplayMedia?navigator.getDisplayMedia.bind(navigator):navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices))({video:!0}).then(function(n){(n&&n.getTracks()&&n.getTracks().length>0?n.getTracks()[0].applyConstraints(e.trackOptions):Promise.resolve()).then(function(){return t({stream:n,sourceId:n.id})})}).catch(function(){return n(new r.a(i.CHROME_EXTENSION_USER_CANCELED))})}};function f(e){return"https://chrome.google.com/webstore/detail/".concat(e.desktopSharingChromeExtId)}function h(e,t){"undefined"!=typeof chrome&&chrome&&chrome.runtime?chrome.runtime.sendMessage(t.desktopSharingChromeExtId,{getVersion:!0},function(n){if(!n||!n.version)return s.warn("Extension not installed?: ",chrome.runtime.lastError),void e(!1,!1);var r=n.version;s.log("Extension version is: ".concat(r));var i=(function(e,t){try{for(var n=e.split("."),r=t.split("."),i=Math.max(n.length,r.length),o=0;ou}return!1}catch(e){return c.callErrorHandler(e),s.error("Failed to parse extension version",e),!0}})(t.desktopSharingChromeMinExtVersion,r);e(!i,i)}):e(!1,!1)}function m(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{response:{},gumOptions:{}},t=arguments.length>1?arguments[1]:void 0,n=arguments.length>2?arguments[2]:void 0,o=e.response||{},s=o.streamId,c=o.streamType,u=o.error;if(s){var l=(function(e){for(var t=1;t=i}},{key:"refreshConnectionStatusForAll",value:function(){var e=this.conference.getParticipants(),t=!0,n=!1,r=void 0;try{for(var i,o=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;this.figureOutConnectionStatus(a.getId())}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}}},{key:"figureOutConnectionStatus",value:function(t){var n=this.conference.getParticipantById(t);if(n){var r=this.conference.isP2PActive(),i=this._isRestoringTimedout(t),o=0===this.rtc.getLastN(),s=n.isVideoMuted()||o,c=this.isVideoTrackFrozen(n),u=this.rtc.isInLastN(t),l=this.connStatusFromJvb[t];"boolean"!=typeof l&&(v.debug("Assuming connection active by JVB - no notification"),l=!0);var d=r?e._getNewStateForP2PMode(s,c):e._getNewStateForJvbMode(l,u,i,s,c);d!==S.RESTORING&&this._clearRestoringTimer(t),v.debug("Figure out conn status for ".concat(t,", is video muted: ").concat(s," is active(jvb): ").concat(l," video track frozen: ").concat(c," p2p mode: ").concat(r," is in last N: ").concat(u," currentStatus => newStatus: ").concat(n.getConnectionStatus()," => ").concat(d));var f=this.connectionStatusMap[t]||{};if(!("p2p"in f&&"connectionStatus"in f&&f.p2p===r&&f.connectionStatus===d)){var h=Date.now();if(this.maybeSendParticipantConnectionStatusEvent(t,h),this.connectionStatusMap[t]=p({},f,{connectionStatus:d,p2p:r,startedMs:h}),!("videoType"in this.connectionStatusMap[t])){var m=n.getTracksByMediaType(a.b);Array.isArray(m)&&0!==m.length&&(this.connectionStatusMap[t].videoType=m[0].videoType)}}this._changeConnectionStatus(n,d)}else v.debug("figure out conn status - no participant for: ".concat(t))}},{key:"maybeSendParticipantConnectionStatusEvent",value:function(e,t){var n=this.connectionStatusMap[e];n&&"startedMs"in n&&"videoType"in n&&"connectionStatus"in n&&"p2p"in n&&(n.value=t-n.startedMs,l.a.sendAnalytics(Object(d.I)(n)))}},{key:"_onLastNChanged",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=Date.now();v.debug("leaving/entering lastN",e,t,n);var r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done);r=!0){var c=a.value;this.enteredLastNTimestamp.delete(c),this._clearRestoringTimer(c),this.figureOutConnectionStatus(c)}}catch(e){i=!0,o=e}finally{try{r||null==s.return||s.return()}finally{if(i)throw o}}var u=!0,l=!1,d=void 0;try{for(var p,f=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=f.next()).done);u=!0){var h=p.value;this.enteredLastNTimestamp.set(h,n),this.figureOutConnectionStatus(h)}}catch(e){l=!0,d=e}finally{try{u||null==f.return||f.return()}finally{if(l)throw d}}}},{key:"_clearRestoringTimer",value:function(e){var t=this.restoringTimers.get(e);t&&(clearTimeout(t),this.restoringTimers.delete(e))}},{key:"_isRestoringTimedout",value:function(e){var t=this,n=this.enteredLastNTimestamp.get(e);return!!(n&&Date.now()-n>=1e4)||(this.restoringTimers.get(e)||this.restoringTimers.set(e,setTimeout(function(){return t.figureOutConnectionStatus(e)},1e4)),!1)}},{key:"onUserLeft",value:function(e){this.maybeSendParticipantConnectionStatusEvent(e,Date.now()),delete this.connectionStatusMap[e]}},{key:"onTrackRtcMuted",value:function(e){var t=this,n=e.getParticipantId(),r=this.conference.getParticipantById(n);if(v.debug("Detector track RTC muted: ".concat(n),Date.now()),r){if(this.rtcMutedTimestamp[n]=Date.now(),!r.isVideoMuted()){this.clearTimeout(n);var i=this._getVideoFrozenTimeout(n);this.trackTimers[n]=window.setTimeout(function(){v.debug("Set RTC mute timeout for: ".concat(n," of ").concat(i," ms")),t.clearTimeout(n),t.figureOutConnectionStatus(n)},i)}}else v.error("No participant for id: ".concat(n))}},{key:"onTrackRtcUnmuted",value:function(e){var t=e.getParticipantId();v.debug("Detector track RTC unmuted: ".concat(t),Date.now()),this.clearTimeout(t),this.clearRtcMutedTimestamp(t),this.figureOutConnectionStatus(t)}},{key:"onSignallingMuteChanged",value:function(e){var t=e.getParticipantId();v.debug("Detector on track signalling mute changed: ".concat(t),e.isMuted()),this.figureOutConnectionStatus(t)}},{key:"onTrackVideoTypeChanged",value:function(e,t){var n=e.getParticipantId(),r=Date.now();this.maybeSendParticipantConnectionStatusEvent(n,r),this.connectionStatusMap[n]=p({},this.connectionStatusMap[n]||{},{videoType:t,startedMs:r})}}]),e})()}).call(this,"modules/connectivity/ParticipantConnectionStatus.js")},function(e,t,n){"use strict";t.a={getFocusRecordingUpdate:function(e){var t=e&&e.getElementsByTagName("jibri-recording-status")[0];if(t)return{error:t.getAttribute("failure_reason"),recordingMode:t.getAttribute("recording_mode"),sessionID:t.getAttribute("session_id"),status:t.getAttribute("status")}},getHiddenDomainUpdate:function(e){var t=e.getElementsByTagName("live-stream-view-url")[0],n=t&&t.textContent,r=e.getElementsByTagName("mode")[0],i=r&&r.textContent&&r.textContent.toLowerCase(),o=e.getElementsByTagName("session_id")[0];return{liveStreamViewURL:n,mode:i,sessionID:o&&o.textContent}},getSessionIdFromIq:function(e){var t=e&&e.getElementsByTagName("jibri")[0];return t&&t.getAttribute("session_id")},getSessionId:function(e){var t=e.getElementsByTagName("session_id")[0];return t&&t.textContent},isFromFocus:function(e){return e.getAttribute("from").includes("focus")}}},function(e,t){function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var r;r=(function(){return this})();try{r=r||new Function("return this")()}catch(e){"object"===("undefined"==typeof window?"undefined":n(window))&&(r=window)}e.exports=r},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return k});var r=n(7),i=n(4),o=n(1),a=n(82),s=n(83),c=n(31),u=n(18),l=n(47),d=n(6),p=n(84),f=n(8),h=n.n(f),m=n(5),v=n(0),y=n.n(v),g=n(86),S=n(11),_=n.n(S);function b(e){return(b="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function E(e,t){return!t||"object"!==b(t)&&"function"!=typeof t?(function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e})(e):t}function T(e){return(T=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function C(e,t){for(var n=0;ncontent[name="video"]');if(t.length){var n=t[0].getAttribute("senders");if("both"===n||"initiator"===n||"responder"===n||"none"===n)return n}return null}}]),R(t,[{key:"_assertNotEnded",value:function(){return this.state!==c.b}},{key:"doInitialize",value:function(e){var t=this;this.failICE=Boolean(e.failICE),this.lasticecandidate=!1,this.options=e,this.isReconnect=!1,this.wasstable=!1,this.webrtcIceUdpDisable=Boolean(e.webrtcIceUdpDisable),this.webrtcIceTcpDisable=Boolean(e.webrtcIceTcpDisable);var n={disableRtx:e.disableRtx};if(e.gatherStats&&(n.maxstats=300),this.isP2P){n.disableSimulcast=!0,n.disableH264=e.p2p&&e.p2p.disableH264,n.preferH264=e.p2p&&e.p2p.preferH264;var i=this._abtestSuspendVideoEnabled(e);void 0!==i&&(n.abtestSuspendVideo=i)}else n.disableSimulcast=e.disableSimulcast||e.preferH264&&!e.disableH264,n.preferH264=e.preferH264,n.enableFirefoxSimulcast=e.testing&&e.testing.enableFirefoxSimulcast,n.enableLayerSuspension=e.enableLayerSuspension;e.startSilent&&(n.startSilent=!0),this.peerconnection=this.rtc.createPeerConnection(this.signalingLayer,this.iceConfig,this.isP2P,n),this.peerconnection.onicecandidate=function(e){if(e){var n=e.candidate,i=window.performance.now();if(n){null===t._gatheringStartedTimestamp&&(t._gatheringStartedTimestamp=i);var o=n.protocol;if("string"==typeof o)if("tcp"===(o=o.toLowerCase())||"ssltcp"===o){if(t.webrtcIceTcpDisable)return}else if("udp"===o&&t.webrtcIceUdpDisable)return}else t._gatheringReported||(m.a.sendAnalytics(r.o,{phase:"gathering",value:i-t._gatheringStartedTimestamp,p2p:t.isP2P,initiator:t.isInitiator}),t._gatheringReported=!0);t.sendIceCandidate(n)}},this.peerconnection.onsignalingstatechange=function(){"stable"===t.peerconnection.signalingState?t.wasstable=!0:"closed"!==t.peerconnection.signalingState&&"closed"!==t.peerconnection.connectionState||t.room.eventEmitter.emit(y.a.SUSPEND_DETECTED,t)},this.peerconnection.oniceconnectionstatechange=function(){var e=window.performance.now();switch(t.isP2P||(t.room.connectionTimes["ice.state.".concat(t.peerconnection.iceConnectionState)]=e),w.log("(TIME) ICE ".concat(t.peerconnection.iceConnectionState)+" P2P? ".concat(t.isP2P,":\t"),e),m.a.sendAnalytics(r.q,{p2p:t.isP2P,state:t.peerconnection.iceConnectionState,signaling_state:t.peerconnection.signalingState,reconnect:t.isReconnect,value:e}),t.room.eventEmitter.emit(y.a.ICE_CONNECTION_STATE_CHANGED,t,t.peerconnection.iceConnectionState),t.peerconnection.iceConnectionState){case"checking":t._iceCheckingStartedTimestamp=e;break;case"connected":if("stable"===t.peerconnection.signalingState&&t.isReconnect&&t.room.eventEmitter.emit(y.a.CONNECTION_RESTORED,t),!t.wasConnected&&t.wasstable){m.a.sendAnalytics(r.o,{phase:"checking",value:e-t._iceCheckingStartedTimestamp,p2p:t.isP2P,initiator:t.isInitiator});var n=Math.min(t._iceCheckingStartedTimestamp,t._gatheringStartedTimestamp);t.establishmentDuration=e-n,m.a.sendAnalytics(r.o,{phase:"establishment",value:t.establishmentDuration,p2p:t.isP2P,initiator:t.isInitiator}),t.wasConnected=!0,t.room.eventEmitter.emit(y.a.CONNECTION_ESTABLISHED,t)}t.isReconnect=!1;break;case"disconnected":t.isReconnect=!0,t.wasstable&&t.room.eventEmitter.emit(y.a.CONNECTION_INTERRUPTED,t);break;case"failed":t.room.eventEmitter.emit(y.a.CONNECTION_ICE_FAILED,t)}},this.peerconnection.onnegotiationneeded=function(){t.room.eventEmitter.emit(y.a.PEERCONNECTION_READY,t)},this.signalingLayer.setChatRoom(this.room),!this.isP2P&&e.enableLayerSuspension&&this.rtc.addListener(h.a.IS_SELECTED_CHANGED,function(e){t.peerconnection.setIsSelected(e),w.info("Doing local O/A due to IS_SELECTED_CHANGED event"),t.modificationQueue.push(function(e){t._renegotiate().then(e).catch(e)})})}},{key:"sendIceCandidate",value:function(e){var t=this,n=new u.a(this.peerconnection.localDescription.sdp);if(e&&!this.lasticecandidate){var r=d.a.iceparams(n.media[e.sdpMLineIndex],n.session),i=d.a.candidateToJingle(e.candidate);if(!r||!i)return _.a.callErrorHandler(new Error("failed to get ice && jcand")),void w.error("failed to get ice && jcand");r.xmlns="urn:xmpp:jingle:transports:ice-udp:1",this.usedrip?(0===this.dripContainer.length&&setTimeout(function(){0!==t.dripContainer.length&&(t.sendIceCandidates(t.dripContainer),t.dripContainer=[])},20),this.dripContainer.push(e)):this.sendIceCandidates([e])}else w.log("sendIceCandidate: last candidate."),this.lasticecandidate=!0}},{key:"sendIceCandidates",value:function(e){var t=this;if(this._assertNotEnded("sendIceCandidates")){w.log("sendIceCandidates",e);for(var n=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-info",initiator:this.initiatorJid,sid:this.sid}),r=new u.a(this.peerconnection.localDescription.sdp),i=function(i){var o=e.filter(function(e){return e.sdpMLineIndex===i}),a=d.a.parseMLine(r.media[i].split("\r\n")[0]);if(o.length>0){var s=d.a.iceparams(r.media[i],r.session);s.xmlns="urn:xmpp:jingle:transports:ice-udp:1",n.c("content",{creator:t.initiatorJid===t.localJid?"initiator":"responder",name:o[0].sdpMid?o[0].sdpMid:a.media}).c("transport",s);for(var c=0;ccontent>transport>candidate").each(function(e,t){var r=d.a.candidateFromJingle(t);r=r.replace("\r\n","").replace("a=","");var i=new RTCIceCandidate({sdpMLineIndex:0,sdpMid:"",candidate:r});n.push(i)}),n.length?(w.debug("Queued add (".concat(n.length,") ICE candidates task...")),this.modificationQueue.push(function(e){for(var r=0;rdescription>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(e,n){var r=Number(n.getAttribute("ssrc"));t.isP2P?t.signalingLayer.setSSRCOwner(r,o.Strophe.getResourceFromJid(t.remoteJid)):$(n).find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]').each(function(e,n){var i=n.getAttribute("owner");i&&i.length&&(isNaN(r)||r<0?w.warn("Invalid SSRC ".concat(r," value received")+" for ".concat(i)):t.signalingLayer.setSSRCOwner(r,o.Strophe.getResourceFromJid(i)))})})}},{key:"generateRecvonlySsrc",value:function(){this.peerconnection?this.peerconnection.generateRecvonlySsrc():w.error("Unable to generate recvonly SSRC - no peerconnection")}},{key:"acceptOffer",value:function(e,t,n,r){var i=this;this.setOfferAnswerCycle(e,function(){i.sendSessionAccept(t,n)},n,r)}},{key:"invite",value:function(e){var t=this;if(!this.isInitiator)throw new Error("Trying to invite from the responder session");this.modificationQueue.push(function(n){var r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done);r=!0){var c=a.value;t.peerconnection.addTrack(c)}}catch(e){i=!0,o=e}finally{try{r||null==s.return||s.return()}finally{if(i)throw o}}t.peerconnection.createOffer(t.mediaConstraints).then(function(e){t.peerconnection.setLocalDescription(e).then(function(){t.sendSessionInitiate(t.peerconnection.localDescription.sdp),n()},function(t){w.error("Failed to set local SDP",t,e),n(t)})},function(e){w.error("Failed to create an offer",e,t.mediaConstraints),n(e)})},function(e){e?w.error("invite error",e):w.debug("invite executed - OK")})}},{key:"sendSessionInitiate",value:function(e){var t=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-initiate",initiator:this.initiatorJid,sid:this.sid});new u.a(e).toJingle(t,this.isInitiator?"initiator":"responder"),t=t.tree(),w.info("Session-initiate: ",t),this.connection.sendIQ(t,function(){w.info('Got RESULT for "session-initiate"')},function(e){w.error('"session-initiate" error',e)},1e4)}},{key:"setAnswer",value:function(e){if(!this.isInitiator)throw new Error("Trying to set an answer on the responder session");this.setOfferAnswerCycle(e,function(){w.info("setAnswer - succeeded")},function(e){w.error("setAnswer failed: ",e)})}},{key:"setOfferAnswerCycle",value:function(e,t,n,r){var i=this;this.modificationQueue.push(function(t){if(r){var n=!0,o=!1,a=void 0;try{for(var s,l=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(s=l.next()).done);n=!0){var d=s.value;i.peerconnection.addTrack(d)}}catch(e){o=!0,a=e}finally{try{n||null==l.return||l.return()}finally{if(o)throw a}}}var p=i._processNewJingleOfferIq(e),f=i.peerconnection.localDescription.sdp,h=$(e).find('>bridge-session[xmlns="http://jitsi.org/protocol/focus"]').attr("id");h!==i._bridgeSessionId&&(i._bridgeSessionId=h),i._renegotiate(p.raw).then(function(){if(i.state===c.c&&(i.state=c.a,i.isP2P&&!i._localVideoActive&&i.sendContentModify(i._localVideoActive)),f){var e=new u.a(i.peerconnection.localDescription.sdp);i.notifyMySSRCUpdate(new u.a(f),e)}t()},function(e){w.error("Error renegotiating after setting new remote ".concat(i.isInitiator?"answer: ":"offer: ").concat(e),p),t(e)})},function(e){e?n(e):t()})}},{key:"replaceTransport",value:function(e,t,n){var r=this;this.room.eventEmitter.emit(y.a.ICE_RESTARTING,this);var i=e.clone();e.find(">content[name='data']").attr("senders","rejected"),e.find(">content>description>source").remove(),e.find(">content>description>ssrc-group").remove();var o=e.find(">content>transport>fingerprint");o.attr("hash","sha-1"),o.text("00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"),this.setOfferAnswerCycle(e,function(){r.setOfferAnswerCycle(i,function(){var e=new u.a(r.peerconnection.localDescription.sdp);r.sendTransportAccept(e,t,n),r.room.eventEmitter.emit(y.a.ICE_RESTART_SUCCESS,r,i)},n)},n)}},{key:"sendSessionAccept",value:function(e,t){var n=this,r=new u.a(this.peerconnection.localDescription.sdp),i=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-accept",initiator:this.initiatorJid,responder:this.responderJid,sid:this.sid});this.webrtcIceTcpDisable&&(r.removeTcpCandidates=!0),this.webrtcIceUdpDisable&&(r.removeUdpCandidates=!0),this.failICE&&(r.failICE=!0),r.toJingle(i,this.initiatorJid===this.localJid?"initiator":"responder",null),i=i.tree(),w.info("Sending session-accept",i),this.connection.sendIQ(i,e,this.newJingleErrorHandler(i,function(e){t(e),n.room.eventEmitter.emit(y.a.SESSION_ACCEPT_TIMEOUT,n)}),1e4)}},{key:"sendContentModify",value:function(e){var t=e?"both":"none",n=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"content-modify",initiator:this.initiatorJid,sid:this.sid}).c("content",{name:"video",senders:t});w.info("Sending content-modify, video senders: ".concat(t)),this.connection.sendIQ(n,null,this.newJingleErrorHandler(n),1e4)}},{key:"sendTransportAccept",value:function(e,t,n){var r=this,i=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-accept",initiator:this.initiatorJid,sid:this.sid});e.media.forEach(function(t,n){var o=d.a.parseMLine(t.split("\r\n")[0]);i.c("content",{creator:r.initiatorJid===r.localJid?"initiator":"responder",name:o.media}),e.transportToJingle(n,i),i.up()}),i=i.tree(),w.info("Sending transport-accept: ",i),this.connection.sendIQ(i,t,this.newJingleErrorHandler(i,n),1e4)}},{key:"sendTransportReject",value:function(e,t){var n=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-reject",initiator:this.initiatorJid,sid:this.sid});n=n.tree(),w.info("Sending 'transport-reject",n),this.connection.sendIQ(n,e,this.newJingleErrorHandler(n,t),1e4)}},{key:"terminate",value:function(e,t,n){if(this.state!==c.b){if(!n||Boolean(n.sendSessionTerminate)){var r=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-terminate",initiator:this.initiatorJid,sid:this.sid}).c("reason").c(n&&n.reason||"success");n&&n.reasonDescription&&r.up().c("text").t(n.reasonDescription),r=r.tree(),w.info("Sending session-terminate",r),this.connection.sendIQ(r,e,this.newJingleErrorHandler(r,t),1e4)}else w.info("Skipped sending session-terminate for ".concat(this));this.connection.jingle.terminate(this.sid)}}},{key:"onTerminated",value:function(e,t){w.info("Session terminated ".concat(this),e,t),this.close()}},{key:"_parseSsrcInfoFromSourceAdd",value:function(e,t){var n=[];return $(e).each(function(e,r){var i=$(r).attr("name"),o="";$(r).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(o+="a=ssrc-group:".concat(e," ").concat(t.join(" "),"\r\n"))}),$(r).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=$(this).attr("ssrc");t.containsSSRC(e)?w.warn("Source-add request for existing SSRC: ".concat(e)):$(this).find(">parameter").each(function(){o+="a=ssrc:".concat(e," ").concat($(this).attr("name")),$(this).attr("value")&&$(this).attr("value").length&&(o+=":".concat($(this).attr("value"))),o+="\r\n"})}),t.media.forEach(function(e,t){d.a.findLine(e,"a=mid:".concat(i))&&(n[t]||(n[t]=""),n[t]+=o)})}),n}},{key:"addRemoteStream",value:function(e){this._addOrRemoveRemoteStream(!0,e)}},{key:"removeRemoteStream",value:function(e){this._addOrRemoveRemoteStream(!1,e)}},{key:"_addOrRemoveRemoteStream",value:function(e,t){var n=this,r=e?"addRemoteStream":"removeRemoteStream";e&&this.readSsrcInfo(t),this.modificationQueue.push(function(i){if(!n.peerconnection.localDescription||!n.peerconnection.localDescription.sdp){var o="".concat(r," - localDescription not ready yet");return w.error(o),void i(o)}w.log("Processing ".concat(r)),w.log("ICE connection state: ",n.peerconnection.iceConnectionState);var a=new u.a(n.peerconnection.localDescription.sdp),s=new u.a(n.peerconnection.remoteDescription.sdp),c=e?n._parseSsrcInfoFromSourceAdd(t,s):n._parseSsrcInfoFromSourceRemove(t,s),l=e?n._processRemoteAddSource(c):n._processRemoteRemoveSource(c);n._renegotiate(l.raw).then(function(){var e=new u.a(n.peerconnection.localDescription.sdp);w.log("".concat(r," - OK, SDPs: "),a,e),n.notifyMySSRCUpdate(a,e),i()},function(e){w.error("".concat(r," failed:"),e),i(e)})})}},{key:"_processNewJingleOfferIq",value:function(e){var t=new u.a("");return this.webrtcIceTcpDisable&&(t.removeTcpCandidates=!0),this.webrtcIceUdpDisable&&(t.removeUdpCandidates=!0),this.failICE&&(t.failICE=!0),t.fromJingle(e),this.readSsrcInfo($(e).find(">content")),t}},{key:"_processRemoteRemoveSource",value:function(e){var t=new u.a(this.peerconnection.remoteDescription.sdp);return e.forEach(function(e,n){(e=e.split("\r\n")).pop(),e.forEach(function(e){t.media[n]=t.media[n].replace("".concat(e,"\r\n"),"")})}),t.raw=t.session+t.media.join(""),t}},{key:"_processRemoteAddSource",value:function(e){var t=new u.a(this.peerconnection.remoteDescription.sdp);return e.forEach(function(e,n){t.media[n]+=e}),t.raw=t.session+t.media.join(""),t}},{key:"_renegotiate",value:function(e){if("closed"===this.peerconnection.signalingState){var t=new Error("Attempted to renegotiate in state closed");return this.room.eventEmitter.emit(y.a.RENEGOTIATION_FAILED,t,this),Promise.reject(t)}var n=e||this.peerconnection.remoteDescription.sdp;if(!n){var r=new Error("Can not renegotiate without remote description, current state: ".concat(this.state));return this.room.eventEmitter.emit(y.a.RENEGOTIATION_FAILED,r,this),Promise.reject(r)}var i=new RTCSessionDescription({type:this.isInitiator?"answer":"offer",sdp:n});return this.isInitiator?this._initiatorRenegotiate(i):this._responderRenegotiate(i)}},{key:"_responderRenegotiate",value:function(e){var t=this;return w.debug("Renegotiate: setting remote description"),this.peerconnection.setRemoteDescription(e).then(function(){return w.debug("Renegotiate: creating answer"),t.peerconnection.createAnswer(t.mediaConstraints).then(function(e){return w.debug("Renegotiate: setting local description"),t.peerconnection.setLocalDescription(e)})})}},{key:"_initiatorRenegotiate",value:function(e){var t=this;return"have-local-offer"===this.peerconnection.signalingState?(w.debug("Renegotiate: setting remote description"),this.peerconnection.setRemoteDescription(e).then(function(){return t._initiatorRenegotiate(e)})):(w.debug("Renegotiate: creating offer"),this.peerconnection.createOffer(this.mediaConstraints).then(function(n){return w.debug("Renegotiate: setting local description"),t.peerconnection.setLocalDescription(n).then(function(){return w.debug("Renegotiate: setting remote description"),t.peerconnection.setRemoteDescription(e)})}))}},{key:"replaceTrack",value:function(e,t){var n=this,r=function(r){var i=n.peerconnection.localDescription.sdp;!e&&t&&t.isVideoTrack()?n.peerconnection.clearRecvonlySsrc():e&&e.isVideoTrack()&&!t&&(n.peerconnection.clearRecvonlySsrc(),n.peerconnection.generateRecvonlySsrc()),n.peerconnection.replaceTrack(e,t).then(function(o){o&&(e||t)&&n.state===c.a?n._renegotiate().then(function(){var e=new u.a(n.peerconnection.localDescription.sdp);n.notifyMySSRCUpdate(new u.a(i),e),r()},r):r()})};return new Promise(function(e,t){n.modificationQueue.push(r,function(n){n?(w.error("Replace track error:",n),t(n)):(w.info("Replace track done!"),e())})})}},{key:"_parseSsrcInfoFromSourceRemove",value:function(e,t){var n=[];return $(e).each(function(e,r){var i=$(r).attr("name"),o="";$(r).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(o+="a=ssrc-group:".concat(e," ").concat(t.join(" "),"\r\n"))});var a=[];$(r).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=$(this).attr("ssrc");a.push(e)}),t.media.forEach(function(e,t){d.a.findLine(e,"a=mid:".concat(i))&&(n[t]||(n[t]=""),a.forEach(function(r){var i=d.a.findLines(e,"a=ssrc:".concat(r));i.length&&(n[t]+="".concat(i.join("\r\n"),"\r\n"))}),n[t]+=o)})}),n}},{key:"_verifyNoSSRCChanged",value:function(e,t){var n=new u.a(this.peerconnection.localDescription.sdp),r=new l.a(t,n),i=r.getNewMedia();if(Object.keys(i).length)return w.error("".concat(this," - some SSRC were added on ").concat(e),i),!1;var o=(r=new l.a(n,t)).getNewMedia();return!Object.keys(o).length||(w.error("".concat(this," - some SSRCs were removed on ").concat(e),o),!1)}},{key:"addTrackAsUnmute",value:function(e){return this._addRemoveTrackAsMuteUnmute(!1,e)}},{key:"removeTrackAsMute",value:function(e){return this._addRemoveTrackAsMuteUnmute(!0,e)}},{key:"_addRemoveTrackAsMuteUnmute",value:function(e,t){var n=this;if(!t)return Promise.reject('invalid "track" argument value');var r=e?"removeTrackMute":"addTrackUnmute",i=function(i){var o=n.peerconnection;if(o){var a=o.localDescription.sdp;(e?o.removeTrackMute.bind(o,t):o.addTrackUnmute.bind(o,t))()?a&&o.remoteDescription.sdp?n._renegotiate().then(function(){n._verifyNoSSRCChanged(r,new u.a(a)),i()},i):i():i("".concat(r," failed!"))}else i("Error: tried ".concat(r," track with no active peer")+"connection")};return new Promise(function(e,t){n.modificationQueue.push(i,function(n){n?t(n):e()})})}},{key:"setMediaTransferActive",value:function(e,t){var n=this;if(!this.peerconnection)return Promise.reject('Can not modify transfer active state, before "initialize" is called');var r=e?"audio active":"audio inactive",i=t?"video active":"video inactive";w.info("Queued make ".concat(i,", ").concat(r," task..."));var o=function(r){var i=n.state===c.a,o=n.peerconnection.setAudioTransferActive(e);n._localVideoActive!==t&&(n._localVideoActive=t,n.isP2P&&i&&n.sendContentModify(t));var a=n.peerconnection.setVideoTransferActive(n._localVideoActive&&n._remoteVideoActive);i&&(o||a)?n._renegotiate().then(r,r):r()};return new Promise(function(e,t){n.modificationQueue.push(o,function(n){n?t(n):e()})})}},{key:"modifyContents",value:function(e){var n=this,r=t.parseVideoSenders(e);null!==r?(w.debug("".concat(this,' queued "content-modify" task')+'(video senders="'.concat(r,'")')),this.modificationQueue.push(function(e){n._assertNotEnded("content-modify")&&n._modifyRemoteVideoActive(r)?n._renegotiate().then(e,e):e()},function(e){e&&w.error('"content-modify" failed',e)})):w.error("".concat(this,' - failed to parse video "senders" attribute in')+'"content-modify" action')}},{key:"_modifyRemoteVideoActive",value:function(e){var t="both"===e||"initiator"===e&&this.isInitiator||"responder"===e&&!this.isInitiator;return t!==this._remoteVideoActive&&(w.debug("".concat(this," new remote video active: ").concat(t)),this._remoteVideoActive=t),this.peerconnection.setVideoTransferActive(this._localVideoActive&&this._remoteVideoActive)}},{key:"notifyMySSRCUpdate",value:function(e,t){if(this.state===c.a){var n=new l.a(t,e),r=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"source-remove",initiator:this.initiatorJid,sid:this.sid});n.toJingle(r)?(w.info("Sending source-remove",r.tree()),this.connection.sendIQ(r,null,this.newJingleErrorHandler(r),1e4)):w.log("removal not necessary"),n=new l.a(e,t);var i=Object(o.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"source-add",initiator:this.initiatorJid,sid:this.sid});n.toJingle(i)?(w.info("Sending source-add",i.tree()),this.connection.sendIQ(i,null,this.newJingleErrorHandler(i),1e4)):w.log("addition not necessary")}else w.warn("Skipping SSRC update in '".concat(this.state," ' state."))}},{key:"newJingleErrorHandler",value:function(e,t){var n=this;return function(e){var r={},i=$(e).find("error");if(i.length){r.code=i.attr("code");var o=$(e).find("error :first");o.length&&(r.reason=o[0].tagName);var a=i.find(">text");a.length&&(r.msg=a.text())}e||(r.reason="timeout"),r.session=n.toString(),t?t(r):n.state===c.b&&"item-not-found"===r.reason?w.debug("Jingle error: ".concat(JSON.stringify(r))):_.a.callErrorHandler(new Error("Jingle error: ".concat(JSON.stringify(r))))}}},{key:"getIceConnectionState",value:function(){return this.peerconnection.iceConnectionState}},{key:"close",value:function(){var e=this;this.state=c.b,this.establishmentDuration=void 0,this.peerconnection&&(this.peerconnection.onicecandidate=null,this.peerconnection.oniceconnectionstatechange=null,this.peerconnection.onnegotiationneeded=null,this.peerconnection.onsignalingstatechange=null),this.modificationQueue.clear(),this.modificationQueue.push(function(t){e.signalingLayer.setChatRoom(null),e.peerconnection&&e.peerconnection.close(),t()}),this.modificationQueue.shutdown()}},{key:"toString",value:function(){return"JingleSessionPC[p2p=".concat(this.isP2P,",")+"initiator=".concat(this.isInitiator,",sid=").concat(this.sid,"]")}},{key:"_abtestSuspendVideoEnabled",value:function(e){var t=e.abTesting;if(t&&t.enableSuspendVideoTest){var n=this._getInitiatorJid();return Object(a.integerHash)(n)%2==0}}}]),t})()}).call(this,"modules/xmpp/JingleSessionPC.js")},function(e,t,n){"use strict";n.r(t),n.d(t,"DEVICE_LIST_CHANGED",function(){return r}),n.d(t,"PERMISSION_PROMPT_IS_SHOWN",function(){return i});var r="mediaDevices.devicechange",i="mediaDevices.permissionPromptIsShown"},function(e,t,n){var r;r=function(){var e=!0;function t(t){function n(e){var n=t.match(e);return n&&n.length>1&&n[1]||""}function r(e){var n=t.match(e);return n&&n.length>1&&n[2]||""}var i,o=n(/(ipod|iphone|ipad)/i).toLowerCase(),a=!/like android/i.test(t)&&/android/i.test(t),s=/nexus\s*[0-6]\s*/i.test(t),c=!s&&/nexus\s*[0-9]+/i.test(t),u=/CrOS/.test(t),l=/silk/i.test(t),d=/sailfish/i.test(t),p=/tizen/i.test(t),f=/(web|hpw)os/i.test(t),h=/windows phone/i.test(t),m=(/SamsungBrowser/i.test(t),!h&&/windows/i.test(t)),v=!o&&!l&&/macintosh/i.test(t),y=!a&&!d&&!p&&!f&&/linux/i.test(t),g=r(/edg([ea]|ios)\/(\d+(\.\d+)?)/i),S=n(/version\/(\d+(\.\d+)?)/i),_=/tablet/i.test(t)&&!/tablet pc/i.test(t),b=!_&&/[^-]mobi/i.test(t),E=/xbox/i.test(t);/opera/i.test(t)?i={name:"Opera",opera:e,version:S||n(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i)}:/opr\/|opios/i.test(t)?i={name:"Opera",opera:e,version:n(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i)||S}:/SamsungBrowser/i.test(t)?i={name:"Samsung Internet for Android",samsungBrowser:e,version:S||n(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i)}:/coast/i.test(t)?i={name:"Opera Coast",coast:e,version:S||n(/(?:coast)[\s\/](\d+(\.\d+)?)/i)}:/yabrowser/i.test(t)?i={name:"Yandex Browser",yandexbrowser:e,version:S||n(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i)}:/ucbrowser/i.test(t)?i={name:"UC Browser",ucbrowser:e,version:n(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i)}:/mxios/i.test(t)?i={name:"Maxthon",maxthon:e,version:n(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i)}:/epiphany/i.test(t)?i={name:"Epiphany",epiphany:e,version:n(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i)}:/puffin/i.test(t)?i={name:"Puffin",puffin:e,version:n(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i)}:/sleipnir/i.test(t)?i={name:"Sleipnir",sleipnir:e,version:n(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i)}:/k-meleon/i.test(t)?i={name:"K-Meleon",kMeleon:e,version:n(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i)}:h?(i={name:"Windows Phone",osname:"Windows Phone",windowsphone:e},g?(i.msedge=e,i.version=g):(i.msie=e,i.version=n(/iemobile\/(\d+(\.\d+)?)/i))):/msie|trident/i.test(t)?i={name:"Internet Explorer",msie:e,version:n(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:u?i={name:"Chrome",osname:"Chrome OS",chromeos:e,chromeBook:e,chrome:e,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:/edg([ea]|ios)/i.test(t)?i={name:"Microsoft Edge",msedge:e,version:g}:/vivaldi/i.test(t)?i={name:"Vivaldi",vivaldi:e,version:n(/vivaldi\/(\d+(\.\d+)?)/i)||S}:d?i={name:"Sailfish",osname:"Sailfish OS",sailfish:e,version:n(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(t)?i={name:"SeaMonkey",seamonkey:e,version:n(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel|fxios/i.test(t)?(i={name:"Firefox",firefox:e,version:n(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(t)&&(i.firefoxos=e,i.osname="Firefox OS")):l?i={name:"Amazon Silk",silk:e,version:n(/silk\/(\d+(\.\d+)?)/i)}:/phantom/i.test(t)?i={name:"PhantomJS",phantom:e,version:n(/phantomjs\/(\d+(\.\d+)?)/i)}:/slimerjs/i.test(t)?i={name:"SlimerJS",slimer:e,version:n(/slimerjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(t)||/rim\stablet/i.test(t)?i={name:"BlackBerry",osname:"BlackBerry OS",blackberry:e,version:S||n(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:f?(i={name:"WebOS",osname:"WebOS",webos:e,version:S||n(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(t)&&(i.touchpad=e)):/bada/i.test(t)?i={name:"Bada",osname:"Bada",bada:e,version:n(/dolfin\/(\d+(\.\d+)?)/i)}:p?i={name:"Tizen",osname:"Tizen",tizen:e,version:n(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||S}:/qupzilla/i.test(t)?i={name:"QupZilla",qupzilla:e,version:n(/(?:qupzilla)[\s\/](\d+(?:\.\d+)+)/i)||S}:/chromium/i.test(t)?i={name:"Chromium",chromium:e,version:n(/(?:chromium)[\s\/](\d+(?:\.\d+)?)/i)||S}:/chrome|crios|crmo/i.test(t)?i={name:"Chrome",chrome:e,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:a?i={name:"Android",version:S}:/safari|applewebkit/i.test(t)?(i={name:"Safari",safari:e},S&&(i.version=S)):o?(i={name:"iphone"==o?"iPhone":"ipad"==o?"iPad":"iPod"},S&&(i.version=S)):i=/googlebot/i.test(t)?{name:"Googlebot",googlebot:e,version:n(/googlebot\/(\d+(\.\d+))/i)||S}:{name:n(/^(.*)\/(.*) /),version:r(/^(.*)\/(.*) /)},!i.msedge&&/(apple)?webkit/i.test(t)?(/(apple)?webkit\/537\.36/i.test(t)?(i.name=i.name||"Blink",i.blink=e):(i.name=i.name||"Webkit",i.webkit=e),!i.version&&S&&(i.version=S)):!i.opera&&/gecko\//i.test(t)&&(i.name=i.name||"Gecko",i.gecko=e,i.version=i.version||n(/gecko\/(\d+(\.\d+)?)/i)),i.windowsphone||!a&&!i.silk?!i.windowsphone&&o?(i[o]=e,i.ios=e,i.osname="iOS"):v?(i.mac=e,i.osname="macOS"):E?(i.xbox=e,i.osname="Xbox"):m?(i.windows=e,i.osname="Windows"):y&&(i.linux=e,i.osname="Linux"):(i.android=e,i.osname="Android");var T="";i.windows?T=(function(e){switch(n(/Windows ((NT|XP)( \d\d?.\d)?)/i)){case"NT":return"NT";case"XP":return"XP";case"NT 5.0":return"2000";case"NT 5.1":return"XP";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}})():i.windowsphone?T=n(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):i.mac?T=(T=n(/Mac OS X (\d+([_\.\s]\d+)*)/i)).replace(/[_\s]/g,"."):o?T=(T=n(/os (\d+([_\s]\d+)*) like mac os x/i)).replace(/[_\s]/g,"."):a?T=n(/android[ \/-](\d+(\.\d+)*)/i):i.webos?T=n(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):i.blackberry?T=n(/rim\stablet\sos\s(\d+(\.\d+)*)/i):i.bada?T=n(/bada\/(\d+(\.\d+)*)/i):i.tizen&&(T=n(/tizen[\/\s](\d+(\.\d+)*)/i)),T&&(i.osversion=T);var C=!i.windows&&T.split(".")[0];return _||c||"ipad"==o||a&&(3==C||C>=4&&!b)||i.silk?i.tablet=e:(b||"iphone"==o||"ipod"==o||a||s||i.blackberry||i.webos||i.bada)&&(i.mobile=e),i.msedge||i.msie&&i.version>=10||i.yandexbrowser&&i.version>=15||i.vivaldi&&i.version>=1||i.chrome&&i.version>=20||i.samsungBrowser&&i.version>=4||i.firefox&&i.version>=20||i.safari&&i.version>=6||i.opera&&i.version>=10||i.ios&&i.osversion&&i.osversion.split(".")[0]>=6||i.blackberry&&i.version>=10.1||i.chromium&&i.version>=20?i.a=e:i.msie&&i.version<10||i.chrome&&i.version<20||i.firefox&&i.version<20||i.safari&&i.version<6||i.opera&&i.version<10||i.ios&&i.osversion&&i.osversion.split(".")[0]<6||i.chromium&&i.version<20?i.c=e:i.x=e,i}var n=t("undefined"!=typeof navigator&&navigator.userAgent||"");function r(e){return e.split(".").length}function i(e,t){var n,r=[];if(Array.prototype.map)return Array.prototype.map.call(e,t);for(n=0;n=0;){if(n[0][t]>n[1][t])return 1;if(n[0][t]!==n[1][t])return-1;if(0===t)return 0}}function a(e,r,i){var a=n;"string"==typeof r&&(i=r,r=void 0),void 0===r&&(r=!1),i&&(a=t(i));var s=""+a.version;for(var c in e)if(e.hasOwnProperty(c)&&a[c]){if("string"!=typeof e[c])throw new Error("Browser version in the minVersion map should be a string: "+c+": "+String(e));return o([s,e[c]])<0}return r}return n.test=function(e){for(var t=0;t1)for(var n=1;n.2?i-.2:a<-.4?i+.4:r,parseFloat(o.toFixed(3))),t.callback(t.audioLevel))},this.intervalMilis)}},i.prototype.stop=function(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)},i.isLocalStatsSupported=function(){return Boolean(r)}},function(e,t,n){var r=n(124),i={loadScript:function(e,t,n,i,o,a){var s=document,c=s.createElement("script"),u=s.getElementsByTagName("script")[0];if(c.async=t,i){var l=r();if(l){var d=l.src,p=d.substring(0,d.lastIndexOf("/")+1);d&&p&&(e=p+e)}}o&&(c.onload=o),a&&(c.onerror=a),c.src=e,n?u.parentNode.insertBefore(c,u):u.parentNode.appendChild(c)}};e.exports=i},function(e,t,n){"use strict";n.r(t),n.d(t,"ON",function(){return r}),n.d(t,"OFF",function(){return i});var r="on",i="off"},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return y});var r=n(29),i=n.n(r),o=n(4),a=n(16),s=n(9),c=n(10);function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function l(e,t){for(var n=0;n=0;--n){var r=t[n];e||(this._onTrackDetach(r),c.a.attachMediaStream(r,null)),e&&r!==e||t.splice(n,1)}e&&(this._onTrackDetach(e),c.a.attachMediaStream(e,null))}},{key:"_onTrackAttach",value:function(e){}},{key:"_onTrackDetach",value:function(e){}},{key:"_attachTTFMTracker",value:function(e){}},{key:"dispose",value:function(){return this.removeAllListeners(),this.disposed=!0,Promise.resolve()}},{key:"isScreenSharing",value:function(){}},{key:"getId",value:function(){return this.stream?c.a.getStreamID(this.stream):null}},{key:"isActive",value:function(){return void 0===this.stream.active||this.stream.active}},{key:"setAudioLevel",value:function(e,t){this.audioLevel!==e&&(this.audioLevel=e,this.emit(a.TRACK_AUDIO_LEVEL_CHANGED,e,t))}},{key:"getMSID",value:function(){var e=this.getStreamId(),t=this.getTrackId();return e&&t?"".concat(e," ").concat(t):null}},{key:"setAudioOutput",value:function(e){var t=this;return c.a.isDeviceChangeAvailable("output")?this.isVideoTrack()?Promise.resolve():Promise.all(this.containers.map(function(t){return t.setSinkId(e).catch(function(e){throw h.warn("Failed to change audio output device on element. Default or previously set audio output device will be used.",t,e),e})})).then(function(){t.emit(a.TRACK_AUDIO_OUTPUT_CHANGED,e)}):Promise.reject(new Error("Audio output device change is not supported"))}}])&&l(n.prototype,r),t})()}).call(this,"modules/RTC/JitsiTrack.js")},function(e,t,n){"use strict";function r(e){var t=e;return e>=Number.MAX_SAFE_INTEGER&&(t=0),t+1}n.d(t,"a",function(){return r})},function(e,t){function n(e,t){for(var n=0;n0}},{key:"setDominantSpeaker",value:function(e){if(!this.isDominantSpeaker()&&e)this._dominantSpeakerStart=Date.now();else if(this.isDominantSpeaker()&&!e){var t=Date.now()-this._dominantSpeakerStart;this.totalDominantSpeakerTime+=t,this._dominantSpeakerStart=0}}},{key:"getTotalDominantSpeakerTime",value:function(){var e=this.totalDominantSpeakerTime;return this.isDominantSpeaker()&&(e+=Date.now()-this._dominantSpeakerStart),e}},{key:"hasLeft",value:function(){return this._hasLeft}},{key:"markAsHasLeft",value:function(){this._hasLeft=!0,this.setDominantSpeaker(!1)}}])&&n(t.prototype,r),e})();e.exports=r},function(e,t){e.exports={IDENTITY_UPDATED:"authentication.identity_updated"}},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return A});var r=n(4),i=n(53),o=n(15),a=n(12),s=n(16),c=n(3),u=n(10),l=n(39),d=n.n(l),p=n(9),f=n(8),h=n.n(f),m=n(14),v=n.n(m),y=n(7),g=n(5);function S(e){return(S="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function _(e,t){for(var n=0;n0&&(this._hasSentData=!0);var r=e.getConnectionState();this._testDataSent&&"connected"===r&&(setTimeout(function(){n._hasSentData||(R.warn("".concat(n," 'bytes sent' <= 0: ").concat(t)),g.a.analytics.sendEvent(y.r,{media_type:n.getType()}))},3e3),this._testDataSent=!1)}},{key:"getCameraFacingMode",value:function(){if(this.isVideoTrack()&&this.videoType===v.a.CAMERA){var e;try{e=this.track.getSettings()}catch(e){}return e&&"facingMode"in e?e.facingMode:void 0!==this._facingMode?this._facingMode:d.a.USER}}},{key:"stopStream",value:function(){this._stopStreamInProgress=!0;try{u.a.stopMediaStream(this.stream)}finally{this._stopStreamInProgress=!1}}},{key:"_switchCamera",value:function(){this.isVideoTrack()&&this.videoType===v.a.CAMERA&&"function"==typeof this.track._switchCamera&&(this.track._switchCamera(),this._facingMode=this._facingMode===d.a.ENVIRONMENT?d.a.USER:d.a.ENVIRONMENT)}},{key:"isReceivingData",value:function(){return!(!this.isVideoTrack()||!this.isMuted()&&!this._stopStreamInProgress&&this.videoType!==v.a.DESKTOP)||!!this.stream&&(this._effectEnabled?this._originalStream:this.stream).getTracks().some(function(e){return!("readyState"in e&&"live"!==e.readyState||"muted"in e&&!0===e.muted)})}},{key:"toString",value:function(){return"LocalTrack[".concat(this.rtcId,",").concat(this.getType(),"]")}}])&&_(n.prototype,r),t})()}).call(this,"modules/RTC/JitsiLocalTrack.js")},function(e,t,n){"use strict";n.d(t,"a",function(){return a});var r=n(1),i=n(42);function o(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};!(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._connection=t.connection,this._mode=t.mode,this._setSessionID(t.sessionID),this.setStatus(t.status)}var t,n;return t=e,(n=[{key:"getError",value:function(){return this._error}},{key:"getID",value:function(){return this._sessionID}},{key:"getLiveStreamViewURL",value:function(){return this._liveStreamViewURL}},{key:"getStatus",value:function(){return this._status}},{key:"getMode",value:function(){return this._mode}},{key:"setError",value:function(e){this._error=e}},{key:"setLiveStreamViewURL",value:function(e){this._liveStreamViewURL=e}},{key:"setStatus",value:function(e){this._status=e}},{key:"start",value:function(e){var t=this,n=e.appData,r=e.broadcastId,o=e.focusMucJid,a=e.streamId;return new Promise(function(e,s){t._connection.sendIQ(t._createIQ({action:"start",appData:n,focusMucJid:o,broadcastId:r,streamId:a}),function(n){t.setStatus("pending"),t._setSessionID(i.a.getSessionIdFromIq(n)),e()},function(e){t._setErrorFromIq(e),s(e)})})}},{key:"stop",value:function(e){var t=this,n=e.focusMucJid;return new Promise(function(e,r){t._connection.sendIQ(t._createIQ({action:"stop",focusMucJid:n}),e,r)})}},{key:"_createIQ",value:function(e){var t=e.action,n=e.appData,i=e.broadcastId,o=e.focusMucJid,a=e.streamId;return Object(r.$iq)({to:o,type:"set"}).c("jibri",{xmlns:"http://jitsi.org/protocol/jibri",action:t,app_data:n,recording_mode:this._mode,streamid:a,you_tube_broadcast_id:i}).up()}},{key:"_setErrorFromIq",value:function(e){var t=e.getElementsByTagName("error")[0];this.setError(t.children[0].tagName)}},{key:"_setSessionID",value:function(e){this._sessionID=e}}])&&o(t.prototype,n),e})()},function(e,t,n){"use strict";var r=n(29),i=n.n(r),o=n(9),a=n(3),s=n(13),c=n(8),u=n.n(c),l=n(5),d=n(45);function p(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function f(e,t){for(var n=0;n0&&this._logOutputDevice(e,s.a.getCurrentlyAvailableMediaDevices()),s.a.setAudioOutputDevice(e)}},{key:"addEventListener",value:function(e,t){this._eventEmitter.addListener(e,t)}},{key:"removeEventListener",value:function(e,t){this._eventEmitter.removeListener(e,t)}},{key:"emitEvent",value:function(e){for(var t,n=arguments.length,r=new Array(n>1?n-1:0),i=1;i1&&d.push("<"+a.methodName+">: ");var p=d.concat(o);l.bind(u).apply(u,p)}}}function a(e,t,r,i){this.id=t,this.options=i||{},this.transports=r,this.transports||(this.transports=[]),this.level=n[e];for(var a=Object.keys(n),s=0;s0?t[0].split("/")[1]:"sendrecv",uri:t[1]}},n.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+"\r\n"},n.parseFmtp=function(e){for(var t,n={},r=e.substr(e.indexOf(" ")+1).split(";"),i=0;i-1?(n.attribute=e.substr(t+1,r-t-1),n.value=e.substr(r+1)):n.attribute=e.substr(t+1),n},n.parseSsrcGroup=function(e){var t=e.substr(13).split(" ");return{semantics:t.shift(),ssrcs:t.map(function(e){return parseInt(e,10)})}},n.getMid=function(e){var t=n.matchPrefix(e,"a=mid:")[0];if(t)return t.substr(6)},n.parseFingerprint=function(e){var t=e.substr(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1]}},n.getDtlsParameters=function(e,t){return{role:"auto",fingerprints:n.matchPrefix(e+t,"a=fingerprint:").map(n.parseFingerprint)}},n.writeDtlsParameters=function(e,t){var n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(function(e){n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},n.getIceParameters=function(e,t){var r=n.splitLines(e);return{usernameFragment:(r=r.concat(n.splitLines(t))).filter(function(e){return 0===e.indexOf("a=ice-ufrag:")})[0].substr(12),password:r.filter(function(e){return 0===e.indexOf("a=ice-pwd:")})[0].substr(10)}},n.writeIceParameters=function(e){return"a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n"},n.parseRtpParameters=function(e){for(var t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=n.splitLines(e)[0].split(" "),i=3;i0?"9":"0",r+=" UDP/TLS/RTP/SAVPF ",r+=t.codecs.map(function(e){return void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType}).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach(function(e){r+=n.writeRtpMap(e),r+=n.writeFmtp(e),r+=n.writeRtcpFb(e)});var i=0;return t.codecs.forEach(function(e){e.maxptime>i&&(i=e.maxptime)}),i>0&&(r+="a=maxptime:"+i+"\r\n"),r+="a=rtcp-mux\r\n",t.headerExtensions&&t.headerExtensions.forEach(function(e){r+=n.writeExtmap(e)}),r},n.parseRtpEncodingParameters=function(e){var t,r=[],i=n.parseRtpParameters(e),o=-1!==i.fecMechanisms.indexOf("RED"),a=-1!==i.fecMechanisms.indexOf("ULPFEC"),s=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute}),c=s.length>0&&s[0].ssrc,u=n.matchPrefix(e,"a=ssrc-group:FID").map(function(e){return e.substr(17).split(" ").map(function(e){return parseInt(e,10)})});u.length>0&&u[0].length>1&&u[0][0]===c&&(t=u[0][1]),i.codecs.forEach(function(e){if("RTX"===e.name.toUpperCase()&&e.parameters.apt){var n={ssrc:c,codecPayloadType:parseInt(e.parameters.apt,10)};c&&t&&(n.rtx={ssrc:t}),r.push(n),o&&((n=JSON.parse(JSON.stringify(n))).fec={ssrc:c,mechanism:a?"red+ulpfec":"red"},r.push(n))}}),0===r.length&&c&&r.push({ssrc:c});var l=n.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substr(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substr(5),10)*.95-16e3:void 0,r.forEach(function(e){e.maxBitrate=l})),r},n.parseRtcpParameters=function(e){var t={},r=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute})[0];r&&(t.cname=r.value,t.ssrc=r.ssrc);var i=n.matchPrefix(e,"a=rtcp-rsize");t.reducedSize=i.length>0,t.compound=0===i.length;var o=n.matchPrefix(e,"a=rtcp-mux");return t.mux=o.length>0,t},n.parseMsid=function(e){var t,r=n.matchPrefix(e,"a=msid:");if(1===r.length)return{stream:(t=r[0].substr(7).split(" "))[0],track:t[1]};var i=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"msid"===e.attribute});return i.length>0?{stream:(t=i[0].value.split(" "))[0],track:t[1]}:void 0},n.parseSctpDescription=function(e){var t,r=n.parseMLine(e),i=n.matchPrefix(e,"a=max-message-size:");i.length>0&&(t=parseInt(i[0].substr(19),10)),isNaN(t)&&(t=65536);var o=n.matchPrefix(e,"a=sctp-port:");if(o.length>0)return{port:parseInt(o[0].substr(12),10),protocol:r.fmt,maxMessageSize:t};if(n.matchPrefix(e,"a=sctpmap:").length>0){var a=n.matchPrefix(e,"a=sctpmap:")[0].substr(10).split(" ");return{port:parseInt(a[0],10),protocol:a[1],maxMessageSize:t}}},n.writeSctpDescription=function(e,t){var n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},n.generateSessionId=function(){return Math.random().toString().substr(2,21)},n.writeSessionBoilerplate=function(e,t,r){var i=void 0!==t?t:2;return"v=0\r\no="+(r||"thisisadapterortc")+" "+(e||n.generateSessionId())+" "+i+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},n.writeMediaSection=function(e,t,r,i){var o=n.writeRtpDescription(e.kind,t);if(o+=n.writeIceParameters(e.iceGatherer.getLocalParameters()),o+=n.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===r?"actpass":"active"),o+="a=mid:"+e.mid+"\r\n",e.direction?o+="a="+e.direction+"\r\n":e.rtpSender&&e.rtpReceiver?o+="a=sendrecv\r\n":e.rtpSender?o+="a=sendonly\r\n":e.rtpReceiver?o+="a=recvonly\r\n":o+="a=inactive\r\n",e.rtpSender){var a="msid:"+i.id+" "+e.rtpSender.track.id+"\r\n";o+="a="+a,o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+a,e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" "+a,o+="a=ssrc-group:FID "+e.sendEncodingParameters[0].ssrc+" "+e.sendEncodingParameters[0].rtx.ssrc+"\r\n")}return o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+n.localCName+"\r\n",e.rtpSender&&e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" cname:"+n.localCName+"\r\n"),o},n.getDirection=function(e,t){for(var r=n.splitLines(e),i=0;i0&&t.data.push(e.data)},t},l.prototype.removeTrack=function(e){if(!e.isVideoTrack()){var t,n=this.recorders;for(t=0;t0&&void 0!==arguments[0]?arguments[0]:{};this.xmpp.connect(e.id,e.password)},c.prototype.attach=function(e){this.xmpp.attach(e)},c.prototype.disconnect=function(){var e;return(e=this.xmpp).disconnect.apply(e,arguments)},c.prototype.getJid=function(){return this.xmpp.getJid()},c.prototype.setToken=function(e){this.token=e},c.prototype.initJitsiConference=function(e,t){return new i.a({name:e,config:t,connection:this})},c.prototype.addEventListener=function(e,t){this.xmpp.addListener(e,t)},c.prototype.removeEventListener=function(e,t){this.xmpp.removeListener(e,t)},c.prototype.getConnectionTimes=function(){return this.xmpp.connectionTimes},c.prototype.addFeature=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return this.xmpp.caps.addFeature(e,t)},c.prototype.removeFeature=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return this.xmpp.caps.removeFeature(e,t)}},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return ee});var r=n(1),i=n(29),o=n.n(i),a=n(4),s=n(68),c=n.n(s),u=n(17),l=n(69),d=n(2),p=n(76),f=n(15),h=n(12),m=n(16),v=n(77),y=n(94),g=n.n(y),S=n(95),_=n(13),b=n(102),E=n(3),T=n(103),C=n(104),R=n(41),A=n(105),w=n(106),k=n(107),O=n(108),I=n(109),P=n(110),D=n(111),N=n(5),L=n(112),M=n.n(L),x=n(11),j=n.n(x),F=n(33),U=n.n(F),H=n(113),J=n(114),G=n(20),B=n(35),V=n(9),q=n(8),K=n(14),W=n.n(K),z=n(7),X=n(0);function Q(e){return(Q="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function Y(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var Z=Object(a.getLogger)(e);function ee(e){if(!e.name||e.name.toLowerCase()!==e.name){var t="Invalid conference name (no conference name passed or it contains invalid characters like capital letters)!";throw Z.error(t),new Error(t)}this.eventEmitter=new o.a,this.options=e,this.eventManager=new l.a(this),this.participants={},this._init(e),this.componentsVersions=new H.a(this),this.jvbJingleSession=null,this.lastDominantSpeaker=null,this.dtmfManager=null,this.somebodySupportsDTMF=!1,this.authEnabled=!1,this.startAudioMuted=!1,this.startVideoMuted=!1,this.startMutedPolicy={audio:!1,video:!1},this.isMutedByFocus=!1,this.mutedByFocusActor=null,this.wasStopped=!1,this.properties={},this.connectionQuality=new T.a(this,this.eventEmitter,e),this.avgRtpStatsReporter=new I.a(this,e.config.avgRtpStatsN||15),this._audioOutputProblemDetector=new P.a(this),this.isJvbConnectionInterrupted=!1,this.speakerStatsCollector=new D.a(this),this.deferredStartP2PTask=null;var n=parseInt(e.config.p2p&&e.config.p2p.backToP2PDelay,10);this.backToP2PDelay=isNaN(n)?5:n,Z.info("backToP2PDelay: ".concat(this.backToP2PDelay)),this.isP2PConnectionInterrupted=!1,this.p2p=!1,this.p2pJingleSession=null,this.videoSIPGWHandler=new J.a(this.room),this.recordingManager=new k.a(this.room)}ee.prototype.constructor=ee,ee.resourceCreator=function(e,t){var n;return t?n=U.a.randomHexString(8).toLowerCase():(n=r.Strophe.getNodeFromJid(e).substr(0,8).toLowerCase(),/[0-9a-f]{8}/g.test(n)||(n=U.a.randomHexString(8).toLowerCase())),n},ee.prototype._init=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t.connection&&(this.connection=t.connection,this.xmpp=this.connection.xmpp,this.eventManager.setupXMPPListeners());var n=this.options.config;if(this.room=this.xmpp.createRoom(this.options.name,n,ee.resourceCreator),this._onIceConnectionInterrupted=this._onIceConnectionInterrupted.bind(this),this.room.addListener(X.CONNECTION_INTERRUPTED,this._onIceConnectionInterrupted),this._onIceConnectionRestored=this._onIceConnectionRestored.bind(this),this.room.addListener(X.CONNECTION_RESTORED,this._onIceConnectionRestored),this._onIceConnectionEstablished=this._onIceConnectionEstablished.bind(this),this.room.addListener(X.CONNECTION_ESTABLISHED,this._onIceConnectionEstablished),this._updateProperties=this._updateProperties.bind(this),this.room.addListener(X.CONFERENCE_PROPERTIES_CHANGED,this._updateProperties),this.rttMonitor=new O.a(n.rttMonitor||{}),this.e2eping=new A.a(this,n,function(t,n){try{e.sendMessage(t,n,!0)}catch(e){Z.warn("Failed to send a ping request or response.")}}),this.rtc||(this.rtc=new _.a(this,t),this.eventManager.setupRTCListeners()),this.participantConnectionStatus=new R.b(this.rtc,this,{rtcMuteTimeout:n._peerConnStatusRtcMuteTimeout,outOfLastNTimeout:n._peerConnStatusOutOfLastNTimeout}),this.participantConnectionStatus.init(),!this.statistics){var r=this.myUserId();n.enableDisplayNameInStats&&n.displayName&&(r=n.displayName),this.statistics=new N.a(this.xmpp,{callStatsAliasName:r,callStatsConfIDNamespace:this.connection.options.hosts.domain,confID:n.confID||"".concat(this.connection.options.hosts.domain,"/").concat(this.options.name),customScriptUrl:n.callStatsCustomScriptUrl,callStatsID:n.callStatsID,callStatsSecret:n.callStatsSecret,roomName:this.options.name,swapUserNameAndAlias:n.enableStatsID,applicationName:n.applicationName,getWiFiStatsMethod:n.getWiFiStatsMethod})}this.eventManager.setupChatRoomListeners(),this.eventManager.setupStatisticsListeners(),n.enableTalkWhileMuted&&new b.a(this,function(){return e.eventEmitter.emit(d.TALK_WHILE_MUTED)}),"channelLastN"in n&&this.setLastN(n.channelLastN),this.jvb121Status=new w.a(this),this.p2pDominantSpeakerDetection=new S.a(this),n&&n.deploymentInfo&&n.deploymentInfo.userRegion&&this.setLocalParticipantProperty("region",n.deploymentInfo.userRegion)},ee.prototype.join=function(e){var t=this;this.room&&this.room.join(e).then(function(){return t._maybeSetSITimeout()})},ee.prototype.authenticateAndUpgradeRole=function(e){return v.a.call(this,(function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:"body";if(this.room){var n=(this.room.getFromPresence("nick")||{}).value;this.room.sendMessage(e,t,n)}},ee.prototype.sendPrivateTextMessage=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"body";this.room&&this.room.sendPrivateMessage(e,t,n)},ee.prototype.sendCommand=function(e,t){this.room?(this.room.addToPresence(e,t),this.room.sendPresence()):Z.warn("Not sending a command, room not initialized.")},ee.prototype.sendCommandOnce=function(e,t){this.sendCommand(e,t),this.removeCommand(e)},ee.prototype.removeCommand=function(e){this.room&&this.room.removeFromPresence(e)},ee.prototype.setDisplayName=function(e){this.room&&(this.room.removeFromPresence("nick"),this.room.addToPresence("nick",{attributes:{xmlns:"http://jabber.org/protocol/nick"},value:e}),this.room.sendPresence())},ee.prototype.setSubject=function(e){this.room&&this.isModerator()&&this.room.setSubject(e)},ee.prototype.getTranscriber=function(){if(void 0===this.transcriber){this.transcriber=new M.a;var e=this.getLocalTracks(V.a),t=!0,n=!1,r=void 0;try{for(var i,o=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;this.transcriber.addTrack(a)}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}var s=this.rtc.getRemoteTracks(V.a),c=!0,u=!1,l=void 0;try{for(var d,p=s["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(c=(d=p.next()).done);c=!0){var f=d.value;this.transcriber.addTrack(f)}}catch(e){u=!0,l=e}finally{try{c||null==p.return||p.return()}finally{if(u)throw l}}}return this.transcriber},ee.prototype.getTranscriptionStatus=function(){return this.room.transcriptionStatus},ee.prototype.addTrack=function(e){if(e.isVideoTrack()){var t=this.rtc.getLocalVideoTrack();if(t)return e===t?Promise.resolve(e):Promise.reject(new Error("cannot add second video track to the conference"))}return this.replaceTrack(null,e)},ee.prototype._fireAudioLevelChangeEvent=function(e,t){var n=this.getActivePeerConnection();t&&n!==t||this.eventEmitter.emit(d.TRACK_AUDIO_LEVEL_CHANGED,this.myUserId(),e)},ee.prototype._fireMuteChangeEvent=function(e){var t;if(this.isMutedByFocus&&e.isAudioTrack()&&!e.isMuted()&&(this.isMutedByFocus=!1,this.room.muteParticipant(this.room.myroomjid,!1)),this.mutedByFocusActor){var n=r.Strophe.getResourceFromJid(this.mutedByFocusActor);t=this.participants[n]}this.eventEmitter.emit(d.TRACK_MUTE_CHANGED,e,t)},ee.prototype.onLocalTrackRemoved=function(e){e._setConference(null),this.rtc.removeLocalTrack(e),e.removeEventListener(m.TRACK_MUTE_CHANGED,e.muteHandler),e.removeEventListener(m.TRACK_AUDIO_LEVEL_CHANGED,e.audioLevelHandler),e.isVideoTrack()&&e.videoType===W.a.DESKTOP&&this.statistics.sendScreenSharingEvent(!1),this.eventEmitter.emit(d.TRACK_REMOVED,e)},ee.prototype.removeTrack=function(e){return this.replaceTrack(e,null)},ee.prototype.replaceTrack=function(e,t){var n=this;return e&&e.disposed?Promise.reject(new f.a(h.TRACK_IS_DISPOSED)):t&&t.disposed?Promise.reject(new f.a(h.TRACK_IS_DISPOSED)):this._doReplaceTrack(e,t).then(function(){return e&&n.onLocalTrackRemoved(e),t&&n._setupNewTrack(t),Promise.resolve()},function(e){return Promise.reject(new Error(e))})},ee.prototype._doReplaceTrack=function(e,t){var n=[];return this.jvbJingleSession?n.push(this.jvbJingleSession.replaceTrack(e,t)):Z.info("_doReplaceTrack - no JVB JingleSession"),this.p2pJingleSession?n.push(this.p2pJingleSession.replaceTrack(e,t)):Z.info("_doReplaceTrack - no P2P JingleSession"),Promise.all(n)},ee.prototype._setupNewTrack=function(e){if(e.isAudioTrack()||e.isVideoTrack()&&e.videoType!==W.a.DESKTOP){var t=_.a.getCurrentlyAvailableMediaDevices().find(function(t){return t.kind==="".concat(e.getTrack().kind,"input")&&t.label===e.getTrack().label});t&&N.a.sendActiveDeviceListEvent(_.a.getEventDataForActiveDevice(t))}e.isVideoTrack()&&(this.removeCommand("videoType"),this.sendCommand("videoType",{value:e.videoType,attributes:{xmlns:"http://jitsi.org/jitmeet/video"}})),this.rtc.addLocalTrack(e),e.isAudioTrack()?this.room.setAudioMute(e.isMuted()):this.room.setVideoMute(e.isMuted()),e.muteHandler=this._fireMuteChangeEvent.bind(this,e),e.audioLevelHandler=this._fireAudioLevelChangeEvent.bind(this),e.addEventListener(m.TRACK_MUTE_CHANGED,e.muteHandler),e.addEventListener(m.TRACK_AUDIO_LEVEL_CHANGED,e.audioLevelHandler),e._setConference(this),this.eventEmitter.emit(d.TRACK_ADDED,e)},ee.prototype._addLocalTrackAsUnmute=function(e){var t=[];return this.jvbJingleSession?t.push(this.jvbJingleSession.addTrackAsUnmute(e)):Z.info("Add local MediaStream as unmute - no JVB Jingle session started yet"),this.p2pJingleSession?t.push(this.p2pJingleSession.addTrackAsUnmute(e)):Z.info("Add local MediaStream as unmute - no P2P Jingle session started yet"),Promise.all(t)},ee.prototype._removeLocalTrackAsMute=function(e){var t=[];return this.jvbJingleSession?t.push(this.jvbJingleSession.removeTrackAsMute(e)):Z.info("Remove local MediaStream - no JVB JingleSession started yet"),this.p2pJingleSession?t.push(this.p2pJingleSession.removeTrackAsMute(e)):Z.info("Remove local MediaStream - no P2P JingleSession started yet"),Promise.all(t)},ee.prototype.getRole=function(){return this.room.role},ee.prototype.isHidden=function(){return this.connection?r.Strophe.getDomainFromJid(this.connection.getJid())===this.options.config.hiddenDomain:null},ee.prototype.isModerator=function(){return this.room?this.room.isModerator():null},ee.prototype.lock=function(e){var t=this;return this.isModerator()?new Promise(function(n,r){t.room.lockRoom(e||"",function(){return n()},function(e){return r(e)},function(){return r(u.PASSWORD_NOT_SUPPORTED)})}):Promise.reject(new Error("You are not moderator."))},ee.prototype.unlock=function(){return this.lock()},ee.prototype.selectParticipant=function(e){this.selectParticipants([e])},ee.prototype.selectParticipants=function(e){if(!Array.isArray(e))throw new Error("Invalid argument; participantIds must be an array.");this.rtc.selectEndpoints(e)},ee.prototype.pinParticipant=function(e){this.rtc.pinEndpoint(e)},ee.prototype.getLastN=function(){return this.rtc.getLastN()},ee.prototype.setLastN=function(e){if(!Number.isInteger(e)&&!Number.parseInt(e,10))throw new Error("Invalid value for lastN: ".concat(e));var t=Number(e);if(t<-1)throw new RangeError("lastN cannot be smaller than -1");if(this.rtc.setLastN(t),this.p2pJingleSession){var n=0!==t;this.p2pJingleSession.setMediaTransferActive(!0,n).catch(function(e){Z.error("Failed to adjust video transfer status (".concat(n,")"),e)})}},ee.prototype.isInLastN=function(e){return this.rtc.isInLastN(e)},ee.prototype.getParticipants=function(){return Object.keys(this.participants).map(function(e){return this.participants[e]},this)},ee.prototype.getParticipantCount=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=this.getParticipants();return e||(t=t.filter(function(e){return!e.isHidden()})),t.length+1},ee.prototype.getParticipantById=function(e){return this.participants[e]},ee.prototype.kickParticipant=function(e){var t=this.getParticipantById(e);t&&this.room.kick(t.getJid())},ee.prototype._maybeClearSITimeout=function(){this._sessionInitiateTimeout&&(this.jvbJingleSession||this.getParticipantCount()<2)&&(window.clearTimeout(this._sessionInitiateTimeout),this._sessionInitiateTimeout=null)},ee.prototype._maybeSetSITimeout=function(){var e=this;!this.jvbJingleSession&&this.getParticipantCount()>=2&&!this._sessionInitiateTimeout&&(this._sessionInitiateTimeout=window.setTimeout(function(){e._sessionInitiateTimeout=null,N.a.sendAnalytics(Object(z.F)(z.d,{p2p:!1,value:5e3}))},5e3))},ee.prototype.muteParticipant=function(e){var t=this.getParticipantById(e);t&&this.room.muteParticipant(t.getJid(),!0)},ee.prototype.onMemberJoined=function(e,t,n,i,o,a,s,c){var u=r.Strophe.getResourceFromJid(e);if("focus"!==u&&this.myUserId()!==u){var l=new p.a(e,this,t,i,o,a,s);l._role=n,l._botType=c,this.participants[u]=l,this.eventEmitter.emit(d.USER_JOINED,u,l),this._updateFeatures(l),this._maybeStartOrStopP2P(),this._maybeSetSITimeout()}},ee.prototype._updateFeatures=function(e){var t=this;e.getFeatures().then(function(n){e._supportsDTMF=n.has("urn:xmpp:jingle:dtmf:0"),t.updateDTMFSupport(),n.has("http://jitsi.org/protocol/jigasi")&&e.setProperty("features_jigasi",!0)}).catch(function(){return!1})},ee.prototype._onMemberBotTypeChanged=function(e,t){var n=this.getParticipants().find(function(t){return t.getJid()===e});if(n){n._botType=t;var i=r.Strophe.getResourceFromJid(e);this.eventEmitter.emit(d.BOT_TYPE_CHANGED,i,t)}n._botType||this._maybeStartOrStopP2P()},ee.prototype.onMemberLeft=function(e){var t=this,n=r.Strophe.getResourceFromJid(e);if("focus"!==n&&this.myUserId()!==n){var i=this.participants[n];delete this.participants[n],this.rtc.removeRemoteTracks(n).forEach(function(e){return t.eventEmitter.emit(d.TRACK_REMOVED,e)}),i&&this.eventEmitter.emit(d.USER_LEFT,n,i),this._maybeStartOrStopP2P(!0),this._maybeClearSITimeout()}},ee.prototype.onMemberKicked=function(e,t,n){var r=this.participants[t];if(e)return this.eventEmitter.emit(d.KICKED,r),void this.leave();var i=this.participants[n];this.eventEmitter.emit(d.PARTICIPANT_KICKED,r,i)},ee.prototype.onLocalRoleChanged=function(e){this.eventEmitter.emit(d.USER_ROLE_CHANGED,this.myUserId(),e)},ee.prototype.onUserRoleChanged=function(e,t){var n=r.Strophe.getResourceFromJid(e),i=this.getParticipantById(n);i&&(i._role=t,this.eventEmitter.emit(d.USER_ROLE_CHANGED,n,t))},ee.prototype.onDisplayNameChanged=function(e,t){var n=r.Strophe.getResourceFromJid(e),i=this.getParticipantById(n);i&&i._displayName!==t&&(i._displayName=t,this.eventEmitter.emit(d.DISPLAY_NAME_CHANGED,n,t))},ee.prototype.onRemoteTrackAdded=function(e){var t=this;if(!e.isP2P||this.isP2PActive())if(e.isP2P||!this.isP2PActive()){var n=e.getParticipantId(),r=this.getParticipantById(n);if(r){r._tracks.push(e),this.transcriber&&this.transcriber.addTrack(e);var i=this.eventEmitter;e.addEventListener(m.TRACK_MUTE_CHANGED,function(){return i.emit(d.TRACK_MUTE_CHANGED,e)}),e.addEventListener(m.TRACK_AUDIO_LEVEL_CHANGED,function(e,r){t.getActivePeerConnection()===r&&i.emit(d.TRACK_AUDIO_LEVEL_CHANGED,n,e)}),i.emit(d.TRACK_ADDED,e)}else Z.error("No participant found for id: ".concat(n))}else Z.info("Trying to add remote JVB track, when in P2P - IGNORED");else Z.info("Trying to add remote P2P track, when not in P2P - IGNORED")},ee.prototype.onCallAccepted=function(e,t){this.p2pJingleSession===e&&(Z.info("P2P setAnswer"),this.p2pJingleSession.setAnswer(t))},ee.prototype.onTransportInfo=function(e,t){this.p2pJingleSession===e&&(Z.info("P2P addIceCandidates"),this.p2pJingleSession.addIceCandidates(t))},ee.prototype.onRemoteTrackRemoved=function(e){var t=this;this.getParticipants().forEach(function(n){for(var r=n.getTracks(),i=0;ibridge-session[xmlns="http://jitsi.org/protocol/focus"]').attr("region");this.eventEmitter.emit(d.SERVER_REGION_CHANGED,i),this._maybeClearSITimeout(),N.a.sendAnalytics(Object(z.F)(z.c,{p2p:!1,value:n}));try{e.initialize(this.room,this.rtc,this.options.config)}catch(e){j.a.callErrorHandler(e)}this._setBridgeChannel(t,e.peerconnection);try{e.acceptOffer(t,function(){r.isP2PActive()&&r.jvbJingleSession&&r._suspendMediaTransferForJvbConnection()},function(e){j.a.callErrorHandler(e),Z.error("Failed to accept incoming Jingle session",e)},this.getLocalTracks()),Z.info("Starting CallStats for JVB connection..."),this.statistics.startCallStats(this.jvbJingleSession.peerconnection,"jitsi"),this.statistics.startRemoteStats(this.jvbJingleSession.peerconnection)}catch(e){j.a.callErrorHandler(e),Z.error(e)}},ee.prototype._setBridgeChannel=function(e,t){var n,r=null,i=$(e).find(">content>transport>web-socket").first();switch(1===i.length&&(r=i[0].getAttribute("url")),this.options.config.openBridgeChannel){case"datachannel":case!0:case void 0:n="datachannel";break;case"websocket":n="websocket"}"datachannel"!==n||E.a.supportsDataChannels()||(n="websocket"),"datachannel"===n?this.rtc.initializeBridgeChannel(t,null):"websocket"===n&&r&&this.rtc.initializeBridgeChannel(null,r)},ee.prototype._rejectIncomingCall=function(e,t){t&&t.errorMsg&&j.a.callErrorHandler(new Error(t.errorMsg)),e.terminate(null,function(e){Z.warn("An error occurred while trying to terminate invalid Jingle session",e)},{reason:t&&t.reason,reasonDescription:t&&t.reasonDescription,sendSessionTerminate:!0})},ee.prototype.onCallEnded=function(e,t,n){Z.info("Call ended: ".concat(t," - ").concat(n," P2P ?").concat(e.isP2P)),e===this.jvbJingleSession?(this.wasStopped=!0,N.a.sendAnalytics(Object(z.F)(z.e,{p2p:!1})),this.statistics&&(this.statistics.stopRemoteStats(this.jvbJingleSession.peerconnection),Z.info("Stopping JVB CallStats"),this.statistics.stopCallStats(this.jvbJingleSession.peerconnection)),this.jvbJingleSession=null,this.rtc.onCallEnded()):e===this.p2pJingleSession?("decline"===t&&"force JVB121"===n?(Z.info("In forced JVB 121 mode..."),N.a.analytics.addPermanentProperties({forceJvb121:!0})):"connectivity-error"===t&&"ICE FAILED"===n&&N.a.analytics.addPermanentProperties({p2pFailed:!0}),this._stopP2PSession()):Z.error("Received onCallEnded for invalid session",e.sid,e.remoteJid,t,n)},ee.prototype.onSuspendDetected=function(e){e.isP2P||(this.leave(),this.eventEmitter.emit(d.SUSPEND_DETECTED))},ee.prototype.updateDTMFSupport=function(){for(var e=!1,t=this.getParticipants(),n=0;n1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=Q(e);if("object"===r||!n&&"string"===r)if(n)this.sendEndpointMessage(t,e);else{var i=e,o="body";if("object"===r){o="json-message",i.hasOwnProperty(B.b)||(i[B.b]="");try{i=JSON.stringify(i)}catch(e){return void Z.error("Can not send a message, stringify failed: ",e)}}t?this.sendPrivateTextMessage(t,i,o):this.sendTextMessage(i,o)}else Z.error("Can not send a message of type ".concat(r))},ee.prototype.isConnectionInterrupted=function(){return this.isP2PActive()?this.isP2PConnectionInterrupted:this.isJvbConnectionInterrupted},ee.prototype._onIceConnectionInterrupted=function(e){e.isP2P?this.isP2PConnectionInterrupted=!0:this.isJvbConnectionInterrupted=!0,e.isP2P===this.isP2PActive()&&this.eventEmitter.emit(d.CONNECTION_INTERRUPTED)},ee.prototype._onIceConnectionFailed=function(e){e.isP2P?(N.a.analytics.addPermanentProperties({p2pFailed:!0}),this.p2pJingleSession&&N.a.sendAnalyticsAndLog(Object(z.H)(z.j,{initiator:this.p2pJingleSession.isInitiator})),this._stopP2PSession("connectivity-error","ICE FAILED")):e&&this.jvbJingleSession===e&&(this.xmpp.isPingSupported()?(this._delayedIceFailed=new C.a(this),this._delayedIceFailed.start(e)):(Z.info("PING not supported - sending ICE failed immediately"),e.sendIceFailedNotification()))},ee.prototype._onIceConnectionRestored=function(e){e.isP2P?this.isP2PConnectionInterrupted=!1:(this.isJvbConnectionInterrupted=!1,this._delayedIceFailed&&this._delayedIceFailed.cancel()),e.isP2P===this.isP2PActive()&&this.eventEmitter.emit(d.CONNECTION_RESTORED)},ee.prototype._acceptP2PIncomingCall=function(e,t){this.isP2PConnectionInterrupted=!1,this.p2pJingleSession=e,this.p2pJingleSession.initialize(this.room,this.rtc,this.options.config),Z.info("Starting CallStats for P2P connection...");var n=r.Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);if(this.options.config.enableStatsID){var i=this.participants[n];i&&(n=i.getStatsID()||n)}this.statistics.startCallStats(this.p2pJingleSession.peerconnection,n);var o=this.getLocalTracks();this.p2pJingleSession.acceptOffer(t,function(){Z.debug('Got RESULT for P2P "session-accept"')},function(e){Z.error("Failed to accept incoming P2P Jingle session",e)},o)},ee.prototype._addRemoteJVBTracks=function(){this._addRemoteTracks("JVB",this.jvbJingleSession.peerconnection.getRemoteTracks())},ee.prototype._addRemoteP2PTracks=function(){this._addRemoteTracks("P2P",this.p2pJingleSession.peerconnection.getRemoteTracks())},ee.prototype._addRemoteTracks=function(e,t){var n=!0,r=!1,i=void 0;try{for(var o,a=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;Z.info("Adding remote ".concat(e," track: ").concat(s)),this.rtc.eventEmitter.emit(q.REMOTE_TRACK_ADDED,s)}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}},ee.prototype._onIceConnectionEstablished=function(e){null!==this.p2pJingleSession&&(this.p2pEstablishmentDuration=this.p2pJingleSession.establishmentDuration),null!==this.jvbJingleSession&&(this.jvbEstablishmentDuration=this.jvbJingleSession.establishmentDuration);var t=!1,n=this.options.config.forceJVB121Ratio;if(e.isP2P?this.p2pJingleSession!==e?(Z.error("CONNECTION_ESTABLISHED - wrong P2P session instance ?!"),t=!0):!e.isInitiator&&"number"==typeof n&&Math.random()0&&void 0!==arguments[0]?arguments[0]:{},t=!c()(e,this.properties);this.properties=e,t&&(this.eventEmitter.emit(d.PROPERTIES_CHANGED,this.properties),["bridge-count","created-ms","octo-enabled"].forEach(function(t){void 0!==e[t]&&N.a.analytics.addPermanentProperties(Y({},t.replace("-","_"),e[t]))}))},ee.prototype.getProperty=function(e){return this.properties[e]},ee.prototype._maybeClearDeferredStartP2P=function(){this.deferredStartP2PTask&&(Z.info("Cleared deferred start P2P task"),clearTimeout(this.deferredStartP2PTask),this.deferredStartP2PTask=null)},ee.prototype._removeRemoteJVBTracks=function(){this._removeRemoteTracks("JVB",this.jvbJingleSession.peerconnection.getRemoteTracks())},ee.prototype._removeRemoteP2PTracks=function(){this._removeRemoteTracks("P2P",this.p2pJingleSession.peerconnection.getRemoteTracks())},ee.prototype._removeRemoteTracks=function(e,t){var n=!0,r=!1,i=void 0;try{for(var o,a=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;Z.info("Removing remote ".concat(e," track: ").concat(s)),this.rtc.eventEmitter.emit(q.REMOTE_TRACK_REMOVED,s)}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}},ee.prototype._resumeMediaTransferForJvbConnection=function(){Z.info("Resuming media transfer over the JVB connection..."),this.jvbJingleSession.setMediaTransferActive(!0,!0).then(function(){Z.info("Resumed media transfer over the JVB connection!")},function(e){Z.error("Failed to resume media transfer over the JVB connection:",e)})},ee.prototype._setP2PStatus=function(e){if(this.p2p!==e){if(this.p2p=e,e){Z.info("Peer to peer connection established!"),N.a.analytics.addPermanentProperties({p2pFailed:!1,forceJvb121:!1});var t=0!==this.rtc.getLastN();this.p2pJingleSession.setMediaTransferActive(!0,t).catch(function(e){Z.error("Failed to sync up P2P video transfer status"+"(".concat(t,")"),e)})}else Z.info("Peer to peer connection closed!");this.jvbJingleSession&&this.statistics.sendConnectionResumeOrHoldEvent(this.jvbJingleSession.peerconnection,!e),this.dtmfManager=null,this.eventEmitter.emit(d.P2P_STATUS,this,this.p2p),this.eventEmitter.emit(this.isConnectionInterrupted()?d.CONNECTION_INTERRUPTED:d.CONNECTION_RESTORED)}else Z.debug("Called _setP2PStatus with the same status: ".concat(e))},ee.prototype._startP2PSession=function(e){if(this._maybeClearDeferredStartP2P(),this.p2pJingleSession)Z.error("P2P session already started!");else{this.isP2PConnectionInterrupted=!1,this.p2pJingleSession=this.xmpp.connection.jingle.newP2PJingleSession(this.room.myroomjid,e),Z.info("Created new P2P JingleSession",this.room.myroomjid,e),this.p2pJingleSession.initialize(this.room,this.rtc,this.options.config),Z.info("Starting CallStats for P2P connection...");var t=r.Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);if(this.options.config.enableStatsID){var n=this.participants[t];n&&(t=n.getStatsID()||t)}this.statistics.startCallStats(this.p2pJingleSession.peerconnection,t);var i=this.getLocalTracks();this.p2pJingleSession.invite(i)}},ee.prototype._suspendMediaTransferForJvbConnection=function(){Z.info("Suspending media transfer over the JVB connection..."),this.jvbJingleSession.setMediaTransferActive(!1,!1).then(function(){Z.info("Suspended media transfer over the JVB connection !")},function(e){Z.error("Failed to suspend media transfer over the JVB connection:",e)})},ee.prototype._maybeStartOrStopP2P=function(e){if(E.a.supportsP2P()&&this.isP2PEnabled()&&!this.isP2PTestModeEnabled()){var t=this.getParticipants(),n=t.length,r=this._shouldBeInP2PMode();if(!r&&this.deferredStartP2PTask&&this._maybeClearDeferredStartP2P(),!this.p2pJingleSession&&r){var i=n&&t[0],o=this.myUserId(),a=i.getId();if(o>a)return void Z.debug("I'm the bigger peersId - the other peer should start P2P",o,a);if(o===a)return void Z.error("The same IDs ? ",o,a);var s=i.getJid();if(e){if(this.deferredStartP2PTask)return void Z.error("Deferred start P2P task's been set already!");Z.info("Will start P2P with: ".concat(s," after ").concat(this.backToP2PDelay," seconds...")),this.deferredStartP2PTask=setTimeout(this._startP2PSession.bind(this,s),1e3*this.backToP2PDelay)}else Z.info("Will start P2P with: ".concat(s)),this._startP2PSession(s)}else this.p2pJingleSession&&!r&&(Z.info("Will stop P2P with: ".concat(this.p2pJingleSession.remoteJid)),this.p2pJingleSession.isInitiator&&n>1&&N.a.sendAnalyticsAndLog(Object(z.H)(z.k)),this._stopP2PSession())}else Z.info("Auto P2P disabled")},ee.prototype._shouldBeInP2PMode=function(){var e=this.getParticipants(),t=e.length,n=void 0!==e.find(function(e){return"poltergeist"===e._botType}),r=1===t&&!n;return Z.debug("P2P? peerCount: ".concat(t,", hasBotPeer: ").concat(n," => ").concat(r)),r},ee.prototype._stopP2PSession=function(e,t){if(this.p2pJingleSession){var n=this.isP2PActive();n&&(this.jvbJingleSession&&this._resumeMediaTransferForJvbConnection(),this._removeRemoteP2PTracks()),Z.info("Stopping remote stats for P2P connection"),this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection),Z.info("Stopping CallStats for P2P connection"),this.statistics.stopCallStats(this.p2pJingleSession.peerconnection),this.p2pJingleSession.terminate(function(){Z.info("P2P session terminate RESULT")},function(t){e&&Z.error("An error occurred while trying to terminate P2P Jingle session",t)},{reason:e||"success",reasonDescription:t||"Turing off P2P session",sendSessionTerminate:this.room&&this.getParticipantById(r.Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid))}),this.p2pJingleSession=null,this._setP2PStatus(!1),n&&(this.jvbJingleSession?this._addRemoteJVBTracks():Z.info("Not adding remote JVB tracks - no session yet"))}else Z.error("No P2P session to be stopped!")},ee.prototype.isP2PActive=function(){return this.p2p},ee.prototype.getP2PConnectionState=function(){return this.isP2PActive()?this.p2pJingleSession.peerconnection.getConnectionState():null},ee.prototype.startP2PSession=function(){var e=this.getParticipants();if(1!==e.length)throw new Error("There must be exactly 1 participant to start the P2P session !");var t=e[0].getJid();this._startP2PSession(t)},ee.prototype.stopP2PSession=function(){this._stopP2PSession()},ee.prototype.getSpeakerStats=function(){return this.speakerStatsCollector.getStats()},ee.prototype.setReceiverVideoConstraint=function(e){this.rtc.setReceiverVideoConstraint(e)},ee.prototype.createVideoSIPGWSession=function(e,t){return this.room?this.videoSIPGWHandler.createVideoSIPGWSession(e,t):new Error(G.ERROR_NO_CONNECTION)}}).call(this,"JitsiConference.js")},function(e,t,n){(function(e,n){function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var i="__lodash_hash_undefined__",o=1,a=2,s=9007199254740991,c="[object Arguments]",u="[object Array]",l="[object AsyncFunction]",d="[object Boolean]",p="[object Date]",f="[object Error]",h="[object Function]",m="[object GeneratorFunction]",v="[object Map]",y="[object Number]",g="[object Null]",S="[object Object]",_="[object Proxy]",b="[object RegExp]",E="[object Set]",T="[object String]",C="[object Symbol]",R="[object Undefined]",A="[object ArrayBuffer]",w="[object DataView]",k=/^\[object .+?Constructor\]$/,O=/^(?:0|[1-9]\d*)$/,I={};I["[object Float32Array]"]=I["[object Float64Array]"]=I["[object Int8Array]"]=I["[object Int16Array]"]=I["[object Int32Array]"]=I["[object Uint8Array]"]=I["[object Uint8ClampedArray]"]=I["[object Uint16Array]"]=I["[object Uint32Array]"]=!0,I[c]=I[u]=I[A]=I[d]=I[w]=I[p]=I[f]=I[h]=I[v]=I[y]=I[S]=I[b]=I[E]=I[T]=I["[object WeakMap]"]=!1;var P="object"==(void 0===e?"undefined":r(e))&&e&&e.Object===Object&&e,D="object"==("undefined"==typeof self?"undefined":r(self))&&self&&self.Object===Object&&self,N=P||D||Function("return this")(),L="object"==r(t)&&t&&!t.nodeType&&t,M=L&&"object"==r(n)&&n&&!n.nodeType&&n,x=M&&M.exports===L,j=x&&P.process,F=(function(){try{return j&&j.binding&&j.binding("util")}catch(e){}})(),U=F&&F.isTypedArray;function H(e,t){for(var n=-1,r=null==e?0:e.length;++nu))return!1;var d=s.get(e);if(d&&s.get(t))return d==t;var p=-1,f=!0,h=n&a?new Ae:void 0;for(s.set(e,t),s.set(t,e);++p-1},Ce.prototype.set=function(e,t){var n=this.__data__,r=Oe(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Re.prototype.clear=function(){this.size=0,this.__data__={hash:new Te,map:new(de||Ce),string:new Te}},Re.prototype.delete=function(e){var t=je(this,e).delete(e);return this.size-=t?1:0,t},Re.prototype.get=function(e){return je(this,e).get(e)},Re.prototype.has=function(e){return je(this,e).has(e)},Re.prototype.set=function(e,t){var n=je(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},Ae.prototype.add=Ae.prototype.push=function(e){return this.__data__.set(e,i),this},Ae.prototype.has=function(e){return this.__data__.has(e)},we.prototype.clear=function(){this.__data__=new Ce,this.size=0},we.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},we.prototype.get=function(e){return this.__data__.get(e)},we.prototype.has=function(e){return this.__data__.has(e)},we.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Ce){var r=n.__data__;if(!de||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Re(r)}return n.set(e,t),this.size=n.size,this};var Ue=se?function(e){return null==e?[]:(e=Object(e),(function(e,t){for(var n=-1,r=null==e?0:e.length,i=0,o=[];++n-1&&e%1==0&&e-1&&e%1==0&&e<=s}function $e(e){var t=r(e);return null!=e&&("object"==t||"function"==t)}function Xe(e){return null!=e&&"object"==r(e)}var Qe=U?(function(e){return function(t){return e(t)}})(U):function(e){return Xe(e)&&ze(e.length)&&!!I[Ie(e)]};function Ye(e){return null!=(t=e)&&ze(t.length)&&!We(t)?ke(e):Le(e);var t}n.exports=function(e,t){return De(e,t)}}).call(this,n(43),n(61)(e))},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return b});var r=n(1),i=n(7),o=n(56),a=n.n(o),s=n(70),c=n.n(s),u=n(4),l=n(17),d=n(2),p=n(9),f=n(8),h=n.n(f),m=n(14),v=n.n(m),y=n(5),g=n(0),S=n.n(g),_=Object(u.getLogger)(e);function b(e){this.conference=e,this.xmppListeners={},e.on(d.TRACK_MUTE_CHANGED,function(t){if(t.isLocal()&&e.statistics){var n=t.isP2P?e.p2pJingleSession:e.jvbJingleSession,r=n&&n.peerconnection||null;e.statistics.sendMuteEvent(r,t.isMuted(),t.getType())}})}b.prototype.setupChatRoomListeners=function(){var e=this,t=this.conference,n=t.room;this.chatRoomForwarder=new c.a(n,this.conference.eventEmitter),n.addListener(S.a.ICE_RESTARTING,function(e){e.isP2P||t.rtc.closeBridgeChannel()}),n.addListener(S.a.ICE_RESTART_SUCCESS,function(e,n){!e.isP2P&&t._setBridgeChannel(n,e.peerconnection)}),n.addListener(S.a.AUDIO_MUTED_BY_FOCUS,function(e){y.a.sendAnalytics(Object(i.J)()),t.mutedByFocusActor=e,t.rtc.setAudioMute(!0).then(function(){t.isMutedByFocus=!0,t.mutedByFocusActor=null}).catch(function(e){t.mutedByFocusActor=null,_.warn("Error while audio muting due to focus request",e)})}),this.chatRoomForwarder.forward(S.a.SUBJECT_CHANGED,d.SUBJECT_CHANGED),this.chatRoomForwarder.forward(S.a.MUC_JOINED,d.CONFERENCE_JOINED),n.addListener(S.a.MUC_JOINED,function(){e.conference.isJvbConnectionInterrupted=!1,Object.keys(n.connectionTimes).forEach(function(e){var t=Object(i.B)("conference_".concat(e),{value:n.connectionTimes[e]});y.a.sendAnalytics(t)}),Object.keys(n.xmpp.connectionTimes).forEach(function(e){var t=Object(i.B)("xmpp_".concat(e),{value:n.xmpp.connectionTimes[e]});y.a.sendAnalytics(t)})}),n.addListener(S.a.RENEGOTIATION_FAILED,function(e,n){n.isP2P||t.eventEmitter.emit(d.CONFERENCE_FAILED,l.OFFER_ANSWER_FAILED,e)}),this.chatRoomForwarder.forward(S.a.ROOM_JOIN_ERROR,d.CONFERENCE_FAILED,l.CONNECTION_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_CONNECT_ERROR,d.CONFERENCE_FAILED,l.CONNECTION_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_CONNECT_NOT_ALLOWED_ERROR,d.CONFERENCE_FAILED,l.NOT_ALLOWED_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_MAX_USERS_ERROR,d.CONFERENCE_FAILED,l.CONFERENCE_MAX_USERS),this.chatRoomForwarder.forward(S.a.PASSWORD_REQUIRED,d.CONFERENCE_FAILED,l.PASSWORD_REQUIRED),this.chatRoomForwarder.forward(S.a.AUTHENTICATION_REQUIRED,d.CONFERENCE_FAILED,l.AUTHENTICATION_REQUIRED),this.chatRoomForwarder.forward(S.a.BRIDGE_DOWN,d.CONFERENCE_FAILED,l.VIDEOBRIDGE_NOT_AVAILABLE),n.addListener(S.a.BRIDGE_DOWN,function(){return y.a.sendAnalytics(Object(i.z)())}),this.chatRoomForwarder.forward(S.a.RESERVATION_ERROR,d.CONFERENCE_FAILED,l.RESERVATION_ERROR),this.chatRoomForwarder.forward(S.a.GRACEFUL_SHUTDOWN,d.CONFERENCE_FAILED,l.GRACEFUL_SHUTDOWN),n.addListener(S.a.CONNECTION_ICE_FAILED,function(e){t._onIceConnectionFailed(e)}),this.chatRoomForwarder.forward(S.a.MUC_DESTROYED,d.CONFERENCE_FAILED,l.CONFERENCE_DESTROYED),this.chatRoomForwarder.forward(S.a.CHAT_ERROR_RECEIVED,d.CONFERENCE_ERROR,l.CHAT_ERROR),this.chatRoomForwarder.forward(S.a.FOCUS_DISCONNECTED,d.CONFERENCE_FAILED,l.FOCUS_DISCONNECTED),n.addListener(S.a.FOCUS_LEFT,function(){y.a.sendAnalytics(Object(i.D)()),t.eventEmitter.emit(d.CONFERENCE_FAILED,l.FOCUS_LEFT)}),n.addListener(S.a.SESSION_ACCEPT_TIMEOUT,function(e){y.a.sendAnalyticsAndLog(Object(i.F)(i.b,{p2p:e.isP2P}))}),this.chatRoomForwarder.forward(S.a.RECORDER_STATE_CHANGED,d.RECORDER_STATE_CHANGED),this.chatRoomForwarder.forward(S.a.TRANSCRIPTION_STATUS_CHANGED,d.TRANSCRIPTION_STATUS_CHANGED),this.chatRoomForwarder.forward(S.a.VIDEO_SIP_GW_AVAILABILITY_CHANGED,d.VIDEO_SIP_GW_AVAILABILITY_CHANGED),this.chatRoomForwarder.forward(S.a.VIDEO_SIP_GW_SESSION_STATE_CHANGED,d.VIDEO_SIP_GW_SESSION_STATE_CHANGED),this.chatRoomForwarder.forward(S.a.PHONE_NUMBER_CHANGED,d.PHONE_NUMBER_CHANGED),n.setParticipantPropertyListener(function(e,n){var r=t.getParticipantById(n);r&&r.setProperty(e.tagName.substring("jitsi_participant_".length),e.value)}),n.addListener(S.a.KICKED,t.onMemberKicked.bind(t)),n.addListener(S.a.SUSPEND_DETECTED,t.onSuspendDetected.bind(t)),this.chatRoomForwarder.forward(S.a.MUC_LOCK_CHANGED,d.LOCK_STATE_CHANGED),n.addListener(S.a.MUC_MEMBER_JOINED,t.onMemberJoined.bind(t)),n.addListener(S.a.MUC_MEMBER_BOT_TYPE_CHANGED,t._onMemberBotTypeChanged.bind(t)),n.addListener(S.a.MUC_MEMBER_LEFT,t.onMemberLeft.bind(t)),this.chatRoomForwarder.forward(S.a.MUC_LEFT,d.CONFERENCE_LEFT),n.addListener(S.a.DISPLAY_NAME_CHANGED,t.onDisplayNameChanged.bind(t)),n.addListener(S.a.LOCAL_ROLE_CHANGED,function(e){t.onLocalRoleChanged(e),t.statistics&&t.isModerator()&&t.on(d.RECORDER_STATE_CHANGED,function(e){var t={error:e.getError(),id:"recorder_status",status:e.getStatus()};y.a.sendLog(JSON.stringify(t))})}),n.addListener(S.a.MUC_ROLE_CHANGED,t.onUserRoleChanged.bind(t)),n.addListener(a.a.IDENTITY_UPDATED,function(e,n){t.authEnabled=e,t.authIdentity=n,t.eventEmitter.emit(d.AUTH_STATUS_CHANGED,e,n)}),n.addListener(S.a.MESSAGE_RECEIVED,function(e,n,i,o,a){var s=r.Strophe.getResourceFromJid(e);t.eventEmitter.emit(d.MESSAGE_RECEIVED,s,i,a,n)}),n.addListener(S.a.PRIVATE_MESSAGE_RECEIVED,function(e,n,i,o,a){var s=r.Strophe.getResourceFromJid(e);t.eventEmitter.emit(d.PRIVATE_MESSAGE_RECEIVED,s,i,a)}),n.addListener(S.a.PRESENCE_STATUS,function(e,n){var i=r.Strophe.getResourceFromJid(e),o=t.getParticipantById(i);o&&o._status!==n&&(o._status=n,t.eventEmitter.emit(d.USER_STATUS_CHANGED,i,n))}),n.addListener(S.a.JSON_MESSAGE_RECEIVED,function(e,n){var i=r.Strophe.getResourceFromJid(e),o=t.getParticipantById(i);o?t.eventEmitter.emit(d.ENDPOINT_MESSAGE_RECEIVED,o,n):_.warn("Ignored XMPPEvents.JSON_MESSAGE_RECEIVED for not existing "+"participant: ".concat(e),n)}),n.addPresenceListener("startmuted",function(e,n){var r=!1;if(t.myUserId()===n&&t.isModerator())r=!0;else{var i=t.getParticipantById(n);i&&i.isModerator()&&(r=!0)}if(r){var o="true"===e.attributes.audio,a="true"===e.attributes.video,s=!1;o!==t.startMutedPolicy.audio&&(t.startMutedPolicy.audio=o,s=!0),a!==t.startMutedPolicy.video&&(t.startMutedPolicy.video=a,s=!0),s&&t.eventEmitter.emit(d.START_MUTED_POLICY_CHANGED,t.startMutedPolicy)}}),t.statistics&&(n.addListener(S.a.CONNECTION_ICE_FAILED,function(e){t.statistics.sendIceConnectionFailedEvent(e.peerconnection)}),n.addListener(S.a.ADD_ICE_CANDIDATE_FAILED,function(e,n){t.statistics.sendAddIceCandidateFailed(e,n)}))},b.prototype.setupRTCListeners=function(){var e=this.conference,t=e.rtc;t.addListener(h.a.REMOTE_TRACK_ADDED,e.onRemoteTrackAdded.bind(e)),t.addListener(h.a.REMOTE_TRACK_REMOVED,e.onRemoteTrackRemoved.bind(e)),t.addListener(h.a.DOMINANT_SPEAKER_CHANGED,function(t){e.lastDominantSpeaker!==t&&e.room&&(e.lastDominantSpeaker=t,e.eventEmitter.emit(d.DOMINANT_SPEAKER_CHANGED,t),e.statistics&&e.myUserId()===t&&e.statistics.sendDominantSpeakerEvent(e.room.roomjid))}),t.addListener(h.a.DATA_CHANNEL_OPEN,function(){var t=window.performance.now(),n="data.channel.opened";_.log("(TIME) ".concat(n),t),e.room.connectionTimes[n]=t,y.a.sendAnalytics(Object(i.B)(n,{value:t})),e.eventEmitter.emit(d.DATA_CHANNEL_OPENED)}),t.addListener(h.a.ENDPOINT_MESSAGE_RECEIVED,function(t,n){var r=e.getParticipantById(t);r?e.eventEmitter.emit(d.ENDPOINT_MESSAGE_RECEIVED,r,n):_.warn("Ignored ENDPOINT_MESSAGE_RECEIVED for not existing "+"participant: ".concat(t),n)}),t.addListener(h.a.LOCAL_UFRAG_CHANGED,function(e,t){e.isP2P||y.a.sendLog(JSON.stringify({id:"local_ufrag",value:t}))}),t.addListener(h.a.REMOTE_UFRAG_CHANGED,function(e,t){e.isP2P||y.a.sendLog(JSON.stringify({id:"remote_ufrag",value:t}))}),t.addListener(h.a.CREATE_ANSWER_FAILED,function(t,n){e.statistics.sendCreateAnswerFailed(t,n),n.isP2P||e.eventEmitter.emit(d.CONFERENCE_FAILED,l.OFFER_ANSWER_FAILED,t)}),t.addListener(h.a.CREATE_OFFER_FAILED,function(t,n){e.statistics.sendCreateOfferFailed(t,n),n.isP2P||e.eventEmitter.emit(d.CONFERENCE_FAILED,l.OFFER_ANSWER_FAILED,t)}),t.addListener(h.a.SET_LOCAL_DESCRIPTION_FAILED,function(t,n){e.statistics.sendSetLocalDescFailed(t,n),n.isP2P||e.eventEmitter.emit(d.CONFERENCE_FAILED,l.OFFER_ANSWER_FAILED,t)}),t.addListener(h.a.SET_REMOTE_DESCRIPTION_FAILED,function(t,n){e.statistics.sendSetRemoteDescFailed(t,n),n.isP2P||e.eventEmitter.emit(d.CONFERENCE_FAILED,l.OFFER_ANSWER_FAILED,t)}),t.addListener(h.a.LOCAL_TRACK_SSRC_UPDATED,function(t,n){t.isVideoTrack()&&t.videoType===v.a.DESKTOP&&e.statistics.sendScreenSharingEvent(!0,n)})},b.prototype.removeXMPPListeners=function(){var e=this,t=this.conference;t.xmpp.caps.removeListener(S.a.PARTCIPANT_FEATURES_CHANGED,this.xmppListeners[S.a.PARTCIPANT_FEATURES_CHANGED]),delete this.xmppListeners[S.a.PARTCIPANT_FEATURES_CHANGED],Object.keys(this.xmppListeners).forEach(function(n){t.xmpp.removeListener(n,e.xmppListeners[n])}),this.xmppListeners={}},b.prototype.setupXMPPListeners=function(){var e=this.conference,t=function(t){var n=e.getParticipantById(r.Strophe.getResourceFromJid(t));n&&e.eventEmitter.emit(d.PARTCIPANT_FEATURES_CHANGED,n)};e.xmpp.caps.addListener(S.a.PARTCIPANT_FEATURES_CHANGED,t),this.xmppListeners[S.a.PARTCIPANT_FEATURES_CHANGED]=t,this._addConferenceXMPPListener(S.a.CALL_INCOMING,e.onIncomingCall.bind(e)),this._addConferenceXMPPListener(S.a.CALL_ACCEPTED,e.onCallAccepted.bind(e)),this._addConferenceXMPPListener(S.a.TRANSPORT_INFO,e.onTransportInfo.bind(e)),this._addConferenceXMPPListener(S.a.CALL_ENDED,e.onCallEnded.bind(e)),this._addConferenceXMPPListener(S.a.START_MUTED_FROM_FOCUS,function(t,n){e.options.config.ignoreStartMuted||(e.startAudioMuted=t,e.startVideoMuted=n,e.getLocalTracks().forEach(function(t){switch(t.getType()){case p.a:e.startAudioMuted&&t.mute();break;case p.b:e.startVideoMuted&&t.mute()}}),e.eventEmitter.emit(d.STARTED_MUTED))})},b.prototype._addConferenceXMPPListener=function(e,t){this.xmppListeners[e]=t,this.conference.xmpp.addListener(e,t)},b.prototype.setupStatisticsListeners=function(){var e=this.conference;e.statistics&&(e.statistics.addAudioLevelListener(function(t,n,r,i){e.rtc.setAudioLevel(t,n,r,i)}),e.statistics.addBeforeDisposedListener(function(){e.eventEmitter.emit(d.BEFORE_STATISTICS_DISPOSED)}),e.options.config.startSilent||e.statistics.addByteSentStatsListener(function(t,n){e.getLocalTracks(p.a).forEach(function(e){var r=t.getLocalSSRC(e);r&&n.hasOwnProperty(r)&&e._onByteSentStatsReceived(t,n[r])})}))}}).call(this,"JitsiConferenceEventManager.js")},function(e,t){function n(e,t){if(!e||!t||"function"!=typeof e.addListener||"function"!=typeof t.emit)throw new Error("Invalid arguments passed to EventEmitterForwarder");this.src=e,this.dest=t}n.prototype.forward=function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&this.analyticsHandlers.forEach(function(e){"function"==typeof e.dispose&&e.dispose()}),this.setAnalyticsHandlers([]),this.disposed=!0}},{key:"setAnalyticsHandlers",value:function(e){var t=this;if(!this.disposed){this.analyticsHandlers=new Set(e),this._setUserProperties();var n=this.cache;this.cache=null,n&&n.forEach(function(e){return t._sendEvent(e)})}}},{key:"_setUserProperties",value:function(){var e=this;this.analyticsHandlers.forEach(function(t){try{t.setUserProperties(e.permanentProperties)}catch(e){l.warn("Error in setUserProperties method of one of the "+"analytics handlers: ".concat(e))}})}},{key:"addPermanentProperties",value:function(e){this.permanentProperties=(function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};if(!this.disposed){var n=null;"string"==typeof e?n={type:r.t,action:e,actionSubject:e,source:e,attributes:t}:"object"===s(e)&&(n=e),this._verifyRequiredFields(n)?this._sendEvent(n):l.error("Dropping a mis-formatted event: ".concat(JSON.stringify(n)))}}},{key:"_verifyRequiredFields",value:function(e){if(!e)return!1;e.type||(e.type=r.t);var t=e.type;return t!==r.t&&t!==r.u&&t!==r.w&&t!==r.v?(l.error("Unknown event type: ".concat(t)),!1):t===r.u?Boolean(e.name):(e.action=e.action||e.name||e.actionSubject,e.actionSubject=e.actionSubject||e.name||e.action,e.source=e.source||e.name||e.action||e.actionSubject,e.action&&e.actionSubject&&e.source?!!(t!==r.v||(e.objectType=e.objectType||"generic-object-type",e.containerType=e.containerType||"conference","conference"!==e.containerType||e.containerId||(e.containerId=this.conferenceName),e.objectType&&e.objectId&&e.containerType&&e.containerId))||(l.error("Required field missing (containerId, containerType, objectId or objectType)"),!1):(l.error("Required field missing (action, actionSubject or source)"),!1))}},{key:"_maybeCacheEvent",value:function(e){return!!this.cache&&(this.cache.push(e),this.cache.length>100&&this.cache.splice(0,1),!0)}},{key:"_sendEvent",value:function(e){this._maybeCacheEvent(e)||this.analyticsHandlers.forEach(function(t){try{t.sendEvent(e)}catch(e){l.warn("Error sending analytics event: ".concat(e))}})}}])&&u(t.prototype,n),e})();t.a=new d}).call(this,"modules/statistics/AnalyticsAdapter.js")},function(e,t,n){"use strict";(function(e,r){n.d(t,"a",function(){return p});var i=n(4),o=n(19);function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function s(e,t){for(var n=0;n-1}},{key:"usesNewGumFlow",value:function(){return this.isChrome()?!this.isVersionLessThan(61):!(!this.isFirefox()&&!this.isSafariWithWebrtc())||!!this.isChromiumBased()&&this._getChromiumBasedVersion()>=61}},{key:"usesAdapter",value:function(){return this.usesNewGumFlow()||this.isEdge()}},{key:"supportsGetDisplayMedia",value:function(){return void 0!==navigator.getDisplayMedia||void 0!==navigator.mediaDevices&&void 0!==navigator.mediaDevices.getDisplayMedia}},{key:"supportsSdpSemantics",value:function(){return this.isChromiumBased()&&this._getChromiumBasedVersion()>=65}},{key:"_getChromiumBasedVersion",value:function(){if(this.isChromiumBased()){if(this.isNWJS())return Number.parseInt(r.versions.chromium,10);var e=navigator.userAgent;if(e.match(/Chrome/))return Number.parseInt(e.match(/Chrome\/([\d.]+)/)[1],10)}return-1}}])&&s(n.prototype,i),t})()}).call(this,"modules/browser/BrowserCapabilities.js",n(48))},function(module,exports,__webpack_require__){(function(process,global){var __WEBPACK_AMD_DEFINE_RESULT__;!(function(){"use strict";var ERROR="input is invalid type",WINDOW="object"==typeof window,root=WINDOW?window:{};root.JS_MD5_NO_WINDOW&&(WINDOW=!1);var WEB_WORKER=!WINDOW&&"object"==typeof self,NODE_JS=!root.JS_MD5_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;NODE_JS?root=global:WEB_WORKER&&(root=self);var COMMON_JS=!root.JS_MD5_NO_COMMON_JS&&"object"==typeof module&&module.exports,AMD=__webpack_require__(123),ARRAY_BUFFER=!root.JS_MD5_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,HEX_CHARS="0123456789abcdef".split(""),EXTRA=[128,32768,8388608,-2147483648],SHIFT=[0,8,16,24],OUTPUT_TYPES=["hex","array","digest","buffer","arrayBuffer","base64"],BASE64_ENCODE_CHAR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),blocks=[],buffer8;if(ARRAY_BUFFER){var buffer=new ArrayBuffer(68);buffer8=new Uint8Array(buffer),blocks=new Uint32Array(buffer)}!root.JS_MD5_NO_NODE_JS&&Array.isArray||(Array.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)}),!ARRAY_BUFFER||!root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(e){return"object"==typeof e&&e.buffer&&e.buffer.constructor===ArrayBuffer});var createOutputMethod=function(e){return function(t){return new Md5(!0).update(t)[e]()}},createMethod=function(){var e=createOutputMethod("hex");NODE_JS&&(e=nodeWrap(e)),e.create=function(){return new Md5},e.update=function(t){return e.create().update(t)};for(var t=0;t>2]|=e[o]<>6,c[i++]=128|63&r):r<55296||r>=57344?(c[i++]=224|r>>12,c[i++]=128|r>>6&63,c[i++]=128|63&r):(r=65536+((1023&r)<<10|1023&e.charCodeAt(++o)),c[i++]=240|r>>18,c[i++]=128|r>>12&63,c[i++]=128|r>>6&63,c[i++]=128|63&r);else for(i=this.start;o>2]|=r<>2]|=(192|r>>6)<>2]|=(128|63&r)<=57344?(s[i>>2]|=(224|r>>12)<>2]|=(128|r>>6&63)<>2]|=(128|63&r)<>2]|=(240|r>>18)<>2]|=(128|r>>12&63)<>2]|=(128|r>>6&63)<>2]|=(128|63&r)<=64?(this.start=i-64,this.hash(),this.hashed=!0):this.start=i}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},Md5.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var e=this.blocks,t=this.lastByteIndex;e[t>>2]|=EXTRA[3&t],t>=56&&(this.hashed||this.hash(),e[0]=e[16],e[16]=e[1]=e[2]=e[3]=e[4]=e[5]=e[6]=e[7]=e[8]=e[9]=e[10]=e[11]=e[12]=e[13]=e[14]=e[15]=0),e[14]=this.bytes<<3,e[15]=this.hBytes<<3|this.bytes>>>29,this.hash()}},Md5.prototype.hash=function(){var e,t,n,r,i,o,a=this.blocks;this.first?t=((t=((e=((e=a[0]-680876937)<<7|e>>>25)-271733879<<0)^(n=((n=(-271733879^(r=((r=(-1732584194^2004318071&e)+a[1]-117830708)<<12|r>>>20)+e<<0)&(-271733879^e))+a[2]-1126478375)<<17|n>>>15)+r<<0)&(r^e))+a[3]-1316259209)<<22|t>>>10)+n<<0:(e=this.h0,t=this.h1,n=this.h2,t=((t+=((e=((e+=((r=this.h3)^t&(n^r))+a[0]-680876936)<<7|e>>>25)+t<<0)^(n=((n+=(t^(r=((r+=(n^e&(t^n))+a[1]-389564586)<<12|r>>>20)+e<<0)&(e^t))+a[2]+606105819)<<17|n>>>15)+r<<0)&(r^e))+a[3]-1044525330)<<22|t>>>10)+n<<0),t=((t+=((e=((e+=(r^t&(n^r))+a[4]-176418897)<<7|e>>>25)+t<<0)^(n=((n+=(t^(r=((r+=(n^e&(t^n))+a[5]+1200080426)<<12|r>>>20)+e<<0)&(e^t))+a[6]-1473231341)<<17|n>>>15)+r<<0)&(r^e))+a[7]-45705983)<<22|t>>>10)+n<<0,t=((t+=((e=((e+=(r^t&(n^r))+a[8]+1770035416)<<7|e>>>25)+t<<0)^(n=((n+=(t^(r=((r+=(n^e&(t^n))+a[9]-1958414417)<<12|r>>>20)+e<<0)&(e^t))+a[10]-42063)<<17|n>>>15)+r<<0)&(r^e))+a[11]-1990404162)<<22|t>>>10)+n<<0,t=((t+=((e=((e+=(r^t&(n^r))+a[12]+1804603682)<<7|e>>>25)+t<<0)^(n=((n+=(t^(r=((r+=(n^e&(t^n))+a[13]-40341101)<<12|r>>>20)+e<<0)&(e^t))+a[14]-1502002290)<<17|n>>>15)+r<<0)&(r^e))+a[15]+1236535329)<<22|t>>>10)+n<<0,t=((t+=((r=((r+=(t^n&((e=((e+=(n^r&(t^n))+a[1]-165796510)<<5|e>>>27)+t<<0)^t))+a[6]-1069501632)<<9|r>>>23)+e<<0)^e&((n=((n+=(e^t&(r^e))+a[11]+643717713)<<14|n>>>18)+r<<0)^r))+a[0]-373897302)<<20|t>>>12)+n<<0,t=((t+=((r=((r+=(t^n&((e=((e+=(n^r&(t^n))+a[5]-701558691)<<5|e>>>27)+t<<0)^t))+a[10]+38016083)<<9|r>>>23)+e<<0)^e&((n=((n+=(e^t&(r^e))+a[15]-660478335)<<14|n>>>18)+r<<0)^r))+a[4]-405537848)<<20|t>>>12)+n<<0,t=((t+=((r=((r+=(t^n&((e=((e+=(n^r&(t^n))+a[9]+568446438)<<5|e>>>27)+t<<0)^t))+a[14]-1019803690)<<9|r>>>23)+e<<0)^e&((n=((n+=(e^t&(r^e))+a[3]-187363961)<<14|n>>>18)+r<<0)^r))+a[8]+1163531501)<<20|t>>>12)+n<<0,t=((t+=((r=((r+=(t^n&((e=((e+=(n^r&(t^n))+a[13]-1444681467)<<5|e>>>27)+t<<0)^t))+a[2]-51403784)<<9|r>>>23)+e<<0)^e&((n=((n+=(e^t&(r^e))+a[7]+1735328473)<<14|n>>>18)+r<<0)^r))+a[12]-1926607734)<<20|t>>>12)+n<<0,t=((t+=((o=(r=((r+=((i=t^n)^(e=((e+=(i^r)+a[5]-378558)<<4|e>>>28)+t<<0))+a[8]-2022574463)<<11|r>>>21)+e<<0)^e)^(n=((n+=(o^t)+a[11]+1839030562)<<16|n>>>16)+r<<0))+a[14]-35309556)<<23|t>>>9)+n<<0,t=((t+=((o=(r=((r+=((i=t^n)^(e=((e+=(i^r)+a[1]-1530992060)<<4|e>>>28)+t<<0))+a[4]+1272893353)<<11|r>>>21)+e<<0)^e)^(n=((n+=(o^t)+a[7]-155497632)<<16|n>>>16)+r<<0))+a[10]-1094730640)<<23|t>>>9)+n<<0,t=((t+=((o=(r=((r+=((i=t^n)^(e=((e+=(i^r)+a[13]+681279174)<<4|e>>>28)+t<<0))+a[0]-358537222)<<11|r>>>21)+e<<0)^e)^(n=((n+=(o^t)+a[3]-722521979)<<16|n>>>16)+r<<0))+a[6]+76029189)<<23|t>>>9)+n<<0,t=((t+=((o=(r=((r+=((i=t^n)^(e=((e+=(i^r)+a[9]-640364487)<<4|e>>>28)+t<<0))+a[12]-421815835)<<11|r>>>21)+e<<0)^e)^(n=((n+=(o^t)+a[15]+530742520)<<16|n>>>16)+r<<0))+a[2]-995338651)<<23|t>>>9)+n<<0,t=((t+=((r=((r+=(t^((e=((e+=(n^(t|~r))+a[0]-198630844)<<6|e>>>26)+t<<0)|~n))+a[7]+1126891415)<<10|r>>>22)+e<<0)^((n=((n+=(e^(r|~t))+a[14]-1416354905)<<15|n>>>17)+r<<0)|~e))+a[5]-57434055)<<21|t>>>11)+n<<0,t=((t+=((r=((r+=(t^((e=((e+=(n^(t|~r))+a[12]+1700485571)<<6|e>>>26)+t<<0)|~n))+a[3]-1894986606)<<10|r>>>22)+e<<0)^((n=((n+=(e^(r|~t))+a[10]-1051523)<<15|n>>>17)+r<<0)|~e))+a[1]-2054922799)<<21|t>>>11)+n<<0,t=((t+=((r=((r+=(t^((e=((e+=(n^(t|~r))+a[8]+1873313359)<<6|e>>>26)+t<<0)|~n))+a[15]-30611744)<<10|r>>>22)+e<<0)^((n=((n+=(e^(r|~t))+a[6]-1560198380)<<15|n>>>17)+r<<0)|~e))+a[13]+1309151649)<<21|t>>>11)+n<<0,t=((t+=((r=((r+=(t^((e=((e+=(n^(t|~r))+a[4]-145523070)<<6|e>>>26)+t<<0)|~n))+a[11]-1120210379)<<10|r>>>22)+e<<0)^((n=((n+=(e^(r|~t))+a[2]+718787259)<<15|n>>>17)+r<<0)|~e))+a[9]-343485551)<<21|t>>>11)+n<<0,this.first?(this.h0=e+1732584193<<0,this.h1=t-271733879<<0,this.h2=n-1732584194<<0,this.h3=r+271733878<<0,this.first=!1):(this.h0=this.h0+e<<0,this.h1=this.h1+t<<0,this.h2=this.h2+n<<0,this.h3=this.h3+r<<0)},Md5.prototype.hex=function(){this.finalize();var e=this.h0,t=this.h1,n=this.h2,r=this.h3;return HEX_CHARS[e>>4&15]+HEX_CHARS[15&e]+HEX_CHARS[e>>12&15]+HEX_CHARS[e>>8&15]+HEX_CHARS[e>>20&15]+HEX_CHARS[e>>16&15]+HEX_CHARS[e>>28&15]+HEX_CHARS[e>>24&15]+HEX_CHARS[t>>4&15]+HEX_CHARS[15&t]+HEX_CHARS[t>>12&15]+HEX_CHARS[t>>8&15]+HEX_CHARS[t>>20&15]+HEX_CHARS[t>>16&15]+HEX_CHARS[t>>28&15]+HEX_CHARS[t>>24&15]+HEX_CHARS[n>>4&15]+HEX_CHARS[15&n]+HEX_CHARS[n>>12&15]+HEX_CHARS[n>>8&15]+HEX_CHARS[n>>20&15]+HEX_CHARS[n>>16&15]+HEX_CHARS[n>>28&15]+HEX_CHARS[n>>24&15]+HEX_CHARS[r>>4&15]+HEX_CHARS[15&r]+HEX_CHARS[r>>12&15]+HEX_CHARS[r>>8&15]+HEX_CHARS[r>>20&15]+HEX_CHARS[r>>16&15]+HEX_CHARS[r>>28&15]+HEX_CHARS[r>>24&15]},Md5.prototype.toString=Md5.prototype.hex,Md5.prototype.digest=function(){this.finalize();var e=this.h0,t=this.h1,n=this.h2,r=this.h3;return[255&e,e>>8&255,e>>16&255,e>>24&255,255&t,t>>8&255,t>>16&255,t>>24&255,255&n,n>>8&255,n>>16&255,n>>24&255,255&r,r>>8&255,r>>16&255,r>>24&255]},Md5.prototype.array=Md5.prototype.digest,Md5.prototype.arrayBuffer=function(){this.finalize();var e=new ArrayBuffer(16),t=new Uint32Array(e);return t[0]=this.h0,t[1]=this.h1,t[2]=this.h2,t[3]=this.h3,e},Md5.prototype.buffer=Md5.prototype.arrayBuffer,Md5.prototype.base64=function(){for(var e,t,n,r="",i=this.array(),o=0;o<15;)e=i[o++],t=i[o++],n=i[o++],r+=BASE64_ENCODE_CHAR[e>>>2]+BASE64_ENCODE_CHAR[63&(e<<4|t>>>4)]+BASE64_ENCODE_CHAR[63&(t<<2|n>>>6)]+BASE64_ENCODE_CHAR[63&n];return e=i[o],r+(BASE64_ENCODE_CHAR[e>>>2]+BASE64_ENCODE_CHAR[e<<4&63]+"==")};var exports=createMethod();COMMON_JS?module.exports=exports:(root.md5=exports,AMD&&(__WEBPACK_AMD_DEFINE_RESULT__=function(){return exports}.call(exports,__webpack_require__,exports,module),void 0===__WEBPACK_AMD_DEFINE_RESULT__||(module.exports=__WEBPACK_AMD_DEFINE_RESULT__)))})()}).call(this,__webpack_require__(48),__webpack_require__(43))},function(e,t,n){var r=n(33),i=["Aaliyah","Aaron","Abagail","Abbey","Abbie","Abbigail","Abby","Abdiel","Abdul","Abdullah","Abe","Abel","Abelardo","Abigail","Abigale","Abigayle","Abner","Abraham","Ada","Adah","Adalberto","Adaline","Adam","Adan","Addie","Addison","Adela","Adelbert","Adele","Adelia","Adeline","Adell","Adella","Adelle","Aditya","Adolf","Adolfo","Adolph","Adolphus","Adonis","Adrain","Adrian","Adriana","Adrianna","Adriel","Adrien","Adrienne","Afton","Aglae","Agnes","Agustin","Agustina","Ahmad","Ahmed","Aida","Aidan","Aiden","Aileen","Aisha","Aiyana","Akeem","Al","Alaina","Alan","Alana","Alanis","Alanna","Alayna","Alba","Albert","Alberta","Albertha","Alberto","Albin","Albina","Alda","Alden","Alec","Aleen","Alejandra","Alejandrin","Alek","Alena","Alene","Alessandra","Alessandro","Alessia","Aletha","Alex","Alexa","Alexander","Alexandra","Alexandre","Alexandrea","Alexandria","Alexandrine","Alexandro","Alexane","Alexanne","Alexie","Alexis","Alexys","Alexzander","Alf","Alfonso","Alfonzo","Alford","Alfred","Alfreda","Alfredo","Ali","Alia","Alice","Alicia","Alisa","Alisha","Alison","Alivia","Aliya","Aliyah","Aliza","Alize","Allan","Allen","Allene","Allie","Allison","Ally","Alphonso","Alta","Althea","Alva","Alvah","Alvena","Alvera","Alverta","Alvina","Alvis","Alyce","Alycia","Alysa","Alysha","Alyson","Alysson","Amalia","Amanda","Amani","Amara","Amari","Amaya","Amber","Ambrose","Amelia","Amelie","Amely","America","Americo","Amie","Amina","Amir","Amira","Amiya","Amos","Amparo","Amy","Amya","Ana","Anabel","Anabelle","Anahi","Anais","Anastacio","Anastasia","Anderson","Andre","Andreane","Andreanne","Andres","Andrew","Andy","Angel","Angela","Angelica","Angelina","Angeline","Angelita","Angelo","Angie","Angus","Anibal","Anika","Anissa","Anita","Aniya","Aniyah","Anjali","Anna","Annabel","Annabell","Annabelle","Annalise","Annamae","Annamarie","Anne","Annetta","Annette","Annie","Ansel","Ansley","Anthony","Antoinette","Antone","Antonetta","Antonette","Antonia","Antonietta","Antonina","Antonio","Antwan","Antwon","Anya","April","Ara","Araceli","Aracely","Arch","Archibald","Ardella","Arden","Ardith","Arely","Ari","Ariane","Arianna","Aric","Ariel","Arielle","Arjun","Arlene","Arlie","Arlo","Armand","Armando","Armani","Arnaldo","Arne","Arno","Arnold","Arnoldo","Arnulfo","Aron","Art","Arthur","Arturo","Arvel","Arvid","Arvilla","Aryanna","Asa","Asha","Ashlee","Ashleigh","Ashley","Ashly","Ashlynn","Ashton","Ashtyn","Asia","Assunta","Astrid","Athena","Aubree","Aubrey","Audie","Audra","Audreanne","Audrey","August","Augusta","Augustine","Augustus","Aurelia","Aurelie","Aurelio","Aurore","Austen","Austin","Austyn","Autumn","Ava","Avery","Avis","Axel","Ayana","Ayden","Ayla","Aylin","Baby","Bailee","Bailey","Barbara","Barney","Baron","Barrett","Barry","Bart","Bartholome","Barton","Baylee","Beatrice","Beau","Beaulah","Bell","Bella","Belle","Ben","Benedict","Benjamin","Bennett","Bennie","Benny","Benton","Berenice","Bernadette","Bernadine","Bernard","Bernardo","Berneice","Bernhard","Bernice","Bernie","Berniece","Bernita","Berry","Bert","Berta","Bertha","Bertram","Bertrand","Beryl","Bessie","Beth","Bethany","Bethel","Betsy","Bette","Bettie","Betty","Bettye","Beulah","Beverly","Bianka","Bill","Billie","Billy","Birdie","Blair","Blaise","Blake","Blanca","Blanche","Blaze","Bo","Bobbie","Bobby","Bonita","Bonnie","Boris","Boyd","Brad","Braden","Bradford","Bradley","Bradly","Brady","Braeden","Brain","Brandi","Brando","Brandon","Brandt","Brandy","Brandyn","Brannon","Branson","Brant","Braulio","Braxton","Brayan","Breana","Breanna","Breanne","Brenda","Brendan","Brenden","Brendon","Brenna","Brennan","Brennon","Brent","Bret","Brett","Bria","Brian","Briana","Brianne","Brice","Bridget","Bridgette","Bridie","Brielle","Brigitte","Brionna","Brisa","Britney","Brittany","Brock","Broderick","Brody","Brook","Brooke","Brooklyn","Brooks","Brown","Bruce","Bryana","Bryce","Brycen","Bryon","Buck","Bud","Buddy","Buford","Bulah","Burdette","Burley","Burnice","Buster","Cade","Caden","Caesar","Caitlyn","Cale","Caleb","Caleigh","Cali","Calista","Callie","Camden","Cameron","Camila","Camilla","Camille","Camren","Camron","Camryn","Camylle","Candace","Candelario","Candice","Candida","Candido","Cara","Carey","Carissa","Carlee","Carleton","Carley","Carli","Carlie","Carlo","Carlos","Carlotta","Carmel","Carmela","Carmella","Carmelo","Carmen","Carmine","Carol","Carolanne","Carole","Carolina","Caroline","Carolyn","Carolyne","Carrie","Carroll","Carson","Carter","Cary","Casandra","Casey","Casimer","Casimir","Casper","Cassandra","Cassandre","Cassidy","Cassie","Catalina","Caterina","Catharine","Catherine","Cathrine","Cathryn","Cathy","Cayla","Ceasar","Cecelia","Cecil","Cecile","Cecilia","Cedrick","Celestine","Celestino","Celia","Celine","Cesar","Chad","Chadd","Chadrick","Chaim","Chance","Chandler","Chanel","Chanelle","Charity","Charlene","Charles","Charley","Charlie","Charlotte","Chase","Chasity","Chauncey","Chaya","Chaz","Chelsea","Chelsey","Chelsie","Chesley","Chester","Chet","Cheyanne","Cheyenne","Chloe","Chris","Christ","Christa","Christelle","Christian","Christiana","Christina","Christine","Christop","Christophe","Christopher","Christy","Chyna","Ciara","Cicero","Cielo","Cierra","Cindy","Citlalli","Clair","Claire","Clara","Clarabelle","Clare","Clarissa","Clark","Claud","Claude","Claudia","Claudie","Claudine","Clay","Clemens","Clement","Clementina","Clementine","Clemmie","Cleo","Cleora","Cleta","Cletus","Cleve","Cleveland","Clifford","Clifton","Clint","Clinton","Clotilde","Clovis","Cloyd","Clyde","Coby","Cody","Colby","Cole","Coleman","Colin","Colleen","Collin","Colt","Colten","Colton","Columbus","Concepcion","Conner","Connie","Connor","Conor","Conrad","Constance","Constantin","Consuelo","Cooper","Cora","Coralie","Corbin","Cordelia","Cordell","Cordia","Cordie","Corene","Corine","Cornelius","Cornell","Corrine","Cortez","Cortney","Cory","Coty","Courtney","Coy","Craig","Crawford","Creola","Cristal","Cristian","Cristina","Cristobal","Cristopher","Cruz","Crystal","Crystel","Cullen","Curt","Curtis","Cydney","Cynthia","Cyril","Cyrus","Dagmar","Dahlia","Daija","Daisha","Daisy","Dakota","Dale","Dallas","Dallin","Dalton","Damaris","Dameon","Damian","Damien","Damion","Damon","Dan","Dana","Dandre","Dane","D'angelo","Dangelo","Danial","Daniela","Daniella","Danielle","Danika","Dannie","Danny","Dante","Danyka","Daphne","Daphnee","Daphney","Darby","Daren","Darian","Dariana","Darien","Dario","Darion","Darius","Darlene","Daron","Darrel","Darrell","Darren","Darrick","Darrin","Darrion","Darron","Darryl","Darwin","Daryl","Dashawn","Dasia","Dave","David","Davin","Davion","Davon","Davonte","Dawn","Dawson","Dax","Dayana","Dayna","Dayne","Dayton","Dean","Deangelo","Deanna","Deborah","Declan","Dedric","Dedrick","Dee","Deion","Deja","Dejah","Dejon","Dejuan","Delaney","Delbert","Delfina","Delia","Delilah","Dell","Della","Delmer","Delores","Delpha","Delphia","Delphine","Delta","Demarco","Demarcus","Demario","Demetris","Demetrius","Demond","Dena","Denis","Dennis","Deon","Deondre","Deontae","Deonte","Dereck","Derek","Derick","Deron","Derrick","Deshaun","Deshawn","Desiree","Desmond","Dessie","Destany","Destin","Destinee","Destiney","Destini","Destiny","Devan","Devante","Deven","Devin","Devon","Devonte","Devyn","Dewayne","Dewitt","Dexter","Diamond","Diana","Dianna","Diego","Dillan","Dillon","Dimitri","Dina","Dino","Dion","Dixie","Dock","Dolly","Dolores","Domenic","Domenica","Domenick","Domenico","Domingo","Dominic","Dominique","Don","Donald","Donato","Donavon","Donna","Donnell","Donnie","Donny","Dora","Dorcas","Dorian","Doris","Dorothea","Dorothy","Dorris","Dortha","Dorthy","Doug","Douglas","Dovie","Doyle","Drake","Drew","Duane","Dudley","Dulce","Duncan","Durward","Dustin","Dusty","Dwight","Dylan","Earl","Earlene","Earline","Earnest","Earnestine","Easter","Easton","Ebba","Ebony","Ed","Eda","Edd","Eddie","Eden","Edgar","Edgardo","Edison","Edmond","Edmund","Edna","Eduardo","Edward","Edwardo","Edwin","Edwina","Edyth","Edythe","Effie","Efrain","Efren","Eileen","Einar","Eino","Eladio","Elaina","Elbert","Elda","Eldon","Eldora","Eldred","Eldridge","Eleanora","Eleanore","Eleazar","Electa","Elena","Elenor","Elenora","Eleonore","Elfrieda","Eli","Elian","Eliane","Elias","Eliezer","Elijah","Elinor","Elinore","Elisa","Elisabeth","Elise","Eliseo","Elisha","Elissa","Eliza","Elizabeth","Ella","Ellen","Ellie","Elliot","Elliott","Ellis","Ellsworth","Elmer","Elmira","Elmo","Elmore","Elna","Elnora","Elody","Eloisa","Eloise","Elouise","Eloy","Elroy","Elsa","Else","Elsie","Elta","Elton","Elva","Elvera","Elvie","Elvis","Elwin","Elwyn","Elyse","Elyssa","Elza","Emanuel","Emelia","Emelie","Emely","Emerald","Emerson","Emery","Emie","Emil","Emile","Emilia","Emiliano","Emilie","Emilio","Emily","Emma","Emmalee","Emmanuel","Emmanuelle","Emmet","Emmett","Emmie","Emmitt","Emmy","Emory","Ena","Enid","Enoch","Enola","Enos","Enrico","Enrique","Ephraim","Era","Eriberto","Eric","Erica","Erich","Erick","Ericka","Erik","Erika","Erin","Erling","Erna","Ernest","Ernestina","Ernestine","Ernesto","Ernie","Ervin","Erwin","Eryn","Esmeralda","Esperanza","Esta","Esteban","Estefania","Estel","Estell","Estella","Estelle","Estevan","Esther","Estrella","Etha","Ethan","Ethel","Ethelyn","Ethyl","Ettie","Eudora","Eugene","Eugenia","Eula","Eulah","Eulalia","Euna","Eunice","Eusebio","Eva","Evalyn","Evan","Evangeline","Evans","Eve","Eveline","Evelyn","Everardo","Everett","Everette","Evert","Evie","Ewald","Ewell","Ezekiel","Ezequiel","Ezra","Fabian","Fabiola","Fae","Fannie","Fanny","Fatima","Faustino","Fausto","Favian","Fay","Faye","Federico","Felicia","Felicita","Felicity","Felipa","Felipe","Felix","Felton","Fermin","Fern","Fernando","Ferne","Fidel","Filiberto","Filomena","Finn","Fiona","Flavie","Flavio","Fleta","Fletcher","Flo","Florence","Florencio","Florian","Florida","Florine","Flossie","Floy","Floyd","Ford","Forest","Forrest","Foster","Frances","Francesca","Francesco","Francis","Francisca","Francisco","Franco","Frank","Frankie","Franz","Fred","Freda","Freddie","Freddy","Frederic","Frederick","Frederik","Frederique","Fredrick","Fredy","Freeda","Freeman","Freida","Frida","Frieda","Friedrich","Fritz","Furman","Gabe","Gabriel","Gabriella","Gabrielle","Gaetano","Gage","Gail","Gardner","Garett","Garfield","Garland","Garnet","Garnett","Garret","Garrett","Garrick","Garrison","Garry","Garth","Gaston","Gavin","Gay","Gayle","Gaylord","Gene","General","Genesis","Genevieve","Gennaro","Genoveva","Geo","Geoffrey","George","Georgette","Georgiana","Georgianna","Geovanni","Geovanny","Geovany","Gerald","Geraldine","Gerard","Gerardo","Gerda","Gerhard","Germaine","German","Gerry","Gerson","Gertrude","Gia","Gianni","Gideon","Gilbert","Gilberto","Gilda","Giles","Gillian","Gina","Gino","Giovani","Giovanna","Giovanni","Giovanny","Gisselle","Giuseppe","Gladyce","Gladys","Glen","Glenda","Glenna","Glennie","Gloria","Godfrey","Golda","Golden","Gonzalo","Gordon","Grace","Gracie","Graciela","Grady","Graham","Grant","Granville","Grayce","Grayson","Green","Greg","Gregg","Gregoria","Gregorio","Gregory","Greta","Gretchen","Greyson","Griffin","Grover","Guadalupe","Gudrun","Guido","Guillermo","Guiseppe","Gunnar","Gunner","Gus","Gussie","Gust","Gustave","Guy","Gwen","Gwendolyn","Hadley","Hailee","Hailey","Hailie","Hal","Haleigh","Haley","Halie","Halle","Hallie","Hank","Hanna","Hannah","Hans","Hardy","Harley","Harmon","Harmony","Harold","Harrison","Harry","Harvey","Haskell","Hassan","Hassie","Hattie","Haven","Hayden","Haylee","Hayley","Haylie","Hazel","Hazle","Heath","Heather","Heaven","Heber","Hector","Heidi","Helen","Helena","Helene","Helga","Hellen","Helmer","Heloise","Henderson","Henri","Henriette","Henry","Herbert","Herman","Hermann","Hermina","Herminia","Herminio","Hershel","Herta","Hertha","Hester","Hettie","Hilario","Hilbert","Hilda","Hildegard","Hillard","Hillary","Hilma","Hilton","Hipolito","Hiram","Hobart","Holden","Hollie","Hollis","Holly","Hope","Horace","Horacio","Hortense","Hosea","Houston","Howard","Howell","Hoyt","Hubert","Hudson","Hugh","Hulda","Humberto","Hunter","Hyman","Ian","Ibrahim","Icie","Ida","Idell","Idella","Ignacio","Ignatius","Ike","Ila","Ilene","Iliana","Ima","Imani","Imelda","Immanuel","Imogene","Ines","Irma","Irving","Irwin","Isaac","Isabel","Isabell","Isabella","Isabelle","Isac","Isadore","Isai","Isaiah","Isaias","Isidro","Ismael","Isobel","Isom","Israel","Issac","Itzel","Iva","Ivah","Ivory","Ivy","Izabella","Izaiah","Jabari","Jace","Jacey","Jacinthe","Jacinto","Jack","Jackeline","Jackie","Jacklyn","Jackson","Jacky","Jaclyn","Jacquelyn","Jacques","Jacynthe","Jada","Jade","Jaden","Jadon","Jadyn","Jaeden","Jaida","Jaiden","Jailyn","Jaime","Jairo","Jakayla","Jake","Jakob","Jaleel","Jalen","Jalon","Jalyn","Jamaal","Jamal","Jamar","Jamarcus","Jamel","Jameson","Jamey","Jamie","Jamil","Jamir","Jamison","Jammie","Jan","Jana","Janae","Jane","Janelle","Janessa","Janet","Janice","Janick","Janie","Janis","Janiya","Jannie","Jany","Jaquan","Jaquelin","Jaqueline","Jared","Jaren","Jarod","Jaron","Jarred","Jarrell","Jarret","Jarrett","Jarrod","Jarvis","Jasen","Jasmin","Jason","Jasper","Jaunita","Javier","Javon","Javonte","Jay","Jayce","Jaycee","Jayda","Jayde","Jayden","Jaydon","Jaylan","Jaylen","Jaylin","Jaylon","Jayme","Jayne","Jayson","Jazlyn","Jazmin","Jazmyn","Jazmyne","Jean","Jeanette","Jeanie","Jeanne","Jed","Jedediah","Jedidiah","Jeff","Jefferey","Jeffery","Jeffrey","Jeffry","Jena","Jenifer","Jennie","Jennifer","Jennings","Jennyfer","Jensen","Jerad","Jerald","Jeramie","Jeramy","Jerel","Jeremie","Jeremy","Jermain","Jermaine","Jermey","Jerod","Jerome","Jeromy","Jerrell","Jerrod","Jerrold","Jerry","Jess","Jesse","Jessica","Jessie","Jessika","Jessy","Jessyca","Jesus","Jett","Jettie","Jevon","Jewel","Jewell","Jillian","Jimmie","Jimmy","Jo","Joan","Joana","Joanie","Joanne","Joannie","Joanny","Joany","Joaquin","Jocelyn","Jodie","Jody","Joe","Joel","Joelle","Joesph","Joey","Johan","Johann","Johanna","Johathan","John","Johnathan","Johnathon","Johnnie","Johnny","Johnpaul","Johnson","Jolie","Jon","Jonas","Jonatan","Jonathan","Jonathon","Jordan","Jordane","Jordi","Jordon","Jordy","Jordyn","Jorge","Jose","Josefa","Josefina","Joseph","Josephine","Josh","Joshua","Joshuah","Josiah","Josiane","Josianne","Josie","Josue","Jovan","Jovani","Jovanny","Jovany","Joy","Joyce","Juana","Juanita","Judah","Judd","Jude","Judge","Judson","Judy","Jules","Julia","Julian","Juliana","Julianne","Julie","Julien","Juliet","Julio","Julius","June","Junior","Junius","Justen","Justice","Justina","Justine","Juston","Justus","Justyn","Juvenal","Juwan","Kacey","Kaci","Kacie","Kade","Kaden","Kadin","Kaela","Kaelyn","Kaia","Kailee","Kailey","Kailyn","Kaitlin","Kaitlyn","Kale","Kaleb","Kaleigh","Kaley","Kali","Kallie","Kameron","Kamille","Kamren","Kamron","Kamryn","Kane","Kara","Kareem","Karelle","Karen","Kari","Kariane","Karianne","Karina","Karine","Karl","Karlee","Karley","Karli","Karlie","Karolann","Karson","Kasandra","Kasey","Kassandra","Katarina","Katelin","Katelyn","Katelynn","Katharina","Katherine","Katheryn","Kathleen","Kathlyn","Kathryn","Kathryne","Katlyn","Katlynn","Katrina","Katrine","Kattie","Kavon","Kay","Kaya","Kaycee","Kayden","Kayla","Kaylah","Kaylee","Kayleigh","Kayley","Kayli","Kaylie","Kaylin","Keagan","Keanu","Keara","Keaton","Keegan","Keeley","Keely","Keenan","Keira","Keith","Kellen","Kelley","Kelli","Kellie","Kelly","Kelsi","Kelsie","Kelton","Kelvin","Ken","Kendall","Kendra","Kendrick","Kenna","Kennedi","Kennedy","Kenneth","Kennith","Kenny","Kenton","Kenya","Kenyatta","Kenyon","Keon","Keshaun","Keshawn","Keven","Kevin","Kevon","Keyon","Keyshawn","Khalid","Khalil","Kian","Kiana","Kianna","Kiara","Kiarra","Kiel","Kiera","Kieran","Kiley","Kim","Kimberly","King","Kip","Kira","Kirk","Kirsten","Kirstin","Kitty","Kobe","Koby","Kody","Kolby","Kole","Korbin","Korey","Kory","Kraig","Kris","Krista","Kristian","Kristin","Kristina","Kristofer","Kristoffer","Kristopher","Kristy","Krystal","Krystel","Krystina","Kurt","Kurtis","Kyla","Kyle","Kylee","Kyleigh","Kyler","Kylie","Kyra","Lacey","Lacy","Ladarius","Lafayette","Laila","Laisha","Lamar","Lambert","Lamont","Lance","Landen","Lane","Laney","Larissa","Laron","Larry","Larue","Laura","Laurel","Lauren","Laurence","Lauretta","Lauriane","Laurianne","Laurie","Laurine","Laury","Lauryn","Lavada","Lavern","Laverna","Laverne","Lavina","Lavinia","Lavon","Lavonne","Lawrence","Lawson","Layla","Layne","Lazaro","Lea","Leann","Leanna","Leanne","Leatha","Leda","Lee","Leif","Leila","Leilani","Lela","Lelah","Leland","Lelia","Lempi","Lemuel","Lenna","Lennie","Lenny","Lenora","Lenore","Leo","Leola","Leon","Leonard","Leonardo","Leone","Leonel","Leonie","Leonor","Leonora","Leopold","Leopoldo","Leora","Lera","Lesley","Leslie","Lesly","Lessie","Lester","Leta","Letha","Letitia","Levi","Lew","Lewis","Lexi","Lexie","Lexus","Lia","Liam","Liana","Libbie","Libby","Lila","Lilian","Liliana","Liliane","Lilla","Lillian","Lilliana","Lillie","Lilly","Lily","Lilyan","Lina","Lincoln","Linda","Lindsay","Lindsey","Linnea","Linnie","Linwood","Lionel","Lisa","Lisandro","Lisette","Litzy","Liza","Lizeth","Lizzie","Llewellyn","Lloyd","Logan","Lois","Lola","Lolita","Loma","Lon","London","Lonie","Lonnie","Lonny","Lonzo","Lora","Loraine","Loren","Lorena","Lorenz","Lorenza","Lorenzo","Lori","Lorine","Lorna","Lottie","Lou","Louie","Louisa","Lourdes","Louvenia","Lowell","Loy","Loyal","Loyce","Lucas","Luciano","Lucie","Lucienne","Lucile","Lucinda","Lucio","Lucious","Lucius","Lucy","Ludie","Ludwig","Lue","Luella","Luigi","Luis","Luisa","Lukas","Lula","Lulu","Luna","Lupe","Lura","Lurline","Luther","Luz","Lyda","Lydia","Lyla","Lynn","Lyric","Lysanne","Mabel","Mabelle","Mable","Mac","Macey","Maci","Macie","Mack","Mackenzie","Macy","Madaline","Madalyn","Maddison","Madeline","Madelyn","Madelynn","Madge","Madie","Madilyn","Madisen","Madison","Madisyn","Madonna","Madyson","Mae","Maegan","Maeve","Mafalda","Magali","Magdalen","Magdalena","Maggie","Magnolia","Magnus","Maia","Maida","Maiya","Major","Makayla","Makenna","Makenzie","Malachi","Malcolm","Malika","Malinda","Mallie","Mallory","Malvina","Mandy","Manley","Manuel","Manuela","Mara","Marc","Marcel","Marcelina","Marcelino","Marcella","Marcelle","Marcellus","Marcelo","Marcia","Marco","Marcos","Marcus","Margaret","Margarete","Margarett","Margaretta","Margarette","Margarita","Marge","Margie","Margot","Margret","Marguerite","Maria","Mariah","Mariam","Marian","Mariana","Mariane","Marianna","Marianne","Mariano","Maribel","Marie","Mariela","Marielle","Marietta","Marilie","Marilou","Marilyne","Marina","Mario","Marion","Marisa","Marisol","Maritza","Marjolaine","Marjorie","Marjory","Mark","Markus","Marlee","Marlen","Marlene","Marley","Marlin","Marlon","Marques","Marquis","Marquise","Marshall","Marta","Martin","Martina","Martine","Marty","Marvin","Mary","Maryam","Maryjane","Maryse","Mason","Mateo","Mathew","Mathias","Mathilde","Matilda","Matilde","Matt","Matteo","Mattie","Maud","Maude","Maudie","Maureen","Maurice","Mauricio","Maurine","Maverick","Mavis","Max","Maxie","Maxime","Maximilian","Maximillia","Maximillian","Maximo","Maximus","Maxine","Maxwell","May","Maya","Maybell","Maybelle","Maye","Maymie","Maynard","Mayra","Mazie","Mckayla","Mckenna","Mckenzie","Meagan","Meaghan","Meda","Megane","Meggie","Meghan","Mekhi","Melany","Melba","Melisa","Melissa","Mellie","Melody","Melvin","Melvina","Melyna","Melyssa","Mercedes","Meredith","Merl","Merle","Merlin","Merritt","Mertie","Mervin","Meta","Mia","Micaela","Micah","Michael","Michaela","Michale","Micheal","Michel","Michele","Michelle","Miguel","Mikayla","Mike","Mikel","Milan","Miles","Milford","Miller","Millie","Milo","Milton","Mina","Minerva","Minnie","Miracle","Mireille","Mireya","Misael","Missouri","Misty","Mitchel","Mitchell","Mittie","Modesta","Modesto","Mohamed","Mohammad","Mohammed","Moises","Mollie","Molly","Mona","Monica","Monique","Monroe","Monserrat","Monserrate","Montana","Monte","Monty","Morgan","Moriah","Morris","Mortimer","Morton","Mose","Moses","Moshe","Mossie","Mozell","Mozelle","Muhammad","Muriel","Murl","Murphy","Murray","Mustafa","Mya","Myah","Mylene","Myles","Myra","Myriam","Myrl","Myrna","Myron","Myrtice","Myrtie","Myrtis","Myrtle","Nadia","Nakia","Name","Nannie","Naomi","Naomie","Napoleon","Narciso","Nash","Nasir","Nat","Natalia","Natalie","Natasha","Nathan","Nathanael","Nathanial","Nathaniel","Nathen","Nayeli","Neal","Ned","Nedra","Neha","Neil","Nelda","Nella","Nelle","Nellie","Nels","Nelson","Neoma","Nestor","Nettie","Neva","Newell","Newton","Nia","Nicholas","Nicholaus","Nichole","Nick","Nicklaus","Nickolas","Nico","Nicola","Nicolas","Nicole","Nicolette","Nigel","Nikita","Nikki","Nikko","Niko","Nikolas","Nils","Nina","Noah","Noble","Noe","Noel","Noelia","Noemi","Noemie","Noemy","Nola","Nolan","Nona","Nora","Norbert","Norberto","Norene","Norma","Norris","Norval","Norwood","Nova","Novella","Nya","Nyah","Nyasia","Obie","Oceane","Ocie","Octavia","Oda","Odell","Odessa","Odie","Ofelia","Okey","Ola","Olaf","Ole","Olen","Oleta","Olga","Olin","Oliver","Ollie","Oma","Omari","Omer","Ona","Onie","Opal","Ophelia","Ora","Oral","Oran","Oren","Orie","Orin","Orion","Orland","Orlando","Orlo","Orpha","Orrin","Orval","Orville","Osbaldo","Osborne","Oscar","Osvaldo","Oswald","Oswaldo","Otha","Otho","Otilia","Otis","Ottilie","Ottis","Otto","Ova","Owen","Ozella","Pablo","Paige","Palma","Pamela","Pansy","Paolo","Paris","Parker","Pascale","Pasquale","Pat","Patience","Patricia","Patrick","Patsy","Pattie","Paul","Paula","Pauline","Paxton","Payton","Pearl","Pearlie","Pearline","Pedro","Peggie","Penelope","Percival","Percy","Perry","Pete","Peter","Petra","Peyton","Philip","Phoebe","Phyllis","Pierce","Pierre","Pietro","Pink","Pinkie","Piper","Polly","Porter","Precious","Presley","Preston","Price","Prince","Princess","Priscilla","Providenci","Prudence","Queen","Queenie","Quentin","Quincy","Quinn","Quinten","Quinton","Rachael","Rachel","Rachelle","Rae","Raegan","Rafael","Rafaela","Raheem","Rahsaan","Rahul","Raina","Raleigh","Ralph","Ramiro","Ramon","Ramona","Randal","Randall","Randi","Randy","Ransom","Raoul","Raphael","Raphaelle","Raquel","Rashad","Rashawn","Rasheed","Raul","Raven","Ray","Raymond","Raymundo","Reagan","Reanna","Reba","Rebeca","Rebecca","Rebeka","Rebekah","Reece","Reed","Reese","Regan","Reggie","Reginald","Reid","Reilly","Reina","Reinhold","Remington","Rene","Renee","Ressie","Reta","Retha","Retta","Reuben","Reva","Rex","Rey","Reyes","Reymundo","Reyna","Reynold","Rhea","Rhett","Rhianna","Rhiannon","Rhoda","Ricardo","Richard","Richie","Richmond","Rick","Rickey","Rickie","Ricky","Rico","Rigoberto","Riley","Rita","River","Robb","Robbie","Robert","Roberta","Roberto","Robin","Robyn","Rocio","Rocky","Rod","Roderick","Rodger","Rodolfo","Rodrick","Rodrigo","Roel","Rogelio","Roger","Rogers","Rolando","Rollin","Roma","Romaine","Roman","Ron","Ronaldo","Ronny","Roosevelt","Rory","Rosa","Rosalee","Rosalia","Rosalind","Rosalinda","Rosalyn","Rosamond","Rosanna","Rosario","Roscoe","Rose","Rosella","Roselyn","Rosemarie","Rosemary","Rosendo","Rosetta","Rosie","Rosina","Roslyn","Ross","Rossie","Rowan","Rowena","Rowland","Roxane","Roxanne","Roy","Royal","Royce","Rozella","Ruben","Rubie","Ruby","Rubye","Rudolph","Rudy","Rupert","Russ","Russel","Russell","Rusty","Ruth","Ruthe","Ruthie","Ryan","Ryann","Ryder","Rylan","Rylee","Ryleigh","Ryley","Sabina","Sabrina","Sabryna","Sadie","Sadye","Sage","Saige","Sallie","Sally","Salma","Salvador","Salvatore","Sam","Samanta","Samantha","Samara","Samir","Sammie","Sammy","Samson","Sandra","Sandrine","Sandy","Sanford","Santa","Santiago","Santina","Santino","Santos","Sarah","Sarai","Sarina","Sasha","Saul","Savanah","Savanna","Savannah","Savion","Scarlett","Schuyler","Scot","Scottie","Scotty","Seamus","Sean","Sebastian","Sedrick","Selena","Selina","Selmer","Serena","Serenity","Seth","Shad","Shaina","Shakira","Shana","Shane","Shanel","Shanelle","Shania","Shanie","Shaniya","Shanna","Shannon","Shanny","Shanon","Shany","Sharon","Shaun","Shawn","Shawna","Shaylee","Shayna","Shayne","Shea","Sheila","Sheldon","Shemar","Sheridan","Sherman","Sherwood","Shirley","Shyann","Shyanne","Sibyl","Sid","Sidney","Sienna","Sierra","Sigmund","Sigrid","Sigurd","Silas","Sim","Simeon","Simone","Sincere","Sister","Skye","Skyla","Skylar","Sofia","Soledad","Solon","Sonia","Sonny","Sonya","Sophia","Sophie","Spencer","Stacey","Stacy","Stan","Stanford","Stanley","Stanton","Stefan","Stefanie","Stella","Stephan","Stephania","Stephanie","Stephany","Stephen","Stephon","Sterling","Steve","Stevie","Stewart","Stone","Stuart","Summer","Sunny","Susan","Susana","Susanna","Susie","Suzanne","Sven","Syble","Sydnee","Sydney","Sydni","Sydnie","Sylvan","Sylvester","Sylvia","Tabitha","Tad","Talia","Talon","Tamara","Tamia","Tania","Tanner","Tanya","Tara","Taryn","Tate","Tatum","Tatyana","Taurean","Tavares","Taya","Taylor","Teagan","Ted","Telly","Terence","Teresa","Terrance","Terrell","Terrence","Terrill","Terry","Tess","Tessie","Tevin","Thad","Thaddeus","Thalia","Thea","Thelma","Theo","Theodora","Theodore","Theresa","Therese","Theresia","Theron","Thomas","Thora","Thurman","Tia","Tiana","Tianna","Tiara","Tierra","Tiffany","Tillman","Timmothy","Timmy","Timothy","Tina","Tito","Titus","Tobin","Toby","Tod","Tom","Tomas","Tomasa","Tommie","Toney","Toni","Tony","Torey","Torrance","Torrey","Toy","Trace","Tracey","Tracy","Travis","Travon","Tre","Tremaine","Tremayne","Trent","Trenton","Tressa","Tressie","Treva","Trever","Trevion","Trevor","Trey","Trinity","Trisha","Tristian","Tristin","Triston","Troy","Trudie","Trycia","Trystan","Turner","Twila","Tyler","Tyra","Tyree","Tyreek","Tyrel","Tyrell","Tyrese","Tyrique","Tyshawn","Tyson","Ubaldo","Ulices","Ulises","Una","Unique","Urban","Uriah","Uriel","Ursula","Vada","Valentin","Valentina","Valentine","Valerie","Vallie","Van","Vance","Vanessa","Vaughn","Veda","Velda","Vella","Velma","Velva","Vena","Verda","Verdie","Vergie","Verla","Verlie","Vern","Verna","Verner","Vernice","Vernie","Vernon","Verona","Veronica","Vesta","Vicenta","Vicente","Vickie","Vicky","Victor","Victoria","Vida","Vidal","Vilma","Vince","Vincent","Vincenza","Vincenzo","Vinnie","Viola","Violet","Violette","Virgie","Virgil","Virginia","Virginie","Vita","Vito","Viva","Vivian","Viviane","Vivianne","Vivien","Vivienne","Vladimir","Wade","Waino","Waldo","Walker","Wallace","Walter","Walton","Wanda","Ward","Warren","Watson","Wava","Waylon","Wayne","Webster","Weldon","Wellington","Wendell","Wendy","Werner","Westley","Weston","Whitney","Wilber","Wilbert","Wilburn","Wiley","Wilford","Wilfred","Wilfredo","Wilfrid","Wilhelm","Wilhelmine","Will","Willa","Willard","William","Willie","Willis","Willow","Willy","Wilma","Wilmer","Wilson","Wilton","Winfield","Winifred","Winnifred","Winona","Winston","Woodrow","Wyatt","Wyman","Xander","Xavier","Xzavier","Yadira","Yasmeen","Yasmin","Yasmine","Yazmin","Yesenia","Yessenia","Yolanda","Yoshiko","Yvette","Yvonne","Zachariah","Zachary","Zachery","Zack","Zackary","Zackery","Zakary","Zander","Zane","Zaria","Zechariah","Zelda","Zella","Zelma","Zena","Zetta","Zion","Zita","Zoe","Zoey","Zoie","Zoila","Zola","Zora","Zula"];e.exports={generateUsername:function(){var e=r.randomElement(i),t=r.randomAlphanumStr(3);return"".concat(e,"-").concat(t)}}},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return h});var r=n(3),i=n(19),o=n(23),a=n(9);function s(e,t){return(function(e){if(Array.isArray(e))return e})(e)||(function(e,t){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){i=!0,o=e}finally{try{r||null==s.return||s.return()}finally{if(i)throw o}}return n})(e,t)||(function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")})()}var c=n(11),u=n(4).getLogger(e),l={};function d(e,t){return!t||t<=0||!e||e<=0?0:Math.round(e/t*100)}function p(){this.loss={},this.bitrate={download:0,upload:0},this.resolution={},this.framerate=0}function f(){this.bandwidth={},this.bitrate={},this.packetLoss=null,this.transport=[]}function h(e,t,n,i){this._browserType=r.a.getName();var o=l[this._browserType];if(!o)throw"The browser type '".concat(this._browserType,"' isn't supported!");this._usesPromiseGetStats=r.a.isSafariWithWebrtc()||r.a.isFirefox(),this._getStatValue=this._usesPromiseGetStats?this._defineNewGetStatValueMethod(o):this._defineGetStatValueMethod(o),this.peerconnection=e,this.baselineAudioLevelsReport=null,this.currentAudioLevelsReport=null,this.currentStatsReport=null,this.previousStatsReport=null,this.audioLevelReportHistory={},this.audioLevelsIntervalId=null,this.eventEmitter=i,this.conferenceStats=new f,this.audioLevelsIntervalMilis=t,this.statsIntervalId=null,this.statsIntervalMilis=n,this.ssrc2stats=new Map}l[i.b.FIREFOX]={ssrc:"ssrc",packetsReceived:"packetsReceived",packetsLost:"packetsLost",packetsSent:"packetsSent",bytesReceived:"bytesReceived",bytesSent:"bytesSent",framerateMean:"framerateMean",ip:"ipAddress",port:"portNumber",protocol:"transport"},l[i.b.CHROME]={receiveBandwidth:"googAvailableReceiveBandwidth",sendBandwidth:"googAvailableSendBandwidth",remoteAddress:"googRemoteAddress",transportType:"googTransportType",localAddress:"googLocalAddress",activeConnection:"googActiveConnection",ssrc:"ssrc",packetsReceived:"packetsReceived",packetsSent:"packetsSent",packetsLost:"packetsLost",bytesReceived:"bytesReceived",bytesSent:"bytesSent",googFrameHeightReceived:"googFrameHeightReceived",googFrameWidthReceived:"googFrameWidthReceived",googFrameHeightSent:"googFrameHeightSent",googFrameWidthSent:"googFrameWidthSent",googFrameRateReceived:"googFrameRateReceived",googFrameRateSent:"googFrameRateSent",audioInputLevel:"audioInputLevel",audioOutputLevel:"audioOutputLevel",currentRoundTripTime:"googRtt",remoteCandidateType:"googRemoteCandidateType",localCandidateType:"googLocalCandidateType",ip:"ip",port:"port",protocol:"protocol"},l[i.b.EDGE]={sendBandwidth:"googAvailableSendBandwidth",remoteAddress:"remoteAddress",transportType:"protocol",localAddress:"localAddress",activeConnection:"activeConnection",ssrc:"ssrc",packetsReceived:"packetsReceived",packetsSent:"packetsSent",packetsLost:"packetsLost",bytesReceived:"bytesReceived",bytesSent:"bytesSent",googFrameHeightReceived:"frameHeight",googFrameWidthReceived:"frameWidth",googFrameHeightSent:"frameHeight",googFrameWidthSent:"frameWidth",googFrameRateReceived:"framesPerSecond",googFrameRateSent:"framesPerSecond",audioInputLevel:"audioLevel",audioOutputLevel:"audioLevel",currentRoundTripTime:"roundTripTime"},l[i.b.OPERA]=l[i.b.CHROME],l[i.b.NWJS]=l[i.b.CHROME],l[i.b.ELECTRON]=l[i.b.CHROME],l[i.b.SAFARI]=l[i.b.CHROME],l[i.b.REACT_NATIVE]=l[i.b.CHROME],p.prototype.setLoss=function(e){this.loss=e||{}},p.prototype.setResolution=function(e){this.resolution=e||{}},p.prototype.addBitrate=function(e){this.bitrate.download+=e.download,this.bitrate.upload+=e.upload},p.prototype.resetBitrate=function(){this.bitrate.download=0,this.bitrate.upload=0},p.prototype.setFramerate=function(e){this.framerate=e||0},h.prototype.stop=function(){this.audioLevelsIntervalId&&(clearInterval(this.audioLevelsIntervalId),this.audioLevelsIntervalId=null),this.statsIntervalId&&(clearInterval(this.statsIntervalId),this.statsIntervalId=null)},h.prototype.errorCallback=function(e){c.callErrorHandler(e),u.error("Get stats error",e),this.stop()},h.prototype.start=function(e){var t=this,n=this;e&&(this.audioLevelsIntervalId=setInterval(function(){n.peerconnection.getStats(function(e){var r;r=e&&e.result&&"function"==typeof e.result?e.result():e,n.currentAudioLevelsReport=r,t._usesPromiseGetStats?n.processNewAudioLevelReport():n.processAudioLevelReport(),n.baselineAudioLevelsReport=n.currentAudioLevelsReport},function(e){return n.errorCallback(e)})},n.audioLevelsIntervalMilis)),r.a.supportsRtpStatistics()&&(this.statsIntervalId=setInterval(function(){n.peerconnection.getStats(function(e){var r;r=e&&e.result&&"function"==typeof e.result?e.result():e,n.currentStatsReport=r;try{t._usesPromiseGetStats?n.processNewStatsReport():n.processStatsReport()}catch(e){c.callErrorHandler(e),u.error("Unsupported key:".concat(e),e)}n.previousStatsReport=n.currentStatsReport},function(e){return n.errorCallback(e)})},n.statsIntervalMilis))},h.prototype._defineGetStatValueMethod=function(e){var t;switch(this._browserType){case i.b.CHROME:case i.b.OPERA:case i.b.NWJS:case i.b.ELECTRON:t=function(e,t){return e.stat(t)};break;case i.b.REACT_NATIVE:t=function(e,t){var n;return e.values.some(function(e){return!!e.hasOwnProperty(t)&&(n=e[t],!0)}),n};break;case i.b.EDGE:t=function(e,t){return e[t]};break;default:t=function(e,t){return e[t]}}return function(n,r){return t(n,(function(t){var n=e[t];if(n)return n;throw"The property '".concat(t,"' isn't supported!")})(r))}},h.prototype.getNonNegativeStat=function(e,t){var n=this._getStatValue(e,t);return"number"!=typeof n&&(n=Number(n)),isNaN(n)?0:Math.max(0,n)},h.prototype.processStatsReport=function(){var e=this;if(this.previousStatsReport){var t=this._getStatValue,n={};for(var i in this.currentStatsReport)if(this.currentStatsReport.hasOwnProperty(i)){var a=this.currentStatsReport[i];if(a){try{var s=t(a,"receiveBandwidth"),c=t(a,"sendBandwidth");(s||c)&&(this.conferenceStats.bandwidth={download:Math.round(s/1e3),upload:Math.round(c/1e3)})}catch(e){}if("googCandidatePair"===a.type&&"continue"===(function(){var n=void 0,r=void 0,i=void 0,o=void 0,s=void 0,c=void 0,u=void 0;try{if(!(n=t(a,"activeConnection")))return"continue";r=t(a,"remoteAddress"),u=t(a,"transportType"),o=t(a,"localAddress"),i=t(a,"localCandidateType"),s=t(a,"remoteCandidateType"),c=e.getNonNegativeStat(a,"currentRoundTripTime")}catch(e){}if(!r||!u||!o||"true"!==n)return"continue";var l=e.conferenceStats.transport;return l.some(function(e){return e.ip===r&&e.type===u&&e.localip===o})||l.push({ip:r,type:u,localip:o,p2p:e.peerconnection.isP2P,localCandidateType:i,remoteCandidateType:s,rtt:c}),"continue"})())continue;if("candidatepair"===a.type){if("succeeded"!==a.state||!a.selected)continue;var l=this.currentStatsReport[a.localCandidateId],d=this.currentStatsReport[a.remoteCandidateId];this.conferenceStats.transport.push({ip:"".concat(d.ipAddress,":").concat(d.portNumber),type:l.transport,localip:"".concat(l.ipAddress,":").concat(l.portNumber),p2p:this.peerconnection.isP2P,localCandidateType:l.candidateType,remoteCandidateType:d.candidateType})}if("transportdiagnostics"===a.msType&&this.conferenceStats.transport.push({ip:a.remoteAddress,type:a.protocol,localip:a.localAddress,p2p:this.peerconnection.isP2P}),("ssrc"===a.type||"outboundrtp"===a.type||"inboundrtp"===a.type||"track"===a.type)&&(!r.a.isEdge()||"inboundrtp"!==a.type&&"outboundrtp"!==a.type)){var f=this.previousStatsReport[i],h=this.getNonNegativeStat(a,"ssrc");if("track"===a.type&&Array.isArray(a.ssrcIds)&&(h=Number(a.ssrcIds[0])),f&&h&&(r.a.isEdge()||!0!==a.isRemote&&!0!==a.remoteSource)){var m=this.ssrc2stats.get(h);m||(m=new p,this.ssrc2stats.set(h,m));var v=!0,y="packetsReceived",g=t(a,y);null!=g&&""!==g||(v=!1,null==(g=t(a,y="packetsSent"))&&u.warn("No packetsReceived nor packetsSent stat found")),(!g||g<0)&&(g=0);var S=this.getNonNegativeStat(f,y),_=Math.max(0,g-S),b=this.getNonNegativeStat(a,"packetsLost"),E=this.getNonNegativeStat(f,"packetsLost"),T=Math.max(0,b-E);m.setLoss({packetsTotal:_+T,packetsLost:T,isDownloadStream:v});var C=this.getNonNegativeStat(a,"bytesReceived"),R=this.getNonNegativeStat(f,"bytesReceived"),A=Math.max(0,C-R),w=0,k=t(a,"bytesSent");"number"!=typeof k&&"string"!=typeof k||(k=Number(k),isNaN(k)||(n[h]=k,k>0&&(w=k-t(f,"bytesSent")))),w=Math.max(0,w);var O=a.timestamp-f.timestamp,I=0,P=0;O>0&&(I=Math.round(8*A/O),P=Math.round(8*w/O)),m.addBitrate({download:I,upload:P});var D={height:null,width:null};try{var N=void 0,L=void 0;(N=t(a,"googFrameHeightReceived"))&&(L=t(a,"googFrameWidthReceived"))?(D.height=N,D.width=L):(N=t(a,"googFrameHeightSent"))&&(L=t(a,"googFrameWidthSent"))&&(D.height=N,D.width=L)}catch(e){}var M=void 0;try{M=t(a,"googFrameRateReceived")||t(a,"googFrameRateSent")||0}catch(e){try{M=this.getNonNegativeStat(a,"framerateMean")}catch(e){}}m.setFramerate(Math.round(M||0)),D.height&&D.width?m.setResolution(D):m.setResolution(null)}}}}this.eventEmitter.emit(o.c,this.peerconnection,n),this._processAndEmitReport()}},h.prototype._processAndEmitReport=function(){var e=this,t={download:0,upload:0},n={download:0,upload:0},r=0,i=0,a={},c={},l=0,p=0,f=0,h=0,m=!0,v=!1,y=void 0;try{for(var g,S=this.ssrc2stats["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(m=(g=S.next()).done);m=!0){var _=s(g.value,2),b=_[0],E=_[1],T=E.loss,C=T.isDownloadStream?"download":"upload";t[C]+=T.packetsTotal,n[C]+=T.packetsLost,r+=E.bitrate.download,i+=E.bitrate.upload;var R=this.peerconnection.getTrackBySSRC(b);if(R){R.isAudioTrack()?(l+=E.bitrate.download,p+=E.bitrate.upload):(f+=E.bitrate.download,h+=E.bitrate.upload);var A=R.getParticipantId();if(A){var w=E.resolution;if(w.width&&w.height&&-1!==w.width&&-1!==w.height){var k=a[A]||{};k[b]=w,a[A]=k}if(0!==E.framerate){var O=c[A]||{};O[b]=E.framerate,c[A]=O}}else u.error("No participant ID returned by ".concat(R))}E.resetBitrate()}}catch(e){v=!0,y=e}finally{try{m||null==S.return||S.return()}finally{if(v)throw y}}this.conferenceStats.bitrate={upload:i,download:r},this.conferenceStats.bitrate.audio={upload:p,download:l},this.conferenceStats.bitrate.video={upload:h,download:f},this.conferenceStats.packetLoss={total:d(n.download+n.upload,t.download+t.upload),download:d(n.download,t.download),upload:d(n.upload,t.upload)};var I,P={};Object.keys(this.audioLevelReportHistory).forEach(function(t){var n=e.audioLevelReportHistory[t],r=n.data,i=n.isLocal,o=r.reduce(function(e,t){return e+t})/r.length;if(i)I=o;else{var a=e.peerconnection.getTrackBySSRC(Number(t));if(a){var s=a.getParticipantId();s&&(P[s]=o)}}}),this.audioLevelReportHistory={},this.eventEmitter.emit(o.d,this.peerconnection,{bandwidth:this.conferenceStats.bandwidth,bitrate:this.conferenceStats.bitrate,packetLoss:this.conferenceStats.packetLoss,resolution:a,framerate:c,transport:this.conferenceStats.transport,localAvgAudioLevels:I,avgAudioLevels:P}),this.conferenceStats.transport=[]},h.prototype.processAudioLevelReport=function(){if(this.baselineAudioLevelsReport){var e=this._getStatValue;for(var t in this.currentAudioLevelsReport)if(this.currentAudioLevelsReport.hasOwnProperty(t)){var n=this.currentAudioLevelsReport[t];if("ssrc"===n.type||"track"===n.type){var i=this.baselineAudioLevelsReport[t],a=this.getNonNegativeStat(n,"ssrc");if(!a&&Array.isArray(n.ssrcIds)&&(a=Number(n.ssrcIds[0])),i)if(a){var s=void 0;try{s=e(n,"audioInputLevel")||e(n,"audioOutputLevel")}catch(e){return u.warn("Audio Levels are not available in the statistics."),void clearInterval(this.audioLevelsIntervalId)}if(s){var c;c="ssrc"===n.type?!e(n,"packetsReceived"):!n.remoteSource,r.a.isEdge()?s=s<0?Math.pow(10,s/20):0:s/=32767,a in this.audioLevelReportHistory||(this.audioLevelReportHistory[a]={isLocal:c,data:[]}),this.audioLevelReportHistory[a].data.push(s),this.eventEmitter.emit(o.a,this.peerconnection,a,s,c)}}else Date.now()-n.timestamp<3e3&&u.warn("No ssrc: ");else u.warn("".concat(a," not enough data"))}}}},h.prototype._defineNewGetStatValueMethod=function(e){return function(t,n){return t[(function(t){var n=e[t];if(n)return n;throw"The property '".concat(t,"' isn't supported!")})(n)]}},h.prototype.getNonNegativeValue=function(e){var t=e;return"number"!=typeof t&&(t=Number(t)),isNaN(t)?0:Math.max(0,t)},h.prototype._calculateBitrate=function(e,t,n){var r=this.getNonNegativeValue(e[n]),i=this.getNonNegativeValue(t[n]),o=Math.max(0,r-i),a=e.timestamp-t.timestamp,s=0;return a>0&&(s=Math.round(8*o/a)),s},h.prototype.processNewStatsReport=function(){var e=this;if(this.previousStatsReport){var t=this._getStatValue,n={};this.currentStatsReport.forEach(function(r){if("candidate-pair"===r.type&&r.nominated&&"succeeded"===r.state){var i=r.availableIncomingBitrate,o=r.availableOutgoingBitrate;(i||o)&&(e.conferenceStats.bandwidth={download:Math.round(i/1e3),upload:Math.round(o/1e3)});var a=e.currentStatsReport.get(r.remoteCandidateId),s=e.currentStatsReport.get(r.localCandidateId);if(a&&s){var c=t(a,"ip"),u=t(a,"port"),l="".concat(c,":").concat(u),d=t(s,"ip"),f=t(s,"port"),h="".concat(d,":").concat(f),m=t(a,"protocol"),v=e.conferenceStats.transport;v.some(function(e){return e.ip===l&&e.type===m&&e.localip===h})||v.push({ip:l,type:m,localIp:h,p2p:e.peerconnection.isP2P,localCandidateType:s.candidateType,remoteCandidateType:a.candidateType,networkType:s.networkType,rtt:1e3*r.currentRoundTripTime})}}else if("inbound-rtp"===r.type||"outbound-rtp"===r.type){var y=e.previousStatsReport.get(r.id),g=e.getNonNegativeValue(r.ssrc);if(!y||!g)return;var S=e.ssrc2stats.get(g);S||(S=new p,e.ssrc2stats.set(g,S));var _=!0,b="packetsReceived";"outbound-rtp"===r.type&&(_=!1,b="packetsSent");var E=r[b];(!E||E<0)&&(E=0);var T=e.getNonNegativeValue(y[b]),C=Math.max(0,E-T),R=e.getNonNegativeValue(r.packetsLost),A=e.getNonNegativeValue(y.packetsLost),w=Math.max(0,R-A);S.setLoss({packetsTotal:C+w,packetsLost:w,isDownloadStream:_}),"inbound-rtp"===r.type?S.addBitrate({download:e._calculateBitrate(r,y,"bytesReceived"),upload:0}):(n[g]=e.getNonNegativeValue(r.bytesSent),S.addBitrate({download:0,upload:e._calculateBitrate(r,y,"bytesSent")}));var k=r.framerateMean;k&&S.setFramerate(Math.round(k||0))}else if("track"===r.type){var O={height:r.frameHeight,width:r.frameWidth},I=r.framesPerSecond;if(!I){var P=e.previousStatsReport.get(r.id);if(P){var D=r.timestamp-P.timestamp;D>0&&r.framesSent&&(I=(r.framesSent-P.framesSent)/D*1e3)}if(!I)return}var N=r.trackIdentifier,L=e.peerconnection.getSsrcByTrackId(N),M=e.ssrc2stats.get(L);M||(M=new p,e.ssrc2stats.set(L,M)),M.setFramerate(Math.round(I||0)),O.height&&O.width?M.setResolution(O):M.setResolution(null)}}),this.eventEmitter.emit(o.c,this.peerconnection,n),this._processAndEmitReport()}},h.prototype.processNewAudioLevelReport=function(){var e=this;this.baselineAudioLevelsReport&&this.currentAudioLevelsReport.forEach(function(t){if("track"===t.type){var n=t.audioLevel;if(n){var r=t.trackIdentifier,i=e.peerconnection.getSsrcByTrackId(r);if(i){var s=i===e.peerconnection.getLocalSSRC(e.peerconnection.getLocalTracks(a.a));e.eventEmitter.emit(o.a,e.peerconnection,i,n,s)}}}})}}).call(this,"modules/statistics/RTPStatsCollector.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return l});var r=n(1),i=n(4),o=n(2),a=n(41),s=n(9);function c(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:5e3;return this._conference.xmpp.caps.getFeatures(this._jid,t).catch(function(n){return n&&n.constructor===String?e._conference.xmpp.caps.getFeatures(e._jid,t):(u.warn("Failed to discover features of ".concat(e._jid),n),Promise.reject(n))})}},{key:"getBotType",value:function(){return this._botType}}])&&c(t.prototype,n),e})()}).call(this,"JitsiParticipant.js")},function(e,t,n){"use strict";n.d(t,"a",function(){return o});var r=n(21),i=n(35);function o(e){var t,n=this,o=e.id,a=e.password,s=e.onCreateResource,c=e.onLoginSuccessful,u=e.roomPassword,l=!1,d=new i.c(this.connection.options),p=new Promise(function(e,i){t=i,d.addListener(r.CONNECTION_DISCONNECTED,function(){d=void 0}),d.addListener(r.CONNECTION_ESTABLISHED,function(){l||(c&&c(),d.createRoom(n.options.name,n.options.config,s).moderator.authenticate().then(function(){d&&d.disconnect(),l||(n.join(u),e())}).catch(function(e){var t=e.error,n=e.message;d.disconnect(),i({authenticationError:t,message:n})}))}),d.addListener(r.CONNECTION_FAILED,function(e,t,n){i({connectionError:e,credentials:n,message:t}),d=void 0}),l||d.connect(o,a)});return p.cancel=function(){l=!0,t({}),d&&d.disconnect()},p}},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(1),o=n(79),a=n(34),s=n(0),c=n.n(s);function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function l(e,t){for(var n=0;nx[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length&&n.createNonAnonymousRoom(),n.onPresence(e),!0)}},{key:"onPresenceUnavailable",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];return!n||(n.onPresenceUnavailable(e,t),!0)}},{key:"onPresenceError",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];return!n||(n.onPresenceError(e,t),!0)}},{key:"onMessage",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];return!n||(n.onMessage(e,t),!0)}},{key:"onMute",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];return!n||(n.onMute(e),!0)}}])&&l(n.prototype,r),t})();t.a=function(e){i.Strophe.addConnectionPlugin("emuc",new v(e))}}).call(this,"modules/xmpp/strophe.emuc.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return E});var r=n(4),i=n(1),o=n(11),a=n.n(o),s=n(52),c=n(24),u=n(25),l=n(9),d=n(0),p=n.n(d),f=n(80);function h(e){return(h="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function m(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.presMap.to=this.myroomjid,this.presMap.xns="http://jabber.org/protocol/muc",this.presMap.nodes=[],e.enableStatsID&&this.presMap.nodes.push({tagName:"stats-id",value:u.a.callStatsUserName}),this.addVideoInfoToPresence(!1),e.deploymentInfo&&e.deploymentInfo.userRegion&&this.presMap.nodes.push({tagName:"region",attributes:{id:e.deploymentInfo.userRegion,xmlns:"http://jitsi.org/jitsi-meet"}})}},{key:"join",value:function(e){var t=this;return this.password=e,new Promise(function(e){t.options.disableFocus&&S.info("Conference focus disabled"),(t.options.disableFocus?Promise.resolve():t.moderator.allocateConferenceFocus()).then(function(){t.sendPresence(!0),e()})})}},{key:"sendPresence",value:function(e){var t=this.presMap.to;if(t&&(this.joined||e)){var n=Object(i.$pres)({to:t});e&&(n.c("x",{xmlns:this.presMap.xns}),this.password&&n.c("password").t(this.password).up(),n.up()),_.json2packet(this.presMap.nodes,n),this.connection.send(n),e&&this.connection.flush()}}},{key:"doLeave",value:function(){S.log("do leave",this.myroomjid);var e=Object(i.$pres)({to:this.myroomjid,type:"unavailable"});this.presMap.length=0,this.connection.flush(),this.connection.send(e),this.connection.flush()}},{key:"discoRoomInfo",value:function(){var e=this,t=Object(i.$iq)({type:"get",to:this.roomjid}).c("query",{xmlns:i.Strophe.NS.DISCO_INFO});this.connection.sendIQ(t,function(t){var n=1===$(t).find('>query>feature[var="muc_passwordprotected"]').length;n!==e.locked&&(e.eventEmitter.emit(p.a.MUC_LOCK_CHANGED,n),e.locked=n)},function(e){a.a.callErrorHandler(e),S.error("Error getting room info: ",e)})}},{key:"createNonAnonymousRoom",value:function(){var e=Object(i.$iq)({type:"get",to:this.roomjid}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"}).c("x",{xmlns:"jabber:x:data",type:"submit"}),t=this;this.connection.sendIQ(e,function(e){if(!$(e).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_whois"]').length){var n="non-anonymous rooms not supported";return a.a.callErrorHandler(new Error(n)),void S.error(n)}var r=Object(i.$iq)({to:t.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"});r.c("x",{xmlns:"jabber:x:data",type:"submit"}),r.c("field",{var:"FORM_TYPE"}).c("value").t("http://jabber.org/protocol/muc#roomconfig").up().up(),r.c("field",{var:"muc#roomconfig_whois"}).c("value").t("anyone").up().up(),t.connection.sendIQ(r)},function(e){a.a.callErrorHandler(e),S.error("Error getting room configuration form: ",e)})}},{key:"onPresence",value:function(e){var t=e.getAttribute("from"),n={},r=e.getElementsByTagName("status")[0];r&&(n.status=r.textContent||"");var o=!1,a=!1,s=e.getElementsByTagNameNS("http://jabber.org/protocol/muc#user","x")[0],c=s&&s.getElementsByTagName("item")[0];n.affiliation=c&&c.getAttribute("affiliation"),n.role=c&&c.getAttribute("role");var u=c&&c.getAttribute("jid");n.jid=u,n.isFocus=u&&0===u.indexOf("".concat(this.moderator.getFocusUserJid(),"/")),n.isHiddenDomain=u&&u.indexOf("@")>0&&this.options.hiddenDomain===u.substring(u.indexOf("@")+1,u.indexOf("/")),this.eventEmitter.emit(p.a.PRESENCE_RECEIVED,{fromHiddenDomain:n.isHiddenDomain,presence:e});var l=e.querySelector("x");l&&l.remove();var d=[];_.packet2JSON(e,d),this.lastPresences[t]=d;for(var f=function(e){var t={},n=e.children.find(function(e){return"user"===e.tagName});if(n){t.user={};for(var r=["id","name","avatar"],i=function(){var e=r[o],i=n.children.find(function(t){return t.tagName===e});i&&(t.user[e]=i.value)},o=0;oignore[xmlns="http://jitsi.org/jitmeet/"]').length)return!0;if($(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>destroy').length){var r,o=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>destroy>reason');return o.length&&(r=o.text()),this.eventEmitter.emit(p.a.MUC_DESTROYED,r),this.connection.emuc.doLeave(this.roomjid),!0}var a=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length,s=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length,c=Object.keys(this.members);if(s){var u,l=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item>actor');l.length&&(u=l.attr("nick")),c.find(function(e){return i.Strophe.getResourceFromJid(e)===u})&&this.eventEmitter.emit(p.a.KICKED,a,u,i.Strophe.getResourceFromJid(t))}a?c.length>0&&(c.forEach(function(e){var t=n.members[e];delete n.members[e],n.onParticipantLeft(e,t.isFocus)}),this.connection.emuc.doLeave(this.roomjid),s||this.eventEmitter.emit(p.a.MUC_LEFT)):(delete this.members[t],this.onParticipantLeft(t,!1))}},{key:"onMessage",value:function(e,t){var n=$(e).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text()||i.Strophe.getResourceFromJid(t),r=$(e).find(">body").text(),o=e.getAttribute("type");if("error"===o)return this.eventEmitter.emit(p.a.CHAT_ERROR_RECEIVED,$(e).find(">text").text(),r),!0;var a=$(e).find(">subject");if(a.length){var s=a.text();(s||""===s)&&(this.eventEmitter.emit(p.a.SUBJECT_CHANGED,s),S.log("Subject is changed to ".concat(s)))}var c=$(e).find(">delay").attr("stamp");if(!c&&(c=$(e).find('>[xmlns="jabber:x:delay"]').attr("stamp"))){var u=c.match(/(\d{4})(\d{2})(\d{2}T\d{2}:\d{2}:\d{2})/);c="".concat(u[1],"-").concat(u[2],"-").concat(u[3],"Z")}t===this.roomjid&&$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="104"]').length&&this.discoRoomInfo();var l=$(e).find(">json-message").text(),d=this.xmpp.tryParseJSONAndVerify(l);d&&void 0===c?this.eventEmitter.emit(p.a.JSON_MESSAGE_RECEIVED,t,d):r&&("chat"===o?this.eventEmitter.emit(p.a.PRIVATE_MESSAGE_RECEIVED,t,n,r,this.myroomjid,c):"groupchat"===o&&this.eventEmitter.emit(p.a.MESSAGE_RECEIVED,t,n,r,this.myroomjid,c))}},{key:"onPresenceError",value:function(e,t){$(e).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length?(S.log("on password required",t),this.eventEmitter.emit(p.a.PASSWORD_REQUIRED)):$(e).find('>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length?i.Strophe.getDomainFromJid(e.getAttribute("to"))===this.xmpp.options.hosts.anonymousdomain?this.eventEmitter.emit(p.a.ROOM_JOIN_ERROR):(S.warn("onPresError ",e),this.eventEmitter.emit(p.a.ROOM_CONNECT_NOT_ALLOWED_ERROR)):$(e).find(">error>service-unavailable").length?(S.warn("Maximum users limit for the room has been reached",e),this.eventEmitter.emit(p.a.ROOM_MAX_USERS_ERROR)):(S.warn("onPresError ",e),this.eventEmitter.emit(p.a.ROOM_CONNECT_ERROR))}},{key:"kick",value:function(e){var t=Object(i.$iq)({to:this.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#admin"}).c("item",{nick:i.Strophe.getResourceFromJid(e),role:"none"}).c("reason").t("You have been kicked.").up().up().up();this.connection.sendIQ(t,function(t){return S.log("Kick participant with jid: ",e,t)},function(e){return S.log("Kick participant error: ",e)})}},{key:"lockRoom",value:function(e,t,n,r){var o=this;this.connection.sendIQ(Object(i.$iq)({to:this.roomjid,type:"get"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"}),function(a){if($(a).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length){var s=Object(i.$iq)({to:o.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"});s.c("x",{xmlns:"jabber:x:data",type:"submit"}),s.c("field",{var:"FORM_TYPE"}).c("value").t("http://jabber.org/protocol/muc#roomconfig").up().up(),s.c("field",{var:"muc#roomconfig_roomsecret"}).c("value").t(e).up().up(),s.c("field",{var:"muc#roomconfig_whois"}).c("value").t("anyone").up().up(),o.connection.sendIQ(s,t,n)}else r()},n)}},{key:"addToPresence",value:function(e,t){t.tagName=e,this.removeFromPresence(e),this.presMap.nodes.push(t)}},{key:"getFromPresence",value:function(e){return this.presMap.nodes.find(function(t){return e===t.tagName})}},{key:"removeFromPresence",value:function(e){var t=this.presMap.nodes.filter(function(t){return e!==t.tagName});this.presMap.nodes=t}},{key:"addPresenceListener",value:function(e,t){if("function"!=typeof t)throw new Error('"handler" is not a function');var n=this.presHandlers[e];n||(this.presHandlers[e]=n=[]),-1===n.indexOf(t)?n.push(t):S.warn("Trying to add the same handler more than once for: ".concat(e))}},{key:"removePresenceListener",value:function(e,t){var n=this.presHandlers[e],r=n?n.indexOf(t):-1;-1!==r?n.splice(r,1):S.warn("Handler for: ".concat(e," was not registered"))}},{key:"isFocus",value:function(e){var t=this.members[e];return t?t.isFocus:null}},{key:"isModerator",value:function(){return"moderator"===this.role}},{key:"getMemberRole",value:function(e){return this.members[e]?this.members[e].role:null}},{key:"setVideoMute",value:function(e,t){this.sendVideoInfoPresence(e),t&&t(e)}},{key:"setAudioMute",value:function(e,t){return this.sendAudioInfoPresence(e,t)}},{key:"addAudioInfoToPresence",value:function(e){this.removeFromPresence("audiomuted"),this.addToPresence("audiomuted",{attributes:{xmlns:"http://jitsi.org/jitmeet/audio"},value:e.toString()})}},{key:"sendAudioInfoPresence",value:function(e,t){this.addAudioInfoToPresence(e),this.connection&&this.sendPresence(),t&&t()}},{key:"addVideoInfoToPresence",value:function(e){this.removeFromPresence("videomuted"),this.addToPresence("videomuted",{attributes:{xmlns:"http://jitsi.org/jitmeet/video"},value:e.toString()})}},{key:"sendVideoInfoPresence",value:function(e){this.addVideoInfoToPresence(e),this.connection&&this.sendPresence()}},{key:"getMediaPresenceInfo",value:function(e,t){var n=this.lastPresences["".concat(this.roomjid,"/").concat(e)];if(!n)return null;var r={muted:!1,videoType:void 0},i=null;if(t===l.a)i=b(n,"audiomuted");else{if(t!==l.b)return S.error("Unsupported media type: ".concat(t)),null;i=b(n,"videomuted");var o=b(n,"videoType");o.length>0&&(r.videoType=o[0].value)}return r.muted=i.length>0&&"true"===i[0].value,r}},{key:"isSIPCallingSupported",value:function(){return!!this.moderator&&this.moderator.isSipGatewayEnabled()}},{key:"dial",value:function(e){return this.connection.rayo.dial(e,"fromnumber",i.Strophe.getBareJidFromJid(this.myroomjid),this.password,this.focusMucJid)}},{key:"hangup",value:function(){return this.connection.rayo.hangup()}},{key:"getPhoneNumber",value:function(){return this.phoneNumber}},{key:"getPhonePin",value:function(){return this.phonePin}},{key:"muteParticipant",value:function(e,t){S.info("set mute",t);var n=Object(i.$iq)({to:this.focusMucJid,type:"set"}).c("mute",{xmlns:"http://jitsi.org/jitmeet/audio",jid:e}).t(t.toString()).up();this.connection.sendIQ(n,function(e){return S.log("set mute",e)},function(e){return S.log("set mute error",e)})}},{key:"onMute",value:function(e){if(e.getAttribute("from")===this.focusMucJid){var t=$(e).find("mute");t.length&&"true"===t.text()?this.eventEmitter.emit(p.a.AUDIO_MUTED_BY_FOCUS,t.attr("actor")):S.warn("Ignoring a mute request which does not explicitly specify a positive mute command.")}else S.warn("Ignored mute from non focus peer")}},{key:"leave",value:function(){var e=this;return new Promise(function(t,n){var r=setTimeout(function(){return o(!0)},5e3),i=e.eventEmitter;function o(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];i.removeListener(p.a.MUC_LEFT,o),clearTimeout(r),e?n(new Error("The timeout for the confirmation about leaving the room expired.")):t()}i.on(p.a.MUC_LEFT,o),e.doLeave()})}}])&&m(n.prototype,r),t})()}).call(this,"modules/xmpp/ChatRoom.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return d});var r=n(1),i=n(3),o=n(25),a=n(4).getLogger(e),s=n(0),c=n(56),u=n(11);function l(e){var t=1;return function(n){if(!n){var r=Math.pow(2,t-1);return t+=1,r*e}t=1}}function d(e,t,n,r){function i(e){if(e.data&&e.data.sessionId){if(e.origin!==window.location.origin)return void a.warn("Ignoring sessionId from different origin: ".concat(e.origin));o.a.sessionId=e.data.sessionId}}this.roomName=e,this.xmppService=t,this.getNextTimeout=l(1e3),this.getNextErrorTimeout=l(1e3),this.externalAuthEnabled=!1,this.options=r,this.sipGatewayEnabled=this.options.connection.hosts&&void 0!==this.options.connection.hosts.call_control,this.eventEmitter=n,this.connection=this.xmppService.connection,window.addEventListener?window.addEventListener("message",i,!1):window.attachEvent("onmessage",i)}d.prototype.isExternalAuthEnabled=function(){return this.externalAuthEnabled},d.prototype.isSipGatewayEnabled=function(){return this.sipGatewayEnabled},d.prototype.onMucMemberLeft=function(e){a.info("Someone left is it focus ? ".concat(e)),"focus"===r.Strophe.getResourceFromJid(e)&&(a.info("Focus has left the room - leaving conference"),this.eventEmitter.emit(s.FOCUS_LEFT))},d.prototype.setFocusUserJid=function(e){this.focusUserJid||(this.focusUserJid=e,a.info("Focus jid set to: ".concat(this.focusUserJid)))},d.prototype.getFocusUserJid=function(){return this.focusUserJid},d.prototype.getFocusComponent=function(){var e=this.options.connection.hosts.focus;return e||(e="focus.".concat(this.options.connection.hosts.domain)),e},d.prototype.createConferenceIq=function(){var e,t=Object(r.$iq)({to:this.getFocusComponent(),type:"set"}),n=o.a.sessionId,s=o.a.machineId,c=this.options.conference;switch(a.info("Session ID: ".concat(n," machine UID: ").concat(s)),t.c("conference",{xmlns:"http://jitsi.org/protocol/focus",room:this.roomName,"machine-uid":s}),n&&t.attrs({"session-id":n}),void 0!==this.options.connection.enforcedBridge&&t.c("property",{name:"enforcedBridge",value:this.options.connection.enforcedBridge}).up(),void 0!==this.options.connection.hosts&&void 0!==this.options.connection.hosts.call_control&&t.c("property",{name:"call_control",value:this.options.connection.hosts.call_control}).up(),void 0!==c.channelLastN&&t.c("property",{name:"channelLastN",value:c.channelLastN}).up(),t.c("property",{name:"disableRtx",value:Boolean(c.disableRtx)}).up(),void 0!==c.enableTcc&&t.c("property",{name:"enableTcc",value:Boolean(c.enableTcc)}).up(),void 0!==c.enableRemb&&t.c("property",{name:"enableRemb",value:Boolean(c.enableRemb)}).up(),void 0!==c.minParticipants&&t.c("property",{name:"minParticipants",value:c.minParticipants}).up(),t.c("property",{name:"enableLipSync",value:!1!==this.options.connection.enableLipSync}).up(),void 0!==c.audioPacketDelay&&t.c("property",{name:"audioPacketDelay",value:c.audioPacketDelay}).up(),c.startBitrate&&t.c("property",{name:"startBitrate",value:c.startBitrate}).up(),c.minBitrate&&t.c("property",{name:"minBitrate",value:c.minBitrate}).up(),c.testing&&c.testing.octo&&"number"==typeof c.testing.octo.probability&&Math.random()conference>property[name='authentication'][value='true']").length>0;a.info("Authentication enabled: ".concat(t)),this.externalAuthEnabled=$(e).find(">conference>property[name='externalAuth'][value='true']").length>0,a.info("External authentication enabled: ".concat(this.externalAuthEnabled)),this.externalAuthEnabled||this.parseSessionId(e);var n=$(e).find(">conference").attr("identity");this.eventEmitter.emit(c.IDENTITY_UPDATED,t,n),$(e).find(">conference>property[name='sipGatewayEnabled'][value='true']").length&&(this.sipGatewayEnabled=!0),a.info("Sip gateway enabled: ".concat(this.sipGatewayEnabled))},d.prototype.allocateConferenceFocus=function(){var e=this;return new Promise(function(t){e.setFocusUserJid(e.options.connection.focusUserJid),e.connection.sendIQ(e.createConferenceIq(),function(n){return e._allocateConferenceFocusSuccess(n,t)},function(n){return e._allocateConferenceFocusError(n,t)}),e.connection.flush()})},d.prototype._allocateConferenceFocusError=function(e,t){var n=this,i=$(e).find(">error>session-invalid").length||$(e).find(">error>not-acceptable").length;if(i&&(a.info("Session expired! - removing"),o.a.sessionId=void 0),$(e).find(">error>graceful-shutdown").length)this.eventEmitter.emit(s.GRACEFUL_SHUTDOWN);else{var c=$(e).find(">error>reservation-error");if(c.length){var l,d=c.attr("error-code"),p=$(e).find(">error>text");return p&&(l=p.text()),void this.eventEmitter.emit(s.RESERVATION_ERROR,d,l)}if($(e).find(">error>not-authorized").length)return a.warn("Unauthorized to start the conference",e),r.Strophe.getDomainFromJid(e.getAttribute("to"))!==this.options.connection.hosts.anonymousdomain&&(this.externalAuthEnabled=!0),void this.eventEmitter.emit(s.AUTHENTICATION_REQUIRED);var f=this.getNextErrorTimeout(),h="Focus error, retry after ".concat(f);u.callErrorHandler(new Error(h)),a.error(h,e);var m=this.getFocusComponent(),v=f/1e3;i||this.eventEmitter.emit(s.FOCUS_DISCONNECTED,m,v),this.getNextTimeout(!0),window.setTimeout(function(){return n.allocateConferenceFocus().then(t)},f)}},d.prototype._allocateConferenceFocusSuccess=function(e,t){var n=this;if(this.parseConfigOptions(e),this.getNextErrorTimeout(!0),"true"===$(e).find("conference").attr("ready"))this.getNextTimeout(!0),t();else{var r=this.getNextTimeout();a.info("Waiting for the focus... ".concat(r)),window.setTimeout(function(){return n.allocateConferenceFocus().then(t)},r)}},d.prototype.authenticate=function(){var e=this;return new Promise(function(t,n){e.connection.sendIQ(e.createConferenceIq(),function(n){e.parseSessionId(n),t()},function(e){return n({error:$(e).find("iq>error :first").prop("tagName"),message:$(e).find("iq>error>text").text()})})})},d.prototype.getLoginUrl=function(e,t){this._getLoginUrl(!1,e,t)},d.prototype._getLoginUrl=function(e,t,n){var i=Object(r.$iq)({to:this.getFocusComponent(),type:"get"}),s={xmlns:"http://jitsi.org/protocol/focus",room:this.roomName,"machine-uid":o.a.machineId},c="auth url";function l(e,t){u.callErrorHandler(new Error(e)),a.error(e,t),n(t)}e&&(s.popup=!0,c="POPUP ".concat(c)),i.c("login-url",s),this.connection.sendIQ(i,function(e){var n=$(e).find("login-url").attr("url");(n=decodeURIComponent(n))?(a.info("Got ".concat(c,": ").concat(n)),t(n)):l("Failed to get ".concat(c," from the focus"),e)},l.bind(void 0,"Get ".concat(c," error")))},d.prototype.getPopupLoginUrl=function(e,t){this._getLoginUrl(!0,e,t)},d.prototype.logout=function(e){var t=Object(r.$iq)({to:this.getFocusComponent(),type:"set"}),n=o.a.sessionId;n?(t.c("logout",{xmlns:"http://jitsi.org/protocol/focus","session-id":n}),this.connection.sendIQ(t,function(t){var n=$(t).find("logout").attr("logout-url");n&&(n=decodeURIComponent(n)),a.info("Log out OK, url: ".concat(n),t),o.a.sessionId=void 0,e(n)},function(e){u.callErrorHandler(new Error("Logout error")),a.error("Logout error",e)})):e()}}).call(this,"modules/xmpp/moderator.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return T});var r=n(7),i=n(4),o=n(1),a=n(0),s=n.n(a),c=n(11),u=n.n(c),l=n(33),d=n.n(l),p=n(5),f=n(44),h=n(34);function m(e){return(m="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function v(e,t){for(var n=0;nstartmuted");if(h&&h.length>0){var m=h.attr("audio"),v=h.attr("video");this.eventEmitter.emit(s.a.START_MUTED_FROM_FOCUS,"true"===m,"true"===v)}b.info("Marking session from ".concat(i," as ").concat(d?"":"*not*"," P2P")),c=new f.a($(e).find("jingle").attr("sid"),$(e).attr("to"),i,this.connection,this.mediaConstraints,d?this.p2pIceConfig:this.jvbIceConfig,d,!1),this.sessions[c.sid]=c,this.eventEmitter.emit(s.a.CALL_INCOMING,c,$(e).find(">jingle"),l);break;case"session-accept":this.eventEmitter.emit(s.a.CALL_ACCEPTED,c,$(e).find(">jingle"));break;case"content-modify":c.modifyContents($(e).find(">jingle"));break;case"transport-info":this.eventEmitter.emit(s.a.TRANSPORT_INFO,c,$(e).find(">jingle"));break;case"session-terminate":b.log("terminating...",c.sid);var y=null,g=null;$(e).find(">jingle>reason").length&&(y=$(e).find(">jingle>reason>:first")[0].tagName,g=$(e).find(">jingle>reason>text").text()),this.terminate(c.sid,y,g),this.eventEmitter.emit(s.a.CALL_ENDED,c,y,g);break;case"transport-replace":b.info("(TIME) Start transport replace",l),p.a.sendAnalytics(Object(r.F)(r.f,{p2p:d,value:l})),c.replaceTransport($(e).find(">jingle"),function(){var e=window.performance.now();b.info("(TIME) Transport replace success!",e),p.a.sendAnalytics(Object(r.F)(r.g,{p2p:d,value:e}))},function(e){u.a.callErrorHandler(e),b.error("Transport replace failed",e),c.sendTransportReject()});break;case"addsource":case"source-add":c.addRemoteStream($(e).find(">jingle>content"));break;case"removesource":case"source-remove":c.removeRemoteStream($(e).find(">jingle>content"));break;default:b.warn("jingle action not implemented",n),a.attrs({type:"error"}),a.c("error",{type:"cancel"}).c("bad-request",{xmlns:"urn:ietf:params:xml:ns:xmpp-stanzas"}).up()}return this.connection.send(a),!0}},{key:"newP2PJingleSession",value:function(e,t){var n=new f.a(d.a.randomHexString(12),e,t,this.connection,this.mediaConstraints,this.p2pIceConfig,!0,!0);return this.sessions[n.sid]=n,n}},{key:"terminate",value:function(e,t,n){this.sessions.hasOwnProperty(e)&&("ended"!==this.sessions[e].state&&this.sessions[e].onTerminated(t,n),delete this.sessions[e])}},{key:"getStunAndTurnCredentials",value:function(){var e=this;this.connection.sendIQ(Object(o.$iq)({type:"get",to:this.connection.domain}).c("services",{xmlns:"urn:xmpp:extdisco:1"}),function(t){var n=[];$(t).find(">services>service").each(function(e,t){var r={},i=(t=$(t)).attr("type");switch(i){case"stun":r.url="stun:".concat(t.attr("host")),t.attr("port")&&(r.url+=":".concat(t.attr("port"))),n.push(r);break;case"turn":case"turns":r.url="".concat(i,":");var o=t.attr("username");if(o){var a=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);a&&parseInt(a[2],10)<28?r.url+="".concat(o,"@"):r.username=o}r.url+=t.attr("host"),t.attr("port")&&(r.url+=":".concat(t.attr("port")));var s=t.attr("transport");s&&"udp"!==s&&(r.url+="?transport=".concat(s)),r.credential=t.attr("password")||r.credential,n.push(r)}});var r=e.xmpp.options;r.useStunTurn&&(e.jvbIceConfig.iceServers=n.filter(function(e){return e.url.startsWith("turns")})),r.p2p&&r.p2p.useStunTurn&&(e.p2pIceConfig.iceServers=n)},function(e){b.warn("getting turn credentials failed",e),b.warn("is mod_turncredentials or similar installed?")})}},{key:"getLog",value:function(){var e=this,t={};return Object.keys(this.sessions).forEach(function(n){var r=e.sessions[n].peerconnection;r&&r.updateLog&&(t["jingle_".concat(n)]={updateLog:r.updateLog,stats:r.stats,url:window.location.href})}),t}}])&&v(n.prototype,i),t})();function T(e,t,n){o.Strophe.addConnectionPlugin("jingle",new E(e,t,n))}}).call(this,"modules/xmpp/strophe.jingle.js")},function(e,t){e.exports={integerHash:function(e){if(!e)return 0;var t,n=0;for(t=0;t=e.length&&n()}d(e,function(e){t(e,c(i))})},s.forEach=s.each,s.eachSeries=function(e,t,n){if(n=n||function(){},!e.length)return n();var r=0;!(function i(){t(e[r],function(t){t?(n(t),n=function(){}):(r+=1)>=e.length?n():i()})})()},s.forEachSeries=s.eachSeries,s.eachLimit=function(e,t,n,r){h(t).apply(null,[e,n,r])},s.forEachLimit=s.eachLimit;var h=function(e){return function(t,n,r){if(r=r||function(){},!t.length||e<=0)return r();var i=0,o=0,a=0;!(function s(){if(i>=t.length)return r();for(;a=t.length?r():s())})})()}},m=function(e){return function(){var t=Array.prototype.slice.call(arguments);return e.apply(null,[s.each].concat(t))}},v=function(e){return function(){var t=Array.prototype.slice.call(arguments);return e.apply(null,[s.eachSeries].concat(t))}},y=function(e,t,n,r){if(t=p(t,function(e,t){return{index:t,value:e}}),r){var i=[];e(t,function(e,t){n(e.value,function(n,r){i[e.index]=r,t(n)})},function(e){r(e,i)})}else e(t,function(e,t){n(e.value,function(e){t(e)})})};s.map=m(y),s.mapSeries=v(y),s.mapLimit=function(e,t,n,r){return g(t)(e,n,r)};var g=function(e){return(function(e,t){return function(){var n=Array.prototype.slice.call(arguments);return t.apply(null,[h(e)].concat(n))}})(e,y)};s.reduce=function(e,t,n,r){s.eachSeries(e,function(e,r){n(t,e,function(e,n){t=n,r(e)})},function(e){r(e,t)})},s.inject=s.reduce,s.foldl=s.reduce,s.reduceRight=function(e,t,n,r){var i=p(e,function(e){return e}).reverse();s.reduce(i,t,n,r)},s.foldr=s.reduceRight;var S=function(e,t,n,r){var i=[];e(t=p(t,function(e,t){return{index:t,value:e}}),function(e,t){n(e.value,function(n){n&&i.push(e),t()})},function(e){r(p(i.sort(function(e,t){return e.index-t.index}),function(e){return e.value}))})};s.filter=m(S),s.filterSeries=v(S),s.select=s.filter,s.selectSeries=s.filterSeries;var _=function(e,t,n,r){var i=[];e(t=p(t,function(e,t){return{index:t,value:e}}),function(e,t){n(e.value,function(n){n||i.push(e),t()})},function(e){r(p(i.sort(function(e,t){return e.index-t.index}),function(e){return e.value}))})};s.reject=m(_),s.rejectSeries=v(_);var b=function(e,t,n,r){e(t,function(e,t){n(e,function(n){n?(r(e),r=function(){}):t()})},function(e){r()})};s.detect=m(b),s.detectSeries=v(b),s.some=function(e,t,n){s.each(e,function(e,r){t(e,function(e){e&&(n(!0),n=function(){}),r()})},function(e){n(!1)})},s.any=s.some,s.every=function(e,t,n){s.each(e,function(e,r){t(e,function(e){e||(n(!1),n=function(){}),r()})},function(e){n(!0)})},s.all=s.every,s.sortBy=function(e,t,n){s.map(e,function(e,n){t(e,function(t,r){t?n(t):n(null,{value:e,criteria:r})})},function(e,t){if(e)return n(e);n(null,p(t.sort(function(e,t){var n=e.criteria,r=t.criteria;return nr?1:0}),function(e){return e.value}))})},s.auto=function(e,t){t=t||function(){};var n=f(e),r=n.length;if(!r)return t();var i={},o=[],a=function(e){o.unshift(e)},c=function(){r--,d(o.slice(0),function(e){e()})};a(function(){if(!r){var e=t;t=function(){},e(null,i)}}),d(n,function(n){var r=l(e[n])?e[n]:[e[n]],u=function(e){var r=Array.prototype.slice.call(arguments,1);if(r.length<=1&&(r=r[0]),e){var o={};d(f(i),function(e){o[e]=i[e]}),o[n]=r,t(e,o),t=function(){}}else i[n]=r,s.setImmediate(c)},p=r.slice(0,Math.abs(r.length-1))||[],h=function(){return t=function(e,t){return e&&i.hasOwnProperty(t)},r=!0,((e=p).reduce?e.reduce(t,r):(d(e,function(e,n,i){r=t(r,e)}),r))&&!i.hasOwnProperty(n);var e,t,r};h()?r[r.length-1](u,i):a(function e(){h()&&((function(e){for(var t=0;t>>1);n(t,e[o])>=0?r=o:i=o-1}return r})(e.tasks,o,n)+1,0,o),e.saturated&&e.tasks.length===e.concurrency&&e.saturated(),s.setImmediate(e.process)})})(r,e,t,i)},delete r.unshift,r},s.cargo=function(e,t){var n=!1,r=[],i={tasks:r,payload:t,saturated:null,empty:null,drain:null,drained:!0,push:function(e,n){l(e)||(e=[e]),d(e,function(e){r.push({data:e,callback:"function"==typeof n?n:null}),i.drained=!1,i.saturated&&r.length===t&&i.saturated()}),s.setImmediate(i.process)},process:function o(){if(!n){if(0===r.length)return i.drain&&!i.drained&&i.drain(),void(i.drained=!0);var a="number"==typeof t?r.splice(0,t):r.splice(0,r.length),s=p(a,function(e){return e.data});i.empty&&i.empty(),n=!0,e(s,function(){n=!1;var e=arguments;d(a,function(t){t.callback&&t.callback.apply(null,e)}),o()})}},length:function(){return r.length},running:function(){return n}};return i};var C=function(e){return function(t){var n=Array.prototype.slice.call(arguments,1);t.apply(null,n.concat([function(t){var n=Array.prototype.slice.call(arguments,1);"undefined"!=typeof console&&(t?console.error&&console.error(t):console[e]&&d(n,function(t){console[e](t)}))}]))}};s.log=C("log"),s.dir=C("dir"),s.memoize=function(e,t){var n={},r={};t=t||function(e){return e};var i=function(){var i=Array.prototype.slice.call(arguments),o=i.pop(),a=t.apply(null,i);a in n?s.nextTick(function(){o.apply(null,n[a])}):a in r?r[a].push(o):(r[a]=[o],e.apply(null,i.concat([function(){n[a]=arguments;var e=r[a];delete r[a];for(var t=0,i=e.length;t2){var r=Array.prototype.slice.call(arguments,2);return n.apply(this,r)}return n};s.applyEach=m(R),s.applyEachSeries=v(R),s.forever=function(e,t){!(function n(r){if(r){if(t)return t(r);throw r}e(n)})()},e.exports?e.exports=s:void 0===(i=function(){return s}.apply(t,[]))||(e.exports=i)})()}).call(this,n(48),n(127).setImmediate)},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(1),o=n(11),a=n.n(o),s=Object(r.getLogger)(e),c=-1,u=/request id \d+.\d+ got 200/,l=/request errored, status: (\d+), number of errors: \d+/;t.a=function(){i.Strophe.log=function(e,t){switch(s.trace("Strophe",e,t),"string"==typeof t&&-1!==t.indexOf("Request ")&&-1!==t.indexOf("timed out (secondary), restarting")&&(e=i.Strophe.LogLevel.WARN),e){case i.Strophe.LogLevel.DEBUG:-1!==c&&u.test(t)&&(s.debug("Reset lastErrorStatus"),c=-1);break;case i.Strophe.LogLevel.WARN:s.warn("Strophe: ".concat(t));var n=l.exec(t);n&&2===n.length&&(c=parseInt(n[1],10),s.debug("lastErrorStatus set to: ".concat(c)));break;case i.Strophe.LogLevel.ERROR:case i.Strophe.LogLevel.FATAL:t="Strophe: ".concat(t),a.a.callErrorHandler(new Error(t)),s.error(t)}},i.Strophe.getLastErrorStatus=function(){return c},i.Strophe.getStatusString=function(e){switch(e){case i.Strophe.Status.ERROR:return"ERROR";case i.Strophe.Status.CONNECTING:return"CONNECTING";case i.Strophe.Status.CONNFAIL:return"CONNFAIL";case i.Strophe.Status.AUTHENTICATING:return"AUTHENTICATING";case i.Strophe.Status.AUTHFAIL:return"AUTHFAIL";case i.Strophe.Status.CONNECTED:return"CONNECTED";case i.Strophe.Status.DISCONNECTED:return"DISCONNECTED";case i.Strophe.Status.DISCONNECTING:return"DISCONNECTING";case i.Strophe.Status.ATTACHED:return"ATTACHED";default:return"unknown"}}}}).call(this,"modules/xmpp/strophe.util.js")},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(1),o=n(11),a=n.n(o),s=n(34);function c(e){return(c="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function u(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:1e4;if(this.intervalId){var r="Ping task scheduled already";return a.a.callErrorHandler(new Error(r)),void h.error(r)}this.intervalId=window.setInterval(function(){t.ping(e,function(){t.failedPings=0},function(e){t.failedPings+=1;var n="Ping ".concat(e?"error":"timeout");t.failedPings>=3?(a.a.callErrorHandler(new Error(n)),h.error(n,e)):h.warn(n,e)},15e3)},n),h.info("XMPP pings will be sent every ".concat(n," ms"))}},{key:"stopInterval",value:function(){this.intervalId&&(window.clearInterval(this.intervalId),this.intervalId=null,this.failedPings=0,h.info("Ping interval cleared"))}},{key:"_addPingExecutionTimestamp",value:function(){this.pingExecIntervals.push((new Date).getTime()),this.pingExecIntervals.length>m&&this.pingExecIntervals.shift()}},{key:"getPingSuspendTime",value:function(){var e=this.pingExecIntervals.slice();e.push((new Date).getTime());var t=0,n=e[0];return e.forEach(function(e){var r=e-n;r>t&&(t=r),n=e}),t-=1e4,Math.max(t,0)}}])&&u(n.prototype,r),t})();t.a=function(e){i.Strophe.addConnectionPlugin("ping",new v(e))}}).call(this,"modules/xmpp/strophe.ping.js")},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(1),o=n(34);function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}function s(e,t){for(var n=0;nt[r]?1:e[r]0&&void 0!==arguments[0]?arguments[0]:{},a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"http://jitsi.org/jitsimeet";if((function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),this,n=u(t).call(this),(e=!n||"object"!==s(n)&&"function"!=typeof n?d(this):n).node=a,e.disco=i.disco,!e.disco)throw new Error("Missing strophe-plugins (disco plugin is required)!");e.versionToCapabilities=Object.create(null),e.jidToVersion=Object.create(null),e.version="",e.rooms=new Set;var c=i.emuc;return c.addListener(o.a.EMUC_ROOM_ADDED,function(t){return e._addChatRoom(t)}),c.addListener(o.a.EMUC_ROOM_REMOVED,function(t){return e._removeChatRoom(t)}),Object.keys(c.rooms).forEach(function(t){e._addChatRoom(c.rooms[t])}),r.Strophe.addNamespace("CAPS","http://jabber.org/protocol/caps"),e.disco.addFeature(r.Strophe.NS.CAPS),i.addHandler(e._handleCaps.bind(d(d(e))),r.Strophe.NS.CAPS),e._onMucMemberLeft=e._removeJidToVersionEntry.bind(d(d(e))),e}var n,i;return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)})(t,a.a),n=t,(i=[{key:"addFeature",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.disco.addFeature(e),this._generateVersion(),t&&this.submit()}},{key:"removeFeature",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.disco.removeFeature(e),this._generateVersion(),t&&this.submit()}},{key:"submit",value:function(){this.rooms.forEach(function(e){return e.sendPresence()})}},{key:"getFeatures",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:5e3,r=e in this.jidToVersion?this.jidToVersion[e]:null;if(!(r&&r.version in this.versionToCapabilities)){var i=r?"".concat(r.node,"#").concat(r.version):null;return this._getDiscoInfo(e,i,n).then(function(e){var n=e.features,o=e.identities;if(r){var a=v(Array.from(o),Array.from(n)),s="".concat(r.node,"#").concat(a);return s===i?(t.versionToCapabilities[s]=n,n):t.versionToCapabilities[s]?t.versionToCapabilities[s]:(p.error("Expected node ".concat(i," but received ").concat(s)),Promise.reject("Feature version mismatch"))}})}return Promise.resolve(this.versionToCapabilities[r.version])}},{key:"getFeaturesAndIdentities",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:5e3;return this._getDiscoInfo(e,null,t)}},{key:"_getDiscoInfo",value:function(e,t,n){var r=this;return new Promise(function(i,o){return r.disco.info(e,t,function(e){var t=new Set,n=new Set;$(e).find(">query>feature").each(function(e,n){return t.add(n.getAttribute("var"))}),$(e).find(">query>identity").each(function(e,t){return n.add({type:t.getAttribute("type"),name:t.getAttribute("name"),category:t.getAttribute("category")})}),i({features:t,identities:n})},o,n)})}},{key:"_addChatRoom",value:function(e){this.rooms.add(e),e.addListener(o.a.MUC_MEMBER_LEFT,this._onMucMemberLeft),this._fixChatRoomPresenceMap(e)}},{key:"_removeChatRoom",value:function(e){this.rooms.delete(e),e.removeListener(o.a.MUC_MEMBER_LEFT,this._onMucMemberLeft)}},{key:"_fixChatRoomPresenceMap",value:function(e){e.addToPresence("c",{attributes:{xmlns:r.Strophe.NS.CAPS,hash:"sha-1",node:this.node,ver:this.version}})}},{key:"_notifyVersionChanged",value:function(){var e=this;this.rooms.forEach(function(t){return e._fixChatRoomPresenceMap(t)})}},{key:"_generateVersion",value:function(){this.version=v(this.disco._identities,this.disco._features),this._notifyVersionChanged()}},{key:"_handleCaps",value:function(e){var t=e.getAttribute("from"),n=e.querySelector("c"),r=n.getAttribute("ver"),i=n.getAttribute("node"),a=this.jidToVersion[t];return this.jidToVersion[t]={version:r,node:i},a&&a.version!==r&&this.eventEmitter.emit(o.a.PARTCIPANT_FEATURES_CHANGED,t),!0}},{key:"_removeJidToVersionEntry",value:function(e){e in this.jidToVersion&&delete this.jidToVersion[e]}}])&&c(n.prototype,i),t})()}).call(this,"modules/xmpp/Caps.js")},function(e,t,n){(function(t){var r=n(4).getLogger(t);function i(e,t){var n=e.getTrack();if(!n)throw new Error("Failed to initialize DTMFSender: no audio track.");this.dtmfSender=t.peerconnection.createDTMFSender(n),r.debug("Initialized DTMFSender")}i.prototype.sendTones=function(e,t,n){this.dtmfSender.insertDTMF(e,t||200,n||200)},e.exports=i}).call(this,"modules/DTMF/JitsiDTMFManager.js")},function(e,t,n){"use strict";n.d(t,"a",function(){return s});var r=n(2),i=n(8),o=n.n(i);function a(e,t){for(var n=0;nthis.maxstats&&(i.values.shift(),i.times.shift()),i.endTime=o};var w=function(e){return null==e?"":"type: ".concat(e.type,"\r\n").concat(e.sdp)};A.prototype.getConnectionState=function(){var e=this.peerconnection.iceConnectionState;return"completed"===e?"connected":e},A.prototype._getDesiredMediaDirection=function(e){var t=!0;return e===c.a?t=this.audioTransferActive:e===c.b&&(t=this.videoTransferActive),t?this.hasAnyTracksOfType(e)?"sendrecv":"recvonly":"inactive"},A.prototype.isSimulcastOn=function(){return!this.options.disableSimulcast&&p.a.supportsSimulcast()&&(!p.a.isFirefox()||this.options.enableFirefoxSimulcast)},A.prototype._peerVideoTypeChanged=function(e,t){if(e){var n=this.getRemoteTracks(e,c.b);n.length&&n[0]._setVideoType(t)}else T.error("No endpointID on peerVideoTypeChanged ".concat(this))},A.prototype._peerMutedChanged=function(e,t,n){if(e){var r=this.getRemoteTracks(e,t);r.length&&r[0].setMute(n)}else T.error("On peerMuteChanged - no endpoint ID")},A.prototype.getLocalTracks=function(e){var t=Array.from(this.localTracks.values());return void 0!==e&&(t=t.filter(function(t){return t.getType()===e})),t},A.prototype.hasAnyTracksOfType=function(e){if(!e)throw new Error('"mediaType" is required');return this.getLocalTracks(e).length>0},A.prototype.getRemoteTracks=function(e,t){var n=[],r=e?[e]:this.remoteTracks.keys(),i=!0,o=!1,a=void 0;try{for(var s,c=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value,l=this.remoteTracks.get(u);if(l){var d=!0,p=!1,f=void 0;try{for(var h,m=l.keys()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(h=m.next()).done);d=!0){var v=h.value;if(!t||t===v){var y=l.get(v);y&&n.push(y)}}}catch(e){p=!0,f=e}finally{try{d||null==m.return||m.return()}finally{if(p)throw f}}}}}catch(e){o=!0,a=e}finally{try{i||null==c.return||c.return()}finally{if(o)throw a}}return n},A.prototype.getTrackBySSRC=function(e){if("number"!=typeof e)throw new Error("SSRC ".concat(e," is not a number"));var t=!0,n=!1,r=void 0;try{for(var i,o=this.localTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;if(this.getLocalSSRC(a)===e)return a}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}var s=!0,c=!1,u=void 0;try{for(var l,d=this.getRemoteTracks()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(s=(l=d.next()).done);s=!0){var p=l.value;if(p.getSSRC()===e)return p}}catch(e){c=!0,u=e}finally{try{s||null==d.return||d.return()}finally{if(c)throw u}}return null},A.prototype.getSsrcByTrackId=function(e){var t=function(t){return t.getTrack().id===e},n=this.getLocalTracks().find(t);if(n)return this.getLocalSSRC(n);var r=this.getRemoteTracks().find(t);return r?r.getSSRC():null},A.prototype._remoteStreamAdded=function(e){var t=this,n=l.a.getStreamID(e);if(l.a.isUserStreamById(n)){(p.a.isChromiumBased()||p.a.isEdge())&&(e.onaddtrack=function(n){t._remoteTrackAdded(e,n.track)},e.onremovetrack=function(n){t._remoteTrackRemoved(e,n.track)});var r=e.getAudioTracks(),i=!0,o=!1,a=void 0;try{for(var s,c=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value;this._remoteTrackAdded(e,u)}}catch(e){o=!0,a=e}finally{try{i||null==c.return||c.return()}finally{if(o)throw a}}var d=e.getVideoTracks(),f=!0,h=!1,m=void 0;try{for(var v,y=d["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(f=(v=y.next()).done);f=!0){var g=v.value;this._remoteTrackAdded(e,g)}}catch(e){h=!0,m=e}finally{try{f||null==y.return||y.return()}finally{if(h)throw m}}}else T.info("".concat(this," ignored remote 'stream added' event for non-user stream")+"id: ".concat(n))},A.prototype._remoteTrackAdded=function(e,t){var n=l.a.getStreamID(e),r=t.kind;if(T.info("".concat(this," remote track added:"),n,r),r){var i=new v.a(this.remoteDescription.sdp).media.filter(function(e){return e.startsWith("m=".concat(r))});if(i.length){var o=S.a.findLines(i[0],"a=ssrc:");if((o=o.filter(function(e){return-1!==e.indexOf("msid:".concat(n))})).length){var s=o[0].substring(7).split(" ")[0],c=Number(s),u=this.signalingLayer.getSSRCOwner(c);if(isNaN(c)||c<0)a.callErrorHandler(new Error("Invalid SSRC: ".concat(s," for remote track, msid: ").concat(n," media type: ").concat(r)));else if(u){T.log("".concat(this," associated ssrc"),u,c);var d=this.signalingLayer.getPeerMediaInfo(u,r);if(d){var p=d.muted,f=d.videoType;this._createRemoteTrack(u,e,t,r,f,c,p)}else a.callErrorHandler(new Error("".concat(this,": no peer media info available for ").concat(u)))}else a.callErrorHandler(new Error("No SSRC owner known for: ".concat(c," for remote track, msid: ").concat(n," media type: ").concat(r)))}else a.callErrorHandler(new Error("No SSRC lines for streamId ".concat(n," for remote track, media type: ").concat(r)))}else a.callErrorHandler(new Error("No media lines for type ".concat(r," found in remote SDP for remote track: ").concat(n)))}else a.callErrorHandler(new Error("MediaType undefined for remote track, stream id: ".concat(n)))},A.prototype._createRemoteTrack=function(e,t,n,r,i,o,a){var c=this.remoteTracks.get(e);c||(c=new Map,this.remoteTracks.set(e,c));var u=c.get(r);if(u&&u.getTrack()===n)T.info("".concat(this," ignored duplicated remote track added event for: ")+"".concat(e,", ").concat(r));else{u&&T.error("".concat(this," overwriting remote track for")+"".concat(e," ").concat(r));var l=new s.a(this.rtc,this.rtc.conference,e,t,n,r,i,o,a,this.isP2P);c.set(r,l),this.eventEmitter.emit(h.a.REMOTE_TRACK_ADDED,l)}},A.prototype._remoteStreamRemoved=function(e){if(l.a.isUserStream(e)){var t=e.getVideoTracks(),n=!0,r=!1,i=void 0;try{for(var o,a=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;this._remoteTrackRemoved(e,s)}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}var c=e.getAudioTracks(),u=!0,d=!1,p=void 0;try{for(var f,h=c["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(f=h.next()).done);u=!0){var m=f.value;this._remoteTrackRemoved(e,m)}}catch(e){d=!0,p=e}finally{try{u||null==h.return||h.return()}finally{if(d)throw p}}}else{var v=l.a.getStreamID(e);T.info("Ignored remote 'stream removed' event for non-user stream ".concat(v))}},A.prototype._remoteTrackRemoved=function(e,t){var n=l.a.getStreamID(e),r=t&&l.a.getTrackID(t);T.info("".concat(this," - remote track removed: ").concat(n,", ").concat(r)),n?r?this._removeRemoteTrackById(n,r)||T.warn("".concat(this," Removed track not found for msid: ").concat(n,",\n track id: ").concat(r)):a.callErrorHandler(new Error("".concat(this," remote track removal failed - no track ID"))):a.callErrorHandler(new Error("".concat(this," remote track removal failed - no stream ID")))},A.prototype._getRemoteTrackById=function(e,t){var n=!0,r=!1,i=void 0;try{for(var o,a=this.remoteTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value,c=!0,u=!1,l=void 0;try{for(var d,p=s.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(c=(d=p.next()).done);c=!0){var f=d.value;if(f.getStreamId()==e&&f.getTrackId()==t)return f}}catch(e){u=!0,l=e}finally{try{c||null==p.return||p.return()}finally{if(u)throw l}}}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}},A.prototype.removeRemoteTracks=function(e){var t=[],n=this.remoteTracks.get(e);if(n){var r=n.get(c.a),i=n.get(c.b);r&&t.push(r),i&&t.push(i),this.remoteTracks.delete(e)}return T.debug("".concat(this," removed remote tracks for ").concat(e," count: ").concat(t.length)),t},A.prototype._removeRemoteTrack=function(e){e.dispose();var t=e.getParticipantId(),n=this.remoteTracks.get(t);n?n.delete(e.getType())||T.error("Failed to remove ".concat(e," - type mapping messed up ?")):T.error("removeRemoteTrack: no remote tracks map for ".concat(t)),this.eventEmitter.emit(h.a.REMOTE_TRACK_REMOVED,e)},A.prototype._removeRemoteTrackById=function(e,t){var n=this._getRemoteTrackById(e,t);return n&&this._removeRemoteTrack(n),n};var k=function(e){if("object"!==E(e)||null===e||"string"!=typeof e.sdp)return T.warn("An empty description was passed as an argument."),e;var t=n(22),r=t.parse(e.sdp);void 0!==r&&void 0!==r.media&&Array.isArray(r.media)&&r.media.forEach(function(e){var t=[],n=[];if(void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){void 0!==e.semantics&&"FID"===e.semantics&&void 0!==e.ssrcs&&t.push(Number(e.ssrcs.split(" ")[0]))}),Array.isArray(e.ssrcs)){var r;for(r=0;r=0&&(n.push(e.ssrcs[r]),delete e.ssrcs[r]);for(r=0;r0&&void 0!==arguments[0]?arguments[0]:[];if(!p.a.isChrome()||!p.a.isVersionGreaterThan(70))return e;var t=b(e);return e.filter(function(e){return"mslabel"===e.attribute&&"-"===e.value}).map(function(e){return e.id}).forEach(function(e){var n=t.find(function(t){return t.id===e&&"cname"===t.attribute});n.value="recvonly-".concat(e),(t=t.filter(function(t){return t.id!==e})).push(n)}),t})(n)}});var i=t.write(r);return new RTCSessionDescription({type:e.type,sdp:i})};A.prototype.getLocalSSRC=function(e){var t=this._getSSRC(e.rtcId);return t&&t.ssrcs[0]},A.prototype._injectSsrcGroupForUnifiedSimulcast=function(e){var t=o.a.parse(e.sdp),n=t.media.find(function(e){return"video"===e.type});if(n.simulcast_03){var r=[];if(n.ssrcs.forEach(function(e){"msid"===e.attribute&&r.push(e.id)}),n.ssrcGroups=n.ssrcGroups||[],n.ssrcGroups.find(function(e){return"SIM"===e.semantics}))return e;n.ssrcGroups.push({semantics:"SIM",ssrcs:r.join(" ")})}return new RTCSessionDescription({type:e.type,sdp:o.a.write(t)})};var O={signalingState:function(){return this.peerconnection.signalingState},iceConnectionState:function(){return this.peerconnection.iceConnectionState},localDescription:function(){var e=this.peerconnection.localDescription;return e?(this.trace("getLocalDescription::preTransform",w(e)),p.a.usesUnifiedPlan()&&(e=this.interop.toPlanB(e),this.trace("getLocalDescription::postTransform (Plan B)",w(e)),e=this._injectSsrcGroupForUnifiedSimulcast(e),this.trace("getLocalDescription::postTransform (inject ssrc group)",w(e))),p.a.doesVideoMuteByStreamRemove()&&(e=this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(e),T.debug("getLocalDescription::postTransform (munge local SDP)",e)),e=(function(e,t){if(!e)throw new Error("No local description passed in.");var n=new g.a(e.sdp),r=n.selectMedia("audio"),i=!1;r&&"sendrecv"!==r.direction&&(t.startSilent?r.direction="inactive":r.direction="sendrecv",i=!0);var o=n.selectMedia("video");return o&&"sendrecv"!==o.direction&&(o.direction="sendrecv",i=!0),i?new RTCSessionDescription({type:e.type,sdp:n.toRawSDP()}):e})(e,this.options),e=this.localSdpMunger.transformStreamIdentifiers(e)):(T.debug("getLocalDescription no localDescription found"),{})},remoteDescription:function(){var e=this.peerconnection.remoteDescription;return this.trace("getRemoteDescription::preTransform",w(e)),p.a.usesUnifiedPlan()&&(e=this.interop.toPlanB(e),this.trace("getRemoteDescription::postTransform (Plan B)",w(e))),e||{}}};function I(e){return e&&e.groups&&e.groups.length?e.groups[0].ssrcs[0]:e&&e.ssrcs&&e.ssrcs.length?e.ssrcs[0]:null}Object.keys(O).forEach(function(e){Object.defineProperty(A.prototype,e,{get:O[e]})}),A.prototype._getSSRC=function(e){return this.localSSRCs.get(e)},A.prototype.addTrack=function(e){var t=e.rtcId;if(T.info("add ".concat(e," to: ").concat(this)),this.localTracks.has(t))T.error("".concat(e," is already in ").concat(this));else{this.localTracks.set(t,e);var n=e.getOriginalStream();if(n?this._addStream(n):(!p.a.doesVideoMuteByStreamRemove()||e.isAudioTrack()||e.isVideoTrack()&&!e.isMuted())&&T.error("".concat(this," no WebRTC stream for: ").concat(e)),p.a.doesVideoMuteByStreamRemove()&&e.isVideoTrack()&&e.isMuted()){var r=this.generateNewStreamSSRCInfo(e);this.sdpConsistency.setPrimarySsrc(r.ssrcs[0]);var i=r.groups.find(function(e){return"SIM"===e.semantics});i&&this.simulcast.setSsrcCache(i.ssrcs);var o=r.groups.filter(function(e){return"FID"===e.semantics});if(o){var a=new Map;o.forEach(function(e){var t=e.ssrcs[0],n=e.ssrcs[1];a.set(t,n)}),this.rtxModifier.setSsrcCache(a)}}}},A.prototype.addTrackUnmute=function(e){if(!this._assertTrackBelongs("addTrackUnmute",e))return!1;T.info("Adding ".concat(e," as unmute to ").concat(this));var t=e.getOriginalStream();return t?(this._addStream(t),!0):(T.error("Unable to add ".concat(e," as unmute to ").concat(this," - no WebRTC stream")),!1)},A.prototype._addStream=function(e){this.peerconnection.addStream(e),this._addedStreams.push(e)},A.prototype._removeStream=function(e){p.a.supportsRtpSender()?this._handleSenderRemoveStream(e):this.peerconnection.removeStream(e),this._addedStreams=this._addedStreams.filter(function(t){return t!==e})},A.prototype._assertTrackBelongs=function(e,t){var n=this.localTracks.has(t.rtcId);return n||T.error("".concat(e,": ").concat(t," does not belong to ").concat(this)),n},A.prototype.isMediaStreamInPc=function(e){return this._addedStreams.indexOf(e)>-1},A.prototype.removeTrack=function(e){var t=e.getOriginalStream();this.trace("removeStream",e.rtcId,t?t.id:void 0),this._assertTrackBelongs("removeStream",e)&&(this.localTracks.delete(e.rtcId),this.localSSRCs.delete(e.rtcId),t&&(p.a.supportsRtpSender()?this._handleSenderRemoveStream(t):this.peerconnection.removeStream(t)))},A.prototype.findSenderByStream=function(e){var t=e.getTracks()[0];if(t)return this.peerconnection.getSenders().find(function(e){return e.track===t});T.error("Cannot find sender: no tracks.")},A.prototype.replaceTrack=function(e,t){var n=this;if(p.a.supportsRtpSender()&&e&&t){var r=this.findSenderByStream(e.getOriginalStream()),i=t.getOriginalStream();if(r&&i){var o=i.getTracks()[0];if(o)return r.replaceTrack(o,i).then(function(){var r=n.localSSRCs.get(e.rtcId);return n.localTracks.delete(e.rtcId),n.localSSRCs.delete(e.rtcId),n._addedStreams=n._addedStreams.filter(function(e){return e!==i}),n.localTracks.set(t.rtcId,t),t.storedMSID=e.storedMSID,n._addedStreams.push(i),n.localSSRCs.set(t.rtcId,r),n.eventEmitter.emit(h.a.LOCAL_TRACK_SSRC_UPDATED,t,I(r)),!1})}}return e&&this.removeTrack(e),t&&this.addTrack(t),Promise.resolve(!0)},A.prototype.removeTrackMute=function(e){var t=e.getOriginalStream();return this.trace("removeStreamMute",e.rtcId,t?t.id:null),!!this._assertTrackBelongs("removeStreamMute",e)&&(t?(T.info("Removing ".concat(e," as mute from ").concat(this)),this._removeStream(t),!0):(T.error("removeStreamMute - no WebRTC stream for ".concat(e)),!1))},A.prototype._handleSenderRemoveStream=function(e){if(e){var t=this.findSenderByStream(e);t?this.peerconnection.removeTrack(t):T.log("Cannot remove tracks: no RTPSender.")}},A.prototype.createDataChannel=function(e,t){return this.trace("createDataChannel",e,t),this.peerconnection.createDataChannel(e,t)},A.prototype._ensureSimulcastGroupIsLast=function(e){var t=e.sdp,n=t.indexOf("m=video"),r=t.indexOf("a=ssrc-group:SIM",n),i=t.lastIndexOf("a=ssrc-group");if(-1===r||-1===i||i===r)return e;var o=t.indexOf("\r\n",r),a=t.substring(r,o+2);i=(t=t.replace(a,"")).lastIndexOf("a=ssrc-group");var s=t.indexOf("\r\n",i),c=t.slice(0,s),u=a.trim(),l=t.slice(s);return t="".concat(c,"\r\n").concat(u).concat(l),new RTCSessionDescription({type:e.type,sdp:t})},A.prototype._adjustLocalMediaDirection=function(e){var t=new g.a(e.sdp),n=!1,r=t.selectMedia("audio");if(r){var i=this._getDesiredMediaDirection(c.a);r.direction!==i&&(r.direction=i,T.info("Adjusted local audio direction to ".concat(i)),n=!0)}else T.warn('No "audio" media found int the local description');var o=t.selectMedia("video");if(o){var a=this._getDesiredMediaDirection(c.b);o.direction!==a&&(o.direction=a,T.info("Adjusted local video direction to ".concat(a)),n=!0)}else T.warn('No "video" media found in the local description');return n?new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()}):e},A.prototype.setLocalDescription=function(e){var t=this,n=e;if(this.trace("setLocalDescription::preTransform",w(n)),this.options.disableH264||this.options.preferH264){var r=o.a.parse(n.sdp),i=r.media.find(function(e){return"video"===e.type});this.options.disableH264?S.a.stripVideoCodec(i,"h264"):S.a.preferVideoCodec(i,"h264"),n=new RTCSessionDescription({type:n.type,sdp:o.a.write(r)}),this.trace("setLocalDescription::postTransform (H264)",w(n))}return n=this._adjustLocalMediaDirection(n),n=this._ensureSimulcastGroupIsLast(n),p.a.usesUnifiedPlan()&&(n=this.interop.toUnifiedPlan(n),this.trace("setLocalDescription::postTransform (Unified Plan)",w(n))),new Promise(function(e,r){t.peerconnection.setLocalDescription(n).then(function(){t.trace("setLocalDescriptionOnSuccess");var r=S.a.getUfrag(n.sdp);r!==t.localUfrag&&(t.localUfrag=r,t.eventEmitter.emit(h.a.LOCAL_UFRAG_CHANGED,t,r)),e()},function(e){t.trace("setLocalDescriptionOnFailure",e),t.eventEmitter.emit(h.a.SET_LOCAL_DESCRIPTION_FAILED,e,t),r(e)})})},A.prototype.setAudioTransferActive=function(e){T.debug("".concat(this," audio transfer active: ").concat(e));var t=this.audioTransferActive!==e;return this.audioTransferActive=e,t},A.prototype._insertUnifiedPlanSimulcastReceive=function(e){var t=o.a.parse(e.sdp),n=t.media.find(function(e){return"video"===e.type});return n.rids=[{id:"1",direction:"recv"},{id:"2",direction:"recv"},{id:"3",direction:"recv"}],n.simulcast_03={value:"recv rid=".concat(R.join(";"))},new RTCSessionDescription({type:e.type,sdp:o.a.write(t)})},A.prototype.setRemoteDescription=function(e){var t=this;if(this.trace("setRemoteDescription::preTransform",w(e)),e=this.simulcast.mungeRemoteDescription(e),this.trace("setRemoteDescription::postTransform (simulcast)",w(e)),this.options.preferH264){var n=o.a.parse(e.sdp),r=n.media.find(function(e){return"video"===e.type});S.a.preferVideoCodec(r,"h264"),e=new RTCSessionDescription({type:e.type,sdp:o.a.write(n)})}return p.a.usesUnifiedPlan()?(e=new RTCSessionDescription({type:e.type,sdp:this.rtxModifier.stripRtx(e.sdp)}),this.trace("setRemoteDescription::postTransform (stripRtx)",w(e)),e=this.interop.toUnifiedPlan(e),this.trace("setRemoteDescription::postTransform (Plan A)",w(e)),this.isSimulcastOn()&&(e=this._insertUnifiedPlanSimulcastReceive(e),this.trace("setRemoteDescription::postTransform (sim receive)",w(e)))):e=k(e),p.a.isSafariWithWebrtc()&&!p.a.isSafariWithVP8()&&(T.debug("Maybe injecting H264 into the remote description"),e=this._injectH264IfNotPresent(e)),new Promise(function(n,r){t.peerconnection.setRemoteDescription(e).then(function(){t.trace("setRemoteDescriptionOnSuccess");var r=S.a.getUfrag(e.sdp);r!==t.remoteUfrag&&(t.remoteUfrag=r,t.eventEmitter.emit(h.a.REMOTE_UFRAG_CHANGED,t,r)),n()},function(e){t.trace("setRemoteDescriptionOnFailure",e),t.eventEmitter.emit(h.a.SET_REMOTE_DESCRIPTION_FAILED,e,t),r(e)})})},A.prototype._injectH264IfNotPresent=function(e){var t=o.a.parse(e.sdp),n=t.media.find(function(e){return"video"===e.type});if(!n)return T.debug("No videoMLine found, no need to inject H264."),e;if(n.rtp.some(function(e){return"h264"===e.codec.toLowerCase()}))return T.debug("H264 codec found in video mLine, no need to inject."),e;for(var r,i=n.fmtp,a=n.payloads,s=n.rtp,c=a.toString().split(" "),u=127;u>=96;u--)if(!c.includes(u)){r=u,c.push(u),n.payloads=c.join(" ");break}return void 0===r?(T.error("Could not find valid payload type to inject."),e):(s.push({codec:"H264",payload:r,rate:9e4}),i.push({config:"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",payload:r}),T.debug("Injecting H264 payload type ".concat(r," into video mLine.")),new RTCSessionDescription({type:e.type,sdp:o.a.write(t)}))},A.prototype.setVideoTransferActive=function(e){T.debug("".concat(this," video transfer active: ").concat(e));var t=this.videoTransferActive!==e;return this.videoTransferActive=e,t},A.prototype.generateRecvonlySsrc=function(){var e=S.a.generateSsrc();T.info("".concat(this," generated new recvonly SSRC: ").concat(e)),this.sdpConsistency.setPrimarySsrc(e)},A.prototype.clearRecvonlySsrc=function(){T.info("Clearing primary video SSRC!"),this.sdpConsistency.clearVideoSsrcCache()},A.prototype.close=function(){this.trace("stop"),this.signalingLayer.off(_.a,this._peerMutedChanged),this.signalingLayer.off(_.b,this._peerVideoTypeChanged);var e=!0,t=!1,n=void 0;try{for(var r,i=this.remoteTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(e=(r=i.next()).done);e=!0){var o=r.value,a=!0,s=!1,c=void 0;try{for(var u,l=o.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(a=(u=l.next()).done);a=!0){var d=u.value;this._removeRemoteTrack(d)}}catch(e){s=!0,c=e}finally{try{a||null==l.return||l.return()}finally{if(s)throw c}}}}catch(e){t=!0,n=e}finally{try{e||null==i.return||i.return()}finally{if(t)throw n}}this.remoteTracks.clear(),this._addedStreams=[],this.rtc._removePeerConnection(this)||T.error("RTC._removePeerConnection returned false"),null!==this.statsinterval&&(window.clearInterval(this.statsinterval),this.statsinterval=null),T.info("Closing ".concat(this,"...")),this.peerconnection.close()},A.prototype.createAnswer=function(e){if(p.a.isFirefox()&&this.isSimulcastOn()){this.peerconnection.getSenders().find(function(e){return null!==e.track&&"video"===e.track.kind}).setParameters({encodings:[{rid:"1",scaleResolutionDownBy:4},{rid:"2",scaleResolutionDownBy:2},{rid:"3"}]})}return this._createOfferOrAnswer(!1,e)},A.prototype.createOffer=function(e){return this._createOfferOrAnswer(!0,e)},A.prototype._createOfferOrAnswer=function(e,t){var n=this,r=e?"Offer":"Answer";this.trace("create".concat(r),JSON.stringify(t,null," "));var i=function(t,i,a){try{if(n.trace("create".concat(r,"OnSuccess::preTransform"),w(t)),p.a.usesUnifiedPlan()&&(t=n.interop.toPlanB(t),n.trace("create".concat(r,"OnSuccess::postTransform (Plan B)"),w(t)),n.isSimulcastOn()&&(t=n._injectSsrcGroupForUnifiedSimulcast(t),n.trace("create".concat(r,"OnSuccess::postTransform")+"(inject ssrc group)",w(t)))),p.a.usesUnifiedPlan()||(n.hasAnyTracksOfType(c.b)||n.sdpConsistency.hasPrimarySsrcCached()||n.generateRecvonlySsrc(),t=new RTCSessionDescription({type:t.type,sdp:n.sdpConsistency.makeVideoPrimarySsrcsConsistent(t.sdp)}),n.trace("create".concat(r,"OnSuccess::postTransform ")+"(make primary audio/video ssrcs consistent)",w(t))),n.isSimulcastOn()&&(t=n.simulcast.mungeLocalDescription(t),n.trace("create".concat(r)+"OnSuccess::postTransform (simulcast)",w(t))),!n.options.disableRtx&&p.a.supportsRtx()&&(t=new RTCSessionDescription({type:t.type,sdp:n.rtxModifier.modifyRtxSsrcs(t.sdp)}),n.trace("create".concat(r)+"OnSuccess::postTransform (rtx modifier)",w(t))),!e){var s=new v.a(n.remoteDescription.sdp),u=new v.a(t.sdp);d=s,f=u,(p.a.isChromiumBased()||p.a.isReactNative())&&d&&f&&d.media&&f.media&&d.media.length===f.media.length&&(f.media.forEach(function(e,t){S.a.findLine(d.media[t],"a=setup:actpass",d.session)&&(f.media[t]=e.replace(/a=setup:active/g,"a=setup:passive"))}),f.raw=f.session+f.media.join("")),t=new RTCSessionDescription({type:t.type,sdp:u.raw})}var l=(function(e){var t=new Map,n=new Map;if("object"!==E(e)||null===e||"string"!=typeof e.sdp)return T.warn("An empty description was passed as an argument."),t;var r=o.a.parse(e.sdp);if(!Array.isArray(r.media))return t;var i=!0,a=!1,s=void 0;try{for(var c,u=r.media["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(c=u.next()).done);i=!0){var l=c.value;if(Array.isArray(l.ssrcs)){if(Array.isArray(l.ssrcGroups)){var d=!0,p=!1,f=void 0;try{for(var h,m=l.ssrcGroups["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(h=m.next()).done);d=!0){var v=h.value;if(void 0!==v.semantics&&void 0!==v.ssrcs){var y=v.ssrcs.split(" ").map(function(e){return parseInt(e,10)}),g=y[0];v.ssrcs=y,n.has(g)||n.set(g,[]),n.get(g).push(v)}}}catch(e){p=!0,f=e}finally{try{d||null==m.return||m.return()}finally{if(p)throw f}}}var S=!0,_=!1,b=void 0;try{for(var C,R=l.ssrcs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(S=(C=R.next()).done);S=!0){var A=C.value;if("msid"===A.attribute){var w=A.value,k=t.get(w);k||(k={ssrcs:[],groups:[],msid:w},t.set(w,k));var O=A.id;if(k.ssrcs.push(O),n.has(O)){var I=n.get(O),P=!0,D=!1,N=void 0;try{for(var L,M=I["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(P=(L=M.next()).done);P=!0){var x=L.value;k.groups.push(x)}}catch(e){D=!0,N=e}finally{try{P||null==M.return||M.return()}finally{if(D)throw N}}}}}}catch(e){_=!0,b=e}finally{try{S||null==R.return||R.return()}finally{if(_)throw b}}}}}catch(e){a=!0,s=e}finally{try{i||null==u.return||u.return()}finally{if(a)throw s}}return t})(t);T.debug("Got local SSRCs MAP: ",l),n._processLocalSSRCsMap(l),i(t)}catch(e){n.trace("create".concat(r,"OnError"),e),n.trace("create".concat(r,"OnError"),w(t)),T.error("create".concat(r,"OnError"),e,w(t)),a(e)}var d,f};return new Promise(function(o,a){(e?n.peerconnection.createOffer(t):n.peerconnection.createAnswer(t)).then(function(e){return i(e,o,a)},function(t){return(function(t,i){n.trace("create".concat(r,"OnFailure"),t);var o=e?h.a.CREATE_OFFER_FAILED:h.a.CREATE_ANSWER_FAILED;n.eventEmitter.emit(o,t,n),i(t)})(t,a)})})},A.prototype._processLocalSSRCsMap=function(e){var t=!0,n=!1,r=void 0;try{for(var i,o=this.localTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value,s=a.storedMSID;if(e.has(s)){var c=e.get(s);if(!c)return void T.error("No SSRC found for: ".concat(s," in ").concat(this));var u=this.localSSRCs.get(a.rtcId),l=I(c),d=I(u);l!==d?(null===d?T.info("Storing new local SSRC for ".concat(a," in ").concat(this),c):T.error("Overwriting SSRC for ".concat(a," ").concat(s," in ").concat(this," with: "),c),this.localSSRCs.set(a.rtcId,c),this.eventEmitter.emit(h.a.LOCAL_TRACK_SSRC_UPDATED,a,l)):T.debug("The local SSRC(".concat(l,") for ").concat(a," ").concat(s)+"is still up to date in ".concat(this))}else a.isVideoTrack()||a.isMuted()||T.warn("No SSRCs found in the local SDP for ".concat(a," MSID: ").concat(s," in ").concat(this))}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}},A.prototype.addIceCandidate=function(e){return this.trace("addIceCandidate",JSON.stringify({candidate:e.candidate,sdpMid:e.sdpMid,sdpMLineIndex:e.sdpMLineIndex,usernameFragment:e.usernameFragment},null," ")),this.peerconnection.addIceCandidate(e)},A.prototype.getStats=function(e,t){p.a.isSafariWithWebrtc()||p.a.isFirefox()||p.a.isReactNative()?this.peerconnection.getStats().then(e).catch(t||function(){}):this.peerconnection.getStats(e)},A.prototype.generateNewStreamSSRCInfo=function(e){var t=e.rtcId,n=this._getSSRC(t);if(n&&T.error("Will overwrite local SSRCs for track ID: ".concat(t)),this.isSimulcastOn()){n={ssrcs:[],groups:[]};for(var r=0;r should fake sdp ? : ").concat(m)),m){var v=this.tpc.isSimulcastOn()?this.tpc.simulcast.ssrcCache:[this.tpc.sdpConsistency.cachedPrimarySsrc];if(v.length){r=!0,n.direction="sendrecv";var y=v[0],g="injected-".concat(y),S=!0,_=!1,b=void 0;try{for(var E,T=v["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(S=(E=T.next()).done);S=!0){var C=E.value;n.removeSSRC(C),s.debug("".concat(this.tpc," injecting video SSRC: ").concat(C," for ").concat(d)),n.addSSRCAttribute({id:C,attribute:"cname",value:g}),n.addSSRCAttribute({id:C,attribute:"msid",value:d.storedMSID})}}catch(e){_=!0,b=e}finally{try{S||null==T.return||T.return()}finally{if(_)throw b}}if(v.length>1){var R={ssrcs:v.join(" "),semantics:"SIM"};n.findGroup(R.semantics,R.ssrcs)||(s.debug("".concat(this.tpc," injecting SIM group for ").concat(d),R),n.addSSRCGroup(R))}this.tpc.options.disableRtx||this.tpc.rtxModifier.modifyRtxSsrcs2(n)}else s.error("No SSRCs stored for: ".concat(d," in ").concat(this.tpc))}}}catch(e){a=!0,c=e}finally{try{o||null==l.return||l.return()}finally{if(a)throw c}}return r}},{key:"_transformMediaIdentifiers",value:function(e){var t=this.tpc.id,n=!0,r=!1,i=void 0;try{for(var o,a=e.ssrcs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var c=o.value;switch(c.attribute){case"cname":case"label":case"mslabel":c.value=c.value&&"".concat(c.value,"-").concat(t);break;case"msid":if(c.value){var u=c.value.split(" ");if(2===u.length){var l=u[0],d=u[1];c.value="".concat(l,"-").concat(t," ").concat(d,"-").concat(t)}else s.warn("Unable to munge local MSID"+"- weird format detected: ".concat(c.value))}}}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}}},{key:"maybeAddMutedLocalVideoTracksToSDP",value:function(e){if(!e)throw new Error("No local description passed in.");var t=new o.a(e.sdp);return this._addMutedLocalVideoTracksToSDP(t)?new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()}):e}},{key:"transformStreamIdentifiers",value:function(e){if(!e||!e.sdp||!e.type)return e;var t=new o.a(e.sdp),n=t.selectMedia("audio");n&&this._transformMediaIdentifiers(n);var r=t.selectMedia("video");return r&&this._transformMediaIdentifiers(r),new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()})}}])&&a(t.prototype,n),e})()}).call(this,"modules/RTC/LocalSdpMunger.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return u});var r=n(4),i=n(27),o=n(6);function a(e,t){for(var n=0;n.6&&(this._eventFired=!0,this._callback())}},{key:"_isLocalAudioTrack",value:function(e){return e.isAudioTrack()&&e.isLocal()}},{key:"_trackAdded",value:function(e){this._isLocalAudioTrack(e)&&(this.audioTrack=e)}},{key:"_trackMuteChanged",value:function(e){this._isLocalAudioTrack(e)&&e.isMuted()&&(this._eventFired=!1)}}])&&i(t.prototype,n),e})()},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return v});var r=n(26),i=n(2),o=n(4);function a(e,t){for(var n=0;n=180&&"break"!==a();i/=2);}}else{var s=t.width*t.height;r=s<=76800?600:s<=307200?1700:s<=518400?2e3:2500}return Math.min(r,(function(e){return e>6e4?Number.MAX_SAFE_INTEGER:f*Math.pow(1.08,e/1e3)})(Math.max(0,n-1e3)))}var v=(function(){function e(t,n,o){var a=this;!(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this.eventEmitter=n,this._conference=t,this._localStats={connectionQuality:100,jvbRTT:void 0},this._lastConnectionQualityUpdate=-1,this._remoteStats={},this._timeIceConnected=-1,this._timeVideoUnmuted=-1,this._timeLastBwCapRemoved=-1,o.config.startBitrate&&o.config.startBitrate>0&&(f=o.config.startBitrate),t.on(i.CONNECTION_INTERRUPTED,function(){a._updateLocalConnectionQuality(0),a.eventEmitter.emit(r.LOCAL_STATS_UPDATED,a._localStats),a._broadcastLocalStats()}),t.room.addListener(s.ICE_CONNECTION_STATE_CHANGED,function(e,t){e.isP2P||"connected"!==t||(a._timeIceConnected=window.performance.now())}),t.on(i.ENDPOINT_MESSAGE_RECEIVED,function(e,t){t.type===d&&a._updateRemoteStats(e.getId(),t.values)}),t.statistics.addConnectionStatsListener(this._updateLocalStats.bind(this)),t.on(i.TRACK_MUTE_CHANGED,function(e){e.isVideoTrack()&&(e.isMuted()?a._timeVideoUnmuted=-1:a._maybeUpdateUnmuteTime())}),t.on(i.TRACK_ADDED,function(e){e.isVideoTrack()&&!e.isMuted()&&a._maybeUpdateUnmuteTime()}),t.on(i.SERVER_REGION_CHANGED,function(e){a._localStats.serverRegion=e}),t.on(i.PROPERTIES_CHANGED,function(e){a._localStats.bridgeCount=Number((e||{})["bridge-count"])})}var t,n;return t=e,(n=[{key:"_maybeUpdateUnmuteTime",value:function(){this._timeVideoUnmuted<0&&(this._timeVideoUnmuted=window.performance.now())}},{key:"_calculateConnectionQuality",value:function(e,t,n){var r,i=u[n],o=100;if(this._localStats.packetLoss&&(r=this._localStats.packetLoss.upload,t&&(r*=.5)),t||!i||e===c.DESKTOP||this._timeIceConnected<0||this._timeVideoUnmuted<0)void 0===r?(l.error("Cannot calculate connection quality, unknown packet loss."),o=100):o=r<=2?100:r<=4?70:r<=6?50:r<=8?30:r<=12?10:0;else{var a=this._conference.getActivePeerConnection(),s=Boolean(a&&a.isSimulcastOn()),d=a&&a.bandwidthLimiter&&a.bandwidthLimiter.getBandwidthLimit("video");!d&&h&&(this._timeLastBwCapRemoved=window.performance.now(),f=h),h=d;var p=m(s,i,window.performance.now()-Math.max(this._timeVideoUnmuted,this._timeIceConnected,this._timeLastBwCapRemoved));p=Math.min(.9*p,2500),h&&(p=Math.min(p,h)),o=100*this._localStats.bitrate.upload/p,r&&r>=10&&(o=Math.min(o,30))}if(this._lastConnectionQualityUpdate>0){var v=this._localStats.connectionQuality,y=(window.performance.now()-this._lastConnectionQualityUpdate)/1e3;o=Math.min(o,v+2*y)}return Math.min(100,o)}},{key:"_updateLocalConnectionQuality",value:function(e){this._localStats.connectionQuality=e,this._lastConnectionQualityUpdate=window.performance.now()}},{key:"_broadcastLocalStats",value:function(){var e={bitrate:this._localStats.bitrate,packetLoss:this._localStats.packetLoss,connectionQuality:this._localStats.connectionQuality,jvbRTT:this._localStats.jvbRTT,serverRegion:this._localStats.serverRegion,avgAudioLevels:this._localStats.localAvgAudioLevels};try{this._conference.broadcastEndpointMessage({type:d,values:e})}catch(e){}}},{key:"_updateLocalStats",value:function(e,t){if(!e.isP2P){var n=t.transport&&t.transport.length&&t.transport[0].rtt;this._localStats.jvbRTT=n||void 0}if(e===this._conference.getActivePeerConnection()){var i,o=!this._conference.isConnectionInterrupted(),a=this._conference.getLocalVideoTrack(),s=a?a.videoType:void 0,c=!a||a.isMuted(),u=a?a.resolution:null;for(i in c||this._maybeUpdateUnmuteTime(),t)t.hasOwnProperty(i)&&(this._localStats[i]=t[i]);o&&this._updateLocalConnectionQuality(this._calculateConnectionQuality(s,c,u)),this.eventEmitter.emit(r.LOCAL_STATS_UPDATED,this._localStats),this._broadcastLocalStats()}}},{key:"_updateRemoteStats",value:function(e,t){this._remoteStats[e]={bitrate:t.bitrate,packetLoss:t.packetLoss,connectionQuality:t.connectionQuality,jvbRTT:t.jvbRTT,serverRegion:t.serverRegion,avgAudioLevels:t.avgAudioLevels},this.eventEmitter.emit(r.REMOTE_STATS_UPDATED,e,this._remoteStats[e])}},{key:"getStats",value:function(){return this._localStats}}])&&a(t.prototype,n),e})()}).call(this,"modules/connectivity/ConnectionQuality.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return a});var r=n(4);function i(e,t){for(var n=0;n0&&this.analyticsIntervalMs ").concat(t)),this._conference.eventEmitter.emit(i.JVB121_STATUS,e,t))}}])&&o(t.prototype,n),e})()}).call(this,"modules/event/Jvb121EventGenerator.js")},function(e,t,n){"use strict";(function(e){var r=n(4),i=n(0),o=n.n(i),a=n(58),s=n(42);function c(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function u(e,t){for(var n=0;n0){var a="".concat(i.stat("ipAddress"),"_").concat(i.stat("portNumber"),"_").concat(i.stat("priority"));e.handleCandidateRtt(a,o,Number(i.stat("stunKeepaliveResponsesReceived")),Number(i.stat("stunKeepaliveRequestsSent")))}}var s=1/0;for(var u in e.candidates)e.candidates.hasOwnProperty(u)&&e.candidates[u].rtt>0&&(s=Math.min(s,e.candidates[u].rtt));e.rtts.push(s),e.rtts.length>6&&(e.rtts=e.rtts.splice(1,7)),e.rtt=Math.min.apply(Math,c(e.rtts))})},this.getStatsIntervalMs)}},{key:"handleCandidateRtt",value:function(e,t,n,r){this.candidates[e]||(this.candidates[e]={rttTotal:0,responsesReceived:0,requestsSent:0,rtt:NaN});var i=t-this.candidates[e].rttTotal,o=n-this.candidates[e].responsesReceived,a=r-n-(this.candidates[e].requestsSent-this.candidates[e].responsesReceived),s=NaN;o>0&&0===a&&(s=i/o),this.candidates[e].rttTotal=t,this.candidates[e].responsesReceived=n,this.candidates[e].requestsSent=r,this.candidates[e].rtt=s}},{key:"stop",value:function(){this.getStatsInterval&&window.clearInterval(this.getStatsInterval),this.pc.close(),this.stopped=!0}}]),e})(),m=(function(){function e(t){var n=this;if(u(this,e),t&&t.enabled&&r.a.supportsLocalCandidateRttStatistics()){this.pcMonitors={},this.startPCMonitors=this.startPCMonitors.bind(this),this.sendAnalytics=this.sendAnalytics.bind(this),this.stop=this.stop.bind(this),this.analyticsInterval=null,this.stopped=!1;var i=t.initialDelay||6e4;p.info("Starting RTT monitor with an initial delay of ".concat(i)),window.setTimeout(function(){return n.startPCMonitors(t)},i)}}return d(e,[{key:"startPCMonitors",value:function(e){var t=this;if(e.stunServers){if(!this.stopped){var n=e.getStatsInterval||1e4,r=e.analyticsInterval||n,i=n/Object.keys(e.stunServers).length,o=0;for(var a in e.stunServers)if(e.stunServers.hasOwnProperty(a)){var s=e.stunServers[a];this.pcMonitors[a]=new h(a,s,n,i*o),o++}window.setTimeout(function(){t.stopped||(t.analyticsInterval=window.setInterval(t.sendAnalytics,r))},1e3)}}else p.warn("No stun servers configured.")}},{key:"sendAnalytics",value:function(){var e={};for(var t in this.pcMonitors)if(this.pcMonitors.hasOwnProperty(t)){var n=this.pcMonitors[t].rtt;isNaN(n)||n===1/0||(e[t.replace("-","_")]=n)}s.a.sendAnalytics(Object(i.L)(e))}},{key:"stop",value:function(){for(var e in p.info("Stopping RttMonitor."),this.stopped=!0,this.pcMonitors)this.pcMonitors.hasOwnProperty(e)&&this.pcMonitors[e].stop();this.pcMonitors={},this.analyticsInterval&&window.clearInterval(this.analyticsInterval)}}]),e})()}).call(this,"modules/rttmonitor/rttmonitor.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return y});var r=n(7),i=n(4),o=n(26),a=n(2),s=n(9),c=n(3),u=n(5),l=n(14);function d(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function p(e,t){for(var n=0;n=this._n){if(c.a.supportsRTTStatistics()){var t=this._avgRtpStatsReporter._conference,n={p2p:this.isP2P,conference_size:t.getParticipantCount()};if(e.transport&&e.transport.length&&_extends(n,{local_candidate_type:e.transport[0].localCandidateType,remote_candidate_type:e.transport[0].remoteCandidateType,transport_type:e.transport[0].type}),this._avgRTT.appendReport(n),this.isP2P){var i=this._avgRtpStatsReporter.jvbStatsMonitor._avgEnd2EndRTT;isNaN(i)||(n.rtt_diff=this._avgRTT.calculate()-i)}else{var o=this._calculateAvgRemoteRTT(),a=this._avgRTT.calculate();this._avgEnd2EndRTT=a+o,isNaN(a)||isNaN(o)||(n.end2end_rtt_avg=this._avgEnd2EndRTT)}u.a.sendAnalytics(Object(r.K)(n))}this._resetAvgStats()}}else h.error("No stats")}},{key:"_calculateAvgRemoteRTT",value:function(){var e=0,t=0,n=!0,r=!1,i=void 0;try{for(var o,a=this._avgRemoteRTTMap.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value,c=s.calculate();isNaN(c)||(t+=c,e+=1,s.reset())}}catch(e){r=!0,i=e}finally{try{n||null==a.return||a.return()}finally{if(r)throw i}}return t/e}},{key:"_processRemoteStats",value:function(e,t){var n="number"==typeof t.jvbRTT,r=this._avgRemoteRTTMap.get(e);!r&&n&&(r=new m("".concat(e,"_stat_rtt")),this._avgRemoteRTTMap.set(e,r)),n?r.addNext(t.jvbRTT):r&&this._avgRemoteRTTMap.delete(e)}},{key:"_resetAvgStats",value:function(){this._avgRTT.reset(),this._avgRemoteRTTMap&&this._avgRemoteRTTMap.clear(),this._sampleIdx=0}},{key:"dispose",value:function(){var e=this._avgRtpStatsReporter._conference;e.statistics.removeConnectionStatsListener(this._onConnectionStats),this.isP2P||(e.off(o.REMOTE_STATS_UPDATED,this._onRemoteStatsUpdated),e.off(a.USER_LEFT,this._onUserLeft))}}]),e})(),y=(function(){function e(t,n){var r=this;d(this,e),this._n=n,n>0?(h.info("Avg RTP stats will be calculated every ".concat(n," samples")),this._sampleIdx=0,this._conference=t,this._avgAudioBitrateUp=new m("bitrate_audio_upload"),this._avgAudioBitrateDown=new m("bitrate_audio_download"),this._avgVideoBitrateUp=new m("bitrate_video_upload"),this._avgVideoBitrateDown=new m("bitrate_video_download"),this._avgBandwidthUp=new m("bandwidth_upload"),this._avgBandwidthDown=new m("bandwidth_download"),this._avgPacketLossTotal=new m("packet_loss_total"),this._avgPacketLossUp=new m("packet_loss_upload"),this._avgPacketLossDown=new m("packet_loss_download"),this._avgRemoteFPS=new m("framerate_remote"),this._avgRemoteScreenFPS=new m("framerate_screen_remote"),this._avgLocalFPS=new m("framerate_local"),this._avgLocalScreenFPS=new m("framerate_screen_local"),this._avgRemoteCameraPixels=new m("pixels_remote"),this._avgRemoteScreenPixels=new m("pixels_screen_remote"),this._avgLocalCameraPixels=new m("pixels_local"),this._avgLocalScreenPixels=new m("pixels_screen_local"),this._avgCQ=new m("connection_quality"),this._onLocalStatsUpdated=function(e){return r._calculateAvgStats(e)},t.on(o.LOCAL_STATS_UPDATED,this._onLocalStatsUpdated),this._onP2PStatusChanged=function(){h.debug("Resetting average stats calculation"),r._resetAvgStats(),r.jvbStatsMonitor._resetAvgStats(),r.p2pStatsMonitor._resetAvgStats()},t.on(a.P2P_STATUS,this._onP2PStatusChanged),this._onJvb121StatusChanged=function(e,t){!0===t&&(h.info("Resetting JVB avg RTP stats"),r._resetAvgJvbStats())},t.on(a.JVB121_STATUS,this._onJvb121StatusChanged),this.jvbStatsMonitor=new v(this,!1,n),this.p2pStatsMonitor=new v(this,!0,n)):h.info("Avg RTP stats reports are disabled.")}return f(e,[{key:"_calculateAvgStats",value:function(e){if(e){var t=this._conference.isP2PActive(),n=this._conference.getParticipantCount();if(t||!(n<2)){var i=e.bitrate,o=e.bandwidth,a=e.packetLoss,s=e.framerate,d=e.resolution;if(i)if(o)if(a)if(s)if(d){if(this._avgAudioBitrateUp.addNext(i.audio.upload),this._avgAudioBitrateDown.addNext(i.audio.download),this._avgVideoBitrateUp.addNext(i.video.upload),this._avgVideoBitrateDown.addNext(i.video.download),c.a.supportsBandwidthStatistics()&&(this._avgBandwidthUp.addNext(o.upload),this._avgBandwidthDown.addNext(o.download)),this._avgPacketLossUp.addNext(a.upload),this._avgPacketLossDown.addNext(a.download),this._avgPacketLossTotal.addNext(a.total),this._avgCQ.addNext(e.connectionQuality),s&&(this._avgRemoteFPS.addNext(this._calculateAvgVideoFps(s,!1,l.CAMERA)),this._avgRemoteScreenFPS.addNext(this._calculateAvgVideoFps(s,!1,l.DESKTOP)),this._avgLocalFPS.addNext(this._calculateAvgVideoFps(s,!0,l.CAMERA)),this._avgLocalScreenFPS.addNext(this._calculateAvgVideoFps(s,!0,l.DESKTOP))),d&&(this._avgRemoteCameraPixels.addNext(this._calculateAvgVideoPixels(d,!1,l.CAMERA)),this._avgRemoteScreenPixels.addNext(this._calculateAvgVideoPixels(d,!1,l.DESKTOP)),this._avgLocalCameraPixels.addNext(this._calculateAvgVideoPixels(d,!0,l.CAMERA)),this._avgLocalScreenPixels.addNext(this._calculateAvgVideoPixels(d,!0,l.DESKTOP))),this._sampleIdx+=1,this._sampleIdx>=this._n){var p={p2p:t,conference_size:n};e.transport&&e.transport.length&&_extends(p,{local_candidate_type:e.transport[0].localCandidateType,remote_candidate_type:e.transport[0].remoteCandidateType,transport_type:e.transport[0].type}),this._avgAudioBitrateUp.appendReport(p),this._avgAudioBitrateDown.appendReport(p),this._avgVideoBitrateUp.appendReport(p),this._avgVideoBitrateDown.appendReport(p),c.a.supportsBandwidthStatistics()&&(this._avgBandwidthUp.appendReport(p),this._avgBandwidthDown.appendReport(p)),this._avgPacketLossUp.appendReport(p),this._avgPacketLossDown.appendReport(p),this._avgPacketLossTotal.appendReport(p),this._avgRemoteFPS.appendReport(p),isNaN(this._avgRemoteScreenFPS.calculate())||this._avgRemoteScreenFPS.appendReport(p),this._avgLocalFPS.appendReport(p),isNaN(this._avgLocalScreenFPS.calculate())||this._avgLocalScreenFPS.appendReport(p),this._avgRemoteCameraPixels.appendReport(p),isNaN(this._avgRemoteScreenPixels.calculate())||this._avgRemoteScreenPixels.appendReport(p),this._avgLocalCameraPixels.appendReport(p),isNaN(this._avgLocalScreenPixels.calculate())||this._avgLocalScreenPixels.appendReport(p),this._avgCQ.appendReport(p),u.a.sendAnalytics(Object(r.K)(p)),this._resetAvgStats()}}else h.error("No resolution");else h.error('No "framerate"');else h.error('No "packetloss"');else h.error('No "bandwidth"');else h.error('No "bitrate"')}}else h.error("No stats")}},{key:"_calculateAvgVideoPixels",value:function(e,t,n){for(var r=0,i=0,o=this._conference.myUserId(),a=Object.keys(e),s=0;s0&&(a+=m,c+=1)}}catch(e){l=!0,d=e}finally{try{u||null==f.return||f.return()}finally{if(l)throw d}}return a/c}},{key:"_calculateAvgVideoFps",value:function(e,t,n){for(var r=0,i=0,o=this._conference.myUserId(),a=Object.keys(e),s=0;s0&&(a+=m,c+=1)}}catch(e){l=!0,d=e}finally{try{u||null==f.return||f.return()}finally{if(l)throw d}}return a/c}},{key:"_resetAvgJvbStats",value:function(){this._resetAvgStats(),this.jvbStatsMonitor._resetAvgStats()}},{key:"_resetAvgStats",value:function(){this._avgAudioBitrateUp.reset(),this._avgAudioBitrateDown.reset(),this._avgVideoBitrateUp.reset(),this._avgVideoBitrateDown.reset(),this._avgBandwidthUp.reset(),this._avgBandwidthDown.reset(),this._avgPacketLossUp.reset(),this._avgPacketLossDown.reset(),this._avgPacketLossTotal.reset(),this._avgRemoteFPS.reset(),this._avgRemoteScreenFPS.reset(),this._avgLocalFPS.reset(),this._avgLocalScreenFPS.reset(),this._avgRemoteCameraPixels.reset(),this._avgRemoteScreenPixels.reset(),this._avgLocalCameraPixels.reset(),this._avgLocalScreenPixels.reset(),this._avgCQ.reset(),this._sampleIdx=0}},{key:"dispose",value:function(){this._conference.off(a.P2P_STATUS,this._onP2PStatusChanged),this._conference.off(o.LOCAL_STATS_UPDATED,this._onLocalStatsUpdated),this._conference.off(a.JVB121_STATUS,this._onJvb121StatusChanged),this.jvbStatsMonitor.dispose(),this.p2pStatsMonitor.dispose()}}]),e})()}).call(this,"modules/statistics/AvgRTPStatsReporter.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return d});var r=n(4),i=n(2),o=n(26),a=n(9),s=n(7),c=n(5);function u(e,t){for(var n=0;n0&&i.isAudioMuted())return;var o=this._localAudioLevelCache[e];Array.isArray(o)&&!o.every(function(e){return 0===e})||(this._audioProblemCandidates[e]={remoteAudioLevels:n,localAudioLevels:[]})}}},{key:"_onLocalAudioLevelsReport",value:function(e,t){var n=this,r=t.avgAudioLevels;e===this._conference.getActivePeerConnection()&&(Object.keys(r).forEach(function(e){if(-1===n._reportedParticipants.indexOf(e)){var t=n._localAudioLevelCache[e];Array.isArray(t)?t.length>=2&&t.shift():n._localAudioLevelCache[e]=[],n._localAudioLevelCache[e].push(r[e])}}),Object.keys(this._audioProblemCandidates).forEach(function(e){var t=n._audioProblemCandidates[e],i=t.localAudioLevels,o=t.remoteAudioLevels;if(i.push(r[e]),2===i.length){if(i.every(function(e){return void 0===e||0===e})){var a=JSON.stringify(i);c.a.sendAnalytics(Object(s.x)(e,a,o)),l.warn("A potential problem is detected with the audio output for participant ".concat(e,", local audio levels: ").concat(a,", remote audio levels: ").concat(o)),n._reportedParticipants.push(e),n._clearUserData(e)}delete n._audioProblemCandidates[e]}}))}},{key:"_clearUserData",value:function(e){delete this._localAudioLevelCache[e]}},{key:"dispose",value:function(){this._conference.off(o.REMOTE_STATS_UPDATED,this._onRemoteAudioLevelReceived),this._conference.off(i.USER_LEFT,this._clearUserData),this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport),this._localAudioLevelCache=void 0,this._audioProblemCandidates=void 0,this._reportedParticipants=void 0,this._numberOfRemoteAudioLevelsReceived=void 0,this._conference=void 0}}])&&u(t.prototype,n),e})()}).call(this,"modules/statistics/AudioOutputProblemDetector.js")},function(e,t,n){"use strict";n.d(t,"a",function(){return u});var r=n(2),i=n(55),o=n.n(i),a=n(0),s=n.n(a);function c(e,t){for(var n=0;n0}a.prototype.start=function(){if(this.state!==o)throw new Error("The transcription can only start when it's in the \"".concat(o,'" state. It\'s currently in the "').concat(this.state,'" state'));this.state="recording",this.audioRecorder.start(),this.startTime=new Date},a.prototype.stop=function(e){var t=this;if("recording"!==this.state)throw new Error("The transcription can only stop when it's in the \"".concat("recording",'" state. It\'s currently in the "').concat(this.state,'" state'));console.log("stopping recording and sending audio files"),this.audioRecorder.stop();var n=function(e,t){if(console.log("retrieved an answer from the transcription service. The answer has an"+" array of length: ".concat(t.wordArray.length)),t.wordArray.length>0){var n=t.startTime.getUTCMilliseconds()-e.startTime.getUTCMilliseconds();n<0&&(n=0);var r="[";t.wordArray.forEach(function(e){e.begin+=n,e.end+=n,r+="".concat(e.word,",")}),r+="]",console.log(r),t.wordArray.name=t.name}e.results.push(t.wordArray),e.counter--,console.log("current counter: ".concat(e.counter)),e.maybeMerge()}.bind(null,this);this.audioRecorder.getRecordingResults().forEach(function(e){t.transcriptionService.send(e,n),t.counter++}),this.state="transcribing",this.callback=e},a.prototype.maybeMerge=function(){"transcribing"===this.state&&0===this.counter&&this.merge()},a.prototype.merge=function(){var e=this;console.log("starting merge process!\n The length of the array: ".concat(this.results.length)),this.transcription="";var t=this.results,n=[];s(t),t.forEach(function(e){return(function(e,t){if(0===e.length)e.push(t);else{if(e[e.length-1].begin<=t.begin)return void e.push(t);for(var n=0;n0&&"break"!==i(););};s(t);)r();this.state="finished",this.callback&&this.callback(this.transcription)},a.prototype.updateTranscription=function(e,t){null!=t&&(this.transcription+="\n".concat(t,":"),this.lineLength=t.length+1),this.lineLength+e.word.length>80&&(this.transcription+="\n ",this.lineLength=4),this.transcription+=" ".concat(e.word),this.lineLength+=e.word.length+1},a.prototype.addTrack=function(e){this.audioRecorder.addTrack(e)},a.prototype.removeTrack=function(e){this.audioRecorder.removeTrack(e)},a.prototype.getTranscription=function(){if("finished"!==this.state)throw new Error("The transcription can only be retrieved when it's in the \"".concat("finished",'" state. It\'s currently in the "').concat(this.state,'" state'));return this.transcription},a.prototype.getState=function(){return this.state},a.prototype.reset=function(){this.state=o,this.counter=null,this.transcription=null,this.startTime=null,this.callback=null,this.results=[],this.lineLength=0},e.exports=a},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return o});var r=n(5),i=n(4).getLogger(e);function o(e){this.versions={},this.conference=e,this.conference.addCommandListener("versions",this.processVersions.bind(this))}o.prototype.processVersions=function(e,t,n){var o=this;if(this.conference._isFocus(n)){var a=[];e.children.forEach(function(e){var t=e.attributes.name,n=e.value;o.versions[t]!==n&&(o.versions[t]=n,i.info("Got ".concat(t," version: ").concat(n)),a.push({id:"component_version",component:t,version:n}))}),a.length>0&&r.a.sendLog(JSON.stringify(a))}else i.warn("Received versions not from the focus user: ".concat(e),n)},o.prototype.getComponentVersion=function(e){return this.versions[e]}}).call(this,"modules/version/ComponentsVersions.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return l});var r=n(4),i=n(115),o=n(20),a=n(0),s=n.n(a);function c(e,t){for(var n=0;n=0||(i[n]=e[n]);return i})(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function h(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};!(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e);var n=t.jitsiConnection,r=f(t,["jitsiConnection"]);this._options=d({iceConfig:n&&n.xmpp.connection.jingle.p2pIceConfig},r),this._peerConnection=null,this._onFatalError=this._onFatalError.bind(this),this._onSendMessage=this._onSendMessage.bind(this),this._onRemoteStream=this._onRemoteStream.bind(this)}var t,n;return t=e,(n=[{key:"processMessage",value:function(e){var t=e.from;if(t)if(this._peerConnection&&this._peerConnection.getPeerJid()!==t)this._onFatalError(t,l.a.CONNECTION_ERROR,"rejected");else{var n=this._convertStringToXML(e.data.iq),r=n&&n.find("jingle"),i=r&&r.attr("action");i===l.a.INITIATE&&(this._peerConnection=this._createPeerConnection(t,{isInitiator:!1,receiveVideo:!0})),this._peerConnection&&this._peerConnection.processMessage(r),i!==l.a.CONNECTION_ERROR&&i!==l.a.UNAVAILABLE&&i!==l.a.TERMINATE||this._selfCloseConnection()}}},{key:"start",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];this._peerConnection=this._createPeerConnection(e,{isInitiator:!0,receiveVideo:!1}),this._peerConnection.start(t)}},{key:"stop",value:function(){this._peerConnection&&this._peerConnection.stop(),this._peerConnection=null}},{key:"_convertStringToXML",value:function(e){try{var t=(new DOMParser).parseFromString(e,"text/xml");return $(t)}catch(e){return m.error("Attempted to convert incorrectly formatted xml"),null}}},{key:"_createPeerConnection",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e)throw new Error("Cannot create ProxyConnectionPC without a peer.");var n=d({iceConfig:this._options.iceConfig,onError:this._onFatalError,onRemoteStream:this._onRemoteStream,onSendMessage:this._onSendMessage,peerJid:e},t);return new u.a(n)}},{key:"_onFatalError",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";m.error("Received a proxy connection error",e,t,n);var r=Object(i.$iq)({to:e,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:t}).c("details").t(n).up();this._onSendMessage(e,r),this._peerConnection&&this._peerConnection.getPeerJid()===e&&this._selfCloseConnection()}},{key:"_onRemoteStream",value:function(e){if(!this._options.onRemoteStream)return m.error("Remote track received without callback."),void e.dispose();var t,n=e.isVideoTrack();n&&(t=this._options.convertVideoToDesktop?s.a.DESKTOP:s.a.CAMERA);var r=e.getOriginalStream(),i=c.a.newCreateLocalTracks([{deviceId:"proxy:".concat(this._peerConnection.getPeerJid()),mediaType:n?o.b:o.a,sourceType:"proxy",stream:r,track:r.getVideoTracks()[0],videoType:t}]);this._options.onRemoteStream(i[0])}},{key:"_onSendMessage",value:function(e,t){if(this._options.onSendMessage)try{var n=(new XMLSerializer).serializeToString(t.nodeTree||t);this._options.onSendMessage(e,{iq:n})}catch(e){m.error("Attempted to send an incorrectly formatted iq.")}}},{key:"_selfCloseConnection",value:function(){this.stop(),this._options.onConnectionClosed&&this._options.onConnectionClosed()}}])&&h(t.prototype,n),e})()}).call(this,"modules/proxyconnection/ProxyConnectionService.js")},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return v});var r=n(4),i=n(13),o=n(8),a=n.n(o),s=n(0),c=n.n(s),u=n(44),l=n(35),d=n(28);function p(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};!(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._options=p({iceConfig:{},isInitiator:!1,receiveAudio:!1,receiveVideo:!1},t),this._tracks=[],this._peerConnection=null,this._onError=this._onError.bind(this),this._onRemoteStream=this._onRemoteStream.bind(this),this._onSendMessage=this._onSendMessage.bind(this)}var t,n;return t=e,(n=[{key:"getPeerJid",value:function(){return this._options.peerJid}},{key:"processMessage",value:function(e){switch(e.attr("action")){case d.a.ACCEPT:this._onSessionAccept(e);break;case d.a.INITIATE:this._onSessionInitiate(e);break;case d.a.TERMINATE:this._onSessionTerminate(e);break;case d.a.TRANSPORT_INFO:this._onTransportInfo(e)}}},{key:"start",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this._peerConnection||(this._tracks=this._tracks.concat(e),this._peerConnection=this._createPeerConnection(),this._peerConnection.invite(e))}},{key:"stop",value:function(){this._peerConnection&&this._peerConnection.terminate(),this._onSessionTerminate()}},{key:"_createPeerConnection",value:function(){var e=this,t={jingle:{terminate:function(){}},sendIQ:this._onSendMessage},n=p({iceServers:l.a},this._options.iceConfig),r={addPresenceListener:function(){},connectionTimes:[],eventEmitter:{emit:function(t){switch(t){case c.a.CONNECTION_ICE_FAILED:case c.a.CONNECTION_FAILED:e._onError(d.a.CONNECTION_ERROR,t)}}},getMediaPresenceInfo:function(){return{}},removePresenceListener:function(){}};this._rtc=new i.a(this,{}),this._rtc.addListener(a.a.REMOTE_TRACK_ADDED,this._onRemoteStream);var o=new u.a(void 0,void 0,this._options.peerJid,t,{offerToReceiveAudio:this._options.receiveAudio,offerToReceiveVideo:this._options.receiveVideo},n,!0,this._options.isInitiator);return o.initialize(r,this._rtc,{}),o}},{key:"_onError",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";this._options.onError(this._options.peerJid,e,t)}},{key:"_onRemoteStream",value:function(e){this._tracks.push(e),this._options.onRemoteStream(e)}},{key:"_onSendMessage",value:function(e){this._options.onSendMessage(this._options.peerJid,e)}},{key:"_onSessionAccept",value:function(e){this._peerConnection?this._peerConnection.setAnswer(e):m.error("Received an answer when no peer connection exists.")}},{key:"_onSessionInitiate",value:function(e){var t=this;this._peerConnection?m.error("Received an offer when an offer was already sent."):(this._peerConnection=this._createPeerConnection(),this._peerConnection.acceptOffer(e,function(){},function(){return t._onError(t._options.peerJid,d.a.CONNECTION_ERROR,"session initiate error")}))}},{key:"_onSessionTerminate",value:function(){this._tracks.forEach(function(e){return e.dispose()}),this._tracks=[],this._peerConnection&&this._peerConnection.onTerminated(),this._rtc&&(this._rtc.removeListener(a.a.REMOTE_TRACK_ADDED,this._onRemoteStream),this._rtc.destroy())}},{key:"_onTransportInfo",value:function(e){this._peerConnection.addIceCandidates(e)}}])&&h(t.prototype,n),e})()}).call(this,"modules/proxyconnection/ProxyConnectionPC.js")},function(e,t,n){e.exports=n(120).default},function(e,t,n){"use strict";n.r(t),function(e){var r=n(7),i=n(65),o=n.n(i),a=n(26),s=n(49),c=n(11),u=n.n(c),l=n(17),d=n(2),p=n(66),f=n(36),h=n(21),m=n(59),v=n(45),y=n(15),g=n(12),S=n(16),_=n(52),b=n(50),E=n(4),T=n.n(E),C=n(9),R=n(30),A=n.n(R),w=n(41),k=n(13),O=n(3),I=n(51),P=n.n(I),D=n(116),N=n(117),L=n(5),M=n(20);function x(e){return(x="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var j,F=T.a.getLogger(e);function U(e){var t={audio_requested:e.devices.includes("audio"),video_requested:e.devices.includes("video"),screen_sharing_requested:e.devices.includes("desktop")};return t.video_requested&&(t.resolution=e.resolution),t}t.default=(j={version:"development",JitsiConnection:p.a,ProxyConnectionService:N.a,constants:{participantConnectionStatus:w.a,recording:D.a,sipVideoGW:M,transcriptionStatus:_},events:{conference:d,connection:h,track:S,mediaDevices:v,connectionQuality:a,e2eping:s},errors:{conference:l,connection:f,track:g},errorTypes:{JitsiTrackError:y.a},logLevels:T.a.levels,mediaDevices:m.a,analytics:L.a.analytics,init:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};L.a.init(e),window.connectionTimes||(window.connectionTimes={}),!0!==e.enableAnalyticsLogging&&(F.warn("Analytics disabled, disposing."),this.analytics.dispose()),e.enableWindowOnErrorHandler&&u.a.addHandler(this.getGlobalOnErrorHandler.bind(this));var t=e.deploymentInfo;if(t&&Object.keys(t).length>0){var n={};for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);n.id="deployment_info",L.a.sendLog(JSON.stringify(n))}if(this.version){var i={id:"component_version",component:"lib-jitsi-meet",version:this.version};L.a.sendLog(JSON.stringify(i))}return k.a.init(e)},isDesktopSharingEnabled:function(){return k.a.isDesktopSharingEnabled()},isWebRtcSupported:function(){return k.a.isWebRtcSupported()},setLogLevel:function(e){T.a.setLogLevel(e)},setLogLevelById:function(e,t){T.a.setLogLevelById(e,t)},addGlobalLogTransport:function(e){T.a.addGlobalTransport(e)},removeGlobalLogTransport:function(e){T.a.removeGlobalTransport(e)},setGlobalLogOptions:function(e){T.a.setGlobalOptions(e)},createLocalTracks:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1?arguments[1]:void 0,i=arguments.length>2?arguments[2]:void 0,o=!1;return!0===n&&window.setTimeout(function(){o||m.a.emitEvent(v.PERMISSION_PROMPT_IS_SHOWN,O.a.getName())},1e3),window.connectionTimes||(window.connectionTimes={}),window.connectionTimes["obtainPermissions.start"]=window.performance.now(),k.a.obtainAudioAndVideoPermissions(t).then(function(e){if(o=!0,window.connectionTimes["obtainPermissions.end"]=window.performance.now(),L.a.sendAnalytics(Object(r.E)("success",U(t))),!k.a.options.disableAudioLevels)for(var n=function(t){var n=e[t],r=n.getOriginalStream();n.getType()===C.a&&(L.a.startLocalStats(r,n.setAudioLevel.bind(n)),n.addEventListener(S.LOCAL_TRACK_STOPPED,function(){L.a.stopLocalStats(r)}))},i=0;i=this.maxEntryLength&&this._flush(!0,!0)},o.prototype.start=function(){this._reschedulePublishInterval()},o.prototype._reschedulePublishInterval=function(){this.storeLogsIntervalID&&(window.clearTimeout(this.storeLogsIntervalID),this.storeLogsIntervalID=null),this.storeLogsIntervalID=window.setTimeout(this._flush.bind(this,!1,!0),this.storeInterval)},o.prototype.flush=function(){this._flush(!1,!0)},o.prototype._flush=function(e,t){this.totalLen>0&&(this.logStorage.isReady()||e)&&(this.logStorage.isReady()?(this.outputCache.length&&(this.outputCache.forEach(function(e){this.logStorage.storeLogs(e)}.bind(this)),this.outputCache=[]),this.logStorage.storeLogs(this.queue)):this.outputCache.push(this.queue),this.queue=[],this.totalLen=0),t&&this._reschedulePublishInterval()},o.prototype.stop=function(){this._flush(!1,!1)},e.exports=o},function(e,t){e.exports=function(){throw new Error("define cannot be used indirect")}},function(e,t){(function(t){e.exports=t}).call(this,{})},function(e,t,n){var r,i;this||window,void 0===(i="function"==typeof(r=function(){var e=/^(interactive|loaded|complete)$/,t=window.location?window.location.href:null,n=t&&t.replace(/#.*$/,"").replace(/\?.*$/,"")||null,r=document.getElementsByTagName("script"),i="readyState"in(r[0]||document.createElement("script")),o=!window.opera||"[object Opera]"!==window.opera.toString(),a="currentScript"in document;"stackTraceLimit"in Error&&Error.stackTraceLimit!==1/0&&(Error.stackTraceLimit,Error.stackTraceLimit=1/0);var s=!1,c=!1;function u(){if(0===r.length)return null;var t,l,d,p,f,h=[],m=u.skipStackDepth||1;for(t=0;t0?i=e(t.slice(t.indexOf(r[0])+r[0].length),n-1):i=r[1])),i})(d,m),h))&&n&&p===n&&(f=(function(e){var t,n,i=null;for(t=0,n=(e=e||r).length;t=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(128),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(43))},function(e,t,n){(function(e,t){!(function(e,n){"use strict";if(!e.setImmediate){var r,i,o,a,s,c=1,u={},l=!1,d=e.document,p=Object.getPrototypeOf&&Object.getPrototypeOf(e);p=p&&p.setTimeout?p:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick(function(){h(e)})}:(function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}})()?(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&h(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),r=function(t){e.postMessage(a+t,"*")}):e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){h(e.data)},r=function(e){o.port2.postMessage(e)}):d&&"onreadystatechange"in d.createElement("script")?(i=d.documentElement,r=function(e){var t=d.createElement("script");t.onreadystatechange=function(){h(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):r=function(e){setTimeout(h,0,e)},p.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n=63)){var n=e.RTCPeerConnection.prototype.getLocalStreams;e.RTCPeerConnection.prototype.getLocalStreams=function(){var e=this,t=n.apply(this);return e._reverseStreams=e._reverseStreams||{},t.map(function(t){return e._reverseStreams[t.id]})};var r=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(t){var n=this;if(n._streams=n._streams||{},n._reverseStreams=n._reverseStreams||{},t.getTracks().forEach(function(e){if(n.getSenders().find(function(t){return t.track===e}))throw new DOMException("Track already exists.","InvalidAccessError")}),!n._reverseStreams[t.id]){var i=new e.MediaStream(t.getTracks());n._streams[t.id]=i,n._reverseStreams[i.id]=t,t=i}r.apply(n,[t])};var o=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){var t=this;t._streams=t._streams||{},t._reverseStreams=t._reverseStreams||{},o.apply(t,[t._streams[e.id]||e]),delete t._reverseStreams[t._streams[e.id]?t._streams[e.id].id:e.id],delete t._streams[e.id]},e.RTCPeerConnection.prototype.addTrack=function(t,n){var r=this;if("closed"===r.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");var i=[].slice.call(arguments,1);if(1!==i.length||!i[0].getTracks().find(function(e){return e===t}))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(r.getSenders().find(function(e){return e.track===t}))throw new DOMException("Track already exists.","InvalidAccessError");r._streams=r._streams||{},r._reverseStreams=r._reverseStreams||{};var o=r._streams[n.id];if(o)o.addTrack(t),Promise.resolve().then(function(){r.dispatchEvent(new Event("negotiationneeded"))});else{var a=new e.MediaStream([t]);r._streams[n.id]=a,r._reverseStreams[a.id]=n,r.addStream(a)}return r.getSenders().find(function(e){return e.track===t})},["createOffer","createAnswer"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){var e=this,t=arguments;return arguments.length&&"function"==typeof arguments[0]?n.apply(e,[function(n){var r=c(e,n);t[0].apply(null,[r])},function(e){t[1]&&t[1].apply(null,e)},arguments[2]]):n.apply(e,arguments).then(function(t){return c(e,t)})}});var a=e.RTCPeerConnection.prototype.setLocalDescription;e.RTCPeerConnection.prototype.setLocalDescription=function(){return arguments.length&&arguments[0].type?(arguments[0]=(e=this,t=arguments[0],n=t.sdp,Object.keys(e._reverseStreams||[]).forEach(function(t){var r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(r.id,"g"),i.id)}),new RTCSessionDescription({type:t.type,sdp:n})),a.apply(this,arguments)):a.apply(this,arguments);var e,t,n};var s=Object.getOwnPropertyDescriptor(e.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(e.RTCPeerConnection.prototype,"localDescription",{get:function(){var e=s.get.apply(this);return""===e.type?e:c(this,e)}}),e.RTCPeerConnection.prototype.removeTrack=function(e){var t,n=this;if("closed"===n.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!e._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(e._pc!==n)throw new DOMException("Sender was not created by this connection.","InvalidAccessError");n._streams=n._streams||{},Object.keys(n._streams).forEach(function(r){n._streams[r].getTracks().find(function(t){return e.track===t})&&(t=n._streams[r])}),t&&(1===t.getTracks().length?n.removeStream(n._reverseStreams[t.id]):t.removeTrack(e.track),n.dispatchEvent(new Event("negotiationneeded")))}}function c(e,t){var n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(function(t){var r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(i.id,"g"),r.id)}),new RTCSessionDescription({type:t.type,sdp:n})}},shimPeerConnection:function(e){var t=i.detectBrowser(e);if(e.RTCPeerConnection){var n=e.RTCPeerConnection;e.RTCPeerConnection=function(e,t){if(e&&e.iceServers){for(var r=[],o=0;o0&&"function"==typeof e)return a.apply(this,arguments);if(0===a.length&&(0===arguments.length||"function"!=typeof arguments[0]))return a.apply(this,[]);var o=function(e){var t={};return e.result().forEach(function(e){var n={id:e.id,timestamp:e.timestamp,type:{localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[e.type]||e.type};e.names().forEach(function(t){n[t]=e.stat(t)}),t[n.id]=n}),t},s=function(e){return new Map(Object.keys(e).map(function(t){return[t,e[t]]}))};return arguments.length>=2?a.apply(this,[function(e){i[1](s(o(e)))},arguments[0]]):new Promise(function(e,t){a.apply(r,[function(t){e(s(o(t)))},t])}).then(t,n)},t.version<51&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){var e=arguments,t=this,r=new Promise(function(r,i){n.apply(t,[e[0],r,i])});return e.length<2?r:r.then(function(){e[1].apply(null,[])},function(t){e.length>=3&&e[2].apply(null,[t])})}}),t.version<52&&["createOffer","createAnswer"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){var e=this;if(arguments.length<1||1===arguments.length&&"object"===r(arguments[0])){var t=1===arguments.length?arguments[0]:void 0;return new Promise(function(r,i){n.apply(e,[r,i,t])})}return n.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}});var s=e.RTCPeerConnection.prototype.addIceCandidate;e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?s.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())}}};e.exports={shimMediaStream:a.shimMediaStream,shimOnTrack:a.shimOnTrack,shimAddTrackRemoveTrack:a.shimAddTrackRemoveTrack,shimGetSendersWithDtmf:a.shimGetSendersWithDtmf,shimSourceObject:a.shimSourceObject,shimPeerConnection:a.shimPeerConnection,shimGetUserMedia:n(132)}},function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var i=n(38),o=i.log;e.exports=function(e){var t=i.detectBrowser(e),n=e&&e.navigator,a=function(e){if("object"!==r(e)||e.mandatory||e.optional)return e;var t={};return Object.keys(e).forEach(function(n){if("require"!==n&&"advanced"!==n&&"mediaSource"!==n){var i="object"===r(e[n])?e[n]:{ideal:e[n]};void 0!==i.exact&&"number"==typeof i.exact&&(i.min=i.max=i.exact);var o=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==i.ideal){t.optional=t.optional||[];var a={};"number"==typeof i.ideal?(a[o("min",n)]=i.ideal,t.optional.push(a),(a={})[o("max",n)]=i.ideal,t.optional.push(a)):(a[o("",n)]=i.ideal,t.optional.push(a))}void 0!==i.exact&&"number"!=typeof i.exact?(t.mandatory=t.mandatory||{},t.mandatory[o("",n)]=i.exact):["min","max"].forEach(function(e){void 0!==i[e]&&(t.mandatory=t.mandatory||{},t.mandatory[o(e,n)]=i[e])})}}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},s=function(e,i){if(t.version>=61)return i(e);if((e=JSON.parse(JSON.stringify(e)))&&"object"===r(e.audio)){var s=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])};s((e=JSON.parse(JSON.stringify(e))).audio,"autoGainControl","googAutoGainControl"),s(e.audio,"noiseSuppression","googNoiseSuppression"),e.audio=a(e.audio)}if(e&&"object"===r(e.video)){var c=e.video.facingMode;c=c&&("object"===r(c)?c:{ideal:c});var u,l=t.version<66;if(c&&("user"===c.exact||"environment"===c.exact||"user"===c.ideal||"environment"===c.ideal)&&(!n.mediaDevices.getSupportedConstraints||!n.mediaDevices.getSupportedConstraints().facingMode||l)&&(delete e.video.facingMode,"environment"===c.exact||"environment"===c.ideal?u=["back","rear"]:"user"!==c.exact&&"user"!==c.ideal||(u=["front"]),u))return n.mediaDevices.enumerateDevices().then(function(t){var n=(t=t.filter(function(e){return"videoinput"===e.kind})).find(function(e){return u.some(function(t){return-1!==e.label.toLowerCase().indexOf(t)})});return!n&&t.length&&-1!==u.indexOf("back")&&(n=t[t.length-1]),n&&(e.video.deviceId=c.exact?{exact:n.deviceId}:{ideal:n.deviceId}),e.video=a(e.video),o("chrome: "+JSON.stringify(e)),i(e)});e.video=a(e.video)}return o("chrome: "+JSON.stringify(e)),i(e)},c=function(e){return{name:{PermissionDeniedError:"NotAllowedError",InvalidStateError:"NotReadableError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotReadableError",MediaDeviceKillSwitchOn:"NotReadableError"}[e.name]||e.name,message:e.message,constraint:e.constraintName,toString:function(){return this.name+(this.message&&": ")+this.message}}};n.getUserMedia=function(e,t,r){s(e,function(e){n.webkitGetUserMedia(e,t,function(e){r&&r(c(e))})})};var u=function(e){return new Promise(function(t,r){n.getUserMedia(e,t,r)})};if(n.mediaDevices||(n.mediaDevices={getUserMedia:u,enumerateDevices:function(){return new Promise(function(t){var n={audio:"audioinput",video:"videoinput"};return e.MediaStreamTrack.getSources(function(e){t(e.map(function(e){return{label:e.label,kind:n[e.kind],deviceId:e.id,groupId:""}}))})})},getSupportedConstraints:function(){return{deviceId:!0,echoCancellation:!0,facingMode:!0,frameRate:!0,height:!0,width:!0}}}),n.mediaDevices.getUserMedia){var l=n.mediaDevices.getUserMedia.bind(n.mediaDevices);n.mediaDevices.getUserMedia=function(e){return s(e,function(e){return l(e).then(function(t){if(e.audio&&!t.getAudioTracks().length||e.video&&!t.getVideoTracks().length)throw t.getTracks().forEach(function(e){e.stop()}),new DOMException("","NotFoundError");return t},function(e){return Promise.reject(c(e))})})}}else n.mediaDevices.getUserMedia=function(e){return u(e)};void 0===n.mediaDevices.addEventListener&&(n.mediaDevices.addEventListener=function(){o("Dummy mediaDevices.addEventListener called.")}),void 0===n.mediaDevices.removeEventListener&&(n.mediaDevices.removeEventListener=function(){o("Dummy mediaDevices.removeEventListener called.")})}},function(e,t,n){"use strict";var r=n(38),i=n(134);e.exports={shimGetUserMedia:n(135),shimPeerConnection:function(e){var t=r.detectBrowser(e);if(e.RTCIceGatherer&&(e.RTCIceCandidate||(e.RTCIceCandidate=function(e){return e}),e.RTCSessionDescription||(e.RTCSessionDescription=function(e){return e}),t.version<15025)){var n=Object.getOwnPropertyDescriptor(e.MediaStreamTrack.prototype,"enabled");Object.defineProperty(e.MediaStreamTrack.prototype,"enabled",{set:function(e){n.set.call(this,e);var t=new Event("enabled");t.enabled=e,this.dispatchEvent(t)}})}!e.RTCRtpSender||"dtmf"in e.RTCRtpSender.prototype||Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get:function(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=new e.RTCDtmfSender(this):"video"===this.track.kind&&(this._dtmf=null)),this._dtmf}}),e.RTCPeerConnection=i(e,t.version)},shimReplaceTrack:function(e){!e.RTCRtpSender||"replaceTrack"in e.RTCRtpSender.prototype||(e.RTCRtpSender.prototype.replaceTrack=e.RTCRtpSender.prototype.setTrack)}}},function(e,t,n){"use strict";var r=n(62);function i(e,t,n,i,o){var a=r.writeRtpDescription(e.kind,t);if(a+=r.writeIceParameters(e.iceGatherer.getLocalParameters()),a+=r.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===n?"actpass":o||"active"),a+="a=mid:"+e.mid+"\r\n",e.rtpSender&&e.rtpReceiver?a+="a=sendrecv\r\n":e.rtpSender?a+="a=sendonly\r\n":e.rtpReceiver?a+="a=recvonly\r\n":a+="a=inactive\r\n",e.rtpSender){var s=e.rtpSender._initialTrackId||e.rtpSender.track.id;e.rtpSender._initialTrackId=s;var c="msid:"+(i?i.id:"-")+" "+s+"\r\n";a+="a="+c,a+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+c,e.sendEncodingParameters[0].rtx&&(a+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" "+c,a+="a=ssrc-group:FID "+e.sendEncodingParameters[0].ssrc+" "+e.sendEncodingParameters[0].rtx.ssrc+"\r\n")}return a+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+r.localCName+"\r\n",e.rtpSender&&e.sendEncodingParameters[0].rtx&&(a+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" cname:"+r.localCName+"\r\n"),a}function o(e,t){var n={codecs:[],headerExtensions:[],fecMechanisms:[]},r=function(e,t){e=parseInt(e,10);for(var n=0;n=14393&&-1===e.indexOf("?transport=udp"):(n=!0,!0)}),delete e.url,e.urls=i?r[0]:r,!!r.length}})})(n.iceServers||[],t),this._iceGatherers=[],n.iceCandidatePoolSize)for(var a=n.iceCandidatePoolSize;a>0;a--)this._iceGatherers.push(new e.RTCIceGatherer({iceServers:n.iceServers,gatherPolicy:n.iceTransportPolicy}));else n.iceCandidatePoolSize=0;this._config=n,this.transceivers=[],this._sdpSessionId=r.generateSessionId(),this._sdpSessionVersion=0,this._dtlsRole=void 0,this._isClosed=!1};Object.defineProperty(l.prototype,"localDescription",{configurable:!0,get:function(){return this._localDescription}}),Object.defineProperty(l.prototype,"remoteDescription",{configurable:!0,get:function(){return this._remoteDescription}}),l.prototype.onicecandidate=null,l.prototype.onaddstream=null,l.prototype.ontrack=null,l.prototype.onremovestream=null,l.prototype.onsignalingstatechange=null,l.prototype.oniceconnectionstatechange=null,l.prototype.onconnectionstatechange=null,l.prototype.onicegatheringstatechange=null,l.prototype.onnegotiationneeded=null,l.prototype.ondatachannel=null,l.prototype._dispatchEvent=function(e,t){this._isClosed||(this.dispatchEvent(t),"function"==typeof this["on"+e]&&this["on"+e](t))},l.prototype._emitGatheringStateChange=function(){var e=new Event("icegatheringstatechange");this._dispatchEvent("icegatheringstatechange",e)},l.prototype.getConfiguration=function(){return this._config},l.prototype.getLocalStreams=function(){return this.localStreams},l.prototype.getRemoteStreams=function(){return this.remoteStreams},l.prototype._createTransceiver=function(e,t){var n=this.transceivers.length>0,r={track:null,iceGatherer:null,iceTransport:null,dtlsTransport:null,localCapabilities:null,remoteCapabilities:null,rtpSender:null,rtpReceiver:null,kind:e,mid:null,sendEncodingParameters:null,recvEncodingParameters:null,stream:null,associatedRemoteMediaStreams:[],wantReceive:!0};if(this.usingBundle&&n)r.iceTransport=this.transceivers[0].iceTransport,r.dtlsTransport=this.transceivers[0].dtlsTransport;else{var i=this._createIceAndDtlsTransports();r.iceTransport=i.iceTransport,r.dtlsTransport=i.dtlsTransport}return t||this.transceivers.push(r),r},l.prototype.addTrack=function(t,n){if(this._isClosed)throw c("InvalidStateError","Attempted to call addTrack on a closed peerconnection.");var r;if(this.transceivers.find(function(e){return e.track===t}))throw c("InvalidAccessError","Track already exists.");for(var i=0;i=15025)e.getTracks().forEach(function(t){n.addTrack(t,e)});else{var r=e.clone();e.getTracks().forEach(function(e,t){var n=r.getTracks()[t];e.addEventListener("enabled",function(e){n.enabled=e.enabled})}),r.getTracks().forEach(function(e){n.addTrack(e,r)})}},l.prototype.removeTrack=function(t){if(this._isClosed)throw c("InvalidStateError","Attempted to call removeTrack on a closed peerconnection.");if(!(t instanceof e.RTCRtpSender))throw new TypeError("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.");var n=this.transceivers.find(function(e){return e.rtpSender===t});if(!n)throw c("InvalidAccessError","Sender was not created by this connection.");var r=n.stream;n.rtpSender.stop(),n.rtpSender=null,n.track=null,n.stream=null,-1===this.transceivers.map(function(e){return e.stream}).indexOf(r)&&this.localStreams.indexOf(r)>-1&&this.localStreams.splice(this.localStreams.indexOf(r),1),this._maybeFireNegotiationNeeded()},l.prototype.removeStream=function(e){var t=this;e.getTracks().forEach(function(e){var n=t.getSenders().find(function(t){return t.track===e});n&&t.removeTrack(n)})},l.prototype.getSenders=function(){return this.transceivers.filter(function(e){return!!e.rtpSender}).map(function(e){return e.rtpSender})},l.prototype.getReceivers=function(){return this.transceivers.filter(function(e){return!!e.rtpReceiver}).map(function(e){return e.rtpReceiver})},l.prototype._createIceGatherer=function(t,n){var r=this;if(n&&t>0)return this.transceivers[0].iceGatherer;if(this._iceGatherers.length)return this._iceGatherers.shift();var i=new e.RTCIceGatherer({iceServers:this._config.iceServers,gatherPolicy:this._config.iceTransportPolicy});return Object.defineProperty(i,"state",{value:"new",writable:!0}),this.transceivers[t].bufferedCandidateEvents=[],this.transceivers[t].bufferCandidates=function(e){var n=!e.candidate||0===Object.keys(e.candidate).length;i.state=n?"completed":"gathering",null!==r.transceivers[t].bufferedCandidateEvents&&r.transceivers[t].bufferedCandidateEvents.push(e)},i.addEventListener("localcandidate",this.transceivers[t].bufferCandidates),i},l.prototype._gather=function(t,n){var i=this,o=this.transceivers[n].iceGatherer;if(!o.onlocalcandidate){var a=this.transceivers[n].bufferedCandidateEvents;this.transceivers[n].bufferedCandidateEvents=null,o.removeEventListener("localcandidate",this.transceivers[n].bufferCandidates),o.onlocalcandidate=function(e){if(!(i.usingBundle&&n>0)){var a=new Event("icecandidate");a.candidate={sdpMid:t,sdpMLineIndex:n};var s=e.candidate,c=!s||0===Object.keys(s).length;if(c)"new"!==o.state&&"gathering"!==o.state||(o.state="completed");else{"new"===o.state&&(o.state="gathering"),s.component=1,s.ufrag=o.getLocalParameters().usernameFragment;var u=r.writeCandidate(s);a.candidate=_extends(a.candidate,r.parseCandidate(u)),a.candidate.candidate=u,a.candidate.toJSON=function(){return{candidate:a.candidate.candidate,sdpMid:a.candidate.sdpMid,sdpMLineIndex:a.candidate.sdpMLineIndex,usernameFragment:a.candidate.usernameFragment}}}var l=r.getMediaSections(i._localDescription.sdp);l[a.candidate.sdpMLineIndex]+=c?"a=end-of-candidates\r\n":"a="+a.candidate.candidate+"\r\n",i._localDescription.sdp=r.getDescription(i._localDescription.sdp)+l.join("");var d=i.transceivers.every(function(e){return e.iceGatherer&&"completed"===e.iceGatherer.state});"gathering"!==i.iceGatheringState&&(i.iceGatheringState="gathering",i._emitGatheringStateChange()),c||i._dispatchEvent("icecandidate",a),d&&(i._dispatchEvent("icecandidate",new Event("icecandidate")),i.iceGatheringState="complete",i._emitGatheringStateChange())}},e.setTimeout(function(){a.forEach(function(e){o.onlocalcandidate(e)})},0)}},l.prototype._createIceAndDtlsTransports=function(){var t=this,n=new e.RTCIceTransport(null);n.onicestatechange=function(){t._updateIceConnectionState(),t._updateConnectionState()};var r=new e.RTCDtlsTransport(n);return r.ondtlsstatechange=function(){t._updateConnectionState()},r.onerror=function(){Object.defineProperty(r,"state",{value:"failed",writable:!0}),t._updateConnectionState()},{iceTransport:n,dtlsTransport:r}},l.prototype._disposeIceAndDtlsTransports=function(e){var t=this.transceivers[e].iceGatherer;t&&(delete t.onlocalcandidate,delete this.transceivers[e].iceGatherer);var n=this.transceivers[e].iceTransport;n&&(delete n.onicestatechange,delete this.transceivers[e].iceTransport);var r=this.transceivers[e].dtlsTransport;r&&(delete r.ondtlsstatechange,delete r.onerror,delete this.transceivers[e].dtlsTransport)},l.prototype._transceive=function(e,n,i){var a=o(e.localCapabilities,e.remoteCapabilities);n&&e.rtpSender&&(a.encodings=e.sendEncodingParameters,a.rtcp={cname:r.localCName,compound:e.rtcpParameters.compound},e.recvEncodingParameters.length&&(a.rtcp.ssrc=e.recvEncodingParameters[0].ssrc),e.rtpSender.send(a)),i&&e.rtpReceiver&&a.codecs.length>0&&("video"===e.kind&&e.recvEncodingParameters&&t<15019&&e.recvEncodingParameters.forEach(function(e){delete e.rtx}),e.recvEncodingParameters.length?a.encodings=e.recvEncodingParameters:a.encodings=[{}],a.rtcp={compound:e.rtcpParameters.compound},e.rtcpParameters.cname&&(a.rtcp.cname=e.rtcpParameters.cname),e.sendEncodingParameters.length&&(a.rtcp.ssrc=e.sendEncodingParameters[0].ssrc),e.rtpReceiver.receive(a))},l.prototype.setLocalDescription=function(e){var t,n,i=this;if(-1===["offer","answer"].indexOf(e.type))return Promise.reject(c("TypeError",'Unsupported type "'+e.type+'"'));if(!a("setLocalDescription",e.type,i.signalingState)||i._isClosed)return Promise.reject(c("InvalidStateError","Can not set local "+e.type+" in state "+i.signalingState));if("offer"===e.type)t=r.splitSections(e.sdp),n=t.shift(),t.forEach(function(e,t){var n=r.parseRtpParameters(e);i.transceivers[t].localCapabilities=n}),i.transceivers.forEach(function(e,t){i._gather(e.mid,t)});else if("answer"===e.type){t=r.splitSections(i._remoteDescription.sdp),n=t.shift();var s=r.matchPrefix(n,"a=ice-lite").length>0;t.forEach(function(e,t){var a=i.transceivers[t],c=a.iceGatherer,u=a.iceTransport,l=a.dtlsTransport,d=a.localCapabilities,p=a.remoteCapabilities;if(!(r.isRejected(e)&&0===r.matchPrefix(e,"a=bundle-only").length||a.rejected)){var f=r.getIceParameters(e,n),h=r.getDtlsParameters(e,n);s&&(h.role="server"),i.usingBundle&&0!==t||(i._gather(a.mid,t),"new"===u.state&&u.start(c,f,s?"controlling":"controlled"),"new"===l.state&&l.start(h));var m=o(d,p);i._transceive(a,m.codecs.length>0,!1)}})}return i._localDescription={type:e.type,sdp:e.sdp},"offer"===e.type?i._updateSignalingState("have-local-offer"):i._updateSignalingState("stable"),Promise.resolve()},l.prototype.setRemoteDescription=function(i){var l=this;if(-1===["offer","answer"].indexOf(i.type))return Promise.reject(c("TypeError",'Unsupported type "'+i.type+'"'));if(!a("setRemoteDescription",i.type,l.signalingState)||l._isClosed)return Promise.reject(c("InvalidStateError","Can not set remote "+i.type+" in state "+l.signalingState));var d={};l.remoteStreams.forEach(function(e){d[e.id]=e});var p=[],f=r.splitSections(i.sdp),h=f.shift(),m=r.matchPrefix(h,"a=ice-lite").length>0,v=r.matchPrefix(h,"a=group:BUNDLE ").length>0;l.usingBundle=v;var y=r.matchPrefix(h,"a=ice-options:")[0];return l.canTrickleIceCandidates=!!y&&y.substr(14).split(" ").indexOf("trickle")>=0,f.forEach(function(a,c){var u=r.splitLines(a),f=r.getKind(a),y=r.isRejected(a)&&0===r.matchPrefix(a,"a=bundle-only").length,g=u[0].substr(2).split(" ")[2],S=r.getDirection(a,h),_=r.parseMsid(a),b=r.getMid(a)||r.generateIdentifier();if(y||"application"===f&&("DTLS/SCTP"===g||"UDP/DTLS/SCTP"===g))l.transceivers[c]={mid:b,kind:f,protocol:g,rejected:!0};else{var E,T,C,R,A,w,k,O,I;!y&&l.transceivers[c]&&l.transceivers[c].rejected&&(l.transceivers[c]=l._createTransceiver(f,!0));var P,D,N=r.parseRtpParameters(a);y||(P=r.getIceParameters(a,h),(D=r.getDtlsParameters(a,h)).role="client"),k=r.parseRtpEncodingParameters(a);var L=r.parseRtcpParameters(a),M=r.matchPrefix(a,"a=end-of-candidates",h).length>0,x=r.matchPrefix(a,"a=candidate:").map(function(e){return r.parseCandidate(e)}).filter(function(e){return 1===e.component});if(("offer"===i.type||"answer"===i.type)&&!y&&v&&c>0&&l.transceivers[c]&&(l._disposeIceAndDtlsTransports(c),l.transceivers[c].iceGatherer=l.transceivers[0].iceGatherer,l.transceivers[c].iceTransport=l.transceivers[0].iceTransport,l.transceivers[c].dtlsTransport=l.transceivers[0].dtlsTransport,l.transceivers[c].rtpSender&&l.transceivers[c].rtpSender.setTransport(l.transceivers[0].dtlsTransport),l.transceivers[c].rtpReceiver&&l.transceivers[c].rtpReceiver.setTransport(l.transceivers[0].dtlsTransport)),"offer"!==i.type||y)"answer"!==i.type||y||(T=(E=l.transceivers[c]).iceGatherer,C=E.iceTransport,R=E.dtlsTransport,A=E.rtpReceiver,w=E.sendEncodingParameters,O=E.localCapabilities,l.transceivers[c].recvEncodingParameters=k,l.transceivers[c].remoteCapabilities=N,l.transceivers[c].rtcpParameters=L,x.length&&"new"===C.state&&(!m&&!M||v&&0!==c?x.forEach(function(e){s(E.iceTransport,e)}):C.setRemoteCandidates(x)),v&&0!==c||("new"===C.state&&C.start(T,P,"controlling"),"new"===R.state&&R.start(D)),!o(E.localCapabilities,E.remoteCapabilities).codecs.filter(function(e){return"rtx"===e.name.toLowerCase()}).length&&E.sendEncodingParameters[0].rtx&&delete E.sendEncodingParameters[0].rtx,l._transceive(E,"sendrecv"===S||"recvonly"===S,"sendrecv"===S||"sendonly"===S),!A||"sendrecv"!==S&&"sendonly"!==S?delete E.rtpReceiver:(I=A.track,_?(d[_.stream]||(d[_.stream]=new e.MediaStream),n(I,d[_.stream]),p.push([I,A,d[_.stream]])):(d.default||(d.default=new e.MediaStream),n(I,d.default),p.push([I,A,d.default]))));else{(E=l.transceivers[c]||l._createTransceiver(f)).mid=b,E.iceGatherer||(E.iceGatherer=l._createIceGatherer(c,v)),x.length&&"new"===E.iceTransport.state&&(!M||v&&0!==c?x.forEach(function(e){s(E.iceTransport,e)}):E.iceTransport.setRemoteCandidates(x)),O=e.RTCRtpReceiver.getCapabilities(f),t<15019&&(O.codecs=O.codecs.filter(function(e){return"rtx"!==e.name})),w=E.sendEncodingParameters||[{ssrc:1001*(2*c+2)}];var j,F=!1;"sendrecv"===S||"sendonly"===S?(F=!E.rtpReceiver,A=E.rtpReceiver||new e.RTCRtpReceiver(E.dtlsTransport,f),F&&(I=A.track,_&&"-"===_.stream||(_?(d[_.stream]||(d[_.stream]=new e.MediaStream,Object.defineProperty(d[_.stream],"id",{get:function(){return _.stream}})),Object.defineProperty(I,"id",{get:function(){return _.track}}),j=d[_.stream]):(d.default||(d.default=new e.MediaStream),j=d.default)),j&&(n(I,j),E.associatedRemoteMediaStreams.push(j)),p.push([I,A,j]))):E.rtpReceiver&&E.rtpReceiver.track&&(E.associatedRemoteMediaStreams.forEach(function(t){var n,r,i=t.getTracks().find(function(e){return e.id===E.rtpReceiver.track.id});i&&(n=i,(r=t).removeTrack(n),r.dispatchEvent(new e.MediaStreamTrackEvent("removetrack",{track:n})))}),E.associatedRemoteMediaStreams=[]),E.localCapabilities=O,E.remoteCapabilities=N,E.rtpReceiver=A,E.rtcpParameters=L,E.sendEncodingParameters=w,E.recvEncodingParameters=k,l._transceive(l.transceivers[c],!1,F)}}}),void 0===l._dtlsRole&&(l._dtlsRole="offer"===i.type?"active":"passive"),l._remoteDescription={type:i.type,sdp:i.sdp},"offer"===i.type?l._updateSignalingState("have-remote-offer"):l._updateSignalingState("stable"),Object.keys(d).forEach(function(t){var n=d[t];if(n.getTracks().length){if(-1===l.remoteStreams.indexOf(n)){l.remoteStreams.push(n);var r=new Event("addstream");r.stream=n,e.setTimeout(function(){l._dispatchEvent("addstream",r)})}p.forEach(function(e){var t=e[0],r=e[1];n.id===e[2].id&&u(l,t,r,[n])})}}),p.forEach(function(e){e[2]||u(l,e[0],e[1],[])}),e.setTimeout(function(){l&&l.transceivers&&l.transceivers.forEach(function(e){e.iceTransport&&"new"===e.iceTransport.state&&e.iceTransport.getRemoteCandidates().length>0&&(console.warn("Timeout for addRemoteCandidate. Consider sending an end-of-candidates notification"),e.iceTransport.addRemoteCandidate({}))})},4e3),Promise.resolve()},l.prototype.close=function(){this.transceivers.forEach(function(e){e.iceTransport&&e.iceTransport.stop(),e.dtlsTransport&&e.dtlsTransport.stop(),e.rtpSender&&e.rtpSender.stop(),e.rtpReceiver&&e.rtpReceiver.stop()}),this._isClosed=!0,this._updateSignalingState("closed")},l.prototype._updateSignalingState=function(e){this.signalingState=e;var t=new Event("signalingstatechange");this._dispatchEvent("signalingstatechange",t)},l.prototype._maybeFireNegotiationNeeded=function(){var t=this;"stable"===this.signalingState&&!0!==this.needNegotiation&&(this.needNegotiation=!0,e.setTimeout(function(){if(t.needNegotiation){t.needNegotiation=!1;var e=new Event("negotiationneeded");t._dispatchEvent("negotiationneeded",e)}},0))},l.prototype._updateIceConnectionState=function(){var e,t={new:0,closed:0,checking:0,connected:0,completed:0,disconnected:0,failed:0};if(this.transceivers.forEach(function(e){e.iceTransport&&!e.rejected&&t[e.iceTransport.state]++}),e="new",t.failed>0?e="failed":t.checking>0?e="checking":t.disconnected>0?e="disconnected":t.new>0?e="new":t.connected>0?e="connected":t.completed>0&&(e="completed"),e!==this.iceConnectionState){this.iceConnectionState=e;var n=new Event("iceconnectionstatechange");this._dispatchEvent("iceconnectionstatechange",n)}},l.prototype._updateConnectionState=function(){var e,t={new:0,closed:0,connecting:0,connected:0,completed:0,disconnected:0,failed:0};if(this.transceivers.forEach(function(e){e.iceTransport&&e.dtlsTransport&&!e.rejected&&(t[e.iceTransport.state]++,t[e.dtlsTransport.state]++)}),t.connected+=t.completed,e="new",t.failed>0?e="failed":t.connecting>0?e="connecting":t.disconnected>0?e="disconnected":t.new>0?e="new":t.connected>0&&(e="connected"),e!==this.connectionState){this.connectionState=e;var n=new Event("connectionstatechange");this._dispatchEvent("connectionstatechange",n)}},l.prototype.createOffer=function(){var n=this;if(n._isClosed)return Promise.reject(c("InvalidStateError","Can not call createOffer after close"));var o=n.transceivers.filter(function(e){return"audio"===e.kind}).length,a=n.transceivers.filter(function(e){return"video"===e.kind}).length,s=arguments[0];if(s){if(s.mandatory||s.optional)throw new TypeError("Legacy mandatory/optional constraints not supported.");void 0!==s.offerToReceiveAudio&&(o=!0===s.offerToReceiveAudio?1:!1===s.offerToReceiveAudio?0:s.offerToReceiveAudio),void 0!==s.offerToReceiveVideo&&(a=!0===s.offerToReceiveVideo?1:!1===s.offerToReceiveVideo?0:s.offerToReceiveVideo)}for(n.transceivers.forEach(function(e){"audio"===e.kind?--o<0&&(e.wantReceive=!1):"video"===e.kind&&--a<0&&(e.wantReceive=!1)});o>0||a>0;)o>0&&(n._createTransceiver("audio"),o--),a>0&&(n._createTransceiver("video"),a--);var u=r.writeSessionBoilerplate(n._sdpSessionId,n._sdpSessionVersion++);n.transceivers.forEach(function(i,o){var a=i.track,s=i.kind,c=i.mid||r.generateIdentifier();i.mid=c,i.iceGatherer||(i.iceGatherer=n._createIceGatherer(o,n.usingBundle));var u=e.RTCRtpSender.getCapabilities(s);t<15019&&(u.codecs=u.codecs.filter(function(e){return"rtx"!==e.name})),u.codecs.forEach(function(e){"H264"===e.name&&void 0===e.parameters["level-asymmetry-allowed"]&&(e.parameters["level-asymmetry-allowed"]="1"),i.remoteCapabilities&&i.remoteCapabilities.codecs&&i.remoteCapabilities.codecs.forEach(function(t){e.name.toLowerCase()===t.name.toLowerCase()&&e.clockRate===t.clockRate&&(e.preferredPayloadType=t.payloadType)})}),u.headerExtensions.forEach(function(e){(i.remoteCapabilities&&i.remoteCapabilities.headerExtensions||[]).forEach(function(t){e.uri===t.uri&&(e.id=t.id)})});var l=i.sendEncodingParameters||[{ssrc:1001*(2*o+1)}];a&&t>=15019&&"video"===s&&!l[0].rtx&&(l[0].rtx={ssrc:l[0].ssrc+1}),i.wantReceive&&(i.rtpReceiver=new e.RTCRtpReceiver(i.dtlsTransport,s)),i.localCapabilities=u,i.sendEncodingParameters=l}),"max-compat"!==n._config.bundlePolicy&&(u+="a=group:BUNDLE "+n.transceivers.map(function(e){return e.mid}).join(" ")+"\r\n"),u+="a=ice-options:trickle\r\n",n.transceivers.forEach(function(e,t){u+=i(e,e.localCapabilities,"offer",e.stream,n._dtlsRole),u+="a=rtcp-rsize\r\n",!e.iceGatherer||"new"===n.iceGatheringState||0!==t&&n.usingBundle||(e.iceGatherer.getLocalCandidates().forEach(function(e){e.component=1,u+="a="+r.writeCandidate(e)+"\r\n"}),"completed"===e.iceGatherer.state&&(u+="a=end-of-candidates\r\n"))});var l=new e.RTCSessionDescription({type:"offer",sdp:u});return Promise.resolve(l)},l.prototype.createAnswer=function(){var n=this;if(n._isClosed)return Promise.reject(c("InvalidStateError","Can not call createAnswer after close"));if("have-remote-offer"!==n.signalingState&&"have-local-pranswer"!==n.signalingState)return Promise.reject(c("InvalidStateError","Can not call createAnswer in signalingState "+n.signalingState));var a=r.writeSessionBoilerplate(n._sdpSessionId,n._sdpSessionVersion++);n.usingBundle&&(a+="a=group:BUNDLE "+n.transceivers.map(function(e){return e.mid}).join(" ")+"\r\n"),a+="a=ice-options:trickle\r\n";var s=r.getMediaSections(n._remoteDescription.sdp).length;n.transceivers.forEach(function(e,r){if(!(r+1>s)){if(e.rejected)return"application"===e.kind?"DTLS/SCTP"===e.protocol?a+="m=application 0 DTLS/SCTP 5000\r\n":a+="m=application 0 "+e.protocol+" webrtc-datachannel\r\n":"audio"===e.kind?a+="m=audio 0 UDP/TLS/RTP/SAVPF 0\r\na=rtpmap:0 PCMU/8000\r\n":"video"===e.kind&&(a+="m=video 0 UDP/TLS/RTP/SAVPF 120\r\na=rtpmap:120 VP8/90000\r\n"),void(a+="c=IN IP4 0.0.0.0\r\na=inactive\r\na=mid:"+e.mid+"\r\n");var c;e.stream&&("audio"===e.kind?c=e.stream.getAudioTracks()[0]:"video"===e.kind&&(c=e.stream.getVideoTracks()[0]),c&&t>=15019&&"video"===e.kind&&!e.sendEncodingParameters[0].rtx&&(e.sendEncodingParameters[0].rtx={ssrc:e.sendEncodingParameters[0].ssrc+1}));var u=o(e.localCapabilities,e.remoteCapabilities);!u.codecs.filter(function(e){return"rtx"===e.name.toLowerCase()}).length&&e.sendEncodingParameters[0].rtx&&delete e.sendEncodingParameters[0].rtx,a+=i(e,u,"answer",e.stream,n._dtlsRole),e.rtcpParameters&&e.rtcpParameters.reducedSize&&(a+="a=rtcp-rsize\r\n")}});var u=new e.RTCSessionDescription({type:"answer",sdp:a});return Promise.resolve(u)},l.prototype.addIceCandidate=function(e){var t,n=this;return e&&void 0===e.sdpMLineIndex&&!e.sdpMid?Promise.reject(new TypeError("sdpMLineIndex or sdpMid required")):new Promise(function(i,o){if(!n._remoteDescription)return o(c("InvalidStateError","Can not add ICE candidate without a remote description"));if(e&&""!==e.candidate){var a=e.sdpMLineIndex;if(e.sdpMid)for(var u=0;u0?r.parseCandidate(e.candidate):{};if("tcp"===d.protocol&&(0===d.port||9===d.port))return i();if(d.component&&1!==d.component)return i();if((0===a||a>0&&l.iceTransport!==n.transceivers[0].iceTransport)&&!s(l.iceTransport,d))return o(c("OperationError","Can not add ICE candidate"));var p=e.candidate.trim();0===p.indexOf("a=")&&(p=p.substr(2)),(t=r.getMediaSections(n._remoteDescription.sdp))[a]+="a="+(d.type?p:"end-of-candidates")+"\r\n",n._remoteDescription.sdp=r.getDescription(n._remoteDescription.sdp)+t.join("")}else for(var f=0;f55&&"autoGainControl"in n.mediaDevices.getSupportedConstraints())){var d=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])},p=n.mediaDevices.getUserMedia.bind(n.mediaDevices);if(n.mediaDevices.getUserMedia=function(e){return"object"===r(e)&&"object"===r(e.audio)&&(e=JSON.parse(JSON.stringify(e)),d(e.audio,"autoGainControl","mozAutoGainControl"),d(e.audio,"noiseSuppression","mozNoiseSuppression")),p(e)},a&&a.prototype.getSettings){var f=a.prototype.getSettings;a.prototype.getSettings=function(){var e=f.apply(this,arguments);return d(e,"mozAutoGainControl","autoGainControl"),d(e,"mozNoiseSuppression","noiseSuppression"),e}}if(a&&a.prototype.applyConstraints){var h=a.prototype.applyConstraints;a.prototype.applyConstraints=function(e){return"audio"===this.kind&&"object"===r(e)&&(e=JSON.parse(JSON.stringify(e)),d(e,"autoGainControl","mozAutoGainControl"),d(e,"noiseSuppression","mozNoiseSuppression")),h.apply(this,[e])}}}n.getUserMedia=function(e,r,o){if(t.version<44)return c(e,r,o);i.deprecated("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(r,o)}}},function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var i=n(38),o={shimLocalStreamsAPI:function(e){if("object"===r(e)&&e.RTCPeerConnection){if("getLocalStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),"getStreamById"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getStreamById=function(e){var t=null;return this._localStreams&&this._localStreams.forEach(function(n){n.id===e&&(t=n)}),this._remoteStreams&&this._remoteStreams.forEach(function(n){n.id===e&&(t=n)}),t}),!("addStream"in e.RTCPeerConnection.prototype)){var t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addStream=function(e){this._localStreams||(this._localStreams=[]),-1===this._localStreams.indexOf(e)&&this._localStreams.push(e);var n=this;e.getTracks().forEach(function(r){t.call(n,r,e)})},e.RTCPeerConnection.prototype.addTrack=function(e,n){n&&(this._localStreams?-1===this._localStreams.indexOf(n)&&this._localStreams.push(n):this._localStreams=[n]),t.call(this,e,n)}}"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(e){this._localStreams||(this._localStreams=[]);var t=this._localStreams.indexOf(e);if(-1!==t){this._localStreams.splice(t,1);var n=this,r=e.getTracks();this.getSenders().forEach(function(e){-1!==r.indexOf(e.track)&&n.removeTrack(e)})}})}},shimRemoteStreamsAPI:function(e){"object"===r(e)&&e.RTCPeerConnection&&("getRemoteStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),"onaddstream"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"onaddstream",{get:function(){return this._onaddstream},set:function(e){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=e),this.addEventListener("track",this._onaddstreampoly=function(e){var t=e.streams[0];if(this._remoteStreams||(this._remoteStreams=[]),!(this._remoteStreams.indexOf(t)>=0)){this._remoteStreams.push(t);var n=new Event("addstream");n.stream=e.streams[0],this.dispatchEvent(n)}}.bind(this))}}))},shimCallbacksAPI:function(e){if("object"===r(e)&&e.RTCPeerConnection){var t=e.RTCPeerConnection.prototype,n=t.createOffer,i=t.createAnswer,o=t.setLocalDescription,a=t.setRemoteDescription,s=t.addIceCandidate;t.createOffer=function(e,t){var r=arguments.length>=2?arguments[2]:arguments[0],i=n.apply(this,[r]);return t?(i.then(e,t),Promise.resolve()):i},t.createAnswer=function(e,t){var n=arguments.length>=2?arguments[2]:arguments[0],r=i.apply(this,[n]);return t?(r.then(e,t),Promise.resolve()):r};var c=function(e,t,n){var r=o.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r};t.setLocalDescription=c,c=function(e,t,n){var r=a.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.setRemoteDescription=c,c=function(e,t,n){var r=s.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.addIceCandidate=c}},shimGetUserMedia:function(e){var t=e&&e.navigator;t.getUserMedia||(t.webkitGetUserMedia?t.getUserMedia=t.webkitGetUserMedia.bind(t):t.mediaDevices&&t.mediaDevices.getUserMedia&&(t.getUserMedia=function(e,n,r){t.mediaDevices.getUserMedia(e).then(n,r)}.bind(t)))},shimRTCIceServerUrls:function(e){var t=e.RTCPeerConnection;e.RTCPeerConnection=function(e,n){if(e&&e.iceServers){for(var r=[],o=0;o=r)return e;var i=n[t];switch(t+=1,e){case"%%":return"%";case"%s":return String(i);case"%d":return Number(i);case"%v":return""}})}.apply(null,r)},a=["v","o","s","i","u","e","p","c","b","t","r","z","a"],s=["i","c","b","a"];e.exports=function(e,t){t=t||{},null==e.version&&(e.version=0),null==e.name&&(e.name=" "),e.media.forEach(function(e){null==e.payloads&&(e.payloads="")});var n=t.outerOrder||a,i=t.innerOrder||s,c=[];return n.forEach(function(t){r[t].forEach(function(n){n.name in e&&null!=e[n.name]?c.push(o(t,n,e)):n.push in e&&null!=e[n.push]&&e[n.push].forEach(function(e){c.push(o(t,n,e))})})}),e.media.forEach(function(e){c.push(o("m",r.m[0],e)),i.forEach(function(t){r[t].forEach(function(n){n.name in e&&null!=e[n.name]?c.push(o(t,n,e)):n.push in e&&null!=e[n.push]&&e[n.push].forEach(function(e){c.push(o(t,n,e))})})})}),c.join("\r\n")+"\r\n"}},function(e,t,n){t.Interop=n(143)},function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e})(e)}var i=n(144),o=n(145);function a(e){"number"==typeof e.mid&&(e.mid=e.mid.toString())}function s(){this.cache={mlB2UMap:{},mlU2BMap:{}}}e.exports=s,s.prototype.candidateToUnifiedPlan=function(e){var t=new RTCIceCandidate(e);return t.sdpMLineIndex=this.cache.mlB2UMap[t.sdpMLineIndex],t},s.prototype.candidateToPlanB=function(e){var t=new RTCIceCandidate(e);if(0===t.sdpMid.indexOf("audio"))t.sdpMid="audio";else{if(0!==t.sdpMid.indexOf("video"))throw new Error("candidate with "+t.sdpMid+" not allowed");t.sdpMid="video"}return t.sdpMLineIndex=this.cache.mlU2BMap[t.sdpMLineIndex],t},s.prototype.getFirstSendingIndexFromAnswer=function(e){if(!this.cache.answer)return null;var t=i.parse(this.cache.answer);if(t&&t.media&&Array.isArray(t.media))for(var n=0;n3||!n.media.every(function(e){return-1!==["video","audio","data"].indexOf(e.mid)}))return console.warn("This description does not look like Plan B."),e;var s=[];n.media.forEach(function(e){s.push(e.mid)});var c,u=!1;if(void 0!==n.groups&&Array.isArray(n.groups)&&(u=n.groups.every(function(e){return"BUNDLE"!==e.type||o.apply(e.mids.sort(),[s.sort()])})),!u)throw new Error("Cannot convert to Unified Plan because m-lines that are not bundled were found.");void 0!==this.cache[e.type]&&(c=i.parse(this.cache[e.type]));var l={audio:{},video:{}},d={},p=0,f=0;if(n.media.forEach(function(n){if(("string"!=typeof n.rtcpMux||"rtcp-mux"!==n.rtcpMux)&&"inactive"!==n.direction&&"application"!==n.type)throw new Error("Cannot convert to Unified Plan because m-lines without the rtcp-mux attribute were found.");if("application"===n.type){var i=null;return c&&c.media&&(i=c.media.find(function(e){return"application"===e.type})),void(i?d[i.mid]=i:d[n.mid]=n)}var o=n.sources,s=n.ssrcGroups,u=n.candidates,h=n.iceUfrag,m=n.icePwd,v=n.fingerprint,y=n.port;delete n.sources,delete n.ssrcGroups,delete n.candidates,delete n.iceUfrag,delete n.icePwd,delete n.fingerprint,delete n.port,delete n.mid;var g={};void 0!==s&&Array.isArray(s)&&s.forEach(function(e){"SIM"!==e.semantics&&void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&e.ssrcs.forEach(function(t){void 0===g[t]&&(g[t]=[]),g[t].push(e)})});var S={};"object"===r(o)&&Object.keys(o).forEach(function(i){var s;if("offer"!==e.type||o[i].msid)if(void 0!==g[i]&&Array.isArray(g[i])&&g[i].some(function(e){return e.ssrcs.some(function(e){if("object"===r(S[e]))return s=S[e],!0})}),"object"===r(s))s.sources[i]=o[i],delete o[i].msid;else{if(s=Object.create(n),S[i]=s,void 0!==o[i].msid&&(s.msid=o[i].msid,delete o[i].msid),s.sources={},s.sources[i]=o[i],s.ssrcGroups=g[i],void 0!==c&&void 0!==c.media&&Array.isArray(c.media)&&c.media.forEach(function(e){"object"===r(e.sources)&&Object.keys(e.sources).forEach(function(t){t===i&&(s.mid=e.mid)})}),a(s),void 0===s.mid){if("answer"===e.type)throw new Error("An unmapped SSRC was found.");s.mid=[n.type,"-",i].join("")}s.candidates=u,s.iceUfrag=h,s.icePwd=m,s.fingerprint=v,s.port=y,d[s.mid]=s,t.cache.mlU2BMap[f]=p,void 0===t.cache.mlB2UMap[p]&&(t.cache.mlB2UMap[p]=f),f++}else l[n.type][i]=o[i]}),p++}),n.media=[],s=[],"answer"===e.type)for(var h=0;h0&&null===(r=t.getFirstSendingIndexFromAnswer(e)))for(var i=0;ir){var o=n.media[r];Object.keys(l[e]).forEach(function(t){o.sources&&o.sources[t]&&console.warn("Replacing an existing SSRC."),o.sources||(o.sources={}),o.sources[t]=l[e][t]})}}}),n.groups.some(function(e){if("BUNDLE"===e.type)return e.mids=s.join(" "),!0}),n.msidSemantic={semantic:"WMS",token:"*"};var v=i.write(n);return this.cache[e.type]=v,new RTCSessionDescription({type:e.type,sdp:v})};var c={inactive:0,recvonly:1,sendonly:2,sendrecv:3}},function(e,t,n){var r=n(22);t.write=function(e,t){return void 0!==e&&void 0!==e.media&&Array.isArray(e.media)&&e.media.forEach(function(e){void 0!==e.sources&&0!==Object.keys(e.sources).length&&(e.ssrcs=[],Object.keys(e.sources).forEach(function(t){var n=e.sources[t];Object.keys(n).forEach(function(r){e.ssrcs.push({id:t,attribute:r,value:n[r]})})}),delete e.sources),void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&(e.ssrcs=e.ssrcs.join(" "))})}),void 0!==e&&void 0!==e.groups&&Array.isArray(e.groups)&&e.groups.forEach(function(e){void 0!==e.mids&&Array.isArray(e.mids)&&(e.mids=e.mids.join(" "))}),r.write(e,t)},t.parse=function(e){var t=r.parse(e);return void 0!==t&&void 0!==t.media&&Array.isArray(t.media)&&t.media.forEach(function(e){void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&(e.sources={},e.ssrcs.forEach(function(t){e.sources[t.id]||(e.sources[t.id]={}),e.sources[t.id][t.attribute]=t.value}),delete e.ssrcs),void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){"string"==typeof e.ssrcs&&(e.ssrcs=e.ssrcs.split(" "))})}),void 0!==t&&void 0!==t.groups&&Array.isArray(t.groups)&&t.groups.forEach(function(e){"string"==typeof e.mids&&(e.mids=e.mids.split(" "))}),t}},function(e,t){e.exports=function e(t){if(!t)return!1;if(this.length!=t.length)return!1;for(var n=0,r=this.length;n2)return e;if(2==n&&0===r)return e;if(1===n)t=e.ssrcs[0].id;else{var i=e.ssrcGroups.filter(function(e){return"FID"===e.semantics})[0];if(!i)return e;t=parseInt(i.ssrcs.split(" ")[0])}return console.log("SdpSimulcast: current ssrc cache: ",this.ssrcCache),console.log("SdpSimulcast: parsed primary ssrc "+t),-1!==this.ssrcCache.indexOf(t)?(console.log("SdpSimulcast: Have seen primary ssrc before, filling in data from cache"),e=this._fillInSourceDataFromCache(e)):(console.log("SdpSimulcast: Have not seen primary ssrc before, generating source data"),e=this._generateSourceData(e,t)),this.ssrcCache=this._parseSimLayers(e),e},u.prototype.mungeRemoteDescription=function(e){if(!d(e))return e;var t=r.parse(e.sdp),n=this;return l(t,function(e){n.options.explodeRemoteSimulcast?(function(e){if(e&&Array.isArray(e.ssrcGroups))for(var t=o(e),n=[],r=e.ssrcGroups.length;r--;)if("SIM"===e.ssrcGroups[r].semantics){for(var i=e.ssrcGroups[r].ssrcs.split(" "),s=0;s=0)){var i=e[r];Object.keys(i).forEach(function(e){n.push({id:r,attribute:e,value:i[e]})})}})}return n},t.parseSsrcs=function(e){var t={};return void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&e.ssrcs.forEach(function(e){t[e.id]||(t[e.id]={}),t[e.id][e.attribute]=e.value}),t}},function(e,t){e.exports=function(e,t,n,r){this.blob=e,this.name=t,this.startTime=n,this.wordArray=r}},function(e,t,n){var r=n(150),i=n(151),o=n(64),a=function(){this.url=(function(){var e="config does not contain an url to a Sphinx4 https server";if(void 0===config.sphinxURL)console.log(e);else{var t=config.sphinxURL;if(void 0!==t.includes&&t.includes("https://"))return t;console.log(e)}})()};a.prototype=Object.create(r.prototype),a.constructor=a,a.prototype.sendRequest=function(e,t){console.log("sending an audio file to ".concat(this.url)),console.log("the audio file being sent: ".concat(e));var n=new XMLHttpRequest;n.onreadystatechange=function(){if(n.readyState===XMLHttpRequest.DONE&&200===n.status)t(n.responseText);else if(n.readyState===XMLHttpRequest.DONE)throw new Error("unable to accept response from sphinx server. status: ".concat(n.status))},n.open("POST",this.url),n.setRequestHeader("Content-Type",o.determineCorrectFileType()),n.send(e),console.log("send ".concat(e))},a.prototype.formatResponse=function(e){var t=JSON.parse(e).objects;t.shift();var n=[];return t.forEach(function(e){return e.filler||n.push(new i(e.word,e.start,e.end))}),n},a.prototype.verify=function(e){if(console.log("response from server:".concat(e.toString())),"string"!=typeof e)return!1;var t;try{t=JSON.parse(e)}catch(e){return console.log(e),!1}if(void 0===t.objects)return!1;var n=t.objects;return!(!n[0]||!n[0]["session-id"])},e.exports=a},function(e,t){var n=function(){throw new Error("TranscriptionService is abstract and cannot becreated")};n.prototype.send=function(e,t){var n=this;this.sendRequest(e.blob,function(r){n.verify(r)?e.wordArray=n.formatResponse(r):(console.log("the retrieved response from the server is not valid!"),e.wordArray=[]),t(e)})},n.prototype.sendRequest=function(e,t){throw new Error("TranscriptionService.sendRequest is abstract")},n.prototype.formatResponse=function(e){throw new Error("TranscriptionService.format is abstract")},n.prototype.verify=function(e){throw new Error("TranscriptionService.verify is abstract")},e.exports=n},function(e,t){var n=function(e,t,n){this.word=e,this.begin=t,this.end=n};n.prototype.getWord=function(){return this.word},n.prototype.getBeginTime=function(){return this.begin},n.prototype.getEndTime=function(){return this.end},e.exports=n}])},"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.JitsiMeetJS=t():e.JitsiMeetJS=t()},990,[16]); -__d(function(e,t,n,r,i,o,a){!(function(e,t){"use strict";"object"==typeof i&&"object"==typeof i.exports?i.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)})("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},v=function(e){return null!=e&&e===e.window},y={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var i,o,a=(n=n||r).createElement("script");if(a.text=e,t)for(i in y)(o=t[i]||t.getAttribute&&t.getAttribute(i))&&a.setAttribute(i,o);n.head.appendChild(a).parentNode.removeChild(a)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b=function e(t,n){return new e.fn.init(t,n)},w=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function T(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!v(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}b.fn=b.prototype={jquery:"3.4.0",constructor:b,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return b.each(this,e)},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|[\\x20\\t\\r\\n\\f])[\\x20\\t\\r\\n\\f]*"),F=new RegExp("[\\x20\\t\\r\\n\\f]|>"),B=new RegExp(R),_=new RegExp("^(?:\\\\.|[\\w-]|[^\0-\\xa0])+$"),z={ID:new RegExp("^#((?:\\\\.|[\\w-]|[^\0-\\xa0])+)"),CLASS:new RegExp("^\\.((?:\\\\.|[\\w-]|[^\0-\\xa0])+)"),TAG:new RegExp("^((?:\\\\.|[\\w-]|[^\0-\\xa0])+|[*])"),ATTR:new RegExp("^\\[[\\x20\\t\\r\\n\\f]*((?:\\\\.|[\\w-]|[^\0-\\xa0])+)(?:[\\x20\\t\\r\\n\\f]*([*^$|!~]?=)[\\x20\\t\\r\\n\\f]*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|((?:\\\\.|[\\w-]|[^\0-\\xa0])+))|)[\\x20\\t\\r\\n\\f]*\\]"),PSEUDO:new RegExp("^:((?:\\\\.|[\\w-]|[^\0-\\xa0])+)(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|\\[[\\x20\\t\\r\\n\\f]*((?:\\\\.|[\\w-]|[^\0-\\xa0])+)(?:[\\x20\\t\\r\\n\\f]*([*^$|!~]?=)[\\x20\\t\\r\\n\\f]*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|((?:\\\\.|[\\w-]|[^\0-\\xa0])+))|)[\\x20\\t\\r\\n\\f]*\\])*)|.*)\\)|)"),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\([\\x20\\t\\r\\n\\f]*(even|odd|(([+-]|)(\\d*)n|)[\\x20\\t\\r\\n\\f]*(?:([+-]|)[\\x20\\t\\r\\n\\f]*(\\d+)|))[\\x20\\t\\r\\n\\f]*\\)|)","i"),bool:new RegExp("^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$","i"),needsContext:new RegExp("^[\\x20\\t\\r\\n\\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\([\\x20\\t\\r\\n\\f]*((?:-\\d)?\\d*)[\\x20\\t\\r\\n\\f]*\\)|)(?=[^-]|$)","i")},U=/HTML$/i,X=/^(?:input|select|textarea|button)$/i,V=/^h\d$/i,G=/^[^{]+\{\s*\[native \w/,Y=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Q=/[+~]/,J=new RegExp("\\\\([\\da-f]{1,6}[\\x20\\t\\r\\n\\f]?|([\\x20\\t\\r\\n\\f])|.)","ig"),K=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Z=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ee=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},te=function(){p()},ne=ve(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(j=O.call(w.childNodes),w.childNodes),j[w.childNodes.length].nodeType}catch(e){H={apply:j.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function re(e,t,r,i){var o,s,l,c,f,h,y,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=Y.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return H.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return H.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!A[e+" "]&&(!v||!v.test(e))&&(1!==T||"object"!==t.nodeName.toLowerCase())){if(y=e,m=t,1===T&&F.test(e)){for((c=t.getAttribute("id"))?c=c.replace(Z,ee):t.setAttribute("id",c=b),s=(h=a(e)).length;s--;)h[s]="#"+c+" "+ge(h[s]);y=h.join(","),m=Q.test(e)&&de(t.parentNode)||t}try{return H.apply(r,m.querySelectorAll(y)),r}catch(t){A(e,!0)}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(I,"$1"),t,r,i)}function ie(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function oe(e){return e[b]=!0,e}function ae(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function se(e,t){for(var n=e.split("|"),i=n.length;i--;)r.attrHandle[n[i]]=t}function ue(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function le(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function ce(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function fe(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ne(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function pe(e){return oe(function(t){return t=+t,oe(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function de(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=re.support={},o=re.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!U.test(t||n&&n.nodeName||"HTML")},p=re.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(h=(d=a).documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",te,!1):i.attachEvent&&i.attachEvent("onunload",te)),n.attributes=ae(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ae(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=G.test(d.getElementsByClassName),n.getById=ae(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(J,K);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(J,K);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=G.test(d.querySelectorAll))&&(ae(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]=[\\x20\\t\\r\\n\\f]*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\[[\\x20\\t\\r\\n\\f]*(?:value|checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]")}),ae(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name[\\x20\\t\\r\\n\\f]*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(n.matchesSelector=G.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ae(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",R)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=G.test(h.compareDocumentPosition),x=t||G.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},N=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?P(c,e)-P(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?P(c,e)-P(c,t):0;if(i===o)return ue(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?ue(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},re.matches=function(e,t){return re(e,null,null,t)},re.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),n.matchesSelector&&g&&!A[t+" "]&&(!y||!y.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){A(t,!0)}return re(t,d,null,[e]).length>0},re.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},re.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&D.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},re.escape=function(e){return(e+"").replace(Z,ee)},re.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},re.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(N),f){for(;t=e[o++];)t===e[o]&&(i=r.push(o));for(;i--;)e.splice(r[i],1)}return c=null,e},i=re.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},(r=re.selectors={cacheLength:50,createPseudo:oe,match:z,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(J,K),e[3]=(e[3]||e[4]||e[5]||"").replace(J,K),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||re.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&re.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&B.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(J,K).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|[\\x20\\t\\r\\n\\f])"+e+"([\\x20\\t\\r\\n\\f]|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=re.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(M," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(x=(d=(l=(c=(f=(p=v)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||re.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?oe(function(e,n){for(var r,o=i(e,t),a=o.length;a--;)e[r=P(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:oe(function(e){var t=[],n=[],r=s(e.replace(I,"$1"));return r[b]?oe(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:oe(function(e){return function(t){return re(e,t).length>0}}),contains:oe(function(e){return e=e.replace(J,K),function(t){return(t.textContent||i(t)).indexOf(e)>-1}}),lang:oe(function(e){return _.test(e||"")||re.error("unsupported lang: "+e),e=e.replace(J,K).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:fe(!1),disabled:fe(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return V.test(e.nodeName)},input:function(e){return X.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:pe(function(){return[0]}),last:pe(function(e,t){return[t-1]}),eq:pe(function(e,t,n){return[n<0?n+t:n]}),even:pe(function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e}),gt:pe(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function me(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else y=xe(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function we(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=ve(function(e){return e===t},s,!0),f=ve(function(e){return P(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&ye(p),u>1&&ge(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(I,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,v,y=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){for(h=0,a||f.ownerDocument===d||(p(f),s=!g);v=e[h++];)if(v(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!v&&f)&&y--,o&&x.push(f))}if(y+=m,n&&m!==y){for(h=0;v=t[h++];)v(x,b,a,s);if(o){if(y>0)for(;m--;)x[m]||b[m]||(b[m]=q.call(u));b=xe(b)}H.apply(u,b),c&&!o&&b.length>0&&y+t.length>1&&re.uniqueSort(u)}return c&&(T=E,l=w),x};return n?oe(o):o}return he.prototype=r.filters=r.pseudos,r.setFilters=new he,a=re.tokenize=function(e,t){var n,i,o,a,s,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);for(s=e,u=[],l=r.preFilter;s;){for(a in n&&!(i=W.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),n=!1,(i=$.exec(s))&&(n=i.shift(),o.push({value:n,type:i[0].replace(I," ")}),s=s.slice(n.length)),r.filter)!(i=z[a].exec(s))||l[a]&&!(i=l[a](i))||(n=i.shift(),o.push({value:n,type:a,matches:i}),s=s.slice(n.length));if(!n)break}return t?s.length:s?re.error(e):k(e,u).slice(0)},s=re.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){for(t||(t=a(e)),n=t.length;n--;)(o=we(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Te(i,r))).selector=e}return o},u=re.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(J,K),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(o=z.needsContext.test(e)?0:u.length;o--&&(l=u[o],!r.relative[c=l.type]);)if((f=r.find[c])&&(i=f(l.matches[0].replace(J,K),Q.test(u[0].type)&&de(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ge(u)))return H.apply(n,i),n;break}}return(p||s(e,d))(i,t,!g,n,!t||Q.test(e)&&de(t.parentNode)||t),n},n.sortStable=b.split("").sort(N).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ae(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ae(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||se("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ae(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||se("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ae(function(e){return null==e.getAttribute("disabled")})||se("checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),re})(e);b.find=C,b.expr=C.selectors,b.expr[":"]=b.expr.pseudos,b.uniqueSort=b.unique=C.uniqueSort,b.text=C.getText,b.isXMLDoc=C.isXML,b.contains=C.contains,b.escapeSelector=C.escape;var E=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&b(e).is(n))break;r.push(e)}return r},k=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},S=b.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,t,n){return g(t)?b.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?b.grep(e,function(e){return e===t!==n}):"string"!=typeof t?b.grep(e,function(e){return u.call(t,e)>-1!==n}):b.filter(t,e,n)}b.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?b.find.matchesSelector(r,e)?[r]:[]:b.find.matches(e,b.grep(t,function(e){return 1===e.nodeType}))},b.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(b(e).filter(function(){for(t=0;t1?b.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&S.test(e)?b(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(b.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:q.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof b?t[0]:t,b.merge(this,b.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),N.test(i[1])&&b.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(b):b.makeArray(e,this)}).prototype=b.fn,j=b(r);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}b.fn.extend({has:function(e){var t=b(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&b.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?b.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(b(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(b.uniqueSort(b.merge(this.get(),b(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return E(e,"parentNode")},parentsUntil:function(e,t,n){return E(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return E(e,"nextSibling")},prevAll:function(e){return E(e,"previousSibling")},nextUntil:function(e,t,n){return E(e,"nextSibling",n)},prevUntil:function(e,t,n){return E(e,"previousSibling",n)},siblings:function(e){return k((e.parentNode||{}).firstChild,e)},children:function(e){return k(e.firstChild)},contents:function(e){return void 0!==e.contentDocument?e.contentDocument:(A(e,"template")&&(e=e.content||e),b.merge([],e.childNodes))}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),this.length>1&&(H[e]||b.uniqueSort(i),L.test(e)&&i.reverse()),this.pushStack(i)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return b.each(e.match(P)||[],function(e,n){t[n]=!0}),t}function M(e){return e}function I(e){throw e}function W(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}b.Callbacks=function(e){e="string"==typeof e?R(e):b.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?b.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},b.extend({Deferred:function(t){var n=[["notify","progress",b.Callbacks("memory"),b.Callbacks("memory"),2],["resolve","done",b.Callbacks("once memory"),b.Callbacks("once memory"),0,"resolved"],["reject","fail",b.Callbacks("once memory"),b.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return b.Deferred(function(t){b.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==I&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(b.Deferred.getStackHook&&(c.stackTrace=b.Deferred.getStackHook()),e.setTimeout(c))}}return b.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:M,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:M)),n[2][3].add(a(0,e,g(r)?r:I))}).promise()},promise:function(e){return null!=e?b.extend(e,i):i}},o={};return b.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=b.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&(W(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();for(;n--;)W(i[n],s(n),a.reject);return a.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;b.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&$.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},b.readyException=function(t){e.setTimeout(function(){throw t})};var F=b.Deferred();function B(){r.removeEventListener("DOMContentLoaded",B),e.removeEventListener("load",B),b.ready()}b.fn.ready=function(e){return F.then(e).catch(function(e){b.readyException(e)}),this},b.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--b.readyWait:b.isReady)||(b.isReady=!0,!0!==e&&--b.readyWait>0||F.resolveWith(r,[b]))}}),b.ready.then=F.then,"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(b.ready):(r.addEventListener("DOMContentLoaded",B),e.addEventListener("load",B));var _=function e(t,n,r,i,o,a,s){var u=0,l=t.length,c=null==r;if("object"===x(r))for(u in o=!0,r)e(t,n,u,r[u],!0,a,s);else if(void 0!==i&&(o=!0,g(i)||(s=!0),c&&(s?(n.call(t,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;u1,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),b.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Q.get(e,t),n&&(!r||Array.isArray(n)?r=Q.access(e,t,b.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){b.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Q.get(e,n)||Q.access(e,n,{empty:b.Callbacks("once memory").add(function(){Q.remove(e,[t+"queue",n])})})}}),b.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ve=/^$|^module$|\/(?:java|ecma)script/i,ye={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function me(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?b.merge([e],n):n}function xe(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=ae(o),a=me(f.appendChild(o),"script"),l&&xe(a),n)for(c=0;o=a[c++];)ve.test(o.type||"")&&n.push(o);return f}be=r.createDocumentFragment().appendChild(r.createElement("div")),(we=r.createElement("input")).setAttribute("type","radio"),we.setAttribute("checked","checked"),we.setAttribute("name","t"),be.appendChild(we),h.checkClone=be.cloneNode(!0).cloneNode(!0).lastChild.checked,be.innerHTML="",h.noCloneChecked=!!be.cloneNode(!0).lastChild.defaultValue;var Ee=/^key/,ke=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Se=/^([^.]*)(?:\.(.+)|)/;function Ae(){return!0}function Ne(){return!1}function De(e,t){return e===je()==("focus"===t)}function je(){try{return r.activeElement}catch(e){}}function qe(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)qe(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return b().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=b.guid++)),e.each(function(){b.event.add(this,t,i,r,n)})}function Le(e,t,n){n?(Q.set(e,t,!1),b.event.add(e,t,{namespace:!1,handler:function(e){var r,i,a=Q.get(this,t);if(1&e.isTrigger&&this[t]){if(a)(b.event.special[t]||{}).delegateType&&e.stopPropagation();else if(a=o.call(arguments),Q.set(this,t,a),r=n(this,t),this[t](),a!==(i=Q.get(this,t))||r?Q.set(this,t,!1):i=void 0,a!==i)return e.stopImmediatePropagation(),e.preventDefault(),i}else a&&(Q.set(this,t,b.event.trigger(b.extend(a.shift(),b.Event.prototype),a,this)),e.stopImmediatePropagation())}})):b.event.add(e,t,Ae)}b.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(e);if(v)for(n.handler&&(n=(o=n).handler,i=o.selector),i&&b.find.matchesSelector(oe,i),n.guid||(n.guid=b.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==b&&b.event.triggered!==t.type?b.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(P)||[""]).length;l--;)d=g=(s=Se.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=b.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=b.event.special[d]||{},c=b.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&b.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),b.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(P)||[""]).length;l--;)if(d=g=(s=Se.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=b.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||b.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=b.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=b.event.special[s.type]||{};for(u[0]=s,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:b.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Oe=/\s*$/g;function Me(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&b(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function $e(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n1&&"string"==typeof v&&!h.checkClone&&Pe.test(v))return e.each(function(i){var o=e.eq(i);y&&(t[0]=v.call(this,i,o.html())),Fe(o,t,n,r)});if(p&&(o=(i=Ce(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=b.map(me(i,"script"),Ie)).length;f")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ae(e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(a=me(c),r=0,i=(o=me(e)).length;r0&&xe(a,!f&&me(e,"script")),c},cleanData:function(e){for(var t,n,r,i=b.event.special,o=0;void 0!==(n=e[o]);o++)if(G(n)){if(t=n[Q.expando]){if(t.events)for(r in t.events)i[r]?b.event.remove(n,r):b.removeEvent(n,r,t.handle);n[Q.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),b.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return _(this,function(e){return void 0===e?b.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Fe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Me(this,e).appendChild(e)})},prepend:function(){return Fe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Me(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Fe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Fe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(b.cleanData(me(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return _(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Oe.test(e)&&!ye[(ge.exec(e)||["",""])[1].toLowerCase()]){e=b.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function ot(e,t,n){var r=ze(e),i=(!h.boxSizingReliable()||n)&&"border-box"===b.css(e,"boxSizing",!1,r),o=i,a=Xe(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(_e.test(a)){if(!n)return a;a="auto"}return(!h.boxSizingReliable()&&i||"auto"===a||!parseFloat(a)&&"inline"===b.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===b.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+it(e,t,n||(i?"border":"content"),o,r,a)+"px"}function at(e,t,n,r,i){return new at.prototype.init(e,t,n,r,i)}b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Xe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=V(t),u=et.test(t),l=e.style;if(u||(t=Ke(s)),a=b.cssHooks[t]||b.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=re.exec(n))&&i[1]&&(n=ce(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(b.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=V(t);return et.test(t)||(t=Ke(s)),(a=b.cssHooks[t]||b.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Xe(e,t,r)),"normal"===i&&t in nt&&(i=nt[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),b.each(["height","width"],function(e,t){b.cssHooks[t]={get:function(e,n,r){if(n)return!Ze.test(b.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?ot(e,t,r):le(e,tt,function(){return ot(e,t,r)})},set:function(e,n,r){var i,o=ze(e),a=!h.scrollboxSize()&&"absolute"===o.position,s=(a||r)&&"border-box"===b.css(e,"boxSizing",!1,o),u=r?it(e,t,r,s,o):0;return s&&a&&(u-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-it(e,t,"border",!1,o)-.5)),u&&(i=re.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=b.css(e,t)),rt(0,n,u)}}}),b.cssHooks.marginLeft=Ve(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Xe(e,"marginLeft"))||e.getBoundingClientRect().left-le(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+ie[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(b.cssHooks[e+t].set=rt)}),b.fn.extend({css:function(e,t){return _(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=ze(e),i=t.length;a1)}}),b.Tween=at,at.prototype={constructor:at,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||b.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=at.propHooks[this.prop];return e&&e.get?e.get(this):at.propHooks._default.get(this)},run:function(e){var t,n=at.propHooks[this.prop];return this.options.duration?this.pos=t=b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):at.propHooks._default.set(this),this}},at.prototype.init.prototype=at.prototype,at.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=b.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):1!==e.elem.nodeType||!b.cssHooks[e.prop]&&null==e.elem.style[Ke(e.prop)]?e.elem[e.prop]=e.now:b.style(e.elem,e.prop,e.now+e.unit)}}},at.propHooks.scrollTop=at.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},b.fx=at.prototype.init,b.fx.step={};var st,ut,lt=/^(?:toggle|show|hide)$/,ct=/queueHooks$/;function ft(){ut&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(ft):e.setTimeout(ft,b.fx.interval),b.fx.tick())}function pt(){return e.setTimeout(function(){st=void 0}),st=Date.now()}function dt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ie[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ht(e,t,n){for(var r,i=(vt.tweeners[t]||[]).concat(vt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})}}),b.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?b.prop(e,t,n):(1===o&&b.isXMLDoc(e)||(i=b.attrHooks[t.toLowerCase()]||(b.expr.match.bool.test(t)?yt:void 0)),void 0!==n?null===n?void b.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=b.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),yt={set:function(e,t,n){return!1===t?b.removeAttr(e,n):e.setAttribute(n,n),n}},b.each(b.expr.match.bool.source.match(/\w+/g),function(e,t){var n=mt[t]||b.find.attr;mt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=mt[a],mt[a]=i,i=null!=n(e,t,r)?a:null,mt[a]=o),i}});var xt=/^(?:input|select|textarea|button)$/i,bt=/^(?:a|area)$/i;function wt(e){return(e.match(P)||[]).join(" ")}function Tt(e){return e.getAttribute&&e.getAttribute("class")||""}function Ct(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}b.fn.extend({prop:function(e,t){return _(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[b.propFix[e]||e]})}}),b.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&b.isXMLDoc(e)||(t=b.propFix[t]||t,i=b.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=b.find.attr(e,"tabindex");return t?parseInt(t,10):xt.test(e.nodeName)||bt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),h.optSelected||(b.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),b.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){b.propFix[this.toLowerCase()]=this}),b.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){b(this).addClass(e.call(this,t,Tt(this)))});if((t=Ct(e)).length)for(;n=this[u++];)if(i=Tt(n),r=1===n.nodeType&&" "+wt(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=wt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){b(this).removeClass(e.call(this,t,Tt(this)))});if(!arguments.length)return this.attr("class","");if((t=Ct(e)).length)for(;n=this[u++];)if(i=Tt(n),r=1===n.nodeType&&" "+wt(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");i!==(s=wt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,Tt(this),t),t)}):this.each(function(){var t,i,o,a;if(r)for(i=0,o=b(this),a=Ct(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||((t=Tt(this))&&Q.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Q.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+wt(Tt(n))+" ").indexOf(t)>-1)return!0;return!1}});var Et=/\r/g;b.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,b(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=b.map(i,function(e){return null==e?"":e+""})),(t=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))})):i?(t=b.valHooks[i.type]||b.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(Et,""):null==n?"":n:void 0}}),b.extend({valHooks:{option:{get:function(e){var t=b.find.attr(e,"value");return null!=t?t:wt(b.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=b.inArray(b(e).val(),t)>-1}},h.checkOn||(b.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var kt=/^(?:focusinfocus|focusoutblur)$/,St=function(e){e.stopPropagation()};b.extend(b.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,y=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!kt.test(m+b.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,(t=t[b.expando]?t:new b.Event(m,"object"==typeof t&&t)).isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:b.makeArray(n,[t]),d=b.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!v(i)){for(l=d.delegateType||m,kt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)y.push(s),u=s;u===(i.ownerDocument||r)&&y.push(u.defaultView||u.parentWindow||e)}for(a=0;(s=y[a++])&&!t.isPropagationStopped();)h=s,t.type=a>1?l:d.bindType||m,(p=(Q.get(s,"events")||{})[t.type]&&Q.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&G(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(y.pop(),n)||!G(i)||c&&g(i[m])&&!v(i)&&((u=i[c])&&(i[c]=null),b.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,St),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,St),b.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=b.extend(new b.Event,n,{type:e,isSimulated:!0});b.event.trigger(r,null,t)}}),b.fn.extend({trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return b.event.trigger(e,t,n,!0)}}),h.focusin||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){b.event.simulate(t,e.target,b.event.fix(e))};b.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=Q.access(r,t);i||r.addEventListener(e,n,!0),Q.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=Q.access(r,t)-1;i?Q.access(r,t,i):(r.removeEventListener(e,n,!0),Q.remove(r,t))}}});var At=e.location,Nt=Date.now(),Dt=/\?/;b.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+t),n};var jt=/\[\]$/,qt=/\r?\n/g,Lt=/^(?:submit|button|image|reset|file)$/i,Ht=/^(?:input|select|textarea|keygen)/i;function Ot(e,t,n,r){var i;if(Array.isArray(t))b.each(t,function(t,i){n||jt.test(e)?r(e,i):Ot(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)Ot(e+"["+i+"]",t[i],n,r)}b.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){i(this.name,this.value)});else for(n in e)Ot(n,e[n],t,i);return r.join("&")},b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&Ht.test(this.nodeName)&&!Lt.test(e)&&(this.checked||!he.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:Array.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(qt,"\r\n")}}):{name:t.name,value:n.replace(qt,"\r\n")}}).get()}});var Pt=/%20/g,Rt=/#.*$/,Mt=/([?&])_=[^&]*/,It=/^(.*?):[ \t]*([^\r\n]*)$/gm,Wt=/^(?:GET|HEAD)$/,$t=/^\/\//,Ft={},Bt={},_t="*/".concat("*"),zt=r.createElement("a");function Ut(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(P)||[];if(g(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function Xt(e,t,n,r){var i={},o=e===Bt;function a(s){var u;return i[s]=!0,b.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function Vt(e,t){var n,r,i=b.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&b.extend(!0,e,r),e}function Gt(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Yt(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}zt.href=At.href,b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:At.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(At.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":_t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Vt(Vt(e,b.ajaxSettings),t):Vt(b.ajaxSettings,e)},ajaxPrefilter:Ut(Ft),ajaxTransport:Ut(Bt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=b.ajaxSetup({},n),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?b(g):b.event,y=b.Deferred(),m=b.Callbacks("once memory"),x=h.statusCode||{},w={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=It.exec(a);)s[t[1].toLowerCase()+" "]=(s[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=s[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,w[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(y.promise(E),h.url=((t||h.url||At.href)+"").replace($t,At.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(P)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=zt.protocol+"//"+zt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=b.param(h.data,h.traditional)),Xt(Ft,h,n,E),c)return E;for(p in(f=b.event&&h.global)&&0==b.active++&&b.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Wt.test(h.type),o=h.url.replace(Rt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(Pt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(Dt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Mt,"$1"),d=(Dt.test(o)?"&":"?")+"_="+Nt+++d),h.url=o+d),h.ifModified&&(b.lastModified[o]&&E.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&E.setRequestHeader("If-None-Match",b.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", */*; q=0.01":""):h.accepts["*"]),h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=Xt(Bt,h,n,E)){if(E.readyState=1,f&&v.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(w,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,w,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(w=Gt(h,E,r)),w=Yt(h,w,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(b.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(b.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=w.state,p=w.data,l=!(d=w.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?y.resolveWith(g,[p,C,E]):y.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(v.trigger("ajaxComplete",[E,h]),--b.active||b.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return b.get(e,t,n,"json")},getScript:function(e,t){return b.get(e,void 0,t,"script")}}),b.each(["get","post"],function(e,t){b[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),b.ajax(b.extend({url:e,type:t,dataType:i,data:n,success:r},b.isPlainObject(e)&&e))}}),b._evalUrl=function(e,t){return b.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){b.globalEval(e,t)}})},b.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=b(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){b(this).replaceWith(this.childNodes)}),this}}),b.expr.pseudos.hidden=function(e){return!b.expr.pseudos.visible(e)},b.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},b.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Qt={0:200,1223:204},Jt=b.ajaxSettings.xhr();h.cors=!!Jt&&"withCredentials"in Jt,h.ajax=Jt=!!Jt,b.ajaxTransport(function(t){var n,r;if(h.cors||Jt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];for(a in t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Qt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),b.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),b.ajaxTransport("script",function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(i,o){t=b("